From d11d904c81a202c7bfb5b18f4ee248eb2fc9174f Mon Sep 17 00:00:00 2001 From: Corey Minyard Date: Fri, 4 Apr 2025 10:47:07 -0500 Subject: [PATCH 0001/2760] hw/ipmi: Move vmsd registration to device class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's the right way to do it now, and it handles multiple instances properly. I need multiple instances for some testing I'm doing so this is the right thing to do. Tested by doing: (qemu) migrate -d exec:cat>filen.mig before and after the fix, then: scripts/analyze-migration.py -d desc -f file1.mig >file1.json scripts/analyze-migration.py -d desc -f file2.mig >file2.json diff file1.json file2.json with no differences. Signed-off-by: Corey Minyard Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Fabiano Rosas --- hw/ipmi/ipmi_bmc_extern.c | 3 +-- hw/ipmi/ipmi_bmc_sim.c | 3 +-- hw/ipmi/isa_ipmi_bt.c | 3 +-- hw/ipmi/isa_ipmi_kcs.c | 12 +++++------- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index d015500254..3e9f8c5a50 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -497,8 +497,6 @@ static void ipmi_bmc_extern_realize(DeviceState *dev, Error **errp) qemu_chr_fe_set_handlers(&ibe->chr, can_receive, receive, chr_event, NULL, ibe, NULL, true); - - vmstate_register(NULL, 0, &vmstate_ipmi_bmc_extern, ibe); } static void ipmi_bmc_extern_init(Object *obj) @@ -528,6 +526,7 @@ static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data) bk->handle_reset = ipmi_bmc_extern_handle_reset; dc->hotpluggable = false; dc->realize = ipmi_bmc_extern_realize; + dc->vmsd = &vmstate_ipmi_bmc_extern; device_class_set_props(dc, ipmi_bmc_extern_properties); } diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 6157ac7120..bc0ddc52a9 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -2187,8 +2187,6 @@ static void ipmi_sim_realize(DeviceState *dev, Error **errp) register_cmds(ibs); ibs->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, ipmi_timeout, ibs); - - vmstate_register(NULL, 0, &vmstate_ipmi_sim, ibs); } static const Property ipmi_sim_properties[] = { @@ -2212,6 +2210,7 @@ static void ipmi_sim_class_init(ObjectClass *oc, void *data) dc->hotpluggable = false; dc->realize = ipmi_sim_realize; + dc->vmsd = &vmstate_ipmi_sim; device_class_set_props(dc, ipmi_sim_properties); bk->handle_command = ipmi_sim_handle_command; } diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index a1b66d5ee8..474a792c9a 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -117,8 +117,6 @@ static void isa_ipmi_bt_realize(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, iib->bt.io_base, iib->bt.io_length); isa_register_ioport(isadev, &iib->bt.io, iib->bt.io_base); - - vmstate_register(NULL, 0, &vmstate_ISAIPMIBTDevice, dev); } static void isa_ipmi_bt_init(Object *obj) @@ -147,6 +145,7 @@ static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data) AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(oc); dc->realize = isa_ipmi_bt_realize; + dc->vmsd = &vmstate_ISAIPMIBTDevice; device_class_set_props(dc, ipmi_isa_properties); iic->get_backend_data = isa_ipmi_bt_get_backend_data; diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index d9ebdd5371..94bbe45d35 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -72,6 +72,10 @@ static bool vmstate_kcs_before_version2(void *opaque, int version) return version <= 1; } +/* + * Version 1 had an incorrect name, it clashed with the BT IPMI + * device, so receive it, but transmit a different version. + */ static const VMStateDescription vmstate_ISAIPMIKCSDevice = { .name = TYPE_IPMI_INTERFACE, .version_id = 2, @@ -119,13 +123,6 @@ static void ipmi_isa_realize(DeviceState *dev, Error **errp) qdev_set_legacy_instance_id(dev, iik->kcs.io_base, iik->kcs.io_length); isa_register_ioport(isadev, &iik->kcs.io, iik->kcs.io_base); - - /* - * Version 1 had an incorrect name, it clashed with the BT - * IPMI device, so receive it, but transmit a different - * version. - */ - vmstate_register(NULL, 0, &vmstate_ISAIPMIKCSDevice, iik); } static void isa_ipmi_kcs_init(Object *obj) @@ -154,6 +151,7 @@ static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data) AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(oc); dc->realize = ipmi_isa_realize; + dc->vmsd = &vmstate_ISAIPMIKCSDevice; device_class_set_props(dc, ipmi_isa_properties); iic->get_backend_data = isa_ipmi_kcs_get_backend_data; From 3e0a763136aa715c536497008d9fa2c0d64c3909 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 2 Apr 2025 00:01:48 +1000 Subject: [PATCH 0002/2760] ipmi/pci-ipmi-bt: Rename copy-paste variables MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IPMI drivers use p/k suffix in variable names depending on bt or kcs. The pci bt driver must have come from the kcs driver because it's still using k suffixes in some cases. Rename. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Nicholas Piggin Message-ID: <20250401140153.685523-2-npiggin@gmail.com> Signed-off-by: Corey Minyard --- hw/ipmi/pci_ipmi_bt.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/hw/ipmi/pci_ipmi_bt.c b/hw/ipmi/pci_ipmi_bt.c index afeea6f303..a3b742d22c 100644 --- a/hw/ipmi/pci_ipmi_bt.c +++ b/hw/ipmi/pci_ipmi_bt.c @@ -38,49 +38,49 @@ struct PCIIPMIBTDevice { uint32_t uuid; }; -static void pci_ipmi_raise_irq(IPMIBT *ik) +static void pci_ipmi_raise_irq(IPMIBT *ib) { - PCIIPMIBTDevice *pik = ik->opaque; + PCIIPMIBTDevice *pib = ib->opaque; - pci_set_irq(&pik->dev, true); + pci_set_irq(&pib->dev, true); } -static void pci_ipmi_lower_irq(IPMIBT *ik) +static void pci_ipmi_lower_irq(IPMIBT *ib) { - PCIIPMIBTDevice *pik = ik->opaque; + PCIIPMIBTDevice *pib = ib->opaque; - pci_set_irq(&pik->dev, false); + pci_set_irq(&pib->dev, false); } static void pci_ipmi_bt_realize(PCIDevice *pd, Error **errp) { Error *err = NULL; - PCIIPMIBTDevice *pik = PCI_IPMI_BT(pd); + PCIIPMIBTDevice *pib = PCI_IPMI_BT(pd); IPMIInterface *ii = IPMI_INTERFACE(pd); IPMIInterfaceClass *iic = IPMI_INTERFACE_GET_CLASS(ii); - if (!pik->bt.bmc) { + if (!pib->bt.bmc) { error_setg(errp, "IPMI device requires a bmc attribute to be set"); return; } - pik->uuid = ipmi_next_uuid(); + pib->uuid = ipmi_next_uuid(); - pik->bt.bmc->intf = ii; - pik->bt.opaque = pik; + pib->bt.bmc->intf = ii; + pib->bt.opaque = pib; pci_config_set_prog_interface(pd->config, 0x02); /* BT */ pci_config_set_interrupt_pin(pd->config, 0x01); - pik->bt.use_irq = 1; - pik->bt.raise_irq = pci_ipmi_raise_irq; - pik->bt.lower_irq = pci_ipmi_lower_irq; + pib->bt.use_irq = 1; + pib->bt.raise_irq = pci_ipmi_raise_irq; + pib->bt.lower_irq = pci_ipmi_lower_irq; iic->init(ii, 8, &err); if (err) { error_propagate(errp, err); return; } - pci_register_bar(pd, 0, PCI_BASE_ADDRESS_SPACE_IO, &pik->bt.io); + pci_register_bar(pd, 0, PCI_BASE_ADDRESS_SPACE_IO, &pib->bt.io); } const VMStateDescription vmstate_PCIIPMIBTDevice = { @@ -96,16 +96,16 @@ const VMStateDescription vmstate_PCIIPMIBTDevice = { static void pci_ipmi_bt_instance_init(Object *obj) { - PCIIPMIBTDevice *pik = PCI_IPMI_BT(obj); + PCIIPMIBTDevice *pib = PCI_IPMI_BT(obj); - ipmi_bmc_find_and_link(obj, (Object **) &pik->bt.bmc); + ipmi_bmc_find_and_link(obj, (Object **) &pib->bt.bmc); } static void *pci_ipmi_bt_get_backend_data(IPMIInterface *ii) { - PCIIPMIBTDevice *pik = PCI_IPMI_BT(ii); + PCIIPMIBTDevice *pib = PCI_IPMI_BT(ii); - return &pik->bt; + return &pib->bt; } static void pci_ipmi_bt_class_init(ObjectClass *oc, void *data) From 7376d10b50fb5faa8324092c3f70195d713e34ac Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 2 Apr 2025 00:01:49 +1000 Subject: [PATCH 0003/2760] ipmi: add fwinfo to pci ipmi devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This requires some adjustments to callers to avoid possible behaviour changes for PCI devices. Signed-off-by: Nicholas Piggin Message-ID: <20250401140153.685523-3-npiggin@gmail.com> Reviewed-by: Philippe Mathieu-Daudé [Rename pci_ipmi_bt_get_fwinfo to pci_ipmi_kcs_get_fwinfo in the pci_ipmi_kcs.c file.] Signed-off-by: Corey Minyard --- hw/acpi/ipmi.c | 3 ++- hw/ipmi/isa_ipmi_bt.c | 1 + hw/ipmi/isa_ipmi_kcs.c | 1 + hw/ipmi/pci_ipmi_bt.c | 12 ++++++++++++ hw/ipmi/pci_ipmi_kcs.c | 11 +++++++++++ hw/smbios/smbios_type_38.c | 7 ++++++- include/hw/ipmi/ipmi.h | 5 +++++ 7 files changed, 38 insertions(+), 2 deletions(-) diff --git a/hw/acpi/ipmi.c b/hw/acpi/ipmi.c index a20e57d465..39f8f2f6d6 100644 --- a/hw/acpi/ipmi.c +++ b/hw/acpi/ipmi.c @@ -55,7 +55,8 @@ static Aml *aml_ipmi_crs(IPMIFwInfo *info) abort(); } - if (info->interrupt_number) { + /* Should PCI interrupts also be appended? */ + if (info->irq_source == IPMI_ISA_IRQ && info->interrupt_number) { aml_append(crs, aml_irq_no_flags(info->interrupt_number)); } diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index 474a792c9a..76585e786e 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -49,6 +49,7 @@ static void isa_ipmi_bt_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) ISAIPMIBTDevice *iib = ISA_IPMI_BT(ii); ipmi_bt_get_fwinfo(&iib->bt, info); + info->irq_source = IPMI_ISA_IRQ; info->interrupt_number = iib->isairq; info->i2c_slave_address = iib->bt.bmc->slave_addr; info->uuid = iib->uuid; diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index 94bbe45d35..ba3ae208b2 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -49,6 +49,7 @@ static void isa_ipmi_kcs_get_fwinfo(IPMIInterface *ii, IPMIFwInfo *info) ISAIPMIKCSDevice *iik = ISA_IPMI_KCS(ii); ipmi_kcs_get_fwinfo(&iik->kcs, info); + info->irq_source = IPMI_ISA_IRQ; info->interrupt_number = iik->isairq; info->uuid = iik->uuid; } diff --git a/hw/ipmi/pci_ipmi_bt.c b/hw/ipmi/pci_ipmi_bt.c index a3b742d22c..7ba8b3ab96 100644 --- a/hw/ipmi/pci_ipmi_bt.c +++ b/hw/ipmi/pci_ipmi_bt.c @@ -38,6 +38,17 @@ struct PCIIPMIBTDevice { uint32_t uuid; }; +static void pci_ipmi_bt_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) +{ + PCIIPMIBTDevice *pib = PCI_IPMI_BT(ii); + + ipmi_bt_get_fwinfo(&pib->bt, info); + info->irq_source = IPMI_PCI_IRQ; + info->interrupt_number = pci_intx(&pib->dev); + info->i2c_slave_address = pib->bt.bmc->slave_addr; + info->uuid = pib->uuid; +} + static void pci_ipmi_raise_irq(IPMIBT *ib) { PCIIPMIBTDevice *pib = ib->opaque; @@ -125,6 +136,7 @@ static void pci_ipmi_bt_class_init(ObjectClass *oc, void *data) iic->get_backend_data = pci_ipmi_bt_get_backend_data; ipmi_bt_class_init(iic); + iic->get_fwinfo = pci_ipmi_bt_get_fwinfo; } static const TypeInfo pci_ipmi_bt_info = { diff --git a/hw/ipmi/pci_ipmi_kcs.c b/hw/ipmi/pci_ipmi_kcs.c index 05ba97ec58..0aa35143e9 100644 --- a/hw/ipmi/pci_ipmi_kcs.c +++ b/hw/ipmi/pci_ipmi_kcs.c @@ -38,6 +38,16 @@ struct PCIIPMIKCSDevice { uint32_t uuid; }; +static void pci_ipmi_kcs_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) +{ + PCIIPMIKCSDevice *pik = PCI_IPMI_KCS(ii); + + ipmi_kcs_get_fwinfo(&pik->kcs, info); + info->irq_source = IPMI_PCI_IRQ; + info->interrupt_number = pci_intx(&pik->dev); + info->uuid = pik->uuid; +} + static void pci_ipmi_raise_irq(IPMIKCS *ik) { PCIIPMIKCSDevice *pik = ik->opaque; @@ -125,6 +135,7 @@ static void pci_ipmi_kcs_class_init(ObjectClass *oc, void *data) iic->get_backend_data = pci_ipmi_kcs_get_backend_data; ipmi_kcs_class_init(iic); + iic->get_fwinfo = pci_ipmi_kcs_get_fwinfo; } static const TypeInfo pci_ipmi_kcs_info = { diff --git a/hw/smbios/smbios_type_38.c b/hw/smbios/smbios_type_38.c index 168b886647..e9b856fcd9 100644 --- a/hw/smbios/smbios_type_38.c +++ b/hw/smbios/smbios_type_38.c @@ -72,7 +72,12 @@ static void smbios_build_one_type_38(IPMIFwInfo *info) " SMBIOS, ignoring this entry.", info->register_spacing); return; } - t->interrupt_number = info->interrupt_number; + if (info->irq_source == IPMI_ISA_IRQ) { + t->interrupt_number = info->interrupt_number; + } else { + /* TODO: How to handle PCI? */ + t->interrupt_number = 0; + } SMBIOS_BUILD_TABLE_POST; } diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index 77a7213ed9..c8ef04856e 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -90,6 +90,11 @@ typedef struct IPMIFwInfo { } memspace; int interrupt_number; + enum { + IPMI_NO_IRQ = 0, + IPMI_ISA_IRQ, + IPMI_PCI_IRQ, + } irq_source; enum { IPMI_LEVEL_IRQ, IPMI_EDGE_IRQ From 7f9e7af40a1721e5adb95761754bc12af0f3d2f1 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 2 Apr 2025 00:01:50 +1000 Subject: [PATCH 0004/2760] ipmi/bmc-sim: Add 'Get Channel Info' command Linux issues this command when booting a powernv machine. Signed-off-by: Nicholas Piggin Message-ID: <20250401140153.685523-4-npiggin@gmail.com> Signed-off-by: Corey Minyard --- hw/ipmi/ipmi_bmc_sim.c | 68 ++++++++++++++++++++++++++++++++++++++++-- hw/ipmi/ipmi_bt.c | 2 ++ hw/ipmi/ipmi_kcs.c | 1 + include/hw/ipmi/ipmi.h | 10 +++++++ 4 files changed, 79 insertions(+), 2 deletions(-) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index bc0ddc52a9..03e58d283e 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -70,6 +70,7 @@ #define IPMI_CMD_GET_MSG 0x33 #define IPMI_CMD_SEND_MSG 0x34 #define IPMI_CMD_READ_EVT_MSG_BUF 0x35 +#define IPMI_CMD_GET_CHANNEL_INFO 0x42 #define IPMI_NETFN_STORAGE 0x0a @@ -1020,8 +1021,8 @@ static void send_msg(IPMIBmcSim *ibs, uint8_t *buf; uint8_t netfn, rqLun, rsLun, rqSeq; - if (cmd[2] != 0) { - /* We only handle channel 0 with no options */ + if (cmd[2] != IPMI_CHANNEL_IPMB) { + /* We only handle channel 0h (IPMB) with no options */ rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); return; } @@ -1219,6 +1220,68 @@ static void get_watchdog_timer(IPMIBmcSim *ibs, } } +static void get_channel_info(IPMIBmcSim *ibs, + uint8_t *cmd, unsigned int cmd_len, + RspBuffer *rsp) +{ + IPMIInterface *s = ibs->parent.intf; + IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + IPMIFwInfo info = {}; + uint8_t ch = cmd[2] & 0x0f; + + /* Only define channel 0h (IPMB) and Fh (system interface) */ + + if (ch == 0x0e) { /* "This channel" */ + ch = IPMI_CHANNEL_SYSTEM; + } + rsp_buffer_push(rsp, ch); + + if (ch != IPMI_CHANNEL_IPMB && ch != IPMI_CHANNEL_SYSTEM) { + /* Not a supported channel */ + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + if (k->get_fwinfo) { + k->get_fwinfo(s, &info); + } + + if (ch == IPMI_CHANNEL_IPMB) { + rsp_buffer_push(rsp, IPMI_CHANNEL_MEDIUM_IPMB); + rsp_buffer_push(rsp, IPMI_CHANNEL_PROTOCOL_IPMB); + } else { /* IPMI_CHANNEL_SYSTEM */ + rsp_buffer_push(rsp, IPMI_CHANNEL_MEDIUM_SYSTEM); + rsp_buffer_push(rsp, info.ipmi_channel_protocol); + } + + rsp_buffer_push(rsp, 0x00); /* Session-less */ + + /* IPMI Enterprise Number for Vendor ID */ + rsp_buffer_push(rsp, 0xf2); + rsp_buffer_push(rsp, 0x1b); + rsp_buffer_push(rsp, 0x00); + + if (ch == IPMI_CHANNEL_SYSTEM) { + uint8_t irq; + + if (info.irq_source == IPMI_ISA_IRQ) { + irq = info.interrupt_number; + } else if (info.irq_source == IPMI_PCI_IRQ) { + irq = 0x10 + info.interrupt_number; + } else { + irq = 0xff; /* no interrupt / unspecified */ + } + + /* Both interrupts use the same irq number */ + rsp_buffer_push(rsp, irq); + rsp_buffer_push(rsp, irq); + } else { + /* Reserved */ + rsp_buffer_push(rsp, 0x00); + rsp_buffer_push(rsp, 0x00); + } +} + static void get_sdr_rep_info(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, RspBuffer *rsp) @@ -2015,6 +2078,7 @@ static const IPMICmdHandler app_cmds[] = { [IPMI_CMD_RESET_WATCHDOG_TIMER] = { reset_watchdog_timer }, [IPMI_CMD_SET_WATCHDOG_TIMER] = { set_watchdog_timer, 8 }, [IPMI_CMD_GET_WATCHDOG_TIMER] = { get_watchdog_timer }, + [IPMI_CMD_GET_CHANNEL_INFO] = { get_channel_info, 3 }, }; static const IPMINetfn app_netfn = { .cmd_nums = ARRAY_SIZE(app_cmds), diff --git a/hw/ipmi/ipmi_bt.c b/hw/ipmi/ipmi_bt.c index 583fc64730..28cf6ab218 100644 --- a/hw/ipmi/ipmi_bt.c +++ b/hw/ipmi/ipmi_bt.c @@ -419,6 +419,8 @@ void ipmi_bt_get_fwinfo(struct IPMIBT *ib, IPMIFwInfo *info) info->interface_type = IPMI_SMBIOS_BT; info->ipmi_spec_major_revision = 2; info->ipmi_spec_minor_revision = 0; + /* BT System Interface Format, IPMI v1.5 */ + info->ipmi_channel_protocol = IPMI_CHANNEL_PROTOCOL_BT_15; info->base_address = ib->io_base; info->register_length = ib->io_length; info->register_spacing = 1; diff --git a/hw/ipmi/ipmi_kcs.c b/hw/ipmi/ipmi_kcs.c index c15977cab4..578dd7cef3 100644 --- a/hw/ipmi/ipmi_kcs.c +++ b/hw/ipmi/ipmi_kcs.c @@ -405,6 +405,7 @@ void ipmi_kcs_get_fwinfo(IPMIKCS *ik, IPMIFwInfo *info) info->interface_type = IPMI_SMBIOS_KCS; info->ipmi_spec_major_revision = 2; info->ipmi_spec_minor_revision = 0; + info->ipmi_channel_protocol = IPMI_CHANNEL_PROTOCOL_KCS; info->base_address = ik->io_base; info->i2c_slave_address = ik->bmc->slave_addr; info->register_length = ik->io_length; diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index c8ef04856e..802a2febb0 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -41,6 +41,15 @@ enum ipmi_op { IPMI_SEND_NMI }; +/* Channel properties */ +#define IPMI_CHANNEL_IPMB 0x00 +#define IPMI_CHANNEL_SYSTEM 0x0f +#define IPMI_CHANNEL_MEDIUM_IPMB 0x01 +#define IPMI_CHANNEL_MEDIUM_SYSTEM 0x0c +#define IPMI_CHANNEL_PROTOCOL_IPMB 0x01 +#define IPMI_CHANNEL_PROTOCOL_KCS 0x05 +#define IPMI_CHANNEL_PROTOCOL_BT_15 0x08 + #define IPMI_CC_INVALID_CMD 0xc1 #define IPMI_CC_COMMAND_INVALID_FOR_LUN 0xc2 #define IPMI_CC_TIMEOUT 0xc3 @@ -76,6 +85,7 @@ typedef struct IPMIFwInfo { int interface_type; uint8_t ipmi_spec_major_revision; uint8_t ipmi_spec_minor_revision; + uint8_t ipmi_channel_protocol; uint8_t i2c_slave_address; uint32_t uuid; From d9494ef96c859515aa9df450fb61e833da7cb7c7 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 2 Apr 2025 00:01:51 +1000 Subject: [PATCH 0005/2760] ipmi/bmc-sim: implement watchdog dont log flag If the dont-log flag is set in the 'timer use' field for the 'set watchdog' command, a watchdog timeout will not get logged as a timer use expiration. Signed-off-by: Nicholas Piggin Message-ID: <20250401140153.685523-5-npiggin@gmail.com> Signed-off-by: Corey Minyard --- hw/ipmi/ipmi_bmc_sim.c | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 03e58d283e..4ed66e1ee0 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -514,7 +514,8 @@ static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert, static void sensor_set_discrete_bit(IPMIBmcSim *ibs, unsigned int sensor, unsigned int bit, unsigned int val, - uint8_t evd1, uint8_t evd2, uint8_t evd3) + uint8_t evd1, uint8_t evd2, uint8_t evd3, + bool do_log) { IPMISensor *sens; uint16_t mask; @@ -534,7 +535,7 @@ static void sensor_set_discrete_bit(IPMIBmcSim *ibs, unsigned int sensor, return; /* Already asserted */ } sens->assert_states |= mask & sens->assert_suppt; - if (sens->assert_enable & mask & sens->assert_states) { + if (do_log && (sens->assert_enable & mask & sens->assert_states)) { /* Send an event on assert */ gen_event(ibs, sensor, 0, evd1, evd2, evd3); } @@ -544,7 +545,7 @@ static void sensor_set_discrete_bit(IPMIBmcSim *ibs, unsigned int sensor, return; /* Already deasserted */ } sens->deassert_states |= mask & sens->deassert_suppt; - if (sens->deassert_enable & mask & sens->deassert_states) { + if (do_log && (sens->deassert_enable & mask & sens->deassert_states)) { /* Send an event on deassert */ gen_event(ibs, sensor, 1, evd1, evd2, evd3); } @@ -700,6 +701,7 @@ static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs) { IPMIInterface *s = ibs->parent.intf; IPMIInterfaceClass *k = IPMI_INTERFACE_GET_CLASS(s); + bool do_log = !IPMI_BMC_WATCHDOG_GET_DONT_LOG(ibs); if (!ibs->watchdog_running) { goto out; @@ -711,14 +713,16 @@ static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs) ibs->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK; k->do_hw_op(s, IPMI_SEND_NMI, 0); sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 8, 1, - 0xc8, (2 << 4) | 0xf, 0xff); + 0xc8, (2 << 4) | 0xf, 0xff, + do_log); break; case IPMI_BMC_WATCHDOG_PRE_MSG_INT: ibs->msg_flags |= IPMI_BMC_MSG_FLAG_WATCHDOG_TIMEOUT_MASK; k->set_atn(s, 1, attn_irq_enabled(ibs)); sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 8, 1, - 0xc8, (3 << 4) | 0xf, 0xff); + 0xc8, (3 << 4) | 0xf, 0xff, + do_log); break; default: @@ -738,24 +742,28 @@ static void ipmi_sim_handle_timeout(IPMIBmcSim *ibs) switch (IPMI_BMC_WATCHDOG_GET_ACTION(ibs)) { case IPMI_BMC_WATCHDOG_ACTION_NONE: sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 0, 1, - 0xc0, ibs->watchdog_use & 0xf, 0xff); + 0xc0, ibs->watchdog_use & 0xf, 0xff, + do_log); break; case IPMI_BMC_WATCHDOG_ACTION_RESET: sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 1, 1, - 0xc1, ibs->watchdog_use & 0xf, 0xff); + 0xc1, ibs->watchdog_use & 0xf, 0xff, + do_log); k->do_hw_op(s, IPMI_RESET_CHASSIS, 0); break; case IPMI_BMC_WATCHDOG_ACTION_POWER_DOWN: sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 2, 1, - 0xc2, ibs->watchdog_use & 0xf, 0xff); + 0xc2, ibs->watchdog_use & 0xf, 0xff, + do_log); k->do_hw_op(s, IPMI_POWEROFF_CHASSIS, 0); break; case IPMI_BMC_WATCHDOG_ACTION_POWER_CYCLE: sensor_set_discrete_bit(ibs, IPMI_WATCHDOG_SENSOR, 2, 1, - 0xc3, ibs->watchdog_use & 0xf, 0xff); + 0xc3, ibs->watchdog_use & 0xf, 0xff, + do_log); k->do_hw_op(s, IPMI_POWERCYCLE_CHASSIS, 0); break; } From dacb3e70ef73d1c9a2d0d4cfd65031deff49f742 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Wed, 2 Apr 2025 00:01:52 +1000 Subject: [PATCH 0006/2760] ipmi/bmc-sim: add error handling for 'Set BMC Global Enables' command Mask out unsupported bits and return failure if attempting to set any. This is not required by the IPMI spec, but it does require that system software not change bits it isn't aware of. Signed-off-by: Nicholas Piggin Message-ID: <20250401140153.685523-6-npiggin@gmail.com> Signed-off-by: Corey Minyard --- hw/ipmi/ipmi_bmc_sim.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 4ed66e1ee0..1c60a71831 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -235,6 +235,7 @@ struct IPMIBmcSim { #define IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE_SET(s) \ (IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE & (s)->msg_flags) +#define IPMI_BMC_GLOBAL_ENABLES_SUPPORTED 0x0f #define IPMI_BMC_RCV_MSG_QUEUE_INT_BIT 0 #define IPMI_BMC_EVBUF_FULL_INT_BIT 1 #define IPMI_BMC_EVENT_MSG_BUF_BIT 2 @@ -934,7 +935,14 @@ static void set_bmc_global_enables(IPMIBmcSim *ibs, uint8_t *cmd, unsigned int cmd_len, RspBuffer *rsp) { - set_global_enables(ibs, cmd[2]); + uint8_t val = cmd[2]; + + if (val & ~IPMI_BMC_GLOBAL_ENABLES_SUPPORTED) { + rsp_buffer_set_error(rsp, IPMI_CC_INVALID_DATA_FIELD); + return; + } + + set_global_enables(ibs, val); } static void get_bmc_global_enables(IPMIBmcSim *ibs, From 08b462dd9970a88d7f0e7c61ca48502463b0b78d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 10 Apr 2025 17:42:52 +0200 Subject: [PATCH 0007/2760] scsi: add conversion from ENODEV to sense This is mostly for completeness; I noticed it because ENODEV is used internally within scsi-disk.c, but when scsi_sense_from_errno(ENODEV) is called the resulting sense is never used and instead scsi_sense_from_host_status() is called later by scsi_req_complete_failed(). Signed-off-by: Paolo Bonzini --- scsi/utils.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/scsi/utils.c b/scsi/utils.c index 357b036671..545956f4f9 100644 --- a/scsi/utils.c +++ b/scsi/utils.c @@ -587,20 +587,27 @@ int scsi_sense_from_errno(int errno_value, SCSISense *sense) return GOOD; case EDOM: return TASK_SET_FULL; +#if ENODEV != ENOMEDIUM + case ENODEV: + /* + * Some of the BSDs have ENODEV and ENOMEDIUM as synonyms. For + * everyone else, give a more severe sense code for ENODEV. + */ +#endif #ifdef CONFIG_LINUX /* These errno mapping are specific to Linux. For more information: * - scsi_check_sense and scsi_decide_disposition in drivers/scsi/scsi_error.c * - scsi_result_to_blk_status in drivers/scsi/scsi_lib.c * - blk_errors[] in block/blk-core.c */ + case EREMOTEIO: + *sense = SENSE_CODE(TARGET_FAILURE); + return CHECK_CONDITION; case EBADE: return RESERVATION_CONFLICT; case ENODATA: *sense = SENSE_CODE(READ_ERROR); return CHECK_CONDITION; - case EREMOTEIO: - *sense = SENSE_CODE(TARGET_FAILURE); - return CHECK_CONDITION; #endif case ENOMEDIUM: *sense = SENSE_CODE(NO_MEDIUM); From 280712b78781c43511d6286d40f9a518a4de25ff Mon Sep 17 00:00:00 2001 From: Ewan Hai Date: Mon, 14 Apr 2025 03:53:42 -0400 Subject: [PATCH 0008/2760] target/i386: Fix model number of Zhaoxin YongFeng vCPU template The model number was mistakenly set to 0x0b (11) in commit ff04bc1ac4. The correct value is 0x5b. This mistake occurred because the extended model bits in cpuid[eax=0x1].eax were overlooked, and only the base model was used. Using the wrong model number can affect guest behavior. One known issue is that vPMU (which relies on the model number) may fail to operate correctly. This patch corrects the model field by introducing a new vCPU version. Fixes: ff04bc1ac4 ("target/i386: Introduce Zhaoxin Yongfeng CPU model") Signed-off-by: Ewan Hai Link: https://lore.kernel.org/r/20250414075342.411626-1-ewanhai-oc@zhaoxin.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 1b64ceaaba..3fb1ec62da 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5621,6 +5621,18 @@ static const X86CPUDefinition builtin_x86_defs[] = { .features[FEAT_VMX_VMFUNC] = MSR_VMX_VMFUNC_EPT_SWITCHING, .xlevel = 0x80000008, .model_id = "Zhaoxin YongFeng Processor", + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { + .version = 2, + .note = "with the correct model number", + .props = (PropValue[]) { + { "model", "0x5b" }, + { /* end of list */ } + } + }, + { /* end of list */ } + } }, }; From f6b5f71f04529d3f56b35f91badac9f5e7e225ca Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Thu, 27 Mar 2025 19:24:16 +0100 Subject: [PATCH 0009/2760] target/i386: Reset parked vCPUs together with the online ones Commit 3f2a05b31ee9 ("target/i386: Reset TSCs of parked vCPUs too on VM reset") introduced a way to reset TSCs of parked vCPUs during VM reset to prevent them getting desynchronized with the online vCPUs and therefore causing the KVM PV clock to lose PVCLOCK_TSC_STABLE_BIT. The way this was done was by registering a parked vCPU-specific QEMU reset callback via qemu_register_reset(). However, it turns out that on particularly device-rich VMs QEMU reset callbacks can take a long time to execute (which isn't surprising, considering that they involve resetting all of VM devices). In particular, their total runtime can exceed the 1-second TSC synchronization window introduced in KVM commit 5d3cb0f6a8e3 ("KVM: Improve TSC offset matching"). Since the TSCs of online vCPUs are only reset from "synchronize_post_reset" AccelOps handler (which runs after all qemu_register_reset() handlers) this essentially makes that fix ineffective on these VMs. The easiest way to guarantee that these parked vCPUs are reset at the same time as the online ones (regardless how long it takes for VM devices to reset) is to piggyback on post-reset vCPU synchronization handler for one of online vCPUs - as there is no generic post-reset AccelOps handler that isn't per-vCPU. The first online vCPU was selected for that since it is easily available under "first_cpu" define. This does not create an ordering issue since the order of vCPU TSC resets does not matter. Fixes: 3f2a05b31ee9 ("target/i386: Reset TSCs of parked vCPUs too on VM reset") Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/r/e8b85a5915f79aa177ca49eccf0e9b534470c1cd.1743099810.git.maciej.szmigiero@oracle.com Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index f89568bfa3..951e8214e0 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -437,9 +437,8 @@ int kvm_unpark_vcpu(KVMState *s, unsigned long vcpu_id) return kvm_fd; } -static void kvm_reset_parked_vcpus(void *param) +static void kvm_reset_parked_vcpus(KVMState *s) { - KVMState *s = param; struct KVMParkedVcpu *cpu; QLIST_FOREACH(cpu, &s->kvm_parked_vcpus, node) { @@ -2738,7 +2737,6 @@ static int kvm_init(MachineState *ms) } qemu_register_reset(kvm_unpoison_all, NULL); - qemu_register_reset(kvm_reset_parked_vcpus, s); if (s->kernel_irqchip_allowed) { kvm_irqchip_create(s); @@ -2908,6 +2906,10 @@ static void do_kvm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg void kvm_cpu_synchronize_post_reset(CPUState *cpu) { run_on_cpu(cpu, do_kvm_cpu_synchronize_post_reset, RUN_ON_CPU_NULL); + + if (cpu == first_cpu) { + kvm_reset_parked_vcpus(kvm_state); + } } static void do_kvm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) From 94a159f3dc737d00749cc930adaec112abe07b3c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 3 Apr 2025 21:39:54 +0200 Subject: [PATCH 0010/2760] target/i386/hvf: fix lflags_to_rflags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clear the flags before adding in the ones computed from lflags. Cc: Wei Liu Cc: qemu-stable@nongnu.org Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Paolo Bonzini --- target/i386/hvf/x86_flags.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/hvf/x86_flags.c b/target/i386/hvf/x86_flags.c index 03d6de5efc..fedc70a1b8 100644 --- a/target/i386/hvf/x86_flags.c +++ b/target/i386/hvf/x86_flags.c @@ -293,6 +293,7 @@ void set_SF(CPUX86State *env, bool val) void lflags_to_rflags(CPUX86State *env) { + env->eflags &= ~(CC_C|CC_P|CC_A|CC_Z|CC_S|CC_O); env->eflags |= get_CF(env) ? CC_C : 0; env->eflags |= get_PF(env) ? CC_P : 0; env->eflags |= get_AF(env) ? CC_A : 0; From bd65810538bfa4baef4f9e2fe40166d008a2a64c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 17 Feb 2025 10:21:02 +0100 Subject: [PATCH 0011/2760] target/i386: special case ADC/SBB x,0 and SBB x,x Avoid the three-operand CC_OP_ADD and CC_OP_ADC in these relatively common cases. Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 71 +++++++++++++++++++++++++++++++++---- target/i386/tcg/translate.c | 20 +++++++++++ 2 files changed, 85 insertions(+), 6 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 0fa1664a24..f477a2da99 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1170,11 +1170,28 @@ static void gen_AAS(DisasContext *s, X86DecodedInsn *decode) assume_cc_op(s, CC_OP_EFLAGS); } +static void gen_ADD(DisasContext *s, X86DecodedInsn *decode); static void gen_ADC(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[1].ot; - TCGv c_in = tcg_temp_new(); + TCGv c_in; + /* + * Try to avoid CC_OP_ADC by transforming as follows: + * CC_ADC: src1 = dst + c_in, src2 = 0, src3 = c_in + * CC_ADD: src1 = dst + c_in, src2 = c_in (no src3) + * + * In general src2 vs. src3 matters when computing AF and OF, but not here: + * - AF is bit 4 of dst^src1^src2, which is bit 4 of dst^src1 in both cases + * - OF is a function of the two MSBs, and in both cases they are zero for src2 + */ + if (decode->e.op2 == X86_TYPE_I && decode->immediate == 0) { + gen_compute_eflags_c(s, s->T1); + gen_ADD(s, decode); + return; + } + + c_in = tcg_temp_new(); gen_compute_eflags_c(s, c_in); if (s->prefix & PREFIX_LOCK) { tcg_gen_add_tl(s->T0, c_in, s->T1); @@ -3830,22 +3847,64 @@ static void gen_SARX(DisasContext *s, X86DecodedInsn *decode) tcg_gen_sar_tl(s->T0, s->T0, s->T1); } +static void gen_SUB(DisasContext *s, X86DecodedInsn *decode); static void gen_SBB(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[0].ot; - TCGv c_in = tcg_temp_new(); + TCGv c_in; + /* + * Try to avoid CC_OP_SBB by transforming as follows: + * CC_SBB: src1 = dst + c_in, src2 = 0, src3 = c_in + * CC_SUB: src1 = dst + c_in, src2 = c_in (no src3) + * + * In general src2 vs. src3 matters when computing AF and OF, but not here: + * - AF is bit 4 of dst^src1^src2, which is bit 4 of dst^src1 in both cases + * - OF is a function of the two MSBs, and in both cases they are zero for src2 + */ + if (decode->e.op2 == X86_TYPE_I && decode->immediate == 0) { + gen_compute_eflags_c(s, s->T1); + gen_SUB(s, decode); + return; + } + + c_in = tcg_temp_new(); gen_compute_eflags_c(s, c_in); + + /* + * Here the change is as follows: + * CC_SBB: src1 = T0, src2 = T0, src3 = c_in + * CC_SUB: src1 = 0, src2 = c_in (no src3) + * + * The difference also does not matter: + * - AF is bit 4 of dst^src1^src2, but bit 4 of src1^src2 is zero in both cases + * therefore AF comes straight from dst (in fact it is c_in) + * - for OF, src1 and src2 have the same sign in both cases, meaning there + * can be no overflow + */ + if (decode->e.op2 != X86_TYPE_I && !decode->op[0].has_ea && decode->op[0].n == decode->op[2].n) { + if (s->cc_op == CC_OP_DYNAMIC) { + tcg_gen_neg_tl(s->T0, c_in); + } else { + /* + * Do not negate c_in because it will often be dead and only the + * instruction generated by negsetcond will survive. + */ + gen_neg_setcc(s, JCC_B << 1, s->T0); + } + tcg_gen_movi_tl(s->cc_srcT, 0); + decode->cc_src = c_in; + decode->cc_dst = s->T0; + decode->cc_op = CC_OP_SUBB + ot; + return; + } + if (s->prefix & PREFIX_LOCK) { tcg_gen_add_tl(s->T0, s->T1, c_in); tcg_gen_neg_tl(s->T0, s->T0); tcg_gen_atomic_add_fetch_tl(s->T0, s->A0, s->T0, s->mem_index, ot | MO_LE); } else { - /* - * TODO: SBB reg, reg could use gen_prepare_eflags_c followed by - * negsetcond, and CC_OP_SUBB as the cc_op. - */ tcg_gen_sub_tl(s->T0, s->T0, s->T1); tcg_gen_sub_tl(s->T0, s->T0, c_in); } diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index a8935f487a..aee3342898 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1183,6 +1183,26 @@ static CCPrepare gen_prepare_cc(DisasContext *s, int b, TCGv reg) return cc; } +static void gen_neg_setcc(DisasContext *s, int b, TCGv reg) +{ + CCPrepare cc = gen_prepare_cc(s, b, reg); + + if (cc.no_setcond) { + if (cc.cond == TCG_COND_EQ) { + tcg_gen_addi_tl(reg, cc.reg, -1); + } else { + tcg_gen_neg_tl(reg, cc.reg); + } + return; + } + + if (cc.use_reg2) { + tcg_gen_negsetcond_tl(cc.cond, reg, cc.reg, cc.reg2); + } else { + tcg_gen_negsetcondi_tl(cc.cond, reg, cc.reg, cc.imm); + } +} + static void gen_setcc(DisasContext *s, int b, TCGv reg) { CCPrepare cc = gen_prepare_cc(s, b, reg); From ec6019f2308b5d27b4ee33497b06174e9f58b100 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Feb 2025 18:45:06 +0100 Subject: [PATCH 0012/2760] target/i386: tcg: remove tmp0 and tmp4 from SHLD/SHRD Apply some of the simplifications used for RCL and RCR. tmp4 is not used anywhere else, so remove it. Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 6 ++--- target/i386/tcg/translate.c | 51 +++++++++++++++++++++---------------- 2 files changed, 31 insertions(+), 26 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index f477a2da99..a41afb7fe4 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -4015,8 +4015,7 @@ static void gen_SHLD(DisasContext *s, X86DecodedInsn *decode) } decode->cc_dst = s->T0; - decode->cc_src = s->tmp0; - gen_shiftd_rm_T1(s, ot, false, count); + decode->cc_src = gen_shiftd_rm_T1(s, ot, false, count); if (can_be_zero) { gen_shift_dynamic_flags(s, decode, count, CC_OP_SHLB + ot); } else { @@ -4068,8 +4067,7 @@ static void gen_SHRD(DisasContext *s, X86DecodedInsn *decode) } decode->cc_dst = s->T0; - decode->cc_src = s->tmp0; - gen_shiftd_rm_T1(s, ot, true, count); + decode->cc_src = gen_shiftd_rm_T1(s, ot, true, count); if (can_be_zero) { gen_shift_dynamic_flags(s, decode, count, CC_OP_SARB + ot); } else { diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index aee3342898..5529327680 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -135,7 +135,6 @@ typedef struct DisasContext { /* TCG local register indexes (only used inside old micro ops) */ TCGv tmp0; - TCGv tmp4; TCGv_i32 tmp2_i32; TCGv_i32 tmp3_i32; TCGv_i64 tmp1_i64; @@ -1580,10 +1579,13 @@ static bool check_cpl0(DisasContext *s) } /* XXX: add faster immediate case */ -static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, +static TCGv gen_shiftd_rm_T1(DisasContext *s, MemOp ot, bool is_right, TCGv count) { target_ulong mask = (ot == MO_64 ? 63 : 31); + TCGv cc_src = tcg_temp_new(); + TCGv tmp = tcg_temp_new(); + TCGv hishift; switch (ot) { case MO_16: @@ -1591,9 +1593,9 @@ static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, This means "shrdw C, B, A" shifts A:B:A >> C. Build the B:A portion by constructing it as a 32-bit value. */ if (is_right) { - tcg_gen_deposit_tl(s->tmp0, s->T0, s->T1, 16, 16); + tcg_gen_deposit_tl(tmp, s->T0, s->T1, 16, 16); tcg_gen_mov_tl(s->T1, s->T0); - tcg_gen_mov_tl(s->T0, s->tmp0); + tcg_gen_mov_tl(s->T0, tmp); } else { tcg_gen_deposit_tl(s->T1, s->T0, s->T1, 16, 16); } @@ -1604,47 +1606,53 @@ static void gen_shiftd_rm_T1(DisasContext *s, MemOp ot, case MO_32: #ifdef TARGET_X86_64 /* Concatenate the two 32-bit values and use a 64-bit shift. */ - tcg_gen_subi_tl(s->tmp0, count, 1); + tcg_gen_subi_tl(tmp, count, 1); if (is_right) { tcg_gen_concat_tl_i64(s->T0, s->T0, s->T1); - tcg_gen_shr_i64(s->tmp0, s->T0, s->tmp0); + tcg_gen_shr_i64(cc_src, s->T0, tmp); tcg_gen_shr_i64(s->T0, s->T0, count); } else { tcg_gen_concat_tl_i64(s->T0, s->T1, s->T0); - tcg_gen_shl_i64(s->tmp0, s->T0, s->tmp0); + tcg_gen_shl_i64(cc_src, s->T0, tmp); tcg_gen_shl_i64(s->T0, s->T0, count); - tcg_gen_shri_i64(s->tmp0, s->tmp0, 32); + tcg_gen_shri_i64(cc_src, cc_src, 32); tcg_gen_shri_i64(s->T0, s->T0, 32); } break; #endif default: - tcg_gen_subi_tl(s->tmp0, count, 1); + hishift = tcg_temp_new(); + tcg_gen_subi_tl(tmp, count, 1); if (is_right) { - tcg_gen_shr_tl(s->tmp0, s->T0, s->tmp0); + tcg_gen_shr_tl(cc_src, s->T0, tmp); - tcg_gen_subfi_tl(s->tmp4, mask + 1, count); + /* mask + 1 - count = mask - tmp = mask ^ tmp */ + tcg_gen_xori_tl(hishift, tmp, mask); tcg_gen_shr_tl(s->T0, s->T0, count); - tcg_gen_shl_tl(s->T1, s->T1, s->tmp4); + tcg_gen_shl_tl(s->T1, s->T1, hishift); } else { - tcg_gen_shl_tl(s->tmp0, s->T0, s->tmp0); + tcg_gen_shl_tl(cc_src, s->T0, tmp); + if (ot == MO_16) { /* Only needed if count > 16, for Intel behaviour. */ - tcg_gen_subfi_tl(s->tmp4, 33, count); - tcg_gen_shr_tl(s->tmp4, s->T1, s->tmp4); - tcg_gen_or_tl(s->tmp0, s->tmp0, s->tmp4); + tcg_gen_subfi_tl(tmp, 33, count); + tcg_gen_shr_tl(tmp, s->T1, tmp); + tcg_gen_or_tl(cc_src, cc_src, tmp); } - tcg_gen_subfi_tl(s->tmp4, mask + 1, count); + /* mask + 1 - count = mask - tmp = mask ^ tmp */ + tcg_gen_xori_tl(hishift, tmp, mask); tcg_gen_shl_tl(s->T0, s->T0, count); - tcg_gen_shr_tl(s->T1, s->T1, s->tmp4); + tcg_gen_shr_tl(s->T1, s->T1, hishift); } - tcg_gen_movi_tl(s->tmp4, 0); - tcg_gen_movcond_tl(TCG_COND_EQ, s->T1, count, s->tmp4, - s->tmp4, s->T1); + tcg_gen_movcond_tl(TCG_COND_EQ, s->T1, + count, tcg_constant_tl(0), + tcg_constant_tl(0), s->T1); tcg_gen_or_tl(s->T0, s->T0, s->T1); break; } + + return cc_src; } #define X86_MAX_INSN_LENGTH 15 @@ -3768,7 +3776,6 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->tmp1_i64 = tcg_temp_new_i64(); dc->tmp2_i32 = tcg_temp_new_i32(); dc->tmp3_i32 = tcg_temp_new_i32(); - dc->tmp4 = tcg_temp_new(); dc->cc_srcT = tcg_temp_new(); } From a440975cc33b48316f26d4ba0f1714375ff47738 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Feb 2025 19:25:41 +0100 Subject: [PATCH 0013/2760] target/i386: tcg: remove subf from SHLD/SHRD expansion It is computing 33-count but 32-count had just been used, so just shift further by one. Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 5529327680..822dbb2e9a 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -1633,17 +1633,16 @@ static TCGv gen_shiftd_rm_T1(DisasContext *s, MemOp ot, } else { tcg_gen_shl_tl(cc_src, s->T0, tmp); - if (ot == MO_16) { - /* Only needed if count > 16, for Intel behaviour. */ - tcg_gen_subfi_tl(tmp, 33, count); - tcg_gen_shr_tl(tmp, s->T1, tmp); - tcg_gen_or_tl(cc_src, cc_src, tmp); - } - /* mask + 1 - count = mask - tmp = mask ^ tmp */ tcg_gen_xori_tl(hishift, tmp, mask); tcg_gen_shl_tl(s->T0, s->T0, count); tcg_gen_shr_tl(s->T1, s->T1, hishift); + + if (ot == MO_16) { + /* Only needed if count > 16, for Intel behaviour. */ + tcg_gen_shri_tl(tmp, s->T1, 1); + tcg_gen_or_tl(cc_src, cc_src, tmp); + } } tcg_gen_movcond_tl(TCG_COND_EQ, s->T1, count, tcg_constant_tl(0), From d387eb7fa989f3397d681111324f6a54c1b32484 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Feb 2025 19:30:20 +0100 Subject: [PATCH 0014/2760] target/i386: tcg: remove tmp0 Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 20 ++++++++++---------- target/i386/tcg/translate.c | 27 +++++++++++++++------------ 2 files changed, 25 insertions(+), 22 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index a41afb7fe4..bd24438230 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1710,22 +1710,22 @@ static void gen_CMPccXADD(DisasContext *s, X86DecodedInsn *decode) switch (jcc_op) { case JCC_O: /* (src1 ^ src2) & (src1 ^ dst). newv is only used here for a moment */ + cmp_lhs = tcg_temp_new(), cmp_rhs = tcg_constant_tl(0); tcg_gen_xor_tl(newv, s->cc_srcT, s->T0); - tcg_gen_xor_tl(s->tmp0, s->cc_srcT, cmpv); - tcg_gen_and_tl(s->tmp0, s->tmp0, newv); - tcg_gen_sextract_tl(s->tmp0, s->tmp0, 0, 8 << ot); - cmp_lhs = s->tmp0, cmp_rhs = tcg_constant_tl(0); + tcg_gen_xor_tl(cmp_lhs, s->cc_srcT, cmpv); + tcg_gen_and_tl(cmp_lhs, cmp_lhs, newv); + tcg_gen_sextract_tl(cmp_lhs, cmp_lhs, 0, 8 << ot); break; case JCC_P: - tcg_gen_ext8u_tl(s->tmp0, s->T0); - tcg_gen_ctpop_tl(s->tmp0, s->tmp0); - cmp_lhs = s->tmp0, cmp_rhs = tcg_constant_tl(1); + cmp_lhs = tcg_temp_new(), cmp_rhs = tcg_constant_tl(1); + tcg_gen_ext8u_tl(cmp_lhs, s->T0); + tcg_gen_ctpop_tl(cmp_lhs, cmp_lhs); break; case JCC_S: - tcg_gen_sextract_tl(s->tmp0, s->T0, 0, 8 << ot); - cmp_lhs = s->tmp0, cmp_rhs = tcg_constant_tl(0); + cmp_lhs = tcg_temp_new(), cmp_rhs = tcg_constant_tl(0); + tcg_gen_sextract_tl(cmp_lhs, s->T0, 0, 8 << ot); break; default: @@ -1876,7 +1876,7 @@ static void gen_CMPXCHG8B(DisasContext *s, X86DecodedInsn *decode) s->mem_index, MO_TEUQ); } - /* Set tmp0 to match the required value of Z. */ + /* Compute the required value of Z. */ tcg_gen_setcond_i64(TCG_COND_EQ, cmp, old, cmp); Z = tcg_temp_new(); tcg_gen_trunc_i64_tl(Z, cmp); diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 822dbb2e9a..5d433f8522 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -134,7 +134,6 @@ typedef struct DisasContext { TCGv T1; /* TCG local register indexes (only used inside old micro ops) */ - TCGv tmp0; TCGv_i32 tmp2_i32; TCGv_i32 tmp3_i32; TCGv_i64 tmp1_i64; @@ -2175,14 +2174,17 @@ static void gen_enter(DisasContext *s, int esp_addend, int level) level &= 31; if (level != 0) { int i; + if (level > 1) { + TCGv fp = tcg_temp_new(); - /* Copy level-1 pointers from the previous frame. */ - for (i = 1; i < level; ++i) { - gen_lea_ss_ofs(s, s->A0, cpu_regs[R_EBP], -size * i); - gen_op_ld_v(s, d_ot, s->tmp0, s->A0); + /* Copy level-1 pointers from the previous frame. */ + for (i = 1; i < level; ++i) { + gen_lea_ss_ofs(s, s->A0, cpu_regs[R_EBP], -size * i); + gen_op_ld_v(s, d_ot, fp, s->A0); - gen_lea_ss_ofs(s, s->A0, s->T1, -size * i); - gen_op_st_v(s, d_ot, s->tmp0, s->A0); + gen_lea_ss_ofs(s, s->A0, s->T1, -size * i); + gen_op_st_v(s, d_ot, fp, s->A0); + } } /* Push the current FrameTemp as the last level. */ @@ -2405,10 +2407,11 @@ static void gen_ldy_env_A0(DisasContext *s, int offset, bool align) int mem_index = s->mem_index; TCGv_i128 t0 = tcg_temp_new_i128(); TCGv_i128 t1 = tcg_temp_new_i128(); + TCGv a0_hi = tcg_temp_new(); tcg_gen_qemu_ld_i128(t0, s->A0, mem_index, mop | (align ? MO_ALIGN_32 : 0)); - tcg_gen_addi_tl(s->tmp0, s->A0, 16); - tcg_gen_qemu_ld_i128(t1, s->tmp0, mem_index, mop); + tcg_gen_addi_tl(a0_hi, s->A0, 16); + tcg_gen_qemu_ld_i128(t1, a0_hi, mem_index, mop); tcg_gen_st_i128(t0, tcg_env, offset + offsetof(YMMReg, YMM_X(0))); tcg_gen_st_i128(t1, tcg_env, offset + offsetof(YMMReg, YMM_X(1))); @@ -2419,12 +2422,13 @@ static void gen_sty_env_A0(DisasContext *s, int offset, bool align) MemOp mop = MO_128 | MO_LE | MO_ATOM_IFALIGN_PAIR; int mem_index = s->mem_index; TCGv_i128 t = tcg_temp_new_i128(); + TCGv a0_hi = tcg_temp_new(); tcg_gen_ld_i128(t, tcg_env, offset + offsetof(YMMReg, YMM_X(0))); tcg_gen_qemu_st_i128(t, s->A0, mem_index, mop | (align ? MO_ALIGN_32 : 0)); - tcg_gen_addi_tl(s->tmp0, s->A0, 16); + tcg_gen_addi_tl(a0_hi, s->A0, 16); tcg_gen_ld_i128(t, tcg_env, offset + offsetof(YMMReg, YMM_X(1))); - tcg_gen_qemu_st_i128(t, s->tmp0, mem_index, mop); + tcg_gen_qemu_st_i128(t, a0_hi, mem_index, mop); } #include "emit.c.inc" @@ -3771,7 +3775,6 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->T1 = tcg_temp_new(); dc->A0 = tcg_temp_new(); - dc->tmp0 = tcg_temp_new(); dc->tmp1_i64 = tcg_temp_new_i64(); dc->tmp2_i32 = tcg_temp_new_i32(); dc->tmp3_i32 = tcg_temp_new_i32(); From 9a688e70bdb2f23112afeddefd91bd45674d4db9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Feb 2025 18:51:34 +0100 Subject: [PATCH 0015/2760] target/i386: tcg: remove some more uses of temporaries Remove all uses of 32-bit temporaries in emit.c.inc. Remove uses in translate.c outside the large multiplexed generator functions. tmp3_i32 is not used anymore and can go away. Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 83 +++++++++++++++++++++++-------------- target/i386/tcg/translate.c | 43 +++++++++++-------- 2 files changed, 77 insertions(+), 49 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index bd24438230..4e09e96fc1 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1916,9 +1916,10 @@ static void gen_CPUID(DisasContext *s, X86DecodedInsn *decode) static void gen_CRC32(DisasContext *s, X86DecodedInsn *decode) { MemOp ot = decode->op[2].ot; + TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_crc32(s->T0, s->tmp2_i32, s->T1, tcg_constant_i32(8 << ot)); + tcg_gen_trunc_tl_i32(tmp, s->T0); + gen_helper_crc32(s->T0, tmp, s->T1, tcg_constant_i32(8 << ot)); } static void gen_CVTPI2Px(DisasContext *s, X86DecodedInsn *decode) @@ -2376,8 +2377,10 @@ static void gen_LAR(DisasContext *s, X86DecodedInsn *decode) static void gen_LDMXCSR(DisasContext *s, X86DecodedInsn *decode) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - gen_helper_ldmxcsr(tcg_env, s->tmp2_i32); + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(tmp, s->T0); + gen_helper_ldmxcsr(tcg_env, tmp); } static void gen_lxx_seg(DisasContext *s, X86DecodedInsn *decode, int seg) @@ -2590,11 +2593,13 @@ static void gen_MOVDQ(DisasContext *s, X86DecodedInsn *decode) static void gen_MOVMSK(DisasContext *s, X86DecodedInsn *decode) { typeof(gen_helper_movmskps_ymm) *ps, *pd, *fn; + TCGv_i32 tmp = tcg_temp_new_i32(); + ps = s->vex_l ? gen_helper_movmskps_ymm : gen_helper_movmskps_xmm; pd = s->vex_l ? gen_helper_movmskpd_ymm : gen_helper_movmskpd_xmm; fn = s->prefix & PREFIX_DATA ? pd : ps; - fn(s->tmp2_i32, tcg_env, OP_PTR2); - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); + fn(tmp, tcg_env, OP_PTR2); + tcg_gen_extu_i32_tl(s->T0, tmp); } static void gen_MOVQ(DisasContext *s, X86DecodedInsn *decode) @@ -2691,13 +2696,17 @@ static void gen_MULX(DisasContext *s, X86DecodedInsn *decode) switch (ot) { case MO_32: #ifdef TARGET_X86_64 - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T1); - tcg_gen_mulu2_i32(s->tmp2_i32, s->tmp3_i32, - s->tmp2_i32, s->tmp3_i32); - tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], s->tmp2_i32); - tcg_gen_extu_i32_tl(s->T0, s->tmp3_i32); - break; + { + TCGv_i32 t0 = tcg_temp_new_i32(); + TCGv_i32 t1 = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(t0, s->T0); + tcg_gen_trunc_tl_i32(t1, s->T1); + tcg_gen_mulu2_i32(t0, t1, t0, t1); + tcg_gen_extu_i32_tl(cpu_regs[s->vex_v], t0); + tcg_gen_extu_i32_tl(s->T0, t1); + break; + } case MO_64: #endif @@ -3741,10 +3750,14 @@ static void gen_RORX(DisasContext *s, X86DecodedInsn *decode) switch (ot) { case MO_32: #ifdef TARGET_X86_64 - tcg_gen_trunc_tl_i32(s->tmp2_i32, s->T0); - tcg_gen_rotri_i32(s->tmp2_i32, s->tmp2_i32, b); - tcg_gen_extu_i32_tl(s->T0, s->tmp2_i32); - break; + { + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(tmp, s->T0); + tcg_gen_rotri_i32(tmp, tmp, b); + tcg_gen_extu_i32_tl(s->T0, tmp); + break; + } case MO_64: #endif @@ -4334,7 +4347,7 @@ static void gen_VCVTSI2Sx(DisasContext *s, X86DecodedInsn *decode) } return; } - in = s->tmp2_i32; + in = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(in, s->T1); #else in = s->T1; @@ -4364,7 +4377,7 @@ static inline void gen_VCVTtSx2SI(DisasContext *s, X86DecodedInsn *decode, return; } - out = s->tmp2_i32; + out = tcg_temp_new_i32(); #else out = s->T0; #endif @@ -4416,7 +4429,7 @@ static void gen_VEXTRACTPS(DisasContext *s, X86DecodedInsn *decode) gen_pextr(s, decode, MO_32); } -static void gen_vinsertps(DisasContext *s, X86DecodedInsn *decode) +static void gen_vinsertps(DisasContext *s, X86DecodedInsn *decode, TCGv_i32 tmp) { int val = decode->immediate; int dest_word = (val >> 4) & 3; @@ -4433,7 +4446,7 @@ static void gen_vinsertps(DisasContext *s, X86DecodedInsn *decode) } if (new_mask != (val & 15)) { - tcg_gen_st_i32(s->tmp2_i32, tcg_env, + tcg_gen_st_i32(tmp, tcg_env, vector_elem_offset(&decode->op[0], MO_32, dest_word)); } @@ -4452,15 +4465,19 @@ static void gen_vinsertps(DisasContext *s, X86DecodedInsn *decode) static void gen_VINSERTPS_r(DisasContext *s, X86DecodedInsn *decode) { int val = decode->immediate; - tcg_gen_ld_i32(s->tmp2_i32, tcg_env, + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_ld_i32(tmp, tcg_env, vector_elem_offset(&decode->op[2], MO_32, (val >> 6) & 3)); - gen_vinsertps(s, decode); + gen_vinsertps(s, decode, tmp); } static void gen_VINSERTPS_m(DisasContext *s, X86DecodedInsn *decode) { - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); - gen_vinsertps(s, decode); + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_qemu_ld_i32(tmp, s->A0, s->mem_index, MO_LEUL); + gen_vinsertps(s, decode, tmp); } static void gen_VINSERTx128(DisasContext *s, X86DecodedInsn *decode) @@ -4581,25 +4598,29 @@ static void gen_VMOVSD_ld(DisasContext *s, X86DecodedInsn *decode) static void gen_VMOVSS(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); + TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_ld_i32(s->tmp2_i32, OP_PTR2, offsetof(ZMMReg, ZMM_L(0))); + tcg_gen_ld_i32(tmp, OP_PTR2, offsetof(ZMMReg, ZMM_L(0))); tcg_gen_gvec_mov(MO_64, decode->op[0].offset, decode->op[1].offset, vec_len, vec_len); - tcg_gen_st_i32(s->tmp2_i32, OP_PTR0, offsetof(ZMMReg, ZMM_L(0))); + tcg_gen_st_i32(tmp, OP_PTR0, offsetof(ZMMReg, ZMM_L(0))); } static void gen_VMOVSS_ld(DisasContext *s, X86DecodedInsn *decode) { int vec_len = vector_len(s, decode); + TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_qemu_ld_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); + tcg_gen_qemu_ld_i32(tmp, s->A0, s->mem_index, MO_LEUL); tcg_gen_gvec_dup_imm(MO_64, decode->op[0].offset, vec_len, vec_len, 0); - tcg_gen_st_i32(s->tmp2_i32, OP_PTR0, offsetof(ZMMReg, ZMM_L(0))); + tcg_gen_st_i32(tmp, OP_PTR0, offsetof(ZMMReg, ZMM_L(0))); } static void gen_VMOVSS_st(DisasContext *s, X86DecodedInsn *decode) { - tcg_gen_ld_i32(s->tmp2_i32, OP_PTR2, offsetof(ZMMReg, ZMM_L(0))); - tcg_gen_qemu_st_i32(s->tmp2_i32, s->A0, s->mem_index, MO_LEUL); + TCGv_i32 tmp = tcg_temp_new_i32(); + + tcg_gen_ld_i32(tmp, OP_PTR2, offsetof(ZMMReg, ZMM_L(0))); + tcg_gen_qemu_st_i32(tmp, s->A0, s->mem_index, MO_LEUL); } static void gen_VPMASKMOV_st(DisasContext *s, X86DecodedInsn *decode) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 5d433f8522..abe210cc4e 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -135,7 +135,6 @@ typedef struct DisasContext { /* TCG local register indexes (only used inside old micro ops) */ TCGv_i32 tmp2_i32; - TCGv_i32 tmp3_i32; TCGv_i64 tmp1_i64; sigjmp_buf jmpbuf; @@ -1318,30 +1317,35 @@ static void gen_bpt_io(DisasContext *s, TCGv_i32 t_port, int ot) static void gen_ins(DisasContext *s, MemOp ot, TCGv dshift) { + TCGv_i32 port = tcg_temp_new_i32(); + gen_string_movl_A0_EDI(s); /* Note: we must do this dummy write first to be restartable in case of page fault. */ tcg_gen_movi_tl(s->T0, 0); gen_op_st_v(s, ot, s->T0, s->A0); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); - gen_helper_in_func(ot, s->T0, s->tmp2_i32); + tcg_gen_trunc_tl_i32(port, cpu_regs[R_EDX]); + tcg_gen_andi_i32(port, port, 0xffff); + gen_helper_in_func(ot, s->T0, port); gen_op_st_v(s, ot, s->T0, s->A0); gen_op_add_reg(s, s->aflag, R_EDI, dshift); - gen_bpt_io(s, s->tmp2_i32, ot); + gen_bpt_io(s, port, ot); } static void gen_outs(DisasContext *s, MemOp ot, TCGv dshift) { + TCGv_i32 port = tcg_temp_new_i32(); + TCGv_i32 value = tcg_temp_new_i32(); + gen_string_movl_A0_ESI(s); gen_op_ld_v(s, ot, s->T0, s->A0); - tcg_gen_trunc_tl_i32(s->tmp2_i32, cpu_regs[R_EDX]); - tcg_gen_andi_i32(s->tmp2_i32, s->tmp2_i32, 0xffff); - tcg_gen_trunc_tl_i32(s->tmp3_i32, s->T0); - gen_helper_out_func(ot, s->tmp2_i32, s->tmp3_i32); + tcg_gen_trunc_tl_i32(port, cpu_regs[R_EDX]); + tcg_gen_andi_i32(port, port, 0xffff); + tcg_gen_trunc_tl_i32(value, s->T0); + gen_helper_out_func(ot, port, value); gen_op_add_reg(s, s->aflag, R_ESI, dshift); - gen_bpt_io(s, s->tmp2_i32, ot); + gen_bpt_io(s, port, ot); } #define REP_MAX 65535 @@ -1869,14 +1873,16 @@ static void gen_bndck(DisasContext *s, X86DecodedInsn *decode, TCGCond cond, TCGv_i64 bndv) { TCGv ea = gen_lea_modrm_1(s, decode->mem, false); + TCGv_i32 t32 = tcg_temp_new_i32(); + TCGv_i64 t64 = tcg_temp_new_i64(); - tcg_gen_extu_tl_i64(s->tmp1_i64, ea); + tcg_gen_extu_tl_i64(t64, ea); if (!CODE64(s)) { - tcg_gen_ext32u_i64(s->tmp1_i64, s->tmp1_i64); + tcg_gen_ext32u_i64(t64, t64); } - tcg_gen_setcond_i64(cond, s->tmp1_i64, s->tmp1_i64, bndv); - tcg_gen_extrl_i64_i32(s->tmp2_i32, s->tmp1_i64); - gen_helper_bndck(tcg_env, s->tmp2_i32); + tcg_gen_setcond_i64(cond, t64, t64, bndv); + tcg_gen_extrl_i64_i32(t32, t64); + gen_helper_bndck(tcg_env, t32); } /* generate modrm load of memory or register. */ @@ -2021,8 +2027,10 @@ static void gen_op_movl_seg_real(DisasContext *s, X86Seg seg_reg, TCGv seg) static void gen_movl_seg(DisasContext *s, X86Seg seg_reg, TCGv src) { if (PE(s) && !VM86(s)) { - tcg_gen_trunc_tl_i32(s->tmp2_i32, src); - gen_helper_load_seg(tcg_env, tcg_constant_i32(seg_reg), s->tmp2_i32); + TCGv_i32 sel = tcg_temp_new_i32(); + + tcg_gen_trunc_tl_i32(sel, src); + gen_helper_load_seg(tcg_env, tcg_constant_i32(seg_reg), sel); /* abort translation because the addseg value may change or because ss32 may change. For R_SS, translation must always stop as a special handling must be done to disable hardware @@ -3777,7 +3785,6 @@ static void i386_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cpu) dc->tmp1_i64 = tcg_temp_new_i64(); dc->tmp2_i32 = tcg_temp_new_i32(); - dc->tmp3_i32 = tcg_temp_new_i32(); dc->cc_srcT = tcg_temp_new(); } From 3fec86e95cd44ac7db639686ef1dda143af59aea Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 3 Apr 2025 14:06:37 +0200 Subject: [PATCH 0016/2760] target/i386: tcg: simplify computation of AF after INC/DEC No difference in generated code, but the XOR-based formula is easily understood on its own. This will make more sense once ADD/SUB stop using dst^src1^src2 to compute AF. Signed-off-by: Paolo Bonzini --- target/i386/tcg/cc_helper_template.h.inc | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/target/i386/tcg/cc_helper_template.h.inc b/target/i386/tcg/cc_helper_template.h.inc index 9aff16b880..b821e5bca3 100644 --- a/target/i386/tcg/cc_helper_template.h.inc +++ b/target/i386/tcg/cc_helper_template.h.inc @@ -175,13 +175,10 @@ static uint32_t glue(compute_all_logic, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) static uint32_t glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { uint32_t cf, pf, af, zf, sf, of; - DATA_TYPE src2; cf = src1; - src1 = dst - 1; - src2 = 1; pf = compute_pf(dst); - af = (dst ^ src1 ^ src2) & CC_A; + af = (dst ^ (dst - 1)) & CC_A; /* bits 0..3 are all clear */ zf = (dst == 0) * CC_Z; sf = lshift(dst, 8 - DATA_BITS) & CC_S; of = (dst == SIGN_MASK) * CC_O; @@ -191,13 +188,10 @@ static uint32_t glue(compute_all_inc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) static uint32_t glue(compute_all_dec, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) { uint32_t cf, pf, af, zf, sf, of; - DATA_TYPE src2; cf = src1; - src1 = dst + 1; - src2 = 1; pf = compute_pf(dst); - af = (dst ^ src1 ^ src2) & CC_A; + af = (dst ^ (dst + 1)) & CC_A; /* bits 0..3 are all set */ zf = (dst == 0) * CC_Z; sf = lshift(dst, 8 - DATA_BITS) & CC_S; of = (dst == SIGN_MASK - 1) * CC_O; From 767149d3d078356073a32238b313cee9d02db5d8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 20 Mar 2025 14:55:42 +0100 Subject: [PATCH 0017/2760] target/i386: emulate: microoptimize and explain ADD_COUT_VEC/SUB_COUT_VEC The logic is the same, but the majority(NOT a, b, c) is brought out to a separate macro and implemented without NOT operations. Signed-off-by: Paolo Bonzini --- target/i386/hvf/x86_flags.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/target/i386/hvf/x86_flags.c b/target/i386/hvf/x86_flags.c index fedc70a1b8..60ab4f01a2 100644 --- a/target/i386/hvf/x86_flags.c +++ b/target/i386/hvf/x86_flags.c @@ -45,14 +45,30 @@ #define LF_MASK_CF (0x01 << LF_BIT_CF) #define LF_MASK_PO (0x01 << LF_BIT_PO) +/* majority(NOT a, b, c) = (a ^ b) ? b : c */ +#define MAJ_INV1(a, b, c) ((((a) ^ (b)) & ((b) ^ (c))) ^ (c)) + +/* + * ADD_COUT_VEC(x, y) = majority((x + y) ^ x ^ y, x, y) + * + * If two corresponding bits in x and y are the same, that's the carry + * independent of the value (x+y)^x^y. Hence x^y can be replaced with + * 1 in (x+y)^x^y, resulting in majority(NOT (x+y), x, y) + */ #define ADD_COUT_VEC(op1, op2, result) \ - (((op1) & (op2)) | (((op1) | (op2)) & (~(result)))) + MAJ_INV1(result, op1, op2) +/* + * SUB_COUT_VEC(x, y) = NOT majority(x, NOT y, (x - y) ^ x ^ NOT y) + * = majority(NOT x, y, (x - y) ^ x ^ y) + * + * Note that the carry out is actually a borrow, i.e. it is inverted. + * If two corresponding bits in x and y are different, the value of the + * bit in (x-y)^x^y likewise does not matter. Hence, x^y can be replaced + * with 0 in (x-y)^x^y, resulting in majority(NOT x, y, x-y) + */ #define SUB_COUT_VEC(op1, op2, result) \ - (((~(op1)) & (op2)) | (((~(op1)) ^ (op2)) & (result))) - -#define GET_ADD_OVERFLOW(op1, op2, result, mask) \ - ((((op1) ^ (result)) & ((op2) ^ (result))) & (mask)) + MAJ_INV1(op1, op2, result) /* ******************* */ /* OSZAPC */ From 5dcdbd071253e249a76c7771bcf78eca3763a131 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 3 Apr 2025 18:20:06 +0200 Subject: [PATCH 0018/2760] target/i386: tcg: use cout to commonize add/adc/sub/sbb cases Use the carry-out vector as the basis to compute AF, CF and OF. The cost is pretty much the same, because the carry-out is just four boolean operations, and the code is much smaller because add/adc/sub/sbb now share most of it. A similar algorithm to what is used in target/i386/emulate can also be used for APX, in order to build the result of CCMP/CTEST with a new CC_OP_*. CCMP needs to place into the flags from either a subtraction or a constant value; CTEST likewise place into the flags either an AND or a constant value. The new CC_OP for CCMP and CTEST would store for a successful predcate: - in DST and SRC2, the result of the operation; - in SRC, a carry-out vector for CCMP or zero for CTEST; If the default flag value is used, DST/SRC/SRC2 can be filled with constants: - in DST the negated ZF; - in SRC's top 2 bits, a value that results in the desired OF and CF; - in SRC2 a suitable value (any of 0/1/~0/~1) that can be used instead of DST to compute the desired SF and PF. Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 25 ++++++++ target/i386/hvf/x86_flags.c | 25 -------- target/i386/tcg/cc_helper_template.h.inc | 80 ++++++++---------------- 3 files changed, 52 insertions(+), 78 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 76f24446a5..7a8d695bdb 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2843,4 +2843,29 @@ static inline bool ctl_has_irq(CPUX86State *env) # define TARGET_VSYSCALL_PAGE (UINT64_C(-10) << 20) #endif +/* majority(NOT a, b, c) = (a ^ b) ? b : c */ +#define MAJ_INV1(a, b, c) ((((a) ^ (b)) & ((b) ^ (c))) ^ (c)) + +/* + * ADD_COUT_VEC(x, y) = majority((x + y) ^ x ^ y, x, y) + * + * If two corresponding bits in x and y are the same, that's the carry + * independent of the value (x+y)^x^y. Hence x^y can be replaced with + * 1 in (x+y)^x^y, resulting in majority(NOT (x+y), x, y) + */ +#define ADD_COUT_VEC(op1, op2, result) \ + MAJ_INV1(result, op1, op2) + +/* + * SUB_COUT_VEC(x, y) = NOT majority(x, NOT y, (x - y) ^ x ^ NOT y) + * = majority(NOT x, y, (x - y) ^ x ^ y) + * + * Note that the carry out is actually a borrow, i.e. it is inverted. + * If two corresponding bits in x and y are different, the value of the + * bit in (x-y)^x^y likewise does not matter. Hence, x^y can be replaced + * with 0 in (x-y)^x^y, resulting in majority(NOT x, y, x-y) + */ +#define SUB_COUT_VEC(op1, op2, result) \ + MAJ_INV1(op1, op2, result) + #endif /* I386_CPU_H */ diff --git a/target/i386/hvf/x86_flags.c b/target/i386/hvf/x86_flags.c index 60ab4f01a2..0c75e0419c 100644 --- a/target/i386/hvf/x86_flags.c +++ b/target/i386/hvf/x86_flags.c @@ -45,31 +45,6 @@ #define LF_MASK_CF (0x01 << LF_BIT_CF) #define LF_MASK_PO (0x01 << LF_BIT_PO) -/* majority(NOT a, b, c) = (a ^ b) ? b : c */ -#define MAJ_INV1(a, b, c) ((((a) ^ (b)) & ((b) ^ (c))) ^ (c)) - -/* - * ADD_COUT_VEC(x, y) = majority((x + y) ^ x ^ y, x, y) - * - * If two corresponding bits in x and y are the same, that's the carry - * independent of the value (x+y)^x^y. Hence x^y can be replaced with - * 1 in (x+y)^x^y, resulting in majority(NOT (x+y), x, y) - */ -#define ADD_COUT_VEC(op1, op2, result) \ - MAJ_INV1(result, op1, op2) - -/* - * SUB_COUT_VEC(x, y) = NOT majority(x, NOT y, (x - y) ^ x ^ NOT y) - * = majority(NOT x, y, (x - y) ^ x ^ y) - * - * Note that the carry out is actually a borrow, i.e. it is inverted. - * If two corresponding bits in x and y are different, the value of the - * bit in (x-y)^x^y likewise does not matter. Hence, x^y can be replaced - * with 0 in (x-y)^x^y, resulting in majority(NOT x, y, x-y) - */ -#define SUB_COUT_VEC(op1, op2, result) \ - MAJ_INV1(op1, op2, result) - /* ******************* */ /* OSZAPC */ /* ******************* */ diff --git a/target/i386/tcg/cc_helper_template.h.inc b/target/i386/tcg/cc_helper_template.h.inc index b821e5bca3..d8fd976ca1 100644 --- a/target/i386/tcg/cc_helper_template.h.inc +++ b/target/i386/tcg/cc_helper_template.h.inc @@ -44,18 +44,32 @@ /* dynamic flags computation */ -static uint32_t glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +static uint32_t glue(compute_all_cout, SUFFIX)(DATA_TYPE dst, DATA_TYPE carries) { - uint32_t cf, pf, af, zf, sf, of; - DATA_TYPE src2 = dst - src1; + uint32_t af_cf, pf, zf, sf, of; - cf = dst < src1; + /* PF, ZF, SF computed from result. */ pf = compute_pf(dst); - af = (dst ^ src1 ^ src2) & CC_A; zf = (dst == 0) * CC_Z; sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; - return cf + pf + af + zf + sf + of; + + /* + * AF, CF, OF computed from carry out vector. To compute AF and CF, rotate it + * left by one so cout(DATA_BITS - 1) is in bit 0 and cout(3) in bit 4. + * + * To compute OF, place the highest two carry bits into OF and the bit + * immediately to the right of it; then, adding CC_O / 2 XORs them. + */ + af_cf = ((carries << 1) | (carries >> (DATA_BITS - 1))) & (CC_A | CC_C); + of = (lshift(carries, 12 - DATA_BITS) + CC_O / 2) & CC_O; + return pf + zf + sf + af_cf + of; +} + +static uint32_t glue(compute_all_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) +{ + DATA_TYPE src2 = dst - src1; + DATA_TYPE carries = ADD_COUT_VEC(src1, src2, dst); + return glue(compute_all_cout, SUFFIX)(dst, carries); } static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) @@ -66,25 +80,9 @@ static int glue(compute_c_add, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) static uint32_t glue(compute_all_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, DATA_TYPE src3) { - uint32_t cf, pf, af, zf, sf, of; - -#ifdef WIDER_TYPE - WIDER_TYPE src13 = (WIDER_TYPE) src1 + (WIDER_TYPE) src3; - DATA_TYPE src2 = dst - src13; - - cf = dst < src13; -#else DATA_TYPE src2 = dst - src1 - src3; - - cf = (src3 ? dst <= src1 : dst < src1); -#endif - - pf = compute_pf(dst); - af = (dst ^ src1 ^ src2) & 0x10; - zf = (dst == 0) << 6; - sf = lshift(dst, 8 - DATA_BITS) & 0x80; - of = lshift((src1 ^ src2 ^ -1) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; - return cf + pf + af + zf + sf + of; + DATA_TYPE carries = ADD_COUT_VEC(src1, src2, dst); + return glue(compute_all_cout, SUFFIX)(dst, carries); } static int glue(compute_c_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, @@ -101,16 +99,9 @@ static int glue(compute_c_adc, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1, static uint32_t glue(compute_all_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) { - uint32_t cf, pf, af, zf, sf, of; DATA_TYPE src1 = dst + src2; - - cf = src1 < src2; - pf = compute_pf(dst); - af = (dst ^ src1 ^ src2) & CC_A; - zf = (dst == 0) * CC_Z; - sf = lshift(dst, 8 - DATA_BITS) & CC_S; - of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; - return cf + pf + af + zf + sf + of; + DATA_TYPE carries = SUB_COUT_VEC(src1, src2, dst); + return glue(compute_all_cout, SUFFIX)(dst, carries); } static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) @@ -123,25 +114,9 @@ static int glue(compute_c_sub, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2) static uint32_t glue(compute_all_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, DATA_TYPE src3) { - uint32_t cf, pf, af, zf, sf, of; - -#ifdef WIDER_TYPE - WIDER_TYPE src23 = (WIDER_TYPE) src2 + (WIDER_TYPE) src3; - DATA_TYPE src1 = dst + src23; - - cf = src1 < src23; -#else DATA_TYPE src1 = dst + src2 + src3; - - cf = (src3 ? src1 <= src2 : src1 < src2); -#endif - - pf = compute_pf(dst); - af = (dst ^ src1 ^ src2) & 0x10; - zf = (dst == 0) << 6; - sf = lshift(dst, 8 - DATA_BITS) & 0x80; - of = lshift((src1 ^ src2) & (src1 ^ dst), 12 - DATA_BITS) & CC_O; - return cf + pf + af + zf + sf + of; + DATA_TYPE carries = SUB_COUT_VEC(src1, src2, dst); + return glue(compute_all_cout, SUFFIX)(dst, carries); } static int glue(compute_c_sbb, SUFFIX)(DATA_TYPE dst, DATA_TYPE src2, @@ -286,6 +261,5 @@ static int glue(compute_c_blsi, SUFFIX)(DATA_TYPE dst, DATA_TYPE src1) #undef DATA_BITS #undef SIGN_MASK #undef DATA_TYPE -#undef DATA_MASK #undef SUFFIX #undef WIDER_TYPE From 26a44d9d2d4296701ceebc1085ab28171a1e7e3b Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:12 -0800 Subject: [PATCH 0019/2760] target/i386/hvf: introduce x86_emul_ops This will be used to remove HVF specific code from the instruction emulator. For now we only introduce two hooks for x86_decode.c. More hooks will be added when the code is refactored. The emulator initialization function now takes in a pointer to the ops structure. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-2-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf.c | 20 +++++++++++++++++++- target/i386/hvf/x86_emu.c | 5 ++++- target/i386/hvf/x86_emu.h | 10 +++++++++- 3 files changed, 32 insertions(+), 3 deletions(-) diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 9ba0e04ac7..03456ffbc7 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -229,6 +229,24 @@ hv_return_t hvf_arch_vm_create(MachineState *ms, uint32_t pa_range) return hv_vm_create(HV_VM_DEFAULT); } +static void hvf_read_segment_descriptor(CPUState *s, struct x86_segment_descriptor *desc, + X86Seg seg) +{ + struct vmx_segment vmx_segment; + vmx_read_segment_descriptor(s, &vmx_segment, seg); + vmx_segment_to_x86_descriptor(s, &vmx_segment, desc); +} + +static void hvf_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes) +{ + vmx_read_mem(cpu, data, gva, bytes); +} + +static const struct x86_emul_ops hvf_x86_emul_ops = { + .read_mem = hvf_read_mem, + .read_segment_descriptor = hvf_read_segment_descriptor, +}; + int hvf_arch_init_vcpu(CPUState *cpu) { X86CPU *x86cpu = X86_CPU(cpu); @@ -237,7 +255,7 @@ int hvf_arch_init_vcpu(CPUState *cpu) int r; uint64_t reqCap; - init_emu(); + init_emu(&hvf_x86_emul_ops); init_decoder(); if (hvf_state->hvf_caps == NULL) { diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index ebba80a36b..c15b5a7ca8 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -1231,6 +1231,8 @@ static struct cmd_handler { static struct cmd_handler _cmd_handler[X86_DECODE_CMD_LAST]; +const struct x86_emul_ops *emul_ops; + static void init_cmd_handler(void) { int i; @@ -1253,7 +1255,8 @@ bool exec_instruction(CPUX86State *env, struct x86_decode *ins) return true; } -void init_emu(void) +void init_emu(const struct x86_emul_ops *o) { + emul_ops = o; init_cmd_handler(); } diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h index bc0fc72c76..1422d06ea1 100644 --- a/target/i386/hvf/x86_emu.h +++ b/target/i386/hvf/x86_emu.h @@ -23,7 +23,15 @@ #include "x86_decode.h" #include "cpu.h" -void init_emu(void); +struct x86_emul_ops { + void (*read_mem)(CPUState *cpu, void *data, target_ulong addr, int bytes); + void (*read_segment_descriptor)(CPUState *cpu, struct x86_segment_descriptor *desc, + enum X86Seg seg); +}; + +extern const struct x86_emul_ops *emul_ops; + +void init_emu(const struct x86_emul_ops *ops); bool exec_instruction(CPUX86State *env, struct x86_decode *ins); void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_code); From 0860abbe849e7345fee28c08fa200f7cc315f175 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:13 -0800 Subject: [PATCH 0020/2760] target/i386/hvf: remove HVF specific calls from x86_decode.c Use the newly defined emul_ops. This allows the module to be reused by other accelerator in the future. No functional change intended. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-3-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/x86_decode.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/target/i386/hvf/x86_decode.c b/target/i386/hvf/x86_decode.c index 5fea2dd3cc..728e159638 100644 --- a/target/i386/hvf/x86_decode.c +++ b/target/i386/hvf/x86_decode.c @@ -21,6 +21,7 @@ #include "panic.h" #include "x86_decode.h" #include "vmx.h" +#include "x86_emu.h" #include "x86_mmu.h" #include "x86_descr.h" @@ -74,7 +75,7 @@ static inline uint64_t decode_bytes(CPUX86State *env, struct x86_decode *decode, break; } target_ulong va = linear_rip(env_cpu(env), env->eip) + decode->len; - vmx_read_mem(env_cpu(env), &val, va, size); + emul_ops->read_mem(env_cpu(env), &val, va, size); decode->len += size; return val; @@ -1893,16 +1894,6 @@ static void decode_prefix(CPUX86State *env, struct x86_decode *decode) } } -static struct x86_segment_descriptor get_cs_descriptor(CPUState *s) -{ - struct vmx_segment vmx_cs; - x86_segment_descriptor cs; - vmx_read_segment_descriptor(s, &vmx_cs, R_CS); - vmx_segment_to_x86_descriptor(s, &vmx_cs, &cs); - - return cs; -} - void set_addressing_size(CPUX86State *env, struct x86_decode *decode) { decode->addressing_size = -1; @@ -1914,7 +1905,8 @@ void set_addressing_size(CPUX86State *env, struct x86_decode *decode) } } else if (!x86_is_long_mode(env_cpu(env))) { /* protected */ - x86_segment_descriptor cs = get_cs_descriptor(env_cpu(env)); + x86_segment_descriptor cs; + emul_ops->read_segment_descriptor(env_cpu(env), &cs, R_CS); /* check db */ if (cs.db) { if (decode->addr_size_override) { @@ -1950,7 +1942,8 @@ void set_operand_size(CPUX86State *env, struct x86_decode *decode) } } else if (!x86_is_long_mode(env_cpu(env))) { /* protected */ - x86_segment_descriptor cs = get_cs_descriptor(env_cpu(env)); + x86_segment_descriptor cs; + emul_ops->read_segment_descriptor(env_cpu(env), &cs, R_CS); /* check db */ if (cs.db) { if (decode->op_size_override) { From 444bae08bbdae175b14cc96a11af8640eb262963 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:14 -0800 Subject: [PATCH 0021/2760] target/i386/hvf: provide and use handle_io in emul_ops This drops the calls to hvf_handle_io from x86_emu.c. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-4-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf.c | 1 + target/i386/hvf/x86_emu.c | 29 +++++++++++++++-------------- target/i386/hvf/x86_emu.h | 2 ++ 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 03456ffbc7..7da03f9c08 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -245,6 +245,7 @@ static void hvf_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes) static const struct x86_emul_ops hvf_x86_emul_ops = { .read_mem = hvf_read_mem, .read_segment_descriptor = hvf_read_segment_descriptor, + .handle_io = hvf_handle_io, }; int hvf_arch_init_vcpu(CPUState *cpu) diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index c15b5a7ca8..7b01ccde5d 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -396,18 +396,18 @@ static void exec_out(CPUX86State *env, struct x86_decode *decode) { switch (decode->opcode[0]) { case 0xe6: - hvf_handle_io(env_cpu(env), decode->op[0].val, &AL(env), 1, 1, 1); + emul_ops->handle_io(env_cpu(env), decode->op[0].val, &AL(env), 1, 1, 1); break; case 0xe7: - hvf_handle_io(env_cpu(env), decode->op[0].val, &RAX(env), 1, - decode->operand_size, 1); + emul_ops->handle_io(env_cpu(env), decode->op[0].val, &RAX(env), 1, + decode->operand_size, 1); break; case 0xee: - hvf_handle_io(env_cpu(env), DX(env), &AL(env), 1, 1, 1); + emul_ops->handle_io(env_cpu(env), DX(env), &AL(env), 1, 1, 1); break; case 0xef: - hvf_handle_io(env_cpu(env), DX(env), &RAX(env), 1, - decode->operand_size, 1); + emul_ops->handle_io(env_cpu(env), DX(env), &RAX(env), 1, + decode->operand_size, 1); break; default: VM_PANIC("Bad out opcode\n"); @@ -421,10 +421,10 @@ static void exec_in(CPUX86State *env, struct x86_decode *decode) target_ulong val = 0; switch (decode->opcode[0]) { case 0xe4: - hvf_handle_io(env_cpu(env), decode->op[0].val, &AL(env), 0, 1, 1); + emul_ops->handle_io(env_cpu(env), decode->op[0].val, &AL(env), 0, 1, 1); break; case 0xe5: - hvf_handle_io(env_cpu(env), decode->op[0].val, &val, 0, + emul_ops->handle_io(env_cpu(env), decode->op[0].val, &val, 0, decode->operand_size, 1); if (decode->operand_size == 2) { AX(env) = val; @@ -433,10 +433,11 @@ static void exec_in(CPUX86State *env, struct x86_decode *decode) } break; case 0xec: - hvf_handle_io(env_cpu(env), DX(env), &AL(env), 0, 1, 1); + emul_ops->handle_io(env_cpu(env), DX(env), &AL(env), 0, 1, 1); break; case 0xed: - hvf_handle_io(env_cpu(env), DX(env), &val, 0, decode->operand_size, 1); + emul_ops->handle_io(env_cpu(env), DX(env), &val, 0, + decode->operand_size, 1); if (decode->operand_size == 2) { AX(env) = val; } else { @@ -486,8 +487,8 @@ static void exec_ins_single(CPUX86State *env, struct x86_decode *decode) target_ulong addr = linear_addr_size(env_cpu(env), RDI(env), decode->addressing_size, R_ES); - hvf_handle_io(env_cpu(env), DX(env), env->hvf_mmio_buf, 0, - decode->operand_size, 1); + emul_ops->handle_io(env_cpu(env), DX(env), env->hvf_mmio_buf, 0, + decode->operand_size, 1); vmx_write_mem(env_cpu(env), addr, env->hvf_mmio_buf, decode->operand_size); @@ -511,8 +512,8 @@ static void exec_outs_single(CPUX86State *env, struct x86_decode *decode) vmx_read_mem(env_cpu(env), env->hvf_mmio_buf, addr, decode->operand_size); - hvf_handle_io(env_cpu(env), DX(env), env->hvf_mmio_buf, 1, - decode->operand_size, 1); + emul_ops->handle_io(env_cpu(env), DX(env), env->hvf_mmio_buf, 1, + decode->operand_size, 1); string_increment_reg(env, R_ESI, decode); } diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h index 1422d06ea1..40cc786694 100644 --- a/target/i386/hvf/x86_emu.h +++ b/target/i386/hvf/x86_emu.h @@ -27,6 +27,8 @@ struct x86_emul_ops { void (*read_mem)(CPUState *cpu, void *data, target_ulong addr, int bytes); void (*read_segment_descriptor)(CPUState *cpu, struct x86_segment_descriptor *desc, enum X86Seg seg); + void (*handle_io)(CPUState *cpu, uint16_t port, void *data, int direction, + int size, int count); }; extern const struct x86_emul_ops *emul_ops; From e9c40026b641da21c96c877153cbe08706b2aac9 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:15 -0800 Subject: [PATCH 0022/2760] target/i386: rename hvf_mmio_buf to emu_mmio_buf We want to refactor HVF's instruction emulator to a common component. Renaming hvf_mmio_buf removes the association between HVF and the instruction emulator. The definition of the field is still guarded by CONFIG_HVF for now, since it is the only user. No functional change. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-5-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 2 +- target/i386/hvf/hvf.c | 4 ++-- target/i386/hvf/x86_emu.c | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 7a8d695bdb..3c5c39ce3d 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2109,7 +2109,7 @@ typedef struct CPUArchState { #endif #if defined(CONFIG_HVF) HVFX86LazyFlags hvf_lflags; - void *hvf_mmio_buf; + void *emu_mmio_buf; #endif uint64_t mcg_cap; diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 7da03f9c08..1cecb76595 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -168,7 +168,7 @@ void hvf_arch_vcpu_destroy(CPUState *cpu) X86CPU *x86_cpu = X86_CPU(cpu); CPUX86State *env = &x86_cpu->env; - g_free(env->hvf_mmio_buf); + g_free(env->emu_mmio_buf); } static void init_tsc_freq(CPUX86State *env) @@ -262,7 +262,7 @@ int hvf_arch_init_vcpu(CPUState *cpu) if (hvf_state->hvf_caps == NULL) { hvf_state->hvf_caps = g_new0(struct hvf_vcpu_caps, 1); } - env->hvf_mmio_buf = g_new(char, 4096); + env->emu_mmio_buf = g_new(char, 4096); if (x86cpu->vmware_cpuid_freq) { init_tsc_freq(env); diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 7b01ccde5d..e59a73e00d 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -184,8 +184,8 @@ void write_val_ext(CPUX86State *env, target_ulong ptr, target_ulong val, int siz uint8_t *read_mmio(CPUX86State *env, target_ulong ptr, int bytes) { - vmx_read_mem(env_cpu(env), env->hvf_mmio_buf, ptr, bytes); - return env->hvf_mmio_buf; + vmx_read_mem(env_cpu(env), env->emu_mmio_buf, ptr, bytes); + return env->emu_mmio_buf; } @@ -487,9 +487,9 @@ static void exec_ins_single(CPUX86State *env, struct x86_decode *decode) target_ulong addr = linear_addr_size(env_cpu(env), RDI(env), decode->addressing_size, R_ES); - emul_ops->handle_io(env_cpu(env), DX(env), env->hvf_mmio_buf, 0, + emul_ops->handle_io(env_cpu(env), DX(env), env->emu_mmio_buf, 0, decode->operand_size, 1); - vmx_write_mem(env_cpu(env), addr, env->hvf_mmio_buf, + vmx_write_mem(env_cpu(env), addr, env->emu_mmio_buf, decode->operand_size); string_increment_reg(env, R_EDI, decode); @@ -510,9 +510,9 @@ static void exec_outs_single(CPUX86State *env, struct x86_decode *decode) { target_ulong addr = decode_linear_addr(env, decode, RSI(env), R_DS); - vmx_read_mem(env_cpu(env), env->hvf_mmio_buf, addr, + vmx_read_mem(env_cpu(env), env->emu_mmio_buf, addr, decode->operand_size); - emul_ops->handle_io(env_cpu(env), DX(env), env->hvf_mmio_buf, 1, + emul_ops->handle_io(env_cpu(env), DX(env), env->emu_mmio_buf, 1, decode->operand_size, 1); string_increment_reg(env, R_ESI, decode); From ae3c6134ecb4c7d3ba1c6bae3ff5b5dd6cf05d56 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:16 -0800 Subject: [PATCH 0023/2760] target/i386/hvf: use emul_ops->read_mem in x86_emu.c No functional change. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-6-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/x86_emu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index e59a73e00d..7b816b5a1d 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -184,7 +184,7 @@ void write_val_ext(CPUX86State *env, target_ulong ptr, target_ulong val, int siz uint8_t *read_mmio(CPUX86State *env, target_ulong ptr, int bytes) { - vmx_read_mem(env_cpu(env), env->emu_mmio_buf, ptr, bytes); + emul_ops->read_mem(env_cpu(env), env->emu_mmio_buf, ptr, bytes); return env->emu_mmio_buf; } @@ -510,8 +510,8 @@ static void exec_outs_single(CPUX86State *env, struct x86_decode *decode) { target_ulong addr = decode_linear_addr(env, decode, RSI(env), R_DS); - vmx_read_mem(env_cpu(env), env->emu_mmio_buf, addr, - decode->operand_size); + emul_ops->read_mem(env_cpu(env), env->emu_mmio_buf, addr, + decode->operand_size); emul_ops->handle_io(env_cpu(env), DX(env), env->emu_mmio_buf, 1, decode->operand_size, 1); @@ -620,7 +620,7 @@ static void exec_scas_single(CPUX86State *env, struct x86_decode *decode) addr = linear_addr_size(env_cpu(env), RDI(env), decode->addressing_size, R_ES); decode->op[1].type = X86_VAR_IMMEDIATE; - vmx_read_mem(env_cpu(env), &decode->op[1].val, addr, decode->operand_size); + emul_ops->read_mem(env_cpu(env), &decode->op[1].val, addr, decode->operand_size); EXEC_2OP_FLAGS_CMD(env, decode, -, SET_FLAGS_OSZAPC_SUB, false); string_increment_reg(env, R_EDI, decode); @@ -645,7 +645,7 @@ static void exec_lods_single(CPUX86State *env, struct x86_decode *decode) target_ulong val = 0; addr = decode_linear_addr(env, decode, RSI(env), R_DS); - vmx_read_mem(env_cpu(env), &val, addr, decode->operand_size); + emul_ops->read_mem(env_cpu(env), &val, addr, decode->operand_size); write_reg(env, R_EAX, val, decode->operand_size); string_increment_reg(env, R_ESI, decode); From 63d8bc669302ec22bd394c45380848a2d5947943 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:17 -0800 Subject: [PATCH 0024/2760] target/i386/hvf: provide and use write_mem in emul_ops Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-7-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf.c | 6 ++++++ target/i386/hvf/x86_emu.c | 8 ++++---- target/i386/hvf/x86_emu.h | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 1cecb76595..e4f48a79fb 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -242,8 +242,14 @@ static void hvf_read_mem(CPUState *cpu, void *data, target_ulong gva, int bytes) vmx_read_mem(cpu, data, gva, bytes); } +static void hvf_write_mem(CPUState *cpu, void *data, target_ulong gva, int bytes) +{ + vmx_write_mem(cpu, gva, data, bytes); +} + static const struct x86_emul_ops hvf_x86_emul_ops = { .read_mem = hvf_read_mem, + .write_mem = hvf_write_mem, .read_segment_descriptor = hvf_read_segment_descriptor, .handle_io = hvf_handle_io, }; diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 7b816b5a1d..3ff41c35d8 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -179,7 +179,7 @@ void write_val_ext(CPUX86State *env, target_ulong ptr, target_ulong val, int siz write_val_to_reg(ptr, val, size); return; } - vmx_write_mem(env_cpu(env), ptr, &val, size); + emul_ops->write_mem(env_cpu(env), &val, ptr, size); } uint8_t *read_mmio(CPUX86State *env, target_ulong ptr, int bytes) @@ -489,8 +489,8 @@ static void exec_ins_single(CPUX86State *env, struct x86_decode *decode) emul_ops->handle_io(env_cpu(env), DX(env), env->emu_mmio_buf, 0, decode->operand_size, 1); - vmx_write_mem(env_cpu(env), addr, env->emu_mmio_buf, - decode->operand_size); + emul_ops->write_mem(env_cpu(env), env->emu_mmio_buf, addr, + decode->operand_size); string_increment_reg(env, R_EDI, decode); } @@ -596,7 +596,7 @@ static void exec_stos_single(CPUX86State *env, struct x86_decode *decode) addr = linear_addr_size(env_cpu(env), RDI(env), decode->addressing_size, R_ES); val = read_reg(env, R_EAX, decode->operand_size); - vmx_write_mem(env_cpu(env), addr, &val, decode->operand_size); + emul_ops->write_mem(env_cpu(env), &val, addr, decode->operand_size); string_increment_reg(env, R_EDI, decode); } diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h index 40cc786694..107c1f1ac8 100644 --- a/target/i386/hvf/x86_emu.h +++ b/target/i386/hvf/x86_emu.h @@ -25,6 +25,7 @@ struct x86_emul_ops { void (*read_mem)(CPUState *cpu, void *data, target_ulong addr, int bytes); + void (*write_mem)(CPUState *cpu, void *data, target_ulong addr, int bytes); void (*read_segment_descriptor)(CPUState *cpu, struct x86_segment_descriptor *desc, enum X86Seg seg); void (*handle_io)(CPUState *cpu, uint16_t port, void *data, int direction, From 585678640c778ee35a86638eacb22372bda70ee1 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:18 -0800 Subject: [PATCH 0025/2760] target/i386/hvf: provide and use simulate_{wrmsr, rdmsr} in emul_ops Change the first argument's type to be CPUState to match other hooks. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-8-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf-i386.h | 4 ++-- target/i386/hvf/hvf.c | 18 ++++++++++-------- target/i386/hvf/x86_emu.c | 4 ++-- target/i386/hvf/x86_emu.h | 2 ++ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/target/i386/hvf/hvf-i386.h b/target/i386/hvf/hvf-i386.h index 044ad236ae..8c42ae6b01 100644 --- a/target/i386/hvf/hvf-i386.h +++ b/target/i386/hvf/hvf-i386.h @@ -19,8 +19,8 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, int reg); void hvf_handle_io(CPUState *, uint16_t, void *, int, int, int); -void hvf_simulate_rdmsr(CPUX86State *env); -void hvf_simulate_wrmsr(CPUX86State *env); +void hvf_simulate_rdmsr(CPUState *cpu); +void hvf_simulate_wrmsr(CPUState *cpu); /* Host specific functions */ int hvf_inject_interrupt(CPUArchState *env, int vector); diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index e4f48a79fb..8c31d2e0cf 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -252,6 +252,8 @@ static const struct x86_emul_ops hvf_x86_emul_ops = { .write_mem = hvf_write_mem, .read_segment_descriptor = hvf_read_segment_descriptor, .handle_io = hvf_handle_io, + .simulate_rdmsr = hvf_simulate_rdmsr, + .simulate_wrmsr = hvf_simulate_wrmsr, }; int hvf_arch_init_vcpu(CPUState *cpu) @@ -506,10 +508,10 @@ void hvf_store_regs(CPUState *cs) macvm_set_rip(cs, env->eip); } -void hvf_simulate_rdmsr(CPUX86State *env) +void hvf_simulate_rdmsr(CPUState *cs) { - X86CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; uint32_t msr = ECX(env); uint64_t val = 0; @@ -611,10 +613,10 @@ void hvf_simulate_rdmsr(CPUX86State *env) RDX(env) = (uint32_t)(val >> 32); } -void hvf_simulate_wrmsr(CPUX86State *env) +void hvf_simulate_wrmsr(CPUState *cs) { - X86CPU *cpu = env_archcpu(env); - CPUState *cs = env_cpu(env); + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; uint32_t msr = ECX(env); uint64_t data = ((uint64_t)EDX(env) << 32) | EAX(env); @@ -900,9 +902,9 @@ int hvf_vcpu_exec(CPUState *cpu) { hvf_load_regs(cpu); if (exit_reason == EXIT_REASON_RDMSR) { - hvf_simulate_rdmsr(env); + hvf_simulate_rdmsr(cpu); } else { - hvf_simulate_wrmsr(env); + hvf_simulate_wrmsr(cpu); } env->eip += ins_len; hvf_store_regs(cpu); diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index 3ff41c35d8..aec7a8a3fa 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -672,13 +672,13 @@ void x86_emul_raise_exception(CPUX86State *env, int exception_index, int error_c static void exec_rdmsr(CPUX86State *env, struct x86_decode *decode) { - hvf_simulate_rdmsr(env); + emul_ops->simulate_rdmsr(env_cpu(env)); env->eip += decode->len; } static void exec_wrmsr(CPUX86State *env, struct x86_decode *decode) { - hvf_simulate_wrmsr(env); + emul_ops->simulate_wrmsr(env_cpu(env)); env->eip += decode->len; } diff --git a/target/i386/hvf/x86_emu.h b/target/i386/hvf/x86_emu.h index 107c1f1ac8..555b567e2c 100644 --- a/target/i386/hvf/x86_emu.h +++ b/target/i386/hvf/x86_emu.h @@ -30,6 +30,8 @@ struct x86_emul_ops { enum X86Seg seg); void (*handle_io)(CPUState *cpu, uint16_t port, void *data, int direction, int size, int count); + void (*simulate_rdmsr)(CPUState *cs); + void (*simulate_wrmsr)(CPUState *cs); }; extern const struct x86_emul_ops *emul_ops; From 7517ee9ec23801d4b6c4e5acbc0ad1bfa06c755a Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:19 -0800 Subject: [PATCH 0026/2760] target/i386: rename lazy flags field and its type The same structure and code can be used by other accelerators. Drop the hvf prefix in the type and field name. No functional change. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-9-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 6 ++-- target/i386/hvf/x86_flags.c | 56 ++++++++++++++++++------------------- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 3c5c39ce3d..119efc6c60 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1811,10 +1811,10 @@ typedef struct CPUCaches { CPUCacheInfo *l3_cache; } CPUCaches; -typedef struct HVFX86LazyFlags { +typedef struct X86LazyFlags { target_ulong result; target_ulong auxbits; -} HVFX86LazyFlags; +} X86LazyFlags; typedef struct CPUArchState { /* standard registers */ @@ -2108,7 +2108,7 @@ typedef struct CPUArchState { QemuMutex xen_timers_lock; #endif #if defined(CONFIG_HVF) - HVFX86LazyFlags hvf_lflags; + X86LazyFlags lflags; void *emu_mmio_buf; #endif diff --git a/target/i386/hvf/x86_flags.c b/target/i386/hvf/x86_flags.c index 0c75e0419c..84e27364a0 100644 --- a/target/i386/hvf/x86_flags.c +++ b/target/i386/hvf/x86_flags.c @@ -53,7 +53,7 @@ #define SET_FLAGS_OSZAPC_SIZE(size, lf_carries, lf_result) { \ target_ulong temp = ((lf_carries) & (LF_MASK_AF)) | \ (((lf_carries) >> (size - 2)) << LF_BIT_PO); \ - env->hvf_lflags.result = (target_ulong)(int##size##_t)(lf_result); \ + env->lflags.result = (target_ulong)(int##size##_t)(lf_result); \ if ((size) == 32) { \ temp = ((lf_carries) & ~(LF_MASK_PDB | LF_MASK_SD)); \ } else if ((size) == 16) { \ @@ -63,7 +63,7 @@ } else { \ VM_PANIC("unimplemented"); \ } \ - env->hvf_lflags.auxbits = (target_ulong)(uint32_t)temp; \ + env->lflags.auxbits = (target_ulong)(uint32_t)temp; \ } /* carries, result */ @@ -90,10 +90,10 @@ } else { \ VM_PANIC("unimplemented"); \ } \ - env->hvf_lflags.result = (target_ulong)(int##size##_t)(lf_result); \ - target_ulong delta_c = (env->hvf_lflags.auxbits ^ temp) & LF_MASK_CF; \ + env->lflags.result = (target_ulong)(int##size##_t)(lf_result); \ + target_ulong delta_c = (env->lflags.auxbits ^ temp) & LF_MASK_CF; \ delta_c ^= (delta_c >> 1); \ - env->hvf_lflags.auxbits = (target_ulong)(uint32_t)(temp ^ delta_c); \ + env->lflags.auxbits = (target_ulong)(uint32_t)(temp ^ delta_c); \ } /* carries, result */ @@ -107,8 +107,8 @@ void SET_FLAGS_OxxxxC(CPUX86State *env, uint32_t new_of, uint32_t new_cf) { uint32_t temp_po = new_of ^ new_cf; - env->hvf_lflags.auxbits &= ~(LF_MASK_PO | LF_MASK_CF); - env->hvf_lflags.auxbits |= (temp_po << LF_BIT_PO) | (new_cf << LF_BIT_CF); + env->lflags.auxbits &= ~(LF_MASK_PO | LF_MASK_CF); + env->lflags.auxbits |= (temp_po << LF_BIT_PO) | (new_cf << LF_BIT_CF); } void SET_FLAGS_OSZAPC_SUB32(CPUX86State *env, uint32_t v1, uint32_t v2, @@ -204,27 +204,27 @@ void SET_FLAGS_OSZAPC_LOGIC8(CPUX86State *env, uint8_t v1, uint8_t v2, bool get_PF(CPUX86State *env) { - uint32_t temp = (255 & env->hvf_lflags.result); - temp = temp ^ (255 & (env->hvf_lflags.auxbits >> LF_BIT_PDB)); + uint32_t temp = (255 & env->lflags.result); + temp = temp ^ (255 & (env->lflags.auxbits >> LF_BIT_PDB)); temp = (temp ^ (temp >> 4)) & 0x0F; return (0x9669U >> temp) & 1; } void set_PF(CPUX86State *env, bool val) { - uint32_t temp = (255 & env->hvf_lflags.result) ^ (!val); - env->hvf_lflags.auxbits &= ~(LF_MASK_PDB); - env->hvf_lflags.auxbits |= (temp << LF_BIT_PDB); + uint32_t temp = (255 & env->lflags.result) ^ (!val); + env->lflags.auxbits &= ~(LF_MASK_PDB); + env->lflags.auxbits |= (temp << LF_BIT_PDB); } bool get_OF(CPUX86State *env) { - return ((env->hvf_lflags.auxbits + (1U << LF_BIT_PO)) >> LF_BIT_CF) & 1; + return ((env->lflags.auxbits + (1U << LF_BIT_PO)) >> LF_BIT_CF) & 1; } bool get_CF(CPUX86State *env) { - return (env->hvf_lflags.auxbits >> LF_BIT_CF) & 1; + return (env->lflags.auxbits >> LF_BIT_CF) & 1; } void set_OF(CPUX86State *env, bool val) @@ -241,45 +241,45 @@ void set_CF(CPUX86State *env, bool val) bool get_AF(CPUX86State *env) { - return (env->hvf_lflags.auxbits >> LF_BIT_AF) & 1; + return (env->lflags.auxbits >> LF_BIT_AF) & 1; } void set_AF(CPUX86State *env, bool val) { - env->hvf_lflags.auxbits &= ~(LF_MASK_AF); - env->hvf_lflags.auxbits |= val << LF_BIT_AF; + env->lflags.auxbits &= ~(LF_MASK_AF); + env->lflags.auxbits |= val << LF_BIT_AF; } bool get_ZF(CPUX86State *env) { - return !env->hvf_lflags.result; + return !env->lflags.result; } void set_ZF(CPUX86State *env, bool val) { if (val) { - env->hvf_lflags.auxbits ^= - (((env->hvf_lflags.result >> LF_SIGN_BIT) & 1) << LF_BIT_SD); + env->lflags.auxbits ^= + (((env->lflags.result >> LF_SIGN_BIT) & 1) << LF_BIT_SD); /* merge the parity bits into the Parity Delta Byte */ - uint32_t temp_pdb = (255 & env->hvf_lflags.result); - env->hvf_lflags.auxbits ^= (temp_pdb << LF_BIT_PDB); + uint32_t temp_pdb = (255 & env->lflags.result); + env->lflags.auxbits ^= (temp_pdb << LF_BIT_PDB); /* now zero the .result value */ - env->hvf_lflags.result = 0; + env->lflags.result = 0; } else { - env->hvf_lflags.result |= (1 << 8); + env->lflags.result |= (1 << 8); } } bool get_SF(CPUX86State *env) { - return ((env->hvf_lflags.result >> LF_SIGN_BIT) ^ - (env->hvf_lflags.auxbits >> LF_BIT_SD)) & 1; + return ((env->lflags.result >> LF_SIGN_BIT) ^ + (env->lflags.auxbits >> LF_BIT_SD)) & 1; } void set_SF(CPUX86State *env, bool val) { bool temp_sf = get_SF(env); - env->hvf_lflags.auxbits ^= (temp_sf ^ val) << LF_BIT_SD; + env->lflags.auxbits ^= (temp_sf ^ val) << LF_BIT_SD; } void lflags_to_rflags(CPUX86State *env) @@ -295,7 +295,7 @@ void lflags_to_rflags(CPUX86State *env) void rflags_to_lflags(CPUX86State *env) { - env->hvf_lflags.auxbits = env->hvf_lflags.result = 0; + env->lflags.auxbits = env->lflags.result = 0; set_OF(env, env->eflags & CC_O); set_SF(env, env->eflags & CC_S); set_ZF(env, env->eflags & CC_Z); From 3667f0bb58427edadc4a049080141d395b48fc29 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:20 -0800 Subject: [PATCH 0027/2760] target/i386/hvf: drop unused headers Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-10-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/x86_decode.c | 3 --- target/i386/hvf/x86_emu.c | 4 ---- 2 files changed, 7 deletions(-) diff --git a/target/i386/hvf/x86_decode.c b/target/i386/hvf/x86_decode.c index 728e159638..ddd7b60bcf 100644 --- a/target/i386/hvf/x86_decode.c +++ b/target/i386/hvf/x86_decode.c @@ -20,10 +20,7 @@ #include "panic.h" #include "x86_decode.h" -#include "vmx.h" #include "x86_emu.h" -#include "x86_mmu.h" -#include "x86_descr.h" #define OPCODE_ESCAPE 0xf diff --git a/target/i386/hvf/x86_emu.c b/target/i386/hvf/x86_emu.c index aec7a8a3fa..26a4876aac 100644 --- a/target/i386/hvf/x86_emu.c +++ b/target/i386/hvf/x86_emu.c @@ -40,11 +40,7 @@ #include "x86_decode.h" #include "x86.h" #include "x86_emu.h" -#include "x86_mmu.h" #include "x86_flags.h" -#include "vmcs.h" -#include "vmx.h" -#include "hvf-i386.h" #define EXEC_2OP_FLAGS_CMD(env, decode, cmd, FLAGS_FUNC, save_res) \ { \ From fb8ebedd2a9d2c2c098464dee4ec7bd1c8c463a1 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:21 -0800 Subject: [PATCH 0028/2760] target/i386/hvf: rename some include guards These headers will be moved out to its own component. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-11-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/x86.h | 4 ++-- target/i386/hvf/x86_decode.h | 4 ++-- target/i386/hvf/x86_flags.h | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/target/i386/hvf/x86.h b/target/i386/hvf/x86.h index 063cd0b83e..73edccfba0 100644 --- a/target/i386/hvf/x86.h +++ b/target/i386/hvf/x86.h @@ -16,8 +16,8 @@ * License along with this program; if not, see . */ -#ifndef HVF_X86_H -#define HVF_X86_H +#ifndef X86_EMU_DEFS_H +#define X86_EMU_DEFS_H typedef struct x86_register { union { diff --git a/target/i386/hvf/x86_decode.h b/target/i386/hvf/x86_decode.h index a2d7a2a27b..930d965164 100644 --- a/target/i386/hvf/x86_decode.h +++ b/target/i386/hvf/x86_decode.h @@ -15,8 +15,8 @@ * License along with this program; if not, see . */ -#ifndef HVF_X86_DECODE_H -#define HVF_X86_DECODE_H +#ifndef X86_EMU_DECODE_H +#define X86_EMU_DECODE_H #include "cpu.h" #include "x86.h" diff --git a/target/i386/hvf/x86_flags.h b/target/i386/hvf/x86_flags.h index 75c2a7feab..6c175007b5 100644 --- a/target/i386/hvf/x86_flags.h +++ b/target/i386/hvf/x86_flags.h @@ -21,8 +21,8 @@ * x86 eflags functions */ -#ifndef X86_FLAGS_H -#define X86_FLAGS_H +#ifndef X86_EMU_FLAGS_H +#define X86_EMU_FLAGS_H #include "cpu.h" void lflags_to_rflags(CPUX86State *env); @@ -78,4 +78,4 @@ void SET_FLAGS_OSZAPC_LOGIC16(CPUX86State *env, uint16_t v1, uint16_t v2, void SET_FLAGS_OSZAPC_LOGIC8(CPUX86State *env, uint8_t v1, uint8_t v2, uint8_t diff); -#endif /* X86_FLAGS_H */ +#endif /* X86_EMU_FLAGS_H */ From 5ffd2705f797ffe59165f3f29ad1065f648d6add Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:22 -0800 Subject: [PATCH 0029/2760] target/i386: add a directory for x86 instruction emulator Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-12-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/emulate/meson.build | 0 target/i386/meson.build | 1 + 2 files changed, 1 insertion(+) create mode 100644 target/i386/emulate/meson.build diff --git a/target/i386/emulate/meson.build b/target/i386/emulate/meson.build new file mode 100644 index 0000000000..e69de29bb2 diff --git a/target/i386/meson.build b/target/i386/meson.build index 2e9c472f49..c1aacea613 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -31,6 +31,7 @@ subdir('whpx') subdir('nvmm') subdir('hvf') subdir('tcg') +subdir('emulate') target_arch += {'i386': i386_ss} target_system_arch += {'i386': i386_system_ss} From 2d4f09523fd48bdb42a163e3b53a420c799217f3 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:23 -0800 Subject: [PATCH 0030/2760] target/i386/emulate: add a panic.h The macros will be used by the instruction emulator. The code is the same as the one under hvf. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-13-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/emulate/panic.h | 45 +++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 target/i386/emulate/panic.h diff --git a/target/i386/emulate/panic.h b/target/i386/emulate/panic.h new file mode 100644 index 0000000000..71c24874ba --- /dev/null +++ b/target/i386/emulate/panic.h @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2016 Veertu Inc, + * Copyright (C) 2017 Google Inc, + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program; if not, see . + */ +#ifndef X86_EMU_PANIC_H +#define X86_EMU_PANIC_H + +#define VM_PANIC(x) {\ + printf("%s\n", x); \ + abort(); \ +} + +#define VM_PANIC_ON(x) {\ + if (x) { \ + printf("%s\n", #x); \ + abort(); \ + } \ +} + +#define VM_PANIC_EX(...) {\ + printf(__VA_ARGS__); \ + abort(); \ +} + +#define VM_PANIC_ON_EX(x, ...) {\ + if (x) { \ + printf(__VA_ARGS__); \ + abort(); \ + } \ +} + +#endif From 27458df871097d7fc14b19d9e01c35d29737b9b3 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:24 -0800 Subject: [PATCH 0031/2760] target/i386: move x86 instruction emulator out of hvf Move x86_decode, x86_emu, x86_flags and some headers to the new location. Fix up all the inclusion sites in hvf. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-14-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/emulate/meson.build | 5 +++++ target/i386/{hvf => emulate}/x86.h | 0 target/i386/{hvf => emulate}/x86_decode.c | 0 target/i386/{hvf => emulate}/x86_decode.h | 0 target/i386/{hvf => emulate}/x86_emu.c | 0 target/i386/{hvf => emulate}/x86_emu.h | 0 target/i386/{hvf => emulate}/x86_flags.c | 0 target/i386/{hvf => emulate}/x86_flags.h | 0 target/i386/hvf/hvf.c | 8 ++++---- target/i386/hvf/meson.build | 3 --- target/i386/hvf/vmx.h | 2 +- target/i386/hvf/x86.c | 4 ++-- target/i386/hvf/x86_cpuid.c | 2 +- target/i386/hvf/x86_descr.h | 2 +- target/i386/hvf/x86_mmu.c | 2 +- target/i386/hvf/x86_task.c | 6 +++--- target/i386/hvf/x86hvf.c | 2 +- 17 files changed, 19 insertions(+), 17 deletions(-) rename target/i386/{hvf => emulate}/x86.h (100%) rename target/i386/{hvf => emulate}/x86_decode.c (100%) rename target/i386/{hvf => emulate}/x86_decode.h (100%) rename target/i386/{hvf => emulate}/x86_emu.c (100%) rename target/i386/{hvf => emulate}/x86_emu.h (100%) rename target/i386/{hvf => emulate}/x86_flags.c (100%) rename target/i386/{hvf => emulate}/x86_flags.h (100%) diff --git a/target/i386/emulate/meson.build b/target/i386/emulate/meson.build index e69de29bb2..4edd4f462f 100644 --- a/target/i386/emulate/meson.build +++ b/target/i386/emulate/meson.build @@ -0,0 +1,5 @@ +i386_system_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( + 'x86_decode.c', + 'x86_emu.c', + 'x86_flags.c', +)) diff --git a/target/i386/hvf/x86.h b/target/i386/emulate/x86.h similarity index 100% rename from target/i386/hvf/x86.h rename to target/i386/emulate/x86.h diff --git a/target/i386/hvf/x86_decode.c b/target/i386/emulate/x86_decode.c similarity index 100% rename from target/i386/hvf/x86_decode.c rename to target/i386/emulate/x86_decode.c diff --git a/target/i386/hvf/x86_decode.h b/target/i386/emulate/x86_decode.h similarity index 100% rename from target/i386/hvf/x86_decode.h rename to target/i386/emulate/x86_decode.h diff --git a/target/i386/hvf/x86_emu.c b/target/i386/emulate/x86_emu.c similarity index 100% rename from target/i386/hvf/x86_emu.c rename to target/i386/emulate/x86_emu.c diff --git a/target/i386/hvf/x86_emu.h b/target/i386/emulate/x86_emu.h similarity index 100% rename from target/i386/hvf/x86_emu.h rename to target/i386/emulate/x86_emu.h diff --git a/target/i386/hvf/x86_flags.c b/target/i386/emulate/x86_flags.c similarity index 100% rename from target/i386/hvf/x86_flags.c rename to target/i386/emulate/x86_flags.c diff --git a/target/i386/hvf/x86_flags.h b/target/i386/emulate/x86_flags.h similarity index 100% rename from target/i386/hvf/x86_flags.h rename to target/i386/emulate/x86_flags.h diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 8c31d2e0cf..23ebf2550a 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -59,12 +59,12 @@ #include "hvf-i386.h" #include "vmcs.h" #include "vmx.h" -#include "x86.h" +#include "emulate/x86.h" #include "x86_descr.h" -#include "x86_flags.h" +#include "emulate/x86_flags.h" #include "x86_mmu.h" -#include "x86_decode.h" -#include "x86_emu.h" +#include "emulate/x86_decode.h" +#include "emulate/x86_emu.h" #include "x86_task.h" #include "x86hvf.h" diff --git a/target/i386/hvf/meson.build b/target/i386/hvf/meson.build index 05c3c8cf18..519d190f0e 100644 --- a/target/i386/hvf/meson.build +++ b/target/i386/hvf/meson.build @@ -2,10 +2,7 @@ i386_system_ss.add(when: [hvf, 'CONFIG_HVF'], if_true: files( 'hvf.c', 'x86.c', 'x86_cpuid.c', - 'x86_decode.c', 'x86_descr.c', - 'x86_emu.c', - 'x86_flags.c', 'x86_mmu.c', 'x86_task.c', 'x86hvf.c', diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h index 80ce26279b..3c56afc9d3 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -29,7 +29,7 @@ #include #include "vmcs.h" #include "cpu.h" -#include "x86.h" +#include "emulate/x86.h" #include "system/hvf.h" #include "system/hvf_int.h" diff --git a/target/i386/hvf/x86.c b/target/i386/hvf/x86.c index a0ede13886..5c75ec9a00 100644 --- a/target/i386/hvf/x86.c +++ b/target/i386/hvf/x86.c @@ -19,8 +19,8 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "x86_decode.h" -#include "x86_emu.h" +#include "emulate/x86_decode.h" +#include "emulate/x86_emu.h" #include "vmcs.h" #include "vmx.h" #include "x86_mmu.h" diff --git a/target/i386/hvf/x86_cpuid.c b/target/i386/hvf/x86_cpuid.c index ae836f65cc..fa131b18c6 100644 --- a/target/i386/hvf/x86_cpuid.c +++ b/target/i386/hvf/x86_cpuid.c @@ -24,7 +24,7 @@ #include "qemu/cpuid.h" #include "host/cpuinfo.h" #include "cpu.h" -#include "x86.h" +#include "emulate/x86.h" #include "vmx.h" #include "system/hvf.h" #include "hvf-i386.h" diff --git a/target/i386/hvf/x86_descr.h b/target/i386/hvf/x86_descr.h index ce5de98349..24af4946cd 100644 --- a/target/i386/hvf/x86_descr.h +++ b/target/i386/hvf/x86_descr.h @@ -19,7 +19,7 @@ #ifndef HVF_X86_DESCR_H #define HVF_X86_DESCR_H -#include "x86.h" +#include "emulate/x86.h" typedef struct vmx_segment { uint16_t sel; diff --git a/target/i386/hvf/x86_mmu.c b/target/i386/hvf/x86_mmu.c index 579d0c3a4c..afc5c17d5d 100644 --- a/target/i386/hvf/x86_mmu.c +++ b/target/i386/hvf/x86_mmu.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "panic.h" #include "cpu.h" -#include "x86.h" +#include "emulate/x86.h" #include "x86_mmu.h" #include "vmcs.h" #include "vmx.h" diff --git a/target/i386/hvf/x86_task.c b/target/i386/hvf/x86_task.c index 161217991f..bdf8b51ae6 100644 --- a/target/i386/hvf/x86_task.c +++ b/target/i386/hvf/x86_task.c @@ -14,11 +14,11 @@ #include "hvf-i386.h" #include "vmcs.h" #include "vmx.h" -#include "x86.h" +#include "emulate/x86.h" #include "x86_descr.h" #include "x86_mmu.h" -#include "x86_decode.h" -#include "x86_emu.h" +#include "emulate/x86_decode.h" +#include "emulate/x86_emu.h" #include "x86_task.h" #include "x86hvf.h" diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 531a340b37..2057314892 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -24,7 +24,7 @@ #include "vmcs.h" #include "cpu.h" #include "x86_descr.h" -#include "x86_decode.h" +#include "emulate/x86_decode.h" #include "system/hw_accel.h" #include "hw/i386/apic_internal.h" From 6aa0d0391c56e411d43bbf9f9c6acd1fea6f55c4 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Fri, 7 Mar 2025 11:55:25 -0800 Subject: [PATCH 0032/2760] MAINTAINERS: add an entry for the x86 instruction emulator Add myself as a reviewer. Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/1741377325-28175-15-git-send-email-liuwe@linux.microsoft.com Signed-off-by: Paolo Bonzini --- MAINTAINERS | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d54b5578f8..67228401f0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -534,6 +534,14 @@ S: Supported F: target/i386/whpx/ F: include/system/whpx.h +X86 Instruction Emulator +M: Cameron Esfahani +M: Roman Bolshakov +R: Phil Dennis-Jordan +R: Wei Liu +S: Maintained +F: target/i386/emulate/ + Guest CPU Cores (Xen) --------------------- X86 Xen CPUs From c901905ea6703a2feb5867537a8559dc129447f7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 3 Apr 2025 21:45:36 +0200 Subject: [PATCH 0033/2760] target/i386/emulate: remove flags_mask The field is written but never read. Cc: Wei Liu Reviewed-by: Richard Henderson Signed-off-by: Paolo Bonzini --- target/i386/emulate/x86_decode.c | 864 +++++++++++++++---------------- target/i386/emulate/x86_decode.h | 2 - 2 files changed, 424 insertions(+), 442 deletions(-) diff --git a/target/i386/emulate/x86_decode.c b/target/i386/emulate/x86_decode.c index ddd7b60bcf..7fee219687 100644 --- a/target/i386/emulate/x86_decode.c +++ b/target/i386/emulate/x86_decode.c @@ -429,7 +429,6 @@ struct decode_tbl { void (*decode_op4)(CPUX86State *env, struct x86_decode *decode, struct x86_decode_op *op4); void (*decode_postfix)(CPUX86State *env, struct x86_decode *decode); - uint32_t flags_mask; }; struct decode_x87_tbl { @@ -445,7 +444,6 @@ struct decode_x87_tbl { void (*decode_op2)(CPUX86State *env, struct x86_decode *decode, struct x86_decode_op *op2); void (*decode_postfix)(CPUX86State *env, struct x86_decode *decode); - uint32_t flags_mask; }; struct decode_tbl invl_inst = {0x0, 0, 0, false, NULL, NULL, NULL, NULL, @@ -470,7 +468,6 @@ static void decode_x87_ins(CPUX86State *env, struct x86_decode *decode) if (decoder->operand_size) { decode->operand_size = decoder->operand_size; } - decode->flags_mask = decoder->flags_mask; decode->fpop_stack = decoder->pop; decode->frev = decoder->rev; @@ -503,9 +500,6 @@ static void decode_ffgroup(CPUX86State *env, struct x86_decode *decode) X86_DECODE_CMD_INVL }; decode->cmd = group[decode->modrm.reg]; - if (decode->modrm.reg > 2) { - decode->flags_mask = 0; - } } static void decode_sldtgroup(CPUX86State *env, struct x86_decode *decode) @@ -693,733 +687,724 @@ static void decode_db_4(CPUX86State *env, struct x86_decode *decode) } -#define RFLAGS_MASK_NONE 0 -#define RFLAGS_MASK_OSZAPC (CC_O | CC_S | CC_Z | CC_A | CC_P | CC_C) -#define RFLAGS_MASK_LAHF (CC_S | CC_Z | CC_A | CC_P | CC_C) -#define RFLAGS_MASK_CF (CC_C) -#define RFLAGS_MASK_IF (IF_MASK) -#define RFLAGS_MASK_TF (TF_MASK) -#define RFLAGS_MASK_DF (DF_MASK) -#define RFLAGS_MASK_ZF (CC_Z) - struct decode_tbl _1op_inst[] = { {0x0, X86_DECODE_CMD_ADD, 1, true, decode_modrm_rm, decode_modrm_reg, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL}, {0x1, X86_DECODE_CMD_ADD, 0, true, decode_modrm_rm, decode_modrm_reg, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL}, {0x2, X86_DECODE_CMD_ADD, 1, true, decode_modrm_reg, decode_modrm_rm, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL}, {0x3, X86_DECODE_CMD_ADD, 0, true, decode_modrm_reg, decode_modrm_rm, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL}, {0x4, X86_DECODE_CMD_ADD, 1, false, decode_rax, decode_imm8, NULL, NULL, - NULL, RFLAGS_MASK_OSZAPC}, + NULL}, {0x5, X86_DECODE_CMD_ADD, 0, false, decode_rax, decode_imm, NULL, NULL, - NULL, RFLAGS_MASK_OSZAPC}, + NULL}, {0x6, X86_DECODE_CMD_PUSH_SEG, 0, false, false, NULL, NULL, NULL, - decode_pushseg, RFLAGS_MASK_NONE}, + decode_pushseg}, {0x7, X86_DECODE_CMD_POP_SEG, 0, false, false, NULL, NULL, NULL, - decode_popseg, RFLAGS_MASK_NONE}, + decode_popseg}, {0x8, X86_DECODE_CMD_OR, 1, true, decode_modrm_rm, decode_modrm_reg, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL}, {0x9, X86_DECODE_CMD_OR, 0, true, decode_modrm_rm, decode_modrm_reg, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL}, {0xa, X86_DECODE_CMD_OR, 1, true, decode_modrm_reg, decode_modrm_rm, NULL, - NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL}, {0xb, X86_DECODE_CMD_OR, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xc, X86_DECODE_CMD_OR, 1, false, decode_rax, decode_imm8, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xd, X86_DECODE_CMD_OR, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xe, X86_DECODE_CMD_PUSH_SEG, 0, false, false, - NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_pushseg}, {0xf, X86_DECODE_CMD_POP_SEG, 0, false, false, - NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_popseg}, {0x10, X86_DECODE_CMD_ADC, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x11, X86_DECODE_CMD_ADC, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x12, X86_DECODE_CMD_ADC, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x13, X86_DECODE_CMD_ADC, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x14, X86_DECODE_CMD_ADC, 1, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x15, X86_DECODE_CMD_ADC, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x16, X86_DECODE_CMD_PUSH_SEG, 0, false, false, - NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_pushseg}, {0x17, X86_DECODE_CMD_POP_SEG, 0, false, false, - NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_popseg}, {0x18, X86_DECODE_CMD_SBB, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x19, X86_DECODE_CMD_SBB, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x1a, X86_DECODE_CMD_SBB, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x1b, X86_DECODE_CMD_SBB, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x1c, X86_DECODE_CMD_SBB, 1, false, decode_rax, decode_imm8, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x1d, X86_DECODE_CMD_SBB, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x1e, X86_DECODE_CMD_PUSH_SEG, 0, false, false, - NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_pushseg}, {0x1f, X86_DECODE_CMD_POP_SEG, 0, false, false, - NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_popseg}, {0x20, X86_DECODE_CMD_AND, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x21, X86_DECODE_CMD_AND, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x22, X86_DECODE_CMD_AND, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x23, X86_DECODE_CMD_AND, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x24, X86_DECODE_CMD_AND, 1, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x25, X86_DECODE_CMD_AND, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x28, X86_DECODE_CMD_SUB, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x29, X86_DECODE_CMD_SUB, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x2a, X86_DECODE_CMD_SUB, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x2b, X86_DECODE_CMD_SUB, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x2c, X86_DECODE_CMD_SUB, 1, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x2d, X86_DECODE_CMD_SUB, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x2f, X86_DECODE_CMD_DAS, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, NULL}, {0x30, X86_DECODE_CMD_XOR, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x31, X86_DECODE_CMD_XOR, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x32, X86_DECODE_CMD_XOR, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x33, X86_DECODE_CMD_XOR, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x34, X86_DECODE_CMD_XOR, 1, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x35, X86_DECODE_CMD_XOR, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x38, X86_DECODE_CMD_CMP, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x39, X86_DECODE_CMD_CMP, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x3a, X86_DECODE_CMD_CMP, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x3b, X86_DECODE_CMD_CMP, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x3c, X86_DECODE_CMD_CMP, 1, false, decode_rax, decode_imm8, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x3d, X86_DECODE_CMD_CMP, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x3f, X86_DECODE_CMD_AAS, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, NULL}, {0x40, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_incgroup}, {0x41, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_incgroup}, {0x42, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_incgroup}, {0x43, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_incgroup}, {0x44, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_incgroup}, {0x45, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_incgroup}, {0x46, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_incgroup}, {0x47, X86_DECODE_CMD_INC, 0, false, - NULL, NULL, NULL, NULL, decode_incgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_incgroup}, {0x48, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_decgroup}, {0x49, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_decgroup}, {0x4a, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_decgroup}, {0x4b, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_decgroup}, {0x4c, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_decgroup}, {0x4d, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_decgroup}, {0x4e, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_decgroup}, {0x4f, X86_DECODE_CMD_DEC, 0, false, - NULL, NULL, NULL, NULL, decode_decgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_decgroup}, {0x50, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_pushgroup}, {0x51, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_pushgroup}, {0x52, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_pushgroup}, {0x53, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_pushgroup}, {0x54, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_pushgroup}, {0x55, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_pushgroup}, {0x56, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_pushgroup}, {0x57, X86_DECODE_CMD_PUSH, 0, false, - NULL, NULL, NULL, NULL, decode_pushgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_pushgroup}, {0x58, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_popgroup}, {0x59, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_popgroup}, {0x5a, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_popgroup}, {0x5b, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_popgroup}, {0x5c, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_popgroup}, {0x5d, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_popgroup}, {0x5e, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_popgroup}, {0x5f, X86_DECODE_CMD_POP, 0, false, - NULL, NULL, NULL, NULL, decode_popgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_popgroup}, {0x60, X86_DECODE_CMD_PUSHA, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0x61, X86_DECODE_CMD_POPA, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0x68, X86_DECODE_CMD_PUSH, 0, false, decode_imm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x6a, X86_DECODE_CMD_PUSH, 0, false, decode_imm8_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x69, X86_DECODE_CMD_IMUL_3, 0, true, decode_modrm_reg, - decode_modrm_rm, decode_imm, NULL, NULL, RFLAGS_MASK_OSZAPC}, + decode_modrm_rm, decode_imm, NULL, NULL}, {0x6b, X86_DECODE_CMD_IMUL_3, 0, true, decode_modrm_reg, decode_modrm_rm, - decode_imm8_signed, NULL, NULL, RFLAGS_MASK_OSZAPC}, + decode_imm8_signed, NULL, NULL}, {0x6c, X86_DECODE_CMD_INS, 1, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0x6d, X86_DECODE_CMD_INS, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0x6e, X86_DECODE_CMD_OUTS, 1, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0x6f, X86_DECODE_CMD_OUTS, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0x70, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x71, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x72, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x73, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x74, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x75, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x76, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x77, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x78, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x79, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x7a, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x7b, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x7c, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x7d, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x7e, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x7f, X86_DECODE_CMD_JXX, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x80, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm8, - NULL, NULL, decode_addgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, decode_addgroup}, {0x81, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm, - NULL, NULL, decode_addgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, decode_addgroup}, {0x82, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm8, - NULL, NULL, decode_addgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, decode_addgroup}, {0x83, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm8_signed, - NULL, NULL, decode_addgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, decode_addgroup}, {0x84, X86_DECODE_CMD_TST, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x85, X86_DECODE_CMD_TST, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0x86, X86_DECODE_CMD_XCHG, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x87, X86_DECODE_CMD_XCHG, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x88, X86_DECODE_CMD_MOV, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x89, X86_DECODE_CMD_MOV, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x8a, X86_DECODE_CMD_MOV, 1, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x8b, X86_DECODE_CMD_MOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x8c, X86_DECODE_CMD_MOV_FROM_SEG, 0, true, decode_modrm_rm, - decode_modrm_reg, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + decode_modrm_reg, NULL, NULL, NULL}, {0x8d, X86_DECODE_CMD_LEA, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x8e, X86_DECODE_CMD_MOV_TO_SEG, 0, true, decode_modrm_reg, - decode_modrm_rm, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + decode_modrm_rm, NULL, NULL, NULL}, {0x8f, X86_DECODE_CMD_POP, 0, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x90, X86_DECODE_CMD_NOP, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0x91, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, + NULL, NULL, decode_xchgroup}, {0x92, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, + NULL, NULL, decode_xchgroup}, {0x93, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, + NULL, NULL, decode_xchgroup}, {0x94, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, + NULL, NULL, decode_xchgroup}, {0x95, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, + NULL, NULL, decode_xchgroup}, {0x96, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, + NULL, NULL, decode_xchgroup}, {0x97, X86_DECODE_CMD_XCHG, 0, false, NULL, decode_rax, - NULL, NULL, decode_xchgroup, RFLAGS_MASK_NONE}, + NULL, NULL, decode_xchgroup}, {0x98, X86_DECODE_CMD_CBW, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x99, X86_DECODE_CMD_CWD, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x9a, X86_DECODE_CMD_CALL_FAR, 0, false, NULL, - NULL, NULL, NULL, decode_farjmp, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_farjmp}, {0x9c, X86_DECODE_CMD_PUSHF, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, /*{0x9d, X86_DECODE_CMD_POPF, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_POPF},*/ + NULL, NULL, NULL},*/ {0x9e, X86_DECODE_CMD_SAHF, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x9f, X86_DECODE_CMD_LAHF, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_LAHF}, + NULL, NULL, NULL}, {0xa0, X86_DECODE_CMD_MOV, 1, false, decode_rax, fetch_moffs, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xa1, X86_DECODE_CMD_MOV, 0, false, decode_rax, fetch_moffs, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xa2, X86_DECODE_CMD_MOV, 1, false, fetch_moffs, decode_rax, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xa3, X86_DECODE_CMD_MOV, 0, false, fetch_moffs, decode_rax, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xa4, X86_DECODE_CMD_MOVS, 1, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xa5, X86_DECODE_CMD_MOVS, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xa6, X86_DECODE_CMD_CMPS, 1, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xa7, X86_DECODE_CMD_CMPS, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xaa, X86_DECODE_CMD_STOS, 1, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xab, X86_DECODE_CMD_STOS, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xac, X86_DECODE_CMD_LODS, 1, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xad, X86_DECODE_CMD_LODS, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xae, X86_DECODE_CMD_SCAS, 1, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xaf, X86_DECODE_CMD_SCAS, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xa8, X86_DECODE_CMD_TST, 1, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xa9, X86_DECODE_CMD_TST, 0, false, decode_rax, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xb0, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup8}, {0xb1, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup8}, {0xb2, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup8}, {0xb3, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup8}, {0xb4, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup8}, {0xb5, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup8}, {0xb6, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup8}, {0xb7, X86_DECODE_CMD_MOV, 1, false, NULL, - NULL, NULL, NULL, decode_movgroup8, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup8}, {0xb8, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup}, {0xb9, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup}, {0xba, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup}, {0xbb, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup}, {0xbc, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup}, {0xbd, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup}, {0xbe, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup}, {0xbf, X86_DECODE_CMD_MOV, 0, false, NULL, - NULL, NULL, NULL, decode_movgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_movgroup}, {0xc0, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm8, - NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, decode_rotgroup}, {0xc1, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm8, - NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, decode_rotgroup}, {0xc2, X86_DECODE_RET_NEAR, 0, false, decode_imm16, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xc3, X86_DECODE_RET_NEAR, 0, false, NULL, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xc4, X86_DECODE_CMD_LES, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xc5, X86_DECODE_CMD_LDS, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xc6, X86_DECODE_CMD_MOV, 1, true, decode_modrm_rm, decode_imm8, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xc7, X86_DECODE_CMD_MOV, 0, true, decode_modrm_rm, decode_imm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xc8, X86_DECODE_CMD_ENTER, 0, false, decode_imm16, decode_imm8, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xc9, X86_DECODE_CMD_LEAVE, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xca, X86_DECODE_RET_FAR, 0, false, decode_imm16, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xcb, X86_DECODE_RET_FAR, 0, false, decode_imm_0, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xcd, X86_DECODE_CMD_INT, 0, false, decode_imm8, NULL, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, /*{0xcf, X86_DECODE_CMD_IRET, 0, false, NULL, NULL, - NULL, NULL, NULL, RFLAGS_MASK_IRET},*/ + NULL, NULL, NULL},*/ {0xd0, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_imm_1, - NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, decode_rotgroup}, {0xd1, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm_1, - NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, decode_rotgroup}, {0xd2, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, decode_rcx, - NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, decode_rotgroup}, {0xd3, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_rcx, - NULL, NULL, decode_rotgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, decode_rotgroup}, {0xd4, X86_DECODE_CMD_AAM, 0, false, decode_imm8, - NULL, NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL}, {0xd5, X86_DECODE_CMD_AAD, 0, false, decode_imm8, - NULL, NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL}, {0xd7, X86_DECODE_CMD_XLAT, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0xd8, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_x87_ins}, {0xd9, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_x87_ins}, {0xda, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_x87_ins}, {0xdb, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_x87_ins}, {0xdc, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_x87_ins}, {0xdd, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_x87_ins}, {0xde, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_x87_ins}, {0xdf, X86_DECODE_CMD_INVL, 0, true, NULL, - NULL, NULL, NULL, decode_x87_ins, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_x87_ins}, {0xe0, X86_DECODE_CMD_LOOP, 0, false, decode_imm8_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xe1, X86_DECODE_CMD_LOOP, 0, false, decode_imm8_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xe2, X86_DECODE_CMD_LOOP, 0, false, decode_imm8_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xe3, X86_DECODE_CMD_JCXZ, 1, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0xe4, X86_DECODE_CMD_IN, 1, false, decode_imm8, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xe5, X86_DECODE_CMD_IN, 0, false, decode_imm8, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xe6, X86_DECODE_CMD_OUT, 1, false, decode_imm8, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xe7, X86_DECODE_CMD_OUT, 0, false, decode_imm8, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xe8, X86_DECODE_CMD_CALL_NEAR, 0, false, decode_imm_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xe9, X86_DECODE_CMD_JMP_NEAR, 0, false, decode_imm_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xea, X86_DECODE_CMD_JMP_FAR, 0, false, - NULL, NULL, NULL, NULL, decode_farjmp, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_farjmp}, {0xeb, X86_DECODE_CMD_JMP_NEAR, 1, false, decode_imm8_signed, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xec, X86_DECODE_CMD_IN, 1, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0xed, X86_DECODE_CMD_IN, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0xee, X86_DECODE_CMD_OUT, 1, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0xef, X86_DECODE_CMD_OUT, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0xf4, X86_DECODE_CMD_HLT, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0xf5, X86_DECODE_CMD_CMC, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_CF}, + NULL, NULL, NULL, NULL, NULL}, {0xf6, X86_DECODE_CMD_INVL, 1, true, - NULL, NULL, NULL, NULL, decode_f7group, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_f7group}, {0xf7, X86_DECODE_CMD_INVL, 0, true, - NULL, NULL, NULL, NULL, decode_f7group, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, NULL, decode_f7group}, {0xf8, X86_DECODE_CMD_CLC, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_CF}, + NULL, NULL, NULL, NULL, NULL}, {0xf9, X86_DECODE_CMD_STC, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_CF}, + NULL, NULL, NULL, NULL, NULL}, {0xfa, X86_DECODE_CMD_CLI, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_IF}, + NULL, NULL, NULL, NULL, NULL}, {0xfb, X86_DECODE_CMD_STI, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_IF}, + NULL, NULL, NULL, NULL, NULL}, {0xfc, X86_DECODE_CMD_CLD, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_DF}, + NULL, NULL, NULL, NULL, NULL}, {0xfd, X86_DECODE_CMD_STD, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_DF}, + NULL, NULL, NULL, NULL, NULL}, {0xfe, X86_DECODE_CMD_INVL, 1, true, decode_modrm_rm, - NULL, NULL, NULL, decode_incgroup2, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, decode_incgroup2}, {0xff, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, - NULL, NULL, NULL, decode_ffgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL, decode_ffgroup}, }; struct decode_tbl _2op_inst[] = { {0x0, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, - NULL, NULL, NULL, decode_sldtgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_sldtgroup}, {0x1, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, - NULL, NULL, NULL, decode_lidtgroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_lidtgroup}, {0x6, X86_DECODE_CMD_CLTS, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_TF}, + NULL, NULL, NULL, NULL, NULL}, {0x9, X86_DECODE_CMD_WBINVD, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0x18, X86_DECODE_CMD_PREFETCH, 0, true, - NULL, NULL, NULL, NULL, decode_x87_general, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_x87_general}, {0x1f, X86_DECODE_CMD_NOP, 0, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x20, X86_DECODE_CMD_MOV_FROM_CR, 0, true, decode_modrm_rm, - decode_modrm_reg, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + decode_modrm_reg, NULL, NULL, NULL}, {0x21, X86_DECODE_CMD_MOV_FROM_DR, 0, true, decode_modrm_rm, - decode_modrm_reg, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + decode_modrm_reg, NULL, NULL, NULL}, {0x22, X86_DECODE_CMD_MOV_TO_CR, 0, true, decode_modrm_reg, - decode_modrm_rm, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + decode_modrm_rm, NULL, NULL, NULL}, {0x23, X86_DECODE_CMD_MOV_TO_DR, 0, true, decode_modrm_reg, - decode_modrm_rm, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + decode_modrm_rm, NULL, NULL, NULL}, {0x30, X86_DECODE_CMD_WRMSR, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0x31, X86_DECODE_CMD_RDTSC, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0x32, X86_DECODE_CMD_RDMSR, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0x40, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x41, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x42, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x43, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x44, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x45, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x46, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x47, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x48, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x49, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x4a, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x4b, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x4c, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x4d, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x4e, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x4f, X86_DECODE_CMD_CMOV, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0x77, X86_DECODE_CMD_EMMS, 0, false, - NULL, NULL, NULL, NULL, decode_x87_general, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_x87_general}, {0x82, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x83, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x84, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x85, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x86, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x87, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x88, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x89, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x8a, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x8b, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x8c, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x8d, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x8e, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x8f, X86_DECODE_CMD_JXX, 0, false, - NULL, NULL, NULL, NULL, decode_jxx, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_jxx}, {0x90, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x91, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x92, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x93, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x94, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x95, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x96, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x97, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x98, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x99, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x9a, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x9b, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x9c, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x9d, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x9e, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0x9f, X86_DECODE_CMD_SETXX, 1, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL}, {0xb0, X86_DECODE_CMD_CMPXCHG, 1, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xb1, X86_DECODE_CMD_CMPXCHG, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xb6, X86_DECODE_CMD_MOVZX, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xb7, X86_DECODE_CMD_MOVZX, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xb8, X86_DECODE_CMD_POPCNT, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xbe, X86_DECODE_CMD_MOVSX, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xbf, X86_DECODE_CMD_MOVSX, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xa0, X86_DECODE_CMD_PUSH_SEG, 0, false, false, - NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_pushseg}, {0xa1, X86_DECODE_CMD_POP_SEG, 0, false, false, - NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_popseg}, {0xa2, X86_DECODE_CMD_CPUID, 0, false, - NULL, NULL, NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, NULL}, {0xa3, X86_DECODE_CMD_BT, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_CF}, + NULL, NULL, NULL}, {0xa4, X86_DECODE_CMD_SHLD, 0, true, decode_modrm_rm, decode_modrm_reg, - decode_imm8, NULL, NULL, RFLAGS_MASK_OSZAPC}, + decode_imm8, NULL, NULL}, {0xa5, X86_DECODE_CMD_SHLD, 0, true, decode_modrm_rm, decode_modrm_reg, - decode_rcx, NULL, NULL, RFLAGS_MASK_OSZAPC}, + decode_rcx, NULL, NULL}, {0xa8, X86_DECODE_CMD_PUSH_SEG, 0, false, false, - NULL, NULL, NULL, decode_pushseg, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_pushseg}, {0xa9, X86_DECODE_CMD_POP_SEG, 0, false, false, - NULL, NULL, NULL, decode_popseg, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_popseg}, {0xab, X86_DECODE_CMD_BTS, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_CF}, + NULL, NULL, NULL}, {0xac, X86_DECODE_CMD_SHRD, 0, true, decode_modrm_rm, decode_modrm_reg, - decode_imm8, NULL, NULL, RFLAGS_MASK_OSZAPC}, + decode_imm8, NULL, NULL}, {0xad, X86_DECODE_CMD_SHRD, 0, true, decode_modrm_rm, decode_modrm_reg, - decode_rcx, NULL, NULL, RFLAGS_MASK_OSZAPC}, + decode_rcx, NULL, NULL}, {0xae, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, - NULL, NULL, NULL, decode_aegroup, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, decode_aegroup}, {0xaf, X86_DECODE_CMD_IMUL_2, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xb2, X86_DECODE_CMD_LSS, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_NONE}, + NULL, NULL, NULL}, {0xb3, X86_DECODE_CMD_BTR, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xba, X86_DECODE_CMD_INVL, 0, true, decode_modrm_rm, decode_imm8, - NULL, NULL, decode_btgroup, RFLAGS_MASK_OSZAPC}, + NULL, NULL, decode_btgroup}, {0xbb, X86_DECODE_CMD_BTC, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xbc, X86_DECODE_CMD_BSF, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xbd, X86_DECODE_CMD_BSR, 0, true, decode_modrm_reg, decode_modrm_rm, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xc1, X86_DECODE_CMD_XADD, 0, true, decode_modrm_rm, decode_modrm_reg, - NULL, NULL, NULL, RFLAGS_MASK_OSZAPC}, + NULL, NULL, NULL}, {0xc7, X86_DECODE_CMD_CMPXCHG8B, 0, true, decode_modrm_rm, - NULL, NULL, NULL, NULL, RFLAGS_MASK_ZF}, + NULL, NULL, NULL, NULL}, {0xc8, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_bswap}, {0xc9, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_bswap}, {0xca, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_bswap}, {0xcb, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_bswap}, {0xcc, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_bswap}, {0xcd, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_bswap}, {0xce, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_bswap}, {0xcf, X86_DECODE_CMD_BSWAP, 0, false, - NULL, NULL, NULL, NULL, decode_bswap, RFLAGS_MASK_NONE}, + NULL, NULL, NULL, NULL, decode_bswap}, }; struct decode_x87_tbl invl_inst_x87 = {0x0, 0, 0, 0, 0, false, false, NULL, @@ -1427,207 +1412,207 @@ struct decode_x87_tbl invl_inst_x87 = {0x0, 0, 0, 0, 0, false, false, NULL, struct decode_x87_tbl _x87_inst[] = { {0xd8, 0, 3, X86_DECODE_CMD_FADD, 10, false, false, - decode_x87_modrm_st0, decode_decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_decode_x87_modrm_st0, NULL}, {0xd8, 0, 0, X86_DECODE_CMD_FADD, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL}, {0xd8, 1, 3, X86_DECODE_CMD_FMUL, 10, false, false, decode_x87_modrm_st0, - decode_decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_decode_x87_modrm_st0, NULL}, {0xd8, 1, 0, X86_DECODE_CMD_FMUL, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL}, {0xd8, 4, 3, X86_DECODE_CMD_FSUB, 10, false, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL}, {0xd8, 4, 0, X86_DECODE_CMD_FSUB, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL}, {0xd8, 5, 3, X86_DECODE_CMD_FSUB, 10, true, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL}, {0xd8, 5, 0, X86_DECODE_CMD_FSUB, 4, true, false, decode_x87_modrm_st0, - decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL}, {0xd8, 6, 3, X86_DECODE_CMD_FDIV, 10, false, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL}, {0xd8, 6, 0, X86_DECODE_CMD_FDIV, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL}, {0xd8, 7, 3, X86_DECODE_CMD_FDIV, 10, true, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL}, {0xd8, 7, 0, X86_DECODE_CMD_FDIV, 4, true, false, decode_x87_modrm_st0, - decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL}, {0xd9, 0, 3, X86_DECODE_CMD_FLD, 10, false, false, - decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL, NULL}, {0xd9, 0, 0, X86_DECODE_CMD_FLD, 4, false, false, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL, NULL}, {0xd9, 1, 3, X86_DECODE_CMD_FXCH, 10, false, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL}, {0xd9, 1, 0, X86_DECODE_CMD_INVL, 10, false, false, - decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL, NULL}, {0xd9, 2, 3, X86_DECODE_CMD_INVL, 10, false, false, - decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL, NULL}, {0xd9, 2, 0, X86_DECODE_CMD_FST, 4, false, false, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL, NULL}, {0xd9, 3, 3, X86_DECODE_CMD_INVL, 10, false, false, - decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL, NULL}, {0xd9, 3, 0, X86_DECODE_CMD_FST, 4, false, true, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL, NULL}, {0xd9, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, - decode_x87_modrm_st0, NULL, decode_d9_4, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL, decode_d9_4}, {0xd9, 4, 0, X86_DECODE_CMD_INVL, 4, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_bytep, NULL, NULL}, {0xd9, 5, 3, X86_DECODE_CMD_FLDxx, 10, false, false, NULL, NULL, NULL, RFLAGS_MASK_NONE}, {0xd9, 5, 0, X86_DECODE_CMD_FLDCW, 2, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_bytep, NULL, NULL}, {0xd9, 7, 3, X86_DECODE_CMD_FNSTCW, 2, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_bytep, NULL, NULL}, {0xd9, 7, 0, X86_DECODE_CMD_FNSTCW, 2, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_bytep, NULL, NULL}, {0xda, 0, 3, X86_DECODE_CMD_FCMOV, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xda, 0, 0, X86_DECODE_CMD_FADD, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL}, {0xda, 1, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, - decode_decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_decode_x87_modrm_st0, NULL}, {0xda, 1, 0, X86_DECODE_CMD_FMUL, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL}, {0xda, 2, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL}, {0xda, 3, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL}, {0xda, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, RFLAGS_MASK_NONE}, {0xda, 4, 0, X86_DECODE_CMD_FSUB, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL}, {0xda, 5, 3, X86_DECODE_CMD_FUCOM, 10, false, true, decode_x87_modrm_st0, - decode_decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_decode_x87_modrm_st0, NULL}, {0xda, 5, 0, X86_DECODE_CMD_FSUB, 4, true, false, decode_x87_modrm_st0, - decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL}, {0xda, 6, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, RFLAGS_MASK_NONE}, {0xda, 6, 0, X86_DECODE_CMD_FDIV, 4, false, false, decode_x87_modrm_st0, - decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL}, {0xda, 7, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, RFLAGS_MASK_NONE}, {0xda, 7, 0, X86_DECODE_CMD_FDIV, 4, true, false, decode_x87_modrm_st0, - decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL}, {0xdb, 0, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, - decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL}, {0xdb, 0, 0, X86_DECODE_CMD_FLD, 4, false, false, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL, NULL}, {0xdb, 1, 3, X86_DECODE_CMD_FCMOV, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdb, 2, 3, X86_DECODE_CMD_FCMOV, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdb, 2, 0, X86_DECODE_CMD_FST, 4, false, false, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL, NULL}, {0xdb, 3, 3, X86_DECODE_CMD_FCMOV, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdb, 3, 0, X86_DECODE_CMD_FST, 4, false, true, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL, NULL}, {0xdb, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, - decode_db_4, RFLAGS_MASK_NONE}, + decode_db_4}, {0xdb, 4, 0, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, RFLAGS_MASK_NONE}, {0xdb, 5, 3, X86_DECODE_CMD_FUCOMI, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdb, 5, 0, X86_DECODE_CMD_FLD, 10, false, false, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL, NULL}, {0xdb, 7, 0, X86_DECODE_CMD_FST, 10, false, true, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL, NULL}, {0xdc, 0, 3, X86_DECODE_CMD_FADD, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdc, 0, 0, X86_DECODE_CMD_FADD, 8, false, false, - decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL}, {0xdc, 1, 3, X86_DECODE_CMD_FMUL, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdc, 1, 0, X86_DECODE_CMD_FMUL, 8, false, false, - decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL}, {0xdc, 4, 3, X86_DECODE_CMD_FSUB, 10, true, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdc, 4, 0, X86_DECODE_CMD_FSUB, 8, false, false, - decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL}, {0xdc, 5, 3, X86_DECODE_CMD_FSUB, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdc, 5, 0, X86_DECODE_CMD_FSUB, 8, true, false, - decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL}, {0xdc, 6, 3, X86_DECODE_CMD_FDIV, 10, true, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdc, 6, 0, X86_DECODE_CMD_FDIV, 8, false, false, - decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL}, {0xdc, 7, 3, X86_DECODE_CMD_FDIV, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdc, 7, 0, X86_DECODE_CMD_FDIV, 8, true, false, - decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_floatp, NULL}, {0xdd, 0, 0, X86_DECODE_CMD_FLD, 8, false, false, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL, NULL}, {0xdd, 1, 3, X86_DECODE_CMD_FXCH, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdd, 2, 3, X86_DECODE_CMD_FST, 10, false, false, - decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL, NULL}, {0xdd, 2, 0, X86_DECODE_CMD_FST, 8, false, false, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL, NULL}, {0xdd, 3, 3, X86_DECODE_CMD_FST, 10, false, true, - decode_x87_modrm_st0, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, NULL, NULL}, {0xdd, 3, 0, X86_DECODE_CMD_FST, 8, false, true, - decode_x87_modrm_floatp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_floatp, NULL, NULL}, {0xdd, 4, 3, X86_DECODE_CMD_FUCOM, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdd, 4, 0, X86_DECODE_CMD_FRSTOR, 8, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_bytep, NULL, NULL}, {0xdd, 5, 3, X86_DECODE_CMD_FUCOM, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdd, 7, 0, X86_DECODE_CMD_FNSTSW, 0, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_bytep, NULL, NULL}, {0xdd, 7, 3, X86_DECODE_CMD_FNSTSW, 0, false, false, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_bytep, NULL, NULL}, {0xde, 0, 3, X86_DECODE_CMD_FADD, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xde, 0, 0, X86_DECODE_CMD_FADD, 2, false, false, - decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, {0xde, 1, 3, X86_DECODE_CMD_FMUL, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xde, 1, 0, X86_DECODE_CMD_FMUL, 2, false, false, - decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, {0xde, 4, 3, X86_DECODE_CMD_FSUB, 10, true, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xde, 4, 0, X86_DECODE_CMD_FSUB, 2, false, false, - decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, {0xde, 5, 3, X86_DECODE_CMD_FSUB, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xde, 5, 0, X86_DECODE_CMD_FSUB, 2, true, false, - decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, {0xde, 6, 3, X86_DECODE_CMD_FDIV, 10, true, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xde, 6, 0, X86_DECODE_CMD_FDIV, 2, false, false, - decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, {0xde, 7, 3, X86_DECODE_CMD_FDIV, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xde, 7, 0, X86_DECODE_CMD_FDIV, 2, true, false, - decode_x87_modrm_st0, decode_x87_modrm_intp, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, {0xdf, 0, 0, X86_DECODE_CMD_FLD, 2, false, false, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL, NULL}, {0xdf, 1, 3, X86_DECODE_CMD_FXCH, 10, false, false, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdf, 2, 3, X86_DECODE_CMD_FST, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdf, 2, 0, X86_DECODE_CMD_FST, 2, false, false, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL, NULL}, {0xdf, 3, 3, X86_DECODE_CMD_FST, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdf, 3, 0, X86_DECODE_CMD_FST, 2, false, true, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL, NULL}, {0xdf, 4, 3, X86_DECODE_CMD_FNSTSW, 2, false, true, - decode_x87_modrm_bytep, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_bytep, NULL, NULL}, {0xdf, 5, 3, X86_DECODE_CMD_FUCOMI, 10, false, true, - decode_x87_modrm_st0, decode_x87_modrm_st0, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdf, 5, 0, X86_DECODE_CMD_FLD, 8, false, false, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL, NULL}, {0xdf, 7, 0, X86_DECODE_CMD_FST, 8, false, true, - decode_x87_modrm_intp, NULL, NULL, RFLAGS_MASK_NONE}, + decode_x87_modrm_intp, NULL, NULL}, }; void calc_modrm_operand16(CPUX86State *env, struct x86_decode *decode, @@ -2045,7 +2030,6 @@ static inline void decode_opcode_general(CPUX86State *env, if (inst_decoder->operand_size) { decode->operand_size = inst_decoder->operand_size; } - decode->flags_mask = inst_decoder->flags_mask; if (inst_decoder->is_modrm) { decode_modrm(env, decode); diff --git a/target/i386/emulate/x86_decode.h b/target/i386/emulate/x86_decode.h index 930d965164..87cc728598 100644 --- a/target/i386/emulate/x86_decode.h +++ b/target/i386/emulate/x86_decode.h @@ -295,8 +295,6 @@ typedef struct x86_decode { struct x86_modrm modrm; struct x86_decode_op op[4]; bool is_fpu; - uint32_t flags_mask; - } x86_decode; uint64_t sign(uint64_t val, int size); From ae39acef49e29169f90cd3a799d6cd0b50bc65d2 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 10 Apr 2025 15:56:19 +0800 Subject: [PATCH 0034/2760] i386/cpu: Consolidate the helper to get Host's vendor Extend host_cpu_vendor_fms() to help more cases to get Host's vendor information. Cc: Dongli Zhang Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250410075619.145792-1-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/host-cpu.c | 10 ++++++---- target/i386/kvm/vmsr_energy.c | 3 +-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index 3e4e85e729..072731a4dd 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -109,9 +109,13 @@ void host_cpu_vendor_fms(char *vendor, int *family, int *model, int *stepping) { uint32_t eax, ebx, ecx, edx; - host_cpuid(0x0, 0, &eax, &ebx, &ecx, &edx); + host_cpuid(0x0, 0, NULL, &ebx, &ecx, &edx); x86_cpu_vendor_words2str(vendor, ebx, edx, ecx); + if (!family && !model && !stepping) { + return; + } + host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx); if (family) { *family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF); @@ -129,11 +133,9 @@ void host_cpu_instance_init(X86CPU *cpu) X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); if (xcc->model) { - uint32_t ebx = 0, ecx = 0, edx = 0; char vendor[CPUID_VENDOR_SZ + 1]; - host_cpuid(0, 0, NULL, &ebx, &ecx, &edx); - x86_cpu_vendor_words2str(vendor, ebx, edx, ecx); + host_cpu_vendor_fms(vendor, NULL, NULL, NULL); object_property_set_str(OBJECT(cpu), "vendor", vendor, &error_abort); } } diff --git a/target/i386/kvm/vmsr_energy.c b/target/i386/kvm/vmsr_energy.c index 31508d4e77..f499ec6e8b 100644 --- a/target/i386/kvm/vmsr_energy.c +++ b/target/i386/kvm/vmsr_energy.c @@ -29,10 +29,9 @@ char *vmsr_compute_default_paths(void) bool is_host_cpu_intel(void) { - int family, model, stepping; char vendor[CPUID_VENDOR_SZ + 1]; - host_cpu_vendor_fms(vendor, &family, &model, &stepping); + host_cpu_vendor_fms(vendor, NULL, NULL, NULL); return g_str_equal(vendor, CPUID_VENDOR_INTEL); } From 1da8f3a3c53b604edfe0d55e475102640490549e Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 22 Apr 2025 15:09:23 -0400 Subject: [PATCH 0035/2760] Open 10.1 development tree Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index a13e7b9c87..54e6ccf854 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.0 +10.0.50 From 6b661b7ed7cd02c54a78426d5eb7dd8543b030ed Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 23 Mar 2025 10:16:23 -0700 Subject: [PATCH 0036/2760] target/avr: Improve decode of LDS, STS The comment about not being able to define a field with zero bits is out of date since 94597b6146f3 ("decodetree: Allow !function with no input bits"). This fixes the missing load of imm in the disassembler. Cc: qemu-stable@nongnu.org Fixes: 9d8caa67a24 ("target/avr: Add support for disassembling via option '-d in_asm'") Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/avr/insn.decode | 7 ++----- target/avr/translate.c | 2 -- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/target/avr/insn.decode b/target/avr/insn.decode index 482c23ad0c..cc302249db 100644 --- a/target/avr/insn.decode +++ b/target/avr/insn.decode @@ -118,11 +118,8 @@ BRBC 1111 01 ....... ... @op_bit_imm @io_rd_imm .... . .. ..... .... &rd_imm rd=%rd imm=%io_imm @ldst_d .. . . .. . rd:5 . ... &rd_imm imm=%ldst_d_imm -# The 16-bit immediate is completely in the next word. -# Fields cannot be defined with no bits, so we cannot play -# the same trick and append to a zero-bit value. -# Defer reading the immediate until trans_{LDS,STS}. -@ldst_s .... ... rd:5 .... imm=0 +%ldst_imm !function=next_word +@ldst_s .... ... rd:5 .... imm=%ldst_imm MOV 0010 11 . ..... .... @op_rd_rr MOVW 0000 0001 .... .... &rd_rr rd=%rd_d rr=%rr_d diff --git a/target/avr/translate.c b/target/avr/translate.c index 4ab71d8138..e7f8ced9b3 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -1578,7 +1578,6 @@ static bool trans_LDS(DisasContext *ctx, arg_LDS *a) TCGv Rd = cpu_r[a->rd]; TCGv addr = tcg_temp_new_i32(); TCGv H = cpu_rampD; - a->imm = next_word(ctx); tcg_gen_mov_tl(addr, H); /* addr = H:M:L */ tcg_gen_shli_tl(addr, addr, 16); @@ -1783,7 +1782,6 @@ static bool trans_STS(DisasContext *ctx, arg_STS *a) TCGv Rd = cpu_r[a->rd]; TCGv addr = tcg_temp_new_i32(); TCGv H = cpu_rampD; - a->imm = next_word(ctx); tcg_gen_mov_tl(addr, H); /* addr = H:M:L */ tcg_gen_shli_tl(addr, addr, 16); From 29bcd5a46a9de61587f490d92c5a5500b2684f22 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 21 Mar 2025 14:31:26 -0700 Subject: [PATCH 0037/2760] target/avr: Remove OFFSET_CPU_REGISTERS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This define isn't really used. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/avr/cpu.h | 2 -- target/avr/helper.c | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/target/avr/cpu.h b/target/avr/cpu.h index 06f5ae4d1b..84a8f5cc8c 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -60,8 +60,6 @@ #define OFFSET_CODE 0x00000000 /* CPU registers, IO registers, and SRAM */ #define OFFSET_DATA 0x00800000 -/* CPU registers specifically, these are mapped at the start of data */ -#define OFFSET_CPU_REGISTERS OFFSET_DATA /* * IO registers, including status register, stack pointer, and memory * mapped peripherals, mapped just after CPU registers diff --git a/target/avr/helper.c b/target/avr/helper.c index 3412312ad5..e5bf16c6b7 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -340,8 +340,7 @@ void helper_fullwr(CPUAVRState *env, uint32_t data, uint32_t addr) env->fullacc = false; /* Following logic assumes this: */ - assert(OFFSET_CPU_REGISTERS == OFFSET_DATA); - assert(OFFSET_IO_REGISTERS == OFFSET_CPU_REGISTERS + + assert(OFFSET_IO_REGISTERS == OFFSET_DATA + NUMBER_OF_CPU_REGISTERS); if (addr < NUMBER_OF_CPU_REGISTERS) { From a2860ff908da0d71ce25adcb02388fe26b467390 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Mar 2025 08:58:56 -0700 Subject: [PATCH 0038/2760] target/avr: Add defines for i/o port registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/avr/cpu.h | 10 ++++++++++ target/avr/helper.c | 36 ++++++++++++++++++------------------ 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/target/avr/cpu.h b/target/avr/cpu.h index 84a8f5cc8c..1a5a5b8e3e 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -47,6 +47,16 @@ /* Number of IO registers accessible by ld/st/in/out */ #define NUMBER_OF_IO_REGISTERS 64 +/* CPU registers mapped into i/o ports 0x38-0x3f. */ +#define REG_38_RAMPD 0 +#define REG_38_RAMPX 1 +#define REG_38_RAMPY 2 +#define REG_38_RAMPZ 3 +#define REG_38_EIDN 4 +#define REG_38_SPL 5 +#define REG_38_SPH 6 +#define REG_38_SREG 7 + /* * Offsets of AVR memory regions in host memory space. * diff --git a/target/avr/helper.c b/target/avr/helper.c index e5bf16c6b7..f8ada8b106 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -216,29 +216,29 @@ target_ulong helper_inb(CPUAVRState *env, uint32_t port) { target_ulong data = 0; - switch (port) { - case 0x38: /* RAMPD */ + switch (port - 0x38) { + case REG_38_RAMPD: data = 0xff & (env->rampD >> 16); break; - case 0x39: /* RAMPX */ + case REG_38_RAMPX: data = 0xff & (env->rampX >> 16); break; - case 0x3a: /* RAMPY */ + case REG_38_RAMPY: data = 0xff & (env->rampY >> 16); break; - case 0x3b: /* RAMPZ */ + case REG_38_RAMPZ: data = 0xff & (env->rampZ >> 16); break; - case 0x3c: /* EIND */ + case REG_38_EIDN: data = 0xff & (env->eind >> 16); break; - case 0x3d: /* SPL */ + case REG_38_SPL: data = env->sp & 0x00ff; break; - case 0x3e: /* SPH */ + case REG_38_SPH: data = env->sp >> 8; break; - case 0x3f: /* SREG */ + case REG_38_SREG: data = cpu_get_sreg(env); break; default: @@ -265,39 +265,39 @@ void helper_outb(CPUAVRState *env, uint32_t port, uint32_t data) { data &= 0x000000ff; - switch (port) { - case 0x38: /* RAMPD */ + switch (port - 0x38) { + case REG_38_RAMPD: if (avr_feature(env, AVR_FEATURE_RAMPD)) { env->rampD = (data & 0xff) << 16; } break; - case 0x39: /* RAMPX */ + case REG_38_RAMPX: if (avr_feature(env, AVR_FEATURE_RAMPX)) { env->rampX = (data & 0xff) << 16; } break; - case 0x3a: /* RAMPY */ + case REG_38_RAMPY: if (avr_feature(env, AVR_FEATURE_RAMPY)) { env->rampY = (data & 0xff) << 16; } break; - case 0x3b: /* RAMPZ */ + case REG_38_RAMPZ: if (avr_feature(env, AVR_FEATURE_RAMPZ)) { env->rampZ = (data & 0xff) << 16; } break; - case 0x3c: /* EIDN */ + case REG_38_EIDN: env->eind = (data & 0xff) << 16; break; - case 0x3d: /* SPL */ + case REG_38_SPL: env->sp = (env->sp & 0xff00) | (data); break; - case 0x3e: /* SPH */ + case REG_38_SPH: if (avr_feature(env, AVR_FEATURE_2_BYTE_SP)) { env->sp = (env->sp & 0x00ff) | (data << 8); } break; - case 0x3f: /* SREG */ + case REG_38_SREG: cpu_set_sreg(env, data); break; default: From 204a7bd85686601e7c60d1d23502540a8d9661bb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 21 Mar 2025 17:14:27 -0700 Subject: [PATCH 0039/2760] target/avr: Move cpu register accesses into system memory Integrate the i/o 0x00-0x1f and 0x38-0x3f loopbacks into the cpu registers with normal address space accesses. We no longer need to trap accesses to the first page within avr_cpu_tlb_fill but can wait until a write occurs. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/avr/cpu.c | 16 ++++ target/avr/cpu.h | 7 ++ target/avr/helper.c | 209 +++++++++++++++++------------------------ target/avr/helper.h | 3 - target/avr/translate.c | 42 +++++---- 5 files changed, 131 insertions(+), 146 deletions(-) diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 834c7082aa..0b14b36c17 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -23,6 +23,7 @@ #include "qemu/qemu-print.h" #include "exec/exec-all.h" #include "exec/translation-block.h" +#include "exec/address-spaces.h" #include "cpu.h" #include "disas/dis-asm.h" #include "tcg/debug-assert.h" @@ -110,6 +111,8 @@ static void avr_cpu_disas_set_info(CPUState *cpu, disassemble_info *info) static void avr_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); + CPUAVRState *env = cpu_env(cs); + AVRCPU *cpu = env_archcpu(env); AVRCPUClass *mcc = AVR_CPU_GET_CLASS(dev); Error *local_err = NULL; @@ -122,6 +125,19 @@ static void avr_cpu_realizefn(DeviceState *dev, Error **errp) cpu_reset(cs); mcc->parent_realize(dev, errp); + + /* + * Two blocks in the low data space loop back into cpu registers. + */ + memory_region_init_io(&cpu->cpu_reg1, OBJECT(cpu), &avr_cpu_reg1, env, + "avr-cpu-reg1", 32); + memory_region_add_subregion(get_system_memory(), + OFFSET_DATA, &cpu->cpu_reg1); + + memory_region_init_io(&cpu->cpu_reg2, OBJECT(cpu), &avr_cpu_reg2, env, + "avr-cpu-reg2", 8); + memory_region_add_subregion(get_system_memory(), + OFFSET_DATA + 0x58, &cpu->cpu_reg2); } static void avr_cpu_set_int(void *opaque, int irq, int level) diff --git a/target/avr/cpu.h b/target/avr/cpu.h index 1a5a5b8e3e..6f68060ab0 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -23,6 +23,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/memory.h" #ifdef CONFIG_USER_ONLY #error "AVR 8-bit does not support user mode" @@ -152,6 +153,9 @@ struct ArchCPU { CPUAVRState env; + MemoryRegion cpu_reg1; + MemoryRegion cpu_reg2; + /* Initial value of stack pointer */ uint32_t init_sp; }; @@ -252,6 +256,9 @@ bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); +extern const MemoryRegionOps avr_cpu_reg1; +extern const MemoryRegionOps avr_cpu_reg2; + #include "exec/cpu-all.h" #endif /* QEMU_AVR_CPU_H */ diff --git a/target/avr/helper.c b/target/avr/helper.c index f8ada8b106..d0e86f5614 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -108,7 +108,7 @@ bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr) { - int prot, page_size = TARGET_PAGE_SIZE; + int prot; uint32_t paddr; address &= TARGET_PAGE_MASK; @@ -133,23 +133,9 @@ bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size, /* Access to memory. */ paddr = OFFSET_DATA + address; prot = PAGE_READ | PAGE_WRITE; - if (address < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) { - /* - * Access to CPU registers, exit and rebuilt this TB to use - * full access in case it touches specially handled registers - * like SREG or SP. For probing, set page_size = 1, in order - * to force tlb_fill to be called for the next access. - */ - if (probe) { - page_size = 1; - } else { - cpu_env(cs)->fullacc = 1; - cpu_loop_exit_restore(cs, retaddr); - } - } } - tlb_set_page(cs, address, paddr, prot, mmu_idx, page_size); + tlb_set_page(cs, address, paddr, prot, mmu_idx, TARGET_PAGE_SIZE); return true; } @@ -203,155 +189,130 @@ void helper_wdr(CPUAVRState *env) } /* - * This function implements IN instruction - * - * It does the following - * a. if an IO register belongs to CPU, its value is read and returned - * b. otherwise io address is translated to mem address and physical memory - * is read. - * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation - * + * The first 32 bytes of the data space are mapped to the cpu regs. + * We cannot write these from normal store operations because TCG + * does not expect global temps to be modified -- a global may be + * live in a host cpu register across the store. We can however + * read these, as TCG does make sure the global temps are saved + * in case the load operation traps. */ -target_ulong helper_inb(CPUAVRState *env, uint32_t port) + +static uint64_t avr_cpu_reg1_read(void *opaque, hwaddr addr, unsigned size) { - target_ulong data = 0; + CPUAVRState *env = opaque; + + assert(addr < 32); + return env->r[addr]; +} - switch (port - 0x38) { +/* + * The range 0x38-0x3f of the i/o space is mapped to cpu regs. + * As above, we cannot write these from normal store operations. + */ + +static uint64_t avr_cpu_reg2_read(void *opaque, hwaddr addr, unsigned size) +{ + CPUAVRState *env = opaque; + + switch (addr) { case REG_38_RAMPD: - data = 0xff & (env->rampD >> 16); - break; + return 0xff & (env->rampD >> 16); case REG_38_RAMPX: - data = 0xff & (env->rampX >> 16); - break; + return 0xff & (env->rampX >> 16); case REG_38_RAMPY: - data = 0xff & (env->rampY >> 16); - break; + return 0xff & (env->rampY >> 16); case REG_38_RAMPZ: - data = 0xff & (env->rampZ >> 16); - break; + return 0xff & (env->rampZ >> 16); case REG_38_EIDN: - data = 0xff & (env->eind >> 16); - break; + return 0xff & (env->eind >> 16); case REG_38_SPL: - data = env->sp & 0x00ff; - break; + return env->sp & 0x00ff; case REG_38_SPH: - data = env->sp >> 8; - break; + return 0xff & (env->sp >> 8); case REG_38_SREG: - data = cpu_get_sreg(env); - break; - default: - /* not a special register, pass to normal memory access */ - data = address_space_ldub(&address_space_memory, - OFFSET_IO_REGISTERS + port, - MEMTXATTRS_UNSPECIFIED, NULL); + return cpu_get_sreg(env); } + g_assert_not_reached(); +} - return data; +static void avr_cpu_trap_write(void *opaque, hwaddr addr, + uint64_t data64, unsigned size) +{ + CPUAVRState *env = opaque; + CPUState *cs = env_cpu(env); + + env->fullacc = true; + cpu_loop_exit_restore(cs, cs->mem_io_pc); } +const MemoryRegionOps avr_cpu_reg1 = { + .read = avr_cpu_reg1_read, + .write = avr_cpu_trap_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 1, +}; + +const MemoryRegionOps avr_cpu_reg2 = { + .read = avr_cpu_reg2_read, + .write = avr_cpu_trap_write, + .endianness = DEVICE_NATIVE_ENDIAN, + .valid.min_access_size = 1, + .valid.max_access_size = 1, +}; + /* - * This function implements OUT instruction - * - * It does the following - * a. if an IO register belongs to CPU, its value is written into the register - * b. otherwise io address is translated to mem address and physical memory - * is written. - * c. it caches the value for sake of SBI, SBIC, SBIS & CBI implementation - * + * this function implements ST instruction when there is a possibility to write + * into a CPU register */ -void helper_outb(CPUAVRState *env, uint32_t port, uint32_t data) +void helper_fullwr(CPUAVRState *env, uint32_t data, uint32_t addr) { - data &= 0x000000ff; + env->fullacc = false; - switch (port - 0x38) { - case REG_38_RAMPD: + switch (addr) { + case 0 ... 31: + /* CPU registers */ + env->r[addr] = data; + break; + + case REG_38_RAMPD + 0x38 + NUMBER_OF_CPU_REGISTERS: if (avr_feature(env, AVR_FEATURE_RAMPD)) { - env->rampD = (data & 0xff) << 16; + env->rampD = data << 16; } break; - case REG_38_RAMPX: + case REG_38_RAMPX + 0x38 + NUMBER_OF_CPU_REGISTERS: if (avr_feature(env, AVR_FEATURE_RAMPX)) { - env->rampX = (data & 0xff) << 16; + env->rampX = data << 16; } break; - case REG_38_RAMPY: + case REG_38_RAMPY + 0x38 + NUMBER_OF_CPU_REGISTERS: if (avr_feature(env, AVR_FEATURE_RAMPY)) { - env->rampY = (data & 0xff) << 16; + env->rampY = data << 16; } break; - case REG_38_RAMPZ: + case REG_38_RAMPZ + 0x38 + NUMBER_OF_CPU_REGISTERS: if (avr_feature(env, AVR_FEATURE_RAMPZ)) { - env->rampZ = (data & 0xff) << 16; + env->rampZ = data << 16; } break; - case REG_38_EIDN: - env->eind = (data & 0xff) << 16; + case REG_38_EIDN + 0x38 + NUMBER_OF_CPU_REGISTERS: + env->eind = data << 16; break; - case REG_38_SPL: - env->sp = (env->sp & 0xff00) | (data); + case REG_38_SPL + 0x38 + NUMBER_OF_CPU_REGISTERS: + env->sp = (env->sp & 0xff00) | data; break; - case REG_38_SPH: + case REG_38_SPH + 0x38 + NUMBER_OF_CPU_REGISTERS: if (avr_feature(env, AVR_FEATURE_2_BYTE_SP)) { env->sp = (env->sp & 0x00ff) | (data << 8); } break; - case REG_38_SREG: + case REG_38_SREG + 0x38 + NUMBER_OF_CPU_REGISTERS: cpu_set_sreg(env, data); break; - default: - /* not a special register, pass to normal memory access */ - address_space_stb(&address_space_memory, OFFSET_IO_REGISTERS + port, - data, MEMTXATTRS_UNSPECIFIED, NULL); - } -} -/* - * this function implements LD instruction when there is a possibility to read - * from a CPU register - */ -target_ulong helper_fullrd(CPUAVRState *env, uint32_t addr) -{ - uint8_t data; - - env->fullacc = false; - - if (addr < NUMBER_OF_CPU_REGISTERS) { - /* CPU registers */ - data = env->r[addr]; - } else if (addr < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) { - /* IO registers */ - data = helper_inb(env, addr - NUMBER_OF_CPU_REGISTERS); - } else { - /* memory */ - data = address_space_ldub(&address_space_memory, OFFSET_DATA + addr, - MEMTXATTRS_UNSPECIFIED, NULL); - } - return data; -} - -/* - * this function implements ST instruction when there is a possibility to write - * into a CPU register - */ -void helper_fullwr(CPUAVRState *env, uint32_t data, uint32_t addr) -{ - env->fullacc = false; - - /* Following logic assumes this: */ - assert(OFFSET_IO_REGISTERS == OFFSET_DATA + - NUMBER_OF_CPU_REGISTERS); - - if (addr < NUMBER_OF_CPU_REGISTERS) { - /* CPU registers */ - env->r[addr] = data; - } else if (addr < NUMBER_OF_CPU_REGISTERS + NUMBER_OF_IO_REGISTERS) { - /* IO registers */ - helper_outb(env, addr - NUMBER_OF_CPU_REGISTERS, data); - } else { - /* memory */ + default: address_space_stb(&address_space_memory, OFFSET_DATA + addr, data, MEMTXATTRS_UNSPECIFIED, NULL); + break; } } diff --git a/target/avr/helper.h b/target/avr/helper.h index 4d02e648fa..e8d13e925f 100644 --- a/target/avr/helper.h +++ b/target/avr/helper.h @@ -23,7 +23,4 @@ DEF_HELPER_1(debug, noreturn, env) DEF_HELPER_1(break, noreturn, env) DEF_HELPER_1(sleep, noreturn, env) DEF_HELPER_1(unsupported, noreturn, env) -DEF_HELPER_3(outb, void, env, i32, i32) -DEF_HELPER_2(inb, tl, env, i32) DEF_HELPER_3(fullwr, void, env, i32, i32) -DEF_HELPER_2(fullrd, tl, env, i32) diff --git a/target/avr/translate.c b/target/avr/translate.c index e7f8ced9b3..0490936cd5 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -194,6 +194,9 @@ static bool avr_have_feature(DisasContext *ctx, int feature) static bool decode_insn(DisasContext *ctx, uint16_t insn); #include "decode-insn.c.inc" +static void gen_inb(DisasContext *ctx, TCGv data, int port); +static void gen_outb(DisasContext *ctx, TCGv data, int port); + /* * Arithmetic Instructions */ @@ -1293,9 +1296,8 @@ static bool trans_SBRS(DisasContext *ctx, arg_SBRS *a) static bool trans_SBIC(DisasContext *ctx, arg_SBIC *a) { TCGv data = tcg_temp_new_i32(); - TCGv port = tcg_constant_i32(a->reg); - gen_helper_inb(data, tcg_env, port); + gen_inb(ctx, data, a->reg); tcg_gen_andi_tl(data, data, 1 << a->bit); ctx->skip_cond = TCG_COND_EQ; ctx->skip_var0 = data; @@ -1311,9 +1313,8 @@ static bool trans_SBIC(DisasContext *ctx, arg_SBIC *a) static bool trans_SBIS(DisasContext *ctx, arg_SBIS *a) { TCGv data = tcg_temp_new_i32(); - TCGv port = tcg_constant_i32(a->reg); - gen_helper_inb(data, tcg_env, port); + gen_inb(ctx, data, a->reg); tcg_gen_andi_tl(data, data, 1 << a->bit); ctx->skip_cond = TCG_COND_NE; ctx->skip_var0 = data; @@ -1502,11 +1503,18 @@ static void gen_data_store(DisasContext *ctx, TCGv data, TCGv addr) static void gen_data_load(DisasContext *ctx, TCGv data, TCGv addr) { - if (ctx->base.tb->flags & TB_FLAGS_FULL_ACCESS) { - gen_helper_fullrd(data, tcg_env, addr); - } else { - tcg_gen_qemu_ld_tl(data, addr, MMU_DATA_IDX, MO_UB); - } + tcg_gen_qemu_ld_tl(data, addr, MMU_DATA_IDX, MO_UB); +} + +static void gen_inb(DisasContext *ctx, TCGv data, int port) +{ + gen_data_load(ctx, data, tcg_constant_i32(port + NUMBER_OF_CPU_REGISTERS)); +} + +static void gen_outb(DisasContext *ctx, TCGv data, int port) +{ + gen_helper_fullwr(tcg_env, data, + tcg_constant_i32(port + NUMBER_OF_CPU_REGISTERS)); } /* @@ -2126,9 +2134,8 @@ static bool trans_SPMX(DisasContext *ctx, arg_SPMX *a) static bool trans_IN(DisasContext *ctx, arg_IN *a) { TCGv Rd = cpu_r[a->rd]; - TCGv port = tcg_constant_i32(a->imm); - gen_helper_inb(Rd, tcg_env, port); + gen_inb(ctx, Rd, a->imm); return true; } @@ -2139,9 +2146,8 @@ static bool trans_IN(DisasContext *ctx, arg_IN *a) static bool trans_OUT(DisasContext *ctx, arg_OUT *a) { TCGv Rd = cpu_r[a->rd]; - TCGv port = tcg_constant_i32(a->imm); - gen_helper_outb(tcg_env, port, Rd); + gen_outb(ctx, Rd, a->imm); return true; } @@ -2407,11 +2413,10 @@ static bool trans_SWAP(DisasContext *ctx, arg_SWAP *a) static bool trans_SBI(DisasContext *ctx, arg_SBI *a) { TCGv data = tcg_temp_new_i32(); - TCGv port = tcg_constant_i32(a->reg); - gen_helper_inb(data, tcg_env, port); + gen_inb(ctx, data, a->reg); tcg_gen_ori_tl(data, data, 1 << a->bit); - gen_helper_outb(tcg_env, port, data); + gen_outb(ctx, data, a->reg); return true; } @@ -2422,11 +2427,10 @@ static bool trans_SBI(DisasContext *ctx, arg_SBI *a) static bool trans_CBI(DisasContext *ctx, arg_CBI *a) { TCGv data = tcg_temp_new_i32(); - TCGv port = tcg_constant_i32(a->reg); - gen_helper_inb(data, tcg_env, port); + gen_inb(ctx, data, a->reg); tcg_gen_andi_tl(data, data, ~(1 << a->bit)); - gen_helper_outb(tcg_env, port, data); + gen_outb(ctx, data, a->reg); return true; } From ba675861ea6f1f8b3eeee4a2d64b47204b883ab1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Mar 2025 08:37:49 -0700 Subject: [PATCH 0040/2760] target/avr: Remove NUMBER_OF_IO_REGISTERS This define is no longer used. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/avr/cpu.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/avr/cpu.h b/target/avr/cpu.h index 6f68060ab0..9862705c6a 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -45,8 +45,6 @@ /* Number of CPU registers */ #define NUMBER_OF_CPU_REGISTERS 32 -/* Number of IO registers accessible by ld/st/in/out */ -#define NUMBER_OF_IO_REGISTERS 64 /* CPU registers mapped into i/o ports 0x38-0x3f. */ #define REG_38_RAMPD 0 From c0f830cb6a756add847f9cd9d62d50397a1284c1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 21 Mar 2025 18:19:28 -0700 Subject: [PATCH 0041/2760] target/avr: Use cpu_stb_mmuidx_ra in helper_fullwr Avoid direct use of address_space_memory. Make use of the softmmu cache of the i/o page. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/avr/helper.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/target/avr/helper.c b/target/avr/helper.c index d0e86f5614..7d6954ec26 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -23,10 +23,10 @@ #include "qemu/error-report.h" #include "cpu.h" #include "accel/tcg/cpu-ops.h" +#include "accel/tcg/getpc.h" #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" -#include "exec/address-spaces.h" #include "exec/helper-proto.h" bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) @@ -67,6 +67,11 @@ bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) return false; } +static void do_stb(CPUAVRState *env, uint32_t addr, uint8_t data, uintptr_t ra) +{ + cpu_stb_mmuidx_ra(env, addr, data, MMU_DATA_IDX, ra); +} + void avr_cpu_do_interrupt(CPUState *cs) { CPUAVRState *env = cpu_env(cs); @@ -311,8 +316,7 @@ void helper_fullwr(CPUAVRState *env, uint32_t data, uint32_t addr) break; default: - address_space_stb(&address_space_memory, OFFSET_DATA + addr, data, - MEMTXATTRS_UNSPECIFIED, NULL); + do_stb(env, addr, data, GETPC()); break; } } From 95d4f72d6a667c71adae8a3c8093efe32043d9b4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 22 Mar 2025 20:10:28 -0700 Subject: [PATCH 0042/2760] target/avr: Use do_stb in avr_cpu_do_interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/avr/helper.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/avr/helper.c b/target/avr/helper.c index 7d6954ec26..f23fa3e8ba 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -88,14 +88,14 @@ void avr_cpu_do_interrupt(CPUState *cs) } if (avr_feature(env, AVR_FEATURE_3_BYTE_PC)) { - cpu_stb_data(env, env->sp--, (ret & 0x0000ff)); - cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8); - cpu_stb_data(env, env->sp--, (ret & 0xff0000) >> 16); + do_stb(env, env->sp--, ret, 0); + do_stb(env, env->sp--, ret >> 8, 0); + do_stb(env, env->sp--, ret >> 16, 0); } else if (avr_feature(env, AVR_FEATURE_2_BYTE_PC)) { - cpu_stb_data(env, env->sp--, (ret & 0x0000ff)); - cpu_stb_data(env, env->sp--, (ret & 0x00ff00) >> 8); + do_stb(env, env->sp--, ret, 0); + do_stb(env, env->sp--, ret >> 8, 0); } else { - cpu_stb_data(env, env->sp--, (ret & 0x0000ff)); + do_stb(env, env->sp--, ret, 0); } env->pc_w = base + vector * size; From 9f99072fc08e1044700404ae0a5f13a30a0a2a60 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Mar 2025 15:26:28 -0700 Subject: [PATCH 0043/2760] hw/avr: Prepare for TARGET_PAGE_SIZE > 256 If i/o does not cover the entire first page, allocate a portion of ram as an i/o device, so that the entire first page is i/o. While memory_region_init_ram_device_ptr is happy to allocate the RAMBlock, it does not register the ram for migration. Do this by hand. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- hw/avr/atmega.c | 39 ++++++++++++++++++++++++++++++++------- hw/avr/atmega.h | 1 + 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index f6844bf118..11fab184de 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -19,6 +19,7 @@ #include "hw/sysbus.h" #include "qom/object.h" #include "hw/misc/unimp.h" +#include "migration/vmstate.h" #include "atmega.h" enum AtmegaPeripheral { @@ -224,8 +225,6 @@ static void atmega_realize(DeviceState *dev, Error **errp) char *devname; size_t i; - assert(mc->io_size <= 0x200); - if (!s->xtal_freq_hz) { error_setg(errp, "\"xtal-frequency-hz\" property must be provided."); return; @@ -240,11 +239,37 @@ static void atmega_realize(DeviceState *dev, Error **errp) qdev_realize(DEVICE(&s->cpu), NULL, &error_abort); cpudev = DEVICE(&s->cpu); - /* SRAM */ - memory_region_init_ram(&s->sram, OBJECT(dev), "sram", mc->sram_size, - &error_abort); - memory_region_add_subregion(get_system_memory(), - OFFSET_DATA + mc->io_size, &s->sram); + /* + * SRAM + * + * Softmmu is not able mix i/o and ram on the same page. + * Therefore in all cases, the first page exclusively contains i/o. + * + * If the MCU's i/o region matches the page size, then we can simply + * allocate all ram starting at the second page. Otherwise, we must + * allocate some ram as i/o to complete the first page. + */ + assert(mc->io_size == 0x100 || mc->io_size == 0x200); + if (mc->io_size >= TARGET_PAGE_SIZE) { + memory_region_init_ram(&s->sram, OBJECT(dev), "sram", mc->sram_size, + &error_abort); + memory_region_add_subregion(get_system_memory(), + OFFSET_DATA + mc->io_size, &s->sram); + } else { + int sram_io_size = TARGET_PAGE_SIZE - mc->io_size; + void *sram_io_mem = g_malloc0(sram_io_size); + + memory_region_init_ram_device_ptr(&s->sram_io, OBJECT(dev), "sram-as-io", + sram_io_size, sram_io_mem); + memory_region_add_subregion(get_system_memory(), + OFFSET_DATA + mc->io_size, &s->sram_io); + vmstate_register_ram(&s->sram_io, dev); + + memory_region_init_ram(&s->sram, OBJECT(dev), "sram", + mc->sram_size - sram_io_size, &error_abort); + memory_region_add_subregion(get_system_memory(), + OFFSET_DATA + TARGET_PAGE_SIZE, &s->sram); + } /* Flash */ memory_region_init_rom(&s->flash, OBJECT(dev), diff --git a/hw/avr/atmega.h b/hw/avr/atmega.h index a99ee15c7e..9ac4678231 100644 --- a/hw/avr/atmega.h +++ b/hw/avr/atmega.h @@ -41,6 +41,7 @@ struct AtmegaMcuState { MemoryRegion flash; MemoryRegion eeprom; MemoryRegion sram; + MemoryRegion sram_io; DeviceState *io; AVRMaskState pwr[POWER_MAX]; AVRUsartState usart[USART_MAX]; From eba24b60a72115e21e850977b3019aaf037c66c9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Mar 2025 15:32:50 -0700 Subject: [PATCH 0044/2760] target/avr: Increase TARGET_PAGE_BITS to 10 Now that we can handle the MCU allocating only a portion of the first page to i/o, increase the page size. Choose 10 as larger than the i/o on every MCU, just so that this path is tested. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/avr/cpu-param.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/target/avr/cpu-param.h b/target/avr/cpu-param.h index 81f3f49ee1..f5248ce9e7 100644 --- a/target/avr/cpu-param.h +++ b/target/avr/cpu-param.h @@ -21,13 +21,7 @@ #ifndef AVR_CPU_PARAM_H #define AVR_CPU_PARAM_H -/* - * TARGET_PAGE_BITS cannot be more than 8 bits because - * 1. all IO registers occupy [0x0000 .. 0x00ff] address range, and they - * should be implemented as a device and not memory - * 2. SRAM starts at the address 0x0100 - */ -#define TARGET_PAGE_BITS 8 +#define TARGET_PAGE_BITS 10 #define TARGET_PHYS_ADDR_SPACE_BITS 24 #define TARGET_VIRT_ADDR_SPACE_BITS 24 From 2de267c330381d44862a29efd9025d64c9c0e9b4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 15 Jan 2025 08:38:12 +0100 Subject: [PATCH 0045/2760] hw/s390x/s390-virtio-ccw: Remove the deprecated 2.9 machine type The s390-ccw-virtio-2.9 machine is older than 6 years, so according to our machine support policy, it can be removed now. Message-ID: <20250115073819.15452-2-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-virtio-ccw.c | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 75b32182eb..72de2bd8dc 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -1270,30 +1270,6 @@ static void ccw_machine_2_10_class_options(MachineClass *mc) } DEFINE_CCW_MACHINE(2, 10); -static void ccw_machine_2_9_instance_options(MachineState *machine) -{ - ccw_machine_2_10_instance_options(machine); - s390_cpudef_featoff_greater(12, 1, S390_FEAT_ESOP); - s390_cpudef_featoff_greater(12, 1, S390_FEAT_SIDE_EFFECT_ACCESS_ESOP2); - s390_cpudef_featoff_greater(12, 1, S390_FEAT_ZPCI); - s390_cpudef_featoff_greater(12, 1, S390_FEAT_ADAPTER_INT_SUPPRESSION); - s390_cpudef_featoff_greater(12, 1, S390_FEAT_ADAPTER_EVENT_NOTIFICATION); - css_migration_enabled = false; -} - -static void ccw_machine_2_9_class_options(MachineClass *mc) -{ - static GlobalProperty compat[] = { - { TYPE_S390_STATTRIB, "migration-enabled", "off", }, - { TYPE_S390_FLIC_COMMON, "migration-enabled", "off", }, - }; - - ccw_machine_2_10_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_2_9, hw_compat_2_9_len); - compat_props_add(mc->compat_props, compat, G_N_ELEMENTS(compat)); -} -DEFINE_CCW_MACHINE(2, 9); - #endif static void ccw_machine_register_types(void) From 2abe33895868c3846cd5d50a7207ca22cc98f741 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 15 Jan 2025 08:38:13 +0100 Subject: [PATCH 0046/2760] hw/s390x/css: Remove the obsolete "css_migration_enabled" variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the s390-ccw-virtio-2.9 machine type has been removed, we don't need the "css_migration_enabled" variable anymore and can remove the related code. Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250115073819.15452-3-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/css.c | 31 +------------------------------ include/hw/s390x/css.h | 6 ------ 2 files changed, 1 insertion(+), 36 deletions(-) diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 738800c98d..17afcbb2cd 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -23,8 +23,6 @@ #include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/s390-ccw.h" -bool css_migration_enabled = true; - typedef struct CrwContainer { CRW crw; QTAILQ_ENTRY(CrwContainer) sibling; @@ -180,16 +178,10 @@ static const VMStateDescription vmstate_orb = { } }; -static bool vmstate_schdev_orb_needed(void *opaque) -{ - return css_migration_enabled; -} - static const VMStateDescription vmstate_schdev_orb = { .name = "s390_subch_dev/orb", .version_id = 1, .minimum_version_id = 1, - .needed = vmstate_schdev_orb_needed, .fields = (const VMStateField[]) { VMSTATE_STRUCT(orb, SubchDev, 1, vmstate_orb, ORB), VMSTATE_END_OF_LIST() @@ -390,33 +382,12 @@ static int subch_dev_post_load(void *opaque, int version_id) css_subch_assign(s->cssid, s->ssid, s->schid, s->devno, s); } - if (css_migration_enabled) { - /* No compat voodoo to do ;) */ - return 0; - } - /* - * Hack alert. If we don't migrate the channel subsystem status - * we still need to find out if the guest enabled mss/mcss-e. - * If the subchannel is enabled, it certainly was able to access it, - * so adjust the max_ssid/max_cssid values for relevant ssid/cssid - * values. This is not watertight, but better than nothing. - */ - if (s->curr_status.pmcw.flags & PMCW_FLAGS_MASK_ENA) { - if (s->ssid) { - channel_subsys.max_ssid = MAX_SSID; - } - if (s->cssid != channel_subsys.default_cssid) { - channel_subsys.max_cssid = MAX_CSSID; - } - } return 0; } void css_register_vmstate(void) { - if (css_migration_enabled) { - vmstate_register(NULL, 0, &vmstate_css, &channel_subsys); - } + vmstate_register(NULL, 0, &vmstate_css, &channel_subsys); } IndAddr *get_indicator(hwaddr ind_addr, int len) diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index cd97e2b707..dbf919bdd2 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -333,10 +333,4 @@ static inline int ccw_dstream_read_buf(CcwDataStream *cds, void *buff, int len) #define ccw_dstream_read(cds, v) ccw_dstream_read_buf((cds), &(v), sizeof(v)) #define ccw_dstream_write(cds, v) ccw_dstream_write_buf((cds), &(v), sizeof(v)) -/** - * true if (vmstate based) migration of the channel subsystem - * is enabled, false if it is disabled. - */ -extern bool css_migration_enabled; - #endif From d6305b6e613fa23882b9b7b58f6b0aec0eeef015 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 15 Jan 2025 08:38:14 +0100 Subject: [PATCH 0047/2760] hw/s390x/s390-stattrib: Remove the old migration_enabled flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the machine types that set the migration_enabled flag to false are gone, we can remove it and the related code. Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250115073819.15452-4-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/s390x/s390-stattrib-kvm.c | 2 +- hw/s390x/s390-stattrib.c | 7 +------ include/hw/s390x/storage-attributes.h | 1 - 3 files changed, 2 insertions(+), 8 deletions(-) diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c index 2a8e31718b..d0ff04364d 100644 --- a/hw/s390x/s390-stattrib-kvm.c +++ b/hw/s390x/s390-stattrib-kvm.c @@ -185,7 +185,7 @@ static long long kvm_s390_stattrib_get_dirtycount(S390StAttribState *sa) static int kvm_s390_stattrib_get_active(S390StAttribState *sa) { - return kvm_s390_cmma_active() && sa->migration_enabled; + return kvm_s390_cmma_active(); } static void kvm_s390_stattrib_class_init(ObjectClass *oc, void *data) diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index be07c28c6e..35bf697ef0 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -304,7 +304,7 @@ static int qemu_s390_set_migrationmode_stub(S390StAttribState *sa, bool value, static int qemu_s390_get_active(S390StAttribState *sa) { - return sa->migration_enabled; + return true; } static void qemu_s390_stattrib_class_init(ObjectClass *oc, void *data) @@ -360,10 +360,6 @@ static void s390_stattrib_realize(DeviceState *dev, Error **errp) &savevm_s390_stattrib_handlers, dev); } -static const Property s390_stattrib_props[] = { - DEFINE_PROP_BOOL("migration-enabled", S390StAttribState, migration_enabled, true), -}; - static void s390_stattrib_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -371,7 +367,6 @@ static void s390_stattrib_class_init(ObjectClass *oc, void *data) dc->hotpluggable = false; set_bit(DEVICE_CATEGORY_MISC, dc->categories); dc->realize = s390_stattrib_realize; - device_class_set_props(dc, s390_stattrib_props); } static void s390_stattrib_instance_init(Object *obj) diff --git a/include/hw/s390x/storage-attributes.h b/include/hw/s390x/storage-attributes.h index 8921a04d51..b5c6d8fa55 100644 --- a/include/hw/s390x/storage-attributes.h +++ b/include/hw/s390x/storage-attributes.h @@ -25,7 +25,6 @@ OBJECT_DECLARE_TYPE(S390StAttribState, S390StAttribClass, S390_STATTRIB) struct S390StAttribState { DeviceState parent_obj; uint64_t migration_cur_gfn; - bool migration_enabled; }; From 7c3b69feb07acbd70cbf3ce6cbd5ccbefedf5e58 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 15 Jan 2025 08:38:15 +0100 Subject: [PATCH 0048/2760] hw/intc/s390_flic: Remove the obsolete migration_enabled flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the machine types that set the migration_enabled flag to false are gone, we can remove it and the related code. Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250115073819.15452-5-thuth@redhat.com> Signed-off-by: Thomas Huth --- hw/intc/s390_flic.c | 14 -------------- include/hw/s390x/s390_flic.h | 1 - 2 files changed, 15 deletions(-) diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index c20f4c1075..4fae023197 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -470,11 +470,6 @@ static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) fsc->inject_crw_mchk = qemu_s390_inject_crw_mchk; } -static const Property s390_flic_common_properties[] = { - DEFINE_PROP_BOOL("migration-enabled", S390FLICState, - migration_enabled, true), -}; - static void s390_flic_common_realize(DeviceState *dev, Error **errp) { S390FLICState *fs = S390_FLIC_COMMON(dev); @@ -486,7 +481,6 @@ static void s390_flic_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); - device_class_set_props(dc, s390_flic_common_properties); dc->realize = s390_flic_common_realize; } @@ -515,18 +509,10 @@ static void qemu_s390_flic_register_types(void) type_init(qemu_s390_flic_register_types) -static bool adapter_info_so_needed(void *opaque) -{ - S390FLICState *fs = s390_get_flic(); - - return fs->migration_enabled; -} - const VMStateDescription vmstate_adapter_info_so = { .name = "s390_adapter_info/summary_offset", .version_id = 1, .minimum_version_id = 1, - .needed = adapter_info_so_needed, .fields = (const VMStateField[]) { VMSTATE_UINT32(summary_offset, AdapterInfo), VMSTATE_END_OF_LIST() diff --git a/include/hw/s390x/s390_flic.h b/include/hw/s390x/s390_flic.h index 85016d5ccc..91edaaca40 100644 --- a/include/hw/s390x/s390_flic.h +++ b/include/hw/s390x/s390_flic.h @@ -42,7 +42,6 @@ OBJECT_DECLARE_TYPE(S390FLICState, S390FLICStateClass, struct S390FLICState { SysBusDevice parent_obj; bool ais_supported; - bool migration_enabled; }; From 4b91f6d16d4e50cf0f19d74f318d0ba61f90cc64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Mar 2025 16:14:11 +0100 Subject: [PATCH 0049/2760] hw/s390x/skeys: Declare QOM types using DEFINE_TYPES() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When multiple QOM types are registered in the same file, it is simpler to use the the DEFINE_TYPES() macro. In particular because type array declared with such macro are easier to review. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Eric Farman Reviewed-by: Thomas Huth Message-ID: <20250310151414.11550-2-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/s390x/s390-skeys.c | 39 +++++++++++++++++---------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 811d892122..d50e71b927 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -316,14 +316,6 @@ static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data) dc->user_creatable = false; } -static const TypeInfo qemu_s390_skeys_info = { - .name = TYPE_QEMU_S390_SKEYS, - .parent = TYPE_S390_SKEYS, - .instance_size = sizeof(QEMUS390SKeysState), - .class_init = qemu_s390_skeys_class_init, - .class_size = sizeof(S390SKeysClass), -}; - static void s390_storage_keys_save(QEMUFile *f, void *opaque) { S390SKeysState *ss = S390_SKEYS(opaque); @@ -481,19 +473,22 @@ static void s390_skeys_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); } -static const TypeInfo s390_skeys_info = { - .name = TYPE_S390_SKEYS, - .parent = TYPE_DEVICE, - .instance_size = sizeof(S390SKeysState), - .class_init = s390_skeys_class_init, - .class_size = sizeof(S390SKeysClass), - .abstract = true, +static const TypeInfo s390_skeys_types[] = { + { + .name = TYPE_S390_SKEYS, + .parent = TYPE_DEVICE, + .instance_size = sizeof(S390SKeysState), + .class_init = s390_skeys_class_init, + .class_size = sizeof(S390SKeysClass), + .abstract = true, + }, + { + .name = TYPE_QEMU_S390_SKEYS, + .parent = TYPE_S390_SKEYS, + .instance_size = sizeof(QEMUS390SKeysState), + .class_init = qemu_s390_skeys_class_init, + .class_size = sizeof(S390SKeysClass), + }, }; -static void qemu_s390_skeys_register_types(void) -{ - type_register_static(&s390_skeys_info); - type_register_static(&qemu_s390_skeys_info); -} - -type_init(qemu_s390_skeys_register_types) +DEFINE_TYPES(s390_skeys_types) From 3f979fe13e405d5721f3b43b5669a462f4816569 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Mar 2025 16:14:12 +0100 Subject: [PATCH 0050/2760] hw/s390x/skeys: Introduce TYPE_DUMP_SKEYS_INTERFACE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The storage keys are part of the machine memory. Introduce the TYPE_DUMP_SKEYS_INTERFACE type, allowing machine using storage keys to dump them when a DumpSKeysInterface::qmp_dump_skeys() callback is provided. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Eric Farman Reviewed-by: Thomas Huth Message-ID: <20250310151414.11550-3-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/s390x/s390-skeys.c | 5 +++++ include/hw/s390x/storage-keys.h | 15 +++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index d50e71b927..0d3d4f74b4 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -474,6 +474,11 @@ static void s390_skeys_class_init(ObjectClass *oc, void *data) } static const TypeInfo s390_skeys_types[] = { + { + .name = TYPE_DUMP_SKEYS_INTERFACE, + .parent = TYPE_INTERFACE, + .class_size = sizeof(DumpSKeysInterface), + }, { .name = TYPE_S390_SKEYS, .parent = TYPE_DEVICE, diff --git a/include/hw/s390x/storage-keys.h b/include/hw/s390x/storage-keys.h index 408d2815d4..fb766d4631 100644 --- a/include/hw/s390x/storage-keys.h +++ b/include/hw/s390x/storage-keys.h @@ -125,4 +125,19 @@ S390SKeysState *s390_get_skeys_device(void); void hmp_dump_skeys(Monitor *mon, const QDict *qdict); void hmp_info_skeys(Monitor *mon, const QDict *qdict); +#define TYPE_DUMP_SKEYS_INTERFACE "dump-skeys-interface" + +typedef struct DumpSKeysInterface DumpSKeysInterface; +DECLARE_CLASS_CHECKERS(DumpSKeysInterface, DUMP_SKEYS_INTERFACE, + TYPE_DUMP_SKEYS_INTERFACE) + +struct DumpSKeysInterface { + InterfaceClass parent_class; + + /** + * @qmp_dump_skeys: Callback to dump guest's storage keys to @filename. + */ + void (*qmp_dump_skeys)(const char *filename, Error **errp); +}; + #endif /* S390_STORAGE_KEYS_H */ From 1b759bb0cdd6ee5c83bdf8ff3c6897071c5a3a2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Mar 2025 16:14:13 +0100 Subject: [PATCH 0051/2760] hw/s390x/ccw: Have CCW machine implement a qmp_dump_skeys() callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to make @dump-skeys command generic, extract s390_qmp_dump_skeys() out of qmp_dump_skeys(). Register it as CCW qmp_dump_skeys() callback. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Eric Farman Message-ID: <20250310151414.11550-4-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/s390x/s390-skeys.c | 7 ++++++- hw/s390x/s390-virtio-ccw.c | 3 +++ include/hw/s390x/storage-keys.h | 1 + 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 0d3d4f74b4..fd1123b0f3 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -142,7 +142,7 @@ void hmp_dump_skeys(Monitor *mon, const QDict *qdict) } } -void qmp_dump_skeys(const char *filename, Error **errp) +void s390_qmp_dump_skeys(const char *filename, Error **errp) { S390SKeysState *ss = s390_get_skeys_device(); S390SKeysClass *skeyclass = S390_SKEYS_GET_CLASS(ss); @@ -219,6 +219,11 @@ void qmp_dump_skeys(const char *filename, Error **errp) fclose(f); } +void qmp_dump_skeys(const char *filename, Error **errp) +{ + s390_qmp_dump_skeys(filename, errp); +} + static bool qemu_s390_skeys_are_enabled(S390SKeysState *ss) { QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(ss); diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 72de2bd8dc..910dab0831 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -810,6 +810,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) NMIClass *nc = NMI_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); + DumpSKeysInterface *dsi = DUMP_SKEYS_INTERFACE_CLASS(oc); s390mc->hpage_1m_allowed = true; s390mc->max_threads = 1; @@ -835,6 +836,7 @@ static void ccw_machine_class_init(ObjectClass *oc, void *data) nc->nmi_monitor_handler = s390_nmi; mc->default_ram_id = "s390.ram"; mc->default_nic = "virtio-net-ccw"; + dsi->qmp_dump_skeys = s390_qmp_dump_skeys; object_class_property_add_bool(oc, "aes-key-wrap", machine_get_aes_key_wrap, @@ -876,6 +878,7 @@ static const TypeInfo ccw_machine_info = { .interfaces = (InterfaceInfo[]) { { TYPE_NMI }, { TYPE_HOTPLUG_HANDLER}, + { TYPE_DUMP_SKEYS_INTERFACE}, { } }, }; diff --git a/include/hw/s390x/storage-keys.h b/include/hw/s390x/storage-keys.h index fb766d4631..ac303001f5 100644 --- a/include/hw/s390x/storage-keys.h +++ b/include/hw/s390x/storage-keys.h @@ -122,6 +122,7 @@ int s390_skeys_set(S390SKeysState *ks, uint64_t start_gfn, S390SKeysState *s390_get_skeys_device(void); +void s390_qmp_dump_skeys(const char *filename, Error **errp); void hmp_dump_skeys(Monitor *mon, const QDict *qdict); void hmp_info_skeys(Monitor *mon, const QDict *qdict); From 76720b6e85b5a6c61044e2110195068c1106fe4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Mar 2025 16:14:14 +0100 Subject: [PATCH 0052/2760] qapi/machine: Make @dump-skeys command generic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce misc-target.json by one target specific command. Error message is returned for machines not implementing TYPE_DUMP_SKEYS_INTERFACE: $ qemu-system-aarch64 -M virt -S -qmp stdio {"QMP": {"version": {"qemu": {"micro": 50, "major": 9}}, "capabilities": ["oob"]}} { "execute": "qmp_capabilities" } {"return": {}} { "execute": "dump-skeys", "arguments": { "filename": "/tmp/foo" } } {"error": {"class": "GenericError", "desc": "Storage keys information not available for this architecture"}} Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Message-ID: <20250310151414.11550-5-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/core/machine-qmp-cmds.c | 14 ++++++++++++++ hw/s390x/s390-skeys.c | 6 +----- qapi/machine.json | 18 ++++++++++++++++++ qapi/misc-target.json | 19 ------------------- 4 files changed, 33 insertions(+), 24 deletions(-) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 3130c5cd45..fd8b4e0b44 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -25,6 +25,7 @@ #include "system/numa.h" #include "system/runstate.h" #include "system/system.h" +#include "hw/s390x/storage-keys.h" /* * fast means: we NEVER interrupt vCPU threads to retrieve @@ -406,3 +407,16 @@ GuidInfo *qmp_query_vm_generation_id(Error **errp) info->guid = qemu_uuid_unparse_strdup(&vms->guid); return info; } + +void qmp_dump_skeys(const char *filename, Error **errp) +{ + ObjectClass *mc = object_get_class(qdev_get_machine()); + ObjectClass *oc = object_class_dynamic_cast(mc, TYPE_DUMP_SKEYS_INTERFACE); + + if (!oc) { + error_setg(errp, "Storage keys information not available" + " for this architecture"); + return; + } + DUMP_SKEYS_INTERFACE_CLASS(oc)->qmp_dump_skeys(filename, errp); +} diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index fd1123b0f3..067ea03726 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -15,6 +15,7 @@ #include "hw/qdev-properties.h" #include "hw/s390x/storage-keys.h" #include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc-target.h" #include "qobject/qdict.h" #include "qemu/error-report.h" @@ -219,11 +220,6 @@ void s390_qmp_dump_skeys(const char *filename, Error **errp) fclose(f); } -void qmp_dump_skeys(const char *filename, Error **errp) -{ - s390_qmp_dump_skeys(filename, errp); -} - static bool qemu_s390_skeys_are_enabled(S390SKeysState *ss) { QEMUS390SKeysState *skeys = QEMU_S390_SKEYS(ss); diff --git a/qapi/machine.json b/qapi/machine.json index a6b8795b09..a9ff807631 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1898,3 +1898,21 @@ { 'command': 'x-query-interrupt-controllers', 'returns': 'HumanReadableText', 'features': [ 'unstable' ]} + +## +# @dump-skeys: +# +# Dump the storage keys for an s390x guest +# +# @filename: the path to the file to dump to +# +# Since: 2.5 +# +# .. qmp-example:: +# +# -> { "execute": "dump-skeys", +# "arguments": { "filename": "/tmp/skeys" } } +# <- { "return": {} } +## +{ 'command': 'dump-skeys', + 'data': { 'filename': 'str' } } diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 8d70bd24d8..42e4a7417d 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -274,25 +274,6 @@ 'returns': 'SevAttestationReport', 'if': 'TARGET_I386' } -## -# @dump-skeys: -# -# Dump guest's storage keys -# -# @filename: the path to the file to dump to -# -# Since: 2.5 -# -# .. qmp-example:: -# -# -> { "execute": "dump-skeys", -# "arguments": { "filename": "/tmp/skeys" } } -# <- { "return": {} } -## -{ 'command': 'dump-skeys', - 'data': { 'filename': 'str' }, - 'if': 'TARGET_S390X' } - ## # @GICCapability: # From 4a00039c4666fb3f5b24dca8f3212fe049e0f92e Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 14 Apr 2025 11:45:43 +0200 Subject: [PATCH 0053/2760] hw: add compat machines for 10.1 Add 10.1 machine types for arm/i440fx/m68k/q35/s390x/spapr. Signed-off-by: Cornelia Huck Reviewed-by: Zhao Liu Reviewed-by: Thomas Huth Acked-by: Michael S. Tsirkin Message-ID: <20250414094543.221241-1-cohuck@redhat.com> Signed-off-by: Thomas Huth --- hw/arm/virt.c | 9 ++++++++- hw/core/machine.c | 3 +++ hw/i386/pc.c | 3 +++ hw/i386/pc_piix.c | 13 +++++++++++-- hw/i386/pc_q35.c | 13 +++++++++++-- hw/m68k/virt.c | 9 ++++++++- hw/ppc/spapr.c | 15 +++++++++++++-- hw/s390x/s390-virtio-ccw.c | 14 +++++++++++++- include/hw/boards.h | 3 +++ include/hw/i386/pc.h | 3 +++ 10 files changed, 76 insertions(+), 9 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index a96452f17a..3e72adaa91 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -3408,10 +3408,17 @@ static void machvirt_machine_init(void) } type_init(machvirt_machine_init); +static void virt_machine_10_1_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(10, 1) + static void virt_machine_10_0_options(MachineClass *mc) { + virt_machine_10_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_0, hw_compat_10_0_len); } -DEFINE_VIRT_MACHINE_AS_LATEST(10, 0) +DEFINE_VIRT_MACHINE(10, 0) static void virt_machine_9_2_options(MachineClass *mc) { diff --git a/hw/core/machine.c b/hw/core/machine.c index 63c6ef93d2..abfcedd4a5 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -37,6 +37,9 @@ #include "hw/virtio/virtio-iommu.h" #include "audio/audio.h" +GlobalProperty hw_compat_10_0[] = {}; +const size_t hw_compat_10_0_len = G_N_ELEMENTS(hw_compat_10_0); + GlobalProperty hw_compat_9_2[] = { {"arm-cpu", "backcompat-pauth-default-use-qarma5", "true"}, { "virtio-balloon-pci", "vectors", "0" }, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 01d0581f62..1b5d55e96d 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -79,6 +79,9 @@ { "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, +GlobalProperty pc_compat_10_0[] = {}; +const size_t pc_compat_10_0_len = G_N_ELEMENTS(pc_compat_10_0); + GlobalProperty pc_compat_9_2[] = {}; const size_t pc_compat_9_2_len = G_N_ELEMENTS(pc_compat_9_2); diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 6c91e2d292..dbb59df64f 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -479,12 +479,21 @@ static void pc_i440fx_machine_options(MachineClass *m) "Use a different south bridge than PIIX3"); } -static void pc_i440fx_machine_10_0_options(MachineClass *m) +static void pc_i440fx_machine_10_1_options(MachineClass *m) { pc_i440fx_machine_options(m); } -DEFINE_I440FX_MACHINE_AS_LATEST(10, 0); +DEFINE_I440FX_MACHINE_AS_LATEST(10, 1); + +static void pc_i440fx_machine_10_0_options(MachineClass *m) +{ + pc_i440fx_machine_10_1_options(m); + compat_props_add(m->compat_props, hw_compat_10_0, hw_compat_10_0_len); + compat_props_add(m->compat_props, pc_compat_10_0, pc_compat_10_0_len); +} + +DEFINE_I440FX_MACHINE(10, 0); static void pc_i440fx_machine_9_2_options(MachineClass *m) { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index fd96d0345c..c538b3d05b 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -361,12 +361,21 @@ static void pc_q35_machine_options(MachineClass *m) pc_q35_compat_defaults, pc_q35_compat_defaults_len); } -static void pc_q35_machine_10_0_options(MachineClass *m) +static void pc_q35_machine_10_1_options(MachineClass *m) { pc_q35_machine_options(m); } -DEFINE_Q35_MACHINE_AS_LATEST(10, 0); +DEFINE_Q35_MACHINE_AS_LATEST(10, 1); + +static void pc_q35_machine_10_0_options(MachineClass *m) +{ + pc_q35_machine_10_1_options(m); + compat_props_add(m->compat_props, hw_compat_10_0, hw_compat_10_0_len); + compat_props_add(m->compat_props, pc_compat_10_0, pc_compat_10_0_len); +} + +DEFINE_Q35_MACHINE(10, 0); static void pc_q35_machine_9_2_options(MachineClass *m) { diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index d967bdd743..295a614e16 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -366,10 +366,17 @@ type_init(virt_machine_register_types) #define DEFINE_VIRT_MACHINE(major, minor) \ DEFINE_VIRT_MACHINE_IMPL(false, major, minor) +static void virt_machine_10_1_options(MachineClass *mc) +{ +} +DEFINE_VIRT_MACHINE_AS_LATEST(10, 1) + static void virt_machine_10_0_options(MachineClass *mc) { + virt_machine_10_1_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_0, hw_compat_10_0_len); } -DEFINE_VIRT_MACHINE_AS_LATEST(10, 0) +DEFINE_VIRT_MACHINE(10, 0) static void virt_machine_9_2_options(MachineClass *mc) { diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index b0a0f8c689..6fef1d167a 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4766,15 +4766,26 @@ static void spapr_machine_latest_class_options(MachineClass *mc) #define DEFINE_SPAPR_MACHINE(major, minor) \ DEFINE_SPAPR_MACHINE_IMPL(false, major, minor) +/* + * pseries-10.1 + */ +static void spapr_machine_10_1_class_options(MachineClass *mc) +{ + /* Defaults for the latest behaviour inherited from the base class */ +} + +DEFINE_SPAPR_MACHINE_AS_LATEST(10, 1); + /* * pseries-10.0 */ static void spapr_machine_10_0_class_options(MachineClass *mc) { - /* Defaults for the latest behaviour inherited from the base class */ + spapr_machine_10_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_0, hw_compat_10_0_len); } -DEFINE_SPAPR_MACHINE_AS_LATEST(10, 0); +DEFINE_SPAPR_MACHINE(10, 0); /* * pseries-9.2 diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 910dab0831..5af3c4f547 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -924,14 +924,26 @@ static const TypeInfo ccw_machine_info = { DEFINE_CCW_MACHINE_IMPL(false, major, minor) +static void ccw_machine_10_1_instance_options(MachineState *machine) +{ +} + +static void ccw_machine_10_1_class_options(MachineClass *mc) +{ +} +DEFINE_CCW_MACHINE_AS_LATEST(10, 1); + static void ccw_machine_10_0_instance_options(MachineState *machine) { + ccw_machine_10_1_instance_options(machine); } static void ccw_machine_10_0_class_options(MachineClass *mc) { + ccw_machine_10_1_class_options(mc); + compat_props_add(mc->compat_props, hw_compat_10_0, hw_compat_10_0_len); } -DEFINE_CCW_MACHINE_AS_LATEST(10, 0); +DEFINE_CCW_MACHINE(10, 0); static void ccw_machine_9_2_instance_options(MachineState *machine) { diff --git a/include/hw/boards.h b/include/hw/boards.h index f22b2e7fc7..bfe8643a27 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -761,6 +761,9 @@ struct MachineState { } \ type_init(machine_initfn##_register_types) +extern GlobalProperty hw_compat_10_0[]; +extern const size_t hw_compat_10_0_len; + extern GlobalProperty hw_compat_9_2[]; extern const size_t hw_compat_9_2_len; diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 103b54301f..8677dc8950 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -215,6 +215,9 @@ void pc_system_parse_ovmf_flash(uint8_t *flash_ptr, size_t flash_size); /* sgx.c */ void pc_machine_init_sgx_epc(PCMachineState *pcms); +extern GlobalProperty pc_compat_10_0[]; +extern const size_t pc_compat_10_0_len; + extern GlobalProperty pc_compat_9_2[]; extern const size_t pc_compat_9_2_len; From 4e3823c68cc5ce04756a8f11d1ec9c18172f0bd3 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Mon, 14 Apr 2025 11:37:32 +0200 Subject: [PATCH 0054/2760] tests/functional/test_vnc: skip test if no crypto backend available The test_change_password test will fail if no cryptographic backend is available (e.g. if QEMU was built on a system with no cryptographic library development packages installed); just skip the test in that case. Signed-off-by: Cornelia Huck Reviewed-by: Thomas Huth Message-ID: <20250414093732.220498-1-cohuck@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_vnc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py index 8c9953bdb0..d4e9dd0279 100755 --- a/tests/functional/test_vnc.py +++ b/tests/functional/test_vnc.py @@ -55,6 +55,8 @@ def launch_guarded(self): except VMLaunchFailure as excp: if "-vnc: invalid option" in excp.output: self.skipTest("VNC support not available") + elif "Cipher backend does not support DES algorithm" in excp.output: + self.skipTest("No cryptographic backend available") else: self.log.info("unhandled launch failure: %s", excp.output) raise excp From 22baa5f340d4fe3e7f271d275367b806ef3da834 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:15 +0200 Subject: [PATCH 0055/2760] gitlab-ci: Remove the avocado tests from the CI pipelines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are going to move the remaining Avocado tests step by step into the functional test framework. Unfortunately, Avocado fails with an error if it cannot determine a test to run, so disable the tests here now to avoid failures in the Gitlab-CI during the next steps. Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-2-thuth@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/buildtest-template.yml | 11 ---------- .gitlab-ci.d/buildtest.yml | 33 +++++++++++------------------ 2 files changed, 12 insertions(+), 32 deletions(-) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 39da7698b0..13fa4f4a4f 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -95,7 +95,6 @@ cache: key: "${CI_JOB_NAME}-cache" paths: - - ${CI_PROJECT_DIR}/avocado-cache - ${CI_PROJECT_DIR}/functional-cache policy: pull-push artifacts: @@ -109,16 +108,6 @@ reports: junit: build/tests/results/latest/results.xml before_script: - - mkdir -p ~/.config/avocado - - echo "[datadir.paths]" > ~/.config/avocado/avocado.conf - - echo "cache_dirs = ['${CI_PROJECT_DIR}/avocado-cache']" - >> ~/.config/avocado/avocado.conf - - echo -e '[job.output.testlogs]\nstatuses = ["FAIL", "INTERRUPT"]' - >> ~/.config/avocado/avocado.conf - - if [ -d ${CI_PROJECT_DIR}/avocado-cache ]; then - du -chs ${CI_PROJECT_DIR}/*-cache ; - fi - - export AVOCADO_ALLOW_UNTRUSTED_CODE=1 - export QEMU_TEST_ALLOW_UNTRUSTED_CODE=1 - export QEMU_TEST_CACHE_DIR=${CI_PROJECT_DIR}/functional-cache after_script: diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 00f4bfcd9f..431bc07d8f 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -29,8 +29,7 @@ functional-system-alpine: artifacts: true variables: IMAGE: alpine - MAKE_CHECK_ARGS: check-avocado check-functional - AVOCADO_TAGS: arch:avr arch:loongarch64 arch:mips64 arch:mipsel + MAKE_CHECK_ARGS: check-functional build-system-ubuntu: extends: @@ -60,8 +59,7 @@ functional-system-ubuntu: artifacts: true variables: IMAGE: ubuntu2204 - MAKE_CHECK_ARGS: check-avocado check-functional - AVOCADO_TAGS: arch:alpha arch:microblazeel arch:mips64el + MAKE_CHECK_ARGS: check-functional build-system-debian: extends: @@ -92,8 +90,7 @@ functional-system-debian: artifacts: true variables: IMAGE: debian - MAKE_CHECK_ARGS: check-avocado check-functional - AVOCADO_TAGS: arch:arm arch:i386 arch:riscv64 arch:sh4 arch:sparc arch:xtensa + MAKE_CHECK_ARGS: check-functional crash-test-debian: extends: .native_test_job_template @@ -155,9 +152,7 @@ functional-system-fedora: artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-avocado check-functional - AVOCADO_TAGS: arch:microblaze arch:mips arch:xtensa arch:m68k - arch:riscv32 arch:ppc arch:sparc64 + MAKE_CHECK_ARGS: check-functional crash-test-fedora: extends: .native_test_job_template @@ -278,9 +273,7 @@ functional-system-centos: artifacts: true variables: IMAGE: centos9 - MAKE_CHECK_ARGS: check-avocado check-functional - AVOCADO_TAGS: arch:ppc64 arch:or1k arch:s390x arch:x86_64 arch:rx - arch:sh4 + MAKE_CHECK_ARGS: check-functional build-system-opensuse: extends: @@ -309,8 +302,7 @@ functional-system-opensuse: artifacts: true variables: IMAGE: opensuse-leap - MAKE_CHECK_ARGS: check-avocado check-functional - AVOCADO_TAGS: arch:s390x arch:x86_64 arch:aarch64 + MAKE_CHECK_ARGS: check-functional # # Flaky tests. We don't run these by default and they are allow fail @@ -338,10 +330,9 @@ functional-system-flaky: allow_failure: true variables: IMAGE: debian - MAKE_CHECK_ARGS: check-avocado check-functional + MAKE_CHECK_ARGS: check-functional QEMU_JOB_OPTIONAL: 1 QEMU_TEST_FLAKY_TESTS: 1 - AVOCADO_TAGS: flaky # This jobs explicitly disable TCG (--disable-tcg), KVM is detected by # the configure script. The container doesn't contain Xen headers so @@ -482,8 +473,8 @@ clang-user: # Since slirp callbacks are used in QEMU Timers, we cannot use libslirp with # CFI builds, and thus have to disable it here. # -# Split in three sets of build/check/avocado to limit the execution time of each -# job +# Split in three sets of build/check/functional to limit the execution time +# of each job build-cfi-aarch64: extends: - .native_build_job_template @@ -520,7 +511,7 @@ functional-cfi-aarch64: artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-avocado check-functional + MAKE_CHECK_ARGS: check-functional build-cfi-ppc64-s390x: extends: @@ -558,7 +549,7 @@ functional-cfi-ppc64-s390x: artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-avocado check-functional + MAKE_CHECK_ARGS: check-functional build-cfi-x86_64: extends: @@ -592,7 +583,7 @@ functional-cfi-x86_64: artifacts: true variables: IMAGE: fedora - MAKE_CHECK_ARGS: check-avocado check-functional + MAKE_CHECK_ARGS: check-functional tsan-build: extends: .native_build_job_template From bc65ae696104c15e52a5e26f225b689e2c2cdba6 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:16 +0200 Subject: [PATCH 0056/2760] tests/functional: Move the check for the parameters from avocado to functional MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_x86_64_pc in tests/avocado/boot_linux_console.py only checks whether the kernel parameters have correctly been passed to the kernel in the guest by looking for them in the console output of the guest. Let's move that to the functional test framework now, but instead of doing it in a separate test, let's do it for all tuxrun tests instead, so it is done automatically for all targets that have a tuxrun test. Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-3-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/avocado/boot_linux_console.py | 34 ------------------------ tests/functional/qemu_test/tuxruntest.py | 9 ++++--- 2 files changed, 5 insertions(+), 38 deletions(-) diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py index c15f39ae1f..cbb1e2fb50 100644 --- a/tests/avocado/boot_linux_console.py +++ b/tests/avocado/boot_linux_console.py @@ -9,17 +9,9 @@ # later. See the COPYING file in the top-level directory. import os -import lzma -import gzip import shutil -from avocado import skip -from avocado import skipUnless -from avocado import skipUnless from avocado_qemu import QemuSystemTest -from avocado_qemu import exec_command -from avocado_qemu import exec_command_and_wait_for_pattern -from avocado_qemu import interrupt_interactive_console_until_pattern from avocado_qemu import wait_for_console_pattern from avocado.utils import process from avocado.utils import archive @@ -68,29 +60,3 @@ def extract_from_rpm(self, rpm, path): process.run("rpm2cpio %s | cpio -id %s" % (rpm, path), shell=True) os.chdir(cwd) return os.path.normpath(os.path.join(self.workdir, path)) - -class BootLinuxConsole(LinuxKernelTest): - """ - Boots a Linux kernel and checks that the console is operational and the - kernel command line is properly passed from QEMU to the kernel - """ - timeout = 90 - - def test_x86_64_pc(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/x86_64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '23bebd2680757891cf7adedb033532163a792495' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.vm.set_console() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) - self.vm.launch() - console_pattern = 'Kernel command line: %s' % kernel_command_line - self.wait_for_console_pattern(console_pattern) diff --git a/tests/functional/qemu_test/tuxruntest.py b/tests/functional/qemu_test/tuxruntest.py index ad74156f9c..c2bd5baaae 100644 --- a/tests/functional/qemu_test/tuxruntest.py +++ b/tests/functional/qemu_test/tuxruntest.py @@ -77,12 +77,12 @@ def prepare_run(self, kernel, disk, drive, dtb=None, console_index=0): blockdev = "driver=raw,file.driver=file," \ + f"file.filename={disk},node-name=hd0" - kcmd_line = self.KERNEL_COMMON_COMMAND_LINE - kcmd_line += f" root=/dev/{self.root}" - kcmd_line += f" console={self.console}" + self.kcmd_line = self.KERNEL_COMMON_COMMAND_LINE + self.kcmd_line += f" root=/dev/{self.root}" + self.kcmd_line += f" console={self.console}" self.vm.add_args('-kernel', kernel, - '-append', kcmd_line, + '-append', self.kcmd_line, '-blockdev', blockdev) # Sometimes we need extra devices attached @@ -103,6 +103,7 @@ def run_tuxtest_tests(self, haltmsg): wait to exit cleanly. """ ps1='root@tuxtest:~#' + self.wait_for_console_pattern(self.kcmd_line) self.wait_for_console_pattern('tuxtest login:') exec_command_and_wait_for_pattern(self, 'root', ps1) exec_command_and_wait_for_pattern(self, 'cat /proc/interrupts', ps1) From 951ededf12a89534195cf5c5210242a169a85656 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:17 +0200 Subject: [PATCH 0057/2760] tests/functional: Convert reverse_debugging tests to the functional framework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These tests are using the gdb-related library functions from the Avocado framework which we don't have in the functional framework yet. So for the time being, keep those imports and skip the test if the Avocado framework is not installed on the host. Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-4-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 2 +- tests/functional/meson.build | 4 + .../reverse_debugging.py | 114 +++--------------- .../functional/test_aarch64_reverse_debug.py | 38 ++++++ tests/functional/test_ppc64_reverse_debug.py | 41 +++++++ tests/functional/test_x86_64_reverse_debug.py | 36 ++++++ 6 files changed, 139 insertions(+), 96 deletions(-) rename tests/{avocado => functional}/reverse_debugging.py (66%) create mode 100755 tests/functional/test_aarch64_reverse_debug.py create mode 100755 tests/functional/test_ppc64_reverse_debug.py create mode 100755 tests/functional/test_x86_64_reverse_debug.py diff --git a/MAINTAINERS b/MAINTAINERS index d54b5578f8..af1d847f8e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3671,7 +3671,7 @@ F: docs/system/replay.rst F: stubs/replay.c F: tests/avocado/replay_kernel.py F: tests/avocado/replay_linux.py -F: tests/avocado/reverse_debugging.py +F: tests/functional/*reverse_debug*.py F: tests/functional/*replay*.py F: qapi/replay.json diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 0f8be30fe2..7b0f4ab0b1 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -13,6 +13,7 @@ endif test_timeouts = { 'aarch64_aspeed' : 600, 'aarch64_raspi4' : 480, + 'aarch64_reverse_debug' : 180, 'aarch64_rme_virt' : 1200, 'aarch64_rme_sbsaref' : 1200, 'aarch64_sbsaref_alpine' : 1200, @@ -78,6 +79,7 @@ tests_aarch64_system_thorough = [ 'aarch64_raspi3', 'aarch64_raspi4', 'aarch64_replay', + 'aarch64_reverse_debug', 'aarch64_rme_virt', 'aarch64_rme_sbsaref', 'aarch64_sbsaref', @@ -229,6 +231,7 @@ tests_ppc64_system_thorough = [ 'ppc64_powernv', 'ppc64_pseries', 'ppc64_replay', + 'ppc64_reverse_debug', 'ppc64_tuxrun', 'ppc64_mac99', ] @@ -311,6 +314,7 @@ tests_x86_64_system_thorough = [ 'x86_64_hotplug_cpu', 'x86_64_kvm_xen', 'x86_64_replay', + 'x86_64_reverse_debug', 'x86_64_tuxrun', ] diff --git a/tests/avocado/reverse_debugging.py b/tests/functional/reverse_debugging.py similarity index 66% rename from tests/avocado/reverse_debugging.py rename to tests/functional/reverse_debugging.py index f24287cd0a..f9a1d395f1 100644 --- a/tests/avocado/reverse_debugging.py +++ b/tests/functional/reverse_debugging.py @@ -1,5 +1,7 @@ # Reverse debugging test # +# SPDX-License-Identifier: GPL-2.0-or-later +# # Copyright (c) 2020 ISP RAS # # Author: @@ -10,14 +12,9 @@ import os import logging -from avocado import skipUnless -from avocado_qemu import BUILD_DIR -from avocado.utils import datadrainer -from avocado.utils import gdb -from avocado.utils import process -from avocado.utils.network.ports import find_free_port -from avocado.utils.path import find_command -from boot_linux_console import LinuxKernelTest +from qemu_test import LinuxKernelTest, get_qemu_img +from qemu_test.ports import Ports + class ReverseDebugging(LinuxKernelTest): """ @@ -36,8 +33,10 @@ class ReverseDebugging(LinuxKernelTest): endian_is_le = True def run_vm(self, record, shift, args, replay_path, image_path, port): + from avocado.utils import datadrainer + logger = logging.getLogger('replay') - vm = self.get_vm() + vm = self.get_vm(name='record' if record else 'replay') vm.set_console() if record: logger.info('recording the execution...') @@ -100,25 +99,25 @@ def vm_get_icount(vm): return vm.qmp('query-replay')['return']['icount'] def reverse_debugging(self, shift=7, args=None): + from avocado.utils import gdb + from avocado.utils import process + logger = logging.getLogger('replay') # create qcow2 for snapshots logger.info('creating qcow2 image for VM snapshots') image_path = os.path.join(self.workdir, 'disk.qcow2') - qemu_img = os.path.join(BUILD_DIR, 'qemu-img') - if not os.path.exists(qemu_img): - qemu_img = find_command('qemu-img', False) - if qemu_img is False: - self.cancel('Could not find "qemu-img", which is required to ' - 'create the temporary qcow2 image') + qemu_img = get_qemu_img(self) + if qemu_img is None: + self.skipTest('Could not find "qemu-img", which is required to ' + 'create the temporary qcow2 image') cmd = '%s create -f qcow2 %s 128M' % (qemu_img, image_path) process.run(cmd) replay_path = os.path.join(self.workdir, 'replay.bin') - port = find_free_port() # record the log - vm = self.run_vm(True, shift, args, replay_path, image_path, port) + vm = self.run_vm(True, shift, args, replay_path, image_path, -1) while self.vm_get_icount(vm) <= self.STEPS: pass last_icount = self.vm_get_icount(vm) @@ -127,7 +126,9 @@ def reverse_debugging(self, shift=7, args=None): logger.info("recorded log with %s+ steps" % last_icount) # replay and run debug commands - vm = self.run_vm(False, shift, args, replay_path, image_path, port) + with Ports() as ports: + port = ports.find_free_port() + vm = self.run_vm(False, shift, args, replay_path, image_path, port) logger.info('connecting to gdbstub') g = gdb.GDBRemote('127.0.0.1', port, False, False) g.connect() @@ -193,80 +194,3 @@ def reverse_debugging(self, shift=7, args=None): logger.info('exiting gdb and qemu') vm.shutdown() - -class ReverseDebugging_X86_64(ReverseDebugging): - """ - :avocado: tags=accel:tcg - """ - - REG_PC = 0x10 - REG_CS = 0x12 - def get_pc(self, g): - return self.get_reg_le(g, self.REG_PC) \ - + self.get_reg_le(g, self.REG_CS) * 0x10 - - # unidentified gitlab timeout problem - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_x86_64_pc(self): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=machine:pc - """ - # start with BIOS only - self.reverse_debugging() - -class ReverseDebugging_AArch64(ReverseDebugging): - """ - :avocado: tags=accel:tcg - """ - - REG_PC = 32 - - # unidentified gitlab timeout problem - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_aarch64_virt(self): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:cortex-a53 - """ - kernel_url = ('https://archives.fedoraproject.org/pub/archive/fedora' - '/linux/releases/29/Everything/aarch64/os/images/pxeboot' - '/vmlinuz') - kernel_hash = '8c73e469fc6ea06a58dc83a628fc695b693b8493' - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - - self.reverse_debugging( - args=('-kernel', kernel_path)) - -class ReverseDebugging_ppc64(ReverseDebugging): - """ - :avocado: tags=accel:tcg - """ - - REG_PC = 0x40 - - # unidentified gitlab timeout problem - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_ppc64_pseries(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:pseries - :avocado: tags=flaky - """ - # SLOF branches back to its entry point, which causes this test - # to take the 'hit a breakpoint again' path. That's not a problem, - # just slightly different than the other machines. - self.endian_is_le = False - self.reverse_debugging() - - # See https://gitlab.com/qemu-project/qemu/-/issues/1992 - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test_ppc64_powernv(self): - """ - :avocado: tags=arch:ppc64 - :avocado: tags=machine:powernv - :avocado: tags=flaky - """ - self.endian_is_le = False - self.reverse_debugging() diff --git a/tests/functional/test_aarch64_reverse_debug.py b/tests/functional/test_aarch64_reverse_debug.py new file mode 100755 index 0000000000..58d4532835 --- /dev/null +++ b/tests/functional/test_aarch64_reverse_debug.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Reverse debugging test +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from qemu_test import Asset, skipIfMissingImports, skipFlakyTest +from reverse_debugging import ReverseDebugging + + +@skipIfMissingImports('avocado.utils') +class ReverseDebugging_AArch64(ReverseDebugging): + + REG_PC = 32 + + KERNEL_ASSET = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), + '7e1430b81c26bdd0da025eeb8fbd77b5dc961da4364af26e771bd39f379cbbf7') + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/2921") + def test_aarch64_virt(self): + self.set_machine('virt') + self.cpu = 'cortex-a53' + kernel_path = self.KERNEL_ASSET.fetch() + self.reverse_debugging(args=('-kernel', kernel_path)) + + +if __name__ == '__main__': + ReverseDebugging.main() diff --git a/tests/functional/test_ppc64_reverse_debug.py b/tests/functional/test_ppc64_reverse_debug.py new file mode 100755 index 0000000000..5931adef5a --- /dev/null +++ b/tests/functional/test_ppc64_reverse_debug.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Reverse debugging test +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from qemu_test import skipIfMissingImports, skipFlakyTest +from reverse_debugging import ReverseDebugging + + +@skipIfMissingImports('avocado.utils') +class ReverseDebugging_ppc64(ReverseDebugging): + + REG_PC = 0x40 + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/1992") + def test_ppc64_pseries(self): + self.set_machine('pseries') + # SLOF branches back to its entry point, which causes this test + # to take the 'hit a breakpoint again' path. That's not a problem, + # just slightly different than the other machines. + self.endian_is_le = False + self.reverse_debugging() + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/1992") + def test_ppc64_powernv(self): + self.set_machine('powernv') + self.endian_is_le = False + self.reverse_debugging() + + +if __name__ == '__main__': + ReverseDebugging.main() diff --git a/tests/functional/test_x86_64_reverse_debug.py b/tests/functional/test_x86_64_reverse_debug.py new file mode 100755 index 0000000000..d713e91e14 --- /dev/null +++ b/tests/functional/test_x86_64_reverse_debug.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Reverse debugging test +# +# Copyright (c) 2020 ISP RAS +# +# Author: +# Pavel Dovgalyuk +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +from qemu_test import skipIfMissingImports, skipFlakyTest +from reverse_debugging import ReverseDebugging + + +@skipIfMissingImports('avocado.utils') +class ReverseDebugging_X86_64(ReverseDebugging): + + REG_PC = 0x10 + REG_CS = 0x12 + def get_pc(self, g): + return self.get_reg_le(g, self.REG_PC) \ + + self.get_reg_le(g, self.REG_CS) * 0x10 + + @skipFlakyTest("https://gitlab.com/qemu-project/qemu/-/issues/2922") + def test_x86_64_pc(self): + self.set_machine('pc') + # start with BIOS only + self.reverse_debugging() + + +if __name__ == '__main__': + ReverseDebugging.main() From 0e756f404d73ddf7b5506e9109e2c074f30fb79e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:18 +0200 Subject: [PATCH 0058/2760] tests/functional: Convert the i386 replay avocado test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since this was the last test in tests/avocado/replay_kernel.py, we can remove that Avocado file now. Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-5-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 1 - tests/avocado/replay_kernel.py | 110 --------------------------- tests/functional/meson.build | 1 + tests/functional/test_i386_replay.py | 28 +++++++ 4 files changed, 29 insertions(+), 111 deletions(-) delete mode 100644 tests/avocado/replay_kernel.py create mode 100755 tests/functional/test_i386_replay.py diff --git a/MAINTAINERS b/MAINTAINERS index af1d847f8e..42348df9d6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3669,7 +3669,6 @@ F: include/system/replay.h F: docs/devel/replay.rst F: docs/system/replay.rst F: stubs/replay.c -F: tests/avocado/replay_kernel.py F: tests/avocado/replay_linux.py F: tests/functional/*reverse_debug*.py F: tests/functional/*replay*.py diff --git a/tests/avocado/replay_kernel.py b/tests/avocado/replay_kernel.py deleted file mode 100644 index 3551532372..0000000000 --- a/tests/avocado/replay_kernel.py +++ /dev/null @@ -1,110 +0,0 @@ -# Record/replay test that boots a Linux kernel -# -# Copyright (c) 2020 ISP RAS -# -# Author: -# Pavel Dovgalyuk -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os -import lzma -import shutil -import logging -import time -import subprocess - -from avocado import skip -from avocado import skipUnless -from avocado import skipUnless -from avocado_qemu import wait_for_console_pattern -from avocado.utils import archive -from avocado.utils import process -from boot_linux_console import LinuxKernelTest - -class ReplayKernelBase(LinuxKernelTest): - """ - Boots a Linux kernel in record mode and checks that the console - is operational and the kernel command line is properly passed - from QEMU to the kernel. - Then replays the same scenario and verifies, that QEMU correctly - terminates. - """ - - timeout = 180 - KERNEL_COMMON_COMMAND_LINE = 'printk.time=1 panic=-1 ' - - def run_vm(self, kernel_path, kernel_command_line, console_pattern, - record, shift, args, replay_path): - # icount requires TCG to be available - self.require_accelerator('tcg') - - logger = logging.getLogger('replay') - start_time = time.time() - vm = self.get_vm() - vm.set_console() - if record: - logger.info('recording the execution...') - mode = 'record' - else: - logger.info('replaying the execution...') - mode = 'replay' - vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' % - (shift, mode, replay_path), - '-kernel', kernel_path, - '-append', kernel_command_line, - '-net', 'none', - '-no-reboot') - if args: - vm.add_args(*args) - vm.launch() - self.wait_for_console_pattern(console_pattern, vm) - if record: - vm.shutdown() - logger.info('finished the recording with log size %s bytes' - % os.path.getsize(replay_path)) - self.run_replay_dump(replay_path) - logger.info('successfully tested replay-dump.py') - else: - vm.wait() - logger.info('successfully finished the replay') - elapsed = time.time() - start_time - logger.info('elapsed time %.2f sec' % elapsed) - return elapsed - - def run_replay_dump(self, replay_path): - try: - subprocess.check_call(["./scripts/replay-dump.py", - "-f", replay_path], - stdout=subprocess.DEVNULL) - except subprocess.CalledProcessError: - self.fail('replay-dump.py failed') - - def run_rr(self, kernel_path, kernel_command_line, console_pattern, - shift=7, args=None): - replay_path = os.path.join(self.workdir, 'replay.bin') - t1 = self.run_vm(kernel_path, kernel_command_line, console_pattern, - True, shift, args, replay_path) - t2 = self.run_vm(kernel_path, kernel_command_line, console_pattern, - False, shift, args, replay_path) - logger = logging.getLogger('replay') - logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) - -class ReplayKernelNormal(ReplayKernelBase): - - def test_i386_pc(self): - """ - :avocado: tags=arch:i386 - :avocado: tags=machine:pc - """ - kernel_url = ('https://storage.tuxboot.com/20230331/i386/bzImage') - kernel_hash = 'a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956' - kernel_path = self.fetch_asset(kernel_url, - asset_hash=kernel_hash, - algorithm = "sha256") - - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'VFS: Cannot open root device' - - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 7b0f4ab0b1..4113b221de 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -151,6 +151,7 @@ tests_i386_system_quick = [ ] tests_i386_system_thorough = [ + 'i386_replay', 'i386_tuxrun', ] diff --git a/tests/functional/test_i386_replay.py b/tests/functional/test_i386_replay.py new file mode 100755 index 0000000000..7c4c2602da --- /dev/null +++ b/tests/functional/test_i386_replay.py @@ -0,0 +1,28 @@ +#!/usr/bin/env python3 +# +# Replay test that boots a Linux kernel on a i386 machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from replay_kernel import ReplayKernelBase + + +class I386Replay(ReplayKernelBase): + + ASSET_KERNEL = Asset( + 'https://storage.tuxboot.com/20230331/i386/bzImage', + 'a3e5b32a354729e65910f5a1ffcda7c14a6c12a55e8213fb86e277f1b76ed956') + + def test_pc(self): + self.set_machine('pc') + kernel_url = () + kernel_path = self.ASSET_KERNEL.fetch() + kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' + console_pattern = 'VFS: Cannot open root device' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + +if __name__ == '__main__': + ReplayKernelBase.main() From 574f71bc1f22640d45a90969805eaaacd38bf859 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:19 +0200 Subject: [PATCH 0059/2760] tests/avocado: Remove the LinuxKernelTest class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All tests that used this class have been converted to the functional framework, so we can remove the boot_linux_console.py file now. Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-6-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/avocado/boot_linux_console.py | 62 ----------------------------- 1 file changed, 62 deletions(-) delete mode 100644 tests/avocado/boot_linux_console.py diff --git a/tests/avocado/boot_linux_console.py b/tests/avocado/boot_linux_console.py deleted file mode 100644 index cbb1e2fb50..0000000000 --- a/tests/avocado/boot_linux_console.py +++ /dev/null @@ -1,62 +0,0 @@ -# Functional test that boots a Linux kernel and checks the console -# -# Copyright (c) 2018 Red Hat, Inc. -# -# Author: -# Cleber Rosa -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os -import shutil - -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado.utils import process -from avocado.utils import archive - -class LinuxKernelTest(QemuSystemTest): - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - - def wait_for_console_pattern(self, success_message, vm=None): - wait_for_console_pattern(self, success_message, - failure_message='Kernel panic - not syncing', - vm=vm) - - def extract_from_deb(self, deb, path): - """ - Extracts a file from a deb package into the test workdir - - :param deb: path to the deb archive - :param path: path within the deb archive of the file to be extracted - :returns: path of the extracted file - """ - cwd = os.getcwd() - os.chdir(self.workdir) - file_path = process.run("ar t %s" % deb).stdout_text.split()[2] - process.run("ar x %s %s" % (deb, file_path)) - archive.extract(file_path, self.workdir) - os.chdir(cwd) - # Return complete path to extracted file. Because callers to - # extract_from_deb() specify 'path' with a leading slash, it is - # necessary to use os.path.relpath() as otherwise os.path.join() - # interprets it as an absolute path and drops the self.workdir part. - return os.path.normpath(os.path.join(self.workdir, - os.path.relpath(path, '/'))) - - def extract_from_rpm(self, rpm, path): - """ - Extracts a file from an RPM package into the test workdir. - - :param rpm: path to the rpm archive - :param path: path within the rpm archive of the file to be extracted - needs to be a relative path (starting with './') because - cpio(1), which is used to extract the file, expects that. - :returns: path of the extracted file - """ - cwd = os.getcwd() - os.chdir(self.workdir) - process.run("rpm2cpio %s | cpio -id %s" % (rpm, path), shell=True) - os.chdir(cwd) - return os.path.normpath(os.path.join(self.workdir, path)) From 42a87f0ce7aaf1923a610cabe4d65f7b1ce9a327 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:20 +0200 Subject: [PATCH 0060/2760] tests/functional: Convert the 32-bit big endian Wheezy mips test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test checks some entries in /proc and the output of some commands ... we put these checks into exportable functions now so that they can be reused more easily. Additionally the linux_ssh_mips_malta.py uses SSH to test the networking of the guest. Since we don't have a SSH module in the functional framework yet, let's use the check_http_download() function here instead. And while we're at it, also switch the NIC to e1000 now to get some more test coverage, since the "pcnet" device is already tested in the test test_mips_malta_cpio. Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-7-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/avocado/linux_ssh_mips_malta.py | 8 -- tests/functional/meson.build | 2 +- tests/functional/test_mips_malta.py | 108 +++++++++++++++++++++++++- 3 files changed, 107 insertions(+), 11 deletions(-) diff --git a/tests/avocado/linux_ssh_mips_malta.py b/tests/avocado/linux_ssh_mips_malta.py index d9bb525ad9..73d294ad5c 100644 --- a/tests/avocado/linux_ssh_mips_malta.py +++ b/tests/avocado/linux_ssh_mips_malta.py @@ -172,14 +172,6 @@ def check_mips_malta(self, uname_m, endianess): # Wait for VM to shut down gracefully self.vm.wait() - def test_mips_malta32eb_kernel3_2_0(self): - """ - :avocado: tags=arch:mips - :avocado: tags=endian:big - :avocado: tags=device:pcnet32 - """ - self.check_mips_malta('mips', 'be') - def test_mips_malta32el_kernel3_2_0(self): """ :avocado: tags=arch:mipsel diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 4113b221de..4dedfc7b9f 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -39,7 +39,7 @@ test_timeouts = { 'arm_tuxrun' : 240, 'arm_sx1' : 360, 'intel_iommu': 300, - 'mips_malta' : 120, + 'mips_malta' : 480, 'mipsel_replay' : 480, 'mips64el_replay' : 180, 'netdev_ethtool' : 180, diff --git a/tests/functional/test_mips_malta.py b/tests/functional/test_mips_malta.py index 9697c7d63f..89b9556f30 100755 --- a/tests/functional/test_mips_malta.py +++ b/tests/functional/test_mips_malta.py @@ -6,10 +6,93 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -from qemu_test import LinuxKernelTest, Asset +import os + +from qemu_test import LinuxKernelTest, Asset, wait_for_console_pattern from qemu_test import exec_command_and_wait_for_pattern +def mips_run_common_commands(test, prompt='#'): + exec_command_and_wait_for_pattern(test, + 'uname -m', + 'mips') + exec_command_and_wait_for_pattern(test, + 'grep XT-PIC /proc/interrupts', + 'timer') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'grep XT-PIC /proc/interrupts', + 'serial') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'grep XT-PIC /proc/interrupts', + 'ata_piix') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'grep XT-PIC /proc/interrupts', + 'rtc') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'cat /proc/devices', + 'input') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'cat /proc/devices', + 'fb') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'cat /proc/ioports', + ' : serial') + wait_for_console_pattern(test, prompt) + exec_command_and_wait_for_pattern(test, + 'cat /proc/ioports', + ' : ata_piix') + wait_for_console_pattern(test, prompt) + +def mips_check_wheezy(test, kernel_path, image_path, kernel_command_line, + dl_file, hsum, nic='pcnet', cpuinfo='MIPS 24Kc'): + test.require_netdev('user') + test.require_device(nic) + test.set_machine('malta') + + port=8080 + test.vm.add_args('-kernel', kernel_path, + '-append', kernel_command_line, + '-drive', 'file=%s,snapshot=on' % image_path, + '-netdev', 'user,id=n1' + + ',tftp=' + os.path.basename(kernel_path) + + ',hostfwd=tcp:127.0.0.1:0-:%d' % port, + '-device', f'{nic},netdev=n1', + '-no-reboot') + test.vm.set_console() + test.vm.launch() + + wait_for_console_pattern(test, 'login: ', 'Oops') + exec_command_and_wait_for_pattern(test, 'root', 'Password:') + exec_command_and_wait_for_pattern(test, 'root', ':~# ') + mips_run_common_commands(test) + + exec_command_and_wait_for_pattern(test, 'cd /', '# ') + test.check_http_download(dl_file, hsum, port, + pythoncmd='python -m SimpleHTTPServer') + + exec_command_and_wait_for_pattern(test, 'cat /proc/cpuinfo', cpuinfo) + exec_command_and_wait_for_pattern(test, 'cat /proc/devices', 'usb') + exec_command_and_wait_for_pattern(test, 'cat /proc/ioports', + ' : piix4_smbus') + # lspci for the host bridge does not work on big endian targets: + # https://gitlab.com/qemu-project/qemu/-/issues/2826 + # exec_command_and_wait_for_pattern(test, 'lspci -d 11ab:4620', + # 'GT-64120') + exec_command_and_wait_for_pattern(test, + 'cat /sys/bus/i2c/devices/i2c-0/name', + 'SMBus PIIX4 adapter') + exec_command_and_wait_for_pattern(test, 'cat /proc/mtd', 'YAMON') + # Empty 'Board Config' (64KB) + exec_command_and_wait_for_pattern(test, 'md5sum /dev/mtd2ro', + '0dfbe8aa4c20b52e1b8bf3cb6cbdf193') + + class MaltaMachineConsole(LinuxKernelTest): ASSET_KERNEL_2_63_2 = Asset( @@ -70,7 +153,8 @@ def test_mips_malta_cpio(self): exec_command_and_wait_for_pattern(self, 'cat /proc/cpuinfo', 'BogoMIPS') exec_command_and_wait_for_pattern(self, 'uname -a', - 'Debian') + '4.5.0-2-4kc-malta #1 Debian') + mips_run_common_commands(self) exec_command_and_wait_for_pattern(self, 'ip link set eth0 up', 'eth0: link up') @@ -89,6 +173,26 @@ def test_mips_malta_cpio(self): # Wait for VM to shut down gracefully self.vm.wait() + ASSET_WHEEZY_KERNEL = Asset( + ('https://people.debian.org/~aurel32/qemu/mips/' + 'vmlinux-3.2.0-4-4kc-malta'), + '0377fcda31299213c10b8e5babe7260ef99188b3ae1aca6f56594abb71e7f67e') + + ASSET_WHEEZY_DISK = Asset( + ('https://people.debian.org/~aurel32/qemu/mips/' + 'debian_wheezy_mips_standard.qcow2'), + 'de03599285b8382ad309309a6c4869f6c6c42a5cfc983342bab9ec0dfa7849a2') + + def test_wheezy(self): + kernel_path = self.ASSET_WHEEZY_KERNEL.fetch() + image_path = self.ASSET_WHEEZY_DISK.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 root=/dev/sda1') + mips_check_wheezy(self, + kernel_path, image_path, kernel_command_line, nic='e1000', + dl_file='/boot/initrd.img-3.2.0-4-4kc-malta', + hsum='ff0c0369143d9bbb9a6e6bc79322a2be535619df639e84103237f406e87493dc') + if __name__ == '__main__': LinuxKernelTest.main() From 689a8b56a6f3d16458004006b604950295da87df Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:21 +0200 Subject: [PATCH 0061/2760] tests/functional: Convert the 32-bit little endian Wheezy mips test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reuse the test function from the big endian test to easily convert the 32-bit little endian Wheezy mips test. Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-8-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/avocado/linux_ssh_mips_malta.py | 8 -------- tests/functional/meson.build | 1 + tests/functional/test_mipsel_malta.py | 22 ++++++++++++++++++++++ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/tests/avocado/linux_ssh_mips_malta.py b/tests/avocado/linux_ssh_mips_malta.py index 73d294ad5c..c1300aec96 100644 --- a/tests/avocado/linux_ssh_mips_malta.py +++ b/tests/avocado/linux_ssh_mips_malta.py @@ -172,14 +172,6 @@ def check_mips_malta(self, uname_m, endianess): # Wait for VM to shut down gracefully self.vm.wait() - def test_mips_malta32el_kernel3_2_0(self): - """ - :avocado: tags=arch:mipsel - :avocado: tags=endian:little - :avocado: tags=device:pcnet32 - """ - self.check_mips_malta('mips', 'le') - def test_mips_malta64eb_kernel3_2_0(self): """ :avocado: tags=arch:mips64 diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 4dedfc7b9f..cba0abf292 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -40,6 +40,7 @@ test_timeouts = { 'arm_sx1' : 360, 'intel_iommu': 300, 'mips_malta' : 480, + 'mipsel_malta' : 420, 'mipsel_replay' : 480, 'mips64el_replay' : 180, 'netdev_ethtool' : 180, diff --git a/tests/functional/test_mipsel_malta.py b/tests/functional/test_mipsel_malta.py index fe9c3a172e..9ee2884da8 100755 --- a/tests/functional/test_mipsel_malta.py +++ b/tests/functional/test_mipsel_malta.py @@ -13,6 +13,8 @@ from qemu_test import interrupt_interactive_console_until_pattern from qemu_test import wait_for_console_pattern +from test_mips_malta import mips_check_wheezy + class MaltaMachineConsole(LinuxKernelTest): @@ -57,6 +59,26 @@ def test_mips_malta32el_nanomips_16k_up(self): def test_mips_malta32el_nanomips_64k_dbg(self): self.do_test_mips_malta32el_nanomips(self.ASSET_KERNEL_64K) + ASSET_WHEEZY_KERNEL = Asset( + ('https://people.debian.org/~aurel32/qemu/mipsel/' + 'vmlinux-3.2.0-4-4kc-malta'), + 'dc8a3648305b0201ca7a5cd135fe2890067a65d93c38728022bb0e656ad2bf9a') + + ASSET_WHEEZY_DISK = Asset( + ('https://people.debian.org/~aurel32/qemu/mipsel/' + 'debian_wheezy_mipsel_standard.qcow2'), + '454f09ae39f7e6461c84727b927100d2c7813841f2a0a5dce328114887ecf914') + + def test_wheezy(self): + kernel_path = self.ASSET_WHEEZY_KERNEL.fetch() + image_path = self.ASSET_WHEEZY_DISK.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 root=/dev/sda1') + mips_check_wheezy(self, + kernel_path, image_path, kernel_command_line, + dl_file='/boot/initrd.img-3.2.0-4-4kc-malta', + hsum='9fc9f250ed56a74e35e704ddfd5a1c5a5625adefc5c9da91f649288d3ca000f0') + class MaltaMachineYAMON(QemuSystemTest): From 8e3461c3a6fc0b1bce4571e46e861db0b2bcf621 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:22 +0200 Subject: [PATCH 0062/2760] tests/functional: Convert the 64-bit little endian Wheezy mips test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reuse the test function from the 32-bit big endian test to easily convert the 64-bit little endian Wheezy mips test. Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-9-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/avocado/linux_ssh_mips_malta.py | 8 -------- tests/functional/meson.build | 1 + tests/functional/test_mips64el_malta.py | 22 ++++++++++++++++++++++ 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/tests/avocado/linux_ssh_mips_malta.py b/tests/avocado/linux_ssh_mips_malta.py index c1300aec96..2fa5cf9a6c 100644 --- a/tests/avocado/linux_ssh_mips_malta.py +++ b/tests/avocado/linux_ssh_mips_malta.py @@ -179,11 +179,3 @@ def test_mips_malta64eb_kernel3_2_0(self): :avocado: tags=device:pcnet32 """ self.check_mips_malta('mips64', 'be') - - def test_mips_malta64el_kernel3_2_0(self): - """ - :avocado: tags=arch:mips64el - :avocado: tags=endian:little - :avocado: tags=device:pcnet32 - """ - self.check_mips_malta('mips64', 'le') diff --git a/tests/functional/meson.build b/tests/functional/meson.build index cba0abf292..a4a317115b 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -42,6 +42,7 @@ test_timeouts = { 'mips_malta' : 480, 'mipsel_malta' : 420, 'mipsel_replay' : 480, + 'mips64el_malta' : 420, 'mips64el_replay' : 180, 'netdev_ethtool' : 180, 'ppc_40p' : 240, diff --git a/tests/functional/test_mips64el_malta.py b/tests/functional/test_mips64el_malta.py index a8da15a26b..dd37212f9d 100755 --- a/tests/functional/test_mips64el_malta.py +++ b/tests/functional/test_mips64el_malta.py @@ -16,6 +16,8 @@ from qemu_test import exec_command_and_wait_for_pattern from qemu_test import skipIfMissingImports, skipFlakyTest, skipUntrustedTest +from test_mips_malta import mips_check_wheezy + class MaltaMachineConsole(LinuxKernelTest): @@ -90,6 +92,26 @@ def test_mips64el_malta_5KEc_cpio(self): # Wait for VM to shut down gracefully self.vm.wait() + ASSET_WHEEZY_KERNEL = Asset( + ('https://people.debian.org/~aurel32/qemu/mipsel/' + 'vmlinux-3.2.0-4-5kc-malta'), + '5e8b725244c59745bb8b64f5d8f49f25fecfa549f3395fb6d19a3b9e5065b85b') + + ASSET_WHEEZY_DISK = Asset( + ('https://people.debian.org/~aurel32/qemu/mipsel/' + 'debian_wheezy_mipsel_standard.qcow2'), + '454f09ae39f7e6461c84727b927100d2c7813841f2a0a5dce328114887ecf914') + + def test_wheezy(self): + kernel_path = self.ASSET_WHEEZY_KERNEL.fetch() + image_path = self.ASSET_WHEEZY_DISK.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 root=/dev/sda1') + mips_check_wheezy(self, + kernel_path, image_path, kernel_command_line, cpuinfo='MIPS 20Kc', + dl_file='/boot/initrd.img-3.2.0-4-5kc-malta', + hsum='7579f8b56c1187c7c04d0dc3c0c56c7a6314c5ddd3a9bf8803ecc7cf8a3be9f8') + @skipIfMissingImports('numpy', 'cv2') class MaltaMachineFramebuffer(LinuxKernelTest): From f79592f427d7faabb25d533815d6c3dd4ab9726d Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:23 +0200 Subject: [PATCH 0063/2760] tests/functional: Convert the 64-bit big endian Wheezy mips test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reuse the test function from the 32-bit big endian test to easily convert the 64-bit big endian Wheezy mips test. Since this was the last test in tests/avocado/linux_ssh_mips_malta.py, we can remove this avocado file now, too. Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-10-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 1 - tests/avocado/linux_ssh_mips_malta.py | 181 -------------------------- tests/functional/meson.build | 2 + tests/functional/test_mips64_malta.py | 35 +++++ 4 files changed, 37 insertions(+), 182 deletions(-) delete mode 100644 tests/avocado/linux_ssh_mips_malta.py create mode 100755 tests/functional/test_mips64_malta.py diff --git a/MAINTAINERS b/MAINTAINERS index 42348df9d6..72f6b208f5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1355,7 +1355,6 @@ F: hw/acpi/piix4.c F: hw/mips/malta.c F: hw/pci-host/gt64120.c F: include/hw/southbridge/piix.h -F: tests/avocado/linux_ssh_mips_malta.py F: tests/functional/test_mips*_malta.py F: tests/functional/test_mips*_tuxrun.py diff --git a/tests/avocado/linux_ssh_mips_malta.py b/tests/avocado/linux_ssh_mips_malta.py deleted file mode 100644 index 2fa5cf9a6c..0000000000 --- a/tests/avocado/linux_ssh_mips_malta.py +++ /dev/null @@ -1,181 +0,0 @@ -# Functional test that boots a VM and run commands via a SSH session -# -# Copyright (c) Philippe Mathieu-Daudé -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os -import re -import base64 -import logging -import time - -from avocado import skipUnless -from avocado_qemu import LinuxSSHMixIn -from avocado_qemu import QemuSystemTest -from avocado_qemu import wait_for_console_pattern -from avocado.utils import process -from avocado.utils import archive -from avocado.utils import ssh - - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -@skipUnless(ssh.SSH_CLIENT_BINARY, 'No SSH client available') -class LinuxSSH(QemuSystemTest, LinuxSSHMixIn): - """ - :avocado: tags=accel:tcg - """ - - timeout = 150 # Not for 'configure --enable-debug --enable-debug-tcg' - - KERNEL_COMMON_COMMAND_LINE = 'printk.time=0 ' - VM_IP = '127.0.0.1' - - BASE_URL = 'https://people.debian.org/~aurel32/qemu/' - IMAGE_INFO = { - 'be': {'base_url': 'mips', - 'image_name': 'debian_wheezy_mips_standard.qcow2', - 'image_hash': '8987a63270df67345b2135a6b7a4885a35e392d5', - 'kernel_hash': { - 32: '592e384a4edc16dade52a6cd5c785c637bcbc9ad', - 64: 'db6eea7de35d36c77d8c165b6bcb222e16eb91db'} - }, - 'le': {'base_url': 'mipsel', - 'image_name': 'debian_wheezy_mipsel_standard.qcow2', - 'image_hash': '7866764d9de3ef536ffca24c9fb9f04ffdb45802', - 'kernel_hash': { - 32: 'a66bea5a8adaa2cb3d36a1d4e0ccdb01be8f6c2a', - 64: '6a7f77245acf231415a0e8b725d91ed2f3487794'} - } - } - CPU_INFO = { - 32: {'cpu': 'MIPS 24Kc', 'kernel_release': '3.2.0-4-4kc-malta'}, - 64: {'cpu': 'MIPS 20Kc', 'kernel_release': '3.2.0-4-5kc-malta'} - } - - def get_url(self, endianess, path=''): - qkey = {'le': 'el', 'be': ''} - return '%s/mips%s/%s' % (self.BASE_URL, qkey[endianess], path) - - def get_image_info(self, endianess): - dinfo = self.IMAGE_INFO[endianess] - image_url = self.get_url(endianess, dinfo['image_name']) - image_hash = dinfo['image_hash'] - return (image_url, image_hash) - - def get_kernel_info(self, endianess, wordsize): - minfo = self.CPU_INFO[wordsize] - kernel_url = self.get_url(endianess, - 'vmlinux-%s' % minfo['kernel_release']) - kernel_hash = self.IMAGE_INFO[endianess]['kernel_hash'][wordsize] - return kernel_url, kernel_hash - - def ssh_disconnect_vm(self): - self.ssh_session.quit() - - def boot_debian_wheezy_image_and_ssh_login(self, endianess, kernel_path): - image_url, image_hash = self.get_image_info(endianess) - image_path = self.fetch_asset(image_url, asset_hash=image_hash) - - self.vm.set_console() - kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE - + 'console=ttyS0 root=/dev/sda1') - self.vm.add_args('-no-reboot', - '-kernel', kernel_path, - '-append', kernel_command_line, - '-drive', 'file=%s,snapshot=on' % image_path, - '-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', - '-device', 'pcnet,netdev=vnet') - self.vm.launch() - - self.log.info('VM launched, waiting for sshd') - console_pattern = 'Starting OpenBSD Secure Shell server: sshd' - wait_for_console_pattern(self, console_pattern, 'Oops') - self.log.info('sshd ready') - - self.ssh_connect('root', 'root', False) - - def shutdown_via_ssh(self): - self.ssh_command('poweroff') - self.ssh_disconnect_vm() - wait_for_console_pattern(self, 'Power down', 'Oops') - - def run_common_commands(self, wordsize): - self.ssh_command_output_contains( - 'cat /proc/cpuinfo', - self.CPU_INFO[wordsize]['cpu']) - self.ssh_command_output_contains( - 'uname -m', - 'mips') - self.ssh_command_output_contains( - 'uname -r', - self.CPU_INFO[wordsize]['kernel_release']) - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC timer') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC i8042') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC serial') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC ata_piix') - self.ssh_command_output_contains( - 'cat /proc/interrupts', - 'XT-PIC eth0') - self.ssh_command_output_contains( - 'cat /proc/devices', - 'input') - self.ssh_command_output_contains( - 'cat /proc/devices', - 'usb') - self.ssh_command_output_contains( - 'cat /proc/devices', - 'fb') - self.ssh_command_output_contains( - 'cat /proc/ioports', - ' : serial') - self.ssh_command_output_contains( - 'cat /proc/ioports', - ' : ata_piix') - self.ssh_command_output_contains( - 'cat /proc/ioports', - ' : piix4_smbus') - self.ssh_command_output_contains( - 'lspci -d 11ab:4620', - 'GT-64120') - self.ssh_command_output_contains( - 'cat /sys/bus/i2c/devices/i2c-0/name', - 'SMBus PIIX4 adapter') - self.ssh_command_output_contains( - 'cat /proc/mtd', - 'YAMON') - # Empty 'Board Config' (64KB) - self.ssh_command_output_contains( - 'md5sum /dev/mtd2ro', - '0dfbe8aa4c20b52e1b8bf3cb6cbdf193') - - def check_mips_malta(self, uname_m, endianess): - wordsize = 64 if '64' in uname_m else 32 - kernel_url, kernel_hash = self.get_kernel_info(endianess, wordsize) - kernel_path = self.fetch_asset(kernel_url, asset_hash=kernel_hash) - self.boot_debian_wheezy_image_and_ssh_login(endianess, kernel_path) - - stdout, _ = self.ssh_command('uname -a') - self.assertIn(True, [uname_m + " GNU/Linux" in line for line in stdout]) - - self.run_common_commands(wordsize) - self.shutdown_via_ssh() - # Wait for VM to shut down gracefully - self.vm.wait() - - def test_mips_malta64eb_kernel3_2_0(self): - """ - :avocado: tags=arch:mips64 - :avocado: tags=endian:big - :avocado: tags=device:pcnet32 - """ - self.check_mips_malta('mips64', 'be') diff --git a/tests/functional/meson.build b/tests/functional/meson.build index a4a317115b..985ac5c27f 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -42,6 +42,7 @@ test_timeouts = { 'mips_malta' : 480, 'mipsel_malta' : 420, 'mipsel_replay' : 480, + 'mips64_malta' : 240, 'mips64el_malta' : 420, 'mips64el_replay' : 180, 'netdev_ethtool' : 180, @@ -191,6 +192,7 @@ tests_mipsel_system_thorough = [ ] tests_mips64_system_thorough = [ + 'mips64_malta', 'mips64_tuxrun', ] diff --git a/tests/functional/test_mips64_malta.py b/tests/functional/test_mips64_malta.py new file mode 100755 index 0000000000..53c3e0c122 --- /dev/null +++ b/tests/functional/test_mips64_malta.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# +# Functional tests for the big-endian 64-bit MIPS Malta board +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset +from test_mips_malta import mips_check_wheezy + + +class MaltaMachineConsole(LinuxKernelTest): + + ASSET_WHEEZY_KERNEL = Asset( + ('https://people.debian.org/~aurel32/qemu/mips/' + 'vmlinux-3.2.0-4-5kc-malta'), + '3e4ec154db080b3f1839f04dde83120654a33e5e1716863de576c47cb94f68f6') + + ASSET_WHEEZY_DISK = Asset( + ('https://people.debian.org/~aurel32/qemu/mips/' + 'debian_wheezy_mips_standard.qcow2'), + 'de03599285b8382ad309309a6c4869f6c6c42a5cfc983342bab9ec0dfa7849a2') + + def test_wheezy(self): + kernel_path = self.ASSET_WHEEZY_KERNEL.fetch() + image_path = self.ASSET_WHEEZY_DISK.fetch() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'console=ttyS0 root=/dev/sda1') + mips_check_wheezy(self, + kernel_path, image_path, kernel_command_line, cpuinfo='MIPS 20Kc', + dl_file='/boot/initrd.img-3.2.0-4-5kc-malta', + hsum='d98b953bb4a41c0fc0fd8d19bbc691c08989ac52568c1d3054d92dfd890d3f06') + + +if __name__ == '__main__': + LinuxKernelTest.main() From e83aee9c6a0f169e8c5d9387f2429ec8f539dda9 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:24 +0200 Subject: [PATCH 0064/2760] tests/avocado: Remove the boot_linux.py tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These tests are based on the cloudinit functions from Avocado. The cloudinit is very, very slow compared to our other tests, so most of these Avocado tests have either been disabled by default with a decorator, or have been marked to only run with KVM. We won't include this sluggish cloudinit stuff in the functional framework, and we've already got plenty of other tests there that check pretty much the same things, so let's simply get rid of these old tests now. Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-11-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/avocado/boot_linux.py | 132 ------------------------------------ 1 file changed, 132 deletions(-) delete mode 100644 tests/avocado/boot_linux.py diff --git a/tests/avocado/boot_linux.py b/tests/avocado/boot_linux.py deleted file mode 100644 index a029ef4ad1..0000000000 --- a/tests/avocado/boot_linux.py +++ /dev/null @@ -1,132 +0,0 @@ -# Functional test that boots a complete Linux system via a cloud image -# -# Copyright (c) 2018-2020 Red Hat, Inc. -# -# Author: -# Cleber Rosa -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os - -from avocado_qemu.linuxtest import LinuxTest -from avocado_qemu import BUILD_DIR - -from avocado import skipUnless - - -class BootLinuxX8664(LinuxTest): - """ - :avocado: tags=arch:x86_64 - """ - timeout = 480 - - def test_pc_i440fx_tcg(self): - """ - :avocado: tags=machine:pc - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.launch_and_wait(set_up_ssh_connection=False) - - def test_pc_i440fx_kvm(self): - """ - :avocado: tags=machine:pc - :avocado: tags=accel:kvm - """ - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.launch_and_wait(set_up_ssh_connection=False) - - def test_pc_q35_tcg(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.launch_and_wait(set_up_ssh_connection=False) - - def test_pc_q35_kvm(self): - """ - :avocado: tags=machine:q35 - :avocado: tags=accel:kvm - """ - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.launch_and_wait(set_up_ssh_connection=False) - - -# For Aarch64 we only boot KVM tests in CI as booting the current -# Fedora OS in TCG tests is very heavyweight. There are lighter weight -# distros which we use in the machine_aarch64_virt.py tests. -class BootLinuxAarch64(LinuxTest): - """ - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - """ - timeout = 720 - - def test_virt_kvm(self): - """ - :avocado: tags=accel:kvm - :avocado: tags=cpu:host - """ - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.vm.add_args("-machine", "virt,gic-version=host") - self.vm.add_args('-bios', - os.path.join(BUILD_DIR, 'pc-bios', - 'edk2-aarch64-code.fd')) - self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') - self.vm.add_args('-object', 'rng-random,id=rng0,filename=/dev/urandom') - self.launch_and_wait(set_up_ssh_connection=False) - - -# See the tux_baseline.py tests for almost the same coverage in a lot -# less time. -class BootLinuxPPC64(LinuxTest): - """ - :avocado: tags=arch:ppc64 - """ - - timeout = 360 - - @skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') - def test_pseries_tcg(self): - """ - :avocado: tags=machine:pseries - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.launch_and_wait(set_up_ssh_connection=False) - - def test_pseries_kvm(self): - """ - :avocado: tags=machine:pseries - :avocado: tags=accel:kvm - """ - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.vm.add_args("-machine", "cap-ccf-assist=off") - self.launch_and_wait(set_up_ssh_connection=False) - -class BootLinuxS390X(LinuxTest): - """ - :avocado: tags=arch:s390x - """ - - timeout = 240 - - @skipUnless(os.getenv('SPEED') == 'slow', 'runtime limited') - def test_s390_ccw_virtio_tcg(self): - """ - :avocado: tags=machine:s390-ccw-virtio - :avocado: tags=accel:tcg - """ - self.require_accelerator("tcg") - self.vm.add_args("-accel", "tcg") - self.launch_and_wait(set_up_ssh_connection=False) From 7fecdb0acd99fa838bb461c67164f6119ec1a3bb Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:25 +0200 Subject: [PATCH 0065/2760] tests/functional: Use the tuxrun kernel for the x86 replay test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way we can do a full boot in record-replay mode and should get a similar test coverage compared to the old replay test from tests/avocado/replay_linux.py. Thus remove the x86 avocado replay_linux test now. Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-12-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/avocado/replay_linux.py | 46 -------------------------- tests/functional/test_x86_64_replay.py | 43 ++++++++++++++++++------ 2 files changed, 33 insertions(+), 56 deletions(-) diff --git a/tests/avocado/replay_linux.py b/tests/avocado/replay_linux.py index 5916922435..6ba283d3bf 100644 --- a/tests/avocado/replay_linux.py +++ b/tests/avocado/replay_linux.py @@ -118,52 +118,6 @@ def run_replay_dump(self, replay_path): except subprocess.CalledProcessError: self.fail('replay-dump.py failed') -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -class ReplayLinuxX8664(ReplayLinux): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=accel:tcg - """ - - chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0' - - def test_pc_i440fx(self): - """ - :avocado: tags=machine:pc - """ - self.run_rr(shift=1) - - def test_pc_q35(self): - """ - :avocado: tags=machine:q35 - """ - self.run_rr(shift=3) - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -class ReplayLinuxX8664Virtio(ReplayLinux): - """ - :avocado: tags=arch:x86_64 - :avocado: tags=virtio - :avocado: tags=accel:tcg - """ - - hdd = 'virtio-blk-pci' - cd = 'virtio-blk-pci' - bus = None - - chksum = 'e3c1b309d9203604922d6e255c2c5d098a309c2d46215d8fc026954f3c5c27a0' - - def test_pc_i440fx(self): - """ - :avocado: tags=machine:pc - """ - self.run_rr(shift=1) - - def test_pc_q35(self): - """ - :avocado: tags=machine:q35 - """ - self.run_rr(shift=3) @skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') class ReplayLinuxAarch64(ReplayLinux): diff --git a/tests/functional/test_x86_64_replay.py b/tests/functional/test_x86_64_replay.py index 180f23a60c..27287d452d 100755 --- a/tests/functional/test_x86_64_replay.py +++ b/tests/functional/test_x86_64_replay.py @@ -5,30 +5,53 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -from qemu_test import Asset, skipFlakyTest +from subprocess import check_call, DEVNULL + +from qemu_test import Asset, skipFlakyTest, get_qemu_img from replay_kernel import ReplayKernelBase class X86Replay(ReplayKernelBase): ASSET_KERNEL = Asset( - ('https://archives.fedoraproject.org/pub/archive/fedora/linux' - '/releases/29/Everything/x86_64/os/images/pxeboot/vmlinuz'), - '8f237d84712b1b411baf3af2aeaaee10b9aae8e345ec265b87ab3a39639eb143') + 'https://storage.tuxboot.com/buildroot/20241119/x86_64/bzImage', + 'f57bfc6553bcd6e0a54aab86095bf642b33b5571d14e3af1731b18c87ed5aef8') + + ASSET_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/x86_64/rootfs.ext4.zst', + '4b8b2a99117519c5290e1202cb36eb6c7aaba92b357b5160f5970cf5fb78a751') - def do_test_x86(self, machine): + def do_test_x86(self, machine, blkdevice, devroot): + self.require_netdev('user') self.set_machine(machine) + self.cpu="Nehalem" kernel_path = self.ASSET_KERNEL.fetch() - kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0' - console_pattern = 'VFS: Cannot open root device' - self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5) + + raw_disk = self.uncompress(self.ASSET_ROOTFS) + disk = self.scratch_file('scratch.qcow2') + qemu_img = get_qemu_img(self) + check_call([qemu_img, 'create', '-f', 'qcow2', '-b', raw_disk, + '-F', 'raw', disk], stdout=DEVNULL, stderr=DEVNULL) + + args = ('-drive', 'file=%s,snapshot=on,id=hd0,if=none' % disk, + '-drive', 'driver=blkreplay,id=hd0-rr,if=none,image=hd0', + '-device', '%s,drive=hd0-rr' % blkdevice, + '-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', + '-device', 'virtio-net,netdev=vnet', + '-object', 'filter-replay,id=replay,netdev=vnet') + + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + f"console=ttyS0 root=/dev/{devroot}") + console_pattern = 'Welcome to TuxTest' + self.run_rr(kernel_path, kernel_command_line, console_pattern, shift=5, + args=args) @skipFlakyTest('https://gitlab.com/qemu-project/qemu/-/issues/2094') def test_pc(self): - self.do_test_x86('pc') + self.do_test_x86('pc', 'virtio-blk', 'vda') def test_q35(self): - self.do_test_x86('q35') + self.do_test_x86('q35', 'ide-hd', 'sda') if __name__ == '__main__': From a820caf8444c963faf69228e4a0a0d4673c24ab5 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:26 +0200 Subject: [PATCH 0066/2760] tests/functional: Use the tuxrun kernel for the aarch64 replay test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This way we can do a full boot in record-replay mode and should get a similar test coverage compared to the old replay test from tests/avocado/replay_linux.py. Since the aarch64 test was the last avocado test in the tests/avocado/replay_linux.py file, we can remove this file now completely. Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth Message-ID: <20250414113031.151105-13-thuth@redhat.com> --- MAINTAINERS | 1 - tests/avocado/replay_linux.py | 160 ------------------------ tests/functional/test_aarch64_replay.py | 37 ++++-- 3 files changed, 29 insertions(+), 169 deletions(-) delete mode 100644 tests/avocado/replay_linux.py diff --git a/MAINTAINERS b/MAINTAINERS index 72f6b208f5..e29910cbd7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3668,7 +3668,6 @@ F: include/system/replay.h F: docs/devel/replay.rst F: docs/system/replay.rst F: stubs/replay.c -F: tests/avocado/replay_linux.py F: tests/functional/*reverse_debug*.py F: tests/functional/*replay*.py F: qapi/replay.json diff --git a/tests/avocado/replay_linux.py b/tests/avocado/replay_linux.py deleted file mode 100644 index 6ba283d3bf..0000000000 --- a/tests/avocado/replay_linux.py +++ /dev/null @@ -1,160 +0,0 @@ -# Record/replay test that boots a complete Linux system via a cloud image -# -# Copyright (c) 2020 ISP RAS -# -# Author: -# Pavel Dovgalyuk -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os -import logging -import time - -from avocado import skipUnless -from avocado_qemu import BUILD_DIR -from avocado.utils import cloudinit -from avocado.utils import network -from avocado.utils import vmimage -from avocado.utils import datadrainer -from avocado.utils.path import find_command -from avocado_qemu.linuxtest import LinuxTest - -class ReplayLinux(LinuxTest): - """ - Boots a Linux system, checking for a successful initialization - """ - - timeout = 1800 - chksum = None - hdd = 'ide-hd' - cd = 'ide-cd' - bus = 'ide' - - def setUp(self): - # LinuxTest does many replay-incompatible things, but includes - # useful methods. Do not setup LinuxTest here and just - # call some functions. - super(LinuxTest, self).setUp() - self._set_distro() - self.boot_path = self.download_boot() - self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0), - self.name) - ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys() - self.cloudinit_path = self.prepare_cloudinit(ssh_pubkey) - - def vm_add_disk(self, vm, path, id, device): - bus_string = '' - if self.bus: - bus_string = ',bus=%s.%d' % (self.bus, id,) - vm.add_args('-drive', 'file=%s,snapshot=on,id=disk%s,if=none' % (path, id)) - vm.add_args('-drive', - 'driver=blkreplay,id=disk%s-rr,if=none,image=disk%s' % (id, id)) - vm.add_args('-device', - '%s,drive=disk%s-rr%s' % (device, id, bus_string)) - - def vm_add_cdrom(self, vm, path, id, device): - vm.add_args('-drive', 'file=%s,id=disk%s,if=none,media=cdrom' % (path, id)) - - def launch_and_wait(self, record, args, shift): - self.require_netdev('user') - vm = self.get_vm() - vm.add_args('-smp', '1') - vm.add_args('-m', '1024') - vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', - '-device', 'virtio-net,netdev=vnet') - vm.add_args('-object', 'filter-replay,id=replay,netdev=vnet') - if args: - vm.add_args(*args) - self.vm_add_disk(vm, self.boot_path, 0, self.hdd) - self.vm_add_cdrom(vm, self.cloudinit_path, 1, self.cd) - logger = logging.getLogger('replay') - if record: - logger.info('recording the execution...') - mode = 'record' - else: - logger.info('replaying the execution...') - mode = 'replay' - replay_path = os.path.join(self.workdir, 'replay.bin') - vm.add_args('-icount', 'shift=%s,rr=%s,rrfile=%s' % - (shift, mode, replay_path)) - - start_time = time.time() - - vm.set_console() - vm.launch() - console_drainer = datadrainer.LineLogger(vm.console_socket.fileno(), - logger=self.log.getChild('console'), - stop_check=(lambda : not vm.is_running())) - console_drainer.start() - if record: - while not self.phone_server.instance_phoned_back: - self.phone_server.handle_request() - vm.shutdown() - logger.info('finished the recording with log size %s bytes' - % os.path.getsize(replay_path)) - self.run_replay_dump(replay_path) - logger.info('successfully tested replay-dump.py') - else: - vm.event_wait('SHUTDOWN', self.timeout) - vm.wait() - logger.info('successfully finished the replay') - elapsed = time.time() - start_time - logger.info('elapsed time %.2f sec' % elapsed) - return elapsed - - def run_rr(self, args=None, shift=7): - t1 = self.launch_and_wait(True, args, shift) - t2 = self.launch_and_wait(False, args, shift) - logger = logging.getLogger('replay') - logger.info('replay overhead {:.2%}'.format(t2 / t1 - 1)) - - def run_replay_dump(self, replay_path): - try: - subprocess.check_call(["./scripts/replay-dump.py", - "-f", replay_path], - stdout=subprocess.DEVNULL) - except subprocess.CalledProcessError: - self.fail('replay-dump.py failed') - - -@skipUnless(os.getenv('AVOCADO_TIMEOUT_EXPECTED'), 'Test might timeout') -class ReplayLinuxAarch64(ReplayLinux): - """ - :avocado: tags=accel:tcg - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=cpu:max - """ - - chksum = '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49' - - hdd = 'virtio-blk-device' - cd = 'virtio-blk-device' - bus = None - - def get_common_args(self): - return ('-bios', - os.path.join(BUILD_DIR, 'pc-bios', 'edk2-aarch64-code.fd'), - "-cpu", "max,lpa2=off", - '-device', 'virtio-rng-pci,rng=rng0', - '-object', 'rng-builtin,id=rng0') - - def test_virt_gicv2(self): - """ - :avocado: tags=machine:gic-version=2 - """ - - self.run_rr(shift=3, - args=(*self.get_common_args(), - "-machine", "virt,gic-version=2")) - - def test_virt_gicv3(self): - """ - :avocado: tags=machine:gic-version=3 - """ - - self.run_rr(shift=3, - args=(*self.get_common_args(), - "-machine", "virt,gic-version=3")) diff --git a/tests/functional/test_aarch64_replay.py b/tests/functional/test_aarch64_replay.py index bd6609d914..db12e76603 100755 --- a/tests/functional/test_aarch64_replay.py +++ b/tests/functional/test_aarch64_replay.py @@ -5,25 +5,46 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -from qemu_test import Asset, skipIfOperatingSystem +from subprocess import check_call, DEVNULL + +from qemu_test import Asset, skipIfOperatingSystem, get_qemu_img from replay_kernel import ReplayKernelBase class Aarch64Replay(ReplayKernelBase): ASSET_KERNEL = Asset( - ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' - 'releases/29/Everything/aarch64/os/images/pxeboot/vmlinuz'), - '7e1430b81c26bdd0da025eeb8fbd77b5dc961da4364af26e771bd39f379cbbf7') + 'https://storage.tuxboot.com/buildroot/20241119/arm64/Image', + 'b74743c5e89e1cea0f73368d24ae0ae85c5204ff84be3b5e9610417417d2f235') + + ASSET_ROOTFS = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/arm64/rootfs.ext4.zst', + 'a1acaaae2068df4648d04ff75f532aaa8c5edcd6b936122b6f0db4848a07b465') def test_aarch64_virt(self): + self.require_netdev('user') self.set_machine('virt') - self.cpu = 'cortex-a53' + self.cpu = 'cortex-a57' kernel_path = self.ASSET_KERNEL.fetch() + + raw_disk = self.uncompress(self.ASSET_ROOTFS) + disk = self.scratch_file('scratch.qcow2') + qemu_img = get_qemu_img(self) + check_call([qemu_img, 'create', '-f', 'qcow2', '-b', raw_disk, + '-F', 'raw', disk], stdout=DEVNULL, stderr=DEVNULL) + + args = ('-drive', 'file=%s,snapshot=on,id=hd0,if=none' % disk, + '-drive', 'driver=blkreplay,id=hd0-rr,if=none,image=hd0', + '-device', 'virtio-blk-device,drive=hd0-rr', + '-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', + '-device', 'virtio-net,netdev=vnet', + '-object', 'filter-replay,id=replay,netdev=vnet') + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'console=ttyAMA0') - console_pattern = 'VFS: Cannot open root device' - self.run_rr(kernel_path, kernel_command_line, console_pattern) + 'console=ttyAMA0 root=/dev/vda') + console_pattern = 'Welcome to TuxTest' + self.run_rr(kernel_path, kernel_command_line, console_pattern, + args=args) if __name__ == '__main__': From 5c2bae2155b162f7355ca759881c5fdaaf7bc209 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:27 +0200 Subject: [PATCH 0067/2760] tests/functional: Convert the SMMU test to the functional framework This test was using cloudinit and a "dnf install" command in the guest to exercise the NIC with SMMU enabled. Since we don't have the cloudinit stuff in the functional framework and we should not rely on having access to external networks (once our ASSETs have been cached), we rather boot into the initrd first, manually mount the root disk and then use the check_http_download() function from the functional framework here instead for testing whether the network works as expected. Unfortunately, there seems to be a small race when using the files from Fedora 33: To enter the initrd shell, we have to send a "return" once. But it does not seem to work if we send it too early. Using a sleep(0.2) makes it work reliably for me, but to make it even more unlikely to trigger this situation, let's better limit the Fedora 33 tests to only run with KVM. Finally, while we're at it, we also add some lines for testing writes to the hard disk, as we already do it in the test_intel_iommu test. Reviewed-by: Eric Auger Tested-by: Eric Auger Message-ID: <20250414113031.151105-14-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 2 +- tests/avocado/smmu.py | 139 ----------------- tests/functional/meson.build | 2 + tests/functional/test_aarch64_smmu.py | 205 ++++++++++++++++++++++++++ 4 files changed, 208 insertions(+), 140 deletions(-) delete mode 100644 tests/avocado/smmu.py create mode 100755 tests/functional/test_aarch64_smmu.py diff --git a/MAINTAINERS b/MAINTAINERS index e29910cbd7..07f77c048e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -211,7 +211,7 @@ L: qemu-arm@nongnu.org S: Maintained F: hw/arm/smmu* F: include/hw/arm/smmu* -F: tests/avocado/smmu.py +F: tests/functional/test_aarch64_smmu.py AVR TCG CPUs M: Michael Rolnik diff --git a/tests/avocado/smmu.py b/tests/avocado/smmu.py deleted file mode 100644 index 83fd79e922..0000000000 --- a/tests/avocado/smmu.py +++ /dev/null @@ -1,139 +0,0 @@ -# SMMUv3 Functional tests -# -# Copyright (c) 2021 Red Hat, Inc. -# -# Author: -# Eric Auger -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. -import os - -from avocado import skipUnless -from avocado_qemu import BUILD_DIR -from avocado_qemu.linuxtest import LinuxTest - -@skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') -class SMMU(LinuxTest): - """ - :avocado: tags=accel:kvm - :avocado: tags=cpu:host - :avocado: tags=arch:aarch64 - :avocado: tags=machine:virt - :avocado: tags=distro:fedora - :avocado: tags=smmu - :avocado: tags=flaky - """ - - IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' - kernel_path = None - initrd_path = None - kernel_params = None - - def set_up_boot(self): - path = self.download_boot() - self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + - 'drive=drv0,id=virtio-disk0,bootindex=1,' - 'werror=stop,rerror=stop' + self.IOMMU_ADDON) - self.vm.add_args('-drive', - 'file=%s,if=none,cache=writethrough,id=drv0' % path) - - def setUp(self): - super(SMMU, self).setUp(None, 'virtio-net-pci' + self.IOMMU_ADDON) - - def common_vm_setup(self, custom_kernel=False): - self.require_accelerator("kvm") - self.vm.add_args("-accel", "kvm") - self.vm.add_args("-cpu", "host") - self.vm.add_args("-machine", "iommu=smmuv3") - self.vm.add_args("-d", "guest_errors") - self.vm.add_args('-bios', os.path.join(BUILD_DIR, 'pc-bios', - 'edk2-aarch64-code.fd')) - self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') - self.vm.add_args('-object', - 'rng-random,id=rng0,filename=/dev/urandom') - - if custom_kernel is False: - return - - kernel_url = self.distro.pxeboot_url + 'vmlinuz' - initrd_url = self.distro.pxeboot_url + 'initrd.img' - self.kernel_path = self.fetch_asset(kernel_url) - self.initrd_path = self.fetch_asset(initrd_url) - - def run_and_check(self): - if self.kernel_path: - self.vm.add_args('-kernel', self.kernel_path, - '-append', self.kernel_params, - '-initrd', self.initrd_path) - self.launch_and_wait() - self.ssh_command('cat /proc/cmdline') - self.ssh_command('dnf -y install numactl-devel') - - - # 5.3 kernel without RIL # - - def test_smmu_noril(self): - """ - :avocado: tags=smmu_noril - :avocado: tags=smmu_noril_tests - :avocado: tags=distro_version:31 - """ - self.common_vm_setup() - self.run_and_check() - - def test_smmu_noril_passthrough(self): - """ - :avocado: tags=smmu_noril_passthrough - :avocado: tags=smmu_noril_tests - :avocado: tags=distro_version:31 - """ - self.common_vm_setup(True) - self.kernel_params = (self.distro.default_kernel_params + - ' iommu.passthrough=on') - self.run_and_check() - - def test_smmu_noril_nostrict(self): - """ - :avocado: tags=smmu_noril_nostrict - :avocado: tags=smmu_noril_tests - :avocado: tags=distro_version:31 - """ - self.common_vm_setup(True) - self.kernel_params = (self.distro.default_kernel_params + - ' iommu.strict=0') - self.run_and_check() - - # 5.8 kernel featuring range invalidation - # >= v5.7 kernel - - def test_smmu_ril(self): - """ - :avocado: tags=smmu_ril - :avocado: tags=smmu_ril_tests - :avocado: tags=distro_version:33 - """ - self.common_vm_setup() - self.run_and_check() - - def test_smmu_ril_passthrough(self): - """ - :avocado: tags=smmu_ril_passthrough - :avocado: tags=smmu_ril_tests - :avocado: tags=distro_version:33 - """ - self.common_vm_setup(True) - self.kernel_params = (self.distro.default_kernel_params + - ' iommu.passthrough=on') - self.run_and_check() - - def test_smmu_ril_nostrict(self): - """ - :avocado: tags=smmu_ril_nostrict - :avocado: tags=smmu_ril_tests - :avocado: tags=distro_version:33 - """ - self.common_vm_setup(True) - self.kernel_params = (self.distro.default_kernel_params + - ' iommu.strict=0') - self.run_and_check() diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 985ac5c27f..b317ad42c5 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -18,6 +18,7 @@ test_timeouts = { 'aarch64_rme_sbsaref' : 1200, 'aarch64_sbsaref_alpine' : 1200, 'aarch64_sbsaref_freebsd' : 720, + 'aarch64_smmu' : 720, 'aarch64_tuxrun' : 240, 'aarch64_virt' : 360, 'aarch64_virt_gpu' : 480, @@ -88,6 +89,7 @@ tests_aarch64_system_thorough = [ 'aarch64_sbsaref', 'aarch64_sbsaref_alpine', 'aarch64_sbsaref_freebsd', + 'aarch64_smmu', 'aarch64_tcg_plugins', 'aarch64_tuxrun', 'aarch64_virt', diff --git a/tests/functional/test_aarch64_smmu.py b/tests/functional/test_aarch64_smmu.py new file mode 100755 index 0000000000..c65d0f2817 --- /dev/null +++ b/tests/functional/test_aarch64_smmu.py @@ -0,0 +1,205 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# SMMUv3 Functional tests +# +# Copyright (c) 2021 Red Hat, Inc. +# +# Author: +# Eric Auger +# +# This work is licensed under the terms of the GNU GPL, version 2 or +# later. See the COPYING file in the top-level directory. + +import os +import time + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import BUILD_DIR +from qemu.utils import kvm_available + + +class SMMU(LinuxKernelTest): + + default_kernel_params = ('earlyprintk=pl011,0x9000000 no_timer_check ' + 'printk.time=1 rd_NO_PLYMOUTH net.ifnames=0 ' + 'console=ttyAMA0 rd.rescue') + IOMMU_ADDON = ',iommu_platform=on,disable-modern=off,disable-legacy=on' + kernel_path = None + initrd_path = None + kernel_params = None + + GUEST_PORT = 8080 + + def set_up_boot(self, path): + self.vm.add_args('-device', 'virtio-blk-pci,bus=pcie.0,' + + 'drive=drv0,id=virtio-disk0,bootindex=1,' + 'werror=stop,rerror=stop' + self.IOMMU_ADDON) + self.vm.add_args('-drive', + f'file={path},if=none,cache=writethrough,id=drv0,snapshot=on') + + self.vm.add_args('-netdev', + 'user,id=n1,hostfwd=tcp:127.0.0.1:0-:%d' % + self.GUEST_PORT) + self.vm.add_args('-device', 'virtio-net,netdev=n1' + self.IOMMU_ADDON) + + def common_vm_setup(self, kernel, initrd, disk): + self.require_accelerator("kvm") + self.require_netdev('user') + self.set_machine("virt") + self.vm.add_args('-m', '1G') + self.vm.add_args("-accel", "kvm") + self.vm.add_args("-cpu", "host") + self.vm.add_args("-machine", "iommu=smmuv3") + self.vm.add_args("-d", "guest_errors") + self.vm.add_args('-bios', os.path.join(BUILD_DIR, 'pc-bios', + 'edk2-aarch64-code.fd')) + self.vm.add_args('-device', 'virtio-rng-pci,rng=rng0') + self.vm.add_args('-object', + 'rng-random,id=rng0,filename=/dev/urandom') + + self.kernel_path = kernel.fetch() + self.initrd_path = initrd.fetch() + self.set_up_boot(disk.fetch()) + + def run_and_check(self, filename, hashsum): + self.vm.add_args('-initrd', self.initrd_path) + self.vm.add_args('-append', self.kernel_params) + self.launch_kernel(self.kernel_path, initrd=self.initrd_path, + wait_for='attach it to a bug report.') + prompt = '# ' + # Fedora 33 requires 'return' to be pressed to enter the shell. + # There seems to be a small race between detecting the previous ':' + # and sending the newline, so we need to add a small delay here. + self.wait_for_console_pattern(':') + time.sleep(0.2) + exec_command_and_wait_for_pattern(self, '\n', prompt) + exec_command_and_wait_for_pattern(self, 'cat /proc/cmdline', + self.kernel_params) + + # Checking for SMMU enablement: + self.log.info("Checking whether SMMU has been enabled...") + exec_command_and_wait_for_pattern(self, 'dmesg | grep smmu', + 'arm-smmu-v3') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, + 'find /sys/kernel/iommu_groups/ -type l', + 'devices/0000:00:') + self.wait_for_console_pattern(prompt) + + # Copy a file (checked later), umount afterwards to drop disk cache: + self.log.info("Checking hard disk...") + exec_command_and_wait_for_pattern(self, + "while ! (dmesg -c | grep vda:) ; do sleep 1 ; done", + "vda2") + exec_command_and_wait_for_pattern(self, 'mount /dev/vda2 /sysroot', + 'mounted filesystem') + exec_command_and_wait_for_pattern(self, 'cp /bin/vi /sysroot/root/vi', + prompt) + exec_command_and_wait_for_pattern(self, 'umount /sysroot', prompt) + # Switch from initrd to the cloud image filesystem: + exec_command_and_wait_for_pattern(self, 'mount /dev/vda2 /sysroot', + prompt) + exec_command_and_wait_for_pattern(self, + ('for d in dev proc sys run ; do ' + 'mount -o bind /$d /sysroot/$d ; done'), prompt) + exec_command_and_wait_for_pattern(self, 'chroot /sysroot', prompt) + # Check files on the hard disk: + exec_command_and_wait_for_pattern(self, + ('if diff -q /root/vi /usr/bin/vi ; then echo "file" "ok" ; ' + 'else echo "files differ"; fi'), 'file ok') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, f'sha256sum {filename}', + hashsum) + + # Check virtio-net via HTTP: + exec_command_and_wait_for_pattern(self, 'dhclient eth0', prompt) + self.check_http_download(filename, hashsum, self.GUEST_PORT) + + + # 5.3 kernel without RIL # + + ASSET_KERNEL_F31 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/31/Server/aarch64/os/images/pxeboot/vmlinuz'), + '3ae07fcafbfc8e4abeb693035a74fe10698faae15e9ccd48882a9167800c1527') + + ASSET_INITRD_F31 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/31/Server/aarch64/os/images/pxeboot/initrd.img'), + '9f3146b28bc531c689f3c5f114cb74e4bd7bd548e0ba19fa77921d8bd256755a') + + ASSET_DISK_F31 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/31/Cloud/aarch64/images/Fedora-Cloud-Base-31-1.9.aarch64.qcow2'), + '1e18d9c0cf734940c4b5d5ec592facaed2af0ad0329383d5639c997fdf16fe49') + + F31_FILENAME = '/boot/initramfs-5.3.7-301.fc31.aarch64.img' + F31_HSUM = '1a4beec6607d94df73d9dd1b4985c9c23dd0fdcf4e6ca1351d477f190df7bef9' + + def test_smmu_noril(self): + self.common_vm_setup(self.ASSET_KERNEL_F31, self.ASSET_INITRD_F31, + self.ASSET_DISK_F31) + self.kernel_params = self.default_kernel_params + self.run_and_check(self.F31_FILENAME, self.F31_HSUM) + + def test_smmu_noril_passthrough(self): + self.common_vm_setup(self.ASSET_KERNEL_F31, self.ASSET_INITRD_F31, + self.ASSET_DISK_F31) + self.kernel_params = (self.default_kernel_params + + ' iommu.passthrough=on') + self.run_and_check(self.F31_FILENAME, self.F31_HSUM) + + def test_smmu_noril_nostrict(self): + self.common_vm_setup(self.ASSET_KERNEL_F31, self.ASSET_INITRD_F31, + self.ASSET_DISK_F31) + self.kernel_params = (self.default_kernel_params + + ' iommu.strict=0') + self.run_and_check(self.F31_FILENAME, self.F31_HSUM) + + + # 5.8 kernel featuring range invalidation + # >= v5.7 kernel + + ASSET_KERNEL_F33 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/33/Server/aarch64/os/images/pxeboot/vmlinuz'), + 'd8b1e6f7241f339d8e7609c456cf0461ffa4583ed07e0b55c7d1d8a0c154aa89') + + ASSET_INITRD_F33 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/' + 'releases/33/Server/aarch64/os/images/pxeboot/initrd.img'), + '92513f55295c2c16a777f7b6c35ccd70a438e9e1e40b6ba39e0e60900615b3df') + + ASSET_DISK_F33 = Asset( + ('https://archives.fedoraproject.org/pub/archive/fedora/linux/releases' + '/33/Cloud/aarch64/images/Fedora-Cloud-Base-33-1.2.aarch64.qcow2'), + 'e7f75cdfd523fe5ac2ca9eeece68edc1a81f386a17f969c1d1c7c87031008a6b') + + F33_FILENAME = '/boot/initramfs-5.8.15-301.fc33.aarch64.img' + F33_HSUM = '079cfad0caa82e84c8ca1fb0897a4999dd769f262216099f518619e807a550d9' + + def test_smmu_ril(self): + self.common_vm_setup(self.ASSET_KERNEL_F33, self.ASSET_INITRD_F33, + self.ASSET_DISK_F33) + self.kernel_params = self.default_kernel_params + self.run_and_check(self.F33_FILENAME, self.F33_HSUM) + + def test_smmu_ril_passthrough(self): + self.common_vm_setup(self.ASSET_KERNEL_F33, self.ASSET_INITRD_F33, + self.ASSET_DISK_F33) + self.kernel_params = (self.default_kernel_params + + ' iommu.passthrough=on') + self.run_and_check(self.F33_FILENAME, self.F33_HSUM) + + def test_smmu_ril_nostrict(self): + self.common_vm_setup(self.ASSET_KERNEL_F33, self.ASSET_INITRD_F33, + self.ASSET_DISK_F33) + self.kernel_params = (self.default_kernel_params + + ' iommu.strict=0') + self.run_and_check(self.F33_FILENAME, self.F33_HSUM) + + +if __name__ == '__main__': + LinuxKernelTest.main() From f8c54844175922d77faa8b586f451e379d53a191 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:28 +0200 Subject: [PATCH 0068/2760] gitlab-ci: Update QEMU_JOB_AVOCADO and QEMU_CI_AVOCADO_TESTING MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we don't run the Avocado jobs in the CI anymore, rename these variables to QEMU_JOB_FUNCTIONAL and QEMU_CI_FUNCTIONAL. Also, there was a mismatch between the documentation and the implementation of QEMU_CI_AVOCADO_TESTING: While the documentation said that you had to "Set this variable to have the tests using the Avocado framework run automatically", you indeed needed to set it to make the pipelines appear in your dashboard - but they were never run automatically in forks and had to be triggered manually. Let's improve this now: No need to hide these pipelines from the users by default anymore (the functional tests should be stable enough nowadays), and rather allow the users to run the pipelines auto- matically with this switch now instead, as was documented. Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-15-thuth@redhat.com> Signed-off-by: Thomas Huth --- .gitlab-ci.d/base.yml | 8 ++------ .gitlab-ci.d/buildtest-template.yml | 2 +- docs/devel/testing/ci-jobs.rst.inc | 19 +++++++++---------- 3 files changed, 12 insertions(+), 17 deletions(-) diff --git a/.gitlab-ci.d/base.yml b/.gitlab-ci.d/base.yml index 25b88aaa06..60a24a9d14 100644 --- a/.gitlab-ci.d/base.yml +++ b/.gitlab-ci.d/base.yml @@ -69,10 +69,6 @@ variables: - if: '$QEMU_CI != "1" && $QEMU_CI != "2" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM' when: never - # Avocado jobs don't run in forks unless $QEMU_CI_AVOCADO_TESTING is set - - if: '$QEMU_JOB_AVOCADO && $QEMU_CI_AVOCADO_TESTING != "1" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM' - when: never - ############################################################# # Stage 2: fine tune execution of jobs in specific scenarios @@ -101,8 +97,8 @@ variables: when: manual allow_failure: true - # Avocado jobs can be manually start in forks if $QEMU_CI_AVOCADO_TESTING is unset - - if: '$QEMU_JOB_AVOCADO && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM' + # Functional jobs can be manually started in forks + - if: '$QEMU_JOB_FUNCTIONAL && $QEMU_CI_FUNCTIONAL != "1" && $CI_PROJECT_NAMESPACE != $QEMU_CI_UPSTREAM' when: manual allow_failure: true diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 13fa4f4a4f..d4f145fdb5 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -114,4 +114,4 @@ - cd build - du -chs ${CI_PROJECT_DIR}/*-cache variables: - QEMU_JOB_AVOCADO: 1 + QEMU_JOB_FUNCTIONAL: 1 diff --git a/docs/devel/testing/ci-jobs.rst.inc b/docs/devel/testing/ci-jobs.rst.inc index 3756bbe355..f1c541cc25 100644 --- a/docs/devel/testing/ci-jobs.rst.inc +++ b/docs/devel/testing/ci-jobs.rst.inc @@ -126,10 +126,10 @@ QEMU_JOB_PUBLISH The job is for publishing content after a branch has been merged into the upstream default branch. -QEMU_JOB_AVOCADO -~~~~~~~~~~~~~~~~ +QEMU_JOB_FUNCTIONAL +~~~~~~~~~~~~~~~~~~~ -The job runs the Avocado integration test suite +The job runs the functional test suite Contributor controlled runtime variables ---------------------------------------- @@ -149,13 +149,12 @@ the jobs to be manually started from the UI Set this variable to 2 to create the pipelines and run all the jobs immediately, as was the historical behaviour -QEMU_CI_AVOCADO_TESTING -~~~~~~~~~~~~~~~~~~~~~~~ -By default, tests using the Avocado framework are not run automatically in -the pipelines (because multiple artifacts have to be downloaded, and if -these artifacts are not already cached, downloading them make the jobs -reach the timeout limit). Set this variable to have the tests using the -Avocado framework run automatically. +QEMU_CI_FUNCTIONAL +~~~~~~~~~~~~~~~~~~ +By default, tests using the functional framework are not run automatically +in the pipelines (because multiple artifacts have to be downloaded, which +might cause a lot of network traffic). Set this variable to have the tests +using the functional framework run automatically. Other misc variables -------------------- From 5748e464150911127d07c0b7adeea474fd905149 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 14 Mar 2025 09:59:57 +0100 Subject: [PATCH 0069/2760] docs/devel/testing: Dissolve the ci-definitions.rst.inc file This file was meant for defining the vocabulary for our testing efforts, but it did not age well: First, the definitions are not only about the CI part, but also about testing in general, so most of the information should rather reside in main.rst instead. Second, some vocabulary has never been really adopted by the QEMU project, for example we never really use the word "system testing" since "system" rather means the system emulator binaries in the QEMU project (and we also don't do any testing with other components like libvirt and virt-managers here). It also defines that the qtests are the "functional" tests in QEMU, which is not really up to date anymore after the "tests/functional" framework has been introduced a couple of months ago (FWIW, the qtests could rather be seen as a mix between unit testing and functional testing). To solve this problem, move the useful parts of this file into main.rst and directly into ci.rst, and drop the ones (like "system testing") that we don't really need anymore. Message-ID: <20250314085959.1585568-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- docs/devel/testing/ci-definitions.rst.inc | 121 ---------------------- docs/devel/testing/ci.rst | 28 ++++- docs/devel/testing/main.rst | 65 ++++++++++-- 3 files changed, 81 insertions(+), 133 deletions(-) delete mode 100644 docs/devel/testing/ci-definitions.rst.inc diff --git a/docs/devel/testing/ci-definitions.rst.inc b/docs/devel/testing/ci-definitions.rst.inc deleted file mode 100644 index 6d5c6fd9f2..0000000000 --- a/docs/devel/testing/ci-definitions.rst.inc +++ /dev/null @@ -1,121 +0,0 @@ -Definition of terms -=================== - -This section defines the terms used in this document and correlates them with -what is currently used on QEMU. - -Automated tests ---------------- - -An automated test is written on a test framework using its generic test -functions/classes. The test framework can run the tests and report their -success or failure [1]_. - -An automated test has essentially three parts: - -1. The test initialization of the parameters, where the expected parameters, - like inputs and expected results, are set up; -2. The call to the code that should be tested; -3. An assertion, comparing the result from the previous call with the expected - result set during the initialization of the parameters. If the result - matches the expected result, the test has been successful; otherwise, it has - failed. - -Unit testing ------------- - -A unit test is responsible for exercising individual software components as a -unit, like interfaces, data structures, and functionality, uncovering errors -within the boundaries of a component. The verification effort is in the -smallest software unit and focuses on the internal processing logic and data -structures. A test case of unit tests should be designed to uncover errors due -to erroneous computations, incorrect comparisons, or improper control flow [2]_. - -On QEMU, unit testing is represented by the 'check-unit' target from 'make'. - -Functional testing ------------------- - -A functional test focuses on the functional requirement of the software. -Deriving sets of input conditions, the functional tests should fully exercise -all the functional requirements for a program. Functional testing is -complementary to other testing techniques, attempting to find errors like -incorrect or missing functions, interface errors, behavior errors, and -initialization and termination errors [3]_. - -On QEMU, functional testing is represented by the 'check-qtest' target from -'make'. - -System testing --------------- - -System tests ensure all application elements mesh properly while the overall -functionality and performance are achieved [4]_. Some or all system components -are integrated to create a complete system to be tested as a whole. System -testing ensures that components are compatible, interact correctly, and -transfer the right data at the right time across their interfaces. As system -testing focuses on interactions, use case-based testing is a practical approach -to system testing [5]_. Note that, in some cases, system testing may require -interaction with third-party software, like operating system images, databases, -networks, and so on. - -On QEMU, system testing is represented by the 'check-avocado' target from -'make'. - -Flaky tests ------------ - -A flaky test is defined as a test that exhibits both a passing and a failing -result with the same code on different runs. Some usual reasons for an -intermittent/flaky test are async wait, concurrency, and test order dependency -[6]_. - -Gating ------- - -A gate restricts the move of code from one stage to another on a -test/deployment pipeline. The step move is granted with approval. The approval -can be a manual intervention or a set of tests succeeding [7]_. - -On QEMU, the gating process happens during the pull request. The approval is -done by the project leader running its own set of tests. The pull request gets -merged when the tests succeed. - -Continuous Integration (CI) ---------------------------- - -Continuous integration (CI) requires the builds of the entire application and -the execution of a comprehensive set of automated tests every time there is a -need to commit any set of changes [8]_. The automated tests can be composed of -the unit, functional, system, and other tests. - -Keynotes about continuous integration (CI) [9]_: - -1. System tests may depend on external software (operating system images, - firmware, database, network). -2. It may take a long time to build and test. It may be impractical to build - the system being developed several times per day. -3. If the development platform is different from the target platform, it may - not be possible to run system tests in the developer’s private workspace. - There may be differences in hardware, operating system, or installed - software. Therefore, more time is required for testing the system. - -References ----------- - -.. [1] Sommerville, Ian (2016). Software Engineering. p. 233. -.. [2] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering, - A Practitioner’s Approach. p. 48, 376, 378, 381. -.. [3] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering, - A Practitioner’s Approach. p. 388. -.. [4] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering, - A Practitioner’s Approach. Software Engineering, p. 377. -.. [5] Sommerville, Ian (2016). Software Engineering. p. 59, 232, 240. -.. [6] Luo, Qingzhou, et al. An empirical analysis of flaky tests. - Proceedings of the 22nd ACM SIGSOFT International Symposium on - Foundations of Software Engineering. 2014. -.. [7] Humble, Jez & Farley, David (2010). Continuous Delivery: - Reliable Software Releases Through Build, Test, and Deployment, p. 122. -.. [8] Humble, Jez & Farley, David (2010). Continuous Delivery: - Reliable Software Releases Through Build, Test, and Deployment, p. 55. -.. [9] Sommerville, Ian (2016). Software Engineering. p. 743. diff --git a/docs/devel/testing/ci.rst b/docs/devel/testing/ci.rst index ed88a2010b..e21d39db57 100644 --- a/docs/devel/testing/ci.rst +++ b/docs/devel/testing/ci.rst @@ -1,14 +1,34 @@ .. _ci: -== -CI -== +Continuous Integration (CI) +=========================== + +Continuous integration (CI) requires the builds of the entire application and +the execution of a comprehensive set of automated tests every time there is a +need to commit any set of changes [1]_. The automated tests are composed +of unit, functional and other tests. Most of QEMU's CI is run on GitLab's infrastructure although a number of other CI services are used for specialised purposes. The most up to date information about them and their status can be found on the `project wiki testing page `_. -.. include:: ci-definitions.rst.inc +These tests are also used as gating tests before merging pull requests. +A gating test restricts the move of code from one stage to another on a +test/deployment pipeline. The step move is granted with approval. The approval +can be a manual intervention or a set of tests succeeding [2]_. + +On QEMU, the gating process happens during the pull request. The approval is +done by the project leader running its own set of tests. The pull request gets +merged when the tests succeed. + .. include:: ci-jobs.rst.inc .. include:: ci-runners.rst.inc + +References +---------- + +.. [1] Humble, Jez & Farley, David (2010). Continuous Delivery: + Reliable Software Releases Through Build, Test, and Deployment, p. 55. +.. [2] Humble, Jez & Farley, David (2010). Continuous Delivery: + Reliable Software Releases Through Build, Test, and Deployment, p. 122. diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst index 9869bcf034..e56da22edf 100644 --- a/docs/devel/testing/main.rst +++ b/docs/devel/testing/main.rst @@ -5,19 +5,32 @@ Testing in QEMU QEMU's testing infrastructure is fairly complex as it covers everything from unit testing and exercising specific sub-systems all -the way to full blown acceptance tests. To get an overview of the +the way to full blown functional tests. To get an overview of the tests you can run ``make check-help`` from either the source or build tree. -Most (but not all) tests are also integrated into the meson build -system so can be run directly from the build tree, for example: - -.. code:: +Most (but not all) tests are also integrated as an automated test into +the meson build system so can be run directly from the build tree, +for example:: [./pyvenv/bin/]meson test --suite qemu:softfloat will run just the softfloat tests. +An automated test is written with one of the test frameworks using its +generic test functions/classes. The test framework can run the tests and +report their success or failure [1]_. + +An automated test has essentially three parts: + +1. The test initialization of the parameters, where the expected parameters, + like inputs and expected results, are set up; +2. The call to the code that should be tested; +3. An assertion, comparing the result from the previous call with the expected + result set during the initialization of the parameters. If the result + matches the expected result, the test has been successful; otherwise, it has + failed. + The rest of this document will cover the details for specific test groups. @@ -44,9 +57,17 @@ cannot find them. Unit tests ~~~~~~~~~~ -Unit tests, which can be invoked with ``make check-unit``, are simple C tests -that typically link to individual QEMU object files and exercise them by -calling exported functions. +A unit test is responsible for exercising individual software components as a +unit, like interfaces, data structures, and functionality, uncovering errors +within the boundaries of a component. The verification effort is in the +smallest software unit and focuses on the internal processing logic and data +structures. A test case of unit tests should be designed to uncover errors +due to erroneous computations, incorrect comparisons, or improper control +flow [2]_. + +In QEMU, unit tests can be invoked with ``make check-unit``. They are +simple C tests that typically link to individual QEMU object files and +exercise them by calling exported functions. If you are writing new code in QEMU, consider adding a unit test, especially for utility modules that are relatively stateless or have few dependencies. To @@ -885,6 +906,10 @@ changing the ``-c`` option. Functional tests using Python ----------------------------- +A functional test focuses on the functional requirement of the software, +attempting to find errors like incorrect functions, interface errors, +behavior errors, and initialization and termination errors [3]_. + The ``tests/functional`` directory hosts functional tests written in Python. You can run the functional tests simply by executing: @@ -1023,3 +1048,27 @@ coverage-html`` which will create Further analysis can be conducted by running the ``gcov`` command directly on the various .gcda output files. Please read the ``gcov`` documentation for more information. + +Flaky tests +----------- + +A flaky test is defined as a test that exhibits both a passing and a failing +result with the same code on different runs. Some usual reasons for an +intermittent/flaky test are async wait, concurrency, and test order dependency +[4]_. + +In QEMU, tests that are identified to be flaky are normally disabled by +default. Set the QEMU_TEST_FLAKY_TESTS environment variable before running +the tests to enable them. + +References +---------- + +.. [1] Sommerville, Ian (2016). Software Engineering. p. 233. +.. [2] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering, + A Practitioner’s Approach. p. 48, 376, 378, 381. +.. [3] Pressman, Roger S. & Maxim, Bruce R. (2020). Software Engineering, + A Practitioner’s Approach. p. 388. +.. [4] Luo, Qingzhou, et al. An empirical analysis of flaky tests. + Proceedings of the 22nd ACM SIGSOFT International Symposium on + Foundations of Software Engineering. 2014. From 52e9ed6d3ac44424e098333772077a41bb88c4db Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 13:30:29 +0200 Subject: [PATCH 0070/2760] Remove the remainders of the Avocado tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all Avocado tests have been converted to or been replaced by other functional tests, we can delete the remainders of the Avocado tests from the QEMU source tree. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-ID: <20250414113031.151105-16-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 8 +- configure | 2 +- docs/about/build-platforms.rst | 10 +- docs/devel/build-system.rst | 11 +- docs/devel/codebase.rst | 5 - docs/devel/testing/avocado.rst | 581 ------------------------ docs/devel/testing/functional.rst | 3 - docs/devel/testing/index.rst | 1 - docs/devel/testing/main.rst | 15 - pythondeps.toml | 8 +- tests/Makefile.include | 60 +-- tests/avocado/README.rst | 10 - tests/avocado/avocado_qemu/__init__.py | 424 ----------------- tests/avocado/avocado_qemu/linuxtest.py | 253 ----------- 14 files changed, 16 insertions(+), 1375 deletions(-) delete mode 100644 docs/devel/testing/avocado.rst delete mode 100644 tests/avocado/README.rst delete mode 100644 tests/avocado/avocado_qemu/__init__.py delete mode 100644 tests/avocado/avocado_qemu/linuxtest.py diff --git a/MAINTAINERS b/MAINTAINERS index 07f77c048e..5fd757c5dd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2073,7 +2073,7 @@ S: Supported F: hw/acpi/viot.c F: hw/acpi/viot.h -ACPI/AVOCADO/BIOSBITS +ACPI/FUNCTIONAL/BIOSBITS M: Ani Sinha M: Michael S. Tsirkin S: Supported @@ -4246,12 +4246,6 @@ R: Philippe Mathieu-Daudé S: Maintained F: tests/tcg/Makefile.target -Integration Testing with the Avocado framework -W: https://trello.com/b/6Qi1pxVn/avocado-qemu -R: Cleber Rosa -S: Odd Fixes -F: tests/avocado/ - GitLab custom runner (Works On Arm Sponsored) M: Alex Bennée M: Philippe Mathieu-Daudé diff --git a/configure b/configure index 02f1dd2311..000309cf61 100755 --- a/configure +++ b/configure @@ -1685,7 +1685,7 @@ LINKS="$LINKS pc-bios/optionrom/Makefile" LINKS="$LINKS pc-bios/s390-ccw/Makefile" LINKS="$LINKS pc-bios/vof/Makefile" LINKS="$LINKS .gdbinit scripts" # scripts needed by relative path in .gdbinit -LINKS="$LINKS tests/avocado tests/data" +LINKS="$LINKS tests/data" LINKS="$LINKS tests/qemu-iotests/check tests/qemu-iotests/Makefile" LINKS="$LINKS python" for f in $LINKS ; do diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index 1552b1a704..52521552c8 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -123,11 +123,11 @@ Rust build dependencies to build QEMU. Optional build dependencies - Build components whose absence does not affect the ability to build - QEMU may not be available in distros, or may be too old for QEMU's - requirements. Many of these, such as the Avocado testing framework - or various linters, are written in Python and therefore can also - be installed using ``pip``. Cross compilers are another example + Build components whose absence does not affect the ability to build QEMU + may not be available in distros, or may be too old for our requirements. + Many of these, such as additional modules for the functional testing + framework or various linters, are written in Python and therefore can + also be installed using ``pip``. Cross compilers are another example of optional build-time dependency; in this case it is possible to download them from repositories such as EPEL, to use container-based cross compilation using ``docker`` or ``podman``, or to use pre-built diff --git a/docs/devel/build-system.rst b/docs/devel/build-system.rst index a759982f45..258cfad3fe 100644 --- a/docs/devel/build-system.rst +++ b/docs/devel/build-system.rst @@ -134,7 +134,7 @@ in how the build process runs Python code. At this stage, ``configure`` also queries the chosen Python interpreter about QEMU's build dependencies. Note that the build process does *not* -look for ``meson``, ``sphinx-build`` or ``avocado`` binaries in the PATH; +look for ``meson`` or ``sphinx-build`` binaries in the PATH; likewise, there are no options such as ``--meson`` or ``--sphinx-build``. This avoids a potential mismatch, where Meson and Sphinx binaries on the PATH might operate in a different Python environment than the one chosen @@ -151,7 +151,7 @@ virtual environment with ``pip``, either from wheels in ``python/wheels`` or by downloading the package with PyPI. Downloading can be disabled with ``--disable-download``; and anyway, it only happens when a ``configure`` option (currently, only ``--enable-docs``) is explicitly enabled but -the dependencies are not present\ [#pip]_. +the dependencies are not present. .. [#distlib] The scripts are created based on the package's metadata, specifically the ``console_script`` entry points. This is the @@ -164,10 +164,6 @@ the dependencies are not present\ [#pip]_. because the Python Packaging Authority provides a package ``distlib.scripts`` to perform this task. -.. [#pip] ``pip`` might also be used when running ``make check-avocado`` - if downloading is enabled, to ensure that Avocado is - available. - The required versions of the packages are stored in a configuration file ``pythondeps.toml``. The format is custom to QEMU, but it is documented at the top of the file itself and it should be easy to understand. The @@ -497,8 +493,7 @@ number of dynamically created files listed later. ``pyvenv/bin``, and calling ``pip`` to install dependencies. ``tests/Makefile.include`` - Rules for external test harnesses. These include the TCG tests - and the Avocado-based integration tests. + Rules for external test harnesses like the TCG tests. ``tests/docker/Makefile.include`` Rules for Docker tests. Like ``tests/Makefile.include``, this file is diff --git a/docs/devel/codebase.rst b/docs/devel/codebase.rst index ef98578296..40273e7d31 100644 --- a/docs/devel/codebase.rst +++ b/docs/devel/codebase.rst @@ -175,11 +175,6 @@ yet, so sometimes the source code is all you have. * `tests `_: QEMU `test ` suite - - `avocado `_: - Functional tests booting full VM using `Avocado framework `. - Those tests will be transformed and moved into - `tests/functional `_ - in the future. - `data `_: Data for various tests. - `decode `_: diff --git a/docs/devel/testing/avocado.rst b/docs/devel/testing/avocado.rst deleted file mode 100644 index eda76fe2db..0000000000 --- a/docs/devel/testing/avocado.rst +++ /dev/null @@ -1,581 +0,0 @@ -.. _checkavocado-ref: - - -Integration testing with Avocado -================================ - -The ``tests/avocado`` directory hosts integration tests. They're usually -higher level tests, and may interact with external resources and with -various guest operating systems. - -These tests are written using the Avocado Testing Framework (which must be -installed separately) in conjunction with a the ``avocado_qemu.QemuSystemTest`` -class, implemented at ``tests/avocado/avocado_qemu``. - -Tests based on ``avocado_qemu.QemuSystemTest`` can easily: - - * Customize the command line arguments given to the convenience - ``self.vm`` attribute (a QEMUMachine instance) - - * Interact with the QEMU monitor, send QMP commands and check - their results - - * Interact with the guest OS, using the convenience console device - (which may be useful to assert the effectiveness and correctness of - command line arguments or QMP commands) - - * Interact with external data files that accompany the test itself - (see ``self.get_data()``) - - * Download (and cache) remote data files, such as firmware and kernel - images - - * Have access to a library of guest OS images (by means of the - ``avocado.utils.vmimage`` library) - - * Make use of various other test related utilities available at the - test class itself and at the utility library: - - - http://avocado-framework.readthedocs.io/en/latest/api/test/avocado.html#avocado.Test - - http://avocado-framework.readthedocs.io/en/latest/api/utils/avocado.utils.html - -Running tests -------------- - -You can run the avocado tests simply by executing: - -.. code:: - - make check-avocado - -This involves the automatic installation, from PyPI, of all the -necessary avocado-framework dependencies into the QEMU venv within the -build tree (at ``./pyvenv``). Test results are also saved within the -build tree (at ``tests/results``). - -Note: the build environment must be using a Python 3 stack, and have -the ``venv`` and ``pip`` packages installed. If necessary, make sure -``configure`` is called with ``--python=`` and that those modules are -available. On Debian and Ubuntu based systems, depending on the -specific version, they may be on packages named ``python3-venv`` and -``python3-pip``. - -It is also possible to run tests based on tags using the -``make check-avocado`` command and the ``AVOCADO_TAGS`` environment -variable: - -.. code:: - - make check-avocado AVOCADO_TAGS=quick - -Note that tags separated with commas have an AND behavior, while tags -separated by spaces have an OR behavior. For more information on Avocado -tags, see: - - https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/tags.html - -To run a single test file, a couple of them, or a test within a file -using the ``make check-avocado`` command, set the ``AVOCADO_TESTS`` -environment variable with the test files or test names. To run all -tests from a single file, use: - - .. code:: - - make check-avocado AVOCADO_TESTS=$FILEPATH - -The same is valid to run tests from multiple test files: - - .. code:: - - make check-avocado AVOCADO_TESTS='$FILEPATH1 $FILEPATH2' - -To run a single test within a file, use: - - .. code:: - - make check-avocado AVOCADO_TESTS=$FILEPATH:$TESTCLASS.$TESTNAME - -The same is valid to run single tests from multiple test files: - - .. code:: - - make check-avocado AVOCADO_TESTS='$FILEPATH1:$TESTCLASS1.$TESTNAME1 $FILEPATH2:$TESTCLASS2.$TESTNAME2' - -The scripts installed inside the virtual environment may be used -without an "activation". For instance, the Avocado test runner -may be invoked by running: - - .. code:: - - pyvenv/bin/avocado run $OPTION1 $OPTION2 tests/avocado/ - -Note that if ``make check-avocado`` was not executed before, it is -possible to create the Python virtual environment with the dependencies -needed running: - - .. code:: - - make check-venv - -It is also possible to run tests from a single file or a single test within -a test file. To run tests from a single file within the build tree, use: - - .. code:: - - pyvenv/bin/avocado run tests/avocado/$TESTFILE - -To run a single test within a test file, use: - - .. code:: - - pyvenv/bin/avocado run tests/avocado/$TESTFILE:$TESTCLASS.$TESTNAME - -Valid test names are visible in the output from any previous execution -of Avocado or ``make check-avocado``, and can also be queried using: - - .. code:: - - pyvenv/bin/avocado list tests/avocado - -Manual Installation -------------------- - -To manually install Avocado and its dependencies, run: - -.. code:: - - pip install --user avocado-framework - -Alternatively, follow the instructions on this link: - - https://avocado-framework.readthedocs.io/en/latest/guides/user/chapters/installing.html - -Overview --------- - -The ``tests/avocado/avocado_qemu`` directory provides the -``avocado_qemu`` Python module, containing the ``avocado_qemu.QemuSystemTest`` -class. Here's a simple usage example: - -.. code:: - - from avocado_qemu import QemuSystemTest - - - class Version(QemuSystemTest): - """ - :avocado: tags=quick - """ - def test_qmp_human_info_version(self): - self.vm.launch() - res = self.vm.cmd('human-monitor-command', - command_line='info version') - self.assertRegex(res, r'^(\d+\.\d+\.\d)') - -To execute your test, run: - -.. code:: - - avocado run version.py - -Tests may be classified according to a convention by using docstring -directives such as ``:avocado: tags=TAG1,TAG2``. To run all tests -in the current directory, tagged as "quick", run: - -.. code:: - - avocado run -t quick . - -The ``avocado_qemu.QemuSystemTest`` base test class -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``avocado_qemu.QemuSystemTest`` class has a number of characteristics -that are worth being mentioned right away. - -First of all, it attempts to give each test a ready to use QEMUMachine -instance, available at ``self.vm``. Because many tests will tweak the -QEMU command line, launching the QEMUMachine (by using ``self.vm.launch()``) -is left to the test writer. - -The base test class has also support for tests with more than one -QEMUMachine. The way to get machines is through the ``self.get_vm()`` -method which will return a QEMUMachine instance. The ``self.get_vm()`` -method accepts arguments that will be passed to the QEMUMachine creation -and also an optional ``name`` attribute so you can identify a specific -machine and get it more than once through the tests methods. A simple -and hypothetical example follows: - -.. code:: - - from avocado_qemu import QemuSystemTest - - - class MultipleMachines(QemuSystemTest): - def test_multiple_machines(self): - first_machine = self.get_vm() - second_machine = self.get_vm() - self.get_vm(name='third_machine').launch() - - first_machine.launch() - second_machine.launch() - - first_res = first_machine.cmd( - 'human-monitor-command', - command_line='info version') - - second_res = second_machine.cmd( - 'human-monitor-command', - command_line='info version') - - third_res = self.get_vm(name='third_machine').cmd( - 'human-monitor-command', - command_line='info version') - - self.assertEqual(first_res, second_res, third_res) - -At test "tear down", ``avocado_qemu.QemuSystemTest`` handles all the -QEMUMachines shutdown. - -The ``avocado_qemu.LinuxTest`` base test class -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The ``avocado_qemu.LinuxTest`` is further specialization of the -``avocado_qemu.QemuSystemTest`` class, so it contains all the characteristics -of the later plus some extra features. - -First of all, this base class is intended for tests that need to -interact with a fully booted and operational Linux guest. At this -time, it uses a Fedora 31 guest image. The most basic example looks -like this: - -.. code:: - - from avocado_qemu import LinuxTest - - - class SomeTest(LinuxTest): - - def test(self): - self.launch_and_wait() - self.ssh_command('some_command_to_be_run_in_the_guest') - -Please refer to tests that use ``avocado_qemu.LinuxTest`` under -``tests/avocado`` for more examples. - -QEMUMachine ------------ - -The QEMUMachine API is already widely used in the Python iotests, -device-crash-test and other Python scripts. It's a wrapper around the -execution of a QEMU binary, giving its users: - - * the ability to set command line arguments to be given to the QEMU - binary - - * a ready to use QMP connection and interface, which can be used to - send commands and inspect its results, as well as asynchronous - events - - * convenience methods to set commonly used command line arguments in - a more succinct and intuitive way - -QEMU binary selection -^^^^^^^^^^^^^^^^^^^^^ - -The QEMU binary used for the ``self.vm`` QEMUMachine instance will -primarily depend on the value of the ``qemu_bin`` parameter. If it's -not explicitly set, its default value will be the result of a dynamic -probe in the same source tree. A suitable binary will be one that -targets the architecture matching host machine. - -Based on this description, test writers will usually rely on one of -the following approaches: - -1) Set ``qemu_bin``, and use the given binary - -2) Do not set ``qemu_bin``, and use a QEMU binary named like - "qemu-system-${arch}", either in the current - working directory, or in the current source tree. - -The resulting ``qemu_bin`` value will be preserved in the -``avocado_qemu.QemuSystemTest`` as an attribute with the same name. - -Attribute reference -------------------- - -Test -^^^^ - -Besides the attributes and methods that are part of the base -``avocado.Test`` class, the following attributes are available on any -``avocado_qemu.QemuSystemTest`` instance. - -vm -"" - -A QEMUMachine instance, initially configured according to the given -``qemu_bin`` parameter. - -arch -"""" - -The architecture can be used on different levels of the stack, e.g. by -the framework or by the test itself. At the framework level, it will -currently influence the selection of a QEMU binary (when one is not -explicitly given). - -Tests are also free to use this attribute value, for their own needs. -A test may, for instance, use the same value when selecting the -architecture of a kernel or disk image to boot a VM with. - -The ``arch`` attribute will be set to the test parameter of the same -name. If one is not given explicitly, it will either be set to -``None``, or, if the test is tagged with one (and only one) -``:avocado: tags=arch:VALUE`` tag, it will be set to ``VALUE``. - -cpu -""" - -The cpu model that will be set to all QEMUMachine instances created -by the test. - -The ``cpu`` attribute will be set to the test parameter of the same -name. If one is not given explicitly, it will either be set to -``None ``, or, if the test is tagged with one (and only one) -``:avocado: tags=cpu:VALUE`` tag, it will be set to ``VALUE``. - -machine -""""""" - -The machine type that will be set to all QEMUMachine instances created -by the test. - -The ``machine`` attribute will be set to the test parameter of the same -name. If one is not given explicitly, it will either be set to -``None``, or, if the test is tagged with one (and only one) -``:avocado: tags=machine:VALUE`` tag, it will be set to ``VALUE``. - -qemu_bin -"""""""" - -The preserved value of the ``qemu_bin`` parameter or the result of the -dynamic probe for a QEMU binary in the current working directory or -source tree. - -LinuxTest -^^^^^^^^^ - -Besides the attributes present on the ``avocado_qemu.QemuSystemTest`` base -class, the ``avocado_qemu.LinuxTest`` adds the following attributes: - -distro -"""""" - -The name of the Linux distribution used as the guest image for the -test. The name should match the **Provider** column on the list -of images supported by the avocado.utils.vmimage library: - -https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images - -distro_version -"""""""""""""" - -The version of the Linux distribution as the guest image for the -test. The name should match the **Version** column on the list -of images supported by the avocado.utils.vmimage library: - -https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images - -distro_checksum -""""""""""""""" - -The sha256 hash of the guest image file used for the test. - -If this value is not set in the code or by a test parameter (with the -same name), no validation on the integrity of the image will be -performed. - -Parameter reference -------------------- - -To understand how Avocado parameters are accessed by tests, and how -they can be passed to tests, please refer to:: - - https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#accessing-test-parameters - -Parameter values can be easily seen in the log files, and will look -like the following: - -.. code:: - - PARAMS (key=qemu_bin, path=*, default=./qemu-system-x86_64) => './qemu-system-x86_64 - -Test -^^^^ - -arch -"""" - -The architecture that will influence the selection of a QEMU binary -(when one is not explicitly given). - -Tests are also free to use this parameter value, for their own needs. -A test may, for instance, use the same value when selecting the -architecture of a kernel or disk image to boot a VM with. - -This parameter has a direct relation with the ``arch`` attribute. If -not given, it will default to None. - -cpu -""" - -The cpu model that will be set to all QEMUMachine instances created -by the test. - -machine -""""""" - -The machine type that will be set to all QEMUMachine instances created -by the test. - -qemu_bin -"""""""" - -The exact QEMU binary to be used on QEMUMachine. - -LinuxTest -^^^^^^^^^ - -Besides the parameters present on the ``avocado_qemu.QemuSystemTest`` base -class, the ``avocado_qemu.LinuxTest`` adds the following parameters: - -distro -"""""" - -The name of the Linux distribution used as the guest image for the -test. The name should match the **Provider** column on the list -of images supported by the avocado.utils.vmimage library: - -https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images - -distro_version -"""""""""""""" - -The version of the Linux distribution as the guest image for the -test. The name should match the **Version** column on the list -of images supported by the avocado.utils.vmimage library: - -https://avocado-framework.readthedocs.io/en/latest/guides/writer/libs/vmimage.html#supported-images - -distro_checksum -""""""""""""""" - -The sha256 hash of the guest image file used for the test. - -If this value is not set in the code or by this parameter no -validation on the integrity of the image will be performed. - -Skipping tests --------------- - -The Avocado framework provides Python decorators which allow for easily skip -tests running under certain conditions. For example, on the lack of a binary -on the test system or when the running environment is a CI system. For further -information about those decorators, please refer to:: - - https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#skipping-tests - -While the conditions for skipping tests are often specifics of each one, there -are recurring scenarios identified by the QEMU developers and the use of -environment variables became a kind of standard way to enable/disable tests. - -Here is a list of the most used variables: - -AVOCADO_ALLOW_LARGE_STORAGE -^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Tests which are going to fetch or produce assets considered *large* are not -going to run unless that ``AVOCADO_ALLOW_LARGE_STORAGE=1`` is exported on -the environment. - -The definition of *large* is a bit arbitrary here, but it usually means an -asset which occupies at least 1GB of size on disk when uncompressed. - -SPEED -^^^^^ -Tests which have a long runtime will not be run unless ``SPEED=slow`` is -exported on the environment. - -The definition of *long* is a bit arbitrary here, and it depends on the -usefulness of the test too. A unique test is worth spending more time on, -small variations on existing tests perhaps less so. As a rough guide, -a test or set of similar tests which take more than 100 seconds to -complete. - -AVOCADO_ALLOW_UNTRUSTED_CODE -^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -There are tests which will boot a kernel image or firmware that can be -considered not safe to run on the developer's workstation, thus they are -skipped by default. The definition of *not safe* is also arbitrary but -usually it means a blob which either its source or build process aren't -public available. - -You should export ``AVOCADO_ALLOW_UNTRUSTED_CODE=1`` on the environment in -order to allow tests which make use of those kind of assets. - -AVOCADO_TIMEOUT_EXPECTED -^^^^^^^^^^^^^^^^^^^^^^^^ -The Avocado framework has a timeout mechanism which interrupts tests to avoid the -test suite of getting stuck. The timeout value can be set via test parameter or -property defined in the test class, for further details:: - - https://avocado-framework.readthedocs.io/en/latest/guides/writer/chapters/writing.html#setting-a-test-timeout - -Even though the timeout can be set by the test developer, there are some tests -that may not have a well-defined limit of time to finish under certain -conditions. For example, tests that take longer to execute when QEMU is -compiled with debug flags. Therefore, the ``AVOCADO_TIMEOUT_EXPECTED`` variable -has been used to determine whether those tests should run or not. - -QEMU_TEST_FLAKY_TESTS -^^^^^^^^^^^^^^^^^^^^^ -Some tests are not working reliably and thus are disabled by default. -This includes tests that don't run reliably on GitLab's CI which -usually expose real issues that are rarely seen on developer machines -due to the constraints of the CI environment. If you encounter a -similar situation then raise a bug and then mark the test as shown on -the code snippet below: - -.. code:: - - # See https://gitlab.com/qemu-project/qemu/-/issues/nnnn - @skipUnless(os.getenv('QEMU_TEST_FLAKY_TESTS'), 'Test is unstable on GitLab') - def test(self): - do_something() - -You can also add ``:avocado: tags=flaky`` to the test meta-data so -only the flaky tests can be run as a group: - -.. code:: - - env QEMU_TEST_FLAKY_TESTS=1 ./pyvenv/bin/avocado \ - run tests/avocado -filter-by-tags=flaky - -Tests should not live in this state forever and should either be fixed -or eventually removed. - - -Uninstalling Avocado --------------------- - -If you've followed the manual installation instructions above, you can -easily uninstall Avocado. Start by listing the packages you have -installed:: - - pip list --user - -And remove any package you want with:: - - pip uninstall - -If you've used ``make check-avocado``, the Python virtual environment where -Avocado is installed will be cleaned up as part of ``make check-clean``. diff --git a/docs/devel/testing/functional.rst b/docs/devel/testing/functional.rst index 9bc973392a..8030cb4299 100644 --- a/docs/devel/testing/functional.rst +++ b/docs/devel/testing/functional.rst @@ -6,9 +6,6 @@ Functional testing with Python The ``tests/functional`` directory hosts functional tests written in Python. They are usually higher level tests, and may interact with external resources and with various guest operating systems. -The functional tests have initially evolved from the Avocado tests, so there -is a lot of similarity to those tests here (see :ref:`checkavocado-ref` for -details about the Avocado tests). The tests should be written in the style of the Python `unittest`_ framework, using stdio for the TAP protocol. The folder ``tests/functional/qemu_test`` diff --git a/docs/devel/testing/index.rst b/docs/devel/testing/index.rst index 1171f7db8f..ccc2fc6cbc 100644 --- a/docs/devel/testing/index.rst +++ b/docs/devel/testing/index.rst @@ -10,7 +10,6 @@ testing infrastructure. main qtest functional - avocado acpi-bits ci fuzzing diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst index e56da22edf..6b18ed875c 100644 --- a/docs/devel/testing/main.rst +++ b/docs/devel/testing/main.rst @@ -919,21 +919,6 @@ Python. You can run the functional tests simply by executing: See :ref:`checkfunctional-ref` for more details. -Integration tests using the Avocado Framework ---------------------------------------------- - -The ``tests/avocado`` directory hosts integration tests. They're usually -higher level tests, and may interact with external resources and with -various guest operating systems. - -You can run the avocado tests simply by executing: - -.. code:: - - make check-avocado - -See :ref:`checkavocado-ref` for more details. - .. _checktcg-ref: Testing with "make check-tcg" diff --git a/pythondeps.toml b/pythondeps.toml index c03c9df81b..7eaaa0fed1 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -27,9 +27,5 @@ pycotap = { accepted = ">=1.1.0", installed = "1.3.1" } sphinx = { accepted = ">=3.4.3", installed = "5.3.0", canary = "sphinx-build" } sphinx_rtd_theme = { accepted = ">=0.5", installed = "1.1.1" } -[avocado] -# Note that qemu.git/python/ is always implicitly installed. -# Prefer an LTS version when updating the accepted versions of -# avocado-framework, for example right now the limit is 92.x. -avocado-framework = { accepted = "(>=103.0, <104.0)", installed = "103.0", canary = "avocado" } -pycdlib = { accepted = ">=1.11.0" } +[testdeps] +qemu.qmp = { accepted = ">=0.0.3", installed = "0.0.3" } diff --git a/tests/Makefile.include b/tests/Makefile.include index 010369bd3a..23fb722d42 100644 --- a/tests/Makefile.include +++ b/tests/Makefile.include @@ -18,7 +18,6 @@ ifneq ($(filter $(all-check-targets), check-softfloat),) @echo " $(MAKE) check-tcg Run TCG tests" @echo " $(MAKE) check-softfloat Run FPU emulation tests" endif - @echo " $(MAKE) check-avocado Run avocado (integration) tests for currently configured targets" @echo @echo " $(MAKE) check-report.junit.xml Generates an aggregated XML test report" @echo " $(MAKE) check-venv Creates a Python venv for tests" @@ -26,7 +25,6 @@ endif @echo @echo "The following are useful for CI builds" @echo " $(MAKE) check-build Build most test binaries" - @echo " $(MAKE) get-vm-images Downloads all images used by avocado tests, according to configured targets (~350 MB each, 1.5 GB max)" @echo @echo @echo "The variable SPEED can be set to control the gtester speed setting." @@ -86,26 +84,12 @@ distclean-tcg: $(DISTCLEAN_TCG_TARGET_RULES) # Python venv for running tests -.PHONY: check-venv check-avocado check-acceptance check-acceptance-deprecated-warning +.PHONY: check-venv # Build up our target list from the filtered list of ninja targets TARGETS=$(patsubst libqemu-%.a, %, $(filter libqemu-%.a, $(ninja-targets))) TESTS_VENV_TOKEN=$(BUILD_DIR)/pyvenv/tests.group -TESTS_RESULTS_DIR=$(BUILD_DIR)/tests/results -ifndef AVOCADO_TESTS - AVOCADO_TESTS=tests/avocado -endif -# Controls the output generated by Avocado when running tests. -# Any number of command separated loggers are accepted. For more -# information please refer to "avocado --help". -AVOCADO_SHOW?=app -ifndef AVOCADO_TAGS - AVOCADO_CMDLINE_TAGS=$(patsubst %-softmmu,-t arch:%, \ - $(filter %-softmmu,$(TARGETS))) -else - AVOCADO_CMDLINE_TAGS=$(addprefix -t , $(AVOCADO_TAGS)) -endif quiet-venv-pip = $(quiet-@)$(call quiet-command-run, \ $(PYTHON) -m pip -q --disable-pip-version-check $1, \ @@ -113,47 +97,11 @@ quiet-venv-pip = $(quiet-@)$(call quiet-command-run, \ $(TESTS_VENV_TOKEN): $(SRC_PATH)/pythondeps.toml $(call quiet-venv-pip,install -e "$(SRC_PATH)/python/") - $(MKVENV_ENSUREGROUP) $< avocado + $(MKVENV_ENSUREGROUP) $< testdeps $(call quiet-command, touch $@) -$(TESTS_RESULTS_DIR): - $(call quiet-command, mkdir -p $@, \ - MKDIR, $@) - check-venv: $(TESTS_VENV_TOKEN) -FEDORA_31_ARCHES_TARGETS=$(patsubst %-softmmu,%, $(filter %-softmmu,$(TARGETS))) -FEDORA_31_ARCHES_CANDIDATES=$(patsubst ppc64,ppc64le,$(FEDORA_31_ARCHES_TARGETS)) -FEDORA_31_ARCHES := x86_64 aarch64 ppc64le s390x -FEDORA_31_DOWNLOAD=$(filter $(FEDORA_31_ARCHES),$(FEDORA_31_ARCHES_CANDIDATES)) - -# download one specific Fedora 31 image -get-vm-image-fedora-31-%: check-venv - $(call quiet-command, \ - $(PYTHON) -m avocado vmimage get \ - --distro=fedora --distro-version=31 --arch=$*, \ - "AVOCADO", "Downloading avocado tests VM image for $*") - -# download all vm images, according to defined targets -get-vm-images: check-venv $(patsubst %,get-vm-image-fedora-31-%, $(FEDORA_31_DOWNLOAD)) - -check-avocado: check-venv $(TESTS_RESULTS_DIR) get-vm-images - $(call quiet-command, \ - $(PYTHON) -m avocado \ - --show=$(AVOCADO_SHOW) run --job-results-dir=$(TESTS_RESULTS_DIR) \ - $(if $(AVOCADO_TAGS),, --filter-by-tags-include-empty \ - --filter-by-tags-include-empty-key) \ - $(AVOCADO_CMDLINE_TAGS) --max-parallel-tasks=1 \ - $(if $(GITLAB_CI),,--failfast) $(AVOCADO_TESTS), \ - "AVOCADO", "tests/avocado") - -check-acceptance-deprecated-warning: - @echo - @echo "Note '$(MAKE) check-acceptance' is deprecated, use '$(MAKE) check-avocado' instead." - @echo - -check-acceptance: check-acceptance-deprecated-warning | check-avocado - FUNCTIONAL_TARGETS=$(patsubst %-softmmu,check-functional-%, $(filter %-softmmu,$(TARGETS))) .PHONY: $(FUNCTIONAL_TARGETS) $(FUNCTIONAL_TARGETS): @@ -166,13 +114,13 @@ check-functional: # Consolidated targets -.PHONY: check check-clean get-vm-images +.PHONY: check check-clean check: check-build: run-ninja check-clean: - rm -rf $(TESTS_RESULTS_DIR) + rm -rf $(BUILD_DIR)/tests/functional clean: check-clean clean-tcg distclean: distclean-tcg diff --git a/tests/avocado/README.rst b/tests/avocado/README.rst deleted file mode 100644 index 94488371bb..0000000000 --- a/tests/avocado/README.rst +++ /dev/null @@ -1,10 +0,0 @@ -============================================= -Integration tests using the Avocado Framework -============================================= - -This directory contains integration tests. They're usually higher -level, and may interact with external resources and with various -guest operating systems. - -For more information, please refer to ``docs/devel/testing.rst``, -section "Integration tests using the Avocado Framework". diff --git a/tests/avocado/avocado_qemu/__init__.py b/tests/avocado/avocado_qemu/__init__.py deleted file mode 100644 index 93c3460242..0000000000 --- a/tests/avocado/avocado_qemu/__init__.py +++ /dev/null @@ -1,424 +0,0 @@ -# Test class and utilities for functional tests -# -# Copyright (c) 2018 Red Hat, Inc. -# -# Author: -# Cleber Rosa -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import logging -import os -import subprocess -import sys -import tempfile -import time -import uuid - -import avocado -from avocado.utils import ssh -from avocado.utils.path import find_command - -from qemu.machine import QEMUMachine -from qemu.utils import (get_info_usernet_hostfwd_port, kvm_available, - tcg_available) - - -#: The QEMU build root directory. It may also be the source directory -#: if building from the source dir, but it's safer to use BUILD_DIR for -#: that purpose. Be aware that if this code is moved outside of a source -#: and build tree, it will not be accurate. -BUILD_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(__file__)))) - - -def has_cmd(name, args=None): - """ - This function is for use in a @avocado.skipUnless decorator, e.g.: - - @skipUnless(*has_cmd('sudo -n', ('sudo', '-n', 'true'))) - def test_something_that_needs_sudo(self): - ... - """ - - if args is None: - args = ('which', name) - - try: - _, stderr, exitcode = run_cmd(args) - except Exception as e: - exitcode = -1 - stderr = str(e) - - if exitcode != 0: - cmd_line = ' '.join(args) - err = f'{name} required, but "{cmd_line}" failed: {stderr.strip()}' - return (False, err) - else: - return (True, '') - -def has_cmds(*cmds): - """ - This function is for use in a @avocado.skipUnless decorator and - allows checking for the availability of multiple commands, e.g.: - - @skipUnless(*has_cmds(('cmd1', ('cmd1', '--some-parameter')), - 'cmd2', 'cmd3')) - def test_something_that_needs_cmd1_and_cmd2(self): - ... - """ - - for cmd in cmds: - if isinstance(cmd, str): - cmd = (cmd,) - - ok, errstr = has_cmd(*cmd) - if not ok: - return (False, errstr) - - return (True, '') - -def run_cmd(args): - subp = subprocess.Popen(args, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - universal_newlines=True) - stdout, stderr = subp.communicate() - ret = subp.returncode - - return (stdout, stderr, ret) - -def is_readable_executable_file(path): - return os.path.isfile(path) and os.access(path, os.R_OK | os.X_OK) - - -def pick_default_qemu_bin(bin_prefix='qemu-system-', arch=None): - """ - Picks the path of a QEMU binary, starting either in the current working - directory or in the source tree root directory. - - :param arch: the arch to use when looking for a QEMU binary (the target - will match the arch given). If None (the default), arch - will be the current host system arch (as given by - :func:`os.uname`). - :type arch: str - :returns: the path to the default QEMU binary or None if one could not - be found - :rtype: str or None - """ - if arch is None: - arch = os.uname()[4] - # qemu binary path does not match arch for powerpc, handle it - if 'ppc64le' in arch: - arch = 'ppc64' - qemu_bin_name = bin_prefix + arch - qemu_bin_paths = [ - os.path.join(".", qemu_bin_name), - os.path.join(BUILD_DIR, qemu_bin_name), - os.path.join(BUILD_DIR, "build", qemu_bin_name), - ] - for path in qemu_bin_paths: - if is_readable_executable_file(path): - return path - return None - - -def _console_interaction(test, success_message, failure_message, - send_string, keep_sending=False, vm=None): - assert not keep_sending or send_string - if vm is None: - vm = test.vm - console = vm.console_file - console_logger = logging.getLogger('console') - while True: - if send_string: - vm.console_socket.sendall(send_string.encode()) - if not keep_sending: - send_string = None # send only once - - # Only consume console output if waiting for something - if success_message is None and failure_message is None: - if send_string is None: - break - continue - - try: - msg = console.readline().decode().strip() - except UnicodeDecodeError: - msg = None - if not msg: - continue - console_logger.debug(msg) - if success_message is None or success_message in msg: - break - if failure_message and failure_message in msg: - console.close() - fail = 'Failure message found in console: "%s". Expected: "%s"' % \ - (failure_message, success_message) - test.fail(fail) - -def interrupt_interactive_console_until_pattern(test, success_message, - failure_message=None, - interrupt_string='\r'): - """ - Keep sending a string to interrupt a console prompt, while logging the - console output. Typical use case is to break a boot loader prompt, such: - - Press a key within 5 seconds to interrupt boot process. - 5 - 4 - 3 - 2 - 1 - Booting default image... - - :param test: an Avocado test containing a VM that will have its console - read and probed for a success or failure message - :type test: :class:`avocado_qemu.QemuSystemTest` - :param success_message: if this message appears, test succeeds - :param failure_message: if this message appears, test fails - :param interrupt_string: a string to send to the console before trying - to read a new line - """ - _console_interaction(test, success_message, failure_message, - interrupt_string, True) - -def wait_for_console_pattern(test, success_message, failure_message=None, - vm=None): - """ - Waits for messages to appear on the console, while logging the content - - :param test: an Avocado test containing a VM that will have its console - read and probed for a success or failure message - :type test: :class:`avocado_qemu.QemuSystemTest` - :param success_message: if this message appears, test succeeds - :param failure_message: if this message appears, test fails - """ - _console_interaction(test, success_message, failure_message, None, vm=vm) - -def exec_command(test, command): - """ - Send a command to a console (appending CRLF characters), while logging - the content. - - :param test: an Avocado test containing a VM. - :type test: :class:`avocado_qemu.QemuSystemTest` - :param command: the command to send - :type command: str - """ - _console_interaction(test, None, None, command + '\r') - -def exec_command_and_wait_for_pattern(test, command, - success_message, failure_message=None): - """ - Send a command to a console (appending CRLF characters), then wait - for success_message to appear on the console, while logging the. - content. Mark the test as failed if failure_message is found instead. - - :param test: an Avocado test containing a VM that will have its console - read and probed for a success or failure message - :type test: :class:`avocado_qemu.QemuSystemTest` - :param command: the command to send - :param success_message: if this message appears, test succeeds - :param failure_message: if this message appears, test fails - """ - _console_interaction(test, success_message, failure_message, command + '\r') - -class QemuBaseTest(avocado.Test): - - # default timeout for all tests, can be overridden - timeout = 120 - - def _get_unique_tag_val(self, tag_name): - """ - Gets a tag value, if unique for a key - """ - vals = self.tags.get(tag_name, []) - if len(vals) == 1: - return vals.pop() - return None - - def setUp(self, bin_prefix): - self.arch = self.params.get('arch', - default=self._get_unique_tag_val('arch')) - - self.cpu = self.params.get('cpu', - default=self._get_unique_tag_val('cpu')) - - default_qemu_bin = pick_default_qemu_bin(bin_prefix, arch=self.arch) - self.qemu_bin = self.params.get('qemu_bin', - default=default_qemu_bin) - if self.qemu_bin is None: - self.cancel("No QEMU binary defined or found in the build tree") - - def fetch_asset(self, name, - asset_hash, algorithm=None, - locations=None, expire=None, - find_only=False, cancel_on_missing=True): - return super().fetch_asset(name, - asset_hash=asset_hash, - algorithm=algorithm, - locations=locations, - expire=expire, - find_only=find_only, - cancel_on_missing=cancel_on_missing) - - -class QemuSystemTest(QemuBaseTest): - """Facilitates system emulation tests.""" - - def setUp(self): - self._vms = {} - - super().setUp('qemu-system-') - - accel_required = self._get_unique_tag_val('accel') - if accel_required: - self.require_accelerator(accel_required) - - self.machine = self.params.get('machine', - default=self._get_unique_tag_val('machine')) - - def require_accelerator(self, accelerator): - """ - Requires an accelerator to be available for the test to continue - - It takes into account the currently set qemu binary. - - If the check fails, the test is canceled. If the check itself - for the given accelerator is not available, the test is also - canceled. - - :param accelerator: name of the accelerator, such as "kvm" or "tcg" - :type accelerator: str - """ - checker = {'tcg': tcg_available, - 'kvm': kvm_available}.get(accelerator) - if checker is None: - self.cancel("Don't know how to check for the presence " - "of accelerator %s" % accelerator) - if not checker(qemu_bin=self.qemu_bin): - self.cancel("%s accelerator does not seem to be " - "available" % accelerator) - - def require_netdev(self, netdevname): - netdevhelp = run_cmd([self.qemu_bin, - '-M', 'none', '-netdev', 'help'])[0]; - if netdevhelp.find('\n' + netdevname + '\n') < 0: - self.cancel('no support for user networking') - - def _new_vm(self, name, *args): - self._sd = tempfile.TemporaryDirectory(prefix="qemu_") - vm = QEMUMachine(self.qemu_bin, base_temp_dir=self.workdir, - log_dir=self.logdir) - self.log.debug('QEMUMachine "%s" created', name) - self.log.debug('QEMUMachine "%s" temp_dir: %s', name, vm.temp_dir) - self.log.debug('QEMUMachine "%s" log_dir: %s', name, vm.log_dir) - if args: - vm.add_args(*args) - return vm - - def get_qemu_img(self): - self.log.debug('Looking for and selecting a qemu-img binary') - - # If qemu-img has been built, use it, otherwise the system wide one - # will be used. - qemu_img = os.path.join(BUILD_DIR, 'qemu-img') - if not os.path.exists(qemu_img): - qemu_img = find_command('qemu-img', False) - if qemu_img is False: - self.cancel('Could not find "qemu-img"') - - return qemu_img - - @property - def vm(self): - return self.get_vm(name='default') - - def get_vm(self, *args, name=None): - if not name: - name = str(uuid.uuid4()) - if self._vms.get(name) is None: - self._vms[name] = self._new_vm(name, *args) - if self.cpu is not None: - self._vms[name].add_args('-cpu', self.cpu) - if self.machine is not None: - self._vms[name].set_machine(self.machine) - return self._vms[name] - - def set_vm_arg(self, arg, value): - """ - Set an argument to list of extra arguments to be given to the QEMU - binary. If the argument already exists then its value is replaced. - - :param arg: the QEMU argument, such as "-cpu" in "-cpu host" - :type arg: str - :param value: the argument value, such as "host" in "-cpu host" - :type value: str - """ - if not arg or not value: - return - if arg not in self.vm.args: - self.vm.args.extend([arg, value]) - else: - idx = self.vm.args.index(arg) + 1 - if idx < len(self.vm.args): - self.vm.args[idx] = value - else: - self.vm.args.append(value) - - def tearDown(self): - for vm in self._vms.values(): - vm.shutdown() - self._sd = None - super().tearDown() - - -class LinuxSSHMixIn: - """Contains utility methods for interacting with a guest via SSH.""" - - def ssh_connect(self, username, credential, credential_is_key=True): - self.ssh_logger = logging.getLogger('ssh') - res = self.vm.cmd('human-monitor-command', - command_line='info usernet') - port = get_info_usernet_hostfwd_port(res) - self.assertIsNotNone(port) - self.assertGreater(port, 0) - self.log.debug('sshd listening on port: %d', port) - if credential_is_key: - self.ssh_session = ssh.Session('127.0.0.1', port=port, - user=username, key=credential) - else: - self.ssh_session = ssh.Session('127.0.0.1', port=port, - user=username, password=credential) - for i in range(10): - try: - self.ssh_session.connect() - return - except: - time.sleep(i) - self.fail('ssh connection timeout') - - def ssh_command(self, command): - self.ssh_logger.info(command) - result = self.ssh_session.cmd(command) - stdout_lines = [line.rstrip() for line - in result.stdout_text.splitlines()] - for line in stdout_lines: - self.ssh_logger.info(line) - stderr_lines = [line.rstrip() for line - in result.stderr_text.splitlines()] - for line in stderr_lines: - self.ssh_logger.warning(line) - - self.assertEqual(result.exit_status, 0, - f'Guest command failed: {command}') - return stdout_lines, stderr_lines - - def ssh_command_output_contains(self, cmd, exp): - stdout, _ = self.ssh_command(cmd) - for line in stdout: - if exp in line: - break - else: - self.fail('"%s" output does not contain "%s"' % (cmd, exp)) diff --git a/tests/avocado/avocado_qemu/linuxtest.py b/tests/avocado/avocado_qemu/linuxtest.py deleted file mode 100644 index 66fb9f1507..0000000000 --- a/tests/avocado/avocado_qemu/linuxtest.py +++ /dev/null @@ -1,253 +0,0 @@ -# Test class and utilities for functional Linux-based tests -# -# Copyright (c) 2018 Red Hat, Inc. -# -# Author: -# Cleber Rosa -# -# This work is licensed under the terms of the GNU GPL, version 2 or -# later. See the COPYING file in the top-level directory. - -import os -import shutil - -from avocado.utils import cloudinit, datadrainer, process, vmimage - -from avocado_qemu import LinuxSSHMixIn -from avocado_qemu import QemuSystemTest - -if os.path.islink(os.path.dirname(os.path.dirname(__file__))): - # The link to the avocado tests dir in the source code directory - lnk = os.path.dirname(os.path.dirname(__file__)) - #: The QEMU root source directory - SOURCE_DIR = os.path.dirname(os.path.dirname(os.readlink(lnk))) -else: - SOURCE_DIR = BUILD_DIR - -class LinuxDistro: - """Represents a Linux distribution - - Holds information of known distros. - """ - #: A collection of known distros and their respective image checksum - KNOWN_DISTROS = { - 'fedora': { - '31': { - 'x86_64': - {'checksum': ('e3c1b309d9203604922d6e255c2c5d09' - '8a309c2d46215d8fc026954f3c5c27a0'), - 'pxeboot_url': ('https://archives.fedoraproject.org/' - 'pub/archive/fedora/linux/releases/31/' - 'Everything/x86_64/os/images/pxeboot/'), - 'kernel_params': ('root=UUID=b1438b9b-2cab-4065-a99a-' - '08a96687f73c ro no_timer_check ' - 'net.ifnames=0 console=tty1 ' - 'console=ttyS0,115200n8'), - }, - 'aarch64': - {'checksum': ('1e18d9c0cf734940c4b5d5ec592facae' - 'd2af0ad0329383d5639c997fdf16fe49'), - 'pxeboot_url': 'https://archives.fedoraproject.org/' - 'pub/archive/fedora/linux/releases/31/' - 'Everything/aarch64/os/images/pxeboot/', - 'kernel_params': ('root=UUID=b6950a44-9f3c-4076-a9c2-' - '355e8475b0a7 ro earlyprintk=pl011,0x9000000' - ' ignore_loglevel no_timer_check' - ' printk.time=1 rd_NO_PLYMOUTH' - ' console=ttyAMA0'), - }, - 'ppc64': - {'checksum': ('7c3528b85a3df4b2306e892199a9e1e4' - '3f991c506f2cc390dc4efa2026ad2f58')}, - 's390x': - {'checksum': ('4caaab5a434fd4d1079149a072fdc789' - '1e354f834d355069ca982fdcaf5a122d')}, - }, - '32': { - 'aarch64': - {'checksum': ('b367755c664a2d7a26955bbfff985855' - 'adfa2ca15e908baf15b4b176d68d3967'), - 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/' - 'releases/32/Server/aarch64/os/images/' - 'pxeboot/'), - 'kernel_params': ('root=UUID=3df75b65-be8d-4db4-8655-' - '14d95c0e90c5 ro no_timer_check net.ifnames=0' - ' console=tty1 console=ttyS0,115200n8'), - }, - }, - '33': { - 'aarch64': - {'checksum': ('e7f75cdfd523fe5ac2ca9eeece68edc1' - 'a81f386a17f969c1d1c7c87031008a6b'), - 'pxeboot_url': ('http://dl.fedoraproject.org/pub/fedora/linux/' - 'releases/33/Server/aarch64/os/images/' - 'pxeboot/'), - 'kernel_params': ('root=UUID=d20b3ffa-6397-4a63-a734-' - '1126a0208f8a ro no_timer_check net.ifnames=0' - ' console=tty1 console=ttyS0,115200n8' - ' console=tty0'), - }, - }, - } - } - - def __init__(self, name, version, arch): - self.name = name - self.version = version - self.arch = arch - try: - info = self.KNOWN_DISTROS.get(name).get(version).get(arch) - except AttributeError: - # Unknown distro - info = None - self._info = info or {} - - @property - def checksum(self): - """Gets the cloud-image file checksum""" - return self._info.get('checksum', None) - - @checksum.setter - def checksum(self, value): - self._info['checksum'] = value - - @property - def pxeboot_url(self): - """Gets the repository url where pxeboot files can be found""" - return self._info.get('pxeboot_url', None) - - @property - def default_kernel_params(self): - """Gets the default kernel parameters""" - return self._info.get('kernel_params', None) - - -class LinuxTest(LinuxSSHMixIn, QemuSystemTest): - """Facilitates having a cloud-image Linux based available. - - For tests that intend to interact with guests, this is a better choice - to start with than the more vanilla `QemuSystemTest` class. - """ - - distro = None - username = 'root' - password = 'password' - smp = '2' - memory = '1024' - - def _set_distro(self): - distro_name = self.params.get( - 'distro', - default=self._get_unique_tag_val('distro')) - if not distro_name: - distro_name = 'fedora' - - distro_version = self.params.get( - 'distro_version', - default=self._get_unique_tag_val('distro_version')) - if not distro_version: - distro_version = '31' - - self.distro = LinuxDistro(distro_name, distro_version, self.arch) - - # The distro checksum behaves differently than distro name and - # version. First, it does not respect a tag with the same - # name, given that it's not expected to be used for filtering - # (distro name versions are the natural choice). Second, the - # order of precedence is: parameter, attribute and then value - # from KNOWN_DISTROS. - distro_checksum = self.params.get('distro_checksum', - default=None) - if distro_checksum: - self.distro.checksum = distro_checksum - - def setUp(self, ssh_pubkey=None, network_device_type='virtio-net'): - super().setUp() - self.require_netdev('user') - self._set_distro() - self.vm.add_args('-smp', self.smp) - self.vm.add_args('-m', self.memory) - # The following network device allows for SSH connections - self.vm.add_args('-netdev', 'user,id=vnet,hostfwd=:127.0.0.1:0-:22', - '-device', '%s,netdev=vnet' % network_device_type) - self.set_up_boot() - if ssh_pubkey is None: - ssh_pubkey, self.ssh_key = self.set_up_existing_ssh_keys() - self.set_up_cloudinit(ssh_pubkey) - - def set_up_existing_ssh_keys(self): - ssh_public_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa.pub') - source_private_key = os.path.join(SOURCE_DIR, 'tests', 'keys', 'id_rsa') - ssh_dir = os.path.join(self.workdir, '.ssh') - os.mkdir(ssh_dir, mode=0o700) - ssh_private_key = os.path.join(ssh_dir, - os.path.basename(source_private_key)) - shutil.copyfile(source_private_key, ssh_private_key) - os.chmod(ssh_private_key, 0o600) - return (ssh_public_key, ssh_private_key) - - def download_boot(self): - # Set the qemu-img binary. - # If none is available, the test will cancel. - vmimage.QEMU_IMG = super().get_qemu_img() - - self.log.info('Downloading/preparing boot image') - # Fedora 31 only provides ppc64le images - image_arch = self.arch - if self.distro.name == 'fedora': - if image_arch == 'ppc64': - image_arch = 'ppc64le' - - try: - boot = vmimage.get( - self.distro.name, arch=image_arch, version=self.distro.version, - checksum=self.distro.checksum, - algorithm='sha256', - cache_dir=self.cache_dirs[0], - snapshot_dir=self.workdir) - except: - self.cancel('Failed to download/prepare boot image') - return boot.path - - def prepare_cloudinit(self, ssh_pubkey=None): - self.log.info('Preparing cloudinit image') - try: - cloudinit_iso = os.path.join(self.workdir, 'cloudinit.iso') - pubkey_content = None - if ssh_pubkey: - with open(ssh_pubkey) as pubkey: - pubkey_content = pubkey.read() - cloudinit.iso(cloudinit_iso, self.name, - username=self.username, - password=self.password, - # QEMU's hard coded usermode router address - phone_home_host='10.0.2.2', - phone_home_port=self.phone_server.server_port, - authorized_key=pubkey_content) - except Exception: - self.cancel('Failed to prepare the cloudinit image') - return cloudinit_iso - - def set_up_boot(self): - path = self.download_boot() - self.vm.add_args('-drive', 'file=%s' % path) - - def set_up_cloudinit(self, ssh_pubkey=None): - self.phone_server = cloudinit.PhoneHomeServer(('0.0.0.0', 0), - self.name) - cloudinit_iso = self.prepare_cloudinit(ssh_pubkey) - self.vm.add_args('-drive', 'file=%s,format=raw' % cloudinit_iso) - - def launch_and_wait(self, set_up_ssh_connection=True): - self.vm.set_console() - self.vm.launch() - console_drainer = datadrainer.LineLogger(self.vm.console_socket.fileno(), - logger=self.log.getChild('console')) - console_drainer.start() - self.log.info('VM launched, waiting for boot confirmation from guest') - while not self.phone_server.instance_phoned_back: - self.phone_server.handle_request() - - if set_up_ssh_connection: - self.log.info('Setting up the SSH connection') - self.ssh_connect(self.username, self.ssh_key) From 858640eaee9f3039580118f5629825825cea311a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 27 Mar 2025 21:13:05 +0100 Subject: [PATCH 0071/2760] tests/functional: Remove semicolons at the end of lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Yes, we are all C coders who try to write Python code for testing... but still, let's better avoid semicolons at the end of the lines to keep "pylint" happy! Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Nina Schoetterl-Glausch Reviewed-by: Cédric Le Goater Message-ID: <20250327201305.996241-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/aspeed.py | 2 +- tests/functional/test_aarch64_aspeed.py | 2 +- tests/functional/test_arm_aspeed_ast2500.py | 6 ++-- tests/functional/test_arm_aspeed_ast2600.py | 36 +++++++++---------- tests/functional/test_arm_aspeed_bletchley.py | 4 +-- tests/functional/test_arm_aspeed_palmetto.py | 4 +-- tests/functional/test_arm_aspeed_romulus.py | 4 +-- .../functional/test_arm_aspeed_witherspoon.py | 4 +-- tests/functional/test_arm_bpim2u.py | 2 +- tests/functional/test_arm_cubieboard.py | 2 +- tests/functional/test_arm_orangepi.py | 2 +- tests/functional/test_s390x_topology.py | 12 +++---- 12 files changed, 40 insertions(+), 40 deletions(-) diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py index 77dc8930fa..7a40d5dda7 100644 --- a/tests/functional/aspeed.py +++ b/tests/functional/aspeed.py @@ -44,7 +44,7 @@ def do_test_arm_aspeed_buildroot_start(self, image, cpu_id, pattern='Aspeed EVB' def do_test_arm_aspeed_buildroot_poweroff(self): exec_command_and_wait_for_pattern(self, 'poweroff', - 'System halted'); + 'System halted') def do_test_arm_aspeed_sdk_start(self, image): self.require_netdev('user') diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index c25c966278..c7f3b3b319 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -85,7 +85,7 @@ def start_ast2700_test(self, name): exec_command_and_wait_for_pattern(self, 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-1/device/new_device ', - 'i2c i2c-1: new_device: Instantiated device lm75 at 0x4d'); + 'i2c i2c-1: new_device: Instantiated device lm75 at 0x4d') exec_command_and_wait_for_pattern(self, 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '0') self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/test_arm_aspeed_ast2500.py index 1ffba6c995..ddc6459f71 100755 --- a/tests/functional/test_arm_aspeed_ast2500.py +++ b/tests/functional/test_arm_aspeed_ast2500.py @@ -22,17 +22,17 @@ def test_arm_ast2500_evb_buildroot(self): image_path = self.ASSET_BR2_202411_AST2500_FLASH.fetch() self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); + 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test') self.do_test_arm_aspeed_buildroot_start(image_path, '0x0', 'ast2500-evb login:') exec_command_and_wait_for_pattern(self, 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', - 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d'); + 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d') exec_command_and_wait_for_pattern(self, 'cat /sys/class/hwmon/hwmon1/temp1_input', '0') self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000); + property='temperature', value=18000) exec_command_and_wait_for_pattern(self, 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000') diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/test_arm_aspeed_ast2600.py index 6ae4ed636a..5ef52f0659 100755 --- a/tests/functional/test_arm_aspeed_ast2600.py +++ b/tests/functional/test_arm_aspeed_ast2600.py @@ -27,38 +27,38 @@ def test_arm_ast2600_evb_buildroot(self): image_path = self.ASSET_BR2_202411_AST2600_FLASH.fetch() self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test'); + 'tmp105,bus=aspeed.i2c.bus.3,address=0x4d,id=tmp-test') self.vm.add_args('-device', - 'ds1338,bus=aspeed.i2c.bus.3,address=0x32'); + 'ds1338,bus=aspeed.i2c.bus.3,address=0x32') self.vm.add_args('-device', - 'i2c-echo,bus=aspeed.i2c.bus.3,address=0x42'); + 'i2c-echo,bus=aspeed.i2c.bus.3,address=0x42') self.do_test_arm_aspeed_buildroot_start(image_path, '0xf00', 'ast2600-evb login:') exec_command_and_wait_for_pattern(self, 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-3/device/new_device', - 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d'); + 'i2c i2c-3: new_device: Instantiated device lm75 at 0x4d') exec_command_and_wait_for_pattern(self, 'cat /sys/class/hwmon/hwmon1/temp1_input', '0') self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000); + property='temperature', value=18000) exec_command_and_wait_for_pattern(self, 'cat /sys/class/hwmon/hwmon1/temp1_input', '18000') exec_command_and_wait_for_pattern(self, 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-3/device/new_device', - 'i2c i2c-3: new_device: Instantiated device ds1307 at 0x32'); + 'i2c i2c-3: new_device: Instantiated device ds1307 at 0x32') year = time.strftime("%Y") - exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year); + exec_command_and_wait_for_pattern(self, 'hwclock -f /dev/rtc1', year) exec_command_and_wait_for_pattern(self, 'echo slave-24c02 0x1064 > /sys/bus/i2c/devices/i2c-3/new_device', - 'i2c i2c-3: new_device: Instantiated device slave-24c02 at 0x64'); + 'i2c i2c-3: new_device: Instantiated device slave-24c02 at 0x64') exec_command_and_wait_for_pattern(self, - 'i2cset -y 3 0x42 0x64 0x00 0xaa i', '#'); + 'i2cset -y 3 0x42 0x64 0x00 0xaa i', '#') exec_command_and_wait_for_pattern(self, 'hexdump /sys/bus/i2c/devices/3-1064/slave-eeprom', - '0000000 ffaa ffff ffff ffff ffff ffff ffff ffff'); + '0000000 ffaa ffff ffff ffff ffff ffff ffff ffff') self.do_test_arm_aspeed_buildroot_poweroff() ASSET_BR2_202302_AST2600_TPM_FLASH = Asset( @@ -90,10 +90,10 @@ def test_arm_ast2600_evb_buildroot_tpm(self): exec_command_and_wait_for_pattern(self, 'echo tpm_tis_i2c 0x2e > /sys/bus/i2c/devices/i2c-12/new_device', - 'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)'); + 'tpm_tis_i2c 12-002e: 2.0 TPM (device-id 0x1, rev-id 1)') exec_command_and_wait_for_pattern(self, 'cat /sys/class/tpm/tpm0/pcr-sha256/0', - 'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0'); + 'B804724EA13F52A9072BA87FE8FDCC497DFC9DF9AA15B9088694639C431688E0') self.do_test_arm_aspeed_buildroot_poweroff() @@ -107,9 +107,9 @@ def test_arm_ast2600_evb_sdk(self): self.archive_extract(self.ASSET_SDK_V806_AST2600_A2) self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test'); + 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test') self.vm.add_args('-device', - 'ds1338,bus=aspeed.i2c.bus.5,address=0x32'); + 'ds1338,bus=aspeed.i2c.bus.5,address=0x32') self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2600-a2", "image-bmc")) @@ -120,20 +120,20 @@ def test_arm_ast2600_evb_sdk(self): exec_command_and_wait_for_pattern(self, 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device', - 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d'); + 'i2c i2c-5: new_device: Instantiated device lm75 at 0x4d') exec_command_and_wait_for_pattern(self, 'cat /sys/class/hwmon/hwmon19/temp1_input', '0') self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000); + property='temperature', value=18000) exec_command_and_wait_for_pattern(self, 'cat /sys/class/hwmon/hwmon19/temp1_input', '18000') exec_command_and_wait_for_pattern(self, 'echo ds1307 0x32 > /sys/class/i2c-dev/i2c-5/device/new_device', - 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32'); + 'i2c i2c-5: new_device: Instantiated device ds1307 at 0x32') year = time.strftime("%Y") exec_command_and_wait_for_pattern(self, - '/sbin/hwclock -f /dev/rtc1', year); + '/sbin/hwclock -f /dev/rtc1', year) if __name__ == '__main__': AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_bletchley.py b/tests/functional/test_arm_aspeed_bletchley.py index 0da856c5ed..5a60b24b3d 100644 --- a/tests/functional/test_arm_aspeed_bletchley.py +++ b/tests/functional/test_arm_aspeed_bletchley.py @@ -12,14 +12,14 @@ class BletchleyMachine(AspeedTest): ASSET_BLETCHLEY_FLASH = Asset( 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/bletchley-bmc/openbmc-20250128071329/obmc-phosphor-image-bletchley-20250128071329.static.mtd.xz', - 'db21d04d47d7bb2a276f59d308614b4dfb70b9c7c81facbbca40a3977a2d8844'); + 'db21d04d47d7bb2a276f59d308614b4dfb70b9c7c81facbbca40a3977a2d8844') def test_arm_ast2600_bletchley_openbmc(self): image_path = self.uncompress(self.ASSET_BLETCHLEY_FLASH) self.do_test_arm_aspeed_openbmc('bletchley-bmc', image=image_path, uboot='2019.04', cpu_id='0xf00', - soc='AST2600 rev A3'); + soc='AST2600 rev A3') if __name__ == '__main__': AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_palmetto.py b/tests/functional/test_arm_aspeed_palmetto.py index 35d832bc98..ff0b821be6 100755 --- a/tests/functional/test_arm_aspeed_palmetto.py +++ b/tests/functional/test_arm_aspeed_palmetto.py @@ -12,14 +12,14 @@ class PalmettoMachine(AspeedTest): ASSET_PALMETTO_FLASH = Asset( 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/palmetto-bmc/openbmc-20250128071432/obmc-phosphor-image-palmetto-20250128071432.static.mtd', - 'bce7c392eec75c707a91cfc8fad7ca9a69d7e4f10df936930d65c1cb9897ac81'); + 'bce7c392eec75c707a91cfc8fad7ca9a69d7e4f10df936930d65c1cb9897ac81') def test_arm_ast2400_palmetto_openbmc(self): image_path = self.ASSET_PALMETTO_FLASH.fetch() self.do_test_arm_aspeed_openbmc('palmetto-bmc', image=image_path, uboot='2019.04', cpu_id='0x0', - soc='AST2400 rev A1'); + soc='AST2400 rev A1') if __name__ == '__main__': AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_romulus.py b/tests/functional/test_arm_aspeed_romulus.py index b97ed951b1..0447212bbf 100755 --- a/tests/functional/test_arm_aspeed_romulus.py +++ b/tests/functional/test_arm_aspeed_romulus.py @@ -12,14 +12,14 @@ class RomulusMachine(AspeedTest): ASSET_ROMULUS_FLASH = Asset( 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/romulus-bmc/openbmc-20250128071340/obmc-phosphor-image-romulus-20250128071340.static.mtd', - '6d031376440c82ed9d087d25e9fa76aea75b42f80daa252ec402c0bc3cf6cf5b'); + '6d031376440c82ed9d087d25e9fa76aea75b42f80daa252ec402c0bc3cf6cf5b') def test_arm_ast2500_romulus_openbmc(self): image_path = self.ASSET_ROMULUS_FLASH.fetch() self.do_test_arm_aspeed_openbmc('romulus-bmc', image=image_path, uboot='2019.04', cpu_id='0x0', - soc='AST2500 rev A1'); + soc='AST2500 rev A1') if __name__ == '__main__': AspeedTest.main() diff --git a/tests/functional/test_arm_aspeed_witherspoon.py b/tests/functional/test_arm_aspeed_witherspoon.py index ea1ce89b05..51a2d47af2 100644 --- a/tests/functional/test_arm_aspeed_witherspoon.py +++ b/tests/functional/test_arm_aspeed_witherspoon.py @@ -12,14 +12,14 @@ class WitherspoonMachine(AspeedTest): ASSET_WITHERSPOON_FLASH = Asset( 'https://github.com/legoater/qemu-aspeed-boot/raw/master/images/witherspoon-bmc/openbmc-20240618035022/obmc-phosphor-image-witherspoon-20240618035022.ubi.mtd', - '937d9ed449ea6c6cbed983519088a42d0cafe276bcfe4fce07772ca6673f9213'); + '937d9ed449ea6c6cbed983519088a42d0cafe276bcfe4fce07772ca6673f9213') def test_arm_ast2500_witherspoon_openbmc(self): image_path = self.ASSET_WITHERSPOON_FLASH.fetch() self.do_test_arm_aspeed_openbmc('witherspoon-bmc', image=image_path, uboot='2016.07', cpu_id='0x0', - soc='AST2500 rev A1'); + soc='AST2500 rev A1') if __name__ == '__main__': AspeedTest.main() diff --git a/tests/functional/test_arm_bpim2u.py b/tests/functional/test_arm_bpim2u.py index 8de6ccba88..8bed64b702 100755 --- a/tests/functional/test_arm_bpim2u.py +++ b/tests/functional/test_arm_bpim2u.py @@ -163,7 +163,7 @@ def test_arm_bpim2u_openwrt_22_03_3(self): self, 'Hit any key to stop autoboot:', '=>') exec_command_and_wait_for_pattern(self, "setenv extraargs '" + kernel_command_line + "'", '=>') - exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); + exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...') self.wait_for_console_pattern( 'Please press Enter to activate this console.') diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/test_arm_cubieboard.py index b87a28154d..1eaca0272b 100755 --- a/tests/functional/test_arm_cubieboard.py +++ b/tests/functional/test_arm_cubieboard.py @@ -128,7 +128,7 @@ def test_arm_cubieboard_openwrt_22_03_2(self): self, 'Hit any key to stop autoboot:', '=>') exec_command_and_wait_for_pattern(self, "setenv extraargs '" + kernel_command_line + "'", '=>') - exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); + exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...') self.wait_for_console_pattern( 'Please press Enter to activate this console.') diff --git a/tests/functional/test_arm_orangepi.py b/tests/functional/test_arm_orangepi.py index 1815f56e02..f9bfa8c78d 100755 --- a/tests/functional/test_arm_orangepi.py +++ b/tests/functional/test_arm_orangepi.py @@ -174,7 +174,7 @@ def test_arm_orangepi_armbian(self): exec_command_and_wait_for_pattern(self, ' ', '=>') exec_command_and_wait_for_pattern(self, "setenv extraargs '" + kernel_command_line + "'", '=>') - exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...'); + exec_command_and_wait_for_pattern(self, 'boot', 'Starting kernel ...') self.wait_for_console_pattern('systemd[1]: Hostname set ' + 'to ') diff --git a/tests/functional/test_s390x_topology.py b/tests/functional/test_s390x_topology.py index eefd9729cb..1b5dc65135 100755 --- a/tests/functional/test_s390x_topology.py +++ b/tests/functional/test_s390x_topology.py @@ -217,12 +217,12 @@ def test_polarization(self): self.assertEqual(res['return']['polarization'], 'horizontal') self.check_topology(0, 0, 0, 0, 'medium', False) - self.guest_set_dispatching('1'); + self.guest_set_dispatching('1') res = self.vm.qmp('query-s390x-cpu-polarization') self.assertEqual(res['return']['polarization'], 'vertical') self.check_topology(0, 0, 0, 0, 'medium', False) - self.guest_set_dispatching('0'); + self.guest_set_dispatching('0') res = self.vm.qmp('query-s390x-cpu-polarization') self.assertEqual(res['return']['polarization'], 'horizontal') self.check_topology(0, 0, 0, 0, 'medium', False) @@ -283,7 +283,7 @@ def test_entitlement(self): self.check_polarization('vertical:high') self.check_topology(0, 0, 0, 0, 'high', False) - self.guest_set_dispatching('0'); + self.guest_set_dispatching('0') self.check_polarization("horizontal") self.check_topology(0, 0, 0, 0, 'high', False) @@ -310,11 +310,11 @@ def test_dedicated(self): self.check_topology(0, 0, 0, 0, 'high', True) self.check_polarization("horizontal") - self.guest_set_dispatching('1'); + self.guest_set_dispatching('1') self.check_topology(0, 0, 0, 0, 'high', True) self.check_polarization("vertical:high") - self.guest_set_dispatching('0'); + self.guest_set_dispatching('0') self.check_topology(0, 0, 0, 0, 'high', True) self.check_polarization("horizontal") @@ -360,7 +360,7 @@ def test_dedicated_error(self): self.check_topology(0, 0, 0, 0, 'high', True) - self.guest_set_dispatching('1'); + self.guest_set_dispatching('1') self.check_topology(0, 0, 0, 0, 'high', True) From 99fb9256b761c3cec4a82d2e9597b6cf24ae1285 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 16:54:57 +0200 Subject: [PATCH 0072/2760] tests/functional: Remove unnecessary import statements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pylint complains about these unnecessary import statements, so let's remove them. Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250414145457.261734-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/ports.py | 3 +-- tests/functional/qemu_test/tuxruntest.py | 2 -- tests/functional/qemu_test/uncompress.py | 2 +- tests/functional/test_aarch64_rme_sbsaref.py | 6 ++---- tests/functional/test_aarch64_rme_virt.py | 2 -- tests/functional/test_aarch64_sbsaref_alpine.py | 3 --- tests/functional/test_aarch64_sbsaref_freebsd.py | 2 -- tests/functional/test_aarch64_tcg_plugins.py | 1 - tests/functional/test_aarch64_virt.py | 8 ++------ tests/functional/test_arm_aspeed_ast2500.py | 3 +-- tests/functional/test_arm_cubieboard.py | 2 -- tests/functional/test_arm_quanta_gsj.py | 2 -- tests/functional/test_arm_smdkc210.py | 2 -- tests/functional/test_migration.py | 3 +-- tests/functional/test_mips64el_replay.py | 6 +----- tests/functional/test_mips_replay.py | 2 +- tests/functional/test_mipsel_replay.py | 2 +- tests/functional/test_ppc64_hv.py | 8 ++++---- tests/functional/test_vnc.py | 4 ++-- tests/functional/test_x86_64_kvm_xen.py | 2 -- 20 files changed, 17 insertions(+), 48 deletions(-) diff --git a/tests/functional/qemu_test/ports.py b/tests/functional/qemu_test/ports.py index cc39939d48..631b77abf6 100644 --- a/tests/functional/qemu_test/ports.py +++ b/tests/functional/qemu_test/ports.py @@ -10,12 +10,11 @@ import fcntl import os import socket -import sys -import tempfile from .config import BUILD_DIR from typing import List + class Ports(): PORTS_ADDR = '127.0.0.1' diff --git a/tests/functional/qemu_test/tuxruntest.py b/tests/functional/qemu_test/tuxruntest.py index c2bd5baaae..6c442ff0dc 100644 --- a/tests/functional/qemu_test/tuxruntest.py +++ b/tests/functional/qemu_test/tuxruntest.py @@ -10,8 +10,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later import os -import stat -from subprocess import check_call, DEVNULL from qemu_test import QemuSystemTest from qemu_test import exec_command_and_wait_for_pattern diff --git a/tests/functional/qemu_test/uncompress.py b/tests/functional/qemu_test/uncompress.py index ce79da1b68..b7ef8f759b 100644 --- a/tests/functional/qemu_test/uncompress.py +++ b/tests/functional/qemu_test/uncompress.py @@ -13,7 +13,7 @@ import stat import shutil from urllib.parse import urlparse -from subprocess import run, CalledProcessError, DEVNULL +from subprocess import run, CalledProcessError from .asset import Asset diff --git a/tests/functional/test_aarch64_rme_sbsaref.py b/tests/functional/test_aarch64_rme_sbsaref.py index 0f4f6103a1..746770e776 100755 --- a/tests/functional/test_aarch64_rme_sbsaref.py +++ b/tests/functional/test_aarch64_rme_sbsaref.py @@ -9,15 +9,13 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import time import os -import logging -from qemu_test import QemuSystemTest, Asset -from qemu_test import exec_command, wait_for_console_pattern +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern from qemu_test import exec_command_and_wait_for_pattern from test_aarch64_rme_virt import test_realms_guest + class Aarch64RMESbsaRefMachine(QemuSystemTest): # Stack is built with OP-TEE build environment from those instructions: diff --git a/tests/functional/test_aarch64_rme_virt.py b/tests/functional/test_aarch64_rme_virt.py index a1abf584f0..8452d27928 100755 --- a/tests/functional/test_aarch64_rme_virt.py +++ b/tests/functional/test_aarch64_rme_virt.py @@ -9,9 +9,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import time import os -import logging from qemu_test import QemuSystemTest, Asset from qemu_test import exec_command, wait_for_console_pattern diff --git a/tests/functional/test_aarch64_sbsaref_alpine.py b/tests/functional/test_aarch64_sbsaref_alpine.py index c660cc7a40..6108ec65a5 100755 --- a/tests/functional/test_aarch64_sbsaref_alpine.py +++ b/tests/functional/test_aarch64_sbsaref_alpine.py @@ -10,11 +10,8 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import QemuSystemTest, Asset, skipSlowTest from qemu_test import wait_for_console_pattern -from unittest import skipUnless from test_aarch64_sbsaref import fetch_firmware diff --git a/tests/functional/test_aarch64_sbsaref_freebsd.py b/tests/functional/test_aarch64_sbsaref_freebsd.py index bd6728dc70..26dfc5878b 100755 --- a/tests/functional/test_aarch64_sbsaref_freebsd.py +++ b/tests/functional/test_aarch64_sbsaref_freebsd.py @@ -10,8 +10,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import QemuSystemTest, Asset, skipSlowTest from qemu_test import wait_for_console_pattern from test_aarch64_sbsaref import fetch_firmware diff --git a/tests/functional/test_aarch64_tcg_plugins.py b/tests/functional/test_aarch64_tcg_plugins.py index 4ea71f5f88..cb7e9298fb 100755 --- a/tests/functional/test_aarch64_tcg_plugins.py +++ b/tests/functional/test_aarch64_tcg_plugins.py @@ -13,7 +13,6 @@ import tempfile import mmap -import os import re from qemu.machine.machine import VMLaunchFailure diff --git a/tests/functional/test_aarch64_virt.py b/tests/functional/test_aarch64_virt.py index 884aad7af6..4d0ad90ff8 100755 --- a/tests/functional/test_aarch64_virt.py +++ b/tests/functional/test_aarch64_virt.py @@ -13,12 +13,8 @@ import logging from subprocess import check_call, DEVNULL -from qemu.machine.machine import VMLaunchFailure - -from qemu_test import QemuSystemTest, Asset -from qemu_test import exec_command, exec_command_and_wait_for_pattern -from qemu_test import wait_for_console_pattern -from qemu_test import skipIfMissingCommands, get_qemu_img +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern, get_qemu_img class Aarch64VirtMachine(QemuSystemTest): diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/test_arm_aspeed_ast2500.py index ddc6459f71..a3b44572fc 100755 --- a/tests/functional/test_arm_aspeed_ast2500.py +++ b/tests/functional/test_arm_aspeed_ast2500.py @@ -4,9 +4,8 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -from qemu_test import Asset +from qemu_test import Asset, exec_command_and_wait_for_pattern from aspeed import AspeedTest -from qemu_test import exec_command_and_wait_for_pattern class AST2500Machine(AspeedTest): diff --git a/tests/functional/test_arm_cubieboard.py b/tests/functional/test_arm_cubieboard.py index 1eaca0272b..b536c2f77a 100755 --- a/tests/functional/test_arm_cubieboard.py +++ b/tests/functional/test_arm_cubieboard.py @@ -4,8 +4,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern from qemu_test import interrupt_interactive_console_until_pattern from qemu_test import skipBigDataTest diff --git a/tests/functional/test_arm_quanta_gsj.py b/tests/functional/test_arm_quanta_gsj.py index da60aeb659..cb0545f7bf 100755 --- a/tests/functional/test_arm_quanta_gsj.py +++ b/tests/functional/test_arm_quanta_gsj.py @@ -4,8 +4,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern from qemu_test import interrupt_interactive_console_until_pattern, skipSlowTest diff --git a/tests/functional/test_arm_smdkc210.py b/tests/functional/test_arm_smdkc210.py index 0fda45c63a..3154e7f732 100755 --- a/tests/functional/test_arm_smdkc210.py +++ b/tests/functional/test_arm_smdkc210.py @@ -4,8 +4,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu_test import LinuxKernelTest, Asset diff --git a/tests/functional/test_migration.py b/tests/functional/test_migration.py index 181223a69e..c4393c3543 100755 --- a/tests/functional/test_migration.py +++ b/tests/functional/test_migration.py @@ -11,14 +11,13 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. - import tempfile -import os import time from qemu_test import QemuSystemTest, skipIfMissingCommands from qemu_test.ports import Ports + class MigrationTest(QemuSystemTest): timeout = 10 diff --git a/tests/functional/test_mips64el_replay.py b/tests/functional/test_mips64el_replay.py index 4f63d7fb34..26a6ccff3f 100755 --- a/tests/functional/test_mips64el_replay.py +++ b/tests/functional/test_mips64el_replay.py @@ -4,11 +4,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os -import logging - -from qemu_test import Asset, exec_command_and_wait_for_pattern -from qemu_test import skipIfMissingImports, skipFlakyTest, skipUntrustedTest +from qemu_test import Asset, skipUntrustedTest from replay_kernel import ReplayKernelBase diff --git a/tests/functional/test_mips_replay.py b/tests/functional/test_mips_replay.py index eda031ccad..4327481e35 100755 --- a/tests/functional/test_mips_replay.py +++ b/tests/functional/test_mips_replay.py @@ -4,7 +4,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -from qemu_test import Asset, skipSlowTest, exec_command_and_wait_for_pattern +from qemu_test import Asset, skipSlowTest from replay_kernel import ReplayKernelBase diff --git a/tests/functional/test_mipsel_replay.py b/tests/functional/test_mipsel_replay.py index 0a330de43f..5f4796cf89 100644 --- a/tests/functional/test_mipsel_replay.py +++ b/tests/functional/test_mipsel_replay.py @@ -4,7 +4,7 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -from qemu_test import Asset, wait_for_console_pattern, skipSlowTest +from qemu_test import Asset, skipSlowTest from replay_kernel import ReplayKernelBase diff --git a/tests/functional/test_ppc64_hv.py b/tests/functional/test_ppc64_hv.py index 1920e91f18..d87f440fa7 100755 --- a/tests/functional/test_ppc64_hv.py +++ b/tests/functional/test_ppc64_hv.py @@ -9,14 +9,14 @@ # This work is licensed under the terms of the GNU GPL, version 2 or # later. See the COPYING file in the top-level directory. +import os +import subprocess + +from datetime import datetime from qemu_test import QemuSystemTest, Asset from qemu_test import wait_for_console_pattern, exec_command from qemu_test import skipIfMissingCommands, skipBigDataTest from qemu_test import exec_command_and_wait_for_pattern -import os -import time -import subprocess -from datetime import datetime # Alpine is a light weight distro that supports QEMU. These tests boot # that on the machine then run a QEMU guest inside it in KVM mode, diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py index d4e9dd0279..5c0ee5f927 100755 --- a/tests/functional/test_vnc.py +++ b/tests/functional/test_vnc.py @@ -11,12 +11,12 @@ # later. See the COPYING file in the top-level directory. import socket -from typing import List -from qemu.machine.machine import VMLaunchFailure +from qemu.machine.machine import VMLaunchFailure from qemu_test import QemuSystemTest from qemu_test.ports import Ports + VNC_ADDR = '127.0.0.1' def check_connect(port: int) -> bool: diff --git a/tests/functional/test_x86_64_kvm_xen.py b/tests/functional/test_x86_64_kvm_xen.py index c6abf6bba3..a5d445023c 100755 --- a/tests/functional/test_x86_64_kvm_xen.py +++ b/tests/functional/test_x86_64_kvm_xen.py @@ -11,8 +11,6 @@ # # SPDX-License-Identifier: GPL-2.0-or-later -import os - from qemu.machine import machine from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern From 12c6b6153063aafcdbadca8fee7eac793ef85e4b Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 14 Apr 2025 14:15:20 +0200 Subject: [PATCH 0073/2760] MAINTAINERS: Add functional tests that are not covered yet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some functional tests are currently not covered by the entries in MAINTAINERS yet, so scripts/get_maintainers.pl fails to suggest the right people who should be CC:-ed for related patches. Add the uncovered tests to the right sections to close this gap. Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250414121520.213665-1-thuth@redhat.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 5fd757c5dd..c6d9b022f9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -475,6 +475,7 @@ F: docs/system/i386/sgx.rst F: target/i386/kvm/ F: target/i386/sev* F: scripts/kvm/vmxcap +F: tests/functional/test_x86_64_hotplug_cpu.py Xen emulation on X86 KVM CPUs M: David Woodhouse @@ -626,6 +627,7 @@ S: Maintained F: hw/alpha/ F: hw/isa/smc37c669-superio.c F: tests/tcg/alpha/system/ +F: tests/functional/test_alpha_clipper.py ARM Machines ------------ @@ -950,7 +952,7 @@ F: hw/misc/sbsa_ec.c F: hw/watchdog/sbsa_gwdt.c F: include/hw/watchdog/sbsa_gwdt.h F: docs/system/arm/sbsa.rst -F: tests/functional/test_aarch64_sbsaref*.py +F: tests/functional/test_aarch64_*sbsaref*.py Sharp SL-5500 (Collie) PDA M: Peter Maydell @@ -1019,9 +1021,10 @@ S: Maintained F: hw/arm/virt* F: include/hw/arm/virt.h F: docs/system/arm/virt.rst -F: tests/functional/test_aarch64_virt*.py +F: tests/functional/test_aarch64_*virt*.py F: tests/functional/test_aarch64_tuxrun.py F: tests/functional/test_arm_tuxrun.py +F: tests/functional/test_arm_virt.py Xilinx Zynq M: Edgar E. Iglesias @@ -1262,6 +1265,7 @@ F: hw/m68k/mcf_intc.c F: hw/char/mcf_uart.c F: hw/net/mcf_fec.c F: include/hw/m68k/mcf*.h +F: tests/functional/test_m68k_mcf5208evb.py NeXTcube M: Thomas Huth @@ -1406,6 +1410,7 @@ S: Maintained F: docs/system/openrisc/or1k-sim.rst F: hw/intc/ompic.c F: hw/openrisc/openrisc_sim.c +F: tests/functional/test_or1k_sim.py PowerPC Machines ---------------- @@ -1827,6 +1832,7 @@ F: include/hw/isa/apm.h F: tests/unit/test-x86-topo.c F: tests/qtest/test-x86-cpuid-compat.c F: tests/functional/test_i386_tuxrun.py +F: tests/functional/test_linux_initrd.py F: tests/functional/test_mem_addr_space.py F: tests/functional/test_pc_cpu_hotplug_props.py F: tests/functional/test_x86_64_tuxrun.py @@ -3150,6 +3156,7 @@ F: include/ui/ F: qapi/ui.json F: util/drm.c F: docs/devel/ui.rst +F: tests/functional/test_vnc.py Cocoa graphics M: Peter Maydell @@ -3815,6 +3822,7 @@ F: configs/targets/*linux-user.mak F: scripts/qemu-binfmt-conf.sh F: scripts/update-syscalltbl.sh F: scripts/update-mips-syscall-args.sh +F: tests/functional/test_arm_bflt.py Tiny Code Generator (TCG) ------------------------- @@ -4187,6 +4195,7 @@ F: hw/remote/vfio-user-obj.c F: include/hw/remote/vfio-user-obj.h F: hw/remote/iommu.c F: include/hw/remote/iommu.h +F: tests/functional/test_multiprocess.py EBPF: M: Jason Wang From 8163eeee4e5b6ce5ecb2f7300b80ae8a6f868afd Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 14 Apr 2025 22:49:40 +0800 Subject: [PATCH 0074/2760] rust/hpet: convert num_timers to u8 type The C version of HPET uses the uint8_t type for num_timers, and usize type in Rust version will break migration between the C and Rust versions. So convert num_timers' type to u8 (consistent with the C version of HPET) to make it friendly for vmstate support. Note the commit 7bda68e8e2b0 ("qdev, rust/hpet: fix type of HPET 'timers property") supports the usize type property, but the uint8 property has to be re-supported now. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250414144943.1112885-7-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/hpet.rs | 31 ++++++++++++++++++------------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 3ae3ec25f1..1afa891362 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -12,7 +12,7 @@ use std::{ use qemu_api::{ bindings::{ address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, - qdev_prop_uint32, qdev_prop_usize, + qdev_prop_uint32, qdev_prop_uint8, }, c_str, cell::{BqlCell, BqlRefCell}, @@ -34,9 +34,9 @@ use crate::fw_cfg::HPETFwConfig; const HPET_REG_SPACE_LEN: u64 = 0x400; // 1024 bytes /// Minimum recommended hardware implementation. -const HPET_MIN_TIMERS: usize = 3; +const HPET_MIN_TIMERS: u8 = 3; /// Maximum timers in each timer block. -const HPET_MAX_TIMERS: usize = 32; +const HPET_MAX_TIMERS: u8 = 32; /// Flags that HPETState.flags supports. const HPET_FLAG_MSI_SUPPORT_SHIFT: usize = 0; @@ -559,14 +559,19 @@ pub struct HPETState { /// HPET timer array managed by this timer block. #[doc(alias = "timer")] - timers: [BqlRefCell; HPET_MAX_TIMERS], - num_timers: BqlCell, + timers: [BqlRefCell; HPET_MAX_TIMERS as usize], + num_timers: BqlCell, /// Instance id (HPET timer block ID). hpet_id: BqlCell, } impl HPETState { + // Get num_timers with `usize` type, which is useful to play with array index. + fn get_num_timers(&self) -> usize { + self.num_timers.get().into() + } + const fn has_msi_flag(&self) -> bool { self.flags & (1 << HPET_FLAG_MSI_SUPPORT_SHIFT) != 0 } @@ -628,7 +633,7 @@ impl HPETState { self.hpet_offset .set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns()); - for timer in self.timers.iter().take(self.num_timers.get()) { + for timer in self.timers.iter().take(self.get_num_timers()) { let mut t = timer.borrow_mut(); if t.is_int_enabled() && t.is_int_active() { @@ -640,7 +645,7 @@ impl HPETState { // Halt main counter and disable interrupt generation. self.counter.set(self.get_ticks()); - for timer in self.timers.iter().take(self.num_timers.get()) { + for timer in self.timers.iter().take(self.get_num_timers()) { timer.borrow_mut().del_timer(); } } @@ -663,7 +668,7 @@ impl HPETState { let new_val = val << shift; let cleared = new_val & self.int_status.get(); - for (index, timer) in self.timers.iter().take(self.num_timers.get()).enumerate() { + for (index, timer) in self.timers.iter().take(self.get_num_timers()).enumerate() { if cleared & (1 << index) != 0 { timer.borrow_mut().update_irq(false); } @@ -737,7 +742,7 @@ impl HPETState { 1 << HPET_CAP_COUNT_SIZE_CAP_SHIFT | 1 << HPET_CAP_LEG_RT_CAP_SHIFT | HPET_CAP_VENDER_ID_VALUE << HPET_CAP_VENDER_ID_SHIFT | - ((self.num_timers.get() - 1) as u64) << HPET_CAP_NUM_TIM_SHIFT | // indicate the last timer + ((self.get_num_timers() - 1) as u64) << HPET_CAP_NUM_TIM_SHIFT | // indicate the last timer (HPET_CLK_PERIOD * FS_PER_NS) << HPET_CAP_CNT_CLK_PERIOD_SHIFT, // 10 ns ); @@ -746,7 +751,7 @@ impl HPETState { } fn reset_hold(&self, _type: ResetType) { - for timer in self.timers.iter().take(self.num_timers.get()) { + for timer in self.timers.iter().take(self.get_num_timers()) { timer.borrow_mut().reset(); } @@ -774,7 +779,7 @@ impl HPETState { GlobalRegister::try_from(addr).map(HPETRegister::Global) } else { let timer_id: usize = ((addr - 0x100) / 0x20) as usize; - if timer_id <= self.num_timers.get() { + if timer_id <= self.get_num_timers() { // TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id) TimerRegister::try_from(addr & 0x18) .map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg)) @@ -859,8 +864,8 @@ qemu_api::declare_properties! { c_str!("timers"), HPETState, num_timers, - unsafe { &qdev_prop_usize }, - usize, + unsafe { &qdev_prop_uint8 }, + u8, default = HPET_MIN_TIMERS ), qemu_api::define_property!( From 64e1256b21395eb7c8a9419faba4187dea73970d Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 14 Apr 2025 22:49:41 +0800 Subject: [PATCH 0075/2760] rust/hpet: convert HPETTimer index to u8 type The C version of HPET uses the uint8_t type for timer index ("tn"), and usize type in Rust version will break migration between the C and Rust versions. So convert HPETTimer index' type to u8 (consistent with the C version of HPET) to make it friendly for vmstate support. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250414144943.1112885-8-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/hpet.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 1afa891362..dc8a23f29d 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -184,7 +184,7 @@ fn timer_handler(timer_cell: &BqlRefCell) { pub struct HPETTimer { /// timer N index within the timer block (`HPETState`) #[doc(alias = "tn")] - index: usize, + index: u8, qemu_timer: Timer, /// timer block abstraction containing this timer state: NonNull, @@ -210,7 +210,7 @@ pub struct HPETTimer { } impl HPETTimer { - fn init(&mut self, index: usize, state: &HPETState) { + fn init(&mut self, index: u8, state: &HPETState) { *self = HPETTimer { index, // SAFETY: the HPETTimer will only be used after the timer @@ -235,7 +235,7 @@ impl HPETTimer { Timer::NS, 0, timer_handler, - &state.timers[self.index], + &state.timers[self.index as usize], ) } @@ -246,7 +246,7 @@ impl HPETTimer { } fn is_int_active(&self) -> bool { - self.get_state().is_timer_int_active(self.index) + self.get_state().is_timer_int_active(self.index.into()) } const fn is_fsb_route_enabled(&self) -> bool { @@ -611,7 +611,7 @@ impl HPETState { fn init_timer(&self) { for (index, timer) in self.timers.iter().enumerate() { - timer.borrow_mut().init(index, self); + timer.borrow_mut().init(index.try_into().unwrap(), self); } } From ad3ab01bb7380f8cc8cf86dd260ce751148d6d21 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 14 Apr 2025 22:49:43 +0800 Subject: [PATCH 0076/2760] rust/hpet: Fix a clippy error Carge clippy complained about: error: casts from `u8` to `u32` can be expressed infallibly using `From` So use `From` to convert `u8` to `u32`. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250414144943.1112885-10-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/hpet.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index dc8a23f29d..cbd2ed4f6b 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -353,7 +353,7 @@ impl HPETTimer { // still operate and generate appropriate status bits, but // will not cause an interrupt" self.get_state() - .update_int_status(self.index as u32, set && self.is_int_level_triggered()); + .update_int_status(self.index.into(), set && self.is_int_level_triggered()); self.set_irq(set); } From d031d2fac9b94126ed0cd440da9e69e229d88aef Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 14 Apr 2025 22:49:38 +0800 Subject: [PATCH 0077/2760] rust/vmstate_test: Fix typo in test_vmstate_macro_array_of_pointer_wrapped() test_vmstate_macro_array_of_pointer_wrapped() tests the 3rd element, so fix the index. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250414144943.1112885-5-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/tests/vmstate_tests.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index b8d8b45b19..8b93492a68 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -383,12 +383,12 @@ fn test_vmstate_macro_array_of_pointer_wrapped() { ); assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE); assert_eq!(foo_fields[3].num_offset, 0); - assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); + assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_uint8 }); assert_eq!(foo_fields[3].version_id, 0); assert_eq!(foo_fields[3].size, PTR_SIZE); assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32); assert_eq!( - foo_fields[2].flags.0, + foo_fields[3].flags.0, VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 ); assert!(foo_fields[3].vmsd.is_null()); From efc5603292bfde97fd82fabcedce86310bedbc65 Mon Sep 17 00:00:00 2001 From: Rakesh Jeyasingh Date: Mon, 7 Apr 2025 23:43:26 +0530 Subject: [PATCH 0078/2760] rust/hw/char/pl011: Extract extract DR read logic into separate function - Split `read()` DR case into `read_data_register()` Signed-off-by: Rakesh Jeyasingh Link: https://lore.kernel.org/r/20250407181327.171563-2-rakeshjb010@gmail.com Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 39 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index bf88e0b00a..87153cdae1 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -190,25 +190,7 @@ impl PL011Registers { let mut update = false; let result = match offset { - DR => { - self.flags.set_receive_fifo_full(false); - let c = self.read_fifo[self.read_pos]; - if self.read_count > 0 { - self.read_count -= 1; - self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1); - } - if self.read_count == 0 { - self.flags.set_receive_fifo_empty(true); - } - if self.read_count + 1 == self.read_trigger { - self.int_level &= !Interrupt::RX.0; - } - // Update error bits. - self.receive_status_error_clear.set_from_data(c); - // Must call qemu_chr_fe_accept_input - update = true; - u32::from(c) - } + DR => self.read_data_register(&mut update), RSR => u32::from(self.receive_status_error_clear), FR => u32::from(self.flags), FBRD => self.fbrd, @@ -306,6 +288,25 @@ impl PL011Registers { false } + fn read_data_register(&mut self, update: &mut bool) -> u32 { + self.flags.set_receive_fifo_full(false); + let c = self.read_fifo[self.read_pos]; + + if self.read_count > 0 { + self.read_count -= 1; + self.read_pos = (self.read_pos + 1) & (self.fifo_depth() - 1); + } + if self.read_count == 0 { + self.flags.set_receive_fifo_empty(true); + } + if self.read_count + 1 == self.read_trigger { + self.int_level &= !Interrupt::RX.0; + } + self.receive_status_error_clear.set_from_data(c); + *update = true; + u32::from(c) + } + #[inline] #[must_use] fn loopback_tx(&mut self, value: registers::Data) -> bool { From 6d8c6dee3a767e7650e5d0640e13adb9f01fa37c Mon Sep 17 00:00:00 2001 From: Rakesh Jeyasingh Date: Mon, 7 Apr 2025 23:43:27 +0530 Subject: [PATCH 0079/2760] rust/hw/char/pl011: Extract DR write logic into separate function - Split `write()` DR case into `write_data_register()` Signed-off-by: Rakesh Jeyasingh Link: https://lore.kernel.org/r/20250407181327.171563-3-rakeshjb010@gmail.com Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 87153cdae1..bb2a0f207a 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -221,12 +221,7 @@ impl PL011Registers { // eprintln!("write offset {offset} value {value}"); use RegisterOffset::*; match offset { - DR => { - // interrupts always checked - let _ = self.loopback_tx(value.into()); - self.int_level |= Interrupt::TX.0; - return true; - } + DR => return self.write_data_register(value), RSR => { self.receive_status_error_clear = 0.into(); } @@ -307,6 +302,13 @@ impl PL011Registers { u32::from(c) } + fn write_data_register(&mut self, value: u32) -> bool { + // interrupts always checked + let _ = self.loopback_tx(value.into()); + self.int_level |= Interrupt::TX.0; + true + } + #[inline] #[must_use] fn loopback_tx(&mut self, value: registers::Data) -> bool { From cfac5cdff51ba72780a22bc63a7c8092c8761af3 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:00 -0700 Subject: [PATCH 0080/2760] exec/tswap: target code can use TARGET_BIG_ENDIAN instead of target_words_bigendian() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-2-pierrick.bouvier@linaro.org> --- cpu-target.c | 1 + include/exec/tswap.h | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index cae77374b3..519b0f8900 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -155,6 +155,7 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) abort(); } +#undef target_words_bigendian bool target_words_bigendian(void) { return TARGET_BIG_ENDIAN; diff --git a/include/exec/tswap.h b/include/exec/tswap.h index ecd4faef01..2683da0adb 100644 --- a/include/exec/tswap.h +++ b/include/exec/tswap.h @@ -13,13 +13,14 @@ /** * target_words_bigendian: * Returns true if the (default) endianness of the target is big endian, - * false otherwise. Note that in target-specific code, you can use - * TARGET_BIG_ENDIAN directly instead. On the other hand, common - * code should normally never need to know about the endianness of the - * target, so please do *not* use this function unless you know very well - * what you are doing! + * false otherwise. Common code should normally never need to know about the + * endianness of the target, so please do *not* use this function unless you + * know very well what you are doing! */ bool target_words_bigendian(void); +#ifdef COMPILING_PER_TARGET +#define target_words_bigendian() TARGET_BIG_ENDIAN +#endif /* * If we're in target-specific code, we can hard-code the swapping From 663310b05c43e59cfe97b9add7b645b3f495cd42 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:01 -0700 Subject: [PATCH 0081/2760] exec/tswap: implement {ld,st}.*_p as functions instead of macros Defining functions allows to use them from common code, by not depending on TARGET_BIG_ENDIAN. Remove previous macros from exec/cpu-all.h. By moving them out of cpu-all.h, we'll be able to break dependency on cpu.h for memory related functions coming in next commits. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-3-pierrick.bouvier@linaro.org> --- include/exec/cpu-all.h | 25 --------------- include/exec/tswap.h | 70 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 25 deletions(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 47b14446b8..d000fe4871 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -26,31 +26,6 @@ #include "exec/tswap.h" #include "hw/core/cpu.h" -/* Target-endianness CPU memory access functions. These fit into the - * {ld,st}{type}{sign}{size}{endian}_p naming scheme described in bswap.h. - */ -#if TARGET_BIG_ENDIAN -#define lduw_p(p) lduw_be_p(p) -#define ldsw_p(p) ldsw_be_p(p) -#define ldl_p(p) ldl_be_p(p) -#define ldq_p(p) ldq_be_p(p) -#define stw_p(p, v) stw_be_p(p, v) -#define stl_p(p, v) stl_be_p(p, v) -#define stq_p(p, v) stq_be_p(p, v) -#define ldn_p(p, sz) ldn_be_p(p, sz) -#define stn_p(p, sz, v) stn_be_p(p, sz, v) -#else -#define lduw_p(p) lduw_le_p(p) -#define ldsw_p(p) ldsw_le_p(p) -#define ldl_p(p) ldl_le_p(p) -#define ldq_p(p) ldq_le_p(p) -#define stw_p(p, v) stw_le_p(p, v) -#define stl_p(p, v) stl_le_p(p, v) -#define stq_p(p, v) stq_le_p(p, v) -#define ldn_p(p, sz) ldn_le_p(p, sz) -#define stn_p(p, sz, v) stn_le_p(p, sz, v) -#endif - /* MMU memory access macros */ #if !defined(CONFIG_USER_ONLY) diff --git a/include/exec/tswap.h b/include/exec/tswap.h index 2683da0adb..84060a4999 100644 --- a/include/exec/tswap.h +++ b/include/exec/tswap.h @@ -80,4 +80,74 @@ static inline void tswap64s(uint64_t *s) } } +/* Return ld{word}_{le,be}_p following target endianness. */ +#define LOAD_IMPL(word, args...) \ +do { \ + if (target_words_bigendian()) { \ + return glue(glue(ld, word), _be_p)(args); \ + } else { \ + return glue(glue(ld, word), _le_p)(args); \ + } \ +} while (0) + +static inline int lduw_p(const void *ptr) +{ + LOAD_IMPL(uw, ptr); +} + +static inline int ldsw_p(const void *ptr) +{ + LOAD_IMPL(sw, ptr); +} + +static inline int ldl_p(const void *ptr) +{ + LOAD_IMPL(l, ptr); +} + +static inline uint64_t ldq_p(const void *ptr) +{ + LOAD_IMPL(q, ptr); +} + +static inline uint64_t ldn_p(const void *ptr, int sz) +{ + LOAD_IMPL(n, ptr, sz); +} + +#undef LOAD_IMPL + +/* Call st{word}_{le,be}_p following target endianness. */ +#define STORE_IMPL(word, args...) \ +do { \ + if (target_words_bigendian()) { \ + glue(glue(st, word), _be_p)(args); \ + } else { \ + glue(glue(st, word), _le_p)(args); \ + } \ +} while (0) + + +static inline void stw_p(void *ptr, uint16_t v) +{ + STORE_IMPL(w, ptr, v); +} + +static inline void stl_p(void *ptr, uint32_t v) +{ + STORE_IMPL(l, ptr, v); +} + +static inline void stq_p(void *ptr, uint64_t v) +{ + STORE_IMPL(q, ptr, v); +} + +static inline void stn_p(void *ptr, int sz, uint64_t v) +{ + STORE_IMPL(n, ptr, sz, v); +} + +#undef STORE_IMPL + #endif /* TSWAP_H */ From 3dd08a6920a3bfbc51da7fdf7b87c1f8d876a457 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:02 -0700 Subject: [PATCH 0082/2760] exec/memory_ldst: extract memory_ldst declarations from cpu-all.h They are now accessible through exec/memory.h instead, and we make sure all variants are available for common or target dependent code. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-4-pierrick.bouvier@linaro.org> --- include/exec/cpu-all.h | 12 ------------ include/exec/memory_ldst.h.inc | 4 ---- 2 files changed, 16 deletions(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index d000fe4871..7e8d66de31 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -32,18 +32,6 @@ #include "exec/hwaddr.h" -#define SUFFIX -#define ARG1 as -#define ARG1_DECL AddressSpace *as -#define TARGET_ENDIANNESS -#include "exec/memory_ldst.h.inc" - -#define SUFFIX _cached_slow -#define ARG1 cache -#define ARG1_DECL MemoryRegionCache *cache -#define TARGET_ENDIANNESS -#include "exec/memory_ldst.h.inc" - static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val) { address_space_stl_notdirty(as, addr, val, diff --git a/include/exec/memory_ldst.h.inc b/include/exec/memory_ldst.h.inc index 92ad74e956..7270235c60 100644 --- a/include/exec/memory_ldst.h.inc +++ b/include/exec/memory_ldst.h.inc @@ -19,7 +19,6 @@ * License along with this library; if not, see . */ -#ifdef TARGET_ENDIANNESS uint16_t glue(address_space_lduw, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); uint32_t glue(address_space_ldl, SUFFIX)(ARG1_DECL, @@ -34,7 +33,6 @@ void glue(address_space_stl, SUFFIX)(ARG1_DECL, hwaddr addr, uint32_t val, MemTxAttrs attrs, MemTxResult *result); void glue(address_space_stq, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); -#else uint8_t glue(address_space_ldub, SUFFIX)(ARG1_DECL, hwaddr addr, MemTxAttrs attrs, MemTxResult *result); uint16_t glue(address_space_lduw_le, SUFFIX)(ARG1_DECL, @@ -63,9 +61,7 @@ void glue(address_space_stq_le, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); void glue(address_space_stq_be, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val, MemTxAttrs attrs, MemTxResult *result); -#endif #undef ARG1_DECL #undef ARG1 #undef SUFFIX -#undef TARGET_ENDIANNESS From 713b5e1dccd1219f663f274a89b17e322e617ef9 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:03 -0700 Subject: [PATCH 0083/2760] exec/memory_ldst_phys: extract memory_ldst_phys declarations from cpu-all.h They are now accessible through exec/memory.h instead, and we make sure all variants are available for common or target dependent code. Move stl_phys_notdirty function as well. Cached endianness agnostic version rely on st/ld*_p, which is available through tswap.h. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-5-pierrick.bouvier@linaro.org> --- include/exec/cpu-all.h | 31 ----------------------------- include/exec/memory.h | 10 ++++++++++ include/exec/memory_ldst_phys.h.inc | 5 +---- 3 files changed, 11 insertions(+), 35 deletions(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 7e8d66de31..66a4252269 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -25,37 +25,6 @@ #include "exec/memory.h" #include "exec/tswap.h" #include "hw/core/cpu.h" - -/* MMU memory access macros */ - -#if !defined(CONFIG_USER_ONLY) - -#include "exec/hwaddr.h" - -static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val) -{ - address_space_stl_notdirty(as, addr, val, - MEMTXATTRS_UNSPECIFIED, NULL); -} - -#define SUFFIX -#define ARG1 as -#define ARG1_DECL AddressSpace *as -#define TARGET_ENDIANNESS -#include "exec/memory_ldst_phys.h.inc" - -/* Inline fast path for direct RAM access. */ -#define ENDIANNESS -#include "exec/memory_ldst_cached.h.inc" - -#define SUFFIX _cached -#define ARG1 cache -#define ARG1_DECL MemoryRegionCache *cache -#define TARGET_ENDIANNESS -#include "exec/memory_ldst_phys.h.inc" -#endif - -/* page related stuff */ #include "exec/cpu-defs.h" #include "exec/target_page.h" diff --git a/include/exec/memory.h b/include/exec/memory.h index e1c196a0c2..cc5915033c 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -21,6 +21,7 @@ #include "exec/memattrs.h" #include "exec/memop.h" #include "exec/ramlist.h" +#include "exec/tswap.h" #include "qemu/bswap.h" #include "qemu/queue.h" #include "qemu/int128.h" @@ -2732,6 +2733,12 @@ MemTxResult address_space_write_rom(AddressSpace *as, hwaddr addr, #define ARG1_DECL AddressSpace *as #include "exec/memory_ldst.h.inc" +static inline void stl_phys_notdirty(AddressSpace *as, hwaddr addr, uint32_t val) +{ + address_space_stl_notdirty(as, addr, val, + MEMTXATTRS_UNSPECIFIED, NULL); +} + #define SUFFIX #define ARG1 as #define ARG1_DECL AddressSpace *as @@ -2798,6 +2805,9 @@ static inline void address_space_stb_cached(MemoryRegionCache *cache, } } +#define ENDIANNESS +#include "exec/memory_ldst_cached.h.inc" + #define ENDIANNESS _le #include "exec/memory_ldst_cached.h.inc" diff --git a/include/exec/memory_ldst_phys.h.inc b/include/exec/memory_ldst_phys.h.inc index ecd678610d..db67de7525 100644 --- a/include/exec/memory_ldst_phys.h.inc +++ b/include/exec/memory_ldst_phys.h.inc @@ -19,7 +19,6 @@ * License along with this library; if not, see . */ -#ifdef TARGET_ENDIANNESS static inline uint16_t glue(lduw_phys, SUFFIX)(ARG1_DECL, hwaddr addr) { return glue(address_space_lduw, SUFFIX)(ARG1, addr, @@ -55,7 +54,7 @@ static inline void glue(stq_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t val) glue(address_space_stq, SUFFIX)(ARG1, addr, val, MEMTXATTRS_UNSPECIFIED, NULL); } -#else + static inline uint8_t glue(ldub_phys, SUFFIX)(ARG1_DECL, hwaddr addr) { return glue(address_space_ldub, SUFFIX)(ARG1, addr, @@ -139,9 +138,7 @@ static inline void glue(stq_be_phys, SUFFIX)(ARG1_DECL, hwaddr addr, uint64_t va glue(address_space_stq_be, SUFFIX)(ARG1, addr, val, MEMTXATTRS_UNSPECIFIED, NULL); } -#endif #undef ARG1_DECL #undef ARG1 #undef SUFFIX -#undef TARGET_ENDIANNESS From 82b75e943bdeb04c414767f8beff31d63dc5f2d3 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:04 -0700 Subject: [PATCH 0084/2760] exec/memory.h: make devend_memop "target defines" agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Will allow to make system/memory.c common later. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-6-pierrick.bouvier@linaro.org> --- include/exec/memory.h | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index cc5915033c..577f473446 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -3138,25 +3138,17 @@ address_space_write_cached(MemoryRegionCache *cache, hwaddr addr, MemTxResult address_space_set(AddressSpace *as, hwaddr addr, uint8_t c, hwaddr len, MemTxAttrs attrs); -#ifdef COMPILING_PER_TARGET /* enum device_endian to MemOp. */ static inline MemOp devend_memop(enum device_endian end) { QEMU_BUILD_BUG_ON(DEVICE_HOST_ENDIAN != DEVICE_LITTLE_ENDIAN && DEVICE_HOST_ENDIAN != DEVICE_BIG_ENDIAN); -#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN - /* Swap if non-host endianness or native (target) endianness */ - return (end == DEVICE_HOST_ENDIAN) ? 0 : MO_BSWAP; -#else - const int non_host_endianness = - DEVICE_LITTLE_ENDIAN ^ DEVICE_BIG_ENDIAN ^ DEVICE_HOST_ENDIAN; - - /* In this case, native (target) endianness needs no swap. */ - return (end == non_host_endianness) ? MO_BSWAP : 0; -#endif + bool big_endian = (end == DEVICE_NATIVE_ENDIAN + ? target_words_bigendian() + : end == DEVICE_BIG_ENDIAN); + return big_endian ? MO_BE : MO_LE; } -#endif /* COMPILING_PER_TARGET */ /* * Inhibit technologies that require discarding of pages in RAM blocks, e.g., From 2ec0dc245f1e0505a75eb8fdd69acf857999d735 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:05 -0700 Subject: [PATCH 0085/2760] codebase: prepare to remove cpu.h from exec/exec-all.h Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-7-pierrick.bouvier@linaro.org> --- hw/ppc/spapr_nested.c | 1 + hw/sh4/sh7750.c | 1 + include/tcg/tcg-op.h | 1 + page-vary-target.c | 2 +- target/ppc/helper_regs.h | 2 ++ target/ppc/tcg-excp_helper.c | 1 + target/riscv/bitmanip_helper.c | 2 +- 7 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr_nested.c b/hw/ppc/spapr_nested.c index 201f629203..a79e398c13 100644 --- a/hw/ppc/spapr_nested.c +++ b/hw/ppc/spapr_nested.c @@ -2,6 +2,7 @@ #include "qemu/cutils.h" #include "exec/exec-all.h" #include "exec/cputlb.h" +#include "exec/target_long.h" #include "helper_regs.h" #include "hw/ppc/ppc.h" #include "hw/ppc/spapr.h" diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 6faf0e3ca8..41306fb600 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -29,6 +29,7 @@ #include "hw/irq.h" #include "hw/sh4/sh.h" #include "system/system.h" +#include "target/sh4/cpu.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "sh7750_regs.h" diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index a02850583b..bc46b5570c 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -9,6 +9,7 @@ #define TCG_TCG_OP_H #include "tcg/tcg-op-common.h" +#include "exec/target_long.h" #ifndef TARGET_LONG_BITS #error must include QEMU headers diff --git a/page-vary-target.c b/page-vary-target.c index 3f81144cda..84ddeb7c26 100644 --- a/page-vary-target.c +++ b/page-vary-target.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "exec/page-vary.h" -#include "exec/exec-all.h" +#include "exec/target_page.h" bool set_preferred_target_page_bits(int bits) { diff --git a/target/ppc/helper_regs.h b/target/ppc/helper_regs.h index 8196c1346d..b928c2c452 100644 --- a/target/ppc/helper_regs.h +++ b/target/ppc/helper_regs.h @@ -20,6 +20,8 @@ #ifndef HELPER_REGS_H #define HELPER_REGS_H +#include "target/ppc/cpu.h" + void hreg_swap_gpr_tgpr(CPUPPCState *env); void hreg_compute_hflags(CPUPPCState *env); void hreg_update_pmu_hflags(CPUPPCState *env); diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c index 5a189dc3d7..c422648cfd 100644 --- a/target/ppc/tcg-excp_helper.c +++ b/target/ppc/tcg-excp_helper.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/log.h" +#include "target/ppc/cpu.h" #include "exec/cpu_ldst.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" diff --git a/target/riscv/bitmanip_helper.c b/target/riscv/bitmanip_helper.c index b99c4a39a1..e9c8d7f778 100644 --- a/target/riscv/bitmanip_helper.c +++ b/target/riscv/bitmanip_helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" +#include "exec/target_long.h" #include "exec/helper-proto.h" #include "tcg/tcg.h" From eab9254fecb57666cd6e550b57d6e56321bc9ba7 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:06 -0700 Subject: [PATCH 0086/2760] exec/exec-all: remove dependency on cpu.h Previous commit changed files relying transitively on it. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-8-pierrick.bouvier@linaro.org> --- include/exec/exec-all.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index dd5c40f223..19b0eda44a 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -20,7 +20,6 @@ #ifndef EXEC_ALL_H #define EXEC_ALL_H -#include "cpu.h" #if defined(CONFIG_USER_ONLY) #include "exec/cpu_ldst.h" #endif From 9ed7bcff051217d4cdeefc97ef3f4b41ab2fbfbe Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:07 -0700 Subject: [PATCH 0087/2760] exec/memory-internal: remove dependency on cpu.h Needed so compilation units including it can be common. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-9-pierrick.bouvier@linaro.org> --- include/exec/memory-internal.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index 100c1237ac..b729f3b25a 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -20,8 +20,6 @@ #ifndef MEMORY_INTERNAL_H #define MEMORY_INTERNAL_H -#include "cpu.h" - #ifndef CONFIG_USER_ONLY static inline AddressSpaceDispatch *flatview_to_dispatch(FlatView *fv) { From ede39130af7d325e8dbc6b533a86cb384fdee65d Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:08 -0700 Subject: [PATCH 0088/2760] exec/ram_addr: remove dependency on cpu.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Needed so compilation units including it can be common. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-10-pierrick.bouvier@linaro.org> --- include/exec/ram_addr.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index e4c28fbec9..f5d574261a 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -20,13 +20,14 @@ #define RAM_ADDR_H #ifndef CONFIG_USER_ONLY -#include "cpu.h" #include "system/xen.h" #include "system/tcg.h" #include "exec/cputlb.h" #include "exec/ramlist.h" #include "exec/ramblock.h" #include "exec/exec-all.h" +#include "exec/memory.h" +#include "exec/target_page.h" #include "qemu/rcu.h" #include "exec/hwaddr.h" From d21be2b61976c57ede5db6e8aa96e57e5e27bf16 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:09 -0700 Subject: [PATCH 0089/2760] system/kvm: make kvm_flush_coalesced_mmio_buffer() accessible for common code This function is used by system/physmem.c will be turn into common code in next commit. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-11-pierrick.bouvier@linaro.org> --- include/system/kvm.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/system/kvm.h b/include/system/kvm.h index ab17c09a55..21da3b8b05 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h @@ -210,11 +210,11 @@ bool kvm_arm_supports_user_irq(void); int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); int kvm_on_sigbus(int code, void *addr); +void kvm_flush_coalesced_mmio_buffer(void); + #ifdef COMPILING_PER_TARGET #include "cpu.h" -void kvm_flush_coalesced_mmio_buffer(void); - /** * kvm_update_guest_debug(): ensure KVM debug structures updated * @cs: the CPUState for this cpu From dea4acbdef980d92cbee60df16e8209946bf026f Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:10 -0700 Subject: [PATCH 0090/2760] exec/ram_addr: call xen_hvm_modified_memory only if xen is enabled Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-12-pierrick.bouvier@linaro.org> --- include/exec/ram_addr.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index f5d574261a..92e8708af7 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -339,7 +339,9 @@ static inline void cpu_physical_memory_set_dirty_range(ram_addr_t start, } } - xen_hvm_modified_memory(start, length); + if (xen_enabled()) { + xen_hvm_modified_memory(start, length); + } } #if !defined(_WIN32) @@ -415,7 +417,9 @@ uint64_t cpu_physical_memory_set_dirty_lebitmap(unsigned long *bitmap, } } - xen_hvm_modified_memory(start, pages << TARGET_PAGE_BITS); + if (xen_enabled()) { + xen_hvm_modified_memory(start, pages << TARGET_PAGE_BITS); + } } else { uint8_t clients = tcg_enabled() ? DIRTY_CLIENTS_ALL : DIRTY_CLIENTS_NOCODE; From 105d7ea0a574a7db79cd378fe563e0ce51e38dda Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:11 -0700 Subject: [PATCH 0091/2760] hw/xen: add stubs for various functions Those symbols are used by system/physmem.c, and are called only if xen_enabled() (which happens only if CONFIG_XEN is set and xen is available). So we can crash the stubs in case those are called, as they are linked only when CONFIG_XEN is not set. Acked-by: Richard Henderson Reviewed-by: Anthony PERARD Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-13-pierrick.bouvier@linaro.org> --- hw/xen/meson.build | 3 +++ hw/xen/xen_stubs.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+) create mode 100644 hw/xen/xen_stubs.c diff --git a/hw/xen/meson.build b/hw/xen/meson.build index 4a486e3673..a1850e7698 100644 --- a/hw/xen/meson.build +++ b/hw/xen/meson.build @@ -9,6 +9,9 @@ system_ss.add(when: ['CONFIG_XEN_BUS'], if_true: files( system_ss.add(when: ['CONFIG_XEN', xen], if_true: files( 'xen-operations.c', +), +if_false: files( + 'xen_stubs.c', )) xen_specific_ss = ss.source_set() diff --git a/hw/xen/xen_stubs.c b/hw/xen/xen_stubs.c new file mode 100644 index 0000000000..5e565df392 --- /dev/null +++ b/hw/xen/xen_stubs.c @@ -0,0 +1,51 @@ +/* + * Various stubs for xen functions + * + * Those functions are used only if xen_enabled(). This file is linked only if + * CONFIG_XEN is not set, so they should never be called. + * + * Copyright (c) 2025 Linaro, Ltd. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/xen.h" +#include "system/xen-mapcache.h" + +void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) +{ + g_assert_not_reached(); +} + +void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, + struct MemoryRegion *mr, Error **errp) +{ + g_assert_not_reached(); +} + +bool xen_mr_is_memory(MemoryRegion *mr) +{ + g_assert_not_reached(); +} + +void xen_invalidate_map_cache_entry(uint8_t *buffer) +{ + g_assert_not_reached(); +} + +ram_addr_t xen_ram_addr_from_mapcache(void *ptr) +{ + g_assert_not_reached(); +} + +uint8_t *xen_map_cache(MemoryRegion *mr, + hwaddr phys_addr, + hwaddr size, + ram_addr_t ram_addr_offset, + uint8_t lock, + bool dma, + bool is_write) +{ + g_assert_not_reached(); +} From d3ab5b56630ba8e68af62aabb5a829e5f55b4dd7 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:12 -0700 Subject: [PATCH 0092/2760] system/xen: remove inline stubs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-14-pierrick.bouvier@linaro.org> --- include/system/xen-mapcache.h | 41 ----------------------------------- include/system/xen.h | 21 +++--------------- 2 files changed, 3 insertions(+), 59 deletions(-) diff --git a/include/system/xen-mapcache.h b/include/system/xen-mapcache.h index b68f196ddd..bb454a7c96 100644 --- a/include/system/xen-mapcache.h +++ b/include/system/xen-mapcache.h @@ -14,8 +14,6 @@ typedef hwaddr (*phys_offset_to_gaddr_t)(hwaddr phys_offset, ram_addr_t size); -#ifdef CONFIG_XEN_IS_POSSIBLE - void xen_map_cache_init(phys_offset_to_gaddr_t f, void *opaque); uint8_t *xen_map_cache(MemoryRegion *mr, hwaddr phys_addr, hwaddr size, @@ -28,44 +26,5 @@ void xen_invalidate_map_cache(void); uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, hwaddr new_phys_addr, hwaddr size); -#else - -static inline void xen_map_cache_init(phys_offset_to_gaddr_t f, - void *opaque) -{ -} - -static inline uint8_t *xen_map_cache(MemoryRegion *mr, - hwaddr phys_addr, - hwaddr size, - ram_addr_t ram_addr_offset, - uint8_t lock, - bool dma, - bool is_write) -{ - abort(); -} - -static inline ram_addr_t xen_ram_addr_from_mapcache(void *ptr) -{ - abort(); -} - -static inline void xen_invalidate_map_cache_entry(uint8_t *buffer) -{ -} - -static inline void xen_invalidate_map_cache(void) -{ -} - -static inline uint8_t *xen_replace_cache_entry(hwaddr old_phys_addr, - hwaddr new_phys_addr, - hwaddr size) -{ - abort(); -} - -#endif #endif /* XEN_MAPCACHE_H */ diff --git a/include/system/xen.h b/include/system/xen.h index 990c19a8ef..5f41915732 100644 --- a/include/system/xen.h +++ b/include/system/xen.h @@ -25,30 +25,15 @@ #endif /* COMPILING_PER_TARGET */ #ifdef CONFIG_XEN_IS_POSSIBLE - extern bool xen_allowed; - #define xen_enabled() (xen_allowed) - -void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length); -void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, - struct MemoryRegion *mr, Error **errp); - #else /* !CONFIG_XEN_IS_POSSIBLE */ - #define xen_enabled() 0 -static inline void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length) -{ - /* nothing */ -} -static inline void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, - MemoryRegion *mr, Error **errp) -{ - g_assert_not_reached(); -} - #endif /* CONFIG_XEN_IS_POSSIBLE */ +void xen_hvm_modified_memory(ram_addr_t start, ram_addr_t length); +void xen_ram_alloc(ram_addr_t ram_addr, ram_addr_t size, + struct MemoryRegion *mr, Error **errp); bool xen_mr_is_memory(MemoryRegion *mr); bool xen_mr_is_grants(MemoryRegion *mr); #endif From 822baa8242a38b0b5dfcd05a859f2f50ea229539 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:13 -0700 Subject: [PATCH 0093/2760] system/physmem: compilation unit is now common to all targets Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-15-pierrick.bouvier@linaro.org> --- system/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/system/meson.build b/system/meson.build index eec07a9451..bd82ef132e 100644 --- a/system/meson.build +++ b/system/meson.build @@ -3,7 +3,6 @@ specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( 'ioport.c', 'globals-target.c', 'memory.c', - 'physmem.c', )]) system_ss.add(files( @@ -16,6 +15,7 @@ system_ss.add(files( 'dma-helpers.c', 'globals.c', 'memory_mapping.c', + 'physmem.c', 'qdev-monitor.c', 'qtest.c', 'rtc.c', From 5519a52c0c269926572300cf2cd7f2f77ab28f87 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:14 -0700 Subject: [PATCH 0094/2760] include/exec/memory: extract devend_big_endian from devend_memop we'll use it in system/memory.c. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-16-pierrick.bouvier@linaro.org> --- include/exec/memory.h | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/include/exec/memory.h b/include/exec/memory.h index 577f473446..fc07f69916 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -3138,16 +3138,22 @@ address_space_write_cached(MemoryRegionCache *cache, hwaddr addr, MemTxResult address_space_set(AddressSpace *as, hwaddr addr, uint8_t c, hwaddr len, MemTxAttrs attrs); -/* enum device_endian to MemOp. */ -static inline MemOp devend_memop(enum device_endian end) +/* returns true if end is big endian. */ +static inline bool devend_big_endian(enum device_endian end) { QEMU_BUILD_BUG_ON(DEVICE_HOST_ENDIAN != DEVICE_LITTLE_ENDIAN && DEVICE_HOST_ENDIAN != DEVICE_BIG_ENDIAN); - bool big_endian = (end == DEVICE_NATIVE_ENDIAN - ? target_words_bigendian() - : end == DEVICE_BIG_ENDIAN); - return big_endian ? MO_BE : MO_LE; + if (end == DEVICE_NATIVE_ENDIAN) { + return target_words_bigendian(); + } + return end == DEVICE_BIG_ENDIAN; +} + +/* enum device_endian to MemOp. */ +static inline MemOp devend_memop(enum device_endian end) +{ + return devend_big_endian(end) ? MO_BE : MO_LE; } /* From a54240931ca6a7968123622f3134eba1e2cccb8a Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:15 -0700 Subject: [PATCH 0095/2760] include/exec/memory: move devend functions to memory-internal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only system/physmem.c and system/memory.c use those functions, so we can move then to internal header. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-17-pierrick.bouvier@linaro.org> --- include/exec/memory-internal.h | 19 +++++++++++++++++++ include/exec/memory.h | 18 ------------------ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/include/exec/memory-internal.h b/include/exec/memory-internal.h index b729f3b25a..c75178a3d6 100644 --- a/include/exec/memory-internal.h +++ b/include/exec/memory-internal.h @@ -43,5 +43,24 @@ void address_space_dispatch_free(AddressSpaceDispatch *d); void mtree_print_dispatch(struct AddressSpaceDispatch *d, MemoryRegion *root); + +/* returns true if end is big endian. */ +static inline bool devend_big_endian(enum device_endian end) +{ + QEMU_BUILD_BUG_ON(DEVICE_HOST_ENDIAN != DEVICE_LITTLE_ENDIAN && + DEVICE_HOST_ENDIAN != DEVICE_BIG_ENDIAN); + + if (end == DEVICE_NATIVE_ENDIAN) { + return target_words_bigendian(); + } + return end == DEVICE_BIG_ENDIAN; +} + +/* enum device_endian to MemOp. */ +static inline MemOp devend_memop(enum device_endian end) +{ + return devend_big_endian(end) ? MO_BE : MO_LE; +} + #endif #endif diff --git a/include/exec/memory.h b/include/exec/memory.h index fc07f69916..2f84a7cfed 100644 --- a/include/exec/memory.h +++ b/include/exec/memory.h @@ -3138,24 +3138,6 @@ address_space_write_cached(MemoryRegionCache *cache, hwaddr addr, MemTxResult address_space_set(AddressSpace *as, hwaddr addr, uint8_t c, hwaddr len, MemTxAttrs attrs); -/* returns true if end is big endian. */ -static inline bool devend_big_endian(enum device_endian end) -{ - QEMU_BUILD_BUG_ON(DEVICE_HOST_ENDIAN != DEVICE_LITTLE_ENDIAN && - DEVICE_HOST_ENDIAN != DEVICE_BIG_ENDIAN); - - if (end == DEVICE_NATIVE_ENDIAN) { - return target_words_bigendian(); - } - return end == DEVICE_BIG_ENDIAN; -} - -/* enum device_endian to MemOp. */ -static inline MemOp devend_memop(enum device_endian end) -{ - return devend_big_endian(end) ? MO_BE : MO_LE; -} - /* * Inhibit technologies that require discarding of pages in RAM blocks, e.g., * to manage the actual amount of memory consumed by the VM (then, the memory From 94f5cd62b81d9f6785ebfb9acd41ae253ff32865 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:16 -0700 Subject: [PATCH 0096/2760] system/memory: make compilation unit common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-18-pierrick.bouvier@linaro.org> --- system/memory.c | 17 +++++------------ system/meson.build | 2 +- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/system/memory.c b/system/memory.c index 4c829793a0..eddd21a6cd 100644 --- a/system/memory.c +++ b/system/memory.c @@ -353,15 +353,6 @@ static void flatview_simplify(FlatView *view) } } -static bool memory_region_big_endian(MemoryRegion *mr) -{ -#if TARGET_BIG_ENDIAN - return mr->ops->endianness != DEVICE_LITTLE_ENDIAN; -#else - return mr->ops->endianness == DEVICE_BIG_ENDIAN; -#endif -} - static void adjust_endianness(MemoryRegion *mr, uint64_t *data, MemOp op) { if ((op & MO_BSWAP) != devend_memop(mr->ops->endianness)) { @@ -563,7 +554,7 @@ static MemTxResult access_with_adjusted_size(hwaddr addr, /* FIXME: support unaligned access? */ access_size = MAX(MIN(size, access_size_max), access_size_min); access_mask = MAKE_64BIT_MASK(0, access_size * 8); - if (memory_region_big_endian(mr)) { + if (devend_big_endian(mr->ops->endianness)) { for (i = 0; i < size; i += access_size) { r |= access_fn(mr, addr + i, value, access_size, (size - access_size - i) * 8, access_mask, attrs); @@ -2584,7 +2575,8 @@ void memory_region_add_eventfd(MemoryRegion *mr, unsigned i; if (size) { - adjust_endianness(mr, &mrfd.data, size_memop(size) | MO_TE); + MemOp mop = (target_words_bigendian() ? MO_BE : MO_LE) | size_memop(size); + adjust_endianness(mr, &mrfd.data, mop); } memory_region_transaction_begin(); for (i = 0; i < mr->ioeventfd_nb; ++i) { @@ -2619,7 +2611,8 @@ void memory_region_del_eventfd(MemoryRegion *mr, unsigned i; if (size) { - adjust_endianness(mr, &mrfd.data, size_memop(size) | MO_TE); + MemOp mop = (target_words_bigendian() ? MO_BE : MO_LE) | size_memop(size); + adjust_endianness(mr, &mrfd.data, mop); } memory_region_transaction_begin(); for (i = 0; i < mr->ioeventfd_nb; ++i) { diff --git a/system/meson.build b/system/meson.build index bd82ef132e..4f44b78df3 100644 --- a/system/meson.build +++ b/system/meson.build @@ -2,7 +2,6 @@ specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( 'arch_init.c', 'ioport.c', 'globals-target.c', - 'memory.c', )]) system_ss.add(files( @@ -15,6 +14,7 @@ system_ss.add(files( 'dma-helpers.c', 'globals.c', 'memory_mapping.c', + 'memory.c', 'physmem.c', 'qdev-monitor.c', 'qtest.c', From 0ac7e071c5d5ada2fc12626e267851fd1c10974a Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 17 Mar 2025 11:34:17 -0700 Subject: [PATCH 0097/2760] system/ioport: make compilation unit common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250317183417.285700-19-pierrick.bouvier@linaro.org> --- system/ioport.c | 1 - system/meson.build | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/system/ioport.c b/system/ioport.c index 55c2a75239..89daae9d60 100644 --- a/system/ioport.c +++ b/system/ioport.c @@ -26,7 +26,6 @@ */ #include "qemu/osdep.h" -#include "cpu.h" #include "exec/ioport.h" #include "exec/memory.h" #include "exec/address-spaces.h" diff --git a/system/meson.build b/system/meson.build index 4f44b78df3..063301c3ad 100644 --- a/system/meson.build +++ b/system/meson.build @@ -1,6 +1,5 @@ specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( 'arch_init.c', - 'ioport.c', 'globals-target.c', )]) @@ -13,6 +12,7 @@ system_ss.add(files( 'dirtylimit.c', 'dma-helpers.c', 'globals.c', + 'ioport.c', 'memory_mapping.c', 'memory.c', 'physmem.c', From c4b45298a9b87dc8195aeb3f6439629be9881a17 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 10 Mar 2025 12:57:24 -0700 Subject: [PATCH 0098/2760] accel/tcg: Build user-exec-stub.c once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONFIG_USER_ONLY == !CONFIG_SYSTEM_ONLY. Therefore it's cleaner to just add to user_ss. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 38ff227eb0..14bf797fda 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -12,7 +12,6 @@ tcg_specific_ss.add(files( 'translator.c', )) tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) -tcg_specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_false: files('user-exec-stub.c')) if get_option('plugins') tcg_specific_ss.add(files('plugin-gen.c')) endif @@ -22,6 +21,10 @@ specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'cputlb.c', )) +user_ss.add(when: ['CONFIG_TCG'], if_true: files( + 'user-exec-stub.c', +)) + system_ss.add(when: ['CONFIG_TCG'], if_true: files( 'icount-common.c', 'monitor.c', From e9358339c5dc7aab0b48d35b57371efae1737046 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 10 Mar 2025 13:15:22 -0700 Subject: [PATCH 0099/2760] accel/tcg: Build plugin-gen.c once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We assert that env immediately follows CPUState in cpu-all.h. Change the offsetof expressions to be based on CPUState instead of ArchCPU. Reviewed-by: Alex Bennée Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 7 ++++--- accel/tcg/plugin-gen.c | 13 +++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 14bf797fda..185830d0f5 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -3,6 +3,10 @@ common_ss.add(when: 'CONFIG_TCG', if_true: files( 'tcg-runtime.c', 'tcg-runtime-gvec.c', )) +if get_option('plugins') + common_ss.add(when: 'CONFIG_TCG', if_true: files('plugin-gen.c')) +endif + tcg_specific_ss = ss.source_set() tcg_specific_ss.add(files( 'tcg-all.c', @@ -12,9 +16,6 @@ tcg_specific_ss.add(files( 'translator.c', )) tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) -if get_option('plugins') - tcg_specific_ss.add(files('plugin-gen.c')) -endif specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index 7e5f040bf7..c1da753894 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -22,13 +22,12 @@ #include "qemu/osdep.h" #include "qemu/plugin.h" #include "qemu/log.h" -#include "cpu.h" #include "tcg/tcg.h" #include "tcg/tcg-temp-internal.h" -#include "tcg/tcg-op.h" -#include "exec/exec-all.h" +#include "tcg/tcg-op-common.h" #include "exec/plugin-gen.h" #include "exec/translator.h" +#include "exec/translation-block.h" enum plugin_gen_from { PLUGIN_GEN_FROM_TB, @@ -89,15 +88,13 @@ static void gen_enable_mem_helper(struct qemu_plugin_tb *ptb, qemu_plugin_add_dyn_cb_arr(arr); tcg_gen_st_ptr(tcg_constant_ptr((intptr_t)arr), tcg_env, - offsetof(CPUState, neg.plugin_mem_cbs) - - offsetof(ArchCPU, env)); + offsetof(CPUState, neg.plugin_mem_cbs) - sizeof(CPUState)); } static void gen_disable_mem_helper(void) { tcg_gen_st_ptr(tcg_constant_ptr(0), tcg_env, - offsetof(CPUState, neg.plugin_mem_cbs) - - offsetof(ArchCPU, env)); + offsetof(CPUState, neg.plugin_mem_cbs) - sizeof(CPUState)); } static TCGv_i32 gen_cpu_index(void) @@ -113,7 +110,7 @@ static TCGv_i32 gen_cpu_index(void) } TCGv_i32 cpu_index = tcg_temp_ebb_new_i32(); tcg_gen_ld_i32(cpu_index, tcg_env, - -offsetof(ArchCPU, env) + offsetof(CPUState, cpu_index)); + offsetof(CPUState, cpu_index) - sizeof(CPUState)); return cpu_index; } From 66269bb96999395906e0e38ca7e59f92ab371933 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 14 Mar 2025 18:20:00 -0700 Subject: [PATCH 0100/2760] accel/tcg: Fix cpu_ld*_code_mmu for user mode These routines are buggy in multiple ways: - Use of target-endian loads, then a bswap that depends on the host endiannness. - A non-unwinding code load must set_helper_retaddr 1, which is magic within adjust_signal_pc. - cpu_ldq_code_mmu used MMU_DATA_LOAD The bugs are hidden because all current uses of cpu_ld*_code_mmu are from system mode. Fixes: 2899062614a ("accel/tcg: Add cpu_ld*_code_mmu") Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 41 ++++------------------------------------- 1 file changed, 4 insertions(+), 37 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 2322181b15..629a1c9ce6 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -1257,58 +1257,25 @@ uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr) uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) { - void *haddr; - uint8_t ret; - - haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_INST_FETCH); - ret = ldub_p(haddr); - clear_helper_retaddr(); - return ret; + return do_ld1_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH); } uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) { - void *haddr; - uint16_t ret; - - haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_INST_FETCH); - ret = lduw_p(haddr); - clear_helper_retaddr(); - if (get_memop(oi) & MO_BSWAP) { - ret = bswap16(ret); - } - return ret; + return do_ld2_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH); } uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) { - void *haddr; - uint32_t ret; - - haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_INST_FETCH); - ret = ldl_p(haddr); - clear_helper_retaddr(); - if (get_memop(oi) & MO_BSWAP) { - ret = bswap32(ret); - } - return ret; + return do_ld4_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH); } uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) { - void *haddr; - uint64_t ret; - - haddr = cpu_mmu_lookup(env_cpu(env), addr, oi, ra, MMU_DATA_LOAD); - ret = ldq_p(haddr); - clear_helper_retaddr(); - if (get_memop(oi) & MO_BSWAP) { - ret = bswap64(ret); - } - return ret; + return do_ld8_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH); } #include "ldst_common.c.inc" From 446321b3234a67ef4576847596ca2859f3b2f89a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Mar 2025 12:52:36 -0700 Subject: [PATCH 0101/2760] include/exec: Use vaddr for *_mmu guest memory access routines Use vaddr only for the newest api, because it has the least number of uses and therefore is the easiest to audit. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/atomic_template.h | 16 ++++++------- accel/tcg/cputlb.c | 8 +++---- accel/tcg/ldst_common.c.inc | 20 ++++++++-------- accel/tcg/user-exec.c | 8 +++---- include/exec/cpu_ldst.h | 48 ++++++++++++++++++------------------- 5 files changed, 50 insertions(+), 50 deletions(-) diff --git a/accel/tcg/atomic_template.h b/accel/tcg/atomic_template.h index 89593b2502..08a475c10c 100644 --- a/accel/tcg/atomic_template.h +++ b/accel/tcg/atomic_template.h @@ -77,7 +77,7 @@ # define END _le #endif -ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr, +ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr, ABI_TYPE cmpv, ABI_TYPE newv, MemOpIdx oi, uintptr_t retaddr) { @@ -101,7 +101,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr, } #if DATA_SIZE < 16 -ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val, +ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) { DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, @@ -120,7 +120,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val, } #define GEN_ATOMIC_HELPER(X) \ -ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ { \ DATA_TYPE *haddr, ret; \ @@ -156,7 +156,7 @@ GEN_ATOMIC_HELPER(xor_fetch) * of CF_PARALLEL's value, we'll trace just a read and a write. */ #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ -ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \ { \ XDATA_TYPE *haddr, cmp, old, new, val = xval; \ @@ -202,7 +202,7 @@ GEN_ATOMIC_HELPER_FN(umax_fetch, MAX, DATA_TYPE, new) # define END _be #endif -ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr, +ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, vaddr addr, ABI_TYPE cmpv, ABI_TYPE newv, MemOpIdx oi, uintptr_t retaddr) { @@ -226,7 +226,7 @@ ABI_TYPE ATOMIC_NAME(cmpxchg)(CPUArchState *env, abi_ptr addr, } #if DATA_SIZE < 16 -ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val, +ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, vaddr addr, ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) { DATA_TYPE *haddr = atomic_mmu_lookup(env_cpu(env), addr, oi, @@ -245,7 +245,7 @@ ABI_TYPE ATOMIC_NAME(xchg)(CPUArchState *env, abi_ptr addr, ABI_TYPE val, } #define GEN_ATOMIC_HELPER(X) \ -ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE val, MemOpIdx oi, uintptr_t retaddr) \ { \ DATA_TYPE *haddr, ret; \ @@ -278,7 +278,7 @@ GEN_ATOMIC_HELPER(xor_fetch) * of CF_PARALLEL's value, we'll trace just a read and a write. */ #define GEN_ATOMIC_HELPER_FN(X, FN, XDATA_TYPE, RET) \ -ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, abi_ptr addr, \ +ABI_TYPE ATOMIC_NAME(X)(CPUArchState *env, vaddr addr, \ ABI_TYPE xval, MemOpIdx oi, uintptr_t retaddr) \ { \ XDATA_TYPE *haddr, ldo, ldn, old, new, val = xval; \ diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index fb22048876..b03998f926 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2925,25 +2925,25 @@ uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr) return do_ld8_mmu(cs, addr, oi, 0, MMU_INST_FETCH); } -uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr, +uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t retaddr) { return do_ld1_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH); } -uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr, +uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t retaddr) { return do_ld2_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH); } -uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr, +uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t retaddr) { return do_ld4_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH); } -uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr, +uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t retaddr) { return do_ld8_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH); diff --git a/accel/tcg/ldst_common.c.inc b/accel/tcg/ldst_common.c.inc index ebbf380d76..0447c0bb92 100644 --- a/accel/tcg/ldst_common.c.inc +++ b/accel/tcg/ldst_common.c.inc @@ -135,7 +135,7 @@ static void plugin_load_cb(CPUArchState *env, abi_ptr addr, } } -uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) +uint8_t cpu_ldb_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { uint8_t ret; @@ -145,7 +145,7 @@ uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra) return ret; } -uint16_t cpu_ldw_mmu(CPUArchState *env, abi_ptr addr, +uint16_t cpu_ldw_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { uint16_t ret; @@ -156,7 +156,7 @@ uint16_t cpu_ldw_mmu(CPUArchState *env, abi_ptr addr, return ret; } -uint32_t cpu_ldl_mmu(CPUArchState *env, abi_ptr addr, +uint32_t cpu_ldl_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { uint32_t ret; @@ -167,7 +167,7 @@ uint32_t cpu_ldl_mmu(CPUArchState *env, abi_ptr addr, return ret; } -uint64_t cpu_ldq_mmu(CPUArchState *env, abi_ptr addr, +uint64_t cpu_ldq_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { uint64_t ret; @@ -178,7 +178,7 @@ uint64_t cpu_ldq_mmu(CPUArchState *env, abi_ptr addr, return ret; } -Int128 cpu_ld16_mmu(CPUArchState *env, abi_ptr addr, +Int128 cpu_ld16_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { Int128 ret; @@ -205,14 +205,14 @@ static void plugin_store_cb(CPUArchState *env, abi_ptr addr, } } -void cpu_stb_mmu(CPUArchState *env, abi_ptr addr, uint8_t val, +void cpu_stb_mmu(CPUArchState *env, vaddr addr, uint8_t val, MemOpIdx oi, uintptr_t retaddr) { helper_stb_mmu(env, addr, val, oi, retaddr); plugin_store_cb(env, addr, val, 0, oi); } -void cpu_stw_mmu(CPUArchState *env, abi_ptr addr, uint16_t val, +void cpu_stw_mmu(CPUArchState *env, vaddr addr, uint16_t val, MemOpIdx oi, uintptr_t retaddr) { tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_16); @@ -220,7 +220,7 @@ void cpu_stw_mmu(CPUArchState *env, abi_ptr addr, uint16_t val, plugin_store_cb(env, addr, val, 0, oi); } -void cpu_stl_mmu(CPUArchState *env, abi_ptr addr, uint32_t val, +void cpu_stl_mmu(CPUArchState *env, vaddr addr, uint32_t val, MemOpIdx oi, uintptr_t retaddr) { tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_32); @@ -228,7 +228,7 @@ void cpu_stl_mmu(CPUArchState *env, abi_ptr addr, uint32_t val, plugin_store_cb(env, addr, val, 0, oi); } -void cpu_stq_mmu(CPUArchState *env, abi_ptr addr, uint64_t val, +void cpu_stq_mmu(CPUArchState *env, vaddr addr, uint64_t val, MemOpIdx oi, uintptr_t retaddr) { tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_64); @@ -236,7 +236,7 @@ void cpu_stq_mmu(CPUArchState *env, abi_ptr addr, uint64_t val, plugin_store_cb(env, addr, val, 0, oi); } -void cpu_st16_mmu(CPUArchState *env, abi_ptr addr, Int128 val, +void cpu_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, MemOpIdx oi, uintptr_t retaddr) { tcg_debug_assert((get_memop(oi) & MO_SIZE) == MO_128); diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 629a1c9ce6..dec17435c5 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -1254,25 +1254,25 @@ uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr) return ret; } -uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr, +uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { return do_ld1_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH); } -uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr, +uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { return do_ld2_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH); } -uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr, +uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { return do_ld4_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH); } -uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr, +uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { return do_ld8_mmu(env_cpu(env), addr, oi, ra ? ra : 1, MMU_INST_FETCH); diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 769e9fc440..ddd8e0cf48 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -157,48 +157,48 @@ void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint64_t val, int mmu_idx, uintptr_t ra); -uint8_t cpu_ldb_mmu(CPUArchState *env, abi_ptr ptr, MemOpIdx oi, uintptr_t ra); -uint16_t cpu_ldw_mmu(CPUArchState *env, abi_ptr ptr, MemOpIdx oi, uintptr_t ra); -uint32_t cpu_ldl_mmu(CPUArchState *env, abi_ptr ptr, MemOpIdx oi, uintptr_t ra); -uint64_t cpu_ldq_mmu(CPUArchState *env, abi_ptr ptr, MemOpIdx oi, uintptr_t ra); -Int128 cpu_ld16_mmu(CPUArchState *env, abi_ptr addr, MemOpIdx oi, uintptr_t ra); +uint8_t cpu_ldb_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); +uint16_t cpu_ldw_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); +uint32_t cpu_ldl_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); +uint64_t cpu_ldq_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); +Int128 cpu_ld16_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra); -void cpu_stb_mmu(CPUArchState *env, abi_ptr ptr, uint8_t val, +void cpu_stb_mmu(CPUArchState *env, vaddr ptr, uint8_t val, MemOpIdx oi, uintptr_t ra); -void cpu_stw_mmu(CPUArchState *env, abi_ptr ptr, uint16_t val, +void cpu_stw_mmu(CPUArchState *env, vaddr ptr, uint16_t val, MemOpIdx oi, uintptr_t ra); -void cpu_stl_mmu(CPUArchState *env, abi_ptr ptr, uint32_t val, +void cpu_stl_mmu(CPUArchState *env, vaddr ptr, uint32_t val, MemOpIdx oi, uintptr_t ra); -void cpu_stq_mmu(CPUArchState *env, abi_ptr ptr, uint64_t val, +void cpu_stq_mmu(CPUArchState *env, vaddr ptr, uint64_t val, MemOpIdx oi, uintptr_t ra); -void cpu_st16_mmu(CPUArchState *env, abi_ptr addr, Int128 val, +void cpu_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, MemOpIdx oi, uintptr_t ra); -uint32_t cpu_atomic_cmpxchgb_mmu(CPUArchState *env, abi_ptr addr, +uint32_t cpu_atomic_cmpxchgb_mmu(CPUArchState *env, vaddr addr, uint32_t cmpv, uint32_t newv, MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgw_le_mmu(CPUArchState *env, abi_ptr addr, +uint32_t cpu_atomic_cmpxchgw_le_mmu(CPUArchState *env, vaddr addr, uint32_t cmpv, uint32_t newv, MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgl_le_mmu(CPUArchState *env, abi_ptr addr, +uint32_t cpu_atomic_cmpxchgl_le_mmu(CPUArchState *env, vaddr addr, uint32_t cmpv, uint32_t newv, MemOpIdx oi, uintptr_t retaddr); -uint64_t cpu_atomic_cmpxchgq_le_mmu(CPUArchState *env, abi_ptr addr, +uint64_t cpu_atomic_cmpxchgq_le_mmu(CPUArchState *env, vaddr addr, uint64_t cmpv, uint64_t newv, MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgw_be_mmu(CPUArchState *env, abi_ptr addr, +uint32_t cpu_atomic_cmpxchgw_be_mmu(CPUArchState *env, vaddr addr, uint32_t cmpv, uint32_t newv, MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgl_be_mmu(CPUArchState *env, abi_ptr addr, +uint32_t cpu_atomic_cmpxchgl_be_mmu(CPUArchState *env, vaddr addr, uint32_t cmpv, uint32_t newv, MemOpIdx oi, uintptr_t retaddr); -uint64_t cpu_atomic_cmpxchgq_be_mmu(CPUArchState *env, abi_ptr addr, +uint64_t cpu_atomic_cmpxchgq_be_mmu(CPUArchState *env, vaddr addr, uint64_t cmpv, uint64_t newv, MemOpIdx oi, uintptr_t retaddr); #define GEN_ATOMIC_HELPER(NAME, TYPE, SUFFIX) \ TYPE cpu_atomic_ ## NAME ## SUFFIX ## _mmu \ - (CPUArchState *env, abi_ptr addr, TYPE val, \ + (CPUArchState *env, vaddr addr, TYPE val, \ MemOpIdx oi, uintptr_t retaddr); #ifdef CONFIG_ATOMIC64 @@ -244,10 +244,10 @@ GEN_ATOMIC_HELPER_ALL(xchg) #undef GEN_ATOMIC_HELPER_ALL #undef GEN_ATOMIC_HELPER -Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, abi_ptr addr, +Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, vaddr addr, Int128 cmpv, Int128 newv, MemOpIdx oi, uintptr_t retaddr); -Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, abi_ptr addr, +Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, vaddr addr, Int128 cmpv, Int128 newv, MemOpIdx oi, uintptr_t retaddr); @@ -297,13 +297,13 @@ Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, abi_ptr addr, # define cpu_stq_mmuidx_ra cpu_stq_le_mmuidx_ra #endif -uint8_t cpu_ldb_code_mmu(CPUArchState *env, abi_ptr addr, +uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra); -uint16_t cpu_ldw_code_mmu(CPUArchState *env, abi_ptr addr, +uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra); -uint32_t cpu_ldl_code_mmu(CPUArchState *env, abi_ptr addr, +uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra); -uint64_t cpu_ldq_code_mmu(CPUArchState *env, abi_ptr addr, +uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra); uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr); From 0b6426ba6c218fa807fe97258d75cb4bc84c860d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Mar 2025 13:03:37 -0700 Subject: [PATCH 0102/2760] include/exec: Split out cpu-ldst-common.h Split out the *_mmu api, which no longer uses target specific argument types. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/exec/cpu-ldst-common.h | 122 +++++++++++++++++++++++++++++++++ include/exec/cpu_ldst.h | 108 +---------------------------- 2 files changed, 123 insertions(+), 107 deletions(-) create mode 100644 include/exec/cpu-ldst-common.h diff --git a/include/exec/cpu-ldst-common.h b/include/exec/cpu-ldst-common.h new file mode 100644 index 0000000000..c46a6ade5d --- /dev/null +++ b/include/exec/cpu-ldst-common.h @@ -0,0 +1,122 @@ +/* + * Software MMU support + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef CPU_LDST_COMMON_H +#define CPU_LDST_COMMON_H + +#ifndef CONFIG_TCG +#error Can only include this header with TCG +#endif + +#include "exec/memopidx.h" +#include "exec/vaddr.h" +#include "exec/mmu-access-type.h" +#include "qemu/int128.h" + +uint8_t cpu_ldb_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); +uint16_t cpu_ldw_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); +uint32_t cpu_ldl_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); +uint64_t cpu_ldq_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); +Int128 cpu_ld16_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra); + +void cpu_stb_mmu(CPUArchState *env, vaddr ptr, uint8_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stw_mmu(CPUArchState *env, vaddr ptr, uint16_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stl_mmu(CPUArchState *env, vaddr ptr, uint32_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_stq_mmu(CPUArchState *env, vaddr ptr, uint64_t val, + MemOpIdx oi, uintptr_t ra); +void cpu_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, + MemOpIdx oi, uintptr_t ra); + +uint32_t cpu_atomic_cmpxchgb_mmu(CPUArchState *env, vaddr addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint32_t cpu_atomic_cmpxchgw_le_mmu(CPUArchState *env, vaddr addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint32_t cpu_atomic_cmpxchgl_le_mmu(CPUArchState *env, vaddr addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint64_t cpu_atomic_cmpxchgq_le_mmu(CPUArchState *env, vaddr addr, + uint64_t cmpv, uint64_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint32_t cpu_atomic_cmpxchgw_be_mmu(CPUArchState *env, vaddr addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint32_t cpu_atomic_cmpxchgl_be_mmu(CPUArchState *env, vaddr addr, + uint32_t cmpv, uint32_t newv, + MemOpIdx oi, uintptr_t retaddr); +uint64_t cpu_atomic_cmpxchgq_be_mmu(CPUArchState *env, vaddr addr, + uint64_t cmpv, uint64_t newv, + MemOpIdx oi, uintptr_t retaddr); + +#define GEN_ATOMIC_HELPER(NAME, TYPE, SUFFIX) \ +TYPE cpu_atomic_ ## NAME ## SUFFIX ## _mmu \ + (CPUArchState *env, vaddr addr, TYPE val, \ + MemOpIdx oi, uintptr_t retaddr); + +#ifdef CONFIG_ATOMIC64 +#define GEN_ATOMIC_HELPER_ALL(NAME) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) \ + GEN_ATOMIC_HELPER(NAME, uint64_t, q_le) \ + GEN_ATOMIC_HELPER(NAME, uint64_t, q_be) +#else +#define GEN_ATOMIC_HELPER_ALL(NAME) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ + GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) +#endif + +GEN_ATOMIC_HELPER_ALL(fetch_add) +GEN_ATOMIC_HELPER_ALL(fetch_sub) +GEN_ATOMIC_HELPER_ALL(fetch_and) +GEN_ATOMIC_HELPER_ALL(fetch_or) +GEN_ATOMIC_HELPER_ALL(fetch_xor) +GEN_ATOMIC_HELPER_ALL(fetch_smin) +GEN_ATOMIC_HELPER_ALL(fetch_umin) +GEN_ATOMIC_HELPER_ALL(fetch_smax) +GEN_ATOMIC_HELPER_ALL(fetch_umax) + +GEN_ATOMIC_HELPER_ALL(add_fetch) +GEN_ATOMIC_HELPER_ALL(sub_fetch) +GEN_ATOMIC_HELPER_ALL(and_fetch) +GEN_ATOMIC_HELPER_ALL(or_fetch) +GEN_ATOMIC_HELPER_ALL(xor_fetch) +GEN_ATOMIC_HELPER_ALL(smin_fetch) +GEN_ATOMIC_HELPER_ALL(umin_fetch) +GEN_ATOMIC_HELPER_ALL(smax_fetch) +GEN_ATOMIC_HELPER_ALL(umax_fetch) + +GEN_ATOMIC_HELPER_ALL(xchg) + +#undef GEN_ATOMIC_HELPER_ALL +#undef GEN_ATOMIC_HELPER + +Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, vaddr addr, + Int128 cmpv, Int128 newv, + MemOpIdx oi, uintptr_t retaddr); +Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, vaddr addr, + Int128 cmpv, Int128 newv, + MemOpIdx oi, uintptr_t retaddr); + +uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t ra); +uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t ra); +uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t ra); +uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr, + MemOpIdx oi, uintptr_t ra); + +#endif /* CPU_LDST_COMMON_H */ diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index ddd8e0cf48..1fbdbe59ae 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -66,11 +66,8 @@ #error Can only include this header with TCG #endif -#include "exec/memopidx.h" -#include "exec/vaddr.h" +#include "exec/cpu-ldst-common.h" #include "exec/abi_ptr.h" -#include "exec/mmu-access-type.h" -#include "qemu/int128.h" #if defined(CONFIG_USER_ONLY) #include "user/guest-host.h" @@ -157,100 +154,6 @@ void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint64_t val, int mmu_idx, uintptr_t ra); -uint8_t cpu_ldb_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); -uint16_t cpu_ldw_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); -uint32_t cpu_ldl_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); -uint64_t cpu_ldq_mmu(CPUArchState *env, vaddr ptr, MemOpIdx oi, uintptr_t ra); -Int128 cpu_ld16_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra); - -void cpu_stb_mmu(CPUArchState *env, vaddr ptr, uint8_t val, - MemOpIdx oi, uintptr_t ra); -void cpu_stw_mmu(CPUArchState *env, vaddr ptr, uint16_t val, - MemOpIdx oi, uintptr_t ra); -void cpu_stl_mmu(CPUArchState *env, vaddr ptr, uint32_t val, - MemOpIdx oi, uintptr_t ra); -void cpu_stq_mmu(CPUArchState *env, vaddr ptr, uint64_t val, - MemOpIdx oi, uintptr_t ra); -void cpu_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, - MemOpIdx oi, uintptr_t ra); - -uint32_t cpu_atomic_cmpxchgb_mmu(CPUArchState *env, vaddr addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgw_le_mmu(CPUArchState *env, vaddr addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgl_le_mmu(CPUArchState *env, vaddr addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -uint64_t cpu_atomic_cmpxchgq_le_mmu(CPUArchState *env, vaddr addr, - uint64_t cmpv, uint64_t newv, - MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgw_be_mmu(CPUArchState *env, vaddr addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -uint32_t cpu_atomic_cmpxchgl_be_mmu(CPUArchState *env, vaddr addr, - uint32_t cmpv, uint32_t newv, - MemOpIdx oi, uintptr_t retaddr); -uint64_t cpu_atomic_cmpxchgq_be_mmu(CPUArchState *env, vaddr addr, - uint64_t cmpv, uint64_t newv, - MemOpIdx oi, uintptr_t retaddr); - -#define GEN_ATOMIC_HELPER(NAME, TYPE, SUFFIX) \ -TYPE cpu_atomic_ ## NAME ## SUFFIX ## _mmu \ - (CPUArchState *env, vaddr addr, TYPE val, \ - MemOpIdx oi, uintptr_t retaddr); - -#ifdef CONFIG_ATOMIC64 -#define GEN_ATOMIC_HELPER_ALL(NAME) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) \ - GEN_ATOMIC_HELPER(NAME, uint64_t, q_le) \ - GEN_ATOMIC_HELPER(NAME, uint64_t, q_be) -#else -#define GEN_ATOMIC_HELPER_ALL(NAME) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, b) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, w_le) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, w_be) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, l_le) \ - GEN_ATOMIC_HELPER(NAME, uint32_t, l_be) -#endif - -GEN_ATOMIC_HELPER_ALL(fetch_add) -GEN_ATOMIC_HELPER_ALL(fetch_sub) -GEN_ATOMIC_HELPER_ALL(fetch_and) -GEN_ATOMIC_HELPER_ALL(fetch_or) -GEN_ATOMIC_HELPER_ALL(fetch_xor) -GEN_ATOMIC_HELPER_ALL(fetch_smin) -GEN_ATOMIC_HELPER_ALL(fetch_umin) -GEN_ATOMIC_HELPER_ALL(fetch_smax) -GEN_ATOMIC_HELPER_ALL(fetch_umax) - -GEN_ATOMIC_HELPER_ALL(add_fetch) -GEN_ATOMIC_HELPER_ALL(sub_fetch) -GEN_ATOMIC_HELPER_ALL(and_fetch) -GEN_ATOMIC_HELPER_ALL(or_fetch) -GEN_ATOMIC_HELPER_ALL(xor_fetch) -GEN_ATOMIC_HELPER_ALL(smin_fetch) -GEN_ATOMIC_HELPER_ALL(umin_fetch) -GEN_ATOMIC_HELPER_ALL(smax_fetch) -GEN_ATOMIC_HELPER_ALL(umax_fetch) - -GEN_ATOMIC_HELPER_ALL(xchg) - -#undef GEN_ATOMIC_HELPER_ALL -#undef GEN_ATOMIC_HELPER - -Int128 cpu_atomic_cmpxchgo_le_mmu(CPUArchState *env, vaddr addr, - Int128 cmpv, Int128 newv, - MemOpIdx oi, uintptr_t retaddr); -Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, vaddr addr, - Int128 cmpv, Int128 newv, - MemOpIdx oi, uintptr_t retaddr); - #if TARGET_BIG_ENDIAN # define cpu_lduw_data cpu_lduw_be_data # define cpu_ldsw_data cpu_ldsw_be_data @@ -297,15 +200,6 @@ Int128 cpu_atomic_cmpxchgo_be_mmu(CPUArchState *env, vaddr addr, # define cpu_stq_mmuidx_ra cpu_stq_le_mmuidx_ra #endif -uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, - MemOpIdx oi, uintptr_t ra); -uint16_t cpu_ldw_code_mmu(CPUArchState *env, vaddr addr, - MemOpIdx oi, uintptr_t ra); -uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr, - MemOpIdx oi, uintptr_t ra); -uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr, - MemOpIdx oi, uintptr_t ra); - uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr); uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr); uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr); From efe25c260cd69dcfc948e1622bedbdec953569a8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 20:04:59 -0700 Subject: [PATCH 0103/2760] include/exec: Split out accel/tcg/cpu-mmu-index.h The implementation of cpu_mmu_index was split between cpu-common.h and cpu-all.h, depending on CONFIG_USER_ONLY. We already have the plumbing common to user and system mode. Using MMU_USER_IDX requires the cpu.h for a specific target, and so is restricted to when we're compiling per-target. Include the new header only where needed. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/accel/tcg/cpu-mmu-index.h | 41 +++++++++++++++++++++++++++++++ include/exec/cpu-all.h | 6 ----- include/exec/cpu-common.h | 20 --------------- include/exec/cpu_ldst.h | 1 + semihosting/uaccess.c | 1 + target/arm/gdbstub64.c | 3 +++ target/hppa/mem_helper.c | 1 + target/i386/tcg/translate.c | 1 + target/loongarch/cpu_helper.c | 1 + target/microblaze/helper.c | 1 + target/microblaze/mmu.c | 1 + target/openrisc/translate.c | 1 + target/sparc/cpu.c | 1 + target/sparc/mmu_helper.c | 1 + target/tricore/helper.c | 1 + target/xtensa/mmu_helper.c | 1 + 16 files changed, 56 insertions(+), 26 deletions(-) create mode 100644 include/accel/tcg/cpu-mmu-index.h diff --git a/include/accel/tcg/cpu-mmu-index.h b/include/accel/tcg/cpu-mmu-index.h new file mode 100644 index 0000000000..8d1cb53bfa --- /dev/null +++ b/include/accel/tcg/cpu-mmu-index.h @@ -0,0 +1,41 @@ +/* + * cpu_mmu_index() + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_CPU_MMU_INDEX_H +#define ACCEL_TCG_CPU_MMU_INDEX_H + +#include "hw/core/cpu.h" +#include "tcg/debug-assert.h" +#ifdef COMPILING_PER_TARGET +# ifdef CONFIG_USER_ONLY +# include "cpu.h" +# endif +#endif + +/** + * cpu_mmu_index: + * @env: The cpu environment + * @ifetch: True for code access, false for data access. + * + * Return the core mmu index for the current translation regime. + * This function is used by generic TCG code paths. + */ +static inline int cpu_mmu_index(CPUState *cs, bool ifetch) +{ +#ifdef COMPILING_PER_TARGET +# ifdef CONFIG_USER_ONLY + return MMU_USER_IDX; +# endif +#endif + + int ret = cs->cc->mmu_index(cs, ifetch); + tcg_debug_assert(ret >= 0 && ret < NB_MMU_MODES); + return ret; +} + +#endif /* ACCEL_TCG_CPU_MMU_INDEX_H */ diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 66a4252269..33b9dc81eb 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -34,8 +34,6 @@ CPUArchState *cpu_copy(CPUArchState *env); #ifdef CONFIG_USER_ONLY -static inline int cpu_mmu_index(CPUState *cs, bool ifetch); - /* * Allow some level of source compatibility with softmmu. We do not * support any of the more exotic features, so only invalid pages may @@ -45,10 +43,6 @@ static inline int cpu_mmu_index(CPUState *cs, bool ifetch); #define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 2)) #define TLB_WATCHPOINT 0 -static inline int cpu_mmu_index(CPUState *cs, bool ifetch) -{ - return MMU_USER_IDX; -} #else /* diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 3771b2130c..be032e1a49 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -272,24 +272,4 @@ static inline CPUState *env_cpu(CPUArchState *env) return (CPUState *)env_cpu_const(env); } -#ifndef CONFIG_USER_ONLY -/** - * cpu_mmu_index: - * @env: The cpu environment - * @ifetch: True for code access, false for data access. - * - * Return the core mmu index for the current translation regime. - * This function is used by generic TCG code paths. - * - * The user-only version of this function is inline in cpu-all.h, - * where it always returns MMU_USER_IDX. - */ -static inline int cpu_mmu_index(CPUState *cs, bool ifetch) -{ - int ret = cs->cc->mmu_index(cs, ifetch); - tcg_debug_assert(ret >= 0 && ret < NB_MMU_MODES); - return ret; -} -#endif /* !CONFIG_USER_ONLY */ - #endif /* CPU_COMMON_H */ diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 1fbdbe59ae..740f5d937f 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -67,6 +67,7 @@ #endif #include "exec/cpu-ldst-common.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/abi_ptr.h" #if defined(CONFIG_USER_ONLY) diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index 382a366ce3..2e33596428 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "exec/cpu-all.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/exec-all.h" #include "semihosting/uaccess.h" diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 1a4dbec567..be38016fc7 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -27,6 +27,9 @@ #include #include "mte_user_helper.h" #endif +#ifdef CONFIG_TCG +#include "accel/tcg/cpu-mmu-index.h" +#endif int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) { diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index fb1d93ef1f..a1ade9079e 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/cputlb.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/page-protection.h" #include "exec/helper-proto.h" #include "hw/core/cpu.h" diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index abe210cc4e..6418d4bb03 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -20,6 +20,7 @@ #include "qemu/host-utils.h" #include "cpu.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/exec-all.h" #include "exec/translation-block.h" #include "tcg/tcg-op.h" diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 930466ca48..f8965cd155 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -8,6 +8,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "accel/tcg/cpu-mmu-index.h" #include "internals.h" #include "cpu-csr.h" diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index 27fc929bee..022c98f0c3 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -21,6 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/cputlb.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/page-protection.h" #include "qemu/host-utils.h" #include "exec/log.h" diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index f8587d5ac4..2d18659b99 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -22,6 +22,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/cputlb.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/page-protection.h" static unsigned int tlb_decode_size(unsigned int f) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index 7a6af183ae..da033bffff 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "qemu/log.h" diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 5716120117..57fbf16ad2 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "qemu/module.h" #include "qemu/qemu-print.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/exec-all.h" #include "exec/translation-block.h" #include "hw/qdev-properties.h" diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index 3821cd91ec..78cb24a8e2 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -21,6 +21,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/cputlb.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/page-protection.h" #include "qemu/qemu-print.h" #include "trace.h" diff --git a/target/tricore/helper.c b/target/tricore/helper.c index a64412e6bd..b1ee126112 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -20,6 +20,7 @@ #include "hw/registerfields.h" #include "cpu.h" #include "exec/cputlb.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/page-protection.h" #include "fpu/softfloat-helpers.h" #include "qemu/qemu-print.h" diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c index 63be741a42..40b02f0a2c 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -33,6 +33,7 @@ #include "exec/helper-proto.h" #include "qemu/host-utils.h" #include "exec/cputlb.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/exec-all.h" #include "exec/page-protection.h" From a333692c48c916f0416b982ecefdcc9452088b41 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Mar 2025 13:45:24 -0700 Subject: [PATCH 0104/2760] include/exec: Inline *_mmuidx_ra memory operations These need to be per-target for 'abi_ptr'. Expand inline to the *_mmu api with trivial massaging of the arguments. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/ldst_common.c.inc | 118 -------------------------- include/exec/cpu_ldst.h | 165 ++++++++++++++++++++++++++++-------- 2 files changed, 130 insertions(+), 153 deletions(-) diff --git a/accel/tcg/ldst_common.c.inc b/accel/tcg/ldst_common.c.inc index 0447c0bb92..99a56df3fb 100644 --- a/accel/tcg/ldst_common.c.inc +++ b/accel/tcg/ldst_common.c.inc @@ -248,124 +248,6 @@ void cpu_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, * Wrappers of the above */ -uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - return cpu_ldb_mmu(env, addr, oi, ra); -} - -int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return (int8_t)cpu_ldub_mmuidx_ra(env, addr, mmu_idx, ra); -} - -uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx); - return cpu_ldw_mmu(env, addr, oi, ra); -} - -int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return (int16_t)cpu_lduw_be_mmuidx_ra(env, addr, mmu_idx, ra); -} - -uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx); - return cpu_ldl_mmu(env, addr, oi, ra); -} - -uint64_t cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_BEUQ | MO_UNALN, mmu_idx); - return cpu_ldq_mmu(env, addr, oi, ra); -} - -uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx); - return cpu_ldw_mmu(env, addr, oi, ra); -} - -int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - return (int16_t)cpu_lduw_le_mmuidx_ra(env, addr, mmu_idx, ra); -} - -uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx); - return cpu_ldl_mmu(env, addr, oi, ra); -} - -uint64_t cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx); - return cpu_ldq_mmu(env, addr, oi, ra); -} - -void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); - cpu_stb_mmu(env, addr, val, oi, ra); -} - -void cpu_stw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx); - cpu_stw_mmu(env, addr, val, oi, ra); -} - -void cpu_stl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx); - cpu_stl_mmu(env, addr, val, oi, ra); -} - -void cpu_stq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_BEUQ | MO_UNALN, mmu_idx); - cpu_stq_mmu(env, addr, val, oi, ra); -} - -void cpu_stw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx); - cpu_stw_mmu(env, addr, val, oi, ra); -} - -void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx); - cpu_stl_mmu(env, addr, val, oi, ra); -} - -void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, - int mmu_idx, uintptr_t ra) -{ - MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx); - cpu_stq_mmu(env, addr, val, oi, ra); -} - -/*--------------------------*/ - uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) { int mmu_index = cpu_mmu_index(env_cpu(env), false); diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 740f5d937f..8e8b9b53f7 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -119,41 +119,136 @@ void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr ptr, void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr ptr, uint64_t val, uintptr_t ra); -uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -int cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -uint32_t cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -int cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -uint32_t cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -uint64_t cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -uint32_t cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -int cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -uint32_t cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); -uint64_t cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, - int mmu_idx, uintptr_t ra); - -void cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, - int mmu_idx, uintptr_t ra); -void cpu_stw_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, - int mmu_idx, uintptr_t ra); -void cpu_stl_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, - int mmu_idx, uintptr_t ra); -void cpu_stq_be_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint64_t val, - int mmu_idx, uintptr_t ra); -void cpu_stw_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, - int mmu_idx, uintptr_t ra); -void cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint32_t val, - int mmu_idx, uintptr_t ra); -void cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr ptr, uint64_t val, - int mmu_idx, uintptr_t ra); +static inline uint32_t +cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + return cpu_ldb_mmu(env, addr, oi, ra); +} + +static inline int +cpu_ldsb_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) +{ + return (int8_t)cpu_ldub_mmuidx_ra(env, addr, mmu_idx, ra); +} + +static inline uint32_t +cpu_lduw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx); + return cpu_ldw_mmu(env, addr, oi, ra); +} + +static inline int +cpu_ldsw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return (int16_t)cpu_lduw_be_mmuidx_ra(env, addr, mmu_idx, ra); +} + +static inline uint32_t +cpu_ldl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx); + return cpu_ldl_mmu(env, addr, oi, ra); +} + +static inline uint64_t +cpu_ldq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_BEUQ | MO_UNALN, mmu_idx); + return cpu_ldq_mmu(env, addr, oi, ra); +} + +static inline uint32_t +cpu_lduw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx); + return cpu_ldw_mmu(env, addr, oi, ra); +} + +static inline int +cpu_ldsw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + return (int16_t)cpu_lduw_le_mmuidx_ra(env, addr, mmu_idx, ra); +} + +static inline uint32_t +cpu_ldl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx); + return cpu_ldl_mmu(env, addr, oi, ra); +} + +static inline uint64_t +cpu_ldq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx); + return cpu_ldq_mmu(env, addr, oi, ra); +} + +static inline void +cpu_stb_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_UB, mmu_idx); + cpu_stb_mmu(env, addr, val, oi, ra); +} + +static inline void +cpu_stw_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_BEUW | MO_UNALN, mmu_idx); + cpu_stw_mmu(env, addr, val, oi, ra); +} + +static inline void +cpu_stl_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_BEUL | MO_UNALN, mmu_idx); + cpu_stl_mmu(env, addr, val, oi, ra); +} + +static inline void +cpu_stq_be_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_BEUQ | MO_UNALN, mmu_idx); + cpu_stq_mmu(env, addr, val, oi, ra); +} + +static inline void +cpu_stw_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_LEUW | MO_UNALN, mmu_idx); + cpu_stw_mmu(env, addr, val, oi, ra); +} + +static inline void +cpu_stl_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint32_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_LEUL | MO_UNALN, mmu_idx); + cpu_stl_mmu(env, addr, val, oi, ra); +} + +static inline void +cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, + int mmu_idx, uintptr_t ra) +{ + MemOpIdx oi = make_memop_idx(MO_LEUQ | MO_UNALN, mmu_idx); + cpu_stq_mmu(env, addr, val, oi, ra); +} #if TARGET_BIG_ENDIAN # define cpu_lduw_data cpu_lduw_be_data From ef6ffba62afabd1a7d42bbec93d299799576c7af Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Mar 2025 13:56:02 -0700 Subject: [PATCH 0105/2760] include/exec: Inline *_data_ra memory operations These need to be per-target for 'abi_ptr'. Expand inline to the *_mmuidx_ra api with a lookup of the target's cpu_mmu_index(). Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/ldst_common.c.inc | 108 --------------------------- include/exec/cpu_ldst.h | 144 +++++++++++++++++++++++++++++------- 2 files changed, 118 insertions(+), 134 deletions(-) diff --git a/accel/tcg/ldst_common.c.inc b/accel/tcg/ldst_common.c.inc index 99a56df3fb..2f203290db 100644 --- a/accel/tcg/ldst_common.c.inc +++ b/accel/tcg/ldst_common.c.inc @@ -248,114 +248,6 @@ void cpu_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, * Wrappers of the above */ -uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - return cpu_ldub_mmuidx_ra(env, addr, mmu_index, ra); -} - -int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) -{ - return (int8_t)cpu_ldub_data_ra(env, addr, ra); -} - -uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - return cpu_lduw_be_mmuidx_ra(env, addr, mmu_index, ra); -} - -int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) -{ - return (int16_t)cpu_lduw_be_data_ra(env, addr, ra); -} - -uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - return cpu_ldl_be_mmuidx_ra(env, addr, mmu_index, ra); -} - -uint64_t cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - return cpu_ldq_be_mmuidx_ra(env, addr, mmu_index, ra); -} - -uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - return cpu_lduw_le_mmuidx_ra(env, addr, mmu_index, ra); -} - -int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) -{ - return (int16_t)cpu_lduw_le_data_ra(env, addr, ra); -} - -uint32_t cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - return cpu_ldl_le_mmuidx_ra(env, addr, mmu_index, ra); -} - -uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - return cpu_ldq_le_mmuidx_ra(env, addr, mmu_index, ra); -} - -void cpu_stb_data_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - cpu_stb_mmuidx_ra(env, addr, val, mmu_index, ra); -} - -void cpu_stw_be_data_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - cpu_stw_be_mmuidx_ra(env, addr, val, mmu_index, ra); -} - -void cpu_stl_be_data_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - cpu_stl_be_mmuidx_ra(env, addr, val, mmu_index, ra); -} - -void cpu_stq_be_data_ra(CPUArchState *env, abi_ptr addr, - uint64_t val, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - cpu_stq_be_mmuidx_ra(env, addr, val, mmu_index, ra); -} - -void cpu_stw_le_data_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - cpu_stw_le_mmuidx_ra(env, addr, val, mmu_index, ra); -} - -void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr addr, - uint32_t val, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - cpu_stl_le_mmuidx_ra(env, addr, val, mmu_index, ra); -} - -void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr addr, - uint64_t val, uintptr_t ra) -{ - int mmu_index = cpu_mmu_index(env_cpu(env), false); - cpu_stq_le_mmuidx_ra(env, addr, val, mmu_index, ra); -} - -/*--------------------------*/ - uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr addr) { return cpu_ldub_data_ra(env, addr, 0); diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 8e8b9b53f7..2eda652a38 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -85,17 +85,6 @@ int cpu_ldsw_le_data(CPUArchState *env, abi_ptr ptr); uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr); uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr ptr); -uint32_t cpu_ldub_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -int cpu_ldsb_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -uint32_t cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -int cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -uint32_t cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -uint64_t cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -uint32_t cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -int cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -uint32_t cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); -uint64_t cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr ptr, uintptr_t ra); - void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val); void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val); void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val); @@ -104,21 +93,6 @@ void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val); void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val); void cpu_stq_le_data(CPUArchState *env, abi_ptr ptr, uint64_t val); -void cpu_stb_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t ra); -void cpu_stw_be_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t ra); -void cpu_stl_be_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t ra); -void cpu_stq_be_data_ra(CPUArchState *env, abi_ptr ptr, - uint64_t val, uintptr_t ra); -void cpu_stw_le_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t ra); -void cpu_stl_le_data_ra(CPUArchState *env, abi_ptr ptr, - uint32_t val, uintptr_t ra); -void cpu_stq_le_data_ra(CPUArchState *env, abi_ptr ptr, - uint64_t val, uintptr_t ra); - static inline uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) { @@ -250,6 +224,124 @@ cpu_stq_le_mmuidx_ra(CPUArchState *env, abi_ptr addr, uint64_t val, cpu_stq_mmu(env, addr, val, oi, ra); } +/*--------------------------*/ + +static inline uint32_t +cpu_ldub_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldub_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline int +cpu_ldsb_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return (int8_t)cpu_ldub_data_ra(env, addr, ra); +} + +static inline uint32_t +cpu_lduw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_lduw_be_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline int +cpu_ldsw_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return (int16_t)cpu_lduw_be_data_ra(env, addr, ra); +} + +static inline uint32_t +cpu_ldl_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldl_be_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline uint64_t +cpu_ldq_be_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldq_be_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline uint32_t +cpu_lduw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_lduw_le_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline int +cpu_ldsw_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + return (int16_t)cpu_lduw_le_data_ra(env, addr, ra); +} + +static inline uint32_t +cpu_ldl_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldl_le_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline uint64_t +cpu_ldq_le_data_ra(CPUArchState *env, abi_ptr addr, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + return cpu_ldq_le_mmuidx_ra(env, addr, mmu_index, ra); +} + +static inline void +cpu_stb_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stb_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +static inline void +cpu_stw_be_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stw_be_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +static inline void +cpu_stl_be_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stl_be_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +static inline void +cpu_stq_be_data_ra(CPUArchState *env, abi_ptr addr, uint64_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stq_be_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +static inline void +cpu_stw_le_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stw_le_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +static inline void +cpu_stl_le_data_ra(CPUArchState *env, abi_ptr addr, uint32_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stl_le_mmuidx_ra(env, addr, val, mmu_index, ra); +} + +static inline void +cpu_stq_le_data_ra(CPUArchState *env, abi_ptr addr, uint64_t val, uintptr_t ra) +{ + int mmu_index = cpu_mmu_index(env_cpu(env), false); + cpu_stq_le_mmuidx_ra(env, addr, val, mmu_index, ra); +} + #if TARGET_BIG_ENDIAN # define cpu_lduw_data cpu_lduw_be_data # define cpu_ldsw_data cpu_ldsw_be_data From 0a29f11676d5b834f980a818d35b23d20d7ea226 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Mar 2025 14:08:55 -0700 Subject: [PATCH 0106/2760] include/exec: Inline *_data memory operations These need to be per-target for 'abi_ptr'. Expand inline to the *_data_ra api with ra == 0. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/ldst_common.c.inc | 89 -------------------------- include/exec/cpu_ldst.h | 123 ++++++++++++++++++++++++++++++------ 2 files changed, 104 insertions(+), 108 deletions(-) diff --git a/accel/tcg/ldst_common.c.inc b/accel/tcg/ldst_common.c.inc index 2f203290db..9791a4e9ef 100644 --- a/accel/tcg/ldst_common.c.inc +++ b/accel/tcg/ldst_common.c.inc @@ -243,92 +243,3 @@ void cpu_st16_mmu(CPUArchState *env, vaddr addr, Int128 val, do_st16_mmu(env_cpu(env), addr, val, oi, retaddr); plugin_store_cb(env, addr, int128_getlo(val), int128_gethi(val), oi); } - -/* - * Wrappers of the above - */ - -uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr addr) -{ - return cpu_ldub_data_ra(env, addr, 0); -} - -int cpu_ldsb_data(CPUArchState *env, abi_ptr addr) -{ - return (int8_t)cpu_ldub_data(env, addr); -} - -uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr addr) -{ - return cpu_lduw_be_data_ra(env, addr, 0); -} - -int cpu_ldsw_be_data(CPUArchState *env, abi_ptr addr) -{ - return (int16_t)cpu_lduw_be_data(env, addr); -} - -uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr addr) -{ - return cpu_ldl_be_data_ra(env, addr, 0); -} - -uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr addr) -{ - return cpu_ldq_be_data_ra(env, addr, 0); -} - -uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr addr) -{ - return cpu_lduw_le_data_ra(env, addr, 0); -} - -int cpu_ldsw_le_data(CPUArchState *env, abi_ptr addr) -{ - return (int16_t)cpu_lduw_le_data(env, addr); -} - -uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr addr) -{ - return cpu_ldl_le_data_ra(env, addr, 0); -} - -uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr addr) -{ - return cpu_ldq_le_data_ra(env, addr, 0); -} - -void cpu_stb_data(CPUArchState *env, abi_ptr addr, uint32_t val) -{ - cpu_stb_data_ra(env, addr, val, 0); -} - -void cpu_stw_be_data(CPUArchState *env, abi_ptr addr, uint32_t val) -{ - cpu_stw_be_data_ra(env, addr, val, 0); -} - -void cpu_stl_be_data(CPUArchState *env, abi_ptr addr, uint32_t val) -{ - cpu_stl_be_data_ra(env, addr, val, 0); -} - -void cpu_stq_be_data(CPUArchState *env, abi_ptr addr, uint64_t val) -{ - cpu_stq_be_data_ra(env, addr, val, 0); -} - -void cpu_stw_le_data(CPUArchState *env, abi_ptr addr, uint32_t val) -{ - cpu_stw_le_data_ra(env, addr, val, 0); -} - -void cpu_stl_le_data(CPUArchState *env, abi_ptr addr, uint32_t val) -{ - cpu_stl_le_data_ra(env, addr, val, 0); -} - -void cpu_stq_le_data(CPUArchState *env, abi_ptr addr, uint64_t val) -{ - cpu_stq_le_data_ra(env, addr, val, 0); -} diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 2eda652a38..0054508eda 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -74,25 +74,6 @@ #include "user/guest-host.h" #endif /* CONFIG_USER_ONLY */ -uint32_t cpu_ldub_data(CPUArchState *env, abi_ptr ptr); -int cpu_ldsb_data(CPUArchState *env, abi_ptr ptr); -uint32_t cpu_lduw_be_data(CPUArchState *env, abi_ptr ptr); -int cpu_ldsw_be_data(CPUArchState *env, abi_ptr ptr); -uint32_t cpu_ldl_be_data(CPUArchState *env, abi_ptr ptr); -uint64_t cpu_ldq_be_data(CPUArchState *env, abi_ptr ptr); -uint32_t cpu_lduw_le_data(CPUArchState *env, abi_ptr ptr); -int cpu_ldsw_le_data(CPUArchState *env, abi_ptr ptr); -uint32_t cpu_ldl_le_data(CPUArchState *env, abi_ptr ptr); -uint64_t cpu_ldq_le_data(CPUArchState *env, abi_ptr ptr); - -void cpu_stb_data(CPUArchState *env, abi_ptr ptr, uint32_t val); -void cpu_stw_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val); -void cpu_stl_be_data(CPUArchState *env, abi_ptr ptr, uint32_t val); -void cpu_stq_be_data(CPUArchState *env, abi_ptr ptr, uint64_t val); -void cpu_stw_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val); -void cpu_stl_le_data(CPUArchState *env, abi_ptr ptr, uint32_t val); -void cpu_stq_le_data(CPUArchState *env, abi_ptr ptr, uint64_t val); - static inline uint32_t cpu_ldub_mmuidx_ra(CPUArchState *env, abi_ptr addr, int mmu_idx, uintptr_t ra) { @@ -342,6 +323,110 @@ cpu_stq_le_data_ra(CPUArchState *env, abi_ptr addr, uint64_t val, uintptr_t ra) cpu_stq_le_mmuidx_ra(env, addr, val, mmu_index, ra); } +/*--------------------------*/ + +static inline uint32_t +cpu_ldub_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldub_data_ra(env, addr, 0); +} + +static inline int +cpu_ldsb_data(CPUArchState *env, abi_ptr addr) +{ + return (int8_t)cpu_ldub_data(env, addr); +} + +static inline uint32_t +cpu_lduw_be_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_lduw_be_data_ra(env, addr, 0); +} + +static inline int +cpu_ldsw_be_data(CPUArchState *env, abi_ptr addr) +{ + return (int16_t)cpu_lduw_be_data(env, addr); +} + +static inline uint32_t +cpu_ldl_be_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldl_be_data_ra(env, addr, 0); +} + +static inline uint64_t +cpu_ldq_be_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldq_be_data_ra(env, addr, 0); +} + +static inline uint32_t +cpu_lduw_le_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_lduw_le_data_ra(env, addr, 0); +} + +static inline int +cpu_ldsw_le_data(CPUArchState *env, abi_ptr addr) +{ + return (int16_t)cpu_lduw_le_data(env, addr); +} + +static inline uint32_t +cpu_ldl_le_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldl_le_data_ra(env, addr, 0); +} + +static inline uint64_t +cpu_ldq_le_data(CPUArchState *env, abi_ptr addr) +{ + return cpu_ldq_le_data_ra(env, addr, 0); +} + +static inline void +cpu_stb_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stb_data_ra(env, addr, val, 0); +} + +static inline void +cpu_stw_be_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stw_be_data_ra(env, addr, val, 0); +} + +static inline void +cpu_stl_be_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stl_be_data_ra(env, addr, val, 0); +} + +static inline void +cpu_stq_be_data(CPUArchState *env, abi_ptr addr, uint64_t val) +{ + cpu_stq_be_data_ra(env, addr, val, 0); +} + +static inline void +cpu_stw_le_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stw_le_data_ra(env, addr, val, 0); +} + +static inline void +cpu_stl_le_data(CPUArchState *env, abi_ptr addr, uint32_t val) +{ + cpu_stl_le_data_ra(env, addr, val, 0); +} + +static inline void +cpu_stq_le_data(CPUArchState *env, abi_ptr addr, uint64_t val) +{ + cpu_stq_le_data_ra(env, addr, val, 0); +} + #if TARGET_BIG_ENDIAN # define cpu_lduw_data cpu_lduw_be_data # define cpu_ldsw_data cpu_ldsw_be_data From 4ba597c138306450e7fcc50d2dba3bfdad8478c8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Mar 2025 14:34:28 -0700 Subject: [PATCH 0107/2760] include/exec: Inline *_code memory operations These need to be per-target for 'abi_ptr' and endianness. These expand inline to the *_mmu api with a lookup of the target's cpu_mmu_index() and ra == 0. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 28 ---------------------------- accel/tcg/user-exec.c | 40 ---------------------------------------- include/exec/cpu_ldst.h | 31 +++++++++++++++++++++++++++---- 3 files changed, 27 insertions(+), 72 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index b03998f926..2817c9dbdd 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2897,34 +2897,6 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val, /* Code access functions. */ -uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr) -{ - CPUState *cs = env_cpu(env); - MemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(cs, true)); - return do_ld1_mmu(cs, addr, oi, 0, MMU_INST_FETCH); -} - -uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr) -{ - CPUState *cs = env_cpu(env); - MemOpIdx oi = make_memop_idx(MO_TEUW, cpu_mmu_index(cs, true)); - return do_ld2_mmu(cs, addr, oi, 0, MMU_INST_FETCH); -} - -uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr) -{ - CPUState *cs = env_cpu(env); - MemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(cs, true)); - return do_ld4_mmu(cs, addr, oi, 0, MMU_INST_FETCH); -} - -uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr) -{ - CPUState *cs = env_cpu(env); - MemOpIdx oi = make_memop_idx(MO_TEUQ, cpu_mmu_index(cs, true)); - return do_ld8_mmu(cs, addr, oi, 0, MMU_INST_FETCH); -} - uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t retaddr) { diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index dec17435c5..ebc7c3ecf5 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -1214,46 +1214,6 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val, clear_helper_retaddr(); } -uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr ptr) -{ - uint32_t ret; - - set_helper_retaddr(1); - ret = ldub_p(g2h_untagged(ptr)); - clear_helper_retaddr(); - return ret; -} - -uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr ptr) -{ - uint32_t ret; - - set_helper_retaddr(1); - ret = lduw_p(g2h_untagged(ptr)); - clear_helper_retaddr(); - return ret; -} - -uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr ptr) -{ - uint32_t ret; - - set_helper_retaddr(1); - ret = ldl_p(g2h_untagged(ptr)); - clear_helper_retaddr(); - return ret; -} - -uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr ptr) -{ - uint64_t ret; - - set_helper_retaddr(1); - ret = ldq_p(g2h_untagged(ptr)); - clear_helper_retaddr(); - return ret; -} - uint8_t cpu_ldb_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra) { diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 0054508eda..77dc5ac61c 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -473,10 +473,33 @@ cpu_stq_le_data(CPUArchState *env, abi_ptr addr, uint64_t val) # define cpu_stq_mmuidx_ra cpu_stq_le_mmuidx_ra #endif -uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr); -uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr); -uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr); -uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr); +static inline uint32_t cpu_ldub_code(CPUArchState *env, abi_ptr addr) +{ + CPUState *cs = env_cpu(env); + MemOpIdx oi = make_memop_idx(MO_UB, cpu_mmu_index(cs, true)); + return cpu_ldb_code_mmu(env, addr, oi, 0); +} + +static inline uint32_t cpu_lduw_code(CPUArchState *env, abi_ptr addr) +{ + CPUState *cs = env_cpu(env); + MemOpIdx oi = make_memop_idx(MO_TEUW, cpu_mmu_index(cs, true)); + return cpu_ldw_code_mmu(env, addr, oi, 0); +} + +static inline uint32_t cpu_ldl_code(CPUArchState *env, abi_ptr addr) +{ + CPUState *cs = env_cpu(env); + MemOpIdx oi = make_memop_idx(MO_TEUL, cpu_mmu_index(cs, true)); + return cpu_ldl_code_mmu(env, addr, oi, 0); +} + +static inline uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr) +{ + CPUState *cs = env_cpu(env); + MemOpIdx oi = make_memop_idx(MO_TEUQ, cpu_mmu_index(cs, true)); + return cpu_ldq_code_mmu(env, addr, oi, 0); +} /** * tlb_vaddr_to_host: From 6a9dfe1984b0c593fb0ddb52d4e70832e6201dd6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Mar 2025 18:34:58 -0700 Subject: [PATCH 0108/2760] accel/tcg: Perform aligned atomic reads in translator_ld Perform aligned atomic reads in translator_ld, if possible. According to https://lore.kernel.org/qemu-devel/20240607101403.1109-1-jim.shu@sifive.com/ this is required for RISC-V Ziccif. Reviewed-by: Alistair Francis Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 42 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index ef1538b4fc..157be33bf6 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -265,12 +265,14 @@ static bool translator_ld(CPUArchState *env, DisasContextBase *db, if (likely(((base ^ last) & TARGET_PAGE_MASK) == 0)) { /* Entire read is from the first page. */ - memcpy(dest, host + (pc - base), len); - return true; + goto do_read; } if (unlikely(((base ^ pc) & TARGET_PAGE_MASK) == 0)) { - /* Read begins on the first page and extends to the second. */ + /* + * Read begins on the first page and extends to the second. + * The unaligned read is never atomic. + */ size_t len0 = -(pc | TARGET_PAGE_MASK); memcpy(dest, host + (pc - base), len0); pc += len0; @@ -329,7 +331,39 @@ static bool translator_ld(CPUArchState *env, DisasContextBase *db, host = db->host_addr[1]; } - memcpy(dest, host + (pc - base), len); + do_read: + /* + * Assume aligned reads should be atomic, if possible. + * We're not in a position to jump out with EXCP_ATOMIC. + */ + host += pc - base; + switch (len) { + case 2: + if (QEMU_IS_ALIGNED(pc, 2)) { + uint16_t t = qatomic_read((uint16_t *)host); + stw_he_p(dest, t); + return true; + } + break; + case 4: + if (QEMU_IS_ALIGNED(pc, 4)) { + uint32_t t = qatomic_read((uint32_t *)host); + stl_he_p(dest, t); + return true; + } + break; +#ifdef CONFIG_ATOMIC64 + case 8: + if (QEMU_IS_ALIGNED(pc, 8)) { + uint64_t t = qatomic_read__nocheck((uint64_t *)host); + stq_he_p(dest, t); + return true; + } + break; +#endif + } + /* Unaligned or partial read from the second page is not atomic. */ + memcpy(dest, host, len); return true; } From 028119c8baedb091e4f358a6176710507b4ce37d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Mar 2025 20:21:26 -0700 Subject: [PATCH 0109/2760] accel/tcg: Use cpu_ld*_code_mmu in translator.c Cache the mmu index in DisasContextBase. Perform the read on host endianness, which lets us share code with the translator_ld fast path. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 58 ++++++++++++++++++--------------------- include/exec/translator.h | 1 + 2 files changed, 28 insertions(+), 31 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 157be33bf6..4c320ab9c3 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -11,10 +11,10 @@ #include "qemu/log.h" #include "qemu/error-report.h" #include "exec/exec-all.h" +#include "exec/cpu-ldst-common.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/translator.h" -#include "exec/cpu_ldst.h" #include "exec/plugin-gen.h" -#include "exec/cpu_ldst.h" #include "exec/tswap.h" #include "tcg/tcg-op-common.h" #include "internal-target.h" @@ -142,6 +142,7 @@ void translator_loop(CPUState *cpu, TranslationBlock *tb, int *max_insns, db->host_addr[1] = NULL; db->record_start = 0; db->record_len = 0; + db->code_mmuidx = cpu_mmu_index(cpu, true); ops->init_disas_context(db, cpu); tcg_debug_assert(db->is_jmp == DISAS_NEXT); /* no early exit */ @@ -457,55 +458,50 @@ bool translator_st(const DisasContextBase *db, void *dest, uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc) { - uint8_t raw; + uint8_t val; - if (!translator_ld(env, db, &raw, pc, sizeof(raw))) { - raw = cpu_ldub_code(env, pc); - record_save(db, pc, &raw, sizeof(raw)); + if (!translator_ld(env, db, &val, pc, sizeof(val))) { + MemOpIdx oi = make_memop_idx(MO_UB, db->code_mmuidx); + val = cpu_ldb_code_mmu(env, pc, oi, 0); + record_save(db, pc, &val, sizeof(val)); } - return raw; + return val; } uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc) { - uint16_t raw, tgt; + uint16_t val; - if (translator_ld(env, db, &raw, pc, sizeof(raw))) { - tgt = tswap16(raw); - } else { - tgt = cpu_lduw_code(env, pc); - raw = tswap16(tgt); - record_save(db, pc, &raw, sizeof(raw)); + if (!translator_ld(env, db, &val, pc, sizeof(val))) { + MemOpIdx oi = make_memop_idx(MO_UW, db->code_mmuidx); + val = cpu_ldw_code_mmu(env, pc, oi, 0); + record_save(db, pc, &val, sizeof(val)); } - return tgt; + return tswap16(val); } uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc) { - uint32_t raw, tgt; + uint32_t val; - if (translator_ld(env, db, &raw, pc, sizeof(raw))) { - tgt = tswap32(raw); - } else { - tgt = cpu_ldl_code(env, pc); - raw = tswap32(tgt); - record_save(db, pc, &raw, sizeof(raw)); + if (!translator_ld(env, db, &val, pc, sizeof(val))) { + MemOpIdx oi = make_memop_idx(MO_UL, db->code_mmuidx); + val = cpu_ldl_code_mmu(env, pc, oi, 0); + record_save(db, pc, &val, sizeof(val)); } - return tgt; + return tswap32(val); } uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) { - uint64_t raw, tgt; + uint64_t val; - if (translator_ld(env, db, &raw, pc, sizeof(raw))) { - tgt = tswap64(raw); - } else { - tgt = cpu_ldq_code(env, pc); - raw = tswap64(tgt); - record_save(db, pc, &raw, sizeof(raw)); + if (!translator_ld(env, db, &val, pc, sizeof(val))) { + MemOpIdx oi = make_memop_idx(MO_UQ, db->code_mmuidx); + val = cpu_ldq_code_mmu(env, pc, oi, 0); + record_save(db, pc, &val, sizeof(val)); } - return tgt; + return tswap64(val); } void translator_fake_ld(DisasContextBase *db, const void *data, size_t len) diff --git a/include/exec/translator.h b/include/exec/translator.h index d70942a10f..205dd85bba 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -73,6 +73,7 @@ struct DisasContextBase { int max_insns; bool plugin_enabled; bool fake_insn; + uint8_t code_mmuidx; struct TCGOp *insn_start; void *host_addr[2]; From 5c43a750b67d803588e0743e571ec055dbe6488f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 06:04:13 -0700 Subject: [PATCH 0110/2760] accel/tcg: Implement translator_ld*_end Add a new family of translator load functions which take an absolute endianness value in the form of MO_BE/MO_LE. Expand the other translator_ld* functions on top of this. Remove exec/tswap.h from translator.c. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 26 +++++++++++++++------ include/exec/translator.h | 49 ++++++++++++++++++++++++--------------- 2 files changed, 49 insertions(+), 26 deletions(-) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 4c320ab9c3..2ab081b95f 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" +#include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/error-report.h" #include "exec/exec-all.h" @@ -15,7 +16,6 @@ #include "accel/tcg/cpu-mmu-index.h" #include "exec/translator.h" #include "exec/plugin-gen.h" -#include "exec/tswap.h" #include "tcg/tcg-op-common.h" #include "internal-target.h" #include "disas/disas.h" @@ -468,7 +468,8 @@ uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc) return val; } -uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc) +uint16_t translator_lduw_end(CPUArchState *env, DisasContextBase *db, + vaddr pc, MemOp endian) { uint16_t val; @@ -477,10 +478,14 @@ uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc) val = cpu_ldw_code_mmu(env, pc, oi, 0); record_save(db, pc, &val, sizeof(val)); } - return tswap16(val); + if (endian & MO_BSWAP) { + val = bswap16(val); + } + return val; } -uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc) +uint32_t translator_ldl_end(CPUArchState *env, DisasContextBase *db, + vaddr pc, MemOp endian) { uint32_t val; @@ -489,10 +494,14 @@ uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc) val = cpu_ldl_code_mmu(env, pc, oi, 0); record_save(db, pc, &val, sizeof(val)); } - return tswap32(val); + if (endian & MO_BSWAP) { + val = bswap32(val); + } + return val; } -uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) +uint64_t translator_ldq_end(CPUArchState *env, DisasContextBase *db, + vaddr pc, MemOp endian) { uint64_t val; @@ -501,7 +510,10 @@ uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) val = cpu_ldq_code_mmu(env, pc, oi, 0); record_save(db, pc, &val, sizeof(val)); } - return tswap64(val); + if (endian & MO_BSWAP) { + val = bswap64(val); + } + return val; } void translator_fake_ld(DisasContextBase *db, const void *data, size_t len) diff --git a/include/exec/translator.h b/include/exec/translator.h index 205dd85bba..3c32655569 100644 --- a/include/exec/translator.h +++ b/include/exec/translator.h @@ -18,7 +18,7 @@ * member in your target-specific DisasContext. */ -#include "qemu/bswap.h" +#include "exec/memop.h" #include "exec/vaddr.h" /** @@ -181,42 +181,53 @@ bool translator_io_start(DisasContextBase *db); */ uint8_t translator_ldub(CPUArchState *env, DisasContextBase *db, vaddr pc); -uint16_t translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc); -uint32_t translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc); -uint64_t translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc); +uint16_t translator_lduw_end(CPUArchState *env, DisasContextBase *db, + vaddr pc, MemOp endian); +uint32_t translator_ldl_end(CPUArchState *env, DisasContextBase *db, + vaddr pc, MemOp endian); +uint64_t translator_ldq_end(CPUArchState *env, DisasContextBase *db, + vaddr pc, MemOp endian); + +#ifdef COMPILING_PER_TARGET +static inline uint16_t +translator_lduw(CPUArchState *env, DisasContextBase *db, vaddr pc) +{ + return translator_lduw_end(env, db, pc, MO_TE); +} + +static inline uint32_t +translator_ldl(CPUArchState *env, DisasContextBase *db, vaddr pc) +{ + return translator_ldl_end(env, db, pc, MO_TE); +} + +static inline uint64_t +translator_ldq(CPUArchState *env, DisasContextBase *db, vaddr pc) +{ + return translator_ldq_end(env, db, pc, MO_TE); +} static inline uint16_t translator_lduw_swap(CPUArchState *env, DisasContextBase *db, vaddr pc, bool do_swap) { - uint16_t ret = translator_lduw(env, db, pc); - if (do_swap) { - ret = bswap16(ret); - } - return ret; + return translator_lduw_end(env, db, pc, MO_TE ^ (do_swap * MO_BSWAP)); } static inline uint32_t translator_ldl_swap(CPUArchState *env, DisasContextBase *db, vaddr pc, bool do_swap) { - uint32_t ret = translator_ldl(env, db, pc); - if (do_swap) { - ret = bswap32(ret); - } - return ret; + return translator_ldl_end(env, db, pc, MO_TE ^ (do_swap * MO_BSWAP)); } static inline uint64_t translator_ldq_swap(CPUArchState *env, DisasContextBase *db, vaddr pc, bool do_swap) { - uint64_t ret = translator_ldq(env, db, pc); - if (do_swap) { - ret = bswap64(ret); - } - return ret; + return translator_ldq_end(env, db, pc, MO_TE ^ (do_swap * MO_BSWAP)); } +#endif /* COMPILING_PER_TARGET */ /** * translator_fake_ld - fake instruction load From 87f8eb1d30050d6df6cbd0c61eee6dc836451370 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 10:37:23 -0700 Subject: [PATCH 0111/2760] accel/tcg: Remove mmap_lock/unlock from watchpoint.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The mmap_lock is user-only, whereas watchpoint.c is only compiled for system mode. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/watchpoint.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/accel/tcg/watchpoint.c b/accel/tcg/watchpoint.c index 65b21884ce..cfb37a49e7 100644 --- a/accel/tcg/watchpoint.c +++ b/accel/tcg/watchpoint.c @@ -124,17 +124,14 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, } cpu->watchpoint_hit = wp; - mmap_lock(); /* This call also restores vCPU state */ tb_check_watchpoint(cpu, ra); if (wp->flags & BP_STOP_BEFORE_ACCESS) { cpu->exception_index = EXCP_DEBUG; - mmap_unlock(); cpu_loop_exit(cpu); } else { /* Force execution of one insn next time. */ cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); - mmap_unlock(); cpu_loop_exit_noexc(cpu); } } else { From 4d3ad3c3ba1f1e9c217d0581e4913a59ef2ac15f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 10:36:39 -0700 Subject: [PATCH 0112/2760] include/exec: Split out mmap-lock.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split out mmap_lock, et al from page-protection.h to a new header. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 1 + accel/tcg/internal-target.h | 1 + accel/tcg/tb-maint.c | 1 + accel/tcg/translate-all.c | 1 + bsd-user/bsd-mem.h | 1 + bsd-user/mmap.c | 1 + include/exec/mmap-lock.h | 33 +++++++++++++++++++++++++++++++++ include/exec/page-protection.h | 22 ---------------------- linux-user/arm/cpu_loop.c | 1 + linux-user/elfload.c | 1 + linux-user/flatload.c | 1 + linux-user/mmap.c | 1 + linux-user/syscall.c | 1 + target/arm/helper.c | 1 + 14 files changed, 45 insertions(+), 22 deletions(-) create mode 100644 include/exec/mmap-lock.h diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index ef3d967e3a..372b876604 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -27,6 +27,7 @@ #include "disas/disas.h" #include "exec/cpu-common.h" #include "exec/page-protection.h" +#include "exec/mmap-lock.h" #include "exec/translation-block.h" #include "tcg/tcg.h" #include "qemu/atomic.h" diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index 2cdf11c905..c88f007ffb 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -13,6 +13,7 @@ #include "exec/translation-block.h" #include "tb-internal.h" #include "tcg-target-mo.h" +#include "exec/mmap-lock.h" /* * Access to the various translations structures need to be serialised diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 3f1bebf6ab..d5899ad047 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -24,6 +24,7 @@ #include "exec/log.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/mmap-lock.h" #include "exec/tb-flush.h" #include "tb-internal.h" #include "system/tcg.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 82bc16bd53..16e5043597 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -45,6 +45,7 @@ #include "exec/cputlb.h" #include "exec/page-protection.h" +#include "exec/mmap-lock.h" #include "tb-internal.h" #include "exec/translator.h" #include "exec/tb-flush.h" diff --git a/bsd-user/bsd-mem.h b/bsd-user/bsd-mem.h index 90ca0e3377..1be906c591 100644 --- a/bsd-user/bsd-mem.h +++ b/bsd-user/bsd-mem.h @@ -56,6 +56,7 @@ #include #include "qemu-bsd.h" +#include "exec/mmap-lock.h" #include "exec/page-protection.h" #include "user/page-protection.h" diff --git a/bsd-user/mmap.c b/bsd-user/mmap.c index 3f0df79c37..47e317517c 100644 --- a/bsd-user/mmap.c +++ b/bsd-user/mmap.c @@ -17,6 +17,7 @@ * along with this program; if not, see . */ #include "qemu/osdep.h" +#include "exec/mmap-lock.h" #include "exec/page-protection.h" #include "user/page-protection.h" diff --git a/include/exec/mmap-lock.h b/include/exec/mmap-lock.h new file mode 100644 index 0000000000..50ffdab9c5 --- /dev/null +++ b/include/exec/mmap-lock.h @@ -0,0 +1,33 @@ +/* + * QEMU user-only mmap lock, with stubs for system mode + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#ifndef EXEC_MMAP_LOCK_H +#define EXEC_MMAP_LOCK_H + +#ifdef CONFIG_USER_ONLY + +void TSA_NO_TSA mmap_lock(void); +void TSA_NO_TSA mmap_unlock(void); +bool have_mmap_lock(void); + +static inline void mmap_unlock_guard(void *unused) +{ + mmap_unlock(); +} + +#define WITH_MMAP_LOCK_GUARD() \ + for (int _mmap_lock_iter __attribute__((cleanup(mmap_unlock_guard))) \ + = (mmap_lock(), 0); _mmap_lock_iter == 0; _mmap_lock_iter = 1) + +#else + +static inline void mmap_lock(void) {} +static inline void mmap_unlock(void) {} +#define WITH_MMAP_LOCK_GUARD() + +#endif /* CONFIG_USER_ONLY */ +#endif /* EXEC_MMAP_LOCK_H */ diff --git a/include/exec/page-protection.h b/include/exec/page-protection.h index 3e0a8a0333..c43231af8b 100644 --- a/include/exec/page-protection.h +++ b/include/exec/page-protection.h @@ -38,26 +38,4 @@ */ #define PAGE_PASSTHROUGH 0x0800 -#ifdef CONFIG_USER_ONLY - -void TSA_NO_TSA mmap_lock(void); -void TSA_NO_TSA mmap_unlock(void); -bool have_mmap_lock(void); - -static inline void mmap_unlock_guard(void *unused) -{ - mmap_unlock(); -} - -#define WITH_MMAP_LOCK_GUARD() \ - for (int _mmap_lock_iter __attribute__((cleanup(mmap_unlock_guard))) \ - = (mmap_lock(), 0); _mmap_lock_iter == 0; _mmap_lock_iter = 1) -#else - -static inline void mmap_lock(void) {} -static inline void mmap_unlock(void) {} -#define WITH_MMAP_LOCK_GUARD() - -#endif /* !CONFIG_USER_ONLY */ - #endif diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 7416e3216e..e8417d0406 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -25,6 +25,7 @@ #include "signal-common.h" #include "semihosting/common-semi.h" #include "exec/page-protection.h" +#include "exec/mmap-lock.h" #include "user/page-protection.h" #include "target/arm/syndrome.h" diff --git a/linux-user/elfload.c b/linux-user/elfload.c index fa83d78667..99811af5e7 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -10,6 +10,7 @@ #include "user/tswap-target.h" #include "user/page-protection.h" #include "exec/page-protection.h" +#include "exec/mmap-lock.h" #include "exec/translation-block.h" #include "user/guest-base.h" #include "user-internals.h" diff --git a/linux-user/flatload.c b/linux-user/flatload.c index d5cb1830dd..4beb3ed1b9 100644 --- a/linux-user/flatload.c +++ b/linux-user/flatload.c @@ -35,6 +35,7 @@ #include "qemu.h" #include "exec/page-protection.h" +#include "exec/mmap-lock.h" #include "user-internals.h" #include "loader.h" #include "user-mmap.h" diff --git a/linux-user/mmap.c b/linux-user/mmap.c index d1f36e6f16..f88a80c31e 100644 --- a/linux-user/mmap.c +++ b/linux-user/mmap.c @@ -21,6 +21,7 @@ #include "trace.h" #include "exec/log.h" #include "exec/page-protection.h" +#include "exec/mmap-lock.h" #include "exec/tb-flush.h" #include "exec/translation-block.h" #include "qemu.h" diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 8bfe4912e1..5826ac3adb 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -26,6 +26,7 @@ #include "tcg/startup.h" #include "target_mman.h" #include "exec/page-protection.h" +#include "exec/mmap-lock.h" #include "exec/tb-flush.h" #include "exec/translation-block.h" #include diff --git a/target/arm/helper.c b/target/arm/helper.c index bb445e30cd..0454b06a6c 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -14,6 +14,7 @@ #include "cpu-features.h" #include "exec/helper-proto.h" #include "exec/page-protection.h" +#include "exec/mmap-lock.h" #include "qemu/main-loop.h" #include "qemu/timer.h" #include "qemu/bitops.h" From 8be545ba5a315a9aaf7307f143a4a7926a6e605c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 12:11:21 -0700 Subject: [PATCH 0113/2760] include/system: Move exec/memory.h to system/memory.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the existing includes with sed -i ,exec/memory.h,system/memory.h,g Move the include within cpu-all.h into a !CONFIG_USER_ONLY block. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- MAINTAINERS | 2 +- accel/kvm/kvm-all.c | 2 +- accel/tcg/cputlb.c | 2 +- backends/tpm/tpm_util.c | 2 +- block/blkio.c | 4 ++-- disas/disas-mon.c | 2 +- docs/devel/memory.rst | 2 +- hw/acpi/erst.c | 2 +- hw/arm/strongarm.h | 2 +- hw/avr/atmega.c | 2 +- hw/block/fdc-sysbus.c | 2 +- hw/core/cpu-system.c | 2 +- hw/core/loader-fit.c | 2 +- hw/core/loader.c | 2 +- hw/display/apple-gfx.h | 2 +- hw/display/edid-region.c | 2 +- hw/display/framebuffer.h | 2 +- hw/display/vga_int.h | 2 +- hw/hyperv/hv-balloon-our_range_memslots.h | 2 +- hw/hyperv/hyperv.c | 2 +- hw/i386/acpi-common.c | 2 +- hw/i386/acpi-microvm.c | 2 +- hw/i386/pc_piix.c | 2 +- hw/intc/ioapic_internal.h | 2 +- hw/intc/mips_gic.c | 2 +- hw/intc/ompic.c | 2 +- hw/net/i82596.h | 2 +- hw/net/ne2000.c | 2 +- hw/net/pcnet.h | 2 +- hw/pci-bridge/pci_bridge_dev.c | 2 +- hw/pci-host/remote.c | 2 +- hw/ppc/pnv_homer.c | 2 +- hw/ppc/sam460ex.c | 2 +- hw/remote/iommu.c | 2 +- hw/remote/machine.c | 2 +- hw/remote/proxy-memory-listener.c | 2 +- hw/remote/vfio-user-obj.c | 2 +- hw/s390x/s390-pci-inst.c | 2 +- hw/timer/sh_timer.c | 2 +- hw/tpm/tpm_ppi.h | 2 +- hw/usb/hcd-uhci.h | 2 +- hw/vfio/common.c | 2 +- hw/vfio/container.c | 2 +- hw/vfio/pci.h | 2 +- hw/vfio/platform.c | 2 +- hw/virtio/vhost-iova-tree.h | 2 +- hw/xtensa/sim.c | 2 +- hw/xtensa/virt.c | 2 +- hw/xtensa/xtensa_memory.c | 2 +- hw/xtensa/xtfpga.c | 2 +- include/exec/cpu-all.h | 5 ++++- include/exec/ioport.h | 2 +- include/exec/ram_addr.h | 2 +- include/hw/acpi/acpi.h | 2 +- include/hw/acpi/ich9_tco.h | 2 +- include/hw/arm/fsl-imx25.h | 2 +- include/hw/arm/fsl-imx31.h | 2 +- include/hw/arm/fsl-imx6.h | 2 +- include/hw/arm/fsl-imx6ul.h | 2 +- include/hw/arm/omap.h | 2 +- include/hw/arm/stm32l4x5_soc.h | 2 +- include/hw/boards.h | 2 +- include/hw/char/parallel.h | 2 +- include/hw/char/riscv_htif.h | 2 +- include/hw/char/serial-mm.h | 2 +- include/hw/char/serial.h | 2 +- include/hw/display/macfb.h | 2 +- include/hw/fsi/aspeed_apb2opb.h | 2 +- include/hw/fsi/cfam.h | 2 +- include/hw/fsi/fsi-master.h | 2 +- include/hw/fsi/fsi.h | 2 +- include/hw/fsi/lbus.h | 2 +- include/hw/gpio/npcm7xx_gpio.h | 2 +- include/hw/i2c/npcm7xx_smbus.h | 2 +- include/hw/i2c/pm_smbus.h | 2 +- include/hw/i386/apic_internal.h | 2 +- include/hw/i386/x86.h | 2 +- include/hw/ide/ahci.h | 2 +- include/hw/ipmi/ipmi.h | 2 +- include/hw/isa/apm.h | 2 +- include/hw/isa/isa.h | 2 +- include/hw/m68k/q800.h | 2 +- include/hw/mem/npcm7xx_mc.h | 2 +- include/hw/mem/pc-dimm.h | 2 +- include/hw/mips/mips.h | 2 +- include/hw/misc/auxbus.h | 2 +- include/hw/misc/ivshmem-flat.h | 2 +- include/hw/misc/mac_via.h | 2 +- include/hw/misc/npcm7xx_mft.h | 2 +- include/hw/misc/npcm_clk.h | 2 +- include/hw/misc/npcm_gcr.h | 2 +- include/hw/misc/pvpanic.h | 2 +- include/hw/net/dp8393x.h | 2 +- include/hw/net/msf2-emac.h | 2 +- include/hw/nvram/mac_nvram.h | 2 +- include/hw/nvram/npcm7xx_otp.h | 2 +- include/hw/pci-host/fsl_imx8m_phy.h | 2 +- include/hw/pci-host/pam.h | 2 +- include/hw/pci-host/remote.h | 2 +- include/hw/pci/pci.h | 2 +- include/hw/pci/pcie_host.h | 2 +- include/hw/pci/shpc.h | 2 +- include/hw/ppc/mac_dbdma.h | 2 +- include/hw/ppc/pnv_lpc.h | 2 +- include/hw/ppc/pnv_occ.h | 2 +- include/hw/ppc/pnv_sbe.h | 2 +- include/hw/ppc/pnv_xscom.h | 2 +- include/hw/ppc/ppc4xx.h | 2 +- include/hw/ppc/vof.h | 2 +- include/hw/ppc/xics.h | 2 +- include/hw/register.h | 2 +- include/hw/remote/proxy-memory-listener.h | 2 +- include/hw/sh4/sh_intc.h | 2 +- include/hw/southbridge/ich9.h | 2 +- include/hw/sysbus.h | 2 +- include/hw/timer/npcm7xx_timer.h | 2 +- include/hw/tricore/tricore.h | 2 +- include/hw/usb.h | 2 +- include/hw/vfio/vfio-common.h | 2 +- include/hw/vfio/vfio-container-base.h | 2 +- include/hw/virtio/vhost-backend.h | 2 +- include/hw/virtio/vhost.h | 2 +- include/hw/virtio/virtio.h | 2 +- include/hw/xen/xen-pvh-common.h | 2 +- include/hw/xtensa/mx_pic.h | 2 +- include/qemu/iova-tree.h | 2 +- include/qemu/reserved-region.h | 2 +- include/system/dma.h | 2 +- include/system/hostmem.h | 2 +- include/system/kvm_int.h | 2 +- include/{exec => system}/memory.h | 8 ++------ include/system/vhost-user-backend.h | 2 +- migration/dirtyrate.c | 2 +- migration/rdma.c | 2 +- migration/rdma.h | 2 +- migration/savevm.c | 2 +- monitor/hmp-cmds-target.c | 2 +- rust/wrapper.h | 2 +- scripts/analyze-inclusions | 2 +- stubs/ram-block.c | 2 +- system/dirtylimit.c | 2 +- system/ioport.c | 2 +- system/memory.c | 2 +- system/memory_mapping.c | 2 +- system/physmem.c | 2 +- system/qtest.c | 2 +- target/avr/cpu.h | 2 +- target/loongarch/cpu.h | 2 +- target/mips/cpu.h | 2 +- target/xtensa/cpu.c | 2 +- tests/qtest/fuzz/generic_fuzz.c | 2 +- tests/qtest/fuzz/qos_fuzz.c | 2 +- tests/unit/test-resv-mem.c | 2 +- ui/console.c | 2 +- util/vfio-helpers.c | 2 +- 155 files changed, 160 insertions(+), 161 deletions(-) rename include/{exec => system}/memory.h (99%) diff --git a/MAINTAINERS b/MAINTAINERS index 04573e2934..5d391cfaa7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3120,7 +3120,7 @@ R: Philippe Mathieu-Daudé S: Supported F: include/exec/ioport.h F: include/exec/memop.h -F: include/exec/memory.h +F: include/system/memory.h F: include/exec/ram_addr.h F: include/exec/ramblock.h F: include/system/memory_mapping.h diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 951e8214e0..9e06a95f75 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -33,7 +33,7 @@ #include "system/cpus.h" #include "system/accel-blocker.h" #include "qemu/bswap.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/ram_addr.h" #include "qemu/event_notifier.h" #include "qemu/main-loop.h" diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 2817c9dbdd..6f0ea9067b 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -22,7 +22,7 @@ #include "accel/tcg/cpu-ops.h" #include "exec/exec-all.h" #include "exec/page-protection.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/cpu_ldst.h" #include "exec/cputlb.h" #include "exec/tb-flush.h" diff --git a/backends/tpm/tpm_util.c b/backends/tpm/tpm_util.c index f07a2656ce..f2d1739e33 100644 --- a/backends/tpm/tpm_util.c +++ b/backends/tpm/tpm_util.c @@ -25,7 +25,7 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "tpm_int.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-properties.h" #include "system/tpm_backend.h" #include "system/tpm_util.h" diff --git a/block/blkio.c b/block/blkio.c index 5f4fce2b1b..4142673984 100644 --- a/block/blkio.c +++ b/block/blkio.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include #include "block/block_int.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/cpu-common.h" /* for qemu_ram_get_fd() */ #include "qemu/defer-call.h" #include "qapi/error.h" @@ -19,7 +19,7 @@ #include "qobject/qdict.h" #include "qemu/module.h" #include "system/block-backend.h" -#include "exec/memory.h" /* for ram_block_discard_disable() */ +#include "system/memory.h" /* for ram_block_discard_disable() */ #include "block/block-io.h" diff --git a/disas/disas-mon.c b/disas/disas-mon.c index 37bf16ac79..9c693618c2 100644 --- a/disas/disas-mon.c +++ b/disas/disas-mon.c @@ -7,7 +7,7 @@ #include "qemu/osdep.h" #include "disas-internal.h" #include "disas/disas.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/core/cpu.h" #include "monitor/monitor.h" diff --git a/docs/devel/memory.rst b/docs/devel/memory.rst index 69c5e3f914..57fb2aec76 100644 --- a/docs/devel/memory.rst +++ b/docs/devel/memory.rst @@ -369,4 +369,4 @@ callbacks are called: API Reference ------------- -.. kernel-doc:: include/exec/memory.h +.. kernel-doc:: include/system/memory.h diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index ec64f92893..5c4c1dc638 100644 --- a/hw/acpi/erst.c +++ b/hw/acpi/erst.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/qdev-core.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #include "hw/pci/pci_device.h" #include "qom/object_interfaces.h" diff --git a/hw/arm/strongarm.h b/hw/arm/strongarm.h index 192821f6aa..b11b3a3379 100644 --- a/hw/arm/strongarm.h +++ b/hw/arm/strongarm.h @@ -1,7 +1,7 @@ #ifndef STRONGARM_H #define STRONGARM_H -#include "exec/memory.h" +#include "system/memory.h" #include "target/arm/cpu-qom.h" #define SA_CS0 0x00000000 diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index 11fab184de..cb721c96b7 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -12,7 +12,7 @@ #include "qemu/module.h" #include "qemu/units.h" #include "qapi/error.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/address-spaces.h" #include "system/system.h" #include "hw/qdev-properties.h" diff --git a/hw/block/fdc-sysbus.c b/hw/block/fdc-sysbus.c index 381b492aec..4955e478cd 100644 --- a/hw/block/fdc-sysbus.c +++ b/hw/block/fdc-sysbus.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qom/object.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" #include "hw/block/fdc.h" #include "migration/vmstate.h" diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index aed5076ec7..5ef8c24b5b 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -22,7 +22,7 @@ #include "qapi/error.h" #include "exec/address-spaces.h" #include "exec/cputlb.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/tb-flush.h" #include "exec/tswap.h" #include "hw/qdev-core.h" diff --git a/hw/core/loader-fit.c b/hw/core/loader-fit.c index 6eb66406b0..2dea485ae0 100644 --- a/hw/core/loader-fit.c +++ b/hw/core/loader-fit.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/units.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/loader.h" #include "hw/loader-fit.h" #include "qemu/cutils.h" diff --git a/hw/core/loader.c b/hw/core/loader.c index 2e35f0aa90..a3aa62d132 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -59,7 +59,7 @@ #include "uboot_image.h" #include "hw/loader.h" #include "hw/nvram/fw_cfg.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/boards.h" #include "qemu/cutils.h" #include "system/runstate.h" diff --git a/hw/display/apple-gfx.h b/hw/display/apple-gfx.h index 3900cdbabb..a8b1d1efc0 100644 --- a/hw/display/apple-gfx.h +++ b/hw/display/apple-gfx.h @@ -9,7 +9,7 @@ #define QEMU_APPLE_GFX_H #include "qemu/queue.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-properties.h" #include "ui/surface.h" diff --git a/hw/display/edid-region.c b/hw/display/edid-region.c index 675429dc18..f1596fba9a 100644 --- a/hw/display/edid-region.c +++ b/hw/display/edid-region.c @@ -1,5 +1,5 @@ #include "qemu/osdep.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/display/edid.h" static uint64_t edid_region_read(void *ptr, hwaddr addr, unsigned size) diff --git a/hw/display/framebuffer.h b/hw/display/framebuffer.h index 38fa0dcec6..29a828ce7a 100644 --- a/hw/display/framebuffer.h +++ b/hw/display/framebuffer.h @@ -1,7 +1,7 @@ #ifndef QEMU_FRAMEBUFFER_H #define QEMU_FRAMEBUFFER_H -#include "exec/memory.h" +#include "system/memory.h" /* Framebuffer device helper routines. */ diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index f77c1c1145..60ad26e03e 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -27,7 +27,7 @@ #include "ui/console.h" #include "exec/ioport.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/display/bochs-vbe.h" #include "hw/acpi/acpi_aml_interface.h" diff --git a/hw/hyperv/hv-balloon-our_range_memslots.h b/hw/hyperv/hv-balloon-our_range_memslots.h index df3b686bc7..b1f19d77da 100644 --- a/hw/hyperv/hv-balloon-our_range_memslots.h +++ b/hw/hyperv/hv-balloon-our_range_memslots.h @@ -11,7 +11,7 @@ #define HW_HYPERV_HV_BALLOON_OUR_RANGE_MEMSLOTS_H -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #include "hv-balloon-page_range_tree.h" diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index 831e04f214..382c62d668 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -12,7 +12,7 @@ #include "qemu/module.h" #include "qapi/error.h" #include "exec/address-spaces.h" -#include "exec/memory.h" +#include "system/memory.h" #include "system/kvm.h" #include "qemu/bitops.h" #include "qemu/error-report.h" diff --git a/hw/i386/acpi-common.c b/hw/i386/acpi-common.c index 0cc2919bb8..7bd08067a7 100644 --- a/hw/i386/acpi-common.c +++ b/hw/i386/acpi-common.c @@ -23,7 +23,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/acpi/acpi.h" #include "hw/acpi/aml-build.h" #include "hw/acpi/utils.h" diff --git a/hw/i386/acpi-microvm.c b/hw/i386/acpi-microvm.c index 279da6b4aa..bc6571778c 100644 --- a/hw/i386/acpi-microvm.c +++ b/hw/i386/acpi-microvm.c @@ -24,7 +24,7 @@ #include "qemu/cutils.h" #include "qapi/error.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/acpi/acpi.h" #include "hw/acpi/acpi_aml_interface.h" #include "hw/acpi/aml-build.h" diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index dbb59df64f..0dce512f18 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -47,7 +47,7 @@ #include "hw/i386/kvm/clock.h" #include "hw/sysbus.h" #include "hw/i2c/smbus_eeprom.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/acpi/acpi.h" #include "qapi/error.h" #include "qemu/error-report.h" diff --git a/hw/intc/ioapic_internal.h b/hw/intc/ioapic_internal.h index 37b8565539..51205767f4 100644 --- a/hw/intc/ioapic_internal.h +++ b/hw/intc/ioapic_internal.h @@ -22,7 +22,7 @@ #ifndef HW_INTC_IOAPIC_INTERNAL_H #define HW_INTC_IOAPIC_INTERNAL_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/intc/ioapic.h" #include "hw/sysbus.h" #include "qemu/notify.h" diff --git a/hw/intc/mips_gic.c b/hw/intc/mips_gic.c index 5e3cbeabec..12d3908938 100644 --- a/hw/intc/mips_gic.c +++ b/hw/intc/mips_gic.c @@ -14,7 +14,7 @@ #include "qemu/module.h" #include "qapi/error.h" #include "hw/sysbus.h" -#include "exec/memory.h" +#include "system/memory.h" #include "system/kvm.h" #include "system/reset.h" #include "kvm_mips.h" diff --git a/hw/intc/ompic.c b/hw/intc/ompic.c index 42af4567c6..169baf2ded 100644 --- a/hw/intc/ompic.c +++ b/hw/intc/ompic.c @@ -13,7 +13,7 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" #include "migration/vmstate.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #define TYPE_OR1K_OMPIC "or1k-ompic" diff --git a/hw/net/i82596.h b/hw/net/i82596.h index f0bbe810eb..4bdfcaf856 100644 --- a/hw/net/i82596.h +++ b/hw/net/i82596.h @@ -3,7 +3,7 @@ #define I82596_IOPORT_SIZE 0x20 -#include "exec/memory.h" +#include "system/memory.h" #include "exec/address-spaces.h" #define PORT_RESET 0x00 /* reset 82596 */ diff --git a/hw/net/ne2000.c b/hw/net/ne2000.c index b482c5f3af..b1923c8c3e 100644 --- a/hw/net/ne2000.c +++ b/hw/net/ne2000.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "net/eth.h" #include "qemu/module.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/irq.h" #include "migration/vmstate.h" #include "ne2000.h" diff --git a/hw/net/pcnet.h b/hw/net/pcnet.h index eb7f46aab3..a94356ec30 100644 --- a/hw/net/pcnet.h +++ b/hw/net/pcnet.h @@ -7,7 +7,7 @@ #define PCNET_LOOPTEST_CRC 1 #define PCNET_LOOPTEST_NOCRC 2 -#include "exec/memory.h" +#include "system/memory.h" #include "hw/irq.h" /* BUS CONFIGURATION REGISTERS */ diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 0a91a8ae6c..4931ea24f6 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -28,7 +28,7 @@ #include "hw/pci/shpc.h" #include "hw/pci/slotid_cap.h" #include "hw/qdev-properties.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/pci/pci_bus.h" #include "hw/hotplug.h" #include "qom/object.h" diff --git a/hw/pci-host/remote.c b/hw/pci-host/remote.c index bfb25ef6af..be077d075e 100644 --- a/hw/pci-host/remote.c +++ b/hw/pci-host/remote.c @@ -28,7 +28,7 @@ #include "hw/pci/pcie_host.h" #include "hw/qdev-properties.h" #include "hw/pci-host/remote.h" -#include "exec/memory.h" +#include "system/memory.h" static const char *remote_pcihost_root_bus_path(PCIHostState *host_bridge, PCIBus *rootbus) diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index 18a53a80c1..0521f9a428 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -20,7 +20,7 @@ #include "qemu/log.h" #include "qapi/error.h" #include "exec/hwaddr.h" -#include "exec/memory.h" +#include "system/memory.h" #include "system/cpus.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index 7dc3b309c8..a070de23cf 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -24,7 +24,7 @@ #include "exec/page-protection.h" #include "hw/loader.h" #include "elf.h" -#include "exec/memory.h" +#include "system/memory.h" #include "ppc440.h" #include "hw/pci-host/ppc4xx.h" #include "hw/block/flash.h" diff --git a/hw/remote/iommu.c b/hw/remote/iommu.c index 7c56aad0fc..ec845d1f58 100644 --- a/hw/remote/iommu.c +++ b/hw/remote/iommu.c @@ -13,7 +13,7 @@ #include "hw/remote/iommu.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pci.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/address-spaces.h" #include "trace.h" diff --git a/hw/remote/machine.c b/hw/remote/machine.c index fdc6c441bb..d4616025e8 100644 --- a/hw/remote/machine.c +++ b/hw/remote/machine.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "hw/remote/machine.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qapi/error.h" #include "hw/pci/pci_host.h" #include "hw/remote/iohub.h" diff --git a/hw/remote/proxy-memory-listener.c b/hw/remote/proxy-memory-listener.c index a926f61ebe..ce7f5b9bfb 100644 --- a/hw/remote/proxy-memory-listener.c +++ b/hw/remote/proxy-memory-listener.c @@ -10,7 +10,7 @@ #include "qemu/int128.h" #include "qemu/range.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/cpu-common.h" #include "exec/ram_addr.h" #include "qapi/error.h" diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index 6e51a92856..9bdd0a465b 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -57,7 +57,7 @@ #include "hw/qdev-core.h" #include "hw/pci/pci.h" #include "qemu/timer.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/remote/vfio-user-obj.h" diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index 8cdeb6cb7f..b4e003c19c 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "exec/memop.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/error-report.h" #include "system/hw_accel.h" #include "hw/boards.h" diff --git a/hw/timer/sh_timer.c b/hw/timer/sh_timer.c index 7788939766..d4fa32c9d6 100644 --- a/hw/timer/sh_timer.c +++ b/hw/timer/sh_timer.c @@ -9,7 +9,7 @@ */ #include "qemu/osdep.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/log.h" #include "hw/irq.h" #include "hw/sh4/sh.h" diff --git a/hw/tpm/tpm_ppi.h b/hw/tpm/tpm_ppi.h index bf5d4a300f..88f316ee95 100644 --- a/hw/tpm/tpm_ppi.h +++ b/hw/tpm/tpm_ppi.h @@ -12,7 +12,7 @@ #ifndef TPM_TPM_PPI_H #define TPM_TPM_PPI_H -#include "exec/memory.h" +#include "system/memory.h" typedef struct TPMPPI { MemoryRegion ram; diff --git a/hw/usb/hcd-uhci.h b/hw/usb/hcd-uhci.h index 6d26b94e92..d4664297cf 100644 --- a/hw/usb/hcd-uhci.h +++ b/hw/usb/hcd-uhci.h @@ -28,7 +28,7 @@ #ifndef HW_USB_HCD_UHCI_H #define HW_USB_HCD_UHCI_H -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/timer.h" #include "hw/pci/pci_device.h" #include "hw/usb.h" diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 1a0d9290f8..989c6ee83d 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -28,7 +28,7 @@ #include "hw/vfio/vfio-common.h" #include "hw/vfio/pci.h" #include "exec/address-spaces.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/ram_addr.h" #include "exec/target_page.h" #include "hw/hw.h" diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 7c57bdd27b..1d1c5f9a77 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -24,7 +24,7 @@ #include "hw/vfio/vfio-common.h" #include "exec/address-spaces.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/ram_addr.h" #include "qemu/error-report.h" #include "qemu/range.h" diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index d94ecaba68..6c59300248 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -12,7 +12,7 @@ #ifndef HW_VFIO_VFIO_PCI_H #define HW_VFIO_VFIO_PCI_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/pci/pci_device.h" #include "hw/vfio/vfio-common.h" #include "qemu/event_notifier.h" diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 67bc57409c..96c6bf5654 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -28,7 +28,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/range.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/address-spaces.h" #include "qemu/queue.h" #include "hw/sysbus.h" diff --git a/hw/virtio/vhost-iova-tree.h b/hw/virtio/vhost-iova-tree.h index 0c4ba5abd5..08f63b61cd 100644 --- a/hw/virtio/vhost-iova-tree.h +++ b/hw/virtio/vhost-iova-tree.h @@ -11,7 +11,7 @@ #define HW_VIRTIO_VHOST_IOVA_TREE_H #include "qemu/iova-tree.h" -#include "exec/memory.h" +#include "system/memory.h" typedef struct VhostIOVATree VhostIOVATree; diff --git a/hw/xtensa/sim.c b/hw/xtensa/sim.c index 1cea29c66d..49d17e7bb2 100644 --- a/hw/xtensa/sim.c +++ b/hw/xtensa/sim.c @@ -32,7 +32,7 @@ #include "hw/boards.h" #include "hw/loader.h" #include "elf.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/error-report.h" #include "xtensa_memory.h" #include "xtensa_sim.h" diff --git a/hw/xtensa/virt.c b/hw/xtensa/virt.c index b08404fc17..b10866ccd8 100644 --- a/hw/xtensa/virt.c +++ b/hw/xtensa/virt.c @@ -33,7 +33,7 @@ #include "hw/pci-host/gpex.h" #include "net/net.h" #include "elf.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/error-report.h" #include "xtensa_memory.h" #include "xtensa_sim.h" diff --git a/hw/xtensa/xtensa_memory.c b/hw/xtensa/xtensa_memory.c index 2c1095f017..13a6077d86 100644 --- a/hw/xtensa/xtensa_memory.c +++ b/hw/xtensa/xtensa_memory.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/error-report.h" #include "xtensa_memory.h" diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 3f3677f1c9..3bd0ef8268 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -34,7 +34,7 @@ #include "hw/loader.h" #include "hw/qdev-properties.h" #include "elf.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/tswap.h" #include "hw/char/serial-mm.h" #include "net/net.h" diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 33b9dc81eb..4395fd08af 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -22,11 +22,14 @@ #include "exec/page-protection.h" #include "exec/cpu-common.h" #include "exec/cpu-interrupt.h" -#include "exec/memory.h" #include "exec/tswap.h" #include "hw/core/cpu.h" #include "exec/cpu-defs.h" #include "exec/target_page.h" +#ifndef CONFIG_USER_ONLY +#include "system/memory.h" +#endif + CPUArchState *cpu_copy(CPUArchState *env); diff --git a/include/exec/ioport.h b/include/exec/ioport.h index 4397f12f93..ecea3575bc 100644 --- a/include/exec/ioport.h +++ b/include/exec/ioport.h @@ -24,7 +24,7 @@ #ifndef IOPORT_H #define IOPORT_H -#include "exec/memory.h" +#include "system/memory.h" #define MAX_IOPORTS (64 * 1024) #define IOPORTS_MASK (MAX_IOPORTS - 1) diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h index 92e8708af7..8677761af5 100644 --- a/include/exec/ram_addr.h +++ b/include/exec/ram_addr.h @@ -26,7 +26,7 @@ #include "exec/ramlist.h" #include "exec/ramblock.h" #include "exec/exec-all.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/target_page.h" #include "qemu/rcu.h" diff --git a/include/hw/acpi/acpi.h b/include/hw/acpi/acpi.h index d1a4fa2af8..4b8ee094c4 100644 --- a/include/hw/acpi/acpi.h +++ b/include/hw/acpi/acpi.h @@ -21,7 +21,7 @@ */ #include "qemu/notify.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/acpi/acpi_dev_interface.h" /* diff --git a/include/hw/acpi/ich9_tco.h b/include/hw/acpi/ich9_tco.h index 2562a7cf39..b3c3f69451 100644 --- a/include/hw/acpi/ich9_tco.h +++ b/include/hw/acpi/ich9_tco.h @@ -10,7 +10,7 @@ #ifndef HW_ACPI_TCO_H #define HW_ACPI_TCO_H -#include "exec/memory.h" +#include "system/memory.h" #include "migration/vmstate.h" /* As per ICH9 spec, the internal timer has an error of ~0.6s on every tick */ diff --git a/include/hw/arm/fsl-imx25.h b/include/hw/arm/fsl-imx25.h index df2f83980f..b68d4334a0 100644 --- a/include/hw/arm/fsl-imx25.h +++ b/include/hw/arm/fsl-imx25.h @@ -29,7 +29,7 @@ #include "hw/sd/sdhci.h" #include "hw/usb/chipidea.h" #include "hw/watchdog/wdt_imx2.h" -#include "exec/memory.h" +#include "system/memory.h" #include "target/arm/cpu.h" #include "qom/object.h" diff --git a/include/hw/arm/fsl-imx31.h b/include/hw/arm/fsl-imx31.h index 40c593a5cf..41232a2237 100644 --- a/include/hw/arm/fsl-imx31.h +++ b/include/hw/arm/fsl-imx31.h @@ -25,7 +25,7 @@ #include "hw/i2c/imx_i2c.h" #include "hw/gpio/imx_gpio.h" #include "hw/watchdog/wdt_imx2.h" -#include "exec/memory.h" +#include "system/memory.h" #include "target/arm/cpu.h" #include "qom/object.h" diff --git a/include/hw/arm/fsl-imx6.h b/include/hw/arm/fsl-imx6.h index 9da32fc189..124bbd478f 100644 --- a/include/hw/arm/fsl-imx6.h +++ b/include/hw/arm/fsl-imx6.h @@ -34,7 +34,7 @@ #include "hw/usb/imx-usb-phy.h" #include "hw/pci-host/designware.h" #include "hw/or-irq.h" -#include "exec/memory.h" +#include "system/memory.h" #include "cpu.h" #include "qom/object.h" diff --git a/include/hw/arm/fsl-imx6ul.h b/include/hw/arm/fsl-imx6ul.h index 8277b0e8b2..4e3209b25b 100644 --- a/include/hw/arm/fsl-imx6ul.h +++ b/include/hw/arm/fsl-imx6ul.h @@ -33,7 +33,7 @@ #include "hw/net/imx_fec.h" #include "hw/usb/chipidea.h" #include "hw/usb/imx-usb-phy.h" -#include "exec/memory.h" +#include "system/memory.h" #include "cpu.h" #include "qom/object.h" #include "qemu/units.h" diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index 7cb87ea89c..6185507373 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -20,7 +20,7 @@ #ifndef HW_ARM_OMAP_H #define HW_ARM_OMAP_H -#include "exec/memory.h" +#include "system/memory.h" #include "target/arm/cpu-qom.h" #include "qemu/log.h" #include "qom/object.h" diff --git a/include/hw/arm/stm32l4x5_soc.h b/include/hw/arm/stm32l4x5_soc.h index c243fb0e7f..c2fae6e23f 100644 --- a/include/hw/arm/stm32l4x5_soc.h +++ b/include/hw/arm/stm32l4x5_soc.h @@ -24,7 +24,7 @@ #ifndef HW_ARM_STM32L4x5_SOC_H #define HW_ARM_STM32L4x5_SOC_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/arm/armv7m.h" #include "hw/or-irq.h" #include "hw/misc/stm32l4x5_syscfg.h" diff --git a/include/hw/boards.h b/include/hw/boards.h index bfe8643a27..8556e01e21 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -3,7 +3,7 @@ #ifndef HW_BOARDS_H #define HW_BOARDS_H -#include "exec/memory.h" +#include "system/memory.h" #include "system/hostmem.h" #include "system/blockdev.h" #include "qapi/qapi-types-machine.h" diff --git a/include/hw/char/parallel.h b/include/hw/char/parallel.h index cfb97cc7cc..7b04478226 100644 --- a/include/hw/char/parallel.h +++ b/include/hw/char/parallel.h @@ -1,7 +1,7 @@ #ifndef HW_PARALLEL_H #define HW_PARALLEL_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/isa/isa.h" #include "hw/irq.h" #include "chardev/char-fe.h" diff --git a/include/hw/char/riscv_htif.h b/include/hw/char/riscv_htif.h index df493fdf6b..ee0ca29902 100644 --- a/include/hw/char/riscv_htif.h +++ b/include/hw/char/riscv_htif.h @@ -22,7 +22,7 @@ #include "chardev/char.h" #include "chardev/char-fe.h" -#include "exec/memory.h" +#include "system/memory.h" #define TYPE_HTIF_UART "riscv.htif.uart" diff --git a/include/hw/char/serial-mm.h b/include/hw/char/serial-mm.h index 62a8489d69..77abd098e0 100644 --- a/include/hw/char/serial-mm.h +++ b/include/hw/char/serial-mm.h @@ -27,7 +27,7 @@ #define HW_SERIAL_MM_H #include "hw/char/serial.h" -#include "exec/memory.h" +#include "system/memory.h" #include "chardev/char.h" #include "hw/sysbus.h" #include "qom/object.h" diff --git a/include/hw/char/serial.h b/include/hw/char/serial.h index 942b372df6..4bf90a46f3 100644 --- a/include/hw/char/serial.h +++ b/include/hw/char/serial.h @@ -27,7 +27,7 @@ #define HW_SERIAL_H #include "chardev/char-fe.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/fifo8.h" #include "qom/object.h" diff --git a/include/hw/display/macfb.h b/include/hw/display/macfb.h index 27cebefc9e..0fae1f33a6 100644 --- a/include/hw/display/macfb.h +++ b/include/hw/display/macfb.h @@ -13,7 +13,7 @@ #ifndef MACFB_H #define MACFB_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/irq.h" #include "hw/nubus/nubus.h" #include "hw/sysbus.h" diff --git a/include/hw/fsi/aspeed_apb2opb.h b/include/hw/fsi/aspeed_apb2opb.h index f6a2387abf..878619eafa 100644 --- a/include/hw/fsi/aspeed_apb2opb.h +++ b/include/hw/fsi/aspeed_apb2opb.h @@ -8,7 +8,7 @@ #ifndef FSI_ASPEED_APB2OPB_H #define FSI_ASPEED_APB2OPB_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/fsi/fsi-master.h" #include "hw/sysbus.h" diff --git a/include/hw/fsi/cfam.h b/include/hw/fsi/cfam.h index 7abc3b287b..cceb4bd6f1 100644 --- a/include/hw/fsi/cfam.h +++ b/include/hw/fsi/cfam.h @@ -7,7 +7,7 @@ #ifndef FSI_CFAM_H #define FSI_CFAM_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/fsi/fsi.h" #include "hw/fsi/lbus.h" diff --git a/include/hw/fsi/fsi-master.h b/include/hw/fsi/fsi-master.h index 68e5f56db2..b634ecd393 100644 --- a/include/hw/fsi/fsi-master.h +++ b/include/hw/fsi/fsi-master.h @@ -7,7 +7,7 @@ #ifndef FSI_FSI_MASTER_H #define FSI_FSI_MASTER_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "hw/fsi/fsi.h" #include "hw/fsi/cfam.h" diff --git a/include/hw/fsi/fsi.h b/include/hw/fsi/fsi.h index e00f6ef078..f34765ed80 100644 --- a/include/hw/fsi/fsi.h +++ b/include/hw/fsi/fsi.h @@ -7,7 +7,7 @@ #ifndef FSI_FSI_H #define FSI_FSI_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "hw/fsi/lbus.h" #include "qemu/bitops.h" diff --git a/include/hw/fsi/lbus.h b/include/hw/fsi/lbus.h index 558268c013..12519073cd 100644 --- a/include/hw/fsi/lbus.h +++ b/include/hw/fsi/lbus.h @@ -9,7 +9,7 @@ #include "hw/qdev-core.h" #include "qemu/units.h" -#include "exec/memory.h" +#include "system/memory.h" #define TYPE_FSI_LBUS_DEVICE "fsi.lbus.device" OBJECT_DECLARE_SIMPLE_TYPE(FSILBusDevice, FSI_LBUS_DEVICE) diff --git a/include/hw/gpio/npcm7xx_gpio.h b/include/hw/gpio/npcm7xx_gpio.h index b1d771bd77..7c0bf61a96 100644 --- a/include/hw/gpio/npcm7xx_gpio.h +++ b/include/hw/gpio/npcm7xx_gpio.h @@ -15,7 +15,7 @@ #ifndef NPCM7XX_GPIO_H #define NPCM7XX_GPIO_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" /* Number of pins managed by each controller. */ diff --git a/include/hw/i2c/npcm7xx_smbus.h b/include/hw/i2c/npcm7xx_smbus.h index dc45963c0e..9c544c561b 100644 --- a/include/hw/i2c/npcm7xx_smbus.h +++ b/include/hw/i2c/npcm7xx_smbus.h @@ -16,7 +16,7 @@ #ifndef NPCM7XX_SMBUS_H #define NPCM7XX_SMBUS_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/i2c/i2c.h" #include "hw/irq.h" #include "hw/sysbus.h" diff --git a/include/hw/i2c/pm_smbus.h b/include/hw/i2c/pm_smbus.h index 0d74207efb..dafe0df4f6 100644 --- a/include/hw/i2c/pm_smbus.h +++ b/include/hw/i2c/pm_smbus.h @@ -1,7 +1,7 @@ #ifndef PM_SMBUS_H #define PM_SMBUS_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/i2c/smbus_master.h" #define PM_SMBUS_MAX_MSG_SIZE 32 diff --git a/include/hw/i386/apic_internal.h b/include/hw/i386/apic_internal.h index d6e85833da..429278da61 100644 --- a/include/hw/i386/apic_internal.h +++ b/include/hw/i386/apic_internal.h @@ -22,7 +22,7 @@ #define QEMU_APIC_INTERNAL_H #include "cpu.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/timer.h" #include "target/i386/cpu-qom.h" #include "qom/object.h" diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index d43cb3908e..258b1343a1 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -18,7 +18,7 @@ #define HW_I386_X86_H #include "exec/hwaddr.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/boards.h" #include "hw/i386/topology.h" diff --git a/include/hw/ide/ahci.h b/include/hw/ide/ahci.h index ac0292c634..cd07b87811 100644 --- a/include/hw/ide/ahci.h +++ b/include/hw/ide/ahci.h @@ -24,7 +24,7 @@ #ifndef HW_IDE_AHCI_H #define HW_IDE_AHCI_H -#include "exec/memory.h" +#include "system/memory.h" typedef struct AHCIDevice AHCIDevice; diff --git a/include/hw/ipmi/ipmi.h b/include/hw/ipmi/ipmi.h index 802a2febb0..cd581aa134 100644 --- a/include/hw/ipmi/ipmi.h +++ b/include/hw/ipmi/ipmi.h @@ -25,7 +25,7 @@ #ifndef HW_IPMI_H #define HW_IPMI_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "qom/object.h" diff --git a/include/hw/isa/apm.h b/include/hw/isa/apm.h index b6e070c00e..0834539045 100644 --- a/include/hw/isa/apm.h +++ b/include/hw/isa/apm.h @@ -1,7 +1,7 @@ #ifndef APM_H #define APM_H -#include "exec/memory.h" +#include "system/memory.h" #define APM_CNT_IOPORT 0xb2 #define ACPI_PORT_SMI_CMD APM_CNT_IOPORT diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 40d6224a4e..1d852011b3 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -3,7 +3,7 @@ /* ISA bus */ -#include "exec/memory.h" +#include "system/memory.h" #include "exec/ioport.h" #include "hw/qdev-core.h" #include "qom/object.h" diff --git a/include/hw/m68k/q800.h b/include/hw/m68k/q800.h index 34365c9860..9caaed9692 100644 --- a/include/hw/m68k/q800.h +++ b/include/hw/m68k/q800.h @@ -26,7 +26,7 @@ #include "hw/boards.h" #include "qom/object.h" #include "target/m68k/cpu-qom.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/m68k/q800-glue.h" #include "hw/misc/mac_via.h" #include "hw/net/dp8393x.h" diff --git a/include/hw/mem/npcm7xx_mc.h b/include/hw/mem/npcm7xx_mc.h index 7ed38be243..568cc35fdd 100644 --- a/include/hw/mem/npcm7xx_mc.h +++ b/include/hw/mem/npcm7xx_mc.h @@ -16,7 +16,7 @@ #ifndef NPCM7XX_MC_H #define NPCM7XX_MC_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" /** diff --git a/include/hw/mem/pc-dimm.h b/include/hw/mem/pc-dimm.h index fe0f3ea963..e0dbdd43dc 100644 --- a/include/hw/mem/pc-dimm.h +++ b/include/hw/mem/pc-dimm.h @@ -16,7 +16,7 @@ #ifndef QEMU_PC_DIMM_H #define QEMU_PC_DIMM_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "qom/object.h" diff --git a/include/hw/mips/mips.h b/include/hw/mips/mips.h index 101799f7d3..1f3672ba5f 100644 --- a/include/hw/mips/mips.h +++ b/include/hw/mips/mips.h @@ -7,7 +7,7 @@ /* Kernels can be configured with 64KB pages */ #define INITRD_PAGE_SIZE (64 * KiB) -#include "exec/memory.h" +#include "system/memory.h" /* bonito.c */ PCIBus *bonito_init(qemu_irq *pic); diff --git a/include/hw/misc/auxbus.h b/include/hw/misc/auxbus.h index 03cacdee42..ccd18ce209 100644 --- a/include/hw/misc/auxbus.h +++ b/include/hw/misc/auxbus.h @@ -25,7 +25,7 @@ #ifndef HW_MISC_AUXBUS_H #define HW_MISC_AUXBUS_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "qom/object.h" diff --git a/include/hw/misc/ivshmem-flat.h b/include/hw/misc/ivshmem-flat.h index 0c2b015781..09bc3abcad 100644 --- a/include/hw/misc/ivshmem-flat.h +++ b/include/hw/misc/ivshmem-flat.h @@ -14,7 +14,7 @@ #include "qemu/queue.h" #include "qemu/event_notifier.h" #include "chardev/char-fe.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #include "hw/sysbus.h" diff --git a/include/hw/misc/mac_via.h b/include/hw/misc/mac_via.h index 63cdcf7c69..6a15228150 100644 --- a/include/hw/misc/mac_via.h +++ b/include/hw/misc/mac_via.h @@ -9,7 +9,7 @@ #ifndef HW_MISC_MAC_VIA_H #define HW_MISC_MAC_VIA_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" #include "hw/misc/mos6522.h" #include "hw/input/adb.h" diff --git a/include/hw/misc/npcm7xx_mft.h b/include/hw/misc/npcm7xx_mft.h index d6384382ce..e4b997a6ad 100644 --- a/include/hw/misc/npcm7xx_mft.h +++ b/include/hw/misc/npcm7xx_mft.h @@ -16,7 +16,7 @@ #ifndef NPCM7XX_MFT_H #define NPCM7XX_MFT_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/clock.h" #include "hw/irq.h" #include "hw/sysbus.h" diff --git a/include/hw/misc/npcm_clk.h b/include/hw/misc/npcm_clk.h index 8fa1e14bdd..52e972f460 100644 --- a/include/hw/misc/npcm_clk.h +++ b/include/hw/misc/npcm_clk.h @@ -16,7 +16,7 @@ #ifndef NPCM_CLK_H #define NPCM_CLK_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/clock.h" #include "hw/sysbus.h" diff --git a/include/hw/misc/npcm_gcr.h b/include/hw/misc/npcm_gcr.h index d81bb9afb2..702e7fddb1 100644 --- a/include/hw/misc/npcm_gcr.h +++ b/include/hw/misc/npcm_gcr.h @@ -16,7 +16,7 @@ #ifndef NPCM_GCR_H #define NPCM_GCR_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" #include "qom/object.h" diff --git a/include/hw/misc/pvpanic.h b/include/hw/misc/pvpanic.h index 049a94c112..5098693437 100644 --- a/include/hw/misc/pvpanic.h +++ b/include/hw/misc/pvpanic.h @@ -15,7 +15,7 @@ #ifndef HW_MISC_PVPANIC_H #define HW_MISC_PVPANIC_H -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #include "standard-headers/misc/pvpanic.h" diff --git a/include/hw/net/dp8393x.h b/include/hw/net/dp8393x.h index 4a3f7478be..24273dc1f4 100644 --- a/include/hw/net/dp8393x.h +++ b/include/hw/net/dp8393x.h @@ -22,7 +22,7 @@ #include "hw/sysbus.h" #include "net/net.h" -#include "exec/memory.h" +#include "system/memory.h" #define SONIC_REG_COUNT 0x40 diff --git a/include/hw/net/msf2-emac.h b/include/hw/net/msf2-emac.h index 846ba6e6dc..b5d9127e46 100644 --- a/include/hw/net/msf2-emac.h +++ b/include/hw/net/msf2-emac.h @@ -23,7 +23,7 @@ */ #include "hw/sysbus.h" -#include "exec/memory.h" +#include "system/memory.h" #include "net/net.h" #include "net/eth.h" #include "qom/object.h" diff --git a/include/hw/nvram/mac_nvram.h b/include/hw/nvram/mac_nvram.h index 0c4dfaeff6..e9d8398f84 100644 --- a/include/hw/nvram/mac_nvram.h +++ b/include/hw/nvram/mac_nvram.h @@ -26,7 +26,7 @@ #ifndef MAC_NVRAM_H #define MAC_NVRAM_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" #define MACIO_NVRAM_SIZE 0x2000 diff --git a/include/hw/nvram/npcm7xx_otp.h b/include/hw/nvram/npcm7xx_otp.h index ea4b5d0731..77b05f8b82 100644 --- a/include/hw/nvram/npcm7xx_otp.h +++ b/include/hw/nvram/npcm7xx_otp.h @@ -16,7 +16,7 @@ #ifndef NPCM7XX_OTP_H #define NPCM7XX_OTP_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" /* Each OTP module holds 8192 bits of one-time programmable storage */ diff --git a/include/hw/pci-host/fsl_imx8m_phy.h b/include/hw/pci-host/fsl_imx8m_phy.h index 4f4875b37d..5f1b212fd9 100644 --- a/include/hw/pci-host/fsl_imx8m_phy.h +++ b/include/hw/pci-host/fsl_imx8m_phy.h @@ -11,7 +11,7 @@ #include "hw/sysbus.h" #include "qom/object.h" -#include "exec/memory.h" +#include "system/memory.h" #define TYPE_FSL_IMX8M_PCIE_PHY "fsl-imx8m-pcie-phy" OBJECT_DECLARE_SIMPLE_TYPE(FslImx8mPciePhyState, FSL_IMX8M_PCIE_PHY) diff --git a/include/hw/pci-host/pam.h b/include/hw/pci-host/pam.h index 005916f826..44f3908160 100644 --- a/include/hw/pci-host/pam.h +++ b/include/hw/pci-host/pam.h @@ -50,7 +50,7 @@ * 0xf0000 - 0xfffff System BIOS Area Memory Segments */ -#include "exec/memory.h" +#include "system/memory.h" #define SMRAM_C_BASE 0xa0000 #define SMRAM_C_END 0xc0000 diff --git a/include/hw/pci-host/remote.h b/include/hw/pci-host/remote.h index 690a01f0fe..5264c35936 100644 --- a/include/hw/pci-host/remote.h +++ b/include/hw/pci-host/remote.h @@ -11,7 +11,7 @@ #ifndef PCI_HOST_REMOTE_H #define PCI_HOST_REMOTE_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/pci/pcie_host.h" #define TYPE_REMOTE_PCIHOST "remote-pcihost" diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 822fbacdf0..c2fe6caa2c 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -1,7 +1,7 @@ #ifndef QEMU_PCI_H #define QEMU_PCI_H -#include "exec/memory.h" +#include "system/memory.h" #include "system/dma.h" #include "system/host_iommu_device.h" diff --git a/include/hw/pci/pcie_host.h b/include/hw/pci/pcie_host.h index 82d92177da..f09de76bfe 100644 --- a/include/hw/pci/pcie_host.h +++ b/include/hw/pci/pcie_host.h @@ -22,7 +22,7 @@ #define PCIE_HOST_H #include "hw/pci/pci_host.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #define TYPE_PCIE_HOST_BRIDGE "pcie-host-bridge" diff --git a/include/hw/pci/shpc.h b/include/hw/pci/shpc.h index a0789df153..ad1089567a 100644 --- a/include/hw/pci/shpc.h +++ b/include/hw/pci/shpc.h @@ -1,7 +1,7 @@ #ifndef SHPC_H #define SHPC_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/hotplug.h" #include "hw/pci/pci_device.h" #include "migration/vmstate.h" diff --git a/include/hw/ppc/mac_dbdma.h b/include/hw/ppc/mac_dbdma.h index 672c2be471..896ee4a2b1 100644 --- a/include/hw/ppc/mac_dbdma.h +++ b/include/hw/ppc/mac_dbdma.h @@ -23,7 +23,7 @@ #ifndef HW_MAC_DBDMA_H #define HW_MAC_DBDMA_H -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/iov.h" #include "system/dma.h" #include "hw/sysbus.h" diff --git a/include/hw/ppc/pnv_lpc.h b/include/hw/ppc/pnv_lpc.h index 174add4c53..266d56214f 100644 --- a/include/hw/ppc/pnv_lpc.h +++ b/include/hw/ppc/pnv_lpc.h @@ -20,7 +20,7 @@ #ifndef PPC_PNV_LPC_H #define PPC_PNV_LPC_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/ppc/pnv.h" #include "hw/qdev-core.h" #include "hw/isa/isa.h" /* For ISA_NUM_IRQS */ diff --git a/include/hw/ppc/pnv_occ.h b/include/hw/ppc/pnv_occ.h index 3ec42de0ff..013ea2e53e 100644 --- a/include/hw/ppc/pnv_occ.h +++ b/include/hw/ppc/pnv_occ.h @@ -20,7 +20,7 @@ #ifndef PPC_PNV_OCC_H #define PPC_PNV_OCC_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #define TYPE_PNV_OCC "pnv-occ" diff --git a/include/hw/ppc/pnv_sbe.h b/include/hw/ppc/pnv_sbe.h index b6b378ad14..48a8b86a80 100644 --- a/include/hw/ppc/pnv_sbe.h +++ b/include/hw/ppc/pnv_sbe.h @@ -20,7 +20,7 @@ #ifndef PPC_PNV_SBE_H #define PPC_PNV_SBE_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #define TYPE_PNV_SBE "pnv-sbe" diff --git a/include/hw/ppc/pnv_xscom.h b/include/hw/ppc/pnv_xscom.h index a927aea1c0..b14549db70 100644 --- a/include/hw/ppc/pnv_xscom.h +++ b/include/hw/ppc/pnv_xscom.h @@ -20,7 +20,7 @@ #ifndef PPC_PNV_XSCOM_H #define PPC_PNV_XSCOM_H -#include "exec/memory.h" +#include "system/memory.h" typedef struct PnvXScomInterface PnvXScomInterface; typedef struct PnvChip PnvChip; diff --git a/include/hw/ppc/ppc4xx.h b/include/hw/ppc/ppc4xx.h index 1bd9b8821b..2e94b00673 100644 --- a/include/hw/ppc/ppc4xx.h +++ b/include/hw/ppc/ppc4xx.h @@ -26,7 +26,7 @@ #define PPC4XX_H #include "hw/ppc/ppc.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" /* diff --git a/include/hw/ppc/vof.h b/include/hw/ppc/vof.h index d3f293da8b..2918aaab12 100644 --- a/include/hw/ppc/vof.h +++ b/include/hw/ppc/vof.h @@ -8,7 +8,7 @@ #include "qom/object.h" #include "exec/address-spaces.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/cpu-defs.h" typedef struct Vof { diff --git a/include/hw/ppc/xics.h b/include/hw/ppc/xics.h index e94d53405f..097fcdf00f 100644 --- a/include/hw/ppc/xics.h +++ b/include/hw/ppc/xics.h @@ -28,7 +28,7 @@ #ifndef XICS_H #define XICS_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "qom/object.h" diff --git a/include/hw/register.h b/include/hw/register.h index 6a076cfcdf..a913c52aee 100644 --- a/include/hw/register.h +++ b/include/hw/register.h @@ -12,7 +12,7 @@ #define REGISTER_H #include "hw/qdev-core.h" -#include "exec/memory.h" +#include "system/memory.h" #include "hw/registerfields.h" #include "qom/object.h" diff --git a/include/hw/remote/proxy-memory-listener.h b/include/hw/remote/proxy-memory-listener.h index c4f3efb928..ec516d8267 100644 --- a/include/hw/remote/proxy-memory-listener.h +++ b/include/hw/remote/proxy-memory-listener.h @@ -9,7 +9,7 @@ #ifndef PROXY_MEMORY_LISTENER_H #define PROXY_MEMORY_LISTENER_H -#include "exec/memory.h" +#include "system/memory.h" #include "io/channel.h" typedef struct ProxyMemoryListener { diff --git a/include/hw/sh4/sh_intc.h b/include/hw/sh4/sh_intc.h index f62d5c5e13..94f183121e 100644 --- a/include/hw/sh4/sh_intc.h +++ b/include/hw/sh4/sh_intc.h @@ -1,7 +1,7 @@ #ifndef SH_INTC_H #define SH_INTC_H -#include "exec/memory.h" +#include "system/memory.h" typedef unsigned char intc_enum; diff --git a/include/hw/southbridge/ich9.h b/include/hw/southbridge/ich9.h index 6c60017024..1e231e89c9 100644 --- a/include/hw/southbridge/ich9.h +++ b/include/hw/southbridge/ich9.h @@ -7,7 +7,7 @@ #include "hw/pci/pci.h" #include "hw/pci/pci_device.h" #include "hw/rtc/mc146818rtc.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/notify.h" #include "qom/object.h" diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index 81bbda10d3..7dc88aaa27 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -4,7 +4,7 @@ /* Devices attached directly to the main system bus. */ #include "hw/qdev-core.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #define QDEV_MAX_MMIO 32 diff --git a/include/hw/timer/npcm7xx_timer.h b/include/hw/timer/npcm7xx_timer.h index d45c051b56..e287375dce 100644 --- a/include/hw/timer/npcm7xx_timer.h +++ b/include/hw/timer/npcm7xx_timer.h @@ -16,7 +16,7 @@ #ifndef NPCM7XX_TIMER_H #define NPCM7XX_TIMER_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/sysbus.h" #include "qemu/timer.h" diff --git a/include/hw/tricore/tricore.h b/include/hw/tricore/tricore.h index c19ed3f013..4ffc0fe1d6 100644 --- a/include/hw/tricore/tricore.h +++ b/include/hw/tricore/tricore.h @@ -1,7 +1,7 @@ #ifndef HW_TRICORE_H #define HW_TRICORE_H -#include "exec/memory.h" +#include "system/memory.h" struct tricore_boot_info { uint64_t ram_size; diff --git a/include/hw/usb.h b/include/hw/usb.h index e410693d0c..26a9f3ecde 100644 --- a/include/hw/usb.h +++ b/include/hw/usb.h @@ -25,7 +25,7 @@ * THE SOFTWARE. */ -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "qemu/iov.h" #include "qemu/queue.h" diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 04b123a6c9..f5b3f45a43 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -21,7 +21,7 @@ #ifndef HW_VFIO_VFIO_COMMON_H #define HW_VFIO_VFIO_COMMON_H -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/queue.h" #include "qemu/notify.h" #include "ui/console.h" diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 4cff9943ab..6aca02fb3d 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -13,7 +13,7 @@ #ifndef HW_VFIO_VFIO_CONTAINER_BASE_H #define HW_VFIO_VFIO_CONTAINER_BASE_H -#include "exec/memory.h" +#include "system/memory.h" typedef struct VFIODevice VFIODevice; typedef struct VFIOIOMMUClass VFIOIOMMUClass; diff --git a/include/hw/virtio/vhost-backend.h b/include/hw/virtio/vhost-backend.h index 70c2e8ffee..d6df209a2f 100644 --- a/include/hw/virtio/vhost-backend.h +++ b/include/hw/virtio/vhost-backend.h @@ -11,7 +11,7 @@ #ifndef VHOST_BACKEND_H #define VHOST_BACKEND_H -#include "exec/memory.h" +#include "system/memory.h" typedef enum VhostBackendType { VHOST_BACKEND_TYPE_NONE = 0, diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index a9469d50bc..bb4b58e115 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -3,7 +3,7 @@ #include "hw/virtio/vhost-backend.h" #include "hw/virtio/virtio.h" -#include "exec/memory.h" +#include "system/memory.h" #define VHOST_F_DEVICE_IOTLB 63 #define VHOST_USER_F_PROTOCOL_FEATURES 30 diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 6386910280..7e0c471ea4 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -14,7 +14,7 @@ #ifndef QEMU_VIRTIO_H #define QEMU_VIRTIO_H -#include "exec/memory.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "net/net.h" #include "migration/vmstate.h" diff --git a/include/hw/xen/xen-pvh-common.h b/include/hw/xen/xen-pvh-common.h index 17c5a58a5a..5db83d88ec 100644 --- a/include/hw/xen/xen-pvh-common.h +++ b/include/hw/xen/xen-pvh-common.h @@ -9,7 +9,7 @@ #ifndef XEN_PVH_COMMON_H__ #define XEN_PVH_COMMON_H__ -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #include "hw/boards.h" #include "hw/pci-host/gpex.h" diff --git a/include/hw/xtensa/mx_pic.h b/include/hw/xtensa/mx_pic.h index 500424c8d3..cd316d86eb 100644 --- a/include/hw/xtensa/mx_pic.h +++ b/include/hw/xtensa/mx_pic.h @@ -28,7 +28,7 @@ #ifndef XTENSA_MX_PIC_H #define XTENSA_MX_PIC_H -#include "exec/memory.h" +#include "system/memory.h" struct XtensaMxPic; typedef struct XtensaMxPic XtensaMxPic; diff --git a/include/qemu/iova-tree.h b/include/qemu/iova-tree.h index 16d354a814..14e82a22d5 100644 --- a/include/qemu/iova-tree.h +++ b/include/qemu/iova-tree.h @@ -23,7 +23,7 @@ * for the thread safety issue. */ -#include "exec/memory.h" +#include "system/memory.h" #include "exec/hwaddr.h" #define IOVA_OK (0) diff --git a/include/qemu/reserved-region.h b/include/qemu/reserved-region.h index 8e6f0a97e2..9026cf08fd 100644 --- a/include/qemu/reserved-region.h +++ b/include/qemu/reserved-region.h @@ -20,7 +20,7 @@ #ifndef QEMU_RESERVED_REGION_H #define QEMU_RESERVED_REGION_H -#include "exec/memory.h" +#include "system/memory.h" /* * Insert a new region into a sorted list of reserved regions. In case diff --git a/include/system/dma.h b/include/system/dma.h index e142f7efa6..aaa03b9711 100644 --- a/include/system/dma.h +++ b/include/system/dma.h @@ -10,7 +10,7 @@ #ifndef DMA_H #define DMA_H -#include "exec/memory.h" +#include "system/memory.h" #include "exec/address-spaces.h" #include "block/block.h" #include "block/accounting.h" diff --git a/include/system/hostmem.h b/include/system/hostmem.h index 62642e602c..88fa791ac7 100644 --- a/include/system/hostmem.h +++ b/include/system/hostmem.h @@ -16,7 +16,7 @@ #include "system/numa.h" #include "qapi/qapi-types-machine.h" #include "qom/object.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/bitmap.h" #include "qemu/thread-context.h" diff --git a/include/system/kvm_int.h b/include/system/kvm_int.h index 4de6106869..756a3c0a25 100644 --- a/include/system/kvm_int.h +++ b/include/system/kvm_int.h @@ -9,7 +9,7 @@ #ifndef QEMU_KVM_INT_H #define QEMU_KVM_INT_H -#include "exec/memory.h" +#include "system/memory.h" #include "qapi/qapi-types-common.h" #include "qemu/accel.h" #include "qemu/queue.h" diff --git a/include/exec/memory.h b/include/system/memory.h similarity index 99% rename from include/exec/memory.h rename to include/system/memory.h index 2f84a7cfed..fbbf4cf911 100644 --- a/include/exec/memory.h +++ b/include/system/memory.h @@ -11,10 +11,8 @@ * */ -#ifndef MEMORY_H -#define MEMORY_H - -#ifndef CONFIG_USER_ONLY +#ifndef SYSTEM_MEMORY_H +#define SYSTEM_MEMORY_H #include "exec/cpu-common.h" #include "exec/hwaddr.h" @@ -3197,5 +3195,3 @@ void ram_block_add_cpr_blocker(RAMBlock *rb, Error **errp); void ram_block_del_cpr_blocker(RAMBlock *rb); #endif - -#endif diff --git a/include/system/vhost-user-backend.h b/include/system/vhost-user-backend.h index 327b0b84f1..5ed953cd53 100644 --- a/include/system/vhost-user-backend.h +++ b/include/system/vhost-user-backend.h @@ -13,7 +13,7 @@ #define QEMU_VHOST_USER_BACKEND_H #include "qom/object.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/option.h" #include "qemu/bitmap.h" #include "hw/virtio/vhost.h" diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index 4cd14779d6..09caf92f87 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -27,7 +27,7 @@ #include "qobject/qdict.h" #include "system/kvm.h" #include "system/runstate.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/xxhash.h" #include "migration.h" diff --git a/migration/rdma.c b/migration/rdma.c index 76fb034923..d9603ab603 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -30,7 +30,7 @@ #include "qemu/sockets.h" #include "qemu/bitmap.h" #include "qemu/coroutine.h" -#include "exec/memory.h" +#include "system/memory.h" #include #include #include diff --git a/migration/rdma.h b/migration/rdma.h index f55f28bbed..4d3386b84a 100644 --- a/migration/rdma.h +++ b/migration/rdma.h @@ -19,7 +19,7 @@ #ifndef QEMU_MIGRATION_RDMA_H #define QEMU_MIGRATION_RDMA_H -#include "exec/memory.h" +#include "system/memory.h" void rdma_start_outgoing_migration(void *opaque, InetSocketAddress *host_port, Error **errp); diff --git a/migration/savevm.c b/migration/savevm.c index ce158c3512..c33200a33f 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -48,7 +48,7 @@ #include "qapi/qapi-builtin-visit.h" #include "qemu/error-report.h" #include "system/cpus.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/target_page.h" #include "trace.h" #include "qemu/iov.h" diff --git a/monitor/hmp-cmds-target.c b/monitor/hmp-cmds-target.c index 239c2a61a4..6654d31406 100644 --- a/monitor/hmp-cmds-target.c +++ b/monitor/hmp-cmds-target.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "disas/disas.h" #include "exec/address-spaces.h" -#include "exec/memory.h" +#include "system/memory.h" #include "monitor/hmp-target.h" #include "monitor/monitor-internal.h" #include "qapi/error.h" diff --git a/rust/wrapper.h b/rust/wrapper.h index d4fec54657..94866b7e32 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -52,7 +52,7 @@ typedef enum memory_order { #include "qemu-io.h" #include "system/system.h" #include "hw/sysbus.h" -#include "exec/memory.h" +#include "system/memory.h" #include "chardev/char-fe.h" #include "hw/clock.h" #include "hw/qdev-clock.h" diff --git a/scripts/analyze-inclusions b/scripts/analyze-inclusions index b6280f25c8..d2c566667d 100644 --- a/scripts/analyze-inclusions +++ b/scripts/analyze-inclusions @@ -53,7 +53,7 @@ echo $(grep_include -F 'trace/generated-tracers.h') files include generated-trac echo $(grep_include -F 'qapi/error.h') files include qapi/error.h echo $(grep_include -F 'qom/object.h') files include qom/object.h echo $(grep_include -F 'block/aio.h') files include block/aio.h -echo $(grep_include -F 'exec/memory.h') files include exec/memory.h +echo $(grep_include -F 'system/memory.h') files include system/memory.h echo $(grep_include -F 'fpu/softfloat.h') files include fpu/softfloat.h echo $(grep_include -F 'qemu/bswap.h') files include qemu/bswap.h echo diff --git a/stubs/ram-block.c b/stubs/ram-block.c index 108197683b..e88fab31a5 100644 --- a/stubs/ram-block.c +++ b/stubs/ram-block.c @@ -1,7 +1,7 @@ #include "qemu/osdep.h" #include "exec/ramlist.h" #include "exec/cpu-common.h" -#include "exec/memory.h" +#include "system/memory.h" void *qemu_ram_get_host_addr(RAMBlock *rb) { diff --git a/system/dirtylimit.c b/system/dirtylimit.c index 7dedef8dd4..30cd09f3d1 100644 --- a/system/dirtylimit.c +++ b/system/dirtylimit.c @@ -19,7 +19,7 @@ #include "system/dirtylimit.h" #include "monitor/hmp.h" #include "monitor/monitor.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/target_page.h" #include "hw/boards.h" #include "system/kvm.h" diff --git a/system/ioport.c b/system/ioport.c index 89daae9d60..2291739039 100644 --- a/system/ioport.c +++ b/system/ioport.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "exec/ioport.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/address-spaces.h" #include "trace.h" diff --git a/system/memory.c b/system/memory.c index eddd21a6cd..2865d0deb1 100644 --- a/system/memory.c +++ b/system/memory.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "qapi/error.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qapi/visitor.h" #include "qemu/bitops.h" #include "qemu/error-report.h" diff --git a/system/memory_mapping.c b/system/memory_mapping.c index 37d3325f77..8538a8241e 100644 --- a/system/memory_mapping.c +++ b/system/memory_mapping.c @@ -16,7 +16,7 @@ #include "qapi/error.h" #include "system/memory_mapping.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/address-spaces.h" #include "hw/core/cpu.h" diff --git a/system/physmem.c b/system/physmem.c index 333a5eb94d..e61fea41b5 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -50,7 +50,7 @@ #include "qemu/log.h" #include "qemu/memalign.h" #include "qemu/memfd.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/ioport.h" #include "system/dma.h" #include "system/hostmem.h" diff --git a/system/qtest.c b/system/qtest.c index 12152efbcd..5407289154 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -17,7 +17,7 @@ #include "system/runstate.h" #include "chardev/char-fe.h" #include "exec/ioport.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/tswap.h" #include "hw/qdev-core.h" #include "hw/irq.h" diff --git a/target/avr/cpu.h b/target/avr/cpu.h index 9862705c6a..b0518a1f60 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -23,7 +23,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" -#include "exec/memory.h" +#include "system/memory.h" #ifdef CONFIG_USER_ONLY #error "AVR 8-bit does not support user mode" diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 254e4fbdcd..02ef6ddecb 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -14,7 +14,7 @@ #include "hw/registerfields.h" #include "qemu/timer.h" #ifndef CONFIG_USER_ONLY -#include "exec/memory.h" +#include "system/memory.h" #endif #include "cpu-csr.h" #include "cpu-qom.h" diff --git a/target/mips/cpu.h b/target/mips/cpu.h index f6877ece8b..9ef72a95d7 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -4,7 +4,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" #ifndef CONFIG_USER_ONLY -#include "exec/memory.h" +#include "system/memory.h" #endif #include "fpu/softfloat-types.h" #include "hw/clock.h" diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 7663b62d01..ec6a0a8b66 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -36,7 +36,7 @@ #include "migration/vmstate.h" #include "hw/qdev-clock.h" #ifndef CONFIG_USER_ONLY -#include "exec/memory.h" +#include "system/memory.h" #endif diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c index d107a496da..239be9372d 100644 --- a/tests/qtest/fuzz/generic_fuzz.c +++ b/tests/qtest/fuzz/generic_fuzz.c @@ -20,7 +20,7 @@ #include "tests/qtest/libqos/pci-pc.h" #include "fuzz.h" #include "string.h" -#include "exec/memory.h" +#include "system/memory.h" #include "exec/ramblock.h" #include "hw/qdev-core.h" #include "hw/pci/pci.h" diff --git a/tests/qtest/fuzz/qos_fuzz.c b/tests/qtest/fuzz/qos_fuzz.c index d3839bf999..9afe8bf6d8 100644 --- a/tests/qtest/fuzz/qos_fuzz.c +++ b/tests/qtest/fuzz/qos_fuzz.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/main-loop.h" #include "tests/qtest/libqtest.h" diff --git a/tests/unit/test-resv-mem.c b/tests/unit/test-resv-mem.c index cd8f7318cc..4de2d042d1 100644 --- a/tests/unit/test-resv-mem.c +++ b/tests/unit/test-resv-mem.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "qemu/range.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qemu/reserved-region.h" #define DEBUG 0 diff --git a/ui/console.c b/ui/console.c index 6456e8dd90..6cd122cf40 100644 --- a/ui/console.c +++ b/ui/console.c @@ -35,7 +35,7 @@ #include "qemu/option.h" #include "chardev/char.h" #include "trace.h" -#include "exec/memory.h" +#include "system/memory.h" #include "qom/object.h" #include "qemu/memfd.h" diff --git a/util/vfio-helpers.c b/util/vfio-helpers.c index f8bab46c68..fdff042ab4 100644 --- a/util/vfio-helpers.c +++ b/util/vfio-helpers.c @@ -16,7 +16,7 @@ #include "qapi/error.h" #include "exec/ramlist.h" #include "exec/cpu-common.h" -#include "exec/memory.h" +#include "system/memory.h" #include "trace.h" #include "qemu/error-report.h" #include "standard-headers/linux/pci_regs.h" From dfc56946a70052136126f6a207f237af8032d74a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 12:31:54 -0700 Subject: [PATCH 0114/2760] include/system: Move exec/address-spaces.h to system/address-spaces.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the existing includes with sed. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/hvf/hvf-accel-ops.c | 2 +- hw/acpi/erst.c | 2 +- hw/arm/aspeed_ast10x0.c | 2 +- hw/arm/bananapi_m2u.c | 2 +- hw/arm/collie.c | 2 +- hw/arm/exynos4_boards.c | 2 +- hw/arm/fsl-imx31.c | 2 +- hw/arm/fsl-imx8mp.c | 2 +- hw/arm/imx8mp-evk.c | 2 +- hw/arm/integratorcp.c | 2 +- hw/arm/kzm.c | 2 +- hw/arm/microbit.c | 2 +- hw/arm/mps2-tz.c | 2 +- hw/arm/mps2.c | 2 +- hw/arm/mps3r.c | 2 +- hw/arm/msf2-soc.c | 2 +- hw/arm/msf2-som.c | 2 +- hw/arm/musca.c | 2 +- hw/arm/omap1.c | 2 +- hw/arm/omap_sx1.c | 2 +- hw/arm/orangepi.c | 2 +- hw/arm/stellaris.c | 2 +- hw/arm/stm32f100_soc.c | 2 +- hw/arm/stm32f205_soc.c | 2 +- hw/arm/stm32f405_soc.c | 2 +- hw/arm/stm32l4x5_soc.c | 2 +- hw/avr/atmega.c | 2 +- hw/char/goldfish_tty.c | 2 +- hw/char/omap_uart.c | 2 +- hw/char/riscv_htif.c | 2 +- hw/core/cpu-system.c | 2 +- hw/core/null-machine.c | 2 +- hw/core/sysbus.c | 2 +- hw/display/apple-gfx.m | 2 +- hw/dma/rc4030.c | 2 +- hw/hyperv/hv-balloon.c | 2 +- hw/hyperv/hyperv.c | 2 +- hw/i386/kvm/xen_evtchn.c | 2 +- hw/i386/kvm/xen_gnttab.c | 2 +- hw/i386/kvm/xen_overlay.c | 2 +- hw/i386/sgx-epc.c | 2 +- hw/i386/sgx.c | 2 +- hw/i386/vapic.c | 2 +- hw/ide/ahci-sysbus.c | 2 +- hw/input/lasips2.c | 2 +- hw/intc/loongarch_extioi.c | 2 +- hw/intc/riscv_aplic.c | 2 +- hw/intc/riscv_imsic.c | 2 +- hw/loongarch/virt.c | 2 +- hw/mem/memory-device.c | 2 +- hw/microblaze/petalogix_ml605_mmu.c | 2 +- hw/microblaze/petalogix_s3adsp1800_mmu.c | 2 +- hw/microblaze/xlnx-zynqmp-pmu.c | 2 +- hw/mips/mipssim.c | 2 +- hw/misc/allwinner-h3-dramc.c | 2 +- hw/misc/allwinner-r40-dramc.c | 2 +- hw/misc/ivshmem-flat.c | 2 +- hw/misc/mac_via.c | 2 +- hw/net/i82596.c | 2 +- hw/net/i82596.h | 2 +- hw/nvram/fw_cfg.c | 2 +- hw/openrisc/openrisc_sim.c | 2 +- hw/openrisc/virt.c | 2 +- hw/pci-host/mv64361.c | 2 +- hw/ppc/pegasos2.c | 2 +- hw/ppc/pnv_psi.c | 2 +- hw/ppc/ppc4xx_sdram.c | 2 +- hw/ppc/prep_systemio.c | 2 +- hw/ppc/rs6000_mc.c | 2 +- hw/ppc/spapr_ovec.c | 2 +- hw/ppc/vof.c | 2 +- hw/remote/iommu.c | 2 +- hw/riscv/microblaze-v-generic.c | 2 +- hw/riscv/opentitan.c | 2 +- hw/riscv/shakti_c.c | 2 +- hw/s390x/css.c | 2 +- hw/s390x/ipl.h | 2 +- hw/s390x/s390-skeys.c | 2 +- hw/s390x/virtio-ccw.c | 2 +- hw/sparc/sun4m_iommu.c | 2 +- hw/sparc64/sun4u_iommu.c | 2 +- hw/timer/hpet.c | 2 +- hw/tpm/tpm_crb.c | 2 +- hw/vfio/ap.c | 2 +- hw/vfio/ccw.c | 2 +- hw/vfio/common.c | 2 +- hw/vfio/container.c | 2 +- hw/vfio/platform.c | 2 +- hw/vfio/spapr.c | 2 +- hw/virtio/vhost-vdpa.c | 2 +- hw/virtio/virtio-balloon.c | 2 +- hw/virtio/virtio-bus.c | 2 +- include/hw/misc/lasi.h | 2 +- include/hw/nubus/nubus.h | 2 +- include/hw/ppc/vof.h | 2 +- include/hw/tricore/triboard.h | 2 +- include/{exec => system}/address-spaces.h | 8 ++------ include/system/dma.h | 2 +- monitor/hmp-cmds-target.c | 2 +- monitor/hmp-cmds.c | 2 +- rust/wrapper.h | 2 +- system/ioport.c | 2 +- system/memory.c | 2 +- system/memory_mapping.c | 2 +- target/arm/hvf/hvf.c | 2 +- target/arm/kvm.c | 2 +- target/avr/cpu.c | 2 +- target/i386/cpu-apic.c | 2 +- target/i386/cpu.c | 2 +- target/i386/hvf/vmx.h | 2 +- target/i386/kvm/xen-emu.c | 2 +- target/i386/nvmm/nvmm-all.c | 2 +- target/i386/sev.c | 2 +- target/i386/tcg/system/misc_helper.c | 2 +- target/i386/tcg/system/tcg-cpu.c | 2 +- target/i386/whpx/whpx-all.c | 2 +- target/loongarch/kvm/kvm.c | 2 +- target/riscv/kvm/kvm-cpu.c | 2 +- target/s390x/mmu_helper.c | 2 +- target/s390x/sigp.c | 2 +- target/s390x/tcg/excp_helper.c | 2 +- target/xtensa/dbg_helper.c | 2 +- 122 files changed, 123 insertions(+), 127 deletions(-) rename include/{exec => system}/address-spaces.h (89%) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 12fc30c276..601c3bc0ac 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -50,7 +50,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "exec/exec-all.h" #include "gdbstub/enums.h" #include "hw/boards.h" diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index 5c4c1dc638..2e49b551f2 100644 --- a/hw/acpi/erst.c +++ b/hw/acpi/erst.c @@ -23,7 +23,7 @@ #include "hw/acpi/acpi-defs.h" #include "hw/acpi/aml-build.h" #include "hw/acpi/bios-linker-loader.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/hostmem.h" #include "hw/acpi/erst.h" #include "trace.h" diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index ec329f4991..21ffab10f3 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/system.h" #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" diff --git a/hw/arm/bananapi_m2u.c b/hw/arm/bananapi_m2u.c index 4d84d10d24..b750a575f7 100644 --- a/hw/arm/bananapi_m2u.c +++ b/hw/arm/bananapi_m2u.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/boards.h" diff --git a/hw/arm/collie.c b/hw/arm/collie.c index eaa5c52d45..e83aee58c6 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -16,7 +16,7 @@ #include "strongarm.h" #include "hw/arm/boot.h" #include "hw/block/flash.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/object.h" #include "qemu/error-report.h" diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index 43dc89d902..2d8f2d7326 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -28,7 +28,7 @@ #include "hw/sysbus.h" #include "net/net.h" #include "hw/arm/boot.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/arm/exynos4210.h" #include "hw/net/lan9118.h" #include "hw/qdev-properties.h" diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 9de0f2148f..2a8ffb15f7 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -23,7 +23,7 @@ #include "qapi/error.h" #include "hw/arm/fsl-imx31.h" #include "system/system.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/qdev-properties.h" #include "chardev/char.h" #include "target/arm/cpu-qom.h" diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 82edf61082..af7a7e6745 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -9,7 +9,7 @@ */ #include "qemu/osdep.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/arm/bsa.h" #include "hw/arm/fsl-imx8mp.h" #include "hw/intc/arm_gicv3.h" diff --git a/hw/arm/imx8mp-evk.c b/hw/arm/imx8mp-evk.c index b5aec06ec5..b3082fa60d 100644 --- a/hw/arm/imx8mp-evk.c +++ b/hw/arm/imx8mp-evk.c @@ -7,7 +7,7 @@ */ #include "qemu/osdep.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/arm/boot.h" #include "hw/arm/fsl-imx8mp.h" #include "hw/boards.h" diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index 8aa2e6e98e..ac0c6c6096 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -16,7 +16,7 @@ #include "hw/misc/arm_integrator_debug.h" #include "hw/net/smc91c111.h" #include "net/net.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/runstate.h" #include "system/system.h" #include "qemu/log.h" diff --git a/hw/arm/kzm.c b/hw/arm/kzm.c index 08d2b3025c..362c145409 100644 --- a/hw/arm/kzm.c +++ b/hw/arm/kzm.c @@ -19,7 +19,7 @@ #include "hw/arm/boot.h" #include "hw/boards.h" #include "qemu/error-report.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "net/net.h" #include "hw/net/lan9118.h" #include "hw/char/serial-mm.h" diff --git a/hw/arm/microbit.c b/hw/arm/microbit.c index 3f56fb45ce..ade363daaa 100644 --- a/hw/arm/microbit.c +++ b/hw/arm/microbit.c @@ -13,7 +13,7 @@ #include "hw/boards.h" #include "hw/arm/boot.h" #include "system/system.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/arm/nrf51_soc.h" #include "hw/i2c/microbit_i2c.h" diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 13ed868b6b..b0633a5a69 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -54,7 +54,7 @@ #include "hw/arm/armv7m.h" #include "hw/or-irq.h" #include "hw/boards.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/system.h" #include "system/reset.h" #include "hw/misc/unimp.h" diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 3f8db0cab6..6958485a66 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -33,7 +33,7 @@ #include "hw/arm/armv7m.h" #include "hw/or-irq.h" #include "hw/boards.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/system.h" #include "hw/qdev-properties.h" #include "hw/misc/unimp.h" diff --git a/hw/arm/mps3r.c b/hw/arm/mps3r.c index 1bddb5e822..4dd1e8a718 100644 --- a/hw/arm/mps3r.c +++ b/hw/arm/mps3r.c @@ -28,7 +28,7 @@ #include "qemu/units.h" #include "qapi/error.h" #include "qobject/qlist.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "cpu.h" #include "system/system.h" #include "hw/boards.h" diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index e8a5b231ba..bc9b419e37 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/char/serial-mm.h" #include "hw/arm/msf2-soc.h" #include "hw/misc/unimp.h" diff --git a/hw/arm/msf2-som.c b/hw/arm/msf2-som.c index 9b20f1e2c9..29c76c6860 100644 --- a/hw/arm/msf2-som.c +++ b/hw/arm/msf2-som.c @@ -33,7 +33,7 @@ #include "hw/qdev-properties.h" #include "hw/arm/boot.h" #include "hw/qdev-clock.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/arm/msf2-soc.h" #define DDR_BASE_ADDRESS 0xA0000000 diff --git a/hw/arm/musca.c b/hw/arm/musca.c index e9c092abc3..a4f43f1992 100644 --- a/hw/arm/musca.c +++ b/hw/arm/musca.c @@ -22,7 +22,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/system.h" #include "hw/arm/boot.h" #include "hw/arm/armsse.h" diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 3ee10b4777..91d7e3f04b 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -23,7 +23,7 @@ #include "qemu/main-loop.h" #include "qapi/error.h" #include "cpu.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/hw.h" #include "hw/irq.h" #include "hw/qdev-properties.h" diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index 24b4043183..aa1e96b3ad 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -34,7 +34,7 @@ #include "hw/arm/boot.h" #include "hw/block/flash.h" #include "system/qtest.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qemu/cutils.h" #include "qemu/error-report.h" diff --git a/hw/arm/orangepi.c b/hw/arm/orangepi.c index 634af9b0a1..e0956880d1 100644 --- a/hw/arm/orangepi.c +++ b/hw/arm/orangepi.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/boards.h" diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index 3361111360..cbe914c93e 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -20,7 +20,7 @@ #include "net/net.h" #include "hw/boards.h" #include "qemu/log.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/system.h" #include "hw/arm/armv7m.h" #include "hw/char/pl011.h" diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c index 53b5636452..0eabaf8d9b 100644 --- a/hw/arm/stm32f100_soc.c +++ b/hw/arm/stm32f100_soc.c @@ -27,7 +27,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "hw/arm/boot.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/arm/stm32f100_soc.h" #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index 47a54e592b..32e96912f0 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "hw/arm/boot.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/arm/stm32f205_soc.h" #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c index 18d8824f29..bba9060daf 100644 --- a/hw/arm/stm32f405_soc.c +++ b/hw/arm/stm32f405_soc.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/system.h" #include "hw/arm/stm32f405_soc.h" #include "hw/qdev-clock.h" diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c index dbf75329f7..6278d354c8 100644 --- a/hw/arm/stm32l4x5_soc.c +++ b/hw/arm/stm32l4x5_soc.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/system.h" #include "hw/or-irq.h" #include "hw/arm/stm32l4x5_soc.h" diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index cb721c96b7..2e8b8e8c67 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -13,7 +13,7 @@ #include "qemu/units.h" #include "qapi/error.h" #include "system/memory.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/system.h" #include "hw/qdev-properties.h" #include "hw/sysbus.h" diff --git a/hw/char/goldfish_tty.c b/hw/char/goldfish_tty.c index 7374561141..f0891ffa4d 100644 --- a/hw/char/goldfish_tty.c +++ b/hw/char/goldfish_tty.c @@ -15,7 +15,7 @@ #include "chardev/char-fe.h" #include "qemu/log.h" #include "trace.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/dma.h" #include "hw/char/goldfish_tty.h" diff --git a/hw/char/omap_uart.c b/hw/char/omap_uart.c index 07fb868965..8cbf6ce803 100644 --- a/hw/char/omap_uart.c +++ b/hw/char/omap_uart.c @@ -21,7 +21,7 @@ #include "chardev/char.h" #include "hw/arm/omap.h" #include "hw/char/serial-mm.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" /* UARTs */ struct omap_uart_s { diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c index ec5db5a597..c884be5d75 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -28,7 +28,7 @@ #include "chardev/char-fe.h" #include "qemu/timer.h" #include "qemu/error-report.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "exec/tswap.h" #include "system/dma.h" #include "system/runstate.h" diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 5ef8c24b5b..82b68b8927 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "exec/cputlb.h" #include "system/memory.h" #include "exec/tb-flush.h" diff --git a/hw/core/null-machine.c b/hw/core/null-machine.c index 7f1fb562be..a6e477a2d8 100644 --- a/hw/core/null-machine.c +++ b/hw/core/null-machine.c @@ -14,7 +14,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "hw/boards.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/core/cpu.h" static void machine_none_init(MachineState *mch) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 98819d5dc6..6eb4c0f15a 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -21,7 +21,7 @@ #include "qapi/error.h" #include "hw/sysbus.h" #include "monitor/monitor.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" static void sysbus_dev_print(Monitor *mon, DeviceState *dev, int indent); static char *sysbus_get_fw_dev_path(DeviceState *dev); diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m index c4323574e1..2ff1c90df7 100644 --- a/hw/display/apple-gfx.m +++ b/hw/display/apple-gfx.m @@ -18,7 +18,7 @@ #include "qapi/visitor.h" #include "qapi/error.h" #include "block/aio-wait.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/dma.h" #include "migration/blocker.h" #include "ui/console.h" diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index 5bf54347ed..6842e7d491 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -32,7 +32,7 @@ #include "qemu/timer.h" #include "qemu/log.h" #include "qemu/module.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "trace.h" #include "qom/object.h" diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index 6f33c3e741..0b1da723c8 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "hv-balloon-internal.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "exec/cpu-common.h" #include "exec/ramblock.h" #include "hw/boards.h" diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index 382c62d668..d21e428eae 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -11,7 +11,7 @@ #include "qemu/main-loop.h" #include "qemu/module.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/memory.h" #include "system/kvm.h" #include "qemu/bitops.h" diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index 9b8b092bc2..f9223ef1a1 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -23,7 +23,7 @@ #include "qobject/qdict.h" #include "qom/object.h" #include "exec/target_page.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "migration/vmstate.h" #include "trace.h" diff --git a/hw/i386/kvm/xen_gnttab.c b/hw/i386/kvm/xen_gnttab.c index 7b843a72b1..430ba62896 100644 --- a/hw/i386/kvm/xen_gnttab.c +++ b/hw/i386/kvm/xen_gnttab.c @@ -17,7 +17,7 @@ #include "qapi/error.h" #include "qom/object.h" #include "exec/target_page.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "migration/vmstate.h" #include "hw/sysbus.h" diff --git a/hw/i386/kvm/xen_overlay.c b/hw/i386/kvm/xen_overlay.c index db9aa7942d..a2b26e9906 100644 --- a/hw/i386/kvm/xen_overlay.c +++ b/hw/i386/kvm/xen_overlay.c @@ -16,7 +16,7 @@ #include "qapi/error.h" #include "qom/object.h" #include "exec/target_page.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "migration/vmstate.h" #include "hw/sysbus.h" diff --git a/hw/i386/sgx-epc.c b/hw/i386/sgx-epc.c index 875e1c5c33..00b220d4d6 100644 --- a/hw/i386/sgx-epc.c +++ b/hw/i386/sgx-epc.c @@ -17,7 +17,7 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "target/i386/cpu.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" static const Property sgx_epc_properties[] = { DEFINE_PROP_UINT64(SGX_EPC_ADDR_PROP, SGXEPCDevice, addr, 0), diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index e665e2111c..5685c4fb80 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -20,7 +20,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qapi/qapi-commands-misc-target.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/hw_accel.h" #include "system/reset.h" #include diff --git a/hw/i386/vapic.c b/hw/i386/vapic.c index 14de9b7a82..26aae64e5d 100644 --- a/hw/i386/vapic.c +++ b/hw/i386/vapic.c @@ -16,7 +16,7 @@ #include "system/hw_accel.h" #include "system/kvm.h" #include "system/runstate.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/i386/apic_internal.h" #include "hw/sysbus.h" #include "hw/boards.h" diff --git a/hw/ide/ahci-sysbus.c b/hw/ide/ahci-sysbus.c index 03a5bd42d0..3c1935d81c 100644 --- a/hw/ide/ahci-sysbus.c +++ b/hw/ide/ahci-sysbus.c @@ -22,7 +22,7 @@ */ #include "qemu/osdep.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" diff --git a/hw/input/lasips2.c b/hw/input/lasips2.c index d9f8c36778..987034efd3 100644 --- a/hw/input/lasips2.c +++ b/hw/input/lasips2.c @@ -29,7 +29,7 @@ #include "hw/input/lasips2.h" #include "exec/hwaddr.h" #include "trace.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "migration/vmstate.h" #include "hw/irq.h" #include "qapi/error.h" diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index a51a215e6e..a558c50185 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -11,7 +11,7 @@ #include "qapi/error.h" #include "hw/irq.h" #include "hw/loongarch/virt.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/intc/loongarch_extioi.h" #include "trace.h" diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 5964cde7e0..789c4a4d6e 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -22,7 +22,7 @@ #include "qemu/module.h" #include "qemu/error-report.h" #include "qemu/bswap.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/sysbus.h" #include "hw/pci/msi.h" #include "hw/boards.h" diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c index 241b12fef0..852f413e5a 100644 --- a/hw/intc/riscv_imsic.c +++ b/hw/intc/riscv_imsic.c @@ -22,7 +22,7 @@ #include "qemu/module.h" #include "qemu/error-report.h" #include "qemu/bswap.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/sysbus.h" #include "hw/pci/msi.h" #include "hw/boards.h" diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 65c9027feb..f1eb42c2c1 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -18,7 +18,7 @@ #include "system/reset.h" #include "system/rtc.h" #include "hw/loongarch/virt.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/irq.h" #include "net/net.h" #include "hw/loader.h" diff --git a/hw/mem/memory-device.c b/hw/mem/memory-device.c index 1de8dfec7d..1a432e9bd2 100644 --- a/hw/mem/memory-device.c +++ b/hw/mem/memory-device.c @@ -17,7 +17,7 @@ #include "qemu/range.h" #include "hw/virtio/vhost.h" #include "system/kvm.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "trace.h" static bool memory_device_is_empty(const MemoryDeviceState *md) diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index 21ad215e44..c887c7a99e 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -36,7 +36,7 @@ #include "hw/boards.h" #include "hw/char/serial-mm.h" #include "hw/qdev-properties.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/ssi/ssi.h" #include "boot.h" diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index bdba2006b7..f976c90bd2 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -33,7 +33,7 @@ #include "system/system.h" #include "hw/boards.h" #include "hw/misc/unimp.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/char/xilinx_uartlite.h" #include "boot.h" diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index bdbf7328bf..0922c65295 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/boards.h" #include "cpu.h" #include "boot.h" diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c index c530688e76..b6dabf2893 100644 --- a/hw/mips/mipssim.c +++ b/hw/mips/mipssim.c @@ -28,7 +28,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/datadir.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/clock.h" #include "hw/mips/mips.h" #include "hw/char/serial-mm.h" diff --git a/hw/misc/allwinner-h3-dramc.c b/hw/misc/allwinner-h3-dramc.c index c4f3eb9274..74ff71b753 100644 --- a/hw/misc/allwinner-h3-dramc.c +++ b/hw/misc/allwinner-h3-dramc.c @@ -24,7 +24,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "hw/misc/allwinner-h3-dramc.h" diff --git a/hw/misc/allwinner-r40-dramc.c b/hw/misc/allwinner-r40-dramc.c index 96e1848c21..5908a059e8 100644 --- a/hw/misc/allwinner-r40-dramc.c +++ b/hw/misc/allwinner-r40-dramc.c @@ -24,7 +24,7 @@ #include "migration/vmstate.h" #include "qemu/log.h" #include "qemu/module.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qemu/bitops.h" diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c index 40309a8ff3..076c4b42de 100644 --- a/hw/misc/ivshmem-flat.c +++ b/hw/misc/ivshmem-flat.c @@ -17,7 +17,7 @@ #include "hw/qdev-properties-system.h" #include "hw/sysbus.h" #include "chardev/char-fe.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "trace.h" #include "hw/misc/ivshmem-flat.h" diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 03b1feda50..3c0819c58a 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -16,7 +16,7 @@ */ #include "qemu/osdep.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "migration/vmstate.h" #include "hw/sysbus.h" #include "hw/irq.h" diff --git a/hw/net/i82596.c b/hw/net/i82596.c index ee919dab3c..64ed3c8390 100644 --- a/hw/net/i82596.c +++ b/hw/net/i82596.c @@ -15,7 +15,7 @@ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qemu/module.h" #include "trace.h" #include "i82596.h" diff --git a/hw/net/i82596.h b/hw/net/i82596.h index 4bdfcaf856..dc1fa1a1dc 100644 --- a/hw/net/i82596.h +++ b/hw/net/i82596.h @@ -4,7 +4,7 @@ #define I82596_IOPORT_SIZE 0x20 #include "system/memory.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #define PORT_RESET 0x00 /* reset 82596 */ #define PORT_SELFTEST 0x01 /* selftest */ diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index a757939cfb..cbfb2b5303 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -27,7 +27,7 @@ #include "system/system.h" #include "system/dma.h" #include "system/reset.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/boards.h" #include "hw/nvram/fw_cfg.h" #include "hw/qdev-properties.h" diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index 83d7c2a8af..c2284a7d41 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -28,7 +28,7 @@ #include "net/net.h" #include "hw/openrisc/boot.h" #include "hw/qdev-properties.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/device_tree.h" #include "system/system.h" #include "hw/sysbus.h" diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index 3055306783..0d1c1f103c 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -11,7 +11,7 @@ #include "qemu/guest-random.h" #include "qapi/error.h" #include "cpu.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/irq.h" #include "hw/boards.h" #include "hw/char/serial-mm.h" diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c index 9c41c155fb..a297318c6e 100644 --- a/hw/pci-host/mv64361.c +++ b/hw/pci-host/mv64361.c @@ -17,7 +17,7 @@ #include "hw/irq.h" #include "hw/intc/i8259.h" #include "hw/qdev-properties.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qemu/log.h" #include "qemu/error-report.h" #include "trace.h" diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 246d6d633b..7b2dc6985c 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -31,7 +31,7 @@ #include "qemu/error-report.h" #include "system/kvm.h" #include "kvm_ppc.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/qom-qobject.h" #include "qobject/qdict.h" #include "trace.h" diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 1fe11dde50..f832ee61e8 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -18,7 +18,7 @@ */ #include "qemu/osdep.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/irq.h" #include "target/ppc/cpu.h" #include "qemu/log.h" diff --git a/hw/ppc/ppc4xx_sdram.c b/hw/ppc/ppc4xx_sdram.c index 562bff8d53..bf0faad9e7 100644 --- a/hw/ppc/ppc4xx_sdram.c +++ b/hw/ppc/ppc4xx_sdram.c @@ -34,7 +34,7 @@ #include "qapi/error.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "exec/address-spaces.h" /* get_system_memory() */ +#include "system/address-spaces.h" /* get_system_memory() */ #include "hw/irq.h" #include "hw/qdev-properties.h" #include "hw/ppc/ppc4xx.h" diff --git a/hw/ppc/prep_systemio.c b/hw/ppc/prep_systemio.c index b1f2e130f0..08f29e72e4 100644 --- a/hw/ppc/prep_systemio.c +++ b/hw/ppc/prep_systemio.c @@ -28,7 +28,7 @@ #include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/object.h" #include "qemu/error-report.h" /* for error_report() */ #include "qemu/module.h" diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c index 0e5d53b8b6..27f1c90f06 100644 --- a/hw/ppc/rs6000_mc.c +++ b/hw/ppc/rs6000_mc.c @@ -24,7 +24,7 @@ #include "hw/isa/isa.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qapi/error.h" #include "trace.h" #include "qom/object.h" diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index 88e29536aa..6d6eaf67cb 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -15,7 +15,7 @@ #include "hw/ppc/spapr_ovec.h" #include "migration/vmstate.h" #include "qemu/bitmap.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qemu/error-report.h" #include "trace.h" #include diff --git a/hw/ppc/vof.c b/hw/ppc/vof.c index 09cb77de93..f14efa3a7c 100644 --- a/hw/ppc/vof.c +++ b/hw/ppc/vof.c @@ -15,7 +15,7 @@ #include "qemu/units.h" #include "qemu/log.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/ppc/vof.h" #include "hw/ppc/fdt.h" #include "system/runstate.h" diff --git a/hw/remote/iommu.c b/hw/remote/iommu.c index ec845d1f58..3e0758a21e 100644 --- a/hw/remote/iommu.c +++ b/hw/remote/iommu.c @@ -14,7 +14,7 @@ #include "hw/pci/pci_bus.h" #include "hw/pci/pci.h" #include "system/memory.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "trace.h" /** diff --git a/hw/riscv/microblaze-v-generic.c b/hw/riscv/microblaze-v-generic.c index d8e67906d2..e863c50cbc 100644 --- a/hw/riscv/microblaze-v-generic.c +++ b/hw/riscv/microblaze-v-generic.c @@ -22,7 +22,7 @@ #include "net/net.h" #include "hw/boards.h" #include "hw/char/serial-mm.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/char/xilinx_uartlite.h" #include "hw/misc/unimp.h" diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 98a67fe52a..019d6b3986 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -28,7 +28,7 @@ #include "hw/riscv/boot.h" #include "qemu/units.h" #include "system/system.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" /* * This version of the OpenTitan machine currently supports diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index e2242b97d0..17c5c72102 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -25,7 +25,7 @@ #include "hw/intc/riscv_aclint.h" #include "system/system.h" #include "hw/qdev-properties.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/riscv/boot.h" static const struct MemmapEntry { diff --git a/hw/s390x/css.c b/hw/s390x/css.c index 17afcbb2cd..53444f6828 100644 --- a/hw/s390x/css.c +++ b/hw/s390x/css.c @@ -14,7 +14,7 @@ #include "qapi/visitor.h" #include "qemu/bitops.h" #include "qemu/error-report.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/s390x/ioinst.h" #include "hw/qdev-properties.h" #include "hw/s390x/css.h" diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 8e3882d506..c6ecb3433c 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -14,7 +14,7 @@ #define HW_S390_IPL_H #include "cpu.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/qdev-core.h" #include "hw/s390x/ipl/qipl.h" #include "qom/object.h" diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 067ea03726..0bb90fed04 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -20,7 +20,7 @@ #include "qobject/qdict.h" #include "qemu/error-report.h" #include "system/memory_mapping.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/kvm.h" #include "migration/qemu-file-types.h" #include "migration/register.h" diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index 43f3b162c8..e8ecb90826 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/kvm.h" #include "net/net.h" #include "hw/virtio/virtio.h" diff --git a/hw/sparc/sun4m_iommu.c b/hw/sparc/sun4m_iommu.c index 5a4c1f5e3b..4a542b18d2 100644 --- a/hw/sparc/sun4m_iommu.c +++ b/hw/sparc/sun4m_iommu.c @@ -29,7 +29,7 @@ #include "hw/sysbus.h" #include "migration/vmstate.h" #include "qemu/module.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "trace.h" /* diff --git a/hw/sparc64/sun4u_iommu.c b/hw/sparc64/sun4u_iommu.c index eba811af0c..533fcae1fb 100644 --- a/hw/sparc64/sun4u_iommu.c +++ b/hw/sparc64/sun4u_iommu.c @@ -27,7 +27,7 @@ #include "qemu/osdep.h" #include "hw/sysbus.h" #include "hw/sparc/sun4u_iommu.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qemu/log.h" #include "qemu/module.h" #include "trace.h" diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index ccb97b6806..ea82472105 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -36,7 +36,7 @@ #include "hw/rtc/mc146818rtc_regs.h" #include "migration/vmstate.h" #include "hw/timer/i8254.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/object.h" #include "trace.h" diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index 6cdeb72df0..b668aee97a 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -18,7 +18,7 @@ #include "qemu/module.h" #include "qapi/error.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/qdev-properties.h" #include "hw/pci/pci_ids.h" #include "hw/acpi/tpm.h" diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index c7ab4ff57a..d6575d7c44 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -28,7 +28,7 @@ #include "migration/vmstate.h" #include "hw/qdev-properties.h" #include "hw/s390x/ap-bridge.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/object.h" #define TYPE_VFIO_AP_DEVICE "vfio-ap" diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index e5e0d9e3e7..29e804e122 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -27,7 +27,7 @@ #include "hw/s390x/vfio-ccw.h" #include "hw/qdev-properties.h" #include "hw/s390x/ccw-device.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/module.h" diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 989c6ee83d..98832af88d 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -27,7 +27,7 @@ #include "hw/vfio/vfio-common.h" #include "hw/vfio/pci.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/memory.h" #include "exec/ram_addr.h" #include "exec/target_page.h" diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 1d1c5f9a77..2e993c7e73 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -23,7 +23,7 @@ #include #include "hw/vfio/vfio-common.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/memory.h" #include "exec/ram_addr.h" #include "qemu/error-report.h" diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 96c6bf5654..c6edbdd4ae 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -29,7 +29,7 @@ #include "qemu/module.h" #include "qemu/range.h" #include "system/memory.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qemu/queue.h" #include "hw/sysbus.h" #include "trace.h" diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 1a5d1611f2..c9a7dd8d68 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -13,7 +13,7 @@ #include #include "system/kvm.h" #include "system/hostmem.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/vfio/vfio-common.h" #include "hw/hw.h" diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 7efbde3d4c..1e0336df1d 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -20,7 +20,7 @@ #include "hw/virtio/virtio-net.h" #include "hw/virtio/vhost-shadow-virtqueue.h" #include "hw/virtio/vhost-vdpa.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "migration/blocker.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 2eb5a14fa2..0d0603c674 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -24,7 +24,7 @@ #include "hw/boards.h" #include "system/balloon.h" #include "hw/virtio/virtio-balloon.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qapi/error.h" #include "qapi/qapi-events-machine.h" #include "qapi/visitor.h" diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index 896feb37a1..d1c79c567b 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -28,7 +28,7 @@ #include "qapi/error.h" #include "hw/virtio/virtio-bus.h" #include "hw/virtio/virtio.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" /* #define DEBUG_VIRTIO_BUS */ diff --git a/include/hw/misc/lasi.h b/include/hw/misc/lasi.h index f01c0f680a..0bdfb11b50 100644 --- a/include/hw/misc/lasi.h +++ b/include/hw/misc/lasi.h @@ -12,7 +12,7 @@ #ifndef LASI_H #define LASI_H -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/pci/pci_host.h" #include "hw/boards.h" diff --git a/include/hw/nubus/nubus.h b/include/hw/nubus/nubus.h index fee79b71d1..7825840dca 100644 --- a/include/hw/nubus/nubus.h +++ b/include/hw/nubus/nubus.h @@ -11,7 +11,7 @@ #include "hw/qdev-properties.h" #include "hw/sysbus.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/object.h" #include "qemu/units.h" diff --git a/include/hw/ppc/vof.h b/include/hw/ppc/vof.h index 2918aaab12..3a0fbffe54 100644 --- a/include/hw/ppc/vof.h +++ b/include/hw/ppc/vof.h @@ -7,7 +7,7 @@ #define HW_VOF_H #include "qom/object.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/memory.h" #include "exec/cpu-defs.h" diff --git a/include/hw/tricore/triboard.h b/include/hw/tricore/triboard.h index 8250470643..ca49a0c752 100644 --- a/include/hw/tricore/triboard.h +++ b/include/hw/tricore/triboard.h @@ -21,7 +21,7 @@ #include "qapi/error.h" #include "hw/boards.h" #include "system/system.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qom/object.h" #include "hw/tricore/tc27x_soc.h" diff --git a/include/exec/address-spaces.h b/include/system/address-spaces.h similarity index 89% rename from include/exec/address-spaces.h rename to include/system/address-spaces.h index 0d0aa61d68..72d17afb0f 100644 --- a/include/exec/address-spaces.h +++ b/include/system/address-spaces.h @@ -11,16 +11,14 @@ * */ -#ifndef EXEC_ADDRESS_SPACES_H -#define EXEC_ADDRESS_SPACES_H +#ifndef SYSTEM_ADDRESS_SPACES_H +#define SYSTEM_ADDRESS_SPACES_H /* * Internal interfaces between memory.c/exec.c/vl.c. Do not #include unless * you're one of them. */ -#ifndef CONFIG_USER_ONLY - /* Get the root memory region. This interface should only be used temporarily * until a proper bus interface is available. */ @@ -35,5 +33,3 @@ extern AddressSpace address_space_memory; extern AddressSpace address_space_io; #endif - -#endif diff --git a/include/system/dma.h b/include/system/dma.h index aaa03b9711..82e7ad5437 100644 --- a/include/system/dma.h +++ b/include/system/dma.h @@ -11,7 +11,7 @@ #define DMA_H #include "system/memory.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "block/block.h" #include "block/accounting.h" diff --git a/monitor/hmp-cmds-target.c b/monitor/hmp-cmds-target.c index 6654d31406..011a367357 100644 --- a/monitor/hmp-cmds-target.c +++ b/monitor/hmp-cmds-target.c @@ -24,7 +24,7 @@ #include "qemu/osdep.h" #include "disas/disas.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "system/memory.h" #include "monitor/hmp-target.h" #include "monitor/monitor-internal.h" diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 7ded3378cf..8ddcdd76c1 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -14,7 +14,7 @@ */ #include "qemu/osdep.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "exec/ioport.h" #include "exec/gdbstub.h" #include "gdbstub/enums.h" diff --git a/rust/wrapper.h b/rust/wrapper.h index 94866b7e32..beddd9aab2 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -64,5 +64,5 @@ typedef enum memory_order { #include "chardev/char-serial.h" #include "exec/memattrs.h" #include "qemu/timer.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/char/pl011.h" diff --git a/system/ioport.c b/system/ioport.c index 2291739039..5300716464 100644 --- a/system/ioport.c +++ b/system/ioport.c @@ -28,7 +28,7 @@ #include "qemu/osdep.h" #include "exec/ioport.h" #include "system/memory.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "trace.h" struct MemoryRegionPortioList { diff --git a/system/memory.c b/system/memory.c index 2865d0deb1..a4185ea353 100644 --- a/system/memory.c +++ b/system/memory.c @@ -33,7 +33,7 @@ #include "qemu/accel.h" #include "hw/boards.h" #include "migration/vmstate.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" //#define DEBUG_UNASSIGNED diff --git a/system/memory_mapping.c b/system/memory_mapping.c index 8538a8241e..da708a08ab 100644 --- a/system/memory_mapping.c +++ b/system/memory_mapping.c @@ -17,7 +17,7 @@ #include "system/memory_mapping.h" #include "system/memory.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/core/cpu.h" //#define DEBUG_GUEST_PHYS_REGION_ADD diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 2439af63a0..93a3f9b53d 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -22,7 +22,7 @@ #include -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/boards.h" #include "hw/irq.h" #include "qemu/main-loop.h" diff --git a/target/arm/kvm.c b/target/arm/kvm.c index da30bdbb23..97de8c7e93 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -30,7 +30,7 @@ #include "internals.h" #include "hw/pci/pci.h" #include "exec/memattrs.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "gdbstub/enums.h" #include "hw/boards.h" #include "hw/irq.h" diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 0b14b36c17..1121822470 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -23,7 +23,7 @@ #include "qemu/qemu-print.h" #include "exec/exec-all.h" #include "exec/translation-block.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "cpu.h" #include "disas/dis-asm.h" #include "tcg/debug-assert.h" diff --git a/target/i386/cpu-apic.c b/target/i386/cpu-apic.c index c1708b04bb..242a05fdbe 100644 --- a/target/i386/cpu-apic.c +++ b/target/i386/cpu-apic.c @@ -14,7 +14,7 @@ #include "system/hw_accel.h" #include "system/kvm.h" #include "system/xen.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/qdev-properties.h" #include "hw/i386/apic_internal.h" #include "cpu-internal.h" diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 3fb1ec62da..5c9f80b4cc 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -38,7 +38,7 @@ #ifndef CONFIG_USER_ONLY #include "system/reset.h" #include "qapi/qapi-commands-machine-target.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/boards.h" #include "hw/i386/sgx-epc.h" #endif diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h index 3c56afc9d3..029516e5c0 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -33,7 +33,7 @@ #include "system/hvf.h" #include "system/hvf_int.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" static inline uint64_t rreg(hv_vcpuid_t vcpu, hv_x86_reg_t reg) { diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index e81a245881..b23010374f 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -17,7 +17,7 @@ #include "system/kvm_int.h" #include "system/kvm_xen.h" #include "kvm/kvm_i386.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "xen-emu.h" #include "trace.h" #include "system/runstate.h" diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 04e5f7e637..91f0e32366 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -9,7 +9,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "exec/ioport.h" #include "qemu/accel.h" #include "system/nvmm.h" diff --git a/target/i386/sev.c b/target/i386/sev.c index 0e1dbb6959..ba88976e9f 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -39,7 +39,7 @@ #include "qapi/qapi-commands-misc-target.h" #include "confidential-guest.h" #include "hw/i386/pc.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "qemu/queue.h" OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON) diff --git a/target/i386/tcg/system/misc_helper.c b/target/i386/tcg/system/misc_helper.c index ce18c75b9f..0555cf2604 100644 --- a/target/i386/tcg/system/misc_helper.c +++ b/target/i386/tcg/system/misc_helper.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "exec/cputlb.h" #include "tcg/helper-tcg.h" #include "hw/i386/apic.h" diff --git a/target/i386/tcg/system/tcg-cpu.c b/target/i386/tcg/system/tcg-cpu.c index 13a3507863..ab1f3c7c59 100644 --- a/target/i386/tcg/system/tcg-cpu.c +++ b/target/i386/tcg/system/tcg-cpu.c @@ -23,7 +23,7 @@ #include "system/system.h" #include "qemu/units.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "tcg/tcg-cpu.h" diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 41fb8c5a4e..d58cb11cee 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "exec/ioport.h" #include "gdbstub/helpers.h" #include "qemu/accel.h" diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index f0e3cfef03..1bda570482 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -18,7 +18,7 @@ #include "system/kvm_int.h" #include "hw/pci/pci.h" #include "exec/memattrs.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/boards.h" #include "hw/irq.h" #include "hw/loongarch/virt.h" diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 0f4997a918..5315134e08 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -35,7 +35,7 @@ #include "accel/accel-cpu-target.h" #include "hw/pci/pci.h" #include "exec/memattrs.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/boards.h" #include "hw/irq.h" #include "hw/intc/riscv_imsic.h" diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index d8f483898d..b079d120db 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -17,7 +17,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "cpu.h" #include "s390x-internal.h" #include "kvm/kvm_s390x.h" diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index 6a4d9c5081..a3347f1236 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -14,7 +14,7 @@ #include "hw/boards.h" #include "system/hw_accel.h" #include "system/runstate.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "exec/cputlb.h" #include "exec/exec-all.h" #include "system/tcg.h" diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index f969850f87..ac733f407f 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -28,7 +28,7 @@ #include "tcg_s390x.h" #ifndef CONFIG_USER_ONLY #include "qemu/timer.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" #include "hw/s390x/ioinst.h" #include "hw/s390x/s390_flic.h" #include "hw/boards.h" diff --git a/target/xtensa/dbg_helper.c b/target/xtensa/dbg_helper.c index 5546c82ecd..163a1ffc7b 100644 --- a/target/xtensa/dbg_helper.c +++ b/target/xtensa/dbg_helper.c @@ -31,7 +31,7 @@ #include "exec/helper-proto.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" -#include "exec/address-spaces.h" +#include "system/address-spaces.h" void HELPER(wsr_ibreakenable)(CPUXtensaState *env, uint32_t v) { From 91a853837da22e35d140471058e5d73dbfa87707 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 12:49:38 -0700 Subject: [PATCH 0115/2760] include/system: Move exec/ioport.h to system/ioport.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the existing includes with sed. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- MAINTAINERS | 2 +- hw/block/fdc-isa.c | 2 +- hw/display/vga_int.h | 2 +- include/hw/char/parallel-isa.h | 2 +- include/hw/dma/i8257.h | 2 +- include/hw/ide/ide-bus.h | 2 +- include/hw/isa/isa.h | 2 +- include/{exec => system}/ioport.h | 6 ++---- monitor/hmp-cmds.c | 2 +- system/ioport.c | 2 +- system/physmem.c | 2 +- system/qtest.c | 2 +- target/i386/nvmm/nvmm-all.c | 2 +- target/i386/whpx/whpx-all.c | 2 +- tests/qtest/fuzz/qtest_wrappers.c | 2 +- 15 files changed, 16 insertions(+), 18 deletions(-) rename include/{exec => system}/ioport.h (97%) diff --git a/MAINTAINERS b/MAINTAINERS index 5d391cfaa7..9a96cc1b5e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3118,7 +3118,7 @@ M: Peter Xu M: David Hildenbrand R: Philippe Mathieu-Daudé S: Supported -F: include/exec/ioport.h +F: include/system/ioport.h F: include/exec/memop.h F: include/system/memory.h F: include/exec/ram_addr.h diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c index a10c24aab1..561cfa47c1 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -42,7 +42,7 @@ #include "system/block-backend.h" #include "system/blockdev.h" #include "system/system.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" diff --git a/hw/display/vga_int.h b/hw/display/vga_int.h index 60ad26e03e..747b5cc6cf 100644 --- a/hw/display/vga_int.h +++ b/hw/display/vga_int.h @@ -26,7 +26,7 @@ #define HW_VGA_INT_H #include "ui/console.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "system/memory.h" #include "hw/display/bochs-vbe.h" diff --git a/include/hw/char/parallel-isa.h b/include/hw/char/parallel-isa.h index 5284b2ffec..3edaf9dbe4 100644 --- a/include/hw/char/parallel-isa.h +++ b/include/hw/char/parallel-isa.h @@ -12,7 +12,7 @@ #include "parallel.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "hw/isa/isa.h" #include "qom/object.h" diff --git a/include/hw/dma/i8257.h b/include/hw/dma/i8257.h index 4342e4a91e..33b6286d5a 100644 --- a/include/hw/dma/i8257.h +++ b/include/hw/dma/i8257.h @@ -2,7 +2,7 @@ #define HW_I8257_H #include "hw/isa/isa.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "qom/object.h" #define TYPE_I8257 "i8257" diff --git a/include/hw/ide/ide-bus.h b/include/hw/ide/ide-bus.h index 4841a7dcd6..121b455fcd 100644 --- a/include/hw/ide/ide-bus.h +++ b/include/hw/ide/ide-bus.h @@ -1,7 +1,7 @@ #ifndef HW_IDE_BUS_H #define HW_IDE_BUS_H -#include "exec/ioport.h" +#include "system/ioport.h" #include "hw/ide/ide-dev.h" #include "hw/ide/ide-dma.h" diff --git a/include/hw/isa/isa.h b/include/hw/isa/isa.h index 1d852011b3..a82c5f1004 100644 --- a/include/hw/isa/isa.h +++ b/include/hw/isa/isa.h @@ -4,7 +4,7 @@ /* ISA bus */ #include "system/memory.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "hw/qdev-core.h" #include "qom/object.h" diff --git a/include/exec/ioport.h b/include/system/ioport.h similarity index 97% rename from include/exec/ioport.h rename to include/system/ioport.h index ecea3575bc..780ea5a676 100644 --- a/include/exec/ioport.h +++ b/include/system/ioport.h @@ -21,8 +21,8 @@ * IO ports API */ -#ifndef IOPORT_H -#define IOPORT_H +#ifndef SYSTEM_IOPORT_H +#define SYSTEM_IOPORT_H #include "system/memory.h" @@ -39,9 +39,7 @@ typedef struct MemoryRegionPortio { #define PORTIO_END_OF_LIST() { } -#ifndef CONFIG_USER_ONLY extern const MemoryRegionOps unassigned_io_ops; -#endif void cpu_outb(uint32_t addr, uint8_t val); void cpu_outw(uint32_t addr, uint16_t val); diff --git a/monitor/hmp-cmds.c b/monitor/hmp-cmds.c index 8ddcdd76c1..74a0f56566 100644 --- a/monitor/hmp-cmds.c +++ b/monitor/hmp-cmds.c @@ -15,7 +15,7 @@ #include "qemu/osdep.h" #include "system/address-spaces.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "exec/gdbstub.h" #include "gdbstub/enums.h" #include "monitor/hmp.h" diff --git a/system/ioport.c b/system/ioport.c index 5300716464..4f96e9119f 100644 --- a/system/ioport.c +++ b/system/ioport.c @@ -26,7 +26,7 @@ */ #include "qemu/osdep.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "system/memory.h" #include "system/address-spaces.h" #include "trace.h" diff --git a/system/physmem.c b/system/physmem.c index e61fea41b5..234e489199 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -51,7 +51,7 @@ #include "qemu/memalign.h" #include "qemu/memfd.h" #include "system/memory.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "system/dma.h" #include "system/hostmem.h" #include "system/hw_accel.h" diff --git a/system/qtest.c b/system/qtest.c index 5407289154..523a047995 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -16,7 +16,7 @@ #include "system/qtest.h" #include "system/runstate.h" #include "chardev/char-fe.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "system/memory.h" #include "exec/tswap.h" #include "hw/qdev-core.h" diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 91f0e32366..17394d073d 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "system/address-spaces.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "qemu/accel.h" #include "system/nvmm.h" #include "system/cpus.h" diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index d58cb11cee..b64852e13e 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "system/address-spaces.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "gdbstub/helpers.h" #include "qemu/accel.h" #include "system/whpx.h" diff --git a/tests/qtest/fuzz/qtest_wrappers.c b/tests/qtest/fuzz/qtest_wrappers.c index 0580f8df86..d7adcbe3fd 100644 --- a/tests/qtest/fuzz/qtest_wrappers.c +++ b/tests/qtest/fuzz/qtest_wrappers.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "hw/core/cpu.h" -#include "exec/ioport.h" +#include "system/ioport.h" #include "fuzz.h" From 4705a71db5909ac5586e87397b2dece132b9e330 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 17:42:03 -0700 Subject: [PATCH 0116/2760] include/system: Move exec/ram_addr.h to system/ram_addr.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the existing includes with sed. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- MAINTAINERS | 2 +- accel/kvm/kvm-all.c | 2 +- accel/tcg/cputlb.c | 2 +- accel/tcg/translate-all.c | 2 +- hw/ppc/spapr.c | 2 +- hw/ppc/spapr_caps.c | 2 +- hw/ppc/spapr_pci.c | 2 +- hw/remote/memory.c | 2 +- hw/remote/proxy-memory-listener.c | 2 +- hw/s390x/s390-stattrib-kvm.c | 2 +- hw/s390x/s390-stattrib.c | 2 +- hw/s390x/s390-virtio-ccw.c | 2 +- hw/vfio/common.c | 3 +-- hw/vfio/container.c | 2 +- hw/vfio/spapr.c | 2 +- hw/virtio/virtio-mem.c | 2 +- include/{exec => system}/ram_addr.h | 7 +++---- migration/ram.c | 2 +- system/memory.c | 2 +- system/physmem.c | 2 +- target/arm/tcg/mte_helper.c | 2 +- target/ppc/kvm.c | 2 +- target/s390x/kvm/kvm.c | 2 +- 23 files changed, 25 insertions(+), 27 deletions(-) rename include/{exec => system}/ram_addr.h (99%) diff --git a/MAINTAINERS b/MAINTAINERS index 9a96cc1b5e..b1ac3ff7d2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3121,7 +3121,7 @@ S: Supported F: include/system/ioport.h F: include/exec/memop.h F: include/system/memory.h -F: include/exec/ram_addr.h +F: include/system/ram_addr.h F: include/exec/ramblock.h F: include/system/memory_mapping.h F: system/dma-helpers.c diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 9e06a95f75..a30b19f455 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -34,7 +34,7 @@ #include "system/accel-blocker.h" #include "qemu/bswap.h" #include "system/memory.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "qemu/event_notifier.h" #include "qemu/main-loop.h" #include "trace.h" diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 6f0ea9067b..134e523cab 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -27,7 +27,7 @@ #include "exec/cputlb.h" #include "exec/tb-flush.h" #include "exec/memory-internal.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "exec/mmu-access-type.h" #include "exec/tlb-common.h" #include "exec/vaddr.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 16e5043597..167535bcb1 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -40,7 +40,7 @@ #endif #endif #else -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #endif #include "exec/cputlb.h" diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 6fef1d167a..c1a7ac3536 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -77,7 +77,7 @@ #include "hw/virtio/virtio-scsi.h" #include "hw/virtio/vhost-scsi-common.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "system/confidential-guest-support.h" #include "hw/usb.h" #include "qemu/config-file.h" diff --git a/hw/ppc/spapr_caps.c b/hw/ppc/spapr_caps.c index 815c94ed2f..f2f5722d8a 100644 --- a/hw/ppc/spapr_caps.c +++ b/hw/ppc/spapr_caps.c @@ -27,7 +27,7 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "system/hw_accel.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "target/ppc/cpu.h" #include "target/ppc/mmu-hash64.h" #include "cpu-models.h" diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index e0a9d50edc..384269b831 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -34,7 +34,7 @@ #include "hw/pci/pci_host.h" #include "hw/ppc/spapr.h" #include "hw/pci-host/spapr.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include #include "trace.h" #include "qemu/error-report.h" diff --git a/hw/remote/memory.c b/hw/remote/memory.c index 6d60da91e0..00193a552f 100644 --- a/hw/remote/memory.c +++ b/hw/remote/memory.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "hw/remote/memory.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "qapi/error.h" static void remote_sysmem_reset(void) diff --git a/hw/remote/proxy-memory-listener.c b/hw/remote/proxy-memory-listener.c index ce7f5b9bfb..30ac74961d 100644 --- a/hw/remote/proxy-memory-listener.c +++ b/hw/remote/proxy-memory-listener.c @@ -12,7 +12,7 @@ #include "qemu/range.h" #include "system/memory.h" #include "exec/cpu-common.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/remote/mpqemu-link.h" diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c index d0ff04364d..ebcd56368f 100644 --- a/hw/s390x/s390-stattrib-kvm.c +++ b/hw/s390x/s390-stattrib-kvm.c @@ -16,7 +16,7 @@ #include "qemu/error-report.h" #include "system/kvm.h" #include "system/memory_mapping.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "kvm/kvm_s390x.h" #include "qapi/error.h" diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index 35bf697ef0..a86002be6a 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -16,7 +16,7 @@ #include "hw/qdev-properties.h" #include "hw/s390x/storage-attributes.h" #include "qemu/error-report.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "qapi/error.h" #include "qobject/qdict.h" #include "cpu.h" diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 5af3c4f547..52c273b3de 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "system/confidential-guest-support.h" #include "hw/boards.h" #include "hw/s390x/sclp.h" diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 98832af88d..bae0633c3d 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -29,8 +29,7 @@ #include "hw/vfio/pci.h" #include "system/address-spaces.h" #include "system/memory.h" -#include "exec/ram_addr.h" -#include "exec/target_page.h" +#include "system/ram_addr.h" #include "hw/hw.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 2e993c7e73..812d5edbcf 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -25,7 +25,7 @@ #include "hw/vfio/vfio-common.h" #include "system/address-spaces.h" #include "system/memory.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "qemu/error-report.h" #include "qemu/range.h" #include "system/reset.h" diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index c9a7dd8d68..66a2d2bb0d 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -17,7 +17,7 @@ #include "hw/vfio/vfio-common.h" #include "hw/hw.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "trace.h" diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 5f57eccbb6..c7968ee0c6 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -24,7 +24,7 @@ #include "hw/virtio/virtio-mem.h" #include "qapi/error.h" #include "qapi/visitor.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "migration/misc.h" #include "hw/boards.h" #include "hw/qdev-properties.h" diff --git a/include/exec/ram_addr.h b/include/system/ram_addr.h similarity index 99% rename from include/exec/ram_addr.h rename to include/system/ram_addr.h index 8677761af5..3b81c3091f 100644 --- a/include/exec/ram_addr.h +++ b/include/system/ram_addr.h @@ -16,10 +16,9 @@ * The functions declared here will be removed soon. */ -#ifndef RAM_ADDR_H -#define RAM_ADDR_H +#ifndef SYSTEM_RAM_ADDR_H +#define SYSTEM_RAM_ADDR_H -#ifndef CONFIG_USER_ONLY #include "system/xen.h" #include "system/tcg.h" #include "exec/cputlb.h" @@ -559,5 +558,5 @@ uint64_t cpu_physical_memory_sync_dirty_bitmap(RAMBlock *rb, return num_dirty; } -#endif + #endif diff --git a/migration/ram.c b/migration/ram.c index 424df6d9f1..6295f675df 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -48,7 +48,7 @@ #include "qapi/qapi-commands-migration.h" #include "qapi/qmp/qerror.h" #include "trace.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "exec/target_page.h" #include "qemu/rcu_queue.h" #include "migration/colo.h" diff --git a/system/memory.c b/system/memory.c index a4185ea353..6a5d853071 100644 --- a/system/memory.c +++ b/system/memory.c @@ -26,7 +26,7 @@ #include "trace.h" #include "exec/memory-internal.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "system/kvm.h" #include "system/runstate.h" #include "system/tcg.h" diff --git a/system/physmem.c b/system/physmem.c index 234e489199..307d0764b6 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -67,7 +67,7 @@ #include "system/replay.h" #include "exec/memory-internal.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "qemu/pmem.h" diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 5d6d8a17ae..80164a8050 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -27,7 +27,7 @@ #include "user/cpu_loop.h" #include "user/page-protection.h" #else -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #endif #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 992356cb75..8b12b8e7d2 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -41,7 +41,7 @@ #include "trace.h" #include "gdbstub/enums.h" #include "exec/memattrs.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "system/hostmem.h" #include "qemu/cutils.h" #include "qemu/main-loop.h" diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 4d56e653dd..b9f1422197 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -41,7 +41,7 @@ #include "system/runstate.h" #include "system/device_tree.h" #include "gdbstub/enums.h" -#include "exec/ram_addr.h" +#include "system/ram_addr.h" #include "trace.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-bus.h" From 548a01650c9be153b352406cd4afb86cb350788e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 19:03:02 -0700 Subject: [PATCH 0117/2760] include/system: Move exec/ramblock.h to system/ramblock.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert the existing includes with sed. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- MAINTAINERS | 2 +- hw/display/virtio-gpu-udmabuf.c | 2 +- hw/hyperv/hv-balloon.c | 2 +- hw/virtio/vhost-user.c | 2 +- include/system/ram_addr.h | 2 +- include/{exec => system}/ramblock.h | 9 ++++----- migration/dirtyrate.c | 2 +- migration/file.c | 2 +- migration/multifd-nocomp.c | 2 +- migration/multifd-qatzip.c | 2 +- migration/multifd-qpl.c | 2 +- migration/multifd-uadk.c | 2 +- migration/multifd-zero-page.c | 2 +- migration/multifd-zlib.c | 2 +- migration/multifd-zstd.c | 2 +- migration/multifd.c | 2 +- migration/postcopy-ram.c | 2 +- tests/qtest/fuzz/generic_fuzz.c | 2 +- 18 files changed, 21 insertions(+), 22 deletions(-) rename include/{exec => system}/ramblock.h (96%) diff --git a/MAINTAINERS b/MAINTAINERS index b1ac3ff7d2..ba885010c5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3122,7 +3122,7 @@ F: include/system/ioport.h F: include/exec/memop.h F: include/system/memory.h F: include/system/ram_addr.h -F: include/exec/ramblock.h +F: include/system/ramblock.h F: include/system/memory_mapping.h F: system/dma-helpers.c F: system/ioport.c diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index 85ca23cb32..0510577475 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -19,7 +19,7 @@ #include "hw/virtio/virtio-gpu.h" #include "hw/virtio/virtio-gpu-pixman.h" #include "trace.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "system/hostmem.h" #include #include diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index 0b1da723c8..acabff2c4a 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -12,7 +12,7 @@ #include "system/address-spaces.h" #include "exec/cpu-common.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "hw/boards.h" #include "hw/hyperv/dynmem-proto.h" #include "hw/hyperv/hv-balloon.h" diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 267b612587..48561d3c74 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -28,7 +28,7 @@ #include "system/cryptodev.h" #include "migration/postcopy-ram.h" #include "trace.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include #include diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index 3b81c3091f..b4e4425acb 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -23,7 +23,7 @@ #include "system/tcg.h" #include "exec/cputlb.h" #include "exec/ramlist.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "exec/exec-all.h" #include "system/memory.h" #include "exec/target_page.h" diff --git a/include/exec/ramblock.h b/include/system/ramblock.h similarity index 96% rename from include/exec/ramblock.h rename to include/system/ramblock.h index 64484cd821..d8a116ba99 100644 --- a/include/exec/ramblock.h +++ b/include/system/ramblock.h @@ -16,11 +16,10 @@ * The functions declared here will be removed soon. */ -#ifndef QEMU_EXEC_RAMBLOCK_H -#define QEMU_EXEC_RAMBLOCK_H +#ifndef SYSTEM_RAMBLOCK_H +#define SYSTEM_RAMBLOCK_H -#ifndef CONFIG_USER_ONLY -#include "cpu-common.h" +#include "exec/cpu-common.h" #include "qemu/rcu.h" #include "exec/ramlist.h" @@ -91,5 +90,5 @@ struct RAMBlock { */ ram_addr_t postcopy_length; }; -#endif + #endif diff --git a/migration/dirtyrate.c b/migration/dirtyrate.c index 09caf92f87..986624c79a 100644 --- a/migration/dirtyrate.c +++ b/migration/dirtyrate.c @@ -14,7 +14,7 @@ #include "qemu/error-report.h" #include "hw/core/cpu.h" #include "qapi/error.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "exec/target_page.h" #include "qemu/rcu_queue.h" #include "qemu/main-loop.h" diff --git a/migration/file.c b/migration/file.c index 7f11e26f5c..bb8031e3c7 100644 --- a/migration/file.c +++ b/migration/file.c @@ -6,7 +6,7 @@ */ #include "qemu/osdep.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "qemu/cutils.h" #include "qemu/error-report.h" #include "qapi/error.h" diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c index ffe75256c9..94f248e8a2 100644 --- a/migration/multifd-nocomp.c +++ b/migration/multifd-nocomp.c @@ -11,7 +11,7 @@ */ #include "qemu/osdep.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "exec/target_page.h" #include "file.h" #include "migration-stats.h" diff --git a/migration/multifd-qatzip.c b/migration/multifd-qatzip.c index 6a0e989fae..7419e5dc0d 100644 --- a/migration/multifd-qatzip.c +++ b/migration/multifd-qatzip.c @@ -13,7 +13,7 @@ */ #include "qemu/osdep.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qapi/qapi-types-migration.h" diff --git a/migration/multifd-qpl.c b/migration/multifd-qpl.c index 88e2344af2..52902eb00c 100644 --- a/migration/multifd-qpl.c +++ b/migration/multifd-qpl.c @@ -14,7 +14,7 @@ #include "qemu/module.h" #include "qapi/error.h" #include "qapi/qapi-types-migration.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "multifd.h" #include "qpl/qpl.h" diff --git a/migration/multifd-uadk.c b/migration/multifd-uadk.c index 6895c1f65a..fd7cd9b5e8 100644 --- a/migration/multifd-uadk.c +++ b/migration/multifd-uadk.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qapi/error.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "migration.h" #include "multifd.h" #include "options.h" diff --git a/migration/multifd-zero-page.c b/migration/multifd-zero-page.c index f1e988a959..dbc1184921 100644 --- a/migration/multifd-zero-page.c +++ b/migration/multifd-zero-page.c @@ -12,7 +12,7 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "migration.h" #include "migration-stats.h" #include "multifd.h" diff --git a/migration/multifd-zlib.c b/migration/multifd-zlib.c index 8cf8a26bb4..8820b2a787 100644 --- a/migration/multifd-zlib.c +++ b/migration/multifd-zlib.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include #include "qemu/rcu.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "exec/target_page.h" #include "qapi/error.h" #include "migration.h" diff --git a/migration/multifd-zstd.c b/migration/multifd-zstd.c index abed140855..3c2dcf76b0 100644 --- a/migration/multifd-zstd.c +++ b/migration/multifd-zstd.c @@ -13,7 +13,7 @@ #include "qemu/osdep.h" #include #include "qemu/rcu.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "exec/target_page.h" #include "qapi/error.h" #include "migration.h" diff --git a/migration/multifd.c b/migration/multifd.c index dfb5189f0e..86c83e43c0 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -16,7 +16,7 @@ #include "qemu/rcu.h" #include "exec/target_page.h" #include "system/system.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "file.h" diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 5d3edfcfec..995614b38c 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -31,7 +31,7 @@ #include "qemu/error-report.h" #include "trace.h" #include "hw/boards.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "socket.h" #include "yank_functions.h" #include "tls.h" diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c index 239be9372d..507de74806 100644 --- a/tests/qtest/fuzz/generic_fuzz.c +++ b/tests/qtest/fuzz/generic_fuzz.c @@ -21,7 +21,7 @@ #include "fuzz.h" #include "string.h" #include "system/memory.h" -#include "exec/ramblock.h" +#include "system/ramblock.h" #include "hw/qdev-core.h" #include "hw/pci/pci.h" #include "hw/pci/pci_device.h" From 261a0303ae4770cadaf02ec21da9b5e60076cd43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Mar 2025 17:13:28 +0100 Subject: [PATCH 0118/2760] accel/tcg: Remove unnecesary inclusion of memory-internal.h in cputlb.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At some point cputlb.c stopped depending on the "exec/memory-internal.h" header. Clean that now. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson Message-ID: <20250317161329.40300-2-philmd@linaro.org> --- accel/tcg/cputlb.c | 1 - 1 file changed, 1 deletion(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 134e523cab..613f919fff 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -26,7 +26,6 @@ #include "exec/cpu_ldst.h" #include "exec/cputlb.h" #include "exec/tb-flush.h" -#include "exec/memory-internal.h" #include "system/ram_addr.h" #include "exec/mmu-access-type.h" #include "exec/tlb-common.h" From d4c9cab37fa440807a4f2bf87efd0511d5694b2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 17 Mar 2025 17:13:29 +0100 Subject: [PATCH 0119/2760] exec: Restrict memory-internal.h to system/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only file units within the system/ directory need access to "memory-internal.h". Restrict its scope by moving it there. The comment from commit 9d70618c684 ("memory-internal.h: Remove obsolete claim that header is obsolete") is now obsolete, remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Signed-off-by: Richard Henderson Message-ID: <20250317161329.40300-3-philmd@linaro.org> --- MAINTAINERS | 2 +- {include/exec => system}/memory-internal.h | 6 ------ system/memory.c | 4 ++-- system/physmem.c | 3 ++- 4 files changed, 5 insertions(+), 10 deletions(-) rename {include/exec => system}/memory-internal.h (88%) diff --git a/MAINTAINERS b/MAINTAINERS index ba885010c5..661a47db5a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3129,7 +3129,7 @@ F: system/ioport.c F: system/memory.c F: system/memory_mapping.c F: system/physmem.c -F: include/exec/memory-internal.h +F: system/memory-internal.h F: scripts/coccinelle/memory-region-housekeeping.cocci Memory devices diff --git a/include/exec/memory-internal.h b/system/memory-internal.h similarity index 88% rename from include/exec/memory-internal.h rename to system/memory-internal.h index c75178a3d6..085e81a9fe 100644 --- a/include/exec/memory-internal.h +++ b/system/memory-internal.h @@ -11,12 +11,6 @@ * */ -/* - * This header is for use by exec.c, memory.c and accel/tcg/cputlb.c ONLY, - * for declarations which are shared between the memory subsystem's - * internals and the TCG TLB code. Do not include it from elsewhere. - */ - #ifndef MEMORY_INTERNAL_H #define MEMORY_INTERNAL_H diff --git a/system/memory.c b/system/memory.c index 6a5d853071..7e2f16f4e9 100644 --- a/system/memory.c +++ b/system/memory.c @@ -24,8 +24,6 @@ #include "qemu/qemu-print.h" #include "qom/object.h" #include "trace.h" - -#include "exec/memory-internal.h" #include "system/ram_addr.h" #include "system/kvm.h" #include "system/runstate.h" @@ -35,6 +33,8 @@ #include "migration/vmstate.h" #include "system/address-spaces.h" +#include "memory-internal.h" + //#define DEBUG_UNASSIGNED static unsigned memory_region_transaction_depth; diff --git a/system/physmem.c b/system/physmem.c index 307d0764b6..16cf557d1a 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -66,7 +66,6 @@ #include "qemu/main-loop.h" #include "system/replay.h" -#include "exec/memory-internal.h" #include "system/ram_addr.h" #include "qemu/pmem.h" @@ -88,6 +87,8 @@ #include #endif +#include "memory-internal.h" + //#define DEBUG_SUBPAGE /* ram_list is read under rcu_read_lock()/rcu_read_unlock(). Writes From 5983a20a0bca7d54846abbb36fb59c7d9b1f226b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 14:33:07 -0700 Subject: [PATCH 0120/2760] meson: Introduce top-level libuser_ss and libsystem_ss We already have two subdirectories for which we need to build files twice, for user vs system modes. Move this handling to the top level. This cannot be combined with user_ss or system_ss, because the formulation has not been extended to support configuration symbols. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- gdbstub/meson.build | 34 +++++++++------------------------- meson.build | 22 ++++++++++++++++++++++ tcg/meson.build | 23 ++--------------------- 3 files changed, 33 insertions(+), 46 deletions(-) diff --git a/gdbstub/meson.build b/gdbstub/meson.build index dff741ddd4..0e8099ae9c 100644 --- a/gdbstub/meson.build +++ b/gdbstub/meson.build @@ -4,32 +4,16 @@ # types such as hwaddr. # -# We need to build the core gdb code via a library to be able to tweak -# cflags so: - -gdb_user_ss = ss.source_set() -gdb_system_ss = ss.source_set() - # We build two versions of gdbstub, one for each mode -gdb_user_ss.add(files('gdbstub.c', 'user.c')) -gdb_system_ss.add(files('gdbstub.c', 'system.c')) - -gdb_user_ss = gdb_user_ss.apply({}) -gdb_system_ss = gdb_system_ss.apply({}) - -libgdb_user = static_library('gdb_user', - gdb_user_ss.sources() + genh, - c_args: '-DCONFIG_USER_ONLY', - build_by_default: false) - -libgdb_system = static_library('gdb_system', - gdb_system_ss.sources() + genh, - build_by_default: false) - -gdb_user = declare_dependency(objects: libgdb_user.extract_all_objects(recursive: false)) -user_ss.add(gdb_user) -gdb_system = declare_dependency(objects: libgdb_system.extract_all_objects(recursive: false)) -system_ss.add(gdb_system) +libuser_ss.add(files( + 'gdbstub.c', + 'user.c' +)) + +libsystem_ss.add(files( + 'gdbstub.c', + 'system.c' +)) common_ss.add(files('syscalls.c')) diff --git a/meson.build b/meson.build index 41f68d3806..7e22afe135 100644 --- a/meson.build +++ b/meson.build @@ -3662,12 +3662,14 @@ io_ss = ss.source_set() qmp_ss = ss.source_set() qom_ss = ss.source_set() system_ss = ss.source_set() +libsystem_ss = ss.source_set() specific_fuzz_ss = ss.source_set() specific_ss = ss.source_set() rust_devices_ss = ss.source_set() stub_ss = ss.source_set() trace_ss = ss.source_set() user_ss = ss.source_set() +libuser_ss = ss.source_set() util_ss = ss.source_set() # accel modules @@ -4045,6 +4047,26 @@ common_ss.add(qom, qemuutil) common_ss.add_all(when: 'CONFIG_SYSTEM_ONLY', if_true: [system_ss]) common_ss.add_all(when: 'CONFIG_USER_ONLY', if_true: user_ss) +libuser_ss = libuser_ss.apply({}) +libuser = static_library('user', + libuser_ss.sources() + genh, + c_args: '-DCONFIG_USER_ONLY', + dependencies: libuser_ss.dependencies(), + build_by_default: false) +libuser = declare_dependency(objects: libuser.extract_all_objects(recursive: false), + dependencies: libuser_ss.dependencies()) +common_ss.add(when: 'CONFIG_USER_ONLY', if_true: libuser) + +libsystem_ss = libsystem_ss.apply({}) +libsystem = static_library('system', + libsystem_ss.sources() + genh, + c_args: '-DCONFIG_SOFTMMU', + dependencies: libsystem_ss.dependencies(), + build_by_default: false) +libsystem = declare_dependency(objects: libsystem.extract_all_objects(recursive: false), + dependencies: libsystem_ss.dependencies()) +common_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: libsystem) + # Note that this library is never used directly (only through extract_objects) # and is not built by default; therefore, source files not used by the build # configuration will be in build.ninja, but are never built by default. diff --git a/tcg/meson.build b/tcg/meson.build index 69ebb4908a..7df378d773 100644 --- a/tcg/meson.build +++ b/tcg/meson.build @@ -27,24 +27,5 @@ if host_os == 'linux' tcg_ss.add(files('perf.c')) endif -tcg_ss = tcg_ss.apply({}) - -libtcg_user = static_library('tcg_user', - tcg_ss.sources() + genh, - dependencies: tcg_ss.dependencies(), - c_args: '-DCONFIG_USER_ONLY', - build_by_default: false) - -tcg_user = declare_dependency(objects: libtcg_user.extract_all_objects(recursive: false), - dependencies: tcg_ss.dependencies()) -user_ss.add(tcg_user) - -libtcg_system = static_library('tcg_system', - tcg_ss.sources() + genh, - dependencies: tcg_ss.dependencies(), - c_args: '-DCONFIG_SOFTMMU', - build_by_default: false) - -tcg_system = declare_dependency(objects: libtcg_system.extract_all_objects(recursive: false), - dependencies: tcg_ss.dependencies()) -system_ss.add(tcg_system) +libuser_ss.add_all(tcg_ss) +libsystem_ss.add_all(tcg_ss) From 7ed79a9adbeff0ff8a8b6c76d3d10b2fe0f68c63 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 14:37:07 -0700 Subject: [PATCH 0121/2760] gdbstub: Move syscalls.c out of common_ss Copy to libuser_ss and libsystem_ss. This file uses semihosting/semihost.h, which has separate implementations with and without CONFIG_USER_ONLY. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- gdbstub/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gdbstub/meson.build b/gdbstub/meson.build index 0e8099ae9c..b25db86767 100644 --- a/gdbstub/meson.build +++ b/gdbstub/meson.build @@ -7,15 +7,15 @@ # We build two versions of gdbstub, one for each mode libuser_ss.add(files( 'gdbstub.c', + 'syscalls.c', 'user.c' )) libsystem_ss.add(files( 'gdbstub.c', + 'syscalls.c', 'system.c' )) -common_ss.add(files('syscalls.c')) - # The user-target is specialised by the guest specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-target.c')) From 1a1567b1747ef46fca2dfa8c22c2262a2e8f6d6c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 14:55:21 -0700 Subject: [PATCH 0122/2760] accel/tcg: Use libuser_ss and libsystem_ss While some of these files are built exactly once, due to being in only libuser_ss or libsystem_ss, some of the includes that they depend on require CONFIG_USER_ONLY. So make use of the common infrastructure to allow that. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 185830d0f5..72d4acfe5e 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -1,12 +1,21 @@ -common_ss.add(when: 'CONFIG_TCG', if_true: files( +if not get_option('tcg').allowed() + subdir_done() +endif + +tcg_ss = ss.source_set() + +tcg_ss.add(files( 'cpu-exec-common.c', 'tcg-runtime.c', 'tcg-runtime-gvec.c', )) if get_option('plugins') - common_ss.add(when: 'CONFIG_TCG', if_true: files('plugin-gen.c')) + tcg_ss.add(files('plugin-gen.c')) endif +libuser_ss.add_all(tcg_ss) +libsystem_ss.add_all(tcg_ss) + tcg_specific_ss = ss.source_set() tcg_specific_ss.add(files( 'tcg-all.c', @@ -22,11 +31,11 @@ specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( 'cputlb.c', )) -user_ss.add(when: ['CONFIG_TCG'], if_true: files( +libuser_ss.add(files( 'user-exec-stub.c', )) -system_ss.add(when: ['CONFIG_TCG'], if_true: files( +libsystem_ss.add(files( 'icount-common.c', 'monitor.c', 'tcg-accel-ops.c', From 695e7f6026c4ed8a1a50cd26d3199fc4f4d9b3a5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 16 Mar 2025 15:48:07 -0700 Subject: [PATCH 0123/2760] target/mips: Restrict semihosting tests to system mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We do not set CONFIG_SEMIHOSTING in configs/targets/mips*-linux-user.mak. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/mips/cpu.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/mips/cpu.c b/target/mips/cpu.c index b207106dd7..47df563e12 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -32,8 +32,10 @@ #include "exec/exec-all.h" #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" -#include "semihosting/semihost.h" #include "fpu_helper.h" +#ifndef CONFIG_USER_ONLY +#include "semihosting/semihost.h" +#endif const char regnames[32][3] = { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", @@ -415,12 +417,11 @@ static void mips_cpu_reset_hold(Object *obj, ResetType type) restore_pamask(env); cs->exception_index = EXCP_NONE; +#ifndef CONFIG_USER_ONLY if (semihosting_get_argc()) { /* UHI interface can be used to obtain argc and argv */ env->active_tc.gpr[4] = -1; } - -#ifndef CONFIG_USER_ONLY if (kvm_enabled()) { kvm_mips_reset_vcpu(cpu); } From 690793e005e004f749aa963ea0040f18e067c98e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 17 Mar 2025 09:36:32 -0700 Subject: [PATCH 0124/2760] target/xtensa: Restrict semihosting tests to system mode We do not set CONFIG_SEMIHOSTING in configs/targets/xtensa*-linux-user.mak. Do not raise SIGILL for user-only unconditionally. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/xtensa/translate.c | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 4f02cefde3..cb817b3119 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -35,14 +35,14 @@ #include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/qemu-print.h" -#include "semihosting/semihost.h" #include "exec/translator.h" #include "exec/translation-block.h" - #include "exec/helper-proto.h" #include "exec/helper-gen.h" - #include "exec/log.h" +#ifndef CONFIG_USER_ONLY +#include "semihosting/semihost.h" +#endif #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" @@ -2241,17 +2241,15 @@ static uint32_t test_exceptions_simcall(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { - bool is_semi = semihosting_enabled(dc->cring != 0); -#ifdef CONFIG_USER_ONLY - bool ill = true; -#else - /* Between RE.2 and RE.3 simcall opcode's become nop for the hardware. */ - bool ill = dc->config->hw_version <= 250002 && !is_semi; -#endif - if (ill || !is_semi) { - qemu_log_mask(LOG_GUEST_ERROR, "SIMCALL but semihosting is disabled\n"); +#ifndef CONFIG_USER_ONLY + if (semihosting_enabled(dc->cring != 0)) { + return 0; } - return ill ? XTENSA_OP_ILL : 0; +#endif + qemu_log_mask(LOG_GUEST_ERROR, "SIMCALL but semihosting is disabled\n"); + + /* Between RE.2 and RE.3 simcall opcode's become nop for the hardware. */ + return dc->config->hw_version <= 250002 ? XTENSA_OP_ILL : 0; } static void translate_simcall(DisasContext *dc, const OpcodeArg arg[], From d0b4cfa62937ce59bd88de5928ada687329be194 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 17:16:05 -0700 Subject: [PATCH 0125/2760] semihosting: Move user-only implementation out-of-line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid testing CONFIG_USER_ONLY in semihost.h. The only function that's required is semihosting_enabled. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/semihosting/semihost.h | 29 ++--------------------------- semihosting/meson.build | 5 +++-- semihosting/stubs-all.c | 6 ++++++ semihosting/stubs-system.c | 6 ------ semihosting/user.c | 20 ++++++++++++++++++++ 5 files changed, 31 insertions(+), 35 deletions(-) create mode 100644 semihosting/user.c diff --git a/include/semihosting/semihost.h b/include/semihosting/semihost.h index 97d2a2ba99..b03e637578 100644 --- a/include/semihosting/semihost.h +++ b/include/semihosting/semihost.h @@ -26,32 +26,6 @@ typedef enum SemihostingTarget { SEMIHOSTING_TARGET_GDB } SemihostingTarget; -#ifdef CONFIG_USER_ONLY -static inline bool semihosting_enabled(bool is_user) -{ - return true; -} - -static inline SemihostingTarget semihosting_get_target(void) -{ - return SEMIHOSTING_TARGET_AUTO; -} - -static inline const char *semihosting_get_arg(int i) -{ - return NULL; -} - -static inline int semihosting_get_argc(void) -{ - return 0; -} - -static inline const char *semihosting_get_cmdline(void) -{ - return NULL; -} -#else /* !CONFIG_USER_ONLY */ /** * semihosting_enabled: * @is_user: true if guest code is in usermode (i.e. not privileged) @@ -59,17 +33,18 @@ static inline const char *semihosting_get_cmdline(void) * Return true if guest code is allowed to make semihosting calls. */ bool semihosting_enabled(bool is_user); + SemihostingTarget semihosting_get_target(void); const char *semihosting_get_arg(int i); int semihosting_get_argc(void); const char *semihosting_get_cmdline(void); void semihosting_arg_fallback(const char *file, const char *cmd); + /* for vl.c hooks */ void qemu_semihosting_enable(void); int qemu_semihosting_config_options(const char *optstr); void qemu_semihosting_chardev_init(void); void qemu_semihosting_console_init(Chardev *); -#endif /* CONFIG_USER_ONLY */ void qemu_semihosting_guestfd_init(void); #endif /* SEMIHOST_H */ diff --git a/semihosting/meson.build b/semihosting/meson.build index 86f5004bed..f3d38dda91 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -7,8 +7,9 @@ specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_true: fil 'uaccess.c', )) -common_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_false: files('stubs-all.c')) -system_ss.add(when: ['CONFIG_SEMIHOSTING'], if_true: files( +common_ss.add(when: 'CONFIG_SEMIHOSTING', if_false: files('stubs-all.c')) +user_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files('user.c')) +system_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( 'config.c', 'console.c', ), if_false: files( diff --git a/semihosting/stubs-all.c b/semihosting/stubs-all.c index a2a1fc9c6f..c001c84574 100644 --- a/semihosting/stubs-all.c +++ b/semihosting/stubs-all.c @@ -11,6 +11,12 @@ #include "qemu/osdep.h" #include "semihosting/semihost.h" +/* Queries to config status default to off */ +bool semihosting_enabled(bool is_user) +{ + return false; +} + SemihostingTarget semihosting_get_target(void) { return SEMIHOSTING_TARGET_AUTO; diff --git a/semihosting/stubs-system.c b/semihosting/stubs-system.c index f26cbb7c25..989789f373 100644 --- a/semihosting/stubs-system.c +++ b/semihosting/stubs-system.c @@ -22,12 +22,6 @@ QemuOptsList qemu_semihosting_config_opts = { }, }; -/* Queries to config status default to off */ -bool semihosting_enabled(bool is_user) -{ - return false; -} - /* * All the rest are empty subs. We could g_assert_not_reached() but * that adds extra weight to the final binary. Waste not want not. diff --git a/semihosting/user.c b/semihosting/user.c new file mode 100644 index 0000000000..515de3d2c0 --- /dev/null +++ b/semihosting/user.c @@ -0,0 +1,20 @@ +/* + * Semihosting for user emulation + * + * Copyright (c) 2019 Linaro Ltd + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "semihosting/semihost.h" + +bool semihosting_enabled(bool is_user) +{ + return true; +} + +SemihostingTarget semihosting_get_target(void) +{ + return SEMIHOSTING_TARGET_AUTO; +} From 17a71e89693e17d60f73a00b4cea6963073152e3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 20 Mar 2025 16:09:51 -0700 Subject: [PATCH 0126/2760] semihosting: Assert is_user in user-only semihosting_enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Suggested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- semihosting/user.c | 1 + 1 file changed, 1 insertion(+) diff --git a/semihosting/user.c b/semihosting/user.c index 515de3d2c0..98c144cb45 100644 --- a/semihosting/user.c +++ b/semihosting/user.c @@ -11,6 +11,7 @@ bool semihosting_enabled(bool is_user) { + assert(is_user); return true; } From 3e57baa22ea6892d1b8c212253b2859be30f45ee Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 14 Mar 2025 10:39:19 -0700 Subject: [PATCH 0127/2760] include/exec: Split out watchpoint.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Relatively few objects in qemu care about watchpoints, so split out to a new header. Removes an instance of CONFIG_USER_ONLY from hw/core/cpu.h. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tcg-accel-ops.c | 1 + include/exec/watchpoint.h | 41 +++++++++++++++++++++++++++++ include/hw/core/cpu.h | 30 --------------------- system/watchpoint.c | 1 + target/arm/debug_helper.c | 1 + target/i386/cpu.c | 1 + target/i386/machine.c | 2 +- target/i386/tcg/system/bpt_helper.c | 1 + target/ppc/cpu.c | 1 + target/ppc/cpu_init.c | 2 +- target/riscv/debug.c | 1 + target/s390x/helper.c | 1 + target/s390x/tcg/excp_helper.c | 1 + target/xtensa/dbg_helper.c | 1 + 14 files changed, 53 insertions(+), 32 deletions(-) create mode 100644 include/exec/watchpoint.h diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index d9b662efe3..5c88056157 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -37,6 +37,7 @@ #include "exec/hwaddr.h" #include "exec/tb-flush.h" #include "exec/translation-block.h" +#include "exec/watchpoint.h" #include "gdbstub/enums.h" #include "hw/core/cpu.h" diff --git a/include/exec/watchpoint.h b/include/exec/watchpoint.h new file mode 100644 index 0000000000..4b6668826c --- /dev/null +++ b/include/exec/watchpoint.h @@ -0,0 +1,41 @@ +/* + * CPU watchpoints + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef EXEC_WATCHPOINT_H +#define EXEC_WATCHPOINT_H + +#if defined(CONFIG_USER_ONLY) +static inline int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, + int flags, CPUWatchpoint **watchpoint) +{ + return -ENOSYS; +} + +static inline int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, + vaddr len, int flags) +{ + return -ENOSYS; +} + +static inline void cpu_watchpoint_remove_by_ref(CPUState *cpu, + CPUWatchpoint *wp) +{ +} + +static inline void cpu_watchpoint_remove_all(CPUState *cpu, int mask) +{ +} +#else +int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, + int flags, CPUWatchpoint **watchpoint); +int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, + vaddr len, int flags); +void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint); +void cpu_watchpoint_remove_all(CPUState *cpu, int mask); +#endif + +#endif /* EXEC_WATCHPOINT_H */ diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index abd8764e83..37cb7d1531 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1109,36 +1109,6 @@ static inline bool cpu_breakpoint_test(CPUState *cpu, vaddr pc, int mask) return false; } -#if defined(CONFIG_USER_ONLY) -static inline int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, - int flags, CPUWatchpoint **watchpoint) -{ - return -ENOSYS; -} - -static inline int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, - vaddr len, int flags) -{ - return -ENOSYS; -} - -static inline void cpu_watchpoint_remove_by_ref(CPUState *cpu, - CPUWatchpoint *wp) -{ -} - -static inline void cpu_watchpoint_remove_all(CPUState *cpu, int mask) -{ -} -#else -int cpu_watchpoint_insert(CPUState *cpu, vaddr addr, vaddr len, - int flags, CPUWatchpoint **watchpoint); -int cpu_watchpoint_remove(CPUState *cpu, vaddr addr, - vaddr len, int flags); -void cpu_watchpoint_remove_by_ref(CPUState *cpu, CPUWatchpoint *watchpoint); -void cpu_watchpoint_remove_all(CPUState *cpu, int mask); -#endif - /** * cpu_get_address_space: * @cpu: CPU to get address space from diff --git a/system/watchpoint.c b/system/watchpoint.c index 08dbd8483d..21d0bb36ca 100644 --- a/system/watchpoint.c +++ b/system/watchpoint.c @@ -21,6 +21,7 @@ #include "qemu/error-report.h" #include "exec/cputlb.h" #include "exec/target_page.h" +#include "exec/watchpoint.h" #include "hw/core/cpu.h" /* Add a watchpoint. */ diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index a9a619ba6b..473ee2af38 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -13,6 +13,7 @@ #include "cpregs.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" +#include "exec/watchpoint.h" #include "system/tcg.h" #ifdef CONFIG_TCG diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 5c9f80b4cc..c596e2174d 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -35,6 +35,7 @@ #include "standard-headers/asm-x86/kvm_para.h" #include "hw/qdev-properties.h" #include "hw/i386/topology.h" +#include "exec/watchpoint.h" #ifndef CONFIG_USER_ONLY #include "system/reset.h" #include "qapi/qapi-commands-machine-target.h" diff --git a/target/i386/machine.c b/target/i386/machine.c index 70f632a36f..6cb561c632 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -7,7 +7,7 @@ #include "hw/i386/x86.h" #include "kvm/kvm_i386.h" #include "hw/xen/xen.h" - +#include "exec/watchpoint.h" #include "system/kvm.h" #include "system/kvm_xen.h" #include "system/tcg.h" diff --git a/target/i386/tcg/system/bpt_helper.c b/target/i386/tcg/system/bpt_helper.c index be232c1ca9..08ccd3f5e6 100644 --- a/target/i386/tcg/system/bpt_helper.c +++ b/target/i386/tcg/system/bpt_helper.c @@ -21,6 +21,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" +#include "exec/watchpoint.h" #include "tcg/helper-tcg.h" diff --git a/target/ppc/cpu.c b/target/ppc/cpu.c index bfcc695de7..4d8faaddee 100644 --- a/target/ppc/cpu.c +++ b/target/ppc/cpu.c @@ -22,6 +22,7 @@ #include "cpu-models.h" #include "cpu-qom.h" #include "exec/log.h" +#include "exec/watchpoint.h" #include "fpu/softfloat-helpers.h" #include "mmu-hash64.h" #include "helper_regs.h" diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index f81cb680fc..17f0f3d3ff 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -40,7 +40,7 @@ #include "qemu/cutils.h" #include "disas/capstone.h" #include "fpu/softfloat.h" - +#include "exec/watchpoint.h" #include "helper_regs.h" #include "internal.h" #include "spr_common.h" diff --git a/target/riscv/debug.c b/target/riscv/debug.c index 9db4048523..fea989afe9 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -30,6 +30,7 @@ #include "trace.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" +#include "exec/watchpoint.h" #include "system/cpu-timers.h" /* diff --git a/target/s390x/helper.c b/target/s390x/helper.c index c689e11b46..e660c69f60 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -27,6 +27,7 @@ #include "target/s390x/kvm/pv.h" #include "system/hw_accel.h" #include "system/runstate.h" +#include "exec/watchpoint.h" void s390x_tod_timer(void *opaque) { diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index ac733f407f..1d51043e88 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -24,6 +24,7 @@ #include "exec/helper-proto.h" #include "exec/cputlb.h" #include "exec/exec-all.h" +#include "exec/watchpoint.h" #include "s390x-internal.h" #include "tcg_s390x.h" #ifndef CONFIG_USER_ONLY diff --git a/target/xtensa/dbg_helper.c b/target/xtensa/dbg_helper.c index 163a1ffc7b..c4f4298a50 100644 --- a/target/xtensa/dbg_helper.c +++ b/target/xtensa/dbg_helper.c @@ -31,6 +31,7 @@ #include "exec/helper-proto.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" +#include "exec/watchpoint.h" #include "system/address-spaces.h" void HELPER(wsr_ibreakenable)(CPUXtensaState *env, uint32_t v) From 53d2354cec1df5a364600ab5dcf4462266a84b63 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 19:45:02 -0700 Subject: [PATCH 0128/2760] hw/core: Move unconditional files to libsystem_ss, libuser_ss Many of the headers used by these require CONFIG_USER_ONLY. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- hw/core/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/core/meson.build b/hw/core/meson.build index b5a545a0ed..547de6527c 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -26,7 +26,7 @@ system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) system_ss.add(when: 'CONFIG_EIF', if_true: [files('eif.c'), zlib, libcbor, gnutls]) -system_ss.add(files( +libsystem_ss.add(files( 'cpu-system.c', 'fw-path-provider.c', 'gpio.c', @@ -46,7 +46,7 @@ system_ss.add(files( 'vm-change-state-handler.c', 'clock-vmstate.c', )) -user_ss.add(files( +libuser_ss.add(files( 'cpu-user.c', 'qdev-user.c', )) From 7c2f3580e51f3a1d74d78b88267be22650b518ac Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 19:45:50 -0700 Subject: [PATCH 0129/2760] system: Move most files to libsystem_ss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the headers used require CONFIG_USER_ONLY. Do not move vl.c, because it has other include dependencies that are present in system_ss. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- system/meson.build | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/system/meson.build b/system/meson.build index 063301c3ad..c2f0082766 100644 --- a/system/meson.build +++ b/system/meson.build @@ -4,6 +4,10 @@ specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: [files( )]) system_ss.add(files( + 'vl.c', +), sdl, libpmem, libdaxctl) + +libsystem_ss.add(files( 'balloon.c', 'bootdevice.c', 'cpus.c', @@ -23,9 +27,8 @@ system_ss.add(files( 'runstate-hmp-cmds.c', 'runstate.c', 'tpm-hmp-cmds.c', - 'vl.c', 'watchpoint.c', -), sdl, libpmem, libdaxctl) +)) if have_tpm system_ss.add(files('tpm.c')) From 6c1ae457a17a9462fb89ef1f30ad7da5266bfea6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 19:46:44 -0700 Subject: [PATCH 0130/2760] plugins: Move api.c, core.c to libuser_ss, libsystem_ss Headers used by these files require CONFIG_USER_ONLY. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- plugins/meson.build | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/plugins/meson.build b/plugins/meson.build index 3be8245a69..5383c7b88b 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -61,5 +61,8 @@ endif user_ss.add(files('user.c', 'api-user.c')) system_ss.add(files('system.c', 'api-system.c')) -common_ss.add(files('loader.c', 'api.c', 'core.c')) +libuser_ss.add(files('api.c', 'core.c')) +libsystem_ss.add(files('api.c', 'core.c')) + +common_ss.add(files('loader.c')) From 1596f1206f1d7d9ab9abfb7e010f3d3775c51202 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 20:08:44 -0700 Subject: [PATCH 0131/2760] include/exec: Drop ifndef CONFIG_USER_ONLY from cpu-common.h We were hiding a number of declarations from user-only, although it hurts nothing to allow them. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/exec/cpu-common.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index be032e1a49..9b83fd7ac8 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -9,9 +9,7 @@ #define CPU_COMMON_H #include "exec/vaddr.h" -#ifndef CONFIG_USER_ONLY #include "exec/hwaddr.h" -#endif #include "hw/core/cpu.h" #include "tcg/debug-assert.h" #include "exec/page-protection.h" @@ -40,8 +38,6 @@ int cpu_get_free_index(void); void tcg_iommu_init_notifier_list(CPUState *cpu); void tcg_iommu_free_notifier_list(CPUState *cpu); -#if !defined(CONFIG_USER_ONLY) - enum device_endian { DEVICE_NATIVE_ENDIAN, DEVICE_BIG_ENDIAN, @@ -176,8 +172,6 @@ int ram_block_discard_range(RAMBlock *rb, uint64_t start, size_t length); int ram_block_discard_guest_memfd_range(RAMBlock *rb, uint64_t start, size_t length); -#endif - /* Returns: 0 on success, -1 on error */ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, void *ptr, size_t len, bool is_write); From bcd6d0d60c5c5edf324af4427039d50693731e46 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 20:10:02 -0700 Subject: [PATCH 0132/2760] include/hw/core: Drop ifndef CONFIG_USER_ONLY from cpu.h We were hiding a number of declarations from user-only, although it hurts nothing to allow them. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/hw/core/cpu.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 37cb7d1531..6dcee5d0ba 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -614,8 +614,6 @@ extern bool mttcg_enabled; */ bool cpu_paging_enabled(const CPUState *cpu); -#if !defined(CONFIG_USER_ONLY) - /** * cpu_get_memory_mapping: * @cpu: The CPU whose memory mappings are to be obtained. @@ -676,8 +674,6 @@ int cpu_write_elf32_qemunote(WriteCoreDumpFunction f, CPUState *cpu, */ GuestPanicInformation *cpu_get_crash_info(CPUState *cpu); -#endif /* !CONFIG_USER_ONLY */ - /** * CPUDumpFlags: * @CPU_DUMP_CODE: @@ -701,7 +697,6 @@ enum CPUDumpFlags { */ void cpu_dump_state(CPUState *cpu, FILE *f, int flags); -#ifndef CONFIG_USER_ONLY /** * cpu_get_phys_page_attrs_debug: * @cpu: The CPU to obtain the physical page address for. @@ -758,8 +753,6 @@ bool cpu_virtio_is_big_endian(CPUState *cpu); */ bool cpu_has_work(CPUState *cpu); -#endif /* CONFIG_USER_ONLY */ - /** * cpu_list_add: * @cpu: The CPU to be added to the list of CPUs. @@ -1136,8 +1129,6 @@ const char *target_name(void); #ifdef COMPILING_PER_TARGET -#ifndef CONFIG_USER_ONLY - extern const VMStateDescription vmstate_cpu_common; #define VMSTATE_CPU() { \ @@ -1147,7 +1138,6 @@ extern const VMStateDescription vmstate_cpu_common; .flags = VMS_STRUCT, \ .offset = 0, \ } -#endif /* !CONFIG_USER_ONLY */ #endif /* COMPILING_PER_TARGET */ From a771605798ba929fa18da2bcdd316c463c387462 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 20:11:41 -0700 Subject: [PATCH 0133/2760] include/hw/intc: Remove ifndef CONFIG_USER_ONLY from armv7m_nvic.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were hiding a number of declarations from user-only, although it hurts nothing to allow them. The inlines for user-only are unused. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/hw/intc/armv7m_nvic.h | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/include/hw/intc/armv7m_nvic.h b/include/hw/intc/armv7m_nvic.h index 89fe8aedaa..7b9964fe7e 100644 --- a/include/hw/intc/armv7m_nvic.h +++ b/include/hw/intc/armv7m_nvic.h @@ -189,21 +189,7 @@ int armv7m_nvic_raw_execution_priority(NVICState *s); * @secure: the security state to test * This corresponds to the pseudocode IsReqExecPriNeg(). */ -#ifndef CONFIG_USER_ONLY bool armv7m_nvic_neg_prio_requested(NVICState *s, bool secure); -#else -static inline bool armv7m_nvic_neg_prio_requested(NVICState *s, bool secure) -{ - return false; -} -#endif -#ifndef CONFIG_USER_ONLY bool armv7m_nvic_can_take_pending_exception(NVICState *s); -#else -static inline bool armv7m_nvic_can_take_pending_exception(NVICState *s) -{ - return true; -} -#endif #endif From 31030cf0ff7c72584663a5fe9a9318fc843210ef Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 20:12:17 -0700 Subject: [PATCH 0134/2760] include/hw/s390x: Remove ifndef CONFIG_USER_ONLY in css.h We were hiding a number of declarations from user-only, although it hurts nothing to allow them. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/hw/s390x/css.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/hw/s390x/css.h b/include/hw/s390x/css.h index dbf919bdd2..0b0400a9d4 100644 --- a/include/hw/s390x/css.h +++ b/include/hw/s390x/css.h @@ -238,7 +238,6 @@ uint32_t css_get_adapter_id(CssIoAdapterType type, uint8_t isc); void css_register_io_adapters(CssIoAdapterType type, bool swap, bool maskable, uint8_t flags, Error **errp); -#ifndef CONFIG_USER_ONLY SubchDev *css_find_subch(uint8_t m, uint8_t cssid, uint8_t ssid, uint16_t schid); bool css_subch_visible(SubchDev *sch); @@ -262,7 +261,6 @@ int css_enable_mss(void); IOInstEnding css_do_rsch(SubchDev *sch); int css_do_rchp(uint8_t cssid, uint8_t chpid); bool css_present(uint8_t cssid); -#endif extern const PropertyInfo css_devid_ro_propinfo; From 161f5bc8e965fa8255db435683e6b52042037bb7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 14 Mar 2025 12:57:31 -0700 Subject: [PATCH 0135/2760] include/exec: Split out icount.h Split icount stuff from system/cpu-timers.h. There are 17 files which only require icount.h, 7 that only require cpu-timers.h, and 7 that require both. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 2 +- accel/tcg/icount-common.c | 2 +- accel/tcg/monitor.c | 1 + accel/tcg/tcg-accel-ops-icount.c | 2 +- accel/tcg/tcg-accel-ops-mttcg.c | 2 +- accel/tcg/tcg-accel-ops-rr.c | 2 +- accel/tcg/tcg-accel-ops.c | 2 +- accel/tcg/tcg-all.c | 2 +- accel/tcg/translate-all.c | 2 +- hw/core/ptimer.c | 2 +- include/exec/icount.h | 68 ++++++++++++++++++++++++++++++++ include/system/cpu-timers.h | 58 --------------------------- replay/replay.c | 2 +- stubs/icount.c | 2 +- system/cpu-timers.c | 1 + system/dma-helpers.c | 2 +- system/vl.c | 1 + target/arm/helper.c | 1 + target/riscv/cpu_helper.c | 2 +- target/riscv/csr.c | 2 +- target/riscv/debug.c | 1 + target/riscv/machine.c | 2 +- target/riscv/pmu.c | 2 +- util/async.c | 2 +- util/main-loop.c | 1 + util/qemu-timer.c | 1 + 26 files changed, 92 insertions(+), 75 deletions(-) create mode 100644 include/exec/icount.h diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 372b876604..034c2ded6b 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -35,7 +35,7 @@ #include "exec/log.h" #include "qemu/main-loop.h" #include "exec/cpu-all.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "exec/replay-core.h" #include "system/tcg.h" #include "exec/helper-proto-common.h" diff --git a/accel/tcg/icount-common.c b/accel/tcg/icount-common.c index 402d3e3f4e..d6471174a3 100644 --- a/accel/tcg/icount-common.c +++ b/accel/tcg/icount-common.c @@ -35,7 +35,7 @@ #include "system/replay.h" #include "system/runstate.h" #include "hw/core/cpu.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "system/cpu-timers-internal.h" /* diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index eeb38a4d9c..1c182b6bfb 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -14,6 +14,7 @@ #include "qapi/qapi-commands-machine.h" #include "monitor/monitor.h" #include "system/cpu-timers.h" +#include "exec/icount.h" #include "system/tcg.h" #include "tcg/tcg.h" #include "internal-common.h" diff --git a/accel/tcg/tcg-accel-ops-icount.c b/accel/tcg/tcg-accel-ops-icount.c index 27cf1044c7..d0f7b410fa 100644 --- a/accel/tcg/tcg-accel-ops-icount.c +++ b/accel/tcg/tcg-accel-ops-icount.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "system/replay.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" #include "hw/core/cpu.h" diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index bdcc385ae9..dfcee30947 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "system/tcg.h" #include "system/replay.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "qemu/main-loop.h" #include "qemu/notify.h" #include "qemu/guest-random.h" diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index f62cf24e1d..6eec5c9eee 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -27,7 +27,7 @@ #include "qemu/lockable.h" #include "system/tcg.h" #include "system/replay.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "qemu/main-loop.h" #include "qemu/notify.h" #include "qemu/guest-random.h" diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 5c88056157..ccdb781eef 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -29,7 +29,7 @@ #include "system/accel-ops.h" #include "system/tcg.h" #include "system/replay.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "qemu/main-loop.h" #include "qemu/guest-random.h" #include "qemu/timer.h" diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index c1a30b0121..7a5b810b88 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "system/tcg.h" #include "exec/replay-core.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "tcg/startup.h" #include "qapi/error.h" #include "qemu/error-report.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 167535bcb1..bb161ae61a 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -55,7 +55,7 @@ #include "qemu/cacheinfo.h" #include "qemu/timer.h" #include "exec/log.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "system/tcg.h" #include "qapi/error.h" #include "accel/tcg/cpu-ops.h" diff --git a/hw/core/ptimer.c b/hw/core/ptimer.c index 7f63d17ca1..0aeb10fb53 100644 --- a/hw/core/ptimer.c +++ b/hw/core/ptimer.c @@ -11,7 +11,7 @@ #include "migration/vmstate.h" #include "qemu/host-utils.h" #include "exec/replay-core.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "system/qtest.h" #include "block/aio.h" #include "hw/clock.h" diff --git a/include/exec/icount.h b/include/exec/icount.h new file mode 100644 index 0000000000..4964987ae4 --- /dev/null +++ b/include/exec/icount.h @@ -0,0 +1,68 @@ +/* + * icount - Instruction Counter API + * CPU timers state API + * + * Copyright 2020 SUSE LLC + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef EXEC_ICOUNT_H +#define EXEC_ICOUNT_H + +/** + * ICountMode: icount enablement state: + * + * @ICOUNT_DISABLED: Disabled - Do not count executed instructions. + * @ICOUNT_PRECISE: Enabled - Fixed conversion of insn to ns via "shift" option + * @ICOUNT_ADAPTATIVE: Enabled - Runtime adaptive algorithm to compute shift + */ +typedef enum { + ICOUNT_DISABLED = 0, + ICOUNT_PRECISE, + ICOUNT_ADAPTATIVE, +} ICountMode; + +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) +extern ICountMode use_icount; +#define icount_enabled() (use_icount) +#else +#define icount_enabled() ICOUNT_DISABLED +#endif + +/* + * Update the icount with the executed instructions. Called by + * cpus-tcg vCPU thread so the main-loop can see time has moved forward. + */ +void icount_update(CPUState *cpu); + +/* get raw icount value */ +int64_t icount_get_raw(void); + +/* return the virtual CPU time in ns, based on the instruction counter. */ +int64_t icount_get(void); +/* + * convert an instruction counter value to ns, based on the icount shift. + * This shift is set as a fixed value with the icount "shift" option + * (precise mode), or it is constantly approximated and corrected at + * runtime in adaptive mode. + */ +int64_t icount_to_ns(int64_t icount); + +/** + * icount_configure: configure the icount options, including "shift" + * @opts: Options to parse + * @errp: pointer to a NULL-initialized error object + * + * Return: true on success, else false setting @errp with error + */ +bool icount_configure(QemuOpts *opts, Error **errp); + +/* used by tcg vcpu thread to calc icount budget */ +int64_t icount_round(int64_t count); + +/* if the CPUs are idle, start accounting real time to virtual clock. */ +void icount_start_warp_timer(void); +void icount_account_warp_timer(void); +void icount_notify_exit(void); + +#endif /* EXEC_ICOUNT_H */ diff --git a/include/system/cpu-timers.h b/include/system/cpu-timers.h index 64ae54f6d6..a1abed0d7a 100644 --- a/include/system/cpu-timers.h +++ b/include/system/cpu-timers.h @@ -15,64 +15,6 @@ /* init the whole cpu timers API, including icount, ticks, and cpu_throttle */ void cpu_timers_init(void); -/* icount - Instruction Counter API */ - -/** - * ICountMode: icount enablement state: - * - * @ICOUNT_DISABLED: Disabled - Do not count executed instructions. - * @ICOUNT_PRECISE: Enabled - Fixed conversion of insn to ns via "shift" option - * @ICOUNT_ADAPTATIVE: Enabled - Runtime adaptive algorithm to compute shift - */ -typedef enum { - ICOUNT_DISABLED = 0, - ICOUNT_PRECISE, - ICOUNT_ADAPTATIVE, -} ICountMode; - -#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) -extern ICountMode use_icount; -#define icount_enabled() (use_icount) -#else -#define icount_enabled() ICOUNT_DISABLED -#endif - -/* - * Update the icount with the executed instructions. Called by - * cpus-tcg vCPU thread so the main-loop can see time has moved forward. - */ -void icount_update(CPUState *cpu); - -/* get raw icount value */ -int64_t icount_get_raw(void); - -/* return the virtual CPU time in ns, based on the instruction counter. */ -int64_t icount_get(void); -/* - * convert an instruction counter value to ns, based on the icount shift. - * This shift is set as a fixed value with the icount "shift" option - * (precise mode), or it is constantly approximated and corrected at - * runtime in adaptive mode. - */ -int64_t icount_to_ns(int64_t icount); - -/** - * icount_configure: configure the icount options, including "shift" - * @opts: Options to parse - * @errp: pointer to a NULL-initialized error object - * - * Return: true on success, else false setting @errp with error - */ -bool icount_configure(QemuOpts *opts, Error **errp); - -/* used by tcg vcpu thread to calc icount budget */ -int64_t icount_round(int64_t count); - -/* if the CPUs are idle, start accounting real time to virtual clock. */ -void icount_start_warp_timer(void); -void icount_account_warp_timer(void); -void icount_notify_exit(void); - /* * CPU Ticks and Clock */ diff --git a/replay/replay.c b/replay/replay.c index 3adc387b3d..a3e24c967a 100644 --- a/replay/replay.c +++ b/replay/replay.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "system/replay.h" #include "system/runstate.h" #include "replay-internal.h" diff --git a/stubs/icount.c b/stubs/icount.c index edbf60cbfa..ceb73b4fc2 100644 --- a/stubs/icount.c +++ b/stubs/icount.c @@ -1,6 +1,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" /* icount - Instruction Counter API */ diff --git a/system/cpu-timers.c b/system/cpu-timers.c index 23dd82b465..cb35fa62b8 100644 --- a/system/cpu-timers.c +++ b/system/cpu-timers.c @@ -36,6 +36,7 @@ #include "hw/core/cpu.h" #include "system/cpu-timers.h" #include "system/cpu-timers-internal.h" +#include "exec/icount.h" /* clock and ticks */ diff --git a/system/dma-helpers.c b/system/dma-helpers.c index 6bad75876f..0d592f6468 100644 --- a/system/dma-helpers.c +++ b/system/dma-helpers.c @@ -13,7 +13,7 @@ #include "trace.h" #include "qemu/thread.h" #include "qemu/main-loop.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "qemu/range.h" /* #define DEBUG_IOMMU */ diff --git a/system/vl.c b/system/vl.c index ec93988a03..c17945c493 100644 --- a/system/vl.c +++ b/system/vl.c @@ -89,6 +89,7 @@ #include "audio/audio.h" #include "system/cpus.h" #include "system/cpu-timers.h" +#include "exec/icount.h" #include "migration/colo.h" #include "migration/postcopy-ram.h" #include "system/kvm.h" diff --git a/target/arm/helper.c b/target/arm/helper.c index 0454b06a6c..becbbbd0d8 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -24,6 +24,7 @@ #include "exec/translation-block.h" #include "hw/irq.h" #include "system/cpu-timers.h" +#include "exec/icount.h" #include "system/kvm.h" #include "system/tcg.h" #include "qapi/error.h" diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 6c4391d96b..0dd8645994 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -31,7 +31,7 @@ #include "accel/tcg/cpu-ops.h" #include "trace.h" #include "semihosting/common-semi.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "cpu_bits.h" #include "debug.h" #include "pmp.h" diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 7948188356..c52c87faae 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -27,7 +27,7 @@ #include "exec/exec-all.h" #include "exec/cputlb.h" #include "exec/tb-flush.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "qemu/guest-random.h" #include "qapi/error.h" #include diff --git a/target/riscv/debug.c b/target/riscv/debug.c index fea989afe9..7fc9e121e1 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -32,6 +32,7 @@ #include "exec/helper-proto.h" #include "exec/watchpoint.h" #include "system/cpu-timers.h" +#include "exec/icount.h" /* * The following M-mode trigger CSRs are implemented: diff --git a/target/riscv/machine.c b/target/riscv/machine.c index 889e2b6570..a1f70cc955 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -21,7 +21,7 @@ #include "qemu/error-report.h" #include "system/kvm.h" #include "migration/cpu.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "debug.h" static bool pmp_needed(void *opaque) diff --git a/target/riscv/pmu.c b/target/riscv/pmu.c index 0408f96e6a..a68809eef3 100644 --- a/target/riscv/pmu.c +++ b/target/riscv/pmu.c @@ -22,7 +22,7 @@ #include "qemu/timer.h" #include "cpu.h" #include "pmu.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "system/device_tree.h" #define RISCV_TIMEBASE_FREQ 1000000000 /* 1Ghz */ diff --git a/util/async.c b/util/async.c index 863416dee9..2719c629ae 100644 --- a/util/async.c +++ b/util/async.c @@ -35,7 +35,7 @@ #include "block/raw-aio.h" #include "qemu/coroutine_int.h" #include "qemu/coroutine-tls.h" -#include "system/cpu-timers.h" +#include "exec/icount.h" #include "trace.h" /***********************************************************/ diff --git a/util/main-loop.c b/util/main-loop.c index acad8c2e6c..42bd75c193 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -27,6 +27,7 @@ #include "qemu/cutils.h" #include "qemu/timer.h" #include "system/cpu-timers.h" +#include "exec/icount.h" #include "system/replay.h" #include "qemu/main-loop.h" #include "block/aio.h" diff --git a/util/qemu-timer.c b/util/qemu-timer.c index 788466fe22..1fb48be281 100644 --- a/util/qemu-timer.c +++ b/util/qemu-timer.c @@ -27,6 +27,7 @@ #include "qemu/timer.h" #include "qemu/lockable.h" #include "system/cpu-timers.h" +#include "exec/icount.h" #include "system/replay.h" #include "system/cpus.h" From 1751889b5a63f7b246525fad4cfd6902e674dcc4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 14 Mar 2025 13:15:07 -0700 Subject: [PATCH 0136/2760] include/exec: Protect icount_enabled from poisoned symbols Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/exec/icount.h | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/include/exec/icount.h b/include/exec/icount.h index 4964987ae4..7a26b40084 100644 --- a/include/exec/icount.h +++ b/include/exec/icount.h @@ -22,13 +22,21 @@ typedef enum { ICOUNT_ADAPTATIVE, } ICountMode; -#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) +#ifdef CONFIG_TCG extern ICountMode use_icount; #define icount_enabled() (use_icount) #else #define icount_enabled() ICOUNT_DISABLED #endif +/* Protect the CONFIG_USER_ONLY test vs poisoning. */ +#if defined(COMPILING_PER_TARGET) || defined(COMPILING_SYSTEM_VS_USER) +# ifdef CONFIG_USER_ONLY +# undef icount_enabled +# define icount_enabled() ICOUNT_DISABLED +# endif +#endif + /* * Update the icount with the executed instructions. Called by * cpus-tcg vCPU thread so the main-loop can see time has moved forward. From 4d9baad7dc1d57cc3fe7d71d16431ccff203dfbb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 20:15:41 -0700 Subject: [PATCH 0137/2760] include/system: Remove ifndef CONFIG_USER_ONLY in qtest.h This is include/system, so CONFIG_USER_ONLY will never be true. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/system/qtest.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/system/qtest.h b/include/system/qtest.h index 6ddddc501b..84b1f8c6ee 100644 --- a/include/system/qtest.h +++ b/include/system/qtest.h @@ -23,7 +23,6 @@ static inline bool qtest_enabled(void) return qtest_allowed; } -#ifndef CONFIG_USER_ONLY void G_GNUC_PRINTF(2, 3) qtest_sendf(CharBackend *chr, const char *fmt, ...); void qtest_set_command_cb(bool (*pc_cb)(CharBackend *chr, gchar **words)); bool qtest_driver(void); @@ -33,6 +32,5 @@ void qtest_server_init(const char *qtest_chrdev, const char *qtest_log, Error ** void qtest_server_set_send_handler(void (*send)(void *, const char *), void *opaque); void qtest_server_inproc_recv(void *opaque, const char *buf); -#endif #endif From c09a2edce0b0764698f61073e968288820b0941c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 20:18:08 -0700 Subject: [PATCH 0138/2760] include/qemu: Remove ifndef CONFIG_USER_ONLY from accel.h While setup_post and has_memory will not be used for CONFIG_USER_ONLY, let the struct have constant layout. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/qemu/accel.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/include/qemu/accel.h b/include/qemu/accel.h index 972a849a2b..fbd3d897fe 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -38,13 +38,13 @@ typedef struct AccelClass { const char *name; int (*init_machine)(MachineState *ms); -#ifndef CONFIG_USER_ONLY + bool (*cpu_common_realize)(CPUState *cpu, Error **errp); + void (*cpu_common_unrealize)(CPUState *cpu); + + /* system related hooks */ void (*setup_post)(MachineState *ms, AccelState *accel); bool (*has_memory)(MachineState *ms, AddressSpace *as, hwaddr start_addr, hwaddr size); -#endif - bool (*cpu_common_realize)(CPUState *cpu, Error **errp); - void (*cpu_common_unrealize)(CPUState *cpu); /* gdbstub related hooks */ int (*gdbstub_supported_sstep_flags)(void); @@ -78,12 +78,10 @@ const char *current_accel_name(void); void accel_init_interfaces(AccelClass *ac); -#ifndef CONFIG_USER_ONLY int accel_init_machine(AccelState *accel, MachineState *ms); /* Called just before os_setup_post (ie just before drop OS privs) */ void accel_setup_post(MachineState *ms); -#endif /* !CONFIG_USER_ONLY */ /** * accel_cpu_instance_init: From e4610f38095a3ae01177fe67fd70e4d66b683259 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 20:23:18 -0700 Subject: [PATCH 0139/2760] target/riscv: Remove ifndef CONFIG_USER_ONLY from cpu_cfg.h While RISCVCPUConfig.satp_mode is unused for user-only, this header is used from disas/riscv.h, whose users are only built once. The savings of 4 bytes isn't worth it. Reviewed-by: Alistair Francis Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/riscv/cpu_cfg.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 8a843482cc..cfe371b829 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -196,9 +196,7 @@ struct RISCVCPUConfig { bool short_isa_string; -#ifndef CONFIG_USER_ONLY RISCVSATPMap satp_mode; -#endif }; typedef struct RISCVCPUConfig RISCVCPUConfig; From 8916c373a3fd56938f1b7d57010491a0b9662b1e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Mar 2025 20:24:44 -0700 Subject: [PATCH 0140/2760] meson: Only allow CONFIG_USER_ONLY from certain source sets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Poison CONFIG_USER_ONLY and CONFIG_SOFTMMU unless the compilation unit is in specific_ss, libuser_ss, or libsystem_ss. This is intended to prevent files being incorrectly added to common_ss. Remove #ifndef CONFIG_USER_ONLY / #error / #endif blocks. All they do is trigger the poison error. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/exec/poison.h | 5 +++++ include/hw/hw.h | 4 ---- include/system/confidential-guest-support.h | 4 ---- include/system/replay.h | 4 ---- include/system/xen.h | 4 ---- meson.build | 6 ++++-- 6 files changed, 9 insertions(+), 18 deletions(-) diff --git a/include/exec/poison.h b/include/exec/poison.h index 2c151fd1e0..4180a5a489 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -66,4 +66,9 @@ #pragma GCC poison CONFIG_WHPX #pragma GCC poison CONFIG_XEN +#ifndef COMPILING_SYSTEM_VS_USER +#pragma GCC poison CONFIG_USER_ONLY +#pragma GCC poison CONFIG_SOFTMMU +#endif + #endif diff --git a/include/hw/hw.h b/include/hw/hw.h index 045c1c8b09..1b33d12b7f 100644 --- a/include/hw/hw.h +++ b/include/hw/hw.h @@ -1,10 +1,6 @@ #ifndef QEMU_HW_H #define QEMU_HW_H -#ifdef CONFIG_USER_ONLY -#error Cannot include hw/hw.h from user emulation -#endif - G_NORETURN void hw_error(const char *fmt, ...) G_GNUC_PRINTF(1, 2); #endif diff --git a/include/system/confidential-guest-support.h b/include/system/confidential-guest-support.h index b68c4bebbc..ea46b50c56 100644 --- a/include/system/confidential-guest-support.h +++ b/include/system/confidential-guest-support.h @@ -18,10 +18,6 @@ #ifndef QEMU_CONFIDENTIAL_GUEST_SUPPORT_H #define QEMU_CONFIDENTIAL_GUEST_SUPPORT_H -#ifdef CONFIG_USER_ONLY -#error Cannot include system/confidential-guest-support.h from user emulation -#endif - #include "qom/object.h" #define TYPE_CONFIDENTIAL_GUEST_SUPPORT "confidential-guest-support" diff --git a/include/system/replay.h b/include/system/replay.h index 8926d8cf4b..1c87c97fdd 100644 --- a/include/system/replay.h +++ b/include/system/replay.h @@ -11,10 +11,6 @@ #ifndef SYSTEM_REPLAY_H #define SYSTEM_REPLAY_H -#ifdef CONFIG_USER_ONLY -#error Cannot include this header from user emulation -#endif - #include "exec/replay-core.h" #include "qapi/qapi-types-misc.h" #include "qapi/qapi-types-run-state.h" diff --git a/include/system/xen.h b/include/system/xen.h index 5f41915732..c2f283d1c2 100644 --- a/include/system/xen.h +++ b/include/system/xen.h @@ -10,10 +10,6 @@ #ifndef SYSTEM_XEN_H #define SYSTEM_XEN_H -#ifdef CONFIG_USER_ONLY -#error Cannot include system/xen.h from user emulation -#endif - #include "exec/cpu-common.h" #ifdef COMPILING_PER_TARGET diff --git a/meson.build b/meson.build index 7e22afe135..657949326b 100644 --- a/meson.build +++ b/meson.build @@ -4050,7 +4050,8 @@ common_ss.add_all(when: 'CONFIG_USER_ONLY', if_true: user_ss) libuser_ss = libuser_ss.apply({}) libuser = static_library('user', libuser_ss.sources() + genh, - c_args: '-DCONFIG_USER_ONLY', + c_args: ['-DCONFIG_USER_ONLY', + '-DCOMPILING_SYSTEM_VS_USER'], dependencies: libuser_ss.dependencies(), build_by_default: false) libuser = declare_dependency(objects: libuser.extract_all_objects(recursive: false), @@ -4060,7 +4061,8 @@ common_ss.add(when: 'CONFIG_USER_ONLY', if_true: libuser) libsystem_ss = libsystem_ss.apply({}) libsystem = static_library('system', libsystem_ss.sources() + genh, - c_args: '-DCONFIG_SOFTMMU', + c_args: ['-DCONFIG_SOFTMMU', + '-DCOMPILING_SYSTEM_VS_USER'], dependencies: libsystem_ss.dependencies(), build_by_default: false) libsystem = declare_dependency(objects: libsystem.extract_all_objects(recursive: false), From 4d43552abe5c20ab414c054dd591bb6777832355 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 20 Mar 2025 15:29:34 -0700 Subject: [PATCH 0141/2760] exec/cpu-all: extract tlb flags defines to exec/tlb-flags.h Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250320223002.2915728-3-pierrick.bouvier@linaro.org> --- accel/tcg/cputlb.c | 1 + accel/tcg/user-exec.c | 1 + include/exec/cpu-all.h | 63 -------------------- include/exec/tlb-flags.h | 89 ++++++++++++++++++++++++++++ semihosting/uaccess.c | 1 + target/arm/ptw.c | 1 + target/arm/tcg/helper-a64.c | 1 + target/arm/tcg/mte_helper.c | 1 + target/arm/tcg/sve_helper.c | 1 + target/i386/tcg/system/excp_helper.c | 1 + target/riscv/op_helper.c | 1 + target/riscv/vector_helper.c | 1 + target/s390x/tcg/mem_helper.c | 1 + target/sparc/mmu_helper.c | 1 + 14 files changed, 101 insertions(+), 63 deletions(-) create mode 100644 include/exec/tlb-flags.h diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 613f919fff..b2db49e305 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -34,6 +34,7 @@ #include "qemu/error-report.h" #include "exec/log.h" #include "exec/helper-proto-common.h" +#include "exec/tlb-flags.h" #include "qemu/atomic.h" #include "qemu/atomic128.h" #include "tb-internal.h" diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index ebc7c3ecf5..667c5e0354 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -21,6 +21,7 @@ #include "disas/disas.h" #include "exec/vaddr.h" #include "exec/exec-all.h" +#include "exec/tlb-flags.h" #include "tcg/tcg.h" #include "qemu/bitops.h" #include "qemu/rcu.h" diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 4395fd08af..5c4379f0d0 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -35,69 +35,6 @@ CPUArchState *cpu_copy(CPUArchState *env); #include "cpu.h" -#ifdef CONFIG_USER_ONLY - -/* - * Allow some level of source compatibility with softmmu. We do not - * support any of the more exotic features, so only invalid pages may - * be signaled by probe_access_flags(). - */ -#define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1)) -#define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 2)) -#define TLB_WATCHPOINT 0 - -#else - -/* - * Flags stored in the low bits of the TLB virtual address. - * These are defined so that fast path ram access is all zeros. - * The flags all must be between TARGET_PAGE_BITS and - * maximum address alignment bit. - * - * Use TARGET_PAGE_BITS_MIN so that these bits are constant - * when TARGET_PAGE_BITS_VARY is in effect. - * - * The count, if not the placement of these bits is known - * to tcg/tcg-op-ldst.c, check_max_alignment(). - */ -/* Zero if TLB entry is valid. */ -#define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1)) -/* Set if TLB entry references a clean RAM page. The iotlb entry will - contain the page physical address. */ -#define TLB_NOTDIRTY (1 << (TARGET_PAGE_BITS_MIN - 2)) -/* Set if TLB entry is an IO callback. */ -#define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 3)) -/* Set if TLB entry writes ignored. */ -#define TLB_DISCARD_WRITE (1 << (TARGET_PAGE_BITS_MIN - 4)) -/* Set if the slow path must be used; more flags in CPUTLBEntryFull. */ -#define TLB_FORCE_SLOW (1 << (TARGET_PAGE_BITS_MIN - 5)) - -/* - * Use this mask to check interception with an alignment mask - * in a TCG backend. - */ -#define TLB_FLAGS_MASK \ - (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_MMIO \ - | TLB_FORCE_SLOW | TLB_DISCARD_WRITE) - -/* - * Flags stored in CPUTLBEntryFull.slow_flags[x]. - * TLB_FORCE_SLOW must be set in CPUTLBEntry.addr_idx[x]. - */ -/* Set if TLB entry requires byte swap. */ -#define TLB_BSWAP (1 << 0) -/* Set if TLB entry contains a watchpoint. */ -#define TLB_WATCHPOINT (1 << 1) -/* Set if TLB entry requires aligned accesses. */ -#define TLB_CHECK_ALIGNED (1 << 2) - -#define TLB_SLOW_FLAGS_MASK (TLB_BSWAP | TLB_WATCHPOINT | TLB_CHECK_ALIGNED) - -/* The two sets of flags must not overlap. */ -QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & TLB_SLOW_FLAGS_MASK); - -#endif /* !CONFIG_USER_ONLY */ - /* Validate correct placement of CPUArchState. */ QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0); QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); diff --git a/include/exec/tlb-flags.h b/include/exec/tlb-flags.h new file mode 100644 index 0000000000..a0e51a4b37 --- /dev/null +++ b/include/exec/tlb-flags.h @@ -0,0 +1,89 @@ +/* + * TLB flags definition + * + * Copyright (c) 2003 Fabrice Bellard + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see . + */ +#ifndef TLB_FLAGS_H +#define TLB_FLAGS_H + +#include "exec/cpu-defs.h" + +#ifdef CONFIG_USER_ONLY + +/* + * Allow some level of source compatibility with softmmu. We do not + * support any of the more exotic features, so only invalid pages may + * be signaled by probe_access_flags(). + */ +#define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1)) +#define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 2)) +#define TLB_WATCHPOINT 0 + +#else + +/* + * Flags stored in the low bits of the TLB virtual address. + * These are defined so that fast path ram access is all zeros. + * The flags all must be between TARGET_PAGE_BITS and + * maximum address alignment bit. + * + * Use TARGET_PAGE_BITS_MIN so that these bits are constant + * when TARGET_PAGE_BITS_VARY is in effect. + * + * The count, if not the placement of these bits is known + * to tcg/tcg-op-ldst.c, check_max_alignment(). + */ +/* Zero if TLB entry is valid. */ +#define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1)) +/* + * Set if TLB entry references a clean RAM page. The iotlb entry will + * contain the page physical address. + */ +#define TLB_NOTDIRTY (1 << (TARGET_PAGE_BITS_MIN - 2)) +/* Set if TLB entry is an IO callback. */ +#define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 3)) +/* Set if TLB entry writes ignored. */ +#define TLB_DISCARD_WRITE (1 << (TARGET_PAGE_BITS_MIN - 4)) +/* Set if the slow path must be used; more flags in CPUTLBEntryFull. */ +#define TLB_FORCE_SLOW (1 << (TARGET_PAGE_BITS_MIN - 5)) + +/* + * Use this mask to check interception with an alignment mask + * in a TCG backend. + */ +#define TLB_FLAGS_MASK \ + (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_MMIO \ + | TLB_FORCE_SLOW | TLB_DISCARD_WRITE) + +/* + * Flags stored in CPUTLBEntryFull.slow_flags[x]. + * TLB_FORCE_SLOW must be set in CPUTLBEntry.addr_idx[x]. + */ +/* Set if TLB entry requires byte swap. */ +#define TLB_BSWAP (1 << 0) +/* Set if TLB entry contains a watchpoint. */ +#define TLB_WATCHPOINT (1 << 1) +/* Set if TLB entry requires aligned accesses. */ +#define TLB_CHECK_ALIGNED (1 << 2) + +#define TLB_SLOW_FLAGS_MASK (TLB_BSWAP | TLB_WATCHPOINT | TLB_CHECK_ALIGNED) + +/* The two sets of flags must not overlap. */ +QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & TLB_SLOW_FLAGS_MASK); + +#endif /* !CONFIG_USER_ONLY */ + +#endif /* TLB_FLAGS_H */ diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index 2e33596428..ccb0c96070 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -11,6 +11,7 @@ #include "exec/cpu-all.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/exec-all.h" +#include "exec/tlb-flags.h" #include "semihosting/uaccess.h" void *uaccess_lock_user(CPUArchState *env, target_ulong addr, diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 4330900348..8d4e9e07a9 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -12,6 +12,7 @@ #include "qemu/main-loop.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/tlb-flags.h" #include "cpu.h" #include "internals.h" #include "cpu-features.h" diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 9244848efe..fa79d19425 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -31,6 +31,7 @@ #include "exec/cpu-common.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" +#include "exec/tlb-flags.h" #include "qemu/int128.h" #include "qemu/atomic128.h" #include "fpu/softfloat.h" diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 80164a8050..888c670754 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -31,6 +31,7 @@ #endif #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" +#include "exec/tlb-flags.h" #include "accel/tcg/cpu-ops.h" #include "qapi/error.h" #include "qemu/guest-random.h" diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index d786b4b111..e3bed77b48 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -23,6 +23,7 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/helper-proto.h" +#include "exec/tlb-flags.h" #include "tcg/tcg-gvec-desc.h" #include "fpu/softfloat.h" #include "tcg/tcg.h" diff --git a/target/i386/tcg/system/excp_helper.c b/target/i386/tcg/system/excp_helper.c index 6876329de2..b0b74df72f 100644 --- a/target/i386/tcg/system/excp_helper.c +++ b/target/i386/tcg/system/excp_helper.c @@ -22,6 +22,7 @@ #include "exec/cpu_ldst.h" #include "exec/cputlb.h" #include "exec/page-protection.h" +#include "exec/tlb-flags.h" #include "tcg/helper-tcg.h" typedef struct TranslateParams { diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 72dc48e58d..f3d26b6b95 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -25,6 +25,7 @@ #include "exec/cputlb.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" +#include "exec/tlb-flags.h" #include "trace.h" /* Exceptions processing helpers */ diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 67b3bafebb..83978be060 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -25,6 +25,7 @@ #include "exec/cpu_ldst.h" #include "exec/page-protection.h" #include "exec/helper-proto.h" +#include "exec/tlb-flags.h" #include "fpu/softfloat.h" #include "tcg/tcg-gvec-desc.h" #include "internals.h" diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 8187b917ba..0ff2e10d81 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -29,6 +29,7 @@ #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" +#include "exec/tlb-flags.h" #include "accel/tcg/cpu-ops.h" #include "qemu/int128.h" #include "qemu/atomic128.h" diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index 78cb24a8e2..249b1f6c4c 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -23,6 +23,7 @@ #include "exec/cputlb.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/page-protection.h" +#include "exec/tlb-flags.h" #include "qemu/qemu-print.h" #include "trace.h" From 31d399ff385498816e597bcc72de0f6efc36051e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Mar 2025 19:47:32 -0700 Subject: [PATCH 0142/2760] accel/tcg: Fix argument types of tlb_reset_dirty MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The arguments to tlb_reset_dirty are host pointers. The conversion from ram_addr_t was done in the sole caller, tlb_reset_dirty_range_all. Fixes: e554861766d ("exec: prepare for splitting") Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 6 +++--- include/exec/cputlb.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index b2db49e305..10090067f7 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -912,7 +912,7 @@ static inline void copy_tlb_helper_locked(CPUTLBEntry *d, const CPUTLBEntry *s) * We must take tlb_c.lock to avoid racing with another vCPU update. The only * thing actually updated is the target TLB entry ->addr_write flags. */ -void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length) +void tlb_reset_dirty(CPUState *cpu, uintptr_t start, uintptr_t length) { int mmu_idx; @@ -923,12 +923,12 @@ void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length) for (i = 0; i < n; i++) { tlb_reset_dirty_range_locked(&cpu->neg.tlb.f[mmu_idx].table[i], - start1, length); + start, length); } for (i = 0; i < CPU_VTLB_SIZE; i++) { tlb_reset_dirty_range_locked(&cpu->neg.tlb.d[mmu_idx].vtable[i], - start1, length); + start, length); } } qemu_spin_unlock(&cpu->neg.tlb.c.lock); diff --git a/include/exec/cputlb.h b/include/exec/cputlb.h index 8125f6809c..03ed7e2165 100644 --- a/include/exec/cputlb.h +++ b/include/exec/cputlb.h @@ -31,7 +31,7 @@ void tlb_unprotect_code(ram_addr_t ram_addr); #endif #ifndef CONFIG_USER_ONLY -void tlb_reset_dirty(CPUState *cpu, ram_addr_t start1, ram_addr_t length); +void tlb_reset_dirty(CPUState *cpu, uintptr_t start, uintptr_t length); void tlb_reset_dirty_range_all(ram_addr_t start, ram_addr_t length); #endif From 970354edc09a0ede505cea117b92e71330f1ed53 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Mar 2025 20:10:42 -0700 Subject: [PATCH 0143/2760] accel/tcg: Pass CPUTLBEntryFull to tlb_reset_dirty_range_locked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While we're renaming things, don't modify addr; save it for reuse in the qatomic_set. Compute the host address into a new local variable. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 10090067f7..5df98d93d0 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -882,18 +882,16 @@ void tlb_unprotect_code(ram_addr_t ram_addr) * * Called with tlb_c.lock held. */ -static void tlb_reset_dirty_range_locked(CPUTLBEntry *tlb_entry, +static void tlb_reset_dirty_range_locked(CPUTLBEntryFull *full, CPUTLBEntry *ent, uintptr_t start, uintptr_t length) { - uintptr_t addr = tlb_entry->addr_write; + const uintptr_t addr = ent->addr_write; if ((addr & (TLB_INVALID_MASK | TLB_MMIO | TLB_DISCARD_WRITE | TLB_NOTDIRTY)) == 0) { - addr &= TARGET_PAGE_MASK; - addr += tlb_entry->addend; - if ((addr - start) < length) { - qatomic_set(&tlb_entry->addr_write, - tlb_entry->addr_write | TLB_NOTDIRTY); + uintptr_t host = (addr & TARGET_PAGE_MASK) + ent->addend; + if ((host - start) < length) { + qatomic_set(&ent->addr_write, addr | TLB_NOTDIRTY); } } } @@ -918,16 +916,18 @@ void tlb_reset_dirty(CPUState *cpu, uintptr_t start, uintptr_t length) qemu_spin_lock(&cpu->neg.tlb.c.lock); for (mmu_idx = 0; mmu_idx < NB_MMU_MODES; mmu_idx++) { + CPUTLBDesc *desc = &cpu->neg.tlb.d[mmu_idx]; + CPUTLBDescFast *fast = &cpu->neg.tlb.f[mmu_idx]; + unsigned int n = tlb_n_entries(fast); unsigned int i; - unsigned int n = tlb_n_entries(&cpu->neg.tlb.f[mmu_idx]); for (i = 0; i < n; i++) { - tlb_reset_dirty_range_locked(&cpu->neg.tlb.f[mmu_idx].table[i], + tlb_reset_dirty_range_locked(&desc->fulltlb[i], &fast->table[i], start, length); } for (i = 0; i < CPU_VTLB_SIZE; i++) { - tlb_reset_dirty_range_locked(&cpu->neg.tlb.d[mmu_idx].vtable[i], + tlb_reset_dirty_range_locked(&desc->vfulltlb[i], &desc->vtable[i], start, length); } } From f05d251906a335465a5c5b3a6ffcdce96eca54f4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Mar 2025 20:21:23 -0700 Subject: [PATCH 0144/2760] accel/tcg: Rebuild full flags in tlb_reset_dirty_range_locked Undo the split between inline and slow flags before masking. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 5df98d93d0..28c47d4872 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -886,9 +886,10 @@ static void tlb_reset_dirty_range_locked(CPUTLBEntryFull *full, CPUTLBEntry *ent uintptr_t start, uintptr_t length) { const uintptr_t addr = ent->addr_write; + int flags = addr | full->slow_flags[MMU_DATA_STORE]; - if ((addr & (TLB_INVALID_MASK | TLB_MMIO | - TLB_DISCARD_WRITE | TLB_NOTDIRTY)) == 0) { + flags &= TLB_INVALID_MASK | TLB_MMIO | TLB_DISCARD_WRITE | TLB_NOTDIRTY; + if (flags == 0) { uintptr_t host = (addr & TARGET_PAGE_MASK) + ent->addend; if ((host - start) < length) { qatomic_set(&ent->addr_write, addr | TLB_NOTDIRTY); From 24b5e0fdb543a09c26d6d77051b17055288bef8e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 25 Mar 2025 20:22:14 -0700 Subject: [PATCH 0145/2760] include/exec: Move TLB_MMIO, TLB_DISCARD_WRITE to slow flags Recover two bits from the inline flags. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/exec/tlb-flags.h | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/include/exec/tlb-flags.h b/include/exec/tlb-flags.h index a0e51a4b37..54a6bae768 100644 --- a/include/exec/tlb-flags.h +++ b/include/exec/tlb-flags.h @@ -53,20 +53,15 @@ * contain the page physical address. */ #define TLB_NOTDIRTY (1 << (TARGET_PAGE_BITS_MIN - 2)) -/* Set if TLB entry is an IO callback. */ -#define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 3)) -/* Set if TLB entry writes ignored. */ -#define TLB_DISCARD_WRITE (1 << (TARGET_PAGE_BITS_MIN - 4)) /* Set if the slow path must be used; more flags in CPUTLBEntryFull. */ -#define TLB_FORCE_SLOW (1 << (TARGET_PAGE_BITS_MIN - 5)) +#define TLB_FORCE_SLOW (1 << (TARGET_PAGE_BITS_MIN - 3)) /* * Use this mask to check interception with an alignment mask * in a TCG backend. */ #define TLB_FLAGS_MASK \ - (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_MMIO \ - | TLB_FORCE_SLOW | TLB_DISCARD_WRITE) + (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_FORCE_SLOW) /* * Flags stored in CPUTLBEntryFull.slow_flags[x]. @@ -78,8 +73,14 @@ #define TLB_WATCHPOINT (1 << 1) /* Set if TLB entry requires aligned accesses. */ #define TLB_CHECK_ALIGNED (1 << 2) +/* Set if TLB entry writes ignored. */ +#define TLB_DISCARD_WRITE (1 << 3) +/* Set if TLB entry is an IO callback. */ +#define TLB_MMIO (1 << 4) -#define TLB_SLOW_FLAGS_MASK (TLB_BSWAP | TLB_WATCHPOINT | TLB_CHECK_ALIGNED) +#define TLB_SLOW_FLAGS_MASK \ + (TLB_BSWAP | TLB_WATCHPOINT | TLB_CHECK_ALIGNED | \ + TLB_DISCARD_WRITE | TLB_MMIO) /* The two sets of flags must not overlap. */ QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & TLB_SLOW_FLAGS_MASK); From a456c72695e59589d88100ca7b3448817a6fa953 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Mar 2025 16:11:09 -0500 Subject: [PATCH 0146/2760] include/exec: Move tb_{,set_}page_addr[01] to translation-block.h Move the accessor functions for TranslationBlock into the header related to the structure. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/exec/exec-all.h | 49 ------------------------------- include/exec/translation-block.h | 50 ++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 49 deletions(-) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 19b0eda44a..fcad3446fe 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -123,55 +123,6 @@ int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, #endif /* !CONFIG_USER_ONLY */ #endif /* CONFIG_TCG */ -static inline tb_page_addr_t tb_page_addr0(const TranslationBlock *tb) -{ -#ifdef CONFIG_USER_ONLY - return tb->itree.start; -#else - return tb->page_addr[0]; -#endif -} - -static inline tb_page_addr_t tb_page_addr1(const TranslationBlock *tb) -{ -#ifdef CONFIG_USER_ONLY - tb_page_addr_t next = tb->itree.last & TARGET_PAGE_MASK; - return next == (tb->itree.start & TARGET_PAGE_MASK) ? -1 : next; -#else - return tb->page_addr[1]; -#endif -} - -static inline void tb_set_page_addr0(TranslationBlock *tb, - tb_page_addr_t addr) -{ -#ifdef CONFIG_USER_ONLY - tb->itree.start = addr; - /* - * To begin, we record an interval of one byte. When the translation - * loop encounters a second page, the interval will be extended to - * include the first byte of the second page, which is sufficient to - * allow tb_page_addr1() above to work properly. The final corrected - * interval will be set by tb_page_add() from tb->size before the - * node is added to the interval tree. - */ - tb->itree.last = addr; -#else - tb->page_addr[0] = addr; -#endif -} - -static inline void tb_set_page_addr1(TranslationBlock *tb, - tb_page_addr_t addr) -{ -#ifdef CONFIG_USER_ONLY - /* Extend the interval to the first byte of the second page. See above. */ - tb->itree.last = addr; -#else - tb->page_addr[1] = addr; -#endif -} - /* TranslationBlock invalidate API */ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last); diff --git a/include/exec/translation-block.h b/include/exec/translation-block.h index 3c69bc71a9..8b8e730561 100644 --- a/include/exec/translation-block.h +++ b/include/exec/translation-block.h @@ -13,6 +13,7 @@ #include "exec/vaddr.h" #ifdef CONFIG_USER_ONLY #include "qemu/interval-tree.h" +#include "exec/target_page.h" #endif /* @@ -157,4 +158,53 @@ static inline uint32_t tb_cflags(const TranslationBlock *tb) bool tcg_cflags_has(CPUState *cpu, uint32_t flags); void tcg_cflags_set(CPUState *cpu, uint32_t flags); +static inline tb_page_addr_t tb_page_addr0(const TranslationBlock *tb) +{ +#ifdef CONFIG_USER_ONLY + return tb->itree.start; +#else + return tb->page_addr[0]; +#endif +} + +static inline tb_page_addr_t tb_page_addr1(const TranslationBlock *tb) +{ +#ifdef CONFIG_USER_ONLY + tb_page_addr_t next = tb->itree.last & TARGET_PAGE_MASK; + return next == (tb->itree.start & TARGET_PAGE_MASK) ? -1 : next; +#else + return tb->page_addr[1]; +#endif +} + +static inline void tb_set_page_addr0(TranslationBlock *tb, + tb_page_addr_t addr) +{ +#ifdef CONFIG_USER_ONLY + tb->itree.start = addr; + /* + * To begin, we record an interval of one byte. When the translation + * loop encounters a second page, the interval will be extended to + * include the first byte of the second page, which is sufficient to + * allow tb_page_addr1() above to work properly. The final corrected + * interval will be set by tb_page_add() from tb->size before the + * node is added to the interval tree. + */ + tb->itree.last = addr; +#else + tb->page_addr[0] = addr; +#endif +} + +static inline void tb_set_page_addr1(TranslationBlock *tb, + tb_page_addr_t addr) +{ +#ifdef CONFIG_USER_ONLY + /* Extend the interval to the first byte of the second page. See above. */ + tb->itree.last = addr; +#else + tb->page_addr[1] = addr; +#endif +} + #endif /* EXEC_TRANSLATION_BLOCK_H */ From aa6f138abada97176598c89a9e4e3a7c3d47cb1e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Mar 2025 16:28:08 -0500 Subject: [PATCH 0147/2760] accel/tcg: Move get_page_addr_code* declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the declarations from exec/exec-all.h to the private accel/tcg/internal-common.h. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/internal-common.h | 34 ++++++++++++++++++++++++++++++++++ accel/tcg/translator.c | 1 + include/exec/exec-all.h | 34 ---------------------------------- 3 files changed, 35 insertions(+), 34 deletions(-) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index 9b6ab3a8cc..2f00560d10 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -74,4 +74,38 @@ uint32_t curr_cflags(CPUState *cpu); void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr); +/** + * get_page_addr_code_hostp() + * @env: CPUArchState + * @addr: guest virtual address of guest code + * + * See get_page_addr_code() (full-system version) for documentation on the + * return value. + * + * Sets *@hostp (when @hostp is non-NULL) as follows. + * If the return value is -1, sets *@hostp to NULL. Otherwise, sets *@hostp + * to the host address where @addr's content is kept. + * + * Note: this function can trigger an exception. + */ +tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, + void **hostp); + +/** + * get_page_addr_code() + * @env: CPUArchState + * @addr: guest virtual address of guest code + * + * If we cannot translate and execute from the entire RAM page, or if + * the region is not backed by RAM, returns -1. Otherwise, returns the + * ram_addr_t corresponding to the guest code at @addr. + * + * Note: this function can trigger an exception. + */ +static inline tb_page_addr_t get_page_addr_code(CPUArchState *env, + vaddr addr) +{ + return get_page_addr_code_hostp(env, addr, NULL); +} + #endif diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 2ab081b95f..5f0aa9d56a 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -17,6 +17,7 @@ #include "exec/translator.h" #include "exec/plugin-gen.h" #include "tcg/tcg-op-common.h" +#include "internal-common.h" #include "internal-target.h" #include "disas/disas.h" #include "tb-internal.h" diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index fcad3446fe..f52a680f42 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -143,40 +143,6 @@ struct MemoryRegionSection *iotlb_to_section(CPUState *cpu, hwaddr index, MemTxAttrs attrs); #endif -/** - * get_page_addr_code_hostp() - * @env: CPUArchState - * @addr: guest virtual address of guest code - * - * See get_page_addr_code() (full-system version) for documentation on the - * return value. - * - * Sets *@hostp (when @hostp is non-NULL) as follows. - * If the return value is -1, sets *@hostp to NULL. Otherwise, sets *@hostp - * to the host address where @addr's content is kept. - * - * Note: this function can trigger an exception. - */ -tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, - void **hostp); - -/** - * get_page_addr_code() - * @env: CPUArchState - * @addr: guest virtual address of guest code - * - * If we cannot translate and execute from the entire RAM page, or if - * the region is not backed by RAM, returns -1. Otherwise, returns the - * ram_addr_t corresponding to the guest code at @addr. - * - * Note: this function can trigger an exception. - */ -static inline tb_page_addr_t get_page_addr_code(CPUArchState *env, - vaddr addr) -{ - return get_page_addr_code_hostp(env, addr, NULL); -} - #if !defined(CONFIG_USER_ONLY) MemoryRegionSection * From b103cc6e74ac92f070a0e004bd84334e845c20b5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Mar 2025 16:48:09 -0500 Subject: [PATCH 0148/2760] accel/tcg: Remove page_protect Merge the user-only page_protect function with the user-only implementation of tb_lock_page0. This avoids pulling page-protection.h into tb-internal.h. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tb-internal.h | 11 +++-------- accel/tcg/user-exec.c | 2 +- include/user/page-protection.h | 1 - 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/accel/tcg/tb-internal.h b/accel/tcg/tb-internal.h index 68aa8d17f4..f7c2073e29 100644 --- a/accel/tcg/tb-internal.h +++ b/accel/tcg/tb-internal.h @@ -51,28 +51,23 @@ #endif /* CONFIG_SOFTMMU */ +void tb_lock_page0(tb_page_addr_t); + #ifdef CONFIG_USER_ONLY -#include "user/page-protection.h" /* * For user-only, page_protect sets the page read-only. * Since most execution is already on read-only pages, and we'd need to * account for other TBs on the same page, defer undoing any page protection * until we receive the write fault. */ -static inline void tb_lock_page0(tb_page_addr_t p0) -{ - page_protect(p0); -} - static inline void tb_lock_page1(tb_page_addr_t p0, tb_page_addr_t p1) { - page_protect(p1); + tb_lock_page0(p1); } static inline void tb_unlock_page1(tb_page_addr_t p0, tb_page_addr_t p1) { } static inline void tb_unlock_pages(TranslationBlock *tb) { } #else -void tb_lock_page0(tb_page_addr_t); void tb_lock_page1(tb_page_addr_t, tb_page_addr_t); void tb_unlock_page1(tb_page_addr_t, tb_page_addr_t); void tb_unlock_pages(TranslationBlock *); diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 667c5e0354..72a9809c2d 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -657,7 +657,7 @@ target_ulong page_find_range_empty(target_ulong min, target_ulong max, } } -void page_protect(tb_page_addr_t address) +void tb_lock_page0(tb_page_addr_t address) { PageFlagsNode *p; target_ulong start, last; diff --git a/include/user/page-protection.h b/include/user/page-protection.h index 51daa18648..d5c8748d49 100644 --- a/include/user/page-protection.h +++ b/include/user/page-protection.h @@ -16,7 +16,6 @@ #include "exec/target_long.h" #include "exec/translation-block.h" -void page_protect(tb_page_addr_t page_addr); int page_unprotect(tb_page_addr_t address, uintptr_t pc); int page_get_flags(target_ulong address); From 7b18e3a727f46771f41eb764c2b40cebca81c3de Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Mar 2025 17:07:09 -0500 Subject: [PATCH 0149/2760] accel/tcg: Remove cpu-all.h, exec-all.h from tb-internal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Not used by tb-internal.h, but add an include for target_page.h in tb-maint.c. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tb-internal.h | 2 -- accel/tcg/tb-maint.c | 1 + 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/accel/tcg/tb-internal.h b/accel/tcg/tb-internal.h index f7c2073e29..f9a06bcbab 100644 --- a/accel/tcg/tb-internal.h +++ b/accel/tcg/tb-internal.h @@ -9,8 +9,6 @@ #ifndef ACCEL_TCG_TB_INTERNAL_TARGET_H #define ACCEL_TCG_TB_INTERNAL_TARGET_H -#include "exec/cpu-all.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" /* diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index d5899ad047..df3438e190 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -26,6 +26,7 @@ #include "exec/page-protection.h" #include "exec/mmap-lock.h" #include "exec/tb-flush.h" +#include "exec/target_page.h" #include "tb-internal.h" #include "system/tcg.h" #include "tcg/tcg.h" From f3ad026e359971729539c6af799859b003f81b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 09:20:52 +0200 Subject: [PATCH 0150/2760] target/rx: Fix copy/paste typo (riscv -> rx) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename riscv_cpu_mmu_index() -> rx_cpu_mmu_index(). Fixes: ef5cc166da1 ("target/rx: Populate CPUClass.mmu_index") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401072052.25892-1-philmd@linaro.org> --- target/rx/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 0ba0d55ab5..a240b3b3ce 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -66,7 +66,7 @@ static bool rx_cpu_has_work(CPUState *cs) (CPU_INTERRUPT_HARD | CPU_INTERRUPT_FIR); } -static int riscv_cpu_mmu_index(CPUState *cs, bool ifunc) +static int rx_cpu_mmu_index(CPUState *cs, bool ifunc) { return 0; } @@ -227,7 +227,7 @@ static void rx_cpu_class_init(ObjectClass *klass, void *data) &rcc->parent_phases); cc->class_by_name = rx_cpu_class_by_name; - cc->mmu_index = riscv_cpu_mmu_index; + cc->mmu_index = rx_cpu_mmu_index; cc->dump_state = rx_cpu_dump_state; cc->set_pc = rx_cpu_set_pc; cc->get_pc = rx_cpu_get_pc; From 6ca6310699f5a295115e090c489d411fd219dcf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:14 +0200 Subject: [PATCH 0151/2760] hw/core/cpu: Update CPUClass::mmu_index docstring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commits 32a8ea12fab..90b7022e698 (target: "Split out TARGET_env_mmu_index"), target's memory_rw_debug() callbacks use the target's TARGET_env_mmu_index(), not the generic CPUClass::mmu_index() callback. Update the documentation. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-2-philmd@linaro.org> --- include/hw/core/cpu.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 6dcee5d0ba..29f6419050 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -104,8 +104,7 @@ struct SysemuCPUOps; * instantiatable CPU type. * @parse_features: Callback to parse command line arguments. * @reset_dump_flags: #CPUDumpFlags to use for reset logging. - * @mmu_index: Callback for choosing softmmu mmu index; - * may be used internally by memory_rw_debug without TCG. + * @mmu_index: Callback for choosing softmmu mmu index. * @memory_rw_debug: Callback for GDB memory access. * @dump_state: Callback for dumping state. * @query_cpu_fast: From 17fa8b6f039d79e91884ecc1af65a3e771536ca3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:15 +0200 Subject: [PATCH 0152/2760] accel/tcg: Introduce TCGCPUOps::mmu_index() callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We'll move CPUClass::mmu_index() to TCGCPUOps::mmu_index(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-3-philmd@linaro.org> --- include/accel/tcg/cpu-mmu-index.h | 5 ++++- include/accel/tcg/cpu-ops.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/accel/tcg/cpu-mmu-index.h b/include/accel/tcg/cpu-mmu-index.h index 8d1cb53bfa..f1ca385d3c 100644 --- a/include/accel/tcg/cpu-mmu-index.h +++ b/include/accel/tcg/cpu-mmu-index.h @@ -10,6 +10,7 @@ #define ACCEL_TCG_CPU_MMU_INDEX_H #include "hw/core/cpu.h" +#include "accel/tcg/cpu-ops.h" #include "tcg/debug-assert.h" #ifdef COMPILING_PER_TARGET # ifdef CONFIG_USER_ONLY @@ -33,7 +34,9 @@ static inline int cpu_mmu_index(CPUState *cs, bool ifetch) # endif #endif - int ret = cs->cc->mmu_index(cs, ifetch); + const TCGCPUOps *tcg_ops = cs->cc->tcg_ops; + int ret = tcg_ops->mmu_index ? tcg_ops->mmu_index(cs, ifetch) + : cs->cc->mmu_index(cs, ifetch); tcg_debug_assert(ret >= 0 && ret < NB_MMU_MODES); return ret; } diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index f60e5303f2..106a0688da 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -67,6 +67,9 @@ struct TCGCPUOps { /** @debug_excp_handler: Callback for handling debug exceptions */ void (*debug_excp_handler)(CPUState *cpu); + /** @mmu_index: Callback for choosing softmmu mmu index */ + int (*mmu_index)(CPUState *cpu, bool ifetch); + #ifdef CONFIG_USER_ONLY /** * @fake_user_interrupt: Callback for 'fake exception' handling. From 6ea8bce7ee60e3f6fd14a959effe2cc6f3be7d94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:16 +0200 Subject: [PATCH 0153/2760] target/alpha: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-4-philmd@linaro.org> --- target/alpha/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 584c2aa76b..56c96b1c4d 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -238,6 +238,7 @@ static const TCGCPUOps alpha_tcg_ops = { .translate_code = alpha_translate_code, .synchronize_from_tb = alpha_cpu_synchronize_from_tb, .restore_state_to_opc = alpha_restore_state_to_opc, + .mmu_index = alpha_cpu_mmu_index, #ifdef CONFIG_USER_ONLY .record_sigsegv = alpha_cpu_record_sigsegv, @@ -262,7 +263,6 @@ static void alpha_cpu_class_init(ObjectClass *oc, void *data) &acc->parent_realize); cc->class_by_name = alpha_cpu_class_by_name; - cc->mmu_index = alpha_cpu_mmu_index; cc->dump_state = alpha_cpu_dump_state; cc->set_pc = alpha_cpu_set_pc; cc->get_pc = alpha_cpu_get_pc; From 61dc4d0da8ae2c7dc46f5d69ef063b64726f841f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:17 +0200 Subject: [PATCH 0154/2760] target/arm: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move arm_cpu_mmu_index() within CONFIG_TCG #ifdef'ry, convert CPUClass::mmu_index() to TCGCPUOps::mmu_index(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-5-philmd@linaro.org> --- target/arm/cpu.c | 13 +++++++------ target/arm/internals.h | 1 + target/arm/tcg/cpu-v7m.c | 1 + 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 01786ac787..21e8cf1400 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -121,6 +121,12 @@ void arm_restore_state_to_opc(CPUState *cs, env->exception.syndrome = data[2] << ARM_INSN_START_WORD2_SHIFT; } } + +int arm_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return arm_env_mmu_index(cpu_env(cs)); +} + #endif /* CONFIG_TCG */ #ifndef CONFIG_USER_ONLY @@ -144,11 +150,6 @@ static bool arm_cpu_has_work(CPUState *cs) } #endif /* !CONFIG_USER_ONLY */ -static int arm_cpu_mmu_index(CPUState *cs, bool ifetch) -{ - return arm_env_mmu_index(cpu_env(cs)); -} - void arm_register_pre_el_change_hook(ARMCPU *cpu, ARMELChangeHookFn *hook, void *opaque) { @@ -2674,6 +2675,7 @@ static const TCGCPUOps arm_tcg_ops = { .synchronize_from_tb = arm_cpu_synchronize_from_tb, .debug_excp_handler = arm_debug_excp_handler, .restore_state_to_opc = arm_restore_state_to_opc, + .mmu_index = arm_cpu_mmu_index, #ifdef CONFIG_USER_ONLY .record_sigsegv = arm_cpu_record_sigsegv, @@ -2708,7 +2710,6 @@ static void arm_cpu_class_init(ObjectClass *oc, void *data) &acc->parent_phases); cc->class_by_name = arm_cpu_class_by_name; - cc->mmu_index = arm_cpu_mmu_index; cc->dump_state = arm_cpu_dump_state; cc->set_pc = arm_cpu_set_pc; cc->get_pc = arm_cpu_get_pc; diff --git a/target/arm/internals.h b/target/arm/internals.h index 28585c0755..8756c24c08 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -373,6 +373,7 @@ void arm_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb); /* Our implementation of TCGCPUOps::cpu_exec_halt */ bool arm_cpu_exec_halt(CPUState *cs); +int arm_cpu_mmu_index(CPUState *cs, bool ifetch); #endif /* CONFIG_TCG */ typedef enum ARMFPRounding { diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index c4dd309272..1a913faa50 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -237,6 +237,7 @@ static const TCGCPUOps arm_v7m_tcg_ops = { .synchronize_from_tb = arm_cpu_synchronize_from_tb, .debug_excp_handler = arm_debug_excp_handler, .restore_state_to_opc = arm_restore_state_to_opc, + .mmu_index = arm_cpu_mmu_index, #ifdef CONFIG_USER_ONLY .record_sigsegv = arm_cpu_record_sigsegv, From 364f4633b7bc917ef9b2ecd307b42276a6582fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:18 +0200 Subject: [PATCH 0155/2760] target/avr: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-6-philmd@linaro.org> --- target/avr/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 1121822470..feb73e722b 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -228,6 +228,7 @@ static const TCGCPUOps avr_tcg_ops = { .translate_code = avr_cpu_translate_code, .synchronize_from_tb = avr_cpu_synchronize_from_tb, .restore_state_to_opc = avr_restore_state_to_opc, + .mmu_index = avr_cpu_mmu_index, .cpu_exec_interrupt = avr_cpu_exec_interrupt, .cpu_exec_halt = avr_cpu_has_work, .tlb_fill = avr_cpu_tlb_fill, @@ -250,7 +251,6 @@ static void avr_cpu_class_init(ObjectClass *oc, void *data) cc->class_by_name = avr_cpu_class_by_name; - cc->mmu_index = avr_cpu_mmu_index; cc->dump_state = avr_cpu_dump_state; cc->set_pc = avr_cpu_set_pc; cc->get_pc = avr_cpu_get_pc; From 7856232a93aa9b768d3dd09882192f8fbbc4d708 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:19 +0200 Subject: [PATCH 0156/2760] target/hppa: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-7-philmd@linaro.org> --- target/hppa/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 2a85495d02..09a6aaa3dd 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -256,6 +256,7 @@ static const TCGCPUOps hppa_tcg_ops = { .translate_code = hppa_translate_code, .synchronize_from_tb = hppa_cpu_synchronize_from_tb, .restore_state_to_opc = hppa_restore_state_to_opc, + .mmu_index = hppa_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill_align = hppa_cpu_tlb_fill_align, @@ -281,7 +282,6 @@ static void hppa_cpu_class_init(ObjectClass *oc, void *data) &acc->parent_phases); cc->class_by_name = hppa_cpu_class_by_name; - cc->mmu_index = hppa_cpu_mmu_index; cc->dump_state = hppa_cpu_dump_state; cc->set_pc = hppa_cpu_set_pc; cc->get_pc = hppa_cpu_get_pc; From c8db986959942edd7bbc2f3d193cacd7f85c145b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:20 +0200 Subject: [PATCH 0157/2760] target/i386: Remove unused cpu_(ldub, stb)_kernel macros MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-8-philmd@linaro.org> --- target/i386/tcg/seg_helper.h | 6 ------ 1 file changed, 6 deletions(-) diff --git a/target/i386/tcg/seg_helper.h b/target/i386/tcg/seg_helper.h index ebf1035277..6b8606cd6d 100644 --- a/target/i386/tcg/seg_helper.h +++ b/target/i386/tcg/seg_helper.h @@ -35,8 +35,6 @@ * TODO: Convert callers to compute cpu_mmu_index_kernel once * and use *_mmuidx_ra directly. */ -#define cpu_ldub_kernel_ra(e, p, r) \ - cpu_ldub_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) #define cpu_lduw_kernel_ra(e, p, r) \ cpu_lduw_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) #define cpu_ldl_kernel_ra(e, p, r) \ @@ -44,8 +42,6 @@ #define cpu_ldq_kernel_ra(e, p, r) \ cpu_ldq_mmuidx_ra(e, p, cpu_mmu_index_kernel(e), r) -#define cpu_stb_kernel_ra(e, p, v, r) \ - cpu_stb_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) #define cpu_stw_kernel_ra(e, p, v, r) \ cpu_stw_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) #define cpu_stl_kernel_ra(e, p, v, r) \ @@ -53,12 +49,10 @@ #define cpu_stq_kernel_ra(e, p, v, r) \ cpu_stq_mmuidx_ra(e, p, v, cpu_mmu_index_kernel(e), r) -#define cpu_ldub_kernel(e, p) cpu_ldub_kernel_ra(e, p, 0) #define cpu_lduw_kernel(e, p) cpu_lduw_kernel_ra(e, p, 0) #define cpu_ldl_kernel(e, p) cpu_ldl_kernel_ra(e, p, 0) #define cpu_ldq_kernel(e, p) cpu_ldq_kernel_ra(e, p, 0) -#define cpu_stb_kernel(e, p, v) cpu_stb_kernel_ra(e, p, v, 0) #define cpu_stw_kernel(e, p, v) cpu_stw_kernel_ra(e, p, v, 0) #define cpu_stl_kernel(e, p, v) cpu_stl_kernel_ra(e, p, v, 0) #define cpu_stq_kernel(e, p, v) cpu_stq_kernel_ra(e, p, v, 0) From 611c34a745ee7ab98733175458378f94e03c23d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:21 +0200 Subject: [PATCH 0158/2760] target/i386: Restrict cpu_mmu_index_kernel() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move cpu_mmu_index_kernel() to seg_helper.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-9-philmd@linaro.org> --- target/i386/cpu.c | 16 ---------------- target/i386/cpu.h | 1 - target/i386/tcg/seg_helper.c | 16 ++++++++++++++++ target/i386/tcg/seg_helper.h | 4 ++++ 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index c596e2174d..fd85663833 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8681,22 +8681,6 @@ static int x86_cpu_mmu_index(CPUState *cs, bool ifetch) return x86_mmu_index_pl(env, env->hflags & HF_CPL_MASK); } -static int x86_mmu_index_kernel_pl(CPUX86State *env, unsigned pl) -{ - int mmu_index_32 = (env->hflags & HF_LMA_MASK) ? 0 : 1; - int mmu_index_base = - !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP64_IDX : - (pl < 3 && (env->eflags & AC_MASK) - ? MMU_KNOSMAP64_IDX : MMU_KSMAP64_IDX); - - return mmu_index_base + mmu_index_32; -} - -int cpu_mmu_index_kernel(CPUX86State *env) -{ - return x86_mmu_index_kernel_pl(env, env->hflags & HF_CPL_MASK); -} - static void x86_disas_set_info(CPUState *cs, disassemble_info *info) { X86CPU *cpu = X86_CPU(cs); diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 119efc6c60..c9f39e99d3 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2598,7 +2598,6 @@ static inline bool is_mmu_index_32(int mmu_index) } int x86_mmu_index_pl(CPUX86State *env, unsigned pl); -int cpu_mmu_index_kernel(CPUX86State *env); #define CC_DST (env->cc_dst) #define CC_SRC (env->cc_src) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 71962113fb..f4370202fe 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -128,6 +128,22 @@ int get_pg_mode(CPUX86State *env) return pg_mode; } +static int x86_mmu_index_kernel_pl(CPUX86State *env, unsigned pl) +{ + int mmu_index_32 = (env->hflags & HF_LMA_MASK) ? 0 : 1; + int mmu_index_base = + !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP64_IDX : + (pl < 3 && (env->eflags & AC_MASK) + ? MMU_KNOSMAP64_IDX : MMU_KSMAP64_IDX); + + return mmu_index_base + mmu_index_32; +} + +int cpu_mmu_index_kernel(CPUX86State *env) +{ + return x86_mmu_index_kernel_pl(env, env->hflags & HF_CPL_MASK); +} + /* return non zero if error */ static inline int load_segment_ra(CPUX86State *env, uint32_t *e1_ptr, uint32_t *e2_ptr, int selector, diff --git a/target/i386/tcg/seg_helper.h b/target/i386/tcg/seg_helper.h index 6b8606cd6d..ea98e1a98e 100644 --- a/target/i386/tcg/seg_helper.h +++ b/target/i386/tcg/seg_helper.h @@ -20,6 +20,8 @@ #ifndef SEG_HELPER_H #define SEG_HELPER_H +#include "cpu.h" + //#define DEBUG_PCALL #ifdef DEBUG_PCALL @@ -31,6 +33,8 @@ # define LOG_PCALL_STATE(cpu) do { } while (0) #endif +int cpu_mmu_index_kernel(CPUX86State *env); + /* * TODO: Convert callers to compute cpu_mmu_index_kernel once * and use *_mmuidx_ra directly. From 8480f7c7454de9527e14eaa2430835684eb03bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:22 +0200 Subject: [PATCH 0159/2760] target/i386: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move x86_cpu_mmu_index() to tcg-cpu.c, convert CPUClass::mmu_index() to TCGCPUOps::mmu_index(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-10-philmd@linaro.org> --- target/i386/cpu.c | 18 ------------------ target/i386/cpu.h | 2 -- target/i386/tcg/seg_helper.c | 1 + target/i386/tcg/tcg-cpu.c | 18 ++++++++++++++++++ target/i386/tcg/tcg-cpu.h | 2 ++ 5 files changed, 21 insertions(+), 20 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index fd85663833..57f62cc869 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8664,23 +8664,6 @@ static bool x86_cpu_has_work(CPUState *cs) } #endif /* !CONFIG_USER_ONLY */ -int x86_mmu_index_pl(CPUX86State *env, unsigned pl) -{ - int mmu_index_32 = (env->hflags & HF_CS64_MASK) ? 0 : 1; - int mmu_index_base = - pl == 3 ? MMU_USER64_IDX : - !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP64_IDX : - (env->eflags & AC_MASK) ? MMU_KNOSMAP64_IDX : MMU_KSMAP64_IDX; - - return mmu_index_base + mmu_index_32; -} - -static int x86_cpu_mmu_index(CPUState *cs, bool ifetch) -{ - CPUX86State *env = cpu_env(cs); - return x86_mmu_index_pl(env, env->hflags & HF_CPL_MASK); -} - static void x86_disas_set_info(CPUState *cs, disassemble_info *info) { X86CPU *cpu = X86_CPU(cs); @@ -8922,7 +8905,6 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) cc->class_by_name = x86_cpu_class_by_name; cc->parse_features = x86_cpu_parse_featurestr; - cc->mmu_index = x86_cpu_mmu_index; cc->dump_state = x86_cpu_dump_state; cc->set_pc = x86_cpu_set_pc; cc->get_pc = x86_cpu_get_pc; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c9f39e99d3..0ad67fe0fd 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2597,8 +2597,6 @@ static inline bool is_mmu_index_32(int mmu_index) return mmu_index & 1; } -int x86_mmu_index_pl(CPUX86State *env, unsigned pl); - #define CC_DST (env->cc_dst) #define CC_SRC (env->cc_src) #define CC_SRC2 (env->cc_src2) diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index f4370202fe..9dfbc4208c 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -28,6 +28,7 @@ #include "helper-tcg.h" #include "seg_helper.h" #include "access.h" +#include "tcg-cpu.h" #ifdef TARGET_X86_64 #define SET_ESP(val, sp_mask) \ diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index b8aff825ee..818653ee6d 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -94,6 +94,23 @@ static void x86_restore_state_to_opc(CPUState *cs, } } +int x86_mmu_index_pl(CPUX86State *env, unsigned pl) +{ + int mmu_index_32 = (env->hflags & HF_CS64_MASK) ? 0 : 1; + int mmu_index_base = + pl == 3 ? MMU_USER64_IDX : + !(env->hflags & HF_SMAP_MASK) ? MMU_KNOSMAP64_IDX : + (env->eflags & AC_MASK) ? MMU_KNOSMAP64_IDX : MMU_KSMAP64_IDX; + + return mmu_index_base + mmu_index_32; +} + +static int x86_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + CPUX86State *env = cpu_env(cs); + return x86_mmu_index_pl(env, env->hflags & HF_CPL_MASK); +} + #ifndef CONFIG_USER_ONLY static bool x86_debug_check_breakpoint(CPUState *cs) { @@ -112,6 +129,7 @@ static const TCGCPUOps x86_tcg_ops = { .translate_code = x86_translate_code, .synchronize_from_tb = x86_cpu_synchronize_from_tb, .restore_state_to_opc = x86_restore_state_to_opc, + .mmu_index = x86_cpu_mmu_index, .cpu_exec_enter = x86_cpu_exec_enter, .cpu_exec_exit = x86_cpu_exec_exit, #ifdef CONFIG_USER_ONLY diff --git a/target/i386/tcg/tcg-cpu.h b/target/i386/tcg/tcg-cpu.h index 53a8494455..7580f8afb4 100644 --- a/target/i386/tcg/tcg-cpu.h +++ b/target/i386/tcg/tcg-cpu.h @@ -78,4 +78,6 @@ QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, pkru_state) != XSAVE_PKRU_OFFSET); bool tcg_cpu_realizefn(CPUState *cs, Error **errp); +int x86_mmu_index_pl(CPUX86State *env, unsigned pl); + #endif /* TCG_CPU_H */ From 3076af3441034a8050f660d5c294d26b2e2943b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:23 +0200 Subject: [PATCH 0160/2760] target/loongarch: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-11-philmd@linaro.org> --- target/loongarch/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index ea1665e270..cb96b17911 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -868,6 +868,7 @@ static const TCGCPUOps loongarch_tcg_ops = { .translate_code = loongarch_translate_code, .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, .restore_state_to_opc = loongarch_restore_state_to_opc, + .mmu_index = loongarch_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = loongarch_cpu_tlb_fill, @@ -919,7 +920,6 @@ static void loongarch_cpu_class_init(ObjectClass *c, void *data) &lacc->parent_phases); cc->class_by_name = loongarch_cpu_class_by_name; - cc->mmu_index = loongarch_cpu_mmu_index; cc->dump_state = loongarch_cpu_dump_state; cc->set_pc = loongarch_cpu_set_pc; cc->get_pc = loongarch_cpu_get_pc; From cc944932ecef3b7a56ae62d89dd92fb9e56c5cc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:24 +0200 Subject: [PATCH 0161/2760] target/m68k: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-12-philmd@linaro.org> --- target/m68k/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 0065e1c1ca..4409d8941c 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -592,6 +592,7 @@ static const TCGCPUOps m68k_tcg_ops = { .initialize = m68k_tcg_init, .translate_code = m68k_translate_code, .restore_state_to_opc = m68k_restore_state_to_opc, + .mmu_index = m68k_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = m68k_cpu_tlb_fill, @@ -615,7 +616,6 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) &mcc->parent_phases); cc->class_by_name = m68k_cpu_class_by_name; - cc->mmu_index = m68k_cpu_mmu_index; cc->dump_state = m68k_cpu_dump_state; cc->set_pc = m68k_cpu_set_pc; cc->get_pc = m68k_cpu_get_pc; From 2b08170ffeca77cb0f02a365f536a341dd857a7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:25 +0200 Subject: [PATCH 0162/2760] target/microblaze: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-13-philmd@linaro.org> --- target/microblaze/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index f3bebea856..88baeb6807 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -431,6 +431,7 @@ static const TCGCPUOps mb_tcg_ops = { .translate_code = mb_translate_code, .synchronize_from_tb = mb_cpu_synchronize_from_tb, .restore_state_to_opc = mb_restore_state_to_opc, + .mmu_index = mb_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = mb_cpu_tlb_fill, @@ -455,7 +456,6 @@ static void mb_cpu_class_init(ObjectClass *oc, void *data) &mcc->parent_phases); cc->class_by_name = mb_cpu_class_by_name; - cc->mmu_index = mb_cpu_mmu_index; cc->dump_state = mb_cpu_dump_state; cc->set_pc = mb_cpu_set_pc; cc->get_pc = mb_cpu_get_pc; From a433b2194ff32d80e4374f340673c5357984f9a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:26 +0200 Subject: [PATCH 0163/2760] target/mips: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-14-philmd@linaro.org> --- target/mips/cpu.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 47df563e12..cb0d6dde0e 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -182,11 +182,6 @@ static bool mips_cpu_has_work(CPUState *cs) } #endif /* !CONFIG_USER_ONLY */ -static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) -{ - return mips_env_mmu_index(cpu_env(cs)); -} - #include "cpu-defs.c.inc" static void mips_cpu_reset_hold(Object *obj, ResetType type) @@ -549,11 +544,18 @@ static const Property mips_cpu_properties[] = { #ifdef CONFIG_TCG #include "accel/tcg/cpu-ops.h" + +static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) +{ + return mips_env_mmu_index(cpu_env(cs)); +} + static const TCGCPUOps mips_tcg_ops = { .initialize = mips_tcg_init, .translate_code = mips_translate_code, .synchronize_from_tb = mips_cpu_synchronize_from_tb, .restore_state_to_opc = mips_restore_state_to_opc, + .mmu_index = mips_cpu_mmu_index, #if !defined(CONFIG_USER_ONLY) .tlb_fill = mips_cpu_tlb_fill, @@ -581,7 +583,6 @@ static void mips_cpu_class_init(ObjectClass *c, void *data) &mcc->parent_phases); cc->class_by_name = mips_cpu_class_by_name; - cc->mmu_index = mips_cpu_mmu_index; cc->dump_state = mips_cpu_dump_state; cc->set_pc = mips_cpu_set_pc; cc->get_pc = mips_cpu_get_pc; From a53066a9d660ee187a06062c1c44639714195a72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:27 +0200 Subject: [PATCH 0164/2760] target/openrisc: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-15-philmd@linaro.org> --- target/openrisc/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index e8abf1f8b5..dc55594a7d 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -247,6 +247,7 @@ static const TCGCPUOps openrisc_tcg_ops = { .translate_code = openrisc_translate_code, .synchronize_from_tb = openrisc_cpu_synchronize_from_tb, .restore_state_to_opc = openrisc_restore_state_to_opc, + .mmu_index = openrisc_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = openrisc_cpu_tlb_fill, @@ -269,7 +270,6 @@ static void openrisc_cpu_class_init(ObjectClass *oc, void *data) &occ->parent_phases); cc->class_by_name = openrisc_cpu_class_by_name; - cc->mmu_index = openrisc_cpu_mmu_index; cc->dump_state = openrisc_cpu_dump_state; cc->set_pc = openrisc_cpu_set_pc; cc->get_pc = openrisc_cpu_get_pc; From 853f9378a325dbeec78afe3478dea277be369a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:28 +0200 Subject: [PATCH 0165/2760] target/ppc: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert CPUClass::mmu_index() to TCGCPUOps::mmu_index(), restricting ppc_cpu_mmu_index() to TCG #ifdef. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-16-philmd@linaro.org> --- target/ppc/cpu_init.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 17f0f3d3ff..fd8c42069e 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7216,6 +7216,11 @@ static void ppc_restore_state_to_opc(CPUState *cs, cpu->env.nip = data[0]; } + +static int ppc_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return ppc_env_mmu_index(cpu_env(cs), ifetch); +} #endif /* CONFIG_TCG */ #ifndef CONFIG_USER_ONLY @@ -7225,11 +7230,6 @@ static bool ppc_cpu_has_work(CPUState *cs) } #endif /* !CONFIG_USER_ONLY */ -static int ppc_cpu_mmu_index(CPUState *cs, bool ifetch) -{ - return ppc_env_mmu_index(cpu_env(cs), ifetch); -} - static void ppc_cpu_reset_hold(Object *obj, ResetType type) { CPUState *cs = CPU(obj); @@ -7482,6 +7482,7 @@ static const TCGCPUOps ppc_tcg_ops = { .initialize = ppc_translate_init, .translate_code = ppc_translate_code, .restore_state_to_opc = ppc_restore_state_to_opc, + .mmu_index = ppc_cpu_mmu_index, #ifdef CONFIG_USER_ONLY .record_sigsegv = ppc_cpu_record_sigsegv, @@ -7518,7 +7519,6 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) &pcc->parent_phases); cc->class_by_name = ppc_cpu_class_by_name; - cc->mmu_index = ppc_cpu_mmu_index; cc->dump_state = ppc_cpu_dump_state; cc->set_pc = ppc_cpu_set_pc; cc->get_pc = ppc_cpu_get_pc; From bf8dc33bbca3d84251d40bd81fa1d832b3171ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:29 +0200 Subject: [PATCH 0166/2760] target/riscv: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move riscv_cpu_mmu_index() to the TCG-specific file, convert CPUClass::mmu_index() to TCGCPUOps::mmu_index(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-17-philmd@linaro.org> --- target/riscv/cpu.c | 6 ------ target/riscv/tcg/tcg-cpu.c | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 09ded6829a..430b02d2a5 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1021,11 +1021,6 @@ bool riscv_cpu_has_work(CPUState *cs) } #endif /* !CONFIG_USER_ONLY */ -static int riscv_cpu_mmu_index(CPUState *cs, bool ifetch) -{ - return riscv_env_mmu_index(cpu_env(cs), ifetch); -} - static void riscv_cpu_reset_hold(Object *obj, ResetType type) { #ifndef CONFIG_USER_ONLY @@ -3049,7 +3044,6 @@ static void riscv_cpu_common_class_init(ObjectClass *c, void *data) &mcc->parent_phases); cc->class_by_name = riscv_cpu_class_by_name; - cc->mmu_index = riscv_cpu_mmu_index; cc->dump_state = riscv_cpu_dump_state; cc->set_pc = riscv_cpu_set_pc; cc->get_pc = riscv_cpu_get_pc; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 5aef9eef36..bee7dfd803 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -91,6 +91,11 @@ static const char *cpu_priv_ver_to_str(int priv_ver) return priv_spec_str; } +static int riscv_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return riscv_env_mmu_index(cpu_env(cs), ifetch); +} + static void riscv_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { @@ -138,6 +143,7 @@ static const TCGCPUOps riscv_tcg_ops = { .translate_code = riscv_translate_code, .synchronize_from_tb = riscv_cpu_synchronize_from_tb, .restore_state_to_opc = riscv_restore_state_to_opc, + .mmu_index = riscv_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = riscv_cpu_tlb_fill, From f1a1c2b9586f6402fa1447c611c9f2d77e648ce9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:30 +0200 Subject: [PATCH 0167/2760] target/rx: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-18-philmd@linaro.org> --- target/rx/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/rx/cpu.c b/target/rx/cpu.c index a240b3b3ce..51743020d4 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -207,6 +207,7 @@ static const TCGCPUOps rx_tcg_ops = { .translate_code = rx_translate_code, .synchronize_from_tb = rx_cpu_synchronize_from_tb, .restore_state_to_opc = rx_restore_state_to_opc, + .mmu_index = rx_cpu_mmu_index, .tlb_fill = rx_cpu_tlb_fill, .cpu_exec_interrupt = rx_cpu_exec_interrupt, @@ -227,7 +228,6 @@ static void rx_cpu_class_init(ObjectClass *klass, void *data) &rcc->parent_phases); cc->class_by_name = rx_cpu_class_by_name; - cc->mmu_index = rx_cpu_mmu_index; cc->dump_state = rx_cpu_dump_state; cc->set_pc = rx_cpu_set_pc; cc->get_pc = rx_cpu_get_pc; From 96a786ed038f015c4f6293e2b3a641941a0e9f19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:31 +0200 Subject: [PATCH 0168/2760] target/s390x: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Convert CPUClass::mmu_index() to TCGCPUOps::mmu_index(), restricting s390x_cpu_mmu_index() to TCG #ifdef. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-19-philmd@linaro.org> --- target/s390x/cpu.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 1f75629ddc..d15b1943e0 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -126,11 +126,6 @@ static vaddr s390_cpu_get_pc(CPUState *cs) return cpu->env.psw.addr; } -static int s390x_cpu_mmu_index(CPUState *cs, bool ifetch) -{ - return s390x_env_mmu_index(cpu_env(cs), ifetch); -} - static void s390_query_cpu_fast(CPUState *cpu, CpuInfoFast *value) { S390CPU *s390_cpu = S390_CPU(cpu); @@ -308,6 +303,11 @@ static const Property s390x_cpu_properties[] = { #ifdef CONFIG_TCG #include "accel/tcg/cpu-ops.h" +static int s390x_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return s390x_env_mmu_index(cpu_env(cs), ifetch); +} + void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, uint64_t *cs_base, uint32_t *pflags) { @@ -348,6 +348,7 @@ static const TCGCPUOps s390_tcg_ops = { .initialize = s390x_translate_init, .translate_code = s390x_translate_code, .restore_state_to_opc = s390x_restore_state_to_opc, + .mmu_index = s390x_cpu_mmu_index, #ifdef CONFIG_USER_ONLY .record_sigsegv = s390_cpu_record_sigsegv, @@ -378,7 +379,6 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) &scc->parent_phases); cc->class_by_name = s390_cpu_class_by_name; - cc->mmu_index = s390x_cpu_mmu_index; cc->dump_state = s390_cpu_dump_state; cc->query_cpu_fast = s390_query_cpu_fast; cc->set_pc = s390_cpu_set_pc; From 32a84b306fe216986d8c78f60571a073f224e4b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:32 +0200 Subject: [PATCH 0169/2760] target/sh4: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-20-philmd@linaro.org> --- target/sh4/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index ce84bdf539..df093988cb 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -266,6 +266,7 @@ static const TCGCPUOps superh_tcg_ops = { .translate_code = sh4_translate_code, .synchronize_from_tb = superh_cpu_synchronize_from_tb, .restore_state_to_opc = superh_restore_state_to_opc, + .mmu_index = sh4_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = superh_cpu_tlb_fill, @@ -291,7 +292,6 @@ static void superh_cpu_class_init(ObjectClass *oc, void *data) &scc->parent_phases); cc->class_by_name = superh_cpu_class_by_name; - cc->mmu_index = sh4_cpu_mmu_index; cc->dump_state = superh_cpu_dump_state; cc->set_pc = superh_cpu_set_pc; cc->get_pc = superh_cpu_get_pc; From f34769a536c80753442f14f40387e2a4c936fd67 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:33 +0200 Subject: [PATCH 0170/2760] target/sparc: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-21-philmd@linaro.org> --- target/sparc/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 57fbf16ad2..af3cec43e7 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -1005,6 +1005,7 @@ static const TCGCPUOps sparc_tcg_ops = { .translate_code = sparc_translate_code, .synchronize_from_tb = sparc_cpu_synchronize_from_tb, .restore_state_to_opc = sparc_restore_state_to_opc, + .mmu_index = sparc_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = sparc_cpu_tlb_fill, @@ -1033,7 +1034,6 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) cc->class_by_name = sparc_cpu_class_by_name; cc->parse_features = sparc_cpu_parse_features; - cc->mmu_index = sparc_cpu_mmu_index; cc->dump_state = sparc_cpu_dump_state; #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) cc->memory_rw_debug = sparc_cpu_memory_rw_debug; From 46708ccec1cd4bb2c022bf97a67affb5a4bea6f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:34 +0200 Subject: [PATCH 0171/2760] target/tricore: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-22-philmd@linaro.org> --- target/tricore/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 16acc4ecb9..833a93d37a 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -176,6 +176,7 @@ static const TCGCPUOps tricore_tcg_ops = { .translate_code = tricore_translate_code, .synchronize_from_tb = tricore_cpu_synchronize_from_tb, .restore_state_to_opc = tricore_restore_state_to_opc, + .mmu_index = tricore_cpu_mmu_index, .tlb_fill = tricore_cpu_tlb_fill, .cpu_exec_interrupt = tricore_cpu_exec_interrupt, .cpu_exec_halt = tricore_cpu_has_work, @@ -194,7 +195,6 @@ static void tricore_cpu_class_init(ObjectClass *c, void *data) resettable_class_set_parent_phases(rc, NULL, tricore_cpu_reset_hold, NULL, &mcc->parent_phases); cc->class_by_name = tricore_cpu_class_by_name; - cc->mmu_index = tricore_cpu_mmu_index; cc->gdb_read_register = tricore_cpu_gdb_read_register; cc->gdb_write_register = tricore_cpu_gdb_write_register; From befb31d349e1761855a24023f21aab4207ce5392 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:35 +0200 Subject: [PATCH 0172/2760] target/xtensa: Restrict SoftMMU mmu_index() to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-23-philmd@linaro.org> --- target/xtensa/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index ec6a0a8b66..51f9ee9e89 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -236,6 +236,7 @@ static const TCGCPUOps xtensa_tcg_ops = { .translate_code = xtensa_translate_code, .debug_excp_handler = xtensa_breakpoint_handler, .restore_state_to_opc = xtensa_restore_state_to_opc, + .mmu_index = xtensa_cpu_mmu_index, #ifndef CONFIG_USER_ONLY .tlb_fill = xtensa_cpu_tlb_fill, @@ -262,7 +263,6 @@ static void xtensa_cpu_class_init(ObjectClass *oc, void *data) &xcc->parent_phases); cc->class_by_name = xtensa_cpu_class_by_name; - cc->mmu_index = xtensa_cpu_mmu_index; cc->dump_state = xtensa_cpu_dump_state; cc->set_pc = xtensa_cpu_set_pc; cc->get_pc = xtensa_cpu_get_pc; From 43d6a56a0574775c1b5ce5c5ad0c7f9dded83c87 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 3 Apr 2025 08:44:13 -0700 Subject: [PATCH 0173/2760] target/hexagon: Implement TCGCPUOps.mmu_index MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This hook is about to become mandatory. Since hexagon is still user-only, the implementation is trivial. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Brian Cain Signed-off-by: Richard Henderson --- target/hexagon/cpu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 766b678651..ad1f303fbc 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -313,6 +313,11 @@ static void hexagon_cpu_realize(DeviceState *dev, Error **errp) mcc->parent_realize(dev, errp); } +static int hexagon_cpu_mmu_index(CPUState *cs, bool ifetch) +{ + return MMU_USER_IDX; +} + static void hexagon_cpu_init(Object *obj) { } @@ -324,6 +329,7 @@ static const TCGCPUOps hexagon_tcg_ops = { .translate_code = hexagon_translate_code, .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, .restore_state_to_opc = hexagon_restore_state_to_opc, + .mmu_index = hexagon_cpu_mmu_index, }; static void hexagon_cpu_class_init(ObjectClass *c, void *data) From 42fec1bbf58a288e86314e495716d609928712df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:09:36 +0200 Subject: [PATCH 0174/2760] hw/core/cpu: Remove CPUClass::mmu_index() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All targets have been converted to TCGCPUOps::mmu_index(), remove the now unused CPUClass::mmu_index(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250401080938.32278-24-philmd@linaro.org> --- accel/tcg/cpu-exec.c | 1 + include/accel/tcg/cpu-mmu-index.h | 4 +--- include/hw/core/cpu.h | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 034c2ded6b..9e15105533 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -1075,6 +1075,7 @@ bool tcg_exec_realizefn(CPUState *cpu, Error **errp) assert(tcg_ops->cpu_exec_interrupt); #endif /* !CONFIG_USER_ONLY */ assert(tcg_ops->translate_code); + assert(tcg_ops->mmu_index); tcg_ops->initialize(); tcg_target_initialized = true; } diff --git a/include/accel/tcg/cpu-mmu-index.h b/include/accel/tcg/cpu-mmu-index.h index f1ca385d3c..e681a90844 100644 --- a/include/accel/tcg/cpu-mmu-index.h +++ b/include/accel/tcg/cpu-mmu-index.h @@ -34,9 +34,7 @@ static inline int cpu_mmu_index(CPUState *cs, bool ifetch) # endif #endif - const TCGCPUOps *tcg_ops = cs->cc->tcg_ops; - int ret = tcg_ops->mmu_index ? tcg_ops->mmu_index(cs, ifetch) - : cs->cc->mmu_index(cs, ifetch); + int ret = cs->cc->tcg_ops->mmu_index(cs, ifetch); tcg_debug_assert(ret >= 0 && ret < NB_MMU_MODES); return ret; } diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 29f6419050..28bd27b8ed 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -104,7 +104,6 @@ struct SysemuCPUOps; * instantiatable CPU type. * @parse_features: Callback to parse command line arguments. * @reset_dump_flags: #CPUDumpFlags to use for reset logging. - * @mmu_index: Callback for choosing softmmu mmu index. * @memory_rw_debug: Callback for GDB memory access. * @dump_state: Callback for dumping state. * @query_cpu_fast: @@ -151,7 +150,6 @@ struct CPUClass { ObjectClass *(*class_by_name)(const char *cpu_model); void (*parse_features)(const char *typename, char *str, Error **errp); - int (*mmu_index)(CPUState *cpu, bool ifetch); int (*memory_rw_debug)(CPUState *cpu, vaddr addr, uint8_t *buf, size_t len, bool is_write); void (*dump_state)(CPUState *cpu, FILE *, int flags); From 41fed3c99288451f0528752709151cef5822bd7e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Mar 2025 17:11:05 -0500 Subject: [PATCH 0175/2760] accel/tcg: Build translator.c twice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop some unnecessary includes. Change the offsetof expressions to be based on CPUState instead of ArchCPU. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 2 +- accel/tcg/translator.c | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 72d4acfe5e..047afa49a2 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -8,6 +8,7 @@ tcg_ss.add(files( 'cpu-exec-common.c', 'tcg-runtime.c', 'tcg-runtime-gvec.c', + 'translator.c', )) if get_option('plugins') tcg_ss.add(files('plugin-gen.c')) @@ -22,7 +23,6 @@ tcg_specific_ss.add(files( 'cpu-exec.c', 'tb-maint.c', 'translate-all.c', - 'translator.c', )) tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index 5f0aa9d56a..c53bbdef99 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -11,14 +11,13 @@ #include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "exec/exec-all.h" #include "exec/cpu-ldst-common.h" #include "accel/tcg/cpu-mmu-index.h" +#include "exec/target_page.h" #include "exec/translator.h" #include "exec/plugin-gen.h" #include "tcg/tcg-op-common.h" #include "internal-common.h" -#include "internal-target.h" #include "disas/disas.h" #include "tb-internal.h" @@ -26,8 +25,7 @@ static void set_can_do_io(DisasContextBase *db, bool val) { QEMU_BUILD_BUG_ON(sizeof_field(CPUState, neg.can_do_io) != 1); tcg_gen_st8_i32(tcg_constant_i32(val), tcg_env, - offsetof(ArchCPU, parent_obj.neg.can_do_io) - - offsetof(ArchCPU, env)); + offsetof(CPUState, neg.can_do_io) - sizeof(CPUState)); } bool translator_io_start(DisasContextBase *db) @@ -50,8 +48,8 @@ static TCGOp *gen_tb_start(DisasContextBase *db, uint32_t cflags) if ((cflags & CF_USE_ICOUNT) || !(cflags & CF_NOIRQ)) { count = tcg_temp_new_i32(); tcg_gen_ld_i32(count, tcg_env, - offsetof(ArchCPU, parent_obj.neg.icount_decr.u32) - - offsetof(ArchCPU, env)); + offsetof(CPUState, neg.icount_decr.u32) - + sizeof(CPUState)); } if (cflags & CF_USE_ICOUNT) { @@ -80,8 +78,8 @@ static TCGOp *gen_tb_start(DisasContextBase *db, uint32_t cflags) if (cflags & CF_USE_ICOUNT) { tcg_gen_st16_i32(count, tcg_env, - offsetof(ArchCPU, parent_obj.neg.icount_decr.u16.low) - - offsetof(ArchCPU, env)); + offsetof(CPUState, neg.icount_decr.u16.low) - + sizeof(CPUState)); } return icount_start_insn; From 33646c72c75637fb3a4c5b225dea5d44ed2e4d44 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Mar 2025 15:48:11 -0500 Subject: [PATCH 0176/2760] accel/tcg: Split out tlb-bounds.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CPU_TLB_DYN_{MIN,MAX}_BITS definitions are not required outside of cputlb.c and translate-all.c. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 1 + accel/tcg/tb-internal.h | 27 --------------------------- accel/tcg/tlb-bounds.h | 32 ++++++++++++++++++++++++++++++++ accel/tcg/translate-all.c | 1 + 4 files changed, 34 insertions(+), 27 deletions(-) create mode 100644 accel/tcg/tlb-bounds.h diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 28c47d4872..a717f357d5 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -41,6 +41,7 @@ #include "trace.h" #include "tb-hash.h" #include "tb-internal.h" +#include "tlb-bounds.h" #include "internal-common.h" #include "internal-target.h" #ifdef CONFIG_PLUGIN diff --git a/accel/tcg/tb-internal.h b/accel/tcg/tb-internal.h index f9a06bcbab..08538e2896 100644 --- a/accel/tcg/tb-internal.h +++ b/accel/tcg/tb-internal.h @@ -22,33 +22,6 @@ */ #define GETPC_ADJ 2 -#ifdef CONFIG_SOFTMMU - -#define CPU_TLB_DYN_MIN_BITS 6 -#define CPU_TLB_DYN_DEFAULT_BITS 8 - -# if HOST_LONG_BITS == 32 -/* Make sure we do not require a double-word shift for the TLB load */ -# define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS) -# else /* HOST_LONG_BITS == 64 */ -/* - * Assuming TARGET_PAGE_BITS==12, with 2**22 entries we can cover 2**(22+12) == - * 2**34 == 16G of address space. This is roughly what one would expect a - * TLB to cover in a modern (as of 2018) x86_64 CPU. For instance, Intel - * Skylake's Level-2 STLB has 16 1G entries. - * Also, make sure we do not size the TLB past the guest's address space. - */ -# ifdef TARGET_PAGE_BITS_VARY -# define CPU_TLB_DYN_MAX_BITS \ - MIN(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) -# else -# define CPU_TLB_DYN_MAX_BITS \ - MIN_CONST(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) -# endif -# endif - -#endif /* CONFIG_SOFTMMU */ - void tb_lock_page0(tb_page_addr_t); #ifdef CONFIG_USER_ONLY diff --git a/accel/tcg/tlb-bounds.h b/accel/tcg/tlb-bounds.h new file mode 100644 index 0000000000..efd34d4793 --- /dev/null +++ b/accel/tcg/tlb-bounds.h @@ -0,0 +1,32 @@ +/* + * softmmu size bounds + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_TLB_BOUNDS_H +#define ACCEL_TCG_TLB_BOUNDS_H + +#define CPU_TLB_DYN_MIN_BITS 6 +#define CPU_TLB_DYN_DEFAULT_BITS 8 + +# if HOST_LONG_BITS == 32 +/* Make sure we do not require a double-word shift for the TLB load */ +# define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS) +# else /* HOST_LONG_BITS == 64 */ +/* + * Assuming TARGET_PAGE_BITS==12, with 2**22 entries we can cover 2**(22+12) == + * 2**34 == 16G of address space. This is roughly what one would expect a + * TLB to cover in a modern (as of 2018) x86_64 CPU. For instance, Intel + * Skylake's Level-2 STLB has 16 1G entries. + * Also, make sure we do not size the TLB past the guest's address space. + */ +# ifdef TARGET_PAGE_BITS_VARY +# define CPU_TLB_DYN_MAX_BITS \ + MIN(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) +# else +# define CPU_TLB_DYN_MAX_BITS \ + MIN_CONST(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) +# endif +# endif + +#endif /* ACCEL_TCG_TLB_BOUNDS_H */ diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index bb161ae61a..87fb6c51d3 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -47,6 +47,7 @@ #include "exec/page-protection.h" #include "exec/mmap-lock.h" #include "tb-internal.h" +#include "tlb-bounds.h" #include "exec/translator.h" #include "exec/tb-flush.h" #include "qemu/bitmap.h" From 6effa87475986093007e3f2dcfd1f781de0993b1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Mar 2025 15:05:12 -0500 Subject: [PATCH 0177/2760] include/exec: Redefine tlb-flags with absolute values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Don't base the values on TARGET_PAGE_BITS_MIN, but do verify that TLB_FLAGS_MASK does not overlap minimum page size. All targets now have the same placement for these flags, simplifying mmu management when we enable heterogeneous systems. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 2 ++ include/exec/tlb-flags.h | 68 +++++++++++++++++++--------------------- 2 files changed, 34 insertions(+), 36 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index a717f357d5..39314e86f3 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -49,6 +49,8 @@ #endif #include "tcg/tcg-ldst.h" +QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & ((1u < TARGET_PAGE_BITS_MIN) - 1)); + /* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */ /* #define DEBUG_TLB */ /* #define DEBUG_TLB_LOG */ diff --git a/include/exec/tlb-flags.h b/include/exec/tlb-flags.h index 54a6bae768..357e79095c 100644 --- a/include/exec/tlb-flags.h +++ b/include/exec/tlb-flags.h @@ -19,54 +19,29 @@ #ifndef TLB_FLAGS_H #define TLB_FLAGS_H -#include "exec/cpu-defs.h" - -#ifdef CONFIG_USER_ONLY - /* - * Allow some level of source compatibility with softmmu. We do not - * support any of the more exotic features, so only invalid pages may - * be signaled by probe_access_flags(). + * Flags returned for lookup of a TLB virtual address. */ -#define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1)) -#define TLB_MMIO (1 << (TARGET_PAGE_BITS_MIN - 2)) -#define TLB_WATCHPOINT 0 -#else +#ifdef CONFIG_USER_ONLY /* - * Flags stored in the low bits of the TLB virtual address. - * These are defined so that fast path ram access is all zeros. - * The flags all must be between TARGET_PAGE_BITS and - * maximum address alignment bit. - * - * Use TARGET_PAGE_BITS_MIN so that these bits are constant - * when TARGET_PAGE_BITS_VARY is in effect. - * - * The count, if not the placement of these bits is known - * to tcg/tcg-op-ldst.c, check_max_alignment(). - */ -/* Zero if TLB entry is valid. */ -#define TLB_INVALID_MASK (1 << (TARGET_PAGE_BITS_MIN - 1)) -/* - * Set if TLB entry references a clean RAM page. The iotlb entry will - * contain the page physical address. + * Allow some level of source compatibility with softmmu. + * Invalid is set when the page does not have requested permissions. + * MMIO is set when we want the target helper to use the functional + * interface for load/store so that plugins see the access. */ -#define TLB_NOTDIRTY (1 << (TARGET_PAGE_BITS_MIN - 2)) -/* Set if the slow path must be used; more flags in CPUTLBEntryFull. */ -#define TLB_FORCE_SLOW (1 << (TARGET_PAGE_BITS_MIN - 3)) +#define TLB_INVALID_MASK (1 << 0) +#define TLB_MMIO (1 << 1) +#define TLB_WATCHPOINT 0 -/* - * Use this mask to check interception with an alignment mask - * in a TCG backend. - */ -#define TLB_FLAGS_MASK \ - (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_FORCE_SLOW) +#else /* * Flags stored in CPUTLBEntryFull.slow_flags[x]. * TLB_FORCE_SLOW must be set in CPUTLBEntry.addr_idx[x]. */ + /* Set if TLB entry requires byte swap. */ #define TLB_BSWAP (1 << 0) /* Set if TLB entry contains a watchpoint. */ @@ -82,6 +57,27 @@ (TLB_BSWAP | TLB_WATCHPOINT | TLB_CHECK_ALIGNED | \ TLB_DISCARD_WRITE | TLB_MMIO) +/* + * Flags stored in CPUTLBEntry.addr_idx[x]. + * These must be above the largest alignment (64 bytes), + * and below the smallest page size (1024 bytes). + * This leaves bits [9:6] available for use. + */ + +/* Zero if TLB entry is valid. */ +#define TLB_INVALID_MASK (1 << 6) +/* Set if TLB entry references a clean RAM page. */ +#define TLB_NOTDIRTY (1 << 7) +/* Set if the slow path must be used; more flags in CPUTLBEntryFull. */ +#define TLB_FORCE_SLOW (1 << 8) + +/* + * Use this mask to check interception with an alignment mask + * in a TCG backend. + */ +#define TLB_FLAGS_MASK \ + (TLB_INVALID_MASK | TLB_NOTDIRTY | TLB_FORCE_SLOW) + /* The two sets of flags must not overlap. */ QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & TLB_SLOW_FLAGS_MASK); From 12eeb04ab4dd98f802ffc503f0da948a8c843086 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 27 Mar 2025 18:52:13 -0500 Subject: [PATCH 0178/2760] page-vary: Move and rename qemu_target_page_bits_min Rename to migration_legacy_page_bits, to make it clear that we cannot change the value without causing a migration break. Move to page-vary.h and page-vary-target.c. Define via TARGET_PAGE_BITS if not TARGET_PAGE_BITS_VARY. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/exec/page-vary.h | 9 +++++++++ include/exec/target_page.h | 1 - migration/savevm.c | 6 +++--- page-target.c | 5 ----- page-vary-target.c | 9 +++++++++ 5 files changed, 21 insertions(+), 9 deletions(-) diff --git a/include/exec/page-vary.h b/include/exec/page-vary.h index 54ddde308a..101c25911c 100644 --- a/include/exec/page-vary.h +++ b/include/exec/page-vary.h @@ -49,4 +49,13 @@ bool set_preferred_target_page_bits(int bits); */ void finalize_target_page_bits(void); +/** + * migration_legacy_page_bits + * + * For migration compatibility with qemu v2.9, prior to the introduction + * of the configuration/target-page-bits section, return the value of + * TARGET_PAGE_BITS that the target had then. + */ +int migration_legacy_page_bits(void); + #endif /* EXEC_PAGE_VARY_H */ diff --git a/include/exec/target_page.h b/include/exec/target_page.h index 8e89e5cbe6..e4bd7f7767 100644 --- a/include/exec/target_page.h +++ b/include/exec/target_page.h @@ -63,7 +63,6 @@ static inline int qemu_target_page_bits(void) return TARGET_PAGE_BITS; } -int qemu_target_page_bits_min(void); size_t qemu_target_pages_to_MiB(size_t pages); #endif diff --git a/migration/savevm.c b/migration/savevm.c index c33200a33f..0c12e373b4 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -50,6 +50,7 @@ #include "system/cpus.h" #include "system/memory.h" #include "exec/target_page.h" +#include "exec/page-vary.h" #include "trace.h" #include "qemu/iov.h" #include "qemu/job.h" @@ -339,7 +340,7 @@ static int configuration_pre_load(void *opaque) * predates the variable-target-page-bits support and is using the * minimum possible value for this CPU. */ - state->target_page_bits = qemu_target_page_bits_min(); + state->target_page_bits = migration_legacy_page_bits(); return 0; } @@ -462,8 +463,7 @@ static const VMStateInfo vmstate_info_capability = { */ static bool vmstate_target_page_bits_needed(void *opaque) { - return qemu_target_page_bits() - > qemu_target_page_bits_min(); + return qemu_target_page_bits() > migration_legacy_page_bits(); } static const VMStateDescription vmstate_target_page_bits = { diff --git a/page-target.c b/page-target.c index 321e43d06f..8fcd5443b5 100644 --- a/page-target.c +++ b/page-target.c @@ -9,11 +9,6 @@ #include "qemu/osdep.h" #include "exec/target_page.h" -int qemu_target_page_bits_min(void) -{ - return TARGET_PAGE_BITS_MIN; -} - /* Convert target pages to MiB (2**20). */ size_t qemu_target_pages_to_MiB(size_t pages) { diff --git a/page-vary-target.c b/page-vary-target.c index 84ddeb7c26..6251d948cf 100644 --- a/page-vary-target.c +++ b/page-vary-target.c @@ -23,6 +23,15 @@ #include "exec/page-vary.h" #include "exec/target_page.h" +int migration_legacy_page_bits(void) +{ +#ifdef TARGET_PAGE_BITS_VARY + return TARGET_PAGE_BITS_MIN; +#else + return TARGET_PAGE_BITS; +#endif +} + bool set_preferred_target_page_bits(int bits) { #ifdef TARGET_PAGE_BITS_VARY From d11bf649d587dec050b6ba900a8f9baea1fe157d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 28 Mar 2025 14:28:06 -0500 Subject: [PATCH 0179/2760] page-vary: Restrict scope of TARGET_PAGE_BITS_MIN The only place we really need to know the minimum is within page-vary-target.c. Rename the target/arm TARGET_PAGE_BITS_MIN to TARGET_PAGE_BITS_LEGACY to emphasize what it really means. Move the assertions related to minimum page size as well. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 1 - include/exec/cpu-defs.h | 10 ++-------- include/exec/target_page.h | 1 - page-vary-target.c | 39 +++++++++++++++++++++++++++++++++++--- target/alpha/cpu-param.h | 1 - target/arm/cpu-param.h | 3 +-- target/ppc/cpu-param.h | 1 - 7 files changed, 39 insertions(+), 17 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 39314e86f3..0de46903dd 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -49,7 +49,6 @@ #endif #include "tcg/tcg-ldst.h" -QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & ((1u < TARGET_PAGE_BITS_MIN) - 1)); /* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */ /* #define DEBUG_TLB */ diff --git a/include/exec/cpu-defs.h b/include/exec/cpu-defs.h index 9f955f53fd..e01acb7c90 100644 --- a/include/exec/cpu-defs.h +++ b/include/exec/cpu-defs.h @@ -34,14 +34,8 @@ #ifndef TARGET_VIRT_ADDR_SPACE_BITS # error TARGET_VIRT_ADDR_SPACE_BITS must be defined in cpu-param.h #endif -#ifndef TARGET_PAGE_BITS -# ifdef TARGET_PAGE_BITS_VARY -# ifndef TARGET_PAGE_BITS_MIN -# error TARGET_PAGE_BITS_MIN must be defined in cpu-param.h -# endif -# else -# error TARGET_PAGE_BITS must be defined in cpu-param.h -# endif +#if !defined(TARGET_PAGE_BITS) && !defined(TARGET_PAGE_BITS_VARY) +# error TARGET_PAGE_BITS must be defined in cpu-param.h #endif #include "exec/target_long.h" diff --git a/include/exec/target_page.h b/include/exec/target_page.h index e4bd7f7767..ca0ebbc8bb 100644 --- a/include/exec/target_page.h +++ b/include/exec/target_page.h @@ -41,7 +41,6 @@ extern const TargetPageBits target_page; # endif # define TARGET_PAGE_SIZE (-(int)TARGET_PAGE_MASK) #else -# define TARGET_PAGE_BITS_MIN TARGET_PAGE_BITS # define TARGET_PAGE_SIZE (1 << TARGET_PAGE_BITS) # define TARGET_PAGE_MASK ((TARGET_PAGE_TYPE)-1 << TARGET_PAGE_BITS) #endif diff --git a/page-vary-target.c b/page-vary-target.c index 6251d948cf..49a32b4fe5 100644 --- a/page-vary-target.c +++ b/page-vary-target.c @@ -23,19 +23,45 @@ #include "exec/page-vary.h" #include "exec/target_page.h" + +/* + * For system mode, the minimum comes from the number of bits + * required for maximum alignment (6) and the number of bits + * required for TLB_FLAGS_MASK (3). + * + * For user mode, TARGET_PAGE_BITS_VARY is a hack to allow the target + * page size to match the host page size. Mostly, this reduces the + * ordinary target page size to run on a host with 4KiB pages (i.e. x86). + * There is no true minimum required by the implementation, but keep the + * same minimum as for system mode for sanity. + * See linux-user/mmap.c, mmap_h_lt_g and mmap_h_gt_g. + */ +#define TARGET_PAGE_BITS_MIN 9 + +#ifndef TARGET_PAGE_BITS_VARY +QEMU_BUILD_BUG_ON(TARGET_PAGE_BITS < TARGET_PAGE_BITS_MIN); +#endif + +#ifndef CONFIG_USER_ONLY +#include "exec/tlb-flags.h" + +QEMU_BUILD_BUG_ON(TLB_FLAGS_MASK & ((1u < TARGET_PAGE_BITS_MIN) - 1)); + int migration_legacy_page_bits(void) { #ifdef TARGET_PAGE_BITS_VARY - return TARGET_PAGE_BITS_MIN; + QEMU_BUILD_BUG_ON(TARGET_PAGE_BITS_LEGACY < TARGET_PAGE_BITS_MIN); + return TARGET_PAGE_BITS_LEGACY; #else return TARGET_PAGE_BITS; #endif } +#endif bool set_preferred_target_page_bits(int bits) { -#ifdef TARGET_PAGE_BITS_VARY assert(bits >= TARGET_PAGE_BITS_MIN); +#ifdef TARGET_PAGE_BITS_VARY return set_preferred_target_page_bits_common(bits); #else return true; @@ -44,5 +70,12 @@ bool set_preferred_target_page_bits(int bits) void finalize_target_page_bits(void) { - finalize_target_page_bits_common(TARGET_PAGE_BITS_MIN); +#ifndef TARGET_PAGE_BITS_VARY + finalize_target_page_bits_common(TARGET_PAGE_BITS); +#elif defined(CONFIG_USER_ONLY) + assert(target_page.bits != 0); + finalize_target_page_bits_common(target_page.bits); +#else + finalize_target_page_bits_common(TARGET_PAGE_BITS_LEGACY); +#endif } diff --git a/target/alpha/cpu-param.h b/target/alpha/cpu-param.h index ff06e41497..63989e71c0 100644 --- a/target/alpha/cpu-param.h +++ b/target/alpha/cpu-param.h @@ -18,7 +18,6 @@ * a 4k minimum to match x86 host, which can minimize emulation issues. */ # define TARGET_PAGE_BITS_VARY -# define TARGET_PAGE_BITS_MIN 12 # define TARGET_VIRT_ADDR_SPACE_BITS 63 #else # define TARGET_PAGE_BITS 13 diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h index 896b35bd6d..a7ae42d17d 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -24,7 +24,6 @@ # else /* Allow user-only to vary page size from 4k */ # define TARGET_PAGE_BITS_VARY -# define TARGET_PAGE_BITS_MIN 12 # endif # else # define TARGET_PAGE_BITS 12 @@ -35,7 +34,7 @@ * have to support 1K tiny pages. */ # define TARGET_PAGE_BITS_VARY -# define TARGET_PAGE_BITS_MIN 10 +# define TARGET_PAGE_BITS_LEGACY 10 #endif /* !CONFIG_USER_ONLY */ /* ARM processors have a weak memory model */ diff --git a/target/ppc/cpu-param.h b/target/ppc/cpu-param.h index 6c4525fdf3..553ad2f4c6 100644 --- a/target/ppc/cpu-param.h +++ b/target/ppc/cpu-param.h @@ -33,7 +33,6 @@ #ifdef CONFIG_USER_ONLY /* Allow user-only to vary page size from 4k */ # define TARGET_PAGE_BITS_VARY -# define TARGET_PAGE_BITS_MIN 12 #else # define TARGET_PAGE_BITS 12 #endif From 33d2cca32b6822767c1f388f0b05b8a046aa556f Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:58:47 -0700 Subject: [PATCH 0180/2760] exec/cpu-all: move cpu_copy to linux-user/qemu.h Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-3-pierrick.bouvier@linaro.org> --- include/exec/cpu-all.h | 2 -- linux-user/qemu.h | 3 +++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 5c4379f0d0..2aaaf0548d 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -31,8 +31,6 @@ #endif -CPUArchState *cpu_copy(CPUArchState *env); - #include "cpu.h" /* Validate correct placement of CPUArchState. */ diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 5f00750151..948de8431a 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -362,4 +362,7 @@ void *lock_user_string(abi_ulong guest_addr); #define unlock_user_struct(host_ptr, guest_addr, copy) \ unlock_user(host_ptr, guest_addr, (copy) ? sizeof(*host_ptr) : 0) +/* Clone cpu state */ +CPUArchState *cpu_copy(CPUArchState *env); + #endif /* QEMU_H */ From 8d535c312ca5944622b3b74177eb12d8f6b7a7fa Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:58:48 -0700 Subject: [PATCH 0181/2760] include/exec/cpu-all: move compile time check for CPUArchState to cpu-target.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-4-pierrick.bouvier@linaro.org> --- cpu-target.c | 5 +++++ include/exec/cpu-all.h | 4 ---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index 519b0f8900..7f3b244ed1 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "cpu.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" @@ -29,6 +30,10 @@ #include "accel/accel-cpu-target.h" #include "trace/trace-root.h" +/* Validate correct placement of CPUArchState. */ +QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0); +QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); + char *cpu_model_from_type(const char *typename) { const char *suffix = "-" CPU_RESOLVING_TYPE; diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 2aaaf0548d..be462c4410 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -33,8 +33,4 @@ #include "cpu.h" -/* Validate correct placement of CPUArchState. */ -QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0); -QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); - #endif /* CPU_ALL_H */ From 342e313d6c1a8e6da758bd642777b85af1a0fc37 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:58:49 -0700 Subject: [PATCH 0182/2760] exec/cpu-all: remove system/memory include We include this header where needed. When includes set already have ifdef CONFIG_USER_ONLY, we add it here, else, we don't condition the include. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-5-pierrick.bouvier@linaro.org> --- hw/ppc/spapr_ovec.c | 1 + hw/s390x/ipl.h | 1 + include/exec/cpu-all.h | 5 ----- target/alpha/helper.c | 1 + target/arm/hvf/hvf.c | 1 + target/arm/internals.h | 1 + target/hppa/cpu.h | 1 + target/i386/arch_memory_mapping.c | 1 + target/i386/helper.c | 1 + target/i386/hvf/vmx.h | 1 + target/i386/tcg/system/misc_helper.c | 1 + target/i386/tcg/system/tcg-cpu.c | 1 + target/m68k/helper.c | 1 + target/ppc/excp_helper.c | 1 + target/ppc/mmu-book3s-v3.c | 1 + target/ppc/mmu-hash32.h | 2 ++ target/ppc/mmu-hash64.c | 1 + target/ppc/mmu-radix64.c | 1 + target/riscv/cpu_helper.c | 1 + target/sparc/ldst_helper.c | 1 + target/sparc/mmu_helper.c | 1 + target/xtensa/mmu_helper.c | 1 + target/xtensa/op_helper.c | 1 + 23 files changed, 23 insertions(+), 5 deletions(-) diff --git a/hw/ppc/spapr_ovec.c b/hw/ppc/spapr_ovec.c index 6d6eaf67cb..75ab4fe262 100644 --- a/hw/ppc/spapr_ovec.c +++ b/hw/ppc/spapr_ovec.c @@ -16,6 +16,7 @@ #include "migration/vmstate.h" #include "qemu/bitmap.h" #include "system/address-spaces.h" +#include "system/memory.h" #include "qemu/error-report.h" #include "trace.h" #include diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index c6ecb3433c..6557ac3be5 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -15,6 +15,7 @@ #include "cpu.h" #include "system/address-spaces.h" +#include "system/memory.h" #include "hw/qdev-core.h" #include "hw/s390x/ipl/qipl.h" #include "qom/object.h" diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index be462c4410..399fcbb9d1 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -26,11 +26,6 @@ #include "hw/core/cpu.h" #include "exec/cpu-defs.h" #include "exec/target_page.h" -#ifndef CONFIG_USER_ONLY -#include "system/memory.h" -#endif - - #include "cpu.h" #endif /* CPU_ALL_H */ diff --git a/target/alpha/helper.c b/target/alpha/helper.c index 57cefcba14..f6261a3a53 100644 --- a/target/alpha/helper.c +++ b/target/alpha/helper.c @@ -25,6 +25,7 @@ #include "fpu/softfloat-types.h" #include "exec/helper-proto.h" #include "qemu/qemu-print.h" +#include "system/memory.h" #define CONVERT_BIT(X, SRC, DST) \ diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 93a3f9b53d..34ca36fab5 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -23,6 +23,7 @@ #include #include "system/address-spaces.h" +#include "system/memory.h" #include "hw/boards.h" #include "hw/irq.h" #include "qemu/main-loop.h" diff --git a/target/arm/internals.h b/target/arm/internals.h index 8756c24c08..01408e40a3 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -28,6 +28,7 @@ #include "exec/breakpoint.h" #include "hw/registerfields.h" #include "tcg/tcg-gvec-desc.h" +#include "system/memory.h" #include "syndrome.h" #include "cpu-features.h" diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 8b36642b59..f6bf068776 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -22,6 +22,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "system/memory.h" #include "qemu/cpu-float.h" #include "qemu/interval-tree.h" #include "hw/registerfields.h" diff --git a/target/i386/arch_memory_mapping.c b/target/i386/arch_memory_mapping.c index ced199862d..a2398c2173 100644 --- a/target/i386/arch_memory_mapping.c +++ b/target/i386/arch_memory_mapping.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "system/memory_mapping.h" +#include "system/memory.h" /* PAE Paging or IA-32e Paging */ static void walk_pte(MemoryMappingList *list, AddressSpace *as, diff --git a/target/i386/helper.c b/target/i386/helper.c index c07b1b16ea..64d9e8ab9c 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -25,6 +25,7 @@ #include "system/runstate.h" #ifndef CONFIG_USER_ONLY #include "system/hw_accel.h" +#include "system/memory.h" #include "monitor/monitor.h" #include "kvm/kvm_i386.h" #endif diff --git a/target/i386/hvf/vmx.h b/target/i386/hvf/vmx.h index 029516e5c0..26d6029fa5 100644 --- a/target/i386/hvf/vmx.h +++ b/target/i386/hvf/vmx.h @@ -34,6 +34,7 @@ #include "system/hvf_int.h" #include "system/address-spaces.h" +#include "system/memory.h" static inline uint64_t rreg(hv_vcpuid_t vcpu, hv_x86_reg_t reg) { diff --git a/target/i386/tcg/system/misc_helper.c b/target/i386/tcg/system/misc_helper.c index 0555cf2604..67896c8c87 100644 --- a/target/i386/tcg/system/misc_helper.c +++ b/target/i386/tcg/system/misc_helper.c @@ -23,6 +23,7 @@ #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include "system/address-spaces.h" +#include "system/memory.h" #include "exec/cputlb.h" #include "tcg/helper-tcg.h" #include "hw/i386/apic.h" diff --git a/target/i386/tcg/system/tcg-cpu.c b/target/i386/tcg/system/tcg-cpu.c index ab1f3c7c59..0538a4fd51 100644 --- a/target/i386/tcg/system/tcg-cpu.c +++ b/target/i386/tcg/system/tcg-cpu.c @@ -24,6 +24,7 @@ #include "system/system.h" #include "qemu/units.h" #include "system/address-spaces.h" +#include "system/memory.h" #include "tcg/tcg-cpu.h" diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 0bf574830f..8251272219 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -25,6 +25,7 @@ #include "exec/page-protection.h" #include "exec/gdbstub.h" #include "exec/helper-proto.h" +#include "system/memory.h" #include "gdbstub/helpers.h" #include "fpu/softfloat.h" #include "qemu/qemu-print.h" diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index c941c89806..da8b525a41 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/log.h" +#include "system/memory.h" #include "system/tcg.h" #include "system/system.h" #include "system/runstate.h" diff --git a/target/ppc/mmu-book3s-v3.c b/target/ppc/mmu-book3s-v3.c index a812cb5113..3865556310 100644 --- a/target/ppc/mmu-book3s-v3.c +++ b/target/ppc/mmu-book3s-v3.c @@ -18,6 +18,7 @@ */ #include "qemu/osdep.h" +#include "system/memory.h" #include "cpu.h" #include "mmu-hash64.h" #include "mmu-book3s-v3.h" diff --git a/target/ppc/mmu-hash32.h b/target/ppc/mmu-hash32.h index 2838de031c..04c23ea75e 100644 --- a/target/ppc/mmu-hash32.h +++ b/target/ppc/mmu-hash32.h @@ -3,6 +3,8 @@ #ifndef CONFIG_USER_ONLY +#include "system/memory.h" + bool ppc_hash32_xlate(PowerPCCPU *cpu, vaddr eaddr, MMUAccessType access_type, hwaddr *raddrp, int *psizep, int *protp, int mmu_idx, bool guest_visible); diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 5ca4faee2a..3ba4810497 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -25,6 +25,7 @@ #include "qemu/error-report.h" #include "qemu/qemu-print.h" #include "system/hw_accel.h" +#include "system/memory.h" #include "kvm_ppc.h" #include "mmu-hash64.h" #include "exec/log.h" diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 461eda4a3d..4ab5f3bb92 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -23,6 +23,7 @@ #include "exec/page-protection.h" #include "qemu/error-report.h" #include "system/kvm.h" +#include "system/memory.h" #include "kvm_ppc.h" #include "exec/log.h" #include "internal.h" diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 0dd8645994..ca58094fb5 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -26,6 +26,7 @@ #include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "system/memory.h" #include "instmap.h" #include "tcg/tcg-op.h" #include "accel/tcg/cpu-ops.h" diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 45882e25db..8890d2b119 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -27,6 +27,7 @@ #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" +#include "system/memory.h" #ifdef CONFIG_USER_ONLY #include "user/page-protection.h" #endif diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index 249b1f6c4c..c5d82a0854 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -24,6 +24,7 @@ #include "accel/tcg/cpu-mmu-index.h" #include "exec/page-protection.h" #include "exec/tlb-flags.h" +#include "system/memory.h" #include "qemu/qemu-print.h" #include "trace.h" diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c index 40b02f0a2c..1ce125794d 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -36,6 +36,7 @@ #include "accel/tcg/cpu-mmu-index.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "system/memory.h" #define XTENSA_MPU_SEGMENT_MASK 0x0000001f #define XTENSA_MPU_ACC_RIGHTS_MASK 0x00000f00 diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c index 028d4e0a1c..c125fa4946 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -31,6 +31,7 @@ #include "exec/page-protection.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" +#include "system/memory.h" #include "qemu/atomic.h" #include "qemu/timer.h" From e33865f0484cf8f65e12e864d8dc825456174ddc Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:58:50 -0700 Subject: [PATCH 0183/2760] exec/cpu-all: remove exec/page-protection include Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-6-pierrick.bouvier@linaro.org> --- include/exec/cpu-all.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 399fcbb9d1..957c86886e 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -19,7 +19,6 @@ #ifndef CPU_ALL_H #define CPU_ALL_H -#include "exec/page-protection.h" #include "exec/cpu-common.h" #include "exec/cpu-interrupt.h" #include "exec/tswap.h" From 4533af18b2ed5a6852ffb2dc024784d243f64ba4 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:58:51 -0700 Subject: [PATCH 0184/2760] exec/cpu-all: remove tswap include Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-7-pierrick.bouvier@linaro.org> --- include/exec/cpu-all.h | 1 - target/i386/tcg/system/excp_helper.c | 1 + target/i386/xsave_helper.c | 1 + target/ppc/mmu-hash64.h | 2 ++ target/riscv/vector_helper.c | 1 + 5 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 957c86886e..bfa039ab76 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -21,7 +21,6 @@ #include "exec/cpu-common.h" #include "exec/cpu-interrupt.h" -#include "exec/tswap.h" #include "hw/core/cpu.h" #include "exec/cpu-defs.h" #include "exec/target_page.h" diff --git a/target/i386/tcg/system/excp_helper.c b/target/i386/tcg/system/excp_helper.c index b0b74df72f..4badd73943 100644 --- a/target/i386/tcg/system/excp_helper.c +++ b/target/i386/tcg/system/excp_helper.c @@ -23,6 +23,7 @@ #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/tlb-flags.h" +#include "exec/tswap.h" #include "tcg/helper-tcg.h" typedef struct TranslateParams { diff --git a/target/i386/xsave_helper.c b/target/i386/xsave_helper.c index 996e9f3bfe..24ab7be8e9 100644 --- a/target/i386/xsave_helper.c +++ b/target/i386/xsave_helper.c @@ -5,6 +5,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/tswap.h" void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen) { diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index ae8d4b37ae..b8fb12a970 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -1,6 +1,8 @@ #ifndef MMU_HASH64_H #define MMU_HASH64_H +#include "exec/tswap.h" + #ifndef CONFIG_USER_ONLY #ifdef TARGET_PPC64 diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 83978be060..7fffa23bc8 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -26,6 +26,7 @@ #include "exec/page-protection.h" #include "exec/helper-proto.h" #include "exec/tlb-flags.h" +#include "exec/tswap.h" #include "fpu/softfloat.h" #include "tcg/tcg-gvec-desc.h" #include "internals.h" From 22a7c2f239229b2ee9fcbac03cb598d9aebb9196 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:58:52 -0700 Subject: [PATCH 0185/2760] exec/cpu-all: remove exec/cpu-interrupt include Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-8-pierrick.bouvier@linaro.org> --- accel/tcg/cpu-exec.c | 1 + hw/alpha/typhoon.c | 1 + hw/m68k/next-cube.c | 1 + hw/ppc/ppc.c | 1 + hw/xtensa/pic_cpu.c | 1 + include/exec/cpu-all.h | 1 - target/alpha/cpu.h | 1 + target/arm/cpu.h | 1 + target/avr/cpu.h | 1 + target/hppa/cpu.h | 1 + target/i386/cpu.h | 1 + target/loongarch/cpu.h | 1 + target/m68k/cpu.h | 1 + target/microblaze/cpu.h | 1 + target/mips/cpu.h | 1 + target/openrisc/cpu.h | 1 + target/ppc/cpu.h | 1 + target/riscv/cpu.h | 1 + target/rx/cpu.h | 1 + target/s390x/cpu.h | 1 + target/sh4/cpu.h | 1 + target/sparc/cpu.h | 1 + target/xtensa/cpu.h | 1 + 23 files changed, 22 insertions(+), 1 deletion(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 9e15105533..d388be83d0 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -26,6 +26,7 @@ #include "trace.h" #include "disas/disas.h" #include "exec/cpu-common.h" +#include "exec/cpu-interrupt.h" #include "exec/page-protection.h" #include "exec/mmap-lock.h" #include "exec/translation-block.h" diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index e8711ae16a..9718e1a579 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" #include "qemu/units.h" +#include "exec/cpu-interrupt.h" #include "qapi/error.h" #include "hw/pci/pci_host.h" #include "cpu.h" diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 0570e4a76f..4ae5668331 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "exec/hwaddr.h" +#include "exec/cpu-interrupt.h" #include "system/system.h" #include "system/qtest.h" #include "hw/irq.h" diff --git a/hw/ppc/ppc.c b/hw/ppc/ppc.c index 3a80931538..43d0d0e755 100644 --- a/hw/ppc/ppc.c +++ b/hw/ppc/ppc.c @@ -27,6 +27,7 @@ #include "hw/ppc/ppc.h" #include "hw/ppc/ppc_e500.h" #include "qemu/timer.h" +#include "exec/cpu-interrupt.h" #include "system/cpus.h" #include "qemu/log.h" #include "qemu/main-loop.h" diff --git a/hw/xtensa/pic_cpu.c b/hw/xtensa/pic_cpu.c index 8cef88c61b..e388531610 100644 --- a/hw/xtensa/pic_cpu.c +++ b/hw/xtensa/pic_cpu.c @@ -27,6 +27,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/cpu-interrupt.h" #include "hw/irq.h" #include "qemu/log.h" #include "qemu/timer.h" diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index bfa039ab76..7b712b2556 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -20,7 +20,6 @@ #define CPU_ALL_H #include "exec/cpu-common.h" -#include "exec/cpu-interrupt.h" #include "hw/core/cpu.h" #include "exec/cpu-defs.h" #include "exec/target_page.h" diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index 80562adfb5..42788a6a0b 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -22,6 +22,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" #define ICACHE_LINE_SIZE 32 diff --git a/target/arm/cpu.h b/target/arm/cpu.h index a8177c6c2e..958a921490 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -25,6 +25,7 @@ #include "hw/registerfields.h" #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "exec/gdbstub.h" #include "exec/page-protection.h" #include "qapi/qapi-types-common.h" diff --git a/target/avr/cpu.h b/target/avr/cpu.h index b0518a1f60..c2cc2daa66 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -23,6 +23,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "system/memory.h" #ifdef CONFIG_USER_ONLY diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index f6bf068776..dab58c227f 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -22,6 +22,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "system/memory.h" #include "qemu/cpu-float.h" #include "qemu/interval-tree.h" diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 0ad67fe0fd..778dfd9637 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -24,6 +24,7 @@ #include "cpu-qom.h" #include "kvm/hyperv-proto.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "exec/memop.h" #include "hw/i386/topology.h" #include "qapi/qapi-types-common.h" diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 02ef6ddecb..a924aa01d7 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -10,6 +10,7 @@ #include "qemu/int128.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "fpu/softfloat-types.h" #include "hw/registerfields.h" #include "qemu/timer.h" diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index ddb0f29f4a..451644a05a 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -22,6 +22,7 @@ #define M68K_CPU_H #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" #include "cpu-qom.h" diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index e44ddd5307..d29681abed 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -23,6 +23,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" +#include "exec/cpu-interrupt.h" typedef struct CPUArchState CPUMBState; #if !defined(CONFIG_USER_ONLY) diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 9ef72a95d7..29362498ec 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -3,6 +3,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #ifndef CONFIG_USER_ONLY #include "system/memory.h" #endif diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index b97d2ffdd2..c153823b62 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -22,6 +22,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "fpu/softfloat-types.h" /** diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 3ee83517dc..7489ba9564 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -23,6 +23,7 @@ #include "qemu/int128.h" #include "qemu/cpu-float.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "cpu-qom.h" #include "qom/object.h" #include "hw/registerfields.h" diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 51e49e03de..556eda57e9 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -24,6 +24,7 @@ #include "hw/registerfields.h" #include "hw/qdev-properties.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "exec/gdbstub.h" #include "qemu/cpu-float.h" #include "qom/object.h" diff --git a/target/rx/cpu.h b/target/rx/cpu.h index 349d61c4e4..5f2fcb6656 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -24,6 +24,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" #ifdef CONFIG_USER_ONLY diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 5b7992deda..0a32ad4c61 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -28,6 +28,7 @@ #include "cpu-qom.h" #include "cpu_models.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" #include "qapi/qapi-types-machine-common.h" diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index d536d5d715..18557d8c38 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -22,6 +22,7 @@ #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" /* CPU Subtypes */ diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 68f8c21e7c..c0aab69b61 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -4,6 +4,7 @@ #include "qemu/bswap.h" #include "cpu-qom.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" #if !defined(TARGET_SPARC64) diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 8d70bfc0cd..6684631478 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -31,6 +31,7 @@ #include "cpu-qom.h" #include "qemu/cpu-float.h" #include "exec/cpu-defs.h" +#include "exec/cpu-interrupt.h" #include "hw/clock.h" #include "xtensa-isa.h" From 98c7c1469880e5430e709fd20c5d5daed6b3ad75 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:58:56 -0700 Subject: [PATCH 0186/2760] accel/tcg: fix missing includes for TCG_GUEST_DEFAULT_MO We prepare to remove cpu.h from cpu-all.h, which will transitively remove it from accel/tcg/tb-internal.h, and thus from most of tcg compilation units. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-12-pierrick.bouvier@linaro.org> --- accel/tcg/internal-target.h | 1 + accel/tcg/translate-all.c | 1 + include/exec/poison.h | 1 + 3 files changed, 3 insertions(+) diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index c88f007ffb..05abaeb8e0 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -9,6 +9,7 @@ #ifndef ACCEL_TCG_INTERNAL_TARGET_H #define ACCEL_TCG_INTERNAL_TARGET_H +#include "cpu-param.h" #include "exec/exec-all.h" #include "exec/translation-block.h" #include "tb-internal.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 87fb6c51d3..ed41fc5d0c 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -43,6 +43,7 @@ #include "system/ram_addr.h" #endif +#include "cpu-param.h" #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/mmap-lock.h" diff --git a/include/exec/poison.h b/include/exec/poison.h index 4180a5a489..8ec02b40e8 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -37,6 +37,7 @@ #pragma GCC poison TARGET_NAME #pragma GCC poison TARGET_SUPPORTS_MTTCG #pragma GCC poison TARGET_BIG_ENDIAN +#pragma GCC poison TCG_GUEST_DEFAULT_MO #pragma GCC poison TARGET_LONG_BITS #pragma GCC poison TARGET_FMT_lx From 4fadca8d649a2aeef8070b973b2a1d5da75dfefb Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:58:57 -0700 Subject: [PATCH 0187/2760] accel/tcg: fix missing includes for TARGET_HAS_PRECISE_SMC We prepare to remove cpu.h from cpu-all.h, which will transitively remove it from accel/tcg/tb-internal.h, and thus from most of tcg compilation units. Note: this was caught by a test regression for s390x-softmmu. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-13-pierrick.bouvier@linaro.org> --- accel/tcg/tb-maint.c | 1 + accel/tcg/user-exec.c | 1 + include/exec/poison.h | 1 + 3 files changed, 3 insertions(+) diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index df3438e190..d479f53ae0 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu/interval-tree.h" #include "qemu/qtree.h" +#include "cpu.h" #include "exec/cputlb.h" #include "exec/log.h" #include "exec/exec-all.h" diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 72a9809c2d..7f57d8f1af 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "accel/tcg/cpu-ops.h" #include "disas/disas.h" +#include "cpu.h" #include "exec/vaddr.h" #include "exec/exec-all.h" #include "exec/tlb-flags.h" diff --git a/include/exec/poison.h b/include/exec/poison.h index 8ec02b40e8..f267da6083 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -38,6 +38,7 @@ #pragma GCC poison TARGET_SUPPORTS_MTTCG #pragma GCC poison TARGET_BIG_ENDIAN #pragma GCC poison TCG_GUEST_DEFAULT_MO +#pragma GCC poison TARGET_HAS_PRECISE_SMC #pragma GCC poison TARGET_LONG_BITS #pragma GCC poison TARGET_FMT_lx From e9659a4da2e5d75b6d100007efe315150a943146 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:58:58 -0700 Subject: [PATCH 0188/2760] exec/cpu-all: remove cpu include Now we made sure important defines are included using their direct path, we can remove cpu.h from cpu-all.h. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-14-pierrick.bouvier@linaro.org> --- accel/tcg/cpu-exec.c | 1 + include/exec/cpu-all.h | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index d388be83d0..8d2b957a3b 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -36,6 +36,7 @@ #include "exec/log.h" #include "qemu/main-loop.h" #include "exec/cpu-all.h" +#include "cpu.h" #include "exec/icount.h" #include "exec/replay-core.h" #include "system/tcg.h" diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index 7b712b2556..dae4fbcea8 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -23,6 +23,5 @@ #include "hw/core/cpu.h" #include "exec/cpu-defs.h" #include "exec/target_page.h" -#include "cpu.h" #endif /* CPU_ALL_H */ From 9c2ff9cdc9b33472333e9431cbf4417f5f228883 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 31 Mar 2025 16:40:55 -0500 Subject: [PATCH 0189/2760] exec/cpu-all: remove exec/target_page include Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tb-hash.h | 1 + hw/alpha/dp264.c | 1 + hw/arm/boot.c | 1 + hw/arm/smmuv3.c | 1 + hw/avr/atmega.c | 1 + hw/hppa/machine.c | 1 + hw/hyperv/hyperv.c | 1 + hw/hyperv/syndbg.c | 1 + hw/hyperv/vmbus.c | 1 + hw/i386/multiboot.c | 1 + hw/i386/pc.c | 1 + hw/i386/pc_sysfw_ovmf.c | 1 + hw/i386/vapic.c | 1 + hw/loongarch/virt.c | 1 + hw/m68k/q800.c | 1 + hw/m68k/virt.c | 1 + hw/openrisc/boot.c | 1 + hw/pci-host/astro.c | 1 + hw/ppc/e500.c | 1 + hw/ppc/mac_newworld.c | 1 + hw/ppc/mac_oldworld.c | 1 + hw/ppc/ppc_booke.c | 1 + hw/ppc/prep.c | 1 + hw/ppc/spapr_hcall.c | 1 + hw/riscv/riscv-iommu-pci.c | 1 + hw/riscv/riscv-iommu.c | 1 + hw/s390x/ipl.h | 1 + hw/s390x/s390-pci-bus.c | 1 + hw/s390x/s390-pci-inst.c | 1 + hw/s390x/s390-skeys.c | 1 + hw/sparc/sun4m.c | 1 + hw/sparc64/sun4u.c | 1 + include/exec/cpu-all.h | 1 - monitor/hmp-cmds-target.c | 1 + semihosting/uaccess.c | 1 + target/alpha/cpu.c | 1 + target/alpha/helper.c | 1 + target/alpha/translate.c | 1 + target/arm/cpu.c | 1 + target/arm/gdbstub64.c | 1 + target/arm/ptw.c | 1 + target/arm/tcg/helper-a64.c | 1 + target/arm/tcg/op_helper.c | 1 + target/arm/tcg/sve_helper.c | 1 + target/arm/tcg/tlb-insns.c | 1 + target/arm/tcg/translate-a64.c | 2 +- target/arm/tcg/translate.c | 1 + target/avr/helper.c | 1 + target/avr/translate.c | 1 + target/hppa/cpu.c | 1 + target/hppa/mem_helper.c | 1 + target/hppa/translate.c | 1 + target/i386/helper.c | 1 + target/i386/kvm/hyperv.c | 1 + target/i386/kvm/kvm.c | 1 + target/i386/kvm/xen-emu.c | 1 + target/i386/sev.c | 1 + target/i386/tcg/access.c | 1 + target/i386/tcg/mpx_helper.c | 1 + target/i386/tcg/system/excp_helper.c | 1 + target/i386/tcg/tcg-cpu.c | 2 +- target/i386/tcg/translate.c | 1 + target/loongarch/cpu_helper.c | 1 + target/loongarch/tcg/tlb_helper.c | 1 + target/loongarch/tcg/translate.c | 1 + target/m68k/helper.c | 1 + target/m68k/translate.c | 1 + target/microblaze/helper.c | 1 + target/microblaze/mmu.c | 1 + target/microblaze/translate.c | 1 + target/mips/tcg/msa_helper.c | 1 + target/mips/tcg/system/cp0_helper.c | 1 + target/mips/tcg/system/tlb_helper.c | 1 + target/mips/tcg/translate.c | 1 + target/openrisc/mmu.c | 1 + target/openrisc/sys_helper.c | 1 + target/openrisc/translate.c | 2 +- target/ppc/mem_helper.c | 1 + target/ppc/mmu-hash32.c | 1 + target/ppc/mmu_common.c | 1 + target/ppc/mmu_helper.c | 1 + target/ppc/translate.c | 1 + target/riscv/cpu_helper.c | 1 + target/riscv/pmp.c | 1 + target/riscv/tcg/tcg-cpu.c | 1 + target/riscv/translate.c | 2 +- target/riscv/vector_helper.c | 1 + target/rx/cpu.c | 1 + target/s390x/helper.c | 1 + target/s390x/ioinst.c | 1 + target/s390x/mmu_helper.c | 1 + target/s390x/tcg/excp_helper.c | 1 + target/s390x/tcg/mem_helper.c | 1 + target/s390x/tcg/misc_helper.c | 1 + target/sh4/helper.c | 1 + target/sh4/translate.c | 1 + target/sparc/ldst_helper.c | 1 + target/sparc/mmu_helper.c | 1 + target/sparc/translate.c | 1 + target/tricore/helper.c | 1 + target/tricore/translate.c | 1 + target/xtensa/helper.c | 1 + target/xtensa/mmu_helper.c | 1 + target/xtensa/translate.c | 1 + target/xtensa/xtensa-semi.c | 1 + 105 files changed, 104 insertions(+), 5 deletions(-) diff --git a/accel/tcg/tb-hash.h b/accel/tcg/tb-hash.h index a5382f460d..3bc5042d9d 100644 --- a/accel/tcg/tb-hash.h +++ b/accel/tcg/tb-hash.h @@ -22,6 +22,7 @@ #include "exec/cpu-defs.h" #include "exec/exec-all.h" +#include "exec/target_page.h" #include "exec/translation-block.h" #include "qemu/xxhash.h" #include "tb-jmp-cache.h" diff --git a/hw/alpha/dp264.c b/hw/alpha/dp264.c index 570ea9edf2..19562b5967 100644 --- a/hw/alpha/dp264.c +++ b/hw/alpha/dp264.c @@ -8,6 +8,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/target_page.h" #include "elf.h" #include "hw/loader.h" #include "alpha_sys.h" diff --git a/hw/arm/boot.c b/hw/arm/boot.c index e296b62fa1..d3811b896f 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -14,6 +14,7 @@ #include #include "hw/arm/boot.h" #include "hw/arm/linux-boot-if.h" +#include "exec/target_page.h" #include "system/kvm.h" #include "system/tcg.h" #include "system/system.h" diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 1a96287ba9..4362ae6aa1 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -25,6 +25,7 @@ #include "hw/qdev-core.h" #include "hw/pci/pci.h" #include "cpu.h" +#include "exec/target_page.h" #include "trace.h" #include "qemu/log.h" #include "qemu/error-report.h" diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index 2e8b8e8c67..c105d2a97c 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -12,6 +12,7 @@ #include "qemu/module.h" #include "qemu/units.h" #include "qapi/error.h" +#include "exec/target_page.h" #include "system/memory.h" #include "system/address-spaces.h" #include "system/system.h" diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index c5f247633e..c430bf28dd 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -11,6 +11,7 @@ #include "elf.h" #include "hw/loader.h" #include "qemu/error-report.h" +#include "exec/target_page.h" #include "system/reset.h" #include "system/system.h" #include "system/qtest.h" diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index d21e428eae..c487f13e2f 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -25,6 +25,7 @@ #include "target/i386/kvm/hyperv-proto.h" #include "target/i386/cpu.h" #include "exec/cpu-all.h" +#include "exec/target_page.h" struct SynICState { DeviceState parent_obj; diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c index d3e3917077..a410b55b9a 100644 --- a/hw/hyperv/syndbg.c +++ b/hw/hyperv/syndbg.c @@ -15,6 +15,7 @@ #include "hw/qdev-properties.h" #include "hw/loader.h" #include "cpu.h" +#include "exec/target_page.h" #include "hw/hyperv/hyperv.h" #include "hw/hyperv/vmbus-bridge.h" #include "hw/hyperv/hyperv-proto.h" diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index 12a7dc4312..06649b2a2e 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "exec/target_page.h" #include "qapi/error.h" #include "migration/vmstate.h" #include "hw/qdev-properties.h" diff --git a/hw/i386/multiboot.c b/hw/i386/multiboot.c index cd07a05861..6e6b96bc34 100644 --- a/hw/i386/multiboot.c +++ b/hw/i386/multiboot.c @@ -29,6 +29,7 @@ #include "multiboot.h" #include "hw/loader.h" #include "elf.h" +#include "exec/target_page.h" #include "system/system.h" #include "qemu/error-report.h" diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 1b5d55e96d..5481fe40be 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "exec/target_page.h" #include "hw/i386/pc.h" #include "hw/char/serial-isa.h" #include "hw/char/parallel.h" diff --git a/hw/i386/pc_sysfw_ovmf.c b/hw/i386/pc_sysfw_ovmf.c index 07a4c267fa..da947c3ca4 100644 --- a/hw/i386/pc_sysfw_ovmf.c +++ b/hw/i386/pc_sysfw_ovmf.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "hw/i386/pc.h" +#include "exec/target_page.h" #include "cpu.h" #define OVMF_TABLE_FOOTER_GUID "96b582de-1fb2-45f7-baea-a366c55a082d" diff --git a/hw/i386/vapic.c b/hw/i386/vapic.c index 26aae64e5d..347431eeef 100644 --- a/hw/i386/vapic.c +++ b/hw/i386/vapic.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "qemu/module.h" +#include "exec/target_page.h" #include "system/system.h" #include "system/cpus.h" #include "system/hw_accel.h" diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index f1eb42c2c1..39ea5cadd6 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -8,6 +8,7 @@ #include "qemu/units.h" #include "qemu/datadir.h" #include "qapi/error.h" +#include "exec/target_page.h" #include "hw/boards.h" #include "hw/char/serial-mm.h" #include "system/kvm.h" diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index aeed4c8ddb..c2e365a820 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -24,6 +24,7 @@ #include "qemu/units.h" #include "qemu/datadir.h" #include "qemu/guest-random.h" +#include "exec/target_page.h" #include "system/system.h" #include "cpu.h" #include "hw/boards.h" diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index 295a614e16..e74d709a18 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qemu/guest-random.h" +#include "exec/target_page.h" #include "system/system.h" #include "cpu.h" #include "hw/boards.h" diff --git a/hw/openrisc/boot.c b/hw/openrisc/boot.c index 0a5881be31..c81efe8138 100644 --- a/hw/openrisc/boot.c +++ b/hw/openrisc/boot.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/cpu-defs.h" +#include "exec/target_page.h" #include "elf.h" #include "hw/loader.h" #include "hw/openrisc/boot.h" diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c index 039cc3ad01..c6f2d4f494 100644 --- a/hw/pci-host/astro.c +++ b/hw/pci-host/astro.c @@ -35,6 +35,7 @@ #include "target/hppa/cpu.h" #include "trace.h" #include "qom/object.h" +#include "exec/target_page.h" /* * Helper functions diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 69269aa24c..809078a2c3 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -18,6 +18,7 @@ #include "qemu/datadir.h" #include "qemu/units.h" #include "qemu/guest-random.h" +#include "exec/target_page.h" #include "qapi/error.h" #include "e500.h" #include "e500-ccsr.h" diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 2d5309d6f5..21b2fc569a 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -50,6 +50,7 @@ #include "qemu/datadir.h" #include "qemu/units.h" #include "qapi/error.h" +#include "exec/target_page.h" #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" #include "hw/nvram/mac_nvram.h" diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index b5814690f5..0d34e6bfda 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -28,6 +28,7 @@ #include "qemu/datadir.h" #include "qemu/units.h" #include "qapi/error.h" +#include "exec/target_page.h" #include "hw/ppc/ppc.h" #include "hw/qdev-properties.h" #include "hw/boards.h" diff --git a/hw/ppc/ppc_booke.c b/hw/ppc/ppc_booke.c index 925e670ba0..3872ae2822 100644 --- a/hw/ppc/ppc_booke.c +++ b/hw/ppc/ppc_booke.c @@ -24,6 +24,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/target_page.h" #include "hw/ppc/ppc.h" #include "qemu/timer.h" #include "system/reset.h" diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 3e68d8e6e2..739526335c 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -39,6 +39,7 @@ #include "hw/rtc/mc146818rtc.h" #include "hw/isa/pc87312.h" #include "hw/qdev-properties.h" +#include "exec/target_page.h" #include "system/kvm.h" #include "system/reset.h" #include "trace.h" diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 406aea4ecb..a4f399c4ff 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -9,6 +9,7 @@ #include "qemu/module.h" #include "qemu/error-report.h" #include "exec/tb-flush.h" +#include "exec/target_page.h" #include "helper_regs.h" #include "hw/ppc/ppc.h" #include "hw/ppc/spapr.h" diff --git a/hw/riscv/riscv-iommu-pci.c b/hw/riscv/riscv-iommu-pci.c index 12451869e4..a795464803 100644 --- a/hw/riscv/riscv-iommu-pci.c +++ b/hw/riscv/riscv-iommu-pci.c @@ -17,6 +17,7 @@ */ #include "qemu/osdep.h" +#include "exec/target_page.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/pci/pci_bus.h" diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 76e0fcd873..65411b3e4c 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -18,6 +18,7 @@ #include "qemu/osdep.h" #include "qom/object.h" +#include "exec/target_page.h" #include "hw/pci/pci_bus.h" #include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index 6557ac3be5..cb55101f06 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -14,6 +14,7 @@ #define HW_S390_IPL_H #include "cpu.h" +#include "exec/target_page.h" #include "system/address-spaces.h" #include "system/memory.h" #include "hw/qdev-core.h" diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 2591ee49c1..4365f8ed1e 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -14,6 +14,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qapi/visitor.h" +#include "exec/target_page.h" #include "hw/s390x/s390-pci-bus.h" #include "hw/s390x/s390-pci-inst.h" #include "hw/s390x/s390-pci-kvm.h" diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index b4e003c19c..b5dddb22b8 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "exec/memop.h" +#include "exec/target_page.h" #include "system/memory.h" #include "qemu/error-report.h" #include "system/hw_accel.h" diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index 0bb90fed04..d437fe070d 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "exec/target_page.h" #include "hw/s390x/s390-virtio-ccw.h" #include "hw/qdev-properties.h" #include "hw/s390x/storage-keys.h" diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index 5aaafb40da..edbf19d958 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -27,6 +27,7 @@ #include "qapi/error.h" #include "qemu/datadir.h" #include "cpu.h" +#include "exec/target_page.h" #include "hw/sysbus.h" #include "qemu/error-report.h" #include "qemu/timer.h" diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index d3cb7270ff..becdf3ea98 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -28,6 +28,7 @@ #include "qapi/error.h" #include "qemu/datadir.h" #include "cpu.h" +#include "exec/target_page.h" #include "hw/irq.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bridge.h" diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index dae4fbcea8..e7c8b8672f 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -22,6 +22,5 @@ #include "exec/cpu-common.h" #include "hw/core/cpu.h" #include "exec/cpu-defs.h" -#include "exec/target_page.h" #endif /* CPU_ALL_H */ diff --git a/monitor/hmp-cmds-target.c b/monitor/hmp-cmds-target.c index 011a367357..8eaf70d9c9 100644 --- a/monitor/hmp-cmds-target.c +++ b/monitor/hmp-cmds-target.c @@ -31,6 +31,7 @@ #include "qapi/error.h" #include "qobject/qdict.h" #include "system/hw_accel.h" +#include "exec/target_page.h" /* Set the current CPU defined by the user. Callers must hold BQL. */ int monitor_set_cpu(Monitor *mon, int cpu_index) diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index ccb0c96070..f51a253626 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -11,6 +11,7 @@ #include "exec/cpu-all.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/exec-all.h" +#include "exec/target_page.h" #include "exec/tlb-flags.h" #include "semihosting/uaccess.h" diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 56c96b1c4d..99d839a279 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -25,6 +25,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/translation-block.h" +#include "exec/target_page.h" #include "fpu/softfloat.h" diff --git a/target/alpha/helper.c b/target/alpha/helper.c index f6261a3a53..096eac3445 100644 --- a/target/alpha/helper.c +++ b/target/alpha/helper.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "exec/cputlb.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "fpu/softfloat-types.h" #include "exec/helper-proto.h" #include "qemu/qemu-print.h" diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 2156c02214..7f3195a5dc 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -27,6 +27,7 @@ #include "exec/helper-gen.h" #include "exec/translator.h" #include "exec/translation-block.h" +#include "exec/target_page.h" #include "exec/log.h" #define HELPER_H "helper.h" diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 21e8cf1400..c9e043bc9b 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -34,6 +34,7 @@ #include "internals.h" #include "cpu-features.h" #include "exec/exec-all.h" +#include "exec/target_page.h" #include "hw/qdev-properties.h" #if !defined(CONFIG_USER_ONLY) #include "hw/loader.h" diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index be38016fc7..64ee9b3b56 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -29,6 +29,7 @@ #endif #ifdef CONFIG_TCG #include "accel/tcg/cpu-mmu-index.h" +#include "exec/target_page.h" #endif int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 8d4e9e07a9..e0e82ae507 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -12,6 +12,7 @@ #include "qemu/main-loop.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/tlb-flags.h" #include "cpu.h" #include "internals.h" diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index fa79d19425..507dbc1a44 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -31,6 +31,7 @@ #include "exec/cpu-common.h" #include "exec/exec-all.h" #include "exec/cpu_ldst.h" +#include "exec/target_page.h" #include "exec/tlb-flags.h" #include "qemu/int128.h" #include "qemu/atomic128.h" diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 30786fd1ff..71ba406782 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -20,6 +20,7 @@ #include "qemu/main-loop.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "exec/target_page.h" #include "internals.h" #include "cpu-features.h" #include "exec/exec-all.h" diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index e3bed77b48..9b0d40c9e1 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -23,6 +23,7 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/helper-proto.h" +#include "exec/target_page.h" #include "exec/tlb-flags.h" #include "tcg/tcg-gvec-desc.h" #include "fpu/softfloat.h" diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index 630a481f0f..0407ad5542 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -8,6 +8,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "exec/cputlb.h" +#include "exec/target_page.h" #include "cpu.h" #include "internals.h" #include "cpu-features.h" diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 39014325df..43408c71bb 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -17,8 +17,8 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" - #include "exec/exec-all.h" +#include "exec/target_page.h" #include "translate.h" #include "translate-a64.h" #include "qemu/log.h" diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index d280018138..273b860d57 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -27,6 +27,7 @@ #include "semihosting/semihost.h" #include "cpregs.h" #include "exec/helper-proto.h" +#include "exec/target_page.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" diff --git a/target/avr/helper.c b/target/avr/helper.c index f23fa3e8ba..32cbf17919 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -26,6 +26,7 @@ #include "accel/tcg/getpc.h" #include "exec/cputlb.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" diff --git a/target/avr/translate.c b/target/avr/translate.c index 0490936cd5..b9c592c899 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -29,6 +29,7 @@ #include "exec/helper-gen.h" #include "exec/log.h" #include "exec/translator.h" +#include "exec/target_page.h" #define HELPER_H "helper.h" #include "exec/helper-info.c.inc" diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 09a6aaa3dd..51bff0c5d6 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -26,6 +26,7 @@ #include "qemu/module.h" #include "exec/exec-all.h" #include "exec/translation-block.h" +#include "exec/target_page.h" #include "fpu/softfloat.h" #include "tcg/tcg.h" #include "hw/hppa/hppa_hardware.h" diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index a1ade9079e..554d7bf4d1 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -24,6 +24,7 @@ #include "exec/cputlb.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/helper-proto.h" #include "hw/core/cpu.h" #include "trace.h" diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 0d0d1bc99b..14f3833322 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -28,6 +28,7 @@ #include "exec/helper-gen.h" #include "exec/translator.h" #include "exec/translation-block.h" +#include "exec/target_page.h" #include "exec/log.h" #define HELPER_H "helper.h" diff --git a/target/i386/helper.c b/target/i386/helper.c index 64d9e8ab9c..197fdac7dd 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "exec/cputlb.h" #include "exec/translation-block.h" +#include "exec/target_page.h" #include "system/runstate.h" #ifndef CONFIG_USER_ONLY #include "system/hw_accel.h" diff --git a/target/i386/kvm/hyperv.c b/target/i386/kvm/hyperv.c index 70b89cacf9..9865120cc4 100644 --- a/target/i386/kvm/hyperv.c +++ b/target/i386/kvm/hyperv.c @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" +#include "exec/target_page.h" #include "hyperv.h" #include "hw/hyperv/hyperv.h" #include "hyperv-proto.h" diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 6c749d4ee8..c9a3c02e3e 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -67,6 +67,7 @@ #include "hw/pci/msix.h" #include "migration/blocker.h" #include "exec/memattrs.h" +#include "exec/target_page.h" #include "trace.h" #include CONFIG_DEVICES diff --git a/target/i386/kvm/xen-emu.c b/target/i386/kvm/xen-emu.c index b23010374f..284c5ef6f6 100644 --- a/target/i386/kvm/xen-emu.c +++ b/target/i386/kvm/xen-emu.c @@ -13,6 +13,7 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/error-report.h" +#include "exec/target_page.h" #include "hw/xen/xen.h" #include "system/kvm_int.h" #include "system/kvm_xen.h" diff --git a/target/i386/sev.c b/target/i386/sev.c index ba88976e9f..878dd20f2c 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -26,6 +26,7 @@ #include "qemu/uuid.h" #include "qemu/error-report.h" #include "crypto/hash.h" +#include "exec/target_page.h" #include "system/kvm.h" #include "kvm/kvm_i386.h" #include "sev.h" diff --git a/target/i386/tcg/access.c b/target/i386/tcg/access.c index e68b73a24b..5a4721dcee 100644 --- a/target/i386/tcg/access.c +++ b/target/i386/tcg/access.c @@ -5,6 +5,7 @@ #include "cpu.h" #include "exec/cpu_ldst.h" #include "exec/exec-all.h" +#include "exec/target_page.h" #include "access.h" diff --git a/target/i386/tcg/mpx_helper.c b/target/i386/tcg/mpx_helper.c index 22423eedcd..b942665adc 100644 --- a/target/i386/tcg/mpx_helper.c +++ b/target/i386/tcg/mpx_helper.c @@ -22,6 +22,7 @@ #include "exec/helper-proto.h" #include "exec/cpu_ldst.h" #include "exec/exec-all.h" +#include "exec/target_page.h" #include "helper-tcg.h" diff --git a/target/i386/tcg/system/excp_helper.c b/target/i386/tcg/system/excp_helper.c index 4badd73943..a563c9b35e 100644 --- a/target/i386/tcg/system/excp_helper.c +++ b/target/i386/tcg/system/excp_helper.c @@ -22,6 +22,7 @@ #include "exec/cpu_ldst.h" #include "exec/cputlb.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/tlb-flags.h" #include "exec/tswap.h" #include "tcg/helper-tcg.h" diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 818653ee6d..35b17f2b18 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -23,7 +23,7 @@ #include "qemu/accel.h" #include "accel/accel-cpu-target.h" #include "exec/translation-block.h" - +#include "exec/target_page.h" #include "tcg-cpu.h" /* Frob eflags into and out of the CPU temporary format. */ diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 6418d4bb03..1dcc35f5df 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -26,6 +26,7 @@ #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "exec/translator.h" +#include "exec/target_page.h" #include "fpu/softfloat.h" #include "exec/helper-proto.h" diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index f8965cd155..bb343078bf 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "accel/tcg/cpu-mmu-index.h" +#include "exec/target_page.h" #include "internals.h" #include "cpu-csr.h" diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 70d1b5cf99..0d6c9844a6 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -15,6 +15,7 @@ #include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/cpu_ldst.h" #include "exec/log.h" #include "cpu-csr.h" diff --git a/target/loongarch/tcg/translate.c b/target/loongarch/tcg/translate.c index e59e4ed25b..53a0b4c3ce 100644 --- a/target/loongarch/tcg/translate.c +++ b/target/loongarch/tcg/translate.c @@ -11,6 +11,7 @@ #include "tcg/tcg-op-gvec.h" #include "exec/translation-block.h" #include "exec/translator.h" +#include "exec/target_page.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/log.h" diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 8251272219..f73e0def23 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -23,6 +23,7 @@ #include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/gdbstub.h" #include "exec/helper-proto.h" #include "system/memory.h" diff --git a/target/m68k/translate.c b/target/m68k/translate.c index dec2967fce..b1266a7875 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/translation-block.h" +#include "exec/target_page.h" #include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/qemu-print.h" diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index 022c98f0c3..9203192483 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -23,6 +23,7 @@ #include "exec/cputlb.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "qemu/host-utils.h" #include "exec/log.h" diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 2d18659b99..95a12e16f8 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -24,6 +24,7 @@ #include "exec/cputlb.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/page-protection.h" +#include "exec/target_page.h" static unsigned int tlb_decode_size(unsigned int f) { diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index b54e5ac4b2..4bb867c969 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -27,6 +27,7 @@ #include "exec/helper-gen.h" #include "exec/translator.h" #include "exec/translation-block.h" +#include "exec/target_page.h" #include "qemu/qemu-print.h" #include "exec/log.h" diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index 74fb80cc25..969dd34b3e 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -25,6 +25,7 @@ #include "exec/cpu_ldst.h" #include "exec/helper-proto.h" #include "exec/memop.h" +#include "exec/target_page.h" #include "fpu/softfloat.h" #include "fpu_helper.h" diff --git a/target/mips/tcg/system/cp0_helper.c b/target/mips/tcg/system/cp0_helper.c index 78e422b0ca..101b1e65fd 100644 --- a/target/mips/tcg/system/cp0_helper.c +++ b/target/mips/tcg/system/cp0_helper.c @@ -28,6 +28,7 @@ #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "exec/cputlb.h" +#include "exec/target_page.h" /* SMP helpers. */ diff --git a/target/mips/tcg/system/tlb_helper.c b/target/mips/tcg/system/tlb_helper.c index df80301a41..d239fa9353 100644 --- a/target/mips/tcg/system/tlb_helper.c +++ b/target/mips/tcg/system/tlb_helper.c @@ -24,6 +24,7 @@ #include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/cpu_ldst.h" #include "exec/log.h" #include "exec/helper-proto.h" diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 78b848a6d9..8658315f93 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -27,6 +27,7 @@ #include "internal.h" #include "exec/helper-proto.h" #include "exec/translation-block.h" +#include "exec/target_page.h" #include "semihosting/semihost.h" #include "trace.h" #include "fpu_helper.h" diff --git a/target/openrisc/mmu.c b/target/openrisc/mmu.c index 47ac783c52..acea50c41e 100644 --- a/target/openrisc/mmu.c +++ b/target/openrisc/mmu.c @@ -23,6 +23,7 @@ #include "cpu.h" #include "exec/cputlb.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "gdbstub/helpers.h" #include "qemu/host-utils.h" #include "hw/loader.h" diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index 21bc137ccc..92badf017f 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/cputlb.h" +#include "exec/target_page.h" #include "exec/helper-proto.h" #include "exception.h" #ifndef CONFIG_USER_ONLY diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index da033bffff..d4ce60188b 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -28,7 +28,7 @@ #include "qemu/qemu-print.h" #include "exec/translator.h" #include "exec/translation-block.h" - +#include "exec/target_page.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 51b137febd..0967624afe 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" +#include "exec/target_page.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "helper_regs.h" diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 1f791a7f2f..5bd3efe70e 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "system/kvm.h" #include "kvm_ppc.h" #include "internal.h" diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index fb62b947f1..394a0c9bb6 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -26,6 +26,7 @@ #include "mmu-hash32.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/log.h" #include "helper_regs.h" #include "qemu/error-report.h" diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index ad9ba8294c..c90ceb7d60 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -27,6 +27,7 @@ #include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/log.h" #include "helper_regs.h" #include "qemu/error-report.h" diff --git a/target/ppc/translate.c b/target/ppc/translate.c index a52cbc869a..399107d319 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -22,6 +22,7 @@ #include "cpu.h" #include "internal.h" #include "exec/exec-all.h" +#include "exec/target_page.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "qemu/host-utils.h" diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index ca58094fb5..619c76cc00 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -26,6 +26,7 @@ #include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "system/memory.h" #include "instmap.h" #include "tcg/tcg-op.h" diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index b0841d44f4..c13a117e3f 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -26,6 +26,7 @@ #include "trace.h" #include "exec/cputlb.h" #include "exec/page-protection.h" +#include "exec/target_page.h" static bool pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, uint8_t val); diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index bee7dfd803..710449d17e 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -22,6 +22,7 @@ #include "exec/translation-block.h" #include "tcg-cpu.h" #include "cpu.h" +#include "exec/target_page.h" #include "internals.h" #include "pmu.h" #include "time_helper.h" diff --git a/target/riscv/translate.c b/target/riscv/translate.c index d6651f244f..cef61b5b29 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -23,7 +23,7 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" - +#include "exec/target_page.h" #include "exec/translator.h" #include "exec/translation-block.h" #include "exec/log.h" diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 7fffa23bc8..7de6cbae5c 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -26,6 +26,7 @@ #include "exec/page-protection.h" #include "exec/helper-proto.h" #include "exec/tlb-flags.h" +#include "exec/target_page.h" #include "exec/tswap.h" #include "fpu/softfloat.h" #include "tcg/tcg-gvec-desc.h" diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 51743020d4..e14d9cbef9 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -24,6 +24,7 @@ #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/translation-block.h" +#include "exec/target_page.h" #include "hw/loader.h" #include "fpu/softfloat.h" #include "tcg/debug-assert.h" diff --git a/target/s390x/helper.c b/target/s390x/helper.c index e660c69f60..3c57c32e47 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -27,6 +27,7 @@ #include "target/s390x/kvm/pv.h" #include "system/hw_accel.h" #include "system/runstate.h" +#include "exec/target_page.h" #include "exec/watchpoint.h" void s390x_tod_timer(void *opaque) diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index a944f16c25..fe62ba5b06 100644 --- a/target/s390x/ioinst.c +++ b/target/s390x/ioinst.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "cpu.h" +#include "exec/target_page.h" #include "s390x-internal.h" #include "hw/s390x/ioinst.h" #include "trace.h" diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index b079d120db..0e133cb9a5 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -25,6 +25,7 @@ #include "system/tcg.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "hw/hw.h" #include "hw/s390x/storage-keys.h" #include "hw/boards.h" diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index 1d51043e88..6cd813e1ab 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -24,6 +24,7 @@ #include "exec/helper-proto.h" #include "exec/cputlb.h" #include "exec/exec-all.h" +#include "exec/target_page.h" #include "exec/watchpoint.h" #include "s390x-internal.h" #include "tcg_s390x.h" diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 0ff2e10d81..d5eece4384 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -29,6 +29,7 @@ #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/cpu_ldst.h" +#include "exec/target_page.h" #include "exec/tlb-flags.h" #include "accel/tcg/cpu-ops.h" #include "qemu/int128.h" diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index 31266aeda4..e02f443850 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -29,6 +29,7 @@ #include "exec/exec-all.h" #include "exec/cputlb.h" #include "exec/cpu_ldst.h" +#include "exec/target_page.h" #include "qapi/error.h" #include "tcg_s390x.h" #include "s390-tod.h" diff --git a/target/sh4/helper.c b/target/sh4/helper.c index 7567e6c8b6..b41d14d5d7 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -23,6 +23,7 @@ #include "exec/cputlb.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/log.h" #if !defined(CONFIG_USER_ONLY) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index bcdd558818..5ce477d0ad 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -25,6 +25,7 @@ #include "exec/helper-gen.h" #include "exec/translation-block.h" #include "exec/translator.h" +#include "exec/target_page.h" #include "exec/log.h" #include "qemu/qemu-print.h" diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 8890d2b119..3fa5e78816 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -26,6 +26,7 @@ #include "exec/exec-all.h" #include "exec/cputlb.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/cpu_ldst.h" #include "system/memory.h" #ifdef CONFIG_USER_ONLY diff --git a/target/sparc/mmu_helper.c b/target/sparc/mmu_helper.c index c5d82a0854..217580a4d8 100644 --- a/target/sparc/mmu_helper.c +++ b/target/sparc/mmu_helper.c @@ -23,6 +23,7 @@ #include "exec/cputlb.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "exec/tlb-flags.h" #include "system/memory.h" #include "qemu/qemu-print.h" diff --git a/target/sparc/translate.c b/target/sparc/translate.c index bfe63649db..adebddf27b 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -23,6 +23,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" +#include "exec/target_page.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "exec/helper-gen.h" diff --git a/target/tricore/helper.c b/target/tricore/helper.c index b1ee126112..e4c53d453d 100644 --- a/target/tricore/helper.c +++ b/target/tricore/helper.c @@ -22,6 +22,7 @@ #include "exec/cputlb.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "fpu/softfloat-helpers.h" #include "qemu/qemu-print.h" diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 6819b77668..5c7ed395ca 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -31,6 +31,7 @@ #include "tricore-opcodes.h" #include "exec/translator.h" #include "exec/translation-block.h" +#include "exec/target_page.h" #include "exec/log.h" #define HELPER_H "helper.h" diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c index 4824b97e37..d02d16f9ec 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -29,6 +29,7 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/cputlb.h" +#include "exec/target_page.h" #include "gdbstub/helpers.h" #include "exec/helper-proto.h" #include "qemu/error-report.h" diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c index 1ce125794d..a7dd810055 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -36,6 +36,7 @@ #include "accel/tcg/cpu-mmu-index.h" #include "exec/exec-all.h" #include "exec/page-protection.h" +#include "exec/target_page.h" #include "system/memory.h" #define XTENSA_MPU_SEGMENT_MASK 0x0000001f diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index cb817b3119..5ebd4a512c 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -37,6 +37,7 @@ #include "qemu/qemu-print.h" #include "exec/translator.h" #include "exec/translation-block.h" +#include "exec/target_page.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/log.h" diff --git a/target/xtensa/xtensa-semi.c b/target/xtensa/xtensa-semi.c index 2ded8e5634..636f421da2 100644 --- a/target/xtensa/xtensa-semi.c +++ b/target/xtensa/xtensa-semi.c @@ -29,6 +29,7 @@ #include "cpu.h" #include "chardev/char-fe.h" #include "exec/helper-proto.h" +#include "exec/target_page.h" #include "semihosting/semihost.h" #include "semihosting/uaccess.h" #include "qapi/error.h" From d97c3b06de5bdd885f0ee3d8153326acd3db480e Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:58:59 -0700 Subject: [PATCH 0190/2760] exec/cpu-all: transfer exec/cpu-common include to cpu.h headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-15-pierrick.bouvier@linaro.org> --- cpu-target.c | 1 + include/exec/cpu-all.h | 1 - include/exec/cpu_ldst.h | 1 + target/alpha/cpu.h | 1 + target/arm/cpu.h | 1 + target/avr/cpu.h | 1 + target/hexagon/cpu.h | 1 + target/hppa/cpu.h | 1 + target/i386/cpu.h | 1 + target/loongarch/cpu.h | 1 + target/m68k/cpu.h | 1 + target/microblaze/cpu.h | 1 + target/mips/cpu.h | 1 + target/openrisc/cpu.h | 1 + target/ppc/cpu.h | 1 + target/riscv/cpu.h | 1 + target/rx/cpu.h | 1 + target/s390x/cpu.h | 1 + target/sh4/cpu.h | 1 + target/sparc/cpu.h | 1 + target/tricore/cpu.h | 1 + target/xtensa/cpu.h | 1 + 22 files changed, 21 insertions(+), 1 deletion(-) diff --git a/cpu-target.c b/cpu-target.c index 7f3b244ed1..14cd623bff 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -24,6 +24,7 @@ #include "qemu/qemu-print.h" #include "system/accel-ops.h" #include "system/cpus.h" +#include "exec/cpu-common.h" #include "exec/tswap.h" #include "exec/replay-core.h" #include "exec/log.h" diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h index e7c8b8672f..5122fdbee3 100644 --- a/include/exec/cpu-all.h +++ b/include/exec/cpu-all.h @@ -19,7 +19,6 @@ #ifndef CPU_ALL_H #define CPU_ALL_H -#include "exec/cpu-common.h" #include "hw/core/cpu.h" #include "exec/cpu-defs.h" diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 77dc5ac61c..63847f6e61 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -66,6 +66,7 @@ #error Can only include this header with TCG #endif +#include "exec/cpu-common.h" #include "exec/cpu-ldst-common.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/abi_ptr.h" diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index 42788a6a0b..fb1d63527e 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -21,6 +21,7 @@ #define ALPHA_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 958a921490..ee92476814 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -24,6 +24,7 @@ #include "qemu/cpu-float.h" #include "hw/registerfields.h" #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "exec/gdbstub.h" diff --git a/target/avr/cpu.h b/target/avr/cpu.h index c2cc2daa66..a0fb40141a 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -22,6 +22,7 @@ #define QEMU_AVR_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "system/memory.h" diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index f78c8f9c2a..e4fc35b112 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -21,6 +21,7 @@ #include "fpu/softfloat-types.h" #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "hex_regs.h" #include "mmvec/mmvec.h" diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index dab58c227f..4e72ab025b 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -21,6 +21,7 @@ #define HPPA_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "system/memory.h" diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 778dfd9637..02ea87347a 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -23,6 +23,7 @@ #include "system/tcg.h" #include "cpu-qom.h" #include "kvm/hyperv-proto.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "exec/memop.h" diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index a924aa01d7..69117c602a 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -9,6 +9,7 @@ #define LOONGARCH_CPU_H #include "qemu/int128.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "fpu/softfloat-types.h" diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 451644a05a..5347fbe397 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -21,6 +21,7 @@ #ifndef M68K_CPU_H #define M68K_CPU_H +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index d29681abed..90d820b90c 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -21,6 +21,7 @@ #define MICROBLAZE_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" #include "exec/cpu-interrupt.h" diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 29362498ec..79f8041ced 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -2,6 +2,7 @@ #define MIPS_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #ifndef CONFIG_USER_ONLY diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index c153823b62..f16a070ef6 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -21,6 +21,7 @@ #define OPENRISC_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "fpu/softfloat-types.h" diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 7489ba9564..aa5df47bda 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -22,6 +22,7 @@ #include "qemu/int128.h" #include "qemu/cpu-float.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "cpu-qom.h" diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 556eda57e9..14a6779b4c 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -23,6 +23,7 @@ #include "hw/core/cpu.h" #include "hw/registerfields.h" #include "hw/qdev-properties.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "exec/gdbstub.h" diff --git a/target/rx/cpu.h b/target/rx/cpu.h index 5f2fcb6656..e2ec78835e 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -23,6 +23,7 @@ #include "hw/registerfields.h" #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 0a32ad4c61..83d01d5c4e 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -27,6 +27,7 @@ #include "cpu-qom.h" #include "cpu_models.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index 18557d8c38..7581f5eecb 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -21,6 +21,7 @@ #define SH4_CPU_H #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index c0aab69b61..b87351a666 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -3,6 +3,7 @@ #include "qemu/bswap.h" #include "cpu-qom.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "qemu/cpu-float.h" diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index cf9dbc6df8..abb9cba136 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -22,6 +22,7 @@ #include "cpu-qom.h" #include "hw/registerfields.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "qemu/cpu-float.h" #include "tricore-defs.h" diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index 6684631478..c5d2042de1 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -30,6 +30,7 @@ #include "cpu-qom.h" #include "qemu/cpu-float.h" +#include "exec/cpu-common.h" #include "exec/cpu-defs.h" #include "exec/cpu-interrupt.h" #include "hw/clock.h" From 0df783b2fbeca9aa3cc19adafb9a4ec7f97e3a6d Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:00 -0700 Subject: [PATCH 0191/2760] exec/cpu-all: remove this header Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-16-pierrick.bouvier@linaro.org> --- accel/tcg/cpu-exec.c | 1 - hw/hyperv/hyperv.c | 1 - include/exec/cpu-all.h | 25 ------------------------- include/hw/core/cpu.h | 2 +- include/qemu/bswap.h | 3 --- semihosting/uaccess.c | 1 - target/alpha/cpu.h | 2 -- target/arm/cpu.h | 2 -- target/avr/cpu.h | 2 -- target/hexagon/cpu.h | 2 -- target/hppa/cpu.h | 2 -- target/i386/cpu.h | 1 - target/loongarch/cpu.h | 2 -- target/m68k/cpu.h | 2 -- target/microblaze/cpu.h | 2 -- target/mips/cpu.h | 2 -- target/openrisc/cpu.h | 2 -- target/ppc/cpu.h | 2 -- target/riscv/cpu.h | 2 -- target/rx/cpu.h | 2 -- target/s390x/cpu.h | 2 -- target/sh4/cpu.h | 2 -- target/sparc/cpu.h | 2 -- target/tricore/cpu.h | 2 -- target/xtensa/cpu.h | 2 -- tcg/tcg-op-ldst.c | 2 +- 26 files changed, 2 insertions(+), 70 deletions(-) delete mode 100644 include/exec/cpu-all.h diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 8d2b957a3b..5ced3879ac 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -35,7 +35,6 @@ #include "qemu/rcu.h" #include "exec/log.h" #include "qemu/main-loop.h" -#include "exec/cpu-all.h" #include "cpu.h" #include "exec/icount.h" #include "exec/replay-core.h" diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index c487f13e2f..8f193fd0bd 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -24,7 +24,6 @@ #include "qom/object.h" #include "target/i386/kvm/hyperv-proto.h" #include "target/i386/cpu.h" -#include "exec/cpu-all.h" #include "exec/target_page.h" struct SynICState { diff --git a/include/exec/cpu-all.h b/include/exec/cpu-all.h deleted file mode 100644 index 5122fdbee3..0000000000 --- a/include/exec/cpu-all.h +++ /dev/null @@ -1,25 +0,0 @@ -/* - * defines common to all virtual CPUs - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ -#ifndef CPU_ALL_H -#define CPU_ALL_H - -#include "hw/core/cpu.h" -#include "exec/cpu-defs.h" - -#endif /* CPU_ALL_H */ diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 28bd27b8ed..10b6b25b34 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -579,7 +579,7 @@ QEMU_BUILD_BUG_ON(offsetof(CPUState, neg) != static inline CPUArchState *cpu_env(CPUState *cpu) { - /* We validate that CPUArchState follows CPUState in cpu-all.h. */ + /* We validate that CPUArchState follows CPUState in cpu-target.c */ return (CPUArchState *)(cpu + 1); } diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index b915835bea..9a11764536 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -205,9 +205,6 @@ CPU_CONVERT(le, 64, uint64_t) * te : target endian * (except for byte accesses, which have no endian infix). * - * The target endian accessors are obviously only available to source - * files which are built per-target; they are defined in cpu-all.h. - * * In all cases these functions take a host pointer. * For accessors that take a guest address rather than a * host address, see the cpu_{ld,st}_* accessors defined in diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index f51a253626..81ffecaaba 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -8,7 +8,6 @@ */ #include "qemu/osdep.h" -#include "exec/cpu-all.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/exec-all.h" #include "exec/target_page.h" diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index fb1d63527e..849f673489 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -289,8 +289,6 @@ void alpha_cpu_dump_state(CPUState *cs, FILE *f, int flags); int alpha_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int alpha_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); -#include "exec/cpu-all.h" - enum { FEATURE_ASN = 0x00000001, FEATURE_SPS = 0x00000002, diff --git a/target/arm/cpu.h b/target/arm/cpu.h index ee92476814..ea9956395c 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2968,8 +2968,6 @@ static inline bool arm_sctlr_b(CPUARMState *env) uint64_t arm_sctlr(CPUARMState *env, int el); -#include "exec/cpu-all.h" - /* * We have more than 32-bits worth of state per TB, so we split the data * between tb->flags and tb->cs_base, which is otherwise unused for ARM. diff --git a/target/avr/cpu.h b/target/avr/cpu.h index a0fb40141a..d6666175a9 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -259,6 +259,4 @@ bool avr_cpu_tlb_fill(CPUState *cs, vaddr address, int size, extern const MemoryRegionOps avr_cpu_reg1; extern const MemoryRegionOps avr_cpu_reg2; -#include "exec/cpu-all.h" - #endif /* QEMU_AVR_CPU_H */ diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index e4fc35b112..c065fa8ddc 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -158,6 +158,4 @@ void hexagon_translate_init(void); void hexagon_translate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, vaddr pc, void *host_pc); -#include "exec/cpu-all.h" - #endif /* HEXAGON_CPU_H */ diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index 4e72ab025b..da5f8adcd5 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -306,8 +306,6 @@ struct HPPACPUClass { ResettablePhases parent_phases; }; -#include "exec/cpu-all.h" - static inline bool hppa_is_pa20(const CPUHPPAState *env) { return env->is_pa20; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 02ea87347a..bd63036334 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2604,7 +2604,6 @@ static inline bool is_mmu_index_32(int mmu_index) #define CC_SRC2 (env->cc_src2) #define CC_OP (env->cc_op) -#include "exec/cpu-all.h" #include "svm.h" #if !defined(CONFIG_USER_ONLY) diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 69117c602a..ad8b0ed235 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -504,8 +504,6 @@ static inline void cpu_get_tb_cpu_state(CPULoongArchState *env, vaddr *pc, *flags |= is_va32(env) * HW_FLAGS_VA32; } -#include "exec/cpu-all.h" - #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU void loongarch_cpu_post_init(Object *obj); diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 5347fbe397..0b70e8c6ab 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -596,8 +596,6 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, MemTxResult response, uintptr_t retaddr); #endif -#include "exec/cpu-all.h" - /* TB flags */ #define TB_FLAGS_MACSR 0x0f #define TB_FLAGS_MSR_S_BIT 13 diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 90d820b90c..2bfa396c96 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -411,8 +411,6 @@ void mb_translate_code(CPUState *cs, TranslationBlock *tb, #define MMU_USER_IDX 2 /* See NB_MMU_MODES in cpu-defs.h. */ -#include "exec/cpu-all.h" - /* Ensure there is no overlap between the two masks. */ QEMU_BUILD_BUG_ON(MSR_TB_MASK & IFLAGS_TB_MASK); diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 79f8041ced..20f31370bc 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -1258,8 +1258,6 @@ static inline int mips_env_mmu_index(CPUMIPSState *env) return hflags_mmu_index(env->hflags); } -#include "exec/cpu-all.h" - /* Exceptions */ enum { EXCP_NONE = -1, diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index f16a070ef6..19ee85ff5a 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -334,8 +334,6 @@ void cpu_openrisc_count_stop(OpenRISCCPU *cpu); #define CPU_RESOLVING_TYPE TYPE_OPENRISC_CPU -#include "exec/cpu-all.h" - #define TB_FLAGS_SM SR_SM #define TB_FLAGS_DME SR_DME #define TB_FLAGS_IME SR_IME diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index aa5df47bda..3c02f7f7d4 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1704,8 +1704,6 @@ void ppc_compat_add_property(Object *obj, const char *name, uint32_t *compat_pvr, const char *basedesc); #endif /* defined(TARGET_PPC64) */ -#include "exec/cpu-all.h" - /*****************************************************************************/ /* CRF definitions */ #define CRF_LT_BIT 3 diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 14a6779b4c..867e539b53 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -634,8 +634,6 @@ G_NORETURN void riscv_raise_exception(CPURISCVState *env, target_ulong riscv_cpu_get_fflags(CPURISCVState *env); void riscv_cpu_set_fflags(CPURISCVState *env, target_ulong); -#include "exec/cpu-all.h" - FIELD(TB_FLAGS, MEM_IDX, 0, 3) FIELD(TB_FLAGS, FS, 3, 2) /* Vector flags */ diff --git a/target/rx/cpu.h b/target/rx/cpu.h index e2ec78835e..5c19c83219 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -147,8 +147,6 @@ void rx_translate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, vaddr pc, void *host_pc); void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte); -#include "exec/cpu-all.h" - #define CPU_INTERRUPT_SOFT CPU_INTERRUPT_TGT_INT_0 #define CPU_INTERRUPT_FIR CPU_INTERRUPT_TGT_INT_1 diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 83d01d5c4e..940eda8dd1 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -948,6 +948,4 @@ uint64_t s390_cpu_get_psw_mask(CPUS390XState *env); /* outside of target/s390x/ */ S390CPU *s390_cpu_addr2state(uint16_t cpu_addr); -#include "exec/cpu-all.h" - #endif diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index 7581f5eecb..7752a0c2e1 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -288,8 +288,6 @@ void cpu_load_tlb(CPUSH4State * env); /* MMU modes definitions */ #define MMU_USER_IDX 1 -#include "exec/cpu-all.h" - /* MMU control register */ #define MMUCR 0x1F000010 #define MMUCR_AT (1<<0) diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index b87351a666..734dfdb1d3 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -729,8 +729,6 @@ static inline int cpu_pil_allowed(CPUSPARCState *env1, int pil) #endif } -#include "exec/cpu-all.h" - #ifdef TARGET_SPARC64 /* sun4u.c */ void cpu_tick_set_count(CPUTimer *timer, uint64_t count); diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index abb9cba136..c76e65f818 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -251,8 +251,6 @@ void fpu_set_state(CPUTriCoreState *env); #define MMU_USER_IDX 2 -#include "exec/cpu-all.h" - FIELD(TB_FLAGS, PRIV, 0, 2) void cpu_state_reset(CPUTriCoreState *s); diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index c5d2042de1..c03ed71c94 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -733,8 +733,6 @@ static inline uint32_t xtensa_replicate_windowstart(CPUXtensaState *env) #define XTENSA_CSBASE_LBEG_OFF_MASK 0x00ff0000 #define XTENSA_CSBASE_LBEG_OFF_SHIFT 16 -#include "exec/cpu-all.h" - static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, vaddr *pc, uint64_t *cs_base, uint32_t *flags) { diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 73838e2701..3b073b4ce0 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -37,7 +37,7 @@ static void check_max_alignment(unsigned a_bits) { /* * The requested alignment cannot overlap the TLB flags. - * FIXME: Must keep the count up-to-date with "exec/cpu-all.h". + * FIXME: Must keep the count up-to-date with "exec/tlb-flags.h". */ if (tcg_use_softmmu) { tcg_debug_assert(a_bits + 5 <= tcg_ctx->page_bits); From f1d2a8e95383090c8b3a57138f742fbd9aedf678 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:02 -0700 Subject: [PATCH 0192/2760] accel/kvm: move KVM_HAVE_MCE_INJECTION define to kvm-all.c This define is used only in accel/kvm/kvm-all.c, so we push directly the definition there. Add more visibility to kvm_arch_on_sigbus_vcpu() to allow removing this define from any header. The architectures defining KVM_HAVE_MCE_INJECTION are i386, x86_64 and aarch64. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-18-pierrick.bouvier@linaro.org> --- accel/kvm/kvm-all.c | 5 +++++ include/system/kvm.h | 2 -- target/arm/cpu.h | 4 ---- target/i386/cpu.h | 2 -- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index a30b19f455..cba9c78d2f 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -57,6 +57,11 @@ #include #endif +#if defined(__i386__) || defined(__x86_64__) || defined(__aarch64__) +# define KVM_HAVE_MCE_INJECTION 1 +#endif + + /* KVM uses PAGE_SIZE in its definition of KVM_COALESCED_MMIO_MAX. We * need to use the real host PAGE_SIZE, as that's what KVM will use. */ diff --git a/include/system/kvm.h b/include/system/kvm.h index 21da3b8b05..18811cad6f 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h @@ -390,9 +390,7 @@ bool kvm_vcpu_id_is_valid(int vcpu_id); /* Returns VCPU ID to be used on KVM_CREATE_VCPU ioctl() */ unsigned long kvm_arch_vcpu_id(CPUState *cpu); -#ifdef KVM_HAVE_MCE_INJECTION void kvm_arch_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); -#endif void kvm_arch_init_irq_routing(KVMState *s); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index ea9956395c..a8a1a8faf6 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -33,10 +33,6 @@ #include "target/arm/multiprocessing.h" #include "target/arm/gtimer.h" -#ifdef TARGET_AARCH64 -#define KVM_HAVE_MCE_INJECTION 1 -#endif - #define EXCP_UDEF 1 /* undefined instruction */ #define EXCP_SWI 2 /* software interrupt */ #define EXCP_PREFETCH_ABORT 3 diff --git a/target/i386/cpu.h b/target/i386/cpu.h index bd63036334..17ad0b644b 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -35,8 +35,6 @@ #define XEN_NR_VIRQS 24 -#define KVM_HAVE_MCE_INJECTION 1 - /* support for self modifying code even if the modified instruction is close to the modifying instruction */ #define TARGET_HAS_PRECISE_SMC From 5a9d472d01a3760a0bae7b93b5d67af8178fbed3 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:03 -0700 Subject: [PATCH 0193/2760] exec/poison: KVM_HAVE_MCE_INJECTION can now be poisoned We prevent common code to use this define by mistake. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-19-pierrick.bouvier@linaro.org> --- include/exec/poison.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/exec/poison.h b/include/exec/poison.h index f267da6083..a09e0c1263 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -73,4 +73,6 @@ #pragma GCC poison CONFIG_SOFTMMU #endif +#pragma GCC poison KVM_HAVE_MCE_INJECTION + #endif From a725f37102317d64f7f23895906f8c8c5f5f1be9 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:04 -0700 Subject: [PATCH 0194/2760] target/arm/cpu: always define kvm related registers This does not hurt, even if they are not used. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-20-pierrick.bouvier@linaro.org> --- target/arm/cpu.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index a8a1a8faf6..ab7412772b 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -971,7 +971,6 @@ struct ArchCPU { */ uint32_t kvm_target; -#ifdef CONFIG_KVM /* KVM init features for this CPU */ uint32_t kvm_init_features[7]; @@ -984,7 +983,6 @@ struct ArchCPU { /* KVM steal time */ OnOffAuto kvm_steal_time; -#endif /* CONFIG_KVM */ /* Uniprocessor system with MP extensions */ bool mp_is_up; From 9fac39750df766c154e0960eca1962159ed50660 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:05 -0700 Subject: [PATCH 0195/2760] target/arm/cpu: flags2 is always uint64_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not rely on target dependent type, but use a fixed type instead. Since the original type is unsigned, it is safe to extend its size without any side effect. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-21-pierrick.bouvier@linaro.org> --- target/arm/cpu.h | 10 ++++------ target/arm/tcg/hflags.c | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index ab7412772b..cc975175c6 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -194,7 +194,7 @@ typedef struct ARMPACKey { /* See the commentary above the TBFLAG field definitions. */ typedef struct CPUARMTBFlags { uint32_t flags; - target_ulong flags2; + uint64_t flags2; } CPUARMTBFlags; typedef struct ARMMMUFaultInfo ARMMMUFaultInfo; @@ -2968,11 +2968,9 @@ uint64_t arm_sctlr(CPUARMState *env, int el); * We collect these two parts in CPUARMTBFlags where they are named * flags and flags2 respectively. * - * The flags that are shared between all execution modes, TBFLAG_ANY, - * are stored in flags. The flags that are specific to a given mode - * are stores in flags2. Since cs_base is sized on the configured - * address size, flags2 always has 64-bits for A64, and a minimum of - * 32-bits for A32 and M32. + * The flags that are shared between all execution modes, TBFLAG_ANY, are stored + * in flags. The flags that are specific to a given mode are stored in flags2. + * flags2 always has 64-bits, even though only 32-bits are used for A32 and M32. * * The bits for 32-bit A-profile and M-profile partially overlap: * diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 8d79b8b7ae..e51d9f7b15 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -506,8 +506,8 @@ void assert_hflags_rebuild_correctly(CPUARMState *env) if (unlikely(c.flags != r.flags || c.flags2 != r.flags2)) { fprintf(stderr, "TCG hflags mismatch " - "(current:(0x%08x,0x" TARGET_FMT_lx ")" - " rebuilt:(0x%08x,0x" TARGET_FMT_lx ")\n", + "(current:(0x%08x,0x%016" PRIx64 ")" + " rebuilt:(0x%08x,0x%016" PRIx64 ")\n", c.flags, c.flags2, r.flags, r.flags2); abort(); } From 77b0893f60ecabdc796565d130343e50cfdf28f9 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:06 -0700 Subject: [PATCH 0196/2760] target/arm/cpu: define same set of registers for aarch32 and aarch64 To eliminate TARGET_AARCH64, we need to make various definitions common between 32 and 64 bit Arm targets. Added registers are used only by aarch64 code, and the only impact is on the size of CPUARMState, and added zarray (ARMVectorReg zarray[ARM_MAX_VQ * 16]) member (+64KB) It could be eventually possible to allocate this array only for aarch64 emulation, but I'm not sure it's worth the hassle to save a few KB per vcpu. Running qemu-system takes already several hundreds of MB of (resident) memory, and qemu-user takes dozens of MB of (resident) memory anyway. As part of this, we define ARM_MAX_VQ once for aarch32 and aarch64, which will affect zregs field for aarch32. This field is used for MVE and SVE implementations. MVE implementation is clipping index value to 0 or 1 for zregs[*].d[], so we should not touch the rest of data in this case anyway. This change is safe regarding migration, because aarch64 registers still have the same size, and for aarch32, only zregs is modified. Migration code explicitly specify a size of 2 for env.vfp.zregs[0].d, VMSTATE_UINT64_SUB_ARRAY(env.vfp.zregs[0].d, ARMCPU, 0, 2). So extending the storage size has no impact. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-22-pierrick.bouvier@linaro.org> --- target/arm/cpu.h | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index cc975175c6..b1c3e46326 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -169,17 +169,12 @@ typedef struct ARMGenericTimer { * Align the data for use with TCG host vector operations. */ -#ifdef TARGET_AARCH64 -# define ARM_MAX_VQ 16 -#else -# define ARM_MAX_VQ 1 -#endif +#define ARM_MAX_VQ 16 typedef struct ARMVectorReg { uint64_t d[2 * ARM_MAX_VQ] QEMU_ALIGNED(16); } ARMVectorReg; -#ifdef TARGET_AARCH64 /* In AArch32 mode, predicate registers do not exist at all. */ typedef struct ARMPredicateReg { uint64_t p[DIV_ROUND_UP(2 * ARM_MAX_VQ, 8)] QEMU_ALIGNED(16); @@ -189,7 +184,6 @@ typedef struct ARMPredicateReg { typedef struct ARMPACKey { uint64_t lo, hi; } ARMPACKey; -#endif /* See the commentary above the TBFLAG field definitions. */ typedef struct CPUARMTBFlags { @@ -660,13 +654,11 @@ typedef struct CPUArchState { struct { ARMVectorReg zregs[32]; -#ifdef TARGET_AARCH64 /* Store FFR as pregs[16] to make it easier to treat as any other. */ #define FFR_PRED_NUM 16 ARMPredicateReg pregs[17]; /* Scratch space for aa64 sve predicate temporary. */ ARMPredicateReg preg_tmp; -#endif /* We store these fpcsr fields separately for convenience. */ uint32_t qc[4] QEMU_ALIGNED(16); @@ -711,7 +703,6 @@ typedef struct CPUArchState { uint32_t cregs[16]; } iwmmxt; -#ifdef TARGET_AARCH64 struct { ARMPACKey apia; ARMPACKey apib; @@ -743,7 +734,6 @@ typedef struct CPUArchState { * to keep the offsets into the rest of the structure smaller. */ ARMVectorReg zarray[ARM_MAX_VQ * 16]; -#endif struct CPUBreakpoint *cpu_breakpoint[16]; struct CPUWatchpoint *cpu_watchpoint[16]; From 63de8825af77edd71450f6bacaa55fb88d7f86e2 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:07 -0700 Subject: [PATCH 0197/2760] target/arm/cpu: remove inline stubs for aarch32 emulation Directly condition associated calls in target/arm/helper.c for now. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-23-pierrick.bouvier@linaro.org> --- target/arm/cpu.h | 8 -------- target/arm/helper.c | 6 ++++++ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index b1c3e46326..c1a0faed3a 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1222,7 +1222,6 @@ int arm_cpu_write_elf32_note(WriteCoreDumpFunction f, CPUState *cs, */ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el); -#ifdef TARGET_AARCH64 int aarch64_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int aarch64_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq); @@ -1254,13 +1253,6 @@ static inline uint64_t *sve_bswap64(uint64_t *dst, uint64_t *src, int nr) #endif } -#else -static inline void aarch64_sve_narrow_vq(CPUARMState *env, unsigned vq) { } -static inline void aarch64_sve_change_el(CPUARMState *env, int o, - int n, bool a) -{ } -#endif - void aarch64_sync_32_to_64(CPUARMState *env); void aarch64_sync_64_to_32(CPUARMState *env); diff --git a/target/arm/helper.c b/target/arm/helper.c index becbbbd0d8..7fb6e88630 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6563,7 +6563,9 @@ static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri, */ new_len = sve_vqm1_for_el(env, cur_el); if (new_len < old_len) { +#ifdef TARGET_AARCH64 aarch64_sve_narrow_vq(env, new_len + 1); +#endif } } @@ -10628,7 +10630,9 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) * Note that new_el can never be 0. If cur_el is 0, then * el0_a64 is is_a64(), else el0_a64 is ignored. */ +#ifdef TARGET_AARCH64 aarch64_sve_change_el(env, cur_el, new_el, is_a64(env)); +#endif } if (cur_el < new_el) { @@ -11640,7 +11644,9 @@ void aarch64_sve_change_el(CPUARMState *env, int old_el, /* When changing vector length, clear inaccessible state. */ if (new_len < old_len) { +#ifdef TARGET_AARCH64 aarch64_sve_narrow_vq(env, new_len + 1); +#endif } } #endif From 652d19a642b34cd99fc2ce65267a216cb9de8f4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Apr 2025 01:58:18 +0200 Subject: [PATCH 0198/2760] target/arm: Expose Aarch64 helpers unconditionally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At worst, for 32-bit arm binary, using these methods will now produce a link time error, instead of a compile time one. Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250403235821.9909-37-philmd@linaro.org> Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- target/arm/internals.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 01408e40a3..d24acdd672 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1808,7 +1808,6 @@ static inline uint64_t pmu_counter_mask(CPUARMState *env) return (1ULL << 31) | ((1ULL << pmu_num_counters(env)) - 1); } -#ifdef TARGET_AARCH64 GDBFeature *arm_gen_dynamic_svereg_feature(CPUState *cpu, int base_reg); int aarch64_gdb_get_sve_reg(CPUState *cs, GByteArray *buf, int reg); int aarch64_gdb_set_sve_reg(CPUState *cs, uint8_t *buf, int reg); @@ -1826,7 +1825,6 @@ void aarch64_max_tcg_initfn(Object *obj); void aarch64_add_pauth_properties(Object *obj); void aarch64_add_sve_properties(Object *obj); void aarch64_add_sme_properties(Object *obj); -#endif /* Read the CONTROL register as the MRS instruction would. */ uint32_t arm_v7m_mrs_control(CPUARMState *env, uint32_t secure); From d4ecfc569dcfb33dd9f00afaa6e2bd883fcf1e4a Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:08 -0700 Subject: [PATCH 0199/2760] meson: add common hw files Those files will be compiled once per base architecture ("arm" in this case), instead of being compiled for every variant/bitness of architecture. We make sure to not include target cpu definitions (exec/cpu-defs.h) by defining header guard directly. This way, a given compilation unit can access a specific cpu definition, but not access to compile time defines associated. Previous commits took care to clean up some headers to not rely on cpu-defs.h content. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-24-pierrick.bouvier@linaro.org> --- meson.build | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 657949326b..bcb9d39a38 100644 --- a/meson.build +++ b/meson.build @@ -3682,6 +3682,7 @@ hw_arch = {} target_arch = {} target_system_arch = {} target_user_arch = {} +hw_common_arch = {} # NOTE: the trace/ subdirectory needs the qapi_trace_events variable # that is filled in by qapi/. @@ -4079,6 +4080,34 @@ common_all = static_library('common', implicit_include_directories: false, dependencies: common_ss.all_dependencies()) +# construct common libraries per base architecture +hw_common_arch_libs = {} +foreach target : target_dirs + config_target = config_target_mak[target] + target_base_arch = config_target['TARGET_BASE_ARCH'] + + # check if already generated + if target_base_arch in hw_common_arch_libs + continue + endif + + if target_base_arch in hw_common_arch + target_inc = [include_directories('target' / target_base_arch)] + src = hw_common_arch[target_base_arch] + lib = static_library( + 'hw_' + target_base_arch, + build_by_default: false, + sources: src.all_sources() + genh, + include_directories: common_user_inc + target_inc, + implicit_include_directories: false, + # prevent common code to access cpu compile time + # definition, but still allow access to cpu.h + c_args: ['-DCPU_DEFS_H', '-DCOMPILING_SYSTEM_VS_USER', '-DCONFIG_SOFTMMU'], + dependencies: src.all_dependencies()) + hw_common_arch_libs += {target_base_arch: lib} + endif +endforeach + if have_rust # We would like to use --generate-cstr, but it is only available # starting with bindgen 0.66.0. The oldest supported versions @@ -4244,8 +4273,14 @@ foreach target : target_dirs arch_deps += t.dependencies() target_common = common_ss.apply(config_target, strict: false) - objects = common_all.extract_objects(target_common.sources()) + objects = [common_all.extract_objects(target_common.sources())] arch_deps += target_common.dependencies() + if target_type == 'system' and target_base_arch in hw_common_arch_libs + src = hw_common_arch[target_base_arch].apply(config_target, strict: false) + lib = hw_common_arch_libs[target_base_arch] + objects += lib.extract_objects(src.sources()) + arch_deps += src.dependencies() + endif target_specific = specific_ss.apply(config_target, strict: false) arch_srcs += target_specific.sources() From acbebffdda13cf502d70037f0cf60b7eb858a459 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:09 -0700 Subject: [PATCH 0200/2760] hw/arm/boot: make compilation unit hw common Now we eliminated poisoned identifiers from headers, this file can now be compiled once for all arm targets. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-25-pierrick.bouvier@linaro.org> --- hw/arm/boot.c | 1 + hw/arm/meson.build | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index d3811b896f..f94b940bc3 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -14,6 +14,7 @@ #include #include "hw/arm/boot.h" #include "hw/arm/linux-boot-if.h" +#include "cpu.h" #include "exec/target_page.h" #include "system/kvm.h" #include "system/tcg.h" diff --git a/hw/arm/meson.build b/hw/arm/meson.build index ac473ce7cd..9e8c96059e 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -1,5 +1,5 @@ arm_ss = ss.source_set() -arm_ss.add(files('boot.c')) +arm_common_ss = ss.source_set() arm_ss.add(when: 'CONFIG_ARM_VIRT', if_true: files('virt.c')) arm_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) arm_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic_boards.c')) @@ -75,4 +75,7 @@ system_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c')) system_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c')) system_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) +arm_common_ss.add(fdt, files('boot.c')) + hw_arch += {'arm': arm_ss} +hw_common_arch += {'arm': arm_common_ss} From f55cc73dffcf2734d6bd54f4edaceb1b0ed64b0d Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:11 -0700 Subject: [PATCH 0201/2760] hw/arm/digic_boards: prepare compilation unit to be common Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-27-pierrick.bouvier@linaro.org> --- hw/arm/digic_boards.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/digic_boards.c b/hw/arm/digic_boards.c index 2492fafeb8..466b8b84c0 100644 --- a/hw/arm/digic_boards.c +++ b/hw/arm/digic_boards.c @@ -80,7 +80,7 @@ static void digic4_board_init(MachineState *machine, DigicBoard *board) static void digic_load_rom(DigicState *s, hwaddr addr, hwaddr max_size, const char *filename) { - target_long rom_size; + ssize_t rom_size; if (qtest_enabled()) { /* qtest runs no code so don't attempt a ROM load which From c16ee1384b3b4cddf9777ce76813e36247f6e895 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:12 -0700 Subject: [PATCH 0202/2760] hw/arm/xlnx-zynqmp: prepare compilation unit to be common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove kvm unused headers. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-28-pierrick.bouvier@linaro.org> --- hw/arm/xlnx-zynqmp.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index d6022ff2d3..ec2b3a41ed 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -22,9 +22,7 @@ #include "hw/intc/arm_gic_common.h" #include "hw/misc/unimp.h" #include "hw/boards.h" -#include "system/kvm.h" #include "system/system.h" -#include "kvm_arm.h" #include "target/arm/cpu-qom.h" #include "target/arm/gtimer.h" From 51cc143e83875c3b90570e80d47d330dd502487a Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:13 -0700 Subject: [PATCH 0203/2760] hw/arm/xlnx-versal: prepare compilation unit to be common MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove kvm unused headers. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-29-pierrick.bouvier@linaro.org> --- hw/arm/xlnx-versal.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index 278545a3f7..f0b383b29e 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -17,9 +17,7 @@ #include "hw/sysbus.h" #include "net/net.h" #include "system/system.h" -#include "system/kvm.h" #include "hw/arm/boot.h" -#include "kvm_arm.h" #include "hw/misc/unimp.h" #include "hw/arm/xlnx-versal.h" #include "qemu/log.h" From 6f4e8a92bbd9590ff87eda638bda82ec1d255f81 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 24 Mar 2025 21:59:14 -0700 Subject: [PATCH 0204/2760] hw/arm: make most of the compilation units common Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250325045915.994760-30-pierrick.bouvier@linaro.org> --- hw/arm/meson.build | 112 ++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 56 deletions(-) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 9e8c96059e..09b1cfe5b5 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -2,43 +2,43 @@ arm_ss = ss.source_set() arm_common_ss = ss.source_set() arm_ss.add(when: 'CONFIG_ARM_VIRT', if_true: files('virt.c')) arm_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) -arm_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic_boards.c')) -arm_ss.add(when: 'CONFIG_EMCRAFT_SF2', if_true: files('msf2-som.c')) -arm_ss.add(when: 'CONFIG_HIGHBANK', if_true: files('highbank.c')) -arm_ss.add(when: 'CONFIG_INTEGRATOR', if_true: files('integratorcp.c')) -arm_ss.add(when: 'CONFIG_MICROBIT', if_true: files('microbit.c')) -arm_ss.add(when: 'CONFIG_MPS3R', if_true: files('mps3r.c')) -arm_ss.add(when: 'CONFIG_MUSICPAL', if_true: files('musicpal.c')) -arm_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c')) -arm_ss.add(when: 'CONFIG_OLIMEX_STM32_H405', if_true: files('olimex-stm32-h405.c')) -arm_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c')) -arm_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm8xx.c', 'npcm8xx_boards.c')) -arm_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c')) +arm_common_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic_boards.c')) +arm_common_ss.add(when: 'CONFIG_EMCRAFT_SF2', if_true: files('msf2-som.c')) +arm_common_ss.add(when: 'CONFIG_HIGHBANK', if_true: files('highbank.c')) +arm_common_ss.add(when: 'CONFIG_INTEGRATOR', if_true: files('integratorcp.c')) +arm_common_ss.add(when: 'CONFIG_MICROBIT', if_true: files('microbit.c')) +arm_common_ss.add(when: 'CONFIG_MPS3R', if_true: files('mps3r.c')) +arm_common_ss.add(when: 'CONFIG_MUSICPAL', if_true: [pixman, files('musicpal.c')]) +arm_common_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c')) +arm_common_ss.add(when: 'CONFIG_OLIMEX_STM32_H405', if_true: files('olimex-stm32-h405.c')) +arm_common_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c')) +arm_common_ss.add(when: 'CONFIG_NPCM8XX', if_true: files('npcm8xx.c', 'npcm8xx_boards.c')) +arm_common_ss.add(when: 'CONFIG_REALVIEW', if_true: files('realview.c')) arm_ss.add(when: 'CONFIG_SBSA_REF', if_true: files('sbsa-ref.c')) -arm_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c')) -arm_ss.add(when: 'CONFIG_STM32VLDISCOVERY', if_true: files('stm32vldiscovery.c')) -arm_ss.add(when: 'CONFIG_ZYNQ', if_true: files('xilinx_zynq.c')) -arm_ss.add(when: 'CONFIG_SABRELITE', if_true: files('sabrelite.c')) +arm_common_ss.add(when: 'CONFIG_STELLARIS', if_true: files('stellaris.c')) +arm_common_ss.add(when: 'CONFIG_STM32VLDISCOVERY', if_true: files('stm32vldiscovery.c')) +arm_common_ss.add(when: 'CONFIG_ZYNQ', if_true: files('xilinx_zynq.c')) +arm_common_ss.add(when: 'CONFIG_SABRELITE', if_true: files('sabrelite.c')) -arm_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m.c')) -arm_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210.c')) -arm_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic.c')) -arm_ss.add(when: 'CONFIG_OMAP', if_true: files('omap1.c')) -arm_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubieboard.c')) -arm_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c')) -arm_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c')) +arm_common_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('armv7m.c')) +arm_common_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210.c')) +arm_common_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic.c')) +arm_common_ss.add(when: 'CONFIG_OMAP', if_true: files('omap1.c')) +arm_common_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubieboard.c')) +arm_common_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c')) +arm_common_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c')) arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2836.c', 'raspi.c')) -arm_ss.add(when: ['CONFIG_RASPI', 'TARGET_AARCH64'], if_true: files('bcm2838.c', 'raspi4b.c')) -arm_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c')) -arm_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: files('stm32f205_soc.c')) -arm_ss.add(when: 'CONFIG_STM32F405_SOC', if_true: files('stm32f405_soc.c')) -arm_ss.add(when: 'CONFIG_B_L475E_IOT01A', if_true: files('b-l475e-iot01a.c')) -arm_ss.add(when: 'CONFIG_STM32L4X5_SOC', if_true: files('stm32l4x5_soc.c')) -arm_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp.c', 'xlnx-zcu102.c')) -arm_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal.c', 'xlnx-versal-virt.c')) -arm_ss.add(when: 'CONFIG_FSL_IMX25', if_true: files('fsl-imx25.c', 'imx25_pdk.c')) -arm_ss.add(when: 'CONFIG_FSL_IMX31', if_true: files('fsl-imx31.c', 'kzm.c')) -arm_ss.add(when: 'CONFIG_FSL_IMX6', if_true: files('fsl-imx6.c')) +arm_common_ss.add(when: ['CONFIG_RASPI', 'TARGET_AARCH64'], if_true: files('bcm2838.c', 'raspi4b.c')) +arm_common_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c')) +arm_common_ss.add(when: 'CONFIG_STM32F205_SOC', if_true: files('stm32f205_soc.c')) +arm_common_ss.add(when: 'CONFIG_STM32F405_SOC', if_true: files('stm32f405_soc.c')) +arm_common_ss.add(when: 'CONFIG_B_L475E_IOT01A', if_true: files('b-l475e-iot01a.c')) +arm_common_ss.add(when: 'CONFIG_STM32L4X5_SOC', if_true: files('stm32l4x5_soc.c')) +arm_common_ss.add(when: 'CONFIG_XLNX_ZYNQMP_ARM', if_true: files('xlnx-zynqmp.c', 'xlnx-zcu102.c')) +arm_common_ss.add(when: 'CONFIG_XLNX_VERSAL', if_true: files('xlnx-versal.c', 'xlnx-versal-virt.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX25', if_true: files('fsl-imx25.c', 'imx25_pdk.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX31', if_true: files('fsl-imx31.c', 'kzm.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX6', if_true: files('fsl-imx6.c')) arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed.c', 'aspeed_soc_common.c', @@ -47,33 +47,33 @@ arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_ast10x0.c', 'aspeed_eeprom.c', 'fby35.c')) -arm_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: files('aspeed_ast27x0.c')) -arm_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2.c')) -arm_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2-tz.c')) -arm_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-soc.c')) -arm_ss.add(when: 'CONFIG_MUSCA', if_true: files('musca.c')) -arm_ss.add(when: 'CONFIG_ARMSSE', if_true: files('armsse.c')) -arm_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.c')) -arm_ss.add(when: 'CONFIG_FSL_IMX8MP', if_true: files('fsl-imx8mp.c')) -arm_ss.add(when: 'CONFIG_FSL_IMX8MP_EVK', if_true: files('imx8mp-evk.c')) -arm_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c')) -arm_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c')) -arm_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c')) +arm_common_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: files('aspeed_ast27x0.c')) +arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2.c')) +arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2-tz.c')) +arm_common_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-soc.c')) +arm_common_ss.add(when: 'CONFIG_MUSCA', if_true: files('musca.c')) +arm_common_ss.add(when: 'CONFIG_ARMSSE', if_true: files('armsse.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX7', if_true: files('fsl-imx7.c', 'mcimx7d-sabre.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX8MP', if_true: files('fsl-imx8mp.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX8MP_EVK', if_true: files('imx8mp-evk.c')) +arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmuv3.c')) +arm_common_ss.add(when: 'CONFIG_FSL_IMX6UL', if_true: files('fsl-imx6ul.c', 'mcimx6ul-evk.c')) +arm_common_ss.add(when: 'CONFIG_NRF51_SOC', if_true: files('nrf51_soc.c')) arm_ss.add(when: 'CONFIG_XEN', if_true: files( 'xen-stubs.c', 'xen-pvh.c', )) -system_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c')) -system_ss.add(when: 'CONFIG_COLLIE', if_true: files('collie.c')) -system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c')) -system_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c')) -system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c')) -system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2838_peripherals.c')) -system_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c')) -system_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c')) -system_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c')) -system_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) +arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c')) +arm_common_ss.add(when: 'CONFIG_COLLIE', if_true: files('collie.c')) +arm_common_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c')) +arm_common_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c')) +arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c')) +arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2838_peripherals.c')) +arm_common_ss.add(when: 'CONFIG_STRONGARM', if_true: files('strongarm.c')) +arm_common_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c')) +arm_common_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c')) +arm_common_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) arm_common_ss.add(fdt, files('boot.c')) From 8280a8b86659bf3c6965ccdad8458ab366817d6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 2 Apr 2025 15:37:26 +0100 Subject: [PATCH 0205/2760] target/riscv: Do not expose rv128 CPU on user mode emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As Richard mentioned: We should allow RV128 in user-mode at all until there's a kernel abi for it. Remove the experimental 'x-rv128' CPU on user emulation (since it is experimental, no deprecation period is required). Reported-by: Richard Henderson Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/riscv/cpu.c | 10 ++++------ target/riscv/tcg/tcg-cpu.c | 5 +++-- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 430b02d2a5..ad534cee51 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -697,7 +697,7 @@ static void rv64_xiangshan_nanhu_cpu_init(Object *obj) #endif } -#ifdef CONFIG_TCG +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) static void rv128_base_cpu_init(Object *obj) { RISCVCPU *cpu = RISCV_CPU(obj); @@ -708,11 +708,9 @@ static void rv128_base_cpu_init(Object *obj) /* Set latest version of privileged specification */ env->priv_ver = PRIV_VERSION_LATEST; -#ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); -#endif } -#endif /* CONFIG_TCG */ +#endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ static void rv64i_bare_cpu_init(Object *obj) { @@ -3255,9 +3253,9 @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_VEYRON_V1, MXL_RV64, rv64_veyron_v1_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_XIANGSHAN_NANHU, MXL_RV64, rv64_xiangshan_nanhu_cpu_init), -#ifdef CONFIG_TCG +#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128, MXL_RV128, rv128_base_cpu_init), -#endif /* CONFIG_TCG */ +#endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV64I, MXL_RV64, rv64i_bare_cpu_init), DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV64E, MXL_RV64, rv64e_bare_cpu_init), DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22U64, MXL_RV64, rva22u64_profile_cpu_init), diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 710449d17e..5d0429b4d0 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1046,7 +1046,6 @@ static bool riscv_cpu_is_generic(Object *cpu_obj) static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) { RISCVCPU *cpu = RISCV_CPU(cs); - RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); if (!riscv_cpu_tcg_compatible(cpu)) { g_autofree char *name = riscv_cpu_get_name(cpu); @@ -1055,6 +1054,9 @@ static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) return false; } +#ifndef CONFIG_USER_ONLY + RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); + if (mcc->misa_mxl_max >= MXL_RV128 && qemu_tcg_mttcg_enabled()) { /* Missing 128-bit aligned atomics */ error_setg(errp, @@ -1063,7 +1065,6 @@ static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) return false; } -#ifndef CONFIG_USER_ONLY CPURISCVState *env = &cpu->env; tcg_cflags_set(CPU(cs), CF_PCREL); From 79b835f1395ea5cba9a1d5f02c0fb1a429d48e7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Apr 2025 23:37:54 +0200 Subject: [PATCH 0206/2760] tcg: Include missing 'cpu.h' in translate-all.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tb_check_watchpoint() calls cpu_get_tb_cpu_state(), which is declared in each "cpu.h" header. It is indirectly included via "tcg/insn-start-words.h". Since we want to rework "tcg/insn-start-words.h", removing "cpu.h" in the next commit, add the missing header now, otherwise we'd get: accel/tcg/translate-all.c:598:9: error: call to undeclared function 'cpu_get_tb_cpu_state' [-Wimplicit-function-declaration] 598 | cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 1 + 1 file changed, 1 insertion(+) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index ed41fc5d0c..c5590eb695 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -69,6 +69,7 @@ #include "internal-target.h" #include "tcg/perf.h" #include "tcg/insn-start-words.h" +#include "cpu.h" TBContext tb_ctx; From 21d41c566d0694a90836d5c7ae4c6b279f5312a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 19 Mar 2025 12:46:47 +0100 Subject: [PATCH 0207/2760] tcg: Declare TARGET_INSN_START_EXTRA_WORDS in 'cpu-param.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To avoid including the huge "cpu.h" for a simple definition, move TARGET_INSN_START_EXTRA_WORDS to "cpu-param.h". Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/insn-start-words.h | 2 +- target/arm/cpu-param.h | 7 +++++++ target/arm/cpu.h | 6 ------ target/hppa/cpu-param.h | 2 ++ target/hppa/cpu.h | 2 -- target/i386/cpu-param.h | 2 ++ target/i386/cpu.h | 2 -- target/m68k/cpu-param.h | 2 ++ target/m68k/cpu.h | 2 -- target/microblaze/cpu-param.h | 2 ++ target/microblaze/cpu.h | 2 -- target/mips/cpu-param.h | 2 ++ target/mips/cpu.h | 2 -- target/openrisc/cpu-param.h | 2 ++ target/openrisc/cpu.h | 2 -- target/riscv/cpu-param.h | 8 ++++++++ target/riscv/cpu.h | 6 ------ target/s390x/cpu-param.h | 2 ++ target/s390x/cpu.h | 2 -- target/sh4/cpu-param.h | 2 ++ target/sh4/cpu.h | 2 -- target/sparc/cpu-param.h | 2 ++ target/sparc/cpu.h | 1 - 23 files changed, 34 insertions(+), 30 deletions(-) diff --git a/include/tcg/insn-start-words.h b/include/tcg/insn-start-words.h index 50c18bd326..c439c09f2f 100644 --- a/include/tcg/insn-start-words.h +++ b/include/tcg/insn-start-words.h @@ -6,7 +6,7 @@ #ifndef TARGET_INSN_START_WORDS -#include "cpu.h" +#include "cpu-param.h" #ifndef TARGET_INSN_START_EXTRA_WORDS # define TARGET_INSN_START_WORDS 1 diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h index a7ae42d17d..2cee4be693 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -37,6 +37,13 @@ # define TARGET_PAGE_BITS_LEGACY 10 #endif /* !CONFIG_USER_ONLY */ +/* + * ARM-specific extra insn start words: + * 1: Conditional execution bits + * 2: Partial exception syndrome for data aborts + */ +#define TARGET_INSN_START_EXTRA_WORDS 2 + /* ARM processors have a weak memory model */ #define TCG_GUEST_DEFAULT_MO (0) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c1a0faed3a..3705b34285 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -98,12 +98,6 @@ #define offsetofhigh32(S, M) (offsetof(S, M) + sizeof(uint32_t)) #endif -/* ARM-specific extra insn start words: - * 1: Conditional execution bits - * 2: Partial exception syndrome for data aborts - */ -#define TARGET_INSN_START_EXTRA_WORDS 2 - /* The 2nd extra word holding syndrome info for data aborts does not use * the upper 6 bits nor the lower 13 bits. We mask and shift it down to * help the sleb128 encoder do a better job. diff --git a/target/hppa/cpu-param.h b/target/hppa/cpu-param.h index 7ed6b5741e..68ed84e84a 100644 --- a/target/hppa/cpu-param.h +++ b/target/hppa/cpu-param.h @@ -19,6 +19,8 @@ #define TARGET_PAGE_BITS 12 +#define TARGET_INSN_START_EXTRA_WORDS 2 + /* PA-RISC 1.x processors have a strong memory model. */ /* * ??? While we do not yet implement PA-RISC 2.0, those processors have diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index da5f8adcd5..acc9937240 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -48,8 +48,6 @@ #define PRIV_KERNEL 0 #define PRIV_USER 3 -#define TARGET_INSN_START_EXTRA_WORDS 2 - /* No need to flush MMU_ABS*_IDX */ #define HPPA_MMU_FLUSH_MASK \ (1 << MMU_KERNEL_IDX | 1 << MMU_KERNEL_P_IDX | \ diff --git a/target/i386/cpu-param.h b/target/i386/cpu-param.h index b0e884c5d7..0c8efce861 100644 --- a/target/i386/cpu-param.h +++ b/target/i386/cpu-param.h @@ -22,6 +22,8 @@ #endif #define TARGET_PAGE_BITS 12 +#define TARGET_INSN_START_EXTRA_WORDS 1 + /* The x86 has a strong memory model with some store-after-load re-ordering */ #define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 17ad0b644b..9866595cd0 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1610,8 +1610,6 @@ typedef struct { #define MAX_FIXED_COUNTERS 3 #define MAX_GP_COUNTERS (MSR_IA32_PERF_STATUS - MSR_P6_EVNTSEL0) -#define TARGET_INSN_START_EXTRA_WORDS 1 - #define NB_OPMASK_REGS 8 /* CPU can't have 0xFFFFFFFF APIC ID, use that value to distinguish diff --git a/target/m68k/cpu-param.h b/target/m68k/cpu-param.h index 7afbf6d302..256a2b5f8b 100644 --- a/target/m68k/cpu-param.h +++ b/target/m68k/cpu-param.h @@ -17,4 +17,6 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_INSN_START_EXTRA_WORDS 1 + #endif diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 0b70e8c6ab..39d0b9d6d7 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -78,8 +78,6 @@ #define M68K_MAX_TTR 2 #define TTR(type, index) ttr[((type & ACCESS_CODE) == ACCESS_CODE) * 2 + index] -#define TARGET_INSN_START_EXTRA_WORDS 1 - typedef CPU_LDoubleU FPReg; typedef struct CPUArchState { diff --git a/target/microblaze/cpu-param.h b/target/microblaze/cpu-param.h index c866ec6c14..5d55e0e3c4 100644 --- a/target/microblaze/cpu-param.h +++ b/target/microblaze/cpu-param.h @@ -27,6 +27,8 @@ /* FIXME: MB uses variable pages down to 1K but linux only uses 4k. */ #define TARGET_PAGE_BITS 12 +#define TARGET_INSN_START_EXTRA_WORDS 1 + /* MicroBlaze is always in-order. */ #define TCG_GUEST_DEFAULT_MO TCG_MO_ALL diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 2bfa396c96..d511f22a55 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -233,8 +233,6 @@ typedef struct CPUArchState CPUMBState; #define STREAM_CONTROL (1 << 3) #define STREAM_NONBLOCK (1 << 4) -#define TARGET_INSN_START_EXTRA_WORDS 1 - /* use-non-secure property masks */ #define USE_NON_SECURE_M_AXI_DP_MASK 0x1 #define USE_NON_SECURE_M_AXI_IP_MASK 0x2 diff --git a/target/mips/cpu-param.h b/target/mips/cpu-param.h index 8fcb1b4f5f..99ca8d1684 100644 --- a/target/mips/cpu-param.h +++ b/target/mips/cpu-param.h @@ -20,6 +20,8 @@ #endif #define TARGET_PAGE_BITS 12 +#define TARGET_INSN_START_EXTRA_WORDS 2 + #define TCG_GUEST_DEFAULT_MO (0) #endif diff --git a/target/mips/cpu.h b/target/mips/cpu.h index 20f31370bc..d16f9a7220 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -100,8 +100,6 @@ struct CPUMIPSFPUContext { #define FP_UNIMPLEMENTED 32 }; -#define TARGET_INSN_START_EXTRA_WORDS 2 - typedef struct CPUMIPSMVPContext CPUMIPSMVPContext; struct CPUMIPSMVPContext { int32_t CP0_MVPControl; diff --git a/target/openrisc/cpu-param.h b/target/openrisc/cpu-param.h index 37627f2c39..7ea0ecb55a 100644 --- a/target/openrisc/cpu-param.h +++ b/target/openrisc/cpu-param.h @@ -12,6 +12,8 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_INSN_START_EXTRA_WORDS 1 + #define TCG_GUEST_DEFAULT_MO (0) #endif diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 19ee85ff5a..569819bfb0 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -40,8 +40,6 @@ struct OpenRISCCPUClass { ResettablePhases parent_phases; }; -#define TARGET_INSN_START_EXTRA_WORDS 1 - enum { MMU_NOMMU_IDX = 0, MMU_SUPERVISOR_IDX = 1, diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h index fba30e966a..ff4ba81965 100644 --- a/target/riscv/cpu-param.h +++ b/target/riscv/cpu-param.h @@ -16,6 +16,14 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 32 /* sv32 */ #endif #define TARGET_PAGE_BITS 12 /* 4 KiB Pages */ + +/* + * RISC-V-specific extra insn start words: + * 1: Original instruction opcode + * 2: more information about instruction + */ +#define TARGET_INSN_START_EXTRA_WORDS 2 + /* * The current MMU Modes are: * - U mode 0b000 diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 867e539b53..167909c89b 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -45,12 +45,6 @@ typedef struct CPUArchState CPURISCVState; # define TYPE_RISCV_CPU_BASE TYPE_RISCV_CPU_BASE64 #endif -/* - * RISC-V-specific extra insn start words: - * 1: Original instruction opcode - * 2: more information about instruction - */ -#define TARGET_INSN_START_EXTRA_WORDS 2 /* * b0: Whether a instruction always raise a store AMO or not. */ diff --git a/target/s390x/cpu-param.h b/target/s390x/cpu-param.h index 5c331ec424..a8a4377f4f 100644 --- a/target/s390x/cpu-param.h +++ b/target/s390x/cpu-param.h @@ -12,6 +12,8 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 64 #define TARGET_VIRT_ADDR_SPACE_BITS 64 +#define TARGET_INSN_START_EXTRA_WORDS 2 + /* * The z/Architecture has a strong memory model with some * store-after-load re-ordering. diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 940eda8dd1..90f64ee20c 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -37,8 +37,6 @@ #define TARGET_HAS_PRECISE_SMC -#define TARGET_INSN_START_EXTRA_WORDS 2 - #define MMU_USER_IDX 0 #define S390_MAX_CPUS 248 diff --git a/target/sh4/cpu-param.h b/target/sh4/cpu-param.h index 2b6e11dd0a..f328715ee8 100644 --- a/target/sh4/cpu-param.h +++ b/target/sh4/cpu-param.h @@ -16,4 +16,6 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif +#define TARGET_INSN_START_EXTRA_WORDS 1 + #endif diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index 7752a0c2e1..906f99ddf0 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -127,8 +127,6 @@ typedef struct tlb_t { #define UTLB_SIZE 64 #define ITLB_SIZE 4 -#define TARGET_INSN_START_EXTRA_WORDS 1 - enum sh_features { SH_FEATURE_SH4A = 1, SH_FEATURE_BCR3_AND_BCR4 = 2, diff --git a/target/sparc/cpu-param.h b/target/sparc/cpu-param.h index 6952ee2b82..62d47b804b 100644 --- a/target/sparc/cpu-param.h +++ b/target/sparc/cpu-param.h @@ -21,6 +21,8 @@ # define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif +#define TARGET_INSN_START_EXTRA_WORDS 1 + /* * From Oracle SPARC Architecture 2015: * diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 734dfdb1d3..83ac818933 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -223,7 +223,6 @@ typedef struct trap_state { uint32_t tt; } trap_state; #endif -#define TARGET_INSN_START_EXTRA_WORDS 1 typedef struct sparc_def_t { const char *name; From 4ff1b33edf95497a8e6f0615a3ae91f736cf1f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 19 Mar 2025 12:46:55 +0100 Subject: [PATCH 0208/2760] tcg: Always define TARGET_INSN_START_EXTRA_WORDS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not define TARGET_INSN_START_EXTRA_WORDS under the hood, have each target explicitly define it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- include/tcg/insn-start-words.h | 4 ---- include/tcg/tcg-op.h | 2 +- target/alpha/cpu-param.h | 2 ++ target/avr/cpu-param.h | 2 ++ target/hexagon/cpu-param.h | 2 ++ target/loongarch/cpu-param.h | 2 ++ target/ppc/cpu-param.h | 2 ++ target/rx/cpu-param.h | 2 ++ target/tricore/cpu-param.h | 2 ++ target/xtensa/cpu-param.h | 2 ++ 10 files changed, 17 insertions(+), 5 deletions(-) diff --git a/include/tcg/insn-start-words.h b/include/tcg/insn-start-words.h index c439c09f2f..d416d19bcf 100644 --- a/include/tcg/insn-start-words.h +++ b/include/tcg/insn-start-words.h @@ -8,10 +8,6 @@ #include "cpu-param.h" -#ifndef TARGET_INSN_START_EXTRA_WORDS -# define TARGET_INSN_START_WORDS 1 -#else # define TARGET_INSN_START_WORDS (1 + TARGET_INSN_START_EXTRA_WORDS) -#endif #endif /* TARGET_INSN_START_WORDS */ diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index bc46b5570c..cded92a447 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -23,7 +23,7 @@ # error #endif -#ifndef TARGET_INSN_START_EXTRA_WORDS +#if TARGET_INSN_START_EXTRA_WORDS == 0 static inline void tcg_gen_insn_start(target_ulong pc) { TCGOp *op = tcg_emit_op(INDEX_op_insn_start, 64 / TCG_TARGET_REG_BITS); diff --git a/target/alpha/cpu-param.h b/target/alpha/cpu-param.h index 63989e71c0..dd44feb179 100644 --- a/target/alpha/cpu-param.h +++ b/target/alpha/cpu-param.h @@ -24,6 +24,8 @@ # define TARGET_VIRT_ADDR_SPACE_BITS (30 + TARGET_PAGE_BITS) #endif +#define TARGET_INSN_START_EXTRA_WORDS 0 + /* Alpha processors have a weak memory model */ #define TCG_GUEST_DEFAULT_MO (0) diff --git a/target/avr/cpu-param.h b/target/avr/cpu-param.h index f5248ce9e7..9d37848d97 100644 --- a/target/avr/cpu-param.h +++ b/target/avr/cpu-param.h @@ -25,6 +25,8 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 24 #define TARGET_VIRT_ADDR_SPACE_BITS 24 +#define TARGET_INSN_START_EXTRA_WORDS 0 + #define TCG_GUEST_DEFAULT_MO 0 #endif diff --git a/target/hexagon/cpu-param.h b/target/hexagon/cpu-param.h index 45ee7b4640..635d509e74 100644 --- a/target/hexagon/cpu-param.h +++ b/target/hexagon/cpu-param.h @@ -23,4 +23,6 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 36 #define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_INSN_START_EXTRA_WORDS 0 + #endif diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h index 52437946e5..dbe414bb35 100644 --- a/target/loongarch/cpu-param.h +++ b/target/loongarch/cpu-param.h @@ -13,6 +13,8 @@ #define TARGET_PAGE_BITS 12 +#define TARGET_INSN_START_EXTRA_WORDS 0 + #define TCG_GUEST_DEFAULT_MO (0) #endif diff --git a/target/ppc/cpu-param.h b/target/ppc/cpu-param.h index 553ad2f4c6..d0651d2ac8 100644 --- a/target/ppc/cpu-param.h +++ b/target/ppc/cpu-param.h @@ -37,6 +37,8 @@ # define TARGET_PAGE_BITS 12 #endif +#define TARGET_INSN_START_EXTRA_WORDS 0 + #define TCG_GUEST_DEFAULT_MO 0 #endif diff --git a/target/rx/cpu-param.h b/target/rx/cpu-param.h index ef1970a09e..84934f3bca 100644 --- a/target/rx/cpu-param.h +++ b/target/rx/cpu-param.h @@ -24,4 +24,6 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_INSN_START_EXTRA_WORDS 0 + #endif diff --git a/target/tricore/cpu-param.h b/target/tricore/cpu-param.h index 790242ef3d..eb33a67c41 100644 --- a/target/tricore/cpu-param.h +++ b/target/tricore/cpu-param.h @@ -12,4 +12,6 @@ #define TARGET_PHYS_ADDR_SPACE_BITS 32 #define TARGET_VIRT_ADDR_SPACE_BITS 32 +#define TARGET_INSN_START_EXTRA_WORDS 0 + #endif diff --git a/target/xtensa/cpu-param.h b/target/xtensa/cpu-param.h index 5e4848ad05..e7cb747aaa 100644 --- a/target/xtensa/cpu-param.h +++ b/target/xtensa/cpu-param.h @@ -16,6 +16,8 @@ #define TARGET_VIRT_ADDR_SPACE_BITS 32 #endif +#define TARGET_INSN_START_EXTRA_WORDS 0 + /* Xtensa processors have a weak memory model */ #define TCG_GUEST_DEFAULT_MO (0) From 231a1c0ff46ae442431fb7a6e5d6f36765628b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:13:20 +0200 Subject: [PATCH 0209/2760] exec: Restrict 'cpu-ldst-common.h' to accel/tcg/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- accel/tcg/translator.c | 2 +- include/{exec => accel/tcg}/cpu-ldst-common.h | 6 +++--- include/exec/cpu_ldst.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename include/{exec => accel/tcg}/cpu-ldst-common.h (97%) diff --git a/accel/tcg/translator.c b/accel/tcg/translator.c index c53bbdef99..034f2f359e 100644 --- a/accel/tcg/translator.c +++ b/accel/tcg/translator.c @@ -11,7 +11,7 @@ #include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/error-report.h" -#include "exec/cpu-ldst-common.h" +#include "accel/tcg/cpu-ldst-common.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/target_page.h" #include "exec/translator.h" diff --git a/include/exec/cpu-ldst-common.h b/include/accel/tcg/cpu-ldst-common.h similarity index 97% rename from include/exec/cpu-ldst-common.h rename to include/accel/tcg/cpu-ldst-common.h index c46a6ade5d..8bf17c2fab 100644 --- a/include/exec/cpu-ldst-common.h +++ b/include/accel/tcg/cpu-ldst-common.h @@ -4,8 +4,8 @@ * SPDX-License-Identifier: LGPL-2.1-or-later */ -#ifndef CPU_LDST_COMMON_H -#define CPU_LDST_COMMON_H +#ifndef ACCEL_TCG_CPU_LDST_COMMON_H +#define ACCEL_TCG_CPU_LDST_COMMON_H #ifndef CONFIG_TCG #error Can only include this header with TCG @@ -119,4 +119,4 @@ uint32_t cpu_ldl_code_mmu(CPUArchState *env, vaddr addr, uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr, MemOpIdx oi, uintptr_t ra); -#endif /* CPU_LDST_COMMON_H */ +#endif /* ACCEL_TCG_CPU_LDST_COMMON_H */ diff --git a/include/exec/cpu_ldst.h b/include/exec/cpu_ldst.h index 63847f6e61..74761ba5f3 100644 --- a/include/exec/cpu_ldst.h +++ b/include/exec/cpu_ldst.h @@ -67,7 +67,7 @@ #endif #include "exec/cpu-common.h" -#include "exec/cpu-ldst-common.h" +#include "accel/tcg/cpu-ldst-common.h" #include "accel/tcg/cpu-mmu-index.h" #include "exec/abi_ptr.h" From 42fa9665e598c268a7ccfab5b92636618d9574ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:14:36 +0200 Subject: [PATCH 0210/2760] exec: Restrict 'cpu_ldst.h' to accel/tcg/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanical change using: $ sed -i -e 's,exec/cpu_ldst,accel/tcg/cpu-ldst,' \ $(git grep -l exec/cpu_ldst.h) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 2 +- accel/tcg/user-exec.c | 2 +- bsd-user/qemu.h | 2 +- include/{exec/cpu_ldst.h => accel/tcg/cpu-ldst.h} | 6 +++--- include/exec/exec-all.h | 2 +- linux-user/qemu.h | 2 +- target/alpha/mem_helper.c | 2 +- target/arm/tcg/helper-a64.c | 2 +- target/arm/tcg/m_helper.c | 2 +- target/arm/tcg/mte_helper.c | 2 +- target/arm/tcg/mve_helper.c | 2 +- target/arm/tcg/op_helper.c | 2 +- target/arm/tcg/pauth_helper.c | 2 +- target/arm/tcg/sme_helper.c | 2 +- target/arm/tcg/sve_ldst_internal.h | 2 +- target/avr/helper.c | 2 +- target/hexagon/op_helper.c | 2 +- target/hexagon/translate.c | 2 +- target/hppa/op_helper.c | 2 +- target/i386/tcg/access.c | 2 +- target/i386/tcg/fpu_helper.c | 2 +- target/i386/tcg/mem_helper.c | 2 +- target/i386/tcg/mpx_helper.c | 2 +- target/i386/tcg/seg_helper.c | 2 +- target/i386/tcg/system/excp_helper.c | 2 +- target/i386/tcg/system/misc_helper.c | 2 +- target/i386/tcg/system/seg_helper.c | 2 +- target/i386/tcg/system/svm_helper.c | 2 +- target/i386/tcg/user/seg_helper.c | 2 +- target/loongarch/cpu.c | 2 +- target/loongarch/tcg/csr_helper.c | 2 +- target/loongarch/tcg/fpu_helper.c | 2 +- target/loongarch/tcg/iocsr_helper.c | 2 +- target/loongarch/tcg/op_helper.c | 2 +- target/loongarch/tcg/tlb_helper.c | 2 +- target/m68k/fpu_helper.c | 2 +- target/m68k/op_helper.c | 2 +- target/microblaze/cpu.c | 2 +- target/microblaze/op_helper.c | 2 +- target/microblaze/translate.c | 2 +- target/mips/tcg/ldst_helper.c | 2 +- target/mips/tcg/msa_helper.c | 2 +- target/mips/tcg/system/tlb_helper.c | 2 +- target/ppc/mem_helper.c | 2 +- target/ppc/mmu_helper.c | 2 +- target/ppc/tcg-excp_helper.c | 2 +- target/riscv/op_helper.c | 2 +- target/riscv/vector_helper.c | 2 +- target/riscv/zce_helper.c | 2 +- target/rx/helper.c | 2 +- target/rx/op_helper.c | 2 +- target/s390x/tcg/crypto_helper.c | 2 +- target/s390x/tcg/int_helper.c | 2 +- target/s390x/tcg/mem_helper.c | 2 +- target/s390x/tcg/misc_helper.c | 2 +- target/s390x/tcg/vec_helper.c | 2 +- target/sh4/op_helper.c | 2 +- target/sparc/int32_helper.c | 2 +- target/sparc/ldst_helper.c | 2 +- target/tricore/op_helper.c | 2 +- target/tricore/translate.c | 2 +- 61 files changed, 63 insertions(+), 63 deletions(-) rename include/{exec/cpu_ldst.h => accel/tcg/cpu-ldst.h} (99%) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 0de46903dd..2cafd38d2a 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -23,7 +23,7 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #include "system/memory.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/cputlb.h" #include "exec/tb-flush.h" #include "system/ram_addr.h" diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 7f57d8f1af..1b878ead7a 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -26,7 +26,7 @@ #include "tcg/tcg.h" #include "qemu/bitops.h" #include "qemu/rcu.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "user/cpu_loop.h" #include "qemu/main-loop.h" #include "user/page-protection.h" diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index c1c508281a..244670dd24 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -22,7 +22,7 @@ #include "qemu/int128.h" #include "cpu.h" #include "qemu/units.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/exec-all.h" #include "user/abitypes.h" diff --git a/include/exec/cpu_ldst.h b/include/accel/tcg/cpu-ldst.h similarity index 99% rename from include/exec/cpu_ldst.h rename to include/accel/tcg/cpu-ldst.h index 74761ba5f3..f97a730703 100644 --- a/include/exec/cpu_ldst.h +++ b/include/accel/tcg/cpu-ldst.h @@ -59,8 +59,8 @@ * The "mmu" suffix carries the full MemOpIdx, with both mmu_idx and the * MemOp including alignment requirements. The alignment will be enforced. */ -#ifndef CPU_LDST_H -#define CPU_LDST_H +#ifndef ACCEL_TCG_CPU_LDST_H +#define ACCEL_TCG_CPU_LDST_H #ifndef CONFIG_TCG #error Can only include this header with TCG @@ -560,4 +560,4 @@ static inline void clear_helper_retaddr(void) #define clear_helper_retaddr() do { } while (0) #endif -#endif /* CPU_LDST_H */ +#endif /* ACCEL_TCG_CPU_LDST_H */ diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index f52a680f42..70608a11b6 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -21,7 +21,7 @@ #define EXEC_ALL_H #if defined(CONFIG_USER_ONLY) -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #endif #include "exec/mmu-access-type.h" #include "exec/translation-block.h" diff --git a/linux-user/qemu.h b/linux-user/qemu.h index 948de8431a..0b19fa43e6 100644 --- a/linux-user/qemu.h +++ b/linux-user/qemu.h @@ -2,7 +2,7 @@ #define QEMU_H #include "cpu.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "user/abitypes.h" #include "user/page-protection.h" diff --git a/target/alpha/mem_helper.c b/target/alpha/mem_helper.c index 872955f5e7..a4d5adb40c 100644 --- a/target/alpha/mem_helper.c +++ b/target/alpha/mem_helper.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" static void do_unaligned_access(CPUAlphaState *env, vaddr addr, uintptr_t retaddr) { diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 507dbc1a44..08d8f63ffe 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -30,7 +30,7 @@ #include "qemu/crc32c.h" #include "exec/cpu-common.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/target_page.h" #include "exec/tlb-flags.h" #include "qemu/int128.h" diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index f7354f3c6e..37dc98dc35 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -18,7 +18,7 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #ifdef CONFIG_TCG -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "semihosting/common-semi.h" #endif #if !defined(CONFIG_USER_ONLY) diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 888c670754..7dc5fb776b 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -29,7 +29,7 @@ #else #include "system/ram_addr.h" #endif -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/helper-proto.h" #include "exec/tlb-flags.h" #include "accel/tcg/cpu-ops.h" diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 274003e2e5..f9f67d1f88 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -22,7 +22,7 @@ #include "internals.h" #include "vec_internal.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/exec-all.h" #include "tcg/tcg.h" #include "fpu/softfloat.h" diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 71ba406782..38d49cbb9d 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -24,7 +24,7 @@ #include "internals.h" #include "cpu-features.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "cpregs.h" #define SIGNBIT (uint32_t)0x80000000 diff --git a/target/arm/tcg/pauth_helper.c b/target/arm/tcg/pauth_helper.c index c4b143024f..59bf27541d 100644 --- a/target/arm/tcg/pauth_helper.c +++ b/target/arm/tcg/pauth_helper.c @@ -22,7 +22,7 @@ #include "internals.h" #include "cpu-features.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" #include "qemu/xxhash.h" diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index dcc48e43db..96b84c37a2 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -22,7 +22,7 @@ #include "internals.h" #include "tcg/tcg-gvec-desc.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/exec-all.h" #include "qemu/int128.h" #include "fpu/softfloat.h" diff --git a/target/arm/tcg/sve_ldst_internal.h b/target/arm/tcg/sve_ldst_internal.h index 4f159ec4ad..f2243daf37 100644 --- a/target/arm/tcg/sve_ldst_internal.h +++ b/target/arm/tcg/sve_ldst_internal.h @@ -20,7 +20,7 @@ #ifndef TARGET_ARM_SVE_LDST_INTERNAL_H #define TARGET_ARM_SVE_LDST_INTERNAL_H -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" /* * Load one element into @vd + @reg_off from @host. diff --git a/target/avr/helper.c b/target/avr/helper.c index 32cbf17919..afa591470f 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -27,7 +27,7 @@ #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/target_page.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/helper-proto.h" bool avr_cpu_exec_interrupt(CPUState *cs, int interrupt_request) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 6da8db8ea5..3f3d86db2b 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -18,7 +18,7 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" #include "cpu.h" diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index fe7858703c..dd26801e64 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -23,7 +23,7 @@ #include "exec/helper-gen.h" #include "exec/helper-proto.h" #include "exec/translation-block.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/log.h" #include "internal.h" #include "attribs.h" diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index beb8f88799..2398ce2c64 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "qemu/timer.h" #include "trace.h" #ifdef CONFIG_USER_ONLY diff --git a/target/i386/tcg/access.c b/target/i386/tcg/access.c index 5a4721dcee..0fdd587edd 100644 --- a/target/i386/tcg/access.c +++ b/target/i386/tcg/access.c @@ -3,7 +3,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/exec-all.h" #include "exec/target_page.h" #include "access.h" diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index c1184ca219..1cbadb1453 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "tcg-cpu.h" #include "exec/cputlb.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" #include "fpu/softfloat-macros.h" diff --git a/target/i386/tcg/mem_helper.c b/target/i386/tcg/mem_helper.c index 3ef84e90d9..84a0815217 100644 --- a/target/i386/tcg/mem_helper.c +++ b/target/i386/tcg/mem_helper.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "qemu/int128.h" #include "qemu/atomic128.h" #include "tcg/tcg.h" diff --git a/target/i386/tcg/mpx_helper.c b/target/i386/tcg/mpx_helper.c index b942665adc..a0f816dfae 100644 --- a/target/i386/tcg/mpx_helper.c +++ b/target/i386/tcg/mpx_helper.c @@ -20,7 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/exec-all.h" #include "exec/target_page.h" #include "helper-tcg.h" diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 9dfbc4208c..3af902e0ec 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -23,7 +23,7 @@ #include "qemu/log.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/log.h" #include "helper-tcg.h" #include "seg_helper.h" diff --git a/target/i386/tcg/system/excp_helper.c b/target/i386/tcg/system/excp_helper.c index a563c9b35e..93614aa3e5 100644 --- a/target/i386/tcg/system/excp_helper.c +++ b/target/i386/tcg/system/excp_helper.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/target_page.h" diff --git a/target/i386/tcg/system/misc_helper.c b/target/i386/tcg/system/misc_helper.c index 67896c8c87..9c3f5cc99b 100644 --- a/target/i386/tcg/system/misc_helper.c +++ b/target/i386/tcg/system/misc_helper.c @@ -21,7 +21,7 @@ #include "qemu/main-loop.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "system/address-spaces.h" #include "system/memory.h" #include "exec/cputlb.h" diff --git a/target/i386/tcg/system/seg_helper.c b/target/i386/tcg/system/seg_helper.c index b07cc9f9b1..d4ea890c12 100644 --- a/target/i386/tcg/system/seg_helper.c +++ b/target/i386/tcg/system/seg_helper.c @@ -23,7 +23,7 @@ #include "qemu/main-loop.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "tcg/helper-tcg.h" #include "../seg_helper.h" diff --git a/target/i386/tcg/system/svm_helper.c b/target/i386/tcg/system/svm_helper.c index f9982b72d1..b27049b9ed 100644 --- a/target/i386/tcg/system/svm_helper.c +++ b/target/i386/tcg/system/svm_helper.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cputlb.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "tcg/helper-tcg.h" /* Secure Virtual Machine helpers */ diff --git a/target/i386/tcg/user/seg_helper.c b/target/i386/tcg/user/seg_helper.c index c45f2ac2ba..5692dd5195 100644 --- a/target/i386/tcg/user/seg_helper.c +++ b/target/i386/tcg/user/seg_helper.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "tcg/helper-tcg.h" #include "tcg/seg_helper.h" diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index cb96b17911..4cc8e02f70 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -29,7 +29,7 @@ #include #endif #ifdef CONFIG_TCG -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "tcg/tcg.h" #endif diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 6a7a65c860..2942d7feb8 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -13,7 +13,7 @@ #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "exec/cputlb.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "hw/irq.h" #include "cpu-csr.h" diff --git a/target/loongarch/tcg/fpu_helper.c b/target/loongarch/tcg/fpu_helper.c index a83acf64b0..fc3fd0561e 100644 --- a/target/loongarch/tcg/fpu_helper.c +++ b/target/loongarch/tcg/fpu_helper.c @@ -9,7 +9,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "fpu/softfloat.h" #include "internals.h" diff --git a/target/loongarch/tcg/iocsr_helper.c b/target/loongarch/tcg/iocsr_helper.c index b6916f53d2..e62170de3c 100644 --- a/target/loongarch/tcg/iocsr_helper.c +++ b/target/loongarch/tcg/iocsr_helper.c @@ -10,7 +10,7 @@ #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #define GET_MEMTXATTRS(cas) \ ((MemTxAttrs){.requester_id = env_cpu(cas)->cpu_index}) diff --git a/target/loongarch/tcg/op_helper.c b/target/loongarch/tcg/op_helper.c index b17208e5b9..94e3b28016 100644 --- a/target/loongarch/tcg/op_helper.c +++ b/target/loongarch/tcg/op_helper.c @@ -11,7 +11,7 @@ #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "internals.h" #include "qemu/crc32c.h" #include /* for crc32 */ diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 0d6c9844a6..9a76a2a205 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -16,7 +16,7 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/log.h" #include "cpu-csr.h" diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index eb1cb8c687..ac4a0d85be 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "softfloat.h" /* diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c index 15bad5dd46..242aecccbb 100644 --- a/target/m68k/op_helper.c +++ b/target/m68k/op_helper.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "semihosting/semihost.h" #if !defined(CONFIG_USER_ONLY) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 88baeb6807..d10ae0702a 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -28,7 +28,7 @@ #include "qemu/module.h" #include "hw/qdev-properties.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/gdbstub.h" #include "exec/translation-block.h" #include "fpu/softfloat-helpers.h" diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index f6378030b7..4624ce5b67 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -24,7 +24,7 @@ #include "exec/helper-proto.h" #include "qemu/host-utils.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "fpu/softfloat.h" void helper_put(uint32_t id, uint32_t ctrl, uint32_t data) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 4bb867c969..7dcad6cf0d 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" diff --git a/target/mips/tcg/ldst_helper.c b/target/mips/tcg/ldst_helper.c index f92a923d7a..2fb879fcbc 100644 --- a/target/mips/tcg/ldst_helper.c +++ b/target/mips/tcg/ldst_helper.c @@ -24,7 +24,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/memop.h" #include "internal.h" diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index 969dd34b3e..14de4a71ff 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -22,7 +22,7 @@ #include "internal.h" #include "tcg/tcg.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/helper-proto.h" #include "exec/memop.h" #include "exec/target_page.h" diff --git a/target/mips/tcg/system/tlb_helper.c b/target/mips/tcg/system/tlb_helper.c index d239fa9353..e477ef812a 100644 --- a/target/mips/tcg/system/tlb_helper.c +++ b/target/mips/tcg/system/tlb_helper.c @@ -25,7 +25,7 @@ #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/log.h" #include "exec/helper-proto.h" diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 0967624afe..d7e8d678f4 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -24,7 +24,7 @@ #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "helper_regs.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "internal.h" #include "qemu/atomic128.h" diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index c90ceb7d60..2138666122 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -37,7 +37,7 @@ #include "mmu-radix64.h" #include "mmu-booke.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" /* #define FLUSH_ALL_TLBS */ diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c index c422648cfd..2b15e5f2f0 100644 --- a/target/ppc/tcg-excp_helper.c +++ b/target/ppc/tcg-excp_helper.c @@ -20,7 +20,7 @@ #include "qemu/main-loop.h" #include "qemu/log.h" #include "target/ppc/cpu.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "system/runstate.h" diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index f3d26b6b95..5b0db2c45a 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -23,7 +23,7 @@ #include "internals.h" #include "exec/exec-all.h" #include "exec/cputlb.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/helper-proto.h" #include "exec/tlb-flags.h" #include "trace.h" diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 7de6cbae5c..b8ae704457 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "exec/memop.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/page-protection.h" #include "exec/helper-proto.h" #include "exec/tlb-flags.h" diff --git a/target/riscv/zce_helper.c b/target/riscv/zce_helper.c index b433bda16d..50d65f386c 100644 --- a/target/riscv/zce_helper.c +++ b/target/riscv/zce_helper.c @@ -20,7 +20,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" target_ulong HELPER(cm_jalt)(CPURISCVState *env, uint32_t index) { diff --git a/target/rx/helper.c b/target/rx/helper.c index e8aabf40ff..0640ab322b 100644 --- a/target/rx/helper.c +++ b/target/rx/helper.c @@ -20,7 +20,7 @@ #include "qemu/bitops.h" #include "cpu.h" #include "exec/log.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "hw/irq.h" void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte) diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c index b3ed822dd1..a2f1f3824d 100644 --- a/target/rx/op_helper.c +++ b/target/rx/op_helper.c @@ -21,7 +21,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "fpu/softfloat.h" #include "tcg/debug-assert.h" diff --git a/target/s390x/tcg/crypto_helper.c b/target/s390x/tcg/crypto_helper.c index 93aabd236f..642c1b18c4 100644 --- a/target/s390x/tcg/crypto_helper.c +++ b/target/s390x/tcg/crypto_helper.c @@ -18,7 +18,7 @@ #include "tcg_s390x.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" static uint64_t R(uint64_t x, int c) { diff --git a/target/s390x/tcg/int_helper.c b/target/s390x/tcg/int_helper.c index 2af970f2c8..253c036415 100644 --- a/target/s390x/tcg/int_helper.c +++ b/target/s390x/tcg/int_helper.c @@ -25,7 +25,7 @@ #include "exec/exec-all.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" /* #define DEBUG_HELPER */ #ifdef DEBUG_HELPER diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index d5eece4384..0cdfd380ce 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -28,7 +28,7 @@ #include "exec/exec-all.h" #include "exec/cputlb.h" #include "exec/page-protection.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/target_page.h" #include "exec/tlb-flags.h" #include "accel/tcg/cpu-ops.h" diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index e02f443850..d5088493ea 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -28,7 +28,7 @@ #include "qemu/timer.h" #include "exec/exec-all.h" #include "exec/cputlb.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/target_page.h" #include "qapi/error.h" #include "tcg_s390x.h" diff --git a/target/s390x/tcg/vec_helper.c b/target/s390x/tcg/vec_helper.c index dafc4c3582..781ccc565b 100644 --- a/target/s390x/tcg/vec_helper.c +++ b/target/s390x/tcg/vec_helper.c @@ -16,7 +16,7 @@ #include "tcg/tcg.h" #include "tcg/tcg-gvec-desc.h" #include "exec/helper-proto.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/exec-all.h" void HELPER(gvec_vbperm)(void *v1, const void *v2, const void *v3, diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c index 99394b714c..e7fcad3c1b 100644 --- a/target/sh4/op_helper.c +++ b/target/sh4/op_helper.c @@ -20,7 +20,7 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "fpu/softfloat.h" #ifndef CONFIG_USER_ONLY diff --git a/target/sparc/int32_helper.c b/target/sparc/int32_helper.c index f026606102..39db4ffa70 100644 --- a/target/sparc/int32_helper.c +++ b/target/sparc/int32_helper.c @@ -21,7 +21,7 @@ #include "qemu/main-loop.h" #include "cpu.h" #include "trace.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "exec/log.h" #include "system/runstate.h" diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 3fa5e78816..4c5dba19d1 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -27,7 +27,7 @@ #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/target_page.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "system/memory.h" #ifdef CONFIG_USER_ONLY #include "user/page-protection.h" diff --git a/target/tricore/op_helper.c b/target/tricore/op_helper.c index a0d5a0da1d..ae559b6922 100644 --- a/target/tricore/op_helper.c +++ b/target/tricore/op_helper.c @@ -19,7 +19,7 @@ #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "exec/exec-all.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include /* for crc32 */ diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 5c7ed395ca..7cd26d8eab 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -22,7 +22,7 @@ #include "cpu.h" #include "exec/exec-all.h" #include "tcg/tcg-op.h" -#include "exec/cpu_ldst.h" +#include "accel/tcg/cpu-ldst.h" #include "qemu/qemu-print.h" #include "exec/helper-proto.h" From d864cbb65da2220038a9b3aff98ae7f73a3198d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Apr 2025 10:27:42 +0200 Subject: [PATCH 0211/2760] exec: Do not include 'accel/tcg/cpu-ldst.h' in 'exec-all.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only 2 files requiring "accel/tcg/cpu-ldst.h" API do not include it: - accel/tcg/cpu-exec.c - target/arm/tcg/sve_helper.c Include it there and remove it from "exec/exec-all.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 1 + include/exec/exec-all.h | 3 --- target/arm/tcg/sve_helper.c | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 5ced3879ac..b00f046b29 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -22,6 +22,7 @@ #include "qapi/error.h" #include "qapi/type-helpers.h" #include "hw/core/cpu.h" +#include "accel/tcg/cpu-ldst.h" #include "accel/tcg/cpu-ops.h" #include "trace.h" #include "disas/disas.h" diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 70608a11b6..944b579d91 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -20,9 +20,6 @@ #ifndef EXEC_ALL_H #define EXEC_ALL_H -#if defined(CONFIG_USER_ONLY) -#include "accel/tcg/cpu-ldst.h" -#endif #include "exec/mmu-access-type.h" #include "exec/translation-block.h" diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 9b0d40c9e1..87b6b4b3e6 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -30,6 +30,7 @@ #include "tcg/tcg.h" #include "vec_internal.h" #include "sve_ldst_internal.h" +#include "accel/tcg/cpu-ldst.h" #include "accel/tcg/cpu-ops.h" #ifdef CONFIG_USER_ONLY #include "user/page-protection.h" From c2ba9fea42391c316794c78f0f6f01760b1fb0f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 21 Mar 2025 12:10:45 +0100 Subject: [PATCH 0212/2760] tcg: Always define TCG_GUEST_DEFAULT_MO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only require the TCG_GUEST_DEFAULT_MO for MTTCG-enabled frontends, otherwise we use a default value of TCG_MO_ALL. In order to simplify, require the definition for all targets, defining it for hexagon, m68k, rx, sh4 and tricore. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 4 ---- target/hexagon/cpu-param.h | 3 +++ target/m68k/cpu-param.h | 3 +++ target/rx/cpu-param.h | 3 +++ target/sh4/cpu-param.h | 3 +++ target/tricore/cpu-param.h | 3 +++ 6 files changed, 15 insertions(+), 4 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index c5590eb695..7467255f6e 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -353,11 +353,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tcg_ctx->tlb_dyn_max_bits = CPU_TLB_DYN_MAX_BITS; #endif tcg_ctx->insn_start_words = TARGET_INSN_START_WORDS; -#ifdef TCG_GUEST_DEFAULT_MO tcg_ctx->guest_mo = TCG_GUEST_DEFAULT_MO; -#else - tcg_ctx->guest_mo = TCG_MO_ALL; -#endif restart_translate: trace_translate_block(tb, pc, tb->tc.ptr); diff --git a/target/hexagon/cpu-param.h b/target/hexagon/cpu-param.h index 635d509e74..7cc63a01d4 100644 --- a/target/hexagon/cpu-param.h +++ b/target/hexagon/cpu-param.h @@ -25,4 +25,7 @@ #define TARGET_INSN_START_EXTRA_WORDS 0 +/* MTTCG not yet supported: require strict ordering */ +#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL + #endif diff --git a/target/m68k/cpu-param.h b/target/m68k/cpu-param.h index 256a2b5f8b..10a8d74bfa 100644 --- a/target/m68k/cpu-param.h +++ b/target/m68k/cpu-param.h @@ -19,4 +19,7 @@ #define TARGET_INSN_START_EXTRA_WORDS 1 +/* MTTCG not yet supported: require strict ordering */ +#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL + #endif diff --git a/target/rx/cpu-param.h b/target/rx/cpu-param.h index 84934f3bca..fe39a77ca3 100644 --- a/target/rx/cpu-param.h +++ b/target/rx/cpu-param.h @@ -26,4 +26,7 @@ #define TARGET_INSN_START_EXTRA_WORDS 0 +/* MTTCG not yet supported: require strict ordering */ +#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL + #endif diff --git a/target/sh4/cpu-param.h b/target/sh4/cpu-param.h index f328715ee8..acdf239749 100644 --- a/target/sh4/cpu-param.h +++ b/target/sh4/cpu-param.h @@ -18,4 +18,7 @@ #define TARGET_INSN_START_EXTRA_WORDS 1 +/* MTTCG not yet supported: require strict ordering */ +#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL + #endif diff --git a/target/tricore/cpu-param.h b/target/tricore/cpu-param.h index eb33a67c41..45fde756b6 100644 --- a/target/tricore/cpu-param.h +++ b/target/tricore/cpu-param.h @@ -14,4 +14,7 @@ #define TARGET_INSN_START_EXTRA_WORDS 0 +/* MTTCG not yet supported: require strict ordering */ +#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL + #endif From adb86e48ad3db9031a5963e03a8be2e2798bf9d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 21 Mar 2025 12:16:28 +0100 Subject: [PATCH 0213/2760] tcg: Simplify tcg_req_mo() macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that TCG_GUEST_DEFAULT_MO is always defined, simplify the tcg_req_mo() macro. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/internal-target.h | 9 +-------- accel/tcg/tcg-all.c | 3 --- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index 05abaeb8e0..1a46a7c87d 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -52,17 +52,10 @@ G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); * memory ordering vs the host memory ordering. A non-zero * result indicates that some barrier is required. * - * If TCG_GUEST_DEFAULT_MO is not defined, assume that the - * guest requires strict ordering. - * * This is a macro so that it's constant even without optimization. */ -#ifdef TCG_GUEST_DEFAULT_MO -# define tcg_req_mo(type) \ +#define tcg_req_mo(type) \ ((type) & TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO) -#else -# define tcg_req_mo(type) ((type) & ~TCG_TARGET_DEFAULT_MO) -#endif /** * cpu_req_mo: diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 7a5b810b88..a5a1fd6a11 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -77,9 +77,6 @@ static bool default_mttcg_enabled(void) return false; } #ifdef TARGET_SUPPORTS_MTTCG -# ifndef TCG_GUEST_DEFAULT_MO -# error "TARGET_SUPPORTS_MTTCG without TCG_GUEST_DEFAULT_MO" -# endif return true; #else return false; From 04583ce7e032ee8e0a12756b69dc67ad7b399997 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 21 Mar 2025 19:01:52 +0100 Subject: [PATCH 0214/2760] tcg: Define guest_default_memory_order in TCGCPUOps MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the TCGCPUOps::guest_default_memory_order field and have each target initialize it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/accel/tcg/cpu-ops.h | 8 ++++++++ target/alpha/cpu.c | 2 ++ target/arm/cpu.c | 2 ++ target/arm/tcg/cpu-v7m.c | 2 ++ target/avr/cpu.c | 1 + target/hexagon/cpu.c | 1 + target/hppa/cpu.c | 2 ++ target/i386/tcg/tcg-cpu.c | 1 + target/loongarch/cpu.c | 2 ++ target/m68k/cpu.c | 2 ++ target/microblaze/cpu.c | 2 ++ target/mips/cpu.c | 2 ++ target/openrisc/cpu.c | 2 ++ target/ppc/cpu_init.c | 1 + target/riscv/tcg/tcg-cpu.c | 2 ++ target/rx/cpu.c | 2 ++ target/s390x/cpu.c | 2 ++ target/sh4/cpu.c | 2 ++ target/sparc/cpu.c | 2 ++ target/tricore/cpu.c | 1 + target/xtensa/cpu.c | 2 ++ 21 files changed, 43 insertions(+) diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index 106a0688da..a4932fc5d7 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -16,8 +16,16 @@ #include "exec/memop.h" #include "exec/mmu-access-type.h" #include "exec/vaddr.h" +#include "tcg/tcg-mo.h" struct TCGCPUOps { + + /** + * @guest_default_memory_order: default barrier that is required + * for the guest memory ordering. + */ + TCGBar guest_default_memory_order; + /** * @initialize: Initialize TCG state * diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 99d839a279..6f931117a2 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -235,6 +235,8 @@ static const struct SysemuCPUOps alpha_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps alpha_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = alpha_translate_init, .translate_code = alpha_translate_code, .synchronize_from_tb = alpha_cpu_synchronize_from_tb, diff --git a/target/arm/cpu.c b/target/arm/cpu.c index c9e043bc9b..3f20e258fd 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2671,6 +2671,8 @@ static const struct SysemuCPUOps arm_sysemu_ops = { #ifdef CONFIG_TCG static const TCGCPUOps arm_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = arm_translate_init, .translate_code = arm_translate_code, .synchronize_from_tb = arm_cpu_synchronize_from_tb, diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 1a913faa50..4553fe9de0 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -232,6 +232,8 @@ static void cortex_m55_initfn(Object *obj) } static const TCGCPUOps arm_v7m_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = arm_translate_init, .translate_code = arm_translate_code, .synchronize_from_tb = arm_cpu_synchronize_from_tb, diff --git a/target/avr/cpu.c b/target/avr/cpu.c index feb73e722b..67918684fa 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -224,6 +224,7 @@ static const struct SysemuCPUOps avr_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps avr_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, .initialize = avr_cpu_tcg_init, .translate_code = avr_cpu_translate_code, .synchronize_from_tb = avr_cpu_synchronize_from_tb, diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index ad1f303fbc..b12e0dccd0 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -325,6 +325,7 @@ static void hexagon_cpu_init(Object *obj) #include "accel/tcg/cpu-ops.h" static const TCGCPUOps hexagon_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, .initialize = hexagon_translate_init, .translate_code = hexagon_translate_code, .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 51bff0c5d6..ac4560febe 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -253,6 +253,8 @@ static const struct SysemuCPUOps hppa_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps hppa_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = hppa_translate_init, .translate_code = hppa_translate_code, .synchronize_from_tb = hppa_cpu_synchronize_from_tb, diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 35b17f2b18..3e1b315340 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -125,6 +125,7 @@ static bool x86_debug_check_breakpoint(CPUState *cs) #include "accel/tcg/cpu-ops.h" static const TCGCPUOps x86_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, .initialize = tcg_x86_init, .translate_code = x86_translate_code, .synchronize_from_tb = x86_cpu_synchronize_from_tb, diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 4cc8e02f70..ee74509a66 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -864,6 +864,8 @@ static void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) #include "accel/tcg/cpu-ops.h" static const TCGCPUOps loongarch_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = loongarch_translate_init, .translate_code = loongarch_translate_code, .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 4409d8941c..bfde9b8594 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -589,6 +589,8 @@ static const struct SysemuCPUOps m68k_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps m68k_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = m68k_tcg_init, .translate_code = m68k_translate_code, .restore_state_to_opc = m68k_restore_state_to_opc, diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index d10ae0702a..e46863574c 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -427,6 +427,8 @@ static const struct SysemuCPUOps mb_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps mb_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = mb_tcg_init, .translate_code = mb_translate_code, .synchronize_from_tb = mb_cpu_synchronize_from_tb, diff --git a/target/mips/cpu.c b/target/mips/cpu.c index cb0d6dde0e..67a8550cc1 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -551,6 +551,8 @@ static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) } static const TCGCPUOps mips_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = mips_tcg_init, .translate_code = mips_translate_code, .synchronize_from_tb = mips_cpu_synchronize_from_tb, diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index dc55594a7d..e62c698a40 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -243,6 +243,8 @@ static const struct SysemuCPUOps openrisc_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps openrisc_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = openrisc_translate_init, .translate_code = openrisc_translate_code, .synchronize_from_tb = openrisc_cpu_synchronize_from_tb, diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index fd8c42069e..1cf18e0dae 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7479,6 +7479,7 @@ static const struct SysemuCPUOps ppc_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps ppc_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, .initialize = ppc_translate_init, .translate_code = ppc_translate_code, .restore_state_to_opc = ppc_restore_state_to_opc, diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 5d0429b4d0..ded2d68ad7 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -140,6 +140,8 @@ static void riscv_restore_state_to_opc(CPUState *cs, } static const TCGCPUOps riscv_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = riscv_translate_init, .translate_code = riscv_translate_code, .synchronize_from_tb = riscv_cpu_synchronize_from_tb, diff --git a/target/rx/cpu.c b/target/rx/cpu.c index e14d9cbef9..d7eac551fd 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -204,6 +204,8 @@ static const struct SysemuCPUOps rx_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps rx_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = rx_translate_init, .translate_code = rx_translate_code, .synchronize_from_tb = rx_cpu_synchronize_from_tb, diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index d15b1943e0..f232d82fa3 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -345,6 +345,8 @@ void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, } static const TCGCPUOps s390_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = s390x_translate_init, .translate_code = s390x_translate_code, .restore_state_to_opc = s390x_restore_state_to_opc, diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index df093988cb..29f4be7ba9 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -262,6 +262,8 @@ static const struct SysemuCPUOps sh4_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps superh_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = sh4_translate_init, .translate_code = sh4_translate_code, .synchronize_from_tb = superh_cpu_synchronize_from_tb, diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index af3cec43e7..ef04efcb18 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -1001,6 +1001,8 @@ static const struct SysemuCPUOps sparc_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps sparc_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = sparc_tcg_init, .translate_code = sparc_translate_code, .synchronize_from_tb = sparc_cpu_synchronize_from_tb, diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 833a93d37a..3bf399335a 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -172,6 +172,7 @@ static const struct SysemuCPUOps tricore_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps tricore_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, .initialize = tricore_tcg_init, .translate_code = tricore_translate_code, .synchronize_from_tb = tricore_cpu_synchronize_from_tb, diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 51f9ee9e89..2347106495 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -232,6 +232,8 @@ static const struct SysemuCPUOps xtensa_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps xtensa_tcg_ops = { + .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .initialize = xtensa_translate_init, .translate_code = xtensa_translate_code, .debug_excp_handler = xtensa_breakpoint_handler, From 9c1f8062d4d0ee47981bf1aead1b877bdf08296e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 21 Mar 2025 19:05:31 +0100 Subject: [PATCH 0215/2760] tcg: Remove use of TCG_GUEST_DEFAULT_MO in tb_gen_code() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use TCGCPUOps::guest_default_memory_order to set TCGContext::guest_mo. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 7467255f6e..c007b9a190 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -353,7 +353,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tcg_ctx->tlb_dyn_max_bits = CPU_TLB_DYN_MAX_BITS; #endif tcg_ctx->insn_start_words = TARGET_INSN_START_WORDS; - tcg_ctx->guest_mo = TCG_GUEST_DEFAULT_MO; + tcg_ctx->guest_mo = cpu->cc->tcg_ops->guest_default_memory_order; restart_translate: trace_translate_block(tb, pc, tb->tc.ptr); From eacd8c7cef297bafa53deaf8d9b9d368acff3936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 21 Mar 2025 18:52:37 +0100 Subject: [PATCH 0216/2760] tcg: Propagate CPUState argument to cpu_req_mo() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation of having tcg_req_mo() access CPUState in the next commit, pass it to cpu_req_mo(), its single caller. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 20 ++++++++++---------- accel/tcg/internal-target.h | 3 ++- accel/tcg/user-exec.c | 20 ++++++++++---------- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 2cafd38d2a..35b1ff03a5 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2324,7 +2324,7 @@ static uint8_t do_ld1_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, MMULookupLocals l; bool crosspage; - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD); crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l); tcg_debug_assert(!crosspage); @@ -2339,7 +2339,7 @@ static uint16_t do_ld2_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, uint16_t ret; uint8_t a, b; - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD); crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l); if (likely(!crosspage)) { return do_ld_2(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra); @@ -2363,7 +2363,7 @@ static uint32_t do_ld4_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, bool crosspage; uint32_t ret; - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD); crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l); if (likely(!crosspage)) { return do_ld_4(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra); @@ -2384,7 +2384,7 @@ static uint64_t do_ld8_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, bool crosspage; uint64_t ret; - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD); crosspage = mmu_lookup(cpu, addr, oi, ra, access_type, &l); if (likely(!crosspage)) { return do_ld_8(cpu, &l.page[0], l.mmu_idx, access_type, l.memop, ra); @@ -2407,7 +2407,7 @@ static Int128 do_ld16_mmu(CPUState *cpu, vaddr addr, Int128 ret; int first; - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD); crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_LOAD, &l); if (likely(!crosspage)) { if (unlikely(l.page[0].flags & TLB_MMIO)) { @@ -2735,7 +2735,7 @@ static void do_st1_mmu(CPUState *cpu, vaddr addr, uint8_t val, MMULookupLocals l; bool crosspage; - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST); crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l); tcg_debug_assert(!crosspage); @@ -2749,7 +2749,7 @@ static void do_st2_mmu(CPUState *cpu, vaddr addr, uint16_t val, bool crosspage; uint8_t a, b; - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST); crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l); if (likely(!crosspage)) { do_st_2(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra); @@ -2771,7 +2771,7 @@ static void do_st4_mmu(CPUState *cpu, vaddr addr, uint32_t val, MMULookupLocals l; bool crosspage; - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST); crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l); if (likely(!crosspage)) { do_st_4(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra); @@ -2792,7 +2792,7 @@ static void do_st8_mmu(CPUState *cpu, vaddr addr, uint64_t val, MMULookupLocals l; bool crosspage; - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST); crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l); if (likely(!crosspage)) { do_st_8(cpu, &l.page[0], val, l.mmu_idx, l.memop, ra); @@ -2815,7 +2815,7 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val, uint64_t a, b; int first; - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST); crosspage = mmu_lookup(cpu, addr, oi, ra, MMU_DATA_STORE, &l); if (likely(!crosspage)) { if (unlikely(l.page[0].flags & TLB_MMIO)) { diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index 1a46a7c87d..23aac39b57 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -59,12 +59,13 @@ G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); /** * cpu_req_mo: + * @cpu: CPUState * @type: TCGBar * * If tcg_req_mo indicates a barrier for @type is required * for the guest memory model, issue a host memory barrier. */ -#define cpu_req_mo(type) \ +#define cpu_req_mo(cpu, type) \ do { \ if (tcg_req_mo(type)) { \ smp_mb(); \ diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 1b878ead7a..3f4d682446 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -1061,7 +1061,7 @@ static uint8_t do_ld1_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, void *haddr; uint8_t ret; - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD); haddr = cpu_mmu_lookup(cpu, addr, get_memop(oi), ra, access_type); ret = ldub_p(haddr); clear_helper_retaddr(); @@ -1075,7 +1075,7 @@ static uint16_t do_ld2_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, uint16_t ret; MemOp mop = get_memop(oi); - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD); haddr = cpu_mmu_lookup(cpu, addr, mop, ra, access_type); ret = load_atom_2(cpu, ra, haddr, mop); clear_helper_retaddr(); @@ -1093,7 +1093,7 @@ static uint32_t do_ld4_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, uint32_t ret; MemOp mop = get_memop(oi); - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD); haddr = cpu_mmu_lookup(cpu, addr, mop, ra, access_type); ret = load_atom_4(cpu, ra, haddr, mop); clear_helper_retaddr(); @@ -1111,7 +1111,7 @@ static uint64_t do_ld8_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, uint64_t ret; MemOp mop = get_memop(oi); - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD); haddr = cpu_mmu_lookup(cpu, addr, mop, ra, access_type); ret = load_atom_8(cpu, ra, haddr, mop); clear_helper_retaddr(); @@ -1130,7 +1130,7 @@ static Int128 do_ld16_mmu(CPUState *cpu, abi_ptr addr, MemOp mop = get_memop(oi); tcg_debug_assert((mop & MO_SIZE) == MO_128); - cpu_req_mo(TCG_MO_LD_LD | TCG_MO_ST_LD); + cpu_req_mo(cpu, TCG_MO_LD_LD | TCG_MO_ST_LD); haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_LOAD); ret = load_atom_16(cpu, ra, haddr, mop); clear_helper_retaddr(); @@ -1146,7 +1146,7 @@ static void do_st1_mmu(CPUState *cpu, vaddr addr, uint8_t val, { void *haddr; - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST); haddr = cpu_mmu_lookup(cpu, addr, get_memop(oi), ra, MMU_DATA_STORE); stb_p(haddr, val); clear_helper_retaddr(); @@ -1158,7 +1158,7 @@ static void do_st2_mmu(CPUState *cpu, vaddr addr, uint16_t val, void *haddr; MemOp mop = get_memop(oi); - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST); haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE); if (mop & MO_BSWAP) { @@ -1174,7 +1174,7 @@ static void do_st4_mmu(CPUState *cpu, vaddr addr, uint32_t val, void *haddr; MemOp mop = get_memop(oi); - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST); haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE); if (mop & MO_BSWAP) { @@ -1190,7 +1190,7 @@ static void do_st8_mmu(CPUState *cpu, vaddr addr, uint64_t val, void *haddr; MemOp mop = get_memop(oi); - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST); haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE); if (mop & MO_BSWAP) { @@ -1206,7 +1206,7 @@ static void do_st16_mmu(CPUState *cpu, vaddr addr, Int128 val, void *haddr; MemOpIdx mop = get_memop(oi); - cpu_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); + cpu_req_mo(cpu, TCG_MO_LD_ST | TCG_MO_ST_ST); haddr = cpu_mmu_lookup(cpu, addr, mop, ra, MMU_DATA_STORE); if (mop & MO_BSWAP) { From 0eca13c29a0823850cf3308528b0de0ed4608c02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 21 Mar 2025 12:18:26 +0100 Subject: [PATCH 0217/2760] tcg: Have tcg_req_mo() use TCGCPUOps::guest_default_memory_order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to use TCG with multiple targets, replace the compile time use of TCG_GUEST_DEFAULT_MO by a runtime access to TCGCPUOps::guest_default_memory_order via CPUState. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- accel/tcg/internal-target.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index 23aac39b57..f5a3fd7e40 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -46,16 +46,15 @@ G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); /** * tcg_req_mo: + * @guest_mo: Guest default memory order * @type: TCGBar * * Filter @type to the barrier that is required for the guest * memory ordering vs the host memory ordering. A non-zero * result indicates that some barrier is required. - * - * This is a macro so that it's constant even without optimization. */ -#define tcg_req_mo(type) \ - ((type) & TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO) +#define tcg_req_mo(guest_mo, type) \ + ((type) & guest_mo & ~TCG_TARGET_DEFAULT_MO) /** * cpu_req_mo: @@ -67,7 +66,7 @@ G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); */ #define cpu_req_mo(cpu, type) \ do { \ - if (tcg_req_mo(type)) { \ + if (tcg_req_mo(cpu->cc->tcg_ops->guest_default_memory_order, type)) { \ smp_mb(); \ } \ } while (0) From 8201f1a29c95bca095bdd6e6c6eba42d8d06499b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 21 Mar 2025 19:02:35 +0100 Subject: [PATCH 0218/2760] tcg: Remove the TCG_GUEST_DEFAULT_MO definition globally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By directly using TCGCPUOps::guest_default_memory_order, we don't need the TCG_GUEST_DEFAULT_MO definition anymore. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/multi-thread-tcg.rst | 4 ++-- target/alpha/cpu-param.h | 3 --- target/alpha/cpu.c | 3 ++- target/arm/cpu-param.h | 3 --- target/arm/cpu.c | 3 ++- target/arm/tcg/cpu-v7m.c | 3 ++- target/avr/cpu-param.h | 2 -- target/avr/cpu.c | 2 +- target/hexagon/cpu-param.h | 3 --- target/hexagon/cpu.c | 3 ++- target/hppa/cpu-param.h | 8 -------- target/hppa/cpu.c | 8 +++++++- target/i386/cpu-param.h | 3 --- target/i386/tcg/tcg-cpu.c | 5 ++++- target/loongarch/cpu-param.h | 2 -- target/loongarch/cpu.c | 2 +- target/m68k/cpu-param.h | 3 --- target/m68k/cpu.c | 3 ++- target/microblaze/cpu-param.h | 3 --- target/microblaze/cpu.c | 3 ++- target/mips/cpu-param.h | 2 -- target/mips/cpu.c | 2 +- target/openrisc/cpu-param.h | 2 -- target/openrisc/cpu.c | 2 +- target/ppc/cpu-param.h | 2 -- target/ppc/cpu_init.c | 2 +- target/riscv/cpu-param.h | 2 -- target/riscv/tcg/tcg-cpu.c | 2 +- target/rx/cpu-param.h | 3 --- target/rx/cpu.c | 3 ++- target/s390x/cpu-param.h | 6 ------ target/s390x/cpu.c | 6 +++++- target/sh4/cpu-param.h | 3 --- target/sh4/cpu.c | 3 ++- target/sparc/cpu-param.h | 23 ----------------------- target/sparc/cpu.c | 23 ++++++++++++++++++++++- target/tricore/cpu-param.h | 3 --- target/tricore/cpu.c | 3 ++- target/xtensa/cpu-param.h | 3 --- target/xtensa/cpu.c | 3 ++- 40 files changed, 66 insertions(+), 101 deletions(-) diff --git a/docs/devel/multi-thread-tcg.rst b/docs/devel/multi-thread-tcg.rst index b0f473961d..14a2a9dc7b 100644 --- a/docs/devel/multi-thread-tcg.rst +++ b/docs/devel/multi-thread-tcg.rst @@ -28,8 +28,8 @@ vCPU Scheduling We introduce a new running mode where each vCPU will run on its own user-space thread. This is enabled by default for all FE/BE combinations where the host memory model is able to accommodate the -guest (TCG_GUEST_DEFAULT_MO & ~TCG_TARGET_DEFAULT_MO is zero) and the -guest has had the required work done to support this safely +guest (TCGCPUOps::guest_default_memory_order & ~TCG_TARGET_DEFAULT_MO is zero) +and the guest has had the required work done to support this safely (TARGET_SUPPORTS_MTTCG). System emulation will fall back to the original round robin approach diff --git a/target/alpha/cpu-param.h b/target/alpha/cpu-param.h index dd44feb179..a799f42db3 100644 --- a/target/alpha/cpu-param.h +++ b/target/alpha/cpu-param.h @@ -26,7 +26,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 0 -/* Alpha processors have a weak memory model */ -#define TCG_GUEST_DEFAULT_MO (0) - #endif diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 6f931117a2..eeaf3a81c1 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -235,7 +235,8 @@ static const struct SysemuCPUOps alpha_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps alpha_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* Alpha processors have a weak memory model */ + .guest_default_memory_order = 0, .initialize = alpha_translate_init, .translate_code = alpha_translate_code, diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h index 2cee4be693..5c5bc8a009 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -44,7 +44,4 @@ */ #define TARGET_INSN_START_EXTRA_WORDS 2 -/* ARM processors have a weak memory model */ -#define TCG_GUEST_DEFAULT_MO (0) - #endif diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 3f20e258fd..3e9760b551 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2671,7 +2671,8 @@ static const struct SysemuCPUOps arm_sysemu_ops = { #ifdef CONFIG_TCG static const TCGCPUOps arm_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* ARM processors have a weak memory model */ + .guest_default_memory_order = 0, .initialize = arm_translate_init, .translate_code = arm_translate_code, diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 4553fe9de0..89d4e4b4a2 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -232,7 +232,8 @@ static void cortex_m55_initfn(Object *obj) } static const TCGCPUOps arm_v7m_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* ARM processors have a weak memory model */ + .guest_default_memory_order = 0, .initialize = arm_translate_init, .translate_code = arm_translate_code, diff --git a/target/avr/cpu-param.h b/target/avr/cpu-param.h index 9d37848d97..f74bfc2580 100644 --- a/target/avr/cpu-param.h +++ b/target/avr/cpu-param.h @@ -27,6 +27,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 0 -#define TCG_GUEST_DEFAULT_MO 0 - #endif diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 67918684fa..8f79cf4c08 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -224,7 +224,7 @@ static const struct SysemuCPUOps avr_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps avr_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .guest_default_memory_order = 0, .initialize = avr_cpu_tcg_init, .translate_code = avr_cpu_translate_code, .synchronize_from_tb = avr_cpu_synchronize_from_tb, diff --git a/target/hexagon/cpu-param.h b/target/hexagon/cpu-param.h index 7cc63a01d4..635d509e74 100644 --- a/target/hexagon/cpu-param.h +++ b/target/hexagon/cpu-param.h @@ -25,7 +25,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 0 -/* MTTCG not yet supported: require strict ordering */ -#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL - #endif diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index b12e0dccd0..3d14e5cc6a 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -325,7 +325,8 @@ static void hexagon_cpu_init(Object *obj) #include "accel/tcg/cpu-ops.h" static const TCGCPUOps hexagon_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* MTTCG not yet supported: require strict ordering */ + .guest_default_memory_order = TCG_MO_ALL, .initialize = hexagon_translate_init, .translate_code = hexagon_translate_code, .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, diff --git a/target/hppa/cpu-param.h b/target/hppa/cpu-param.h index 68ed84e84a..9bf7ac76d0 100644 --- a/target/hppa/cpu-param.h +++ b/target/hppa/cpu-param.h @@ -21,12 +21,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 2 -/* PA-RISC 1.x processors have a strong memory model. */ -/* - * ??? While we do not yet implement PA-RISC 2.0, those processors have - * a weak memory model, but with TLB bits that force ordering on a per-page - * basis. It's probably easier to fall back to a strong memory model. - */ -#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL - #endif diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index ac4560febe..dfbd933056 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -253,7 +253,13 @@ static const struct SysemuCPUOps hppa_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps hppa_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* PA-RISC 1.x processors have a strong memory model. */ + /* + * ??? While we do not yet implement PA-RISC 2.0, those processors have + * a weak memory model, but with TLB bits that force ordering on a per-page + * basis. It's probably easier to fall back to a strong memory model. + */ + .guest_default_memory_order = TCG_MO_ALL, .initialize = hppa_translate_init, .translate_code = hppa_translate_code, diff --git a/target/i386/cpu-param.h b/target/i386/cpu-param.h index 0c8efce861..ebb844bcc8 100644 --- a/target/i386/cpu-param.h +++ b/target/i386/cpu-param.h @@ -24,7 +24,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 1 -/* The x86 has a strong memory model with some store-after-load re-ordering */ -#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) - #endif diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 3e1b315340..d941df0956 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -125,7 +125,10 @@ static bool x86_debug_check_breakpoint(CPUState *cs) #include "accel/tcg/cpu-ops.h" static const TCGCPUOps x86_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* + * The x86 has a strong memory model with some store-after-load re-ordering + */ + .guest_default_memory_order = TCG_MO_ALL & ~TCG_MO_ST_LD, .initialize = tcg_x86_init, .translate_code = x86_translate_code, .synchronize_from_tb = x86_cpu_synchronize_from_tb, diff --git a/target/loongarch/cpu-param.h b/target/loongarch/cpu-param.h index dbe414bb35..58cc45a377 100644 --- a/target/loongarch/cpu-param.h +++ b/target/loongarch/cpu-param.h @@ -15,6 +15,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 0 -#define TCG_GUEST_DEFAULT_MO (0) - #endif diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index ee74509a66..f5b8ef29ab 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -864,7 +864,7 @@ static void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) #include "accel/tcg/cpu-ops.h" static const TCGCPUOps loongarch_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .guest_default_memory_order = 0, .initialize = loongarch_translate_init, .translate_code = loongarch_translate_code, diff --git a/target/m68k/cpu-param.h b/target/m68k/cpu-param.h index 10a8d74bfa..256a2b5f8b 100644 --- a/target/m68k/cpu-param.h +++ b/target/m68k/cpu-param.h @@ -19,7 +19,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 1 -/* MTTCG not yet supported: require strict ordering */ -#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL - #endif diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index bfde9b8594..b2d8c8f1de 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -589,7 +589,8 @@ static const struct SysemuCPUOps m68k_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps m68k_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* MTTCG not yet supported: require strict ordering */ + .guest_default_memory_order = TCG_MO_ALL, .initialize = m68k_tcg_init, .translate_code = m68k_translate_code, diff --git a/target/microblaze/cpu-param.h b/target/microblaze/cpu-param.h index 5d55e0e3c4..e0a3794513 100644 --- a/target/microblaze/cpu-param.h +++ b/target/microblaze/cpu-param.h @@ -29,7 +29,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 1 -/* MicroBlaze is always in-order. */ -#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL - #endif diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index e46863574c..4efba0dddb 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -427,7 +427,8 @@ static const struct SysemuCPUOps mb_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps mb_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* MicroBlaze is always in-order. */ + .guest_default_memory_order = TCG_MO_ALL, .initialize = mb_tcg_init, .translate_code = mb_translate_code, diff --git a/target/mips/cpu-param.h b/target/mips/cpu-param.h index 99ca8d1684..58f450827f 100644 --- a/target/mips/cpu-param.h +++ b/target/mips/cpu-param.h @@ -22,6 +22,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 2 -#define TCG_GUEST_DEFAULT_MO (0) - #endif diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 67a8550cc1..2ae7ba4407 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -551,7 +551,7 @@ static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) } static const TCGCPUOps mips_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .guest_default_memory_order = 0, .initialize = mips_tcg_init, .translate_code = mips_translate_code, diff --git a/target/openrisc/cpu-param.h b/target/openrisc/cpu-param.h index 7ea0ecb55a..b4f57bbe69 100644 --- a/target/openrisc/cpu-param.h +++ b/target/openrisc/cpu-param.h @@ -14,6 +14,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 1 -#define TCG_GUEST_DEFAULT_MO (0) - #endif diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index e62c698a40..87fe779042 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -243,7 +243,7 @@ static const struct SysemuCPUOps openrisc_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps openrisc_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .guest_default_memory_order = 0, .initialize = openrisc_translate_init, .translate_code = openrisc_translate_code, diff --git a/target/ppc/cpu-param.h b/target/ppc/cpu-param.h index d0651d2ac8..e4ed9080ee 100644 --- a/target/ppc/cpu-param.h +++ b/target/ppc/cpu-param.h @@ -39,6 +39,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 0 -#define TCG_GUEST_DEFAULT_MO 0 - #endif diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 1cf18e0dae..9ba775971a 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7479,7 +7479,7 @@ static const struct SysemuCPUOps ppc_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps ppc_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .guest_default_memory_order = 0, .initialize = ppc_translate_init, .translate_code = ppc_translate_code, .restore_state_to_opc = ppc_restore_state_to_opc, diff --git a/target/riscv/cpu-param.h b/target/riscv/cpu-param.h index ff4ba81965..cfdc67c258 100644 --- a/target/riscv/cpu-param.h +++ b/target/riscv/cpu-param.h @@ -34,6 +34,4 @@ * - M mode HLV/HLVX/HSV 0b111 */ -#define TCG_GUEST_DEFAULT_MO 0 - #endif diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index ded2d68ad7..50e81b2e52 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -140,7 +140,7 @@ static void riscv_restore_state_to_opc(CPUState *cs, } static const TCGCPUOps riscv_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + .guest_default_memory_order = 0, .initialize = riscv_translate_init, .translate_code = riscv_translate_code, diff --git a/target/rx/cpu-param.h b/target/rx/cpu-param.h index fe39a77ca3..84934f3bca 100644 --- a/target/rx/cpu-param.h +++ b/target/rx/cpu-param.h @@ -26,7 +26,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 0 -/* MTTCG not yet supported: require strict ordering */ -#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL - #endif diff --git a/target/rx/cpu.c b/target/rx/cpu.c index d7eac551fd..f073fe8fc9 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -204,7 +204,8 @@ static const struct SysemuCPUOps rx_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps rx_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* MTTCG not yet supported: require strict ordering */ + .guest_default_memory_order = TCG_MO_ALL, .initialize = rx_translate_init, .translate_code = rx_translate_code, diff --git a/target/s390x/cpu-param.h b/target/s390x/cpu-param.h index a8a4377f4f..abfae3bedf 100644 --- a/target/s390x/cpu-param.h +++ b/target/s390x/cpu-param.h @@ -14,10 +14,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 2 -/* - * The z/Architecture has a strong memory model with some - * store-after-load re-ordering. - */ -#define TCG_GUEST_DEFAULT_MO (TCG_MO_ALL & ~TCG_MO_ST_LD) - #endif diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index f232d82fa3..1e101b5afe 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -345,7 +345,11 @@ void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, } static const TCGCPUOps s390_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* + * The z/Architecture has a strong memory model with some + * store-after-load re-ordering. + */ + .guest_default_memory_order = TCG_MO_ALL & ~TCG_MO_ST_LD, .initialize = s390x_translate_init, .translate_code = s390x_translate_code, diff --git a/target/sh4/cpu-param.h b/target/sh4/cpu-param.h index acdf239749..f328715ee8 100644 --- a/target/sh4/cpu-param.h +++ b/target/sh4/cpu-param.h @@ -18,7 +18,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 1 -/* MTTCG not yet supported: require strict ordering */ -#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL - #endif diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 29f4be7ba9..7a05301c6f 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -262,7 +262,8 @@ static const struct SysemuCPUOps sh4_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps superh_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* MTTCG not yet supported: require strict ordering */ + .guest_default_memory_order = TCG_MO_ALL, .initialize = sh4_translate_init, .translate_code = sh4_translate_code, diff --git a/target/sparc/cpu-param.h b/target/sparc/cpu-param.h index 62d47b804b..45eea9d6ba 100644 --- a/target/sparc/cpu-param.h +++ b/target/sparc/cpu-param.h @@ -23,27 +23,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 1 -/* - * From Oracle SPARC Architecture 2015: - * - * Compatibility notes: The PSO memory model described in SPARC V8 and - * SPARC V9 compatibility architecture specifications was never implemented - * in a SPARC V9 implementation and is not included in the Oracle SPARC - * Architecture specification. - * - * The RMO memory model described in the SPARC V9 specification was - * implemented in some non-Sun SPARC V9 implementations, but is not - * directly supported in Oracle SPARC Architecture 2015 implementations. - * - * Therefore always use TSO in QEMU. - * - * D.5 Specification of Partial Store Order (PSO) - * ... [loads] are followed by an implied MEMBAR #LoadLoad | #LoadStore. - * - * D.6 Specification of Total Store Order (TSO) - * ... PSO with the additional requirement that all [stores] are followed - * by an implied MEMBAR #StoreStore. - */ -#define TCG_GUEST_DEFAULT_MO (TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST) - #endif diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index ef04efcb18..56d9417ae3 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -1001,7 +1001,28 @@ static const struct SysemuCPUOps sparc_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps sparc_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* + * From Oracle SPARC Architecture 2015: + * + * Compatibility notes: The PSO memory model described in SPARC V8 and + * SPARC V9 compatibility architecture specifications was never + * implemented in a SPARC V9 implementation and is not included in the + * Oracle SPARC Architecture specification. + * + * The RMO memory model described in the SPARC V9 specification was + * implemented in some non-Sun SPARC V9 implementations, but is not + * directly supported in Oracle SPARC Architecture 2015 implementations. + * + * Therefore always use TSO in QEMU. + * + * D.5 Specification of Partial Store Order (PSO) + * ... [loads] are followed by an implied MEMBAR #LoadLoad | #LoadStore. + * + * D.6 Specification of Total Store Order (TSO) + * ... PSO with the additional requirement that all [stores] are followed + * by an implied MEMBAR #StoreStore. + */ + .guest_default_memory_order = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST, .initialize = sparc_tcg_init, .translate_code = sparc_translate_code, diff --git a/target/tricore/cpu-param.h b/target/tricore/cpu-param.h index 45fde756b6..eb33a67c41 100644 --- a/target/tricore/cpu-param.h +++ b/target/tricore/cpu-param.h @@ -14,7 +14,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 0 -/* MTTCG not yet supported: require strict ordering */ -#define TCG_GUEST_DEFAULT_MO TCG_MO_ALL - #endif diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 3bf399335a..c68954b409 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -172,7 +172,8 @@ static const struct SysemuCPUOps tricore_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps tricore_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* MTTCG not yet supported: require strict ordering */ + .guest_default_memory_order = TCG_MO_ALL, .initialize = tricore_tcg_init, .translate_code = tricore_translate_code, .synchronize_from_tb = tricore_cpu_synchronize_from_tb, diff --git a/target/xtensa/cpu-param.h b/target/xtensa/cpu-param.h index e7cb747aaa..7a0c22c900 100644 --- a/target/xtensa/cpu-param.h +++ b/target/xtensa/cpu-param.h @@ -18,7 +18,4 @@ #define TARGET_INSN_START_EXTRA_WORDS 0 -/* Xtensa processors have a weak memory model */ -#define TCG_GUEST_DEFAULT_MO (0) - #endif diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 2347106495..2cbf4e3010 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -232,7 +232,8 @@ static const struct SysemuCPUOps xtensa_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps xtensa_tcg_ops = { - .guest_default_memory_order = TCG_GUEST_DEFAULT_MO, + /* Xtensa processors have a weak memory model */ + .guest_default_memory_order = 0, .initialize = xtensa_translate_init, .translate_code = xtensa_translate_code, From 9638cb59ee3d3a505f4bb6b9a4bcc49c3df4edcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 18:45:29 +0100 Subject: [PATCH 0219/2760] tcg: Move cpu_req_mo() macro to target-agnostic 'backend-ldst.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- accel/tcg/backend-ldst.h | 41 +++++++++++++++++++++++++++++++++++++ accel/tcg/cputlb.c | 1 + accel/tcg/internal-target.h | 28 ------------------------- accel/tcg/user-exec.c | 1 + 4 files changed, 43 insertions(+), 28 deletions(-) create mode 100644 accel/tcg/backend-ldst.h diff --git a/accel/tcg/backend-ldst.h b/accel/tcg/backend-ldst.h new file mode 100644 index 0000000000..9c3a407a5a --- /dev/null +++ b/accel/tcg/backend-ldst.h @@ -0,0 +1,41 @@ +/* + * Internal memory barrier helpers for QEMU (target agnostic) + * + * Copyright (c) 2003 Fabrice Bellard + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_BACKEND_LDST_H +#define ACCEL_TCG_BACKEND_LDST_H + +#include "tcg-target-mo.h" + +/** + * tcg_req_mo: + * @guest_mo: Guest default memory order + * @type: TCGBar + * + * Filter @type to the barrier that is required for the guest + * memory ordering vs the host memory ordering. A non-zero + * result indicates that some barrier is required. + */ +#define tcg_req_mo(guest_mo, type) \ + ((type) & guest_mo & ~TCG_TARGET_DEFAULT_MO) + +/** + * cpu_req_mo: + * @cpu: CPUState + * @type: TCGBar + * + * If tcg_req_mo indicates a barrier for @type is required + * for the guest memory model, issue a host memory barrier. + */ +#define cpu_req_mo(cpu, type) \ + do { \ + if (tcg_req_mo(cpu->cc->tcg_ops->guest_default_memory_order, type)) { \ + smp_mb(); \ + } \ + } while (0) + +#endif diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 35b1ff03a5..d9fb68d719 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -48,6 +48,7 @@ #include "qemu/plugin-memory.h" #endif #include "tcg/tcg-ldst.h" +#include "backend-ldst.h" /* DEBUG defines, enable DEBUG_TLB_LOG to log to the CPU_LOG_MMU target */ diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h index f5a3fd7e40..9a9cef3140 100644 --- a/accel/tcg/internal-target.h +++ b/accel/tcg/internal-target.h @@ -13,7 +13,6 @@ #include "exec/exec-all.h" #include "exec/translation-block.h" #include "tb-internal.h" -#include "tcg-target-mo.h" #include "exec/mmap-lock.h" /* @@ -44,31 +43,4 @@ void page_table_config_init(void); G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); #endif /* CONFIG_USER_ONLY */ -/** - * tcg_req_mo: - * @guest_mo: Guest default memory order - * @type: TCGBar - * - * Filter @type to the barrier that is required for the guest - * memory ordering vs the host memory ordering. A non-zero - * result indicates that some barrier is required. - */ -#define tcg_req_mo(guest_mo, type) \ - ((type) & guest_mo & ~TCG_TARGET_DEFAULT_MO) - -/** - * cpu_req_mo: - * @cpu: CPUState - * @type: TCGBar - * - * If tcg_req_mo indicates a barrier for @type is required - * for the guest memory model, issue a host memory barrier. - */ -#define cpu_req_mo(cpu, type) \ - do { \ - if (tcg_req_mo(cpu->cc->tcg_ops->guest_default_memory_order, type)) { \ - smp_mb(); \ - } \ - } while (0) - #endif /* ACCEL_TCG_INTERNAL_H */ diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 3f4d682446..5eef8e7f18 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -37,6 +37,7 @@ #include "qemu/int128.h" #include "trace.h" #include "tcg/tcg-ldst.h" +#include "backend-ldst.h" #include "internal-common.h" #include "internal-target.h" #include "tb-internal.h" From a9d107fa0eacf6c999c042b276e54a7058ae0bf9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Apr 2025 16:30:57 -0700 Subject: [PATCH 0220/2760] tcg: Pass max_threads not max_cpus to tcg_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In effect, hoist the check for mttcg from tcg_n_regions() to tcg_init_machine(). Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tcg-all.c | 14 ++++++++------ include/tcg/startup.h | 6 +++--- tcg/region.c | 27 ++++++++++++--------------- tcg/tcg-internal.h | 2 +- tcg/tcg.c | 14 +++++++------- 5 files changed, 31 insertions(+), 32 deletions(-) diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index a5a1fd6a11..3efc7350eb 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -103,18 +103,20 @@ bool one_insn_per_tb; static int tcg_init_machine(MachineState *ms) { TCGState *s = TCG_STATE(current_accel()); -#ifdef CONFIG_USER_ONLY - unsigned max_cpus = 1; -#else - unsigned max_cpus = ms->smp.max_cpus; -#endif + unsigned max_threads = 1; tcg_allowed = true; mttcg_enabled = s->mttcg_enabled; page_init(); tb_htable_init(); - tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_cpus); + +#ifndef CONFIG_USER_ONLY + if (mttcg_enabled) { + max_threads = ms->smp.max_cpus; + } +#endif + tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_threads); #if defined(CONFIG_SOFTMMU) /* diff --git a/include/tcg/startup.h b/include/tcg/startup.h index f71305765c..95f574af2b 100644 --- a/include/tcg/startup.h +++ b/include/tcg/startup.h @@ -29,12 +29,12 @@ * tcg_init: Initialize the TCG runtime * @tb_size: translation buffer size * @splitwx: use separate rw and rx mappings - * @max_cpus: number of vcpus in system mode + * @max_threads: number of vcpu threads in system mode * * Allocate and initialize TCG resources, especially the JIT buffer. - * In user-only mode, @max_cpus is unused. + * In user-only mode, @max_threads is unused. */ -void tcg_init(size_t tb_size, int splitwx, unsigned max_cpus); +void tcg_init(size_t tb_size, int splitwx, unsigned max_threads); /** * tcg_register_thread: Register this thread with the TCG runtime diff --git a/tcg/region.c b/tcg/region.c index 478ec051c4..7ea0b37a84 100644 --- a/tcg/region.c +++ b/tcg/region.c @@ -422,7 +422,7 @@ void tcg_region_reset_all(void) tcg_region_tree_reset_all(); } -static size_t tcg_n_regions(size_t tb_size, unsigned max_cpus) +static size_t tcg_n_regions(size_t tb_size, unsigned max_threads) { #ifdef CONFIG_USER_ONLY return 1; @@ -431,24 +431,25 @@ static size_t tcg_n_regions(size_t tb_size, unsigned max_cpus) /* * It is likely that some vCPUs will translate more code than others, - * so we first try to set more regions than max_cpus, with those regions + * so we first try to set more regions than threads, with those regions * being of reasonable size. If that's not possible we make do by evenly * dividing the code_gen_buffer among the vCPUs. + * + * Use a single region if all we have is one vCPU thread. */ - /* Use a single region if all we have is one vCPU thread */ - if (max_cpus == 1 || !qemu_tcg_mttcg_enabled()) { + if (max_threads == 1) { return 1; } /* - * Try to have more regions than max_cpus, with each region being >= 2 MB. + * Try to have more regions than threads, with each region being >= 2 MB. * If we can't, then just allocate one region per vCPU thread. */ n_regions = tb_size / (2 * MiB); - if (n_regions <= max_cpus) { - return max_cpus; + if (n_regions <= max_threads) { + return max_threads; } - return MIN(n_regions, max_cpus * 8); + return MIN(n_regions, max_threads * 8); #endif } @@ -731,11 +732,7 @@ static int alloc_code_gen_buffer(size_t size, int splitwx, Error **errp) * and then assigning regions to TCG threads so that the threads can translate * code in parallel without synchronization. * - * In system-mode the number of TCG threads is bounded by max_cpus, so we use at - * least max_cpus regions in MTTCG. In !MTTCG we use a single region. - * Note that the TCG options from the command-line (i.e. -accel accel=tcg,[...]) - * must have been parsed before calling this function, since it calls - * qemu_tcg_mttcg_enabled(). + * In system-mode the number of TCG threads is bounded by max_threads, * * In user-mode we use a single region. Having multiple regions in user-mode * is not supported, because the number of vCPU threads (recall that each thread @@ -749,7 +746,7 @@ static int alloc_code_gen_buffer(size_t size, int splitwx, Error **errp) * in practice. Multi-threaded guests share most if not all of their translated * code, which makes parallel code generation less appealing than in system-mode */ -void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus) +void tcg_region_init(size_t tb_size, int splitwx, unsigned max_threads) { const size_t page_size = qemu_real_host_page_size(); size_t region_size; @@ -787,7 +784,7 @@ void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus) * As a result of this we might end up with a few extra pages at the end of * the buffer; we will assign those to the last region. */ - region.n = tcg_n_regions(tb_size, max_cpus); + region.n = tcg_n_regions(tb_size, max_threads); region_size = tb_size / region.n; region_size = QEMU_ALIGN_DOWN(region_size, page_size); diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index a648ee7a0e..ff85fb23fa 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -34,7 +34,7 @@ extern TCGContext **tcg_ctxs; extern unsigned int tcg_cur_ctxs; extern unsigned int tcg_max_ctxs; -void tcg_region_init(size_t tb_size, int splitwx, unsigned max_cpus); +void tcg_region_init(size_t tb_size, int splitwx, unsigned max_threads); bool tcg_region_alloc(TCGContext *s); void tcg_region_initial_alloc(TCGContext *s); void tcg_region_prologue_set(TCGContext *s); diff --git a/tcg/tcg.c b/tcg/tcg.c index dfd48b8264..ec7f6743d7 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1499,7 +1499,7 @@ static void process_constraint_sets(void); static TCGTemp *tcg_global_reg_new_internal(TCGContext *s, TCGType type, TCGReg reg, const char *name); -static void tcg_context_init(unsigned max_cpus) +static void tcg_context_init(unsigned max_threads) { TCGContext *s = &tcg_init_ctx; int n, i; @@ -1538,15 +1538,15 @@ static void tcg_context_init(unsigned max_cpus) * In user-mode we simply share the init context among threads, since we * use a single region. See the documentation tcg_region_init() for the * reasoning behind this. - * In system-mode we will have at most max_cpus TCG threads. + * In system-mode we will have at most max_threads TCG threads. */ #ifdef CONFIG_USER_ONLY tcg_ctxs = &tcg_ctx; tcg_cur_ctxs = 1; tcg_max_ctxs = 1; #else - tcg_max_ctxs = max_cpus; - tcg_ctxs = g_new0(TCGContext *, max_cpus); + tcg_max_ctxs = max_threads; + tcg_ctxs = g_new0(TCGContext *, max_threads); #endif tcg_debug_assert(!tcg_regset_test_reg(s->reserved_regs, TCG_AREG0)); @@ -1554,10 +1554,10 @@ static void tcg_context_init(unsigned max_cpus) tcg_env = temp_tcgv_ptr(ts); } -void tcg_init(size_t tb_size, int splitwx, unsigned max_cpus) +void tcg_init(size_t tb_size, int splitwx, unsigned max_threads) { - tcg_context_init(max_cpus); - tcg_region_init(tb_size, splitwx, max_cpus); + tcg_context_init(max_threads); + tcg_region_init(tb_size, splitwx, max_threads); } /* From 60b2c2e66b81c323c0bc70ea4233cdbf8cdae5b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Apr 2025 00:04:16 +0200 Subject: [PATCH 0221/2760] tcg: Move qemu_tcg_mttcg_enabled() to 'system/tcg.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_tcg_mttcg_enabled() is specific to 1/ TCG and 2/ system emulation. Move the prototype declaration to "system/tcg.h", reducing 'mttcg_enabled' variable scope. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-ID: <20250403220420.78937-17-philmd@linaro.org> Signed-off-by: Richard Henderson --- accel/tcg/tcg-all.c | 17 ++++++++++++++--- include/hw/core/cpu.h | 9 --------- include/system/tcg.h | 8 ++++++++ target/riscv/tcg/tcg-cpu.c | 1 + 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 3efc7350eb..bb759cec07 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -38,6 +38,7 @@ #include "hw/qdev-core.h" #else #include "hw/boards.h" +#include "system/tcg.h" #endif #include "internal-common.h" #include "cpu-param.h" @@ -58,6 +59,17 @@ typedef struct TCGState TCGState; DECLARE_INSTANCE_CHECKER(TCGState, TCG_STATE, TYPE_TCG_ACCEL) +#ifndef CONFIG_USER_ONLY + +static bool mttcg_enabled; + +bool qemu_tcg_mttcg_enabled(void) +{ + return mttcg_enabled; +} + +#endif /* !CONFIG_USER_ONLY */ + /* * We default to false if we know other options have been enabled * which are currently incompatible with MTTCG. Otherwise when each @@ -97,7 +109,6 @@ static void tcg_accel_instance_init(Object *obj) #endif } -bool mttcg_enabled; bool one_insn_per_tb; static int tcg_init_machine(MachineState *ms) @@ -106,14 +117,14 @@ static int tcg_init_machine(MachineState *ms) unsigned max_threads = 1; tcg_allowed = true; - mttcg_enabled = s->mttcg_enabled; page_init(); tb_htable_init(); #ifndef CONFIG_USER_ONLY - if (mttcg_enabled) { + if (s->mttcg_enabled) { max_threads = ms->smp.max_cpus; + mttcg_enabled = true; } #endif tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_threads); diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 10b6b25b34..c8d6abff19 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -594,15 +594,6 @@ extern CPUTailQ cpus_queue; extern __thread CPUState *current_cpu; -/** - * qemu_tcg_mttcg_enabled: - * Check whether we are running MultiThread TCG or not. - * - * Returns: %true if we are in MTTCG mode %false otherwise. - */ -extern bool mttcg_enabled; -#define qemu_tcg_mttcg_enabled() (mttcg_enabled) - /** * cpu_paging_enabled: * @cpu: The CPU whose state is to be inspected. diff --git a/include/system/tcg.h b/include/system/tcg.h index 73229648c6..7622dcea30 100644 --- a/include/system/tcg.h +++ b/include/system/tcg.h @@ -17,4 +17,12 @@ extern bool tcg_allowed; #define tcg_enabled() 0 #endif +/** + * qemu_tcg_mttcg_enabled: + * Check whether we are running MultiThread TCG or not. + * + * Returns: %true if we are in MTTCG mode %false otherwise. + */ +bool qemu_tcg_mttcg_enabled(void); + #endif diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 50e81b2e52..88f7cdb887 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -36,6 +36,7 @@ #include "tcg/tcg.h" #ifndef CONFIG_USER_ONLY #include "hw/boards.h" +#include "system/tcg.h" #endif /* Hash that stores user set extensions */ From 61fc4c2bfaee8f4da75ce217911f103da7a8a69e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Apr 2025 18:10:53 -0700 Subject: [PATCH 0222/2760] accel/tcg: Remove mttcg_enabled In qemu_tcg_mttcg_enabled, read the value from TCGState and eliminate the separate global variable. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tcg-all.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index bb759cec07..b754f92905 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -60,14 +60,11 @@ DECLARE_INSTANCE_CHECKER(TCGState, TCG_STATE, TYPE_TCG_ACCEL) #ifndef CONFIG_USER_ONLY - -static bool mttcg_enabled; - bool qemu_tcg_mttcg_enabled(void) { - return mttcg_enabled; + TCGState *s = TCG_STATE(current_accel()); + return s->mttcg_enabled; } - #endif /* !CONFIG_USER_ONLY */ /* @@ -124,7 +121,6 @@ static int tcg_init_machine(MachineState *ms) #ifndef CONFIG_USER_ONLY if (s->mttcg_enabled) { max_threads = ms->smp.max_cpus; - mttcg_enabled = true; } #endif tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_threads); From d1aa577228e6eeda3589c6480d994ee4bd0d23e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 2 Apr 2025 16:48:24 +0200 Subject: [PATCH 0223/2760] tcg: Convert TCGState::mttcg_enabled to TriState MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the OnOffAuto type as 3-state. Since the TCGState instance is zero-initialized, the mttcg_enabled is initialzed as AUTO (ON_OFF_AUTO_AUTO). In tcg_init_machine(), if mttcg_enabled is still AUTO, set a default value (effectively inlining the default_mttcg_enabled() method content). In the tcg_get_thread() getter, consider AUTO / OFF states as "single", otherwise ON is "multi". Reviewed-by: Anton Johansson Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/tcg-all.c | 74 ++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index b754f92905..fa77a4c5a2 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -32,6 +32,7 @@ #include "qemu/error-report.h" #include "qemu/accel.h" #include "qemu/atomic.h" +#include "qapi/qapi-types-common.h" #include "qapi/qapi-builtin-visit.h" #include "qemu/units.h" #if defined(CONFIG_USER_ONLY) @@ -47,7 +48,7 @@ struct TCGState { AccelState parent_obj; - bool mttcg_enabled; + OnOffAuto mttcg_enabled; bool one_insn_per_tb; int splitwx_enabled; unsigned long tb_size; @@ -63,41 +64,14 @@ DECLARE_INSTANCE_CHECKER(TCGState, TCG_STATE, bool qemu_tcg_mttcg_enabled(void) { TCGState *s = TCG_STATE(current_accel()); - return s->mttcg_enabled; + return s->mttcg_enabled == ON_OFF_AUTO_ON; } #endif /* !CONFIG_USER_ONLY */ -/* - * We default to false if we know other options have been enabled - * which are currently incompatible with MTTCG. Otherwise when each - * guest (target) has been updated to support: - * - atomic instructions - * - memory ordering primitives (barriers) - * they can set the appropriate CONFIG flags in ${target}-softmmu.mak - * - * Once a guest architecture has been converted to the new primitives - * there is one remaining limitation to check: - * - The guest can't be oversized (e.g. 64 bit guest on 32 bit host) - */ - -static bool default_mttcg_enabled(void) -{ - if (icount_enabled()) { - return false; - } -#ifdef TARGET_SUPPORTS_MTTCG - return true; -#else - return false; -#endif -} - static void tcg_accel_instance_init(Object *obj) { TCGState *s = TCG_STATE(obj); - s->mttcg_enabled = default_mttcg_enabled(); - /* If debugging enabled, default "auto on", otherwise off. */ #if defined(CONFIG_DEBUG_TCG) && !defined(CONFIG_USER_ONLY) s->splitwx_enabled = -1; @@ -113,16 +87,40 @@ static int tcg_init_machine(MachineState *ms) TCGState *s = TCG_STATE(current_accel()); unsigned max_threads = 1; - tcg_allowed = true; - - page_init(); - tb_htable_init(); - #ifndef CONFIG_USER_ONLY - if (s->mttcg_enabled) { +# ifdef TARGET_SUPPORTS_MTTCG + bool mttcg_supported = true; +# else + bool mttcg_supported = false; +# endif + if (s->mttcg_enabled == ON_OFF_AUTO_AUTO) { + /* + * We default to false if we know other options have been enabled + * which are currently incompatible with MTTCG. Otherwise when each + * guest (target) has been updated to support: + * - atomic instructions + * - memory ordering primitives (barriers) + * they can set the appropriate CONFIG flags in ${target}-softmmu.mak + * + * Once a guest architecture has been converted to the new primitives + * there is one remaining limitation to check: + * - The guest can't be oversized (e.g. 64 bit guest on 32 bit host) + */ + if (mttcg_supported && !icount_enabled()) { + s->mttcg_enabled = ON_OFF_AUTO_ON; + } else { + s->mttcg_enabled = ON_OFF_AUTO_OFF; + } + } + if (s->mttcg_enabled == ON_OFF_AUTO_ON) { max_threads = ms->smp.max_cpus; } #endif + + tcg_allowed = true; + + page_init(); + tb_htable_init(); tcg_init(s->tb_size * MiB, s->splitwx_enabled, max_threads); #if defined(CONFIG_SOFTMMU) @@ -144,7 +142,7 @@ static char *tcg_get_thread(Object *obj, Error **errp) { TCGState *s = TCG_STATE(obj); - return g_strdup(s->mttcg_enabled ? "multi" : "single"); + return g_strdup(s->mttcg_enabled == ON_OFF_AUTO_ON ? "multi" : "single"); } static void tcg_set_thread(Object *obj, const char *value, Error **errp) @@ -159,10 +157,10 @@ static void tcg_set_thread(Object *obj, const char *value, Error **errp) warn_report("Guest not yet converted to MTTCG - " "you may get unexpected results"); #endif - s->mttcg_enabled = true; + s->mttcg_enabled = ON_OFF_AUTO_ON; } } else if (strcmp(value, "single") == 0) { - s->mttcg_enabled = false; + s->mttcg_enabled = ON_OFF_AUTO_OFF; } else { error_setg(errp, "Invalid 'thread' setting %s", value); } From 84cde4af081b9fa9c0fc82d3f1a8da406b4ba9e4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Apr 2025 18:52:36 -0700 Subject: [PATCH 0224/2760] accel/tcg: Move mttcg warning to tcg_init_machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Delay the warning to tcg_init_machine, because we will have resolved the CPUClass at that point. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tcg-all.c | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index fa77a4c5a2..ecdd48847c 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -93,7 +93,8 @@ static int tcg_init_machine(MachineState *ms) # else bool mttcg_supported = false; # endif - if (s->mttcg_enabled == ON_OFF_AUTO_AUTO) { + switch (s->mttcg_enabled) { + case ON_OFF_AUTO_AUTO: /* * We default to false if we know other options have been enabled * which are currently incompatible with MTTCG. Otherwise when each @@ -108,12 +109,22 @@ static int tcg_init_machine(MachineState *ms) */ if (mttcg_supported && !icount_enabled()) { s->mttcg_enabled = ON_OFF_AUTO_ON; + max_threads = ms->smp.max_cpus; } else { s->mttcg_enabled = ON_OFF_AUTO_OFF; } - } - if (s->mttcg_enabled == ON_OFF_AUTO_ON) { + break; + case ON_OFF_AUTO_ON: + if (!mttcg_supported) { + warn_report("Guest not yet converted to MTTCG - " + "you may get unexpected results"); + } max_threads = ms->smp.max_cpus; + break; + case ON_OFF_AUTO_OFF: + break; + default: + g_assert_not_reached(); } #endif @@ -153,10 +164,6 @@ static void tcg_set_thread(Object *obj, const char *value, Error **errp) if (icount_enabled()) { error_setg(errp, "No MTTCG when icount is enabled"); } else { -#ifndef TARGET_SUPPORTS_MTTCG - warn_report("Guest not yet converted to MTTCG - " - "you may get unexpected results"); -#endif s->mttcg_enabled = ON_OFF_AUTO_ON; } } else if (strcmp(value, "single") == 0) { From f50d0f335a6e48ac757cb7e534542822a8db8211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 5 Apr 2025 18:13:05 +0200 Subject: [PATCH 0225/2760] target/riscv: Remove AccelCPUClass::cpu_class_init need MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose riscv_tcg_ops symbol, then directly set it as CPUClass::tcg_ops in TYPE_RISCV_CPU's class_init(), using CONFIG_TCG #ifdef'ry. No need for the AccelCPUClass::cpu_class_init() handler anymore. Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250405161320.76854-2-philmd@linaro.org> Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- target/riscv/cpu.c | 3 +++ target/riscv/tcg/tcg-cpu.c | 16 +--------------- target/riscv/tcg/tcg-cpu.h | 2 ++ 3 files changed, 6 insertions(+), 15 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index ad534cee51..2b830b3317 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3054,6 +3054,9 @@ static void riscv_cpu_common_class_init(ObjectClass *c, void *data) cc->get_arch_id = riscv_get_arch_id; #endif cc->gdb_arch_name = riscv_gdb_arch_name; +#ifdef CONFIG_TCG + cc->tcg_ops = &riscv_tcg_ops; +#endif /* CONFIG_TCG */ device_class_set_props(dc, riscv_cpu_properties); } diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 88f7cdb887..44fdf6c4cf 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -140,7 +140,7 @@ static void riscv_restore_state_to_opc(CPUState *cs, env->excp_uw2 = data[2]; } -static const TCGCPUOps riscv_tcg_ops = { +const TCGCPUOps riscv_tcg_ops = { .guest_default_memory_order = 0, .initialize = riscv_translate_init, @@ -1527,24 +1527,10 @@ static void riscv_tcg_cpu_instance_init(CPUState *cs) } } -static void riscv_tcg_cpu_init_ops(AccelCPUClass *accel_cpu, CPUClass *cc) -{ - /* - * All cpus use the same set of operations. - */ - cc->tcg_ops = &riscv_tcg_ops; -} - -static void riscv_tcg_cpu_class_init(CPUClass *cc) -{ - cc->init_accel_cpu = riscv_tcg_cpu_init_ops; -} - static void riscv_tcg_cpu_accel_class_init(ObjectClass *oc, void *data) { AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); - acc->cpu_class_init = riscv_tcg_cpu_class_init; acc->cpu_instance_init = riscv_tcg_cpu_instance_init; acc->cpu_target_realize = riscv_tcg_cpu_realize; } diff --git a/target/riscv/tcg/tcg-cpu.h b/target/riscv/tcg/tcg-cpu.h index ce94253fe4..a23716a5ac 100644 --- a/target/riscv/tcg/tcg-cpu.h +++ b/target/riscv/tcg/tcg-cpu.h @@ -26,6 +26,8 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp); void riscv_tcg_cpu_finalize_features(RISCVCPU *cpu, Error **errp); bool riscv_cpu_tcg_compatible(RISCVCPU *cpu); +extern const TCGCPUOps riscv_tcg_ops; + struct DisasContext; struct RISCVCPUConfig; typedef struct RISCVDecoder { From a522b04bb9cf67789116ad7a6165946d4b214bac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 5 Apr 2025 18:13:06 +0200 Subject: [PATCH 0226/2760] target/i386: Remove AccelCPUClass::cpu_class_init need MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expose x86_tcg_ops symbol, then directly set it as CPUClass::tcg_ops in TYPE_X86_CPU's class_init(), using CONFIG_TCG #ifdef'ry. No need for the AccelCPUClass::cpu_class_init() handler anymore. Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250405161320.76854-3-philmd@linaro.org> Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson --- target/i386/cpu.c | 4 ++++ target/i386/tcg/tcg-cpu.c | 14 +------------- target/i386/tcg/tcg-cpu.h | 4 ++++ 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 57f62cc869..1f970aa4da 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -43,6 +43,7 @@ #include "hw/boards.h" #include "hw/i386/sgx-epc.h" #endif +#include "tcg/tcg-cpu.h" #include "disas/capstone.h" #include "cpu-internal.h" @@ -8915,6 +8916,9 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) #ifndef CONFIG_USER_ONLY cc->sysemu_ops = &i386_sysemu_ops; #endif /* !CONFIG_USER_ONLY */ +#ifdef CONFIG_TCG + cc->tcg_ops = &x86_tcg_ops; +#endif /* CONFIG_TCG */ cc->gdb_arch_name = x86_gdb_arch_name; #ifdef TARGET_X86_64 diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index d941df0956..e13d0f6f86 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -124,7 +124,7 @@ static bool x86_debug_check_breakpoint(CPUState *cs) #include "accel/tcg/cpu-ops.h" -static const TCGCPUOps x86_tcg_ops = { +const TCGCPUOps x86_tcg_ops = { /* * The x86 has a strong memory model with some store-after-load re-ordering */ @@ -152,17 +152,6 @@ static const TCGCPUOps x86_tcg_ops = { #endif /* !CONFIG_USER_ONLY */ }; -static void x86_tcg_cpu_init_ops(AccelCPUClass *accel_cpu, CPUClass *cc) -{ - /* for x86, all cpus use the same set of operations */ - cc->tcg_ops = &x86_tcg_ops; -} - -static void x86_tcg_cpu_class_init(CPUClass *cc) -{ - cc->init_accel_cpu = x86_tcg_cpu_init_ops; -} - static void x86_tcg_cpu_xsave_init(void) { #define XO(bit, field) \ @@ -211,7 +200,6 @@ static void x86_tcg_cpu_accel_class_init(ObjectClass *oc, void *data) acc->cpu_target_realize = tcg_cpu_realizefn; #endif /* CONFIG_USER_ONLY */ - acc->cpu_class_init = x86_tcg_cpu_class_init; acc->cpu_instance_init = x86_tcg_cpu_instance_init; } static const TypeInfo x86_tcg_cpu_accel_type_info = { diff --git a/target/i386/tcg/tcg-cpu.h b/target/i386/tcg/tcg-cpu.h index 7580f8afb4..85bcd61678 100644 --- a/target/i386/tcg/tcg-cpu.h +++ b/target/i386/tcg/tcg-cpu.h @@ -19,6 +19,8 @@ #ifndef TCG_CPU_H #define TCG_CPU_H +#include "cpu.h" + #define XSAVE_FCW_FSW_OFFSET 0x000 #define XSAVE_FTW_FOP_OFFSET 0x004 #define XSAVE_CWD_RIP_OFFSET 0x008 @@ -76,6 +78,8 @@ QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, zmm_hi256_state) != XSAVE_ZMM_HI256_OFF QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, hi16_zmm_state) != XSAVE_HI16_ZMM_OFFSET); QEMU_BUILD_BUG_ON(offsetof(X86XSaveArea, pkru_state) != XSAVE_PKRU_OFFSET); +extern const TCGCPUOps x86_tcg_ops; + bool tcg_cpu_realizefn(CPUState *cs, Error **errp); int x86_mmu_index_pl(CPUX86State *env, unsigned pl); From a3d40b5effafdd299d1850f0c9956f60199b5b56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sat, 5 Apr 2025 18:13:20 +0200 Subject: [PATCH 0227/2760] tcg: Convert TARGET_SUPPORTS_MTTCG to TCGCPUOps::mttcg_supported field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of having a compile-time TARGET_SUPPORTS_MTTCG definition, have each target set the 'mttcg_supported' field in the TCGCPUOps structure. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-ID: <20250405161320.76854-17-philmd@linaro.org> Signed-off-by: Richard Henderson --- accel/tcg/tcg-all.c | 11 +++++------ configs/targets/aarch64-softmmu.mak | 1 - configs/targets/alpha-softmmu.mak | 1 - configs/targets/arm-softmmu.mak | 1 - configs/targets/hppa-softmmu.mak | 1 - configs/targets/i386-softmmu.mak | 1 - configs/targets/loongarch64-softmmu.mak | 1 - configs/targets/microblaze-softmmu.mak | 1 - configs/targets/microblazeel-softmmu.mak | 1 - configs/targets/mips-softmmu.mak | 1 - configs/targets/mipsel-softmmu.mak | 1 - configs/targets/or1k-softmmu.mak | 1 - configs/targets/ppc64-softmmu.mak | 1 - configs/targets/riscv32-softmmu.mak | 1 - configs/targets/riscv64-softmmu.mak | 1 - configs/targets/s390x-softmmu.mak | 1 - configs/targets/sparc-softmmu.mak | 1 - configs/targets/sparc64-softmmu.mak | 1 - configs/targets/x86_64-softmmu.mak | 1 - configs/targets/xtensa-softmmu.mak | 1 - configs/targets/xtensaeb-softmmu.mak | 1 - docs/devel/multi-thread-tcg.rst | 2 +- include/accel/tcg/cpu-ops.h | 8 ++++++++ include/exec/poison.h | 1 - target/alpha/cpu.c | 1 + target/arm/cpu.c | 1 + target/arm/tcg/cpu-v7m.c | 1 + target/avr/cpu.c | 1 + target/hexagon/cpu.c | 1 + target/hppa/cpu.c | 1 + target/i386/tcg/tcg-cpu.c | 1 + target/loongarch/cpu.c | 1 + target/m68k/cpu.c | 1 + target/microblaze/cpu.c | 1 + target/mips/cpu.c | 1 + target/openrisc/cpu.c | 1 + target/ppc/cpu_init.c | 1 + target/riscv/tcg/tcg-cpu.c | 1 + target/rx/cpu.c | 1 + target/s390x/cpu.c | 1 + target/sh4/cpu.c | 1 + target/sparc/cpu.c | 1 + target/tricore/cpu.c | 1 + target/xtensa/cpu.c | 1 + 44 files changed, 34 insertions(+), 28 deletions(-) diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index ecdd48847c..b0d4e3e136 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -41,8 +41,9 @@ #include "hw/boards.h" #include "system/tcg.h" #endif +#include "accel/tcg/cpu-ops.h" #include "internal-common.h" -#include "cpu-param.h" +#include "cpu.h" struct TCGState { @@ -88,11 +89,9 @@ static int tcg_init_machine(MachineState *ms) unsigned max_threads = 1; #ifndef CONFIG_USER_ONLY -# ifdef TARGET_SUPPORTS_MTTCG - bool mttcg_supported = true; -# else - bool mttcg_supported = false; -# endif + CPUClass *cc = CPU_CLASS(object_class_by_name(CPU_RESOLVING_TYPE)); + bool mttcg_supported = cc->tcg_ops->mttcg_supported; + switch (s->mttcg_enabled) { case ON_OFF_AUTO_AUTO: /* diff --git a/configs/targets/aarch64-softmmu.mak b/configs/targets/aarch64-softmmu.mak index 82cb72cb83..5dfeb35af9 100644 --- a/configs/targets/aarch64-softmmu.mak +++ b/configs/targets/aarch64-softmmu.mak @@ -1,6 +1,5 @@ TARGET_ARCH=aarch64 TARGET_BASE_ARCH=arm -TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml gdb-xml/aarch64-pauth.xml # needed by boot.c diff --git a/configs/targets/alpha-softmmu.mak b/configs/targets/alpha-softmmu.mak index 89f3517aca..5275076e50 100644 --- a/configs/targets/alpha-softmmu.mak +++ b/configs/targets/alpha-softmmu.mak @@ -1,3 +1,2 @@ TARGET_ARCH=alpha -TARGET_SUPPORTS_MTTCG=y TARGET_LONG_BITS=64 diff --git a/configs/targets/arm-softmmu.mak b/configs/targets/arm-softmmu.mak index afc64f5927..6a5a8eda94 100644 --- a/configs/targets/arm-softmmu.mak +++ b/configs/targets/arm-softmmu.mak @@ -1,5 +1,4 @@ TARGET_ARCH=arm -TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/arm-core.xml gdb-xml/arm-vfp.xml gdb-xml/arm-vfp3.xml gdb-xml/arm-vfp-sysregs.xml gdb-xml/arm-neon.xml gdb-xml/arm-m-profile.xml gdb-xml/arm-m-profile-mve.xml # needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/hppa-softmmu.mak b/configs/targets/hppa-softmmu.mak index 63ca74ed5e..ea331107a0 100644 --- a/configs/targets/hppa-softmmu.mak +++ b/configs/targets/hppa-softmmu.mak @@ -1,4 +1,3 @@ TARGET_ARCH=hppa TARGET_BIG_ENDIAN=y -TARGET_SUPPORTS_MTTCG=y TARGET_LONG_BITS=64 diff --git a/configs/targets/i386-softmmu.mak b/configs/targets/i386-softmmu.mak index 5dd8921756..e9d89e8ab4 100644 --- a/configs/targets/i386-softmmu.mak +++ b/configs/targets/i386-softmmu.mak @@ -1,5 +1,4 @@ TARGET_ARCH=i386 -TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_KVM_HAVE_RESET_PARKED_VCPU=y TARGET_XML_FILES= gdb-xml/i386-32bit.xml diff --git a/configs/targets/loongarch64-softmmu.mak b/configs/targets/loongarch64-softmmu.mak index 351341132f..fc44c54233 100644 --- a/configs/targets/loongarch64-softmmu.mak +++ b/configs/targets/loongarch64-softmmu.mak @@ -1,7 +1,6 @@ TARGET_ARCH=loongarch64 TARGET_BASE_ARCH=loongarch TARGET_KVM_HAVE_GUEST_DEBUG=y -TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/loongarch-base32.xml gdb-xml/loongarch-base64.xml gdb-xml/loongarch-fpu.xml gdb-xml/loongarch-lsx.xml gdb-xml/loongarch-lasx.xml # all boards require libfdt TARGET_NEED_FDT=y diff --git a/configs/targets/microblaze-softmmu.mak b/configs/targets/microblaze-softmmu.mak index 99a33ed44a..23457d0ae6 100644 --- a/configs/targets/microblaze-softmmu.mak +++ b/configs/targets/microblaze-softmmu.mak @@ -1,6 +1,5 @@ TARGET_ARCH=microblaze TARGET_BIG_ENDIAN=y -TARGET_SUPPORTS_MTTCG=y # needed by boot.c TARGET_NEED_FDT=y TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml diff --git a/configs/targets/microblazeel-softmmu.mak b/configs/targets/microblazeel-softmmu.mak index 52cdeae1a2..c82c509623 100644 --- a/configs/targets/microblazeel-softmmu.mak +++ b/configs/targets/microblazeel-softmmu.mak @@ -1,5 +1,4 @@ TARGET_ARCH=microblaze -TARGET_SUPPORTS_MTTCG=y # needed by boot.c TARGET_NEED_FDT=y TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml diff --git a/configs/targets/mips-softmmu.mak b/configs/targets/mips-softmmu.mak index b62a088249..c9588066b8 100644 --- a/configs/targets/mips-softmmu.mak +++ b/configs/targets/mips-softmmu.mak @@ -1,4 +1,3 @@ TARGET_ARCH=mips TARGET_BIG_ENDIAN=y -TARGET_SUPPORTS_MTTCG=y TARGET_LONG_BITS=32 diff --git a/configs/targets/mipsel-softmmu.mak b/configs/targets/mipsel-softmmu.mak index 620ec68178..90e09bdc3e 100644 --- a/configs/targets/mipsel-softmmu.mak +++ b/configs/targets/mipsel-softmmu.mak @@ -1,3 +1,2 @@ TARGET_ARCH=mips -TARGET_SUPPORTS_MTTCG=y TARGET_LONG_BITS=32 diff --git a/configs/targets/or1k-softmmu.mak b/configs/targets/or1k-softmmu.mak index adfddb1a8a..0e47d9878b 100644 --- a/configs/targets/or1k-softmmu.mak +++ b/configs/targets/or1k-softmmu.mak @@ -1,5 +1,4 @@ TARGET_ARCH=openrisc -TARGET_SUPPORTS_MTTCG=y TARGET_BIG_ENDIAN=y # needed by boot.c and all boards TARGET_NEED_FDT=y diff --git a/configs/targets/ppc64-softmmu.mak b/configs/targets/ppc64-softmmu.mak index 7cee0e97f4..74572864b3 100644 --- a/configs/targets/ppc64-softmmu.mak +++ b/configs/targets/ppc64-softmmu.mak @@ -1,7 +1,6 @@ TARGET_ARCH=ppc64 TARGET_BASE_ARCH=ppc TARGET_BIG_ENDIAN=y -TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/power64-core.xml gdb-xml/power-fpu.xml gdb-xml/power-altivec.xml gdb-xml/power-spe.xml gdb-xml/power-vsx.xml # all boards require libfdt diff --git a/configs/targets/riscv32-softmmu.mak b/configs/targets/riscv32-softmmu.mak index c828066ce6..db55275b86 100644 --- a/configs/targets/riscv32-softmmu.mak +++ b/configs/targets/riscv32-softmmu.mak @@ -1,6 +1,5 @@ TARGET_ARCH=riscv32 TARGET_BASE_ARCH=riscv -TARGET_SUPPORTS_MTTCG=y TARGET_XML_FILES= gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-32bit-virtual.xml # needed by boot.c TARGET_NEED_FDT=y diff --git a/configs/targets/riscv64-softmmu.mak b/configs/targets/riscv64-softmmu.mak index 09f613d24a..2bdd4a62cd 100644 --- a/configs/targets/riscv64-softmmu.mak +++ b/configs/targets/riscv64-softmmu.mak @@ -1,6 +1,5 @@ TARGET_ARCH=riscv64 TARGET_BASE_ARCH=riscv -TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/riscv-64bit-cpu.xml gdb-xml/riscv-32bit-fpu.xml gdb-xml/riscv-64bit-fpu.xml gdb-xml/riscv-64bit-virtual.xml gdb-xml/riscv-32bit-cpu.xml gdb-xml/riscv-32bit-virtual.xml # needed by boot.c diff --git a/configs/targets/s390x-softmmu.mak b/configs/targets/s390x-softmmu.mak index 5242ebe7c2..76dd5de658 100644 --- a/configs/targets/s390x-softmmu.mak +++ b/configs/targets/s390x-softmmu.mak @@ -1,6 +1,5 @@ TARGET_ARCH=s390x TARGET_BIG_ENDIAN=y -TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_XML_FILES= gdb-xml/s390x-core64.xml gdb-xml/s390-acr.xml gdb-xml/s390-fpr.xml gdb-xml/s390-vx.xml gdb-xml/s390-cr.xml gdb-xml/s390-virt.xml gdb-xml/s390-virt-kvm.xml gdb-xml/s390-gs.xml TARGET_LONG_BITS=64 diff --git a/configs/targets/sparc-softmmu.mak b/configs/targets/sparc-softmmu.mak index 78c2e25bd1..57801faf1f 100644 --- a/configs/targets/sparc-softmmu.mak +++ b/configs/targets/sparc-softmmu.mak @@ -1,4 +1,3 @@ TARGET_ARCH=sparc TARGET_BIG_ENDIAN=y -TARGET_SUPPORTS_MTTCG=y TARGET_LONG_BITS=32 diff --git a/configs/targets/sparc64-softmmu.mak b/configs/targets/sparc64-softmmu.mak index f7bab97a00..2504e31ae3 100644 --- a/configs/targets/sparc64-softmmu.mak +++ b/configs/targets/sparc64-softmmu.mak @@ -1,5 +1,4 @@ TARGET_ARCH=sparc64 TARGET_BASE_ARCH=sparc TARGET_BIG_ENDIAN=y -TARGET_SUPPORTS_MTTCG=y TARGET_LONG_BITS=64 diff --git a/configs/targets/x86_64-softmmu.mak b/configs/targets/x86_64-softmmu.mak index 1ceefde131..5619b2bc68 100644 --- a/configs/targets/x86_64-softmmu.mak +++ b/configs/targets/x86_64-softmmu.mak @@ -1,6 +1,5 @@ TARGET_ARCH=x86_64 TARGET_BASE_ARCH=i386 -TARGET_SUPPORTS_MTTCG=y TARGET_KVM_HAVE_GUEST_DEBUG=y TARGET_KVM_HAVE_RESET_PARKED_VCPU=y TARGET_XML_FILES= gdb-xml/i386-64bit.xml diff --git a/configs/targets/xtensa-softmmu.mak b/configs/targets/xtensa-softmmu.mak index 65845df4ff..2a9797338a 100644 --- a/configs/targets/xtensa-softmmu.mak +++ b/configs/targets/xtensa-softmmu.mak @@ -1,3 +1,2 @@ TARGET_ARCH=xtensa -TARGET_SUPPORTS_MTTCG=y TARGET_LONG_BITS=32 diff --git a/configs/targets/xtensaeb-softmmu.mak b/configs/targets/xtensaeb-softmmu.mak index f1f789d697..5204729af8 100644 --- a/configs/targets/xtensaeb-softmmu.mak +++ b/configs/targets/xtensaeb-softmmu.mak @@ -1,4 +1,3 @@ TARGET_ARCH=xtensa TARGET_BIG_ENDIAN=y -TARGET_SUPPORTS_MTTCG=y TARGET_LONG_BITS=32 diff --git a/docs/devel/multi-thread-tcg.rst b/docs/devel/multi-thread-tcg.rst index 14a2a9dc7b..da9a1530c9 100644 --- a/docs/devel/multi-thread-tcg.rst +++ b/docs/devel/multi-thread-tcg.rst @@ -30,7 +30,7 @@ user-space thread. This is enabled by default for all FE/BE combinations where the host memory model is able to accommodate the guest (TCGCPUOps::guest_default_memory_order & ~TCG_TARGET_DEFAULT_MO is zero) and the guest has had the required work done to support this safely -(TARGET_SUPPORTS_MTTCG). +(TCGCPUOps::mttcg_supported). System emulation will fall back to the original round robin approach if: diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index a4932fc5d7..0e4352513d 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -19,6 +19,14 @@ #include "tcg/tcg-mo.h" struct TCGCPUOps { + /** + * mttcg_supported: multi-threaded TCG is supported + * + * Target (TCG frontend) supports: + * - atomic instructions + * - memory ordering primitives (barriers) + */ + bool mttcg_supported; /** * @guest_default_memory_order: default barrier that is required diff --git a/include/exec/poison.h b/include/exec/poison.h index a09e0c1263..bc422719d8 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -35,7 +35,6 @@ #pragma GCC poison TARGET_HAS_BFLT #pragma GCC poison TARGET_NAME -#pragma GCC poison TARGET_SUPPORTS_MTTCG #pragma GCC poison TARGET_BIG_ENDIAN #pragma GCC poison TCG_GUEST_DEFAULT_MO #pragma GCC poison TARGET_HAS_PRECISE_SMC diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index eeaf3a81c1..35fb145d27 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -237,6 +237,7 @@ static const struct SysemuCPUOps alpha_sysemu_ops = { static const TCGCPUOps alpha_tcg_ops = { /* Alpha processors have a weak memory model */ .guest_default_memory_order = 0, + .mttcg_supported = true, .initialize = alpha_translate_init, .translate_code = alpha_translate_code, diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 3e9760b551..377791c84d 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2671,6 +2671,7 @@ static const struct SysemuCPUOps arm_sysemu_ops = { #ifdef CONFIG_TCG static const TCGCPUOps arm_tcg_ops = { + .mttcg_supported = true, /* ARM processors have a weak memory model */ .guest_default_memory_order = 0, diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 89d4e4b4a2..f71560aa43 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -234,6 +234,7 @@ static void cortex_m55_initfn(Object *obj) static const TCGCPUOps arm_v7m_tcg_ops = { /* ARM processors have a weak memory model */ .guest_default_memory_order = 0, + .mttcg_supported = true, .initialize = arm_translate_init, .translate_code = arm_translate_code, diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 8f79cf4c08..84f3b839c9 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -225,6 +225,7 @@ static const struct SysemuCPUOps avr_sysemu_ops = { static const TCGCPUOps avr_tcg_ops = { .guest_default_memory_order = 0, + .mttcg_supported = false, .initialize = avr_cpu_tcg_init, .translate_code = avr_cpu_translate_code, .synchronize_from_tb = avr_cpu_synchronize_from_tb, diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 3d14e5cc6a..3c5191282e 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -327,6 +327,7 @@ static void hexagon_cpu_init(Object *obj) static const TCGCPUOps hexagon_tcg_ops = { /* MTTCG not yet supported: require strict ordering */ .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = false, .initialize = hexagon_translate_init, .translate_code = hexagon_translate_code, .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index dfbd933056..10e18c945e 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -260,6 +260,7 @@ static const TCGCPUOps hppa_tcg_ops = { * basis. It's probably easier to fall back to a strong memory model. */ .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = true, .initialize = hppa_translate_init, .translate_code = hppa_translate_code, diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index e13d0f6f86..621502c984 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -125,6 +125,7 @@ static bool x86_debug_check_breakpoint(CPUState *cs) #include "accel/tcg/cpu-ops.h" const TCGCPUOps x86_tcg_ops = { + .mttcg_supported = true, /* * The x86 has a strong memory model with some store-after-load re-ordering */ diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index f5b8ef29ab..fe9462b3b7 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -865,6 +865,7 @@ static void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) static const TCGCPUOps loongarch_tcg_ops = { .guest_default_memory_order = 0, + .mttcg_supported = true, .initialize = loongarch_translate_init, .translate_code = loongarch_translate_code, diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index b2d8c8f1de..99adc5eb91 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -591,6 +591,7 @@ static const struct SysemuCPUOps m68k_sysemu_ops = { static const TCGCPUOps m68k_tcg_ops = { /* MTTCG not yet supported: require strict ordering */ .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = false, .initialize = m68k_tcg_init, .translate_code = m68k_translate_code, diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 4efba0dddb..edfb05758b 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -429,6 +429,7 @@ static const struct SysemuCPUOps mb_sysemu_ops = { static const TCGCPUOps mb_tcg_ops = { /* MicroBlaze is always in-order. */ .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = true, .initialize = mb_tcg_init, .translate_code = mb_translate_code, diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 2ae7ba4407..473cecdebc 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -551,6 +551,7 @@ static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) } static const TCGCPUOps mips_tcg_ops = { + .mttcg_supported = TARGET_LONG_BITS == 32, .guest_default_memory_order = 0, .initialize = mips_tcg_init, diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 87fe779042..6601e0c066 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -244,6 +244,7 @@ static const struct SysemuCPUOps openrisc_sysemu_ops = { static const TCGCPUOps openrisc_tcg_ops = { .guest_default_memory_order = 0, + .mttcg_supported = true, .initialize = openrisc_translate_init, .translate_code = openrisc_translate_code, diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 9ba775971a..fde7d71fc6 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7479,6 +7479,7 @@ static const struct SysemuCPUOps ppc_sysemu_ops = { #include "accel/tcg/cpu-ops.h" static const TCGCPUOps ppc_tcg_ops = { + .mttcg_supported = TARGET_LONG_BITS == 64, .guest_default_memory_order = 0, .initialize = ppc_translate_init, .translate_code = ppc_translate_code, diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 44fdf6c4cf..426145c3b9 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -141,6 +141,7 @@ static void riscv_restore_state_to_opc(CPUState *cs, } const TCGCPUOps riscv_tcg_ops = { + .mttcg_supported = true, .guest_default_memory_order = 0, .initialize = riscv_translate_init, diff --git a/target/rx/cpu.c b/target/rx/cpu.c index f073fe8fc9..0a7a2b55b5 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -206,6 +206,7 @@ static const struct SysemuCPUOps rx_sysemu_ops = { static const TCGCPUOps rx_tcg_ops = { /* MTTCG not yet supported: require strict ordering */ .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = false, .initialize = rx_translate_init, .translate_code = rx_translate_code, diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 1e101b5afe..41cccc1e69 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -345,6 +345,7 @@ void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, } static const TCGCPUOps s390_tcg_ops = { + .mttcg_supported = true, /* * The z/Architecture has a strong memory model with some * store-after-load re-ordering. diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 7a05301c6f..861fdd47f7 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -264,6 +264,7 @@ static const struct SysemuCPUOps sh4_sysemu_ops = { static const TCGCPUOps superh_tcg_ops = { /* MTTCG not yet supported: require strict ordering */ .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = false, .initialize = sh4_translate_init, .translate_code = sh4_translate_code, diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 56d9417ae3..f7d231c6f8 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -1023,6 +1023,7 @@ static const TCGCPUOps sparc_tcg_ops = { * by an implied MEMBAR #StoreStore. */ .guest_default_memory_order = TCG_MO_LD_LD | TCG_MO_LD_ST | TCG_MO_ST_ST, + .mttcg_supported = true, .initialize = sparc_tcg_init, .translate_code = sparc_translate_code, diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index c68954b409..a4f93e7d91 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -174,6 +174,7 @@ static const struct SysemuCPUOps tricore_sysemu_ops = { static const TCGCPUOps tricore_tcg_ops = { /* MTTCG not yet supported: require strict ordering */ .guest_default_memory_order = TCG_MO_ALL, + .mttcg_supported = false, .initialize = tricore_tcg_init, .translate_code = tricore_translate_code, .synchronize_from_tb = tricore_cpu_synchronize_from_tb, diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 2cbf4e3010..971e67ad97 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -234,6 +234,7 @@ static const struct SysemuCPUOps xtensa_sysemu_ops = { static const TCGCPUOps xtensa_tcg_ops = { /* Xtensa processors have a weak memory model */ .guest_default_memory_order = 0, + .mttcg_supported = true, .initialize = xtensa_translate_init, .translate_code = xtensa_translate_code, From 9d7a951e352e7b99b8d96826ac9fdd384412137a Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 10 Apr 2025 16:50:04 +0800 Subject: [PATCH 0228/2760] hw/intc/loongarch_pch_msi: Remove gpio input handler MSI interrupt is triggered by writing message on specified memory address. In generic it is used by PCI devices, and no device is connected pch MSI irqchip with GPIO pin line method, here remove gpio input setting for MSI controller. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Tested-by: Song Gao Message-Id: <20250410085004.3577627-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_pch_msi.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c index 66b5c1e660..bc93504ff7 100644 --- a/hw/intc/loongarch_pch_msi.c +++ b/hw/intc/loongarch_pch_msi.c @@ -42,13 +42,6 @@ static const MemoryRegionOps loongarch_pch_msi_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void pch_msi_irq_handler(void *opaque, int irq, int level) -{ - LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(opaque); - - qemu_set_irq(s->pch_msi_irq[irq], level); -} - static void loongarch_pch_msi_realize(DeviceState *dev, Error **errp) { LoongArchPCHMSI *s = LOONGARCH_PCH_MSI(dev); @@ -59,9 +52,7 @@ static void loongarch_pch_msi_realize(DeviceState *dev, Error **errp) } s->pch_msi_irq = g_new(qemu_irq, s->irq_num); - qdev_init_gpio_out(dev, s->pch_msi_irq, s->irq_num); - qdev_init_gpio_in(dev, pch_msi_irq_handler, s->irq_num); } static void loongarch_pch_msi_unrealize(DeviceState *dev) From 4ac7eecb7439f6a978b44a0504a446c95f1bcc76 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 23 Apr 2025 16:04:09 +0800 Subject: [PATCH 0229/2760] target/loongarch: Move header file helper.h to directory tcg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Header file helper.h is specified for tcg mode, move this file to directory tcg. And create new file helper.h to include header file in tcg mode. Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250423080417.3739809-2-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/helper.h | 720 +-------------------------------- target/loongarch/tcg/helper.h | 722 ++++++++++++++++++++++++++++++++++ 2 files changed, 724 insertions(+), 718 deletions(-) create mode 100644 target/loongarch/tcg/helper.h diff --git a/target/loongarch/helper.h b/target/loongarch/helper.h index 1d5cb0198c..99981abd01 100644 --- a/target/loongarch/helper.h +++ b/target/loongarch/helper.h @@ -1,722 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ /* - * Copyright (c) 2021 Loongson Technology Corporation Limited + * Copyright (c) 2025 Loongson Technology Corporation Limited */ -DEF_HELPER_2(raise_exception, noreturn, env, i32) - -DEF_HELPER_FLAGS_1(bitrev_w, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_1(bitrev_d, TCG_CALL_NO_RWG_SE, tl, tl) -DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) - -DEF_HELPER_FLAGS_3(asrtle_d, TCG_CALL_NO_WG, void, env, tl, tl) -DEF_HELPER_FLAGS_3(asrtgt_d, TCG_CALL_NO_WG, void, env, tl, tl) - -DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) -DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) -DEF_HELPER_FLAGS_2(cpucfg, TCG_CALL_NO_RWG_SE, tl, env, tl) - -/* Floating-point helper */ -DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmaxa_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmaxa_d, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmina_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fmina_d, TCG_CALL_NO_WG, i64, env, i64, i64) - -DEF_HELPER_FLAGS_5(fmuladd_s, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i32) -DEF_HELPER_FLAGS_5(fmuladd_d, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i32) - -DEF_HELPER_FLAGS_3(fscaleb_s, TCG_CALL_NO_WG, i64, env, i64, i64) -DEF_HELPER_FLAGS_3(fscaleb_d, TCG_CALL_NO_WG, i64, env, i64, i64) - -DEF_HELPER_FLAGS_2(flogb_s, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(flogb_d, TCG_CALL_NO_WG, i64, env, i64) - -DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(frsqrt_s, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(frsqrt_d, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(frecip_s, TCG_CALL_NO_WG, i64, env, i64) -DEF_HELPER_FLAGS_2(frecip_d, TCG_CALL_NO_WG, i64, env, i64) - -DEF_HELPER_FLAGS_2(fclass_s, TCG_CALL_NO_RWG_SE, i64, env, i64) -DEF_HELPER_FLAGS_2(fclass_d, TCG_CALL_NO_RWG_SE, i64, env, i64) - -/* fcmp.cXXX.s */ -DEF_HELPER_4(fcmp_c_s, i64, env, i64, i64, i32) -/* fcmp.sXXX.s */ -DEF_HELPER_4(fcmp_s_s, i64, env, i64, i64, i32) -/* fcmp.cXXX.d */ -DEF_HELPER_4(fcmp_c_d, i64, env, i64, i64, i32) -/* fcmp.sXXX.d */ -DEF_HELPER_4(fcmp_s_d, i64, env, i64, i64, i32) - -DEF_HELPER_2(fcvt_d_s, i64, env, i64) -DEF_HELPER_2(fcvt_s_d, i64, env, i64) -DEF_HELPER_2(ffint_d_w, i64, env, i64) -DEF_HELPER_2(ffint_d_l, i64, env, i64) -DEF_HELPER_2(ffint_s_w, i64, env, i64) -DEF_HELPER_2(ffint_s_l, i64, env, i64) -DEF_HELPER_2(ftintrm_l_s, i64, env, i64) -DEF_HELPER_2(ftintrm_l_d, i64, env, i64) -DEF_HELPER_2(ftintrm_w_s, i64, env, i64) -DEF_HELPER_2(ftintrm_w_d, i64, env, i64) -DEF_HELPER_2(ftintrp_l_s, i64, env, i64) -DEF_HELPER_2(ftintrp_l_d, i64, env, i64) -DEF_HELPER_2(ftintrp_w_s, i64, env, i64) -DEF_HELPER_2(ftintrp_w_d, i64, env, i64) -DEF_HELPER_2(ftintrz_l_s, i64, env, i64) -DEF_HELPER_2(ftintrz_l_d, i64, env, i64) -DEF_HELPER_2(ftintrz_w_s, i64, env, i64) -DEF_HELPER_2(ftintrz_w_d, i64, env, i64) -DEF_HELPER_2(ftintrne_l_s, i64, env, i64) -DEF_HELPER_2(ftintrne_l_d, i64, env, i64) -DEF_HELPER_2(ftintrne_w_s, i64, env, i64) -DEF_HELPER_2(ftintrne_w_d, i64, env, i64) -DEF_HELPER_2(ftint_l_s, i64, env, i64) -DEF_HELPER_2(ftint_l_d, i64, env, i64) -DEF_HELPER_2(ftint_w_s, i64, env, i64) -DEF_HELPER_2(ftint_w_d, i64, env, i64) -DEF_HELPER_2(frint_s, i64, env, i64) -DEF_HELPER_2(frint_d, i64, env, i64) - -DEF_HELPER_FLAGS_1(set_rounding_mode, TCG_CALL_NO_RWG, void, env) - -DEF_HELPER_1(rdtime_d, i64, env) - -#ifndef CONFIG_USER_ONLY -/* CSRs helper */ -DEF_HELPER_1(csrrd_pgd, i64, env) -DEF_HELPER_1(csrrd_cpuid, i64, env) -DEF_HELPER_1(csrrd_tval, i64, env) -DEF_HELPER_2(csrwr_stlbps, i64, env, tl) -DEF_HELPER_2(csrwr_estat, i64, env, tl) -DEF_HELPER_2(csrwr_asid, i64, env, tl) -DEF_HELPER_2(csrwr_tcfg, i64, env, tl) -DEF_HELPER_2(csrwr_ticlr, i64, env, tl) -DEF_HELPER_2(csrwr_pwcl, i64, env, tl) -DEF_HELPER_2(iocsrrd_b, i64, env, tl) -DEF_HELPER_2(iocsrrd_h, i64, env, tl) -DEF_HELPER_2(iocsrrd_w, i64, env, tl) -DEF_HELPER_2(iocsrrd_d, i64, env, tl) -DEF_HELPER_3(iocsrwr_b, void, env, tl, tl) -DEF_HELPER_3(iocsrwr_h, void, env, tl, tl) -DEF_HELPER_3(iocsrwr_w, void, env, tl, tl) -DEF_HELPER_3(iocsrwr_d, void, env, tl, tl) - -/* TLB helper */ -DEF_HELPER_1(tlbwr, void, env) -DEF_HELPER_1(tlbfill, void, env) -DEF_HELPER_1(tlbsrch, void, env) -DEF_HELPER_1(tlbrd, void, env) -DEF_HELPER_1(tlbclr, void, env) -DEF_HELPER_1(tlbflush, void, env) -DEF_HELPER_1(invtlb_all, void, env) -DEF_HELPER_2(invtlb_all_g, void, env, i32) -DEF_HELPER_2(invtlb_all_asid, void, env, tl) -DEF_HELPER_3(invtlb_page_asid, void, env, tl, tl) -DEF_HELPER_3(invtlb_page_asid_or_g, void, env, tl, tl) - -DEF_HELPER_4(lddir, tl, env, tl, tl, i32) -DEF_HELPER_4(ldpte, void, env, tl, tl, i32) -DEF_HELPER_1(ertn, void, env) -DEF_HELPER_1(idle, void, env) -#endif - -/* LoongArch LSX */ -DEF_HELPER_FLAGS_4(vhaddw_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhaddw_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhaddw_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhaddw_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhaddw_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhaddw_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhaddw_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhaddw_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhsubw_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhsubw_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhsubw_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhsubw_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhsubw_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhsubw_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhsubw_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vhsubw_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vaddwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwev_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwod_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vsubwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwev_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwod_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vaddwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwev_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwod_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vsubwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwev_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsubwod_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vaddwev_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwev_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwev_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwev_q_du_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwod_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwod_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwod_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vaddwod_q_du_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vavg_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavg_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavg_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavg_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavg_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavg_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vavgr_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavgr_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavgr_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavgr_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavgr_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavgr_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavgr_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vavgr_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vabsd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vabsd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vabsd_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vabsd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vabsd_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vabsd_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vabsd_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vabsd_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vadda_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vadda_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vadda_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vadda_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vmini_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmini_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmini_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmini_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmini_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmini_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmini_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmini_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vmaxi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmaxi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmaxi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmaxi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmaxi_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmaxi_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmaxi_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vmaxi_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vmuh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmuh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmuh_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmuh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmuh_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmuh_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmuh_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmuh_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vmulwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vmulwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vmulwev_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwev_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwev_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwod_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwod_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmulwod_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vmadd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmadd_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmsub_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmsub_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vmaddwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vmaddwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vmaddwev_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwev_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwev_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwod_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwod_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmaddwod_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vdiv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vdiv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vdiv_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vdiv_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vdiv_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vdiv_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vdiv_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmod_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmod_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmod_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmod_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmod_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmod_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmod_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vmod_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vsat_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsat_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsat_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsat_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsat_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsat_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsat_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsat_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_3(vexth_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vexth_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vexth_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vexth_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vexth_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vexth_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vexth_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vexth_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(vext2xv_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vext2xv_w_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vext2xv_d_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vext2xv_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vext2xv_d_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vext2xv_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vext2xv_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vext2xv_wu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vext2xv_du_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vext2xv_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vext2xv_du_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vext2xv_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vsigncov_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsigncov_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsigncov_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsigncov_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(vmskltz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vmskltz_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vmskltz_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vmskltz_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vmskgez_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vmsknz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vnori_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vsllwil_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsllwil_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsllwil_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_3(vextl_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsllwil_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsllwil_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsllwil_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_3(vextl_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vsrlr_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrlr_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrlr_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrlr_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrlri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrlri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrlri_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrlri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vsrar_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrar_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrar_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrar_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrari_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrari_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrari_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrari_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vsrln_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrln_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrln_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsran_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsran_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsran_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vsrlni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrlni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrlni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrlni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrani_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrani_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrani_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrani_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vsrlrn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrlrn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrlrn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrarn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrarn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vsrarn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vsrlrni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrlrni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrlrni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrlrni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrarni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrarni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrarni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vsrarni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vssrln_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrln_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrln_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssran_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssran_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssran_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrln_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrln_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrln_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssran_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssran_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssran_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vssrlni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrani_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrani_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrani_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrani_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlni_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlni_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlni_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlni_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrani_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrani_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrani_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrani_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vssrlrn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrlrn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrlrn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrarn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrarn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrarn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrlrn_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrlrn_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrlrn_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrarn_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrarn_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vssrarn_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vssrlrni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlrni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlrni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlrni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrarni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrarni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrarni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrarni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlrni_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlrni_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlrni_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrlrni_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrarni_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrarni_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrarni_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vssrarni_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_3(vclo_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vclo_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vclo_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vclo_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vclz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vclz_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vclz_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vclz_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(vpcnt_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vpcnt_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vpcnt_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(vpcnt_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vbitclr_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vbitclr_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vbitclr_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vbitclr_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vbitclri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vbitclri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vbitclri_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vbitclri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vbitset_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vbitset_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vbitset_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vbitset_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vbitseti_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vbitseti_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vbitseti_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vbitseti_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vbitrev_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vbitrev_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vbitrev_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vbitrev_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vbitrevi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vbitrevi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vbitrevi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vbitrevi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vfrstp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vfrstp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vfrstpi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vfrstpi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_5(vfadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_6(vfmadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_6(vfmadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_6(vfmsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_6(vfmsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_6(vfnmadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_6(vfnmadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_6(vfnmsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_6(vfnmsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_5(vfmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfmin_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_5(vfmaxa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfmaxa_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfmina_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfmina_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_4(vflogb_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vflogb_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_4(vfclass_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfclass_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_4(vfsqrt_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfsqrt_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrecip_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrecip_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrsqrt_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrsqrt_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_4(vfcvtl_s_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfcvth_s_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfcvtl_d_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfcvth_d_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfcvt_h_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vfcvt_s_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_4(vfrintrne_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrintrne_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrintrz_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrintrz_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrintrp_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrintrp_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrintrm_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrintrm_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrint_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vfrint_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_4(vftintrne_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrne_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrz_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrz_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrp_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrp_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrm_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrm_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftint_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftint_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrz_wu_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrz_lu_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftint_wu_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftint_lu_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vftintrne_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vftintrz_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vftintrp_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vftintrm_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vftint_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrnel_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrneh_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrzl_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrzh_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrpl_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrph_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrml_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintrmh_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftintl_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vftinth_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_4(vffint_s_w, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vffint_d_l, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vffint_s_wu, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vffint_d_lu, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vffintl_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(vffinth_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(vffint_s_l, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_4(vseqi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vseqi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vseqi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vseqi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vslei_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslei_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslei_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslei_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslei_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslei_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslei_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslei_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vslti_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslti_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslti_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslti_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslti_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslti_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslti_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vslti_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_6(vfcmp_c_s, void, env, i32, i32, i32, i32, i32) -DEF_HELPER_6(vfcmp_s_s, void, env, i32, i32, i32, i32, i32) -DEF_HELPER_6(vfcmp_c_d, void, env, i32, i32, i32, i32, i32) -DEF_HELPER_6(vfcmp_s_d, void, env, i32, i32, i32, i32, i32) - -DEF_HELPER_FLAGS_4(vbitseli_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_4(vsetanyeqz_b, void, env, i32, i32, i32) -DEF_HELPER_4(vsetanyeqz_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsetanyeqz_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsetanyeqz_d, void, env, i32, i32, i32) -DEF_HELPER_4(vsetallnez_b, void, env, i32, i32, i32) -DEF_HELPER_4(vsetallnez_h, void, env, i32, i32, i32) -DEF_HELPER_4(vsetallnez_w, void, env, i32, i32, i32) -DEF_HELPER_4(vsetallnez_d, void, env, i32, i32, i32) - -DEF_HELPER_FLAGS_4(xvinsve0_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(xvinsve0_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(xvpickve_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(xvpickve_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vpackev_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpackev_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpackev_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpackev_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpackod_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpackod_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpackod_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpackod_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vpickev_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpickev_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpickev_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpickev_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpickod_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpickod_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpickod_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpickod_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(vilvl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vilvl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vilvl_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vilvl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vilvh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vilvh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vilvh_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vilvh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(vshuf_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vshuf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vshuf_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vshuf_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vshuf4i_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vshuf4i_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vshuf4i_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vshuf4i_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vperm_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(vpermi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vpermi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vpermi_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) - -DEF_HELPER_FLAGS_4(vextrins_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vextrins_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vextrins_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) -DEF_HELPER_FLAGS_4(vextrins_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +#include "tcg/helper.h" diff --git a/target/loongarch/tcg/helper.h b/target/loongarch/tcg/helper.h new file mode 100644 index 0000000000..1d5cb0198c --- /dev/null +++ b/target/loongarch/tcg/helper.h @@ -0,0 +1,722 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (c) 2021 Loongson Technology Corporation Limited + */ + +DEF_HELPER_2(raise_exception, noreturn, env, i32) + +DEF_HELPER_FLAGS_1(bitrev_w, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(bitrev_d, TCG_CALL_NO_RWG_SE, tl, tl) +DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) + +DEF_HELPER_FLAGS_3(asrtle_d, TCG_CALL_NO_WG, void, env, tl, tl) +DEF_HELPER_FLAGS_3(asrtgt_d, TCG_CALL_NO_WG, void, env, tl, tl) + +DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) +DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, tl, tl, tl, tl) +DEF_HELPER_FLAGS_2(cpucfg, TCG_CALL_NO_RWG_SE, tl, env, tl) + +/* Floating-point helper */ +DEF_HELPER_FLAGS_3(fadd_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fadd_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fsub_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fsub_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmul_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmul_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fdiv_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fdiv_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmax_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmax_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmin_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmin_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmaxa_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmaxa_d, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmina_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fmina_d, TCG_CALL_NO_WG, i64, env, i64, i64) + +DEF_HELPER_FLAGS_5(fmuladd_s, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i32) +DEF_HELPER_FLAGS_5(fmuladd_d, TCG_CALL_NO_WG, i64, env, i64, i64, i64, i32) + +DEF_HELPER_FLAGS_3(fscaleb_s, TCG_CALL_NO_WG, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(fscaleb_d, TCG_CALL_NO_WG, i64, env, i64, i64) + +DEF_HELPER_FLAGS_2(flogb_s, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(flogb_d, TCG_CALL_NO_WG, i64, env, i64) + +DEF_HELPER_FLAGS_2(fsqrt_s, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(fsqrt_d, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(frsqrt_s, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(frsqrt_d, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(frecip_s, TCG_CALL_NO_WG, i64, env, i64) +DEF_HELPER_FLAGS_2(frecip_d, TCG_CALL_NO_WG, i64, env, i64) + +DEF_HELPER_FLAGS_2(fclass_s, TCG_CALL_NO_RWG_SE, i64, env, i64) +DEF_HELPER_FLAGS_2(fclass_d, TCG_CALL_NO_RWG_SE, i64, env, i64) + +/* fcmp.cXXX.s */ +DEF_HELPER_4(fcmp_c_s, i64, env, i64, i64, i32) +/* fcmp.sXXX.s */ +DEF_HELPER_4(fcmp_s_s, i64, env, i64, i64, i32) +/* fcmp.cXXX.d */ +DEF_HELPER_4(fcmp_c_d, i64, env, i64, i64, i32) +/* fcmp.sXXX.d */ +DEF_HELPER_4(fcmp_s_d, i64, env, i64, i64, i32) + +DEF_HELPER_2(fcvt_d_s, i64, env, i64) +DEF_HELPER_2(fcvt_s_d, i64, env, i64) +DEF_HELPER_2(ffint_d_w, i64, env, i64) +DEF_HELPER_2(ffint_d_l, i64, env, i64) +DEF_HELPER_2(ffint_s_w, i64, env, i64) +DEF_HELPER_2(ffint_s_l, i64, env, i64) +DEF_HELPER_2(ftintrm_l_s, i64, env, i64) +DEF_HELPER_2(ftintrm_l_d, i64, env, i64) +DEF_HELPER_2(ftintrm_w_s, i64, env, i64) +DEF_HELPER_2(ftintrm_w_d, i64, env, i64) +DEF_HELPER_2(ftintrp_l_s, i64, env, i64) +DEF_HELPER_2(ftintrp_l_d, i64, env, i64) +DEF_HELPER_2(ftintrp_w_s, i64, env, i64) +DEF_HELPER_2(ftintrp_w_d, i64, env, i64) +DEF_HELPER_2(ftintrz_l_s, i64, env, i64) +DEF_HELPER_2(ftintrz_l_d, i64, env, i64) +DEF_HELPER_2(ftintrz_w_s, i64, env, i64) +DEF_HELPER_2(ftintrz_w_d, i64, env, i64) +DEF_HELPER_2(ftintrne_l_s, i64, env, i64) +DEF_HELPER_2(ftintrne_l_d, i64, env, i64) +DEF_HELPER_2(ftintrne_w_s, i64, env, i64) +DEF_HELPER_2(ftintrne_w_d, i64, env, i64) +DEF_HELPER_2(ftint_l_s, i64, env, i64) +DEF_HELPER_2(ftint_l_d, i64, env, i64) +DEF_HELPER_2(ftint_w_s, i64, env, i64) +DEF_HELPER_2(ftint_w_d, i64, env, i64) +DEF_HELPER_2(frint_s, i64, env, i64) +DEF_HELPER_2(frint_d, i64, env, i64) + +DEF_HELPER_FLAGS_1(set_rounding_mode, TCG_CALL_NO_RWG, void, env) + +DEF_HELPER_1(rdtime_d, i64, env) + +#ifndef CONFIG_USER_ONLY +/* CSRs helper */ +DEF_HELPER_1(csrrd_pgd, i64, env) +DEF_HELPER_1(csrrd_cpuid, i64, env) +DEF_HELPER_1(csrrd_tval, i64, env) +DEF_HELPER_2(csrwr_stlbps, i64, env, tl) +DEF_HELPER_2(csrwr_estat, i64, env, tl) +DEF_HELPER_2(csrwr_asid, i64, env, tl) +DEF_HELPER_2(csrwr_tcfg, i64, env, tl) +DEF_HELPER_2(csrwr_ticlr, i64, env, tl) +DEF_HELPER_2(csrwr_pwcl, i64, env, tl) +DEF_HELPER_2(iocsrrd_b, i64, env, tl) +DEF_HELPER_2(iocsrrd_h, i64, env, tl) +DEF_HELPER_2(iocsrrd_w, i64, env, tl) +DEF_HELPER_2(iocsrrd_d, i64, env, tl) +DEF_HELPER_3(iocsrwr_b, void, env, tl, tl) +DEF_HELPER_3(iocsrwr_h, void, env, tl, tl) +DEF_HELPER_3(iocsrwr_w, void, env, tl, tl) +DEF_HELPER_3(iocsrwr_d, void, env, tl, tl) + +/* TLB helper */ +DEF_HELPER_1(tlbwr, void, env) +DEF_HELPER_1(tlbfill, void, env) +DEF_HELPER_1(tlbsrch, void, env) +DEF_HELPER_1(tlbrd, void, env) +DEF_HELPER_1(tlbclr, void, env) +DEF_HELPER_1(tlbflush, void, env) +DEF_HELPER_1(invtlb_all, void, env) +DEF_HELPER_2(invtlb_all_g, void, env, i32) +DEF_HELPER_2(invtlb_all_asid, void, env, tl) +DEF_HELPER_3(invtlb_page_asid, void, env, tl, tl) +DEF_HELPER_3(invtlb_page_asid_or_g, void, env, tl, tl) + +DEF_HELPER_4(lddir, tl, env, tl, tl, i32) +DEF_HELPER_4(ldpte, void, env, tl, tl, i32) +DEF_HELPER_1(ertn, void, env) +DEF_HELPER_1(idle, void, env) +#endif + +/* LoongArch LSX */ +DEF_HELPER_FLAGS_4(vhaddw_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhaddw_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vhsubw_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vaddwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsubwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vaddwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsubwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwev_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsubwod_q_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vaddwev_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwev_q_du_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vaddwod_q_du_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vavg_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavg_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vavgr_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vavgr_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vabsd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vabsd_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vadda_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vadda_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vadda_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vadda_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmini_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmini_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vmaxi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vmaxi_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vmuh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmuh_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmulwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmulwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmulwev_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwev_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmulwod_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmadd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmadd_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmsub_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmsub_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmaddwev_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmaddwev_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_h_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_w_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_d_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vmaddwev_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwev_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_h_bu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_w_hu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmaddwod_d_wu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vdiv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vdiv_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_bu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_hu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_wu, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vmod_du, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsat_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsat_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_3(vexth_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vexth_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(vext2xv_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_w_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_d_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_d_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_wu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_du_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_du_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vext2xv_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsigncov_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsigncov_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsigncov_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsigncov_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(vmskltz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmskltz_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmskltz_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmskltz_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmskgez_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vmsknz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vnori_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vsllwil_h_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsllwil_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsllwil_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_3(vextl_q_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsllwil_hu_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsllwil_wu_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsllwil_du_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_3(vextl_qu_du, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsrlr_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlr_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlr_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlr_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlri_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vsrar_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrar_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrar_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrar_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrari_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrari_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrari_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrari_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vsrln_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrln_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrln_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsran_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsran_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsran_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsrlni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrani_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrani_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrani_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrani_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vsrlrn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlrn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrlrn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrarn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrarn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vsrarn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vsrlrni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlrni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlrni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrlrni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrarni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrarni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrarni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vsrarni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vssrln_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrln_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssran_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vssrlni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlni_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrani_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vssrlrn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrlrn_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vssrarn_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vssrlrni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_b_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_h_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_d_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrlrni_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_bu_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_hu_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_wu_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vssrarni_du_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_3(vclo_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclo_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclo_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclo_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclz_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclz_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vclz_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(vpcnt_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vpcnt_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vpcnt_w, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(vpcnt_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vbitclr_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitclr_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitclr_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitclr_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitclri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitclri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitclri_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitclri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vbitset_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitset_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitset_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitset_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitseti_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitseti_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitseti_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitseti_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vbitrev_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitrev_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitrev_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitrev_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vbitrevi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitrevi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitrevi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vbitrevi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vfrstp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vfrstp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vfrstpi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vfrstpi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_5(vfadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfdiv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_6(vfmadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfmadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfmsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfmsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfnmadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfnmadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfnmsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(vfnmsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_5(vfmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmin_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_5(vfmaxa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmaxa_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmina_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfmina_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vflogb_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vflogb_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vfclass_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfclass_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vfsqrt_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfsqrt_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrecip_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrecip_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrsqrt_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrsqrt_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vfcvtl_s_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfcvth_s_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfcvtl_d_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfcvth_d_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfcvt_h_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vfcvt_s_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vfrintrne_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrne_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrz_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrz_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrp_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrp_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrm_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrintrm_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrint_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vfrint_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vftintrne_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrne_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrz_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrz_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrp_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrp_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrm_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrm_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftint_w_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftint_l_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrz_wu_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrz_lu_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftint_wu_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftint_lu_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftintrne_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftintrz_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftintrp_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftintrm_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vftint_w_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrnel_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrneh_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrzl_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrzh_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrpl_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrph_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrml_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintrmh_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftintl_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vftinth_l_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vffint_s_w, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffint_d_l, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffint_s_wu, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffint_d_lu, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffintl_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(vffinth_d_w, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(vffint_s_l, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(vseqi_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vseqi_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vseqi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vseqi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vslei_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslei_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vslti_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_bu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_hu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_wu, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vslti_du, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_6(vfcmp_c_s, void, env, i32, i32, i32, i32, i32) +DEF_HELPER_6(vfcmp_s_s, void, env, i32, i32, i32, i32, i32) +DEF_HELPER_6(vfcmp_c_d, void, env, i32, i32, i32, i32, i32) +DEF_HELPER_6(vfcmp_s_d, void, env, i32, i32, i32, i32, i32) + +DEF_HELPER_FLAGS_4(vbitseli_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_4(vsetanyeqz_b, void, env, i32, i32, i32) +DEF_HELPER_4(vsetanyeqz_h, void, env, i32, i32, i32) +DEF_HELPER_4(vsetanyeqz_w, void, env, i32, i32, i32) +DEF_HELPER_4(vsetanyeqz_d, void, env, i32, i32, i32) +DEF_HELPER_4(vsetallnez_b, void, env, i32, i32, i32) +DEF_HELPER_4(vsetallnez_h, void, env, i32, i32, i32) +DEF_HELPER_4(vsetallnez_w, void, env, i32, i32, i32) +DEF_HELPER_4(vsetallnez_d, void, env, i32, i32, i32) + +DEF_HELPER_FLAGS_4(xvinsve0_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(xvinsve0_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(xvpickve_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(xvpickve_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vpackev_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackev_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackev_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackev_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackod_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackod_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackod_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpackod_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vpickev_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickev_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickev_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickev_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickod_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickod_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickod_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpickod_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(vilvl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvl_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvh_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vilvh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(vshuf_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vshuf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vshuf_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vshuf_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vshuf4i_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vshuf4i_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vshuf4i_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vshuf4i_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vperm_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(vpermi_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vpermi_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vpermi_q, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) + +DEF_HELPER_FLAGS_4(vextrins_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vextrins_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vextrins_w, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) +DEF_HELPER_FLAGS_4(vextrins_d, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) From a8d1b5bca5d234bfeaf215c079696735e5aafe71 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 23 Apr 2025 16:04:10 +0800 Subject: [PATCH 0230/2760] target/loongarch: Add function loongarch_get_addr_from_tlb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function loongarch_get_addr_from_tlb() is added to get physical address from TLB tables. TLB table only works in TCG mode, in future this function will be moved to TCG directory. Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250423080417.3739809-3-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu_helper.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 930466ca48..a326859000 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "system/tcg.h" #include "cpu.h" #include "internals.h" #include "cpu-csr.h" @@ -141,6 +142,21 @@ bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, return false; } +static int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + int index, match; + + match = loongarch_tlb_search(env, address, &index); + if (match) { + return loongarch_map_tlb_entry(env, physical, prot, + address, access_type, index, mmu_idx); + } + + return TLBRET_NOMATCH; +} + static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address) { @@ -221,13 +237,17 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, MMUAccessType access_type, int mmu_idx, int is_debug) { - int index, match; + int ret; - match = loongarch_tlb_search(env, address, &index); - if (match) { - return loongarch_map_tlb_entry(env, physical, prot, - address, access_type, index, mmu_idx); - } else if (is_debug) { + if (tcg_enabled()) { + ret = loongarch_get_addr_from_tlb(env, physical, prot, address, + access_type, mmu_idx); + if (ret != TLBRET_NOMATCH) { + return ret; + } + } + + if (is_debug) { /* * For debugger memory access, we want to do the map when there is a * legal mapping, even if the mapping is not yet in TLB. return 0 if From 885398ee09b5ee3d26d2ee670f468282c9ce8512 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 23 Apr 2025 16:04:11 +0800 Subject: [PATCH 0231/2760] target/loongarch: Move function get_dir_base_width to common directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function get_dir_base_width() is used by loongarch_page_table_walker(), so it is used by KVM mode also, here move this function from directory tcg to common directory. Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250423080417.3739809-4-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu_helper.c | 28 ++++++++++++++++++++++++++++ target/loongarch/tcg/tlb_helper.c | 28 ---------------------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index a326859000..8ae9a448b4 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -157,6 +157,34 @@ static int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, return TLBRET_NOMATCH; } +void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, + uint64_t *dir_width, target_ulong level) +{ + switch (level) { + case 1: + *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE); + *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH); + break; + case 2: + *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE); + *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH); + break; + case 3: + *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE); + *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH); + break; + case 4: + *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE); + *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH); + break; + default: + /* level may be zero for ldpte */ + *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); + *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); + break; + } +} + static int loongarch_page_table_walker(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address) { diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 70d1b5cf99..e6cfcc55c8 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -27,34 +27,6 @@ bool check_ps(CPULoongArchState *env, uint8_t tlb_ps) return BIT_ULL(tlb_ps) & (env->CSR_PRCFG2); } -void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, - uint64_t *dir_width, target_ulong level) -{ - switch (level) { - case 1: - *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_BASE); - *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR1_WIDTH); - break; - case 2: - *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_BASE); - *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, DIR2_WIDTH); - break; - case 3: - *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_BASE); - *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR3_WIDTH); - break; - case 4: - *dir_base = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_BASE); - *dir_width = FIELD_EX64(env->CSR_PWCH, CSR_PWCH, DIR4_WIDTH); - break; - default: - /* level may be zero for ldpte */ - *dir_base = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); - *dir_width = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); - break; - } -} - static void raise_mmu_exception(CPULoongArchState *env, target_ulong address, MMUAccessType access_type, int tlb_error) { From 566bf2de87160a8a2fe5a1ba1a2f31f6869f4f80 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 23 Apr 2025 16:04:12 +0800 Subject: [PATCH 0232/2760] target/loongarch: Add stub function loongarch_get_addr_from_tlb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Stub function loongarch_get_addr_from_tlb() is added if option CONFIG_TCG is not enabled, so this function can be called in KVM only mode. Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250423080417.3739809-5-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu_helper.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 8ae9a448b4..71180bc345 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -156,7 +156,16 @@ static int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, return TLBRET_NOMATCH; } +#else +static int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + return TLBRET_NOMATCH; +} +#endif +#ifdef CONFIG_TCG void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, uint64_t *dir_width, target_ulong level) { From d192494a685ff6b132caec8ebdfdbcdcd04408b9 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 23 Apr 2025 16:04:13 +0800 Subject: [PATCH 0233/2760] target/loongarch: Set function loongarch_map_address() with common code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function loongarch_map_address is to get physical address from virtual address, it is used by qmp commands to dump memory from virtual address. It is used by kvm mode also, here move function loongarch_map_address() out of macro CONFIG_TCG. And it is common code, the similar with function loongarch_page_table_walker(). Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250423080417.3739809-6-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu_helper.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 71180bc345..9a87cae358 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -165,7 +165,6 @@ static int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, } #endif -#ifdef CONFIG_TCG void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, uint64_t *dir_width, target_ulong level) { @@ -295,15 +294,6 @@ static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, return TLBRET_NOMATCH; } -#else -static int loongarch_map_address(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx, - int is_debug) -{ - return TLBRET_NOMATCH; -} -#endif static hwaddr dmw_va2pa(CPULoongArchState *env, target_ulong va, target_ulong dmw) From 9fd0cc4df871d762b91076410b6b6fb637cec0d4 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 23 Apr 2025 16:04:14 +0800 Subject: [PATCH 0234/2760] target/loongarch: Define function loongarch_get_addr_from_tlb() non-static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define function loongarch_get_addr_from_tlb() non-static, and add its definition in header file tcg/tcg_loongarch.h Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250423080417.3739809-7-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu_helper.c | 10 ++-------- target/loongarch/tcg/tcg_loongarch.h | 5 +++++ 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 9a87cae358..97d9caa06e 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -11,6 +11,7 @@ #include "cpu.h" #include "internals.h" #include "cpu-csr.h" +#include "tcg/tcg_loongarch.h" #ifdef CONFIG_TCG static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, @@ -142,7 +143,7 @@ bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, return false; } -static int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, +int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, MMUAccessType access_type, int mmu_idx) { @@ -156,13 +157,6 @@ static int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, return TLBRET_NOMATCH; } -#else -static int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - return TLBRET_NOMATCH; -} #endif void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, diff --git a/target/loongarch/tcg/tcg_loongarch.h b/target/loongarch/tcg/tcg_loongarch.h index da2539e995..b29427d981 100644 --- a/target/loongarch/tcg/tcg_loongarch.h +++ b/target/loongarch/tcg/tcg_loongarch.h @@ -6,7 +6,12 @@ */ #ifndef TARGET_LOONGARCH_TCG_LOONGARCH_H #define TARGET_LOONGARCH_TCG_LOONGARCH_H +#include "cpu.h" void loongarch_csr_translate_init(void); +int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx); + #endif /* TARGET_LOONGARCH_TCG_LOONGARCH_H */ From 9c9ffc013db126727d6121754e461550ba60a69f Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 23 Apr 2025 16:04:15 +0800 Subject: [PATCH 0235/2760] target/loongarch: Move function loongarch_tlb_search to directory tcg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function loongarch_tlb_search() and loongarch_map_tlb_entry() works only in TCG mode, move these functions to directory tcg. There is no any function change, only code moving. Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250423080417.3739809-8-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu_helper.c | 146 ------------------------------ target/loongarch/tcg/tlb_helper.c | 145 +++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 146 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index 97d9caa06e..998857b8dc 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -13,152 +13,6 @@ #include "cpu-csr.h" #include "tcg/tcg_loongarch.h" -#ifdef CONFIG_TCG -static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - int access_type, int index, int mmu_idx) -{ - LoongArchTLB *tlb = &env->tlb[index]; - uint64_t plv = mmu_idx; - uint64_t tlb_entry, tlb_ppn; - uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; - - if (index >= LOONGARCH_STLB) { - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - } else { - tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - } - n = (address >> tlb_ps) & 0x1;/* Odd or even */ - - tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; - tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); - tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); - tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); - if (is_la64(env)) { - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); - tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); - tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); - tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); - } else { - tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); - tlb_nx = 0; - tlb_nr = 0; - tlb_rplv = 0; - } - - /* Remove sw bit between bit12 -- bit PS*/ - tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) -1)); - - /* Check access rights */ - if (!tlb_v) { - return TLBRET_INVALID; - } - - if (access_type == MMU_INST_FETCH && tlb_nx) { - return TLBRET_XI; - } - - if (access_type == MMU_DATA_LOAD && tlb_nr) { - return TLBRET_RI; - } - - if (((tlb_rplv == 0) && (plv > tlb_plv)) || - ((tlb_rplv == 1) && (plv != tlb_plv))) { - return TLBRET_PE; - } - - if ((access_type == MMU_DATA_STORE) && !tlb_d) { - return TLBRET_DIRTY; - } - - *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | - (address & MAKE_64BIT_MASK(0, tlb_ps)); - *prot = PAGE_READ; - if (tlb_d) { - *prot |= PAGE_WRITE; - } - if (!tlb_nx) { - *prot |= PAGE_EXEC; - } - return TLBRET_MATCH; -} - -/* - * One tlb entry holds an adjacent odd/even pair, the vpn is the - * content of the virtual page number divided by 2. So the - * compare vpn is bit[47:15] for 16KiB page. while the vppn - * field in tlb entry contains bit[47:13], so need adjust. - * virt_vpn = vaddr[47:13] - */ -bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, - int *index) -{ - LoongArchTLB *tlb; - uint16_t csr_asid, tlb_asid, stlb_idx; - uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps; - int i, compare_shift; - uint64_t vpn, tlb_vppn; - - csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); - stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1); - stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */ - compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; - - /* Search STLB */ - for (i = 0; i < 8; ++i) { - tlb = &env->tlb[i * 256 + stlb_idx]; - tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); - if (tlb_e) { - tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); - tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - - if ((tlb_g == 1 || tlb_asid == csr_asid) && - (vpn == (tlb_vppn >> compare_shift))) { - *index = i * 256 + stlb_idx; - return true; - } - } - } - - /* Search MTLB */ - for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) { - tlb = &env->tlb[i]; - tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); - if (tlb_e) { - tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); - tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); - tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); - tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); - compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; - vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1); - if ((tlb_g == 1 || tlb_asid == csr_asid) && - (vpn == (tlb_vppn >> compare_shift))) { - *index = i; - return true; - } - } - } - return false; -} - -int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, - int *prot, target_ulong address, - MMUAccessType access_type, int mmu_idx) -{ - int index, match; - - match = loongarch_tlb_search(env, address, &index); - if (match) { - return loongarch_map_tlb_entry(env, physical, prot, - address, access_type, index, mmu_idx); - } - - return TLBRET_NOMATCH; -} -#endif - void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, uint64_t *dir_width, target_ulong level) { diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index e6cfcc55c8..8509aa99cf 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -18,6 +18,7 @@ #include "exec/cpu_ldst.h" #include "exec/log.h" #include "cpu-csr.h" +#include "tcg/tcg_loongarch.h" bool check_ps(CPULoongArchState *env, uint8_t tlb_ps) { @@ -201,6 +202,66 @@ static uint32_t get_random_tlb(uint32_t low, uint32_t high) return val % (high - low + 1) + low; } +/* + * One tlb entry holds an adjacent odd/even pair, the vpn is the + * content of the virtual page number divided by 2. So the + * compare vpn is bit[47:15] for 16KiB page. while the vppn + * field in tlb entry contains bit[47:13], so need adjust. + * virt_vpn = vaddr[47:13] + */ +bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, + int *index) +{ + LoongArchTLB *tlb; + uint16_t csr_asid, tlb_asid, stlb_idx; + uint8_t tlb_e, tlb_ps, tlb_g, stlb_ps; + int i, compare_shift; + uint64_t vpn, tlb_vppn; + + csr_asid = FIELD_EX64(env->CSR_ASID, CSR_ASID, ASID); + stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + vpn = (vaddr & TARGET_VIRT_MASK) >> (stlb_ps + 1); + stlb_idx = vpn & 0xff; /* VA[25:15] <==> TLBIDX.index for 16KiB Page */ + compare_shift = stlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; + + /* Search STLB */ + for (i = 0; i < 8; ++i) { + tlb = &env->tlb[i * 256 + stlb_idx]; + tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + if (tlb_e) { + tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); + tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); + tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + + if ((tlb_g == 1 || tlb_asid == csr_asid) && + (vpn == (tlb_vppn >> compare_shift))) { + *index = i * 256 + stlb_idx; + return true; + } + } + } + + /* Search MTLB */ + for (i = LOONGARCH_STLB; i < LOONGARCH_TLB_MAX; ++i) { + tlb = &env->tlb[i]; + tlb_e = FIELD_EX64(tlb->tlb_misc, TLB_MISC, E); + if (tlb_e) { + tlb_vppn = FIELD_EX64(tlb->tlb_misc, TLB_MISC, VPPN); + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); + tlb_asid = FIELD_EX64(tlb->tlb_misc, TLB_MISC, ASID); + tlb_g = FIELD_EX64(tlb->tlb_entry0, TLBENTRY, G); + compare_shift = tlb_ps + 1 - R_TLB_MISC_VPPN_SHIFT; + vpn = (vaddr & TARGET_VIRT_MASK) >> (tlb_ps + 1); + if ((tlb_g == 1 || tlb_asid == csr_asid) && + (vpn == (tlb_vppn >> compare_shift))) { + *index = i; + return true; + } + } + } + return false; +} + void helper_tlbsrch(CPULoongArchState *env) { int index, match; @@ -609,3 +670,87 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, } env->CSR_TLBREHI = FIELD_DP64(env->CSR_TLBREHI, CSR_TLBREHI, PS, ps); } + +static int loongarch_map_tlb_entry(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + int access_type, int index, int mmu_idx) +{ + LoongArchTLB *tlb = &env->tlb[index]; + uint64_t plv = mmu_idx; + uint64_t tlb_entry, tlb_ppn; + uint8_t tlb_ps, n, tlb_v, tlb_d, tlb_plv, tlb_nx, tlb_nr, tlb_rplv; + + if (index >= LOONGARCH_STLB) { + tlb_ps = FIELD_EX64(tlb->tlb_misc, TLB_MISC, PS); + } else { + tlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); + } + n = (address >> tlb_ps) & 0x1;/* Odd or even */ + + tlb_entry = n ? tlb->tlb_entry1 : tlb->tlb_entry0; + tlb_v = FIELD_EX64(tlb_entry, TLBENTRY, V); + tlb_d = FIELD_EX64(tlb_entry, TLBENTRY, D); + tlb_plv = FIELD_EX64(tlb_entry, TLBENTRY, PLV); + if (is_la64(env)) { + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_64, PPN); + tlb_nx = FIELD_EX64(tlb_entry, TLBENTRY_64, NX); + tlb_nr = FIELD_EX64(tlb_entry, TLBENTRY_64, NR); + tlb_rplv = FIELD_EX64(tlb_entry, TLBENTRY_64, RPLV); + } else { + tlb_ppn = FIELD_EX64(tlb_entry, TLBENTRY_32, PPN); + tlb_nx = 0; + tlb_nr = 0; + tlb_rplv = 0; + } + + /* Remove sw bit between bit12 -- bit PS*/ + tlb_ppn = tlb_ppn & ~(((0x1UL << (tlb_ps - 12)) - 1)); + + /* Check access rights */ + if (!tlb_v) { + return TLBRET_INVALID; + } + + if (access_type == MMU_INST_FETCH && tlb_nx) { + return TLBRET_XI; + } + + if (access_type == MMU_DATA_LOAD && tlb_nr) { + return TLBRET_RI; + } + + if (((tlb_rplv == 0) && (plv > tlb_plv)) || + ((tlb_rplv == 1) && (plv != tlb_plv))) { + return TLBRET_PE; + } + + if ((access_type == MMU_DATA_STORE) && !tlb_d) { + return TLBRET_DIRTY; + } + + *physical = (tlb_ppn << R_TLBENTRY_64_PPN_SHIFT) | + (address & MAKE_64BIT_MASK(0, tlb_ps)); + *prot = PAGE_READ; + if (tlb_d) { + *prot |= PAGE_WRITE; + } + if (!tlb_nx) { + *prot |= PAGE_EXEC; + } + return TLBRET_MATCH; +} + +int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, + int *prot, target_ulong address, + MMUAccessType access_type, int mmu_idx) +{ + int index, match; + + match = loongarch_tlb_search(env, address, &index); + if (match) { + return loongarch_map_tlb_entry(env, physical, prot, + address, access_type, index, mmu_idx); + } + + return TLBRET_NOMATCH; +} From ad5233ba5c7dcc92ee79d015f2168fb7e0279118 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 23 Apr 2025 16:04:16 +0800 Subject: [PATCH 0236/2760] target/loongarch: Add static definition with function loongarch_tlb_search() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function loongarch_tlb_search() is only referenced in file tcg/tlb_helper.c, define this function with static attribution. Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250423080417.3739809-9-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/internals.h | 2 -- target/loongarch/tcg/tlb_helper.c | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index 9fdc3059d8..3a079feb1d 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -54,8 +54,6 @@ uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu); uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu); void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu, uint64_t value); -bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, - int *index); int get_physical_address(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, MMUAccessType access_type, int mmu_idx, int is_debug); diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index 8509aa99cf..2fdd10022b 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -209,8 +209,8 @@ static uint32_t get_random_tlb(uint32_t low, uint32_t high) * field in tlb entry contains bit[47:13], so need adjust. * virt_vpn = vaddr[47:13] */ -bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, - int *index) +static bool loongarch_tlb_search(CPULoongArchState *env, target_ulong vaddr, + int *index) { LoongArchTLB *tlb; uint16_t csr_asid, tlb_asid, stlb_idx; From 0d4c2e408d418ec5c412ec9f58e7b8f3aecc6948 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 23 Apr 2025 16:04:17 +0800 Subject: [PATCH 0237/2760] target/loongarch: Move definition of TCG specified function to tcg directory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Function loongarch_cpu_tlb_fill() only works in TCG mode, move its definition from header file internals.h to file tcg/tcg_loongarch.h Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250423080417.3739809-10-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu.c | 1 + target/loongarch/internals.h | 5 ----- target/loongarch/tcg/tcg_loongarch.h | 4 ++++ 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index ea1665e270..bf3d592574 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -32,6 +32,7 @@ #include "exec/cpu_ldst.h" #include "tcg/tcg.h" #endif +#include "tcg/tcg_loongarch.h" const char * const regnames[32] = { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", diff --git a/target/loongarch/internals.h b/target/loongarch/internals.h index 3a079feb1d..a7384b0d31 100644 --- a/target/loongarch/internals.h +++ b/target/loongarch/internals.h @@ -61,11 +61,6 @@ void get_dir_base_width(CPULoongArchState *env, uint64_t *dir_base, uint64_t *dir_width, target_ulong level); hwaddr loongarch_cpu_get_phys_page_debug(CPUState *cpu, vaddr addr); -#ifdef CONFIG_TCG -bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, - MMUAccessType access_type, int mmu_idx, - bool probe, uintptr_t retaddr); -#endif #endif /* !CONFIG_USER_ONLY */ uint64_t read_fcc(CPULoongArchState *env); diff --git a/target/loongarch/tcg/tcg_loongarch.h b/target/loongarch/tcg/tcg_loongarch.h index b29427d981..fd4e116022 100644 --- a/target/loongarch/tcg/tcg_loongarch.h +++ b/target/loongarch/tcg/tcg_loongarch.h @@ -10,6 +10,10 @@ void loongarch_csr_translate_init(void); +bool loongarch_cpu_tlb_fill(CPUState *cs, vaddr address, int size, + MMUAccessType access_type, int mmu_idx, + bool probe, uintptr_t retaddr); + int loongarch_get_addr_from_tlb(CPULoongArchState *env, hwaddr *physical, int *prot, target_ulong address, MMUAccessType access_type, int mmu_idx); From a9d3d1dff6f9e1544e8a84e74645b8e4fe08c0ad Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Mon, 14 Apr 2025 15:49:52 +0800 Subject: [PATCH 0238/2760] linux-user/loongarch64: Decode BRK break codes for FPE signals Handle specific LoongArch BRK break codes in user-mode emulation to deliver accurate floating-point exception signals. Specifically, BRK_OVERFLOW (6) triggers TARGET_FPE_INTOVF, and BRK_DIVZERO (7) triggers TARGET_FPE_INTDIV. Other BRK codes fall back to a generic SIGTRAP. This improves correctness for programs that rely on BRK to signal overflow or divide-by-zero conditions. Signed-off-by: WANG Rui Acked-by: Song Gao Message-Id: <20250414074952.6253-1-wangrui@loongson.cn> Signed-off-by: Song Gao --- linux-user/loongarch64/cpu_loop.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/linux-user/loongarch64/cpu_loop.c b/linux-user/loongarch64/cpu_loop.c index 0614d3de22..ec8a06c88c 100644 --- a/linux-user/loongarch64/cpu_loop.c +++ b/linux-user/loongarch64/cpu_loop.c @@ -11,6 +11,12 @@ #include "user/cpu_loop.h" #include "signal-common.h" +/* Break codes */ +enum { + BRK_OVERFLOW = 6, + BRK_DIVZERO = 7 +}; + void cpu_loop(CPULoongArchState *env) { CPUState *cs = env_cpu(env); @@ -66,9 +72,26 @@ void cpu_loop(CPULoongArchState *env) force_sig_fault(TARGET_SIGFPE, si_code, env->pc); break; case EXCP_DEBUG: - case EXCCODE_BRK: force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); break; + case EXCCODE_BRK: + { + unsigned int opcode; + + get_user_u32(opcode, env->pc); + + switch (opcode & 0x7fff) { + case BRK_OVERFLOW: + force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTOVF, env->pc); + break; + case BRK_DIVZERO: + force_sig_fault(TARGET_SIGFPE, TARGET_FPE_INTDIV, env->pc); + break; + default: + force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->pc); + } + } + break; case EXCCODE_BCE: force_sig_fault(TARGET_SIGSYS, TARGET_SI_KERNEL, env->pc); break; From 256df51e727235b3d5e937ca2784c45663c00f59 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Fri, 18 Apr 2025 16:21:01 +0800 Subject: [PATCH 0239/2760] target/loongarch: Add CRC feature flag and use it to gate CRC instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch replaces the obsolete IOCSR_BRD bit with CRC in cpucfg1[25], in both LA464 and LA132 CPU initialization functions. The corresponding field macro in `cpu.h` is updated to reflect this change. Additionally, the availability macro `avail_CRC()` is introduced in `translate.h` to check the CRC feature flag. All CRC-related instruction translations are updated to be gated by the new CRC feature flag instead of hardcoded CPU features. This ensures correctness and configurability when enabling CRC instructions based on hardware capabilities. Signed-off-by: WANG Rui Reviewed-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250418082103.447780-2-wangrui@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu.c | 4 ++-- target/loongarch/cpu.h | 2 +- .../loongarch/tcg/insn_trans/trans_extra.c.inc | 16 ++++++++-------- target/loongarch/translate.h | 1 + 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index bf3d592574..588f5fd021 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -432,7 +432,7 @@ static void loongarch_la464_initfn(Object *obj) data = FIELD_DP32(data, CPUCFG1, EP, 1); data = FIELD_DP32(data, CPUCFG1, RPLV, 1); data = FIELD_DP32(data, CPUCFG1, HP, 1); - data = FIELD_DP32(data, CPUCFG1, IOCSR_BRD, 1); + data = FIELD_DP32(data, CPUCFG1, CRC, 1); env->cpucfg[1] = data; data = 0; @@ -531,7 +531,7 @@ static void loongarch_la132_initfn(Object *obj) data = FIELD_DP32(data, CPUCFG1, EP, 0); data = FIELD_DP32(data, CPUCFG1, RPLV, 0); data = FIELD_DP32(data, CPUCFG1, HP, 1); - data = FIELD_DP32(data, CPUCFG1, IOCSR_BRD, 1); + data = FIELD_DP32(data, CPUCFG1, CRC, 1); env->cpucfg[1] = data; } diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 254e4fbdcd..ab76a0b451 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -129,7 +129,7 @@ FIELD(CPUCFG1, RI, 21, 1) FIELD(CPUCFG1, EP, 22, 1) FIELD(CPUCFG1, RPLV, 23, 1) FIELD(CPUCFG1, HP, 24, 1) -FIELD(CPUCFG1, IOCSR_BRD, 25, 1) +FIELD(CPUCFG1, CRC, 25, 1) FIELD(CPUCFG1, MSG_INT, 26, 1) /* cpucfg[1].arch */ diff --git a/target/loongarch/tcg/insn_trans/trans_extra.c.inc b/target/loongarch/tcg/insn_trans/trans_extra.c.inc index cfa361fecf..eda3d6e561 100644 --- a/target/loongarch/tcg/insn_trans/trans_extra.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_extra.c.inc @@ -97,11 +97,11 @@ static bool gen_crc(DisasContext *ctx, arg_rrr *a, return true; } -TRANS(crc_w_b_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(1)) -TRANS(crc_w_h_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(2)) -TRANS(crc_w_w_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(4)) -TRANS(crc_w_d_w, 64, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) -TRANS(crcc_w_b_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(1)) -TRANS(crcc_w_h_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(2)) -TRANS(crcc_w_w_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(4)) -TRANS(crcc_w_d_w, 64, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) +TRANS(crc_w_b_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(1)) +TRANS(crc_w_h_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(2)) +TRANS(crc_w_w_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(4)) +TRANS(crc_w_d_w, CRC, gen_crc, gen_helper_crc32, tcg_constant_tl(8)) +TRANS(crcc_w_b_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(1)) +TRANS(crcc_w_h_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(2)) +TRANS(crcc_w_w_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(4)) +TRANS(crcc_w_d_w, CRC, gen_crc, gen_helper_crc32c, tcg_constant_tl(8)) diff --git a/target/loongarch/translate.h b/target/loongarch/translate.h index 195f53573a..018dc5eb17 100644 --- a/target/loongarch/translate.h +++ b/target/loongarch/translate.h @@ -25,6 +25,7 @@ #define avail_LSX(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LSX)) #define avail_LASX(C) (FIELD_EX32((C)->cpucfg2, CPUCFG2, LASX)) #define avail_IOCSR(C) (FIELD_EX32((C)->cpucfg1, CPUCFG1, IOCSR)) +#define avail_CRC(C) (FIELD_EX32((C)->cpucfg1, CPUCFG1, CRC)) /* * If an operation is being performed on less than TARGET_LONG_BITS, From 875caabdb1701a7c57ad0655a7963d74afc1b4d9 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Fri, 18 Apr 2025 16:21:02 +0800 Subject: [PATCH 0240/2760] target/loongarch: Guard BCEQZ/BCNEZ instructions with FP feature The BCEQZ and BCNEZ instructions depend on access to condition codes from floating-point comparisons. Previously, these instructions were unconditionally enabled for 64-bit targets. This patch updates their translation to be gated under the `FP` feature flag instead, ensuring they are only available when the floating-point unit is present. This improves correctness for CPUs lacking floating-point support. Signed-off-by: WANG Rui Reviewed-by: Bibo Mao Message-Id: <20250418082103.447780-3-wangrui@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/tcg/insn_trans/trans_branch.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/loongarch/tcg/insn_trans/trans_branch.c.inc b/target/loongarch/tcg/insn_trans/trans_branch.c.inc index 221e5159db..f94c1f37ab 100644 --- a/target/loongarch/tcg/insn_trans/trans_branch.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_branch.c.inc @@ -80,5 +80,5 @@ TRANS(bltu, ALL, gen_rr_bc, TCG_COND_LTU) TRANS(bgeu, ALL, gen_rr_bc, TCG_COND_GEU) TRANS(beqz, ALL, gen_rz_bc, TCG_COND_EQ) TRANS(bnez, ALL, gen_rz_bc, TCG_COND_NE) -TRANS(bceqz, 64, gen_cz_bc, TCG_COND_EQ) -TRANS(bcnez, 64, gen_cz_bc, TCG_COND_NE) +TRANS(bceqz, FP, gen_cz_bc, TCG_COND_EQ) +TRANS(bcnez, FP, gen_cz_bc, TCG_COND_NE) From 720a0e417ef0814d90aa884096a643b02ee854dc Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 7 Apr 2025 10:26:41 +0200 Subject: [PATCH 0241/2760] cleanup: Re-run return_directly.cocci MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coccinelle's indentation of virt_create_plic() results in a long line. Avoid that by mimicking the old indentation manually. Don't touch tests/tcg/mips/user/. I'm not sure these files are ours to make style cleanups on. They might be imported third-party code, which we should leave as is to not complicate future updates. Signed-off-by: Markus Armbruster Reviewed-by: Richard Henderson Message-ID: <20250407082643.2310002-2-armbru@redhat.com> Reviewed-by: Philippe Mathieu-Daudé --- hw/gpio/pca9554.c | 5 +---- hw/i386/kvm/xen_xenstore.c | 4 +--- hw/riscv/virt.c | 25 ++++++++++--------------- hw/scsi/esp.c | 5 +---- hw/vfio/common.c | 7 ++----- plugins/api.c | 4 +--- tests/qtest/cmsdk-apb-watchdog-test.c | 6 +----- tests/qtest/pnv-host-i2c-test.c | 4 +--- tests/qtest/stm32l4x5_usart-test.c | 6 +----- tools/i386/qemu-vmsr-helper.c | 5 +---- 10 files changed, 20 insertions(+), 51 deletions(-) diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c index fe03bb4b5e..7301fce934 100644 --- a/hw/gpio/pca9554.c +++ b/hw/gpio/pca9554.c @@ -118,11 +118,8 @@ static void pca9554_write(PCA9554State *s, uint8_t reg, uint8_t data) static uint8_t pca9554_recv(I2CSlave *i2c) { PCA9554State *s = PCA9554(i2c); - uint8_t ret; - ret = pca9554_read(s, s->pointer & 0x3); - - return ret; + return pca9554_read(s, s->pointer & 0x3); } static int pca9554_send(I2CSlave *i2c, uint8_t data) diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c index 17802aa33d..227ad7ace3 100644 --- a/hw/i386/kvm/xen_xenstore.c +++ b/hw/i386/kvm/xen_xenstore.c @@ -209,7 +209,6 @@ static int xen_xenstore_post_load(void *opaque, int ver) { XenXenstoreState *s = opaque; GByteArray *save; - int ret; /* * As qemu/dom0, rebind to the guest's port. The Windows drivers may @@ -231,8 +230,7 @@ static int xen_xenstore_post_load(void *opaque, int ver) s->impl_state = NULL; s->impl_state_size = 0; - ret = xs_impl_deserialize(s->impl, save, xen_domid, fire_watch_cb, s); - return ret; + return xs_impl_deserialize(s->impl, save, xen_domid, fire_watch_cb, s); } static const VMStateDescription xen_xenstore_vmstate = { diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index e517002fdf..85849e604c 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1276,27 +1276,22 @@ static FWCfgState *create_fw_cfg(const MachineState *ms) static DeviceState *virt_create_plic(const MemMapEntry *memmap, int socket, int base_hartid, int hart_count) { - DeviceState *ret; g_autofree char *plic_hart_config = NULL; /* Per-socket PLIC hart topology configuration string */ plic_hart_config = riscv_plic_hart_config_string(hart_count); /* Per-socket PLIC */ - ret = sifive_plic_create( - memmap[VIRT_PLIC].base + socket * memmap[VIRT_PLIC].size, - plic_hart_config, hart_count, base_hartid, - VIRT_IRQCHIP_NUM_SOURCES, - ((1U << VIRT_IRQCHIP_NUM_PRIO_BITS) - 1), - VIRT_PLIC_PRIORITY_BASE, - VIRT_PLIC_PENDING_BASE, - VIRT_PLIC_ENABLE_BASE, - VIRT_PLIC_ENABLE_STRIDE, - VIRT_PLIC_CONTEXT_BASE, - VIRT_PLIC_CONTEXT_STRIDE, - memmap[VIRT_PLIC].size); - - return ret; + return sifive_plic_create( + memmap[VIRT_PLIC].base + socket * memmap[VIRT_PLIC].size, + plic_hart_config, hart_count, base_hartid, + VIRT_IRQCHIP_NUM_SOURCES, + ((1U << VIRT_IRQCHIP_NUM_PRIO_BITS) - 1), + VIRT_PLIC_PRIORITY_BASE, VIRT_PLIC_PENDING_BASE, + VIRT_PLIC_ENABLE_BASE, VIRT_PLIC_ENABLE_STRIDE, + VIRT_PLIC_CONTEXT_BASE, + VIRT_PLIC_CONTEXT_STRIDE, + memmap[VIRT_PLIC].size); } static DeviceState *virt_create_aia(RISCVVirtAIAType aia_type, int aia_guests, diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index ac841dc32e..01bdfe2701 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -242,10 +242,7 @@ static uint32_t esp_get_stc(ESPState *s) static uint8_t esp_pdma_read(ESPState *s) { - uint8_t val; - - val = esp_fifo_pop(s); - return val; + return esp_fifo_pop(s); } static void esp_pdma_write(ESPState *s, uint8_t val) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 1a0d9290f8..d8aad4e1ce 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -110,8 +110,6 @@ static bool vfio_multiple_devices_migration_is_supported(void) int vfio_block_multiple_devices_migration(VFIODevice *vbasedev, Error **errp) { - int ret; - if (vfio_multiple_devices_migration_is_supported()) { return 0; } @@ -129,9 +127,8 @@ int vfio_block_multiple_devices_migration(VFIODevice *vbasedev, Error **errp) error_setg(&multiple_devices_migration_blocker, "Multiple VFIO devices migration is supported only if all of " "them support P2P migration"); - ret = migrate_add_blocker_normal(&multiple_devices_migration_blocker, errp); - - return ret; + return migrate_add_blocker_normal(&multiple_devices_migration_blocker, + errp); } void vfio_unblock_multiple_devices_migration(void) diff --git a/plugins/api.c b/plugins/api.c index 604ce06802..3c9d4832e9 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -237,12 +237,10 @@ uint64_t qemu_plugin_tb_vaddr(const struct qemu_plugin_tb *tb) struct qemu_plugin_insn * qemu_plugin_tb_get_insn(const struct qemu_plugin_tb *tb, size_t idx) { - struct qemu_plugin_insn *insn; if (unlikely(idx >= tb->n)) { return NULL; } - insn = g_ptr_array_index(tb->insns, idx); - return insn; + return g_ptr_array_index(tb->insns, idx); } /* diff --git a/tests/qtest/cmsdk-apb-watchdog-test.c b/tests/qtest/cmsdk-apb-watchdog-test.c index 53538f98c9..cd0c602361 100644 --- a/tests/qtest/cmsdk-apb-watchdog-test.c +++ b/tests/qtest/cmsdk-apb-watchdog-test.c @@ -364,8 +364,6 @@ static void test_watchdog_inten_luminary(const void *ptr) int main(int argc, char **argv) { - int r; - g_test_init(&argc, &argv, NULL); g_test_set_nonfatal_assertions(); @@ -393,7 +391,5 @@ int main(int argc, char **argv) test_watchdog_inten); } - r = g_test_run(); - - return r; + return g_test_run(); } diff --git a/tests/qtest/pnv-host-i2c-test.c b/tests/qtest/pnv-host-i2c-test.c index 7f64d597ac..51e613ebdc 100644 --- a/tests/qtest/pnv-host-i2c-test.c +++ b/tests/qtest/pnv-host-i2c-test.c @@ -191,12 +191,10 @@ static uint8_t pnv_i2c_pca9554_read_pins(PnvI2cDev *dev) { uint8_t send_buf[1]; uint8_t recv_buf[1]; - uint8_t inputs; send_buf[0] = PCA9554_INPUT; pnv_i2c_send(dev, send_buf, 1); pnv_i2c_recv(dev, recv_buf, 1); - inputs = recv_buf[0]; - return inputs; + return recv_buf[0]; } static void pnv_i2c_pca9554_flip_polarity(PnvI2cDev *dev) diff --git a/tests/qtest/stm32l4x5_usart-test.c b/tests/qtest/stm32l4x5_usart-test.c index 927bab6361..98a7472307 100644 --- a/tests/qtest/stm32l4x5_usart-test.c +++ b/tests/qtest/stm32l4x5_usart-test.c @@ -360,8 +360,6 @@ static void test_clock_enable(void) int main(int argc, char **argv) { - int ret; - g_test_init(&argc, &argv, NULL); g_test_set_nonfatal_assertions(); @@ -372,8 +370,6 @@ int main(int argc, char **argv) qtest_add_func("stm32l4x5/usart/send_str", test_send_str); qtest_add_func("stm32l4x5/usart/ack", test_ack); qtest_add_func("stm32l4x5/usart/clock_enable", test_clock_enable); - ret = g_test_run(); - - return ret; + return g_test_run(); } diff --git a/tools/i386/qemu-vmsr-helper.c b/tools/i386/qemu-vmsr-helper.c index a35dcb88a3..5f19a48cbd 100644 --- a/tools/i386/qemu-vmsr-helper.c +++ b/tools/i386/qemu-vmsr-helper.c @@ -71,7 +71,6 @@ static void compute_default_paths(void) static int is_intel_processor(void) { - int result; int ebx, ecx, edx; /* Execute CPUID instruction with eax=0 (basic identification) */ @@ -87,9 +86,7 @@ static int is_intel_processor(void) * 0x49656e69 = "ineI" * 0x6c65746e = "ntel" */ - result = (ebx == 0x756e6547) && (edx == 0x49656e69) && (ecx == 0x6c65746e); - - return result; + return (ebx == 0x756e6547) && (edx == 0x49656e69) && (ecx == 0x6c65746e); } static int is_rapl_enabled(void) From 8a2b516ba2855c4530388051de2b8d17bc780ea8 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 7 Apr 2025 10:26:42 +0200 Subject: [PATCH 0242/2760] cleanup: Drop pointless return at end of function A few functions now end with a label. The next commit will clean them up. Signed-off-by: Markus Armbruster Reviewed-by: Richard Henderson Message-ID: <20250407082643.2310002-3-armbru@redhat.com> [Straightforward conflict with commit 988ad4ccebb6 (hw/loongarch/virt: Fix cpuslot::cpu set at last in virt_cpu_plug()) resolved] --- accel/tcg/cpu-exec.c | 1 - block/gluster.c | 4 ---- block/rbd.c | 1 - block/replication.c | 1 - block/throttle-groups.c | 1 - bsd-user/signal.c | 1 - dump/win_dump.c | 2 -- event-loop-base.c | 2 -- hw/acpi/acpi-cpu-hotplug-stub.c | 8 -------- hw/acpi/acpi-mem-hotplug-stub.c | 5 ----- hw/acpi/acpi-nvdimm-stub.c | 1 - hw/acpi/acpi-pci-hotplug-stub.c | 6 ------ hw/arm/exynos4210.c | 1 - hw/arm/smmu-common.c | 1 - hw/arm/xen-stubs.c | 1 - hw/audio/asc.c | 1 - hw/core/qdev-properties-system.c | 1 - hw/cxl/cxl-host.c | 2 -- hw/display/macfb.c | 1 - hw/display/tcx.c | 1 - hw/display/virtio-gpu-base.c | 1 - hw/dma/sifive_pdma.c | 1 - hw/gpio/aspeed_gpio.c | 5 ----- hw/gpio/bcm2838_gpio.c | 1 - hw/gpio/imx_gpio.c | 2 -- hw/gpio/pl061.c | 1 - hw/hyperv/vmbus.c | 1 - hw/i2c/pm_smbus.c | 1 - hw/i386/intel_iommu.c | 2 -- hw/i386/nitro_enclave.c | 1 - hw/i386/xen/xen-hvm.c | 2 -- hw/input/virtio-input-host.c | 1 - hw/intc/arm_gicv3_cpuif.c | 1 - hw/intc/aspeed_intc.c | 4 ---- hw/intc/mips_gic.c | 1 - hw/ipmi/ipmi_bmc_extern.c | 2 -- hw/ipmi/ipmi_bmc_sim.c | 2 -- hw/ipmi/ipmi_bt.c | 1 - hw/ipmi/ipmi_kcs.c | 1 - hw/loongarch/virt.c | 2 -- hw/m68k/next-cube.c | 1 - hw/m68k/q800.c | 2 -- hw/mem/cxl_type3.c | 4 ---- hw/mem/sparse-mem.c | 1 - hw/misc/i2c-echo.c | 2 -- hw/misc/ivshmem-flat.c | 2 -- hw/misc/mips_cpc.c | 2 -- hw/net/can/ctucan_core.c | 2 -- hw/net/can/xlnx-versal-canfd.c | 2 -- hw/net/imx_fec.c | 1 - hw/net/vmxnet3.c | 1 - hw/nvram/xlnx-versal-efuse-ctrl.c | 1 - hw/ppc/mac_newworld.c | 2 -- hw/ppc/pnv_occ.c | 1 - hw/ppc/spapr_hcall.c | 1 - hw/ppc/spapr_nested.c | 1 - hw/ppc/spapr_nvdimm.c | 2 -- hw/s390x/s390-pci-bus.c | 1 - hw/s390x/s390-pci-vfio.c | 2 -- hw/scsi/megasas.c | 1 - hw/scsi/vhost-scsi.c | 1 - hw/ssi/ibex_spi_host.c | 1 - hw/ssi/pnv_spi.c | 2 -- hw/tpm/tpm_tis_i2c.c | 4 ---- hw/usb/dev-mtp.c | 2 -- hw/usb/dev-serial.c | 2 -- hw/usb/dev-smartcard-reader.c | 1 - hw/usb/dev-uas.c | 1 - hw/vfio/display.c | 1 - hw/vfio/pci.c | 1 - hw/vfio/platform.c | 1 - hw/virtio/vhost-user-fs.c | 1 - hw/virtio/vhost-user-scmi.c | 2 -- hw/virtio/vhost-user-vsock.c | 1 - hw/virtio/vhost-user.c | 2 -- hw/virtio/vhost-vdpa.c | 2 -- hw/virtio/vhost.c | 1 - hw/virtio/virtio-nsm.c | 1 - hw/virtio/virtio.c | 2 -- hw/watchdog/sbsa_gwdt.c | 1 - hw/watchdog/wdt_aspeed.c | 1 - include/system/os-win32.h | 1 - linux-user/xtensa/signal.c | 1 - migration/multifd-nocomp.c | 1 - migration/qemu-file.c | 2 -- migration/ram.c | 2 -- net/colo-compare.c | 2 -- qemu-keymap.c | 1 - qga/commands-win32.c | 4 ---- system/dirtylimit.c | 2 -- target/arm/tcg/helper-a64.c | 2 -- target/i386/kvm/vmsr_energy.c | 1 - target/i386/tcg/translate.c | 1 - target/i386/whpx/whpx-all.c | 11 ----------- target/m68k/helper.c | 1 - target/mips/tcg/system/mips-semi.c | 1 - target/ppc/kvm.c | 1 - target/ppc/kvm_ppc.h | 3 --- target/ppc/translate.c | 1 - target/riscv/debug.c | 6 ------ target/s390x/cpu_models.c | 1 - target/sh4/translate.c | 1 - tests/qtest/ahci-test.c | 1 - tests/qtest/fuzz/generic_fuzz.c | 1 - tests/qtest/libqos/libqos-malloc.c | 1 - tests/qtest/libqtest.c | 1 - tests/qtest/test-x86-cpuid-compat.c | 1 - tests/unit/socket-helpers.c | 1 - tests/unit/test-qgraph.c | 1 - ui/input-linux.c | 1 - ui/vnc.c | 2 -- util/main-loop.c | 1 - util/qht.c | 1 - 113 files changed, 196 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index ef3d967e3a..8e28136392 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -665,7 +665,6 @@ static inline void tb_add_jump(TranslationBlock *tb, int n, out_unlock_next: qemu_spin_unlock(&tb_next->jmp_lock); - return; } static inline bool cpu_handle_halt(CPUState *cpu) diff --git a/block/gluster.c b/block/gluster.c index c6d25ae733..8712aa606a 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -972,8 +972,6 @@ static void qemu_gluster_reopen_commit(BDRVReopenState *state) g_free(state->opaque); state->opaque = NULL; - - return; } @@ -993,8 +991,6 @@ static void qemu_gluster_reopen_abort(BDRVReopenState *state) g_free(state->opaque); state->opaque = NULL; - - return; } #ifdef CONFIG_GLUSTERFS_ZEROFILL diff --git a/block/rbd.c b/block/rbd.c index af984fb7db..7446e66659 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -254,7 +254,6 @@ static void qemu_rbd_parse_filename(const char *filename, QDict *options, done: g_free(buf); qobject_unref(keypairs); - return; } static int qemu_rbd_set_auth(rados_t cluster, BlockdevOptionsRbd *opts, diff --git a/block/replication.c b/block/replication.c index 0020f33843..d6625c51fe 100644 --- a/block/replication.c +++ b/block/replication.c @@ -176,7 +176,6 @@ static void replication_child_perm(BlockDriverState *bs, BdrvChild *c, *nshared = BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE | BLK_PERM_WRITE_UNCHANGED; - return; } static int64_t coroutine_fn GRAPH_RDLOCK diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 32553b39e3..9f4d252c74 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -908,7 +908,6 @@ static void throttle_group_set_limits(Object *obj, Visitor *v, qemu_mutex_unlock(&tg->lock); qapi_free_ThrottleLimits(argp); error_propagate(errp, local_err); - return; } static void throttle_group_get_limits(Object *obj, Visitor *v, diff --git a/bsd-user/signal.c b/bsd-user/signal.c index a8cfcca130..1aa0fd79d6 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -441,7 +441,6 @@ void queue_signal(CPUArchState *env, int sig, int si_type, ts->sync_signal.pending = sig; /* Signal that a new signal is pending. */ qatomic_set(&ts->signal_pending, 1); - return; } static int fatal_signal(int sig) diff --git a/dump/win_dump.c b/dump/win_dump.c index 2c2576672a..3162e8bd48 100644 --- a/dump/win_dump.c +++ b/dump/win_dump.c @@ -476,8 +476,6 @@ void create_win_dump(DumpState *s, Error **errp) g_free(saved_ctx); out_cr3: first_x86_cpu->env.cr[3] = saved_cr3; - - return; } #else /* !TARGET_X86_64 */ diff --git a/event-loop-base.c b/event-loop-base.c index 0cfb1c9496..ddf8400a6b 100644 --- a/event-loop-base.c +++ b/event-loop-base.c @@ -73,8 +73,6 @@ static void event_loop_base_set_param(Object *obj, Visitor *v, if (bc->update_params) { bc->update_params(base, errp); } - - return; } static void event_loop_base_complete(UserCreatable *uc, Error **errp) diff --git a/hw/acpi/acpi-cpu-hotplug-stub.c b/hw/acpi/acpi-cpu-hotplug-stub.c index c6c61bb9cd..9872dd55e4 100644 --- a/hw/acpi/acpi-cpu-hotplug-stub.c +++ b/hw/acpi/acpi-cpu-hotplug-stub.c @@ -10,47 +10,39 @@ void acpi_switch_to_modern_cphp(AcpiCpuHotplug *gpe_cpu, CPUHotplugState *cpuhp_state, uint16_t io_port) { - return; } void legacy_acpi_cpu_hotplug_init(MemoryRegion *parent, Object *owner, AcpiCpuHotplug *gpe_cpu, uint16_t base) { - return; } void cpu_hotplug_hw_init(MemoryRegion *as, Object *owner, CPUHotplugState *state, hwaddr base_addr) { - return; } void acpi_cpu_ospm_status(CPUHotplugState *cpu_st, ACPIOSTInfoList ***list) { - return; } void acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, CPUHotplugState *cpu_st, DeviceState *dev, Error **errp) { - return; } void legacy_acpi_cpu_plug_cb(HotplugHandler *hotplug_dev, AcpiCpuHotplug *g, DeviceState *dev, Error **errp) { - return; } void acpi_cpu_unplug_cb(CPUHotplugState *cpu_st, DeviceState *dev, Error **errp) { - return; } void acpi_cpu_unplug_request_cb(HotplugHandler *hotplug_dev, CPUHotplugState *cpu_st, DeviceState *dev, Error **errp) { - return; } diff --git a/hw/acpi/acpi-mem-hotplug-stub.c b/hw/acpi/acpi-mem-hotplug-stub.c index 73a076a265..7ad0fdcdf2 100644 --- a/hw/acpi/acpi-mem-hotplug-stub.c +++ b/hw/acpi/acpi-mem-hotplug-stub.c @@ -7,29 +7,24 @@ const VMStateDescription vmstate_memory_hotplug; void acpi_memory_hotplug_init(MemoryRegion *as, Object *owner, MemHotplugState *state, hwaddr io_base) { - return; } void acpi_memory_ospm_status(MemHotplugState *mem_st, ACPIOSTInfoList ***list) { - return; } void acpi_memory_plug_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp) { - return; } void acpi_memory_unplug_cb(MemHotplugState *mem_st, DeviceState *dev, Error **errp) { - return; } void acpi_memory_unplug_request_cb(HotplugHandler *hotplug_dev, MemHotplugState *mem_st, DeviceState *dev, Error **errp) { - return; } diff --git a/hw/acpi/acpi-nvdimm-stub.c b/hw/acpi/acpi-nvdimm-stub.c index 8baff9be6f..65f491d653 100644 --- a/hw/acpi/acpi-nvdimm-stub.c +++ b/hw/acpi/acpi-nvdimm-stub.c @@ -4,5 +4,4 @@ void nvdimm_acpi_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev) { - return; } diff --git a/hw/acpi/acpi-pci-hotplug-stub.c b/hw/acpi/acpi-pci-hotplug-stub.c index dcee3ad7a1..b67b4a92da 100644 --- a/hw/acpi/acpi-pci-hotplug-stub.c +++ b/hw/acpi/acpi-pci-hotplug-stub.c @@ -7,37 +7,31 @@ const VMStateDescription vmstate_acpi_pcihp_pci_status; void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, MemoryRegion *address_space_io, uint16_t io_base) { - return; } void acpi_pcihp_device_plug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { - return; } void acpi_pcihp_device_pre_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { - return; } void acpi_pcihp_device_unplug_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { - return; } void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp) { - return; } void acpi_pcihp_reset(AcpiPciHpState *s) { - return; } bool acpi_pcihp_is_hotpluggbale_bus(AcpiPciHpState *s, BusState *bus) diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index b452470598..0c27588116 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -462,7 +462,6 @@ static uint64_t exynos4210_chipid_and_omr_read(void *opaque, hwaddr offset, static void exynos4210_chipid_and_omr_write(void *opaque, hwaddr offset, uint64_t value, unsigned size) { - return; } static const MemoryRegionOps exynos4210_chipid_and_omr_ops = { diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 6e720e1b9a..1aa2eabfbd 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -712,7 +712,6 @@ static void combine_tlb(SMMUTLBEntry *tlbe, SMMUTLBEntry *tlbe_s2, tlbe->entry.iova = iova & ~tlbe->entry.addr_mask; /* parent_perm has s2 perm while perm keeps s1 perm. */ tlbe->parent_perm = tlbe_s2->entry.perm; - return; } /** diff --git a/hw/arm/xen-stubs.c b/hw/arm/xen-stubs.c index 5551584dc2..6a83043553 100644 --- a/hw/arm/xen-stubs.c +++ b/hw/arm/xen-stubs.c @@ -14,7 +14,6 @@ void arch_handle_ioreq(XenIOState *state, ioreq_t *req) { hw_error("Invalid ioreq type 0x%x\n", req->type); - return; } void arch_xen_set_memory(XenIOState *state, MemoryRegionSection *section, diff --git a/hw/audio/asc.c b/hw/audio/asc.c index cc205bf063..cea7a1c053 100644 --- a/hw/audio/asc.c +++ b/hw/audio/asc.c @@ -406,7 +406,6 @@ static void asc_fifo_write(void *opaque, hwaddr addr, uint64_t value, } else { fs->fifo[addr] = value; } - return; } static const MemoryRegionOps asc_fifo_ops = { diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index a7dde73c29..8e11e6388b 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -793,7 +793,6 @@ static void set_reserved_region(Object *obj, Visitor *v, const char *name, error_setg(errp, "reserved region fields must be separated with ':'"); out: g_free(str); - return; } const PropertyInfo qdev_prop_reserved_region = { diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index 2c6b43cd0d..e010163174 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -67,8 +67,6 @@ static void cxl_fixed_memory_window_config(CXLState *cxl_state, cxl_state->fixed_windows = g_list_append(cxl_state->fixed_windows, g_steal_pointer(&fw)); - - return; } void cxl_fmws_link_targets(CXLState *cxl_state, Error **errp) diff --git a/hw/display/macfb.c b/hw/display/macfb.c index e83fc863be..b08eb06cbd 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -383,7 +383,6 @@ static void macfb_sense_write(MacfbState *s, uint32_t val) s->regs[DAFB_MODE_SENSE >> 2] = val; trace_macfb_sense_write(val); - return; } static void macfb_update_mode(MacfbState *s) diff --git a/hw/display/tcx.c b/hw/display/tcx.c index 2cfc1e8f01..5968d33e48 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -729,7 +729,6 @@ static uint64_t tcx_dummy_readl(void *opaque, hwaddr addr, static void tcx_dummy_writel(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - return; } static const MemoryRegionOps tcx_dummy_ops = { diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index 7827536ac4..321a6f4998 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -110,7 +110,6 @@ static void virtio_gpu_ui_info(void *opaque, uint32_t idx, QemuUIInfo *info) /* send event to guest */ virtio_gpu_notify_event(g, VIRTIO_GPU_EVENT_DISPLAY); - return; } static void diff --git a/hw/dma/sifive_pdma.c b/hw/dma/sifive_pdma.c index 25b3d6a155..a115af8d60 100644 --- a/hw/dma/sifive_pdma.c +++ b/hw/dma/sifive_pdma.c @@ -152,7 +152,6 @@ static void sifive_pdma_run(SiFivePDMAState *s, int ch) error: s->chan[ch].state = DMA_CHAN_STATE_ERROR; s->chan[ch].control |= CONTROL_ERR; - return; } static inline void sifive_pdma_update_irq(SiFivePDMAState *s, int ch) diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index a5b3f454e8..aedaf5238b 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -800,7 +800,6 @@ static void aspeed_gpio_write_index_mode(void *opaque, hwaddr offset, return; } aspeed_gpio_update(s, set, set->data_value, UINT32_MAX); - return; } static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, @@ -928,7 +927,6 @@ static void aspeed_gpio_write(void *opaque, hwaddr offset, uint64_t data, return; } aspeed_gpio_update(s, set, set->data_value, UINT32_MAX); - return; } static int get_set_idx(AspeedGPIOState *s, const char *group, int *group_idx) @@ -1183,7 +1181,6 @@ static void aspeed_gpio_2700_write_control_reg(AspeedGPIOState *s, } aspeed_gpio_update(s, set, set->data_value, UINT32_MAX); - return; } static uint64_t aspeed_gpio_2700_read(void *opaque, hwaddr offset, @@ -1308,8 +1305,6 @@ static void aspeed_gpio_2700_write(void *opaque, hwaddr offset, PRIx64"\n", __func__, offset); break; } - - return; } /* Setup functions */ diff --git a/hw/gpio/bcm2838_gpio.c b/hw/gpio/bcm2838_gpio.c index 0a1739fc46..53be8f2d23 100644 --- a/hw/gpio/bcm2838_gpio.c +++ b/hw/gpio/bcm2838_gpio.c @@ -293,7 +293,6 @@ static void bcm2838_gpio_write(void *opaque, hwaddr offset, uint64_t value, qemu_log_mask(LOG_GUEST_ERROR, "%s: %s: bad offset %"HWADDR_PRIx"\n", TYPE_BCM2838_GPIO, __func__, offset); } - return; } static void bcm2838_gpio_reset(DeviceState *dev) diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index 549a281ed7..8c8299c4c4 100644 --- a/hw/gpio/imx_gpio.c +++ b/hw/gpio/imx_gpio.c @@ -257,8 +257,6 @@ static void imx_gpio_write(void *opaque, hwaddr offset, uint64_t value, HWADDR_PRIx "\n", TYPE_IMX_GPIO, __func__, offset); break; } - - return; } static const MemoryRegionOps imx_gpio_ops = { diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index 60ce4a7f62..2e69785f2a 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -443,7 +443,6 @@ static void pl061_write(void *opaque, hwaddr offset, return; } pl061_update(s); - return; } static void pl061_enter_reset(Object *obj, ResetType type) diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index 12a7dc4312..f195e56c83 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -2073,7 +2073,6 @@ static void send_unload(VMBus *vmbus) qemu_mutex_unlock(&vmbus->rx_queue_lock); post_msg(vmbus, &msg, sizeof(msg)); - return; } static bool complete_unload(VMBus *vmbus) diff --git a/hw/i2c/pm_smbus.c b/hw/i2c/pm_smbus.c index 3eed8110b9..4e685fd26e 100644 --- a/hw/i2c/pm_smbus.c +++ b/hw/i2c/pm_smbus.c @@ -205,7 +205,6 @@ static void smb_transaction(PMSMBus *s) error: s->smb_stat |= STS_DEV_ERR; - return; } static void smb_transaction_start(PMSMBus *s) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index dffd7ee885..0608aec8c5 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -4515,8 +4515,6 @@ static void vtd_iommu_replay(IOMMUMemoryRegion *iommu_mr, IOMMUNotifier *n) trace_vtd_replay_ce_invalid(bus_n, PCI_SLOT(vtd_as->devfn), PCI_FUNC(vtd_as->devfn)); } - - return; } static void vtd_cap_init(IntelIOMMUState *s) diff --git a/hw/i386/nitro_enclave.c b/hw/i386/nitro_enclave.c index a058608afc..4b69f265cc 100644 --- a/hw/i386/nitro_enclave.c +++ b/hw/i386/nitro_enclave.c @@ -203,7 +203,6 @@ static void x86_load_eif(X86MachineState *x86ms, FWCfgState *fw_cfg, unlink(machine->kernel_filename); unlink(machine->initrd_filename); - return; } static bool create_memfd_backend(MachineState *ms, const char *path, diff --git a/hw/i386/xen/xen-hvm.c b/hw/i386/xen/xen-hvm.c index d4516acec6..ceb2242aa7 100644 --- a/hw/i386/xen/xen-hvm.c +++ b/hw/i386/xen/xen-hvm.c @@ -758,6 +758,4 @@ void arch_handle_ioreq(XenIOState *state, ioreq_t *req) default: hw_error("Invalid ioreq type 0x%x\n", req->type); } - - return; } diff --git a/hw/input/virtio-input-host.c b/hw/input/virtio-input-host.c index 8bfb17f3c4..b21a79046e 100644 --- a/hw/input/virtio-input-host.c +++ b/hw/input/virtio-input-host.c @@ -178,7 +178,6 @@ static void virtio_input_host_realize(DeviceState *dev, Error **errp) err_close: close(vih->fd); vih->fd = -1; - return; } static void virtio_input_host_unrealize(DeviceState *dev) diff --git a/hw/intc/arm_gicv3_cpuif.c b/hw/intc/arm_gicv3_cpuif.c index de37465bc8..4b4cf09157 100644 --- a/hw/intc/arm_gicv3_cpuif.c +++ b/hw/intc/arm_gicv3_cpuif.c @@ -584,7 +584,6 @@ static void icv_ap_write(CPUARMState *env, const ARMCPRegInfo *ri, } gicv3_cpuif_virt_irq_fiq_update(cs); - return; } static uint64_t icv_bpr_read(CPUARMState *env, const ARMCPRegInfo *ri) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index f17bf43925..bae7dc95ea 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -448,8 +448,6 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, s->regs[reg] = data; break; } - - return; } static uint64_t aspeed_intcio_read(void *opaque, hwaddr offset, @@ -496,8 +494,6 @@ static void aspeed_intcio_write(void *opaque, hwaddr offset, uint64_t data, s->regs[reg] = data; break; } - - return; } diff --git a/hw/intc/mips_gic.c b/hw/intc/mips_gic.c index 5e3cbeabec..bd1dedd4b9 100644 --- a/hw/intc/mips_gic.c +++ b/hw/intc/mips_gic.c @@ -255,7 +255,6 @@ static void gic_write_vp(MIPSGICState *gic, uint32_t vp_index, hwaddr addr, return; bad_offset: qemu_log_mask(LOG_GUEST_ERROR, "Wrong GIC offset at 0x%" PRIx64 "\n", addr); - return; } static void gic_write(void *opaque, hwaddr addr, uint64_t data, unsigned size) diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index 3e9f8c5a50..22cb9b590b 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -142,7 +142,6 @@ static void continue_send(IPMIBmcExtern *ibe) qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) + 4000000000ULL); } } - return; } static void extern_timeout(void *opaque) @@ -231,7 +230,6 @@ static void ipmi_bmc_extern_handle_command(IPMIBmc *b, continue_send(ibe); out: - return; } static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 1c60a71831..c6a85e8cd1 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -472,7 +472,6 @@ void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log) ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; k->set_atn(s, 1, attn_irq_enabled(ibs)); out: - return; } static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert, uint8_t evd1, uint8_t evd2, uint8_t evd3) @@ -1014,7 +1013,6 @@ static void get_msg(IPMIBmcSim *ibs, } out: - return; } static unsigned char diff --git a/hw/ipmi/ipmi_bt.c b/hw/ipmi/ipmi_bt.c index 28cf6ab218..5cf12c59a8 100644 --- a/hw/ipmi/ipmi_bt.c +++ b/hw/ipmi/ipmi_bt.c @@ -146,7 +146,6 @@ static void ipmi_bt_handle_event(IPMIInterface *ii) sizeof(ib->inmsg), ib->waiting_rsp); } out: - return; } static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id, diff --git a/hw/ipmi/ipmi_kcs.c b/hw/ipmi/ipmi_kcs.c index 578dd7cef3..ae549fb864 100644 --- a/hw/ipmi/ipmi_kcs.c +++ b/hw/ipmi/ipmi_kcs.c @@ -198,7 +198,6 @@ static void ipmi_kcs_handle_event(IPMIInterface *ii) ik->data_in_reg = -1; IPMI_KCS_SET_IBF(ik->status_reg, 0); out_noibf: - return; } static void ipmi_kcs_handle_rsp(IPMIInterface *ii, uint8_t msg_id, diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 65c9027feb..b2793c803e 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -948,7 +948,6 @@ static void virt_cpu_unplug(HotplugHandler *hotplug_dev, cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); cpu_slot->cpu = NULL; - return; } static void virt_cpu_plug(HotplugHandler *hotplug_dev, @@ -973,7 +972,6 @@ static void virt_cpu_plug(HotplugHandler *hotplug_dev, cpu_slot = virt_find_cpu_slot(MACHINE(lvms), cpu->phy_id); cpu_slot->cpu = CPU(dev); - return; } static bool memhp_type_supported(DeviceState *dev) diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 0570e4a76f..7ea7e91032 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -899,7 +899,6 @@ static void next_dummy_en_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { /* Do nothing */ - return; } static uint64_t next_dummy_en_read(void *opaque, hwaddr addr, unsigned size) diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index aeed4c8ddb..1e5b0e21df 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -210,7 +210,6 @@ static uint64_t machine_id_read(void *opaque, hwaddr addr, unsigned size) static void machine_id_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - return; } static const MemoryRegionOps machine_id_ops = { @@ -231,7 +230,6 @@ static uint64_t ramio_read(void *opaque, hwaddr addr, unsigned size) static void ramio_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - return; } static const MemoryRegionOps ramio_ops = { diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 6fffa21ead..43aa02ab2a 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -957,7 +957,6 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) if (ct3d->hostvmem) { address_space_destroy(&ct3d->hostvmem_as); } - return; } static void ct3_exit(PCIDevice *pci_dev) @@ -1511,8 +1510,6 @@ void qmp_cxl_inject_uncorrectable_errors(const char *path, stl_le_p(reg_state + R_CXL_RAS_UNC_ERR_STATUS, unc_err); pcie_aer_inject_error(PCI_DEVICE(obj), &err); - - return; } void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type, @@ -1788,7 +1785,6 @@ void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log, uint8_t flags, if (cxl_event_insert(cxlds, enc_log, (CXLEventRecordRaw *)&dram)) { cxl_event_irq_assert(ct3d); } - return; } void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, diff --git a/hw/mem/sparse-mem.c b/hw/mem/sparse-mem.c index 6a9a591370..8bed5dbe16 100644 --- a/hw/mem/sparse-mem.c +++ b/hw/mem/sparse-mem.c @@ -82,7 +82,6 @@ static void sparse_mem_enter_reset(Object *obj, ResetType type) { SparseMemState *s = SPARSE_MEM(obj); g_hash_table_remove_all(s->mapped); - return; } static const MemoryRegionOps sparse_mem_ops = { diff --git a/hw/misc/i2c-echo.c b/hw/misc/i2c-echo.c index 65d10029dc..fcd407dfc6 100644 --- a/hw/misc/i2c-echo.c +++ b/hw/misc/i2c-echo.c @@ -143,8 +143,6 @@ static void i2c_echo_realize(DeviceState *dev, Error **errp) state->bus = I2C_BUS(bus); state->bh = qemu_bh_new(i2c_echo_bh, state); - - return; } static void i2c_echo_class_init(ObjectClass *oc, void *data) diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c index 40309a8ff3..a0ac7543a2 100644 --- a/hw/misc/ivshmem-flat.c +++ b/hw/misc/ivshmem-flat.c @@ -289,8 +289,6 @@ static void ivshmem_flat_iomem_write(void *opaque, hwaddr offset, trace_ivshmem_flat_read_write_mmr_invalid(offset); break; } - - return; } static const MemoryRegionOps ivshmem_flat_ops = { diff --git a/hw/misc/mips_cpc.c b/hw/misc/mips_cpc.c index 772b8c0017..b7a13d1afb 100644 --- a/hw/misc/mips_cpc.c +++ b/hw/misc/mips_cpc.c @@ -92,8 +92,6 @@ static void cpc_write(void *opaque, hwaddr offset, uint64_t data, "%s: Bad offset 0x%x\n", __func__, (int)offset); break; } - - return; } static uint64_t cpc_read(void *opaque, hwaddr offset, unsigned size) diff --git a/hw/net/can/ctucan_core.c b/hw/net/can/ctucan_core.c index 4402d4cb1f..17131a4e18 100644 --- a/hw/net/can/ctucan_core.c +++ b/hw/net/can/ctucan_core.c @@ -400,8 +400,6 @@ void ctucan_mem_write(CtuCanCoreState *s, hwaddr addr, uint64_t val, ctucan_update_irq(s); } - - return; } uint64_t ctucan_mem_read(CtuCanCoreState *s, hwaddr addr, unsigned size) diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c index dc242e9215..b5a4a4af7e 100644 --- a/hw/net/can/xlnx-versal-canfd.c +++ b/hw/net/can/xlnx-versal-canfd.c @@ -1298,8 +1298,6 @@ static void free_list(GSList *list) } g_slist_free(list); - - return; } static GSList *prepare_tx_data(XlnxVersalCANFDState *s) diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index 0f0afda58a..b7c9ee0b9a 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -668,7 +668,6 @@ static void imx_default_write(IMXFECState *s, uint32_t index, uint32_t value) { qemu_log_mask(LOG_GUEST_ERROR, "[%s]%s: Bad address at offset 0x%" PRIx32 "\n", TYPE_IMX_FEC, __func__, index * 4); - return; } static void imx_fec_write(IMXFECState *s, uint32_t index, uint32_t value) diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 7abed66469..f370d4a2ec 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -932,7 +932,6 @@ static void vmxnet3_rx_update_descr(struct NetRxPkt *pkt, nocsum: rxcd->cnc = 1; - return; } static void diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c index 3246eb3ca6..ff4d544ad6 100644 --- a/hw/nvram/xlnx-versal-efuse-ctrl.c +++ b/hw/nvram/xlnx-versal-efuse-ctrl.c @@ -494,7 +494,6 @@ static void efuse_rd_addr_postw(RegisterInfo *reg, uint64_t val64) ARRAY_FIELD_DP32(s->regs, EFUSE_ISR, RD_DONE, 1); efuse_imr_update_irq(s); - return; } static uint64_t efuse_cache_load_prew(RegisterInfo *reg, uint64_t val64) diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 2d5309d6f5..8109e213a1 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -630,8 +630,6 @@ static void core99_instance_init(Object *obj) object_property_set_description(obj, "via", "Set VIA configuration. " "Valid values are cuda, pmu and pmu-adb"); - - return; } static const TypeInfo core99_machine_info = { diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 177c5e514b..0c9d3daec2 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -150,7 +150,6 @@ static void pnv_occ_common_area_write(void *opaque, hwaddr addr, uint64_t val, unsigned width) { /* callback function defined to occ common area write */ - return; } static const MemoryRegionOps pnv_occ_power8_xscom_ops = { diff --git a/hw/ppc/spapr_hcall.c b/hw/ppc/spapr_hcall.c index 406aea4ecb..29453a880e 100644 --- a/hw/ppc/spapr_hcall.c +++ b/hw/ppc/spapr_hcall.c @@ -981,7 +981,6 @@ static void spapr_check_setup_free_hpt(SpaprMachineState *spapr, /* RADIX->HASH || NOTHING->HASH : Allocate HPT */ spapr_setup_hpt(spapr); } - return; } #define FLAGS_MASK 0x01FULL diff --git a/hw/ppc/spapr_nested.c b/hw/ppc/spapr_nested.c index 201f629203..3797220a29 100644 --- a/hw/ppc/spapr_nested.c +++ b/hw/ppc/spapr_nested.c @@ -1735,7 +1735,6 @@ static void exit_process_output_buffer(SpaprMachineState *spapr, getset_state(spapr, guest, vcpuid, &gsr); address_space_unmap(CPU(cpu)->as, gsb, len, true, len); - return; } static diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index 6f875d73b2..6e93ff9b33 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -235,8 +235,6 @@ void spapr_dt_persistent_memory(SpaprMachineState *spapr, void *fdt) spapr_dt_nvdimm(spapr, fdt, offset, nvdimm); } g_slist_free(nvdimms); - - return; } static target_ulong h_scm_read_metadata(PowerPCCPU *cpu, diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 2591ee49c1..d96819f1a4 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -597,7 +597,6 @@ static void s390_pci_iommu_replay(IOMMUMemoryRegion *iommu, * zpci device" construct. But when we support migration of vfio-pci * devices in future, we need to revisit this. */ - return; } static S390PCIIOMMU *s390_pci_get_iommu(S390pciState *s, PCIBus *bus, diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index 6236ac7f1e..db152a6252 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -367,6 +367,4 @@ void s390_pci_get_clp_info(S390PCIBusDevice *pbdev) s390_pci_read_group(pbdev, info); s390_pci_read_util(pbdev, info); s390_pci_read_pfip(pbdev, info); - - return; } diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 9f3b30e6ce..d56bfc711d 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2226,7 +2226,6 @@ static uint64_t megasas_queue_read(void *opaque, hwaddr addr, static void megasas_queue_write(void *opaque, hwaddr addr, uint64_t val, unsigned size) { - return; } static const MemoryRegionOps megasas_queue_ops = { diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 8039d13fd9..66e0c21c22 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -314,7 +314,6 @@ static void vhost_scsi_realize(DeviceState *dev, Error **errp) if (vhostfd >= 0) { close(vhostfd); } - return; } static void vhost_scsi_unrealize(DeviceState *dev) diff --git a/hw/ssi/ibex_spi_host.c b/hw/ssi/ibex_spi_host.c index 46c7b633c2..6b28cda200 100644 --- a/hw/ssi/ibex_spi_host.c +++ b/hw/ssi/ibex_spi_host.c @@ -154,7 +154,6 @@ static void ibex_spi_host_reset(DeviceState *dev) ibex_spi_txfifo_reset(s); s->init_status = true; - return; } /* diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c index 126070393e..367a2ff3bb 100644 --- a/hw/ssi/pnv_spi.c +++ b/hw/ssi/pnv_spi.c @@ -996,7 +996,6 @@ static void operation_sequencer(PnvSpi *s) } /* end of while */ /* Update sequencer index field in status.*/ s->status = SETFIELD(SPI_STS_SEQ_INDEX, s->status, seq_index); - return; } /* end of operation_sequencer() */ /* @@ -1142,7 +1141,6 @@ static void pnv_spi_xscom_write(void *opaque, hwaddr addr, qemu_log_mask(LOG_GUEST_ERROR, "pnv_spi_regs: Invalid xscom " "write at 0x%" PRIx32 "\n", reg); } - return; } static const MemoryRegionOps pnv_spi_xscom_ops = { diff --git a/hw/tpm/tpm_tis_i2c.c b/hw/tpm/tpm_tis_i2c.c index 504328e3b0..92d3de1ea3 100644 --- a/hw/tpm/tpm_tis_i2c.c +++ b/hw/tpm/tpm_tis_i2c.c @@ -211,8 +211,6 @@ static inline void tpm_tis_i2c_clear_data(TPMStateI2C *i2cst) i2cst->tis_addr = 0xffffffff; i2cst->reg_name = NULL; memset(i2cst->data, 0, sizeof(i2cst->data)); - - return; } /* Send data to TPM */ @@ -281,8 +279,6 @@ static inline void tpm_tis_i2c_tpm_send(TPMStateI2C *i2cst) tpm_tis_i2c_clear_data(i2cst); } - - return; } /* Callback from TPM to indicate that response is copied */ diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 326c92a43d..4cd14c3df4 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -1234,8 +1234,6 @@ static void usb_mtp_object_delete(MTPState *s, uint32_t handle, default: g_assert_not_reached(); } - - return; } static void usb_mtp_command(MTPState *s, MTPControl *c) diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index aa50a92e26..31f6cf5b31 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -472,8 +472,6 @@ static void usb_serial_token_in(USBSerialState *s, USBPacket *p) s->recv_ptr = (s->recv_ptr + len) % RECV_BUF; packet_len -= len + 2; } - - return; } static void usb_serial_handle_data(USBDevice *dev, USBPacket *p) diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 73deb3ce83..84ca8c48e2 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1069,7 +1069,6 @@ static void ccid_handle_bulk_out(USBCCIDState *s, USBPacket *p) err: p->status = USB_RET_STALL; s->bulk_out_pos = 0; - return; } static void ccid_bulk_in_copy_to_guest(USBCCIDState *s, USBPacket *p, diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index 44e30013d7..b1d6b6ecc3 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -914,7 +914,6 @@ static void usb_uas_handle_data(USBDevice *dev, USBPacket *p) err_stream: error_report("%s: invalid stream %d", __func__, p->stream); p->status = USB_RET_STALL; - return; } static void usb_uas_unrealize(USBDevice *dev) diff --git a/hw/vfio/display.c b/hw/vfio/display.c index ea87830fe0..4fdcef505d 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -104,7 +104,6 @@ static void vfio_display_edid_update(VFIOPCIDevice *vdev, bool enabled, err: trace_vfio_display_edid_write_error(); - return; } static void vfio_display_edid_ui_info(void *opaque, uint32_t idx, diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 7f1532fbed..f87f3ccbe1 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2383,7 +2383,6 @@ static void vfio_add_ext_cap(VFIOPCIDevice *vdev) } g_free(config); - return; } static bool vfio_add_capabilities(VFIOPCIDevice *vdev, Error **errp) diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 67bc57409c..7b4e100e27 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -418,7 +418,6 @@ static void vfio_start_irqfd_injection(SysBusDevice *sbdev, qemu_irq irq) abort(); fail_irqfd: vfio_start_eventfd_injection(sbdev, irq); - return; } /* VFIO skeleton */ diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index 3f00d79ed0..0e8d4b3823 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -267,7 +267,6 @@ static void vuf_device_realize(DeviceState *dev, Error **errp) g_free(fs->req_vqs); virtio_cleanup(vdev); g_free(fs->vhost_dev.vqs); - return; } static void vuf_device_unrealize(DeviceState *dev) diff --git a/hw/virtio/vhost-user-scmi.c b/hw/virtio/vhost-user-scmi.c index 410a936ca7..04cd36dd2e 100644 --- a/hw/virtio/vhost-user-scmi.c +++ b/hw/virtio/vhost-user-scmi.c @@ -258,8 +258,6 @@ static void vu_scmi_device_realize(DeviceState *dev, Error **errp) qemu_chr_fe_set_handlers(&scmi->chardev, NULL, NULL, vu_scmi_event, NULL, dev, NULL, true); - - return; } static void vu_scmi_device_unrealize(DeviceState *dev) diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c index 293273080b..d21cd4b516 100644 --- a/hw/virtio/vhost-user-vsock.c +++ b/hw/virtio/vhost-user-vsock.c @@ -128,7 +128,6 @@ static void vuv_device_realize(DeviceState *dev, Error **errp) err_virtio: vhost_vsock_common_unrealize(vdev); vhost_user_cleanup(&vsock->vhost_user); - return; } static void vuv_device_unrealize(DeviceState *dev) diff --git a/hw/virtio/vhost-user.c b/hw/virtio/vhost-user.c index 267b612587..fdc01ab99e 100644 --- a/hw/virtio/vhost-user.c +++ b/hw/virtio/vhost-user.c @@ -654,8 +654,6 @@ static void scrub_shadow_regions(struct vhost_dev *dev, } *nr_rem_reg = rm_idx; *nr_add_reg = add_idx; - - return; } static int send_remove_regions(struct vhost_dev *dev, diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 7efbde3d4c..8b45756ae7 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -288,8 +288,6 @@ static void vhost_vdpa_iommu_region_add(MemoryListener *listener, QLIST_INSERT_HEAD(&s->iommu_list, iommu, iommu_next); memory_region_iommu_replay(iommu->iommu_mr, &iommu->n); - - return; } static void vhost_vdpa_iommu_region_del(MemoryListener *listener, diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 6aa72fd434..4cae7c1664 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -732,7 +732,6 @@ static void vhost_commit(MemoryListener *listener) memory_region_unref(old_sections[n_old_sections].mr); } g_free(old_sections); - return; } /* Adds the section data to the tmp_section structure. diff --git a/hw/virtio/virtio-nsm.c b/hw/virtio/virtio-nsm.c index b22aa74e34..accf7334e3 100644 --- a/hw/virtio/virtio-nsm.c +++ b/hw/virtio/virtio-nsm.c @@ -1609,7 +1609,6 @@ static void handle_input(VirtIODevice *vdev, VirtQueue *vq) if (in_elem) { virtqueue_detach_element(vq, in_elem, 0); } - return; } static uint64_t get_features(VirtIODevice *vdev, uint64_t f, Error **errp) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 85110bce37..ffc12635ae 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -3648,7 +3648,6 @@ static void virtio_queue_packed_restore_last_avail_idx(VirtIODevice *vdev, int n) { /* We don't have a reference like avail idx in shared memory */ - return; } static void virtio_queue_split_restore_last_avail_idx(VirtIODevice *vdev, @@ -3673,7 +3672,6 @@ void virtio_queue_restore_last_avail_idx(VirtIODevice *vdev, int n) static void virtio_queue_packed_update_used_idx(VirtIODevice *vdev, int n) { /* used idx was updated through set_last_avail_idx() */ - return; } static void virtio_queue_split_update_used_idx(VirtIODevice *vdev, int n) diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c index 65ac42a187..1dd21392a9 100644 --- a/hw/watchdog/sbsa_gwdt.c +++ b/hw/watchdog/sbsa_gwdt.c @@ -174,7 +174,6 @@ static void sbsa_gwdt_write(void *opaque, hwaddr offset, uint64_t data, qemu_log_mask(LOG_GUEST_ERROR, "bad address in control frame write :" " 0x%x\n", (int)offset); } - return; } static void wdt_sbsa_gwdt_reset(DeviceState *dev) diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index d94b83c109..a503b2aea7 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -225,7 +225,6 @@ static void aspeed_wdt_write(void *opaque, hwaddr offset, uint64_t data, "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", __func__, offset); } - return; } static const VMStateDescription vmstate_aspeed_wdt = { diff --git a/include/system/os-win32.h b/include/system/os-win32.h index bc623061d8..3aa6cee4c2 100644 --- a/include/system/os-win32.h +++ b/include/system/os-win32.h @@ -130,7 +130,6 @@ static inline int os_mlock(bool on_fault G_GNUC_UNUSED) static inline void os_setup_limits(void) { - return; } #define fsync _commit diff --git a/linux-user/xtensa/signal.c b/linux-user/xtensa/signal.c index 6514b8dd57..ef8b0c3a27 100644 --- a/linux-user/xtensa/signal.c +++ b/linux-user/xtensa/signal.c @@ -241,7 +241,6 @@ void setup_rt_frame(int sig, struct target_sigaction *ka, give_sigsegv: force_sigsegv(sig); - return; } static void restore_sigcontext(CPUXtensaState *env, diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c index ffe75256c9..d0f38b410c 100644 --- a/migration/multifd-nocomp.c +++ b/migration/multifd-nocomp.c @@ -82,7 +82,6 @@ static void multifd_nocomp_send_cleanup(MultiFDSendParams *p, Error **errp) { g_free(p->iov); p->iov = NULL; - return; } static void multifd_ram_prepare_header(MultiFDSendParams *p) diff --git a/migration/qemu-file.c b/migration/qemu-file.c index 1303a5bf58..b6ac190034 100644 --- a/migration/qemu-file.c +++ b/migration/qemu-file.c @@ -561,8 +561,6 @@ void qemu_put_buffer_at(QEMUFile *f, const uint8_t *buf, size_t buflen, } stat64_add(&mig_stats.qemu_file_transferred, buflen); - - return; } diff --git a/migration/ram.c b/migration/ram.c index 424df6d9f1..dc909f5eef 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3963,8 +3963,6 @@ static void parse_ramblock_mapped_ram(QEMUFile *f, RAMBlock *block, /* Skip pages array */ qemu_set_offset(f, block->pages_offset + length, SEEK_SET); - - return; } static int parse_ramblock(QEMUFile *f, RAMBlock *block, ram_addr_t length) diff --git a/net/colo-compare.c b/net/colo-compare.c index 165610bfe2..893beed528 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -1328,8 +1328,6 @@ static void colo_compare_complete(UserCreatable *uc, Error **errp) } QTAILQ_INSERT_TAIL(&net_compares, s, next); qemu_mutex_unlock(&colo_compare_mutex); - - return; } static void colo_flush_packets(void *opaque, void *user_data) diff --git a/qemu-keymap.c b/qemu-keymap.c index 6707067fea..1c081db287 100644 --- a/qemu-keymap.c +++ b/qemu-keymap.c @@ -116,7 +116,6 @@ static void walk_map(struct xkb_keymap *map, xkb_keycode_t code, void *data) if (kshift != kaltgrshift && kaltgr != kaltgrshift) { print_sym(kaltgrshift, qcode, " shift altgr"); } - return; } static void usage(FILE *out) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 749fdf8895..d4482538ec 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -826,8 +826,6 @@ static void get_disk_properties(HANDLE vol_h, GuestDiskAddress *disk, } out_free: g_free(dev_desc); - - return; } static void get_single_disk_info(int disk_number, @@ -891,7 +889,6 @@ static void get_single_disk_info(int disk_number, err_close: CloseHandle(disk_h); - return; } /* VSS provider works with volumes, thus there is no difference if @@ -2117,7 +2114,6 @@ static void ga_get_win_version(RTL_OSVERSIONINFOEXW *info, Error **errp) rtl_get_version_t rtl_get_version = (rtl_get_version_t)fun; rtl_get_version(info); - return; } static char *ga_get_win_name(const OSVERSIONINFOEXW *os_version, bool id) diff --git a/system/dirtylimit.c b/system/dirtylimit.c index 7dedef8dd4..1626fa5617 100644 --- a/system/dirtylimit.c +++ b/system/dirtylimit.c @@ -337,8 +337,6 @@ static void dirtylimit_adjust_throttle(CPUState *cpu) if (!dirtylimit_done(quota, current)) { dirtylimit_set_throttle(cpu, quota, current); } - - return; } void dirtylimit_process(void) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 9244848efe..ad3c4f38a1 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -1147,7 +1147,6 @@ static void do_setp(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc, env->ZF = 1; /* our env->ZF encoding is inverted */ env->CF = 0; env->VF = 0; - return; } void HELPER(setp)(CPUARMState *env, uint32_t syndrome, uint32_t mtedesc) @@ -1547,7 +1546,6 @@ static void do_cpyp(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, env->ZF = 1; /* our env->ZF encoding is inverted */ env->CF = 0; env->VF = 0; - return; } void HELPER(cpyp)(CPUARMState *env, uint32_t syndrome, uint32_t wdesc, diff --git a/target/i386/kvm/vmsr_energy.c b/target/i386/kvm/vmsr_energy.c index f499ec6e8b..d6aad5246b 100644 --- a/target/i386/kvm/vmsr_energy.c +++ b/target/i386/kvm/vmsr_energy.c @@ -284,7 +284,6 @@ void vmsr_read_thread_stat(pid_t pid, } fclose(file); - return; } /* Read QEMU stat task folder to retrieve all QEMU threads ID */ diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index abe210cc4e..1bbf09aca7 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -3640,7 +3640,6 @@ static void gen_multi0F(DisasContext *s, X86DecodedInsn *decode) return; illegal_op: gen_illegal_opcode(s); - return; } #include "decode-new.c.inc" diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 41fb8c5a4e..e44d044ecb 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -549,8 +549,6 @@ static void whpx_set_registers(CPUState *cpu, int level) error_report("WHPX: Failed to set virtual processor context, hr=%08lx", hr); } - - return; } static int whpx_get_tsc(CPUState *cpu) @@ -771,8 +769,6 @@ static void whpx_get_registers(CPUState *cpu) } x86_update_hflags(env); - - return; } static HRESULT CALLBACK whpx_emu_ioport_callback( @@ -1570,8 +1566,6 @@ static void whpx_vcpu_pre_run(CPUState *cpu) " hr=%08lx", hr); } } - - return; } static void whpx_vcpu_post_run(CPUState *cpu) @@ -1595,8 +1589,6 @@ static void whpx_vcpu_post_run(CPUState *cpu) vcpu->interruptable = !vcpu->exit_ctx.VpContext.ExecutionState.InterruptShadow; - - return; } static void whpx_vcpu_process_async_events(CPUState *cpu) @@ -1634,8 +1626,6 @@ static void whpx_vcpu_process_async_events(CPUState *cpu) apic_handle_tpr_access_report(x86_cpu->apic_state, env->eip, env->tpr_access_type); } - - return; } static int whpx_vcpu_run(CPUState *cpu) @@ -2280,7 +2270,6 @@ void whpx_destroy_vcpu(CPUState *cpu) whp_dispatch.WHvDeleteVirtualProcessor(whpx->partition, cpu->cpu_index); whp_dispatch.WHvEmulatorDestroyEmulator(vcpu->emulator); g_free(cpu->accel); - return; } void whpx_vcpu_kick(CPUState *cpu) diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 0bf574830f..a37d3f6a96 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -290,7 +290,6 @@ void HELPER(m68k_movec_to)(CPUM68KState *env, uint32_t reg, uint32_t val) /* Invalid control registers will generate an exception. */ raise_exception_ra(env, EXCP_ILLEGAL, 0); - return; } uint32_t HELPER(m68k_movec_from)(CPUM68KState *env, uint32_t reg) diff --git a/target/mips/tcg/system/mips-semi.c b/target/mips/tcg/system/mips-semi.c index df0c3256d9..e822a42614 100644 --- a/target/mips/tcg/system/mips-semi.c +++ b/target/mips/tcg/system/mips-semi.c @@ -374,5 +374,4 @@ void mips_semihosting(CPUMIPSState *env) error_report("Unknown UHI operation %d", op); abort(); } - return; } diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 992356cb75..80f6c185bc 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -1332,7 +1332,6 @@ int kvmppc_set_interrupt(PowerPCCPU *cpu, int irq, int level) void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) { - return; } MemTxAttrs kvm_arch_post_run(CPUState *cs, struct kvm_run *run) diff --git a/target/ppc/kvm_ppc.h b/target/ppc/kvm_ppc.h index a8768c1dfd..a1d9ce9f9a 100644 --- a/target/ppc/kvm_ppc.h +++ b/target/ppc/kvm_ppc.h @@ -221,7 +221,6 @@ static inline int kvmppc_smt_threads(void) static inline void kvmppc_error_append_smt_possible_hint(Error *const *errp) { - return; } static inline int kvmppc_set_smt_threads(int smt) @@ -259,7 +258,6 @@ static inline target_ulong kvmppc_configure_v3_mmu(PowerPCCPU *cpu, static inline void kvmppc_set_reg_ppc_online(PowerPCCPU *cpu, unsigned int online) { - return; } static inline void kvmppc_set_reg_tb_offset(PowerPCCPU *cpu, int64_t tb_offset) @@ -456,7 +454,6 @@ static inline PowerPCCPUClass *kvm_ppc_get_host_cpu_class(void) static inline void kvmppc_check_papr_resize_hpt(Error **errp) { - return; } static inline int kvmppc_resize_hpt_prepare(PowerPCCPU *cpu, diff --git a/target/ppc/translate.c b/target/ppc/translate.c index a52cbc869a..a9ff5020a2 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -3627,7 +3627,6 @@ static void pmu_count_insns(DisasContext *ctx) #else static void pmu_count_insns(DisasContext *ctx) { - return; } #endif /* #if defined(TARGET_PPC64) */ diff --git a/target/riscv/debug.c b/target/riscv/debug.c index 9db4048523..198d051521 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -551,8 +551,6 @@ static void type2_reg_write(CPURISCVState *env, target_ulong index, default: g_assert_not_reached(); } - - return; } /* type 6 trigger */ @@ -667,8 +665,6 @@ static void type6_reg_write(CPURISCVState *env, target_ulong index, default: g_assert_not_reached(); } - - return; } /* icount trigger type */ @@ -849,8 +845,6 @@ static void itrigger_reg_write(CPURISCVState *env, target_ulong index, default: g_assert_not_reached(); } - - return; } static int itrigger_get_adjust_count(CPURISCVState *env) diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 93a05e43d7..8e0b01dc65 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -578,7 +578,6 @@ static void check_compat_model_failed(Error **errp, error_setg(errp, "%s. Maximum supported model in the current configuration: \'%s\'", msg, max_model->def->name); error_append_hint(errp, "Consider a different accelerator, try \"-accel help\"\n"); - return; } static bool check_compatibility(const S390CPUModel *max_model, diff --git a/target/sh4/translate.c b/target/sh4/translate.c index bcdd558818..57b972e14f 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -1792,7 +1792,6 @@ static void _decode_opc(DisasContext * ctx) gen_helper_raise_fpu_disable(tcg_env); } ctx->base.is_jmp = DISAS_NORETURN; - return; } static void decode_opc(DisasContext * ctx) diff --git a/tests/qtest/ahci-test.c b/tests/qtest/ahci-test.c index 88ac6c66ce..e8aabfc13f 100644 --- a/tests/qtest/ahci-test.c +++ b/tests/qtest/ahci-test.c @@ -1881,7 +1881,6 @@ static void test_io_interface(gconstpointer opaque) sector = offset_sector(opts->offset, opts->address_type, bufsize); test_io_rw_interface(opts->address_type, opts->io_type, bufsize, sector); g_free(opts); - return; } static void create_ahci_io_test(enum IOMode type, enum AddrMode addr, diff --git a/tests/qtest/fuzz/generic_fuzz.c b/tests/qtest/fuzz/generic_fuzz.c index d107a496da..b42473d1ce 100644 --- a/tests/qtest/fuzz/generic_fuzz.c +++ b/tests/qtest/fuzz/generic_fuzz.c @@ -572,7 +572,6 @@ static void op_add_dma_pattern(QTestState *s, pattern p = {a.index, a.stride, len - sizeof(a), data + sizeof(a)}; p.index = a.index % p.len; g_array_append_val(dma_patterns, p); - return; } static void op_clear_dma_patterns(QTestState *s, diff --git a/tests/qtest/libqos/libqos-malloc.c b/tests/qtest/libqos/libqos-malloc.c index d7566972c4..c90f8f03f4 100644 --- a/tests/qtest/libqos/libqos-malloc.c +++ b/tests/qtest/libqos/libqos-malloc.c @@ -342,5 +342,4 @@ void migrate_allocator(QGuestAllocator *src, QTAILQ_INIT(src->free); node = mlist_new(src->start, src->end - src->start); QTAILQ_INSERT_HEAD(src->free, node, MLIST_ENTNAME); - return; } diff --git a/tests/qtest/libqtest.c b/tests/qtest/libqtest.c index fad307d125..358580361d 100644 --- a/tests/qtest/libqtest.c +++ b/tests/qtest/libqtest.c @@ -2022,7 +2022,6 @@ void qtest_client_inproc_recv(void *opaque, const char *str) qts->rx = g_string_new(NULL); } g_string_append(qts->rx, str); - return; } void qtest_qom_set_bool(QTestState *s, const char *path, const char *property, diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c index b9603d46fa..c9de47bb26 100644 --- a/tests/qtest/test-x86-cpuid-compat.c +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -193,7 +193,6 @@ static void add_feature_test(const char *name, const char *cpu, args->bitnr = bitnr; args->expected_value = expected_value; qtest_add_data_func(name, args, test_feature_flag); - return; } static void test_plus_minus_subprocess(void) diff --git a/tests/unit/socket-helpers.c b/tests/unit/socket-helpers.c index f3439cc4e5..37db24f72a 100644 --- a/tests/unit/socket-helpers.c +++ b/tests/unit/socket-helpers.c @@ -170,5 +170,4 @@ void socket_check_afunix_support(bool *has_afunix) if (*has_afunix) { close(fd); } - return; } diff --git a/tests/unit/test-qgraph.c b/tests/unit/test-qgraph.c index 334c76c8e7..ca1d60fc18 100644 --- a/tests/unit/test-qgraph.c +++ b/tests/unit/test-qgraph.c @@ -44,7 +44,6 @@ static void *driverfunct(void *obj, QGuestAllocator *machine, void *arg) static void testfunct(void *obj, void *arg, QGuestAllocator *alloc) { - return; } static void check_interface(const char *interface) diff --git a/ui/input-linux.c b/ui/input-linux.c index 381148ea99..203e264212 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -412,7 +412,6 @@ static void input_linux_complete(UserCreatable *uc, Error **errp) err_close: close(il->fd); - return; } static void input_linux_instance_finalize(Object *obj) diff --git a/ui/vnc.c b/ui/vnc.c index 9241caaad9..9e097dc4b4 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -146,8 +146,6 @@ static void vnc_init_basic_info(SocketAddress *addr, default: abort(); } - - return; } static void vnc_init_basic_info_from_server_addr(QIOChannelSocket *ioc, diff --git a/util/main-loop.c b/util/main-loop.c index acad8c2e6c..979db51a1f 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -212,7 +212,6 @@ static void main_loop_init(EventLoopBase *base, Error **errp) main_loop_update_params(base, errp); mloop = m; - return; } static bool main_loop_can_be_deleted(EventLoopBase *base) diff --git a/util/qht.c b/util/qht.c index 92c6b78759..208c2f4b32 100644 --- a/util/qht.c +++ b/util/qht.c @@ -367,7 +367,6 @@ void qht_map_lock_buckets__no_stale(struct qht *ht, struct qht_map **pmap) qht_map_lock_buckets(map); qht_unlock(ht); *pmap = map; - return; } /* From abb55b1abaaa8521e93d8202dd67b9e70a4ca1a2 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 7 Apr 2025 10:26:43 +0200 Subject: [PATCH 0243/2760] cleanup: Drop pointless label at end of function Signed-off-by: Markus Armbruster Reviewed-by: Richard Henderson Acked-by: Corey Minyard Message-ID: <20250407082643.2310002-4-armbru@redhat.com> --- hw/ipmi/ipmi_bmc_extern.c | 4 +--- hw/ipmi/ipmi_bmc_sim.c | 7 ++----- hw/ipmi/ipmi_bt.c | 7 +++---- hw/ipmi/ipmi_kcs.c | 3 +-- 4 files changed, 7 insertions(+), 14 deletions(-) diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index 22cb9b590b..e563214390 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -213,7 +213,7 @@ static void ipmi_bmc_extern_handle_command(IPMIBmc *b, rsp[2] = err; ibe->waiting_rsp = false; k->handle_rsp(s, msg_id, rsp, 3); - goto out; + return; } addchar(ibe, msg_id); @@ -228,8 +228,6 @@ static void ipmi_bmc_extern_handle_command(IPMIBmc *b, /* Start the transmit */ continue_send(ibe); - - out: } static void handle_hw_op(IPMIBmcExtern *ibe, unsigned char hw_op) diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index c6a85e8cd1..41fe6835a7 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -465,13 +465,12 @@ void ipmi_bmc_gen_event(IPMIBmc *b, uint8_t *evt, bool log) } if (ibs->msg_flags & IPMI_BMC_MSG_FLAG_EVT_BUF_FULL) { - goto out; + return; } memcpy(ibs->evtbuf, evt, 16); ibs->msg_flags |= IPMI_BMC_MSG_FLAG_EVT_BUF_FULL; k->set_atn(s, 1, attn_irq_enabled(ibs)); - out: } static void gen_event(IPMIBmcSim *ibs, unsigned int sens_num, uint8_t deassert, uint8_t evd1, uint8_t evd2, uint8_t evd3) @@ -996,7 +995,7 @@ static void get_msg(IPMIBmcSim *ibs, if (QTAILQ_EMPTY(&ibs->rcvbufs)) { rsp_buffer_set_error(rsp, 0x80); /* Queue empty */ - goto out; + return; } rsp_buffer_push(rsp, 0); /* Channel 0 */ msg = QTAILQ_FIRST(&ibs->rcvbufs); @@ -1011,8 +1010,6 @@ static void get_msg(IPMIBmcSim *ibs, ibs->msg_flags &= ~IPMI_BMC_MSG_FLAG_RCV_MSG_QUEUE; k->set_atn(s, attn_set(ibs), attn_irq_enabled(ibs)); } - -out: } static unsigned char diff --git a/hw/ipmi/ipmi_bt.c b/hw/ipmi/ipmi_bt.c index 5cf12c59a8..e01d02fe52 100644 --- a/hw/ipmi/ipmi_bt.c +++ b/hw/ipmi/ipmi_bt.c @@ -98,14 +98,14 @@ static void ipmi_bt_handle_event(IPMIInterface *ii) IPMIBT *ib = iic->get_backend_data(ii); if (ib->inlen < 4) { - goto out; + return; } /* Note that overruns are handled by handle_command */ if (ib->inmsg[0] != (ib->inlen - 1)) { /* Length mismatch, just ignore. */ IPMI_BT_SET_BBUSY(ib->control_reg, 1); ib->inlen = 0; - goto out; + return; } if ((ib->inmsg[1] == (IPMI_NETFN_APP << 2)) && (ib->inmsg[3] == IPMI_CMD_GET_BT_INTF_CAP)) { @@ -136,7 +136,7 @@ static void ipmi_bt_handle_event(IPMIInterface *ii) IPMI_BT_SET_B2H_IRQ(ib->mask_reg, 1); ipmi_bt_raise_irq(ib); } - goto out; + return; } ib->waiting_seq = ib->inmsg[2]; ib->inmsg[2] = ib->inmsg[1]; @@ -145,7 +145,6 @@ static void ipmi_bt_handle_event(IPMIInterface *ii) bk->handle_command(ib->bmc, ib->inmsg + 2, ib->inlen - 2, sizeof(ib->inmsg), ib->waiting_rsp); } - out: } static void ipmi_bt_handle_rsp(IPMIInterface *ii, uint8_t msg_id, diff --git a/hw/ipmi/ipmi_kcs.c b/hw/ipmi/ipmi_kcs.c index ae549fb864..d5cfe6c068 100644 --- a/hw/ipmi/ipmi_kcs.c +++ b/hw/ipmi/ipmi_kcs.c @@ -168,7 +168,7 @@ static void ipmi_kcs_handle_event(IPMIInterface *ii) ik->outpos = 0; bk->handle_command(ik->bmc, ik->inmsg, ik->inlen, sizeof(ik->inmsg), ik->waiting_rsp); - goto out_noibf; + return; } else if (ik->cmd_reg == IPMI_KCS_WRITE_END_CMD) { ik->cmd_reg = -1; ik->write_end = 1; @@ -197,7 +197,6 @@ static void ipmi_kcs_handle_event(IPMIInterface *ii) ik->cmd_reg = -1; ik->data_in_reg = -1; IPMI_KCS_SET_IBF(ik->status_reg, 0); - out_noibf: } static void ipmi_kcs_handle_rsp(IPMIInterface *ii, uint8_t msg_id, From fdcb7483ae08f5d21d8b5588413c0b0f3e39cda8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Mar 2025 10:16:53 +0100 Subject: [PATCH 0244/2760] target/hexagon: Explode MO_TExx -> MO_TE | MO_xx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract the implicit MO_TE definition in order to replace it in the next commit. Mechanical change using: $ for n in UW UL UQ UO SW SL SQ; do \ sed -i -e "s/MO_TE$n/MO_TE | MO_$n/" \ $(git grep -l MO_TE$n target/hexagon); \ done Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20250312103238.99981-2-philmd@linaro.org> --- target/hexagon/genptr.c | 8 ++++---- target/hexagon/macros.h | 10 +++++----- target/hexagon/translate.c | 6 +++--- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 2c5e15cfcf..561e93c9fd 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -329,14 +329,14 @@ void gen_set_byte_i64(int N, TCGv_i64 result, TCGv src) static inline void gen_load_locked4u(TCGv dest, TCGv vaddr, int mem_index) { - tcg_gen_qemu_ld_tl(dest, vaddr, mem_index, MO_TEUL); + tcg_gen_qemu_ld_tl(dest, vaddr, mem_index, MO_TE | MO_UL); tcg_gen_mov_tl(hex_llsc_addr, vaddr); tcg_gen_mov_tl(hex_llsc_val, dest); } static inline void gen_load_locked8u(TCGv_i64 dest, TCGv vaddr, int mem_index) { - tcg_gen_qemu_ld_i64(dest, vaddr, mem_index, MO_TEUQ); + tcg_gen_qemu_ld_i64(dest, vaddr, mem_index, MO_TE | MO_UQ); tcg_gen_mov_tl(hex_llsc_addr, vaddr); tcg_gen_mov_i64(hex_llsc_val_i64, dest); } @@ -756,7 +756,7 @@ static void gen_load_frame(DisasContext *ctx, TCGv_i64 frame, TCGv EA) { Insn *insn = ctx->insn; /* Needed for CHECK_NOSHUF */ CHECK_NOSHUF(EA, 8); - tcg_gen_qemu_ld_i64(frame, EA, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_ld_i64(frame, EA, ctx->mem_idx, MO_TE | MO_UQ); } #ifndef CONFIG_HEXAGON_IDEF_PARSER @@ -1230,7 +1230,7 @@ static void gen_vreg_load(DisasContext *ctx, intptr_t dstoff, TCGv src, tcg_gen_andi_tl(src, src, ~((int32_t)sizeof(MMVector) - 1)); } for (int i = 0; i < sizeof(MMVector) / 8; i++) { - tcg_gen_qemu_ld_i64(tmp, src, ctx->mem_idx, MO_TEUQ); + tcg_gen_qemu_ld_i64(tmp, src, ctx->mem_idx, MO_TE | MO_UQ); tcg_gen_addi_tl(src, src, 8); tcg_gen_st_i64(tmp, tcg_env, dstoff + i * 8); } diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index ee3d4c88e7..57825efa55 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -115,27 +115,27 @@ #define MEM_LOAD2s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 2); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TESW); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TE | MO_SW); \ } while (0) #define MEM_LOAD2u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 2); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TEUW); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TE | MO_UW); \ } while (0) #define MEM_LOAD4s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 4); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TESL); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TE | MO_SL); \ } while (0) #define MEM_LOAD4u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 4); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TEUL); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TE | MO_UL); \ } while (0) #define MEM_LOAD8u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 8); \ - tcg_gen_qemu_ld_i64(DST, VA, ctx->mem_idx, MO_TEUQ); \ + tcg_gen_qemu_ld_i64(DST, VA, ctx->mem_idx, MO_TE | MO_UQ); \ } while (0) #define MEM_STORE1_FUNC(X) \ diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index dd26801e64..0109f31e19 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -656,17 +656,17 @@ void process_store(DisasContext *ctx, int slot_num) case 2: tcg_gen_qemu_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_TEUW); + ctx->mem_idx, MO_TE | MO_UW); break; case 4: tcg_gen_qemu_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_TEUL); + ctx->mem_idx, MO_TE | MO_UL); break; case 8: tcg_gen_qemu_st_i64(hex_store_val64[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_TEUQ); + ctx->mem_idx, MO_TE | MO_UQ); break; default: { From beb38fda0f007b034ea7ff624b9f1db7699aa883 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Mar 2025 10:42:31 +0100 Subject: [PATCH 0245/2760] target/hexagon: Replace MO_TE -> MO_LE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We only build the Hexagon target using little endianness order. The MO_TE definition always expands to MO_LE. Use the latter to simplify. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Anton Johansson Message-Id: <20250312103238.99981-3-philmd@linaro.org> --- target/hexagon/genptr.c | 8 ++++---- target/hexagon/idef-parser/parser-helpers.c | 2 +- target/hexagon/macros.h | 10 +++++----- target/hexagon/translate.c | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/target/hexagon/genptr.c b/target/hexagon/genptr.c index 561e93c9fd..08fc5413de 100644 --- a/target/hexagon/genptr.c +++ b/target/hexagon/genptr.c @@ -329,14 +329,14 @@ void gen_set_byte_i64(int N, TCGv_i64 result, TCGv src) static inline void gen_load_locked4u(TCGv dest, TCGv vaddr, int mem_index) { - tcg_gen_qemu_ld_tl(dest, vaddr, mem_index, MO_TE | MO_UL); + tcg_gen_qemu_ld_tl(dest, vaddr, mem_index, MO_LE | MO_UL); tcg_gen_mov_tl(hex_llsc_addr, vaddr); tcg_gen_mov_tl(hex_llsc_val, dest); } static inline void gen_load_locked8u(TCGv_i64 dest, TCGv vaddr, int mem_index) { - tcg_gen_qemu_ld_i64(dest, vaddr, mem_index, MO_TE | MO_UQ); + tcg_gen_qemu_ld_i64(dest, vaddr, mem_index, MO_LE | MO_UQ); tcg_gen_mov_tl(hex_llsc_addr, vaddr); tcg_gen_mov_i64(hex_llsc_val_i64, dest); } @@ -756,7 +756,7 @@ static void gen_load_frame(DisasContext *ctx, TCGv_i64 frame, TCGv EA) { Insn *insn = ctx->insn; /* Needed for CHECK_NOSHUF */ CHECK_NOSHUF(EA, 8); - tcg_gen_qemu_ld_i64(frame, EA, ctx->mem_idx, MO_TE | MO_UQ); + tcg_gen_qemu_ld_i64(frame, EA, ctx->mem_idx, MO_LE | MO_UQ); } #ifndef CONFIG_HEXAGON_IDEF_PARSER @@ -1230,7 +1230,7 @@ static void gen_vreg_load(DisasContext *ctx, intptr_t dstoff, TCGv src, tcg_gen_andi_tl(src, src, ~((int32_t)sizeof(MMVector) - 1)); } for (int i = 0; i < sizeof(MMVector) / 8; i++) { - tcg_gen_qemu_ld_i64(tmp, src, ctx->mem_idx, MO_TE | MO_UQ); + tcg_gen_qemu_ld_i64(tmp, src, ctx->mem_idx, MO_LE | MO_UQ); tcg_gen_addi_tl(src, src, 8); tcg_gen_st_i64(tmp, tcg_env, dstoff + i * 8); } diff --git a/target/hexagon/idef-parser/parser-helpers.c b/target/hexagon/idef-parser/parser-helpers.c index a7dcd85fe4..542af8d0a6 100644 --- a/target/hexagon/idef-parser/parser-helpers.c +++ b/target/hexagon/idef-parser/parser-helpers.c @@ -1761,7 +1761,7 @@ void gen_load(Context *c, YYLTYPE *locp, HexValue *width, if (signedness == SIGNED) { OUT(c, locp, " | MO_SIGN"); } - OUT(c, locp, " | MO_TE);\n"); + OUT(c, locp, " | MO_LE);\n"); } void gen_store(Context *c, YYLTYPE *locp, HexValue *width, HexValue *ea, diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index 57825efa55..e5eb31e671 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -115,27 +115,27 @@ #define MEM_LOAD2s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 2); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TE | MO_SW); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_SW); \ } while (0) #define MEM_LOAD2u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 2); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TE | MO_UW); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_UW); \ } while (0) #define MEM_LOAD4s(DST, VA) \ do { \ CHECK_NOSHUF(VA, 4); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TE | MO_SL); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_SL); \ } while (0) #define MEM_LOAD4u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 4); \ - tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_TE | MO_UL); \ + tcg_gen_qemu_ld_tl(DST, VA, ctx->mem_idx, MO_LE | MO_UL); \ } while (0) #define MEM_LOAD8u(DST, VA) \ do { \ CHECK_NOSHUF(VA, 8); \ - tcg_gen_qemu_ld_i64(DST, VA, ctx->mem_idx, MO_TE | MO_UQ); \ + tcg_gen_qemu_ld_i64(DST, VA, ctx->mem_idx, MO_LE | MO_UQ); \ } while (0) #define MEM_STORE1_FUNC(X) \ diff --git a/target/hexagon/translate.c b/target/hexagon/translate.c index 0109f31e19..02fd40c160 100644 --- a/target/hexagon/translate.c +++ b/target/hexagon/translate.c @@ -656,17 +656,17 @@ void process_store(DisasContext *ctx, int slot_num) case 2: tcg_gen_qemu_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_TE | MO_UW); + ctx->mem_idx, MO_LE | MO_UW); break; case 4: tcg_gen_qemu_st_tl(hex_store_val32[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_TE | MO_UL); + ctx->mem_idx, MO_LE | MO_UL); break; case 8: tcg_gen_qemu_st_i64(hex_store_val64[slot_num], hex_store_addr[slot_num], - ctx->mem_idx, MO_TE | MO_UQ); + ctx->mem_idx, MO_LE | MO_UQ); break; default: { From 9ce6caa0d94a52a0ada77f494e8e23e62198aef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 12 Mar 2025 10:18:42 +0100 Subject: [PATCH 0246/2760] target/i386: Replace MO_TE* -> MO_LE* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The x86 architecture is only implemented as little-endian. The MO_TE definition always expands to MO_LE. Replace: - MO_TEUQ -> MO_LEUQ - MO_TE -> MO_LE Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250312142124.15138-1-philmd@linaro.org> --- target/i386/tcg/emit.c.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index 4e09e96fc1..ca6bc2ea82 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -1813,7 +1813,7 @@ static void gen_CMPXCHG(DisasContext *s, X86DecodedInsn *decode) static void gen_CMPXCHG16B(DisasContext *s, X86DecodedInsn *decode) { #ifdef TARGET_X86_64 - MemOp mop = MO_TE | MO_128 | MO_ALIGN; + MemOp mop = MO_LE | MO_128 | MO_ALIGN; TCGv_i64 t0, t1; TCGv_i128 cmp, val; @@ -1870,10 +1870,10 @@ static void gen_CMPXCHG8B(DisasContext *s, X86DecodedInsn *decode) /* Only require atomic with LOCK; non-parallel handled in generator. */ if (s->prefix & PREFIX_LOCK) { - tcg_gen_atomic_cmpxchg_i64(old, s->A0, cmp, val, s->mem_index, MO_TEUQ); + tcg_gen_atomic_cmpxchg_i64(old, s->A0, cmp, val, s->mem_index, MO_LEUQ); } else { tcg_gen_nonatomic_cmpxchg_i64(old, s->A0, cmp, val, - s->mem_index, MO_TEUQ); + s->mem_index, MO_LEUQ); } /* Compute the required value of Z. */ From 5dbe25e9db070ea87c173fd9a4dc0932ee6d1b41 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Thu, 13 Mar 2025 23:03:39 +0800 Subject: [PATCH 0247/2760] vfio/igd: Update IGD passthrough documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A previous change made the OpRegion and LPC quirks independent of the existing legacy mode, update the documentation accordingly. More related topics, like creating EFI Option ROM of IGD for OVMF, how to solve the VFIO_DMA_MAP Invalid Argument warning, as well as details on IGD memory internals, are also added. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250313150339.358621-1-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- docs/igd-assign.txt | 265 ++++++++++++++++++++++++++++++++------------ 1 file changed, 196 insertions(+), 69 deletions(-) diff --git a/docs/igd-assign.txt b/docs/igd-assign.txt index e17bb50789..3aed7956d5 100644 --- a/docs/igd-assign.txt +++ b/docs/igd-assign.txt @@ -1,44 +1,69 @@ Intel Graphics Device (IGD) assignment with vfio-pci ==================================================== -IGD has two different modes for assignment using vfio-pci: - -1) Universal Pass-Through (UPT) mode: - - In this mode the IGD device is added as a *secondary* (ie. non-primary) - graphics device in combination with an emulated primary graphics device. - This mode *requires* guest driver support to remove the external - dependencies generally associated with IGD (see below). Those guest - drivers only support this mode for Broadwell and newer IGD, according to - Intel. Additionally, this mode by default, and as officially supported - by Intel, does not support direct video output. The intention is to use - this mode either to provide hardware acceleration to the emulated graphics - or to use this mode in combination with guest-based remote access software, - for example VNC (see below for optional output support). This mode - theoretically has no device specific handling dependencies on vfio-pci or - the VM firmware. - -2) "Legacy" mode: - - In this mode the IGD device is intended to be the primary and exclusive - graphics device in the VM[1], as such QEMU does not facilitate any sort - of remote graphics to the VM in this mode. A connected physical monitor - is the intended output device for IGD. This mode includes several - requirements and restrictions: - - * IGD must be given address 02.0 on the PCI root bus in the VM - * The host kernel must support vfio extensions for IGD (v4.6) - * vfio VGA support very likely needs to be enabled in the host kernel - * The VM firmware must support specific fw_cfg enablers for IGD - * The VM machine type must support a PCI host bridge at 00.0 (standard) - * The VM machine type must provide or allow to be created a special - ISA/LPC bridge device (vfio-pci-igd-lpc-bridge) on the root bus at - PCI address 1f.0. - * The IGD device must have a VGA ROM, either provided via the romfile - option or loaded automatically through vfio (standard). rombar=0 - will disable legacy mode support. - * Hotplug of the IGD device is not supported. - * The IGD device must be a SandyBridge or newer model device. +Using vfio-pci, we can passthrough Intel Graphics Device (IGD) to guest, either +serve as primary and exclusive graphics adapter, or used in combination with an +emulated primary graphics device, depending on the config and guest driver +support. However, IGD devices are not "clean" PCI devices, they use extra +memory regions other than BARs. Special handling is required to make them work +properly, including: + +* OpRegion for accessing Virtual BIOS Table (VBT) that contains display output + information. +* Data Stolen Memory (DSM) region used as VRAM at early stage (BIOS/UEFI) + +Certain guest software also depends on following conditions to work: +(*-Required by) + +| Condition | Linux | Windows | VBIOS | EFI GOP | +|---------------------------------------------|-------|---------|-------|---------| +| #1 IGD has a valid OpRegion containing VBT | * ^1 | * | * | * | +| #2 VID/DID of LPC bridge at 00:1f.0 matches | | | * | * | +| #3 IGD is assigned to BDF 00:02.0 | | | * | * | +| #4 IGD has VGA controller device class | | | * | * | +| #5 Host's VGA ranges are mapped to IGD | | | * | | +| #6 Guest has valid VBIOS or UEFI Option ROM | | | * | * | + +^1 Though i915 driver is able to mock a OpRegion, it is still recommended to + use the VBT copied from host OpRegion to prevent incorrect configuration. + +For #1, the "x-igd-opregion=on" option exposes a copy of host IGD OpRegion to +guest via fw_cfg, where guest firmware can set up guest OpRegion with it. + +For #2, "x-igd-lpc=on" option copies the IDs of host LPC bridge and host bridge +to guest. Currently this is only supported on i440fx machines as there is +already an ICH9 LPC bridge present on q35 machines, overwriting its IDs may +lead to unexpected behavior. + +For #3, "addr=2.0" assigns IGD to 00:02.0. + +For #4, the primary display must be set to IGD in host BIOS. + +For #5, "x-vga=on" enables guest access to standard VGA IO/MMIO ranges. + +For #6, ROM either provided via the ROM BAR or romfile= option is needed, this +Intel document [1] shows how to dump VBIOS to file. For UEFI Option ROM, see +"Guest firmware" section. + +QEMU also provides a "Legacy" mode that implicitly enables full functionality +on IGD, it is automatically enabled when +* Machine type is i440fx +* IGD is assigned to guest BDF 00:02.0 +* ROM BAR or romfile is present + +In "Legacy" mode, QEMU will automatically setup OpRegion, LPC bridge IDs and +VGA range access, which is equivalent to: + x-igd-opregion=on,x-igd-lpc=on,x-vga=on + +By default, "Legacy" mode won't fail, it continues on error. User can set +"x-igd-legacy-mode=on" to force enabling legacy mode, this also checks if the +conditions above for legacy mode is met, and if any error occurs, QEMU will +fail immediately. Users can also set "x-igd-legacy-mode=off" to disable legacy +mode. + +In legacy mode, as the guest VGA ranges are assigned to IGD device, all other +graphics devices should be removed, this can be done using "-nographic" or +"-vga none" or "-nodefaults", along with adding the device using vfio-pci. For either mode, depending on the host kernel, the i915 driver in the host may generate faults and errors upon re-binding to an IGD device after it @@ -73,31 +98,39 @@ DVI, or DisplayPort) may be unsupported in some use cases. In the author's experience, even DP to VGA adapters can be troublesome while adapters between digital formats work well. -Usage -===== -The intention is for IGD assignment to be transparent for users and thus for -management tools like libvirt. To make use of legacy mode, simply remove all -other graphics options and use "-nographic" and either "-vga none" or -"-nodefaults", along with adding the device using vfio-pci: - -device vfio-pci,host=00:02.0,id=hostdev0,bus=pci.0,addr=0x2 +Options +======= +* x-igd-opregion=[on|*off*] + Copy host IGD OpRegion and expose it to guest with fw_cfg + +* x-igd-lpc=[on|*off*] + Creates a dummy LPC bridge at 00:1f:0 with host VID/DID (i440fx only) + +* x-igd-legacy-mode=[on|off|*auto*] + Enable/Disable legacy mode + +* x-igd-gms=[hex, default 0] + Overriding DSM region size in GGC register, 0 means uses host value. + Use this only when the DSM size cannot be changed through the + 'DVMT Pre-Allocated' option in host BIOS. -For UPT mode, retain the default emulated graphics and simply add the vfio-pci -device making use of any other bus address other than 02.0. libvirt will -default to assigning the device a UPT compatible address while legacy mode -users will need to manually edit the XML if using a tool like virt-manager -where the VM device address is not expressly specified. -An experimental vfio-pci option also exists to enable OpRegion, and thus -external monitor support, for UPT mode. This can be enabled by adding -"x-igd-opregion=on" to the vfio-pci device options for the IGD device. As -with legacy mode, this requires the host to support features introduced in -the v4.6 kernel. If Intel chooses to embrace this support, the option may -be made non-experimental in the future, opening it to libvirt support. +Examples +======== +* Adding IGD with automatically legacy mode support + -device vfio-pci,host=00:02.0,id=hostdev0,addr=2.0 -Developer ABI -============= -Legacy mode IGD support imposes two fw_cfg requirements on the VM firmware: +* Adding IGD with OpRegion and LPC ID hack, but without VGA ranges + (For UEFI guests) + -device vfio-pci,host=00:02.0,id=hostdev0,addr=2.0,x-igd-legacy-mode=off,x-igd-opregion=on,x-igd-lpc=on,romfile=efi_oprom.rom + + +Guest firmware +============== +Guest firmware is responsible for setting up OpRegion and Base of Data Stolen +Memory (BDSM) in guest address space. IGD passthrough support imposes two +fw_cfg requirements on the VM firmware: 1) "etc/igd-opregion" @@ -117,17 +150,111 @@ Legacy mode IGD support imposes two fw_cfg requirements on the VM firmware: Firmware must allocate a reserved memory below 4GB with required 1MB alignment equal to this size. Additionally the base address of this reserved region must be written to the dword BDSM register in PCI config - space of the IGD device at offset 0x5C. As this support is related to - running the IGD ROM, which has other dependencies on the device appearing - at guest address 00:02.0, it's expected that this fw_cfg file is only - relevant to a single PCI class VGA device with Intel vendor ID, appearing - at PCI bus address 00:02.0. + space of the IGD device at offset 0x5C (or 0xC0 for Gen 11+ devices using + 64-bit BDSM). As this support is related to running the IGD ROM, which + has other dependencies on the device appearing at guest address 00:02.0, + it's expected that this fw_cfg file is only relevant to a single PCI + class VGA device with Intel vendor ID, appearing at PCI bus address 00:02.0. + +Upstream Seabios has OpRegion and BDSM (pre-Gen11 device only) support. +However, the support is not accepted by upstream EDK2/OVMF. A recommended +solution is to create a virtual OpRom with following DXE drivers: + +* IgdAssignmentDxe: Set up OpRegion and BDSM according to fw_cfg (must) +* IntelGopDriver: Closed-source Intel GOP driver +* PlatformGopPolicy: Protocol required by IntelGopDriver + +IntelGopDriver and PlatformGopPolicy is only required when enabling GOP on IGD. + +The original IgdAssignmentDxe can be found at [3]. A Intel maintained version +with PlatformGopPolicy for industrial computing is at [4]. There is also an +unofficially maintained version with newer Gen11+ device support at [5]. +You need to build them with EDK2. + +For the IntelGopDriver, Intel never released it to public. You may contact +Intel support to get one as [4] said, if you are an Intel Premier Support +customer, or you can try extracting it from your host firmware using +"UEFI BIOS Updater"[6]. + +Once you got all the required DXE drivers, a Option ROM can be generated with +EfiRom utility in EDK2, using + EfiRom -f 0x8086 -i -o output.rom \ + -e IgdAssignmentDxe.efi PlatformGOPPolicy.efi IntelGopDriver.efi + + +Known issues +============ +When using OVMF as guest firmware, you may encounter the following warning: +warning: vfio_container_dma_map(0x55fab36ce610, 0x380010000000, 0x108000, 0x7fd336000000) = -22 (Invalid argument) + +Solution: +Set the host physical address bits to IOMMU address width using + -cpu host,host-phys-bits-limit= +Or in libvirt XML with + + + +The IOMMU address width can be determined with + echo $(( ((0x$(cat /sys/devices/virtual/iommu/dmar0/intel-iommu/cap) & 0x3F0000) >> 16) + 1 )) +Refer https://edk2.groups.io/g/devel/topic/patch_v1/102359124 for more details + + +Memory View +=========== +IGD has it own address space. To use system RAM as VRAM, a single-level page +table named Global Graphics Translation Table (GTT) is used for the address +translation. Each page table entry points a 4KB page. Illustration below shows +the translation flow on IGD with 64-bit GTT PTEs. + +(PTE_SIZE == 8) +-------------+---+ + | Address | V | V: Valid Bit + +-------------+---+ + | ... | | +IGD:0x01ae9010 0xd740| 0x70ffc000 | 1 | Mem:0x42ba3e010^ +-----------------------> 0xd748| 0x42ba3e000 | 1 +------------------> +(addr >> 12) * PTE_SIZE 0xd750| 0x42ba3f000 | 1 | + | ... | | + +-------------+---+ +^ The address may be remapped by IOMMU + +The memory region store GTT is called GTT Stolen Memory (GSM) it is located +right below the Data Stolen Memory (DSM). Accessing this region directly is +not allowed, any access will immediately freeze the whole system. The only way +to access it is through the second half of MMIO BAR0. + +The Data Stolen Memory is reserved by firmware, and acts as the VRAM in pre-OS +environments. In QEMU, guest firmware (Seabios/OVMF) is responsible for +reserving a continuous region and program its base address to BDSM register, +then let VBIOS/GOP driver initializing this region. Illustration below shows +how DSM is mapped. + + IGD Addr Space Host Addr Space Guest Addr Space + +-------------+ +-------------+ +-------------+ + | | | | | | + | | | | | | + | | +-------------+ +-------------+ + | | | Data Stolen | | Data Stolen | + | | | (Guest) | | (Guest) | + | | +------------>+-------------+<------->+-------------+<--Guest BDSM + | | | Passthrough | | EPT | | Emulated by QEMU +DSMSIZE+-------------+ | with IOMMU | | Mapping | | Programmed by guest FW + | | | | | | | + | | | | | | | + 0+-------------+--+ | | | | + | +-------------+ | | + | | Data Stolen | +-------------+ + | | (Host) | + +------------>+-------------+<--Host BDSM + Non- | | "real" one in HW + Passthrough | | Programmed by host FW + +-------------+ Footnotes ========= -[1] Nothing precludes adding additional emulated or assigned graphics devices - as non-primary, other than the combination typically not working. I only - intend to set user expectations, others are welcome to find working - combinations or fix whatever issues prevent this from working in the common - case. +[1] https://www.intel.com/content/www/us/en/docs/graphics-for-linux/developer-reference/1-0/dump-video-bios.html [2] # echo "vfio-pci" > /sys/bus/pci/devices/0000:00:02.0/driver_override +[3] https://web.archive.org/web/20240827012422/https://bugzilla.tianocore.org/show_bug.cgi?id=935 + Tianocore bugzilla was down since Jan 2025 :( +[4] https://eci.intel.com/docs/3.3/components/kvm-hypervisor.html, Patch 0001-0004 +[5] https://github.com/tomitamoeko/VfioIgdPkg +[6] https://winraid.level1techs.com/t/tool-guide-news-uefi-bios-updater-ubu/30357 From 10c7f1cf2c9d5af28dbc342634eb1f3979a7c379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 24 Mar 2025 13:33:15 +0100 Subject: [PATCH 0248/2760] vfio: Open code vfio_migration_set_error() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VFIO uses migration_file_set_error() in a couple of places where an 'Error **' parameter is not provided. In MemoryListener handlers : vfio_listener_region_add vfio_listener_log_global_stop vfio_listener_log_sync and in callback routines for IOMMU notifiers : vfio_iommu_map_notify vfio_iommu_map_dirty_notify Hopefully, one day, we will be able to extend these callbacks with an 'Error **' parameter and avoid setting the global migration error. Until then, it seems sensible to clearly identify the use cases, which are limited, and open code vfio_migration_set_error(). One other benefit is an improved error reporting when migration is running. While at it, slightly modify error reporting to only report errors when migration is not active and not always as is currently done. Cc: Prasad Pandit Reviewed-by: Avihai Horon Link: https://lore.kernel.org/qemu-devel/20250324123315.637827-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 60 +++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 9b493451c5..5ef14931cd 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -145,13 +145,6 @@ bool vfio_viommu_preset(VFIODevice *vbasedev) return vbasedev->bcontainer->space->as != &address_space_memory; } -static void vfio_set_migration_error(int ret) -{ - if (migration_is_running()) { - migration_file_set_error(ret, NULL); - } -} - bool vfio_device_state_is_running(VFIODevice *vbasedev) { VFIOMigration *migration = vbasedev->migration; @@ -287,9 +280,14 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) iova, iova + iotlb->addr_mask); if (iotlb->target_as != &address_space_memory) { - error_report("Wrong target AS \"%s\", only system memory is allowed", - iotlb->target_as->name ? iotlb->target_as->name : "none"); - vfio_set_migration_error(-EINVAL); + error_setg(&local_err, + "Wrong target AS \"%s\", only system memory is allowed", + iotlb->target_as->name ? iotlb->target_as->name : "none"); + if (migration_is_running()) { + migration_file_set_error(-EINVAL, local_err); + } else { + error_report_err(local_err); + } return; } @@ -322,11 +320,16 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) ret = vfio_container_dma_unmap(bcontainer, iova, iotlb->addr_mask + 1, iotlb); if (ret) { - error_report("vfio_container_dma_unmap(%p, 0x%"HWADDR_PRIx", " - "0x%"HWADDR_PRIx") = %d (%s)", - bcontainer, iova, - iotlb->addr_mask + 1, ret, strerror(-ret)); - vfio_set_migration_error(ret); + error_setg(&local_err, + "vfio_container_dma_unmap(%p, 0x%"HWADDR_PRIx", " + "0x%"HWADDR_PRIx") = %d (%s)", + bcontainer, iova, + iotlb->addr_mask + 1, ret, strerror(-ret)); + if (migration_is_running()) { + migration_file_set_error(ret, local_err); + } else { + error_report_err(local_err); + } } } out: @@ -1108,8 +1111,11 @@ static void vfio_listener_log_global_stop(MemoryListener *listener) if (ret) { error_prepend(&local_err, "vfio: Could not stop dirty page tracking - "); - error_report_err(local_err); - vfio_set_migration_error(ret); + if (migration_is_running()) { + migration_file_set_error(ret, local_err); + } else { + error_report_err(local_err); + } } } @@ -1225,14 +1231,14 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) trace_vfio_iommu_map_dirty_notify(iova, iova + iotlb->addr_mask); if (iotlb->target_as != &address_space_memory) { - error_report("Wrong target AS \"%s\", only system memory is allowed", - iotlb->target_as->name ? iotlb->target_as->name : "none"); + error_setg(&local_err, + "Wrong target AS \"%s\", only system memory is allowed", + iotlb->target_as->name ? iotlb->target_as->name : "none"); goto out; } rcu_read_lock(); if (!vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL, &local_err)) { - error_report_err(local_err); goto out_unlock; } @@ -1243,7 +1249,6 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) "vfio_iommu_map_dirty_notify(%p, 0x%"HWADDR_PRIx", " "0x%"HWADDR_PRIx") failed - ", bcontainer, iova, iotlb->addr_mask + 1); - error_report_err(local_err); } out_unlock: @@ -1251,7 +1256,11 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) out: if (ret) { - vfio_set_migration_error(ret); + if (migration_is_running()) { + migration_file_set_error(ret, local_err); + } else { + error_report_err(local_err); + } } } @@ -1384,8 +1393,11 @@ static void vfio_listener_log_sync(MemoryListener *listener, if (vfio_log_sync_needed(bcontainer)) { ret = vfio_sync_dirty_bitmap(bcontainer, section, &local_err); if (ret) { - error_report_err(local_err); - vfio_set_migration_error(ret); + if (migration_is_running()) { + migration_file_set_error(ret, local_err); + } else { + error_report_err(local_err); + } } } } From 6a7abe1c96bf5fbaa546c710b147f594c9db562e Mon Sep 17 00:00:00 2001 From: Amit Machhiwal Date: Tue, 8 Apr 2025 18:10:41 +0530 Subject: [PATCH 0249/2760] vfio/spapr: Enhance error handling in vfio_spapr_create_window() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce an Error ** parameter to vfio_spapr_create_window() to enable structured error reporting. This allows the function to propagate detailed errors back to callers. Suggested-by: Cédric Le Goater Signed-off-by: Amit Machhiwal Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250408124042.2695955-2-amachhiw@linux.ibm.com Signed-off-by: Cédric Le Goater --- hw/vfio/spapr.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 66a2d2bb0d..3d6354134c 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -230,9 +230,9 @@ static int vfio_spapr_remove_window(VFIOContainer *container, return 0; } -static int vfio_spapr_create_window(VFIOContainer *container, +static bool vfio_spapr_create_window(VFIOContainer *container, MemoryRegionSection *section, - hwaddr *pgsize) + hwaddr *pgsize, Error **errp) { int ret = 0; VFIOContainerBase *bcontainer = &container->bcontainer; @@ -252,11 +252,11 @@ static int vfio_spapr_create_window(VFIOContainer *container, pgmask = bcontainer->pgsizes & (pagesize | (pagesize - 1)); pagesize = pgmask ? (1ULL << (63 - clz64(pgmask))) : 0; if (!pagesize) { - error_report("Host doesn't support page size 0x%"PRIx64 - ", the supported mask is 0x%lx", - memory_region_iommu_get_min_page_size(iommu_mr), - bcontainer->pgsizes); - return -EINVAL; + error_setg_errno(errp, EINVAL, "Host doesn't support page size 0x%"PRIx64 + ", the supported mask is 0x%lx", + memory_region_iommu_get_min_page_size(iommu_mr), + bcontainer->pgsizes); + return false; } /* @@ -302,17 +302,17 @@ static int vfio_spapr_create_window(VFIOContainer *container, } } if (ret) { - error_report("Failed to create a window, ret = %d (%m)", ret); - return -errno; + error_setg_errno(errp, errno, "Failed to create a window, ret = %d", ret); + return false; } if (create.start_addr != section->offset_within_address_space) { vfio_spapr_remove_window(container, create.start_addr); - error_report("Host doesn't support DMA window at %"HWADDR_PRIx", must be %"PRIx64, - section->offset_within_address_space, - (uint64_t)create.start_addr); - return -EINVAL; + error_setg_errno(errp, EINVAL, "Host doesn't support DMA window at %"HWADDR_PRIx + ", must be %"PRIx64, section->offset_within_address_space, + (uint64_t)create.start_addr); + return false; } trace_vfio_spapr_create_window(create.page_shift, create.levels, @@ -320,7 +320,7 @@ static int vfio_spapr_create_window(VFIOContainer *container, create.start_addr); *pgsize = pagesize; - return 0; + return true; } static bool @@ -377,9 +377,8 @@ vfio_spapr_container_add_section_window(VFIOContainerBase *bcontainer, } } - ret = vfio_spapr_create_window(container, section, &pgsize); - if (ret) { - error_setg_errno(errp, -ret, "Failed to create SPAPR window"); + ret = vfio_spapr_create_window(container, section, &pgsize, errp); + if (!ret) { return false; } From d5e8e6195bcd37292657b5f69d7633b51670caf1 Mon Sep 17 00:00:00 2001 From: Amit Machhiwal Date: Tue, 8 Apr 2025 18:10:42 +0530 Subject: [PATCH 0250/2760] vfio/spapr: Fix L2 crash with PCI device passthrough and memory > 128G MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit An L2 KVM guest fails to boot inside a pSeries LPAR when booted with a memory more than 128 GB and PCI device passthrough. The L2 guest also crashes when it is booted with a memory greater than 128 GB and a PCI device is hotplugged later. The issue arises from a conditional check for `levels > 1` in `spapr_tce_create_table()` within L1 KVM. This check is meant to prevent multi-level TCEs, which are not supported by the PowerVM hypervisor. As a result, when QEMU makes a `VFIO_IOMMU_SPAPR_TCE_CREATE` ioctl call with `levels > 1`, it triggers the conditional check and returns `EINVAL`, causing the guest to crash with the following errors: 2025-03-04T06:36:36.133117Z qemu-system-ppc64: Failed to create a window, ret = -1 (Invalid argument) 2025-03-04T06:36:36.133176Z qemu-system-ppc64: Failed to create SPAPR window: Invalid argument qemu: hardware error: vfio: DMA mapping failed, unable to continue Fix this by checking the supported DDW "levels" returned by the VFIO_IOMMU_SPAPR_TCE_GET_INFO ioctl before attempting the TCE create ioctl in KVM. The patch has been tested on KVM guests with memory configurations of up to 390GB, and 450GB on PowerVM and bare-metal environments respectively. Signed-off-by: Amit Machhiwal Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250408124042.2695955-3-amachhiw@linux.ibm.com Signed-off-by: Cédric Le Goater --- hw/vfio/spapr.c | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 3d6354134c..7e5cb95f6a 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -26,6 +26,7 @@ typedef struct VFIOSpaprContainer { VFIOContainer container; MemoryListener prereg_listener; QLIST_HEAD(, VFIOHostDMAWindow) hostwin_list; + unsigned int levels; } VFIOSpaprContainer; OBJECT_DECLARE_SIMPLE_TYPE(VFIOSpaprContainer, VFIO_IOMMU_SPAPR); @@ -236,9 +237,11 @@ static bool vfio_spapr_create_window(VFIOContainer *container, { int ret = 0; VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOSpaprContainer *scontainer = container_of(container, VFIOSpaprContainer, + container); IOMMUMemoryRegion *iommu_mr = IOMMU_MEMORY_REGION(section->mr); uint64_t pagesize = memory_region_iommu_get_min_page_size(iommu_mr), pgmask; - unsigned entries, bits_total, bits_per_level, max_levels; + unsigned entries, bits_total, bits_per_level, max_levels, ddw_levels; struct vfio_iommu_spapr_tce_create create = { .argsz = sizeof(create) }; long rampagesize = qemu_minrampagesize(); @@ -291,16 +294,29 @@ static bool vfio_spapr_create_window(VFIOContainer *container, */ bits_per_level = ctz64(qemu_real_host_page_size()) + 8; create.levels = bits_total / bits_per_level; - if (bits_total % bits_per_level) { - ++create.levels; - } - max_levels = (64 - create.page_shift) / ctz64(qemu_real_host_page_size()); - for ( ; create.levels <= max_levels; ++create.levels) { - ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_CREATE, &create); - if (!ret) { - break; + + ddw_levels = scontainer->levels; + if (ddw_levels > 1) { + if (bits_total % bits_per_level) { + ++create.levels; } + max_levels = (64 - create.page_shift) / ctz64(qemu_real_host_page_size()); + for ( ; create.levels <= max_levels; ++create.levels) { + ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_CREATE, &create); + if (!ret) { + break; + } + } + } else { /* ddw_levels == 1 */ + if (create.levels > ddw_levels) { + error_setg_errno(errp, EINVAL, "Host doesn't support multi-level TCE tables" + ". Use larger IO page size. Supported mask is 0x%lx", + bcontainer->pgsizes); + return false; + } + ret = ioctl(container->fd, VFIO_IOMMU_SPAPR_TCE_CREATE, &create); } + if (ret) { error_setg_errno(errp, errno, "Failed to create a window, ret = %d", ret); return false; @@ -501,6 +517,8 @@ static bool vfio_spapr_container_setup(VFIOContainerBase *bcontainer, goto listener_unregister_exit; } + scontainer->levels = info.ddw.levels; + if (v2) { bcontainer->pgsizes = info.ddw.pgsizes; /* From acea0f0fafcf89f374fd7a69a4cd2c91d6d7d68a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:46 +0100 Subject: [PATCH 0251/2760] vfio: Move vfio_mig_active() into migration.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_mig_active() is part of the VFIO migration API. Move the definitions where VFIO migration is implemented. Reviewed-by: Avihai Horon Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-2-clg@redhat.com Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-2-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 16 ---------------- hw/vfio/migration.c | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 5ef14931cd..2ea4e12c90 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -65,22 +65,6 @@ int vfio_kvm_device_fd = -1; * Device state interfaces */ -bool vfio_mig_active(void) -{ - VFIODevice *vbasedev; - - if (QLIST_EMPTY(&vfio_device_list)) { - return false; - } - - QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { - if (vbasedev->migration_blocker) { - return false; - } - } - return true; -} - static Error *multiple_devices_migration_blocker; /* diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index fbff46cfc3..b5fb0d2188 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -1062,6 +1062,22 @@ void vfio_mig_add_bytes_transferred(unsigned long val) qatomic_add(&bytes_transferred, val); } +bool vfio_mig_active(void) +{ + VFIODevice *vbasedev; + + if (QLIST_EMPTY(&vfio_device_list)) { + return false; + } + + QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->migration_blocker) { + return false; + } + } + return true; +} + /* * Return true when either migration initialized or blocker registered. * Currently only return false when adding blocker fails which will From 426ffab477e22606473282c684b9239f8a30dd6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:47 +0100 Subject: [PATCH 0252/2760] vfio: Rename vfio_reset_bytes_transferred() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enforce a 'vfio_mig_' prefix for the VFIO migration API to better reflect the namespace these routines belong to. Reviewed-by: Avihai Horon Reviewed-by: John Levon Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-3-clg@redhat.com Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-3-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/migration.c | 2 +- include/hw/vfio/vfio-common.h | 2 +- migration/target.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index b5fb0d2188..8bf65b8e11 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -1052,7 +1052,7 @@ int64_t vfio_mig_bytes_transferred(void) return MIN(qatomic_read(&bytes_transferred), INT64_MAX); } -void vfio_reset_bytes_transferred(void) +void vfio_mig_reset_bytes_transferred(void) { qatomic_set(&bytes_transferred, 0); } diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index f5b3f45a43..f58cae9e55 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -295,7 +295,7 @@ int vfio_block_multiple_devices_migration(VFIODevice *vbasedev, Error **errp); void vfio_unblock_multiple_devices_migration(void); bool vfio_viommu_preset(VFIODevice *vbasedev); int64_t vfio_mig_bytes_transferred(void); -void vfio_reset_bytes_transferred(void); +void vfio_mig_reset_bytes_transferred(void); void vfio_mig_add_bytes_transferred(unsigned long val); bool vfio_device_state_is_running(VFIODevice *vbasedev); bool vfio_device_state_is_precopy(VFIODevice *vbasedev); diff --git a/migration/target.c b/migration/target.c index a6ffa9a5ce..f5d8cfe7c2 100644 --- a/migration/target.c +++ b/migration/target.c @@ -25,7 +25,7 @@ void migration_populate_vfio_info(MigrationInfo *info) void migration_reset_vfio_bytes_transferred(void) { - vfio_reset_bytes_transferred(); + vfio_mig_reset_bytes_transferred(); } #else void migration_populate_vfio_info(MigrationInfo *info) From e1d4ea53d6e393def88258a3badf8907db33608e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:48 +0100 Subject: [PATCH 0253/2760] vfio: Introduce a new header file for external migration services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The migration core subsystem makes use of the VFIO migration API to collect statistics on the number of bytes transferred. These services are declared in "hw/vfio/vfio-common.h" which also contains VFIO internal declarations. Move the migration declarations into a new header file "hw/vfio/vfio-migration.h" to reduce the exposure of VFIO internals. While at it, use a 'vfio_migration_' prefix for these services. To be noted, vfio_migration_add_bytes_transferred() is a VFIO migration internal service which we will be moved in the subsequent patches. Cc: Kirti Wankhede Cc: Avihai Horon Reviewed-by: Prasad Pandit Reviewed-by: John Levon Reviewed-by: Avihai Horon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-4-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/migration-multifd.c | 5 +++-- hw/vfio/migration.c | 11 ++++++----- include/hw/vfio/vfio-common.h | 5 +---- include/hw/vfio/vfio-migration.h | 16 ++++++++++++++++ migration/target.c | 8 ++++---- 5 files changed, 30 insertions(+), 15 deletions(-) create mode 100644 include/hw/vfio/vfio-migration.h diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index 378f6f3bf0..09aa57f5f8 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-migration.h" #include "migration/misc.h" #include "qapi/error.h" #include "qemu/bswap.h" @@ -575,7 +576,7 @@ vfio_save_complete_precopy_thread_config_state(VFIODevice *vbasedev, return false; } - vfio_mig_add_bytes_transferred(packet_len); + vfio_migration_add_bytes_transferred(packet_len); return true; } @@ -645,7 +646,7 @@ vfio_multifd_save_complete_precopy_thread(SaveLiveCompletePrecopyThreadData *d, goto thread_exit; } - vfio_mig_add_bytes_transferred(packet_size); + vfio_migration_add_bytes_transferred(packet_size); } if (!vfio_save_complete_precopy_thread_config_state(vbasedev, diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 8bf65b8e11..582d65932a 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -17,6 +17,7 @@ #include "system/runstate.h" #include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-migration.h" #include "migration/misc.h" #include "migration/savevm.h" #include "migration/vmstate.h" @@ -373,7 +374,7 @@ static ssize_t vfio_save_block(QEMUFile *f, VFIOMigration *migration) qemu_put_be64(f, VFIO_MIG_FLAG_DEV_DATA_STATE); qemu_put_be64(f, data_size); qemu_put_buffer(f, migration->data_buffer, data_size); - vfio_mig_add_bytes_transferred(data_size); + vfio_migration_add_bytes_transferred(data_size); trace_vfio_save_block(migration->vbasedev->name, data_size); @@ -1047,22 +1048,22 @@ static int vfio_block_migration(VFIODevice *vbasedev, Error *err, Error **errp) /* ---------------------------------------------------------------------- */ -int64_t vfio_mig_bytes_transferred(void) +int64_t vfio_migration_bytes_transferred(void) { return MIN(qatomic_read(&bytes_transferred), INT64_MAX); } -void vfio_mig_reset_bytes_transferred(void) +void vfio_migration_reset_bytes_transferred(void) { qatomic_set(&bytes_transferred, 0); } -void vfio_mig_add_bytes_transferred(unsigned long val) +void vfio_migration_add_bytes_transferred(unsigned long val) { qatomic_add(&bytes_transferred, val); } -bool vfio_mig_active(void) +bool vfio_migration_active(void) { VFIODevice *vbasedev; diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index f58cae9e55..7a551bb230 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -290,13 +290,10 @@ extern VFIODeviceList vfio_device_list; extern const MemoryListener vfio_memory_listener; extern int vfio_kvm_device_fd; -bool vfio_mig_active(void); int vfio_block_multiple_devices_migration(VFIODevice *vbasedev, Error **errp); void vfio_unblock_multiple_devices_migration(void); bool vfio_viommu_preset(VFIODevice *vbasedev); -int64_t vfio_mig_bytes_transferred(void); -void vfio_mig_reset_bytes_transferred(void); -void vfio_mig_add_bytes_transferred(unsigned long val); +void vfio_migration_add_bytes_transferred(unsigned long val); bool vfio_device_state_is_running(VFIODevice *vbasedev); bool vfio_device_state_is_precopy(VFIODevice *vbasedev); diff --git a/include/hw/vfio/vfio-migration.h b/include/hw/vfio/vfio-migration.h new file mode 100644 index 0000000000..0d4ecd33d5 --- /dev/null +++ b/include/hw/vfio/vfio-migration.h @@ -0,0 +1,16 @@ +/* + * VFIO migration interface + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_MIGRATION_H +#define HW_VFIO_VFIO_MIGRATION_H + +bool vfio_migration_active(void); +int64_t vfio_migration_bytes_transferred(void); +void vfio_migration_reset_bytes_transferred(void); + +#endif /* HW_VFIO_VFIO_MIGRATION_H */ diff --git a/migration/target.c b/migration/target.c index f5d8cfe7c2..12fd399f0c 100644 --- a/migration/target.c +++ b/migration/target.c @@ -11,21 +11,21 @@ #include CONFIG_DEVICES #ifdef CONFIG_VFIO -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-migration.h" #endif #ifdef CONFIG_VFIO void migration_populate_vfio_info(MigrationInfo *info) { - if (vfio_mig_active()) { + if (vfio_migration_active()) { info->vfio = g_malloc0(sizeof(*info->vfio)); - info->vfio->transferred = vfio_mig_bytes_transferred(); + info->vfio->transferred = vfio_migration_bytes_transferred(); } } void migration_reset_vfio_bytes_transferred(void) { - vfio_mig_reset_bytes_transferred(); + vfio_migration_reset_bytes_transferred(); } #else void migration_populate_vfio_info(MigrationInfo *info) From 0a73045687edb6d4b0dac90ca7ae19bf6b3eeb6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:49 +0100 Subject: [PATCH 0254/2760] vfio: Make vfio_un/block_multiple_devices_migration() static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both of these routines are only used in file "migration.c". Move them there. Reviewed-by: Joao Martins Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-5-clg@redhat.com Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-5-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 59 ----------------------------------- hw/vfio/migration.c | 59 +++++++++++++++++++++++++++++++++++ include/hw/vfio/vfio-common.h | 2 -- 3 files changed, 59 insertions(+), 61 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 2ea4e12c90..d65e77b93a 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -40,7 +40,6 @@ #include "trace.h" #include "qapi/error.h" #include "migration/misc.h" -#include "migration/blocker.h" #include "migration/qemu-file.h" #include "system/tcg.h" #include "system/tpm.h" @@ -65,64 +64,6 @@ int vfio_kvm_device_fd = -1; * Device state interfaces */ -static Error *multiple_devices_migration_blocker; - -/* - * Multiple devices migration is allowed only if all devices support P2P - * migration. Single device migration is allowed regardless of P2P migration - * support. - */ -static bool vfio_multiple_devices_migration_is_supported(void) -{ - VFIODevice *vbasedev; - unsigned int device_num = 0; - bool all_support_p2p = true; - - QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { - if (vbasedev->migration) { - device_num++; - - if (!(vbasedev->migration->mig_flags & VFIO_MIGRATION_P2P)) { - all_support_p2p = false; - } - } - } - - return all_support_p2p || device_num <= 1; -} - -int vfio_block_multiple_devices_migration(VFIODevice *vbasedev, Error **errp) -{ - if (vfio_multiple_devices_migration_is_supported()) { - return 0; - } - - if (vbasedev->enable_migration == ON_OFF_AUTO_ON) { - error_setg(errp, "Multiple VFIO devices migration is supported only if " - "all of them support P2P migration"); - return -EINVAL; - } - - if (multiple_devices_migration_blocker) { - return 0; - } - - error_setg(&multiple_devices_migration_blocker, - "Multiple VFIO devices migration is supported only if all of " - "them support P2P migration"); - return migrate_add_blocker_normal(&multiple_devices_migration_blocker, - errp); -} - -void vfio_unblock_multiple_devices_migration(void) -{ - if (!multiple_devices_migration_blocker || - !vfio_multiple_devices_migration_is_supported()) { - return; - } - - migrate_del_blocker(&multiple_devices_migration_blocker); -} bool vfio_viommu_preset(VFIODevice *vbasedev) { diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 582d65932a..ace3d8548e 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -1022,6 +1022,65 @@ static int vfio_migration_init(VFIODevice *vbasedev) return 0; } +static Error *multiple_devices_migration_blocker; + +/* + * Multiple devices migration is allowed only if all devices support P2P + * migration. Single device migration is allowed regardless of P2P migration + * support. + */ +static bool vfio_multiple_devices_migration_is_supported(void) +{ + VFIODevice *vbasedev; + unsigned int device_num = 0; + bool all_support_p2p = true; + + QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->migration) { + device_num++; + + if (!(vbasedev->migration->mig_flags & VFIO_MIGRATION_P2P)) { + all_support_p2p = false; + } + } + } + + return all_support_p2p || device_num <= 1; +} + +static int vfio_block_multiple_devices_migration(VFIODevice *vbasedev, Error **errp) +{ + if (vfio_multiple_devices_migration_is_supported()) { + return 0; + } + + if (vbasedev->enable_migration == ON_OFF_AUTO_ON) { + error_setg(errp, "Multiple VFIO devices migration is supported only if " + "all of them support P2P migration"); + return -EINVAL; + } + + if (multiple_devices_migration_blocker) { + return 0; + } + + error_setg(&multiple_devices_migration_blocker, + "Multiple VFIO devices migration is supported only if all of " + "them support P2P migration"); + return migrate_add_blocker_normal(&multiple_devices_migration_blocker, + errp); +} + +static void vfio_unblock_multiple_devices_migration(void) +{ + if (!multiple_devices_migration_blocker || + !vfio_multiple_devices_migration_is_supported()) { + return; + } + + migrate_del_blocker(&multiple_devices_migration_blocker); +} + static void vfio_migration_deinit(VFIODevice *vbasedev) { VFIOMigration *migration = vbasedev->migration; diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 7a551bb230..5f1c0bee9d 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -290,8 +290,6 @@ extern VFIODeviceList vfio_device_list; extern const MemoryListener vfio_memory_listener; extern int vfio_kvm_device_fd; -int vfio_block_multiple_devices_migration(VFIODevice *vbasedev, Error **errp); -void vfio_unblock_multiple_devices_migration(void); bool vfio_viommu_preset(VFIODevice *vbasedev); void vfio_migration_add_bytes_transferred(unsigned long val); bool vfio_device_state_is_running(VFIODevice *vbasedev); From d04a35cb742e6e3b3682e31addb7b68b22b1c6c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:50 +0100 Subject: [PATCH 0255/2760] vfio: Make vfio_viommu_preset() static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This routine is only used in file "migration.c". Move it there. Reviewed-by: John Levon Reviewed-by: Joao Martins Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-6-clg@redhat.com Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-6-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 5 ----- hw/vfio/migration.c | 5 +++++ include/hw/vfio/vfio-common.h | 1 - 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index d65e77b93a..679076343a 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -65,11 +65,6 @@ int vfio_kvm_device_fd = -1; */ -bool vfio_viommu_preset(VFIODevice *vbasedev) -{ - return vbasedev->bcontainer->space->as != &address_space_memory; -} - bool vfio_device_state_is_running(VFIODevice *vbasedev) { VFIOMigration *migration = vbasedev->migration; diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index ace3d8548e..b5bb0cd092 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -1138,6 +1138,11 @@ bool vfio_migration_active(void) return true; } +static bool vfio_viommu_preset(VFIODevice *vbasedev) +{ + return vbasedev->bcontainer->space->as != &address_space_memory; +} + /* * Return true when either migration initialized or blocker registered. * Currently only return false when adding blocker fails which will diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 5f1c0bee9d..8c56921506 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -290,7 +290,6 @@ extern VFIODeviceList vfio_device_list; extern const MemoryListener vfio_memory_listener; extern int vfio_kvm_device_fd; -bool vfio_viommu_preset(VFIODevice *vbasedev); void vfio_migration_add_bytes_transferred(unsigned long val); bool vfio_device_state_is_running(VFIODevice *vbasedev); bool vfio_device_state_is_precopy(VFIODevice *vbasedev); From b553d2c414a1b5c5bd3bf8bce0b648ba7f8e2baf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:51 +0100 Subject: [PATCH 0256/2760] vfio: Introduce a new header file for internal migration services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gather all VFIO migration related declarations into "vfio-migration-internal.h" to reduce exposure of VFIO internals in "hw/vfio/vfio-common.h". Cc: Kirti Wankhede Cc: Avihai Horon Reviewed-by: Prasad Pandit Reviewed-by: John Levon Reviewed-by: Avihai Horon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-7-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 1 + hw/vfio/migration-multifd.c | 2 +- hw/vfio/migration.c | 1 + hw/vfio/pci.c | 1 + hw/vfio/vfio-migration-internal.h | 72 +++++++++++++++++++++++++++++++ include/hw/vfio/vfio-common.h | 52 +--------------------- 6 files changed, 77 insertions(+), 52 deletions(-) create mode 100644 hw/vfio/vfio-migration-internal.h diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 679076343a..bef5414dd7 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -43,6 +43,7 @@ #include "migration/qemu-file.h" #include "system/tcg.h" #include "system/tpm.h" +#include "vfio-migration-internal.h" VFIODeviceList vfio_device_list = QLIST_HEAD_INITIALIZER(vfio_device_list); diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index 09aa57f5f8..a3005226b9 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "hw/vfio/vfio-common.h" -#include "hw/vfio/vfio-migration.h" #include "migration/misc.h" #include "qapi/error.h" #include "qemu/bswap.h" @@ -22,6 +21,7 @@ #include "io/channel-buffer.h" #include "migration/qemu-file.h" #include "migration-multifd.h" +#include "vfio-migration-internal.h" #include "trace.h" #define VFIO_DEVICE_STATE_CONFIG_STATE (1) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index b5bb0cd092..54f6ca3e7c 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -31,6 +31,7 @@ #include "pci.h" #include "trace.h" #include "hw/hw.h" +#include "vfio-migration-internal.h" /* * This is an arbitrary size based on migration of mlx5 devices, where typically diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index f87f3ccbe1..b0aac2f3a0 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -44,6 +44,7 @@ #include "migration/blocker.h" #include "migration/qemu-file.h" #include "system/iommufd.h" +#include "vfio-migration-internal.h" #define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug" diff --git a/hw/vfio/vfio-migration-internal.h b/hw/vfio/vfio-migration-internal.h new file mode 100644 index 0000000000..ab6a1bad9b --- /dev/null +++ b/hw/vfio/vfio-migration-internal.h @@ -0,0 +1,72 @@ +/* + * VFIO migration + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_MIGRATION_INTERNAL_H +#define HW_VFIO_VFIO_MIGRATION_INTERNAL_H + +#ifdef CONFIG_LINUX +#include +#endif + +#include "qemu/typedefs.h" +#include "qemu/notify.h" + +/* + * Flags to be used as unique delimiters for VFIO devices in the migration + * stream. These flags are composed as: + * 0xffffffff => MSB 32-bit all 1s + * 0xef10 => Magic ID, represents emulated (virtual) function IO + * 0x0000 => 16-bits reserved for flags + * + * The beginning of state information is marked by _DEV_CONFIG_STATE, + * _DEV_SETUP_STATE, or _DEV_DATA_STATE, respectively. The end of a + * certain state information is marked by _END_OF_STATE. + */ +#define VFIO_MIG_FLAG_END_OF_STATE (0xffffffffef100001ULL) +#define VFIO_MIG_FLAG_DEV_CONFIG_STATE (0xffffffffef100002ULL) +#define VFIO_MIG_FLAG_DEV_SETUP_STATE (0xffffffffef100003ULL) +#define VFIO_MIG_FLAG_DEV_DATA_STATE (0xffffffffef100004ULL) +#define VFIO_MIG_FLAG_DEV_INIT_DATA_SENT (0xffffffffef100005ULL) + +typedef struct VFIODevice VFIODevice; +typedef struct VFIOMultifd VFIOMultifd; + +typedef struct VFIOMigration { + struct VFIODevice *vbasedev; + VMChangeStateEntry *vm_state; + NotifierWithReturn migration_state; + uint32_t device_state; + int data_fd; + void *data_buffer; + size_t data_buffer_size; + uint64_t mig_flags; + uint64_t precopy_init_size; + uint64_t precopy_dirty_size; + bool multifd_transfer; + VFIOMultifd *multifd; + bool initial_data_sent; + + bool event_save_iterate_started; + bool event_precopy_empty_hit; +} VFIOMigration; + +bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp); +void vfio_migration_exit(VFIODevice *vbasedev); +int vfio_save_device_config_state(QEMUFile *f, void *opaque, Error **errp); +int vfio_load_device_config_state(QEMUFile *f, void *opaque); + +#ifdef CONFIG_LINUX +int vfio_migration_set_state(VFIODevice *vbasedev, + enum vfio_device_mig_state new_state, + enum vfio_device_mig_state recover_state, + Error **errp); +#endif + +void vfio_migration_add_bytes_transferred(unsigned long val); + +#endif /* HW_VFIO_VFIO_MIGRATION_INTERNAL_H */ diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 8c56921506..05c88753ce 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -23,7 +23,6 @@ #include "system/memory.h" #include "qemu/queue.h" -#include "qemu/notify.h" #include "ui/console.h" #include "hw/display/ramfb.h" #ifdef CONFIG_LINUX @@ -36,23 +35,6 @@ #define VFIO_MSG_PREFIX "vfio %s: " -/* - * Flags to be used as unique delimiters for VFIO devices in the migration - * stream. These flags are composed as: - * 0xffffffff => MSB 32-bit all 1s - * 0xef10 => Magic ID, represents emulated (virtual) function IO - * 0x0000 => 16-bits reserved for flags - * - * The beginning of state information is marked by _DEV_CONFIG_STATE, - * _DEV_SETUP_STATE, or _DEV_DATA_STATE, respectively. The end of a - * certain state information is marked by _END_OF_STATE. - */ -#define VFIO_MIG_FLAG_END_OF_STATE (0xffffffffef100001ULL) -#define VFIO_MIG_FLAG_DEV_CONFIG_STATE (0xffffffffef100002ULL) -#define VFIO_MIG_FLAG_DEV_SETUP_STATE (0xffffffffef100003ULL) -#define VFIO_MIG_FLAG_DEV_DATA_STATE (0xffffffffef100004ULL) -#define VFIO_MIG_FLAG_DEV_INIT_DATA_SENT (0xffffffffef100005ULL) - enum { VFIO_DEVICE_TYPE_PCI = 0, VFIO_DEVICE_TYPE_PLATFORM = 1, @@ -78,27 +60,6 @@ typedef struct VFIORegion { uint8_t nr; /* cache the region number for debug */ } VFIORegion; -typedef struct VFIOMultifd VFIOMultifd; - -typedef struct VFIOMigration { - struct VFIODevice *vbasedev; - VMChangeStateEntry *vm_state; - NotifierWithReturn migration_state; - uint32_t device_state; - int data_fd; - void *data_buffer; - size_t data_buffer_size; - uint64_t mig_flags; - uint64_t precopy_init_size; - uint64_t precopy_dirty_size; - bool multifd_transfer; - VFIOMultifd *multifd; - bool initial_data_sent; - - bool event_save_iterate_started; - bool event_precopy_empty_hit; -} VFIOMigration; - struct VFIOGroup; typedef struct VFIOContainer { @@ -136,6 +97,7 @@ typedef struct VFIOIOMMUFDContainer { OBJECT_DECLARE_SIMPLE_TYPE(VFIOIOMMUFDContainer, VFIO_IOMMU_IOMMUFD); typedef struct VFIODeviceOps VFIODeviceOps; +typedef struct VFIOMigration VFIOMigration; typedef struct VFIODevice { QLIST_ENTRY(VFIODevice) next; @@ -290,13 +252,9 @@ extern VFIODeviceList vfio_device_list; extern const MemoryListener vfio_memory_listener; extern int vfio_kvm_device_fd; -void vfio_migration_add_bytes_transferred(unsigned long val); bool vfio_device_state_is_running(VFIODevice *vbasedev); bool vfio_device_state_is_precopy(VFIODevice *vbasedev); -int vfio_save_device_config_state(QEMUFile *f, void *opaque, Error **errp); -int vfio_load_device_config_state(QEMUFile *f, void *opaque); - #ifdef CONFIG_LINUX int vfio_get_region_info(VFIODevice *vbasedev, int index, struct vfio_region_info **info); @@ -311,16 +269,8 @@ struct vfio_info_cap_header * vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id); struct vfio_info_cap_header * vfio_get_cap(void *ptr, uint32_t cap_offset, uint16_t id); - -int vfio_migration_set_state(VFIODevice *vbasedev, - enum vfio_device_mig_state new_state, - enum vfio_device_mig_state recover_state, - Error **errp); #endif -bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp); -void vfio_migration_exit(VFIODevice *vbasedev); - int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size); bool vfio_devices_all_dirty_tracking_started( const VFIOContainerBase *bcontainer); From eb6caa79162a89a8dcbe6a6d4788acd813b687a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:52 +0100 Subject: [PATCH 0257/2760] vfio: Move vfio_device_state_is_running/precopy() into migration.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These routines are migration related. Move their declaration and implementation under the migration files. Reviewed-by: Prasad Pandit Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-8-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 16 ---------------- hw/vfio/migration.c | 16 ++++++++++++++++ hw/vfio/vfio-migration-internal.h | 2 ++ include/hw/vfio/vfio-common.h | 3 --- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index bef5414dd7..8f55e7b212 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -66,22 +66,6 @@ int vfio_kvm_device_fd = -1; */ -bool vfio_device_state_is_running(VFIODevice *vbasedev) -{ - VFIOMigration *migration = vbasedev->migration; - - return migration->device_state == VFIO_DEVICE_STATE_RUNNING || - migration->device_state == VFIO_DEVICE_STATE_RUNNING_P2P; -} - -bool vfio_device_state_is_precopy(VFIODevice *vbasedev) -{ - VFIOMigration *migration = vbasedev->migration; - - return migration->device_state == VFIO_DEVICE_STATE_PRE_COPY || - migration->device_state == VFIO_DEVICE_STATE_PRE_COPY_P2P; -} - static bool vfio_devices_all_device_dirty_tracking_started( const VFIOContainerBase *bcontainer) { diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 54f6ca3e7c..4da0526325 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -1220,3 +1220,19 @@ void vfio_migration_exit(VFIODevice *vbasedev) migrate_del_blocker(&vbasedev->migration_blocker); } + +bool vfio_device_state_is_running(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + + return migration->device_state == VFIO_DEVICE_STATE_RUNNING || + migration->device_state == VFIO_DEVICE_STATE_RUNNING_P2P; +} + +bool vfio_device_state_is_precopy(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + + return migration->device_state == VFIO_DEVICE_STATE_PRE_COPY || + migration->device_state == VFIO_DEVICE_STATE_PRE_COPY_P2P; +} diff --git a/hw/vfio/vfio-migration-internal.h b/hw/vfio/vfio-migration-internal.h index ab6a1bad9b..a8b456b239 100644 --- a/hw/vfio/vfio-migration-internal.h +++ b/hw/vfio/vfio-migration-internal.h @@ -57,6 +57,8 @@ typedef struct VFIOMigration { bool vfio_migration_realize(VFIODevice *vbasedev, Error **errp); void vfio_migration_exit(VFIODevice *vbasedev); +bool vfio_device_state_is_running(VFIODevice *vbasedev); +bool vfio_device_state_is_precopy(VFIODevice *vbasedev); int vfio_save_device_config_state(QEMUFile *f, void *opaque, Error **errp); int vfio_load_device_config_state(QEMUFile *f, void *opaque); diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 05c88753ce..fa0b74d5ea 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -252,9 +252,6 @@ extern VFIODeviceList vfio_device_list; extern const MemoryListener vfio_memory_listener; extern int vfio_kvm_device_fd; -bool vfio_device_state_is_running(VFIODevice *vbasedev); -bool vfio_device_state_is_precopy(VFIODevice *vbasedev); - #ifdef CONFIG_LINUX int vfio_get_region_info(VFIODevice *vbasedev, int index, struct vfio_region_info **info); From aa173cb279e258ff591297fd0dedea43fd6f321d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:53 +0100 Subject: [PATCH 0258/2760] vfio: Introduce a new header file for VFIOdisplay declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gather all VFIOdisplay related declarations into "vfio-display.h" to reduce exposure of VFIO internals in "hw/vfio/vfio-common.h". Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-8-clg@redhat.com Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-9-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/display.c | 2 +- hw/vfio/pci.h | 1 + hw/vfio/vfio-display.h | 41 +++++++++++++++++++++++++++++++++++ include/hw/vfio/vfio-common.h | 28 ------------------------ 4 files changed, 43 insertions(+), 29 deletions(-) create mode 100644 hw/vfio/vfio-display.h diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 4fdcef505d..70cfd685bd 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -16,9 +16,9 @@ #include "qemu/error-report.h" #include "hw/display/edid.h" -#include "ui/console.h" #include "qapi/error.h" #include "pci.h" +#include "vfio-display.h" #include "trace.h" #ifndef DRM_PLANE_TYPE_PRIMARY diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 6c59300248..62fd5f4997 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -20,6 +20,7 @@ #include "qemu/timer.h" #include "qom/object.h" #include "system/kvm.h" +#include "vfio-display.h" #define PCI_ANY_ID (~0) diff --git a/hw/vfio/vfio-display.h b/hw/vfio/vfio-display.h new file mode 100644 index 0000000000..99b8cb67ef --- /dev/null +++ b/hw/vfio/vfio-display.h @@ -0,0 +1,41 @@ +/* + * VFIO display + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_DISPLAY_H +#define HW_VFIO_VFIO_DISPLAY_H + +#include "ui/console.h" +#include "hw/display/ramfb.h" + +typedef struct VFIODMABuf { + QemuDmaBuf *buf; + uint32_t pos_x, pos_y, pos_updates; + uint32_t hot_x, hot_y, hot_updates; + int dmabuf_id; + QTAILQ_ENTRY(VFIODMABuf) next; +} VFIODMABuf; + +typedef struct VFIODisplay { + QemuConsole *con; + RAMFBState *ramfb; + struct vfio_region_info *edid_info; + struct vfio_region_gfx_edid *edid_regs; + uint8_t *edid_blob; + QEMUTimer *edid_link_timer; + struct { + VFIORegion buffer; + DisplaySurface *surface; + } region; + struct { + QTAILQ_HEAD(, VFIODMABuf) bufs; + VFIODMABuf *primary; + VFIODMABuf *cursor; + } dmabuf; +} VFIODisplay; + +#endif /* HW_VFIO_VFIO_DISPLAY_H */ diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index fa0b74d5ea..528eafadf9 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -23,8 +23,6 @@ #include "system/memory.h" #include "qemu/queue.h" -#include "ui/console.h" -#include "hw/display/ramfb.h" #ifdef CONFIG_LINUX #include #endif @@ -182,32 +180,6 @@ typedef struct VFIOGroup { #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" -typedef struct VFIODMABuf { - QemuDmaBuf *buf; - uint32_t pos_x, pos_y, pos_updates; - uint32_t hot_x, hot_y, hot_updates; - int dmabuf_id; - QTAILQ_ENTRY(VFIODMABuf) next; -} VFIODMABuf; - -typedef struct VFIODisplay { - QemuConsole *con; - RAMFBState *ramfb; - struct vfio_region_info *edid_info; - struct vfio_region_gfx_edid *edid_regs; - uint8_t *edid_blob; - QEMUTimer *edid_link_timer; - struct { - VFIORegion buffer; - DisplaySurface *surface; - } region; - struct { - QTAILQ_HEAD(, VFIODMABuf) bufs; - VFIODMABuf *primary; - VFIODMABuf *cursor; - } dmabuf; -} VFIODisplay; - VFIOAddressSpace *vfio_get_address_space(AddressSpace *as); void vfio_put_address_space(VFIOAddressSpace *space); void vfio_address_space_insert(VFIOAddressSpace *space, From 5a339dd8c1e34e13ae7f64477e20decdf381aae7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:54 +0100 Subject: [PATCH 0259/2760] vfio: Move VFIOHostDMAWindow definition into spapr.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VFIOHostDMAWindow is only used in file "spapr.c". Move it there. Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-9-clg@redhat.com Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-10-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/spapr.c | 7 +++++++ include/hw/vfio/vfio-common.h | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 7e5cb95f6a..f21955a344 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -22,6 +22,13 @@ #include "qapi/error.h" #include "trace.h" +typedef struct VFIOHostDMAWindow { + hwaddr min_iova; + hwaddr max_iova; + uint64_t iova_pgsizes; + QLIST_ENTRY(VFIOHostDMAWindow) hostwin_next; +} VFIOHostDMAWindow; + typedef struct VFIOSpaprContainer { VFIOContainer container; MemoryListener prereg_listener; diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 528eafadf9..83b1f7be80 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -69,13 +69,6 @@ typedef struct VFIOContainer { OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_IOMMU_LEGACY); -typedef struct VFIOHostDMAWindow { - hwaddr min_iova; - hwaddr max_iova; - uint64_t iova_pgsizes; - QLIST_ENTRY(VFIOHostDMAWindow) hostwin_next; -} VFIOHostDMAWindow; - typedef struct IOMMUFDBackend IOMMUFDBackend; typedef struct VFIOIOASHwpt { From d4a8f286e991c468489b19e45b959a1920d6890c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:55 +0100 Subject: [PATCH 0260/2760] vfio: Introduce a new header file for VFIOIOMMUFD declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gather all VFIOIOMMUFD related declarations introduced by commits 5ee3dc7af785 ("vfio/iommufd: Implement the iommufd backend") and 5b1e96e65403 ("vfio/iommufd: Introduce auto domain creation") into "vfio-iommufd.h". This to reduce exposure of VFIO internals in "hw/vfio/vfio-common.h". Cc: Joao Martins Cc: Yi Liu Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-10-clg@redhat.com Reviewed-by: Joao Martins Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-11-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 1 + hw/vfio/vfio-iommufd.h | 34 ++++++++++++++++++++++++++++++++++ include/hw/vfio/vfio-common.h | 21 +++------------------ 3 files changed, 38 insertions(+), 18 deletions(-) create mode 100644 hw/vfio/vfio-iommufd.h diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 42c8412bbf..7196c40801 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -25,6 +25,7 @@ #include "qemu/cutils.h" #include "qemu/chardev_open.h" #include "pci.h" +#include "vfio-iommufd.h" static int iommufd_cdev_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly) diff --git a/hw/vfio/vfio-iommufd.h b/hw/vfio/vfio-iommufd.h new file mode 100644 index 0000000000..07ea0f4304 --- /dev/null +++ b/hw/vfio/vfio-iommufd.h @@ -0,0 +1,34 @@ +/* + * VFIO iommufd + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_IOMMUFD_H +#define HW_VFIO_VFIO_IOMMUFD_H + +#include "hw/vfio/vfio-container-base.h" + +typedef struct VFIODevice VFIODevice; + +typedef struct VFIOIOASHwpt { + uint32_t hwpt_id; + uint32_t hwpt_flags; + QLIST_HEAD(, VFIODevice) device_list; + QLIST_ENTRY(VFIOIOASHwpt) next; +} VFIOIOASHwpt; + +typedef struct IOMMUFDBackend IOMMUFDBackend; + +typedef struct VFIOIOMMUFDContainer { + VFIOContainerBase bcontainer; + IOMMUFDBackend *be; + uint32_t ioas_id; + QLIST_HEAD(, VFIOIOASHwpt) hwpt_list; +} VFIOIOMMUFDContainer; + +OBJECT_DECLARE_SIMPLE_TYPE(VFIOIOMMUFDContainer, VFIO_IOMMU_IOMMUFD); + +#endif /* HW_VFIO_VFIO_IOMMUFD_H */ diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 83b1f7be80..9ec90fbac0 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -69,27 +69,12 @@ typedef struct VFIOContainer { OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_IOMMU_LEGACY); -typedef struct IOMMUFDBackend IOMMUFDBackend; - -typedef struct VFIOIOASHwpt { - uint32_t hwpt_id; - uint32_t hwpt_flags; - QLIST_HEAD(, VFIODevice) device_list; - QLIST_ENTRY(VFIOIOASHwpt) next; -} VFIOIOASHwpt; - -typedef struct VFIOIOMMUFDContainer { - VFIOContainerBase bcontainer; - IOMMUFDBackend *be; - uint32_t ioas_id; - QLIST_HEAD(, VFIOIOASHwpt) hwpt_list; -} VFIOIOMMUFDContainer; - -OBJECT_DECLARE_SIMPLE_TYPE(VFIOIOMMUFDContainer, VFIO_IOMMU_IOMMUFD); - typedef struct VFIODeviceOps VFIODeviceOps; typedef struct VFIOMigration VFIOMigration; +typedef struct IOMMUFDBackend IOMMUFDBackend; +typedef struct VFIOIOASHwpt VFIOIOASHwpt; + typedef struct VFIODevice { QLIST_ENTRY(VFIODevice) next; QLIST_ENTRY(VFIODevice) container_next; From 499e53cce9445d23ee1bf54562de558562fc8d22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:56 +0100 Subject: [PATCH 0261/2760] vfio: Introduce new files for VFIORegion definitions and declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gather all VFIORegion related declarations and definitions into their own files to reduce exposure of VFIO internals in "hw/vfio/vfio-common.h". They were introduced for 'vfio-platform' support in commits db0da029a185 ("vfio: Generalize region support") and a664477db8da ("hw/vfio/pci: Introduce VFIORegion"). To be noted that the 'vfio-platform' devices have been deprecated and will be removed in QEMU 10.2. Until then, make the declarations available externally for 'sysbus-fdt.c'. Cc: Eric Auger Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-12-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/core/sysbus-fdt.c | 1 + hw/vfio/helpers.c | 363 ----------------------------- hw/vfio/meson.build | 1 + hw/vfio/pci.h | 1 + hw/vfio/platform.c | 1 + hw/vfio/region.c | 394 ++++++++++++++++++++++++++++++++ hw/vfio/trace-events | 16 +- hw/vfio/vfio-display.h | 1 + include/hw/vfio/vfio-common.h | 32 +-- include/hw/vfio/vfio-platform.h | 2 + include/hw/vfio/vfio-region.h | 47 ++++ 11 files changed, 458 insertions(+), 401 deletions(-) create mode 100644 hw/vfio/region.c create mode 100644 include/hw/vfio/vfio-region.h diff --git a/hw/core/sysbus-fdt.c b/hw/core/sysbus-fdt.c index e85066b905..c339a27875 100644 --- a/hw/core/sysbus-fdt.c +++ b/hw/core/sysbus-fdt.c @@ -35,6 +35,7 @@ #include "hw/vfio/vfio-platform.h" #include "hw/vfio/vfio-calxeda-xgmac.h" #include "hw/vfio/vfio-amd-xgbe.h" +#include "hw/vfio/vfio-region.h" #include "hw/display/ramfb.h" #include "hw/uefi/var-service-api.h" #include "hw/arm/fdt.h" diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 4b255d4f3a..89403943a7 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -147,118 +147,6 @@ bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, return false; } -/* - * IO Port/MMIO - Beware of the endians, VFIO is always little endian - */ -void vfio_region_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - VFIORegion *region = opaque; - VFIODevice *vbasedev = region->vbasedev; - union { - uint8_t byte; - uint16_t word; - uint32_t dword; - uint64_t qword; - } buf; - - switch (size) { - case 1: - buf.byte = data; - break; - case 2: - buf.word = cpu_to_le16(data); - break; - case 4: - buf.dword = cpu_to_le32(data); - break; - case 8: - buf.qword = cpu_to_le64(data); - break; - default: - hw_error("vfio: unsupported write size, %u bytes", size); - break; - } - - if (pwrite(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) { - error_report("%s(%s:region%d+0x%"HWADDR_PRIx", 0x%"PRIx64 - ",%d) failed: %m", - __func__, vbasedev->name, region->nr, - addr, data, size); - } - - trace_vfio_region_write(vbasedev->name, region->nr, addr, data, size); - - /* - * A read or write to a BAR always signals an INTx EOI. This will - * do nothing if not pending (including not in INTx mode). We assume - * that a BAR access is in response to an interrupt and that BAR - * accesses will service the interrupt. Unfortunately, we don't know - * which access will service the interrupt, so we're potentially - * getting quite a few host interrupts per guest interrupt. - */ - vbasedev->ops->vfio_eoi(vbasedev); -} - -uint64_t vfio_region_read(void *opaque, - hwaddr addr, unsigned size) -{ - VFIORegion *region = opaque; - VFIODevice *vbasedev = region->vbasedev; - union { - uint8_t byte; - uint16_t word; - uint32_t dword; - uint64_t qword; - } buf; - uint64_t data = 0; - - if (pread(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) { - error_report("%s(%s:region%d+0x%"HWADDR_PRIx", %d) failed: %m", - __func__, vbasedev->name, region->nr, - addr, size); - return (uint64_t)-1; - } - switch (size) { - case 1: - data = buf.byte; - break; - case 2: - data = le16_to_cpu(buf.word); - break; - case 4: - data = le32_to_cpu(buf.dword); - break; - case 8: - data = le64_to_cpu(buf.qword); - break; - default: - hw_error("vfio: unsupported read size, %u bytes", size); - break; - } - - trace_vfio_region_read(vbasedev->name, region->nr, addr, size, data); - - /* Same as write above */ - vbasedev->ops->vfio_eoi(vbasedev); - - return data; -} - -const MemoryRegionOps vfio_region_ops = { - .read = vfio_region_read, - .write = vfio_region_write, - .endianness = DEVICE_LITTLE_ENDIAN, - .valid = { - .min_access_size = 1, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 1, - .max_access_size = 8, - }, -}; - int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size) { vbmap->pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size(); @@ -306,257 +194,6 @@ vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id) return vfio_get_cap((void *)info, info->cap_offset, id); } -static int vfio_setup_region_sparse_mmaps(VFIORegion *region, - struct vfio_region_info *info) -{ - struct vfio_info_cap_header *hdr; - struct vfio_region_info_cap_sparse_mmap *sparse; - int i, j; - - hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP); - if (!hdr) { - return -ENODEV; - } - - sparse = container_of(hdr, struct vfio_region_info_cap_sparse_mmap, header); - - trace_vfio_region_sparse_mmap_header(region->vbasedev->name, - region->nr, sparse->nr_areas); - - region->mmaps = g_new0(VFIOMmap, sparse->nr_areas); - - for (i = 0, j = 0; i < sparse->nr_areas; i++) { - if (sparse->areas[i].size) { - trace_vfio_region_sparse_mmap_entry(i, sparse->areas[i].offset, - sparse->areas[i].offset + - sparse->areas[i].size - 1); - region->mmaps[j].offset = sparse->areas[i].offset; - region->mmaps[j].size = sparse->areas[i].size; - j++; - } - } - - region->nr_mmaps = j; - region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap)); - - return 0; -} - -int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, - int index, const char *name) -{ - g_autofree struct vfio_region_info *info = NULL; - int ret; - - ret = vfio_get_region_info(vbasedev, index, &info); - if (ret) { - return ret; - } - - region->vbasedev = vbasedev; - region->flags = info->flags; - region->size = info->size; - region->fd_offset = info->offset; - region->nr = index; - - if (region->size) { - region->mem = g_new0(MemoryRegion, 1); - memory_region_init_io(region->mem, obj, &vfio_region_ops, - region, name, region->size); - - if (!vbasedev->no_mmap && - region->flags & VFIO_REGION_INFO_FLAG_MMAP) { - - ret = vfio_setup_region_sparse_mmaps(region, info); - - if (ret) { - region->nr_mmaps = 1; - region->mmaps = g_new0(VFIOMmap, region->nr_mmaps); - region->mmaps[0].offset = 0; - region->mmaps[0].size = region->size; - } - } - } - - trace_vfio_region_setup(vbasedev->name, index, name, - region->flags, region->fd_offset, region->size); - return 0; -} - -static void vfio_subregion_unmap(VFIORegion *region, int index) -{ - trace_vfio_region_unmap(memory_region_name(®ion->mmaps[index].mem), - region->mmaps[index].offset, - region->mmaps[index].offset + - region->mmaps[index].size - 1); - memory_region_del_subregion(region->mem, ®ion->mmaps[index].mem); - munmap(region->mmaps[index].mmap, region->mmaps[index].size); - object_unparent(OBJECT(®ion->mmaps[index].mem)); - region->mmaps[index].mmap = NULL; -} - -int vfio_region_mmap(VFIORegion *region) -{ - int i, ret, prot = 0; - char *name; - - if (!region->mem) { - return 0; - } - - prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0; - prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0; - - for (i = 0; i < region->nr_mmaps; i++) { - size_t align = MIN(1ULL << ctz64(region->mmaps[i].size), 1 * GiB); - void *map_base, *map_align; - - /* - * Align the mmap for more efficient mapping in the kernel. Ideally - * we'd know the PMD and PUD mapping sizes to use as discrete alignment - * intervals, but we don't. As of Linux v6.12, the largest PUD size - * supporting huge pfnmap is 1GiB (ARCH_SUPPORTS_PUD_PFNMAP is only set - * on x86_64). Align by power-of-two size, capped at 1GiB. - * - * NB. qemu_memalign() and friends actually allocate memory, whereas - * the region size here can exceed host memory, therefore we manually - * create an oversized anonymous mapping and clean it up for alignment. - */ - map_base = mmap(0, region->mmaps[i].size + align, PROT_NONE, - MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); - if (map_base == MAP_FAILED) { - ret = -errno; - goto no_mmap; - } - - map_align = (void *)ROUND_UP((uintptr_t)map_base, (uintptr_t)align); - munmap(map_base, map_align - map_base); - munmap(map_align + region->mmaps[i].size, - align - (map_align - map_base)); - - region->mmaps[i].mmap = mmap(map_align, region->mmaps[i].size, prot, - MAP_SHARED | MAP_FIXED, - region->vbasedev->fd, - region->fd_offset + - region->mmaps[i].offset); - if (region->mmaps[i].mmap == MAP_FAILED) { - ret = -errno; - goto no_mmap; - } - - name = g_strdup_printf("%s mmaps[%d]", - memory_region_name(region->mem), i); - memory_region_init_ram_device_ptr(®ion->mmaps[i].mem, - memory_region_owner(region->mem), - name, region->mmaps[i].size, - region->mmaps[i].mmap); - g_free(name); - memory_region_add_subregion(region->mem, region->mmaps[i].offset, - ®ion->mmaps[i].mem); - - trace_vfio_region_mmap(memory_region_name(®ion->mmaps[i].mem), - region->mmaps[i].offset, - region->mmaps[i].offset + - region->mmaps[i].size - 1); - } - - return 0; - -no_mmap: - trace_vfio_region_mmap_fault(memory_region_name(region->mem), i, - region->fd_offset + region->mmaps[i].offset, - region->fd_offset + region->mmaps[i].offset + - region->mmaps[i].size - 1, ret); - - region->mmaps[i].mmap = NULL; - - for (i--; i >= 0; i--) { - vfio_subregion_unmap(region, i); - } - - return ret; -} - -void vfio_region_unmap(VFIORegion *region) -{ - int i; - - if (!region->mem) { - return; - } - - for (i = 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - vfio_subregion_unmap(region, i); - } - } -} - -void vfio_region_exit(VFIORegion *region) -{ - int i; - - if (!region->mem) { - return; - } - - for (i = 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - memory_region_del_subregion(region->mem, ®ion->mmaps[i].mem); - } - } - - trace_vfio_region_exit(region->vbasedev->name, region->nr); -} - -void vfio_region_finalize(VFIORegion *region) -{ - int i; - - if (!region->mem) { - return; - } - - for (i = 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - munmap(region->mmaps[i].mmap, region->mmaps[i].size); - object_unparent(OBJECT(®ion->mmaps[i].mem)); - } - } - - object_unparent(OBJECT(region->mem)); - - g_free(region->mem); - g_free(region->mmaps); - - trace_vfio_region_finalize(region->vbasedev->name, region->nr); - - region->mem = NULL; - region->mmaps = NULL; - region->nr_mmaps = 0; - region->size = 0; - region->flags = 0; - region->nr = 0; -} - -void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled) -{ - int i; - - if (!region->mem) { - return; - } - - for (i = 0; i < region->nr_mmaps; i++) { - if (region->mmaps[i].mmap) { - memory_region_set_enabled(®ion->mmaps[i].mem, enabled); - } - } - - trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem), - enabled); -} - int vfio_get_region_info(VFIODevice *vbasedev, int index, struct vfio_region_info **info) { diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index a8939c8386..07010c7c9e 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -23,6 +23,7 @@ system_ss.add(when: 'CONFIG_VFIO', if_true: files( 'migration.c', 'migration-multifd.c', 'cpr.c', + 'region.c', )) system_ss.add(when: ['CONFIG_VFIO', 'CONFIG_IOMMUFD'], if_true: files( 'iommufd.c', diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 62fd5f4997..801ea445b8 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -15,6 +15,7 @@ #include "system/memory.h" #include "hw/pci/pci_device.h" #include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-region.h" #include "qemu/event_notifier.h" #include "qemu/queue.h" #include "qemu/timer.h" diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index f273ae9a51..ca6528cc56 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -37,6 +37,7 @@ #include "hw/platform-bus.h" #include "hw/qdev-properties.h" #include "system/kvm.h" +#include "hw/vfio/vfio-region.h" /* * Functions used whatever the injection method diff --git a/hw/vfio/region.c b/hw/vfio/region.c new file mode 100644 index 0000000000..08cd69e704 --- /dev/null +++ b/hw/vfio/region.c @@ -0,0 +1,394 @@ +/* + * VFIO regions + * + * Copyright Red Hat, Inc. 2012 + * + * Authors: + * Alex Williamson + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Based on qemu-kvm device-assignment: + * Adapted for KVM by Qumranet. + * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com) + * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com) + * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com) + * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) + * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) + */ + +#include "qemu/osdep.h" +#include + +#include "hw/vfio/vfio-common.h" +#include "hw/vfio/pci.h" +#include "hw/hw.h" +#include "trace.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/units.h" +#include "monitor/monitor.h" + +/* + * IO Port/MMIO - Beware of the endians, VFIO is always little endian + */ +void vfio_region_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + VFIORegion *region = opaque; + VFIODevice *vbasedev = region->vbasedev; + union { + uint8_t byte; + uint16_t word; + uint32_t dword; + uint64_t qword; + } buf; + + switch (size) { + case 1: + buf.byte = data; + break; + case 2: + buf.word = cpu_to_le16(data); + break; + case 4: + buf.dword = cpu_to_le32(data); + break; + case 8: + buf.qword = cpu_to_le64(data); + break; + default: + hw_error("vfio: unsupported write size, %u bytes", size); + break; + } + + if (pwrite(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) { + error_report("%s(%s:region%d+0x%"HWADDR_PRIx", 0x%"PRIx64 + ",%d) failed: %m", + __func__, vbasedev->name, region->nr, + addr, data, size); + } + + trace_vfio_region_write(vbasedev->name, region->nr, addr, data, size); + + /* + * A read or write to a BAR always signals an INTx EOI. This will + * do nothing if not pending (including not in INTx mode). We assume + * that a BAR access is in response to an interrupt and that BAR + * accesses will service the interrupt. Unfortunately, we don't know + * which access will service the interrupt, so we're potentially + * getting quite a few host interrupts per guest interrupt. + */ + vbasedev->ops->vfio_eoi(vbasedev); +} + +uint64_t vfio_region_read(void *opaque, + hwaddr addr, unsigned size) +{ + VFIORegion *region = opaque; + VFIODevice *vbasedev = region->vbasedev; + union { + uint8_t byte; + uint16_t word; + uint32_t dword; + uint64_t qword; + } buf; + uint64_t data = 0; + + if (pread(vbasedev->fd, &buf, size, region->fd_offset + addr) != size) { + error_report("%s(%s:region%d+0x%"HWADDR_PRIx", %d) failed: %m", + __func__, vbasedev->name, region->nr, + addr, size); + return (uint64_t)-1; + } + switch (size) { + case 1: + data = buf.byte; + break; + case 2: + data = le16_to_cpu(buf.word); + break; + case 4: + data = le32_to_cpu(buf.dword); + break; + case 8: + data = le64_to_cpu(buf.qword); + break; + default: + hw_error("vfio: unsupported read size, %u bytes", size); + break; + } + + trace_vfio_region_read(vbasedev->name, region->nr, addr, size, data); + + /* Same as write above */ + vbasedev->ops->vfio_eoi(vbasedev); + + return data; +} + +static const MemoryRegionOps vfio_region_ops = { + .read = vfio_region_read, + .write = vfio_region_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 1, + .max_access_size = 8, + }, + .impl = { + .min_access_size = 1, + .max_access_size = 8, + }, +}; + +static int vfio_setup_region_sparse_mmaps(VFIORegion *region, + struct vfio_region_info *info) +{ + struct vfio_info_cap_header *hdr; + struct vfio_region_info_cap_sparse_mmap *sparse; + int i, j; + + hdr = vfio_get_region_info_cap(info, VFIO_REGION_INFO_CAP_SPARSE_MMAP); + if (!hdr) { + return -ENODEV; + } + + sparse = container_of(hdr, struct vfio_region_info_cap_sparse_mmap, header); + + trace_vfio_region_sparse_mmap_header(region->vbasedev->name, + region->nr, sparse->nr_areas); + + region->mmaps = g_new0(VFIOMmap, sparse->nr_areas); + + for (i = 0, j = 0; i < sparse->nr_areas; i++) { + if (sparse->areas[i].size) { + trace_vfio_region_sparse_mmap_entry(i, sparse->areas[i].offset, + sparse->areas[i].offset + + sparse->areas[i].size - 1); + region->mmaps[j].offset = sparse->areas[i].offset; + region->mmaps[j].size = sparse->areas[i].size; + j++; + } + } + + region->nr_mmaps = j; + region->mmaps = g_realloc(region->mmaps, j * sizeof(VFIOMmap)); + + return 0; +} + +int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, + int index, const char *name) +{ + g_autofree struct vfio_region_info *info = NULL; + int ret; + + ret = vfio_get_region_info(vbasedev, index, &info); + if (ret) { + return ret; + } + + region->vbasedev = vbasedev; + region->flags = info->flags; + region->size = info->size; + region->fd_offset = info->offset; + region->nr = index; + + if (region->size) { + region->mem = g_new0(MemoryRegion, 1); + memory_region_init_io(region->mem, obj, &vfio_region_ops, + region, name, region->size); + + if (!vbasedev->no_mmap && + region->flags & VFIO_REGION_INFO_FLAG_MMAP) { + + ret = vfio_setup_region_sparse_mmaps(region, info); + + if (ret) { + region->nr_mmaps = 1; + region->mmaps = g_new0(VFIOMmap, region->nr_mmaps); + region->mmaps[0].offset = 0; + region->mmaps[0].size = region->size; + } + } + } + + trace_vfio_region_setup(vbasedev->name, index, name, + region->flags, region->fd_offset, region->size); + return 0; +} + +static void vfio_subregion_unmap(VFIORegion *region, int index) +{ + trace_vfio_region_unmap(memory_region_name(®ion->mmaps[index].mem), + region->mmaps[index].offset, + region->mmaps[index].offset + + region->mmaps[index].size - 1); + memory_region_del_subregion(region->mem, ®ion->mmaps[index].mem); + munmap(region->mmaps[index].mmap, region->mmaps[index].size); + object_unparent(OBJECT(®ion->mmaps[index].mem)); + region->mmaps[index].mmap = NULL; +} + +int vfio_region_mmap(VFIORegion *region) +{ + int i, ret, prot = 0; + char *name; + + if (!region->mem) { + return 0; + } + + prot |= region->flags & VFIO_REGION_INFO_FLAG_READ ? PROT_READ : 0; + prot |= region->flags & VFIO_REGION_INFO_FLAG_WRITE ? PROT_WRITE : 0; + + for (i = 0; i < region->nr_mmaps; i++) { + size_t align = MIN(1ULL << ctz64(region->mmaps[i].size), 1 * GiB); + void *map_base, *map_align; + + /* + * Align the mmap for more efficient mapping in the kernel. Ideally + * we'd know the PMD and PUD mapping sizes to use as discrete alignment + * intervals, but we don't. As of Linux v6.12, the largest PUD size + * supporting huge pfnmap is 1GiB (ARCH_SUPPORTS_PUD_PFNMAP is only set + * on x86_64). Align by power-of-two size, capped at 1GiB. + * + * NB. qemu_memalign() and friends actually allocate memory, whereas + * the region size here can exceed host memory, therefore we manually + * create an oversized anonymous mapping and clean it up for alignment. + */ + map_base = mmap(0, region->mmaps[i].size + align, PROT_NONE, + MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + if (map_base == MAP_FAILED) { + ret = -errno; + goto no_mmap; + } + + map_align = (void *)ROUND_UP((uintptr_t)map_base, (uintptr_t)align); + munmap(map_base, map_align - map_base); + munmap(map_align + region->mmaps[i].size, + align - (map_align - map_base)); + + region->mmaps[i].mmap = mmap(map_align, region->mmaps[i].size, prot, + MAP_SHARED | MAP_FIXED, + region->vbasedev->fd, + region->fd_offset + + region->mmaps[i].offset); + if (region->mmaps[i].mmap == MAP_FAILED) { + ret = -errno; + goto no_mmap; + } + + name = g_strdup_printf("%s mmaps[%d]", + memory_region_name(region->mem), i); + memory_region_init_ram_device_ptr(®ion->mmaps[i].mem, + memory_region_owner(region->mem), + name, region->mmaps[i].size, + region->mmaps[i].mmap); + g_free(name); + memory_region_add_subregion(region->mem, region->mmaps[i].offset, + ®ion->mmaps[i].mem); + + trace_vfio_region_mmap(memory_region_name(®ion->mmaps[i].mem), + region->mmaps[i].offset, + region->mmaps[i].offset + + region->mmaps[i].size - 1); + } + + return 0; + +no_mmap: + trace_vfio_region_mmap_fault(memory_region_name(region->mem), i, + region->fd_offset + region->mmaps[i].offset, + region->fd_offset + region->mmaps[i].offset + + region->mmaps[i].size - 1, ret); + + region->mmaps[i].mmap = NULL; + + for (i--; i >= 0; i--) { + vfio_subregion_unmap(region, i); + } + + return ret; +} + +void vfio_region_unmap(VFIORegion *region) +{ + int i; + + if (!region->mem) { + return; + } + + for (i = 0; i < region->nr_mmaps; i++) { + if (region->mmaps[i].mmap) { + vfio_subregion_unmap(region, i); + } + } +} + +void vfio_region_exit(VFIORegion *region) +{ + int i; + + if (!region->mem) { + return; + } + + for (i = 0; i < region->nr_mmaps; i++) { + if (region->mmaps[i].mmap) { + memory_region_del_subregion(region->mem, ®ion->mmaps[i].mem); + } + } + + trace_vfio_region_exit(region->vbasedev->name, region->nr); +} + +void vfio_region_finalize(VFIORegion *region) +{ + int i; + + if (!region->mem) { + return; + } + + for (i = 0; i < region->nr_mmaps; i++) { + if (region->mmaps[i].mmap) { + munmap(region->mmaps[i].mmap, region->mmaps[i].size); + object_unparent(OBJECT(®ion->mmaps[i].mem)); + } + } + + object_unparent(OBJECT(region->mem)); + + g_free(region->mem); + g_free(region->mmaps); + + trace_vfio_region_finalize(region->vbasedev->name, region->nr); + + region->mem = NULL; + region->mmaps = NULL; + region->nr_mmaps = 0; + region->size = 0; + region->flags = 0; + region->nr = 0; +} + +void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled) +{ + int i; + + if (!region->mem) { + return; + } + + for (i = 0; i < region->nr_mmaps; i++) { + if (region->mmaps[i].mmap) { + memory_region_set_enabled(®ion->mmaps[i].mem, enabled); + } + } + + trace_vfio_region_mmaps_set_enabled(memory_region_name(region->mem), + enabled); +} diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 9347e3a5f6..81f4130100 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -90,8 +90,6 @@ vfio_pci_igd_host_bridge_enabled(const char *name) "%s" vfio_pci_igd_lpc_bridge_enabled(const char *name) "%s" # common.c -vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)" -vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64 vfio_iommu_map_notify(const char *op, uint64_t iova_start, uint64_t iova_end) "iommu %s @ 0x%"PRIx64" - 0x%"PRIx64 vfio_listener_region_skip(const char *name, uint64_t start, uint64_t end) "SKIPPING %s 0x%"PRIx64" - 0x%"PRIx64 vfio_spapr_group_attach(int groupfd, int tablefd) "Attached groupfd %d to liobn fd %d" @@ -107,6 +105,15 @@ vfio_disconnect_container(int fd) "close container->fd=%d" vfio_put_group(int fd) "close group->fd=%d" vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" vfio_put_base_device(int fd) "close vdev->fd=%d" +vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x" +vfio_legacy_dma_unmap_overflow_workaround(void) "" +vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 +vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 +vfio_reset_handler(void) "" + +# region.c +vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)" +vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64 vfio_region_setup(const char *dev, int index, const char *name, unsigned long flags, unsigned long offset, unsigned long size) "Device %s, region %d \"%s\", flags: 0x%lx, offset: 0x%lx, size: 0x%lx" vfio_region_mmap_fault(const char *name, int index, unsigned long offset, unsigned long size, int fault) "Region %s mmaps[%d], [0x%lx - 0x%lx], fault: %d" vfio_region_mmap(const char *name, unsigned long offset, unsigned long end) "Region %s [0x%lx - 0x%lx]" @@ -116,11 +123,6 @@ vfio_region_mmaps_set_enabled(const char *name, bool enabled) "Region %s mmaps e vfio_region_unmap(const char *name, unsigned long offset, unsigned long end) "Region %s unmap [0x%lx - 0x%lx]" vfio_region_sparse_mmap_header(const char *name, int index, int nr_areas) "Device %s region %d: %d sparse mmap entries" vfio_region_sparse_mmap_entry(int i, unsigned long start, unsigned long end) "sparse entry %d [0x%lx - 0x%lx]" -vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x" -vfio_legacy_dma_unmap_overflow_workaround(void) "" -vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 -vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 -vfio_reset_handler(void) "" # platform.c vfio_platform_realize(char *name, char *compat) "vfio device %s, compat = %s" diff --git a/hw/vfio/vfio-display.h b/hw/vfio/vfio-display.h index 99b8cb67ef..2606c34b39 100644 --- a/hw/vfio/vfio-display.h +++ b/hw/vfio/vfio-display.h @@ -11,6 +11,7 @@ #include "ui/console.h" #include "hw/display/ramfb.h" +#include "hw/vfio/vfio-region.h" typedef struct VFIODMABuf { QemuDmaBuf *buf; diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 9ec90fbac0..b372033741 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -39,25 +39,6 @@ enum { VFIO_DEVICE_TYPE_CCW = 2, VFIO_DEVICE_TYPE_AP = 3, }; - -typedef struct VFIOMmap { - MemoryRegion mem; - void *mmap; - off_t offset; - size_t size; -} VFIOMmap; - -typedef struct VFIORegion { - struct VFIODevice *vbasedev; - off_t fd_offset; /* offset of region within device fd */ - MemoryRegion *mem; /* slow, read/write access */ - size_t size; - uint32_t flags; /* VFIO region flags (rd/wr/mmap) */ - uint32_t nr_mmaps; - VFIOMmap *mmaps; - uint8_t nr; /* cache the region number for debug */ -} VFIORegion; - struct VFIOGroup; typedef struct VFIOContainer { @@ -168,17 +149,7 @@ void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index); void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index); bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, int action, int fd, Error **errp); -void vfio_region_write(void *opaque, hwaddr addr, - uint64_t data, unsigned size); -uint64_t vfio_region_read(void *opaque, - hwaddr addr, unsigned size); -int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, - int index, const char *name); -int vfio_region_mmap(VFIORegion *region); -void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled); -void vfio_region_unmap(VFIORegion *region); -void vfio_region_exit(VFIORegion *region); -void vfio_region_finalize(VFIORegion *region); + void vfio_reset_handler(void *opaque); struct vfio_device_info *vfio_get_device_info(int fd); bool vfio_device_is_mdev(VFIODevice *vbasedev); @@ -194,7 +165,6 @@ int vfio_kvm_device_del_fd(int fd, Error **errp); bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp); void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer); -extern const MemoryRegionOps vfio_region_ops; typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList; typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList; extern VFIOGroupList vfio_group_list; diff --git a/include/hw/vfio/vfio-platform.h b/include/hw/vfio/vfio-platform.h index c414c3dffc..3191545717 100644 --- a/include/hw/vfio/vfio-platform.h +++ b/include/hw/vfio/vfio-platform.h @@ -47,6 +47,8 @@ typedef struct VFIOINTp { /* function type for user side eventfd handler */ typedef void (*eventfd_user_side_handler_t)(VFIOINTp *intp); +typedef struct VFIORegion VFIORegion; + struct VFIOPlatformDevice { SysBusDevice sbdev; VFIODevice vbasedev; /* not a QOM object */ diff --git a/include/hw/vfio/vfio-region.h b/include/hw/vfio/vfio-region.h new file mode 100644 index 0000000000..cbffb26962 --- /dev/null +++ b/include/hw/vfio/vfio-region.h @@ -0,0 +1,47 @@ +/* + * VFIO region + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_REGION_H +#define HW_VFIO_REGION_H + +#include "system/memory.h" + +typedef struct VFIOMmap { + MemoryRegion mem; + void *mmap; + off_t offset; + size_t size; +} VFIOMmap; + +typedef struct VFIODevice VFIODevice; + +typedef struct VFIORegion { + struct VFIODevice *vbasedev; + off_t fd_offset; /* offset of region within device fd */ + MemoryRegion *mem; /* slow, read/write access */ + size_t size; + uint32_t flags; /* VFIO region flags (rd/wr/mmap) */ + uint32_t nr_mmaps; + VFIOMmap *mmaps; + uint8_t nr; /* cache the region number for debug */ +} VFIORegion; + + +void vfio_region_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size); +uint64_t vfio_region_read(void *opaque, + hwaddr addr, unsigned size); +int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, + int index, const char *name); +int vfio_region_mmap(VFIORegion *region); +void vfio_region_mmaps_set_enabled(VFIORegion *region, bool enabled); +void vfio_region_unmap(VFIORegion *region); +void vfio_region_exit(VFIORegion *region); +void vfio_region_finalize(VFIORegion *region); + +#endif /* HW_VFIO_REGION_H */ From aa90d775f0f568239f2a6bd3ead7e8d3b4a35a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:57 +0100 Subject: [PATCH 0262/2760] vfio: Introduce a new header file for VFIOcontainer declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gather all VFIOcontainer related declarations into "hw/vfio/vfio-container.h" to reduce exposure of VFIO internals in "hw/vfio/vfio-common.h". These declarations were initially introduced in commit 65501a745dba ("vfio: vfio-pci device assignment driver"). They are made available externally for PPC and s390x. Reviewed-by: Zhenzhong Duan Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-12-clg@redhat.com Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-13-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/ppc/spapr_pci_vfio.c | 1 + hw/s390x/s390-pci-vfio.c | 2 +- hw/vfio/container.c | 1 + hw/vfio/spapr.c | 1 + include/hw/vfio/vfio-common.h | 19 ----------------- include/hw/vfio/vfio-container.h | 36 ++++++++++++++++++++++++++++++++ 6 files changed, 40 insertions(+), 20 deletions(-) create mode 100644 include/hw/vfio/vfio-container.h diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index 76b2a3487b..1722a5bfa3 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -25,6 +25,7 @@ #include "hw/pci/msix.h" #include "hw/pci/pci_device.h" #include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-container.h" #include "qemu/error-report.h" #include CONFIG_DEVICES /* CONFIG_VFIO_PCI */ diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index db152a6252..748a51fd8c 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -20,7 +20,7 @@ #include "hw/s390x/s390-pci-clp.h" #include "hw/s390x/s390-pci-vfio.h" #include "hw/vfio/pci.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-container.h" /* * Get the current DMA available count from vfio. Returns true if vfio is diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 812d5edbcf..bda1942662 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -32,6 +32,7 @@ #include "trace.h" #include "qapi/error.h" #include "pci.h" +#include "hw/vfio/vfio-container.h" VFIOGroupList vfio_group_list = QLIST_HEAD_INITIALIZER(vfio_group_list); diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index f21955a344..31e4dddc9e 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -16,6 +16,7 @@ #include "system/address-spaces.h" #include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-container.h" #include "hw/hw.h" #include "system/ram_addr.h" #include "qemu/error-report.h" diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index b372033741..bcdbc9de57 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -39,16 +39,6 @@ enum { VFIO_DEVICE_TYPE_CCW = 2, VFIO_DEVICE_TYPE_AP = 3, }; -struct VFIOGroup; - -typedef struct VFIOContainer { - VFIOContainerBase bcontainer; - int fd; /* /dev/vfio/vfio, empowered by the attached groups */ - unsigned iommu_type; - QLIST_HEAD(, VFIOGroup) group_list; -} VFIOContainer; - -OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_IOMMU_LEGACY); typedef struct VFIODeviceOps VFIODeviceOps; typedef struct VFIOMigration VFIOMigration; @@ -125,15 +115,6 @@ struct VFIODeviceOps { int (*vfio_load_config)(VFIODevice *vdev, QEMUFile *f); }; -typedef struct VFIOGroup { - int fd; - int groupid; - VFIOContainer *container; - QLIST_HEAD(, VFIODevice) device_list; - QLIST_ENTRY(VFIOGroup) next; - QLIST_ENTRY(VFIOGroup) container_next; - bool ram_block_discard_allowed; -} VFIOGroup; #define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio" #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container.h new file mode 100644 index 0000000000..afc498da49 --- /dev/null +++ b/include/hw/vfio/vfio-container.h @@ -0,0 +1,36 @@ +/* + * VFIO container + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_CONTAINER_H +#define HW_VFIO_CONTAINER_H + +#include "hw/vfio/vfio-container-base.h" + +typedef struct VFIOContainer VFIOContainer; +typedef struct VFIODevice VFIODevice; + +typedef struct VFIOGroup { + int fd; + int groupid; + VFIOContainer *container; + QLIST_HEAD(, VFIODevice) device_list; + QLIST_ENTRY(VFIOGroup) next; + QLIST_ENTRY(VFIOGroup) container_next; + bool ram_block_discard_allowed; +} VFIOGroup; + +typedef struct VFIOContainer { + VFIOContainerBase bcontainer; + int fd; /* /dev/vfio/vfio, empowered by the attached groups */ + unsigned iommu_type; + QLIST_HEAD(, VFIOGroup) group_list; +} VFIOContainer; + +OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_IOMMU_LEGACY); + +#endif /* HW_VFIO_CONTAINER_H */ From 0778f9b3bef0438530e1dd0d277571405637d880 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:58 +0100 Subject: [PATCH 0263/2760] vfio: Make vfio_group_list static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_group_list is only used in file "container.c". Reviewed-by: John Levon Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-13-clg@redhat.com Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-14-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 3 ++- include/hw/vfio/vfio-common.h | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index bda1942662..8a216b24b5 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -34,7 +34,8 @@ #include "pci.h" #include "hw/vfio/vfio-container.h" -VFIOGroupList vfio_group_list = +typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList; +static VFIOGroupList vfio_group_list = QLIST_HEAD_INITIALIZER(vfio_group_list); static int vfio_ram_block_discard_disable(VFIOContainer *container, bool state) diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index bcdbc9de57..d806349475 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -146,9 +146,7 @@ int vfio_kvm_device_del_fd(int fd, Error **errp); bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp); void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer); -typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList; typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList; -extern VFIOGroupList vfio_group_list; extern VFIODeviceList vfio_device_list; extern const MemoryListener vfio_memory_listener; extern int vfio_kvm_device_fd; From d158ed092332d8c79193921112ea5e7b5e62cd6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:50:59 +0100 Subject: [PATCH 0264/2760] vfio: Move VFIOAddressSpace helpers into container-base.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VFIOAddressSpace is a common object used by VFIOContainerBase which is declared in "hw/vfio/vfio-container-base.h". Move the VFIOAddressSpace related services into "container-base.c". While at it, rename : vfio_get_address_space -> vfio_address_space_get vfio_put_address_space -> vfio_address_space_put to better reflect the namespace these routines belong to. Reviewed-by: John Levon Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-15-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/ppc/spapr_pci_vfio.c | 5 ++- hw/vfio/common.c | 47 ------------------------- hw/vfio/container-base.c | 50 +++++++++++++++++++++++++++ hw/vfio/container.c | 6 ++-- hw/vfio/iommufd.c | 6 ++-- include/hw/vfio/vfio-common.h | 5 --- include/hw/vfio/vfio-container-base.h | 6 ++++ 7 files changed, 64 insertions(+), 61 deletions(-) diff --git a/hw/ppc/spapr_pci_vfio.c b/hw/ppc/spapr_pci_vfio.c index 1722a5bfa3..e318d0d912 100644 --- a/hw/ppc/spapr_pci_vfio.c +++ b/hw/ppc/spapr_pci_vfio.c @@ -24,7 +24,6 @@ #include "hw/pci-host/spapr.h" #include "hw/pci/msix.h" #include "hw/pci/pci_device.h" -#include "hw/vfio/vfio-common.h" #include "hw/vfio/vfio-container.h" #include "qemu/error-report.h" #include CONFIG_DEVICES /* CONFIG_VFIO_PCI */ @@ -86,7 +85,7 @@ static int vfio_eeh_container_op(VFIOContainer *container, uint32_t op) static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) { - VFIOAddressSpace *space = vfio_get_address_space(as); + VFIOAddressSpace *space = vfio_address_space_get(as); VFIOContainerBase *bcontainer = NULL; if (QLIST_EMPTY(&space->containers)) { @@ -106,7 +105,7 @@ static VFIOContainer *vfio_eeh_as_container(AddressSpace *as) } out: - vfio_put_address_space(space); + vfio_address_space_put(space); return container_of(bcontainer, VFIOContainer, bcontainer); } diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 8f55e7b212..c099d4d24d 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -47,8 +47,6 @@ VFIODeviceList vfio_device_list = QLIST_HEAD_INITIALIZER(vfio_device_list); -static QLIST_HEAD(, VFIOAddressSpace) vfio_address_spaces = - QLIST_HEAD_INITIALIZER(vfio_address_spaces); #ifdef CONFIG_KVM /* @@ -1392,51 +1390,6 @@ int vfio_kvm_device_del_fd(int fd, Error **errp) return 0; } -VFIOAddressSpace *vfio_get_address_space(AddressSpace *as) -{ - VFIOAddressSpace *space; - - QLIST_FOREACH(space, &vfio_address_spaces, list) { - if (space->as == as) { - return space; - } - } - - /* No suitable VFIOAddressSpace, create a new one */ - space = g_malloc0(sizeof(*space)); - space->as = as; - QLIST_INIT(&space->containers); - - if (QLIST_EMPTY(&vfio_address_spaces)) { - qemu_register_reset(vfio_reset_handler, NULL); - } - - QLIST_INSERT_HEAD(&vfio_address_spaces, space, list); - - return space; -} - -void vfio_put_address_space(VFIOAddressSpace *space) -{ - if (!QLIST_EMPTY(&space->containers)) { - return; - } - - QLIST_REMOVE(space, list); - g_free(space); - - if (QLIST_EMPTY(&vfio_address_spaces)) { - qemu_unregister_reset(vfio_reset_handler, NULL); - } -} - -void vfio_address_space_insert(VFIOAddressSpace *space, - VFIOContainerBase *bcontainer) -{ - QLIST_INSERT_HEAD(&space->containers, bcontainer, next); - bcontainer->space = space; -} - struct vfio_device_info *vfio_get_device_info(int fd) { struct vfio_device_info *info; diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 749a3fd29d..2c2d8329e3 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -14,6 +14,56 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/vfio/vfio-container-base.h" +#include "hw/vfio/vfio-common.h" /* vfio_reset_handler */ +#include "system/reset.h" + +static QLIST_HEAD(, VFIOAddressSpace) vfio_address_spaces = + QLIST_HEAD_INITIALIZER(vfio_address_spaces); + +VFIOAddressSpace *vfio_address_space_get(AddressSpace *as) +{ + VFIOAddressSpace *space; + + QLIST_FOREACH(space, &vfio_address_spaces, list) { + if (space->as == as) { + return space; + } + } + + /* No suitable VFIOAddressSpace, create a new one */ + space = g_malloc0(sizeof(*space)); + space->as = as; + QLIST_INIT(&space->containers); + + if (QLIST_EMPTY(&vfio_address_spaces)) { + qemu_register_reset(vfio_reset_handler, NULL); + } + + QLIST_INSERT_HEAD(&vfio_address_spaces, space, list); + + return space; +} + +void vfio_address_space_put(VFIOAddressSpace *space) +{ + if (!QLIST_EMPTY(&space->containers)) { + return; + } + + QLIST_REMOVE(space, list); + g_free(space); + + if (QLIST_EMPTY(&vfio_address_spaces)) { + qemu_unregister_reset(vfio_reset_handler, NULL); + } +} + +void vfio_address_space_insert(VFIOAddressSpace *space, + VFIOContainerBase *bcontainer) +{ + QLIST_INSERT_HEAD(&space->containers, bcontainer, next); + bcontainer->space = space; +} int vfio_container_dma_map(VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 8a216b24b5..cc8cc0f272 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -546,7 +546,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, VFIOAddressSpace *space; VFIOIOMMUClass *vioc; - space = vfio_get_address_space(as); + space = vfio_address_space_get(as); /* * VFIO is currently incompatible with discarding of RAM insofar as the @@ -675,7 +675,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, close(fd); put_space_exit: - vfio_put_address_space(space); + vfio_address_space_put(space); return false; } @@ -714,7 +714,7 @@ static void vfio_disconnect_container(VFIOGroup *group) close(container->fd); object_unref(container); - vfio_put_address_space(space); + vfio_address_space_put(space); } } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 7196c40801..a520d40afc 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -486,7 +486,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, goto err_connect_bind; } - space = vfio_get_address_space(as); + space = vfio_address_space_get(as); /* * The HostIOMMUDevice data from legacy backend is static and doesn't need @@ -606,7 +606,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, err_attach_container: iommufd_cdev_container_destroy(container); err_alloc_ioas: - vfio_put_address_space(space); + vfio_address_space_put(space); iommufd_cdev_unbind_and_disconnect(vbasedev); err_connect_bind: close(vbasedev->fd); @@ -631,7 +631,7 @@ static void iommufd_cdev_detach(VFIODevice *vbasedev) vfio_cpr_unregister_container(bcontainer); iommufd_cdev_detach_container(vbasedev, container); iommufd_cdev_container_destroy(container); - vfio_put_address_space(space); + vfio_address_space_put(space); iommufd_cdev_unbind_and_disconnect(vbasedev); close(vbasedev->fd); diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index d806349475..7d78eb85da 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -120,11 +120,6 @@ struct VFIODeviceOps { #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" -VFIOAddressSpace *vfio_get_address_space(AddressSpace *as); -void vfio_put_address_space(VFIOAddressSpace *space); -void vfio_address_space_insert(VFIOAddressSpace *space, - VFIOContainerBase *bcontainer); - void vfio_disable_irqindex(VFIODevice *vbasedev, int index); void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index); void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index); diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 6aca02fb3d..e0c458d487 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -71,6 +71,11 @@ typedef struct VFIORamDiscardListener { QLIST_ENTRY(VFIORamDiscardListener) next; } VFIORamDiscardListener; +VFIOAddressSpace *vfio_address_space_get(AddressSpace *as); +void vfio_address_space_put(VFIOAddressSpace *space); +void vfio_address_space_insert(VFIOAddressSpace *space, + VFIOContainerBase *bcontainer); + int vfio_container_dma_map(VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly); @@ -163,4 +168,5 @@ struct VFIOIOMMUClass { MemoryRegionSection *section); void (*release)(VFIOContainerBase *bcontainer); }; + #endif /* HW_VFIO_VFIO_CONTAINER_BASE_H */ From 5cf52416e428836b1328ed47173a41948e1643bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:00 +0100 Subject: [PATCH 0265/2760] vfio: Move Host IOMMU type declarations into their respective files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These definitions don't have any use outside of their respective submodules. There is no need to expose them externally. Keep them private. Reviewed-by: Zhenzhong Duan Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-15-clg@redhat.com Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-16-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 2 ++ hw/vfio/iommufd.c | 3 +++ include/hw/vfio/vfio-common.h | 5 ----- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index cc8cc0f272..6ef53ee187 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -34,6 +34,8 @@ #include "pci.h" #include "hw/vfio/vfio-container.h" +#define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio" + typedef QLIST_HEAD(VFIOGroupList, VFIOGroup) VFIOGroupList; static VFIOGroupList vfio_group_list = QLIST_HEAD_INITIALIZER(vfio_group_list); diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index a520d40afc..2ec15bc269 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -27,6 +27,9 @@ #include "pci.h" #include "vfio-iommufd.h" +#define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ + TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" + static int iommufd_cdev_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly) { diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 7d78eb85da..bb11b8215a 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -115,11 +115,6 @@ struct VFIODeviceOps { int (*vfio_load_config)(VFIODevice *vdev, QEMUFile *f); }; - -#define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio" -#define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ - TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" - void vfio_disable_irqindex(VFIODevice *vbasedev, int index); void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index); void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index); From ac28680d5e7a84943cd9f55a4aae245d6d7fdcae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:01 +0100 Subject: [PATCH 0266/2760] vfio: Introduce a new header file for helper services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gather all helper routine declarations into "vfio-helpers.h" to reduce exposure of VFIO internals in "hw/vfio/vfio-common.h". Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-16-clg@redhat.com Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-17-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/s390x/s390-pci-vfio.c | 1 + hw/vfio/common.c | 1 + hw/vfio/container.c | 1 + hw/vfio/helpers.c | 1 + hw/vfio/pci.c | 1 + hw/vfio/region.c | 1 + hw/vfio/vfio-helpers.h | 26 ++++++++++++++++++++++++++ include/hw/vfio/vfio-common.h | 7 ------- 8 files changed, 32 insertions(+), 7 deletions(-) create mode 100644 hw/vfio/vfio-helpers.h diff --git a/hw/s390x/s390-pci-vfio.c b/hw/s390x/s390-pci-vfio.c index 748a51fd8c..aaf91319b4 100644 --- a/hw/s390x/s390-pci-vfio.c +++ b/hw/s390x/s390-pci-vfio.c @@ -21,6 +21,7 @@ #include "hw/s390x/s390-pci-vfio.h" #include "hw/vfio/pci.h" #include "hw/vfio/vfio-container.h" +#include "hw/vfio/vfio-helpers.h" /* * Get the current DMA available count from vfio. Returns true if vfio is diff --git a/hw/vfio/common.c b/hw/vfio/common.c index c099d4d24d..18e5aacbd1 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -44,6 +44,7 @@ #include "system/tcg.h" #include "system/tpm.h" #include "vfio-migration-internal.h" +#include "vfio-helpers.h" VFIODeviceList vfio_device_list = QLIST_HEAD_INITIALIZER(vfio_device_list); diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 6ef53ee187..b2d72f5036 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -33,6 +33,7 @@ #include "qapi/error.h" #include "pci.h" #include "hw/vfio/vfio-container.h" +#include "vfio-helpers.h" #define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio" diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 89403943a7..054ee6e31e 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -30,6 +30,7 @@ #include "qemu/error-report.h" #include "qemu/units.h" #include "monitor/monitor.h" +#include "vfio-helpers.h" /* * Common VFIO interrupt disable diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b0aac2f3a0..bade3b80d7 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -45,6 +45,7 @@ #include "migration/qemu-file.h" #include "system/iommufd.h" #include "vfio-migration-internal.h" +#include "vfio-helpers.h" #define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug" diff --git a/hw/vfio/region.c b/hw/vfio/region.c index 08cd69e704..9049143abf 100644 --- a/hw/vfio/region.c +++ b/hw/vfio/region.c @@ -29,6 +29,7 @@ #include "qemu/error-report.h" #include "qemu/units.h" #include "monitor/monitor.h" +#include "vfio-helpers.h" /* * IO Port/MMIO - Beware of the endians, VFIO is always little endian diff --git a/hw/vfio/vfio-helpers.h b/hw/vfio/vfio-helpers.h new file mode 100644 index 0000000000..d7e4dcba51 --- /dev/null +++ b/hw/vfio/vfio-helpers.h @@ -0,0 +1,26 @@ +/* + * VFIO helpers + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_HELPERS_H +#define HW_VFIO_VFIO_HELPERS_H + +#ifdef CONFIG_LINUX +#include + +struct vfio_info_cap_header * +vfio_get_cap(void *ptr, uint32_t cap_offset, uint16_t id); +struct vfio_info_cap_header * +vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id); +struct vfio_info_cap_header * +vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id); + +#endif + +int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size); + +#endif /* HW_VFIO_VFIO_HELPERS_H */ diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index bb11b8215a..8e465111d2 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -147,17 +147,10 @@ int vfio_get_region_info(VFIODevice *vbasedev, int index, int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, uint32_t subtype, struct vfio_region_info **info); bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type); -struct vfio_info_cap_header * -vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id); bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, unsigned int *avail); -struct vfio_info_cap_header * -vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id); -struct vfio_info_cap_header * -vfio_get_cap(void *ptr, uint32_t cap_offset, uint16_t id); #endif -int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size); bool vfio_devices_all_dirty_tracking_started( const VFIOContainerBase *bcontainer); bool From f6d7f5d02bbb57b5b469863afe89278d64598d81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:02 +0100 Subject: [PATCH 0267/2760] vfio: Move vfio_get_info_dma_avail() into helpers.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_get_info_dma_avail() is a low level routine similar to the other routines extracting capabilities from 'struct vfio_iommu_type1_info'. It belongs to file "helpers.c". Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-17-clg@redhat.com Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-18-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 31 ------------------------------- hw/vfio/helpers.c | 31 +++++++++++++++++++++++++++++++ hw/vfio/vfio-helpers.h | 5 ++++- include/hw/vfio/vfio-common.h | 2 -- 4 files changed, 35 insertions(+), 34 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index b2d72f5036..3fdd5dac37 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -278,37 +278,6 @@ static int vfio_legacy_query_dirty_bitmap(const VFIOContainerBase *bcontainer, return ret; } -static struct vfio_info_cap_header * -vfio_get_iommu_type1_info_cap(struct vfio_iommu_type1_info *info, uint16_t id) -{ - if (!(info->flags & VFIO_IOMMU_INFO_CAPS)) { - return NULL; - } - - return vfio_get_cap((void *)info, info->cap_offset, id); -} - -bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, - unsigned int *avail) -{ - struct vfio_info_cap_header *hdr; - struct vfio_iommu_type1_info_dma_avail *cap; - - /* If the capability cannot be found, assume no DMA limiting */ - hdr = vfio_get_iommu_type1_info_cap(info, - VFIO_IOMMU_TYPE1_INFO_DMA_AVAIL); - if (!hdr) { - return false; - } - - if (avail != NULL) { - cap = (void *) hdr; - *avail = cap->avail; - } - - return true; -} - static bool vfio_get_info_iova_range(struct vfio_iommu_type1_info *info, VFIOContainerBase *bcontainer) { diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 054ee6e31e..1a584ba5f0 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -222,6 +222,37 @@ int vfio_get_region_info(VFIODevice *vbasedev, int index, return 0; } +struct vfio_info_cap_header * +vfio_get_iommu_type1_info_cap(struct vfio_iommu_type1_info *info, uint16_t id) +{ + if (!(info->flags & VFIO_IOMMU_INFO_CAPS)) { + return NULL; + } + + return vfio_get_cap((void *)info, info->cap_offset, id); +} + +bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, + unsigned int *avail) +{ + struct vfio_info_cap_header *hdr; + struct vfio_iommu_type1_info_dma_avail *cap; + + /* If the capability cannot be found, assume no DMA limiting */ + hdr = vfio_get_iommu_type1_info_cap(info, + VFIO_IOMMU_TYPE1_INFO_DMA_AVAIL); + if (!hdr) { + return false; + } + + if (avail != NULL) { + cap = (void *) hdr; + *avail = cap->avail; + } + + return true; +} + int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, uint32_t subtype, struct vfio_region_info **info) { diff --git a/hw/vfio/vfio-helpers.h b/hw/vfio/vfio-helpers.h index d7e4dcba51..9af43878b8 100644 --- a/hw/vfio/vfio-helpers.h +++ b/hw/vfio/vfio-helpers.h @@ -18,7 +18,10 @@ struct vfio_info_cap_header * vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id); struct vfio_info_cap_header * vfio_get_region_info_cap(struct vfio_region_info *info, uint16_t id); - +struct vfio_info_cap_header * +vfio_get_iommu_type1_info_cap(struct vfio_iommu_type1_info *info, uint16_t id); +bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, + unsigned int *avail); #endif int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size); diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 8e465111d2..be2558f7e4 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -147,8 +147,6 @@ int vfio_get_region_info(VFIODevice *vbasedev, int index, int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, uint32_t subtype, struct vfio_region_info **info); bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type); -bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, - unsigned int *avail); #endif bool vfio_devices_all_dirty_tracking_started( From 545256134fdcac6c342f8e7f45eb591e3b12c700 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:03 +0100 Subject: [PATCH 0268/2760] vfio: Move vfio_kvm_device_add/del_fd() to helpers.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_kvm_device_add/del_fd() are low level routines. Move them with the other helpers. Reviewed-by: Zhenzhong Duan Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-18-clg@redhat.com Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-19-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 58 ---------------------------------- hw/vfio/helpers.c | 59 +++++++++++++++++++++++++++++++++++ hw/vfio/iommufd.c | 1 + hw/vfio/meson.build | 2 +- hw/vfio/vfio-helpers.h | 3 ++ include/hw/vfio/vfio-common.h | 3 -- 6 files changed, 64 insertions(+), 62 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 18e5aacbd1..2b3af051cc 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1333,64 +1333,6 @@ void vfio_reset_handler(void *opaque) } } -int vfio_kvm_device_add_fd(int fd, Error **errp) -{ -#ifdef CONFIG_KVM - struct kvm_device_attr attr = { - .group = KVM_DEV_VFIO_FILE, - .attr = KVM_DEV_VFIO_FILE_ADD, - .addr = (uint64_t)(unsigned long)&fd, - }; - - if (!kvm_enabled()) { - return 0; - } - - if (vfio_kvm_device_fd < 0) { - struct kvm_create_device cd = { - .type = KVM_DEV_TYPE_VFIO, - }; - - if (kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd)) { - error_setg_errno(errp, errno, "Failed to create KVM VFIO device"); - return -errno; - } - - vfio_kvm_device_fd = cd.fd; - } - - if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { - error_setg_errno(errp, errno, "Failed to add fd %d to KVM VFIO device", - fd); - return -errno; - } -#endif - return 0; -} - -int vfio_kvm_device_del_fd(int fd, Error **errp) -{ -#ifdef CONFIG_KVM - struct kvm_device_attr attr = { - .group = KVM_DEV_VFIO_FILE, - .attr = KVM_DEV_VFIO_FILE_DEL, - .addr = (uint64_t)(unsigned long)&fd, - }; - - if (vfio_kvm_device_fd < 0) { - error_setg(errp, "KVM VFIO device isn't created yet"); - return -EINVAL; - } - - if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { - error_setg_errno(errp, errno, - "Failed to remove fd %d from KVM VFIO device", fd); - return -errno; - } -#endif - return 0; -} - struct vfio_device_info *vfio_get_device_info(int fd) { struct vfio_device_info *info; diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 1a584ba5f0..e6b75baa80 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -22,6 +22,7 @@ #include "qemu/osdep.h" #include +#include "system/kvm.h" #include "hw/vfio/vfio-common.h" #include "hw/vfio/pci.h" #include "hw/hw.h" @@ -253,6 +254,64 @@ bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, return true; } +int vfio_kvm_device_add_fd(int fd, Error **errp) +{ +#ifdef CONFIG_KVM + struct kvm_device_attr attr = { + .group = KVM_DEV_VFIO_FILE, + .attr = KVM_DEV_VFIO_FILE_ADD, + .addr = (uint64_t)(unsigned long)&fd, + }; + + if (!kvm_enabled()) { + return 0; + } + + if (vfio_kvm_device_fd < 0) { + struct kvm_create_device cd = { + .type = KVM_DEV_TYPE_VFIO, + }; + + if (kvm_vm_ioctl(kvm_state, KVM_CREATE_DEVICE, &cd)) { + error_setg_errno(errp, errno, "Failed to create KVM VFIO device"); + return -errno; + } + + vfio_kvm_device_fd = cd.fd; + } + + if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { + error_setg_errno(errp, errno, "Failed to add fd %d to KVM VFIO device", + fd); + return -errno; + } +#endif + return 0; +} + +int vfio_kvm_device_del_fd(int fd, Error **errp) +{ +#ifdef CONFIG_KVM + struct kvm_device_attr attr = { + .group = KVM_DEV_VFIO_FILE, + .attr = KVM_DEV_VFIO_FILE_DEL, + .addr = (uint64_t)(unsigned long)&fd, + }; + + if (vfio_kvm_device_fd < 0) { + error_setg(errp, "KVM VFIO device isn't created yet"); + return -EINVAL; + } + + if (ioctl(vfio_kvm_device_fd, KVM_SET_DEVICE_ATTR, &attr)) { + error_setg_errno(errp, errno, + "Failed to remove fd %d from KVM VFIO device", fd); + return -errno; + } +#endif + return 0; +} + int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, uint32_t subtype, struct vfio_region_info **info) { diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 2ec15bc269..85b5a8146a 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -26,6 +26,7 @@ #include "qemu/chardev_open.h" #include "pci.h" #include "vfio-iommufd.h" +#include "vfio-helpers.h" #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 07010c7c9e..21795b3d19 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -2,6 +2,7 @@ vfio_ss = ss.source_set() vfio_ss.add(files( 'common.c', 'container.c', + 'helpers.c', )) vfio_ss.add(when: 'CONFIG_PSERIES', if_true: files('spapr.c')) vfio_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( @@ -18,7 +19,6 @@ specific_ss.add_all(when: 'CONFIG_VFIO', if_true: vfio_ss) system_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) system_ss.add(when: 'CONFIG_VFIO_AMD_XGBE', if_true: files('amd-xgbe.c')) system_ss.add(when: 'CONFIG_VFIO', if_true: files( - 'helpers.c', 'container-base.c', 'migration.c', 'migration-multifd.c', diff --git a/hw/vfio/vfio-helpers.h b/hw/vfio/vfio-helpers.h index 9af43878b8..5d91e33d27 100644 --- a/hw/vfio/vfio-helpers.h +++ b/hw/vfio/vfio-helpers.h @@ -26,4 +26,7 @@ bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size); +int vfio_kvm_device_add_fd(int fd, Error **errp); +int vfio_kvm_device_del_fd(int fd, Error **errp); + #endif /* HW_VFIO_VFIO_HELPERS_H */ diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index be2558f7e4..442ec287d7 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -130,9 +130,6 @@ bool vfio_attach_device(char *name, VFIODevice *vbasedev, void vfio_detach_device(VFIODevice *vbasedev); VFIODevice *vfio_get_vfio_device(Object *obj); -int vfio_kvm_device_add_fd(int fd, Error **errp); -int vfio_kvm_device_del_fd(int fd, Error **errp); - bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp); void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer); From 005b8d10450d2d41e9c1bcf8da4085f23cb85b76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:04 +0100 Subject: [PATCH 0269/2760] vfio: Move vfio_get_device_info() to helpers.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_get_device_info() is a low level routine. Move it with the other helpers. Reviewed-by: Zhenzhong Duan Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-19-clg@redhat.com Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-20-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 24 ------------------------ hw/vfio/helpers.c | 24 ++++++++++++++++++++++++ hw/vfio/vfio-helpers.h | 1 + include/hw/vfio/vfio-common.h | 1 - 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 2b3af051cc..f80c0ef229 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1333,30 +1333,6 @@ void vfio_reset_handler(void *opaque) } } -struct vfio_device_info *vfio_get_device_info(int fd) -{ - struct vfio_device_info *info; - uint32_t argsz = sizeof(*info); - - info = g_malloc0(argsz); - -retry: - info->argsz = argsz; - - if (ioctl(fd, VFIO_DEVICE_GET_INFO, info)) { - g_free(info); - return NULL; - } - - if (info->argsz > argsz) { - argsz = info->argsz; - info = g_realloc(info, argsz); - goto retry; - } - - return info; -} - bool vfio_attach_device(char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp) { diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index e6b75baa80..b7f75b47af 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -312,6 +312,30 @@ int vfio_kvm_device_del_fd(int fd, Error **errp) return 0; } +struct vfio_device_info *vfio_get_device_info(int fd) +{ + struct vfio_device_info *info; + uint32_t argsz = sizeof(*info); + + info = g_malloc0(argsz); + +retry: + info->argsz = argsz; + + if (ioctl(fd, VFIO_DEVICE_GET_INFO, info)) { + g_free(info); + return NULL; + } + + if (info->argsz > argsz) { + argsz = info->argsz; + info = g_realloc(info, argsz); + goto retry; + } + + return info; +} + int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, uint32_t subtype, struct vfio_region_info **info) { diff --git a/hw/vfio/vfio-helpers.h b/hw/vfio/vfio-helpers.h index 5d91e33d27..dbcb68bbb0 100644 --- a/hw/vfio/vfio-helpers.h +++ b/hw/vfio/vfio-helpers.h @@ -25,6 +25,7 @@ bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, #endif int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size); +struct vfio_device_info *vfio_get_device_info(int fd); int vfio_kvm_device_add_fd(int fd, Error **errp); int vfio_kvm_device_del_fd(int fd, Error **errp); diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 442ec287d7..dca7a6a4b3 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -122,7 +122,6 @@ bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, int action, int fd, Error **errp); void vfio_reset_handler(void *opaque); -struct vfio_device_info *vfio_get_device_info(int fd); bool vfio_device_is_mdev(VFIODevice *vbasedev); bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp); bool vfio_attach_device(char *name, VFIODevice *vbasedev, From 68c07d76359589c9de2aa190d247afca7eb8cb8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:05 +0100 Subject: [PATCH 0270/2760] vfio: Introduce a new file for VFIODevice definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move all VFIODevice related routines of "helpers.c" into a new "device.c" file. Reviewed-by: Zhenzhong Duan Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-21-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/device.c | 330 +++++++++++++++++++++++++++++++++++++++++++ hw/vfio/helpers.c | 303 --------------------------------------- hw/vfio/meson.build | 1 + hw/vfio/trace-events | 4 +- 4 files changed, 334 insertions(+), 304 deletions(-) create mode 100644 hw/vfio/device.c diff --git a/hw/vfio/device.c b/hw/vfio/device.c new file mode 100644 index 0000000000..21c6824430 --- /dev/null +++ b/hw/vfio/device.c @@ -0,0 +1,330 @@ +/* + * VFIO device + * + * Copyright Red Hat, Inc. 2012 + * + * Authors: + * Alex Williamson + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + * Based on qemu-kvm device-assignment: + * Adapted for KVM by Qumranet. + * Copyright (c) 2007, Neocleus, Alex Novik (alex@neocleus.com) + * Copyright (c) 2007, Neocleus, Guy Zana (guy@neocleus.com) + * Copyright (C) 2008, Qumranet, Amit Shah (amit.shah@qumranet.com) + * Copyright (C) 2008, Red Hat, Amit Shah (amit.shah@redhat.com) + * Copyright (C) 2008, IBM, Muli Ben-Yehuda (muli@il.ibm.com) + */ + +#include "qemu/osdep.h" +#include + +#include "hw/vfio/vfio-common.h" +#include "hw/vfio/pci.h" +#include "hw/hw.h" +#include "trace.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/units.h" +#include "monitor/monitor.h" +#include "vfio-helpers.h" + +/* + * Common VFIO interrupt disable + */ +void vfio_disable_irqindex(VFIODevice *vbasedev, int index) +{ + struct vfio_irq_set irq_set = { + .argsz = sizeof(irq_set), + .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER, + .index = index, + .start = 0, + .count = 0, + }; + + ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); +} + +void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index) +{ + struct vfio_irq_set irq_set = { + .argsz = sizeof(irq_set), + .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK, + .index = index, + .start = 0, + .count = 1, + }; + + ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); +} + +void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index) +{ + struct vfio_irq_set irq_set = { + .argsz = sizeof(irq_set), + .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_MASK, + .index = index, + .start = 0, + .count = 1, + }; + + ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); +} + +static inline const char *action_to_str(int action) +{ + switch (action) { + case VFIO_IRQ_SET_ACTION_MASK: + return "MASK"; + case VFIO_IRQ_SET_ACTION_UNMASK: + return "UNMASK"; + case VFIO_IRQ_SET_ACTION_TRIGGER: + return "TRIGGER"; + default: + return "UNKNOWN ACTION"; + } +} + +static const char *index_to_str(VFIODevice *vbasedev, int index) +{ + if (vbasedev->type != VFIO_DEVICE_TYPE_PCI) { + return NULL; + } + + switch (index) { + case VFIO_PCI_INTX_IRQ_INDEX: + return "INTX"; + case VFIO_PCI_MSI_IRQ_INDEX: + return "MSI"; + case VFIO_PCI_MSIX_IRQ_INDEX: + return "MSIX"; + case VFIO_PCI_ERR_IRQ_INDEX: + return "ERR"; + case VFIO_PCI_REQ_IRQ_INDEX: + return "REQ"; + default: + return NULL; + } +} + +bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, + int action, int fd, Error **errp) +{ + ERRP_GUARD(); + g_autofree struct vfio_irq_set *irq_set = NULL; + int argsz; + const char *name; + int32_t *pfd; + + argsz = sizeof(*irq_set) + sizeof(*pfd); + + irq_set = g_malloc0(argsz); + irq_set->argsz = argsz; + irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | action; + irq_set->index = index; + irq_set->start = subindex; + irq_set->count = 1; + pfd = (int32_t *)&irq_set->data; + *pfd = fd; + + if (!ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) { + return true; + } + + error_setg_errno(errp, errno, "VFIO_DEVICE_SET_IRQS failure"); + + name = index_to_str(vbasedev, index); + if (name) { + error_prepend(errp, "%s-%d: ", name, subindex); + } else { + error_prepend(errp, "index %d-%d: ", index, subindex); + } + error_prepend(errp, + "Failed to %s %s eventfd signaling for interrupt ", + fd < 0 ? "tear down" : "set up", action_to_str(action)); + return false; +} + +int vfio_get_region_info(VFIODevice *vbasedev, int index, + struct vfio_region_info **info) +{ + size_t argsz = sizeof(struct vfio_region_info); + + *info = g_malloc0(argsz); + + (*info)->index = index; +retry: + (*info)->argsz = argsz; + + if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) { + g_free(*info); + *info = NULL; + return -errno; + } + + if ((*info)->argsz > argsz) { + argsz = (*info)->argsz; + *info = g_realloc(*info, argsz); + + goto retry; + } + + return 0; +} + +int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, + uint32_t subtype, struct vfio_region_info **info) +{ + int i; + + for (i = 0; i < vbasedev->num_regions; i++) { + struct vfio_info_cap_header *hdr; + struct vfio_region_info_cap_type *cap_type; + + if (vfio_get_region_info(vbasedev, i, info)) { + continue; + } + + hdr = vfio_get_region_info_cap(*info, VFIO_REGION_INFO_CAP_TYPE); + if (!hdr) { + g_free(*info); + continue; + } + + cap_type = container_of(hdr, struct vfio_region_info_cap_type, header); + + trace_vfio_get_dev_region(vbasedev->name, i, + cap_type->type, cap_type->subtype); + + if (cap_type->type == type && cap_type->subtype == subtype) { + return 0; + } + + g_free(*info); + } + + *info = NULL; + return -ENODEV; +} + +bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) +{ + g_autofree struct vfio_region_info *info = NULL; + bool ret = false; + + if (!vfio_get_region_info(vbasedev, region, &info)) { + if (vfio_get_region_info_cap(info, cap_type)) { + ret = true; + } + } + + return ret; +} + +bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp) +{ + ERRP_GUARD(); + struct stat st; + + if (vbasedev->fd < 0) { + if (stat(vbasedev->sysfsdev, &st) < 0) { + error_setg_errno(errp, errno, "no such host device"); + error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->sysfsdev); + return false; + } + /* User may specify a name, e.g: VFIO platform device */ + if (!vbasedev->name) { + vbasedev->name = g_path_get_basename(vbasedev->sysfsdev); + } + } else { + if (!vbasedev->iommufd) { + error_setg(errp, "Use FD passing only with iommufd backend"); + return false; + } + /* + * Give a name with fd so any function printing out vbasedev->name + * will not break. + */ + if (!vbasedev->name) { + vbasedev->name = g_strdup_printf("VFIO_FD%d", vbasedev->fd); + } + } + + return true; +} + +void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp) +{ + ERRP_GUARD(); + int fd = monitor_fd_param(monitor_cur(), str, errp); + + if (fd < 0) { + error_prepend(errp, "Could not parse remote object fd %s:", str); + return; + } + vbasedev->fd = fd; +} + +void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops, + DeviceState *dev, bool ram_discard) +{ + vbasedev->type = type; + vbasedev->ops = ops; + vbasedev->dev = dev; + vbasedev->fd = -1; + + vbasedev->ram_block_discard_allowed = ram_discard; +} + +int vfio_device_get_aw_bits(VFIODevice *vdev) +{ + /* + * iova_ranges is a sorted list. For old kernels that support + * VFIO but not support query of iova ranges, iova_ranges is NULL, + * in this case HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX(64) is returned. + */ + GList *l = g_list_last(vdev->bcontainer->iova_ranges); + + if (l) { + Range *range = l->data; + return range_get_last_bit(range) + 1; + } + + return HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX; +} + +bool vfio_device_is_mdev(VFIODevice *vbasedev) +{ + g_autofree char *subsys = NULL; + g_autofree char *tmp = NULL; + + if (!vbasedev->sysfsdev) { + return false; + } + + tmp = g_strdup_printf("%s/subsystem", vbasedev->sysfsdev); + subsys = realpath(tmp, NULL); + return subsys && (strcmp(subsys, "/sys/bus/mdev") == 0); +} + +bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp) +{ + HostIOMMUDevice *hiod = vbasedev->hiod; + + if (!hiod) { + return true; + } + + return HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp); +} + +VFIODevice *vfio_get_vfio_device(Object *obj) +{ + if (object_dynamic_cast(obj, TYPE_VFIO_PCI)) { + return &VFIO_PCI(obj)->vbasedev; + } else { + return NULL; + } +} diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index b7f75b47af..7ddc9797ef 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -24,131 +24,10 @@ #include "system/kvm.h" #include "hw/vfio/vfio-common.h" -#include "hw/vfio/pci.h" #include "hw/hw.h" -#include "trace.h" #include "qapi/error.h" -#include "qemu/error-report.h" -#include "qemu/units.h" -#include "monitor/monitor.h" #include "vfio-helpers.h" -/* - * Common VFIO interrupt disable - */ -void vfio_disable_irqindex(VFIODevice *vbasedev, int index) -{ - struct vfio_irq_set irq_set = { - .argsz = sizeof(irq_set), - .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_TRIGGER, - .index = index, - .start = 0, - .count = 0, - }; - - ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); -} - -void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index) -{ - struct vfio_irq_set irq_set = { - .argsz = sizeof(irq_set), - .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_UNMASK, - .index = index, - .start = 0, - .count = 1, - }; - - ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); -} - -void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index) -{ - struct vfio_irq_set irq_set = { - .argsz = sizeof(irq_set), - .flags = VFIO_IRQ_SET_DATA_NONE | VFIO_IRQ_SET_ACTION_MASK, - .index = index, - .start = 0, - .count = 1, - }; - - ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); -} - -static inline const char *action_to_str(int action) -{ - switch (action) { - case VFIO_IRQ_SET_ACTION_MASK: - return "MASK"; - case VFIO_IRQ_SET_ACTION_UNMASK: - return "UNMASK"; - case VFIO_IRQ_SET_ACTION_TRIGGER: - return "TRIGGER"; - default: - return "UNKNOWN ACTION"; - } -} - -static const char *index_to_str(VFIODevice *vbasedev, int index) -{ - if (vbasedev->type != VFIO_DEVICE_TYPE_PCI) { - return NULL; - } - - switch (index) { - case VFIO_PCI_INTX_IRQ_INDEX: - return "INTX"; - case VFIO_PCI_MSI_IRQ_INDEX: - return "MSI"; - case VFIO_PCI_MSIX_IRQ_INDEX: - return "MSIX"; - case VFIO_PCI_ERR_IRQ_INDEX: - return "ERR"; - case VFIO_PCI_REQ_IRQ_INDEX: - return "REQ"; - default: - return NULL; - } -} - -bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, - int action, int fd, Error **errp) -{ - ERRP_GUARD(); - g_autofree struct vfio_irq_set *irq_set = NULL; - int argsz; - const char *name; - int32_t *pfd; - - argsz = sizeof(*irq_set) + sizeof(*pfd); - - irq_set = g_malloc0(argsz); - irq_set->argsz = argsz; - irq_set->flags = VFIO_IRQ_SET_DATA_EVENTFD | action; - irq_set->index = index; - irq_set->start = subindex; - irq_set->count = 1; - pfd = (int32_t *)&irq_set->data; - *pfd = fd; - - if (!ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, irq_set)) { - return true; - } - - error_setg_errno(errp, errno, "VFIO_DEVICE_SET_IRQS failure"); - - name = index_to_str(vbasedev, index); - if (name) { - error_prepend(errp, "%s-%d: ", name, subindex); - } else { - error_prepend(errp, "index %d-%d: ", index, subindex); - } - error_prepend(errp, - "Failed to %s %s eventfd signaling for interrupt ", - fd < 0 ? "tear down" : "set up", action_to_str(action)); - return false; -} - int vfio_bitmap_alloc(VFIOBitmap *vbmap, hwaddr size) { vbmap->pages = REAL_HOST_PAGE_ALIGN(size) / qemu_real_host_page_size(); @@ -196,33 +75,6 @@ vfio_get_device_info_cap(struct vfio_device_info *info, uint16_t id) return vfio_get_cap((void *)info, info->cap_offset, id); } -int vfio_get_region_info(VFIODevice *vbasedev, int index, - struct vfio_region_info **info) -{ - size_t argsz = sizeof(struct vfio_region_info); - - *info = g_malloc0(argsz); - - (*info)->index = index; -retry: - (*info)->argsz = argsz; - - if (ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, *info)) { - g_free(*info); - *info = NULL; - return -errno; - } - - if ((*info)->argsz > argsz) { - argsz = (*info)->argsz; - *info = g_realloc(*info, argsz); - - goto retry; - } - - return 0; -} - struct vfio_info_cap_header * vfio_get_iommu_type1_info_cap(struct vfio_iommu_type1_info *info, uint16_t id) { @@ -335,158 +187,3 @@ struct vfio_device_info *vfio_get_device_info(int fd) return info; } - -int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, - uint32_t subtype, struct vfio_region_info **info) -{ - int i; - - for (i = 0; i < vbasedev->num_regions; i++) { - struct vfio_info_cap_header *hdr; - struct vfio_region_info_cap_type *cap_type; - - if (vfio_get_region_info(vbasedev, i, info)) { - continue; - } - - hdr = vfio_get_region_info_cap(*info, VFIO_REGION_INFO_CAP_TYPE); - if (!hdr) { - g_free(*info); - continue; - } - - cap_type = container_of(hdr, struct vfio_region_info_cap_type, header); - - trace_vfio_get_dev_region(vbasedev->name, i, - cap_type->type, cap_type->subtype); - - if (cap_type->type == type && cap_type->subtype == subtype) { - return 0; - } - - g_free(*info); - } - - *info = NULL; - return -ENODEV; -} - -bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) -{ - g_autofree struct vfio_region_info *info = NULL; - bool ret = false; - - if (!vfio_get_region_info(vbasedev, region, &info)) { - if (vfio_get_region_info_cap(info, cap_type)) { - ret = true; - } - } - - return ret; -} - -bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp) -{ - ERRP_GUARD(); - struct stat st; - - if (vbasedev->fd < 0) { - if (stat(vbasedev->sysfsdev, &st) < 0) { - error_setg_errno(errp, errno, "no such host device"); - error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->sysfsdev); - return false; - } - /* User may specify a name, e.g: VFIO platform device */ - if (!vbasedev->name) { - vbasedev->name = g_path_get_basename(vbasedev->sysfsdev); - } - } else { - if (!vbasedev->iommufd) { - error_setg(errp, "Use FD passing only with iommufd backend"); - return false; - } - /* - * Give a name with fd so any function printing out vbasedev->name - * will not break. - */ - if (!vbasedev->name) { - vbasedev->name = g_strdup_printf("VFIO_FD%d", vbasedev->fd); - } - } - - return true; -} - -void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp) -{ - ERRP_GUARD(); - int fd = monitor_fd_param(monitor_cur(), str, errp); - - if (fd < 0) { - error_prepend(errp, "Could not parse remote object fd %s:", str); - return; - } - vbasedev->fd = fd; -} - -void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops, - DeviceState *dev, bool ram_discard) -{ - vbasedev->type = type; - vbasedev->ops = ops; - vbasedev->dev = dev; - vbasedev->fd = -1; - - vbasedev->ram_block_discard_allowed = ram_discard; -} - -int vfio_device_get_aw_bits(VFIODevice *vdev) -{ - /* - * iova_ranges is a sorted list. For old kernels that support - * VFIO but not support query of iova ranges, iova_ranges is NULL, - * in this case HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX(64) is returned. - */ - GList *l = g_list_last(vdev->bcontainer->iova_ranges); - - if (l) { - Range *range = l->data; - return range_get_last_bit(range) + 1; - } - - return HOST_IOMMU_DEVICE_CAP_AW_BITS_MAX; -} - -bool vfio_device_is_mdev(VFIODevice *vbasedev) -{ - g_autofree char *subsys = NULL; - g_autofree char *tmp = NULL; - - if (!vbasedev->sysfsdev) { - return false; - } - - tmp = g_strdup_printf("%s/subsystem", vbasedev->sysfsdev); - subsys = realpath(tmp, NULL); - return subsys && (strcmp(subsys, "/sys/bus/mdev") == 0); -} - -bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp) -{ - HostIOMMUDevice *hiod = vbasedev->hiod; - - if (!hiod) { - return true; - } - - return HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp); -} - -VFIODevice *vfio_get_vfio_device(Object *obj) -{ - if (object_dynamic_cast(obj, TYPE_VFIO_PCI)) { - return &VFIO_PCI(obj)->vbasedev; - } else { - return NULL; - } -} diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 21795b3d19..60caa36617 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -20,6 +20,7 @@ system_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) system_ss.add(when: 'CONFIG_VFIO_AMD_XGBE', if_true: files('amd-xgbe.c')) system_ss.add(when: 'CONFIG_VFIO', if_true: files( 'container-base.c', + 'device.c', 'migration.c', 'migration-multifd.c', 'cpr.c', diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 81f4130100..590d9674cf 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -105,7 +105,6 @@ vfio_disconnect_container(int fd) "close container->fd=%d" vfio_put_group(int fd) "close group->fd=%d" vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" vfio_put_base_device(int fd) "close vdev->fd=%d" -vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x" vfio_legacy_dma_unmap_overflow_workaround(void) "" vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 @@ -194,3 +193,6 @@ iommufd_cdev_fail_attach_existing_container(const char *msg) " %s" iommufd_cdev_alloc_ioas(int iommufd, int ioas_id) " [iommufd=%d] new IOMMUFD container with ioasid=%d" iommufd_cdev_device_info(char *name, int devfd, int num_irqs, int num_regions, int flags) " %s (%d) num_irqs=%d num_regions=%d flags=%d" iommufd_cdev_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int dev_id) "\t%04x:%02x:%02x.%x devid %d" + +# device.c +vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x" From 8140d45b108c2b48960bbfb0a215f586f2d1d9e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:06 +0100 Subject: [PATCH 0271/2760] vfio: Introduce new files for CPR definitions and declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gather all CPR related declarations into "vfio-cpr.h" to reduce exposure of VFIO internals in "hw/vfio/vfio-common.h". These were introduced in commit d9fa4223b30a ("vfio: register container for cpr"). Order file list in meson.build while at it. Cc: Steve Sistare Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-22-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 1 + hw/vfio/cpr.c | 1 + hw/vfio/iommufd.c | 1 + hw/vfio/meson.build | 2 +- hw/vfio/vfio-cpr.h | 15 +++++++++++++++ include/hw/vfio/vfio-common.h | 3 --- 6 files changed, 19 insertions(+), 4 deletions(-) create mode 100644 hw/vfio/vfio-cpr.h diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 3fdd5dac37..c55fe8e4be 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -34,6 +34,7 @@ #include "pci.h" #include "hw/vfio/vfio-container.h" #include "vfio-helpers.h" +#include "vfio-cpr.h" #define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio" diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index 3d1c8d290a..696987006b 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -10,6 +10,7 @@ #include "migration/misc.h" #include "qapi/error.h" #include "system/runstate.h" +#include "vfio-cpr.h" static int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier, MigrationEvent *e, Error **errp) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 85b5a8146a..a5bd189a86 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -27,6 +27,7 @@ #include "pci.h" #include "vfio-iommufd.h" #include "vfio-helpers.h" +#include "vfio-cpr.h" #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 60caa36617..1f89bd28c1 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -20,10 +20,10 @@ system_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) system_ss.add(when: 'CONFIG_VFIO_AMD_XGBE', if_true: files('amd-xgbe.c')) system_ss.add(when: 'CONFIG_VFIO', if_true: files( 'container-base.c', + 'cpr.c', 'device.c', 'migration.c', 'migration-multifd.c', - 'cpr.c', 'region.c', )) system_ss.add(when: ['CONFIG_VFIO', 'CONFIG_IOMMUFD'], if_true: files( diff --git a/hw/vfio/vfio-cpr.h b/hw/vfio/vfio-cpr.h new file mode 100644 index 0000000000..134b83a624 --- /dev/null +++ b/hw/vfio/vfio-cpr.h @@ -0,0 +1,15 @@ +/* + * VFIO CPR + * + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_CPR_H +#define HW_VFIO_CPR_H + +bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp); +void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer); + +#endif /* HW_VFIO_CPR_H */ diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index dca7a6a4b3..2065c2f9e4 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -129,9 +129,6 @@ bool vfio_attach_device(char *name, VFIODevice *vbasedev, void vfio_detach_device(VFIODevice *vbasedev); VFIODevice *vfio_get_vfio_device(Object *obj); -bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp); -void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer); - typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList; extern VFIODeviceList vfio_device_list; extern const MemoryListener vfio_memory_listener; From c3fbdba15a8b3d80c1f5e6026e636030b3692437 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:07 +0100 Subject: [PATCH 0272/2760] vfio: Move vfio_kvm_device_fd() into helpers.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vfio_kvm_device_add/del_fd() routines opening the VFIO pseudo device are defined in "helpers.c". Move 'vfio_kvm_device_fd' definition there and its declaration into "vfio-helpers.h" to reduce exposure of VFIO internals in "hw/vfio/vfio-common.h". Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-22-clg@redhat.com Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-23-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 11 ----------- hw/vfio/helpers.c | 11 +++++++++++ hw/vfio/spapr.c | 2 +- hw/vfio/vfio-helpers.h | 2 ++ include/hw/vfio/vfio-common.h | 1 - 5 files changed, 14 insertions(+), 13 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index f80c0ef229..84a9a37d9d 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -49,17 +49,6 @@ VFIODeviceList vfio_device_list = QLIST_HEAD_INITIALIZER(vfio_device_list); -#ifdef CONFIG_KVM -/* - * We have a single VFIO pseudo device per KVM VM. Once created it lives - * for the life of the VM. Closing the file descriptor only drops our - * reference to it and the device's reference to kvm. Therefore once - * initialized, this file descriptor is only released on QEMU exit and - * we'll re-use it should another vfio device be attached before then. - */ -int vfio_kvm_device_fd = -1; -#endif - /* * Device state interfaces */ diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 7ddc9797ef..48bd61d528 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -106,6 +106,17 @@ bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, return true; } +#ifdef CONFIG_KVM +/* + * We have a single VFIO pseudo device per KVM VM. Once created it lives + * for the life of the VM. Closing the file descriptor only drops our + * reference to it and the device's reference to kvm. Therefore once + * initialized, this file descriptor is only released on QEMU exit and + * we'll re-use it should another vfio device be attached before then. + */ +int vfio_kvm_device_fd = -1; +#endif + int vfio_kvm_device_add_fd(int fd, Error **errp) { #ifdef CONFIG_KVM diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 31e4dddc9e..95ccbad418 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -15,13 +15,13 @@ #include "system/hostmem.h" #include "system/address-spaces.h" -#include "hw/vfio/vfio-common.h" #include "hw/vfio/vfio-container.h" #include "hw/hw.h" #include "system/ram_addr.h" #include "qemu/error-report.h" #include "qapi/error.h" #include "trace.h" +#include "vfio-helpers.h" typedef struct VFIOHostDMAWindow { hwaddr min_iova; diff --git a/hw/vfio/vfio-helpers.h b/hw/vfio/vfio-helpers.h index dbcb68bbb0..54a327ffbc 100644 --- a/hw/vfio/vfio-helpers.h +++ b/hw/vfio/vfio-helpers.h @@ -12,6 +12,8 @@ #ifdef CONFIG_LINUX #include +extern int vfio_kvm_device_fd; + struct vfio_info_cap_header * vfio_get_cap(void *ptr, uint32_t cap_offset, uint16_t id); struct vfio_info_cap_header * diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 2065c2f9e4..06178cf282 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -132,7 +132,6 @@ VFIODevice *vfio_get_vfio_device(Object *obj); typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList; extern VFIODeviceList vfio_device_list; extern const MemoryListener vfio_memory_listener; -extern int vfio_kvm_device_fd; #ifdef CONFIG_LINUX int vfio_get_region_info(VFIODevice *vbasedev, int index, From a997b506e715d96549869e2d0fca28a1a9f110dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:08 +0100 Subject: [PATCH 0273/2760] vfio: Move vfio_device_list into device.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'vfio_device_list' is VFIODevice related. Move its definitions into "device.c". Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-23-clg@redhat.com Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-24-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 3 --- hw/vfio/device.c | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 84a9a37d9d..4e7d8e83ac 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -46,9 +46,6 @@ #include "vfio-migration-internal.h" #include "vfio-helpers.h" -VFIODeviceList vfio_device_list = - QLIST_HEAD_INITIALIZER(vfio_device_list); - /* * Device state interfaces */ diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 21c6824430..25fdba10a8 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -31,6 +31,9 @@ #include "monitor/monitor.h" #include "vfio-helpers.h" +VFIODeviceList vfio_device_list = + QLIST_HEAD_INITIALIZER(vfio_device_list); + /* * Common VFIO interrupt disable */ From 923b11411e0e68b460f6fd8e6a7f8ff11554e48c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:09 +0100 Subject: [PATCH 0274/2760] vfio: Move vfio_de/attach_device() into device.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These routines are VFIODevice related. Move their definitions into "device.c". Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-24-clg@redhat.com Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-25-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 37 ------------------------------------- hw/vfio/device.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 37 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 4e7d8e83ac..a85ed36409 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1318,40 +1318,3 @@ void vfio_reset_handler(void *opaque) } } } - -bool vfio_attach_device(char *name, VFIODevice *vbasedev, - AddressSpace *as, Error **errp) -{ - const VFIOIOMMUClass *ops = - VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_LEGACY)); - HostIOMMUDevice *hiod = NULL; - - if (vbasedev->iommufd) { - ops = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD)); - } - - assert(ops); - - - if (!vbasedev->mdev) { - hiod = HOST_IOMMU_DEVICE(object_new(ops->hiod_typename)); - vbasedev->hiod = hiod; - } - - if (!ops->attach_device(name, vbasedev, as, errp)) { - object_unref(hiod); - vbasedev->hiod = NULL; - return false; - } - - return true; -} - -void vfio_detach_device(VFIODevice *vbasedev) -{ - if (!vbasedev->bcontainer) { - return; - } - object_unref(vbasedev->hiod); - VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer)->detach_device(vbasedev); -} diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 25fdba10a8..179c9fb8de 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -331,3 +331,40 @@ VFIODevice *vfio_get_vfio_device(Object *obj) return NULL; } } + +bool vfio_attach_device(char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp) +{ + const VFIOIOMMUClass *ops = + VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_LEGACY)); + HostIOMMUDevice *hiod = NULL; + + if (vbasedev->iommufd) { + ops = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD)); + } + + assert(ops); + + + if (!vbasedev->mdev) { + hiod = HOST_IOMMU_DEVICE(object_new(ops->hiod_typename)); + vbasedev->hiod = hiod; + } + + if (!ops->attach_device(name, vbasedev, as, errp)) { + object_unref(hiod); + vbasedev->hiod = NULL; + return false; + } + + return true; +} + +void vfio_detach_device(VFIODevice *vbasedev) +{ + if (!vbasedev->bcontainer) { + return; + } + object_unref(vbasedev->hiod); + VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer)->detach_device(vbasedev); +} From 819a5865c0524c4bfcf1906955c9d15952fdbcc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:10 +0100 Subject: [PATCH 0275/2760] vfio: Move vfio_reset_handler() into device.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass-through devices of a VM are not necessarily in the same group and all groups/address_spaces need to be scanned when the machine is reset. Commit f16f39c3fc97 ("Implement PCI hot reset") introduced a VM reset handler for this purpose. Move it under device.c Also reintroduce the comment which explained the context and was lost along the way. Reviewed-by: Zhenzhong Duan Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-26-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 18 ------------------ hw/vfio/device.c | 35 +++++++++++++++++++++++++++++++++++ hw/vfio/trace-events | 2 +- 3 files changed, 36 insertions(+), 19 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index a85ed36409..45ac8b7d8b 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1300,21 +1300,3 @@ const MemoryListener vfio_memory_listener = { .log_global_stop = vfio_listener_log_global_stop, .log_sync = vfio_listener_log_sync, }; - -void vfio_reset_handler(void *opaque) -{ - VFIODevice *vbasedev; - - trace_vfio_reset_handler(); - QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { - if (vbasedev->dev->realized) { - vbasedev->ops->vfio_compute_needs_reset(vbasedev); - } - } - - QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { - if (vbasedev->dev->realized && vbasedev->needs_reset) { - vbasedev->ops->vfio_hot_reset_multi(vbasedev); - } - } -} diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 179c9fb8de..e122c797c2 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -34,6 +34,41 @@ VFIODeviceList vfio_device_list = QLIST_HEAD_INITIALIZER(vfio_device_list); +/* + * We want to differentiate hot reset of multiple in-use devices vs + * hot reset of a single in-use device. VFIO_DEVICE_RESET will already + * handle the case of doing hot resets when there is only a single + * device per bus. The in-use here refers to how many VFIODevices are + * affected. A hot reset that affects multiple devices, but only a + * single in-use device, means that we can call it from our bus + * ->reset() callback since the extent is effectively a single + * device. This allows us to make use of it in the hotplug path. When + * there are multiple in-use devices, we can only trigger the hot + * reset during a system reset and thus from our reset handler. We + * separate _one vs _multi here so that we don't overlap and do a + * double reset on the system reset path where both our reset handler + * and ->reset() callback are used. Calling _one() will only do a hot + * reset for the one in-use devices case, calling _multi() will do + * nothing if a _one() would have been sufficient. + */ +void vfio_reset_handler(void *opaque) +{ + VFIODevice *vbasedev; + + trace_vfio_reset_handler(); + QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->dev->realized) { + vbasedev->ops->vfio_compute_needs_reset(vbasedev); + } + } + + QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { + if (vbasedev->dev->realized && vbasedev->needs_reset) { + vbasedev->ops->vfio_hot_reset_multi(vbasedev); + } + } +} + /* * Common VFIO interrupt disable */ diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 590d9674cf..9fee7df876 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -108,7 +108,6 @@ vfio_put_base_device(int fd) "close vdev->fd=%d" vfio_legacy_dma_unmap_overflow_workaround(void) "" vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 -vfio_reset_handler(void) "" # region.c vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)" @@ -196,3 +195,4 @@ iommufd_cdev_pci_hot_reset_dep_devices(int domain, int bus, int slot, int functi # device.c vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x" +vfio_reset_handler(void) "" From 6b7c812972a697c412f81497999af267e0c6333a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:11 +0100 Subject: [PATCH 0276/2760] vfio: Move dirty tracking related services into container-base.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Routines of common.c : vfio_devices_all_dirty_tracking_started vfio_devices_all_device_dirty_tracking vfio_devices_query_dirty_bitmap vfio_get_dirty_bitmap are all related to dirty page tracking directly at the container level or at the container device level. Naming is a bit confusing. We will propose new names in the following changes. Reviewed-by: Joao Martins Reviewed-by: Avihai Horon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-27-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 130 ------------------------ hw/vfio/container-base.c | 138 ++++++++++++++++++++++++++ hw/vfio/meson.build | 2 +- hw/vfio/trace-events | 4 +- include/hw/vfio/vfio-common.h | 9 -- include/hw/vfio/vfio-container-base.h | 7 ++ 6 files changed, 149 insertions(+), 141 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 45ac8b7d8b..ed49ef88a8 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -51,27 +51,6 @@ */ -static bool vfio_devices_all_device_dirty_tracking_started( - const VFIOContainerBase *bcontainer) -{ - VFIODevice *vbasedev; - - QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { - if (!vbasedev->dirty_tracking) { - return false; - } - } - - return true; -} - -bool vfio_devices_all_dirty_tracking_started( - const VFIOContainerBase *bcontainer) -{ - return vfio_devices_all_device_dirty_tracking_started(bcontainer) || - bcontainer->dirty_pages_started; -} - static bool vfio_log_sync_needed(const VFIOContainerBase *bcontainer) { VFIODevice *vbasedev; @@ -96,22 +75,6 @@ static bool vfio_log_sync_needed(const VFIOContainerBase *bcontainer) return true; } -bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer) -{ - VFIODevice *vbasedev; - - QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { - if (vbasedev->device_dirty_page_tracking == ON_OFF_AUTO_OFF) { - return false; - } - if (!vbasedev->dirty_pages_supported) { - return false; - } - } - - return true; -} - static bool vfio_listener_skipped_section(MemoryRegionSection *section) { return (!memory_region_is_ram(section->mr) && @@ -1009,99 +972,6 @@ static void vfio_listener_log_global_stop(MemoryListener *listener) } } -static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova, - hwaddr size, void *bitmap) -{ - uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) + - sizeof(struct vfio_device_feature_dma_logging_report), - sizeof(uint64_t))] = {}; - struct vfio_device_feature *feature = (struct vfio_device_feature *)buf; - struct vfio_device_feature_dma_logging_report *report = - (struct vfio_device_feature_dma_logging_report *)feature->data; - - report->iova = iova; - report->length = size; - report->page_size = qemu_real_host_page_size(); - report->bitmap = (uintptr_t)bitmap; - - feature->argsz = sizeof(buf); - feature->flags = VFIO_DEVICE_FEATURE_GET | - VFIO_DEVICE_FEATURE_DMA_LOGGING_REPORT; - - if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) { - return -errno; - } - - return 0; -} - -int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) -{ - VFIODevice *vbasedev; - int ret; - - QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { - ret = vfio_device_dma_logging_report(vbasedev, iova, size, - vbmap->bitmap); - if (ret) { - error_setg_errno(errp, -ret, - "%s: Failed to get DMA logging report, iova: " - "0x%" HWADDR_PRIx ", size: 0x%" HWADDR_PRIx, - vbasedev->name, iova, size); - - return ret; - } - } - - return 0; -} - -int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, - uint64_t size, ram_addr_t ram_addr, Error **errp) -{ - bool all_device_dirty_tracking = - vfio_devices_all_device_dirty_tracking(bcontainer); - uint64_t dirty_pages; - VFIOBitmap vbmap; - int ret; - - if (!bcontainer->dirty_pages_supported && !all_device_dirty_tracking) { - cpu_physical_memory_set_dirty_range(ram_addr, size, - tcg_enabled() ? DIRTY_CLIENTS_ALL : - DIRTY_CLIENTS_NOCODE); - return 0; - } - - ret = vfio_bitmap_alloc(&vbmap, size); - if (ret) { - error_setg_errno(errp, -ret, - "Failed to allocate dirty tracking bitmap"); - return ret; - } - - if (all_device_dirty_tracking) { - ret = vfio_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size, - errp); - } else { - ret = vfio_container_query_dirty_bitmap(bcontainer, &vbmap, iova, size, - errp); - } - - if (ret) { - goto out; - } - - dirty_pages = cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, ram_addr, - vbmap.pages); - - trace_vfio_get_dirty_bitmap(iova, size, vbmap.size, ram_addr, dirty_pages); -out: - g_free(vbmap.bitmap); - - return ret; -} - typedef struct { IOMMUNotifier n; VFIOGuestIOMMU *giommu; diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 2c2d8329e3..f9cf317018 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -10,12 +10,20 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ +#include +#include + #include "qemu/osdep.h" +#include "system/tcg.h" +#include "system/ram_addr.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/vfio/vfio-container-base.h" #include "hw/vfio/vfio-common.h" /* vfio_reset_handler */ #include "system/reset.h" +#include "vfio-helpers.h" + +#include "trace.h" static QLIST_HEAD(, VFIOAddressSpace) vfio_address_spaces = QLIST_HEAD_INITIALIZER(vfio_address_spaces); @@ -143,6 +151,136 @@ int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, errp); } +static bool vfio_devices_all_device_dirty_tracking_started( + const VFIOContainerBase *bcontainer) +{ + VFIODevice *vbasedev; + + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + if (!vbasedev->dirty_tracking) { + return false; + } + } + + return true; +} + +bool vfio_devices_all_dirty_tracking_started( + const VFIOContainerBase *bcontainer) +{ + return vfio_devices_all_device_dirty_tracking_started(bcontainer) || + bcontainer->dirty_pages_started; +} + +bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer) +{ + VFIODevice *vbasedev; + + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + if (vbasedev->device_dirty_page_tracking == ON_OFF_AUTO_OFF) { + return false; + } + if (!vbasedev->dirty_pages_supported) { + return false; + } + } + + return true; +} + +static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova, + hwaddr size, void *bitmap) +{ + uint64_t buf[DIV_ROUND_UP(sizeof(struct vfio_device_feature) + + sizeof(struct vfio_device_feature_dma_logging_report), + sizeof(uint64_t))] = {}; + struct vfio_device_feature *feature = (struct vfio_device_feature *)buf; + struct vfio_device_feature_dma_logging_report *report = + (struct vfio_device_feature_dma_logging_report *)feature->data; + + report->iova = iova; + report->length = size; + report->page_size = qemu_real_host_page_size(); + report->bitmap = (uintptr_t)bitmap; + + feature->argsz = sizeof(buf); + feature->flags = VFIO_DEVICE_FEATURE_GET | + VFIO_DEVICE_FEATURE_DMA_LOGGING_REPORT; + + if (ioctl(vbasedev->fd, VFIO_DEVICE_FEATURE, feature)) { + return -errno; + } + + return 0; +} + +int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) +{ + VFIODevice *vbasedev; + int ret; + + QLIST_FOREACH(vbasedev, &bcontainer->device_list, container_next) { + ret = vfio_device_dma_logging_report(vbasedev, iova, size, + vbmap->bitmap); + if (ret) { + error_setg_errno(errp, -ret, + "%s: Failed to get DMA logging report, iova: " + "0x%" HWADDR_PRIx ", size: 0x%" HWADDR_PRIx, + vbasedev->name, iova, size); + + return ret; + } + } + + return 0; +} + +int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, + uint64_t size, ram_addr_t ram_addr, Error **errp) +{ + bool all_device_dirty_tracking = + vfio_devices_all_device_dirty_tracking(bcontainer); + uint64_t dirty_pages; + VFIOBitmap vbmap; + int ret; + + if (!bcontainer->dirty_pages_supported && !all_device_dirty_tracking) { + cpu_physical_memory_set_dirty_range(ram_addr, size, + tcg_enabled() ? DIRTY_CLIENTS_ALL : + DIRTY_CLIENTS_NOCODE); + return 0; + } + + ret = vfio_bitmap_alloc(&vbmap, size); + if (ret) { + error_setg_errno(errp, -ret, + "Failed to allocate dirty tracking bitmap"); + return ret; + } + + if (all_device_dirty_tracking) { + ret = vfio_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size, + errp); + } else { + ret = vfio_container_query_dirty_bitmap(bcontainer, &vbmap, iova, size, + errp); + } + + if (ret) { + goto out; + } + + dirty_pages = cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, ram_addr, + vbmap.pages); + + trace_vfio_get_dirty_bitmap(iova, size, vbmap.size, ram_addr, dirty_pages); +out: + g_free(vbmap.bitmap); + + return ret; +} + static gpointer copy_iova_range(gconstpointer src, gpointer data) { Range *source = (Range *)src; diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 1f89bd28c1..9c8a989db2 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -1,6 +1,7 @@ vfio_ss = ss.source_set() vfio_ss.add(files( 'common.c', + 'container-base.c', 'container.c', 'helpers.c', )) @@ -19,7 +20,6 @@ specific_ss.add_all(when: 'CONFIG_VFIO', if_true: vfio_ss) system_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) system_ss.add(when: 'CONFIG_VFIO_AMD_XGBE', if_true: files('amd-xgbe.c')) system_ss.add(when: 'CONFIG_VFIO', if_true: files( - 'container-base.c', 'cpr.c', 'device.c', 'migration.c', diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 9fee7df876..d4cd09cb0f 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -106,9 +106,11 @@ vfio_put_group(int fd) "close group->fd=%d" vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" vfio_put_base_device(int fd) "close vdev->fd=%d" vfio_legacy_dma_unmap_overflow_workaround(void) "" -vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 +# container-base.c +vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 + # region.c vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)" vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64 diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 06178cf282..0dfae24b72 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -141,15 +141,6 @@ int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type); #endif -bool vfio_devices_all_dirty_tracking_started( - const VFIOContainerBase *bcontainer); -bool -vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer); -int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); -int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, - uint64_t size, ram_addr_t ram_addr, Error **errp); - /* Returns 0 on success, or a negative errno. */ bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp); void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp); diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index e0c458d487..41ca27b3f0 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -91,6 +91,13 @@ int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, bool start, Error **errp); int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); +bool vfio_devices_all_dirty_tracking_started(const VFIOContainerBase *bcontainer); +bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer); +int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, + Error **errp); +int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, + uint64_t size, ram_addr_t ram_addr, Error **errp); GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer); From d90aa1b8627b6a2a1e55fc559ce9082598bd47ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:12 +0100 Subject: [PATCH 0277/2760] vfio: Make vfio_devices_query_dirty_bitmap() static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_devices_query_dirty_bitmap() is only used in "container-base.c". Also, rename to vfio_container_devices_query_dirty_bitmap() to reflect with the prefix 'vfio_container_devices_' that it simply loops over the container's device list. Reviewed-by: Joao Martins Reviewed-by: John Levon Reviewed-by: Avihai Horon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-28-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/container-base.c | 6 +++--- include/hw/vfio/vfio-container-base.h | 3 --- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index f9cf317018..eb7ab8461e 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -214,7 +214,7 @@ static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova, return 0; } -int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, +static int vfio_container_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { VFIODevice *vbasedev; @@ -260,8 +260,8 @@ int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, } if (all_device_dirty_tracking) { - ret = vfio_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size, - errp); + ret = vfio_container_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size, + errp); } else { ret = vfio_container_query_dirty_bitmap(bcontainer, &vbmap, iova, size, errp); diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 41ca27b3f0..60975194a3 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -93,9 +93,6 @@ int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); bool vfio_devices_all_dirty_tracking_started(const VFIOContainerBase *bcontainer); bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer); -int vfio_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, hwaddr iova, hwaddr size, - Error **errp); int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, uint64_t size, ram_addr_t ram_addr, Error **errp); From 35e6d2c1d07dfd45c8b00019a98499d2cd6442ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:13 +0100 Subject: [PATCH 0278/2760] vfio: Make vfio_container_query_dirty_bitmap() static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_container_query_dirty_bitmap() is only used in "container-base.c". Also, rename to vfio_container_iommu_query_dirty_bitmap() to reflect it is using the VFIO IOMMU backend device ->query_dirty_bitmap() handler. Reviewed-by: Joao Martins Reviewed-by: John Levon Reviewed-by: Avihai Horon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-29-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/container-base.c | 24 ++++++++++++------------ include/hw/vfio/vfio-container-base.h | 2 -- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index eb7ab8461e..3c8039d52a 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -141,16 +141,6 @@ int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, return ret; } -int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) -{ - VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); - - g_assert(vioc->query_dirty_bitmap); - return vioc->query_dirty_bitmap(bcontainer, vbmap, iova, size, - errp); -} - static bool vfio_devices_all_device_dirty_tracking_started( const VFIOContainerBase *bcontainer) { @@ -214,6 +204,16 @@ static int vfio_device_dma_logging_report(VFIODevice *vbasedev, hwaddr iova, return 0; } +static int vfio_container_iommu_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + g_assert(vioc->query_dirty_bitmap); + return vioc->query_dirty_bitmap(bcontainer, vbmap, iova, size, + errp); +} + static int vfio_container_devices_query_dirty_bitmap(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp) { @@ -263,8 +263,8 @@ int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, ret = vfio_container_devices_query_dirty_bitmap(bcontainer, &vbmap, iova, size, errp); } else { - ret = vfio_container_query_dirty_bitmap(bcontainer, &vbmap, iova, size, - errp); + ret = vfio_container_iommu_query_dirty_bitmap(bcontainer, &vbmap, iova, size, + errp); } if (ret) { diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 60975194a3..69fb698bc1 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -89,8 +89,6 @@ void vfio_container_del_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section); int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, bool start, Error **errp); -int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, - VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); bool vfio_devices_all_dirty_tracking_started(const VFIOContainerBase *bcontainer); bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer); int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, From e17c281e7c12d0bbe5de841f0a910687cdaaedd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:14 +0100 Subject: [PATCH 0279/2760] vfio: Rename vfio_devices_all_dirty_tracking_started() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also rename vfio_devices_all_device_dirty_tracking_started() while at it and use the prefix 'vfio_container_devices_' for routines simply looping over the container's device list. Reviewed-by: Joao Martins Reviewed-by: John Levon Reviewed-by: Avihai Horon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-30-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 2 +- hw/vfio/container-base.c | 6 +++--- hw/vfio/container.c | 2 +- include/hw/vfio/vfio-container-base.h | 3 ++- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index ed49ef88a8..89b7a71385 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -55,7 +55,7 @@ static bool vfio_log_sync_needed(const VFIOContainerBase *bcontainer) { VFIODevice *vbasedev; - if (!vfio_devices_all_dirty_tracking_started(bcontainer)) { + if (!vfio_container_dirty_tracking_is_started(bcontainer)) { return false; } diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 3c8039d52a..1beba37a77 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -141,7 +141,7 @@ int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, return ret; } -static bool vfio_devices_all_device_dirty_tracking_started( +static bool vfio_container_devices_dirty_tracking_is_started( const VFIOContainerBase *bcontainer) { VFIODevice *vbasedev; @@ -155,10 +155,10 @@ static bool vfio_devices_all_device_dirty_tracking_started( return true; } -bool vfio_devices_all_dirty_tracking_started( +bool vfio_container_dirty_tracking_is_started( const VFIOContainerBase *bcontainer) { - return vfio_devices_all_device_dirty_tracking_started(bcontainer) || + return vfio_container_devices_dirty_tracking_is_started(bcontainer) || bcontainer->dirty_pages_started; } diff --git a/hw/vfio/container.c b/hw/vfio/container.c index c55fe8e4be..e8cd927136 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -137,7 +137,7 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer, int ret; Error *local_err = NULL; - if (iotlb && vfio_devices_all_dirty_tracking_started(bcontainer)) { + if (iotlb && vfio_container_dirty_tracking_is_started(bcontainer)) { if (!vfio_devices_all_device_dirty_tracking(bcontainer) && bcontainer->dirty_pages_supported) { return vfio_dma_unmap_bitmap(container, iova, size, iotlb); diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 69fb698bc1..9b33e71f59 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -89,7 +89,8 @@ void vfio_container_del_section_window(VFIOContainerBase *bcontainer, MemoryRegionSection *section); int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, bool start, Error **errp); -bool vfio_devices_all_dirty_tracking_started(const VFIOContainerBase *bcontainer); +bool vfio_container_dirty_tracking_is_started( + const VFIOContainerBase *bcontainer); bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer); int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, uint64_t size, ram_addr_t ram_addr, Error **errp); From 60f29d08237b6184009d0b00b933a5a788da5f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:15 +0100 Subject: [PATCH 0280/2760] vfio: Rename vfio_devices_all_device_dirty_tracking() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the prefix 'vfio_container_devices_' to reflect the routine simply loops over the container's device list. Reviewed-by: John Levon Reviewed-by: Avihai Horon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-31-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 4 ++-- hw/vfio/container-base.c | 5 +++-- hw/vfio/container.c | 2 +- include/hw/vfio/vfio-container-base.h | 3 ++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 89b7a71385..5a7327979d 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -935,7 +935,7 @@ static bool vfio_listener_log_global_start(MemoryListener *listener, listener); bool ret; - if (vfio_devices_all_device_dirty_tracking(bcontainer)) { + if (vfio_container_devices_dirty_tracking_is_supported(bcontainer)) { ret = vfio_devices_dma_logging_start(bcontainer, errp); } else { ret = vfio_container_set_dirty_page_tracking(bcontainer, true, errp) == 0; @@ -954,7 +954,7 @@ static void vfio_listener_log_global_stop(MemoryListener *listener) Error *local_err = NULL; int ret = 0; - if (vfio_devices_all_device_dirty_tracking(bcontainer)) { + if (vfio_container_devices_dirty_tracking_is_supported(bcontainer)) { vfio_devices_dma_logging_stop(bcontainer); } else { ret = vfio_container_set_dirty_page_tracking(bcontainer, false, diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 1beba37a77..2627908e3f 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -162,7 +162,8 @@ bool vfio_container_dirty_tracking_is_started( bcontainer->dirty_pages_started; } -bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer) +bool vfio_container_devices_dirty_tracking_is_supported( + const VFIOContainerBase *bcontainer) { VFIODevice *vbasedev; @@ -240,7 +241,7 @@ int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, uint64_t size, ram_addr_t ram_addr, Error **errp) { bool all_device_dirty_tracking = - vfio_devices_all_device_dirty_tracking(bcontainer); + vfio_container_devices_dirty_tracking_is_supported(bcontainer); uint64_t dirty_pages; VFIOBitmap vbmap; int ret; diff --git a/hw/vfio/container.c b/hw/vfio/container.c index e8cd927136..8e4a2cae03 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -138,7 +138,7 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer, Error *local_err = NULL; if (iotlb && vfio_container_dirty_tracking_is_started(bcontainer)) { - if (!vfio_devices_all_device_dirty_tracking(bcontainer) && + if (!vfio_container_devices_dirty_tracking_is_supported(bcontainer) && bcontainer->dirty_pages_supported) { return vfio_dma_unmap_bitmap(container, iova, size, iotlb); } diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 9b33e71f59..395ace02e4 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -91,7 +91,8 @@ int vfio_container_set_dirty_page_tracking(VFIOContainerBase *bcontainer, bool start, Error **errp); bool vfio_container_dirty_tracking_is_started( const VFIOContainerBase *bcontainer); -bool vfio_devices_all_device_dirty_tracking(const VFIOContainerBase *bcontainer); +bool vfio_container_devices_dirty_tracking_is_supported( + const VFIOContainerBase *bcontainer); int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, uint64_t size, ram_addr_t ram_addr, Error **errp); From c51358bd17c324dae65f745df9c954e3a7137219 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:16 +0100 Subject: [PATCH 0281/2760] vfio: Rename vfio_get_dirty_bitmap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename to vfio_container_query_dirty_bitmap() to be consistent with the VFIO container routine naming scheme. Reviewed-by: Joao Martins Reviewed-by: John Levon Reviewed-by: Avihai Horon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-32-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/common.c | 6 +++--- hw/vfio/container-base.c | 5 +++-- hw/vfio/container.c | 2 +- hw/vfio/trace-events | 2 +- include/hw/vfio/vfio-container-base.h | 4 ++-- 5 files changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/vfio/common.c b/hw/vfio/common.c index 5a7327979d..bde1bb3c13 100644 --- a/hw/vfio/common.c +++ b/hw/vfio/common.c @@ -1002,7 +1002,7 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) goto out_unlock; } - ret = vfio_get_dirty_bitmap(bcontainer, iova, iotlb->addr_mask + 1, + ret = vfio_container_query_dirty_bitmap(bcontainer, iova, iotlb->addr_mask + 1, translated_addr, &local_err); if (ret) { error_prepend(&local_err, @@ -1039,7 +1039,7 @@ static int vfio_ram_discard_get_dirty_bitmap(MemoryRegionSection *section, * Sync the whole mapped region (spanning multiple individual mappings) * in one go. */ - ret = vfio_get_dirty_bitmap(vrdl->bcontainer, iova, size, ram_addr, + ret = vfio_container_query_dirty_bitmap(vrdl->bcontainer, iova, size, ram_addr, &local_err); if (ret) { error_report_err(local_err); @@ -1133,7 +1133,7 @@ static int vfio_sync_dirty_bitmap(VFIOContainerBase *bcontainer, ram_addr = memory_region_get_ram_addr(section->mr) + section->offset_within_region; - return vfio_get_dirty_bitmap(bcontainer, + return vfio_container_query_dirty_bitmap(bcontainer, REAL_HOST_PAGE_ALIGN(section->offset_within_address_space), int128_get64(section->size), ram_addr, errp); } diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 2627908e3f..fb86bc41bf 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -237,7 +237,7 @@ static int vfio_container_devices_query_dirty_bitmap(const VFIOContainerBase *bc return 0; } -int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, +int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, uint64_t size, ram_addr_t ram_addr, Error **errp) { bool all_device_dirty_tracking = @@ -275,7 +275,8 @@ int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, dirty_pages = cpu_physical_memory_set_dirty_lebitmap(vbmap.bitmap, ram_addr, vbmap.pages); - trace_vfio_get_dirty_bitmap(iova, size, vbmap.size, ram_addr, dirty_pages); + trace_vfio_container_query_dirty_bitmap(iova, size, vbmap.size, ram_addr, + dirty_pages); out: g_free(vbmap.bitmap); diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 8e4a2cae03..bb3990aece 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -169,7 +169,7 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer, } if (need_dirty_sync) { - ret = vfio_get_dirty_bitmap(bcontainer, iova, size, + ret = vfio_container_query_dirty_bitmap(bcontainer, iova, size, iotlb->translated_addr, &local_err); if (ret) { error_report_err(local_err); diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index d4cd09cb0f..aa0ba695fa 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -109,7 +109,7 @@ vfio_legacy_dma_unmap_overflow_workaround(void) "" vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 # container-base.c -vfio_get_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 +vfio_container_query_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 # region.c vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)" diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 395ace02e4..af63373c92 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -93,8 +93,8 @@ bool vfio_container_dirty_tracking_is_started( const VFIOContainerBase *bcontainer); bool vfio_container_devices_dirty_tracking_is_supported( const VFIOContainerBase *bcontainer); -int vfio_get_dirty_bitmap(const VFIOContainerBase *bcontainer, uint64_t iova, - uint64_t size, ram_addr_t ram_addr, Error **errp); +int vfio_container_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + uint64_t iova, uint64_t size, ram_addr_t ram_addr, Error **errp); GList *vfio_container_get_iova_ranges(const VFIOContainerBase *bcontainer); From 6b62a90c24eb766b331343f9ad80104a7f16e7a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:17 +0100 Subject: [PATCH 0282/2760] vfio: Introduce new files for VFIO MemoryListener MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit File "common.c" has been emptied of most of its definitions by the previous changes and the only definitions left are related to the VFIO MemoryListener handlers. Rename it to "listener.c" and introduce its associated "vfio-listener.h" header file for the declarations. Cleanup a little the includes while at it. Reviewed-by: Joao Martins Reviewed-by: John Levon Reviewed-by: Avihai Horon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-33-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 1 + hw/vfio/iommufd.c | 1 + hw/vfio/{common.c => listener.c} | 0 hw/vfio/meson.build | 2 +- hw/vfio/trace-events | 2 +- hw/vfio/vfio-listener.h | 14 ++++++++++++++ include/hw/vfio/vfio-common.h | 1 - 7 files changed, 18 insertions(+), 3 deletions(-) rename hw/vfio/{common.c => listener.c} (100%) create mode 100644 hw/vfio/vfio-listener.h diff --git a/hw/vfio/container.c b/hw/vfio/container.c index bb3990aece..ff540e1c59 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -35,6 +35,7 @@ #include "hw/vfio/vfio-container.h" #include "vfio-helpers.h" #include "vfio-cpr.h" +#include "vfio-listener.h" #define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio" diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index a5bd189a86..7488d21215 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -28,6 +28,7 @@ #include "vfio-iommufd.h" #include "vfio-helpers.h" #include "vfio-cpr.h" +#include "vfio-listener.h" #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" diff --git a/hw/vfio/common.c b/hw/vfio/listener.c similarity index 100% rename from hw/vfio/common.c rename to hw/vfio/listener.c diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 9c8a989db2..bccb05098c 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -1,6 +1,6 @@ vfio_ss = ss.source_set() vfio_ss.add(files( - 'common.c', + 'listener.c', 'container-base.c', 'container.c', 'helpers.c', diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index aa0ba695fa..ddb1bcc24a 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -89,7 +89,7 @@ vfio_pci_igd_bdsm_enabled(const char *name, int size) "%s %dMB" vfio_pci_igd_host_bridge_enabled(const char *name) "%s" vfio_pci_igd_lpc_bridge_enabled(const char *name) "%s" -# common.c +# listener.c vfio_iommu_map_notify(const char *op, uint64_t iova_start, uint64_t iova_end) "iommu %s @ 0x%"PRIx64" - 0x%"PRIx64 vfio_listener_region_skip(const char *name, uint64_t start, uint64_t end) "SKIPPING %s 0x%"PRIx64" - 0x%"PRIx64 vfio_spapr_group_attach(int groupfd, int tablefd) "Attached groupfd %d to liobn fd %d" diff --git a/hw/vfio/vfio-listener.h b/hw/vfio/vfio-listener.h new file mode 100644 index 0000000000..93af6747b2 --- /dev/null +++ b/hw/vfio/vfio-listener.h @@ -0,0 +1,14 @@ +/* + * VFIO MemoryListener services + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_LISTENER_H +#define HW_VFIO_VFIO_LISTENER_H + +extern const MemoryListener vfio_memory_listener; + +#endif /* HW_VFIO_VFIO_LISTENER_H */ diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-common.h index 0dfae24b72..92381c6160 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-common.h @@ -131,7 +131,6 @@ VFIODevice *vfio_get_vfio_device(Object *obj); typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList; extern VFIODeviceList vfio_device_list; -extern const MemoryListener vfio_memory_listener; #ifdef CONFIG_LINUX int vfio_get_region_info(VFIODevice *vbasedev, int index, From 74d376378e2a41392a36f34cef7d2c89ac2dfb33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:18 +0100 Subject: [PATCH 0283/2760] vfio: Rename RAM discard related services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename some routines to better reflect the namespace they belong to. Reviewed-by: Avihai Horon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-34-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/listener.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index bde1bb3c13..70bdeb3ce7 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -242,7 +242,7 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl, return 0; } -static void vfio_register_ram_discard_listener(VFIOContainerBase *bcontainer, +static void vfio_ram_discard_register_listener(VFIOContainerBase *bcontainer, MemoryRegionSection *section) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); @@ -317,7 +317,7 @@ static void vfio_register_ram_discard_listener(VFIOContainerBase *bcontainer, } } -static void vfio_unregister_ram_discard_listener(VFIOContainerBase *bcontainer, +static void vfio_ram_discard_unregister_listener(VFIOContainerBase *bcontainer, MemoryRegionSection *section) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); @@ -504,7 +504,7 @@ static void vfio_listener_region_add(MemoryListener *listener, * about changes. */ if (memory_region_has_ram_discard_manager(section->mr)) { - vfio_register_ram_discard_listener(bcontainer, section); + vfio_ram_discard_register_listener(bcontainer, section); return; } @@ -627,7 +627,7 @@ static void vfio_listener_region_del(MemoryListener *listener, pgmask = (1ULL << ctz64(bcontainer->pgsizes)) - 1; try_unmap = !((iova & pgmask) || (int128_get64(llsize) & pgmask)); } else if (memory_region_has_ram_discard_manager(section->mr)) { - vfio_unregister_ram_discard_listener(bcontainer, section); + vfio_ram_discard_unregister_listener(bcontainer, section); /* Unregistering will trigger an unmap. */ try_unmap = false; } @@ -1024,7 +1024,7 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) } } -static int vfio_ram_discard_get_dirty_bitmap(MemoryRegionSection *section, +static int vfio_ram_discard_query_dirty_bitmap(MemoryRegionSection *section, void *opaque) { const hwaddr size = int128_get64(section->size); @@ -1071,7 +1071,7 @@ vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainerBase *bcontainer, * which correspond to populated parts. Replay all populated parts. */ return ram_discard_manager_replay_populated(rdm, section, - vfio_ram_discard_get_dirty_bitmap, + vfio_ram_discard_query_dirty_bitmap, &vrdl); } From a9183378f54969c8b11f08fdb3063925de8d77c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:19 +0100 Subject: [PATCH 0284/2760] vfio: Introduce vfio_listener_un/register() routines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This hides the MemoryListener implementation and makes the code common to both IOMMU backends, legacy and IOMMUFD. Reviewed-by: Joao Martins Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-35-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 11 +++-------- hw/vfio/iommufd.c | 9 ++------- hw/vfio/listener.c | 22 +++++++++++++++++++++- hw/vfio/vfio-listener.h | 3 ++- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index ff540e1c59..e4fcb1ad8b 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -616,12 +616,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, group->container = container; QLIST_INSERT_HEAD(&container->group_list, group, container_next); - bcontainer->listener = vfio_memory_listener; - memory_listener_register(&bcontainer->listener, bcontainer->space->as); - - if (bcontainer->error) { - error_propagate_prepend(errp, bcontainer->error, - "memory listener initialization failed: "); + if (!vfio_listener_register(bcontainer, errp)) { goto listener_release_exit; } @@ -631,7 +626,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, listener_release_exit: QLIST_REMOVE(group, container_next); vfio_kvm_device_del_group(group); - memory_listener_unregister(&bcontainer->listener); + vfio_listener_unregister(bcontainer); if (vioc->release) { vioc->release(bcontainer); } @@ -669,7 +664,7 @@ static void vfio_disconnect_container(VFIOGroup *group) * group. */ if (QLIST_EMPTY(&container->group_list)) { - memory_listener_unregister(&bcontainer->listener); + vfio_listener_unregister(bcontainer); if (vioc->release) { vioc->release(bcontainer); } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 7488d21215..e47720247d 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -410,7 +410,7 @@ static void iommufd_cdev_container_destroy(VFIOIOMMUFDContainer *container) if (!QLIST_EMPTY(&bcontainer->device_list)) { return; } - memory_listener_unregister(&bcontainer->listener); + vfio_listener_unregister(bcontainer); iommufd_backend_free_id(container->be, container->ioas_id); object_unref(container); } @@ -562,12 +562,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, bcontainer->pgsizes = qemu_real_host_page_size(); } - bcontainer->listener = vfio_memory_listener; - memory_listener_register(&bcontainer->listener, bcontainer->space->as); - - if (bcontainer->error) { - error_propagate_prepend(errp, bcontainer->error, - "memory listener initialization failed: "); + if (!vfio_listener_register(bcontainer, errp)) { goto err_listener_register; } diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index 70bdeb3ce7..d19674503c 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -45,6 +45,7 @@ #include "system/tpm.h" #include "vfio-migration-internal.h" #include "vfio-helpers.h" +#include "vfio-listener.h" /* * Device state interfaces @@ -1162,7 +1163,7 @@ static void vfio_listener_log_sync(MemoryListener *listener, } } -const MemoryListener vfio_memory_listener = { +static const MemoryListener vfio_memory_listener = { .name = "vfio", .region_add = vfio_listener_region_add, .region_del = vfio_listener_region_del, @@ -1170,3 +1171,22 @@ const MemoryListener vfio_memory_listener = { .log_global_stop = vfio_listener_log_global_stop, .log_sync = vfio_listener_log_sync, }; + +bool vfio_listener_register(VFIOContainerBase *bcontainer, Error **errp) +{ + bcontainer->listener = vfio_memory_listener; + memory_listener_register(&bcontainer->listener, bcontainer->space->as); + + if (bcontainer->error) { + error_propagate_prepend(errp, bcontainer->error, + "memory listener initialization failed: "); + return false; + } + + return true; +} + +void vfio_listener_unregister(VFIOContainerBase *bcontainer) +{ + memory_listener_unregister(&bcontainer->listener); +} diff --git a/hw/vfio/vfio-listener.h b/hw/vfio/vfio-listener.h index 93af6747b2..eb69ddd374 100644 --- a/hw/vfio/vfio-listener.h +++ b/hw/vfio/vfio-listener.h @@ -9,6 +9,7 @@ #ifndef HW_VFIO_VFIO_LISTENER_H #define HW_VFIO_VFIO_LISTENER_H -extern const MemoryListener vfio_memory_listener; +bool vfio_listener_register(VFIOContainerBase *bcontainer, Error **errp); +void vfio_listener_unregister(VFIOContainerBase *bcontainer); #endif /* HW_VFIO_VFIO_LISTENER_H */ From 11b8b9d53d8b30bf1823e9d2e303c28fe13acf34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:20 +0100 Subject: [PATCH 0285/2760] vfio: Rename vfio-common.h to vfio-device.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/vfio/vfio-common.h" has been emptied of most of its declarations by the previous changes and the only declarations left are related to VFIODevice. Rename it to "hw/vfio/vfio-device.h" and make the necessary adjustments. Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-36-clg@redhat.com Signed-off-by: Cédric Le Goater --- backends/iommufd.c | 2 +- hw/vfio/ap.c | 2 +- hw/vfio/ccw.c | 2 +- hw/vfio/container-base.c | 2 +- hw/vfio/container.c | 2 +- hw/vfio/cpr.c | 2 +- hw/vfio/device.c | 2 +- hw/vfio/helpers.c | 2 +- hw/vfio/iommufd.c | 2 +- hw/vfio/listener.c | 2 +- hw/vfio/migration-multifd.c | 2 +- hw/vfio/migration-multifd.h | 2 +- hw/vfio/migration.c | 2 +- hw/vfio/pci.h | 2 +- hw/vfio/region.c | 4 ++-- include/hw/s390x/vfio-ccw.h | 2 +- include/hw/vfio/{vfio-common.h => vfio-device.h} | 2 +- include/hw/vfio/vfio-platform.h | 2 +- 18 files changed, 19 insertions(+), 19 deletions(-) rename include/hw/vfio/{vfio-common.h => vfio-device.h} (98%) diff --git a/backends/iommufd.c b/backends/iommufd.c index d57da44755..9587e4d99b 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -18,7 +18,7 @@ #include "qemu/error-report.h" #include "monitor/monitor.h" #include "trace.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include #include diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index d6575d7c44..9e2251437e 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -15,7 +15,7 @@ #include #include #include "qapi/error.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "system/iommufd.h" #include "hw/s390x/ap-device.h" #include "qemu/error-report.h" diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 29e804e122..d0713ca4c7 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -21,7 +21,7 @@ #include #include "qapi/error.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "system/iommufd.h" #include "hw/s390x/s390-ccw.h" #include "hw/s390x/vfio-ccw.h" diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index fb86bc41bf..c037bafe21 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -19,7 +19,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/vfio/vfio-container-base.h" -#include "hw/vfio/vfio-common.h" /* vfio_reset_handler */ +#include "hw/vfio/vfio-device.h" /* vfio_reset_handler */ #include "system/reset.h" #include "vfio-helpers.h" diff --git a/hw/vfio/container.c b/hw/vfio/container.c index e4fcb1ad8b..ef14046072 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -22,7 +22,7 @@ #include #include -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "system/address-spaces.h" #include "system/memory.h" #include "system/ram_addr.h" diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index 696987006b..3214184f97 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -6,7 +6,7 @@ */ #include "qemu/osdep.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "migration/misc.h" #include "qapi/error.h" #include "system/runstate.h" diff --git a/hw/vfio/device.c b/hw/vfio/device.c index e122c797c2..543750c3b1 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -21,7 +21,7 @@ #include "qemu/osdep.h" #include -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "hw/vfio/pci.h" #include "hw/hw.h" #include "trace.h" diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 48bd61d528..d0dbab1d17 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -23,7 +23,7 @@ #include #include "system/kvm.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "hw/hw.h" #include "qapi/error.h" #include "vfio-helpers.h" diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index e47720247d..48db105422 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -15,7 +15,7 @@ #include #include -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "qemu/error-report.h" #include "trace.h" #include "qapi/error.h" diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index d19674503c..6f77e18a7a 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -25,7 +25,7 @@ #endif #include -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "hw/vfio/pci.h" #include "system/address-spaces.h" #include "system/memory.h" diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index a3005226b9..850a319488 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -10,7 +10,7 @@ */ #include "qemu/osdep.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "migration/misc.h" #include "qapi/error.h" #include "qemu/bswap.h" diff --git a/hw/vfio/migration-multifd.h b/hw/vfio/migration-multifd.h index a664051eb8..0bab63211d 100644 --- a/hw/vfio/migration-multifd.h +++ b/hw/vfio/migration-multifd.h @@ -12,7 +12,7 @@ #ifndef HW_VFIO_MIGRATION_MULTIFD_H #define HW_VFIO_MIGRATION_MULTIFD_H -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" bool vfio_multifd_setup(VFIODevice *vbasedev, bool alloc_multifd, Error **errp); void vfio_multifd_cleanup(VFIODevice *vbasedev); diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 4da0526325..1dceab1b19 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -16,7 +16,7 @@ #include #include "system/runstate.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "hw/vfio/vfio-migration.h" #include "migration/misc.h" #include "migration/savevm.h" diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 801ea445b8..f835b1dbc2 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -14,7 +14,7 @@ #include "system/memory.h" #include "hw/pci/pci_device.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "hw/vfio/vfio-region.h" #include "qemu/event_notifier.h" #include "qemu/queue.h" diff --git a/hw/vfio/region.c b/hw/vfio/region.c index 9049143abf..010b5241e1 100644 --- a/hw/vfio/region.c +++ b/hw/vfio/region.c @@ -21,8 +21,8 @@ #include "qemu/osdep.h" #include -#include "hw/vfio/vfio-common.h" -#include "hw/vfio/pci.h" +#include "hw/vfio/vfio-region.h" +#include "hw/vfio/vfio-device.h" #include "hw/hw.h" #include "trace.h" #include "qapi/error.h" diff --git a/include/hw/s390x/vfio-ccw.h b/include/hw/s390x/vfio-ccw.h index 4209d27657..1e0922dca1 100644 --- a/include/hw/s390x/vfio-ccw.h +++ b/include/hw/s390x/vfio-ccw.h @@ -14,7 +14,7 @@ #ifndef HW_VFIO_CCW_H #define HW_VFIO_CCW_H -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "hw/s390x/s390-ccw.h" #include "hw/s390x/ccw-device.h" #include "qom/object.h" diff --git a/include/hw/vfio/vfio-common.h b/include/hw/vfio/vfio-device.h similarity index 98% rename from include/hw/vfio/vfio-common.h rename to include/hw/vfio/vfio-device.h index 92381c6160..cfbe254e31 100644 --- a/include/hw/vfio/vfio-common.h +++ b/include/hw/vfio/vfio-device.h @@ -1,5 +1,5 @@ /* - * common header for vfio based device assignment support + * VFIO Device interface * * Copyright Red Hat, Inc. 2012 * diff --git a/include/hw/vfio/vfio-platform.h b/include/hw/vfio/vfio-platform.h index 3191545717..256d8500b7 100644 --- a/include/hw/vfio/vfio-platform.h +++ b/include/hw/vfio/vfio-platform.h @@ -17,7 +17,7 @@ #define HW_VFIO_VFIO_PLATFORM_H #include "hw/sysbus.h" -#include "hw/vfio/vfio-common.h" +#include "hw/vfio/vfio-device.h" #include "qemu/event_notifier.h" #include "qemu/queue.h" #include "qom/object.h" From e218ccf0c94b6fe26b333e2bdad87da85fe1c428 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:21 +0100 Subject: [PATCH 0286/2760] vfio: Rename VFIODevice related services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename these routines : vfio_disable_irqindex -> vfio_device_irq_disable vfio_unmask_single_irqindex -> vfio_device_irq_unmask vfio_mask_single_irqindex -> vfio_device_irq_mask vfio_set_irq_signaling -> vfio_device_irq_set_signaling vfio_attach_device -> vfio_device_attach vfio_detach_device -> vfio_device_detach vfio_get_region_info -> vfio_device_get_region_info vfio_get_dev_region_info -> vfio_device_get_region_info_type vfio_has_region_cap -> vfio_device_has_region_cap vfio_reset_handler -> vfio_device_reset_hander to better reflect the namespace they belong to. Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-37-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/ap.c | 12 +++---- hw/vfio/ccw.c | 28 +++++++-------- hw/vfio/container-base.c | 6 ++-- hw/vfio/container.c | 6 ++-- hw/vfio/device.c | 36 +++++++++---------- hw/vfio/display.c | 8 ++--- hw/vfio/igd.c | 10 +++--- hw/vfio/pci.c | 68 +++++++++++++++++------------------ hw/vfio/platform.c | 14 ++++---- hw/vfio/region.c | 2 +- hw/vfio/trace-events | 8 ++--- include/hw/vfio/vfio-device.h | 26 +++++++------- 12 files changed, 112 insertions(+), 112 deletions(-) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 9e2251437e..be2e2927c2 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -117,8 +117,8 @@ static bool vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, fd = event_notifier_get_fd(notifier); qemu_set_fd_handler(fd, fd_read, NULL, vapdev); - if (!vfio_set_irq_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, - errp)) { + if (!vfio_device_irq_set_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, + errp)) { qemu_set_fd_handler(fd, NULL, NULL, vapdev); event_notifier_cleanup(notifier); } @@ -141,8 +141,8 @@ static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev, return; } - if (!vfio_set_irq_signaling(&vapdev->vdev, irq, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + if (!vfio_device_irq_set_signaling(&vapdev->vdev, irq, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name); } @@ -162,7 +162,7 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) return; } - if (!vfio_attach_device(vbasedev->name, vbasedev, + if (!vfio_device_attach(vbasedev->name, vbasedev, &address_space_memory, errp)) { goto error; } @@ -187,7 +187,7 @@ static void vfio_ap_unrealize(DeviceState *dev) VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev); vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX); - vfio_detach_device(&vapdev->vdev); + vfio_device_detach(&vapdev->vdev); g_free(vapdev->vdev.name); } diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index d0713ca4c7..3a91efce81 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -426,8 +426,8 @@ static bool vfio_ccw_register_irq_notifier(VFIOCCWDevice *vcdev, fd = event_notifier_get_fd(notifier); qemu_set_fd_handler(fd, fd_read, NULL, vcdev); - if (!vfio_set_irq_signaling(vdev, irq, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { + if (!vfio_device_irq_set_signaling(vdev, irq, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { qemu_set_fd_handler(fd, NULL, NULL, vcdev); event_notifier_cleanup(notifier); } @@ -456,8 +456,8 @@ static void vfio_ccw_unregister_irq_notifier(VFIOCCWDevice *vcdev, return; } - if (!vfio_set_irq_signaling(&vcdev->vdev, irq, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + if (!vfio_device_irq_set_signaling(&vcdev->vdev, irq, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { warn_reportf_err(err, VFIO_MSG_PREFIX, vcdev->vdev.name); } @@ -488,7 +488,7 @@ static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) return false; } - ret = vfio_get_region_info(vdev, VFIO_CCW_CONFIG_REGION_INDEX, &info); + ret = vfio_device_get_region_info(vdev, VFIO_CCW_CONFIG_REGION_INDEX, &info); if (ret) { error_setg_errno(errp, -ret, "vfio: Error getting config info"); return false; @@ -505,8 +505,8 @@ static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) g_free(info); /* check for the optional async command region */ - ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, - VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD, &info); + ret = vfio_device_get_region_info_type(vdev, VFIO_REGION_TYPE_CCW, + VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD, &info); if (!ret) { vcdev->async_cmd_region_size = info->size; if (sizeof(*vcdev->async_cmd_region) != vcdev->async_cmd_region_size) { @@ -518,8 +518,8 @@ static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) g_free(info); } - ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, - VFIO_REGION_SUBTYPE_CCW_SCHIB, &info); + ret = vfio_device_get_region_info_type(vdev, VFIO_REGION_TYPE_CCW, + VFIO_REGION_SUBTYPE_CCW_SCHIB, &info); if (!ret) { vcdev->schib_region_size = info->size; if (sizeof(*vcdev->schib_region) != vcdev->schib_region_size) { @@ -531,8 +531,8 @@ static bool vfio_ccw_get_region(VFIOCCWDevice *vcdev, Error **errp) g_free(info); } - ret = vfio_get_dev_region_info(vdev, VFIO_REGION_TYPE_CCW, - VFIO_REGION_SUBTYPE_CCW_CRW, &info); + ret = vfio_device_get_region_info_type(vdev, VFIO_REGION_TYPE_CCW, + VFIO_REGION_SUBTYPE_CCW_CRW, &info); if (!ret) { vcdev->crw_region_size = info->size; @@ -583,7 +583,7 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) goto out_unrealize; } - if (!vfio_attach_device(cdev->mdevid, vbasedev, + if (!vfio_device_attach(cdev->mdevid, vbasedev, &address_space_memory, errp)) { goto out_attach_dev_err; } @@ -620,7 +620,7 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) out_io_notifier_err: vfio_ccw_put_region(vcdev); out_region_err: - vfio_detach_device(vbasedev); + vfio_device_detach(vbasedev); out_attach_dev_err: g_free(vbasedev->name); out_unrealize: @@ -639,7 +639,7 @@ static void vfio_ccw_unrealize(DeviceState *dev) vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_CRW_IRQ_INDEX); vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); vfio_ccw_put_region(vcdev); - vfio_detach_device(&vcdev->vdev); + vfio_device_detach(&vcdev->vdev); g_free(vcdev->vdev.name); if (cdc->unrealize) { diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index c037bafe21..09340fd97a 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -19,7 +19,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "hw/vfio/vfio-container-base.h" -#include "hw/vfio/vfio-device.h" /* vfio_reset_handler */ +#include "hw/vfio/vfio-device.h" /* vfio_device_reset_handler */ #include "system/reset.h" #include "vfio-helpers.h" @@ -44,7 +44,7 @@ VFIOAddressSpace *vfio_address_space_get(AddressSpace *as) QLIST_INIT(&space->containers); if (QLIST_EMPTY(&vfio_address_spaces)) { - qemu_register_reset(vfio_reset_handler, NULL); + qemu_register_reset(vfio_device_reset_handler, NULL); } QLIST_INSERT_HEAD(&vfio_address_spaces, space, list); @@ -62,7 +62,7 @@ void vfio_address_space_put(VFIOAddressSpace *space) g_free(space); if (QLIST_EMPTY(&vfio_address_spaces)) { - qemu_unregister_reset(vfio_reset_handler, NULL); + qemu_unregister_reset(vfio_device_reset_handler, NULL); } } diff --git a/hw/vfio/container.c b/hw/vfio/container.c index ef14046072..149ea26aff 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -865,7 +865,7 @@ static int vfio_device_groupid(VFIODevice *vbasedev, Error **errp) } /* - * vfio_attach_device: attach a device to a security context + * vfio_device_attach: attach a device to a security context * @name and @vbasedev->name are likely to be different depending * on the type of the device, hence the need for passing @name */ @@ -881,7 +881,7 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, return false; } - trace_vfio_attach_device(vbasedev->name, groupid); + trace_vfio_device_attach(vbasedev->name, groupid); if (!vfio_device_hiod_realize(vbasedev, errp)) { return false; @@ -919,7 +919,7 @@ static void vfio_legacy_detach_device(VFIODevice *vbasedev) QLIST_REMOVE(vbasedev, global_next); QLIST_REMOVE(vbasedev, container_next); vbasedev->bcontainer = NULL; - trace_vfio_detach_device(vbasedev->name, group->groupid); + trace_vfio_device_detach(vbasedev->name, group->groupid); vfio_put_base_device(vbasedev); vfio_put_group(group); } diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 543750c3b1..4de6948cf4 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -51,11 +51,11 @@ VFIODeviceList vfio_device_list = * reset for the one in-use devices case, calling _multi() will do * nothing if a _one() would have been sufficient. */ -void vfio_reset_handler(void *opaque) +void vfio_device_reset_handler(void *opaque) { VFIODevice *vbasedev; - trace_vfio_reset_handler(); + trace_vfio_device_reset_handler(); QLIST_FOREACH(vbasedev, &vfio_device_list, global_next) { if (vbasedev->dev->realized) { vbasedev->ops->vfio_compute_needs_reset(vbasedev); @@ -72,7 +72,7 @@ void vfio_reset_handler(void *opaque) /* * Common VFIO interrupt disable */ -void vfio_disable_irqindex(VFIODevice *vbasedev, int index) +void vfio_device_irq_disable(VFIODevice *vbasedev, int index) { struct vfio_irq_set irq_set = { .argsz = sizeof(irq_set), @@ -85,7 +85,7 @@ void vfio_disable_irqindex(VFIODevice *vbasedev, int index) ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); } -void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index) +void vfio_device_irq_unmask(VFIODevice *vbasedev, int index) { struct vfio_irq_set irq_set = { .argsz = sizeof(irq_set), @@ -98,7 +98,7 @@ void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index) ioctl(vbasedev->fd, VFIO_DEVICE_SET_IRQS, &irq_set); } -void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index) +void vfio_device_irq_mask(VFIODevice *vbasedev, int index) { struct vfio_irq_set irq_set = { .argsz = sizeof(irq_set), @@ -147,8 +147,8 @@ static const char *index_to_str(VFIODevice *vbasedev, int index) } } -bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, - int action, int fd, Error **errp) +bool vfio_device_irq_set_signaling(VFIODevice *vbasedev, int index, int subindex, + int action, int fd, Error **errp) { ERRP_GUARD(); g_autofree struct vfio_irq_set *irq_set = NULL; @@ -185,8 +185,8 @@ bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, return false; } -int vfio_get_region_info(VFIODevice *vbasedev, int index, - struct vfio_region_info **info) +int vfio_device_get_region_info(VFIODevice *vbasedev, int index, + struct vfio_region_info **info) { size_t argsz = sizeof(struct vfio_region_info); @@ -212,8 +212,8 @@ int vfio_get_region_info(VFIODevice *vbasedev, int index, return 0; } -int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, - uint32_t subtype, struct vfio_region_info **info) +int vfio_device_get_region_info_type(VFIODevice *vbasedev, uint32_t type, + uint32_t subtype, struct vfio_region_info **info) { int i; @@ -221,7 +221,7 @@ int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, struct vfio_info_cap_header *hdr; struct vfio_region_info_cap_type *cap_type; - if (vfio_get_region_info(vbasedev, i, info)) { + if (vfio_device_get_region_info(vbasedev, i, info)) { continue; } @@ -233,8 +233,8 @@ int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, cap_type = container_of(hdr, struct vfio_region_info_cap_type, header); - trace_vfio_get_dev_region(vbasedev->name, i, - cap_type->type, cap_type->subtype); + trace_vfio_device_get_region_info_type(vbasedev->name, i, + cap_type->type, cap_type->subtype); if (cap_type->type == type && cap_type->subtype == subtype) { return 0; @@ -247,12 +247,12 @@ int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, return -ENODEV; } -bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) +bool vfio_device_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type) { g_autofree struct vfio_region_info *info = NULL; bool ret = false; - if (!vfio_get_region_info(vbasedev, region, &info)) { + if (!vfio_device_get_region_info(vbasedev, region, &info)) { if (vfio_get_region_info_cap(info, cap_type)) { ret = true; } @@ -367,7 +367,7 @@ VFIODevice *vfio_get_vfio_device(Object *obj) } } -bool vfio_attach_device(char *name, VFIODevice *vbasedev, +bool vfio_device_attach(char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp) { const VFIOIOMMUClass *ops = @@ -395,7 +395,7 @@ bool vfio_attach_device(char *name, VFIODevice *vbasedev, return true; } -void vfio_detach_device(VFIODevice *vbasedev) +void vfio_device_detach(VFIODevice *vbasedev) { if (!vbasedev->bcontainer) { return; diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 70cfd685bd..f3e6581f15 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -129,10 +129,10 @@ static bool vfio_display_edid_init(VFIOPCIDevice *vdev, Error **errp) int fd = vdev->vbasedev.fd; int ret; - ret = vfio_get_dev_region_info(&vdev->vbasedev, - VFIO_REGION_TYPE_GFX, - VFIO_REGION_SUBTYPE_GFX_EDID, - &dpy->edid_info); + ret = vfio_device_get_region_info_type(&vdev->vbasedev, + VFIO_REGION_TYPE_GFX, + VFIO_REGION_SUBTYPE_GFX_EDID, + &dpy->edid_info); if (ret) { /* Failed to get GFX edid info, allow to go through without edid. */ return true; diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 265fffc2aa..6678e0e5cd 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -200,7 +200,7 @@ static bool vfio_pci_igd_setup_opregion(VFIOPCIDevice *vdev, Error **errp) return false; } - ret = vfio_get_dev_region_info(&vdev->vbasedev, + ret = vfio_device_get_region_info_type(&vdev->vbasedev, VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, &opregion); if (ret) { @@ -385,7 +385,7 @@ static bool vfio_pci_igd_setup_lpc_bridge(VFIOPCIDevice *vdev, Error **errp) * Check whether we have all the vfio device specific regions to * support LPC quirk (added in Linux v4.6). */ - ret = vfio_get_dev_region_info(&vdev->vbasedev, + ret = vfio_device_get_region_info_type(&vdev->vbasedev, VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, VFIO_REGION_SUBTYPE_INTEL_IGD_LPC_CFG, &lpc); if (ret) { @@ -393,7 +393,7 @@ static bool vfio_pci_igd_setup_lpc_bridge(VFIOPCIDevice *vdev, Error **errp) return false; } - ret = vfio_get_dev_region_info(&vdev->vbasedev, + ret = vfio_device_get_region_info_type(&vdev->vbasedev, VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, VFIO_REGION_SUBTYPE_INTEL_IGD_HOST_CFG, &host); if (ret) { @@ -542,8 +542,8 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) * there's no ROM, there's no point in setting up this quirk. * NB. We only seem to get BIOS ROMs, so UEFI VM would need CSM support. */ - ret = vfio_get_region_info(&vdev->vbasedev, - VFIO_PCI_ROM_REGION_INDEX, &rom); + ret = vfio_device_get_region_info(&vdev->vbasedev, + VFIO_PCI_ROM_REGION_INDEX, &rom); if ((ret || !rom->size) && !vdev->pdev.romfile) { error_setg(&err, "Device has no ROM"); goto error; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index bade3b80d7..6fa7217db8 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -115,7 +115,7 @@ static void vfio_intx_eoi(VFIODevice *vbasedev) vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); - vfio_unmask_single_irqindex(vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_unmask(vbasedev, VFIO_PCI_INTX_IRQ_INDEX); } static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) @@ -131,7 +131,7 @@ static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) /* Get to a known interrupt state */ qemu_set_fd_handler(irq_fd, NULL, NULL, vdev); - vfio_mask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_mask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); @@ -149,15 +149,15 @@ static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) goto fail_irqfd; } - if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_UNMASK, - event_notifier_get_fd(&vdev->intx.unmask), - errp)) { + if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_UNMASK, + event_notifier_get_fd(&vdev->intx.unmask), + errp)) { goto fail_vfio; } /* Let'em rip */ - vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_unmask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.kvm_accel = true; @@ -172,7 +172,7 @@ static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) event_notifier_cleanup(&vdev->intx.unmask); fail: qemu_set_fd_handler(irq_fd, vfio_intx_interrupt, NULL, vdev); - vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_unmask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); return false; #else return true; @@ -190,7 +190,7 @@ static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) * Get to a known state, hardware masked, QEMU ready to accept new * interrupts, QEMU IRQ de-asserted. */ - vfio_mask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_mask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); @@ -210,7 +210,7 @@ static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) vdev->intx.kvm_accel = false; /* If we've missed an event, let it re-fire through QEMU */ - vfio_unmask_single_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_unmask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); trace_vfio_intx_disable_kvm(vdev->vbasedev.name); #endif @@ -299,7 +299,7 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) fd = event_notifier_get_fd(&vdev->intx.interrupt); qemu_set_fd_handler(fd, vfio_intx_interrupt, NULL, vdev); - if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, + if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { qemu_set_fd_handler(fd, NULL, NULL, vdev); event_notifier_cleanup(&vdev->intx.interrupt); @@ -322,7 +322,7 @@ static void vfio_intx_disable(VFIOPCIDevice *vdev) timer_del(vdev->intx.mmap_timer); vfio_intx_disable_kvm(vdev); - vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); + vfio_device_irq_disable(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); vfio_mmap_set_enabled(vdev, true); @@ -578,7 +578,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, if (!vdev->defer_kvm_irq_routing) { if (vdev->msix->noresize && resizing) { - vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); + vfio_device_irq_disable(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); ret = vfio_enable_vectors(vdev, true); if (ret) { error_report("vfio: failed to enable vectors, %d", ret); @@ -593,7 +593,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, fd = event_notifier_get_fd(&vector->interrupt); } - if (!vfio_set_irq_signaling(&vdev->vbasedev, + if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX, nr, VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { @@ -638,7 +638,7 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) int32_t fd = event_notifier_get_fd(&vector->interrupt); Error *err = NULL; - if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX, + if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX, nr, VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); @@ -835,7 +835,7 @@ static void vfio_msix_disable(VFIOPCIDevice *vdev) * Always clear MSI-X IRQ index. A PF device could have enabled * MSI-X with no vectors. See vfio_msix_enable(). */ - vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); + vfio_device_irq_disable(&vdev->vbasedev, VFIO_PCI_MSIX_IRQ_INDEX); vfio_msi_disable_common(vdev); if (!vfio_intx_enable(vdev, &err)) { @@ -852,7 +852,7 @@ static void vfio_msi_disable(VFIOPCIDevice *vdev) { Error *err = NULL; - vfio_disable_irqindex(&vdev->vbasedev, VFIO_PCI_MSI_IRQ_INDEX); + vfio_device_irq_disable(&vdev->vbasedev, VFIO_PCI_MSI_IRQ_INDEX); vfio_msi_disable_common(vdev); vfio_intx_enable(vdev, &err); if (err) { @@ -886,8 +886,8 @@ static void vfio_pci_load_rom(VFIOPCIDevice *vdev) off_t off = 0; ssize_t bytes; - if (vfio_get_region_info(&vdev->vbasedev, - VFIO_PCI_ROM_REGION_INDEX, ®_info)) { + if (vfio_device_get_region_info(&vdev->vbasedev, + VFIO_PCI_ROM_REGION_INDEX, ®_info)) { error_report("vfio: Error getting ROM info: %m"); return; } @@ -1380,8 +1380,8 @@ static void vfio_pci_fixup_msix_region(VFIOPCIDevice *vdev) * If the host driver allows mapping of a MSIX data, we are going to * do map the entire BAR and emulate MSIX table on top of that. */ - if (vfio_has_region_cap(&vdev->vbasedev, region->nr, - VFIO_REGION_INFO_CAP_MSIX_MAPPABLE)) { + if (vfio_device_has_region_cap(&vdev->vbasedev, region->nr, + VFIO_REGION_INFO_CAP_MSIX_MAPPABLE)) { return; } @@ -2673,7 +2673,7 @@ bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) g_autofree struct vfio_region_info *reg_info = NULL; int ret; - ret = vfio_get_region_info(vbasedev, VFIO_PCI_VGA_REGION_INDEX, ®_info); + ret = vfio_device_get_region_info(vbasedev, VFIO_PCI_VGA_REGION_INDEX, ®_info); if (ret) { error_setg_errno(errp, -ret, "failed getting region info for VGA region index %d", @@ -2771,8 +2771,8 @@ static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) QLIST_INIT(&vdev->bars[i].quirks); } - ret = vfio_get_region_info(vbasedev, - VFIO_PCI_CONFIG_REGION_INDEX, ®_info); + ret = vfio_device_get_region_info(vbasedev, + VFIO_PCI_CONFIG_REGION_INDEX, ®_info); if (ret) { error_setg_errno(errp, -ret, "failed to get config info"); return false; @@ -2816,7 +2816,7 @@ static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) static void vfio_pci_put_device(VFIOPCIDevice *vdev) { - vfio_detach_device(&vdev->vbasedev); + vfio_device_detach(&vdev->vbasedev); g_free(vdev->vbasedev.name); g_free(vdev->msix); @@ -2868,8 +2868,8 @@ static void vfio_register_err_notifier(VFIOPCIDevice *vdev) fd = event_notifier_get_fd(&vdev->err_notifier); qemu_set_fd_handler(fd, vfio_err_notifier_handler, NULL, vdev); - if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { + if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); qemu_set_fd_handler(fd, NULL, NULL, vdev); event_notifier_cleanup(&vdev->err_notifier); @@ -2885,8 +2885,8 @@ static void vfio_unregister_err_notifier(VFIOPCIDevice *vdev) return; } - if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } qemu_set_fd_handler(event_notifier_get_fd(&vdev->err_notifier), @@ -2933,8 +2933,8 @@ static void vfio_register_req_notifier(VFIOPCIDevice *vdev) fd = event_notifier_get_fd(&vdev->req_notifier); qemu_set_fd_handler(fd, vfio_req_notifier_handler, NULL, vdev); - if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { + if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); qemu_set_fd_handler(fd, NULL, NULL, vdev); event_notifier_cleanup(&vdev->req_notifier); @@ -2951,8 +2951,8 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) return; } - if (!vfio_set_irq_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { + if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } qemu_set_fd_handler(event_notifier_get_fd(&vdev->req_notifier), @@ -3015,7 +3015,7 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) name = g_strdup(vbasedev->name); } - if (!vfio_attach_device(name, vbasedev, + if (!vfio_device_attach(name, vbasedev, pci_device_iommu_address_space(pdev), errp)) { goto error; } diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index ca6528cc56..49cd9816c3 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -119,8 +119,8 @@ static int vfio_set_trigger_eventfd(VFIOINTp *intp, qemu_set_fd_handler(fd, (IOHandler *)handler, NULL, intp); - if (!vfio_set_irq_signaling(vbasedev, intp->pin, 0, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { + if (!vfio_device_irq_set_signaling(vbasedev, intp->pin, 0, + VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name); qemu_set_fd_handler(fd, NULL, NULL, NULL); return -EINVAL; @@ -301,7 +301,7 @@ static void vfio_platform_eoi(VFIODevice *vbasedev) if (vfio_irq_is_automasked(intp)) { /* unmasks the physical level-sensitive IRQ */ - vfio_unmask_single_irqindex(vbasedev, intp->pin); + vfio_device_irq_unmask(vbasedev, intp->pin); } /* a single IRQ can be active at a time */ @@ -357,8 +357,8 @@ static int vfio_set_resample_eventfd(VFIOINTp *intp) Error *err = NULL; qemu_set_fd_handler(fd, NULL, NULL, NULL); - if (!vfio_set_irq_signaling(vbasedev, intp->pin, 0, - VFIO_IRQ_SET_ACTION_UNMASK, fd, &err)) { + if (!vfio_device_irq_set_signaling(vbasedev, intp->pin, 0, + VFIO_IRQ_SET_ACTION_UNMASK, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name); return -EINVAL; } @@ -546,7 +546,7 @@ static bool vfio_base_device_init(VFIODevice *vbasedev, Error **errp) return false; } - if (!vfio_attach_device(vbasedev->name, vbasedev, + if (!vfio_device_attach(vbasedev->name, vbasedev, &address_space_memory, errp)) { return false; } @@ -555,7 +555,7 @@ static bool vfio_base_device_init(VFIODevice *vbasedev, Error **errp) return true; } - vfio_detach_device(vbasedev); + vfio_device_detach(vbasedev); return false; } diff --git a/hw/vfio/region.c b/hw/vfio/region.c index 010b5241e1..04bf9eb098 100644 --- a/hw/vfio/region.c +++ b/hw/vfio/region.c @@ -185,7 +185,7 @@ int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, g_autofree struct vfio_region_info *info = NULL; int ret; - ret = vfio_get_region_info(vbasedev, index, &info); + ret = vfio_device_get_region_info(vbasedev, index, &info); if (ret) { return ret; } diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index ddb1bcc24a..8843560576 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -37,8 +37,6 @@ vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s" vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device '%s' config: size: 0x%lx, offset: 0x%lx, flags: 0x%lx" vfio_populate_device_get_irq_info_failure(const char *errstr) "VFIO_DEVICE_GET_IRQ_INFO failure: %s" -vfio_attach_device(const char *name, int group_id) " (%s) group %d" -vfio_detach_device(const char *name, int group_id) " (%s) group %d" vfio_mdev(const char *name, bool is_mdev) " (%s) is_mdev %d" vfio_add_ext_cap_dropped(const char *name, uint16_t cap, uint16_t offset) "%s 0x%x@0x%x" vfio_pci_reset(const char *name) " (%s)" @@ -196,5 +194,7 @@ iommufd_cdev_device_info(char *name, int devfd, int num_irqs, int num_regions, i iommufd_cdev_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int dev_id) "\t%04x:%02x:%02x.%x devid %d" # device.c -vfio_get_dev_region(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x" -vfio_reset_handler(void) "" +vfio_device_get_region_info_type(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x" +vfio_device_reset_handler(void) "" +vfio_device_attach(const char *name, int group_id) " (%s) group %d" +vfio_device_detach(const char *name, int group_id) " (%s) group %d" diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index cfbe254e31..696ed87f01 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -115,29 +115,29 @@ struct VFIODeviceOps { int (*vfio_load_config)(VFIODevice *vdev, QEMUFile *f); }; -void vfio_disable_irqindex(VFIODevice *vbasedev, int index); -void vfio_unmask_single_irqindex(VFIODevice *vbasedev, int index); -void vfio_mask_single_irqindex(VFIODevice *vbasedev, int index); -bool vfio_set_irq_signaling(VFIODevice *vbasedev, int index, int subindex, - int action, int fd, Error **errp); +void vfio_device_irq_disable(VFIODevice *vbasedev, int index); +void vfio_device_irq_unmask(VFIODevice *vbasedev, int index); +void vfio_device_irq_mask(VFIODevice *vbasedev, int index); +bool vfio_device_irq_set_signaling(VFIODevice *vbasedev, int index, int subindex, + int action, int fd, Error **errp); -void vfio_reset_handler(void *opaque); +void vfio_device_reset_handler(void *opaque); bool vfio_device_is_mdev(VFIODevice *vbasedev); bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp); -bool vfio_attach_device(char *name, VFIODevice *vbasedev, +bool vfio_device_attach(char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp); -void vfio_detach_device(VFIODevice *vbasedev); +void vfio_device_detach(VFIODevice *vbasedev); VFIODevice *vfio_get_vfio_device(Object *obj); typedef QLIST_HEAD(VFIODeviceList, VFIODevice) VFIODeviceList; extern VFIODeviceList vfio_device_list; #ifdef CONFIG_LINUX -int vfio_get_region_info(VFIODevice *vbasedev, int index, - struct vfio_region_info **info); -int vfio_get_dev_region_info(VFIODevice *vbasedev, uint32_t type, - uint32_t subtype, struct vfio_region_info **info); -bool vfio_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type); +int vfio_device_get_region_info(VFIODevice *vbasedev, int index, + struct vfio_region_info **info); +int vfio_device_get_region_info_type(VFIODevice *vbasedev, uint32_t type, + uint32_t subtype, struct vfio_region_info **info); +bool vfio_device_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type); #endif /* Returns 0 on success, or a negative errno. */ From 7d810bb166820237f57b8e20272a366af2641f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 26 Mar 2025 08:51:22 +0100 Subject: [PATCH 0287/2760] vfio: Rename VFIOContainer related services MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename these routines : vfio_put_group -> vfio_group_put vfio_get_group -> vfio_group_get vfio_kvm_device_del_group -> vfio_group_del_kvm_device vfio_kvm_device_add_group -> vfio_group_add_kvm_device vfio_get_device -> vfio_device_get vfio_put_base_device -> vfio_device_put vfio_device_groupid -> vfio_device_get_groupid vfio_connect_container -> vfio_container_connect vfio_disconnect_container -> vfio_container_disconnect to better reflect the namespace they belong to. Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250318095415.670319-30-clg@redhat.com Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/20250326075122.1299361-38-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 52 ++++++++++++++++++++++---------------------- hw/vfio/trace-events | 12 +++++----- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 149ea26aff..7177747603 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -306,7 +306,7 @@ static bool vfio_get_info_iova_range(struct vfio_iommu_type1_info *info, return true; } -static void vfio_kvm_device_add_group(VFIOGroup *group) +static void vfio_group_add_kvm_device(VFIOGroup *group) { Error *err = NULL; @@ -315,7 +315,7 @@ static void vfio_kvm_device_add_group(VFIOGroup *group) } } -static void vfio_kvm_device_del_group(VFIOGroup *group) +static void vfio_group_del_kvm_device(VFIOGroup *group) { Error *err = NULL; @@ -511,7 +511,7 @@ static bool vfio_legacy_setup(VFIOContainerBase *bcontainer, Error **errp) return true; } -static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, +static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, Error **errp) { VFIOContainer *container; @@ -569,7 +569,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, } group->container = container; QLIST_INSERT_HEAD(&container->group_list, group, container_next); - vfio_kvm_device_add_group(group); + vfio_group_add_kvm_device(group); return true; } } @@ -609,7 +609,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, goto enable_discards_exit; } - vfio_kvm_device_add_group(group); + vfio_group_add_kvm_device(group); vfio_address_space_insert(space, bcontainer); @@ -625,7 +625,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, return true; listener_release_exit: QLIST_REMOVE(group, container_next); - vfio_kvm_device_del_group(group); + vfio_group_del_kvm_device(group); vfio_listener_unregister(bcontainer); if (vioc->release) { vioc->release(bcontainer); @@ -649,7 +649,7 @@ static bool vfio_connect_container(VFIOGroup *group, AddressSpace *as, return false; } -static void vfio_disconnect_container(VFIOGroup *group) +static void vfio_container_disconnect(VFIOGroup *group) { VFIOContainer *container = group->container; VFIOContainerBase *bcontainer = &container->bcontainer; @@ -678,7 +678,7 @@ static void vfio_disconnect_container(VFIOGroup *group) if (QLIST_EMPTY(&container->group_list)) { VFIOAddressSpace *space = bcontainer->space; - trace_vfio_disconnect_container(container->fd); + trace_vfio_container_disconnect(container->fd); vfio_cpr_unregister_container(bcontainer); close(container->fd); object_unref(container); @@ -687,7 +687,7 @@ static void vfio_disconnect_container(VFIOGroup *group) } } -static VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp) +static VFIOGroup *vfio_group_get(int groupid, AddressSpace *as, Error **errp) { ERRP_GUARD(); VFIOGroup *group; @@ -731,7 +731,7 @@ static VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp) group->groupid = groupid; QLIST_INIT(&group->device_list); - if (!vfio_connect_container(group, as, errp)) { + if (!vfio_container_connect(group, as, errp)) { error_prepend(errp, "failed to setup container for group %d: ", groupid); goto close_fd_exit; @@ -750,7 +750,7 @@ static VFIOGroup *vfio_get_group(int groupid, AddressSpace *as, Error **errp) return NULL; } -static void vfio_put_group(VFIOGroup *group) +static void vfio_group_put(VFIOGroup *group) { if (!group || !QLIST_EMPTY(&group->device_list)) { return; @@ -759,15 +759,15 @@ static void vfio_put_group(VFIOGroup *group) if (!group->ram_block_discard_allowed) { vfio_ram_block_discard_disable(group->container, false); } - vfio_kvm_device_del_group(group); - vfio_disconnect_container(group); + vfio_group_del_kvm_device(group); + vfio_container_disconnect(group); QLIST_REMOVE(group, next); - trace_vfio_put_group(group->fd); + trace_vfio_group_put(group->fd); close(group->fd); g_free(group); } -static bool vfio_get_device(VFIOGroup *group, const char *name, +static bool vfio_device_get(VFIOGroup *group, const char *name, VFIODevice *vbasedev, Error **errp) { g_autofree struct vfio_device_info *info = NULL; @@ -819,25 +819,25 @@ static bool vfio_get_device(VFIOGroup *group, const char *name, vbasedev->num_regions = info->num_regions; vbasedev->flags = info->flags; - trace_vfio_get_device(name, info->flags, info->num_regions, info->num_irqs); + trace_vfio_device_get(name, info->flags, info->num_regions, info->num_irqs); vbasedev->reset_works = !!(info->flags & VFIO_DEVICE_FLAGS_RESET); return true; } -static void vfio_put_base_device(VFIODevice *vbasedev) +static void vfio_device_put(VFIODevice *vbasedev) { if (!vbasedev->group) { return; } QLIST_REMOVE(vbasedev, next); vbasedev->group = NULL; - trace_vfio_put_base_device(vbasedev->fd); + trace_vfio_device_put(vbasedev->fd); close(vbasedev->fd); } -static int vfio_device_groupid(VFIODevice *vbasedev, Error **errp) +static int vfio_device_get_groupid(VFIODevice *vbasedev, Error **errp) { char *tmp, group_path[PATH_MAX]; g_autofree char *group_name = NULL; @@ -872,7 +872,7 @@ static int vfio_device_groupid(VFIODevice *vbasedev, Error **errp) static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp) { - int groupid = vfio_device_groupid(vbasedev, errp); + int groupid = vfio_device_get_groupid(vbasedev, errp); VFIODevice *vbasedev_iter; VFIOGroup *group; VFIOContainerBase *bcontainer; @@ -887,7 +887,7 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, return false; } - group = vfio_get_group(groupid, as, errp); + group = vfio_group_get(groupid, as, errp); if (!group) { return false; } @@ -895,12 +895,12 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) { error_setg(errp, "device is already attached"); - vfio_put_group(group); + vfio_group_put(group); return false; } } - if (!vfio_get_device(group, name, vbasedev, errp)) { - vfio_put_group(group); + if (!vfio_device_get(group, name, vbasedev, errp)) { + vfio_group_put(group); return false; } @@ -920,8 +920,8 @@ static void vfio_legacy_detach_device(VFIODevice *vbasedev) QLIST_REMOVE(vbasedev, container_next); vbasedev->bcontainer = NULL; trace_vfio_device_detach(vbasedev->name, group->groupid); - vfio_put_base_device(vbasedev); - vfio_put_group(group); + vfio_device_put(vbasedev); + vfio_group_put(group); } static int vfio_legacy_pci_hot_reset(VFIODevice *vbasedev, bool single) diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 8843560576..e90ec9bff8 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -99,16 +99,18 @@ vfio_listener_region_add_no_dma_map(const char *name, uint64_t iova, uint64_t si vfio_listener_region_del(uint64_t start, uint64_t end) "region_del 0x%"PRIx64" - 0x%"PRIx64 vfio_device_dirty_tracking_update(uint64_t start, uint64_t end, uint64_t min, uint64_t max) "section 0x%"PRIx64" - 0x%"PRIx64" -> update [0x%"PRIx64" - 0x%"PRIx64"]" vfio_device_dirty_tracking_start(int nr_ranges, uint64_t min32, uint64_t max32, uint64_t min64, uint64_t max64, uint64_t minpci, uint64_t maxpci) "nr_ranges %d 32:[0x%"PRIx64" - 0x%"PRIx64"], 64:[0x%"PRIx64" - 0x%"PRIx64"], pci64:[0x%"PRIx64" - 0x%"PRIx64"]" -vfio_disconnect_container(int fd) "close container->fd=%d" -vfio_put_group(int fd) "close group->fd=%d" -vfio_get_device(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" -vfio_put_base_device(int fd) "close vdev->fd=%d" -vfio_legacy_dma_unmap_overflow_workaround(void) "" vfio_iommu_map_dirty_notify(uint64_t iova_start, uint64_t iova_end) "iommu dirty @ 0x%"PRIx64" - 0x%"PRIx64 # container-base.c vfio_container_query_dirty_bitmap(uint64_t iova, uint64_t size, uint64_t bitmap_size, uint64_t start, uint64_t dirty_pages) "iova=0x%"PRIx64" size= 0x%"PRIx64" bitmap_size=0x%"PRIx64" start=0x%"PRIx64" dirty_pages=%"PRIu64 +# container.c +vfio_container_disconnect(int fd) "close container->fd=%d" +vfio_group_put(int fd) "close group->fd=%d" +vfio_device_get(const char * name, unsigned int flags, unsigned int num_regions, unsigned int num_irqs) "Device %s flags: %u, regions: %u, irqs: %u" +vfio_device_put(int fd) "close vdev->fd=%d" +vfio_legacy_dma_unmap_overflow_workaround(void) "" + # region.c vfio_region_write(const char *name, int index, uint64_t addr, uint64_t data, unsigned size) " (%s:region%d+0x%"PRIx64", 0x%"PRIx64 ", %d)" vfio_region_read(char *name, int index, uint64_t addr, unsigned size, uint64_t data) " (%s:region%d+0x%"PRIx64", %d) = 0x%"PRIx64 From 85ae745edd2815c504a3198410a545e50f583e6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 22 Apr 2025 18:29:54 +0200 Subject: [PATCH 0288/2760] MAINTAINERS: Add a maintainer for util/vfio-helpers.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NVMe Block device driver makes use of a reduced VFIO library managing the host interface. These routines are VFIO related and do not have a maintainer. Move util/vfio-helpers.c under VFIO jurisdiction. Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250422162954.210706-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 661a47db5a..a316907e76 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2197,6 +2197,7 @@ M: Alex Williamson M: Cédric Le Goater S: Supported F: hw/vfio/* +F: util/vfio-helpers.c F: include/hw/vfio/ F: docs/devel/migration/vfio.rst F: qapi/vfio.json From 436114cc4390ff46535229e67e43056196a971c1 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 23 Apr 2025 15:28:20 +0800 Subject: [PATCH 0289/2760] vfio/iommufd: Make a separate call to get IOMMU capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we depend on .realize() calling iommufd_backend_get_device_info() to get IOMMU capabilities and check for dirty page tracking support. By make a extra separate call, this dependency is removed. This happens only during device attach, it's not a hot path. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Joao Martins Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250423072824.3647952-2-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 48db105422..2253778b3a 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -287,7 +287,8 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, { ERRP_GUARD(); IOMMUFDBackend *iommufd = vbasedev->iommufd; - uint32_t flags = 0; + uint32_t type, flags = 0; + uint64_t hw_caps; VFIOIOASHwpt *hwpt; uint32_t hwpt_id; int ret; @@ -324,7 +325,12 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, * vfio_migration_realize() may decide to use VF dirty tracking * instead. */ - if (vbasedev->hiod->caps.hw_caps & IOMMU_HW_CAP_DIRTY_TRACKING) { + if (!iommufd_backend_get_device_info(vbasedev->iommufd, vbasedev->devid, + &type, NULL, 0, &hw_caps, errp)) { + return false; + } + + if (hw_caps & IOMMU_HW_CAP_DIRTY_TRACKING) { flags = IOMMU_HWPT_ALLOC_DIRTY_TRACKING; } From c45d88b8a2f25600c69748bfcf37911412c396ab Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 23 Apr 2025 15:28:21 +0800 Subject: [PATCH 0290/2760] vfio/iommufd: Move realize() after attachment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously device attaching depends on realize() getting host IOMMU capabilities to check dirty tracking support. Now we have a separate call to ioctl(IOMMU_GET_HW_INFO) to get host IOMMU capabilities and check that for dirty tracking support, there is no dependency any more, move realize() call after attachment succeed. Suggested-by: Cédric Le Goater Suggested-by: Donald Dutile Signed-off-by: Zhenzhong Duan Reviewed-by: Joao Martins Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250423072824.3647952-3-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 2253778b3a..f273dc8712 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -500,17 +500,6 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, space = vfio_address_space_get(as); - /* - * The HostIOMMUDevice data from legacy backend is static and doesn't need - * any information from the (type1-iommu) backend to be initialized. In - * contrast however, the IOMMUFD HostIOMMUDevice data requires the iommufd - * FD to be connected and having a devid to be able to successfully call - * iommufd_backend_get_device_info(). - */ - if (!vfio_device_hiod_realize(vbasedev, errp)) { - goto err_alloc_ioas; - } - /* try to attach to an existing container in this space */ QLIST_FOREACH(bcontainer, &space->containers, next) { container = container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); @@ -585,6 +574,10 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, goto err_listener_register; } + if (!vfio_device_hiod_realize(vbasedev, errp)) { + goto err_hiod_realize; + } + /* * TODO: examine RAM_BLOCK_DISCARD stuff, should we do group level * for discarding incompatibility check as well? @@ -606,6 +599,8 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, vbasedev->num_regions, vbasedev->flags); return true; +err_hiod_realize: + vfio_cpr_unregister_container(bcontainer); err_listener_register: iommufd_cdev_ram_block_discard_disable(false); err_discard_disable: From 0327ffc8530e9733526fab6c790ad5f0661b008d Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 23 Apr 2025 15:28:22 +0800 Subject: [PATCH 0291/2760] vfio/container: Move realize() after attachment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To match the change for IOMMUFD backend, move realize() after attachment for legacy backend too. Suggested-by: Cédric Le Goater Suggested-by: Donald Dutile Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250423072824.3647952-4-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 7177747603..652a6197ce 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -883,10 +883,6 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, trace_vfio_device_attach(vbasedev->name, groupid); - if (!vfio_device_hiod_realize(vbasedev, errp)) { - return false; - } - group = vfio_group_get(groupid, as, errp); if (!group) { return false; @@ -895,13 +891,15 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, QLIST_FOREACH(vbasedev_iter, &group->device_list, next) { if (strcmp(vbasedev_iter->name, vbasedev->name) == 0) { error_setg(errp, "device is already attached"); - vfio_group_put(group); - return false; + goto group_put_exit; } } if (!vfio_device_get(group, name, vbasedev, errp)) { - vfio_group_put(group); - return false; + goto group_put_exit; + } + + if (!vfio_device_hiod_realize(vbasedev, errp)) { + goto device_put_exit; } bcontainer = &group->container->bcontainer; @@ -910,6 +908,12 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, QLIST_INSERT_HEAD(&vfio_device_list, vbasedev, global_next); return true; + +device_put_exit: + vfio_device_put(vbasedev); +group_put_exit: + vfio_group_put(group); + return false; } static void vfio_legacy_detach_device(VFIODevice *vbasedev) From 0805f829a1aa07888fa7378f9421d37c521c4193 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 23 Apr 2025 15:28:23 +0800 Subject: [PATCH 0292/2760] vfio: Cleanup host IOMMU device creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit realize() is now moved after attachment, do the same for hiod creation. Introduce a new function vfio_device_hiod_create_and_realize() to do them all in one go. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250423072824.3647952-5-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 5 ++++- hw/vfio/device.c | 33 ++++++++++++++------------------- hw/vfio/iommufd.c | 4 +++- include/hw/vfio/vfio-device.h | 3 ++- 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 652a6197ce..78f70e63d6 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -898,7 +898,9 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, goto group_put_exit; } - if (!vfio_device_hiod_realize(vbasedev, errp)) { + if (!vfio_device_hiod_create_and_realize(vbasedev, + TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO, + errp)) { goto device_put_exit; } @@ -924,6 +926,7 @@ static void vfio_legacy_detach_device(VFIODevice *vbasedev) QLIST_REMOVE(vbasedev, container_next); vbasedev->bcontainer = NULL; trace_vfio_device_detach(vbasedev->name, group->groupid); + object_unref(vbasedev->hiod); vfio_device_put(vbasedev); vfio_group_put(group); } diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 4de6948cf4..d625a7c4db 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -347,15 +347,24 @@ bool vfio_device_is_mdev(VFIODevice *vbasedev) return subsys && (strcmp(subsys, "/sys/bus/mdev") == 0); } -bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp) +bool vfio_device_hiod_create_and_realize(VFIODevice *vbasedev, + const char *typename, Error **errp) { - HostIOMMUDevice *hiod = vbasedev->hiod; + HostIOMMUDevice *hiod; - if (!hiod) { + if (vbasedev->mdev) { return true; } - return HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp); + hiod = HOST_IOMMU_DEVICE(object_new(typename)); + + if (!HOST_IOMMU_DEVICE_GET_CLASS(hiod)->realize(hiod, vbasedev, errp)) { + object_unref(hiod); + return false; + } + + vbasedev->hiod = hiod; + return true; } VFIODevice *vfio_get_vfio_device(Object *obj) @@ -372,7 +381,6 @@ bool vfio_device_attach(char *name, VFIODevice *vbasedev, { const VFIOIOMMUClass *ops = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_LEGACY)); - HostIOMMUDevice *hiod = NULL; if (vbasedev->iommufd) { ops = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD)); @@ -380,19 +388,7 @@ bool vfio_device_attach(char *name, VFIODevice *vbasedev, assert(ops); - - if (!vbasedev->mdev) { - hiod = HOST_IOMMU_DEVICE(object_new(ops->hiod_typename)); - vbasedev->hiod = hiod; - } - - if (!ops->attach_device(name, vbasedev, as, errp)) { - object_unref(hiod); - vbasedev->hiod = NULL; - return false; - } - - return true; + return ops->attach_device(name, vbasedev, as, errp); } void vfio_device_detach(VFIODevice *vbasedev) @@ -400,6 +396,5 @@ void vfio_device_detach(VFIODevice *vbasedev) if (!vbasedev->bcontainer) { return; } - object_unref(vbasedev->hiod); VFIO_IOMMU_GET_CLASS(vbasedev->bcontainer)->detach_device(vbasedev); } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index f273dc8712..8a010a51ea 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -574,7 +574,8 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, goto err_listener_register; } - if (!vfio_device_hiod_realize(vbasedev, errp)) { + if (!vfio_device_hiod_create_and_realize(vbasedev, + TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO, errp)) { goto err_hiod_realize; } @@ -630,6 +631,7 @@ static void iommufd_cdev_detach(VFIODevice *vbasedev) iommufd_cdev_ram_block_discard_disable(false); } + object_unref(vbasedev->hiod); vfio_cpr_unregister_container(bcontainer); iommufd_cdev_detach_container(vbasedev, container); iommufd_cdev_container_destroy(container); diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 696ed87f01..81c95bb51e 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -123,7 +123,8 @@ bool vfio_device_irq_set_signaling(VFIODevice *vbasedev, int index, int subindex void vfio_device_reset_handler(void *opaque); bool vfio_device_is_mdev(VFIODevice *vbasedev); -bool vfio_device_hiod_realize(VFIODevice *vbasedev, Error **errp); +bool vfio_device_hiod_create_and_realize(VFIODevice *vbasedev, + const char *typename, Error **errp); bool vfio_device_attach(char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp); void vfio_device_detach(VFIODevice *vbasedev); From fdd75967836ca1affa29ad5a50a690851d310cd9 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 23 Apr 2025 15:28:24 +0800 Subject: [PATCH 0293/2760] vfio: Remove hiod_typename property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because we handle host IOMMU device creation in each container backend, we know which type name to use, so hiod_typename property is useless now, just remove it. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250423072824.3647952-6-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 2 -- hw/vfio/iommufd.c | 2 -- include/hw/vfio/vfio-container-base.h | 3 --- 3 files changed, 7 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 78f70e63d6..29170f0634 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1103,8 +1103,6 @@ static void vfio_iommu_legacy_class_init(ObjectClass *klass, void *data) { VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); - vioc->hiod_typename = TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO; - vioc->setup = vfio_legacy_setup; vioc->dma_map = vfio_legacy_dma_map; vioc->dma_unmap = vfio_legacy_dma_unmap; diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 8a010a51ea..f24054a6a5 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -795,8 +795,6 @@ static void vfio_iommu_iommufd_class_init(ObjectClass *klass, void *data) { VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); - vioc->hiod_typename = TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO; - vioc->dma_map = iommufd_cdev_map; vioc->dma_unmap = iommufd_cdev_unmap; vioc->attach_device = iommufd_cdev_attach; diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index af63373c92..5527e02722 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -115,9 +115,6 @@ OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU) struct VFIOIOMMUClass { ObjectClass parent_class; - /* Properties */ - const char *hiod_typename; - /* basic feature */ bool (*setup)(VFIOContainerBase *bcontainer, Error **errp); int (*dma_map)(const VFIOContainerBase *bcontainer, From f3028b7d2d9895799159433ccb18180c2d4d1516 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Thu, 24 Apr 2025 14:33:55 +0800 Subject: [PATCH 0294/2760] vfio: Register/unregister container for CPR only once for each container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_cpr_register_container and vfio_cpr_unregister_container are container scoped function. Calling them for each device attaching/detaching would corrupt CPR reboot notifier list, i.e., when two VFIO devices are attached to same container and have same notifier registered twice. Fixes: d9fa4223b30a ("vfio: register container for cpr") Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250424063355.3855174-1-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index f24054a6a5..b2f72dc8c3 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -416,6 +416,7 @@ static void iommufd_cdev_container_destroy(VFIOIOMMUFDContainer *container) if (!QLIST_EMPTY(&bcontainer->device_list)) { return; } + vfio_cpr_unregister_container(bcontainer); vfio_listener_unregister(bcontainer); iommufd_backend_free_id(container->be, container->ioas_id); object_unref(container); @@ -561,6 +562,10 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, goto err_listener_register; } + if (!vfio_cpr_register_container(bcontainer, errp)) { + goto err_listener_register; + } + bcontainer->initialized = true; found_container: @@ -570,13 +575,9 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, goto err_listener_register; } - if (!vfio_cpr_register_container(bcontainer, errp)) { - goto err_listener_register; - } - if (!vfio_device_hiod_create_and_realize(vbasedev, TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO, errp)) { - goto err_hiod_realize; + goto err_listener_register; } /* @@ -600,8 +601,6 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, vbasedev->num_regions, vbasedev->flags); return true; -err_hiod_realize: - vfio_cpr_unregister_container(bcontainer); err_listener_register: iommufd_cdev_ram_block_discard_disable(false); err_discard_disable: @@ -632,7 +631,6 @@ static void iommufd_cdev_detach(VFIODevice *vbasedev) } object_unref(vbasedev->hiod); - vfio_cpr_unregister_container(bcontainer); iommufd_cdev_detach_container(vbasedev, container); iommufd_cdev_container_destroy(container); vfio_address_space_put(space); From 54594b520862682d846cb6fa91705a927290eec5 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 9 Apr 2025 15:48:01 +0200 Subject: [PATCH 0295/2760] vfio: refactor out vfio_interrupt_setup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the interrupt setup code out of vfio_realize() for readability. Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250409134814.478903-2-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 55 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 6fa7217db8..27cc6426a5 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2962,6 +2962,38 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) vdev->req_enabled = false; } +static bool vfio_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) +{ + PCIDevice *pdev = &vdev->pdev; + + /* QEMU emulates all of MSI & MSIX */ + if (pdev->cap_present & QEMU_PCI_CAP_MSIX) { + memset(vdev->emulated_config_bits + pdev->msix_cap, 0xff, + MSIX_CAP_LENGTH); + } + + if (pdev->cap_present & QEMU_PCI_CAP_MSI) { + memset(vdev->emulated_config_bits + pdev->msi_cap, 0xff, + vdev->msi_cap_size); + } + + if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) { + vdev->intx.mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, + vfio_intx_mmap_enable, vdev); + pci_device_set_intx_routing_notifier(&vdev->pdev, + vfio_intx_routing_notifier); + vdev->irqchip_change_notifier.notify = vfio_irqchip_change; + kvm_irqchip_add_change_notifier(&vdev->irqchip_change_notifier); + if (!vfio_intx_enable(vdev, errp)) { + timer_free(vdev->intx.mmap_timer); + pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); + kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); + return false; + } + } + return true; +} + static void vfio_realize(PCIDevice *pdev, Error **errp) { ERRP_GUARD(); @@ -3141,27 +3173,8 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) vfio_bar_quirk_setup(vdev, i); } - /* QEMU emulates all of MSI & MSIX */ - if (pdev->cap_present & QEMU_PCI_CAP_MSIX) { - memset(vdev->emulated_config_bits + pdev->msix_cap, 0xff, - MSIX_CAP_LENGTH); - } - - if (pdev->cap_present & QEMU_PCI_CAP_MSI) { - memset(vdev->emulated_config_bits + pdev->msi_cap, 0xff, - vdev->msi_cap_size); - } - - if (vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1)) { - vdev->intx.mmap_timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, - vfio_intx_mmap_enable, vdev); - pci_device_set_intx_routing_notifier(&vdev->pdev, - vfio_intx_routing_notifier); - vdev->irqchip_change_notifier.notify = vfio_irqchip_change; - kvm_irqchip_add_change_notifier(&vdev->irqchip_change_notifier); - if (!vfio_intx_enable(vdev, errp)) { - goto out_deregister; - } + if (!vfio_interrupt_setup(vdev, errp)) { + goto out_unset_idev; } if (vdev->display != ON_OFF_AUTO_OFF) { From a9d270f6b85bab40d388fe5244f02d145759cc08 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 9 Apr 2025 15:48:02 +0200 Subject: [PATCH 0296/2760] vfio: refactor out vfio_pci_config_setup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Refactor the PCI config setup code out of vfio_realize() for readability. Reviewed-by: Cédric Le Goater Signed-off-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250409134814.478903-3-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 176 +++++++++++++++++++++++++++----------------------- 1 file changed, 94 insertions(+), 82 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 27cc6426a5..4a9484dfbe 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2962,6 +2962,99 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) vdev->req_enabled = false; } +static bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) +{ + PCIDevice *pdev = &vdev->pdev; + VFIODevice *vbasedev = &vdev->vbasedev; + + /* vfio emulates a lot for us, but some bits need extra love */ + vdev->emulated_config_bits = g_malloc0(vdev->config_size); + + /* QEMU can choose to expose the ROM or not */ + memset(vdev->emulated_config_bits + PCI_ROM_ADDRESS, 0xff, 4); + /* QEMU can also add or extend BARs */ + memset(vdev->emulated_config_bits + PCI_BASE_ADDRESS_0, 0xff, 6 * 4); + + /* + * The PCI spec reserves vendor ID 0xffff as an invalid value. The + * device ID is managed by the vendor and need only be a 16-bit value. + * Allow any 16-bit value for subsystem so they can be hidden or changed. + */ + if (vdev->vendor_id != PCI_ANY_ID) { + if (vdev->vendor_id >= 0xffff) { + error_setg(errp, "invalid PCI vendor ID provided"); + return false; + } + vfio_add_emulated_word(vdev, PCI_VENDOR_ID, vdev->vendor_id, ~0); + trace_vfio_pci_emulated_vendor_id(vbasedev->name, vdev->vendor_id); + } else { + vdev->vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID); + } + + if (vdev->device_id != PCI_ANY_ID) { + if (vdev->device_id > 0xffff) { + error_setg(errp, "invalid PCI device ID provided"); + return false; + } + vfio_add_emulated_word(vdev, PCI_DEVICE_ID, vdev->device_id, ~0); + trace_vfio_pci_emulated_device_id(vbasedev->name, vdev->device_id); + } else { + vdev->device_id = pci_get_word(pdev->config + PCI_DEVICE_ID); + } + + if (vdev->sub_vendor_id != PCI_ANY_ID) { + if (vdev->sub_vendor_id > 0xffff) { + error_setg(errp, "invalid PCI subsystem vendor ID provided"); + return false; + } + vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_VENDOR_ID, + vdev->sub_vendor_id, ~0); + trace_vfio_pci_emulated_sub_vendor_id(vbasedev->name, + vdev->sub_vendor_id); + } + + if (vdev->sub_device_id != PCI_ANY_ID) { + if (vdev->sub_device_id > 0xffff) { + error_setg(errp, "invalid PCI subsystem device ID provided"); + return false; + } + vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_ID, vdev->sub_device_id, ~0); + trace_vfio_pci_emulated_sub_device_id(vbasedev->name, + vdev->sub_device_id); + } + + /* QEMU can change multi-function devices to single function, or reverse */ + vdev->emulated_config_bits[PCI_HEADER_TYPE] = + PCI_HEADER_TYPE_MULTI_FUNCTION; + + /* Restore or clear multifunction, this is always controlled by QEMU */ + if (vdev->pdev.cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + vdev->pdev.config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; + } else { + vdev->pdev.config[PCI_HEADER_TYPE] &= ~PCI_HEADER_TYPE_MULTI_FUNCTION; + } + + /* + * Clear host resource mapping info. If we choose not to register a + * BAR, such as might be the case with the option ROM, we can get + * confusing, unwritable, residual addresses from the host here. + */ + memset(&vdev->pdev.config[PCI_BASE_ADDRESS_0], 0, 24); + memset(&vdev->pdev.config[PCI_ROM_ADDRESS], 0, 4); + + vfio_pci_size_rom(vdev); + + vfio_bars_prepare(vdev); + + if (!vfio_msix_early_setup(vdev, errp)) { + return false; + } + + vfio_bars_register(vdev); + + return true; +} + static bool vfio_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) { PCIDevice *pdev = &vdev->pdev; @@ -3066,91 +3159,10 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) goto error; } - /* vfio emulates a lot for us, but some bits need extra love */ - vdev->emulated_config_bits = g_malloc0(vdev->config_size); - - /* QEMU can choose to expose the ROM or not */ - memset(vdev->emulated_config_bits + PCI_ROM_ADDRESS, 0xff, 4); - /* QEMU can also add or extend BARs */ - memset(vdev->emulated_config_bits + PCI_BASE_ADDRESS_0, 0xff, 6 * 4); - - /* - * The PCI spec reserves vendor ID 0xffff as an invalid value. The - * device ID is managed by the vendor and need only be a 16-bit value. - * Allow any 16-bit value for subsystem so they can be hidden or changed. - */ - if (vdev->vendor_id != PCI_ANY_ID) { - if (vdev->vendor_id >= 0xffff) { - error_setg(errp, "invalid PCI vendor ID provided"); - goto error; - } - vfio_add_emulated_word(vdev, PCI_VENDOR_ID, vdev->vendor_id, ~0); - trace_vfio_pci_emulated_vendor_id(vbasedev->name, vdev->vendor_id); - } else { - vdev->vendor_id = pci_get_word(pdev->config + PCI_VENDOR_ID); - } - - if (vdev->device_id != PCI_ANY_ID) { - if (vdev->device_id > 0xffff) { - error_setg(errp, "invalid PCI device ID provided"); - goto error; - } - vfio_add_emulated_word(vdev, PCI_DEVICE_ID, vdev->device_id, ~0); - trace_vfio_pci_emulated_device_id(vbasedev->name, vdev->device_id); - } else { - vdev->device_id = pci_get_word(pdev->config + PCI_DEVICE_ID); - } - - if (vdev->sub_vendor_id != PCI_ANY_ID) { - if (vdev->sub_vendor_id > 0xffff) { - error_setg(errp, "invalid PCI subsystem vendor ID provided"); - goto error; - } - vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_VENDOR_ID, - vdev->sub_vendor_id, ~0); - trace_vfio_pci_emulated_sub_vendor_id(vbasedev->name, - vdev->sub_vendor_id); - } - - if (vdev->sub_device_id != PCI_ANY_ID) { - if (vdev->sub_device_id > 0xffff) { - error_setg(errp, "invalid PCI subsystem device ID provided"); - goto error; - } - vfio_add_emulated_word(vdev, PCI_SUBSYSTEM_ID, vdev->sub_device_id, ~0); - trace_vfio_pci_emulated_sub_device_id(vbasedev->name, - vdev->sub_device_id); - } - - /* QEMU can change multi-function devices to single function, or reverse */ - vdev->emulated_config_bits[PCI_HEADER_TYPE] = - PCI_HEADER_TYPE_MULTI_FUNCTION; - - /* Restore or clear multifunction, this is always controlled by QEMU */ - if (vdev->pdev.cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { - vdev->pdev.config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; - } else { - vdev->pdev.config[PCI_HEADER_TYPE] &= ~PCI_HEADER_TYPE_MULTI_FUNCTION; - } - - /* - * Clear host resource mapping info. If we choose not to register a - * BAR, such as might be the case with the option ROM, we can get - * confusing, unwritable, residual addresses from the host here. - */ - memset(&vdev->pdev.config[PCI_BASE_ADDRESS_0], 0, 24); - memset(&vdev->pdev.config[PCI_ROM_ADDRESS], 0, 4); - - vfio_pci_size_rom(vdev); - - vfio_bars_prepare(vdev); - - if (!vfio_msix_early_setup(vdev, errp)) { + if (!vfio_pci_config_setup(vdev, errp)) { goto error; } - vfio_bars_register(vdev); - if (!vbasedev->mdev && !pci_device_set_iommu_device(pdev, vbasedev->hiod, errp)) { error_prepend(errp, "Failed to set vIOMMU: "); From 0a5692fecc76c6fee9a4fc86dbe8faf84ce30ce8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 16:09:06 +0100 Subject: [PATCH 0297/2760] cpus: Introduce CPUClass::list_cpus() callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some targets define cpu_list to a method listing their CPUs on stdout. In order to make list_cpus() generic, introduce the CPUClass::list_cpus() callback. When no callback is registered, list_cpus() defaults to the cpu_list definition. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250324185837.46506-2-philmd@linaro.org> --- cpu-target.c | 8 +++++++- include/hw/core/cpu.h | 2 ++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/cpu-target.c b/cpu-target.c index 14cd623bff..d139a18f5b 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -104,7 +104,13 @@ static void cpu_list(void) void list_cpus(void) { - cpu_list(); + CPUClass *cc = CPU_CLASS(object_class_by_name(CPU_RESOLVING_TYPE)); + + if (cc->list_cpus) { + cc->list_cpus(); + } else { + cpu_list(); + } } /* enable or disable single step mode. EXCP_DEBUG is returned by the diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index c8d6abff19..5b645df59f 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -102,6 +102,7 @@ struct SysemuCPUOps; * CPUClass: * @class_by_name: Callback to map -cpu command line model name to an * instantiatable CPU type. + * @list_cpus: list available CPU models and flags. * @parse_features: Callback to parse command line arguments. * @reset_dump_flags: #CPUDumpFlags to use for reset logging. * @memory_rw_debug: Callback for GDB memory access. @@ -148,6 +149,7 @@ struct CPUClass { /*< public >*/ ObjectClass *(*class_by_name)(const char *cpu_model); + void (*list_cpus)(void); void (*parse_features)(const char *typename, char *str, Error **errp); int (*memory_rw_debug)(CPUState *cpu, vaddr addr, From 0bc15e47f000e83f81ac5b1aeccaee1bbfaa0d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 16:18:00 +0100 Subject: [PATCH 0298/2760] target/i386: Register CPUClass:list_cpus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register x86_cpu_list() as CPUClass:list_cpus callback. Reduce its scope and remove the cpu_list definition. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250324185837.46506-3-philmd@linaro.org> --- target/i386/cpu.c | 3 ++- target/i386/cpu.h | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 1f970aa4da..955295fe79 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6319,7 +6319,7 @@ static void x86_cpu_list_entry(gpointer data, gpointer user_data) } /* list available CPU models and flags */ -void x86_cpu_list(void) +static void x86_cpu_list(void) { int i, j; GSList *list; @@ -8905,6 +8905,7 @@ static void x86_cpu_common_class_init(ObjectClass *oc, void *data) cc->reset_dump_flags = CPU_DUMP_FPU | CPU_DUMP_CCOP; cc->class_by_name = x86_cpu_class_by_name; + cc->list_cpus = x86_cpu_list; cc->parse_features = x86_cpu_parse_featurestr; cc->dump_state = x86_cpu_dump_state; cc->set_pc = x86_cpu_set_pc; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 9866595cd0..54bf9639f1 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2365,7 +2365,6 @@ int x86_cpu_gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); int x86_cpu_gdb_write_register(CPUState *cpu, uint8_t *buf, int reg); void x86_cpu_gdb_init(CPUState *cs); -void x86_cpu_list(void); int cpu_x86_support_mca_broadcast(CPUX86State *env); #ifndef CONFIG_USER_ONLY @@ -2559,8 +2558,6 @@ uint64_t cpu_get_tsc(CPUX86State *env); #define TARGET_DEFAULT_CPU_TYPE X86_CPU_TYPE_NAME("qemu32") #endif -#define cpu_list x86_cpu_list - /* MMU modes definitions */ #define MMU_KSMAP64_IDX 0 #define MMU_KSMAP32_IDX 1 From 53c895fbb2d31b0de6a04a6f7e86e17b48324dc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 16:22:41 +0100 Subject: [PATCH 0299/2760] target/ppc: Register CPUClass:list_cpus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register ppc_cpu_list() as CPUClass:list_cpus callback. Reduce its scope and remove the cpu_list definition. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250324185837.46506-4-philmd@linaro.org> --- target/ppc/cpu.h | 4 ---- target/ppc/cpu_init.c | 3 ++- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 3c02f7f7d4..f4cc823c5c 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -1610,8 +1610,6 @@ void ppc_store_dawrx1(CPUPPCState *env, uint32_t value); #endif /* !defined(CONFIG_USER_ONLY) */ void ppc_store_msr(CPUPPCState *env, target_ulong value); -void ppc_cpu_list(void); - /* Time-base and decrementer management */ uint64_t cpu_ppc_load_tbl(CPUPPCState *env); uint32_t cpu_ppc_load_tbu(CPUPPCState *env); @@ -1673,8 +1671,6 @@ static inline uint64_t ppc_dump_gpr(CPUPPCState *env, int gprn) int ppc_dcr_read(ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp); int ppc_dcr_write(ppc_dcr_t *dcr_env, int dcrn, uint32_t val); -#define cpu_list ppc_cpu_list - /* MMU modes definitions */ #define MMU_USER_IDX 0 static inline int ppc_env_mmu_index(CPUPPCState *env, bool ifetch) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index fde7d71fc6..077991ed53 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7177,7 +7177,7 @@ static void ppc_cpu_list_entry(gpointer data, gpointer user_data) g_free(name); } -void ppc_cpu_list(void) +static void ppc_cpu_list(void) { GSList *list; @@ -7521,6 +7521,7 @@ static void ppc_cpu_class_init(ObjectClass *oc, void *data) &pcc->parent_phases); cc->class_by_name = ppc_cpu_class_by_name; + cc->list_cpus = ppc_cpu_list; cc->dump_state = ppc_cpu_dump_state; cc->set_pc = ppc_cpu_set_pc; cc->get_pc = ppc_cpu_get_pc; From 8b54467fb61eb507651ea8be6d786b0f2b0a1524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 16:15:35 +0100 Subject: [PATCH 0300/2760] target/sparc: Register CPUClass:list_cpus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register sparc_cpu_list() as CPUClass:list_cpus callback. Reduce its scope and remove the cpu_list definition. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250324185837.46506-5-philmd@linaro.org> --- target/sparc/cpu.c | 3 ++- target/sparc/cpu.h | 3 --- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index f7d231c6f8..174b76f762 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -580,7 +580,7 @@ static void print_features(uint32_t features, const char *prefix) } } -void sparc_cpu_list(void) +static void sparc_cpu_list(void) { unsigned int i; @@ -1057,6 +1057,7 @@ static void sparc_cpu_class_init(ObjectClass *oc, void *data) &scc->parent_phases); cc->class_by_name = sparc_cpu_class_by_name; + cc->list_cpus = sparc_cpu_list, cc->parse_features = sparc_cpu_parse_features; cc->dump_state = sparc_cpu_dump_state; #if !defined(TARGET_SPARC64) && !defined(CONFIG_USER_ONLY) diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 83ac818933..37fd1e066e 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -595,7 +595,6 @@ G_NORETURN void cpu_raise_exception_ra(CPUSPARCState *, int, uintptr_t); /* cpu_init.c */ void cpu_sparc_set_id(CPUSPARCState *env, unsigned int cpu); -void sparc_cpu_list(void); /* mmu_helper.c */ bool sparc_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, @@ -666,8 +665,6 @@ hwaddr cpu_get_phys_page_nofault(CPUSPARCState *env, target_ulong addr, #define CPU_RESOLVING_TYPE TYPE_SPARC_CPU -#define cpu_list sparc_cpu_list - /* MMU modes definitions */ #if defined (TARGET_SPARC64) #define MMU_USER_IDX 0 From e828206a0f63867deb9eedc194c49cc40a3b249b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 24 Mar 2025 19:29:03 +0100 Subject: [PATCH 0301/2760] target/s390x: Register CPUClass:list_cpus MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both s390_cpu_list() and s390_set_qemu_cpu_model() are defined in cpu_models.c, move their declarations in the related "cpu_models.h" header. Use full path to header in s390-virtio-ccw.c file. Register s390_cpu_list() as CPUClass:list_cpus callback and remove the cpu_list definition. Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Eric Farman Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250324185837.46506-6-philmd@linaro.org> --- hw/s390x/s390-virtio-ccw.c | 2 +- target/s390x/cpu.c | 1 + target/s390x/cpu.h | 7 ------- target/s390x/cpu_models.h | 3 +++ 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 52c273b3de..cf5b9974c5 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -35,7 +35,7 @@ #include "hw/s390x/css-bridge.h" #include "hw/s390x/ap-bridge.h" #include "migration/register.h" -#include "cpu_models.h" +#include "target/s390x/cpu_models.h" #include "hw/nmi.h" #include "hw/qdev-properties.h" #include "hw/s390x/tod.h" diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 41cccc1e69..43fc3194bc 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -386,6 +386,7 @@ static void s390_cpu_class_init(ObjectClass *oc, void *data) &scc->parent_phases); cc->class_by_name = s390_cpu_class_by_name; + cc->list_cpus = s390_cpu_list; cc->dump_state = s390_cpu_dump_state; cc->query_cpu_fast = s390_query_cpu_fast; cc->set_pc = s390_cpu_set_pc; diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 90f64ee20c..d9ca2506e2 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -900,13 +900,6 @@ static inline uint8_t s390_cpu_get_state(S390CPU *cpu) } -/* cpu_models.c */ -void s390_cpu_list(void); -#define cpu_list s390_cpu_list -void s390_set_qemu_cpu_model(uint16_t type, uint8_t gen, uint8_t ec_ga, - const S390FeatInit feat_init); - - /* helper.c */ #define CPU_RESOLVING_TYPE TYPE_S390_CPU diff --git a/target/s390x/cpu_models.h b/target/s390x/cpu_models.h index 71d4bc2dd4..f701bc0b53 100644 --- a/target/s390x/cpu_models.h +++ b/target/s390x/cpu_models.h @@ -113,6 +113,9 @@ static inline uint64_t s390_cpuid_from_cpu_model(const S390CPUModel *model) } S390CPUDef const *s390_find_cpu_def(uint16_t type, uint8_t gen, uint8_t ec_ga, S390FeatBitmap features); +void s390_set_qemu_cpu_model(uint16_t type, uint8_t gen, uint8_t ec_ga, + const S390FeatInit feat_init); +void s390_cpu_list(void); bool kvm_s390_cpu_models_supported(void); bool kvm_s390_get_host_cpu_model(S390CPUModel *model, Error **errp); From edbb66bb305b7d7f9b13734d222cc5e333180948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 16:24:29 +0100 Subject: [PATCH 0302/2760] cpus: Remove #ifdef check on cpu_list definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we removed all definitions of cpu_list, the #ifdef check is always true. Remove it, inlining cpu_list(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250324185837.46506-7-philmd@linaro.org> --- cpu-target.c | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index d139a18f5b..c99d208a7c 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -77,7 +77,6 @@ const char *parse_cpu_option(const char *cpu_option) return cpu_type; } -#ifndef cpu_list static void cpu_list_entry(gpointer data, gpointer user_data) { CPUClass *cc = CPU_CLASS(OBJECT_CLASS(data)); @@ -91,17 +90,6 @@ static void cpu_list_entry(gpointer data, gpointer user_data) } } -static void cpu_list(void) -{ - GSList *list; - - list = object_class_get_list_sorted(TYPE_CPU, false); - qemu_printf("Available CPUs:\n"); - g_slist_foreach(list, cpu_list_entry, NULL); - g_slist_free(list); -} -#endif - void list_cpus(void) { CPUClass *cc = CPU_CLASS(object_class_by_name(CPU_RESOLVING_TYPE)); @@ -109,7 +97,12 @@ void list_cpus(void) if (cc->list_cpus) { cc->list_cpus(); } else { - cpu_list(); + GSList *list; + + list = object_class_get_list_sorted(TYPE_CPU, false); + qemu_printf("Available CPUs:\n"); + g_slist_foreach(list, cpu_list_entry, NULL); + g_slist_free(list); } } From f605796aae42885034400c83ed6a9b07cd6d6481 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 17 Apr 2025 11:05:27 -0400 Subject: [PATCH 0303/2760] file-posix: probe discard alignment on Linux block devices Populate the pdiscard_alignment block limit so the block layer is able align discard requests correctly. Signed-off-by: Stefan Hajnoczi Message-ID: <20250417150528.76470-2-stefanha@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/file-posix.c | 67 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/block/file-posix.c b/block/file-posix.c index 56d1972d15..0d6e12f880 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1276,10 +1276,10 @@ static int get_sysfs_zoned_model(struct stat *st, BlockZoneModel *zoned) } #endif /* defined(CONFIG_BLKZONED) */ +#ifdef CONFIG_LINUX /* * Get a sysfs attribute value as a long integer. */ -#ifdef CONFIG_LINUX static long get_sysfs_long_val(struct stat *st, const char *attribute) { g_autofree char *str = NULL; @@ -1299,6 +1299,30 @@ static long get_sysfs_long_val(struct stat *st, const char *attribute) } return ret; } + +/* + * Get a sysfs attribute value as a uint32_t. + */ +static int get_sysfs_u32_val(struct stat *st, const char *attribute, + uint32_t *u32) +{ + g_autofree char *str = NULL; + const char *end; + unsigned int val; + int ret; + + ret = get_sysfs_str_val(st, attribute, &str); + if (ret < 0) { + return ret; + } + + /* The file is ended with '\n', pass 'end' to accept that. */ + ret = qemu_strtoui(str, &end, 10, &val); + if (ret == 0 && end && *end == '\0') { + *u32 = val; + } + return ret; +} #endif static int hdev_get_max_segments(int fd, struct stat *st) @@ -1318,6 +1342,23 @@ static int hdev_get_max_segments(int fd, struct stat *st) #endif } +/* + * Fills in *dalign with the discard alignment and returns 0 on success, + * -errno otherwise. + */ +static int hdev_get_pdiscard_alignment(struct stat *st, uint32_t *dalign) +{ +#ifdef CONFIG_LINUX + /* + * Note that Linux "discard_granularity" is QEMU "discard_alignment". Linux + * "discard_alignment" is something else. + */ + return get_sysfs_u32_val(st, "discard_granularity", dalign); +#else + return -ENOTSUP; +#endif +} + #if defined(CONFIG_BLKZONED) /* * If the reset_all flag is true, then the wps of zone whose state is @@ -1527,6 +1568,30 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) } } + if (S_ISBLK(st.st_mode)) { + uint32_t dalign = 0; + int ret; + + ret = hdev_get_pdiscard_alignment(&st, &dalign); + if (ret == 0) { + uint32_t ralign = bs->bl.request_alignment; + + /* Probably never happens, but handle it just in case */ + if (dalign < ralign && (ralign % dalign == 0)) { + dalign = ralign; + } + + /* The block layer requires a multiple of request_alignment */ + if (dalign % ralign != 0) { + error_setg(errp, "Invalid pdiscard_alignment limit %u is not a " + "multiple of request_alignment %u", dalign, ralign); + return; + } + + bs->bl.pdiscard_alignment = dalign; + } + } + raw_refresh_zoned_limits(bs, &st, errp); } From 4733cb0833c4b223f92ec0136980eeb5239ecb87 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 17 Apr 2025 11:05:28 -0400 Subject: [PATCH 0304/2760] block/io: skip head/tail requests on EINVAL When guests send misaligned discard requests, the block layer breaks them up into a misaligned head, an aligned main body, and a misaligned tail. The file-posix block driver on Linux returns -EINVAL on misaligned discard requests. This causes bdrv_co_pdiscard() to fail and guests configured with werror=stop will pause. Add a special case for misaligned head/tail requests. Simply continue when EINVAL is encountered so that the aligned main body of the request can be completed and the guest is not paused. This is the best we can do when guest discard limits do not match the host discard limits. Fixes: https://issues.redhat.com/browse/RHEL-86032 Signed-off-by: Stefan Hajnoczi Reviewed-by: Hanna Czenczek Message-ID: <20250417150528.76470-3-stefanha@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/io.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/block/io.c b/block/io.c index 1ba8d1aeea..ccec11386b 100644 --- a/block/io.c +++ b/block/io.c @@ -3109,11 +3109,12 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, /* Invalidate the cached block-status data range if this discard overlaps */ bdrv_bsc_invalidate_range(bs, offset, bytes); - /* Discard is advisory, but some devices track and coalesce + /* + * Discard is advisory, but some devices track and coalesce * unaligned requests, so we must pass everything down rather than - * round here. Still, most devices will just silently ignore - * unaligned requests (by returning -ENOTSUP), so we must fragment - * the request accordingly. */ + * round here. Still, most devices reject unaligned requests with + * -EINVAL or -ENOTSUP, so we must fragment the request accordingly. + */ align = MAX(bs->bl.pdiscard_alignment, bs->bl.request_alignment); assert(align % bs->bl.request_alignment == 0); head = offset % align; @@ -3180,7 +3181,11 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, } } if (ret && ret != -ENOTSUP) { - goto out; + if (ret == -EINVAL && (offset % align != 0 || num % align != 0)) { + /* Silently skip rejected unaligned head/tail requests */ + } else { + goto out; /* bail out */ + } } offset += num; From 6970f91ac7819e186dc0e2efea08120450fb5404 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 31 Mar 2025 17:15:47 +0200 Subject: [PATCH 0305/2760] hw/pci-host/designware: Use deposit/extract API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer the safer (less bug-prone) deposit/extract API to access lower/upper 32-bit of 64-bit registers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Gustavo Romero Message-Id: <20250331152041.74533-3-philmd@linaro.org> --- hw/pci-host/designware.c | 48 ++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 31 deletions(-) diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c index 5598d18f47..9c3a5f8d91 100644 --- a/hw/pci-host/designware.c +++ b/hw/pci-host/designware.c @@ -22,6 +22,7 @@ #include "qapi/error.h" #include "qemu/module.h" #include "qemu/log.h" +#include "qemu/bitops.h" #include "hw/pci/msi.h" #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_host.h" @@ -162,11 +163,9 @@ designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len) break; case DESIGNWARE_PCIE_MSI_ADDR_LO: - val = root->msi.base; - break; - case DESIGNWARE_PCIE_MSI_ADDR_HI: - val = root->msi.base >> 32; + val = extract64(root->msi.base, + address == DESIGNWARE_PCIE_MSI_ADDR_LO ? 0 : 32, 32); break; case DESIGNWARE_PCIE_MSI_INTR0_ENABLE: @@ -190,19 +189,16 @@ designware_pcie_root_config_read(PCIDevice *d, uint32_t address, int len) break; case DESIGNWARE_PCIE_ATU_LOWER_BASE: - val = viewport->base; - break; - case DESIGNWARE_PCIE_ATU_UPPER_BASE: - val = viewport->base >> 32; + val = extract64(viewport->base, + address == DESIGNWARE_PCIE_ATU_LOWER_BASE ? 0 : 32, 32); break; case DESIGNWARE_PCIE_ATU_LOWER_TARGET: - val = viewport->target; - break; - case DESIGNWARE_PCIE_ATU_UPPER_TARGET: - val = viewport->target >> 32; + val = extract64(viewport->target, + address == DESIGNWARE_PCIE_ATU_LOWER_TARGET ? 0 : 32, + 32); break; case DESIGNWARE_PCIE_ATU_LIMIT: @@ -321,14 +317,10 @@ static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address, break; case DESIGNWARE_PCIE_MSI_ADDR_LO: - root->msi.base &= 0xFFFFFFFF00000000ULL; - root->msi.base |= val; - designware_pcie_root_update_msi_mapping(root); - break; - case DESIGNWARE_PCIE_MSI_ADDR_HI: - root->msi.base &= 0x00000000FFFFFFFFULL; - root->msi.base |= (uint64_t)val << 32; + root->msi.base = deposit64(root->msi.base, + address == DESIGNWARE_PCIE_MSI_ADDR_LO + ? 0 : 32, 32, val); designware_pcie_root_update_msi_mapping(root); break; @@ -355,23 +347,17 @@ static void designware_pcie_root_config_write(PCIDevice *d, uint32_t address, break; case DESIGNWARE_PCIE_ATU_LOWER_BASE: - viewport->base &= 0xFFFFFFFF00000000ULL; - viewport->base |= val; - break; - case DESIGNWARE_PCIE_ATU_UPPER_BASE: - viewport->base &= 0x00000000FFFFFFFFULL; - viewport->base |= (uint64_t)val << 32; + viewport->base = deposit64(root->msi.base, + address == DESIGNWARE_PCIE_ATU_LOWER_BASE + ? 0 : 32, 32, val); break; case DESIGNWARE_PCIE_ATU_LOWER_TARGET: - viewport->target &= 0xFFFFFFFF00000000ULL; - viewport->target |= val; - break; - case DESIGNWARE_PCIE_ATU_UPPER_TARGET: - viewport->target &= 0x00000000FFFFFFFFULL; - viewport->target |= (uint64_t)val << 32; + viewport->target = deposit64(root->msi.base, + address == DESIGNWARE_PCIE_ATU_LOWER_TARGET + ? 0 : 32, 32, val); break; case DESIGNWARE_PCIE_ATU_LIMIT: From fbb23135d691d20f713bf2318e1cf6575e77cda7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 23 Apr 2025 19:00:39 +0200 Subject: [PATCH 0306/2760] hw/misc/edu: Convert type_init() -> DEFINE_TYPES() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Prefer DEFINE_TYPES() macro over type_init() to register QOM types. Initialize the .interfaces struct field as compound literal casted to InterfaceInfo type like the rest of our code base. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250424194905.82506-2-philmd@linaro.org> --- hw/misc/edu.c | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/hw/misc/edu.c b/hw/misc/edu.c index 504178b4a2..5723ef0ed1 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -429,21 +429,18 @@ static void edu_class_init(ObjectClass *class, void *data) set_bit(DEVICE_CATEGORY_MISC, dc->categories); } -static void pci_edu_register_types(void) -{ - static InterfaceInfo interfaces[] = { - { INTERFACE_CONVENTIONAL_PCI_DEVICE }, - { }, - }; - static const TypeInfo edu_info = { +static const TypeInfo edu_types[] = { + { .name = TYPE_PCI_EDU_DEVICE, .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(EduState), .instance_init = edu_instance_init, .class_init = edu_class_init, - .interfaces = interfaces, - }; + .interfaces = (InterfaceInfo[]) { + { INTERFACE_CONVENTIONAL_PCI_DEVICE }, + { }, + }, + } +}; - type_register_static(&edu_info); -} -type_init(pci_edu_register_types) +DEFINE_TYPES(edu_types) From f1fa787b92c52d1034de164d2a26771ff969454e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 9 Feb 2025 22:15:54 +0100 Subject: [PATCH 0307/2760] qom: Have class_base_init() take a const data argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250424194905.82506-3-philmd@linaro.org> --- hw/core/machine.c | 2 +- hw/core/qdev.c | 2 +- hw/pci/pci.c | 2 +- include/qom/object.h | 2 +- qom/object.c | 2 +- rust/qemu-api/src/qom.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index abfcedd4a5..bbff84855a 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1243,7 +1243,7 @@ static void machine_class_init(ObjectClass *oc, void *data) "Memory size configuration"); } -static void machine_class_base_init(ObjectClass *oc, void *data) +static void machine_class_base_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->max_cpus = mc->max_cpus ?: 1; diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 2745b5e092..1e0f47cc84 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -693,7 +693,7 @@ static void device_finalize(Object *obj) g_free(dev->id); } -static void device_class_base_init(ObjectClass *class, void *data) +static void device_class_base_init(ObjectClass *class, const void *data) { DeviceClass *klass = DEVICE_CLASS(class); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 2844ec5556..475b97c649 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2809,7 +2809,7 @@ static void pci_device_class_init(ObjectClass *klass, void *data) "access to indirect DMA memory"); } -static void pci_device_class_base_init(ObjectClass *klass, void *data) +static void pci_device_class_base_init(ObjectClass *klass, const void *data) { if (!object_class_is_abstract(klass)) { ObjectClass *conventional = diff --git a/include/qom/object.h b/include/qom/object.h index 9192265db7..7bb14ca706 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -487,7 +487,7 @@ struct TypeInfo size_t class_size; void (*class_init)(ObjectClass *klass, void *data); - void (*class_base_init)(ObjectClass *klass, void *data); + void (*class_base_init)(ObjectClass *klass, const void *data); void *class_data; InterfaceInfo *interfaces; diff --git a/qom/object.c b/qom/object.c index 01618d06bd..dfd59502d1 100644 --- a/qom/object.c +++ b/qom/object.c @@ -55,7 +55,7 @@ struct TypeImpl size_t instance_align; void (*class_init)(ObjectClass *klass, void *data); - void (*class_base_init)(ObjectClass *klass, void *data); + void (*class_base_init)(ObjectClass *klass, const void *data); void *class_data; diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 34d7bc0ef9..03fe6247ff 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -492,7 +492,7 @@ pub trait ObjectImpl: ObjectType + IsA { /// the effects of copying the contents of the parent's class struct /// to the descendants. const CLASS_BASE_INIT: Option< - unsafe extern "C" fn(klass: *mut ObjectClass, data: *mut c_void), + unsafe extern "C" fn(klass: *mut ObjectClass, data: *const c_void), > = None; const TYPE_INFO: TypeInfo = TypeInfo { From 12d1a768bdfea6e27a3a829228840d72507613a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 9 Feb 2025 23:47:35 +0100 Subject: [PATCH 0308/2760] qom: Have class_init() take a const data argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanical change using gsed, then style manually adapted to pass checkpatch.pl script. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250424194905.82506-4-philmd@linaro.org> --- accel/hvf/hvf-accel-ops.c | 4 +- accel/kvm/kvm-accel-ops.c | 2 +- accel/kvm/kvm-all.c | 2 +- accel/qtest/qtest.c | 4 +- accel/tcg/tcg-accel-ops.c | 2 +- accel/tcg/tcg-all.c | 2 +- accel/xen/xen-all.c | 4 +- authz/list.c | 2 +- authz/listfile.c | 2 +- authz/pamacct.c | 2 +- authz/simple.c | 2 +- backends/confidential-guest-support.c | 3 +- backends/cryptodev-builtin.c | 2 +- backends/cryptodev-lkcf.c | 2 +- backends/cryptodev-vhost-user.c | 2 +- backends/cryptodev.c | 2 +- backends/dbus-vmstate.c | 2 +- backends/host_iommu_device.c | 2 +- backends/hostmem-epc.c | 2 +- backends/hostmem-file.c | 2 +- backends/hostmem-memfd.c | 2 +- backends/hostmem-ram.c | 2 +- backends/hostmem-shm.c | 2 +- backends/hostmem.c | 2 +- backends/iommufd.c | 4 +- backends/rng-builtin.c | 2 +- backends/rng-egd.c | 2 +- backends/rng-random.c | 2 +- backends/rng.c | 2 +- backends/tpm/tpm_emulator.c | 2 +- backends/tpm/tpm_passthrough.c | 2 +- backends/vhost-user.c | 2 +- block/throttle-groups.c | 3 +- chardev/baum.c | 2 +- chardev/char-console.c | 2 +- chardev/char-fd.c | 2 +- chardev/char-file.c | 2 +- chardev/char-hub.c | 2 +- chardev/char-mux.c | 2 +- chardev/char-null.c | 2 +- chardev/char-parallel.c | 2 +- chardev/char-pipe.c | 2 +- chardev/char-pty.c | 2 +- chardev/char-ringbuf.c | 2 +- chardev/char-serial.c | 2 +- chardev/char-socket.c | 2 +- chardev/char-stdio.c | 2 +- chardev/char-udp.c | 2 +- chardev/char-win-stdio.c | 2 +- chardev/char-win.c | 2 +- chardev/char.c | 2 +- chardev/msmouse.c | 2 +- chardev/spice.c | 6 +- chardev/testdev.c | 2 +- chardev/wctablet.c | 2 +- crypto/secret.c | 2 +- crypto/secret_common.c | 2 +- crypto/secret_keyring.c | 2 +- crypto/tls-cipher-suites.c | 3 +- crypto/tlscreds.c | 2 +- crypto/tlscredsanon.c | 2 +- crypto/tlscredspsk.c | 2 +- crypto/tlscredsx509.c | 2 +- docs/devel/qom.rst | 8 +- docs/devel/reset.rst | 2 +- docs/devel/virtio-backends.rst | 2 +- event-loop-base.c | 3 +- gdbstub/system.c | 2 +- hw/9pfs/virtio-9p-device.c | 2 +- hw/acpi/erst.c | 2 +- hw/acpi/generic_event_device.c | 2 +- hw/acpi/pci.c | 4 +- hw/acpi/piix4.c | 2 +- hw/acpi/vmclock.c | 2 +- hw/acpi/vmgenid.c | 2 +- hw/adc/aspeed_adc.c | 10 +-- hw/adc/npcm7xx_adc.c | 2 +- hw/adc/stm32f2xx_adc.c | 2 +- hw/adc/zynq-xadc.c | 2 +- hw/alpha/typhoon.c | 2 +- hw/arm/allwinner-a10.c | 2 +- hw/arm/allwinner-h3.c | 2 +- hw/arm/allwinner-r40.c | 2 +- hw/arm/armsse.c | 2 +- hw/arm/armv7m.c | 4 +- hw/arm/aspeed.c | 59 +++++++----- hw/arm/aspeed_ast10x0.c | 2 +- hw/arm/aspeed_ast2400.c | 4 +- hw/arm/aspeed_ast2600.c | 2 +- hw/arm/aspeed_ast27x0.c | 4 +- hw/arm/aspeed_soc_common.c | 2 +- hw/arm/b-l475e-iot01a.c | 2 +- hw/arm/bcm2835_peripherals.c | 2 +- hw/arm/bcm2836.c | 8 +- hw/arm/bcm2838.c | 2 +- hw/arm/bcm2838_peripherals.c | 2 +- hw/arm/collie.c | 2 +- hw/arm/digic.c | 2 +- hw/arm/exynos4210.c | 2 +- hw/arm/exynos4_boards.c | 4 +- hw/arm/fby35.c | 2 +- hw/arm/fsl-imx25.c | 2 +- hw/arm/fsl-imx31.c | 2 +- hw/arm/fsl-imx6.c | 2 +- hw/arm/fsl-imx6ul.c | 2 +- hw/arm/fsl-imx7.c | 2 +- hw/arm/fsl-imx8mp.c | 2 +- hw/arm/highbank.c | 6 +- hw/arm/integratorcp.c | 6 +- hw/arm/microbit.c | 2 +- hw/arm/mps2-tz.c | 10 +-- hw/arm/mps2.c | 10 +-- hw/arm/mps3r.c | 4 +- hw/arm/msf2-soc.c | 2 +- hw/arm/musca.c | 6 +- hw/arm/musicpal.c | 14 +-- hw/arm/npcm7xx.c | 6 +- hw/arm/npcm7xx_boards.c | 12 +-- hw/arm/npcm8xx.c | 2 +- hw/arm/npcm8xx_boards.c | 4 +- hw/arm/nrf51_soc.c | 2 +- hw/arm/omap_sx1.c | 4 +- hw/arm/raspi.c | 10 +-- hw/arm/raspi4b.c | 2 +- hw/arm/realview.c | 8 +- hw/arm/sbsa-ref.c | 2 +- hw/arm/smmu-common.c | 2 +- hw/arm/smmuv3.c | 4 +- hw/arm/stellaris.c | 10 +-- hw/arm/stm32f100_soc.c | 2 +- hw/arm/stm32f205_soc.c | 2 +- hw/arm/stm32f405_soc.c | 2 +- hw/arm/stm32l4x5_soc.c | 8 +- hw/arm/strongarm.c | 13 +-- hw/arm/versatilepb.c | 6 +- hw/arm/vexpress.c | 6 +- hw/arm/virt.c | 4 +- hw/arm/xen-pvh.c | 2 +- hw/arm/xilinx_zynq.c | 2 +- hw/arm/xlnx-versal-virt.c | 2 +- hw/arm/xlnx-versal.c | 2 +- hw/arm/xlnx-zcu102.c | 2 +- hw/arm/xlnx-zynqmp.c | 2 +- hw/audio/ac97.c | 2 +- hw/audio/adlib.c | 2 +- hw/audio/asc.c | 2 +- hw/audio/cs4231.c | 2 +- hw/audio/cs4231a.c | 2 +- hw/audio/es1370.c | 2 +- hw/audio/gus.c | 2 +- hw/audio/hda-codec.c | 8 +- hw/audio/intel-hda.c | 8 +- hw/audio/marvell_88w8618.c | 2 +- hw/audio/pcspk.c | 2 +- hw/audio/pl041.c | 2 +- hw/audio/sb16.c | 2 +- hw/audio/via-ac97.c | 4 +- hw/audio/virtio-snd-pci.c | 2 +- hw/audio/virtio-snd.c | 2 +- hw/audio/wm8750.c | 2 +- hw/avr/arduino.c | 10 +-- hw/avr/atmega.c | 10 +-- hw/block/fdc-isa.c | 2 +- hw/block/fdc-sysbus.c | 6 +- hw/block/fdc.c | 2 +- hw/block/m25p80.c | 4 +- hw/block/nand.c | 2 +- hw/block/pflash_cfi01.c | 2 +- hw/block/pflash_cfi02.c | 2 +- hw/block/swim.c | 4 +- hw/block/vhost-user-blk.c | 2 +- hw/block/virtio-blk.c | 2 +- hw/block/xen-block.c | 6 +- hw/char/avr_usart.c | 2 +- hw/char/bcm2835_aux.c | 2 +- hw/char/cadence_uart.c | 2 +- hw/char/cmsdk-apb-uart.c | 2 +- hw/char/debugcon.c | 2 +- hw/char/digic-uart.c | 2 +- hw/char/diva-gsp.c | 4 +- hw/char/escc.c | 2 +- hw/char/exynos4210_uart.c | 2 +- hw/char/goldfish_tty.c | 2 +- hw/char/grlib_apbuart.c | 2 +- hw/char/ibex_uart.c | 2 +- hw/char/imx_serial.c | 2 +- hw/char/ipoctal232.c | 2 +- hw/char/mcf_uart.c | 2 +- hw/char/mchp_pfsoc_mmuart.c | 2 +- hw/char/nrf51_uart.c | 2 +- hw/char/parallel.c | 2 +- hw/char/pl011.c | 2 +- hw/char/renesas_sci.c | 2 +- hw/char/sclpconsole-lm.c | 2 +- hw/char/sclpconsole.c | 2 +- hw/char/serial-isa.c | 2 +- hw/char/serial-mm.c | 2 +- hw/char/serial-pci-multi.c | 6 +- hw/char/serial-pci.c | 2 +- hw/char/serial.c | 2 +- hw/char/sh_serial.c | 2 +- hw/char/shakti_uart.c | 2 +- hw/char/sifive_uart.c | 2 +- hw/char/spapr_vty.c | 2 +- hw/char/stm32f2xx_usart.c | 2 +- hw/char/stm32l4x5_usart.c | 9 +- hw/char/terminal3270.c | 2 +- hw/char/virtio-console.c | 4 +- hw/char/virtio-serial-bus.c | 6 +- hw/char/xen_console.c | 2 +- hw/char/xilinx_uartlite.c | 2 +- hw/core/bus.c | 2 +- hw/core/clock.c | 2 +- hw/core/cpu-common.c | 2 +- hw/core/generic-loader.c | 2 +- hw/core/guest-loader.c | 2 +- hw/core/machine.c | 2 +- hw/core/or-irq.c | 2 +- hw/core/platform-bus.c | 2 +- hw/core/qdev.c | 2 +- hw/core/register.c | 2 +- hw/core/reset.c | 2 +- hw/core/resetcontainer.c | 3 +- hw/core/split-irq.c | 2 +- hw/core/sysbus.c | 7 +- hw/cpu/a15mpcore.c | 2 +- hw/cpu/a9mpcore.c | 2 +- hw/cpu/arm11mpcore.c | 2 +- hw/cpu/cluster.c | 2 +- hw/cpu/core.c | 2 +- hw/cpu/realview_mpcore.c | 2 +- hw/cxl/switch-mailbox-cci.c | 2 +- hw/display/apple-gfx-mmio.m | 2 +- hw/display/apple-gfx-pci.m | 2 +- hw/display/artist.c | 2 +- hw/display/ati.c | 2 +- hw/display/bcm2835_fb.c | 2 +- hw/display/bochs-display.c | 2 +- hw/display/cg3.c | 2 +- hw/display/cirrus_vga.c | 2 +- hw/display/cirrus_vga_isa.c | 2 +- hw/display/dm163.c | 2 +- hw/display/dpcd.c | 2 +- hw/display/exynos4210_fimd.c | 2 +- hw/display/g364fb.c | 2 +- hw/display/i2c-ddc.c | 2 +- hw/display/jazz_led.c | 2 +- hw/display/macfb.c | 4 +- hw/display/next-fb.c | 2 +- hw/display/pl110.c | 2 +- hw/display/qxl.c | 6 +- hw/display/ramfb-standalone.c | 2 +- hw/display/sii9022.c | 2 +- hw/display/sm501.c | 4 +- hw/display/ssd0303.c | 2 +- hw/display/ssd0323.c | 2 +- hw/display/tcx.c | 2 +- hw/display/vga-isa.c | 2 +- hw/display/vga-mmio.c | 2 +- hw/display/vga-pci.c | 6 +- hw/display/vhost-user-gpu.c | 2 +- hw/display/virtio-gpu-base.c | 2 +- hw/display/virtio-gpu-gl.c | 2 +- hw/display/virtio-gpu-pci.c | 2 +- hw/display/virtio-gpu-rutabaga.c | 2 +- hw/display/virtio-gpu.c | 2 +- hw/display/virtio-vga.c | 2 +- hw/display/vmware_vga.c | 2 +- hw/display/xlnx_dp.c | 2 +- hw/dma/bcm2835_dma.c | 2 +- hw/dma/i82374.c | 2 +- hw/dma/i8257.c | 2 +- hw/dma/pl080.c | 2 +- hw/dma/pl330.c | 2 +- hw/dma/rc4030.c | 4 +- hw/dma/sifive_pdma.c | 2 +- hw/dma/sparc32_dma.c | 10 ++- hw/dma/xilinx_axidma.c | 5 +- hw/dma/xlnx-zdma.c | 2 +- hw/dma/xlnx-zynq-devcfg.c | 2 +- hw/dma/xlnx_csu_dma.c | 2 +- hw/dma/xlnx_dpdma.c | 2 +- hw/fsi/aspeed_apb2opb.c | 2 +- hw/fsi/cfam.c | 2 +- hw/fsi/fsi-master.c | 2 +- hw/fsi/fsi.c | 2 +- hw/fsi/lbus.c | 2 +- hw/gpio/aspeed_gpio.c | 16 ++-- hw/gpio/bcm2835_gpio.c | 2 +- hw/gpio/bcm2838_gpio.c | 2 +- hw/gpio/gpio_key.c | 2 +- hw/gpio/imx_gpio.c | 2 +- hw/gpio/mpc8xxx.c | 2 +- hw/gpio/npcm7xx_gpio.c | 2 +- hw/gpio/nrf51_gpio.c | 2 +- hw/gpio/omap_gpio.c | 2 +- hw/gpio/pca9552.c | 4 +- hw/gpio/pca9554.c | 2 +- hw/gpio/pcf8574.c | 2 +- hw/gpio/pl061.c | 2 +- hw/gpio/sifive_gpio.c | 2 +- hw/gpio/stm32l4x5_gpio.c | 2 +- hw/gpio/zaurus.c | 2 +- hw/hppa/machine.c | 4 +- hw/hyperv/hv-balloon.c | 2 +- hw/hyperv/hyperv.c | 2 +- hw/hyperv/hyperv_testdev.c | 2 +- hw/hyperv/syndbg.c | 2 +- hw/hyperv/vmbus.c | 6 +- hw/i2c/allwinner-i2c.c | 2 +- hw/i2c/aspeed_i2c.c | 17 ++-- hw/i2c/bcm2835_i2c.c | 2 +- hw/i2c/bitbang_i2c.c | 2 +- hw/i2c/core.c | 2 +- hw/i2c/exynos4210_i2c.c | 2 +- hw/i2c/i2c_mux_pca954x.c | 6 +- hw/i2c/imx_i2c.c | 2 +- hw/i2c/microbit_i2c.c | 2 +- hw/i2c/mpc_i2c.c | 2 +- hw/i2c/npcm7xx_smbus.c | 2 +- hw/i2c/omap_i2c.c | 2 +- hw/i2c/pmbus_device.c | 2 +- hw/i2c/ppc4xx_i2c.c | 2 +- hw/i2c/smbus_eeprom.c | 2 +- hw/i2c/smbus_ich9.c | 2 +- hw/i2c/smbus_slave.c | 2 +- hw/i386/amd_iommu.c | 7 +- hw/i386/intel_iommu.c | 4 +- hw/i386/kvm/apic.c | 2 +- hw/i386/kvm/clock.c | 2 +- hw/i386/kvm/i8254.c | 2 +- hw/i386/kvm/i8259.c | 2 +- hw/i386/kvm/ioapic.c | 2 +- hw/i386/kvm/xen_evtchn.c | 2 +- hw/i386/kvm/xen_gnttab.c | 2 +- hw/i386/kvm/xen_overlay.c | 2 +- hw/i386/kvm/xen_primary_console.c | 2 +- hw/i386/kvm/xen_xenstore.c | 2 +- hw/i386/microvm.c | 2 +- hw/i386/nitro_enclave.c | 2 +- hw/i386/pc.c | 2 +- hw/i386/port92.c | 2 +- hw/i386/sgx-epc.c | 2 +- hw/i386/vapic.c | 2 +- hw/i386/vmmouse.c | 2 +- hw/i386/vmport.c | 2 +- hw/i386/x86-iommu.c | 2 +- hw/i386/x86.c | 2 +- hw/i386/xen/xen-pvh.c | 2 +- hw/i386/xen/xen_apic.c | 2 +- hw/i386/xen/xen_platform.c | 2 +- hw/i386/xen/xen_pvdevice.c | 2 +- hw/ide/ahci-allwinner.c | 2 +- hw/ide/ahci-sysbus.c | 2 +- hw/ide/cf.c | 2 +- hw/ide/cmd646.c | 2 +- hw/ide/ich.c | 2 +- hw/ide/ide-bus.c | 2 +- hw/ide/ide-dev.c | 6 +- hw/ide/isa.c | 2 +- hw/ide/macio.c | 2 +- hw/ide/mmio.c | 2 +- hw/ide/piix.c | 4 +- hw/ide/sii3112.c | 2 +- hw/ide/via.c | 2 +- hw/input/adb-kbd.c | 2 +- hw/input/adb-mouse.c | 2 +- hw/input/adb.c | 4 +- hw/input/lasips2.c | 8 +- hw/input/pckbd.c | 4 +- hw/input/pl050.c | 6 +- hw/input/ps2.c | 6 +- hw/input/stellaris_gamepad.c | 2 +- hw/input/virtio-input-hid.c | 6 +- hw/input/virtio-input-host.c | 2 +- hw/input/virtio-input.c | 2 +- hw/intc/allwinner-a10-pic.c | 2 +- hw/intc/apic.c | 2 +- hw/intc/apic_common.c | 2 +- hw/intc/arm_gic.c | 2 +- hw/intc/arm_gic_common.c | 2 +- hw/intc/arm_gic_kvm.c | 2 +- hw/intc/arm_gicv2m.c | 2 +- hw/intc/arm_gicv3.c | 2 +- hw/intc/arm_gicv3_common.c | 2 +- hw/intc/arm_gicv3_its.c | 2 +- hw/intc/arm_gicv3_its_common.c | 2 +- hw/intc/arm_gicv3_its_kvm.c | 2 +- hw/intc/arm_gicv3_kvm.c | 2 +- hw/intc/armv7m_nvic.c | 2 +- hw/intc/aspeed_intc.c | 6 +- hw/intc/aspeed_vic.c | 2 +- hw/intc/bcm2835_ic.c | 2 +- hw/intc/bcm2836_control.c | 2 +- hw/intc/exynos4210_combiner.c | 2 +- hw/intc/exynos4210_gic.c | 2 +- hw/intc/goldfish_pic.c | 2 +- hw/intc/grlib_irqmp.c | 2 +- hw/intc/heathrow_pic.c | 2 +- hw/intc/i8259.c | 2 +- hw/intc/i8259_common.c | 2 +- hw/intc/imx_avic.c | 2 +- hw/intc/imx_gpcv2.c | 2 +- hw/intc/ioapic.c | 2 +- hw/intc/ioapic_common.c | 2 +- hw/intc/loongarch_extioi.c | 2 +- hw/intc/loongarch_extioi_common.c | 3 +- hw/intc/loongarch_ipi.c | 2 +- hw/intc/loongarch_pch_msi.c | 2 +- hw/intc/loongarch_pch_pic.c | 2 +- hw/intc/loongarch_pic_common.c | 3 +- hw/intc/loongson_ipi.c | 2 +- hw/intc/loongson_ipi_common.c | 2 +- hw/intc/m68k_irqc.c | 2 +- hw/intc/mips_gic.c | 2 +- hw/intc/omap_intc.c | 2 +- hw/intc/ompic.c | 2 +- hw/intc/openpic.c | 2 +- hw/intc/openpic_kvm.c | 2 +- hw/intc/pl190.c | 2 +- hw/intc/pnv_xive.c | 2 +- hw/intc/pnv_xive2.c | 2 +- hw/intc/ppc-uic.c | 2 +- hw/intc/realview_gic.c | 2 +- hw/intc/riscv_aclint.c | 4 +- hw/intc/riscv_aplic.c | 2 +- hw/intc/riscv_imsic.c | 2 +- hw/intc/rx_icu.c | 2 +- hw/intc/s390_flic.c | 4 +- hw/intc/s390_flic_kvm.c | 2 +- hw/intc/sifive_plic.c | 2 +- hw/intc/slavio_intctl.c | 2 +- hw/intc/spapr_xive.c | 2 +- hw/intc/xics.c | 4 +- hw/intc/xics_pnv.c | 2 +- hw/intc/xics_spapr.c | 2 +- hw/intc/xilinx_intc.c | 2 +- hw/intc/xive.c | 8 +- hw/intc/xive2.c | 4 +- hw/intc/xlnx-pmu-iomod-intc.c | 2 +- hw/intc/xlnx-zynqmp-ipi.c | 2 +- hw/ipack/ipack.c | 2 +- hw/ipack/tpci200.c | 2 +- hw/ipmi/ipmi.c | 4 +- hw/ipmi/ipmi_bmc_extern.c | 2 +- hw/ipmi/ipmi_bmc_sim.c | 2 +- hw/ipmi/isa_ipmi_bt.c | 2 +- hw/ipmi/isa_ipmi_kcs.c | 2 +- hw/ipmi/pci_ipmi_bt.c | 2 +- hw/ipmi/pci_ipmi_kcs.c | 2 +- hw/ipmi/smbus_ipmi.c | 2 +- hw/isa/fdc37m81x-superio.c | 2 +- hw/isa/i82378.c | 2 +- hw/isa/isa-bus.c | 6 +- hw/isa/isa-superio.c | 2 +- hw/isa/lpc_ich9.c | 2 +- hw/isa/pc87312.c | 2 +- hw/isa/piix.c | 6 +- hw/isa/smc37c669-superio.c | 2 +- hw/isa/vt82c686.c | 12 +-- hw/loongarch/virt.c | 2 +- hw/m68k/mcf5206.c | 2 +- hw/m68k/mcf_intc.c | 2 +- hw/m68k/next-cube.c | 8 +- hw/m68k/next-kbd.c | 2 +- hw/m68k/q800-glue.c | 2 +- hw/m68k/q800.c | 2 +- hw/m68k/virt.c | 4 +- hw/mem/cxl_type3.c | 2 +- hw/mem/npcm7xx_mc.c | 2 +- hw/mem/nvdimm.c | 2 +- hw/mem/pc-dimm.c | 2 +- hw/mem/sparse-mem.c | 2 +- hw/microblaze/petalogix_s3adsp1800_mmu.c | 3 +- hw/microblaze/xlnx-zynqmp-pmu.c | 2 +- hw/mips/cps.c | 2 +- hw/mips/jazz.c | 4 +- hw/mips/loongson3_virt.c | 2 +- hw/misc/a9scu.c | 2 +- hw/misc/allwinner-a10-ccm.c | 2 +- hw/misc/allwinner-a10-dramc.c | 2 +- hw/misc/allwinner-cpucfg.c | 2 +- hw/misc/allwinner-h3-ccu.c | 2 +- hw/misc/allwinner-h3-dramc.c | 2 +- hw/misc/allwinner-h3-sysctrl.c | 3 +- hw/misc/allwinner-r40-ccu.c | 2 +- hw/misc/allwinner-r40-dramc.c | 2 +- hw/misc/allwinner-sid.c | 2 +- hw/misc/allwinner-sramc.c | 4 +- hw/misc/applesmc.c | 2 +- hw/misc/arm11scu.c | 2 +- hw/misc/arm_l2x0.c | 2 +- hw/misc/arm_sysctl.c | 2 +- hw/misc/armsse-cpu-pwrctrl.c | 2 +- hw/misc/armsse-cpuid.c | 2 +- hw/misc/armsse-mhu.c | 2 +- hw/misc/armv7m_ras.c | 2 +- hw/misc/aspeed_hace.c | 12 +-- hw/misc/aspeed_i3c.c | 4 +- hw/misc/aspeed_lpc.c | 2 +- hw/misc/aspeed_peci.c | 2 +- hw/misc/aspeed_sbc.c | 4 +- hw/misc/aspeed_scu.c | 14 +-- hw/misc/aspeed_sdmc.c | 10 +-- hw/misc/aspeed_sli.c | 6 +- hw/misc/aspeed_xdma.c | 8 +- hw/misc/auxbus.c | 6 +- hw/misc/avr_power.c | 2 +- hw/misc/axp2xx.c | 6 +- hw/misc/bcm2835_cprman.c | 10 +-- hw/misc/bcm2835_mbox.c | 2 +- hw/misc/bcm2835_mphi.c | 2 +- hw/misc/bcm2835_powermgt.c | 2 +- hw/misc/bcm2835_property.c | 2 +- hw/misc/bcm2835_rng.c | 2 +- hw/misc/bcm2835_thermal.c | 2 +- hw/misc/debugexit.c | 2 +- hw/misc/djmemc.c | 2 +- hw/misc/eccmemctl.c | 2 +- hw/misc/edu.c | 2 +- hw/misc/empty_slot.c | 2 +- hw/misc/exynos4210_clk.c | 2 +- hw/misc/exynos4210_pmu.c | 2 +- hw/misc/exynos4210_rng.c | 2 +- hw/misc/grlib_ahb_apb_pnp.c | 4 +- hw/misc/i2c-echo.c | 2 +- hw/misc/imx25_ccm.c | 2 +- hw/misc/imx31_ccm.c | 2 +- hw/misc/imx6_ccm.c | 2 +- hw/misc/imx6_src.c | 2 +- hw/misc/imx6ul_ccm.c | 2 +- hw/misc/imx7_ccm.c | 4 +- hw/misc/imx7_gpr.c | 2 +- hw/misc/imx7_snvs.c | 2 +- hw/misc/imx7_src.c | 2 +- hw/misc/imx8mp_analog.c | 2 +- hw/misc/imx8mp_ccm.c | 2 +- hw/misc/imx_rngc.c | 2 +- hw/misc/iosb.c | 2 +- hw/misc/iotkit-secctl.c | 2 +- hw/misc/iotkit-sysctl.c | 2 +- hw/misc/iotkit-sysinfo.c | 2 +- hw/misc/ivshmem-flat.c | 2 +- hw/misc/ivshmem-pci.c | 6 +- hw/misc/lasi.c | 2 +- hw/misc/led.c | 2 +- hw/misc/mac_via.c | 4 +- hw/misc/macio/cuda.c | 4 +- hw/misc/macio/gpio.c | 2 +- hw/misc/macio/mac_dbdma.c | 2 +- hw/misc/macio/macio.c | 6 +- hw/misc/macio/pmu.c | 4 +- hw/misc/mchp_pfsoc_dmc.c | 5 +- hw/misc/mchp_pfsoc_ioscb.c | 2 +- hw/misc/mchp_pfsoc_sysreg.c | 2 +- hw/misc/mips_cmgcr.c | 2 +- hw/misc/mips_cpc.c | 2 +- hw/misc/mips_itu.c | 2 +- hw/misc/mos6522.c | 2 +- hw/misc/mps2-fpgaio.c | 2 +- hw/misc/mps2-scc.c | 2 +- hw/misc/msf2-sysreg.c | 2 +- hw/misc/npcm7xx_mft.c | 2 +- hw/misc/npcm7xx_pwm.c | 2 +- hw/misc/npcm7xx_rng.c | 2 +- hw/misc/npcm_clk.c | 12 +-- hw/misc/npcm_gcr.c | 6 +- hw/misc/nrf51_rng.c | 2 +- hw/misc/pc-testdev.c | 2 +- hw/misc/pci-testdev.c | 2 +- hw/misc/pvpanic-isa.c | 2 +- hw/misc/pvpanic-mmio.c | 2 +- hw/misc/pvpanic-pci.c | 2 +- hw/misc/sbsa_ec.c | 2 +- hw/misc/sifive_e_aon.c | 2 +- hw/misc/sifive_u_otp.c | 2 +- hw/misc/sifive_u_prci.c | 2 +- hw/misc/slavio_misc.c | 2 +- hw/misc/stm32_rcc.c | 2 +- hw/misc/stm32f2xx_syscfg.c | 2 +- hw/misc/stm32f4xx_exti.c | 2 +- hw/misc/stm32f4xx_syscfg.c | 2 +- hw/misc/stm32l4x5_exti.c | 2 +- hw/misc/stm32l4x5_rcc.c | 6 +- hw/misc/stm32l4x5_syscfg.c | 2 +- hw/misc/tz-mpc.c | 4 +- hw/misc/tz-msc.c | 2 +- hw/misc/tz-ppc.c | 2 +- hw/misc/unimp.c | 2 +- hw/misc/virt_ctrl.c | 2 +- hw/misc/vmcoreinfo.c | 2 +- hw/misc/xlnx-versal-cframe-reg.c | 4 +- hw/misc/xlnx-versal-cfu.c | 6 +- hw/misc/xlnx-versal-crl.c | 2 +- hw/misc/xlnx-versal-pmc-iou-slcr.c | 3 +- hw/misc/xlnx-versal-trng.c | 2 +- hw/misc/xlnx-versal-xramc.c | 2 +- hw/misc/xlnx-zynqmp-apu-ctrl.c | 2 +- hw/misc/xlnx-zynqmp-crf.c | 2 +- hw/misc/zynq_slcr.c | 2 +- hw/net/allwinner-sun8i-emac.c | 3 +- hw/net/allwinner_emac.c | 2 +- hw/net/cadence_gem.c | 2 +- hw/net/can/can_kvaser_pci.c | 2 +- hw/net/can/can_mioe3680_pci.c | 2 +- hw/net/can/can_pcm3680_pci.c | 2 +- hw/net/can/ctucan_pci.c | 2 +- hw/net/can/xlnx-versal-canfd.c | 2 +- hw/net/can/xlnx-zynqmp-can.c | 2 +- hw/net/dp8393x.c | 2 +- hw/net/e1000.c | 2 +- hw/net/e1000e.c | 2 +- hw/net/eepro100.c | 2 +- hw/net/fsl_etsec/etsec.c | 2 +- hw/net/ftgmac100.c | 4 +- hw/net/igb.c | 2 +- hw/net/igbvf.c | 2 +- hw/net/imx_fec.c | 2 +- hw/net/lan9118.c | 2 +- hw/net/lan9118_phy.c | 2 +- hw/net/lance.c | 2 +- hw/net/lasi_i82596.c | 2 +- hw/net/mcf_fec.c | 2 +- hw/net/mipsnet.c | 2 +- hw/net/msf2-emac.c | 2 +- hw/net/mv88w8618_eth.c | 2 +- hw/net/ne2000-isa.c | 2 +- hw/net/ne2000-pci.c | 2 +- hw/net/npcm7xx_emc.c | 2 +- hw/net/npcm_gmac.c | 2 +- hw/net/npcm_pcs.c | 2 +- hw/net/opencores_eth.c | 2 +- hw/net/pcnet-pci.c | 2 +- hw/net/rocker/rocker.c | 2 +- hw/net/rtl8139.c | 2 +- hw/net/smc91c111.c | 2 +- hw/net/spapr_llan.c | 2 +- hw/net/stellaris_enet.c | 2 +- hw/net/sungem.c | 2 +- hw/net/sunhme.c | 2 +- hw/net/tulip.c | 2 +- hw/net/virtio-net.c | 2 +- hw/net/vmxnet3.c | 2 +- hw/net/xen_nic.c | 2 +- hw/net/xgmac.c | 2 +- hw/net/xilinx_axienet.c | 7 +- hw/net/xilinx_ethlite.c | 2 +- hw/nubus/mac-nubus-bridge.c | 2 +- hw/nubus/nubus-bridge.c | 2 +- hw/nubus/nubus-bus.c | 2 +- hw/nubus/nubus-device.c | 2 +- hw/nubus/nubus-virtio-mmio.c | 2 +- hw/nvme/ctrl.c | 2 +- hw/nvme/ns.c | 2 +- hw/nvme/subsys.c | 2 +- hw/nvram/bcm2835_otp.c | 2 +- hw/nvram/ds1225y.c | 2 +- hw/nvram/eeprom_at24c.c | 2 +- hw/nvram/fw_cfg.c | 6 +- hw/nvram/mac_nvram.c | 2 +- hw/nvram/npcm7xx_otp.c | 6 +- hw/nvram/nrf51_nvm.c | 2 +- hw/nvram/spapr_nvram.c | 2 +- hw/nvram/xlnx-bbram.c | 2 +- hw/nvram/xlnx-efuse.c | 2 +- hw/nvram/xlnx-versal-efuse-cache.c | 2 +- hw/nvram/xlnx-versal-efuse-ctrl.c | 2 +- hw/nvram/xlnx-zynqmp-efuse.c | 2 +- hw/openrisc/openrisc_sim.c | 2 +- hw/openrisc/virt.c | 2 +- hw/pci-bridge/cxl_downstream.c | 2 +- hw/pci-bridge/cxl_root_port.c | 2 +- hw/pci-bridge/cxl_upstream.c | 2 +- hw/pci-bridge/gen_pcie_root_port.c | 2 +- hw/pci-bridge/i82801b11.c | 2 +- hw/pci-bridge/ioh3420.c | 2 +- hw/pci-bridge/pci_bridge_dev.c | 4 +- hw/pci-bridge/pci_expander_bridge.c | 12 +-- hw/pci-bridge/pcie_pci_bridge.c | 2 +- hw/pci-bridge/pcie_root_port.c | 2 +- hw/pci-bridge/simba.c | 2 +- hw/pci-bridge/xio3130_downstream.c | 2 +- hw/pci-bridge/xio3130_upstream.c | 2 +- hw/pci-host/articia.c | 6 +- hw/pci-host/astro.c | 6 +- hw/pci-host/bonito.c | 4 +- hw/pci-host/designware.c | 9 +- hw/pci-host/dino.c | 2 +- hw/pci-host/fsl_imx8m_phy.c | 2 +- hw/pci-host/gpex.c | 4 +- hw/pci-host/grackle.c | 4 +- hw/pci-host/gt64120.c | 4 +- hw/pci-host/i440fx.c | 4 +- hw/pci-host/mv64361.c | 6 +- hw/pci-host/pnv_phb.c | 4 +- hw/pci-host/pnv_phb3.c | 6 +- hw/pci-host/pnv_phb3_msi.c | 2 +- hw/pci-host/pnv_phb3_pbcq.c | 2 +- hw/pci-host/pnv_phb4.c | 6 +- hw/pci-host/pnv_phb4_pec.c | 4 +- hw/pci-host/ppc440_pcix.c | 2 +- hw/pci-host/ppc4xx_pci.c | 4 +- hw/pci-host/ppce500.c | 4 +- hw/pci-host/q35.c | 4 +- hw/pci-host/raven.c | 4 +- hw/pci-host/remote.c | 2 +- hw/pci-host/sabre.c | 4 +- hw/pci-host/sh_pci.c | 4 +- hw/pci-host/uninorth.c | 19 ++-- hw/pci-host/versatile.c | 4 +- hw/pci-host/xen_igd_pt.c | 3 +- hw/pci-host/xilinx-pcie.c | 4 +- hw/pci/pci.c | 6 +- hw/pci/pci_bridge.c | 2 +- hw/pci/pci_host.c | 2 +- hw/pci/pcie_port.c | 4 +- hw/ppc/amigaone.c | 2 +- hw/ppc/e500plat.c | 2 +- hw/ppc/mac_newworld.c | 2 +- hw/ppc/mac_oldworld.c | 2 +- hw/ppc/mpc8544ds.c | 2 +- hw/ppc/pef.c | 2 +- hw/ppc/pegasos2.c | 2 +- hw/ppc/pnv.c | 25 +++--- hw/ppc/pnv_adu.c | 2 +- hw/ppc/pnv_chiptod.c | 6 +- hw/ppc/pnv_core.c | 14 +-- hw/ppc/pnv_homer.c | 8 +- hw/ppc/pnv_i2c.c | 2 +- hw/ppc/pnv_lpc.c | 8 +- hw/ppc/pnv_n1_chiplet.c | 2 +- hw/ppc/pnv_nest_pervasive.c | 2 +- hw/ppc/pnv_occ.c | 8 +- hw/ppc/pnv_pnor.c | 2 +- hw/ppc/pnv_psi.c | 8 +- hw/ppc/pnv_sbe.c | 6 +- hw/ppc/ppc440_uc.c | 2 +- hw/ppc/ppc4xx_devs.c | 8 +- hw/ppc/ppc4xx_sdram.c | 4 +- hw/ppc/ppce500_spin.c | 2 +- hw/ppc/prep_systemio.c | 2 +- hw/ppc/rs6000_mc.c | 2 +- hw/ppc/spapr.c | 4 +- hw/ppc/spapr_cpu_core.c | 2 +- hw/ppc/spapr_drc.c | 16 ++-- hw/ppc/spapr_iommu.c | 5 +- hw/ppc/spapr_nvdimm.c | 2 +- hw/ppc/spapr_pci.c | 2 +- hw/ppc/spapr_rng.c | 2 +- hw/ppc/spapr_rtc.c | 2 +- hw/ppc/spapr_tpm_proxy.c | 2 +- hw/ppc/spapr_vio.c | 6 +- hw/remote/machine.c | 2 +- hw/remote/proxy.c | 2 +- hw/remote/remote-obj.c | 2 +- hw/remote/vfio-user-obj.c | 2 +- hw/riscv/microchip_pfsoc.c | 5 +- hw/riscv/opentitan.c | 4 +- hw/riscv/riscv-iommu-pci.c | 2 +- hw/riscv/riscv-iommu-sys.c | 2 +- hw/riscv/riscv-iommu.c | 4 +- hw/riscv/riscv_hart.c | 2 +- hw/riscv/shakti_c.c | 4 +- hw/riscv/sifive_e.c | 4 +- hw/riscv/sifive_u.c | 4 +- hw/riscv/spike.c | 2 +- hw/riscv/virt.c | 2 +- hw/rtc/allwinner-rtc.c | 8 +- hw/rtc/aspeed_rtc.c | 2 +- hw/rtc/ds1338.c | 2 +- hw/rtc/exynos4210_rtc.c | 2 +- hw/rtc/goldfish_rtc.c | 2 +- hw/rtc/ls7a_rtc.c | 2 +- hw/rtc/m41t80.c | 2 +- hw/rtc/m48t59-isa.c | 4 +- hw/rtc/m48t59.c | 5 +- hw/rtc/mc146818rtc.c | 2 +- hw/rtc/pl031.c | 2 +- hw/rtc/rs5c372.c | 2 +- hw/rtc/sun4v-rtc.c | 2 +- hw/rtc/xlnx-zynqmp-rtc.c | 2 +- hw/rx/rx-gdbsim.c | 6 +- hw/rx/rx62n.c | 6 +- hw/s390x/3270-ccw.c | 2 +- hw/s390x/ap-bridge.c | 4 +- hw/s390x/ap-device.c | 2 +- hw/s390x/ccw-device.c | 2 +- hw/s390x/css-bridge.c | 4 +- hw/s390x/event-facility.c | 4 +- hw/s390x/ipl.c | 2 +- hw/s390x/s390-ccw.c | 2 +- hw/s390x/s390-pci-bus.c | 7 +- hw/s390x/s390-skeys-kvm.c | 2 +- hw/s390x/s390-skeys.c | 4 +- hw/s390x/s390-stattrib-kvm.c | 2 +- hw/s390x/s390-stattrib.c | 4 +- hw/s390x/s390-virtio-ccw.c | 4 +- hw/s390x/sclp.c | 2 +- hw/s390x/sclpcpu.c | 2 +- hw/s390x/sclpquiesce.c | 2 +- hw/s390x/tod-kvm.c | 2 +- hw/s390x/tod-tcg.c | 2 +- hw/s390x/tod.c | 2 +- hw/s390x/vhost-scsi-ccw.c | 2 +- hw/s390x/vhost-user-fs-ccw.c | 2 +- hw/s390x/vhost-vsock-ccw.c | 2 +- hw/s390x/virtio-ccw-9p.c | 2 +- hw/s390x/virtio-ccw-balloon.c | 2 +- hw/s390x/virtio-ccw-blk.c | 2 +- hw/s390x/virtio-ccw-crypto.c | 2 +- hw/s390x/virtio-ccw-gpu.c | 2 +- hw/s390x/virtio-ccw-input.c | 2 +- hw/s390x/virtio-ccw-mem.c | 2 +- hw/s390x/virtio-ccw-net.c | 2 +- hw/s390x/virtio-ccw-rng.c | 2 +- hw/s390x/virtio-ccw-scsi.c | 2 +- hw/s390x/virtio-ccw-serial.c | 2 +- hw/s390x/virtio-ccw.c | 4 +- hw/scsi/esp-pci.c | 4 +- hw/scsi/esp.c | 4 +- hw/scsi/lsi53c895a.c | 4 +- hw/scsi/megasas.c | 2 +- hw/scsi/mptsas.c | 2 +- hw/scsi/scsi-bus.c | 4 +- hw/scsi/scsi-disk.c | 8 +- hw/scsi/scsi-generic.c | 2 +- hw/scsi/spapr_vscsi.c | 2 +- hw/scsi/vhost-scsi.c | 2 +- hw/scsi/vhost-user-scsi.c | 2 +- hw/scsi/virtio-scsi.c | 4 +- hw/scsi/vmw_pvscsi.c | 2 +- hw/sd/allwinner-sdhost.c | 15 ++-- hw/sd/aspeed_sdhci.c | 10 +-- hw/sd/bcm2835_sdhost.c | 2 +- hw/sd/cadence_sdhci.c | 2 +- hw/sd/npcm7xx_sdhci.c | 2 +- hw/sd/omap_mmc.c | 2 +- hw/sd/pl181.c | 4 +- hw/sd/sd.c | 8 +- hw/sd/sdhci-pci.c | 2 +- hw/sd/sdhci.c | 4 +- hw/sd/ssi-sd.c | 2 +- hw/sensor/adm1266.c | 2 +- hw/sensor/adm1272.c | 2 +- hw/sensor/dps310.c | 2 +- hw/sensor/emc141x.c | 4 +- hw/sensor/isl_pmbus_vr.c | 8 +- hw/sensor/lsm303dlhc_mag.c | 2 +- hw/sensor/max31785.c | 2 +- hw/sensor/max34451.c | 2 +- hw/sensor/tmp105.c | 2 +- hw/sensor/tmp421.c | 2 +- hw/sparc/sun4m.c | 28 +++--- hw/sparc/sun4m_iommu.c | 5 +- hw/sparc64/niagara.c | 2 +- hw/sparc64/sun4u.c | 12 +-- hw/sparc64/sun4u_iommu.c | 5 +- hw/ssi/allwinner-a10-spi.c | 2 +- hw/ssi/aspeed_smc.c | 36 ++++---- hw/ssi/bcm2835_spi.c | 2 +- hw/ssi/ibex_spi_host.c | 2 +- hw/ssi/imx_spi.c | 2 +- hw/ssi/mss-spi.c | 2 +- hw/ssi/npcm7xx_fiu.c | 2 +- hw/ssi/npcm_pspi.c | 2 +- hw/ssi/pl022.c | 2 +- hw/ssi/pnv_spi.c | 2 +- hw/ssi/sifive_spi.c | 2 +- hw/ssi/ssi.c | 4 +- hw/ssi/stm32f2xx_spi.c | 2 +- hw/ssi/xilinx_spi.c | 2 +- hw/ssi/xilinx_spips.c | 6 +- hw/ssi/xlnx-versal-ospi.c | 2 +- hw/timer/a9gtimer.c | 2 +- hw/timer/allwinner-a10-pit.c | 2 +- hw/timer/arm_mptimer.c | 2 +- hw/timer/arm_timer.c | 2 +- hw/timer/armv7m_systick.c | 2 +- hw/timer/aspeed_timer.c | 12 +-- hw/timer/avr_timer16.c | 2 +- hw/timer/bcm2835_systmr.c | 2 +- hw/timer/cadence_ttc.c | 2 +- hw/timer/cmsdk-apb-dualtimer.c | 2 +- hw/timer/cmsdk-apb-timer.c | 2 +- hw/timer/digic-timer.c | 2 +- hw/timer/exynos4210_mct.c | 2 +- hw/timer/exynos4210_pwm.c | 2 +- hw/timer/grlib_gptimer.c | 2 +- hw/timer/hpet.c | 2 +- hw/timer/i8254.c | 2 +- hw/timer/i8254_common.c | 2 +- hw/timer/ibex_timer.c | 2 +- hw/timer/imx_epit.c | 2 +- hw/timer/imx_gpt.c | 2 +- hw/timer/mss-timer.c | 2 +- hw/timer/npcm7xx_timer.c | 2 +- hw/timer/nrf51_timer.c | 2 +- hw/timer/pxa2xx_timer.c | 4 +- hw/timer/renesas_cmt.c | 2 +- hw/timer/renesas_tmr.c | 2 +- hw/timer/sifive_pwm.c | 2 +- hw/timer/slavio_timer.c | 2 +- hw/timer/sse-counter.c | 2 +- hw/timer/sse-timer.c | 2 +- hw/timer/stellaris-gptm.c | 2 +- hw/timer/stm32f2xx_timer.c | 2 +- hw/timer/xilinx_timer.c | 2 +- hw/tpm/tpm_crb.c | 2 +- hw/tpm/tpm_spapr.c | 2 +- hw/tpm/tpm_tis_i2c.c | 2 +- hw/tpm/tpm_tis_isa.c | 2 +- hw/tpm/tpm_tis_sysbus.c | 2 +- hw/tricore/tc27x_soc.c | 4 +- hw/tricore/triboard.c | 2 +- hw/tricore/tricore_testdevice.c | 2 +- hw/uefi/var-service-sysbus.c | 4 +- hw/ufs/lu.c | 2 +- hw/ufs/ufs.c | 4 +- hw/usb/bus.c | 4 +- hw/usb/canokey.c | 2 +- hw/usb/ccid-card-emulated.c | 2 +- hw/usb/ccid-card-passthru.c | 2 +- hw/usb/chipidea.c | 2 +- hw/usb/dev-audio.c | 2 +- hw/usb/dev-hid.c | 8 +- hw/usb/dev-hub.c | 2 +- hw/usb/dev-mtp.c | 2 +- hw/usb/dev-network.c | 2 +- hw/usb/dev-serial.c | 6 +- hw/usb/dev-smartcard-reader.c | 4 +- hw/usb/dev-storage-bot.c | 2 +- hw/usb/dev-storage-classic.c | 2 +- hw/usb/dev-storage.c | 2 +- hw/usb/dev-uas.c | 2 +- hw/usb/dev-wacom.c | 2 +- hw/usb/hcd-dwc2.c | 2 +- hw/usb/hcd-dwc3.c | 2 +- hw/usb/hcd-ehci-pci.c | 4 +- hw/usb/hcd-ehci-sysbus.c | 16 ++-- hw/usb/hcd-ohci-pci.c | 2 +- hw/usb/hcd-ohci-sysbus.c | 2 +- hw/usb/hcd-uhci.c | 4 +- hw/usb/hcd-uhci.h | 2 +- hw/usb/hcd-xhci-nec.c | 2 +- hw/usb/hcd-xhci-pci.c | 4 +- hw/usb/hcd-xhci-sysbus.c | 2 +- hw/usb/hcd-xhci.c | 2 +- hw/usb/host-libusb.c | 2 +- hw/usb/imx-usb-phy.c | 2 +- hw/usb/redirect.c | 2 +- hw/usb/u2f-emulated.c | 2 +- hw/usb/u2f-passthru.c | 2 +- hw/usb/u2f.c | 2 +- hw/usb/xlnx-usb-subsystem.c | 2 +- hw/usb/xlnx-versal-usb2-ctrl-regs.c | 2 +- hw/vfio/amd-xgbe.c | 2 +- hw/vfio/ap.c | 2 +- hw/vfio/calxeda-xgmac.c | 2 +- hw/vfio/ccw.c | 2 +- hw/vfio/container.c | 4 +- hw/vfio/igd.c | 3 +- hw/vfio/iommufd.c | 4 +- hw/vfio/pci.c | 5 +- hw/vfio/platform.c | 2 +- hw/vfio/spapr.c | 2 +- hw/virtio/vdpa-dev-pci.c | 3 +- hw/virtio/vdpa-dev.c | 2 +- hw/virtio/vhost-scsi-pci.c | 2 +- hw/virtio/vhost-user-base.c | 2 +- hw/virtio/vhost-user-blk-pci.c | 2 +- hw/virtio/vhost-user-device-pci.c | 3 +- hw/virtio/vhost-user-device.c | 2 +- hw/virtio/vhost-user-fs-pci.c | 2 +- hw/virtio/vhost-user-fs.c | 2 +- hw/virtio/vhost-user-gpio-pci.c | 2 +- hw/virtio/vhost-user-gpio.c | 2 +- hw/virtio/vhost-user-i2c-pci.c | 2 +- hw/virtio/vhost-user-i2c.c | 2 +- hw/virtio/vhost-user-input.c | 2 +- hw/virtio/vhost-user-rng-pci.c | 2 +- hw/virtio/vhost-user-rng.c | 2 +- hw/virtio/vhost-user-scmi-pci.c | 2 +- hw/virtio/vhost-user-scmi.c | 2 +- hw/virtio/vhost-user-scsi-pci.c | 2 +- hw/virtio/vhost-user-snd-pci.c | 2 +- hw/virtio/vhost-user-snd.c | 2 +- hw/virtio/vhost-user-vsock-pci.c | 3 +- hw/virtio/vhost-user-vsock.c | 2 +- hw/virtio/vhost-vsock-common.c | 2 +- hw/virtio/vhost-vsock-pci.c | 2 +- hw/virtio/vhost-vsock.c | 2 +- hw/virtio/virtio-9p-pci.c | 2 +- hw/virtio/virtio-balloon-pci.c | 2 +- hw/virtio/virtio-balloon.c | 2 +- hw/virtio/virtio-blk-pci.c | 2 +- hw/virtio/virtio-bus.c | 2 +- hw/virtio/virtio-crypto-pci.c | 2 +- hw/virtio/virtio-crypto.c | 2 +- hw/virtio/virtio-input-pci.c | 7 +- hw/virtio/virtio-iommu-pci.c | 2 +- hw/virtio/virtio-iommu.c | 4 +- hw/virtio/virtio-mem-pci.c | 2 +- hw/virtio/virtio-mem.c | 5 +- hw/virtio/virtio-mmio.c | 4 +- hw/virtio/virtio-net-pci.c | 2 +- hw/virtio/virtio-nsm-pci.c | 2 +- hw/virtio/virtio-nsm.c | 2 +- hw/virtio/virtio-pci.c | 8 +- hw/virtio/virtio-pmem-pci.c | 2 +- hw/virtio/virtio-pmem.c | 2 +- hw/virtio/virtio-rng-pci.c | 2 +- hw/virtio/virtio-rng.c | 2 +- hw/virtio/virtio-scsi-pci.c | 2 +- hw/virtio/virtio-serial-pci.c | 2 +- hw/virtio/virtio.c | 2 +- hw/vmapple/aes.c | 2 +- hw/vmapple/bdif.c | 2 +- hw/vmapple/cfg.c | 2 +- hw/vmapple/virtio-blk.c | 5 +- hw/vmapple/vmapple.c | 2 +- hw/watchdog/allwinner-wdt.c | 6 +- hw/watchdog/cmsdk-apb-watchdog.c | 2 +- hw/watchdog/sbsa_gwdt.c | 2 +- hw/watchdog/spapr_watchdog.c | 2 +- hw/watchdog/wdt_aspeed.c | 12 +-- hw/watchdog/wdt_diag288.c | 2 +- hw/watchdog/wdt_i6300esb.c | 2 +- hw/watchdog/wdt_ib700.c | 2 +- hw/watchdog/wdt_imx2.c | 2 +- hw/xen/xen-bus.c | 4 +- hw/xen/xen-legacy-backend.c | 4 +- hw/xen/xen-pvh-common.c | 2 +- hw/xen/xen_pt.c | 2 +- hw/xen/xen_pt_graphics.c | 2 +- hw/xtensa/xtfpga.c | 16 ++-- include/hw/boards.h | 2 +- include/hw/i386/pc.h | 5 +- include/hw/virtio/virtio-pci.h | 2 +- include/qom/object.h | 4 +- io/channel-buffer.c | 2 +- io/channel-command.c | 2 +- io/channel-file.c | 2 +- io/channel-null.c | 2 +- io/channel-socket.c | 2 +- io/channel-tls.c | 2 +- io/channel-websock.c | 2 +- iothread.c | 2 +- migration/channel-block.c | 2 +- migration/migration.c | 2 +- migration/rdma.c | 2 +- net/can/can_core.c | 2 +- net/can/can_host.c | 2 +- net/can/can_socketcan.c | 2 +- net/colo-compare.c | 2 +- net/dump.c | 2 +- net/filter-buffer.c | 2 +- net/filter-mirror.c | 4 +- net/filter-replay.c | 2 +- net/filter-rewriter.c | 2 +- net/filter.c | 2 +- qom/object.c | 4 +- rust/qemu-api/src/qom.rs | 2 +- .../codeconverter/qom_type_info.py | 3 +- scsi/pr-manager-helper.c | 2 +- system/qtest.c | 2 +- target/alpha/cpu.c | 2 +- target/arm/cpu.c | 4 +- target/arm/cpu.h | 2 +- target/arm/cpu64.c | 4 +- target/arm/tcg/cpu-v7m.c | 2 +- target/avr/cpu.c | 2 +- target/hexagon/cpu.c | 2 +- target/hppa/cpu.c | 2 +- target/i386/confidential-guest.c | 2 +- target/i386/cpu.c | 8 +- target/i386/host-cpu.c | 2 +- target/i386/hvf/hvf-cpu.c | 2 +- target/i386/kvm/kvm-cpu.c | 2 +- target/i386/nvmm/nvmm-accel-ops.c | 2 +- target/i386/nvmm/nvmm-all.c | 2 +- target/i386/sev.c | 6 +- target/i386/tcg/tcg-cpu.c | 2 +- target/i386/whpx/whpx-accel-ops.c | 2 +- target/i386/whpx/whpx-all.c | 2 +- target/i386/whpx/whpx-apic.c | 2 +- target/loongarch/cpu.c | 6 +- target/m68k/cpu.c | 6 +- target/microblaze/cpu.c | 2 +- target/mips/cpu.c | 4 +- target/openrisc/cpu.c | 2 +- target/ppc/cpu-models.c | 2 +- target/ppc/cpu.h | 3 +- target/ppc/cpu_init.c | 90 +++++++++---------- target/ppc/kvm.c | 4 +- target/riscv/cpu.c | 4 +- target/riscv/kvm/kvm-cpu.c | 4 +- target/riscv/tcg/tcg-cpu.c | 2 +- target/rx/cpu.c | 2 +- target/s390x/cpu.c | 2 +- target/s390x/cpu_models.c | 10 +-- target/s390x/kvm/pv.c | 2 +- target/sh4/cpu.c | 8 +- target/sparc/cpu.c | 4 +- target/tricore/cpu.c | 2 +- target/xtensa/cpu.c | 2 +- target/xtensa/helper.c | 2 +- tests/unit/check-qom-interface.c | 2 +- tests/unit/check-qom-proplist.c | 6 +- tests/unit/test-qdev-global-props.c | 8 +- tests/unit/test-smp-parse.c | 23 ++--- ui/console-vc.c | 6 +- ui/console.c | 4 +- ui/dbus-chardev.c | 2 +- ui/dbus.c | 4 +- ui/gtk.c | 2 +- ui/input-barrier.c | 2 +- ui/input-linux.c | 2 +- ui/spice-app.c | 2 +- ui/vdagent.c | 2 +- util/main-loop.c | 2 +- util/thread-context.c | 2 +- 1121 files changed, 1774 insertions(+), 1707 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 601c3bc0ac..5375de7bcf 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -355,7 +355,7 @@ static inline int hvf_gdbstub_sstep_flags(void) return SSTEP_ENABLE | SSTEP_NOIRQ; } -static void hvf_accel_class_init(ObjectClass *oc, void *data) +static void hvf_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "HVF"; @@ -578,7 +578,7 @@ static void hvf_remove_all_breakpoints(CPUState *cpu) } } -static void hvf_accel_ops_class_init(ObjectClass *oc, void *data) +static void hvf_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index 54ea60909e..e5c15449aa 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -90,7 +90,7 @@ static int kvm_update_guest_debug_ops(CPUState *cpu) } #endif -static void kvm_accel_ops_class_init(ObjectClass *oc, void *data) +static void kvm_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index cba9c78d2f..b8c68c7819 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3964,7 +3964,7 @@ static int kvm_gdbstub_sstep_flags(void) return kvm_sstep_flags; } -static void kvm_accel_class_init(ObjectClass *oc, void *data) +static void kvm_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "KVM"; diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index 7fae80f6a1..92bed9264c 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -42,7 +42,7 @@ static int qtest_init_accel(MachineState *ms) return 0; } -static void qtest_accel_class_init(ObjectClass *oc, void *data) +static void qtest_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "QTest"; @@ -59,7 +59,7 @@ static const TypeInfo qtest_accel_type = { }; module_obj(TYPE_QTEST_ACCEL); -static void qtest_accel_ops_class_init(ObjectClass *oc, void *data) +static void qtest_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index ccdb781eef..b24d6a7562 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -224,7 +224,7 @@ static void tcg_accel_ops_init(AccelOpsClass *ops) ops->remove_all_breakpoints = tcg_remove_all_breakpoints; } -static void tcg_accel_ops_class_init(ObjectClass *oc, void *data) +static void tcg_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index b0d4e3e136..40d7364979 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -237,7 +237,7 @@ static int tcg_gdbstub_supported_sstep_flags(void) } } -static void tcg_accel_class_init(ObjectClass *oc, void *data) +static void tcg_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "tcg"; diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index 7aa28b9ab9..de52a8f882 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -116,7 +116,7 @@ static int xen_init(MachineState *ms) return 0; } -static void xen_accel_class_init(ObjectClass *oc, void *data) +static void xen_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); static GlobalProperty compat[] = { @@ -147,7 +147,7 @@ static const TypeInfo xen_accel_type = { .class_init = xen_accel_class_init, }; -static void xen_accel_ops_class_init(ObjectClass *oc, void *data) +static void xen_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); diff --git a/authz/list.c b/authz/list.c index 0e17eed897..bbd99f2b7f 100644 --- a/authz/list.c +++ b/authz/list.c @@ -116,7 +116,7 @@ qauthz_list_finalize(Object *obj) static void -qauthz_list_class_init(ObjectClass *oc, void *data) +qauthz_list_class_init(ObjectClass *oc, const void *data) { QAuthZClass *authz = QAUTHZ_CLASS(oc); diff --git a/authz/listfile.c b/authz/listfile.c index d31d9103f7..b58d4ebd1d 100644 --- a/authz/listfile.c +++ b/authz/listfile.c @@ -220,7 +220,7 @@ qauthz_list_file_finalize(Object *obj) static void -qauthz_list_file_class_init(ObjectClass *oc, void *data) +qauthz_list_file_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); QAuthZClass *authz = QAUTHZ_CLASS(oc); diff --git a/authz/pamacct.c b/authz/pamacct.c index c862d9ff39..07b8aad497 100644 --- a/authz/pamacct.c +++ b/authz/pamacct.c @@ -103,7 +103,7 @@ qauthz_pam_finalize(Object *obj) static void -qauthz_pam_class_init(ObjectClass *oc, void *data) +qauthz_pam_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); QAuthZClass *authz = QAUTHZ_CLASS(oc); diff --git a/authz/simple.c b/authz/simple.c index 0597dcd8ea..f6985b840e 100644 --- a/authz/simple.c +++ b/authz/simple.c @@ -78,7 +78,7 @@ qauthz_simple_complete(UserCreatable *uc, Error **errp) static void -qauthz_simple_class_init(ObjectClass *oc, void *data) +qauthz_simple_class_init(ObjectClass *oc, const void *data) { QAuthZClass *authz = QAUTHZ_CLASS(oc); UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); diff --git a/backends/confidential-guest-support.c b/backends/confidential-guest-support.c index 1cd9bed505..8ff7bfa857 100644 --- a/backends/confidential-guest-support.c +++ b/backends/confidential-guest-support.c @@ -20,7 +20,8 @@ OBJECT_DEFINE_ABSTRACT_TYPE(ConfidentialGuestSupport, CONFIDENTIAL_GUEST_SUPPORT, OBJECT) -static void confidential_guest_support_class_init(ObjectClass *oc, void *data) +static void confidential_guest_support_class_init(ObjectClass *oc, + const void *data) { } diff --git a/backends/cryptodev-builtin.c b/backends/cryptodev-builtin.c index 764cee4311..0414c01e06 100644 --- a/backends/cryptodev-builtin.c +++ b/backends/cryptodev-builtin.c @@ -608,7 +608,7 @@ static void cryptodev_builtin_cleanup( } static void -cryptodev_builtin_class_init(ObjectClass *oc, void *data) +cryptodev_builtin_class_init(ObjectClass *oc, const void *data) { CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc); diff --git a/backends/cryptodev-lkcf.c b/backends/cryptodev-lkcf.c index 352c3e8958..bb7a81d5d0 100644 --- a/backends/cryptodev-lkcf.c +++ b/backends/cryptodev-lkcf.c @@ -619,7 +619,7 @@ static int cryptodev_lkcf_close_session(CryptoDevBackend *backend, return 0; } -static void cryptodev_lkcf_class_init(ObjectClass *oc, void *data) +static void cryptodev_lkcf_class_init(ObjectClass *oc, const void *data) { CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc); diff --git a/backends/cryptodev-vhost-user.c b/backends/cryptodev-vhost-user.c index 3295c6198a..cb04e68b02 100644 --- a/backends/cryptodev-vhost-user.c +++ b/backends/cryptodev-vhost-user.c @@ -393,7 +393,7 @@ static void cryptodev_vhost_user_finalize(Object *obj) } static void -cryptodev_vhost_user_class_init(ObjectClass *oc, void *data) +cryptodev_vhost_user_class_init(ObjectClass *oc, const void *data) { CryptoDevBackendClass *bc = CRYPTODEV_BACKEND_CLASS(oc); diff --git a/backends/cryptodev.c b/backends/cryptodev.c index 1187b08dac..51bbe5ce40 100644 --- a/backends/cryptodev.c +++ b/backends/cryptodev.c @@ -608,7 +608,7 @@ static void cryptodev_backend_schemas_cb(StatsSchemaList **result, } static void -cryptodev_backend_class_init(ObjectClass *oc, void *data) +cryptodev_backend_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); diff --git a/backends/dbus-vmstate.c b/backends/dbus-vmstate.c index be6c4d8e0a..8c2deef43d 100644 --- a/backends/dbus-vmstate.c +++ b/backends/dbus-vmstate.c @@ -485,7 +485,7 @@ dbus_vmstate_get_id(VMStateIf *vmif) } static void -dbus_vmstate_class_init(ObjectClass *oc, void *data) +dbus_vmstate_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); VMStateIfClass *vc = VMSTATE_IF_CLASS(oc); diff --git a/backends/host_iommu_device.c b/backends/host_iommu_device.c index cea76c6925..f6965e4027 100644 --- a/backends/host_iommu_device.c +++ b/backends/host_iommu_device.c @@ -17,7 +17,7 @@ OBJECT_DEFINE_ABSTRACT_TYPE(HostIOMMUDevice, HOST_IOMMU_DEVICE, OBJECT) -static void host_iommu_device_class_init(ObjectClass *oc, void *data) +static void host_iommu_device_class_init(ObjectClass *oc, const void *data) { } diff --git a/backends/hostmem-epc.c b/backends/hostmem-epc.c index 1fa2d031e4..ab20b18233 100644 --- a/backends/hostmem-epc.c +++ b/backends/hostmem-epc.c @@ -50,7 +50,7 @@ static void sgx_epc_backend_instance_init(Object *obj) m->dump = false; } -static void sgx_epc_backend_class_init(ObjectClass *oc, void *data) +static void sgx_epc_backend_class_init(ObjectClass *oc, const void *data) { HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c index 691a827819..8e3219c061 100644 --- a/backends/hostmem-file.c +++ b/backends/hostmem-file.c @@ -270,7 +270,7 @@ static void file_backend_unparent(Object *obj) } static void -file_backend_class_init(ObjectClass *oc, void *data) +file_backend_class_init(ObjectClass *oc, const void *data) { HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); diff --git a/backends/hostmem-memfd.c b/backends/hostmem-memfd.c index 85daa1432c..923239f9cf 100644 --- a/backends/hostmem-memfd.c +++ b/backends/hostmem-memfd.c @@ -133,7 +133,7 @@ memfd_backend_instance_init(Object *obj) } static void -memfd_backend_class_init(ObjectClass *oc, void *data) +memfd_backend_class_init(ObjectClass *oc, const void *data) { HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); diff --git a/backends/hostmem-ram.c b/backends/hostmem-ram.c index 868ae6ca80..062b1abb11 100644 --- a/backends/hostmem-ram.c +++ b/backends/hostmem-ram.c @@ -37,7 +37,7 @@ ram_backend_memory_alloc(HostMemoryBackend *backend, Error **errp) } static void -ram_backend_class_init(ObjectClass *oc, void *data) +ram_backend_class_init(ObjectClass *oc, const void *data) { HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); diff --git a/backends/hostmem-shm.c b/backends/hostmem-shm.c index f67ad2740b..f66211a2ec 100644 --- a/backends/hostmem-shm.c +++ b/backends/hostmem-shm.c @@ -69,7 +69,7 @@ shm_backend_instance_init(Object *obj) } static void -shm_backend_class_init(ObjectClass *oc, void *data) +shm_backend_class_init(ObjectClass *oc, const void *data) { HostMemoryBackendClass *bc = MEMORY_BACKEND_CLASS(oc); diff --git a/backends/hostmem.c b/backends/hostmem.c index bceca1a8d9..195f37fa44 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -501,7 +501,7 @@ host_memory_backend_set_use_canonical_path(Object *obj, bool value, } static void -host_memory_backend_class_init(ObjectClass *oc, void *data) +host_memory_backend_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); diff --git a/backends/iommufd.c b/backends/iommufd.c index d57da44755..17f7ae3809 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -64,7 +64,7 @@ static bool iommufd_backend_can_be_deleted(UserCreatable *uc) return !be->users; } -static void iommufd_backend_class_init(ObjectClass *oc, void *data) +static void iommufd_backend_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -326,7 +326,7 @@ static int hiod_iommufd_get_cap(HostIOMMUDevice *hiod, int cap, Error **errp) } } -static void hiod_iommufd_class_init(ObjectClass *oc, void *data) +static void hiod_iommufd_class_init(ObjectClass *oc, const void *data) { HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc); diff --git a/backends/rng-builtin.c b/backends/rng-builtin.c index 4cfa7e578b..41b7bfaa27 100644 --- a/backends/rng-builtin.c +++ b/backends/rng-builtin.c @@ -55,7 +55,7 @@ static void rng_builtin_finalize(Object *obj) qemu_bh_delete(s->bh); } -static void rng_builtin_class_init(ObjectClass *klass, void *data) +static void rng_builtin_class_init(ObjectClass *klass, const void *data) { RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); diff --git a/backends/rng-egd.c b/backends/rng-egd.c index 82da46365d..9fd3393ede 100644 --- a/backends/rng-egd.c +++ b/backends/rng-egd.c @@ -143,7 +143,7 @@ static void rng_egd_finalize(Object *obj) g_free(s->chr_name); } -static void rng_egd_class_init(ObjectClass *klass, void *data) +static void rng_egd_class_init(ObjectClass *klass, const void *data) { RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); diff --git a/backends/rng-random.c b/backends/rng-random.c index 3ce6cc9b4a..820bf48c9b 100644 --- a/backends/rng-random.c +++ b/backends/rng-random.c @@ -121,7 +121,7 @@ static void rng_random_finalize(Object *obj) g_free(s->filename); } -static void rng_random_class_init(ObjectClass *klass, void *data) +static void rng_random_class_init(ObjectClass *klass, const void *data) { RngBackendClass *rbc = RNG_BACKEND_CLASS(klass); diff --git a/backends/rng.c b/backends/rng.c index 1f6fb106ae..b3480d27a1 100644 --- a/backends/rng.c +++ b/backends/rng.c @@ -99,7 +99,7 @@ static void rng_backend_finalize(Object *obj) rng_backend_free_requests(s); } -static void rng_backend_class_init(ObjectClass *oc, void *data) +static void rng_backend_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index 00fe015a94..43d350e895 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -1056,7 +1056,7 @@ static void tpm_emulator_inst_finalize(Object *obj) vmstate_unregister(NULL, &vmstate_tpm_emulator, obj); } -static void tpm_emulator_class_init(ObjectClass *klass, void *data) +static void tpm_emulator_class_init(ObjectClass *klass, const void *data) { TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); diff --git a/backends/tpm/tpm_passthrough.c b/backends/tpm/tpm_passthrough.c index 09a6abf02d..b7c7074c2a 100644 --- a/backends/tpm/tpm_passthrough.c +++ b/backends/tpm/tpm_passthrough.c @@ -364,7 +364,7 @@ static void tpm_passthrough_inst_finalize(Object *obj) qapi_free_TPMPassthroughOptions(tpm_pt->options); } -static void tpm_passthrough_class_init(ObjectClass *klass, void *data) +static void tpm_passthrough_class_init(ObjectClass *klass, const void *data) { TPMBackendClass *tbc = TPM_BACKEND_CLASS(klass); diff --git a/backends/vhost-user.c b/backends/vhost-user.c index d0e4d71a63..94274a619d 100644 --- a/backends/vhost-user.c +++ b/backends/vhost-user.c @@ -163,7 +163,7 @@ static char *get_chardev(Object *obj, Error **errp) return NULL; } -static void vhost_user_backend_class_init(ObjectClass *oc, void *data) +static void vhost_user_backend_class_init(ObjectClass *oc, const void *data) { object_class_property_add_str(oc, "chardev", get_chardev, set_chardev); } diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 9f4d252c74..9720cafb96 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -933,7 +933,8 @@ static bool throttle_group_can_be_deleted(UserCreatable *uc) return OBJECT(uc)->ref == 1; } -static void throttle_group_obj_class_init(ObjectClass *klass, void *class_data) +static void throttle_group_obj_class_init(ObjectClass *klass, + const void *class_data) { size_t i = 0; UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass); diff --git a/chardev/baum.c b/chardev/baum.c index a1d9784d92..f3e8cd27f0 100644 --- a/chardev/baum.c +++ b/chardev/baum.c @@ -668,7 +668,7 @@ static void baum_chr_open(Chardev *chr, qemu_set_fd_handler(baum->brlapi_fd, baum_chr_read, NULL, baum); } -static void char_braille_class_init(ObjectClass *oc, void *data) +static void char_braille_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-console.c b/chardev/char-console.c index 6c4ce5dbce..7e1bf642eb 100644 --- a/chardev/char-console.c +++ b/chardev/char-console.c @@ -34,7 +34,7 @@ static void qemu_chr_open_win_con(Chardev *chr, win_chr_set_file(chr, GetStdHandle(STD_OUTPUT_HANDLE), true); } -static void char_console_class_init(ObjectClass *oc, void *data) +static void char_console_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-fd.c b/chardev/char-fd.c index d2c4923359..23bfe3c0b1 100644 --- a/chardev/char-fd.c +++ b/chardev/char-fd.c @@ -238,7 +238,7 @@ void qemu_chr_open_fd(Chardev *chr, } } -static void char_fd_class_init(ObjectClass *oc, void *data) +static void char_fd_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-file.c b/chardev/char-file.c index 263e6da563..a9e8c5e0d7 100644 --- a/chardev/char-file.c +++ b/chardev/char-file.c @@ -123,7 +123,7 @@ static void qemu_chr_parse_file_out(QemuOpts *opts, ChardevBackend *backend, file->append = qemu_opt_get_bool(opts, "append", false); } -static void char_file_class_init(ObjectClass *oc, void *data) +static void char_file_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-hub.c b/chardev/char-hub.c index 3a4aae3289..16ffee2017 100644 --- a/chardev/char-hub.c +++ b/chardev/char-hub.c @@ -272,7 +272,7 @@ static void qemu_chr_parse_hub(QemuOpts *opts, ChardevBackend *backend, } } -static void char_hub_class_init(ObjectClass *oc, void *data) +static void char_hub_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-mux.c b/chardev/char-mux.c index d5f7e1a9cf..6b36290e2c 100644 --- a/chardev/char-mux.c +++ b/chardev/char-mux.c @@ -447,7 +447,7 @@ void resume_mux_open(void) chardev_options_parsed_cb, NULL); } -static void char_mux_class_init(ObjectClass *oc, void *data) +static void char_mux_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-null.c b/chardev/char-null.c index 1c6a2900f9..89cb85da79 100644 --- a/chardev/char-null.c +++ b/chardev/char-null.c @@ -34,7 +34,7 @@ static void null_chr_open(Chardev *chr, *be_opened = false; } -static void char_null_class_init(ObjectClass *oc, void *data) +static void char_null_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-parallel.c b/chardev/char-parallel.c index 78697d7522..62a44b2f96 100644 --- a/chardev/char-parallel.c +++ b/chardev/char-parallel.c @@ -270,7 +270,7 @@ static void qemu_chr_parse_parallel(QemuOpts *opts, ChardevBackend *backend, parallel->device = g_strdup(device); } -static void char_parallel_class_init(ObjectClass *oc, void *data) +static void char_parallel_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-pipe.c b/chardev/char-pipe.c index 5ad30bcc59..3d1b0ce2d2 100644 --- a/chardev/char-pipe.c +++ b/chardev/char-pipe.c @@ -171,7 +171,7 @@ static void qemu_chr_parse_pipe(QemuOpts *opts, ChardevBackend *backend, dev->device = g_strdup(device); } -static void char_pipe_class_init(ObjectClass *oc, void *data) +static void char_pipe_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-pty.c b/chardev/char-pty.c index 6a2c1dc13a..c28554e6e0 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -390,7 +390,7 @@ static void char_pty_parse(QemuOpts *opts, ChardevBackend *backend, pty->path = g_strdup(path); } -static void char_pty_class_init(ObjectClass *oc, void *data) +static void char_pty_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-ringbuf.c b/chardev/char-ringbuf.c index d40d21d3cf..98aadb6acf 100644 --- a/chardev/char-ringbuf.c +++ b/chardev/char-ringbuf.c @@ -223,7 +223,7 @@ static void qemu_chr_parse_ringbuf(QemuOpts *opts, ChardevBackend *backend, } } -static void char_ringbuf_class_init(ObjectClass *oc, void *data) +static void char_ringbuf_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-serial.c b/chardev/char-serial.c index 4b0b83d5b4..0a68b4b4e0 100644 --- a/chardev/char-serial.c +++ b/chardev/char-serial.c @@ -298,7 +298,7 @@ static void qemu_chr_parse_serial(QemuOpts *opts, ChardevBackend *backend, serial->device = g_strdup(device); } -static void char_serial_class_init(ObjectClass *oc, void *data) +static void char_serial_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-socket.c b/chardev/char-socket.c index 2f842f9f88..e8dd2931dc 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -1581,7 +1581,7 @@ char_socket_get_connected(Object *obj, Error **errp) return s->state == TCP_CHARDEV_STATE_CONNECTED; } -static void char_socket_class_init(ObjectClass *oc, void *data) +static void char_socket_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-stdio.c b/chardev/char-stdio.c index b960ddd4e4..48db8d2f30 100644 --- a/chardev/char-stdio.c +++ b/chardev/char-stdio.c @@ -136,7 +136,7 @@ static void qemu_chr_parse_stdio(QemuOpts *opts, ChardevBackend *backend, stdio->signal = qemu_opt_get_bool(opts, "signal", true); } -static void char_stdio_class_init(ObjectClass *oc, void *data) +static void char_stdio_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-udp.c b/chardev/char-udp.c index 3d9a2d5e77..572fab0ad1 100644 --- a/chardev/char-udp.c +++ b/chardev/char-udp.c @@ -219,7 +219,7 @@ static void qmp_chardev_open_udp(Chardev *chr, *be_opened = false; } -static void char_udp_class_init(ObjectClass *oc, void *data) +static void char_udp_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-win-stdio.c b/chardev/char-win-stdio.c index 13325ca967..fb802a00b1 100644 --- a/chardev/char-win-stdio.c +++ b/chardev/char-win-stdio.c @@ -256,7 +256,7 @@ static int win_stdio_write(Chardev *chr, const uint8_t *buf, int len) return len - len1; } -static void char_win_stdio_class_init(ObjectClass *oc, void *data) +static void char_win_stdio_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char-win.c b/chardev/char-win.c index d4fb44c4dc..fef45e83aa 100644 --- a/chardev/char-win.c +++ b/chardev/char-win.c @@ -220,7 +220,7 @@ void win_chr_set_file(Chardev *chr, HANDLE file, bool keep_open) s->file = file; } -static void char_win_class_init(ObjectClass *oc, void *data) +static void char_win_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/char.c b/chardev/char.c index 5a9e9762ad..bbebd246c3 100644 --- a/chardev/char.c +++ b/chardev/char.c @@ -295,7 +295,7 @@ static int null_chr_write(Chardev *chr, const uint8_t *buf, int len) return len; } -static void char_class_init(ObjectClass *oc, void *data) +static void char_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/msmouse.c b/chardev/msmouse.c index 2279694cfa..1a55755d39 100644 --- a/chardev/msmouse.c +++ b/chardev/msmouse.c @@ -267,7 +267,7 @@ static void msmouse_chr_open(Chardev *chr, fifo8_create(&mouse->outbuf, MSMOUSE_BUF_SZ); } -static void char_msmouse_class_init(ObjectClass *oc, void *data) +static void char_msmouse_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/spice.c b/chardev/spice.c index e843d961a7..db53b49da2 100644 --- a/chardev/spice.c +++ b/chardev/spice.c @@ -347,7 +347,7 @@ static void qemu_chr_parse_spice_port(QemuOpts *opts, ChardevBackend *backend, spiceport->fqdn = g_strdup(name); } -static void char_spice_class_init(ObjectClass *oc, void *data) +static void char_spice_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -366,7 +366,7 @@ static const TypeInfo char_spice_type_info = { }; module_obj(TYPE_CHARDEV_SPICE); -static void char_spicevmc_class_init(ObjectClass *oc, void *data) +static void char_spicevmc_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); @@ -382,7 +382,7 @@ static const TypeInfo char_spicevmc_type_info = { }; module_obj(TYPE_CHARDEV_SPICEVMC); -static void char_spiceport_class_init(ObjectClass *oc, void *data) +static void char_spiceport_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/testdev.c b/chardev/testdev.c index a92caca3c3..e91f4e8343 100644 --- a/chardev/testdev.c +++ b/chardev/testdev.c @@ -110,7 +110,7 @@ static int testdev_chr_write(Chardev *chr, const uint8_t *buf, int len) return orig_len; } -static void char_testdev_class_init(ObjectClass *oc, void *data) +static void char_testdev_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/chardev/wctablet.c b/chardev/wctablet.c index f4008bf35b..0dc6ef08f5 100644 --- a/chardev/wctablet.c +++ b/chardev/wctablet.c @@ -342,7 +342,7 @@ static void wctablet_chr_open(Chardev *chr, &wctablet_handler); } -static void wctablet_chr_class_init(ObjectClass *oc, void *data) +static void wctablet_chr_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/crypto/secret.c b/crypto/secret.c index 44eaff16f6..61a4584286 100644 --- a/crypto/secret.c +++ b/crypto/secret.c @@ -117,7 +117,7 @@ qcrypto_secret_finalize(Object *obj) } static void -qcrypto_secret_class_init(ObjectClass *oc, void *data) +qcrypto_secret_class_init(ObjectClass *oc, const void *data) { QCryptoSecretCommonClass *sic = QCRYPTO_SECRET_COMMON_CLASS(oc); sic->load_data = qcrypto_secret_load_data; diff --git a/crypto/secret_common.c b/crypto/secret_common.c index dbda998940..2399ce412b 100644 --- a/crypto/secret_common.c +++ b/crypto/secret_common.c @@ -263,7 +263,7 @@ qcrypto_secret_finalize(Object *obj) } static void -qcrypto_secret_class_init(ObjectClass *oc, void *data) +qcrypto_secret_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); diff --git a/crypto/secret_keyring.c b/crypto/secret_keyring.c index 1b7edec84a..78d7f09b3b 100644 --- a/crypto/secret_keyring.c +++ b/crypto/secret_keyring.c @@ -103,7 +103,7 @@ qcrypto_secret_prop_get_key(Object *obj, Visitor *v, static void -qcrypto_secret_keyring_class_init(ObjectClass *oc, void *data) +qcrypto_secret_keyring_class_init(ObjectClass *oc, const void *data) { QCryptoSecretCommonClass *sic = QCRYPTO_SECRET_COMMON_CLASS(oc); sic->load_data = qcrypto_secret_keyring_load_data; diff --git a/crypto/tls-cipher-suites.c b/crypto/tls-cipher-suites.c index d0df4badc0..e546cc7c0e 100644 --- a/crypto/tls-cipher-suites.c +++ b/crypto/tls-cipher-suites.c @@ -102,7 +102,8 @@ static GByteArray *qcrypto_tls_cipher_suites_fw_cfg_gen_data(Object *obj, errp); } -static void qcrypto_tls_cipher_suites_class_init(ObjectClass *oc, void *data) +static void qcrypto_tls_cipher_suites_class_init(ObjectClass *oc, + const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); FWCfgDataGeneratorClass *fwgc = FW_CFG_DATA_GENERATOR_CLASS(oc); diff --git a/crypto/tlscreds.c b/crypto/tlscreds.c index 084ce0d51a..9e59594d67 100644 --- a/crypto/tlscreds.c +++ b/crypto/tlscreds.c @@ -223,7 +223,7 @@ qcrypto_tls_creds_prop_get_endpoint(Object *obj, static void -qcrypto_tls_creds_class_init(ObjectClass *oc, void *data) +qcrypto_tls_creds_class_init(ObjectClass *oc, const void *data) { object_class_property_add_bool(oc, "verify-peer", qcrypto_tls_creds_prop_get_verify, diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c index 476cf89c96..0e2d133821 100644 --- a/crypto/tlscredsanon.c +++ b/crypto/tlscredsanon.c @@ -137,7 +137,7 @@ qcrypto_tls_creds_anon_finalize(Object *obj) static void -qcrypto_tls_creds_anon_class_init(ObjectClass *oc, void *data) +qcrypto_tls_creds_anon_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c index aa270d7988..287c2a3c96 100644 --- a/crypto/tlscredspsk.c +++ b/crypto/tlscredspsk.c @@ -236,7 +236,7 @@ qcrypto_tls_creds_psk_prop_get_username(Object *obj, } static void -qcrypto_tls_creds_psk_class_init(ObjectClass *oc, void *data) +qcrypto_tls_creds_psk_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c index 24ec584922..143993f539 100644 --- a/crypto/tlscredsx509.c +++ b/crypto/tlscredsx509.c @@ -802,7 +802,7 @@ qcrypto_tls_creds_x509_finalize(Object *obj) static void -qcrypto_tls_creds_x509_class_init(ObjectClass *oc, void *data) +qcrypto_tls_creds_x509_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); QCryptoTLSCredsClass *ctcc = QCRYPTO_TLS_CREDS_CLASS(oc); diff --git a/docs/devel/qom.rst b/docs/devel/qom.rst index 0889ca949c..5870745ba2 100644 --- a/docs/devel/qom.rst +++ b/docs/devel/qom.rst @@ -147,7 +147,7 @@ to introduce an overridden virtual function: #include "qdev.h" - void my_device_class_init(ObjectClass *klass, void *class_data) + void my_device_class_init(ObjectClass *klass, const void *class_data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->reset = my_device_reset; @@ -249,7 +249,7 @@ class, which someone might choose to change at some point. // do something } - static void my_class_init(ObjectClass *oc, void *data) + static void my_class_init(ObjectClass *oc, const void *data) { MyClass *mc = MY_CLASS(oc); @@ -279,7 +279,7 @@ class, which someone might choose to change at some point. // do something else here } - static void derived_class_init(ObjectClass *oc, void *data) + static void derived_class_init(ObjectClass *oc, const void *data) { MyClass *mc = MY_CLASS(oc); DerivedClass *dc = DERIVED_CLASS(oc); @@ -363,7 +363,7 @@ This is equivalent to the following: :caption: Expansion from defining a simple type static void my_device_finalize(Object *obj); - static void my_device_class_init(ObjectClass *oc, void *data); + static void my_device_class_init(ObjectClass *oc, const void *data); static void my_device_init(Object *obj); static const TypeInfo my_device_info = { diff --git a/docs/devel/reset.rst b/docs/devel/reset.rst index 0b8b2fa5f4..c02fe0a405 100644 --- a/docs/devel/reset.rst +++ b/docs/devel/reset.rst @@ -216,7 +216,7 @@ in reset. ResettablePhases parent_phases; } MyDevClass; - static void mydev_class_init(ObjectClass *class, void *data) + static void mydev_class_init(ObjectClass *class, const void *data) { MyDevClass *myclass = MYDEV_CLASS(class); ResettableClass *rc = RESETTABLE_CLASS(class); diff --git a/docs/devel/virtio-backends.rst b/docs/devel/virtio-backends.rst index 679d7544b8..ebddc3b9f5 100644 --- a/docs/devel/virtio-backends.rst +++ b/docs/devel/virtio-backends.rst @@ -119,7 +119,7 @@ manually instantiated: qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } - static void virtio_blk_pci_class_init(ObjectClass *klass, void *data) + static void virtio_blk_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/event-loop-base.c b/event-loop-base.c index ddf8400a6b..733c54486c 100644 --- a/event-loop-base.c +++ b/event-loop-base.c @@ -97,7 +97,8 @@ static bool event_loop_base_can_be_deleted(UserCreatable *uc) return true; } -static void event_loop_base_class_init(ObjectClass *klass, void *class_data) +static void event_loop_base_class_init(ObjectClass *klass, + const void *class_data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(klass); ucc->complete = event_loop_base_complete; diff --git a/gdbstub/system.c b/gdbstub/system.c index dd22ff0fb3..8a32d8e1a1 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -243,7 +243,7 @@ static void gdb_monitor_open(Chardev *chr, ChardevBackend *backend, *be_opened = false; } -static void char_gdb_class_init(ObjectClass *oc, void *data) +static void char_gdb_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/hw/9pfs/virtio-9p-device.c b/hw/9pfs/virtio-9p-device.c index bb2843da0f..81b91e47c6 100644 --- a/hw/9pfs/virtio-9p-device.c +++ b/hw/9pfs/virtio-9p-device.c @@ -248,7 +248,7 @@ static const Property virtio_9p_properties[] = { DEFINE_PROP_STRING("fsdev", V9fsVirtioState, state.fsconf.fsdev_id), }; -static void virtio_9p_class_init(ObjectClass *klass, void *data) +static void virtio_9p_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index 2e49b551f2..90148ec9dc 100644 --- a/hw/acpi/erst.c +++ b/hw/acpi/erst.c @@ -1018,7 +1018,7 @@ static const Property erst_properties[] = { default_record_size, ERST_RECORD_SIZE), }; -static void erst_class_init(ObjectClass *klass, void *data) +static void erst_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index c85d97ca37..f589e79a2b 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -469,7 +469,7 @@ static void acpi_ged_initfn(Object *obj) sysbus_init_mmio(sbd, &ged_st->regs); } -static void acpi_ged_class_init(ObjectClass *class, void *data) +static void acpi_ged_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class); diff --git a/hw/acpi/pci.c b/hw/acpi/pci.c index f88f450af3..d511a85029 100644 --- a/hw/acpi/pci.c +++ b/hw/acpi/pci.c @@ -133,7 +133,7 @@ static void acpi_generic_initiator_set_node(Object *obj, Visitor *v, ms->numa_state->nodes[gi->node].has_gi = true; } -static void acpi_generic_initiator_class_init(ObjectClass *oc, void *data) +static void acpi_generic_initiator_class_init(ObjectClass *oc, const void *data) { object_class_property_add_str(oc, "pci-dev", NULL, acpi_generic_initiator_set_pci_device); @@ -247,7 +247,7 @@ static void acpi_generic_port_set_node(Object *obj, Visitor *v, gp->node = value; } -static void acpi_generic_port_class_init(ObjectClass *oc, void *data) +static void acpi_generic_port_class_init(ObjectClass *oc, const void *data) { object_class_property_add_str(oc, "pci-bus", NULL, acpi_generic_port_set_pci_bus); diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 6d023e595b..5860e8408b 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -619,7 +619,7 @@ static const Property piix4_pm_properties[] = { not_migrate_acpi_index, false), }; -static void piix4_pm_class_init(ObjectClass *klass, void *data) +static void piix4_pm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/acpi/vmclock.c b/hw/acpi/vmclock.c index 7387e5c9ca..c582c0c1f8 100644 --- a/hw/acpi/vmclock.c +++ b/hw/acpi/vmclock.c @@ -154,7 +154,7 @@ static void vmclock_realize(DeviceState *dev, Error **errp) vmclock_update_guest(vms); } -static void vmclock_device_class_init(ObjectClass *klass, void *data) +static void vmclock_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index 008768e036..fac3d6d97e 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -218,7 +218,7 @@ static const Property vmgenid_device_properties[] = { DEFINE_PROP_UUID(VMGENID_GUID, VmGenIdState, guid), }; -static void vmgenid_device_class_init(ObjectClass *klass, void *data) +static void vmgenid_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/adc/aspeed_adc.c b/hw/adc/aspeed_adc.c index 1cc554f179..3e820cae1e 100644 --- a/hw/adc/aspeed_adc.c +++ b/hw/adc/aspeed_adc.c @@ -291,7 +291,7 @@ static const Property aspeed_adc_engine_properties[] = { DEFINE_PROP_UINT32("nr-channels", AspeedADCEngineState, nr_channels, 0), }; -static void aspeed_adc_engine_class_init(ObjectClass *klass, void *data) +static void aspeed_adc_engine_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -369,7 +369,7 @@ static void aspeed_adc_realize(DeviceState *dev, Error **errp) } } -static void aspeed_adc_class_init(ObjectClass *klass, void *data) +static void aspeed_adc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedADCClass *aac = ASPEED_ADC_CLASS(klass); @@ -379,7 +379,7 @@ static void aspeed_adc_class_init(ObjectClass *klass, void *data) aac->nr_engines = 1; } -static void aspeed_2600_adc_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_adc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedADCClass *aac = ASPEED_ADC_CLASS(klass); @@ -388,7 +388,7 @@ static void aspeed_2600_adc_class_init(ObjectClass *klass, void *data) aac->nr_engines = 2; } -static void aspeed_1030_adc_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_adc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedADCClass *aac = ASPEED_ADC_CLASS(klass); @@ -397,7 +397,7 @@ static void aspeed_1030_adc_class_init(ObjectClass *klass, void *data) aac->nr_engines = 2; } -static void aspeed_2700_adc_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_adc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedADCClass *aac = ASPEED_ADC_CLASS(klass); diff --git a/hw/adc/npcm7xx_adc.c b/hw/adc/npcm7xx_adc.c index 0a83d28605..ddb219d456 100644 --- a/hw/adc/npcm7xx_adc.c +++ b/hw/adc/npcm7xx_adc.c @@ -271,7 +271,7 @@ static const Property npcm7xx_timer_properties[] = { DEFINE_PROP_UINT32("iref", NPCM7xxADCState, iref, NPCM7XX_ADC_DEFAULT_IREF), }; -static void npcm7xx_adc_class_init(ObjectClass *klass, void *data) +static void npcm7xx_adc_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/adc/stm32f2xx_adc.c b/hw/adc/stm32f2xx_adc.c index e3b21f9077..a490ae640d 100644 --- a/hw/adc/stm32f2xx_adc.c +++ b/hw/adc/stm32f2xx_adc.c @@ -284,7 +284,7 @@ static void stm32f2xx_adc_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } -static void stm32f2xx_adc_class_init(ObjectClass *klass, void *data) +static void stm32f2xx_adc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/adc/zynq-xadc.c b/hw/adc/zynq-xadc.c index 26d9a7b9a5..748a51ba78 100644 --- a/hw/adc/zynq-xadc.c +++ b/hw/adc/zynq-xadc.c @@ -281,7 +281,7 @@ static const VMStateDescription vmstate_zynq_xadc = { } }; -static void zynq_xadc_class_init(ObjectClass *klass, void *data) +static void zynq_xadc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/alpha/typhoon.c b/hw/alpha/typhoon.c index 9718e1a579..4c56f981d7 100644 --- a/hw/alpha/typhoon.c +++ b/hw/alpha/typhoon.c @@ -935,7 +935,7 @@ static const TypeInfo typhoon_pcihost_info = { }; static void typhoon_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/arm/allwinner-a10.c b/hw/arm/allwinner-a10.c index f1b399759a..dc910d4177 100644 --- a/hw/arm/allwinner-a10.c +++ b/hw/arm/allwinner-a10.c @@ -208,7 +208,7 @@ static void aw_a10_realize(DeviceState *dev, Error **errp) sysbus_mmio_map_overlap(SYS_BUS_DEVICE(&s->wdt), 0, AW_A10_WDT_BASE, 1); } -static void aw_a10_class_init(ObjectClass *oc, void *data) +static void aw_a10_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/allwinner-h3.c b/hw/arm/allwinner-h3.c index 1b1afa4fb6..edffc21dd8 100644 --- a/hw/arm/allwinner-h3.c +++ b/hw/arm/allwinner-h3.c @@ -466,7 +466,7 @@ static void allwinner_h3_realize(DeviceState *dev, Error **errp) } } -static void allwinner_h3_class_init(ObjectClass *oc, void *data) +static void allwinner_h3_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index cef6e4d18c..0bf700865c 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -539,7 +539,7 @@ static void allwinner_r40_realize(DeviceState *dev, Error **errp) } } -static void allwinner_r40_class_init(ObjectClass *oc, void *data) +static void allwinner_r40_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index ffd732f806..d65a46b8d8 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -1691,7 +1691,7 @@ static void armsse_reset(DeviceState *dev) s->nsccfg = 0; } -static void armsse_class_init(ObjectClass *klass, void *data) +static void armsse_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(klass); diff --git a/hw/arm/armv7m.c b/hw/arm/armv7m.c index 64009174b9..cea3eb49ee 100644 --- a/hw/arm/armv7m.c +++ b/hw/arm/armv7m.c @@ -565,7 +565,7 @@ static const VMStateDescription vmstate_armv7m = { } }; -static void armv7m_class_init(ObjectClass *klass, void *data) +static void armv7m_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -636,7 +636,7 @@ static const Property bitband_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), }; -static void bitband_class_init(ObjectClass *klass, void *data) +static void bitband_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 82f42582fa..20f418fb63 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -1227,7 +1227,7 @@ static void aspeed_machine_ast2600_class_emmc_init(ObjectClass *oc) "Set or unset boot from EMMC"); } -static void aspeed_machine_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1243,7 +1243,8 @@ static void aspeed_machine_class_init(ObjectClass *oc, void *data) aspeed_machine_class_props_init(oc); } -static void aspeed_machine_palmetto_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_palmetto_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1260,7 +1261,8 @@ static void aspeed_machine_palmetto_class_init(ObjectClass *oc, void *data) aspeed_machine_class_init_cpus_defaults(mc); }; -static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1278,7 +1280,7 @@ static void aspeed_machine_quanta_q71l_class_init(ObjectClass *oc, void *data) } static void aspeed_machine_supermicrox11_bmc_class_init(ObjectClass *oc, - void *data) + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1297,7 +1299,7 @@ static void aspeed_machine_supermicrox11_bmc_class_init(ObjectClass *oc, } static void aspeed_machine_supermicro_x11spi_bmc_class_init(ObjectClass *oc, - void *data) + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1315,7 +1317,8 @@ static void aspeed_machine_supermicro_x11spi_bmc_class_init(ObjectClass *oc, aspeed_machine_class_init_cpus_defaults(mc); } -static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1332,7 +1335,8 @@ static void aspeed_machine_ast2500_evb_class_init(ObjectClass *oc, void *data) aspeed_machine_class_init_cpus_defaults(mc); }; -static void aspeed_machine_yosemitev2_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_yosemitev2_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1350,7 +1354,8 @@ static void aspeed_machine_yosemitev2_class_init(ObjectClass *oc, void *data) aspeed_machine_class_init_cpus_defaults(mc); }; -static void aspeed_machine_romulus_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_romulus_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1367,7 +1372,8 @@ static void aspeed_machine_romulus_class_init(ObjectClass *oc, void *data) aspeed_machine_class_init_cpus_defaults(mc); }; -static void aspeed_machine_tiogapass_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_tiogapass_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1385,7 +1391,8 @@ static void aspeed_machine_tiogapass_class_init(ObjectClass *oc, void *data) aspeed_machine_class_init_cpus_defaults(mc); }; -static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1402,7 +1409,8 @@ static void aspeed_machine_sonorapass_class_init(ObjectClass *oc, void *data) aspeed_machine_class_init_cpus_defaults(mc); }; -static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1419,7 +1427,8 @@ static void aspeed_machine_witherspoon_class_init(ObjectClass *oc, void *data) aspeed_machine_class_init_cpus_defaults(mc); }; -static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1441,7 +1450,7 @@ static void aspeed_machine_ast2600_evb_class_init(ObjectClass *oc, void *data) aspeed_machine_ast2600_class_emmc_init(oc); }; -static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_g220a_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1459,7 +1468,8 @@ static void aspeed_machine_g220a_class_init(ObjectClass *oc, void *data) aspeed_machine_class_init_cpus_defaults(mc); }; -static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1477,7 +1487,7 @@ static void aspeed_machine_fp5280g2_class_init(ObjectClass *oc, void *data) aspeed_machine_class_init_cpus_defaults(mc); }; -static void aspeed_machine_rainier_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_rainier_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1499,7 +1509,7 @@ static void aspeed_machine_rainier_class_init(ObjectClass *oc, void *data) #define FUJI_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB) -static void aspeed_machine_fuji_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_fuji_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1521,7 +1531,8 @@ static void aspeed_machine_fuji_class_init(ObjectClass *oc, void *data) #define BLETCHLEY_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB) -static void aspeed_machine_bletchley_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_bletchley_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1566,7 +1577,7 @@ static void fby35_reset(MachineState *state, ResetType type) object_property_set_bool(OBJECT(gpio), "gpioB5", false, &error_fatal); } -static void aspeed_machine_fby35_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_fby35_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1644,7 +1655,7 @@ static void ast1030_evb_i2c_init(AspeedMachineState *bmc) } static void aspeed_minibmc_machine_ast1030_evb_class_init(ObjectClass *oc, - void *data) + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1673,7 +1684,8 @@ static void ast2700_evb_i2c_init(AspeedMachineState *bmc) TYPE_TMP105, 0x4d); } -static void aspeed_machine_ast2700a0_evb_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_ast2700a0_evb_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1694,7 +1706,8 @@ static void aspeed_machine_ast2700a0_evb_class_init(ObjectClass *oc, void *data) aspeed_machine_class_init_cpus_defaults(mc); } -static void aspeed_machine_ast2700a1_evb_class_init(ObjectClass *oc, void *data) +static void aspeed_machine_ast2700a1_evb_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1716,7 +1729,7 @@ static void aspeed_machine_ast2700a1_evb_class_init(ObjectClass *oc, void *data) #endif static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, - void *data) + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); @@ -1736,7 +1749,7 @@ static void aspeed_machine_qcom_dc_scm_v1_class_init(ObjectClass *oc, }; static void aspeed_machine_qcom_firework_class_init(ObjectClass *oc, - void *data) + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); diff --git a/hw/arm/aspeed_ast10x0.c b/hw/arm/aspeed_ast10x0.c index 21ffab10f3..e6e1ee63c1 100644 --- a/hw/arm/aspeed_ast10x0.c +++ b/hw/arm/aspeed_ast10x0.c @@ -415,7 +415,7 @@ static void aspeed_soc_ast1030_realize(DeviceState *dev_soc, Error **errp) sc->memmap[ASPEED_DEV_JTAG1], 0x20); } -static void aspeed_soc_ast1030_class_init(ObjectClass *klass, void *data) +static void aspeed_soc_ast1030_class_init(ObjectClass *klass, const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-m4"), /* TODO cortex-m4f */ diff --git a/hw/arm/aspeed_ast2400.c b/hw/arm/aspeed_ast2400.c index 0158f6e9c2..c7b0f21887 100644 --- a/hw/arm/aspeed_ast2400.c +++ b/hw/arm/aspeed_ast2400.c @@ -502,7 +502,7 @@ static void aspeed_ast2400_soc_realize(DeviceState *dev, Error **errp) aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); } -static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data) +static void aspeed_soc_ast2400_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("arm926"), @@ -530,7 +530,7 @@ static void aspeed_soc_ast2400_class_init(ObjectClass *oc, void *data) sc->get_irq = aspeed_soc_ast2400_get_irq; } -static void aspeed_soc_ast2500_class_init(ObjectClass *oc, void *data) +static void aspeed_soc_ast2500_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("arm1176"), diff --git a/hw/arm/aspeed_ast2600.c b/hw/arm/aspeed_ast2600.c index 1f994ba26c..d12707f0ab 100644 --- a/hw/arm/aspeed_ast2600.c +++ b/hw/arm/aspeed_ast2600.c @@ -653,7 +653,7 @@ static bool aspeed_soc_ast2600_boot_from_emmc(AspeedSoCState *s) return !!(hw_strap1 & SCU_AST2600_HW_STRAP_BOOT_SRC_EMMC); } -static void aspeed_soc_ast2600_class_init(ObjectClass *oc, void *data) +static void aspeed_soc_ast2600_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-a7"), diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index dce7255a2c..63a366f7e8 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -883,7 +883,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) create_unimplemented_device("ast2700.io", 0x0, 0x4000000); } -static void aspeed_soc_ast2700a0_class_init(ObjectClass *oc, void *data) +static void aspeed_soc_ast2700a0_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-a35"), @@ -910,7 +910,7 @@ static void aspeed_soc_ast2700a0_class_init(ObjectClass *oc, void *data) sc->get_irq = aspeed_soc_ast2700_get_irq; } -static void aspeed_soc_ast2700a1_class_init(ObjectClass *oc, void *data) +static void aspeed_soc_ast2700a1_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-a35"), diff --git a/hw/arm/aspeed_soc_common.c b/hw/arm/aspeed_soc_common.c index 1ddcb26c1e..1c4ac93a0f 100644 --- a/hw/arm/aspeed_soc_common.c +++ b/hw/arm/aspeed_soc_common.c @@ -146,7 +146,7 @@ static const Property aspeed_soc_properties[] = { MemoryRegion *), }; -static void aspeed_soc_class_init(ObjectClass *oc, void *data) +static void aspeed_soc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); AspeedSoCClass *sc = ASPEED_SOC_CLASS(oc); diff --git a/hw/arm/b-l475e-iot01a.c b/hw/arm/b-l475e-iot01a.c index c9a5209216..34ed2e0851 100644 --- a/hw/arm/b-l475e-iot01a.c +++ b/hw/arm/b-l475e-iot01a.c @@ -110,7 +110,7 @@ static void bl475e_init(MachineState *machine) } } -static void bl475e_machine_init(ObjectClass *oc, void *data) +static void bl475e_machine_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); static const char *machine_valid_cpu_types[] = { diff --git a/hw/arm/bcm2835_peripherals.c b/hw/arm/bcm2835_peripherals.c index adc9730c2e..8a1e72dfab 100644 --- a/hw/arm/bcm2835_peripherals.c +++ b/hw/arm/bcm2835_peripherals.c @@ -520,7 +520,7 @@ void bcm_soc_peripherals_common_realize(DeviceState *dev, Error **errp) create_unimp(s, &s->sdramc, "bcm2835-sdramc", SDRAMC_OFFSET, 0x100); } -static void bcm2835_peripherals_class_init(ObjectClass *oc, void *data) +static void bcm2835_peripherals_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); BCMSocPeripheralBaseClass *bc = BCM_SOC_PERIPHERALS_BASE_CLASS(oc); diff --git a/hw/arm/bcm2836.c b/hw/arm/bcm2836.c index 95e16806fa..cd61ba1505 100644 --- a/hw/arm/bcm2836.c +++ b/hw/arm/bcm2836.c @@ -163,7 +163,7 @@ static void bcm2836_realize(DeviceState *dev, Error **errp) } } -static void bcm283x_base_class_init(ObjectClass *oc, void *data) +static void bcm283x_base_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -171,7 +171,7 @@ static void bcm283x_base_class_init(ObjectClass *oc, void *data) dc->user_creatable = false; } -static void bcm2835_class_init(ObjectClass *oc, void *data) +static void bcm2835_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); BCM283XBaseClass *bc = BCM283X_BASE_CLASS(oc); @@ -182,7 +182,7 @@ static void bcm2835_class_init(ObjectClass *oc, void *data) dc->realize = bcm2835_realize; }; -static void bcm2836_class_init(ObjectClass *oc, void *data) +static void bcm2836_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); BCM283XBaseClass *bc = BCM283X_BASE_CLASS(oc); @@ -196,7 +196,7 @@ static void bcm2836_class_init(ObjectClass *oc, void *data) }; #ifdef TARGET_AARCH64 -static void bcm2837_class_init(ObjectClass *oc, void *data) +static void bcm2837_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); BCM283XBaseClass *bc = BCM283X_BASE_CLASS(oc); diff --git a/hw/arm/bcm2838.c b/hw/arm/bcm2838.c index ddb7c5f757..22aa754613 100644 --- a/hw/arm/bcm2838.c +++ b/hw/arm/bcm2838.c @@ -233,7 +233,7 @@ static void bcm2838_realize(DeviceState *dev, Error **errp) qdev_pass_gpios(DEVICE(&s->gic), DEVICE(&s->peripherals), NULL); } -static void bcm2838_class_init(ObjectClass *oc, void *data) +static void bcm2838_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); BCM283XBaseClass *bc_base = BCM283X_BASE_CLASS(oc); diff --git a/hw/arm/bcm2838_peripherals.c b/hw/arm/bcm2838_peripherals.c index e28bef4a37..812b5b8480 100644 --- a/hw/arm/bcm2838_peripherals.c +++ b/hw/arm/bcm2838_peripherals.c @@ -196,7 +196,7 @@ static void bcm2838_peripherals_realize(DeviceState *dev, Error **errp) create_unimp(s_base, &s->asb, "bcm2838-asb", BRDG_OFFSET, 0x24); } -static void bcm2838_peripherals_class_init(ObjectClass *oc, void *data) +static void bcm2838_peripherals_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); BCM2838PeripheralClass *bc = BCM2838_PERIPHERALS_CLASS(oc); diff --git a/hw/arm/collie.c b/hw/arm/collie.c index e83aee58c6..93bb190f1f 100644 --- a/hw/arm/collie.c +++ b/hw/arm/collie.c @@ -69,7 +69,7 @@ static void collie_init(MachineState *machine) arm_load_kernel(cms->sa1110->cpu, machine, &collie_binfo); } -static void collie_machine_class_init(ObjectClass *oc, void *data) +static void collie_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/digic.c b/hw/arm/digic.c index 5836619d9f..d831bc974d 100644 --- a/hw/arm/digic.c +++ b/hw/arm/digic.c @@ -79,7 +79,7 @@ static void digic_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(sbd, 0, DIGIC_UART_BASE); } -static void digic_class_init(ObjectClass *oc, void *data) +static void digic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/exynos4210.c b/hw/arm/exynos4210.c index 0c27588116..76001ff0df 100644 --- a/hw/arm/exynos4210.c +++ b/hw/arm/exynos4210.c @@ -842,7 +842,7 @@ static void exynos4210_init(Object *obj) TYPE_EXYNOS4210_COMBINER); } -static void exynos4210_class_init(ObjectClass *klass, void *data) +static void exynos4210_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/exynos4_boards.c b/hw/arm/exynos4_boards.c index 2d8f2d7326..7304974131 100644 --- a/hw/arm/exynos4_boards.c +++ b/hw/arm/exynos4_boards.c @@ -154,7 +154,7 @@ static const char * const valid_cpu_types[] = { NULL }; -static void nuri_class_init(ObjectClass *oc, void *data) +static void nuri_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -174,7 +174,7 @@ static const TypeInfo nuri_type = { .class_init = nuri_class_init, }; -static void smdkc210_class_init(ObjectClass *oc, void *data) +static void smdkc210_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/fby35.c b/hw/arm/fby35.c index 6d3663f14a..e123fa69e1 100644 --- a/hw/arm/fby35.c +++ b/hw/arm/fby35.c @@ -162,7 +162,7 @@ static void fby35_instance_init(Object *obj) FBY35(obj)->mmio_exec = false; } -static void fby35_class_init(ObjectClass *oc, void *data) +static void fby35_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/fsl-imx25.c b/hw/arm/fsl-imx25.c index 02214ca1a1..7aad6359ea 100644 --- a/hw/arm/fsl-imx25.c +++ b/hw/arm/fsl-imx25.c @@ -311,7 +311,7 @@ static const Property fsl_imx25_properties[] = { DEFINE_PROP_UINT32("fec-phy-num", FslIMX25State, phy_num, 0), }; -static void fsl_imx25_class_init(ObjectClass *oc, void *data) +static void fsl_imx25_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/fsl-imx31.c b/hw/arm/fsl-imx31.c index 2a8ffb15f7..e9f70ad94b 100644 --- a/hw/arm/fsl-imx31.c +++ b/hw/arm/fsl-imx31.c @@ -218,7 +218,7 @@ static void fsl_imx31_realize(DeviceState *dev, Error **errp) &s->iram_alias); } -static void fsl_imx31_class_init(ObjectClass *oc, void *data) +static void fsl_imx31_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/fsl-imx6.c b/hw/arm/fsl-imx6.c index a114dc0d63..f3a60022d8 100644 --- a/hw/arm/fsl-imx6.c +++ b/hw/arm/fsl-imx6.c @@ -484,7 +484,7 @@ static const Property fsl_imx6_properties[] = { DEFINE_PROP_UINT32("fec-phy-num", FslIMX6State, phy_num, 0), }; -static void fsl_imx6_class_init(ObjectClass *oc, void *data) +static void fsl_imx6_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/fsl-imx6ul.c b/hw/arm/fsl-imx6ul.c index ce8d3ef535..883c7fc534 100644 --- a/hw/arm/fsl-imx6ul.c +++ b/hw/arm/fsl-imx6ul.c @@ -715,7 +715,7 @@ static const Property fsl_imx6ul_properties[] = { true), }; -static void fsl_imx6ul_class_init(ObjectClass *oc, void *data) +static void fsl_imx6ul_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/fsl-imx7.c b/hw/arm/fsl-imx7.c index ed1f10bca2..02f7602077 100644 --- a/hw/arm/fsl-imx7.c +++ b/hw/arm/fsl-imx7.c @@ -748,7 +748,7 @@ static const Property fsl_imx7_properties[] = { true), }; -static void fsl_imx7_class_init(ObjectClass *oc, void *data) +static void fsl_imx7_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index af7a7e6745..23e662c16c 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -689,7 +689,7 @@ static const Property fsl_imx8mp_properties[] = { DEFINE_PROP_BOOL("fec1-phy-connected", FslImx8mpState, phy_connected, true), }; -static void fsl_imx8mp_class_init(ObjectClass *oc, void *data) +static void fsl_imx8mp_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 0f3c207d54..3ae26ebebd 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -139,7 +139,7 @@ static void highbank_regs_init(Object *obj) sysbus_init_mmio(dev, &s->iomem); } -static void highbank_regs_class_init(ObjectClass *klass, void *data) +static void highbank_regs_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -341,7 +341,7 @@ static void midway_init(MachineState *machine) calxeda_init(machine, CALXEDA_MIDWAY); } -static void highbank_class_init(ObjectClass *oc, void *data) +static void highbank_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-a9"), @@ -365,7 +365,7 @@ static const TypeInfo highbank_type = { .class_init = highbank_class_init, }; -static void midway_class_init(ObjectClass *oc, void *data) +static void midway_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-a15"), diff --git a/hw/arm/integratorcp.c b/hw/arm/integratorcp.c index ac0c6c6096..b1d8fbd470 100644 --- a/hw/arm/integratorcp.c +++ b/hw/arm/integratorcp.c @@ -699,7 +699,7 @@ static const Property core_properties[] = { DEFINE_PROP_UINT32("memsz", IntegratorCMState, memsz, 0), }; -static void core_class_init(ObjectClass *klass, void *data) +static void core_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -708,14 +708,14 @@ static void core_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_integratorcm; } -static void icp_pic_class_init(ObjectClass *klass, void *data) +static void icp_pic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_icp_pic; } -static void icp_control_class_init(ObjectClass *klass, void *data) +static void icp_control_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/microbit.c b/hw/arm/microbit.c index ade363daaa..525443fdb9 100644 --- a/hw/arm/microbit.c +++ b/hw/arm/microbit.c @@ -60,7 +60,7 @@ static void microbit_init(MachineState *machine) 0, s->nrf51.flash_size); } -static void microbit_machine_class_init(ObjectClass *oc, void *data) +static void microbit_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index b0633a5a69..8474549f5f 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -1267,7 +1267,7 @@ static void mps2_machine_reset(MachineState *machine, ResetType type) qemu_devices_reset(type); } -static void mps2tz_class_init(ObjectClass *oc, void *data) +static void mps2tz_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); IDAUInterfaceClass *iic = IDAU_INTERFACE_CLASS(oc); @@ -1304,7 +1304,7 @@ static void mps2tz_set_default_ram_info(MPS2TZMachineClass *mmc) g_assert_not_reached(); } -static void mps2tz_an505_class_init(ObjectClass *oc, void *data) +static void mps2tz_an505_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); @@ -1338,7 +1338,7 @@ static void mps2tz_an505_class_init(ObjectClass *oc, void *data) mps2tz_set_default_ram_info(mmc); } -static void mps2tz_an521_class_init(ObjectClass *oc, void *data) +static void mps2tz_an521_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); @@ -1372,7 +1372,7 @@ static void mps2tz_an521_class_init(ObjectClass *oc, void *data) mps2tz_set_default_ram_info(mmc); } -static void mps3tz_an524_class_init(ObjectClass *oc, void *data) +static void mps3tz_an524_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); @@ -1411,7 +1411,7 @@ static void mps3tz_an524_class_init(ObjectClass *oc, void *data) "are BRAM (default) and QSPI."); } -static void mps3tz_an547_class_init(ObjectClass *oc, void *data) +static void mps3tz_an547_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2TZMachineClass *mmc = MPS2TZ_MACHINE_CLASS(oc); diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 6958485a66..58efb41e6d 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -464,7 +464,7 @@ static void mps2_common_init(MachineState *machine) 0, 0x400000); } -static void mps2_class_init(ObjectClass *oc, void *data) +static void mps2_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -474,7 +474,7 @@ static void mps2_class_init(ObjectClass *oc, void *data) mc->default_ram_id = "mps.ram"; } -static void mps2_an385_class_init(ObjectClass *oc, void *data) +static void mps2_an385_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc); @@ -493,7 +493,7 @@ static void mps2_an385_class_init(ObjectClass *oc, void *data) mmc->has_block_ram = true; } -static void mps2_an386_class_init(ObjectClass *oc, void *data) +static void mps2_an386_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc); @@ -512,7 +512,7 @@ static void mps2_an386_class_init(ObjectClass *oc, void *data) mmc->has_block_ram = true; } -static void mps2_an500_class_init(ObjectClass *oc, void *data) +static void mps2_an500_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc); @@ -531,7 +531,7 @@ static void mps2_an500_class_init(ObjectClass *oc, void *data) mmc->has_block_ram = false; } -static void mps2_an511_class_init(ObjectClass *oc, void *data) +static void mps2_an511_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS2MachineClass *mmc = MPS2_MACHINE_CLASS(oc); diff --git a/hw/arm/mps3r.c b/hw/arm/mps3r.c index 4dd1e8a718..48c73acc62 100644 --- a/hw/arm/mps3r.c +++ b/hw/arm/mps3r.c @@ -583,14 +583,14 @@ static void mps3r_set_default_ram_info(MPS3RMachineClass *mmc) g_assert_not_reached(); } -static void mps3r_class_init(ObjectClass *oc, void *data) +static void mps3r_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->init = mps3r_common_init; } -static void mps3r_an536_class_init(ObjectClass *oc, void *data) +static void mps3r_an536_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MPS3RMachineClass *mmc = MPS3R_MACHINE_CLASS(oc); diff --git a/hw/arm/msf2-soc.c b/hw/arm/msf2-soc.c index bc9b419e37..c5e9c7175a 100644 --- a/hw/arm/msf2-soc.c +++ b/hw/arm/msf2-soc.c @@ -236,7 +236,7 @@ static const Property m2sxxx_soc_properties[] = { DEFINE_PROP_UINT8("apb1div", MSF2State, apb1div, 2), }; -static void m2sxxx_soc_class_init(ObjectClass *klass, void *data) +static void m2sxxx_soc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/musca.c b/hw/arm/musca.c index a4f43f1992..250b3b5bf8 100644 --- a/hw/arm/musca.c +++ b/hw/arm/musca.c @@ -594,7 +594,7 @@ static void musca_init(MachineState *machine) 0, 0x2000000); } -static void musca_class_init(ObjectClass *oc, void *data) +static void musca_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); static const char * const valid_cpu_types[] = { @@ -609,7 +609,7 @@ static void musca_class_init(ObjectClass *oc, void *data) mc->init = musca_init; } -static void musca_a_class_init(ObjectClass *oc, void *data) +static void musca_a_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); @@ -623,7 +623,7 @@ static void musca_a_class_init(ObjectClass *oc, void *data) mmc->num_mpcs = ARRAY_SIZE(a_mpc_info); } -static void musca_b1_class_init(ObjectClass *oc, void *data) +static void musca_b1_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MuscaMachineClass *mmc = MUSCA_MACHINE_CLASS(oc); diff --git a/hw/arm/musicpal.c b/hw/arm/musicpal.c index 48a32c2407..329b162eb2 100644 --- a/hw/arm/musicpal.c +++ b/hw/arm/musicpal.c @@ -286,7 +286,7 @@ static const VMStateDescription musicpal_lcd_vmsd = { } }; -static void musicpal_lcd_class_init(ObjectClass *klass, void *data) +static void musicpal_lcd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -407,7 +407,7 @@ static const VMStateDescription mv88w8618_pic_vmsd = { } }; -static void mv88w8618_pic_class_init(ObjectClass *klass, void *data) +static void mv88w8618_pic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -601,7 +601,7 @@ static const VMStateDescription mv88w8618_pit_vmsd = { } }; -static void mv88w8618_pit_class_init(ObjectClass *klass, void *data) +static void mv88w8618_pit_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -687,7 +687,7 @@ static const VMStateDescription mv88w8618_flashcfg_vmsd = { } }; -static void mv88w8618_flashcfg_class_init(ObjectClass *klass, void *data) +static void mv88w8618_flashcfg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1026,7 +1026,7 @@ static const VMStateDescription musicpal_gpio_vmsd = { } }; -static void musicpal_gpio_class_init(ObjectClass *klass, void *data) +static void musicpal_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1171,7 +1171,7 @@ static const VMStateDescription musicpal_key_vmsd = { } }; -static void musicpal_key_class_init(ObjectClass *klass, void *data) +static void musicpal_key_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1348,7 +1348,7 @@ static void musicpal_machine_init(MachineClass *mc) DEFINE_MACHINE("musicpal", musicpal_machine_init) -static void mv88w8618_wlan_class_init(ObjectClass *klass, void *data) +static void mv88w8618_wlan_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index 2d6e08b72b..2f30c49df5 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -821,7 +821,7 @@ static const Property npcm7xx_properties[] = { MemoryRegion *), }; -static void npcm7xx_class_init(ObjectClass *oc, void *data) +static void npcm7xx_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -830,7 +830,7 @@ static void npcm7xx_class_init(ObjectClass *oc, void *data) device_class_set_props(dc, npcm7xx_properties); } -static void npcm730_class_init(ObjectClass *oc, void *data) +static void npcm730_class_init(ObjectClass *oc, const void *data) { NPCM7xxClass *nc = NPCM7XX_CLASS(oc); @@ -839,7 +839,7 @@ static void npcm730_class_init(ObjectClass *oc, void *data) nc->num_cpus = 2; } -static void npcm750_class_init(ObjectClass *oc, void *data) +static void npcm750_class_init(ObjectClass *oc, const void *data) { NPCM7xxClass *nc = NPCM7XX_CLASS(oc); diff --git a/hw/arm/npcm7xx_boards.c b/hw/arm/npcm7xx_boards.c index eb28b97ad8..465a0e5ace 100644 --- a/hw/arm/npcm7xx_boards.c +++ b/hw/arm/npcm7xx_boards.c @@ -453,7 +453,7 @@ static void npcm7xx_set_soc_type(NPCM7xxMachineClass *nmc, const char *type) mc->default_cpus = mc->min_cpus = mc->max_cpus = sc->num_cpus; } -static void npcm7xx_machine_class_init(ObjectClass *oc, void *data) +static void npcm7xx_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); static const char * const valid_cpu_types[] = { @@ -472,7 +472,7 @@ static void npcm7xx_machine_class_init(ObjectClass *oc, void *data) * Schematics: * https://github.com/Nuvoton-Israel/nuvoton-info/blob/master/npcm7xx-poleg/evaluation-board/board_deliverables/NPCM750x_EB_ver.A1.1_COMPLETE.pdf */ -static void npcm750_evb_machine_class_init(ObjectClass *oc, void *data) +static void npcm750_evb_machine_class_init(ObjectClass *oc, const void *data) { NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); @@ -485,7 +485,7 @@ static void npcm750_evb_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 512 * MiB; }; -static void gsj_machine_class_init(ObjectClass *oc, void *data) +static void gsj_machine_class_init(ObjectClass *oc, const void *data) { NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); @@ -498,7 +498,7 @@ static void gsj_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 512 * MiB; }; -static void gbs_bmc_machine_class_init(ObjectClass *oc, void *data) +static void gbs_bmc_machine_class_init(ObjectClass *oc, const void *data) { NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); @@ -511,7 +511,7 @@ static void gbs_bmc_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 1 * GiB; } -static void kudo_bmc_machine_class_init(ObjectClass *oc, void *data) +static void kudo_bmc_machine_class_init(ObjectClass *oc, const void *data) { NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); @@ -524,7 +524,7 @@ static void kudo_bmc_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_size = 1 * GiB; }; -static void mori_bmc_machine_class_init(ObjectClass *oc, void *data) +static void mori_bmc_machine_class_init(ObjectClass *oc, const void *data) { NPCM7xxMachineClass *nmc = NPCM7XX_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/npcm8xx.c b/hw/arm/npcm8xx.c index f182accc47..5cc67b132f 100644 --- a/hw/arm/npcm8xx.c +++ b/hw/arm/npcm8xx.c @@ -779,7 +779,7 @@ static const Property npcm8xx_properties[] = { MemoryRegion *), }; -static void npcm8xx_class_init(ObjectClass *oc, void *data) +static void npcm8xx_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); NPCM8xxClass *nc = NPCM8XX_CLASS(oc); diff --git a/hw/arm/npcm8xx_boards.c b/hw/arm/npcm8xx_boards.c index 3fb8478e72..9d9f6d0c9a 100644 --- a/hw/arm/npcm8xx_boards.c +++ b/hw/arm/npcm8xx_boards.c @@ -209,7 +209,7 @@ static void npcm8xx_set_soc_type(NPCM8xxMachineClass *nmc, const char *type) mc->default_cpus = mc->min_cpus = mc->max_cpus = sc->num_cpus; } -static void npcm8xx_machine_class_init(ObjectClass *oc, void *data) +static void npcm8xx_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); static const char * const valid_cpu_types[] = { @@ -224,7 +224,7 @@ static void npcm8xx_machine_class_init(ObjectClass *oc, void *data) mc->valid_cpu_types = valid_cpu_types; } -static void npcm845_evb_machine_class_init(ObjectClass *oc, void *data) +static void npcm845_evb_machine_class_init(ObjectClass *oc, const void *data) { NPCM8xxMachineClass *nmc = NPCM8XX_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/nrf51_soc.c b/hw/arm/nrf51_soc.c index dee06ab565..d8cc3214ed 100644 --- a/hw/arm/nrf51_soc.c +++ b/hw/arm/nrf51_soc.c @@ -216,7 +216,7 @@ static const Property nrf51_soc_properties[] = { NRF51822_FLASH_SIZE), }; -static void nrf51_soc_class_init(ObjectClass *klass, void *data) +static void nrf51_soc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index aa1e96b3ad..1d89a202bb 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -202,7 +202,7 @@ static void sx1_init_v2(MachineState *machine) sx1_init(machine, 2); } -static void sx1_machine_v2_class_init(ObjectClass *oc, void *data) +static void sx1_machine_v2_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -221,7 +221,7 @@ static const TypeInfo sx1_machine_v2_type = { .class_init = sx1_machine_v2_class_init, }; -static void sx1_machine_v1_class_init(ObjectClass *oc, void *data) +static void sx1_machine_v1_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/raspi.c b/hw/arm/raspi.c index dce35ca11a..9d9af63d65 100644 --- a/hw/arm/raspi.c +++ b/hw/arm/raspi.c @@ -337,7 +337,7 @@ static void raspi_machine_class_init(MachineClass *mc, mc->init = raspi_machine_init; }; -static void raspi0_machine_class_init(ObjectClass *oc, void *data) +static void raspi0_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); @@ -347,7 +347,7 @@ static void raspi0_machine_class_init(ObjectClass *oc, void *data) raspi_machine_class_init(mc, rmc->board_rev); }; -static void raspi1ap_machine_class_init(ObjectClass *oc, void *data) +static void raspi1ap_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); @@ -357,7 +357,7 @@ static void raspi1ap_machine_class_init(ObjectClass *oc, void *data) raspi_machine_class_init(mc, rmc->board_rev); }; -static void raspi2b_machine_class_init(ObjectClass *oc, void *data) +static void raspi2b_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); @@ -368,7 +368,7 @@ static void raspi2b_machine_class_init(ObjectClass *oc, void *data) }; #ifdef TARGET_AARCH64 -static void raspi3ap_machine_class_init(ObjectClass *oc, void *data) +static void raspi3ap_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); @@ -378,7 +378,7 @@ static void raspi3ap_machine_class_init(ObjectClass *oc, void *data) raspi_machine_class_init(mc, rmc->board_rev); }; -static void raspi3b_machine_class_init(ObjectClass *oc, void *data) +static void raspi3b_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); diff --git a/hw/arm/raspi4b.c b/hw/arm/raspi4b.c index f6de103a3e..20082d5266 100644 --- a/hw/arm/raspi4b.c +++ b/hw/arm/raspi4b.c @@ -107,7 +107,7 @@ static void raspi4b_machine_init(MachineState *machine) raspi_base_machine_init(machine, &soc->parent_obj); } -static void raspi4b_machine_class_init(ObjectClass *oc, void *data) +static void raspi4b_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); RaspiBaseMachineClass *rmc = RASPI_BASE_MACHINE_CLASS(oc); diff --git a/hw/arm/realview.c b/hw/arm/realview.c index 008eeaf049..5c9050490b 100644 --- a/hw/arm/realview.c +++ b/hw/arm/realview.c @@ -413,7 +413,7 @@ static void realview_pbx_a9_init(MachineState *machine) realview_init(machine, BOARD_PBX_A9); } -static void realview_eb_class_init(ObjectClass *oc, void *data) +static void realview_eb_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -433,7 +433,7 @@ static const TypeInfo realview_eb_type = { .class_init = realview_eb_class_init, }; -static void realview_eb_mpcore_class_init(ObjectClass *oc, void *data) +static void realview_eb_mpcore_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -454,7 +454,7 @@ static const TypeInfo realview_eb_mpcore_type = { .class_init = realview_eb_mpcore_class_init, }; -static void realview_pb_a8_class_init(ObjectClass *oc, void *data) +static void realview_pb_a8_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -473,7 +473,7 @@ static const TypeInfo realview_pb_a8_type = { .class_init = realview_pb_a8_class_init, }; -static void realview_pbx_a9_class_init(ObjectClass *oc, void *data) +static void realview_pbx_a9_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index aa09d7a091..deae5cf986 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -880,7 +880,7 @@ static void sbsa_ref_instance_init(Object *obj) sbsa_flash_create(sms); } -static void sbsa_ref_class_init(ObjectClass *oc, void *data) +static void sbsa_ref_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); static const char * const valid_cpu_types[] = { diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index 1aa2eabfbd..f39b99e526 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -965,7 +965,7 @@ static const Property smmu_dev_properties[] = { TYPE_PCI_BUS, PCIBus *), }; -static void smmu_base_class_init(ObjectClass *klass, void *data) +static void smmu_base_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/arm/smmuv3.c b/hw/arm/smmuv3.c index 4362ae6aa1..ab67972353 100644 --- a/hw/arm/smmuv3.c +++ b/hw/arm/smmuv3.c @@ -1984,7 +1984,7 @@ static void smmuv3_instance_init(Object *obj) /* Nothing much to do here as of now */ } -static void smmuv3_class_init(ObjectClass *klass, void *data) +static void smmuv3_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -2031,7 +2031,7 @@ static int smmuv3_notify_flag_changed(IOMMUMemoryRegion *iommu, } static void smmuv3_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/arm/stellaris.c b/hw/arm/stellaris.c index cbe914c93e..031ea3a24e 100644 --- a/hw/arm/stellaris.c +++ b/hw/arm/stellaris.c @@ -1413,7 +1413,7 @@ static void lm3s6965evb_init(MachineState *machine) * Stellaris LM3S811 Evaluation Board Schematics: * https://www.ti.com/lit/ug/symlink/spmu030.pdf */ -static void lm3s811evb_class_init(ObjectClass *oc, void *data) +static void lm3s811evb_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1433,7 +1433,7 @@ static const TypeInfo lm3s811evb_type = { * Stellaris: LM3S6965 Evaluation Board Schematics: * https://www.ti.com/lit/ug/symlink/spmu029.pdf */ -static void lm3s6965evb_class_init(ObjectClass *oc, void *data) +static void lm3s6965evb_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1458,7 +1458,7 @@ static void stellaris_machine_init(void) type_init(stellaris_machine_init) -static void stellaris_i2c_class_init(ObjectClass *klass, void *data) +static void stellaris_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -1477,7 +1477,7 @@ static const TypeInfo stellaris_i2c_info = { .class_init = stellaris_i2c_class_init, }; -static void stellaris_adc_class_init(ObjectClass *klass, void *data) +static void stellaris_adc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -1494,7 +1494,7 @@ static const TypeInfo stellaris_adc_info = { .class_init = stellaris_adc_class_init, }; -static void stellaris_sys_class_init(ObjectClass *klass, void *data) +static void stellaris_sys_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/arm/stm32f100_soc.c b/hw/arm/stm32f100_soc.c index 0eabaf8d9b..0702d51cc3 100644 --- a/hw/arm/stm32f100_soc.c +++ b/hw/arm/stm32f100_soc.c @@ -181,7 +181,7 @@ static void stm32f100_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("CRC", 0x40023000, 0x400); } -static void stm32f100_soc_class_init(ObjectClass *klass, void *data) +static void stm32f100_soc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/stm32f205_soc.c b/hw/arm/stm32f205_soc.c index 32e96912f0..229af7fb10 100644 --- a/hw/arm/stm32f205_soc.c +++ b/hw/arm/stm32f205_soc.c @@ -202,7 +202,7 @@ static void stm32f205_soc_realize(DeviceState *dev_soc, Error **errp) } } -static void stm32f205_soc_class_init(ObjectClass *klass, void *data) +static void stm32f205_soc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/stm32f405_soc.c b/hw/arm/stm32f405_soc.c index bba9060daf..c8684e2b4c 100644 --- a/hw/arm/stm32f405_soc.c +++ b/hw/arm/stm32f405_soc.c @@ -298,7 +298,7 @@ static void stm32f405_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("RNG", 0x50060800, 0x400); } -static void stm32f405_soc_class_init(ObjectClass *klass, void *data) +static void stm32f405_soc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/stm32l4x5_soc.c b/hw/arm/stm32l4x5_soc.c index 6278d354c8..64da5559c0 100644 --- a/hw/arm/stm32l4x5_soc.c +++ b/hw/arm/stm32l4x5_soc.c @@ -435,7 +435,7 @@ static void stm32l4x5_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("QUADSPI", 0xA0001000, 0x400); } -static void stm32l4x5_soc_class_init(ObjectClass *klass, void *data) +static void stm32l4x5_soc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -446,21 +446,21 @@ static void stm32l4x5_soc_class_init(ObjectClass *klass, void *data) /* No vmstate or reset required: device has no internal state */ } -static void stm32l4x5xc_soc_class_init(ObjectClass *oc, void *data) +static void stm32l4x5xc_soc_class_init(ObjectClass *oc, const void *data) { Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); ssc->flash_size = 256 * KiB; } -static void stm32l4x5xe_soc_class_init(ObjectClass *oc, void *data) +static void stm32l4x5xe_soc_class_init(ObjectClass *oc, const void *data) { Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); ssc->flash_size = 512 * KiB; } -static void stm32l4x5xg_soc_class_init(ObjectClass *oc, void *data) +static void stm32l4x5xg_soc_class_init(ObjectClass *oc, const void *data) { Stm32l4x5SocClass *ssc = STM32L4X5_SOC_CLASS(oc); diff --git a/hw/arm/strongarm.c b/hw/arm/strongarm.c index a31f4b4c65..229c98ddd9 100644 --- a/hw/arm/strongarm.c +++ b/hw/arm/strongarm.c @@ -215,7 +215,7 @@ static const VMStateDescription vmstate_strongarm_pic_regs = { }, }; -static void strongarm_pic_class_init(ObjectClass *klass, void *data) +static void strongarm_pic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -448,7 +448,8 @@ static const VMStateDescription vmstate_strongarm_rtc_regs = { }, }; -static void strongarm_rtc_sysbus_class_init(ObjectClass *klass, void *data) +static void strongarm_rtc_sysbus_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -693,7 +694,7 @@ static const VMStateDescription vmstate_strongarm_gpio_regs = { }, }; -static void strongarm_gpio_class_init(ObjectClass *klass, void *data) +static void strongarm_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -865,7 +866,7 @@ static const VMStateDescription vmstate_strongarm_ppc_regs = { }, }; -static void strongarm_ppc_class_init(ObjectClass *klass, void *data) +static void strongarm_ppc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1336,7 +1337,7 @@ static const Property strongarm_uart_properties[] = { DEFINE_PROP_CHR("chardev", StrongARMUARTState, chr), }; -static void strongarm_uart_class_init(ObjectClass *klass, void *data) +static void strongarm_uart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1589,7 +1590,7 @@ static const VMStateDescription vmstate_strongarm_ssp_regs = { }, }; -static void strongarm_ssp_class_init(ObjectClass *klass, void *data) +static void strongarm_ssp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/versatilepb.c b/hw/arm/versatilepb.c index 35766445fa..5cf1a70d10 100644 --- a/hw/arm/versatilepb.c +++ b/hw/arm/versatilepb.c @@ -412,7 +412,7 @@ static void vab_init(MachineState *machine) versatile_init(machine, 0x25e); } -static void versatilepb_class_init(ObjectClass *oc, void *data) +static void versatilepb_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -433,7 +433,7 @@ static const TypeInfo versatilepb_type = { .class_init = versatilepb_class_init, }; -static void versatileab_class_init(ObjectClass *oc, void *data) +static void versatileab_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -462,7 +462,7 @@ static void versatile_machine_init(void) type_init(versatile_machine_init) -static void vpb_sic_class_init(ObjectClass *klass, void *data) +static void vpb_sic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/vexpress.c b/hw/arm/vexpress.c index 76c6107766..35f8d05ea1 100644 --- a/hw/arm/vexpress.c +++ b/hw/arm/vexpress.c @@ -777,7 +777,7 @@ static void vexpress_a9_instance_init(Object *obj) vms->virt = false; } -static void vexpress_class_init(ObjectClass *oc, void *data) +static void vexpress_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -795,7 +795,7 @@ static void vexpress_class_init(ObjectClass *oc, void *data) "Security Extensions (TrustZone)"); } -static void vexpress_a9_class_init(ObjectClass *oc, void *data) +static void vexpress_a9_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-a9"), @@ -811,7 +811,7 @@ static void vexpress_a9_class_init(ObjectClass *oc, void *data) vmc->daughterboard = &a9_daughterboard; } -static void vexpress_a15_class_init(ObjectClass *oc, void *data) +static void vexpress_a15_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-a15"), diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 3e72adaa91..17faf34aae 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -107,7 +107,7 @@ static void arm_virt_compat_set(MachineClass *mc) #define DEFINE_VIRT_MACHINE_IMPL(latest, ...) \ static void MACHINE_VER_SYM(class_init, virt, __VA_ARGS__)( \ ObjectClass *oc, \ - void *data) \ + const void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ arm_virt_compat_set(mc); \ @@ -3124,7 +3124,7 @@ static int virt_hvf_get_physical_address_range(MachineState *ms) return requested_ipa_size; } -static void virt_machine_class_init(ObjectClass *oc, void *data) +static void virt_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); diff --git a/hw/arm/xen-pvh.c b/hw/arm/xen-pvh.c index d1509bd235..4b26bcff7a 100644 --- a/hw/arm/xen-pvh.c +++ b/hw/arm/xen-pvh.c @@ -49,7 +49,7 @@ static void xen_pvh_set_pci_intx_irq(void *opaque, int intx_irq, int level) } } -static void xen_arm_machine_class_init(ObjectClass *oc, void *data) +static void xen_arm_machine_class_init(ObjectClass *oc, const void *data) { XenPVHMachineClass *xpc = XEN_PVH_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/xilinx_zynq.c b/hw/arm/xilinx_zynq.c index b8916665ed..0372cd0ac4 100644 --- a/hw/arm/xilinx_zynq.c +++ b/hw/arm/xilinx_zynq.c @@ -453,7 +453,7 @@ static void zynq_init(MachineState *machine) arm_load_kernel(zynq_machine->cpu[0], machine, &zynq_binfo); } -static void zynq_machine_class_init(ObjectClass *oc, void *data) +static void zynq_machine_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { ARM_CPU_TYPE_NAME("cortex-a9"), diff --git a/hw/arm/xlnx-versal-virt.c b/hw/arm/xlnx-versal-virt.c index 0c6f0359e3..adadbb7290 100644 --- a/hw/arm/xlnx-versal-virt.c +++ b/hw/arm/xlnx-versal-virt.c @@ -808,7 +808,7 @@ static void versal_virt_machine_finalize(Object *obj) g_free(s->ospi_model); } -static void versal_virt_machine_class_init(ObjectClass *oc, void *data) +static void versal_virt_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/xlnx-versal.c b/hw/arm/xlnx-versal.c index f0b383b29e..a42b9e7140 100644 --- a/hw/arm/xlnx-versal.c +++ b/hw/arm/xlnx-versal.c @@ -975,7 +975,7 @@ static const Property versal_properties[] = { TYPE_CAN_BUS, CanBusState *), }; -static void versal_class_init(ObjectClass *klass, void *data) +static void versal_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/arm/xlnx-zcu102.c b/hw/arm/xlnx-zcu102.c index 4fdb153e4d..14b6641a71 100644 --- a/hw/arm/xlnx-zcu102.c +++ b/hw/arm/xlnx-zcu102.c @@ -267,7 +267,7 @@ static void xlnx_zcu102_machine_instance_init(Object *obj) 0); } -static void xlnx_zcu102_machine_class_init(ObjectClass *oc, void *data) +static void xlnx_zcu102_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c index ec2b3a41ed..ec96a46eec 100644 --- a/hw/arm/xlnx-zynqmp.c +++ b/hw/arm/xlnx-zynqmp.c @@ -853,7 +853,7 @@ static const Property xlnx_zynqmp_props[] = { CanBusState *), }; -static void xlnx_zynqmp_class_init(ObjectClass *oc, void *data) +static void xlnx_zynqmp_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index 05c573776e..7454cc60de 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -1328,7 +1328,7 @@ static const Property ac97_properties[] = { DEFINE_AUDIO_PROPERTIES(AC97LinkState, card), }; -static void ac97_class_init(ObjectClass *klass, void *data) +static void ac97_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/audio/adlib.c b/hw/audio/adlib.c index 8c9767b537..1f29a7e319 100644 --- a/hw/audio/adlib.c +++ b/hw/audio/adlib.c @@ -303,7 +303,7 @@ static const Property adlib_properties[] = { DEFINE_PROP_UINT32 ("freq", AdlibState, freq, 44100), }; -static void adlib_class_initfn (ObjectClass *klass, void *data) +static void adlib_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS (klass); diff --git a/hw/audio/asc.c b/hw/audio/asc.c index cea7a1c053..18382ccf6a 100644 --- a/hw/audio/asc.c +++ b/hw/audio/asc.c @@ -699,7 +699,7 @@ static const Property asc_properties[] = { DEFINE_PROP_UINT8("asctype", ASCState, type, ASC_TYPE_ASC), }; -static void asc_class_init(ObjectClass *oc, void *data) +static void asc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ResettableClass *rc = RESETTABLE_CLASS(oc); diff --git a/hw/audio/cs4231.c b/hw/audio/cs4231.c index 8321f89c88..97cceb44d8 100644 --- a/hw/audio/cs4231.c +++ b/hw/audio/cs4231.c @@ -160,7 +160,7 @@ static void cs4231_init(Object *obj) sysbus_init_irq(dev, &s->irq); } -static void cs4231_class_init(ObjectClass *klass, void *data) +static void cs4231_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index 5a9be80ba3..06b44da869 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -696,7 +696,7 @@ static const Property cs4231a_properties[] = { DEFINE_PROP_UINT32 ("dma", CSState, dma, 3), }; -static void cs4231a_class_initfn (ObjectClass *klass, void *data) +static void cs4231a_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS (klass); diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 75f71e5d78..322b779814 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -872,7 +872,7 @@ static const Property es1370_properties[] = { DEFINE_AUDIO_PROPERTIES(ES1370State, card), }; -static void es1370_class_init (ObjectClass *klass, void *data) +static void es1370_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS (klass); PCIDeviceClass *k = PCI_DEVICE_CLASS (klass); diff --git a/hw/audio/gus.c b/hw/audio/gus.c index e718c1183e..87e8634893 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -298,7 +298,7 @@ static const Property gus_properties[] = { DEFINE_PROP_UINT32 ("dma", GUSState, emu.gusdma, 3), }; -static void gus_class_initfn (ObjectClass *klass, void *data) +static void gus_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS (klass); diff --git a/hw/audio/hda-codec.c b/hw/audio/hda-codec.c index 6f3a8f691b..66edad280f 100644 --- a/hw/audio/hda-codec.c +++ b/hw/audio/hda-codec.c @@ -900,7 +900,7 @@ static void hda_audio_init_micro(HDACodecDevice *hda, Error **errp) hda_audio_init(hda, desc, errp); } -static void hda_audio_base_class_init(ObjectClass *klass, void *data) +static void hda_audio_base_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); @@ -922,7 +922,7 @@ static const TypeInfo hda_audio_info = { .abstract = true, }; -static void hda_audio_output_class_init(ObjectClass *klass, void *data) +static void hda_audio_output_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); @@ -937,7 +937,7 @@ static const TypeInfo hda_audio_output_info = { .class_init = hda_audio_output_class_init, }; -static void hda_audio_duplex_class_init(ObjectClass *klass, void *data) +static void hda_audio_duplex_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); @@ -952,7 +952,7 @@ static const TypeInfo hda_audio_duplex_info = { .class_init = hda_audio_duplex_class_init, }; -static void hda_audio_micro_class_init(ObjectClass *klass, void *data) +static void hda_audio_micro_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); HDACodecDeviceClass *k = HDA_CODEC_DEVICE_CLASS(klass); diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 3214992ddc..2f1b08e9c1 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -1220,7 +1220,7 @@ static const Property intel_hda_properties[] = { DEFINE_PROP_BOOL("old_msi_addr", IntelHDAState, old_msi_addr, false), }; -static void intel_hda_class_init(ObjectClass *klass, void *data) +static void intel_hda_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1234,7 +1234,7 @@ static void intel_hda_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, intel_hda_properties); } -static void intel_hda_class_init_ich6(ObjectClass *klass, void *data) +static void intel_hda_class_init_ich6(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1245,7 +1245,7 @@ static void intel_hda_class_init_ich6(ObjectClass *klass, void *data) dc->desc = "Intel HD Audio Controller (ich6)"; } -static void intel_hda_class_init_ich9(ObjectClass *klass, void *data) +static void intel_hda_class_init_ich9(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1280,7 +1280,7 @@ static const TypeInfo intel_hda_info_ich9 = { .class_init = intel_hda_class_init_ich9, }; -static void hda_codec_device_class_init(ObjectClass *klass, void *data) +static void hda_codec_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = hda_codec_dev_realize; diff --git a/hw/audio/marvell_88w8618.c b/hw/audio/marvell_88w8618.c index 28f9af320d..6d3ebbb0c8 100644 --- a/hw/audio/marvell_88w8618.c +++ b/hw/audio/marvell_88w8618.c @@ -287,7 +287,7 @@ static const VMStateDescription mv88w8618_audio_vmsd = { } }; -static void mv88w8618_audio_class_init(ObjectClass *klass, void *data) +static void mv88w8618_audio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/audio/pcspk.c b/hw/audio/pcspk.c index 17be185547..a419161b5b 100644 --- a/hw/audio/pcspk.c +++ b/hw/audio/pcspk.c @@ -221,7 +221,7 @@ static const Property pcspk_properties[] = { DEFINE_PROP_BOOL("migrate", PCSpkState, migrate, true), }; -static void pcspk_class_initfn(ObjectClass *klass, void *data) +static void pcspk_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/audio/pl041.c b/hw/audio/pl041.c index f771d725fa..5d9d6c1178 100644 --- a/hw/audio/pl041.c +++ b/hw/audio/pl041.c @@ -632,7 +632,7 @@ static const Property pl041_device_properties[] = { DEFAULT_FIFO_DEPTH), }; -static void pl041_device_class_init(ObjectClass *klass, void *data) +static void pl041_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index 0c661b4947..19fd3b9020 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -1449,7 +1449,7 @@ static const Property sb16_properties[] = { DEFINE_PROP_UINT32 ("dma16", SB16State, hdma, 5), }; -static void sb16_class_initfn (ObjectClass *klass, void *data) +static void sb16_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS (klass); diff --git a/hw/audio/via-ac97.c b/hw/audio/via-ac97.c index 4e115e011e..5feef663d8 100644 --- a/hw/audio/via-ac97.c +++ b/hw/audio/via-ac97.c @@ -463,7 +463,7 @@ static const Property via_ac97_properties[] = { DEFINE_AUDIO_PROPERTIES(ViaAC97State, card), }; -static void via_ac97_class_init(ObjectClass *klass, void *data) +static void via_ac97_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -501,7 +501,7 @@ static void via_mc97_realize(PCIDevice *pci_dev, Error **errp) pci_set_long(pci_dev->config + PCI_INTERRUPT_PIN, 0x03); } -static void via_mc97_class_init(ObjectClass *klass, void *data) +static void via_mc97_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/audio/virtio-snd-pci.c b/hw/audio/virtio-snd-pci.c index 74d93f4e9c..9eb0007392 100644 --- a/hw/audio/virtio-snd-pci.c +++ b/hw/audio/virtio-snd-pci.c @@ -42,7 +42,7 @@ static void virtio_snd_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_snd_pci_class_init(ObjectClass *klass, void *data) +static void virtio_snd_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *vpciklass = VIRTIO_PCI_CLASS(klass); diff --git a/hw/audio/virtio-snd.c b/hw/audio/virtio-snd.c index 0b47741f01..eca3319e59 100644 --- a/hw/audio/virtio-snd.c +++ b/hw/audio/virtio-snd.c @@ -1361,7 +1361,7 @@ static void virtio_snd_reset(VirtIODevice *vdev) } } -static void virtio_snd_class_init(ObjectClass *klass, void *data) +static void virtio_snd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/audio/wm8750.c b/hw/audio/wm8750.c index 8d381dbc65..2846b55fe2 100644 --- a/hw/audio/wm8750.c +++ b/hw/audio/wm8750.c @@ -710,7 +710,7 @@ static const Property wm8750_properties[] = { DEFINE_AUDIO_PROPERTIES(WM8750State, card), }; -static void wm8750_class_init(ObjectClass *klass, void *data) +static void wm8750_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); diff --git a/hw/avr/arduino.c b/hw/avr/arduino.c index 48ef478346..e166ca18e1 100644 --- a/hw/avr/arduino.c +++ b/hw/avr/arduino.c @@ -56,7 +56,7 @@ static void arduino_machine_init(MachineState *machine) } } -static void arduino_machine_class_init(ObjectClass *oc, void *data) +static void arduino_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -69,7 +69,7 @@ static void arduino_machine_class_init(ObjectClass *oc, void *data) mc->no_parallel = 1; } -static void arduino_duemilanove_class_init(ObjectClass *oc, void *data) +static void arduino_duemilanove_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc); @@ -84,7 +84,7 @@ static void arduino_duemilanove_class_init(ObjectClass *oc, void *data) amc->xtal_hz = 16 * 1000 * 1000; }; -static void arduino_uno_class_init(ObjectClass *oc, void *data) +static void arduino_uno_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc); @@ -99,7 +99,7 @@ static void arduino_uno_class_init(ObjectClass *oc, void *data) amc->xtal_hz = 16 * 1000 * 1000; }; -static void arduino_mega_class_init(ObjectClass *oc, void *data) +static void arduino_mega_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc); @@ -114,7 +114,7 @@ static void arduino_mega_class_init(ObjectClass *oc, void *data) amc->xtal_hz = 16 * 1000 * 1000; }; -static void arduino_mega2560_class_init(ObjectClass *oc, void *data) +static void arduino_mega2560_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); ArduinoMachineClass *amc = ARDUINO_MACHINE_CLASS(oc); diff --git a/hw/avr/atmega.c b/hw/avr/atmega.c index c105d2a97c..95b6da5e34 100644 --- a/hw/avr/atmega.c +++ b/hw/avr/atmega.c @@ -386,7 +386,7 @@ static const Property atmega_props[] = { xtal_freq_hz, 0), }; -static void atmega_class_init(ObjectClass *oc, void *data) +static void atmega_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -396,7 +396,7 @@ static void atmega_class_init(ObjectClass *oc, void *data) dc->user_creatable = false; } -static void atmega168_class_init(ObjectClass *oc, void *data) +static void atmega168_class_init(ObjectClass *oc, const void *data) { AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); @@ -411,7 +411,7 @@ static void atmega168_class_init(ObjectClass *oc, void *data) amc->dev = dev168_328; }; -static void atmega328_class_init(ObjectClass *oc, void *data) +static void atmega328_class_init(ObjectClass *oc, const void *data) { AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); @@ -426,7 +426,7 @@ static void atmega328_class_init(ObjectClass *oc, void *data) amc->dev = dev168_328; }; -static void atmega1280_class_init(ObjectClass *oc, void *data) +static void atmega1280_class_init(ObjectClass *oc, const void *data) { AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); @@ -441,7 +441,7 @@ static void atmega1280_class_init(ObjectClass *oc, void *data) amc->dev = dev1280_2560; }; -static void atmega2560_class_init(ObjectClass *oc, void *data) +static void atmega2560_class_init(ObjectClass *oc, const void *data) { AtmegaMcuClass *amc = ATMEGA_MCU_CLASS(oc); diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c index 561cfa47c1..fbba2ab629 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -298,7 +298,7 @@ static const Property isa_fdc_properties[] = { FloppyDriveType), }; -static void isabus_fdc_class_init(ObjectClass *klass, void *data) +static void isabus_fdc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); diff --git a/hw/block/fdc-sysbus.c b/hw/block/fdc-sysbus.c index 4955e478cd..956860ab29 100644 --- a/hw/block/fdc-sysbus.c +++ b/hw/block/fdc-sysbus.c @@ -176,7 +176,7 @@ static const VMStateDescription vmstate_sysbus_fdc = { } }; -static void sysbus_fdc_common_class_init(ObjectClass *klass, void *data) +static void sysbus_fdc_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -208,7 +208,7 @@ static const Property sysbus_fdc_properties[] = { FloppyDriveType), }; -static void sysbus_fdc_class_init(ObjectClass *klass, void *data) +static void sysbus_fdc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -231,7 +231,7 @@ static const Property sun4m_fdc_properties[] = { FloppyDriveType), }; -static void sun4m_fdc_class_init(ObjectClass *klass, void *data) +static void sun4m_fdc_class_init(ObjectClass *klass, const void *data) { FDCtrlSysBusClass *sbdc = SYSBUS_FDC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/block/fdc.c b/hw/block/fdc.c index 2df941d3f4..d0f08c7be5 100644 --- a/hw/block/fdc.c +++ b/hw/block/fdc.c @@ -553,7 +553,7 @@ static void floppy_drive_realize(DeviceState *qdev, Error **errp) fd_revalidate(drive); } -static void floppy_drive_class_init(ObjectClass *klass, void *data) +static void floppy_drive_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = floppy_drive_realize; diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 0887c103e4..75b9d71251 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -528,7 +528,7 @@ struct Flash { struct M25P80Class { SSIPeripheralClass parent_class; - FlashPartInfo *pi; + const FlashPartInfo *pi; }; OBJECT_DECLARE_TYPE(Flash, M25P80Class, M25P80) @@ -1857,7 +1857,7 @@ static const VMStateDescription vmstate_m25p80 = { } }; -static void m25p80_class_init(ObjectClass *klass, void *data) +static void m25p80_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); diff --git a/hw/block/nand.c b/hw/block/nand.c index e98c55b729..c80bf78fe5 100644 --- a/hw/block/nand.c +++ b/hw/block/nand.c @@ -451,7 +451,7 @@ static const Property nand_properties[] = { DEFINE_PROP_DRIVE("drive", NANDFlashState, blk), }; -static void nand_class_init(ObjectClass *klass, void *data) +static void nand_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/block/pflash_cfi01.c b/hw/block/pflash_cfi01.c index b5ea927f36..168101d8df 100644 --- a/hw/block/pflash_cfi01.c +++ b/hw/block/pflash_cfi01.c @@ -934,7 +934,7 @@ static const Property pflash_cfi01_properties[] = { old_multiple_chip_handling, false), }; -static void pflash_cfi01_class_init(ObjectClass *klass, void *data) +static void pflash_cfi01_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/block/pflash_cfi02.c b/hw/block/pflash_cfi02.c index 315a53629a..3244b699b9 100644 --- a/hw/block/pflash_cfi02.c +++ b/hw/block/pflash_cfi02.c @@ -968,7 +968,7 @@ static void pflash_cfi02_unrealize(DeviceState *dev) g_free(pfl->sector_erase_map); } -static void pflash_cfi02_class_init(ObjectClass *klass, void *data) +static void pflash_cfi02_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/block/swim.c b/hw/block/swim.c index 4645468dcf..ad047362f8 100644 --- a/hw/block/swim.c +++ b/hw/block/swim.c @@ -253,7 +253,7 @@ static void swim_drive_realize(DeviceState *qdev, Error **errp) blk_set_dev_ops(drive->blk, &swim_block_ops, drive); } -static void swim_drive_class_init(ObjectClass *klass, void *data) +static void swim_drive_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = swim_drive_realize; @@ -550,7 +550,7 @@ static const VMStateDescription vmstate_sysbus_swim = { } }; -static void sysbus_swim_class_init(ObjectClass *oc, void *data) +static void sysbus_swim_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index ae42327cf8..4bb5ed299e 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -583,7 +583,7 @@ static const Property vhost_user_blk_properties[] = { VIRTIO_BLK_F_WRITE_ZEROES, true), }; -static void vhost_user_blk_class_init(ObjectClass *klass, void *data) +static void vhost_user_blk_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index 5077793e5e..b54d01d3a2 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -1886,7 +1886,7 @@ static const Property virtio_blk_properties[] = { conf.x_enable_wce_if_config_wce, true), }; -static void virtio_blk_class_init(ObjectClass *klass, void *data) +static void virtio_blk_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/block/xen-block.c b/hw/block/xen-block.c index ec04102b66..74de897c79 100644 --- a/hw/block/xen-block.c +++ b/hw/block/xen-block.c @@ -679,7 +679,7 @@ static const Property xen_block_props[] = { TYPE_IOTHREAD, IOThread *), }; -static void xen_block_class_init(ObjectClass *class, void *data) +static void xen_block_class_init(ObjectClass *class, const void *data) { DeviceClass *dev_class = DEVICE_CLASS(class); XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class); @@ -724,7 +724,7 @@ static void xen_disk_realize(XenBlockDevice *blockdev, Error **errp) blockdev->info = blk_supports_write_perm(conf->blk) ? 0 : VDISK_READONLY; } -static void xen_disk_class_init(ObjectClass *class, void *data) +static void xen_disk_class_init(ObjectClass *class, const void *data) { DeviceClass *dev_class = DEVICE_CLASS(class); XenBlockDeviceClass *blockdev_class = XEN_BLOCK_DEVICE_CLASS(class); @@ -771,7 +771,7 @@ static void xen_cdrom_realize(XenBlockDevice *blockdev, Error **errp) blockdev->info = VDISK_READONLY | VDISK_CDROM; } -static void xen_cdrom_class_init(ObjectClass *class, void *data) +static void xen_cdrom_class_init(ObjectClass *class, const void *data) { DeviceClass *dev_class = DEVICE_CLASS(class); XenBlockDeviceClass *blockdev_class = XEN_BLOCK_DEVICE_CLASS(class); diff --git a/hw/char/avr_usart.c b/hw/char/avr_usart.c index e8012cae3a..fae15217e9 100644 --- a/hw/char/avr_usart.c +++ b/hw/char/avr_usart.c @@ -295,7 +295,7 @@ static void avr_usart_realize(DeviceState *dev, Error **errp) avr_usart_reset(dev); } -static void avr_usart_class_init(ObjectClass *klass, void *data) +static void avr_usart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/bcm2835_aux.c b/hw/char/bcm2835_aux.c index 9b073fc330..2b397f2ff3 100644 --- a/hw/char/bcm2835_aux.c +++ b/hw/char/bcm2835_aux.c @@ -296,7 +296,7 @@ static const Property bcm2835_aux_props[] = { DEFINE_PROP_CHR("chardev", BCM2835AuxState, chr), }; -static void bcm2835_aux_class_init(ObjectClass *oc, void *data) +static void bcm2835_aux_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/char/cadence_uart.c b/hw/char/cadence_uart.c index ebd846a083..0dfa356b6d 100644 --- a/hw/char/cadence_uart.c +++ b/hw/char/cadence_uart.c @@ -621,7 +621,7 @@ static const Property cadence_uart_properties[] = { DEFINE_PROP_CHR("chardev", CadenceUARTState, chr), }; -static void cadence_uart_class_init(ObjectClass *klass, void *data) +static void cadence_uart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/char/cmsdk-apb-uart.c b/hw/char/cmsdk-apb-uart.c index 0506500215..32090f3516 100644 --- a/hw/char/cmsdk-apb-uart.c +++ b/hw/char/cmsdk-apb-uart.c @@ -382,7 +382,7 @@ static const Property cmsdk_apb_uart_properties[] = { DEFINE_PROP_UINT32("pclk-frq", CMSDKAPBUART, pclk_frq, 0), }; -static void cmsdk_apb_uart_class_init(ObjectClass *klass, void *data) +static void cmsdk_apb_uart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/debugcon.c b/hw/char/debugcon.c index 1bc3bf85fe..bf44aaf9e4 100644 --- a/hw/char/debugcon.c +++ b/hw/char/debugcon.c @@ -120,7 +120,7 @@ static const Property debugcon_isa_properties[] = { DEFINE_PROP_UINT32("readback", ISADebugconState, state.readback, 0xe9), }; -static void debugcon_isa_class_initfn(ObjectClass *klass, void *data) +static void debugcon_isa_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/digic-uart.c b/hw/char/digic-uart.c index b0b0714e0f..0f6af51bb7 100644 --- a/hw/char/digic-uart.c +++ b/hw/char/digic-uart.c @@ -176,7 +176,7 @@ static const Property digic_uart_properties[] = { DEFINE_PROP_CHR("chardev", DigicUartState, chr), }; -static void digic_uart_class_init(ObjectClass *klass, void *data) +static void digic_uart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/diva-gsp.c b/hw/char/diva-gsp.c index ecec1f7bb1..9a623d680b 100644 --- a/hw/char/diva-gsp.c +++ b/hw/char/diva-gsp.c @@ -183,7 +183,7 @@ static const Property diva_serial_properties[] = { PCI_DEVICE_ID_HP_DIVA_TOSCA1), }; -static void diva_serial_class_initfn(ObjectClass *klass, void *data) +static void diva_serial_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); @@ -242,7 +242,7 @@ static void diva_aux_exit(PCIDevice *dev) qemu_free_irq(pci->irq); } -static void diva_aux_class_initfn(ObjectClass *klass, void *data) +static void diva_aux_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); diff --git a/hw/char/escc.c b/hw/char/escc.c index a5fdd8f698..afe4ca483e 100644 --- a/hw/char/escc.c +++ b/hw/char/escc.c @@ -1101,7 +1101,7 @@ static const Property escc_properties[] = { DEFINE_PROP_STRING("chnA-sunkbd-layout", ESCCState, chn[1].sunkbd_layout), }; -static void escc_class_init(ObjectClass *klass, void *data) +static void escc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/exynos4210_uart.c b/hw/char/exynos4210_uart.c index a1a9a12caf..6521b4cedd 100644 --- a/hw/char/exynos4210_uart.c +++ b/hw/char/exynos4210_uart.c @@ -711,7 +711,7 @@ static const Property exynos4210_uart_properties[] = { DEFINE_PROP_UINT32("tx-size", Exynos4210UartState, tx.size, 16), }; -static void exynos4210_uart_class_init(ObjectClass *klass, void *data) +static void exynos4210_uart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/goldfish_tty.c b/hw/char/goldfish_tty.c index f0891ffa4d..a37408adae 100644 --- a/hw/char/goldfish_tty.c +++ b/hw/char/goldfish_tty.c @@ -256,7 +256,7 @@ static void goldfish_tty_instance_init(Object *obj) sysbus_init_irq(dev, &s->irq); } -static void goldfish_tty_class_init(ObjectClass *oc, void *data) +static void goldfish_tty_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/char/grlib_apbuart.c b/hw/char/grlib_apbuart.c index db6bcdad41..81c26e3389 100644 --- a/hw/char/grlib_apbuart.c +++ b/hw/char/grlib_apbuart.c @@ -281,7 +281,7 @@ static const Property grlib_apbuart_properties[] = { DEFINE_PROP_CHR("chrdev", UART, chr), }; -static void grlib_apbuart_class_init(ObjectClass *klass, void *data) +static void grlib_apbuart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/ibex_uart.c b/hw/char/ibex_uart.c index 392375ad55..d6f0d18c77 100644 --- a/hw/char/ibex_uart.c +++ b/hw/char/ibex_uart.c @@ -542,7 +542,7 @@ static void ibex_uart_realize(DeviceState *dev, Error **errp) s, NULL, true); } -static void ibex_uart_class_init(ObjectClass *klass, void *data) +static void ibex_uart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/imx_serial.c b/hw/char/imx_serial.c index 6f14f8403a..509b0141d0 100644 --- a/hw/char/imx_serial.c +++ b/hw/char/imx_serial.c @@ -467,7 +467,7 @@ static const Property imx_serial_properties[] = { DEFINE_PROP_CHR("chardev", IMXSerialState, chr), }; -static void imx_serial_class_init(ObjectClass *klass, void *data) +static void imx_serial_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/ipoctal232.c b/hw/char/ipoctal232.c index a2879977fb..752c6c818a 100644 --- a/hw/char/ipoctal232.c +++ b/hw/char/ipoctal232.c @@ -569,7 +569,7 @@ static const Property ipoctal_properties[] = { DEFINE_PROP_CHR("chardev7", IPOctalState, ch[7].dev), }; -static void ipoctal_class_init(ObjectClass *klass, void *data) +static void ipoctal_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IPackDeviceClass *ic = IPACK_DEVICE_CLASS(klass); diff --git a/hw/char/mcf_uart.c b/hw/char/mcf_uart.c index 529c26be93..87bfcbebdc 100644 --- a/hw/char/mcf_uart.c +++ b/hw/char/mcf_uart.c @@ -322,7 +322,7 @@ static const Property mcf_uart_properties[] = { DEFINE_PROP_CHR("chardev", mcf_uart_state, chr), }; -static void mcf_uart_class_init(ObjectClass *oc, void *data) +static void mcf_uart_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/char/mchp_pfsoc_mmuart.c b/hw/char/mchp_pfsoc_mmuart.c index 3c3224c05d..6149f9d204 100644 --- a/hw/char/mchp_pfsoc_mmuart.c +++ b/hw/char/mchp_pfsoc_mmuart.c @@ -121,7 +121,7 @@ static const VMStateDescription mchp_pfsoc_mmuart_vmstate = { } }; -static void mchp_pfsoc_mmuart_class_init(ObjectClass *oc, void *data) +static void mchp_pfsoc_mmuart_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/char/nrf51_uart.c b/hw/char/nrf51_uart.c index 82a61ee95f..41d423446f 100644 --- a/hw/char/nrf51_uart.c +++ b/hw/char/nrf51_uart.c @@ -308,7 +308,7 @@ static const Property nrf51_uart_properties[] = { DEFINE_PROP_CHR("chardev", NRF51UARTState, chr), }; -static void nrf51_uart_class_init(ObjectClass *klass, void *data) +static void nrf51_uart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/parallel.c b/hw/char/parallel.c index e1651d52a4..217ddaf2e3 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -610,7 +610,7 @@ static const Property parallel_isa_properties[] = { DEFINE_PROP_CHR("chardev", ISAParallelState, state.chr), }; -static void parallel_isa_class_initfn(ObjectClass *klass, void *data) +static void parallel_isa_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); diff --git a/hw/char/pl011.c b/hw/char/pl011.c index 0e9ec1301d..01335d9437 100644 --- a/hw/char/pl011.c +++ b/hw/char/pl011.c @@ -669,7 +669,7 @@ static void pl011_reset(DeviceState *dev) pl011_reset_tx_fifo(s); } -static void pl011_class_init(ObjectClass *oc, void *data) +static void pl011_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/char/renesas_sci.c b/hw/char/renesas_sci.c index ea94494932..b9d0ed1c89 100644 --- a/hw/char/renesas_sci.c +++ b/hw/char/renesas_sci.c @@ -324,7 +324,7 @@ static const Property rsci_properties[] = { DEFINE_PROP_CHR("chardev", RSCIState, chr), }; -static void rsci_class_init(ObjectClass *klass, void *data) +static void rsci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index ddb9a726d5..e9580aacba 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -339,7 +339,7 @@ static const Property console_properties[] = { DEFINE_PROP_BOOL("echo", SCLPConsoleLM, echo, true), }; -static void console_class_init(ObjectClass *klass, void *data) +static void console_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); diff --git a/hw/char/sclpconsole.c b/hw/char/sclpconsole.c index 01233b933d..95e3045178 100644 --- a/hw/char/sclpconsole.c +++ b/hw/char/sclpconsole.c @@ -255,7 +255,7 @@ static const Property console_properties[] = { DEFINE_PROP_CHR("chardev", SCLPConsole, chr), }; -static void console_class_init(ObjectClass *klass, void *data) +static void console_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCLPEventClass *ec = SCLP_EVENT_CLASS(klass); diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index 3d913891dc..fe7fb1625b 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -119,7 +119,7 @@ static const Property serial_isa_properties[] = { DEFINE_PROP_UINT32("irq", ISASerialState, isairq, -1), }; -static void serial_isa_class_initfn(ObjectClass *klass, void *data) +static void serial_isa_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); diff --git a/hw/char/serial-mm.c b/hw/char/serial-mm.c index 6338e7c0ba..13aba780ec 100644 --- a/hw/char/serial-mm.c +++ b/hw/char/serial-mm.c @@ -134,7 +134,7 @@ static const Property serial_mm_properties[] = { DEFINE_PROP_UINT8("endianness", SerialMM, endianness, DEVICE_NATIVE_ENDIAN), }; -static void serial_mm_class_init(ObjectClass *oc, void *data) +static void serial_mm_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/char/serial-pci-multi.c b/hw/char/serial-pci-multi.c index 718ae25131..ee1c0f7dc4 100644 --- a/hw/char/serial-pci-multi.c +++ b/hw/char/serial-pci-multi.c @@ -144,7 +144,8 @@ static const Property multi_4x_serial_pci_properties[] = { DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), }; -static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data) +static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); @@ -159,7 +160,8 @@ static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } -static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, void *data) +static void multi_4x_serial_pci_class_initfn(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c index 6659cef5d4..bd38c7428c 100644 --- a/hw/char/serial-pci.c +++ b/hw/char/serial-pci.c @@ -85,7 +85,7 @@ static const Property serial_pci_properties[] = { DEFINE_PROP_UINT8("prog_if", PCISerialState, prog_if, 0x02), }; -static void serial_pci_class_initfn(ObjectClass *klass, void *data) +static void serial_pci_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); diff --git a/hw/char/serial.c b/hw/char/serial.c index 70044e14a0..03fec3fe75 100644 --- a/hw/char/serial.c +++ b/hw/char/serial.c @@ -970,7 +970,7 @@ static const Property serial_properties[] = { DEFINE_PROP_BOOL("wakeup", SerialState, wakeup, false), }; -static void serial_class_init(ObjectClass *klass, void* data) +static void serial_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 41c8175a63..6abd80386f 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -450,7 +450,7 @@ static const Property sh_serial_properties[] = { DEFINE_PROP_UINT8("features", SHSerialState, feat, 0), }; -static void sh_serial_class_init(ObjectClass *oc, void *data) +static void sh_serial_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/char/shakti_uart.c b/hw/char/shakti_uart.c index 09975d9d34..6e216edb0f 100644 --- a/hw/char/shakti_uart.c +++ b/hw/char/shakti_uart.c @@ -161,7 +161,7 @@ static const Property shakti_uart_properties[] = { DEFINE_PROP_CHR("chardev", ShaktiUartState, chr), }; -static void shakti_uart_class_init(ObjectClass *klass, void *data) +static void shakti_uart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_legacy_reset(dc, shakti_uart_reset); diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index b45e6c098c..0fc89e76d1 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -334,7 +334,7 @@ static const VMStateDescription vmstate_sifive_uart = { }; -static void sifive_uart_class_init(ObjectClass *oc, void *data) +static void sifive_uart_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ResettableClass *rc = RESETTABLE_CLASS(oc); diff --git a/hw/char/spapr_vty.c b/hw/char/spapr_vty.c index 6451d010ac..fc8ea604f8 100644 --- a/hw/char/spapr_vty.c +++ b/hw/char/spapr_vty.c @@ -182,7 +182,7 @@ static const VMStateDescription vmstate_spapr_vty = { }, }; -static void spapr_vty_class_init(ObjectClass *klass, void *data) +static void spapr_vty_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); diff --git a/hw/char/stm32f2xx_usart.c b/hw/char/stm32f2xx_usart.c index 87882daa71..45c30643a7 100644 --- a/hw/char/stm32f2xx_usart.c +++ b/hw/char/stm32f2xx_usart.c @@ -220,7 +220,7 @@ static void stm32f2xx_usart_realize(DeviceState *dev, Error **errp) s, NULL, true); } -static void stm32f2xx_usart_class_init(ObjectClass *klass, void *data) +static void stm32f2xx_usart_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/char/stm32l4x5_usart.c b/hw/char/stm32l4x5_usart.c index bcc310bd97..afbe4bab29 100644 --- a/hw/char/stm32l4x5_usart.c +++ b/hw/char/stm32l4x5_usart.c @@ -594,7 +594,8 @@ static void stm32l4x5_usart_base_realize(DeviceState *dev, Error **errp) s, NULL, true); } -static void stm32l4x5_usart_base_class_init(ObjectClass *klass, void *data) +static void stm32l4x5_usart_base_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -605,21 +606,21 @@ static void stm32l4x5_usart_base_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_stm32l4x5_usart_base; } -static void stm32l4x5_usart_class_init(ObjectClass *oc, void *data) +static void stm32l4x5_usart_class_init(ObjectClass *oc, const void *data) { Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); subc->type = STM32L4x5_USART; } -static void stm32l4x5_uart_class_init(ObjectClass *oc, void *data) +static void stm32l4x5_uart_class_init(ObjectClass *oc, const void *data) { Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); subc->type = STM32L4x5_UART; } -static void stm32l4x5_lpuart_class_init(ObjectClass *oc, void *data) +static void stm32l4x5_lpuart_class_init(ObjectClass *oc, const void *data) { Stm32l4x5UsartBaseClass *subc = STM32L4X5_USART_BASE_CLASS(oc); diff --git a/hw/char/terminal3270.c b/hw/char/terminal3270.c index 04ee26dcbd..d950c17292 100644 --- a/hw/char/terminal3270.c +++ b/hw/char/terminal3270.c @@ -292,7 +292,7 @@ static const VMStateDescription terminal3270_vmstate = { .unmigratable = 1, }; -static void terminal_class_init(ObjectClass *klass, void *data) +static void terminal_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); EmulatedCcw3270Class *ck = EMULATED_CCW_3270_CLASS(klass); diff --git a/hw/char/virtio-console.c b/hw/char/virtio-console.c index aa6d611a47..0932a3572b 100644 --- a/hw/char/virtio-console.c +++ b/hw/char/virtio-console.c @@ -261,7 +261,7 @@ static void virtconsole_unrealize(DeviceState *dev) } } -static void virtconsole_class_init(ObjectClass *klass, void *data) +static void virtconsole_class_init(ObjectClass *klass, const void *data) { VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass); @@ -278,7 +278,7 @@ static const Property virtserialport_properties[] = { DEFINE_PROP_CHR("chardev", VirtConsole, chr), }; -static void virtserialport_class_init(ObjectClass *klass, void *data) +static void virtserialport_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOSerialPortClass *k = VIRTIO_SERIAL_PORT_CLASS(klass); diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index b6d2743a9c..00572873d2 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -840,7 +840,7 @@ static const Property virtser_props[] = { DEFINE_PROP_STRING("name", VirtIOSerialPort, name), }; -static void virtser_bus_class_init(ObjectClass *klass, void *data) +static void virtser_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); k->print_dev = virtser_bus_dev_print; @@ -1092,7 +1092,7 @@ static void virtio_serial_device_realize(DeviceState *dev, Error **errp) QLIST_INSERT_HEAD(&vserdevices.devices, vser, next); } -static void virtio_serial_port_class_init(ObjectClass *klass, void *data) +static void virtio_serial_port_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); @@ -1159,7 +1159,7 @@ static const Property virtio_serial_properties[] = { VIRTIO_CONSOLE_F_EMERG_WRITE, true), }; -static void virtio_serial_class_init(ObjectClass *klass, void *data) +static void virtio_serial_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/char/xen_console.c b/hw/char/xen_console.c index d03c188d1d..9c34a554bf 100644 --- a/hw/char/xen_console.c +++ b/hw/char/xen_console.c @@ -492,7 +492,7 @@ static const Property xen_console_properties[] = { DEFINE_PROP_INT32("idx", XenConsole, dev, -1), }; -static void xen_console_class_init(ObjectClass *class, void *data) +static void xen_console_class_init(ObjectClass *class, const void *data) { DeviceClass *dev_class = DEVICE_CLASS(class); XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class); diff --git a/hw/char/xilinx_uartlite.c b/hw/char/xilinx_uartlite.c index 4037c937ee..8008171eea 100644 --- a/hw/char/xilinx_uartlite.c +++ b/hw/char/xilinx_uartlite.c @@ -241,7 +241,7 @@ static void xilinx_uartlite_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } -static void xilinx_uartlite_class_init(ObjectClass *klass, void *data) +static void xilinx_uartlite_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/bus.c b/hw/core/bus.c index b9d89495cd..c3b431a014 100644 --- a/hw/core/bus.c +++ b/hw/core/bus.c @@ -232,7 +232,7 @@ static char *default_bus_get_fw_dev_path(DeviceState *dev) return g_strdup(object_get_typename(OBJECT(dev))); } -static void bus_class_init(ObjectClass *class, void *data) +static void bus_class_init(ObjectClass *class, const void *data) { BusClass *bc = BUS_CLASS(class); ResettableClass *rc = RESETTABLE_CLASS(class); diff --git a/hw/core/clock.c b/hw/core/clock.c index a81f888e62..9c906761e1 100644 --- a/hw/core/clock.c +++ b/hw/core/clock.c @@ -206,7 +206,7 @@ static void clock_finalizefn(Object *obj) g_free(clk->canonical_path); } -static void clock_class_init(ObjectClass *klass, void *data) +static void clock_class_init(ObjectClass *klass, const void *data) { klass->unparent = clock_unparent; } diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 9064dd24f8..1fb6ea3892 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -320,7 +320,7 @@ static int64_t cpu_common_get_arch_id(CPUState *cpu) return cpu->cpu_index; } -static void cpu_common_class_init(ObjectClass *klass, void *data) +static void cpu_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c index d3a426a1a2..e72bbde2a2 100644 --- a/hw/core/generic-loader.c +++ b/hw/core/generic-loader.c @@ -182,7 +182,7 @@ static const Property generic_loader_props[] = { DEFINE_PROP_STRING("file", GenericLoaderState, file), }; -static void generic_loader_class_init(ObjectClass *klass, void *data) +static void generic_loader_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/guest-loader.c b/hw/core/guest-loader.c index 76271df9f5..3db89d7a2e 100644 --- a/hw/core/guest-loader.c +++ b/hw/core/guest-loader.c @@ -118,7 +118,7 @@ static const Property guest_loader_props[] = { DEFINE_PROP_STRING("initrd", GuestLoaderState, initrd), }; -static void guest_loader_class_init(ObjectClass *klass, void *data) +static void guest_loader_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/machine.c b/hw/core/machine.c index bbff84855a..ed01798d37 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -1100,7 +1100,7 @@ static bool create_default_memdev(MachineState *ms, const char *path, return r; } -static void machine_class_init(ObjectClass *oc, void *data) +static void machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/core/or-irq.c b/hw/core/or-irq.c index 4d0d3cabf1..3942c70993 100644 --- a/hw/core/or-irq.c +++ b/hw/core/or-irq.c @@ -119,7 +119,7 @@ static const Property or_irq_properties[] = { DEFINE_PROP_UINT16("num-lines", OrIRQState, num_lines, 1), }; -static void or_irq_class_init(ObjectClass *klass, void *data) +static void or_irq_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/platform-bus.c b/hw/core/platform-bus.c index 1d00c4d36d..6950063de4 100644 --- a/hw/core/platform-bus.c +++ b/hw/core/platform-bus.c @@ -209,7 +209,7 @@ static const Property platform_bus_properties[] = { DEFINE_PROP_UINT32("mmio_size", PlatformBusDevice, mmio_size, 0), }; -static void platform_bus_class_init(ObjectClass *klass, void *data) +static void platform_bus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 1e0f47cc84..4a3760c101 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -731,7 +731,7 @@ device_vmstate_if_get_id(VMStateIf *obj) return qdev_get_dev_path(dev); } -static void device_class_init(ObjectClass *class, void *data) +static void device_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); VMStateIfClass *vc = VMSTATE_IF_CLASS(class); diff --git a/hw/core/register.c b/hw/core/register.c index 95b0150c0a..8f63d9f227 100644 --- a/hw/core/register.c +++ b/hw/core/register.c @@ -319,7 +319,7 @@ void register_finalize_block(RegisterInfoArray *r_array) g_free(r_array); } -static void register_class_init(ObjectClass *oc, void *data) +static void register_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/core/reset.c b/hw/core/reset.c index 8a3e0e518f..65f82fa43d 100644 --- a/hw/core/reset.c +++ b/hw/core/reset.c @@ -84,7 +84,7 @@ static void legacy_reset_finalize(Object *obj) { } -static void legacy_reset_class_init(ObjectClass *klass, void *data) +static void legacy_reset_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/core/resetcontainer.c b/hw/core/resetcontainer.c index e4ece68e83..5ff17002e7 100644 --- a/hw/core/resetcontainer.c +++ b/hw/core/resetcontainer.c @@ -68,7 +68,8 @@ static void resettable_container_finalize(Object *obj) { } -static void resettable_container_class_init(ObjectClass *klass, void *data) +static void resettable_container_class_init(ObjectClass *klass, + const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/core/split-irq.c b/hw/core/split-irq.c index fc12274811..f8b48750c5 100644 --- a/hw/core/split-irq.c +++ b/hw/core/split-irq.c @@ -63,7 +63,7 @@ static const Property split_irq_properties[] = { DEFINE_PROP_UINT16("num-lines", SplitIRQ, num_lines, 1), }; -static void split_irq_class_init(ObjectClass *klass, void *data) +static void split_irq_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index 6eb4c0f15a..e71367adfb 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -71,7 +71,7 @@ void foreach_dynamic_sysbus_device(FindSysbusDeviceFunc *func, void *opaque) } -static void system_bus_class_init(ObjectClass *klass, void *data) +static void system_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -280,7 +280,7 @@ static char *sysbus_get_fw_dev_path(DeviceState *dev) return g_strdup(qdev_fw_name(dev)); } -static void sysbus_device_class_init(ObjectClass *klass, void *data) +static void sysbus_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = sysbus_device_realize; @@ -320,7 +320,8 @@ BusState *sysbus_get_default(void) return main_system_bus; } -static void dynamic_sysbus_device_class_init(ObjectClass *klass, void *data) +static void dynamic_sysbus_device_class_init(ObjectClass *klass, + const void *data) { DeviceClass *k = DEVICE_CLASS(klass); diff --git a/hw/cpu/a15mpcore.c b/hw/cpu/a15mpcore.c index 676f65a0af..bd36dd94d4 100644 --- a/hw/cpu/a15mpcore.c +++ b/hw/cpu/a15mpcore.c @@ -161,7 +161,7 @@ static const Property a15mp_priv_properties[] = { DEFINE_PROP_UINT32("num-irq", A15MPPrivState, num_irq, 0), }; -static void a15mp_priv_class_init(ObjectClass *klass, void *data) +static void a15mp_priv_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/cpu/a9mpcore.c b/hw/cpu/a9mpcore.c index 1b9f2bef93..64bebbd19c 100644 --- a/hw/cpu/a9mpcore.c +++ b/hw/cpu/a9mpcore.c @@ -175,7 +175,7 @@ static const Property a9mp_priv_properties[] = { DEFINE_PROP_UINT32("num-irq", A9MPPrivState, num_irq, 0), }; -static void a9mp_priv_class_init(ObjectClass *klass, void *data) +static void a9mp_priv_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/cpu/arm11mpcore.c b/hw/cpu/arm11mpcore.c index b56bee6d54..01772e7f77 100644 --- a/hw/cpu/arm11mpcore.c +++ b/hw/cpu/arm11mpcore.c @@ -144,7 +144,7 @@ static const Property mpcore_priv_properties[] = { DEFINE_PROP_UINT32("num-irq", ARM11MPCorePriveState, num_irq, 64), }; -static void mpcore_priv_class_init(ObjectClass *klass, void *data) +static void mpcore_priv_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/cpu/cluster.c b/hw/cpu/cluster.c index 9da5221f88..ef3b3d1e94 100644 --- a/hw/cpu/cluster.c +++ b/hw/cpu/cluster.c @@ -72,7 +72,7 @@ static void cpu_cluster_realize(DeviceState *dev, Error **errp) assert(cbdata.cpu_count > 0); } -static void cpu_cluster_class_init(ObjectClass *klass, void *data) +static void cpu_cluster_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/cpu/core.c b/hw/cpu/core.c index 495a5c30ff..5cb2e9a7f5 100644 --- a/hw/cpu/core.c +++ b/hw/cpu/core.c @@ -77,7 +77,7 @@ static void cpu_core_instance_init(Object *obj) } } -static void cpu_core_class_init(ObjectClass *oc, void *data) +static void cpu_core_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/cpu/realview_mpcore.c b/hw/cpu/realview_mpcore.c index b140888618..099b71a9ef 100644 --- a/hw/cpu/realview_mpcore.c +++ b/hw/cpu/realview_mpcore.c @@ -107,7 +107,7 @@ static void mpcore_rirq_init(Object *obj) } } -static void mpcore_rirq_class_init(ObjectClass *klass, void *data) +static void mpcore_rirq_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/cxl/switch-mailbox-cci.c b/hw/cxl/switch-mailbox-cci.c index 833b824619..b92bbeb16e 100644 --- a/hw/cxl/switch-mailbox-cci.c +++ b/hw/cxl/switch-mailbox-cci.c @@ -72,7 +72,7 @@ static const Property cxl_switch_cci_props[] = { target, TYPE_CXL_USP, PCIDevice *), }; -static void cswmbcci_class_init(ObjectClass *oc, void *data) +static void cswmbcci_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); diff --git a/hw/display/apple-gfx-mmio.m b/hw/display/apple-gfx-mmio.m index b2e0e7a30f..b0b6e2993e 100644 --- a/hw/display/apple-gfx-mmio.m +++ b/hw/display/apple-gfx-mmio.m @@ -261,7 +261,7 @@ static void apple_gfx_mmio_reset(Object *obj, ResetType type) qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode), }; -static void apple_gfx_mmio_class_init(ObjectClass *klass, void *data) +static void apple_gfx_mmio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/display/apple-gfx-pci.m b/hw/display/apple-gfx-pci.m index b939bb9b23..2f0d24f7fe 100644 --- a/hw/display/apple-gfx-pci.m +++ b/hw/display/apple-gfx-pci.m @@ -121,7 +121,7 @@ static void apple_gfx_pci_reset(Object *obj, ResetType type) qdev_prop_apple_gfx_display_mode, AppleGFXDisplayMode), }; -static void apple_gfx_pci_class_init(ObjectClass *klass, void *data) +static void apple_gfx_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pci = PCI_DEVICE_CLASS(klass); diff --git a/hw/display/artist.c b/hw/display/artist.c index f24c1d83dd..3fafc8a222 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -1487,7 +1487,7 @@ static void artist_reset(DeviceState *qdev) { } -static void artist_class_init(ObjectClass *klass, void *data) +static void artist_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/ati.c b/hw/display/ati.c index 864fa4fc2c..4e88d09943 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -1049,7 +1049,7 @@ static const Property ati_vga_properties[] = { DEFINE_PROP_UINT8("x-pixman", ATIVGAState, use_pixman, DEFAULT_X_PIXMAN), }; -static void ati_vga_class_init(ObjectClass *klass, void *data) +static void ati_vga_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/display/bcm2835_fb.c b/hw/display/bcm2835_fb.c index a5bded5156..820e67ac8b 100644 --- a/hw/display/bcm2835_fb.c +++ b/hw/display/bcm2835_fb.c @@ -442,7 +442,7 @@ static const Property bcm2835_fb_props[] = { initial_config.alpha, 2), /* alpha ignored */ }; -static void bcm2835_fb_class_init(ObjectClass *klass, void *data) +static void bcm2835_fb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index 086f7a0f06..1d329fc9cc 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -351,7 +351,7 @@ static const Property bochs_display_properties[] = { DEFINE_EDID_PROPERTIES(BochsDisplayState, edid_info), }; -static void bochs_display_class_init(ObjectClass *klass, void *data) +static void bochs_display_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/display/cg3.c b/hw/display/cg3.c index 3f971d875f..daeef15217 100644 --- a/hw/display/cg3.c +++ b/hw/display/cg3.c @@ -368,7 +368,7 @@ static const Property cg3_properties[] = { DEFINE_PROP_UINT16("depth", CG3State, depth, -1), }; -static void cg3_class_init(ObjectClass *klass, void *data) +static void cg3_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 76124d3656..4e5ae04af0 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -2991,7 +2991,7 @@ static const Property pci_vga_cirrus_properties[] = { cirrus_vga.vga.global_vmstate, false), }; -static void cirrus_vga_class_init(ObjectClass *klass, void *data) +static void cirrus_vga_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/display/cirrus_vga_isa.c b/hw/display/cirrus_vga_isa.c index 60b7fd20f1..4b55c48eff 100644 --- a/hw/display/cirrus_vga_isa.c +++ b/hw/display/cirrus_vga_isa.c @@ -76,7 +76,7 @@ static const Property isa_cirrus_vga_properties[] = { cirrus_vga.enable_blitter, true), }; -static void isa_cirrus_vga_class_init(ObjectClass *klass, void *data) +static void isa_cirrus_vga_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/dm163.c b/hw/display/dm163.c index f6f0ec0c63..f8340d8275 100644 --- a/hw/display/dm163.c +++ b/hw/display/dm163.c @@ -325,7 +325,7 @@ static void dm163_realize(DeviceState *dev, Error **errp) RGB_MATRIX_NUM_ROWS * LED_SQUARE_SIZE); } -static void dm163_class_init(ObjectClass *klass, void *data) +static void dm163_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/display/dpcd.c b/hw/display/dpcd.c index 108faf7887..a157dc64e7 100644 --- a/hw/display/dpcd.c +++ b/hw/display/dpcd.c @@ -141,7 +141,7 @@ static const VMStateDescription vmstate_dpcd = { } }; -static void dpcd_class_init(ObjectClass *oc, void *data) +static void dpcd_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/display/exynos4210_fimd.c b/hw/display/exynos4210_fimd.c index 04c864a308..c61e0280a7 100644 --- a/hw/display/exynos4210_fimd.c +++ b/hw/display/exynos4210_fimd.c @@ -1958,7 +1958,7 @@ static void exynos4210_fimd_realize(DeviceState *dev, Error **errp) s->console = graphic_console_init(dev, 0, &exynos4210_fimd_ops, s); } -static void exynos4210_fimd_class_init(ObjectClass *klass, void *data) +static void exynos4210_fimd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/g364fb.c b/hw/display/g364fb.c index 30b5ea67f2..a6ddc21d3e 100644 --- a/hw/display/g364fb.c +++ b/hw/display/g364fb.c @@ -526,7 +526,7 @@ static const VMStateDescription vmstate_g364fb_sysbus = { } }; -static void g364fb_sysbus_class_init(ObjectClass *klass, void *data) +static void g364fb_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/i2c-ddc.c b/hw/display/i2c-ddc.c index d8ab9eee40..2adfc1a147 100644 --- a/hw/display/i2c-ddc.c +++ b/hw/display/i2c-ddc.c @@ -99,7 +99,7 @@ static const Property i2c_ddc_properties[] = { DEFINE_EDID_PROPERTIES(I2CDDCState, edid_info), }; -static void i2c_ddc_class_init(ObjectClass *oc, void *data) +static void i2c_ddc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc); diff --git a/hw/display/jazz_led.c b/hw/display/jazz_led.c index 1448488d06..90e82b58be 100644 --- a/hw/display/jazz_led.c +++ b/hw/display/jazz_led.c @@ -294,7 +294,7 @@ static void jazz_led_reset(DeviceState *d) qemu_console_resize(s->con, 60, 80); } -static void jazz_led_class_init(ObjectClass *klass, void *data) +static void jazz_led_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/macfb.c b/hw/display/macfb.c index b08eb06cbd..574d667173 100644 --- a/hw/display/macfb.c +++ b/hw/display/macfb.c @@ -793,7 +793,7 @@ static const VMStateDescription vmstate_macfb_nubus = { } }; -static void macfb_sysbus_class_init(ObjectClass *klass, void *data) +static void macfb_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -804,7 +804,7 @@ static void macfb_sysbus_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, macfb_sysbus_properties); } -static void macfb_nubus_class_init(ObjectClass *klass, void *data) +static void macfb_nubus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); MacfbNubusDeviceClass *ndc = NUBUS_MACFB_CLASS(klass); diff --git a/hw/display/next-fb.c b/hw/display/next-fb.c index 8446ff3c00..ec81b766a7 100644 --- a/hw/display/next-fb.c +++ b/hw/display/next-fb.c @@ -119,7 +119,7 @@ static void nextfb_realize(DeviceState *dev, Error **errp) qemu_console_resize(s->con, s->cols, s->rows); } -static void nextfb_class_init(ObjectClass *oc, void *data) +static void nextfb_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/display/pl110.c b/hw/display/pl110.c index 4d4f477b94..09c3c59e0e 100644 --- a/hw/display/pl110.c +++ b/hw/display/pl110.c @@ -580,7 +580,7 @@ static void pl111_init(Object *obj) s->version = VERSION_PL111; } -static void pl110_class_init(ObjectClass *klass, void *data) +static void pl110_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/qxl.c b/hw/display/qxl.c index da14da5209..6c820bcdb5 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2498,7 +2498,7 @@ static const Property qxl_properties[] = { DEFINE_PROP_BOOL("global-vmstate", PCIQXLDevice, vga.global_vmstate, false), }; -static void qxl_pci_class_init(ObjectClass *klass, void *data) +static void qxl_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -2523,7 +2523,7 @@ static const TypeInfo qxl_pci_type_info = { }, }; -static void qxl_primary_class_init(ObjectClass *klass, void *data) +static void qxl_primary_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -2543,7 +2543,7 @@ static const TypeInfo qxl_primary_info = { module_obj("qxl-vga"); module_kconfig(QXL); -static void qxl_secondary_class_init(ObjectClass *klass, void *data) +static void qxl_secondary_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c index 1be106b57f..08f2d5db4e 100644 --- a/hw/display/ramfb-standalone.c +++ b/hw/display/ramfb-standalone.c @@ -64,7 +64,7 @@ static const Property ramfb_properties[] = { DEFINE_PROP_BOOL("x-migrate", RAMFBStandaloneState, migrate, true), }; -static void ramfb_class_initfn(ObjectClass *klass, void *data) +static void ramfb_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/sii9022.c b/hw/display/sii9022.c index 16f8cb487c..d00d3e9fc5 100644 --- a/hw/display/sii9022.c +++ b/hw/display/sii9022.c @@ -167,7 +167,7 @@ static void sii9022_realize(DeviceState *dev, Error **errp) i2c_slave_create_simple(bus, TYPE_I2CDDC, 0x50); } -static void sii9022_class_init(ObjectClass *klass, void *data) +static void sii9022_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 09edcf86f8..dcff1e978e 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -2077,7 +2077,7 @@ static const VMStateDescription vmstate_sm501_sysbus = { } }; -static void sm501_sysbus_class_init(ObjectClass *klass, void *data) +static void sm501_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -2167,7 +2167,7 @@ static const VMStateDescription vmstate_sm501_pci = { } }; -static void sm501_pci_class_init(ObjectClass *klass, void *data) +static void sm501_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/display/ssd0303.c b/hw/display/ssd0303.c index e292cff44e..87781438cd 100644 --- a/hw/display/ssd0303.c +++ b/hw/display/ssd0303.c @@ -311,7 +311,7 @@ static void ssd0303_realize(DeviceState *dev, Error **errp) qemu_console_resize(s->con, 96 * MAGNIFY, 16 * MAGNIFY); } -static void ssd0303_class_init(ObjectClass *klass, void *data) +static void ssd0303_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/display/ssd0323.c b/hw/display/ssd0323.c index 96cf0dc662..af5ff4fecd 100644 --- a/hw/display/ssd0323.c +++ b/hw/display/ssd0323.c @@ -361,7 +361,7 @@ static void ssd0323_realize(SSIPeripheral *d, Error **errp) qdev_init_gpio_in(dev, ssd0323_cd, 1); } -static void ssd0323_class_init(ObjectClass *klass, void *data) +static void ssd0323_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); diff --git a/hw/display/tcx.c b/hw/display/tcx.c index 5968d33e48..4853c5e142 100644 --- a/hw/display/tcx.c +++ b/hw/display/tcx.c @@ -885,7 +885,7 @@ static const Property tcx_properties[] = { DEFINE_PROP_UINT16("depth", TCXState, depth, -1), }; -static void tcx_class_init(ObjectClass *klass, void *data) +static void tcx_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/vga-isa.c b/hw/display/vga-isa.c index 2920628f78..3618913b3b 100644 --- a/hw/display/vga-isa.c +++ b/hw/display/vga-isa.c @@ -92,7 +92,7 @@ static const Property vga_isa_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", ISAVGAState, state.vram_size_mb, 8), }; -static void vga_isa_class_initfn(ObjectClass *klass, void *data) +static void vga_isa_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/vga-mmio.c b/hw/display/vga-mmio.c index 1e0c2dbf74..33263856b7 100644 --- a/hw/display/vga-mmio.c +++ b/hw/display/vga-mmio.c @@ -116,7 +116,7 @@ static const Property vga_mmio_properties[] = { DEFINE_PROP_UINT32("vgamem_mb", VGAMmioState, vga.vram_size_mb, 8), }; -static void vga_mmio_class_initfn(ObjectClass *klass, void *data) +static void vga_mmio_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index dd084c20b1..a860197274 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -350,7 +350,7 @@ static const Property secondary_pci_properties[] = { DEFINE_EDID_PROPERTIES(PCIVGAState, edid_info), }; -static void vga_pci_class_init(ObjectClass *klass, void *data) +static void vga_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -376,7 +376,7 @@ static const TypeInfo vga_pci_type_info = { }, }; -static void vga_class_init(ObjectClass *klass, void *data) +static void vga_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -392,7 +392,7 @@ static void vga_class_init(ObjectClass *klass, void *data) vga_get_big_endian_fb, vga_set_big_endian_fb); } -static void secondary_class_init(ObjectClass *klass, void *data) +static void secondary_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 2aed6243f6..06c4e7e190 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -658,7 +658,7 @@ static const Property vhost_user_gpu_properties[] = { }; static void -vhost_user_gpu_class_init(ObjectClass *klass, void *data) +vhost_user_gpu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index 321a6f4998..9eb806b71f 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -262,7 +262,7 @@ virtio_gpu_base_device_unrealize(DeviceState *qdev) } static void -virtio_gpu_base_class_init(ObjectClass *klass, void *data) +virtio_gpu_base_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/display/virtio-gpu-gl.c b/hw/display/virtio-gpu-gl.c index 683fad3bf8..c06a078fb3 100644 --- a/hw/display/virtio-gpu-gl.c +++ b/hw/display/virtio-gpu-gl.c @@ -182,7 +182,7 @@ static void virtio_gpu_gl_device_unrealize(DeviceState *qdev) g_array_unref(g->capset_ids); } -static void virtio_gpu_gl_class_init(ObjectClass *klass, void *data) +static void virtio_gpu_gl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/display/virtio-gpu-pci.c b/hw/display/virtio-gpu-pci.c index 6d789701a3..c0d71b6254 100644 --- a/hw/display/virtio-gpu-pci.c +++ b/hw/display/virtio-gpu-pci.c @@ -57,7 +57,7 @@ static void virtio_gpu_pci_base_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } } -static void virtio_gpu_pci_base_class_init(ObjectClass *klass, void *data) +static void virtio_gpu_pci_base_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/display/virtio-gpu-rutabaga.c b/hw/display/virtio-gpu-rutabaga.c index f6eb29472e..ed5ae52acb 100644 --- a/hw/display/virtio-gpu-rutabaga.c +++ b/hw/display/virtio-gpu-rutabaga.c @@ -1110,7 +1110,7 @@ static const Property virtio_gpu_rutabaga_properties[] = { DEFINE_PROP_STRING("wsi", VirtIOGPURutabaga, wsi), }; -static void virtio_gpu_rutabaga_class_init(ObjectClass *klass, void *data) +static void virtio_gpu_rutabaga_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index 11a7a85750..0a1a625b0e 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1684,7 +1684,7 @@ static const Property virtio_gpu_properties[] = { DEFINE_PROP_UINT8("x-scanout-vmstate-version", VirtIOGPU, scanout_vmstate_version, 2), }; -static void virtio_gpu_class_init(ObjectClass *klass, void *data) +static void virtio_gpu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/display/virtio-vga.c b/hw/display/virtio-vga.c index fefbdb61e1..40e60f70fc 100644 --- a/hw/display/virtio-vga.c +++ b/hw/display/virtio-vga.c @@ -213,7 +213,7 @@ static const Property virtio_vga_base_properties[] = { DEFINE_VIRTIO_GPU_PCI_PROPERTIES(VirtIOPCIProxy), }; -static void virtio_vga_base_class_init(ObjectClass *klass, void *data) +static void virtio_vga_base_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 2dd661e3c1..7777deb17d 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1339,7 +1339,7 @@ static const Property vga_vmware_properties[] = { chip.vga.global_vmstate, false), }; -static void vmsvga_class_init(ObjectClass *klass, void *data) +static void vmsvga_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/display/xlnx_dp.c b/hw/display/xlnx_dp.c index 1272da0133..7c980ee642 100644 --- a/hw/display/xlnx_dp.c +++ b/hw/display/xlnx_dp.c @@ -1391,7 +1391,7 @@ static const Property xlnx_dp_device_properties[] = { DEFINE_AUDIO_PROPERTIES(XlnxDPState, aud_card), }; -static void xlnx_dp_class_init(ObjectClass *oc, void *data) +static void xlnx_dp_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/dma/bcm2835_dma.c b/hw/dma/bcm2835_dma.c index 9b2fca2c7c..a2771ddcb5 100644 --- a/hw/dma/bcm2835_dma.c +++ b/hw/dma/bcm2835_dma.c @@ -385,7 +385,7 @@ static void bcm2835_dma_realize(DeviceState *dev, Error **errp) bcm2835_dma_reset(dev); } -static void bcm2835_dma_class_init(ObjectClass *klass, void *data) +static void bcm2835_dma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/dma/i82374.c b/hw/dma/i82374.c index 0bf69ef399..e226eda6d1 100644 --- a/hw/dma/i82374.c +++ b/hw/dma/i82374.c @@ -143,7 +143,7 @@ static const Property i82374_properties[] = { DEFINE_PROP_UINT32("iobase", I82374State, iobase, 0x400), }; -static void i82374_class_init(ObjectClass *klass, void *data) +static void i82374_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index 74c38d2ee8..1d67e50536 100644 --- a/hw/dma/i8257.c +++ b/hw/dma/i8257.c @@ -592,7 +592,7 @@ static const Property i8257_properties[] = { DEFINE_PROP_INT32("dshift", I8257State, dshift, 0), }; -static void i8257_class_init(ObjectClass *klass, void *data) +static void i8257_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IsaDmaClass *idc = ISADMA_CLASS(klass); diff --git a/hw/dma/pl080.c b/hw/dma/pl080.c index 8a9b073b24..277d934322 100644 --- a/hw/dma/pl080.c +++ b/hw/dma/pl080.c @@ -413,7 +413,7 @@ static const Property pl080_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), }; -static void pl080_class_init(ObjectClass *oc, void *data) +static void pl080_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/dma/pl330.c b/hw/dma/pl330.c index 545aa44e45..a570bb08ec 100644 --- a/hw/dma/pl330.c +++ b/hw/dma/pl330.c @@ -1671,7 +1671,7 @@ static const Property pl330_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), }; -static void pl330_class_init(ObjectClass *klass, void *data) +static void pl330_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/dma/rc4030.c b/hw/dma/rc4030.c index 6842e7d491..b6ed1d4643 100644 --- a/hw/dma/rc4030.c +++ b/hw/dma/rc4030.c @@ -701,7 +701,7 @@ static void rc4030_unrealize(DeviceState *dev) object_unparent(OBJECT(&s->dma_mr)); } -static void rc4030_class_init(ObjectClass *klass, void *class_data) +static void rc4030_class_init(ObjectClass *klass, const void *class_data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -720,7 +720,7 @@ static const TypeInfo rc4030_info = { }; static void rc4030_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/dma/sifive_pdma.c b/hw/dma/sifive_pdma.c index a115af8d60..48de3a2478 100644 --- a/hw/dma/sifive_pdma.c +++ b/hw/dma/sifive_pdma.c @@ -464,7 +464,7 @@ static void sifive_pdma_realize(DeviceState *dev, Error **errp) } } -static void sifive_pdma_class_init(ObjectClass *klass, void *data) +static void sifive_pdma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/dma/sparc32_dma.c b/hw/dma/sparc32_dma.c index 280b747521..60c23b69e5 100644 --- a/hw/dma/sparc32_dma.c +++ b/hw/dma/sparc32_dma.c @@ -274,7 +274,7 @@ static void sparc32_dma_device_init(Object *obj) qdev_init_gpio_out(dev, s->gpio, 2); } -static void sparc32_dma_device_class_init(ObjectClass *klass, void *data) +static void sparc32_dma_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -316,7 +316,8 @@ static void sparc32_espdma_device_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(sysbus), &error_fatal); } -static void sparc32_espdma_device_class_init(ObjectClass *klass, void *data) +static void sparc32_espdma_device_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -351,7 +352,8 @@ static void sparc32_ledma_device_realize(DeviceState *dev, Error **errp) sysbus_realize(SYS_BUS_DEVICE(lance), &error_fatal); } -static void sparc32_ledma_device_class_init(ObjectClass *klass, void *data) +static void sparc32_ledma_device_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -426,7 +428,7 @@ static void sparc32_dma_init(Object *obj) TYPE_SPARC32_LEDMA_DEVICE); } -static void sparc32_dma_class_init(ObjectClass *klass, void *data) +static void sparc32_dma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index 22fe35751a..bf1b523ac8 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -621,7 +621,7 @@ static const Property axidma_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), }; -static void axidma_class_init(ObjectClass *klass, void *data) +static void axidma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -639,7 +639,8 @@ static StreamSinkClass xilinx_axidma_control_stream_class = { .push = xilinx_axidma_control_stream_push, }; -static void xilinx_axidma_stream_class_init(ObjectClass *klass, void *data) +static void xilinx_axidma_stream_class_init(ObjectClass *klass, + const void *data) { StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c index bb27cb2e64..0c075e7d0d 100644 --- a/hw/dma/xlnx-zdma.c +++ b/hw/dma/xlnx-zdma.c @@ -816,7 +816,7 @@ static const Property zdma_props[] = { TYPE_MEMORY_REGION, MemoryRegion *), }; -static void zdma_class_init(ObjectClass *klass, void *data) +static void zdma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/dma/xlnx-zynq-devcfg.c b/hw/dma/xlnx-zynq-devcfg.c index 0fd0d23f57..26845713ee 100644 --- a/hw/dma/xlnx-zynq-devcfg.c +++ b/hw/dma/xlnx-zynq-devcfg.c @@ -380,7 +380,7 @@ static void xlnx_zynq_devcfg_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static void xlnx_zynq_devcfg_class_init(ObjectClass *klass, void *data) +static void xlnx_zynq_devcfg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index 1afaa0bf51..6943c927d0 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -712,7 +712,7 @@ static const Property xlnx_csu_dma_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), }; -static void xlnx_csu_dma_class_init(ObjectClass *klass, void *data) +static void xlnx_csu_dma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); diff --git a/hw/dma/xlnx_dpdma.c b/hw/dma/xlnx_dpdma.c index 2657808d37..3d88ccc8da 100644 --- a/hw/dma/xlnx_dpdma.c +++ b/hw/dma/xlnx_dpdma.c @@ -593,7 +593,7 @@ static void xlnx_dpdma_reset(DeviceState *dev) } } -static void xlnx_dpdma_class_init(ObjectClass *oc, void *data) +static void xlnx_dpdma_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/fsi/aspeed_apb2opb.c b/hw/fsi/aspeed_apb2opb.c index 0e2cc143f1..172ba16b0c 100644 --- a/hw/fsi/aspeed_apb2opb.c +++ b/hw/fsi/aspeed_apb2opb.c @@ -320,7 +320,7 @@ static void fsi_aspeed_apb2opb_reset(DeviceState *dev) memcpy(s->regs, aspeed_apb2opb_reset, ASPEED_APB2OPB_NR_REGS); } -static void fsi_aspeed_apb2opb_class_init(ObjectClass *klass, void *data) +static void fsi_aspeed_apb2opb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/fsi/cfam.c b/hw/fsi/cfam.c index c62f0f78de..e2145c5934 100644 --- a/hw/fsi/cfam.c +++ b/hw/fsi/cfam.c @@ -145,7 +145,7 @@ static void fsi_cfam_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(&cfam->lbus.mr, 0, &fsi_dev->iomem); } -static void fsi_cfam_class_init(ObjectClass *klass, void *data) +static void fsi_cfam_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->bus_type = TYPE_FSI_BUS; diff --git a/hw/fsi/fsi-master.c b/hw/fsi/fsi-master.c index 50fb1cd467..083a5507ab 100644 --- a/hw/fsi/fsi-master.c +++ b/hw/fsi/fsi-master.c @@ -144,7 +144,7 @@ static void fsi_master_reset(DeviceState *dev) s->regs[FSI_MVER] = 0xe0050101; } -static void fsi_master_class_init(ObjectClass *klass, void *data) +static void fsi_master_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/fsi/fsi.c b/hw/fsi/fsi.c index 83ddb17ae6..6c52d5e745 100644 --- a/hw/fsi/fsi.c +++ b/hw/fsi/fsi.c @@ -76,7 +76,7 @@ static void fsi_slave_init(Object *o) s, TYPE_FSI_SLAVE, 0x400); } -static void fsi_slave_class_init(ObjectClass *klass, void *data) +static void fsi_slave_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/fsi/lbus.c b/hw/fsi/lbus.c index 4f87b28a22..8ec7f5fd78 100644 --- a/hw/fsi/lbus.c +++ b/hw/fsi/lbus.c @@ -91,7 +91,7 @@ static void fsi_scratchpad_reset(DeviceState *dev) memset(s->regs, 0, sizeof(s->regs)); } -static void fsi_scratchpad_class_init(ObjectClass *klass, void *data) +static void fsi_scratchpad_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/gpio/aspeed_gpio.c b/hw/gpio/aspeed_gpio.c index aedaf5238b..609a556908 100644 --- a/hw/gpio/aspeed_gpio.c +++ b/hw/gpio/aspeed_gpio.c @@ -1473,7 +1473,7 @@ static const VMStateDescription vmstate_aspeed_gpio = { } }; -static void aspeed_gpio_class_init(ObjectClass *klass, void *data) +static void aspeed_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1483,7 +1483,7 @@ static void aspeed_gpio_class_init(ObjectClass *klass, void *data) dc->vmsd = &vmstate_aspeed_gpio; } -static void aspeed_gpio_ast2400_class_init(ObjectClass *klass, void *data) +static void aspeed_gpio_ast2400_class_init(ObjectClass *klass, const void *data) { AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); @@ -1496,7 +1496,7 @@ static void aspeed_gpio_ast2400_class_init(ObjectClass *klass, void *data) agc->reg_ops = &aspeed_gpio_ops; } -static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data) +static void aspeed_gpio_2500_class_init(ObjectClass *klass, const void *data) { AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); @@ -1509,7 +1509,8 @@ static void aspeed_gpio_2500_class_init(ObjectClass *klass, void *data) agc->reg_ops = &aspeed_gpio_ops; } -static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, void *data) +static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, + const void *data) { AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); @@ -1522,7 +1523,8 @@ static void aspeed_gpio_ast2600_3_3v_class_init(ObjectClass *klass, void *data) agc->reg_ops = &aspeed_gpio_ops; } -static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data) +static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, + const void *data) { AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); @@ -1535,7 +1537,7 @@ static void aspeed_gpio_ast2600_1_8v_class_init(ObjectClass *klass, void *data) agc->reg_ops = &aspeed_gpio_ops; } -static void aspeed_gpio_1030_class_init(ObjectClass *klass, void *data) +static void aspeed_gpio_1030_class_init(ObjectClass *klass, const void *data) { AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); @@ -1548,7 +1550,7 @@ static void aspeed_gpio_1030_class_init(ObjectClass *klass, void *data) agc->reg_ops = &aspeed_gpio_ops; } -static void aspeed_gpio_2700_class_init(ObjectClass *klass, void *data) +static void aspeed_gpio_2700_class_init(ObjectClass *klass, const void *data) { AspeedGPIOClass *agc = ASPEED_GPIO_CLASS(klass); diff --git a/hw/gpio/bcm2835_gpio.c b/hw/gpio/bcm2835_gpio.c index 5a5f1df5e8..dfb5d5cb57 100644 --- a/hw/gpio/bcm2835_gpio.c +++ b/hw/gpio/bcm2835_gpio.c @@ -319,7 +319,7 @@ static void bcm2835_gpio_realize(DeviceState *dev, Error **errp) s->sdbus_sdhost = SD_BUS(obj); } -static void bcm2835_gpio_class_init(ObjectClass *klass, void *data) +static void bcm2835_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/gpio/bcm2838_gpio.c b/hw/gpio/bcm2838_gpio.c index 53be8f2d23..1069e7811b 100644 --- a/hw/gpio/bcm2838_gpio.c +++ b/hw/gpio/bcm2838_gpio.c @@ -364,7 +364,7 @@ static void bcm2838_gpio_realize(DeviceState *dev, Error **errp) s->sdbus_sdhost = SD_BUS(obj); } -static void bcm2838_gpio_class_init(ObjectClass *klass, void *data) +static void bcm2838_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/gpio/gpio_key.c b/hw/gpio/gpio_key.c index 2fcab9ead6..40c028bed9 100644 --- a/hw/gpio/gpio_key.c +++ b/hw/gpio/gpio_key.c @@ -85,7 +85,7 @@ static void gpio_key_realize(DeviceState *dev, Error **errp) s->timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, gpio_key_timer_expired, s); } -static void gpio_key_class_init(ObjectClass *klass, void *data) +static void gpio_key_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/gpio/imx_gpio.c b/hw/gpio/imx_gpio.c index 8c8299c4c4..f23c52af26 100644 --- a/hw/gpio/imx_gpio.c +++ b/hw/gpio/imx_gpio.c @@ -321,7 +321,7 @@ static void imx_gpio_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); } -static void imx_gpio_class_init(ObjectClass *klass, void *data) +static void imx_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/gpio/mpc8xxx.c b/hw/gpio/mpc8xxx.c index a3c1d2fbf4..257497af58 100644 --- a/hw/gpio/mpc8xxx.c +++ b/hw/gpio/mpc8xxx.c @@ -199,7 +199,7 @@ static void mpc8xxx_gpio_initfn(Object *obj) qdev_init_gpio_out(dev, s->out, 32); } -static void mpc8xxx_gpio_class_init(ObjectClass *klass, void *data) +static void mpc8xxx_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/gpio/npcm7xx_gpio.c b/hw/gpio/npcm7xx_gpio.c index 2916056fae..66f8256a7a 100644 --- a/hw/gpio/npcm7xx_gpio.c +++ b/hw/gpio/npcm7xx_gpio.c @@ -396,7 +396,7 @@ static const Property npcm7xx_gpio_properties[] = { DEFINE_PROP_UINT32("reset-odsc", NPCM7xxGPIOState, reset_odsc, 0), }; -static void npcm7xx_gpio_class_init(ObjectClass *klass, void *data) +static void npcm7xx_gpio_class_init(ObjectClass *klass, const void *data) { ResettableClass *reset = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/gpio/nrf51_gpio.c b/hw/gpio/nrf51_gpio.c index d08c254e36..d94c0c47da 100644 --- a/hw/gpio/nrf51_gpio.c +++ b/hw/gpio/nrf51_gpio.c @@ -304,7 +304,7 @@ static void nrf51_gpio_init(Object *obj) qdev_init_gpio_out_named(DEVICE(s), &s->detect, "detect", 1); } -static void nrf51_gpio_class_init(ObjectClass *klass, void *data) +static void nrf51_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c index 8a9f14ba15..61ea7862af 100644 --- a/hw/gpio/omap_gpio.c +++ b/hw/gpio/omap_gpio.c @@ -229,7 +229,7 @@ static const Property omap_gpio_properties[] = { DEFINE_PROP_INT32("mpu_model", Omap1GpioState, mpu_model, 0), }; -static void omap_gpio_class_init(ObjectClass *klass, void *data) +static void omap_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index 1ac0cf6c46..d65c0a2e90 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -432,7 +432,7 @@ static const Property pca955x_properties[] = { DEFINE_PROP_STRING("description", PCA955xState, description), }; -static void pca955x_class_init(ObjectClass *klass, void *data) +static void pca955x_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); @@ -454,7 +454,7 @@ static const TypeInfo pca955x_info = { .abstract = true, }; -static void pca9552_class_init(ObjectClass *oc, void *data) +static void pca9552_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCA955xClass *pc = PCA955X_CLASS(oc); diff --git a/hw/gpio/pca9554.c b/hw/gpio/pca9554.c index 7301fce934..de3f883aee 100644 --- a/hw/gpio/pca9554.c +++ b/hw/gpio/pca9554.c @@ -292,7 +292,7 @@ static const Property pca9554_properties[] = { DEFINE_PROP_STRING("description", PCA9554State, description), }; -static void pca9554_class_init(ObjectClass *klass, void *data) +static void pca9554_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/gpio/pcf8574.c b/hw/gpio/pcf8574.c index 208efe69ea..274b44bb61 100644 --- a/hw/gpio/pcf8574.c +++ b/hw/gpio/pcf8574.c @@ -138,7 +138,7 @@ static void pcf8574_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out_named(dev, &s->intrq, "nINT", 1); } -static void pcf8574_class_init(ObjectClass *klass, void *data) +static void pcf8574_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/gpio/pl061.c b/hw/gpio/pl061.c index 2e69785f2a..1acca3f2f8 100644 --- a/hw/gpio/pl061.c +++ b/hw/gpio/pl061.c @@ -566,7 +566,7 @@ static const Property pl061_props[] = { DEFINE_PROP_UINT32("pulldowns", PL061State, pulldowns, 0x0), }; -static void pl061_class_init(ObjectClass *klass, void *data) +static void pl061_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/gpio/sifive_gpio.c b/hw/gpio/sifive_gpio.c index 0d5206ae6b..5831647b4d 100644 --- a/hw/gpio/sifive_gpio.c +++ b/hw/gpio/sifive_gpio.c @@ -370,7 +370,7 @@ static void sifive_gpio_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(DEVICE(s), s->output, s->ngpio); } -static void sifive_gpio_class_init(ObjectClass *klass, void *data) +static void sifive_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/gpio/stm32l4x5_gpio.c b/hw/gpio/stm32l4x5_gpio.c index f69fc1db4f..414ce83039 100644 --- a/hw/gpio/stm32l4x5_gpio.c +++ b/hw/gpio/stm32l4x5_gpio.c @@ -454,7 +454,7 @@ static const Property stm32l4x5_gpio_properties[] = { DEFINE_PROP_UINT32("pupd-reset", Stm32l4x5GpioState, pupdr_reset, 0), }; -static void stm32l4x5_gpio_class_init(ObjectClass *klass, void *data) +static void stm32l4x5_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/gpio/zaurus.c b/hw/gpio/zaurus.c index 7342440b95..b8d27f5973 100644 --- a/hw/gpio/zaurus.c +++ b/hw/gpio/zaurus.c @@ -243,7 +243,7 @@ static const VMStateDescription vmstate_scoop_regs = { }, }; -static void scoop_sysbus_class_init(ObjectClass *klass, void *data) +static void scoop_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index c430bf28dd..1812276678 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -683,7 +683,7 @@ static void hppa_nmi(NMIState *n, int cpu_index, Error **errp) } } -static void HP_B160L_machine_init_class_init(ObjectClass *oc, void *data) +static void HP_B160L_machine_init_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { TYPE_HPPA_CPU, @@ -719,7 +719,7 @@ static const TypeInfo HP_B160L_machine_init_typeinfo = { }, }; -static void HP_C3700_machine_init_class_init(ObjectClass *oc, void *data) +static void HP_C3700_machine_init_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { TYPE_HPPA64_CPU, diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index acabff2c4a..94b0abbd68 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -1743,7 +1743,7 @@ static const Property hv_balloon_properties[] = { DEFINE_PROP_UINT64(HV_BALLOON_ADDR_PROP, HvBalloon, addr, 0), }; -static void hv_balloon_class_init(ObjectClass *klass, void *data) +static void hv_balloon_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VMBusDeviceClass *vdc = VMBUS_DEVICE_CLASS(klass); diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index 8f193fd0bd..0271cfd271 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -133,7 +133,7 @@ static void synic_reset(DeviceState *dev) assert(QLIST_EMPTY(&synic->sint_routes)); } -static void synic_class_init(ObjectClass *klass, void *data) +static void synic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/hyperv/hyperv_testdev.c b/hw/hyperv/hyperv_testdev.c index a630ca7047..2d4a63693b 100644 --- a/hw/hyperv/hyperv_testdev.c +++ b/hw/hyperv/hyperv_testdev.c @@ -303,7 +303,7 @@ static void hv_test_dev_realizefn(DeviceState *d, Error **errp) memory_region_add_subregion(io, 0x3000, &dev->sint_control); } -static void hv_test_dev_class_init(ObjectClass *klass, void *data) +static void hv_test_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c index a410b55b9a..ca291826a0 100644 --- a/hw/hyperv/syndbg.c +++ b/hw/hyperv/syndbg.c @@ -373,7 +373,7 @@ static const Property hv_syndbg_properties[] = { DEFINE_PROP_BOOL("use_hcalls", HvSynDbg, use_hcalls, false), }; -static void hv_syndbg_class_init(ObjectClass *klass, void *data) +static void hv_syndbg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index 98ea968e51..b147ea06d8 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -2351,7 +2351,7 @@ static const Property vmbus_dev_props[] = { }; -static void vmbus_dev_class_init(ObjectClass *klass, void *data) +static void vmbus_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *kdev = DEVICE_CLASS(klass); device_class_set_props(kdev, vmbus_dev_props); @@ -2469,7 +2469,7 @@ static char *vmbus_get_fw_dev_path(DeviceState *dev) return g_strdup_printf("%s@%s", qdev_fw_name(dev), uuid); } -static void vmbus_class_init(ObjectClass *klass, void *data) +static void vmbus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -2656,7 +2656,7 @@ static const Property vmbus_bridge_props[] = { DEFINE_PROP_UINT8("irq", VMBusBridge, irq, 7), }; -static void vmbus_bridge_class_init(ObjectClass *klass, void *data) +static void vmbus_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); SysBusDeviceClass *sk = SYS_BUS_DEVICE_CLASS(klass); diff --git a/hw/i2c/allwinner-i2c.c b/hw/i2c/allwinner-i2c.c index 66d6431c50..fe887e1c6a 100644 --- a/hw/i2c/allwinner-i2c.c +++ b/hw/i2c/allwinner-i2c.c @@ -438,7 +438,7 @@ static void allwinner_i2c_realize(DeviceState *dev, Error **errp) s->bus = i2c_init_bus(dev, "i2c"); } -static void allwinner_i2c_class_init(ObjectClass *klass, void *data) +static void allwinner_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/i2c/aspeed_i2c.c b/hw/i2c/aspeed_i2c.c index a8fbb9f44a..83fb906bdc 100644 --- a/hw/i2c/aspeed_i2c.c +++ b/hw/i2c/aspeed_i2c.c @@ -1263,7 +1263,7 @@ static const Property aspeed_i2c_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), }; -static void aspeed_i2c_class_init(ObjectClass *klass, void *data) +static void aspeed_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1390,7 +1390,8 @@ static void aspeed_i2c_bus_slave_send_async(I2CSlave *slave, uint8_t data) aspeed_i2c_bus_raise_interrupt(bus); } -static void aspeed_i2c_bus_slave_class_init(ObjectClass *klass, void *data) +static void aspeed_i2c_bus_slave_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); @@ -1451,7 +1452,7 @@ static const Property aspeed_i2c_bus_properties[] = { AspeedI2CState *), }; -static void aspeed_i2c_bus_class_init(ObjectClass *klass, void *data) +static void aspeed_i2c_bus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1483,7 +1484,7 @@ static uint8_t *aspeed_2400_i2c_bus_pool_base(AspeedI2CBus *bus) return &pool_page[ARRAY_FIELD_EX32(bus->regs, I2CD_POOL_CTRL, OFFSET)]; } -static void aspeed_2400_i2c_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); @@ -1517,7 +1518,7 @@ static uint8_t *aspeed_2500_i2c_bus_pool_base(AspeedI2CBus *bus) return bus->pool; } -static void aspeed_2500_i2c_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); @@ -1547,7 +1548,7 @@ static qemu_irq aspeed_2600_i2c_bus_get_irq(AspeedI2CBus *bus) return bus->irq; } -static void aspeed_2600_i2c_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); @@ -1571,7 +1572,7 @@ static const TypeInfo aspeed_2600_i2c_info = { .class_init = aspeed_2600_i2c_class_init, }; -static void aspeed_1030_i2c_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); @@ -1595,7 +1596,7 @@ static const TypeInfo aspeed_1030_i2c_info = { .class_init = aspeed_1030_i2c_class_init, }; -static void aspeed_2700_i2c_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedI2CClass *aic = ASPEED_I2C_CLASS(klass); diff --git a/hw/i2c/bcm2835_i2c.c b/hw/i2c/bcm2835_i2c.c index 67bfdef3b4..be11cca2a9 100644 --- a/hw/i2c/bcm2835_i2c.c +++ b/hw/i2c/bcm2835_i2c.c @@ -258,7 +258,7 @@ static const VMStateDescription vmstate_bcm2835_i2c = { } }; -static void bcm2835_i2c_class_init(ObjectClass *klass, void *data) +static void bcm2835_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i2c/bitbang_i2c.c b/hw/i2c/bitbang_i2c.c index de5f5aacf5..e020f314e2 100644 --- a/hw/i2c/bitbang_i2c.c +++ b/hw/i2c/bitbang_i2c.c @@ -222,7 +222,7 @@ static void gpio_i2c_init(Object *obj) qdev_init_gpio_out(dev, &s->out, 1); } -static void gpio_i2c_class_init(ObjectClass *klass, void *data) +static void gpio_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i2c/core.c b/hw/i2c/core.c index 26bb18514a..4b6345b588 100644 --- a/hw/i2c/core.c +++ b/hw/i2c/core.c @@ -401,7 +401,7 @@ static bool i2c_slave_match(I2CSlave *candidate, uint8_t address, return false; } -static void i2c_slave_class_init(ObjectClass *klass, void *data) +static void i2c_slave_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); diff --git a/hw/i2c/exynos4210_i2c.c b/hw/i2c/exynos4210_i2c.c index b1d00096ee..9d0c1cdaa8 100644 --- a/hw/i2c/exynos4210_i2c.c +++ b/hw/i2c/exynos4210_i2c.c @@ -309,7 +309,7 @@ static void exynos4210_i2c_init(Object *obj) s->bus = i2c_init_bus(dev, "i2c"); } -static void exynos4210_i2c_class_init(ObjectClass *klass, void *data) +static void exynos4210_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i2c/i2c_mux_pca954x.c b/hw/i2c/i2c_mux_pca954x.c index 779cc4e66e..a8ef640cd2 100644 --- a/hw/i2c/i2c_mux_pca954x.c +++ b/hw/i2c/i2c_mux_pca954x.c @@ -172,13 +172,13 @@ I2CBus *pca954x_i2c_get_bus(I2CSlave *mux, uint8_t channel) return pca954x->bus[channel]; } -static void pca9546_class_init(ObjectClass *klass, void *data) +static void pca9546_class_init(ObjectClass *klass, const void *data) { Pca954xClass *s = PCA954X_CLASS(klass); s->nchans = PCA9546_CHANNEL_COUNT; } -static void pca9548_class_init(ObjectClass *klass, void *data) +static void pca9548_class_init(ObjectClass *klass, const void *data) { Pca954xClass *s = PCA954X_CLASS(klass); s->nchans = PCA9548_CHANNEL_COUNT; @@ -215,7 +215,7 @@ static const Property pca954x_props[] = { DEFINE_PROP_STRING("name", Pca954xState, name), }; -static void pca954x_class_init(ObjectClass *klass, void *data) +static void pca954x_class_init(ObjectClass *klass, const void *data) { I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/i2c/imx_i2c.c b/hw/i2c/imx_i2c.c index d62213b9e0..91f84c2ad7 100644 --- a/hw/i2c/imx_i2c.c +++ b/hw/i2c/imx_i2c.c @@ -297,7 +297,7 @@ static void imx_i2c_realize(DeviceState *dev, Error **errp) s->bus = i2c_init_bus(dev, NULL); } -static void imx_i2c_class_init(ObjectClass *klass, void *data) +static void imx_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i2c/microbit_i2c.c b/hw/i2c/microbit_i2c.c index 06fbd18a78..2291d6370e 100644 --- a/hw/i2c/microbit_i2c.c +++ b/hw/i2c/microbit_i2c.c @@ -105,7 +105,7 @@ static void microbit_i2c_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static void microbit_i2c_class_init(ObjectClass *klass, void *data) +static void microbit_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i2c/mpc_i2c.c b/hw/i2c/mpc_i2c.c index 913d044ac1..25f91b7bc8 100644 --- a/hw/i2c/mpc_i2c.c +++ b/hw/i2c/mpc_i2c.c @@ -334,7 +334,7 @@ static void mpc_i2c_realize(DeviceState *dev, Error **errp) i2c->bus = i2c_init_bus(dev, "i2c"); } -static void mpc_i2c_class_init(ObjectClass *klass, void *data) +static void mpc_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i2c/npcm7xx_smbus.c b/hw/i2c/npcm7xx_smbus.c index 22d68fc67d..179852a4fd 100644 --- a/hw/i2c/npcm7xx_smbus.c +++ b/hw/i2c/npcm7xx_smbus.c @@ -1075,7 +1075,7 @@ static const VMStateDescription vmstate_npcm7xx_smbus = { }, }; -static void npcm7xx_smbus_class_init(ObjectClass *klass, void *data) +static void npcm7xx_smbus_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c index a641db2348..2e45266e74 100644 --- a/hw/i2c/omap_i2c.c +++ b/hw/i2c/omap_i2c.c @@ -515,7 +515,7 @@ static const Property omap_i2c_properties[] = { DEFINE_PROP_UINT8("revision", OMAPI2CState, revision, 0), }; -static void omap_i2c_class_init(ObjectClass *klass, void *data) +static void omap_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i2c/pmbus_device.c b/hw/i2c/pmbus_device.c index ba1d2fd716..853dc4b434 100644 --- a/hw/i2c/pmbus_device.c +++ b/hw/i2c/pmbus_device.c @@ -1902,7 +1902,7 @@ static void pmbus_device_finalize(Object *obj) g_free(pmdev->pages); } -static void pmbus_device_class_init(ObjectClass *klass, void *data) +static void pmbus_device_class_init(ObjectClass *klass, const void *data) { SMBusDeviceClass *k = SMBUS_DEVICE_CLASS(klass); diff --git a/hw/i2c/ppc4xx_i2c.c b/hw/i2c/ppc4xx_i2c.c index 7b124a7e33..09d4c49d65 100644 --- a/hw/i2c/ppc4xx_i2c.c +++ b/hw/i2c/ppc4xx_i2c.c @@ -354,7 +354,7 @@ static void ppc4xx_i2c_init(Object *o) bitbang_i2c_init(&s->bitbang, s->bus); } -static void ppc4xx_i2c_class_init(ObjectClass *klass, void *data) +static void ppc4xx_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i2c/smbus_eeprom.c b/hw/i2c/smbus_eeprom.c index e3e96d4a2d..0a1088fbb0 100644 --- a/hw/i2c/smbus_eeprom.c +++ b/hw/i2c/smbus_eeprom.c @@ -137,7 +137,7 @@ static void smbus_eeprom_realize(DeviceState *dev, Error **errp) } } -static void smbus_eeprom_class_initfn(ObjectClass *klass, void *data) +static void smbus_eeprom_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass); diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index 208f263ac5..f1fca30fea 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -118,7 +118,7 @@ static void build_ich9_smb_aml(AcpiDevAmlIf *adev, Aml *scope) qbus_build_aml(bus, scope); } -static void ich9_smb_class_init(ObjectClass *klass, void *data) +static void ich9_smb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/i2c/smbus_slave.c b/hw/i2c/smbus_slave.c index 9f9afc25a4..cfb61c879e 100644 --- a/hw/i2c/smbus_slave.c +++ b/hw/i2c/smbus_slave.c @@ -201,7 +201,7 @@ static int smbus_i2c_send(I2CSlave *s, uint8_t data) return 0; } -static void smbus_device_class_init(ObjectClass *klass, void *data) +static void smbus_device_class_init(ObjectClass *klass, const void *data) { I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 5f9b952799..b94802e21a 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1677,7 +1677,7 @@ static void amdvi_sysbus_instance_init(Object *klass) object_initialize(&s->pci, sizeof(s->pci), TYPE_AMD_IOMMU_PCI); } -static void amdvi_sysbus_class_init(ObjectClass *klass, void *data) +static void amdvi_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); X86IOMMUClass *dc_class = X86_IOMMU_DEVICE_CLASS(klass); @@ -1700,7 +1700,7 @@ static const TypeInfo amdvi_sysbus = { .class_init = amdvi_sysbus_class_init }; -static void amdvi_pci_class_init(ObjectClass *klass, void *data) +static void amdvi_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1725,7 +1725,8 @@ static const TypeInfo amdvi_pci = { }, }; -static void amdvi_iommu_memory_region_class_init(ObjectClass *klass, void *data) +static void amdvi_iommu_memory_region_class_init(ObjectClass *klass, + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 0608aec8c5..5f8ed1243d 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -4859,7 +4859,7 @@ static void vtd_realize(DeviceState *dev, Error **errp) qemu_add_machine_init_done_notifier(&vtd_machine_done_notify); } -static void vtd_class_init(ObjectClass *klass, void *data) +static void vtd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); X86IOMMUClass *x86_class = X86_IOMMU_DEVICE_CLASS(klass); @@ -4887,7 +4887,7 @@ static const TypeInfo vtd_info = { }; static void vtd_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c index 7575106000..39035db042 100644 --- a/hw/i386/kvm/apic.c +++ b/hw/i386/kvm/apic.c @@ -240,7 +240,7 @@ static void kvm_apic_unrealize(DeviceState *dev) { } -static void kvm_apic_class_init(ObjectClass *klass, void *data) +static void kvm_apic_class_init(ObjectClass *klass, const void *data) { APICCommonClass *k = APIC_COMMON_CLASS(klass); diff --git a/hw/i386/kvm/clock.c b/hw/i386/kvm/clock.c index 17443552e9..f56382717f 100644 --- a/hw/i386/kvm/clock.c +++ b/hw/i386/kvm/clock.c @@ -309,7 +309,7 @@ static const Property kvmclock_properties[] = { mach_use_reliable_get_clock, true), }; -static void kvmclock_class_init(ObjectClass *klass, void *data) +static void kvmclock_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i386/kvm/i8254.c b/hw/i386/kvm/i8254.c index 3b92771c79..14b78f30a8 100644 --- a/hw/i386/kvm/i8254.c +++ b/hw/i386/kvm/i8254.c @@ -292,7 +292,7 @@ static const Property kvm_pit_properties[] = { lost_tick_policy, LOST_TICK_POLICY_DELAY), }; -static void kvm_pit_class_init(ObjectClass *klass, void *data) +static void kvm_pit_class_init(ObjectClass *klass, const void *data) { KVMPITClass *kpc = KVM_PIT_CLASS(klass); PITCommonClass *k = PIT_COMMON_CLASS(klass); diff --git a/hw/i386/kvm/i8259.c b/hw/i386/kvm/i8259.c index 272c04df0b..8a72d6e4dd 100644 --- a/hw/i386/kvm/i8259.c +++ b/hw/i386/kvm/i8259.c @@ -139,7 +139,7 @@ qemu_irq *kvm_i8259_init(ISABus *bus) return qemu_allocate_irqs(kvm_pic_set_irq, NULL, ISA_NUM_IRQS); } -static void kvm_i8259_class_init(ObjectClass *klass, void *data) +static void kvm_i8259_class_init(ObjectClass *klass, const void *data) { KVMPICClass *kpc = KVM_PIC_CLASS(klass); PICCommonClass *k = PIC_COMMON_CLASS(klass); diff --git a/hw/i386/kvm/ioapic.c b/hw/i386/kvm/ioapic.c index 5419e191b5..693ee978a1 100644 --- a/hw/i386/kvm/ioapic.c +++ b/hw/i386/kvm/ioapic.c @@ -137,7 +137,7 @@ static const Property kvm_ioapic_properties[] = { DEFINE_PROP_UINT32("gsi_base", KVMIOAPICState, kvm_gsi_base, 0), }; -static void kvm_ioapic_class_init(ObjectClass *klass, void *data) +static void kvm_ioapic_class_init(ObjectClass *klass, const void *data) { IOAPICCommonClass *k = IOAPIC_COMMON_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index f9223ef1a1..b5190549a8 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -271,7 +271,7 @@ static const VMStateDescription xen_evtchn_vmstate = { } }; -static void xen_evtchn_class_init(ObjectClass *klass, void *data) +static void xen_evtchn_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i386/kvm/xen_gnttab.c b/hw/i386/kvm/xen_gnttab.c index 430ba62896..4b9e272c5e 100644 --- a/hw/i386/kvm/xen_gnttab.c +++ b/hw/i386/kvm/xen_gnttab.c @@ -135,7 +135,7 @@ static const VMStateDescription xen_gnttab_vmstate = { } }; -static void xen_gnttab_class_init(ObjectClass *klass, void *data) +static void xen_gnttab_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i386/kvm/xen_overlay.c b/hw/i386/kvm/xen_overlay.c index a2b26e9906..3cb7361937 100644 --- a/hw/i386/kvm/xen_overlay.c +++ b/hw/i386/kvm/xen_overlay.c @@ -151,7 +151,7 @@ static void xen_overlay_reset(DeviceState *dev) kvm_xen_soft_reset(); } -static void xen_overlay_class_init(ObjectClass *klass, void *data) +static void xen_overlay_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i386/kvm/xen_primary_console.c b/hw/i386/kvm/xen_primary_console.c index 8ad2363d18..6e9d6417c3 100644 --- a/hw/i386/kvm/xen_primary_console.c +++ b/hw/i386/kvm/xen_primary_console.c @@ -67,7 +67,7 @@ static void xen_primary_console_realize(DeviceState *dev, Error **errp) xen_primary_console_singleton = s; } -static void xen_primary_console_class_init(ObjectClass *klass, void *data) +static void xen_primary_console_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i386/kvm/xen_xenstore.c b/hw/i386/kvm/xen_xenstore.c index 227ad7ace3..42955cccd9 100644 --- a/hw/i386/kvm/xen_xenstore.c +++ b/hw/i386/kvm/xen_xenstore.c @@ -259,7 +259,7 @@ static const VMStateDescription xen_xenstore_vmstate = { } }; -static void xen_xenstore_class_init(ObjectClass *klass, void *data) +static void xen_xenstore_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index d0a236c74f..14a918a531 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -635,7 +635,7 @@ GlobalProperty microvm_properties[] = { { "pcie-root-port", "io-reserve", "0" }, }; -static void microvm_class_init(ObjectClass *oc, void *data) +static void microvm_class_init(ObjectClass *oc, const void *data) { X86MachineClass *x86mc = X86_MACHINE_CLASS(oc); MicrovmMachineClass *mmc = MICROVM_MACHINE_CLASS(oc); diff --git a/hw/i386/nitro_enclave.c b/hw/i386/nitro_enclave.c index 4b69f265cc..5ee50f3b85 100644 --- a/hw/i386/nitro_enclave.c +++ b/hw/i386/nitro_enclave.c @@ -293,7 +293,7 @@ static void nitro_enclave_set_parent_id(Object *obj, const char *value, nems->parent_id = g_strdup(value); } -static void nitro_enclave_class_init(ObjectClass *oc, void *data) +static void nitro_enclave_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); MicrovmMachineClass *mmc = MICROVM_MACHINE_CLASS(oc); diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 5481fe40be..49753bf0b3 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1766,7 +1766,7 @@ static bool pc_hotplug_allowed(MachineState *ms, DeviceState *dev, Error **errp) return true; } -static void pc_machine_class_init(ObjectClass *oc, void *data) +static void pc_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); X86MachineClass *x86mc = X86_MACHINE_CLASS(oc); diff --git a/hw/i386/port92.c b/hw/i386/port92.c index 1ba3f32887..39b6f3178f 100644 --- a/hw/i386/port92.c +++ b/hw/i386/port92.c @@ -97,7 +97,7 @@ static void port92_realizefn(DeviceState *dev, Error **errp) isa_register_ioport(isadev, &s->io, 0x92); } -static void port92_class_initfn(ObjectClass *klass, void *data) +static void port92_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i386/sgx-epc.c b/hw/i386/sgx-epc.c index 00b220d4d6..8fb80900b7 100644 --- a/hw/i386/sgx-epc.c +++ b/hw/i386/sgx-epc.c @@ -147,7 +147,7 @@ static void sgx_epc_md_fill_device_info(const MemoryDeviceState *md, info->type = MEMORY_DEVICE_INFO_KIND_SGX_EPC; } -static void sgx_epc_class_init(ObjectClass *oc, void *data) +static void sgx_epc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc); diff --git a/hw/i386/vapic.c b/hw/i386/vapic.c index 347431eeef..0c1c92c479 100644 --- a/hw/i386/vapic.c +++ b/hw/i386/vapic.c @@ -847,7 +847,7 @@ static const VMStateDescription vmstate_vapic = { } }; -static void vapic_class_init(ObjectClass *klass, void *data) +static void vapic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i386/vmmouse.c b/hw/i386/vmmouse.c index 3e07d12512..3896159b05 100644 --- a/hw/i386/vmmouse.c +++ b/hw/i386/vmmouse.c @@ -321,7 +321,7 @@ static const Property vmmouse_properties[] = { DEFINE_PROP_LINK("i8042", VMMouseState, i8042, TYPE_I8042, ISAKBDState *), }; -static void vmmouse_class_initfn(ObjectClass *klass, void *data) +static void vmmouse_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i386/vmport.c b/hw/i386/vmport.c index 2f19b970b5..6d93457c52 100644 --- a/hw/i386/vmport.c +++ b/hw/i386/vmport.c @@ -286,7 +286,7 @@ static const Property vmport_properties[] = { DEFINE_PROP_UINT8("vmware-vmx-type", VMPortState, vmware_vmx_type, 2), }; -static void vmport_class_initfn(ObjectClass *klass, void *data) +static void vmport_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/i386/x86-iommu.c b/hw/i386/x86-iommu.c index 5cdd165af0..d34a6849f4 100644 --- a/hw/i386/x86-iommu.c +++ b/hw/i386/x86-iommu.c @@ -132,7 +132,7 @@ static const Property x86_iommu_properties[] = { DEFINE_PROP_BOOL("pt", X86IOMMUState, pt_supported, true), }; -static void x86_iommu_class_init(ObjectClass *klass, void *data) +static void x86_iommu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = x86_iommu_realize; diff --git a/hw/i386/x86.c b/hw/i386/x86.c index 69bfc00b9a..c8e2551b2b 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -372,7 +372,7 @@ static void x86_machine_initfn(Object *obj) x86ms->above_4g_mem_start = 4 * GiB; } -static void x86_machine_class_init(ObjectClass *oc, void *data) +static void x86_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); X86MachineClass *x86mc = X86_MACHINE_CLASS(oc); diff --git a/hw/i386/xen/xen-pvh.c b/hw/i386/xen/xen-pvh.c index f6356f2a7e..067f73e977 100644 --- a/hw/i386/xen/xen-pvh.c +++ b/hw/i386/xen/xen-pvh.c @@ -76,7 +76,7 @@ static void xen_pvh_set_pci_intx_irq(void *opaque, int irq, int level) } } -static void xen_pvh_machine_class_init(ObjectClass *oc, void *data) +static void xen_pvh_machine_class_init(ObjectClass *oc, const void *data) { XenPVHMachineClass *xpc = XEN_PVH_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/i386/xen/xen_apic.c b/hw/i386/xen/xen_apic.c index a94e9005cb..f30398fa4a 100644 --- a/hw/i386/xen/xen_apic.c +++ b/hw/i386/xen/xen_apic.c @@ -76,7 +76,7 @@ static void xen_send_msi(MSIMessage *msi) xen_hvm_inject_msi(msi->address, msi->data); } -static void xen_apic_class_init(ObjectClass *klass, void *data) +static void xen_apic_class_init(ObjectClass *klass, const void *data) { APICCommonClass *k = APIC_COMMON_CLASS(klass); diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index dd648a2ee9..7c0d345f96 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -581,7 +581,7 @@ static void platform_reset(DeviceState *dev) platform_fixed_ioport_reset(s); } -static void xen_platform_class_init(ObjectClass *klass, void *data) +static void xen_platform_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/i386/xen/xen_pvdevice.c b/hw/i386/xen/xen_pvdevice.c index 9453da97bd..65868bd5e5 100644 --- a/hw/i386/xen/xen_pvdevice.c +++ b/hw/i386/xen/xen_pvdevice.c @@ -122,7 +122,7 @@ static const Property xen_pv_props[] = { DEFINE_PROP_UINT32("size", XenPVDevice, size, 0x400000), }; -static void xen_pv_class_init(ObjectClass *klass, void *data) +static void xen_pv_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/ide/ahci-allwinner.c b/hw/ide/ahci-allwinner.c index 575be36fc5..bc7a116a89 100644 --- a/hw/ide/ahci-allwinner.c +++ b/hw/ide/ahci-allwinner.c @@ -103,7 +103,7 @@ static const VMStateDescription vmstate_allwinner_ahci = { } }; -static void allwinner_ahci_class_init(ObjectClass *klass, void *data) +static void allwinner_ahci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ide/ahci-sysbus.c b/hw/ide/ahci-sysbus.c index 3c1935d81c..210818d047 100644 --- a/hw/ide/ahci-sysbus.c +++ b/hw/ide/ahci-sysbus.c @@ -66,7 +66,7 @@ static const Property sysbus_ahci_properties[] = { DEFINE_PROP_UINT32("num-ports", SysbusAHCIState, ahci.ports, 1), }; -static void sysbus_ahci_class_init(ObjectClass *klass, void *data) +static void sysbus_ahci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ide/cf.c b/hw/ide/cf.c index cfb4394f80..f87cd413b6 100644 --- a/hw/ide/cf.c +++ b/hw/ide/cf.c @@ -31,7 +31,7 @@ static const Property ide_cf_properties[] = { IDEDrive, dev.chs_trans, BIOS_ATA_TRANSLATION_AUTO), }; -static void ide_cf_class_init(ObjectClass *klass, void *data) +static void ide_cf_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IDEDeviceClass *k = IDE_DEVICE_CLASS(klass); diff --git a/hw/ide/cmd646.c b/hw/ide/cmd646.c index 8e568e4c35..2a59516a9d 100644 --- a/hw/ide/cmd646.c +++ b/hw/ide/cmd646.c @@ -317,7 +317,7 @@ static const Property cmd646_ide_properties[] = { DEFINE_PROP_UINT32("secondary", PCIIDEState, secondary, 0), }; -static void cmd646_ide_class_init(ObjectClass *klass, void *data) +static void cmd646_ide_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/ide/ich.c b/hw/ide/ich.c index a83128465f..f2773ab462 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -175,7 +175,7 @@ static void pci_ich9_uninit(PCIDevice *dev) ahci_uninit(&d->ahci); } -static void ich_ahci_class_init(ObjectClass *klass, void *data) +static void ich_ahci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/ide/ide-bus.c b/hw/ide/ide-bus.c index 437502b5b4..b24e4d17b4 100644 --- a/hw/ide/ide-bus.c +++ b/hw/ide/ide-bus.c @@ -29,7 +29,7 @@ static char *idebus_get_fw_dev_path(DeviceState *dev); static void idebus_unrealize(BusState *qdev); -static void ide_bus_class_init(ObjectClass *klass, void *data) +static void ide_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); diff --git a/hw/ide/ide-dev.c b/hw/ide/ide-dev.c index 26f0517019..5d478588c6 100644 --- a/hw/ide/ide-dev.c +++ b/hw/ide/ide-dev.c @@ -198,7 +198,7 @@ static const Property ide_hd_properties[] = { DEFINE_PROP_UINT16("rotation_rate", IDEDrive, dev.rotation_rate, 0), }; -static void ide_hd_class_init(ObjectClass *klass, void *data) +static void ide_hd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IDEDeviceClass *k = IDE_DEVICE_CLASS(klass); @@ -220,7 +220,7 @@ static const Property ide_cd_properties[] = { DEFINE_IDE_DEV_PROPERTIES(), }; -static void ide_cd_class_init(ObjectClass *klass, void *data) +static void ide_cd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IDEDeviceClass *k = IDE_DEVICE_CLASS(klass); @@ -238,7 +238,7 @@ static const TypeInfo ide_cd_info = { .class_init = ide_cd_class_init, }; -static void ide_device_class_init(ObjectClass *klass, void *data) +static void ide_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = ide_qdev_realize; diff --git a/hw/ide/isa.c b/hw/ide/isa.c index 4863ad8080..5f418413c1 100644 --- a/hw/ide/isa.c +++ b/hw/ide/isa.c @@ -107,7 +107,7 @@ static const Property isa_ide_properties[] = { DEFINE_PROP_UINT32("irq", ISAIDEState, irqnum, 14), }; -static void isa_ide_class_initfn(ObjectClass *klass, void *data) +static void isa_ide_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ide/macio.c b/hw/ide/macio.c index c8e8e44cc9..c23bf32d2b 100644 --- a/hw/ide/macio.c +++ b/hw/ide/macio.c @@ -463,7 +463,7 @@ static const Property macio_ide_properties[] = { DEFINE_PROP_UINT32("addr", MACIOIDEState, addr, -1), }; -static void macio_ide_class_init(ObjectClass *oc, void *data) +static void macio_ide_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ide/mmio.c b/hw/ide/mmio.c index 13f16170ff..699874db78 100644 --- a/hw/ide/mmio.c +++ b/hw/ide/mmio.c @@ -145,7 +145,7 @@ static const Property mmio_ide_properties[] = { DEFINE_PROP_UINT32("shift", MMIOIDEState, shift, 0), }; -static void mmio_ide_class_init(ObjectClass *oc, void *data) +static void mmio_ide_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ide/piix.c b/hw/ide/piix.c index 818ff60d6f..a0f2709c69 100644 --- a/hw/ide/piix.c +++ b/hw/ide/piix.c @@ -178,7 +178,7 @@ static void pci_piix_ide_exitfn(PCIDevice *dev) } /* NOTE: for the PIIX3, the IRQs and IOports are hardcoded */ -static void piix3_ide_class_init(ObjectClass *klass, void *data) +static void piix3_ide_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -201,7 +201,7 @@ static const TypeInfo piix3_ide_info = { }; /* NOTE: for the PIIX4, the IRQs and IOports are hardcoded */ -static void piix4_ide_class_init(ObjectClass *klass, void *data) +static void piix4_ide_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/ide/sii3112.c b/hw/ide/sii3112.c index ce8a1e4cba..9b28c691fd 100644 --- a/hw/ide/sii3112.c +++ b/hw/ide/sii3112.c @@ -290,7 +290,7 @@ static void sii3112_pci_realize(PCIDevice *dev, Error **errp) } } -static void sii3112_pci_class_init(ObjectClass *klass, void *data) +static void sii3112_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pd = PCI_DEVICE_CLASS(klass); diff --git a/hw/ide/via.c b/hw/ide/via.c index 89fd28f646..dedc2674c0 100644 --- a/hw/ide/via.c +++ b/hw/ide/via.c @@ -245,7 +245,7 @@ static void via_ide_exitfn(PCIDevice *dev) } } -static void via_ide_class_init(ObjectClass *klass, void *data) +static void via_ide_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/input/adb-kbd.c b/hw/input/adb-kbd.c index 3649d03ef2..507557deec 100644 --- a/hw/input/adb-kbd.c +++ b/hw/input/adb-kbd.c @@ -375,7 +375,7 @@ static void adb_kbd_initfn(Object *obj) d->devaddr = ADB_DEVID_KEYBOARD; } -static void adb_kbd_class_init(ObjectClass *oc, void *data) +static void adb_kbd_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc); diff --git a/hw/input/adb-mouse.c b/hw/input/adb-mouse.c index 77b280d242..373ef3f953 100644 --- a/hw/input/adb-mouse.c +++ b/hw/input/adb-mouse.c @@ -287,7 +287,7 @@ static void adb_mouse_initfn(Object *obj) d->devaddr = ADB_DEVID_MOUSE; } -static void adb_mouse_class_init(ObjectClass *oc, void *data) +static void adb_mouse_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ADBDeviceClass *adc = ADB_DEVICE_CLASS(oc); diff --git a/hw/input/adb.c b/hw/input/adb.c index aff7130fd0..bcb11edca3 100644 --- a/hw/input/adb.c +++ b/hw/input/adb.c @@ -259,7 +259,7 @@ static void adb_bus_unrealize(BusState *qbus) vmstate_unregister(NULL, &vmstate_adb_bus, adb_bus); } -static void adb_bus_class_init(ObjectClass *klass, void *data) +static void adb_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -299,7 +299,7 @@ static void adb_device_realizefn(DeviceState *dev, Error **errp) bus->devices[bus->nb_devices++] = d; } -static void adb_device_class_init(ObjectClass *oc, void *data) +static void adb_device_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/input/lasips2.c b/hw/input/lasips2.c index 987034efd3..de625723c7 100644 --- a/hw/input/lasips2.c +++ b/hw/input/lasips2.c @@ -306,7 +306,7 @@ static void lasips2_init(Object *obj) "lasips2-port-input-irq", 2); } -static void lasips2_class_init(ObjectClass *klass, void *data) +static void lasips2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -347,7 +347,7 @@ static void lasips2_port_init(Object *obj) "ps2-input-irq", 1); } -static void lasips2_port_class_init(ObjectClass *klass, void *data) +static void lasips2_port_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -397,7 +397,7 @@ static void lasips2_kbd_port_init(Object *obj) lp->lasips2 = container_of(s, LASIPS2State, kbd_port); } -static void lasips2_kbd_port_class_init(ObjectClass *klass, void *data) +static void lasips2_kbd_port_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LASIPS2PortDeviceClass *lpdc = LASIPS2_PORT_CLASS(klass); @@ -447,7 +447,7 @@ static void lasips2_mouse_port_init(Object *obj) lp->lasips2 = container_of(s, LASIPS2State, mouse_port); } -static void lasips2_mouse_port_class_init(ObjectClass *klass, void *data) +static void lasips2_mouse_port_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LASIPS2PortDeviceClass *lpdc = LASIPS2_PORT_CLASS(klass); diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index fa0c549eb9..83930dd50c 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -750,7 +750,7 @@ static const VMStateDescription vmstate_kbd_mmio = { } }; -static void i8042_mmio_class_init(ObjectClass *klass, void *data) +static void i8042_mmio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -939,7 +939,7 @@ static const Property i8042_properties[] = { DEFINE_PROP_UINT8("mouse-irq", ISAKBDState, mouse_irq, 12), }; -static void i8042_class_initfn(ObjectClass *klass, void *data) +static void i8042_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); diff --git a/hw/input/pl050.c b/hw/input/pl050.c index 6519e260ed..c5f4a3fa84 100644 --- a/hw/input/pl050.c +++ b/hw/input/pl050.c @@ -203,7 +203,7 @@ static void pl050_mouse_init(Object *obj) object_initialize_child(obj, "mouse", &s->mouse, TYPE_PS2_MOUSE_DEVICE); } -static void pl050_kbd_class_init(ObjectClass *oc, void *data) +static void pl050_kbd_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PL050DeviceClass *pdc = PL050_CLASS(oc); @@ -220,7 +220,7 @@ static const TypeInfo pl050_kbd_info = { .class_init = pl050_kbd_class_init, }; -static void pl050_mouse_class_init(ObjectClass *oc, void *data) +static void pl050_mouse_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PL050DeviceClass *pdc = PL050_CLASS(oc); @@ -249,7 +249,7 @@ static void pl050_init(Object *obj) qdev_init_gpio_in_named(DEVICE(obj), pl050_set_irq, "ps2-input-irq", 1); } -static void pl050_class_init(ObjectClass *oc, void *data) +static void pl050_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/input/ps2.c b/hw/input/ps2.c index 6a41b024c8..7f7b1fce2e 100644 --- a/hw/input/ps2.c +++ b/hw/input/ps2.c @@ -1254,7 +1254,7 @@ static void ps2_mouse_realize(DeviceState *dev, Error **errp) qemu_input_handler_register(dev, &ps2_mouse_handler); } -static void ps2_kbd_class_init(ObjectClass *klass, void *data) +static void ps2_kbd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -1273,7 +1273,7 @@ static const TypeInfo ps2_kbd_info = { .class_init = ps2_kbd_class_init }; -static void ps2_mouse_class_init(ObjectClass *klass, void *data) +static void ps2_mouse_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -1299,7 +1299,7 @@ static void ps2_init(Object *obj) qdev_init_gpio_out(DEVICE(obj), &s->irq, 1); } -static void ps2_class_init(ObjectClass *klass, void *data) +static void ps2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/input/stellaris_gamepad.c b/hw/input/stellaris_gamepad.c index 98382a0e15..fec1161c9c 100644 --- a/hw/input/stellaris_gamepad.c +++ b/hw/input/stellaris_gamepad.c @@ -82,7 +82,7 @@ static const Property stellaris_gamepad_properties[] = { keycodes, qdev_prop_uint32, uint32_t), }; -static void stellaris_gamepad_class_init(ObjectClass *klass, void *data) +static void stellaris_gamepad_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/input/virtio-input-hid.c b/hw/input/virtio-input-hid.c index 812faaef8f..d986c3c16e 100644 --- a/hw/input/virtio-input-hid.c +++ b/hw/input/virtio-input-hid.c @@ -242,7 +242,7 @@ static const Property virtio_input_hid_properties[] = { DEFINE_PROP_UINT32("head", VirtIOInputHID, head, 0), }; -static void virtio_input_hid_class_init(ObjectClass *klass, void *data) +static void virtio_input_hid_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass); @@ -383,7 +383,7 @@ static const Property virtio_mouse_properties[] = { DEFINE_PROP_BOOL("wheel-axis", VirtIOInputHID, wheel_axis, true), }; -static void virtio_mouse_class_init(ObjectClass *klass, void *data) +static void virtio_mouse_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -507,7 +507,7 @@ static const Property virtio_tablet_properties[] = { DEFINE_PROP_BOOL("wheel-axis", VirtIOInputHID, wheel_axis, true), }; -static void virtio_tablet_class_init(ObjectClass *klass, void *data) +static void virtio_tablet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/input/virtio-input-host.c b/hw/input/virtio-input-host.c index b21a79046e..bbfee9d3b9 100644 --- a/hw/input/virtio-input-host.c +++ b/hw/input/virtio-input-host.c @@ -224,7 +224,7 @@ static const Property virtio_input_host_properties[] = { DEFINE_PROP_STRING("evdev", VirtIOInputHost, evdev), }; -static void virtio_input_host_class_init(ObjectClass *klass, void *data) +static void virtio_input_host_class_init(ObjectClass *klass, const void *data) { VirtIOInputClass *vic = VIRTIO_INPUT_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c index 1394d99c6b..1818cbddc7 100644 --- a/hw/input/virtio-input.c +++ b/hw/input/virtio-input.c @@ -304,7 +304,7 @@ static const Property virtio_input_properties[] = { DEFINE_PROP_STRING("serial", VirtIOInput, serial), }; -static void virtio_input_class_init(ObjectClass *klass, void *data) +static void virtio_input_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/intc/allwinner-a10-pic.c b/hw/intc/allwinner-a10-pic.c index 93a604f7a0..0409734155 100644 --- a/hw/intc/allwinner-a10-pic.c +++ b/hw/intc/allwinner-a10-pic.c @@ -187,7 +187,7 @@ static void aw_a10_pic_reset(DeviceState *d) } } -static void aw_a10_pic_class_init(ObjectClass *klass, void *data) +static void aw_a10_pic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/apic.c b/hw/intc/apic.c index d18c1dbf2c..bcb103560c 100644 --- a/hw/intc/apic.c +++ b/hw/intc/apic.c @@ -1176,7 +1176,7 @@ static void apic_unrealize(DeviceState *dev) local_apics[s->initial_apic_id] = NULL; } -static void apic_class_init(ObjectClass *klass, void *data) +static void apic_class_init(ObjectClass *klass, const void *data) { APICCommonClass *k = APIC_COMMON_CLASS(klass); diff --git a/hw/intc/apic_common.c b/hw/intc/apic_common.c index 2a3e878c4d..37a7a7019d 100644 --- a/hw/intc/apic_common.c +++ b/hw/intc/apic_common.c @@ -466,7 +466,7 @@ static void apic_common_initfn(Object *obj) apic_common_set_id, NULL, NULL); } -static void apic_common_class_init(ObjectClass *klass, void *data) +static void apic_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index 3581ff8e8a..d18bef40fc 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -2162,7 +2162,7 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) } -static void arm_gic_class_init(ObjectClass *klass, void *data) +static void arm_gic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ARMGICClass *agc = ARM_GIC_CLASS(klass); diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 5ac56e3389..f61d1c1fe6 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -362,7 +362,7 @@ static const Property arm_gic_common_properties[] = { DEFINE_PROP_UINT32("num-priority-bits", GICState, n_prio_bits, 8), }; -static void arm_gic_common_class_init(ObjectClass *klass, void *data) +static void arm_gic_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/arm_gic_kvm.c b/hw/intc/arm_gic_kvm.c index 40adb02865..1e9232f47c 100644 --- a/hw/intc/arm_gic_kvm.c +++ b/hw/intc/arm_gic_kvm.c @@ -584,7 +584,7 @@ static void kvm_arm_gic_realize(DeviceState *dev, Error **errp) } } -static void kvm_arm_gic_class_init(ObjectClass *klass, void *data) +static void kvm_arm_gic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/arm_gicv2m.c b/hw/intc/arm_gicv2m.c index 3a8c62698c..cef0688221 100644 --- a/hw/intc/arm_gicv2m.c +++ b/hw/intc/arm_gicv2m.c @@ -175,7 +175,7 @@ static const Property gicv2m_properties[] = { DEFINE_PROP_UINT32("num-spi", ARMGICv2mState, num_spi, 64), }; -static void gicv2m_class_init(ObjectClass *klass, void *data) +static void gicv2m_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/arm_gicv3.c b/hw/intc/arm_gicv3.c index 58e18fff54..6059ce926a 100644 --- a/hw/intc/arm_gicv3.c +++ b/hw/intc/arm_gicv3.c @@ -452,7 +452,7 @@ static void arm_gic_realize(DeviceState *dev, Error **errp) gicv3_init_cpuif(s); } -static void arm_gicv3_class_init(ObjectClass *klass, void *data) +static void arm_gicv3_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ARMGICv3CommonClass *agcc = ARM_GICV3_COMMON_CLASS(klass); diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 76b2283c92..dd86a50300 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -623,7 +623,7 @@ static const Property arm_gicv3_common_properties[] = { MemoryRegion *), }; -static void arm_gicv3_common_class_init(ObjectClass *klass, void *data) +static void arm_gicv3_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/arm_gicv3_its.c b/hw/intc/arm_gicv3_its.c index 936368c901..577b445405 100644 --- a/hw/intc/arm_gicv3_its.c +++ b/hw/intc/arm_gicv3_its.c @@ -2007,7 +2007,7 @@ static const Property gicv3_its_props[] = { GICv3State *), }; -static void gicv3_its_class_init(ObjectClass *klass, void *data) +static void gicv3_its_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/arm_gicv3_its_common.c b/hw/intc/arm_gicv3_its_common.c index 70dbee83a6..e946e3fb87 100644 --- a/hw/intc/arm_gicv3_its_common.c +++ b/hw/intc/arm_gicv3_its_common.c @@ -135,7 +135,7 @@ static void gicv3_its_common_reset_hold(Object *obj, ResetType type) memset(&s->baser, 0, sizeof(s->baser)); } -static void gicv3_its_common_class_init(ObjectClass *klass, void *data) +static void gicv3_its_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/arm_gicv3_its_kvm.c b/hw/intc/arm_gicv3_its_kvm.c index e198974560..9812d50859 100644 --- a/hw/intc/arm_gicv3_its_kvm.c +++ b/hw/intc/arm_gicv3_its_kvm.c @@ -239,7 +239,7 @@ static const Property kvm_arm_its_props[] = { GICv3State *), }; -static void kvm_arm_its_class_init(ObjectClass *klass, void *data) +static void kvm_arm_its_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 8e17cab2a0..3be3bf6c28 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -893,7 +893,7 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) } } -static void kvm_arm_gicv3_class_init(ObjectClass *klass, void *data) +static void kvm_arm_gicv3_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 7212c87c68..83ff74f899 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -2730,7 +2730,7 @@ static void armv7m_nvic_instance_init(Object *obj) qdev_init_gpio_in_named(dev, nvic_nmi_trigger, "NMI", 1); } -static void armv7m_nvic_class_init(ObjectClass *klass, void *data) +static void armv7m_nvic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index bae7dc95ea..be7f516a3b 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -583,7 +583,7 @@ static void aspeed_intc_unrealize(DeviceState *dev) s->regs = NULL; } -static void aspeed_intc_class_init(ObjectClass *klass, void *data) +static void aspeed_intc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); @@ -620,7 +620,7 @@ static AspeedINTCIRQ aspeed_2700_intc_irqs[ASPEED_INTC_MAX_INPINS] = { {9, 18, 1, R_GICINT136_EN, R_GICINT136_STATUS}, }; -static void aspeed_2700_intc_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_intc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); @@ -651,7 +651,7 @@ static AspeedINTCIRQ aspeed_2700_intcio_irqs[ASPEED_INTC_MAX_INPINS] = { {5, 5, 1, R_GICINT197_EN, R_GICINT197_STATUS}, }; -static void aspeed_2700_intcio_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_intcio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); diff --git a/hw/intc/aspeed_vic.c b/hw/intc/aspeed_vic.c index 55fe51a667..7120088454 100644 --- a/hw/intc/aspeed_vic.c +++ b/hw/intc/aspeed_vic.c @@ -339,7 +339,7 @@ static const VMStateDescription vmstate_aspeed_vic = { } }; -static void aspeed_vic_class_init(ObjectClass *klass, void *data) +static void aspeed_vic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_vic_realize; diff --git a/hw/intc/bcm2835_ic.c b/hw/intc/bcm2835_ic.c index 4a42fcf60d..55e0a5a503 100644 --- a/hw/intc/bcm2835_ic.c +++ b/hw/intc/bcm2835_ic.c @@ -219,7 +219,7 @@ static const VMStateDescription vmstate_bcm2835_ic = { } }; -static void bcm2835_ic_class_init(ObjectClass *klass, void *data) +static void bcm2835_ic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/bcm2836_control.c b/hw/intc/bcm2836_control.c index 197a0e2ccf..1c02853669 100644 --- a/hw/intc/bcm2836_control.c +++ b/hw/intc/bcm2836_control.c @@ -384,7 +384,7 @@ static const VMStateDescription vmstate_bcm2836_control = { } }; -static void bcm2836_control_class_init(ObjectClass *klass, void *data) +static void bcm2836_control_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/exynos4210_combiner.c b/hw/intc/exynos4210_combiner.c index 6ddbcd4c6d..ebbe23436f 100644 --- a/hw/intc/exynos4210_combiner.c +++ b/hw/intc/exynos4210_combiner.c @@ -329,7 +329,7 @@ static const Property exynos4210_combiner_properties[] = { DEFINE_PROP_UINT32("external", Exynos4210CombinerState, external, 0), }; -static void exynos4210_combiner_class_init(ObjectClass *klass, void *data) +static void exynos4210_combiner_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/exynos4210_gic.c b/hw/intc/exynos4210_gic.c index 01a53936d3..7e2d79d00c 100644 --- a/hw/intc/exynos4210_gic.c +++ b/hw/intc/exynos4210_gic.c @@ -115,7 +115,7 @@ static const Property exynos4210_gic_properties[] = { DEFINE_PROP_UINT32("num-cpu", Exynos4210GicState, num_cpu, 1), }; -static void exynos4210_gic_class_init(ObjectClass *klass, void *data) +static void exynos4210_gic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/goldfish_pic.c b/hw/intc/goldfish_pic.c index aa5162c18f..b80538cdeb 100644 --- a/hw/intc/goldfish_pic.c +++ b/hw/intc/goldfish_pic.c @@ -185,7 +185,7 @@ static const Property goldfish_pic_properties[] = { DEFINE_PROP_UINT8("index", GoldfishPICState, idx, 0), }; -static void goldfish_pic_class_init(ObjectClass *oc, void *data) +static void goldfish_pic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(oc); diff --git a/hw/intc/grlib_irqmp.c b/hw/intc/grlib_irqmp.c index 95cdb411d2..e0f26466ba 100644 --- a/hw/intc/grlib_irqmp.c +++ b/hw/intc/grlib_irqmp.c @@ -380,7 +380,7 @@ static const Property grlib_irqmp_properties[] = { DEFINE_PROP_UINT32("ncpus", IRQMP, ncpus, 1), }; -static void grlib_irqmp_class_init(ObjectClass *klass, void *data) +static void grlib_irqmp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/heathrow_pic.c b/hw/intc/heathrow_pic.c index 729498f1df..447e8c25d8 100644 --- a/hw/intc/heathrow_pic.c +++ b/hw/intc/heathrow_pic.c @@ -184,7 +184,7 @@ static void heathrow_init(Object *obj) sysbus_init_mmio(sbd, &s->mem); } -static void heathrow_class_init(ObjectClass *oc, void *data) +static void heathrow_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/intc/i8259.c b/hw/intc/i8259.c index d88b20f40b..2359dd8253 100644 --- a/hw/intc/i8259.c +++ b/hw/intc/i8259.c @@ -436,7 +436,7 @@ qemu_irq *i8259_init(ISABus *bus, qemu_irq parent_irq_in) return irq_set; } -static void i8259_class_init(ObjectClass *klass, void *data) +static void i8259_class_init(ObjectClass *klass, const void *data) { PICClass *k = PIC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c index c77ff683bb..4a92e0da90 100644 --- a/hw/intc/i8259_common.c +++ b/hw/intc/i8259_common.c @@ -200,7 +200,7 @@ static const Property pic_properties_common[] = { DEFINE_PROP_BIT("master", PICCommonState, master, 0, false), }; -static void pic_common_class_init(ObjectClass *klass, void *data) +static void pic_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass); diff --git a/hw/intc/imx_avic.c b/hw/intc/imx_avic.c index e1c9ce769d..09c3bfac0b 100644 --- a/hw/intc/imx_avic.c +++ b/hw/intc/imx_avic.c @@ -341,7 +341,7 @@ static void imx_avic_init(Object *obj) } -static void imx_avic_class_init(ObjectClass *klass, void *data) +static void imx_avic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/imx_gpcv2.c b/hw/intc/imx_gpcv2.c index 9e5cf28371..58d286c1cf 100644 --- a/hw/intc/imx_gpcv2.c +++ b/hw/intc/imx_gpcv2.c @@ -102,7 +102,7 @@ static const VMStateDescription vmstate_imx_gpcv2 = { }, }; -static void imx_gpcv2_class_init(ObjectClass *klass, void *data) +static void imx_gpcv2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/ioapic.c b/hw/intc/ioapic.c index 8cd1d85e06..133bef852d 100644 --- a/hw/intc/ioapic.c +++ b/hw/intc/ioapic.c @@ -480,7 +480,7 @@ static const Property ioapic_properties[] = { DEFINE_PROP_UINT8("version", IOAPICCommonState, version, IOAPIC_VER_DEF), }; -static void ioapic_class_init(ObjectClass *klass, void *data) +static void ioapic_class_init(ObjectClass *klass, const void *data) { IOAPICCommonClass *k = IOAPIC_COMMON_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c index 769896353a..b0381c7990 100644 --- a/hw/intc/ioapic_common.c +++ b/hw/intc/ioapic_common.c @@ -197,7 +197,7 @@ static const VMStateDescription vmstate_ioapic_common = { } }; -static void ioapic_common_class_init(ObjectClass *klass, void *data) +static void ioapic_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass); diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index a558c50185..f4fe961a98 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -401,7 +401,7 @@ static int vmstate_extioi_post_load(void *opaque, int version_id) return 0; } -static void loongarch_extioi_class_init(ObjectClass *klass, void *data) +static void loongarch_extioi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_CLASS(klass); diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index ff3974f2a1..126f13d12c 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c @@ -174,7 +174,8 @@ static const Property extioi_properties[] = { features, EXTIOI_HAS_VIRT_EXTENSION, 0), }; -static void loongarch_extioi_common_class_init(ObjectClass *klass, void *data) +static void loongarch_extioi_common_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass); diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index b10641dd03..4dad240689 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -140,7 +140,7 @@ static void loongarch_ipi_cpu_unplug(HotplugHandler *hotplug_dev, core->cpu = NULL; } -static void loongarch_ipi_class_init(ObjectClass *klass, void *data) +static void loongarch_ipi_class_init(ObjectClass *klass, const void *data) { LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c index bc93504ff7..06eb944da0 100644 --- a/hw/intc/loongarch_pch_msi.c +++ b/hw/intc/loongarch_pch_msi.c @@ -79,7 +79,7 @@ static const Property loongarch_msi_properties[] = { DEFINE_PROP_UINT32("msi_irq_num", LoongArchPCHMSI, irq_num, 0), }; -static void loongarch_pch_msi_class_init(ObjectClass *klass, void *data) +static void loongarch_pch_msi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index acd75ccb0c..6c2b6de3f0 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -404,7 +404,7 @@ static void loongarch_pic_realize(DeviceState *dev, Error **errp) } -static void loongarch_pic_class_init(ObjectClass *klass, void *data) +static void loongarch_pic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass); diff --git a/hw/intc/loongarch_pic_common.c b/hw/intc/loongarch_pic_common.c index e7f541db4b..fdb250c418 100644 --- a/hw/intc/loongarch_pic_common.c +++ b/hw/intc/loongarch_pic_common.c @@ -71,7 +71,8 @@ static const VMStateDescription vmstate_loongarch_pic_common = { } }; -static void loongarch_pic_common_class_init(ObjectClass *klass, void *data) +static void loongarch_pic_common_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_CLASS(klass); diff --git a/hw/intc/loongson_ipi.c b/hw/intc/loongson_ipi.c index d2268a27f8..fbc73e8b00 100644 --- a/hw/intc/loongson_ipi.c +++ b/hw/intc/loongson_ipi.c @@ -101,7 +101,7 @@ static const Property loongson_ipi_properties[] = { DEFINE_PROP_UINT32("num-cpu", LoongsonIPICommonState, num_cpu, 1), }; -static void loongson_ipi_class_init(ObjectClass *klass, void *data) +static void loongson_ipi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LoongsonIPIClass *lic = LOONGSON_IPI_CLASS(klass); diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c index f5ab5024c0..f32661c40f 100644 --- a/hw/intc/loongson_ipi_common.c +++ b/hw/intc/loongson_ipi_common.c @@ -303,7 +303,7 @@ static const VMStateDescription vmstate_loongson_ipi_common = { } }; -static void loongson_ipi_common_class_init(ObjectClass *klass, void *data) +static void loongson_ipi_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index a82b80f5c6..215e1a6ed5 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -90,7 +90,7 @@ static const Property m68k_irqc_properties[] = { TYPE_M68K_CPU, ArchCPU *), }; -static void m68k_irqc_class_init(ObjectClass *oc, void *data) +static void m68k_irqc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); diff --git a/hw/intc/mips_gic.c b/hw/intc/mips_gic.c index 627a76ba7b..0c50ba41f6 100644 --- a/hw/intc/mips_gic.c +++ b/hw/intc/mips_gic.c @@ -442,7 +442,7 @@ static const Property mips_gic_properties[] = { DEFINE_PROP_UINT32("num-irq", MIPSGICState, num_irq, 256), }; -static void mips_gic_class_init(ObjectClass *klass, void *data) +static void mips_gic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/omap_intc.c b/hw/intc/omap_intc.c index 095a3d504f..9e8737be33 100644 --- a/hw/intc/omap_intc.c +++ b/hw/intc/omap_intc.c @@ -379,7 +379,7 @@ static const Property omap_intc_properties[] = { DEFINE_PROP_UINT32("size", OMAPIntcState, size, 0x100), }; -static void omap_intc_class_init(ObjectClass *klass, void *data) +static void omap_intc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/ompic.c b/hw/intc/ompic.c index 169baf2ded..047c367478 100644 --- a/hw/intc/ompic.c +++ b/hw/intc/ompic.c @@ -155,7 +155,7 @@ static const VMStateDescription vmstate_or1k_ompic = { } }; -static void or1k_ompic_class_init(ObjectClass *klass, void *data) +static void or1k_ompic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/openpic.c b/hw/intc/openpic.c index 78a82d0d30..87733eb7c3 100644 --- a/hw/intc/openpic.c +++ b/hw/intc/openpic.c @@ -1611,7 +1611,7 @@ static const Property openpic_properties[] = { DEFINE_PROP_UINT32("nb_cpus", OpenPICState, nb_cpus, 1), }; -static void openpic_class_init(ObjectClass *oc, void *data) +static void openpic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/intc/openpic_kvm.c b/hw/intc/openpic_kvm.c index 9cdaa97004..673ea9ca05 100644 --- a/hw/intc/openpic_kvm.c +++ b/hw/intc/openpic_kvm.c @@ -267,7 +267,7 @@ static const Property kvm_openpic_properties[] = { OPENPIC_MODEL_FSL_MPIC_20), }; -static void kvm_openpic_class_init(ObjectClass *oc, void *data) +static void kvm_openpic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/intc/pl190.c b/hw/intc/pl190.c index a5e2d76315..838c21c4a0 100644 --- a/hw/intc/pl190.c +++ b/hw/intc/pl190.c @@ -273,7 +273,7 @@ static const VMStateDescription vmstate_pl190 = { } }; -static void pl190_class_init(ObjectClass *klass, void *data) +static void pl190_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index ccbe95a58e..cd73881b5b 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -2068,7 +2068,7 @@ static const Property pnv_xive_properties[] = { DEFINE_PROP_LINK("chip", PnvXive, chip, TYPE_PNV_CHIP, PnvChip *), }; -static void pnv_xive_class_init(ObjectClass *klass, void *data) +static void pnv_xive_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 0b81dad6ba..02437dddac 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -2505,7 +2505,7 @@ static int pnv_xive2_dt_xscom(PnvXScomInterface *dev, void *fdt, return 0; } -static void pnv_xive2_class_init(ObjectClass *klass, void *data) +static void pnv_xive2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); diff --git a/hw/intc/ppc-uic.c b/hw/intc/ppc-uic.c index 7de4bf9885..bc4dc90ade 100644 --- a/hw/intc/ppc-uic.c +++ b/hw/intc/ppc-uic.c @@ -281,7 +281,7 @@ static const VMStateDescription ppc_uic_vmstate = { }, }; -static void ppc_uic_class_init(ObjectClass *klass, void *data) +static void ppc_uic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/realview_gic.c b/hw/intc/realview_gic.c index 9b12116b2a..63e25c2a78 100644 --- a/hw/intc/realview_gic.c +++ b/hw/intc/realview_gic.c @@ -63,7 +63,7 @@ static void realview_gic_init(Object *obj) qdev_prop_set_uint32(DEVICE(&s->gic), "num-cpu", 1); } -static void realview_gic_class_init(ObjectClass *oc, void *data) +static void realview_gic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index db374a7c2d..b0139f03f5 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -328,7 +328,7 @@ static const VMStateDescription vmstate_riscv_mtimer = { } }; -static void riscv_aclint_mtimer_class_init(ObjectClass *klass, void *data) +static void riscv_aclint_mtimer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = riscv_aclint_mtimer_realize; @@ -509,7 +509,7 @@ static void riscv_aclint_swi_reset_enter(Object *obj, ResetType type) } } -static void riscv_aclint_swi_class_init(ObjectClass *klass, void *data) +static void riscv_aclint_swi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = riscv_aclint_swi_realize; diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 789c4a4d6e..8bcd9f4697 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -997,7 +997,7 @@ static const VMStateDescription vmstate_riscv_aplic = { } }; -static void riscv_aplic_class_init(ObjectClass *klass, void *data) +static void riscv_aplic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c index 852f413e5a..2169988167 100644 --- a/hw/intc/riscv_imsic.c +++ b/hw/intc/riscv_imsic.c @@ -416,7 +416,7 @@ static const VMStateDescription vmstate_riscv_imsic = { } }; -static void riscv_imsic_class_init(ObjectClass *klass, void *data) +static void riscv_imsic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/rx_icu.c b/hw/intc/rx_icu.c index ca13c5fb37..f8615527b7 100644 --- a/hw/intc/rx_icu.c +++ b/hw/intc/rx_icu.c @@ -368,7 +368,7 @@ static const Property rxicu_properties[] = { qdev_prop_uint8, uint8_t), }; -static void rxicu_class_init(ObjectClass *klass, void *data) +static void rxicu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/s390_flic.c b/hw/intc/s390_flic.c index 4fae023197..8f4c9fd52e 100644 --- a/hw/intc/s390_flic.c +++ b/hw/intc/s390_flic.c @@ -450,7 +450,7 @@ static const Property qemu_s390_flic_properties[] = { migrate_all_state, true), }; -static void qemu_s390_flic_class_init(ObjectClass *oc, void *data) +static void qemu_s390_flic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); @@ -477,7 +477,7 @@ static void s390_flic_common_realize(DeviceState *dev, Error **errp) fs->ais_supported = s390_has_feat(S390_FEAT_ADAPTER_INT_SUPPRESSION); } -static void s390_flic_class_init(ObjectClass *oc, void *data) +static void s390_flic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/intc/s390_flic_kvm.c b/hw/intc/s390_flic_kvm.c index 10aaafbb31..f833a3996a 100644 --- a/hw/intc/s390_flic_kvm.c +++ b/hw/intc/s390_flic_kvm.c @@ -670,7 +670,7 @@ static void kvm_s390_flic_reset(DeviceState *dev) flic_enable_pfault(flic); } -static void kvm_s390_flic_class_init(ObjectClass *oc, void *data) +static void kvm_s390_flic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); S390FLICStateClass *fsc = S390_FLIC_COMMON_CLASS(oc); diff --git a/hw/intc/sifive_plic.c b/hw/intc/sifive_plic.c index a5b0f6ef1b..3160b216fd 100644 --- a/hw/intc/sifive_plic.c +++ b/hw/intc/sifive_plic.c @@ -446,7 +446,7 @@ static const Property sifive_plic_properties[] = { DEFINE_PROP_UINT32("aperture-size", SiFivePLICState, aperture_size, 0), }; -static void sifive_plic_class_init(ObjectClass *klass, void *data) +static void sifive_plic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/slavio_intctl.c b/hw/intc/slavio_intctl.c index f83709a857..5776055a8b 100644 --- a/hw/intc/slavio_intctl.c +++ b/hw/intc/slavio_intctl.c @@ -441,7 +441,7 @@ static void slavio_intctl_init(Object *obj) } } -static void slavio_intctl_class_init(ObjectClass *klass, void *data) +static void slavio_intctl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); InterruptStatsProviderClass *ic = INTERRUPT_STATS_PROVIDER_CLASS(klass); diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index ce734b03ab..7fde6059bf 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -809,7 +809,7 @@ static bool spapr_xive_in_kernel_xptr(const XivePresenter *xptr) return spapr_xive_in_kernel(SPAPR_XIVE(xptr)); } -static void spapr_xive_class_init(ObjectClass *klass, void *data) +static void spapr_xive_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XiveRouterClass *xrc = XIVE_ROUTER_CLASS(klass); diff --git a/hw/intc/xics.c b/hw/intc/xics.c index 9c1b7bbe9e..d9a199e883 100644 --- a/hw/intc/xics.c +++ b/hw/intc/xics.c @@ -350,7 +350,7 @@ static const Property icp_properties[] = { DEFINE_PROP_LINK(ICP_PROP_CPU, ICPState, cs, TYPE_CPU, CPUState *), }; -static void icp_class_init(ObjectClass *klass, void *data) +static void icp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -681,7 +681,7 @@ static const Property ics_properties[] = { XICSFabric *), }; -static void ics_class_init(ObjectClass *klass, void *data) +static void ics_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/intc/xics_pnv.c b/hw/intc/xics_pnv.c index 753c067f17..ff602d9a34 100644 --- a/hw/intc/xics_pnv.c +++ b/hw/intc/xics_pnv.c @@ -176,7 +176,7 @@ static void pnv_icp_realize(DeviceState *dev, Error **errp) icp, "icp-thread", 0x1000); } -static void pnv_icp_class_init(ObjectClass *klass, void *data) +static void pnv_icp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ICPStateClass *icpc = ICP_CLASS(klass); diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index a0d97bdefe..9e465fb8f3 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -436,7 +436,7 @@ static void xics_spapr_deactivate(SpaprInterruptController *intc) } } -static void ics_spapr_class_init(ObjectClass *klass, void *data) +static void ics_spapr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ICSStateClass *isc = ICS_CLASS(klass); diff --git a/hw/intc/xilinx_intc.c b/hw/intc/xilinx_intc.c index ab1c4a3222..5257ad54b1 100644 --- a/hw/intc/xilinx_intc.c +++ b/hw/intc/xilinx_intc.c @@ -214,7 +214,7 @@ static const Property xilinx_intc_properties[] = { DEFINE_PROP_UINT32("kind-of-intr", XpsIntc, c_kind_of_intr, 0), }; -static void xilinx_intc_class_init(ObjectClass *klass, void *data) +static void xilinx_intc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 3eb28c2265..069c1e9a5e 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -930,7 +930,7 @@ static const Property xive_tctx_properties[] = { XivePresenter *), }; -static void xive_tctx_class_init(ObjectClass *klass, void *data) +static void xive_tctx_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1412,7 +1412,7 @@ static const Property xive_source_properties[] = { XiveNotifier *), }; -static void xive_source_class_init(ObjectClass *klass, void *data) +static void xive_source_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -2085,7 +2085,7 @@ static const Property xive_router_properties[] = { TYPE_XIVE_FABRIC, XiveFabric *), }; -static void xive_router_class_init(ObjectClass *klass, void *data) +static void xive_router_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass); @@ -2254,7 +2254,7 @@ static const Property xive_end_source_properties[] = { XiveRouter *), }; -static void xive_end_source_class_init(ObjectClass *klass, void *data) +static void xive_end_source_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 7d584dfafa..3337a943fb 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1590,7 +1590,7 @@ static const Property xive2_router_properties[] = { TYPE_XIVE_FABRIC, XiveFabric *), }; -static void xive2_router_class_init(ObjectClass *klass, void *data) +static void xive2_router_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XiveNotifierClass *xnc = XIVE_NOTIFIER_CLASS(klass); @@ -1805,7 +1805,7 @@ static const Property xive2_end_source_properties[] = { Xive2Router *), }; -static void xive2_end_source_class_init(ObjectClass *klass, void *data) +static void xive2_end_source_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/xlnx-pmu-iomod-intc.c b/hw/intc/xlnx-pmu-iomod-intc.c index ccdab244b3..9200585e32 100644 --- a/hw/intc/xlnx-pmu-iomod-intc.c +++ b/hw/intc/xlnx-pmu-iomod-intc.c @@ -531,7 +531,7 @@ static const VMStateDescription vmstate_xlnx_pmu_io_intc = { } }; -static void xlnx_pmu_io_intc_class_init(ObjectClass *klass, void *data) +static void xlnx_pmu_io_intc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/intc/xlnx-zynqmp-ipi.c b/hw/intc/xlnx-zynqmp-ipi.c index 7241377298..610cd0e316 100644 --- a/hw/intc/xlnx-zynqmp-ipi.c +++ b/hw/intc/xlnx-zynqmp-ipi.c @@ -355,7 +355,7 @@ static const VMStateDescription vmstate_zynqmp_pmu_ipi = { } }; -static void xlnx_zynqmp_ipi_class_init(ObjectClass *klass, void *data) +static void xlnx_zynqmp_ipi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ipack/ipack.c b/hw/ipack/ipack.c index b6defae602..ab602bff73 100644 --- a/hw/ipack/ipack.c +++ b/hw/ipack/ipack.c @@ -74,7 +74,7 @@ static const Property ipack_device_props[] = { DEFINE_PROP_INT32("slot", IPackDevice, slot, -1), }; -static void ipack_device_class_init(ObjectClass *klass, void *data) +static void ipack_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); diff --git a/hw/ipack/tpci200.c b/hw/ipack/tpci200.c index 470a4203ae..f6993330d2 100644 --- a/hw/ipack/tpci200.c +++ b/hw/ipack/tpci200.c @@ -629,7 +629,7 @@ static const VMStateDescription vmstate_tpci200 = { } }; -static void tpci200_class_init(ObjectClass *klass, void *data) +static void tpci200_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/ipmi/ipmi.c b/hw/ipmi/ipmi.c index fdeaa5269f..b91e487e1b 100644 --- a/hw/ipmi/ipmi.c +++ b/hw/ipmi/ipmi.c @@ -78,7 +78,7 @@ static int ipmi_do_hw_op(IPMIInterface *s, enum ipmi_op op, int checkonly) } } -static void ipmi_interface_class_init(ObjectClass *class, void *data) +static void ipmi_interface_class_init(ObjectClass *class, const void *data) { IPMIInterfaceClass *ik = IPMI_INTERFACE_CLASS(class); @@ -112,7 +112,7 @@ static const Property ipmi_bmc_properties[] = { DEFINE_PROP_UINT8("slave_addr", IPMIBmc, slave_addr, 0x20), }; -static void bmc_class_init(ObjectClass *oc, void *data) +static void bmc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ipmi/ipmi_bmc_extern.c b/hw/ipmi/ipmi_bmc_extern.c index e563214390..9f1ba7b2f8 100644 --- a/hw/ipmi/ipmi_bmc_extern.c +++ b/hw/ipmi/ipmi_bmc_extern.c @@ -513,7 +513,7 @@ static const Property ipmi_bmc_extern_properties[] = { DEFINE_PROP_CHR("chardev", IPMIBmcExtern, chr), }; -static void ipmi_bmc_extern_class_init(ObjectClass *oc, void *data) +static void ipmi_bmc_extern_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIBmcClass *bk = IPMI_BMC_CLASS(oc); diff --git a/hw/ipmi/ipmi_bmc_sim.c b/hw/ipmi/ipmi_bmc_sim.c index 41fe6835a7..04e1dcd0e7 100644 --- a/hw/ipmi/ipmi_bmc_sim.c +++ b/hw/ipmi/ipmi_bmc_sim.c @@ -2278,7 +2278,7 @@ static const Property ipmi_sim_properties[] = { DEFINE_PROP_UUID_NODEFAULT("guid", IPMIBmcSim, uuid), }; -static void ipmi_sim_class_init(ObjectClass *oc, void *data) +static void ipmi_sim_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIBmcClass *bk = IPMI_BMC_CLASS(oc); diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index 76585e786e..db539e68ae 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -139,7 +139,7 @@ static const Property ipmi_isa_properties[] = { DEFINE_PROP_INT32("irq", ISAIPMIBTDevice, isairq, 5), }; -static void isa_ipmi_bt_class_init(ObjectClass *oc, void *data) +static void isa_ipmi_bt_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index ba3ae208b2..4cbc6c577c 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -145,7 +145,7 @@ static const Property ipmi_isa_properties[] = { DEFINE_PROP_INT32("irq", ISAIPMIKCSDevice, isairq, 5), }; -static void isa_ipmi_kcs_class_init(ObjectClass *oc, void *data) +static void isa_ipmi_kcs_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); diff --git a/hw/ipmi/pci_ipmi_bt.c b/hw/ipmi/pci_ipmi_bt.c index 7ba8b3ab96..23f65c6886 100644 --- a/hw/ipmi/pci_ipmi_bt.c +++ b/hw/ipmi/pci_ipmi_bt.c @@ -119,7 +119,7 @@ static void *pci_ipmi_bt_get_backend_data(IPMIInterface *ii) return &pib->bt; } -static void pci_ipmi_bt_class_init(ObjectClass *oc, void *data) +static void pci_ipmi_bt_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); diff --git a/hw/ipmi/pci_ipmi_kcs.c b/hw/ipmi/pci_ipmi_kcs.c index 0aa35143e9..4077b6a7b0 100644 --- a/hw/ipmi/pci_ipmi_kcs.c +++ b/hw/ipmi/pci_ipmi_kcs.c @@ -118,7 +118,7 @@ static void *pci_ipmi_kcs_get_backend_data(IPMIInterface *ii) return &pik->kcs; } -static void pci_ipmi_kcs_class_init(ObjectClass *oc, void *data) +static void pci_ipmi_kcs_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); diff --git a/hw/ipmi/smbus_ipmi.c b/hw/ipmi/smbus_ipmi.c index 56865df7db..7345844a3a 100644 --- a/hw/ipmi/smbus_ipmi.c +++ b/hw/ipmi/smbus_ipmi.c @@ -351,7 +351,7 @@ static void smbus_ipmi_get_fwinfo(struct IPMIInterface *ii, IPMIFwInfo *info) info->uuid = sid->uuid; } -static void smbus_ipmi_class_init(ObjectClass *oc, void *data) +static void smbus_ipmi_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); IPMIInterfaceClass *iic = IPMI_INTERFACE_CLASS(oc); diff --git a/hw/isa/fdc37m81x-superio.c b/hw/isa/fdc37m81x-superio.c index 55e91fbca1..c2a38f04b1 100644 --- a/hw/isa/fdc37m81x-superio.c +++ b/hw/isa/fdc37m81x-superio.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "hw/isa/superio.h" -static void fdc37m81x_class_init(ObjectClass *klass, void *data) +static void fdc37m81x_class_init(ObjectClass *klass, const void *data) { ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index cbaa152a89..26c8ec4f77 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -122,7 +122,7 @@ static void i82378_init(Object *obj) qdev_init_gpio_in(dev, i82378_request_pic_irq, 16); } -static void i82378_class_init(ObjectClass *klass, void *data) +static void i82378_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/isa/isa-bus.c b/hw/isa/isa-bus.c index 2599c1219a..6c9802eb7a 100644 --- a/hw/isa/isa-bus.c +++ b/hw/isa/isa-bus.c @@ -29,7 +29,7 @@ static ISABus *isabus; static char *isabus_get_fw_dev_path(DeviceState *dev); -static void isa_bus_class_init(ObjectClass *klass, void *data) +static void isa_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -205,7 +205,7 @@ ISADevice *isa_vga_init(ISABus *bus) } } -static void isabus_bridge_class_init(ObjectClass *klass, void *data) +static void isabus_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -220,7 +220,7 @@ static const TypeInfo isabus_bridge_info = { .class_init = isabus_bridge_class_init, }; -static void isa_device_class_init(ObjectClass *klass, void *data) +static void isa_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->bus_type = TYPE_ISA_BUS; diff --git a/hw/isa/isa-superio.c b/hw/isa/isa-superio.c index 4260da547c..2853485977 100644 --- a/hw/isa/isa-superio.c +++ b/hw/isa/isa-superio.c @@ -172,7 +172,7 @@ static void isa_superio_realize(DeviceState *dev, Error **errp) } } -static void isa_superio_class_init(ObjectClass *oc, void *data) +static void isa_superio_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index dcb0ac2848..d3e623b1e8 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -875,7 +875,7 @@ static void build_ich9_isa_aml(AcpiDevAmlIf *adev, Aml *scope) qbus_build_aml(bus, scope); } -static void ich9_lpc_class_init(ObjectClass *klass, void *data) +static void ich9_lpc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/isa/pc87312.c b/hw/isa/pc87312.c index 5f5868442a..388da8f590 100644 --- a/hw/isa/pc87312.c +++ b/hw/isa/pc87312.c @@ -332,7 +332,7 @@ static const Property pc87312_properties[] = { DEFINE_PROP_UINT8("config", PC87312State, config, 1), }; -static void pc87312_class_init(ObjectClass *klass, void *data) +static void pc87312_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); diff --git a/hw/isa/piix.c b/hw/isa/piix.c index 7fc9e3ec9d..2c6e76f97c 100644 --- a/hw/isa/piix.c +++ b/hw/isa/piix.c @@ -417,7 +417,7 @@ static const Property pci_piix_props[] = { DEFINE_PROP_BOOL("smm-enabled", PIIXState, smm_enabled, false), }; -static void pci_piix_class_init(ObjectClass *klass, void *data) +static void pci_piix_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -464,7 +464,7 @@ static void piix3_init(Object *obj) object_initialize_child(obj, "ide", &d->ide, TYPE_PIIX3_IDE); } -static void piix3_class_init(ObjectClass *klass, void *data) +static void piix3_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -494,7 +494,7 @@ static void piix4_init(Object *obj) object_initialize_child(obj, "ide", &s->ide, TYPE_PIIX4_IDE); } -static void piix4_class_init(ObjectClass *klass, void *data) +static void piix4_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/isa/smc37c669-superio.c b/hw/isa/smc37c669-superio.c index d2e58c9a89..0ec63f520c 100644 --- a/hw/isa/smc37c669-superio.c +++ b/hw/isa/smc37c669-superio.c @@ -58,7 +58,7 @@ static unsigned int get_fdc_dma(ISASuperIODevice *sio, uint8_t index) return 2; } -static void smc37c669_class_init(ObjectClass *klass, void *data) +static void smc37c669_class_init(ObjectClass *klass, const void *data) { ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 43bd67eeef..80366aaf64 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -220,7 +220,7 @@ typedef struct via_pm_init_info { uint16_t device_id; } ViaPMInitInfo; -static void via_pm_class_init(ObjectClass *klass, void *data) +static void via_pm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -337,7 +337,7 @@ static void via_superio_devices_enable(ViaSuperIOState *s, uint8_t data) isa_fdc_set_enabled(s->superio.floppy, data & BIT(4)); } -static void via_superio_class_init(ObjectClass *klass, void *data) +static void via_superio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); @@ -456,7 +456,7 @@ static void vt82c686b_superio_init(Object *obj) VIA_SUPERIO(obj)->io_ops = &vt82c686b_superio_cfg_ops; } -static void vt82c686b_superio_class_init(ObjectClass *klass, void *data) +static void vt82c686b_superio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); @@ -565,7 +565,7 @@ static void vt8231_superio_init(Object *obj) VIA_SUPERIO(obj)->io_ops = &vt8231_superio_cfg_ops; } -static void vt8231_superio_class_init(ObjectClass *klass, void *data) +static void vt8231_superio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ISASuperIOClass *sc = ISA_SUPERIO_CLASS(klass); @@ -833,7 +833,7 @@ static void vt82c686b_init(Object *obj) object_initialize_child(obj, "pm", &s->pm, TYPE_VT82C686B_PM); } -static void vt82c686b_class_init(ObjectClass *klass, void *data) +static void vt82c686b_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -898,7 +898,7 @@ static void vt8231_init(Object *obj) object_initialize_child(obj, "pm", &s->pm, TYPE_VT8231_PM); } -static void vt8231_class_init(ObjectClass *klass, void *data) +static void vt8231_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index aaaea64c4c..fde25e9409 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -1133,7 +1133,7 @@ static int64_t virt_get_default_cpu_node_id(const MachineState *ms, int idx) } } -static void virt_class_init(ObjectClass *oc, void *data) +static void virt_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); diff --git a/hw/m68k/mcf5206.c b/hw/m68k/mcf5206.c index c22e615f7a..a25e782403 100644 --- a/hw/m68k/mcf5206.c +++ b/hw/m68k/mcf5206.c @@ -605,7 +605,7 @@ static const Property mcf5206_mbar_properties[] = { TYPE_M68K_CPU, M68kCPU *), }; -static void mcf5206_mbar_class_init(ObjectClass *oc, void *data) +static void mcf5206_mbar_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/m68k/mcf_intc.c b/hw/m68k/mcf_intc.c index 7b9213947d..e3055b841e 100644 --- a/hw/m68k/mcf_intc.c +++ b/hw/m68k/mcf_intc.c @@ -182,7 +182,7 @@ static const Property mcf_intc_properties[] = { TYPE_M68K_CPU, M68kCPU *), }; -static void mcf_intc_class_init(ObjectClass *oc, void *data) +static void mcf_intc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/m68k/next-cube.c b/hw/m68k/next-cube.c index 1d5925c17b..957644b2d1 100644 --- a/hw/m68k/next-cube.c +++ b/hw/m68k/next-cube.c @@ -794,7 +794,7 @@ static const VMStateDescription next_scsi_vmstate = { }, }; -static void next_scsi_class_init(ObjectClass *klass, void *data) +static void next_scsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1064,7 +1064,7 @@ static const VMStateDescription next_rtc_vmstate = { }, }; -static void next_rtc_class_init(ObjectClass *klass, void *data) +static void next_rtc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -1228,7 +1228,7 @@ static const VMStateDescription next_pc_vmstate = { }, }; -static void next_pc_class_init(ObjectClass *klass, void *data) +static void next_pc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -1348,7 +1348,7 @@ static void next_cube_init(MachineState *machine) memory_region_add_subregion(sysmem, 0x02000000, &m->dmamem); } -static void next_machine_class_init(ObjectClass *oc, void *data) +static void next_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/m68k/next-kbd.c b/hw/m68k/next-kbd.c index 68b17786b2..2bec945acf 100644 --- a/hw/m68k/next-kbd.c +++ b/hw/m68k/next-kbd.c @@ -312,7 +312,7 @@ static const VMStateDescription nextkbd_vmstate = { .unmigratable = 1, /* TODO: Implement this when m68k CPU is migratable */ }; -static void nextkbd_class_init(ObjectClass *oc, void *data) +static void nextkbd_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/m68k/q800-glue.c b/hw/m68k/q800-glue.c index 168665b382..b428e7c833 100644 --- a/hw/m68k/q800-glue.c +++ b/hw/m68k/q800-glue.c @@ -228,7 +228,7 @@ static void glue_init(Object *obj) s->nmi_release = timer_new_ms(QEMU_CLOCK_VIRTUAL, glue_nmi_release, s); } -static void glue_class_init(ObjectClass *klass, void *data) +static void glue_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/m68k/q800.c b/hw/m68k/q800.c index 46435238d2..793b23f815 100644 --- a/hw/m68k/q800.c +++ b/hw/m68k/q800.c @@ -727,7 +727,7 @@ static GlobalProperty hw_compat_q800[] = { }; static const size_t hw_compat_q800_len = G_N_ELEMENTS(hw_compat_q800); -static void q800_machine_class_init(ObjectClass *oc, void *data) +static void q800_machine_class_init(ObjectClass *oc, const void *data) { static const char * const valid_cpu_types[] = { M68K_CPU_TYPE_NAME("m68040"), diff --git a/hw/m68k/virt.c b/hw/m68k/virt.c index e74d709a18..875fd00ef8 100644 --- a/hw/m68k/virt.c +++ b/hw/m68k/virt.c @@ -310,7 +310,7 @@ static void virt_init(MachineState *machine) } } -static void virt_machine_class_init(ObjectClass *oc, void *data) +static void virt_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->desc = "QEMU M68K Virtual Machine"; @@ -339,7 +339,7 @@ type_init(virt_machine_register_types) #define DEFINE_VIRT_MACHINE_IMPL(latest, ...) \ static void MACHINE_VER_SYM(class_init, virt, __VA_ARGS__)( \ ObjectClass *oc, \ - void *data) \ + const void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ MACHINE_VER_SYM(options, virt, __VA_ARGS__)(mc); \ diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 43aa02ab2a..c95722a2ae 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -2141,7 +2141,7 @@ void qmp_cxl_release_dynamic_capacity(const char *path, uint16_t host_id, } } -static void ct3_class_init(ObjectClass *oc, void *data) +static void ct3_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); diff --git a/hw/mem/npcm7xx_mc.c b/hw/mem/npcm7xx_mc.c index abc5af5620..07fc108e0a 100644 --- a/hw/mem/npcm7xx_mc.c +++ b/hw/mem/npcm7xx_mc.c @@ -65,7 +65,7 @@ static void npcm7xx_mc_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio); } -static void npcm7xx_mc_class_init(ObjectClass *klass, void *data) +static void npcm7xx_mc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/mem/nvdimm.c b/hw/mem/nvdimm.c index c05007ab21..23ab143ef8 100644 --- a/hw/mem/nvdimm.c +++ b/hw/mem/nvdimm.c @@ -250,7 +250,7 @@ static const Property nvdimm_properties[] = { DEFINE_PROP_BOOL(NVDIMM_UNARMED_PROP, NVDIMMDevice, unarmed, false), }; -static void nvdimm_class_init(ObjectClass *oc, void *data) +static void nvdimm_class_init(ObjectClass *oc, const void *data) { PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc); MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc); diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 799a618c1c..6f68171442 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -276,7 +276,7 @@ static void pc_dimm_md_fill_device_info(const MemoryDeviceState *md, } } -static void pc_dimm_class_init(ObjectClass *oc, void *data) +static void pc_dimm_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc); diff --git a/hw/mem/sparse-mem.c b/hw/mem/sparse-mem.c index 8bed5dbe16..d7b00e563a 100644 --- a/hw/mem/sparse-mem.c +++ b/hw/mem/sparse-mem.c @@ -136,7 +136,7 @@ static void sparse_mem_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->mmio); } -static void sparse_mem_class_init(ObjectClass *klass, void *data) +static void sparse_mem_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index f976c90bd2..032f6f70ea 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -141,7 +141,8 @@ petalogix_s3adsp1800_init(MachineState *machine) NULL); } -static void petalogix_s3adsp1800_machine_class_init(ObjectClass *oc, void *data) +static void petalogix_s3adsp1800_machine_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index 0922c65295..ea1430f408 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -121,7 +121,7 @@ static void xlnx_zynqmp_pmu_soc_realize(DeviceState *dev, Error **errp) } } -static void xlnx_zynqmp_pmu_soc_class_init(ObjectClass *oc, void *data) +static void xlnx_zynqmp_pmu_soc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/mips/cps.c b/hw/mips/cps.c index 0d8cbdc892..2a3ba3f58d 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -173,7 +173,7 @@ static const Property mips_cps_properties[] = { DEFINE_PROP_BOOL("cpu-big-endian", MIPSCPSState, cpu_is_bigendian, false), }; -static void mips_cps_class_init(ObjectClass *klass, void *data) +static void mips_cps_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index 1700c3765d..cee92e1825 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -415,7 +415,7 @@ void mips_pica61_init(MachineState *machine) mips_jazz_init(machine, JAZZ_PICA61); } -static void mips_magnum_class_init(ObjectClass *oc, void *data) +static void mips_magnum_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -432,7 +432,7 @@ static const TypeInfo mips_magnum_type = { .class_init = mips_magnum_class_init, }; -static void mips_pica61_class_init(ObjectClass *oc, void *data) +static void mips_pica61_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/mips/loongson3_virt.c b/hw/mips/loongson3_virt.c index 1da20dccec..de6fbcc0cb 100644 --- a/hw/mips/loongson3_virt.c +++ b/hw/mips/loongson3_virt.c @@ -667,7 +667,7 @@ static void mips_loongson3_virt_init(MachineState *machine) loongson3_virt_devices_init(machine, liointc); } -static void loongson3v_machine_class_init(ObjectClass *oc, void *data) +static void loongson3v_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/misc/a9scu.c b/hw/misc/a9scu.c index 088d4adb0d..bb00ae2969 100644 --- a/hw/misc/a9scu.c +++ b/hw/misc/a9scu.c @@ -127,7 +127,7 @@ static const Property a9_scu_properties[] = { DEFINE_PROP_UINT32("num-cpu", A9SCUState, num_cpu, 1), }; -static void a9_scu_class_init(ObjectClass *klass, void *data) +static void a9_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/allwinner-a10-ccm.c b/hw/misc/allwinner-a10-ccm.c index 6ca1daaff8..6b188c25a5 100644 --- a/hw/misc/allwinner-a10-ccm.c +++ b/hw/misc/allwinner-a10-ccm.c @@ -199,7 +199,7 @@ static const VMStateDescription allwinner_a10_ccm_vmstate = { } }; -static void allwinner_a10_ccm_class_init(ObjectClass *klass, void *data) +static void allwinner_a10_ccm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/allwinner-a10-dramc.c b/hw/misc/allwinner-a10-dramc.c index badc4c56eb..c16814cc5b 100644 --- a/hw/misc/allwinner-a10-dramc.c +++ b/hw/misc/allwinner-a10-dramc.c @@ -154,7 +154,7 @@ static const VMStateDescription allwinner_a10_dramc_vmstate = { } }; -static void allwinner_a10_dramc_class_init(ObjectClass *klass, void *data) +static void allwinner_a10_dramc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/allwinner-cpucfg.c b/hw/misc/allwinner-cpucfg.c index a4f7a01141..90dd872abf 100644 --- a/hw/misc/allwinner-cpucfg.c +++ b/hw/misc/allwinner-cpucfg.c @@ -258,7 +258,7 @@ static const VMStateDescription allwinner_cpucfg_vmstate = { } }; -static void allwinner_cpucfg_class_init(ObjectClass *klass, void *data) +static void allwinner_cpucfg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/allwinner-h3-ccu.c b/hw/misc/allwinner-h3-ccu.c index e765f4c54b..be91c0c1ca 100644 --- a/hw/misc/allwinner-h3-ccu.c +++ b/hw/misc/allwinner-h3-ccu.c @@ -218,7 +218,7 @@ static const VMStateDescription allwinner_h3_ccu_vmstate = { } }; -static void allwinner_h3_ccu_class_init(ObjectClass *klass, void *data) +static void allwinner_h3_ccu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/allwinner-h3-dramc.c b/hw/misc/allwinner-h3-dramc.c index 74ff71b753..8834524c30 100644 --- a/hw/misc/allwinner-h3-dramc.c +++ b/hw/misc/allwinner-h3-dramc.c @@ -331,7 +331,7 @@ static const VMStateDescription allwinner_h3_dramc_vmstate = { } }; -static void allwinner_h3_dramc_class_init(ObjectClass *klass, void *data) +static void allwinner_h3_dramc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/allwinner-h3-sysctrl.c b/hw/misc/allwinner-h3-sysctrl.c index 32a0ceb01a..6b86524606 100644 --- a/hw/misc/allwinner-h3-sysctrl.c +++ b/hw/misc/allwinner-h3-sysctrl.c @@ -116,7 +116,8 @@ static const VMStateDescription allwinner_h3_sysctrl_vmstate = { } }; -static void allwinner_h3_sysctrl_class_init(ObjectClass *klass, void *data) +static void allwinner_h3_sysctrl_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/allwinner-r40-ccu.c b/hw/misc/allwinner-r40-ccu.c index 8f37a9213c..4e21eeafdd 100644 --- a/hw/misc/allwinner-r40-ccu.c +++ b/hw/misc/allwinner-r40-ccu.c @@ -185,7 +185,7 @@ static const VMStateDescription allwinner_r40_ccu_vmstate = { } }; -static void allwinner_r40_ccu_class_init(ObjectClass *klass, void *data) +static void allwinner_r40_ccu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/allwinner-r40-dramc.c b/hw/misc/allwinner-r40-dramc.c index 5908a059e8..1c8e17e3c0 100644 --- a/hw/misc/allwinner-r40-dramc.c +++ b/hw/misc/allwinner-r40-dramc.c @@ -484,7 +484,7 @@ static const VMStateDescription allwinner_r40_dramc_vmstate = { } }; -static void allwinner_r40_dramc_class_init(ObjectClass *klass, void *data) +static void allwinner_r40_dramc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/allwinner-sid.c b/hw/misc/allwinner-sid.c index 2bb81f9c54..1e66c14567 100644 --- a/hw/misc/allwinner-sid.c +++ b/hw/misc/allwinner-sid.c @@ -143,7 +143,7 @@ static const VMStateDescription allwinner_sid_vmstate = { } }; -static void allwinner_sid_class_init(ObjectClass *klass, void *data) +static void allwinner_sid_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/allwinner-sramc.c b/hw/misc/allwinner-sramc.c index 51df5e45aa..ed299ecaae 100644 --- a/hw/misc/allwinner-sramc.c +++ b/hw/misc/allwinner-sramc.c @@ -135,7 +135,7 @@ static void allwinner_sramc_reset(DeviceState *dev) } } -static void allwinner_sramc_class_init(ObjectClass *klass, void *data) +static void allwinner_sramc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -163,7 +163,7 @@ static const TypeInfo allwinner_sramc_info = { .class_init = allwinner_sramc_class_init, }; -static void allwinner_r40_sramc_class_init(ObjectClass *klass, void *data) +static void allwinner_r40_sramc_class_init(ObjectClass *klass, const void *data) { AwSRAMCClass *sc = AW_SRAMC_CLASS(klass); diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index 97ea842d60..d83a81b60d 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -375,7 +375,7 @@ static void build_applesmc_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(scope, dev); } -static void qdev_applesmc_class_init(ObjectClass *klass, void *data) +static void qdev_applesmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); diff --git a/hw/misc/arm11scu.c b/hw/misc/arm11scu.c index 02493cec31..2ad4fd1d21 100644 --- a/hw/misc/arm11scu.c +++ b/hw/misc/arm11scu.c @@ -79,7 +79,7 @@ static const Property arm11_scu_properties[] = { DEFINE_PROP_UINT32("num-cpu", ARM11SCUState, num_cpu, 1), }; -static void arm11_scu_class_init(ObjectClass *oc, void *data) +static void arm11_scu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/misc/arm_l2x0.c b/hw/misc/arm_l2x0.c index 39b4642da7..8b4b61eed0 100644 --- a/hw/misc/arm_l2x0.c +++ b/hw/misc/arm_l2x0.c @@ -177,7 +177,7 @@ static const Property l2x0_properties[] = { DEFINE_PROP_UINT32("cache-type", L2x0State, cache_type, 0x1c100100), }; -static void l2x0_class_init(ObjectClass *klass, void *data) +static void l2x0_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/arm_sysctl.c b/hw/misc/arm_sysctl.c index 01663407ec..0f4e37cd47 100644 --- a/hw/misc/arm_sysctl.c +++ b/hw/misc/arm_sysctl.c @@ -634,7 +634,7 @@ static const Property arm_sysctl_properties[] = { db_clock_reset, qdev_prop_uint32, uint32_t), }; -static void arm_sysctl_class_init(ObjectClass *klass, void *data) +static void arm_sysctl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/armsse-cpu-pwrctrl.c b/hw/misc/armsse-cpu-pwrctrl.c index 2d3a0ac29c..66e9218f27 100644 --- a/hw/misc/armsse-cpu-pwrctrl.c +++ b/hw/misc/armsse-cpu-pwrctrl.c @@ -125,7 +125,7 @@ static void pwrctrl_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static void pwrctrl_class_init(ObjectClass *klass, void *data) +static void pwrctrl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/armsse-cpuid.c b/hw/misc/armsse-cpuid.c index 58cb37333f..a57764d731 100644 --- a/hw/misc/armsse-cpuid.c +++ b/hw/misc/armsse-cpuid.c @@ -106,7 +106,7 @@ static void armsse_cpuid_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static void armsse_cpuid_class_init(ObjectClass *klass, void *data) +static void armsse_cpuid_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/armsse-mhu.c b/hw/misc/armsse-mhu.c index 91c49108b0..d5d307a186 100644 --- a/hw/misc/armsse-mhu.c +++ b/hw/misc/armsse-mhu.c @@ -176,7 +176,7 @@ static void armsse_mhu_init(Object *obj) sysbus_init_irq(sbd, &s->cpu1irq); } -static void armsse_mhu_class_init(ObjectClass *klass, void *data) +static void armsse_mhu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/armv7m_ras.c b/hw/misc/armv7m_ras.c index de24922c94..7bf5acd0a5 100644 --- a/hw/misc/armv7m_ras.c +++ b/hw/misc/armv7m_ras.c @@ -72,7 +72,7 @@ static void armv7m_ras_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static void armv7m_ras_class_init(ObjectClass *klass, void *data) +static void armv7m_ras_class_init(ObjectClass *klass, const void *data) { /* This device has no state: no need for vmstate or reset */ } diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index d75da33353..f4bff32a00 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -479,7 +479,7 @@ static const VMStateDescription vmstate_aspeed_hace = { } }; -static void aspeed_hace_class_init(ObjectClass *klass, void *data) +static void aspeed_hace_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -497,7 +497,7 @@ static const TypeInfo aspeed_hace_info = { .class_size = sizeof(AspeedHACEClass) }; -static void aspeed_ast2400_hace_class_init(ObjectClass *klass, void *data) +static void aspeed_ast2400_hace_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); @@ -516,7 +516,7 @@ static const TypeInfo aspeed_ast2400_hace_info = { .class_init = aspeed_ast2400_hace_class_init, }; -static void aspeed_ast2500_hace_class_init(ObjectClass *klass, void *data) +static void aspeed_ast2500_hace_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); @@ -535,7 +535,7 @@ static const TypeInfo aspeed_ast2500_hace_info = { .class_init = aspeed_ast2500_hace_class_init, }; -static void aspeed_ast2600_hace_class_init(ObjectClass *klass, void *data) +static void aspeed_ast2600_hace_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); @@ -554,7 +554,7 @@ static const TypeInfo aspeed_ast2600_hace_info = { .class_init = aspeed_ast2600_hace_class_init, }; -static void aspeed_ast1030_hace_class_init(ObjectClass *klass, void *data) +static void aspeed_ast1030_hace_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); @@ -573,7 +573,7 @@ static const TypeInfo aspeed_ast1030_hace_info = { .class_init = aspeed_ast1030_hace_class_init, }; -static void aspeed_ast2700_hace_class_init(ObjectClass *klass, void *data) +static void aspeed_ast2700_hace_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedHACEClass *ahc = ASPEED_HACE_CLASS(klass); diff --git a/hw/misc/aspeed_i3c.c b/hw/misc/aspeed_i3c.c index ab39c6435b..3bef1c84dd 100644 --- a/hw/misc/aspeed_i3c.c +++ b/hw/misc/aspeed_i3c.c @@ -327,7 +327,7 @@ static const Property aspeed_i3c_device_properties[] = { DEFINE_PROP_UINT8("device-id", AspeedI3CDevice, id, 0), }; -static void aspeed_i3c_device_class_init(ObjectClass *klass, void *data) +static void aspeed_i3c_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -356,7 +356,7 @@ static const VMStateDescription vmstate_aspeed_i3c = { } }; -static void aspeed_i3c_class_init(ObjectClass *klass, void *data) +static void aspeed_i3c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/aspeed_lpc.c b/hw/misc/aspeed_lpc.c index 228d250dc0..78406dae24 100644 --- a/hw/misc/aspeed_lpc.c +++ b/hw/misc/aspeed_lpc.c @@ -458,7 +458,7 @@ static const Property aspeed_lpc_properties[] = { DEFINE_PROP_UINT32("hicr7", AspeedLPCState, hicr7, 0), }; -static void aspeed_lpc_class_init(ObjectClass *klass, void *data) +static void aspeed_lpc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/aspeed_peci.c b/hw/misc/aspeed_peci.c index 9025b35f83..a7a449a923 100644 --- a/hw/misc/aspeed_peci.c +++ b/hw/misc/aspeed_peci.c @@ -130,7 +130,7 @@ static void aspeed_peci_reset(DeviceState *dev) memset(s->regs, 0, sizeof(s->regs)); } -static void aspeed_peci_class_init(ObjectClass *klass, void *data) +static void aspeed_peci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/aspeed_sbc.c b/hw/misc/aspeed_sbc.c index e4a6bd1581..a7d101ba71 100644 --- a/hw/misc/aspeed_sbc.c +++ b/hw/misc/aspeed_sbc.c @@ -141,7 +141,7 @@ static const Property aspeed_sbc_properties[] = { DEFINE_PROP_UINT32("signing-settings", AspeedSBCState, signing_settings, 0), }; -static void aspeed_sbc_class_init(ObjectClass *klass, void *data) +static void aspeed_sbc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -159,7 +159,7 @@ static const TypeInfo aspeed_sbc_info = { .class_size = sizeof(AspeedSBCClass) }; -static void aspeed_ast2600_sbc_class_init(ObjectClass *klass, void *data) +static void aspeed_ast2600_sbc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 1af1a35a08..4930e00fed 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -618,7 +618,7 @@ static const Property aspeed_scu_properties[] = { DEFINE_PROP_UINT32("hw-prot-key", AspeedSCUState, hw_prot_key, 0), }; -static void aspeed_scu_class_init(ObjectClass *klass, void *data) +static void aspeed_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_scu_realize; @@ -637,7 +637,7 @@ static const TypeInfo aspeed_scu_info = { .abstract = true, }; -static void aspeed_2400_scu_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); @@ -659,7 +659,7 @@ static const TypeInfo aspeed_2400_scu_info = { .class_init = aspeed_2400_scu_class_init, }; -static void aspeed_2500_scu_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); @@ -835,7 +835,7 @@ static void aspeed_ast2600_scu_reset(DeviceState *dev) s->regs[PROT_KEY] = s->hw_prot_key; } -static void aspeed_2600_scu_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); @@ -954,7 +954,7 @@ static void aspeed_ast2700_scu_reset(DeviceState *dev) s->regs[AST2700_HW_STRAP1] = s->hw_strap1; } -static void aspeed_2700_scu_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); @@ -1068,7 +1068,7 @@ static const uint32_t ast2700_a0_resets_io[ASPEED_AST2700_SCU_NR_REGS] = { [AST2700_SCUIO_CLK_DUTY_MEAS_RST] = 0x0c9100d2, }; -static void aspeed_2700_scuio_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_scuio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); @@ -1126,7 +1126,7 @@ static void aspeed_ast1030_scu_reset(DeviceState *dev) s->regs[PROT_KEY] = s->hw_prot_key; } -static void aspeed_1030_scu_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_scu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSCUClass *asc = ASPEED_SCU_CLASS(klass); diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index f359640a9a..f04d9930dd 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -299,7 +299,7 @@ static const Property aspeed_sdmc_properties[] = { DEFINE_PROP_BOOL("unlocked", AspeedSDMCState, unlocked, false), }; -static void aspeed_sdmc_class_init(ObjectClass *klass, void *data) +static void aspeed_sdmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_sdmc_realize; @@ -380,7 +380,7 @@ static void aspeed_2400_sdmc_write(AspeedSDMCState *s, uint32_t reg, static const uint64_t aspeed_2400_ram_sizes[] = { 64 * MiB, 128 * MiB, 256 * MiB, 512 * MiB, 0}; -static void aspeed_2400_sdmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_sdmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass); @@ -448,7 +448,7 @@ static void aspeed_2500_sdmc_write(AspeedSDMCState *s, uint32_t reg, static const uint64_t aspeed_2500_ram_sizes[] = { 128 * MiB, 256 * MiB, 512 * MiB, 1024 * MiB, 0}; -static void aspeed_2500_sdmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_sdmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass); @@ -542,7 +542,7 @@ static void aspeed_2600_sdmc_write(AspeedSDMCState *s, uint32_t reg, static const uint64_t aspeed_2600_ram_sizes[] = { 256 * MiB, 512 * MiB, 1024 * MiB, 2048 * MiB, 0}; -static void aspeed_2600_sdmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_sdmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass); @@ -670,7 +670,7 @@ static const uint64_t aspeed_2700_ram_sizes[] = { 256 * MiB, 512 * MiB, 1024 * MiB, 2048 * MiB, 4096 * MiB, 8192 * MiB, 0}; -static void aspeed_2700_sdmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_sdmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSDMCClass *asc = ASPEED_SDMC_CLASS(klass); diff --git a/hw/misc/aspeed_sli.c b/hw/misc/aspeed_sli.c index fe720ead50..c51484035e 100644 --- a/hw/misc/aspeed_sli.c +++ b/hw/misc/aspeed_sli.c @@ -124,7 +124,7 @@ static void aspeed_sliio_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static void aspeed_sli_class_init(ObjectClass *klass, void *data) +static void aspeed_sli_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -140,14 +140,14 @@ static const TypeInfo aspeed_sli_info = { .abstract = true, }; -static void aspeed_2700_sli_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_sli_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->desc = "AST2700 SLI Controller"; } -static void aspeed_2700_sliio_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_sliio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/aspeed_xdma.c b/hw/misc/aspeed_xdma.c index 1dd32f72f4..cc03c422b0 100644 --- a/hw/misc/aspeed_xdma.c +++ b/hw/misc/aspeed_xdma.c @@ -150,7 +150,7 @@ static const VMStateDescription aspeed_xdma_vmstate = { }, }; -static void aspeed_2600_xdma_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_xdma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedXDMAClass *axc = ASPEED_XDMA_CLASS(klass); @@ -173,7 +173,7 @@ static const TypeInfo aspeed_2600_xdma_info = { .class_init = aspeed_2600_xdma_class_init, }; -static void aspeed_2500_xdma_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_xdma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedXDMAClass *axc = ASPEED_XDMA_CLASS(klass); @@ -195,7 +195,7 @@ static const TypeInfo aspeed_2500_xdma_info = { .class_init = aspeed_2500_xdma_class_init, }; -static void aspeed_2400_xdma_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_xdma_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedXDMAClass *axc = ASPEED_XDMA_CLASS(klass); @@ -217,7 +217,7 @@ static const TypeInfo aspeed_2400_xdma_info = { .class_init = aspeed_2400_xdma_class_init, }; -static void aspeed_xdma_class_init(ObjectClass *classp, void *data) +static void aspeed_xdma_class_init(ObjectClass *classp, const void *data) { DeviceClass *dc = DEVICE_CLASS(classp); diff --git a/hw/misc/auxbus.c b/hw/misc/auxbus.c index 28d50d9d09..877f345606 100644 --- a/hw/misc/auxbus.c +++ b/hw/misc/auxbus.c @@ -50,7 +50,7 @@ static void aux_slave_dev_print(Monitor *mon, DeviceState *dev, int indent); static inline I2CBus *aux_bridge_get_i2c_bus(AUXTOI2CState *bridge); /* aux-bus implementation (internal not public) */ -static void aux_bus_class_init(ObjectClass *klass, void *data) +static void aux_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -256,7 +256,7 @@ struct AUXTOI2CState { I2CBus *i2c_bus; }; -static void aux_bridge_class_init(ObjectClass *oc, void *data) +static void aux_bridge_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -311,7 +311,7 @@ void aux_init_mmio(AUXSlave *aux_slave, MemoryRegion *mmio) aux_slave->mmio = mmio; } -static void aux_slave_class_init(ObjectClass *klass, void *data) +static void aux_slave_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); diff --git a/hw/misc/avr_power.c b/hw/misc/avr_power.c index ac7b96f53e..411f016c99 100644 --- a/hw/misc/avr_power.c +++ b/hw/misc/avr_power.c @@ -90,7 +90,7 @@ static void avr_mask_init(Object *dev) s->val = 0x00; } -static void avr_mask_class_init(ObjectClass *klass, void *data) +static void avr_mask_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/axp2xx.c b/hw/misc/axp2xx.c index af646878cd..46d17712dd 100644 --- a/hw/misc/axp2xx.c +++ b/hw/misc/axp2xx.c @@ -225,7 +225,7 @@ static const VMStateDescription vmstate_axp2xx = { } }; -static void axp2xx_class_init(ObjectClass *oc, void *data) +static void axp2xx_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); I2CSlaveClass *isc = I2C_SLAVE_CLASS(oc); @@ -247,7 +247,7 @@ static const TypeInfo axp2xx_info = { .abstract = true, }; -static void axp209_class_init(ObjectClass *oc, void *data) +static void axp209_class_init(ObjectClass *oc, const void *data) { AXP2xxClass *sc = AXP2XX_CLASS(oc); @@ -260,7 +260,7 @@ static const TypeInfo axp209_info = { .class_init = axp209_class_init }; -static void axp221_class_init(ObjectClass *oc, void *data) +static void axp221_class_init(ObjectClass *oc, const void *data) { AXP2xxClass *sc = AXP2XX_CLASS(oc); diff --git a/hw/misc/bcm2835_cprman.c b/hw/misc/bcm2835_cprman.c index 0c4d4b7de5..efe6f900db 100644 --- a/hw/misc/bcm2835_cprman.c +++ b/hw/misc/bcm2835_cprman.c @@ -131,7 +131,7 @@ static const VMStateDescription pll_vmstate = { } }; -static void pll_class_init(ObjectClass *klass, void *data) +static void pll_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -237,7 +237,7 @@ static const VMStateDescription pll_channel_vmstate = { } }; -static void pll_channel_class_init(ObjectClass *klass, void *data) +static void pll_channel_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -360,7 +360,7 @@ static const VMStateDescription clock_mux_vmstate = { } }; -static void clock_mux_class_init(ObjectClass *klass, void *data) +static void clock_mux_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -417,7 +417,7 @@ static const VMStateDescription dsi0hsck_mux_vmstate = { } }; -static void dsi0hsck_mux_class_init(ObjectClass *klass, void *data) +static void dsi0hsck_mux_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -790,7 +790,7 @@ static const Property cprman_properties[] = { DEFINE_PROP_UINT32("xosc-freq-hz", BCM2835CprmanState, xosc_freq, 19200000), }; -static void cprman_class_init(ObjectClass *klass, void *data) +static void cprman_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/bcm2835_mbox.c b/hw/misc/bcm2835_mbox.c index ed6dbea191..603eaaa710 100644 --- a/hw/misc/bcm2835_mbox.c +++ b/hw/misc/bcm2835_mbox.c @@ -314,7 +314,7 @@ static void bcm2835_mbox_realize(DeviceState *dev, Error **errp) bcm2835_mbox_reset(dev); } -static void bcm2835_mbox_class_init(ObjectClass *klass, void *data) +static void bcm2835_mbox_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/bcm2835_mphi.c b/hw/misc/bcm2835_mphi.c index 7309cf22fc..55d79e7e87 100644 --- a/hw/misc/bcm2835_mphi.c +++ b/hw/misc/bcm2835_mphi.c @@ -166,7 +166,7 @@ const VMStateDescription vmstate_mphi_state = { } }; -static void mphi_class_init(ObjectClass *klass, void *data) +static void mphi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/bcm2835_powermgt.c b/hw/misc/bcm2835_powermgt.c index e4e9bae374..3ec7abad0e 100644 --- a/hw/misc/bcm2835_powermgt.c +++ b/hw/misc/bcm2835_powermgt.c @@ -136,7 +136,7 @@ static void bcm2835_powermgt_reset(DeviceState *dev) s->wdog = 0x00000000; } -static void bcm2835_powermgt_class_init(ObjectClass *klass, void *data) +static void bcm2835_powermgt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/bcm2835_property.c b/hw/misc/bcm2835_property.c index 2bae64b64c..a21c6a541c 100644 --- a/hw/misc/bcm2835_property.c +++ b/hw/misc/bcm2835_property.c @@ -556,7 +556,7 @@ static const Property bcm2835_property_props[] = { DEFINE_PROP_STRING("command-line", BCM2835PropertyState, command_line), }; -static void bcm2835_property_class_init(ObjectClass *klass, void *data) +static void bcm2835_property_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/bcm2835_rng.c b/hw/misc/bcm2835_rng.c index 06f40817df..e4d2c224c8 100644 --- a/hw/misc/bcm2835_rng.c +++ b/hw/misc/bcm2835_rng.c @@ -123,7 +123,7 @@ static void bcm2835_rng_reset(DeviceState *dev) s->rng_status = 0; } -static void bcm2835_rng_class_init(ObjectClass *klass, void *data) +static void bcm2835_rng_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/bcm2835_thermal.c b/hw/misc/bcm2835_thermal.c index 1c1b0671cc..33bfc91c7a 100644 --- a/hw/misc/bcm2835_thermal.c +++ b/hw/misc/bcm2835_thermal.c @@ -113,7 +113,7 @@ static const VMStateDescription bcm2835_thermal_vmstate = { } }; -static void bcm2835_thermal_class_init(ObjectClass *klass, void *data) +static void bcm2835_thermal_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/debugexit.c b/hw/misc/debugexit.c index 577b884494..04a9fc3122 100644 --- a/hw/misc/debugexit.c +++ b/hw/misc/debugexit.c @@ -61,7 +61,7 @@ static const Property debug_exit_properties[] = { DEFINE_PROP_UINT32("iosize", ISADebugExitState, iosize, 0x02), }; -static void debug_exit_class_initfn(ObjectClass *klass, void *data) +static void debug_exit_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/djmemc.c b/hw/misc/djmemc.c index 96d5efb5e3..c5b09f551b 100644 --- a/hw/misc/djmemc.c +++ b/hw/misc/djmemc.c @@ -113,7 +113,7 @@ static const VMStateDescription vmstate_djmemc = { } }; -static void djmemc_class_init(ObjectClass *oc, void *data) +static void djmemc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ResettableClass *rc = RESETTABLE_CLASS(oc); diff --git a/hw/misc/eccmemctl.c b/hw/misc/eccmemctl.c index d7452c4cc8..81fc536131 100644 --- a/hw/misc/eccmemctl.c +++ b/hw/misc/eccmemctl.c @@ -329,7 +329,7 @@ static const Property ecc_properties[] = { DEFINE_PROP_UINT32("version", ECCState, version, -1), }; -static void ecc_class_init(ObjectClass *klass, void *data) +static void ecc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/edu.c b/hw/misc/edu.c index 5723ef0ed1..8224603593 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -415,7 +415,7 @@ static void edu_instance_init(Object *obj) &edu->dma_mask, OBJ_PROP_FLAG_READWRITE); } -static void edu_class_init(ObjectClass *class, void *data) +static void edu_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *k = PCI_DEVICE_CLASS(class); diff --git a/hw/misc/empty_slot.c b/hw/misc/empty_slot.c index 221ea7cb54..239d760320 100644 --- a/hw/misc/empty_slot.c +++ b/hw/misc/empty_slot.c @@ -84,7 +84,7 @@ static const Property empty_slot_properties[] = { DEFINE_PROP_STRING("name", EmptySlot, name), }; -static void empty_slot_class_init(ObjectClass *klass, void *data) +static void empty_slot_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/exynos4210_clk.c b/hw/misc/exynos4210_clk.c index 886d10bbab..fdf5bdd603 100644 --- a/hw/misc/exynos4210_clk.c +++ b/hw/misc/exynos4210_clk.c @@ -141,7 +141,7 @@ static const VMStateDescription exynos4210_clk_vmstate = { } }; -static void exynos4210_clk_class_init(ObjectClass *klass, void *data) +static void exynos4210_clk_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/exynos4210_pmu.c b/hw/misc/exynos4210_pmu.c index d44aac3af5..a86ec9aba8 100644 --- a/hw/misc/exynos4210_pmu.c +++ b/hw/misc/exynos4210_pmu.c @@ -498,7 +498,7 @@ static const VMStateDescription exynos4210_pmu_vmstate = { } }; -static void exynos4210_pmu_class_init(ObjectClass *klass, void *data) +static void exynos4210_pmu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/exynos4210_rng.c b/hw/misc/exynos4210_rng.c index a741cf176b..2d0ebc457b 100644 --- a/hw/misc/exynos4210_rng.c +++ b/hw/misc/exynos4210_rng.c @@ -255,7 +255,7 @@ static const VMStateDescription exynos4210_rng_vmstate = { } }; -static void exynos4210_rng_class_init(ObjectClass *klass, void *data) +static void exynos4210_rng_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/grlib_ahb_apb_pnp.c b/hw/misc/grlib_ahb_apb_pnp.c index 5b05f15859..cdca00ad54 100644 --- a/hw/misc/grlib_ahb_apb_pnp.c +++ b/hw/misc/grlib_ahb_apb_pnp.c @@ -168,7 +168,7 @@ static void grlib_ahb_pnp_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &ahb_pnp->iomem); } -static void grlib_ahb_pnp_class_init(ObjectClass *klass, void *data) +static void grlib_ahb_pnp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -280,7 +280,7 @@ static void grlib_apb_pnp_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &apb_pnp->iomem); } -static void grlib_apb_pnp_class_init(ObjectClass *klass, void *data) +static void grlib_apb_pnp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/i2c-echo.c b/hw/misc/i2c-echo.c index fcd407dfc6..2bb99ec0db 100644 --- a/hw/misc/i2c-echo.c +++ b/hw/misc/i2c-echo.c @@ -145,7 +145,7 @@ static void i2c_echo_realize(DeviceState *dev, Error **errp) state->bh = qemu_bh_new(i2c_echo_bh, state); } -static void i2c_echo_class_init(ObjectClass *oc, void *data) +static void i2c_echo_class_init(ObjectClass *oc, const void *data) { I2CSlaveClass *sc = I2C_SLAVE_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/misc/imx25_ccm.c b/hw/misc/imx25_ccm.c index 9654d23f19..a6665d5535 100644 --- a/hw/misc/imx25_ccm.c +++ b/hw/misc/imx25_ccm.c @@ -292,7 +292,7 @@ static void imx25_ccm_init(Object *obj) sysbus_init_mmio(sd, &s->iomem); } -static void imx25_ccm_class_init(ObjectClass *klass, void *data) +static void imx25_ccm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IMXCCMClass *ccm = IMX_CCM_CLASS(klass); diff --git a/hw/misc/imx31_ccm.c b/hw/misc/imx31_ccm.c index 93130b24e5..339458e859 100644 --- a/hw/misc/imx31_ccm.c +++ b/hw/misc/imx31_ccm.c @@ -319,7 +319,7 @@ static void imx31_ccm_init(Object *obj) sysbus_init_mmio(sd, &s->iomem); } -static void imx31_ccm_class_init(ObjectClass *klass, void *data) +static void imx31_ccm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IMXCCMClass *ccm = IMX_CCM_CLASS(klass); diff --git a/hw/misc/imx6_ccm.c b/hw/misc/imx6_ccm.c index 7d522ed7c5..a10b67d396 100644 --- a/hw/misc/imx6_ccm.c +++ b/hw/misc/imx6_ccm.c @@ -741,7 +741,7 @@ static void imx6_ccm_init(Object *obj) sysbus_init_mmio(sd, &s->container); } -static void imx6_ccm_class_init(ObjectClass *klass, void *data) +static void imx6_ccm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IMXCCMClass *ccm = IMX_CCM_CLASS(klass); diff --git a/hw/misc/imx6_src.c b/hw/misc/imx6_src.c index 06cc46292e..8d2c4175e4 100644 --- a/hw/misc/imx6_src.c +++ b/hw/misc/imx6_src.c @@ -273,7 +273,7 @@ static void imx6_src_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); } -static void imx6_src_class_init(ObjectClass *klass, void *data) +static void imx6_src_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/imx6ul_ccm.c b/hw/misc/imx6ul_ccm.c index c836dfe494..7f3ae61710 100644 --- a/hw/misc/imx6ul_ccm.c +++ b/hw/misc/imx6ul_ccm.c @@ -904,7 +904,7 @@ static void imx6ul_ccm_init(Object *obj) sysbus_init_mmio(sd, &s->container); } -static void imx6ul_ccm_class_init(ObjectClass *klass, void *data) +static void imx6ul_ccm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IMXCCMClass *ccm = IMX_CCM_CLASS(klass); diff --git a/hw/misc/imx7_ccm.c b/hw/misc/imx7_ccm.c index c3ecfd78c1..c061a584e4 100644 --- a/hw/misc/imx7_ccm.c +++ b/hw/misc/imx7_ccm.c @@ -262,7 +262,7 @@ static uint32_t imx7_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) return freq; } -static void imx7_ccm_class_init(ObjectClass *klass, void *data) +static void imx7_ccm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IMXCCMClass *ccm = IMX_CCM_CLASS(klass); @@ -293,7 +293,7 @@ static const VMStateDescription vmstate_imx7_analog = { }, }; -static void imx7_analog_class_init(ObjectClass *klass, void *data) +static void imx7_analog_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/imx7_gpr.c b/hw/misc/imx7_gpr.c index b03341a2eb..e12b496273 100644 --- a/hw/misc/imx7_gpr.c +++ b/hw/misc/imx7_gpr.c @@ -102,7 +102,7 @@ static void imx7_gpr_init(Object *obj) sysbus_init_mmio(sd, &s->mmio); } -static void imx7_gpr_class_init(ObjectClass *klass, void *data) +static void imx7_gpr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/imx7_snvs.c b/hw/misc/imx7_snvs.c index c8a096bc13..6a8733d23d 100644 --- a/hw/misc/imx7_snvs.c +++ b/hw/misc/imx7_snvs.c @@ -143,7 +143,7 @@ static void imx7_snvs_init(Object *obj) qemu_clock_get_ns(rtc_clock) / NANOSECONDS_PER_SECOND; } -static void imx7_snvs_class_init(ObjectClass *klass, void *data) +static void imx7_snvs_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/imx7_src.c b/hw/misc/imx7_src.c index 35341c6819..df0b0a6905 100644 --- a/hw/misc/imx7_src.c +++ b/hw/misc/imx7_src.c @@ -251,7 +251,7 @@ static void imx7_src_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); } -static void imx7_src_class_init(ObjectClass *klass, void *data) +static void imx7_src_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/imx8mp_analog.c b/hw/misc/imx8mp_analog.c index f7e7c83cc4..23ffae84f8 100644 --- a/hw/misc/imx8mp_analog.c +++ b/hw/misc/imx8mp_analog.c @@ -138,7 +138,7 @@ static const VMStateDescription imx8mp_analog_vmstate = { }, }; -static void imx8mp_analog_class_init(ObjectClass *klass, void *data) +static void imx8mp_analog_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/imx8mp_ccm.c b/hw/misc/imx8mp_ccm.c index 1a1c932427..911911ed86 100644 --- a/hw/misc/imx8mp_ccm.c +++ b/hw/misc/imx8mp_ccm.c @@ -150,7 +150,7 @@ static uint32_t imx8mp_ccm_get_clock_frequency(IMXCCMState *dev, IMXClk clock) return freq; } -static void imx8mp_ccm_class_init(ObjectClass *klass, void *data) +static void imx8mp_ccm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); IMXCCMClass *ccm = IMX_CCM_CLASS(klass); diff --git a/hw/misc/imx_rngc.c b/hw/misc/imx_rngc.c index 0cbf28db5d..630f6cb54b 100644 --- a/hw/misc/imx_rngc.c +++ b/hw/misc/imx_rngc.c @@ -254,7 +254,7 @@ static const VMStateDescription vmstate_imx_rngc = { } }; -static void imx_rngc_class_init(ObjectClass *klass, void *data) +static void imx_rngc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/iosb.c b/hw/misc/iosb.c index 31927eaedb..96221e1ee5 100644 --- a/hw/misc/iosb.c +++ b/hw/misc/iosb.c @@ -111,7 +111,7 @@ static const VMStateDescription vmstate_iosb = { } }; -static void iosb_class_init(ObjectClass *oc, void *data) +static void iosb_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ResettableClass *rc = RESETTABLE_CLASS(oc); diff --git a/hw/misc/iotkit-secctl.c b/hw/misc/iotkit-secctl.c index 04ced3559c..afd9ab48df 100644 --- a/hw/misc/iotkit-secctl.c +++ b/hw/misc/iotkit-secctl.c @@ -818,7 +818,7 @@ static const Property iotkit_secctl_props[] = { DEFINE_PROP_UINT32("sse-version", IoTKitSecCtl, sse_version, 0), }; -static void iotkit_secctl_class_init(ObjectClass *klass, void *data) +static void iotkit_secctl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/iotkit-sysctl.c b/hw/misc/iotkit-sysctl.c index c654af2e88..d70e51ab2e 100644 --- a/hw/misc/iotkit-sysctl.c +++ b/hw/misc/iotkit-sysctl.c @@ -844,7 +844,7 @@ static const Property iotkit_sysctl_props[] = { 0x10000000), }; -static void iotkit_sysctl_class_init(ObjectClass *klass, void *data) +static void iotkit_sysctl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/iotkit-sysinfo.c b/hw/misc/iotkit-sysinfo.c index 75260f7fab..57405cb7e1 100644 --- a/hw/misc/iotkit-sysinfo.c +++ b/hw/misc/iotkit-sysinfo.c @@ -158,7 +158,7 @@ static void iotkit_sysinfo_realize(DeviceState *dev, Error **errp) } } -static void iotkit_sysinfo_class_init(ObjectClass *klass, void *data) +static void iotkit_sysinfo_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c index c4e82a0827..be28c24d73 100644 --- a/hw/misc/ivshmem-flat.c +++ b/hw/misc/ivshmem-flat.c @@ -431,7 +431,7 @@ static const Property ivshmem_flat_props[] = { DEFINE_PROP_UINT32("shmem-size", IvshmemFTState, shmem_size, 4 * MiB), }; -static void ivshmem_flat_class_init(ObjectClass *klass, void *data) +static void ivshmem_flat_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c index 900d523334..2844b6f909 100644 --- a/hw/misc/ivshmem-pci.c +++ b/hw/misc/ivshmem-pci.c @@ -979,7 +979,7 @@ static int ivshmem_post_load(void *opaque, int version_id) return 0; } -static void ivshmem_common_class_init(ObjectClass *klass, void *data) +static void ivshmem_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1044,7 +1044,7 @@ static void ivshmem_plain_realize(PCIDevice *dev, Error **errp) ivshmem_common_realize(dev, errp); } -static void ivshmem_plain_class_init(ObjectClass *klass, void *data) +static void ivshmem_plain_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1103,7 +1103,7 @@ static void ivshmem_doorbell_realize(PCIDevice *dev, Error **errp) ivshmem_common_realize(dev, errp); } -static void ivshmem_doorbell_class_init(ObjectClass *klass, void *data) +static void ivshmem_doorbell_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/misc/lasi.c b/hw/misc/lasi.c index 24d20ffcb8..9f758c6a86 100644 --- a/hw/misc/lasi.c +++ b/hw/misc/lasi.c @@ -263,7 +263,7 @@ static void lasi_init(Object *obj) qdev_init_gpio_in(DEVICE(obj), lasi_set_irq, LASI_IRQS); } -static void lasi_class_init(ObjectClass *klass, void *data) +static void lasi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/led.c b/hw/misc/led.c index 9364d9945e..f7f709072a 100644 --- a/hw/misc/led.c +++ b/hw/misc/led.c @@ -107,7 +107,7 @@ static const Property led_properties[] = { DEFINE_PROP_BOOL("gpio-active-high", LEDState, gpio_active_high, true), }; -static void led_class_init(ObjectClass *klass, void *data) +static void led_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/mac_via.c b/hw/misc/mac_via.c index 3c0819c58a..bc37e2a2cb 100644 --- a/hw/misc/mac_via.c +++ b/hw/misc/mac_via.c @@ -1326,7 +1326,7 @@ static const Property mos6522_q800_via1_properties[] = { DEFINE_PROP_DRIVE("drive", MOS6522Q800VIA1State, blk), }; -static void mos6522_q800_via1_class_init(ObjectClass *oc, void *data) +static void mos6522_q800_via1_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ResettableClass *rc = RESETTABLE_CLASS(oc); @@ -1415,7 +1415,7 @@ static const VMStateDescription vmstate_q800_via2 = { } }; -static void mos6522_q800_via2_class_init(ObjectClass *oc, void *data) +static void mos6522_q800_via2_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ResettableClass *rc = RESETTABLE_CLASS(oc); diff --git a/hw/misc/macio/cuda.c b/hw/misc/macio/cuda.c index 34731ae560..bcd00c9bb1 100644 --- a/hw/misc/macio/cuda.c +++ b/hw/misc/macio/cuda.c @@ -558,7 +558,7 @@ static const Property cuda_properties[] = { DEFINE_PROP_UINT64("timebase-frequency", CUDAState, tb_frequency, 0), }; -static void cuda_class_init(ObjectClass *oc, void *data) +static void cuda_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -598,7 +598,7 @@ static void mos6522_cuda_reset_hold(Object *obj, ResetType type) ms->timers[1].frequency = (SCALE_US * 6000) / 4700; } -static void mos6522_cuda_class_init(ObjectClass *oc, void *data) +static void mos6522_cuda_class_init(ObjectClass *oc, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index e87bfca1f5..e5d1e1168e 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -194,7 +194,7 @@ static void macio_gpio_nmi(NMIState *n, int cpu_index, Error **errp) macio_set_gpio(MACIO_GPIO(n), 9, false); } -static void macio_gpio_class_init(ObjectClass *oc, void *data) +static void macio_gpio_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); diff --git a/hw/misc/macio/mac_dbdma.c b/hw/misc/macio/mac_dbdma.c index de0f934f7d..b2b42dd562 100644 --- a/hw/misc/macio/mac_dbdma.c +++ b/hw/misc/macio/mac_dbdma.c @@ -917,7 +917,7 @@ static void mac_dbdma_realize(DeviceState *dev, Error **errp) s->bh = qemu_bh_new_guarded(DBDMA_run_bh, s, &dev->mem_reentrancy_guard); } -static void mac_dbdma_class_init(ObjectClass *oc, void *data) +static void mac_dbdma_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index 194b152eff..b0418db49e 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -385,7 +385,7 @@ static const VMStateDescription vmstate_macio_oldworld = { } }; -static void macio_oldworld_class_init(ObjectClass *oc, void *data) +static void macio_oldworld_class_init(ObjectClass *oc, const void *data) { PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -410,7 +410,7 @@ static const Property macio_newworld_properties[] = { DEFINE_PROP_BOOL("has-adb", NewWorldMacIOState, has_adb, false), }; -static void macio_newworld_class_init(ObjectClass *oc, void *data) +static void macio_newworld_class_init(ObjectClass *oc, const void *data) { PCIDeviceClass *pdc = PCI_DEVICE_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -425,7 +425,7 @@ static const Property macio_properties[] = { DEFINE_PROP_UINT64("frequency", MacIOState, frequency, 0), }; -static void macio_class_init(ObjectClass *klass, void *data) +static void macio_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/macio/pmu.c b/hw/misc/macio/pmu.c index 73190559a8..3734913994 100644 --- a/hw/misc/macio/pmu.c +++ b/hw/misc/macio/pmu.c @@ -764,7 +764,7 @@ static const Property pmu_properties[] = { DEFINE_PROP_BOOL("has-adb", PMUState, has_adb, true), }; -static void pmu_class_init(ObjectClass *oc, void *data) +static void pmu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -808,7 +808,7 @@ static void mos6522_pmu_reset_hold(Object *obj, ResetType type) s->last_b = ms->b = TACK | TREQ; } -static void mos6522_pmu_class_init(ObjectClass *oc, void *data) +static void mos6522_pmu_class_init(ObjectClass *oc, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(oc); MOS6522DeviceClass *mdc = MOS6522_CLASS(oc); diff --git a/hw/misc/mchp_pfsoc_dmc.c b/hw/misc/mchp_pfsoc_dmc.c index 43d8e970ab..599f845f45 100644 --- a/hw/misc/mchp_pfsoc_dmc.c +++ b/hw/misc/mchp_pfsoc_dmc.c @@ -110,7 +110,8 @@ static void mchp_pfsoc_ddr_sgmii_phy_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->sgmii_phy); } -static void mchp_pfsoc_ddr_sgmii_phy_class_init(ObjectClass *klass, void *data) +static void mchp_pfsoc_ddr_sgmii_phy_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -192,7 +193,7 @@ static void mchp_pfsoc_ddr_cfg_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->cfg); } -static void mchp_pfsoc_ddr_cfg_class_init(ObjectClass *klass, void *data) +static void mchp_pfsoc_ddr_cfg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/mchp_pfsoc_ioscb.c b/hw/misc/mchp_pfsoc_ioscb.c index a71d134295..10fc7ea2a9 100644 --- a/hw/misc/mchp_pfsoc_ioscb.c +++ b/hw/misc/mchp_pfsoc_ioscb.c @@ -292,7 +292,7 @@ static void mchp_pfsoc_ioscb_realize(DeviceState *dev, Error **errp) sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); } -static void mchp_pfsoc_ioscb_class_init(ObjectClass *klass, void *data) +static void mchp_pfsoc_ioscb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/mchp_pfsoc_sysreg.c b/hw/misc/mchp_pfsoc_sysreg.c index 7876fe0c5b..bfa78d3d2f 100644 --- a/hw/misc/mchp_pfsoc_sysreg.c +++ b/hw/misc/mchp_pfsoc_sysreg.c @@ -85,7 +85,7 @@ static void mchp_pfsoc_sysreg_realize(DeviceState *dev, Error **errp) sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); } -static void mchp_pfsoc_sysreg_class_init(ObjectClass *klass, void *data) +static void mchp_pfsoc_sysreg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/mips_cmgcr.c b/hw/misc/mips_cmgcr.c index 95f19912b4..5484b73967 100644 --- a/hw/misc/mips_cmgcr.c +++ b/hw/misc/mips_cmgcr.c @@ -229,7 +229,7 @@ static void mips_gcr_realize(DeviceState *dev, Error **errp) s->vps = g_new(MIPSGCRVPState, s->num_vps); } -static void mips_gcr_class_init(ObjectClass *klass, void *data) +static void mips_gcr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, mips_gcr_properties); diff --git a/hw/misc/mips_cpc.c b/hw/misc/mips_cpc.c index b7a13d1afb..9bfb7c9721 100644 --- a/hw/misc/mips_cpc.c +++ b/hw/misc/mips_cpc.c @@ -166,7 +166,7 @@ static const Property mips_cpc_properties[] = { DEFINE_PROP_UINT64("vp-start-running", MIPSCPCState, vp_start_running, 0x1), }; -static void mips_cpc_class_init(ObjectClass *klass, void *data) +static void mips_cpc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/mips_itu.c b/hw/misc/mips_itu.c index 2d126ebaf8..fc17385cde 100644 --- a/hw/misc/mips_itu.c +++ b/hw/misc/mips_itu.c @@ -540,7 +540,7 @@ static const Property mips_itu_properties[] = { ITC_SEMAPH_NUM_MAX), }; -static void mips_itu_class_init(ObjectClass *klass, void *data) +static void mips_itu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/mos6522.c b/hw/misc/mos6522.c index 0b8f6a4cb4..8dd6b82ac5 100644 --- a/hw/misc/mos6522.c +++ b/hw/misc/mos6522.c @@ -700,7 +700,7 @@ static const Property mos6522_properties[] = { DEFINE_PROP_UINT64("frequency", MOS6522State, frequency, 0), }; -static void mos6522_class_init(ObjectClass *oc, void *data) +static void mos6522_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); ResettableClass *rc = RESETTABLE_CLASS(oc); diff --git a/hw/misc/mps2-fpgaio.c b/hw/misc/mps2-fpgaio.c index 04a3da5db0..bee1309f5a 100644 --- a/hw/misc/mps2-fpgaio.c +++ b/hw/misc/mps2-fpgaio.c @@ -328,7 +328,7 @@ static const Property mps2_fpgaio_properties[] = { DEFINE_PROP_BOOL("has-dbgctrl", MPS2FPGAIO, has_dbgctrl, false), }; -static void mps2_fpgaio_class_init(ObjectClass *klass, void *data) +static void mps2_fpgaio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/mps2-scc.c b/hw/misc/mps2-scc.c index 5f8d6bca43..a9a5d4a535 100644 --- a/hw/misc/mps2-scc.c +++ b/hw/misc/mps2-scc.c @@ -474,7 +474,7 @@ static const Property mps2_scc_properties[] = { qdev_prop_uint32, uint32_t), }; -static void mps2_scc_class_init(ObjectClass *klass, void *data) +static void mps2_scc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/msf2-sysreg.c b/hw/misc/msf2-sysreg.c index 20009adbd9..ce0ad50c1b 100644 --- a/hw/misc/msf2-sysreg.c +++ b/hw/misc/msf2-sysreg.c @@ -136,7 +136,7 @@ static void msf2_sysreg_realize(DeviceState *dev, Error **errp) } } -static void msf2_sysreg_class_init(ObjectClass *klass, void *data) +static void msf2_sysreg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/npcm7xx_mft.c b/hw/misc/npcm7xx_mft.c index e565cac05d..b35e971fe5 100644 --- a/hw/misc/npcm7xx_mft.c +++ b/hw/misc/npcm7xx_mft.c @@ -515,7 +515,7 @@ static const VMStateDescription vmstate_npcm7xx_mft = { }, }; -static void npcm7xx_mft_class_init(ObjectClass *klass, void *data) +static void npcm7xx_mft_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/npcm7xx_pwm.c b/hw/misc/npcm7xx_pwm.c index f7f77e30a2..2de18d09b8 100644 --- a/hw/misc/npcm7xx_pwm.c +++ b/hw/misc/npcm7xx_pwm.c @@ -543,7 +543,7 @@ static const VMStateDescription vmstate_npcm7xx_pwm_module = { }, }; -static void npcm7xx_pwm_class_init(ObjectClass *klass, void *data) +static void npcm7xx_pwm_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/npcm7xx_rng.c b/hw/misc/npcm7xx_rng.c index 7f7e5eca62..7d47a1caa0 100644 --- a/hw/misc/npcm7xx_rng.c +++ b/hw/misc/npcm7xx_rng.c @@ -158,7 +158,7 @@ static const VMStateDescription vmstate_npcm7xx_rng = { }, }; -static void npcm7xx_rng_class_init(ObjectClass *klass, void *data) +static void npcm7xx_rng_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/npcm_clk.c b/hw/misc/npcm_clk.c index b6a893ffb2..c48d40b446 100644 --- a/hw/misc/npcm_clk.c +++ b/hw/misc/npcm_clk.c @@ -1102,7 +1102,7 @@ static const VMStateDescription vmstate_npcm_clk = { }, }; -static void npcm7xx_clk_pll_class_init(ObjectClass *klass, void *data) +static void npcm7xx_clk_pll_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1112,7 +1112,7 @@ static void npcm7xx_clk_pll_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } -static void npcm7xx_clk_sel_class_init(ObjectClass *klass, void *data) +static void npcm7xx_clk_sel_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1122,7 +1122,7 @@ static void npcm7xx_clk_sel_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } -static void npcm7xx_clk_divider_class_init(ObjectClass *klass, void *data) +static void npcm7xx_clk_divider_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1132,7 +1132,7 @@ static void npcm7xx_clk_divider_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } -static void npcm_clk_class_init(ObjectClass *klass, void *data) +static void npcm_clk_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -1142,7 +1142,7 @@ static void npcm_clk_class_init(ObjectClass *klass, void *data) rc->phases.enter = npcm_clk_enter_reset; } -static void npcm7xx_clk_class_init(ObjectClass *klass, void *data) +static void npcm7xx_clk_class_init(ObjectClass *klass, const void *data) { NPCMCLKClass *c = NPCM_CLK_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -1152,7 +1152,7 @@ static void npcm7xx_clk_class_init(ObjectClass *klass, void *data) c->cold_reset_values = npcm7xx_cold_reset_values; } -static void npcm8xx_clk_class_init(ObjectClass *klass, void *data) +static void npcm8xx_clk_class_init(ObjectClass *klass, const void *data) { NPCMCLKClass *c = NPCM_CLK_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/npcm_gcr.c b/hw/misc/npcm_gcr.c index 4e8ce2cb89..2acaa16771 100644 --- a/hw/misc/npcm_gcr.c +++ b/hw/misc/npcm_gcr.c @@ -422,7 +422,7 @@ static const Property npcm_gcr_properties[] = { DEFINE_PROP_UINT32("power-on-straps", NPCMGCRState, reset_pwron, 0), }; -static void npcm_gcr_class_init(ObjectClass *klass, void *data) +static void npcm_gcr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -432,7 +432,7 @@ static void npcm_gcr_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, npcm_gcr_properties); } -static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data) +static void npcm7xx_gcr_class_init(ObjectClass *klass, const void *data) { NPCMGCRClass *c = NPCM_GCR_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -446,7 +446,7 @@ static void npcm7xx_gcr_class_init(ObjectClass *klass, void *data) rc->phases.enter = npcm7xx_gcr_enter_reset; } -static void npcm8xx_gcr_class_init(ObjectClass *klass, void *data) +static void npcm8xx_gcr_class_init(ObjectClass *klass, const void *data) { NPCMGCRClass *c = NPCM_GCR_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/nrf51_rng.c b/hw/misc/nrf51_rng.c index 1e67acdf23..8cd7ffe3f5 100644 --- a/hw/misc/nrf51_rng.c +++ b/hw/misc/nrf51_rng.c @@ -240,7 +240,7 @@ static const VMStateDescription vmstate_rng = { } }; -static void nrf51_rng_class_init(ObjectClass *klass, void *data) +static void nrf51_rng_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/pc-testdev.c b/hw/misc/pc-testdev.c index e389651869..67c486f347 100644 --- a/hw/misc/pc-testdev.c +++ b/hw/misc/pc-testdev.c @@ -193,7 +193,7 @@ static void testdev_realizefn(DeviceState *d, Error **errp) memory_region_add_subregion(mem, 0xff000000, &dev->iomem); } -static void testdev_class_init(ObjectClass *klass, void *data) +static void testdev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index f6718a7c37..0ea26451f1 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -323,7 +323,7 @@ static const Property pci_testdev_properties[] = { DEFINE_PROP_SIZE("membar", PCITestDevState, membar_size, 0), }; -static void pci_testdev_class_init(ObjectClass *klass, void *data) +static void pci_testdev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c index c3713dc5c4..55522ee56c 100644 --- a/hw/misc/pvpanic-isa.c +++ b/hw/misc/pvpanic-isa.c @@ -104,7 +104,7 @@ static const Property pvpanic_isa_properties[] = { PVPANIC_EVENTS), }; -static void pvpanic_isa_class_init(ObjectClass *klass, void *data) +static void pvpanic_isa_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); diff --git a/hw/misc/pvpanic-mmio.c b/hw/misc/pvpanic-mmio.c index 70097cecc7..2a363106b2 100644 --- a/hw/misc/pvpanic-mmio.c +++ b/hw/misc/pvpanic-mmio.c @@ -36,7 +36,7 @@ static const Property pvpanic_mmio_properties[] = { PVPANIC_PANICKED | PVPANIC_CRASH_LOADED), }; -static void pvpanic_mmio_class_init(ObjectClass *klass, void *data) +static void pvpanic_mmio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c index e5f0788ec0..51ebf66107 100644 --- a/hw/misc/pvpanic-pci.c +++ b/hw/misc/pvpanic-pci.c @@ -58,7 +58,7 @@ static const Property pvpanic_pci_properties[] = { PVPANIC_EVENTS), }; -static void pvpanic_pci_class_init(ObjectClass *klass, void *data) +static void pvpanic_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pc = PCI_DEVICE_CLASS(klass); diff --git a/hw/misc/sbsa_ec.c b/hw/misc/sbsa_ec.c index a1e813691e..dfee1af5ad 100644 --- a/hw/misc/sbsa_ec.c +++ b/hw/misc/sbsa_ec.c @@ -73,7 +73,7 @@ static void sbsa_ec_init(Object *obj) sysbus_init_mmio(dev, &s->iomem); } -static void sbsa_ec_class_init(ObjectClass *klass, void *data) +static void sbsa_ec_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/sifive_e_aon.c b/hw/misc/sifive_e_aon.c index 17a522ccf9..6eef38d622 100644 --- a/hw/misc/sifive_e_aon.c +++ b/hw/misc/sifive_e_aon.c @@ -294,7 +294,7 @@ static const Property sifive_e_aon_properties[] = { SIFIVE_E_LFCLK_DEFAULT_FREQ), }; -static void sifive_e_aon_class_init(ObjectClass *oc, void *data) +static void sifive_e_aon_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/misc/sifive_u_otp.c b/hw/misc/sifive_u_otp.c index d6df867fbd..1ebed2fd8b 100644 --- a/hw/misc/sifive_u_otp.c +++ b/hw/misc/sifive_u_otp.c @@ -270,7 +270,7 @@ static void sifive_u_otp_realize(DeviceState *dev, Error **errp) memset(s->fuse_wo, 0x00, sizeof(s->fuse_wo)); } -static void sifive_u_otp_class_init(ObjectClass *klass, void *data) +static void sifive_u_otp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/sifive_u_prci.c b/hw/misc/sifive_u_prci.c index cafe6a66f4..6e75cb6d0d 100644 --- a/hw/misc/sifive_u_prci.c +++ b/hw/misc/sifive_u_prci.c @@ -146,7 +146,7 @@ static void sifive_u_prci_reset(DeviceState *dev) s->coreclksel = SIFIVE_U_PRCI_CORECLKSEL_HFCLK; } -static void sifive_u_prci_class_init(ObjectClass *klass, void *data) +static void sifive_u_prci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/slavio_misc.c b/hw/misc/slavio_misc.c index dace6d28cc..a034df3592 100644 --- a/hw/misc/slavio_misc.c +++ b/hw/misc/slavio_misc.c @@ -483,7 +483,7 @@ static void slavio_misc_init(Object *obj) qdev_init_gpio_in(dev, slavio_set_power_fail, 1); } -static void slavio_misc_class_init(ObjectClass *klass, void *data) +static void slavio_misc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/stm32_rcc.c b/hw/misc/stm32_rcc.c index 26672b5b24..94e8dae441 100644 --- a/hw/misc/stm32_rcc.c +++ b/hw/misc/stm32_rcc.c @@ -138,7 +138,7 @@ static const VMStateDescription vmstate_stm32_rcc = { } }; -static void stm32_rcc_class_init(ObjectClass *klass, void *data) +static void stm32_rcc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/stm32f2xx_syscfg.c b/hw/misc/stm32f2xx_syscfg.c index 6c7b722274..d285896ea7 100644 --- a/hw/misc/stm32f2xx_syscfg.c +++ b/hw/misc/stm32f2xx_syscfg.c @@ -138,7 +138,7 @@ static void stm32f2xx_syscfg_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } -static void stm32f2xx_syscfg_class_init(ObjectClass *klass, void *data) +static void stm32f2xx_syscfg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/stm32f4xx_exti.c b/hw/misc/stm32f4xx_exti.c index efd996df94..0688e6e73e 100644 --- a/hw/misc/stm32f4xx_exti.c +++ b/hw/misc/stm32f4xx_exti.c @@ -164,7 +164,7 @@ static const VMStateDescription vmstate_stm32f4xx_exti = { } }; -static void stm32f4xx_exti_class_init(ObjectClass *klass, void *data) +static void stm32f4xx_exti_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/stm32f4xx_syscfg.c b/hw/misc/stm32f4xx_syscfg.c index 7d0f3eb5f5..addfb031e8 100644 --- a/hw/misc/stm32f4xx_syscfg.c +++ b/hw/misc/stm32f4xx_syscfg.c @@ -147,7 +147,7 @@ static const VMStateDescription vmstate_stm32f4xx_syscfg = { } }; -static void stm32f4xx_syscfg_class_init(ObjectClass *klass, void *data) +static void stm32f4xx_syscfg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/stm32l4x5_exti.c b/hw/misc/stm32l4x5_exti.c index e281841dcf..9c002164c8 100644 --- a/hw/misc/stm32l4x5_exti.c +++ b/hw/misc/stm32l4x5_exti.c @@ -271,7 +271,7 @@ static const VMStateDescription vmstate_stm32l4x5_exti = { } }; -static void stm32l4x5_exti_class_init(ObjectClass *klass, void *data) +static void stm32l4x5_exti_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/stm32l4x5_rcc.c b/hw/misc/stm32l4x5_rcc.c index 158b743cae..0e1f27fbdd 100644 --- a/hw/misc/stm32l4x5_rcc.c +++ b/hw/misc/stm32l4x5_rcc.c @@ -141,7 +141,7 @@ static const VMStateDescription clock_mux_vmstate = { } }; -static void clock_mux_class_init(ObjectClass *klass, void *data) +static void clock_mux_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -295,7 +295,7 @@ static const VMStateDescription pll_vmstate = { } }; -static void pll_class_init(ObjectClass *klass, void *data) +static void pll_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -1439,7 +1439,7 @@ static const Property stm32l4x5_rcc_properties[] = { sai2_extclk_frequency, 0), }; -static void stm32l4x5_rcc_class_init(ObjectClass *klass, void *data) +static void stm32l4x5_rcc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/stm32l4x5_syscfg.c b/hw/misc/stm32l4x5_syscfg.c index a947a9e036..4e21756e0b 100644 --- a/hw/misc/stm32l4x5_syscfg.c +++ b/hw/misc/stm32l4x5_syscfg.c @@ -259,7 +259,7 @@ static const VMStateDescription vmstate_stm32l4x5_syscfg = { } }; -static void stm32l4x5_syscfg_class_init(ObjectClass *klass, void *data) +static void stm32l4x5_syscfg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/tz-mpc.c b/hw/misc/tz-mpc.c index 6d827d21dc..a158d4a294 100644 --- a/hw/misc/tz-mpc.c +++ b/hw/misc/tz-mpc.c @@ -592,7 +592,7 @@ static const Property tz_mpc_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), }; -static void tz_mpc_class_init(ObjectClass *klass, void *data) +static void tz_mpc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -611,7 +611,7 @@ static const TypeInfo tz_mpc_info = { }; static void tz_mpc_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/misc/tz-msc.c b/hw/misc/tz-msc.c index 505df4e190..af0cc5d471 100644 --- a/hw/misc/tz-msc.c +++ b/hw/misc/tz-msc.c @@ -285,7 +285,7 @@ static const Property tz_msc_properties[] = { TYPE_IDAU_INTERFACE, IDAUInterface *), }; -static void tz_msc_class_init(ObjectClass *klass, void *data) +static void tz_msc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/tz-ppc.c b/hw/misc/tz-ppc.c index 1daa54c5e6..e4235a846d 100644 --- a/hw/misc/tz-ppc.c +++ b/hw/misc/tz-ppc.c @@ -325,7 +325,7 @@ static const Property tz_ppc_properties[] = { DEFINE_PORT(15), }; -static void tz_ppc_class_init(ObjectClass *klass, void *data) +static void tz_ppc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/unimp.c b/hw/misc/unimp.c index 257282a3a9..4370c14ef1 100644 --- a/hw/misc/unimp.c +++ b/hw/misc/unimp.c @@ -75,7 +75,7 @@ static const Property unimp_properties[] = { DEFINE_PROP_STRING("name", UnimplementedDeviceState, name), }; -static void unimp_class_init(ObjectClass *klass, void *data) +static void unimp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/virt_ctrl.c b/hw/misc/virt_ctrl.c index a210a5924c..9f16093ca2 100644 --- a/hw/misc/virt_ctrl.c +++ b/hw/misc/virt_ctrl.c @@ -125,7 +125,7 @@ static void virt_ctrl_instance_init(Object *obj) sysbus_init_irq(dev, &s->irq); } -static void virt_ctrl_class_init(ObjectClass *oc, void *data) +static void virt_ctrl_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/misc/vmcoreinfo.c b/hw/misc/vmcoreinfo.c index b0145fa504..9c2e9005ad 100644 --- a/hw/misc/vmcoreinfo.c +++ b/hw/misc/vmcoreinfo.c @@ -83,7 +83,7 @@ static const VMStateDescription vmstate_vmcoreinfo = { }, }; -static void vmcoreinfo_device_class_init(ObjectClass *klass, void *data) +static void vmcoreinfo_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/xlnx-versal-cframe-reg.c b/hw/misc/xlnx-versal-cframe-reg.c index 8db0f7e658..e28d569ebe 100644 --- a/hw/misc/xlnx-versal-cframe-reg.c +++ b/hw/misc/xlnx-versal-cframe-reg.c @@ -803,7 +803,7 @@ static const Property cframe_bcast_regs_props[] = { TYPE_XLNX_CFI_IF, XlnxCfiIf *), }; -static void cframe_reg_class_init(ObjectClass *klass, void *data) +static void cframe_reg_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -817,7 +817,7 @@ static void cframe_reg_class_init(ObjectClass *klass, void *data) xcic->cfi_transfer_packet = cframe_reg_cfi_transfer_packet; } -static void cframe_bcast_reg_class_init(ObjectClass *klass, void *data) +static void cframe_bcast_reg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/xlnx-versal-cfu.c b/hw/misc/xlnx-versal-cfu.c index 26d06e2557..02e4fed05b 100644 --- a/hw/misc/xlnx-versal-cfu.c +++ b/hw/misc/xlnx-versal-cfu.c @@ -496,7 +496,7 @@ static const VMStateDescription vmstate_cfu_sfr = { } }; -static void cfu_apb_class_init(ObjectClass *klass, void *data) +static void cfu_apb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -505,7 +505,7 @@ static void cfu_apb_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, cfu_props); } -static void cfu_fdro_class_init(ObjectClass *klass, void *data) +static void cfu_fdro_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -516,7 +516,7 @@ static void cfu_fdro_class_init(ObjectClass *klass, void *data) rc->phases.enter = cfu_fdro_reset_enter; } -static void cfu_sfr_class_init(ObjectClass *klass, void *data) +static void cfu_sfr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/xlnx-versal-crl.c b/hw/misc/xlnx-versal-crl.c index f143900d5b..08ff2fcc24 100644 --- a/hw/misc/xlnx-versal-crl.c +++ b/hw/misc/xlnx-versal-crl.c @@ -394,7 +394,7 @@ static const VMStateDescription vmstate_crl = { } }; -static void crl_class_init(ObjectClass *klass, void *data) +static void crl_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/xlnx-versal-pmc-iou-slcr.c b/hw/misc/xlnx-versal-pmc-iou-slcr.c index e469c04d76..d76df468d4 100644 --- a/hw/misc/xlnx-versal-pmc-iou-slcr.c +++ b/hw/misc/xlnx-versal-pmc-iou-slcr.c @@ -1419,7 +1419,8 @@ static const VMStateDescription vmstate_pmc_iou_slcr = { } }; -static void xlnx_versal_pmc_iou_slcr_class_init(ObjectClass *klass, void *data) +static void xlnx_versal_pmc_iou_slcr_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/xlnx-versal-trng.c b/hw/misc/xlnx-versal-trng.c index ba93f93cab..f34dd3ef35 100644 --- a/hw/misc/xlnx-versal-trng.c +++ b/hw/misc/xlnx-versal-trng.c @@ -682,7 +682,7 @@ static const VMStateDescription vmstate_trng = { } }; -static void trng_class_init(ObjectClass *klass, void *data) +static void trng_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/misc/xlnx-versal-xramc.c b/hw/misc/xlnx-versal-xramc.c index d1e76be027..07370b80c0 100644 --- a/hw/misc/xlnx-versal-xramc.c +++ b/hw/misc/xlnx-versal-xramc.c @@ -222,7 +222,7 @@ static const Property xram_ctrl_properties[] = { DEFINE_PROP_UINT64("size", XlnxXramCtrl, cfg.size, 1 * MiB), }; -static void xram_ctrl_class_init(ObjectClass *klass, void *data) +static void xram_ctrl_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/xlnx-zynqmp-apu-ctrl.c b/hw/misc/xlnx-zynqmp-apu-ctrl.c index 87e4a14067..e85da32d99 100644 --- a/hw/misc/xlnx-zynqmp-apu-ctrl.c +++ b/hw/misc/xlnx-zynqmp-apu-ctrl.c @@ -224,7 +224,7 @@ static const VMStateDescription vmstate_zynqmp_apu = { } }; -static void zynqmp_apu_class_init(ObjectClass *klass, void *data) +static void zynqmp_apu_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/xlnx-zynqmp-crf.c b/hw/misc/xlnx-zynqmp-crf.c index e5aba56f69..cccca0e814 100644 --- a/hw/misc/xlnx-zynqmp-crf.c +++ b/hw/misc/xlnx-zynqmp-crf.c @@ -239,7 +239,7 @@ static const VMStateDescription vmstate_crf = { } }; -static void crf_class_init(ObjectClass *klass, void *data) +static void crf_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/misc/zynq_slcr.c b/hw/misc/zynq_slcr.c index a766bab182..010387beec 100644 --- a/hw/misc/zynq_slcr.c +++ b/hw/misc/zynq_slcr.c @@ -627,7 +627,7 @@ static const Property zynq_slcr_props[] = { DEFINE_PROP_UINT8("boot-mode", ZynqSLCRState, boot_mode, 1), }; -static void zynq_slcr_class_init(ObjectClass *klass, void *data) +static void zynq_slcr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/net/allwinner-sun8i-emac.c b/hw/net/allwinner-sun8i-emac.c index 5adb41dc46..30a81576b4 100644 --- a/hw/net/allwinner-sun8i-emac.c +++ b/hw/net/allwinner-sun8i-emac.c @@ -875,7 +875,8 @@ static const VMStateDescription vmstate_aw_emac = { } }; -static void allwinner_sun8i_emac_class_init(ObjectClass *klass, void *data) +static void allwinner_sun8i_emac_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/allwinner_emac.c b/hw/net/allwinner_emac.c index 47f1e7f086..77d089d988 100644 --- a/hw/net/allwinner_emac.c +++ b/hw/net/allwinner_emac.c @@ -514,7 +514,7 @@ static const VMStateDescription vmstate_aw_emac = { } }; -static void aw_emac_class_init(ObjectClass *klass, void *data) +static void aw_emac_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 80fbbacc1e..50025d5a6f 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1817,7 +1817,7 @@ static const Property gem_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), }; -static void gem_class_init(ObjectClass *klass, void *data) +static void gem_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/can/can_kvaser_pci.c b/hw/net/can/can_kvaser_pci.c index 9e363d532f..c0bb598bae 100644 --- a/hw/net/can/can_kvaser_pci.c +++ b/hw/net/can/can_kvaser_pci.c @@ -282,7 +282,7 @@ static void kvaser_pci_instance_init(Object *obj) 0); } -static void kvaser_pci_class_init(ObjectClass *klass, void *data) +static void kvaser_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/net/can/can_mioe3680_pci.c b/hw/net/can/can_mioe3680_pci.c index 580f099e00..9aac70dccd 100644 --- a/hw/net/can/can_mioe3680_pci.c +++ b/hw/net/can/can_mioe3680_pci.c @@ -223,7 +223,7 @@ static void mioe3680_pci_instance_init(Object *obj) 0); } -static void mioe3680_pci_class_init(ObjectClass *klass, void *data) +static void mioe3680_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/net/can/can_pcm3680_pci.c b/hw/net/can/can_pcm3680_pci.c index 3195b79954..b305f7e9b7 100644 --- a/hw/net/can/can_pcm3680_pci.c +++ b/hw/net/can/can_pcm3680_pci.c @@ -224,7 +224,7 @@ static void pcm3680i_pci_instance_init(Object *obj) 0); } -static void pcm3680i_pci_class_init(ObjectClass *klass, void *data) +static void pcm3680i_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/net/can/ctucan_pci.c b/hw/net/can/ctucan_pci.c index a8c77b9194..0dee9b59d1 100644 --- a/hw/net/can/ctucan_pci.c +++ b/hw/net/can/ctucan_pci.c @@ -237,7 +237,7 @@ static void ctucan_pci_instance_init(Object *obj) #endif } -static void ctucan_pci_class_init(ObjectClass *klass, void *data) +static void ctucan_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c index b5a4a4af7e..79ccc10098 100644 --- a/hw/net/can/xlnx-versal-canfd.c +++ b/hw/net/can/xlnx-versal-canfd.c @@ -2052,7 +2052,7 @@ static const Property canfd_core_properties[] = { CanBusState *), }; -static void canfd_class_init(ObjectClass *klass, void *data) +static void canfd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/can/xlnx-zynqmp-can.c b/hw/net/can/xlnx-zynqmp-can.c index 9fbdeea368..ca9edd4a5b 100644 --- a/hw/net/can/xlnx-zynqmp-can.c +++ b/hw/net/can/xlnx-zynqmp-can.c @@ -1176,7 +1176,7 @@ static const Property xlnx_zynqmp_can_properties[] = { CanBusState *), }; -static void xlnx_zynqmp_can_class_init(ObjectClass *klass, void *data) +static void xlnx_zynqmp_can_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/net/dp8393x.c b/hw/net/dp8393x.c index c80ddb12e3..d49032059b 100644 --- a/hw/net/dp8393x.c +++ b/hw/net/dp8393x.c @@ -939,7 +939,7 @@ static const Property dp8393x_properties[] = { DEFINE_PROP_BOOL("big_endian", dp8393xState, big_endian, false), }; -static void dp8393x_class_init(ObjectClass *klass, void *data) +static void dp8393x_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 3d0b227703..d49730f4ad 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -1694,7 +1694,7 @@ typedef struct E1000Info { uint16_t phy_id2; } E1000Info; -static void e1000_class_init(ObjectClass *klass, void *data) +static void e1000_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index b72cbab7e8..f38249a6a9 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -673,7 +673,7 @@ static const Property e1000e_properties[] = { DEFINE_PROP_BOOL("migrate-timadj", E1000EState, timadj, true), }; -static void e1000e_class_init(ObjectClass *class, void *data) +static void e1000e_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); ResettableClass *rc = RESETTABLE_CLASS(class); diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index 29a39865a6..ef0f9337a0 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -2060,7 +2060,7 @@ static const Property e100_properties[] = { DEFINE_NIC_PROPERTIES(EEPRO100State, conf), }; -static void eepro100_class_init(ObjectClass *klass, void *data) +static void eepro100_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index adde644892..d14cb2a101 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -418,7 +418,7 @@ static const Property etsec_properties[] = { DEFINE_NIC_PROPERTIES(eTSEC, conf), }; -static void etsec_class_init(ObjectClass *klass, void *data) +static void etsec_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/ftgmac100.c b/hw/net/ftgmac100.c index 1f524d7a01..c41ce889cf 100644 --- a/hw/net/ftgmac100.c +++ b/hw/net/ftgmac100.c @@ -1260,7 +1260,7 @@ static const Property ftgmac100_properties[] = { DEFINE_PROP_BOOL("dma64", FTGMAC100State, dma64, false), }; -static void ftgmac100_class_init(ObjectClass *klass, void *data) +static void ftgmac100_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1419,7 +1419,7 @@ static const Property aspeed_mii_properties[] = { FTGMAC100State *), }; -static void aspeed_mii_class_init(ObjectClass *klass, void *data) +static void aspeed_mii_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/igb.c b/hw/net/igb.c index e318df40e0..ba30433a50 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -599,7 +599,7 @@ static const Property igb_properties[] = { DEFINE_PROP_BOOL("x-pcie-flr-init", IGBState, has_flr, true), }; -static void igb_class_init(ObjectClass *class, void *data) +static void igb_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); ResettableClass *rc = RESETTABLE_CLASS(class); diff --git a/hw/net/igbvf.c b/hw/net/igbvf.c index 21a97d4d61..91e7ccf931 100644 --- a/hw/net/igbvf.c +++ b/hw/net/igbvf.c @@ -299,7 +299,7 @@ static void igbvf_pci_uninit(PCIDevice *dev) msix_uninit(dev, &s->msix, &s->msix); } -static void igbvf_class_init(ObjectClass *class, void *data) +static void igbvf_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *c = PCI_DEVICE_CLASS(class); diff --git a/hw/net/imx_fec.c b/hw/net/imx_fec.c index b7c9ee0b9a..e5e34dd1a4 100644 --- a/hw/net/imx_fec.c +++ b/hw/net/imx_fec.c @@ -1230,7 +1230,7 @@ static const Property imx_eth_properties[] = { IMXFECState *), }; -static void imx_eth_class_init(ObjectClass *klass, void *data) +static void imx_eth_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index afee68c7db..6dda1e5c94 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -1309,7 +1309,7 @@ static const Property lan9118_properties[] = { DEFINE_PROP_UINT32("mode_16bit", lan9118_state, mode_16bit, 0), }; -static void lan9118_class_init(ObjectClass *klass, void *data) +static void lan9118_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/lan9118_phy.c b/hw/net/lan9118_phy.c index 5c53a4a1e3..4c4e03df11 100644 --- a/hw/net/lan9118_phy.c +++ b/hw/net/lan9118_phy.c @@ -200,7 +200,7 @@ static const VMStateDescription vmstate_lan9118_phy = { } }; -static void lan9118_phy_class_init(ObjectClass *klass, void *data) +static void lan9118_phy_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/lance.c b/hw/net/lance.c index 15492382f9..dfb855c23a 100644 --- a/hw/net/lance.c +++ b/hw/net/lance.c @@ -143,7 +143,7 @@ static const Property lance_properties[] = { DEFINE_NIC_PROPERTIES(SysBusPCNetState, state.conf), }; -static void lance_class_init(ObjectClass *klass, void *data) +static void lance_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/lasi_i82596.c b/hw/net/lasi_i82596.c index cad01f5351..9e1dd21546 100644 --- a/hw/net/lasi_i82596.c +++ b/hw/net/lasi_i82596.c @@ -162,7 +162,7 @@ static const Property lasi_82596_properties[] = { DEFINE_NIC_PROPERTIES(SysBusI82596State, state.conf), }; -static void lasi_82596_class_init(ObjectClass *klass, void *data) +static void lasi_82596_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/mcf_fec.c b/hw/net/mcf_fec.c index d5572a81d3..ae128fa311 100644 --- a/hw/net/mcf_fec.c +++ b/hw/net/mcf_fec.c @@ -664,7 +664,7 @@ static const Property mcf_fec_properties[] = { DEFINE_NIC_PROPERTIES(mcf_fec_state, conf), }; -static void mcf_fec_class_init(ObjectClass *oc, void *data) +static void mcf_fec_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/net/mipsnet.c b/hw/net/mipsnet.c index 8852b6f3a1..583aa1c7de 100644 --- a/hw/net/mipsnet.c +++ b/hw/net/mipsnet.c @@ -270,7 +270,7 @@ static const Property mipsnet_properties[] = { DEFINE_NIC_PROPERTIES(MIPSnetState, conf), }; -static void mipsnet_class_init(ObjectClass *klass, void *data) +static void mipsnet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/msf2-emac.c b/hw/net/msf2-emac.c index 80f75f19dd..59045973ab 100644 --- a/hw/net/msf2-emac.c +++ b/hw/net/msf2-emac.c @@ -565,7 +565,7 @@ static const VMStateDescription vmstate_msf2_emac = { } }; -static void msf2_emac_class_init(ObjectClass *klass, void *data) +static void msf2_emac_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/mv88w8618_eth.c b/hw/net/mv88w8618_eth.c index 5a9d14bef6..6f08846c81 100644 --- a/hw/net/mv88w8618_eth.c +++ b/hw/net/mv88w8618_eth.c @@ -377,7 +377,7 @@ static const Property mv88w8618_eth_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), }; -static void mv88w8618_eth_class_init(ObjectClass *klass, void *data) +static void mv88w8618_eth_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/ne2000-isa.c b/hw/net/ne2000-isa.c index 20973651f3..673c785abc 100644 --- a/hw/net/ne2000-isa.c +++ b/hw/net/ne2000-isa.c @@ -85,7 +85,7 @@ static const Property ne2000_isa_properties[] = { DEFINE_NIC_PROPERTIES(ISANE2000State, ne2000.c), }; -static void isa_ne2000_class_initfn(ObjectClass *klass, void *data) +static void isa_ne2000_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/ne2000-pci.c b/hw/net/ne2000-pci.c index 6840d0e720..2153973af4 100644 --- a/hw/net/ne2000-pci.c +++ b/hw/net/ne2000-pci.c @@ -100,7 +100,7 @@ static const Property ne2000_properties[] = { DEFINE_NIC_PROPERTIES(PCINE2000State, ne2000.c), }; -static void ne2000_class_init(ObjectClass *klass, void *data) +static void ne2000_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/net/npcm7xx_emc.c b/hw/net/npcm7xx_emc.c index e06f652629..9ba35e2c81 100644 --- a/hw/net/npcm7xx_emc.c +++ b/hw/net/npcm7xx_emc.c @@ -849,7 +849,7 @@ static const Property npcm7xx_emc_properties[] = { DEFINE_NIC_PROPERTIES(NPCM7xxEMCState, conf), }; -static void npcm7xx_emc_class_init(ObjectClass *klass, void *data) +static void npcm7xx_emc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index e1fb383772..a434112580 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -916,7 +916,7 @@ static const Property npcm_gmac_properties[] = { DEFINE_NIC_PROPERTIES(NPCMGMACState, conf), }; -static void npcm_gmac_class_init(ObjectClass *klass, void *data) +static void npcm_gmac_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/npcm_pcs.c b/hw/net/npcm_pcs.c index ce5034e234..6aec105271 100644 --- a/hw/net/npcm_pcs.c +++ b/hw/net/npcm_pcs.c @@ -387,7 +387,7 @@ static const VMStateDescription vmstate_npcm_pcs = { }, }; -static void npcm_pcs_class_init(ObjectClass *klass, void *data) +static void npcm_pcs_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/opencores_eth.c b/hw/net/opencores_eth.c index 54daab7b04..7e955c0132 100644 --- a/hw/net/opencores_eth.c +++ b/hw/net/opencores_eth.c @@ -747,7 +747,7 @@ static const Property open_eth_properties[] = { DEFINE_NIC_PROPERTIES(OpenEthState, conf), }; -static void open_eth_class_init(ObjectClass *klass, void *data) +static void open_eth_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index b314ea7d6d..429c217180 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -256,7 +256,7 @@ static const Property pcnet_properties[] = { DEFINE_NIC_PROPERTIES(PCIPCNetState, state.conf), }; -static void pcnet_class_init(ObjectClass *klass, void *data) +static void pcnet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index aa5d87fbc5..3d307f4ab1 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -1475,7 +1475,7 @@ static const VMStateDescription rocker_vmsd = { .unmigratable = 1, }; -static void rocker_class_init(ObjectClass *klass, void *data) +static void rocker_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 135ab57160..ad812954cf 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -3414,7 +3414,7 @@ static const Property rtl8139_properties[] = { DEFINE_NIC_PROPERTIES(RTL8139State, conf), }; -static void rtl8139_class_init(ObjectClass *klass, void *data) +static void rtl8139_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/net/smc91c111.c b/hw/net/smc91c111.c index 9ce42b5615..5cd78e334b 100644 --- a/hw/net/smc91c111.c +++ b/hw/net/smc91c111.c @@ -913,7 +913,7 @@ static const Property smc91c111_properties[] = { DEFINE_NIC_PROPERTIES(smc91c111_state, conf), }; -static void smc91c111_class_init(ObjectClass *klass, void *data) +static void smc91c111_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/spapr_llan.c b/hw/net/spapr_llan.c index 13fc6565f0..f6f217d632 100644 --- a/hw/net/spapr_llan.c +++ b/hw/net/spapr_llan.c @@ -848,7 +848,7 @@ static const VMStateDescription vmstate_spapr_llan = { } }; -static void spapr_vlan_class_init(ObjectClass *klass, void *data) +static void spapr_vlan_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); diff --git a/hw/net/stellaris_enet.c b/hw/net/stellaris_enet.c index a420732d48..2fc51e1e16 100644 --- a/hw/net/stellaris_enet.c +++ b/hw/net/stellaris_enet.c @@ -501,7 +501,7 @@ static const Property stellaris_enet_properties[] = { DEFINE_NIC_PROPERTIES(stellaris_enet_state, conf), }; -static void stellaris_enet_class_init(ObjectClass *klass, void *data) +static void stellaris_enet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/sungem.c b/hw/net/sungem.c index 12a9a9df46..123d08ee8e 100644 --- a/hw/net/sungem.c +++ b/hw/net/sungem.c @@ -1454,7 +1454,7 @@ static const VMStateDescription vmstate_sungem = { } }; -static void sungem_class_init(ObjectClass *klass, void *data) +static void sungem_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c index fa234d0da1..46c9f5020b 100644 --- a/hw/net/sunhme.c +++ b/hw/net/sunhme.c @@ -937,7 +937,7 @@ static const VMStateDescription vmstate_hme = { } }; -static void sunhme_class_init(ObjectClass *klass, void *data) +static void sunhme_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/net/tulip.c b/hw/net/tulip.c index a0646bb84c..fb3366d8ee 100644 --- a/hw/net/tulip.c +++ b/hw/net/tulip.c @@ -1011,7 +1011,7 @@ static const Property tulip_properties[] = { DEFINE_NIC_PROPERTIES(TULIPState, c), }; -static void tulip_class_init(ObjectClass *klass, void *data) +static void tulip_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index bd37651dab..2de037c273 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -4134,7 +4134,7 @@ static const Property virtio_net_properties[] = { VIRTIO_NET_F_HOST_USO, true), }; -static void virtio_net_class_init(ObjectClass *klass, void *data) +static void virtio_net_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index f370d4a2ec..4bcf1f902f 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -2491,7 +2491,7 @@ static void vmxnet3_realize(DeviceState *qdev, Error **errp) vc->parent_dc_realize(qdev, errp); } -static void vmxnet3_class_init(ObjectClass *class, void *data) +static void vmxnet3_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *c = PCI_DEVICE_CLASS(class); diff --git a/hw/net/xen_nic.c b/hw/net/xen_nic.c index c48691207d..34c6a1d0b0 100644 --- a/hw/net/xen_nic.c +++ b/hw/net/xen_nic.c @@ -559,7 +559,7 @@ static const Property xen_netdev_properties[] = { DEFINE_PROP_INT32("idx", XenNetDev, dev, -1), }; -static void xen_netdev_class_init(ObjectClass *class, void *data) +static void xen_netdev_class_init(ObjectClass *class, const void *data) { DeviceClass *dev_class = DEVICE_CLASS(class); XenDeviceClass *xendev_class = XEN_DEVICE_CLASS(class); diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index e3cc4c60eb..9c87c4e70f 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -418,7 +418,7 @@ static const Property xgmac_properties[] = { DEFINE_NIC_PROPERTIES(XgmacState, conf), }; -static void xgmac_enet_class_init(ObjectClass *klass, void *data) +static void xgmac_enet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index 457952af19..e45bc048e0 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -1007,7 +1007,7 @@ static const Property xilinx_enet_properties[] = { tx_control_dev, TYPE_STREAM_SINK, StreamSink *), }; -static void xilinx_enet_class_init(ObjectClass *klass, void *data) +static void xilinx_enet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1017,14 +1017,15 @@ static void xilinx_enet_class_init(ObjectClass *klass, void *data) } static void xilinx_enet_control_stream_class_init(ObjectClass *klass, - void *data) + const void *data) { StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); ssc->push = xilinx_axienet_control_stream_push; } -static void xilinx_enet_data_stream_class_init(ObjectClass *klass, void *data) +static void xilinx_enet_data_stream_class_init(ObjectClass *klass, + const void *data) { StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); diff --git a/hw/net/xilinx_ethlite.c b/hw/net/xilinx_ethlite.c index 15d9b95aa8..42b19d07c7 100644 --- a/hw/net/xilinx_ethlite.c +++ b/hw/net/xilinx_ethlite.c @@ -385,7 +385,7 @@ static const Property xilinx_ethlite_properties[] = { DEFINE_NIC_PROPERTIES(XlnxXpsEthLite, conf), }; -static void xilinx_ethlite_class_init(ObjectClass *klass, void *data) +static void xilinx_ethlite_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nubus/mac-nubus-bridge.c b/hw/nubus/mac-nubus-bridge.c index a0da5a8b2f..0dac8d19b3 100644 --- a/hw/nubus/mac-nubus-bridge.c +++ b/hw/nubus/mac-nubus-bridge.c @@ -40,7 +40,7 @@ static void mac_nubus_bridge_init(Object *obj) sysbus_init_mmio(sbd, &s->slot_alias); } -static void mac_nubus_bridge_class_init(ObjectClass *klass, void *data) +static void mac_nubus_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nubus/nubus-bridge.c b/hw/nubus/nubus-bridge.c index 8fe4362723..fb14402c4f 100644 --- a/hw/nubus/nubus-bridge.c +++ b/hw/nubus/nubus-bridge.c @@ -28,7 +28,7 @@ static const Property nubus_bridge_properties[] = { bus.slot_available_mask, 0xffff), }; -static void nubus_bridge_class_init(ObjectClass *klass, void *data) +static void nubus_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nubus/nubus-bus.c b/hw/nubus/nubus-bus.c index 07c279bde5..44820f13a8 100644 --- a/hw/nubus/nubus-bus.c +++ b/hw/nubus/nubus-bus.c @@ -162,7 +162,7 @@ static bool nubus_check_address(BusState *bus, DeviceState *dev, Error **errp) return true; } -static void nubus_class_init(ObjectClass *oc, void *data) +static void nubus_class_init(ObjectClass *oc, const void *data) { BusClass *bc = BUS_CLASS(oc); diff --git a/hw/nubus/nubus-device.c b/hw/nubus/nubus-device.c index 6755c3dc43..7797e61c7f 100644 --- a/hw/nubus/nubus-device.c +++ b/hw/nubus/nubus-device.c @@ -112,7 +112,7 @@ static const Property nubus_device_properties[] = { DEFINE_PROP_STRING("romfile", NubusDevice, romfile), }; -static void nubus_device_class_init(ObjectClass *oc, void *data) +static void nubus_device_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/nubus/nubus-virtio-mmio.c b/hw/nubus/nubus-virtio-mmio.c index 7a98731c45..63aeca5b12 100644 --- a/hw/nubus/nubus-virtio-mmio.c +++ b/hw/nubus/nubus-virtio-mmio.c @@ -81,7 +81,7 @@ static void nubus_virtio_mmio_init(Object *obj) "pic-input-irq", 1); } -static void nubus_virtio_mmio_class_init(ObjectClass *oc, void *data) +static void nubus_virtio_mmio_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); NubusVirtioMMIODeviceClass *nvmdc = NUBUS_VIRTIO_MMIO_CLASS(oc); diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index d6b77d4fbc..e87295f5f8 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -9183,7 +9183,7 @@ static const VMStateDescription nvme_vmstate = { .unmigratable = 1, }; -static void nvme_class_init(ObjectClass *oc, void *data) +static void nvme_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); diff --git a/hw/nvme/ns.c b/hw/nvme/ns.c index 4ab8ba74f5..6df2e8e7c5 100644 --- a/hw/nvme/ns.c +++ b/hw/nvme/ns.c @@ -806,7 +806,7 @@ static const Property nvme_ns_props[] = { DEFINE_PROP_STRING("fdp.ruhs", NvmeNamespace, params.fdp.ruhs), }; -static void nvme_ns_class_init(ObjectClass *oc, void *data) +static void nvme_ns_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index b617ac3892..38271d78c8 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -218,7 +218,7 @@ static const Property nvme_subsystem_props[] = { DEFINE_PROP_UINT16("fdp.nruh", NvmeSubsystem, params.fdp.nruh, 0), }; -static void nvme_subsys_class_init(ObjectClass *oc, void *data) +static void nvme_subsys_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/nvram/bcm2835_otp.c b/hw/nvram/bcm2835_otp.c index c4aed28472..6816b53417 100644 --- a/hw/nvram/bcm2835_otp.c +++ b/hw/nvram/bcm2835_otp.c @@ -164,7 +164,7 @@ static const VMStateDescription vmstate_bcm2835_otp = { } }; -static void bcm2835_otp_class_init(ObjectClass *klass, void *data) +static void bcm2835_otp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nvram/ds1225y.c b/hw/nvram/ds1225y.c index 6b2aa8c7d2..dbfd0d2e53 100644 --- a/hw/nvram/ds1225y.c +++ b/hw/nvram/ds1225y.c @@ -147,7 +147,7 @@ static const Property nvram_sysbus_properties[] = { DEFINE_PROP_STRING("filename", SysBusNvRamState, nvram.filename), }; -static void nvram_sysbus_class_init(ObjectClass *klass, void *data) +static void nvram_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nvram/eeprom_at24c.c b/hw/nvram/eeprom_at24c.c index ff7a21eee7..82ea97e552 100644 --- a/hw/nvram/eeprom_at24c.c +++ b/hw/nvram/eeprom_at24c.c @@ -235,7 +235,7 @@ static const Property at24c_eeprom_props[] = { }; static -void at24c_eeprom_class_init(ObjectClass *klass, void *data) +void at24c_eeprom_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index cbfb2b5303..237b9f7d1f 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -1228,7 +1228,7 @@ void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key, fw_cfg_add_bytes(fw_cfg, data_key, data, size); } -static void fw_cfg_class_init(ObjectClass *klass, void *data) +static void fw_cfg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1303,7 +1303,7 @@ static void fw_cfg_io_realize(DeviceState *dev, Error **errp) fw_cfg_common_realize(dev, errp); } -static void fw_cfg_io_class_init(ObjectClass *klass, void *data) +static void fw_cfg_io_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1364,7 +1364,7 @@ static void fw_cfg_mem_realize(DeviceState *dev, Error **errp) fw_cfg_common_realize(dev, errp); } -static void fw_cfg_mem_class_init(ObjectClass *klass, void *data) +static void fw_cfg_mem_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nvram/mac_nvram.c b/hw/nvram/mac_nvram.c index 0d82e5a128..66526a2291 100644 --- a/hw/nvram/mac_nvram.c +++ b/hw/nvram/mac_nvram.c @@ -140,7 +140,7 @@ static const Property macio_nvram_properties[] = { DEFINE_PROP_DRIVE("drive", MacIONVRAMState, blk), }; -static void macio_nvram_class_init(ObjectClass *oc, void *data) +static void macio_nvram_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/nvram/npcm7xx_otp.c b/hw/nvram/npcm7xx_otp.c index f00ebfa931..1fb752b20f 100644 --- a/hw/nvram/npcm7xx_otp.c +++ b/hw/nvram/npcm7xx_otp.c @@ -391,7 +391,7 @@ static const VMStateDescription vmstate_npcm7xx_otp = { }, }; -static void npcm7xx_otp_class_init(ObjectClass *klass, void *data) +static void npcm7xx_otp_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -403,14 +403,14 @@ static void npcm7xx_otp_class_init(ObjectClass *klass, void *data) rc->phases.enter = npcm7xx_otp_enter_reset; } -static void npcm7xx_key_storage_class_init(ObjectClass *klass, void *data) +static void npcm7xx_key_storage_class_init(ObjectClass *klass, const void *data) { NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass); oc->mmio_ops = &npcm7xx_key_storage_ops; } -static void npcm7xx_fuse_array_class_init(ObjectClass *klass, void *data) +static void npcm7xx_fuse_array_class_init(ObjectClass *klass, const void *data) { NPCM7xxOTPClass *oc = NPCM7XX_OTP_CLASS(klass); diff --git a/hw/nvram/nrf51_nvm.c b/hw/nvram/nrf51_nvm.c index 2ed4078858..23cc9fe9b3 100644 --- a/hw/nvram/nrf51_nvm.c +++ b/hw/nvram/nrf51_nvm.c @@ -370,7 +370,7 @@ static const VMStateDescription vmstate_nvm = { } }; -static void nrf51_nvm_class_init(ObjectClass *klass, void *data) +static void nrf51_nvm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nvram/spapr_nvram.c b/hw/nvram/spapr_nvram.c index a45827f6aa..d0ac4e5735 100644 --- a/hw/nvram/spapr_nvram.c +++ b/hw/nvram/spapr_nvram.c @@ -257,7 +257,7 @@ static const Property spapr_nvram_properties[] = { DEFINE_PROP_DRIVE("drive", SpaprNvram, blk), }; -static void spapr_nvram_class_init(ObjectClass *klass, void *data) +static void spapr_nvram_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); diff --git a/hw/nvram/xlnx-bbram.c b/hw/nvram/xlnx-bbram.c index 14cc9073c7..5702bb3f31 100644 --- a/hw/nvram/xlnx-bbram.c +++ b/hw/nvram/xlnx-bbram.c @@ -525,7 +525,7 @@ static const Property bbram_ctrl_props[] = { DEFINE_PROP_UINT32("crc-zpads", XlnxBBRam, crc_zpads, 1), }; -static void bbram_ctrl_class_init(ObjectClass *klass, void *data) +static void bbram_ctrl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/nvram/xlnx-efuse.c b/hw/nvram/xlnx-efuse.c index 176e88fcd1..4c23f8b931 100644 --- a/hw/nvram/xlnx-efuse.c +++ b/hw/nvram/xlnx-efuse.c @@ -274,7 +274,7 @@ static const Property efuse_properties[] = { qdev_prop_uint32, uint32_t), }; -static void efuse_class_init(ObjectClass *klass, void *data) +static void efuse_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nvram/xlnx-versal-efuse-cache.c b/hw/nvram/xlnx-versal-efuse-cache.c index 2fb599422c..d4ec96a626 100644 --- a/hw/nvram/xlnx-versal-efuse-cache.c +++ b/hw/nvram/xlnx-versal-efuse-cache.c @@ -89,7 +89,7 @@ static const Property efuse_cache_props[] = { TYPE_XLNX_EFUSE, XlnxEFuse *), }; -static void efuse_cache_class_init(ObjectClass *klass, void *data) +static void efuse_cache_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/nvram/xlnx-versal-efuse-ctrl.c b/hw/nvram/xlnx-versal-efuse-ctrl.c index ff4d544ad6..9096219800 100644 --- a/hw/nvram/xlnx-versal-efuse-ctrl.c +++ b/hw/nvram/xlnx-versal-efuse-ctrl.c @@ -751,7 +751,7 @@ static const Property efuse_ctrl_props[] = { extra_pg0_lock_spec, qdev_prop_uint16, uint16_t), }; -static void efuse_ctrl_class_init(ObjectClass *klass, void *data) +static void efuse_ctrl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/nvram/xlnx-zynqmp-efuse.c b/hw/nvram/xlnx-zynqmp-efuse.c index 15024daf4f..5a218c32e8 100644 --- a/hw/nvram/xlnx-zynqmp-efuse.c +++ b/hw/nvram/xlnx-zynqmp-efuse.c @@ -839,7 +839,7 @@ static const Property zynqmp_efuse_props[] = { TYPE_XLNX_EFUSE, XlnxEFuse *), }; -static void zynqmp_efuse_class_init(ObjectClass *klass, void *data) +static void zynqmp_efuse_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/openrisc/openrisc_sim.c b/hw/openrisc/openrisc_sim.c index c2284a7d41..880c8ebbb8 100644 --- a/hw/openrisc/openrisc_sim.c +++ b/hw/openrisc/openrisc_sim.c @@ -357,7 +357,7 @@ static void openrisc_sim_init(MachineState *machine) } } -static void openrisc_sim_machine_init(ObjectClass *oc, void *data) +static void openrisc_sim_machine_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/openrisc/virt.c b/hw/openrisc/virt.c index 0d1c1f103c..a98071c936 100644 --- a/hw/openrisc/virt.c +++ b/hw/openrisc/virt.c @@ -543,7 +543,7 @@ static void openrisc_virt_init(MachineState *machine) } } -static void openrisc_virt_machine_init(ObjectClass *oc, void *data) +static void openrisc_virt_machine_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/pci-bridge/cxl_downstream.c b/hw/pci-bridge/cxl_downstream.c index e337f1ac50..ab3b550a88 100644 --- a/hw/pci-bridge/cxl_downstream.c +++ b/hw/pci-bridge/cxl_downstream.c @@ -219,7 +219,7 @@ static const Property cxl_dsp_props[] = { width, PCIE_LINK_WIDTH_16), }; -static void cxl_dsp_class_init(ObjectClass *oc, void *data) +static void cxl_dsp_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); diff --git a/hw/pci-bridge/cxl_root_port.c b/hw/pci-bridge/cxl_root_port.c index c0037f2cfb..8b1e149e9b 100644 --- a/hw/pci-bridge/cxl_root_port.c +++ b/hw/pci-bridge/cxl_root_port.c @@ -262,7 +262,7 @@ static void cxl_rp_write_config(PCIDevice *d, uint32_t address, uint32_t val, cxl_rp_dvsec_write_config(d, address, val, len); } -static void cxl_root_port_class_init(ObjectClass *oc, void *data) +static void cxl_root_port_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c index 28b109c49a..822a828555 100644 --- a/hw/pci-bridge/cxl_upstream.c +++ b/hw/pci-bridge/cxl_upstream.c @@ -371,7 +371,7 @@ static const Property cxl_upstream_props[] = { width, PCIE_LINK_WIDTH_16), }; -static void cxl_upstream_class_init(ObjectClass *oc, void *data) +static void cxl_upstream_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *k = PCI_DEVICE_CLASS(oc); diff --git a/hw/pci-bridge/gen_pcie_root_port.c b/hw/pci-bridge/gen_pcie_root_port.c index 3c0b41ef1a..d9078e783b 100644 --- a/hw/pci-bridge/gen_pcie_root_port.c +++ b/hw/pci-bridge/gen_pcie_root_port.c @@ -147,7 +147,7 @@ static const Property gen_rp_props[] = { width, PCIE_LINK_WIDTH_32), }; -static void gen_rp_dev_class_init(ObjectClass *klass, void *data) +static void gen_rp_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c index 00d2fbd7cf..f2b294aee2 100644 --- a/hw/pci-bridge/i82801b11.c +++ b/hw/pci-bridge/i82801b11.c @@ -87,7 +87,7 @@ static const VMStateDescription i82801b11_bridge_dev_vmstate = { } }; -static void i82801b11_bridge_class_init(ObjectClass *klass, void *data) +static void i82801b11_bridge_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-bridge/ioh3420.c b/hw/pci-bridge/ioh3420.c index be752a4bda..bba640f495 100644 --- a/hw/pci-bridge/ioh3420.c +++ b/hw/pci-bridge/ioh3420.c @@ -96,7 +96,7 @@ static const VMStateDescription vmstate_ioh3420 = { } }; -static void ioh3420_class_init(ObjectClass *klass, void *data) +static void ioh3420_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 4931ea24f6..3b57583199 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -240,7 +240,7 @@ void pci_bridge_dev_unplug_request_cb(HotplugHandler *hotplug_dev, shpc_device_unplug_request_cb(hotplug_dev, dev, errp); } -static void pci_bridge_dev_class_init(ObjectClass *klass, void *data) +static void pci_bridge_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -280,7 +280,7 @@ static const TypeInfo pci_bridge_dev_info = { * different pci id, so we can match it easily in the guest for * automagic multiseat configuration. See docs/multiseat.txt for more. */ -static void pci_bridge_dev_seat_class_init(ObjectClass *klass, void *data) +static void pci_bridge_dev_seat_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 3396ab4bdd..1e2e394ee8 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -92,7 +92,7 @@ static void prop_pxb_uid_get(Object *obj, Visitor *v, const char *name, visit_type_uint32(v, name, &uid, errp); } -static void pxb_bus_class_init(ObjectClass *class, void *data) +static void pxb_bus_class_init(ObjectClass *class, const void *data) { PCIBusClass *pbc = PCI_BUS_CLASS(class); @@ -169,7 +169,7 @@ static char *pxb_host_ofw_unit_address(const SysBusDevice *dev) return NULL; } -static void pxb_host_class_init(ObjectClass *class, void *data) +static void pxb_host_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(class); @@ -224,7 +224,7 @@ void pxb_cxl_hook_up_registers(CXLState *cxl_state, PCIBus *bus, Error **errp) cxl_state->next_mr_idx++; } -static void pxb_cxl_host_class_init(ObjectClass *class, void *data) +static void pxb_cxl_host_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(class); @@ -427,7 +427,7 @@ static const Property pxb_dev_properties[] = { DEFINE_PROP_BOOL("bypass_iommu", PXBDev, bypass_iommu, false), }; -static void pxb_dev_class_init(ObjectClass *klass, void *data) +static void pxb_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -465,7 +465,7 @@ static void pxb_pcie_dev_realize(PCIDevice *dev, Error **errp) pxb_dev_realize_common(dev, PCIE, errp); } -static void pxb_pcie_dev_class_init(ObjectClass *klass, void *data) +static void pxb_pcie_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -510,7 +510,7 @@ static const Property pxb_cxl_dev_properties[] = { DEFINE_PROP_BOOL("hdm_for_passthrough", PXBCXLDev, hdm_for_passthrough, false), }; -static void pxb_cxl_dev_class_init(ObjectClass *klass, void *data) +static void pxb_cxl_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c index 2429503cfb..833fe35cd5 100644 --- a/hw/pci-bridge/pcie_pci_bridge.c +++ b/hw/pci-bridge/pcie_pci_bridge.c @@ -137,7 +137,7 @@ static const VMStateDescription pcie_pci_bridge_dev_vmstate = { } }; -static void pcie_pci_bridge_class_init(ObjectClass *klass, void *data) +static void pcie_pci_bridge_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index dd40b366bf..512c2ab305 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -167,7 +167,7 @@ static void rp_instance_post_init(Object *obj) } } -static void rp_class_init(ObjectClass *klass, void *data) +static void rp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-bridge/simba.c b/hw/pci-bridge/simba.c index 5fe090df6c..c7565d9e94 100644 --- a/hw/pci-bridge/simba.c +++ b/hw/pci-bridge/simba.c @@ -66,7 +66,7 @@ static void simba_pci_bridge_realize(PCIDevice *dev, Error **errp) pci_bridge_update_mappings(PCI_BRIDGE(br)); } -static void simba_pci_bridge_class_init(ObjectClass *klass, void *data) +static void simba_pci_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index d4e94f2657..d85c23fe4a 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -153,7 +153,7 @@ static const VMStateDescription vmstate_xio3130_downstream = { } }; -static void xio3130_downstream_class_init(ObjectClass *klass, void *data) +static void xio3130_downstream_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c index fb1547b74a..d7a2715812 100644 --- a/hw/pci-bridge/xio3130_upstream.c +++ b/hw/pci-bridge/xio3130_upstream.c @@ -123,7 +123,7 @@ static const VMStateDescription vmstate_xio3130_upstream = { } }; -static void xio3130_upstream_class_init(ObjectClass *klass, void *data) +static void xio3130_upstream_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-host/articia.c b/hw/pci-host/articia.c index f3fcc49f81..043fb85e84 100644 --- a/hw/pci-host/articia.c +++ b/hw/pci-host/articia.c @@ -195,7 +195,7 @@ static void articia_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, s->irq, ARRAY_SIZE(s->irq)); } -static void articia_class_init(ObjectClass *klass, void *data) +static void articia_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -228,7 +228,7 @@ static void articia_pci_host_cfg_write(PCIDevice *d, uint32_t addr, } } -static void articia_pci_host_class_init(ObjectClass *klass, void *data) +static void articia_pci_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -246,7 +246,7 @@ static void articia_pci_host_class_init(ObjectClass *klass, void *data) /* TYPE_ARTICIA_PCI_BRIDGE */ -static void articia_pci_bridge_class_init(ObjectClass *klass, void *data) +static void articia_pci_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-host/astro.c b/hw/pci-host/astro.c index c6f2d4f494..859e308c57 100644 --- a/hw/pci-host/astro.c +++ b/hw/pci-host/astro.c @@ -482,7 +482,7 @@ static const VMStateDescription vmstate_elroy = { } }; -static void elroy_pcihost_class_init(ObjectClass *klass, void *data) +static void elroy_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -909,7 +909,7 @@ static void astro_realize(DeviceState *obj, Error **errp) } } -static void astro_class_init(ObjectClass *klass, void *data) +static void astro_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -932,7 +932,7 @@ static const TypeInfo astro_chip_info = { }; static void astro_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index 4966914892..4508cdd21a 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -757,7 +757,7 @@ PCIBus *bonito_init(qemu_irq *pic) return phb->bus; } -static void bonito_pci_class_init(ObjectClass *klass, void *data) +static void bonito_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -789,7 +789,7 @@ static const TypeInfo bonito_pci_info = { }, }; -static void bonito_host_class_init(ObjectClass *klass, void *data) +static void bonito_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c index 9c3a5f8d91..d03c998e3a 100644 --- a/hw/pci-host/designware.c +++ b/hw/pci-host/designware.c @@ -56,7 +56,8 @@ #define DESIGNWARE_PCIE_ATU_DEVFN(x) (((x) >> 16) & 0xff) #define DESIGNWARE_PCIE_ATU_UPPER_TARGET 0x91C -static void designware_pcie_root_bus_class_init(ObjectClass *klass, void *data) +static void designware_pcie_root_bus_class_init(ObjectClass *klass, + const void *data) { BusClass *k = BUS_CLASS(klass); @@ -587,7 +588,8 @@ static const VMStateDescription vmstate_designware_pcie_root = { } }; -static void designware_pcie_root_class_init(ObjectClass *klass, void *data) +static void designware_pcie_root_class_init(ObjectClass *klass, + const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -727,7 +729,8 @@ static const VMStateDescription vmstate_designware_pcie_host = { } }; -static void designware_pcie_host_class_init(ObjectClass *klass, void *data) +static void designware_pcie_host_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); diff --git a/hw/pci-host/dino.c b/hw/pci-host/dino.c index 58fdbf7bc9..11b353be2e 100644 --- a/hw/pci-host/dino.c +++ b/hw/pci-host/dino.c @@ -497,7 +497,7 @@ static const Property dino_pcihost_properties[] = { MemoryRegion *), }; -static void dino_pcihost_class_init(ObjectClass *klass, void *data) +static void dino_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/fsl_imx8m_phy.c b/hw/pci-host/fsl_imx8m_phy.c index aa304b102b..04da3f99a0 100644 --- a/hw/pci-host/fsl_imx8m_phy.c +++ b/hw/pci-host/fsl_imx8m_phy.c @@ -76,7 +76,7 @@ static const VMStateDescription fsl_imx8m_pcie_phy_vmstate = { } }; -static void fsl_imx8m_pcie_phy_class_init(ObjectClass *klass, void *data) +static void fsl_imx8m_pcie_phy_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c index 9fcedd7fc5..7dcac4ee3c 100644 --- a/hw/pci-host/gpex.c +++ b/hw/pci-host/gpex.c @@ -192,7 +192,7 @@ static const Property gpex_host_properties[] = { DEFINE_PROP_UINT8("num-irqs", GPEXHost, num_irqs, PCI_NUM_PINS), }; -static void gpex_host_class_init(ObjectClass *klass, void *data) +static void gpex_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); @@ -237,7 +237,7 @@ static const VMStateDescription vmstate_gpex_root = { } }; -static void gpex_root_class_init(ObjectClass *klass, void *data) +static void gpex_root_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index 84e5ee8c6e..b48d44623d 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -94,7 +94,7 @@ static void grackle_pci_realize(PCIDevice *d, Error **errp) d->config[PCI_CLASS_PROG] = 0x01; } -static void grackle_pci_class_init(ObjectClass *klass, void *data) +static void grackle_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -133,7 +133,7 @@ static const Property grackle_properties[] = { DEFINE_PROP_UINT32("ofw-addr", GrackleState, ofw_addr, -1), }; -static void grackle_class_init(ObjectClass *klass, void *data) +static void grackle_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); diff --git a/hw/pci-host/gt64120.c b/hw/pci-host/gt64120.c index d5c13a89b6..bd74b6e871 100644 --- a/hw/pci-host/gt64120.c +++ b/hw/pci-host/gt64120.c @@ -1244,7 +1244,7 @@ static void gt64120_pci_reset_hold(Object *obj, ResetType type) pci_set_byte(d->config + 0x3d, 0x01); } -static void gt64120_pci_class_init(ObjectClass *klass, void *data) +static void gt64120_pci_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -1279,7 +1279,7 @@ static const Property gt64120_properties[] = { cpu_little_endian, false), }; -static void gt64120_class_init(ObjectClass *klass, void *data) +static void gt64120_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/i440fx.c b/hw/pci-host/i440fx.c index 1e69691c6d..fcc9f3818a 100644 --- a/hw/pci-host/i440fx.c +++ b/hw/pci-host/i440fx.c @@ -315,7 +315,7 @@ static void i440fx_pcihost_realize(DeviceState *dev, Error **errp) i440fx_update_memory_mappings(f); } -static void i440fx_class_init(ObjectClass *klass, void *data) +static void i440fx_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -364,7 +364,7 @@ static const Property i440fx_props[] = { DEFINE_PROP_STRING(I440FX_HOST_PROP_PCI_TYPE, I440FXState, pci_type), }; -static void i440fx_pcihost_class_init(ObjectClass *klass, void *data) +static void i440fx_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c index a297318c6e..f51f385b22 100644 --- a/hw/pci-host/mv64361.c +++ b/hw/pci-host/mv64361.c @@ -26,7 +26,7 @@ #define TYPE_MV64361_PCI_BRIDGE "mv64361-pcibridge" -static void mv64361_pcibridge_class_init(ObjectClass *klass, void *data) +static void mv64361_pcibridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -102,7 +102,7 @@ static const Property mv64361_pcihost_props[] = { DEFINE_PROP_UINT8("index", MV64361PCIState, index, 0), }; -static void mv64361_pcihost_class_init(ObjectClass *klass, void *data) +static void mv64361_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -923,7 +923,7 @@ static void mv64361_reset(DeviceState *dev) set_mem_windows(s, 0xfbfff); } -static void mv64361_class_init(ObjectClass *klass, void *data) +static void mv64361_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/pnv_phb.c b/hw/pci-host/pnv_phb.c index 6c1e76fbbb..4b0ced79b0 100644 --- a/hw/pci-host/pnv_phb.c +++ b/hw/pci-host/pnv_phb.c @@ -194,7 +194,7 @@ static const Property pnv_phb_properties[] = { PnvPhb4PecState *), }; -static void pnv_phb_class_init(ObjectClass *klass, void *data) +static void pnv_phb_class_init(ObjectClass *klass, const void *data) { PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -304,7 +304,7 @@ static const Property pnv_phb_root_port_properties[] = { DEFINE_PROP_UINT32("version", PnvPHBRootPort, version, 0), }; -static void pnv_phb_root_port_class_init(ObjectClass *klass, void *data) +static void pnv_phb_root_port_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index 82884e1e92..a4335f44f2 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -888,7 +888,7 @@ DECLARE_INSTANCE_CHECKER(IOMMUMemoryRegion, PNV_PHB3_IOMMU_MEMORY_REGION, TYPE_PNV_PHB3_IOMMU_MEMORY_REGION) static void pnv_phb3_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); @@ -1097,7 +1097,7 @@ static const Property pnv_phb3_properties[] = { DEFINE_PROP_LINK("phb-base", PnvPHB3, phb_base, TYPE_PNV_PHB, PnvPHB *), }; -static void pnv_phb3_class_init(ObjectClass *klass, void *data) +static void pnv_phb3_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1149,7 +1149,7 @@ static void pnv_phb3_root_bus_set_prop(Object *obj, Visitor *v, } } -static void pnv_phb3_root_bus_class_init(ObjectClass *klass, void *data) +static void pnv_phb3_root_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); diff --git a/hw/pci-host/pnv_phb3_msi.c b/hw/pci-host/pnv_phb3_msi.c index 81986644b1..3a83311faf 100644 --- a/hw/pci-host/pnv_phb3_msi.c +++ b/hw/pci-host/pnv_phb3_msi.c @@ -284,7 +284,7 @@ static void phb3_msi_instance_init(Object *obj) ics->offset = 0; } -static void phb3_msi_class_init(ObjectClass *klass, void *data) +static void phb3_msi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ICSStateClass *isc = ICS_CLASS(klass); diff --git a/hw/pci-host/pnv_phb3_pbcq.c b/hw/pci-host/pnv_phb3_pbcq.c index 82f70efa43..4e24b1449d 100644 --- a/hw/pci-host/pnv_phb3_pbcq.c +++ b/hw/pci-host/pnv_phb3_pbcq.c @@ -337,7 +337,7 @@ static void phb3_pbcq_instance_init(Object *obj) OBJ_PROP_LINK_STRONG); } -static void pnv_pbcq_class_init(ObjectClass *klass, void *data) +static void pnv_pbcq_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 178c73f519..feb812dc1a 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -1362,7 +1362,7 @@ DECLARE_INSTANCE_CHECKER(IOMMUMemoryRegion, PNV_PHB4_IOMMU_MEMORY_REGION, TYPE_PNV_PHB4_IOMMU_MEMORY_REGION) static void pnv_phb4_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); @@ -1696,7 +1696,7 @@ static const Property pnv_phb4_properties[] = { DEFINE_PROP_LINK("phb-base", PnvPHB4, phb_base, TYPE_PNV_PHB, PnvPHB *), }; -static void pnv_phb4_class_init(ObjectClass *klass, void *data) +static void pnv_phb4_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XiveNotifierClass *xfc = XIVE_NOTIFIER_CLASS(klass); @@ -1761,7 +1761,7 @@ static void pnv_phb4_root_bus_set_prop(Object *obj, Visitor *v, } } -static void pnv_phb4_root_bus_class_init(ObjectClass *klass, void *data) +static void pnv_phb4_root_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c index cb8a7e3afa..cc46641cdf 100644 --- a/hw/pci-host/pnv_phb4_pec.c +++ b/hw/pci-host/pnv_phb4_pec.c @@ -354,7 +354,7 @@ static uint32_t pnv_pec_xscom_nest_base(PnvPhb4PecState *pec) */ static const uint32_t pnv_pec_num_phbs[] = { 1, 2, 3 }; -static void pnv_pec_class_init(ObjectClass *klass, void *data) +static void pnv_pec_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); @@ -419,7 +419,7 @@ static uint32_t pnv_phb5_pec_xscom_nest_base(PnvPhb4PecState *pec) */ static const uint32_t pnv_phb5_pec_num_stacks[] = { 3, 3 }; -static void pnv_phb5_pec_class_init(ObjectClass *klass, void *data) +static void pnv_phb5_pec_class_init(ObjectClass *klass, const void *data) { PnvPhb4PecClass *pecc = PNV_PHB4_PEC_CLASS(klass); static const char compat[] = "ibm,power10-pbcq"; diff --git a/hw/pci-host/ppc440_pcix.c b/hw/pci-host/ppc440_pcix.c index 07924bce28..744b85e49c 100644 --- a/hw/pci-host/ppc440_pcix.c +++ b/hw/pci-host/ppc440_pcix.c @@ -519,7 +519,7 @@ static void ppc440_pcix_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static void ppc440_pcix_class_init(ObjectClass *klass, void *data) +static void ppc440_pcix_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/ppc4xx_pci.c b/hw/pci-host/ppc4xx_pci.c index 292cb308ba..dcc4b78660 100644 --- a/hw/pci-host/ppc4xx_pci.c +++ b/hw/pci-host/ppc4xx_pci.c @@ -349,7 +349,7 @@ static void ppc4xx_pcihost_realize(DeviceState *dev, Error **errp) qemu_register_reset(ppc4xx_pci_reset, s); } -static void ppc4xx_host_bridge_class_init(ObjectClass *klass, void *data) +static void ppc4xx_host_bridge_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -376,7 +376,7 @@ static const TypeInfo ppc4xx_host_bridge_info = { }, }; -static void ppc4xx_pcihost_class_init(ObjectClass *klass, void *data) +static void ppc4xx_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 9b905d1971..2f6354c931 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -490,7 +490,7 @@ static void e500_pcihost_realize(DeviceState *dev, Error **errp) pci_bus_set_route_irq_fn(b, e500_route_intx_pin_to_irq); } -static void e500_host_bridge_class_init(ObjectClass *klass, void *data) +static void e500_host_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -512,7 +512,7 @@ static const Property pcihost_properties[] = { DEFINE_PROP_UINT32("first_pin_irq", PPCE500PCIState, first_pin_irq, 0x1), }; -static void e500_pcihost_class_init(ObjectClass *klass, void *data) +static void e500_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index 06be3d77cb..c2a71108f2 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -184,7 +184,7 @@ static const Property q35_host_props[] = { DEFINE_PROP_BOOL("x-pci-hole64-fix", Q35PCIHost, pci_hole64_fix, true), }; -static void q35_host_class_init(ObjectClass *klass, void *data) +static void q35_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); @@ -667,7 +667,7 @@ static const Property mch_props[] = { DEFINE_PROP_BOOL("smbase-smram", MCHPCIState, has_smram_at_smbase, true), }; -static void mch_class_init(ObjectClass *klass, void *data) +static void mch_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index e3d8d206b7..3f158838a0 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -392,7 +392,7 @@ static const VMStateDescription vmstate_raven = { }, }; -static void raven_class_init(ObjectClass *klass, void *data) +static void raven_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -431,7 +431,7 @@ static const Property raven_pcihost_properties[] = { false), }; -static void raven_pcihost_class_init(ObjectClass *klass, void *data) +static void raven_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/remote.c b/hw/pci-host/remote.c index be077d075e..e6d2af4502 100644 --- a/hw/pci-host/remote.c +++ b/hw/pci-host/remote.c @@ -46,7 +46,7 @@ static void remote_pcihost_realize(DeviceState *dev, Error **errp) 0, TYPE_PCIE_BUS); } -static void remote_pcihost_class_init(ObjectClass *klass, void *data) +static void remote_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); diff --git a/hw/pci-host/sabre.c b/hw/pci-host/sabre.c index be6641de15..f7086086f9 100644 --- a/hw/pci-host/sabre.c +++ b/hw/pci-host/sabre.c @@ -456,7 +456,7 @@ static void sabre_pci_realize(PCIDevice *d, Error **errp) PCI_STATUS_DEVSEL_MEDIUM); } -static void sabre_pci_class_init(ObjectClass *klass, void *data) +static void sabre_pci_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -497,7 +497,7 @@ static const Property sabre_properties[] = { DEFINE_PROP_UINT64("mem-base", SabreState, mem_base, 0), }; -static void sabre_class_init(ObjectClass *klass, void *data) +static void sabre_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); diff --git a/hw/pci-host/sh_pci.c b/hw/pci-host/sh_pci.c index 4edebced5e..52bff66d6a 100644 --- a/hw/pci-host/sh_pci.c +++ b/hw/pci-host/sh_pci.c @@ -153,7 +153,7 @@ static void sh_pcic_pci_realize(PCIDevice *d, Error **errp) PCI_STATUS_FAST_BACK | PCI_STATUS_DEVSEL_MEDIUM); } -static void sh_pcic_pci_class_init(ObjectClass *klass, void *data) +static void sh_pcic_pci_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -168,7 +168,7 @@ static void sh_pcic_pci_class_init(ObjectClass *klass, void *data) dc->user_creatable = false; } -static void sh_pcic_host_class_init(ObjectClass *klass, void *data) +static void sh_pcic_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 37e2461bbb..7cb37e01d8 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -311,7 +311,7 @@ static void unin_internal_pci_host_realize(PCIDevice *d, Error **errp) d->config[PCI_CAPABILITY_LIST] = 0x00; } -static void unin_main_pci_host_class_init(ObjectClass *klass, void *data) +static void unin_main_pci_host_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -339,7 +339,7 @@ static const TypeInfo unin_main_pci_host_info = { }, }; -static void u3_agp_pci_host_class_init(ObjectClass *klass, void *data) +static void u3_agp_pci_host_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -367,7 +367,7 @@ static const TypeInfo u3_agp_pci_host_info = { }, }; -static void unin_agp_pci_host_class_init(ObjectClass *klass, void *data) +static void unin_agp_pci_host_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -395,7 +395,8 @@ static const TypeInfo unin_agp_pci_host_info = { }, }; -static void unin_internal_pci_host_class_init(ObjectClass *klass, void *data) +static void unin_internal_pci_host_class_init(ObjectClass *klass, + const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -427,7 +428,7 @@ static const Property pci_unin_main_pci_host_props[] = { DEFINE_PROP_UINT32("ofw-addr", UNINHostState, ofw_addr, -1), }; -static void pci_unin_main_class_init(ObjectClass *klass, void *data) +static void pci_unin_main_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); @@ -447,7 +448,7 @@ static const TypeInfo pci_unin_main_info = { .class_init = pci_unin_main_class_init, }; -static void pci_u3_agp_class_init(ObjectClass *klass, void *data) +static void pci_u3_agp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -463,7 +464,7 @@ static const TypeInfo pci_u3_agp_info = { .class_init = pci_u3_agp_class_init, }; -static void pci_unin_agp_class_init(ObjectClass *klass, void *data) +static void pci_unin_agp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -479,7 +480,7 @@ static const TypeInfo pci_unin_agp_info = { .class_init = pci_unin_agp_class_init, }; -static void pci_unin_internal_class_init(ObjectClass *klass, void *data) +static void pci_unin_internal_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -535,7 +536,7 @@ static void unin_init(Object *obj) sysbus_init_mmio(sbd, &s->mem); } -static void unin_class_init(ObjectClass *klass, void *data) +static void unin_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index 33a8ceb3b5..b333158e10 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -471,7 +471,7 @@ static void versatile_pci_host_realize(PCIDevice *d, Error **errp) pci_set_byte(d->config + PCI_LATENCY_TIMER, 0x10); } -static void versatile_pci_host_class_init(ObjectClass *klass, void *data) +static void versatile_pci_host_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -503,7 +503,7 @@ static const Property pci_vpb_properties[] = { PCI_VPB_IRQMAP_ASSUME_OK), }; -static void pci_vpb_class_init(ObjectClass *klass, void *data) +static void pci_vpb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci-host/xen_igd_pt.c b/hw/pci-host/xen_igd_pt.c index d094b675d6..5dd17ef236 100644 --- a/hw/pci-host/xen_igd_pt.c +++ b/hw/pci-host/xen_igd_pt.c @@ -95,7 +95,8 @@ static void igd_pt_i440fx_realize(PCIDevice *pci_dev, Error **errp) } } -static void igd_passthrough_i440fx_class_init(ObjectClass *klass, void *data) +static void igd_passthrough_i440fx_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index 18688485f4..70e9b2b981 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -165,7 +165,7 @@ static const Property xilinx_pcie_host_props[] = { DEFINE_PROP_BOOL("link_up", XilinxPCIEHost, link_up, true), }; -static void xilinx_pcie_host_class_init(ObjectClass *klass, void *data) +static void xilinx_pcie_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); @@ -286,7 +286,7 @@ static void xilinx_pcie_root_realize(PCIDevice *pci_dev, Error **errp) } } -static void xilinx_pcie_root_class_init(ObjectClass *klass, void *data) +static void xilinx_pcie_root_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 475b97c649..c60991def8 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -261,7 +261,7 @@ static GByteArray *pci_bus_fw_cfg_gen_data(Object *obj, Error **errp) return byte_array; } -static void pci_bus_class_init(ObjectClass *klass, void *data) +static void pci_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); PCIBusClass *pbc = PCI_BUS_CLASS(klass); @@ -309,7 +309,7 @@ static const TypeInfo conventional_pci_interface_info = { .parent = TYPE_INTERFACE, }; -static void pcie_bus_class_init(ObjectClass *klass, void *data) +static void pcie_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -2795,7 +2795,7 @@ MemoryRegion *pci_address_space_io(PCIDevice *dev) return pci_get_bus(dev)->address_space_io; } -static void pci_device_class_init(ObjectClass *klass, void *data) +static void pci_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index aee4dd7d1f..0fe66e8b12 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -482,7 +482,7 @@ static const Property pci_bridge_properties[] = { pcie_writeable_slt_bug, false), }; -static void pci_bridge_class_init(ObjectClass *klass, void *data) +static void pci_bridge_class_init(ObjectClass *klass, const void *data) { AcpiDevAmlIfClass *adevc = ACPI_DEV_AML_IF_CLASS(klass); DeviceClass *k = DEVICE_CLASS(klass); diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index 80f91f409f..abe83bbab8 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -246,7 +246,7 @@ static const Property pci_host_properties_common[] = { DEFINE_PROP_BOOL(PCI_HOST_BYPASS_IOMMU, PCIHostState, bypass_iommu, false), }; -static void pci_host_class_init(ObjectClass *klass, void *data) +static void pci_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_props(dc, pci_host_properties_common); diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index c73db30e98..8629b3aafd 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -118,7 +118,7 @@ static const Property pcie_port_props[] = { PCIE_AER_LOG_MAX_DEFAULT), }; -static void pcie_port_class_init(ObjectClass *oc, void *data) +static void pcie_port_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -211,7 +211,7 @@ static const Property pcie_slot_props[] = { hide_native_hotplug_cap, false), }; -static void pcie_slot_class_init(ObjectClass *oc, void *data) +static void pcie_slot_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); diff --git a/hw/ppc/amigaone.c b/hw/ppc/amigaone.c index e9407a51b5..12279f42bc 100644 --- a/hw/ppc/amigaone.c +++ b/hw/ppc/amigaone.c @@ -173,7 +173,7 @@ static const Property nvram_properties[] = { DEFINE_PROP_DRIVE("drive", A1NVRAMState, blk), }; -static void nvram_class_init(ObjectClass *oc, void *data) +static void nvram_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 70a8033373..cd594eeb3e 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -68,7 +68,7 @@ HotplugHandler *e500plat_machine_get_hotpug_handler(MachineState *machine, #define TYPE_E500PLAT_MACHINE MACHINE_TYPE_NAME("ppce500") -static void e500plat_machine_class_init(ObjectClass *oc, void *data) +static void e500plat_machine_class_init(ObjectClass *oc, const void *data) { PPCE500MachineClass *pmc = PPCE500_MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 5a827846ed..92fe60b2a2 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -563,7 +563,7 @@ static int core99_kvm_type(MachineState *machine, const char *arg) return 2; } -static void core99_machine_class_init(ObjectClass *oc, void *data) +static void core99_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 0d34e6bfda..5c5bf99b4d 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -402,7 +402,7 @@ static int heathrow_kvm_type(MachineState *machine, const char *arg) return 2; } -static void heathrow_class_init(ObjectClass *oc, void *data) +static void heathrow_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index d74af766ee..97fb0f35ba 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -37,7 +37,7 @@ static void mpc8544ds_init(MachineState *machine) ppce500_init(machine); } -static void mpc8544ds_machine_class_init(ObjectClass *oc, void *data) +static void mpc8544ds_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); PPCE500MachineClass *pmc = PPCE500_MACHINE_CLASS(oc); diff --git a/hw/ppc/pef.c b/hw/ppc/pef.c index 8b2d726e00..254f570787 100644 --- a/hw/ppc/pef.c +++ b/hw/ppc/pef.c @@ -128,7 +128,7 @@ OBJECT_DEFINE_TYPE_WITH_INTERFACES(PefGuest, { TYPE_USER_CREATABLE }, { NULL }) -static void pef_guest_class_init(ObjectClass *oc, void *data) +static void pef_guest_class_init(ObjectClass *oc, const void *data) { ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index 7b2dc6985c..bb6f94f502 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -588,7 +588,7 @@ static bool pegasos2_setprop(MachineState *ms, const char *path, return true; } -static void pegasos2_machine_class_init(ObjectClass *oc, void *data) +static void pegasos2_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); PPCVirtualHypervisorClass *vhc = PPC_VIRTUAL_HYPERVISOR_CLASS(oc); diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 63f2232f32..4590231f88 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -1618,7 +1618,7 @@ static uint32_t pnv_chip_power8_xscom_pcba(PnvChip *chip, uint64_t addr) return ((addr >> 4) & ~0xfull) | ((addr >> 3) & 0xf); } -static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power8e_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -1642,7 +1642,7 @@ static void pnv_chip_power8e_class_init(ObjectClass *klass, void *data) &k->parent_realize); } -static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power8_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -1666,7 +1666,7 @@ static void pnv_chip_power8_class_init(ObjectClass *klass, void *data) &k->parent_realize); } -static void pnv_chip_power8nvl_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power8nvl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -1954,7 +1954,7 @@ static uint32_t pnv_chip_power9_xscom_pcba(PnvChip *chip, uint64_t addr) return addr >> 3; } -static void pnv_chip_power9_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power9_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -2302,7 +2302,7 @@ static uint32_t pnv_chip_power10_xscom_pcba(PnvChip *chip, uint64_t addr) return addr >> 3; } -static void pnv_chip_power10_class_init(ObjectClass *klass, void *data) +static void pnv_chip_power10_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvChipClass *k = PNV_CHIP_CLASS(klass); @@ -2461,7 +2461,7 @@ static const Property pnv_chip_properties[] = { DEFINE_PROP_BOOL("lpar-per-core", PnvChip, lpar_per_core, false), }; -static void pnv_chip_class_init(ObjectClass *klass, void *data) +static void pnv_chip_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -2724,7 +2724,7 @@ static void pnv_machine_set_hb(Object *obj, bool value, Error **errp) } } -static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) +static void pnv_machine_power8_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); XICSFabricClass *xic = XICS_FABRIC_CLASS(oc); @@ -2753,7 +2753,7 @@ static void pnv_machine_power8_class_init(ObjectClass *oc, void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); } -static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) +static void pnv_machine_power9_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); XiveFabricClass *xfc = XIVE_FABRIC_CLASS(oc); @@ -2792,7 +2792,7 @@ static void pnv_machine_power9_class_init(ObjectClass *oc, void *data) "Use 1 LPAR per core mode"); } -static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) +static void pnv_machine_p10_common_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); @@ -2822,7 +2822,7 @@ static void pnv_machine_p10_common_class_init(ObjectClass *oc, void *data) machine_class_allow_dynamic_sysbus_dev(mc, TYPE_PNV_PHB); } -static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) +static void pnv_machine_power10_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -2847,7 +2847,8 @@ static void pnv_machine_power10_class_init(ObjectClass *oc, void *data) "Use 1 LPAR per core mode"); } -static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, void *data) +static void pnv_machine_p10_rainier_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); PnvMachineClass *pmc = PNV_MACHINE_CLASS(oc); @@ -2912,7 +2913,7 @@ static void pnv_nmi(NMIState *n, int cpu_index, Error **errp) } } -static void pnv_machine_class_init(ObjectClass *oc, void *data) +static void pnv_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); InterruptStatsProviderClass *ispc = INTERRUPT_STATS_PROVIDER_CLASS(oc); diff --git a/hw/ppc/pnv_adu.c b/hw/ppc/pnv_adu.c index d09a167466..f9620806ec 100644 --- a/hw/ppc/pnv_adu.c +++ b/hw/ppc/pnv_adu.c @@ -189,7 +189,7 @@ static const Property pnv_adu_properties[] = { DEFINE_PROP_LINK("lpc", PnvADU, lpc, TYPE_PNV_LPC, PnvLpcController *), }; -static void pnv_adu_class_init(ObjectClass *klass, void *data) +static void pnv_adu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c index c8987ae67a..4ca511a4de 100644 --- a/hw/ppc/pnv_chiptod.c +++ b/hw/ppc/pnv_chiptod.c @@ -456,7 +456,7 @@ static const Property pnv_chiptod_properties[] = { DEFINE_PROP_LINK("chip", PnvChipTOD , chip, TYPE_PNV_CHIP, PnvChip *), }; -static void pnv_chiptod_power9_class_init(ObjectClass *klass, void *data) +static void pnv_chiptod_power9_class_init(ObjectClass *klass, const void *data) { PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -492,7 +492,7 @@ static int pnv_chiptod_power10_dt_xscom(PnvXScomInterface *dev, void *fdt, return pnv_chiptod_dt_xscom(dev, fdt, xscom_offset, compat, sizeof(compat)); } -static void pnv_chiptod_power10_class_init(ObjectClass *klass, void *data) +static void pnv_chiptod_power10_class_init(ObjectClass *klass, const void *data) { PnvChipTODClass *pctc = PNV_CHIPTOD_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -555,7 +555,7 @@ static void pnv_chiptod_unrealize(DeviceState *dev) qemu_unregister_reset(pnv_chiptod_reset, chiptod); } -static void pnv_chiptod_class_init(ObjectClass *klass, void *data) +static void pnv_chiptod_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_core.c b/hw/ppc/pnv_core.c index a33977da18..08c20224b9 100644 --- a/hw/ppc/pnv_core.c +++ b/hw/ppc/pnv_core.c @@ -449,7 +449,7 @@ static const Property pnv_core_properties[] = { DEFINE_PROP_LINK("chip", PnvCore, chip, TYPE_PNV_CHIP, PnvChip *), }; -static void pnv_core_power8_class_init(ObjectClass *oc, void *data) +static void pnv_core_power8_class_init(ObjectClass *oc, const void *data) { PnvCoreClass *pcc = PNV_CORE_CLASS(oc); @@ -457,7 +457,7 @@ static void pnv_core_power8_class_init(ObjectClass *oc, void *data) pcc->xscom_size = PNV_XSCOM_EX_SIZE; } -static void pnv_core_power9_class_init(ObjectClass *oc, void *data) +static void pnv_core_power9_class_init(ObjectClass *oc, const void *data) { PnvCoreClass *pcc = PNV_CORE_CLASS(oc); @@ -465,7 +465,7 @@ static void pnv_core_power9_class_init(ObjectClass *oc, void *data) pcc->xscom_size = PNV_XSCOM_EX_SIZE; } -static void pnv_core_power10_class_init(ObjectClass *oc, void *data) +static void pnv_core_power10_class_init(ObjectClass *oc, const void *data) { PnvCoreClass *pcc = PNV_CORE_CLASS(oc); @@ -473,7 +473,7 @@ static void pnv_core_power10_class_init(ObjectClass *oc, void *data) pcc->xscom_size = PNV10_XSCOM_EC_SIZE; } -static void pnv_core_class_init(ObjectClass *oc, void *data) +static void pnv_core_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -700,7 +700,7 @@ static const Property pnv_quad_properties[] = { DEFINE_PROP_UINT32("quad-id", PnvQuad, quad_id, 0), }; -static void pnv_quad_power9_class_init(ObjectClass *oc, void *data) +static void pnv_quad_power9_class_init(ObjectClass *oc, const void *data) { PnvQuadClass *pqc = PNV_QUAD_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -711,7 +711,7 @@ static void pnv_quad_power9_class_init(ObjectClass *oc, void *data) pqc->xscom_size = PNV9_XSCOM_EQ_SIZE; } -static void pnv_quad_power10_class_init(ObjectClass *oc, void *data) +static void pnv_quad_power10_class_init(ObjectClass *oc, const void *data) { PnvQuadClass *pqc = PNV_QUAD_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -725,7 +725,7 @@ static void pnv_quad_power10_class_init(ObjectClass *oc, void *data) pqc->xscom_qme_size = PNV10_XSCOM_QME_SIZE; } -static void pnv_quad_class_init(ObjectClass *oc, void *data) +static void pnv_quad_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/pnv_homer.c b/hw/ppc/pnv_homer.c index 0521f9a428..2208ffe632 100644 --- a/hw/ppc/pnv_homer.c +++ b/hw/ppc/pnv_homer.c @@ -89,7 +89,7 @@ static hwaddr pnv_homer_power8_get_base(PnvChip *chip) return PNV_HOMER_BASE(chip); } -static void pnv_homer_power8_class_init(ObjectClass *klass, void *data) +static void pnv_homer_power8_class_init(ObjectClass *klass, const void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); @@ -156,7 +156,7 @@ static hwaddr pnv_homer_power9_get_base(PnvChip *chip) return PNV9_HOMER_BASE(chip); } -static void pnv_homer_power9_class_init(ObjectClass *klass, void *data) +static void pnv_homer_power9_class_init(ObjectClass *klass, const void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); @@ -223,7 +223,7 @@ static hwaddr pnv_homer_power10_get_base(PnvChip *chip) return PNV10_HOMER_BASE(chip); } -static void pnv_homer_power10_class_init(ObjectClass *klass, void *data) +static void pnv_homer_power10_class_init(ObjectClass *klass, const void *data) { PnvHomerClass *homer = PNV_HOMER_CLASS(klass); @@ -266,7 +266,7 @@ static const Property pnv_homer_properties[] = { DEFINE_PROP_LINK("chip", PnvHomer, chip, TYPE_PNV_CHIP, PnvChip *), }; -static void pnv_homer_class_init(ObjectClass *klass, void *data) +static void pnv_homer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c index 8d35f452a2..b2f372c874 100644 --- a/hw/ppc/pnv_i2c.c +++ b/hw/ppc/pnv_i2c.c @@ -549,7 +549,7 @@ static const Property pnv_i2c_properties[] = { DEFINE_PROP_UINT32("num-busses", PnvI2C, num_busses, 1), }; -static void pnv_i2c_class_init(ObjectClass *klass, void *data) +static void pnv_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xscomc = PNV_XSCOM_INTERFACE_CLASS(klass); diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index d812dc8268..d92347bcd2 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -714,7 +714,7 @@ static void pnv_lpc_power8_realize(DeviceState *dev, Error **errp) PNV_XSCOM_LPC_SIZE); } -static void pnv_lpc_power8_class_init(ObjectClass *klass, void *data) +static void pnv_lpc_power8_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); @@ -760,7 +760,7 @@ static void pnv_lpc_power9_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out_named(dev, lpc->psi_irq_serirq, "SERIRQ", 4); } -static void pnv_lpc_power9_class_init(ObjectClass *klass, void *data) +static void pnv_lpc_power9_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvLpcClass *plc = PNV_LPC_CLASS(klass); @@ -777,7 +777,7 @@ static const TypeInfo pnv_lpc_power9_info = { .class_init = pnv_lpc_power9_class_init, }; -static void pnv_lpc_power10_class_init(ObjectClass *klass, void *data) +static void pnv_lpc_power10_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -843,7 +843,7 @@ static const Property pnv_lpc_properties[] = { DEFINE_PROP_BOOL("psi-serirq", PnvLpcController, psi_has_serirq, false), }; -static void pnv_lpc_class_init(ObjectClass *klass, void *data) +static void pnv_lpc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_n1_chiplet.c b/hw/ppc/pnv_n1_chiplet.c index 03ff9fbad0..05e3fd6f73 100644 --- a/hw/ppc/pnv_n1_chiplet.c +++ b/hw/ppc/pnv_n1_chiplet.c @@ -136,7 +136,7 @@ static void pnv_n1_chiplet_realize(DeviceState *dev, Error **errp) PNV10_XSCOM_N1_PB_SCOM_ES_SIZE); } -static void pnv_n1_chiplet_class_init(ObjectClass *klass, void *data) +static void pnv_n1_chiplet_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_nest_pervasive.c b/hw/ppc/pnv_nest_pervasive.c index 780fa69dde..b5182d54fa 100644 --- a/hw/ppc/pnv_nest_pervasive.c +++ b/hw/ppc/pnv_nest_pervasive.c @@ -181,7 +181,7 @@ static void pnv_nest_pervasive_realize(DeviceState *dev, Error **errp) PNV10_XSCOM_CHIPLET_CTRL_REGS_SIZE); } -static void pnv_nest_pervasive_class_init(ObjectClass *klass, void *data) +static void pnv_nest_pervasive_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index 0c9d3daec2..fa6f31cb8d 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -172,7 +172,7 @@ const MemoryRegionOps pnv_occ_sram_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_occ_power8_class_init(ObjectClass *klass, void *data) +static void pnv_occ_power8_class_init(ObjectClass *klass, const void *data) { PnvOCCClass *poc = PNV_OCC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -246,7 +246,7 @@ static const MemoryRegionOps pnv_occ_power9_xscom_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_occ_power9_class_init(ObjectClass *klass, void *data) +static void pnv_occ_power9_class_init(ObjectClass *klass, const void *data) { PnvOCCClass *poc = PNV_OCC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -266,7 +266,7 @@ static const TypeInfo pnv_occ_power9_type_info = { .class_init = pnv_occ_power9_class_init, }; -static void pnv_occ_power10_class_init(ObjectClass *klass, void *data) +static void pnv_occ_power10_class_init(ObjectClass *klass, const void *data) { PnvOCCClass *poc = PNV_OCC_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -335,7 +335,7 @@ static const Property pnv_occ_properties[] = { DEFINE_PROP_LINK("homer", PnvOCC, homer, TYPE_PNV_HOMER, PnvHomer *), }; -static void pnv_occ_class_init(ObjectClass *klass, void *data) +static void pnv_occ_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_pnor.c b/hw/ppc/pnv_pnor.c index 9db44ca21d..af7cfd028b 100644 --- a/hw/ppc/pnv_pnor.c +++ b/hw/ppc/pnv_pnor.c @@ -119,7 +119,7 @@ static const Property pnv_pnor_properties[] = { DEFINE_PROP_DRIVE("drive", PnvPnor, blk), }; -static void pnv_pnor_class_init(ObjectClass *klass, void *data) +static void pnv_pnor_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index f832ee61e8..0fd247e6ad 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -557,7 +557,7 @@ static const Property pnv_psi_properties[] = { DEFINE_PROP_UINT64("fsp-bar", PnvPsi, fsp_bar, 0), }; -static void pnv_psi_power8_class_init(ObjectClass *klass, void *data) +static void pnv_psi_power8_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); @@ -887,7 +887,7 @@ static void pnv_psi_power9_realize(DeviceState *dev, Error **errp) pnv_psi_realize(dev, errp); } -static void pnv_psi_power9_class_init(ObjectClass *klass, void *data) +static void pnv_psi_power9_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); @@ -919,7 +919,7 @@ static const TypeInfo pnv_psi_power9_info = { }, }; -static void pnv_psi_power10_class_init(ObjectClass *klass, void *data) +static void pnv_psi_power10_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvPsiClass *ppc = PNV_PSI_CLASS(klass); @@ -939,7 +939,7 @@ static const TypeInfo pnv_psi_power10_info = { .class_init = pnv_psi_power10_class_init, }; -static void pnv_psi_class_init(ObjectClass *klass, void *data) +static void pnv_psi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xdc = PNV_XSCOM_INTERFACE_CLASS(klass); diff --git a/hw/ppc/pnv_sbe.c b/hw/ppc/pnv_sbe.c index 74cee4eea7..34dc013d47 100644 --- a/hw/ppc/pnv_sbe.c +++ b/hw/ppc/pnv_sbe.c @@ -331,7 +331,7 @@ static const MemoryRegionOps pnv_sbe_power9_xscom_mbox_ops = { .endianness = DEVICE_BIG_ENDIAN, }; -static void pnv_sbe_power9_class_init(ObjectClass *klass, void *data) +static void pnv_sbe_power9_class_init(ObjectClass *klass, const void *data) { PnvSBEClass *psc = PNV_SBE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -350,7 +350,7 @@ static const TypeInfo pnv_sbe_power9_type_info = { .class_init = pnv_sbe_power9_class_init, }; -static void pnv_sbe_power10_class_init(ObjectClass *klass, void *data) +static void pnv_sbe_power10_class_init(ObjectClass *klass, const void *data) { PnvSBEClass *psc = PNV_SBE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -386,7 +386,7 @@ static void pnv_sbe_realize(DeviceState *dev, Error **errp) sbe->timer = timer_new_us(QEMU_CLOCK_VIRTUAL, sbe_timer, sbe); } -static void pnv_sbe_class_init(ObjectClass *klass, void *data) +static void pnv_sbe_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/ppc440_uc.c b/hw/ppc/ppc440_uc.c index 9da30a1724..89e3fae08d 100644 --- a/hw/ppc/ppc440_uc.c +++ b/hw/ppc/ppc440_uc.c @@ -1027,7 +1027,7 @@ static const Property ppc460ex_pcie_props[] = { PowerPCCPU *), }; -static void ppc460ex_pcie_class_init(ObjectClass *klass, void *data) +static void ppc460ex_pcie_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/ppc4xx_devs.c b/hw/ppc/ppc4xx_devs.c index 9ce9777510..f36c519c8b 100644 --- a/hw/ppc/ppc4xx_devs.c +++ b/hw/ppc/ppc4xx_devs.c @@ -236,7 +236,7 @@ static const Property ppc4xx_mal_properties[] = { DEFINE_PROP_UINT8("rxc-num", Ppc4xxMalState, rxcnum, 0), }; -static void ppc4xx_mal_class_init(ObjectClass *oc, void *data) +static void ppc4xx_mal_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -326,7 +326,7 @@ static void ppc405_plb_realize(DeviceState *dev, Error **errp) ppc4xx_dcr_register(dcr, PLB4A1_ACR, plb, &dcr_read_plb, &dcr_write_plb); } -static void ppc405_plb_class_init(ObjectClass *oc, void *data) +static void ppc405_plb_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -512,7 +512,7 @@ static void ppc405_ebc_realize(DeviceState *dev, Error **errp) ppc4xx_dcr_register(dcr, EBC0_CFGDATA, ebc, &dcr_read_ebc, &dcr_write_ebc); } -static void ppc405_ebc_class_init(ObjectClass *oc, void *data) +static void ppc405_ebc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -543,7 +543,7 @@ static const Property ppc4xx_dcr_properties[] = { PowerPCCPU *), }; -static void ppc4xx_dcr_class_init(ObjectClass *oc, void *data) +static void ppc4xx_dcr_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/ppc4xx_sdram.c b/hw/ppc/ppc4xx_sdram.c index bf0faad9e7..592769826b 100644 --- a/hw/ppc/ppc4xx_sdram.c +++ b/hw/ppc/ppc4xx_sdram.c @@ -431,7 +431,7 @@ static const Property ppc4xx_sdram_ddr_props[] = { DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdrState, nbanks, 4), }; -static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, void *data) +static void ppc4xx_sdram_ddr_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -715,7 +715,7 @@ static const Property ppc4xx_sdram_ddr2_props[] = { DEFINE_PROP_UINT32("nbanks", Ppc4xxSdramDdr2State, nbanks, 4), }; -static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, void *data) +static void ppc4xx_sdram_ddr2_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/ppce500_spin.c b/hw/ppc/ppce500_spin.c index baab74c4ed..2310f62a91 100644 --- a/hw/ppc/ppce500_spin.c +++ b/hw/ppc/ppce500_spin.c @@ -175,7 +175,7 @@ static void ppce500_spin_initfn(Object *obj) sysbus_init_mmio(dev, &s->iomem); } -static void ppce500_spin_class_init(ObjectClass *klass, void *data) +static void ppce500_spin_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/prep_systemio.c b/hw/ppc/prep_systemio.c index 08f29e72e4..41cd923b94 100644 --- a/hw/ppc/prep_systemio.c +++ b/hw/ppc/prep_systemio.c @@ -290,7 +290,7 @@ static const Property prep_systemio_properties[] = { DEFINE_PROP_UINT8("equipment", PrepSystemIoState, equipment, 0), }; -static void prep_systemio_class_initfn(ObjectClass *klass, void *data) +static void prep_systemio_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/rs6000_mc.c b/hw/ppc/rs6000_mc.c index 27f1c90f06..a0964051d1 100644 --- a/hw/ppc/rs6000_mc.c +++ b/hw/ppc/rs6000_mc.c @@ -212,7 +212,7 @@ static const Property rs6000mc_properties[] = { DEFINE_PROP_BOOL("auto-configure", RS6000MCState, autoconfigure, true), }; -static void rs6000mc_class_initfn(ObjectClass *klass, void *data) +static void rs6000mc_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index c1a7ac3536..02663851ae 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4595,7 +4595,7 @@ static void spapr_cpu_exec_exit(PPCVirtualHypervisor *vhyp, PowerPCCPU *cpu) } } -static void spapr_machine_class_init(ObjectClass *oc, void *data) +static void spapr_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); SpaprMachineClass *smc = SPAPR_MACHINE_CLASS(oc); @@ -4739,7 +4739,7 @@ static void spapr_machine_latest_class_options(MachineClass *mc) #define DEFINE_SPAPR_MACHINE_IMPL(latest, ...) \ static void MACHINE_VER_SYM(class_init, spapr, __VA_ARGS__)( \ ObjectClass *oc, \ - void *data) \ + const void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ MACHINE_VER_SYM(class_options, spapr, __VA_ARGS__)(mc); \ diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index faf9170ba6..b4b926d759 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -373,7 +373,7 @@ static const Property spapr_cpu_core_properties[] = { DEFINE_PROP_INT32("node-id", SpaprCpuCore, node_id, CPU_UNSET_NUMA_NODE_ID), }; -static void spapr_cpu_core_class_init(ObjectClass *oc, void *data) +static void spapr_cpu_core_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); SpaprCpuCoreClass *scc = SPAPR_CPU_CORE_CLASS(oc); diff --git a/hw/ppc/spapr_drc.c b/hw/ppc/spapr_drc.c index 549b652c20..d2044b4fb5 100644 --- a/hw/ppc/spapr_drc.c +++ b/hw/ppc/spapr_drc.c @@ -589,7 +589,7 @@ static void spapr_dr_connector_instance_init(Object *obj) drc->state = drck->empty_state; } -static void spapr_dr_connector_class_init(ObjectClass *k, void *data) +static void spapr_dr_connector_class_init(ObjectClass *k, const void *data) { DeviceClass *dk = DEVICE_CLASS(k); @@ -665,7 +665,7 @@ static void unrealize_physical(DeviceState *d) qemu_unregister_reset(drc_physical_reset, drcp); } -static void spapr_drc_physical_class_init(ObjectClass *k, void *data) +static void spapr_drc_physical_class_init(ObjectClass *k, const void *data) { DeviceClass *dk = DEVICE_CLASS(k); SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -679,7 +679,7 @@ static void spapr_drc_physical_class_init(ObjectClass *k, void *data) drck->empty_state = SPAPR_DRC_STATE_PHYSICAL_POWERON; } -static void spapr_drc_logical_class_init(ObjectClass *k, void *data) +static void spapr_drc_logical_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -690,7 +690,7 @@ static void spapr_drc_logical_class_init(ObjectClass *k, void *data) drck->empty_state = SPAPR_DRC_STATE_LOGICAL_UNUSABLE; } -static void spapr_drc_cpu_class_init(ObjectClass *k, void *data) +static void spapr_drc_cpu_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -701,7 +701,7 @@ static void spapr_drc_cpu_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_core_dt_populate; } -static void spapr_drc_pci_class_init(ObjectClass *k, void *data) +static void spapr_drc_pci_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -712,7 +712,7 @@ static void spapr_drc_pci_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_pci_dt_populate; } -static void spapr_drc_lmb_class_init(ObjectClass *k, void *data) +static void spapr_drc_lmb_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -723,7 +723,7 @@ static void spapr_drc_lmb_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_lmb_dt_populate; } -static void spapr_drc_phb_class_init(ObjectClass *k, void *data) +static void spapr_drc_phb_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); @@ -734,7 +734,7 @@ static void spapr_drc_phb_class_init(ObjectClass *k, void *data) drck->dt_populate = spapr_phb_dt_populate; } -static void spapr_drc_pmem_class_init(ObjectClass *k, void *data) +static void spapr_drc_pmem_class_init(ObjectClass *k, const void *data) { SpaprDrcClass *drck = SPAPR_DR_CONNECTOR_CLASS(k); diff --git a/hw/ppc/spapr_iommu.c b/hw/ppc/spapr_iommu.c index db3a14c1df..c2432a0c00 100644 --- a/hw/ppc/spapr_iommu.c +++ b/hw/ppc/spapr_iommu.c @@ -668,7 +668,7 @@ int spapr_tcet_dma_dt(void *fdt, int node_off, const char *propname, tcet->liobn, 0, tcet->nb_table << tcet->page_shift); } -static void spapr_tce_table_class_init(ObjectClass *klass, void *data) +static void spapr_tce_table_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = spapr_tce_table_realize; @@ -693,7 +693,8 @@ static const TypeInfo spapr_tce_table_info = { .class_init = spapr_tce_table_class_init, }; -static void spapr_iommu_memory_region_class_init(ObjectClass *klass, void *data) +static void spapr_iommu_memory_region_class_init(ObjectClass *klass, + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/ppc/spapr_nvdimm.c b/hw/ppc/spapr_nvdimm.c index 6e93ff9b33..72b4a6329f 100644 --- a/hw/ppc/spapr_nvdimm.c +++ b/hw/ppc/spapr_nvdimm.c @@ -888,7 +888,7 @@ static const Property spapr_nvdimm_properties[] = { }; #endif -static void spapr_nvdimm_class_init(ObjectClass *oc, void *data) +static void spapr_nvdimm_class_init(ObjectClass *oc, const void *data) { NVDIMMClass *nvc = NVDIMM_CLASS(oc); diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index 384269b831..d0468e3fe6 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -2173,7 +2173,7 @@ static const char *spapr_phb_root_bus_path(PCIHostState *host_bridge, return sphb->dtbusname; } -static void spapr_phb_class_init(ObjectClass *klass, void *data) +static void spapr_phb_class_init(ObjectClass *klass, const void *data) { PCIHostBridgeClass *hc = PCI_HOST_BRIDGE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ppc/spapr_rng.c b/hw/ppc/spapr_rng.c index 95def5b1e5..6fec607037 100644 --- a/hw/ppc/spapr_rng.c +++ b/hw/ppc/spapr_rng.c @@ -136,7 +136,7 @@ static const Property spapr_rng_properties[] = { RngBackend *), }; -static void spapr_rng_class_init(ObjectClass *oc, void *data) +static void spapr_rng_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/spapr_rtc.c b/hw/ppc/spapr_rtc.c index 46fbc78900..1f7d2d8f89 100644 --- a/hw/ppc/spapr_rtc.c +++ b/hw/ppc/spapr_rtc.c @@ -163,7 +163,7 @@ static const VMStateDescription vmstate_spapr_rtc = { }, }; -static void spapr_rtc_class_init(ObjectClass *oc, void *data) +static void spapr_rtc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ppc/spapr_tpm_proxy.c b/hw/ppc/spapr_tpm_proxy.c index ceaa0acaa1..862eeaa50a 100644 --- a/hw/ppc/spapr_tpm_proxy.c +++ b/hw/ppc/spapr_tpm_proxy.c @@ -149,7 +149,7 @@ static const Property spapr_tpm_proxy_properties[] = { DEFINE_PROP_STRING("host-path", SpaprTpmProxy, host_path), }; -static void spapr_tpm_proxy_class_init(ObjectClass *k, void *data) +static void spapr_tpm_proxy_class_init(ObjectClass *k, const void *data) { DeviceClass *dk = DEVICE_CLASS(k); diff --git a/hw/ppc/spapr_vio.c b/hw/ppc/spapr_vio.c index 09243c183b..7759436a4f 100644 --- a/hw/ppc/spapr_vio.c +++ b/hw/ppc/spapr_vio.c @@ -50,7 +50,7 @@ static char *spapr_vio_get_dev_name(DeviceState *qdev) return g_strdup_printf("%s@%x", pc->dt_name, dev->reg); } -static void spapr_vio_bus_class_init(ObjectClass *klass, void *data) +static void spapr_vio_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -599,7 +599,7 @@ SpaprVioBus *spapr_vio_bus_init(void) return bus; } -static void spapr_vio_bridge_class_init(ObjectClass *klass, void *data) +static void spapr_vio_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -631,7 +631,7 @@ const VMStateDescription vmstate_spapr_vio = { }, }; -static void vio_spapr_device_class_init(ObjectClass *klass, void *data) +static void vio_spapr_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->realize = spapr_vio_busdev_realize; diff --git a/hw/remote/machine.c b/hw/remote/machine.c index d4616025e8..9fb92ec6f1 100644 --- a/hw/remote/machine.c +++ b/hw/remote/machine.c @@ -121,7 +121,7 @@ static void remote_machine_dev_unplug_cb(HotplugHandler *hotplug_dev, } } -static void remote_machine_class_init(ObjectClass *oc, void *data) +static void remote_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c index 96d831a579..d2de48c9e3 100644 --- a/hw/remote/proxy.c +++ b/hw/remote/proxy.c @@ -195,7 +195,7 @@ static const Property proxy_properties[] = { DEFINE_PROP_STRING("fd", PCIProxyDev, fd), }; -static void pci_proxy_dev_class_init(ObjectClass *klass, void *data) +static void pci_proxy_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/remote/remote-obj.c b/hw/remote/remote-obj.c index 2f25f92dcd..75f8d6df8a 100644 --- a/hw/remote/remote-obj.c +++ b/hw/remote/remote-obj.c @@ -163,7 +163,7 @@ static void remote_object_finalize(Object *obj) g_free(o->devid); } -static void remote_object_class_init(ObjectClass *klass, void *data) +static void remote_object_class_init(ObjectClass *klass, const void *data) { RemoteObjectClass *k = REMOTE_OBJECT_CLASS(klass); diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index 9bdd0a465b..b0ae403f06 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -917,7 +917,7 @@ static void vfu_object_finalize(Object *obj) } } -static void vfu_object_class_init(ObjectClass *klass, void *data) +static void vfu_object_class_init(ObjectClass *klass, const void *data) { VfuObjectClass *k = VFU_OBJECT_CLASS(klass); diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 9c846f9b5b..e39ee657cd 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -479,7 +479,7 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) qspi_xip_mem); } -static void microchip_pfsoc_soc_class_init(ObjectClass *oc, void *data) +static void microchip_pfsoc_soc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -639,7 +639,8 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) } } -static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, void *data) +static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c index 019d6b3986..d369a8a7dc 100644 --- a/hw/riscv/opentitan.c +++ b/hw/riscv/opentitan.c @@ -112,7 +112,7 @@ static void opentitan_machine_init(MachineState *machine) } } -static void opentitan_machine_class_init(ObjectClass *oc, void *data) +static void opentitan_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -313,7 +313,7 @@ static const Property lowrisc_ibex_soc_props[] = { DEFINE_PROP_UINT32("resetvec", LowRISCIbexSoCState, resetvec, 0x20000400), }; -static void lowrisc_ibex_soc_class_init(ObjectClass *oc, void *data) +static void lowrisc_ibex_soc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/riscv/riscv-iommu-pci.c b/hw/riscv/riscv-iommu-pci.c index a795464803..d93cf7521b 100644 --- a/hw/riscv/riscv-iommu-pci.c +++ b/hw/riscv/riscv-iommu-pci.c @@ -184,7 +184,7 @@ static void riscv_iommu_pci_reset_hold(Object *obj, ResetType type) trace_riscv_iommu_pci_reset_hold(type); } -static void riscv_iommu_pci_class_init(ObjectClass *klass, void *data) +static void riscv_iommu_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/riscv/riscv-iommu-sys.c b/hw/riscv/riscv-iommu-sys.c index 65b24fb07d..be2e3944dc 100644 --- a/hw/riscv/riscv-iommu-sys.c +++ b/hw/riscv/riscv-iommu-sys.c @@ -227,7 +227,7 @@ static void riscv_iommu_sys_reset_hold(Object *obj, ResetType type) trace_riscv_iommu_sys_reset_hold(type); } -static void riscv_iommu_sys_class_init(ObjectClass *klass, void *data) +static void riscv_iommu_sys_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index 65411b3e4c..a877e5da84 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2513,7 +2513,7 @@ static const Property riscv_iommu_properties[] = { RISCV_IOMMU_IOCOUNT_NUM), }; -static void riscv_iommu_class_init(ObjectClass *klass, void* data) +static void riscv_iommu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -2654,7 +2654,7 @@ static int riscv_iommu_memory_region_index_len(IOMMUMemoryRegion *iommu_mr) return 1 << as->iommu->pid_bits; } -static void riscv_iommu_memory_region_init(ObjectClass *klass, void *data) +static void riscv_iommu_memory_region_init(ObjectClass *klass, const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index a55d156668..ac6539bd3e 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -160,7 +160,7 @@ static void riscv_harts_realize(DeviceState *dev, Error **errp) } } -static void riscv_harts_class_init(ObjectClass *klass, void *data) +static void riscv_harts_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/riscv/shakti_c.c b/hw/riscv/shakti_c.c index 17c5c72102..3e7f441172 100644 --- a/hw/riscv/shakti_c.c +++ b/hw/riscv/shakti_c.c @@ -71,7 +71,7 @@ static void shakti_c_machine_instance_init(Object *obj) { } -static void shakti_c_machine_class_init(ObjectClass *klass, void *data) +static void shakti_c_machine_class_init(ObjectClass *klass, const void *data) { MachineClass *mc = MACHINE_CLASS(klass); static const char * const valid_cpu_types[] = { @@ -142,7 +142,7 @@ static void shakti_c_soc_state_realize(DeviceState *dev, Error **errp) shakti_c_memmap[SHAKTI_C_ROM].base, &sss->rom); } -static void shakti_c_soc_class_init(ObjectClass *klass, void *data) +static void shakti_c_soc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = shakti_c_soc_state_realize; diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c index 73d3b74281..7baed1958e 100644 --- a/hw/riscv/sifive_e.c +++ b/hw/riscv/sifive_e.c @@ -143,7 +143,7 @@ static void sifive_e_machine_instance_init(Object *obj) s->revb = false; } -static void sifive_e_machine_class_init(ObjectClass *oc, void *data) +static void sifive_e_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -284,7 +284,7 @@ static void sifive_e_soc_realize(DeviceState *dev, Error **errp) &s->xip_mem); } -static void sifive_e_soc_class_init(ObjectClass *oc, void *data) +static void sifive_e_soc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c index 679f2024bc..d69f942cfb 100644 --- a/hw/riscv/sifive_u.c +++ b/hw/riscv/sifive_u.c @@ -713,7 +713,7 @@ static void sifive_u_machine_instance_init(Object *obj) object_property_set_description(obj, "serial", "Board serial number"); } -static void sifive_u_machine_class_init(ObjectClass *oc, void *data) +static void sifive_u_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -946,7 +946,7 @@ static const Property sifive_u_soc_props[] = { DEFINE_PROP_STRING("cpu-type", SiFiveUSoCState, cpu_type), }; -static void sifive_u_soc_class_init(ObjectClass *oc, void *data) +static void sifive_u_soc_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c index 74a20016f1..641aae8c01 100644 --- a/hw/riscv/spike.c +++ b/hw/riscv/spike.c @@ -342,7 +342,7 @@ static void spike_machine_instance_init(Object *obj) { } -static void spike_machine_class_init(ObjectClass *oc, void *data) +static void spike_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 85849e604c..557efd15a1 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1906,7 +1906,7 @@ static void virt_machine_device_plug_cb(HotplugHandler *hotplug_dev, } } -static void virt_machine_class_init(ObjectClass *oc, void *data) +static void virt_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); diff --git a/hw/rtc/allwinner-rtc.c b/hw/rtc/allwinner-rtc.c index fd8355a867..a747bff534 100644 --- a/hw/rtc/allwinner-rtc.c +++ b/hw/rtc/allwinner-rtc.c @@ -315,7 +315,7 @@ static const Property allwinner_rtc_properties[] = { DEFINE_PROP_INT32("base-year", AwRtcState, base_year, 0), }; -static void allwinner_rtc_class_init(ObjectClass *klass, void *data) +static void allwinner_rtc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -330,7 +330,7 @@ static void allwinner_rtc_sun4i_init(Object *obj) s->base_year = 2010; } -static void allwinner_rtc_sun4i_class_init(ObjectClass *klass, void *data) +static void allwinner_rtc_sun4i_class_init(ObjectClass *klass, const void *data) { AwRtcClass *arc = AW_RTC_CLASS(klass); @@ -346,7 +346,7 @@ static void allwinner_rtc_sun6i_init(Object *obj) s->base_year = 1970; } -static void allwinner_rtc_sun6i_class_init(ObjectClass *klass, void *data) +static void allwinner_rtc_sun6i_class_init(ObjectClass *klass, const void *data) { AwRtcClass *arc = AW_RTC_CLASS(klass); @@ -362,7 +362,7 @@ static void allwinner_rtc_sun7i_init(Object *obj) s->base_year = 1970; } -static void allwinner_rtc_sun7i_class_init(ObjectClass *klass, void *data) +static void allwinner_rtc_sun7i_class_init(ObjectClass *klass, const void *data) { AwRtcClass *arc = AW_RTC_CLASS(klass); allwinner_rtc_sun4i_class_init(klass, arc); diff --git a/hw/rtc/aspeed_rtc.c b/hw/rtc/aspeed_rtc.c index fbdeb0781f..c4feea23a0 100644 --- a/hw/rtc/aspeed_rtc.c +++ b/hw/rtc/aspeed_rtc.c @@ -156,7 +156,7 @@ static void aspeed_rtc_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static void aspeed_rtc_class_init(ObjectClass *klass, void *data) +static void aspeed_rtc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/rtc/ds1338.c b/hw/rtc/ds1338.c index 8dd17fdc07..5f1ee2e410 100644 --- a/hw/rtc/ds1338.c +++ b/hw/rtc/ds1338.c @@ -220,7 +220,7 @@ static void ds1338_reset(DeviceState *dev) s->addr_byte = false; } -static void ds1338_class_init(ObjectClass *klass, void *data) +static void ds1338_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/rtc/exynos4210_rtc.c b/hw/rtc/exynos4210_rtc.c index aa1b3cd115..624b4f61a8 100644 --- a/hw/rtc/exynos4210_rtc.c +++ b/hw/rtc/exynos4210_rtc.c @@ -592,7 +592,7 @@ static void exynos4210_rtc_finalize(Object *obj) ptimer_free(s->ptimer_1Hz); } -static void exynos4210_rtc_class_init(ObjectClass *klass, void *data) +static void exynos4210_rtc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/rtc/goldfish_rtc.c b/hw/rtc/goldfish_rtc.c index d83cc26481..78df031cf2 100644 --- a/hw/rtc/goldfish_rtc.c +++ b/hw/rtc/goldfish_rtc.c @@ -273,7 +273,7 @@ static const Property goldfish_rtc_properties[] = { false), }; -static void goldfish_rtc_class_init(ObjectClass *klass, void *data) +static void goldfish_rtc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/rtc/ls7a_rtc.c b/hw/rtc/ls7a_rtc.c index fce23a3dbe..10097b2db7 100644 --- a/hw/rtc/ls7a_rtc.c +++ b/hw/rtc/ls7a_rtc.c @@ -464,7 +464,7 @@ static const VMStateDescription vmstate_ls7a_rtc = { } }; -static void ls7a_rtc_class_init(ObjectClass *klass, void *data) +static void ls7a_rtc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->vmsd = &vmstate_ls7a_rtc; diff --git a/hw/rtc/m41t80.c b/hw/rtc/m41t80.c index 9600695679..c631ec3ea1 100644 --- a/hw/rtc/m41t80.c +++ b/hw/rtc/m41t80.c @@ -94,7 +94,7 @@ static int m41t80_event(I2CSlave *i2c, enum i2c_event event) return 0; } -static void m41t80_class_init(ObjectClass *klass, void *data) +static void m41t80_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass); diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c index 9c3855a3ef..4a7c0af9f0 100644 --- a/hw/rtc/m48t59-isa.c +++ b/hw/rtc/m48t59-isa.c @@ -113,7 +113,7 @@ static void m48t59_isa_realize(DeviceState *dev, Error **errp) } } -static void m48txx_isa_class_init(ObjectClass *klass, void *data) +static void m48txx_isa_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); NvramClass *nc = NVRAM_CLASS(klass); @@ -126,7 +126,7 @@ static void m48txx_isa_class_init(ObjectClass *klass, void *data) nc->toggle_lock = m48txx_isa_toggle_lock; } -static void m48txx_isa_concrete_class_init(ObjectClass *klass, void *data) +static void m48txx_isa_concrete_class_init(ObjectClass *klass, const void *data) { M48txxISADeviceClass *u = M48TXX_ISA_CLASS(klass); const M48txxInfo *info = data; diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index 3fb2f27d9d..821472a680 100644 --- a/hw/rtc/m48t59.c +++ b/hw/rtc/m48t59.c @@ -622,7 +622,7 @@ static const Property m48t59_sysbus_properties[] = { DEFINE_PROP_INT32("base-year", M48txxSysBusState, state.base_year, 0), }; -static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) +static void m48txx_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); NvramClass *nc = NVRAM_CLASS(klass); @@ -636,7 +636,8 @@ static void m48txx_sysbus_class_init(ObjectClass *klass, void *data) nc->toggle_lock = m48txx_sysbus_toggle_lock; } -static void m48txx_sysbus_concrete_class_init(ObjectClass *klass, void *data) +static void m48txx_sysbus_concrete_class_init(ObjectClass *klass, + const void *data) { M48txxSysBusDeviceClass *u = M48TXX_SYS_BUS_CLASS(klass); const M48txxInfo *info = data; diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index e322fc2ffb..93b632bdf4 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -1018,7 +1018,7 @@ static void rtc_build_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(scope, dev); } -static void rtc_class_initfn(ObjectClass *klass, void *data) +static void rtc_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/rtc/pl031.c b/hw/rtc/pl031.c index ed439bd3ad..e545b9dfd6 100644 --- a/hw/rtc/pl031.c +++ b/hw/rtc/pl031.c @@ -332,7 +332,7 @@ static const Property pl031_properties[] = { PL031State, migrate_tick_offset, true), }; -static void pl031_class_init(ObjectClass *klass, void *data) +static void pl031_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/rtc/rs5c372.c b/hw/rtc/rs5c372.c index 5542f74085..bb924534a7 100644 --- a/hw/rtc/rs5c372.c +++ b/hw/rtc/rs5c372.c @@ -210,7 +210,7 @@ static void rs5c372_init(Object *obj) qdev_prop_set_uint8(DEVICE(obj), "address", 0x32); } -static void rs5c372_class_init(ObjectClass *klass, void *data) +static void rs5c372_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/rtc/sun4v-rtc.c b/hw/rtc/sun4v-rtc.c index ffcc0aa25d..29e24ef6be 100644 --- a/hw/rtc/sun4v-rtc.c +++ b/hw/rtc/sun4v-rtc.c @@ -75,7 +75,7 @@ static void sun4v_rtc_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &s->iomem); } -static void sun4v_rtc_class_init(ObjectClass *klass, void *data) +static void sun4v_rtc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/rtc/xlnx-zynqmp-rtc.c b/hw/rtc/xlnx-zynqmp-rtc.c index b596b608c5..500982a801 100644 --- a/hw/rtc/xlnx-zynqmp-rtc.c +++ b/hw/rtc/xlnx-zynqmp-rtc.c @@ -251,7 +251,7 @@ static const VMStateDescription vmstate_rtc = { } }; -static void rtc_class_init(ObjectClass *klass, void *data) +static void rtc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/rx/rx-gdbsim.c b/hw/rx/rx-gdbsim.c index 4afd77efd5..5b9004e9e1 100644 --- a/hw/rx/rx-gdbsim.c +++ b/hw/rx/rx-gdbsim.c @@ -155,7 +155,7 @@ static void rx_gdbsim_init(MachineState *machine) } } -static void rx_gdbsim_class_init(ObjectClass *oc, void *data) +static void rx_gdbsim_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -165,7 +165,7 @@ static void rx_gdbsim_class_init(ObjectClass *oc, void *data) mc->default_ram_id = "ext-sdram"; } -static void rx62n7_class_init(ObjectClass *oc, void *data) +static void rx62n7_class_init(ObjectClass *oc, const void *data) { RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); @@ -175,7 +175,7 @@ static void rx62n7_class_init(ObjectClass *oc, void *data) mc->desc = "gdb simulator (R5F562N7 MCU and external RAM)"; }; -static void rx62n8_class_init(ObjectClass *oc, void *data) +static void rx62n8_class_init(ObjectClass *oc, const void *data) { RxGdbSimMachineClass *rxc = RX_GDBSIM_MACHINE_CLASS(oc); MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/rx/rx62n.c b/hw/rx/rx62n.c index e6bac4f053..a2a243afa4 100644 --- a/hw/rx/rx62n.c +++ b/hw/rx/rx62n.c @@ -264,7 +264,7 @@ static const Property rx62n_properties[] = { DEFINE_PROP_UINT32("xtal-frequency-hz", RX62NState, xtal_freq_hz, 0), }; -static void rx62n_class_init(ObjectClass *klass, void *data) +static void rx62n_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -272,7 +272,7 @@ static void rx62n_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, rx62n_properties); } -static void r5f562n7_class_init(ObjectClass *oc, void *data) +static void r5f562n7_class_init(ObjectClass *oc, const void *data) { RX62NClass *rxc = RX62N_MCU_CLASS(oc); @@ -281,7 +281,7 @@ static void r5f562n7_class_init(ObjectClass *oc, void *data) rxc->data_flash_size = 32 * KiB; }; -static void r5f562n8_class_init(ObjectClass *oc, void *data) +static void r5f562n8_class_init(ObjectClass *oc, const void *data) { RX62NClass *rxc = RX62N_MCU_CLASS(oc); diff --git a/hw/s390x/3270-ccw.c b/hw/s390x/3270-ccw.c index 3a8930dfd1..3f0d384fd8 100644 --- a/hw/s390x/3270-ccw.c +++ b/hw/s390x/3270-ccw.c @@ -150,7 +150,7 @@ static void emulated_ccw_3270_realize(DeviceState *ds, Error **errp) g_free(sch); } -static void emulated_ccw_3270_class_init(ObjectClass *klass, void *data) +static void emulated_ccw_3270_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/s390x/ap-bridge.c b/hw/s390x/ap-bridge.c index ef8fa2b15b..4aa7d5a90d 100644 --- a/hw/s390x/ap-bridge.c +++ b/hw/s390x/ap-bridge.c @@ -22,7 +22,7 @@ static char *ap_bus_get_dev_path(DeviceState *dev) return g_strdup_printf("/1"); } -static void ap_bus_class_init(ObjectClass *oc, void *data) +static void ap_bus_class_init(ObjectClass *oc, const void *data) { BusClass *k = BUS_CLASS(oc); @@ -61,7 +61,7 @@ void s390_init_ap(void) qbus_set_hotplug_handler(bus, OBJECT(dev)); } -static void ap_bridge_class_init(ObjectClass *oc, void *data) +static void ap_bridge_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(oc); diff --git a/hw/s390x/ap-device.c b/hw/s390x/ap-device.c index 237d1f19c5..733104403e 100644 --- a/hw/s390x/ap-device.c +++ b/hw/s390x/ap-device.c @@ -12,7 +12,7 @@ #include "qapi/error.h" #include "hw/s390x/ap-device.h" -static void ap_class_init(ObjectClass *klass, void *data) +static void ap_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 1ea9934f6c..19c2238f76 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -94,7 +94,7 @@ static void ccw_device_reset_hold(Object *obj, ResetType type) css_reset_sch(ccw_dev->sch); } -static void ccw_device_class_init(ObjectClass *klass, void *data) +static void ccw_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); CCWDeviceClass *k = CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index c48d5571b5..9d91e5a5fe 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -70,7 +70,7 @@ static char *virtual_css_bus_get_dev_path(DeviceState *dev) return g_strdup_printf("/%02x.%1x.%04x", sch->cssid, sch->ssid, sch->devno); } -static void virtual_css_bus_class_init(ObjectClass *klass, void *data) +static void virtual_css_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -117,7 +117,7 @@ static bool prop_get_true(Object *obj, Error **errp) return true; } -static void virtual_css_bridge_class_init(ObjectClass *klass, void *data) +static void virtual_css_bridge_class_init(ObjectClass *klass, const void *data) { HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index 2b0332c20e..1afe364573 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -460,7 +460,7 @@ static void reset_event_facility(DeviceState *dev) sdev->receive_mask = 0; } -static void init_event_facility_class(ObjectClass *klass, void *data) +static void init_event_facility_class(ObjectClass *klass, const void *data) { SysBusDeviceClass *sbdc = SYS_BUS_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(sbdc); @@ -497,7 +497,7 @@ static void event_realize(DeviceState *qdev, Error **errp) } } -static void event_class_init(ObjectClass *klass, void *data) +static void event_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index ce6f6078d7..716a6b7869 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -736,7 +736,7 @@ static void s390_ipl_reset(DeviceState *dev) } } -static void s390_ipl_class_init(ObjectClass *klass, void *data) +static void s390_ipl_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/s390x/s390-ccw.c b/hw/s390x/s390-ccw.c index 909475f048..10c81a4c89 100644 --- a/hw/s390x/s390-ccw.c +++ b/hw/s390x/s390-ccw.c @@ -175,7 +175,7 @@ static void s390_ccw_instance_init(Object *obj) "/disk@0,0", DEVICE(obj)); } -static void s390_ccw_class_init(ObjectClass *klass, void *data) +static void s390_ccw_class_init(ObjectClass *klass, const void *data) { S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 501330bfa7..838b7e6484 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -1373,7 +1373,7 @@ static void s390_pcihost_reset(DeviceState *dev) pci_for_each_device_under_bus(bus, s390_pci_enumerate_bridge, s); } -static void s390_pcihost_class_init(ObjectClass *klass, void *data) +static void s390_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); @@ -1557,7 +1557,7 @@ static const VMStateDescription s390_pci_device_vmstate = { .unmigratable = 1, }; -static void s390_pci_device_class_init(ObjectClass *klass, void *data) +static void s390_pci_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1583,7 +1583,8 @@ static const TypeInfo s390_pci_iommu_info = { .instance_size = sizeof(S390PCIIOMMU), }; -static void s390_iommu_memory_region_class_init(ObjectClass *klass, void *data) +static void s390_iommu_memory_region_class_init(ObjectClass *klass, + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/s390x/s390-skeys-kvm.c b/hw/s390x/s390-skeys-kvm.c index 0215e94388..f3056d6d25 100644 --- a/hw/s390x/s390-skeys-kvm.c +++ b/hw/s390x/s390-skeys-kvm.c @@ -52,7 +52,7 @@ static int kvm_s390_skeys_set(S390SKeysState *ss, uint64_t start_gfn, return kvm_vm_ioctl(kvm_state, KVM_S390_SET_SKEYS, &args); } -static void kvm_s390_skeys_class_init(ObjectClass *oc, void *data) +static void kvm_s390_skeys_class_init(ObjectClass *oc, const void *data) { S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index d437fe070d..aedb62b2d3 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -304,7 +304,7 @@ static int qemu_s390_skeys_get(S390SKeysState *ss, uint64_t start_gfn, return 0; } -static void qemu_s390_skeys_class_init(ObjectClass *oc, void *data) +static void qemu_s390_skeys_class_init(ObjectClass *oc, const void *data) { S390SKeysClass *skeyclass = S390_SKEYS_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -466,7 +466,7 @@ static void s390_skeys_realize(DeviceState *dev, Error **errp) register_savevm_live(TYPE_S390_SKEYS, 0, 1, &savevm_s390_storage_keys, ss); } -static void s390_skeys_class_init(ObjectClass *oc, void *data) +static void s390_skeys_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/s390x/s390-stattrib-kvm.c b/hw/s390x/s390-stattrib-kvm.c index ebcd56368f..e1fee361dc 100644 --- a/hw/s390x/s390-stattrib-kvm.c +++ b/hw/s390x/s390-stattrib-kvm.c @@ -188,7 +188,7 @@ static int kvm_s390_stattrib_get_active(S390StAttribState *sa) return kvm_s390_cmma_active(); } -static void kvm_s390_stattrib_class_init(ObjectClass *oc, void *data) +static void kvm_s390_stattrib_class_init(ObjectClass *oc, const void *data) { S390StAttribClass *sac = S390_STATTRIB_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index a86002be6a..f74cf32636 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -307,7 +307,7 @@ static int qemu_s390_get_active(S390StAttribState *sa) return true; } -static void qemu_s390_stattrib_class_init(ObjectClass *oc, void *data) +static void qemu_s390_stattrib_class_init(ObjectClass *oc, const void *data) { S390StAttribClass *sa_cl = S390_STATTRIB_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -360,7 +360,7 @@ static void s390_stattrib_realize(DeviceState *dev, Error **errp) &savevm_s390_stattrib_handlers, dev); } -static void s390_stattrib_class_init(ObjectClass *oc, void *data) +static void s390_stattrib_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index cf5b9974c5..00e9e46aef 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -804,7 +804,7 @@ static void machine_set_loadparm(Object *obj, Visitor *v, s390_ipl_fmt_loadparm(ms->loadparm, val, errp); } -static void ccw_machine_class_init(ObjectClass *oc, void *data) +static void ccw_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); NMIClass *nc = NMI_CLASS(oc); @@ -892,7 +892,7 @@ static const TypeInfo ccw_machine_info = { } \ static void MACHINE_VER_SYM(class_init, ccw, __VA_ARGS__)( \ ObjectClass *oc, \ - void *data) \ + const void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ MACHINE_VER_SYM(class_options, ccw, __VA_ARGS__)(mc); \ diff --git a/hw/s390x/sclp.c b/hw/s390x/sclp.c index 5945c9b1d8..9718564fa4 100644 --- a/hw/s390x/sclp.c +++ b/hw/s390x/sclp.c @@ -424,7 +424,7 @@ static void sclp_init(Object *obj) sclp_memory_init(sclp); } -static void sclp_class_init(ObjectClass *oc, void *data) +static void sclp_class_init(ObjectClass *oc, const void *data) { SCLPDeviceClass *sc = SCLP_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/s390x/sclpcpu.c b/hw/s390x/sclpcpu.c index a178a9dd4c..4b6ebfed46 100644 --- a/hw/s390x/sclpcpu.c +++ b/hw/s390x/sclpcpu.c @@ -73,7 +73,7 @@ static int read_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr, return 1; } -static void sclp_cpu_class_init(ObjectClass *oc, void *data) +static void sclp_cpu_class_init(ObjectClass *oc, const void *data) { SCLPEventClass *k = SCLP_EVENT_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/s390x/sclpquiesce.c b/hw/s390x/sclpquiesce.c index 7bb5aad520..da4c8f3e36 100644 --- a/hw/s390x/sclpquiesce.c +++ b/hw/s390x/sclpquiesce.c @@ -112,7 +112,7 @@ static void quiesce_reset(DeviceState *dev) event->event_pending = false; } -static void quiesce_class_init(ObjectClass *klass, void *data) +static void quiesce_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCLPEventClass *k = SCLP_EVENT_CLASS(klass); diff --git a/hw/s390x/tod-kvm.c b/hw/s390x/tod-kvm.c index 5da9037e0c..c9b8896b63 100644 --- a/hw/s390x/tod-kvm.c +++ b/hw/s390x/tod-kvm.c @@ -133,7 +133,7 @@ static void kvm_s390_tod_realize(DeviceState *dev, Error **errp) qemu_add_vm_change_state_handler(kvm_s390_tod_vm_state_change, td); } -static void kvm_s390_tod_class_init(ObjectClass *oc, void *data) +static void kvm_s390_tod_class_init(ObjectClass *oc, const void *data) { S390TODClass *tdc = S390_TOD_CLASS(oc); diff --git a/hw/s390x/tod-tcg.c b/hw/s390x/tod-tcg.c index 3b3ef8843e..0cc96624e1 100644 --- a/hw/s390x/tod-tcg.c +++ b/hw/s390x/tod-tcg.c @@ -52,7 +52,7 @@ static void qemu_s390_tod_set(S390TODState *td, const S390TOD *tod, } } -static void qemu_s390_tod_class_init(ObjectClass *oc, void *data) +static void qemu_s390_tod_class_init(ObjectClass *oc, const void *data) { S390TODClass *tdc = S390_TOD_CLASS(oc); diff --git a/hw/s390x/tod.c b/hw/s390x/tod.c index 6afbb23fc7..3f913cc88a 100644 --- a/hw/s390x/tod.c +++ b/hw/s390x/tod.c @@ -111,7 +111,7 @@ static void s390_tod_realize(DeviceState *dev, Error **errp) register_savevm_live("todclock", 0, 1, &savevm_tod, td); } -static void s390_tod_class_init(ObjectClass *oc, void *data) +static void s390_tod_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/s390x/vhost-scsi-ccw.c b/hw/s390x/vhost-scsi-ccw.c index e6bf0c55bc..8341b23a95 100644 --- a/hw/s390x/vhost-scsi-ccw.c +++ b/hw/s390x/vhost-scsi-ccw.c @@ -46,7 +46,7 @@ static const Property vhost_ccw_scsi_properties[] = { VIRTIO_CCW_MAX_REV), }; -static void vhost_ccw_scsi_class_init(ObjectClass *klass, void *data) +static void vhost_ccw_scsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/vhost-user-fs-ccw.c b/hw/s390x/vhost-user-fs-ccw.c index 6a9654d77b..cc1b8227fc 100644 --- a/hw/s390x/vhost-user-fs-ccw.c +++ b/hw/s390x/vhost-user-fs-ccw.c @@ -48,7 +48,7 @@ static void vhost_user_fs_ccw_instance_init(Object *obj) TYPE_VHOST_USER_FS); } -static void vhost_user_fs_ccw_class_init(ObjectClass *klass, void *data) +static void vhost_user_fs_ccw_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/vhost-vsock-ccw.c b/hw/s390x/vhost-vsock-ccw.c index 875ccf3485..552e9e86a4 100644 --- a/hw/s390x/vhost-vsock-ccw.c +++ b/hw/s390x/vhost-vsock-ccw.c @@ -35,7 +35,7 @@ static void vhost_vsock_ccw_realize(VirtioCcwDevice *ccw_dev, Error **errp) qdev_realize(vdev, BUS(&ccw_dev->bus), errp); } -static void vhost_vsock_ccw_class_init(ObjectClass *klass, void *data) +static void vhost_vsock_ccw_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-9p.c b/hw/s390x/virtio-ccw-9p.c index 287ae2ba76..72bf6ec80c 100644 --- a/hw/s390x/virtio-ccw-9p.c +++ b/hw/s390x/virtio-ccw-9p.c @@ -48,7 +48,7 @@ static const Property virtio_ccw_9p_properties[] = { VIRTIO_CCW_MAX_REV), }; -static void virtio_ccw_9p_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_9p_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-balloon.c b/hw/s390x/virtio-ccw-balloon.c index 1180efaf6d..399b40f366 100644 --- a/hw/s390x/virtio-ccw-balloon.c +++ b/hw/s390x/virtio-ccw-balloon.c @@ -53,7 +53,7 @@ static const Property virtio_ccw_balloon_properties[] = { VIRTIO_CCW_MAX_REV), }; -static void virtio_ccw_balloon_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_balloon_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-blk.c b/hw/s390x/virtio-ccw-blk.c index db9d442ffb..7d8c4a75ce 100644 --- a/hw/s390x/virtio-ccw-blk.c +++ b/hw/s390x/virtio-ccw-blk.c @@ -51,7 +51,7 @@ static const Property virtio_ccw_blk_properties[] = { DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), }; -static void virtio_ccw_blk_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_blk_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-crypto.c b/hw/s390x/virtio-ccw-crypto.c index b693f87c70..75e714603b 100644 --- a/hw/s390x/virtio-ccw-crypto.c +++ b/hw/s390x/virtio-ccw-crypto.c @@ -51,7 +51,7 @@ static const Property virtio_ccw_crypto_properties[] = { VIRTIO_CCW_MAX_REV), }; -static void virtio_ccw_crypto_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_crypto_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-gpu.c b/hw/s390x/virtio-ccw-gpu.c index a6b14c25d9..edb6a47d37 100644 --- a/hw/s390x/virtio-ccw-gpu.c +++ b/hw/s390x/virtio-ccw-gpu.c @@ -49,7 +49,7 @@ static const Property virtio_ccw_gpu_properties[] = { VIRTIO_CCW_MAX_REV), }; -static void virtio_ccw_gpu_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_gpu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-input.c b/hw/s390x/virtio-ccw-input.c index 6ca10d58ee..2250d8cf98 100644 --- a/hw/s390x/virtio-ccw-input.c +++ b/hw/s390x/virtio-ccw-input.c @@ -50,7 +50,7 @@ static const Property virtio_ccw_input_properties[] = { VIRTIO_CCW_MAX_REV), }; -static void virtio_ccw_input_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_input_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-mem.c b/hw/s390x/virtio-ccw-mem.c index 90fd89f015..daa485d189 100644 --- a/hw/s390x/virtio-ccw-mem.c +++ b/hw/s390x/virtio-ccw-mem.c @@ -160,7 +160,7 @@ static const Property virtio_ccw_mem_properties[] = { VIRTIO_CCW_MAX_REV), }; -static void virtio_ccw_mem_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_mem_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-net.c b/hw/s390x/virtio-ccw-net.c index 80a5581baf..a7d4afbeb9 100644 --- a/hw/s390x/virtio-ccw-net.c +++ b/hw/s390x/virtio-ccw-net.c @@ -54,7 +54,7 @@ static const Property virtio_ccw_net_properties[] = { DEFINE_PROP_CCW_LOADPARM("loadparm", CcwDevice, loadparm), }; -static void virtio_ccw_net_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_net_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-rng.c b/hw/s390x/virtio-ccw-rng.c index ccd124ee91..3263287d45 100644 --- a/hw/s390x/virtio-ccw-rng.c +++ b/hw/s390x/virtio-ccw-rng.c @@ -50,7 +50,7 @@ static const Property virtio_ccw_rng_properties[] = { VIRTIO_CCW_MAX_REV), }; -static void virtio_ccw_rng_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_rng_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-scsi.c b/hw/s390x/virtio-ccw-scsi.c index bfcea3cfe7..06b4c6c4a5 100644 --- a/hw/s390x/virtio-ccw-scsi.c +++ b/hw/s390x/virtio-ccw-scsi.c @@ -60,7 +60,7 @@ static const Property virtio_ccw_scsi_properties[] = { VIRTIO_CCW_MAX_REV), }; -static void virtio_ccw_scsi_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_scsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw-serial.c b/hw/s390x/virtio-ccw-serial.c index 59743d1e25..0dac590c08 100644 --- a/hw/s390x/virtio-ccw-serial.c +++ b/hw/s390x/virtio-ccw-serial.c @@ -60,7 +60,7 @@ static const Property virtio_ccw_serial_properties[] = { VIRTIO_CCW_MAX_REV), }; -static void virtio_ccw_serial_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_serial_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtIOCCWDeviceClass *k = VIRTIO_CCW_DEVICE_CLASS(klass); diff --git a/hw/s390x/virtio-ccw.c b/hw/s390x/virtio-ccw.c index e8ecb90826..d2f85b39f3 100644 --- a/hw/s390x/virtio-ccw.c +++ b/hw/s390x/virtio-ccw.c @@ -1228,7 +1228,7 @@ static void virtio_ccw_busdev_unplug(HotplugHandler *hotplug_dev, virtio_ccw_stop_ioeventfd(_dev); } -static void virtio_ccw_device_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); CCWDeviceClass *k = CCW_DEVICE_CLASS(dc); @@ -1262,7 +1262,7 @@ static void virtio_ccw_bus_new(VirtioBusState *bus, size_t bus_size, qbus_init(bus, bus_size, TYPE_VIRTIO_CCW_BUS, qdev, virtio_bus_name); } -static void virtio_ccw_bus_class_init(ObjectClass *klass, void *data) +static void virtio_ccw_bus_class_init(ObjectClass *klass, const void *data) { VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); BusClass *bus_class = BUS_CLASS(klass); diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c index fe4e045a6f..74e9af0b5d 100644 --- a/hw/scsi/esp-pci.c +++ b/hw/scsi/esp-pci.c @@ -427,7 +427,7 @@ static void esp_pci_init(Object *obj) object_initialize_child(obj, "esp", &pci->esp, TYPE_ESP); } -static void esp_pci_class_init(ObjectClass *klass, void *data) +static void esp_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -557,7 +557,7 @@ static void dc390_scsi_realize(PCIDevice *dev, Error **errp) contents[EE_CHKSUM2] = chksum >> 8; } -static void dc390_class_init(ObjectClass *klass, void *data) +static void dc390_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 01bdfe2701..f24991fd16 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -1568,7 +1568,7 @@ static const VMStateDescription vmstate_sysbus_esp_scsi = { } }; -static void sysbus_esp_class_init(ObjectClass *klass, void *data) +static void sysbus_esp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1594,7 +1594,7 @@ static void esp_init(Object *obj) fifo8_create(&s->cmdfifo, ESP_CMDFIFO_SZ); } -static void esp_class_init(ObjectClass *klass, void *data) +static void esp_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 6689ebba25..0ad61565bf 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -2375,7 +2375,7 @@ static void lsi_scsi_exit(PCIDevice *dev) timer_free(s->scripts_timer); } -static void lsi_class_init(ObjectClass *klass, void *data) +static void lsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -2402,7 +2402,7 @@ static const TypeInfo lsi_info = { }, }; -static void lsi53c810_class_init(ObjectClass *klass, void *data) +static void lsi53c810_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index d56bfc711d..ffcabd5a8e 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2527,7 +2527,7 @@ static struct MegasasInfo megasas_devices[] = { } }; -static void megasas_class_init(ObjectClass *oc, void *data) +static void megasas_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index ba7a7d0770..17f73ce381 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -1416,7 +1416,7 @@ static const Property mptsas_properties[] = { DEFINE_PROP_ON_OFF_AUTO("msi", MPTSASState, msi, ON_OFF_AUTO_AUTO), }; -static void mptsas1068_class_init(ObjectClass *oc, void *data) +static void mptsas1068_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index ece1107ee8..0456b4de0a 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -2001,7 +2001,7 @@ static const Property scsi_props[] = { DEFINE_PROP_UINT32("lun", SCSIDevice, lun, -1), }; -static void scsi_device_class_init(ObjectClass *klass, void *data) +static void scsi_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_STORAGE, k->categories); @@ -2030,7 +2030,7 @@ static const TypeInfo scsi_device_type_info = { .instance_init = scsi_dev_instance_init, }; -static void scsi_bus_class_init(ObjectClass *klass, void *data) +static void scsi_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index e59632e9b1..cb4af1b715 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -3177,7 +3177,7 @@ static void scsi_property_add_specifics(DeviceClass *dc) } } -static void scsi_disk_base_class_initfn(ObjectClass *klass, void *data) +static void scsi_disk_base_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCSIDiskClass *sdc = SCSI_DISK_BASE_CLASS(klass); @@ -3247,7 +3247,7 @@ static const VMStateDescription vmstate_scsi_disk_state = { } }; -static void scsi_hd_class_initfn(ObjectClass *klass, void *data) +static void scsi_hd_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); @@ -3289,7 +3289,7 @@ static const Property scsi_cd_properties[] = { SCSI_DISK_QUIRK_MODE_PAGE_TRUNCATED, 0), }; -static void scsi_cd_class_initfn(ObjectClass *klass, void *data) +static void scsi_cd_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); @@ -3326,7 +3326,7 @@ static const Property scsi_block_properties[] = { DEFAULT_IO_TIMEOUT), }; -static void scsi_block_class_initfn(ObjectClass *klass, void *data) +static void scsi_block_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); diff --git a/hw/scsi/scsi-generic.c b/hw/scsi/scsi-generic.c index 6566720064..9e380a2109 100644 --- a/hw/scsi/scsi-generic.c +++ b/hw/scsi/scsi-generic.c @@ -786,7 +786,7 @@ static int scsi_generic_parse_cdb(SCSIDevice *dev, SCSICommand *cmd, return scsi_bus_parse_cdb(dev, cmd, buf, buf_len, hba_private); } -static void scsi_generic_class_initfn(ObjectClass *klass, void *data) +static void scsi_generic_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCSIDeviceClass *sc = SCSI_DEVICE_CLASS(klass); diff --git a/hw/scsi/spapr_vscsi.c b/hw/scsi/spapr_vscsi.c index 6962194eaa..20f70fb272 100644 --- a/hw/scsi/spapr_vscsi.c +++ b/hw/scsi/spapr_vscsi.c @@ -1267,7 +1267,7 @@ static const VMStateDescription vmstate_spapr_vscsi = { }, }; -static void spapr_vscsi_class_init(ObjectClass *klass, void *data) +static void spapr_vscsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 66e0c21c22..be5a416c1d 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -363,7 +363,7 @@ static const Property vhost_scsi_properties[] = { conf.worker_per_virtqueue, false), }; -static void vhost_scsi_class_init(ObjectClass *klass, void *data) +static void vhost_scsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index adb41b9816..807a58ecf4 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -386,7 +386,7 @@ static const VMStateDescription vmstate_vhost_scsi = { }, }; -static void vhost_user_scsi_class_init(ObjectClass *klass, void *data) +static void vhost_user_scsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index f5a3aa2366..ae67a7682a 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -1395,7 +1395,7 @@ static const VMStateDescription vmstate_virtio_scsi = { }, }; -static void virtio_scsi_common_class_init(ObjectClass *klass, void *data) +static void virtio_scsi_common_class_init(ObjectClass *klass, const void *data) { VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -1404,7 +1404,7 @@ static void virtio_scsi_common_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); } -static void virtio_scsi_class_init(ObjectClass *klass, void *data) +static void virtio_scsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index f07e377cb8..a8ea57ffad 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -1317,7 +1317,7 @@ static void pvscsi_realize(DeviceState *qdev, Error **errp) pvs_c->parent_dc_realize(qdev, errp); } -static void pvscsi_class_init(ObjectClass *klass, void *data) +static void pvscsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index 03980d2716..b31da5c399 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -888,14 +888,15 @@ static void allwinner_sdhost_reset(DeviceState *dev) } } -static void allwinner_sdhost_bus_class_init(ObjectClass *klass, void *data) +static void allwinner_sdhost_bus_class_init(ObjectClass *klass, + const void *data) { SDBusClass *sbc = SD_BUS_CLASS(klass); sbc->set_inserted = allwinner_sdhost_set_inserted; } -static void allwinner_sdhost_class_init(ObjectClass *klass, void *data) +static void allwinner_sdhost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -905,7 +906,8 @@ static void allwinner_sdhost_class_init(ObjectClass *klass, void *data) device_class_set_props(dc, allwinner_sdhost_properties); } -static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data) +static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, + const void *data) { AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 8 * KiB; @@ -913,7 +915,8 @@ static void allwinner_sdhost_sun4i_class_init(ObjectClass *klass, void *data) sc->can_calibrate = false; } -static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data) +static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, + const void *data) { AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 64 * KiB; @@ -922,7 +925,7 @@ static void allwinner_sdhost_sun5i_class_init(ObjectClass *klass, void *data) } static void allwinner_sdhost_sun50i_a64_class_init(ObjectClass *klass, - void *data) + const void *data) { AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 64 * KiB; @@ -931,7 +934,7 @@ static void allwinner_sdhost_sun50i_a64_class_init(ObjectClass *klass, } static void allwinner_sdhost_sun50i_a64_emmc_class_init(ObjectClass *klass, - void *data) + const void *data) { AwSdHostClass *sc = AW_SDHOST_CLASS(klass); sc->max_desc_size = 8 * KiB; diff --git a/hw/sd/aspeed_sdhci.c b/hw/sd/aspeed_sdhci.c index 12cbbae5e7..fc38ad39ce 100644 --- a/hw/sd/aspeed_sdhci.c +++ b/hw/sd/aspeed_sdhci.c @@ -208,7 +208,7 @@ static const Property aspeed_sdhci_properties[] = { DEFINE_PROP_UINT8("num-slots", AspeedSDHCIState, num_slots, 0), }; -static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) +static void aspeed_sdhci_class_init(ObjectClass *classp, const void *data) { DeviceClass *dc = DEVICE_CLASS(classp); @@ -218,7 +218,7 @@ static void aspeed_sdhci_class_init(ObjectClass *classp, void *data) device_class_set_props(dc, aspeed_sdhci_properties); } -static void aspeed_2400_sdhci_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_sdhci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass); @@ -227,7 +227,7 @@ static void aspeed_2400_sdhci_class_init(ObjectClass *klass, void *data) asc->capareg = 0x0000000001e80080; } -static void aspeed_2500_sdhci_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_sdhci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass); @@ -236,7 +236,7 @@ static void aspeed_2500_sdhci_class_init(ObjectClass *klass, void *data) asc->capareg = 0x0000000001e80080; } -static void aspeed_2600_sdhci_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_sdhci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass); @@ -245,7 +245,7 @@ static void aspeed_2600_sdhci_class_init(ObjectClass *klass, void *data) asc->capareg = 0x0000000701f80080; } -static void aspeed_2700_sdhci_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_sdhci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSDHCIClass *asc = ASPEED_SDHCI_CLASS(klass); diff --git a/hw/sd/bcm2835_sdhost.c b/hw/sd/bcm2835_sdhost.c index 0724949d0c..29debdf59e 100644 --- a/hw/sd/bcm2835_sdhost.c +++ b/hw/sd/bcm2835_sdhost.c @@ -428,7 +428,7 @@ static void bcm2835_sdhost_reset(DeviceState *dev) s->fifo_len = 0; } -static void bcm2835_sdhost_class_init(ObjectClass *klass, void *data) +static void bcm2835_sdhost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/sd/cadence_sdhci.c b/hw/sd/cadence_sdhci.c index ad9daa20ed..d576855a1a 100644 --- a/hw/sd/cadence_sdhci.c +++ b/hw/sd/cadence_sdhci.c @@ -165,7 +165,7 @@ static const VMStateDescription vmstate_cadence_sdhci = { }, }; -static void cadence_sdhci_class_init(ObjectClass *classp, void *data) +static void cadence_sdhci_class_init(ObjectClass *classp, const void *data) { DeviceClass *dc = DEVICE_CLASS(classp); diff --git a/hw/sd/npcm7xx_sdhci.c b/hw/sd/npcm7xx_sdhci.c index 99028c1a2c..0233d7bd4d 100644 --- a/hw/sd/npcm7xx_sdhci.c +++ b/hw/sd/npcm7xx_sdhci.c @@ -149,7 +149,7 @@ static const VMStateDescription vmstate_npcm7xx_sdhci = { }, }; -static void npcm7xx_sdhci_class_init(ObjectClass *classp, void *data) +static void npcm7xx_sdhci_class_init(ObjectClass *classp, const void *data) { DeviceClass *dc = DEVICE_CLASS(classp); diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index bbe7b971bb..b7648d41cc 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -612,7 +612,7 @@ static void omap_mmc_initfn(Object *obj) qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_SD_BUS, DEVICE(obj), "sd-bus"); } -static void omap_mmc_class_init(ObjectClass *oc, void *data) +static void omap_mmc_class_init(ObjectClass *oc, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(oc); diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index 03d2ae7d21..b8fc9f86f1 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -509,7 +509,7 @@ static void pl181_init(Object *obj) qbus_init(&s->sdbus, sizeof(s->sdbus), TYPE_PL181_BUS, dev, "sd-bus"); } -static void pl181_class_init(ObjectClass *klass, void *data) +static void pl181_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); @@ -519,7 +519,7 @@ static void pl181_class_init(ObjectClass *klass, void *data) k->user_creatable = false; } -static void pl181_bus_class_init(ObjectClass *klass, void *data) +static void pl181_bus_class_init(ObjectClass *klass, const void *data) { SDBusClass *sbc = SD_BUS_CLASS(klass); diff --git a/hw/sd/sd.c b/hw/sd/sd.c index e541c57f8c..c275fdda2d 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2751,7 +2751,7 @@ static const Property emmc_properties[] = { DEFINE_PROP_UINT8("boot-config", SDState, boot_config, 0x0), }; -static void sdmmc_common_class_init(ObjectClass *klass, void *data) +static void sdmmc_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SDCardClass *sc = SDMMC_COMMON_CLASS(klass); @@ -2774,7 +2774,7 @@ static void sdmmc_common_class_init(ObjectClass *klass, void *data) sc->get_readonly = sd_get_readonly; } -static void sd_class_init(ObjectClass *klass, void *data) +static void sd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SDCardClass *sc = SDMMC_COMMON_CLASS(klass); @@ -2793,7 +2793,7 @@ static void sd_class_init(ObjectClass *klass, void *data) * board to ensure that ssi transfers only occur when the chip select * is asserted. */ -static void sd_spi_class_init(ObjectClass *klass, void *data) +static void sd_spi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SDCardClass *sc = SDMMC_COMMON_CLASS(klass); @@ -2802,7 +2802,7 @@ static void sd_spi_class_init(ObjectClass *klass, void *data) sc->proto = &sd_proto_spi; } -static void emmc_class_init(ObjectClass *klass, void *data) +static void emmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SDCardClass *sc = SDMMC_COMMON_CLASS(klass); diff --git a/hw/sd/sdhci-pci.c b/hw/sd/sdhci-pci.c index bca149e811..2a56fbf2cd 100644 --- a/hw/sd/sdhci-pci.c +++ b/hw/sd/sdhci-pci.c @@ -54,7 +54,7 @@ static void sdhci_pci_exit(PCIDevice *dev) sdhci_uninitfn(s); } -static void sdhci_pci_class_init(ObjectClass *klass, void *data) +static void sdhci_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 69baf73ae9..226ff133ff 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -1620,7 +1620,7 @@ static void sdhci_sysbus_unrealize(DeviceState *dev) } } -static void sdhci_sysbus_class_init(ObjectClass *klass, void *data) +static void sdhci_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1633,7 +1633,7 @@ static void sdhci_sysbus_class_init(ObjectClass *klass, void *data) /* --- qdev bus master --- */ -static void sdhci_bus_class_init(ObjectClass *klass, void *data) +static void sdhci_bus_class_init(ObjectClass *klass, const void *data) { SDBusClass *sbc = SD_BUS_CLASS(klass); diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index c4a58da0ab..6c90a86ab4 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -389,7 +389,7 @@ static void ssi_sd_reset(DeviceState *dev) s->stopping = 0; } -static void ssi_sd_class_init(ObjectClass *klass, void *data) +static void ssi_sd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SSIPeripheralClass *k = SSI_PERIPHERAL_CLASS(klass); diff --git a/hw/sensor/adm1266.c b/hw/sensor/adm1266.c index 25b87a7296..9017ce6116 100644 --- a/hw/sensor/adm1266.c +++ b/hw/sensor/adm1266.c @@ -223,7 +223,7 @@ static void adm1266_init(Object *obj) } } -static void adm1266_class_init(ObjectClass *klass, void *data) +static void adm1266_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/sensor/adm1272.c b/hw/sensor/adm1272.c index 3fc1e5d0ad..0c739aa0d8 100644 --- a/hw/sensor/adm1272.c +++ b/hw/sensor/adm1272.c @@ -511,7 +511,7 @@ static void adm1272_init(Object *obj) } -static void adm1272_class_init(ObjectClass *klass, void *data) +static void adm1272_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/sensor/dps310.c b/hw/sensor/dps310.c index 6966a53248..bcf615423a 100644 --- a/hw/sensor/dps310.c +++ b/hw/sensor/dps310.c @@ -197,7 +197,7 @@ static const VMStateDescription vmstate_dps310 = { } }; -static void dps310_class_init(ObjectClass *klass, void *data) +static void dps310_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/sensor/emc141x.c b/hw/sensor/emc141x.c index 33c1bd330f..7b2ce383a1 100644 --- a/hw/sensor/emc141x.c +++ b/hw/sensor/emc141x.c @@ -277,7 +277,7 @@ static void emc141x_class_init(ObjectClass *klass, const void *data) dc->vmsd = &vmstate_emc141x; } -static void emc1413_class_init(ObjectClass *klass, void *data) +static void emc1413_class_init(ObjectClass *klass, const void *data) { EMC141XClass *ec = EMC141X_CLASS(klass); @@ -286,7 +286,7 @@ static void emc1413_class_init(ObjectClass *klass, void *data) ec->sensors_count = 3; } -static void emc1414_class_init(ObjectClass *klass, void *data) +static void emc1414_class_init(ObjectClass *klass, const void *data) { EMC141XClass *ec = EMC141X_CLASS(klass); diff --git a/hw/sensor/isl_pmbus_vr.c b/hw/sensor/isl_pmbus_vr.c index c60282cfe7..e8d29b08ff 100644 --- a/hw/sensor/isl_pmbus_vr.c +++ b/hw/sensor/isl_pmbus_vr.c @@ -242,7 +242,7 @@ static void isl_pmbus_vr_class_init(ObjectClass *klass, const void *data, k->device_num_pages = pages; } -static void isl69260_class_init(ObjectClass *klass, void *data) +static void isl69260_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -251,7 +251,7 @@ static void isl69260_class_init(ObjectClass *klass, void *data) isl_pmbus_vr_class_init(klass, data, 2); } -static void raa228000_class_init(ObjectClass *klass, void *data) +static void raa228000_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -260,7 +260,7 @@ static void raa228000_class_init(ObjectClass *klass, void *data) isl_pmbus_vr_class_init(klass, data, 1); } -static void raa229004_class_init(ObjectClass *klass, void *data) +static void raa229004_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -269,7 +269,7 @@ static void raa229004_class_init(ObjectClass *klass, void *data) isl_pmbus_vr_class_init(klass, data, 2); } -static void isl69259_class_init(ObjectClass *klass, void *data) +static void isl69259_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/sensor/lsm303dlhc_mag.c b/hw/sensor/lsm303dlhc_mag.c index 04471539b5..f9e501da84 100644 --- a/hw/sensor/lsm303dlhc_mag.c +++ b/hw/sensor/lsm303dlhc_mag.c @@ -530,7 +530,7 @@ static void lsm303dlhc_mag_initfn(Object *obj) /* * Set the virtual method pointers (bus state change, tx/rx, etc.). */ -static void lsm303dlhc_mag_class_init(ObjectClass *klass, void *data) +static void lsm303dlhc_mag_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/sensor/max31785.c b/hw/sensor/max31785.c index 3577a7c218..c75581455a 100644 --- a/hw/sensor/max31785.c +++ b/hw/sensor/max31785.c @@ -544,7 +544,7 @@ static void max31785_init(Object *obj) } } -static void max31785_class_init(ObjectClass *klass, void *data) +static void max31785_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/sensor/max34451.c b/hw/sensor/max34451.c index 93b53f3db2..a369d2b531 100644 --- a/hw/sensor/max34451.c +++ b/hw/sensor/max34451.c @@ -746,7 +746,7 @@ static void max34451_init(Object *obj) } -static void max34451_class_init(ObjectClass *klass, void *data) +static void max34451_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/sensor/tmp105.c b/hw/sensor/tmp105.c index ef2824f3e1..f5b61109e3 100644 --- a/hw/sensor/tmp105.c +++ b/hw/sensor/tmp105.c @@ -313,7 +313,7 @@ static void tmp105_initfn(Object *obj) tmp105_set_temperature, NULL, NULL); } -static void tmp105_class_init(ObjectClass *klass, void *data) +static void tmp105_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/sensor/tmp421.c b/hw/sensor/tmp421.c index 007f7cd018..263bfa1bbd 100644 --- a/hw/sensor/tmp421.c +++ b/hw/sensor/tmp421.c @@ -337,7 +337,7 @@ static void tmp421_realize(DeviceState *dev, Error **errp) tmp421_reset(&s->i2c); } -static void tmp421_class_init(ObjectClass *klass, void *data) +static void tmp421_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/sparc/sun4m.c b/hw/sparc/sun4m.c index edbf19d958..8ac7e625ef 100644 --- a/hw/sparc/sun4m.c +++ b/hw/sparc/sun4m.c @@ -595,7 +595,7 @@ static void idreg_realize(DeviceState *ds, Error **errp) sysbus_init_mmio(dev, &s->mem); } -static void idreg_class_init(ObjectClass *oc, void *data) +static void idreg_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -645,7 +645,7 @@ static void afx_realize(DeviceState *ds, Error **errp) sysbus_init_mmio(dev, &s->mem); } -static void afx_class_init(ObjectClass *oc, void *data) +static void afx_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -727,7 +727,7 @@ static void prom_realize(DeviceState *ds, Error **errp) sysbus_init_mmio(dev, &s->prom); } -static void prom_class_init(ObjectClass *klass, void *data) +static void prom_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -771,7 +771,7 @@ static void ram_initfn(Object *obj) "Valid value is ID of a hostmem backend"); } -static void ram_class_init(ObjectClass *klass, void *data) +static void ram_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1098,7 +1098,7 @@ enum { ss600mp_id, }; -static void sun4m_machine_class_init(ObjectClass *oc, void *data) +static void sun4m_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -1109,7 +1109,7 @@ static void sun4m_machine_class_init(ObjectClass *oc, void *data) mc->default_ram_id = "sun4m.ram"; } -static void ss5_class_init(ObjectClass *oc, void *data) +static void ss5_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1146,7 +1146,7 @@ static void ss5_class_init(ObjectClass *oc, void *data) smc->hwdef = &ss5_hwdef; } -static void ss10_class_init(ObjectClass *oc, void *data) +static void ss10_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1181,7 +1181,7 @@ static void ss10_class_init(ObjectClass *oc, void *data) smc->hwdef = &ss10_hwdef; } -static void ss600mp_class_init(ObjectClass *oc, void *data) +static void ss600mp_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1214,7 +1214,7 @@ static void ss600mp_class_init(ObjectClass *oc, void *data) smc->hwdef = &ss600mp_hwdef; } -static void ss20_class_init(ObjectClass *oc, void *data) +static void ss20_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1265,7 +1265,7 @@ static void ss20_class_init(ObjectClass *oc, void *data) smc->hwdef = &ss20_hwdef; } -static void voyager_class_init(ObjectClass *oc, void *data) +static void voyager_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1297,7 +1297,7 @@ static void voyager_class_init(ObjectClass *oc, void *data) smc->hwdef = &voyager_hwdef; } -static void ss_lx_class_init(ObjectClass *oc, void *data) +static void ss_lx_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1330,7 +1330,7 @@ static void ss_lx_class_init(ObjectClass *oc, void *data) smc->hwdef = &ss_lx_hwdef; } -static void ss4_class_init(ObjectClass *oc, void *data) +static void ss4_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1363,7 +1363,7 @@ static void ss4_class_init(ObjectClass *oc, void *data) smc->hwdef = &ss4_hwdef; } -static void scls_class_init(ObjectClass *oc, void *data) +static void scls_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); @@ -1395,7 +1395,7 @@ static void scls_class_init(ObjectClass *oc, void *data) smc->hwdef = &scls_hwdef; } -static void sbook_class_init(ObjectClass *oc, void *data) +static void sbook_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); Sun4mMachineClass *smc = SUN4M_MACHINE_CLASS(mc); diff --git a/hw/sparc/sun4m_iommu.c b/hw/sparc/sun4m_iommu.c index 4a542b18d2..a7ff36ee78 100644 --- a/hw/sparc/sun4m_iommu.c +++ b/hw/sparc/sun4m_iommu.c @@ -372,7 +372,7 @@ static const Property iommu_properties[] = { DEFINE_PROP_UINT32("version", IOMMUState, version, 0), }; -static void iommu_class_init(ObjectClass *klass, void *data) +static void iommu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -389,7 +389,8 @@ static const TypeInfo iommu_info = { .class_init = iommu_class_init, }; -static void sun4m_iommu_memory_region_class_init(ObjectClass *klass, void *data) +static void sun4m_iommu_memory_region_class_init(ObjectClass *klass, + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/sparc64/niagara.c b/hw/sparc64/niagara.c index 805ba6b1e3..1ffe92060a 100644 --- a/hw/sparc64/niagara.c +++ b/hw/sparc64/niagara.c @@ -157,7 +157,7 @@ static void niagara_init(MachineState *machine) sun4v_rtc_init(NIAGARA_RTC_BASE); } -static void niagara_class_init(ObjectClass *oc, void *data) +static void niagara_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index becdf3ea98..0e5fb4e1b5 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -266,7 +266,7 @@ static void power_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(sbd, &d->power_mmio); } -static void power_class_init(ObjectClass *klass, void *data) +static void power_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -373,7 +373,7 @@ static const Property ebus_properties[] = { console_serial_base, 0), }; -static void ebus_class_init(ObjectClass *klass, void *data) +static void ebus_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -464,7 +464,7 @@ static void prom_realize(DeviceState *ds, Error **errp) sysbus_init_mmio(dev, &s->prom); } -static void prom_class_init(ObjectClass *klass, void *data) +static void prom_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -524,7 +524,7 @@ static const Property ram_properties[] = { DEFINE_PROP_UINT64("size", RamDevice, size, 0), }; -static void ram_class_init(ObjectClass *klass, void *data) +static void ram_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -787,7 +787,7 @@ static GlobalProperty hw_compat_sparc64[] = { }; static const size_t hw_compat_sparc64_len = G_N_ELEMENTS(hw_compat_sparc64); -static void sun4u_class_init(ObjectClass *oc, void *data) +static void sun4u_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); FWPathProviderClass *fwc = FW_PATH_PROVIDER_CLASS(oc); @@ -817,7 +817,7 @@ static const TypeInfo sun4u_type = { }, }; -static void sun4v_class_init(ObjectClass *oc, void *data) +static void sun4v_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/sparc64/sun4u_iommu.c b/hw/sparc64/sun4u_iommu.c index 533fcae1fb..14645f475a 100644 --- a/hw/sparc64/sun4u_iommu.c +++ b/hw/sparc64/sun4u_iommu.c @@ -305,7 +305,7 @@ static void iommu_init(Object *obj) sysbus_init_mmio(sbd, &s->iomem); } -static void iommu_class_init(ObjectClass *klass, void *data) +static void iommu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -320,7 +320,8 @@ static const TypeInfo iommu_info = { .class_init = iommu_class_init, }; -static void sun4u_iommu_memory_region_class_init(ObjectClass *klass, void *data) +static void sun4u_iommu_memory_region_class_init(ObjectClass *klass, + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/ssi/allwinner-a10-spi.c b/hw/ssi/allwinner-a10-spi.c index d2f6bb9cdc..6b7cca8d32 100644 --- a/hw/ssi/allwinner-a10-spi.c +++ b/hw/ssi/allwinner-a10-spi.c @@ -535,7 +535,7 @@ static void allwinner_a10_spi_realize(DeviceState *dev, Error **errp) fifo8_create(&s->rx_fifo, AW_A10_SPI_FIFO_SIZE); } -static void allwinner_a10_spi_class_init(ObjectClass *klass, void *data) +static void allwinner_a10_spi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index faef1a8e5b..0d38f95c7a 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -1294,7 +1294,7 @@ static const Property aspeed_smc_properties[] = { TYPE_MEMORY_REGION, MemoryRegion *), }; -static void aspeed_smc_class_init(ObjectClass *klass, void *data) +static void aspeed_smc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1341,7 +1341,7 @@ static const Property aspeed_smc_flash_properties[] = { AspeedSMCState *), }; -static void aspeed_smc_flash_class_init(ObjectClass *klass, void *data) +static void aspeed_smc_flash_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1383,7 +1383,7 @@ static const AspeedSegments aspeed_2400_smc_segments[] = { { 0x10000000, 32 * MiB }, }; -static void aspeed_2400_smc_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_smc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1429,7 +1429,7 @@ static const AspeedSegments aspeed_2400_fmc_segments[] = { { 0x2A000000, 32 * MiB } }; -static void aspeed_2400_fmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_fmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1473,7 +1473,7 @@ static int aspeed_2400_spi1_addr_width(const AspeedSMCState *s) return s->regs[R_SPI_CTRL0] & CTRL_AST2400_SPI_4BYTE ? 4 : 3; } -static void aspeed_2400_spi1_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_spi1_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1515,7 +1515,7 @@ static const AspeedSegments aspeed_2500_fmc_segments[] = { { 0x2A000000, 32 * MiB }, }; -static void aspeed_2500_fmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_fmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1555,7 +1555,7 @@ static const AspeedSegments aspeed_2500_spi1_segments[] = { { 0x32000000, 96 * MiB }, /* end address is readonly */ }; -static void aspeed_2500_spi1_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_spi1_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1591,7 +1591,7 @@ static const AspeedSegments aspeed_2500_spi2_segments[] = { { 0x3A000000, 96 * MiB }, /* end address is readonly */ }; -static void aspeed_2500_spi2_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_spi2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1674,7 +1674,7 @@ static const AspeedSegments aspeed_2600_fmc_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_2600_fmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_fmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1715,7 +1715,7 @@ static const AspeedSegments aspeed_2600_spi1_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_2600_spi1_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_spi1_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1756,7 +1756,7 @@ static const AspeedSegments aspeed_2600_spi2_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_2600_spi2_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_spi2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1839,7 +1839,7 @@ static const AspeedSegments aspeed_1030_fmc_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_1030_fmc_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_fmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1879,7 +1879,7 @@ static const AspeedSegments aspeed_1030_spi1_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_1030_spi1_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_spi1_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -1917,7 +1917,7 @@ static const AspeedSegments aspeed_1030_spi2_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_1030_spi2_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_spi2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -2022,7 +2022,7 @@ static const AspeedSegments aspeed_2700_fmc_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_2700_fmc_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_fmc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -2064,7 +2064,7 @@ static const AspeedSegments aspeed_2700_spi0_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_2700_spi0_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_spi0_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -2104,7 +2104,7 @@ static const AspeedSegments aspeed_2700_spi1_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_2700_spi1_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_spi1_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); @@ -2144,7 +2144,7 @@ static const AspeedSegments aspeed_2700_spi2_segments[] = { { 0x0, 0 }, /* disabled */ }; -static void aspeed_2700_spi2_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_spi2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedSMCClass *asc = ASPEED_SMC_CLASS(klass); diff --git a/hw/ssi/bcm2835_spi.c b/hw/ssi/bcm2835_spi.c index ebd8809f7c..bf8ba35e04 100644 --- a/hw/ssi/bcm2835_spi.c +++ b/hw/ssi/bcm2835_spi.c @@ -264,7 +264,7 @@ static const VMStateDescription vmstate_bcm2835_spi = { } }; -static void bcm2835_spi_class_init(ObjectClass *klass, void *data) +static void bcm2835_spi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ssi/ibex_spi_host.c b/hw/ssi/ibex_spi_host.c index 6b28cda200..f05be68748 100644 --- a/hw/ssi/ibex_spi_host.c +++ b/hw/ssi/ibex_spi_host.c @@ -622,7 +622,7 @@ static void ibex_spi_host_init(Object *obj) sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); } -static void ibex_spi_host_class_init(ObjectClass *klass, void *data) +static void ibex_spi_host_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = ibex_spi_host_realize; diff --git a/hw/ssi/imx_spi.c b/hw/ssi/imx_spi.c index 2e317879b4..1312f588d2 100644 --- a/hw/ssi/imx_spi.c +++ b/hw/ssi/imx_spi.c @@ -475,7 +475,7 @@ static void imx_spi_realize(DeviceState *dev, Error **errp) fifo32_create(&s->rx_fifo, ECSPI_FIFO_SIZE); } -static void imx_spi_class_init(ObjectClass *klass, void *data) +static void imx_spi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ssi/mss-spi.c b/hw/ssi/mss-spi.c index 340adcdd3f..fd7ba7eeb0 100644 --- a/hw/ssi/mss-spi.c +++ b/hw/ssi/mss-spi.c @@ -398,7 +398,7 @@ static const VMStateDescription vmstate_mss_spi = { } }; -static void mss_spi_class_init(ObjectClass *klass, void *data) +static void mss_spi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ssi/npcm7xx_fiu.c b/hw/ssi/npcm7xx_fiu.c index 8df4bec3f1..056ce13394 100644 --- a/hw/ssi/npcm7xx_fiu.c +++ b/hw/ssi/npcm7xx_fiu.c @@ -557,7 +557,7 @@ static const Property npcm7xx_fiu_properties[] = { DEFINE_PROP_SIZE("flash-size", NPCM7xxFIUState, flash_size, 0), }; -static void npcm7xx_fiu_class_init(ObjectClass *klass, void *data) +static void npcm7xx_fiu_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ssi/npcm_pspi.c b/hw/ssi/npcm_pspi.c index 41a5323530..a31dcc050e 100644 --- a/hw/ssi/npcm_pspi.c +++ b/hw/ssi/npcm_pspi.c @@ -199,7 +199,7 @@ static const VMStateDescription vmstate_npcm_pspi = { }; -static void npcm_pspi_class_init(ObjectClass *klass, void *data) +static void npcm_pspi_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ssi/pl022.c b/hw/ssi/pl022.c index 53c9c225ad..1dc0bcbcc0 100644 --- a/hw/ssi/pl022.c +++ b/hw/ssi/pl022.c @@ -292,7 +292,7 @@ static void pl022_realize(DeviceState *dev, Error **errp) s->ssi = ssi_create_bus(dev, "ssi"); } -static void pl022_class_init(ObjectClass *klass, void *data) +static void pl022_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c index 367a2ff3bb..0bb6b0d935 100644 --- a/hw/ssi/pnv_spi.c +++ b/hw/ssi/pnv_spi.c @@ -1199,7 +1199,7 @@ static int pnv_spi_dt_xscom(PnvXScomInterface *dev, void *fdt, return 0; } -static void pnv_spi_class_init(ObjectClass *klass, void *data) +static void pnv_spi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PnvXScomInterfaceClass *xscomc = PNV_XSCOM_INTERFACE_CLASS(klass); diff --git a/hw/ssi/sifive_spi.c b/hw/ssi/sifive_spi.c index 76f8654f41..3e01fef61c 100644 --- a/hw/ssi/sifive_spi.c +++ b/hw/ssi/sifive_spi.c @@ -332,7 +332,7 @@ static const Property sifive_spi_properties[] = { DEFINE_PROP_UINT32("num-cs", SiFiveSPIState, num_cs, 1), }; -static void sifive_spi_class_init(ObjectClass *klass, void *data) +static void sifive_spi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ssi/ssi.c b/hw/ssi/ssi.c index 872c4e8036..d0de640fe6 100644 --- a/hw/ssi/ssi.c +++ b/hw/ssi/ssi.c @@ -55,7 +55,7 @@ static bool ssi_bus_check_address(BusState *b, DeviceState *dev, Error **errp) return true; } -static void ssi_bus_class_init(ObjectClass *klass, void *data) +static void ssi_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); @@ -112,7 +112,7 @@ static const Property ssi_peripheral_properties[] = { DEFINE_PROP_UINT8("cs", SSIPeripheral, cs_index, 0), }; -static void ssi_peripheral_class_init(ObjectClass *klass, void *data) +static void ssi_peripheral_class_init(ObjectClass *klass, const void *data) { SSIPeripheralClass *ssc = SSI_PERIPHERAL_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ssi/stm32f2xx_spi.c b/hw/ssi/stm32f2xx_spi.c index ea9b74a409..871d57324d 100644 --- a/hw/ssi/stm32f2xx_spi.c +++ b/hw/ssi/stm32f2xx_spi.c @@ -202,7 +202,7 @@ static void stm32f2xx_spi_init(Object *obj) s->ssi = ssi_create_bus(dev, "ssi"); } -static void stm32f2xx_spi_class_init(ObjectClass *klass, void *data) +static void stm32f2xx_spi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ssi/xilinx_spi.c b/hw/ssi/xilinx_spi.c index be5baa6b35..4144c8a627 100644 --- a/hw/ssi/xilinx_spi.c +++ b/hw/ssi/xilinx_spi.c @@ -379,7 +379,7 @@ static const Property xilinx_spi_properties[] = { DEFINE_PROP_UINT8("num-ss-bits", XilinxSPI, num_cs, 1), }; -static void xilinx_spi_class_init(ObjectClass *klass, void *data) +static void xilinx_spi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ssi/xilinx_spips.c b/hw/ssi/xilinx_spips.c index 60d092039f..a79f3b8e49 100644 --- a/hw/ssi/xilinx_spips.c +++ b/hw/ssi/xilinx_spips.c @@ -1430,7 +1430,7 @@ static const Property xilinx_spips_properties[] = { DEFINE_PROP_UINT8("num-txrx-bytes", XilinxSPIPS, num_txrx_bytes, 1), }; -static void xilinx_qspips_class_init(ObjectClass *klass, void * data) +static void xilinx_qspips_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XilinxSPIPSClass *xsc = XILINX_SPIPS_CLASS(klass); @@ -1442,7 +1442,7 @@ static void xilinx_qspips_class_init(ObjectClass *klass, void * data) xsc->tx_fifo_size = TXFF_A_Q; } -static void xilinx_spips_class_init(ObjectClass *klass, void *data) +static void xilinx_spips_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XilinxSPIPSClass *xsc = XILINX_SPIPS_CLASS(klass); @@ -1458,7 +1458,7 @@ static void xilinx_spips_class_init(ObjectClass *klass, void *data) xsc->tx_fifo_size = TXFF_A; } -static void xlnx_zynqmp_qspips_class_init(ObjectClass *klass, void * data) +static void xlnx_zynqmp_qspips_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); XilinxSPIPSClass *xsc = XILINX_SPIPS_CLASS(klass); diff --git a/hw/ssi/xlnx-versal-ospi.c b/hw/ssi/xlnx-versal-ospi.c index 9e96c9b69a..56d51ce0e3 100644 --- a/hw/ssi/xlnx-versal-ospi.c +++ b/hw/ssi/xlnx-versal-ospi.c @@ -1831,7 +1831,7 @@ static const Property xlnx_versal_ospi_properties[] = { ind_write_disabled, false), }; -static void xlnx_versal_ospi_class_init(ObjectClass *klass, void *data) +static void xlnx_versal_ospi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/a9gtimer.c b/hw/timer/a9gtimer.c index 9835c35483..690140f5a6 100644 --- a/hw/timer/a9gtimer.c +++ b/hw/timer/a9gtimer.c @@ -377,7 +377,7 @@ static const Property a9_gtimer_properties[] = { DEFINE_PROP_UINT32("num-cpu", A9GTimerState, num_cpu, 0), }; -static void a9_gtimer_class_init(ObjectClass *klass, void *data) +static void a9_gtimer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/allwinner-a10-pit.c b/hw/timer/allwinner-a10-pit.c index da3d7173ef..e4c353273a 100644 --- a/hw/timer/allwinner-a10-pit.c +++ b/hw/timer/allwinner-a10-pit.c @@ -288,7 +288,7 @@ static void a10_pit_finalize(Object *obj) } } -static void a10_pit_class_init(ObjectClass *klass, void *data) +static void a10_pit_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/arm_mptimer.c b/hw/timer/arm_mptimer.c index 803dad1e8a..7cc5915e9e 100644 --- a/hw/timer/arm_mptimer.c +++ b/hw/timer/arm_mptimer.c @@ -304,7 +304,7 @@ static const Property arm_mptimer_properties[] = { DEFINE_PROP_UINT32("num-cpu", ARMMPTimerState, num_cpu, 0), }; -static void arm_mptimer_class_init(ObjectClass *klass, void *data) +static void arm_mptimer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/arm_timer.c b/hw/timer/arm_timer.c index 1213b77aa0..56638ff5cd 100644 --- a/hw/timer/arm_timer.c +++ b/hw/timer/arm_timer.c @@ -392,7 +392,7 @@ static const Property sp804_properties[] = { DEFINE_PROP_UINT32("freq1", SP804State, freq1, 1000000), }; -static void sp804_class_init(ObjectClass *klass, void *data) +static void sp804_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); diff --git a/hw/timer/armv7m_systick.c b/hw/timer/armv7m_systick.c index a07febd1d1..7e4ddcd405 100644 --- a/hw/timer/armv7m_systick.c +++ b/hw/timer/armv7m_systick.c @@ -285,7 +285,7 @@ static const VMStateDescription vmstate_systick = { } }; -static void systick_class_init(ObjectClass *klass, void *data) +static void systick_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/aspeed_timer.c b/hw/timer/aspeed_timer.c index ecda49574e..57db03512f 100644 --- a/hw/timer/aspeed_timer.c +++ b/hw/timer/aspeed_timer.c @@ -895,7 +895,7 @@ static const Property aspeed_timer_properties[] = { AspeedSCUState *), }; -static void timer_class_init(ObjectClass *klass, void *data) +static void timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -915,7 +915,7 @@ static const TypeInfo aspeed_timer_info = { .abstract = true, }; -static void aspeed_2400_timer_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); @@ -931,7 +931,7 @@ static const TypeInfo aspeed_2400_timer_info = { .class_init = aspeed_2400_timer_class_init, }; -static void aspeed_2500_timer_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); @@ -947,7 +947,7 @@ static const TypeInfo aspeed_2500_timer_info = { .class_init = aspeed_2500_timer_class_init, }; -static void aspeed_2600_timer_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); @@ -963,7 +963,7 @@ static const TypeInfo aspeed_2600_timer_info = { .class_init = aspeed_2600_timer_class_init, }; -static void aspeed_1030_timer_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); @@ -979,7 +979,7 @@ static const TypeInfo aspeed_1030_timer_info = { .class_init = aspeed_1030_timer_class_init, }; -static void aspeed_2700_timer_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedTimerClass *awc = ASPEED_TIMER_CLASS(klass); diff --git a/hw/timer/avr_timer16.c b/hw/timer/avr_timer16.c index 96baf9cf60..012d829001 100644 --- a/hw/timer/avr_timer16.c +++ b/hw/timer/avr_timer16.c @@ -595,7 +595,7 @@ static void avr_timer16_realize(DeviceState *dev, Error **errp) s->enabled = true; } -static void avr_timer16_class_init(ObjectClass *klass, void *data) +static void avr_timer16_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/bcm2835_systmr.c b/hw/timer/bcm2835_systmr.c index 2f0fee3342..7929aaa882 100644 --- a/hw/timer/bcm2835_systmr.c +++ b/hw/timer/bcm2835_systmr.c @@ -154,7 +154,7 @@ static const VMStateDescription bcm2835_systmr_vmstate = { } }; -static void bcm2835_systmr_class_init(ObjectClass *klass, void *data) +static void bcm2835_systmr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/cadence_ttc.c b/hw/timer/cadence_ttc.c index 54dbd4c564..9c7ba16876 100644 --- a/hw/timer/cadence_ttc.c +++ b/hw/timer/cadence_ttc.c @@ -451,7 +451,7 @@ static const VMStateDescription vmstate_cadence_ttc = { } }; -static void cadence_ttc_class_init(ObjectClass *klass, void *data) +static void cadence_ttc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/cmsdk-apb-dualtimer.c b/hw/timer/cmsdk-apb-dualtimer.c index 2ecd8dfe3c..34c550a3f9 100644 --- a/hw/timer/cmsdk-apb-dualtimer.c +++ b/hw/timer/cmsdk-apb-dualtimer.c @@ -534,7 +534,7 @@ static const VMStateDescription cmsdk_apb_dualtimer_vmstate = { } }; -static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, void *data) +static void cmsdk_apb_dualtimer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/cmsdk-apb-timer.c b/hw/timer/cmsdk-apb-timer.c index 16d0b2170e..4095267b4a 100644 --- a/hw/timer/cmsdk-apb-timer.c +++ b/hw/timer/cmsdk-apb-timer.c @@ -261,7 +261,7 @@ static const VMStateDescription cmsdk_apb_timer_vmstate = { } }; -static void cmsdk_apb_timer_class_init(ObjectClass *klass, void *data) +static void cmsdk_apb_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/digic-timer.c b/hw/timer/digic-timer.c index 00c32978d2..355138d354 100644 --- a/hw/timer/digic-timer.c +++ b/hw/timer/digic-timer.c @@ -161,7 +161,7 @@ static void digic_timer_finalize(Object *obj) ptimer_free(s->ptimer); } -static void digic_timer_class_init(ObjectClass *klass, void *class_data) +static void digic_timer_class_init(ObjectClass *klass, const void *class_data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/exynos4210_mct.c b/hw/timer/exynos4210_mct.c index 5c6e139b20..bb0f9c8b9a 100644 --- a/hw/timer/exynos4210_mct.c +++ b/hw/timer/exynos4210_mct.c @@ -1546,7 +1546,7 @@ static void exynos4210_mct_finalize(Object *obj) } } -static void exynos4210_mct_class_init(ObjectClass *klass, void *data) +static void exynos4210_mct_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/exynos4210_pwm.c b/hw/timer/exynos4210_pwm.c index 703d1d2b4a..69f737a8e6 100644 --- a/hw/timer/exynos4210_pwm.c +++ b/hw/timer/exynos4210_pwm.c @@ -420,7 +420,7 @@ static void exynos4210_pwm_finalize(Object *obj) } } -static void exynos4210_pwm_class_init(ObjectClass *klass, void *data) +static void exynos4210_pwm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/grlib_gptimer.c b/hw/timer/grlib_gptimer.c index f0802b6eb6..0e06fa09e9 100644 --- a/hw/timer/grlib_gptimer.c +++ b/hw/timer/grlib_gptimer.c @@ -409,7 +409,7 @@ static const Property grlib_gptimer_properties[] = { DEFINE_PROP_UINT32("nr-timers", GPTimerUnit, nr_timers, 2), }; -static void grlib_gptimer_class_init(ObjectClass *klass, void *data) +static void grlib_gptimer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index ea82472105..d1b7bc52b7 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -736,7 +736,7 @@ static const Property hpet_device_properties[] = { DEFINE_PROP_BOOL("hpet-offset-saved", HPETState, hpet_offset_saved, true), }; -static void hpet_device_class_init(ObjectClass *klass, void *data) +static void hpet_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/i8254.c b/hw/timer/i8254.c index 058fc61ce9..4b25c487f7 100644 --- a/hw/timer/i8254.c +++ b/hw/timer/i8254.c @@ -350,7 +350,7 @@ static void pit_realizefn(DeviceState *dev, Error **errp) pc->parent_realize(dev, errp); } -static void pit_class_initfn(ObjectClass *klass, void *data) +static void pit_class_initfn(ObjectClass *klass, const void *data) { PITClass *pc = PIT_CLASS(klass); PITCommonClass *k = PIT_COMMON_CLASS(klass); diff --git a/hw/timer/i8254_common.c b/hw/timer/i8254_common.c index 29105afcc3..ad091594cd 100644 --- a/hw/timer/i8254_common.c +++ b/hw/timer/i8254_common.c @@ -242,7 +242,7 @@ static const Property pit_common_properties[] = { DEFINE_PROP_UINT32("iobase", PITCommonState, iobase, -1), }; -static void pit_common_class_init(ObjectClass *klass, void *data) +static void pit_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/ibex_timer.c b/hw/timer/ibex_timer.c index 3ebc870097..c7320ef30f 100644 --- a/hw/timer/ibex_timer.c +++ b/hw/timer/ibex_timer.c @@ -286,7 +286,7 @@ static void ibex_timer_realize(DeviceState *dev, Error **errp) } -static void ibex_timer_class_init(ObjectClass *klass, void *data) +static void ibex_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/imx_epit.c b/hw/timer/imx_epit.c index f40ab16697..6123321c35 100644 --- a/hw/timer/imx_epit.c +++ b/hw/timer/imx_epit.c @@ -427,7 +427,7 @@ static void imx_epit_dev_reset(DeviceState *dev) imx_epit_reset(s, true); } -static void imx_epit_class_init(ObjectClass *klass, void *data) +static void imx_epit_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/imx_gpt.c b/hw/timer/imx_gpt.c index 200a89225b..8c7cbfdeac 100644 --- a/hw/timer/imx_gpt.c +++ b/hw/timer/imx_gpt.c @@ -518,7 +518,7 @@ static void imx_gpt_realize(DeviceState *dev, Error **errp) s->timer = ptimer_init(imx_gpt_timeout, s, PTIMER_POLICY_LEGACY); } -static void imx_gpt_class_init(ObjectClass *klass, void *data) +static void imx_gpt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/mss-timer.c b/hw/timer/mss-timer.c index 594da64eae..2ce821178b 100644 --- a/hw/timer/mss-timer.c +++ b/hw/timer/mss-timer.c @@ -285,7 +285,7 @@ static const Property mss_timer_properties[] = { 100 * 1000000), }; -static void mss_timer_class_init(ObjectClass *klass, void *data) +static void mss_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/npcm7xx_timer.c b/hw/timer/npcm7xx_timer.c index c55ba02235..6a116ad54b 100644 --- a/hw/timer/npcm7xx_timer.c +++ b/hw/timer/npcm7xx_timer.c @@ -689,7 +689,7 @@ static const VMStateDescription vmstate_npcm7xx_timer_ctrl = { }, }; -static void npcm7xx_timer_class_init(ObjectClass *klass, void *data) +static void npcm7xx_timer_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/nrf51_timer.c b/hw/timer/nrf51_timer.c index 11ad8b575e..e228fdebc3 100644 --- a/hw/timer/nrf51_timer.c +++ b/hw/timer/nrf51_timer.c @@ -383,7 +383,7 @@ static const Property nrf51_timer_properties[] = { DEFINE_PROP_UINT8("id", NRF51TimerState, id, 0), }; -static void nrf51_timer_class_init(ObjectClass *klass, void *data) +static void nrf51_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c index 9e4dd0fd9f..7a94366b0f 100644 --- a/hw/timer/pxa2xx_timer.c +++ b/hw/timer/pxa2xx_timer.c @@ -555,7 +555,7 @@ static const Property pxa25x_timer_dev_properties[] = { PXA2XX_TIMER_HAVE_TM4, false), }; -static void pxa25x_timer_dev_class_init(ObjectClass *klass, void *data) +static void pxa25x_timer_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -570,7 +570,7 @@ static const TypeInfo pxa25x_timer_dev_info = { .class_init = pxa25x_timer_dev_class_init, }; -static void pxa2xx_timer_class_init(ObjectClass *oc, void *data) +static void pxa2xx_timer_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/timer/renesas_cmt.c b/hw/timer/renesas_cmt.c index 93e7f58cc2..cdff7f47f1 100644 --- a/hw/timer/renesas_cmt.c +++ b/hw/timer/renesas_cmt.c @@ -257,7 +257,7 @@ static const Property rcmt_properties[] = { DEFINE_PROP_UINT64("input-freq", RCMTState, input_freq, 0), }; -static void rcmt_class_init(ObjectClass *klass, void *data) +static void rcmt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/renesas_tmr.c b/hw/timer/renesas_tmr.c index 884349c2cc..95707f2b8c 100644 --- a/hw/timer/renesas_tmr.c +++ b/hw/timer/renesas_tmr.c @@ -467,7 +467,7 @@ static const Property rtmr_properties[] = { DEFINE_PROP_UINT64("input-freq", RTMRState, input_freq, 0), }; -static void rtmr_class_init(ObjectClass *klass, void *data) +static void rtmr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/sifive_pwm.c b/hw/timer/sifive_pwm.c index fc796e9bc3..e85e389f7a 100644 --- a/hw/timer/sifive_pwm.c +++ b/hw/timer/sifive_pwm.c @@ -441,7 +441,7 @@ static void sifive_pwm_realize(DeviceState *dev, Error **errp) sifive_pwm_interrupt_3, s); } -static void sifive_pwm_class_init(ObjectClass *klass, void *data) +static void sifive_pwm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/slavio_timer.c b/hw/timer/slavio_timer.c index 65b24e4f06..3e071fbdb4 100644 --- a/hw/timer/slavio_timer.c +++ b/hw/timer/slavio_timer.c @@ -424,7 +424,7 @@ static const Property slavio_timer_properties[] = { DEFINE_PROP_UINT32("num_cpus", SLAVIO_TIMERState, num_cpus, 0), }; -static void slavio_timer_class_init(ObjectClass *klass, void *data) +static void slavio_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/sse-counter.c b/hw/timer/sse-counter.c index f17064abe3..31f77acf61 100644 --- a/hw/timer/sse-counter.c +++ b/hw/timer/sse-counter.c @@ -448,7 +448,7 @@ static const VMStateDescription sse_counter_vmstate = { } }; -static void sse_counter_class_init(ObjectClass *klass, void *data) +static void sse_counter_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/sse-timer.c b/hw/timer/sse-timer.c index e106739ea9..866d5eef8a 100644 --- a/hw/timer/sse-timer.c +++ b/hw/timer/sse-timer.c @@ -444,7 +444,7 @@ static const Property sse_timer_properties[] = { DEFINE_PROP_LINK("counter", SSETimer, counter, TYPE_SSE_COUNTER, SSECounter *), }; -static void sse_timer_class_init(ObjectClass *klass, void *data) +static void sse_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/stellaris-gptm.c b/hw/timer/stellaris-gptm.c index f28958cefc..d97b2f8309 100644 --- a/hw/timer/stellaris-gptm.c +++ b/hw/timer/stellaris-gptm.c @@ -308,7 +308,7 @@ static void stellaris_gptm_realize(DeviceState *dev, Error **errp) s->timer[1] = timer_new_ns(QEMU_CLOCK_VIRTUAL, gptm_tick, &s->opaque[1]); } -static void stellaris_gptm_class_init(ObjectClass *klass, void *data) +static void stellaris_gptm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/stm32f2xx_timer.c b/hw/timer/stm32f2xx_timer.c index 4707190d6a..be844e7f5a 100644 --- a/hw/timer/stm32f2xx_timer.c +++ b/hw/timer/stm32f2xx_timer.c @@ -320,7 +320,7 @@ static void stm32f2xx_timer_realize(DeviceState *dev, Error **errp) s->timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, stm32f2xx_timer_interrupt, s); } -static void stm32f2xx_timer_class_init(ObjectClass *klass, void *data) +static void stm32f2xx_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/timer/xilinx_timer.c b/hw/timer/xilinx_timer.c index 4620528f98..ff4a224d08 100644 --- a/hw/timer/xilinx_timer.c +++ b/hw/timer/xilinx_timer.c @@ -268,7 +268,7 @@ static const Property xilinx_timer_properties[] = { DEFINE_PROP_UINT8("one-timer-only", XpsTimerState, one_timer_only, 0), }; -static void xilinx_timer_class_init(ObjectClass *klass, void *data) +static void xilinx_timer_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index b668aee97a..9aff4d1a6f 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -315,7 +315,7 @@ static void tpm_crb_realize(DeviceState *dev, Error **errp) } } -static void tpm_crb_class_init(ObjectClass *klass, void *data) +static void tpm_crb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); TPMIfClass *tc = TPM_IF_CLASS(klass); diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c index 9a031e1e75..1cad9a88ea 100644 --- a/hw/tpm/tpm_spapr.c +++ b/hw/tpm/tpm_spapr.c @@ -387,7 +387,7 @@ static void tpm_spapr_realizefn(SpaprVioDevice *dev, Error **errp) s->buffer = g_malloc(TPM_SPAPR_BUFFER_MAX); } -static void tpm_spapr_class_init(ObjectClass *klass, void *data) +static void tpm_spapr_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SpaprVioDeviceClass *k = VIO_SPAPR_DEVICE_CLASS(klass); diff --git a/hw/tpm/tpm_tis_i2c.c b/hw/tpm/tpm_tis_i2c.c index 92d3de1ea3..b97d768a5b 100644 --- a/hw/tpm/tpm_tis_i2c.c +++ b/hw/tpm/tpm_tis_i2c.c @@ -526,7 +526,7 @@ static void tpm_tis_i2c_reset(DeviceState *dev) return tpm_tis_reset(s); } -static void tpm_tis_i2c_class_init(ObjectClass *klass, void *data) +static void tpm_tis_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); I2CSlaveClass *k = I2C_SLAVE_CLASS(klass); diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c index 876cb02cb5..c0098a26e8 100644 --- a/hw/tpm/tpm_tis_isa.c +++ b/hw/tpm/tpm_tis_isa.c @@ -166,7 +166,7 @@ static void build_tpm_tis_isa_aml(AcpiDevAmlIf *adev, Aml *scope) aml_append(scope, dev); } -static void tpm_tis_isa_class_init(ObjectClass *klass, void *data) +static void tpm_tis_isa_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); TPMIfClass *tc = TPM_IF_CLASS(klass); diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c index 4f187690a2..19abf0b948 100644 --- a/hw/tpm/tpm_tis_sysbus.c +++ b/hw/tpm/tpm_tis_sysbus.c @@ -124,7 +124,7 @@ static void tpm_tis_sysbus_realizefn(DeviceState *dev, Error **errp) } } -static void tpm_tis_sysbus_class_init(ObjectClass *klass, void *data) +static void tpm_tis_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); TPMIfClass *tc = TPM_IF_CLASS(klass); diff --git a/hw/tricore/tc27x_soc.c b/hw/tricore/tc27x_soc.c index 81bb16d89b..f3b84980e5 100644 --- a/hw/tricore/tc27x_soc.c +++ b/hw/tricore/tc27x_soc.c @@ -201,14 +201,14 @@ static void tc27x_soc_init(Object *obj) object_initialize_child(obj, "tc27x", &s->cpu, sc->cpu_type); } -static void tc27x_soc_class_init(ObjectClass *klass, void *data) +static void tc27x_soc_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = tc27x_soc_realize; } -static void tc277d_soc_class_init(ObjectClass *oc, void *data) +static void tc277d_soc_class_init(ObjectClass *oc, const void *data) { TC27XSoCClass *sc = TC27X_SOC_CLASS(oc); diff --git a/hw/tricore/triboard.c b/hw/tricore/triboard.c index f5baa8ccbb..cb45b01d2d 100644 --- a/hw/tricore/triboard.c +++ b/hw/tricore/triboard.c @@ -65,7 +65,7 @@ static void triboard_machine_init(MachineState *machine) } static void triboard_machine_tc277d_class_init(ObjectClass *oc, - void *data) + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); TriBoardMachineClass *amc = TRIBOARD_MACHINE_CLASS(oc); diff --git a/hw/tricore/tricore_testdevice.c b/hw/tricore/tricore_testdevice.c index d2da74e384..e8daf95298 100644 --- a/hw/tricore/tricore_testdevice.c +++ b/hw/tricore/tricore_testdevice.c @@ -58,7 +58,7 @@ static void tricore_testdevice_init(Object *obj) "tricore_testdevice", 0x4); } -static void tricore_testdevice_class_init(ObjectClass *klass, void *data) +static void tricore_testdevice_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/uefi/var-service-sysbus.c b/hw/uefi/var-service-sysbus.c index 97da8672ee..a5aa218e26 100644 --- a/hw/uefi/var-service-sysbus.c +++ b/hw/uefi/var-service-sysbus.c @@ -64,7 +64,7 @@ static void uefi_vars_sysbus_realize(DeviceState *dev, Error **errp) uefi_vars_realize(&uv->state, errp); } -static void uefi_vars_sysbus_class_init(ObjectClass *klass, void *data) +static void uefi_vars_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -100,7 +100,7 @@ static void uefi_vars_x64_realize(DeviceState *dev, Error **errp) sysbus_mmio_map(sysbus, 0, hwinfo.mmio_address); } -static void uefi_vars_x64_class_init(ObjectClass *klass, void *data) +static void uefi_vars_x64_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index 1c3794b2d4..57b307ea56 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -419,7 +419,7 @@ static void ufs_lu_unrealize(DeviceState *dev) } } -static void ufs_lu_class_init(ObjectClass *oc, void *data) +static void ufs_lu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 542f13b10e..749519760f 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -1844,7 +1844,7 @@ static const VMStateDescription ufs_vmstate = { .unmigratable = 1, }; -static void ufs_class_init(ObjectClass *oc, void *data) +static void ufs_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PCIDeviceClass *pc = PCI_DEVICE_CLASS(oc); @@ -1880,7 +1880,7 @@ static char *ufs_bus_get_dev_path(DeviceState *dev) return qdev_get_dev_path(bus->parent); } -static void ufs_bus_class_init(ObjectClass *class, void *data) +static void ufs_bus_class_init(ObjectClass *class, const void *data) { BusClass *bc = BUS_CLASS(class); bc->get_dev_path = ufs_bus_get_dev_path; diff --git a/hw/usb/bus.c b/hw/usb/bus.c index f45b82c776..d8446cf50e 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -26,7 +26,7 @@ static const Property usb_props[] = { DEFINE_PROP_STRING("pcap", USBDevice, pcap_filename), }; -static void usb_bus_class_init(ObjectClass *klass, void *data) +static void usb_bus_class_init(ObjectClass *klass, const void *data) { BusClass *k = BUS_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); @@ -713,7 +713,7 @@ static void usb_device_instance_init(Object *obj) } } -static void usb_device_class_init(ObjectClass *klass, void *data) +static void usb_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->bus_type = TYPE_USB_BUS; diff --git a/hw/usb/canokey.c b/hw/usb/canokey.c index e2d66179e0..cbefbb5daf 100644 --- a/hw/usb/canokey.c +++ b/hw/usb/canokey.c @@ -300,7 +300,7 @@ static const Property canokey_properties[] = { DEFINE_PROP_STRING("file", CanoKeyState, file), }; -static void canokey_class_init(ObjectClass *klass, void *data) +static void canokey_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/ccid-card-emulated.c b/hw/usb/ccid-card-emulated.c index b1e330f21d..c21cefd82d 100644 --- a/hw/usb/ccid-card-emulated.c +++ b/hw/usb/ccid-card-emulated.c @@ -591,7 +591,7 @@ static const Property emulated_card_properties[] = { DEFINE_PROP_UINT8("debug", EmulatedState, debug, 0), }; -static void emulated_class_initfn(ObjectClass *klass, void *data) +static void emulated_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); CCIDCardClass *cc = CCID_CARD_CLASS(klass); diff --git a/hw/usb/ccid-card-passthru.c b/hw/usb/ccid-card-passthru.c index bf81485f87..1eea21a733 100644 --- a/hw/usb/ccid-card-passthru.c +++ b/hw/usb/ccid-card-passthru.c @@ -393,7 +393,7 @@ static const Property passthru_card_properties[] = { DEFINE_PROP_UINT8("debug", PassthruState, debug, 0), }; -static void passthru_class_initfn(ObjectClass *klass, void *data) +static void passthru_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); CCIDCardClass *cc = CCID_CARD_CLASS(klass); diff --git a/hw/usb/chipidea.c b/hw/usb/chipidea.c index b1c85404d6..250c2b3bca 100644 --- a/hw/usb/chipidea.c +++ b/hw/usb/chipidea.c @@ -144,7 +144,7 @@ static void chipidea_init(Object *obj) } } -static void chipidea_class_init(ObjectClass *klass, void *data) +static void chipidea_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(klass); diff --git a/hw/usb/dev-audio.c b/hw/usb/dev-audio.c index 40f031252a..26af709f31 100644 --- a/hw/usb/dev-audio.c +++ b/hw/usb/dev-audio.c @@ -997,7 +997,7 @@ static const Property usb_audio_properties[] = { DEFINE_PROP_BOOL("multi", USBAudioState, multi, false), }; -static void usb_audio_class_init(ObjectClass *klass, void *data) +static void usb_audio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *k = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index accdd460e3..54d064e54e 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -774,7 +774,7 @@ static const VMStateDescription vmstate_usb_kbd = { } }; -static void usb_hid_class_initfn(ObjectClass *klass, void *data) +static void usb_hid_class_initfn(ObjectClass *klass, const void *data) { USBDeviceClass *uc = USB_DEVICE_CLASS(klass); @@ -799,7 +799,7 @@ static const Property usb_tablet_properties[] = { DEFINE_PROP_UINT32("head", USBHIDState, head, 0), }; -static void usb_tablet_class_initfn(ObjectClass *klass, void *data) +static void usb_tablet_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); @@ -821,7 +821,7 @@ static const Property usb_mouse_properties[] = { DEFINE_PROP_UINT32("usb_version", USBHIDState, usb_version, 2), }; -static void usb_mouse_class_initfn(ObjectClass *klass, void *data) +static void usb_mouse_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); @@ -844,7 +844,7 @@ static const Property usb_keyboard_properties[] = { DEFINE_PROP_STRING("display", USBHIDState, display), }; -static void usb_keyboard_class_initfn(ObjectClass *klass, void *data) +static void usb_keyboard_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-hub.c b/hw/usb/dev-hub.c index 3880e2aca8..a19350d9c4 100644 --- a/hw/usb/dev-hub.c +++ b/hw/usb/dev-hub.c @@ -670,7 +670,7 @@ static const Property usb_hub_properties[] = { DEFINE_PROP_BOOL("port-power", USBHubState, port_power, false), }; -static void usb_hub_class_initfn(ObjectClass *klass, void *data) +static void usb_hub_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-mtp.c b/hw/usb/dev-mtp.c index 4cd14c3df4..ce45c9cd06 100644 --- a/hw/usb/dev-mtp.c +++ b/hw/usb/dev-mtp.c @@ -2082,7 +2082,7 @@ static const Property mtp_properties[] = { DEFINE_PROP_BOOL("readonly", MTPState, readonly, true), }; -static void usb_mtp_class_initfn(ObjectClass *klass, void *data) +static void usb_mtp_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-network.c b/hw/usb/dev-network.c index a87a0ffb95..81cc09dcac 100644 --- a/hw/usb/dev-network.c +++ b/hw/usb/dev-network.c @@ -1411,7 +1411,7 @@ static const Property net_properties[] = { DEFINE_NIC_PROPERTIES(USBNetState, conf), }; -static void usb_net_class_initfn(ObjectClass *klass, void *data) +static void usb_net_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-serial.c b/hw/usb/dev-serial.c index 31f6cf5b31..1c116d8b0f 100644 --- a/hw/usb/dev-serial.c +++ b/hw/usb/dev-serial.c @@ -637,7 +637,7 @@ static const Property serial_properties[] = { DEFINE_PROP_BOOL("always-plugged", USBSerialState, always_plugged, false), }; -static void usb_serial_dev_class_init(ObjectClass *klass, void *data) +static void usb_serial_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); @@ -658,7 +658,7 @@ static const TypeInfo usb_serial_dev_type_info = { .class_init = usb_serial_dev_class_init, }; -static void usb_serial_class_initfn(ObjectClass *klass, void *data) +static void usb_serial_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); @@ -678,7 +678,7 @@ static const Property braille_properties[] = { DEFINE_PROP_CHR("chardev", USBSerialState, cs), }; -static void usb_braille_class_initfn(ObjectClass *klass, void *data) +static void usb_braille_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 84ca8c48e2..87018a3f01 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1433,7 +1433,7 @@ static const Property ccid_properties[] = { DEFINE_PROP_UINT8("debug", USBCCIDState, debug, 0), }; -static void ccid_class_initfn(ObjectClass *klass, void *data) +static void ccid_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); @@ -1464,7 +1464,7 @@ static const TypeInfo ccid_info = { } }; -static void ccid_card_class_init(ObjectClass *klass, void *data) +static void ccid_card_class_init(ObjectClass *klass, const void *data) { DeviceClass *k = DEVICE_CLASS(klass); k->bus_type = TYPE_CCID_BUS; diff --git a/hw/usb/dev-storage-bot.c b/hw/usb/dev-storage-bot.c index 1e5c5c711f..df6ab7f656 100644 --- a/hw/usb/dev-storage-bot.c +++ b/hw/usb/dev-storage-bot.c @@ -40,7 +40,7 @@ static void usb_msd_bot_realize(USBDevice *dev, Error **errp) usb_msd_handle_reset(dev); } -static void usb_msd_class_bot_initfn(ObjectClass *klass, void *data) +static void usb_msd_class_bot_initfn(ObjectClass *klass, const void *data) { USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-storage-classic.c b/hw/usb/dev-storage-classic.c index 56ef39da2e..dabe156359 100644 --- a/hw/usb/dev-storage-classic.c +++ b/hw/usb/dev-storage-classic.c @@ -74,7 +74,7 @@ static const Property msd_properties[] = { DEFINE_PROP_BOOL("commandlog", MSDState, commandlog, false), }; -static void usb_msd_class_storage_initfn(ObjectClass *klass, void *data) +static void usb_msd_class_storage_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-storage.c b/hw/usb/dev-storage.c index 4f1e8b7f6c..b13fe345c4 100644 --- a/hw/usb/dev-storage.c +++ b/hw/usb/dev-storage.c @@ -585,7 +585,7 @@ static const VMStateDescription vmstate_usb_msd = { } }; -static void usb_msd_class_initfn_common(ObjectClass *klass, void *data) +static void usb_msd_class_initfn_common(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-uas.c b/hw/usb/dev-uas.c index b1d6b6ecc3..21cc2835c6 100644 --- a/hw/usb/dev-uas.c +++ b/hw/usb/dev-uas.c @@ -956,7 +956,7 @@ static const Property uas_properties[] = { DEFINE_PROP_UINT32("log-scsi-req", UASDevice, requestlog, 0), }; -static void usb_uas_class_initfn(ObjectClass *klass, void *data) +static void usb_uas_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/dev-wacom.c b/hw/usb/dev-wacom.c index 7177c17f03..f4b71a2147 100644 --- a/hw/usb/dev-wacom.c +++ b/hw/usb/dev-wacom.c @@ -420,7 +420,7 @@ static const VMStateDescription vmstate_usb_wacom = { .unmigratable = 1, }; -static void usb_wacom_class_init(ObjectClass *klass, void *data) +static void usb_wacom_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/hcd-dwc2.c b/hw/usb/hcd-dwc2.c index e8152719f8..83864505bb 100644 --- a/hw/usb/hcd-dwc2.c +++ b/hw/usb/hcd-dwc2.c @@ -1452,7 +1452,7 @@ static const Property dwc2_usb_properties[] = { DEFINE_PROP_UINT32("usb_version", DWC2State, usb_version, 2), }; -static void dwc2_class_init(ObjectClass *klass, void *data) +static void dwc2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); DWC2Class *c = DWC2_USB_CLASS(klass); diff --git a/hw/usb/hcd-dwc3.c b/hw/usb/hcd-dwc3.c index 0bceee2712..98a342b8b8 100644 --- a/hw/usb/hcd-dwc3.c +++ b/hw/usb/hcd-dwc3.c @@ -666,7 +666,7 @@ static const Property usb_dwc3_properties[] = { 0x12345678), }; -static void usb_dwc3_class_init(ObjectClass *klass, void *data) +static void usb_dwc3_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index e00316721a..73122aa797 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -150,7 +150,7 @@ static const VMStateDescription vmstate_ehci_pci = { } }; -static void ehci_class_init(ObjectClass *klass, void *data) +static void ehci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -178,7 +178,7 @@ static const TypeInfo ehci_pci_type_info = { }, }; -static void ehci_data_class_init(ObjectClass *klass, void *data) +static void ehci_data_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/hcd-ehci-sysbus.c b/hw/usb/hcd-ehci-sysbus.c index 768c3dd797..0449f5fa6d 100644 --- a/hw/usb/hcd-ehci-sysbus.c +++ b/hw/usb/hcd-ehci-sysbus.c @@ -80,7 +80,7 @@ static void ehci_sysbus_finalize(Object *obj) usb_ehci_finalize(s); } -static void ehci_sysbus_class_init(ObjectClass *klass, void *data) +static void ehci_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(klass); @@ -95,7 +95,7 @@ static void ehci_sysbus_class_init(ObjectClass *klass, void *data) set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static void ehci_platform_class_init(ObjectClass *oc, void *data) +static void ehci_platform_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -105,7 +105,7 @@ static void ehci_platform_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static void ehci_exynos4210_class_init(ObjectClass *oc, void *data) +static void ehci_exynos4210_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -115,7 +115,7 @@ static void ehci_exynos4210_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static void ehci_aw_h3_class_init(ObjectClass *oc, void *data) +static void ehci_aw_h3_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -125,7 +125,7 @@ static void ehci_aw_h3_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static void ehci_npcm7xx_class_init(ObjectClass *oc, void *data) +static void ehci_npcm7xx_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -137,7 +137,7 @@ static void ehci_npcm7xx_class_init(ObjectClass *oc, void *data) set_bit(DEVICE_CATEGORY_USB, dc->categories); } -static void ehci_tegra2_class_init(ObjectClass *oc, void *data) +static void ehci_tegra2_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -154,7 +154,7 @@ static void ehci_ppc4xx_init(Object *o) s->ehci.companion_enable = true; } -static void ehci_ppc4xx_class_init(ObjectClass *oc, void *data) +static void ehci_ppc4xx_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); @@ -220,7 +220,7 @@ static void fusbh200_ehci_init(Object *obj) &f->mem_vendor); } -static void fusbh200_ehci_class_init(ObjectClass *oc, void *data) +static void fusbh200_ehci_class_init(ObjectClass *oc, const void *data) { SysBusEHCIClass *sec = SYS_BUS_EHCI_CLASS(oc); DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/usb/hcd-ohci-pci.c b/hw/usb/hcd-ohci-pci.c index b3684a2ef6..3c82525a1e 100644 --- a/hw/usb/hcd-ohci-pci.c +++ b/hw/usb/hcd-ohci-pci.c @@ -126,7 +126,7 @@ static const VMStateDescription vmstate_ohci = { } }; -static void ohci_pci_class_init(ObjectClass *klass, void *data) +static void ohci_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/usb/hcd-ohci-sysbus.c b/hw/usb/hcd-ohci-sysbus.c index 15311949b3..3fc6cce44b 100644 --- a/hw/usb/hcd-ohci-sysbus.c +++ b/hw/usb/hcd-ohci-sysbus.c @@ -64,7 +64,7 @@ static const Property ohci_sysbus_properties[] = { DEFINE_PROP_DMAADDR("dma-offset", OHCISysBusState, dma_offset, 0), }; -static void ohci_sysbus_class_init(ObjectClass *klass, void *data) +static void ohci_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 0561a6d801..2b1aee1f21 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1260,7 +1260,7 @@ static const Property uhci_properties_standalone[] = { DEFINE_PROP_UINT32("maxframes", UHCIState, maxframes, 128), }; -static void uhci_class_init(ObjectClass *klass, void *data) +static void uhci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -1284,7 +1284,7 @@ static const TypeInfo uhci_pci_type_info = { }, }; -void uhci_data_class_init(ObjectClass *klass, void *data) +void uhci_data_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/hcd-uhci.h b/hw/usb/hcd-uhci.h index d4664297cf..e0a6525505 100644 --- a/hw/usb/hcd-uhci.h +++ b/hw/usb/hcd-uhci.h @@ -88,7 +88,7 @@ typedef struct UHCIInfo { bool notuser; /* disallow user_creatable */ } UHCIInfo; -void uhci_data_class_init(ObjectClass *klass, void *data); +void uhci_data_class_init(ObjectClass *klass, const void *data); void usb_uhci_common_realize(PCIDevice *dev, Error **errp); #define TYPE_PIIX3_USB_UHCI "piix3-usb-uhci" diff --git a/hw/usb/hcd-xhci-nec.c b/hw/usb/hcd-xhci-nec.c index 1df518baf5..9e0fea26f4 100644 --- a/hw/usb/hcd-xhci-nec.c +++ b/hw/usb/hcd-xhci-nec.c @@ -50,7 +50,7 @@ static void nec_xhci_instance_init(Object *obj) pci->xhci.numslots = nec->slots; } -static void nec_xhci_class_init(ObjectClass *klass, void *data) +static void nec_xhci_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index d908eb787d..6167bb9100 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -223,7 +223,7 @@ static const Property xhci_pci_properties[] = { conditional_intr_mapping, false), }; -static void xhci_class_init(ObjectClass *klass, void *data) +static void xhci_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); @@ -255,7 +255,7 @@ static const TypeInfo xhci_pci_info = { }, }; -static void qemu_xhci_class_init(ObjectClass *klass, void *data) +static void qemu_xhci_class_init(ObjectClass *klass, const void *data) { PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/usb/hcd-xhci-sysbus.c b/hw/usb/hcd-xhci-sysbus.c index ce43322396..244698e5f2 100644 --- a/hw/usb/hcd-xhci-sysbus.c +++ b/hw/usb/hcd-xhci-sysbus.c @@ -96,7 +96,7 @@ static const VMStateDescription vmstate_xhci_sysbus = { } }; -static void xhci_sysbus_class_init(ObjectClass *klass, void *data) +static void xhci_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index 64c3a23b9b..b3785b8ba6 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -3638,7 +3638,7 @@ static const Property xhci_properties[] = { DeviceState *), }; -static void xhci_class_init(ObjectClass *klass, void *data) +static void xhci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/host-libusb.c b/hw/usb/host-libusb.c index c3d642c9d3..b74670ae25 100644 --- a/hw/usb/host-libusb.c +++ b/hw/usb/host-libusb.c @@ -1781,7 +1781,7 @@ static const Property usb_host_dev_properties[] = { suppress_remote_wake, true), }; -static void usb_host_class_initfn(ObjectClass *klass, void *data) +static void usb_host_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/imx-usb-phy.c b/hw/usb/imx-usb-phy.c index f519250567..c25566d0fa 100644 --- a/hw/usb/imx-usb-phy.c +++ b/hw/usb/imx-usb-phy.c @@ -214,7 +214,7 @@ static void imx_usbphy_realize(DeviceState *dev, Error **errp) sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->iomem); } -static void imx_usbphy_class_init(ObjectClass *klass, void *data) +static void imx_usbphy_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/redirect.c b/hw/usb/redirect.c index f3a83b3f4c..f516ff42a1 100644 --- a/hw/usb/redirect.c +++ b/hw/usb/redirect.c @@ -2582,7 +2582,7 @@ static const Property usbredir_properties[] = { suppress_remote_wake, true), }; -static void usbredir_class_initfn(ObjectClass *klass, void *data) +static void usbredir_class_initfn(ObjectClass *klass, const void *data) { USBDeviceClass *uc = USB_DEVICE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/u2f-emulated.c b/hw/usb/u2f-emulated.c index e1dd19ee92..ace5eceadd 100644 --- a/hw/usb/u2f-emulated.c +++ b/hw/usb/u2f-emulated.c @@ -377,7 +377,7 @@ static const Property u2f_emulated_properties[] = { DEFINE_PROP_STRING("counter", U2FEmulatedState, counter), }; -static void u2f_emulated_class_init(ObjectClass *klass, void *data) +static void u2f_emulated_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); U2FKeyClass *kc = U2F_KEY_CLASS(klass); diff --git a/hw/usb/u2f-passthru.c b/hw/usb/u2f-passthru.c index 8df5215a1f..fa8d9cdda8 100644 --- a/hw/usb/u2f-passthru.c +++ b/hw/usb/u2f-passthru.c @@ -520,7 +520,7 @@ static const Property u2f_passthru_properties[] = { DEFINE_PROP_STRING("hidraw", U2FPassthruState, hidraw), }; -static void u2f_passthru_class_init(ObjectClass *klass, void *data) +static void u2f_passthru_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); U2FKeyClass *kc = U2F_KEY_CLASS(klass); diff --git a/hw/usb/u2f.c b/hw/usb/u2f.c index 1fb59cf404..b051a999d3 100644 --- a/hw/usb/u2f.c +++ b/hw/usb/u2f.c @@ -317,7 +317,7 @@ const VMStateDescription vmstate_u2f_key = { } }; -static void u2f_key_class_init(ObjectClass *klass, void *data) +static void u2f_key_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); USBDeviceClass *uc = USB_DEVICE_CLASS(klass); diff --git a/hw/usb/xlnx-usb-subsystem.c b/hw/usb/xlnx-usb-subsystem.c index d8deeb6ced..98967ef49f 100644 --- a/hw/usb/xlnx-usb-subsystem.c +++ b/hw/usb/xlnx-usb-subsystem.c @@ -69,7 +69,7 @@ static void versal_usb2_init(Object *obj) object_property_add_alias(obj, "dma", OBJECT(&s->dwc3.sysbus_xhci), "dma"); } -static void versal_usb2_class_init(ObjectClass *klass, void *data) +static void versal_usb2_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/usb/xlnx-versal-usb2-ctrl-regs.c b/hw/usb/xlnx-versal-usb2-ctrl-regs.c index 66c793a602..4114672d4f 100644 --- a/hw/usb/xlnx-versal-usb2-ctrl-regs.c +++ b/hw/usb/xlnx-versal-usb2-ctrl-regs.c @@ -202,7 +202,7 @@ static const VMStateDescription vmstate_usb2_ctrl_regs = { } }; -static void usb2_ctrl_regs_class_init(ObjectClass *klass, void *data) +static void usb2_ctrl_regs_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/vfio/amd-xgbe.c b/hw/vfio/amd-xgbe.c index 5927503b5c..58f590e385 100644 --- a/hw/vfio/amd-xgbe.c +++ b/hw/vfio/amd-xgbe.c @@ -34,7 +34,7 @@ static const VMStateDescription vfio_platform_amd_xgbe_vmstate = { .unmigratable = 1, }; -static void vfio_amd_xgbe_class_init(ObjectClass *klass, void *data) +static void vfio_amd_xgbe_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VFIOAmdXgbeDeviceClass *vcxc = diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index d6575d7c44..a8f0e6ae14 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -241,7 +241,7 @@ static void vfio_ap_set_fd(Object *obj, const char *str, Error **errp) } #endif -static void vfio_ap_class_init(ObjectClass *klass, void *data) +static void vfio_ap_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/vfio/calxeda-xgmac.c b/hw/vfio/calxeda-xgmac.c index a5ef262def..03f2ff5763 100644 --- a/hw/vfio/calxeda-xgmac.c +++ b/hw/vfio/calxeda-xgmac.c @@ -34,7 +34,7 @@ static const VMStateDescription vfio_platform_calxeda_xgmac_vmstate = { .unmigratable = 1, }; -static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, void *data) +static void vfio_calxeda_xgmac_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VFIOCalxedaXgmacDeviceClass *vcxc = diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index 29e804e122..a381ab09ff 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -689,7 +689,7 @@ static void vfio_ccw_set_fd(Object *obj, const char *str, Error **errp) } #endif -static void vfio_ccw_class_init(ObjectClass *klass, void *data) +static void vfio_ccw_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); S390CCWDeviceClass *cdc = S390_CCW_DEVICE_CLASS(klass); diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 812d5edbcf..63fbe2b054 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -1121,7 +1121,7 @@ static int vfio_legacy_pci_hot_reset(VFIODevice *vbasedev, bool single) return ret; } -static void vfio_iommu_legacy_class_init(ObjectClass *klass, void *data) +static void vfio_iommu_legacy_class_init(ObjectClass *klass, const void *data) { VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); @@ -1185,7 +1185,7 @@ static void vfio_iommu_legacy_instance_init(Object *obj) QLIST_INIT(&container->group_list); } -static void hiod_legacy_vfio_class_init(ObjectClass *oc, void *data) +static void hiod_legacy_vfio_class_init(ObjectClass *oc, const void *data) { HostIOMMUDeviceClass *hioc = HOST_IOMMU_DEVICE_CLASS(oc); diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 265fffc2aa..fd55b8d884 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -301,7 +301,8 @@ static void vfio_pci_igd_lpc_bridge_realize(PCIDevice *pdev, Error **errp) } } -static void vfio_pci_igd_lpc_bridge_class_init(ObjectClass *klass, void *data) +static void vfio_pci_igd_lpc_bridge_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 42c8412bbf..911da7d86d 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -786,7 +786,7 @@ static int iommufd_cdev_pci_hot_reset(VFIODevice *vbasedev, bool single) return ret; } -static void vfio_iommu_iommufd_class_init(ObjectClass *klass, void *data) +static void vfio_iommu_iommufd_class_init(ObjectClass *klass, const void *data) { VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); @@ -846,7 +846,7 @@ hiod_iommufd_vfio_get_page_size_mask(HostIOMMUDevice *hiod) } -static void hiod_iommufd_vfio_class_init(ObjectClass *oc, void *data) +static void hiod_iommufd_vfio_class_init(ObjectClass *oc, const void *data) { HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_CLASS(oc); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index f87f3ccbe1..0db9f03846 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3409,7 +3409,7 @@ static void vfio_pci_set_fd(Object *obj, const char *str, Error **errp) } #endif -static void vfio_pci_dev_class_init(ObjectClass *klass, void *data) +static void vfio_pci_dev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); @@ -3567,7 +3567,8 @@ static const Property vfio_pci_dev_nohotplug_properties[] = { ON_OFF_AUTO_AUTO), }; -static void vfio_pci_nohotplug_dev_class_init(ObjectClass *klass, void *data) +static void vfio_pci_nohotplug_dev_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index f273ae9a51..3f8d092f7f 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -658,7 +658,7 @@ static void vfio_platform_set_fd(Object *obj, const char *str, Error **errp) } #endif -static void vfio_platform_class_init(ObjectClass *klass, void *data) +static void vfio_platform_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SysBusDeviceClass *sbc = SYS_BUS_DEVICE_CLASS(klass); diff --git a/hw/vfio/spapr.c b/hw/vfio/spapr.c index 66a2d2bb0d..bc795bdaf1 100644 --- a/hw/vfio/spapr.c +++ b/hw/vfio/spapr.c @@ -534,7 +534,7 @@ static bool vfio_spapr_container_setup(VFIOContainerBase *bcontainer, return false; } -static void vfio_iommu_spapr_class_init(ObjectClass *klass, void *data) +static void vfio_iommu_spapr_class_init(ObjectClass *klass, const void *data) { VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); diff --git a/hw/virtio/vdpa-dev-pci.c b/hw/virtio/vdpa-dev-pci.c index 787926801a..3068112146 100644 --- a/hw/virtio/vdpa-dev-pci.c +++ b/hw/virtio/vdpa-dev-pci.c @@ -70,7 +70,8 @@ vhost_vdpa_device_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(DEVICE(&dev->vdev), BUS(&vpci_dev->bus), errp); } -static void vhost_vdpa_device_pci_class_init(ObjectClass *klass, void *data) +static void vhost_vdpa_device_pci_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c index a7e73b1c99..dd8837ce4e 100644 --- a/hw/virtio/vdpa-dev.c +++ b/hw/virtio/vdpa-dev.c @@ -353,7 +353,7 @@ static const VMStateDescription vmstate_vhost_vdpa_device = { }, }; -static void vhost_vdpa_device_class_init(ObjectClass *klass, void *data) +static void vhost_vdpa_device_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-scsi-pci.c b/hw/virtio/vhost-scsi-pci.c index 3778f6131e..7399acef8e 100644 --- a/hw/virtio/vhost-scsi-pci.c +++ b/hw/virtio/vhost-scsi-pci.c @@ -61,7 +61,7 @@ static void vhost_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_scsi_pci_class_init(ObjectClass *klass, void *data) +static void vhost_scsi_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c index 2bc3423326..77143320a2 100644 --- a/hw/virtio/vhost-user-base.c +++ b/hw/virtio/vhost-user-base.c @@ -348,7 +348,7 @@ static void vub_device_unrealize(DeviceState *dev) do_vhost_user_cleanup(vdev, vub); } -static void vub_class_init(ObjectClass *klass, void *data) +static void vub_class_init(ObjectClass *klass, const void *data) { VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-user-blk-pci.c b/hw/virtio/vhost-user-blk-pci.c index 1767ef2c9c..904369f5a3 100644 --- a/hw/virtio/vhost-user-blk-pci.c +++ b/hw/virtio/vhost-user-blk-pci.c @@ -65,7 +65,7 @@ static void vhost_user_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_blk_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_blk_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-device-pci.c b/hw/virtio/vhost-user-device-pci.c index efaf55d3dd..f10bac874e 100644 --- a/hw/virtio/vhost-user-device-pci.c +++ b/hw/virtio/vhost-user-device-pci.c @@ -31,7 +31,8 @@ static void vhost_user_device_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_device_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_device_pci_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-device.c b/hw/virtio/vhost-user-device.c index 86eba138b4..3939bdf755 100644 --- a/hw/virtio/vhost-user-device.c +++ b/hw/virtio/vhost-user-device.c @@ -37,7 +37,7 @@ static const Property vud_properties[] = { DEFINE_PROP_UINT32("config_size", VHostUserBase, config_size, 0), }; -static void vud_class_init(ObjectClass *klass, void *data) +static void vud_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-user-fs-pci.c b/hw/virtio/vhost-user-fs-pci.c index 116eaab907..1490c118bc 100644 --- a/hw/virtio/vhost-user-fs-pci.c +++ b/hw/virtio/vhost-user-fs-pci.c @@ -47,7 +47,7 @@ static void vhost_user_fs_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_fs_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_fs_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index 0e8d4b3823..f6d1fc8804 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -418,7 +418,7 @@ static void vuf_instance_init(Object *obj) "/filesystem@0", DEVICE(obj)); } -static void vuf_class_init(ObjectClass *klass, void *data) +static void vuf_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-user-gpio-pci.c b/hw/virtio/vhost-user-gpio-pci.c index b3028a24a1..9b165b54f8 100644 --- a/hw/virtio/vhost-user-gpio-pci.c +++ b/hw/virtio/vhost-user-gpio-pci.c @@ -32,7 +32,7 @@ static void vhost_user_gpio_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_gpio_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_gpio_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-gpio.c b/hw/virtio/vhost-user-gpio.c index 4a08814904..a7fd49b10a 100644 --- a/hw/virtio/vhost-user-gpio.c +++ b/hw/virtio/vhost-user-gpio.c @@ -36,7 +36,7 @@ static const VMStateDescription vu_gpio_vmstate = { .unmigratable = 1, }; -static void vu_gpio_class_init(ObjectClass *klass, void *data) +static void vu_gpio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass); diff --git a/hw/virtio/vhost-user-i2c-pci.c b/hw/virtio/vhost-user-i2c-pci.c index 00ac10941f..692cd66fde 100644 --- a/hw/virtio/vhost-user-i2c-pci.c +++ b/hw/virtio/vhost-user-i2c-pci.c @@ -32,7 +32,7 @@ static void vhost_user_i2c_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_i2c_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_i2c_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-i2c.c b/hw/virtio/vhost-user-i2c.c index 1c7cde503c..ae007fe97d 100644 --- a/hw/virtio/vhost-user-i2c.c +++ b/hw/virtio/vhost-user-i2c.c @@ -36,7 +36,7 @@ static const VMStateDescription vu_i2c_vmstate = { .unmigratable = 1, }; -static void vu_i2c_class_init(ObjectClass *klass, void *data) +static void vu_i2c_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass); diff --git a/hw/virtio/vhost-user-input.c b/hw/virtio/vhost-user-input.c index 917405329f..5cfc5bbb56 100644 --- a/hw/virtio/vhost-user-input.c +++ b/hw/virtio/vhost-user-input.c @@ -30,7 +30,7 @@ static const VMStateDescription vmstate_vhost_input = { .unmigratable = 1, }; -static void vhost_input_class_init(ObjectClass *klass, void *data) +static void vhost_input_class_init(ObjectClass *klass, const void *data) { VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-user-rng-pci.c b/hw/virtio/vhost-user-rng-pci.c index a4e690148d..9f45fc6f35 100644 --- a/hw/virtio/vhost-user-rng-pci.c +++ b/hw/virtio/vhost-user-rng-pci.c @@ -40,7 +40,7 @@ static void vhost_user_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_rng_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_rng_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-rng.c b/hw/virtio/vhost-user-rng.c index 5aa432e5e1..61dadcda05 100644 --- a/hw/virtio/vhost-user-rng.c +++ b/hw/virtio/vhost-user-rng.c @@ -37,7 +37,7 @@ static void vu_rng_base_realize(DeviceState *dev, Error **errp) vubs->parent_realize(dev, errp); } -static void vu_rng_class_init(ObjectClass *klass, void *data) +static void vu_rng_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass); diff --git a/hw/virtio/vhost-user-scmi-pci.c b/hw/virtio/vhost-user-scmi-pci.c index 7f53af7fce..0ab56a50bb 100644 --- a/hw/virtio/vhost-user-scmi-pci.c +++ b/hw/virtio/vhost-user-scmi-pci.c @@ -31,7 +31,7 @@ static void vhost_user_scmi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_scmi_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_scmi_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-scmi.c b/hw/virtio/vhost-user-scmi.c index 04cd36dd2e..7a0f622181 100644 --- a/hw/virtio/vhost-user-scmi.c +++ b/hw/virtio/vhost-user-scmi.c @@ -279,7 +279,7 @@ static const Property vu_scmi_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserSCMI, chardev), }; -static void vu_scmi_class_init(ObjectClass *klass, void *data) +static void vu_scmi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-user-scsi-pci.c b/hw/virtio/vhost-user-scsi-pci.c index 4a4128d961..994e51a37b 100644 --- a/hw/virtio/vhost-user-scsi-pci.c +++ b/hw/virtio/vhost-user-scsi-pci.c @@ -67,7 +67,7 @@ static void vhost_user_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_scsi_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_scsi_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-snd-pci.c b/hw/virtio/vhost-user-snd-pci.c index 0cb86b7d85..f5015fb6c4 100644 --- a/hw/virtio/vhost-user-snd-pci.c +++ b/hw/virtio/vhost-user-snd-pci.c @@ -33,7 +33,7 @@ static void vhost_user_snd_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_snd_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_snd_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-snd.c b/hw/virtio/vhost-user-snd.c index b414c75c06..732411c655 100644 --- a/hw/virtio/vhost-user-snd.c +++ b/hw/virtio/vhost-user-snd.c @@ -54,7 +54,7 @@ static void vu_snd_base_realize(DeviceState *dev, Error **errp) vubs->parent_realize(dev, errp); } -static void vu_snd_class_init(ObjectClass *klass, void *data) +static void vu_snd_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VHostUserBaseClass *vubc = VHOST_USER_BASE_CLASS(klass); diff --git a/hw/virtio/vhost-user-vsock-pci.c b/hw/virtio/vhost-user-vsock-pci.c index f730a05e78..adb877b6e0 100644 --- a/hw/virtio/vhost-user-vsock-pci.c +++ b/hw/virtio/vhost-user-vsock-pci.c @@ -46,7 +46,8 @@ static void vhost_user_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_user_vsock_pci_class_init(ObjectClass *klass, void *data) +static void vhost_user_vsock_pci_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c index d21cd4b516..2776792f59 100644 --- a/hw/virtio/vhost-user-vsock.c +++ b/hw/virtio/vhost-user-vsock.c @@ -151,7 +151,7 @@ static const Property vuv_properties[] = { DEFINE_PROP_CHR("chardev", VHostUserVSock, conf.chardev), }; -static void vuv_class_init(ObjectClass *klass, void *data) +static void vuv_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-vsock-common.c b/hw/virtio/vhost-vsock-common.c index 9ac587d20c..4b4fbb45cc 100644 --- a/hw/virtio/vhost-vsock-common.c +++ b/hw/virtio/vhost-vsock-common.c @@ -290,7 +290,7 @@ static const Property vhost_vsock_common_properties[] = { ON_OFF_AUTO_AUTO), }; -static void vhost_vsock_common_class_init(ObjectClass *klass, void *data) +static void vhost_vsock_common_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/vhost-vsock-pci.c b/hw/virtio/vhost-vsock-pci.c index 6c618ee908..0022a713d4 100644 --- a/hw/virtio/vhost-vsock-pci.c +++ b/hw/virtio/vhost-vsock-pci.c @@ -56,7 +56,7 @@ static void vhost_vsock_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void vhost_vsock_pci_class_init(ObjectClass *klass, void *data) +static void vhost_vsock_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index 940b30fa27..b73dc723c2 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -210,7 +210,7 @@ static const Property vhost_vsock_properties[] = { DEFINE_PROP_STRING("vhostfd", VHostVSock, conf.vhostfd), }; -static void vhost_vsock_class_init(ObjectClass *klass, void *data) +static void vhost_vsock_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-9p-pci.c b/hw/virtio/virtio-9p-pci.c index aa1dce8f28..594742ff65 100644 --- a/hw/virtio/virtio-9p-pci.c +++ b/hw/virtio/virtio-9p-pci.c @@ -49,7 +49,7 @@ static const Property virtio_9p_pci_properties[] = { DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, 2), }; -static void virtio_9p_pci_class_init(ObjectClass *klass, void *data) +static void virtio_9p_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-balloon-pci.c b/hw/virtio/virtio-balloon-pci.c index db7e1cb475..96e88b6b86 100644 --- a/hw/virtio/virtio-balloon-pci.c +++ b/hw/virtio/virtio-balloon-pci.c @@ -55,7 +55,7 @@ static void virtio_balloon_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_balloon_pci_class_init(ObjectClass *klass, void *data) +static void virtio_balloon_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 0d0603c674..91510ec2e2 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -1058,7 +1058,7 @@ static const Property virtio_balloon_properties[] = { IOThread *), }; -static void virtio_balloon_class_init(ObjectClass *klass, void *data) +static void virtio_balloon_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-blk-pci.c b/hw/virtio/virtio-blk-pci.c index fc06cec656..fd33bbd7e8 100644 --- a/hw/virtio/virtio-blk-pci.c +++ b/hw/virtio/virtio-blk-pci.c @@ -63,7 +63,7 @@ static void virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_blk_pci_class_init(ObjectClass *klass, void *data) +static void virtio_blk_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-bus.c b/hw/virtio/virtio-bus.c index d1c79c567b..11adfbf3ab 100644 --- a/hw/virtio/virtio-bus.c +++ b/hw/virtio/virtio-bus.c @@ -348,7 +348,7 @@ bool virtio_bus_device_iommu_enabled(VirtIODevice *vdev) return klass->iommu_enabled(qbus->parent); } -static void virtio_bus_class_init(ObjectClass *klass, void *data) +static void virtio_bus_class_init(ObjectClass *klass, const void *data) { BusClass *bus_class = BUS_CLASS(klass); bus_class->get_dev_path = virtio_bus_get_dev_path; diff --git a/hw/virtio/virtio-crypto-pci.c b/hw/virtio/virtio-crypto-pci.c index 8699481375..868abc03a9 100644 --- a/hw/virtio/virtio-crypto-pci.c +++ b/hw/virtio/virtio-crypto-pci.c @@ -59,7 +59,7 @@ static void virtio_crypto_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } } -static void virtio_crypto_pci_class_init(ObjectClass *klass, void *data) +static void virtio_crypto_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index a1b3c90618..e24d6914b6 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -1264,7 +1264,7 @@ static struct vhost_dev *virtio_crypto_get_vhost(VirtIODevice *vdev) return &vhost_crypto->dev; } -static void virtio_crypto_class_init(ObjectClass *klass, void *data) +static void virtio_crypto_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-input-pci.c b/hw/virtio/virtio-input-pci.c index 9e3c106777..3be5358b4c 100644 --- a/hw/virtio/virtio-input-pci.c +++ b/hw/virtio/virtio-input-pci.c @@ -50,7 +50,7 @@ static void virtio_input_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_input_pci_class_init(ObjectClass *klass, void *data) +static void virtio_input_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); @@ -63,7 +63,8 @@ static void virtio_input_pci_class_init(ObjectClass *klass, void *data) pcidev_k->class_id = PCI_CLASS_INPUT_OTHER; } -static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, void *data) +static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, + const void *data) { PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); @@ -71,7 +72,7 @@ static void virtio_input_hid_kbd_pci_class_init(ObjectClass *klass, void *data) } static void virtio_input_hid_mouse_pci_class_init(ObjectClass *klass, - void *data) + const void *data) { PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-iommu-pci.c b/hw/virtio/virtio-iommu-pci.c index 97e03ce803..8123c6f83a 100644 --- a/hw/virtio/virtio-iommu-pci.c +++ b/hw/virtio/virtio-iommu-pci.c @@ -73,7 +73,7 @@ static void virtio_iommu_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_iommu_pci_class_init(ObjectClass *klass, void *data) +static void virtio_iommu_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index b6e7e01ef7..54060988ef 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -1664,7 +1664,7 @@ static const Property virtio_iommu_properties[] = { DEFINE_PROP_UINT8("aw-bits", VirtIOIOMMU, aw_bits, 64), }; -static void virtio_iommu_class_init(ObjectClass *klass, void *data) +static void virtio_iommu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -1690,7 +1690,7 @@ static void virtio_iommu_class_init(ObjectClass *klass, void *data) } static void virtio_iommu_memory_region_class_init(ObjectClass *klass, - void *data) + const void *data) { IOMMUMemoryRegionClass *imrc = IOMMU_MEMORY_REGION_CLASS(klass); diff --git a/hw/virtio/virtio-mem-pci.c b/hw/virtio/virtio-mem-pci.c index 6cc5f0fd3b..f592eb1a78 100644 --- a/hw/virtio/virtio-mem-pci.c +++ b/hw/virtio/virtio-mem-pci.c @@ -163,7 +163,7 @@ static const Property virtio_mem_pci_class_properties[] = { DEV_NVECTORS_UNSPECIFIED), }; -static void virtio_mem_pci_class_init(ObjectClass *klass, void *data) +static void virtio_mem_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index c7968ee0c6..0e14dd37d6 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -1865,7 +1865,7 @@ static void virtio_mem_unplug_request_check(VirtIOMEM *vmem, Error **errp) } } -static void virtio_mem_class_init(ObjectClass *klass, void *data) +static void virtio_mem_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -1957,7 +1957,8 @@ static void virtio_mem_system_reset_hold(Object *obj, ResetType type) virtio_mem_unplug_all(vmem); } -static void virtio_mem_system_reset_class_init(ObjectClass *klass, void *data) +static void virtio_mem_system_reset_class_init(ObjectClass *klass, + const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/virtio/virtio-mmio.c b/hw/virtio/virtio-mmio.c index 029817139b..532c67107b 100644 --- a/hw/virtio/virtio-mmio.c +++ b/hw/virtio/virtio-mmio.c @@ -784,7 +784,7 @@ static void virtio_mmio_realizefn(DeviceState *d, Error **errp) sysbus_init_mmio(sbd, &proxy->iomem); } -static void virtio_mmio_class_init(ObjectClass *klass, void *data) +static void virtio_mmio_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -855,7 +855,7 @@ static void virtio_mmio_vmstate_change(DeviceState *d, bool running) } } -static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data) +static void virtio_mmio_bus_class_init(ObjectClass *klass, const void *data) { BusClass *bus_class = BUS_CLASS(klass); VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); diff --git a/hw/virtio/virtio-net-pci.c b/hw/virtio/virtio-net-pci.c index e18953ad67..8cf9788bc3 100644 --- a/hw/virtio/virtio-net-pci.c +++ b/hw/virtio/virtio-net-pci.c @@ -63,7 +63,7 @@ static void virtio_net_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_net_pci_class_init(ObjectClass *klass, void *data) +static void virtio_net_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-nsm-pci.c b/hw/virtio/virtio-nsm-pci.c index dca797315a..ec243963e1 100644 --- a/hw/virtio/virtio-nsm-pci.c +++ b/hw/virtio/virtio-nsm-pci.c @@ -40,7 +40,7 @@ static void virtio_nsm_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } } -static void virtio_nsm_pci_class_init(ObjectClass *klass, void *data) +static void virtio_nsm_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-nsm.c b/hw/virtio/virtio-nsm.c index accf7334e3..3bf5e7009a 100644 --- a/hw/virtio/virtio-nsm.c +++ b/hw/virtio/virtio-nsm.c @@ -1708,7 +1708,7 @@ static const Property virtio_nsm_properties[] = { DEFINE_PROP_STRING("module-id", VirtIONSM, module_id), }; -static void virtio_nsm_class_init(ObjectClass *klass, void *data) +static void virtio_nsm_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 3ca3f849d3..c0fd3db063 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2399,7 +2399,7 @@ static int virtio_pci_sync_config(DeviceState *dev, Error **errp) return qdev_sync_config(DEVICE(vdev), errp); } -static void virtio_pci_class_init(ObjectClass *klass, void *data) +static void virtio_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); @@ -2433,7 +2433,7 @@ static const Property virtio_pci_generic_properties[] = { DEFINE_PROP_BOOL("disable-modern", VirtIOPCIProxy, disable_modern, false), }; -static void virtio_pci_base_class_init(ObjectClass *klass, void *data) +static void virtio_pci_base_class_init(ObjectClass *klass, const void *data) { const VirtioPCIDeviceTypeInfo *t = data; if (t->class_init) { @@ -2441,7 +2441,7 @@ static void virtio_pci_base_class_init(ObjectClass *klass, void *data) } } -static void virtio_pci_generic_class_init(ObjectClass *klass, void *data) +static void virtio_pci_generic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -2586,7 +2586,7 @@ static void virtio_pci_bus_new(VirtioBusState *bus, size_t bus_size, qbus_init(bus, bus_size, TYPE_VIRTIO_PCI_BUS, qdev, virtio_bus_name); } -static void virtio_pci_bus_class_init(ObjectClass *klass, void *data) +static void virtio_pci_bus_class_init(ObjectClass *klass, const void *data) { BusClass *bus_class = BUS_CLASS(klass); VirtioBusClass *k = VIRTIO_BUS_CLASS(klass); diff --git a/hw/virtio/virtio-pmem-pci.c b/hw/virtio/virtio-pmem-pci.c index cfe7f3b67c..babd91c21f 100644 --- a/hw/virtio/virtio-pmem-pci.c +++ b/hw/virtio/virtio-pmem-pci.c @@ -80,7 +80,7 @@ static void virtio_pmem_pci_fill_device_info(const MemoryDeviceState *md, info->type = MEMORY_DEVICE_INFO_KIND_VIRTIO_PMEM; } -static void virtio_pmem_pci_class_init(ObjectClass *klass, void *data) +static void virtio_pmem_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-pmem.c b/hw/virtio/virtio-pmem.c index adb3268bd4..3416ea1827 100644 --- a/hw/virtio/virtio-pmem.c +++ b/hw/virtio/virtio-pmem.c @@ -161,7 +161,7 @@ static const Property virtio_pmem_properties[] = { TYPE_MEMORY_BACKEND, HostMemoryBackend *), }; -static void virtio_pmem_class_init(ObjectClass *klass, void *data) +static void virtio_pmem_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-rng-pci.c b/hw/virtio/virtio-rng-pci.c index a94ff767b2..39b600356e 100644 --- a/hw/virtio/virtio-rng-pci.c +++ b/hw/virtio/virtio-rng-pci.c @@ -53,7 +53,7 @@ static void virtio_rng_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) } } -static void virtio_rng_pci_class_init(ObjectClass *klass, void *data) +static void virtio_rng_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index a515fc5cd9..dcb3c71d6a 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -260,7 +260,7 @@ static const Property virtio_rng_properties[] = { DEFINE_PROP_LINK("rng", VirtIORNG, conf.rng, TYPE_RNG_BACKEND, RngBackend *), }; -static void virtio_rng_class_init(ObjectClass *klass, void *data) +static void virtio_rng_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/virtio/virtio-scsi-pci.c b/hw/virtio/virtio-scsi-pci.c index d44fd2fffb..af87759207 100644 --- a/hw/virtio/virtio-scsi-pci.c +++ b/hw/virtio/virtio-scsi-pci.c @@ -72,7 +72,7 @@ static void virtio_scsi_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) qdev_realize(vdev, BUS(&vpci_dev->bus), errp); } -static void virtio_scsi_pci_class_init(ObjectClass *klass, void *data) +static void virtio_scsi_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio-serial-pci.c b/hw/virtio/virtio-serial-pci.c index b5b77eb266..3f212ffe52 100644 --- a/hw/virtio/virtio-serial-pci.c +++ b/hw/virtio/virtio-serial-pci.c @@ -76,7 +76,7 @@ static const Property virtio_serial_pci_properties[] = { DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), }; -static void virtio_serial_pci_class_init(ObjectClass *klass, void *data) +static void virtio_serial_pci_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index ffc12635ae..f0fa36f8ce 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -4138,7 +4138,7 @@ void virtio_device_release_ioeventfd(VirtIODevice *vdev) virtio_bus_release_ioeventfd(vbus); } -static void virtio_device_class_init(ObjectClass *klass, void *data) +static void virtio_device_class_init(ObjectClass *klass, const void *data) { /* Set the default value here. */ VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); diff --git a/hw/vmapple/aes.c b/hw/vmapple/aes.c index 3a7641ab4b..a4853a98f8 100644 --- a/hw/vmapple/aes.c +++ b/hw/vmapple/aes.c @@ -558,7 +558,7 @@ static void aes_init(Object *obj) s->as = &address_space_memory; } -static void aes_class_init(ObjectClass *klass, void *data) +static void aes_class_init(ObjectClass *klass, const void *data) { ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/vmapple/bdif.c b/hw/vmapple/bdif.c index 5827dd2aab..5ccd374581 100644 --- a/hw/vmapple/bdif.c +++ b/hw/vmapple/bdif.c @@ -250,7 +250,7 @@ static const Property bdif_properties[] = { DEFINE_PROP_DRIVE("root", VMAppleBdifState, root), }; -static void bdif_class_init(ObjectClass *klass, void *data) +static void bdif_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/vmapple/cfg.c b/hw/vmapple/cfg.c index 63414d801f..3d58a29f69 100644 --- a/hw/vmapple/cfg.c +++ b/hw/vmapple/cfg.c @@ -168,7 +168,7 @@ static const Property vmapple_cfg_properties[] = { DEFINE_PROP_STRING("soc_name", VMAppleCfgState, soc_name), }; -static void vmapple_cfg_class_init(ObjectClass *klass, void *data) +static void vmapple_cfg_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); diff --git a/hw/vmapple/virtio-blk.c b/hw/vmapple/virtio-blk.c index aa3f18c47d..532b5649ab 100644 --- a/hw/vmapple/virtio-blk.c +++ b/hw/vmapple/virtio-blk.c @@ -82,7 +82,7 @@ static void vmapple_virtio_blk_get_config(VirtIODevice *vdev, uint8_t *config) stl_he_p(&blkcfg->max_secure_erase_sectors, dev->apple_type); } -static void vmapple_virtio_blk_class_init(ObjectClass *klass, void *data) +static void vmapple_virtio_blk_class_init(ObjectClass *klass, const void *data) { VirtIOBlkClass *vbk = VIRTIO_BLK_CLASS(klass); VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); @@ -165,7 +165,8 @@ static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **err PCI_DEVICE_ID_APPLE_VIRTIO_BLK); } -static void vmapple_virtio_blk_pci_class_init(ObjectClass *klass, void *data) +static void vmapple_virtio_blk_pci_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); diff --git a/hw/vmapple/vmapple.c b/hw/vmapple/vmapple.c index fa117bf151..16e6110b68 100644 --- a/hw/vmapple/vmapple.c +++ b/hw/vmapple/vmapple.c @@ -570,7 +570,7 @@ static GlobalProperty vmapple_compat_defaults[] = { { TYPE_XHCI_PCI, "conditional-intr-mapping", "on" }, }; -static void vmapple_machine_class_init(ObjectClass *oc, void *data) +static void vmapple_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/watchdog/allwinner-wdt.c b/hw/watchdog/allwinner-wdt.c index 78f4f9d6f6..8fcd776675 100644 --- a/hw/watchdog/allwinner-wdt.c +++ b/hw/watchdog/allwinner-wdt.c @@ -348,7 +348,7 @@ static void allwinner_wdt_realize(DeviceState *dev, Error **errp) ptimer_transaction_commit(s->timer); } -static void allwinner_wdt_class_init(ObjectClass *klass, void *data) +static void allwinner_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); @@ -358,7 +358,7 @@ static void allwinner_wdt_class_init(ObjectClass *klass, void *data) dc->vmsd = &allwinner_wdt_vmstate; } -static void allwinner_wdt_sun4i_class_init(ObjectClass *klass, void *data) +static void allwinner_wdt_sun4i_class_init(ObjectClass *klass, const void *data) { AwWdtClass *awc = AW_WDT_CLASS(klass); @@ -371,7 +371,7 @@ static void allwinner_wdt_sun4i_class_init(ObjectClass *klass, void *data) awc->get_intv_value = allwinner_wdt_sun4i_get_intv_value; } -static void allwinner_wdt_sun6i_class_init(ObjectClass *klass, void *data) +static void allwinner_wdt_sun6i_class_init(ObjectClass *klass, const void *data) { AwWdtClass *awc = AW_WDT_CLASS(klass); diff --git a/hw/watchdog/cmsdk-apb-watchdog.c b/hw/watchdog/cmsdk-apb-watchdog.c index a52121dc44..6a8d07ca56 100644 --- a/hw/watchdog/cmsdk-apb-watchdog.c +++ b/hw/watchdog/cmsdk-apb-watchdog.c @@ -394,7 +394,7 @@ static const VMStateDescription cmsdk_apb_watchdog_vmstate = { } }; -static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, void *data) +static void cmsdk_apb_watchdog_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/watchdog/sbsa_gwdt.c b/hw/watchdog/sbsa_gwdt.c index 1dd21392a9..ce84849df0 100644 --- a/hw/watchdog/sbsa_gwdt.c +++ b/hw/watchdog/sbsa_gwdt.c @@ -271,7 +271,7 @@ static const Property wdt_sbsa_gwdt_props[] = { 62500000), }; -static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, void *data) +static void wdt_sbsa_gwdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/watchdog/spapr_watchdog.c b/hw/watchdog/spapr_watchdog.c index 2bb1d3c532..5b3f50de3a 100644 --- a/hw/watchdog/spapr_watchdog.c +++ b/hw/watchdog/spapr_watchdog.c @@ -249,7 +249,7 @@ static void spapr_wdt_realize(DeviceState *dev, Error **errp) &w->leave_others, OBJ_PROP_FLAG_READ); } -static void spapr_wdt_class_init(ObjectClass *oc, void *data) +static void spapr_wdt_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/hw/watchdog/wdt_aspeed.c b/hw/watchdog/wdt_aspeed.c index a503b2aea7..30226435ef 100644 --- a/hw/watchdog/wdt_aspeed.c +++ b/hw/watchdog/wdt_aspeed.c @@ -307,7 +307,7 @@ static const Property aspeed_wdt_properties[] = { AspeedSCUState *), }; -static void aspeed_wdt_class_init(ObjectClass *klass, void *data) +static void aspeed_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -329,7 +329,7 @@ static const TypeInfo aspeed_wdt_info = { .abstract = true, }; -static void aspeed_2400_wdt_class_init(ObjectClass *klass, void *data) +static void aspeed_2400_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); @@ -366,7 +366,7 @@ static void aspeed_2500_wdt_reset_pulse(AspeedWDTState *s, uint32_t property) } } -static void aspeed_2500_wdt_class_init(ObjectClass *klass, void *data) +static void aspeed_2500_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); @@ -389,7 +389,7 @@ static const TypeInfo aspeed_2500_wdt_info = { .class_init = aspeed_2500_wdt_class_init, }; -static void aspeed_2600_wdt_class_init(ObjectClass *klass, void *data) +static void aspeed_2600_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); @@ -412,7 +412,7 @@ static const TypeInfo aspeed_2600_wdt_info = { .class_init = aspeed_2600_wdt_class_init, }; -static void aspeed_1030_wdt_class_init(ObjectClass *klass, void *data) +static void aspeed_1030_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); @@ -435,7 +435,7 @@ static const TypeInfo aspeed_1030_wdt_info = { .class_init = aspeed_1030_wdt_class_init, }; -static void aspeed_2700_wdt_class_init(ObjectClass *klass, void *data) +static void aspeed_2700_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedWDTClass *awc = ASPEED_WDT_CLASS(klass); diff --git a/hw/watchdog/wdt_diag288.c b/hw/watchdog/wdt_diag288.c index 39f2894f21..1275353e8e 100644 --- a/hw/watchdog/wdt_diag288.c +++ b/hw/watchdog/wdt_diag288.c @@ -108,7 +108,7 @@ static void wdt_diag288_unrealize(DeviceState *dev) timer_free(diag288->timer); } -static void wdt_diag288_class_init(ObjectClass *klass, void *data) +static void wdt_diag288_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); DIAG288Class *diag288 = DIAG288_CLASS(klass); diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index aa1d0866c8..1536e1fe03 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -457,7 +457,7 @@ static void i6300esb_exit(PCIDevice *dev) timer_free(d->timer); } -static void i6300esb_class_init(ObjectClass *klass, void *data) +static void i6300esb_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/watchdog/wdt_ib700.c b/hw/watchdog/wdt_ib700.c index 23519e058e..51a26a4cbb 100644 --- a/hw/watchdog/wdt_ib700.c +++ b/hw/watchdog/wdt_ib700.c @@ -128,7 +128,7 @@ static void wdt_ib700_reset(DeviceState *dev) timer_del(s->timer); } -static void wdt_ib700_class_init(ObjectClass *klass, void *data) +static void wdt_ib700_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/watchdog/wdt_imx2.c b/hw/watchdog/wdt_imx2.c index 18e40bd466..10151a15d0 100644 --- a/hw/watchdog/wdt_imx2.c +++ b/hw/watchdog/wdt_imx2.c @@ -286,7 +286,7 @@ static const Property imx2_wdt_properties[] = { false), }; -static void imx2_wdt_class_init(ObjectClass *klass, void *data) +static void imx2_wdt_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index f808a01813..e6272282cd 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -380,7 +380,7 @@ static void xen_bus_unplug_request(HotplugHandler *hotplug, xen_device_unplug(xendev, errp); } -static void xen_bus_class_init(ObjectClass *class, void *data) +static void xen_bus_class_init(ObjectClass *class, const void *data) { BusClass *bus_class = BUS_CLASS(class); HotplugHandlerClass *hotplug_class = HOTPLUG_HANDLER_CLASS(class); @@ -1107,7 +1107,7 @@ static const Property xen_device_props[] = { DOMID_INVALID), }; -static void xen_device_class_init(ObjectClass *class, void *data) +static void xen_device_class_init(ObjectClass *class, const void *data) { DeviceClass *dev_class = DEVICE_CLASS(class); diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index bf58db0ca6..d6fe7d4f3e 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -635,7 +635,7 @@ int xen_be_bind_evtchn(struct XenLegacyDevice *xendev) } -static void xendev_class_init(ObjectClass *klass, void *data) +static void xendev_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -650,7 +650,7 @@ static const TypeInfo xendev_type_info = { .instance_size = sizeof(XenLegacyDevice), }; -static void xen_sysbus_class_init(ObjectClass *klass, void *data) +static void xen_sysbus_class_init(ObjectClass *klass, const void *data) { HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); diff --git a/hw/xen/xen-pvh-common.c b/hw/xen/xen-pvh-common.c index d675f7a8ae..b93ff80c85 100644 --- a/hw/xen/xen-pvh-common.c +++ b/hw/xen/xen-pvh-common.c @@ -369,7 +369,7 @@ do { \ #endif } -static void xen_pvh_class_init(ObjectClass *oc, void *data) +static void xen_pvh_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 9487f68f2e..7f9c351d96 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -1047,7 +1047,7 @@ static void xen_igd_clear_slot(DeviceState *qdev, Error **errp) xpdc->pci_qdev_realize(qdev, errp); } -static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) +static void xen_pci_passthrough_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c index 6c2e3f4840..264851ece3 100644 --- a/hw/xen/xen_pt_graphics.c +++ b/hw/xen/xen_pt_graphics.c @@ -347,7 +347,7 @@ static const IGDDeviceIDInfo igd_combo_id_infos[] = { {0x162D, 0x9cc3, 0x03}, /* BDWGT3SRVR, BDWM_w7 */ }; -static void isa_bridge_class_init(ObjectClass *klass, void *data) +static void isa_bridge_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); diff --git a/hw/xtensa/xtfpga.c b/hw/xtensa/xtfpga.c index 3bd0ef8268..6efffae466 100644 --- a/hw/xtensa/xtfpga.c +++ b/hw/xtensa/xtfpga.c @@ -585,7 +585,7 @@ static void xtfpga_kc705_nommu_init(MachineState *machine) xtfpga_init(&kc705_board, machine); } -static void xtfpga_lx60_class_init(ObjectClass *oc, void *data) +static void xtfpga_lx60_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -602,7 +602,7 @@ static const TypeInfo xtfpga_lx60_type = { .class_init = xtfpga_lx60_class_init, }; -static void xtfpga_lx60_nommu_class_init(ObjectClass *oc, void *data) +static void xtfpga_lx60_nommu_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -619,7 +619,7 @@ static const TypeInfo xtfpga_lx60_nommu_type = { .class_init = xtfpga_lx60_nommu_class_init, }; -static void xtfpga_lx200_class_init(ObjectClass *oc, void *data) +static void xtfpga_lx200_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -636,7 +636,7 @@ static const TypeInfo xtfpga_lx200_type = { .class_init = xtfpga_lx200_class_init, }; -static void xtfpga_lx200_nommu_class_init(ObjectClass *oc, void *data) +static void xtfpga_lx200_nommu_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -653,7 +653,7 @@ static const TypeInfo xtfpga_lx200_nommu_type = { .class_init = xtfpga_lx200_nommu_class_init, }; -static void xtfpga_ml605_class_init(ObjectClass *oc, void *data) +static void xtfpga_ml605_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -670,7 +670,7 @@ static const TypeInfo xtfpga_ml605_type = { .class_init = xtfpga_ml605_class_init, }; -static void xtfpga_ml605_nommu_class_init(ObjectClass *oc, void *data) +static void xtfpga_ml605_nommu_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -687,7 +687,7 @@ static const TypeInfo xtfpga_ml605_nommu_type = { .class_init = xtfpga_ml605_nommu_class_init, }; -static void xtfpga_kc705_class_init(ObjectClass *oc, void *data) +static void xtfpga_kc705_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -704,7 +704,7 @@ static const TypeInfo xtfpga_kc705_type = { .class_init = xtfpga_kc705_class_init, }; -static void xtfpga_kc705_nommu_class_init(ObjectClass *oc, void *data) +static void xtfpga_kc705_nommu_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/include/hw/boards.h b/include/hw/boards.h index 8556e01e21..765dc8dd35 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -745,7 +745,7 @@ struct MachineState { } while (0) #define DEFINE_MACHINE(namestr, machine_initfn) \ - static void machine_initfn##_class_init(ObjectClass *oc, void *data) \ + static void machine_initfn##_class_init(ObjectClass *oc, const void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ machine_initfn(mc); \ diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 8677dc8950..9563674e2d 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -306,7 +306,8 @@ extern GlobalProperty pc_compat_2_4[]; extern const size_t pc_compat_2_4_len; #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ - static void pc_machine_##suffix##_class_init(ObjectClass *oc, void *data) \ + static void pc_machine_##suffix##_class_init(ObjectClass *oc, \ + const void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ optsfn(mc); \ @@ -331,7 +332,7 @@ extern const size_t pc_compat_2_4_len; } \ static void MACHINE_VER_SYM(class_init, namesym, __VA_ARGS__)( \ ObjectClass *oc, \ - void *data) \ + const void *data) \ { \ MachineClass *mc = MACHINE_CLASS(oc); \ MACHINE_VER_SYM(options, namesym, __VA_ARGS__)(mc); \ diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h index 971c5fabd4..567a9b0a9d 100644 --- a/include/hw/virtio/virtio-pci.h +++ b/include/hw/virtio/virtio-pci.h @@ -255,7 +255,7 @@ typedef struct VirtioPCIDeviceTypeInfo { size_t class_size; void (*instance_init)(Object *obj); void (*instance_finalize)(Object *obj); - void (*class_init)(ObjectClass *klass, void *data); + void (*class_init)(ObjectClass *klass, const void *data); InterfaceInfo *interfaces; } VirtioPCIDeviceTypeInfo; diff --git a/include/qom/object.h b/include/qom/object.h index 7bb14ca706..2fb86f00a6 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -280,7 +280,7 @@ struct Object static void \ module_obj_name##_finalize(Object *obj); \ static void \ - module_obj_name##_class_init(ObjectClass *oc, void *data); \ + module_obj_name##_class_init(ObjectClass *oc, const void *data); \ static void \ module_obj_name##_init(Object *obj); \ \ @@ -486,7 +486,7 @@ struct TypeInfo bool abstract; size_t class_size; - void (*class_init)(ObjectClass *klass, void *data); + void (*class_init)(ObjectClass *klass, const void *data); void (*class_base_init)(ObjectClass *klass, const void *data); void *class_data; diff --git a/io/channel-buffer.c b/io/channel-buffer.c index 8096180f85..189fa67ac2 100644 --- a/io/channel-buffer.c +++ b/io/channel-buffer.c @@ -225,7 +225,7 @@ static GSource *qio_channel_buffer_create_watch(QIOChannel *ioc, static void qio_channel_buffer_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/io/channel-command.c b/io/channel-command.c index 6d5f64e146..8966dd3a2b 100644 --- a/io/channel-command.c +++ b/io/channel-command.c @@ -358,7 +358,7 @@ static GSource *qio_channel_command_create_watch(QIOChannel *ioc, static void qio_channel_command_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/io/channel-file.c b/io/channel-file.c index 2ea8d08360..ca3f180cc2 100644 --- a/io/channel-file.c +++ b/io/channel-file.c @@ -290,7 +290,7 @@ static GSource *qio_channel_file_create_watch(QIOChannel *ioc, } static void qio_channel_file_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/io/channel-null.c b/io/channel-null.c index ef99586348..49f1c80a54 100644 --- a/io/channel-null.c +++ b/io/channel-null.c @@ -207,7 +207,7 @@ qio_channel_null_create_watch(QIOChannel *ioc, static void qio_channel_null_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/io/channel-socket.c b/io/channel-socket.c index 608bcf066e..088b49ffdb 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -949,7 +949,7 @@ static GSource *qio_channel_socket_create_watch(QIOChannel *ioc, } static void qio_channel_socket_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/io/channel-tls.c b/io/channel-tls.c index caf8301a9e..db2ac1deae 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -561,7 +561,7 @@ qio_channel_tls_get_session(QIOChannelTLS *ioc) } static void qio_channel_tls_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/io/channel-websock.c b/io/channel-websock.c index 55192b770a..08ddb274f0 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -1308,7 +1308,7 @@ static GSource *qio_channel_websock_create_watch(QIOChannel *ioc, } static void qio_channel_websock_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/iothread.c b/iothread.c index 589bcd3552..8810376dce 100644 --- a/iothread.c +++ b/iothread.c @@ -292,7 +292,7 @@ static void iothread_set_poll_param(Object *obj, Visitor *v, } } -static void iothread_class_init(ObjectClass *klass, void *class_data) +static void iothread_class_init(ObjectClass *klass, const void *class_data) { EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(klass); diff --git a/migration/channel-block.c b/migration/channel-block.c index b0477f5b6d..97de5a691b 100644 --- a/migration/channel-block.c +++ b/migration/channel-block.c @@ -170,7 +170,7 @@ qio_channel_block_set_aio_fd_handler(QIOChannel *ioc, static void qio_channel_block_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/migration/migration.c b/migration/migration.c index d46e776e24..55ec4bfab6 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -4016,7 +4016,7 @@ void migration_connect(MigrationState *s, Error *error_in) migration_cleanup(s); } -static void migration_class_init(ObjectClass *klass, void *data) +static void migration_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); diff --git a/migration/rdma.c b/migration/rdma.c index d9603ab603..b31652baac 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -3985,7 +3985,7 @@ static void qio_channel_rdma_finalize(Object *obj) } static void qio_channel_rdma_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { QIOChannelClass *ioc_klass = QIO_CHANNEL_CLASS(klass); diff --git a/net/can/can_core.c b/net/can/can_core.c index 0115d78794..e16e15c036 100644 --- a/net/can/can_core.c +++ b/net/can/can_core.c @@ -149,7 +149,7 @@ static bool can_bus_can_be_deleted(UserCreatable *uc) } static void can_bus_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass); diff --git a/net/can/can_host.c b/net/can/can_host.c index b2fe553f91..ca5fcf1ea1 100644 --- a/net/can/can_host.c +++ b/net/can/can_host.c @@ -72,7 +72,7 @@ static void can_host_complete(UserCreatable *uc, Error **errp) } static void can_host_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass); diff --git a/net/can/can_socketcan.c b/net/can/can_socketcan.c index c1a1ad0563..8a57ae0717 100644 --- a/net/can/can_socketcan.c +++ b/net/can/can_socketcan.c @@ -308,7 +308,7 @@ static void can_host_socketcan_instance_init(Object *obj) } static void can_host_socketcan_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { CanHostClass *chc = CAN_HOST_CLASS(klass); diff --git a/net/colo-compare.c b/net/colo-compare.c index 893beed528..425eb6e971 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -1352,7 +1352,7 @@ static void colo_flush_packets(void *opaque, void *user_data) } } -static void colo_compare_class_init(ObjectClass *oc, void *data) +static void colo_compare_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); diff --git a/net/dump.c b/net/dump.c index 140215aa10..581234b775 100644 --- a/net/dump.c +++ b/net/dump.c @@ -234,7 +234,7 @@ static void filter_dump_instance_finalize(Object *obj) g_free(nfds->filename); } -static void filter_dump_class_init(ObjectClass *oc, void *data) +static void filter_dump_class_init(ObjectClass *oc, const void *data) { NetFilterClass *nfc = NETFILTER_CLASS(oc); diff --git a/net/filter-buffer.c b/net/filter-buffer.c index 283dc9cbe6..a36be31dc8 100644 --- a/net/filter-buffer.c +++ b/net/filter-buffer.c @@ -172,7 +172,7 @@ static void filter_buffer_set_interval(Object *obj, Visitor *v, s->interval = value; } -static void filter_buffer_class_init(ObjectClass *oc, void *data) +static void filter_buffer_class_init(ObjectClass *oc, const void *data) { NetFilterClass *nfc = NETFILTER_CLASS(oc); diff --git a/net/filter-mirror.c b/net/filter-mirror.c index 34a63b5dbb..27734c91a7 100644 --- a/net/filter-mirror.c +++ b/net/filter-mirror.c @@ -410,7 +410,7 @@ static void filter_redirector_set_vnet_hdr(Object *obj, s->vnet_hdr = value; } -static void filter_mirror_class_init(ObjectClass *oc, void *data) +static void filter_mirror_class_init(ObjectClass *oc, const void *data) { NetFilterClass *nfc = NETFILTER_CLASS(oc); @@ -425,7 +425,7 @@ static void filter_mirror_class_init(ObjectClass *oc, void *data) nfc->receive_iov = filter_mirror_receive_iov; } -static void filter_redirector_class_init(ObjectClass *oc, void *data) +static void filter_redirector_class_init(ObjectClass *oc, const void *data) { NetFilterClass *nfc = NETFILTER_CLASS(oc); diff --git a/net/filter-replay.c b/net/filter-replay.c index 81b71afe35..451663c5a5 100644 --- a/net/filter-replay.c +++ b/net/filter-replay.c @@ -65,7 +65,7 @@ static void filter_replay_instance_finalize(Object *obj) replay_unregister_net(nfrs->rns); } -static void filter_replay_class_init(ObjectClass *oc, void *data) +static void filter_replay_class_init(ObjectClass *oc, const void *data) { NetFilterClass *nfc = NETFILTER_CLASS(oc); diff --git a/net/filter-rewriter.c b/net/filter-rewriter.c index c18c4c2019..cdf85aa5ee 100644 --- a/net/filter-rewriter.c +++ b/net/filter-rewriter.c @@ -411,7 +411,7 @@ static void filter_rewriter_init(Object *obj) s->failover_mode = FAILOVER_MODE_OFF; } -static void colo_rewriter_class_init(ObjectClass *oc, void *data) +static void colo_rewriter_class_init(ObjectClass *oc, const void *data) { NetFilterClass *nfc = NETFILTER_CLASS(oc); diff --git a/net/filter.c b/net/filter.c index 3335908771..03b3c793f7 100644 --- a/net/filter.c +++ b/net/filter.c @@ -333,7 +333,7 @@ static void default_handle_event(NetFilterState *nf, int event, Error **errp) } } -static void netfilter_class_init(ObjectClass *oc, void *data) +static void netfilter_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); NetFilterClass *nfc = NETFILTER_CLASS(oc); diff --git a/qom/object.c b/qom/object.c index dfd59502d1..06d7367032 100644 --- a/qom/object.c +++ b/qom/object.c @@ -54,7 +54,7 @@ struct TypeImpl size_t instance_size; size_t instance_align; - void (*class_init)(ObjectClass *klass, void *data); + void (*class_init)(ObjectClass *klass, const void *data); void (*class_base_init)(ObjectClass *klass, const void *data); void *class_data; @@ -2891,7 +2891,7 @@ void object_class_property_set_description(ObjectClass *klass, op->description = g_strdup(description); } -static void object_class_init(ObjectClass *klass, void *data) +static void object_class_init(ObjectClass *klass, const void *data) { object_class_property_add_str(klass, "type", object_get_type, NULL); diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 03fe6247ff..f385cb7275 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -227,7 +227,7 @@ unsafe extern "C" fn rust_instance_post_init(obj: *mut bindings:: unsafe extern "C" fn rust_class_init( klass: *mut ObjectClass, - _data: *mut c_void, + _data: *const c_void, ) { let mut klass = NonNull::new(klass) .unwrap() diff --git a/scripts/codeconverter/codeconverter/qom_type_info.py b/scripts/codeconverter/codeconverter/qom_type_info.py index f92c3a4730..22a2556076 100644 --- a/scripts/codeconverter/codeconverter/qom_type_info.py +++ b/scripts/codeconverter/codeconverter/qom_type_info.py @@ -798,7 +798,8 @@ def gen_patches(self) -> Iterable[Patch]: # # # if 'class_init' not in fields: -# yield self.prepend(('static void %s_class_init(ObjectClass *oc, void *data)\n' +# yield self.prepend(('static void %s_class_init(ObjectClass *oc,\n' +# 'const void *data)\n' # '{\n' # '}\n\n') % (ids.lowercase)) # yield self.append_field('class_init', ids.lowercase+'_class_init') diff --git a/scsi/pr-manager-helper.c b/scsi/pr-manager-helper.c index 3be52a98d5..6b86f01b01 100644 --- a/scsi/pr-manager-helper.c +++ b/scsi/pr-manager-helper.c @@ -300,7 +300,7 @@ static void pr_manager_helper_instance_init(Object *obj) } static void pr_manager_helper_class_init(ObjectClass *klass, - void *class_data G_GNUC_UNUSED) + const void *class_data G_GNUC_UNUSED) { PRManagerClass *prmgr_klass = PR_MANAGER_CLASS(klass); UserCreatableClass *uc_klass = USER_CREATABLE_CLASS(klass); diff --git a/system/qtest.c b/system/qtest.c index 523a047995..b7689088a4 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -994,7 +994,7 @@ static char *qtest_get_chardev(Object *obj, Error **errp) return g_strdup(q->chr_name); } -static void qtest_class_init(ObjectClass *oc, void *data) +static void qtest_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 35fb145d27..27e2008a4e 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -258,7 +258,7 @@ static const TCGCPUOps alpha_tcg_ops = { #endif /* !CONFIG_USER_ONLY */ }; -static void alpha_cpu_class_init(ObjectClass *oc, void *data) +static void alpha_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 377791c84d..81257f20fd 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2699,7 +2699,7 @@ static const TCGCPUOps arm_tcg_ops = { }; #endif /* CONFIG_TCG */ -static void arm_cpu_class_init(ObjectClass *oc, void *data) +static void arm_cpu_class_init(ObjectClass *oc, const void *data) { ARMCPUClass *acc = ARM_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(acc); @@ -2740,7 +2740,7 @@ static void arm_cpu_instance_init(Object *obj) arm_cpu_post_init(obj); } -static void cpu_register_class_init(ObjectClass *oc, void *data) +static void cpu_register_class_init(ObjectClass *oc, const void *data) { ARMCPUClass *acc = ARM_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(acc); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 3705b34285..fdcf8cd1ae 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1123,7 +1123,7 @@ typedef struct ARMCPUInfo { const char *name; const char *deprecation_note; void (*initfn)(Object *obj); - void (*class_init)(ObjectClass *oc, void *data); + void (*class_init)(ObjectClass *oc, const void *data); } ARMCPUInfo; /** diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 8188ede5cc..1184c92b4c 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -818,7 +818,7 @@ static const gchar *aarch64_gdb_arch_name(CPUState *cs) return "aarch64"; } -static void aarch64_cpu_class_init(ObjectClass *oc, void *data) +static void aarch64_cpu_class_init(ObjectClass *oc, const void *data) { CPUClass *cc = CPU_CLASS(oc); @@ -842,7 +842,7 @@ static void aarch64_cpu_instance_init(Object *obj) arm_cpu_post_init(obj); } -static void cpu_register_class_init(ObjectClass *oc, void *data) +static void cpu_register_class_init(ObjectClass *oc, const void *data) { ARMCPUClass *acc = ARM_CPU_CLASS(oc); diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index f71560aa43..7426aac0dc 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -259,7 +259,7 @@ static const TCGCPUOps arm_v7m_tcg_ops = { #endif /* !CONFIG_USER_ONLY */ }; -static void arm_v7m_class_init(ObjectClass *oc, void *data) +static void arm_v7m_class_init(ObjectClass *oc, const void *data) { ARMCPUClass *acc = ARM_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 84f3b839c9..3f261c6fec 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -237,7 +237,7 @@ static const TCGCPUOps avr_tcg_ops = { .do_interrupt = avr_cpu_do_interrupt, }; -static void avr_cpu_class_init(ObjectClass *oc, void *data) +static void avr_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 3c5191282e..a5d31c33bd 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -335,7 +335,7 @@ static const TCGCPUOps hexagon_tcg_ops = { .mmu_index = hexagon_cpu_mmu_index, }; -static void hexagon_cpu_class_init(ObjectClass *c, void *data) +static void hexagon_cpu_class_init(ObjectClass *c, const void *data) { HexagonCPUClass *mcc = HEXAGON_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 10e18c945e..b792cb247a 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -278,7 +278,7 @@ static const TCGCPUOps hppa_tcg_ops = { #endif /* !CONFIG_USER_ONLY */ }; -static void hppa_cpu_class_init(ObjectClass *oc, void *data) +static void hppa_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); diff --git a/target/i386/confidential-guest.c b/target/i386/confidential-guest.c index b3727845ad..cfb71bf034 100644 --- a/target/i386/confidential-guest.c +++ b/target/i386/confidential-guest.c @@ -20,7 +20,7 @@ OBJECT_DEFINE_ABSTRACT_TYPE(X86ConfidentialGuest, X86_CONFIDENTIAL_GUEST, CONFIDENTIAL_GUEST_SUPPORT) -static void x86_confidential_guest_class_init(ObjectClass *oc, void *data) +static void x86_confidential_guest_class_init(ObjectClass *oc, const void *data) { } diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 955295fe79..6f21d5ed22 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5703,7 +5703,7 @@ static void max_x86_cpu_realize(DeviceState *dev, Error **errp) x86_cpu_realizefn(dev, errp); } -static void max_x86_cpu_class_init(ObjectClass *oc, void *data) +static void max_x86_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); X86CPUClass *xcc = X86_CPU_CLASS(oc); @@ -6702,7 +6702,7 @@ static const gchar *x86_gdb_arch_name(CPUState *cs) #endif } -static void x86_cpu_cpudef_class_init(ObjectClass *oc, void *data) +static void x86_cpu_cpudef_class_init(ObjectClass *oc, const void *data) { const X86CPUModel *model = data; X86CPUClass *xcc = X86_CPU_CLASS(oc); @@ -8886,7 +8886,7 @@ static const struct SysemuCPUOps i386_sysemu_ops = { }; #endif -static void x86_cpu_common_class_init(ObjectClass *oc, void *data) +static void x86_cpu_common_class_init(ObjectClass *oc, const void *data) { X86CPUClass *xcc = X86_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); @@ -8986,7 +8986,7 @@ static const TypeInfo x86_cpu_type_info = { }; /* "base" CPU model, used by query-cpu-model-expansion */ -static void x86_cpu_base_class_init(ObjectClass *oc, void *data) +static void x86_cpu_base_class_init(ObjectClass *oc, const void *data) { X86CPUClass *xcc = X86_CPU_CLASS(oc); diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index 072731a4dd..a2d3830f5b 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -161,7 +161,7 @@ void host_cpu_max_instance_init(X86CPU *cpu) &error_abort); } -static void host_cpu_class_init(ObjectClass *oc, void *data) +static void host_cpu_class_init(ObjectClass *oc, const void *data) { X86CPUClass *xcc = X86_CPU_CLASS(oc); diff --git a/target/i386/hvf/hvf-cpu.c b/target/i386/hvf/hvf-cpu.c index b5f4c80028..dfdda70126 100644 --- a/target/i386/hvf/hvf-cpu.c +++ b/target/i386/hvf/hvf-cpu.c @@ -74,7 +74,7 @@ static void hvf_cpu_instance_init(CPUState *cs) hvf_cpu_xsave_init(); } -static void hvf_cpu_accel_class_init(ObjectClass *oc, void *data) +static void hvf_cpu_accel_class_init(ObjectClass *oc, const void *data) { AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index 6269fa8045..16bde4de01 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -223,7 +223,7 @@ static void kvm_cpu_instance_init(CPUState *cs) kvm_cpu_xsave_init(); } -static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data) +static void kvm_cpu_accel_class_init(ObjectClass *oc, const void *data) { AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index 4e4e63de78..21443078b7 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -81,7 +81,7 @@ static void nvmm_kick_vcpu_thread(CPUState *cpu) cpus_kick_thread(cpu); } -static void nvmm_accel_ops_class_init(ObjectClass *oc, void *data) +static void nvmm_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 17394d073d..f1c6120ccf 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -1200,7 +1200,7 @@ nvmm_enabled(void) } static void -nvmm_accel_class_init(ObjectClass *oc, void *data) +nvmm_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "NVMM"; diff --git a/target/i386/sev.c b/target/i386/sev.c index 878dd20f2c..7ef4801d5f 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -2046,7 +2046,7 @@ static void sev_common_set_kernel_hashes(Object *obj, bool value, Error **errp) } static void -sev_common_class_init(ObjectClass *oc, void *data) +sev_common_class_init(ObjectClass *oc, const void *data) { ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); @@ -2141,7 +2141,7 @@ static void sev_guest_set_legacy_vm_type(Object *obj, Visitor *v, } static void -sev_guest_class_init(ObjectClass *oc, void *data) +sev_guest_class_init(ObjectClass *oc, const void *data) { SevCommonStateClass *klass = SEV_COMMON_CLASS(oc); X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); @@ -2395,7 +2395,7 @@ sev_snp_guest_set_host_data(Object *obj, const char *value, Error **errp) } static void -sev_snp_guest_class_init(ObjectClass *oc, void *data) +sev_snp_guest_class_init(ObjectClass *oc, const void *data) { SevCommonStateClass *klass = SEV_COMMON_CLASS(oc); X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 621502c984..e53aaa31bf 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -193,7 +193,7 @@ static void x86_tcg_cpu_instance_init(CPUState *cs) x86_tcg_cpu_xsave_init(); } -static void x86_tcg_cpu_accel_class_init(ObjectClass *oc, void *data) +static void x86_tcg_cpu_accel_class_init(ObjectClass *oc, const void *data) { AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index 81fdd06e48..b8bebe403c 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -83,7 +83,7 @@ static bool whpx_vcpu_thread_is_idle(CPUState *cpu) return !whpx_apic_in_platform(); } -static void whpx_accel_ops_class_init(ObjectClass *oc, void *data) +static void whpx_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 2e1c03b20e..cf6d3e4cdd 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -2698,7 +2698,7 @@ bool whpx_apic_in_platform(void) { return whpx_global.apic_in_platform; } -static void whpx_accel_class_init(ObjectClass *oc, void *data) +static void whpx_accel_class_init(ObjectClass *oc, const void *data) { AccelClass *ac = ACCEL_CLASS(oc); ac->name = "WHPX"; diff --git a/target/i386/whpx/whpx-apic.c b/target/i386/whpx/whpx-apic.c index 630a9616d7..e1ef6d4e6d 100644 --- a/target/i386/whpx/whpx-apic.c +++ b/target/i386/whpx/whpx-apic.c @@ -252,7 +252,7 @@ static void whpx_apic_realize(DeviceState *dev, Error **errp) msi_nonbroken = true; } -static void whpx_apic_class_init(ObjectClass *klass, void *data) +static void whpx_apic_class_init(ObjectClass *klass, const void *data) { APICCommonClass *k = APIC_COMMON_CLASS(klass); diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 0e6c89eb1b..8ad45b453d 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -908,7 +908,7 @@ static const Property loongarch_cpu_properties[] = { DEFINE_PROP_INT32("node-id", LoongArchCPU, node_id, CPU_UNSET_NUMA_NODE_ID), }; -static void loongarch_cpu_class_init(ObjectClass *c, void *data) +static void loongarch_cpu_class_init(ObjectClass *c, const void *data) { LoongArchCPUClass *lacc = LOONGARCH_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); @@ -948,7 +948,7 @@ static const gchar *loongarch32_gdb_arch_name(CPUState *cs) return "loongarch32"; } -static void loongarch32_cpu_class_init(ObjectClass *c, void *data) +static void loongarch32_cpu_class_init(ObjectClass *c, const void *data) { CPUClass *cc = CPU_CLASS(c); @@ -961,7 +961,7 @@ static const gchar *loongarch64_gdb_arch_name(CPUState *cs) return "loongarch64"; } -static void loongarch64_cpu_class_init(ObjectClass *c, void *data) +static void loongarch64_cpu_class_init(ObjectClass *c, const void *data) { CPUClass *cc = CPU_CLASS(c); diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 99adc5eb91..6f33b86c7d 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -607,7 +607,7 @@ static const TCGCPUOps m68k_tcg_ops = { #endif /* !CONFIG_USER_ONLY */ }; -static void m68k_cpu_class_init(ObjectClass *c, void *data) +static void m68k_cpu_class_init(ObjectClass *c, const void *data) { M68kCPUClass *mcc = M68K_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); @@ -634,7 +634,7 @@ static void m68k_cpu_class_init(ObjectClass *c, void *data) cc->tcg_ops = &m68k_tcg_ops; } -static void m68k_cpu_class_init_cf_core(ObjectClass *c, void *data) +static void m68k_cpu_class_init_cf_core(ObjectClass *c, const void *data) { CPUClass *cc = CPU_CLASS(c); @@ -649,7 +649,7 @@ static void m68k_cpu_class_init_cf_core(ObjectClass *c, void *data) .class_init = m68k_cpu_class_init_cf_core \ } -static void m68k_cpu_class_init_m68k_core(ObjectClass *c, void *data) +static void m68k_cpu_class_init_m68k_core(ObjectClass *c, const void *data) { CPUClass *cc = CPU_CLASS(c); diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index edfb05758b..00a2730de4 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -447,7 +447,7 @@ static const TCGCPUOps mb_tcg_ops = { #endif /* !CONFIG_USER_ONLY */ }; -static void mb_cpu_class_init(ObjectClass *oc, void *data) +static void mb_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 473cecdebc..29611a0a1c 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -572,7 +572,7 @@ static const TCGCPUOps mips_tcg_ops = { }; #endif /* CONFIG_TCG */ -static void mips_cpu_class_init(ObjectClass *c, void *data) +static void mips_cpu_class_init(ObjectClass *c, const void *data) { MIPSCPUClass *mcc = MIPS_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); @@ -613,7 +613,7 @@ static const TypeInfo mips_cpu_type_info = { .class_init = mips_cpu_class_init, }; -static void mips_cpu_cpudef_class_init(ObjectClass *oc, void *data) +static void mips_cpu_cpudef_class_init(ObjectClass *oc, const void *data) { MIPSCPUClass *mcc = MIPS_CPU_CLASS(oc); mcc->cpu_def = data; diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 6601e0c066..2ec267efec 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -260,7 +260,7 @@ static const TCGCPUOps openrisc_tcg_ops = { #endif /* !CONFIG_USER_ONLY */ }; -static void openrisc_cpu_class_init(ObjectClass *oc, void *data) +static void openrisc_cpu_class_init(ObjectClass *oc, const void *data) { OpenRISCCPUClass *occ = OPENRISC_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(occ); diff --git a/target/ppc/cpu-models.c b/target/ppc/cpu-models.c index ece3481781..ea86ea202a 100644 --- a/target/ppc/cpu-models.c +++ b/target/ppc/cpu-models.c @@ -35,7 +35,7 @@ #define POWERPC_DEF_SVR(_name, _desc, _pvr, _svr, _type) \ static void \ glue(POWERPC_DEF_PREFIX(_pvr, _svr, _type), _cpu_class_init) \ - (ObjectClass *oc, void *data) \ + (ObjectClass *oc, const void *data) \ { \ DeviceClass *dc = DEVICE_CLASS(oc); \ PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); \ diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index f4cc823c5c..13115a89ff 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -3047,7 +3047,8 @@ static inline int check_attn_none(CPUPPCState *env) #define POWERPC_FAMILY(_name) \ static void \ - glue(glue(ppc_, _name), _cpu_family_class_init)(ObjectClass *, void *); \ + glue(glue(ppc_, _name), _cpu_family_class_init)(ObjectClass *, \ + const void *); \ \ static const TypeInfo \ glue(glue(ppc_, _name), _cpu_family_type_info) = { \ diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 077991ed53..90b21b9c93 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -2167,7 +2167,7 @@ static void init_proc_405(CPUPPCState *env) SET_WDT_PERIOD(16, 20, 24, 28); } -POWERPC_FAMILY(405)(ObjectClass *oc, void *data) +POWERPC_FAMILY(405)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2235,7 +2235,7 @@ static void init_proc_440EP(CPUPPCState *env) SET_WDT_PERIOD(20, 24, 28, 32); } -POWERPC_FAMILY(440EP)(ObjectClass *oc, void *data) +POWERPC_FAMILY(440EP)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2274,7 +2274,7 @@ POWERPC_FAMILY(440EP)(ObjectClass *oc, void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } -POWERPC_FAMILY(460EX)(ObjectClass *oc, void *data) +POWERPC_FAMILY(460EX)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2329,7 +2329,7 @@ static void init_proc_440GP(CPUPPCState *env) SET_WDT_PERIOD(20, 24, 28, 32); } -POWERPC_FAMILY(440GP)(ObjectClass *oc, void *data) +POWERPC_FAMILY(440GP)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2399,7 +2399,7 @@ static void init_proc_440x5(CPUPPCState *env) SET_WDT_PERIOD(20, 24, 28, 32); } -POWERPC_FAMILY(440x5)(ObjectClass *oc, void *data) +POWERPC_FAMILY(440x5)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2435,7 +2435,7 @@ POWERPC_FAMILY(440x5)(ObjectClass *oc, void *data) POWERPC_FLAG_DE | POWERPC_FLAG_BUS_CLK; } -POWERPC_FAMILY(440x5wDFPU)(ObjectClass *oc, void *data) +POWERPC_FAMILY(440x5wDFPU)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2484,7 +2484,7 @@ static void init_proc_MPC5xx(CPUPPCState *env) /* XXX: TODO: allocate internal IRQ controller */ } -POWERPC_FAMILY(MPC5xx)(ObjectClass *oc, void *data) +POWERPC_FAMILY(MPC5xx)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2527,7 +2527,7 @@ static void init_proc_MPC8xx(CPUPPCState *env) /* XXX: TODO: allocate internal IRQ controller */ } -POWERPC_FAMILY(MPC8xx)(ObjectClass *oc, void *data) +POWERPC_FAMILY(MPC8xx)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2578,7 +2578,7 @@ static void init_proc_G2(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(G2)(ObjectClass *oc, void *data) +POWERPC_FAMILY(G2)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2617,7 +2617,7 @@ POWERPC_FAMILY(G2)(ObjectClass *oc, void *data) POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; } -POWERPC_FAMILY(G2LE)(ObjectClass *oc, void *data) +POWERPC_FAMILY(G2LE)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -2752,7 +2752,7 @@ static void init_proc_e200(CPUPPCState *env) /* XXX: TODO: allocate internal IRQ controller */ } -POWERPC_FAMILY(e200)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e200)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3045,7 +3045,7 @@ static void init_proc_e500v1(CPUPPCState *env) init_proc_e500(env, fsl_e500v1); } -POWERPC_FAMILY(e500v1)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e500v1)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3089,7 +3089,7 @@ static void init_proc_e500v2(CPUPPCState *env) init_proc_e500(env, fsl_e500v2); } -POWERPC_FAMILY(e500v2)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e500v2)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3133,7 +3133,7 @@ static void init_proc_e500mc(CPUPPCState *env) init_proc_e500(env, fsl_e500mc); } -POWERPC_FAMILY(e500mc)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e500mc)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3180,7 +3180,7 @@ static void init_proc_e5500(CPUPPCState *env) init_proc_e500(env, fsl_e5500); } -POWERPC_FAMILY(e5500)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e5500)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3229,7 +3229,7 @@ static void init_proc_e6500(CPUPPCState *env) init_proc_e500(env, fsl_e6500); } -POWERPC_FAMILY(e6500)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e6500)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3292,7 +3292,7 @@ static void init_proc_603(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(603)(ObjectClass *oc, void *data) +POWERPC_FAMILY(603)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3332,7 +3332,7 @@ POWERPC_FAMILY(603)(ObjectClass *oc, void *data) POWERPC_FLAG_BE | POWERPC_FLAG_BUS_CLK; } -POWERPC_FAMILY(603E)(ObjectClass *oc, void *data) +POWERPC_FAMILY(603E)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3378,7 +3378,7 @@ static void init_proc_e300(CPUPPCState *env) register_e300_sprs(env); } -POWERPC_FAMILY(e300)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e300)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3434,7 +3434,7 @@ static void init_proc_604(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(604)(ObjectClass *oc, void *data) +POWERPC_FAMILY(604)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3480,7 +3480,7 @@ static void init_proc_604E(CPUPPCState *env) register_604e_sprs(env); } -POWERPC_FAMILY(604E)(ObjectClass *oc, void *data) +POWERPC_FAMILY(604E)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3537,7 +3537,7 @@ static void init_proc_740(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(740)(ObjectClass *oc, void *data) +POWERPC_FAMILY(740)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3603,7 +3603,7 @@ static void init_proc_750(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(750)(ObjectClass *oc, void *data) +POWERPC_FAMILY(750)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3750,7 +3750,7 @@ static void init_proc_750cl(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(750cl)(ObjectClass *oc, void *data) +POWERPC_FAMILY(750cl)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3858,7 +3858,7 @@ static void init_proc_750cx(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(750cx)(ObjectClass *oc, void *data) +POWERPC_FAMILY(750cx)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -3931,7 +3931,7 @@ static void init_proc_750fx(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(750fx)(ObjectClass *oc, void *data) +POWERPC_FAMILY(750fx)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4004,7 +4004,7 @@ static void init_proc_750gx(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(750gx)(ObjectClass *oc, void *data) +POWERPC_FAMILY(750gx)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4064,7 +4064,7 @@ static void init_proc_745(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(745)(ObjectClass *oc, void *data) +POWERPC_FAMILY(745)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4110,7 +4110,7 @@ static void init_proc_755(CPUPPCState *env) register_755_sprs(env); } -POWERPC_FAMILY(755)(ObjectClass *oc, void *data) +POWERPC_FAMILY(755)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4177,7 +4177,7 @@ static void init_proc_7400(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7400)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7400)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4257,7 +4257,7 @@ static void init_proc_7410(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7410)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7410)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4358,7 +4358,7 @@ static void init_proc_7440(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7440)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7440)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4481,7 +4481,7 @@ static void init_proc_7450(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7450)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7450)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4611,7 +4611,7 @@ static void init_proc_7445(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7445)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7445)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4743,7 +4743,7 @@ static void init_proc_7455(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7455)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7455)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -4895,7 +4895,7 @@ static void init_proc_7457(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(7457)(ObjectClass *oc, void *data) +POWERPC_FAMILY(7457)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -5030,7 +5030,7 @@ static void init_proc_e600(CPUPPCState *env) ppc6xx_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(e600)(ObjectClass *oc, void *data) +POWERPC_FAMILY(e600)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -5995,7 +5995,7 @@ static void init_proc_970(CPUPPCState *env) ppc970_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(970)(ObjectClass *oc, void *data) +POWERPC_FAMILY(970)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -6070,7 +6070,7 @@ static void init_proc_power5plus(CPUPPCState *env) ppc970_irq_init(env_archcpu(env)); } -POWERPC_FAMILY(POWER5P)(ObjectClass *oc, void *data) +POWERPC_FAMILY(POWER5P)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -6176,7 +6176,7 @@ static bool ppc_pvr_match_power7(PowerPCCPUClass *pcc, uint32_t pvr, bool best) return true; } -POWERPC_FAMILY(POWER7)(ObjectClass *oc, void *data) +POWERPC_FAMILY(POWER7)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -6340,7 +6340,7 @@ static bool ppc_pvr_match_power8(PowerPCCPUClass *pcc, uint32_t pvr, bool best) return true; } -POWERPC_FAMILY(POWER8)(ObjectClass *oc, void *data) +POWERPC_FAMILY(POWER8)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -6549,7 +6549,7 @@ static bool ppc_pvr_match_power9(PowerPCCPUClass *pcc, uint32_t pvr, bool best) return false; } -POWERPC_FAMILY(POWER9)(ObjectClass *oc, void *data) +POWERPC_FAMILY(POWER9)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -6647,7 +6647,7 @@ static bool ppc_pvr_match_power10(PowerPCCPUClass *pcc, uint32_t pvr, bool best) return false; } -POWERPC_FAMILY(POWER10)(ObjectClass *oc, void *data) +POWERPC_FAMILY(POWER10)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -6707,7 +6707,7 @@ static bool ppc_pvr_match_power11(PowerPCCPUClass *pcc, uint32_t pvr, bool best) return false; } -POWERPC_FAMILY(POWER11)(ObjectClass *oc, void *data) +POWERPC_FAMILY(POWER11)(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); @@ -7504,7 +7504,7 @@ static const TCGCPUOps ppc_tcg_ops = { }; #endif /* CONFIG_TCG */ -static void ppc_cpu_class_init(ObjectClass *oc, void *data) +static void ppc_cpu_class_init(ObjectClass *oc, const void *data) { PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 3fe54798c6..8a957c3c7d 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -2383,7 +2383,7 @@ static bool kvmppc_cpu_realize(CPUState *cs, Error **errp) return true; } -static void kvmppc_host_cpu_class_init(ObjectClass *oc, void *data) +static void kvmppc_host_cpu_class_init(ObjectClass *oc, const void *data) { PowerPCCPUClass *pcc = POWERPC_CPU_CLASS(oc); uint32_t dcache_size = kvmppc_read_int_cpu_dt("d-cache-size"); @@ -3004,7 +3004,7 @@ void kvm_arch_accel_class_init(ObjectClass *oc) { } -static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data) +static void kvm_cpu_accel_class_init(ObjectClass *oc, const void *data) { AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 2b830b3317..e0604f4c78 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3028,7 +3028,7 @@ static const struct SysemuCPUOps riscv_sysemu_ops = { }; #endif -static void riscv_cpu_common_class_init(ObjectClass *c, void *data) +static void riscv_cpu_common_class_init(ObjectClass *c, const void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); @@ -3061,7 +3061,7 @@ static void riscv_cpu_common_class_init(ObjectClass *c, void *data) device_class_set_props(dc, riscv_cpu_properties); } -static void riscv_cpu_class_init(ObjectClass *c, void *data) +static void riscv_cpu_class_init(ObjectClass *c, const void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 5315134e08..75724b6af4 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1976,7 +1976,7 @@ void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp) kvm_riscv_destroy_scratch_vcpu(&kvmcpu); } -static void kvm_cpu_accel_class_init(ObjectClass *oc, void *data) +static void kvm_cpu_accel_class_init(ObjectClass *oc, const void *data) { AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); @@ -1997,7 +1997,7 @@ static void kvm_cpu_accel_register_types(void) } type_init(kvm_cpu_accel_register_types); -static void riscv_host_cpu_class_init(ObjectClass *c, void *data) +static void riscv_host_cpu_class_init(ObjectClass *c, const void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 426145c3b9..54ac54f2e1 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1528,7 +1528,7 @@ static void riscv_tcg_cpu_instance_init(CPUState *cs) } } -static void riscv_tcg_cpu_accel_class_init(ObjectClass *oc, void *data) +static void riscv_tcg_cpu_accel_class_init(ObjectClass *oc, const void *data) { AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 0a7a2b55b5..a51b543028 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -220,7 +220,7 @@ static const TCGCPUOps rx_tcg_ops = { .do_interrupt = rx_cpu_do_interrupt, }; -static void rx_cpu_class_init(ObjectClass *klass, void *data) +static void rx_cpu_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); CPUClass *cc = CPU_CLASS(klass); diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 43fc3194bc..3d644f5e23 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -371,7 +371,7 @@ static const TCGCPUOps s390_tcg_ops = { }; #endif /* CONFIG_TCG */ -static void s390_cpu_class_init(ObjectClass *oc, void *data) +static void s390_cpu_class_init(ObjectClass *oc, const void *data) { S390CPUClass *scc = S390_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(scc); diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index 8e0b01dc65..b097ed55d9 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -919,7 +919,7 @@ void s390_cpu_model_class_register_props(ObjectClass *oc) } #ifdef CONFIG_KVM -static void s390_host_cpu_model_class_init(ObjectClass *oc, void *data) +static void s390_host_cpu_model_class_init(ObjectClass *oc, const void *data) { S390CPUClass *xcc = S390_CPU_CLASS(oc); @@ -928,7 +928,7 @@ static void s390_host_cpu_model_class_init(ObjectClass *oc, void *data) } #endif -static void s390_base_cpu_model_class_init(ObjectClass *oc, void *data) +static void s390_base_cpu_model_class_init(ObjectClass *oc, const void *data) { S390CPUClass *xcc = S390_CPU_CLASS(oc); @@ -939,7 +939,7 @@ static void s390_base_cpu_model_class_init(ObjectClass *oc, void *data) xcc->desc = xcc->cpu_def->desc; } -static void s390_cpu_model_class_init(ObjectClass *oc, void *data) +static void s390_cpu_model_class_init(ObjectClass *oc, const void *data) { S390CPUClass *xcc = S390_CPU_CLASS(oc); @@ -949,7 +949,7 @@ static void s390_cpu_model_class_init(ObjectClass *oc, void *data) xcc->desc = xcc->cpu_def->desc; } -static void s390_qemu_cpu_model_class_init(ObjectClass *oc, void *data) +static void s390_qemu_cpu_model_class_init(ObjectClass *oc, const void *data) { S390CPUClass *xcc = S390_CPU_CLASS(oc); @@ -958,7 +958,7 @@ static void s390_qemu_cpu_model_class_init(ObjectClass *oc, void *data) qemu_hw_version()); } -static void s390_max_cpu_model_class_init(ObjectClass *oc, void *data) +static void s390_max_cpu_model_class_init(ObjectClass *oc, const void *data) { S390CPUClass *xcc = S390_CPU_CLASS(oc); diff --git a/target/s390x/kvm/pv.c b/target/s390x/kvm/pv.c index b191a4a68a..fe0a72c416 100644 --- a/target/s390x/kvm/pv.c +++ b/target/s390x/kvm/pv.c @@ -367,7 +367,7 @@ OBJECT_DEFINE_TYPE_WITH_INTERFACES(S390PVGuest, { TYPE_USER_CREATABLE }, { NULL }) -static void s390_pv_guest_class_init(ObjectClass *oc, void *data) +static void s390_pv_guest_class_init(ObjectClass *oc, const void *data) { ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 861fdd47f7..57d7b5fbc8 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -177,7 +177,7 @@ static void sh7750r_cpu_initfn(Object *obj) env->features = SH_FEATURE_BCR3_AND_BCR4; } -static void sh7750r_class_init(ObjectClass *oc, void *data) +static void sh7750r_class_init(ObjectClass *oc, const void *data) { SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); @@ -194,7 +194,7 @@ static void sh7751r_cpu_initfn(Object *obj) env->features = SH_FEATURE_BCR3_AND_BCR4; } -static void sh7751r_class_init(ObjectClass *oc, void *data) +static void sh7751r_class_init(ObjectClass *oc, const void *data) { SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); @@ -211,7 +211,7 @@ static void sh7785_cpu_initfn(Object *obj) env->features = SH_FEATURE_SH4A; } -static void sh7785_class_init(ObjectClass *oc, void *data) +static void sh7785_class_init(ObjectClass *oc, const void *data) { SuperHCPUClass *scc = SUPERH_CPU_CLASS(oc); @@ -282,7 +282,7 @@ static const TCGCPUOps superh_tcg_ops = { #endif /* !CONFIG_USER_ONLY */ }; -static void superh_cpu_class_init(ObjectClass *oc, void *data) +static void superh_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 174b76f762..981aa86e0e 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -1042,7 +1042,7 @@ static const TCGCPUOps sparc_tcg_ops = { }; #endif /* CONFIG_TCG */ -static void sparc_cpu_class_init(ObjectClass *oc, void *data) +static void sparc_cpu_class_init(ObjectClass *oc, const void *data) { SPARCCPUClass *scc = SPARC_CPU_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); @@ -1091,7 +1091,7 @@ static const TypeInfo sparc_cpu_type_info = { .class_init = sparc_cpu_class_init, }; -static void sparc_cpu_cpudef_class_init(ObjectClass *oc, void *data) +static void sparc_cpu_cpudef_class_init(ObjectClass *oc, const void *data) { SPARCCPUClass *scc = SPARC_CPU_CLASS(oc); scc->cpu_def = data; diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index a4f93e7d91..098cd06c54 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -185,7 +185,7 @@ static const TCGCPUOps tricore_tcg_ops = { .cpu_exec_halt = tricore_cpu_has_work, }; -static void tricore_cpu_class_init(ObjectClass *c, void *data) +static void tricore_cpu_class_init(ObjectClass *c, const void *data) { TriCoreCPUClass *mcc = TRICORE_CPU_CLASS(c); CPUClass *cc = CPU_CLASS(c); diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 971e67ad97..27d6e40195 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -253,7 +253,7 @@ static const TCGCPUOps xtensa_tcg_ops = { #endif /* !CONFIG_USER_ONLY */ }; -static void xtensa_cpu_class_init(ObjectClass *oc, void *data) +static void xtensa_cpu_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); CPUClass *cc = CPU_CLASS(oc); diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c index d02d16f9ec..0459787981 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -170,7 +170,7 @@ static void xtensa_finalize_config(XtensaConfig *config) } } -static void xtensa_core_class_init(ObjectClass *oc, void *data) +static void xtensa_core_class_init(ObjectClass *oc, const void *data) { CPUClass *cc = CPU_CLASS(oc); XtensaCPUClass *xcc = XTENSA_CPU_CLASS(oc); diff --git a/tests/unit/check-qom-interface.c b/tests/unit/check-qom-interface.c index c99be97ed8..4e1c4d6729 100644 --- a/tests/unit/check-qom-interface.c +++ b/tests/unit/check-qom-interface.c @@ -38,7 +38,7 @@ static const TypeInfo test_if_info = { #define PATTERN 0xFAFBFCFD -static void test_class_init(ObjectClass *oc, void *data) +static void test_class_init(ObjectClass *oc, const void *data) { TestIfClass *tc = TEST_IF_CLASS(oc); diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplist.c index 13d632cfed..f1de1618f6 100644 --- a/tests/unit/check-qom-proplist.c +++ b/tests/unit/check-qom-proplist.c @@ -135,7 +135,7 @@ static void dummy_init(Object *obj) } -static void dummy_class_init(ObjectClass *cls, void *data) +static void dummy_class_init(ObjectClass *cls, const void *data) { object_class_property_add_str(cls, "sv", dummy_get_sv, @@ -264,7 +264,7 @@ static void dummy_dev_unparent(Object *obj) object_unparent(OBJECT(dev->bus)); } -static void dummy_dev_class_init(ObjectClass *klass, void *opaque) +static void dummy_dev_class_init(ObjectClass *klass, const void *opaque) { klass->unparent = dummy_dev_unparent; } @@ -288,7 +288,7 @@ static void dummy_bus_unparent(Object *obj) object_unparent(OBJECT(bus->backend)); } -static void dummy_bus_class_init(ObjectClass *klass, void *opaque) +static void dummy_bus_class_init(ObjectClass *klass, const void *opaque) { klass->unparent = dummy_bus_unparent; } diff --git a/tests/unit/test-qdev-global-props.c b/tests/unit/test-qdev-global-props.c index 6f6a306788..33062762a6 100644 --- a/tests/unit/test-qdev-global-props.c +++ b/tests/unit/test-qdev-global-props.c @@ -51,7 +51,7 @@ static const Property static_props[] = { DEFINE_PROP_UINT32("prop2", MyType, prop2, PROP_DEFAULT), }; -static void static_prop_class_init(ObjectClass *oc, void *data) +static void static_prop_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -177,7 +177,7 @@ static void dynamic_instance_init(Object *obj) NULL, NULL); } -static void dynamic_class_init(ObjectClass *oc, void *data) +static void dynamic_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -193,7 +193,7 @@ static const TypeInfo dynamic_prop_type = { .class_init = dynamic_class_init, }; -static void hotplug_class_init(ObjectClass *oc, void *data) +static void hotplug_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); @@ -209,7 +209,7 @@ static const TypeInfo hotplug_type = { .class_init = hotplug_class_init, }; -static void nohotplug_class_init(ObjectClass *oc, void *data) +static void nohotplug_class_init(ObjectClass *oc, const void *data) { DeviceClass *dc = DEVICE_CLASS(oc); diff --git a/tests/unit/test-smp-parse.c b/tests/unit/test-smp-parse.c index f9bccb56ab..326045ecbb 100644 --- a/tests/unit/test-smp-parse.c +++ b/tests/unit/test-smp-parse.c @@ -924,7 +924,7 @@ static void unsupported_params_init(const MachineClass *mc, SMPTestData *data) } } -static void machine_base_class_init(ObjectClass *oc, void *data) +static void machine_base_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -934,7 +934,8 @@ static void machine_base_class_init(ObjectClass *oc, void *data) mc->name = g_strdup(SMP_MACHINE_NAME); } -static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) +static void machine_generic_invalid_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -943,21 +944,22 @@ static void machine_generic_invalid_class_init(ObjectClass *oc, void *data) mc->max_cpus = MAX_CPUS - 1; } -static void machine_with_modules_class_init(ObjectClass *oc, void *data) +static void machine_with_modules_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->smp_props.modules_supported = true; } -static void machine_with_dies_class_init(ObjectClass *oc, void *data) +static void machine_with_dies_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->smp_props.dies_supported = true; } -static void machine_with_modules_dies_class_init(ObjectClass *oc, void *data) +static void machine_with_modules_dies_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -965,28 +967,29 @@ static void machine_with_modules_dies_class_init(ObjectClass *oc, void *data) mc->smp_props.dies_supported = true; } -static void machine_with_clusters_class_init(ObjectClass *oc, void *data) +static void machine_with_clusters_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->smp_props.clusters_supported = true; } -static void machine_with_books_class_init(ObjectClass *oc, void *data) +static void machine_with_books_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->smp_props.books_supported = true; } -static void machine_with_drawers_class_init(ObjectClass *oc, void *data) +static void machine_with_drawers_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); mc->smp_props.drawers_supported = true; } -static void machine_with_drawers_books_class_init(ObjectClass *oc, void *data) +static void machine_with_drawers_books_class_init(ObjectClass *oc, + const void *data) { MachineClass *mc = MACHINE_CLASS(oc); @@ -994,7 +997,7 @@ static void machine_with_drawers_books_class_init(ObjectClass *oc, void *data) mc->smp_props.books_supported = true; } -static void machine_full_topo_class_init(ObjectClass *oc, void *data) +static void machine_full_topo_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); diff --git a/ui/console-vc.c b/ui/console-vc.c index df1341513d..830842064d 100644 --- a/ui/console-vc.c +++ b/ui/console-vc.c @@ -1036,7 +1036,7 @@ qemu_text_console_finalize(Object *obj) } static void -qemu_text_console_class_init(ObjectClass *oc, void *data) +qemu_text_console_class_init(ObjectClass *oc, const void *data) { if (!cursor_timer) { cursor_timer = timer_new_ms(QEMU_CLOCK_REALTIME, cursor_timer_cb, NULL); @@ -1065,7 +1065,7 @@ qemu_fixed_text_console_finalize(Object *obj) } static void -qemu_fixed_text_console_class_init(ObjectClass *oc, void *data) +qemu_fixed_text_console_class_init(ObjectClass *oc, const void *data) { } @@ -1181,7 +1181,7 @@ static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp) } } -static void char_vc_class_init(ObjectClass *oc, void *data) +static void char_vc_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/ui/console.c b/ui/console.c index 6cd122cf40..2d00828c53 100644 --- a/ui/console.c +++ b/ui/console.c @@ -401,7 +401,7 @@ qemu_console_finalize(Object *obj) } static void -qemu_console_class_init(ObjectClass *oc, void *data) +qemu_console_class_init(ObjectClass *oc, const void *data) { } @@ -437,7 +437,7 @@ qemu_graphic_console_prop_get_head(Object *obj, Visitor *v, const char *name, } static void -qemu_graphic_console_class_init(ObjectClass *oc, void *data) +qemu_graphic_console_class_init(ObjectClass *oc, const void *data) { object_class_property_add_link(oc, "device", TYPE_DEVICE, offsetof(QemuGraphicConsole, device), diff --git a/ui/dbus-chardev.c b/ui/dbus-chardev.c index bf061cbc93..d05dddaf81 100644 --- a/ui/dbus-chardev.c +++ b/ui/dbus-chardev.c @@ -269,7 +269,7 @@ dbus_chr_parse(QemuOpts *opts, ChardevBackend *backend, } static void -char_dbus_class_init(ObjectClass *oc, void *data) +char_dbus_class_init(ObjectClass *oc, const void *data) { DBusChardevClass *klass = DBUS_CHARDEV_CLASS(oc); ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/ui/dbus.c b/ui/dbus.c index 2eb03aa247..0c0f86eaa6 100644 --- a/ui/dbus.c +++ b/ui/dbus.c @@ -404,7 +404,7 @@ set_gl_mode(Object *o, int val, Error **errp) } static void -dbus_display_class_init(ObjectClass *oc, void *data) +dbus_display_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); @@ -453,7 +453,7 @@ dbus_vc_parse(QemuOpts *opts, ChardevBackend *backend, } static void -dbus_vc_class_init(ObjectClass *oc, void *data) +dbus_vc_class_init(ObjectClass *oc, const void *data) { DBusVCClass *klass = DBUS_VC_CLASS(oc); ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/ui/gtk.c b/ui/gtk.c index 59bda83da6..982037b2c0 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -1879,7 +1879,7 @@ static void gd_vc_open(Chardev *chr, *be_opened = false; } -static void char_gd_vc_class_init(ObjectClass *oc, void *data) +static void char_gd_vc_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/ui/input-barrier.c b/ui/input-barrier.c index c86e5d67eb..9dce31ea9a 100644 --- a/ui/input-barrier.c +++ b/ui/input-barrier.c @@ -696,7 +696,7 @@ static void input_barrier_instance_init(Object *obj) ib->height = 1080; } -static void input_barrier_class_init(ObjectClass *oc, void *data) +static void input_barrier_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); diff --git a/ui/input-linux.c b/ui/input-linux.c index 203e264212..2f5adb82d2 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -494,7 +494,7 @@ static void input_linux_instance_init(Object *obj) { } -static void input_linux_class_init(ObjectClass *oc, void *data) +static void input_linux_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); diff --git a/ui/spice-app.c b/ui/spice-app.c index 91e258a621..24f78f305c 100644 --- a/ui/spice-app.c +++ b/ui/spice-app.c @@ -101,7 +101,7 @@ static void vc_chr_parse(QemuOpts *opts, ChardevBackend *backend, Error **errp) /* fqdn is dealt with in vc_chr_open() */ } -static void char_vc_class_init(ObjectClass *oc, void *data) +static void char_vc_class_init(ObjectClass *oc, const void *data) { VCChardevClass *vc = CHARDEV_VC_CLASS(oc); ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/ui/vdagent.c b/ui/vdagent.c index 724eff972f..04513ded29 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -905,7 +905,7 @@ static void vdagent_chr_parse(QemuOpts *opts, ChardevBackend *backend, /* ------------------------------------------------------------------ */ -static void vdagent_chr_class_init(ObjectClass *oc, void *data) +static void vdagent_chr_class_init(ObjectClass *oc, const void *data) { ChardevClass *cc = CHARDEV_CLASS(oc); diff --git a/util/main-loop.c b/util/main-loop.c index 4683db1888..51aeb2432e 100644 --- a/util/main-loop.c +++ b/util/main-loop.c @@ -220,7 +220,7 @@ static bool main_loop_can_be_deleted(EventLoopBase *base) return false; } -static void main_loop_class_init(ObjectClass *oc, void *class_data) +static void main_loop_class_init(ObjectClass *oc, const void *class_data) { EventLoopBaseClass *bc = EVENT_LOOP_BASE_CLASS(oc); diff --git a/util/thread-context.c b/util/thread-context.c index 2bc7883b9e..95228a3d4f 100644 --- a/util/thread-context.c +++ b/util/thread-context.c @@ -273,7 +273,7 @@ static void thread_context_instance_complete(UserCreatable *uc, Error **errp) } } -static void thread_context_class_init(ObjectClass *oc, void *data) +static void thread_context_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); From b282b859cf3442d922644e2cd2bee68272baafd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Feb 2025 09:56:56 +0100 Subject: [PATCH 0309/2760] qom: Constify TypeInfo::class_data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All callers now correctly expect a const class data. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250424194905.82506-5-philmd@linaro.org> --- hw/arm/armsse.c | 2 +- hw/block/m25p80.c | 2 +- hw/isa/vt82c686.c | 4 ++-- hw/net/e1000.c | 2 +- hw/ppc/spapr_cpu_core.c | 2 +- hw/scsi/megasas.c | 2 +- hw/sensor/tmp421.c | 2 +- hw/virtio/virtio-pci.c | 4 ++-- include/qom/object.h | 2 +- qom/object.c | 2 +- rust/qemu-api/src/qom.rs | 2 +- scripts/codeconverter/codeconverter/test_regexps.py | 2 +- target/arm/cpu.c | 2 +- target/arm/cpu64.c | 2 +- target/mips/cpu.c | 2 +- target/s390x/cpu_models.c | 4 ++-- target/sparc/cpu.c | 2 +- target/xtensa/helper.c | 2 +- 18 files changed, 21 insertions(+), 21 deletions(-) diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index d65a46b8d8..9403b65ddb 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -1730,7 +1730,7 @@ static void armsse_register_types(void) .name = armsse_variants[i].name, .parent = TYPE_ARM_SSE, .class_init = armsse_class_init, - .class_data = (void *)&armsse_variants[i], + .class_data = &armsse_variants[i], }; type_register_static(&ti); } diff --git a/hw/block/m25p80.c b/hw/block/m25p80.c index 75b9d71251..a5336d92ff 100644 --- a/hw/block/m25p80.c +++ b/hw/block/m25p80.c @@ -1893,7 +1893,7 @@ static void m25p80_register_types(void) .name = known_devices[i].part_name, .parent = TYPE_M25P80, .class_init = m25p80_class_init, - .class_data = (void *)&known_devices[i], + .class_data = &known_devices[i], }; type_register_static(&ti); } diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index 80366aaf64..c62afc907b 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -259,7 +259,7 @@ static const TypeInfo vt82c686b_pm_info = { .name = TYPE_VT82C686B_PM, .parent = TYPE_VIA_PM, .class_init = via_pm_class_init, - .class_data = (void *)&vt82c686b_pm_init_info, + .class_data = &vt82c686b_pm_init_info, }; static const ViaPMInitInfo vt8231_pm_init_info = { @@ -272,7 +272,7 @@ static const TypeInfo vt8231_pm_info = { .name = TYPE_VT8231_PM, .parent = TYPE_VIA_PM, .class_init = via_pm_class_init, - .class_data = (void *)&vt8231_pm_init_info, + .class_data = &vt8231_pm_init_info, }; diff --git a/hw/net/e1000.c b/hw/net/e1000.c index d49730f4ad..13814e84d1 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -1770,7 +1770,7 @@ static void e1000_register_types(void) type_info.name = info->name; type_info.parent = TYPE_E1000_BASE; - type_info.class_data = (void *)info; + type_info.class_data = info; type_info.class_init = e1000_class_init; type_register_static(&type_info); diff --git a/hw/ppc/spapr_cpu_core.c b/hw/ppc/spapr_cpu_core.c index b4b926d759..4952f9bd2c 100644 --- a/hw/ppc/spapr_cpu_core.c +++ b/hw/ppc/spapr_cpu_core.c @@ -388,7 +388,7 @@ static void spapr_cpu_core_class_init(ObjectClass *oc, const void *data) #define DEFINE_SPAPR_CPU_CORE_TYPE(cpu_model) \ { \ .parent = TYPE_SPAPR_CPU_CORE, \ - .class_data = (void *) POWERPC_CPU_TYPE_NAME(cpu_model), \ + .class_data = POWERPC_CPU_TYPE_NAME(cpu_model), \ .class_init = spapr_cpu_core_class_init, \ .name = SPAPR_CPU_CORE_TYPE_NAME(cpu_model), \ } diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index ffcabd5a8e..b024905a01 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2572,7 +2572,7 @@ static void megasas_register_types(void) type_info.name = info->name; type_info.parent = TYPE_MEGASAS_BASE; - type_info.class_data = (void *)info; + type_info.class_data = info; type_info.class_init = megasas_class_init; type_info.interfaces = info->interfaces; diff --git a/hw/sensor/tmp421.c b/hw/sensor/tmp421.c index 263bfa1bbd..3421c44086 100644 --- a/hw/sensor/tmp421.c +++ b/hw/sensor/tmp421.c @@ -382,7 +382,7 @@ static void tmp421_register_types(void) .name = devices[i].name, .parent = TYPE_TMP421, .class_init = tmp421_class_init, - .class_data = (void *) &devices[i], + .class_data = &devices[i], }; type_register_static(&ti); } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index c0fd3db063..95bf7ddd97 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2497,13 +2497,13 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) generic_type_info.parent = base_name; generic_type_info.class_init = virtio_pci_base_class_init; - generic_type_info.class_data = (void *)t; + generic_type_info.class_data = t; assert(!t->non_transitional_name); assert(!t->transitional_name); } else { base_type_info.class_init = virtio_pci_base_class_init; - base_type_info.class_data = (void *)t; + base_type_info.class_data = t; } type_register_static(&base_type_info); diff --git a/include/qom/object.h b/include/qom/object.h index 2fb86f00a6..42b75d10a4 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -488,7 +488,7 @@ struct TypeInfo void (*class_init)(ObjectClass *klass, const void *data); void (*class_base_init)(ObjectClass *klass, const void *data); - void *class_data; + const void *class_data; InterfaceInfo *interfaces; }; diff --git a/qom/object.c b/qom/object.c index 06d7367032..425ee2f0ee 100644 --- a/qom/object.c +++ b/qom/object.c @@ -57,7 +57,7 @@ struct TypeImpl void (*class_init)(ObjectClass *klass, const void *data); void (*class_base_init)(ObjectClass *klass, const void *data); - void *class_data; + const void *class_data; void (*instance_init)(Object *obj); void (*instance_post_init)(Object *obj); diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index f385cb7275..f0a79f96d5 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -513,7 +513,7 @@ pub trait ObjectImpl: ObjectType + IsA { class_size: core::mem::size_of::(), class_init: Some(rust_class_init::), class_base_init: Self::CLASS_BASE_INIT, - class_data: core::ptr::null_mut(), + class_data: core::ptr::null(), interfaces: core::ptr::null_mut(), }; diff --git a/scripts/codeconverter/codeconverter/test_regexps.py b/scripts/codeconverter/codeconverter/test_regexps.py index 7211392796..08857c5008 100644 --- a/scripts/codeconverter/codeconverter/test_regexps.py +++ b/scripts/codeconverter/codeconverter/test_regexps.py @@ -70,7 +70,7 @@ def fullmatch(regexp, s): .name = armsse_variants[i].name, .parent = TYPE_ARMSSE, .class_init = armsse_class_init, - .class_data = (void *)&armsse_variants[i], + .class_data = &armsse_variants[i], };''', re.MULTILINE) print(RE_ARRAY_ITEM) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 81257f20fd..00577f97eb 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2758,7 +2758,7 @@ void arm_cpu_register(const ARMCPUInfo *info) .parent = TYPE_ARM_CPU, .instance_init = arm_cpu_instance_init, .class_init = info->class_init ?: cpu_register_class_init, - .class_data = (void *)info, + .class_data = info, }; type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name); diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 1184c92b4c..eaf5705cdc 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -855,7 +855,7 @@ void aarch64_cpu_register(const ARMCPUInfo *info) .parent = TYPE_AARCH64_CPU, .instance_init = aarch64_cpu_instance_init, .class_init = info->class_init ?: cpu_register_class_init, - .class_data = (void *)info, + .class_data = info, }; type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name); diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 29611a0a1c..d13361a150 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -626,7 +626,7 @@ static void mips_register_cpudef_type(const struct mips_def_t *def) .name = typename, .parent = TYPE_MIPS_CPU, .class_init = mips_cpu_cpudef_class_init, - .class_data = (void *)def, + .class_data = def, }; type_register_static(&ti); diff --git a/target/s390x/cpu_models.c b/target/s390x/cpu_models.c index b097ed55d9..8951f1b36f 100644 --- a/target/s390x/cpu_models.c +++ b/target/s390x/cpu_models.c @@ -1072,7 +1072,7 @@ static void register_types(void) .instance_init = s390_cpu_model_initfn, .instance_finalize = s390_cpu_model_finalize, .class_init = s390_base_cpu_model_class_init, - .class_data = (void *) &s390_cpu_defs[i], + .class_data = &s390_cpu_defs[i], }; char *name = s390_cpu_type_name(s390_cpu_defs[i].name); TypeInfo ti = { @@ -1081,7 +1081,7 @@ static void register_types(void) .instance_init = s390_cpu_model_initfn, .instance_finalize = s390_cpu_model_finalize, .class_init = s390_cpu_model_class_init, - .class_data = (void *) &s390_cpu_defs[i], + .class_data = &s390_cpu_defs[i], }; type_register_static(&ti_base); diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 981aa86e0e..bc753d5f62 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -1104,7 +1104,7 @@ static void sparc_register_cpudef_type(const struct sparc_def_t *def) .name = typename, .parent = TYPE_SPARC_CPU, .class_init = sparc_cpu_cpudef_class_init, - .class_data = (void *)def, + .class_data = def, }; type_register_static(&ti); diff --git a/target/xtensa/helper.c b/target/xtensa/helper.c index 0459787981..2d93b45036 100644 --- a/target/xtensa/helper.c +++ b/target/xtensa/helper.c @@ -192,7 +192,7 @@ void xtensa_register_core(XtensaConfigList *node) TypeInfo type = { .parent = TYPE_XTENSA_CPU, .class_init = xtensa_core_class_init, - .class_data = (void *)node->config, + .class_data = node->config, }; xtensa_finalize_config(node->config); From 231bf6dda1ef7b4894ba374efb248c65b1841e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 23 Apr 2025 18:44:12 +0200 Subject: [PATCH 0310/2760] qom: Constify TypeInfo::interfaces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250424194905.82506-6-philmd@linaro.org> --- hw/scsi/megasas.c | 2 +- include/hw/virtio/virtio-pci.h | 2 +- include/qom/object.h | 2 +- rust/qemu-api/src/qom.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index b024905a01..a39e3e0e4f 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2486,7 +2486,7 @@ typedef struct MegasasInfo { const VMStateDescription *vmsd; const Property *props; size_t props_count; - InterfaceInfo *interfaces; + const InterfaceInfo *interfaces; } MegasasInfo; static struct MegasasInfo megasas_devices[] = { diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h index 567a9b0a9d..31ec144509 100644 --- a/include/hw/virtio/virtio-pci.h +++ b/include/hw/virtio/virtio-pci.h @@ -256,7 +256,7 @@ typedef struct VirtioPCIDeviceTypeInfo { void (*instance_init)(Object *obj); void (*instance_finalize)(Object *obj); void (*class_init)(ObjectClass *klass, const void *data); - InterfaceInfo *interfaces; + const InterfaceInfo *interfaces; } VirtioPCIDeviceTypeInfo; /* Register virtio-pci type(s). @t must be static. */ diff --git a/include/qom/object.h b/include/qom/object.h index 42b75d10a4..26a45f638c 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -490,7 +490,7 @@ struct TypeInfo void (*class_base_init)(ObjectClass *klass, const void *data); const void *class_data; - InterfaceInfo *interfaces; + const InterfaceInfo *interfaces; }; /** diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index f0a79f96d5..f1b4022157 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -514,7 +514,7 @@ pub trait ObjectImpl: ObjectType + IsA { class_init: Some(rust_class_init::), class_base_init: Self::CLASS_BASE_INIT, class_data: core::ptr::null(), - interfaces: core::ptr::null_mut(), + interfaces: core::ptr::null(), }; // methods on ObjectClass From 2cd09e47aa522dfc7bb206f13d6dccb68dd09887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 23 Apr 2025 18:46:19 +0200 Subject: [PATCH 0311/2760] qom: Make InterfaceInfo[] uses const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mechanical change using: $ sed -i -E 's/\(InterfaceInfo.?\[/\(const InterfaceInfo\[/g' \ $(git grep -lE '\(InterfaceInfo.?\[\]\)') Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250424194905.82506-7-philmd@linaro.org> --- authz/list.c | 2 +- authz/listfile.c | 2 +- authz/pamacct.c | 2 +- authz/simple.c | 2 +- backends/cryptodev.c | 2 +- backends/dbus-vmstate.c | 2 +- backends/hostmem.c | 2 +- backends/iommufd.c | 2 +- backends/rng.c | 2 +- block/throttle-groups.c | 2 +- crypto/secret_common.c | 2 +- crypto/tls-cipher-suites.c | 2 +- crypto/tlscredsanon.c | 2 +- crypto/tlscredspsk.c | 2 +- crypto/tlscredsx509.c | 2 +- event-loop-base.c | 2 +- hw/acpi/erst.c | 2 +- hw/acpi/generic_event_device.c | 2 +- hw/acpi/piix4.c | 2 +- hw/arm/armsse.c | 2 +- hw/arm/mps2-tz.c | 2 +- hw/arm/virt.c | 2 +- hw/audio/ac97.c | 2 +- hw/audio/es1370.c | 2 +- hw/audio/intel-hda.c | 2 +- hw/audio/via-ac97.c | 4 ++-- hw/block/fdc-isa.c | 2 +- hw/char/diva-gsp.c | 4 ++-- hw/char/parallel.c | 2 +- hw/char/serial-isa.c | 2 +- hw/char/serial-pci-multi.c | 4 ++-- hw/char/serial-pci.c | 2 +- hw/char/virtio-serial-bus.c | 2 +- hw/core/bus.c | 2 +- hw/core/qdev.c | 2 +- hw/cxl/switch-mailbox-cci.c | 2 +- hw/display/apple-gfx-pci.m | 2 +- hw/display/ati.c | 2 +- hw/display/bochs-display.c | 2 +- hw/display/cirrus_vga.c | 2 +- hw/display/qxl.c | 2 +- hw/display/sm501.c | 2 +- hw/display/vga-pci.c | 2 +- hw/display/virtio-gpu-pci-rutabaga.c | 2 +- hw/display/vmware_vga.c | 2 +- hw/dma/i8257.c | 2 +- hw/dma/xilinx_axidma.c | 4 ++-- hw/dma/xlnx_csu_dma.c | 2 +- hw/hppa/machine.c | 4 ++-- hw/i2c/smbus_ich9.c | 2 +- hw/i386/amd_iommu.c | 2 +- hw/i386/microvm.c | 2 +- hw/i386/pc.c | 2 +- hw/i386/sgx-epc.c | 2 +- hw/i386/x86.c | 2 +- hw/i386/xen/xen_platform.c | 2 +- hw/i386/xen/xen_pvdevice.c | 2 +- hw/ide/ich.c | 2 +- hw/ide/pci.c | 2 +- hw/input/pckbd.c | 2 +- hw/intc/arm_gic_common.c | 2 +- hw/intc/arm_gicv3_common.c | 2 +- hw/intc/goldfish_pic.c | 2 +- hw/intc/i8259_common.c | 2 +- hw/intc/ioapic_common.c | 2 +- hw/intc/loongarch_extioi_common.c | 2 +- hw/intc/loongarch_ipi.c | 2 +- hw/intc/m68k_irqc.c | 2 +- hw/intc/pnv_xive.c | 2 +- hw/intc/pnv_xive2.c | 2 +- hw/intc/slavio_intctl.c | 2 +- hw/intc/spapr_xive.c | 2 +- hw/intc/xics_spapr.c | 2 +- hw/intc/xive.c | 2 +- hw/intc/xive2.c | 2 +- hw/ipack/tpci200.c | 2 +- hw/ipmi/isa_ipmi_bt.c | 2 +- hw/ipmi/isa_ipmi_kcs.c | 2 +- hw/ipmi/pci_ipmi_bt.c | 2 +- hw/ipmi/pci_ipmi_kcs.c | 2 +- hw/ipmi/smbus_ipmi.c | 2 +- hw/isa/i82378.c | 2 +- hw/isa/lpc_ich9.c | 2 +- hw/isa/piix.c | 2 +- hw/isa/vt82c686.c | 4 ++-- hw/loongarch/virt.c | 2 +- hw/m68k/q800-glue.c | 2 +- hw/mem/cxl_type3.c | 2 +- hw/mem/pc-dimm.c | 2 +- hw/misc/applesmc.c | 2 +- hw/misc/edu.c | 2 +- hw/misc/ivshmem-pci.c | 2 +- hw/misc/macio/gpio.c | 2 +- hw/misc/macio/macio.c | 2 +- hw/misc/pci-testdev.c | 2 +- hw/misc/pvpanic-isa.c | 2 +- hw/misc/pvpanic-pci.c | 2 +- hw/misc/xlnx-versal-cframe-reg.c | 2 +- hw/misc/xlnx-versal-cfu.c | 4 ++-- hw/net/can/can_kvaser_pci.c | 2 +- hw/net/can/can_mioe3680_pci.c | 2 +- hw/net/can/can_pcm3680_pci.c | 2 +- hw/net/can/ctucan_pci.c | 2 +- hw/net/e1000.c | 2 +- hw/net/e1000e.c | 2 +- hw/net/eepro100.c | 2 +- hw/net/igb.c | 2 +- hw/net/igbvf.c | 2 +- hw/net/ne2000-pci.c | 2 +- hw/net/pcnet-pci.c | 2 +- hw/net/rocker/rocker.c | 2 +- hw/net/rtl8139.c | 2 +- hw/net/sungem.c | 2 +- hw/net/sunhme.c | 2 +- hw/net/tulip.c | 2 +- hw/net/vmxnet3.c | 2 +- hw/net/xilinx_axienet.c | 4 ++-- hw/nvme/ctrl.c | 2 +- hw/pci-bridge/cxl_downstream.c | 2 +- hw/pci-bridge/cxl_root_port.c | 2 +- hw/pci-bridge/cxl_upstream.c | 2 +- hw/pci-bridge/i82801b11.c | 2 +- hw/pci-bridge/pci_bridge_dev.c | 2 +- hw/pci-bridge/pci_expander_bridge.c | 6 +++--- hw/pci-bridge/pcie_pci_bridge.c | 2 +- hw/pci-bridge/pcie_root_port.c | 2 +- hw/pci-bridge/simba.c | 2 +- hw/pci-bridge/xio3130_downstream.c | 2 +- hw/pci-bridge/xio3130_upstream.c | 2 +- hw/pci-host/articia.c | 4 ++-- hw/pci-host/bonito.c | 2 +- hw/pci-host/designware.c | 2 +- hw/pci-host/gpex.c | 2 +- hw/pci-host/grackle.c | 2 +- hw/pci-host/gt64120.c | 2 +- hw/pci-host/i440fx.c | 2 +- hw/pci-host/mv64361.c | 2 +- hw/pci-host/pnv_phb3_pbcq.c | 2 +- hw/pci-host/pnv_phb4.c | 2 +- hw/pci-host/pnv_phb4_pec.c | 4 ++-- hw/pci-host/ppc4xx_pci.c | 2 +- hw/pci-host/ppce500.c | 2 +- hw/pci-host/q35.c | 2 +- hw/pci-host/raven.c | 2 +- hw/pci-host/sabre.c | 2 +- hw/pci-host/sh_pci.c | 2 +- hw/pci-host/uninorth.c | 8 ++++---- hw/pci-host/versatile.c | 2 +- hw/pci-host/xilinx-pcie.c | 2 +- hw/pci/pci.c | 2 +- hw/pci/pci_bridge.c | 2 +- hw/pci/pcie_port.c | 2 +- hw/ppc/e500plat.c | 2 +- hw/ppc/mac_newworld.c | 2 +- hw/ppc/mac_oldworld.c | 2 +- hw/ppc/pegasos2.c | 2 +- hw/ppc/pnv.c | 8 ++++---- hw/ppc/pnv_adu.c | 2 +- hw/ppc/pnv_chiptod.c | 4 ++-- hw/ppc/pnv_i2c.c | 2 +- hw/ppc/pnv_lpc.c | 2 +- hw/ppc/pnv_n1_chiplet.c | 2 +- hw/ppc/pnv_nest_pervasive.c | 2 +- hw/ppc/pnv_psi.c | 4 ++-- hw/ppc/spapr.c | 2 +- hw/ppc/spapr_pci.c | 2 +- hw/remote/machine.c | 2 +- hw/remote/proxy.c | 2 +- hw/remote/remote-obj.c | 2 +- hw/remote/vfio-user-obj.c | 2 +- hw/riscv/riscv-iommu-pci.c | 2 +- hw/riscv/virt.c | 2 +- hw/rtc/m48t59-isa.c | 2 +- hw/rtc/m48t59.c | 2 +- hw/rtc/mc146818rtc.c | 2 +- hw/s390x/ap-bridge.c | 2 +- hw/s390x/css-bridge.c | 2 +- hw/s390x/s390-pci-bus.c | 2 +- hw/s390x/s390-virtio-ccw.c | 2 +- hw/s390x/virtio-ccw-md.c | 2 +- hw/scsi/esp-pci.c | 2 +- hw/scsi/lsi53c895a.c | 2 +- hw/scsi/megasas.c | 4 ++-- hw/scsi/mptsas.c | 2 +- hw/scsi/scsi-bus.c | 2 +- hw/scsi/vhost-scsi.c | 2 +- hw/scsi/vhost-user-scsi.c | 2 +- hw/scsi/virtio-scsi.c | 2 +- hw/scsi/vmw_pvscsi.c | 2 +- hw/sd/sdhci-pci.c | 2 +- hw/sparc64/sun4u.c | 4 ++-- hw/ssi/pnv_spi.c | 2 +- hw/tpm/tpm_crb.c | 2 +- hw/tpm/tpm_spapr.c | 2 +- hw/tpm/tpm_tis_i2c.c | 2 +- hw/tpm/tpm_tis_isa.c | 2 +- hw/tpm/tpm_tis_sysbus.c | 2 +- hw/ufs/ufs.c | 2 +- hw/usb/bus.c | 2 +- hw/usb/dev-smartcard-reader.c | 2 +- hw/usb/hcd-ehci-pci.c | 2 +- hw/usb/hcd-ohci-pci.c | 2 +- hw/usb/hcd-uhci.c | 2 +- hw/usb/hcd-xhci-pci.c | 2 +- hw/vfio/igd.c | 2 +- hw/vfio/pci.c | 2 +- hw/virtio/virtio-md-pci.c | 2 +- hw/virtio/virtio-mem.c | 2 +- hw/virtio/virtio-pci.c | 6 +++--- hw/watchdog/wdt_i6300esb.c | 2 +- hw/xen/xen-bus.c | 2 +- hw/xen/xen-legacy-backend.c | 2 +- hw/xen/xen_pt.c | 2 +- hw/xen/xen_pt_graphics.c | 2 +- include/qom/object.h | 2 +- net/can/can_core.c | 2 +- net/can/can_host.c | 2 +- net/colo-compare.c | 2 +- net/filter.c | 2 +- scripts/codeconverter/codeconverter/test_regexps.py | 10 +++++----- scsi/pr-manager.c | 2 +- system/qtest.c | 2 +- target/i386/sev.c | 2 +- target/ppc/cpu_init.c | 2 +- tests/unit/check-qom-interface.c | 2 +- tests/unit/check-qom-proplist.c | 2 +- ui/dbus.c | 2 +- ui/input-barrier.c | 2 +- ui/input-linux.c | 2 +- util/thread-context.c | 2 +- 230 files changed, 258 insertions(+), 258 deletions(-) diff --git a/authz/list.c b/authz/list.c index bbd99f2b7f..17aa0efd80 100644 --- a/authz/list.c +++ b/authz/list.c @@ -253,7 +253,7 @@ static const TypeInfo qauthz_list_info = { .instance_size = sizeof(QAuthZList), .instance_finalize = qauthz_list_finalize, .class_init = qauthz_list_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/authz/listfile.c b/authz/listfile.c index b58d4ebd1d..13741d5a72 100644 --- a/authz/listfile.c +++ b/authz/listfile.c @@ -272,7 +272,7 @@ static const TypeInfo qauthz_list_file_info = { .instance_size = sizeof(QAuthZListFile), .instance_finalize = qauthz_list_file_finalize, .class_init = qauthz_list_file_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/authz/pamacct.c b/authz/pamacct.c index 07b8aad497..c0ad67479a 100644 --- a/authz/pamacct.c +++ b/authz/pamacct.c @@ -136,7 +136,7 @@ static const TypeInfo qauthz_pam_info = { .instance_size = sizeof(QAuthZPAM), .instance_finalize = qauthz_pam_finalize, .class_init = qauthz_pam_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/authz/simple.c b/authz/simple.c index f6985b840e..f8f2b98518 100644 --- a/authz/simple.c +++ b/authz/simple.c @@ -111,7 +111,7 @@ static const TypeInfo qauthz_simple_info = { .instance_size = sizeof(QAuthZSimple), .instance_finalize = qauthz_simple_finalize, .class_init = qauthz_simple_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/backends/cryptodev.c b/backends/cryptodev.c index 51bbe5ce40..79f8882d3b 100644 --- a/backends/cryptodev.c +++ b/backends/cryptodev.c @@ -641,7 +641,7 @@ static const TypeInfo cryptodev_backend_info = { .instance_finalize = cryptodev_backend_finalize, .class_size = sizeof(CryptoDevBackendClass), .class_init = cryptodev_backend_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/backends/dbus-vmstate.c b/backends/dbus-vmstate.c index 8c2deef43d..7d5b58b4c9 100644 --- a/backends/dbus-vmstate.c +++ b/backends/dbus-vmstate.c @@ -505,7 +505,7 @@ static const TypeInfo dbus_vmstate_info = { .instance_size = sizeof(DBusVMState), .instance_finalize = dbus_vmstate_finalize, .class_init = dbus_vmstate_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { TYPE_VMSTATE_IF }, { } diff --git a/backends/hostmem.c b/backends/hostmem.c index 195f37fa44..35734d6f4d 100644 --- a/backends/hostmem.c +++ b/backends/hostmem.c @@ -586,7 +586,7 @@ static const TypeInfo host_memory_backend_info = { .instance_size = sizeof(HostMemoryBackend), .instance_init = host_memory_backend_init, .instance_post_init = host_memory_backend_post_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/backends/iommufd.c b/backends/iommufd.c index 17f7ae3809..1498102099 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -342,7 +342,7 @@ static const TypeInfo types[] = { .instance_finalize = iommufd_backend_finalize, .class_size = sizeof(IOMMUFDBackendClass), .class_init = iommufd_backend_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/backends/rng.c b/backends/rng.c index b3480d27a1..ab94dfea85 100644 --- a/backends/rng.c +++ b/backends/rng.c @@ -119,7 +119,7 @@ static const TypeInfo rng_backend_info = { .class_size = sizeof(RngBackendClass), .class_init = rng_backend_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/block/throttle-groups.c b/block/throttle-groups.c index 9720cafb96..66fdce9a90 100644 --- a/block/throttle-groups.c +++ b/block/throttle-groups.c @@ -967,7 +967,7 @@ static const TypeInfo throttle_group_info = { .instance_size = sizeof(ThrottleGroup), .instance_init = throttle_group_obj_init, .instance_finalize = throttle_group_obj_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } }, diff --git a/crypto/secret_common.c b/crypto/secret_common.c index 2399ce412b..a5ecb876ae 100644 --- a/crypto/secret_common.c +++ b/crypto/secret_common.c @@ -375,7 +375,7 @@ static const TypeInfo qcrypto_secret_info = { .class_size = sizeof(QCryptoSecretCommonClass), .class_init = qcrypto_secret_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/crypto/tls-cipher-suites.c b/crypto/tls-cipher-suites.c index e546cc7c0e..d9b61d0c08 100644 --- a/crypto/tls-cipher-suites.c +++ b/crypto/tls-cipher-suites.c @@ -118,7 +118,7 @@ static const TypeInfo qcrypto_tls_cipher_suites_info = { .instance_size = sizeof(QCryptoTLSCipherSuites), .class_size = sizeof(QCryptoTLSCredsClass), .class_init = qcrypto_tls_cipher_suites_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { TYPE_FW_CFG_DATA_GENERATOR_INTERFACE }, { } diff --git a/crypto/tlscredsanon.c b/crypto/tlscredsanon.c index 0e2d133821..44af9e6c9a 100644 --- a/crypto/tlscredsanon.c +++ b/crypto/tlscredsanon.c @@ -152,7 +152,7 @@ static const TypeInfo qcrypto_tls_creds_anon_info = { .instance_finalize = qcrypto_tls_creds_anon_finalize, .class_size = sizeof(QCryptoTLSCredsAnonClass), .class_init = qcrypto_tls_creds_anon_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/crypto/tlscredspsk.c b/crypto/tlscredspsk.c index 287c2a3c96..5b68a6b7ba 100644 --- a/crypto/tlscredspsk.c +++ b/crypto/tlscredspsk.c @@ -255,7 +255,7 @@ static const TypeInfo qcrypto_tls_creds_psk_info = { .instance_finalize = qcrypto_tls_creds_psk_finalize, .class_size = sizeof(QCryptoTLSCredsPSKClass), .class_init = qcrypto_tls_creds_psk_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c index 143993f539..63a72fe47c 100644 --- a/crypto/tlscredsx509.c +++ b/crypto/tlscredsx509.c @@ -828,7 +828,7 @@ static const TypeInfo qcrypto_tls_creds_x509_info = { .instance_finalize = qcrypto_tls_creds_x509_finalize, .class_size = sizeof(QCryptoTLSCredsX509Class), .class_init = qcrypto_tls_creds_x509_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/event-loop-base.c b/event-loop-base.c index 733c54486c..8ca143bea4 100644 --- a/event-loop-base.c +++ b/event-loop-base.c @@ -126,7 +126,7 @@ static const TypeInfo event_loop_base_info = { .class_size = sizeof(EventLoopBaseClass), .class_init = event_loop_base_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/hw/acpi/erst.c b/hw/acpi/erst.c index 90148ec9dc..099cabb7ab 100644 --- a/hw/acpi/erst.c +++ b/hw/acpi/erst.c @@ -1044,7 +1044,7 @@ static const TypeInfo erst_type_info = { .parent = TYPE_PCI_DEVICE, .class_init = erst_class_init, .instance_size = sizeof(ERSTDeviceState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index f589e79a2b..d8adfea648 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -494,7 +494,7 @@ static const TypeInfo acpi_ged_info = { .instance_size = sizeof(AcpiGedState), .instance_init = acpi_ged_initfn, .class_init = acpi_ged_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, { } diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index 5860e8408b..b16d45f03e 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -657,7 +657,7 @@ static const TypeInfo piix4_pm_info = { .instance_init = piix4_pm_init, .instance_size = sizeof(PIIX4PMState), .class_init = piix4_pm_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, diff --git a/hw/arm/armsse.c b/hw/arm/armsse.c index 9403b65ddb..50ab7f4810 100644 --- a/hw/arm/armsse.c +++ b/hw/arm/armsse.c @@ -1713,7 +1713,7 @@ static const TypeInfo armsse_info = { .class_size = sizeof(ARMSSEClass), .instance_init = armsse_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IDAU_INTERFACE }, { } } diff --git a/hw/arm/mps2-tz.c b/hw/arm/mps2-tz.c index 8474549f5f..5dd87cc028 100644 --- a/hw/arm/mps2-tz.c +++ b/hw/arm/mps2-tz.c @@ -1453,7 +1453,7 @@ static const TypeInfo mps2tz_info = { .instance_size = sizeof(MPS2TZMachineState), .class_size = sizeof(MPS2TZMachineClass), .class_init = mps2tz_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IDAU_INTERFACE }, { } }, diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 17faf34aae..177f3dd22c 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -3396,7 +3396,7 @@ static const TypeInfo virt_machine_info = { .class_size = sizeof(VirtMachineClass), .class_init = virt_machine_class_init, .instance_init = virt_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index 7454cc60de..669a0463cc 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -1351,7 +1351,7 @@ static const TypeInfo ac97_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(AC97LinkState), .class_init = ac97_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 322b779814..8efb969212 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -896,7 +896,7 @@ static const TypeInfo es1370_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof (ES1370State), .class_init = es1370_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/audio/intel-hda.c b/hw/audio/intel-hda.c index 2f1b08e9c1..b256c8ccea 100644 --- a/hw/audio/intel-hda.c +++ b/hw/audio/intel-hda.c @@ -1262,7 +1262,7 @@ static const TypeInfo intel_hda_info = { .instance_size = sizeof(IntelHDAState), .class_init = intel_hda_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/audio/via-ac97.c b/hw/audio/via-ac97.c index 5feef663d8..1e0a5c7398 100644 --- a/hw/audio/via-ac97.c +++ b/hw/audio/via-ac97.c @@ -487,7 +487,7 @@ static const TypeInfo via_ac97_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(ViaAC97State), .class_init = via_ac97_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -522,7 +522,7 @@ static const TypeInfo via_mc97_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = via_mc97_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/block/fdc-isa.c b/hw/block/fdc-isa.c index fbba2ab629..6d1790e0e6 100644 --- a/hw/block/fdc-isa.c +++ b/hw/block/fdc-isa.c @@ -331,7 +331,7 @@ static const TypeInfo isa_fdc_info = { .instance_size = sizeof(FDCtrlISABus), .class_init = isabus_fdc_class_init, .instance_init = isabus_fdc_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/char/diva-gsp.c b/hw/char/diva-gsp.c index 9a623d680b..60f933191d 100644 --- a/hw/char/diva-gsp.c +++ b/hw/char/diva-gsp.c @@ -268,7 +268,7 @@ static const TypeInfo diva_aux_info = { .instance_size = sizeof(DivaAuxState), .instance_init = diva_aux_init, .class_init = diva_aux_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -282,7 +282,7 @@ static const TypeInfo diva_serial_pci_info = { .instance_size = sizeof(PCIDivaSerialState), .instance_init = diva_serial_init, .class_init = diva_serial_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/char/parallel.c b/hw/char/parallel.c index 217ddaf2e3..8732e4e9f9 100644 --- a/hw/char/parallel.c +++ b/hw/char/parallel.c @@ -627,7 +627,7 @@ static const TypeInfo parallel_isa_info = { .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(ISAParallelState), .class_init = parallel_isa_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/char/serial-isa.c b/hw/char/serial-isa.c index fe7fb1625b..0ea59a3d5c 100644 --- a/hw/char/serial-isa.c +++ b/hw/char/serial-isa.c @@ -146,7 +146,7 @@ static const TypeInfo serial_isa_info = { .instance_size = sizeof(ISASerialState), .instance_init = serial_isa_initfn, .class_init = serial_isa_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/char/serial-pci-multi.c b/hw/char/serial-pci-multi.c index ee1c0f7dc4..fb184c2e6d 100644 --- a/hw/char/serial-pci-multi.c +++ b/hw/char/serial-pci-multi.c @@ -194,7 +194,7 @@ static const TypeInfo multi_2x_serial_pci_info = { .instance_size = sizeof(PCIMultiSerialState), .instance_init = multi_serial_init, .class_init = multi_2x_serial_pci_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -206,7 +206,7 @@ static const TypeInfo multi_4x_serial_pci_info = { .instance_size = sizeof(PCIMultiSerialState), .instance_init = multi_serial_init, .class_init = multi_4x_serial_pci_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c index bd38c7428c..8707e81914 100644 --- a/hw/char/serial-pci.c +++ b/hw/char/serial-pci.c @@ -115,7 +115,7 @@ static const TypeInfo serial_pci_info = { .instance_size = sizeof(PCISerialState), .instance_init = serial_pci_init, .class_init = serial_pci_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index 00572873d2..eb79f5258b 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -1188,7 +1188,7 @@ static const TypeInfo virtio_device_info = { .parent = TYPE_VIRTIO_DEVICE, .instance_size = sizeof(VirtIOSerial), .class_init = virtio_serial_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/core/bus.c b/hw/core/bus.c index c3b431a014..bddfc22d38 100644 --- a/hw/core/bus.c +++ b/hw/core/bus.c @@ -260,7 +260,7 @@ static const TypeInfo bus_info = { .instance_init = qbus_initfn, .instance_finalize = qbus_finalize, .class_init = bus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_RESETTABLE_INTERFACE }, { } }, diff --git a/hw/core/qdev.c b/hw/core/qdev.c index 4a3760c101..f600226176 100644 --- a/hw/core/qdev.c +++ b/hw/core/qdev.c @@ -870,7 +870,7 @@ static const TypeInfo device_type_info = { .class_init = device_class_init, .abstract = true, .class_size = sizeof(DeviceClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_VMSTATE_IF }, { TYPE_RESETTABLE_INTERFACE }, { } diff --git a/hw/cxl/switch-mailbox-cci.c b/hw/cxl/switch-mailbox-cci.c index b92bbeb16e..223f220433 100644 --- a/hw/cxl/switch-mailbox-cci.c +++ b/hw/cxl/switch-mailbox-cci.c @@ -99,7 +99,7 @@ static const TypeInfo cswmbcci_info = { .parent = TYPE_PCI_DEVICE, .class_init = cswmbcci_class_init, .instance_size = sizeof(CSWMBCCIDev), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/display/apple-gfx-pci.m b/hw/display/apple-gfx-pci.m index 2f0d24f7fe..b0694f4cb8 100644 --- a/hw/display/apple-gfx-pci.m +++ b/hw/display/apple-gfx-pci.m @@ -147,7 +147,7 @@ static void apple_gfx_pci_class_init(ObjectClass *klass, const void *data) .instance_size = sizeof(AppleGFXPCIState), .class_init = apple_gfx_pci_class_init, .instance_init = apple_gfx_pci_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { }, }, diff --git a/hw/display/ati.c b/hw/display/ati.c index 4e88d09943..7de27732cd 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -1079,7 +1079,7 @@ static const TypeInfo ati_vga_info = { .instance_size = sizeof(ATIVGAState), .class_init = ati_vga_class_init, .instance_init = ati_vga_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/display/bochs-display.c b/hw/display/bochs-display.c index 1d329fc9cc..ad2821c974 100644 --- a/hw/display/bochs-display.c +++ b/hw/display/bochs-display.c @@ -374,7 +374,7 @@ static const TypeInfo bochs_display_type_info = { .instance_size = sizeof(BochsDisplayState), .instance_init = bochs_display_init, .class_init = bochs_display_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, diff --git a/hw/display/cirrus_vga.c b/hw/display/cirrus_vga.c index 4e5ae04af0..ef08694626 100644 --- a/hw/display/cirrus_vga.c +++ b/hw/display/cirrus_vga.c @@ -3013,7 +3013,7 @@ static const TypeInfo cirrus_vga_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCICirrusVGAState), .class_init = cirrus_vga_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/display/qxl.c b/hw/display/qxl.c index 6c820bcdb5..18f482ca7f 100644 --- a/hw/display/qxl.c +++ b/hw/display/qxl.c @@ -2517,7 +2517,7 @@ static const TypeInfo qxl_pci_type_info = { .instance_size = sizeof(PCIQXLDevice), .abstract = true, .class_init = qxl_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/display/sm501.c b/hw/display/sm501.c index dcff1e978e..6d2f18684c 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -2196,7 +2196,7 @@ static const TypeInfo sm501_pci_info = { .instance_size = sizeof(SM501PCIState), .class_init = sm501_pci_class_init, .instance_init = sm501_pci_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/display/vga-pci.c b/hw/display/vga-pci.c index a860197274..b81f7fd2d0 100644 --- a/hw/display/vga-pci.c +++ b/hw/display/vga-pci.c @@ -369,7 +369,7 @@ static const TypeInfo vga_pci_type_info = { .instance_size = sizeof(PCIVGAState), .abstract = true, .class_init = vga_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { TYPE_ACPI_DEV_AML_IF }, { }, diff --git a/hw/display/virtio-gpu-pci-rutabaga.c b/hw/display/virtio-gpu-pci-rutabaga.c index abbb898c65..5fdff37f2c 100644 --- a/hw/display/virtio-gpu-pci-rutabaga.c +++ b/hw/display/virtio-gpu-pci-rutabaga.c @@ -34,7 +34,7 @@ static const TypeInfo virtio_gpu_rutabaga_pci_info[] = { .parent = TYPE_VIRTIO_GPU_PCI_BASE, .instance_size = sizeof(VirtIOGPURutabagaPCI), .instance_init = virtio_gpu_rutabaga_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, } diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 7777deb17d..544bb65320 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -1363,7 +1363,7 @@ static const TypeInfo vmsvga_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(struct pci_vmsvga_state_s), .class_init = vmsvga_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/dma/i8257.c b/hw/dma/i8257.c index 1d67e50536..2463952ada 100644 --- a/hw/dma/i8257.c +++ b/hw/dma/i8257.c @@ -618,7 +618,7 @@ static const TypeInfo i8257_info = { .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(I8257State), .class_init = i8257_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ISADMA }, { } } diff --git a/hw/dma/xilinx_axidma.c b/hw/dma/xilinx_axidma.c index bf1b523ac8..2020399fd5 100644 --- a/hw/dma/xilinx_axidma.c +++ b/hw/dma/xilinx_axidma.c @@ -662,7 +662,7 @@ static const TypeInfo xilinx_axidma_data_stream_info = { .instance_size = sizeof(XilinxAXIDMAStreamSink), .class_init = xilinx_axidma_stream_class_init, .class_data = &xilinx_axidma_data_stream_class, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_STREAM_SINK }, { } } @@ -674,7 +674,7 @@ static const TypeInfo xilinx_axidma_control_stream_info = { .instance_size = sizeof(XilinxAXIDMAStreamSink), .class_init = xilinx_axidma_stream_class_init, .class_data = &xilinx_axidma_control_stream_class, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_STREAM_SINK }, { } } diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index 6943c927d0..3db3904d83 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -744,7 +744,7 @@ static const TypeInfo xlnx_csu_dma_info = { .class_init = xlnx_csu_dma_class_init, .class_size = sizeof(XlnxCSUDMAClass), .instance_init = xlnx_csu_dma_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_STREAM_SINK }, { } } diff --git a/hw/hppa/machine.c b/hw/hppa/machine.c index 1812276678..dacedc5409 100644 --- a/hw/hppa/machine.c +++ b/hw/hppa/machine.c @@ -713,7 +713,7 @@ static const TypeInfo HP_B160L_machine_init_typeinfo = { .name = MACHINE_TYPE_NAME("B160L"), .parent = TYPE_MACHINE, .class_init = HP_B160L_machine_init_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { } }, @@ -749,7 +749,7 @@ static const TypeInfo HP_C3700_machine_init_typeinfo = { .name = MACHINE_TYPE_NAME("C3700"), .parent = TYPE_MACHINE, .class_init = HP_C3700_machine_init_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { } }, diff --git a/hw/i2c/smbus_ich9.c b/hw/i2c/smbus_ich9.c index f1fca30fea..956c9b59bb 100644 --- a/hw/i2c/smbus_ich9.c +++ b/hw/i2c/smbus_ich9.c @@ -145,7 +145,7 @@ static const TypeInfo ich9_smb_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(ICH9SMBState), .class_init = ich9_smb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { TYPE_ACPI_DEV_AML_IF }, { }, diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index b94802e21a..2cf7e24a21 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1719,7 +1719,7 @@ static const TypeInfo amdvi_pci = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(AMDVIPCIState), .class_init = amdvi_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index 14a918a531..e0daf0d4fc 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -726,7 +726,7 @@ static const TypeInfo microvm_machine_info = { .instance_init = microvm_machine_initfn, .class_size = sizeof(MicrovmMachineClass), .class_init = microvm_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 49753bf0b3..70656157ca 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1870,7 +1870,7 @@ static const TypeInfo pc_machine_info = { .instance_init = pc_machine_initfn, .class_size = sizeof(PCMachineClass), .class_init = pc_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, diff --git a/hw/i386/sgx-epc.c b/hw/i386/sgx-epc.c index 8fb80900b7..2b3b2823b5 100644 --- a/hw/i386/sgx-epc.c +++ b/hw/i386/sgx-epc.c @@ -173,7 +173,7 @@ static const TypeInfo sgx_epc_info = { .instance_init = sgx_epc_init, .class_init = sgx_epc_class_init, .class_size = sizeof(DeviceClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_MEMORY_DEVICE }, { } }, diff --git a/hw/i386/x86.c b/hw/i386/x86.c index c8e2551b2b..e2d0409299 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -450,7 +450,7 @@ static const TypeInfo x86_machine_info = { .instance_init = x86_machine_initfn, .class_size = sizeof(X86MachineClass), .class_init = x86_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { } }, diff --git a/hw/i386/xen/xen_platform.c b/hw/i386/xen/xen_platform.c index 7c0d345f96..c8b852be0c 100644 --- a/hw/i386/xen/xen_platform.c +++ b/hw/i386/xen/xen_platform.c @@ -604,7 +604,7 @@ static const TypeInfo xen_platform_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIXenPlatformState), .class_init = xen_platform_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/i386/xen/xen_pvdevice.c b/hw/i386/xen/xen_pvdevice.c index 65868bd5e5..87a974ae5a 100644 --- a/hw/i386/xen/xen_pvdevice.c +++ b/hw/i386/xen/xen_pvdevice.c @@ -139,7 +139,7 @@ static const TypeInfo xen_pv_type_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(XenPVDevice), .class_init = xen_pv_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/ide/ich.c b/hw/ide/ich.c index f2773ab462..4cade0d121 100644 --- a/hw/ide/ich.c +++ b/hw/ide/ich.c @@ -197,7 +197,7 @@ static const TypeInfo ich_ahci_info = { .instance_size = sizeof(AHCIPCIState), .instance_init = pci_ich9_ahci_init, .class_init = ich_ahci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/ide/pci.c b/hw/ide/pci.c index 0ed72e4223..1e50bb9e48 100644 --- a/hw/ide/pci.c +++ b/hw/ide/pci.c @@ -625,7 +625,7 @@ static const TypeInfo pci_ide_type_info = { .instance_size = sizeof(PCIIDEState), .instance_init = pci_ide_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c index 83930dd50c..71f5f976e9 100644 --- a/hw/input/pckbd.c +++ b/hw/input/pckbd.c @@ -958,7 +958,7 @@ static const TypeInfo i8042_info = { .instance_size = sizeof(ISAKBDState), .instance_init = i8042_initfn, .class_init = i8042_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index f61d1c1fe6..0f0c48d89a 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -382,7 +382,7 @@ static const TypeInfo arm_gic_common_type = { .class_size = sizeof(ARMGICCommonClass), .class_init = arm_gic_common_class_init, .abstract = true, - .interfaces = (InterfaceInfo []) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ARM_LINUX_BOOT_IF }, { }, }, diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index dd86a50300..1cee68193c 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -644,7 +644,7 @@ static const TypeInfo arm_gicv3_common_type = { .class_init = arm_gicv3_common_class_init, .instance_finalize = arm_gicv3_finalize, .abstract = true, - .interfaces = (InterfaceInfo []) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ARM_LINUX_BOOT_IF }, { }, }, diff --git a/hw/intc/goldfish_pic.c b/hw/intc/goldfish_pic.c index b80538cdeb..2359861785 100644 --- a/hw/intc/goldfish_pic.c +++ b/hw/intc/goldfish_pic.c @@ -204,7 +204,7 @@ static const TypeInfo goldfish_pic_info = { .class_init = goldfish_pic_class_init, .instance_init = goldfish_pic_instance_init, .instance_size = sizeof(GoldfishPICState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { } }, diff --git a/hw/intc/i8259_common.c b/hw/intc/i8259_common.c index 4a92e0da90..602e44c8ea 100644 --- a/hw/intc/i8259_common.c +++ b/hw/intc/i8259_common.c @@ -226,7 +226,7 @@ static const TypeInfo pic_common_type = { .class_size = sizeof(PICCommonClass), .class_init = pic_common_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { } }, diff --git a/hw/intc/ioapic_common.c b/hw/intc/ioapic_common.c index b0381c7990..fce3486e51 100644 --- a/hw/intc/ioapic_common.c +++ b/hw/intc/ioapic_common.c @@ -215,7 +215,7 @@ static const TypeInfo ioapic_common_type = { .class_size = sizeof(IOAPICCommonClass), .class_init = ioapic_common_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { } }, diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index 126f13d12c..9e1589060c 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c @@ -196,7 +196,7 @@ static const TypeInfo loongarch_extioi_common_types[] = { .instance_size = sizeof(LoongArchExtIOICommonState), .class_size = sizeof(LoongArchExtIOICommonClass), .class_init = loongarch_extioi_common_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 4dad240689..2f8bb57828 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -162,7 +162,7 @@ static const TypeInfo loongarch_ipi_types[] = { .instance_size = sizeof(LoongarchIPIState), .class_size = sizeof(LoongarchIPIClass), .class_init = loongarch_ipi_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, diff --git a/hw/intc/m68k_irqc.c b/hw/intc/m68k_irqc.c index 215e1a6ed5..2532322618 100644 --- a/hw/intc/m68k_irqc.c +++ b/hw/intc/m68k_irqc.c @@ -110,7 +110,7 @@ static const TypeInfo m68k_irqc_type_info = { .instance_size = sizeof(M68KIRQCState), .instance_init = m68k_irqc_instance_init, .class_init = m68k_irqc_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { TYPE_INTERRUPT_STATS_PROVIDER }, { } diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index cd73881b5b..935c0e4742 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -2106,7 +2106,7 @@ static const TypeInfo pnv_xive_info = { .instance_size = sizeof(PnvXive), .class_init = pnv_xive_class_init, .class_size = sizeof(PnvXiveClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 02437dddac..ec8b0c68f1 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -2547,7 +2547,7 @@ static const TypeInfo pnv_xive2_info = { .instance_size = sizeof(PnvXive2), .class_init = pnv_xive2_class_init, .class_size = sizeof(PnvXive2Class), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/intc/slavio_intctl.c b/hw/intc/slavio_intctl.c index 5776055a8b..00b80bb177 100644 --- a/hw/intc/slavio_intctl.c +++ b/hw/intc/slavio_intctl.c @@ -460,7 +460,7 @@ static const TypeInfo slavio_intctl_info = { .instance_size = sizeof(SLAVIO_INTCTLState), .instance_init = slavio_intctl_init, .class_init = slavio_intctl_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { } }, diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 7fde6059bf..440edb97d8 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -856,7 +856,7 @@ static const TypeInfo spapr_xive_info = { .instance_size = sizeof(SpaprXive), .class_init = spapr_xive_class_init, .class_size = sizeof(SpaprXiveClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_SPAPR_INTC }, { } }, diff --git a/hw/intc/xics_spapr.c b/hw/intc/xics_spapr.c index 9e465fb8f3..7663596a4a 100644 --- a/hw/intc/xics_spapr.c +++ b/hw/intc/xics_spapr.c @@ -461,7 +461,7 @@ static const TypeInfo ics_spapr_info = { .name = TYPE_ICS_SPAPR, .parent = TYPE_ICS, .class_init = ics_spapr_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_SPAPR_INTC }, { } }, diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 069c1e9a5e..27b473e4d7 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -2108,7 +2108,7 @@ static const TypeInfo xive_router_info = { .instance_size = sizeof(XiveRouter), .class_size = sizeof(XiveRouterClass), .class_init = xive_router_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_NOTIFIER }, { TYPE_XIVE_PRESENTER }, { } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 3337a943fb..a08cf906d0 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1609,7 +1609,7 @@ static const TypeInfo xive2_router_info = { .instance_size = sizeof(Xive2Router), .class_size = sizeof(Xive2RouterClass), .class_init = xive2_router_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_NOTIFIER }, { TYPE_XIVE_PRESENTER }, { } diff --git a/hw/ipack/tpci200.c b/hw/ipack/tpci200.c index f6993330d2..40b30517c7 100644 --- a/hw/ipack/tpci200.c +++ b/hw/ipack/tpci200.c @@ -650,7 +650,7 @@ static const TypeInfo tpci200_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(TPCI200State), .class_init = tpci200_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/ipmi/isa_ipmi_bt.c b/hw/ipmi/isa_ipmi_bt.c index db539e68ae..0ad91ccf68 100644 --- a/hw/ipmi/isa_ipmi_bt.c +++ b/hw/ipmi/isa_ipmi_bt.c @@ -161,7 +161,7 @@ static const TypeInfo isa_ipmi_bt_info = { .instance_size = sizeof(ISAIPMIBTDevice), .instance_init = isa_ipmi_bt_init, .class_init = isa_ipmi_bt_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, { TYPE_ACPI_DEV_AML_IF }, { } diff --git a/hw/ipmi/isa_ipmi_kcs.c b/hw/ipmi/isa_ipmi_kcs.c index 4cbc6c577c..418d234e0f 100644 --- a/hw/ipmi/isa_ipmi_kcs.c +++ b/hw/ipmi/isa_ipmi_kcs.c @@ -167,7 +167,7 @@ static const TypeInfo isa_ipmi_kcs_info = { .instance_size = sizeof(ISAIPMIKCSDevice), .instance_init = isa_ipmi_kcs_init, .class_init = isa_ipmi_kcs_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, { TYPE_ACPI_DEV_AML_IF }, { } diff --git a/hw/ipmi/pci_ipmi_bt.c b/hw/ipmi/pci_ipmi_bt.c index 23f65c6886..905101dcaf 100644 --- a/hw/ipmi/pci_ipmi_bt.c +++ b/hw/ipmi/pci_ipmi_bt.c @@ -145,7 +145,7 @@ static const TypeInfo pci_ipmi_bt_info = { .instance_size = sizeof(PCIIPMIBTDevice), .instance_init = pci_ipmi_bt_instance_init, .class_init = pci_ipmi_bt_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } diff --git a/hw/ipmi/pci_ipmi_kcs.c b/hw/ipmi/pci_ipmi_kcs.c index 4077b6a7b0..4d6cde826e 100644 --- a/hw/ipmi/pci_ipmi_kcs.c +++ b/hw/ipmi/pci_ipmi_kcs.c @@ -144,7 +144,7 @@ static const TypeInfo pci_ipmi_kcs_info = { .instance_size = sizeof(PCIIPMIKCSDevice), .instance_init = pci_ipmi_kcs_instance_init, .class_init = pci_ipmi_kcs_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } diff --git a/hw/ipmi/smbus_ipmi.c b/hw/ipmi/smbus_ipmi.c index 7345844a3a..78c332de54 100644 --- a/hw/ipmi/smbus_ipmi.c +++ b/hw/ipmi/smbus_ipmi.c @@ -376,7 +376,7 @@ static const TypeInfo smbus_ipmi_info = { .instance_size = sizeof(SMBusIPMIDevice), .instance_init = smbus_ipmi_init, .class_init = smbus_ipmi_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_IPMI_INTERFACE }, { TYPE_ACPI_DEV_AML_IF }, { } diff --git a/hw/isa/i82378.c b/hw/isa/i82378.c index 26c8ec4f77..06e8f0ce3e 100644 --- a/hw/isa/i82378.c +++ b/hw/isa/i82378.c @@ -142,7 +142,7 @@ static const TypeInfo i82378_type_info = { .instance_size = sizeof(I82378State), .instance_init = i82378_init, .class_init = i82378_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/isa/lpc_ich9.c b/hw/isa/lpc_ich9.c index d3e623b1e8..71afb45b63 100644 --- a/hw/isa/lpc_ich9.c +++ b/hw/isa/lpc_ich9.c @@ -915,7 +915,7 @@ static const TypeInfo ich9_lpc_info = { .instance_size = sizeof(ICH9LPCState), .instance_init = ich9_lpc_initfn, .class_init = ich9_lpc_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, diff --git a/hw/isa/piix.c b/hw/isa/piix.c index 2c6e76f97c..52c14d3cd5 100644 --- a/hw/isa/piix.c +++ b/hw/isa/piix.c @@ -445,7 +445,7 @@ static const TypeInfo piix_pci_type_info = { .instance_init = pci_piix_init, .abstract = true, .class_init = pci_piix_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { TYPE_ACPI_DEV_AML_IF }, { }, diff --git a/hw/isa/vt82c686.c b/hw/isa/vt82c686.c index c62afc907b..337958617a 100644 --- a/hw/isa/vt82c686.c +++ b/hw/isa/vt82c686.c @@ -243,7 +243,7 @@ static const TypeInfo via_pm_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(ViaPMState), .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -634,7 +634,7 @@ static const TypeInfo via_isa_info = { .instance_size = sizeof(ViaISAState), .instance_init = via_isa_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index fde25e9409..779544fada 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -1186,7 +1186,7 @@ static const TypeInfo virt_machine_types[] = { .instance_size = sizeof(LoongArchVirtMachineState), .class_init = virt_class_init, .instance_init = virt_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, diff --git a/hw/m68k/q800-glue.c b/hw/m68k/q800-glue.c index b428e7c833..36de67c328 100644 --- a/hw/m68k/q800-glue.c +++ b/hw/m68k/q800-glue.c @@ -248,7 +248,7 @@ static const TypeInfo glue_info_types[] = { .instance_init = glue_init, .instance_finalize = glue_finalize, .class_init = glue_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { } }, diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index c95722a2ae..bba923f8ea 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -2174,7 +2174,7 @@ static const TypeInfo ct3d_info = { .class_size = sizeof(struct CXLType3Class), .class_init = ct3_class_init, .instance_size = sizeof(CXLType3Dev), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CXL_DEVICE }, { INTERFACE_PCIE_DEVICE }, {} diff --git a/hw/mem/pc-dimm.c b/hw/mem/pc-dimm.c index 6f68171442..f701d5b5f9 100644 --- a/hw/mem/pc-dimm.c +++ b/hw/mem/pc-dimm.c @@ -301,7 +301,7 @@ static const TypeInfo pc_dimm_info = { .instance_init = pc_dimm_init, .class_init = pc_dimm_class_init, .class_size = sizeof(PCDIMMDeviceClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_MEMORY_DEVICE }, { } }, diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c index d83a81b60d..a015d4a9b8 100644 --- a/hw/misc/applesmc.c +++ b/hw/misc/applesmc.c @@ -393,7 +393,7 @@ static const TypeInfo applesmc_isa_info = { .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(AppleSMCState), .class_init = qdev_applesmc_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/misc/edu.c b/hw/misc/edu.c index 8224603593..cece633e11 100644 --- a/hw/misc/edu.c +++ b/hw/misc/edu.c @@ -436,7 +436,7 @@ static const TypeInfo edu_types[] = { .instance_size = sizeof(EduState), .instance_init = edu_instance_init, .class_init = edu_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c index 2844b6f909..5a10bca633 100644 --- a/hw/misc/ivshmem-pci.c +++ b/hw/misc/ivshmem-pci.c @@ -1002,7 +1002,7 @@ static const TypeInfo ivshmem_common_info = { .instance_size = sizeof(IVShmemState), .abstract = true, .class_init = ivshmem_common_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/misc/macio/gpio.c b/hw/misc/macio/gpio.c index e5d1e1168e..990551f91f 100644 --- a/hw/misc/macio/gpio.c +++ b/hw/misc/macio/gpio.c @@ -210,7 +210,7 @@ static const TypeInfo macio_gpio_init_info = { .instance_size = sizeof(MacIOGPIOState), .instance_init = macio_gpio_init, .class_init = macio_gpio_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { } }, diff --git a/hw/misc/macio/macio.c b/hw/misc/macio/macio.c index b0418db49e..6710485d72 100644 --- a/hw/misc/macio/macio.c +++ b/hw/misc/macio/macio.c @@ -465,7 +465,7 @@ static const TypeInfo macio_type_info = { .instance_init = macio_instance_init, .abstract = true, .class_init = macio_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 0ea26451f1..3f6a8bba84 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -345,7 +345,7 @@ static const TypeInfo pci_testdev_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCITestDevState), .class_init = pci_testdev_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/misc/pvpanic-isa.c b/hw/misc/pvpanic-isa.c index 55522ee56c..f7b421c713 100644 --- a/hw/misc/pvpanic-isa.c +++ b/hw/misc/pvpanic-isa.c @@ -121,7 +121,7 @@ static const TypeInfo pvpanic_isa_info = { .instance_size = sizeof(PVPanicISAState), .instance_init = pvpanic_isa_initfn, .class_init = pvpanic_isa_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/misc/pvpanic-pci.c b/hw/misc/pvpanic-pci.c index 51ebf66107..2869b6a7ff 100644 --- a/hw/misc/pvpanic-pci.c +++ b/hw/misc/pvpanic-pci.c @@ -80,7 +80,7 @@ static const TypeInfo pvpanic_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PVPanicPCIState), .class_init = pvpanic_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } diff --git a/hw/misc/xlnx-versal-cframe-reg.c b/hw/misc/xlnx-versal-cframe-reg.c index e28d569ebe..1ce083e240 100644 --- a/hw/misc/xlnx-versal-cframe-reg.c +++ b/hw/misc/xlnx-versal-cframe-reg.c @@ -833,7 +833,7 @@ static const TypeInfo cframe_reg_info = { .instance_size = sizeof(XlnxVersalCFrameReg), .class_init = cframe_reg_class_init, .instance_init = cframe_reg_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XLNX_CFI_IF }, { } } diff --git a/hw/misc/xlnx-versal-cfu.c b/hw/misc/xlnx-versal-cfu.c index 02e4fed05b..b920fc77c3 100644 --- a/hw/misc/xlnx-versal-cfu.c +++ b/hw/misc/xlnx-versal-cfu.c @@ -532,7 +532,7 @@ static const TypeInfo cfu_apb_info = { .instance_size = sizeof(XlnxVersalCFUAPB), .class_init = cfu_apb_class_init, .instance_init = cfu_apb_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XLNX_CFI_IF }, { } } @@ -545,7 +545,7 @@ static const TypeInfo cfu_fdro_info = { .class_init = cfu_fdro_class_init, .instance_init = cfu_fdro_init, .instance_finalize = cfu_fdro_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XLNX_CFI_IF }, { } } diff --git a/hw/net/can/can_kvaser_pci.c b/hw/net/can/can_kvaser_pci.c index c0bb598bae..be16769de2 100644 --- a/hw/net/can/can_kvaser_pci.c +++ b/hw/net/can/can_kvaser_pci.c @@ -305,7 +305,7 @@ static const TypeInfo kvaser_pci_info = { .instance_size = sizeof(KvaserPCIState), .class_init = kvaser_pci_class_init, .instance_init = kvaser_pci_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/can/can_mioe3680_pci.c b/hw/net/can/can_mioe3680_pci.c index 9aac70dccd..44f3ba370d 100644 --- a/hw/net/can/can_mioe3680_pci.c +++ b/hw/net/can/can_mioe3680_pci.c @@ -248,7 +248,7 @@ static const TypeInfo mioe3680_pci_info = { .instance_size = sizeof(Mioe3680PCIState), .class_init = mioe3680_pci_class_init, .instance_init = mioe3680_pci_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/can/can_pcm3680_pci.c b/hw/net/can/can_pcm3680_pci.c index b305f7e9b7..7296d63be7 100644 --- a/hw/net/can/can_pcm3680_pci.c +++ b/hw/net/can/can_pcm3680_pci.c @@ -249,7 +249,7 @@ static const TypeInfo pcm3680i_pci_info = { .instance_size = sizeof(Pcm3680iPCIState), .class_init = pcm3680i_pci_class_init, .instance_init = pcm3680i_pci_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/can/ctucan_pci.c b/hw/net/can/ctucan_pci.c index 0dee9b59d1..bed6785433 100644 --- a/hw/net/can/ctucan_pci.c +++ b/hw/net/can/ctucan_pci.c @@ -262,7 +262,7 @@ static const TypeInfo ctucan_pci_info = { .instance_size = sizeof(CtuCanPCIState), .class_init = ctucan_pci_class_init, .instance_init = ctucan_pci_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/e1000.c b/hw/net/e1000.c index 13814e84d1..cba4999e6d 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -1732,7 +1732,7 @@ static const TypeInfo e1000_base_info = { .instance_init = e1000_instance_init, .class_size = sizeof(E1000BaseClass), .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/e1000e.c b/hw/net/e1000e.c index f38249a6a9..89e6d52ba0 100644 --- a/hw/net/e1000e.c +++ b/hw/net/e1000e.c @@ -721,7 +721,7 @@ static const TypeInfo e1000e_info = { .instance_size = sizeof(E1000EState), .class_init = e1000e_class_init, .instance_init = e1000e_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/net/eepro100.c b/hw/net/eepro100.c index ef0f9337a0..d47df5a97f 100644 --- a/hw/net/eepro100.c +++ b/hw/net/eepro100.c @@ -2094,7 +2094,7 @@ static void eepro100_register_types(void) type_info.class_init = eepro100_class_init; type_info.instance_size = sizeof(EEPRO100State); type_info.instance_init = eepro100_instance_init; - type_info.interfaces = (InterfaceInfo[]) { + type_info.interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }; diff --git a/hw/net/igb.c b/hw/net/igb.c index ba30433a50..e4c02365d6 100644 --- a/hw/net/igb.c +++ b/hw/net/igb.c @@ -635,7 +635,7 @@ static const TypeInfo igb_info = { .instance_size = sizeof(IGBState), .class_init = igb_class_init, .instance_init = igb_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/net/igbvf.c b/hw/net/igbvf.c index 91e7ccf931..31d72c4977 100644 --- a/hw/net/igbvf.c +++ b/hw/net/igbvf.c @@ -325,7 +325,7 @@ static const TypeInfo igbvf_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(IgbVfState), .class_init = igbvf_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/net/ne2000-pci.c b/hw/net/ne2000-pci.c index 2153973af4..ce937e1b61 100644 --- a/hw/net/ne2000-pci.c +++ b/hw/net/ne2000-pci.c @@ -122,7 +122,7 @@ static const TypeInfo ne2000_info = { .instance_size = sizeof(PCINE2000State), .class_init = ne2000_class_init, .instance_init = ne2000_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/pcnet-pci.c b/hw/net/pcnet-pci.c index 429c217180..0ca5bc2193 100644 --- a/hw/net/pcnet-pci.c +++ b/hw/net/pcnet-pci.c @@ -280,7 +280,7 @@ static const TypeInfo pcnet_info = { .instance_size = sizeof(PCIPCNetState), .class_init = pcnet_class_init, .instance_init = pcnet_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/rocker/rocker.c b/hw/net/rocker/rocker.c index 3d307f4ab1..cc49701dd3 100644 --- a/hw/net/rocker/rocker.c +++ b/hw/net/rocker/rocker.c @@ -1498,7 +1498,7 @@ static const TypeInfo rocker_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(Rocker), .class_init = rocker_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index ad812954cf..15b8f7501a 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -3438,7 +3438,7 @@ static const TypeInfo rtl8139_info = { .instance_size = sizeof(RTL8139State), .class_init = rtl8139_class_init, .instance_init = rtl8139_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/sungem.c b/hw/net/sungem.c index 123d08ee8e..b405eb89fa 100644 --- a/hw/net/sungem.c +++ b/hw/net/sungem.c @@ -1477,7 +1477,7 @@ static const TypeInfo sungem_info = { .instance_size = sizeof(SunGEMState), .class_init = sungem_class_init, .instance_init = sungem_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } diff --git a/hw/net/sunhme.c b/hw/net/sunhme.c index 46c9f5020b..c2f7a8483d 100644 --- a/hw/net/sunhme.c +++ b/hw/net/sunhme.c @@ -958,7 +958,7 @@ static const TypeInfo sunhme_info = { .class_init = sunhme_class_init, .instance_size = sizeof(SunHMEState), .instance_init = sunhme_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } } diff --git a/hw/net/tulip.c b/hw/net/tulip.c index fb3366d8ee..63fe513458 100644 --- a/hw/net/tulip.c +++ b/hw/net/tulip.c @@ -1035,7 +1035,7 @@ static const TypeInfo tulip_info = { .instance_size = sizeof(TULIPState), .class_init = tulip_class_init, .instance_init = tulip_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 4bcf1f902f..83d942af17 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -2522,7 +2522,7 @@ static const TypeInfo vmxnet3_info = { .instance_size = sizeof(VMXNET3State), .class_init = vmxnet3_class_init, .instance_init = vmxnet3_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } diff --git a/hw/net/xilinx_axienet.c b/hw/net/xilinx_axienet.c index e45bc048e0..1f5c748047 100644 --- a/hw/net/xilinx_axienet.c +++ b/hw/net/xilinx_axienet.c @@ -1045,7 +1045,7 @@ static const TypeInfo xilinx_enet_data_stream_info = { .parent = TYPE_OBJECT, .instance_size = sizeof(XilinxAXIEnetStreamSink), .class_init = xilinx_enet_data_stream_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_STREAM_SINK }, { } } @@ -1056,7 +1056,7 @@ static const TypeInfo xilinx_enet_control_stream_info = { .parent = TYPE_OBJECT, .instance_size = sizeof(XilinxAXIEnetStreamSink), .class_init = xilinx_enet_control_stream_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_STREAM_SINK }, { } } diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index e87295f5f8..fd935507bc 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -9221,7 +9221,7 @@ static const TypeInfo nvme_info = { .instance_size = sizeof(NvmeCtrl), .instance_init = nvme_instance_init, .class_init = nvme_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/pci-bridge/cxl_downstream.c b/hw/pci-bridge/cxl_downstream.c index ab3b550a88..1065245a8b 100644 --- a/hw/pci-bridge/cxl_downstream.c +++ b/hw/pci-bridge/cxl_downstream.c @@ -241,7 +241,7 @@ static const TypeInfo cxl_dsp_info = { .instance_size = sizeof(CXLDownstreamPort), .parent = TYPE_PCIE_SLOT, .class_init = cxl_dsp_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CXL_DEVICE }, { } diff --git a/hw/pci-bridge/cxl_root_port.c b/hw/pci-bridge/cxl_root_port.c index 8b1e149e9b..e6a4035d26 100644 --- a/hw/pci-bridge/cxl_root_port.c +++ b/hw/pci-bridge/cxl_root_port.c @@ -294,7 +294,7 @@ static const TypeInfo cxl_root_port_info = { .parent = TYPE_PCIE_ROOT_PORT, .instance_size = sizeof(CXLRootPort), .class_init = cxl_root_port_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CXL_DEVICE }, { } }, diff --git a/hw/pci-bridge/cxl_upstream.c b/hw/pci-bridge/cxl_upstream.c index 822a828555..208e0c6172 100644 --- a/hw/pci-bridge/cxl_upstream.c +++ b/hw/pci-bridge/cxl_upstream.c @@ -394,7 +394,7 @@ static const TypeInfo cxl_usp_info = { .parent = TYPE_PCIE_PORT, .instance_size = sizeof(CXLUpstreamPort), .class_init = cxl_upstream_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CXL_DEVICE }, { } diff --git a/hw/pci-bridge/i82801b11.c b/hw/pci-bridge/i82801b11.c index f2b294aee2..1d73c14c1f 100644 --- a/hw/pci-bridge/i82801b11.c +++ b/hw/pci-bridge/i82801b11.c @@ -107,7 +107,7 @@ static const TypeInfo i82801b11_bridge_info = { .parent = TYPE_PCI_BRIDGE, .instance_size = sizeof(I82801b11Bridge), .class_init = i82801b11_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-bridge/pci_bridge_dev.c b/hw/pci-bridge/pci_bridge_dev.c index 3b57583199..b328e50ab3 100644 --- a/hw/pci-bridge/pci_bridge_dev.c +++ b/hw/pci-bridge/pci_bridge_dev.c @@ -268,7 +268,7 @@ static const TypeInfo pci_bridge_dev_info = { .instance_size = sizeof(PCIBridgeDev), .class_init = pci_bridge_dev_class_init, .instance_finalize = pci_bridge_dev_instance_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } diff --git a/hw/pci-bridge/pci_expander_bridge.c b/hw/pci-bridge/pci_expander_bridge.c index 1e2e394ee8..3a29dfefc2 100644 --- a/hw/pci-bridge/pci_expander_bridge.c +++ b/hw/pci-bridge/pci_expander_bridge.c @@ -449,7 +449,7 @@ static const TypeInfo pxb_dev_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PXBDev), .class_init = pxb_dev_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -486,7 +486,7 @@ static const TypeInfo pxb_pcie_dev_info = { .parent = TYPE_PXB_DEV, .instance_size = sizeof(PXBPCIEDev), .class_init = pxb_pcie_dev_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -537,7 +537,7 @@ static const TypeInfo pxb_cxl_dev_info = { .instance_size = sizeof(PXBCXLDev), .class_init = pxb_cxl_dev_class_init, .interfaces = - (InterfaceInfo[]){ + (const InterfaceInfo[]){ { INTERFACE_CONVENTIONAL_PCI_DEVICE }, {}, }, diff --git a/hw/pci-bridge/pcie_pci_bridge.c b/hw/pci-bridge/pcie_pci_bridge.c index 833fe35cd5..fce292a519 100644 --- a/hw/pci-bridge/pcie_pci_bridge.c +++ b/hw/pci-bridge/pcie_pci_bridge.c @@ -162,7 +162,7 @@ static const TypeInfo pcie_pci_bridge_info = { .parent = TYPE_PCI_BRIDGE, .instance_size = sizeof(PCIEPCIBridge), .class_init = pcie_pci_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { INTERFACE_PCIE_DEVICE }, { }, diff --git a/hw/pci-bridge/pcie_root_port.c b/hw/pci-bridge/pcie_root_port.c index 512c2ab305..22c2fdb71e 100644 --- a/hw/pci-bridge/pcie_root_port.c +++ b/hw/pci-bridge/pcie_root_port.c @@ -188,7 +188,7 @@ static const TypeInfo rp_info = { .class_init = rp_class_init, .abstract = true, .class_size = sizeof(PCIERootPortClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/pci-bridge/simba.c b/hw/pci-bridge/simba.c index c7565d9e94..bbae594e11 100644 --- a/hw/pci-bridge/simba.c +++ b/hw/pci-bridge/simba.c @@ -87,7 +87,7 @@ static const TypeInfo simba_pci_bridge_info = { .parent = TYPE_PCI_BRIDGE, .class_init = simba_pci_bridge_class_init, .instance_size = sizeof(SimbaPCIBridge), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-bridge/xio3130_downstream.c b/hw/pci-bridge/xio3130_downstream.c index d85c23fe4a..dc7d1aa7d7 100644 --- a/hw/pci-bridge/xio3130_downstream.c +++ b/hw/pci-bridge/xio3130_downstream.c @@ -175,7 +175,7 @@ static const TypeInfo xio3130_downstream_info = { .name = TYPE_XIO3130_DOWNSTREAM, .parent = TYPE_PCIE_SLOT, .class_init = xio3130_downstream_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/pci-bridge/xio3130_upstream.c b/hw/pci-bridge/xio3130_upstream.c index d7a2715812..40057b749b 100644 --- a/hw/pci-bridge/xio3130_upstream.c +++ b/hw/pci-bridge/xio3130_upstream.c @@ -144,7 +144,7 @@ static const TypeInfo xio3130_upstream_info = { .name = "x3130-upstream", .parent = TYPE_PCIE_PORT, .class_init = xio3130_upstream_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/pci-host/articia.c b/hw/pci-host/articia.c index 043fb85e84..cc65aac2a8 100644 --- a/hw/pci-host/articia.c +++ b/hw/pci-host/articia.c @@ -273,7 +273,7 @@ static const TypeInfo articia_types[] = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(ArticiaHostState), .class_init = articia_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -283,7 +283,7 @@ static const TypeInfo articia_types[] = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = articia_pci_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/bonito.c b/hw/pci-host/bonito.c index 4508cdd21a..7d6251a78d 100644 --- a/hw/pci-host/bonito.c +++ b/hw/pci-host/bonito.c @@ -783,7 +783,7 @@ static const TypeInfo bonito_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIBonitoState), .class_init = bonito_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/designware.c b/hw/pci-host/designware.c index d03c998e3a..183f838392 100644 --- a/hw/pci-host/designware.c +++ b/hw/pci-host/designware.c @@ -769,7 +769,7 @@ static const TypeInfo designware_pcie_types[] = { .parent = TYPE_PCI_BRIDGE, .instance_size = sizeof(DesignwarePCIERoot), .class_init = designware_pcie_root_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/pci-host/gpex.c b/hw/pci-host/gpex.c index 7dcac4ee3c..b806a2286f 100644 --- a/hw/pci-host/gpex.c +++ b/hw/pci-host/gpex.c @@ -261,7 +261,7 @@ static const TypeInfo gpex_root_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(GPEXRootState), .class_init = gpex_root_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/grackle.c b/hw/pci-host/grackle.c index b48d44623d..f9da5a908c 100644 --- a/hw/pci-host/grackle.c +++ b/hw/pci-host/grackle.c @@ -116,7 +116,7 @@ static const TypeInfo grackle_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = grackle_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/gt64120.c b/hw/pci-host/gt64120.c index bd74b6e871..56a6ef93b7 100644 --- a/hw/pci-host/gt64120.c +++ b/hw/pci-host/gt64120.c @@ -1268,7 +1268,7 @@ static const TypeInfo gt64120_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = gt64120_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/i440fx.c b/hw/pci-host/i440fx.c index fcc9f3818a..e13bb1b53e 100644 --- a/hw/pci-host/i440fx.c +++ b/hw/pci-host/i440fx.c @@ -341,7 +341,7 @@ static const TypeInfo i440fx_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCII440FXState), .class_init = i440fx_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/mv64361.c b/hw/pci-host/mv64361.c index f51f385b22..e05b677010 100644 --- a/hw/pci-host/mv64361.c +++ b/hw/pci-host/mv64361.c @@ -46,7 +46,7 @@ static const TypeInfo mv64361_pcibridge_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = mv64361_pcibridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/pnv_phb3_pbcq.c b/hw/pci-host/pnv_phb3_pbcq.c index 4e24b1449d..1f7a149580 100644 --- a/hw/pci-host/pnv_phb3_pbcq.c +++ b/hw/pci-host/pnv_phb3_pbcq.c @@ -354,7 +354,7 @@ static const TypeInfo pnv_pbcq_type_info = { .instance_size = sizeof(PnvPBCQState), .instance_init = phb3_pbcq_instance_init, .class_init = pnv_pbcq_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index feb812dc1a..77ea35299d 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -1714,7 +1714,7 @@ static const TypeInfo pnv_phb4_type_info = { .instance_init = pnv_phb4_instance_init, .instance_size = sizeof(PnvPHB4), .class_init = pnv_phb4_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_NOTIFIER }, { }, } diff --git a/hw/pci-host/pnv_phb4_pec.c b/hw/pci-host/pnv_phb4_pec.c index cc46641cdf..5bac1c42ed 100644 --- a/hw/pci-host/pnv_phb4_pec.c +++ b/hw/pci-host/pnv_phb4_pec.c @@ -388,7 +388,7 @@ static const TypeInfo pnv_pec_type_info = { .instance_size = sizeof(PnvPhb4PecState), .class_init = pnv_pec_class_init, .class_size = sizeof(PnvPhb4PecClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } @@ -445,7 +445,7 @@ static const TypeInfo pnv_phb5_pec_type_info = { .instance_size = sizeof(PnvPhb4PecState), .class_init = pnv_phb5_pec_class_init, .class_size = sizeof(PnvPhb4PecClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/pci-host/ppc4xx_pci.c b/hw/pci-host/ppc4xx_pci.c index dcc4b78660..2547817688 100644 --- a/hw/pci-host/ppc4xx_pci.c +++ b/hw/pci-host/ppc4xx_pci.c @@ -370,7 +370,7 @@ static const TypeInfo ppc4xx_host_bridge_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = ppc4xx_host_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 2f6354c931..e97a515d5f 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -528,7 +528,7 @@ static const TypeInfo e500_pci_types[] = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PPCE500PCIBridgeState), .class_init = e500_host_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/q35.c b/hw/pci-host/q35.c index c2a71108f2..1951ae440c 100644 --- a/hw/pci-host/q35.c +++ b/hw/pci-host/q35.c @@ -703,7 +703,7 @@ static const TypeInfo mch_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(MCHPCIState), .class_init = mch_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index 3f158838a0..21f7ca65e0 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -416,7 +416,7 @@ static const TypeInfo raven_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(RavenPCIState), .class_init = raven_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/sabre.c b/hw/pci-host/sabre.c index f7086086f9..538624c507 100644 --- a/hw/pci-host/sabre.c +++ b/hw/pci-host/sabre.c @@ -477,7 +477,7 @@ static const TypeInfo sabre_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(SabrePCIState), .class_init = sabre_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/sh_pci.c b/hw/pci-host/sh_pci.c index 52bff66d6a..de8f6a84aa 100644 --- a/hw/pci-host/sh_pci.c +++ b/hw/pci-host/sh_pci.c @@ -186,7 +186,7 @@ static const TypeInfo sh_pcic_types[] = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = sh_pcic_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/uninorth.c b/hw/pci-host/uninorth.c index 7cb37e01d8..194037d6e7 100644 --- a/hw/pci-host/uninorth.c +++ b/hw/pci-host/uninorth.c @@ -333,7 +333,7 @@ static const TypeInfo unin_main_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = unin_main_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -361,7 +361,7 @@ static const TypeInfo u3_agp_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = u3_agp_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -389,7 +389,7 @@ static const TypeInfo unin_agp_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = unin_agp_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -418,7 +418,7 @@ static const TypeInfo unin_internal_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = unin_internal_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/versatile.c b/hw/pci-host/versatile.c index b333158e10..8ea26e3ff0 100644 --- a/hw/pci-host/versatile.c +++ b/hw/pci-host/versatile.c @@ -492,7 +492,7 @@ static const TypeInfo versatile_pci_host_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = versatile_pci_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/pci-host/xilinx-pcie.c b/hw/pci-host/xilinx-pcie.c index 70e9b2b981..c71492de9e 100644 --- a/hw/pci-host/xilinx-pcie.c +++ b/hw/pci-host/xilinx-pcie.c @@ -314,7 +314,7 @@ static const TypeInfo xilinx_pcie_root_info = { .parent = TYPE_PCI_BRIDGE, .instance_size = sizeof(XilinxPCIERoot), .class_init = xilinx_pcie_root_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/pci/pci.c b/hw/pci/pci.c index c60991def8..fe38c4c028 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -288,7 +288,7 @@ static const TypeInfo pci_bus_info = { .instance_size = sizeof(PCIBus), .class_size = sizeof(PCIBusClass), .class_init = pci_bus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_CFG_DATA_GENERATOR_INTERFACE }, { } } diff --git a/hw/pci/pci_bridge.c b/hw/pci/pci_bridge.c index 0fe66e8b12..76255c4cd8 100644 --- a/hw/pci/pci_bridge.c +++ b/hw/pci/pci_bridge.c @@ -497,7 +497,7 @@ static const TypeInfo pci_bridge_type_info = { .instance_size = sizeof(PCIBridge), .class_init = pci_bridge_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/pci/pcie_port.c b/hw/pci/pcie_port.c index 8629b3aafd..54f639e3d4 100644 --- a/hw/pci/pcie_port.c +++ b/hw/pci/pcie_port.c @@ -230,7 +230,7 @@ static const TypeInfo pcie_slot_type_info = { .instance_size = sizeof(PCIESlot), .abstract = true, .class_init = pcie_slot_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index cd594eeb3e..775b9d8da0 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -107,7 +107,7 @@ static const TypeInfo e500plat_info = { .name = TYPE_E500PLAT_MACHINE, .parent = TYPE_PPCE500_MACHINE, .class_init = e500plat_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/ppc/mac_newworld.c b/hw/ppc/mac_newworld.c index 92fe60b2a2..0b6e096116 100644 --- a/hw/ppc/mac_newworld.c +++ b/hw/ppc/mac_newworld.c @@ -639,7 +639,7 @@ static const TypeInfo core99_machine_info = { .class_init = core99_machine_class_init, .instance_init = core99_instance_init, .instance_size = sizeof(Core99MachineState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { } }, diff --git a/hw/ppc/mac_oldworld.c b/hw/ppc/mac_oldworld.c index 5c5bf99b4d..40ae936ad8 100644 --- a/hw/ppc/mac_oldworld.c +++ b/hw/ppc/mac_oldworld.c @@ -430,7 +430,7 @@ static const TypeInfo ppc_heathrow_machine_info = { .name = MACHINE_TYPE_NAME("g3beige"), .parent = TYPE_MACHINE, .class_init = heathrow_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { } }, diff --git a/hw/ppc/pegasos2.c b/hw/ppc/pegasos2.c index bb6f94f502..e15cf96427 100644 --- a/hw/ppc/pegasos2.c +++ b/hw/ppc/pegasos2.c @@ -619,7 +619,7 @@ static const TypeInfo pegasos2_machine_info = { .parent = TYPE_MACHINE, .class_init = pegasos2_machine_class_init, .instance_size = sizeof(Pegasos2MachineState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PPC_VIRTUAL_HYPERVISOR }, { TYPE_VOF_MACHINE_IF }, { } diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 4590231f88..4a49e9d1a8 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2973,7 +2973,7 @@ static const TypeInfo types[] = { .name = MACHINE_TYPE_NAME("powernv10"), .parent = TYPE_PNV_MACHINE, .class_init = pnv_machine_power10_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_FABRIC }, { }, }, @@ -2982,7 +2982,7 @@ static const TypeInfo types[] = { .name = MACHINE_TYPE_NAME("powernv9"), .parent = TYPE_PNV_MACHINE, .class_init = pnv_machine_power9_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_FABRIC }, { }, }, @@ -2991,7 +2991,7 @@ static const TypeInfo types[] = { .name = MACHINE_TYPE_NAME("powernv8"), .parent = TYPE_PNV_MACHINE, .class_init = pnv_machine_power8_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XICS_FABRIC }, { }, }, @@ -3003,7 +3003,7 @@ static const TypeInfo types[] = { .instance_size = sizeof(PnvMachineState), .class_init = pnv_machine_class_init, .class_size = sizeof(PnvMachineClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { TYPE_NMI }, { }, diff --git a/hw/ppc/pnv_adu.c b/hw/ppc/pnv_adu.c index f9620806ec..005fbda475 100644 --- a/hw/ppc/pnv_adu.c +++ b/hw/ppc/pnv_adu.c @@ -204,7 +204,7 @@ static const TypeInfo pnv_adu_type_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(PnvADU), .class_init = pnv_adu_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } }, }; diff --git a/hw/ppc/pnv_chiptod.c b/hw/ppc/pnv_chiptod.c index 4ca511a4de..b9e9c7ba3d 100644 --- a/hw/ppc/pnv_chiptod.c +++ b/hw/ppc/pnv_chiptod.c @@ -478,7 +478,7 @@ static const TypeInfo pnv_chiptod_power9_type_info = { .parent = TYPE_PNV_CHIPTOD, .instance_size = sizeof(PnvChipTOD), .class_init = pnv_chiptod_power9_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } @@ -514,7 +514,7 @@ static const TypeInfo pnv_chiptod_power10_type_info = { .parent = TYPE_PNV_CHIPTOD, .instance_size = sizeof(PnvChipTOD), .class_init = pnv_chiptod_power10_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/ppc/pnv_i2c.c b/hw/ppc/pnv_i2c.c index b2f372c874..60de479491 100644 --- a/hw/ppc/pnv_i2c.c +++ b/hw/ppc/pnv_i2c.c @@ -569,7 +569,7 @@ static const TypeInfo pnv_i2c_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(PnvI2C), .class_init = pnv_i2c_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/ppc/pnv_lpc.c b/hw/ppc/pnv_lpc.c index d92347bcd2..f6beba0917 100644 --- a/hw/ppc/pnv_lpc.c +++ b/hw/ppc/pnv_lpc.c @@ -732,7 +732,7 @@ static const TypeInfo pnv_lpc_power8_info = { .name = TYPE_PNV8_LPC, .parent = TYPE_PNV_LPC, .class_init = pnv_lpc_power8_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/ppc/pnv_n1_chiplet.c b/hw/ppc/pnv_n1_chiplet.c index 05e3fd6f73..053f6473f2 100644 --- a/hw/ppc/pnv_n1_chiplet.c +++ b/hw/ppc/pnv_n1_chiplet.c @@ -159,7 +159,7 @@ static const TypeInfo pnv_n1_chiplet_info = { .instance_init = pnv_n1_chiplet_instance_init, .instance_size = sizeof(PnvN1Chiplet), .class_init = pnv_n1_chiplet_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/ppc/pnv_nest_pervasive.c b/hw/ppc/pnv_nest_pervasive.c index b5182d54fa..1b1b14fed9 100644 --- a/hw/ppc/pnv_nest_pervasive.c +++ b/hw/ppc/pnv_nest_pervasive.c @@ -194,7 +194,7 @@ static const TypeInfo pnv_nest_pervasive_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(PnvNestChipletPervasive), .class_init = pnv_nest_pervasive_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/ppc/pnv_psi.c b/hw/ppc/pnv_psi.c index 0fd247e6ad..5d947d8b52 100644 --- a/hw/ppc/pnv_psi.c +++ b/hw/ppc/pnv_psi.c @@ -913,7 +913,7 @@ static const TypeInfo pnv_psi_power9_info = { .instance_size = sizeof(Pnv9Psi), .instance_init = pnv_psi_power9_instance_init, .class_init = pnv_psi_power9_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_XIVE_NOTIFIER }, { }, }, @@ -959,7 +959,7 @@ static const TypeInfo pnv_psi_info = { .class_init = pnv_psi_class_init, .class_size = sizeof(PnvPsiClass), .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 02663851ae..702f774cda 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4717,7 +4717,7 @@ static const TypeInfo spapr_machine_info = { .instance_finalize = spapr_machine_finalizefn, .class_size = sizeof(SpaprMachineClass), .class_init = spapr_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { TYPE_NMI }, { TYPE_HOTPLUG_HANDLER }, diff --git a/hw/ppc/spapr_pci.c b/hw/ppc/spapr_pci.c index d0468e3fe6..1ac1185825 100644 --- a/hw/ppc/spapr_pci.c +++ b/hw/ppc/spapr_pci.c @@ -2200,7 +2200,7 @@ static const TypeInfo spapr_phb_info = { .instance_size = sizeof(SpaprPhbState), .instance_finalize = spapr_phb_finalizefn, .class_init = spapr_phb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/remote/machine.c b/hw/remote/machine.c index 9fb92ec6f1..e4b47838ba 100644 --- a/hw/remote/machine.c +++ b/hw/remote/machine.c @@ -146,7 +146,7 @@ static const TypeInfo remote_machine = { .instance_size = sizeof(RemoteMachineState), .instance_init = remote_machine_instance_init, .class_init = remote_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/remote/proxy.c b/hw/remote/proxy.c index d2de48c9e3..b0165aa2a1 100644 --- a/hw/remote/proxy.c +++ b/hw/remote/proxy.c @@ -215,7 +215,7 @@ static const TypeInfo pci_proxy_dev_type_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIProxyDev), .class_init = pci_proxy_dev_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/remote/remote-obj.c b/hw/remote/remote-obj.c index 75f8d6df8a..85882902d7 100644 --- a/hw/remote/remote-obj.c +++ b/hw/remote/remote-obj.c @@ -188,7 +188,7 @@ static const TypeInfo remote_object_info = { .instance_finalize = remote_object_finalize, .class_size = sizeof(RemoteObjectClass), .class_init = remote_object_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/hw/remote/vfio-user-obj.c b/hw/remote/vfio-user-obj.c index b0ae403f06..ea6165ebdc 100644 --- a/hw/remote/vfio-user-obj.c +++ b/hw/remote/vfio-user-obj.c @@ -944,7 +944,7 @@ static const TypeInfo vfu_object_info = { .instance_finalize = vfu_object_finalize, .class_size = sizeof(VfuObjectClass), .class_init = vfu_object_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/hw/riscv/riscv-iommu-pci.c b/hw/riscv/riscv-iommu-pci.c index d93cf7521b..1f44eef74e 100644 --- a/hw/riscv/riscv-iommu-pci.c +++ b/hw/riscv/riscv-iommu-pci.c @@ -209,7 +209,7 @@ static const TypeInfo riscv_iommu_pci = { .class_init = riscv_iommu_pci_class_init, .instance_init = riscv_iommu_pci_init, .instance_size = sizeof(RISCVIOMMUStatePci), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { }, }, diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 557efd15a1..be1bf0f646 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1980,7 +1980,7 @@ static const TypeInfo virt_machine_typeinfo = { .class_init = virt_machine_class_init, .instance_init = virt_machine_instance_init, .instance_size = sizeof(RISCVVirtState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, diff --git a/hw/rtc/m48t59-isa.c b/hw/rtc/m48t59-isa.c index 4a7c0af9f0..9e2f6563a0 100644 --- a/hw/rtc/m48t59-isa.c +++ b/hw/rtc/m48t59-isa.c @@ -140,7 +140,7 @@ static const TypeInfo m48txx_isa_type_info = { .instance_size = sizeof(M48txxISAState), .abstract = true, .class_init = m48txx_isa_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NVRAM }, { } } diff --git a/hw/rtc/m48t59.c b/hw/rtc/m48t59.c index 821472a680..68be2dad6f 100644 --- a/hw/rtc/m48t59.c +++ b/hw/rtc/m48t59.c @@ -658,7 +658,7 @@ static const TypeInfo m48txx_sysbus_type_info = { .instance_init = m48t59_init1, .abstract = true, .class_init = m48txx_sysbus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NVRAM }, { } } diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 93b632bdf4..6f787be7af 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -1038,7 +1038,7 @@ static const TypeInfo mc146818rtc_info = { .parent = TYPE_ISA_DEVICE, .instance_size = sizeof(MC146818RtcState), .class_init = rtc_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_ACPI_DEV_AML_IF }, { }, }, diff --git a/hw/s390x/ap-bridge.c b/hw/s390x/ap-bridge.c index 4aa7d5a90d..edeb3dbef3 100644 --- a/hw/s390x/ap-bridge.c +++ b/hw/s390x/ap-bridge.c @@ -75,7 +75,7 @@ static const TypeInfo ap_bridge_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = 0, .class_init = ap_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/s390x/css-bridge.c b/hw/s390x/css-bridge.c index 9d91e5a5fe..0f87b8c5c4 100644 --- a/hw/s390x/css-bridge.c +++ b/hw/s390x/css-bridge.c @@ -136,7 +136,7 @@ static const TypeInfo virtual_css_bridge_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(VirtualCssBridge), .class_init = virtual_css_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index 838b7e6484..e6aa44531f 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -1393,7 +1393,7 @@ static const TypeInfo s390_pcihost_info = { .parent = TYPE_PCI_HOST_BRIDGE, .instance_size = sizeof(S390pciState), .class_init = s390_pcihost_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 00e9e46aef..94edd42dd2 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -875,7 +875,7 @@ static const TypeInfo ccw_machine_info = { .instance_init = s390_machine_initfn, .class_size = sizeof(S390CcwMachineClass), .class_init = ccw_machine_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_NMI }, { TYPE_HOTPLUG_HANDLER}, { TYPE_DUMP_SKEYS_INTERFACE}, diff --git a/hw/s390x/virtio-ccw-md.c b/hw/s390x/virtio-ccw-md.c index de333282df..0370f58450 100644 --- a/hw/s390x/virtio-ccw-md.c +++ b/hw/s390x/virtio-ccw-md.c @@ -140,7 +140,7 @@ static const TypeInfo virtio_ccw_md_info = { .instance_size = sizeof(VirtIOMDCcw), .class_size = sizeof(VirtIOMDCcwClass), .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_MEMORY_DEVICE }, { } }, diff --git a/hw/scsi/esp-pci.c b/hw/scsi/esp-pci.c index 74e9af0b5d..12c86eb7aa 100644 --- a/hw/scsi/esp-pci.c +++ b/hw/scsi/esp-pci.c @@ -450,7 +450,7 @@ static const TypeInfo esp_pci_info = { .instance_init = esp_pci_init, .instance_size = sizeof(PCIESPState), .class_init = esp_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index 0ad61565bf..f4f2ef321e 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -2396,7 +2396,7 @@ static const TypeInfo lsi_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(LSIState), .class_init = lsi_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index a39e3e0e4f..55cd188bd5 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -2503,7 +2503,7 @@ static struct MegasasInfo megasas_devices[] = { .vmsd = &vmstate_megasas_gen1, .props = megasas_properties_gen1, .props_count = ARRAY_SIZE(megasas_properties_gen1), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -2520,7 +2520,7 @@ static struct MegasasInfo megasas_devices[] = { .vmsd = &vmstate_megasas_gen2, .props = megasas_properties_gen2, .props_count = ARRAY_SIZE(megasas_properties_gen2), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { } }, diff --git a/hw/scsi/mptsas.c b/hw/scsi/mptsas.c index 17f73ce381..1ebe0b82a7 100644 --- a/hw/scsi/mptsas.c +++ b/hw/scsi/mptsas.c @@ -1441,7 +1441,7 @@ static const TypeInfo mptsas_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(MPTSASState), .class_init = mptsas1068_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 0456b4de0a..70be4a7367 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -2046,7 +2046,7 @@ static const TypeInfo scsi_bus_info = { .parent = TYPE_BUS, .instance_size = sizeof(SCSIBus), .class_init = scsi_bus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index be5a416c1d..10fde8eee0 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -397,7 +397,7 @@ static const TypeInfo vhost_scsi_info = { .instance_size = sizeof(VHostSCSI), .class_init = vhost_scsi_class_init, .instance_init = vhost_scsi_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { } }, diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 807a58ecf4..8298e8cc6d 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -422,7 +422,7 @@ static const TypeInfo vhost_user_scsi_info = { .instance_size = sizeof(VHostUserSCSI), .class_init = vhost_user_scsi_class_init, .instance_init = vhost_user_scsi_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { } }, diff --git a/hw/scsi/virtio-scsi.c b/hw/scsi/virtio-scsi.c index ae67a7682a..34ae14f7bf 100644 --- a/hw/scsi/virtio-scsi.c +++ b/hw/scsi/virtio-scsi.c @@ -1438,7 +1438,7 @@ static const TypeInfo virtio_scsi_info = { .parent = TYPE_VIRTIO_SCSI_COMMON, .instance_size = sizeof(VirtIOSCSI), .class_init = virtio_scsi_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index a8ea57ffad..d5825b6786 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -1346,7 +1346,7 @@ static const TypeInfo pvscsi_info = { .class_size = sizeof(PVSCSIClass), .instance_size = sizeof(PVSCSIState), .class_init = pvscsi_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, diff --git a/hw/sd/sdhci-pci.c b/hw/sd/sdhci-pci.c index 2a56fbf2cd..c18b91fe63 100644 --- a/hw/sd/sdhci-pci.c +++ b/hw/sd/sdhci-pci.c @@ -75,7 +75,7 @@ static const TypeInfo sdhci_pci_types[] = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(SDHCIState), .class_init = sdhci_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/sparc64/sun4u.c b/hw/sparc64/sun4u.c index 0e5fb4e1b5..e9f9b0a4cb 100644 --- a/hw/sparc64/sun4u.c +++ b/hw/sparc64/sun4u.c @@ -391,7 +391,7 @@ static const TypeInfo ebus_info = { .parent = TYPE_PCI_DEVICE, .class_init = ebus_class_init, .instance_size = sizeof(EbusState), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, @@ -811,7 +811,7 @@ static const TypeInfo sun4u_type = { .name = MACHINE_TYPE_NAME("sun4u"), .parent = TYPE_MACHINE, .class_init = sun4u_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_FW_PATH_PROVIDER }, { } }, diff --git a/hw/ssi/pnv_spi.c b/hw/ssi/pnv_spi.c index 0bb6b0d935..f40e8836b9 100644 --- a/hw/ssi/pnv_spi.c +++ b/hw/ssi/pnv_spi.c @@ -1217,7 +1217,7 @@ static const TypeInfo pnv_spi_info = { .parent = TYPE_SYS_BUS_DEVICE, .instance_size = sizeof(PnvSpi), .class_init = pnv_spi_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_PNV_XSCOM_INTERFACE }, { } } diff --git a/hw/tpm/tpm_crb.c b/hw/tpm/tpm_crb.c index 9aff4d1a6f..bc7a78f898 100644 --- a/hw/tpm/tpm_crb.c +++ b/hw/tpm/tpm_crb.c @@ -337,7 +337,7 @@ static const TypeInfo tpm_crb_info = { .parent = TYPE_DEVICE, .instance_size = sizeof(CRBState), .class_init = tpm_crb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { } } diff --git a/hw/tpm/tpm_spapr.c b/hw/tpm/tpm_spapr.c index 1cad9a88ea..ea608ba4c8 100644 --- a/hw/tpm/tpm_spapr.c +++ b/hw/tpm/tpm_spapr.c @@ -414,7 +414,7 @@ static const TypeInfo tpm_spapr_info = { .parent = TYPE_VIO_SPAPR_DEVICE, .instance_size = sizeof(SpaprTpmState), .class_init = tpm_spapr_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { } } diff --git a/hw/tpm/tpm_tis_i2c.c b/hw/tpm/tpm_tis_i2c.c index b97d768a5b..5ce84dc7a4 100644 --- a/hw/tpm/tpm_tis_i2c.c +++ b/hw/tpm/tpm_tis_i2c.c @@ -552,7 +552,7 @@ static const TypeInfo tpm_tis_i2c_info = { .parent = TYPE_I2C_SLAVE, .instance_size = sizeof(TPMStateI2C), .class_init = tpm_tis_i2c_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { } } diff --git a/hw/tpm/tpm_tis_isa.c b/hw/tpm/tpm_tis_isa.c index c0098a26e8..dce83057a9 100644 --- a/hw/tpm/tpm_tis_isa.c +++ b/hw/tpm/tpm_tis_isa.c @@ -189,7 +189,7 @@ static const TypeInfo tpm_tis_isa_info = { .instance_size = sizeof(TPMStateISA), .instance_init = tpm_tis_isa_initfn, .class_init = tpm_tis_isa_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { TYPE_ACPI_DEV_AML_IF }, { } diff --git a/hw/tpm/tpm_tis_sysbus.c b/hw/tpm/tpm_tis_sysbus.c index 19abf0b948..2ffa85852a 100644 --- a/hw/tpm/tpm_tis_sysbus.c +++ b/hw/tpm/tpm_tis_sysbus.c @@ -145,7 +145,7 @@ static const TypeInfo tpm_tis_sysbus_info = { .instance_size = sizeof(TPMStateSysBus), .instance_init = tpm_tis_sysbus_initfn, .class_init = tpm_tis_sysbus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { } } diff --git a/hw/ufs/ufs.c b/hw/ufs/ufs.c index 749519760f..0577747f46 100644 --- a/hw/ufs/ufs.c +++ b/hw/ufs/ufs.c @@ -1892,7 +1892,7 @@ static const TypeInfo ufs_info = { .parent = TYPE_PCI_DEVICE, .class_init = ufs_class_init, .instance_size = sizeof(UfsHc), - .interfaces = (InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} }, + .interfaces = (const InterfaceInfo[]){ { INTERFACE_PCIE_DEVICE }, {} }, }; static const TypeInfo ufs_bus_info = { diff --git a/hw/usb/bus.c b/hw/usb/bus.c index d8446cf50e..8dd2ce415e 100644 --- a/hw/usb/bus.c +++ b/hw/usb/bus.c @@ -42,7 +42,7 @@ static const TypeInfo usb_bus_info = { .parent = TYPE_BUS, .instance_size = sizeof(USBBus), .class_init = usb_bus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/usb/dev-smartcard-reader.c b/hw/usb/dev-smartcard-reader.c index 87018a3f01..6ce7154fee 100644 --- a/hw/usb/dev-smartcard-reader.c +++ b/hw/usb/dev-smartcard-reader.c @@ -1458,7 +1458,7 @@ static const TypeInfo ccid_info = { .parent = TYPE_USB_DEVICE, .instance_size = sizeof(USBCCIDState), .class_init = ccid_class_initfn, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/usb/hcd-ehci-pci.c b/hw/usb/hcd-ehci-pci.c index 73122aa797..38ad3406b3 100644 --- a/hw/usb/hcd-ehci-pci.c +++ b/hw/usb/hcd-ehci-pci.c @@ -172,7 +172,7 @@ static const TypeInfo ehci_pci_type_info = { .instance_finalize = usb_ehci_pci_finalize, .abstract = true, .class_init = ehci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/usb/hcd-ohci-pci.c b/hw/usb/hcd-ohci-pci.c index 3c82525a1e..94d1077eb9 100644 --- a/hw/usb/hcd-ohci-pci.c +++ b/hw/usb/hcd-ohci-pci.c @@ -149,7 +149,7 @@ static const TypeInfo ohci_pci_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(OHCIPCIState), .class_init = ohci_pci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/usb/hcd-uhci.c b/hw/usb/hcd-uhci.c index 2b1aee1f21..4822c704f6 100644 --- a/hw/usb/hcd-uhci.c +++ b/hw/usb/hcd-uhci.c @@ -1278,7 +1278,7 @@ static const TypeInfo uhci_pci_type_info = { .class_size = sizeof(UHCIPCIDeviceClass), .abstract = true, .class_init = uhci_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/usb/hcd-xhci-pci.c b/hw/usb/hcd-xhci-pci.c index 6167bb9100..b93c80b09d 100644 --- a/hw/usb/hcd-xhci-pci.c +++ b/hw/usb/hcd-xhci-pci.c @@ -248,7 +248,7 @@ static const TypeInfo xhci_pci_info = { .class_init = xhci_class_init, .instance_init = xhci_instance_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index fd55b8d884..0a13b185fe 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -318,7 +318,7 @@ static const TypeInfo vfio_pci_igd_lpc_bridge_info = { .name = "vfio-pci-igd-lpc-bridge", .parent = TYPE_PCI_DEVICE, .class_init = vfio_pci_igd_lpc_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 0db9f03846..c3d9360710 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3554,7 +3554,7 @@ static const TypeInfo vfio_pci_dev_info = { .class_init = vfio_pci_dev_class_init, .instance_init = vfio_instance_init, .instance_finalize = vfio_instance_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } diff --git a/hw/virtio/virtio-md-pci.c b/hw/virtio/virtio-md-pci.c index 9ec5067662..9278b32cf8 100644 --- a/hw/virtio/virtio-md-pci.c +++ b/hw/virtio/virtio-md-pci.c @@ -138,7 +138,7 @@ static const TypeInfo virtio_md_pci_info = { .instance_size = sizeof(VirtIOMDPCI), .class_size = sizeof(VirtIOMDPCIClass), .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_MEMORY_DEVICE }, { } }, diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 0e14dd37d6..a3d1a676e7 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -1907,7 +1907,7 @@ static const TypeInfo virtio_mem_info = { .instance_finalize = virtio_mem_instance_finalize, .class_init = virtio_mem_class_init, .class_size = sizeof(VirtIOMEMClass), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_RAM_DISCARD_MANAGER }, { } }, diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 95bf7ddd97..0fa8fe4955 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2481,7 +2481,7 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) .name = t->generic_name, .parent = base_type_info.name, .class_init = virtio_pci_generic_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } @@ -2516,7 +2516,7 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) .name = t->non_transitional_name, .parent = base_type_info.name, .instance_init = virtio_pci_non_transitional_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_PCIE_DEVICE }, { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { } @@ -2530,7 +2530,7 @@ void virtio_pci_types_register(const VirtioPCIDeviceTypeInfo *t) .name = t->transitional_name, .parent = base_type_info.name, .instance_init = virtio_pci_transitional_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { /* * Transitional virtio devices work only as Conventional PCI * devices because they require PIO ports. diff --git a/hw/watchdog/wdt_i6300esb.c b/hw/watchdog/wdt_i6300esb.c index 1536e1fe03..bb8a2766b6 100644 --- a/hw/watchdog/wdt_i6300esb.c +++ b/hw/watchdog/wdt_i6300esb.c @@ -480,7 +480,7 @@ static const TypeInfo i6300esb_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(I6300State), .class_init = i6300esb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/hw/xen/xen-bus.c b/hw/xen/xen-bus.c index e6272282cd..6bd2e546f6 100644 --- a/hw/xen/xen-bus.c +++ b/hw/xen/xen-bus.c @@ -399,7 +399,7 @@ static const TypeInfo xen_bus_type_info = { .instance_size = sizeof(XenBus), .class_size = sizeof(XenBusClass), .class_init = xen_bus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } }, diff --git a/hw/xen/xen-legacy-backend.c b/hw/xen/xen-legacy-backend.c index d6fe7d4f3e..5ed53f8943 100644 --- a/hw/xen/xen-legacy-backend.c +++ b/hw/xen/xen-legacy-backend.c @@ -661,7 +661,7 @@ static const TypeInfo xensysbus_info = { .name = TYPE_XENSYSBUS, .parent = TYPE_BUS, .class_init = xen_sysbus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { } } diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 7f9c351d96..9d16644d82 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -1079,7 +1079,7 @@ static const TypeInfo xen_pci_passthrough_info = { .class_init = xen_pci_passthrough_class_init, .class_size = sizeof(XenPTDeviceClass), .instance_init = xen_pci_passthrough_instance_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { INTERFACE_PCIE_DEVICE }, { }, diff --git a/hw/xen/xen_pt_graphics.c b/hw/xen/xen_pt_graphics.c index 264851ece3..2c0cec9723 100644 --- a/hw/xen/xen_pt_graphics.c +++ b/hw/xen/xen_pt_graphics.c @@ -363,7 +363,7 @@ static const TypeInfo isa_bridge_info = { .parent = TYPE_PCI_DEVICE, .instance_size = sizeof(PCIDevice), .class_init = isa_bridge_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { INTERFACE_CONVENTIONAL_PCI_DEVICE }, { }, }, diff --git a/include/qom/object.h b/include/qom/object.h index 26a45f638c..1d5b033724 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -294,7 +294,7 @@ struct Object .class_size = CLASS_SIZE, \ .class_init = module_obj_name##_class_init, \ .abstract = ABSTRACT, \ - .interfaces = (InterfaceInfo[]) { __VA_ARGS__ } , \ + .interfaces = (const InterfaceInfo[]) { __VA_ARGS__ } , \ }; \ \ static void \ diff --git a/net/can/can_core.c b/net/can/can_core.c index e16e15c036..77fe2b8ba4 100644 --- a/net/can/can_core.c +++ b/net/can/can_core.c @@ -162,7 +162,7 @@ static const TypeInfo can_bus_info = { .instance_size = sizeof(CanBusState), .instance_init = can_bus_instance_init, .class_init = can_bus_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/net/can/can_host.c b/net/can/can_host.c index ca5fcf1ea1..3f9bb33e47 100644 --- a/net/can/can_host.c +++ b/net/can/can_host.c @@ -92,7 +92,7 @@ static const TypeInfo can_host_info = { .class_size = sizeof(CanHostClass), .abstract = true, .class_init = can_host_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/net/colo-compare.c b/net/colo-compare.c index 425eb6e971..0e1844ee4c 100644 --- a/net/colo-compare.c +++ b/net/colo-compare.c @@ -1476,7 +1476,7 @@ static const TypeInfo colo_compare_info = { .instance_finalize = colo_compare_finalize, .class_size = sizeof(CompareClass), .class_init = colo_compare_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/net/filter.c b/net/filter.c index 03b3c793f7..c7cc6615dc 100644 --- a/net/filter.c +++ b/net/filter.c @@ -363,7 +363,7 @@ static const TypeInfo netfilter_info = { .instance_size = sizeof(NetFilterState), .instance_init = netfilter_init, .instance_finalize = netfilter_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/scripts/codeconverter/codeconverter/test_regexps.py b/scripts/codeconverter/codeconverter/test_regexps.py index 08857c5008..4526268ae8 100644 --- a/scripts/codeconverter/codeconverter/test_regexps.py +++ b/scripts/codeconverter/codeconverter/test_regexps.py @@ -77,8 +77,8 @@ def fullmatch(regexp, s): assert fullmatch(RE_ARRAY_ITEM, '{ TYPE_HOTPLUG_HANDLER },') assert fullmatch(RE_ARRAY_ITEM, '{ TYPE_ACPI_DEVICE_IF },') assert fullmatch(RE_ARRAY_ITEM, '{ }') - assert fullmatch(RE_ARRAY_CAST, '(InterfaceInfo[])') - assert fullmatch(RE_ARRAY, '''(InterfaceInfo[]) { + assert fullmatch(RE_ARRAY_CAST, '(const InterfaceInfo[])') + assert fullmatch(RE_ARRAY, '''(const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, { } @@ -98,7 +98,7 @@ def fullmatch(regexp, s): .parent = TYPE_DEVICE, .instance_size = sizeof(CRBState), .class_init = tpm_crb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { } } @@ -134,7 +134,7 @@ def fullmatch(regexp, s): .instance_size = sizeof(AcpiGedState), .instance_init = acpi_ged_initfn, .class_init = acpi_ged_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, { } @@ -164,7 +164,7 @@ def fullmatch(regexp, s): .parent = TYPE_DEVICE, .instance_size = sizeof(CRBState), .class_init = tpm_crb_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TPM_IF }, { } } diff --git a/scsi/pr-manager.c b/scsi/pr-manager.c index 1977d99ce0..40e1210eb2 100644 --- a/scsi/pr-manager.c +++ b/scsi/pr-manager.c @@ -77,7 +77,7 @@ static const TypeInfo pr_manager_info = { .name = TYPE_PR_MANAGER, .class_size = sizeof(PRManagerClass), .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/system/qtest.c b/system/qtest.c index b7689088a4..ade3eb3221 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -1012,7 +1012,7 @@ static const TypeInfo qtest_info = { .parent = TYPE_OBJECT, .class_init = qtest_class_init, .instance_size = sizeof(QTest), - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/target/i386/sev.c b/target/i386/sev.c index 7ef4801d5f..7ee700d6a3 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -2089,7 +2089,7 @@ static const TypeInfo sev_common_info = { .class_size = sizeof(SevCommonStateClass), .class_init = sev_common_class_init, .abstract = true, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 90b21b9c93..aed9e26599 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7570,7 +7570,7 @@ static const TypeInfo ppc_cpu_type_info = { .class_size = sizeof(PowerPCCPUClass), .class_init = ppc_cpu_class_init, #ifndef CONFIG_USER_ONLY - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_INTERRUPT_STATS_PROVIDER }, { } }, diff --git a/tests/unit/check-qom-interface.c b/tests/unit/check-qom-interface.c index 4e1c4d6729..86ae5f6c3b 100644 --- a/tests/unit/check-qom-interface.c +++ b/tests/unit/check-qom-interface.c @@ -52,7 +52,7 @@ static const TypeInfo direct_impl_info = { .name = TYPE_DIRECT_IMPL, .parent = TYPE_OBJECT, .class_init = test_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_TEST_IF }, { } } diff --git a/tests/unit/check-qom-proplist.c b/tests/unit/check-qom-proplist.c index f1de1618f6..ee3c6fb32b 100644 --- a/tests/unit/check-qom-proplist.c +++ b/tests/unit/check-qom-proplist.c @@ -164,7 +164,7 @@ static const TypeInfo dummy_info = { .instance_finalize = dummy_finalize, .class_size = sizeof(DummyObjectClass), .class_init = dummy_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/ui/dbus.c b/ui/dbus.c index 0c0f86eaa6..dd0336702d 100644 --- a/ui/dbus.c +++ b/ui/dbus.c @@ -514,7 +514,7 @@ static const TypeInfo dbus_display_info = { .instance_init = dbus_display_init, .instance_finalize = dbus_display_finalize, .class_init = dbus_display_class_init, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/ui/input-barrier.c b/ui/input-barrier.c index 9dce31ea9a..9793258aac 100644 --- a/ui/input-barrier.c +++ b/ui/input-barrier.c @@ -732,7 +732,7 @@ static const TypeInfo input_barrier_info = { .instance_size = sizeof(InputBarrier), .instance_init = input_barrier_instance_init, .instance_finalize = input_barrier_instance_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/ui/input-linux.c b/ui/input-linux.c index 2f5adb82d2..92e1a1aa64 100644 --- a/ui/input-linux.c +++ b/ui/input-linux.c @@ -522,7 +522,7 @@ static const TypeInfo input_linux_info = { .instance_size = sizeof(InputLinux), .instance_init = input_linux_instance_init, .instance_finalize = input_linux_instance_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } diff --git a/util/thread-context.c b/util/thread-context.c index 95228a3d4f..0146154fa5 100644 --- a/util/thread-context.c +++ b/util/thread-context.c @@ -319,7 +319,7 @@ static const TypeInfo thread_context_info = { .instance_size = sizeof(ThreadContext), .instance_init = thread_context_instance_init, .instance_finalize = thread_context_instance_finalize, - .interfaces = (InterfaceInfo[]) { + .interfaces = (const InterfaceInfo[]) { { TYPE_USER_CREATABLE }, { } } From 7748cdbae899f54f672e2e5ecd2c556c256b2b08 Mon Sep 17 00:00:00 2001 From: Kohei Tokunaga Date: Tue, 22 Apr 2025 14:27:06 +0900 Subject: [PATCH 0312/2760] qom/object: Fix type conflict of GLib function pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Emscripten, function pointer casts can result in runtime failures due to strict function signature checks. This affects the use of g_list_sort and g_slist_sort, which internally perform function pointer casts that are not supported by Emscripten. To avoid these issues, g_list_sort_with_data and g_slist_sort_with_data should be used instead, as they do not rely on function pointer casting. Signed-off-by: Kohei Tokunaga Reviewed-by: Philippe Mathieu-Daudé Message-ID: <8ca13f4e2b9eba9d1f6030b0afb442a24330e463.1745295397.git.ktokunaga.mail@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- qom/object.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/qom/object.c b/qom/object.c index 425ee2f0ee..664f0f24ae 100644 --- a/qom/object.c +++ b/qom/object.c @@ -1191,7 +1191,7 @@ GSList *object_class_get_list(const char *implements_type, return list; } -static gint object_class_cmp(gconstpointer a, gconstpointer b) +static gint object_class_cmp(gconstpointer a, gconstpointer b, gpointer d) { return strcasecmp(object_class_get_name((ObjectClass *)a), object_class_get_name((ObjectClass *)b)); @@ -1200,8 +1200,9 @@ static gint object_class_cmp(gconstpointer a, gconstpointer b) GSList *object_class_get_list_sorted(const char *implements_type, bool include_abstract) { - return g_slist_sort(object_class_get_list(implements_type, include_abstract), - object_class_cmp); + return g_slist_sort_with_data( + object_class_get_list(implements_type, include_abstract), + object_class_cmp, NULL); } Object *object_ref(void *objptr) From d5f241834be1b323ea697a469ff0f1335a1823fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 22 Apr 2025 10:32:31 +0200 Subject: [PATCH 0313/2760] hw/core: Get default_cpu_type calling machine_class_default_cpu_type() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 62b4a227a33 the default cpu type can come from the valid_cpu_types[] array. Call the machine_class_default_cpu_type() instead of accessing MachineClass::default_cpu_type field. Cc: qemu-stable@nongnu.org Fixes: 62b4a227a33 ("hw/core: Add machine_class_default_cpu_type()") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Reviewed-by: Zhao Liu Message-Id: <20250422084114.39499-1-philmd@linaro.org> --- hw/core/machine-qmp-cmds.c | 5 +++-- target/ppc/cpu_init.c | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index fd8b4e0b44..9447e345b3 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -73,6 +73,7 @@ MachineInfoList *qmp_query_machines(bool has_compat_props, bool compat_props, for (el = machines; el; el = el->next) { MachineClass *mc = el->data; + const char *default_cpu_type = machine_class_default_cpu_type(mc); MachineInfo *info; info = g_malloc0(sizeof(*info)); @@ -91,8 +92,8 @@ MachineInfoList *qmp_query_machines(bool has_compat_props, bool compat_props, info->numa_mem_supported = mc->numa_mem_supported; info->deprecated = !!mc->deprecation_reason; info->acpi = !!object_class_property_find(OBJECT_CLASS(mc), "acpi"); - if (mc->default_cpu_type) { - info->default_cpu_type = g_strdup(mc->default_cpu_type); + if (default_cpu_type) { + info->default_cpu_type = g_strdup(default_cpu_type); } if (mc->default_ram_id) { info->default_ram_id = g_strdup(mc->default_ram_id); diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index aed9e26599..b0973b6df9 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7082,7 +7082,7 @@ ObjectClass *ppc_cpu_class_by_name(const char *name) if (strcmp(name, "max") == 0) { MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); if (mc) { - return object_class_by_name(mc->default_cpu_type); + return object_class_by_name(machine_class_default_cpu_type(mc)); } } #endif From 56a9f0d4c4a483ce217e5290db69cb1788586787 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Mar 2025 14:28:11 +0000 Subject: [PATCH 0314/2760] hw/core/cpu: gdb_arch_name string should not be freed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation for the CPUClass::gdb_arch_name method claims that the returned string should be freed with g_free(). This is not correct: in commit a650683871ba728 we changed this method to instead return a simple constant string, but forgot to update the documentation. Make the documentation match the new semantics. Fixes: a650683871ba728 ("hw/core/cpu: Return static value with gdb_arch_name()") Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250317142819.900029-2-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/core/cpu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 5b645df59f..6ea246514e 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -134,7 +134,8 @@ struct SysemuCPUOps; * @gdb_stop_before_watchpoint: Indicates whether GDB expects the CPU to stop * before the insn which triggers a watchpoint rather than after it. * @gdb_arch_name: Optional callback that returns the architecture name known - * to GDB. The caller must free the returned string with g_free. + * to GDB. The returned value is expected to be a simple constant string: + * the caller will not g_free() it. * @disas_set_info: Setup architecture specific components of disassembly info * @adjust_watchpoint_address: Perform a target-specific adjustment to an * address before attempting to match it against watchpoints. From a1f728ecc90cf6c6db24df53cc951713fca8b94d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Mar 2025 14:28:12 +0000 Subject: [PATCH 0315/2760] gdbstub: Allow gdb_core_xml_file to be set at runtime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the CPUClass:gdb_core_xml_file setting is a simple 'const char *' which the CPU class must set to a fixed string. Allow the CPU class to instead set a new method gdb_get_core_xml_file() which returns this string. This will allow Arm CPUs to use different XML files for AArch32 vs AArch64 without having to have an extra AArch64-specific class type purely to give somewhere to set cc->gdb_core_xml_file differently. Signed-off-by: Peter Maydell Acked-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250317142819.900029-3-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- gdbstub/gdbstub.c | 23 +++++++++++++++++++---- include/hw/core/cpu.h | 5 +++++ 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 282e13e163..565f6b33a9 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -565,15 +565,30 @@ static void gdb_register_feature(CPUState *cpu, int base_reg, g_array_append_val(cpu->gdb_regs, s); } +static const char *gdb_get_core_xml_file(CPUState *cpu) +{ + CPUClass *cc = cpu->cc; + + /* + * The CPU class can provide the XML filename via a method, + * or as a simple fixed string field. + */ + if (cc->gdb_get_core_xml_file) { + return cc->gdb_get_core_xml_file(cpu); + } + return cc->gdb_core_xml_file; +} + void gdb_init_cpu(CPUState *cpu) { CPUClass *cc = cpu->cc; const GDBFeature *feature; + const char *xmlfile = gdb_get_core_xml_file(cpu); cpu->gdb_regs = g_array_new(false, false, sizeof(GDBRegisterState)); - if (cc->gdb_core_xml_file) { - feature = gdb_find_static_feature(cc->gdb_core_xml_file); + if (xmlfile) { + feature = gdb_find_static_feature(xmlfile); gdb_register_feature(cpu, 0, cc->gdb_read_register, cc->gdb_write_register, feature); @@ -1644,7 +1659,7 @@ void gdb_extend_qsupported_features(char *qflags) static void handle_query_supported(GArray *params, void *user_ctx) { g_string_printf(gdbserver_state.str_buf, "PacketSize=%x", MAX_PACKET_LENGTH); - if (first_cpu->cc->gdb_core_xml_file) { + if (gdb_get_core_xml_file(first_cpu)) { g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+"); } @@ -1701,7 +1716,7 @@ static void handle_query_xfer_features(GArray *params, void *user_ctx) } process = gdb_get_cpu_process(gdbserver_state.g_cpu); - if (!gdbserver_state.g_cpu->cc->gdb_core_xml_file) { + if (!gdb_get_core_xml_file(gdbserver_state.g_cpu)) { gdb_put_packet(""); return; } diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 6ea246514e..2a02d4f078 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -131,6 +131,10 @@ struct SysemuCPUOps; * @gdb_num_core_regs: Number of core registers accessible to GDB or 0 to infer * from @gdb_core_xml_file. * @gdb_core_xml_file: File name for core registers GDB XML description. + * @gdb_get_core_xml_file: Optional callback that returns the file name for + * the core registers GDB XML description. The returned value is expected to + * be a simple constant string: the caller will not g_free() it. If this + * is NULL then @gdb_core_xml_file will be used instead. * @gdb_stop_before_watchpoint: Indicates whether GDB expects the CPU to stop * before the insn which triggers a watchpoint rather than after it. * @gdb_arch_name: Optional callback that returns the architecture name known @@ -166,6 +170,7 @@ struct CPUClass { const char *gdb_core_xml_file; const gchar * (*gdb_arch_name)(CPUState *cpu); + const char * (*gdb_get_core_xml_file)(CPUState *cpu); void (*disas_set_info)(CPUState *cpu, disassemble_info *info); From bae9fb4b016b1927f6e0cdea9213e33e3a70c9ae Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Mar 2025 14:28:13 +0000 Subject: [PATCH 0316/2760] target/arm: Handle AArch64 in TYPE_ARM_CPU gdb_arch_name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of having the TYPE_AARCH64_CPU subclass set CPUClass::gdb_arch_name to a different function, make the TYPE_ARM_CPU implementation of the method handle AArch64. For the moment we make the "is this AArch64?" function test "is the CPU of TYPE_AARCH64_CPU?", so that this produces no behavioural change. When we've moved all the gdbstub related methods across to the base class, we will be able to change this to be "does the CPU have the ARM_FEATURE_AARCH64 feature?". Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250317142819.900029-4-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- target/arm/cpu.c | 3 +++ target/arm/cpu64.c | 6 ------ target/arm/internals.h | 6 ++++++ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 00577f97eb..bed0e58f3c 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2649,6 +2649,9 @@ static const gchar *arm_gdb_arch_name(CPUState *cs) ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; + if (arm_gdbstub_is_aarch64(cpu)) { + return "aarch64"; + } if (arm_feature(env, ARM_FEATURE_IWMMXT)) { return "iwmmxt"; } diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index eaf5705cdc..fbb7e7b3d6 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -813,11 +813,6 @@ static void aarch64_cpu_finalizefn(Object *obj) { } -static const gchar *aarch64_gdb_arch_name(CPUState *cs) -{ - return "aarch64"; -} - static void aarch64_cpu_class_init(ObjectClass *oc, const void *data) { CPUClass *cc = CPU_CLASS(oc); @@ -825,7 +820,6 @@ static void aarch64_cpu_class_init(ObjectClass *oc, const void *data) cc->gdb_read_register = aarch64_cpu_gdb_read_register; cc->gdb_write_register = aarch64_cpu_gdb_write_register; cc->gdb_core_xml_file = "aarch64-core.xml"; - cc->gdb_arch_name = aarch64_gdb_arch_name; object_class_property_add_bool(oc, "aarch64", aarch64_cpu_get_aarch64, aarch64_cpu_set_aarch64); diff --git a/target/arm/internals.h b/target/arm/internals.h index d24acdd672..08f4bd1679 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1826,6 +1826,12 @@ void aarch64_add_pauth_properties(Object *obj); void aarch64_add_sve_properties(Object *obj); void aarch64_add_sme_properties(Object *obj); +/* Return true if the gdbstub is presenting an AArch64 CPU */ +static inline bool arm_gdbstub_is_aarch64(ARMCPU *cpu) +{ + return object_dynamic_cast(OBJECT(cpu), TYPE_AARCH64_CPU); +} + /* Read the CONTROL register as the MRS instruction would. */ uint32_t arm_v7m_mrs_control(CPUARMState *env, uint32_t secure); From 984ea94818000618c4ff95d0b63f63aba8f2a95e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Mar 2025 14:28:14 +0000 Subject: [PATCH 0317/2760] target/arm: Handle gdb_core_xml_file in TYPE_ARM_CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of having the TYPE_AARCH64_CPU subclass set CPUClass:gdb_core_xml_file to a different value from that that TYPE_ARM_CPU uses, implement the gdb_get_core_xml_file method in the TYPE_ARM_CPU class to return either the AArch64 or AArch32 XML file name. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250317142819.900029-5-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- target/arm/cpu.c | 16 +++++++++++++++- target/arm/cpu64.c | 1 - target/arm/tcg/cpu-v7m.c | 1 - 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index bed0e58f3c..5e951675c6 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2658,6 +2658,20 @@ static const gchar *arm_gdb_arch_name(CPUState *cs) return "arm"; } +static const char *arm_gdb_get_core_xml_file(CPUState *cs) +{ + ARMCPU *cpu = ARM_CPU(cs); + CPUARMState *env = &cpu->env; + + if (arm_gdbstub_is_aarch64(cpu)) { + return "aarch64-core.xml"; + } + if (arm_feature(env, ARM_FEATURE_M)) { + return "arm-m-profile.xml"; + } + return "arm-core.xml"; +} + #ifndef CONFIG_USER_ONLY #include "hw/core/sysemu-cpu-ops.h" @@ -2727,6 +2741,7 @@ static void arm_cpu_class_init(ObjectClass *oc, const void *data) cc->sysemu_ops = &arm_sysemu_ops; #endif cc->gdb_arch_name = arm_gdb_arch_name; + cc->gdb_get_core_xml_file = arm_gdb_get_core_xml_file; cc->gdb_stop_before_watchpoint = true; cc->disas_set_info = arm_disas_set_info; @@ -2749,7 +2764,6 @@ static void cpu_register_class_init(ObjectClass *oc, const void *data) CPUClass *cc = CPU_CLASS(acc); acc->info = data; - cc->gdb_core_xml_file = "arm-core.xml"; if (acc->info->deprecation_note) { cc->deprecation_note = acc->info->deprecation_note; } diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index fbb7e7b3d6..5135ef63cb 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -819,7 +819,6 @@ static void aarch64_cpu_class_init(ObjectClass *oc, const void *data) cc->gdb_read_register = aarch64_cpu_gdb_read_register; cc->gdb_write_register = aarch64_cpu_gdb_write_register; - cc->gdb_core_xml_file = "aarch64-core.xml"; object_class_property_add_bool(oc, "aarch64", aarch64_cpu_get_aarch64, aarch64_cpu_set_aarch64); diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 7426aac0dc..b34b657857 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -266,7 +266,6 @@ static void arm_v7m_class_init(ObjectClass *oc, const void *data) acc->info = data; cc->tcg_ops = &arm_v7m_tcg_ops; - cc->gdb_core_xml_file = "arm-m-profile.xml"; } static const ARMCPUInfo arm_v7m_cpus[] = { From 4b6f74d86c4d4f179643ae2c5f7bbfcf7c1a7725 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 17 Mar 2025 14:28:15 +0000 Subject: [PATCH 0318/2760] target/arm: Handle AArch64 gdb read/write regs in TYPE_ARM_CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of having the TYPE_AARCH64_CPU subclass set CPUClass::gdb_read_register and ::gdb_write_register to different methods from those of the TYPE_ARM_CPU parent class, have the TYPE_ARM_CPU methods handle either AArch32 or AArch64 at runtime. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250317142819.900029-6-peter.maydell@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- target/arm/cpu64.c | 5 ----- target/arm/gdbstub.c | 12 ++++++++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 5135ef63cb..00629a5d1d 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -815,11 +815,6 @@ static void aarch64_cpu_finalizefn(Object *obj) static void aarch64_cpu_class_init(ObjectClass *oc, const void *data) { - CPUClass *cc = CPU_CLASS(oc); - - cc->gdb_read_register = aarch64_cpu_gdb_read_register; - cc->gdb_write_register = aarch64_cpu_gdb_write_register; - object_class_property_add_bool(oc, "aarch64", aarch64_cpu_get_aarch64, aarch64_cpu_set_aarch64); object_class_property_set_description(oc, "aarch64", diff --git a/target/arm/gdbstub.c b/target/arm/gdbstub.c index 30068c2262..ce4497ad7c 100644 --- a/target/arm/gdbstub.c +++ b/target/arm/gdbstub.c @@ -44,6 +44,12 @@ int arm_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; +#ifdef TARGET_AARCH64 + if (arm_gdbstub_is_aarch64(cpu)) { + return aarch64_cpu_gdb_read_register(cs, mem_buf, n); + } +#endif + if (n < 16) { /* Core integer register. */ return gdb_get_reg32(mem_buf, env->regs[n]); @@ -66,6 +72,12 @@ int arm_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) CPUARMState *env = &cpu->env; uint32_t tmp; +#ifdef TARGET_AARCH64 + if (arm_gdbstub_is_aarch64(cpu)) { + return aarch64_cpu_gdb_write_register(cs, mem_buf, n); + } +#endif + tmp = ldl_p(mem_buf); /* From 35ca9d14c173bf7593367d19048ce7784fd122c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Apr 2025 00:19:59 +0200 Subject: [PATCH 0319/2760] target/arm: Replace target_ulong -> hwaddr in ARMMMUFaultInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20250415172246.79470-2-philmd@linaro.org> --- target/arm/internals.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 08f4bd1679..0818de530b 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -25,6 +25,7 @@ #ifndef TARGET_ARM_INTERNALS_H #define TARGET_ARM_INTERNALS_H +#include "exec/hwaddr.h" #include "exec/breakpoint.h" #include "hw/registerfields.h" #include "tcg/tcg-gvec-desc.h" @@ -726,8 +727,8 @@ typedef struct ARMMMUFaultInfo ARMMMUFaultInfo; struct ARMMMUFaultInfo { ARMFaultType type; ARMGPCF gpcf; - target_ulong s2addr; - target_ulong paddr; + hwaddr s2addr; + hwaddr paddr; ARMSecuritySpace paddr_space; int level; int domain; From 6fa103069d13edb0875242e10dbcc47df19ba412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Apr 2025 00:25:06 +0200 Subject: [PATCH 0320/2760] target/arm: Replace target_ulong -> vaddr for CPUWatchpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPUWatchpoint::vaddr/len are of type vaddr. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20250415172246.79470-4-philmd@linaro.org> --- target/arm/hyp_gdbstub.c | 8 ++++---- target/arm/internals.h | 9 +++++---- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/target/arm/hyp_gdbstub.c b/target/arm/hyp_gdbstub.c index 1e861263b3..0512d67f8c 100644 --- a/target/arm/hyp_gdbstub.c +++ b/target/arm/hyp_gdbstub.c @@ -125,7 +125,7 @@ int delete_hw_breakpoint(target_ulong pc) * need to ensure you mask the address as required and set BAS=0xff */ -int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type) +int insert_hw_watchpoint(vaddr addr, vaddr len, int type) { HWWatchpoint wp = { .wcr = R_DBGWCR_E_MASK, /* E=1, enable */ @@ -182,7 +182,7 @@ int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type) return 0; } -bool check_watchpoint_in_range(int i, target_ulong addr) +bool check_watchpoint_in_range(int i, vaddr addr) { HWWatchpoint *wp = get_hw_wp(i); uint64_t addr_top, addr_bottom = wp->wvr; @@ -214,7 +214,7 @@ bool check_watchpoint_in_range(int i, target_ulong addr) * Delete a breakpoint and shuffle any above down */ -int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type) +int delete_hw_watchpoint(vaddr addr, vaddr len, int type) { int i; for (i = 0; i < cur_hw_wps; i++) { @@ -239,7 +239,7 @@ bool find_hw_breakpoint(CPUState *cpu, target_ulong pc) return false; } -CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr) +CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, vaddr addr) { int i; diff --git a/target/arm/internals.h b/target/arm/internals.h index 0818de530b..4d3d84ffeb 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -26,6 +26,7 @@ #define TARGET_ARM_INTERNALS_H #include "exec/hwaddr.h" +#include "exec/vaddr.h" #include "exec/breakpoint.h" #include "hw/registerfields.h" #include "tcg/tcg-gvec-desc.h" @@ -1952,10 +1953,10 @@ bool find_hw_breakpoint(CPUState *cpu, target_ulong pc); int insert_hw_breakpoint(target_ulong pc); int delete_hw_breakpoint(target_ulong pc); -bool check_watchpoint_in_range(int i, target_ulong addr); -CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, target_ulong addr); -int insert_hw_watchpoint(target_ulong addr, target_ulong len, int type); -int delete_hw_watchpoint(target_ulong addr, target_ulong len, int type); +bool check_watchpoint_in_range(int i, vaddr addr); +CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, vaddr addr); +int insert_hw_watchpoint(vaddr addr, vaddr len, int type); +int delete_hw_watchpoint(vaddr addr, vaddr len, int type); /* Return the current value of the system counter in ticks */ uint64_t gt_get_countervalue(CPUARMState *env); From d4a785ba30ce6d8acf0206f049fb4a7494e0898a Mon Sep 17 00:00:00 2001 From: Hauke Mehrtens Date: Sat, 12 Apr 2025 21:40:03 +0200 Subject: [PATCH 0321/2760] target/mips: Fix MIPS16e translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a wrong conversion to gen_op_addr_addi(). The framesize should be added like it was done before. This bug broke booting OpenWrt MIPS32 BE malta Linux system images generated by OpenWrt. Cc: qemu-stable@nongnu.org Fixes: d0b24b7f50e1 ("target/mips: Use gen_op_addr_addi() when possible") Signed-off-by: Hauke Mehrtens Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250412194003.181411-1-hauke@hauke-m.de> Signed-off-by: Philippe Mathieu-Daudé --- target/mips/tcg/mips16e_translate.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/mips/tcg/mips16e_translate.c.inc b/target/mips/tcg/mips16e_translate.c.inc index a9af8f1e74..97da3456ea 100644 --- a/target/mips/tcg/mips16e_translate.c.inc +++ b/target/mips/tcg/mips16e_translate.c.inc @@ -306,7 +306,7 @@ static void gen_mips16_restore(DisasContext *ctx, int astatic; TCGv t0 = tcg_temp_new(); - gen_op_addr_addi(ctx, t0, cpu_gpr[29], -framesize); + gen_op_addr_addi(ctx, t0, cpu_gpr[29], framesize); if (do_ra) { decr_and_load(ctx, 31, t0); @@ -386,7 +386,7 @@ static void gen_mips16_restore(DisasContext *ctx, } } - gen_op_addr_addi(ctx, cpu_gpr[29], cpu_gpr[29], -framesize); + gen_op_addr_addi(ctx, cpu_gpr[29], cpu_gpr[29], framesize); } #if defined(TARGET_MIPS64) From b939b8e42acedc2ff35534f96fae026f8fe2efcf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Apr 2025 09:31:24 +0200 Subject: [PATCH 0322/2760] exec: Rename target_words_bigendian() -> target_big_endian() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 98ed8ecfc9d ("exec: introduce target_words_bigendian() helper") target_words_bigendian() was matching the definition it was depending on (TARGET_WORDS_BIGENDIAN). Later in commit ee3eb3a7ce7 ("Replace TARGET_WORDS_BIGENDIAN") the definition was renamed as TARGET_BIG_ENDIAN but we didn't update the helper. Do it now mechanically using: $ sed -i -e s/target_words_bigendian/target_big_endian/g \ $(git grep -wl target_words_bigendian) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Reviewed-by: Pierrick Bouvier Message-Id: <20250417210025.68322-1-philmd@linaro.org> --- cpu-target.c | 4 ++-- hw/core/cpu-system.c | 2 +- hw/display/vga.c | 2 +- hw/virtio/virtio.c | 2 +- include/exec/tswap.h | 12 ++++++------ system/memory-internal.h | 2 +- system/memory.c | 4 ++-- system/qtest.c | 2 +- 8 files changed, 15 insertions(+), 15 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index c99d208a7c..d68cbab5da 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -160,8 +160,8 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) abort(); } -#undef target_words_bigendian -bool target_words_bigendian(void) +#undef target_big_endian +bool target_big_endian(void) { return TARGET_BIG_ENDIAN; } diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 82b68b8927..3c84176a0c 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -133,7 +133,7 @@ bool cpu_virtio_is_big_endian(CPUState *cpu) if (cpu->cc->sysemu_ops->virtio_is_big_endian) { return cpu->cc->sysemu_ops->virtio_is_big_endian(cpu); } - return target_words_bigendian(); + return target_big_endian(); } GuestPanicInformation *cpu_get_crash_info(CPUState *cpu) diff --git a/hw/display/vga.c b/hw/display/vga.c index b01f67c65f..20475ebbd3 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -2264,7 +2264,7 @@ bool vga_common_init(VGACommonState *s, Object *obj, Error **errp) * into a device attribute set by the machine/platform to remove * all target endian dependencies from this file. */ - s->default_endian_fb = target_words_bigendian(); + s->default_endian_fb = target_big_endian(); s->big_endian_fb = s->default_endian_fb; vga_dirty_log_start(s); diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index f0fa36f8ce..480c2e5036 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2248,7 +2248,7 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) static enum virtio_device_endian virtio_default_endian(void) { - if (target_words_bigendian()) { + if (target_big_endian()) { return VIRTIO_DEVICE_ENDIAN_BIG; } else { return VIRTIO_DEVICE_ENDIAN_LITTLE; diff --git a/include/exec/tswap.h b/include/exec/tswap.h index 84060a4999..49511f2611 100644 --- a/include/exec/tswap.h +++ b/include/exec/tswap.h @@ -11,15 +11,15 @@ #include "qemu/bswap.h" /** - * target_words_bigendian: + * target_big_endian: * Returns true if the (default) endianness of the target is big endian, * false otherwise. Common code should normally never need to know about the * endianness of the target, so please do *not* use this function unless you * know very well what you are doing! */ -bool target_words_bigendian(void); +bool target_big_endian(void); #ifdef COMPILING_PER_TARGET -#define target_words_bigendian() TARGET_BIG_ENDIAN +#define target_big_endian() TARGET_BIG_ENDIAN #endif /* @@ -29,7 +29,7 @@ bool target_words_bigendian(void); #ifdef COMPILING_PER_TARGET #define target_needs_bswap() (HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN) #else -#define target_needs_bswap() (HOST_BIG_ENDIAN != target_words_bigendian()) +#define target_needs_bswap() (HOST_BIG_ENDIAN != target_big_endian()) #endif /* COMPILING_PER_TARGET */ static inline uint16_t tswap16(uint16_t s) @@ -83,7 +83,7 @@ static inline void tswap64s(uint64_t *s) /* Return ld{word}_{le,be}_p following target endianness. */ #define LOAD_IMPL(word, args...) \ do { \ - if (target_words_bigendian()) { \ + if (target_big_endian()) { \ return glue(glue(ld, word), _be_p)(args); \ } else { \ return glue(glue(ld, word), _le_p)(args); \ @@ -120,7 +120,7 @@ static inline uint64_t ldn_p(const void *ptr, int sz) /* Call st{word}_{le,be}_p following target endianness. */ #define STORE_IMPL(word, args...) \ do { \ - if (target_words_bigendian()) { \ + if (target_big_endian()) { \ glue(glue(st, word), _be_p)(args); \ } else { \ glue(glue(st, word), _le_p)(args); \ diff --git a/system/memory-internal.h b/system/memory-internal.h index 085e81a9fe..29717b3c58 100644 --- a/system/memory-internal.h +++ b/system/memory-internal.h @@ -45,7 +45,7 @@ static inline bool devend_big_endian(enum device_endian end) DEVICE_HOST_ENDIAN != DEVICE_BIG_ENDIAN); if (end == DEVICE_NATIVE_ENDIAN) { - return target_words_bigendian(); + return target_big_endian(); } return end == DEVICE_BIG_ENDIAN; } diff --git a/system/memory.c b/system/memory.c index 7e2f16f4e9..67e433095b 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2575,7 +2575,7 @@ void memory_region_add_eventfd(MemoryRegion *mr, unsigned i; if (size) { - MemOp mop = (target_words_bigendian() ? MO_BE : MO_LE) | size_memop(size); + MemOp mop = (target_big_endian() ? MO_BE : MO_LE) | size_memop(size); adjust_endianness(mr, &mrfd.data, mop); } memory_region_transaction_begin(); @@ -2611,7 +2611,7 @@ void memory_region_del_eventfd(MemoryRegion *mr, unsigned i; if (size) { - MemOp mop = (target_words_bigendian() ? MO_BE : MO_LE) | size_memop(size); + MemOp mop = (target_big_endian() ? MO_BE : MO_LE) | size_memop(size); adjust_endianness(mr, &mrfd.data, mop); } memory_region_transaction_begin(); diff --git a/system/qtest.c b/system/qtest.c index ade3eb3221..301b03be2d 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -693,7 +693,7 @@ static void qtest_process_command(CharBackend *chr, gchar **words) qtest_send(chr, "OK\n"); } else if (strcmp(words[0], "endianness") == 0) { - if (target_words_bigendian()) { + if (target_big_endian()) { qtest_sendf(chr, "OK big\n"); } else { qtest_sendf(chr, "OK little\n"); From aca4967567aaa168ce51d54145ba970aafb135de Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 18 Apr 2025 14:51:48 +0900 Subject: [PATCH 0323/2760] hw/usb/hcd-xhci: Unmap canceled packet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the Stop Endpoint Command is received, packets running asynchronously are canceled and then all packets are cleaned up. Packets running asynchronously hold the DMA mapping so cleaning the packets leak the mapping. Remove the mapping after canceling packets to fix the leak. Fixes: 62c6ae04cf43 ("xhci: Initial xHCI implementation") Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250418-xhc-v1-1-bb32dab6a67e@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/usb/hcd-xhci.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hw/usb/hcd-xhci.c b/hw/usb/hcd-xhci.c index b3785b8ba6..292c378bfc 100644 --- a/hw/usb/hcd-xhci.c +++ b/hw/usb/hcd-xhci.c @@ -1187,6 +1187,12 @@ static void xhci_ep_free_xfer(XHCITransfer *xfer) g_free(xfer); } +static void xhci_xfer_unmap(XHCITransfer *xfer) +{ + usb_packet_unmap(&xfer->packet, &xfer->sgl); + qemu_sglist_destroy(&xfer->sgl); +} + static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) { int killed = 0; @@ -1198,6 +1204,7 @@ static int xhci_ep_nuke_one_xfer(XHCITransfer *t, TRBCCode report) if (t->running_async) { usb_cancel_packet(&t->packet); + xhci_xfer_unmap(t); t->running_async = 0; killed = 1; } @@ -1480,12 +1487,6 @@ static int xhci_xfer_create_sgl(XHCITransfer *xfer, int in_xfer) return -1; } -static void xhci_xfer_unmap(XHCITransfer *xfer) -{ - usb_packet_unmap(&xfer->packet, &xfer->sgl); - qemu_sglist_destroy(&xfer->sgl); -} - static void xhci_xfer_report(XHCITransfer *xfer) { uint32_t edtla = 0; From 8cd3b84c8aa8d22c130377ac65e2aac7151a8d27 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 23 Apr 2025 12:11:25 +0200 Subject: [PATCH 0324/2760] hw/intc/i8259: Remove unused DEBUG_PIC define MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The debug printfs were converted to traces so this define is now unused. Fixes: 0880a87300 (i8259: convert DPRINTFs into trace) Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Message-ID: <20250423101125.B243A55C592@zero.eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/intc/i8259.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hw/intc/i8259.c b/hw/intc/i8259.c index 2359dd8253..b6f96bf208 100644 --- a/hw/intc/i8259.c +++ b/hw/intc/i8259.c @@ -32,10 +32,7 @@ #include "trace.h" #include "qom/object.h" -/* debug PIC */ -//#define DEBUG_PIC - -//#define DEBUG_IRQ_LATENCY +/*#define DEBUG_IRQ_LATENCY*/ #define TYPE_I8259 "isa-i8259" typedef struct PICClass PICClass; From 604ac1d87bd650db2c25adcac238577c6aeb5bee Mon Sep 17 00:00:00 2001 From: Kohei Tokunaga Date: Tue, 22 Apr 2025 14:27:05 +0900 Subject: [PATCH 0325/2760] hw/core/loader: Fix type conflict of GLib function pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Emscripten, function pointer casts can result in runtime failures due to strict function signature checks. This affects the use of g_list_sort and g_slist_sort, which internally perform function pointer casts that are not supported by Emscripten. To avoid these issues, g_list_sort_with_data and g_slist_sort_with_data should be used instead, as they do not rely on function pointer casting. Signed-off-by: Kohei Tokunaga Reviewed-by: Philippe Mathieu-Daudé Message-ID: <26dfe9191154ca65dca6ef51ce768ad2a0c30d5f.1745295397.git.ktokunaga.mail@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/core/loader.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index a3aa62d132..b792a54bb0 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -1410,7 +1410,7 @@ typedef struct RomSec { * work, but this way saves a little work later by avoiding * dealing with "gaps" of 0 length. */ -static gint sort_secs(gconstpointer a, gconstpointer b) +static gint sort_secs(gconstpointer a, gconstpointer b, gpointer d) { RomSec *ra = (RomSec *) a; RomSec *rb = (RomSec *) b; @@ -1463,7 +1463,7 @@ RomGap rom_find_largest_gap_between(hwaddr base, size_t size) /* sentinel */ secs = add_romsec_to_list(secs, base + size, 1); - secs = g_list_sort(secs, sort_secs); + secs = g_list_sort_with_data(secs, sort_secs, NULL); for (it = g_list_first(secs); it; it = g_list_next(it)) { cand = (RomSec *) it->data; From ed1aef171671ef49761017cb1d8d10bf67d05c57 Mon Sep 17 00:00:00 2001 From: Sunny Zhu Date: Tue, 22 Apr 2025 02:21:26 +0800 Subject: [PATCH 0326/2760] block: Remove unused callback function *bdrv_aio_pdiscard MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bytes type in *bdrv_aio_pdiscard should be int64_t rather than int. There are no drivers implementing the *bdrv_aio_pdiscard() callback, it appears to be an unused function. Therefore, we'll simply remove it instead of fixing it. Additionally, coroutine-based callbacks are preferred. If someone needs to implement bdrv_aio_pdiscard, a coroutine-based version would be straightforward to implement. Signed-off-by: Sunny Zhu Message-ID: Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/io.c | 22 +++------------------- include/block/block_int-common.h | 4 ---- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/block/io.c b/block/io.c index ccec11386b..6d98b0abb9 100644 --- a/block/io.c +++ b/block/io.c @@ -3102,7 +3102,7 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, return 0; } - if (!bs->drv->bdrv_co_pdiscard && !bs->drv->bdrv_aio_pdiscard) { + if (!bs->drv->bdrv_co_pdiscard) { return 0; } @@ -3162,24 +3162,8 @@ int coroutine_fn bdrv_co_pdiscard(BdrvChild *child, int64_t offset, ret = -ENOMEDIUM; goto out; } - if (bs->drv->bdrv_co_pdiscard) { - ret = bs->drv->bdrv_co_pdiscard(bs, offset, num); - } else { - BlockAIOCB *acb; - CoroutineIOCompletion co = { - .coroutine = qemu_coroutine_self(), - }; - - acb = bs->drv->bdrv_aio_pdiscard(bs, offset, num, - bdrv_co_io_em_complete, &co); - if (acb == NULL) { - ret = -EIO; - goto out; - } else { - qemu_coroutine_yield(); - ret = co.ret; - } - } + + ret = bs->drv->bdrv_co_pdiscard(bs, offset, num); if (ret && ret != -ENOTSUP) { if (ret == -EINVAL && (offset % align != 0 || num % align != 0)) { /* Silently skip rejected unaligned head/tail requests */ diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index ebb4e56a50..0d8187f656 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -506,10 +506,6 @@ struct BlockDriver { BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_flush)( BlockDriverState *bs, BlockCompletionFunc *cb, void *opaque); - BlockAIOCB * GRAPH_RDLOCK_PTR (*bdrv_aio_pdiscard)( - BlockDriverState *bs, int64_t offset, int bytes, - BlockCompletionFunc *cb, void *opaque); - int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_readv)(BlockDriverState *bs, int64_t sector_num, int nb_sectors, QEMUIOVector *qiov); From 141af1b31b10764dcc03a5854ff767b692545e7c Mon Sep 17 00:00:00 2001 From: Kohei Tokunaga Date: Tue, 22 Apr 2025 14:27:11 +0900 Subject: [PATCH 0327/2760] hw/net/can: Fix type conflict of GLib function pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Emscripten, function pointer casts can result in runtime failures due to strict function signature checks. This affects the use of g_list_sort and g_slist_sort, which internally perform function pointer casts that are not supported by Emscripten. To avoid these issues, g_list_sort_with_data and g_slist_sort_with_data should be used instead, as they do not rely on function pointer casting. Signed-off-by: Kohei Tokunaga Reviewed-by: Philippe Mathieu-Daudé Acked-by: Francisco Iglesias Message-ID: <4d47a75c5768c9a6dc5d8b3504e78837577ad70d.1745295397.git.ktokunaga.mail@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/can/xlnx-versal-canfd.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/net/can/xlnx-versal-canfd.c b/hw/net/can/xlnx-versal-canfd.c index 79ccc10098..3eb111949f 100644 --- a/hw/net/can/xlnx-versal-canfd.c +++ b/hw/net/can/xlnx-versal-canfd.c @@ -1278,7 +1278,7 @@ static void tx_fifo_stamp(XlnxVersalCANFDState *s, uint32_t tb0_regid) } } -static gint g_cmp_ids(gconstpointer data1, gconstpointer data2) +static gint g_cmp_ids(gconstpointer data1, gconstpointer data2, gpointer d) { tx_ready_reg_info *tx_reg_1 = (tx_ready_reg_info *) data1; tx_ready_reg_info *tx_reg_2 = (tx_ready_reg_info *) data2; @@ -1316,7 +1316,7 @@ static GSList *prepare_tx_data(XlnxVersalCANFDState *s) temp->can_id = s->regs[reg_num]; temp->reg_num = reg_num; list = g_slist_prepend(list, temp); - list = g_slist_sort(list, g_cmp_ids); + list = g_slist_sort_with_data(list, g_cmp_ids, NULL); } reg_ready >>= 1; From 01499add2ae6529589002860e1880ff193a6578a Mon Sep 17 00:00:00 2001 From: Kohei Tokunaga Date: Tue, 22 Apr 2025 14:27:10 +0900 Subject: [PATCH 0328/2760] contrib/plugins: Fix type conflict of GLib function pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Emscripten, function pointer casts can result in runtime failures due to strict function signature checks. This affects the use of g_list_sort and g_slist_sort, which internally perform function pointer casts that are not supported by Emscripten. To avoid these issues, g_list_sort_with_data and g_slist_sort_with_data should be used instead, as they do not rely on function pointer casting. Signed-off-by: Kohei Tokunaga Reviewed-by: Philippe Mathieu-Daudé Message-ID: <0fcddfca16ca8da2bdaa7b2c114476f5b73d032b.1745295397.git.ktokunaga.mail@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- contrib/plugins/cache.c | 12 ++++++------ contrib/plugins/cflow.c | 10 +++++----- contrib/plugins/hotblocks.c | 4 ++-- contrib/plugins/hotpages.c | 4 ++-- contrib/plugins/howvec.c | 4 ++-- contrib/plugins/hwprofile.c | 8 ++++---- tests/tcg/plugins/mem.c | 4 ++-- tests/tcg/plugins/syscall.c | 4 ++-- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/contrib/plugins/cache.c b/contrib/plugins/cache.c index 7cfd3df249..56508587d3 100644 --- a/contrib/plugins/cache.c +++ b/contrib/plugins/cache.c @@ -576,7 +576,7 @@ static void sum_stats(void) } } -static int dcmp(gconstpointer a, gconstpointer b) +static int dcmp(gconstpointer a, gconstpointer b, gpointer d) { InsnData *insn_a = (InsnData *) a; InsnData *insn_b = (InsnData *) b; @@ -584,7 +584,7 @@ static int dcmp(gconstpointer a, gconstpointer b) return insn_a->l1_dmisses < insn_b->l1_dmisses ? 1 : -1; } -static int icmp(gconstpointer a, gconstpointer b) +static int icmp(gconstpointer a, gconstpointer b, gpointer d) { InsnData *insn_a = (InsnData *) a; InsnData *insn_b = (InsnData *) b; @@ -592,7 +592,7 @@ static int icmp(gconstpointer a, gconstpointer b) return insn_a->l1_imisses < insn_b->l1_imisses ? 1 : -1; } -static int l2_cmp(gconstpointer a, gconstpointer b) +static int l2_cmp(gconstpointer a, gconstpointer b, gpointer d) { InsnData *insn_a = (InsnData *) a; InsnData *insn_b = (InsnData *) b; @@ -645,7 +645,7 @@ static void log_top_insns(void) InsnData *insn; miss_insns = g_hash_table_get_values(miss_ht); - miss_insns = g_list_sort(miss_insns, dcmp); + miss_insns = g_list_sort_with_data(miss_insns, dcmp, NULL); g_autoptr(GString) rep = g_string_new(""); g_string_append_printf(rep, "%s", "address, data misses, instruction\n"); @@ -659,7 +659,7 @@ static void log_top_insns(void) insn->l1_dmisses, insn->disas_str); } - miss_insns = g_list_sort(miss_insns, icmp); + miss_insns = g_list_sort_with_data(miss_insns, icmp, NULL); g_string_append_printf(rep, "%s", "\naddress, fetch misses, instruction\n"); for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) { @@ -676,7 +676,7 @@ static void log_top_insns(void) goto finish; } - miss_insns = g_list_sort(miss_insns, l2_cmp); + miss_insns = g_list_sort_with_data(miss_insns, l2_cmp, NULL); g_string_append_printf(rep, "%s", "\naddress, L2 misses, instruction\n"); for (curr = miss_insns, i = 0; curr && i < limit; i++, curr = curr->next) { diff --git a/contrib/plugins/cflow.c b/contrib/plugins/cflow.c index 930ecb46fc..b5e33f25f9 100644 --- a/contrib/plugins/cflow.c +++ b/contrib/plugins/cflow.c @@ -98,7 +98,7 @@ static GHashTable *nodes; struct qemu_plugin_scoreboard *state; /* SORT_HOTTEST */ -static gint hottest(gconstpointer a, gconstpointer b) +static gint hottest(gconstpointer a, gconstpointer b, gpointer d) { NodeData *na = (NodeData *) a; NodeData *nb = (NodeData *) b; @@ -107,7 +107,7 @@ static gint hottest(gconstpointer a, gconstpointer b) na->dest_count == nb->dest_count ? 0 : 1; } -static gint exception(gconstpointer a, gconstpointer b) +static gint exception(gconstpointer a, gconstpointer b, gpointer d) { NodeData *na = (NodeData *) a; NodeData *nb = (NodeData *) b; @@ -116,7 +116,7 @@ static gint exception(gconstpointer a, gconstpointer b) na->early_exit == nb->early_exit ? 0 : 1; } -static gint popular(gconstpointer a, gconstpointer b) +static gint popular(gconstpointer a, gconstpointer b, gpointer d) { NodeData *na = (NodeData *) a; NodeData *nb = (NodeData *) b; @@ -138,7 +138,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) { g_autoptr(GString) result = g_string_new("collected "); GList *data; - GCompareFunc sort = &hottest; + GCompareDataFunc sort = &hottest; int i = 0; g_mutex_lock(&node_lock); @@ -162,7 +162,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) break; } - data = g_list_sort(data, sort); + data = g_list_sort_with_data(data, sort, NULL); for (GList *l = data; l != NULL && i < topn; diff --git a/contrib/plugins/hotblocks.c b/contrib/plugins/hotblocks.c index f12bfb7a26..98404b6885 100644 --- a/contrib/plugins/hotblocks.c +++ b/contrib/plugins/hotblocks.c @@ -39,7 +39,7 @@ typedef struct { unsigned long insns; } ExecCount; -static gint cmp_exec_count(gconstpointer a, gconstpointer b) +static gint cmp_exec_count(gconstpointer a, gconstpointer b, gpointer d) { ExecCount *ea = (ExecCount *) a; ExecCount *eb = (ExecCount *) b; @@ -79,7 +79,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) g_string_append_printf(report, "%d entries in the hash table\n", g_hash_table_size(hotblocks)); counts = g_hash_table_get_values(hotblocks); - it = g_list_sort(counts, cmp_exec_count); + it = g_list_sort_with_data(counts, cmp_exec_count, NULL); if (it) { g_string_append_printf(report, "pc, tcount, icount, ecount\n"); diff --git a/contrib/plugins/hotpages.c b/contrib/plugins/hotpages.c index c6e6493719..9d48ac969e 100644 --- a/contrib/plugins/hotpages.c +++ b/contrib/plugins/hotpages.c @@ -48,7 +48,7 @@ typedef struct { static GMutex lock; static GHashTable *pages; -static gint cmp_access_count(gconstpointer a, gconstpointer b) +static gint cmp_access_count(gconstpointer a, gconstpointer b, gpointer d) { PageCounters *ea = (PageCounters *) a; PageCounters *eb = (PageCounters *) b; @@ -83,7 +83,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) if (counts && g_list_next(counts)) { GList *it; - it = g_list_sort(counts, cmp_access_count); + it = g_list_sort_with_data(counts, cmp_access_count, NULL); for (i = 0; i < limit && it->next; i++, it = it->next) { PageCounters *rec = (PageCounters *) it->data; diff --git a/contrib/plugins/howvec.c b/contrib/plugins/howvec.c index 2aa9029c3f..42bddb6566 100644 --- a/contrib/plugins/howvec.c +++ b/contrib/plugins/howvec.c @@ -155,7 +155,7 @@ static ClassSelector class_tables[] = { static InsnClassExecCount *class_table; static int class_table_sz; -static gint cmp_exec_count(gconstpointer a, gconstpointer b) +static gint cmp_exec_count(gconstpointer a, gconstpointer b, gpointer d) { InsnExecCount *ea = (InsnExecCount *) a; InsnExecCount *eb = (InsnExecCount *) b; @@ -208,7 +208,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) counts = g_hash_table_get_values(insns); if (counts && g_list_next(counts)) { g_string_append_printf(report, "Individual Instructions:\n"); - counts = g_list_sort(counts, cmp_exec_count); + counts = g_list_sort_with_data(counts, cmp_exec_count, NULL); for (i = 0; i < limit && g_list_next(counts); i++, counts = g_list_next(counts)) { diff --git a/contrib/plugins/hwprofile.c b/contrib/plugins/hwprofile.c index 2a4cbc47d4..a9838ccc87 100644 --- a/contrib/plugins/hwprofile.c +++ b/contrib/plugins/hwprofile.c @@ -71,7 +71,7 @@ static void plugin_init(void) devices = g_hash_table_new(NULL, NULL); } -static gint sort_cmp(gconstpointer a, gconstpointer b) +static gint sort_cmp(gconstpointer a, gconstpointer b, gpointer d) { DeviceCounts *ea = (DeviceCounts *) a; DeviceCounts *eb = (DeviceCounts *) b; @@ -79,7 +79,7 @@ static gint sort_cmp(gconstpointer a, gconstpointer b) eb->totals.reads + eb->totals.writes ? -1 : 1; } -static gint sort_loc(gconstpointer a, gconstpointer b) +static gint sort_loc(gconstpointer a, gconstpointer b, gpointer d) { IOLocationCounts *ea = (IOLocationCounts *) a; IOLocationCounts *eb = (IOLocationCounts *) b; @@ -126,13 +126,13 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) if (counts && g_list_next(counts)) { GList *it; - it = g_list_sort(counts, sort_cmp); + it = g_list_sort_with_data(counts, sort_cmp, NULL); while (it) { DeviceCounts *rec = (DeviceCounts *) it->data; if (rec->detail) { GList *accesses = g_hash_table_get_values(rec->detail); - GList *io_it = g_list_sort(accesses, sort_loc); + GList *io_it = g_list_sort_with_data(accesses, sort_loc, NULL); const char *prefix = pattern ? "off" : "pc"; g_string_append_printf(report, "%s @ 0x%"PRIx64"\n", rec->name, rec->base); diff --git a/tests/tcg/plugins/mem.c b/tests/tcg/plugins/mem.c index d87d6628e0..ca4e8883dd 100644 --- a/tests/tcg/plugins/mem.c +++ b/tests/tcg/plugins/mem.c @@ -67,7 +67,7 @@ static enum qemu_plugin_mem_rw rw = QEMU_PLUGIN_MEM_RW; static GMutex lock; static GHashTable *regions; -static gint addr_order(gconstpointer a, gconstpointer b) +static gint addr_order(gconstpointer a, gconstpointer b, gpointer d) { RegionInfo *na = (RegionInfo *) a; RegionInfo *nb = (RegionInfo *) b; @@ -94,7 +94,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) if (do_region_summary) { GList *counts = g_hash_table_get_values(regions); - counts = g_list_sort(counts, addr_order); + counts = g_list_sort_with_data(counts, addr_order, NULL); g_string_printf(out, "Region Base, Reads, Writes, Seen all\n"); diff --git a/tests/tcg/plugins/syscall.c b/tests/tcg/plugins/syscall.c index 47aad55fc1..42801f5c86 100644 --- a/tests/tcg/plugins/syscall.c +++ b/tests/tcg/plugins/syscall.c @@ -180,7 +180,7 @@ static void print_entry(gpointer val, gpointer user_data) qemu_plugin_outs(out); } -static gint comp_func(gconstpointer ea, gconstpointer eb) +static gint comp_func(gconstpointer ea, gconstpointer eb, gpointer d) { SyscallStats *ent_a = (SyscallStats *) ea; SyscallStats *ent_b = (SyscallStats *) eb; @@ -197,7 +197,7 @@ static void plugin_exit(qemu_plugin_id_t id, void *p) g_mutex_lock(&lock); GList *entries = g_hash_table_get_values(statistics); - entries = g_list_sort(entries, comp_func); + entries = g_list_sort_with_data(entries, comp_func, NULL); qemu_plugin_outs("syscall no. calls errors\n"); g_list_foreach(entries, print_entry, NULL); From 16704598bf3fa8578880e7146a0196d9411ea098 Mon Sep 17 00:00:00 2001 From: Kohei Tokunaga Date: Tue, 22 Apr 2025 14:27:07 +0900 Subject: [PATCH 0329/2760] system/vl: Fix type conflict of GLib function pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On Emscripten, function pointer casts can result in runtime failures due to strict function signature checks. This affects the use of g_list_sort and g_slist_sort, which internally perform function pointer casts that are not supported by Emscripten. To avoid these issues, g_list_sort_with_data and g_slist_sort_with_data should be used instead, as they do not rely on function pointer casting. Signed-off-by: Kohei Tokunaga Reviewed-by: Philippe Mathieu-Daudé Message-ID: Signed-off-by: Philippe Mathieu-Daudé --- system/vl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/system/vl.c b/system/vl.c index c17945c493..4ab2001df7 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1524,7 +1524,7 @@ static bool debugcon_parse(const char *devname, Error **errp) return true; } -static gint machine_class_cmp(gconstpointer a, gconstpointer b) +static gint machine_class_cmp(gconstpointer a, gconstpointer b, gpointer d) { const MachineClass *mc1 = a, *mc2 = b; int res; @@ -1574,7 +1574,7 @@ static void machine_help_func(const QDict *qdict) } printf("Supported machines are:\n"); - machines = g_slist_sort(machines, machine_class_cmp); + machines = g_slist_sort_with_data(machines, machine_class_cmp, NULL); for (el = machines; el; el = el->next) { MachineClass *mc = el->data; if (mc->alias) { From 8af0e52b28c553c901e7c564c3380cd56d363053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 23 Apr 2025 13:11:31 +0200 Subject: [PATCH 0330/2760] system/memory: Remove DEVICE_HOST_ENDIAN definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the previous commit ("exec/memory.h: make devend_memop "target defines" agnostic") there is a single use of the DEVICE_HOST_ENDIAN definition in ram_device_mem_ops: inline it and remove its definition altogether. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: David Hildenbrand Reviewed-by: Richard Henderson Message-Id: <20250423111625.10424-1-philmd@linaro.org> --- include/exec/cpu-common.h | 6 ------ system/memory-internal.h | 3 --- system/memory.c | 2 +- 3 files changed, 1 insertion(+), 10 deletions(-) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index 9b83fd7ac8..dab1e7e580 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -44,12 +44,6 @@ enum device_endian { DEVICE_LITTLE_ENDIAN, }; -#if HOST_BIG_ENDIAN -#define DEVICE_HOST_ENDIAN DEVICE_BIG_ENDIAN -#else -#define DEVICE_HOST_ENDIAN DEVICE_LITTLE_ENDIAN -#endif - /* address in the RAM (different from a physical address) */ #if defined(CONFIG_XEN_BACKEND) typedef uint64_t ram_addr_t; diff --git a/system/memory-internal.h b/system/memory-internal.h index 29717b3c58..46f758fa7e 100644 --- a/system/memory-internal.h +++ b/system/memory-internal.h @@ -41,9 +41,6 @@ void mtree_print_dispatch(struct AddressSpaceDispatch *d, /* returns true if end is big endian. */ static inline bool devend_big_endian(enum device_endian end) { - QEMU_BUILD_BUG_ON(DEVICE_HOST_ENDIAN != DEVICE_LITTLE_ENDIAN && - DEVICE_HOST_ENDIAN != DEVICE_BIG_ENDIAN); - if (end == DEVICE_NATIVE_ENDIAN) { return target_big_endian(); } diff --git a/system/memory.c b/system/memory.c index 67e433095b..71434e7ad0 100644 --- a/system/memory.c +++ b/system/memory.c @@ -1382,7 +1382,7 @@ static void memory_region_ram_device_write(void *opaque, hwaddr addr, static const MemoryRegionOps ram_device_mem_ops = { .read = memory_region_ram_device_read, .write = memory_region_ram_device_write, - .endianness = DEVICE_HOST_ENDIAN, + .endianness = HOST_BIG_ENDIAN ? DEVICE_BIG_ENDIAN : DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, .max_access_size = 8, From fcb1ad456c58ba2304127b0131ac0b48895b2a3b Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 23 Apr 2025 12:02:20 +0200 Subject: [PATCH 0331/2760] system/datadir: Add new type constant for DTB files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently DTB files are mixed with ROMs under BIOS type. Separate them under a new type constant and turn defines into an enum while at it. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-ID: Signed-off-by: Philippe Mathieu-Daudé --- hw/microblaze/boot.c | 2 +- hw/ppc/ppc440_bamboo.c | 2 +- hw/ppc/sam460ex.c | 2 +- hw/ppc/virtex_ml507.c | 2 +- include/qemu/datadir.h | 11 ++++++++--- system/datadir.c | 3 ++- 6 files changed, 14 insertions(+), 8 deletions(-) diff --git a/hw/microblaze/boot.c b/hw/microblaze/boot.c index 60b4ef0abe..4a9c9df318 100644 --- a/hw/microblaze/boot.c +++ b/hw/microblaze/boot.c @@ -130,7 +130,7 @@ void microblaze_load_kernel(MicroBlazeCPU *cpu, bool is_little_endian, dtb_arg = current_machine->dtb; /* default to pcbios dtb as passed by machine_init */ if (!dtb_arg && dtb_filename) { - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_filename); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, dtb_filename); } boot_info.machine_cpu_reset = machine_cpu_reset; diff --git a/hw/ppc/ppc440_bamboo.c b/hw/ppc/ppc440_bamboo.c index 099fda3909..6fff0d8afb 100644 --- a/hw/ppc/ppc440_bamboo.c +++ b/hw/ppc/ppc440_bamboo.c @@ -64,7 +64,7 @@ static int bamboo_load_device_tree(MachineState *machine, uint32_t tb_freq = 400000000; uint32_t clock_freq = 400000000; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, BINARY_DEVICE_TREE_FILE); if (!filename) { return -1; } diff --git a/hw/ppc/sam460ex.c b/hw/ppc/sam460ex.c index a070de23cf..ee31bd8f34 100644 --- a/hw/ppc/sam460ex.c +++ b/hw/ppc/sam460ex.c @@ -142,7 +142,7 @@ static int sam460ex_load_device_tree(MachineState *machine, uint32_t clock_freq = CPU_FREQ; int offset; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, BINARY_DEVICE_TREE_FILE); if (!filename) { error_report("Couldn't find dtb file `%s'", BINARY_DEVICE_TREE_FILE); exit(1); diff --git a/hw/ppc/virtex_ml507.c b/hw/ppc/virtex_ml507.c index 17115be74d..c9969ae48a 100644 --- a/hw/ppc/virtex_ml507.c +++ b/hw/ppc/virtex_ml507.c @@ -146,7 +146,7 @@ static int xilinx_load_device_tree(MachineState *machine, /* Try the local "ppc.dtb" override. */ fdt = load_device_tree("ppc.dtb", &fdt_size); if (!fdt) { - path = qemu_find_file(QEMU_FILE_TYPE_BIOS, BINARY_DEVICE_TREE_FILE); + path = qemu_find_file(QEMU_FILE_TYPE_DTB, BINARY_DEVICE_TREE_FILE); if (path) { fdt = load_device_tree(path, &fdt_size); g_free(path); diff --git a/include/qemu/datadir.h b/include/qemu/datadir.h index 21f9097f58..cca32af300 100644 --- a/include/qemu/datadir.h +++ b/include/qemu/datadir.h @@ -1,11 +1,16 @@ #ifndef QEMU_DATADIR_H #define QEMU_DATADIR_H -#define QEMU_FILE_TYPE_BIOS 0 -#define QEMU_FILE_TYPE_KEYMAP 1 +typedef enum { + QEMU_FILE_TYPE_BIOS, + QEMU_FILE_TYPE_DTB, + QEMU_FILE_TYPE_KEYMAP, +} QemuFileType; + /** * qemu_find_file: * @type: QEMU_FILE_TYPE_BIOS (for BIOS, VGA BIOS) + * QEMU_FILE_TYPE_DTB (for device tree blobs) * or QEMU_FILE_TYPE_KEYMAP (for keymaps). * @name: Relative or absolute file name * @@ -20,7 +25,7 @@ * * Returns: a path that can access @name, or NULL if no matching file exists. */ -char *qemu_find_file(int type, const char *name); +char *qemu_find_file(QemuFileType type, const char *name); void qemu_add_default_firmwarepath(void); void qemu_add_data_dir(char *path); void qemu_list_data_dirs(void); diff --git a/system/datadir.c b/system/datadir.c index c9237cb5d4..e450b84ce9 100644 --- a/system/datadir.c +++ b/system/datadir.c @@ -30,7 +30,7 @@ static const char *data_dir[16]; static int data_dir_idx; -char *qemu_find_file(int type, const char *name) +char *qemu_find_file(QemuFileType type, const char *name) { int i; const char *subdir; @@ -44,6 +44,7 @@ char *qemu_find_file(int type, const char *name) switch (type) { case QEMU_FILE_TYPE_BIOS: + case QEMU_FILE_TYPE_DTB: subdir = ""; break; case QEMU_FILE_TYPE_KEYMAP: From 12963e79ca461db7b098c8eb00bb21cf88a250a4 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Wed, 23 Apr 2025 12:02:21 +0200 Subject: [PATCH 0332/2760] pc-bios: Move device tree files in their own subdir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have several device tree files already and may have more in the future so add a new dtb subdirectory and move device tree files there so they are not mixed with ROM binaries. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <57f179bd3904c1f2ca062ca4d4ff9592bb4f4daa.1745402140.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 2 +- pc-bios/{ => dtb}/bamboo.dtb | Bin pc-bios/{ => dtb}/bamboo.dts | 0 pc-bios/{ => dtb}/canyonlands.dtb | Bin pc-bios/{ => dtb}/canyonlands.dts | 0 pc-bios/dtb/meson.build | 23 +++++++++++++++++++++ pc-bios/{ => dtb}/petalogix-ml605.dtb | Bin pc-bios/{ => dtb}/petalogix-ml605.dts | 0 pc-bios/{ => dtb}/petalogix-s3adsp1800.dtb | Bin pc-bios/{ => dtb}/petalogix-s3adsp1800.dts | 0 pc-bios/meson.build | 23 +-------------------- qemu.nsi | 2 +- system/datadir.c | 4 +++- 13 files changed, 29 insertions(+), 25 deletions(-) rename pc-bios/{ => dtb}/bamboo.dtb (100%) rename pc-bios/{ => dtb}/bamboo.dts (100%) rename pc-bios/{ => dtb}/canyonlands.dtb (100%) rename pc-bios/{ => dtb}/canyonlands.dts (100%) create mode 100644 pc-bios/dtb/meson.build rename pc-bios/{ => dtb}/petalogix-ml605.dtb (100%) rename pc-bios/{ => dtb}/petalogix-ml605.dts (100%) rename pc-bios/{ => dtb}/petalogix-s3adsp1800.dtb (100%) rename pc-bios/{ => dtb}/petalogix-s3adsp1800.dts (100%) diff --git a/MAINTAINERS b/MAINTAINERS index 661a47db5a..d82d962f1a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1581,7 +1581,7 @@ F: hw/pci-host/ppc440_pcix.c F: hw/display/sm501* F: hw/ide/sii3112.c F: hw/rtc/m41t80.c -F: pc-bios/canyonlands.dt[sb] +F: pc-bios/dtb/canyonlands.dt[sb] F: pc-bios/u-boot-sam460ex-20100605.bin F: roms/u-boot-sam460ex F: docs/system/ppc/amigang.rst diff --git a/pc-bios/bamboo.dtb b/pc-bios/dtb/bamboo.dtb similarity index 100% rename from pc-bios/bamboo.dtb rename to pc-bios/dtb/bamboo.dtb diff --git a/pc-bios/bamboo.dts b/pc-bios/dtb/bamboo.dts similarity index 100% rename from pc-bios/bamboo.dts rename to pc-bios/dtb/bamboo.dts diff --git a/pc-bios/canyonlands.dtb b/pc-bios/dtb/canyonlands.dtb similarity index 100% rename from pc-bios/canyonlands.dtb rename to pc-bios/dtb/canyonlands.dtb diff --git a/pc-bios/canyonlands.dts b/pc-bios/dtb/canyonlands.dts similarity index 100% rename from pc-bios/canyonlands.dts rename to pc-bios/dtb/canyonlands.dts diff --git a/pc-bios/dtb/meson.build b/pc-bios/dtb/meson.build new file mode 100644 index 0000000000..7a71835bca --- /dev/null +++ b/pc-bios/dtb/meson.build @@ -0,0 +1,23 @@ +dtbs = [ + 'bamboo.dtb', + 'canyonlands.dtb', + 'petalogix-ml605.dtb', + 'petalogix-s3adsp1800.dtb', +] + +dtc = find_program('dtc', required: false) +if dtc.found() + foreach out : dtbs + f = fs.replace_suffix(out, '.dts') + custom_target(f, + build_by_default: have_system, + input: files(f), + output: out, + install: get_option('install_blobs'), + install_dir: qemu_datadir / 'dtb', + command: [ dtc, '-q', '-I', 'dts', '-O', 'dtb', + '-o', '@OUTPUT@', '@INPUT0@' ]) + endforeach +else + install_data(dtbs, install_dir: qemu_datadir / 'dtb') +endif diff --git a/pc-bios/petalogix-ml605.dtb b/pc-bios/dtb/petalogix-ml605.dtb similarity index 100% rename from pc-bios/petalogix-ml605.dtb rename to pc-bios/dtb/petalogix-ml605.dtb diff --git a/pc-bios/petalogix-ml605.dts b/pc-bios/dtb/petalogix-ml605.dts similarity index 100% rename from pc-bios/petalogix-ml605.dts rename to pc-bios/dtb/petalogix-ml605.dts diff --git a/pc-bios/petalogix-s3adsp1800.dtb b/pc-bios/dtb/petalogix-s3adsp1800.dtb similarity index 100% rename from pc-bios/petalogix-s3adsp1800.dtb rename to pc-bios/dtb/petalogix-s3adsp1800.dtb diff --git a/pc-bios/petalogix-s3adsp1800.dts b/pc-bios/dtb/petalogix-s3adsp1800.dts similarity index 100% rename from pc-bios/petalogix-s3adsp1800.dts rename to pc-bios/dtb/petalogix-s3adsp1800.dts diff --git a/pc-bios/meson.build b/pc-bios/meson.build index 34d6616c32..34d8cc4f33 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -86,31 +86,10 @@ blobs = [ 'vof-nvram.bin', ] -dtc = find_program('dtc', required: false) -foreach f : [ - 'bamboo.dts', - 'canyonlands.dts', - 'petalogix-s3adsp1800.dts', - 'petalogix-ml605.dts', -] - out = fs.replace_suffix(f, '.dtb') - if dtc.found() - custom_target(f, - build_by_default: have_system, - input: files(f), - output: out, - install: get_option('install_blobs'), - install_dir: qemu_datadir, - command: [ dtc, '-q', '-I', 'dts', '-O', 'dtb', - '-o', '@OUTPUT@', '@INPUT0@' ]) - else - blobs += out - endif -endforeach - if get_option('install_blobs') install_data(blobs, install_dir: qemu_datadir) endif subdir('descriptors') +subdir('dtb') subdir('keymaps') diff --git a/qemu.nsi b/qemu.nsi index b186f223e1..d419986ca0 100644 --- a/qemu.nsi +++ b/qemu.nsi @@ -204,7 +204,6 @@ Section "Uninstall" Delete "$INSTDIR\*.bmp" Delete "$INSTDIR\*.bin" Delete "$INSTDIR\*.dll" - Delete "$INSTDIR\*.dtb" Delete "$INSTDIR\*.fd" Delete "$INSTDIR\*.img" Delete "$INSTDIR\*.lid" @@ -215,6 +214,7 @@ Section "Uninstall" Delete "$INSTDIR\qemu-io.exe" Delete "$INSTDIR\qemu.exe" Delete "$INSTDIR\qemu-system-*.exe" + RMDir /r "$INSTDIR\dtb" RMDir /r "$INSTDIR\doc" RMDir /r "$INSTDIR\share" ; Remove generated files diff --git a/system/datadir.c b/system/datadir.c index e450b84ce9..f96f8fc264 100644 --- a/system/datadir.c +++ b/system/datadir.c @@ -44,9 +44,11 @@ char *qemu_find_file(QemuFileType type, const char *name) switch (type) { case QEMU_FILE_TYPE_BIOS: - case QEMU_FILE_TYPE_DTB: subdir = ""; break; + case QEMU_FILE_TYPE_DTB: + subdir = "dtb/"; + break; case QEMU_FILE_TYPE_KEYMAP: subdir = "keymaps/"; break; From 563cd698dffb977eea0ccfef3b95f6f9786766f3 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 24 Apr 2025 13:50:11 +0900 Subject: [PATCH 0333/2760] meson: Use has_header_symbol() to check getcpu() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The use of gnu_source_prefix in the detection of getcpu() was ineffective because the header file that declares getcpu() when _GNU_SOURCE is defined was not included. Pass sched.h to has_header_symbol() so that the existence of the declaration will be properly checked. Cc: qemu-stable@nongnu.org Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250424-buildsys-v1-1-97655e3b25d7@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index bcb9d39a38..f77a9ce569 100644 --- a/meson.build +++ b/meson.build @@ -2635,7 +2635,6 @@ config_host_data.set('CONFIG_CLOCK_ADJTIME', cc.has_function('clock_adjtime')) config_host_data.set('CONFIG_DUP3', cc.has_function('dup3')) config_host_data.set('CONFIG_FALLOCATE', cc.has_function('fallocate')) config_host_data.set('CONFIG_POSIX_FALLOCATE', cc.has_function('posix_fallocate')) -config_host_data.set('CONFIG_GETCPU', cc.has_function('getcpu', prefix: gnu_source_prefix)) config_host_data.set('CONFIG_SCHED_GETCPU', cc.has_function('sched_getcpu', prefix: '#include ')) # Note that we need to specify prefix: here to avoid incorrectly # thinking that Windows has posix_memalign() @@ -2713,6 +2712,8 @@ config_host_data.set('CONFIG_FALLOCATE_ZERO_RANGE', config_host_data.set('CONFIG_FIEMAP', cc.has_header('linux/fiemap.h') and cc.has_header_symbol('linux/fs.h', 'FS_IOC_FIEMAP')) +config_host_data.set('CONFIG_GETCPU', + cc.has_header_symbol('sched.h', 'getcpu', prefix: gnu_source_prefix)) config_host_data.set('CONFIG_GETRANDOM', cc.has_function('getrandom') and cc.has_header_symbol('sys/random.h', 'GRND_NONBLOCK')) From 6804b89fb531f5dd49c1e038214c89272383e220 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 24 Apr 2025 13:50:12 +0900 Subject: [PATCH 0334/2760] meson: Remove CONFIG_STATX and CONFIG_STATX_MNT_ID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CONFIG_STATX and CONFIG_STATX_MNT_ID are not used since commit e0dc2631ec4 ("virtiofsd: Remove source"). Cc: qemu-stable@nongnu.org Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250424-buildsys-v1-2-97655e3b25d7@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- meson.build | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/meson.build b/meson.build index f77a9ce569..35dcec8ce5 100644 --- a/meson.build +++ b/meson.build @@ -2191,14 +2191,6 @@ gnu_source_prefix = ''' #endif ''' -# Check whether the glibc provides STATX_BASIC_STATS - -has_statx = cc.has_header_symbol('sys/stat.h', 'STATX_BASIC_STATS', prefix: gnu_source_prefix) - -# Check whether statx() provides mount ID information - -has_statx_mnt_id = cc.has_header_symbol('sys/stat.h', 'STATX_MNT_ID', prefix: gnu_source_prefix) - have_vhost_user_blk_server = get_option('vhost_user_blk_server') \ .require(host_os == 'linux', error_message: 'vhost_user_blk_server requires linux') \ @@ -2560,8 +2552,6 @@ config_host_data.set('CONFIG_CRYPTO_SM3', crypto_sm3.found()) config_host_data.set('CONFIG_HOGWEED', hogweed.found()) config_host_data.set('CONFIG_QEMU_PRIVATE_XTS', xts == 'private') config_host_data.set('CONFIG_MALLOC_TRIM', has_malloc_trim) -config_host_data.set('CONFIG_STATX', has_statx) -config_host_data.set('CONFIG_STATX_MNT_ID', has_statx_mnt_id) config_host_data.set('CONFIG_ZSTD', zstd.found()) config_host_data.set('CONFIG_QPL', qpl.found()) config_host_data.set('CONFIG_UADK', uadk.found()) From 797150d69d2edba8b1bd4a7d8c7ba2df1219c503 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 24 Apr 2025 13:50:13 +0900 Subject: [PATCH 0335/2760] meson: Share common C source prefixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit gnu_source_prefix defines _GNU_SOURCE for compiler object functions. The definition is universally available in the code base. docs/devel/style.rst also says that the "qemu/osdep.h" header is always included, so files included in the file is also universally available in the code base. Rename gnu_source_prefix to osdep_prefix, and add #include directives that are referred by the users of gnu_source_prefix and contained in qemu/osdep.h to safely de-duplicate #include directives. Cc: qemu-stable@nongnu.org Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250424-buildsys-v1-3-97655e3b25d7@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- meson.build | 68 +++++++++++++++++++++++++---------------------------- 1 file changed, 32 insertions(+), 36 deletions(-) diff --git a/meson.build b/meson.build index 35dcec8ce5..d93dbde194 100644 --- a/meson.build +++ b/meson.build @@ -2185,10 +2185,21 @@ if not has_malloc_trim and get_option('malloc_trim').enabled() endif endif -gnu_source_prefix = ''' +osdep_prefix = ''' #ifndef _GNU_SOURCE #define _GNU_SOURCE #endif + + #include + #include + + #include + /* Put unistd.h before time.h as that triggers localtime_r/gmtime_r + * function availability on recentish Mingw-w64 platforms. */ + #include + #include + #include + #include ''' have_vhost_user_blk_server = get_option('vhost_user_blk_server') \ @@ -2703,7 +2714,7 @@ config_host_data.set('CONFIG_FIEMAP', cc.has_header('linux/fiemap.h') and cc.has_header_symbol('linux/fs.h', 'FS_IOC_FIEMAP')) config_host_data.set('CONFIG_GETCPU', - cc.has_header_symbol('sched.h', 'getcpu', prefix: gnu_source_prefix)) + cc.has_header_symbol('sched.h', 'getcpu', prefix: osdep_prefix)) config_host_data.set('CONFIG_GETRANDOM', cc.has_function('getrandom') and cc.has_header_symbol('sys/random.h', 'GRND_NONBLOCK')) @@ -2748,8 +2759,7 @@ config_host_data.set('HAVE_UTMPX', config_host_data.set('CONFIG_EVENTFD', cc.links(''' #include int main(void) { return eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); }''')) -config_host_data.set('CONFIG_FDATASYNC', cc.links(gnu_source_prefix + ''' - #include +config_host_data.set('CONFIG_FDATASYNC', cc.links(osdep_prefix + ''' int main(void) { #if defined(_POSIX_SYNCHRONIZED_IO) && _POSIX_SYNCHRONIZED_IO > 0 return fdatasync(0); @@ -2758,10 +2768,8 @@ config_host_data.set('CONFIG_FDATASYNC', cc.links(gnu_source_prefix + ''' #endif }''')) -has_madvise = cc.links(gnu_source_prefix + ''' - #include +has_madvise = cc.links(osdep_prefix + ''' #include - #include int main(void) { return madvise(NULL, 0, MADV_DONTNEED); }''') missing_madvise_proto = false if has_madvise @@ -2771,21 +2779,18 @@ if has_madvise # missing-prototype case, we try again with a definitely-bogus prototype. # This will only compile if the system headers don't provide the prototype; # otherwise the conflicting prototypes will cause a compiler error. - missing_madvise_proto = cc.links(gnu_source_prefix + ''' - #include + missing_madvise_proto = cc.links(osdep_prefix + '''> #include - #include extern int madvise(int); int main(void) { return madvise(0); }''') endif config_host_data.set('CONFIG_MADVISE', has_madvise) config_host_data.set('HAVE_MADVISE_WITHOUT_PROTOTYPE', missing_madvise_proto) -config_host_data.set('CONFIG_MEMFD', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_MEMFD', cc.links(osdep_prefix + ''' #include int main(void) { return memfd_create("foo", MFD_ALLOW_SEALING); }''')) -config_host_data.set('CONFIG_OPEN_BY_HANDLE', cc.links(gnu_source_prefix + ''' - #include +config_host_data.set('CONFIG_OPEN_BY_HANDLE', cc.links(osdep_prefix + ''' #if !defined(AT_EMPTY_PATH) # error missing definition #else @@ -2796,13 +2801,12 @@ config_host_data.set('CONFIG_OPEN_BY_HANDLE', cc.links(gnu_source_prefix + ''' # i.e. errno is set and -1 is returned. That's not really how POSIX defines the # function. On the flip side, it has madvise() which is preferred anyways. if host_os != 'darwin' - config_host_data.set('CONFIG_POSIX_MADVISE', cc.links(gnu_source_prefix + ''' + config_host_data.set('CONFIG_POSIX_MADVISE', cc.links(osdep_prefix + ''' #include - #include int main(void) { return posix_madvise(NULL, 0, POSIX_MADV_DONTNEED); }''')) endif -config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_W_TID', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_W_TID', cc.links(osdep_prefix + ''' #include static void *f(void *p) { return NULL; } @@ -2813,7 +2817,7 @@ config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_W_TID', cc.links(gnu_source_pref pthread_setname_np(thread, "QEMU"); return 0; }''', dependencies: threads)) -config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_WO_TID', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_WO_TID', cc.links(osdep_prefix + ''' #include static void *f(void *p) { pthread_setname_np("QEMU"); return NULL; } @@ -2823,7 +2827,7 @@ config_host_data.set('CONFIG_PTHREAD_SETNAME_NP_WO_TID', cc.links(gnu_source_pre pthread_create(&thread, 0, f, 0); return 0; }''', dependencies: threads)) -config_host_data.set('CONFIG_PTHREAD_SET_NAME_NP', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_PTHREAD_SET_NAME_NP', cc.links(osdep_prefix + ''' #include #include @@ -2835,9 +2839,8 @@ config_host_data.set('CONFIG_PTHREAD_SET_NAME_NP', cc.links(gnu_source_prefix + pthread_set_name_np(thread, "QEMU"); return 0; }''', dependencies: threads)) -config_host_data.set('CONFIG_PTHREAD_CONDATTR_SETCLOCK', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_PTHREAD_CONDATTR_SETCLOCK', cc.links(osdep_prefix + ''' #include - #include int main(void) { @@ -2846,7 +2849,7 @@ config_host_data.set('CONFIG_PTHREAD_CONDATTR_SETCLOCK', cc.links(gnu_source_pre pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); return 0; }''', dependencies: threads)) -config_host_data.set('CONFIG_PTHREAD_AFFINITY_NP', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_PTHREAD_AFFINITY_NP', cc.links(osdep_prefix + ''' #include static void *f(void *p) { return NULL; } @@ -2863,15 +2866,10 @@ config_host_data.set('CONFIG_PTHREAD_AFFINITY_NP', cc.links(gnu_source_prefix + CPU_FREE(cpuset); return 0; }''', dependencies: threads)) -config_host_data.set('CONFIG_SIGNALFD', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_SIGNALFD', cc.links(osdep_prefix + ''' #include - #include int main(void) { return signalfd(-1, NULL, SFD_CLOEXEC); }''')) -config_host_data.set('CONFIG_SPLICE', cc.links(gnu_source_prefix + ''' - #include - #include - #include - +config_host_data.set('CONFIG_SPLICE', cc.links(osdep_prefix + ''' int main(void) { int len, fd = 0; @@ -2880,13 +2878,13 @@ config_host_data.set('CONFIG_SPLICE', cc.links(gnu_source_prefix + ''' return 0; }''')) -config_host_data.set('HAVE_MLOCKALL', cc.links(gnu_source_prefix + ''' +config_host_data.set('HAVE_MLOCKALL', cc.links(osdep_prefix + ''' #include int main(void) { return mlockall(MCL_FUTURE); }''')) -config_host_data.set('HAVE_MLOCK_ONFAULT', cc.links(gnu_source_prefix + ''' +config_host_data.set('HAVE_MLOCK_ONFAULT', cc.links(osdep_prefix + ''' #include int main(void) { return mlockall(MCL_FUTURE | MCL_ONFAULT); @@ -2895,7 +2893,7 @@ config_host_data.set('HAVE_MLOCK_ONFAULT', cc.links(gnu_source_prefix + ''' have_l2tpv3 = false if get_option('l2tpv3').allowed() and have_system have_l2tpv3 = cc.has_type('struct mmsghdr', - prefix: gnu_source_prefix + ''' + prefix: osdep_prefix + ''' #include #include ''') endif @@ -3011,13 +3009,13 @@ if has_int128_type endif endif -config_host_data.set('CONFIG_GETAUXVAL', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_GETAUXVAL', cc.links(osdep_prefix + ''' #include int main(void) { return getauxval(AT_HWCAP) == 0; }''')) -config_host_data.set('CONFIG_ELF_AUX_INFO', cc.links(gnu_source_prefix + ''' +config_host_data.set('CONFIG_ELF_AUX_INFO', cc.links(osdep_prefix + ''' #include int main(void) { unsigned long hwcap = 0; @@ -3130,9 +3128,7 @@ config_host_data.set('CONFIG_MEMBARRIER', get_option('membarrier') \ .allowed()) have_afalg = get_option('crypto_afalg') \ - .require(cc.compiles(gnu_source_prefix + ''' - #include - #include + .require(cc.compiles(osdep_prefix + ''' #include #include int main(void) { From a5b30be534538dc6e44a68ce9734e45dd08f52ec Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 24 Apr 2025 13:50:14 +0900 Subject: [PATCH 0336/2760] meson: Use osdep_prefix for strchrnul() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit macOS SDK may have the symbol of strchrnul(), but it is actually available only on macOS 15.4 or later and that fact is codified in string.h. Include the header file using osdep_prefix to check if the function is available on the deployment target. Cc: qemu-stable@nongnu.org Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250424-buildsys-v1-4-97655e3b25d7@daynix.com> Signed-off-by: Philippe Mathieu-Daudé --- meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index d93dbde194..c736a6f4c4 100644 --- a/meson.build +++ b/meson.build @@ -2193,6 +2193,7 @@ osdep_prefix = ''' #include #include + #include #include /* Put unistd.h before time.h as that triggers localtime_r/gmtime_r * function availability on recentish Mingw-w64 platforms. */ @@ -2657,7 +2658,7 @@ config_host_data.set('HAVE_GETIFADDRS', cc.has_function('getifaddrs')) config_host_data.set('HAVE_GLIB_WITH_SLICE_ALLOCATOR', glib_has_gslice) config_host_data.set('HAVE_GLIB_WITH_ALIGNED_ALLOC', glib_has_aligned_alloc) config_host_data.set('HAVE_OPENPTY', cc.has_function('openpty', dependencies: util)) -config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul')) +config_host_data.set('HAVE_STRCHRNUL', cc.has_function('strchrnul', prefix: osdep_prefix)) config_host_data.set('HAVE_SYSTEM_FUNCTION', cc.has_function('system', prefix: '#include ')) if rbd.found() config_host_data.set('HAVE_RBD_NAMESPACE_EXISTS', From 2ef9faf03720a40eae7fc8956f1941dddfb0eea7 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 24 Apr 2025 16:28:28 -0700 Subject: [PATCH 0337/2760] system/kvm: make functions accessible from common code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250424232829.141163-8-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- include/system/kvm.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/system/kvm.h b/include/system/kvm.h index 18811cad6f..b690dda137 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h @@ -210,6 +210,10 @@ bool kvm_arm_supports_user_irq(void); int kvm_on_sigbus_vcpu(CPUState *cpu, int code, void *addr); int kvm_on_sigbus(int code, void *addr); +int kvm_check_extension(KVMState *s, unsigned int extension); + +int kvm_vm_ioctl(KVMState *s, unsigned long type, ...); + void kvm_flush_coalesced_mmio_buffer(void); #ifdef COMPILING_PER_TARGET @@ -237,8 +241,6 @@ static inline int kvm_update_guest_debug(CPUState *cpu, unsigned long reinject_t int kvm_ioctl(KVMState *s, unsigned long type, ...); -int kvm_vm_ioctl(KVMState *s, unsigned long type, ...); - int kvm_vcpu_ioctl(CPUState *cpu, unsigned long type, ...); /** @@ -441,8 +443,6 @@ void kvm_arch_update_guest_debug(CPUState *cpu, struct kvm_guest_debug *dbg); bool kvm_arch_stop_on_emulation_error(CPUState *cpu); -int kvm_check_extension(KVMState *s, unsigned int extension); - int kvm_vm_check_extension(KVMState *s, unsigned int extension); #define kvm_vm_enable_cap(s, capability, cap_flags, ...) \ From 58bc9db84af4e1bf3a8c55fa18277acbfaeb1caf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 Apr 2025 09:48:42 +0200 Subject: [PATCH 0338/2760] accel/tcg: Correct list of included headers in tcg-stub.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 3549118b498 we moved cpu_loop_exit*() declarations to "exec/cpu-common.h" but neglected to update tcg-stub.c. We missed it because "exec/cpu-common.h" is indirectly pulled in via "exec/exec-all.h" -> "exec/translation-block.h". Include it directly instead of the not necessary "exec/exec-all.h". Commit bb6cf6f0168 ("accel/tcg: Factor tcg_cpu_reset_hold() out") removed the need for "exec/tb-flush.h", so remote it too. Fixes: 3549118b498 ("exec: Move cpu_loop_foo() functions to 'cpu-common.h'") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Message-Id: <20250424094653.35932-4-philmd@linaro.org> --- accel/stubs/tcg-stub.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/accel/stubs/tcg-stub.c b/accel/stubs/tcg-stub.c index b2b9881bdf..3b76b8b17c 100644 --- a/accel/stubs/tcg-stub.c +++ b/accel/stubs/tcg-stub.c @@ -11,8 +11,7 @@ */ #include "qemu/osdep.h" -#include "exec/tb-flush.h" -#include "exec/exec-all.h" +#include "exec/cpu-common.h" G_NORETURN void cpu_loop_exit(CPUState *cpu) { From f56159e82885bb7cbbe5d28698a486c7ccbfb2ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 Apr 2025 10:18:58 +0200 Subject: [PATCH 0339/2760] target/hexagon: Include missing 'accel/tcg/getpc.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the macros.h headers call GETPC(), they need to include "accel/tcg/getpc.h", which defines it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Reviewed-by: Brian Cain Message-Id: <20250424094653.35932-9-philmd@linaro.org> --- target/hexagon/macros.h | 1 + target/hexagon/mmvec/macros.h | 1 + 2 files changed, 2 insertions(+) diff --git a/target/hexagon/macros.h b/target/hexagon/macros.h index e5eb31e671..9ba9be408d 100644 --- a/target/hexagon/macros.h +++ b/target/hexagon/macros.h @@ -21,6 +21,7 @@ #include "cpu.h" #include "hex_regs.h" #include "reg_fields.h" +#include "accel/tcg/getpc.h" #define GET_FIELD(FIELD, REGIN) \ fEXTRACTU_BITS(REGIN, reg_field_info[FIELD].width, \ diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h index bcd4a1e897..c1a88392c0 100644 --- a/target/hexagon/mmvec/macros.h +++ b/target/hexagon/mmvec/macros.h @@ -21,6 +21,7 @@ #include "qemu/host-utils.h" #include "arch.h" #include "mmvec/system_ext_mmvec.h" +#include "accel/tcg/getpc.h" #ifndef QEMU_GENERATE #define VdV (*(MMVector *restrict)(VdV_void)) From 4e442406fde1957a8c4eebbcd878fb5f25cc49c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Apr 2025 10:38:10 +0200 Subject: [PATCH 0340/2760] linux-user/elfload: Use target_needs_bswap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check whether we need to swap at runtime using target_needs_bswap(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250417131004.47205-2-philmd@linaro.org> --- linux-user/elfload.c | 63 +++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 99811af5e7..fbfdec2f17 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -12,6 +12,7 @@ #include "exec/page-protection.h" #include "exec/mmap-lock.h" #include "exec/translation-block.h" +#include "exec/tswap.h" #include "user/guest-base.h" #include "user-internals.h" #include "signal-common.h" @@ -2122,9 +2123,12 @@ static inline void memcpy_fromfs(void * to, const void * from, unsigned long n) memcpy(to, from, n); } -#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static void bswap_ehdr(struct elfhdr *ehdr) { + if (!target_needs_bswap()) { + return; + } + bswap16s(&ehdr->e_type); /* Object file type */ bswap16s(&ehdr->e_machine); /* Architecture */ bswap32s(&ehdr->e_version); /* Object file version */ @@ -2142,8 +2146,11 @@ static void bswap_ehdr(struct elfhdr *ehdr) static void bswap_phdr(struct elf_phdr *phdr, int phnum) { - int i; - for (i = 0; i < phnum; ++i, ++phdr) { + if (!target_needs_bswap()) { + return; + } + + for (int i = 0; i < phnum; ++i, ++phdr) { bswap32s(&phdr->p_type); /* Segment type */ bswap32s(&phdr->p_flags); /* Segment flags */ bswaptls(&phdr->p_offset); /* Segment file offset */ @@ -2157,8 +2164,11 @@ static void bswap_phdr(struct elf_phdr *phdr, int phnum) static void bswap_shdr(struct elf_shdr *shdr, int shnum) { - int i; - for (i = 0; i < shnum; ++i, ++shdr) { + if (!target_needs_bswap()) { + return; + } + + for (int i = 0; i < shnum; ++i, ++shdr) { bswap32s(&shdr->sh_name); bswap32s(&shdr->sh_type); bswaptls(&shdr->sh_flags); @@ -2174,6 +2184,10 @@ static void bswap_shdr(struct elf_shdr *shdr, int shnum) static void bswap_sym(struct elf_sym *sym) { + if (!target_needs_bswap()) { + return; + } + bswap32s(&sym->st_name); bswaptls(&sym->st_value); bswaptls(&sym->st_size); @@ -2183,6 +2197,10 @@ static void bswap_sym(struct elf_sym *sym) #ifdef TARGET_MIPS static void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) { + if (!target_needs_bswap()) { + return; + } + bswap16s(&abiflags->version); bswap32s(&abiflags->ases); bswap32s(&abiflags->isa_ext); @@ -2190,15 +2208,6 @@ static void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) bswap32s(&abiflags->flags2); } #endif -#else -static inline void bswap_ehdr(struct elfhdr *ehdr) { } -static inline void bswap_phdr(struct elf_phdr *phdr, int phnum) { } -static inline void bswap_shdr(struct elf_shdr *shdr, int shnum) { } -static inline void bswap_sym(struct elf_sym *sym) { } -#ifdef TARGET_MIPS -static inline void bswap_mips_abiflags(Mips_elf_abiflags_v0 *abiflags) { } -#endif -#endif #ifdef USE_ELF_CORE_DUMP static int elf_core_dump(int, const CPUArchState *); @@ -3144,11 +3153,11 @@ static bool parse_elf_properties(const ImageSource *src, * The contents of a valid PT_GNU_PROPERTY is a sequence of uint32_t. * Swap most of them now, beyond the header and namesz. */ -#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN - for (int i = 4; i < n / 4; i++) { - bswap32s(note.data + i); + if (target_needs_bswap()) { + for (int i = 4; i < n / 4; i++) { + bswap32s(note.data + i); + } } -#endif /* * Note that nhdr is 3 words, and that the "name" described by namesz @@ -4000,9 +4009,12 @@ struct target_elf_prpsinfo { char pr_psargs[ELF_PRARGSZ]; /* initial part of arg list */ }; -#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN static void bswap_prstatus(struct target_elf_prstatus *prstatus) { + if (!target_needs_bswap()) { + return; + } + prstatus->pr_info.si_signo = tswap32(prstatus->pr_info.si_signo); prstatus->pr_info.si_code = tswap32(prstatus->pr_info.si_code); prstatus->pr_info.si_errno = tswap32(prstatus->pr_info.si_errno); @@ -4020,6 +4032,10 @@ static void bswap_prstatus(struct target_elf_prstatus *prstatus) static void bswap_psinfo(struct target_elf_prpsinfo *psinfo) { + if (!target_needs_bswap()) { + return; + } + psinfo->pr_flag = tswapal(psinfo->pr_flag); psinfo->pr_uid = tswap16(psinfo->pr_uid); psinfo->pr_gid = tswap16(psinfo->pr_gid); @@ -4031,15 +4047,14 @@ static void bswap_psinfo(struct target_elf_prpsinfo *psinfo) static void bswap_note(struct elf_note *en) { + if (!target_needs_bswap()) { + return; + } + bswap32s(&en->n_namesz); bswap32s(&en->n_descsz); bswap32s(&en->n_type); } -#else -static inline void bswap_prstatus(struct target_elf_prstatus *p) { } -static inline void bswap_psinfo(struct target_elf_prpsinfo *p) {} -static inline void bswap_note(struct elf_note *en) { } -#endif /* HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN */ /* * Calculate file (dump) size of given memory region. From 56f8fb6886c49a99962d50912a8dde9e8dbfc306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Apr 2025 10:37:53 +0200 Subject: [PATCH 0341/2760] accel/kvm: Use target_needs_bswap() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check whether we need to swap at runtime using target_needs_bswap(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250417131004.47205-3-philmd@linaro.org> --- accel/kvm/kvm-all.c | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index b8c68c7819..278a50690c 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -33,6 +33,7 @@ #include "system/cpus.h" #include "system/accel-blocker.h" #include "qemu/bswap.h" +#include "exec/tswap.h" #include "system/memory.h" #include "system/ram_addr.h" #include "qemu/event_notifier.h" @@ -1318,21 +1319,22 @@ bool kvm_hwpoisoned_mem(void) static uint32_t adjust_ioeventfd_endianness(uint32_t val, uint32_t size) { -#if HOST_BIG_ENDIAN != TARGET_BIG_ENDIAN - /* The kernel expects ioeventfd values in HOST_BIG_ENDIAN - * endianness, but the memory core hands them in target endianness. - * For example, PPC is always treated as big-endian even if running - * on KVM and on PPC64LE. Correct here. - */ - switch (size) { - case 2: - val = bswap16(val); - break; - case 4: - val = bswap32(val); - break; + if (target_needs_bswap()) { + /* + * The kernel expects ioeventfd values in HOST_BIG_ENDIAN + * endianness, but the memory core hands them in target endianness. + * For example, PPC is always treated as big-endian even if running + * on KVM and on PPC64LE. Correct here, swapping back. + */ + switch (size) { + case 2: + val = bswap16(val); + break; + case 4: + val = bswap32(val); + break; + } } -#endif return val; } From 33fa8f02a91986c6c3e7de748964a312c14a5a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Apr 2025 10:47:00 +0200 Subject: [PATCH 0342/2760] target/mips: Check CPU endianness at runtime using env_is_bigendian() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since CPU endianness can be toggled at runtime before resetting, checking the endianness at build time preprocessing the TARGET_BIG_ENDIAN definition isn't correct. We have to call mips_env_is_bigendian() to get the CPU endianness at runtime. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250417131004.47205-4-philmd@linaro.org> --- target/mips/tcg/msa_helper.c | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index 14de4a71ff..e349344647 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -8212,7 +8212,6 @@ void helper_msa_ffint_u_df(CPUMIPSState *env, uint32_t df, uint32_t wd, /* Element-by-element access macros */ #define DF_ELEMENTS(df) (MSA_WRLEN / DF_BITS(df)) -#if TARGET_BIG_ENDIAN static inline uint64_t bswap16x4(uint64_t x) { uint64_t m = 0x00ff00ff00ff00ffull; @@ -8223,7 +8222,6 @@ static inline uint64_t bswap32x2(uint64_t x) { return ror64(bswap64(x), 32); } -#endif void helper_msa_ld_b(CPUMIPSState *env, uint32_t wd, target_ulong addr) @@ -8252,10 +8250,10 @@ void helper_msa_ld_h(CPUMIPSState *env, uint32_t wd, */ d0 = cpu_ldq_le_data_ra(env, addr + 0, ra); d1 = cpu_ldq_le_data_ra(env, addr + 8, ra); -#if TARGET_BIG_ENDIAN - d0 = bswap16x4(d0); - d1 = bswap16x4(d1); -#endif + if (mips_env_is_bigendian(env)) { + d0 = bswap16x4(d0); + d1 = bswap16x4(d1); + } pwd->d[0] = d0; pwd->d[1] = d1; } @@ -8273,10 +8271,10 @@ void helper_msa_ld_w(CPUMIPSState *env, uint32_t wd, */ d0 = cpu_ldq_le_data_ra(env, addr + 0, ra); d1 = cpu_ldq_le_data_ra(env, addr + 8, ra); -#if TARGET_BIG_ENDIAN - d0 = bswap32x2(d0); - d1 = bswap32x2(d1); -#endif + if (mips_env_is_bigendian(env)) { + d0 = bswap32x2(d0); + d1 = bswap32x2(d1); + } pwd->d[0] = d0; pwd->d[1] = d1; } @@ -8339,10 +8337,10 @@ void helper_msa_st_h(CPUMIPSState *env, uint32_t wd, /* Store 8 bytes at a time. See helper_msa_ld_h. */ d0 = pwd->d[0]; d1 = pwd->d[1]; -#if TARGET_BIG_ENDIAN - d0 = bswap16x4(d0); - d1 = bswap16x4(d1); -#endif + if (mips_env_is_bigendian(env)) { + d0 = bswap16x4(d0); + d1 = bswap16x4(d1); + } cpu_stq_le_data_ra(env, addr + 0, d0, ra); cpu_stq_le_data_ra(env, addr + 8, d1, ra); } @@ -8360,10 +8358,10 @@ void helper_msa_st_w(CPUMIPSState *env, uint32_t wd, /* Store 8 bytes at a time. See helper_msa_ld_w. */ d0 = pwd->d[0]; d1 = pwd->d[1]; -#if TARGET_BIG_ENDIAN - d0 = bswap32x2(d0); - d1 = bswap32x2(d1); -#endif + if (mips_env_is_bigendian(env)) { + d0 = bswap32x2(d0); + d1 = bswap32x2(d1); + } cpu_stq_le_data_ra(env, addr + 0, d0, ra); cpu_stq_le_data_ra(env, addr + 8, d1, ra); } From 770f2e64b6a6ae13c00cd1cc083eaf9728c0f934 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Apr 2025 10:40:29 +0200 Subject: [PATCH 0343/2760] target/xtensa: Evaluate TARGET_BIG_ENDIAN at compile time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than evaluating TARGET_BIG_ENDIAN at preprocessing time via #ifdef'ry, do it in C at compile time Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250417131004.47205-6-philmd@linaro.org> --- target/xtensa/translate.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 5ebd4a512c..2af83c07c2 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -1395,11 +1395,11 @@ static void translate_bbi(DisasContext *dc, const OpcodeArg arg[], const uint32_t par[]) { TCGv_i32 tmp = tcg_temp_new_i32(); -#if TARGET_BIG_ENDIAN - tcg_gen_andi_i32(tmp, arg[0].in, 0x80000000u >> arg[1].imm); -#else - tcg_gen_andi_i32(tmp, arg[0].in, 0x00000001u << arg[1].imm); -#endif + if (TARGET_BIG_ENDIAN) { + tcg_gen_andi_i32(tmp, arg[0].in, 0x80000000u >> arg[1].imm); + } else { + tcg_gen_andi_i32(tmp, arg[0].in, 0x00000001u << arg[1].imm); + } gen_brcondi(dc, par[0], tmp, 0, arg[2].imm); } From 1b079a6eebb879d14da193919afafc303e938427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 16 Apr 2025 14:33:06 +0200 Subject: [PATCH 0344/2760] hw/mips: Evaluate TARGET_BIG_ENDIAN at compile time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than evaluating TARGET_BIG_ENDIAN at preprocessing time via #ifdef'ry, do it in C at compile time Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250417131004.47205-7-philmd@linaro.org> --- hw/mips/jazz.c | 11 ++++------- hw/mips/malta.c | 21 ++++++--------------- hw/mips/mipssim.c | 11 ++++------- 3 files changed, 14 insertions(+), 29 deletions(-) diff --git a/hw/mips/jazz.c b/hw/mips/jazz.c index cee92e1825..7fb0b97a38 100644 --- a/hw/mips/jazz.c +++ b/hw/mips/jazz.c @@ -59,12 +59,6 @@ enum jazz_model_e { JAZZ_PICA61, }; -#if TARGET_BIG_ENDIAN -#define BIOS_FILENAME "mips_bios.bin" -#else -#define BIOS_FILENAME "mipsel_bios.bin" -#endif - static void main_cpu_reset(void *opaque) { MIPSCPU *cpu = opaque; @@ -168,6 +162,8 @@ static void mips_jazz_init_net(IOMMUMemoryRegion *rc4030_dma_mr, static void mips_jazz_init(MachineState *machine, enum jazz_model_e jazz_model) { + const char *bios_name = TARGET_BIG_ENDIAN ? "mips_bios.bin" + : "mipsel_bios.bin"; MemoryRegion *address_space = get_system_memory(); char *filename; int bios_size, n; @@ -245,7 +241,8 @@ static void mips_jazz_init(MachineState *machine, memory_region_add_subregion(address_space, 0xfff00000LL, bios2); /* load the BIOS image. */ - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware ?: BIOS_FILENAME); + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, + machine->firmware ?: bios_name); if (filename) { bios_size = load_image_targphys(filename, 0xfff00000LL, MAGNUM_BIOS_SIZE); diff --git a/hw/mips/malta.c b/hw/mips/malta.c index 8e9cea70b1..cbdbb21056 100644 --- a/hw/mips/malta.c +++ b/hw/mips/malta.c @@ -94,12 +94,6 @@ typedef struct { bool display_inited; } MaltaFPGAState; -#if TARGET_BIG_ENDIAN -#define BIOS_FILENAME "mips_bios.bin" -#else -#define BIOS_FILENAME "mipsel_bios.bin" -#endif - #define TYPE_MIPS_MALTA "mips-malta" OBJECT_DECLARE_SIMPLE_TYPE(MaltaState, MIPS_MALTA) @@ -383,11 +377,7 @@ static uint64_t malta_fpga_read(void *opaque, hwaddr addr, /* STATUS Register */ case 0x00208: -#if TARGET_BIG_ENDIAN - val = 0x00000012; -#else - val = 0x00000010; -#endif + val = TARGET_BIG_ENDIAN ? 0x00000012 : 0x00000010; break; /* JMPRS Register */ @@ -1177,9 +1167,12 @@ void mips_malta_init(MachineState *machine) target_long bios_size = FLASH_SIZE; /* Load firmware from flash. */ if (!dinfo) { + const char *bios_name = TARGET_BIG_ENDIAN ? "mips_bios.bin" + : "mipsel_bios.bin"; + /* Load a BIOS image. */ filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, - machine->firmware ?: BIOS_FILENAME); + machine->firmware ?: bios_name); if (filename) { bios_size = load_image_targphys(filename, FLASH_ADDRESS, BIOS_SIZE); @@ -1197,8 +1190,7 @@ void mips_malta_init(MachineState *machine) * In little endian mode the 32bit words in the bios are swapped, * a neat trick which allows bi-endian firmware. */ -#if !TARGET_BIG_ENDIAN - { + if (!TARGET_BIG_ENDIAN) { uint32_t *end, *addr; const size_t swapsize = MIN(bios_size, 0x3e0000); addr = rom_ptr(FLASH_ADDRESS, swapsize); @@ -1211,7 +1203,6 @@ void mips_malta_init(MachineState *machine) addr++; } } -#endif } /* diff --git a/hw/mips/mipssim.c b/hw/mips/mipssim.c index b6dabf2893..e843307b9b 100644 --- a/hw/mips/mipssim.c +++ b/hw/mips/mipssim.c @@ -46,12 +46,6 @@ #define BIOS_SIZE (4 * MiB) -#if TARGET_BIG_ENDIAN -#define BIOS_FILENAME "mips_bios.bin" -#else -#define BIOS_FILENAME "mipsel_bios.bin" -#endif - static struct _loaderparams { int ram_size; const char *kernel_filename; @@ -143,6 +137,8 @@ mips_mipssim_init(MachineState *machine) const char *kernel_filename = machine->kernel_filename; const char *kernel_cmdline = machine->kernel_cmdline; const char *initrd_filename = machine->initrd_filename; + const char *bios_name = TARGET_BIG_ENDIAN ? "mips_bios.bin" + : "mipsel_bios.bin"; char *filename; MemoryRegion *address_space_mem = get_system_memory(); MemoryRegion *isa = g_new(MemoryRegion, 1); @@ -179,7 +175,8 @@ mips_mipssim_init(MachineState *machine) /* Map the BIOS / boot exception handler. */ memory_region_add_subregion(address_space_mem, 0x1fc00000LL, bios); /* Load a BIOS / boot exception handler image. */ - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, machine->firmware ?: BIOS_FILENAME); + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, + machine->firmware ?: bios_name); if (filename) { bios_size = load_image_targphys(filename, 0x1fc00000LL, BIOS_SIZE); g_free(filename); From eb3020b6ed2baca63c2a3fad012335670841ead6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Apr 2025 10:18:35 +0200 Subject: [PATCH 0345/2760] hw/microblaze: Evaluate TARGET_BIG_ENDIAN at compile time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than evaluating TARGET_BIG_ENDIAN at preprocessing time via #ifdef'ry, do it in C at compile time Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250417131004.47205-8-philmd@linaro.org> --- hw/microblaze/petalogix_ml605_mmu.c | 12 ++++++------ hw/microblaze/xlnx-zynqmp-pmu.c | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index c887c7a99e..bea6b689fd 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -218,12 +218,12 @@ petalogix_ml605_init(MachineState *machine) static void petalogix_ml605_machine_init(MachineClass *mc) { -#if TARGET_BIG_ENDIAN - mc->desc = "PetaLogix linux refdesign for xilinx ml605 (big endian)"; - mc->deprecation_reason = "big endian support is not tested"; -#else - mc->desc = "PetaLogix linux refdesign for xilinx ml605 (little endian)"; -#endif + if (TARGET_BIG_ENDIAN) { + mc->desc = "PetaLogix linux refdesign for xilinx ml605 (big endian)"; + mc->deprecation_reason = "big endian support is not tested"; + } else { + mc->desc = "PetaLogix linux refdesign for xilinx ml605 (little endian)"; + } mc->init = petalogix_ml605_init; } diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index ea1430f408..ed40b5f2e0 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -181,12 +181,12 @@ static void xlnx_zynqmp_pmu_init(MachineState *machine) static void xlnx_zynqmp_pmu_machine_init(MachineClass *mc) { -#if TARGET_BIG_ENDIAN - mc->desc = "Xilinx ZynqMP PMU machine (big endian)"; - mc->deprecation_reason = "big endian support is not tested"; -#else - mc->desc = "Xilinx ZynqMP PMU machine (little endian)"; -#endif + if (TARGET_BIG_ENDIAN) { + mc->desc = "Xilinx ZynqMP PMU machine (big endian)"; + mc->deprecation_reason = "big endian support is not tested"; + } else { + mc->desc = "Xilinx ZynqMP PMU machine (little endian)"; + } mc->init = xlnx_zynqmp_pmu_init; } From 0c9d76f519c0f67506d276c0ee5637bac0b40121 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 16 Apr 2025 13:25:23 +0200 Subject: [PATCH 0346/2760] qapi: Rename TargetInfo structure as QemuTargetInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QAPI-generated 'TargetInfo' structure name is only used in a single file. We want to heavily use another structure similarly named. Rename the QAPI one, since structure names are not part of the public API. Suggested-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Reviewed-by: Markus Armbruster Message-Id: <20250422145502.70770-2-philmd@linaro.org> --- hw/core/machine-qmp-cmds.c | 4 ++-- qapi/machine.json | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 9447e345b3..a5e635152d 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -134,9 +134,9 @@ CurrentMachineParams *qmp_query_current_machine(Error **errp) return params; } -TargetInfo *qmp_query_target(Error **errp) +QemuTargetInfo *qmp_query_target(Error **errp) { - TargetInfo *info = g_malloc0(sizeof(*info)); + QemuTargetInfo *info = g_malloc0(sizeof(*info)); info->arch = qapi_enum_parse(&SysEmuTarget_lookup, target_name(), -1, &error_abort); diff --git a/qapi/machine.json b/qapi/machine.json index a9ff807631..c8feb9fe17 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -275,15 +275,15 @@ { 'command': 'query-current-machine', 'returns': 'CurrentMachineParams' } ## -# @TargetInfo: +# @QemuTargetInfo: # -# Information describing the QEMU target. +# Information on the target configuration built into the QEMU binary. # # @arch: the target architecture # # Since: 1.2 ## -{ 'struct': 'TargetInfo', +{ 'struct': 'QemuTargetInfo', 'data': { 'arch': 'SysEmuTarget' } } ## @@ -291,11 +291,11 @@ # # Return information about the target for this QEMU # -# Returns: TargetInfo +# Returns: QemuTargetInfo # # Since: 1.2 ## -{ 'command': 'query-target', 'returns': 'TargetInfo' } +{ 'command': 'query-target', 'returns': 'QemuTargetInfo' } ## # @UuidInfo: From 2b7ae6e0f645f3c3676152f3aa7b8c50a091c486 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 17 Apr 2025 17:59:35 +0200 Subject: [PATCH 0347/2760] qemu: Introduce target_cpu_type() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the target_cpu_type() helper to access the CPU_RESOLVING_TYPE target-specific definition from target-agnostic code. Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250417165430.58213-2-philmd@linaro.org> --- MAINTAINERS | 2 ++ include/qemu/target-info.h | 19 +++++++++++++++++++ meson.build | 2 ++ target-info-stub.c | 16 ++++++++++++++++ 4 files changed, 39 insertions(+) create mode 100644 include/qemu/target-info.h create mode 100644 target-info-stub.c diff --git a/MAINTAINERS b/MAINTAINERS index d82d962f1a..28b1e9ba44 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -496,6 +496,7 @@ F: include/exec/cpu*.h F: include/exec/exec-all.h F: include/exec/target_long.h F: include/qemu/accel.h +F: include/qemu/target-info*.h F: include/system/accel-*.h F: include/system/cpus.h F: include/accel/accel-cpu-target.h @@ -504,6 +505,7 @@ F: accel/Makefile.objs F: accel/stubs/Makefile.objs F: cpu-common.c F: cpu-target.c +F: target-info*.c F: system/cpus.c Apple Silicon HVF CPUs diff --git a/include/qemu/target-info.h b/include/qemu/target-info.h new file mode 100644 index 0000000000..b4cc4888ca --- /dev/null +++ b/include/qemu/target-info.h @@ -0,0 +1,19 @@ +/* + * QEMU target info API + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_TARGET_INFO_H +#define QEMU_TARGET_INFO_H + +/** + * target_cpu_type: + * + * Returns: target CPU base QOM type name (i.e. TYPE_X86_CPU). + */ +const char *target_cpu_type(void); + +#endif diff --git a/meson.build b/meson.build index c736a6f4c4..185c2fb0d1 100644 --- a/meson.build +++ b/meson.build @@ -3795,6 +3795,8 @@ endif common_ss.add(pagevary) specific_ss.add(files('page-target.c', 'page-vary-target.c')) +specific_ss.add(files('target-info-stub.c')) + subdir('backends') subdir('disas') subdir('migration') diff --git a/target-info-stub.c b/target-info-stub.c new file mode 100644 index 0000000000..e5d2195e89 --- /dev/null +++ b/target-info-stub.c @@ -0,0 +1,16 @@ +/* + * QEMU target info stubs (target specific) + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/target-info.h" +#include "cpu.h" + +const char *target_cpu_type(void) +{ + return CPU_RESOLVING_TYPE; +} From 5e15bb7d66d63cbcd863b6b3f69d3fa6715fb75c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 17:42:37 +0100 Subject: [PATCH 0348/2760] cpus: Replace CPU_RESOLVING_TYPE -> target_cpu_type() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the target-specific CPU_RESOLVING_TYPE definition by a call to the target-agnostic target_cpu_type() runtime helper. Since the big "cpu.h" is not required anymore in tcg-all.c, remove it, using the tinier "cpu-param.h" header. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-Id: <20250417165430.58213-3-philmd@linaro.org> --- accel/accel-target.c | 6 ++++-- accel/tcg/tcg-all.c | 5 +++-- cpu-target.c | 7 ++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/accel/accel-target.c b/accel/accel-target.c index 33a539b4cb..08d4e450bd 100644 --- a/accel/accel-target.c +++ b/accel/accel-target.c @@ -25,6 +25,7 @@ #include "qemu/osdep.h" #include "qemu/accel.h" +#include "qemu/target-info.h" #include "cpu.h" #include "accel/accel-cpu-target.h" @@ -88,17 +89,18 @@ static void accel_init_cpu_interfaces(AccelClass *ac) const char *ac_name; /* AccelClass name */ char *acc_name; /* AccelCPUClass name */ ObjectClass *acc; /* AccelCPUClass */ + const char *cpu_resolving_type = target_cpu_type(); ac_name = object_class_get_name(OBJECT_CLASS(ac)); g_assert(ac_name != NULL); - acc_name = g_strdup_printf("%s-%s", ac_name, CPU_RESOLVING_TYPE); + acc_name = g_strdup_printf("%s-%s", ac_name, cpu_resolving_type); acc = object_class_by_name(acc_name); g_free(acc_name); if (acc) { object_class_foreach(accel_init_cpu_int_aux, - CPU_RESOLVING_TYPE, false, acc); + cpu_resolving_type, false, acc); } } diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 40d7364979..0ce34ac912 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -35,6 +35,7 @@ #include "qapi/qapi-types-common.h" #include "qapi/qapi-builtin-visit.h" #include "qemu/units.h" +#include "qemu/target-info.h" #if defined(CONFIG_USER_ONLY) #include "hw/qdev-core.h" #else @@ -43,7 +44,7 @@ #endif #include "accel/tcg/cpu-ops.h" #include "internal-common.h" -#include "cpu.h" +#include "cpu-param.h" struct TCGState { @@ -89,7 +90,7 @@ static int tcg_init_machine(MachineState *ms) unsigned max_threads = 1; #ifndef CONFIG_USER_ONLY - CPUClass *cc = CPU_CLASS(object_class_by_name(CPU_RESOLVING_TYPE)); + CPUClass *cc = CPU_CLASS(object_class_by_name(target_cpu_type())); bool mttcg_supported = cc->tcg_ops->mttcg_supported; switch (s->mttcg_enabled) { diff --git a/cpu-target.c b/cpu-target.c index d68cbab5da..c2dd590d48 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -22,6 +22,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" +#include "qemu/target-info.h" #include "system/accel-ops.h" #include "system/cpus.h" #include "exec/cpu-common.h" @@ -37,7 +38,7 @@ QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); char *cpu_model_from_type(const char *typename) { - const char *suffix = "-" CPU_RESOLVING_TYPE; + g_autofree char *suffix = g_strdup_printf("-%s", target_cpu_type()); if (!object_class_by_name(typename)) { return NULL; @@ -63,7 +64,7 @@ const char *parse_cpu_option(const char *cpu_option) exit(1); } - oc = cpu_class_by_name(CPU_RESOLVING_TYPE, model_pieces[0]); + oc = cpu_class_by_name(target_cpu_type(), model_pieces[0]); if (oc == NULL) { error_report("unable to find CPU model '%s'", model_pieces[0]); g_strfreev(model_pieces); @@ -92,7 +93,7 @@ static void cpu_list_entry(gpointer data, gpointer user_data) void list_cpus(void) { - CPUClass *cc = CPU_CLASS(object_class_by_name(CPU_RESOLVING_TYPE)); + CPUClass *cc = CPU_CLASS(object_class_by_name(target_cpu_type())); if (cc->list_cpus) { cc->list_cpus(); From 2492008d0de0380ee910730dc10159a8e29b13a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 2 Apr 2025 05:32:03 +0200 Subject: [PATCH 0349/2760] cpus: Move target-agnostic methods out of cpu-target.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Various methods of cpu-target.c don't use any target-specific knowledge at all and can be built once in the target-agnostic cpu-common.c file. Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250417165430.58213-4-philmd@linaro.org> --- cpu-target.c | 77 +------------------------------------------- hw/core/cpu-common.c | 74 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 76 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index c2dd590d48..b5645ff0db 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -19,94 +19,19 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "qemu/qemu-print.h" -#include "qemu/target-info.h" #include "system/accel-ops.h" #include "system/cpus.h" #include "exec/cpu-common.h" #include "exec/tswap.h" #include "exec/replay-core.h" #include "exec/log.h" -#include "accel/accel-cpu-target.h" +#include "hw/core/cpu.h" #include "trace/trace-root.h" /* Validate correct placement of CPUArchState. */ QEMU_BUILD_BUG_ON(offsetof(ArchCPU, parent_obj) != 0); QEMU_BUILD_BUG_ON(offsetof(ArchCPU, env) != sizeof(CPUState)); -char *cpu_model_from_type(const char *typename) -{ - g_autofree char *suffix = g_strdup_printf("-%s", target_cpu_type()); - - if (!object_class_by_name(typename)) { - return NULL; - } - - if (g_str_has_suffix(typename, suffix)) { - return g_strndup(typename, strlen(typename) - strlen(suffix)); - } - - return g_strdup(typename); -} - -const char *parse_cpu_option(const char *cpu_option) -{ - ObjectClass *oc; - CPUClass *cc; - gchar **model_pieces; - const char *cpu_type; - - model_pieces = g_strsplit(cpu_option, ",", 2); - if (!model_pieces[0]) { - error_report("-cpu option cannot be empty"); - exit(1); - } - - oc = cpu_class_by_name(target_cpu_type(), model_pieces[0]); - if (oc == NULL) { - error_report("unable to find CPU model '%s'", model_pieces[0]); - g_strfreev(model_pieces); - exit(EXIT_FAILURE); - } - - cpu_type = object_class_get_name(oc); - cc = CPU_CLASS(oc); - cc->parse_features(cpu_type, model_pieces[1], &error_fatal); - g_strfreev(model_pieces); - return cpu_type; -} - -static void cpu_list_entry(gpointer data, gpointer user_data) -{ - CPUClass *cc = CPU_CLASS(OBJECT_CLASS(data)); - const char *typename = object_class_get_name(OBJECT_CLASS(data)); - g_autofree char *model = cpu_model_from_type(typename); - - if (cc->deprecation_note) { - qemu_printf(" %s (deprecated)\n", model); - } else { - qemu_printf(" %s\n", model); - } -} - -void list_cpus(void) -{ - CPUClass *cc = CPU_CLASS(object_class_by_name(target_cpu_type())); - - if (cc->list_cpus) { - cc->list_cpus(); - } else { - GSList *list; - - list = object_class_get_list_sorted(TYPE_CPU, false); - qemu_printf("Available CPUs:\n"); - g_slist_foreach(list, cpu_list_entry, NULL); - g_slist_free(list); - } -} - /* enable or disable single step mode. EXCP_DEBUG is returned by the CPU loop after each instruction */ void cpu_single_step(CPUState *cpu, int enabled) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 1fb6ea3892..92c40b6bf8 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -25,6 +25,9 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/lockcnt.h" +#include "qemu/error-report.h" +#include "qemu/qemu-print.h" +#include "qemu/target-info.h" #include "exec/log.h" #include "exec/gdbstub.h" #include "system/tcg.h" @@ -152,6 +155,21 @@ ObjectClass *cpu_class_by_name(const char *typename, const char *cpu_model) return NULL; } +char *cpu_model_from_type(const char *typename) +{ + g_autofree char *suffix = g_strdup_printf("-%s", target_cpu_type()); + + if (!object_class_by_name(typename)) { + return NULL; + } + + if (g_str_has_suffix(typename, suffix)) { + return g_strndup(typename, strlen(typename) - strlen(suffix)); + } + + return g_strdup(typename); +} + static void cpu_common_parse_features(const char *typename, char *features, Error **errp) { @@ -183,6 +201,33 @@ static void cpu_common_parse_features(const char *typename, char *features, } } +const char *parse_cpu_option(const char *cpu_option) +{ + ObjectClass *oc; + CPUClass *cc; + gchar **model_pieces; + const char *cpu_type; + + model_pieces = g_strsplit(cpu_option, ",", 2); + if (!model_pieces[0]) { + error_report("-cpu option cannot be empty"); + exit(1); + } + + oc = cpu_class_by_name(target_cpu_type(), model_pieces[0]); + if (oc == NULL) { + error_report("unable to find CPU model '%s'", model_pieces[0]); + g_strfreev(model_pieces); + exit(EXIT_FAILURE); + } + + cpu_type = object_class_get_name(oc); + cc = CPU_CLASS(oc); + cc->parse_features(cpu_type, model_pieces[1], &error_fatal); + g_strfreev(model_pieces); + return cpu_type; +} + bool cpu_exec_realizefn(CPUState *cpu, Error **errp) { if (!accel_cpu_common_realize(cpu, errp)) { @@ -359,3 +404,32 @@ static void cpu_register_types(void) } type_init(cpu_register_types) + +static void cpu_list_entry(gpointer data, gpointer user_data) +{ + CPUClass *cc = CPU_CLASS(OBJECT_CLASS(data)); + const char *typename = object_class_get_name(OBJECT_CLASS(data)); + g_autofree char *model = cpu_model_from_type(typename); + + if (cc->deprecation_note) { + qemu_printf(" %s (deprecated)\n", model); + } else { + qemu_printf(" %s\n", model); + } +} + +void list_cpus(void) +{ + CPUClass *cc = CPU_CLASS(object_class_by_name(target_cpu_type())); + + if (cc->list_cpus) { + cc->list_cpus(); + } else { + GSList *list; + + list = object_class_get_list_sorted(TYPE_CPU, false); + qemu_printf("Available CPUs:\n"); + g_slist_foreach(list, cpu_list_entry, NULL); + g_slist_free(list); + } +} From 64cbcf1d9782c340cf59868a1be97f0054c4ec44 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 18:17:15 +0100 Subject: [PATCH 0350/2760] accel: Implement accel_init_ops_interfaces() for both system/user mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to build more common code, moving objects from meson's specific_ss[] set to common_ss[]. Since the CONFIG_USER_ONLY definitions isn't applied on the common_ss[] set, it is simpler to add an empty accel_init_ops_interfaces() stub on user emulation, removing any CONFIG_USER_ONLY use in accel-target.c. Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250417165430.58213-5-philmd@linaro.org> --- MAINTAINERS | 2 +- accel/{accel-system.h => accel-internal.h} | 8 ++++---- accel/accel-system.c | 4 ++-- accel/accel-target.c | 10 ++-------- accel/accel-user.c | 6 ++++++ 5 files changed, 15 insertions(+), 15 deletions(-) rename accel/{accel-system.h => accel-internal.h} (56%) diff --git a/MAINTAINERS b/MAINTAINERS index 28b1e9ba44..07711cfd38 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -500,7 +500,7 @@ F: include/qemu/target-info*.h F: include/system/accel-*.h F: include/system/cpus.h F: include/accel/accel-cpu-target.h -F: accel/accel-*.c +F: accel/accel-*.? F: accel/Makefile.objs F: accel/stubs/Makefile.objs F: cpu-common.c diff --git a/accel/accel-system.h b/accel/accel-internal.h similarity index 56% rename from accel/accel-system.h rename to accel/accel-internal.h index 2d37c73c97..03426aa21e 100644 --- a/accel/accel-system.h +++ b/accel/accel-internal.h @@ -1,5 +1,5 @@ /* - * QEMU System Emulation accel internal functions + * QEMU accel internal functions * * Copyright 2021 SUSE LLC * @@ -7,9 +7,9 @@ * See the COPYING file in the top-level directory. */ -#ifndef ACCEL_SYSTEM_H -#define ACCEL_SYSTEM_H +#ifndef ACCEL_INTERNAL_H +#define ACCEL_INTERNAL_H -void accel_system_init_ops_interfaces(AccelClass *ac); +void accel_init_ops_interfaces(AccelClass *ac); #endif /* ACCEL_SYSTEM_H */ diff --git a/accel/accel-system.c b/accel/accel-system.c index 5df49fbe83..a0f562ae9f 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -29,7 +29,7 @@ #include "system/accel-ops.h" #include "system/cpus.h" #include "qemu/error-report.h" -#include "accel-system.h" +#include "accel-internal.h" int accel_init_machine(AccelState *accel, MachineState *ms) { @@ -63,7 +63,7 @@ void accel_setup_post(MachineState *ms) } /* initialize the arch-independent accel operation interfaces */ -void accel_system_init_ops_interfaces(AccelClass *ac) +void accel_init_ops_interfaces(AccelClass *ac) { const char *ac_name; char *ops_name; diff --git a/accel/accel-target.c b/accel/accel-target.c index 08d4e450bd..7f3bbf31a8 100644 --- a/accel/accel-target.c +++ b/accel/accel-target.c @@ -29,10 +29,7 @@ #include "cpu.h" #include "accel/accel-cpu-target.h" - -#ifndef CONFIG_USER_ONLY -#include "accel-system.h" -#endif /* !CONFIG_USER_ONLY */ +#include "accel-internal.h" static const TypeInfo accel_type = { .name = TYPE_ACCEL, @@ -106,10 +103,7 @@ static void accel_init_cpu_interfaces(AccelClass *ac) void accel_init_interfaces(AccelClass *ac) { -#ifndef CONFIG_USER_ONLY - accel_system_init_ops_interfaces(ac); -#endif /* !CONFIG_USER_ONLY */ - + accel_init_ops_interfaces(ac); accel_init_cpu_interfaces(ac); } diff --git a/accel/accel-user.c b/accel/accel-user.c index 22b6a1a1a8..7d192306a6 100644 --- a/accel/accel-user.c +++ b/accel/accel-user.c @@ -9,6 +9,12 @@ #include "qemu/osdep.h" #include "qemu/accel.h" +#include "accel-internal.h" + +void accel_init_ops_interfaces(AccelClass *ac) +{ + /* nothing */ +} AccelState *current_accel(void) { From 559fe89916a3e000317ba7fa8ca495c620a17beb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 18:29:31 +0100 Subject: [PATCH 0351/2760] accel: Include missing 'qemu/accel.h' header in accel-internal.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "qemu/accel.h" header is implicitly pulled in. Include it explicitly in order to avoid when refactoring unrelated headers: accel/accel-internal.h:13:32: error: unknown type name 'AccelClass' 13 | void accel_init_ops_interfaces(AccelClass *ac); | ^ Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250417165430.58213-6-philmd@linaro.org> --- accel/accel-internal.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/accel/accel-internal.h b/accel/accel-internal.h index 03426aa21e..d3a4422cbf 100644 --- a/accel/accel-internal.h +++ b/accel/accel-internal.h @@ -10,6 +10,8 @@ #ifndef ACCEL_INTERNAL_H #define ACCEL_INTERNAL_H +#include "qemu/accel.h" + void accel_init_ops_interfaces(AccelClass *ac); #endif /* ACCEL_SYSTEM_H */ From 44246e717018b30ee40db45fe2dd34765df61c7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 18:18:24 +0100 Subject: [PATCH 0352/2760] accel: Make AccelCPUClass structure target-agnostic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the target-agnostic parts of "accel/accel-cpu-target.h" to "accel/accel-cpu.h". Doing so we need to include missing "hw/core/cpu.h" header in "accel/accel-cpu.h" otherwise we get: include/accel/accel-cpu-target.h:39:28: error: unknown type name 'CPUClass' 39 | void (*cpu_class_init)(CPUClass *cc); | ^ Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250417165430.58213-7-philmd@linaro.org> --- MAINTAINERS | 2 +- accel/accel-target.c | 1 - include/accel/accel-cpu-target.h | 12 +----------- include/accel/accel-cpu.h | 23 +++++++++++++++++++++++ 4 files changed, 25 insertions(+), 13 deletions(-) create mode 100644 include/accel/accel-cpu.h diff --git a/MAINTAINERS b/MAINTAINERS index 07711cfd38..59d9712819 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -499,7 +499,7 @@ F: include/qemu/accel.h F: include/qemu/target-info*.h F: include/system/accel-*.h F: include/system/cpus.h -F: include/accel/accel-cpu-target.h +F: include/accel/accel-cpu*.h F: accel/accel-*.? F: accel/Makefile.objs F: accel/stubs/Makefile.objs diff --git a/accel/accel-target.c b/accel/accel-target.c index 7f3bbf31a8..82a29e6147 100644 --- a/accel/accel-target.c +++ b/accel/accel-target.c @@ -27,7 +27,6 @@ #include "qemu/accel.h" #include "qemu/target-info.h" -#include "cpu.h" #include "accel/accel-cpu-target.h" #include "accel-internal.h" diff --git a/include/accel/accel-cpu-target.h b/include/accel/accel-cpu-target.h index 37dde7fae3..6feb344e29 100644 --- a/include/accel/accel-cpu-target.h +++ b/include/accel/accel-cpu-target.h @@ -21,21 +21,11 @@ */ #include "qom/object.h" +#include "accel/accel-cpu.h" #include "cpu.h" #define TYPE_ACCEL_CPU "accel-" CPU_RESOLVING_TYPE #define ACCEL_CPU_NAME(name) (name "-" TYPE_ACCEL_CPU) -typedef struct AccelCPUClass AccelCPUClass; DECLARE_CLASS_CHECKERS(AccelCPUClass, ACCEL_CPU, TYPE_ACCEL_CPU) -typedef struct AccelCPUClass { - /*< private >*/ - ObjectClass parent_class; - /*< public >*/ - - void (*cpu_class_init)(CPUClass *cc); - void (*cpu_instance_init)(CPUState *cpu); - bool (*cpu_target_realize)(CPUState *cpu, Error **errp); -} AccelCPUClass; - #endif /* ACCEL_CPU_H */ diff --git a/include/accel/accel-cpu.h b/include/accel/accel-cpu.h new file mode 100644 index 0000000000..9e7eede7c3 --- /dev/null +++ b/include/accel/accel-cpu.h @@ -0,0 +1,23 @@ +/* + * Accelerator interface, specializes CPUClass + * + * Copyright 2021 SUSE LLC + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ACCEL_CPU_H +#define ACCEL_CPU_H + +#include "qom/object.h" +#include "hw/core/cpu.h" + +typedef struct AccelCPUClass { + ObjectClass parent_class; + + void (*cpu_class_init)(CPUClass *cc); + void (*cpu_instance_init)(CPUState *cpu); + bool (*cpu_target_realize)(CPUState *cpu, Error **errp); +} AccelCPUClass; + +#endif /* ACCEL_CPU_H */ From 802c4daf331339085823f2fc81917967da4b4c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 18:20:40 +0100 Subject: [PATCH 0353/2760] accel: Move target-agnostic code from accel-target.c -> accel-common.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Various methods of accel-target.c don't use any target-specific knowledge at all and can be built once in the target-agnostic accel-common.c file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-Id: <20250417165430.58213-8-philmd@linaro.org> --- accel/accel-common.c | 142 +++++++++++++++++++++++++++++++++++++++++++ accel/accel-target.c | 129 --------------------------------------- accel/meson.build | 1 + 3 files changed, 143 insertions(+), 129 deletions(-) create mode 100644 accel/accel-common.c diff --git a/accel/accel-common.c b/accel/accel-common.c new file mode 100644 index 0000000000..4894b98d64 --- /dev/null +++ b/accel/accel-common.c @@ -0,0 +1,142 @@ +/* + * QEMU accel class, components common to system emulation and user mode + * + * Copyright (c) 2003-2008 Fabrice Bellard + * Copyright (c) 2014 Red Hat Inc. + * + * SPDX-License-Identifier: MIT + */ + +#include "qemu/osdep.h" +#include "qemu/accel.h" +#include "qemu/target-info.h" +#include "accel/accel-cpu.h" +#include "accel-internal.h" + +/* Lookup AccelClass from opt_name. Returns NULL if not found */ +AccelClass *accel_find(const char *opt_name) +{ + char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name); + AccelClass *ac = ACCEL_CLASS(module_object_class_by_name(class_name)); + g_free(class_name); + return ac; +} + +/* Return the name of the current accelerator */ +const char *current_accel_name(void) +{ + AccelClass *ac = ACCEL_GET_CLASS(current_accel()); + + return ac->name; +} + +static void accel_init_cpu_int_aux(ObjectClass *klass, void *opaque) +{ + CPUClass *cc = CPU_CLASS(klass); + AccelCPUClass *accel_cpu = opaque; + + /* + * The first callback allows accel-cpu to run initializations + * for the CPU, customizing CPU behavior according to the accelerator. + * + * The second one allows the CPU to customize the accel-cpu + * behavior according to the CPU. + * + * The second is currently only used by TCG, to specialize the + * TCGCPUOps depending on the CPU type. + */ + cc->accel_cpu = accel_cpu; + if (accel_cpu->cpu_class_init) { + accel_cpu->cpu_class_init(cc); + } + if (cc->init_accel_cpu) { + cc->init_accel_cpu(accel_cpu, cc); + } +} + +/* initialize the arch-specific accel CpuClass interfaces */ +static void accel_init_cpu_interfaces(AccelClass *ac) +{ + const char *ac_name; /* AccelClass name */ + char *acc_name; /* AccelCPUClass name */ + ObjectClass *acc; /* AccelCPUClass */ + const char *cpu_resolving_type = target_cpu_type(); + + ac_name = object_class_get_name(OBJECT_CLASS(ac)); + g_assert(ac_name != NULL); + + acc_name = g_strdup_printf("%s-%s", ac_name, cpu_resolving_type); + acc = object_class_by_name(acc_name); + g_free(acc_name); + + if (acc) { + object_class_foreach(accel_init_cpu_int_aux, + cpu_resolving_type, false, acc); + } +} + +void accel_init_interfaces(AccelClass *ac) +{ + accel_init_ops_interfaces(ac); + accel_init_cpu_interfaces(ac); +} + +void accel_cpu_instance_init(CPUState *cpu) +{ + if (cpu->cc->accel_cpu && cpu->cc->accel_cpu->cpu_instance_init) { + cpu->cc->accel_cpu->cpu_instance_init(cpu); + } +} + +bool accel_cpu_common_realize(CPUState *cpu, Error **errp) +{ + AccelState *accel = current_accel(); + AccelClass *acc = ACCEL_GET_CLASS(accel); + + /* target specific realization */ + if (cpu->cc->accel_cpu + && cpu->cc->accel_cpu->cpu_target_realize + && !cpu->cc->accel_cpu->cpu_target_realize(cpu, errp)) { + return false; + } + + /* generic realization */ + if (acc->cpu_common_realize && !acc->cpu_common_realize(cpu, errp)) { + return false; + } + + return true; +} + +void accel_cpu_common_unrealize(CPUState *cpu) +{ + AccelState *accel = current_accel(); + AccelClass *acc = ACCEL_GET_CLASS(accel); + + /* generic unrealization */ + if (acc->cpu_common_unrealize) { + acc->cpu_common_unrealize(cpu); + } +} + +int accel_supported_gdbstub_sstep_flags(void) +{ + AccelState *accel = current_accel(); + AccelClass *acc = ACCEL_GET_CLASS(accel); + if (acc->gdbstub_supported_sstep_flags) { + return acc->gdbstub_supported_sstep_flags(); + } + return 0; +} + +static const TypeInfo accel_types[] = { + { + .name = TYPE_ACCEL, + .parent = TYPE_OBJECT, + .class_size = sizeof(AccelClass), + .instance_size = sizeof(AccelState), + .abstract = true, + }, +}; + +DEFINE_TYPES(accel_types) diff --git a/accel/accel-target.c b/accel/accel-target.c index 82a29e6147..7fd392fbc4 100644 --- a/accel/accel-target.c +++ b/accel/accel-target.c @@ -24,135 +24,7 @@ */ #include "qemu/osdep.h" -#include "qemu/accel.h" -#include "qemu/target-info.h" - #include "accel/accel-cpu-target.h" -#include "accel-internal.h" - -static const TypeInfo accel_type = { - .name = TYPE_ACCEL, - .parent = TYPE_OBJECT, - .class_size = sizeof(AccelClass), - .instance_size = sizeof(AccelState), - .abstract = true, -}; - -/* Lookup AccelClass from opt_name. Returns NULL if not found */ -AccelClass *accel_find(const char *opt_name) -{ - char *class_name = g_strdup_printf(ACCEL_CLASS_NAME("%s"), opt_name); - AccelClass *ac = ACCEL_CLASS(module_object_class_by_name(class_name)); - g_free(class_name); - return ac; -} - -/* Return the name of the current accelerator */ -const char *current_accel_name(void) -{ - AccelClass *ac = ACCEL_GET_CLASS(current_accel()); - - return ac->name; -} - -static void accel_init_cpu_int_aux(ObjectClass *klass, void *opaque) -{ - CPUClass *cc = CPU_CLASS(klass); - AccelCPUClass *accel_cpu = opaque; - - /* - * The first callback allows accel-cpu to run initializations - * for the CPU, customizing CPU behavior according to the accelerator. - * - * The second one allows the CPU to customize the accel-cpu - * behavior according to the CPU. - * - * The second is currently only used by TCG, to specialize the - * TCGCPUOps depending on the CPU type. - */ - cc->accel_cpu = accel_cpu; - if (accel_cpu->cpu_class_init) { - accel_cpu->cpu_class_init(cc); - } - if (cc->init_accel_cpu) { - cc->init_accel_cpu(accel_cpu, cc); - } -} - -/* initialize the arch-specific accel CpuClass interfaces */ -static void accel_init_cpu_interfaces(AccelClass *ac) -{ - const char *ac_name; /* AccelClass name */ - char *acc_name; /* AccelCPUClass name */ - ObjectClass *acc; /* AccelCPUClass */ - const char *cpu_resolving_type = target_cpu_type(); - - ac_name = object_class_get_name(OBJECT_CLASS(ac)); - g_assert(ac_name != NULL); - - acc_name = g_strdup_printf("%s-%s", ac_name, cpu_resolving_type); - acc = object_class_by_name(acc_name); - g_free(acc_name); - - if (acc) { - object_class_foreach(accel_init_cpu_int_aux, - cpu_resolving_type, false, acc); - } -} - -void accel_init_interfaces(AccelClass *ac) -{ - accel_init_ops_interfaces(ac); - accel_init_cpu_interfaces(ac); -} - -void accel_cpu_instance_init(CPUState *cpu) -{ - if (cpu->cc->accel_cpu && cpu->cc->accel_cpu->cpu_instance_init) { - cpu->cc->accel_cpu->cpu_instance_init(cpu); - } -} - -bool accel_cpu_common_realize(CPUState *cpu, Error **errp) -{ - AccelState *accel = current_accel(); - AccelClass *acc = ACCEL_GET_CLASS(accel); - - /* target specific realization */ - if (cpu->cc->accel_cpu - && cpu->cc->accel_cpu->cpu_target_realize - && !cpu->cc->accel_cpu->cpu_target_realize(cpu, errp)) { - return false; - } - - /* generic realization */ - if (acc->cpu_common_realize && !acc->cpu_common_realize(cpu, errp)) { - return false; - } - - return true; -} - -void accel_cpu_common_unrealize(CPUState *cpu) -{ - AccelState *accel = current_accel(); - AccelClass *acc = ACCEL_GET_CLASS(accel); - - /* generic unrealization */ - if (acc->cpu_common_unrealize) { - acc->cpu_common_unrealize(cpu); - } -} - -int accel_supported_gdbstub_sstep_flags(void) -{ - AccelState *accel = current_accel(); - AccelClass *acc = ACCEL_GET_CLASS(accel); - if (acc->gdbstub_supported_sstep_flags) { - return acc->gdbstub_supported_sstep_flags(); - } - return 0; -} static const TypeInfo accel_cpu_type = { .name = TYPE_ACCEL_CPU, @@ -163,7 +35,6 @@ static const TypeInfo accel_cpu_type = { static void register_accel_types(void) { - type_register_static(&accel_type); type_register_static(&accel_cpu_type); } diff --git a/accel/meson.build b/accel/meson.build index 5eaeb68338..52909314bf 100644 --- a/accel/meson.build +++ b/accel/meson.build @@ -1,3 +1,4 @@ +common_ss.add(files('accel-common.c')) specific_ss.add(files('accel-target.c')) system_ss.add(files('accel-system.c', 'accel-blocker.c')) user_ss.add(files('accel-user.c')) From 3d881164d4fb2b0f6791cf28d9725926b8ded0d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 12:47:37 +0100 Subject: [PATCH 0354/2760] qemu: Convert target_name() to TargetInfo API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have target_name() be a target-agnostic method, dispatching to a per-target TargetInfo singleton structure. By default a stub singleton is used. No logical change expected. Inspired-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20250424222112.36194-3-philmd@linaro.org> --- MAINTAINERS | 9 +++++++-- cpu-target.c | 5 ----- hw/core/machine-qmp-cmds.c | 1 + include/hw/core/cpu.h | 2 -- include/qemu/target-info-impl.h | 26 ++++++++++++++++++++++++++ include/qemu/target-info.h | 7 +++++++ meson.build | 1 + plugins/loader.c | 2 +- system/vl.c | 2 +- target-info-stub.c | 10 ++++++++++ target-info.c | 16 ++++++++++++++++ 11 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 include/qemu/target-info-impl.h create mode 100644 target-info.c diff --git a/MAINTAINERS b/MAINTAINERS index 59d9712819..f8fee87c70 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -496,7 +496,6 @@ F: include/exec/cpu*.h F: include/exec/exec-all.h F: include/exec/target_long.h F: include/qemu/accel.h -F: include/qemu/target-info*.h F: include/system/accel-*.h F: include/system/cpus.h F: include/accel/accel-cpu*.h @@ -505,7 +504,6 @@ F: accel/Makefile.objs F: accel/stubs/Makefile.objs F: cpu-common.c F: cpu-target.c -F: target-info*.c F: system/cpus.c Apple Silicon HVF CPUs @@ -1928,6 +1926,13 @@ F: tests/functional/test_empty_cpu_model.py F: tests/unit/test-smp-parse.c T: git https://gitlab.com/ehabkost/qemu.git machine-next +TargetInfo API +M: Pierrick Bouvier +M: Philippe Mathieu-Daudé +S: Supported +F: include/qemu/target-info*.h +F: target-info*.c + Xtensa Machines --------------- sim diff --git a/cpu-target.c b/cpu-target.c index b5645ff0db..1c90a30759 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -91,8 +91,3 @@ bool target_big_endian(void) { return TARGET_BIG_ENDIAN; } - -const char *target_name(void) -{ - return TARGET_NAME; -} diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index a5e635152d..d82043e1c6 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -19,6 +19,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/type-helpers.h" #include "qemu/uuid.h" +#include "qemu/target-info.h" #include "qom/qom-qobject.h" #include "system/hostmem.h" #include "system/hw_accel.h" diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 2a02d4f078..12b2ff1f7d 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1121,8 +1121,6 @@ bool cpu_exec_realizefn(CPUState *cpu, Error **errp); void cpu_exec_unrealizefn(CPUState *cpu); void cpu_exec_reset_hold(CPUState *cpu); -const char *target_name(void); - #ifdef COMPILING_PER_TARGET extern const VMStateDescription vmstate_cpu_common; diff --git a/include/qemu/target-info-impl.h b/include/qemu/target-info-impl.h new file mode 100644 index 0000000000..d30805f7f2 --- /dev/null +++ b/include/qemu/target-info-impl.h @@ -0,0 +1,26 @@ +/* + * QEMU TargetInfo structure definition + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_TARGET_INFO_IMPL_H +#define QEMU_TARGET_INFO_IMPL_H + +#include "qemu/target-info.h" + +typedef struct TargetInfo { + /* runtime equivalent of TARGET_NAME definition */ + const char *target_name; +} TargetInfo; + +/** + * target_info: + * + * Returns: The TargetInfo structure definition for this target binary. + */ +const TargetInfo *target_info(void); + +#endif diff --git a/include/qemu/target-info.h b/include/qemu/target-info.h index b4cc4888ca..58d4136897 100644 --- a/include/qemu/target-info.h +++ b/include/qemu/target-info.h @@ -9,6 +9,13 @@ #ifndef QEMU_TARGET_INFO_H #define QEMU_TARGET_INFO_H +/** + * target_name: + * + * Returns: Canonical target name (i.e. "i386"). + */ +const char *target_name(void); + /** * target_cpu_type: * diff --git a/meson.build b/meson.build index 185c2fb0d1..8ae70dbe45 100644 --- a/meson.build +++ b/meson.build @@ -3795,6 +3795,7 @@ endif common_ss.add(pagevary) specific_ss.add(files('page-target.c', 'page-vary-target.c')) +common_ss.add(files('target-info.c')) specific_ss.add(files('target-info-stub.c')) subdir('backends') diff --git a/plugins/loader.c b/plugins/loader.c index 0d6e082e17..8f0d75c904 100644 --- a/plugins/loader.c +++ b/plugins/loader.c @@ -29,7 +29,7 @@ #include "qemu/xxhash.h" #include "qemu/plugin.h" #include "qemu/memalign.h" -#include "hw/core/cpu.h" +#include "qemu/target-info.h" #include "exec/tb-flush.h" #include "plugin.h" diff --git a/system/vl.c b/system/vl.c index 4ab2001df7..520956f4a1 100644 --- a/system/vl.c +++ b/system/vl.c @@ -40,6 +40,7 @@ #include "qemu/help_option.h" #include "qemu/hw-version.h" #include "qemu/uuid.h" +#include "qemu/target-info.h" #include "system/reset.h" #include "system/runstate.h" #include "system/runstate-action.h" @@ -79,7 +80,6 @@ #include "hw/block/block.h" #include "hw/i386/x86.h" #include "hw/i386/pc.h" -#include "hw/core/cpu.h" #include "migration/cpr.h" #include "migration/misc.h" #include "migration/snapshot.h" diff --git a/target-info-stub.c b/target-info-stub.c index e5d2195e89..773a10188c 100644 --- a/target-info-stub.c +++ b/target-info-stub.c @@ -8,8 +8,18 @@ #include "qemu/osdep.h" #include "qemu/target-info.h" +#include "qemu/target-info-impl.h" #include "cpu.h" +static const TargetInfo target_info_stub = { + .target_name = TARGET_NAME, +}; + +const TargetInfo *target_info(void) +{ + return &target_info_stub; +} + const char *target_cpu_type(void) { return CPU_RESOLVING_TYPE; diff --git a/target-info.c b/target-info.c new file mode 100644 index 0000000000..84b18931e7 --- /dev/null +++ b/target-info.c @@ -0,0 +1,16 @@ +/* + * QEMU target info helpers + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/target-info.h" +#include "qemu/target-info-impl.h" + +const char *target_name(void) +{ + return target_info()->target_name; +} From 2b689db0bedd24eda8b491cb1fcfb015dfec5a31 Mon Sep 17 00:00:00 2001 From: Denis Rastyogin Date: Thu, 27 Mar 2025 19:24:23 +0300 Subject: [PATCH 0355/2760] qemu-img: improve queue depth validation in img_bench This error was discovered by fuzzing qemu-img. Currently, running `qemu-img bench -d 0` in img_bench is allowed, which is a pointless operation and causes qemu-img to hang. Signed-off-by: Denis Rastyogin Message-ID: <20250327162423.25154-5-gerben@altlinux.org> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qemu-img.c b/qemu-img.c index 2044c22a4c..76ac5d3028 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -4571,7 +4571,7 @@ static int img_bench(int argc, char **argv) { unsigned long res; - if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) { + if (qemu_strtoul(optarg, NULL, 0, &res) <= 0 || res > INT_MAX) { error_report("Invalid queue depth specified"); return 1; } From a079836005fb92eb1fee19e59654b337a41fd0ff Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 24 Apr 2025 18:23:36 +0000 Subject: [PATCH 0356/2760] tcg/loongarch64: Fix vec_val computation in tcg_target_const_match MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only use vece for a vector constant. This avoids an assertion failure in sextract64 when vece contains garbage. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index cbd7642b58..740b7c264d 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -211,12 +211,14 @@ static bool tcg_target_const_match(int64_t val, int ct, if ((ct & TCG_CT_CONST_WSZ) && val == (type == TCG_TYPE_I32 ? 32 : 64)) { return true; } - int64_t vec_val = sextract64(val, 0, 8 << vece); - if ((ct & TCG_CT_CONST_VCMP) && -0x10 <= vec_val && vec_val <= 0x1f) { - return true; - } - if ((ct & TCG_CT_CONST_VADD) && -0x1f <= vec_val && vec_val <= 0x1f) { - return true; + if (ct & (TCG_CT_CONST_VCMP | TCG_CT_CONST_VADD)) { + int64_t vec_val = sextract64(val, 0, 8 << vece); + if ((ct & TCG_CT_CONST_VCMP) && -0x10 <= vec_val && vec_val <= 0x1f) { + return true; + } + if ((ct & TCG_CT_CONST_VADD) && -0x1f <= vec_val && vec_val <= 0x1f) { + return true; + } } return false; } From 911f7328e9e9de80bf6b1a4697ecf83cc3661f5f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 24 Apr 2025 18:45:28 +0000 Subject: [PATCH 0357/2760] tcg/loongarch64: Improve constraints for TCG_CT_CONST_VCMP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the TCGCond given to tcg_target_const_match to exactly match the supported constant. Adjust the code generation to assume this has been done -- recall that encode_*_insn contain assertions that the constants are valid. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 38 ++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 740b7c264d..879f66f255 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -213,8 +213,18 @@ static bool tcg_target_const_match(int64_t val, int ct, } if (ct & (TCG_CT_CONST_VCMP | TCG_CT_CONST_VADD)) { int64_t vec_val = sextract64(val, 0, 8 << vece); - if ((ct & TCG_CT_CONST_VCMP) && -0x10 <= vec_val && vec_val <= 0x1f) { - return true; + if (ct & TCG_CT_CONST_VCMP) { + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_LE: + case TCG_COND_LT: + return -0x10 <= vec_val && vec_val <= 0x0f; + case TCG_COND_LEU: + case TCG_COND_LTU: + return 0x00 <= vec_val && vec_val <= 0x1f; + default: + return false; + } } if ((ct & TCG_CT_CONST_VADD) && -0x1f <= vec_val && vec_val <= 0x1f) { return true; @@ -2029,28 +2039,22 @@ static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, * Try vseqi/vslei/vslti */ int64_t value = sextract64(a2, 0, 8 << vece); - if ((cond == TCG_COND_EQ || - cond == TCG_COND_LE || - cond == TCG_COND_LT) && - (-0x10 <= value && value <= 0x0f)) { + switch (cond) { + case TCG_COND_EQ: + case TCG_COND_LE: + case TCG_COND_LT: insn = cmp_vec_imm_insn[cond][lasx][vece]; tcg_out32(s, encode_vdvjsk5_insn(insn, a0, a1, value)); break; - } else if ((cond == TCG_COND_LEU || - cond == TCG_COND_LTU) && - (0x00 <= value && value <= 0x1f)) { + case TCG_COND_LEU: + case TCG_COND_LTU: insn = cmp_vec_imm_insn[cond][lasx][vece]; tcg_out32(s, encode_vdvjuk5_insn(insn, a0, a1, value)); break; + default: + g_assert_not_reached(); } - - /* - * Fallback to: - * dupi_vec temp, a2 - * cmp_vec a0, a1, temp, cond - */ - tcg_out_dupi_vec(s, type, vece, TCG_VEC_TMP0, a2); - a2 = TCG_VEC_TMP0; + break; } insn = cmp_vec_insn[cond][lasx][vece]; From a3c1c579baf511a2b95f7b233187a981b6ef46ea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 21 Apr 2025 11:05:29 -0700 Subject: [PATCH 0358/2760] tcg/optimize: Introduce opt_insert_{before,after} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consolidate the places we call tcg_op_insert_{before,after} within the optimization pass. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index f922f86a1d..a4d4ad3005 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -344,6 +344,18 @@ static TCGArg arg_new_temp(OptContext *ctx) return temp_arg(ts); } +static TCGOp *opt_insert_after(OptContext *ctx, TCGOp *op, + TCGOpcode opc, unsigned narg) +{ + return tcg_op_insert_after(ctx->tcg, op, opc, narg); +} + +static TCGOp *opt_insert_before(OptContext *ctx, TCGOp *op, + TCGOpcode opc, unsigned narg) +{ + return tcg_op_insert_before(ctx->tcg, op, opc, narg); +} + static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) { TCGTemp *dst_ts = arg_temp(dst); @@ -808,7 +820,7 @@ static int do_constant_folding_cond1(OptContext *ctx, TCGOp *op, TCGArg dest, if (!TCG_TARGET_HAS_tst) { TCGOpcode and_opc = (ctx->type == TCG_TYPE_I32 ? INDEX_op_and_i32 : INDEX_op_and_i64); - TCGOp *op2 = tcg_op_insert_before(ctx->tcg, op, and_opc, 3); + TCGOp *op2 = opt_insert_before(ctx, op, and_opc, 3); TCGArg tmp = arg_new_temp(ctx); op2->args[0] = tmp; @@ -901,8 +913,8 @@ static int do_constant_folding_cond2(OptContext *ctx, TCGOp *op, TCGArg *args) /* Expand to AND with a temporary if no backend support. */ if (!TCG_TARGET_HAS_tst && is_tst_cond(c)) { - TCGOp *op1 = tcg_op_insert_before(ctx->tcg, op, INDEX_op_and_i32, 3); - TCGOp *op2 = tcg_op_insert_before(ctx->tcg, op, INDEX_op_and_i32, 3); + TCGOp *op1 = opt_insert_before(ctx, op, INDEX_op_and_i32, 3); + TCGOp *op2 = opt_insert_before(ctx, op, INDEX_op_and_i32, 3); TCGArg t1 = arg_new_temp(ctx); TCGArg t2 = arg_new_temp(ctx); @@ -1263,7 +1275,7 @@ static bool fold_addsub2(OptContext *ctx, TCGOp *op, bool add) rh = op->args[1]; /* The proper opcode is supplied by tcg_opt_gen_mov. */ - op2 = tcg_op_insert_before(ctx->tcg, op, 0, 2); + op2 = opt_insert_before(ctx, op, 0, 2); tcg_opt_gen_movi(ctx, op, rl, al); tcg_opt_gen_movi(ctx, op2, rh, ah); @@ -2096,7 +2108,7 @@ static bool fold_multiply2(OptContext *ctx, TCGOp *op) rh = op->args[1]; /* The proper opcode is supplied by tcg_opt_gen_mov. */ - op2 = tcg_op_insert_before(ctx->tcg, op, 0, 2); + op2 = opt_insert_before(ctx, op, 0, 2); tcg_opt_gen_movi(ctx, op, rl, l); tcg_opt_gen_movi(ctx, op2, rh, h); @@ -2406,7 +2418,7 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) op->args[3] = 1; } else { if (sh) { - op2 = tcg_op_insert_before(ctx->tcg, op, shr_opc, 3); + op2 = opt_insert_before(ctx, op, shr_opc, 3); op2->args[0] = ret; op2->args[1] = src1; op2->args[2] = arg_new_constant(ctx, sh); @@ -2418,17 +2430,17 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) } if (neg && inv) { - op2 = tcg_op_insert_after(ctx->tcg, op, sub_opc, 3); + op2 = opt_insert_after(ctx, op, sub_opc, 3); op2->args[0] = ret; op2->args[1] = ret; op2->args[2] = arg_new_constant(ctx, 1); } else if (inv) { - op2 = tcg_op_insert_after(ctx->tcg, op, xor_opc, 3); + op2 = opt_insert_after(ctx, op, xor_opc, 3); op2->args[0] = ret; op2->args[1] = ret; op2->args[2] = arg_new_constant(ctx, 1); } else if (neg) { - op2 = tcg_op_insert_after(ctx->tcg, op, neg_opc, 2); + op2 = opt_insert_after(ctx, op, neg_opc, 2); op2->args[0] = ret; op2->args[1] = ret; } From cf5c9f697f2025880e8555467ddb6debc6349cd8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 21 Jan 2025 20:34:41 -0800 Subject: [PATCH 0359/2760] tcg: Add TCGType to tcg_op_insert_{after,before} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We cannot rely on the value copied from TCGOP_TYPE(op), because the relevant op could be typeless, such as INDEX_op_call. Fixes: fb744ece3a78 ("tcg: Copy TCGOP_TYPE in tcg_op_insert_{after,before}") Suggested-by: Nicholas Piggin Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 4 ++-- tcg/tcg-internal.h | 4 ++-- tcg/tcg.c | 17 ++++++++++------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index a4d4ad3005..3bd4ee4d58 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -347,13 +347,13 @@ static TCGArg arg_new_temp(OptContext *ctx) static TCGOp *opt_insert_after(OptContext *ctx, TCGOp *op, TCGOpcode opc, unsigned narg) { - return tcg_op_insert_after(ctx->tcg, op, opc, narg); + return tcg_op_insert_after(ctx->tcg, op, opc, ctx->type, narg); } static TCGOp *opt_insert_before(OptContext *ctx, TCGOp *op, TCGOpcode opc, unsigned narg) { - return tcg_op_insert_before(ctx->tcg, op, opc, narg); + return tcg_op_insert_before(ctx->tcg, op, opc, ctx->type, narg); } static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) diff --git a/tcg/tcg-internal.h b/tcg/tcg-internal.h index ff85fb23fa..d6a12afe06 100644 --- a/tcg/tcg-internal.h +++ b/tcg/tcg-internal.h @@ -107,8 +107,8 @@ void vec_gen_6(TCGOpcode opc, TCGType type, unsigned vece, TCGArg r, TCGArg a, TCGArg b, TCGArg c, TCGArg d, TCGArg e); TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *op, - TCGOpcode opc, unsigned nargs); + TCGOpcode, TCGType, unsigned nargs); TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *op, - TCGOpcode opc, unsigned nargs); + TCGOpcode, TCGType, unsigned nargs); #endif /* TCG_INTERNAL_H */ diff --git a/tcg/tcg.c b/tcg/tcg.c index ec7f6743d7..198d6181d9 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -3449,21 +3449,21 @@ TCGOp *tcg_emit_op(TCGOpcode opc, unsigned nargs) } TCGOp *tcg_op_insert_before(TCGContext *s, TCGOp *old_op, - TCGOpcode opc, unsigned nargs) + TCGOpcode opc, TCGType type, unsigned nargs) { TCGOp *new_op = tcg_op_alloc(opc, nargs); - TCGOP_TYPE(new_op) = TCGOP_TYPE(old_op); + TCGOP_TYPE(new_op) = type; QTAILQ_INSERT_BEFORE(old_op, new_op, link); return new_op; } TCGOp *tcg_op_insert_after(TCGContext *s, TCGOp *old_op, - TCGOpcode opc, unsigned nargs) + TCGOpcode opc, TCGType type, unsigned nargs) { TCGOp *new_op = tcg_op_alloc(opc, nargs); - TCGOP_TYPE(new_op) = TCGOP_TYPE(old_op); + TCGOP_TYPE(new_op) = type; QTAILQ_INSERT_AFTER(&s->ops, old_op, new_op, link); return new_op; } @@ -4214,7 +4214,8 @@ liveness_pass_2(TCGContext *s) TCGOpcode lopc = (arg_ts->type == TCG_TYPE_I32 ? INDEX_op_ld_i32 : INDEX_op_ld_i64); - TCGOp *lop = tcg_op_insert_before(s, op, lopc, 3); + TCGOp *lop = tcg_op_insert_before(s, op, lopc, + arg_ts->type, 3); lop->args[0] = temp_arg(dir_ts); lop->args[1] = temp_arg(arg_ts->mem_base); @@ -4277,7 +4278,8 @@ liveness_pass_2(TCGContext *s) TCGOpcode sopc = (arg_ts->type == TCG_TYPE_I32 ? INDEX_op_st_i32 : INDEX_op_st_i64); - TCGOp *sop = tcg_op_insert_after(s, op, sopc, 3); + TCGOp *sop = tcg_op_insert_after(s, op, sopc, + arg_ts->type, 3); TCGTemp *out_ts = dir_ts; if (IS_DEAD_ARG(0)) { @@ -4313,7 +4315,8 @@ liveness_pass_2(TCGContext *s) TCGOpcode sopc = (arg_ts->type == TCG_TYPE_I32 ? INDEX_op_st_i32 : INDEX_op_st_i64); - TCGOp *sop = tcg_op_insert_after(s, op, sopc, 3); + TCGOp *sop = tcg_op_insert_after(s, op, sopc, + arg_ts->type, 3); sop->args[0] = temp_arg(dir_ts); sop->args[1] = temp_arg(arg_ts->mem_base); From 5500bd9e2ec5ec85858fcca06c04a57337aa14c7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 3 Jan 2025 14:55:56 -0800 Subject: [PATCH 0360/2760] tcg: Add all_outop[] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add infrastructure for more consolidated output of opcodes. The base structure allows for constraints to be either static or dynamic, and for the existence of those constraints to replace TCG_TARGET_HAS_* and the bulk of tcg_op_supported. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tcg.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 68 insertions(+), 8 deletions(-) diff --git a/tcg/tcg.c b/tcg/tcg.c index 198d6181d9..5090cdb3c6 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -861,6 +861,7 @@ static int tcg_out_pool_finalize(TCGContext *s) #define C_N1_O1_I4(O1, O2, I1, I2, I3, I4) C_PFX6(c_n1_o1_i4_, O1, O2, I1, I2, I3, I4), typedef enum { + C_Dynamic = -2, C_NotImplemented = -1, #include "tcg-target-con-set.h" } TCGConstraintSetIndex; @@ -954,6 +955,29 @@ static const TCGConstraintSet constraint_sets[] = { #define C_O2_I4(O1, O2, I1, I2, I3, I4) C_PFX6(c_o2_i4_, O1, O2, I1, I2, I3, I4) #define C_N1_O1_I4(O1, O2, I1, I2, I3, I4) C_PFX6(c_n1_o1_i4_, O1, O2, I1, I2, I3, I4) +/* + * TCGOutOp is the base class for a set of structures that describe how + * to generate code for a given TCGOpcode. + * + * @static_constraint: + * C_NotImplemented: The TCGOpcode is not supported by the backend. + * C_Dynamic: Use @dynamic_constraint to select a constraint set + * based on any of @type, @flags, or host isa. + * Otherwise: The register allocation constrains for the TCGOpcode. + * + * Subclasses of TCGOutOp will define a set of output routines that may + * be used. Such routines will often be selected by the set of registers + * and constants that come out of register allocation. The set of + * routines that are provided will guide the set of constraints that are + * legal. In particular, assume that tcg_optimize() has done its job in + * swapping commutative operands and folding operations for which all + * operands are constant. + */ +typedef struct TCGOutOp { + TCGConstraintSetIndex static_constraint; + TCGConstraintSetIndex (*dynamic_constraint)(TCGType type, unsigned flags); +} TCGOutOp; + #include "tcg-target.c.inc" #ifndef CONFIG_TCG_INTERPRETER @@ -963,6 +987,10 @@ QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) - < MIN_TLB_MASK_TABLE_OFS); #endif +/* Register allocation descriptions for every TCGOpcode. */ +static const TCGOutOp * const all_outop[NB_OPS] = { +}; + /* * All TCG threads except the parent (i.e. the one that called tcg_context_init * and registered the target's TCG globals) must register with this function @@ -2416,8 +2444,32 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return has_type && TCG_TARGET_HAS_cmpsel_vec; default: - tcg_debug_assert(op > INDEX_op_last_generic && op < NB_OPS); + if (op < INDEX_op_last_generic) { + const TCGOutOp *outop; + TCGConstraintSetIndex con_set; + + if (!has_type) { + return false; + } + + outop = all_outop[op]; + tcg_debug_assert(outop != NULL); + + con_set = outop->static_constraint; + if (con_set == C_Dynamic) { + con_set = outop->dynamic_constraint(type, flags); + } + if (con_set >= 0) { + return true; + } + tcg_debug_assert(con_set == C_NotImplemented); + return false; + } + tcg_debug_assert(op < NB_OPS); return true; + + case INDEX_op_last_generic: + g_assert_not_reached(); } } @@ -3335,19 +3387,27 @@ static void process_constraint_sets(void) static const TCGArgConstraint *opcode_args_ct(const TCGOp *op) { - const TCGOpDef *def = &tcg_op_defs[op->opc]; + TCGOpcode opc = op->opc; + TCGType type = TCGOP_TYPE(op); + unsigned flags = TCGOP_FLAGS(op); + const TCGOpDef *def = &tcg_op_defs[opc]; + const TCGOutOp *outop = all_outop[opc]; TCGConstraintSetIndex con_set; -#ifdef CONFIG_DEBUG_TCG - assert(tcg_op_supported(op->opc, TCGOP_TYPE(op), TCGOP_FLAGS(op))); -#endif - if (def->flags & TCG_OPF_NOT_PRESENT) { return empty_cts; } - con_set = tcg_target_op_def(op->opc, TCGOP_TYPE(op), TCGOP_FLAGS(op)); - tcg_debug_assert(con_set >= 0 && con_set < ARRAY_SIZE(constraint_sets)); + if (outop) { + con_set = outop->static_constraint; + if (con_set == C_Dynamic) { + con_set = outop->dynamic_constraint(type, flags); + } + } else { + con_set = tcg_target_op_def(opc, type, flags); + } + tcg_debug_assert(con_set >= 0); + tcg_debug_assert(con_set < ARRAY_SIZE(constraint_sets)); /* The constraint arguments must match TCGOpcode arguments. */ tcg_debug_assert(constraint_sets[con_set].nb_oargs == def->nb_oargs); From 2225fa242c5d95c5cd83bb6f8566d43dd7a00211 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 22 Feb 2025 09:36:21 -0800 Subject: [PATCH 0361/2760] tcg: Use extract2 for cross-word 64-bit extract on 32-bit host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/tcg-op.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index fec6d678a2..f68c4f9702 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2804,9 +2804,18 @@ void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, tcg_gen_movi_i32(TCGV_HIGH(ret), 0); return; } - /* The field is split across two words. One double-word - shift is better than two double-word shifts. */ - goto do_shift_and; + + /* The field is split across two words. */ + tcg_gen_extract2_i32(TCGV_LOW(ret), TCGV_LOW(arg), + TCGV_HIGH(arg), ofs); + if (len <= 32) { + tcg_gen_extract_i32(TCGV_LOW(ret), TCGV_LOW(ret), 0, len); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + } else { + tcg_gen_extract_i32(TCGV_HIGH(ret), TCGV_HIGH(arg), + ofs, len - 32); + } + return; } if (TCG_TARGET_extract_valid(TCG_TYPE_I64, ofs, len)) { @@ -2844,7 +2853,6 @@ void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, so that we get ext8u, ext16u, and ext32u. */ switch (len) { case 1 ... 8: case 16: case 32: - do_shift_and: tcg_gen_shri_i64(ret, arg, ofs); tcg_gen_andi_i64(ret, ret, (1ull << len) - 1); break; From 48e8de684aff7ad112aafcf74f776d2a66ef192e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 26 Dec 2024 12:01:57 -0800 Subject: [PATCH 0362/2760] tcg: Remove INDEX_op_ext{8,16,32}* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the fully general extract opcodes instead. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 14 -- include/tcg/tcg-opc.h | 10 - tcg/aarch64/tcg-target-has.h | 10 - tcg/aarch64/tcg-target.c.inc | 22 +- tcg/arm/tcg-target-has.h | 4 - tcg/arm/tcg-target.c.inc | 7 - tcg/i386/tcg-target-has.h | 10 - tcg/i386/tcg-target.c.inc | 24 +- tcg/loongarch64/tcg-target-has.h | 10 - tcg/loongarch64/tcg-target.c.inc | 22 +- tcg/mips/tcg-target-has.h | 13 - tcg/mips/tcg-target.c.inc | 20 +- tcg/optimize.c | 61 +---- tcg/ppc/tcg-target-has.h | 12 - tcg/ppc/tcg-target.c.inc | 17 +- tcg/riscv/tcg-target-has.h | 10 - tcg/riscv/tcg-target.c.inc | 22 +- tcg/s390x/tcg-target-has.h | 10 - tcg/s390x/tcg-target.c.inc | 22 +- tcg/sparc64/tcg-target-has.h | 10 - tcg/sparc64/tcg-target.c.inc | 14 +- tcg/tcg-has.h | 6 - tcg/tcg-op.c | 414 +++++++------------------------ tcg/tcg.c | 46 ---- tcg/tci.c | 36 --- tcg/tci/tcg-target-has.h | 10 - tcg/tci/tcg-target.c.inc | 102 +++----- 27 files changed, 135 insertions(+), 823 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 688984fd39..3db7b81637 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -396,20 +396,6 @@ Misc - | *t0* = *t1* | Move *t1* to *t0* (both operands must have the same type). - * - ext8s_i32/i64 *t0*, *t1* - - ext8u_i32/i64 *t0*, *t1* - - ext16s_i32/i64 *t0*, *t1* - - ext16u_i32/i64 *t0*, *t1* - - ext32s_i64 *t0*, *t1* - - ext32u_i64 *t0*, *t1* - - - | 8, 16 or 32 bit sign/zero extension (both operands must have the same type) - * - bswap16_i32/i64 *t0*, *t1*, *flags* - | 16 bit byte swap on the low bits of a 32/64 bit input. diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 5bf78b0764..c26cffaa3f 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -85,10 +85,6 @@ DEF(mulsh_i32, 1, 2, 0, 0) DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(setcond2_i32, 1, 4, 1, 0) -DEF(ext8s_i32, 1, 1, 0, 0) -DEF(ext16s_i32, 1, 1, 0, 0) -DEF(ext8u_i32, 1, 1, 0, 0) -DEF(ext16u_i32, 1, 1, 0, 0) DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) DEF(not_i32, 1, 1, 0, 0) @@ -149,12 +145,6 @@ DEF(extrl_i64_i32, 1, 1, 0, 0) DEF(extrh_i64_i32, 1, 1, 0, 0) DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) -DEF(ext8s_i64, 1, 1, 0, 0) -DEF(ext16s_i64, 1, 1, 0, 0) -DEF(ext32s_i64, 1, 1, 0, 0) -DEF(ext8u_i64, 1, 1, 0, 0) -DEF(ext16u_i64, 1, 1, 0, 0) -DEF(ext32u_i64, 1, 1, 0, 0) DEF(bswap16_i64, 1, 1, 1, 0) DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 39f01c14cd..bfd587c0fc 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -15,10 +15,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 @@ -44,12 +40,6 @@ #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 1 -#define TCG_TARGET_HAS_ext16u_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 4645242d85..b8b26c1c93 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2493,17 +2493,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16s_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_ext_i32_i64: + case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -2979,16 +2969,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: - case INDEX_op_ext8s_i32: - case INDEX_op_ext16s_i32: - case INDEX_op_ext8u_i32: - case INDEX_op_ext16u_i32: - case INDEX_op_ext8s_i64: - case INDEX_op_ext16s_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32u_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extract_i32: diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index e3510a8f7a..8398c80c8e 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -24,10 +24,6 @@ extern bool use_neon_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 0 /* and r0, r1, #0xff */ -#define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index cec3d761d4..0e48f790f9 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2113,10 +2113,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ - case INDEX_op_ext8u_i32: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16u_i32: default: g_assert_not_reached(); } @@ -2138,9 +2134,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_not_i32: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: - case INDEX_op_ext8s_i32: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16u_i32: case INDEX_op_extract_i32: case INDEX_op_sextract_i32: return C_O1_I1(r, r); diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index 63768ff058..bbf55c86b6 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -28,10 +28,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_div2_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 @@ -57,12 +53,6 @@ #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_div2_i64 1 #define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 1 -#define TCG_TARGET_HAS_ext16u_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 33d303a123..02024018cb 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3016,17 +3016,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_ext_i32_i64: + case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -3663,18 +3653,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrh_i64_i32: return C_O1_I1(r, 0); - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - return C_O1_I1(r, q); - - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 188b00799f..166c9d7e41 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -22,10 +22,6 @@ #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_muluh_i32 1 #define TCG_TARGET_HAS_mulsh_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 @@ -47,12 +43,6 @@ #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 1 -#define TCG_TARGET_HAS_ext16u_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 879f66f255..6e77d3e79b 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1707,17 +1707,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_ext_i32_i64: + case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -2243,16 +2233,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_brcond_i64: return C_O0_I2(rz, rz); - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index df6960fe9a..fd96905484 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -80,8 +80,6 @@ extern bool use_mips32r2_instructions; /* optional instructions detected at runtime */ #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_ext8s_i32 use_mips32r2_instructions -#define TCG_TARGET_HAS_ext16s_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_rot_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_clz_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_ctz_i32 0 @@ -93,23 +91,12 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_ext8s_i64 use_mips32r2_instructions -#define TCG_TARGET_HAS_ext16s_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_rot_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_clz_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 #endif -/* optional instructions automatically implemented */ -#define TCG_TARGET_HAS_ext8u_i32 0 /* andi rt, rs, 0xff */ -#define TCG_TARGET_HAS_ext16u_i32 0 /* andi rt, rs, 0xffff */ - -#if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_ext8u_i64 0 /* andi rt, rs, 0xff */ -#define TCG_TARGET_HAS_ext16u_i64 0 /* andi rt, rs, 0xffff */ -#endif - #define TCG_TARGET_HAS_qemu_ldst_i128 0 #define TCG_TARGET_HAS_tst 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index f8c105ba37..f77159bdc7 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -647,7 +647,7 @@ static void tcg_out_movi(TCGContext *s, TCGType type, static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) { - tcg_debug_assert(TCG_TARGET_HAS_ext8s_i32); + tcg_debug_assert(use_mips32r2_instructions); tcg_out_opc_reg(s, OPC_SEB, rd, TCG_REG_ZERO, rs); } @@ -658,7 +658,7 @@ static void tcg_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs) static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) { - tcg_debug_assert(TCG_TARGET_HAS_ext16s_i32); + tcg_debug_assert(use_mips32r2_instructions); tcg_out_opc_reg(s, OPC_SEH, rd, TCG_REG_ZERO, rs); } @@ -2106,15 +2106,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_ext_i32_i64: + case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -2138,8 +2130,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_not_i32: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: - case INDEX_op_ext8s_i32: - case INDEX_op_ext16s_i32: case INDEX_op_extract_i32: case INDEX_op_sextract_i32: case INDEX_op_ld8u_i64: @@ -2154,10 +2144,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: - case INDEX_op_ext8s_i64: - case INDEX_op_ext16s_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: diff --git a/tcg/optimize.c b/tcg/optimize.c index 3bd4ee4d58..e9e654597d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -513,18 +513,6 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) case INDEX_op_ctpop_i64: return ctpop64(x); - CASE_OP_32_64(ext8s): - return (int8_t)x; - - CASE_OP_32_64(ext16s): - return (int16_t)x; - - CASE_OP_32_64(ext8u): - return (uint8_t)x; - - CASE_OP_32_64(ext16u): - return (uint16_t)x; - CASE_OP_32_64(bswap16): x = bswap16(x); return y & TCG_BSWAP_OS ? (int16_t)x : x; @@ -537,12 +525,10 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) return bswap64(x); case INDEX_op_ext_i32_i64: - case INDEX_op_ext32s_i64: return (int32_t)x; case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: - case INDEX_op_ext32u_i64: return (uint32_t)x; case INDEX_op_extrh_i64_i32: @@ -1869,8 +1855,7 @@ static bool fold_extract2(OptContext *ctx, TCGOp *op) static bool fold_exts(OptContext *ctx, TCGOp *op) { - uint64_t s_mask_old, s_mask, z_mask; - bool type_change = false; + uint64_t s_mask, z_mask; TempOptInfo *t1; if (fold_const1(ctx, op)) { @@ -1880,72 +1865,38 @@ static bool fold_exts(OptContext *ctx, TCGOp *op) t1 = arg_info(op->args[1]); z_mask = t1->z_mask; s_mask = t1->s_mask; - s_mask_old = s_mask; switch (op->opc) { - CASE_OP_32_64(ext8s): - s_mask |= INT8_MIN; - z_mask = (int8_t)z_mask; - break; - CASE_OP_32_64(ext16s): - s_mask |= INT16_MIN; - z_mask = (int16_t)z_mask; - break; case INDEX_op_ext_i32_i64: - type_change = true; - QEMU_FALLTHROUGH; - case INDEX_op_ext32s_i64: s_mask |= INT32_MIN; z_mask = (int32_t)z_mask; break; default: g_assert_not_reached(); } - - if (!type_change && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { - return true; - } - return fold_masks_zs(ctx, op, z_mask, s_mask); } static bool fold_extu(OptContext *ctx, TCGOp *op) { - uint64_t z_mask_old, z_mask; - bool type_change = false; + uint64_t z_mask; if (fold_const1(ctx, op)) { return true; } - z_mask_old = z_mask = arg_info(op->args[1])->z_mask; - + z_mask = arg_info(op->args[1])->z_mask; switch (op->opc) { - CASE_OP_32_64(ext8u): - z_mask = (uint8_t)z_mask; - break; - CASE_OP_32_64(ext16u): - z_mask = (uint16_t)z_mask; - break; case INDEX_op_extrl_i64_i32: case INDEX_op_extu_i32_i64: - type_change = true; - QEMU_FALLTHROUGH; - case INDEX_op_ext32u_i64: z_mask = (uint32_t)z_mask; break; case INDEX_op_extrh_i64_i32: - type_change = true; z_mask >>= 32; break; default: g_assert_not_reached(); } - - if (!type_change && fold_affected_mask(ctx, op, z_mask_old ^ z_mask)) { - return true; - } - return fold_masks_z(ctx, op, z_mask); } @@ -2948,15 +2899,9 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(extract2): done = fold_extract2(&ctx, op); break; - CASE_OP_32_64(ext8s): - CASE_OP_32_64(ext16s): - case INDEX_op_ext32s_i64: case INDEX_op_ext_i32_i64: done = fold_exts(&ctx, op); break; - CASE_OP_32_64(ext8u): - CASE_OP_32_64(ext16u): - case INDEX_op_ext32u_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 6db91f78ce..9acfc574c5 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -16,16 +16,10 @@ #define have_altivec (cpuinfo & CPUINFO_ALTIVEC) #define have_vsx (cpuinfo & CPUINFO_VSX) -/* optional instructions automatically implemented */ -#define TCG_TARGET_HAS_ext8u_i32 0 /* andi */ -#define TCG_TARGET_HAS_ext16u_i32 0 - /* optional instructions */ #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 have_isa_3_00 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 @@ -52,12 +46,6 @@ #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 have_isa_3_00 #define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 0 -#define TCG_TARGET_HAS_ext16u_i64 0 -#define TCG_TARGET_HAS_ext32u_i64 0 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 822925a19b..e10c1c5162 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3473,17 +3473,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_ext_i32_i64: + case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -4109,8 +4099,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ctpop_i32: case INDEX_op_neg_i32: case INDEX_op_not_i32: - case INDEX_op_ext8s_i32: - case INDEX_op_ext16s_i32: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: case INDEX_op_extract_i32: @@ -4125,9 +4113,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ctpop_i64: case INDEX_op_neg_i64: case INDEX_op_not_i64: - case INDEX_op_ext8s_i64: - case INDEX_op_ext16s_i64: - case INDEX_op_ext32s_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_bswap16_i64: diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 98081084f2..fc62049c78 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -22,10 +22,6 @@ #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i32 1 @@ -46,12 +42,6 @@ #define TCG_TARGET_HAS_rot_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 1 -#define TCG_TARGET_HAS_ext16u_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 #define TCG_TARGET_HAS_bswap16_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index f7e1ca5a56..d525df4e1d 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2385,17 +2385,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_ext_i32_i64: + case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -2643,17 +2633,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_not_i64: case INDEX_op_neg_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32u_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext32s_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ext_i32_i64: diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index e99e671642..aea805455f 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -31,10 +31,6 @@ extern uint64_t s390_facilities[3]; /* optional instructions */ #define TCG_TARGET_HAS_div2_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 HAVE_FACILITY(MISC_INSN_EXT3) @@ -59,12 +55,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_div2_i64 1 #define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 1 -#define TCG_TARGET_HAS_ext16u_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index b2e1cd60ff..8421320928 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2781,17 +2781,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_ext_i32_i64: + case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -3340,16 +3330,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_neg_i64: case INDEX_op_not_i32: case INDEX_op_not_i64: - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extract_i32: diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 2f46df8c61..ad6f35da17 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -17,10 +17,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 0 #define TCG_TARGET_HAS_rot_i32 0 -#define TCG_TARGET_HAS_ext8s_i32 0 -#define TCG_TARGET_HAS_ext16s_i32 0 -#define TCG_TARGET_HAS_ext8u_i32 0 -#define TCG_TARGET_HAS_ext16u_i32 0 #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 #define TCG_TARGET_HAS_not_i32 1 @@ -46,12 +42,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 0 #define TCG_TARGET_HAS_rot_i64 0 -#define TCG_TARGET_HAS_ext8s_i64 0 -#define TCG_TARGET_HAS_ext16s_i64 0 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 0 -#define TCG_TARGET_HAS_ext16u_i64 0 -#define TCG_TARGET_HAS_ext32u_i64 1 #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 7c722f59a8..787e0d896c 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1517,17 +1517,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_ext_i32_i64: + case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: default: g_assert_not_reached(); @@ -1557,8 +1547,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_neg_i64: case INDEX_op_not_i32: case INDEX_op_not_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extract_i64: diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 418e4673eb..4ccdc6bbee 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -16,12 +16,6 @@ #define TCG_TARGET_HAS_rem_i64 0 #define TCG_TARGET_HAS_div2_i64 0 #define TCG_TARGET_HAS_rot_i64 0 -#define TCG_TARGET_HAS_ext8s_i64 0 -#define TCG_TARGET_HAS_ext16s_i64 0 -#define TCG_TARGET_HAS_ext32s_i64 0 -#define TCG_TARGET_HAS_ext8u_i64 0 -#define TCG_TARGET_HAS_ext16u_i64 0 -#define TCG_TARGET_HAS_ext32u_i64 0 #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index f68c4f9702..48793ed439 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -414,17 +414,19 @@ void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) case -1: tcg_gen_mov_i32(ret, arg1); return; - case 0xff: - /* Don't recurse with tcg_gen_ext8u_i32. */ - if (TCG_TARGET_HAS_ext8u_i32) { - tcg_gen_op2_i32(INDEX_op_ext8u_i32, ret, arg1); - return; - } - break; - case 0xffff: - if (TCG_TARGET_HAS_ext16u_i32) { - tcg_gen_op2_i32(INDEX_op_ext16u_i32, ret, arg1); - return; + default: + /* + * Canonicalize on extract, if valid. This aids x86 with its + * 2 operand MOVZBL and 2 operand AND, selecting the TCGOpcode + * which does not require matching operands. Other backends can + * trivially expand the extract to AND during code generation. + */ + if (!(arg2 & (arg2 + 1))) { + unsigned len = ctz32(~arg2); + if (TCG_TARGET_extract_valid(TCG_TYPE_I32, 0, len)) { + tcg_gen_extract_i32(ret, arg1, 0, len); + return; + } } break; } @@ -955,40 +957,20 @@ void tcg_gen_deposit_z_i32(TCGv_i32 ret, TCGv_i32 arg, TCGv_i32 zero = tcg_constant_i32(0); tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, zero, arg, ofs, len); } else { - /* To help two-operand hosts we prefer to zero-extend first, - which allows ARG to stay live. */ - switch (len) { - case 16: - if (TCG_TARGET_HAS_ext16u_i32) { - tcg_gen_ext16u_i32(ret, arg); - tcg_gen_shli_i32(ret, ret, ofs); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8u_i32) { - tcg_gen_ext8u_i32(ret, arg); - tcg_gen_shli_i32(ret, ret, ofs); - return; - } - break; + /* + * To help two-operand hosts we prefer to zero-extend first, + * which allows ARG to stay live. + */ + if (TCG_TARGET_extract_valid(TCG_TYPE_I32, 0, len)) { + tcg_gen_extract_i32(ret, arg, 0, len); + tcg_gen_shli_i32(ret, ret, ofs); + return; } /* Otherwise prefer zero-extension over AND for code size. */ - switch (ofs + len) { - case 16: - if (TCG_TARGET_HAS_ext16u_i32) { - tcg_gen_shli_i32(ret, arg, ofs); - tcg_gen_ext16u_i32(ret, ret); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8u_i32) { - tcg_gen_shli_i32(ret, arg, ofs); - tcg_gen_ext8u_i32(ret, ret); - return; - } - break; + if (TCG_TARGET_extract_valid(TCG_TYPE_I32, 0, ofs + len)) { + tcg_gen_shli_i32(ret, arg, ofs); + tcg_gen_extract_i32(ret, ret, 0, ofs + len); + return; } tcg_gen_andi_i32(ret, arg, (1u << len) - 1); tcg_gen_shli_i32(ret, ret, ofs); @@ -1008,32 +990,21 @@ void tcg_gen_extract_i32(TCGv_i32 ret, TCGv_i32 arg, tcg_gen_shri_i32(ret, arg, 32 - len); return; } - if (ofs == 0) { - tcg_gen_andi_i32(ret, arg, (1u << len) - 1); - return; - } if (TCG_TARGET_extract_valid(TCG_TYPE_I32, ofs, len)) { tcg_gen_op4ii_i32(INDEX_op_extract_i32, ret, arg, ofs, len); return; } + if (ofs == 0) { + tcg_gen_andi_i32(ret, arg, (1u << len) - 1); + return; + } /* Assume that zero-extension, if available, is cheaper than a shift. */ - switch (ofs + len) { - case 16: - if (TCG_TARGET_HAS_ext16u_i32) { - tcg_gen_ext16u_i32(ret, arg); - tcg_gen_shri_i32(ret, ret, ofs); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8u_i32) { - tcg_gen_ext8u_i32(ret, arg); - tcg_gen_shri_i32(ret, ret, ofs); - return; - } - break; + if (TCG_TARGET_extract_valid(TCG_TYPE_I32, 0, ofs + len)) { + tcg_gen_op4ii_i32(INDEX_op_extract_i32, ret, arg, 0, ofs + len); + tcg_gen_shri_i32(ret, ret, ofs); + return; } /* ??? Ideally we'd know what values are available for immediate AND. @@ -1064,16 +1035,6 @@ void tcg_gen_sextract_i32(TCGv_i32 ret, TCGv_i32 arg, tcg_gen_sari_i32(ret, arg, 32 - len); return; } - if (ofs == 0) { - switch (len) { - case 16: - tcg_gen_ext16s_i32(ret, arg); - return; - case 8: - tcg_gen_ext8s_i32(ret, arg); - return; - } - } if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, ofs, len)) { tcg_gen_op4ii_i32(INDEX_op_sextract_i32, ret, arg, ofs, len); @@ -1081,37 +1042,15 @@ void tcg_gen_sextract_i32(TCGv_i32 ret, TCGv_i32 arg, } /* Assume that sign-extension, if available, is cheaper than a shift. */ - switch (ofs + len) { - case 16: - if (TCG_TARGET_HAS_ext16s_i32) { - tcg_gen_ext16s_i32(ret, arg); - tcg_gen_sari_i32(ret, ret, ofs); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8s_i32) { - tcg_gen_ext8s_i32(ret, arg); - tcg_gen_sari_i32(ret, ret, ofs); - return; - } - break; + if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, 0, ofs + len)) { + tcg_gen_op4ii_i32(INDEX_op_sextract_i32, ret, arg, 0, ofs + len); + tcg_gen_sari_i32(ret, ret, ofs); + return; } - switch (len) { - case 16: - if (TCG_TARGET_HAS_ext16s_i32) { - tcg_gen_shri_i32(ret, arg, ofs); - tcg_gen_ext16s_i32(ret, ret); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8s_i32) { - tcg_gen_shri_i32(ret, arg, ofs); - tcg_gen_ext8s_i32(ret, ret); - return; - } - break; + if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, 0, len)) { + tcg_gen_shri_i32(ret, arg, ofs); + tcg_gen_op4ii_i32(INDEX_op_sextract_i32, ret, ret, 0, len); + return; } tcg_gen_shli_i32(ret, arg, 32 - len - ofs); @@ -1281,40 +1220,22 @@ void tcg_gen_mulsu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_ext8s_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_ext8s_i32) { - tcg_gen_op2_i32(INDEX_op_ext8s_i32, ret, arg); - } else { - tcg_gen_shli_i32(ret, arg, 24); - tcg_gen_sari_i32(ret, ret, 24); - } + tcg_gen_sextract_i32(ret, arg, 0, 8); } void tcg_gen_ext16s_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_ext16s_i32) { - tcg_gen_op2_i32(INDEX_op_ext16s_i32, ret, arg); - } else { - tcg_gen_shli_i32(ret, arg, 16); - tcg_gen_sari_i32(ret, ret, 16); - } + tcg_gen_sextract_i32(ret, arg, 0, 16); } void tcg_gen_ext8u_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_ext8u_i32) { - tcg_gen_op2_i32(INDEX_op_ext8u_i32, ret, arg); - } else { - tcg_gen_andi_i32(ret, arg, 0xffu); - } + tcg_gen_extract_i32(ret, arg, 0, 8); } void tcg_gen_ext16u_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_ext16u_i32) { - tcg_gen_op2_i32(INDEX_op_ext16u_i32, ret, arg); - } else { - tcg_gen_andi_i32(ret, arg, 0xffffu); - } + tcg_gen_extract_i32(ret, arg, 0, 16); } /* @@ -1794,23 +1715,19 @@ void tcg_gen_andi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) case -1: tcg_gen_mov_i64(ret, arg1); return; - case 0xff: - /* Don't recurse with tcg_gen_ext8u_i64. */ - if (TCG_TARGET_HAS_ext8u_i64) { - tcg_gen_op2_i64(INDEX_op_ext8u_i64, ret, arg1); - return; - } - break; - case 0xffff: - if (TCG_TARGET_HAS_ext16u_i64) { - tcg_gen_op2_i64(INDEX_op_ext16u_i64, ret, arg1); - return; - } - break; - case 0xffffffffu: - if (TCG_TARGET_HAS_ext32u_i64) { - tcg_gen_op2_i64(INDEX_op_ext32u_i64, ret, arg1); - return; + default: + /* + * Canonicalize on extract, if valid. This aids x86 with its + * 2 operand MOVZBL and 2 operand AND, selecting the TCGOpcode + * which does not require matching operands. Other backends can + * trivially expand the extract to AND during code generation. + */ + if (!(arg2 & (arg2 + 1))) { + unsigned len = ctz64(~arg2); + if (TCG_TARGET_extract_valid(TCG_TYPE_I64, 0, len)) { + tcg_gen_extract_i64(ret, arg1, 0, len); + return; + } } break; } @@ -2118,77 +2035,32 @@ void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_ext8s_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_ext8s_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); - } else if (TCG_TARGET_HAS_ext8s_i64) { - tcg_gen_op2_i64(INDEX_op_ext8s_i64, ret, arg); - } else { - tcg_gen_shli_i64(ret, arg, 56); - tcg_gen_sari_i64(ret, ret, 56); - } + tcg_gen_sextract_i64(ret, arg, 0, 8); } void tcg_gen_ext16s_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_ext16s_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); - } else if (TCG_TARGET_HAS_ext16s_i64) { - tcg_gen_op2_i64(INDEX_op_ext16s_i64, ret, arg); - } else { - tcg_gen_shli_i64(ret, arg, 48); - tcg_gen_sari_i64(ret, ret, 48); - } + tcg_gen_sextract_i64(ret, arg, 0, 16); } void tcg_gen_ext32s_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); - } else if (TCG_TARGET_HAS_ext32s_i64) { - tcg_gen_op2_i64(INDEX_op_ext32s_i64, ret, arg); - } else { - tcg_gen_shli_i64(ret, arg, 32); - tcg_gen_sari_i64(ret, ret, 32); - } + tcg_gen_sextract_i64(ret, arg, 0, 32); } void tcg_gen_ext8u_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_ext8u_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); - } else if (TCG_TARGET_HAS_ext8u_i64) { - tcg_gen_op2_i64(INDEX_op_ext8u_i64, ret, arg); - } else { - tcg_gen_andi_i64(ret, arg, 0xffu); - } + tcg_gen_extract_i64(ret, arg, 0, 8); } void tcg_gen_ext16u_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_ext16u_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); - } else if (TCG_TARGET_HAS_ext16u_i64) { - tcg_gen_op2_i64(INDEX_op_ext16u_i64, ret, arg); - } else { - tcg_gen_andi_i64(ret, arg, 0xffffu); - } + tcg_gen_extract_i64(ret, arg, 0, 16); } void tcg_gen_ext32u_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_REG_BITS == 32) { - tcg_gen_mov_i32(TCGV_LOW(ret), TCGV_LOW(arg)); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); - } else if (TCG_TARGET_HAS_ext32u_i64) { - tcg_gen_op2_i64(INDEX_op_ext32u_i64, ret, arg); - } else { - tcg_gen_andi_i64(ret, arg, 0xffffffffu); - } + tcg_gen_extract_i64(ret, arg, 0, 32); } /* @@ -2720,54 +2592,20 @@ void tcg_gen_deposit_z_i64(TCGv_i64 ret, TCGv_i64 arg, return; } } - /* To help two-operand hosts we prefer to zero-extend first, - which allows ARG to stay live. */ - switch (len) { - case 32: - if (TCG_TARGET_HAS_ext32u_i64) { - tcg_gen_ext32u_i64(ret, arg); - tcg_gen_shli_i64(ret, ret, ofs); - return; - } - break; - case 16: - if (TCG_TARGET_HAS_ext16u_i64) { - tcg_gen_ext16u_i64(ret, arg); - tcg_gen_shli_i64(ret, ret, ofs); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8u_i64) { - tcg_gen_ext8u_i64(ret, arg); - tcg_gen_shli_i64(ret, ret, ofs); - return; - } - break; + /* + * To help two-operand hosts we prefer to zero-extend first, + * which allows ARG to stay live. + */ + if (TCG_TARGET_extract_valid(TCG_TYPE_I64, 0, len)) { + tcg_gen_extract_i64(ret, arg, 0, len); + tcg_gen_shli_i64(ret, ret, ofs); + return; } /* Otherwise prefer zero-extension over AND for code size. */ - switch (ofs + len) { - case 32: - if (TCG_TARGET_HAS_ext32u_i64) { - tcg_gen_shli_i64(ret, arg, ofs); - tcg_gen_ext32u_i64(ret, ret); - return; - } - break; - case 16: - if (TCG_TARGET_HAS_ext16u_i64) { - tcg_gen_shli_i64(ret, arg, ofs); - tcg_gen_ext16u_i64(ret, ret); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8u_i64) { - tcg_gen_shli_i64(ret, arg, ofs); - tcg_gen_ext8u_i64(ret, ret); - return; - } - break; + if (TCG_TARGET_extract_valid(TCG_TYPE_I64, 0, ofs + len)) { + tcg_gen_shli_i64(ret, arg, ofs); + tcg_gen_extract_i64(ret, ret, 0, ofs + len); + return; } tcg_gen_andi_i64(ret, arg, (1ull << len) - 1); tcg_gen_shli_i64(ret, ret, ofs); @@ -2787,10 +2625,6 @@ void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, tcg_gen_shri_i64(ret, arg, 64 - len); return; } - if (ofs == 0) { - tcg_gen_andi_i64(ret, arg, (1ull << len) - 1); - return; - } if (TCG_TARGET_REG_BITS == 32) { /* Look for a 32-bit extract within one of the two words. */ @@ -2822,30 +2656,16 @@ void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, tcg_gen_op4ii_i64(INDEX_op_extract_i64, ret, arg, ofs, len); return; } + if (ofs == 0) { + tcg_gen_andi_i64(ret, arg, (1ull << len) - 1); + return; + } /* Assume that zero-extension, if available, is cheaper than a shift. */ - switch (ofs + len) { - case 32: - if (TCG_TARGET_HAS_ext32u_i64) { - tcg_gen_ext32u_i64(ret, arg); - tcg_gen_shri_i64(ret, ret, ofs); - return; - } - break; - case 16: - if (TCG_TARGET_HAS_ext16u_i64) { - tcg_gen_ext16u_i64(ret, arg); - tcg_gen_shri_i64(ret, ret, ofs); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8u_i64) { - tcg_gen_ext8u_i64(ret, arg); - tcg_gen_shri_i64(ret, ret, ofs); - return; - } - break; + if (TCG_TARGET_extract_valid(TCG_TYPE_I64, 0, ofs + len)) { + tcg_gen_op4ii_i64(INDEX_op_extract_i64, ret, arg, 0, ofs + len); + tcg_gen_shri_i64(ret, ret, ofs); + return; } /* ??? Ideally we'd know what values are available for immediate AND. @@ -2876,19 +2696,6 @@ void tcg_gen_sextract_i64(TCGv_i64 ret, TCGv_i64 arg, tcg_gen_sari_i64(ret, arg, 64 - len); return; } - if (ofs == 0) { - switch (len) { - case 32: - tcg_gen_ext32s_i64(ret, arg); - return; - case 16: - tcg_gen_ext16s_i64(ret, arg); - return; - case 8: - tcg_gen_ext8s_i64(ret, arg); - return; - } - } if (TCG_TARGET_REG_BITS == 32) { /* Look for a 32-bit extract within one of the two words. */ @@ -2928,52 +2735,17 @@ void tcg_gen_sextract_i64(TCGv_i64 ret, TCGv_i64 arg, } /* Assume that sign-extension, if available, is cheaper than a shift. */ - switch (ofs + len) { - case 32: - if (TCG_TARGET_HAS_ext32s_i64) { - tcg_gen_ext32s_i64(ret, arg); - tcg_gen_sari_i64(ret, ret, ofs); - return; - } - break; - case 16: - if (TCG_TARGET_HAS_ext16s_i64) { - tcg_gen_ext16s_i64(ret, arg); - tcg_gen_sari_i64(ret, ret, ofs); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8s_i64) { - tcg_gen_ext8s_i64(ret, arg); - tcg_gen_sari_i64(ret, ret, ofs); - return; - } - break; + if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, 0, ofs + len)) { + tcg_gen_op4ii_i64(INDEX_op_sextract_i64, ret, arg, 0, ofs + len); + tcg_gen_sari_i64(ret, ret, ofs); + return; } - switch (len) { - case 32: - if (TCG_TARGET_HAS_ext32s_i64) { - tcg_gen_shri_i64(ret, arg, ofs); - tcg_gen_ext32s_i64(ret, ret); - return; - } - break; - case 16: - if (TCG_TARGET_HAS_ext16s_i64) { - tcg_gen_shri_i64(ret, arg, ofs); - tcg_gen_ext16s_i64(ret, ret); - return; - } - break; - case 8: - if (TCG_TARGET_HAS_ext8s_i64) { - tcg_gen_shri_i64(ret, arg, ofs); - tcg_gen_ext8s_i64(ret, ret); - return; - } - break; + if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, 0, len)) { + tcg_gen_shri_i64(ret, arg, ofs); + tcg_gen_op4ii_i64(INDEX_op_sextract_i64, ret, ret, 0, len); + return; } + tcg_gen_shli_i64(ret, arg, 64 - len - ofs); tcg_gen_sari_i64(ret, ret, 64 - len); } diff --git a/tcg/tcg.c b/tcg/tcg.c index 5090cdb3c6..e8fd89e4c8 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2242,14 +2242,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_muluh_i32; case INDEX_op_mulsh_i32: return TCG_TARGET_HAS_mulsh_i32; - case INDEX_op_ext8s_i32: - return TCG_TARGET_HAS_ext8s_i32; - case INDEX_op_ext16s_i32: - return TCG_TARGET_HAS_ext16s_i32; - case INDEX_op_ext8u_i32: - return TCG_TARGET_HAS_ext8u_i32; - case INDEX_op_ext16u_i32: - return TCG_TARGET_HAS_ext16u_i32; case INDEX_op_bswap16_i32: return TCG_TARGET_HAS_bswap16_i32; case INDEX_op_bswap32_i32: @@ -2328,18 +2320,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: return TCG_TARGET_HAS_extr_i64_i32; - case INDEX_op_ext8s_i64: - return TCG_TARGET_HAS_ext8s_i64; - case INDEX_op_ext16s_i64: - return TCG_TARGET_HAS_ext16s_i64; - case INDEX_op_ext32s_i64: - return TCG_TARGET_HAS_ext32s_i64; - case INDEX_op_ext8u_i64: - return TCG_TARGET_HAS_ext8u_i64; - case INDEX_op_ext16u_i64: - return TCG_TARGET_HAS_ext16u_i64; - case INDEX_op_ext32u_i64: - return TCG_TARGET_HAS_ext32u_i64; case INDEX_op_bswap16_i64: return TCG_TARGET_HAS_bswap16_i64; case INDEX_op_bswap32_i64: @@ -5430,32 +5410,6 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) /* emit instruction */ switch (op->opc) { - case INDEX_op_ext8s_i32: - tcg_out_ext8s(s, TCG_TYPE_I32, new_args[0], new_args[1]); - break; - case INDEX_op_ext8s_i64: - tcg_out_ext8s(s, TCG_TYPE_I64, new_args[0], new_args[1]); - break; - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - tcg_out_ext8u(s, new_args[0], new_args[1]); - break; - case INDEX_op_ext16s_i32: - tcg_out_ext16s(s, TCG_TYPE_I32, new_args[0], new_args[1]); - break; - case INDEX_op_ext16s_i64: - tcg_out_ext16s(s, TCG_TYPE_I64, new_args[0], new_args[1]); - break; - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - tcg_out_ext16u(s, new_args[0], new_args[1]); - break; - case INDEX_op_ext32s_i64: - tcg_out_ext32s(s, new_args[0], new_args[1]); - break; - case INDEX_op_ext32u_i64: - tcg_out_ext32u(s, new_args[0], new_args[1]); - break; case INDEX_op_ext_i32_i64: tcg_out_exts_i32_i64(s, new_args[0], new_args[1]); break; diff --git a/tcg/tci.c b/tcg/tci.c index d223258efe..531cd83aae 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -689,31 +689,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_write_reg64(regs, r1, r0, tmp64); break; #endif -#if TCG_TARGET_HAS_ext8s_i32 || TCG_TARGET_HAS_ext8s_i64 - CASE_32_64(ext8s) - tci_args_rr(insn, &r0, &r1); - regs[r0] = (int8_t)regs[r1]; - break; -#endif -#if TCG_TARGET_HAS_ext16s_i32 || TCG_TARGET_HAS_ext16s_i64 || \ - TCG_TARGET_HAS_bswap16_i32 || TCG_TARGET_HAS_bswap16_i64 - CASE_32_64(ext16s) - tci_args_rr(insn, &r0, &r1); - regs[r0] = (int16_t)regs[r1]; - break; -#endif -#if TCG_TARGET_HAS_ext8u_i32 || TCG_TARGET_HAS_ext8u_i64 - CASE_32_64(ext8u) - tci_args_rr(insn, &r0, &r1); - regs[r0] = (uint8_t)regs[r1]; - break; -#endif -#if TCG_TARGET_HAS_ext16u_i32 || TCG_TARGET_HAS_ext16u_i64 - CASE_32_64(ext16u) - tci_args_rr(insn, &r0, &r1); - regs[r0] = (uint16_t)regs[r1]; - break; -#endif #if TCG_TARGET_HAS_bswap16_i32 || TCG_TARGET_HAS_bswap16_i64 CASE_32_64(bswap16) tci_args_rr(insn, &r0, &r1); @@ -864,12 +839,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tb_ptr = ptr; } break; - case INDEX_op_ext32s_i64: case INDEX_op_ext_i32_i64: tci_args_rr(insn, &r0, &r1); regs[r0] = (int32_t)regs[r1]; break; - case INDEX_op_ext32u_i64: case INDEX_op_extu_i32_i64: tci_args_rr(insn, &r0, &r1); regs[r0] = (uint32_t)regs[r1]; @@ -1092,15 +1065,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_mov_i32: case INDEX_op_mov_i64: - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_bswap16_i32: diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index c8785ca8dc..cb0964c3d4 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -11,10 +11,6 @@ #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 -#define TCG_TARGET_HAS_ext8s_i32 1 -#define TCG_TARGET_HAS_ext16s_i32 1 -#define TCG_TARGET_HAS_ext8u_i32 1 -#define TCG_TARGET_HAS_ext16u_i32 1 #define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_eqv_i32 1 @@ -40,12 +36,6 @@ #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_ext8s_i64 1 -#define TCG_TARGET_HAS_ext16s_i64 1 -#define TCG_TARGET_HAS_ext32s_i64 1 -#define TCG_TARGET_HAS_ext8u_i64 1 -#define TCG_TARGET_HAS_ext16u_i64 1 -#define TCG_TARGET_HAS_ext32u_i64 1 #define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_eqv_i64 1 #define TCG_TARGET_HAS_nand_i64 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 36e018dd19..6f8f1dd8ae 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -59,16 +59,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_not_i64: case INDEX_op_neg_i32: case INDEX_op_neg_i64: - case INDEX_op_ext8s_i32: - case INDEX_op_ext8s_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_bswap16_i32: @@ -535,76 +525,54 @@ static void tcg_out_movi(TCGContext *s, TCGType type, } } +static void tcg_out_extract(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rs, unsigned pos, unsigned len) +{ + TCGOpcode opc = type == TCG_TYPE_I32 ? + INDEX_op_extract_i32 : + INDEX_op_extract_i64; + tcg_out_op_rrbb(s, opc, rd, rs, pos, len); +} + +static void tcg_out_sextract(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rs, unsigned pos, unsigned len) +{ + TCGOpcode opc = type == TCG_TYPE_I32 ? + INDEX_op_sextract_i32 : + INDEX_op_sextract_i64; + tcg_out_op_rrbb(s, opc, rd, rs, pos, len); +} + static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) { - switch (type) { - case TCG_TYPE_I32: - tcg_debug_assert(TCG_TARGET_HAS_ext8s_i32); - tcg_out_op_rr(s, INDEX_op_ext8s_i32, rd, rs); - break; -#if TCG_TARGET_REG_BITS == 64 - case TCG_TYPE_I64: - tcg_debug_assert(TCG_TARGET_HAS_ext8s_i64); - tcg_out_op_rr(s, INDEX_op_ext8s_i64, rd, rs); - break; -#endif - default: - g_assert_not_reached(); - } + tcg_out_sextract(s, type, rd, rs, 0, 8); } static void tcg_out_ext8u(TCGContext *s, TCGReg rd, TCGReg rs) { - if (TCG_TARGET_REG_BITS == 64) { - tcg_debug_assert(TCG_TARGET_HAS_ext8u_i64); - tcg_out_op_rr(s, INDEX_op_ext8u_i64, rd, rs); - } else { - tcg_debug_assert(TCG_TARGET_HAS_ext8u_i32); - tcg_out_op_rr(s, INDEX_op_ext8u_i32, rd, rs); - } + tcg_out_extract(s, TCG_TYPE_REG, rd, rs, 0, 8); } static void tcg_out_ext16s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) { - switch (type) { - case TCG_TYPE_I32: - tcg_debug_assert(TCG_TARGET_HAS_ext16s_i32); - tcg_out_op_rr(s, INDEX_op_ext16s_i32, rd, rs); - break; -#if TCG_TARGET_REG_BITS == 64 - case TCG_TYPE_I64: - tcg_debug_assert(TCG_TARGET_HAS_ext16s_i64); - tcg_out_op_rr(s, INDEX_op_ext16s_i64, rd, rs); - break; -#endif - default: - g_assert_not_reached(); - } + tcg_out_sextract(s, type, rd, rs, 0, 16); } static void tcg_out_ext16u(TCGContext *s, TCGReg rd, TCGReg rs) { - if (TCG_TARGET_REG_BITS == 64) { - tcg_debug_assert(TCG_TARGET_HAS_ext16u_i64); - tcg_out_op_rr(s, INDEX_op_ext16u_i64, rd, rs); - } else { - tcg_debug_assert(TCG_TARGET_HAS_ext16u_i32); - tcg_out_op_rr(s, INDEX_op_ext16u_i32, rd, rs); - } + tcg_out_extract(s, TCG_TYPE_REG, rd, rs, 0, 16); } static void tcg_out_ext32s(TCGContext *s, TCGReg rd, TCGReg rs) { tcg_debug_assert(TCG_TARGET_REG_BITS == 64); - tcg_debug_assert(TCG_TARGET_HAS_ext32s_i64); - tcg_out_op_rr(s, INDEX_op_ext32s_i64, rd, rs); + tcg_out_sextract(s, TCG_TYPE_I64, rd, rs, 0, 32); } static void tcg_out_ext32u(TCGContext *s, TCGReg rd, TCGReg rs) { tcg_debug_assert(TCG_TARGET_REG_BITS == 64); - tcg_debug_assert(TCG_TARGET_HAS_ext32u_i64); - tcg_out_op_rr(s, INDEX_op_ext32u_i64, rd, rs); + tcg_out_extract(s, TCG_TYPE_I64, rd, rs, 0, 32); } static void tcg_out_exts_i32_i64(TCGContext *s, TCGReg rd, TCGReg rs) @@ -690,7 +658,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - TCGOpcode exts; + int width; switch (opc) { case INDEX_op_goto_ptr: @@ -777,18 +745,16 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_bswap16_i32: /* Optional (TCG_TARGET_HAS_bswap16_i32). */ - exts = INDEX_op_ext16s_i32; - goto do_bswap; case INDEX_op_bswap16_i64: /* Optional (TCG_TARGET_HAS_bswap16_i64). */ - exts = INDEX_op_ext16s_i64; + width = 16; goto do_bswap; case INDEX_op_bswap32_i64: /* Optional (TCG_TARGET_HAS_bswap32_i64). */ - exts = INDEX_op_ext32s_i64; + width = 32; do_bswap: /* The base tci bswaps zero-extend, and ignore high bits. */ tcg_out_op_rr(s, opc, args[0], args[1]); if (args[2] & TCG_BSWAP_OS) { - tcg_out_op_rr(s, exts, args[0], args[0]); + tcg_out_sextract(s, TCG_TYPE_REG, args[0], args[0], 0, width); } break; @@ -838,17 +804,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext8s_i32: /* Always emitted via tcg_reg_alloc_op. */ - case INDEX_op_ext8s_i64: - case INDEX_op_ext8u_i32: - case INDEX_op_ext8u_i64: - case INDEX_op_ext16s_i32: - case INDEX_op_ext16s_i64: - case INDEX_op_ext16u_i32: - case INDEX_op_ext16u_i64: - case INDEX_op_ext32s_i64: - case INDEX_op_ext32u_i64: - case INDEX_op_ext_i32_i64: + case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: From b5701261da6607e61ef1fe605d85bf31806fcd34 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 28 Dec 2024 15:58:24 -0800 Subject: [PATCH 0363/2760] tcg: Merge INDEX_op_mov_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Begin to rely on TCGOp.type to discriminate operations, rather than two different opcodes. Convert mov first. Introduce TCG_OPF_INT in order to keep opcode dumps the same. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 4 ++-- include/tcg/tcg-opc.h | 4 ++-- include/tcg/tcg.h | 2 ++ tcg/aarch64/tcg-target.c.inc | 2 -- tcg/arm/tcg-target.c.inc | 1 - tcg/i386/tcg-target.c.inc | 2 -- tcg/loongarch64/tcg-target.c.inc | 2 -- tcg/mips/tcg-target.c.inc | 2 -- tcg/optimize.c | 7 +++---- tcg/ppc/tcg-target.c.inc | 2 -- tcg/riscv/tcg-target.c.inc | 2 -- tcg/s390x/tcg-target.c.inc | 2 -- tcg/sparc64/tcg-target.c.inc | 2 -- tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 32 ++++++++++++++++++-------------- tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 15 +-------------- 17 files changed, 32 insertions(+), 58 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 3db7b81637..e6ccc78fa1 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -391,10 +391,10 @@ Misc .. list-table:: - * - mov_i32/i64 *t0*, *t1* + * - mov *t0*, *t1* - | *t0* = *t1* - | Move *t1* to *t0* (both operands must have the same type). + | Move *t1* to *t0*. * - bswap16_i32/i64 *t0*, *t1*, *flags* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index c26cffaa3f..766fd00d99 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -37,7 +37,8 @@ DEF(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) DEF(mb, 0, 0, 1, TCG_OPF_NOT_PRESENT) -DEF(mov_i32, 1, 1, 0, TCG_OPF_NOT_PRESENT) +DEF(mov, 1, 1, 0, TCG_OPF_INT | TCG_OPF_NOT_PRESENT) + DEF(setcond_i32, 1, 2, 1, 0) DEF(negsetcond_i32, 1, 2, 1, 0) DEF(movcond_i32, 1, 4, 1, 0) @@ -98,7 +99,6 @@ DEF(clz_i32, 1, 2, 0, 0) DEF(ctz_i32, 1, 2, 0, 0) DEF(ctpop_i32, 1, 1, 0, 0) -DEF(mov_i64, 1, 1, 0, TCG_OPF_NOT_PRESENT) DEF(setcond_i64, 1, 2, 1, 0) DEF(negsetcond_i64, 1, 2, 1, 0) DEF(movcond_i64, 1, 4, 1, 0) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 84d99508b6..c6b50b5226 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -741,6 +741,8 @@ enum { /* Instruction has side effects: it cannot be removed if its outputs are not used, and might trigger exceptions. */ TCG_OPF_SIDE_EFFECTS = 0x08, + /* Instruction operands may be I32 or I64 */ + TCG_OPF_INT = 0x10, /* Instruction is optional and not implemented by the host, or insn is generic and should not be implemented by the host. */ TCG_OPF_NOT_PRESENT = 0x20, diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index b8b26c1c93..466042a577 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2488,8 +2488,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_mb(s, a0); break; - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 0e48f790f9..0fafe97230 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2109,7 +2109,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mb(s, args[0]); break; - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 02024018cb..75c8665d74 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3011,8 +3011,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_mb: tcg_out_mb(s, a0); break; - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 6e77d3e79b..b0a4ec4c13 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1702,8 +1702,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_ldst_i128(s, a0, a1, a2, a3, false); break; - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index f77159bdc7..4d52e0bde0 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2101,8 +2101,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_mb: tcg_out_mb(s, a0); break; - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/optimize.c b/tcg/optimize.c index e9e654597d..8d5bad07aa 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -375,10 +375,8 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) switch (ctx->type) { case TCG_TYPE_I32: - new_op = INDEX_op_mov_i32; - break; case TCG_TYPE_I64: - new_op = INDEX_op_mov_i64; + new_op = INDEX_op_mov; break; case TCG_TYPE_V64: case TCG_TYPE_V128: @@ -2933,7 +2931,8 @@ void tcg_optimize(TCGContext *s) case INDEX_op_mb: done = fold_mb(&ctx, op); break; - CASE_OP_32_64_VEC(mov): + case INDEX_op_mov: + case INDEX_op_mov_vec: done = fold_mov(&ctx, op); break; CASE_OP_32_64(movcond): diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index e10c1c5162..11dcfe66f3 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3468,8 +3468,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mb(s, args[0]); break; - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index d525df4e1d..6f9d87df48 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2380,8 +2380,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 8421320928..30fa26e884 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2776,8 +2776,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 787e0d896c..cb5e8d554d 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1512,8 +1512,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_arithi(s, a0, a1, a2, SHIFT_SRA); break; - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 48793ed439..108dc61e9a 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -351,7 +351,7 @@ void tcg_gen_discard_i32(TCGv_i32 arg) void tcg_gen_mov_i32(TCGv_i32 ret, TCGv_i32 arg) { if (ret != arg) { - tcg_gen_op2_i32(INDEX_op_mov_i32, ret, arg); + tcg_gen_op2_i32(INDEX_op_mov, ret, arg); } } @@ -1411,7 +1411,7 @@ void tcg_gen_mov_i64(TCGv_i64 ret, TCGv_i64 arg) return; } if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op2_i64(INDEX_op_mov_i64, ret, arg); + tcg_gen_op2_i64(INDEX_op_mov, ret, arg); } else { TCGTemp *ts = tcgv_i64_temp(arg); diff --git a/tcg/tcg.c b/tcg/tcg.c index e8fd89e4c8..7ff211081d 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2187,7 +2187,9 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i128: return TCG_TARGET_HAS_qemu_ldst_i128; - case INDEX_op_mov_i32: + case INDEX_op_mov: + return has_type; + case INDEX_op_setcond_i32: case INDEX_op_brcond_i32: case INDEX_op_movcond_i32: @@ -2269,7 +2271,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond2_i32: return TCG_TARGET_REG_BITS == 32; - case INDEX_op_mov_i64: case INDEX_op_setcond_i64: case INDEX_op_brcond_i64: case INDEX_op_movcond_i64: @@ -2840,18 +2841,23 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) col += ne_fprintf(f, ",%s", t); } } else { - col += ne_fprintf(f, " %s ", def->name); + if (def->flags & TCG_OPF_INT) { + col += ne_fprintf(f, " %s_i%d ", + def->name, + 8 * tcg_type_size(TCGOP_TYPE(op))); + } else if (def->flags & TCG_OPF_VECTOR) { + col += ne_fprintf(f, "%s v%d,e%d,", + def->name, + 8 * tcg_type_size(TCGOP_TYPE(op)), + 8 << TCGOP_VECE(op)); + } else { + col += ne_fprintf(f, " %s ", def->name); + } nb_oargs = def->nb_oargs; nb_iargs = def->nb_iargs; nb_cargs = def->nb_cargs; - if (def->flags & TCG_OPF_VECTOR) { - col += ne_fprintf(f, "v%d,e%d,", - 8 * tcg_type_size(TCGOP_TYPE(op)), - 8 << TCGOP_VECE(op)); - } - k = 0; for (i = 0; i < nb_oargs; i++) { const char *sep = k ? "," : ""; @@ -4144,8 +4150,7 @@ liveness_pass_1(TCGContext *s) /* Incorporate constraints for this operand. */ switch (opc) { - case INDEX_op_mov_i32: - case INDEX_op_mov_i64: + case INDEX_op_mov: /* Note that these are TCG_OPF_NOT_PRESENT and do not have proper constraints. That said, special case moves to propagate preferences backward. */ @@ -4304,7 +4309,7 @@ liveness_pass_2(TCGContext *s) } /* Outputs become available. */ - if (opc == INDEX_op_mov_i32 || opc == INDEX_op_mov_i64) { + if (opc == INDEX_op_mov) { arg_ts = arg_temp(op->args[0]); dir_ts = arg_ts->state_ptr; if (dir_ts) { @@ -6435,8 +6440,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) TCGOpcode opc = op->opc; switch (opc) { - case INDEX_op_mov_i32: - case INDEX_op_mov_i64: + case INDEX_op_mov: case INDEX_op_mov_vec: tcg_reg_alloc_mov(s, op); break; diff --git a/tcg/tci.c b/tcg/tci.c index 531cd83aae..78183ea47d 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -463,7 +463,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = regs[tmp32 ? r3 : r4]; break; #endif - CASE_32_64(mov) + case INDEX_op_mov: tci_args_rr(insn, &r0, &r1); regs[r0] = regs[r1]; break; @@ -1063,8 +1063,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) op_name, str_r(r0), str_r(r1), s2); break; - case INDEX_op_mov_i32: - case INDEX_op_mov_i64: + case INDEX_op_mov: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_bswap16_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 6f8f1dd8ae..9a5d3c2875 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -483,18 +483,7 @@ static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg val, TCGReg base, static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) { - switch (type) { - case TCG_TYPE_I32: - tcg_out_op_rr(s, INDEX_op_mov_i32, ret, arg); - break; -#if TCG_TARGET_REG_BITS == 64 - case TCG_TYPE_I64: - tcg_out_op_rr(s, INDEX_op_mov_i64, ret, arg); - break; -#endif - default: - g_assert_not_reached(); - } + tcg_out_op_rr(s, INDEX_op_mov, ret, arg); return true; } @@ -799,8 +788,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_v(s, opc); break; - case INDEX_op_mov_i32: /* Always emitted via tcg_out_mov. */ - case INDEX_op_mov_i64: case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ From 662cdbcf0073c4de8456f5e573523571e3d9da5b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 09:00:07 -0800 Subject: [PATCH 0364/2760] tcg: Convert add to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop all backend support for an immediate as the first operand. This should never happen in any case, as we swap commutative operands to place immediates as the second operand. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 51 +++++++------- tcg/arm/tcg-target.c.inc | 43 ++++++++---- tcg/i386/tcg-target.c.inc | 56 +++++++++------- tcg/loongarch64/tcg-target.c.inc | 38 +++++------ tcg/mips/tcg-target.c.inc | 31 ++++++--- tcg/ppc/tcg-target.c.inc | 47 +++++++------ tcg/riscv/tcg-target.c.inc | 39 ++++++----- tcg/s390x/tcg-target.c.inc | 110 +++++++++++++++---------------- tcg/sparc64/tcg-target-con-set.h | 1 + tcg/sparc64/tcg-target.c.inc | 25 +++++-- tcg/tcg.c | 41 +++++++++++- tcg/tci/tcg-target.c.inc | 15 ++++- 12 files changed, 302 insertions(+), 195 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 466042a577..a181b7e65a 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1592,16 +1592,6 @@ static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rn) tcg_out_mov(s, TCG_TYPE_I32, rd, rn); } -static void tcg_out_addsubi(TCGContext *s, int ext, TCGReg rd, - TCGReg rn, int64_t aimm) -{ - if (aimm >= 0) { - tcg_out_insn(s, 3401, ADDI, ext, rd, rn, aimm); - } else { - tcg_out_insn(s, 3401, SUBI, ext, rd, rn, -aimm); - } -} - static void tcg_out_addsub2(TCGContext *s, TCGType ext, TCGReg rl, TCGReg rh, TCGReg al, TCGReg ah, tcg_target_long bl, tcg_target_long bh, @@ -2115,6 +2105,30 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, flush_idcache_range(jmp_rx, jmp_rw, 4); } + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3502, ADD, type, a0, a1, a2); +} + +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 >= 0) { + tcg_out_insn(s, 3401, ADDI, type, a0, a1, a2); + } else { + tcg_out_insn(s, 3401, SUBI, type, a0, a1, -a2); + } +} + +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rA), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2181,23 +2195,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_add_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_add_i64: - if (c2) { - tcg_out_addsubi(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3502, ADD, ext, a0, a1, a2); - } - break; - case INDEX_op_sub_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ case INDEX_op_sub_i64: if (c2) { - tcg_out_addsubi(s, ext, a0, a1, -a2); + tgen_addi(s, ext, a0, a1, -a2); } else { tcg_out_insn(s, 3502, SUB, ext, a0, a1, a2); } @@ -2984,8 +2985,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_add_i32: - case INDEX_op_add_i64: case INDEX_op_sub_i32: case INDEX_op_sub_i64: return C_O1_I2(r, r, rA); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 0fafe97230..a1f2184ac4 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -890,6 +890,17 @@ static void tcg_out_dat_rIK(TCGContext *s, ARMCond cond, ARMInsn opc, } } +static void tcg_out_dat_IN(TCGContext *s, ARMCond cond, ARMInsn opc, + ARMInsn opneg, TCGReg dst, TCGReg lhs, TCGArg rhs) +{ + int imm12 = encode_imm(rhs); + if (imm12 < 0) { + imm12 = encode_imm_nofail(-rhs); + opc = opneg; + } + tcg_out_dat_imm(s, cond, opc, dst, lhs, imm12); +} + static void tcg_out_dat_rIN(TCGContext *s, ARMCond cond, ARMInsn opc, ARMInsn opneg, TCGReg dst, TCGReg lhs, TCGArg rhs, bool rhs_is_const) @@ -898,12 +909,7 @@ static void tcg_out_dat_rIN(TCGContext *s, ARMCond cond, ARMInsn opc, * rhs must satisfy the "rIN" constraint. */ if (rhs_is_const) { - int imm12 = encode_imm(rhs); - if (imm12 < 0) { - imm12 = encode_imm_nofail(-rhs); - opc = opneg; - } - tcg_out_dat_imm(s, cond, opc, dst, lhs, imm12); + tcg_out_dat_IN(s, cond, opc, opneg, dst, lhs, rhs); } else { tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); } @@ -1821,6 +1827,26 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, flush_idcache_range(jmp_rx, jmp_rw, 4); } + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_ADD, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IN(s, COND_AL, ARITH_ADD, ARITH_SUB, a0, a1, a2); +} + +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rIN), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1869,10 +1895,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_dat_rIK(s, tcg_cond_to_arm_cond[c], ARITH_MOV, ARITH_MVN, args[0], 0, args[3], const_args[3]); break; - case INDEX_op_add_i32: - tcg_out_dat_rIN(s, COND_AL, ARITH_ADD, ARITH_SUB, - args[0], args[1], args[2], const_args[2]); - break; case INDEX_op_sub_i32: if (const_args[1]) { if (const_args[2]) { @@ -2142,7 +2164,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i32: return C_O0_I2(r, r); - case INDEX_op_add_i32: case INDEX_op_sub_i32: case INDEX_op_setcond_i32: case INDEX_op_negsetcond_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 75c8665d74..1115d1e38d 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2562,6 +2562,40 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, /* no need to flush icache explicitly */ } + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + if (a0 == a1) { + tgen_arithr(s, ARITH_ADD + rexw, a0, a2); + } else if (a0 == a2) { + tgen_arithr(s, ARITH_ADD + rexw, a0, a1); + } else { + tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a2, 0, 0); + } +} + +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + if (a0 == a1) { + tgen_arithi(s, ARITH_ADD + rexw, a0, a2, false); + } else { + tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, -1, 0, a2); + } +} + +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, re), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2642,24 +2676,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(add): - /* For 3-operand addition, use LEA. */ - if (a0 != a1) { - TCGArg c3 = 0; - if (const_a2) { - c3 = a2, a2 = -1; - } else if (a0 == a2) { - /* Watch out for dest = src + dest, since we've removed - the matching constraint on the add. */ - tgen_arithr(s, ARITH_ADD + rexw, a0, a1); - break; - } - - tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a2, 0, c3); - break; - } - c = ARITH_ADD; - goto gen_arith; OP_32_64(sub): c = ARITH_SUB; goto gen_arith; @@ -3599,10 +3615,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_add_i32: - case INDEX_op_add_i64: - return C_O1_I2(r, r, re); - case INDEX_op_sub_i32: case INDEX_op_sub_i64: case INDEX_op_mul_i32: diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index b0a4ec4c13..fee5e7c577 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1286,6 +1286,24 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, flush_idcache_range(jmp_rx, jmp_rw, 4); } + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_add_w(s, a0, a1, a2); + } else { + tcg_out_opc_add_d(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_add, + .out_rri = tcg_out_addi, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1544,21 +1562,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_add_i32: - if (c2) { - tcg_out_addi(s, TCG_TYPE_I32, a0, a1, a2); - } else { - tcg_out_opc_add_w(s, a0, a1, a2); - } - break; - case INDEX_op_add_i64: - if (c2) { - tcg_out_addi(s, TCG_TYPE_I64, a0, a1, a2); - } else { - tcg_out_opc_add_d(s, a0, a1, a2); - } - break; - case INDEX_op_sub_i32: if (c2) { tcg_out_addi(s, TCG_TYPE_I32, a0, a1, -a2); @@ -2287,11 +2290,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rotr_i64: return C_O1_I2(r, r, ri); - case INDEX_op_add_i32: - return C_O1_I2(r, r, ri); - case INDEX_op_add_i64: - return C_O1_I2(r, r, rJ); - case INDEX_op_and_i32: case INDEX_op_and_i64: case INDEX_op_nor_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 4d52e0bde0..263e7e66c9 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1655,6 +1655,28 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, /* Always indirect, nothing to do */ } + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_ADDU : OPC_DADDU; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_ADDIU : OPC_DADDIU; + tcg_out_opc_imm(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1727,12 +1749,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_add_i32: - i1 = OPC_ADDU, i2 = OPC_ADDIU; - goto do_binary; - case INDEX_op_add_i64: - i1 = OPC_DADDU, i2 = OPC_DADDIU; - goto do_binary; case INDEX_op_or_i32: case INDEX_op_or_i64: i1 = OPC_OR, i2 = OPC_ORI; @@ -2159,9 +2175,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_add_i32: - case INDEX_op_add_i64: - return C_O1_I2(r, r, rJ); case INDEX_op_sub_i32: case INDEX_op_sub_i64: return C_O1_I2(r, rz, rN); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 11dcfe66f3..6b27238499 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2902,6 +2902,26 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, flush_idcache_range(jmp_rx, jmp_rw, 4); } + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, ADD | TAB(a0, a1, a2)); +} + +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mem_long(s, ADDI, ADD, a0, a1, a2); +} + +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rT), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2971,15 +2991,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); break; - case INDEX_op_add_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - do_addi_32: - tcg_out_mem_long(s, ADDI, ADD, a0, a1, (int32_t)a2); - } else { - tcg_out32(s, ADD | TAB(a0, a1, a2)); - } - break; case INDEX_op_sub_i32: a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[1]) { @@ -2989,8 +3000,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); } } else if (const_args[2]) { - a2 = -a2; - goto do_addi_32; + tgen_addi(s, type, a0, a1, (int32_t)-a2); } else { tcg_out32(s, SUBF | TAB(a0, a2, a1)); } @@ -3185,15 +3195,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, NOR | SAB(args[1], args[0], args[1])); break; - case INDEX_op_add_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - do_addi_64: - tcg_out_mem_long(s, ADDI, ADD, a0, a1, a2); - } else { - tcg_out32(s, ADD | TAB(a0, a1, a2)); - } - break; case INDEX_op_sub_i64: a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[1]) { @@ -3203,8 +3204,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); } } else if (const_args[2]) { - a2 = -a2; - goto do_addi_64; + tgen_addi(s, type, a0, a1, -a2); } else { tcg_out32(s, SUBF | TAB(a0, a2, a1)); } @@ -4129,7 +4129,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_add_i32: case INDEX_op_and_i32: case INDEX_op_or_i32: case INDEX_op_xor_i32: @@ -4176,8 +4175,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i32: return C_O1_I2(r, rI, ri); - case INDEX_op_add_i64: - return C_O1_I2(r, r, rT); case INDEX_op_or_i64: case INDEX_op_xor_i64: return C_O1_I2(r, r, rU); diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 6f9d87df48..135137ff53 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1957,6 +1957,28 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, flush_idcache_range(jmp_rx, jmp_rw, 4); } + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_ADDW : OPC_ADD; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_ADDIW : OPC_ADDI; + tcg_out_opc_imm(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2019,21 +2041,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_add_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_ADDIW, a0, a1, a2); - } else { - tcg_out_opc_reg(s, OPC_ADDW, a0, a1, a2); - } - break; - case INDEX_op_add_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_ADDI, a0, a1, a2); - } else { - tcg_out_opc_reg(s, OPC_ADD, a0, a1, a2); - } - break; - case INDEX_op_sub_i32: if (c2) { tcg_out_opc_imm(s, OPC_ADDIW, a0, a1, -a2); @@ -2657,11 +2664,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_add_i32: case INDEX_op_and_i32: case INDEX_op_or_i32: case INDEX_op_xor_i32: - case INDEX_op_add_i64: case INDEX_op_and_i64: case INDEX_op_or_i64: case INDEX_op_xor_i64: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 30fa26e884..f5441d2033 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2145,6 +2145,58 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, /* no need to flush icache explicitly */ } + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (a0 != a1) { + tcg_out_insn(s, RX, LA, a0, a1, a2, 0); + } else if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, AR, a0, a2); + } else { + tcg_out_insn(s, RRE, AGR, a0, a2); + } +} + +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a0 == a1) { + if (type == TCG_TYPE_I32) { + if (a2 == (int16_t)a2) { + tcg_out_insn(s, RI, AHI, a0, a2); + } else { + tcg_out_insn(s, RIL, AFI, a0, a2); + } + return; + } + if (a2 == (int16_t)a2) { + tcg_out_insn(s, RI, AGHI, a0, a2); + return; + } + if (a2 == (int32_t)a2) { + tcg_out_insn(s, RIL, AGFI, a0, a2); + return; + } + if (a2 == (uint32_t)a2) { + tcg_out_insn(s, RIL, ALGFI, a0, a2); + return; + } + if (-a2 == (uint32_t)-a2) { + tcg_out_insn(s, RIL, SLGFI, a0, -a2); + return; + } + } + tcg_out_mem(s, RX_LA, RXY_LAY, a0, a1, TCG_REG_NONE, a2); +} + +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; + + # define OP_32_64(x) \ case glue(glue(INDEX_op_,x),_i32): \ case glue(glue(INDEX_op_,x),_i64) @@ -2201,30 +2253,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_add_i32: - a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; - if (const_args[2]) { - do_addi_32: - if (a0 == a1) { - if (a2 == (int16_t)a2) { - tcg_out_insn(s, RI, AHI, a0, a2); - break; - } - tcg_out_insn(s, RIL, AFI, a0, a2); - break; - } - tcg_out_mem(s, RX_LA, RXY_LAY, a0, a1, TCG_REG_NONE, a2); - } else if (a0 == a1) { - tcg_out_insn(s, RR, AR, a0, a2); - } else { - tcg_out_insn(s, RX, LA, a0, a1, a2, 0); - } - break; case INDEX_op_sub_i32: - a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; + a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - a2 = -a2; - goto do_addi_32; + tgen_addi(s, type, a0, a1, (int32_t)-a2); } else if (a0 == a1) { tcg_out_insn(s, RR, SR, a0, a2); } else { @@ -2494,40 +2526,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); break; - case INDEX_op_add_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - do_addi_64: - if (a0 == a1) { - if (a2 == (int16_t)a2) { - tcg_out_insn(s, RI, AGHI, a0, a2); - break; - } - if (a2 == (int32_t)a2) { - tcg_out_insn(s, RIL, AGFI, a0, a2); - break; - } - if (a2 == (uint32_t)a2) { - tcg_out_insn(s, RIL, ALGFI, a0, a2); - break; - } - if (-a2 == (uint32_t)-a2) { - tcg_out_insn(s, RIL, SLGFI, a0, -a2); - break; - } - } - tcg_out_mem(s, RX_LA, RXY_LAY, a0, a1, TCG_REG_NONE, a2); - } else if (a0 == a1) { - tcg_out_insn(s, RRE, AGR, a0, a2); - } else { - tcg_out_insn(s, RX, LA, a0, a1, a2, 0); - } - break; case INDEX_op_sub_i64: a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { - a2 = -a2; - goto do_addi_64; + tgen_addi(s, type, a0, a1, -a2); } else { tcg_out_insn(s, RRFa, SGRK, a0, a1, a2); } @@ -3253,8 +3255,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_add_i32: - case INDEX_op_add_i64: case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: diff --git a/tcg/sparc64/tcg-target-con-set.h b/tcg/sparc64/tcg-target-con-set.h index 61f9fa3d9f..d90ba11443 100644 --- a/tcg/sparc64/tcg-target-con-set.h +++ b/tcg/sparc64/tcg-target-con-set.h @@ -14,6 +14,7 @@ C_O0_I2(rz, r) C_O0_I2(rz, rJ) C_O1_I1(r, r) C_O1_I2(r, r, r) +C_O1_I2(r, r, rJ) C_O1_I2(r, rz, rJ) C_O1_I4(r, rz, rJ, rI, 0) C_O2_I2(r, r, rz, rJ) diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index cb5e8d554d..f43d95b025 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1285,6 +1285,26 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, { } + +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_ADD); +} + +static void tgen_addi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_arithi(s, a0, a1, a2, ARITH_ADD); +} + +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_add, + .out_rri = tgen_addi, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1338,9 +1358,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_st32_i64: tcg_out_ldst(s, a0, a1, a2, STW); break; - OP_32_64(add): - c = ARITH_ADD; - goto gen_arith; OP_32_64(sub): c = ARITH_SUB; goto gen_arith; @@ -1564,8 +1581,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); - case INDEX_op_add_i32: - case INDEX_op_add_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: case INDEX_op_div_i32: diff --git a/tcg/tcg.c b/tcg/tcg.c index 7ff211081d..18b2981c79 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -978,6 +978,14 @@ typedef struct TCGOutOp { TCGConstraintSetIndex (*dynamic_constraint)(TCGType type, unsigned flags); } TCGOutOp; +typedef struct TCGOutOpBinary { + TCGOutOp base; + void (*out_rrr)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2); + void (*out_rri)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2); +} TCGOutOpBinary; + #include "tcg-target.c.inc" #ifndef CONFIG_TCG_INTERPRETER @@ -987,10 +995,21 @@ QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) - < MIN_TLB_MASK_TABLE_OFS); #endif +/* + * Register V as the TCGOutOp for O. + * This verifies that V is of type T, otherwise give a nice compiler error. + * This prevents trivial mistakes within each arch/tcg-target.c.inc. + */ +#define OUTOP(O, T, V) [O] = _Generic(V, T: &V.base) + /* Register allocation descriptions for every TCGOpcode. */ static const TCGOutOp * const all_outop[NB_OPS] = { + OUTOP(INDEX_op_add_i32, TCGOutOpBinary, outop_add), + OUTOP(INDEX_op_add_i64, TCGOutOpBinary, outop_add), }; +#undef OUTOP + /* * All TCG threads except the parent (i.e. the one that called tcg_context_init * and registered the target's TCG globals) must register with this function @@ -5414,6 +5433,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } /* emit instruction */ + TCGType type = TCGOP_TYPE(op); switch (op->opc) { case INDEX_op_ext_i32_i64: tcg_out_exts_i32_i64(s, new_args[0], new_args[1]); @@ -5424,12 +5444,29 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_extrl_i64_i32: tcg_out_extrl_i64_i32(s, new_args[0], new_args[1]); break; + + case INDEX_op_add_i32: + case INDEX_op_add_i64: + { + const TCGOutOpBinary *out = + container_of(all_outop[op->opc], TCGOutOpBinary, base); + + /* Constants should never appear in the first source operand. */ + tcg_debug_assert(!const_args[1]); + if (const_args[2]) { + out->out_rri(s, type, new_args[0], new_args[1], new_args[2]); + } else { + out->out_rrr(s, type, new_args[0], new_args[1], new_args[2]); + } + } + break; + default: if (def->flags & TCG_OPF_VECTOR) { - tcg_out_vec_op(s, op->opc, TCGOP_TYPE(op) - TCG_TYPE_V64, + tcg_out_vec_op(s, op->opc, type - TCG_TYPE_V64, TCGOP_VECE(op), new_args, const_args); } else { - tcg_out_op(s, op->opc, TCGOP_TYPE(op), new_args, const_args); + tcg_out_op(s, op->opc, type, new_args, const_args); } break; } diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 9a5d3c2875..e6ec31e351 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -91,8 +91,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rem_i64: case INDEX_op_remu_i32: case INDEX_op_remu_i64: - case INDEX_op_add_i32: - case INDEX_op_add_i64: case INDEX_op_sub_i32: case INDEX_op_sub_i64: case INDEX_op_mul_i32: @@ -643,6 +641,18 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, /* Always indirect, nothing to do */ } +static void tgen_add(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, glue(INDEX_op_add_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_add = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_add, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -684,7 +694,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, opc, args[0], args[1], args[2]); break; - CASE_32_64(add) CASE_32_64(sub) CASE_32_64(mul) CASE_32_64(and) From 79602f632a20228fc963161cd53f7d5f6a3bd953 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 09:11:39 -0800 Subject: [PATCH 0365/2760] tcg: Merge INDEX_op_add_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rely on TCGOP_TYPE instead of opcodes specific to each type. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 4 ++-- target/sh4/translate.c | 6 +++--- tcg/optimize.c | 13 +++++-------- tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 15 +++++---------- tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 6 ++---- 8 files changed, 22 insertions(+), 33 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index e6ccc78fa1..67387bfddf 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -261,7 +261,7 @@ Arithmetic .. list-table:: - * - add_i32/i64 *t0*, *t1*, *t2* + * - add *t0*, *t1*, *t2* - | *t0* = *t1* + *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 766fd00d99..0282779468 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -39,6 +39,8 @@ DEF(mb, 0, 0, 1, TCG_OPF_NOT_PRESENT) DEF(mov, 1, 1, 0, TCG_OPF_INT | TCG_OPF_NOT_PRESENT) +DEF(add, 1, 2, 0, TCG_OPF_INT) + DEF(setcond_i32, 1, 2, 1, 0) DEF(negsetcond_i32, 1, 2, 1, 0) DEF(movcond_i32, 1, 4, 1, 0) @@ -52,7 +54,6 @@ DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* arith */ -DEF(add_i32, 1, 2, 0, 0) DEF(sub_i32, 1, 2, 0, 0) DEF(mul_i32, 1, 2, 0, 0) DEF(div_i32, 1, 2, 0, 0) @@ -115,7 +116,6 @@ DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* arith */ -DEF(add_i64, 1, 2, 0, 0) DEF(sub_i64, 1, 2, 0, 0) DEF(mul_i64, 1, 2, 0, 0) DEF(div_i64, 1, 2, 0, 0) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index d796ad52c4..c20204cb52 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -1940,7 +1940,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) NEXT_INSN; switch (ctx->opcode & 0xf00f) { case 0x300c: /* add Rm,Rn */ - op_opc = INDEX_op_add_i32; + op_opc = INDEX_op_add; goto do_reg_op; case 0x2009: /* and Rm,Rn */ op_opc = INDEX_op_and_i32; @@ -1984,7 +1984,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) if (op_dst != B11_8 || mv_src >= 0) { goto fail; } - op_opc = INDEX_op_add_i32; + op_opc = INDEX_op_add; op_arg = tcg_constant_i32(B7_0s); break; @@ -2087,7 +2087,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) ctx->memidx, ld_mop); break; - case INDEX_op_add_i32: + case INDEX_op_add: if (op_dst != st_src) { goto fail; } diff --git a/tcg/optimize.c b/tcg/optimize.c index 8d5bad07aa..a53e4f4675 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -424,7 +424,7 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) uint64_t l64, h64; switch (op) { - CASE_OP_32_64(add): + case INDEX_op_add: return x + y; CASE_OP_32_64(sub): @@ -2261,7 +2261,7 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) break; } if (convert) { - TCGOpcode add_opc, xor_opc, neg_opc; + TCGOpcode xor_opc, neg_opc; if (!inv && !neg) { return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); @@ -2269,12 +2269,10 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) switch (ctx->type) { case TCG_TYPE_I32: - add_opc = INDEX_op_add_i32; neg_opc = INDEX_op_neg_i32; xor_opc = INDEX_op_xor_i32; break; case TCG_TYPE_I64: - add_opc = INDEX_op_add_i64; neg_opc = INDEX_op_neg_i64; xor_opc = INDEX_op_xor_i64; break; @@ -2285,7 +2283,7 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) if (!inv) { op->opc = neg_opc; } else if (neg) { - op->opc = add_opc; + op->opc = INDEX_op_add; op->args[2] = arg_new_constant(ctx, -1); } else { op->opc = xor_opc; @@ -2650,8 +2648,7 @@ static bool fold_sub(OptContext *ctx, TCGOp *op) if (arg_is_const(op->args[2])) { uint64_t val = arg_info(op->args[2])->val; - op->opc = (ctx->type == TCG_TYPE_I32 - ? INDEX_op_add_i32 : INDEX_op_add_i64); + op->opc = INDEX_op_add; op->args[2] = arg_new_constant(ctx, -val); } return finish_folding(ctx, op); @@ -2842,7 +2839,7 @@ void tcg_optimize(TCGContext *s) * Sorted alphabetically by opcode as much as possible. */ switch (opc) { - CASE_OP_32_64(add): + case INDEX_op_add: done = fold_add(&ctx, op); break; case INDEX_op_add_vec: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 108dc61e9a..344d490966 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -362,7 +362,7 @@ void tcg_gen_movi_i32(TCGv_i32 ret, int32_t arg) void tcg_gen_add_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - tcg_gen_op3_i32(INDEX_op_add_i32, ret, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_add, ret, arg1, arg2); } void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) @@ -1555,7 +1555,7 @@ void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op3_i64(INDEX_op_add_i64, ret, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_add, ret, arg1, arg2); } else { tcg_gen_add2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); diff --git a/tcg/tcg.c b/tcg/tcg.c index 18b2981c79..0f0a3f56d8 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1004,8 +1004,7 @@ QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) - /* Register allocation descriptions for every TCGOpcode. */ static const TCGOutOp * const all_outop[NB_OPS] = { - OUTOP(INDEX_op_add_i32, TCGOutOpBinary, outop_add), - OUTOP(INDEX_op_add_i64, TCGOutOpBinary, outop_add), + OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), }; #undef OUTOP @@ -2206,6 +2205,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i128: return TCG_TARGET_HAS_qemu_ldst_i128; + case INDEX_op_add: case INDEX_op_mov: return has_type; @@ -2220,7 +2220,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: - case INDEX_op_add_i32: case INDEX_op_sub_i32: case INDEX_op_neg_i32: case INDEX_op_mul_i32: @@ -2304,7 +2303,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st16_i64: case INDEX_op_st32_i64: case INDEX_op_st_i64: - case INDEX_op_add_i64: case INDEX_op_sub_i64: case INDEX_op_neg_i64: case INDEX_op_mul_i64: @@ -4015,14 +4013,12 @@ liveness_pass_1(TCGContext *s) break; case INDEX_op_add2_i32: - opc_new = INDEX_op_add_i32; + case INDEX_op_add2_i64: + opc_new = INDEX_op_add; goto do_addsub2; case INDEX_op_sub2_i32: opc_new = INDEX_op_sub_i32; goto do_addsub2; - case INDEX_op_add2_i64: - opc_new = INDEX_op_add_i64; - goto do_addsub2; case INDEX_op_sub2_i64: opc_new = INDEX_op_sub_i64; do_addsub2: @@ -5445,8 +5441,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) tcg_out_extrl_i64_i32(s, new_args[0], new_args[1]); break; - case INDEX_op_add_i32: - case INDEX_op_add_i64: + case INDEX_op_add: { const TCGOutOpBinary *out = container_of(all_outop[op->opc], TCGOutOpBinary, base); diff --git a/tcg/tci.c b/tcg/tci.c index 78183ea47d..ceb791a735 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -523,7 +523,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, /* Arithmetic operations (mixed 32/64 bit). */ - CASE_32_64(add) + case INDEX_op_add: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] + regs[r2]; break; @@ -1082,8 +1082,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) op_name, str_r(r0), str_r(r1)); break; - case INDEX_op_add_i32: - case INDEX_op_add_i64: + case INDEX_op_add: case INDEX_op_sub_i32: case INDEX_op_sub_i64: case INDEX_op_mul_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index e6ec31e351..726b645da8 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -453,9 +453,7 @@ static void tcg_out_ldst(TCGContext *s, TCGOpcode op, TCGReg val, stack_bounds_check(base, offset); if (offset != sextract32(offset, 0, 16)) { tcg_out_movi(s, TCG_TYPE_PTR, TCG_REG_TMP, offset); - tcg_out_op_rrr(s, (TCG_TARGET_REG_BITS == 32 - ? INDEX_op_add_i32 : INDEX_op_add_i64), - TCG_REG_TMP, TCG_REG_TMP, base); + tcg_out_op_rrr(s, INDEX_op_add, TCG_REG_TMP, TCG_REG_TMP, base); base = TCG_REG_TMP; offset = 0; } @@ -644,7 +642,7 @@ void tb_target_set_jmp_target(const TranslationBlock *tb, int n, static void tgen_add(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_op_rrr(s, glue(INDEX_op_add_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_add, a0, a1, a2); } static const TCGOutOpBinary outop_add = { From bf40f915fc24b97e27e852f0eae5f59eaf63de59 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 10:22:29 -0800 Subject: [PATCH 0366/2760] tcg: Convert and to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop all backend support for an immediate as the first operand. This should never happen in any case, as we swap commutative operands to place immediates as the second operand. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 31 ++++++++++-------- tcg/arm/tcg-target.c.inc | 41 +++++++++++++++++------- tcg/i386/tcg-target.c.inc | 27 ++++++++++++---- tcg/loongarch64/tcg-target.c.inc | 29 ++++++++++------- tcg/mips/tcg-target.c.inc | 55 +++++++++++++++++++------------- tcg/ppc/tcg-target.c.inc | 40 ++++++++++++----------- tcg/riscv/tcg-target.c.inc | 29 ++++++++++------- tcg/s390x/tcg-target.c.inc | 48 +++++++++++++++------------- tcg/sparc64/tcg-target.c.inc | 23 ++++++++++--- tcg/tcg.c | 4 +++ tcg/tci/tcg-target.c.inc | 14 ++++++-- 11 files changed, 216 insertions(+), 125 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index a181b7e65a..b7d11887e3 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2128,6 +2128,24 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3510, AND, type, a0, a1, a2); +} + +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_logicali(s, I3404_ANDI, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rL), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2209,17 +2227,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_insn(s, 3502, SUB, ext, a0, TCG_REG_XZR, a1); break; - case INDEX_op_and_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_and_i64: - if (c2) { - tcg_out_logicali(s, I3404_ANDI, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3510, AND, ext, a0, a1, a2); - } - break; - case INDEX_op_andc_i32: a2 = (int32_t)a2; /* FALLTHRU */ @@ -3009,8 +3016,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_mulsh_i64: return C_O1_I2(r, r, r); - case INDEX_op_and_i32: - case INDEX_op_and_i64: case INDEX_op_or_i32: case INDEX_op_or_i64: case INDEX_op_xor_i32: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index a1f2184ac4..cb4b2becef 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -874,17 +874,23 @@ static void tcg_out_dat_rI(TCGContext *s, ARMCond cond, ARMInsn opc, * Emit either the reg,imm or reg,reg form of a data-processing insn. * rhs must satisfy the "rIK" constraint. */ +static void tcg_out_dat_IK(TCGContext *s, ARMCond cond, ARMInsn opc, + ARMInsn opinv, TCGReg dst, TCGReg lhs, TCGArg rhs) +{ + int imm12 = encode_imm(rhs); + if (imm12 < 0) { + imm12 = encode_imm_nofail(~rhs); + opc = opinv; + } + tcg_out_dat_imm(s, cond, opc, dst, lhs, imm12); +} + static void tcg_out_dat_rIK(TCGContext *s, ARMCond cond, ARMInsn opc, ARMInsn opinv, TCGReg dst, TCGReg lhs, TCGArg rhs, bool rhs_is_const) { if (rhs_is_const) { - int imm12 = encode_imm(rhs); - if (imm12 < 0) { - imm12 = encode_imm_nofail(~rhs); - opc = opinv; - } - tcg_out_dat_imm(s, cond, opc, dst, lhs, imm12); + tcg_out_dat_IK(s, cond, opc, opinv, dst, lhs, rhs); } else { tcg_out_dat_reg(s, cond, opc, dst, lhs, rhs, SHIFT_IMM_LSL(0)); } @@ -1846,6 +1852,24 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_AND, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IK(s, COND_AL, ARITH_AND, ARITH_BIC, a0, a1, a2); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rIK), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1908,10 +1932,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[0], args[1], args[2], const_args[2]); } break; - case INDEX_op_and_i32: - tcg_out_dat_rIK(s, COND_AL, ARITH_AND, ARITH_BIC, - args[0], args[1], args[2], const_args[2]); - break; case INDEX_op_andc_i32: tcg_out_dat_rIK(s, COND_AL, ARITH_BIC, ARITH_AND, args[0], args[1], args[2], const_args[2]); @@ -2169,7 +2189,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i32: return C_O1_I2(r, r, rIN); - case INDEX_op_and_i32: case INDEX_op_andc_i32: case INDEX_op_clz_i32: case INDEX_op_ctz_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 1115d1e38d..4f4c5ebbb1 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2595,6 +2595,26 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_AND + rexw, a0, a2); +} + +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_AND + rexw, a0, a2, false); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, 0, reZ), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2679,9 +2699,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, OP_32_64(sub): c = ARITH_SUB; goto gen_arith; - OP_32_64(and): - c = ARITH_AND; - goto gen_arith; OP_32_64(or): c = ARITH_OR; goto gen_arith; @@ -3625,10 +3642,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_xor_i64: return C_O1_I2(r, 0, re); - case INDEX_op_and_i32: - case INDEX_op_and_i64: - return C_O1_I2(r, 0, reZ); - case INDEX_op_andc_i32: case INDEX_op_andc_i64: return C_O1_I2(r, r, rI); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index fee5e7c577..b9c6e0d017 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1303,6 +1303,24 @@ static const TCGOutOpBinary outop_add = { .out_rri = tcg_out_addi, }; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_and(s, a0, a1, a2); +} + +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_andi(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rU), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1373,15 +1391,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_and_i32: - case INDEX_op_and_i64: - if (c2) { - tcg_out_opc_andi(s, a0, a1, a2); - } else { - tcg_out_opc_and(s, a0, a1, a2); - } - break; - case INDEX_op_or_i32: case INDEX_op_or_i64: if (c2) { @@ -2290,8 +2299,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rotr_i64: return C_O1_I2(r, r, ri); - case INDEX_op_and_i32: - case INDEX_op_and_i64: case INDEX_op_nor_i32: case INDEX_op_nor_i64: case INDEX_op_or_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 263e7e66c9..460f73d06a 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1676,6 +1676,38 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_AND, a0, a1, a2); +} + +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int msb; + + if (a2 == (uint16_t)a2) { + tcg_out_opc_imm(s, OPC_ANDI, a0, a1, a2); + return; + } + + tcg_debug_assert(use_mips32r2_instructions); + tcg_debug_assert(is_p2m1(a2)); + msb = ctz64(~a2) - 1; + if (type == TCG_TYPE_I32) { + tcg_out_opc_bf(s, OPC_EXT, a0, a1, msb, 0); + } else { + tcg_out_opc_bf64(s, OPC_DEXT, OPC_DEXTM, OPC_DEXTU, a0, a1, msb, 0); + } +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rIK), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1776,26 +1808,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; } goto do_binaryv; - case INDEX_op_and_i32: - if (c2 && a2 != (uint16_t)a2) { - int msb = ctz32(~a2) - 1; - tcg_debug_assert(use_mips32r2_instructions); - tcg_debug_assert(is_p2m1(a2)); - tcg_out_opc_bf(s, OPC_EXT, a0, a1, msb, 0); - break; - } - i1 = OPC_AND, i2 = OPC_ANDI; - goto do_binary; - case INDEX_op_and_i64: - if (c2 && a2 != (uint16_t)a2) { - int msb = ctz64(~a2) - 1; - tcg_debug_assert(use_mips32r2_instructions); - tcg_debug_assert(is_p2m1(a2)); - tcg_out_opc_bf64(s, OPC_DEXT, OPC_DEXTM, OPC_DEXTU, a0, a1, msb, 0); - break; - } - i1 = OPC_AND, i2 = OPC_ANDI; - goto do_binary; case INDEX_op_nor_i32: case INDEX_op_nor_i64: i1 = OPC_NOR; @@ -2202,9 +2214,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i64: case INDEX_op_mulu2_i64: return C_O2_I2(r, r, r, r); - case INDEX_op_and_i32: - case INDEX_op_and_i64: - return C_O1_I2(r, r, rIK); case INDEX_op_or_i32: case INDEX_op_xor_i32: case INDEX_op_or_i64: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 6b27238499..3d34edfa79 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2921,6 +2921,28 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, AND | SAB(a1, a0, a2)); +} + +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_andi32(s, a0, a1, a2); + } else { + tcg_out_andi64(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3006,22 +3028,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_and_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi32(s, a0, a1, a2); - } else { - tcg_out32(s, AND | SAB(a1, a0, a2)); - } - break; - case INDEX_op_and_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi64(s, a0, a1, a2); - } else { - tcg_out32(s, AND | SAB(a1, a0, a2)); - } - break; case INDEX_op_or_i64: case INDEX_op_or_i32: a0 = args[0], a1 = args[1], a2 = args[2]; @@ -4129,7 +4135,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_and_i32: case INDEX_op_or_i32: case INDEX_op_xor_i32: case INDEX_op_andc_i32: @@ -4140,7 +4145,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: - case INDEX_op_and_i64: case INDEX_op_andc_i64: case INDEX_op_shl_i64: case INDEX_op_shr_i64: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 135137ff53..7f585bc4f9 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1978,6 +1978,24 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_AND, a0, a1, a2); +} + +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_imm(s, OPC_ANDI, a0, a1, a2); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2056,15 +2074,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_and_i32: - case INDEX_op_and_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_ANDI, a0, a1, a2); - } else { - tcg_out_opc_reg(s, OPC_AND, a0, a1, a2); - } - break; - case INDEX_op_or_i32: case INDEX_op_or_i64: if (c2) { @@ -2664,10 +2673,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_and_i32: case INDEX_op_or_i32: case INDEX_op_xor_i32: - case INDEX_op_and_i64: case INDEX_op_or_i64: case INDEX_op_xor_i64: case INDEX_op_setcond_i32: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index f5441d2033..d60bdaba25 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2196,6 +2196,31 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, NGRK, a0, a1, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, NR, a0, a2); + } else { + tcg_out_insn(s, RRFa, NRK, a0, a1, a2); + } +} + +static void tgen_andi_3(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + tgen_andi(s, type, a0, a2); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rNKR), + .out_rrr = tgen_and, + .out_rri = tgen_andi_3, +}; + # define OP_32_64(x) \ case glue(glue(INDEX_op_,x),_i32): \ @@ -2264,17 +2289,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_and_i32: - a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - tgen_andi(s, TCG_TYPE_I32, a0, a2); - } else if (a0 == a1) { - tcg_out_insn(s, RR, NR, a0, a2); - } else { - tcg_out_insn(s, RRFa, NRK, a0, a1, a2); - } - break; case INDEX_op_or_i32: a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; if (const_args[2]) { @@ -2535,15 +2549,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_and_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I64, a0, a1); - tgen_andi(s, TCG_TYPE_I64, args[0], args[2]); - } else { - tcg_out_insn(s, RRFa, NGRK, a0, a1, a2); - } - break; case INDEX_op_or_i64: a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { @@ -3274,12 +3279,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i32: case INDEX_op_sub_i64: - case INDEX_op_and_i32: case INDEX_op_or_i32: case INDEX_op_xor_i32: return C_O1_I2(r, r, ri); - case INDEX_op_and_i64: - return C_O1_I2(r, r, rNKR); case INDEX_op_or_i64: case INDEX_op_xor_i64: return C_O1_I2(r, r, rK); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index f43d95b025..b3fbe127c0 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1304,6 +1304,24 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_AND); +} + +static void tgen_andi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_arithi(s, a0, a1, a2, ARITH_AND); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_and, + .out_rri = tgen_andi, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1361,9 +1379,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, OP_32_64(sub): c = ARITH_SUB; goto gen_arith; - OP_32_64(and): - c = ARITH_AND; - goto gen_arith; OP_32_64(andc): c = ARITH_ANDN; goto gen_arith; @@ -1589,8 +1604,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_divu_i64: case INDEX_op_sub_i32: case INDEX_op_sub_i64: - case INDEX_op_and_i32: - case INDEX_op_and_i64: case INDEX_op_andc_i32: case INDEX_op_andc_i64: case INDEX_op_or_i32: diff --git a/tcg/tcg.c b/tcg/tcg.c index 0f0a3f56d8..94574c90c5 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1005,6 +1005,8 @@ QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) - /* Register allocation descriptions for every TCGOpcode. */ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), + OUTOP(INDEX_op_and_i32, TCGOutOpBinary, outop_and), + OUTOP(INDEX_op_and_i64, TCGOutOpBinary, outop_and), }; #undef OUTOP @@ -5442,6 +5444,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_add: + case INDEX_op_and_i32: + case INDEX_op_and_i64: { const TCGOutOpBinary *out = container_of(all_outop[op->opc], TCGOutOpBinary, base); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 726b645da8..fd38ecad39 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -95,8 +95,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_and_i32: - case INDEX_op_and_i64: case INDEX_op_andc_i32: case INDEX_op_andc_i64: case INDEX_op_eqv_i32: @@ -650,6 +648,17 @@ static const TCGOutOpBinary outop_add = { .out_rrr = tgen_add, }; +static void tgen_and(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, glue(INDEX_op_and_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_and = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_and, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -694,7 +703,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, CASE_32_64(sub) CASE_32_64(mul) - CASE_32_64(and) CASE_32_64(or) CASE_32_64(xor) CASE_32_64(andc) /* Optional (TCG_TARGET_HAS_andc_*). */ From c3b920b3d6a685484904d3060f3eb69401051bf0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 10:32:44 -0800 Subject: [PATCH 0367/2760] tcg: Merge INDEX_op_and_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- target/sh4/translate.c | 4 ++-- tcg/optimize.c | 40 ++++++++++++---------------------------- tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 9 +++------ tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 8 files changed, 24 insertions(+), 45 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 67387bfddf..6a8025c0bf 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -303,7 +303,7 @@ Logical .. list-table:: - * - and_i32/i64 *t0*, *t1*, *t2* + * - and *t0*, *t1*, *t2* - | *t0* = *t1* & *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 0282779468..f45029caa7 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -40,6 +40,7 @@ DEF(mb, 0, 0, 1, TCG_OPF_NOT_PRESENT) DEF(mov, 1, 1, 0, TCG_OPF_INT | TCG_OPF_NOT_PRESENT) DEF(add, 1, 2, 0, TCG_OPF_INT) +DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(setcond_i32, 1, 2, 1, 0) DEF(negsetcond_i32, 1, 2, 1, 0) @@ -62,7 +63,6 @@ DEF(rem_i32, 1, 2, 0, 0) DEF(remu_i32, 1, 2, 0, 0) DEF(div2_i32, 2, 3, 0, 0) DEF(divu2_i32, 2, 3, 0, 0) -DEF(and_i32, 1, 2, 0, 0) DEF(or_i32, 1, 2, 0, 0) DEF(xor_i32, 1, 2, 0, 0) /* shifts/rotates */ @@ -124,7 +124,6 @@ DEF(rem_i64, 1, 2, 0, 0) DEF(remu_i64, 1, 2, 0, 0) DEF(div2_i64, 2, 3, 0, 0) DEF(divu2_i64, 2, 3, 0, 0) -DEF(and_i64, 1, 2, 0, 0) DEF(or_i64, 1, 2, 0, 0) DEF(xor_i64, 1, 2, 0, 0) /* shifts/rotates */ diff --git a/target/sh4/translate.c b/target/sh4/translate.c index c20204cb52..3d0eda2128 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -1943,7 +1943,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) op_opc = INDEX_op_add; goto do_reg_op; case 0x2009: /* and Rm,Rn */ - op_opc = INDEX_op_and_i32; + op_opc = INDEX_op_and; goto do_reg_op; case 0x200a: /* xor Rm,Rn */ op_opc = INDEX_op_xor_i32; @@ -2105,7 +2105,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) } break; - case INDEX_op_and_i32: + case INDEX_op_and: if (op_dst != st_src) { goto fail; } diff --git a/tcg/optimize.c b/tcg/optimize.c index a53e4f4675..20cde598fb 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -433,7 +433,8 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) CASE_OP_32_64(mul): return x * y; - CASE_OP_32_64_VEC(and): + case INDEX_op_and: + case INDEX_op_and_vec: return x & y; CASE_OP_32_64_VEC(or): @@ -802,9 +803,7 @@ static int do_constant_folding_cond1(OptContext *ctx, TCGOp *op, TCGArg dest, /* Expand to AND with a temporary if no backend support. */ if (!TCG_TARGET_HAS_tst) { - TCGOpcode and_opc = (ctx->type == TCG_TYPE_I32 - ? INDEX_op_and_i32 : INDEX_op_and_i64); - TCGOp *op2 = opt_insert_before(ctx, op, and_opc, 3); + TCGOp *op2 = opt_insert_before(ctx, op, INDEX_op_and, 3); TCGArg tmp = arg_new_temp(ctx); op2->args[0] = tmp; @@ -897,8 +896,8 @@ static int do_constant_folding_cond2(OptContext *ctx, TCGOp *op, TCGArg *args) /* Expand to AND with a temporary if no backend support. */ if (!TCG_TARGET_HAS_tst && is_tst_cond(c)) { - TCGOp *op1 = opt_insert_before(ctx, op, INDEX_op_and_i32, 3); - TCGOp *op2 = opt_insert_before(ctx, op, INDEX_op_and_i32, 3); + TCGOp *op1 = opt_insert_before(ctx, op, INDEX_op_and, 3); + TCGOp *op2 = opt_insert_before(ctx, op, INDEX_op_and, 3); TCGArg t1 = arg_new_temp(ctx); TCGArg t2 = arg_new_temp(ctx); @@ -1709,8 +1708,7 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) TempOptInfo *t2 = arg_info(op->args[2]); int ofs = op->args[3]; int len = op->args[4]; - int width; - TCGOpcode and_opc; + int width = 8 * tcg_type_size(ctx->type); uint64_t z_mask, s_mask; if (ti_is_const(t1) && ti_is_const(t2)) { @@ -1719,24 +1717,11 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) ti_const_val(t2))); } - switch (ctx->type) { - case TCG_TYPE_I32: - and_opc = INDEX_op_and_i32; - width = 32; - break; - case TCG_TYPE_I64: - and_opc = INDEX_op_and_i64; - width = 64; - break; - default: - g_assert_not_reached(); - } - /* Inserting a value into zero at offset 0. */ if (ti_is_const_val(t1, 0) && ofs == 0) { uint64_t mask = MAKE_64BIT_MASK(0, len); - op->opc = and_opc; + op->opc = INDEX_op_and; op->args[1] = op->args[2]; op->args[2] = arg_new_constant(ctx, mask); return fold_and(ctx, op); @@ -1746,7 +1731,7 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) if (ti_is_const_val(t2, 0)) { uint64_t mask = deposit64(-1, ofs, len, 0); - op->opc = and_opc; + op->opc = INDEX_op_and; op->args[2] = arg_new_constant(ctx, mask); return fold_and(ctx, op); } @@ -2297,7 +2282,7 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) { - TCGOpcode and_opc, sub_opc, xor_opc, neg_opc, shr_opc; + TCGOpcode sub_opc, xor_opc, neg_opc, shr_opc; TCGOpcode uext_opc = 0, sext_opc = 0; TCGCond cond = op->args[3]; TCGArg ret, src1, src2; @@ -2319,7 +2304,6 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) switch (ctx->type) { case TCG_TYPE_I32: - and_opc = INDEX_op_and_i32; sub_opc = INDEX_op_sub_i32; xor_opc = INDEX_op_xor_i32; shr_opc = INDEX_op_shr_i32; @@ -2332,7 +2316,6 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) } break; case TCG_TYPE_I64: - and_opc = INDEX_op_and_i64; sub_opc = INDEX_op_sub_i64; xor_opc = INDEX_op_xor_i64; shr_opc = INDEX_op_shr_i64; @@ -2371,7 +2354,7 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) op2->args[2] = arg_new_constant(ctx, sh); src1 = ret; } - op->opc = and_opc; + op->opc = INDEX_op_and; op->args[1] = src1; op->args[2] = arg_new_constant(ctx, 1); } @@ -2848,7 +2831,8 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(add2): done = fold_add2(&ctx, op); break; - CASE_OP_32_64_VEC(and): + case INDEX_op_and: + case INDEX_op_and_vec: done = fold_and(&ctx, op); break; CASE_OP_32_64_VEC(andc): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 344d490966..82f3ad501f 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -401,7 +401,7 @@ void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - tcg_gen_op3_i32(INDEX_op_and_i32, ret, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_and, ret, arg1, arg2); } void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) @@ -1575,7 +1575,7 @@ void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op3_i64(INDEX_op_and_i64, ret, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_and, ret, arg1, arg2); } else { tcg_gen_and_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_and_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); diff --git a/tcg/tcg.c b/tcg/tcg.c index 94574c90c5..d60427eb7f 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1005,8 +1005,7 @@ QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) - /* Register allocation descriptions for every TCGOpcode. */ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), - OUTOP(INDEX_op_and_i32, TCGOutOpBinary, outop_and), - OUTOP(INDEX_op_and_i64, TCGOutOpBinary, outop_and), + OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), }; #undef OUTOP @@ -2208,6 +2207,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_qemu_ldst_i128; case INDEX_op_add: + case INDEX_op_and: case INDEX_op_mov: return has_type; @@ -2225,7 +2225,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i32: case INDEX_op_neg_i32: case INDEX_op_mul_i32: - case INDEX_op_and_i32: case INDEX_op_or_i32: case INDEX_op_xor_i32: case INDEX_op_shl_i32: @@ -2308,7 +2307,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: case INDEX_op_neg_i64: case INDEX_op_mul_i64: - case INDEX_op_and_i64: case INDEX_op_or_i64: case INDEX_op_xor_i64: case INDEX_op_shl_i64: @@ -5444,8 +5442,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_add: - case INDEX_op_and_i32: - case INDEX_op_and_i64: + case INDEX_op_and: { const TCGOutOpBinary *out = container_of(all_outop[op->opc], TCGOutOpBinary, base); diff --git a/tcg/tci.c b/tcg/tci.c index ceb791a735..8762a99fb6 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -535,7 +535,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] * regs[r2]; break; - CASE_32_64(and) + case INDEX_op_and: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] & regs[r2]; break; @@ -1083,12 +1083,11 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) break; case INDEX_op_add: + case INDEX_op_and: case INDEX_op_sub_i32: case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_and_i32: - case INDEX_op_and_i64: case INDEX_op_or_i32: case INDEX_op_or_i64: case INDEX_op_xor_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index fd38ecad39..b0141f8ed6 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -651,7 +651,7 @@ static const TCGOutOpBinary outop_add = { static void tgen_and(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_op_rrr(s, glue(INDEX_op_and_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_and, a0, a1, a2); } static const TCGOutOpBinary outop_and = { From 899281c8f589cce55951e13307661a7253eb4909 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 Nov 2023 11:18:55 -0800 Subject: [PATCH 0368/2760] tcg/optimize: Fold andc with immediate to and MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tcg/optimize.c b/tcg/optimize.c index 20cde598fb..1f6fdee734 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1343,6 +1343,25 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) t2 = arg_info(op->args[2]); z_mask = t1->z_mask; + if (ti_is_const(t2)) { + /* Fold andc r,x,i to and r,x,~i. */ + switch (ctx->type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + op->opc = INDEX_op_and; + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + op->opc = INDEX_op_and_vec; + break; + default: + g_assert_not_reached(); + } + op->args[2] = arg_new_constant(ctx, ~ti_const_val(t2)); + return fold_and(ctx, op); + } + /* * Known-zeros does not imply known-ones. Therefore unless * arg2 is constant, we can't infer anything from it. From 93a9ddb84ae534a04e8d7764caa9e29b8285b2b1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 22:06:08 -0800 Subject: [PATCH 0369/2760] tcg/optimize: Emit add r,r,-1 in fold_setcond_tst_pow2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We canonicalize subtract with constant to add with constant. Fix this missed instance. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 1f6fdee734..6c7b6af624 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2301,7 +2301,7 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) { - TCGOpcode sub_opc, xor_opc, neg_opc, shr_opc; + TCGOpcode xor_opc, neg_opc, shr_opc; TCGOpcode uext_opc = 0, sext_opc = 0; TCGCond cond = op->args[3]; TCGArg ret, src1, src2; @@ -2323,7 +2323,6 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) switch (ctx->type) { case TCG_TYPE_I32: - sub_opc = INDEX_op_sub_i32; xor_opc = INDEX_op_xor_i32; shr_opc = INDEX_op_shr_i32; neg_opc = INDEX_op_neg_i32; @@ -2335,7 +2334,6 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) } break; case TCG_TYPE_I64: - sub_opc = INDEX_op_sub_i64; xor_opc = INDEX_op_xor_i64; shr_opc = INDEX_op_shr_i64; neg_opc = INDEX_op_neg_i64; @@ -2379,10 +2377,10 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) } if (neg && inv) { - op2 = opt_insert_after(ctx, op, sub_opc, 3); + op2 = opt_insert_after(ctx, op, INDEX_op_add, 3); op2->args[0] = ret; op2->args[1] = ret; - op2->args[2] = arg_new_constant(ctx, 1); + op2->args[2] = arg_new_constant(ctx, -1); } else if (inv) { op2 = opt_insert_after(ctx, op, xor_opc, 3); op2->args[0] = ret; From a341c84e8153b7282b083e871ca534f15fa70898 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 12:26:28 -0800 Subject: [PATCH 0370/2760] tcg: Convert andc to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At the same time, drop all backend support for immediate operands, as we now transform andc to and during optimize. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 24 ++++++++-------- tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 16 +++++++---- tcg/i386/tcg-target-con-set.h | 2 +- tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 31 +++++++++++--------- tcg/loongarch64/tcg-target-con-set.h | 1 + tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 23 ++++++++------- tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 4 +++ tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 29 ++++++++----------- tcg/riscv/tcg-target-con-set.h | 1 + tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 27 +++++++++++------- tcg/s390x/tcg-target-con-set.h | 1 - tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 42 ++++++++++++++-------------- tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 16 +++++++---- tcg/tcg-has.h | 1 - tcg/tcg-op.c | 4 +-- tcg/tcg.c | 8 +++--- tcg/tci.c | 2 -- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target.c.inc | 14 ++++++++-- 28 files changed, 135 insertions(+), 130 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index bfd587c0fc..851f6b01b4 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -19,7 +19,6 @@ #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 1 #define TCG_TARGET_HAS_nand_i32 0 @@ -45,7 +44,6 @@ #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 1 #define TCG_TARGET_HAS_nand_i64 0 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index b7d11887e3..c7167cad15 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2146,6 +2146,17 @@ static const TCGOutOpBinary outop_and = { .out_rri = tgen_andi, }; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3510, BIC, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_andc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2227,17 +2238,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_insn(s, 3502, SUB, ext, a0, TCG_REG_XZR, a1); break; - case INDEX_op_andc_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_andc_i64: - if (c2) { - tcg_out_logicali(s, I3404_ANDI, ext, a0, a1, ~a2); - } else { - tcg_out_insn(s, 3510, BIC, ext, a0, a1, a2); - } - break; - case INDEX_op_or_i32: a2 = (int32_t)a2; /* FALLTHRU */ @@ -3020,8 +3020,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_or_i64: case INDEX_op_xor_i32: case INDEX_op_xor_i64: - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: case INDEX_op_orc_i32: case INDEX_op_orc_i64: case INDEX_op_eqv_i32: diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 8398c80c8e..0268858a3b 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -28,7 +28,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 0 #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index cb4b2becef..feea82145a 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1870,6 +1870,17 @@ static const TCGOutOpBinary outop_and = { .out_rri = tgen_andi, }; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_BIC, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_andc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1932,10 +1943,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[0], args[1], args[2], const_args[2]); } break; - case INDEX_op_andc_i32: - tcg_out_dat_rIK(s, COND_AL, ARITH_BIC, ARITH_AND, - args[0], args[1], args[2], const_args[2]); - break; case INDEX_op_or_i32: c = ARITH_ORR; goto gen_arith; @@ -2189,7 +2196,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i32: return C_O1_I2(r, r, rIN); - case INDEX_op_andc_i32: case INDEX_op_clz_i32: case INDEX_op_ctz_i32: return C_O1_I2(r, r, rIK); diff --git a/tcg/i386/tcg-target-con-set.h b/tcg/i386/tcg-target-con-set.h index 06e6521001..0ae9775944 100644 --- a/tcg/i386/tcg-target-con-set.h +++ b/tcg/i386/tcg-target-con-set.h @@ -42,9 +42,9 @@ C_O1_I2(r, 0, reZ) C_O1_I2(r, 0, ri) C_O1_I2(r, 0, rI) C_O1_I2(r, L, L) +C_O1_I2(r, r, r) C_O1_I2(r, r, re) C_O1_I2(r, r, ri) -C_O1_I2(r, r, rI) C_O1_I2(x, x, x) C_N1_I2(r, r, r) C_N1_I2(r, r, rW) diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index bbf55c86b6..b29b70357a 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -31,7 +31,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_andc_i32 have_bmi1 #define TCG_TARGET_HAS_orc_i32 0 #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 @@ -57,7 +56,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_andc_i64 have_bmi1 #define TCG_TARGET_HAS_orc_i64 0 #define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 4f4c5ebbb1..33c1fcc717 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2615,6 +2615,24 @@ static const TCGOutOpBinary outop_and = { .out_rri = tgen_andi, }; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_vex_modrm(s, OPC_ANDN + rexw, a0, a2, a1); +} + +static TCGConstraintSetIndex cset_andc(TCGType type, unsigned flags) +{ + return have_bmi1 ? C_O1_I2(r, r, r) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_andc, + .out_rrr = tgen_andc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2713,15 +2731,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(andc): - if (const_a2) { - tcg_out_mov(s, rexw ? TCG_TYPE_I64 : TCG_TYPE_I32, a0, a1); - tgen_arithi(s, ARITH_AND + rexw, a0, ~a2, 0); - } else { - tcg_out_vex_modrm(s, OPC_ANDN + rexw, a0, a2, a1); - } - break; - OP_32_64(mul): if (const_a2) { int32_t val; @@ -3642,10 +3651,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_xor_i64: return C_O1_I2(r, 0, re); - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: - return C_O1_I2(r, r, rI); - case INDEX_op_shl_i32: case INDEX_op_shl_i64: case INDEX_op_shr_i32: diff --git a/tcg/loongarch64/tcg-target-con-set.h b/tcg/loongarch64/tcg-target-con-set.h index 8afaee9476..b7c9b89e9e 100644 --- a/tcg/loongarch64/tcg-target-con-set.h +++ b/tcg/loongarch64/tcg-target-con-set.h @@ -22,6 +22,7 @@ C_O0_I3(r, r, r) C_O1_I1(r, r) C_O1_I1(w, r) C_O1_I1(w, w) +C_O1_I2(r, r, r) C_O1_I2(r, r, rC) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 166c9d7e41..71d91fec19 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -25,7 +25,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 @@ -47,7 +46,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index b9c6e0d017..b7d2984a8a 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1321,6 +1321,17 @@ static const TCGOutOpBinary outop_and = { .out_rri = tgen_andi, }; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_andn(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_andc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1371,16 +1382,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: - if (c2) { - /* guaranteed to fit due to constraint */ - tcg_out_opc_andi(s, a0, a1, ~a2); - } else { - tcg_out_opc_andn(s, a0, a1, a2); - } - break; - case INDEX_op_orc_i32: case INDEX_op_orc_i64: if (c2) { @@ -2276,8 +2277,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: case INDEX_op_orc_i32: case INDEX_op_orc_i64: /* diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index fd96905484..6a6d4377e7 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -43,7 +43,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_nor_i32 1 -#define TCG_TARGET_HAS_andc_i32 0 #define TCG_TARGET_HAS_orc_i32 0 #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 @@ -63,7 +62,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_nor_i64 1 -#define TCG_TARGET_HAS_andc_i64 0 #define TCG_TARGET_HAS_orc_i64 0 #define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 460f73d06a..ab57c78095 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1708,6 +1708,10 @@ static const TCGOutOpBinary outop_and = { .out_rri = tgen_andi, }; +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_NotImplemented, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 9acfc574c5..63bb66f446 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -23,7 +23,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 1 #define TCG_TARGET_HAS_nand_i32 1 @@ -50,7 +49,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 1 #define TCG_TARGET_HAS_nand_i64 1 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 3d34edfa79..7b1a82c9fa 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2943,6 +2943,17 @@ static const TCGOutOpBinary outop_and = { .out_rri = tgen_andi, }; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, ANDC | SAB(a1, a0, a2)); +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_andc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3046,22 +3057,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, XOR | SAB(a1, a0, a2)); } break; - case INDEX_op_andc_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi32(s, a0, a1, ~a2); - } else { - tcg_out32(s, ANDC | SAB(a1, a0, a2)); - } - break; - case INDEX_op_andc_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_andi64(s, a0, a1, ~a2); - } else { - tcg_out32(s, ANDC | SAB(a1, a0, a2)); - } - break; case INDEX_op_orc_i32: if (const_args[2]) { tcg_out_ori32(s, args[0], args[1], ~args[2]); @@ -4137,7 +4132,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_or_i32: case INDEX_op_xor_i32: - case INDEX_op_andc_i32: case INDEX_op_orc_i32: case INDEX_op_eqv_i32: case INDEX_op_shl_i32: @@ -4145,7 +4139,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: - case INDEX_op_andc_i64: case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: diff --git a/tcg/riscv/tcg-target-con-set.h b/tcg/riscv/tcg-target-con-set.h index e92e815491..f1f5d415f7 100644 --- a/tcg/riscv/tcg-target-con-set.h +++ b/tcg/riscv/tcg-target-con-set.h @@ -13,6 +13,7 @@ C_O0_I1(r) C_O0_I2(rz, r) C_O0_I2(rz, rz) C_O1_I1(r, r) +C_O1_I2(r, r, r) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) C_O1_I2(r, r, rJ) diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index fc62049c78..a3918bf7f5 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -25,7 +25,6 @@ #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_andc_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_orc_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_eqv_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_nand_i32 0 @@ -46,7 +45,6 @@ #define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_andc_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_orc_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_eqv_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_nand_i64 0 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 7f585bc4f9..f637604e98 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1996,6 +1996,23 @@ static const TCGOutOpBinary outop_and = { .out_rri = tgen_andi, }; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_ANDN, a0, a1, a2); +} + +static TCGConstraintSetIndex cset_zbb_rrr(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_ZBB ? C_O1_I2(r, r, r) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_andc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2092,14 +2109,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_ANDI, a0, a1, ~a2); - } else { - tcg_out_opc_reg(s, OPC_ANDN, a0, a1, a2); - } - break; case INDEX_op_orc_i32: case INDEX_op_orc_i64: if (c2) { @@ -2683,8 +2692,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: case INDEX_op_orc_i32: case INDEX_op_orc_i64: case INDEX_op_eqv_i32: diff --git a/tcg/s390x/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h index 370e4b1295..39903a60ad 100644 --- a/tcg/s390x/tcg-target-con-set.h +++ b/tcg/s390x/tcg-target-con-set.h @@ -31,7 +31,6 @@ C_O1_I2(r, r, rC) C_O1_I2(r, r, rI) C_O1_I2(r, r, rJ) C_O1_I2(r, r, rK) -C_O1_I2(r, r, rKR) C_O1_I2(r, r, rNK) C_O1_I2(r, r, rNKR) C_O1_I2(r, rZ, r) diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index aea805455f..15ec0dc2ff 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -34,7 +34,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_andc_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_orc_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_eqv_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_nand_i32 HAVE_FACILITY(MISC_INSN_EXT3) @@ -59,7 +58,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_andc_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_orc_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_eqv_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_nand_i64 HAVE_FACILITY(MISC_INSN_EXT3) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index d60bdaba25..e4b60d1924 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2221,6 +2221,27 @@ static const TCGOutOpBinary outop_and = { .out_rri = tgen_andi_3, }; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, NCRK, a0, a1, a2); + } else { + tcg_out_insn(s, RRFa, NCGRK, a0, a1, a2); + } +} + +static TCGConstraintSetIndex cset_misc3_rrr(TCGType type, unsigned flags) +{ + return HAVE_FACILITY(MISC_INSN_EXT3) ? C_O1_I2(r, r, r) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_misc3_rrr, + .out_rrr = tgen_andc, +}; + # define OP_32_64(x) \ case glue(glue(INDEX_op_,x),_i32): \ @@ -2312,15 +2333,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_andc_i32: - a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - tgen_andi(s, TCG_TYPE_I32, a0, (uint32_t)~a2); - } else { - tcg_out_insn(s, RRFa, NCRK, a0, a1, a2); - } - break; case INDEX_op_orc_i32: a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; if (const_args[2]) { @@ -2568,15 +2580,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_andc_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I64, a0, a1); - tgen_andi(s, TCG_TYPE_I64, a0, ~a2); - } else { - tcg_out_insn(s, RRFa, NCGRK, a0, a1, a2); - } - break; case INDEX_op_orc_i64: a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { @@ -3286,12 +3289,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_xor_i64: return C_O1_I2(r, r, rK); - case INDEX_op_andc_i32: case INDEX_op_orc_i32: case INDEX_op_eqv_i32: return C_O1_I2(r, r, ri); - case INDEX_op_andc_i64: - return C_O1_I2(r, r, rKR); case INDEX_op_orc_i64: case INDEX_op_eqv_i64: return C_O1_I2(r, r, rNK); diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index ad6f35da17..510b9e64a4 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -20,7 +20,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 @@ -46,7 +45,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index b3fbe127c0..fe9175aa1a 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1322,6 +1322,17 @@ static const TCGOutOpBinary outop_and = { .out_rri = tgen_andi, }; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_ANDN); +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_andc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1379,9 +1390,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, OP_32_64(sub): c = ARITH_SUB; goto gen_arith; - OP_32_64(andc): - c = ARITH_ANDN; - goto gen_arith; OP_32_64(or): c = ARITH_OR; goto gen_arith; @@ -1604,8 +1612,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_divu_i64: case INDEX_op_sub_i32: case INDEX_op_sub_i64: - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: case INDEX_op_or_i32: case INDEX_op_or_i64: case INDEX_op_orc_i32: diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 4ccdc6bbee..7e4301521e 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -20,7 +20,6 @@ #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_not_i64 0 -#define TCG_TARGET_HAS_andc_i64 0 #define TCG_TARGET_HAS_orc_i64 0 #define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 82f3ad501f..68818cbb0c 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -668,7 +668,7 @@ void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_andc_i32) { + if (tcg_op_supported(INDEX_op_andc_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_andc_i32, ret, arg1, arg2); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); @@ -2264,7 +2264,7 @@ void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_andc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_andc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (TCG_TARGET_HAS_andc_i64) { + } else if (tcg_op_supported(INDEX_op_andc_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_andc_i64, ret, arg1, arg2); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index d60427eb7f..3d6dc9d1ca 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1006,6 +1006,8 @@ QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) - static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), + OUTOP(INDEX_op_andc_i32, TCGOutOpBinary, outop_andc), + OUTOP(INDEX_op_andc_i64, TCGOutOpBinary, outop_andc), }; #undef OUTOP @@ -2269,8 +2271,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap32_i32; case INDEX_op_not_i32: return TCG_TARGET_HAS_not_i32; - case INDEX_op_andc_i32: - return TCG_TARGET_HAS_andc_i32; case INDEX_op_orc_i32: return TCG_TARGET_HAS_orc_i32; case INDEX_op_eqv_i32: @@ -2346,8 +2346,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap64_i64; case INDEX_op_not_i64: return TCG_TARGET_HAS_not_i64; - case INDEX_op_andc_i64: - return TCG_TARGET_HAS_andc_i64; case INDEX_op_orc_i64: return TCG_TARGET_HAS_orc_i64; case INDEX_op_eqv_i64: @@ -5443,6 +5441,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_add: case INDEX_op_and: + case INDEX_op_andc_i32: + case INDEX_op_andc_i64: { const TCGOutOpBinary *out = container_of(all_outop[op->opc], TCGOutOpBinary, base); diff --git a/tcg/tci.c b/tcg/tci.c index 8762a99fb6..95a61e9df1 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -547,12 +547,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ^ regs[r2]; break; -#if TCG_TARGET_HAS_andc_i32 || TCG_TARGET_HAS_andc_i64 CASE_32_64(andc) tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] & ~regs[r2]; break; -#endif #if TCG_TARGET_HAS_orc_i32 || TCG_TARGET_HAS_orc_i64 CASE_32_64(orc) tci_args_rrr(insn, &r0, &r1, &r2); diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index cb0964c3d4..e09d366517 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -11,7 +11,6 @@ #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 -#define TCG_TARGET_HAS_andc_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_eqv_i32 1 #define TCG_TARGET_HAS_nand_i32 1 @@ -36,7 +35,6 @@ #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_andc_i64 1 #define TCG_TARGET_HAS_eqv_i64 1 #define TCG_TARGET_HAS_nand_i64 1 #define TCG_TARGET_HAS_nor_i64 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index b0141f8ed6..fb7c648b63 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -95,8 +95,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: case INDEX_op_eqv_i32: case INDEX_op_eqv_i64: case INDEX_op_nand_i32: @@ -659,6 +657,17 @@ static const TCGOutOpBinary outop_and = { .out_rrr = tgen_and, }; +static void tgen_andc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, glue(INDEX_op_andc_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_andc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_andc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -705,7 +714,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, CASE_32_64(mul) CASE_32_64(or) CASE_32_64(xor) - CASE_32_64(andc) /* Optional (TCG_TARGET_HAS_andc_*). */ CASE_32_64(orc) /* Optional (TCG_TARGET_HAS_orc_*). */ CASE_32_64(eqv) /* Optional (TCG_TARGET_HAS_eqv_*). */ CASE_32_64(nand) /* Optional (TCG_TARGET_HAS_nand_*). */ From 46f96bff163512f9f8f9959de4a18c0799001422 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 12:37:02 -0800 Subject: [PATCH 0371/2760] tcg: Merge INDEX_op_andc_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- target/arm/tcg/translate-a64.c | 2 +- target/tricore/translate.c | 2 +- tcg/optimize.c | 6 ++++-- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 6 ++---- tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 9 files changed, 17 insertions(+), 19 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 6a8025c0bf..116a0438b1 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -319,7 +319,7 @@ Logical - | *t0* = ~\ *t1* - * - andc_i32/i64 *t0*, *t1*, *t2* + * - andc *t0*, *t1*, *t2* - | *t0* = *t1* & ~\ *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index f45029caa7..9bc511992d 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -41,6 +41,7 @@ DEF(mov, 1, 1, 0, TCG_OPF_INT | TCG_OPF_NOT_PRESENT) DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) +DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(setcond_i32, 1, 2, 1, 0) DEF(negsetcond_i32, 1, 2, 1, 0) @@ -91,7 +92,6 @@ DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) DEF(not_i32, 1, 1, 0, 0) DEF(neg_i32, 1, 1, 0, 0) -DEF(andc_i32, 1, 2, 0, 0) DEF(orc_i32, 1, 2, 0, 0) DEF(eqv_i32, 1, 2, 0, 0) DEF(nand_i32, 1, 2, 0, 0) @@ -149,7 +149,6 @@ DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) DEF(not_i64, 1, 1, 0, 0) DEF(neg_i64, 1, 1, 0, 0) -DEF(andc_i64, 1, 2, 0, 0) DEF(orc_i64, 1, 2, 0, 0) DEF(eqv_i64, 1, 2, 0, 0) DEF(nand_i64, 1, 2, 0, 0) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 43408c71bb..e076d4aa05 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -8600,7 +8600,7 @@ static bool trans_CCMP(DisasContext *s, arg_CCMP *a) tcg_gen_subi_i32(tcg_t2, tcg_t0, 1); nzcv = a->nzcv; - has_andc = tcg_op_supported(INDEX_op_andc_i32, TCG_TYPE_I32, 0); + has_andc = tcg_op_supported(INDEX_op_andc, TCG_TYPE_I32, 0); if (nzcv & 8) { /* N */ tcg_gen_or_i32(cpu_NF, cpu_NF, tcg_t1); } else { diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 7cd26d8eab..2d0cde0268 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -3981,7 +3981,7 @@ static void decode_bit_andacc(DisasContext *ctx) pos1, pos2, &tcg_gen_andc_tl, &tcg_gen_and_tl); break; case OPC2_32_BIT_AND_NOR_T: - if (tcg_op_supported(INDEX_op_andc_i32, TCG_TYPE_I32, 0)) { + if (tcg_op_supported(INDEX_op_andc, TCG_TYPE_I32, 0)) { gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], pos1, pos2, &tcg_gen_or_tl, &tcg_gen_andc_tl); } else { diff --git a/tcg/optimize.c b/tcg/optimize.c index 6c7b6af624..875d80c254 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -479,7 +479,8 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) CASE_OP_32_64(neg): return -x; - CASE_OP_32_64_VEC(andc): + case INDEX_op_andc: + case INDEX_op_andc_vec: return x & ~y; CASE_OP_32_64_VEC(orc): @@ -2852,7 +2853,8 @@ void tcg_optimize(TCGContext *s) case INDEX_op_and_vec: done = fold_and(&ctx, op); break; - CASE_OP_32_64_VEC(andc): + case INDEX_op_andc: + case INDEX_op_andc_vec: done = fold_andc(&ctx, op); break; CASE_OP_32_64(brcond): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 68818cbb0c..d87bd13375 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -668,8 +668,8 @@ void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_andc_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3_i32(INDEX_op_andc_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_andc, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_andc, ret, arg1, arg2); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_not_i32(t0, arg2); @@ -2264,8 +2264,8 @@ void tcg_gen_andc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_andc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_andc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (tcg_op_supported(INDEX_op_andc_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3_i64(INDEX_op_andc_i64, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_andc, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_andc, ret, arg1, arg2); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_not_i64(t0, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index 3d6dc9d1ca..38b2dd1c44 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1006,8 +1006,7 @@ QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) - static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), - OUTOP(INDEX_op_andc_i32, TCGOutOpBinary, outop_andc), - OUTOP(INDEX_op_andc_i64, TCGOutOpBinary, outop_andc), + OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), }; #undef OUTOP @@ -5441,8 +5440,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_add: case INDEX_op_and: - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: + case INDEX_op_andc: { const TCGOutOpBinary *out = container_of(all_outop[op->opc], TCGOutOpBinary, base); diff --git a/tcg/tci.c b/tcg/tci.c index 95a61e9df1..e4a0408fec 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -547,7 +547,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ^ regs[r2]; break; - CASE_32_64(andc) + case INDEX_op_andc: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] & ~regs[r2]; break; @@ -1082,6 +1082,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_add: case INDEX_op_and: + case INDEX_op_andc: case INDEX_op_sub_i32: case INDEX_op_sub_i64: case INDEX_op_mul_i32: @@ -1090,8 +1091,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_or_i64: case INDEX_op_xor_i32: case INDEX_op_xor_i64: - case INDEX_op_andc_i32: - case INDEX_op_andc_i64: case INDEX_op_orc_i32: case INDEX_op_orc_i64: case INDEX_op_eqv_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index fb7c648b63..92c588305a 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -660,7 +660,7 @@ static const TCGOutOpBinary outop_and = { static void tgen_andc(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_op_rrr(s, glue(INDEX_op_andc_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_andc, a0, a1, a2); } static const TCGOutOpBinary outop_andc = { From 6497af5b0d76819475839de9ea1ef9faad6215c0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 13:54:22 -0800 Subject: [PATCH 0372/2760] tcg: Convert or to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 31 ++++++++++++--------- tcg/arm/tcg-target.c.inc | 24 ++++++++++++---- tcg/i386/tcg-target.c.inc | 25 +++++++++++++---- tcg/loongarch64/tcg-target.c.inc | 29 ++++++++++++-------- tcg/mips/tcg-target.c.inc | 25 ++++++++++++----- tcg/ppc/tcg-target.c.inc | 29 ++++++++++++-------- tcg/riscv/tcg-target.c.inc | 29 ++++++++++++-------- tcg/s390x/tcg-target.c.inc | 47 +++++++++++++++++--------------- tcg/sparc64/tcg-target.c.inc | 23 ++++++++++++---- tcg/tcg.c | 4 +++ tcg/tci/tcg-target.c.inc | 14 ++++++++-- 11 files changed, 186 insertions(+), 94 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index c7167cad15..4b62e4e382 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2157,6 +2157,24 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3510, ORR, type, a0, a1, a2); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_logicali(s, I3404_ORRI, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rL), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2238,17 +2256,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_insn(s, 3502, SUB, ext, a0, TCG_REG_XZR, a1); break; - case INDEX_op_or_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_or_i64: - if (c2) { - tcg_out_logicali(s, I3404_ORRI, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3510, ORR, ext, a0, a1, a2); - } - break; - case INDEX_op_orc_i32: a2 = (int32_t)a2; /* FALLTHRU */ @@ -3016,8 +3023,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_mulsh_i64: return C_O1_I2(r, r, r); - case INDEX_op_or_i32: - case INDEX_op_or_i64: case INDEX_op_xor_i32: case INDEX_op_xor_i64: case INDEX_op_orc_i32: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index feea82145a..0575d397c9 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1881,6 +1881,24 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_ORR, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_ORR, a0, a1, encode_imm_nofail(a2)); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1943,13 +1961,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[0], args[1], args[2], const_args[2]); } break; - case INDEX_op_or_i32: - c = ARITH_ORR; - goto gen_arith; case INDEX_op_xor_i32: c = ARITH_EOR; - /* Fall through. */ - gen_arith: tcg_out_dat_rI(s, COND_AL, c, args[0], args[1], args[2], const_args[2]); break; case INDEX_op_add2_i32: @@ -2209,7 +2222,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i32: return C_O2_I2(r, r, r, r); - case INDEX_op_or_i32: case INDEX_op_xor_i32: return C_O1_I2(r, r, rI); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 33c1fcc717..813092622c 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2633,6 +2633,26 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_OR + rexw, a0, a2); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_OR + rexw, a0, a2, false); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2717,9 +2737,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, OP_32_64(sub): c = ARITH_SUB; goto gen_arith; - OP_32_64(or): - c = ARITH_OR; - goto gen_arith; OP_32_64(xor): c = ARITH_XOR; goto gen_arith; @@ -3645,8 +3662,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_or_i32: - case INDEX_op_or_i64: case INDEX_op_xor_i32: case INDEX_op_xor_i64: return C_O1_I2(r, 0, re); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index b7d2984a8a..a7ead51263 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1332,6 +1332,24 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_or(s, a0, a1, a2); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_ori(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rU), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1392,15 +1410,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_or_i32: - case INDEX_op_or_i64: - if (c2) { - tcg_out_opc_ori(s, a0, a1, a2); - } else { - tcg_out_opc_or(s, a0, a1, a2); - } - break; - case INDEX_op_xor_i32: case INDEX_op_xor_i64: if (c2) { @@ -2300,8 +2309,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_nor_i32: case INDEX_op_nor_i64: - case INDEX_op_or_i32: - case INDEX_op_or_i64: case INDEX_op_xor_i32: case INDEX_op_xor_i64: /* LoongArch reg-imm bitops have their imms ZERO-extended */ diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index ab57c78095..74eef1d3b3 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1712,6 +1712,24 @@ static const TCGOutOpBinary outop_andc = { .base.static_constraint = C_NotImplemented, }; +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_OR, a0, a1, a2); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_imm(s, OPC_ORI, a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1785,14 +1803,9 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_or_i32: - case INDEX_op_or_i64: - i1 = OPC_OR, i2 = OPC_ORI; - goto do_binary; case INDEX_op_xor_i32: case INDEX_op_xor_i64: i1 = OPC_XOR, i2 = OPC_XORI; - do_binary: if (c2) { tcg_out_opc_imm(s, i2, a0, a1, a2); break; @@ -2218,9 +2231,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i64: case INDEX_op_mulu2_i64: return C_O2_I2(r, r, r, r); - case INDEX_op_or_i32: case INDEX_op_xor_i32: - case INDEX_op_or_i64: case INDEX_op_xor_i64: return C_O1_I2(r, r, rI); case INDEX_op_shl_i32: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 7b1a82c9fa..b638a5f813 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2954,6 +2954,24 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, OR | SAB(a1, a0, a2)); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_ori32(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rU), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3039,15 +3057,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_or_i64: - case INDEX_op_or_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_ori32(s, a0, a1, a2); - } else { - tcg_out32(s, OR | SAB(a1, a0, a2)); - } - break; case INDEX_op_xor_i64: case INDEX_op_xor_i32: a0 = args[0], a1 = args[1], a2 = args[2]; @@ -4130,7 +4139,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_or_i32: case INDEX_op_xor_i32: case INDEX_op_orc_i32: case INDEX_op_eqv_i32: @@ -4172,7 +4180,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i32: return C_O1_I2(r, rI, ri); - case INDEX_op_or_i64: case INDEX_op_xor_i64: return C_O1_I2(r, r, rU); case INDEX_op_sub_i64: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index f637604e98..9bacd109d4 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2013,6 +2013,24 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_OR, a0, a1, a2); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_imm(s, OPC_ORI, a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2091,15 +2109,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_or_i32: - case INDEX_op_or_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_ORI, a0, a1, a2); - } else { - tcg_out_opc_reg(s, OPC_OR, a0, a1, a2); - } - break; - case INDEX_op_xor_i32: case INDEX_op_xor_i64: if (c2) { @@ -2682,9 +2691,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_or_i32: case INDEX_op_xor_i32: - case INDEX_op_or_i64: case INDEX_op_xor_i64: case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index e4b60d1924..9267aef544 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2242,6 +2242,31 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, OGRK, a0, a1, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, OR, a0, a2); + } else { + tcg_out_insn(s, RRFa, ORK, a0, a1, a2); + } +} + +static void tgen_ori_3(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + tgen_ori(s, a0, type == TCG_TYPE_I32 ? (uint32_t)a2 : a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rK), + .out_rrr = tgen_or, + .out_rri = tgen_ori_3, +}; + # define OP_32_64(x) \ case glue(glue(INDEX_op_,x),_i32): \ @@ -2310,17 +2335,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_or_i32: - a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - tgen_ori(s, a0, a2); - } else if (a0 == a1) { - tcg_out_insn(s, RR, OR, a0, a2); - } else { - tcg_out_insn(s, RRFa, ORK, a0, a1, a2); - } - break; case INDEX_op_xor_i32: a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; if (const_args[2]) { @@ -2561,15 +2575,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_or_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I64, a0, a1); - tgen_ori(s, a0, a2); - } else { - tcg_out_insn(s, RRFa, OGRK, a0, a1, a2); - } - break; case INDEX_op_xor_i64: a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { @@ -3282,10 +3287,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i32: case INDEX_op_sub_i64: - case INDEX_op_or_i32: case INDEX_op_xor_i32: return C_O1_I2(r, r, ri); - case INDEX_op_or_i64: case INDEX_op_xor_i64: return C_O1_I2(r, r, rK); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index fe9175aa1a..b01d55c80b 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1333,6 +1333,24 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_OR); +} + +static void tgen_ori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_arithi(s, a0, a1, a2, ARITH_OR); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_or, + .out_rri = tgen_ori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1390,9 +1408,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, OP_32_64(sub): c = ARITH_SUB; goto gen_arith; - OP_32_64(or): - c = ARITH_OR; - goto gen_arith; OP_32_64(orc): c = ARITH_ORN; goto gen_arith; @@ -1612,8 +1627,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_divu_i64: case INDEX_op_sub_i32: case INDEX_op_sub_i64: - case INDEX_op_or_i32: - case INDEX_op_or_i64: case INDEX_op_orc_i32: case INDEX_op_orc_i64: case INDEX_op_xor_i32: diff --git a/tcg/tcg.c b/tcg/tcg.c index 38b2dd1c44..7357b5c127 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1007,6 +1007,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), + OUTOP(INDEX_op_or_i32, TCGOutOpBinary, outop_or), + OUTOP(INDEX_op_or_i64, TCGOutOpBinary, outop_or), }; #undef OUTOP @@ -5441,6 +5443,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: + case INDEX_op_or_i32: + case INDEX_op_or_i64: { const TCGOutOpBinary *out = container_of(all_outop[op->opc], TCGOutOpBinary, base); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 92c588305a..6fdfcab061 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -101,8 +101,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_nand_i64: case INDEX_op_nor_i32: case INDEX_op_nor_i64: - case INDEX_op_or_i32: - case INDEX_op_or_i64: case INDEX_op_orc_i32: case INDEX_op_orc_i64: case INDEX_op_xor_i32: @@ -668,6 +666,17 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_or(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, glue(INDEX_op_or_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_or = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_or, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -712,7 +721,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, CASE_32_64(sub) CASE_32_64(mul) - CASE_32_64(or) CASE_32_64(xor) CASE_32_64(orc) /* Optional (TCG_TARGET_HAS_orc_*). */ CASE_32_64(eqv) /* Optional (TCG_TARGET_HAS_eqv_*). */ From 49bd751497f3b71550b152ef9da0e265a94a64c1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 14:00:40 -0800 Subject: [PATCH 0373/2760] tcg: Merge INDEX_op_or_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- target/sh4/translate.c | 4 ++-- tcg/optimize.c | 6 ++++-- tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 9 +++------ tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 8 files changed, 16 insertions(+), 19 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 116a0438b1..8d67b0cdeb 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -307,7 +307,7 @@ Logical - | *t0* = *t1* & *t2* - * - or_i32/i64 *t0*, *t1*, *t2* + * - or *t0*, *t1*, *t2* - | *t0* = *t1* | *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 9bc511992d..95608d6d31 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -42,6 +42,7 @@ DEF(mov, 1, 1, 0, TCG_OPF_INT | TCG_OPF_NOT_PRESENT) DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) +DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(setcond_i32, 1, 2, 1, 0) DEF(negsetcond_i32, 1, 2, 1, 0) @@ -64,7 +65,6 @@ DEF(rem_i32, 1, 2, 0, 0) DEF(remu_i32, 1, 2, 0, 0) DEF(div2_i32, 2, 3, 0, 0) DEF(divu2_i32, 2, 3, 0, 0) -DEF(or_i32, 1, 2, 0, 0) DEF(xor_i32, 1, 2, 0, 0) /* shifts/rotates */ DEF(shl_i32, 1, 2, 0, 0) @@ -124,7 +124,6 @@ DEF(rem_i64, 1, 2, 0, 0) DEF(remu_i64, 1, 2, 0, 0) DEF(div2_i64, 2, 3, 0, 0) DEF(divu2_i64, 2, 3, 0, 0) -DEF(or_i64, 1, 2, 0, 0) DEF(xor_i64, 1, 2, 0, 0) /* shifts/rotates */ DEF(shl_i64, 1, 2, 0, 0) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 3d0eda2128..094613d312 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -1949,7 +1949,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) op_opc = INDEX_op_xor_i32; goto do_reg_op; case 0x200b: /* or Rm,Rn */ - op_opc = INDEX_op_or_i32; + op_opc = INDEX_op_or; do_reg_op: /* The operation register should be as expected, and the other input cannot depend on the load. */ @@ -2119,7 +2119,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) } break; - case INDEX_op_or_i32: + case INDEX_op_or: if (op_dst != st_src) { goto fail; } diff --git a/tcg/optimize.c b/tcg/optimize.c index 875d80c254..7d5f7af223 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -437,7 +437,8 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) case INDEX_op_and_vec: return x & y; - CASE_OP_32_64_VEC(or): + case INDEX_op_or: + case INDEX_op_or_vec: return x | y; CASE_OP_32_64_VEC(xor): @@ -2961,7 +2962,8 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64_VEC(not): done = fold_not(&ctx, op); break; - CASE_OP_32_64_VEC(or): + case INDEX_op_or: + case INDEX_op_or_vec: done = fold_or(&ctx, op); break; CASE_OP_32_64_VEC(orc): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index d87bd13375..6807f4eebd 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -436,7 +436,7 @@ void tcg_gen_andi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_or_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - tcg_gen_op3_i32(INDEX_op_or_i32, ret, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_or, ret, arg1, arg2); } void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) @@ -1585,7 +1585,7 @@ void tcg_gen_and_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op3_i64(INDEX_op_or_i64, ret, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_or, ret, arg1, arg2); } else { tcg_gen_or_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_or_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); diff --git a/tcg/tcg.c b/tcg/tcg.c index 7357b5c127..f31ae4e56b 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1007,8 +1007,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), - OUTOP(INDEX_op_or_i32, TCGOutOpBinary, outop_or), - OUTOP(INDEX_op_or_i64, TCGOutOpBinary, outop_or), + OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), }; #undef OUTOP @@ -2212,6 +2211,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_add: case INDEX_op_and: case INDEX_op_mov: + case INDEX_op_or: return has_type; case INDEX_op_setcond_i32: @@ -2228,7 +2228,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i32: case INDEX_op_neg_i32: case INDEX_op_mul_i32: - case INDEX_op_or_i32: case INDEX_op_xor_i32: case INDEX_op_shl_i32: case INDEX_op_shr_i32: @@ -2308,7 +2307,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: case INDEX_op_neg_i64: case INDEX_op_mul_i64: - case INDEX_op_or_i64: case INDEX_op_xor_i64: case INDEX_op_shl_i64: case INDEX_op_shr_i64: @@ -5443,8 +5441,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: - case INDEX_op_or_i32: - case INDEX_op_or_i64: + case INDEX_op_or: { const TCGOutOpBinary *out = container_of(all_outop[op->opc], TCGOutOpBinary, base); diff --git a/tcg/tci.c b/tcg/tci.c index e4a0408fec..3e361be6bd 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -539,7 +539,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] & regs[r2]; break; - CASE_32_64(or) + case INDEX_op_or: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] | regs[r2]; break; @@ -1083,12 +1083,11 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: + case INDEX_op_or: case INDEX_op_sub_i32: case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_or_i32: - case INDEX_op_or_i64: case INDEX_op_xor_i32: case INDEX_op_xor_i64: case INDEX_op_orc_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 6fdfcab061..4214b76b34 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -669,7 +669,7 @@ static const TCGOutOpBinary outop_andc = { static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_op_rrr(s, glue(INDEX_op_or_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_or, a0, a1, a2); } static const TCGOutOpBinary outop_or = { From 50e40ecd7a5eb803a67d02aa586b5671968ac58b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 08:13:10 -0600 Subject: [PATCH 0374/2760] tcg/optimize: Fold orc with immediate to or MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 7d5f7af223..684b1099d0 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2143,6 +2143,7 @@ static bool fold_or(OptContext *ctx, TCGOp *op) static bool fold_orc(OptContext *ctx, TCGOp *op) { uint64_t s_mask; + TempOptInfo *t1, *t2; if (fold_const2(ctx, op) || fold_xx_to_i(ctx, op, -1) || @@ -2151,8 +2152,28 @@ static bool fold_orc(OptContext *ctx, TCGOp *op) return true; } - s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; + t2 = arg_info(op->args[2]); + if (ti_is_const(t2)) { + /* Fold orc r,x,i to or r,x,~i. */ + switch (ctx->type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + op->opc = INDEX_op_or; + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + op->opc = INDEX_op_or_vec; + break; + default: + g_assert_not_reached(); + } + op->args[2] = arg_new_constant(ctx, ~ti_const_val(t2)); + return fold_or(ctx, op); + } + + t1 = arg_info(op->args[1]); + s_mask = t1->s_mask & t2->s_mask; return fold_masks_s(ctx, op, s_mask); } From d262ae608115d2e6fc61ba6998d9813a5208b0e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 14:30:50 -0800 Subject: [PATCH 0375/2760] tcg: Convert orc to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At the same time, drop all backend support for immediate operands, as we now transform orc to or during optimize. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 24 ++++++++--------- tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 4 +++ tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 4 +++ tcg/loongarch64/tcg-target-con-set.h | 1 - tcg/loongarch64/tcg-target-con-str.h | 1 - tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 40 ++++++++++------------------ tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 4 +++ tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 22 +++++++-------- tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 22 ++++++++------- tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 36 +++++++++++-------------- tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 16 +++++++---- tcg/tcg-has.h | 1 - tcg/tcg-op.c | 4 +-- tcg/tcg.c | 8 +++--- tcg/tci.c | 2 -- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target.c.inc | 14 +++++++--- 26 files changed, 104 insertions(+), 118 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 851f6b01b4..8469a9446f 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -19,7 +19,6 @@ #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 1 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 @@ -44,7 +43,6 @@ #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 1 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 4b62e4e382..13592303a8 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2175,6 +2175,17 @@ static const TCGOutOpBinary outop_or = { .out_rri = tgen_ori, }; +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3510, ORN, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_orc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2256,17 +2267,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_insn(s, 3502, SUB, ext, a0, TCG_REG_XZR, a1); break; - case INDEX_op_orc_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_orc_i64: - if (c2) { - tcg_out_logicali(s, I3404_ORRI, ext, a0, a1, ~a2); - } else { - tcg_out_insn(s, 3510, ORN, ext, a0, a1, a2); - } - break; - case INDEX_op_xor_i32: a2 = (int32_t)a2; /* FALLTHRU */ @@ -3025,8 +3025,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_xor_i32: case INDEX_op_xor_i64: - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: case INDEX_op_eqv_i32: case INDEX_op_eqv_i64: return C_O1_I2(r, r, rL); diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 0268858a3b..39dcc87fe8 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -28,7 +28,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_orc_i32 0 #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 0575d397c9..48cbcd67b9 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1899,6 +1899,10 @@ static const TCGOutOpBinary outop_or = { .out_rri = tgen_ori, }; +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_NotImplemented, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index b29b70357a..e525f23c05 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -31,7 +31,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_orc_i32 0 #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 @@ -56,7 +55,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_orc_i64 0 #define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 813092622c..3fe1d9d9cc 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2653,6 +2653,10 @@ static const TCGOutOpBinary outop_or = { .out_rri = tgen_ori, }; +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_NotImplemented, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], diff --git a/tcg/loongarch64/tcg-target-con-set.h b/tcg/loongarch64/tcg-target-con-set.h index b7c9b89e9e..b4af4f5423 100644 --- a/tcg/loongarch64/tcg-target-con-set.h +++ b/tcg/loongarch64/tcg-target-con-set.h @@ -23,7 +23,6 @@ C_O1_I1(r, r) C_O1_I1(w, r) C_O1_I1(w, w) C_O1_I2(r, r, r) -C_O1_I2(r, r, rC) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) C_O1_I2(r, r, rJ) diff --git a/tcg/loongarch64/tcg-target-con-str.h b/tcg/loongarch64/tcg-target-con-str.h index 99759120b4..e5e57452d6 100644 --- a/tcg/loongarch64/tcg-target-con-str.h +++ b/tcg/loongarch64/tcg-target-con-str.h @@ -23,7 +23,6 @@ REGS('w', ALL_VECTOR_REGS) CONST('I', TCG_CT_CONST_S12) CONST('J', TCG_CT_CONST_S32) CONST('U', TCG_CT_CONST_U12) -CONST('C', TCG_CT_CONST_C12) CONST('W', TCG_CT_CONST_WSZ) CONST('M', TCG_CT_CONST_VCMP) CONST('A', TCG_CT_CONST_VADD) diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 71d91fec19..fb1142958c 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -25,7 +25,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 1 @@ -46,7 +45,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 1 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index a7ead51263..b6f13090b9 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -176,10 +176,9 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define TCG_CT_CONST_S12 0x100 #define TCG_CT_CONST_S32 0x200 #define TCG_CT_CONST_U12 0x400 -#define TCG_CT_CONST_C12 0x800 -#define TCG_CT_CONST_WSZ 0x1000 -#define TCG_CT_CONST_VCMP 0x2000 -#define TCG_CT_CONST_VADD 0x4000 +#define TCG_CT_CONST_WSZ 0x800 +#define TCG_CT_CONST_VCMP 0x1000 +#define TCG_CT_CONST_VADD 0x2000 #define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) #define ALL_VECTOR_REGS MAKE_64BIT_MASK(32, 32) @@ -205,9 +204,6 @@ static bool tcg_target_const_match(int64_t val, int ct, if ((ct & TCG_CT_CONST_U12) && val >= 0 && val <= 0xfff) { return true; } - if ((ct & TCG_CT_CONST_C12) && ~val >= 0 && ~val <= 0xfff) { - return true; - } if ((ct & TCG_CT_CONST_WSZ) && val == (type == TCG_TYPE_I32 ? 32 : 64)) { return true; } @@ -1350,6 +1346,17 @@ static const TCGOutOpBinary outop_or = { .out_rri = tgen_ori, }; +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_orn(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_orc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1400,16 +1407,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: - if (c2) { - /* guaranteed to fit due to constraint */ - tcg_out_opc_ori(s, a0, a1, ~a2); - } else { - tcg_out_opc_orn(s, a0, a1, a2); - } - break; - case INDEX_op_xor_i32: case INDEX_op_xor_i64: if (c2) { @@ -2286,15 +2283,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: - /* - * LoongArch insns for these ops don't have reg-imm forms, but we - * can express using andi/ori if ~constant satisfies - * TCG_CT_CONST_U12. - */ - return C_O1_I2(r, r, rC); - case INDEX_op_shl_i32: case INDEX_op_shl_i64: case INDEX_op_shr_i32: diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 6a6d4377e7..b3dfa390f9 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -43,7 +43,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_nor_i32 1 -#define TCG_TARGET_HAS_orc_i32 0 #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) @@ -62,7 +61,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_nor_i64 1 -#define TCG_TARGET_HAS_orc_i64 0 #define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_add2_i64 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 74eef1d3b3..f6987963ec 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1730,6 +1730,10 @@ static const TCGOutOpBinary outop_or = { .out_rri = tgen_ori, }; +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_NotImplemented, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 63bb66f446..6f3ab41ebb 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -23,7 +23,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 1 #define TCG_TARGET_HAS_nand_i32 1 #define TCG_TARGET_HAS_nor_i32 1 @@ -49,7 +48,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 1 #define TCG_TARGET_HAS_nand_i64 1 #define TCG_TARGET_HAS_nor_i64 1 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index b638a5f813..ccd7812016 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2972,6 +2972,17 @@ static const TCGOutOpBinary outop_or = { .out_rri = tgen_ori, }; +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, ORC | SAB(a1, a0, a2)); +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_orc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3066,15 +3077,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, XOR | SAB(a1, a0, a2)); } break; - case INDEX_op_orc_i32: - if (const_args[2]) { - tcg_out_ori32(s, args[0], args[1], ~args[2]); - break; - } - /* FALLTHRU */ - case INDEX_op_orc_i64: - tcg_out32(s, ORC | SAB(args[1], args[0], args[2])); - break; case INDEX_op_eqv_i32: if (const_args[2]) { tcg_out_xori32(s, args[0], args[1], ~args[2]); @@ -4140,7 +4142,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O0_I2(r, r); case INDEX_op_xor_i32: - case INDEX_op_orc_i32: case INDEX_op_eqv_i32: case INDEX_op_shl_i32: case INDEX_op_shr_i32: @@ -4166,7 +4167,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_nor_i32: case INDEX_op_muluh_i32: case INDEX_op_mulsh_i32: - case INDEX_op_orc_i64: case INDEX_op_eqv_i64: case INDEX_op_nand_i64: case INDEX_op_nor_i64: diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index a3918bf7f5..7b8f4386c9 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -25,7 +25,6 @@ #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_orc_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_eqv_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 @@ -45,7 +44,6 @@ #define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_orc_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_eqv_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 9bacd109d4..14216e9dff 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2031,6 +2031,18 @@ static const TCGOutOpBinary outop_or = { .out_rri = tgen_ori, }; +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_ORN, a0, a1, a2); +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_orc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2118,14 +2130,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_ORI, a0, a1, ~a2); - } else { - tcg_out_opc_reg(s, OPC_ORN, a0, a1, a2); - } - break; case INDEX_op_eqv_i32: case INDEX_op_eqv_i64: if (c2) { @@ -2699,8 +2703,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: case INDEX_op_eqv_i32: case INDEX_op_eqv_i64: return C_O1_I2(r, r, rJ); diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 15ec0dc2ff..850c16a164 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -34,7 +34,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_orc_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_eqv_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_nand_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_nor_i32 HAVE_FACILITY(MISC_INSN_EXT3) @@ -58,7 +57,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_orc_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_eqv_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_nand_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_nor_i64 HAVE_FACILITY(MISC_INSN_EXT3) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 9267aef544..97587939bd 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2267,6 +2267,22 @@ static const TCGOutOpBinary outop_or = { .out_rri = tgen_ori_3, }; +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, OCRK, a0, a1, a2); + } else { + tcg_out_insn(s, RRFa, OCGRK, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_misc3_rrr, + .out_rrr = tgen_orc, +}; + # define OP_32_64(x) \ case glue(glue(INDEX_op_,x),_i32): \ @@ -2347,15 +2363,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_orc_i32: - a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - tgen_ori(s, a0, (uint32_t)~a2); - } else { - tcg_out_insn(s, RRFa, OCRK, a0, a1, a2); - } - break; case INDEX_op_eqv_i32: a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; if (const_args[2]) { @@ -2585,15 +2592,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_orc_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I64, a0, a1); - tgen_ori(s, a0, ~a2); - } else { - tcg_out_insn(s, RRFa, OCGRK, a0, a1, a2); - } - break; case INDEX_op_eqv_i64: a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { @@ -3292,10 +3290,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_xor_i64: return C_O1_I2(r, r, rK); - case INDEX_op_orc_i32: case INDEX_op_eqv_i32: return C_O1_I2(r, r, ri); - case INDEX_op_orc_i64: case INDEX_op_eqv_i64: return C_O1_I2(r, r, rNK); diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 510b9e64a4..8e20e4cdeb 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -20,7 +20,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 @@ -45,7 +44,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index b01d55c80b..38b325e8a9 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1351,6 +1351,17 @@ static const TCGOutOpBinary outop_or = { .out_rri = tgen_ori, }; +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_ORN); +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_orc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1408,9 +1419,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, OP_32_64(sub): c = ARITH_SUB; goto gen_arith; - OP_32_64(orc): - c = ARITH_ORN; - goto gen_arith; OP_32_64(xor): c = ARITH_XOR; goto gen_arith; @@ -1627,8 +1635,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_divu_i64: case INDEX_op_sub_i32: case INDEX_op_sub_i64: - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: case INDEX_op_xor_i32: case INDEX_op_xor_i64: case INDEX_op_shl_i32: diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 7e4301521e..df9c951262 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -20,7 +20,6 @@ #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_not_i64 0 -#define TCG_TARGET_HAS_orc_i64 0 #define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 6807f4eebd..503d395ac8 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -710,7 +710,7 @@ void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_orc_i32) { + if (tcg_op_supported(INDEX_op_orc_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_orc_i32, ret, arg1, arg2); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); @@ -2318,7 +2318,7 @@ void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_orc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_orc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (TCG_TARGET_HAS_orc_i64) { + } else if (tcg_op_supported(INDEX_op_orc_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_orc_i64, ret, arg1, arg2); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index f31ae4e56b..4737a6b2cc 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1008,6 +1008,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), + OUTOP(INDEX_op_orc_i32, TCGOutOpBinary, outop_orc), + OUTOP(INDEX_op_orc_i64, TCGOutOpBinary, outop_orc), }; #undef OUTOP @@ -2271,8 +2273,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap32_i32; case INDEX_op_not_i32: return TCG_TARGET_HAS_not_i32; - case INDEX_op_orc_i32: - return TCG_TARGET_HAS_orc_i32; case INDEX_op_eqv_i32: return TCG_TARGET_HAS_eqv_i32; case INDEX_op_nand_i32: @@ -2345,8 +2345,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap64_i64; case INDEX_op_not_i64: return TCG_TARGET_HAS_not_i64; - case INDEX_op_orc_i64: - return TCG_TARGET_HAS_orc_i64; case INDEX_op_eqv_i64: return TCG_TARGET_HAS_eqv_i64; case INDEX_op_nand_i64: @@ -5442,6 +5440,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_or: + case INDEX_op_orc_i32: + case INDEX_op_orc_i64: { const TCGOutOpBinary *out = container_of(all_outop[op->opc], TCGOutOpBinary, base); diff --git a/tcg/tci.c b/tcg/tci.c index 3e361be6bd..7a926b30db 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -551,12 +551,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] & ~regs[r2]; break; -#if TCG_TARGET_HAS_orc_i32 || TCG_TARGET_HAS_orc_i64 CASE_32_64(orc) tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] | ~regs[r2]; break; -#endif #if TCG_TARGET_HAS_eqv_i32 || TCG_TARGET_HAS_eqv_i64 CASE_32_64(eqv) tci_args_rrr(insn, &r0, &r1, &r2); diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index e09d366517..d247774e52 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -19,7 +19,6 @@ #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_orc_i32 1 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_muls2_i32 1 @@ -42,7 +41,6 @@ #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_orc_i64 1 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_muls2_i64 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 4214b76b34..2e45cc4768 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -101,8 +101,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_nand_i64: case INDEX_op_nor_i32: case INDEX_op_nor_i64: - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: case INDEX_op_xor_i32: case INDEX_op_xor_i64: case INDEX_op_shl_i32: @@ -677,6 +675,17 @@ static const TCGOutOpBinary outop_or = { .out_rrr = tgen_or, }; +static void tgen_orc(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, glue(INDEX_op_orc_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_orc = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_orc, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -722,7 +731,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, CASE_32_64(sub) CASE_32_64(mul) CASE_32_64(xor) - CASE_32_64(orc) /* Optional (TCG_TARGET_HAS_orc_*). */ CASE_32_64(eqv) /* Optional (TCG_TARGET_HAS_eqv_*). */ CASE_32_64(nand) /* Optional (TCG_TARGET_HAS_nand_*). */ CASE_32_64(nor) /* Optional (TCG_TARGET_HAS_nor_*). */ From 6aba25ebb9eb6e1e86398294694aa0ab1f12076f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 14:46:26 -0800 Subject: [PATCH 0376/2760] tcg: Merge INDEX_op_orc_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- target/arm/tcg/translate-sve.c | 2 +- target/tricore/translate.c | 2 +- tcg/optimize.c | 6 ++++-- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 6 ++---- tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 9 files changed, 17 insertions(+), 19 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 8d67b0cdeb..c5c5a4d19e 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -335,7 +335,7 @@ Logical - | *t0* = ~(*t1* | *t2*) - * - orc_i32/i64 *t0*, *t1*, *t2* + * - orc *t0*, *t1*, *t2* - | *t0* = *t1* | ~\ *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 95608d6d31..caf0f01042 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -43,6 +43,7 @@ DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(or, 1, 2, 0, TCG_OPF_INT) +DEF(orc, 1, 2, 0, TCG_OPF_INT) DEF(setcond_i32, 1, 2, 1, 0) DEF(negsetcond_i32, 1, 2, 1, 0) @@ -92,7 +93,6 @@ DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) DEF(not_i32, 1, 1, 0, 0) DEF(neg_i32, 1, 1, 0, 0) -DEF(orc_i32, 1, 2, 0, 0) DEF(eqv_i32, 1, 2, 0, 0) DEF(nand_i32, 1, 2, 0, 0) DEF(nor_i32, 1, 2, 0, 0) @@ -148,7 +148,6 @@ DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) DEF(not_i64, 1, 1, 0, 0) DEF(neg_i64, 1, 1, 0, 0) -DEF(orc_i64, 1, 2, 0, 0) DEF(eqv_i64, 1, 2, 0, 0) DEF(nand_i64, 1, 2, 0, 0) DEF(nor_i64, 1, 2, 0, 0) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index d23be477b4..f3cf028cb9 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -629,7 +629,7 @@ static void gen_bsl2n_i64(TCGv_i64 d, TCGv_i64 n, TCGv_i64 m, TCGv_i64 k) * = | ~(m | k) */ tcg_gen_and_i64(n, n, k); - if (tcg_op_supported(INDEX_op_orc_i64, TCG_TYPE_I64, 0)) { + if (tcg_op_supported(INDEX_op_orc, TCG_TYPE_I64, 0)) { tcg_gen_or_i64(m, m, k); tcg_gen_orc_i64(d, n, m); } else { diff --git a/target/tricore/translate.c b/target/tricore/translate.c index 2d0cde0268..ede0c92c1e 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -4114,7 +4114,7 @@ static void decode_bit_orand(DisasContext *ctx) pos1, pos2, &tcg_gen_andc_tl, &tcg_gen_or_tl); break; case OPC2_32_BIT_OR_NOR_T: - if (tcg_op_supported(INDEX_op_orc_i32, TCG_TYPE_I32, 0)) { + if (tcg_op_supported(INDEX_op_orc, TCG_TYPE_I32, 0)) { gen_bit_2op(cpu_gpr_d[r3], cpu_gpr_d[r1], cpu_gpr_d[r2], pos1, pos2, &tcg_gen_or_tl, &tcg_gen_orc_tl); } else { diff --git a/tcg/optimize.c b/tcg/optimize.c index 684b1099d0..5f0ab354d6 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -484,7 +484,8 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) case INDEX_op_andc_vec: return x & ~y; - CASE_OP_32_64_VEC(orc): + case INDEX_op_orc: + case INDEX_op_orc_vec: return x | ~y; CASE_OP_32_64_VEC(eqv): @@ -2987,7 +2988,8 @@ void tcg_optimize(TCGContext *s) case INDEX_op_or_vec: done = fold_or(&ctx, op); break; - CASE_OP_32_64_VEC(orc): + case INDEX_op_orc: + case INDEX_op_orc_vec: done = fold_orc(&ctx, op); break; case INDEX_op_qemu_ld_i32: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 503d395ac8..bf481060fa 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -710,8 +710,8 @@ void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_orc_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3_i32(INDEX_op_orc_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_orc, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_orc, ret, arg1, arg2); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_not_i32(t0, arg2); @@ -2318,8 +2318,8 @@ void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_orc_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_orc_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (tcg_op_supported(INDEX_op_orc_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3_i64(INDEX_op_orc_i64, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_orc, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_orc, ret, arg1, arg2); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_not_i64(t0, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index 4737a6b2cc..1b7e230219 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1008,8 +1008,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), - OUTOP(INDEX_op_orc_i32, TCGOutOpBinary, outop_orc), - OUTOP(INDEX_op_orc_i64, TCGOutOpBinary, outop_orc), + OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), }; #undef OUTOP @@ -5440,8 +5439,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_or: - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: + case INDEX_op_orc: { const TCGOutOpBinary *out = container_of(all_outop[op->opc], TCGOutOpBinary, base); diff --git a/tcg/tci.c b/tcg/tci.c index 7a926b30db..68636e70da 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -551,7 +551,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] & ~regs[r2]; break; - CASE_32_64(orc) + case INDEX_op_orc: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] | ~regs[r2]; break; @@ -1082,14 +1082,13 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_or: + case INDEX_op_orc: case INDEX_op_sub_i32: case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: case INDEX_op_xor_i32: case INDEX_op_xor_i64: - case INDEX_op_orc_i32: - case INDEX_op_orc_i64: case INDEX_op_eqv_i32: case INDEX_op_eqv_i64: case INDEX_op_nand_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 2e45cc4768..b9309e2fb9 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -678,7 +678,7 @@ static const TCGOutOpBinary outop_or = { static void tgen_orc(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_op_rrr(s, glue(INDEX_op_orc_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_orc, a0, a1, a2); } static const TCGOutOpBinary outop_orc = { From d3e56f382493540b3a46a432a7e09261f4f5dbe7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 15:11:22 -0800 Subject: [PATCH 0377/2760] tcg: Convert xor to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 31 +++++++++++--------- tcg/arm/tcg-target.c.inc | 25 +++++++++++----- tcg/i386/tcg-target.c.inc | 27 ++++++++++++----- tcg/loongarch64/tcg-target.c.inc | 29 +++++++++++------- tcg/mips/tcg-target.c.inc | 28 +++++++++++------- tcg/ppc/tcg-target.c.inc | 30 +++++++++++-------- tcg/riscv/tcg-target.c.inc | 29 +++++++++++------- tcg/s390x/tcg-target.c.inc | 50 ++++++++++++++++---------------- tcg/sparc64/tcg-target.c.inc | 23 +++++++++++---- tcg/tcg.c | 4 +++ tcg/tci/tcg-target.c.inc | 14 +++++++-- 11 files changed, 186 insertions(+), 104 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 13592303a8..d575635fe0 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2186,6 +2186,24 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3510, EOR, type, a0, a1, a2); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_logicali(s, I3404_EORI, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rL), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2267,17 +2285,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_insn(s, 3502, SUB, ext, a0, TCG_REG_XZR, a1); break; - case INDEX_op_xor_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_xor_i64: - if (c2) { - tcg_out_logicali(s, I3404_EORI, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3510, EOR, ext, a0, a1, a2); - } - break; - case INDEX_op_eqv_i32: a2 = (int32_t)a2; /* FALLTHRU */ @@ -3023,8 +3030,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_mulsh_i64: return C_O1_I2(r, r, r); - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: case INDEX_op_eqv_i32: case INDEX_op_eqv_i64: return C_O1_I2(r, r, rL); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 48cbcd67b9..98cb3cf5e2 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1903,6 +1903,24 @@ static const TCGOutOpBinary outop_orc = { .base.static_constraint = C_NotImplemented, }; +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_EOR, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_EOR, a0, a1, encode_imm_nofail(a2)); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1965,10 +1983,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[0], args[1], args[2], const_args[2]); } break; - case INDEX_op_xor_i32: - c = ARITH_EOR; - tcg_out_dat_rI(s, COND_AL, c, args[0], args[1], args[2], const_args[2]); - break; case INDEX_op_add2_i32: a0 = args[0], a1 = args[1], a2 = args[2]; a3 = args[3], a4 = args[4], a5 = args[5]; @@ -2226,9 +2240,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i32: return C_O2_I2(r, r, r, r); - case INDEX_op_xor_i32: - return C_O1_I2(r, r, rI); - case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 3fe1d9d9cc..9126f9aeff 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2657,6 +2657,26 @@ static const TCGOutOpBinary outop_orc = { .base.static_constraint = C_NotImplemented, }; +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_XOR + rexw, a0, a2); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_XOR + rexw, a0, a2, false); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2740,11 +2760,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, OP_32_64(sub): c = ARITH_SUB; - goto gen_arith; - OP_32_64(xor): - c = ARITH_XOR; - goto gen_arith; - gen_arith: if (const_a2) { tgen_arithi(s, c + rexw, a0, a2, 0); } else { @@ -3666,8 +3681,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: return C_O1_I2(r, 0, re); case INDEX_op_shl_i32: diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index b6f13090b9..296a84af79 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1357,6 +1357,24 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_xor(s, a0, a1, a2); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_xori(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rU), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1407,15 +1425,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: - if (c2) { - tcg_out_opc_xori(s, a0, a1, a2); - } else { - tcg_out_opc_xor(s, a0, a1, a2); - } - break; - case INDEX_op_extract_i32: if (a2 == 0 && args[3] <= 12) { tcg_out_opc_andi(s, a0, a1, (1 << args[3]) - 1); @@ -2297,8 +2306,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_nor_i32: case INDEX_op_nor_i64: - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: /* LoongArch reg-imm bitops have their imms ZERO-extended */ return C_O1_I2(r, r, rU); diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index f6987963ec..30fb01cb0a 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1734,6 +1734,24 @@ static const TCGOutOpBinary outop_orc = { .base.static_constraint = C_NotImplemented, }; +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_XOR, a0, a1, a2); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_imm(s, OPC_XORI, a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1807,13 +1825,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: - i1 = OPC_XOR, i2 = OPC_XORI; - if (c2) { - tcg_out_opc_imm(s, i2, a0, a1, a2); - break; - } do_binaryv: tcg_out_opc_reg(s, i1, a0, a1, a2); break; @@ -2235,9 +2246,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i64: case INDEX_op_mulu2_i64: return C_O2_I2(r, r, r, r); - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: - return C_O1_I2(r, r, rI); case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index ccd7812016..16d3dbd841 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2983,6 +2983,24 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, XOR | SAB(a1, a0, a2)); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_xori32(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rU), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3068,15 +3086,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_xor_i64: - case INDEX_op_xor_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_xori32(s, a0, a1, a2); - } else { - tcg_out32(s, XOR | SAB(a1, a0, a2)); - } - break; case INDEX_op_eqv_i32: if (const_args[2]) { tcg_out_xori32(s, args[0], args[1], ~args[2]); @@ -4141,7 +4150,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_xor_i32: case INDEX_op_eqv_i32: case INDEX_op_shl_i32: case INDEX_op_shr_i32: @@ -4180,8 +4188,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i32: return C_O1_I2(r, rI, ri); - case INDEX_op_xor_i64: - return C_O1_I2(r, r, rU); case INDEX_op_sub_i64: return C_O1_I2(r, rI, rT); case INDEX_op_clz_i32: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 14216e9dff..c981ea389a 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2043,6 +2043,24 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_XOR, a0, a1, a2); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_opc_imm(s, OPC_XORI, a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2121,15 +2139,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_XORI, a0, a1, a2); - } else { - tcg_out_opc_reg(s, OPC_XOR, a0, a1, a2); - } - break; - case INDEX_op_eqv_i32: case INDEX_op_eqv_i64: if (c2) { @@ -2695,8 +2704,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: case INDEX_op_negsetcond_i32: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 97587939bd..bedad7137b 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2283,6 +2283,31 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, XGRK, a0, a1, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, XR, a0, a2); + } else { + tcg_out_insn(s, RRFa, XRK, a0, a1, a2); + } +} + +static void tgen_xori_3(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + tgen_xori(s, a0, type == TCG_TYPE_I32 ? (uint32_t)a2 : a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rK), + .out_rrr = tgen_xor, + .out_rri = tgen_xori_3, +}; + # define OP_32_64(x) \ case glue(glue(INDEX_op_,x),_i32): \ @@ -2351,18 +2376,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_xor_i32: - a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - tcg_out_insn(s, RIL, XILF, a0, a2); - } else if (a0 == a1) { - tcg_out_insn(s, RR, XR, args[0], args[2]); - } else { - tcg_out_insn(s, RRFa, XRK, a0, a1, a2); - } - break; - case INDEX_op_eqv_i32: a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; if (const_args[2]) { @@ -2582,16 +2595,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_xor_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I64, a0, a1); - tgen_xori(s, a0, a2); - } else { - tcg_out_insn(s, RRFa, XGRK, a0, a1, a2); - } - break; - case INDEX_op_eqv_i64: a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[2]) { @@ -3285,10 +3288,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i32: case INDEX_op_sub_i64: - case INDEX_op_xor_i32: return C_O1_I2(r, r, ri); - case INDEX_op_xor_i64: - return C_O1_I2(r, r, rK); case INDEX_op_eqv_i32: return C_O1_I2(r, r, ri); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 38b325e8a9..8a6c9852d2 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1362,6 +1362,24 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_XOR); +} + +static void tgen_xori(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_arithi(s, a0, a1, a2, ARITH_XOR); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_xor, + .out_rri = tgen_xori, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1419,9 +1437,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, OP_32_64(sub): c = ARITH_SUB; goto gen_arith; - OP_32_64(xor): - c = ARITH_XOR; - goto gen_arith; case INDEX_op_shl_i32: c = SHIFT_SLL; do_shift32: @@ -1635,8 +1650,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_divu_i64: case INDEX_op_sub_i32: case INDEX_op_sub_i64: - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: case INDEX_op_shl_i32: case INDEX_op_shl_i64: case INDEX_op_shr_i32: diff --git a/tcg/tcg.c b/tcg/tcg.c index 1b7e230219..042f177966 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1009,6 +1009,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), + OUTOP(INDEX_op_xor_i32, TCGOutOpBinary, outop_xor), + OUTOP(INDEX_op_xor_i64, TCGOutOpBinary, outop_xor), }; #undef OUTOP @@ -5440,6 +5442,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_andc: case INDEX_op_or: case INDEX_op_orc: + case INDEX_op_xor_i32: + case INDEX_op_xor_i64: { const TCGOutOpBinary *out = container_of(all_outop[op->opc], TCGOutOpBinary, base); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index b9309e2fb9..85caff300f 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -101,8 +101,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_nand_i64: case INDEX_op_nor_i32: case INDEX_op_nor_i64: - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: case INDEX_op_shl_i32: case INDEX_op_shl_i64: case INDEX_op_shr_i32: @@ -686,6 +684,17 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_xor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, glue(INDEX_op_xor_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_xor = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_xor, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -730,7 +739,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, CASE_32_64(sub) CASE_32_64(mul) - CASE_32_64(xor) CASE_32_64(eqv) /* Optional (TCG_TARGET_HAS_eqv_*). */ CASE_32_64(nand) /* Optional (TCG_TARGET_HAS_nand_*). */ CASE_32_64(nor) /* Optional (TCG_TARGET_HAS_nor_*). */ From fffd3dc9022efe89b9196d738127c294cf43a4d6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 15:18:35 -0800 Subject: [PATCH 0378/2760] tcg: Merge INDEX_op_xor_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- target/sh4/translate.c | 6 +++--- tcg/optimize.c | 18 ++++++++---------- tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 9 +++------ tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 8 files changed, 21 insertions(+), 28 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index c5c5a4d19e..a4aa4f8824 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -311,7 +311,7 @@ Logical - | *t0* = *t1* | *t2* - * - xor_i32/i64 *t0*, *t1*, *t2* + * - xor *t0*, *t1*, *t2* - | *t0* = *t1* ^ *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index caf0f01042..8f6115bedb 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -44,6 +44,7 @@ DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) +DEF(xor, 1, 2, 0, TCG_OPF_INT) DEF(setcond_i32, 1, 2, 1, 0) DEF(negsetcond_i32, 1, 2, 1, 0) @@ -66,7 +67,6 @@ DEF(rem_i32, 1, 2, 0, 0) DEF(remu_i32, 1, 2, 0, 0) DEF(div2_i32, 2, 3, 0, 0) DEF(divu2_i32, 2, 3, 0, 0) -DEF(xor_i32, 1, 2, 0, 0) /* shifts/rotates */ DEF(shl_i32, 1, 2, 0, 0) DEF(shr_i32, 1, 2, 0, 0) @@ -124,7 +124,6 @@ DEF(rem_i64, 1, 2, 0, 0) DEF(remu_i64, 1, 2, 0, 0) DEF(div2_i64, 2, 3, 0, 0) DEF(divu2_i64, 2, 3, 0, 0) -DEF(xor_i64, 1, 2, 0, 0) /* shifts/rotates */ DEF(shl_i64, 1, 2, 0, 0) DEF(shr_i64, 1, 2, 0, 0) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 094613d312..8248648c0c 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -1946,7 +1946,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) op_opc = INDEX_op_and; goto do_reg_op; case 0x200a: /* xor Rm,Rn */ - op_opc = INDEX_op_xor_i32; + op_opc = INDEX_op_xor; goto do_reg_op; case 0x200b: /* or Rm,Rn */ op_opc = INDEX_op_or; @@ -1976,7 +1976,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) goto fail; } op_dst = B11_8; - op_opc = INDEX_op_xor_i32; + op_opc = INDEX_op_xor; op_arg = tcg_constant_i32(-1); break; @@ -2133,7 +2133,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) } break; - case INDEX_op_xor_i32: + case INDEX_op_xor: if (op_dst != st_src) { goto fail; } diff --git a/tcg/optimize.c b/tcg/optimize.c index 5f0ab354d6..9303bb5b64 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -441,7 +441,8 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) case INDEX_op_or_vec: return x | y; - CASE_OP_32_64_VEC(xor): + case INDEX_op_xor: + case INDEX_op_xor_vec: return x ^ y; case INDEX_op_shl_i32: @@ -2289,7 +2290,7 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) break; } if (convert) { - TCGOpcode xor_opc, neg_opc; + TCGOpcode neg_opc; if (!inv && !neg) { return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); @@ -2298,11 +2299,9 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) switch (ctx->type) { case TCG_TYPE_I32: neg_opc = INDEX_op_neg_i32; - xor_opc = INDEX_op_xor_i32; break; case TCG_TYPE_I64: neg_opc = INDEX_op_neg_i64; - xor_opc = INDEX_op_xor_i64; break; default: g_assert_not_reached(); @@ -2314,7 +2313,7 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) op->opc = INDEX_op_add; op->args[2] = arg_new_constant(ctx, -1); } else { - op->opc = xor_opc; + op->opc = INDEX_op_xor; op->args[2] = arg_new_constant(ctx, 1); } return -1; @@ -2325,7 +2324,7 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) { - TCGOpcode xor_opc, neg_opc, shr_opc; + TCGOpcode neg_opc, shr_opc; TCGOpcode uext_opc = 0, sext_opc = 0; TCGCond cond = op->args[3]; TCGArg ret, src1, src2; @@ -2347,7 +2346,6 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) switch (ctx->type) { case TCG_TYPE_I32: - xor_opc = INDEX_op_xor_i32; shr_opc = INDEX_op_shr_i32; neg_opc = INDEX_op_neg_i32; if (TCG_TARGET_extract_valid(TCG_TYPE_I32, sh, 1)) { @@ -2358,7 +2356,6 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) } break; case TCG_TYPE_I64: - xor_opc = INDEX_op_xor_i64; shr_opc = INDEX_op_shr_i64; neg_opc = INDEX_op_neg_i64; if (TCG_TARGET_extract_valid(TCG_TYPE_I64, sh, 1)) { @@ -2406,7 +2403,7 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) op2->args[1] = ret; op2->args[2] = arg_new_constant(ctx, -1); } else if (inv) { - op2 = opt_insert_after(ctx, op, xor_opc, 3); + op2 = opt_insert_after(ctx, op, INDEX_op_xor, 3); op2->args[0] = ret; op2->args[1] = ret; op2->args[2] = arg_new_constant(ctx, 1); @@ -3051,7 +3048,8 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(sub2): done = fold_sub2(&ctx, op); break; - CASE_OP_32_64_VEC(xor): + case INDEX_op_xor: + case INDEX_op_xor_vec: done = fold_xor(&ctx, op); break; case INDEX_op_set_label: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index bf481060fa..b10f61435c 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -453,7 +453,7 @@ void tcg_gen_ori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_xor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - tcg_gen_op3_i32(INDEX_op_xor_i32, ret, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_xor, ret, arg1, arg2); } void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) @@ -1595,7 +1595,7 @@ void tcg_gen_or_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op3_i64(INDEX_op_xor_i64, ret, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_xor, ret, arg1, arg2); } else { tcg_gen_xor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_xor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); diff --git a/tcg/tcg.c b/tcg/tcg.c index 042f177966..3c4905aa68 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1009,8 +1009,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), - OUTOP(INDEX_op_xor_i32, TCGOutOpBinary, outop_xor), - OUTOP(INDEX_op_xor_i64, TCGOutOpBinary, outop_xor), + OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), }; #undef OUTOP @@ -2215,6 +2214,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_and: case INDEX_op_mov: case INDEX_op_or: + case INDEX_op_xor: return has_type; case INDEX_op_setcond_i32: @@ -2231,7 +2231,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i32: case INDEX_op_neg_i32: case INDEX_op_mul_i32: - case INDEX_op_xor_i32: case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: @@ -2308,7 +2307,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: case INDEX_op_neg_i64: case INDEX_op_mul_i64: - case INDEX_op_xor_i64: case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: @@ -5442,8 +5440,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_andc: case INDEX_op_or: case INDEX_op_orc: - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: + case INDEX_op_xor: { const TCGOutOpBinary *out = container_of(all_outop[op->opc], TCGOutOpBinary, base); diff --git a/tcg/tci.c b/tcg/tci.c index 68636e70da..cb300c4846 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -543,7 +543,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] | regs[r2]; break; - CASE_32_64(xor) + case INDEX_op_xor: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ^ regs[r2]; break; @@ -1083,12 +1083,11 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_andc: case INDEX_op_or: case INDEX_op_orc: + case INDEX_op_xor: case INDEX_op_sub_i32: case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_xor_i32: - case INDEX_op_xor_i64: case INDEX_op_eqv_i32: case INDEX_op_eqv_i64: case INDEX_op_nand_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 85caff300f..0a912744b3 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -687,7 +687,7 @@ static const TCGOutOpBinary outop_orc = { static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_op_rrr(s, glue(INDEX_op_xor_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_xor, a0, a1, a2); } static const TCGOutOpBinary outop_xor = { From 46c68d75063d2d1119d5907e24e64e068ff64ba4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 Nov 2023 11:51:28 -0800 Subject: [PATCH 0379/2760] tcg/optimize: Fold eqv with immediate to xor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/optimize.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 9303bb5b64..e18fe37ad2 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1807,6 +1807,7 @@ static bool fold_dup2(OptContext *ctx, TCGOp *op) static bool fold_eqv(OptContext *ctx, TCGOp *op) { uint64_t s_mask; + TempOptInfo *t1, *t2; if (fold_const2_commutative(ctx, op) || fold_xi_to_x(ctx, op, -1) || @@ -1814,8 +1815,28 @@ static bool fold_eqv(OptContext *ctx, TCGOp *op) return true; } - s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; + t2 = arg_info(op->args[2]); + if (ti_is_const(t2)) { + /* Fold eqv r,x,i to xor r,x,~i. */ + switch (ctx->type) { + case TCG_TYPE_I32: + case TCG_TYPE_I64: + op->opc = INDEX_op_xor; + break; + case TCG_TYPE_V64: + case TCG_TYPE_V128: + case TCG_TYPE_V256: + op->opc = INDEX_op_xor_vec; + break; + default: + g_assert_not_reached(); + } + op->args[2] = arg_new_constant(ctx, ~ti_const_val(t2)); + return fold_xor(ctx, op); + } + + t1 = arg_info(op->args[1]); + s_mask = t1->s_mask & t2->s_mask; return fold_masks_s(ctx, op, s_mask); } From 18bc92091641880121d43584093ff207b0c44cfb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 15:37:43 -0800 Subject: [PATCH 0380/2760] tcg: Convert eqv to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 26 +++++++++------------ tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 4 ++++ tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 4 ++++ tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 4 ++++ tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 4 ++++ tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 22 +++++++++--------- tcg/riscv/tcg-target-con-set.h | 1 - tcg/riscv/tcg-target-con-str.h | 1 - tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 37 ++++++++++++------------------ tcg/s390x/tcg-target-con-set.h | 1 - tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 39 +++++++++++++------------------- tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 4 ++++ tcg/tcg-has.h | 1 - tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 8 +++---- tcg/tci.c | 2 -- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target.c.inc | 14 +++++++++--- 27 files changed, 89 insertions(+), 106 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 8469a9446f..c17aafc3bb 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -19,7 +19,6 @@ #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_eqv_i32 1 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 1 @@ -43,7 +42,6 @@ #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_eqv_i64 1 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index d575635fe0..83813af63e 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2157,6 +2157,17 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_eqv(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3510, EON, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_eqv, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2285,17 +2296,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_insn(s, 3502, SUB, ext, a0, TCG_REG_XZR, a1); break; - case INDEX_op_eqv_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_eqv_i64: - if (c2) { - tcg_out_logicali(s, I3404_EORI, ext, a0, a1, ~a2); - } else { - tcg_out_insn(s, 3510, EON, ext, a0, a1, a2); - } - break; - case INDEX_op_not_i64: case INDEX_op_not_i32: tcg_out_insn(s, 3510, ORN, ext, a0, TCG_REG_XZR, a1); @@ -3030,10 +3030,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_mulsh_i64: return C_O1_I2(r, r, r); - case INDEX_op_eqv_i32: - case INDEX_op_eqv_i64: - return C_O1_I2(r, r, rL); - case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 39dcc87fe8..9ed85798e7 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -28,7 +28,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 1 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 98cb3cf5e2..57acb44c7a 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1881,6 +1881,10 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index e525f23c05..0183cafe61 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -31,7 +31,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 1 @@ -55,7 +54,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 9126f9aeff..1fd53cb94f 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2633,6 +2633,10 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index fb1142958c..d3697ee0f2 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -25,7 +25,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 1 #define TCG_TARGET_HAS_clz_i32 1 @@ -45,7 +44,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 1 #define TCG_TARGET_HAS_clz_i64 1 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 296a84af79..6bd1826ef9 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1328,6 +1328,10 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index b3dfa390f9..9745c64db1 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -43,7 +43,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_nor_i32 1 -#define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) @@ -61,7 +60,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_nor_i64 1 -#define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 30fb01cb0a..3a3c72cb11 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1712,6 +1712,10 @@ static const TCGOutOpBinary outop_andc = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 6f3ab41ebb..8ede19bfad 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -23,7 +23,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_eqv_i32 1 #define TCG_TARGET_HAS_nand_i32 1 #define TCG_TARGET_HAS_nor_i32 1 #define TCG_TARGET_HAS_clz_i32 1 @@ -48,7 +47,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_eqv_i64 1 #define TCG_TARGET_HAS_nand_i64 1 #define TCG_TARGET_HAS_nor_i64 1 #define TCG_TARGET_HAS_clz_i64 1 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 16d3dbd841..203f089cd7 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2954,6 +2954,17 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_eqv(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, EQV | SAB(a1, a0, a2)); +} + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_eqv, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3086,15 +3097,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_eqv_i32: - if (const_args[2]) { - tcg_out_xori32(s, args[0], args[1], ~args[2]); - break; - } - /* FALLTHRU */ - case INDEX_op_eqv_i64: - tcg_out32(s, EQV | SAB(args[1], args[0], args[2])); - break; case INDEX_op_nand_i32: case INDEX_op_nand_i64: tcg_out32(s, NAND | SAB(args[1], args[0], args[2])); @@ -4150,7 +4152,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_eqv_i32: case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: @@ -4175,7 +4176,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_nor_i32: case INDEX_op_muluh_i32: case INDEX_op_mulsh_i32: - case INDEX_op_eqv_i64: case INDEX_op_nand_i64: case INDEX_op_nor_i64: case INDEX_op_div_i64: diff --git a/tcg/riscv/tcg-target-con-set.h b/tcg/riscv/tcg-target-con-set.h index f1f5d415f7..21f8833b3b 100644 --- a/tcg/riscv/tcg-target-con-set.h +++ b/tcg/riscv/tcg-target-con-set.h @@ -16,7 +16,6 @@ C_O1_I1(r, r) C_O1_I2(r, r, r) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) -C_O1_I2(r, r, rJ) C_O1_I2(r, rz, rN) C_O1_I2(r, rz, rz) C_N1_I2(r, r, rM) diff --git a/tcg/riscv/tcg-target-con-str.h b/tcg/riscv/tcg-target-con-str.h index 2f9700638c..1956f75f9a 100644 --- a/tcg/riscv/tcg-target-con-str.h +++ b/tcg/riscv/tcg-target-con-str.h @@ -16,7 +16,6 @@ REGS('v', ALL_VECTOR_REGS) * CONST(letter, TCG_CT_CONST_* bit set) */ CONST('I', TCG_CT_CONST_S12) -CONST('J', TCG_CT_CONST_J12) CONST('K', TCG_CT_CONST_S5) CONST('L', TCG_CT_CONST_CMP_VI) CONST('N', TCG_CT_CONST_N12) diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 7b8f4386c9..2faa2895e3 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -25,7 +25,6 @@ #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_eqv_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 (cpuinfo & CPUINFO_ZBB) @@ -44,7 +43,6 @@ #define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_eqv_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 (cpuinfo & CPUINFO_ZBB) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index c981ea389a..ff2a412821 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -115,9 +115,8 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define TCG_CT_CONST_S12 0x100 #define TCG_CT_CONST_N12 0x200 #define TCG_CT_CONST_M12 0x400 -#define TCG_CT_CONST_J12 0x800 -#define TCG_CT_CONST_S5 0x1000 -#define TCG_CT_CONST_CMP_VI 0x2000 +#define TCG_CT_CONST_S5 0x800 +#define TCG_CT_CONST_CMP_VI 0x1000 #define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) #define ALL_VECTOR_REGS MAKE_64BIT_MASK(32, 32) @@ -416,13 +415,6 @@ static bool tcg_target_const_match(int64_t val, int ct, if ((ct & TCG_CT_CONST_M12) && val >= -0x7ff && val <= 0x7ff) { return 1; } - /* - * Inverse of sign extended from 12 bits: ~[-0x800, 0x7ff]. - * Used to map ANDN back to ANDI, etc. - */ - if ((ct & TCG_CT_CONST_J12) && ~val >= -0x800 && ~val <= 0x7ff) { - return 1; - } /* * Sign extended from 5 bits: [-0x10, 0x0f]. * Used for vector-immediate. @@ -2013,6 +2005,18 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_eqv(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_XNOR, a0, a1, a2); +} + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_zbb_rrr, + .out_rrr = tgen_eqv, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2139,15 +2143,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_eqv_i32: - case INDEX_op_eqv_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_XORI, a0, a1, ~a2); - } else { - tcg_out_opc_reg(s, OPC_XNOR, a0, a1, a2); - } - break; - case INDEX_op_not_i32: case INDEX_op_not_i64: tcg_out_opc_imm(s, OPC_XORI, a0, a1, -1); @@ -2710,10 +2705,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_eqv_i32: - case INDEX_op_eqv_i64: - return C_O1_I2(r, r, rJ); - case INDEX_op_sub_i32: case INDEX_op_sub_i64: return C_O1_I2(r, rz, rN); diff --git a/tcg/s390x/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h index 39903a60ad..86af067965 100644 --- a/tcg/s390x/tcg-target-con-set.h +++ b/tcg/s390x/tcg-target-con-set.h @@ -31,7 +31,6 @@ C_O1_I2(r, r, rC) C_O1_I2(r, r, rI) C_O1_I2(r, r, rJ) C_O1_I2(r, r, rK) -C_O1_I2(r, r, rNK) C_O1_I2(r, r, rNKR) C_O1_I2(r, rZ, r) C_O1_I2(v, v, r) diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 850c16a164..722a2ede1c 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -34,7 +34,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_eqv_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_nand_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_nor_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_clz_i32 0 @@ -57,7 +56,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_eqv_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_nand_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_nor_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_clz_i64 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index bedad7137b..6c32aa286d 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2242,6 +2242,22 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_eqv(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, NXRK, a0, a1, a2); + } else { + tcg_out_insn(s, RRFa, NXGRK, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_misc3_rrr, + .out_rrr = tgen_eqv, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2376,15 +2392,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_eqv_i32: - a0 = args[0], a1 = args[1], a2 = (uint32_t)args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - tcg_out_insn(s, RIL, XILF, a0, ~a2); - } else { - tcg_out_insn(s, RRFa, NXRK, a0, a1, a2); - } - break; case INDEX_op_nand_i32: tcg_out_insn(s, RRFa, NNRK, args[0], args[1], args[2]); break; @@ -2595,15 +2602,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_eqv_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I64, a0, a1); - tgen_xori(s, a0, ~a2); - } else { - tcg_out_insn(s, RRFa, NXGRK, a0, a1, a2); - } - break; case INDEX_op_nand_i64: tcg_out_insn(s, RRFa, NNGRK, args[0], args[1], args[2]); break; @@ -3290,11 +3288,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: return C_O1_I2(r, r, ri); - case INDEX_op_eqv_i32: - return C_O1_I2(r, r, ri); - case INDEX_op_eqv_i64: - return C_O1_I2(r, r, rNK); - case INDEX_op_nand_i32: case INDEX_op_nand_i64: case INDEX_op_nor_i32: diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 8e20e4cdeb..2ec5f5657c 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -20,7 +20,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_eqv_i32 0 #define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 0 @@ -44,7 +43,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 8a6c9852d2..6d7ee19db1 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1333,6 +1333,10 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index df9c951262..a5808dcc0a 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -20,7 +20,6 @@ #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_not_i64 0 -#define TCG_TARGET_HAS_eqv_i64 0 #define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index b10f61435c..8008b0d3e0 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -680,7 +680,7 @@ void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_eqv_i32) { + if (tcg_op_supported(INDEX_op_eqv_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_eqv_i32, ret, arg1, arg2); } else { tcg_gen_xor_i32(ret, arg1, arg2); @@ -2279,7 +2279,7 @@ void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_eqv_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_eqv_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (TCG_TARGET_HAS_eqv_i64) { + } else if (tcg_op_supported(INDEX_op_eqv_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_eqv_i64, ret, arg1, arg2); } else { tcg_gen_xor_i64(ret, arg1, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index 3c4905aa68..53158a292b 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1007,6 +1007,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), + OUTOP(INDEX_op_eqv_i32, TCGOutOpBinary, outop_eqv), + OUTOP(INDEX_op_eqv_i64, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), @@ -2273,8 +2275,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap32_i32; case INDEX_op_not_i32: return TCG_TARGET_HAS_not_i32; - case INDEX_op_eqv_i32: - return TCG_TARGET_HAS_eqv_i32; case INDEX_op_nand_i32: return TCG_TARGET_HAS_nand_i32; case INDEX_op_nor_i32: @@ -2344,8 +2344,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap64_i64; case INDEX_op_not_i64: return TCG_TARGET_HAS_not_i64; - case INDEX_op_eqv_i64: - return TCG_TARGET_HAS_eqv_i64; case INDEX_op_nand_i64: return TCG_TARGET_HAS_nand_i64; case INDEX_op_nor_i64: @@ -5438,6 +5436,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: + case INDEX_op_eqv_i32: + case INDEX_op_eqv_i64: case INDEX_op_or: case INDEX_op_orc: case INDEX_op_xor: diff --git a/tcg/tci.c b/tcg/tci.c index cb300c4846..26a271e71f 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -555,12 +555,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] | ~regs[r2]; break; -#if TCG_TARGET_HAS_eqv_i32 || TCG_TARGET_HAS_eqv_i64 CASE_32_64(eqv) tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] ^ regs[r2]); break; -#endif #if TCG_TARGET_HAS_nand_i32 || TCG_TARGET_HAS_nand_i64 CASE_32_64(nand) tci_args_rrr(insn, &r0, &r1, &r2); diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index d247774e52..2c0876a0fd 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -12,7 +12,6 @@ #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_eqv_i32 1 #define TCG_TARGET_HAS_nand_i32 1 #define TCG_TARGET_HAS_nor_i32 1 #define TCG_TARGET_HAS_clz_i32 1 @@ -34,7 +33,6 @@ #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_eqv_i64 1 #define TCG_TARGET_HAS_nand_i64 1 #define TCG_TARGET_HAS_nor_i64 1 #define TCG_TARGET_HAS_clz_i64 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 0a912744b3..4c9e055614 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -95,8 +95,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_eqv_i32: - case INDEX_op_eqv_i64: case INDEX_op_nand_i32: case INDEX_op_nand_i64: case INDEX_op_nor_i32: @@ -662,6 +660,17 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_eqv(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, glue(INDEX_op_eqv_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_eqv = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_eqv, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -739,7 +748,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, CASE_32_64(sub) CASE_32_64(mul) - CASE_32_64(eqv) /* Optional (TCG_TARGET_HAS_eqv_*). */ CASE_32_64(nand) /* Optional (TCG_TARGET_HAS_nand_*). */ CASE_32_64(nor) /* Optional (TCG_TARGET_HAS_nor_*). */ CASE_32_64(shl) From 5c0968a7e1da73f91f148d563a29af529427c5a5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 15:47:53 -0800 Subject: [PATCH 0381/2760] tcg: Merge INDEX_op_eqv_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 6 ++++-- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 6 ++---- tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 15 insertions(+), 17 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index a4aa4f8824..fe149e012d 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -323,7 +323,7 @@ Logical - | *t0* = *t1* & ~\ *t2* - * - eqv_i32/i64 *t0*, *t1*, *t2* + * - eqv *t0*, *t1*, *t2* - | *t0* = ~(*t1* ^ *t2*), or equivalently, *t0* = *t1* ^ ~\ *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 8f6115bedb..c6869de244 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -42,6 +42,7 @@ DEF(mov, 1, 1, 0, TCG_OPF_INT | TCG_OPF_NOT_PRESENT) DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) +DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) @@ -93,7 +94,6 @@ DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) DEF(not_i32, 1, 1, 0, 0) DEF(neg_i32, 1, 1, 0, 0) -DEF(eqv_i32, 1, 2, 0, 0) DEF(nand_i32, 1, 2, 0, 0) DEF(nor_i32, 1, 2, 0, 0) DEF(clz_i32, 1, 2, 0, 0) @@ -147,7 +147,6 @@ DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) DEF(not_i64, 1, 1, 0, 0) DEF(neg_i64, 1, 1, 0, 0) -DEF(eqv_i64, 1, 2, 0, 0) DEF(nand_i64, 1, 2, 0, 0) DEF(nor_i64, 1, 2, 0, 0) DEF(clz_i64, 1, 2, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index e18fe37ad2..47898b7086 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -489,7 +489,8 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) case INDEX_op_orc_vec: return x | ~y; - CASE_OP_32_64_VEC(eqv): + case INDEX_op_eqv: + case INDEX_op_eqv_vec: return ~(x ^ y); CASE_OP_32_64_VEC(nand): @@ -2929,7 +2930,8 @@ void tcg_optimize(TCGContext *s) case INDEX_op_dup2_vec: done = fold_dup2(&ctx, op); break; - CASE_OP_32_64_VEC(eqv): + case INDEX_op_eqv: + case INDEX_op_eqv_vec: done = fold_eqv(&ctx, op); break; CASE_OP_32_64(extract): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 8008b0d3e0..2520a60cee 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -680,8 +680,8 @@ void tcg_gen_andc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_eqv_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3_i32(INDEX_op_eqv_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_eqv, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_eqv, ret, arg1, arg2); } else { tcg_gen_xor_i32(ret, arg1, arg2); tcg_gen_not_i32(ret, ret); @@ -2279,8 +2279,8 @@ void tcg_gen_eqv_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_eqv_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_eqv_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (tcg_op_supported(INDEX_op_eqv_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3_i64(INDEX_op_eqv_i64, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_eqv, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_eqv, ret, arg1, arg2); } else { tcg_gen_xor_i64(ret, arg1, arg2); tcg_gen_not_i64(ret, ret); diff --git a/tcg/tcg.c b/tcg/tcg.c index 53158a292b..6642429df6 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1007,8 +1007,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), - OUTOP(INDEX_op_eqv_i32, TCGOutOpBinary, outop_eqv), - OUTOP(INDEX_op_eqv_i64, TCGOutOpBinary, outop_eqv), + OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), @@ -5436,8 +5435,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: - case INDEX_op_eqv_i32: - case INDEX_op_eqv_i64: + case INDEX_op_eqv: case INDEX_op_or: case INDEX_op_orc: case INDEX_op_xor: diff --git a/tcg/tci.c b/tcg/tci.c index 26a271e71f..d2baa8d3fc 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -555,7 +555,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] | ~regs[r2]; break; - CASE_32_64(eqv) + case INDEX_op_eqv: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] ^ regs[r2]); break; @@ -1079,6 +1079,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: + case INDEX_op_eqv: case INDEX_op_or: case INDEX_op_orc: case INDEX_op_xor: @@ -1086,8 +1087,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_eqv_i32: - case INDEX_op_eqv_i64: case INDEX_op_nand_i32: case INDEX_op_nand_i64: case INDEX_op_nor_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 4c9e055614..fe3450373e 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -663,7 +663,7 @@ static const TCGOutOpBinary outop_andc = { static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_op_rrr(s, glue(INDEX_op_eqv_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_eqv, a0, a1, a2); } static const TCGOutOpBinary outop_eqv = { From e5a3162cb6c1aec4f47c6b51671e989b5975f345 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 16:18:19 -0800 Subject: [PATCH 0382/2760] tcg: Convert nand to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 4 ++++ tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 4 ++++ tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 4 ++++ tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 4 ++++ tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 4 ++++ tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 17 +++++++++++------ tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 4 ++++ tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 24 ++++++++++++++++-------- tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 4 ++++ tcg/tcg-has.h | 1 - tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 8 ++++---- tcg/tci.c | 2 -- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target.c.inc | 14 +++++++++++--- 24 files changed, 72 insertions(+), 45 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index c17aafc3bb..2acc9bd3b7 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -19,7 +19,6 @@ #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 @@ -42,7 +41,6 @@ #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 83813af63e..093bb0afb7 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2168,6 +2168,10 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 9ed85798e7..8d7b176993 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -28,7 +28,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 use_armv7_instructions diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 57acb44c7a..55d28be15b 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1885,6 +1885,10 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index 0183cafe61..93552f2337 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -31,7 +31,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 @@ -54,7 +53,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 1fd53cb94f..51c3711ee5 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2637,6 +2637,10 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index d3697ee0f2..55249de465 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -25,7 +25,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 @@ -44,7 +43,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 6bd1826ef9..814596608a 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1332,6 +1332,10 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 9745c64db1..2f8325d56f 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -43,7 +43,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_nor_i32 1 -#define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muluh_i32 1 @@ -60,7 +59,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_nor_i64 1 -#define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 (!use_mips32r6_instructions) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 3a3c72cb11..46cf393041 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1716,6 +1716,10 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 8ede19bfad..810f20d120 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -23,7 +23,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nand_i32 1 #define TCG_TARGET_HAS_nor_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 have_isa_3_00 @@ -47,7 +46,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_nand_i64 1 #define TCG_TARGET_HAS_nor_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 have_isa_3_00 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 203f089cd7..29341aff2c 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2965,6 +2965,17 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static void tgen_nand(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, NAND | SAB(a1, a0, a2)); +} + +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_nand, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3097,10 +3108,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_nand_i32: - case INDEX_op_nand_i64: - tcg_out32(s, NAND | SAB(args[1], args[0], args[2])); - break; case INDEX_op_nor_i32: case INDEX_op_nor_i64: tcg_out32(s, NOR | SAB(args[1], args[0], args[2])); @@ -4172,11 +4179,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_nand_i32: case INDEX_op_nor_i32: case INDEX_op_muluh_i32: case INDEX_op_mulsh_i32: - case INDEX_op_nand_i64: case INDEX_op_nor_i64: case INDEX_op_div_i64: case INDEX_op_divu_i64: diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 2faa2895e3..3736a52d56 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -25,7 +25,6 @@ #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctz_i32 (cpuinfo & CPUINFO_ZBB) @@ -43,7 +42,6 @@ #define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctz_i64 (cpuinfo & CPUINFO_ZBB) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index ff2a412821..cb2b58e495 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2017,6 +2017,10 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 722a2ede1c..d8afd73814 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -34,7 +34,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_nand_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_nor_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_clz_i32 0 #define TCG_TARGET_HAS_ctz_i32 0 @@ -56,7 +55,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_nand_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_nor_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 0 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 6c32aa286d..33eece6e5d 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2258,6 +2258,22 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static void tgen_nand(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, NNRK, a0, a1, a2); + } else { + tcg_out_insn(s, RRFa, NNGRK, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_misc3_rrr, + .out_rrr = tgen_nand, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2392,9 +2408,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_nand_i32: - tcg_out_insn(s, RRFa, NNRK, args[0], args[1], args[2]); - break; case INDEX_op_nor_i32: tcg_out_insn(s, RRFa, NORK, args[0], args[1], args[2]); break; @@ -2602,9 +2615,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_nand_i64: - tcg_out_insn(s, RRFa, NNGRK, args[0], args[1], args[2]); - break; case INDEX_op_nor_i64: tcg_out_insn(s, RRFa, NOGRK, args[0], args[1], args[2]); break; @@ -3288,8 +3298,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: return C_O1_I2(r, r, ri); - case INDEX_op_nand_i32: - case INDEX_op_nand_i64: case INDEX_op_nor_i32: case INDEX_op_nor_i64: return C_O1_I2(r, r, r); diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 2ec5f5657c..9bc0474107 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -20,7 +20,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nand_i32 0 #define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 0 #define TCG_TARGET_HAS_ctz_i32 0 @@ -43,7 +42,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 0 #define TCG_TARGET_HAS_ctz_i64 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 6d7ee19db1..02c443efb9 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1337,6 +1337,10 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index a5808dcc0a..e2a99067ac 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -20,7 +20,6 @@ #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_not_i64 0 -#define TCG_TARGET_HAS_nand_i64 0 #define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 0 #define TCG_TARGET_HAS_ctz_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 2520a60cee..3921bac48d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -690,7 +690,7 @@ void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_nand_i32) { + if (tcg_op_supported(INDEX_op_nand_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_nand_i32, ret, arg1, arg2); } else { tcg_gen_and_i32(ret, arg1, arg2); @@ -2292,7 +2292,7 @@ void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_nand_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_nand_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (TCG_TARGET_HAS_nand_i64) { + } else if (tcg_op_supported(INDEX_op_nand_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_nand_i64, ret, arg1, arg2); } else { tcg_gen_and_i64(ret, arg1, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index 6642429df6..50361864aa 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1008,6 +1008,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), + OUTOP(INDEX_op_nand_i32, TCGOutOpBinary, outop_nand), + OUTOP(INDEX_op_nand_i64, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), @@ -2274,8 +2276,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap32_i32; case INDEX_op_not_i32: return TCG_TARGET_HAS_not_i32; - case INDEX_op_nand_i32: - return TCG_TARGET_HAS_nand_i32; case INDEX_op_nor_i32: return TCG_TARGET_HAS_nor_i32; case INDEX_op_clz_i32: @@ -2343,8 +2343,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap64_i64; case INDEX_op_not_i64: return TCG_TARGET_HAS_not_i64; - case INDEX_op_nand_i64: - return TCG_TARGET_HAS_nand_i64; case INDEX_op_nor_i64: return TCG_TARGET_HAS_nor_i64; case INDEX_op_clz_i64: @@ -5436,6 +5434,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_eqv: + case INDEX_op_nand_i32: + case INDEX_op_nand_i64: case INDEX_op_or: case INDEX_op_orc: case INDEX_op_xor: diff --git a/tcg/tci.c b/tcg/tci.c index d2baa8d3fc..8be59a0193 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -559,12 +559,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] ^ regs[r2]); break; -#if TCG_TARGET_HAS_nand_i32 || TCG_TARGET_HAS_nand_i64 CASE_32_64(nand) tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] & regs[r2]); break; -#endif #if TCG_TARGET_HAS_nor_i32 || TCG_TARGET_HAS_nor_i64 CASE_32_64(nor) tci_args_rrr(insn, &r0, &r1, &r2); diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 2c0876a0fd..8be70297f5 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -12,7 +12,6 @@ #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_nand_i32 1 #define TCG_TARGET_HAS_nor_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 @@ -33,7 +32,6 @@ #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_nand_i64 1 #define TCG_TARGET_HAS_nor_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index fe3450373e..2a5c72705d 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -95,8 +95,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_nand_i32: - case INDEX_op_nand_i64: case INDEX_op_nor_i32: case INDEX_op_nor_i64: case INDEX_op_shl_i32: @@ -671,6 +669,17 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static void tgen_nand(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, glue(INDEX_op_nand_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_nand = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_nand, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -748,7 +757,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, CASE_32_64(sub) CASE_32_64(mul) - CASE_32_64(nand) /* Optional (TCG_TARGET_HAS_nand_*). */ CASE_32_64(nor) /* Optional (TCG_TARGET_HAS_nor_*). */ CASE_32_64(shl) CASE_32_64(shr) From 59379a45af1f4d62fc8c1ae0ddee988f47075787 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 20:32:54 -0800 Subject: [PATCH 0383/2760] tcg: Merge INDEX_op_nand_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 6 ++++-- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 6 ++---- tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 15 insertions(+), 17 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index fe149e012d..7703dfbc51 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -327,7 +327,7 @@ Logical - | *t0* = ~(*t1* ^ *t2*), or equivalently, *t0* = *t1* ^ ~\ *t2* - * - nand_i32/i64 *t0*, *t1*, *t2* + * - nand *t0*, *t1*, *t2* - | *t0* = ~(*t1* & *t2*) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index c6869de244..1acdd7cfda 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -43,6 +43,7 @@ DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) +DEF(nand, 1, 2, 0, TCG_OPF_INT) DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) @@ -94,7 +95,6 @@ DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) DEF(not_i32, 1, 1, 0, 0) DEF(neg_i32, 1, 1, 0, 0) -DEF(nand_i32, 1, 2, 0, 0) DEF(nor_i32, 1, 2, 0, 0) DEF(clz_i32, 1, 2, 0, 0) DEF(ctz_i32, 1, 2, 0, 0) @@ -147,7 +147,6 @@ DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) DEF(not_i64, 1, 1, 0, 0) DEF(neg_i64, 1, 1, 0, 0) -DEF(nand_i64, 1, 2, 0, 0) DEF(nor_i64, 1, 2, 0, 0) DEF(clz_i64, 1, 2, 0, 0) DEF(ctz_i64, 1, 2, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index 47898b7086..e8e6a0c2ce 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -493,7 +493,8 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) case INDEX_op_eqv_vec: return ~(x ^ y); - CASE_OP_32_64_VEC(nand): + case INDEX_op_nand: + case INDEX_op_nand_vec: return ~(x & y); CASE_OP_32_64_VEC(nor): @@ -2992,7 +2993,8 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(mulu2): done = fold_multiply2(&ctx, op); break; - CASE_OP_32_64_VEC(nand): + case INDEX_op_nand: + case INDEX_op_nand_vec: done = fold_nand(&ctx, op); break; CASE_OP_32_64(neg): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 3921bac48d..57782864fa 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -690,8 +690,8 @@ void tcg_gen_eqv_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_nand_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3_i32(INDEX_op_nand_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_nand, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_nand, ret, arg1, arg2); } else { tcg_gen_and_i32(ret, arg1, arg2); tcg_gen_not_i32(ret, ret); @@ -2292,8 +2292,8 @@ void tcg_gen_nand_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_nand_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_nand_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (tcg_op_supported(INDEX_op_nand_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3_i64(INDEX_op_nand_i64, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_nand, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_nand, ret, arg1, arg2); } else { tcg_gen_and_i64(ret, arg1, arg2); tcg_gen_not_i64(ret, ret); diff --git a/tcg/tcg.c b/tcg/tcg.c index 50361864aa..72e9175d06 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1008,8 +1008,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), - OUTOP(INDEX_op_nand_i32, TCGOutOpBinary, outop_nand), - OUTOP(INDEX_op_nand_i64, TCGOutOpBinary, outop_nand), + OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), @@ -5434,8 +5433,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_eqv: - case INDEX_op_nand_i32: - case INDEX_op_nand_i64: + case INDEX_op_nand: case INDEX_op_or: case INDEX_op_orc: case INDEX_op_xor: diff --git a/tcg/tci.c b/tcg/tci.c index 8be59a0193..9886ddf001 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -559,7 +559,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] ^ regs[r2]); break; - CASE_32_64(nand) + case INDEX_op_nand: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] & regs[r2]); break; @@ -1078,6 +1078,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_eqv: + case INDEX_op_nand: case INDEX_op_or: case INDEX_op_orc: case INDEX_op_xor: @@ -1085,8 +1086,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_nand_i32: - case INDEX_op_nand_i64: case INDEX_op_nor_i32: case INDEX_op_nor_i64: case INDEX_op_div_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 2a5c72705d..34a44a7674 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -672,7 +672,7 @@ static const TCGOutOpBinary outop_eqv = { static void tgen_nand(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_op_rrr(s, glue(INDEX_op_nand_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_nand, a0, a1, a2); } static const TCGOutOpBinary outop_nand = { From 8fb04b8295994e7416a222906939696d496638f3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 20:37:25 -0800 Subject: [PATCH 0384/2760] tcg/loongarch64: Do not accept constant argument to nor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The instruction set does not implement nor with immediate. There is no reason to pretend that we do. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target.c.inc | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 814596608a..e74c7d8a87 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1425,12 +1425,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_nor_i32: case INDEX_op_nor_i64: - if (c2) { - tcg_out_opc_ori(s, a0, a1, a2); - tcg_out_opc_nor(s, a0, a0, TCG_REG_ZERO); - } else { - tcg_out_opc_nor(s, a0, a1, a2); - } + tcg_out_opc_nor(s, a0, a1, a2); break; case INDEX_op_extract_i32: @@ -2314,8 +2309,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_nor_i32: case INDEX_op_nor_i64: - /* LoongArch reg-imm bitops have their imms ZERO-extended */ - return C_O1_I2(r, r, rU); + return C_O1_I2(r, r, r); case INDEX_op_clz_i32: case INDEX_op_clz_i64: From 3f6b223012f62bb3f73552bea4489383f08cdc23 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 20:57:21 -0800 Subject: [PATCH 0385/2760] tcg: Convert nor to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 4 ++++ tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 4 ++++ tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 4 ++++ tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 20 +++++++++++--------- tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 17 +++++++++++------ tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 18 +++++++++++------- tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 4 ++++ tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 28 ++++++++++++++++------------ tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 4 ++++ tcg/tcg-has.h | 1 - tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 8 ++++---- tcg/tci.c | 2 -- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target.c.inc | 14 +++++++++++--- 24 files changed, 86 insertions(+), 65 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 2acc9bd3b7..240fcac2cc 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -19,7 +19,6 @@ #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 0 @@ -41,7 +40,6 @@ #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_rot_i64 1 -#define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 0 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 093bb0afb7..30cad937b7 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2172,6 +2172,10 @@ static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 8d7b176993..e80711ee40 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -28,7 +28,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 -#define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 use_armv7_instructions #define TCG_TARGET_HAS_ctpop_i32 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 55d28be15b..8e9edeb7c6 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1889,6 +1889,10 @@ static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index 93552f2337..b27f853dcd 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -31,7 +31,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 have_popcnt @@ -53,7 +52,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 have_popcnt diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 51c3711ee5..9185f6879c 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2641,6 +2641,10 @@ static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 55249de465..7860432489 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -25,7 +25,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nor_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 0 @@ -43,7 +42,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_nor_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 0 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index e74c7d8a87..dc4ed0e8b0 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1336,6 +1336,17 @@ static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; +static void tgen_nor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_nor(s, a0, a1, a2); +} + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_nor, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1423,11 +1434,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_nor(s, a0, a1, TCG_REG_ZERO); break; - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - tcg_out_opc_nor(s, a0, a1, a2); - break; - case INDEX_op_extract_i32: if (a2 == 0 && args[3] <= 12) { tcg_out_opc_andi(s, a0, a1, (1 << args[3]) - 1); @@ -2307,10 +2313,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rotr_i64: return C_O1_I2(r, r, ri); - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - return C_O1_I2(r, r, r); - case INDEX_op_clz_i32: case INDEX_op_clz_i64: case INDEX_op_ctz_i32: diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 2f8325d56f..987f83f761 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -42,7 +42,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nor_i32 1 #define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muluh_i32 1 @@ -58,7 +57,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_nor_i64 1 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 (!use_mips32r6_instructions) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 46cf393041..bfe329b3ef 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1720,6 +1720,17 @@ static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; +static void tgen_nor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_NOR, a0, a1, a2); +} + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_nor, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1848,10 +1859,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; } goto do_binaryv; - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - i1 = OPC_NOR; - goto do_binaryv; case INDEX_op_mul_i32: if (use_mips32_instructions) { @@ -2237,7 +2244,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_nor_i32: case INDEX_op_setcond_i32: case INDEX_op_mul_i64: case INDEX_op_mulsh_i64: @@ -2246,7 +2252,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_divu_i64: case INDEX_op_rem_i64: case INDEX_op_remu_i64: - case INDEX_op_nor_i64: case INDEX_op_setcond_i64: return C_O1_I2(r, rz, rz); case INDEX_op_muls2_i32: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 810f20d120..6be6d7f994 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -23,7 +23,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nor_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 have_isa_3_00 #define TCG_TARGET_HAS_ctpop_i32 have_isa_2_06 @@ -46,7 +45,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_nor_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 have_isa_3_00 #define TCG_TARGET_HAS_ctpop_i64 have_isa_2_06 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 29341aff2c..c3366e4316 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2976,6 +2976,17 @@ static const TCGOutOpBinary outop_nand = { .out_rrr = tgen_nand, }; +static void tgen_nor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, NOR | SAB(a1, a0, a2)); +} + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_nor, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3108,11 +3119,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - tcg_out32(s, NOR | SAB(args[1], args[0], args[2])); - break; - case INDEX_op_clz_i32: tcg_out_cntxz(s, TCG_TYPE_I32, CNTLZW, args[0], args[1], args[2], const_args[2]); @@ -4179,10 +4185,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_nor_i32: case INDEX_op_muluh_i32: case INDEX_op_mulsh_i32: - case INDEX_op_nor_i64: case INDEX_op_div_i64: case INDEX_op_divu_i64: case INDEX_op_rem_i64: diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 3736a52d56..0fcf940a8a 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -25,7 +25,6 @@ #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctz_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctpop_i32 (cpuinfo & CPUINFO_ZBB) @@ -42,7 +41,6 @@ #define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctz_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctpop_i64 (cpuinfo & CPUINFO_ZBB) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index cb2b58e495..887f20d4cb 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2021,6 +2021,10 @@ static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index d8afd73814..374db3cf9d 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -34,7 +34,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_not_i32 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_nor_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_clz_i32 0 #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 1 @@ -55,7 +54,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_not_i64 HAVE_FACILITY(MISC_INSN_EXT3) -#define TCG_TARGET_HAS_nor_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 33eece6e5d..29570d3be1 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2274,6 +2274,22 @@ static const TCGOutOpBinary outop_nand = { .out_rrr = tgen_nand, }; +static void tgen_nor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, NORK, a0, a1, a2); + } else { + tcg_out_insn(s, RRFa, NOGRK, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_misc3_rrr, + .out_rrr = tgen_nor, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2408,10 +2424,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_nor_i32: - tcg_out_insn(s, RRFa, NORK, args[0], args[1], args[2]); - break; - case INDEX_op_neg_i32: tcg_out_insn(s, RR, LCR, args[0], args[1]); break; @@ -2615,10 +2627,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_nor_i64: - tcg_out_insn(s, RRFa, NOGRK, args[0], args[1], args[2]); - break; - case INDEX_op_neg_i64: tcg_out_insn(s, RRE, LCGR, args[0], args[1]); break; @@ -3298,10 +3306,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: return C_O1_I2(r, r, ri); - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: - return C_O1_I2(r, r, r); - case INDEX_op_mul_i32: return (HAVE_FACILITY(MISC_INSN_EXT2) ? C_O1_I2(r, r, ri) diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 9bc0474107..35ae536879 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -20,7 +20,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 #define TCG_TARGET_HAS_not_i32 1 -#define TCG_TARGET_HAS_nor_i32 0 #define TCG_TARGET_HAS_clz_i32 0 #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 0 @@ -42,7 +41,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_not_i64 1 -#define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 0 #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 02c443efb9..1ebff04af4 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1341,6 +1341,10 @@ static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index e2a99067ac..7de13ef383 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -20,7 +20,6 @@ #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_not_i64 0 -#define TCG_TARGET_HAS_nor_i64 0 #define TCG_TARGET_HAS_clz_i64 0 #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 57782864fa..ac939bb4ea 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -700,7 +700,7 @@ void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_nor_i32) { + if (tcg_op_supported(INDEX_op_nor_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_nor_i32, ret, arg1, arg2); } else { tcg_gen_or_i32(ret, arg1, arg2); @@ -2305,7 +2305,7 @@ void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_nor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_nor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (TCG_TARGET_HAS_nor_i64) { + } else if (tcg_op_supported(INDEX_op_nor_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_nor_i64, ret, arg1, arg2); } else { tcg_gen_or_i64(ret, arg1, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index 72e9175d06..d9807b77dc 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1009,6 +1009,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), + OUTOP(INDEX_op_nor_i32, TCGOutOpBinary, outop_nor), + OUTOP(INDEX_op_nor_i64, TCGOutOpBinary, outop_nor), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), @@ -2275,8 +2277,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap32_i32; case INDEX_op_not_i32: return TCG_TARGET_HAS_not_i32; - case INDEX_op_nor_i32: - return TCG_TARGET_HAS_nor_i32; case INDEX_op_clz_i32: return TCG_TARGET_HAS_clz_i32; case INDEX_op_ctz_i32: @@ -2342,8 +2342,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap64_i64; case INDEX_op_not_i64: return TCG_TARGET_HAS_not_i64; - case INDEX_op_nor_i64: - return TCG_TARGET_HAS_nor_i64; case INDEX_op_clz_i64: return TCG_TARGET_HAS_clz_i64; case INDEX_op_ctz_i64: @@ -5434,6 +5432,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_andc: case INDEX_op_eqv: case INDEX_op_nand: + case INDEX_op_nor_i32: + case INDEX_op_nor_i64: case INDEX_op_or: case INDEX_op_orc: case INDEX_op_xor: diff --git a/tcg/tci.c b/tcg/tci.c index 9886ddf001..3ea93fa5a6 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -563,12 +563,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] & regs[r2]); break; -#if TCG_TARGET_HAS_nor_i32 || TCG_TARGET_HAS_nor_i64 CASE_32_64(nor) tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] | regs[r2]); break; -#endif /* Arithmetic operations (32 bit). */ diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 8be70297f5..13c9dc3dfa 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -12,7 +12,6 @@ #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_nor_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 1 @@ -32,7 +31,6 @@ #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_nor_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 34a44a7674..a0f4c58be8 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -95,8 +95,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: case INDEX_op_shl_i32: case INDEX_op_shl_i64: case INDEX_op_shr_i32: @@ -680,6 +678,17 @@ static const TCGOutOpBinary outop_nand = { .out_rrr = tgen_nand, }; +static void tgen_nor(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, glue(INDEX_op_nor_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_nor = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_nor, +}; + static void tgen_or(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -757,7 +766,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, CASE_32_64(sub) CASE_32_64(mul) - CASE_32_64(nor) /* Optional (TCG_TARGET_HAS_nor_*). */ CASE_32_64(shl) CASE_32_64(shr) CASE_32_64(sar) From 3a8c4e9e53c6f4aa7c590971950000b174e74fa1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 21:02:17 -0800 Subject: [PATCH 0386/2760] tcg: Merge INDEX_op_nor_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 6 ++++-- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 6 ++---- tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 15 insertions(+), 17 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 7703dfbc51..26d464fa38 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -331,7 +331,7 @@ Logical - | *t0* = ~(*t1* & *t2*) - * - nor_i32/i64 *t0*, *t1*, *t2* + * - nor *t0*, *t1*, *t2* - | *t0* = ~(*t1* | *t2*) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 1acdd7cfda..aa9ed393c9 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -44,6 +44,7 @@ DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(nand, 1, 2, 0, TCG_OPF_INT) +DEF(nor, 1, 2, 0, TCG_OPF_INT) DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) @@ -95,7 +96,6 @@ DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) DEF(not_i32, 1, 1, 0, 0) DEF(neg_i32, 1, 1, 0, 0) -DEF(nor_i32, 1, 2, 0, 0) DEF(clz_i32, 1, 2, 0, 0) DEF(ctz_i32, 1, 2, 0, 0) DEF(ctpop_i32, 1, 1, 0, 0) @@ -147,7 +147,6 @@ DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) DEF(not_i64, 1, 1, 0, 0) DEF(neg_i64, 1, 1, 0, 0) -DEF(nor_i64, 1, 2, 0, 0) DEF(clz_i64, 1, 2, 0, 0) DEF(ctz_i64, 1, 2, 0, 0) DEF(ctpop_i64, 1, 1, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index e8e6a0c2ce..e4c319fe45 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -497,7 +497,8 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) case INDEX_op_nand_vec: return ~(x & y); - CASE_OP_32_64_VEC(nor): + case INDEX_op_nor: + case INDEX_op_nor_vec: return ~(x | y); case INDEX_op_clz_i32: @@ -3000,7 +3001,8 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(neg): done = fold_neg(&ctx, op); break; - CASE_OP_32_64_VEC(nor): + case INDEX_op_nor: + case INDEX_op_nor_vec: done = fold_nor(&ctx, op); break; CASE_OP_32_64_VEC(not): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index ac939bb4ea..228aa8f088 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -700,8 +700,8 @@ void tcg_gen_nand_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_nor_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_nor_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3_i32(INDEX_op_nor_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_nor, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_nor, ret, arg1, arg2); } else { tcg_gen_or_i32(ret, arg1, arg2); tcg_gen_not_i32(ret, ret); @@ -2305,8 +2305,8 @@ void tcg_gen_nor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_nor_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_LOW(arg2)); tcg_gen_nor_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), TCGV_HIGH(arg2)); - } else if (tcg_op_supported(INDEX_op_nor_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3_i64(INDEX_op_nor_i64, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_nor, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_nor, ret, arg1, arg2); } else { tcg_gen_or_i64(ret, arg1, arg2); tcg_gen_not_i64(ret, ret); diff --git a/tcg/tcg.c b/tcg/tcg.c index d9807b77dc..c0178030ce 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1009,8 +1009,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), - OUTOP(INDEX_op_nor_i32, TCGOutOpBinary, outop_nor), - OUTOP(INDEX_op_nor_i64, TCGOutOpBinary, outop_nor), + OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), @@ -5432,8 +5431,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_andc: case INDEX_op_eqv: case INDEX_op_nand: - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: + case INDEX_op_nor: case INDEX_op_or: case INDEX_op_orc: case INDEX_op_xor: diff --git a/tcg/tci.c b/tcg/tci.c index 3ea93fa5a6..ff129266c2 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -563,7 +563,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] & regs[r2]); break; - CASE_32_64(nor) + case INDEX_op_nor: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] | regs[r2]); break; @@ -1077,6 +1077,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_andc: case INDEX_op_eqv: case INDEX_op_nand: + case INDEX_op_nor: case INDEX_op_or: case INDEX_op_orc: case INDEX_op_xor: @@ -1084,8 +1085,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: - case INDEX_op_nor_i32: - case INDEX_op_nor_i64: case INDEX_op_div_i32: case INDEX_op_div_i64: case INDEX_op_rem_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index a0f4c58be8..dec51692f0 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -681,7 +681,7 @@ static const TCGOutOpBinary outop_nand = { static void tgen_nor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_op_rrr(s, glue(INDEX_op_nor_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_nor, a0, a1, a2); } static const TCGOutOpBinary outop_nor = { From a3b37bc6faafe5932739c997b2b34a8f6dd57bfd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 2 Jan 2025 13:25:15 -0800 Subject: [PATCH 0387/2760] tcg/arm: Fix constraints for sub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 7536b82d288 we lost the rI constraint that allowed the use of RSB to perform reg = imm - reg. At the same time, drop support for reg = reg - imm, which is now transformed generically to addition, and need not be handled by the backend. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/arm/tcg-target-con-set.h | 1 + tcg/arm/tcg-target.c.inc | 11 ++++------- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/tcg/arm/tcg-target-con-set.h b/tcg/arm/tcg-target-con-set.h index 229ae258ac..f46a8444fb 100644 --- a/tcg/arm/tcg-target-con-set.h +++ b/tcg/arm/tcg-target-con-set.h @@ -30,6 +30,7 @@ C_O1_I2(r, r, rI) C_O1_I2(r, r, rIK) C_O1_I2(r, r, rIN) C_O1_I2(r, r, ri) +C_O1_I2(r, rI, r) C_O1_I2(r, rZ, rZ) C_O1_I2(w, 0, w) C_O1_I2(w, w, w) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 8e9edeb7c6..47c09ff2b1 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1984,12 +1984,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_sub_i32: if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi32(s, COND_AL, args[0], args[1] - args[2]); - } else { - tcg_out_dat_rI(s, COND_AL, ARITH_RSB, - args[0], args[2], args[1], 1); - } + tcg_out_dat_imm(s, COND_AL, ARITH_RSB, + args[0], args[2], encode_imm_nofail(args[1])); } else { tcg_out_dat_rIN(s, COND_AL, ARITH_SUB, ARITH_ADD, args[0], args[1], args[2], const_args[2]); @@ -2234,10 +2230,11 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i32: return C_O0_I2(r, r); - case INDEX_op_sub_i32: case INDEX_op_setcond_i32: case INDEX_op_negsetcond_i32: return C_O1_I2(r, r, rIN); + case INDEX_op_sub_i32: + return C_O1_I2(r, rI, r); case INDEX_op_clz_i32: case INDEX_op_ctz_i32: From 3f057e24006fbc5eaf42278ba9368d383a1c7bed Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 21:57:43 -0800 Subject: [PATCH 0388/2760] tcg: Convert sub to TCGOutOpSubtract Create a special subclass for sub, because two backends can support "subtract from immediate". Drop all backend support for an immediate as the second operand, as we transform sub to add during optimize. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 24 +++++++-------- tcg/arm/tcg-target.c.inc | 29 +++++++++++------- tcg/i386/tcg-target.c.inc | 23 +++++++------- tcg/loongarch64/tcg-target.c.inc | 32 +++++++++----------- tcg/mips/tcg-target-con-set.h | 1 - tcg/mips/tcg-target.c.inc | 31 ++++++++----------- tcg/ppc/tcg-target-con-set.h | 3 +- tcg/ppc/tcg-target.c.inc | 52 +++++++++++--------------------- tcg/riscv/tcg-target-con-set.h | 1 - tcg/riscv/tcg-target-con-str.h | 1 - tcg/riscv/tcg-target.c.inc | 45 +++++++++------------------ tcg/s390x/tcg-target.c.inc | 41 +++++++++++-------------- tcg/sparc64/tcg-target.c.inc | 16 +++++++--- tcg/tcg.c | 30 ++++++++++++++++-- tcg/tci/tcg-target.c.inc | 14 +++++++-- 15 files changed, 169 insertions(+), 174 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 30cad937b7..dfe67c1261 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2205,6 +2205,17 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3502, SUB, type, a0, a1, a2); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2290,15 +2301,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: - if (c2) { - tgen_addi(s, ext, a0, a1, -a2); - } else { - tcg_out_insn(s, 3502, SUB, ext, a0, a1, a2); - } - break; - case INDEX_op_neg_i64: case INDEX_op_neg_i32: tcg_out_insn(s, 3502, SUB, ext, a0, TCG_REG_XZR, a1); @@ -3014,10 +3016,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: - return C_O1_I2(r, r, rA); - case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: case INDEX_op_negsetcond_i32: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 47c09ff2b1..13b78f0ada 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1915,6 +1915,24 @@ static const TCGOutOpBinary outop_orc = { .base.static_constraint = C_NotImplemented, }; +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_SUB, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_subfi(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_RSB, a0, a2, encode_imm_nofail(a1)); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, rI, r), + .out_rrr = tgen_sub, + .out_rir = tgen_subfi, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1982,15 +2000,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_dat_rIK(s, tcg_cond_to_arm_cond[c], ARITH_MOV, ARITH_MVN, args[0], 0, args[3], const_args[3]); break; - case INDEX_op_sub_i32: - if (const_args[1]) { - tcg_out_dat_imm(s, COND_AL, ARITH_RSB, - args[0], args[2], encode_imm_nofail(args[1])); - } else { - tcg_out_dat_rIN(s, COND_AL, ARITH_SUB, ARITH_ADD, - args[0], args[1], args[2], const_args[2]); - } - break; case INDEX_op_add2_i32: a0 = args[0], a1 = args[1], a2 = args[2]; a3 = args[3], a4 = args[4], a5 = args[5]; @@ -2233,8 +2242,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i32: case INDEX_op_negsetcond_i32: return C_O1_I2(r, r, rIN); - case INDEX_op_sub_i32: - return C_O1_I2(r, rI, r); case INDEX_op_clz_i32: case INDEX_op_ctz_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 9185f6879c..104f1b010a 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2669,6 +2669,18 @@ static const TCGOutOpBinary outop_orc = { .base.static_constraint = C_NotImplemented, }; +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_SUB + rexw, a0, a2); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, 0, r), + .out_rrr = tgen_sub, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2770,15 +2782,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(sub): - c = ARITH_SUB; - if (const_a2) { - tgen_arithi(s, c + rexw, a0, a2, 0); - } else { - tgen_arithr(s, c + rexw, a0, a2); - } - break; - OP_32_64(mul): if (const_a2) { int32_t val; @@ -3689,8 +3692,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: return C_O1_I2(r, 0, re); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index dc4ed0e8b0..f01b19463b 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1376,6 +1376,21 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sub_w(s, a0, a1, a2); + } else { + tcg_out_opc_sub_d(s, a0, a1, a2); + } +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1596,21 +1611,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_sub_i32: - if (c2) { - tcg_out_addi(s, TCG_TYPE_I32, a0, a1, -a2); - } else { - tcg_out_opc_sub_w(s, a0, a1, a2); - } - break; - case INDEX_op_sub_i64: - if (c2) { - tcg_out_addi(s, TCG_TYPE_I64, a0, a1, -a2); - } else { - tcg_out_opc_sub_d(s, a0, a1, a2); - } - break; - case INDEX_op_neg_i32: tcg_out_opc_sub_w(s, a0, TCG_REG_ZERO, a1); break; @@ -2324,10 +2324,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) /* Must deposit into the same register as input */ return C_O1_I2(r, 0, rz); - case INDEX_op_sub_i32: case INDEX_op_setcond_i32: return C_O1_I2(r, rz, ri); - case INDEX_op_sub_i64: case INDEX_op_setcond_i64: return C_O1_I2(r, rz, rJ); diff --git a/tcg/mips/tcg-target-con-set.h b/tcg/mips/tcg-target-con-set.h index 06ab04cc4d..248bc95d9b 100644 --- a/tcg/mips/tcg-target-con-set.h +++ b/tcg/mips/tcg-target-con-set.h @@ -24,7 +24,6 @@ C_O1_I2(r, r, rI) C_O1_I2(r, r, rIK) C_O1_I2(r, r, rJ) C_O1_I2(r, r, rzW) -C_O1_I2(r, rz, rN) C_O1_I2(r, rz, rz) C_O1_I4(r, rz, rz, rz, 0) C_O1_I4(r, rz, rz, rz, rz) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index bfe329b3ef..15c5661fb8 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1753,6 +1753,18 @@ static const TCGOutOpBinary outop_orc = { .base.static_constraint = C_NotImplemented, }; +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_SUBU : OPC_DSUBU; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1844,22 +1856,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - do_binaryv: - tcg_out_opc_reg(s, i1, a0, a1, a2); - break; - - case INDEX_op_sub_i32: - i1 = OPC_SUBU, i2 = OPC_ADDIU; - goto do_subtract; - case INDEX_op_sub_i64: - i1 = OPC_DSUBU, i2 = OPC_DADDIU; - do_subtract: - if (c2) { - tcg_out_opc_imm(s, i2, a0, a1, -a2); - break; - } - goto do_binaryv; - case INDEX_op_mul_i32: if (use_mips32_instructions) { tcg_out_opc_reg(s, OPC_MUL, a0, a1, a2); @@ -2234,9 +2230,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: - return C_O1_I2(r, rz, rN); case INDEX_op_mul_i32: case INDEX_op_mulsh_i32: case INDEX_op_muluh_i32: diff --git a/tcg/ppc/tcg-target-con-set.h b/tcg/ppc/tcg-target-con-set.h index 453abde6c1..77a1038d51 100644 --- a/tcg/ppc/tcg-target-con-set.h +++ b/tcg/ppc/tcg-target-con-set.h @@ -22,8 +22,7 @@ C_O1_I1(v, r) C_O1_I1(v, v) C_O1_I1(v, vr) C_O1_I2(r, 0, rZ) -C_O1_I2(r, rI, ri) -C_O1_I2(r, rI, rT) +C_O1_I2(r, rI, r) C_O1_I2(r, r, r) C_O1_I2(r, r, ri) C_O1_I2(r, r, rC) diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index c3366e4316..bfbfdc2dfa 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3016,6 +3016,24 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, SUBF | TAB(a0, a2, a1)); +} + +static void tgen_subfi(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, rI, r), + .out_rrr = tgen_sub, + .out_rir = tgen_subfi, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3104,21 +3122,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); break; - case INDEX_op_sub_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi(s, TCG_TYPE_I32, a0, a1 - a2); - } else { - tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); - } - } else if (const_args[2]) { - tgen_addi(s, type, a0, a1, (int32_t)-a2); - } else { - tcg_out32(s, SUBF | TAB(a0, a2, a1)); - } - break; - case INDEX_op_clz_i32: tcg_out_cntxz(s, TCG_TYPE_I32, CNTLZW, args[0], args[1], args[2], const_args[2]); @@ -3231,21 +3234,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, NOR | SAB(args[1], args[0], args[1])); break; - case INDEX_op_sub_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi(s, TCG_TYPE_I64, a0, a1 - a2); - } else { - tcg_out32(s, SUBFIC | TAI(a0, a2, a1)); - } - } else if (const_args[2]) { - tgen_addi(s, type, a0, a1, -a2); - } else { - tcg_out32(s, SUBF | TAB(a0, a2, a1)); - } - break; - case INDEX_op_shl_i64: if (const_args[2]) { /* Limit immediate shift count lest we create an illegal insn. */ @@ -4195,10 +4183,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muluh_i64: return C_O1_I2(r, r, r); - case INDEX_op_sub_i32: - return C_O1_I2(r, rI, ri); - case INDEX_op_sub_i64: - return C_O1_I2(r, rI, rT); case INDEX_op_clz_i32: case INDEX_op_ctz_i32: case INDEX_op_clz_i64: diff --git a/tcg/riscv/tcg-target-con-set.h b/tcg/riscv/tcg-target-con-set.h index 21f8833b3b..f3a6f7a7ed 100644 --- a/tcg/riscv/tcg-target-con-set.h +++ b/tcg/riscv/tcg-target-con-set.h @@ -16,7 +16,6 @@ C_O1_I1(r, r) C_O1_I2(r, r, r) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) -C_O1_I2(r, rz, rN) C_O1_I2(r, rz, rz) C_N1_I2(r, r, rM) C_O1_I4(r, r, rI, rM, rM) diff --git a/tcg/riscv/tcg-target-con-str.h b/tcg/riscv/tcg-target-con-str.h index 1956f75f9a..c04e15ddfa 100644 --- a/tcg/riscv/tcg-target-con-str.h +++ b/tcg/riscv/tcg-target-con-str.h @@ -18,5 +18,4 @@ REGS('v', ALL_VECTOR_REGS) CONST('I', TCG_CT_CONST_S12) CONST('K', TCG_CT_CONST_S5) CONST('L', TCG_CT_CONST_CMP_VI) -CONST('N', TCG_CT_CONST_N12) CONST('M', TCG_CT_CONST_M12) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 887f20d4cb..54da432ab1 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -113,10 +113,9 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) } #define TCG_CT_CONST_S12 0x100 -#define TCG_CT_CONST_N12 0x200 -#define TCG_CT_CONST_M12 0x400 -#define TCG_CT_CONST_S5 0x800 -#define TCG_CT_CONST_CMP_VI 0x1000 +#define TCG_CT_CONST_M12 0x200 +#define TCG_CT_CONST_S5 0x400 +#define TCG_CT_CONST_CMP_VI 0x800 #define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 32) #define ALL_VECTOR_REGS MAKE_64BIT_MASK(32, 32) @@ -400,13 +399,6 @@ static bool tcg_target_const_match(int64_t val, int ct, if ((ct & TCG_CT_CONST_S12) && val >= -0x800 && val <= 0x7ff) { return 1; } - /* - * Sign extended from 12 bits, negated: [-0x7ff, 0x800]. - * Used for subtraction, where a constant must be handled by ADDI. - */ - if ((ct & TCG_CT_CONST_N12) && val >= -0x7ff && val <= 0x800) { - return 1; - } /* * Sign extended from 12 bits, +/- matching: [-0x7ff, 0x7ff]. * Used by addsub2 and movcond, which may need the negative value, @@ -2055,6 +2047,18 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SUBW : OPC_SUB; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2136,21 +2140,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_sub_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_ADDIW, a0, a1, -a2); - } else { - tcg_out_opc_reg(s, OPC_SUBW, a0, a1, a2); - } - break; - case INDEX_op_sub_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_ADDI, a0, a1, -a2); - } else { - tcg_out_opc_reg(s, OPC_SUB, a0, a1, a2); - } - break; - case INDEX_op_not_i32: case INDEX_op_not_i64: tcg_out_opc_imm(s, OPC_XORI, a0, a1, -1); @@ -2713,10 +2702,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: - return C_O1_I2(r, rz, rN); - case INDEX_op_mul_i32: case INDEX_op_mulsh_i32: case INDEX_op_muluh_i32: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 29570d3be1..662984f733 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2331,6 +2331,23 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, SGRK, a0, a1, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, SR, a0, a2); + } else { + tcg_out_insn(s, RRFa, SRK, a0, a1, a2); + } +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2413,17 +2430,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_sub_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tgen_addi(s, type, a0, a1, (int32_t)-a2); - } else if (a0 == a1) { - tcg_out_insn(s, RR, SR, a0, a2); - } else { - tcg_out_insn(s, RRFa, SRK, a0, a1, a2); - } - break; - case INDEX_op_neg_i32: tcg_out_insn(s, RR, LCR, args[0], args[1]); break; @@ -2618,15 +2624,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); break; - case INDEX_op_sub_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tgen_addi(s, type, a0, a1, -a2); - } else { - tcg_out_insn(s, RRFa, SGRK, a0, a1, a2); - } - break; - case INDEX_op_neg_i64: tcg_out_insn(s, RRE, LCGR, args[0], args[1]); break; @@ -3302,10 +3299,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_clz_i64: return C_O1_I2(r, r, rI); - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: - return C_O1_I2(r, r, ri); - case INDEX_op_mul_i32: return (HAVE_FACILITY(MISC_INSN_EXT2) ? C_O1_I2(r, r, ri) diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 1ebff04af4..04b2b3b195 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1374,6 +1374,17 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_SUB); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1446,9 +1457,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_st32_i64: tcg_out_ldst(s, a0, a1, a2, STW); break; - OP_32_64(sub): - c = ARITH_SUB; - goto gen_arith; case INDEX_op_shl_i32: c = SHIFT_SLL; do_shift32: @@ -1660,8 +1668,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_div_i64: case INDEX_op_divu_i32: case INDEX_op_divu_i64: - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: case INDEX_op_shl_i32: case INDEX_op_shl_i64: case INDEX_op_shr_i32: diff --git a/tcg/tcg.c b/tcg/tcg.c index c0178030ce..d7a44ac1b1 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -986,6 +986,14 @@ typedef struct TCGOutOpBinary { TCGReg a0, TCGReg a1, tcg_target_long a2); } TCGOutOpBinary; +typedef struct TCGOutOpSubtract { + TCGOutOp base; + void (*out_rrr)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2); + void (*out_rir)(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2); +} TCGOutOpSubtract; + #include "tcg-target.c.inc" #ifndef CONFIG_TCG_INTERPRETER @@ -1012,6 +1020,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), + OUTOP(INDEX_op_sub_i32, TCGOutOpSubtract, outop_sub), + OUTOP(INDEX_op_sub_i64, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), }; @@ -2231,7 +2241,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: - case INDEX_op_sub_i32: case INDEX_op_neg_i32: case INDEX_op_mul_i32: case INDEX_op_shl_i32: @@ -2301,7 +2310,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st16_i64: case INDEX_op_st32_i64: case INDEX_op_st_i64: - case INDEX_op_sub_i64: case INDEX_op_neg_i64: case INDEX_op_mul_i64: case INDEX_op_shl_i64: @@ -5449,6 +5457,24 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_sub_i32: + case INDEX_op_sub_i64: + { + const TCGOutOpSubtract *out = &outop_sub; + + /* + * Constants should never appear in the second source operand. + * These are folded to add with negative constant. + */ + tcg_debug_assert(!const_args[2]); + if (const_args[1]) { + out->out_rir(s, type, new_args[0], new_args[1], new_args[2]); + } else { + out->out_rrr(s, type, new_args[0], new_args[1], new_args[2]); + } + } + break; + default: if (def->flags & TCG_OPF_VECTOR) { tcg_out_vec_op(s, op->opc, type - TCG_TYPE_V64, diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index dec51692f0..353994e83f 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -91,8 +91,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rem_i64: case INDEX_op_remu_i32: case INDEX_op_remu_i64: - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: case INDEX_op_shl_i32: @@ -711,6 +709,17 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_sub(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, glue(INDEX_op_sub_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpSubtract outop_sub = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sub, +}; + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -764,7 +773,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, opc, args[0], args[1], args[2]); break; - CASE_32_64(sub) CASE_32_64(mul) CASE_32_64(shl) CASE_32_64(shr) From 60f34f55f1a708c071774bd7f837163d6b686867 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 22:06:32 -0800 Subject: [PATCH 0389/2760] tcg: Merge INDEX_op_sub_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 4 ++-- tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 10 +++------- tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 12 insertions(+), 18 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 26d464fa38..96b7f05919 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -265,7 +265,7 @@ Arithmetic - | *t0* = *t1* + *t2* - * - sub_i32/i64 *t0*, *t1*, *t2* + * - sub *t0*, *t1*, *t2* - | *t0* = *t1* - *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index aa9ed393c9..1be9b01caf 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -47,6 +47,7 @@ DEF(nand, 1, 2, 0, TCG_OPF_INT) DEF(nor, 1, 2, 0, TCG_OPF_INT) DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) +DEF(sub, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) DEF(setcond_i32, 1, 2, 1, 0) @@ -62,7 +63,6 @@ DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* arith */ -DEF(sub_i32, 1, 2, 0, 0) DEF(mul_i32, 1, 2, 0, 0) DEF(div_i32, 1, 2, 0, 0) DEF(divu_i32, 1, 2, 0, 0) @@ -116,7 +116,6 @@ DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* arith */ -DEF(sub_i64, 1, 2, 0, 0) DEF(mul_i64, 1, 2, 0, 0) DEF(div_i64, 1, 2, 0, 0) DEF(divu_i64, 1, 2, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index e4c319fe45..718809ab8d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -427,7 +427,7 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) case INDEX_op_add: return x + y; - CASE_OP_32_64(sub): + case INDEX_op_sub: return x - y; CASE_OP_32_64(mul): @@ -3066,7 +3066,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(sextract): done = fold_sextract(&ctx, op); break; - CASE_OP_32_64(sub): + case INDEX_op_sub: done = fold_sub(&ctx, op); break; case INDEX_op_sub_vec: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 228aa8f088..15faf4dc57 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -377,7 +377,7 @@ void tcg_gen_addi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_sub_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - tcg_gen_op3_i32(INDEX_op_sub_i32, ret, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_sub, ret, arg1, arg2); } void tcg_gen_subfi_i32(TCGv_i32 ret, int32_t arg1, TCGv_i32 arg2) @@ -1565,7 +1565,7 @@ void tcg_gen_add_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_sub_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op3_i64(INDEX_op_sub_i64, ret, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_sub, ret, arg1, arg2); } else { tcg_gen_sub2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2)); diff --git a/tcg/tcg.c b/tcg/tcg.c index d7a44ac1b1..b31e9798c5 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1020,8 +1020,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), - OUTOP(INDEX_op_sub_i32, TCGOutOpSubtract, outop_sub), - OUTOP(INDEX_op_sub_i64, TCGOutOpSubtract, outop_sub), + OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), }; @@ -4010,10 +4009,8 @@ liveness_pass_1(TCGContext *s) opc_new = INDEX_op_add; goto do_addsub2; case INDEX_op_sub2_i32: - opc_new = INDEX_op_sub_i32; - goto do_addsub2; case INDEX_op_sub2_i64: - opc_new = INDEX_op_sub_i64; + opc_new = INDEX_op_sub; do_addsub2: nb_iargs = 4; nb_oargs = 2; @@ -5457,8 +5454,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: + case INDEX_op_sub: { const TCGOutOpSubtract *out = &outop_sub; diff --git a/tcg/tci.c b/tcg/tci.c index ff129266c2..508d1405cd 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -527,7 +527,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] + regs[r2]; break; - CASE_32_64(sub) + case INDEX_op_sub: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] - regs[r2]; break; @@ -1080,9 +1080,8 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_nor: case INDEX_op_or: case INDEX_op_orc: + case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_sub_i32: - case INDEX_op_sub_i64: case INDEX_op_mul_i32: case INDEX_op_mul_i64: case INDEX_op_div_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 353994e83f..67a46c6321 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -712,7 +712,7 @@ static const TCGOutOpBinary outop_orc = { static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_op_rrr(s, glue(INDEX_op_sub_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_sub, a0, a1, a2); } static const TCGOutOpSubtract outop_sub = { From e126a91c38f85750d84cabf27f44c055e17ef6df Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 22:37:07 -0800 Subject: [PATCH 0390/2760] tcg: Convert neg to TCGOutOpUnary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 18 +++++++++++------- tcg/arm/tcg-target.c.inc | 14 ++++++++++---- tcg/i386/tcg-target.c.inc | 16 +++++++++++----- tcg/loongarch64/tcg-target.c.inc | 19 ++++++++++--------- tcg/mips/tcg-target.c.inc | 18 ++++++++++-------- tcg/ppc/tcg-target.c.inc | 17 ++++++++++------- tcg/riscv/tcg-target.c.inc | 19 ++++++++++--------- tcg/s390x/tcg-target.c.inc | 22 ++++++++++++++-------- tcg/sparc64/tcg-target.c.inc | 15 ++++++++++----- tcg/tcg.c | 21 +++++++++++++++++++-- tcg/tci/tcg-target.c.inc | 13 ++++++++++--- 11 files changed, 125 insertions(+), 67 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index dfe67c1261..cf7a3f2632 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2235,6 +2235,17 @@ static const TCGOutOpBinary outop_xor = { }; +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_sub(s, type, a0, TCG_REG_XZR, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2301,11 +2312,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_neg_i64: - case INDEX_op_neg_i32: - tcg_out_insn(s, 3502, SUB, ext, a0, TCG_REG_XZR, a1); - break; - case INDEX_op_not_i64: case INDEX_op_not_i32: tcg_out_insn(s, 3510, ORN, ext, a0, TCG_REG_XZR, a1); @@ -2990,8 +2996,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: case INDEX_op_not_i32: case INDEX_op_not_i64: case INDEX_op_bswap16_i32: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 13b78f0ada..5ea4488606 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1951,6 +1951,16 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_subfi(s, type, a0, 0, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2040,9 +2050,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } tcg_out_mov_reg(s, COND_AL, args[0], a0); break; - case INDEX_op_neg_i32: - tcg_out_dat_imm(s, COND_AL, ARITH_RSB, args[0], args[1], 0); - break; case INDEX_op_not_i32: tcg_out_dat_reg(s, COND_AL, ARITH_MVN, args[0], 0, args[1], SHIFT_IMM_LSL(0)); @@ -2226,7 +2233,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_neg_i32: case INDEX_op_not_i32: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 104f1b010a..082aa982fb 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2701,6 +2701,17 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NEG, a0); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, 0), + .out_rr = tgen_neg, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2900,9 +2911,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(neg): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NEG, a0); - break; OP_32_64(not): tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, a0); break; @@ -3719,8 +3727,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_bswap32_i32: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: case INDEX_op_not_i32: case INDEX_op_not_i64: case INDEX_op_extrh_i64_i32: diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index f01b19463b..31ec7262e0 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1409,6 +1409,16 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_sub(s, type, a0, TCG_REG_ZERO, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1611,13 +1621,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_neg_i32: - tcg_out_opc_sub_w(s, a0, TCG_REG_ZERO, a1); - break; - case INDEX_op_neg_i64: - tcg_out_opc_sub_d(s, a0, TCG_REG_ZERO, a1); - break; - case INDEX_op_mul_i32: tcg_out_opc_mul_w(s, a0, a1, a2); break; @@ -2272,8 +2275,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ext_i32_i64: - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: case INDEX_op_not_i32: case INDEX_op_not_i64: case INDEX_op_extract_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 15c5661fb8..0fda255a7b 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1783,6 +1783,16 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_sub(s, type, a0, TCG_REG_ZERO, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1975,12 +1985,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0); break; - case INDEX_op_neg_i32: - i1 = OPC_SUBU; - goto do_unary; - case INDEX_op_neg_i64: - i1 = OPC_DSUBU; - goto do_unary; case INDEX_op_not_i32: case INDEX_op_not_i64: i1 = OPC_NOR; @@ -2195,7 +2199,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_neg_i32: case INDEX_op_not_i32: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: @@ -2208,7 +2211,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_neg_i64: case INDEX_op_not_i64: case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i64: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index bfbfdc2dfa..da45436a5a 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3052,6 +3052,16 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out32(s, NEG | RT(a0) | RA(a1)); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3224,11 +3234,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_brcond2(s, args, const_args); break; - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: - tcg_out32(s, NEG | RT(args[0]) | RA(args[1])); - break; - case INDEX_op_not_i32: case INDEX_op_not_i64: tcg_out32(s, NOR | SAB(args[1], args[0], args[1])); @@ -4119,7 +4124,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: case INDEX_op_ctpop_i32: - case INDEX_op_neg_i32: case INDEX_op_not_i32: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: @@ -4133,7 +4137,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: case INDEX_op_ctpop_i64: - case INDEX_op_neg_i64: case INDEX_op_not_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 54da432ab1..4e16c44aa5 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2077,6 +2077,16 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_sub(s, type, a0, TCG_REG_ZERO, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2145,13 +2155,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_imm(s, OPC_XORI, a0, a1, -1); break; - case INDEX_op_neg_i32: - tcg_out_opc_reg(s, OPC_SUBW, a0, TCG_REG_ZERO, a1); - break; - case INDEX_op_neg_i64: - tcg_out_opc_reg(s, OPC_SUB, a0, TCG_REG_ZERO, a1); - break; - case INDEX_op_mul_i32: tcg_out_opc_reg(s, OPC_MULW, a0, a1, a2); break; @@ -2660,7 +2663,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: case INDEX_op_not_i32: - case INDEX_op_neg_i32: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i64: case INDEX_op_ld16u_i64: @@ -2669,7 +2671,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: case INDEX_op_not_i64: - case INDEX_op_neg_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 662984f733..08e65834d7 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2373,6 +2373,20 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori_3, }; +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, LCR, a0, a1); + } else { + tcg_out_insn(s, RRE, LCGR, a0, a1); + } +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + # define OP_32_64(x) \ case glue(glue(INDEX_op_,x),_i32): \ @@ -2430,9 +2444,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_neg_i32: - tcg_out_insn(s, RR, LCR, args[0], args[1]); - break; case INDEX_op_not_i32: tcg_out_insn(s, RRFa, NORK, args[0], args[1], args[1]); break; @@ -2624,9 +2635,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); break; - case INDEX_op_neg_i64: - tcg_out_insn(s, RRE, LCGR, args[0], args[1]); - break; case INDEX_op_not_i64: tcg_out_insn(s, RRFa, NOGRK, args[0], args[1], args[1]); break; @@ -3323,8 +3331,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_bswap32_i32: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: case INDEX_op_not_i32: case INDEX_op_not_i64: case INDEX_op_ext_i32_i64: diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 04b2b3b195..a3926ea1c3 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1403,6 +1403,16 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_sub(s, type, a0, TCG_REG_G0, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1473,9 +1483,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, c = ARITH_UMUL; goto gen_arith; - OP_32_64(neg): - c = ARITH_SUB; - goto gen_arith1; OP_32_64(not): c = ARITH_ORN; goto gen_arith1; @@ -1639,8 +1646,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: case INDEX_op_not_i32: case INDEX_op_not_i64: case INDEX_op_ext_i32_i64: diff --git a/tcg/tcg.c b/tcg/tcg.c index b31e9798c5..b5de69e4a9 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -986,6 +986,11 @@ typedef struct TCGOutOpBinary { TCGReg a0, TCGReg a1, tcg_target_long a2); } TCGOutOpBinary; +typedef struct TCGOutOpUnary { + TCGOutOp base; + void (*out_rr)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1); +} TCGOutOpUnary; + typedef struct TCGOutOpSubtract { TCGOutOp base; void (*out_rrr)(TCGContext *s, TCGType type, @@ -1017,6 +1022,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), + OUTOP(INDEX_op_neg_i32, TCGOutOpUnary, outop_neg), + OUTOP(INDEX_op_neg_i64, TCGOutOpUnary, outop_neg), OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), @@ -2240,7 +2247,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: - case INDEX_op_neg_i32: case INDEX_op_mul_i32: case INDEX_op_shl_i32: case INDEX_op_shr_i32: @@ -2309,7 +2315,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st16_i64: case INDEX_op_st32_i64: case INDEX_op_st_i64: - case INDEX_op_neg_i64: case INDEX_op_mul_i64: case INDEX_op_shl_i64: case INDEX_op_shr_i64: @@ -5471,6 +5476,18 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_neg_i32: + case INDEX_op_neg_i64: + { + const TCGOutOpUnary *out = + container_of(all_outop[op->opc], TCGOutOpUnary, base); + + /* Constants should have been folded. */ + tcg_debug_assert(!const_args[1]); + out->out_rr(s, type, new_args[0], new_args[1]); + } + break; + default: if (def->flags & TCG_OPF_VECTOR) { tcg_out_vec_op(s, op->opc, type - TCG_TYPE_V64, diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 67a46c6321..200b256e73 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -57,8 +57,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_not_i32: case INDEX_op_not_i64: - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_bswap16_i32: @@ -731,6 +729,16 @@ static const TCGOutOpBinary outop_xor = { .out_rrr = tgen_xor, }; +static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_op_rr(s, glue(INDEX_op_neg_i,TCG_TARGET_REG_BITS), a0, a1); +} + +static const TCGOutOpUnary outop_neg = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_neg, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -804,7 +812,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_rl(s, opc, TCG_REG_TMP, arg_label(args[3])); break; - CASE_32_64(neg) /* Optional (TCG_TARGET_HAS_neg_*). */ CASE_32_64(not) /* Optional (TCG_TARGET_HAS_not_*). */ CASE_32_64(ctpop) /* Optional (TCG_TARGET_HAS_ctpop_*). */ case INDEX_op_bswap32_i32: /* Optional (TCG_TARGET_HAS_bswap32_i32). */ From 6971358747d8998a5770d1bf997495d3061d6c6a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 22:48:57 -0800 Subject: [PATCH 0391/2760] tcg: Merge INDEX_op_neg_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 30 ++++++------------------------ tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 6 ++---- tcg/tci.c | 11 +++++------ tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 18 insertions(+), 40 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 96b7f05919..fb51691538 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -269,7 +269,7 @@ Arithmetic - | *t0* = *t1* - *t2* - * - neg_i32/i64 *t0*, *t1* + * - neg *t0*, *t1* - | *t0* = -*t1* (two's complement) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 1be9b01caf..13b7650cec 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -44,6 +44,7 @@ DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(nand, 1, 2, 0, TCG_OPF_INT) +DEF(neg, 1, 1, 0, TCG_OPF_INT) DEF(nor, 1, 2, 0, TCG_OPF_INT) DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) @@ -95,7 +96,6 @@ DEF(setcond2_i32, 1, 4, 1, 0) DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) DEF(not_i32, 1, 1, 0, 0) -DEF(neg_i32, 1, 1, 0, 0) DEF(clz_i32, 1, 2, 0, 0) DEF(ctz_i32, 1, 2, 0, 0) DEF(ctpop_i32, 1, 1, 0, 0) @@ -145,7 +145,6 @@ DEF(bswap16_i64, 1, 1, 1, 0) DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) DEF(not_i64, 1, 1, 0, 0) -DEF(neg_i64, 1, 1, 0, 0) DEF(clz_i64, 1, 2, 0, 0) DEF(ctz_i64, 1, 2, 0, 0) DEF(ctpop_i64, 1, 1, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index 718809ab8d..eb360e2b63 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -478,7 +478,7 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) CASE_OP_32_64_VEC(not): return ~x; - CASE_OP_32_64(neg): + case INDEX_op_neg: return -x; case INDEX_op_andc: @@ -2314,25 +2314,12 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) break; } if (convert) { - TCGOpcode neg_opc; - if (!inv && !neg) { return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); } - switch (ctx->type) { - case TCG_TYPE_I32: - neg_opc = INDEX_op_neg_i32; - break; - case TCG_TYPE_I64: - neg_opc = INDEX_op_neg_i64; - break; - default: - g_assert_not_reached(); - } - if (!inv) { - op->opc = neg_opc; + op->opc = INDEX_op_neg; } else if (neg) { op->opc = INDEX_op_add; op->args[2] = arg_new_constant(ctx, -1); @@ -2348,7 +2335,7 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) { - TCGOpcode neg_opc, shr_opc; + TCGOpcode shr_opc; TCGOpcode uext_opc = 0, sext_opc = 0; TCGCond cond = op->args[3]; TCGArg ret, src1, src2; @@ -2371,7 +2358,6 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) switch (ctx->type) { case TCG_TYPE_I32: shr_opc = INDEX_op_shr_i32; - neg_opc = INDEX_op_neg_i32; if (TCG_TARGET_extract_valid(TCG_TYPE_I32, sh, 1)) { uext_opc = INDEX_op_extract_i32; } @@ -2381,7 +2367,6 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) break; case TCG_TYPE_I64: shr_opc = INDEX_op_shr_i64; - neg_opc = INDEX_op_neg_i64; if (TCG_TARGET_extract_valid(TCG_TYPE_I64, sh, 1)) { uext_opc = INDEX_op_extract_i64; } @@ -2432,7 +2417,7 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) op2->args[1] = ret; op2->args[2] = arg_new_constant(ctx, 1); } else if (neg) { - op2 = opt_insert_after(ctx, op, neg_opc, 2); + op2 = opt_insert_after(ctx, op, INDEX_op_neg, 2); op2->args[0] = ret; op2->args[1] = ret; } @@ -2644,11 +2629,8 @@ static bool fold_sub_to_neg(OptContext *ctx, TCGOp *op) switch (ctx->type) { case TCG_TYPE_I32: - neg_op = INDEX_op_neg_i32; - have_neg = true; - break; case TCG_TYPE_I64: - neg_op = INDEX_op_neg_i64; + neg_op = INDEX_op_neg; have_neg = true; break; case TCG_TYPE_V64: @@ -2998,7 +2980,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_nand_vec: done = fold_nand(&ctx, op); break; - CASE_OP_32_64(neg): + case INDEX_op_neg: done = fold_neg(&ctx, op); break; case INDEX_op_nor: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 15faf4dc57..cb2eb9ae52 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -396,7 +396,7 @@ void tcg_gen_subi_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_neg_i32(TCGv_i32 ret, TCGv_i32 arg) { - tcg_gen_op2_i32(INDEX_op_neg_i32, ret, arg); + tcg_gen_op2_i32(INDEX_op_neg, ret, arg); } void tcg_gen_and_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) @@ -1691,7 +1691,7 @@ void tcg_gen_subi_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) void tcg_gen_neg_i64(TCGv_i64 ret, TCGv_i64 arg) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op2_i64(INDEX_op_neg_i64, ret, arg); + tcg_gen_op2_i64(INDEX_op_neg, ret, arg); } else { TCGv_i32 zero = tcg_constant_i32(0); tcg_gen_sub2_i32(TCGV_LOW(ret), TCGV_HIGH(ret), diff --git a/tcg/tcg.c b/tcg/tcg.c index b5de69e4a9..92d185558f 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1022,8 +1022,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), - OUTOP(INDEX_op_neg_i32, TCGOutOpUnary, outop_neg), - OUTOP(INDEX_op_neg_i64, TCGOutOpUnary, outop_neg), + OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), @@ -5476,8 +5475,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: + case INDEX_op_neg: { const TCGOutOpUnary *out = container_of(all_outop[op->opc], TCGOutOpUnary, base); diff --git a/tcg/tci.c b/tcg/tci.c index 508d1405cd..c736691e9f 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -567,6 +567,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ~(regs[r1] | regs[r2]); break; + case INDEX_op_neg: + tci_args_rr(insn, &r0, &r1); + regs[r0] = -regs[r1]; + break; /* Arithmetic operations (32 bit). */ @@ -697,10 +701,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = ~regs[r1]; break; #endif - CASE_32_64(neg) - tci_args_rr(insn, &r0, &r1); - regs[r0] = -regs[r1]; - break; #if TCG_TARGET_REG_BITS == 64 /* Load/store operations (64 bit). */ @@ -1054,6 +1054,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) break; case INDEX_op_mov: + case INDEX_op_neg: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_bswap16_i32: @@ -1063,8 +1064,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_bswap64_i64: case INDEX_op_not_i32: case INDEX_op_not_i64: - case INDEX_op_neg_i32: - case INDEX_op_neg_i64: case INDEX_op_ctpop_i32: case INDEX_op_ctpop_i64: tci_args_rr(insn, &r0, &r1); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 200b256e73..c42f9dff11 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -731,7 +731,7 @@ static const TCGOutOpBinary outop_xor = { static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { - tcg_out_op_rr(s, glue(INDEX_op_neg_i,TCG_TARGET_REG_BITS), a0, a1); + tcg_out_op_rr(s, INDEX_op_neg, a0, a1); } static const TCGOutOpUnary outop_neg = { From 592982bf04cfa92dc4c992056c7330ac46f4882f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 23:37:54 -0800 Subject: [PATCH 0392/2760] tcg: Convert not to TCGOutOpUnary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 17 ++++++++++------- tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 15 ++++++++++----- tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 17 +++++++++++------ tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 17 ++++++++++------- tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 20 ++++++++++---------- tcg/optimize.c | 4 ++-- tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 17 ++++++++++------- tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 17 ++++++++++------- tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 25 ++++++++++++++++--------- tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 20 ++++++++++---------- tcg/tcg-has.h | 1 - tcg/tcg-op.c | 10 ++++++---- tcg/tcg.c | 8 ++++---- tcg/tci.c | 2 -- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target.c.inc | 13 ++++++++++--- 25 files changed, 119 insertions(+), 103 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 240fcac2cc..7f18727686 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -17,7 +17,6 @@ #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 @@ -38,7 +37,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index cf7a3f2632..97b444bc17 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2245,6 +2245,16 @@ static const TCGOutOpUnary outop_neg = { .out_rr = tgen_neg, }; +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_orc(s, type, a0, TCG_REG_XZR, a1); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2312,11 +2322,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_not_i64: - case INDEX_op_not_i32: - tcg_out_insn(s, 3510, ORN, ext, a0, TCG_REG_XZR, a1); - break; - case INDEX_op_mul_i64: case INDEX_op_mul_i32: tcg_out_insn(s, 3509, MADD, ext, a0, a1, a2, TCG_REG_XZR); @@ -2996,8 +3001,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_not_i32: - case INDEX_op_not_i64: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: case INDEX_op_bswap16_i64: diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index e80711ee40..e766c6d628 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -26,7 +26,6 @@ extern bool use_neon_instructions; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 use_armv7_instructions diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 5ea4488606..2477b1c4ab 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1961,6 +1961,16 @@ static const TCGOutOpUnary outop_neg = { .out_rr = tgen_neg, }; +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MVN, a0, 0, a1, SHIFT_IMM_LSL(0)); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2050,10 +2060,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } tcg_out_mov_reg(s, COND_AL, args[0], a0); break; - case INDEX_op_not_i32: - tcg_out_dat_reg(s, COND_AL, - ARITH_MVN, args[0], 0, args[1], SHIFT_IMM_LSL(0)); - break; case INDEX_op_mul_i32: tcg_out_mul32(s, COND_AL, args[0], args[1], args[2]); break; @@ -2233,7 +2239,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_not_i32: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: case INDEX_op_extract_i32: diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index b27f853dcd..3d36fe58f2 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -30,7 +30,6 @@ #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 have_popcnt @@ -51,7 +50,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 have_popcnt diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 082aa982fb..24fef09c9e 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2712,6 +2712,17 @@ static const TCGOutOpUnary outop_neg = { .out_rr = tgen_neg, }; +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, a0); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, 0), + .out_rr = tgen_not, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2911,10 +2922,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(not): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, a0); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, -1, a1, a2, TCG_TYPE_I32); break; @@ -3727,8 +3734,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_bswap32_i32: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: - case INDEX_op_not_i32: - case INDEX_op_not_i64: case INDEX_op_extrh_i64_i32: return C_O1_I1(r, 0); diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 7860432489..ffacb41e80 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -24,7 +24,6 @@ #define TCG_TARGET_HAS_mulsh_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 0 @@ -41,7 +40,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 0 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 31ec7262e0..c24882b6ba 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1419,6 +1419,16 @@ static const TCGOutOpUnary outop_neg = { .out_rr = tgen_neg, }; +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_nor(s, type, a0, a1, TCG_REG_ZERO); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1454,11 +1464,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_srai_d(s, a0, a1, 32); break; - case INDEX_op_not_i32: - case INDEX_op_not_i64: - tcg_out_opc_nor(s, a0, a1, TCG_REG_ZERO); - break; - case INDEX_op_extract_i32: if (a2 == 0 && args[3] <= 12) { tcg_out_opc_andi(s, a0, a1, (1 << args[3]) - 1); @@ -2275,8 +2280,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ext_i32_i64: - case INDEX_op_not_i32: - case INDEX_op_not_i64: case INDEX_op_extract_i32: case INDEX_op_extract_i64: case INDEX_op_sextract_i32: diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 987f83f761..9d8e0fb8df 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -41,7 +41,6 @@ extern bool use_mips32r2_instructions; /* optional instructions */ #define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 -#define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muluh_i32 1 @@ -56,7 +55,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 (!use_mips32r6_instructions) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 0fda255a7b..4942855189 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1793,6 +1793,16 @@ static const TCGOutOpUnary outop_neg = { .out_rr = tgen_neg, }; +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_nor(s, type, a0, TCG_REG_ZERO, a1); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1985,14 +1995,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0); break; - case INDEX_op_not_i32: - case INDEX_op_not_i64: - i1 = OPC_NOR; - goto do_unary; - do_unary: - tcg_out_opc_reg(s, i1, a0, TCG_REG_ZERO, a1); - break; - case INDEX_op_bswap16_i32: case INDEX_op_bswap16_i64: tcg_out_bswap16(s, a0, a1, a2); @@ -2199,7 +2201,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_not_i32: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: case INDEX_op_extract_i32: @@ -2211,7 +2212,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_not_i64: case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: diff --git a/tcg/optimize.c b/tcg/optimize.c index eb360e2b63..b4a675ea37 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1104,11 +1104,11 @@ static bool fold_to_not(OptContext *ctx, TCGOp *op, int idx) switch (ctx->type) { case TCG_TYPE_I32: not_op = INDEX_op_not_i32; - have_not = TCG_TARGET_HAS_not_i32; + have_not = tcg_op_supported(INDEX_op_not_i32, TCG_TYPE_I32, 0); break; case TCG_TYPE_I64: not_op = INDEX_op_not_i64; - have_not = TCG_TARGET_HAS_not_i64; + have_not = tcg_op_supported(INDEX_op_not_i64, TCG_TYPE_I64, 0); break; case TCG_TYPE_V64: case TCG_TYPE_V128: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 6be6d7f994..7ebcb49a19 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -22,7 +22,6 @@ #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 have_isa_3_00 #define TCG_TARGET_HAS_ctpop_i32 have_isa_2_06 @@ -44,7 +43,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 have_isa_3_00 #define TCG_TARGET_HAS_ctpop_i64 have_isa_2_06 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index da45436a5a..38cbe5223b 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3062,6 +3062,16 @@ static const TCGOutOpUnary outop_neg = { .out_rr = tgen_neg, }; +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_nor(s, type, a0, a1, a1); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3234,11 +3244,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_brcond2(s, args, const_args); break; - case INDEX_op_not_i32: - case INDEX_op_not_i64: - tcg_out32(s, NOR | SAB(args[1], args[0], args[1])); - break; - case INDEX_op_shl_i64: if (const_args[2]) { /* Limit immediate shift count lest we create an illegal insn. */ @@ -4124,7 +4129,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: case INDEX_op_ctpop_i32: - case INDEX_op_not_i32: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: case INDEX_op_extract_i32: @@ -4137,7 +4141,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: case INDEX_op_ctpop_i64: - case INDEX_op_not_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_bswap16_i64: diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 0fcf940a8a..e3018717ea 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -24,7 +24,6 @@ #define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_clz_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctz_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctpop_i32 (cpuinfo & CPUINFO_ZBB) @@ -40,7 +39,6 @@ #define TCG_TARGET_HAS_bswap16_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_clz_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctz_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctpop_i64 (cpuinfo & CPUINFO_ZBB) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 4e16c44aa5..5e9e14815d 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2087,6 +2087,16 @@ static const TCGOutOpUnary outop_neg = { .out_rr = tgen_neg, }; +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_xori(s, type, a0, a1, -1); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2150,11 +2160,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_not_i32: - case INDEX_op_not_i64: - tcg_out_opc_imm(s, OPC_XORI, a0, a1, -1); - break; - case INDEX_op_mul_i32: tcg_out_opc_reg(s, OPC_MULW, a0, a1, a2); break; @@ -2662,7 +2667,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_not_i32: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i64: case INDEX_op_ld16u_i64: @@ -2670,7 +2674,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_not_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 374db3cf9d..e5c132cf12 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -33,7 +33,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_not_i32 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_clz_i32 0 #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 1 @@ -53,7 +52,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_not_i64 HAVE_FACILITY(MISC_INSN_EXT3) #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 08e65834d7..98bf3ee19e 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2387,6 +2387,22 @@ static const TCGOutOpUnary outop_neg = { .out_rr = tgen_neg, }; +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_nor(s, type, a0, a1, a1); +} + +static TCGConstraintSetIndex cset_not(TCGType type, unsigned flags) +{ + return HAVE_FACILITY(MISC_INSN_EXT3) ? C_O1_I1(r, r) : C_NotImplemented; +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_not, + .out_rr = tgen_not, +}; + # define OP_32_64(x) \ case glue(glue(INDEX_op_,x),_i32): \ @@ -2444,10 +2460,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_not_i32: - tcg_out_insn(s, RRFa, NORK, args[0], args[1], args[1]); - break; - case INDEX_op_mul_i32: a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; if (const_args[2]) { @@ -2635,9 +2647,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); break; - case INDEX_op_not_i64: - tcg_out_insn(s, RRFa, NOGRK, args[0], args[1], args[1]); - break; case INDEX_op_bswap64_i64: tcg_out_insn(s, RRE, LRVGR, args[0], args[1]); break; @@ -3331,8 +3340,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_bswap32_i32: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: - case INDEX_op_not_i32: - case INDEX_op_not_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extract_i32: diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 35ae536879..df87249df2 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -19,7 +19,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_rot_i32 0 #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 -#define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_clz_i32 0 #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 0 @@ -40,7 +39,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_clz_i64 0 #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index a3926ea1c3..5819dc44fe 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1413,6 +1413,16 @@ static const TCGOutOpUnary outop_neg = { .out_rr = tgen_neg, }; +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tgen_orc(s, type, a0, TCG_REG_G0, a1); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1483,10 +1493,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, c = ARITH_UMUL; goto gen_arith; - OP_32_64(not): - c = ARITH_ORN; - goto gen_arith1; - case INDEX_op_div_i32: tcg_out_div32(s, a0, a1, a2, c2, 0); break; @@ -1600,10 +1606,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_arithc(s, a0, a1, a2, c2, c); break; - gen_arith1: - tcg_out_arithc(s, a0, TCG_REG_G0, a1, const_args[1], c); - break; - case INDEX_op_mb: tcg_out_mb(s, a0); break; @@ -1646,8 +1648,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_not_i32: - case INDEX_op_not_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extract_i64: diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 7de13ef383..a84ed1313a 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -19,7 +19,6 @@ #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_not_i64 0 #define TCG_TARGET_HAS_clz_i64 0 #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index cb2eb9ae52..e0f8ab28b8 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -461,7 +461,8 @@ void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) /* Some cases can be optimized here. */ if (arg2 == 0) { tcg_gen_mov_i32(ret, arg1); - } else if (arg2 == -1 && TCG_TARGET_HAS_not_i32) { + } else if (arg2 == -1 && + tcg_op_supported(INDEX_op_not_i32, TCG_TYPE_I32, 0)) { /* Don't recurse with tcg_gen_not_i32. */ tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg1); } else { @@ -471,7 +472,7 @@ void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_not_i32) { + if (tcg_op_supported(INDEX_op_not_i32, TCG_TYPE_I32, 0)) { tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg); } else { tcg_gen_xori_i32(ret, arg, -1); @@ -1762,7 +1763,8 @@ void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) /* Some cases can be optimized here. */ if (arg2 == 0) { tcg_gen_mov_i64(ret, arg1); - } else if (arg2 == -1 && TCG_TARGET_HAS_not_i64) { + } else if (arg2 == -1 && + tcg_op_supported(INDEX_op_not_i64, TCG_TYPE_I64, 0)) { /* Don't recurse with tcg_gen_not_i64. */ tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg1); } else { @@ -2252,7 +2254,7 @@ void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_not_i32(TCGV_LOW(ret), TCGV_LOW(arg)); tcg_gen_not_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); - } else if (TCG_TARGET_HAS_not_i64) { + } else if (tcg_op_supported(INDEX_op_not_i64, TCG_TYPE_I64, 0)) { tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg); } else { tcg_gen_xori_i64(ret, arg, -1); diff --git a/tcg/tcg.c b/tcg/tcg.c index 92d185558f..acd666d30b 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1024,6 +1024,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), + OUTOP(INDEX_op_not_i32, TCGOutOpUnary, outop_not), + OUTOP(INDEX_op_not_i64, TCGOutOpUnary, outop_not), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), @@ -2287,8 +2289,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap16_i32; case INDEX_op_bswap32_i32: return TCG_TARGET_HAS_bswap32_i32; - case INDEX_op_not_i32: - return TCG_TARGET_HAS_not_i32; case INDEX_op_clz_i32: return TCG_TARGET_HAS_clz_i32; case INDEX_op_ctz_i32: @@ -2350,8 +2350,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap32_i64; case INDEX_op_bswap64_i64: return TCG_TARGET_HAS_bswap64_i64; - case INDEX_op_not_i64: - return TCG_TARGET_HAS_not_i64; case INDEX_op_clz_i64: return TCG_TARGET_HAS_clz_i64; case INDEX_op_ctz_i64: @@ -5476,6 +5474,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_neg: + case INDEX_op_not_i32: + case INDEX_op_not_i64: { const TCGOutOpUnary *out = container_of(all_outop[op->opc], TCGOutOpUnary, base); diff --git a/tcg/tci.c b/tcg/tci.c index c736691e9f..25ad37fcd5 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -695,12 +695,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = bswap32(regs[r1]); break; #endif -#if TCG_TARGET_HAS_not_i32 || TCG_TARGET_HAS_not_i64 CASE_32_64(not) tci_args_rr(insn, &r0, &r1); regs[r0] = ~regs[r1]; break; -#endif #if TCG_TARGET_REG_BITS == 64 /* Load/store operations (64 bit). */ diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 13c9dc3dfa..f147da5c0e 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -15,7 +15,6 @@ #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 1 -#define TCG_TARGET_HAS_not_i32 1 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_muls2_i32 1 @@ -34,7 +33,6 @@ #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 1 -#define TCG_TARGET_HAS_not_i64 1 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_muls2_i64 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index c42f9dff11..d3da498098 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -55,8 +55,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_not_i32: - case INDEX_op_not_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_bswap16_i32: @@ -739,6 +737,16 @@ static const TCGOutOpUnary outop_neg = { .out_rr = tgen_neg, }; +static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_op_rr(s, glue(INDEX_op_not_i,TCG_TARGET_REG_BITS), a0, a1); +} + +static const TCGOutOpUnary outop_not = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_not, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -812,7 +820,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_rl(s, opc, TCG_REG_TMP, arg_label(args[3])); break; - CASE_32_64(not) /* Optional (TCG_TARGET_HAS_not_*). */ CASE_32_64(ctpop) /* Optional (TCG_TARGET_HAS_ctpop_*). */ case INDEX_op_bswap32_i32: /* Optional (TCG_TARGET_HAS_bswap32_i32). */ case INDEX_op_bswap64_i64: /* Optional (TCG_TARGET_HAS_bswap64_i64). */ From 5c62d3779b8b1075782672751165c0e4f716762f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 6 Jan 2025 23:46:47 -0800 Subject: [PATCH 0393/2760] tcg: Merge INDEX_op_not_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 13 ++++++------- tcg/tcg-op.c | 16 ++++++++-------- tcg/tcg.c | 6 ++---- tcg/tci.c | 11 +++++------ tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 24 insertions(+), 29 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index fb51691538..96dddc5fd3 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -315,7 +315,7 @@ Logical - | *t0* = *t1* ^ *t2* - * - not_i32/i64 *t0*, *t1* + * - not *t0*, *t1* - | *t0* = ~\ *t1* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 13b7650cec..d0fcdfd241 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -46,6 +46,7 @@ DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(nand, 1, 2, 0, TCG_OPF_INT) DEF(neg, 1, 1, 0, TCG_OPF_INT) DEF(nor, 1, 2, 0, TCG_OPF_INT) +DEF(not, 1, 1, 0, TCG_OPF_INT) DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) @@ -95,7 +96,6 @@ DEF(setcond2_i32, 1, 4, 1, 0) DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) -DEF(not_i32, 1, 1, 0, 0) DEF(clz_i32, 1, 2, 0, 0) DEF(ctz_i32, 1, 2, 0, 0) DEF(ctpop_i32, 1, 1, 0, 0) @@ -144,7 +144,6 @@ DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(bswap16_i64, 1, 1, 1, 0) DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) -DEF(not_i64, 1, 1, 0, 0) DEF(clz_i64, 1, 2, 0, 0) DEF(ctz_i64, 1, 2, 0, 0) DEF(ctpop_i64, 1, 1, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index b4a675ea37..315ee0a8bc 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -475,7 +475,8 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) case INDEX_op_rotl_i64: return rol64(x, y & 63); - CASE_OP_32_64_VEC(not): + case INDEX_op_not: + case INDEX_op_not_vec: return ~x; case INDEX_op_neg: @@ -1103,12 +1104,9 @@ static bool fold_to_not(OptContext *ctx, TCGOp *op, int idx) switch (ctx->type) { case TCG_TYPE_I32: - not_op = INDEX_op_not_i32; - have_not = tcg_op_supported(INDEX_op_not_i32, TCG_TYPE_I32, 0); - break; case TCG_TYPE_I64: - not_op = INDEX_op_not_i64; - have_not = tcg_op_supported(INDEX_op_not_i64, TCG_TYPE_I64, 0); + not_op = INDEX_op_not; + have_not = tcg_op_supported(INDEX_op_not, ctx->type, 0); break; case TCG_TYPE_V64: case TCG_TYPE_V128: @@ -2987,7 +2985,8 @@ void tcg_optimize(TCGContext *s) case INDEX_op_nor_vec: done = fold_nor(&ctx, op); break; - CASE_OP_32_64_VEC(not): + case INDEX_op_not: + case INDEX_op_not_vec: done = fold_not(&ctx, op); break; case INDEX_op_or: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index e0f8ab28b8..ddc1f465a4 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -462,9 +462,9 @@ void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) if (arg2 == 0) { tcg_gen_mov_i32(ret, arg1); } else if (arg2 == -1 && - tcg_op_supported(INDEX_op_not_i32, TCG_TYPE_I32, 0)) { + tcg_op_supported(INDEX_op_not, TCG_TYPE_I32, 0)) { /* Don't recurse with tcg_gen_not_i32. */ - tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg1); + tcg_gen_op2_i32(INDEX_op_not, ret, arg1); } else { tcg_gen_xor_i32(ret, arg1, tcg_constant_i32(arg2)); } @@ -472,8 +472,8 @@ void tcg_gen_xori_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (tcg_op_supported(INDEX_op_not_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op2_i32(INDEX_op_not_i32, ret, arg); + if (tcg_op_supported(INDEX_op_not, TCG_TYPE_I32, 0)) { + tcg_gen_op2_i32(INDEX_op_not, ret, arg); } else { tcg_gen_xori_i32(ret, arg, -1); } @@ -1764,9 +1764,9 @@ void tcg_gen_xori_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) if (arg2 == 0) { tcg_gen_mov_i64(ret, arg1); } else if (arg2 == -1 && - tcg_op_supported(INDEX_op_not_i64, TCG_TYPE_I64, 0)) { + tcg_op_supported(INDEX_op_not, TCG_TYPE_I64, 0)) { /* Don't recurse with tcg_gen_not_i64. */ - tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg1); + tcg_gen_op2_i64(INDEX_op_not, ret, arg1); } else { tcg_gen_xor_i64(ret, arg1, tcg_constant_i64(arg2)); } @@ -2254,8 +2254,8 @@ void tcg_gen_not_i64(TCGv_i64 ret, TCGv_i64 arg) if (TCG_TARGET_REG_BITS == 32) { tcg_gen_not_i32(TCGV_LOW(ret), TCGV_LOW(arg)); tcg_gen_not_i32(TCGV_HIGH(ret), TCGV_HIGH(arg)); - } else if (tcg_op_supported(INDEX_op_not_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op2_i64(INDEX_op_not_i64, ret, arg); + } else if (tcg_op_supported(INDEX_op_not, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_not, ret, arg); } else { tcg_gen_xori_i64(ret, arg, -1); } diff --git a/tcg/tcg.c b/tcg/tcg.c index acd666d30b..5e43a304c0 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1024,8 +1024,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), - OUTOP(INDEX_op_not_i32, TCGOutOpUnary, outop_not), - OUTOP(INDEX_op_not_i64, TCGOutOpUnary, outop_not), + OUTOP(INDEX_op_not, TCGOutOpUnary, outop_not), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), @@ -5474,8 +5473,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_neg: - case INDEX_op_not_i32: - case INDEX_op_not_i64: + case INDEX_op_not: { const TCGOutOpUnary *out = container_of(all_outop[op->opc], TCGOutOpUnary, base); diff --git a/tcg/tci.c b/tcg/tci.c index 25ad37fcd5..96e3667ab2 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -571,6 +571,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rr(insn, &r0, &r1); regs[r0] = -regs[r1]; break; + case INDEX_op_not: + tci_args_rr(insn, &r0, &r1); + regs[r0] = ~regs[r1]; + break; /* Arithmetic operations (32 bit). */ @@ -695,10 +699,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = bswap32(regs[r1]); break; #endif - CASE_32_64(not) - tci_args_rr(insn, &r0, &r1); - regs[r0] = ~regs[r1]; - break; #if TCG_TARGET_REG_BITS == 64 /* Load/store operations (64 bit). */ @@ -1053,6 +1053,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_mov: case INDEX_op_neg: + case INDEX_op_not: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_bswap16_i32: @@ -1060,8 +1061,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_bswap32_i32: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: - case INDEX_op_not_i32: - case INDEX_op_not_i64: case INDEX_op_ctpop_i32: case INDEX_op_ctpop_i64: tci_args_rr(insn, &r0, &r1); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index d3da498098..a1f9a3a2f0 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -739,7 +739,7 @@ static const TCGOutOpUnary outop_neg = { static void tgen_not(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { - tcg_out_op_rr(s, glue(INDEX_op_not_i,TCG_TARGET_REG_BITS), a0, a1); + tcg_out_op_rr(s, INDEX_op_not, a0, a1); } static const TCGOutOpUnary outop_not = { From 1e4ac0ea5dc86c54e97bbc4db9638f2febdfde8b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 09:15:09 -0800 Subject: [PATCH 0394/2760] tcg: Convert mul to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 18 ++++--- tcg/arm/tcg-target.c.inc | 23 ++++---- tcg/i386/tcg-target.c.inc | 47 +++++++++------- tcg/loongarch64/tcg-target.c.inc | 24 +++++---- tcg/mips/tcg-target.c.inc | 43 +++++++++------ tcg/ppc/tcg-target.c.inc | 42 +++++++-------- tcg/riscv/tcg-target.c.inc | 21 ++++---- tcg/s390x/tcg-target.c.inc | 92 ++++++++++++++++++-------------- tcg/sparc64/tcg-target.c.inc | 28 +++++++--- tcg/tcg.c | 6 ++- tcg/tci/tcg-target.c.inc | 14 +++-- 11 files changed, 210 insertions(+), 148 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 97b444bc17..4513140f58 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2168,6 +2168,17 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3509, MADD, type, a0, a1, a2, TCG_REG_XZR); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mul, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -2322,11 +2333,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_mul_i64: - case INDEX_op_mul_i32: - tcg_out_insn(s, 3509, MADD, ext, a0, a1, a2, TCG_REG_XZR); - break; - case INDEX_op_div_i64: case INDEX_op_div_i32: tcg_out_insn(s, 3508, SDIV, ext, a0, a1, a2); @@ -3029,8 +3035,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rC); - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: case INDEX_op_div_i32: case INDEX_op_div_i64: case INDEX_op_divu_i32: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 2477b1c4ab..93e5c70ae3 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -921,13 +921,6 @@ static void tcg_out_dat_rIN(TCGContext *s, ARMCond cond, ARMInsn opc, } } -static void tcg_out_mul32(TCGContext *s, ARMCond cond, TCGReg rd, - TCGReg rn, TCGReg rm) -{ - /* mul */ - tcg_out32(s, (cond << 28) | 0x90 | (rd << 16) | (rm << 8) | rn); -} - static void tcg_out_umull32(TCGContext *s, ARMCond cond, TCGReg rd0, TCGReg rd1, TCGReg rn, TCGReg rm) { @@ -1885,6 +1878,18 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + /* mul */ + tcg_out32(s, (COND_AL << 28) | 0x90 | (a0 << 16) | (a1 << 8) | a2); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mul, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -2060,9 +2065,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } tcg_out_mov_reg(s, COND_AL, args[0], a0); break; - case INDEX_op_mul_i32: - tcg_out_mul32(s, COND_AL, args[0], args[1], args[2]); - break; case INDEX_op_mulu2_i32: tcg_out_umull32(s, COND_AL, args[0], args[1], args[2], args[3]); break; @@ -2258,7 +2260,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ctz_i32: return C_O1_I2(r, r, rIK); - case INDEX_op_mul_i32: case INDEX_op_div_i32: case INDEX_op_divu_i32: return C_O1_I2(r, r, r); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 24fef09c9e..4abe89d06e 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2637,6 +2637,33 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_IMUL_GvEv + rexw, a0, a2); +} + +static void tgen_muli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + if (a2 == (int8_t)a2) { + tcg_out_modrm(s, OPC_IMUL_GvEvIb + rexw, a0, a0); + tcg_out8(s, a2); + } else { + tcg_out_modrm(s, OPC_IMUL_GvEvIz + rexw, a0, a0); + tcg_out32(s, a2); + } +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_mul, + .out_rri = tgen_muli, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -2804,22 +2831,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(mul): - if (const_a2) { - int32_t val; - val = a2; - if (val == (int8_t)val) { - tcg_out_modrm(s, OPC_IMUL_GvEvIb + rexw, a0, a0); - tcg_out8(s, val); - } else { - tcg_out_modrm(s, OPC_IMUL_GvEvIz + rexw, a0, a0); - tcg_out32(s, val); - } - } else { - tcg_out_modrm(s, OPC_IMUL_GvEv + rexw, a0, a2); - } - break; - OP_32_64(div2): tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IDIV, args[4]); break; @@ -3707,10 +3718,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: - return C_O1_I2(r, 0, re); - case INDEX_op_shl_i32: case INDEX_op_shl_i64: case INDEX_op_shr_i32: diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index c24882b6ba..448896ac0d 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1332,6 +1332,21 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_mul_w(s, a0, a1, a2); + } else { + tcg_out_opc_mul_d(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mul, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -1626,13 +1641,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_mul_i32: - tcg_out_opc_mul_w(s, a0, a1, a2); - break; - case INDEX_op_mul_i64: - tcg_out_opc_mul_d(s, a0, a1, a2); - break; - case INDEX_op_mulsh_i32: tcg_out_opc_mulh_w(s, a0, a1, a2); break; @@ -2333,8 +2341,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i64: return C_O1_I2(r, rz, rJ); - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: case INDEX_op_mulsh_i32: case INDEX_op_mulsh_i64: case INDEX_op_muluh_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 4942855189..95c2645226 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1716,6 +1716,33 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn; + + if (type == TCG_TYPE_I32) { + if (use_mips32_instructions) { + tcg_out_opc_reg(s, OPC_MUL, a0, a1, a2); + return; + } + insn = OPC_MULT; + } else { + if (use_mips32r6_instructions) { + tcg_out_opc_reg(s, OPC_DMUL, a0, a1, a2); + return; + } + insn = OPC_DMULT; + } + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mul, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -1876,13 +1903,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_mul_i32: - if (use_mips32_instructions) { - tcg_out_opc_reg(s, OPC_MUL, a0, a1, a2); - break; - } - i1 = OPC_MULT, i2 = OPC_MFLO; - goto do_hilo1; case INDEX_op_mulsh_i32: if (use_mips32r6_instructions) { tcg_out_opc_reg(s, OPC_MUH, a0, a1, a2); @@ -1925,13 +1945,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } i1 = OPC_DIVU, i2 = OPC_MFHI; goto do_hilo1; - case INDEX_op_mul_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DMUL, a0, a1, a2); - break; - } - i1 = OPC_DMULT, i2 = OPC_MFLO; - goto do_hilo1; case INDEX_op_mulsh_i64: if (use_mips32r6_instructions) { tcg_out_opc_reg(s, OPC_DMUH, a0, a1, a2); @@ -2232,7 +2245,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_mul_i32: case INDEX_op_mulsh_i32: case INDEX_op_muluh_i32: case INDEX_op_div_i32: @@ -2240,7 +2252,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rem_i32: case INDEX_op_remu_i32: case INDEX_op_setcond_i32: - case INDEX_op_mul_i64: case INDEX_op_mulsh_i64: case INDEX_op_muluh_i64: case INDEX_op_div_i64: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 38cbe5223b..a7cc9d0bc7 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2965,6 +2965,25 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? MULLW : MULLD; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static void tgen_muli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out32(s, MULLI | TAI(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_mul, + .out_rri = tgen_muli, +}; + static void tgen_nand(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3077,7 +3096,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - TCGArg a0, a1, a2; + TCGArg a0, a1; switch (opc) { case INDEX_op_goto_ptr: @@ -3166,15 +3185,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, CNTPOPD | SAB(args[1], args[0], 0)); break; - case INDEX_op_mul_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out32(s, MULLI | TAI(a0, a1, a2)); - } else { - tcg_out32(s, MULLW | TAB(a0, a1, a2)); - } - break; - case INDEX_op_div_i32: tcg_out32(s, DIVW | TAB(args[0], args[1], args[2])); break; @@ -3283,14 +3293,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_mul_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out32(s, MULLI | TAI(a0, a1, a2)); - } else { - tcg_out32(s, MULLD | TAB(a0, a1, a2)); - } - break; case INDEX_op_div_i64: tcg_out32(s, DIVD | TAB(args[0], args[1], args[2])); break; @@ -4171,10 +4173,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rotr_i64: return C_O1_I2(r, r, ri); - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: - return C_O1_I2(r, r, rI); - case INDEX_op_div_i32: case INDEX_op_divu_i32: case INDEX_op_rem_i32: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 5e9e14815d..ff685037d7 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2009,6 +2009,18 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_MULW : OPC_MUL; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mul, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -2160,13 +2172,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_mul_i32: - tcg_out_opc_reg(s, OPC_MULW, a0, a1, a2); - break; - case INDEX_op_mul_i64: - tcg_out_opc_reg(s, OPC_MUL, a0, a1, a2); - break; - case INDEX_op_div_i32: tcg_out_opc_reg(s, OPC_DIVW, a0, a1, a2); break; @@ -2706,14 +2711,12 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_mul_i32: case INDEX_op_mulsh_i32: case INDEX_op_muluh_i32: case INDEX_op_div_i32: case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_mul_i64: case INDEX_op_mulsh_i64: case INDEX_op_muluh_i64: case INDEX_op_div_i64: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 98bf3ee19e..1ba9741fdd 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2258,6 +2258,57 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + if (a0 == a1) { + tcg_out_insn(s, RRE, MSR, a0, a2); + } else { + tcg_out_insn(s, RRFa, MSRKC, a0, a1, a2); + } + } else { + if (a0 == a1) { + tcg_out_insn(s, RRE, MSGR, a0, a2); + } else { + tcg_out_insn(s, RRFa, MSGRKC, a0, a1, a2); + } + } +} + +static void tgen_muli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + if (type == TCG_TYPE_I32) { + if (a2 == (int16_t)a2) { + tcg_out_insn(s, RI, MHI, a0, a2); + } else { + tcg_out_insn(s, RIL, MSFI, a0, a2); + } + } else { + if (a2 == (int16_t)a2) { + tcg_out_insn(s, RI, MGHI, a0, a2); + } else { + tcg_out_insn(s, RIL, MSGFI, a0, a2); + } + } +} + +static TCGConstraintSetIndex cset_mul(TCGType type, unsigned flags) +{ + return (HAVE_FACILITY(MISC_INSN_EXT2) + ? C_O1_I2(r, r, rJ) + : C_O1_I2(r, 0, rJ)); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul, + .out_rrr = tgen_mul, + .out_rri = tgen_muli, +}; + static void tgen_nand(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2460,22 +2511,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_mul_i32: - a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - if (a2 == (int16_t)a2) { - tcg_out_insn(s, RI, MHI, a0, a2); - } else { - tcg_out_insn(s, RIL, MSFI, a0, a2); - } - } else if (a0 == a1) { - tcg_out_insn(s, RRE, MSR, a0, a2); - } else { - tcg_out_insn(s, RRFa, MSRKC, a0, a1, a2); - } - break; - case INDEX_op_div2_i32: tcg_debug_assert(args[0] == args[2]); tcg_debug_assert(args[1] == args[3]); @@ -2651,22 +2686,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_insn(s, RRE, LRVGR, args[0], args[1]); break; - case INDEX_op_mul_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[2]) { - tcg_out_mov(s, TCG_TYPE_I64, a0, a1); - if (a2 == (int16_t)a2) { - tcg_out_insn(s, RI, MGHI, a0, a2); - } else { - tcg_out_insn(s, RIL, MSGFI, a0, a2); - } - } else if (a0 == a1) { - tcg_out_insn(s, RRE, MSGR, a0, a2); - } else { - tcg_out_insn(s, RRFa, MSGRKC, a0, a1, a2); - } - break; - case INDEX_op_div2_i64: /* * ??? We get an unnecessary sign-extension of the dividend @@ -3316,15 +3335,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_clz_i64: return C_O1_I2(r, r, rI); - case INDEX_op_mul_i32: - return (HAVE_FACILITY(MISC_INSN_EXT2) - ? C_O1_I2(r, r, ri) - : C_O1_I2(r, 0, ri)); - case INDEX_op_mul_i64: - return (HAVE_FACILITY(MISC_INSN_EXT2) - ? C_O1_I2(r, r, rJ) - : C_O1_I2(r, 0, rJ)); - case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 5819dc44fe..0a13a91166 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1337,6 +1337,26 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? ARITH_UMUL : ARITH_MULX; + tcg_out_arith(s, a0, a1, a2, insn); +} + +static void tgen_muli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? ARITH_UMUL : ARITH_MULX; + tcg_out_arithi(s, a0, a1, a2, insn); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_mul, + .out_rri = tgen_muli, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -1489,9 +1509,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_sar_i32: c = SHIFT_SRA; goto do_shift32; - case INDEX_op_mul_i32: - c = ARITH_UMUL; - goto gen_arith; case INDEX_op_div_i32: tcg_out_div32(s, a0, a1, a2, c2, 0); @@ -1568,9 +1585,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_sar_i64: c = SHIFT_SRAX; goto do_shift64; - case INDEX_op_mul_i64: - c = ARITH_MULX; - goto gen_arith; case INDEX_op_div_i64: c = ARITH_SDIVX; goto gen_arith; @@ -1667,8 +1681,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: case INDEX_op_div_i32: case INDEX_op_div_i64: case INDEX_op_divu_i32: diff --git a/tcg/tcg.c b/tcg/tcg.c index 5e43a304c0..4d221cea6f 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1021,6 +1021,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), + OUTOP(INDEX_op_mul_i32, TCGOutOpBinary, outop_mul), + OUTOP(INDEX_op_mul_i64, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), @@ -2247,7 +2249,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: - case INDEX_op_mul_i32: case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: @@ -2313,7 +2314,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st16_i64: case INDEX_op_st32_i64: case INDEX_op_st_i64: - case INDEX_op_mul_i64: case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: @@ -5436,6 +5436,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_eqv: + case INDEX_op_mul_i32: + case INDEX_op_mul_i64: case INDEX_op_nand: case INDEX_op_nor: case INDEX_op_or: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index a1f9a3a2f0..ce17079ffc 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -87,8 +87,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rem_i64: case INDEX_op_remu_i32: case INDEX_op_remu_i64: - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: case INDEX_op_shl_i32: case INDEX_op_shl_i64: case INDEX_op_shr_i32: @@ -661,6 +659,17 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static void tgen_mul(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, glue(INDEX_op_mul_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_mul = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mul, +}; + static void tgen_nand(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -789,7 +798,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, opc, args[0], args[1], args[2]); break; - CASE_32_64(mul) CASE_32_64(shl) CASE_32_64(shr) CASE_32_64(sar) From d2c3ecadea89832ab82566e881bc3a288b020473 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 09:32:18 -0800 Subject: [PATCH 0395/2760] tcg: Merge INDEX_op_mul_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 4 ++-- tcg/tcg-op.c | 12 ++++++------ tcg/tcg.c | 14 ++++++-------- tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 19 insertions(+), 23 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 96dddc5fd3..6c36e72242 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -273,7 +273,7 @@ Arithmetic - | *t0* = -*t1* (two's complement) - * - mul_i32/i64 *t0*, *t1*, *t2* + * - mul *t0*, *t1*, *t2* - | *t0* = *t1* * *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index d0fcdfd241..4ecba62fda 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -43,6 +43,7 @@ DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) +DEF(mul, 1, 2, 0, TCG_OPF_INT) DEF(nand, 1, 2, 0, TCG_OPF_INT) DEF(neg, 1, 1, 0, TCG_OPF_INT) DEF(nor, 1, 2, 0, TCG_OPF_INT) @@ -65,7 +66,6 @@ DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* arith */ -DEF(mul_i32, 1, 2, 0, 0) DEF(div_i32, 1, 2, 0, 0) DEF(divu_i32, 1, 2, 0, 0) DEF(rem_i32, 1, 2, 0, 0) @@ -116,7 +116,6 @@ DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* arith */ -DEF(mul_i64, 1, 2, 0, 0) DEF(div_i64, 1, 2, 0, 0) DEF(divu_i64, 1, 2, 0, 0) DEF(rem_i64, 1, 2, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index 315ee0a8bc..653246f3d2 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -430,7 +430,7 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) case INDEX_op_sub: return x - y; - CASE_OP_32_64(mul): + case INDEX_op_mul: return x * y; case INDEX_op_and: @@ -2963,7 +2963,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(movcond): done = fold_movcond(&ctx, op); break; - CASE_OP_32_64(mul): + case INDEX_op_mul: done = fold_mul(&ctx, op); break; CASE_OP_32_64(mulsh): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index ddc1f465a4..76d5b67fba 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -585,7 +585,7 @@ void tcg_gen_negsetcondi_i32(TCGCond cond, TCGv_i32 ret, void tcg_gen_mul_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - tcg_gen_op3_i32(INDEX_op_mul_i32, ret, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_mul, ret, arg1, arg2); } void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) @@ -1134,7 +1134,7 @@ void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_op4_i32(INDEX_op_mulu2_i32, rl, rh, arg1, arg2); } else if (TCG_TARGET_HAS_muluh_i32) { TCGv_i32 t = tcg_temp_ebb_new_i32(); - tcg_gen_op3_i32(INDEX_op_mul_i32, t, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_mul, t, arg1, arg2); tcg_gen_op3_i32(INDEX_op_muluh_i32, rh, arg1, arg2); tcg_gen_mov_i32(rl, t); tcg_temp_free_i32(t); @@ -1158,7 +1158,7 @@ void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_op4_i32(INDEX_op_muls2_i32, rl, rh, arg1, arg2); } else if (TCG_TARGET_HAS_mulsh_i32) { TCGv_i32 t = tcg_temp_ebb_new_i32(); - tcg_gen_op3_i32(INDEX_op_mul_i32, t, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_mul, t, arg1, arg2); tcg_gen_op3_i32(INDEX_op_mulsh_i32, rh, arg1, arg2); tcg_gen_mov_i32(rl, t); tcg_temp_free_i32(t); @@ -1636,7 +1636,7 @@ void tcg_gen_mul_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) TCGv_i32 t1; if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op3_i64(INDEX_op_mul_i64, ret, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_mul, ret, arg1, arg2); return; } @@ -2844,7 +2844,7 @@ void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_op4_i64(INDEX_op_mulu2_i64, rl, rh, arg1, arg2); } else if (TCG_TARGET_HAS_muluh_i64) { TCGv_i64 t = tcg_temp_ebb_new_i64(); - tcg_gen_op3_i64(INDEX_op_mul_i64, t, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_mul, t, arg1, arg2); tcg_gen_op3_i64(INDEX_op_muluh_i64, rh, arg1, arg2); tcg_gen_mov_i64(rl, t); tcg_temp_free_i64(t); @@ -2863,7 +2863,7 @@ void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_op4_i64(INDEX_op_muls2_i64, rl, rh, arg1, arg2); } else if (TCG_TARGET_HAS_mulsh_i64) { TCGv_i64 t = tcg_temp_ebb_new_i64(); - tcg_gen_op3_i64(INDEX_op_mul_i64, t, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_mul, t, arg1, arg2); tcg_gen_op3_i64(INDEX_op_mulsh_i64, rh, arg1, arg2); tcg_gen_mov_i64(rl, t); tcg_temp_free_i64(t); diff --git a/tcg/tcg.c b/tcg/tcg.c index 4d221cea6f..d16ed332c8 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1021,8 +1021,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), - OUTOP(INDEX_op_mul_i32, TCGOutOpBinary, outop_mul), - OUTOP(INDEX_op_mul_i64, TCGOutOpBinary, outop_mul), + OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), @@ -4035,22 +4034,22 @@ liveness_pass_1(TCGContext *s) goto do_not_remove; case INDEX_op_mulu2_i32: - opc_new = INDEX_op_mul_i32; + opc_new = INDEX_op_mul; opc_new2 = INDEX_op_muluh_i32; have_opc_new2 = TCG_TARGET_HAS_muluh_i32; goto do_mul2; case INDEX_op_muls2_i32: - opc_new = INDEX_op_mul_i32; + opc_new = INDEX_op_mul; opc_new2 = INDEX_op_mulsh_i32; have_opc_new2 = TCG_TARGET_HAS_mulsh_i32; goto do_mul2; case INDEX_op_mulu2_i64: - opc_new = INDEX_op_mul_i64; + opc_new = INDEX_op_mul; opc_new2 = INDEX_op_muluh_i64; have_opc_new2 = TCG_TARGET_HAS_muluh_i64; goto do_mul2; case INDEX_op_muls2_i64: - opc_new = INDEX_op_mul_i64; + opc_new = INDEX_op_mul; opc_new2 = INDEX_op_mulsh_i64; have_opc_new2 = TCG_TARGET_HAS_mulsh_i64; goto do_mul2; @@ -5436,8 +5435,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_eqv: - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: + case INDEX_op_mul: case INDEX_op_nand: case INDEX_op_nor: case INDEX_op_or: diff --git a/tcg/tci.c b/tcg/tci.c index 96e3667ab2..61c0ccf21e 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -531,7 +531,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] - regs[r2]; break; - CASE_32_64(mul) + case INDEX_op_mul: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] * regs[r2]; break; @@ -1072,14 +1072,13 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_eqv: + case INDEX_op_mul: case INDEX_op_nand: case INDEX_op_nor: case INDEX_op_or: case INDEX_op_orc: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_mul_i32: - case INDEX_op_mul_i64: case INDEX_op_div_i32: case INDEX_op_div_i64: case INDEX_op_rem_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index ce17079ffc..ffc8654427 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -662,7 +662,7 @@ static const TCGOutOpBinary outop_eqv = { static void tgen_mul(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_op_rrr(s, glue(INDEX_op_mul_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_mul, a0, a1, a2); } static const TCGOutOpBinary outop_mul = { From 937246f2ee87d01062bac7c356c1766b8c5038a8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 10:16:03 -0800 Subject: [PATCH 0396/2760] tcg: Convert muluh to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove unreachable mul[su]h_i32 leftovers from commit aeb6326ec5e ("tcg/riscv: Require TCG_TARGET_REG_BITS == 64"). Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 21 ++++++++++++++++---- tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 4 ++++ tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 4 ++++ tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 24 +++++++++++++--------- tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 34 +++++++++++++++++--------------- tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 20 +++++++++++-------- tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 24 +++++++++++++++------- tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 4 ++++ tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 23 ++++++++++++++++----- tcg/tcg-has.h | 1 - tcg/tcg-op.c | 7 ++++--- tcg/tcg.c | 16 ++++++--------- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target.c.inc | 4 ++++ 23 files changed, 123 insertions(+), 82 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 7f18727686..207a85ed61 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -27,7 +27,6 @@ #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -47,7 +46,6 @@ #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 1 #define TCG_TARGET_HAS_mulsh_i64 1 /* diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 4513140f58..bd0b7938c8 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2179,6 +2179,23 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static TCGConstraintSetIndex cset_mulh(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_I64 ? C_O1_I2(r, r, r) : C_NotImplemented; +} + +static void tgen_muluh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, UMULH, TCG_TYPE_I64, a0, a1, a2); +} + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mulh, + .out_rrr = tgen_muluh, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -2526,9 +2543,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, args[5], const_args[4], const_args[5], true); break; - case INDEX_op_muluh_i64: - tcg_out_insn(s, 3508, UMULH, TCG_TYPE_I64, a0, a1, a2); - break; case INDEX_op_mulsh_i64: tcg_out_insn(s, 3508, SMULH, TCG_TYPE_I64, a0, a1, a2); break; @@ -3043,7 +3057,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rem_i64: case INDEX_op_remu_i32: case INDEX_op_remu_i64: - case INDEX_op_muluh_i64: case INDEX_op_mulsh_i64: return C_O1_I2(r, r, r); diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index e766c6d628..d6b06e96bf 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -34,7 +34,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_div_i32 use_idiv_instructions #define TCG_TARGET_HAS_rem_i32 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 93e5c70ae3..b08e23d0dc 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1890,6 +1890,10 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index 3d36fe58f2..f4487ac1fc 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -39,7 +39,6 @@ #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 #if TCG_TARGET_REG_BITS == 64 @@ -59,7 +58,6 @@ #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 1 #define TCG_TARGET_HAS_muls2_i64 1 -#define TCG_TARGET_HAS_muluh_i64 0 #define TCG_TARGET_HAS_mulsh_i64 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #else diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 4abe89d06e..5fdca05c9d 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2664,6 +2664,10 @@ static const TCGOutOpBinary outop_mul = { .out_rri = tgen_muli, }; +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index ffacb41e80..53335b2cdb 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -20,7 +20,6 @@ #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 1 #define TCG_TARGET_HAS_mulsh_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 @@ -47,7 +46,6 @@ #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 1 #define TCG_TARGET_HAS_mulsh_i64 1 #define TCG_TARGET_HAS_qemu_ldst_i128 (cpuinfo & CPUINFO_LSX) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 448896ac0d..738fdd1a9e 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1347,6 +1347,21 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static void tgen_muluh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_mulh_wu(s, a0, a1, a2); + } else { + tcg_out_opc_mulh_du(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_muluh, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -1648,13 +1663,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_mulh_d(s, a0, a1, a2); break; - case INDEX_op_muluh_i32: - tcg_out_opc_mulh_wu(s, a0, a1, a2); - break; - case INDEX_op_muluh_i64: - tcg_out_opc_mulh_du(s, a0, a1, a2); - break; - case INDEX_op_div_i32: tcg_out_opc_div_w(s, a0, a1, a2); break; @@ -2343,8 +2351,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_mulsh_i32: case INDEX_op_mulsh_i64: - case INDEX_op_muluh_i32: - case INDEX_op_muluh_i64: case INDEX_op_div_i32: case INDEX_op_div_i64: case INDEX_op_divu_i32: diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 9d8e0fb8df..b559ab3846 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -43,7 +43,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_muluh_i32 1 #define TCG_TARGET_HAS_mulsh_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 @@ -59,7 +58,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muls2_i64 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_muluh_i64 1 #define TCG_TARGET_HAS_mulsh_i64 1 #define TCG_TARGET_HAS_ext32s_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 95c2645226..24f8184c33 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1743,6 +1743,24 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static void tgen_muluh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_MUHU : OPC_DMUHU; + tcg_out_opc_reg(s, insn, a0, a1, a2); + } else { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_MULTU : OPC_DMULTU; + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFHI, a0, 0, 0); + } +} + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_muluh, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -1910,13 +1928,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } i1 = OPC_MULT, i2 = OPC_MFHI; goto do_hilo1; - case INDEX_op_muluh_i32: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_MUHU, a0, a1, a2); - break; - } - i1 = OPC_MULTU, i2 = OPC_MFHI; - goto do_hilo1; case INDEX_op_div_i32: if (use_mips32r6_instructions) { tcg_out_opc_reg(s, OPC_DIV_R6, a0, a1, a2); @@ -1952,13 +1963,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } i1 = OPC_DMULT, i2 = OPC_MFHI; goto do_hilo1; - case INDEX_op_muluh_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DMUHU, a0, a1, a2); - break; - } - i1 = OPC_DMULTU, i2 = OPC_MFHI; - goto do_hilo1; case INDEX_op_div_i64: if (use_mips32r6_instructions) { tcg_out_opc_reg(s, OPC_DDIV_R6, a0, a1, a2); @@ -2246,14 +2250,12 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O0_I2(rz, r); case INDEX_op_mulsh_i32: - case INDEX_op_muluh_i32: case INDEX_op_div_i32: case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: case INDEX_op_setcond_i32: case INDEX_op_mulsh_i64: - case INDEX_op_muluh_i64: case INDEX_op_div_i64: case INDEX_op_divu_i64: case INDEX_op_rem_i64: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 7ebcb49a19..18ec573f7e 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -29,7 +29,6 @@ #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 1 #define TCG_TARGET_HAS_mulsh_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -52,7 +51,6 @@ #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 1 #define TCG_TARGET_HAS_mulsh_i64 1 #endif diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index a7cc9d0bc7..06a7abf2ba 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2984,6 +2984,18 @@ static const TCGOutOpBinary outop_mul = { .out_rri = tgen_muli, }; +static void tgen_muluh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? MULHWU : MULHDU; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_muluh, +}; + static void tgen_nand(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3487,15 +3499,9 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_muluh_i32: - tcg_out32(s, MULHWU | TAB(args[0], args[1], args[2])); - break; case INDEX_op_mulsh_i32: tcg_out32(s, MULHW | TAB(args[0], args[1], args[2])); break; - case INDEX_op_muluh_i64: - tcg_out32(s, MULHDU | TAB(args[0], args[1], args[2])); - break; case INDEX_op_mulsh_i64: tcg_out32(s, MULHD | TAB(args[0], args[1], args[2])); break; @@ -4177,14 +4183,12 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_muluh_i32: case INDEX_op_mulsh_i32: case INDEX_op_div_i64: case INDEX_op_divu_i64: case INDEX_op_rem_i64: case INDEX_op_remu_i64: case INDEX_op_mulsh_i64: - case INDEX_op_muluh_i64: return C_O1_I2(r, r, r); case INDEX_op_clz_i32: diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index e3018717ea..453942a6a5 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -20,7 +20,6 @@ #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) @@ -46,7 +45,6 @@ #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 1 #define TCG_TARGET_HAS_mulsh_i64 1 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index ff685037d7..65246cc450 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2021,6 +2021,23 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static TCGConstraintSetIndex cset_mulh(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_I32 ? C_NotImplemented : C_O1_I2(r, r, r); +} + +static void tgen_muluh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_MULHU, a0, a1, a2); +} + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mulh, + .out_rrr = tgen_muluh, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -2379,11 +2396,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_reg(s, OPC_MULH, a0, a1, a2); break; - case INDEX_op_muluh_i32: - case INDEX_op_muluh_i64: - tcg_out_opc_reg(s, OPC_MULHU, a0, a1, a2); - break; - case INDEX_op_mb: tcg_out_mb(s, a0); break; @@ -2712,13 +2724,11 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O1_I2(r, r, rI); case INDEX_op_mulsh_i32: - case INDEX_op_muluh_i32: case INDEX_op_div_i32: case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: case INDEX_op_mulsh_i64: - case INDEX_op_muluh_i64: case INDEX_op_div_i64: case INDEX_op_divu_i64: case INDEX_op_rem_i64: diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index e5c132cf12..ac808e21e5 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -42,7 +42,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -61,7 +60,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 1 #define TCG_TARGET_HAS_muls2_i64 HAVE_FACILITY(MISC_INSN_EXT2) -#define TCG_TARGET_HAS_muluh_i64 0 #define TCG_TARGET_HAS_mulsh_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 1ba9741fdd..e3d70ca236 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2309,6 +2309,10 @@ static const TCGOutOpBinary outop_mul = { .out_rri = tgen_muli, }; +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_nand(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index df87249df2..093de87a1d 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -28,7 +28,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -48,7 +47,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 use_vis3_instructions #define TCG_TARGET_HAS_mulsh_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 0a13a91166..31bdaecafa 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1357,6 +1357,24 @@ static const TCGOutOpBinary outop_mul = { .out_rri = tgen_muli, }; +static void tgen_muluh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_UMULXHI); +} + +static TCGConstraintSetIndex cset_muluh(TCGType type, unsigned flags) +{ + return (type == TCG_TYPE_I64 && use_vis3_instructions + ? C_O1_I2(r, r, r) : C_NotImplemented); +} + +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_muluh, + .out_rrr = tgen_muluh, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -1612,9 +1630,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_addsub2_i64(s, args[0], args[1], args[2], args[3], args[4], const_args[4], args[5], const_args[5], true); break; - case INDEX_op_muluh_i64: - tcg_out_arith(s, args[0], args[1], args[2], ARITH_UMULXHI); - break; gen_arith: tcg_out_arithc(s, a0, a1, a2, c2, c); @@ -1711,8 +1726,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_mulu2_i32: case INDEX_op_muls2_i32: return C_O2_I2(r, r, rz, rJ); - case INDEX_op_muluh_i64: - return C_O1_I2(r, r, r); default: return C_NotImplemented; diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index a84ed1313a..7f3ef73f2e 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -28,7 +28,6 @@ #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_muluh_i64 0 #define TCG_TARGET_HAS_mulsh_i64 0 /* Turn some undef macros into true macros. */ #define TCG_TARGET_HAS_add2_i32 1 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 76d5b67fba..39581465f2 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1132,7 +1132,7 @@ void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) { if (TCG_TARGET_HAS_mulu2_i32) { tcg_gen_op4_i32(INDEX_op_mulu2_i32, rl, rh, arg1, arg2); - } else if (TCG_TARGET_HAS_muluh_i32) { + } else if (tcg_op_supported(INDEX_op_muluh_i32, TCG_TYPE_I32, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_mul, t, arg1, arg2); tcg_gen_op3_i32(INDEX_op_muluh_i32, rh, arg1, arg2); @@ -2842,7 +2842,7 @@ void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_HAS_mulu2_i64) { tcg_gen_op4_i64(INDEX_op_mulu2_i64, rl, rh, arg1, arg2); - } else if (TCG_TARGET_HAS_muluh_i64) { + } else if (tcg_op_supported(INDEX_op_muluh_i64, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_mul, t, arg1, arg2); tcg_gen_op3_i64(INDEX_op_muluh_i64, rh, arg1, arg2); @@ -2867,7 +2867,8 @@ void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_op3_i64(INDEX_op_mulsh_i64, rh, arg1, arg2); tcg_gen_mov_i64(rl, t); tcg_temp_free_i64(t); - } else if (TCG_TARGET_HAS_mulu2_i64 || TCG_TARGET_HAS_muluh_i64) { + } else if (TCG_TARGET_HAS_mulu2_i64 || + tcg_op_supported(INDEX_op_muluh_i64, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); TCGv_i64 t2 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index d16ed332c8..cd85967229 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1022,6 +1022,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), + OUTOP(INDEX_op_muluh_i32, TCGOutOpBinary, outop_muluh), + OUTOP(INDEX_op_muluh_i64, TCGOutOpBinary, outop_muluh), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), @@ -2280,8 +2282,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_mulu2_i32; case INDEX_op_muls2_i32: return TCG_TARGET_HAS_muls2_i32; - case INDEX_op_muluh_i32: - return TCG_TARGET_HAS_muluh_i32; case INDEX_op_mulsh_i32: return TCG_TARGET_HAS_mulsh_i32; case INDEX_op_bswap16_i32: @@ -2362,8 +2362,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_mulu2_i64; case INDEX_op_muls2_i64: return TCG_TARGET_HAS_muls2_i64; - case INDEX_op_muluh_i64: - return TCG_TARGET_HAS_muluh_i64; case INDEX_op_mulsh_i64: return TCG_TARGET_HAS_mulsh_i64; @@ -3876,7 +3874,6 @@ liveness_pass_1(TCGContext *s) QTAILQ_FOREACH_REVERSE_SAFE(op, &s->ops, link, op_prev) { int nb_iargs, nb_oargs; TCGOpcode opc_new, opc_new2; - bool have_opc_new2; TCGLifeData arg_life = 0; TCGTemp *ts; TCGOpcode opc = op->opc; @@ -4036,22 +4033,18 @@ liveness_pass_1(TCGContext *s) case INDEX_op_mulu2_i32: opc_new = INDEX_op_mul; opc_new2 = INDEX_op_muluh_i32; - have_opc_new2 = TCG_TARGET_HAS_muluh_i32; goto do_mul2; case INDEX_op_muls2_i32: opc_new = INDEX_op_mul; opc_new2 = INDEX_op_mulsh_i32; - have_opc_new2 = TCG_TARGET_HAS_mulsh_i32; goto do_mul2; case INDEX_op_mulu2_i64: opc_new = INDEX_op_mul; opc_new2 = INDEX_op_muluh_i64; - have_opc_new2 = TCG_TARGET_HAS_muluh_i64; goto do_mul2; case INDEX_op_muls2_i64: opc_new = INDEX_op_mul; opc_new2 = INDEX_op_mulsh_i64; - have_opc_new2 = TCG_TARGET_HAS_mulsh_i64; goto do_mul2; do_mul2: nb_iargs = 2; @@ -4065,7 +4058,8 @@ liveness_pass_1(TCGContext *s) op->opc = opc = opc_new; op->args[1] = op->args[2]; op->args[2] = op->args[3]; - } else if (arg_temp(op->args[0])->state == TS_DEAD && have_opc_new2) { + } else if (arg_temp(op->args[0])->state == TS_DEAD && + tcg_op_supported(opc_new2, TCGOP_TYPE(op), 0)) { /* The low part of the operation is dead; generate the high. */ op->opc = opc = opc_new2; op->args[0] = op->args[1]; @@ -5436,6 +5430,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_andc: case INDEX_op_eqv: case INDEX_op_mul: + case INDEX_op_muluh_i32: + case INDEX_op_muluh_i64: case INDEX_op_nand: case INDEX_op_nor: case INDEX_op_or: diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index f147da5c0e..b99b12c24c 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -18,7 +18,6 @@ #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_muluh_i32 0 #define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -42,7 +41,6 @@ #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 1 -#define TCG_TARGET_HAS_muluh_i64 0 #define TCG_TARGET_HAS_mulsh_i64 0 #else #define TCG_TARGET_HAS_mulu2_i32 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index ffc8654427..e4a2b171df 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -670,6 +670,10 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static const TCGOutOpBinary outop_muluh = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_nand(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { From aa28c9ef8e109db40d4781d82452805486f2a2bf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 10:36:24 -0800 Subject: [PATCH 0397/2760] tcg: Merge INDEX_op_muluh_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 19 +++++++++++-------- tcg/tcg-op.c | 10 +++++----- tcg/tcg.c | 13 ++++--------- 5 files changed, 22 insertions(+), 25 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 6c36e72242..4fed5a77c6 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -591,7 +591,7 @@ Multiword arithmetic support * - mulsh_i32/i64 *t0*, *t1*, *t2* - muluh_i32/i64 *t0*, *t1*, *t2* + muluh *t0*, *t1*, *t2* - | Provide the high part of a signed or unsigned multiply, respectively. | diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 4ecba62fda..28a5128537 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -44,6 +44,7 @@ DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(mul, 1, 2, 0, TCG_OPF_INT) +DEF(muluh, 1, 2, 0, TCG_OPF_INT) DEF(nand, 1, 2, 0, TCG_OPF_INT) DEF(neg, 1, 1, 0, TCG_OPF_INT) DEF(nor, 1, 2, 0, TCG_OPF_INT) @@ -89,7 +90,6 @@ DEF(add2_i32, 2, 4, 0, 0) DEF(sub2_i32, 2, 4, 0, 0) DEF(mulu2_i32, 2, 2, 0, 0) DEF(muls2_i32, 2, 2, 0, 0) -DEF(muluh_i32, 1, 2, 0, 0) DEF(mulsh_i32, 1, 2, 0, 0) DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(setcond2_i32, 1, 4, 1, 0) @@ -151,7 +151,6 @@ DEF(add2_i64, 2, 4, 0, 0) DEF(sub2_i64, 2, 4, 0, 0) DEF(mulu2_i64, 2, 2, 0, 0) DEF(muls2_i64, 2, 2, 0, 0) -DEF(muluh_i64, 1, 2, 0, 0) DEF(mulsh_i64, 1, 2, 0, 0) #define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) diff --git a/tcg/optimize.c b/tcg/optimize.c index 653246f3d2..e19bccf906 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -419,7 +419,8 @@ static bool tcg_opt_gen_movi(OptContext *ctx, TCGOp *op, return tcg_opt_gen_mov(ctx, op, dst, arg_new_constant(ctx, val)); } -static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) +static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, + uint64_t x, uint64_t y) { uint64_t l64, h64; @@ -541,14 +542,16 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) case INDEX_op_extrh_i64_i32: return (uint64_t)x >> 32; - case INDEX_op_muluh_i32: - return ((uint64_t)(uint32_t)x * (uint32_t)y) >> 32; + case INDEX_op_muluh: + if (type == TCG_TYPE_I32) { + return ((uint64_t)(uint32_t)x * (uint32_t)y) >> 32; + } + mulu64(&l64, &h64, x, y); + return h64; + case INDEX_op_mulsh_i32: return ((int64_t)(int32_t)x * (int32_t)y) >> 32; - case INDEX_op_muluh_i64: - mulu64(&l64, &h64, x, y); - return h64; case INDEX_op_mulsh_i64: muls64(&l64, &h64, x, y); return h64; @@ -580,7 +583,7 @@ static uint64_t do_constant_folding_2(TCGOpcode op, uint64_t x, uint64_t y) static uint64_t do_constant_folding(TCGOpcode op, TCGType type, uint64_t x, uint64_t y) { - uint64_t res = do_constant_folding_2(op, x, y); + uint64_t res = do_constant_folding_2(op, type, x, y); if (type == TCG_TYPE_I32) { res = (int32_t)res; } @@ -2967,7 +2970,7 @@ void tcg_optimize(TCGContext *s) done = fold_mul(&ctx, op); break; CASE_OP_32_64(mulsh): - CASE_OP_32_64(muluh): + case INDEX_op_muluh: done = fold_mul_highpart(&ctx, op); break; CASE_OP_32_64(muls2): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 39581465f2..7a37b21c56 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1132,10 +1132,10 @@ void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) { if (TCG_TARGET_HAS_mulu2_i32) { tcg_gen_op4_i32(INDEX_op_mulu2_i32, rl, rh, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_muluh_i32, TCG_TYPE_I32, 0)) { + } else if (tcg_op_supported(INDEX_op_muluh, TCG_TYPE_I32, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_mul, t, arg1, arg2); - tcg_gen_op3_i32(INDEX_op_muluh_i32, rh, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_muluh, rh, arg1, arg2); tcg_gen_mov_i32(rl, t); tcg_temp_free_i32(t); } else if (TCG_TARGET_REG_BITS == 64) { @@ -2842,10 +2842,10 @@ void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_HAS_mulu2_i64) { tcg_gen_op4_i64(INDEX_op_mulu2_i64, rl, rh, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_muluh_i64, TCG_TYPE_I64, 0)) { + } else if (tcg_op_supported(INDEX_op_muluh, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_mul, t, arg1, arg2); - tcg_gen_op3_i64(INDEX_op_muluh_i64, rh, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_muluh, rh, arg1, arg2); tcg_gen_mov_i64(rl, t); tcg_temp_free_i64(t); } else { @@ -2868,7 +2868,7 @@ void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_mov_i64(rl, t); tcg_temp_free_i64(t); } else if (TCG_TARGET_HAS_mulu2_i64 || - tcg_op_supported(INDEX_op_muluh_i64, TCG_TYPE_I64, 0)) { + tcg_op_supported(INDEX_op_muluh, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); TCGv_i64 t2 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index cd85967229..808ac8c431 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1022,8 +1022,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), - OUTOP(INDEX_op_muluh_i32, TCGOutOpBinary, outop_muluh), - OUTOP(INDEX_op_muluh_i64, TCGOutOpBinary, outop_muluh), + OUTOP(INDEX_op_muluh, TCGOutOpBinary, outop_muluh), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), @@ -4030,17 +4029,14 @@ liveness_pass_1(TCGContext *s) } goto do_not_remove; - case INDEX_op_mulu2_i32: - opc_new = INDEX_op_mul; - opc_new2 = INDEX_op_muluh_i32; - goto do_mul2; case INDEX_op_muls2_i32: opc_new = INDEX_op_mul; opc_new2 = INDEX_op_mulsh_i32; goto do_mul2; + case INDEX_op_mulu2_i32: case INDEX_op_mulu2_i64: opc_new = INDEX_op_mul; - opc_new2 = INDEX_op_muluh_i64; + opc_new2 = INDEX_op_muluh; goto do_mul2; case INDEX_op_muls2_i64: opc_new = INDEX_op_mul; @@ -5430,8 +5426,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_andc: case INDEX_op_eqv: case INDEX_op_mul: - case INDEX_op_muluh_i32: - case INDEX_op_muluh_i64: + case INDEX_op_muluh: case INDEX_op_nand: case INDEX_op_nor: case INDEX_op_or: From a9983f81290d41ed614a193a33d03be936f6435c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 11:13:05 -0800 Subject: [PATCH 0398/2760] tcg: Convert mulsh to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 17 +++++++++++----- tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 4 ++++ tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 4 ++++ tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 24 +++++++++++++--------- tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 34 +++++++++++++++++--------------- tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 21 +++++++++++--------- tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 19 +++++++++++------- tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 4 ++++ tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 4 ++++ tcg/tcg-has.h | 1 - tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 8 ++++---- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target.c.inc | 4 ++++ 23 files changed, 95 insertions(+), 72 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 207a85ed61..bde6db8f2a 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -27,7 +27,6 @@ #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -46,7 +45,6 @@ #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 1 /* * Without FEAT_LSE2, we must use LDXP+STXP to implement atomic 128-bit load, diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index bd0b7938c8..493c504682 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2184,6 +2184,18 @@ static TCGConstraintSetIndex cset_mulh(TCGType type, unsigned flags) return type == TCG_TYPE_I64 ? C_O1_I2(r, r, r) : C_NotImplemented; } +static void tgen_mulsh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, SMULH, TCG_TYPE_I64, a0, a1, a2); +} + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mulh, + .out_rrr = tgen_mulsh, +}; + static void tgen_muluh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2543,10 +2555,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, args[5], const_args[4], const_args[5], true); break; - case INDEX_op_mulsh_i64: - tcg_out_insn(s, 3508, SMULH, TCG_TYPE_I64, a0, a1, a2); - break; - case INDEX_op_mb: tcg_out_mb(s, a0); break; @@ -3057,7 +3065,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rem_i64: case INDEX_op_remu_i32: case INDEX_op_remu_i64: - case INDEX_op_mulsh_i64: return C_O1_I2(r, r, r); case INDEX_op_shl_i32: diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index d6b06e96bf..ab9b7b6162 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -34,7 +34,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_div_i32 use_idiv_instructions #define TCG_TARGET_HAS_rem_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index b08e23d0dc..1c19004e6e 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1890,6 +1890,10 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_muluh = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index f4487ac1fc..121fb95ee0 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -39,7 +39,6 @@ #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_mulsh_i32 0 #if TCG_TARGET_REG_BITS == 64 /* Keep 32-bit values zero-extended in a register. */ @@ -58,7 +57,6 @@ #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 1 #define TCG_TARGET_HAS_muls2_i64 1 -#define TCG_TARGET_HAS_mulsh_i64 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #else #define TCG_TARGET_HAS_qemu_st8_i32 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 5fdca05c9d..d0391157a4 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2664,6 +2664,10 @@ static const TCGOutOpBinary outop_mul = { .out_rri = tgen_muli, }; +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_muluh = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 53335b2cdb..e29c892756 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -20,7 +20,6 @@ #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_clz_i32 1 @@ -46,7 +45,6 @@ #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 1 #define TCG_TARGET_HAS_qemu_ldst_i128 (cpuinfo & CPUINFO_LSX) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 738fdd1a9e..7503270ca3 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1347,6 +1347,21 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static void tgen_mulsh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_mulh_w(s, a0, a1, a2); + } else { + tcg_out_opc_mulh_d(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mulsh, +}; + static void tgen_muluh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1656,13 +1671,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_mulsh_i32: - tcg_out_opc_mulh_w(s, a0, a1, a2); - break; - case INDEX_op_mulsh_i64: - tcg_out_opc_mulh_d(s, a0, a1, a2); - break; - case INDEX_op_div_i32: tcg_out_opc_div_w(s, a0, a1, a2); break; @@ -2349,8 +2357,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i64: return C_O1_I2(r, rz, rJ); - case INDEX_op_mulsh_i32: - case INDEX_op_mulsh_i64: case INDEX_op_div_i32: case INDEX_op_div_i64: case INDEX_op_divu_i32: diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index b559ab3846..ebaaa49cdd 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -43,7 +43,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_mulsh_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 @@ -58,7 +57,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muls2_i64 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_mulsh_i64 1 #define TCG_TARGET_HAS_ext32s_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 24f8184c33..a1c215c25d 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1743,6 +1743,24 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static void tgen_mulsh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_MUH : OPC_DMUH; + tcg_out_opc_reg(s, insn, a0, a1, a2); + } else { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_MULT : OPC_DMULT; + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFHI, a0, 0, 0); + } +} + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mulsh, +}; + static void tgen_muluh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1921,13 +1939,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_mulsh_i32: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_MUH, a0, a1, a2); - break; - } - i1 = OPC_MULT, i2 = OPC_MFHI; - goto do_hilo1; case INDEX_op_div_i32: if (use_mips32r6_instructions) { tcg_out_opc_reg(s, OPC_DIV_R6, a0, a1, a2); @@ -1956,13 +1967,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } i1 = OPC_DIVU, i2 = OPC_MFHI; goto do_hilo1; - case INDEX_op_mulsh_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DMUH, a0, a1, a2); - break; - } - i1 = OPC_DMULT, i2 = OPC_MFHI; - goto do_hilo1; case INDEX_op_div_i64: if (use_mips32r6_instructions) { tcg_out_opc_reg(s, OPC_DDIV_R6, a0, a1, a2); @@ -2249,13 +2253,11 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_mulsh_i32: case INDEX_op_div_i32: case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: case INDEX_op_setcond_i32: - case INDEX_op_mulsh_i64: case INDEX_op_div_i64: case INDEX_op_divu_i64: case INDEX_op_rem_i64: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 18ec573f7e..bbbd8de2c7 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -29,7 +29,6 @@ #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 @@ -51,7 +50,6 @@ #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 1 #endif #define TCG_TARGET_HAS_qemu_ldst_i128 \ diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 06a7abf2ba..7ebadf396a 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2984,6 +2984,18 @@ static const TCGOutOpBinary outop_mul = { .out_rri = tgen_muli, }; +static void tgen_mulsh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? MULHW : MULHD; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_mulsh, +}; + static void tgen_muluh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3499,13 +3511,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_mulsh_i32: - tcg_out32(s, MULHW | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_mulsh_i64: - tcg_out32(s, MULHD | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_mb: tcg_out_mb(s, args[0]); break; @@ -4183,12 +4188,10 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_mulsh_i32: case INDEX_op_div_i64: case INDEX_op_divu_i64: case INDEX_op_rem_i64: case INDEX_op_remu_i64: - case INDEX_op_mulsh_i64: return C_O1_I2(r, r, r); case INDEX_op_clz_i32: diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 453942a6a5..f7e1ef82fc 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -20,7 +20,6 @@ #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_clz_i32 (cpuinfo & CPUINFO_ZBB) @@ -45,7 +44,6 @@ #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 1 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 65246cc450..82f76b8e0c 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2026,6 +2026,18 @@ static TCGConstraintSetIndex cset_mulh(TCGType type, unsigned flags) return type == TCG_TYPE_I32 ? C_NotImplemented : C_O1_I2(r, r, r); } +static void tgen_mulsh(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_opc_reg(s, OPC_MULH, a0, a1, a2); +} + +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mulh, + .out_rrr = tgen_mulsh, +}; + static void tgen_muluh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2391,11 +2403,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_imm(s, OPC_SRAI, a0, a1, 32); break; - case INDEX_op_mulsh_i32: - case INDEX_op_mulsh_i64: - tcg_out_opc_reg(s, OPC_MULH, a0, a1, a2); - break; - case INDEX_op_mb: tcg_out_mb(s, a0); break; @@ -2723,12 +2730,10 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_mulsh_i32: case INDEX_op_div_i32: case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_mulsh_i64: case INDEX_op_div_i64: case INDEX_op_divu_i64: case INDEX_op_rem_i64: diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index ac808e21e5..64f1805641 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -42,7 +42,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_muls2_i32 0 -#define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -60,7 +59,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 1 #define TCG_TARGET_HAS_muls2_i64 HAVE_FACILITY(MISC_INSN_EXT2) -#define TCG_TARGET_HAS_mulsh_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index e3d70ca236..2685e6ffa1 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2309,6 +2309,10 @@ static const TCGOutOpBinary outop_mul = { .out_rri = tgen_muli, }; +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_muluh = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 093de87a1d..5a517b6835 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -28,7 +28,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 @@ -47,7 +46,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 31bdaecafa..95a138ef56 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1357,6 +1357,10 @@ static const TCGOutOpBinary outop_mul = { .out_rri = tgen_muli, }; +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_muluh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 7f3ef73f2e..3d4c67698f 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -28,7 +28,6 @@ #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_muls2_i64 0 -#define TCG_TARGET_HAS_mulsh_i64 0 /* Turn some undef macros into true macros. */ #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 7a37b21c56..a043c4554b 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1156,7 +1156,7 @@ void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) { if (TCG_TARGET_HAS_muls2_i32) { tcg_gen_op4_i32(INDEX_op_muls2_i32, rl, rh, arg1, arg2); - } else if (TCG_TARGET_HAS_mulsh_i32) { + } else if (tcg_op_supported(INDEX_op_mulsh_i32, TCG_TYPE_I32, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_mul, t, arg1, arg2); tcg_gen_op3_i32(INDEX_op_mulsh_i32, rh, arg1, arg2); @@ -2861,7 +2861,7 @@ void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_HAS_muls2_i64) { tcg_gen_op4_i64(INDEX_op_muls2_i64, rl, rh, arg1, arg2); - } else if (TCG_TARGET_HAS_mulsh_i64) { + } else if (tcg_op_supported(INDEX_op_mulsh_i64, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_mul, t, arg1, arg2); tcg_gen_op3_i64(INDEX_op_mulsh_i64, rh, arg1, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index 808ac8c431..4bfda0a38f 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1022,6 +1022,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), + OUTOP(INDEX_op_mulsh_i32, TCGOutOpBinary, outop_mulsh), + OUTOP(INDEX_op_mulsh_i64, TCGOutOpBinary, outop_mulsh), OUTOP(INDEX_op_muluh, TCGOutOpBinary, outop_muluh), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), @@ -2281,8 +2283,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_mulu2_i32; case INDEX_op_muls2_i32: return TCG_TARGET_HAS_muls2_i32; - case INDEX_op_mulsh_i32: - return TCG_TARGET_HAS_mulsh_i32; case INDEX_op_bswap16_i32: return TCG_TARGET_HAS_bswap16_i32; case INDEX_op_bswap32_i32: @@ -2361,8 +2361,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_mulu2_i64; case INDEX_op_muls2_i64: return TCG_TARGET_HAS_muls2_i64; - case INDEX_op_mulsh_i64: - return TCG_TARGET_HAS_mulsh_i64; case INDEX_op_mov_vec: case INDEX_op_dup_vec: @@ -5426,6 +5424,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_andc: case INDEX_op_eqv: case INDEX_op_mul: + case INDEX_op_mulsh_i32: + case INDEX_op_mulsh_i64: case INDEX_op_muluh: case INDEX_op_nand: case INDEX_op_nor: diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index b99b12c24c..0627585097 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -18,7 +18,6 @@ #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_mulsh_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 @@ -41,7 +40,6 @@ #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 1 -#define TCG_TARGET_HAS_mulsh_i64 0 #else #define TCG_TARGET_HAS_mulu2_i32 1 #endif /* TCG_TARGET_REG_BITS == 64 */ diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index e4a2b171df..1dcce543ec 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -670,6 +670,10 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static const TCGOutOpBinary outop_mulsh = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_muluh = { .base.static_constraint = C_NotImplemented, }; From c742824dd8df3283098d5339291d49e65e515751 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 11:19:29 -0800 Subject: [PATCH 0399/2760] tcg: Merge INDEX_op_mulsh_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 10 +++++----- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 14 ++++---------- 5 files changed, 15 insertions(+), 22 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 4fed5a77c6..fe922d1dac 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -589,7 +589,7 @@ Multiword arithmetic support - | Similar to mulu2, except the two inputs *t1* and *t2* are signed. - * - mulsh_i32/i64 *t0*, *t1*, *t2* + * - mulsh *t0*, *t1*, *t2* muluh *t0*, *t1*, *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 28a5128537..a9d7938a52 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -44,6 +44,7 @@ DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(mul, 1, 2, 0, TCG_OPF_INT) +DEF(mulsh, 1, 2, 0, TCG_OPF_INT) DEF(muluh, 1, 2, 0, TCG_OPF_INT) DEF(nand, 1, 2, 0, TCG_OPF_INT) DEF(neg, 1, 1, 0, TCG_OPF_INT) @@ -90,7 +91,6 @@ DEF(add2_i32, 2, 4, 0, 0) DEF(sub2_i32, 2, 4, 0, 0) DEF(mulu2_i32, 2, 2, 0, 0) DEF(muls2_i32, 2, 2, 0, 0) -DEF(mulsh_i32, 1, 2, 0, 0) DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(setcond2_i32, 1, 4, 1, 0) @@ -151,7 +151,6 @@ DEF(add2_i64, 2, 4, 0, 0) DEF(sub2_i64, 2, 4, 0, 0) DEF(mulu2_i64, 2, 2, 0, 0) DEF(muls2_i64, 2, 2, 0, 0) -DEF(mulsh_i64, 1, 2, 0, 0) #define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) diff --git a/tcg/optimize.c b/tcg/optimize.c index e19bccf906..fd446fc47d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -549,10 +549,10 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, mulu64(&l64, &h64, x, y); return h64; - case INDEX_op_mulsh_i32: - return ((int64_t)(int32_t)x * (int32_t)y) >> 32; - - case INDEX_op_mulsh_i64: + case INDEX_op_mulsh: + if (type == TCG_TYPE_I32) { + return ((int64_t)(int32_t)x * (int32_t)y) >> 32; + } muls64(&l64, &h64, x, y); return h64; @@ -2969,7 +2969,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_mul: done = fold_mul(&ctx, op); break; - CASE_OP_32_64(mulsh): + case INDEX_op_mulsh: case INDEX_op_muluh: done = fold_mul_highpart(&ctx, op); break; diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index a043c4554b..664c698187 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1156,10 +1156,10 @@ void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) { if (TCG_TARGET_HAS_muls2_i32) { tcg_gen_op4_i32(INDEX_op_muls2_i32, rl, rh, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_mulsh_i32, TCG_TYPE_I32, 0)) { + } else if (tcg_op_supported(INDEX_op_mulsh, TCG_TYPE_I32, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_mul, t, arg1, arg2); - tcg_gen_op3_i32(INDEX_op_mulsh_i32, rh, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_mulsh, rh, arg1, arg2); tcg_gen_mov_i32(rl, t); tcg_temp_free_i32(t); } else if (TCG_TARGET_REG_BITS == 32) { @@ -2861,10 +2861,10 @@ void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_HAS_muls2_i64) { tcg_gen_op4_i64(INDEX_op_muls2_i64, rl, rh, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_mulsh_i64, TCG_TYPE_I64, 0)) { + } else if (tcg_op_supported(INDEX_op_mulsh, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_mul, t, arg1, arg2); - tcg_gen_op3_i64(INDEX_op_mulsh_i64, rh, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_mulsh, rh, arg1, arg2); tcg_gen_mov_i64(rl, t); tcg_temp_free_i64(t); } else if (TCG_TARGET_HAS_mulu2_i64 || diff --git a/tcg/tcg.c b/tcg/tcg.c index 4bfda0a38f..d4b5872128 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1022,8 +1022,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), - OUTOP(INDEX_op_mulsh_i32, TCGOutOpBinary, outop_mulsh), - OUTOP(INDEX_op_mulsh_i64, TCGOutOpBinary, outop_mulsh), + OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), OUTOP(INDEX_op_muluh, TCGOutOpBinary, outop_muluh), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), @@ -4028,18 +4027,14 @@ liveness_pass_1(TCGContext *s) goto do_not_remove; case INDEX_op_muls2_i32: + case INDEX_op_muls2_i64: opc_new = INDEX_op_mul; - opc_new2 = INDEX_op_mulsh_i32; + opc_new2 = INDEX_op_mulsh; goto do_mul2; case INDEX_op_mulu2_i32: case INDEX_op_mulu2_i64: opc_new = INDEX_op_mul; opc_new2 = INDEX_op_muluh; - goto do_mul2; - case INDEX_op_muls2_i64: - opc_new = INDEX_op_mul; - opc_new2 = INDEX_op_mulsh_i64; - goto do_mul2; do_mul2: nb_iargs = 2; nb_oargs = 2; @@ -5424,8 +5419,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_andc: case INDEX_op_eqv: case INDEX_op_mul: - case INDEX_op_mulsh_i32: - case INDEX_op_mulsh_i64: + case INDEX_op_mulsh: case INDEX_op_muluh: case INDEX_op_nand: case INDEX_op_nor: From 0cdacacebbe8a5e0d8a68a8c0007bff364c0e79a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 13:04:24 -0800 Subject: [PATCH 0400/2760] tcg: Convert div to TCGOutOpBinary For TCI, we're losing type information in the interpreter. Introduce a tci-specific opcode to handle the difference. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 17 ++++++++----- tcg/arm/tcg-target.c.inc | 28 +++++++++++++-------- tcg/i386/tcg-target.c.inc | 4 +++ tcg/loongarch64/tcg-target.c.inc | 24 +++++++++++------- tcg/mips/tcg-target.c.inc | 37 ++++++++++++++++------------ tcg/ppc/tcg-target.c.inc | 21 +++++++++------- tcg/riscv/tcg-target.c.inc | 21 +++++++++------- tcg/s390x/tcg-target.c.inc | 4 +++ tcg/sparc64/tcg-target.c.inc | 42 ++++++++++++++++++++++++++------ tcg/tcg-op.c | 8 +++--- tcg/tcg.c | 6 +++-- tcg/tci.c | 3 ++- tcg/tci/tcg-target-opc.h.inc | 1 + tcg/tci/tcg-target.c.inc | 17 ++++++++++--- 14 files changed, 156 insertions(+), 77 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 493c504682..52069f1445 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2157,6 +2157,17 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, SDIV, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divs, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2362,10 +2373,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_div_i64: - case INDEX_op_div_i32: - tcg_out_insn(s, 3508, SDIV, ext, a0, a1, a2); - break; case INDEX_op_divu_i64: case INDEX_op_divu_i32: tcg_out_insn(s, 3508, UDIV, ext, a0, a1, a2); @@ -3057,8 +3064,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rC); - case INDEX_op_div_i32: - case INDEX_op_div_i64: case INDEX_op_divu_i32: case INDEX_op_divu_i64: case INDEX_op_rem_i32: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 1c19004e6e..e07e4c06d9 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -937,12 +937,6 @@ static void tcg_out_smull32(TCGContext *s, ARMCond cond, TCGReg rd0, (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); } -static void tcg_out_sdiv(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, TCGReg rm) -{ - tcg_out32(s, 0x0710f010 | (cond << 28) | (rd << 16) | rn | (rm << 8)); -} - static void tcg_out_udiv(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn, TCGReg rm) { @@ -1874,6 +1868,24 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static TCGConstraintSetIndex cset_idiv(TCGType type, unsigned flags) +{ + return use_idiv_instructions ? C_O1_I2(r, r, r) : C_NotImplemented; +} + +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + /* sdiv */ + tcg_out32(s, 0x0710f010 | (COND_AL << 28) | (a0 << 16) | a1 | (a2 << 8)); +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_idiv, + .out_rrr = tgen_divs, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; @@ -2218,9 +2230,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_div_i32: - tcg_out_sdiv(s, COND_AL, args[0], args[1], args[2]); - break; case INDEX_op_divu_i32: tcg_out_udiv(s, COND_AL, args[0], args[1], args[2]); break; @@ -2268,7 +2277,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ctz_i32: return C_O1_I2(r, r, rIK); - case INDEX_op_div_i32: case INDEX_op_divu_i32: return C_O1_I2(r, r, r); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index d0391157a4..e132dd0c88 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2633,6 +2633,10 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 7503270ca3..c42d8d690a 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1328,6 +1328,21 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_div_w(s, a0, a1, a2); + } else { + tcg_out_opc_div_d(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divs, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; @@ -1671,13 +1686,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_div_i32: - tcg_out_opc_div_w(s, a0, a1, a2); - break; - case INDEX_op_div_i64: - tcg_out_opc_div_d(s, a0, a1, a2); - break; - case INDEX_op_divu_i32: tcg_out_opc_div_wu(s, a0, a1, a2); break; @@ -2357,8 +2365,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i64: return C_O1_I2(r, rz, rJ); - case INDEX_op_div_i32: - case INDEX_op_div_i64: case INDEX_op_divu_i32: case INDEX_op_divu_i64: case INDEX_op_rem_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index a1c215c25d..7762d88e6b 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1712,6 +1712,27 @@ static const TCGOutOpBinary outop_andc = { .base.static_constraint = C_NotImplemented, }; +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + if (type == TCG_TYPE_I32) { + tcg_out_opc_reg(s, OPC_DIV_R6, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_DDIV_R6, a0, a1, a2); + } + } else { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_DIV : OPC_DDIV; + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); + } +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divs, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; @@ -1939,13 +1960,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_div_i32: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DIV_R6, a0, a1, a2); - break; - } - i1 = OPC_DIV, i2 = OPC_MFLO; - goto do_hilo1; case INDEX_op_divu_i32: if (use_mips32r6_instructions) { tcg_out_opc_reg(s, OPC_DIVU_R6, a0, a1, a2); @@ -1967,13 +1981,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } i1 = OPC_DIVU, i2 = OPC_MFHI; goto do_hilo1; - case INDEX_op_div_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DDIV_R6, a0, a1, a2); - break; - } - i1 = OPC_DDIV, i2 = OPC_MFLO; - goto do_hilo1; case INDEX_op_divu_i64: if (use_mips32r6_instructions) { tcg_out_opc_reg(s, OPC_DDIVU_R6, a0, a1, a2); @@ -2253,12 +2260,10 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_div_i32: case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: case INDEX_op_setcond_i32: - case INDEX_op_div_i64: case INDEX_op_divu_i64: case INDEX_op_rem_i64: case INDEX_op_remu_i64: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 7ebadf396a..9fdf8df082 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2960,6 +2960,18 @@ static void tgen_eqv(TCGContext *s, TCGType type, tcg_out32(s, EQV | SAB(a1, a0, a2)); } +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? DIVW : DIVD; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divs, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_O1_I2(r, r, r), .out_rrr = tgen_eqv, @@ -3209,10 +3221,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, CNTPOPD | SAB(args[1], args[0], 0)); break; - case INDEX_op_div_i32: - tcg_out32(s, DIVW | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_divu_i32: tcg_out32(s, DIVWU | TAB(args[0], args[1], args[2])); break; @@ -3317,9 +3325,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_div_i64: - tcg_out32(s, DIVD | TAB(args[0], args[1], args[2])); - break; case INDEX_op_divu_i64: tcg_out32(s, DIVDU | TAB(args[0], args[1], args[2])); break; @@ -4184,11 +4189,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rotr_i64: return C_O1_I2(r, r, ri); - case INDEX_op_div_i32: case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_div_i64: case INDEX_op_divu_i64: case INDEX_op_rem_i64: case INDEX_op_remu_i64: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 82f76b8e0c..15925729dc 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1997,6 +1997,18 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_DIVW : OPC_DIV; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divs, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2201,13 +2213,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_div_i32: - tcg_out_opc_reg(s, OPC_DIVW, a0, a1, a2); - break; - case INDEX_op_div_i64: - tcg_out_opc_reg(s, OPC_DIV, a0, a1, a2); - break; - case INDEX_op_divu_i32: tcg_out_opc_reg(s, OPC_DIVUW, a0, a1, a2); break; @@ -2730,11 +2735,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_div_i32: case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_div_i64: case INDEX_op_divu_i64: case INDEX_op_rem_i64: case INDEX_op_remu_i64: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 2685e6ffa1..fd0e717c49 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2242,6 +2242,10 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 95a138ef56..779d0ce882 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1333,6 +1333,40 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_divs_rJ(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGArg a2, bool c2) +{ + uint32_t insn; + + if (type == TCG_TYPE_I32) { + /* Load Y with the sign extension of a1 to 64-bits. */ + tcg_out_arithi(s, TCG_REG_T1, a1, 31, SHIFT_SRA); + tcg_out_sety(s, TCG_REG_T1); + insn = ARITH_SDIV; + } else { + insn = ARITH_SDIVX; + } + tcg_out_arithc(s, a0, a1, a2, c2, insn); +} + +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_divs_rJ(s, type, a0, a1, a2, false); +} + +static void tgen_divsi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_divs_rJ(s, type, a0, a1, a2, true); +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_divs, + .out_rri = tgen_divsi, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; @@ -1532,9 +1566,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, c = SHIFT_SRA; goto do_shift32; - case INDEX_op_div_i32: - tcg_out_div32(s, a0, a1, a2, c2, 0); - break; case INDEX_op_divu_i32: tcg_out_div32(s, a0, a1, a2, c2, 1); break; @@ -1607,9 +1638,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_sar_i64: c = SHIFT_SRAX; goto do_shift64; - case INDEX_op_div_i64: - c = ARITH_SDIVX; - goto gen_arith; case INDEX_op_divu_i64: c = ARITH_UDIVX; goto gen_arith; @@ -1700,8 +1728,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); - case INDEX_op_div_i32: - case INDEX_op_div_i64: case INDEX_op_divu_i32: case INDEX_op_divu_i64: case INDEX_op_shl_i32: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 664c698187..69e50f968f 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -601,7 +601,7 @@ void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_div_i32) { + if (tcg_op_supported(INDEX_op_div_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_div_i32, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div2_i32) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); @@ -617,7 +617,7 @@ void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { if (TCG_TARGET_HAS_rem_i32) { tcg_gen_op3_i32(INDEX_op_rem_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div_i32) { + } else if (tcg_op_supported(INDEX_op_div_i32, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_div_i32, t0, arg1, arg2); tcg_gen_mul_i32(t0, t0, arg2); @@ -1969,7 +1969,7 @@ void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (TCG_TARGET_HAS_div_i64) { + if (tcg_op_supported(INDEX_op_div_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_div_i64, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div2_i64) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); @@ -1985,7 +1985,7 @@ void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_HAS_rem_i64) { tcg_gen_op3_i64(INDEX_op_rem_i64, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div_i64) { + } else if (tcg_op_supported(INDEX_op_div_i64, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_div_i64, t0, arg1, arg2); tcg_gen_mul_i64(t0, t0, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index d4b5872128..f99213a154 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1020,6 +1020,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), + OUTOP(INDEX_op_div_i32, TCGOutOpBinary, outop_divs), + OUTOP(INDEX_op_div_i64, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), @@ -2260,7 +2262,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i32: return TCG_TARGET_HAS_negsetcond_i32; - case INDEX_op_div_i32: case INDEX_op_divu_i32: return TCG_TARGET_HAS_div_i32; case INDEX_op_rem_i32: @@ -2323,7 +2324,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return TCG_TARGET_HAS_negsetcond_i64; - case INDEX_op_div_i64: case INDEX_op_divu_i64: return TCG_TARGET_HAS_div_i64; case INDEX_op_rem_i64: @@ -5417,6 +5417,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: + case INDEX_op_div_i32: + case INDEX_op_div_i64: case INDEX_op_eqv: case INDEX_op_mul: case INDEX_op_mulsh: diff --git a/tcg/tci.c b/tcg/tci.c index 61c0ccf21e..4ecbb2d335 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -578,7 +578,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, /* Arithmetic operations (32 bit). */ - case INDEX_op_div_i32: + case INDEX_op_tci_divs32: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int32_t)regs[r1] / (int32_t)regs[r2]; break; @@ -1101,6 +1101,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_clz_i64: case INDEX_op_ctz_i32: case INDEX_op_ctz_i64: + case INDEX_op_tci_divs32: tci_args_rrr(insn, &r0, &r1, &r2); info->fprintf_func(info->stream, "%-12s %s, %s, %s", op_name, str_r(r0), str_r(r1), str_r(r2)); diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc index ecc8c4e55e..f503374643 100644 --- a/tcg/tci/tcg-target-opc.h.inc +++ b/tcg/tci/tcg-target-opc.h.inc @@ -2,3 +2,4 @@ /* These opcodes for use between the tci generator and interpreter. */ DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT) +DEF(tci_divs32, 1, 2, 0, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 1dcce543ec..c8e86a3253 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -79,8 +79,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_div_i32: - case INDEX_op_div_i64: case INDEX_op_divu_i32: case INDEX_op_divu_i64: case INDEX_op_rem_i32: @@ -648,6 +646,20 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_divs(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_divs32 + : INDEX_op_div_i64); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_divs = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divs, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -811,7 +823,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, CASE_32_64(sar) CASE_32_64(rotl) /* Optional (TCG_TARGET_HAS_rot_*). */ CASE_32_64(rotr) /* Optional (TCG_TARGET_HAS_rot_*). */ - CASE_32_64(div) /* Optional (TCG_TARGET_HAS_div_*). */ CASE_32_64(divu) /* Optional (TCG_TARGET_HAS_div_*). */ CASE_32_64(rem) /* Optional (TCG_TARGET_HAS_div_*). */ CASE_32_64(remu) /* Optional (TCG_TARGET_HAS_div_*). */ From b2c514f9d5cab89814dc8a6b7c98c653ca8523d3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 13:22:56 -0800 Subject: [PATCH 0401/2760] tcg: Merge INDEX_op_div_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rename to INDEX_op_divs to emphasize signed inputs, and mirroring INDEX_op_divu_*. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 12 +++++++----- tcg/tcg-op.c | 16 ++++++++-------- tcg/tcg.c | 6 ++---- tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 22 insertions(+), 24 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index fe922d1dac..a833b3b7b2 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -277,7 +277,7 @@ Arithmetic - | *t0* = *t1* * *t2* - * - div_i32/i64 *t0*, *t1*, *t2* + * - divs *t0*, *t1*, *t2* - | *t0* = *t1* / *t2* (signed) | Undefined behavior if division by zero or overflow. diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index a9d7938a52..6d4edd0b16 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -42,6 +42,7 @@ DEF(mov, 1, 1, 0, TCG_OPF_INT | TCG_OPF_NOT_PRESENT) DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) +DEF(divs, 1, 2, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(mul, 1, 2, 0, TCG_OPF_INT) DEF(mulsh, 1, 2, 0, TCG_OPF_INT) @@ -68,7 +69,6 @@ DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* arith */ -DEF(div_i32, 1, 2, 0, 0) DEF(divu_i32, 1, 2, 0, 0) DEF(rem_i32, 1, 2, 0, 0) DEF(remu_i32, 1, 2, 0, 0) @@ -116,7 +116,6 @@ DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* arith */ -DEF(div_i64, 1, 2, 0, 0) DEF(divu_i64, 1, 2, 0, 0) DEF(rem_i64, 1, 2, 0, 0) DEF(remu_i64, 1, 2, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index fd446fc47d..af9054be37 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -556,13 +556,15 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, muls64(&l64, &h64, x, y); return h64; - case INDEX_op_div_i32: + case INDEX_op_divs: /* Avoid crashing on divide by zero, otherwise undefined. */ - return (int32_t)x / ((int32_t)y ? : 1); + if (type == TCG_TYPE_I32) { + return (int32_t)x / ((int32_t)y ? : 1); + } + return (int64_t)x / ((int64_t)y ? : 1); + case INDEX_op_divu_i32: return (uint32_t)x / ((uint32_t)y ? : 1); - case INDEX_op_div_i64: - return (int64_t)x / ((int64_t)y ? : 1); case INDEX_op_divu_i64: return (uint64_t)x / ((uint64_t)y ? : 1); @@ -2905,7 +2907,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(deposit): done = fold_deposit(&ctx, op); break; - CASE_OP_32_64(div): + case INDEX_op_divs: CASE_OP_32_64(divu): done = fold_divide(&ctx, op); break; diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 69e50f968f..9dba520d40 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -601,8 +601,8 @@ void tcg_gen_muli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_div_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3_i32(INDEX_op_div_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_divs, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_divs, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div2_i32) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_sari_i32(t0, arg1, 31); @@ -617,9 +617,9 @@ void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { if (TCG_TARGET_HAS_rem_i32) { tcg_gen_op3_i32(INDEX_op_rem_i32, ret, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_div_i32, TCG_TYPE_I32, 0)) { + } else if (tcg_op_supported(INDEX_op_divs, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); - tcg_gen_op3_i32(INDEX_op_div_i32, t0, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_divs, t0, arg1, arg2); tcg_gen_mul_i32(t0, t0, arg2); tcg_gen_sub_i32(ret, arg1, t0); tcg_temp_free_i32(t0); @@ -1969,8 +1969,8 @@ void tcg_gen_muli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (tcg_op_supported(INDEX_op_div_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3_i64(INDEX_op_div_i64, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_divs, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_divs, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div2_i64) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_sari_i64(t0, arg1, 63); @@ -1985,9 +1985,9 @@ void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_HAS_rem_i64) { tcg_gen_op3_i64(INDEX_op_rem_i64, ret, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_div_i64, TCG_TYPE_I64, 0)) { + } else if (tcg_op_supported(INDEX_op_divs, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - tcg_gen_op3_i64(INDEX_op_div_i64, t0, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_divs, t0, arg1, arg2); tcg_gen_mul_i64(t0, t0, arg2); tcg_gen_sub_i64(ret, arg1, t0); tcg_temp_free_i64(t0); diff --git a/tcg/tcg.c b/tcg/tcg.c index f99213a154..d4e30d0b33 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1020,8 +1020,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), - OUTOP(INDEX_op_div_i32, TCGOutOpBinary, outop_divs), - OUTOP(INDEX_op_div_i64, TCGOutOpBinary, outop_divs), + OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), @@ -5417,8 +5416,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: - case INDEX_op_div_i32: - case INDEX_op_div_i64: + case INDEX_op_divs: case INDEX_op_eqv: case INDEX_op_mul: case INDEX_op_mulsh: diff --git a/tcg/tci.c b/tcg/tci.c index 4ecbb2d335..4b3ca53bc5 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -720,7 +720,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, /* Arithmetic operations (64 bit). */ - case INDEX_op_div_i64: + case INDEX_op_divs: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int64_t)regs[r1] / (int64_t)regs[r2]; break; @@ -1071,6 +1071,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: + case INDEX_op_divs: case INDEX_op_eqv: case INDEX_op_mul: case INDEX_op_nand: @@ -1079,8 +1080,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_orc: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_div_i32: - case INDEX_op_div_i64: case INDEX_op_rem_i32: case INDEX_op_rem_i64: case INDEX_op_divu_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index c8e86a3253..4a556e2ce7 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -651,7 +651,7 @@ static void tgen_divs(TCGContext *s, TCGType type, { TCGOpcode opc = (type == TCG_TYPE_I32 ? INDEX_op_tci_divs32 - : INDEX_op_div_i64); + : INDEX_op_divs); tcg_out_op_rrr(s, opc, a0, a1, a2); } From 6d1a2365eaee0603347fd2fabd89a8dc935c8ac7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 14:10:27 -0800 Subject: [PATCH 0402/2760] tcg: Convert divu to TCGOutOpBinary For TCI, we're losing type information in the interpreter. Introduce a tci-specific opcode to handle the difference. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 18 ++++++---- tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 26 +++++++------- tcg/i386/tcg-target.c.inc | 4 +++ tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 24 ++++++++----- tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 37 ++++++++++--------- tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 21 ++++++----- tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 21 ++++++----- tcg/s390x/tcg-target.c.inc | 4 +++ tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 61 +++++++++++++++++--------------- tcg/tcg-has.h | 15 ++++---- tcg/tcg-op.c | 8 ++--- tcg/tcg.c | 8 ++--- tcg/tci.c | 3 +- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target-opc.h.inc | 1 + tcg/tci/tcg-target.c.inc | 17 +++++++-- 23 files changed, 157 insertions(+), 126 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index bde6db8f2a..e961668ef0 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -13,7 +13,6 @@ #define have_lse2 (cpuinfo & CPUINFO_LSE2) /* optional instructions */ -#define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 @@ -30,7 +29,6 @@ #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 52069f1445..167c51c897 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2168,6 +2168,17 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, UDIV, type, a0, a1, a2); +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divu, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2373,11 +2384,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_divu_i64: - case INDEX_op_divu_i32: - tcg_out_insn(s, 3508, UDIV, ext, a0, a1, a2); - break; - case INDEX_op_rem_i64: case INDEX_op_rem_i32: tcg_out_insn(s, 3508, SDIV, ext, TCG_REG_TMP0, a1, a2); @@ -3064,8 +3070,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rC); - case INDEX_op_divu_i32: - case INDEX_op_divu_i64: case INDEX_op_rem_i32: case INDEX_op_rem_i64: case INDEX_op_remu_i32: diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index ab9b7b6162..6ed2b49c84 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -34,7 +34,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_div_i32 use_idiv_instructions #define TCG_TARGET_HAS_rem_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index e07e4c06d9..65d0ae83b2 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -937,12 +937,6 @@ static void tcg_out_smull32(TCGContext *s, ARMCond cond, TCGReg rd0, (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); } -static void tcg_out_udiv(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, TCGReg rm) -{ - tcg_out32(s, 0x0730f010 | (cond << 28) | (rd << 16) | rn | (rm << 8)); -} - static void tcg_out_ext8s(TCGContext *s, TCGType t, TCGReg rd, TCGReg rn) { /* sxtb */ @@ -1886,6 +1880,19 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + /* udiv */ + tcg_out32(s, 0x0730f010 | (COND_AL << 28) | (a0 << 16) | a1 | (a2 << 8)); +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_idiv, + .out_rrr = tgen_divu, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; @@ -2230,10 +2237,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_divu_i32: - tcg_out_udiv(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_mb: tcg_out_mb(s, args[0]); break; @@ -2277,9 +2280,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ctz_i32: return C_O1_I2(r, r, rIK); - case INDEX_op_divu_i32: - return C_O1_I2(r, r, r); - case INDEX_op_mulu2_i32: case INDEX_op_muls2_i32: return C_O2_I2(r, r, r, r); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index e132dd0c88..f258d6383b 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2637,6 +2637,10 @@ static const TCGOutOpBinary outop_divs = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index e29c892756..96a99b6d4c 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -11,7 +11,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_negsetcond_i32 0 -#define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_div2_i32 0 #define TCG_TARGET_HAS_rot_i32 1 @@ -29,7 +28,6 @@ /* 64-bit operations */ #define TCG_TARGET_HAS_negsetcond_i64 0 -#define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_div2_i64 0 #define TCG_TARGET_HAS_rot_i64 1 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index c42d8d690a..e82a62d09e 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1343,6 +1343,21 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_div_wu(s, a0, a1, a2); + } else { + tcg_out_opc_div_du(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divu, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; @@ -1686,13 +1701,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_divu_i32: - tcg_out_opc_div_wu(s, a0, a1, a2); - break; - case INDEX_op_divu_i64: - tcg_out_opc_div_du(s, a0, a1, a2); - break; - case INDEX_op_rem_i32: tcg_out_opc_mod_w(s, a0, a1, a2); break; @@ -2365,8 +2373,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i64: return C_O1_I2(r, rz, rJ); - case INDEX_op_divu_i32: - case INDEX_op_divu_i64: case INDEX_op_rem_i32: case INDEX_op_rem_i64: case INDEX_op_remu_i32: diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index ebaaa49cdd..9aa5bf9f1b 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -39,7 +39,6 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) @@ -51,7 +50,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 7762d88e6b..ab9546f104 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1733,6 +1733,27 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + if (type == TCG_TYPE_I32) { + tcg_out_opc_reg(s, OPC_DIVU_R6, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_DDIVU_R6, a0, a1, a2); + } + } else { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_DIVU : OPC_DDIVU; + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); + } +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divu, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; @@ -1960,13 +1981,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_divu_i32: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DIVU_R6, a0, a1, a2); - break; - } - i1 = OPC_DIVU, i2 = OPC_MFLO; - goto do_hilo1; case INDEX_op_rem_i32: if (use_mips32r6_instructions) { tcg_out_opc_reg(s, OPC_MOD, a0, a1, a2); @@ -1981,13 +1995,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } i1 = OPC_DIVU, i2 = OPC_MFHI; goto do_hilo1; - case INDEX_op_divu_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DDIVU_R6, a0, a1, a2); - break; - } - i1 = OPC_DDIVU, i2 = OPC_MFLO; - goto do_hilo1; case INDEX_op_rem_i64: if (use_mips32r6_instructions) { tcg_out_opc_reg(s, OPC_DMOD, a0, a1, a2); @@ -2260,11 +2267,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: case INDEX_op_setcond_i32: - case INDEX_op_divu_i64: case INDEX_op_rem_i64: case INDEX_op_remu_i64: case INDEX_op_setcond_i64: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index bbbd8de2c7..f8e4c0ad3c 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -17,7 +17,6 @@ #define have_vsx (cpuinfo & CPUINFO_VSX) /* optional instructions */ -#define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 have_isa_3_00 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 @@ -35,7 +34,6 @@ #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 have_isa_3_00 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 9fdf8df082..b347595131 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2972,6 +2972,18 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? DIVWU : DIVDU; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divu, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_O1_I2(r, r, r), .out_rrr = tgen_eqv, @@ -3221,10 +3233,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, CNTPOPD | SAB(args[1], args[0], 0)); break; - case INDEX_op_divu_i32: - tcg_out32(s, DIVWU | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_rem_i32: tcg_out32(s, MODSW | TAB(args[0], args[1], args[2])); break; @@ -3325,9 +3333,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_divu_i64: - tcg_out32(s, DIVDU | TAB(args[0], args[1], args[2])); - break; case INDEX_op_rem_i64: tcg_out32(s, MODSD | TAB(args[0], args[1], args[2])); break; @@ -4189,10 +4194,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rotr_i64: return C_O1_I2(r, r, ri); - case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_divu_i64: case INDEX_op_rem_i64: case INDEX_op_remu_i64: return C_O1_I2(r, r, r); diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index f7e1ef82fc..ae6624b9a4 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -11,7 +11,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_negsetcond_i32 1 -#define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_div2_i32 0 #define TCG_TARGET_HAS_rot_i32 (cpuinfo & CPUINFO_ZBB) @@ -28,7 +27,6 @@ #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_negsetcond_i64 1 -#define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_div2_i64 0 #define TCG_TARGET_HAS_rot_i64 (cpuinfo & CPUINFO_ZBB) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 15925729dc..74fa38d273 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2009,6 +2009,18 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_DIVUW : OPC_DIVU; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divu, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2213,13 +2225,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_divu_i32: - tcg_out_opc_reg(s, OPC_DIVUW, a0, a1, a2); - break; - case INDEX_op_divu_i64: - tcg_out_opc_reg(s, OPC_DIVU, a0, a1, a2); - break; - case INDEX_op_rem_i32: tcg_out_opc_reg(s, OPC_REMW, a0, a1, a2); break; @@ -2735,10 +2740,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_divu_i32: case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_divu_i64: case INDEX_op_rem_i64: case INDEX_op_remu_i64: return C_O1_I2(r, rz, rz); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index fd0e717c49..f55309f48e 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2246,6 +2246,10 @@ static const TCGOutOpBinary outop_divs = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 5a517b6835..35f0dd4230 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -14,7 +14,6 @@ extern bool use_vis3_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 0 #define TCG_TARGET_HAS_rot_i32 0 #define TCG_TARGET_HAS_bswap16_i32 0 @@ -31,7 +30,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 0 #define TCG_TARGET_HAS_rot_i64 0 #define TCG_TARGET_HAS_bswap16_i64 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 779d0ce882..3a3372d7aa 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -596,21 +596,6 @@ static void tcg_out_sety(TCGContext *s, TCGReg rs) tcg_out32(s, WRY | INSN_RS1(TCG_REG_G0) | INSN_RS2(rs)); } -static void tcg_out_div32(TCGContext *s, TCGReg rd, TCGReg rs1, - int32_t val2, int val2const, int uns) -{ - /* Load Y with the sign/zero extension of RS1 to 64-bits. */ - if (uns) { - tcg_out_sety(s, TCG_REG_G0); - } else { - tcg_out_arithi(s, TCG_REG_T1, rs1, 31, SHIFT_SRA); - tcg_out_sety(s, TCG_REG_T1); - } - - tcg_out_arithc(s, rd, rs1, val2, val2const, - uns ? ARITH_UDIV : ARITH_SDIV); -} - static const uint8_t tcg_cond_to_bcond[16] = { [TCG_COND_EQ] = COND_E, [TCG_COND_NE] = COND_NE, @@ -1367,6 +1352,39 @@ static const TCGOutOpBinary outop_divs = { .out_rri = tgen_divsi, }; +static void tgen_divu_rJ(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGArg a2, bool c2) +{ + uint32_t insn; + + if (type == TCG_TYPE_I32) { + /* Load Y with the zero extension to 64-bits. */ + tcg_out_sety(s, TCG_REG_G0); + insn = ARITH_UDIV; + } else { + insn = ARITH_UDIVX; + } + tcg_out_arithc(s, a0, a1, a2, c2, insn); +} + +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_divu_rJ(s, type, a0, a1, a2, false); +} + +static void tgen_divui(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_divu_rJ(s, type, a0, a1, a2, true); +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_divu, + .out_rri = tgen_divui, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; @@ -1566,10 +1584,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, c = SHIFT_SRA; goto do_shift32; - case INDEX_op_divu_i32: - tcg_out_div32(s, a0, a1, a2, c2, 1); - break; - case INDEX_op_brcond_i32: tcg_out_brcond_i32(s, a2, a0, a1, const_args[1], arg_label(args[3])); break; @@ -1638,9 +1652,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_sar_i64: c = SHIFT_SRAX; goto do_shift64; - case INDEX_op_divu_i64: - c = ARITH_UDIVX; - goto gen_arith; case INDEX_op_brcond_i64: tcg_out_brcond_i64(s, a2, a0, a1, const_args[1], arg_label(args[3])); @@ -1663,10 +1674,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const_args[4], args[5], const_args[5], true); break; - gen_arith: - tcg_out_arithc(s, a0, a1, a2, c2, c); - break; - case INDEX_op_mb: tcg_out_mb(s, a0); break; @@ -1728,8 +1735,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); - case INDEX_op_divu_i32: - case INDEX_op_divu_i64: case INDEX_op_shl_i32: case INDEX_op_shl_i64: case INDEX_op_shr_i32: diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 3d4c67698f..9680ccfc53 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -33,17 +33,16 @@ #define TCG_TARGET_HAS_sub2_i32 1 #endif -/* Only one of DIV or DIV2 should be defined. */ -#if defined(TCG_TARGET_HAS_div_i32) +#ifndef TCG_TARGET_HAS_div2_i32 #define TCG_TARGET_HAS_div2_i32 0 -#elif defined(TCG_TARGET_HAS_div2_i32) -#define TCG_TARGET_HAS_div_i32 0 -#define TCG_TARGET_HAS_rem_i32 0 #endif -#if defined(TCG_TARGET_HAS_div_i64) +#ifndef TCG_TARGET_HAS_div2_i64 #define TCG_TARGET_HAS_div2_i64 0 -#elif defined(TCG_TARGET_HAS_div2_i64) -#define TCG_TARGET_HAS_div_i64 0 +#endif +#ifndef TCG_TARGET_HAS_rem_i32 +#define TCG_TARGET_HAS_rem_i32 0 +#endif +#ifndef TCG_TARGET_HAS_rem_i64 #define TCG_TARGET_HAS_rem_i64 0 #endif diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 9dba520d40..19be461214 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -635,7 +635,7 @@ void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_div_i32) { + if (tcg_op_supported(INDEX_op_divu_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_divu_i32, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div2_i32) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); @@ -651,7 +651,7 @@ void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { if (TCG_TARGET_HAS_rem_i32) { tcg_gen_op3_i32(INDEX_op_remu_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div_i32) { + } else if (tcg_op_supported(INDEX_op_divu_i32, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_divu_i32, t0, arg1, arg2); tcg_gen_mul_i32(t0, t0, arg2); @@ -2003,7 +2003,7 @@ void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (TCG_TARGET_HAS_div_i64) { + if (tcg_op_supported(INDEX_op_divu_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_divu_i64, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div2_i64) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); @@ -2019,7 +2019,7 @@ void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_HAS_rem_i64) { tcg_gen_op3_i64(INDEX_op_remu_i64, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div_i64) { + } else if (tcg_op_supported(INDEX_op_divu_i64, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_divu_i64, t0, arg1, arg2); tcg_gen_mul_i64(t0, t0, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index d4e30d0b33..a0e58c07d7 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1021,6 +1021,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), + OUTOP(INDEX_op_divu_i32, TCGOutOpBinary, outop_divu), + OUTOP(INDEX_op_divu_i64, TCGOutOpBinary, outop_divu), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), @@ -2261,8 +2263,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i32: return TCG_TARGET_HAS_negsetcond_i32; - case INDEX_op_divu_i32: - return TCG_TARGET_HAS_div_i32; case INDEX_op_rem_i32: case INDEX_op_remu_i32: return TCG_TARGET_HAS_rem_i32; @@ -2323,8 +2323,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return TCG_TARGET_HAS_negsetcond_i64; - case INDEX_op_divu_i64: - return TCG_TARGET_HAS_div_i64; case INDEX_op_rem_i64: case INDEX_op_remu_i64: return TCG_TARGET_HAS_rem_i64; @@ -5417,6 +5415,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_divs: + case INDEX_op_divu_i32: + case INDEX_op_divu_i64: case INDEX_op_eqv: case INDEX_op_mul: case INDEX_op_mulsh: diff --git a/tcg/tci.c b/tcg/tci.c index 4b3ca53bc5..0691824f97 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -582,7 +582,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int32_t)regs[r1] / (int32_t)regs[r2]; break; - case INDEX_op_divu_i32: + case INDEX_op_tci_divu32: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint32_t)regs[r1] / (uint32_t)regs[r2]; break; @@ -1101,6 +1101,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_ctz_i32: case INDEX_op_ctz_i64: case INDEX_op_tci_divs32: + case INDEX_op_tci_divu32: tci_args_rrr(insn, &r0, &r1, &r2); info->fprintf_func(info->stream, "%-12s %s, %s, %s", op_name, str_r(r0), str_r(r1), str_r(r2)); diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 0627585097..ccec96b610 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -9,7 +9,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_div_i32 1 #define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_clz_i32 1 @@ -26,7 +25,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_div_i64 1 #define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc index f503374643..43c07a269f 100644 --- a/tcg/tci/tcg-target-opc.h.inc +++ b/tcg/tci/tcg-target-opc.h.inc @@ -3,3 +3,4 @@ DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(tci_divs32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_divu32, 1, 2, 0, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 4a556e2ce7..18a10156a6 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -79,8 +79,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_divu_i32: - case INDEX_op_divu_i64: case INDEX_op_rem_i32: case INDEX_op_rem_i64: case INDEX_op_remu_i32: @@ -660,6 +658,20 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static void tgen_divu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_divu32 + : INDEX_op_divu_i64); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_divu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_divu, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -823,7 +835,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, CASE_32_64(sar) CASE_32_64(rotl) /* Optional (TCG_TARGET_HAS_rot_*). */ CASE_32_64(rotr) /* Optional (TCG_TARGET_HAS_rot_*). */ - CASE_32_64(divu) /* Optional (TCG_TARGET_HAS_div_*). */ CASE_32_64(rem) /* Optional (TCG_TARGET_HAS_div_*). */ CASE_32_64(remu) /* Optional (TCG_TARGET_HAS_div_*). */ CASE_32_64(clz) /* Optional (TCG_TARGET_HAS_clz_*). */ From 961b80aecd1a503eedb885c309a1d5267d89c98c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 14:27:19 -0800 Subject: [PATCH 0403/2760] tcg: Merge INDEX_op_divu_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 9 +++++---- tcg/tcg-op.c | 16 ++++++++-------- tcg/tcg.c | 6 ++---- tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 20 insertions(+), 23 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index a833b3b7b2..41985be012 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -282,7 +282,7 @@ Arithmetic - | *t0* = *t1* / *t2* (signed) | Undefined behavior if division by zero or overflow. - * - divu_i32/i64 *t0*, *t1*, *t2* + * - divu *t0*, *t1*, *t2* - | *t0* = *t1* / *t2* (unsigned) | Undefined behavior if division by zero. diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 6d4edd0b16..243f002a61 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -43,6 +43,7 @@ DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(divs, 1, 2, 0, TCG_OPF_INT) +DEF(divu, 1, 2, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(mul, 1, 2, 0, TCG_OPF_INT) DEF(mulsh, 1, 2, 0, TCG_OPF_INT) @@ -69,7 +70,6 @@ DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* arith */ -DEF(divu_i32, 1, 2, 0, 0) DEF(rem_i32, 1, 2, 0, 0) DEF(remu_i32, 1, 2, 0, 0) DEF(div2_i32, 2, 3, 0, 0) @@ -116,7 +116,6 @@ DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* arith */ -DEF(divu_i64, 1, 2, 0, 0) DEF(rem_i64, 1, 2, 0, 0) DEF(remu_i64, 1, 2, 0, 0) DEF(div2_i64, 2, 3, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index af9054be37..c11cce782a 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -563,9 +563,10 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, } return (int64_t)x / ((int64_t)y ? : 1); - case INDEX_op_divu_i32: - return (uint32_t)x / ((uint32_t)y ? : 1); - case INDEX_op_divu_i64: + case INDEX_op_divu: + if (type == TCG_TYPE_I32) { + return (uint32_t)x / ((uint32_t)y ? : 1); + } return (uint64_t)x / ((uint64_t)y ? : 1); case INDEX_op_rem_i32: @@ -2908,7 +2909,7 @@ void tcg_optimize(TCGContext *s) done = fold_deposit(&ctx, op); break; case INDEX_op_divs: - CASE_OP_32_64(divu): + case INDEX_op_divu: done = fold_divide(&ctx, op); break; case INDEX_op_dup_vec: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 19be461214..f326c452a4 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -635,8 +635,8 @@ void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_divu_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3_i32(INDEX_op_divu_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_divu, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div2_i32) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 zero = tcg_constant_i32(0); @@ -651,9 +651,9 @@ void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { if (TCG_TARGET_HAS_rem_i32) { tcg_gen_op3_i32(INDEX_op_remu_i32, ret, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_divu_i32, TCG_TYPE_I32, 0)) { + } else if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); - tcg_gen_op3_i32(INDEX_op_divu_i32, t0, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_divu, t0, arg1, arg2); tcg_gen_mul_i32(t0, t0, arg2); tcg_gen_sub_i32(ret, arg1, t0); tcg_temp_free_i32(t0); @@ -2003,8 +2003,8 @@ void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (tcg_op_supported(INDEX_op_divu_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3_i64(INDEX_op_divu_i64, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_divu, ret, arg1, arg2); } else if (TCG_TARGET_HAS_div2_i64) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 zero = tcg_constant_i64(0); @@ -2019,9 +2019,9 @@ void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_HAS_rem_i64) { tcg_gen_op3_i64(INDEX_op_remu_i64, ret, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_divu_i64, TCG_TYPE_I64, 0)) { + } else if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - tcg_gen_op3_i64(INDEX_op_divu_i64, t0, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_divu, t0, arg1, arg2); tcg_gen_mul_i64(t0, t0, arg2); tcg_gen_sub_i64(ret, arg1, t0); tcg_temp_free_i64(t0); diff --git a/tcg/tcg.c b/tcg/tcg.c index a0e58c07d7..9aa6d40905 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1021,8 +1021,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), - OUTOP(INDEX_op_divu_i32, TCGOutOpBinary, outop_divu), - OUTOP(INDEX_op_divu_i64, TCGOutOpBinary, outop_divu), + OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), @@ -5415,8 +5414,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_divs: - case INDEX_op_divu_i32: - case INDEX_op_divu_i64: + case INDEX_op_divu: case INDEX_op_eqv: case INDEX_op_mul: case INDEX_op_mulsh: diff --git a/tcg/tci.c b/tcg/tci.c index 0691824f97..bf97849bfe 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -724,7 +724,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int64_t)regs[r1] / (int64_t)regs[r2]; break; - case INDEX_op_divu_i64: + case INDEX_op_divu: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint64_t)regs[r1] / (uint64_t)regs[r2]; break; @@ -1072,6 +1072,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_divs: + case INDEX_op_divu: case INDEX_op_eqv: case INDEX_op_mul: case INDEX_op_nand: @@ -1082,8 +1083,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_xor: case INDEX_op_rem_i32: case INDEX_op_rem_i64: - case INDEX_op_divu_i32: - case INDEX_op_divu_i64: case INDEX_op_remu_i32: case INDEX_op_remu_i64: case INDEX_op_shl_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 18a10156a6..dfa8aecc7a 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -663,7 +663,7 @@ static void tgen_divu(TCGContext *s, TCGType type, { TCGOpcode opc = (type == TCG_TYPE_I32 ? INDEX_op_tci_divu32 - : INDEX_op_divu_i64); + : INDEX_op_divu); tcg_out_op_rrr(s, opc, a0, a1, a2); } From d6cad9c9278566deb7f646f027c056ba24f7988d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 16:32:29 -0800 Subject: [PATCH 0404/2760] tcg: Convert div2 to TCGOutOpDivRem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 4 +++ tcg/arm/tcg-target.c.inc | 4 +++ tcg/i386/tcg-target.c.inc | 17 ++++++++---- tcg/loongarch64/tcg-target.c.inc | 4 +++ tcg/mips/tcg-target.c.inc | 4 +++ tcg/ppc/tcg-target.c.inc | 4 +++ tcg/riscv/tcg-target.c.inc | 4 +++ tcg/s390x/tcg-target.c.inc | 44 ++++++++++++++++---------------- tcg/sparc64/tcg-target.c.inc | 4 +++ tcg/tcg.c | 24 +++++++++++++++-- tcg/tci/tcg-target.c.inc | 4 +++ 11 files changed, 88 insertions(+), 29 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 167c51c897..ea5766414d 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2168,6 +2168,10 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_divu(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 65d0ae83b2..ff750e2df8 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1880,6 +1880,10 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_divu(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index f258d6383b..9238e0e8e4 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2637,6 +2637,18 @@ static const TCGOutOpBinary outop_divs = { .base.static_constraint = C_NotImplemented, }; +static void tgen_divs2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a4) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IDIV, a4); +} + +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_O2_I3(a, d, 0, 1, r), + .out_rr01r = tgen_divs2, +}; + static const TCGOutOpBinary outop_divu = { .base.static_constraint = C_NotImplemented, }; @@ -2847,9 +2859,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(div2): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IDIV, args[4]); - break; OP_32_64(divu2): tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_DIV, args[4]); break; @@ -3789,8 +3798,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_movcond_i64: return C_O1_I4(r, r, reT, r, 0); - case INDEX_op_div2_i32: - case INDEX_op_div2_i64: case INDEX_op_divu2_i32: case INDEX_op_divu2_i64: return C_O2_I3(a, d, 0, 1, r); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index e82a62d09e..8ec46114b8 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1343,6 +1343,10 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_divu(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index ab9546f104..adbc7ee39d 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1733,6 +1733,10 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_divu(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index b347595131..1eb3e785c0 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2972,6 +2972,10 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_divu(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 74fa38d273..19c690c1c2 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2009,6 +2009,10 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_divu(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index f55309f48e..b434ce423a 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2246,6 +2246,28 @@ static const TCGOutOpBinary outop_divs = { .base.static_constraint = C_NotImplemented, }; +static void tgen_divs2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a4) +{ + tcg_debug_assert((a1 & 1) == 0); + tcg_debug_assert(a0 == a1 + 1); + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RR, DR, a1, a4); + } else { + /* + * TODO: Move the sign-extend of the numerator from a2 into a3 + * into the tcg backend, instead of in early expansion. It is + * required for 32-bit DR, but not 64-bit DSGR. + */ + tcg_out_insn(s, RRE, DSGR, a1, a4); + } +} + +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_O2_I3(o, m, 0, 1, r), + .out_rr01r = tgen_divs2, +}; + static const TCGOutOpBinary outop_divu = { .base.static_constraint = C_NotImplemented, }; @@ -2527,13 +2549,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_div2_i32: - tcg_debug_assert(args[0] == args[2]); - tcg_debug_assert(args[1] == args[3]); - tcg_debug_assert((args[1] & 1) == 0); - tcg_debug_assert(args[0] == args[1] + 1); - tcg_out_insn(s, RR, DR, args[1], args[4]); - break; case INDEX_op_divu2_i32: tcg_debug_assert(args[0] == args[2]); tcg_debug_assert(args[1] == args[3]); @@ -2702,19 +2717,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_insn(s, RRE, LRVGR, args[0], args[1]); break; - case INDEX_op_div2_i64: - /* - * ??? We get an unnecessary sign-extension of the dividend - * into op0 with this definition, but as we do in fact always - * produce both quotient and remainder using INDEX_op_div_i64 - * instead requires jumping through even more hoops. - */ - tcg_debug_assert(args[0] == args[2]); - tcg_debug_assert(args[1] == args[3]); - tcg_debug_assert((args[1] & 1) == 0); - tcg_debug_assert(args[0] == args[1] + 1); - tcg_out_insn(s, RRE, DSGR, args[1], args[4]); - break; case INDEX_op_divu2_i64: tcg_debug_assert(args[0] == args[2]); tcg_debug_assert(args[1] == args[3]); @@ -3396,8 +3398,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_movcond_i64: return C_O1_I4(r, r, rC, rI, r); - case INDEX_op_div2_i32: - case INDEX_op_div2_i64: case INDEX_op_divu2_i32: case INDEX_op_divu2_i64: return C_O2_I3(o, m, 0, 1, r); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 3a3372d7aa..472ccd7608 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1352,6 +1352,10 @@ static const TCGOutOpBinary outop_divs = { .out_rri = tgen_divsi, }; +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_divu_rJ(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGArg a2, bool c2) { diff --git a/tcg/tcg.c b/tcg/tcg.c index 9aa6d40905..ef3af4157a 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -986,6 +986,12 @@ typedef struct TCGOutOpBinary { TCGReg a0, TCGReg a1, tcg_target_long a2); } TCGOutOpBinary; +typedef struct TCGOutOpDivRem { + TCGOutOp base; + void (*out_rr01r)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a4); +} TCGOutOpDivRem; + typedef struct TCGOutOpUnary { TCGOutOp base; void (*out_rr)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1); @@ -1022,6 +1028,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), + OUTOP(INDEX_op_div2_i32, TCGOutOpDivRem, outop_divs2), + OUTOP(INDEX_op_div2_i64, TCGOutOpDivRem, outop_divs2), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), @@ -2265,7 +2273,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rem_i32: case INDEX_op_remu_i32: return TCG_TARGET_HAS_rem_i32; - case INDEX_op_div2_i32: case INDEX_op_divu2_i32: return TCG_TARGET_HAS_div2_i32; case INDEX_op_rotl_i32: @@ -2325,7 +2332,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rem_i64: case INDEX_op_remu_i64: return TCG_TARGET_HAS_rem_i64; - case INDEX_op_div2_i64: case INDEX_op_divu2_i64: return TCG_TARGET_HAS_div2_i64; case INDEX_op_rotl_i64: @@ -5467,6 +5473,20 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_div2_i32: + case INDEX_op_div2_i64: + { + const TCGOutOpDivRem *out = + container_of(all_outop[op->opc], TCGOutOpDivRem, base); + + /* Only used by x86 and s390x, which use matching constraints. */ + tcg_debug_assert(new_args[0] == new_args[2]); + tcg_debug_assert(new_args[1] == new_args[3]); + tcg_debug_assert(!const_args[4]); + out->out_rr01r(s, type, new_args[0], new_args[1], new_args[4]); + } + break; + default: if (def->flags & TCG_OPF_VECTOR) { tcg_out_vec_op(s, op->opc, type - TCG_TYPE_V64, diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index dfa8aecc7a..6646be224d 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -658,6 +658,10 @@ static const TCGOutOpBinary outop_divs = { .out_rrr = tgen_divs, }; +static const TCGOutOpDivRem outop_divs2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_divu(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { From ee1805b9e66d6b6229270a339586058bbf275412 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 16:44:23 -0800 Subject: [PATCH 0405/2760] tcg: Merge INDEX_op_div2_{i32,i64} Rename to INDEX_op_divs2 to emphasize signed inputs, and mirroring INDEX_op_divu2_*. Document the opcode. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 9 +++++++++ include/tcg/tcg-opc.h | 3 +-- tcg/tcg-op.c | 16 ++++++++-------- tcg/tcg.c | 6 ++---- 4 files changed, 20 insertions(+), 14 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 41985be012..62af390854 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -297,6 +297,15 @@ Arithmetic - | *t0* = *t1* % *t2* (unsigned) | Undefined behavior if division by zero. + * - divs2 *q*, *r*, *nl*, *nh*, *d* + + - | *q* = *nh:nl* / *d* (signed) + | *r* = *nh:nl* % *d* + | Undefined behaviour if division by zero, or the double-word + numerator divided by the single-word divisor does not fit + within the single-word quotient. The code generator will + pass *nh* as a simple sign-extension of *nl*, so the only + overflow should be *INT_MIN* / -1. Logical ------- diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 243f002a61..36dfbf80ad 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -43,6 +43,7 @@ DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(divs, 1, 2, 0, TCG_OPF_INT) +DEF(divs2, 2, 3, 0, TCG_OPF_INT) DEF(divu, 1, 2, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(mul, 1, 2, 0, TCG_OPF_INT) @@ -72,7 +73,6 @@ DEF(st_i32, 0, 2, 1, 0) /* arith */ DEF(rem_i32, 1, 2, 0, 0) DEF(remu_i32, 1, 2, 0, 0) -DEF(div2_i32, 2, 3, 0, 0) DEF(divu2_i32, 2, 3, 0, 0) /* shifts/rotates */ DEF(shl_i32, 1, 2, 0, 0) @@ -118,7 +118,6 @@ DEF(st_i64, 0, 2, 1, 0) /* arith */ DEF(rem_i64, 1, 2, 0, 0) DEF(remu_i64, 1, 2, 0, 0) -DEF(div2_i64, 2, 3, 0, 0) DEF(divu2_i64, 2, 3, 0, 0) /* shifts/rotates */ DEF(shl_i64, 1, 2, 0, 0) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index f326c452a4..f95beb8b5d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -603,10 +603,10 @@ void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { if (tcg_op_supported(INDEX_op_divs, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_divs, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div2_i32) { + } else if (tcg_op_supported(INDEX_op_divs2, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_sari_i32(t0, arg1, 31); - tcg_gen_op5_i32(INDEX_op_div2_i32, ret, t0, arg1, t0, arg2); + tcg_gen_op5_i32(INDEX_op_divs2, ret, t0, arg1, t0, arg2); tcg_temp_free_i32(t0); } else { gen_helper_div_i32(ret, arg1, arg2); @@ -623,10 +623,10 @@ void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_mul_i32(t0, t0, arg2); tcg_gen_sub_i32(ret, arg1, t0); tcg_temp_free_i32(t0); - } else if (TCG_TARGET_HAS_div2_i32) { + } else if (tcg_op_supported(INDEX_op_divs2, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_sari_i32(t0, arg1, 31); - tcg_gen_op5_i32(INDEX_op_div2_i32, t0, ret, arg1, t0, arg2); + tcg_gen_op5_i32(INDEX_op_divs2, t0, ret, arg1, t0, arg2); tcg_temp_free_i32(t0); } else { gen_helper_rem_i32(ret, arg1, arg2); @@ -1971,10 +1971,10 @@ void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (tcg_op_supported(INDEX_op_divs, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_divs, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div2_i64) { + } else if (tcg_op_supported(INDEX_op_divs2, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_sari_i64(t0, arg1, 63); - tcg_gen_op5_i64(INDEX_op_div2_i64, ret, t0, arg1, t0, arg2); + tcg_gen_op5_i64(INDEX_op_divs2, ret, t0, arg1, t0, arg2); tcg_temp_free_i64(t0); } else { gen_helper_div_i64(ret, arg1, arg2); @@ -1991,10 +1991,10 @@ void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_mul_i64(t0, t0, arg2); tcg_gen_sub_i64(ret, arg1, t0); tcg_temp_free_i64(t0); - } else if (TCG_TARGET_HAS_div2_i64) { + } else if (tcg_op_supported(INDEX_op_divs2, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_sari_i64(t0, arg1, 63); - tcg_gen_op5_i64(INDEX_op_div2_i64, t0, ret, arg1, t0, arg2); + tcg_gen_op5_i64(INDEX_op_divs2, t0, ret, arg1, t0, arg2); tcg_temp_free_i64(t0); } else { gen_helper_rem_i64(ret, arg1, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index ef3af4157a..30b7f8ee19 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1028,8 +1028,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), - OUTOP(INDEX_op_div2_i32, TCGOutOpDivRem, outop_divs2), - OUTOP(INDEX_op_div2_i64, TCGOutOpDivRem, outop_divs2), + OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), @@ -5473,8 +5472,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_div2_i32: - case INDEX_op_div2_i64: + case INDEX_op_divs2: { const TCGOutOpDivRem *out = container_of(all_outop[op->opc], TCGOutOpDivRem, base); From 9bf558ed17c274b172549894e8e343e6a1a1508c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 18:10:14 -0800 Subject: [PATCH 0406/2760] tcg: Convert divu2 to TCGOutOpDivRem Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 4 ++++ tcg/arm/tcg-target.c.inc | 4 ++++ tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 20 +++++++++++------- tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 4 ++++ tcg/mips/tcg-target.c.inc | 4 ++++ tcg/ppc/tcg-target.c.inc | 4 ++++ tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 4 ++++ tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 36 +++++++++++++++----------------- tcg/sparc64/tcg-target.c.inc | 4 ++++ tcg/tcg-has.h | 7 ------- tcg/tcg-op.c | 8 +++---- tcg/tcg.c | 8 +++---- tcg/tci/tcg-target.c.inc | 4 ++++ 17 files changed, 69 insertions(+), 50 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index ea5766414d..456159cdc6 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2183,6 +2183,10 @@ static const TCGOutOpBinary outop_divu = { .out_rrr = tgen_divu, }; +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index ff750e2df8..b2c08bba3e 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1897,6 +1897,10 @@ static const TCGOutOpBinary outop_divu = { .out_rrr = tgen_divu, }; +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index 121fb95ee0..aee6066579 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -26,7 +26,6 @@ #define have_avx512vbmi2 ((cpuinfo & CPUINFO_AVX512VBMI2) && have_avx512vl) /* optional instructions */ -#define TCG_TARGET_HAS_div2_i32 1 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 @@ -43,7 +42,6 @@ #if TCG_TARGET_REG_BITS == 64 /* Keep 32-bit values zero-extended in a register. */ #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_div2_i64 1 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 9238e0e8e4..0e6b743fb2 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2653,6 +2653,18 @@ static const TCGOutOpBinary outop_divu = { .base.static_constraint = C_NotImplemented, }; +static void tgen_divu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a4) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_DIV, a4); +} + +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_O2_I3(a, d, 0, 1, r), + .out_rr01r = tgen_divu2, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; @@ -2859,10 +2871,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(divu2): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_DIV, args[4]); - break; - OP_32_64(shl): /* For small constant 3-operand shift, use LEA. */ if (const_a2 && a0 != a1 && (a2 - 1) < 3) { @@ -3798,10 +3806,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_movcond_i64: return C_O1_I4(r, r, reT, r, 0); - case INDEX_op_divu2_i32: - case INDEX_op_divu2_i64: - return C_O2_I3(a, d, 0, 1, r); - case INDEX_op_mulu2_i32: case INDEX_op_mulu2_i64: case INDEX_op_muls2_i32: diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 96a99b6d4c..aecd2879b8 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -12,7 +12,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_rem_i32 1 -#define TCG_TARGET_HAS_div2_i32 0 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 0 @@ -29,7 +28,6 @@ /* 64-bit operations */ #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_div2_i64 0 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 8ec46114b8..be09c362cb 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1362,6 +1362,10 @@ static const TCGOutOpBinary outop_divu = { .out_rrr = tgen_divu, }; +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index adbc7ee39d..280afbf297 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1758,6 +1758,10 @@ static const TCGOutOpBinary outop_divu = { .out_rrr = tgen_divu, }; +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 1eb3e785c0..8b14d57d1c 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2988,6 +2988,10 @@ static const TCGOutOpBinary outop_divu = { .out_rrr = tgen_divu, }; +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_O1_I2(r, r, r), .out_rrr = tgen_eqv, diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index ae6624b9a4..e5861e5260 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -12,7 +12,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_rem_i32 1 -#define TCG_TARGET_HAS_div2_i32 0 #define TCG_TARGET_HAS_rot_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 @@ -28,7 +27,6 @@ #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_rem_i64 1 -#define TCG_TARGET_HAS_div2_i64 0 #define TCG_TARGET_HAS_rot_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 19c690c1c2..72910b0f25 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2025,6 +2025,10 @@ static const TCGOutOpBinary outop_divu = { .out_rrr = tgen_divu, }; +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 64f1805641..d61cc7a144 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -29,7 +29,6 @@ extern uint64_t s390_facilities[3]; ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) /* optional instructions */ -#define TCG_TARGET_HAS_div2_i32 1 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 @@ -45,7 +44,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_div2_i64 1 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index b434ce423a..9af626eec2 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2272,6 +2272,23 @@ static const TCGOutOpBinary outop_divu = { .base.static_constraint = C_NotImplemented, }; +static void tgen_divu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a4) +{ + tcg_debug_assert((a1 & 1) == 0); + tcg_debug_assert(a0 == a1 + 1); + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRE, DLR, a1, a4); + } else { + tcg_out_insn(s, RRE, DLGR, a1, a4); + } +} + +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_O2_I3(o, m, 0, 1, r), + .out_rr01r = tgen_divu2, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2549,14 +2566,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_divu2_i32: - tcg_debug_assert(args[0] == args[2]); - tcg_debug_assert(args[1] == args[3]); - tcg_debug_assert((args[1] & 1) == 0); - tcg_debug_assert(args[0] == args[1] + 1); - tcg_out_insn(s, RRE, DLR, args[1], args[4]); - break; - case INDEX_op_shl_i32: op = RS_SLL; op2 = RSY_SLLK; @@ -2717,13 +2726,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_insn(s, RRE, LRVGR, args[0], args[1]); break; - case INDEX_op_divu2_i64: - tcg_debug_assert(args[0] == args[2]); - tcg_debug_assert(args[1] == args[3]); - tcg_debug_assert((args[1] & 1) == 0); - tcg_debug_assert(args[0] == args[1] + 1); - tcg_out_insn(s, RRE, DLGR, args[1], args[4]); - break; case INDEX_op_mulu2_i64: tcg_debug_assert(args[0] == args[2]); tcg_debug_assert((args[1] & 1) == 0); @@ -3398,10 +3400,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_movcond_i64: return C_O1_I4(r, r, rC, rI, r); - case INDEX_op_divu2_i32: - case INDEX_op_divu2_i64: - return C_O2_I3(o, m, 0, 1, r); - case INDEX_op_mulu2_i64: return C_O2_I2(o, m, 0, r); case INDEX_op_muls2_i64: diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 472ccd7608..a4659653b3 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1389,6 +1389,10 @@ static const TCGOutOpBinary outop_divu = { .out_rri = tgen_divui, }; +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 9680ccfc53..bae9918024 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -14,7 +14,6 @@ #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_div_i64 0 #define TCG_TARGET_HAS_rem_i64 0 -#define TCG_TARGET_HAS_div2_i64 0 #define TCG_TARGET_HAS_rot_i64 0 #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 @@ -33,12 +32,6 @@ #define TCG_TARGET_HAS_sub2_i32 1 #endif -#ifndef TCG_TARGET_HAS_div2_i32 -#define TCG_TARGET_HAS_div2_i32 0 -#endif -#ifndef TCG_TARGET_HAS_div2_i64 -#define TCG_TARGET_HAS_div2_i64 0 -#endif #ifndef TCG_TARGET_HAS_rem_i32 #define TCG_TARGET_HAS_rem_i32 0 #endif diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index f95beb8b5d..5511106554 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -637,7 +637,7 @@ void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_divu, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div2_i32) { + } else if (tcg_op_supported(INDEX_op_divu2_i32, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 zero = tcg_constant_i32(0); tcg_gen_op5_i32(INDEX_op_divu2_i32, ret, t0, arg1, zero, arg2); @@ -657,7 +657,7 @@ void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_mul_i32(t0, t0, arg2); tcg_gen_sub_i32(ret, arg1, t0); tcg_temp_free_i32(t0); - } else if (TCG_TARGET_HAS_div2_i32) { + } else if (tcg_op_supported(INDEX_op_divu2_i32, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 zero = tcg_constant_i32(0); tcg_gen_op5_i32(INDEX_op_divu2_i32, t0, ret, arg1, zero, arg2); @@ -2005,7 +2005,7 @@ void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_divu, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_div2_i64) { + } else if (tcg_op_supported(INDEX_op_divu2_i64, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 zero = tcg_constant_i64(0); tcg_gen_op5_i64(INDEX_op_divu2_i64, ret, t0, arg1, zero, arg2); @@ -2025,7 +2025,7 @@ void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_mul_i64(t0, t0, arg2); tcg_gen_sub_i64(ret, arg1, t0); tcg_temp_free_i64(t0); - } else if (TCG_TARGET_HAS_div2_i64) { + } else if (tcg_op_supported(INDEX_op_divu2_i64, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 zero = tcg_constant_i64(0); tcg_gen_op5_i64(INDEX_op_divu2_i64, t0, ret, arg1, zero, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index 30b7f8ee19..1029cba3f0 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1029,6 +1029,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), + OUTOP(INDEX_op_divu2_i32, TCGOutOpDivRem, outop_divu2), + OUTOP(INDEX_op_divu2_i64, TCGOutOpDivRem, outop_divu2), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), @@ -2272,8 +2274,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rem_i32: case INDEX_op_remu_i32: return TCG_TARGET_HAS_rem_i32; - case INDEX_op_divu2_i32: - return TCG_TARGET_HAS_div2_i32; case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: return TCG_TARGET_HAS_rot_i32; @@ -2331,8 +2331,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rem_i64: case INDEX_op_remu_i64: return TCG_TARGET_HAS_rem_i64; - case INDEX_op_divu2_i64: - return TCG_TARGET_HAS_div2_i64; case INDEX_op_rotl_i64: case INDEX_op_rotr_i64: return TCG_TARGET_HAS_rot_i64; @@ -5473,6 +5471,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_divs2: + case INDEX_op_divu2_i32: + case INDEX_op_divu2_i64: { const TCGOutOpDivRem *out = container_of(all_outop[op->opc], TCGOutOpDivRem, base); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 6646be224d..27271c178c 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -676,6 +676,10 @@ static const TCGOutOpBinary outop_divu = { .out_rrr = tgen_divu, }; +static const TCGOutOpDivRem outop_divu2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { From 8109598b683ad2b6b02cd9c79dc15b7fc0b685aa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 18:23:17 -0800 Subject: [PATCH 0407/2760] tcg: Merge INDEX_op_divu2_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 10 ++++++++++ include/tcg/tcg-opc.h | 3 +-- tcg/tcg-op.c | 16 ++++++++-------- tcg/tcg.c | 6 ++---- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 62af390854..8f3b5e91b2 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -307,6 +307,16 @@ Arithmetic pass *nh* as a simple sign-extension of *nl*, so the only overflow should be *INT_MIN* / -1. + * - divu2 *q*, *r*, *nl*, *nh*, *d* + + - | *q* = *nh:nl* / *d* (unsigned) + | *r* = *nh:nl* % *d* + | Undefined behaviour if division by zero, or the double-word + numerator divided by the single-word divisor does not fit + within the single-word quotient. The code generator will + pass 0 to *nh* to make a simple zero-extension of *nl*, + so overflow should never occur. + Logical ------- diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 36dfbf80ad..61e5e185cc 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -45,6 +45,7 @@ DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(divs, 1, 2, 0, TCG_OPF_INT) DEF(divs2, 2, 3, 0, TCG_OPF_INT) DEF(divu, 1, 2, 0, TCG_OPF_INT) +DEF(divu2, 2, 3, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(mul, 1, 2, 0, TCG_OPF_INT) DEF(mulsh, 1, 2, 0, TCG_OPF_INT) @@ -73,7 +74,6 @@ DEF(st_i32, 0, 2, 1, 0) /* arith */ DEF(rem_i32, 1, 2, 0, 0) DEF(remu_i32, 1, 2, 0, 0) -DEF(divu2_i32, 2, 3, 0, 0) /* shifts/rotates */ DEF(shl_i32, 1, 2, 0, 0) DEF(shr_i32, 1, 2, 0, 0) @@ -118,7 +118,6 @@ DEF(st_i64, 0, 2, 1, 0) /* arith */ DEF(rem_i64, 1, 2, 0, 0) DEF(remu_i64, 1, 2, 0, 0) -DEF(divu2_i64, 2, 3, 0, 0) /* shifts/rotates */ DEF(shl_i64, 1, 2, 0, 0) DEF(shr_i64, 1, 2, 0, 0) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 5511106554..7ed92157de 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -637,10 +637,10 @@ void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_divu, ret, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_divu2_i32, TCG_TYPE_I32, 0)) { + } else if (tcg_op_supported(INDEX_op_divu2, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 zero = tcg_constant_i32(0); - tcg_gen_op5_i32(INDEX_op_divu2_i32, ret, t0, arg1, zero, arg2); + tcg_gen_op5_i32(INDEX_op_divu2, ret, t0, arg1, zero, arg2); tcg_temp_free_i32(t0); } else { gen_helper_divu_i32(ret, arg1, arg2); @@ -657,10 +657,10 @@ void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_mul_i32(t0, t0, arg2); tcg_gen_sub_i32(ret, arg1, t0); tcg_temp_free_i32(t0); - } else if (tcg_op_supported(INDEX_op_divu2_i32, TCG_TYPE_I32, 0)) { + } else if (tcg_op_supported(INDEX_op_divu2, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 zero = tcg_constant_i32(0); - tcg_gen_op5_i32(INDEX_op_divu2_i32, t0, ret, arg1, zero, arg2); + tcg_gen_op5_i32(INDEX_op_divu2, t0, ret, arg1, zero, arg2); tcg_temp_free_i32(t0); } else { gen_helper_remu_i32(ret, arg1, arg2); @@ -2005,10 +2005,10 @@ void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_divu, ret, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_divu2_i64, TCG_TYPE_I64, 0)) { + } else if (tcg_op_supported(INDEX_op_divu2, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 zero = tcg_constant_i64(0); - tcg_gen_op5_i64(INDEX_op_divu2_i64, ret, t0, arg1, zero, arg2); + tcg_gen_op5_i64(INDEX_op_divu2, ret, t0, arg1, zero, arg2); tcg_temp_free_i64(t0); } else { gen_helper_divu_i64(ret, arg1, arg2); @@ -2025,10 +2025,10 @@ void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_mul_i64(t0, t0, arg2); tcg_gen_sub_i64(ret, arg1, t0); tcg_temp_free_i64(t0); - } else if (tcg_op_supported(INDEX_op_divu2_i64, TCG_TYPE_I64, 0)) { + } else if (tcg_op_supported(INDEX_op_divu2, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 zero = tcg_constant_i64(0); - tcg_gen_op5_i64(INDEX_op_divu2_i64, t0, ret, arg1, zero, arg2); + tcg_gen_op5_i64(INDEX_op_divu2, t0, ret, arg1, zero, arg2); tcg_temp_free_i64(t0); } else { gen_helper_remu_i64(ret, arg1, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index 1029cba3f0..2c1307e3fc 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1029,8 +1029,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), - OUTOP(INDEX_op_divu2_i32, TCGOutOpDivRem, outop_divu2), - OUTOP(INDEX_op_divu2_i64, TCGOutOpDivRem, outop_divu2), + OUTOP(INDEX_op_divu2, TCGOutOpDivRem, outop_divu2), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), @@ -5471,8 +5470,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_divs2: - case INDEX_op_divu2_i32: - case INDEX_op_divu2_i64: + case INDEX_op_divu2: { const TCGOutOpDivRem *out = container_of(all_outop[op->opc], TCGOutOpDivRem, base); From 646f36fead167e924e83d23d0e1dc59c37b5cca5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 18:52:30 -0800 Subject: [PATCH 0408/2760] tcg: Convert rem to TCGOutOpBinary For TCI, we're losing type information in the interpreter. Introduce a tci-specific opcode to handle the difference. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 19 ++++++++++------ tcg/arm/tcg-target.c.inc | 4 ++++ tcg/i386/tcg-target.c.inc | 4 ++++ tcg/loongarch64/tcg-target.c.inc | 24 +++++++++++++-------- tcg/mips/tcg-target.c.inc | 37 ++++++++++++++++++-------------- tcg/ppc/tcg-target.c.inc | 27 +++++++++++++++-------- tcg/riscv/tcg-target.c.inc | 21 ++++++++++-------- tcg/s390x/tcg-target.c.inc | 4 ++++ tcg/sparc64/tcg-target.c.inc | 4 ++++ tcg/tcg.c | 6 ++++-- tcg/tci.c | 4 ++-- tcg/tci/tcg-target-opc.h.inc | 1 + tcg/tci/tcg-target.c.inc | 17 ++++++++++++--- 13 files changed, 115 insertions(+), 57 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 456159cdc6..6e80e18a6a 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2275,6 +2275,18 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_rems(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, SDIV, type, TCG_REG_TMP0, a1, a2); + tcg_out_insn(s, 3509, MSUB, type, a0, TCG_REG_TMP0, a2, a1); +} + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rems, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2392,11 +2404,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_rem_i64: - case INDEX_op_rem_i32: - tcg_out_insn(s, 3508, SDIV, ext, TCG_REG_TMP0, a1, a2); - tcg_out_insn(s, 3509, MSUB, ext, a0, TCG_REG_TMP0, a2, a1); - break; case INDEX_op_remu_i64: case INDEX_op_remu_i32: tcg_out_insn(s, 3508, UDIV, ext, TCG_REG_TMP0, a1, a2); @@ -3078,8 +3085,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rC); - case INDEX_op_rem_i32: - case INDEX_op_rem_i64: case INDEX_op_remu_i32: case INDEX_op_remu_i64: return C_O1_I2(r, r, r); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index b2c08bba3e..673c8fb7a6 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1955,6 +1955,10 @@ static const TCGOutOpBinary outop_orc = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 0e6b743fb2..ac0721d71c 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2736,6 +2736,10 @@ static const TCGOutOpBinary outop_orc = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index be09c362cb..ef37b4daa7 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1459,6 +1459,21 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_rems(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_mod_w(s, a0, a1, a2); + } else { + tcg_out_opc_mod_d(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rems, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1709,13 +1724,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_rem_i32: - tcg_out_opc_mod_w(s, a0, a1, a2); - break; - case INDEX_op_rem_i64: - tcg_out_opc_mod_d(s, a0, a1, a2); - break; - case INDEX_op_remu_i32: tcg_out_opc_mod_wu(s, a0, a1, a2); break; @@ -2381,8 +2389,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i64: return C_O1_I2(r, rz, rJ); - case INDEX_op_rem_i32: - case INDEX_op_rem_i64: case INDEX_op_remu_i32: case INDEX_op_remu_i64: return C_O1_I2(r, rz, rz); diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 280afbf297..37b878ec61 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1866,6 +1866,27 @@ static const TCGOutOpBinary outop_orc = { .base.static_constraint = C_NotImplemented, }; +static void tgen_rems(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + if (type == TCG_TYPE_I32) { + tcg_out_opc_reg(s, OPC_MOD, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_DMOD, a0, a1, a2); + } + } else { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_DIV : OPC_DDIV; + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFHI, a0, 0, 0); + } +} + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rems, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1989,13 +2010,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_rem_i32: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_MOD, a0, a1, a2); - break; - } - i1 = OPC_DIV, i2 = OPC_MFHI; - goto do_hilo1; case INDEX_op_remu_i32: if (use_mips32r6_instructions) { tcg_out_opc_reg(s, OPC_MODU, a0, a1, a2); @@ -2003,13 +2017,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } i1 = OPC_DIVU, i2 = OPC_MFHI; goto do_hilo1; - case INDEX_op_rem_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DMOD, a0, a1, a2); - break; - } - i1 = OPC_DDIV, i2 = OPC_MFHI; - goto do_hilo1; case INDEX_op_remu_i64: if (use_mips32r6_instructions) { tcg_out_opc_reg(s, OPC_DMODU, a0, a1, a2); @@ -2275,10 +2282,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_rem_i32: case INDEX_op_remu_i32: case INDEX_op_setcond_i32: - case INDEX_op_rem_i64: case INDEX_op_remu_i64: case INDEX_op_setcond_i64: return C_O1_I2(r, rz, rz); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 8b14d57d1c..c331f0d672 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3091,6 +3091,24 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static TCGConstraintSetIndex cset_mod(TCGType type, unsigned flags) +{ + return have_isa_3_00 ? C_O1_I2(r, r, r) : C_NotImplemented; +} + +static void tgen_rems(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? MODSW : MODSD; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mod, + .out_rrr = tgen_rems, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3241,10 +3259,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, CNTPOPD | SAB(args[1], args[0], 0)); break; - case INDEX_op_rem_i32: - tcg_out32(s, MODSW | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_remu_i32: tcg_out32(s, MODUW | TAB(args[0], args[1], args[2])); break; @@ -3341,9 +3355,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_rem_i64: - tcg_out32(s, MODSD | TAB(args[0], args[1], args[2])); - break; case INDEX_op_remu_i64: tcg_out32(s, MODUD | TAB(args[0], args[1], args[2])); break; @@ -4202,9 +4213,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rotr_i64: return C_O1_I2(r, r, ri); - case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_rem_i64: case INDEX_op_remu_i64: return C_O1_I2(r, r, r); diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 72910b0f25..b0a98273f1 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2120,6 +2120,18 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_rems(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_REMW : OPC_REM; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rems, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2233,13 +2245,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_rem_i32: - tcg_out_opc_reg(s, OPC_REMW, a0, a1, a2); - break; - case INDEX_op_rem_i64: - tcg_out_opc_reg(s, OPC_REM, a0, a1, a2); - break; - case INDEX_op_remu_i32: tcg_out_opc_reg(s, OPC_REMUW, a0, a1, a2); break; @@ -2748,9 +2753,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_rem_i32: case INDEX_op_remu_i32: - case INDEX_op_rem_i64: case INDEX_op_remu_i64: return C_O1_I2(r, rz, rz); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 9af626eec2..320268669a 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2437,6 +2437,10 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index a4659653b3..23cca5c664 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1476,6 +1476,10 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/tcg.c b/tcg/tcg.c index 2c1307e3fc..5af5529284 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1040,6 +1040,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_not, TCGOutOpUnary, outop_not), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), + OUTOP(INDEX_op_rem_i32, TCGOutOpBinary, outop_rems), + OUTOP(INDEX_op_rem_i64, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), }; @@ -2270,7 +2272,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i32: return TCG_TARGET_HAS_negsetcond_i32; - case INDEX_op_rem_i32: case INDEX_op_remu_i32: return TCG_TARGET_HAS_rem_i32; case INDEX_op_rotl_i32: @@ -2327,7 +2328,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return TCG_TARGET_HAS_negsetcond_i64; - case INDEX_op_rem_i64: case INDEX_op_remu_i64: return TCG_TARGET_HAS_rem_i64; case INDEX_op_rotl_i64: @@ -5425,6 +5425,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_nor: case INDEX_op_or: case INDEX_op_orc: + case INDEX_op_rem_i32: + case INDEX_op_rem_i64: case INDEX_op_xor: { const TCGOutOpBinary *out = diff --git a/tcg/tci.c b/tcg/tci.c index bf97849bfe..65f493c3d4 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -586,7 +586,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint32_t)regs[r1] / (uint32_t)regs[r2]; break; - case INDEX_op_rem_i32: + case INDEX_op_tci_rems32: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int32_t)regs[r1] % (int32_t)regs[r2]; break; @@ -1081,7 +1081,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_orc: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_rem_i32: case INDEX_op_rem_i64: case INDEX_op_remu_i32: case INDEX_op_remu_i64: @@ -1101,6 +1100,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_ctz_i64: case INDEX_op_tci_divs32: case INDEX_op_tci_divu32: + case INDEX_op_tci_rems32: tci_args_rrr(insn, &r0, &r1, &r2); info->fprintf_func(info->stream, "%-12s %s, %s, %s", op_name, str_r(r0), str_r(r1), str_r(r2)); diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc index 43c07a269f..2822fbffc8 100644 --- a/tcg/tci/tcg-target-opc.h.inc +++ b/tcg/tci/tcg-target-opc.h.inc @@ -4,3 +4,4 @@ DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(tci_divs32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_divu32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_rems32, 1, 2, 0, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 27271c178c..4d9c142a00 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -79,8 +79,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_rem_i32: - case INDEX_op_rem_i64: case INDEX_op_remu_i32: case INDEX_op_remu_i64: case INDEX_op_shl_i32: @@ -754,6 +752,20 @@ static const TCGOutOpBinary outop_orc = { .out_rrr = tgen_orc, }; +static void tgen_rems(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_rems32 + : INDEX_op_rem_i64); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_rems = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rems, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -843,7 +855,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, CASE_32_64(sar) CASE_32_64(rotl) /* Optional (TCG_TARGET_HAS_rot_*). */ CASE_32_64(rotr) /* Optional (TCG_TARGET_HAS_rot_*). */ - CASE_32_64(rem) /* Optional (TCG_TARGET_HAS_div_*). */ CASE_32_64(remu) /* Optional (TCG_TARGET_HAS_div_*). */ CASE_32_64(clz) /* Optional (TCG_TARGET_HAS_clz_*). */ CASE_32_64(ctz) /* Optional (TCG_TARGET_HAS_ctz_*). */ From 9a6bc1840ec105902bda1a59c42e9e0c56a9ed05 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 19:00:51 -0800 Subject: [PATCH 0409/2760] tcg: Merge INDEX_op_rem_{i32,i64} Rename to INDEX_op_rems to emphasize signed inputs, and mirroring INDEX_op_remu_*. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 12 +++++++----- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 6 ++---- tcg/tci.c | 4 ++-- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 18 insertions(+), 19 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 8f3b5e91b2..1f4160a585 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -287,7 +287,7 @@ Arithmetic - | *t0* = *t1* / *t2* (unsigned) | Undefined behavior if division by zero. - * - rem_i32/i64 *t0*, *t1*, *t2* + * - rems *t0*, *t1*, *t2* - | *t0* = *t1* % *t2* (signed) | Undefined behavior if division by zero or overflow. diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 61e5e185cc..040f4da835 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -56,6 +56,7 @@ DEF(nor, 1, 2, 0, TCG_OPF_INT) DEF(not, 1, 1, 0, TCG_OPF_INT) DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) +DEF(rems, 1, 2, 0, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) @@ -72,7 +73,6 @@ DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* arith */ -DEF(rem_i32, 1, 2, 0, 0) DEF(remu_i32, 1, 2, 0, 0) /* shifts/rotates */ DEF(shl_i32, 1, 2, 0, 0) @@ -116,7 +116,6 @@ DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* arith */ -DEF(rem_i64, 1, 2, 0, 0) DEF(remu_i64, 1, 2, 0, 0) /* shifts/rotates */ DEF(shl_i64, 1, 2, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index c11cce782a..01ec365175 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -569,12 +569,14 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, } return (uint64_t)x / ((uint64_t)y ? : 1); - case INDEX_op_rem_i32: - return (int32_t)x % ((int32_t)y ? : 1); + case INDEX_op_rems: + if (type == TCG_TYPE_I32) { + return (int32_t)x % ((int32_t)y ? : 1); + } + return (int64_t)x % ((int64_t)y ? : 1); + case INDEX_op_remu_i32: return (uint32_t)x % ((uint32_t)y ? : 1); - case INDEX_op_rem_i64: - return (int64_t)x % ((int64_t)y ? : 1); case INDEX_op_remu_i64: return (uint64_t)x % ((uint64_t)y ? : 1); @@ -3021,7 +3023,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_qemu_st_i128: done = fold_qemu_st(&ctx, op); break; - CASE_OP_32_64(rem): + case INDEX_op_rems: CASE_OP_32_64(remu): done = fold_remainder(&ctx, op); break; diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 7ed92157de..6da8b30547 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -615,8 +615,8 @@ void tcg_gen_div_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_rem_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_rem_i32) { - tcg_gen_op3_i32(INDEX_op_rem_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_rems, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_rems, ret, arg1, arg2); } else if (tcg_op_supported(INDEX_op_divs, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_divs, t0, arg1, arg2); @@ -1983,8 +1983,8 @@ void tcg_gen_div_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_rem_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (TCG_TARGET_HAS_rem_i64) { - tcg_gen_op3_i64(INDEX_op_rem_i64, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_rems, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_rems, ret, arg1, arg2); } else if (tcg_op_supported(INDEX_op_divs, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_divs, t0, arg1, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index 5af5529284..fad828fa2f 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1040,8 +1040,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_not, TCGOutOpUnary, outop_not), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), - OUTOP(INDEX_op_rem_i32, TCGOutOpBinary, outop_rems), - OUTOP(INDEX_op_rem_i64, TCGOutOpBinary, outop_rems), + OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), }; @@ -5425,8 +5424,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_nor: case INDEX_op_or: case INDEX_op_orc: - case INDEX_op_rem_i32: - case INDEX_op_rem_i64: + case INDEX_op_rems: case INDEX_op_xor: { const TCGOutOpBinary *out = diff --git a/tcg/tci.c b/tcg/tci.c index 65f493c3d4..6ca033f3be 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -728,7 +728,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint64_t)regs[r1] / (uint64_t)regs[r2]; break; - case INDEX_op_rem_i64: + case INDEX_op_rems: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int64_t)regs[r1] % (int64_t)regs[r2]; break; @@ -1079,9 +1079,9 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_nor: case INDEX_op_or: case INDEX_op_orc: + case INDEX_op_rems: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_rem_i64: case INDEX_op_remu_i32: case INDEX_op_remu_i64: case INDEX_op_shl_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 4d9c142a00..2b05da7d06 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -757,7 +757,7 @@ static void tgen_rems(TCGContext *s, TCGType type, { TCGOpcode opc = (type == TCG_TYPE_I32 ? INDEX_op_tci_rems32 - : INDEX_op_rem_i64); + : INDEX_op_rems); tcg_out_op_rrr(s, opc, a0, a1, a2); } From 967e7ccd9c5c6a5a2cc5d7a101cd24acced22749 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 20:12:08 -0800 Subject: [PATCH 0410/2760] tcg: Convert remu to TCGOutOpBinary For TCI, we're losing type information in the interpreter. Introduce a tci-specific opcode to handle the difference. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 22 ++++++++------- tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 4 +++ tcg/i386/tcg-target.c.inc | 4 +++ tcg/loongarch64/tcg-target-con-set.h | 1 - tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 26 ++++++++++-------- tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 41 ++++++++++++++-------------- tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 25 +++++++++-------- tcg/riscv/tcg-target-con-set.h | 1 - tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 23 ++++++++-------- tcg/s390x/tcg-target.c.inc | 4 +++ tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 4 +++ tcg/tcg-has.h | 9 ------ tcg/tcg-op.c | 4 +-- tcg/tcg.c | 8 +++--- tcg/tci.c | 4 +-- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target-opc.h.inc | 1 + tcg/tci/tcg-target.c.inc | 17 ++++++++++-- 25 files changed, 112 insertions(+), 101 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index e961668ef0..1fdff25d05 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -13,7 +13,6 @@ #define have_lse2 (cpuinfo & CPUINFO_LSE2) /* optional instructions */ -#define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_rot_i32 1 @@ -29,7 +28,6 @@ #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 6e80e18a6a..8aa11e9d9d 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2287,6 +2287,18 @@ static const TCGOutOpBinary outop_rems = { .out_rrr = tgen_rems, }; +static void tgen_remu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, UDIV, type, TCG_REG_TMP0, a1, a2); + tcg_out_insn(s, 3509, MSUB, type, a0, TCG_REG_TMP0, a2, a1); +} + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_remu, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2404,12 +2416,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_remu_i64: - case INDEX_op_remu_i32: - tcg_out_insn(s, 3508, UDIV, ext, TCG_REG_TMP0, a1, a2); - tcg_out_insn(s, 3509, MSUB, ext, a0, TCG_REG_TMP0, a2, a1); - break; - case INDEX_op_shl_i64: case INDEX_op_shl_i32: if (c2) { @@ -3085,10 +3091,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rC); - case INDEX_op_remu_i32: - case INDEX_op_remu_i64: - return C_O1_I2(r, r, r); - case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 6ed2b49c84..32d73d3443 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -34,7 +34,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_muls2_i32 1 -#define TCG_TARGET_HAS_rem_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 673c8fb7a6..c08cd712b1 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1959,6 +1959,10 @@ static const TCGOutOpBinary outop_rems = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index ac0721d71c..02dd440052 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2740,6 +2740,10 @@ static const TCGOutOpBinary outop_rems = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/loongarch64/tcg-target-con-set.h b/tcg/loongarch64/tcg-target-con-set.h index b4af4f5423..da84e4d49c 100644 --- a/tcg/loongarch64/tcg-target-con-set.h +++ b/tcg/loongarch64/tcg-target-con-set.h @@ -31,7 +31,6 @@ C_O1_I2(r, r, rW) C_O1_I2(r, 0, rz) C_O1_I2(r, rz, ri) C_O1_I2(r, rz, rJ) -C_O1_I2(r, rz, rz) C_O1_I2(w, w, w) C_O1_I2(w, w, wM) C_O1_I2(w, w, wA) diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index aecd2879b8..5dfc69ae6a 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -11,7 +11,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_negsetcond_i32 0 -#define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 0 @@ -27,7 +26,6 @@ /* 64-bit operations */ #define TCG_TARGET_HAS_negsetcond_i64 0 -#define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index ef37b4daa7..a0313b1140 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1474,6 +1474,21 @@ static const TCGOutOpBinary outop_rems = { .out_rrr = tgen_rems, }; +static void tgen_remu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_mod_wu(s, a0, a1, a2); + } else { + tcg_out_opc_mod_du(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_remu, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1724,13 +1739,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_remu_i32: - tcg_out_opc_mod_wu(s, a0, a1, a2); - break; - case INDEX_op_remu_i64: - tcg_out_opc_mod_du(s, a0, a1, a2); - break; - case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: tcg_out_setcond(s, args[3], a0, a1, a2, c2); @@ -2389,10 +2397,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i64: return C_O1_I2(r, rz, rJ); - case INDEX_op_remu_i32: - case INDEX_op_remu_i64: - return C_O1_I2(r, rz, rz); - case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: return C_O1_I4(r, rz, rJ, rz, rz); diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 9aa5bf9f1b..ab6a134796 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -39,7 +39,6 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_bswap16_i32 1 @@ -50,7 +49,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 (!use_mips32r6_instructions) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 37b878ec61..bd38c7ab95 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1887,6 +1887,27 @@ static const TCGOutOpBinary outop_rems = { .out_rrr = tgen_rems, }; +static void tgen_remu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + if (type == TCG_TYPE_I32) { + tcg_out_opc_reg(s, OPC_MODU, a0, a1, a2); + } else { + tcg_out_opc_reg(s, OPC_DMODU, a0, a1, a2); + } + } else { + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_DIVU : OPC_DDIVU; + tcg_out_opc_reg(s, insn, 0, a1, a2); + tcg_out_opc_reg(s, OPC_MFHI, a0, 0, 0); + } +} + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_remu, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2010,24 +2031,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_remu_i32: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_MODU, a0, a1, a2); - break; - } - i1 = OPC_DIVU, i2 = OPC_MFHI; - goto do_hilo1; - case INDEX_op_remu_i64: - if (use_mips32r6_instructions) { - tcg_out_opc_reg(s, OPC_DMODU, a0, a1, a2); - break; - } - i1 = OPC_DDIVU, i2 = OPC_MFHI; - do_hilo1: - tcg_out_opc_reg(s, i1, 0, a1, a2); - tcg_out_opc_reg(s, i2, a0, 0, 0); - break; - case INDEX_op_muls2_i32: i1 = OPC_MULT; goto do_hilo2; @@ -2282,9 +2285,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_remu_i32: case INDEX_op_setcond_i32: - case INDEX_op_remu_i64: case INDEX_op_setcond_i64: return C_O1_I2(r, rz, rz); case INDEX_op_muls2_i32: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index f8e4c0ad3c..37e88a3193 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -17,7 +17,6 @@ #define have_vsx (cpuinfo & CPUINFO_VSX) /* optional instructions */ -#define TCG_TARGET_HAS_rem_i32 have_isa_3_00 #define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 @@ -34,7 +33,6 @@ #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_rem_i64 have_isa_3_00 #define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index c331f0d672..80ee4d04c9 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3109,6 +3109,19 @@ static const TCGOutOpBinary outop_rems = { .out_rrr = tgen_rems, }; +static void tgen_remu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? MODUW : MODUD; + tcg_out32(s, insn | TAB(a0, a1, a2)); +} + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mod, + .out_rrr = tgen_remu, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3259,10 +3272,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, CNTPOPD | SAB(args[1], args[0], 0)); break; - case INDEX_op_remu_i32: - tcg_out32(s, MODUW | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_shl_i32: if (const_args[2]) { /* Limit immediate shift count lest we create an illegal insn. */ @@ -3355,10 +3364,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_remu_i64: - tcg_out32(s, MODUD | TAB(args[0], args[1], args[2])); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; @@ -4213,10 +4218,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_rotr_i64: return C_O1_I2(r, r, ri); - case INDEX_op_remu_i32: - case INDEX_op_remu_i64: - return C_O1_I2(r, r, r); - case INDEX_op_clz_i32: case INDEX_op_ctz_i32: case INDEX_op_clz_i64: diff --git a/tcg/riscv/tcg-target-con-set.h b/tcg/riscv/tcg-target-con-set.h index f3a6f7a7ed..f0d3cb81bd 100644 --- a/tcg/riscv/tcg-target-con-set.h +++ b/tcg/riscv/tcg-target-con-set.h @@ -16,7 +16,6 @@ C_O1_I1(r, r) C_O1_I2(r, r, r) C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) -C_O1_I2(r, rz, rz) C_N1_I2(r, r, rM) C_O1_I4(r, r, rI, rM, rM) C_O2_I4(r, r, rz, rz, rM, rM) diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index e5861e5260..b3c6899887 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -11,7 +11,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_negsetcond_i32 1 -#define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_rot_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 @@ -26,7 +25,6 @@ #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_negsetcond_i64 1 -#define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_rot_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index b0a98273f1..38ba898042 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2132,6 +2132,18 @@ static const TCGOutOpBinary outop_rems = { .out_rrr = tgen_rems, }; +static void tgen_remu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_REMUW : OPC_REMU; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_remu, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2245,13 +2257,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_remu_i32: - tcg_out_opc_reg(s, OPC_REMUW, a0, a1, a2); - break; - case INDEX_op_remu_i64: - tcg_out_opc_reg(s, OPC_REMU, a0, a1, a2); - break; - case INDEX_op_shl_i32: if (c2) { tcg_out_opc_imm(s, OPC_SLLIW, a0, a1, a2 & 0x1f); @@ -2753,10 +2758,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_remu_i32: - case INDEX_op_remu_i64: - return C_O1_I2(r, rz, rz); - case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 320268669a..8702d8c928 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2441,6 +2441,10 @@ static const TCGOutOpBinary outop_rems = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 35f0dd4230..42de99efbf 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -14,7 +14,6 @@ extern bool use_vis3_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_rem_i32 0 #define TCG_TARGET_HAS_rot_i32 0 #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 @@ -30,7 +29,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_rem_i64 0 #define TCG_TARGET_HAS_rot_i64 0 #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 23cca5c664..d465c8dd06 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1480,6 +1480,10 @@ static const TCGOutOpBinary outop_rems = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index bae9918024..0bb829be36 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -12,8 +12,6 @@ #if TCG_TARGET_REG_BITS == 32 /* Turn some undef macros into false macros. */ #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_div_i64 0 -#define TCG_TARGET_HAS_rem_i64 0 #define TCG_TARGET_HAS_rot_i64 0 #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 @@ -32,13 +30,6 @@ #define TCG_TARGET_HAS_sub2_i32 1 #endif -#ifndef TCG_TARGET_HAS_rem_i32 -#define TCG_TARGET_HAS_rem_i32 0 -#endif -#ifndef TCG_TARGET_HAS_rem_i64 -#define TCG_TARGET_HAS_rem_i64 0 -#endif - #if !defined(TCG_TARGET_HAS_v64) \ && !defined(TCG_TARGET_HAS_v128) \ && !defined(TCG_TARGET_HAS_v256) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 6da8b30547..4ff6c9f0ab 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -649,7 +649,7 @@ void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_rem_i32) { + if (tcg_op_supported(INDEX_op_remu_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_remu_i32, ret, arg1, arg2); } else if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); @@ -2017,7 +2017,7 @@ void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (TCG_TARGET_HAS_rem_i64) { + if (tcg_op_supported(INDEX_op_remu_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_remu_i64, ret, arg1, arg2); } else if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index fad828fa2f..537b665d07 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1041,6 +1041,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), + OUTOP(INDEX_op_remu_i32, TCGOutOpBinary, outop_remu), + OUTOP(INDEX_op_remu_i64, TCGOutOpBinary, outop_remu), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), }; @@ -2271,8 +2273,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i32: return TCG_TARGET_HAS_negsetcond_i32; - case INDEX_op_remu_i32: - return TCG_TARGET_HAS_rem_i32; case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: return TCG_TARGET_HAS_rot_i32; @@ -2327,8 +2327,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return TCG_TARGET_HAS_negsetcond_i64; - case INDEX_op_remu_i64: - return TCG_TARGET_HAS_rem_i64; case INDEX_op_rotl_i64: case INDEX_op_rotr_i64: return TCG_TARGET_HAS_rot_i64; @@ -5425,6 +5423,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_or: case INDEX_op_orc: case INDEX_op_rems: + case INDEX_op_remu_i32: + case INDEX_op_remu_i64: case INDEX_op_xor: { const TCGOutOpBinary *out = diff --git a/tcg/tci.c b/tcg/tci.c index 6ca033f3be..bd5817a382 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -590,7 +590,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int32_t)regs[r1] % (int32_t)regs[r2]; break; - case INDEX_op_remu_i32: + case INDEX_op_tci_remu32: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint32_t)regs[r1] % (uint32_t)regs[r2]; break; @@ -1082,7 +1082,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_rems: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_remu_i32: case INDEX_op_remu_i64: case INDEX_op_shl_i32: case INDEX_op_shl_i64: @@ -1101,6 +1100,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_tci_divs32: case INDEX_op_tci_divu32: case INDEX_op_tci_rems32: + case INDEX_op_tci_remu32: tci_args_rrr(insn, &r0, &r1, &r2); info->fprintf_func(info->stream, "%-12s %s, %s, %s", op_name, str_r(r0), str_r(r1), str_r(r2)); diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index ccec96b610..bd51b9346d 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -9,7 +9,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_rem_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 @@ -25,7 +24,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_rem_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 1 diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc index 2822fbffc8..82d2a38cae 100644 --- a/tcg/tci/tcg-target-opc.h.inc +++ b/tcg/tci/tcg-target-opc.h.inc @@ -5,3 +5,4 @@ DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(tci_divs32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_divu32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_rems32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_remu32, 1, 2, 0, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 2b05da7d06..421a2a8ac7 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -79,8 +79,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_remu_i32: - case INDEX_op_remu_i64: case INDEX_op_shl_i32: case INDEX_op_shl_i64: case INDEX_op_shr_i32: @@ -766,6 +764,20 @@ static const TCGOutOpBinary outop_rems = { .out_rrr = tgen_rems, }; +static void tgen_remu(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_remu32 + : INDEX_op_remu_i64); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_remu = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_remu, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -855,7 +867,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, CASE_32_64(sar) CASE_32_64(rotl) /* Optional (TCG_TARGET_HAS_rot_*). */ CASE_32_64(rotr) /* Optional (TCG_TARGET_HAS_rot_*). */ - CASE_32_64(remu) /* Optional (TCG_TARGET_HAS_div_*). */ CASE_32_64(clz) /* Optional (TCG_TARGET_HAS_clz_*). */ CASE_32_64(ctz) /* Optional (TCG_TARGET_HAS_ctz_*). */ tcg_out_op_rrr(s, opc, args[0], args[1], args[2]); From cd9acd2049a385e54314d58f35b4bfce7c031d55 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 20:25:14 -0800 Subject: [PATCH 0411/2760] tcg: Merge INDEX_op_remu_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 5 +---- tcg/optimize.c | 9 +++++---- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 6 ++---- tcg/tci.c | 4 ++-- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 16 insertions(+), 20 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 1f4160a585..bceecb0596 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -292,7 +292,7 @@ Arithmetic - | *t0* = *t1* % *t2* (signed) | Undefined behavior if division by zero or overflow. - * - remu_i32/i64 *t0*, *t1*, *t2* + * - remu *t0*, *t1*, *t2* - | *t0* = *t1* % *t2* (unsigned) | Undefined behavior if division by zero. diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 040f4da835..ebb23347e9 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -57,6 +57,7 @@ DEF(not, 1, 1, 0, TCG_OPF_INT) DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) DEF(rems, 1, 2, 0, TCG_OPF_INT) +DEF(remu, 1, 2, 0, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) @@ -72,8 +73,6 @@ DEF(ld_i32, 1, 1, 1, 0) DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) -/* arith */ -DEF(remu_i32, 1, 2, 0, 0) /* shifts/rotates */ DEF(shl_i32, 1, 2, 0, 0) DEF(shr_i32, 1, 2, 0, 0) @@ -115,8 +114,6 @@ DEF(st8_i64, 0, 2, 1, 0) DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) -/* arith */ -DEF(remu_i64, 1, 2, 0, 0) /* shifts/rotates */ DEF(shl_i64, 1, 2, 0, 0) DEF(shr_i64, 1, 2, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index 01ec365175..69f9ba1555 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -575,9 +575,10 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, } return (int64_t)x % ((int64_t)y ? : 1); - case INDEX_op_remu_i32: - return (uint32_t)x % ((uint32_t)y ? : 1); - case INDEX_op_remu_i64: + case INDEX_op_remu: + if (type == TCG_TYPE_I32) { + return (uint32_t)x % ((uint32_t)y ? : 1); + } return (uint64_t)x % ((uint64_t)y ? : 1); default: @@ -3024,7 +3025,7 @@ void tcg_optimize(TCGContext *s) done = fold_qemu_st(&ctx, op); break; case INDEX_op_rems: - CASE_OP_32_64(remu): + case INDEX_op_remu: done = fold_remainder(&ctx, op); break; CASE_OP_32_64(rotl): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 4ff6c9f0ab..0f1e83a49f 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -649,8 +649,8 @@ void tcg_gen_divu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_remu_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_remu_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3_i32(INDEX_op_remu_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_remu, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_remu, ret, arg1, arg2); } else if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_divu, t0, arg1, arg2); @@ -2017,8 +2017,8 @@ void tcg_gen_divu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_remu_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (tcg_op_supported(INDEX_op_remu_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3_i64(INDEX_op_remu_i64, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_remu, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_remu, ret, arg1, arg2); } else if (tcg_op_supported(INDEX_op_divu, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_divu, t0, arg1, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index 537b665d07..cd89ef1faa 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1041,8 +1041,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), - OUTOP(INDEX_op_remu_i32, TCGOutOpBinary, outop_remu), - OUTOP(INDEX_op_remu_i64, TCGOutOpBinary, outop_remu), + OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), }; @@ -5423,8 +5422,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_or: case INDEX_op_orc: case INDEX_op_rems: - case INDEX_op_remu_i32: - case INDEX_op_remu_i64: + case INDEX_op_remu: case INDEX_op_xor: { const TCGOutOpBinary *out = diff --git a/tcg/tci.c b/tcg/tci.c index bd5817a382..5d2cba4941 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -732,7 +732,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int64_t)regs[r1] % (int64_t)regs[r2]; break; - case INDEX_op_remu_i64: + case INDEX_op_remu: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint64_t)regs[r1] % (uint64_t)regs[r2]; break; @@ -1080,9 +1080,9 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_or: case INDEX_op_orc: case INDEX_op_rems: + case INDEX_op_remu: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_remu_i64: case INDEX_op_shl_i32: case INDEX_op_shl_i64: case INDEX_op_shr_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 421a2a8ac7..eb30fd04ba 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -769,7 +769,7 @@ static void tgen_remu(TCGContext *s, TCGType type, { TCGOpcode opc = (type == TCG_TYPE_I32 ? INDEX_op_tci_remu32 - : INDEX_op_remu_i64); + : INDEX_op_remu); tcg_out_op_rrr(s, opc, a0, a1, a2); } From 27d21ee7c79133e5dfdab8f160f547e833dc5dd0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 21:33:33 -0800 Subject: [PATCH 0412/2760] tcg: Convert shl to TCGOutOpBinary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 38 ++++++++++---------- tcg/arm/tcg-target.c.inc | 25 +++++++++---- tcg/i386/tcg-target.c.inc | 60 +++++++++++++++++++++++--------- tcg/loongarch64/tcg-target.c.inc | 43 ++++++++++++++--------- tcg/mips/tcg-target.c.inc | 35 ++++++++++++------- tcg/ppc/tcg-target.c.inc | 42 ++++++++++++---------- tcg/riscv/tcg-target.c.inc | 38 +++++++++++--------- tcg/s390x/tcg-target.c.inc | 37 ++++++++++++++++---- tcg/sparc64/tcg-target.c.inc | 27 ++++++++++---- tcg/tcg.c | 6 ++-- tcg/tci/tcg-target.c.inc | 14 ++++++-- 11 files changed, 241 insertions(+), 124 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 8aa11e9d9d..b57baa1eec 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1347,14 +1347,6 @@ static inline void tcg_out_extr(TCGContext *s, TCGType ext, TCGReg rd, tcg_out_insn(s, 3403, EXTR, ext, rd, rn, rm, a); } -static inline void tcg_out_shl(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int bits = ext ? 64 : 32; - int max = bits - 1; - tcg_out_ubfm(s, ext, rd, rn, (bits - m) & max, (max - m) & max); -} - static inline void tcg_out_shr(TCGContext *s, TCGType ext, TCGReg rd, TCGReg rn, unsigned int m) { @@ -2299,6 +2291,25 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, LSLV, type, a0, a1, a2); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int max = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_ubfm(s, type, a0, a1, -a2 & max, ~a2 & max); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2416,15 +2427,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_shl_i64: - case INDEX_op_shl_i32: - if (c2) { - tcg_out_shl(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3508, LSLV, ext, a0, a1, a2); - } - break; - case INDEX_op_shr_i64: case INDEX_op_shr_i32: if (c2) { @@ -3091,12 +3093,10 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rC); - case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: - case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: case INDEX_op_rotl_i64: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index c08cd712b1..2b9e52914c 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1963,6 +1963,25 @@ static const TCGOutOpBinary outop_remu = { .base.static_constraint = C_NotImplemented, }; +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, SHIFT_REG_LSL(a2)); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, + SHIFT_IMM_LSL(a2 & 0x1f)); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2114,11 +2133,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_muls2_i32: tcg_out_smull32(s, COND_AL, args[0], args[1], args[2], args[3]); break; - /* XXX: Perhaps args[2] & 0x1f is wrong */ - case INDEX_op_shl_i32: - c = const_args[2] ? - SHIFT_IMM_LSL(args[2] & 0x1f) : SHIFT_REG_LSL(args[2]); - goto gen_shift32; case INDEX_op_shr_i32: c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_LSR(args[2] & 0x1f) : SHIFT_IMM_LSL(0) : SHIFT_REG_LSR(args[2]); @@ -2300,7 +2314,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i32: return C_O2_I2(r, r, r, r); - case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: case INDEX_op_rotl_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 02dd440052..648d9ee66c 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2744,6 +2744,49 @@ static const TCGOutOpBinary outop_remu = { .base.static_constraint = C_NotImplemented, }; +static TCGConstraintSetIndex cset_shift(TCGType type, unsigned flags) +{ + return have_bmi2 ? C_O1_I2(r, r, ri) : C_O1_I2(r, 0, ci); +} + +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + if (have_bmi2) { + tcg_out_vex_modrm(s, OPC_SHLX + rexw, a0, a2, a1); + } else { + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, SHIFT_SHL, a0); + } +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + /* For small constant 3-operand shift, use LEA. */ + if (a0 != a1 && a2 >= 1 && a2 <= 3) { + if (a2 == 1) { + /* shl $1,a1,a0 -> lea (a1,a1),a0 */ + tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a1, 0, 0); + } else { + /* shl $n,a1,a0 -> lea 0(,a1,n),a0 */ + tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, -1, a1, a2, 0); + } + return; + } + tcg_out_mov(s, type, a0, a1); + tcg_out_shifti(s, SHIFT_SHL + rexw, a0, a2); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_shift, + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2879,21 +2922,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(shl): - /* For small constant 3-operand shift, use LEA. */ - if (const_a2 && a0 != a1 && (a2 - 1) < 3) { - if (a2 - 1 == 0) { - /* shl $1,a1,a0 -> lea (a1,a1),a0 */ - tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, a1, a1, 0, 0); - } else { - /* shl $n,a1,a0 -> lea 0(,a1,n),a0 */ - tcg_out_modrm_sib_offset(s, OPC_LEA + rexw, a0, -1, a1, a2, 0); - } - break; - } - c = SHIFT_SHL; - vexop = OPC_SHLX; - goto gen_shift_maybe_vex; OP_32_64(shr): c = SHIFT_SHR; vexop = OPC_SHRX; @@ -3759,8 +3787,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_shl_i32: - case INDEX_op_shl_i64: case INDEX_op_shr_i32: case INDEX_op_shr_i64: case INDEX_op_sar_i32: diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index a0313b1140..9e34c37e62 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1489,6 +1489,32 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sll_w(s, a0, a1, a2); + } else { + tcg_out_opc_sll_d(s, a0, a1, a2); + } +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_slli_w(s, a0, a1, a2 & 0x1f); + } else { + tcg_out_opc_slli_d(s, a0, a1, a2 & 0x3f); + } +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1660,21 +1686,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_clzctz(s, OPC_CTZ_D, a0, a1, a2, c2, false); break; - case INDEX_op_shl_i32: - if (c2) { - tcg_out_opc_slli_w(s, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_sll_w(s, a0, a1, a2); - } - break; - case INDEX_op_shl_i64: - if (c2) { - tcg_out_opc_slli_d(s, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_sll_d(s, a0, a1, a2); - } - break; - case INDEX_op_shr_i32: if (c2) { tcg_out_opc_srli_w(s, a0, a1, a2 & 0x1f); @@ -2369,8 +2380,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_shl_i32: - case INDEX_op_shl_i64: case INDEX_op_shr_i32: case INDEX_op_shr_i64: case INDEX_op_sar_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index bd38c7ab95..30d8872b4f 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1908,6 +1908,29 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_SLLV : OPC_DSLLV; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sa(s, OPC_SLL, a0, a1, a2); + } else { + tcg_out_dsll(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2068,9 +2091,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_sar_i32: i1 = OPC_SRAV, i2 = OPC_SRA; goto do_shift; - case INDEX_op_shl_i32: - i1 = OPC_SLLV, i2 = OPC_SLL; - goto do_shift; case INDEX_op_shr_i32: i1 = OPC_SRLV, i2 = OPC_SRL; goto do_shift; @@ -2099,13 +2119,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } i1 = OPC_DSRAV; goto do_shiftv; - case INDEX_op_shl_i64: - if (c2) { - tcg_out_dsll(s, a0, a1, a2); - break; - } - i1 = OPC_DSLLV; - goto do_shiftv; case INDEX_op_shr_i64: if (c2) { tcg_out_dsrl(s, a0, a1, a2); @@ -2293,12 +2306,10 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i64: case INDEX_op_mulu2_i64: return C_O2_I2(r, r, r, r); - case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: case INDEX_op_rotr_i32: case INDEX_op_rotl_i32: - case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: case INDEX_op_rotr_i64: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 80ee4d04c9..88cfcd1d91 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3122,6 +3122,30 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SLW : SLD; + tcg_out32(s, insn | SAB(a1, a0, a2)); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* Limit immediate shift count lest we create an illegal insn. */ + if (type == TCG_TYPE_I32) { + tcg_out_shli32(s, a0, a1, a2 & 31); + } else { + tcg_out_shli64(s, a0, a1, a2 & 63); + } +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3272,14 +3296,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, CNTPOPD | SAB(args[1], args[0], 0)); break; - case INDEX_op_shl_i32: - if (const_args[2]) { - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_shli32(s, args[0], args[1], args[2] & 31); - } else { - tcg_out32(s, SLW | SAB(args[1], args[0], args[2])); - } - break; case INDEX_op_shr_i32: if (const_args[2]) { /* Limit immediate shift count lest we create an illegal insn. */ @@ -3325,14 +3341,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_brcond2(s, args, const_args); break; - case INDEX_op_shl_i64: - if (const_args[2]) { - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_shli64(s, args[0], args[1], args[2] & 63); - } else { - tcg_out32(s, SLD | SAB(args[1], args[0], args[2])); - } - break; case INDEX_op_shr_i64: if (const_args[2]) { /* Limit immediate shift count lest we create an illegal insn. */ @@ -4206,12 +4214,10 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: - case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: case INDEX_op_rotl_i64: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 38ba898042..372c4e1651 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2144,6 +2144,27 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SLLW : OPC_SLL; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SLLIW : OPC_SLLI; + unsigned mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_opc_imm(s, insn, a0, a1, a2 & mask); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2257,21 +2278,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_shl_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_SLLIW, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_reg(s, OPC_SLLW, a0, a1, a2); - } - break; - case INDEX_op_shl_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_SLLI, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_reg(s, OPC_SLL, a0, a1, a2); - } - break; - case INDEX_op_shr_i32: if (c2) { tcg_out_opc_imm(s, OPC_SRLIW, a0, a1, a2 & 0x1f); @@ -2758,12 +2764,10 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: - case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: case INDEX_op_rotl_i64: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 8702d8c928..ed68054664 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2445,6 +2445,36 @@ static const TCGOutOpBinary outop_remu = { .base.static_constraint = C_NotImplemented, }; +static void tgen_shl_int(TCGContext *s, TCGType type, TCGReg dst, + TCGReg src, TCGReg v, tcg_target_long i) +{ + if (type != TCG_TYPE_I32) { + tcg_out_sh64(s, RSY_SLLG, dst, src, v, i); + } else if (dst == src) { + tcg_out_sh32(s, RS_SLL, dst, v, i); + } else { + tcg_out_sh64(s, RSY_SLLK, dst, src, v, i); + } +} + +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_shl_int(s, type, a0, a1, a2, 0); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_shl_int(s, type, a0, a1, TCG_REG_NONE, a2); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2574,9 +2604,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_shl_i32: - op = RS_SLL; - op2 = RSY_SLLK; do_shift32: a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; if (a0 == a1) { @@ -2746,8 +2773,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_insn(s, RRFa, MGRK, args[1], args[2], args[3]); break; - case INDEX_op_shl_i64: - op = RSY_SLLG; do_shift64: if (const_args[2]) { tcg_out_sh64(s, op, args[0], args[1], TCG_REG_NONE, args[2]); @@ -3346,7 +3371,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: case INDEX_op_rotl_i32: @@ -3363,7 +3387,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_clz_i64: return C_O1_I2(r, r, rI); - case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: return C_O1_I2(r, r, ri); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index d465c8dd06..6b320a8622 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1484,6 +1484,27 @@ static const TCGOutOpBinary outop_remu = { .base.static_constraint = C_NotImplemented, }; +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SHIFT_SLL : SHIFT_SLLX; + tcg_out_arith(s, a0, a1, a2, insn); +} + +static void tgen_shli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SHIFT_SLL : SHIFT_SLLX; + uint32_t mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_arithi(s, a0, a1, a2 & mask, insn); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_shl, + .out_rri = tgen_shli, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1587,8 +1608,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_st32_i64: tcg_out_ldst(s, a0, a1, a2, STW); break; - case INDEX_op_shl_i32: - c = SHIFT_SLL; do_shift32: /* Limit immediate shift count lest we create an illegal insn. */ tcg_out_arithc(s, a0, a1, a2 & 31, c2, c); @@ -1656,8 +1675,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_st_i64: tcg_out_ldst(s, a0, a1, a2, STX); break; - case INDEX_op_shl_i64: - c = SHIFT_SLLX; do_shift64: /* Limit immediate shift count lest we create an illegal insn. */ tcg_out_arithc(s, a0, a1, a2 & 63, c2, c); @@ -1751,8 +1768,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); - case INDEX_op_shl_i32: - case INDEX_op_shl_i64: case INDEX_op_shr_i32: case INDEX_op_shr_i64: case INDEX_op_sar_i32: diff --git a/tcg/tcg.c b/tcg/tcg.c index cd89ef1faa..369a1e6d48 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1042,6 +1042,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), + OUTOP(INDEX_op_shl_i32, TCGOutOpBinary, outop_shl), + OUTOP(INDEX_op_shl_i64, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), }; @@ -2262,7 +2264,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: - case INDEX_op_shl_i32: case INDEX_op_shr_i32: case INDEX_op_sar_i32: case INDEX_op_extract_i32: @@ -2314,7 +2315,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st16_i64: case INDEX_op_st32_i64: case INDEX_op_st_i64: - case INDEX_op_shl_i64: case INDEX_op_shr_i64: case INDEX_op_sar_i64: case INDEX_op_ext_i32_i64: @@ -5423,6 +5423,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_orc: case INDEX_op_rems: case INDEX_op_remu: + case INDEX_op_shl_i32: + case INDEX_op_shl_i64: case INDEX_op_xor: { const TCGOutOpBinary *out = diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index eb30fd04ba..748bb8118f 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -79,8 +79,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_shl_i32: - case INDEX_op_shl_i64: case INDEX_op_shr_i32: case INDEX_op_shr_i64: case INDEX_op_sar_i32: @@ -778,6 +776,17 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_shl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, glue(INDEX_op_shl_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_shl = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_shl, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -862,7 +871,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, opc, args[0], args[1], args[2]); break; - CASE_32_64(shl) CASE_32_64(shr) CASE_32_64(sar) CASE_32_64(rotl) /* Optional (TCG_TARGET_HAS_rot_*). */ From 6ca594517ab389f3095c4aab745e168cdd8e8ff5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 21:50:04 -0800 Subject: [PATCH 0413/2760] tcg: Merge INDEX_op_shl_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 4 ++-- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 10 +++++----- tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 6 ++---- tcg/tci.c | 13 ++++--------- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 17 insertions(+), 25 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index bceecb0596..f64c881530 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -379,10 +379,10 @@ Shifts/Rotates .. list-table:: - * - shl_i32/i64 *t0*, *t1*, *t2* + * - shl *t0*, *t1*, *t2* - | *t0* = *t1* << *t2* - | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + | Unspecified behavior for negative or out-of-range shifts. * - shr_i32/i64 *t0*, *t1*, *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index ebb23347e9..c2ac25d1b6 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -58,6 +58,7 @@ DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) DEF(rems, 1, 2, 0, TCG_OPF_INT) DEF(remu, 1, 2, 0, TCG_OPF_INT) +DEF(shl, 1, 2, 0, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) @@ -74,7 +75,6 @@ DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* shifts/rotates */ -DEF(shl_i32, 1, 2, 0, 0) DEF(shr_i32, 1, 2, 0, 0) DEF(sar_i32, 1, 2, 0, 0) DEF(rotl_i32, 1, 2, 0, 0) @@ -115,7 +115,6 @@ DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* shifts/rotates */ -DEF(shl_i64, 1, 2, 0, 0) DEF(shr_i64, 1, 2, 0, 0) DEF(sar_i64, 1, 2, 0, 0) DEF(rotl_i64, 1, 2, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index 69f9ba1555..3142daa800 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -446,10 +446,10 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, case INDEX_op_xor_vec: return x ^ y; - case INDEX_op_shl_i32: - return (uint32_t)x << (y & 31); - - case INDEX_op_shl_i64: + case INDEX_op_shl: + if (type == TCG_TYPE_I32) { + return (uint32_t)x << (y & 31); + } return (uint64_t)x << (y & 63); case INDEX_op_shr_i32: @@ -3031,7 +3031,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(rotl): CASE_OP_32_64(rotr): CASE_OP_32_64(sar): - CASE_OP_32_64(shl): + case INDEX_op_shl: CASE_OP_32_64(shr): done = fold_shift(&ctx, op); break; diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 0f1e83a49f..c85c056726 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -481,7 +481,7 @@ void tcg_gen_not_i32(TCGv_i32 ret, TCGv_i32 arg) void tcg_gen_shl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - tcg_gen_op3_i32(INDEX_op_shl_i32, ret, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_shl, ret, arg1, arg2); } void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) @@ -1606,7 +1606,7 @@ void tcg_gen_xor_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op3_i64(INDEX_op_shl_i64, ret, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_shl, ret, arg1, arg2); } else { gen_helper_shl_i64(ret, arg1, arg2); } diff --git a/tcg/tcg.c b/tcg/tcg.c index 369a1e6d48..50935b7e03 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1042,8 +1042,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), - OUTOP(INDEX_op_shl_i32, TCGOutOpBinary, outop_shl), - OUTOP(INDEX_op_shl_i64, TCGOutOpBinary, outop_shl), + OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), }; @@ -5423,8 +5422,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_orc: case INDEX_op_rems: case INDEX_op_remu: - case INDEX_op_shl_i32: - case INDEX_op_shl_i64: + case INDEX_op_shl: case INDEX_op_xor: { const TCGOutOpBinary *out = diff --git a/tcg/tci.c b/tcg/tci.c index 5d2cba4941..22401ce1f6 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -615,11 +615,11 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, break; #endif - /* Shift/rotate operations (32 bit). */ + /* Shift/rotate operations. */ - case INDEX_op_shl_i32: + case INDEX_op_shl: tci_args_rrr(insn, &r0, &r1, &r2); - regs[r0] = (uint32_t)regs[r1] << (regs[r2] & 31); + regs[r0] = regs[r1] << (regs[r2] % TCG_TARGET_REG_BITS); break; case INDEX_op_shr_i32: tci_args_rrr(insn, &r0, &r1, &r2); @@ -787,10 +787,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, /* Shift/rotate operations (64 bit). */ - case INDEX_op_shl_i64: - tci_args_rrr(insn, &r0, &r1, &r2); - regs[r0] = regs[r1] << (regs[r2] & 63); - break; case INDEX_op_shr_i64: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] >> (regs[r2] & 63); @@ -1081,10 +1077,9 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_orc: case INDEX_op_rems: case INDEX_op_remu: + case INDEX_op_shl: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_shl_i32: - case INDEX_op_shl_i64: case INDEX_op_shr_i32: case INDEX_op_shr_i64: case INDEX_op_sar_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 748bb8118f..ca83a097ab 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -779,7 +779,7 @@ static const TCGOutOpBinary outop_remu = { static void tgen_shl(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { - tcg_out_op_rrr(s, glue(INDEX_op_shl_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_shl, a0, a1, a2); } static const TCGOutOpBinary outop_shl = { From edd6ba8a6bc804153a8fe643a7e2dae0802db98c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 22:22:36 -0800 Subject: [PATCH 0414/2760] tcg: Convert shr to TCGOutOpBinary Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 37 ++++++++++++++------------- tcg/arm/tcg-target.c.inc | 24 ++++++++++++++---- tcg/i386/tcg-target.c.inc | 33 +++++++++++++++++++----- tcg/loongarch64/tcg-target.c.inc | 43 +++++++++++++++++++------------- tcg/mips/tcg-target.c.inc | 35 +++++++++++++++++--------- tcg/ppc/tcg-target.c.inc | 42 ++++++++++++++++++------------- tcg/riscv/tcg-target.c.inc | 38 +++++++++++++++------------- tcg/s390x/tcg-target.c.inc | 39 ++++++++++++++++++++++------- tcg/sparc64/tcg-target.c.inc | 29 +++++++++++++++------ tcg/tcg.c | 6 +++-- tcg/tci/tcg-target.c.inc | 18 ++++++++++--- 11 files changed, 229 insertions(+), 115 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index b57baa1eec..87b97e852a 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1347,13 +1347,6 @@ static inline void tcg_out_extr(TCGContext *s, TCGType ext, TCGReg rd, tcg_out_insn(s, 3403, EXTR, ext, rd, rn, rm, a); } -static inline void tcg_out_shr(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int max = ext ? 63 : 31; - tcg_out_ubfm(s, ext, rd, rn, m & max, max); -} - static inline void tcg_out_sar(TCGContext *s, TCGType ext, TCGReg rd, TCGReg rn, unsigned int m) { @@ -2310,6 +2303,25 @@ static const TCGOutOpBinary outop_shl = { .out_rri = tgen_shli, }; +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, LSRV, type, a0, a1, a2); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int max = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_ubfm(s, type, a0, a1, a2 & max, max); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2427,15 +2439,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_shr_i64: - case INDEX_op_shr_i32: - if (c2) { - tcg_out_shr(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3508, LSRV, ext, a0, a1, a2); - } - break; - case INDEX_op_sar_i64: case INDEX_op_sar_i32: if (c2) { @@ -3093,11 +3096,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rC); - case INDEX_op_shr_i32: case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: - case INDEX_op_shr_i64: case INDEX_op_sar_i64: case INDEX_op_rotl_i64: case INDEX_op_rotr_i64: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 2b9e52914c..247aefd0a1 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1982,6 +1982,25 @@ static const TCGOutOpBinary outop_shl = { .out_rri = tgen_shli, }; +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, SHIFT_REG_LSR(a2)); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, + SHIFT_IMM_LSR(a2 & 0x1f)); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2133,10 +2152,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_muls2_i32: tcg_out_smull32(s, COND_AL, args[0], args[1], args[2], args[3]); break; - case INDEX_op_shr_i32: - c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_LSR(args[2] & 0x1f) : - SHIFT_IMM_LSL(0) : SHIFT_REG_LSR(args[2]); - goto gen_shift32; case INDEX_op_sar_i32: c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ASR(args[2] & 0x1f) : SHIFT_IMM_LSL(0) : SHIFT_REG_ASR(args[2]); @@ -2314,7 +2329,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i32: return C_O2_I2(r, r, r, r); - case INDEX_op_shr_i32: case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 648d9ee66c..93d94e7881 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2787,6 +2787,33 @@ static const TCGOutOpBinary outop_shl = { .out_rri = tgen_shli, }; +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + if (have_bmi2) { + tcg_out_vex_modrm(s, OPC_SHRX + rexw, a0, a2, a1); + } else { + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, SHIFT_SHR, a0); + } +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + tcg_out_mov(s, type, a0, a1); + tcg_out_shifti(s, SHIFT_SHR + rexw, a0, a2); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_shift, + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2922,10 +2949,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(shr): - c = SHIFT_SHR; - vexop = OPC_SHRX; - goto gen_shift_maybe_vex; OP_32_64(sar): c = SHIFT_SAR; vexop = OPC_SARX; @@ -3787,8 +3810,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_shr_i32: - case INDEX_op_shr_i64: case INDEX_op_sar_i32: case INDEX_op_sar_i64: return have_bmi2 ? C_O1_I2(r, r, ri) : C_O1_I2(r, 0, ci); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 9e34c37e62..2699079073 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1515,6 +1515,32 @@ static const TCGOutOpBinary outop_shl = { .out_rri = tgen_shli, }; +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_srl_w(s, a0, a1, a2); + } else { + tcg_out_opc_srl_d(s, a0, a1, a2); + } +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_srli_w(s, a0, a1, a2 & 0x1f); + } else { + tcg_out_opc_srli_d(s, a0, a1, a2 & 0x3f); + } +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1686,21 +1712,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_clzctz(s, OPC_CTZ_D, a0, a1, a2, c2, false); break; - case INDEX_op_shr_i32: - if (c2) { - tcg_out_opc_srli_w(s, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_srl_w(s, a0, a1, a2); - } - break; - case INDEX_op_shr_i64: - if (c2) { - tcg_out_opc_srli_d(s, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_srl_d(s, a0, a1, a2); - } - break; - case INDEX_op_sar_i32: if (c2) { tcg_out_opc_srai_w(s, a0, a1, a2 & 0x1f); @@ -2380,8 +2391,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_shr_i32: - case INDEX_op_shr_i64: case INDEX_op_sar_i32: case INDEX_op_sar_i64: case INDEX_op_rotl_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 30d8872b4f..03b4248ea9 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1931,6 +1931,29 @@ static const TCGOutOpBinary outop_shl = { .out_rri = tgen_shli, }; +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_SRLV : OPC_DSRLV; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sa(s, OPC_SRL, a0, a1, a2); + } else { + tcg_out_dsrl(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2091,9 +2114,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_sar_i32: i1 = OPC_SRAV, i2 = OPC_SRA; goto do_shift; - case INDEX_op_shr_i32: - i1 = OPC_SRLV, i2 = OPC_SRL; - goto do_shift; case INDEX_op_rotr_i32: i1 = OPC_ROTRV, i2 = OPC_ROTR; do_shift: @@ -2119,13 +2139,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } i1 = OPC_DSRAV; goto do_shiftv; - case INDEX_op_shr_i64: - if (c2) { - tcg_out_dsrl(s, a0, a1, a2); - break; - } - i1 = OPC_DSRLV; - goto do_shiftv; case INDEX_op_rotr_i64: if (c2) { tcg_out_opc_sa64(s, OPC_DROTR, OPC_DROTR32, a0, a1, a2); @@ -2306,11 +2319,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i64: case INDEX_op_mulu2_i64: return C_O2_I2(r, r, r, r); - case INDEX_op_shr_i32: case INDEX_op_sar_i32: case INDEX_op_rotr_i32: case INDEX_op_rotl_i32: - case INDEX_op_shr_i64: case INDEX_op_sar_i64: case INDEX_op_rotr_i64: case INDEX_op_rotl_i64: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 88cfcd1d91..2012734bb3 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3146,6 +3146,30 @@ static const TCGOutOpBinary outop_shl = { .out_rri = tgen_shli, }; +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SRW : SRD; + tcg_out32(s, insn | SAB(a1, a0, a2)); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* Limit immediate shift count lest we create an illegal insn. */ + if (type == TCG_TYPE_I32) { + tcg_out_shri32(s, a0, a1, a2 & 31); + } else { + tcg_out_shri64(s, a0, a1, a2 & 63); + } +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3296,14 +3320,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, CNTPOPD | SAB(args[1], args[0], 0)); break; - case INDEX_op_shr_i32: - if (const_args[2]) { - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_shri32(s, args[0], args[1], args[2] & 31); - } else { - tcg_out32(s, SRW | SAB(args[1], args[0], args[2])); - } - break; case INDEX_op_sar_i32: if (const_args[2]) { tcg_out_sari32(s, args[0], args[1], args[2]); @@ -3341,14 +3357,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_brcond2(s, args, const_args); break; - case INDEX_op_shr_i64: - if (const_args[2]) { - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_shri64(s, args[0], args[1], args[2] & 63); - } else { - tcg_out32(s, SRD | SAB(args[1], args[0], args[2])); - } - break; case INDEX_op_sar_i64: if (const_args[2]) { tcg_out_sari64(s, args[0], args[1], args[2]); @@ -4214,11 +4222,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_shr_i32: case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: - case INDEX_op_shr_i64: case INDEX_op_sar_i64: case INDEX_op_rotl_i64: case INDEX_op_rotr_i64: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 372c4e1651..8020cc0b3f 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2165,6 +2165,27 @@ static const TCGOutOpBinary outop_shl = { .out_rri = tgen_shli, }; +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SRLW : OPC_SRL; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SRLIW : OPC_SRLI; + unsigned mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_opc_imm(s, insn, a0, a1, a2 & mask); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2278,21 +2299,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_shr_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_SRLIW, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_reg(s, OPC_SRLW, a0, a1, a2); - } - break; - case INDEX_op_shr_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_SRLI, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_reg(s, OPC_SRL, a0, a1, a2); - } - break; - case INDEX_op_sar_i32: if (c2) { tcg_out_opc_imm(s, OPC_SRAIW, a0, a1, a2 & 0x1f); @@ -2764,11 +2770,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_shr_i32: case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: - case INDEX_op_shr_i64: case INDEX_op_sar_i64: case INDEX_op_rotl_i64: case INDEX_op_rotr_i64: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index ed68054664..0417bbef50 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2475,6 +2475,36 @@ static const TCGOutOpBinary outop_shl = { .out_rri = tgen_shli, }; +static void tgen_shr_int(TCGContext *s, TCGType type, TCGReg dst, + TCGReg src, TCGReg v, tcg_target_long i) +{ + if (type != TCG_TYPE_I32) { + tcg_out_sh64(s, RSY_SRLG, dst, src, v, i); + } else if (dst == src) { + tcg_out_sh32(s, RS_SRL, dst, v, i); + } else { + tcg_out_sh64(s, RSY_SRLK, dst, src, v, i); + } +} + +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_shr_int(s, type, a0, a1, a2, 0); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_shr_int(s, type, a0, a1, TCG_REG_NONE, a2); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2621,10 +2651,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } } break; - case INDEX_op_shr_i32: - op = RS_SRL; - op2 = RSY_SRLK; - goto do_shift32; case INDEX_op_sar_i32: op = RS_SRA; op2 = RSY_SRAK; @@ -2780,9 +2806,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_sh64(s, op, args[0], args[1], args[2], 0); } break; - case INDEX_op_shr_i64: - op = RSY_SRLG; - goto do_shift64; case INDEX_op_sar_i64: op = RSY_SRAG; goto do_shift64; @@ -3371,7 +3394,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_shr_i64: case INDEX_op_sar_i64: case INDEX_op_rotl_i32: case INDEX_op_rotl_i64: @@ -3387,7 +3409,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_clz_i64: return C_O1_I2(r, r, rI); - case INDEX_op_shr_i32: case INDEX_op_sar_i32: return C_O1_I2(r, r, ri); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 6b320a8622..f679fa04ea 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1505,6 +1505,27 @@ static const TCGOutOpBinary outop_shl = { .out_rri = tgen_shli, }; +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SHIFT_SRL : SHIFT_SRLX; + tcg_out_arith(s, a0, a1, a2, insn); +} + +static void tgen_shri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SHIFT_SRL : SHIFT_SRLX; + uint32_t mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_arithi(s, a0, a1, a2 & mask, insn); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_shr, + .out_rri = tgen_shri, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1612,9 +1633,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, /* Limit immediate shift count lest we create an illegal insn. */ tcg_out_arithc(s, a0, a1, a2 & 31, c2, c); break; - case INDEX_op_shr_i32: - c = SHIFT_SRL; - goto do_shift32; case INDEX_op_sar_i32: c = SHIFT_SRA; goto do_shift32; @@ -1679,9 +1697,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, /* Limit immediate shift count lest we create an illegal insn. */ tcg_out_arithc(s, a0, a1, a2 & 63, c2, c); break; - case INDEX_op_shr_i64: - c = SHIFT_SRLX; - goto do_shift64; case INDEX_op_sar_i64: c = SHIFT_SRAX; goto do_shift64; @@ -1768,8 +1783,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); - case INDEX_op_shr_i32: - case INDEX_op_shr_i64: case INDEX_op_sar_i32: case INDEX_op_sar_i64: case INDEX_op_setcond_i32: diff --git a/tcg/tcg.c b/tcg/tcg.c index 50935b7e03..134ab9c6c2 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1043,6 +1043,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), + OUTOP(INDEX_op_shr_i32, TCGOutOpBinary, outop_shr), + OUTOP(INDEX_op_shr_i64, TCGOutOpBinary, outop_shr), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), }; @@ -2263,7 +2265,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: - case INDEX_op_shr_i32: case INDEX_op_sar_i32: case INDEX_op_extract_i32: case INDEX_op_sextract_i32: @@ -2314,7 +2315,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st16_i64: case INDEX_op_st32_i64: case INDEX_op_st_i64: - case INDEX_op_shr_i64: case INDEX_op_sar_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: @@ -5423,6 +5423,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_rems: case INDEX_op_remu: case INDEX_op_shl: + case INDEX_op_shr_i32: + case INDEX_op_shr_i64: case INDEX_op_xor: { const TCGOutOpBinary *out = diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index ca83a097ab..5651833ac9 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -79,8 +79,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_shr_i32: - case INDEX_op_shr_i64: case INDEX_op_sar_i32: case INDEX_op_sar_i64: case INDEX_op_rotl_i32: @@ -787,6 +785,21 @@ static const TCGOutOpBinary outop_shl = { .out_rrr = tgen_shl, }; +static void tgen_shr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type < TCG_TYPE_REG) { + tcg_out_ext32u(s, TCG_REG_TMP, a1); + a1 = TCG_REG_TMP; + } + tcg_out_op_rrr(s, glue(INDEX_op_shr_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_shr = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_shr, +}; + static void tgen_sub(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -871,7 +884,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, opc, args[0], args[1], args[2]); break; - CASE_32_64(shr) CASE_32_64(sar) CASE_32_64(rotl) /* Optional (TCG_TARGET_HAS_rot_*). */ CASE_32_64(rotr) /* Optional (TCG_TARGET_HAS_rot_*). */ From 74dbd36f1f87bd7fc4705644d63c5561a23b0567 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 22:52:10 -0800 Subject: [PATCH 0415/2760] tcg: Merge INDEX_op_shr_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 4 ++-- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 17 +++++++---------- tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 6 ++---- tcg/tci.c | 11 +++-------- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 18 insertions(+), 29 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index f64c881530..f9fd4b0087 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -384,10 +384,10 @@ Shifts/Rotates - | *t0* = *t1* << *t2* | Unspecified behavior for negative or out-of-range shifts. - * - shr_i32/i64 *t0*, *t1*, *t2* + * - shr *t0*, *t1*, *t2* - | *t0* = *t1* >> *t2* (unsigned) - | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + | Unspecified behavior for negative or out-of-range shifts. * - sar_i32/i64 *t0*, *t1*, *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index c2ac25d1b6..35e0be8f80 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -59,6 +59,7 @@ DEF(orc, 1, 2, 0, TCG_OPF_INT) DEF(rems, 1, 2, 0, TCG_OPF_INT) DEF(remu, 1, 2, 0, TCG_OPF_INT) DEF(shl, 1, 2, 0, TCG_OPF_INT) +DEF(shr, 1, 2, 0, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) @@ -75,7 +76,6 @@ DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* shifts/rotates */ -DEF(shr_i32, 1, 2, 0, 0) DEF(sar_i32, 1, 2, 0, 0) DEF(rotl_i32, 1, 2, 0, 0) DEF(rotr_i32, 1, 2, 0, 0) @@ -115,7 +115,6 @@ DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* shifts/rotates */ -DEF(shr_i64, 1, 2, 0, 0) DEF(sar_i64, 1, 2, 0, 0) DEF(rotl_i64, 1, 2, 0, 0) DEF(rotr_i64, 1, 2, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index 3142daa800..43db079693 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -452,10 +452,10 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, } return (uint64_t)x << (y & 63); - case INDEX_op_shr_i32: - return (uint32_t)x >> (y & 31); - - case INDEX_op_shr_i64: + case INDEX_op_shr: + if (type == TCG_TYPE_I32) { + return (uint32_t)x >> (y & 31); + } return (uint64_t)x >> (y & 63); case INDEX_op_sar_i32: @@ -2342,7 +2342,6 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) { - TCGOpcode shr_opc; TCGOpcode uext_opc = 0, sext_opc = 0; TCGCond cond = op->args[3]; TCGArg ret, src1, src2; @@ -2364,7 +2363,6 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) switch (ctx->type) { case TCG_TYPE_I32: - shr_opc = INDEX_op_shr_i32; if (TCG_TARGET_extract_valid(TCG_TYPE_I32, sh, 1)) { uext_opc = INDEX_op_extract_i32; } @@ -2373,7 +2371,6 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) } break; case TCG_TYPE_I64: - shr_opc = INDEX_op_shr_i64; if (TCG_TARGET_extract_valid(TCG_TYPE_I64, sh, 1)) { uext_opc = INDEX_op_extract_i64; } @@ -2402,7 +2399,7 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) op->args[3] = 1; } else { if (sh) { - op2 = opt_insert_before(ctx, op, shr_opc, 3); + op2 = opt_insert_before(ctx, op, INDEX_op_shr, 3); op2->args[0] = ret; op2->args[1] = src1; op2->args[2] = arg_new_constant(ctx, sh); @@ -2609,7 +2606,7 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) * input sign repetitions. */ return fold_masks_s(ctx, op, s_mask); - CASE_OP_32_64(shr): + case INDEX_op_shr: /* * If the sign bit is known zero, then logical right shift * will not reduce the number of input sign repetitions. @@ -3032,7 +3029,7 @@ void tcg_optimize(TCGContext *s) CASE_OP_32_64(rotr): CASE_OP_32_64(sar): case INDEX_op_shl: - CASE_OP_32_64(shr): + case INDEX_op_shr: done = fold_shift(&ctx, op); break; CASE_OP_32_64(setcond): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index c85c056726..ef8cf5a1ac 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -496,7 +496,7 @@ void tcg_gen_shli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_shr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - tcg_gen_op3_i32(INDEX_op_shr_i32, ret, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_shr, ret, arg1, arg2); } void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) @@ -1615,7 +1615,7 @@ void tcg_gen_shl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op3_i64(INDEX_op_shr_i64, ret, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_shr, ret, arg1, arg2); } else { gen_helper_shr_i64(ret, arg1, arg2); } diff --git a/tcg/tcg.c b/tcg/tcg.c index 134ab9c6c2..939bbe86e9 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1043,8 +1043,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), - OUTOP(INDEX_op_shr_i32, TCGOutOpBinary, outop_shr), - OUTOP(INDEX_op_shr_i64, TCGOutOpBinary, outop_shr), + OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), }; @@ -5423,8 +5422,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_rems: case INDEX_op_remu: case INDEX_op_shl: - case INDEX_op_shr_i32: - case INDEX_op_shr_i64: + case INDEX_op_shr: case INDEX_op_xor: { const TCGOutOpBinary *out = diff --git a/tcg/tci.c b/tcg/tci.c index 22401ce1f6..376b1b1ece 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -621,9 +621,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] << (regs[r2] % TCG_TARGET_REG_BITS); break; - case INDEX_op_shr_i32: + case INDEX_op_shr: tci_args_rrr(insn, &r0, &r1, &r2); - regs[r0] = (uint32_t)regs[r1] >> (regs[r2] & 31); + regs[r0] = regs[r1] >> (regs[r2] % TCG_TARGET_REG_BITS); break; case INDEX_op_sar_i32: tci_args_rrr(insn, &r0, &r1, &r2); @@ -787,10 +787,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, /* Shift/rotate operations (64 bit). */ - case INDEX_op_shr_i64: - tci_args_rrr(insn, &r0, &r1, &r2); - regs[r0] = regs[r1] >> (regs[r2] & 63); - break; case INDEX_op_sar_i64: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (int64_t)regs[r1] >> (regs[r2] & 63); @@ -1078,10 +1074,9 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_rems: case INDEX_op_remu: case INDEX_op_shl: + case INDEX_op_shr: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_shr_i32: - case INDEX_op_shr_i64: case INDEX_op_sar_i32: case INDEX_op_sar_i64: case INDEX_op_rotl_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 5651833ac9..c0dbe873f1 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -792,7 +792,7 @@ static void tgen_shr(TCGContext *s, TCGType type, tcg_out_ext32u(s, TCG_REG_TMP, a1); a1 = TCG_REG_TMP; } - tcg_out_op_rrr(s, glue(INDEX_op_shr_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_shr, a0, a1, a2); } static const TCGOutOpBinary outop_shr = { From b5aafbaa834653abd4c208794bacbc53a4c1bc16 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 7 Jan 2025 23:36:22 -0800 Subject: [PATCH 0416/2760] tcg: Convert sar to TCGOutOpBinary Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 37 ++++++++--------- tcg/arm/tcg-target.c.inc | 26 ++++++++---- tcg/i386/tcg-target.c.inc | 46 ++++++++++++--------- tcg/loongarch64/tcg-target.c.inc | 43 ++++++++++++-------- tcg/mips/tcg-target.c.inc | 36 +++++++++++------ tcg/ppc/tcg-target.c.inc | 40 +++++++++++-------- tcg/riscv/tcg-target.c.inc | 38 ++++++++++-------- tcg/s390x/tcg-target.c.inc | 68 ++++++++++++++------------------ tcg/sparc64/tcg-target.c.inc | 37 +++++++++-------- tcg/tcg.c | 6 ++- tcg/tci/tcg-target.c.inc | 17 +++++++- 11 files changed, 230 insertions(+), 164 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 87b97e852a..90bdbf8387 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1347,13 +1347,6 @@ static inline void tcg_out_extr(TCGContext *s, TCGType ext, TCGReg rd, tcg_out_insn(s, 3403, EXTR, ext, rd, rn, rm, a); } -static inline void tcg_out_sar(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int max = ext ? 63 : 31; - tcg_out_sbfm(s, ext, rd, rn, m & max, max); -} - static inline void tcg_out_rotr(TCGContext *s, TCGType ext, TCGReg rd, TCGReg rn, unsigned int m) { @@ -2284,6 +2277,25 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, ASRV, type, a0, a1, a2); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int max = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_sbfm(s, type, a0, a1, a2 & max, max); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + static void tgen_shl(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2439,15 +2451,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_sar_i64: - case INDEX_op_sar_i32: - if (c2) { - tcg_out_sar(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3508, ASRV, ext, a0, a1, a2); - } - break; - case INDEX_op_rotr_i64: case INDEX_op_rotr_i32: if (c2) { @@ -3096,10 +3099,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rC); - case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: - case INDEX_op_sar_i64: case INDEX_op_rotl_i64: case INDEX_op_rotr_i64: return C_O1_I2(r, r, ri); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 247aefd0a1..058677650b 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1963,6 +1963,25 @@ static const TCGOutOpBinary outop_remu = { .base.static_constraint = C_NotImplemented, }; +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, SHIFT_REG_ASR(a2)); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, + SHIFT_IMM_ASR(a2 & 0x1f)); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + static void tgen_shl(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2152,15 +2171,9 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_muls2_i32: tcg_out_smull32(s, COND_AL, args[0], args[1], args[2], args[3]); break; - case INDEX_op_sar_i32: - c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ASR(args[2] & 0x1f) : - SHIFT_IMM_LSL(0) : SHIFT_REG_ASR(args[2]); - goto gen_shift32; case INDEX_op_rotr_i32: c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ROR(args[2] & 0x1f) : SHIFT_IMM_LSL(0) : SHIFT_REG_ROR(args[2]); - /* Fall through. */ - gen_shift32: tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], c); break; @@ -2329,7 +2342,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i32: return C_O2_I2(r, r, r, r); - case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: return C_O1_I2(r, r, ri); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 93d94e7881..1e81455461 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2749,6 +2749,33 @@ static TCGConstraintSetIndex cset_shift(TCGType type, unsigned flags) return have_bmi2 ? C_O1_I2(r, r, ri) : C_O1_I2(r, 0, ci); } +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + if (have_bmi2) { + tcg_out_vex_modrm(s, OPC_SARX + rexw, a0, a2, a1); + } else { + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, SHIFT_SAR, a0); + } +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + tcg_out_mov(s, type, a0, a1); + tcg_out_shifti(s, SHIFT_SAR + rexw, a0, a2); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_shift, + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + static void tgen_shl(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2874,7 +2901,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { TCGArg a0, a1, a2; - int c, const_a2, vexop, rexw; + int c, const_a2, rexw; #if TCG_TARGET_REG_BITS == 64 # define OP_32_64(x) \ @@ -2949,25 +2976,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(sar): - c = SHIFT_SAR; - vexop = OPC_SARX; - goto gen_shift_maybe_vex; OP_32_64(rotl): c = SHIFT_ROL; goto gen_shift; OP_32_64(rotr): c = SHIFT_ROR; goto gen_shift; - gen_shift_maybe_vex: - if (have_bmi2) { - if (!const_a2) { - tcg_out_vex_modrm(s, vexop + rexw, a0, a2, a1); - break; - } - tcg_out_mov(s, rexw ? TCG_TYPE_I64 : TCG_TYPE_I32, a0, a1); - } - /* FALLTHRU */ gen_shift: if (const_a2) { tcg_out_shifti(s, c + rexw, a0, a2); @@ -3810,10 +3824,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_sar_i32: - case INDEX_op_sar_i64: - return have_bmi2 ? C_O1_I2(r, r, ri) : C_O1_I2(r, 0, ci); - case INDEX_op_rotl_i32: case INDEX_op_rotl_i64: case INDEX_op_rotr_i32: diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 2699079073..aae0f03505 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1489,6 +1489,32 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sra_w(s, a0, a1, a2); + } else { + tcg_out_opc_sra_d(s, a0, a1, a2); + } +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_srai_w(s, a0, a1, a2 & 0x1f); + } else { + tcg_out_opc_srai_d(s, a0, a1, a2 & 0x3f); + } +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + static void tgen_shl(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1712,21 +1738,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_clzctz(s, OPC_CTZ_D, a0, a1, a2, c2, false); break; - case INDEX_op_sar_i32: - if (c2) { - tcg_out_opc_srai_w(s, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_sra_w(s, a0, a1, a2); - } - break; - case INDEX_op_sar_i64: - if (c2) { - tcg_out_opc_srai_d(s, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_sra_d(s, a0, a1, a2); - } - break; - case INDEX_op_rotl_i32: /* transform into equivalent rotr/rotri */ if (c2) { @@ -2391,8 +2402,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_sar_i32: - case INDEX_op_sar_i64: case INDEX_op_rotl_i32: case INDEX_op_rotl_i64: case INDEX_op_rotr_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 03b4248ea9..16c3d59c19 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1908,6 +1908,29 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_SRAV : OPC_DSRAV; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sa(s, OPC_SRA, a0, a1, a2); + } else { + tcg_out_dsra(s, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + static void tgen_shl(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2111,12 +2134,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_dsra(s, a0, a1, 32); break; - case INDEX_op_sar_i32: - i1 = OPC_SRAV, i2 = OPC_SRA; - goto do_shift; case INDEX_op_rotr_i32: i1 = OPC_ROTRV, i2 = OPC_ROTR; - do_shift: if (c2) { tcg_out_opc_sa(s, i2, a0, a1, a2); break; @@ -2132,13 +2151,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_reg(s, OPC_ROTRV, a0, TCG_TMP0, a1); } break; - case INDEX_op_sar_i64: - if (c2) { - tcg_out_dsra(s, a0, a1, a2); - break; - } - i1 = OPC_DSRAV; - goto do_shiftv; case INDEX_op_rotr_i64: if (c2) { tcg_out_opc_sa64(s, OPC_DROTR, OPC_DROTR32, a0, a1, a2); @@ -2319,10 +2331,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i64: case INDEX_op_mulu2_i64: return C_O2_I2(r, r, r, r); - case INDEX_op_sar_i32: case INDEX_op_rotr_i32: case INDEX_op_rotl_i32: - case INDEX_op_sar_i64: case INDEX_op_rotr_i64: case INDEX_op_rotl_i64: return C_O1_I2(r, r, ri); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 2012734bb3..24e8f675bb 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3122,6 +3122,30 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SRAW : SRAD; + tcg_out32(s, insn | SAB(a1, a0, a2)); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* Limit immediate shift count lest we create an illegal insn. */ + if (type == TCG_TYPE_I32) { + tcg_out_sari32(s, a0, a1, a2 & 31); + } else { + tcg_out_sari64(s, a0, a1, a2 & 63); + } +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + static void tgen_shl(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3320,13 +3344,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, CNTPOPD | SAB(args[1], args[0], 0)); break; - case INDEX_op_sar_i32: - if (const_args[2]) { - tcg_out_sari32(s, args[0], args[1], args[2]); - } else { - tcg_out32(s, SRAW | SAB(args[1], args[0], args[2])); - } - break; case INDEX_op_rotl_i32: if (const_args[2]) { tcg_out_rlw(s, RLWINM, args[0], args[1], args[2], 0, 31); @@ -3357,13 +3374,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_brcond2(s, args, const_args); break; - case INDEX_op_sar_i64: - if (const_args[2]) { - tcg_out_sari64(s, args[0], args[1], args[2]); - } else { - tcg_out32(s, SRAD | SAB(args[1], args[0], args[2])); - } - break; case INDEX_op_rotl_i64: if (const_args[2]) { tcg_out_rld(s, RLDICL, args[0], args[1], args[2], 0); @@ -4222,10 +4232,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: - case INDEX_op_sar_i64: case INDEX_op_rotl_i64: case INDEX_op_rotr_i64: return C_O1_I2(r, r, ri); diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 8020cc0b3f..8cab07a392 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2144,6 +2144,27 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SRAW : OPC_SRA; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_SRAIW : OPC_SRAI; + unsigned mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_opc_imm(s, insn, a0, a1, a2 & mask); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + static void tgen_shl(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2299,21 +2320,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_sar_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_SRAIW, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_reg(s, OPC_SRAW, a0, a1, a2); - } - break; - case INDEX_op_sar_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_SRAI, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_reg(s, OPC_SRA, a0, a1, a2); - } - break; - case INDEX_op_rotl_i32: if (c2) { tcg_out_opc_imm(s, OPC_RORIW, a0, a1, -a2 & 0x1f); @@ -2770,10 +2776,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_sar_i32: case INDEX_op_rotl_i32: case INDEX_op_rotr_i32: - case INDEX_op_sar_i64: case INDEX_op_rotl_i64: case INDEX_op_rotr_i64: return C_O1_I2(r, r, ri); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 0417bbef50..1cf4920276 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2445,6 +2445,36 @@ static const TCGOutOpBinary outop_remu = { .base.static_constraint = C_NotImplemented, }; +static void tgen_sar_int(TCGContext *s, TCGType type, TCGReg dst, + TCGReg src, TCGReg v, tcg_target_long i) +{ + if (type != TCG_TYPE_I32) { + tcg_out_sh64(s, RSY_SRAG, dst, src, v, i); + } else if (dst == src) { + tcg_out_sh32(s, RS_SRA, dst, v, i); + } else { + tcg_out_sh64(s, RSY_SRAK, dst, src, v, i); + } +} + +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_sar_int(s, type, a0, a1, a2, 0); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_sar_int(s, type, a0, a1, TCG_REG_NONE, a2); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + static void tgen_shl_int(TCGContext *s, TCGType type, TCGReg dst, TCGReg src, TCGReg v, tcg_target_long i) { @@ -2586,7 +2616,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - S390Opcode op, op2; TCGArg a0, a1, a2; switch (opc) { @@ -2634,28 +2663,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - do_shift32: - a0 = args[0], a1 = args[1], a2 = (int32_t)args[2]; - if (a0 == a1) { - if (const_args[2]) { - tcg_out_sh32(s, op, a0, TCG_REG_NONE, a2); - } else { - tcg_out_sh32(s, op, a0, a2, 0); - } - } else { - /* Using tcg_out_sh64 here for the format; it is a 32-bit shift. */ - if (const_args[2]) { - tcg_out_sh64(s, op2, a0, a1, TCG_REG_NONE, a2); - } else { - tcg_out_sh64(s, op2, a0, a1, a2, 0); - } - } - break; - case INDEX_op_sar_i32: - op = RS_SRA; - op2 = RSY_SRAK; - goto do_shift32; - case INDEX_op_rotl_i32: /* ??? Using tcg_out_sh64 here for the format; it is a 32-bit rol. */ if (const_args[2]) { @@ -2799,17 +2806,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_insn(s, RRFa, MGRK, args[1], args[2], args[3]); break; - do_shift64: - if (const_args[2]) { - tcg_out_sh64(s, op, args[0], args[1], TCG_REG_NONE, args[2]); - } else { - tcg_out_sh64(s, op, args[0], args[1], args[2], 0); - } - break; - case INDEX_op_sar_i64: - op = RSY_SRAG; - goto do_shift64; - case INDEX_op_rotl_i64: if (const_args[2]) { tcg_out_sh64(s, RSY_RLLG, args[0], args[1], @@ -3394,7 +3390,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_sar_i64: case INDEX_op_rotl_i32: case INDEX_op_rotl_i64: case INDEX_op_rotr_i32: @@ -3409,9 +3404,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_clz_i64: return C_O1_I2(r, r, rI); - case INDEX_op_sar_i32: - return C_O1_I2(r, r, ri); - case INDEX_op_brcond_i32: return C_O0_I2(r, ri); case INDEX_op_brcond_i64: diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index f679fa04ea..42d81c1e6c 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1484,6 +1484,27 @@ static const TCGOutOpBinary outop_remu = { .base.static_constraint = C_NotImplemented, }; +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SHIFT_SRA : SHIFT_SRAX; + tcg_out_arith(s, a0, a1, a2, insn); +} + +static void tgen_sari(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? SHIFT_SRA : SHIFT_SRAX; + uint32_t mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_arithi(s, a0, a1, a2 & mask, insn); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_sar, + .out_rri = tgen_sari, +}; + static void tgen_shl(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1629,13 +1650,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_st32_i64: tcg_out_ldst(s, a0, a1, a2, STW); break; - do_shift32: - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_arithc(s, a0, a1, a2 & 31, c2, c); - break; - case INDEX_op_sar_i32: - c = SHIFT_SRA; - goto do_shift32; case INDEX_op_brcond_i32: tcg_out_brcond_i32(s, a2, a0, a1, const_args[1], arg_label(args[3])); @@ -1693,13 +1707,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_st_i64: tcg_out_ldst(s, a0, a1, a2, STX); break; - do_shift64: - /* Limit immediate shift count lest we create an illegal insn. */ - tcg_out_arithc(s, a0, a1, a2 & 63, c2, c); - break; - case INDEX_op_sar_i64: - c = SHIFT_SRAX; - goto do_shift64; case INDEX_op_brcond_i64: tcg_out_brcond_i64(s, a2, a0, a1, const_args[1], arg_label(args[3])); @@ -1783,8 +1790,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); - case INDEX_op_sar_i32: - case INDEX_op_sar_i64: case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: case INDEX_op_negsetcond_i32: diff --git a/tcg/tcg.c b/tcg/tcg.c index 939bbe86e9..ffe9efbf79 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1042,6 +1042,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), + OUTOP(INDEX_op_sar_i32, TCGOutOpBinary, outop_sar), + OUTOP(INDEX_op_sar_i64, TCGOutOpBinary, outop_sar), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), @@ -2264,7 +2266,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: - case INDEX_op_sar_i32: case INDEX_op_extract_i32: case INDEX_op_sextract_i32: case INDEX_op_deposit_i32: @@ -2314,7 +2315,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st16_i64: case INDEX_op_st32_i64: case INDEX_op_st_i64: - case INDEX_op_sar_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extract_i64: @@ -5421,6 +5421,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_orc: case INDEX_op_rems: case INDEX_op_remu: + case INDEX_op_sar_i32: + case INDEX_op_sar_i64: case INDEX_op_shl: case INDEX_op_shr: case INDEX_op_xor: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index c0dbe873f1..f50a2d6574 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -79,8 +79,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_sar_i32: - case INDEX_op_sar_i64: case INDEX_op_rotl_i32: case INDEX_op_rotl_i64: case INDEX_op_rotr_i32: @@ -774,6 +772,21 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_sar(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type < TCG_TYPE_REG) { + tcg_out_ext32s(s, TCG_REG_TMP, a1); + a1 = TCG_REG_TMP; + } + tcg_out_op_rrr(s, glue(INDEX_op_sar_i,TCG_TARGET_REG_BITS), a0, a1, a2); +} + +static const TCGOutOpBinary outop_sar = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_sar, +}; + static void tgen_shl(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { From 3949f365eb6e7c934831c65c67b729562846ede9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 08:05:18 -0800 Subject: [PATCH 0417/2760] tcg: Merge INDEX_op_sar_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 4 ++-- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 12 ++++++------ tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 6 ++---- tcg/tci.c | 12 ++++-------- tcg/tci/tcg-target.c.inc | 3 +-- 7 files changed, 18 insertions(+), 26 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index f9fd4b0087..be82fed41a 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -389,10 +389,10 @@ Shifts/Rotates - | *t0* = *t1* >> *t2* (unsigned) | Unspecified behavior for negative or out-of-range shifts. - * - sar_i32/i64 *t0*, *t1*, *t2* + * - sar *t0*, *t1*, *t2* - | *t0* = *t1* >> *t2* (signed) - | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + | Unspecified behavior for negative or out-of-range shifts. * - rotl_i32/i64 *t0*, *t1*, *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 35e0be8f80..cb8c134e94 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -58,6 +58,7 @@ DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) DEF(rems, 1, 2, 0, TCG_OPF_INT) DEF(remu, 1, 2, 0, TCG_OPF_INT) +DEF(sar, 1, 2, 0, TCG_OPF_INT) DEF(shl, 1, 2, 0, TCG_OPF_INT) DEF(shr, 1, 2, 0, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) @@ -76,7 +77,6 @@ DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* shifts/rotates */ -DEF(sar_i32, 1, 2, 0, 0) DEF(rotl_i32, 1, 2, 0, 0) DEF(rotr_i32, 1, 2, 0, 0) DEF(deposit_i32, 1, 2, 2, 0) @@ -115,7 +115,6 @@ DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* shifts/rotates */ -DEF(sar_i64, 1, 2, 0, 0) DEF(rotl_i64, 1, 2, 0, 0) DEF(rotr_i64, 1, 2, 0, 0) DEF(deposit_i64, 1, 2, 2, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index 43db079693..f94be19b72 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -458,10 +458,10 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, } return (uint64_t)x >> (y & 63); - case INDEX_op_sar_i32: - return (int32_t)x >> (y & 31); - - case INDEX_op_sar_i64: + case INDEX_op_sar: + if (type == TCG_TYPE_I32) { + return (int32_t)x >> (y & 31); + } return (int64_t)x >> (y & 63); case INDEX_op_rotr_i32: @@ -2600,7 +2600,7 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) } switch (op->opc) { - CASE_OP_32_64(sar): + case INDEX_op_sar: /* * Arithmetic right shift will not reduce the number of * input sign repetitions. @@ -3027,7 +3027,7 @@ void tcg_optimize(TCGContext *s) break; CASE_OP_32_64(rotl): CASE_OP_32_64(rotr): - CASE_OP_32_64(sar): + case INDEX_op_sar: case INDEX_op_shl: case INDEX_op_shr: done = fold_shift(&ctx, op); diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index ef8cf5a1ac..43848ebc4f 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -511,7 +511,7 @@ void tcg_gen_shri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_sar_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - tcg_gen_op3_i32(INDEX_op_sar_i32, ret, arg1, arg2); + tcg_gen_op3_i32(INDEX_op_sar, ret, arg1, arg2); } void tcg_gen_sari_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) @@ -1624,7 +1624,7 @@ void tcg_gen_shr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_sar_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op3_i64(INDEX_op_sar_i64, ret, arg1, arg2); + tcg_gen_op3_i64(INDEX_op_sar, ret, arg1, arg2); } else { gen_helper_sar_i64(ret, arg1, arg2); } diff --git a/tcg/tcg.c b/tcg/tcg.c index ffe9efbf79..8f67107190 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1042,8 +1042,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), - OUTOP(INDEX_op_sar_i32, TCGOutOpBinary, outop_sar), - OUTOP(INDEX_op_sar_i64, TCGOutOpBinary, outop_sar), + OUTOP(INDEX_op_sar, TCGOutOpBinary, outop_sar), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), @@ -5421,8 +5420,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_orc: case INDEX_op_rems: case INDEX_op_remu: - case INDEX_op_sar_i32: - case INDEX_op_sar_i64: + case INDEX_op_sar: case INDEX_op_shl: case INDEX_op_shr: case INDEX_op_xor: diff --git a/tcg/tci.c b/tcg/tci.c index 376b1b1ece..2a2f216898 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -625,9 +625,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] >> (regs[r2] % TCG_TARGET_REG_BITS); break; - case INDEX_op_sar_i32: + case INDEX_op_sar: tci_args_rrr(insn, &r0, &r1, &r2); - regs[r0] = (int32_t)regs[r1] >> (regs[r2] & 31); + regs[r0] = ((tcg_target_long)regs[r1] + >> (regs[r2] % TCG_TARGET_REG_BITS)); break; #if TCG_TARGET_HAS_rot_i32 case INDEX_op_rotl_i32: @@ -787,10 +788,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, /* Shift/rotate operations (64 bit). */ - case INDEX_op_sar_i64: - tci_args_rrr(insn, &r0, &r1, &r2); - regs[r0] = (int64_t)regs[r1] >> (regs[r2] & 63); - break; #if TCG_TARGET_HAS_rot_i64 case INDEX_op_rotl_i64: tci_args_rrr(insn, &r0, &r1, &r2); @@ -1073,12 +1070,11 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_orc: case INDEX_op_rems: case INDEX_op_remu: + case INDEX_op_sar: case INDEX_op_shl: case INDEX_op_shr: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_sar_i32: - case INDEX_op_sar_i64: case INDEX_op_rotl_i32: case INDEX_op_rotl_i64: case INDEX_op_rotr_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index f50a2d6574..feaa13dff0 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -779,7 +779,7 @@ static void tgen_sar(TCGContext *s, TCGType type, tcg_out_ext32s(s, TCG_REG_TMP, a1); a1 = TCG_REG_TMP; } - tcg_out_op_rrr(s, glue(INDEX_op_sar_i,TCG_TARGET_REG_BITS), a0, a1, a2); + tcg_out_op_rrr(s, INDEX_op_sar, a0, a1, a2); } static const TCGOutOpBinary outop_sar = { @@ -897,7 +897,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, opc, args[0], args[1], args[2]); break; - CASE_32_64(sar) CASE_32_64(rotl) /* Optional (TCG_TARGET_HAS_rot_*). */ CASE_32_64(rotr) /* Optional (TCG_TARGET_HAS_rot_*). */ CASE_32_64(clz) /* Optional (TCG_TARGET_HAS_clz_*). */ From 8726c7d79967c740f7d5a963ac2d855354805cd2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 09:53:38 -0800 Subject: [PATCH 0418/2760] tcg: Do not require both rotr and rotl from the backend Many host architectures do not implement both rotate right and rotate left and require the compiler to negate the shift count to rotate the opposite direction. We have been requiring the backend to perform this transformation. Do this during opcode expansion so that the next patch can drop support where possible in the backend. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/tcg-op.c | 98 +++++++++++++++++++++++++++++----------------------- 1 file changed, 54 insertions(+), 44 deletions(-) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 43848ebc4f..8c8b9d179b 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -829,15 +829,18 @@ void tcg_gen_ctpop_i32(TCGv_i32 ret, TCGv_i32 arg1) void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_rot_i32) { + if (tcg_op_supported(INDEX_op_rotl_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_rotl_i32, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_rotr_i32, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + tcg_gen_neg_i32(t0, arg2); + tcg_gen_op3_i32(INDEX_op_rotr_i32, ret, arg1, t0); + tcg_temp_free_i32(t0); } else { - TCGv_i32 t0, t1; - - t0 = tcg_temp_ebb_new_i32(); - t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); tcg_gen_shl_i32(t0, arg1, arg2); - tcg_gen_subfi_i32(t1, 32, arg2); + tcg_gen_neg_i32(t1, arg2); tcg_gen_shr_i32(t1, arg1, t1); tcg_gen_or_i32(ret, t0, t1); tcg_temp_free_i32(t0); @@ -851,12 +854,15 @@ void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) /* some cases can be optimized here */ if (arg2 == 0) { tcg_gen_mov_i32(ret, arg1); - } else if (TCG_TARGET_HAS_rot_i32) { - tcg_gen_rotl_i32(ret, arg1, tcg_constant_i32(arg2)); + } else if (tcg_op_supported(INDEX_op_rotl_i32, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_constant_i32(arg2); + tcg_gen_op3_i32(INDEX_op_rotl_i32, ret, arg1, t0); + } else if (tcg_op_supported(INDEX_op_rotr_i32, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_constant_i32(32 - arg2); + tcg_gen_op3_i32(INDEX_op_rotr_i32, ret, arg1, t0); } else { - TCGv_i32 t0, t1; - t0 = tcg_temp_ebb_new_i32(); - t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); tcg_gen_shli_i32(t0, arg1, arg2); tcg_gen_shri_i32(t1, arg1, 32 - arg2); tcg_gen_or_i32(ret, t0, t1); @@ -867,15 +873,18 @@ void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_rot_i32) { + if (tcg_op_supported(INDEX_op_rotr_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_rotr_i32, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_rotl_i32, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + tcg_gen_neg_i32(t0, arg2); + tcg_gen_op3_i32(INDEX_op_rotl_i32, ret, arg1, t0); + tcg_temp_free_i32(t0); } else { - TCGv_i32 t0, t1; - - t0 = tcg_temp_ebb_new_i32(); - t1 = tcg_temp_ebb_new_i32(); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); tcg_gen_shr_i32(t0, arg1, arg2); - tcg_gen_subfi_i32(t1, 32, arg2); + tcg_gen_neg_i32(t1, arg2); tcg_gen_shl_i32(t1, arg1, t1); tcg_gen_or_i32(ret, t0, t1); tcg_temp_free_i32(t0); @@ -886,12 +895,7 @@ void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_rotri_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) { tcg_debug_assert(arg2 >= 0 && arg2 < 32); - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i32(ret, arg1); - } else { - tcg_gen_rotli_i32(ret, arg1, 32 - arg2); - } + tcg_gen_rotli_i32(ret, arg1, -arg2 & 31); } void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, @@ -2437,14 +2441,18 @@ void tcg_gen_ctpop_i64(TCGv_i64 ret, TCGv_i64 arg1) void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (TCG_TARGET_HAS_rot_i64) { + if (tcg_op_supported(INDEX_op_rotl_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_rotl_i64, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_rotl_i64, TCG_TYPE_I64, 0)) { + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + tcg_gen_neg_i64(t0, arg2); + tcg_gen_op3_i64(INDEX_op_rotr_i64, ret, arg1, t0); + tcg_temp_free_i64(t0); } else { - TCGv_i64 t0, t1; - t0 = tcg_temp_ebb_new_i64(); - t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); tcg_gen_shl_i64(t0, arg1, arg2); - tcg_gen_subfi_i64(t1, 64, arg2); + tcg_gen_neg_i64(t1, arg2); tcg_gen_shr_i64(t1, arg1, t1); tcg_gen_or_i64(ret, t0, t1); tcg_temp_free_i64(t0); @@ -2458,12 +2466,15 @@ void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) /* some cases can be optimized here */ if (arg2 == 0) { tcg_gen_mov_i64(ret, arg1); - } else if (TCG_TARGET_HAS_rot_i64) { - tcg_gen_rotl_i64(ret, arg1, tcg_constant_i64(arg2)); + } else if (tcg_op_supported(INDEX_op_rotl_i64, TCG_TYPE_I64, 0)) { + TCGv_i64 t0 = tcg_constant_i64(arg2); + tcg_gen_op3_i64(INDEX_op_rotl_i64, ret, arg1, t0); + } else if (tcg_op_supported(INDEX_op_rotr_i64, TCG_TYPE_I64, 0)) { + TCGv_i64 t0 = tcg_constant_i64(64 - arg2); + tcg_gen_op3_i64(INDEX_op_rotr_i64, ret, arg1, t0); } else { - TCGv_i64 t0, t1; - t0 = tcg_temp_ebb_new_i64(); - t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); tcg_gen_shli_i64(t0, arg1, arg2); tcg_gen_shri_i64(t1, arg1, 64 - arg2); tcg_gen_or_i64(ret, t0, t1); @@ -2474,14 +2485,18 @@ void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (TCG_TARGET_HAS_rot_i64) { + if (tcg_op_supported(INDEX_op_rotr_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_rotr_i64, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_rotl_i64, TCG_TYPE_I64, 0)) { + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + tcg_gen_neg_i64(t0, arg2); + tcg_gen_op3_i64(INDEX_op_rotl_i64, ret, arg1, t0); + tcg_temp_free_i64(t0); } else { - TCGv_i64 t0, t1; - t0 = tcg_temp_ebb_new_i64(); - t1 = tcg_temp_ebb_new_i64(); + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); tcg_gen_shr_i64(t0, arg1, arg2); - tcg_gen_subfi_i64(t1, 64, arg2); + tcg_gen_neg_i64(t1, arg2); tcg_gen_shl_i64(t1, arg1, t1); tcg_gen_or_i64(ret, t0, t1); tcg_temp_free_i64(t0); @@ -2492,12 +2507,7 @@ void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_rotri_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) { tcg_debug_assert(arg2 >= 0 && arg2 < 64); - /* some cases can be optimized here */ - if (arg2 == 0) { - tcg_gen_mov_i64(ret, arg1); - } else { - tcg_gen_rotli_i64(ret, arg1, 64 - arg2); - } + tcg_gen_rotli_i64(ret, arg1, -arg2 & 63); } void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, From 03568c0d539581c6e86263d2fd7396f5a1e25a6b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 10:22:53 -0800 Subject: [PATCH 0419/2760] tcg: Convert rotl, rotr to TCGOutOpBinary For aarch64, arm, loongarch64, mips, we can drop rotl. For ppc, s390x we can drop rotr. Only x86, riscv, tci have both rotl and rotr. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 - tcg/aarch64/tcg-target.c.inc | 62 +++++++++--------------- tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 44 ++++++++--------- tcg/i386/tcg-target-has.h | 2 - tcg/i386/tcg-target.c.inc | 62 ++++++++++++++++-------- tcg/loongarch64/tcg-target-has.h | 2 - tcg/loongarch64/tcg-target.c.inc | 70 ++++++++++++--------------- tcg/mips/tcg-target-has.h | 2 - tcg/mips/tcg-target.c.inc | 75 +++++++++++++---------------- tcg/ppc/tcg-target-has.h | 2 - tcg/ppc/tcg-target.c.inc | 70 ++++++++++++--------------- tcg/riscv/tcg-target-has.h | 2 - tcg/riscv/tcg-target.c.inc | 83 ++++++++++++++++++-------------- tcg/s390x/tcg-target-has.h | 2 - tcg/s390x/tcg-target.c.inc | 72 +++++++++++---------------- tcg/sparc64/tcg-target-has.h | 2 - tcg/sparc64/tcg-target.c.inc | 8 +++ tcg/tcg-has.h | 1 - tcg/tcg.c | 14 +++--- tcg/tci.c | 12 ++--- tcg/tci/tcg-target-has.h | 2 - tcg/tci/tcg-target-opc.h.inc | 2 + tcg/tci/tcg-target.c.inc | 34 ++++++++++--- 24 files changed, 306 insertions(+), 322 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 1fdff25d05..fa79cbc1f0 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -15,7 +15,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 0 @@ -31,7 +30,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 0 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 90bdbf8387..00fca43840 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1347,20 +1347,6 @@ static inline void tcg_out_extr(TCGContext *s, TCGType ext, TCGReg rd, tcg_out_insn(s, 3403, EXTR, ext, rd, rn, rm, a); } -static inline void tcg_out_rotr(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int max = ext ? 63 : 31; - tcg_out_extr(s, ext, rd, rn, rn, m & max); -} - -static inline void tcg_out_rotl(TCGContext *s, TCGType ext, - TCGReg rd, TCGReg rn, unsigned int m) -{ - int max = ext ? 63 : 31; - tcg_out_extr(s, ext, rd, rn, rn, -m & max); -} - static inline void tcg_out_dep(TCGContext *s, TCGType ext, TCGReg rd, TCGReg rn, unsigned lsb, unsigned width) { @@ -2277,6 +2263,29 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3508, RORV, type, a0, a1, a2); +} + +static void tgen_rotri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int max = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_extr(s, type, a0, a1, a1, a2 & max); +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_rotr, + .out_rri = tgen_rotri, +}; + static void tgen_sar(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2451,25 +2460,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_rotr_i64: - case INDEX_op_rotr_i32: - if (c2) { - tcg_out_rotr(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3508, RORV, ext, a0, a1, a2); - } - break; - - case INDEX_op_rotl_i64: - case INDEX_op_rotl_i32: - if (c2) { - tcg_out_rotl(s, ext, a0, a1, a2); - } else { - tcg_out_insn(s, 3502, SUB, 0, TCG_REG_TMP0, TCG_REG_XZR, a2); - tcg_out_insn(s, 3508, RORV, ext, a0, a1, TCG_REG_TMP0); - } - break; - case INDEX_op_clz_i64: case INDEX_op_clz_i32: tcg_out_cltz(s, ext, a0, a1, a2, c2, false); @@ -3099,12 +3089,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rC); - case INDEX_op_rotl_i32: - case INDEX_op_rotr_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i64: - return C_O1_I2(r, r, ri); - case INDEX_op_clz_i32: case INDEX_op_ctz_i32: case INDEX_op_clz_i64: diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 32d73d3443..12ffbcda2b 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -26,7 +26,6 @@ extern bool use_neon_instructions; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 use_armv7_instructions #define TCG_TARGET_HAS_ctpop_i32 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 058677650b..462f0ec08d 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1963,6 +1963,28 @@ static const TCGOutOpBinary outop_remu = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, SHIFT_REG_ROR(a2)); +} + +static void tgen_rotri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_MOV, a0, 0, a1, SHIFT_IMM_ROR(a2 & 0x1f)); +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_rotr, + .out_rri = tgen_rotri, +}; + static void tgen_sar(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2171,24 +2193,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_muls2_i32: tcg_out_smull32(s, COND_AL, args[0], args[1], args[2], args[3]); break; - case INDEX_op_rotr_i32: - c = const_args[2] ? (args[2] & 0x1f) ? SHIFT_IMM_ROR(args[2] & 0x1f) : - SHIFT_IMM_LSL(0) : SHIFT_REG_ROR(args[2]); - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], c); - break; - - case INDEX_op_rotl_i32: - if (const_args[2]) { - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], - ((0x20 - args[2]) & 0x1f) ? - SHIFT_IMM_ROR((0x20 - args[2]) & 0x1f) : - SHIFT_IMM_LSL(0)); - } else { - tcg_out_dat_imm(s, COND_AL, ARITH_RSB, TCG_REG_TMP, args[2], 0x20); - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, args[1], - SHIFT_REG_ROR(TCG_REG_TMP)); - } - break; case INDEX_op_ctz_i32: tcg_out_dat_reg(s, COND_AL, INSN_RBIT, TCG_REG_TMP, 0, args[1], 0); @@ -2342,10 +2346,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i32: return C_O2_I2(r, r, r, r); - case INDEX_op_rotl_i32: - case INDEX_op_rotr_i32: - return C_O1_I2(r, r, ri); - case INDEX_op_brcond_i32: return C_O0_I2(r, rIN); case INDEX_op_deposit_i32: diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index aee6066579..a7199463df 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -26,7 +26,6 @@ #define have_avx512vbmi2 ((cpuinfo & CPUINFO_AVX512VBMI2) && have_avx512vl) /* optional instructions */ -#define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_clz_i32 1 @@ -42,7 +41,6 @@ #if TCG_TARGET_REG_BITS == 64 /* Keep 32-bit values zero-extended in a register. */ #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 1e81455461..dd35bba57f 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2744,6 +2744,46 @@ static const TCGOutOpBinary outop_remu = { .base.static_constraint = C_NotImplemented, }; +static void tgen_rotl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, SHIFT_ROL, a0); +} + +static void tgen_rotli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_shifti(s, SHIFT_ROL + rexw, a0, a2); +} + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_O1_I2(r, 0, ci), + .out_rrr = tgen_rotl, + .out_rri = tgen_rotli, +}; + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_SHIFT_cl + rexw, SHIFT_ROR, a0); +} + +static void tgen_rotri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_shifti(s, SHIFT_ROR + rexw, a0, a2); +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_O1_I2(r, 0, ci), + .out_rrr = tgen_rotr, + .out_rri = tgen_rotri, +}; + static TCGConstraintSetIndex cset_shift(TCGType type, unsigned flags) { return have_bmi2 ? C_O1_I2(r, r, ri) : C_O1_I2(r, 0, ci); @@ -2901,7 +2941,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { TCGArg a0, a1, a2; - int c, const_a2, rexw; + int const_a2, rexw; #if TCG_TARGET_REG_BITS == 64 # define OP_32_64(x) \ @@ -2976,20 +3016,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(rotl): - c = SHIFT_ROL; - goto gen_shift; - OP_32_64(rotr): - c = SHIFT_ROR; - goto gen_shift; - gen_shift: - if (const_a2) { - tcg_out_shifti(s, c + rexw, a0, a2); - } else { - tcg_out_modrm(s, OPC_SHIFT_cl + rexw, c, a0); - } - break; - OP_32_64(ctz): tcg_out_ctz(s, rexw, args[0], args[1], args[2], const_args[2]); break; @@ -3824,12 +3850,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_rotl_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i32: - case INDEX_op_rotr_i64: - return C_O1_I2(r, 0, ci); - case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: return C_O0_I2(r, reT); diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 5dfc69ae6a..303134390a 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -11,7 +11,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_negsetcond_i32 0 -#define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 @@ -26,7 +25,6 @@ /* 64-bit operations */ #define TCG_TARGET_HAS_negsetcond_i64 0 -#define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_bswap16_i64 1 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index aae0f03505..26cf982780 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1489,6 +1489,36 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_NotImplemented, +}; + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_rotr_w(s, a0, a1, a2); + } else { + tcg_out_opc_rotr_d(s, a0, a1, a2); + } +} + +static void tgen_rotri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_rotri_w(s, a0, a1, a2 & 0x1f); + } else { + tcg_out_opc_rotri_d(s, a0, a1, a2 & 0x3f); + } +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_rotr, + .out_rri = tgen_rotri, +}; + static void tgen_sar(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1738,40 +1768,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_clzctz(s, OPC_CTZ_D, a0, a1, a2, c2, false); break; - case INDEX_op_rotl_i32: - /* transform into equivalent rotr/rotri */ - if (c2) { - tcg_out_opc_rotri_w(s, a0, a1, (32 - a2) & 0x1f); - } else { - tcg_out_opc_sub_w(s, TCG_REG_TMP0, TCG_REG_ZERO, a2); - tcg_out_opc_rotr_w(s, a0, a1, TCG_REG_TMP0); - } - break; - case INDEX_op_rotl_i64: - /* transform into equivalent rotr/rotri */ - if (c2) { - tcg_out_opc_rotri_d(s, a0, a1, (64 - a2) & 0x3f); - } else { - tcg_out_opc_sub_w(s, TCG_REG_TMP0, TCG_REG_ZERO, a2); - tcg_out_opc_rotr_d(s, a0, a1, TCG_REG_TMP0); - } - break; - - case INDEX_op_rotr_i32: - if (c2) { - tcg_out_opc_rotri_w(s, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_rotr_w(s, a0, a1, a2); - } - break; - case INDEX_op_rotr_i64: - if (c2) { - tcg_out_opc_rotri_d(s, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_rotr_d(s, a0, a1, a2); - } - break; - case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: tcg_out_setcond(s, args[3], a0, a1, a2, c2); @@ -2402,12 +2398,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_rotl_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i32: - case INDEX_op_rotr_i64: - return C_O1_I2(r, r, ri); - case INDEX_op_clz_i32: case INDEX_op_clz_i64: case INDEX_op_ctz_i32: diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index ab6a134796..880eb084eb 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -60,7 +60,6 @@ extern bool use_mips32r2_instructions; /* optional instructions detected at runtime */ #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_rot_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_clz_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 0 @@ -71,7 +70,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_rot_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_clz_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 16c3d59c19..fb9fe0c40e 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1908,6 +1908,39 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_NotImplemented, +}; + +static TCGConstraintSetIndex cset_rotr(TCGType type, unsigned flags) +{ + return use_mips32r2_instructions ? C_O1_I2(r, r, ri) : C_NotImplemented; +} + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_ROTRV : OPC_DROTRV; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_rotri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_sa(s, OPC_ROTR, a0, a1, a2); + } else { + tcg_out_opc_sa64(s, OPC_DROTR, OPC_DROTR32, a0, a1, a2); + } +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_rotr, + .out_rrr = tgen_rotr, + .out_rri = tgen_rotri, +}; + static void tgen_sar(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2032,14 +2065,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - MIPSInsn i1, i2; + MIPSInsn i1; TCGArg a0, a1, a2; - int c2; a0 = args[0]; a1 = args[1]; a2 = args[2]; - c2 = const_args[2]; switch (opc) { case INDEX_op_goto_ptr: @@ -2134,39 +2165,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_dsra(s, a0, a1, 32); break; - case INDEX_op_rotr_i32: - i1 = OPC_ROTRV, i2 = OPC_ROTR; - if (c2) { - tcg_out_opc_sa(s, i2, a0, a1, a2); - break; - } - do_shiftv: - tcg_out_opc_reg(s, i1, a0, a2, a1); - break; - case INDEX_op_rotl_i32: - if (c2) { - tcg_out_opc_sa(s, OPC_ROTR, a0, a1, 32 - a2); - } else { - tcg_out_opc_reg(s, OPC_SUBU, TCG_TMP0, TCG_REG_ZERO, a2); - tcg_out_opc_reg(s, OPC_ROTRV, a0, TCG_TMP0, a1); - } - break; - case INDEX_op_rotr_i64: - if (c2) { - tcg_out_opc_sa64(s, OPC_DROTR, OPC_DROTR32, a0, a1, a2); - break; - } - i1 = OPC_DROTRV; - goto do_shiftv; - case INDEX_op_rotl_i64: - if (c2) { - tcg_out_opc_sa64(s, OPC_DROTR, OPC_DROTR32, a0, a1, 64 - a2); - } else { - tcg_out_opc_reg(s, OPC_DSUBU, TCG_TMP0, TCG_REG_ZERO, a2); - tcg_out_opc_reg(s, OPC_DROTRV, a0, TCG_TMP0, a1); - } - break; - case INDEX_op_clz_i32: tcg_out_clz(s, OPC_CLZ, OPC_CLZ_R6, 32, a0, a1, a2); break; @@ -2331,11 +2329,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i64: case INDEX_op_mulu2_i64: return C_O2_I2(r, r, r, r); - case INDEX_op_rotr_i32: - case INDEX_op_rotl_i32: - case INDEX_op_rotr_i64: - case INDEX_op_rotl_i64: - return C_O1_I2(r, r, ri); case INDEX_op_clz_i32: case INDEX_op_clz_i64: return C_O1_I2(r, r, rzW); diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 37e88a3193..71c02d88b9 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -17,7 +17,6 @@ #define have_vsx (cpuinfo & CPUINFO_VSX) /* optional instructions */ -#define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_clz_i32 1 @@ -33,7 +32,6 @@ #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 24e8f675bb..687b66af54 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3122,6 +3122,36 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_rotl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out32(s, RLWNM | SAB(a1, a0, a2) | MB(0) | ME(31)); + } else { + tcg_out32(s, RLDCL | SAB(a1, a0, a2) | MB64(0)); + } +} + +static void tgen_rotli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_rlw(s, RLWINM, a0, a1, a2, 0, 31); + } else { + tcg_out_rld(s, RLDICL, a0, a1, a2, 0); + } +} + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_rotl, + .out_rri = tgen_rotli, +}; + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_sar(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3344,24 +3374,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, CNTPOPD | SAB(args[1], args[0], 0)); break; - case INDEX_op_rotl_i32: - if (const_args[2]) { - tcg_out_rlw(s, RLWINM, args[0], args[1], args[2], 0, 31); - } else { - tcg_out32(s, RLWNM | SAB(args[1], args[0], args[2]) - | MB(0) | ME(31)); - } - break; - case INDEX_op_rotr_i32: - if (const_args[2]) { - tcg_out_rlw(s, RLWINM, args[0], args[1], 32 - args[2], 0, 31); - } else { - tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 32)); - tcg_out32(s, RLWNM | SAB(args[1], args[0], TCG_REG_R0) - | MB(0) | ME(31)); - } - break; - case INDEX_op_brcond_i32: tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], arg_label(args[3]), TCG_TYPE_I32); @@ -3374,22 +3386,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_brcond2(s, args, const_args); break; - case INDEX_op_rotl_i64: - if (const_args[2]) { - tcg_out_rld(s, RLDICL, args[0], args[1], args[2], 0); - } else { - tcg_out32(s, RLDCL | SAB(args[1], args[0], args[2]) | MB64(0)); - } - break; - case INDEX_op_rotr_i64: - if (const_args[2]) { - tcg_out_rld(s, RLDICL, args[0], args[1], 64 - args[2], 0); - } else { - tcg_out32(s, SUBFIC | TAI(TCG_REG_R0, args[2], 64)); - tcg_out32(s, RLDCL | SAB(args[1], args[0], TCG_REG_R0) | MB64(0)); - } - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; @@ -4232,12 +4228,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_rotl_i32: - case INDEX_op_rotr_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i64: - return C_O1_I2(r, r, ri); - case INDEX_op_clz_i32: case INDEX_op_ctz_i32: case INDEX_op_clz_i64: diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index b3c6899887..c7745a6462 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -11,7 +11,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_negsetcond_i32 1 -#define TCG_TARGET_HAS_rot_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 @@ -25,7 +24,6 @@ #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_negsetcond_i64 1 -#define TCG_TARGET_HAS_rot_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_bswap16_i64 (cpuinfo & CPUINFO_ZBB) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 8cab07a392..4dd892d98d 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2144,6 +2144,53 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static TCGConstraintSetIndex cset_rot(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_ZBB ? C_O1_I2(r, r, ri) : C_NotImplemented; +} + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_RORW : OPC_ROR; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_rotri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_RORIW : OPC_RORI; + unsigned mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_opc_imm(s, insn, a0, a1, a2 & mask); +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_rot, + .out_rrr = tgen_rotr, + .out_rri = tgen_rotri, +}; + +static void tgen_rotl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_ROLW : OPC_ROL; + tcg_out_opc_reg(s, insn, a0, a1, a2); +} + +static void tgen_rotli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_rotri(s, type, a0, a1, -a2); +} + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_rot, + .out_rrr = tgen_rotl, + .out_rri = tgen_rotli, +}; + static void tgen_sar(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2320,36 +2367,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_rotl_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_RORIW, a0, a1, -a2 & 0x1f); - } else { - tcg_out_opc_reg(s, OPC_ROLW, a0, a1, a2); - } - break; - case INDEX_op_rotl_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_RORI, a0, a1, -a2 & 0x3f); - } else { - tcg_out_opc_reg(s, OPC_ROL, a0, a1, a2); - } - break; - - case INDEX_op_rotr_i32: - if (c2) { - tcg_out_opc_imm(s, OPC_RORIW, a0, a1, a2 & 0x1f); - } else { - tcg_out_opc_reg(s, OPC_RORW, a0, a1, a2); - } - break; - case INDEX_op_rotr_i64: - if (c2) { - tcg_out_opc_imm(s, OPC_RORI, a0, a1, a2 & 0x3f); - } else { - tcg_out_opc_reg(s, OPC_ROR, a0, a1, a2); - } - break; - case INDEX_op_bswap64_i64: tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); break; @@ -2776,12 +2793,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_rotl_i32: - case INDEX_op_rotr_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i64: - return C_O1_I2(r, r, ri); - case INDEX_op_clz_i32: case INDEX_op_clz_i64: case INDEX_op_ctz_i32: diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index d61cc7a144..eaddf7005e 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -29,7 +29,6 @@ extern uint64_t s390_facilities[3]; ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) /* optional instructions */ -#define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_clz_i32 0 @@ -44,7 +43,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 1cf4920276..76180dabcb 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2445,6 +2445,35 @@ static const TCGOutOpBinary outop_remu = { .base.static_constraint = C_NotImplemented, }; +static void tgen_rotl_int(TCGContext *s, TCGType type, TCGReg dst, + TCGReg src, TCGReg v, tcg_target_long i) +{ + S390Opcode insn = type == TCG_TYPE_I32 ? RSY_RLL : RSY_RLLG; + tcg_out_sh64(s, insn, dst, src, v, i); +} + +static void tgen_rotl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_rotl_int(s, type, a0, a1, a2, 0); +} + +static void tgen_rotli(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_rotl_int(s, type, a0, a1, TCG_REG_NONE, a2); +} + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_O1_I2(r, r, ri), + .out_rrr = tgen_rotl, + .out_rri = tgen_rotli, +}; + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_sar_int(TCGContext *s, TCGType type, TCGReg dst, TCGReg src, TCGReg v, tcg_target_long i) { @@ -2663,24 +2692,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_rotl_i32: - /* ??? Using tcg_out_sh64 here for the format; it is a 32-bit rol. */ - if (const_args[2]) { - tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_REG_NONE, args[2]); - } else { - tcg_out_sh64(s, RSY_RLL, args[0], args[1], args[2], 0); - } - break; - case INDEX_op_rotr_i32: - if (const_args[2]) { - tcg_out_sh64(s, RSY_RLL, args[0], args[1], - TCG_REG_NONE, (32 - args[2]) & 31); - } else { - tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]); - tcg_out_sh64(s, RSY_RLL, args[0], args[1], TCG_TMP0, 0); - } - break; - case INDEX_op_bswap16_i32: a0 = args[0], a1 = args[1], a2 = args[2]; tcg_out_insn(s, RRE, LRVR, a0, a1); @@ -2806,26 +2817,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_insn(s, RRFa, MGRK, args[1], args[2], args[3]); break; - case INDEX_op_rotl_i64: - if (const_args[2]) { - tcg_out_sh64(s, RSY_RLLG, args[0], args[1], - TCG_REG_NONE, args[2]); - } else { - tcg_out_sh64(s, RSY_RLLG, args[0], args[1], args[2], 0); - } - break; - case INDEX_op_rotr_i64: - if (const_args[2]) { - tcg_out_sh64(s, RSY_RLLG, args[0], args[1], - TCG_REG_NONE, (64 - args[2]) & 63); - } else { - /* We can use the smaller 32-bit negate because only the - low 6 bits are examined for the rotate. */ - tcg_out_insn(s, RR, LCR, TCG_TMP0, args[2]); - tcg_out_sh64(s, RSY_RLLG, args[0], args[1], TCG_TMP0, 0); - } - break; - case INDEX_op_add2_i64: if (const_args[4]) { if ((int64_t)args[4] >= 0) { @@ -3390,11 +3381,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_rotl_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i32: - case INDEX_op_rotr_i64: - return C_O1_I2(r, r, ri); case INDEX_op_setcond_i32: case INDEX_op_negsetcond_i32: case INDEX_op_setcond_i64: diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 42de99efbf..1dd86c363d 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -14,7 +14,6 @@ extern bool use_vis3_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_rot_i32 0 #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 #define TCG_TARGET_HAS_clz_i32 0 @@ -29,7 +28,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_rot_i64 0 #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 42d81c1e6c..57b26ae33b 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1484,6 +1484,14 @@ static const TCGOutOpBinary outop_remu = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_sar(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 0bb829be36..7bfa55adb1 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -12,7 +12,6 @@ #if TCG_TARGET_REG_BITS == 32 /* Turn some undef macros into false macros. */ #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_rot_i64 0 #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 diff --git a/tcg/tcg.c b/tcg/tcg.c index 8f67107190..40a3e44b7c 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1042,6 +1042,10 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), + OUTOP(INDEX_op_rotl_i32, TCGOutOpBinary, outop_rotl), + OUTOP(INDEX_op_rotl_i64, TCGOutOpBinary, outop_rotl), + OUTOP(INDEX_op_rotr_i32, TCGOutOpBinary, outop_rotr), + OUTOP(INDEX_op_rotr_i64, TCGOutOpBinary, outop_rotr), OUTOP(INDEX_op_sar, TCGOutOpBinary, outop_sar), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), @@ -2272,9 +2276,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i32: return TCG_TARGET_HAS_negsetcond_i32; - case INDEX_op_rotl_i32: - case INDEX_op_rotr_i32: - return TCG_TARGET_HAS_rot_i32; case INDEX_op_extract2_i32: return TCG_TARGET_HAS_extract2_i32; case INDEX_op_add2_i32: @@ -2323,9 +2324,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return TCG_TARGET_HAS_negsetcond_i64; - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i64: - return TCG_TARGET_HAS_rot_i64; case INDEX_op_extract2_i64: return TCG_TARGET_HAS_extract2_i64; case INDEX_op_extrl_i64_i32: @@ -5420,6 +5418,10 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_orc: case INDEX_op_rems: case INDEX_op_remu: + case INDEX_op_rotl_i32: + case INDEX_op_rotl_i64: + case INDEX_op_rotr_i32: + case INDEX_op_rotr_i64: case INDEX_op_sar: case INDEX_op_shl: case INDEX_op_shr: diff --git a/tcg/tci.c b/tcg/tci.c index 2a2f216898..0fb13ff61d 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -630,16 +630,14 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = ((tcg_target_long)regs[r1] >> (regs[r2] % TCG_TARGET_REG_BITS)); break; -#if TCG_TARGET_HAS_rot_i32 - case INDEX_op_rotl_i32: + case INDEX_op_tci_rotl32: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = rol32(regs[r1], regs[r2] & 31); break; - case INDEX_op_rotr_i32: + case INDEX_op_tci_rotr32: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ror32(regs[r1], regs[r2] & 31); break; -#endif case INDEX_op_deposit_i32: tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); regs[r0] = deposit32(regs[r1], pos, len, regs[r2]); @@ -788,7 +786,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, /* Shift/rotate operations (64 bit). */ -#if TCG_TARGET_HAS_rot_i64 case INDEX_op_rotl_i64: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = rol64(regs[r1], regs[r2] & 63); @@ -797,7 +794,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ror64(regs[r1], regs[r2] & 63); break; -#endif case INDEX_op_deposit_i64: tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); regs[r0] = deposit64(regs[r1], pos, len, regs[r2]); @@ -1075,9 +1071,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_shr: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_rotl_i32: case INDEX_op_rotl_i64: - case INDEX_op_rotr_i32: case INDEX_op_rotr_i64: case INDEX_op_clz_i32: case INDEX_op_clz_i64: @@ -1087,6 +1081,8 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_tci_divu32: case INDEX_op_tci_rems32: case INDEX_op_tci_remu32: + case INDEX_op_tci_rotl32: + case INDEX_op_tci_rotr32: tci_args_rrr(insn, &r0, &r1, &r2); info->fprintf_func(info->stream, "%-12s %s, %s, %s", op_name, str_r(r0), str_r(r1), str_r(r2)); diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index bd51b9346d..04d341a8d2 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -13,7 +13,6 @@ #define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 1 -#define TCG_TARGET_HAS_rot_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_muls2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -27,7 +26,6 @@ #define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 1 -#define TCG_TARGET_HAS_rot_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_muls2_i64 1 #define TCG_TARGET_HAS_add2_i32 1 diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc index 82d2a38cae..cff215490a 100644 --- a/tcg/tci/tcg-target-opc.h.inc +++ b/tcg/tci/tcg-target-opc.h.inc @@ -6,3 +6,5 @@ DEF(tci_divs32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_divu32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_rems32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_remu32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_rotl32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_rotr32, 1, 2, 0, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index feaa13dff0..0a2da3ba47 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -79,10 +79,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_rotl_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i32: - case INDEX_op_rotr_i64: case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: case INDEX_op_deposit_i32: @@ -772,6 +768,34 @@ static const TCGOutOpBinary outop_remu = { .out_rrr = tgen_remu, }; +static void tgen_rotl(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_rotl32 + : INDEX_op_rotl_i64); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_rotl = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rotl, +}; + +static void tgen_rotr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_rotr32 + : INDEX_op_rotr_i64); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_rotr = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_rotr, +}; + static void tgen_sar(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -897,8 +921,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, opc, args[0], args[1], args[2]); break; - CASE_32_64(rotl) /* Optional (TCG_TARGET_HAS_rot_*). */ - CASE_32_64(rotr) /* Optional (TCG_TARGET_HAS_rot_*). */ CASE_32_64(clz) /* Optional (TCG_TARGET_HAS_clz_*). */ CASE_32_64(ctz) /* Optional (TCG_TARGET_HAS_ctz_*). */ tcg_out_op_rrr(s, opc, args[0], args[1], args[2]); From 005a87e148dc20f59835b328336240759703d63d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 10:42:16 -0800 Subject: [PATCH 0420/2760] tcg: Merge INDEX_op_rot{l,r}_{i32,i64} Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 8 +++---- include/tcg/tcg-opc.h | 6 ++--- tcg/optimize.c | 20 ++++++++--------- tcg/tcg-op.c | 48 ++++++++++++++++++++-------------------- tcg/tcg.c | 12 ++++------ tcg/tci.c | 8 +++---- tcg/tci/tcg-target.c.inc | 4 ++-- 7 files changed, 50 insertions(+), 56 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index be82fed41a..c3a6499d01 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -394,15 +394,15 @@ Shifts/Rotates - | *t0* = *t1* >> *t2* (signed) | Unspecified behavior for negative or out-of-range shifts. - * - rotl_i32/i64 *t0*, *t1*, *t2* + * - rotl *t0*, *t1*, *t2* - | Rotation of *t2* bits to the left - | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + | Unspecified behavior for negative or out-of-range shifts. - * - rotr_i32/i64 *t0*, *t1*, *t2* + * - rotr *t0*, *t1*, *t2* - | Rotation of *t2* bits to the right. - | Unspecified behavior if *t2* < 0 or *t2* >= 32 (resp 64) + | Unspecified behavior for negative or out-of-range shifts. Misc diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index cb8c134e94..25fd93eb28 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -58,6 +58,8 @@ DEF(or, 1, 2, 0, TCG_OPF_INT) DEF(orc, 1, 2, 0, TCG_OPF_INT) DEF(rems, 1, 2, 0, TCG_OPF_INT) DEF(remu, 1, 2, 0, TCG_OPF_INT) +DEF(rotl, 1, 2, 0, TCG_OPF_INT) +DEF(rotr, 1, 2, 0, TCG_OPF_INT) DEF(sar, 1, 2, 0, TCG_OPF_INT) DEF(shl, 1, 2, 0, TCG_OPF_INT) DEF(shr, 1, 2, 0, TCG_OPF_INT) @@ -77,8 +79,6 @@ DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* shifts/rotates */ -DEF(rotl_i32, 1, 2, 0, 0) -DEF(rotr_i32, 1, 2, 0, 0) DEF(deposit_i32, 1, 2, 2, 0) DEF(extract_i32, 1, 1, 2, 0) DEF(sextract_i32, 1, 1, 2, 0) @@ -115,8 +115,6 @@ DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* shifts/rotates */ -DEF(rotl_i64, 1, 2, 0, 0) -DEF(rotr_i64, 1, 2, 0, 0) DEF(deposit_i64, 1, 2, 2, 0) DEF(extract_i64, 1, 1, 2, 0) DEF(sextract_i64, 1, 1, 2, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index f94be19b72..97a566a617 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -464,16 +464,16 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, } return (int64_t)x >> (y & 63); - case INDEX_op_rotr_i32: - return ror32(x, y & 31); - - case INDEX_op_rotr_i64: + case INDEX_op_rotr: + if (type == TCG_TYPE_I32) { + return ror32(x, y & 31); + } return ror64(x, y & 63); - case INDEX_op_rotl_i32: - return rol32(x, y & 31); - - case INDEX_op_rotl_i64: + case INDEX_op_rotl: + if (type == TCG_TYPE_I32) { + return rol32(x, y & 31); + } return rol64(x, y & 63); case INDEX_op_not: @@ -3025,8 +3025,8 @@ void tcg_optimize(TCGContext *s) case INDEX_op_remu: done = fold_remainder(&ctx, op); break; - CASE_OP_32_64(rotl): - CASE_OP_32_64(rotr): + case INDEX_op_rotl: + case INDEX_op_rotr: case INDEX_op_sar: case INDEX_op_shl: case INDEX_op_shr: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 8c8b9d179b..1989d8d12c 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -829,12 +829,12 @@ void tcg_gen_ctpop_i32(TCGv_i32 ret, TCGv_i32 arg1) void tcg_gen_rotl_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_rotl_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3_i32(INDEX_op_rotl_i32, ret, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_rotr_i32, TCG_TYPE_I32, 0)) { + if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_rotl, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_rotr, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_neg_i32(t0, arg2); - tcg_gen_op3_i32(INDEX_op_rotr_i32, ret, arg1, t0); + tcg_gen_op3_i32(INDEX_op_rotr, ret, arg1, t0); tcg_temp_free_i32(t0); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); @@ -854,12 +854,12 @@ void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) /* some cases can be optimized here */ if (arg2 == 0) { tcg_gen_mov_i32(ret, arg1); - } else if (tcg_op_supported(INDEX_op_rotl_i32, TCG_TYPE_I32, 0)) { + } else if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_constant_i32(arg2); - tcg_gen_op3_i32(INDEX_op_rotl_i32, ret, arg1, t0); - } else if (tcg_op_supported(INDEX_op_rotr_i32, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_rotl, ret, arg1, t0); + } else if (tcg_op_supported(INDEX_op_rotr, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_constant_i32(32 - arg2); - tcg_gen_op3_i32(INDEX_op_rotr_i32, ret, arg1, t0); + tcg_gen_op3_i32(INDEX_op_rotr, ret, arg1, t0); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 t1 = tcg_temp_ebb_new_i32(); @@ -873,12 +873,12 @@ void tcg_gen_rotli_i32(TCGv_i32 ret, TCGv_i32 arg1, int32_t arg2) void tcg_gen_rotr_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_rotr_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3_i32(INDEX_op_rotr_i32, ret, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_rotl_i32, TCG_TYPE_I32, 0)) { + if (tcg_op_supported(INDEX_op_rotr, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_rotr, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I32, 0)) { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_neg_i32(t0, arg2); - tcg_gen_op3_i32(INDEX_op_rotl_i32, ret, arg1, t0); + tcg_gen_op3_i32(INDEX_op_rotl, ret, arg1, t0); tcg_temp_free_i32(t0); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); @@ -2441,12 +2441,12 @@ void tcg_gen_ctpop_i64(TCGv_i64 ret, TCGv_i64 arg1) void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (tcg_op_supported(INDEX_op_rotl_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3_i64(INDEX_op_rotl_i64, ret, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_rotl_i64, TCG_TYPE_I64, 0)) { + if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_rotl, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_neg_i64(t0, arg2); - tcg_gen_op3_i64(INDEX_op_rotr_i64, ret, arg1, t0); + tcg_gen_op3_i64(INDEX_op_rotr, ret, arg1, t0); tcg_temp_free_i64(t0); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); @@ -2466,12 +2466,12 @@ void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) /* some cases can be optimized here */ if (arg2 == 0) { tcg_gen_mov_i64(ret, arg1); - } else if (tcg_op_supported(INDEX_op_rotl_i64, TCG_TYPE_I64, 0)) { + } else if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_constant_i64(arg2); - tcg_gen_op3_i64(INDEX_op_rotl_i64, ret, arg1, t0); - } else if (tcg_op_supported(INDEX_op_rotr_i64, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_rotl, ret, arg1, t0); + } else if (tcg_op_supported(INDEX_op_rotr, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_constant_i64(64 - arg2); - tcg_gen_op3_i64(INDEX_op_rotr_i64, ret, arg1, t0); + tcg_gen_op3_i64(INDEX_op_rotr, ret, arg1, t0); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); @@ -2485,12 +2485,12 @@ void tcg_gen_rotli_i64(TCGv_i64 ret, TCGv_i64 arg1, int64_t arg2) void tcg_gen_rotr_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (tcg_op_supported(INDEX_op_rotr_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3_i64(INDEX_op_rotr_i64, ret, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_rotl_i64, TCG_TYPE_I64, 0)) { + if (tcg_op_supported(INDEX_op_rotr, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_rotr, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_rotl, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_neg_i64(t0, arg2); - tcg_gen_op3_i64(INDEX_op_rotl_i64, ret, arg1, t0); + tcg_gen_op3_i64(INDEX_op_rotl, ret, arg1, t0); tcg_temp_free_i64(t0); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index 40a3e44b7c..182f19e5f0 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1042,10 +1042,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), - OUTOP(INDEX_op_rotl_i32, TCGOutOpBinary, outop_rotl), - OUTOP(INDEX_op_rotl_i64, TCGOutOpBinary, outop_rotl), - OUTOP(INDEX_op_rotr_i32, TCGOutOpBinary, outop_rotr), - OUTOP(INDEX_op_rotr_i64, TCGOutOpBinary, outop_rotr), + OUTOP(INDEX_op_rotl, TCGOutOpBinary, outop_rotl), + OUTOP(INDEX_op_rotr, TCGOutOpBinary, outop_rotr), OUTOP(INDEX_op_sar, TCGOutOpBinary, outop_sar), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), @@ -5418,10 +5416,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_orc: case INDEX_op_rems: case INDEX_op_remu: - case INDEX_op_rotl_i32: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i32: - case INDEX_op_rotr_i64: + case INDEX_op_rotl: + case INDEX_op_rotr: case INDEX_op_sar: case INDEX_op_shl: case INDEX_op_shr: diff --git a/tcg/tci.c b/tcg/tci.c index 0fb13ff61d..b1ee14e65f 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -786,11 +786,11 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, /* Shift/rotate operations (64 bit). */ - case INDEX_op_rotl_i64: + case INDEX_op_rotl: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = rol64(regs[r1], regs[r2] & 63); break; - case INDEX_op_rotr_i64: + case INDEX_op_rotr: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ror64(regs[r1], regs[r2] & 63); break; @@ -1066,13 +1066,13 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_orc: case INDEX_op_rems: case INDEX_op_remu: + case INDEX_op_rotl: + case INDEX_op_rotr: case INDEX_op_sar: case INDEX_op_shl: case INDEX_op_shr: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_rotl_i64: - case INDEX_op_rotr_i64: case INDEX_op_clz_i32: case INDEX_op_clz_i64: case INDEX_op_ctz_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 0a2da3ba47..0d15547c9f 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -773,7 +773,7 @@ static void tgen_rotl(TCGContext *s, TCGType type, { TCGOpcode opc = (type == TCG_TYPE_I32 ? INDEX_op_tci_rotl32 - : INDEX_op_rotl_i64); + : INDEX_op_rotl); tcg_out_op_rrr(s, opc, a0, a1, a2); } @@ -787,7 +787,7 @@ static void tgen_rotr(TCGContext *s, TCGType type, { TCGOpcode opc = (type == TCG_TYPE_I32 ? INDEX_op_tci_rotr32 - : INDEX_op_rotr_i64); + : INDEX_op_rotr); tcg_out_op_rrr(s, opc, a0, a1, a2); } From 8b915879b0fdd05afab41f0ca156113811ebac38 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 14:16:04 -0800 Subject: [PATCH 0421/2760] tcg: Convert clz to TCGOutOpBinary Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 - tcg/aarch64/tcg-target.c.inc | 83 +++++++++++++----------- tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 47 +++++++++----- tcg/i386/tcg-target-has.h | 2 - tcg/i386/tcg-target.c.inc | 72 +++++++++++---------- tcg/loongarch64/tcg-target-has.h | 2 - tcg/loongarch64/tcg-target.c.inc | 36 ++++++++--- tcg/mips/tcg-target-has.h | 2 - tcg/mips/tcg-target.c.inc | 86 +++++++++++++----------- tcg/ppc/tcg-target-has.h | 2 - tcg/ppc/tcg-target.c.inc | 30 ++++++--- tcg/riscv/tcg-target-has.h | 2 - tcg/riscv/tcg-target.c.inc | 34 +++++++--- tcg/s390x/tcg-target-has.h | 2 - tcg/s390x/tcg-target.c.inc | 75 +++++++++++++-------- tcg/sparc64/tcg-target-has.h | 2 - tcg/sparc64/tcg-target.c.inc | 4 ++ tcg/tcg-has.h | 1 - tcg/tcg-op.c | 108 ++++++++++++++++--------------- tcg/tcg.c | 8 +-- tcg/tci.c | 8 +-- tcg/tci/tcg-target-has.h | 2 - tcg/tci/tcg-target-opc.h.inc | 1 + tcg/tci/tcg-target.c.inc | 17 ++++- 25 files changed, 365 insertions(+), 264 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index fa79cbc1f0..8c839d8949 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -15,7 +15,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_extract2_i32 1 @@ -30,7 +29,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_extract2_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 00fca43840..3bd8231117 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1618,37 +1618,6 @@ static inline void tcg_out_mb(TCGContext *s, TCGArg a0) tcg_out32(s, sync[a0 & TCG_MO_ALL]); } -static void tcg_out_cltz(TCGContext *s, TCGType ext, TCGReg d, - TCGReg a0, TCGArg b, bool const_b, bool is_ctz) -{ - TCGReg a1 = a0; - if (is_ctz) { - a1 = TCG_REG_TMP0; - tcg_out_insn(s, 3507, RBIT, ext, a1, a0); - } - if (const_b && b == (ext ? 64 : 32)) { - tcg_out_insn(s, 3507, CLZ, ext, d, a1); - } else { - AArch64Insn sel = I3506_CSEL; - - tcg_out_cmp(s, ext, TCG_COND_NE, a0, 0, 1); - tcg_out_insn(s, 3507, CLZ, ext, TCG_REG_TMP0, a1); - - if (const_b) { - if (b == -1) { - b = TCG_REG_XZR; - sel = I3506_CSINV; - } else if (b == 0) { - b = TCG_REG_XZR; - } else { - tcg_out_movi(s, ext, d, b); - b = d; - } - } - tcg_out_insn_3506(s, sel, ext, d, TCG_REG_TMP0, b, TCG_COND_NE); - } -} - typedef struct { TCGReg base; TCGReg index; @@ -2121,6 +2090,45 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_cmp(s, type, TCG_COND_NE, a1, 0, true); + tcg_out_insn(s, 3507, CLZ, type, TCG_REG_TMP0, a1); + tcg_out_insn(s, 3506, CSEL, type, a0, TCG_REG_TMP0, a2, TCG_COND_NE); +} + +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 == (type == TCG_TYPE_I32 ? 32 : 64)) { + tcg_out_insn(s, 3507, CLZ, type, a0, a1); + return; + } + + tcg_out_cmp(s, type, TCG_COND_NE, a1, 0, true); + tcg_out_insn(s, 3507, CLZ, type, a0, a1); + + switch (a2) { + case -1: + tcg_out_insn(s, 3506, CSINV, type, a0, a0, TCG_REG_XZR, TCG_COND_NE); + break; + case 0: + tcg_out_insn(s, 3506, CSEL, type, a0, a0, TCG_REG_XZR, TCG_COND_NE); + break; + default: + tcg_out_movi(s, type, TCG_REG_TMP0, a2); + tcg_out_insn(s, 3506, CSEL, type, a0, a0, TCG_REG_TMP0, TCG_COND_NE); + break; + } +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_O1_I2(r, r, rAL), + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; + static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2460,13 +2468,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_clz_i64: - case INDEX_op_clz_i32: - tcg_out_cltz(s, ext, a0, a1, a2, c2, false); - break; case INDEX_op_ctz_i64: case INDEX_op_ctz_i32: - tcg_out_cltz(s, ext, a0, a1, a2, c2, true); + tcg_out_insn(s, 3507, RBIT, ext, TCG_REG_TMP0, a1); + if (c2) { + tgen_clzi(s, ext, a0, TCG_REG_TMP0, a2); + } else { + tgen_clz(s, ext, a0, TCG_REG_TMP0, a2); + } break; case INDEX_op_brcond_i32: @@ -3089,9 +3098,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rC); - case INDEX_op_clz_i32: case INDEX_op_ctz_i32: - case INDEX_op_clz_i64: case INDEX_op_ctz_i64: return C_O1_I2(r, r, rAL); diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 12ffbcda2b..fceec2f0ca 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -26,7 +26,6 @@ extern bool use_neon_instructions; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 use_armv7_instructions #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_extract2_i32 1 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 462f0ec08d..681eb5aba1 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1862,6 +1862,32 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0, a1, 0); + tcg_out_dat_reg(s, COND_NE, INSN_CLZ, a0, 0, a1, 0); + tcg_out_mov_reg(s, COND_EQ, a0, a2); +} + +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 == 32) { + tcg_out_dat_reg(s, COND_AL, INSN_CLZ, a0, 0, a1, 0); + } else { + tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0, a1, 0); + tcg_out_dat_reg(s, COND_NE, INSN_CLZ, a0, 0, a1, 0); + tcg_out_movi32(s, COND_EQ, a0, a2); + } +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_O1_I2(r, r, rIK), + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; + static TCGConstraintSetIndex cset_idiv(TCGType type, unsigned flags) { return use_idiv_instructions ? C_O1_I2(r, r, r) : C_NotImplemented; @@ -2196,23 +2222,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_ctz_i32: tcg_out_dat_reg(s, COND_AL, INSN_RBIT, TCG_REG_TMP, 0, args[1], 0); - a1 = TCG_REG_TMP; - goto do_clz; - - case INDEX_op_clz_i32: - a1 = args[1]; - do_clz: - a0 = args[0]; - a2 = args[2]; - c = const_args[2]; - if (c && a2 == 32) { - tcg_out_dat_reg(s, COND_AL, INSN_CLZ, a0, 0, a1, 0); - break; - } - tcg_out_dat_imm(s, COND_AL, ARITH_CMP, 0, a1, 0); - tcg_out_dat_reg(s, COND_NE, INSN_CLZ, a0, 0, a1, 0); - if (c || a0 != a2) { - tcg_out_dat_rIK(s, COND_EQ, ARITH_MOV, ARITH_MVN, a0, 0, a2, c); + if (const_args[2]) { + tgen_clzi(s, TCG_TYPE_I32, args[0], TCG_REG_TMP, args[2]); + } else { + tgen_clz(s, TCG_TYPE_I32, args[0], TCG_REG_TMP, args[2]); } break; diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index a7199463df..2277872ff3 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -28,7 +28,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 have_popcnt #define TCG_TARGET_HAS_extract2_i32 1 @@ -44,7 +43,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 have_popcnt #define TCG_TARGET_HAS_extract2_i64 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index dd35bba57f..0edd4cbc07 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1869,32 +1869,6 @@ static void tcg_out_ctz(TCGContext *s, int rexw, TCGReg dest, TCGReg arg1, } } -static void tcg_out_clz(TCGContext *s, int rexw, TCGReg dest, TCGReg arg1, - TCGArg arg2, bool const_a2) -{ - if (have_lzcnt) { - tcg_out_modrm(s, OPC_LZCNT + rexw, dest, arg1); - if (const_a2) { - tcg_debug_assert(arg2 == (rexw ? 64 : 32)); - } else { - tcg_debug_assert(dest != arg2); - tcg_out_cmov(s, JCC_JB, rexw, dest, arg2); - } - } else { - tcg_debug_assert(!const_a2); - tcg_debug_assert(dest != arg1); - tcg_debug_assert(dest != arg2); - - /* Recall that the output of BSR is the index not the count. */ - tcg_out_modrm(s, OPC_BSR + rexw, dest, arg1); - tgen_arithi(s, ARITH_XOR + rexw, dest, rexw ? 63 : 31, 0); - - /* Since we have destroyed the flags from BSR, we have to re-test. */ - int jcc = tcg_out_cmp(s, TCG_COND_EQ, arg1, 0, 1, rexw); - tcg_out_cmov(s, jcc, rexw, dest, arg2); - } -} - static void tcg_out_branch(TCGContext *s, int call, const tcg_insn_unit *dest) { intptr_t disp = tcg_pcrel_diff(s, dest) - 5; @@ -2633,6 +2607,45 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + int jcc; + + if (have_lzcnt) { + tcg_out_modrm(s, OPC_LZCNT + rexw, a0, a1); + jcc = JCC_JB; + } else { + /* Recall that the output of BSR is the index not the count. */ + tcg_out_modrm(s, OPC_BSR + rexw, a0, a1); + tgen_arithi(s, ARITH_XOR + rexw, a0, rexw ? 63 : 31, 0); + + /* Since we have destroyed the flags from BSR, we have to re-test. */ + jcc = tcg_out_cmp(s, TCG_COND_EQ, a1, 0, 1, rexw); + } + tcg_out_cmov(s, jcc, rexw, a0, a2); +} + +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_LZCNT + rexw, a0, a1); +} + +static TCGConstraintSetIndex cset_clz(TCGType type, unsigned flags) +{ + return have_lzcnt ? C_N1_I2(r, r, rW) : C_N1_I2(r, r, r); +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_clz, + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; + static const TCGOutOpBinary outop_divs = { .base.static_constraint = C_NotImplemented, }; @@ -3019,9 +3032,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, OP_32_64(ctz): tcg_out_ctz(s, rexw, args[0], args[1], args[2], const_args[2]); break; - OP_32_64(clz): - tcg_out_clz(s, rexw, args[0], args[1], args[2], const_args[2]); - break; OP_32_64(ctpop): tcg_out_modrm(s, OPC_POPCNT + rexw, a0, a1); break; @@ -3907,10 +3917,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ctz_i64: return have_bmi1 ? C_N1_I2(r, r, rW) : C_N1_I2(r, r, r); - case INDEX_op_clz_i32: - case INDEX_op_clz_i64: - return have_lzcnt ? C_N1_I2(r, r, rW) : C_N1_I2(r, r, r); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, L); diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 303134390a..2eba2132b8 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -18,7 +18,6 @@ #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -30,7 +29,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_add2_i64 0 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 26cf982780..332ce6c86b 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1328,6 +1328,33 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* a2 is constrained to exactly the type width. */ + if (type == TCG_TYPE_I32) { + tcg_out_opc_clz_w(s, a0, a1); + } else { + tcg_out_opc_clz_d(s, a0, a1); + } +} + +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_clzi(s, type, TCG_REG_TMP0, a1, /* ignored */ 0); + /* a0 = a1 ? REG_TMP0 : a2 */ + tcg_out_opc_maskeqz(s, TCG_REG_TMP0, TCG_REG_TMP0, a1); + tcg_out_opc_masknez(s, a0, a2, a1); + tcg_out_opc_or(s, a0, a0, TCG_REG_TMP0); +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_O1_I2(r, r, rW), + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; + static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1754,13 +1781,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_revb_d(s, a0, a1); break; - case INDEX_op_clz_i32: - tcg_out_clzctz(s, OPC_CLZ_W, a0, a1, a2, c2, true); - break; - case INDEX_op_clz_i64: - tcg_out_clzctz(s, OPC_CLZ_D, a0, a1, a2, c2, false); - break; - case INDEX_op_ctz_i32: tcg_out_clzctz(s, OPC_CTZ_W, a0, a1, a2, c2, true); break; @@ -2398,8 +2418,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_clz_i32: - case INDEX_op_clz_i64: case INDEX_op_ctz_i32: case INDEX_op_ctz_i64: return C_O1_I2(r, r, rW); diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 880eb084eb..c27ca7e543 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -60,7 +60,6 @@ extern bool use_mips32r2_instructions; /* optional instructions detected at runtime */ #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_clz_i32 use_mips32r2_instructions #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -70,7 +69,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_clz_i64 use_mips32r2_instructions #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 #endif diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index fb9fe0c40e..5052d6481c 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1563,33 +1563,6 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) tcg_out32(s, sync[a0 & TCG_MO_ALL]); } -static void tcg_out_clz(TCGContext *s, MIPSInsn opcv2, MIPSInsn opcv6, - int width, TCGReg a0, TCGReg a1, TCGArg a2) -{ - if (use_mips32r6_instructions) { - if (a2 == width) { - tcg_out_opc_reg(s, opcv6, a0, a1, 0); - } else { - tcg_out_opc_reg(s, opcv6, TCG_TMP0, a1, 0); - tcg_out_movcond(s, TCG_COND_EQ, a0, a1, 0, a2, TCG_TMP0); - } - } else { - if (a2 == width) { - tcg_out_opc_reg(s, opcv2, a0, a1, a1); - } else if (a0 == a2) { - tcg_out_opc_reg(s, opcv2, TCG_TMP0, a1, a1); - tcg_out_opc_reg(s, OPC_MOVN, a0, TCG_TMP0, a1); - } else if (a0 != a1) { - tcg_out_opc_reg(s, opcv2, a0, a1, a1); - tcg_out_opc_reg(s, OPC_MOVZ, a0, a2, a1); - } else { - tcg_out_opc_reg(s, opcv2, TCG_TMP0, a1, a1); - tcg_out_opc_reg(s, OPC_MOVZ, TCG_TMP0, a2, a1); - tcg_out_mov(s, TCG_TYPE_REG, a0, TCG_TMP0); - } - } -} - static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) { TCGReg base = TCG_REG_ZERO; @@ -1712,6 +1685,55 @@ static const TCGOutOpBinary outop_andc = { .base.static_constraint = C_NotImplemented, }; +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (use_mips32r6_instructions) { + MIPSInsn opcv6 = type == TCG_TYPE_I32 ? OPC_CLZ_R6 : OPC_DCLZ_R6; + tcg_out_opc_reg(s, opcv6, TCG_TMP0, a1, 0); + tcg_out_movcond(s, TCG_COND_EQ, a0, a1, 0, a2, TCG_TMP0); + } else { + MIPSInsn opcv2 = type == TCG_TYPE_I32 ? OPC_CLZ : OPC_DCLZ; + if (a0 == a2) { + tcg_out_opc_reg(s, opcv2, TCG_TMP0, a1, a1); + tcg_out_opc_reg(s, OPC_MOVN, a0, TCG_TMP0, a1); + } else if (a0 != a1) { + tcg_out_opc_reg(s, opcv2, a0, a1, a1); + tcg_out_opc_reg(s, OPC_MOVZ, a0, a2, a1); + } else { + tcg_out_opc_reg(s, opcv2, TCG_TMP0, a1, a1); + tcg_out_opc_reg(s, OPC_MOVZ, TCG_TMP0, a2, a1); + tcg_out_mov(s, type, a0, TCG_TMP0); + } + } +} + +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 == 0) { + tgen_clz(s, type, a0, a1, TCG_REG_ZERO); + } else if (use_mips32r6_instructions) { + MIPSInsn opcv6 = type == TCG_TYPE_I32 ? OPC_CLZ_R6 : OPC_DCLZ_R6; + tcg_out_opc_reg(s, opcv6, a0, a1, 0); + } else { + MIPSInsn opcv2 = type == TCG_TYPE_I32 ? OPC_CLZ : OPC_DCLZ; + tcg_out_opc_reg(s, opcv2, a0, a1, a1); + } +} + +static TCGConstraintSetIndex cset_clz(TCGType type, unsigned flags) +{ + return use_mips32r2_instructions ? C_O1_I2(r, r, rzW) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_clz, + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; + static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2165,13 +2187,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_dsra(s, a0, a1, 32); break; - case INDEX_op_clz_i32: - tcg_out_clz(s, OPC_CLZ, OPC_CLZ_R6, 32, a0, a1, a2); - break; - case INDEX_op_clz_i64: - tcg_out_clz(s, OPC_DCLZ, OPC_DCLZ_R6, 64, a0, a1, a2); - break; - case INDEX_op_deposit_i32: tcg_out_opc_bf(s, OPC_INS, a0, a2, args[3] + args[4] - 1, args[3]); break; @@ -2329,9 +2344,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_muls2_i64: case INDEX_op_mulu2_i64: return C_O2_I2(r, r, r, r); - case INDEX_op_clz_i32: - case INDEX_op_clz_i64: - return C_O1_I2(r, r, rzW); case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 71c02d88b9..cd7346011b 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -19,7 +19,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 have_isa_3_00 #define TCG_TARGET_HAS_ctpop_i32 have_isa_2_06 #define TCG_TARGET_HAS_extract2_i32 0 @@ -35,7 +34,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 have_isa_3_00 #define TCG_TARGET_HAS_ctpop_i64 have_isa_2_06 #define TCG_TARGET_HAS_extract2_i64 0 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 687b66af54..518cf1e9ef 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2954,6 +2954,26 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? CNTLZW : CNTLZD; + tcg_out_cntxz(s, type, insn, a0, a1, a2, false); +} + +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? CNTLZW : CNTLZD; + tcg_out_cntxz(s, type, insn, a0, a1, a2, true); +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_O1_I2(r, r, rZW), + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3350,10 +3370,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); break; - case INDEX_op_clz_i32: - tcg_out_cntxz(s, TCG_TYPE_I32, CNTLZW, args[0], args[1], - args[2], const_args[2]); - break; case INDEX_op_ctz_i32: tcg_out_cntxz(s, TCG_TYPE_I32, CNTTZW, args[0], args[1], args[2], const_args[2]); @@ -3362,10 +3378,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, CNTPOPW | SAB(args[1], args[0], 0)); break; - case INDEX_op_clz_i64: - tcg_out_cntxz(s, TCG_TYPE_I64, CNTLZD, args[0], args[1], - args[2], const_args[2]); - break; case INDEX_op_ctz_i64: tcg_out_cntxz(s, TCG_TYPE_I64, CNTTZD, args[0], args[1], args[2], const_args[2]); @@ -4228,9 +4240,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_clz_i32: case INDEX_op_ctz_i32: - case INDEX_op_clz_i64: case INDEX_op_ctz_i64: return C_O1_I2(r, r, rZW); diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index c7745a6462..41e287130d 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -18,7 +18,6 @@ #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_clz_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctz_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctpop_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -29,7 +28,6 @@ #define TCG_TARGET_HAS_bswap16_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_clz_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctz_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctpop_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 4dd892d98d..77eef02db5 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1997,6 +1997,32 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_CLZW : OPC_CLZ; + tcg_out_cltz(s, type, insn, a0, a1, a2, false); +} + +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_CLZW : OPC_CLZ; + tcg_out_cltz(s, type, insn, a0, a1, a2, true); +} + +static TCGConstraintSetIndex cset_clzctz(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_ZBB ? C_N1_I2(r, r, rM) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_clzctz, + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; + static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2398,12 +2424,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_imm(s, OPC_CPOP, a0, a1, 0); break; - case INDEX_op_clz_i32: - tcg_out_cltz(s, TCG_TYPE_I32, OPC_CLZW, a0, a1, a2, c2); - break; - case INDEX_op_clz_i64: - tcg_out_cltz(s, TCG_TYPE_I64, OPC_CLZ, a0, a1, a2, c2); - break; case INDEX_op_ctz_i32: tcg_out_cltz(s, TCG_TYPE_I32, OPC_CTZW, a0, a1, a2, c2); break; @@ -2793,8 +2813,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_clz_i32: - case INDEX_op_clz_i64: case INDEX_op_ctz_i32: case INDEX_op_ctz_i64: return C_N1_I2(r, r, rM); diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index eaddf7005e..85a4f23e95 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -31,7 +31,6 @@ extern uint64_t s390_facilities[3]; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_clz_i32 0 #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 @@ -46,7 +45,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 76180dabcb..adfe403bef 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1514,27 +1514,6 @@ static void tgen_movcond(TCGContext *s, TCGType type, TCGCond c, TCGReg dest, tgen_movcond_int(s, type, dest, v3, v3const, v4, cc, inv_cc); } -static void tgen_clz(TCGContext *s, TCGReg dest, TCGReg a1, - TCGArg a2, int a2const) -{ - /* Since this sets both R and R+1, we have no choice but to store the - result into R0, allowing R1 == TCG_TMP0 to be clobbered as well. */ - QEMU_BUILD_BUG_ON(TCG_TMP0 != TCG_REG_R1); - tcg_out_insn(s, RRE, FLOGR, TCG_REG_R0, a1); - - if (a2const && a2 == 64) { - tcg_out_mov(s, TCG_TYPE_I64, dest, TCG_REG_R0); - return; - } - - /* - * Conditions from FLOGR are: - * 2 -> one bit found - * 8 -> no one bit found - */ - tgen_movcond_int(s, TCG_TYPE_I64, dest, a2, a2const, TCG_REG_R0, 8, 2); -} - static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) { /* With MIE3, and bit 0 of m4 set, we get the complete result. */ @@ -2242,6 +2221,53 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_clz_int(TCGContext *s, TCGReg dest, TCGReg a1, + TCGArg a2, int a2const) +{ + /* + * Since this sets both R and R+1, we have no choice but to store the + * result into R0, allowing R1 == TCG_TMP0 to be clobbered as well. + */ + QEMU_BUILD_BUG_ON(TCG_TMP0 != TCG_REG_R1); + tcg_out_insn(s, RRE, FLOGR, TCG_REG_R0, a1); + + if (a2const && a2 == 64) { + tcg_out_mov(s, TCG_TYPE_I64, dest, TCG_REG_R0); + return; + } + + /* + * Conditions from FLOGR are: + * 2 -> one bit found + * 8 -> no one bit found + */ + tgen_movcond_int(s, TCG_TYPE_I64, dest, a2, a2const, TCG_REG_R0, 8, 2); +} + +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_clz_int(s, a0, a1, a2, false); +} + +static void tgen_clzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_clz_int(s, a0, a1, a2, true); +} + +static TCGConstraintSetIndex cset_clz(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_I64 ? C_O1_I2(r, r, rI) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_clz, + .out_rrr = tgen_clz, + .out_rri = tgen_clzi, +}; + static const TCGOutOpBinary outop_divs = { .base.static_constraint = C_NotImplemented, }; @@ -2884,10 +2910,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tgen_sextract(s, args[0], args[1], args[2], args[3]); break; - case INDEX_op_clz_i64: - tgen_clz(s, args[0], args[1], args[2], const_args[2]); - break; - case INDEX_op_ctpop_i32: tgen_ctpop(s, TCG_TYPE_I32, args[0], args[1]); break; @@ -3387,9 +3409,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rC); - case INDEX_op_clz_i64: - return C_O1_I2(r, r, rI); - case INDEX_op_brcond_i32: return C_O0_I2(r, ri); case INDEX_op_brcond_i64: diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 1dd86c363d..21fa0f3663 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -16,7 +16,6 @@ extern bool use_vis3_instructions; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 -#define TCG_TARGET_HAS_clz_i32 0 #define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 @@ -31,7 +30,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_clz_i64 0 #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 57b26ae33b..a4fb41764b 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1318,6 +1318,10 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_divs_rJ(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGArg a2, bool c2) { diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 7bfa55adb1..27d6ec7636 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -15,7 +15,6 @@ #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_clz_i64 0 #define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 1989d8d12c..e1e57ff3f8 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -723,9 +723,9 @@ void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_clz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_clz_i32) { + if (tcg_op_supported(INDEX_op_clz_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_clz_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_clz_i64) { + } else if (tcg_op_supported(INDEX_op_clz_i64, TCG_TYPE_I64, 0)) { TCGv_i64 t1 = tcg_temp_ebb_new_i64(); TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t1, arg1); @@ -748,9 +748,13 @@ void tcg_gen_clzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { + TCGv_i32 z, t; + if (TCG_TARGET_HAS_ctz_i32) { tcg_gen_op3_i32(INDEX_op_ctz_i32, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_ctz_i64) { + return; + } + if (TCG_TARGET_HAS_ctz_i64) { TCGv_i64 t1 = tcg_temp_ebb_new_i64(); TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t1, arg1); @@ -759,29 +763,28 @@ void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_extrl_i64_i32(ret, t1); tcg_temp_free_i64(t1); tcg_temp_free_i64(t2); - } else if (TCG_TARGET_HAS_ctpop_i32 - || TCG_TARGET_HAS_ctpop_i64 - || TCG_TARGET_HAS_clz_i32 - || TCG_TARGET_HAS_clz_i64) { - TCGv_i32 z, t = tcg_temp_ebb_new_i32(); - - if (TCG_TARGET_HAS_ctpop_i32 || TCG_TARGET_HAS_ctpop_i64) { - tcg_gen_subi_i32(t, arg1, 1); - tcg_gen_andc_i32(t, t, arg1); - tcg_gen_ctpop_i32(t, t); - } else { - /* Since all non-x86 hosts have clz(0) == 32, don't fight it. */ - tcg_gen_neg_i32(t, arg1); - tcg_gen_and_i32(t, t, arg1); - tcg_gen_clzi_i32(t, t, 32); - tcg_gen_xori_i32(t, t, 31); - } - z = tcg_constant_i32(0); - tcg_gen_movcond_i32(TCG_COND_EQ, ret, arg1, z, arg2, t); - tcg_temp_free_i32(t); + return; + } + if (TCG_TARGET_HAS_ctpop_i32 || TCG_TARGET_HAS_ctpop_i64) { + t = tcg_temp_ebb_new_i32(); + tcg_gen_subi_i32(t, arg1, 1); + tcg_gen_andc_i32(t, t, arg1); + tcg_gen_ctpop_i32(t, t); + } else if (tcg_op_supported(INDEX_op_clz_i32, TCG_TYPE_I32, 0) || + tcg_op_supported(INDEX_op_clz_i64, TCG_TYPE_I64, 0)) { + t = tcg_temp_ebb_new_i32(); + tcg_gen_neg_i32(t, arg1); + tcg_gen_and_i32(t, t, arg1); + tcg_gen_clzi_i32(t, t, 32); + tcg_gen_xori_i32(t, t, 31); } else { gen_helper_ctz_i32(ret, arg1, arg2); + return; } + + z = tcg_constant_i32(0); + tcg_gen_movcond_i32(TCG_COND_EQ, ret, arg1, z, arg2, t); + tcg_temp_free_i32(t); } void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) @@ -800,7 +803,8 @@ void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) void tcg_gen_clrsb_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_clz_i32) { + if (tcg_op_supported(INDEX_op_clz_i32, TCG_TYPE_I32, 0) || + tcg_op_supported(INDEX_op_clz_i64, TCG_TYPE_I64, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_sari_i32(t, arg, 31); tcg_gen_xor_i32(t, t, arg); @@ -2336,7 +2340,7 @@ void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_clz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (TCG_TARGET_HAS_clz_i64) { + if (tcg_op_supported(INDEX_op_clz_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_clz_i64, ret, arg1, arg2); } else { gen_helper_clz_i64(ret, arg1, arg2); @@ -2346,8 +2350,8 @@ void tcg_gen_clz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_clzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) { if (TCG_TARGET_REG_BITS == 32 - && TCG_TARGET_HAS_clz_i32 - && arg2 <= 0xffffffffu) { + && arg2 <= 0xffffffffu + && tcg_op_supported(INDEX_op_clz_i32, TCG_TYPE_I32, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_clzi_i32(t, TCGV_LOW(arg1), arg2 - 32); tcg_gen_addi_i32(t, t, 32); @@ -2361,45 +2365,47 @@ void tcg_gen_clzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) void tcg_gen_ctz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { + TCGv_i64 z, t; + if (TCG_TARGET_HAS_ctz_i64) { tcg_gen_op3_i64(INDEX_op_ctz_i64, ret, arg1, arg2); - } else if (TCG_TARGET_HAS_ctpop_i64 || TCG_TARGET_HAS_clz_i64) { - TCGv_i64 z, t = tcg_temp_ebb_new_i64(); - - if (TCG_TARGET_HAS_ctpop_i64) { - tcg_gen_subi_i64(t, arg1, 1); - tcg_gen_andc_i64(t, t, arg1); - tcg_gen_ctpop_i64(t, t); - } else { - /* Since all non-x86 hosts have clz(0) == 64, don't fight it. */ - tcg_gen_neg_i64(t, arg1); - tcg_gen_and_i64(t, t, arg1); - tcg_gen_clzi_i64(t, t, 64); - tcg_gen_xori_i64(t, t, 63); - } - z = tcg_constant_i64(0); - tcg_gen_movcond_i64(TCG_COND_EQ, ret, arg1, z, arg2, t); - tcg_temp_free_i64(t); - tcg_temp_free_i64(z); + return; + } + if (TCG_TARGET_HAS_ctpop_i64) { + t = tcg_temp_ebb_new_i64(); + tcg_gen_subi_i64(t, arg1, 1); + tcg_gen_andc_i64(t, t, arg1); + tcg_gen_ctpop_i64(t, t); + } else if (tcg_op_supported(INDEX_op_clz_i64, TCG_TYPE_I64, 0)) { + t = tcg_temp_ebb_new_i64(); + tcg_gen_neg_i64(t, arg1); + tcg_gen_and_i64(t, t, arg1); + tcg_gen_clzi_i64(t, t, 64); + tcg_gen_xori_i64(t, t, 63); } else { gen_helper_ctz_i64(ret, arg1, arg2); + return; } + + z = tcg_constant_i64(0); + tcg_gen_movcond_i64(TCG_COND_EQ, ret, arg1, z, arg2, t); + tcg_temp_free_i64(t); } void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) { if (TCG_TARGET_REG_BITS == 32 - && TCG_TARGET_HAS_ctz_i32 - && arg2 <= 0xffffffffu) { + && arg2 <= 0xffffffffu + && tcg_op_supported(INDEX_op_ctz_i32, TCG_TYPE_I32, 0)) { TCGv_i32 t32 = tcg_temp_ebb_new_i32(); tcg_gen_ctzi_i32(t32, TCGV_HIGH(arg1), arg2 - 32); tcg_gen_addi_i32(t32, t32, 32); tcg_gen_ctz_i32(TCGV_LOW(ret), TCGV_LOW(arg1), t32); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); tcg_temp_free_i32(t32); - } else if (!TCG_TARGET_HAS_ctz_i64 - && TCG_TARGET_HAS_ctpop_i64 - && arg2 == 64) { + } else if (arg2 == 64 + && !tcg_op_supported(INDEX_op_ctz_i64, TCG_TYPE_I64, 0) + && TCG_TARGET_HAS_ctpop_i64) { /* This equivalence has the advantage of not requiring a fixup. */ TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_subi_i64(t, arg1, 1); @@ -2413,7 +2419,7 @@ void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) void tcg_gen_clrsb_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (TCG_TARGET_HAS_clz_i64 || TCG_TARGET_HAS_clz_i32) { + if (tcg_op_supported(INDEX_op_clz_i64, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_sari_i64(t, arg, 63); tcg_gen_xor_i64(t, t, arg); diff --git a/tcg/tcg.c b/tcg/tcg.c index 182f19e5f0..e04d3adcec 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1026,6 +1026,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), + OUTOP(INDEX_op_clz_i32, TCGOutOpBinary, outop_clz), + OUTOP(INDEX_op_clz_i64, TCGOutOpBinary, outop_clz), OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), @@ -2288,8 +2290,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap16_i32; case INDEX_op_bswap32_i32: return TCG_TARGET_HAS_bswap32_i32; - case INDEX_op_clz_i32: - return TCG_TARGET_HAS_clz_i32; case INDEX_op_ctz_i32: return TCG_TARGET_HAS_ctz_i32; case INDEX_op_ctpop_i32: @@ -2333,8 +2333,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap32_i64; case INDEX_op_bswap64_i64: return TCG_TARGET_HAS_bswap64_i64; - case INDEX_op_clz_i64: - return TCG_TARGET_HAS_clz_i64; case INDEX_op_ctz_i64: return TCG_TARGET_HAS_ctz_i64; case INDEX_op_ctpop_i64: @@ -5404,6 +5402,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: + case INDEX_op_clz_i32: + case INDEX_op_clz_i64: case INDEX_op_divs: case INDEX_op_divu: case INDEX_op_eqv: diff --git a/tcg/tci.c b/tcg/tci.c index b1ee14e65f..11b11ce642 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -594,13 +594,11 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint32_t)regs[r1] % (uint32_t)regs[r2]; break; -#if TCG_TARGET_HAS_clz_i32 - case INDEX_op_clz_i32: + case INDEX_op_tci_clz32: tci_args_rrr(insn, &r0, &r1, &r2); tmp32 = regs[r1]; regs[r0] = tmp32 ? clz32(tmp32) : regs[r2]; break; -#endif #if TCG_TARGET_HAS_ctz_i32 case INDEX_op_ctz_i32: tci_args_rrr(insn, &r0, &r1, &r2); @@ -735,12 +733,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint64_t)regs[r1] % (uint64_t)regs[r2]; break; -#if TCG_TARGET_HAS_clz_i64 case INDEX_op_clz_i64: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ? clz64(regs[r1]) : regs[r2]; break; -#endif #if TCG_TARGET_HAS_ctz_i64 case INDEX_op_ctz_i64: tci_args_rrr(insn, &r0, &r1, &r2); @@ -1073,10 +1069,10 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_shr: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_clz_i32: case INDEX_op_clz_i64: case INDEX_op_ctz_i32: case INDEX_op_ctz_i64: + case INDEX_op_tci_clz32: case INDEX_op_tci_divs32: case INDEX_op_tci_divu32: case INDEX_op_tci_rems32: diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 04d341a8d2..ae1f724702 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -10,7 +10,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_clz_i32 1 #define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 @@ -23,7 +22,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_clz_i64 1 #define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 0 diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc index cff215490a..04774ca9c4 100644 --- a/tcg/tci/tcg-target-opc.h.inc +++ b/tcg/tci/tcg-target-opc.h.inc @@ -2,6 +2,7 @@ /* These opcodes for use between the tci generator and interpreter. */ DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT) +DEF(tci_clz32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_divs32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_divu32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_rems32, 1, 2, 0, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 0d15547c9f..ee7e6f15eb 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -83,8 +83,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i64: case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: - case INDEX_op_clz_i32: - case INDEX_op_clz_i64: case INDEX_op_ctz_i32: case INDEX_op_ctz_i64: return C_O1_I2(r, r, r); @@ -630,6 +628,20 @@ static const TCGOutOpBinary outop_andc = { .out_rrr = tgen_andc, }; +static void tgen_clz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_clz32 + : INDEX_op_clz_i64); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_clz = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_clz, +}; + static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -921,7 +933,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, opc, args[0], args[1], args[2]); break; - CASE_32_64(clz) /* Optional (TCG_TARGET_HAS_clz_*). */ CASE_32_64(ctz) /* Optional (TCG_TARGET_HAS_ctz_*). */ tcg_out_op_rrr(s, opc, args[0], args[1], args[2]); break; From 5a5bb0a5a0b879c8f110c6a9bde9146181ef840c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 16:12:46 -0800 Subject: [PATCH 0422/2760] tcg: Merge INDEX_op_clz_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 10 +++++----- tcg/tcg-op.c | 22 ++++++++++------------ tcg/tcg.c | 6 ++---- tcg/tci.c | 4 ++-- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 22 insertions(+), 27 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index c3a6499d01..22f0432988 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -358,7 +358,7 @@ Logical - | *t0* = *t1* | ~\ *t2* - * - clz_i32/i64 *t0*, *t1*, *t2* + * - clz *t0*, *t1*, *t2* - | *t0* = *t1* ? clz(*t1*) : *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 25fd93eb28..ad1d193ef4 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -42,6 +42,7 @@ DEF(mov, 1, 1, 0, TCG_OPF_INT | TCG_OPF_NOT_PRESENT) DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) +DEF(clz, 1, 2, 0, TCG_OPF_INT) DEF(divs, 1, 2, 0, TCG_OPF_INT) DEF(divs2, 2, 3, 0, TCG_OPF_INT) DEF(divu, 1, 2, 0, TCG_OPF_INT) @@ -95,7 +96,6 @@ DEF(setcond2_i32, 1, 4, 1, 0) DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) -DEF(clz_i32, 1, 2, 0, 0) DEF(ctz_i32, 1, 2, 0, 0) DEF(ctpop_i32, 1, 1, 0, 0) @@ -130,7 +130,6 @@ DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(bswap16_i64, 1, 1, 1, 0) DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) -DEF(clz_i64, 1, 2, 0, 0) DEF(ctz_i64, 1, 2, 0, 0) DEF(ctpop_i64, 1, 1, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index 97a566a617..d8d0e728aa 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -503,10 +503,10 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, case INDEX_op_nor_vec: return ~(x | y); - case INDEX_op_clz_i32: - return (uint32_t)x ? clz32(x) : y; - - case INDEX_op_clz_i64: + case INDEX_op_clz: + if (type == TCG_TYPE_I32) { + return (uint32_t)x ? clz32(x) : y; + } return x ? clz64(x) : y; case INDEX_op_ctz_i32: @@ -2898,7 +2898,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_bswap64_i64: done = fold_bswap(&ctx, op); break; - CASE_OP_32_64(clz): + case INDEX_op_clz: CASE_OP_32_64(ctz): done = fold_count_zeros(&ctx, op); break; diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index e1e57ff3f8..76e9efc655 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -723,9 +723,9 @@ void tcg_gen_orc_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_clz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_clz_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3_i32(INDEX_op_clz_i32, ret, arg1, arg2); - } else if (tcg_op_supported(INDEX_op_clz_i64, TCG_TYPE_I64, 0)) { + if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_clz, ret, arg1, arg2); + } else if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_I64, 0)) { TCGv_i64 t1 = tcg_temp_ebb_new_i64(); TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t1, arg1); @@ -770,8 +770,7 @@ void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) tcg_gen_subi_i32(t, arg1, 1); tcg_gen_andc_i32(t, t, arg1); tcg_gen_ctpop_i32(t, t); - } else if (tcg_op_supported(INDEX_op_clz_i32, TCG_TYPE_I32, 0) || - tcg_op_supported(INDEX_op_clz_i64, TCG_TYPE_I64, 0)) { + } else if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_REG, 0)) { t = tcg_temp_ebb_new_i32(); tcg_gen_neg_i32(t, arg1); tcg_gen_and_i32(t, t, arg1); @@ -803,8 +802,7 @@ void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) void tcg_gen_clrsb_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (tcg_op_supported(INDEX_op_clz_i32, TCG_TYPE_I32, 0) || - tcg_op_supported(INDEX_op_clz_i64, TCG_TYPE_I64, 0)) { + if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_REG, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_sari_i32(t, arg, 31); tcg_gen_xor_i32(t, t, arg); @@ -2340,8 +2338,8 @@ void tcg_gen_orc_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_clz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { - if (tcg_op_supported(INDEX_op_clz_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3_i64(INDEX_op_clz_i64, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_clz, ret, arg1, arg2); } else { gen_helper_clz_i64(ret, arg1, arg2); } @@ -2351,7 +2349,7 @@ void tcg_gen_clzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) { if (TCG_TARGET_REG_BITS == 32 && arg2 <= 0xffffffffu - && tcg_op_supported(INDEX_op_clz_i32, TCG_TYPE_I32, 0)) { + && tcg_op_supported(INDEX_op_clz, TCG_TYPE_I32, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_clzi_i32(t, TCGV_LOW(arg1), arg2 - 32); tcg_gen_addi_i32(t, t, 32); @@ -2376,7 +2374,7 @@ void tcg_gen_ctz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_subi_i64(t, arg1, 1); tcg_gen_andc_i64(t, t, arg1); tcg_gen_ctpop_i64(t, t); - } else if (tcg_op_supported(INDEX_op_clz_i64, TCG_TYPE_I64, 0)) { + } else if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_I64, 0)) { t = tcg_temp_ebb_new_i64(); tcg_gen_neg_i64(t, arg1); tcg_gen_and_i64(t, t, arg1); @@ -2419,7 +2417,7 @@ void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) void tcg_gen_clrsb_i64(TCGv_i64 ret, TCGv_i64 arg) { - if (tcg_op_supported(INDEX_op_clz_i64, TCG_TYPE_I64, 0)) { + if (tcg_op_supported(INDEX_op_clz, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_sari_i64(t, arg, 63); tcg_gen_xor_i64(t, t, arg); diff --git a/tcg/tcg.c b/tcg/tcg.c index e04d3adcec..71b0721fb5 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1026,8 +1026,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), - OUTOP(INDEX_op_clz_i32, TCGOutOpBinary, outop_clz), - OUTOP(INDEX_op_clz_i64, TCGOutOpBinary, outop_clz), + OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), @@ -5402,8 +5401,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: - case INDEX_op_clz_i32: - case INDEX_op_clz_i64: + case INDEX_op_clz: case INDEX_op_divs: case INDEX_op_divu: case INDEX_op_eqv: diff --git a/tcg/tci.c b/tcg/tci.c index 11b11ce642..7c2f2a524b 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -733,7 +733,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = (uint64_t)regs[r1] % (uint64_t)regs[r2]; break; - case INDEX_op_clz_i64: + case INDEX_op_clz: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ? clz64(regs[r1]) : regs[r2]; break; @@ -1052,6 +1052,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: + case INDEX_op_clz: case INDEX_op_divs: case INDEX_op_divu: case INDEX_op_eqv: @@ -1069,7 +1070,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_shr: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_clz_i64: case INDEX_op_ctz_i32: case INDEX_op_ctz_i64: case INDEX_op_tci_clz32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index ee7e6f15eb..0fd1f5510a 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -633,7 +633,7 @@ static void tgen_clz(TCGContext *s, TCGType type, { TCGOpcode opc = (type == TCG_TYPE_I32 ? INDEX_op_tci_clz32 - : INDEX_op_clz_i64); + : INDEX_op_clz); tcg_out_op_rrr(s, opc, a0, a1, a2); } From e3fcde59c92fb421789890c145d5fffdd3d1672d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 17:02:13 -0800 Subject: [PATCH 0423/2760] tcg: Convert ctz to TCGOutOpBinary Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 34 ++++++++++-------- tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 39 ++++++++++++++------- tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 60 +++++++++++++++++++------------- tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 60 ++++++++++++++------------------ tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 4 +++ tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 39 ++++++++++++++------- tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 32 +++++++++++------ tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 4 +++ tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 4 +++ tcg/tcg-has.h | 1 - tcg/tcg-op.c | 9 ++--- tcg/tcg.c | 8 ++--- tcg/tci.c | 8 ++--- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target-opc.h.inc | 1 + tcg/tci/tcg-target.c.inc | 20 +++++++---- 25 files changed, 193 insertions(+), 149 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 8c839d8949..478d59676e 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -15,7 +15,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 @@ -29,7 +28,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 3bd8231117..8441c5f4bf 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2129,6 +2129,26 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3507, RBIT, type, TCG_REG_TMP0, a1); + tgen_clz(s, type, a0, TCG_REG_TMP0, a2); +} + +static void tgen_ctzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_insn(s, 3507, RBIT, type, TCG_REG_TMP0, a1); + tgen_clzi(s, type, a0, TCG_REG_TMP0, a2); +} + +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_O1_I2(r, r, rAL), + .out_rrr = tgen_ctz, + .out_rri = tgen_ctzi, +}; + static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2468,16 +2488,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_ctz_i64: - case INDEX_op_ctz_i32: - tcg_out_insn(s, 3507, RBIT, ext, TCG_REG_TMP0, a1); - if (c2) { - tgen_clzi(s, ext, a0, TCG_REG_TMP0, a2); - } else { - tgen_clz(s, ext, a0, TCG_REG_TMP0, a2); - } - break; - case INDEX_op_brcond_i32: a1 = (int32_t)a1; /* FALLTHRU */ @@ -3098,10 +3108,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rC); - case INDEX_op_ctz_i32: - case INDEX_op_ctz_i64: - return C_O1_I2(r, r, rAL); - case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: return C_O0_I2(r, rC); diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index fceec2f0ca..1485a52c21 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -26,7 +26,6 @@ extern bool use_neon_instructions; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_ctz_i32 use_armv7_instructions #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 681eb5aba1..c05f21c82c 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1888,6 +1888,32 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, INSN_RBIT, TCG_REG_TMP, 0, a1, 0); + tgen_clz(s, TCG_TYPE_I32, a0, TCG_REG_TMP, a2); +} + +static void tgen_ctzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_reg(s, COND_AL, INSN_RBIT, TCG_REG_TMP, 0, a1, 0); + tgen_clzi(s, TCG_TYPE_I32, a0, TCG_REG_TMP, a2); +} + +static TCGConstraintSetIndex cset_ctz(TCGType type, unsigned flags) +{ + return use_armv7_instructions ? C_O1_I2(r, r, rIK) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctz, + .out_rrr = tgen_ctz, + .out_rri = tgen_ctzi, +}; + static TCGConstraintSetIndex cset_idiv(TCGType type, unsigned flags) { return use_idiv_instructions ? C_O1_I2(r, r, r) : C_NotImplemented; @@ -2220,15 +2246,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_smull32(s, COND_AL, args[0], args[1], args[2], args[3]); break; - case INDEX_op_ctz_i32: - tcg_out_dat_reg(s, COND_AL, INSN_RBIT, TCG_REG_TMP, 0, args[1], 0); - if (const_args[2]) { - tgen_clzi(s, TCG_TYPE_I32, args[0], TCG_REG_TMP, args[2]); - } else { - tgen_clz(s, TCG_TYPE_I32, args[0], TCG_REG_TMP, args[2]); - } - break; - case INDEX_op_brcond_i32: c = tcg_out_cmp(s, args[2], args[0], args[1], const_args[1]); tcg_out_goto_label(s, tcg_cond_to_arm_cond[c], arg_label(args[3])); @@ -2351,10 +2368,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i32: return C_O1_I2(r, r, rIN); - case INDEX_op_clz_i32: - case INDEX_op_ctz_i32: - return C_O1_I2(r, r, rIK); - case INDEX_op_mulu2_i32: case INDEX_op_muls2_i32: return C_O2_I2(r, r, r, r); diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index 2277872ff3..b8a0a5c619 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -28,7 +28,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 have_popcnt #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 @@ -43,7 +42,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 have_popcnt #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 0edd4cbc07..f7d0b93af0 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1851,24 +1851,6 @@ static void tcg_out_movcond(TCGContext *s, int rexw, TCGCond cond, tcg_out_cmov(s, jcc, rexw, dest, v1); } -static void tcg_out_ctz(TCGContext *s, int rexw, TCGReg dest, TCGReg arg1, - TCGArg arg2, bool const_a2) -{ - if (have_bmi1) { - tcg_out_modrm(s, OPC_TZCNT + rexw, dest, arg1); - if (const_a2) { - tcg_debug_assert(arg2 == (rexw ? 64 : 32)); - } else { - tcg_debug_assert(dest != arg2); - tcg_out_cmov(s, JCC_JB, rexw, dest, arg2); - } - } else { - tcg_debug_assert(dest != arg2); - tcg_out_modrm(s, OPC_BSF + rexw, dest, arg1); - tcg_out_cmov(s, JCC_JE, rexw, dest, arg2); - } -} - static void tcg_out_branch(TCGContext *s, int call, const tcg_insn_unit *dest) { intptr_t disp = tcg_pcrel_diff(s, dest) - 5; @@ -2646,6 +2628,41 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + int jcc; + + if (have_bmi1) { + tcg_out_modrm(s, OPC_TZCNT + rexw, a0, a1); + jcc = JCC_JB; + } else { + tcg_out_modrm(s, OPC_BSF + rexw, a0, a1); + jcc = JCC_JE; + } + tcg_out_cmov(s, jcc, rexw, a0, a2); +} + +static void tgen_ctzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_TZCNT + rexw, a0, a1); +} + +static TCGConstraintSetIndex cset_ctz(TCGType type, unsigned flags) +{ + return have_bmi1 ? C_N1_I2(r, r, rW) : C_N1_I2(r, r, r); +} + +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctz, + .out_rrr = tgen_ctz, + .out_rri = tgen_ctzi, +}; + static const TCGOutOpBinary outop_divs = { .base.static_constraint = C_NotImplemented, }; @@ -3029,9 +3046,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(ctz): - tcg_out_ctz(s, rexw, args[0], args[1], args[2], const_args[2]); - break; OP_32_64(ctpop): tcg_out_modrm(s, OPC_POPCNT + rexw, a0, a1); break; @@ -3913,10 +3927,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub2_i64: return C_N1_O1_I4(r, r, 0, 1, re, re); - case INDEX_op_ctz_i32: - case INDEX_op_ctz_i64: - return have_bmi1 ? C_N1_I2(r, r, rW) : C_N1_I2(r, r, r); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, L); diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 2eba2132b8..f87d05efc6 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -18,7 +18,6 @@ #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -29,7 +28,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 332ce6c86b..14f3ed1f5c 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -546,28 +546,6 @@ static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg ret, TCGReg arg) tcg_out_ext32s(s, ret, arg); } -static void tcg_out_clzctz(TCGContext *s, LoongArchInsn opc, - TCGReg a0, TCGReg a1, TCGReg a2, - bool c2, bool is_32bit) -{ - if (c2) { - /* - * Fast path: semantics already satisfied due to constraint and - * insn behavior, single instruction is enough. - */ - tcg_debug_assert(a2 == (is_32bit ? 32 : 64)); - /* all clz/ctz insns belong to DJ-format */ - tcg_out32(s, encode_dj_insn(opc, a0, a1)); - return; - } - - tcg_out32(s, encode_dj_insn(opc, TCG_REG_TMP0, a1)); - /* a0 = a1 ? REG_TMP0 : a2 */ - tcg_out_opc_maskeqz(s, TCG_REG_TMP0, TCG_REG_TMP0, a1); - tcg_out_opc_masknez(s, a0, a2, a1); - tcg_out_opc_or(s, a0, TCG_REG_TMP0, a0); -} - #define SETCOND_INV TCG_TARGET_NB_REGS #define SETCOND_NEZ (SETCOND_INV << 1) #define SETCOND_FLAGS (SETCOND_INV | SETCOND_NEZ) @@ -1355,6 +1333,33 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static void tgen_ctzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* a2 is constrained to exactly the type width. */ + if (type == TCG_TYPE_I32) { + tcg_out_opc_ctz_w(s, a0, a1); + } else { + tcg_out_opc_ctz_d(s, a0, a1); + } +} + +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_ctzi(s, type, TCG_REG_TMP0, a1, /* ignored */ 0); + /* a0 = a1 ? REG_TMP0 : a2 */ + tcg_out_opc_maskeqz(s, TCG_REG_TMP0, TCG_REG_TMP0, a1); + tcg_out_opc_masknez(s, a0, a2, a1); + tcg_out_opc_or(s, a0, a0, TCG_REG_TMP0); +} + +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_O1_I2(r, r, rW), + .out_rrr = tgen_ctz, + .out_rri = tgen_ctzi, +}; + static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1781,13 +1786,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_revb_d(s, a0, a1); break; - case INDEX_op_ctz_i32: - tcg_out_clzctz(s, OPC_CTZ_W, a0, a1, a2, c2, true); - break; - case INDEX_op_ctz_i64: - tcg_out_clzctz(s, OPC_CTZ_D, a0, a1, a2, c2, false); - break; - case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: tcg_out_setcond(s, args[3], a0, a1, a2, c2); @@ -2418,10 +2416,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_ctz_i32: - case INDEX_op_ctz_i64: - return C_O1_I2(r, r, rW); - case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: /* Must deposit into the same register as input */ diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index c27ca7e543..ca33c9b745 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -60,7 +60,6 @@ extern bool use_mips32r2_instructions; /* optional instructions detected at runtime */ #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -69,7 +68,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 #endif diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 5052d6481c..e8720b63ed 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1734,6 +1734,10 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index cd7346011b..2b381b99a2 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -19,7 +19,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_ctz_i32 have_isa_3_00 #define TCG_TARGET_HAS_ctpop_i32 have_isa_2_06 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 @@ -34,7 +33,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_ctz_i64 have_isa_3_00 #define TCG_TARGET_HAS_ctpop_i64 have_isa_2_06 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 518cf1e9ef..2cdabcf610 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2974,6 +2974,32 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? CNTTZW : CNTTZD; + tcg_out_cntxz(s, type, insn, a0, a1, a2, false); +} + +static void tgen_ctzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + uint32_t insn = type == TCG_TYPE_I32 ? CNTTZW : CNTTZD; + tcg_out_cntxz(s, type, insn, a0, a1, a2, true); +} + +static TCGConstraintSetIndex cset_ctz(TCGType type, unsigned flags) +{ + return have_isa_3_00 ? C_O1_I2(r, r, rZW) : C_NotImplemented; +} + +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctz, + .out_rrr = tgen_ctz, + .out_rri = tgen_ctzi, +}; + static void tgen_eqv(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3370,18 +3396,9 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); break; - case INDEX_op_ctz_i32: - tcg_out_cntxz(s, TCG_TYPE_I32, CNTTZW, args[0], args[1], - args[2], const_args[2]); - break; case INDEX_op_ctpop_i32: tcg_out32(s, CNTPOPW | SAB(args[1], args[0], 0)); break; - - case INDEX_op_ctz_i64: - tcg_out_cntxz(s, TCG_TYPE_I64, CNTTZD, args[0], args[1], - args[2], const_args[2]); - break; case INDEX_op_ctpop_i64: tcg_out32(s, CNTPOPD | SAB(args[1], args[0], 0)); break; @@ -4240,10 +4257,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_ctz_i32: - case INDEX_op_ctz_i64: - return C_O1_I2(r, r, rZW); - case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: return C_O0_I2(r, rC); diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 41e287130d..385a6736c0 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -18,7 +18,6 @@ #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_ctz_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctpop_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -28,7 +27,6 @@ #define TCG_TARGET_HAS_bswap16_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_ctz_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_ctpop_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 77eef02db5..1ceb1aeb1c 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2023,6 +2023,27 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_CTZW : OPC_CTZ; + tcg_out_cltz(s, type, insn, a0, a1, a2, false); +} + +static void tgen_ctzi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_CTZW : OPC_CTZ; + tcg_out_cltz(s, type, insn, a0, a1, a2, true); +} + +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_clzctz, + .out_rrr = tgen_ctz, + .out_rri = tgen_ctzi, +}; + static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2424,13 +2445,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_imm(s, OPC_CPOP, a0, a1, 0); break; - case INDEX_op_ctz_i32: - tcg_out_cltz(s, TCG_TYPE_I32, OPC_CTZW, a0, a1, a2, c2); - break; - case INDEX_op_ctz_i64: - tcg_out_cltz(s, TCG_TYPE_I64, OPC_CTZ, a0, a1, a2, c2); - break; - case INDEX_op_add2_i32: tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], const_args[4], const_args[5], false, true); @@ -2813,10 +2827,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i64: return C_O1_I2(r, r, rI); - case INDEX_op_ctz_i32: - case INDEX_op_ctz_i64: - return C_N1_I2(r, r, rM); - case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: return C_O0_I2(rz, rz); diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 85a4f23e95..0794394fea 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -31,7 +31,6 @@ extern uint64_t s390_facilities[3]; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 @@ -45,7 +44,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index adfe403bef..374136ed14 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2268,6 +2268,10 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_divs = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 21fa0f3663..56262640ff 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -16,7 +16,6 @@ extern bool use_vis3_instructions; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 -#define TCG_TARGET_HAS_ctz_i32 0 #define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 @@ -30,7 +29,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index a4fb41764b..a9257b8b93 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1322,6 +1322,10 @@ static const TCGOutOpBinary outop_clz = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_divs_rJ(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGArg a2, bool c2) { diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 27d6ec7636..6bba845944 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -15,7 +15,6 @@ #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_ctz_i64 0 #define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 76e9efc655..b117a59f05 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -750,11 +750,11 @@ void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { TCGv_i32 z, t; - if (TCG_TARGET_HAS_ctz_i32) { + if (tcg_op_supported(INDEX_op_ctz_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3_i32(INDEX_op_ctz_i32, ret, arg1, arg2); return; } - if (TCG_TARGET_HAS_ctz_i64) { + if (tcg_op_supported(INDEX_op_ctz_i64, TCG_TYPE_I64, 0)) { TCGv_i64 t1 = tcg_temp_ebb_new_i64(); TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t1, arg1); @@ -788,7 +788,8 @@ void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) { - if (!TCG_TARGET_HAS_ctz_i32 && TCG_TARGET_HAS_ctpop_i32 && arg2 == 32) { + if (!tcg_op_supported(INDEX_op_ctz_i32, TCG_TYPE_I32, 0) + && TCG_TARGET_HAS_ctpop_i32 && arg2 == 32) { /* This equivalence has the advantage of not requiring a fixup. */ TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_subi_i32(t, arg1, 1); @@ -2365,7 +2366,7 @@ void tcg_gen_ctz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { TCGv_i64 z, t; - if (TCG_TARGET_HAS_ctz_i64) { + if (tcg_op_supported(INDEX_op_ctz_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3_i64(INDEX_op_ctz_i64, ret, arg1, arg2); return; } diff --git a/tcg/tcg.c b/tcg/tcg.c index 71b0721fb5..3f610e3f83 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1027,6 +1027,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), + OUTOP(INDEX_op_ctz_i32, TCGOutOpBinary, outop_ctz), + OUTOP(INDEX_op_ctz_i64, TCGOutOpBinary, outop_ctz), OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), @@ -2289,8 +2291,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap16_i32; case INDEX_op_bswap32_i32: return TCG_TARGET_HAS_bswap32_i32; - case INDEX_op_ctz_i32: - return TCG_TARGET_HAS_ctz_i32; case INDEX_op_ctpop_i32: return TCG_TARGET_HAS_ctpop_i32; @@ -2332,8 +2332,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap32_i64; case INDEX_op_bswap64_i64: return TCG_TARGET_HAS_bswap64_i64; - case INDEX_op_ctz_i64: - return TCG_TARGET_HAS_ctz_i64; case INDEX_op_ctpop_i64: return TCG_TARGET_HAS_ctpop_i64; case INDEX_op_add2_i64: @@ -5402,6 +5400,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_clz: + case INDEX_op_ctz_i32: + case INDEX_op_ctz_i64: case INDEX_op_divs: case INDEX_op_divu: case INDEX_op_eqv: diff --git a/tcg/tci.c b/tcg/tci.c index 7c2f2a524b..b505944b10 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -599,13 +599,11 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tmp32 = regs[r1]; regs[r0] = tmp32 ? clz32(tmp32) : regs[r2]; break; -#if TCG_TARGET_HAS_ctz_i32 - case INDEX_op_ctz_i32: + case INDEX_op_tci_ctz32: tci_args_rrr(insn, &r0, &r1, &r2); tmp32 = regs[r1]; regs[r0] = tmp32 ? ctz32(tmp32) : regs[r2]; break; -#endif #if TCG_TARGET_HAS_ctpop_i32 case INDEX_op_ctpop_i32: tci_args_rr(insn, &r0, &r1); @@ -737,12 +735,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ? clz64(regs[r1]) : regs[r2]; break; -#if TCG_TARGET_HAS_ctz_i64 case INDEX_op_ctz_i64: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ? ctz64(regs[r1]) : regs[r2]; break; -#endif #if TCG_TARGET_HAS_ctpop_i64 case INDEX_op_ctpop_i64: tci_args_rr(insn, &r0, &r1); @@ -1070,8 +1066,8 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_shr: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_ctz_i32: case INDEX_op_ctz_i64: + case INDEX_op_tci_ctz32: case INDEX_op_tci_clz32: case INDEX_op_tci_divs32: case INDEX_op_tci_divu32: diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index ae1f724702..daa6db4799 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -10,7 +10,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_ctz_i32 1 #define TCG_TARGET_HAS_ctpop_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_muls2_i32 1 @@ -22,7 +21,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_ctz_i64 1 #define TCG_TARGET_HAS_ctpop_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_muls2_i64 1 diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc index 04774ca9c4..2bb346f4c8 100644 --- a/tcg/tci/tcg-target-opc.h.inc +++ b/tcg/tci/tcg-target-opc.h.inc @@ -3,6 +3,7 @@ DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(tci_clz32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_ctz32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_divs32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_divu32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_rems32, 1, 2, 0, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 0fd1f5510a..47bdec5f44 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -83,8 +83,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i64: case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: - case INDEX_op_ctz_i32: - case INDEX_op_ctz_i64: return C_O1_I2(r, r, r); case INDEX_op_brcond_i32: @@ -642,6 +640,20 @@ static const TCGOutOpBinary outop_clz = { .out_rrr = tgen_clz, }; +static void tgen_ctz(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_ctz32 + : INDEX_op_ctz_i64); + tcg_out_op_rrr(s, opc, a0, a1, a2); +} + +static const TCGOutOpBinary outop_ctz = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_ctz, +}; + static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -933,10 +945,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, opc, args[0], args[1], args[2]); break; - CASE_32_64(ctz) /* Optional (TCG_TARGET_HAS_ctz_*). */ - tcg_out_op_rrr(s, opc, args[0], args[1], args[2]); - break; - CASE_32_64(deposit) tcg_out_op_rrrbb(s, opc, args[0], args[1], args[2], args[3], args[4]); break; From c96447d838d67db509cde1a190132e14b8672055 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 17:07:01 -0800 Subject: [PATCH 0424/2760] tcg: Merge INDEX_op_ctz_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 10 +++++----- tcg/tcg-op.c | 16 ++++++++-------- tcg/tcg.c | 6 ++---- tcg/tci.c | 4 ++-- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 20 insertions(+), 23 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 22f0432988..92344b8786 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -362,7 +362,7 @@ Logical - | *t0* = *t1* ? clz(*t1*) : *t2* - * - ctz_i32/i64 *t0*, *t1*, *t2* + * - ctz *t0*, *t1*, *t2* - | *t0* = *t1* ? ctz(*t1*) : *t2* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index ad1d193ef4..4dfd8708a5 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -43,6 +43,7 @@ DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(clz, 1, 2, 0, TCG_OPF_INT) +DEF(ctz, 1, 2, 0, TCG_OPF_INT) DEF(divs, 1, 2, 0, TCG_OPF_INT) DEF(divs2, 2, 3, 0, TCG_OPF_INT) DEF(divu, 1, 2, 0, TCG_OPF_INT) @@ -96,7 +97,6 @@ DEF(setcond2_i32, 1, 4, 1, 0) DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) -DEF(ctz_i32, 1, 2, 0, 0) DEF(ctpop_i32, 1, 1, 0, 0) DEF(setcond_i64, 1, 2, 1, 0) @@ -130,7 +130,6 @@ DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(bswap16_i64, 1, 1, 1, 0) DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) -DEF(ctz_i64, 1, 2, 0, 0) DEF(ctpop_i64, 1, 1, 0, 0) DEF(add2_i64, 2, 4, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index d8d0e728aa..af4e76e81b 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -509,10 +509,10 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, } return x ? clz64(x) : y; - case INDEX_op_ctz_i32: - return (uint32_t)x ? ctz32(x) : y; - - case INDEX_op_ctz_i64: + case INDEX_op_ctz: + if (type == TCG_TYPE_I32) { + return (uint32_t)x ? ctz32(x) : y; + } return x ? ctz64(x) : y; case INDEX_op_ctpop_i32: @@ -2899,7 +2899,7 @@ void tcg_optimize(TCGContext *s) done = fold_bswap(&ctx, op); break; case INDEX_op_clz: - CASE_OP_32_64(ctz): + case INDEX_op_ctz: done = fold_count_zeros(&ctx, op); break; CASE_OP_32_64(ctpop): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index b117a59f05..7bf7de1213 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -750,11 +750,11 @@ void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) { TCGv_i32 z, t; - if (tcg_op_supported(INDEX_op_ctz_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3_i32(INDEX_op_ctz_i32, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I32, 0)) { + tcg_gen_op3_i32(INDEX_op_ctz, ret, arg1, arg2); return; } - if (tcg_op_supported(INDEX_op_ctz_i64, TCG_TYPE_I64, 0)) { + if (tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I64, 0)) { TCGv_i64 t1 = tcg_temp_ebb_new_i64(); TCGv_i64 t2 = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t1, arg1); @@ -788,7 +788,7 @@ void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) { - if (!tcg_op_supported(INDEX_op_ctz_i32, TCG_TYPE_I32, 0) + if (!tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I32, 0) && TCG_TARGET_HAS_ctpop_i32 && arg2 == 32) { /* This equivalence has the advantage of not requiring a fixup. */ TCGv_i32 t = tcg_temp_ebb_new_i32(); @@ -2366,8 +2366,8 @@ void tcg_gen_ctz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) { TCGv_i64 z, t; - if (tcg_op_supported(INDEX_op_ctz_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3_i64(INDEX_op_ctz_i64, ret, arg1, arg2); + if (tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I64, 0)) { + tcg_gen_op3_i64(INDEX_op_ctz, ret, arg1, arg2); return; } if (TCG_TARGET_HAS_ctpop_i64) { @@ -2395,7 +2395,7 @@ void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) { if (TCG_TARGET_REG_BITS == 32 && arg2 <= 0xffffffffu - && tcg_op_supported(INDEX_op_ctz_i32, TCG_TYPE_I32, 0)) { + && tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I32, 0)) { TCGv_i32 t32 = tcg_temp_ebb_new_i32(); tcg_gen_ctzi_i32(t32, TCGV_HIGH(arg1), arg2 - 32); tcg_gen_addi_i32(t32, t32, 32); @@ -2403,7 +2403,7 @@ void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) tcg_gen_movi_i32(TCGV_HIGH(ret), 0); tcg_temp_free_i32(t32); } else if (arg2 == 64 - && !tcg_op_supported(INDEX_op_ctz_i64, TCG_TYPE_I64, 0) + && !tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I64, 0) && TCG_TARGET_HAS_ctpop_i64) { /* This equivalence has the advantage of not requiring a fixup. */ TCGv_i64 t = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index 3f610e3f83..18b28a670e 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1027,8 +1027,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), - OUTOP(INDEX_op_ctz_i32, TCGOutOpBinary, outop_ctz), - OUTOP(INDEX_op_ctz_i64, TCGOutOpBinary, outop_ctz), + OUTOP(INDEX_op_ctz, TCGOutOpBinary, outop_ctz), OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), @@ -5400,8 +5399,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_clz: - case INDEX_op_ctz_i32: - case INDEX_op_ctz_i64: + case INDEX_op_ctz: case INDEX_op_divs: case INDEX_op_divu: case INDEX_op_eqv: diff --git a/tcg/tci.c b/tcg/tci.c index b505944b10..550f2014a8 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -735,7 +735,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ? clz64(regs[r1]) : regs[r2]; break; - case INDEX_op_ctz_i64: + case INDEX_op_ctz: tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ? ctz64(regs[r1]) : regs[r2]; break; @@ -1049,6 +1049,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_and: case INDEX_op_andc: case INDEX_op_clz: + case INDEX_op_ctz: case INDEX_op_divs: case INDEX_op_divu: case INDEX_op_eqv: @@ -1066,7 +1067,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_shr: case INDEX_op_sub: case INDEX_op_xor: - case INDEX_op_ctz_i64: case INDEX_op_tci_ctz32: case INDEX_op_tci_clz32: case INDEX_op_tci_divs32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 47bdec5f44..d8d45e2c4b 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -645,7 +645,7 @@ static void tgen_ctz(TCGContext *s, TCGType type, { TCGOpcode opc = (type == TCG_TYPE_I32 ? INDEX_op_tci_ctz32 - : INDEX_op_ctz_i64); + : INDEX_op_ctz); tcg_out_op_rrr(s, opc, a0, a1, a2); } From f8fa1dae3db5493617ff42e8ccfd797058df243e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 17:56:01 -0800 Subject: [PATCH 0425/2760] tcg: Convert ctpop to TCGOutOpUnary Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 - tcg/aarch64/tcg-target.c.inc | 4 ++ tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 4 ++ tcg/i386/tcg-target-has.h | 2 - tcg/i386/tcg-target.c.inc | 23 ++++++++--- tcg/loongarch64/tcg-target-has.h | 2 - tcg/loongarch64/tcg-target.c.inc | 4 ++ tcg/mips/tcg-target-has.h | 2 - tcg/mips/tcg-target.c.inc | 4 ++ tcg/ppc/tcg-target-has.h | 2 - tcg/ppc/tcg-target.c.inc | 26 ++++++++----- tcg/riscv/tcg-target-has.h | 2 - tcg/riscv/tcg-target.c.inc | 26 ++++++++----- tcg/s390x/tcg-target-has.h | 2 - tcg/s390x/tcg-target.c.inc | 66 +++++++++++++++----------------- tcg/sparc64/tcg-target-has.h | 2 - tcg/sparc64/tcg-target.c.inc | 4 ++ tcg/tcg-has.h | 1 - tcg/tcg-op.c | 37 ++++++++++-------- tcg/tcg.c | 8 ++-- tcg/tci.c | 19 ++++----- tcg/tci/tcg-target-has.h | 2 - tcg/tci/tcg-target.c.inc | 19 +++++++-- 24 files changed, 151 insertions(+), 113 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 478d59676e..4f1840f44e 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -15,7 +15,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -28,7 +27,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 8441c5f4bf..0f01fa8c20 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2129,6 +2129,10 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_ctz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 1485a52c21..1cf3911613 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -26,7 +26,6 @@ extern bool use_neon_instructions; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index c05f21c82c..e109c65965 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1888,6 +1888,10 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_ctz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index b8a0a5c619..a71f8c7370 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -28,7 +28,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 have_popcnt #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -42,7 +41,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 have_popcnt #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index f7d0b93af0..318a30ebe0 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2628,6 +2628,23 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_POPCNT + rexw, a0, a1); +} + +static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) +{ + return have_popcnt ? C_O1_I1(r, r) : C_NotImplemented; +} + +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctpop, + .out_rr = tgen_ctpop, +}; + static void tgen_ctz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3046,10 +3063,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(ctpop): - tcg_out_modrm(s, OPC_POPCNT + rexw, a0, a1); - break; - OP_32_64(brcond): tcg_out_brcond(s, rexw, a2, a0, a1, const_args[1], arg_label(args[3]), 0); @@ -3893,8 +3906,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: - case INDEX_op_ctpop_i32: - case INDEX_op_ctpop_i64: return C_O1_I1(r, r); case INDEX_op_extract2_i32: diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index f87d05efc6..33a1cf2326 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -18,7 +18,6 @@ #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 /* 64-bit operations */ @@ -28,7 +27,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 0 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 14f3ed1f5c..4ef7c6b945 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1333,6 +1333,10 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_ctzi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, tcg_target_long a2) { diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index ca33c9b745..470aa16452 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -60,7 +60,6 @@ extern bool use_mips32r2_instructions; /* optional instructions detected at runtime */ #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 @@ -68,7 +67,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 0 #endif #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index e8720b63ed..a94c965046 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1734,6 +1734,10 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_ctz = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 2b381b99a2..f071435d98 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -19,7 +19,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 have_isa_2_06 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 @@ -33,7 +32,6 @@ #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 have_isa_2_06 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 2cdabcf610..ab56c623c7 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2974,6 +2974,23 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + uint32_t insn = type == TCG_TYPE_I32 ? CNTPOPW : CNTPOPD; + tcg_out32(s, insn | SAB(a1, a0, 0)); +} + +static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) +{ + return have_isa_2_06 ? C_O1_I1(r, r) : C_NotImplemented; +} + +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctpop, + .out_rr = tgen_ctpop, +}; + static void tgen_ctz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3396,13 +3413,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); break; - case INDEX_op_ctpop_i32: - tcg_out32(s, CNTPOPW | SAB(args[1], args[0], 0)); - break; - case INDEX_op_ctpop_i64: - tcg_out32(s, CNTPOPD | SAB(args[1], args[0], 0)); - break; - case INDEX_op_brcond_i32: tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], arg_label(args[3]), TCG_TYPE_I32); @@ -4226,7 +4236,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_ctpop_i32: case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: case INDEX_op_extract_i32: @@ -4238,7 +4247,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_ctpop_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_bswap16_i64: diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 385a6736c0..a3b634570b 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -18,7 +18,6 @@ #define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_ctpop_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_negsetcond_i64 1 @@ -27,7 +26,6 @@ #define TCG_TARGET_HAS_bswap16_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) -#define TCG_TARGET_HAS_ctpop_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 1ceb1aeb1c..a5cd18c99e 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2023,6 +2023,23 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + RISCVInsn insn = type == TCG_TYPE_I32 ? OPC_CPOPW : OPC_CPOP; + tcg_out_opc_imm(s, insn, a0, a1, 0); +} + +static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_ZBB ? C_O1_I1(r, r) : C_NotImplemented; +} + +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctpop, + .out_rr = tgen_ctpop, +}; + static void tgen_ctz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2438,13 +2455,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_ctpop_i32: - tcg_out_opc_imm(s, OPC_CPOPW, a0, a1, 0); - break; - case INDEX_op_ctpop_i64: - tcg_out_opc_imm(s, OPC_CPOP, a0, a1, 0); - break; - case INDEX_op_add2_i32: tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], const_args[4], const_args[5], false, true); @@ -2808,8 +2818,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: - case INDEX_op_ctpop_i32: - case INDEX_op_ctpop_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 0794394fea..87f117ce58 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -31,7 +31,6 @@ extern uint64_t s390_facilities[3]; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_ctpop_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -44,7 +43,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 -#define TCG_TARGET_HAS_ctpop_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 374136ed14..71adb0964d 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1514,32 +1514,6 @@ static void tgen_movcond(TCGContext *s, TCGType type, TCGCond c, TCGReg dest, tgen_movcond_int(s, type, dest, v3, v3const, v4, cc, inv_cc); } -static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) -{ - /* With MIE3, and bit 0 of m4 set, we get the complete result. */ - if (HAVE_FACILITY(MISC_INSN_EXT3)) { - if (type == TCG_TYPE_I32) { - tcg_out_ext32u(s, dest, src); - src = dest; - } - tcg_out_insn(s, RRFc, POPCNT, dest, src, 8); - return; - } - - /* Without MIE3, each byte gets the count of bits for the byte. */ - tcg_out_insn(s, RRFc, POPCNT, dest, src, 0); - - /* Multiply to sum each byte at the top of the word. */ - if (type == TCG_TYPE_I32) { - tcg_out_insn(s, RIL, MSFI, dest, 0x01010101); - tcg_out_sh32(s, RS_SRL, dest, TCG_REG_NONE, 24); - } else { - tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, 0x0101010101010101ull); - tcg_out_insn(s, RRE, MSGR, dest, TCG_TMP0); - tcg_out_sh64(s, RSY_SRLG, dest, dest, TCG_REG_NONE, 56); - } -} - static void tgen_deposit(TCGContext *s, TCGReg dest, TCGReg src, int ofs, int len, int z) { @@ -2268,6 +2242,37 @@ static const TCGOutOpBinary outop_clz = { .out_rri = tgen_clzi, }; +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg dest, TCGReg src) +{ + /* With MIE3, and bit 0 of m4 set, we get the complete result. */ + if (HAVE_FACILITY(MISC_INSN_EXT3)) { + if (type == TCG_TYPE_I32) { + tcg_out_ext32u(s, dest, src); + src = dest; + } + tcg_out_insn(s, RRFc, POPCNT, dest, src, 8); + return; + } + + /* Without MIE3, each byte gets the count of bits for the byte. */ + tcg_out_insn(s, RRFc, POPCNT, dest, src, 0); + + /* Multiply to sum each byte at the top of the word. */ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, MSFI, dest, 0x01010101); + tcg_out_sh32(s, RS_SRL, dest, TCG_REG_NONE, 24); + } else { + tcg_out_movi(s, TCG_TYPE_I64, TCG_TMP0, 0x0101010101010101ull); + tcg_out_insn(s, RRE, MSGR, dest, TCG_TMP0); + tcg_out_sh64(s, RSY_SRLG, dest, dest, TCG_REG_NONE, 56); + } +} + +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_ctpop, +}; + static const TCGOutOpBinary outop_ctz = { .base.static_constraint = C_NotImplemented, }; @@ -2914,13 +2919,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tgen_sextract(s, args[0], args[1], args[2], args[3]); break; - case INDEX_op_ctpop_i32: - tgen_ctpop(s, TCG_TYPE_I32, args[0], args[1]); - break; - case INDEX_op_ctpop_i64: - tgen_ctpop(s, TCG_TYPE_I64, args[0], args[1]); - break; - case INDEX_op_mb: /* The host memory model is quite strong, we simply need to serialize the instruction stream. */ @@ -3429,8 +3427,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: - case INDEX_op_ctpop_i32: - case INDEX_op_ctpop_i64: return C_O1_I1(r, r); case INDEX_op_qemu_ld_i32: diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 56262640ff..40e54e1543 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -16,7 +16,6 @@ extern bool use_vis3_instructions; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 -#define TCG_TARGET_HAS_ctpop_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -29,7 +28,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index a9257b8b93..43ca23f593 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1322,6 +1322,10 @@ static const TCGOutOpBinary outop_clz = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_NotImplemented, +}; + static const TCGOutOpBinary outop_ctz = { .base.static_constraint = C_NotImplemented, }; diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 6bba845944..97f4e83303 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -15,7 +15,6 @@ #define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_add2_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 7bf7de1213..db0e79059b 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -765,7 +765,8 @@ void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) tcg_temp_free_i64(t2); return; } - if (TCG_TARGET_HAS_ctpop_i32 || TCG_TARGET_HAS_ctpop_i64) { + if (tcg_op_supported(INDEX_op_ctpop_i32, TCG_TYPE_I32, 0) || + tcg_op_supported(INDEX_op_ctpop_i64, TCG_TYPE_I64, 0)) { t = tcg_temp_ebb_new_i32(); tcg_gen_subi_i32(t, arg1, 1); tcg_gen_andc_i32(t, t, arg1); @@ -788,8 +789,9 @@ void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) { - if (!tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I32, 0) - && TCG_TARGET_HAS_ctpop_i32 && arg2 == 32) { + if (arg2 == 32 + && !tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I32, 0) + && tcg_op_supported(INDEX_op_ctpop_i32, TCG_TYPE_I32, 0)) { /* This equivalence has the advantage of not requiring a fixup. */ TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_subi_i32(t, arg1, 1); @@ -817,9 +819,9 @@ void tcg_gen_clrsb_i32(TCGv_i32 ret, TCGv_i32 arg) void tcg_gen_ctpop_i32(TCGv_i32 ret, TCGv_i32 arg1) { - if (TCG_TARGET_HAS_ctpop_i32) { + if (tcg_op_supported(INDEX_op_ctpop_i32, TCG_TYPE_I32, 0)) { tcg_gen_op2_i32(INDEX_op_ctpop_i32, ret, arg1); - } else if (TCG_TARGET_HAS_ctpop_i64) { + } else if (tcg_op_supported(INDEX_op_ctpop_i64, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t, arg1); tcg_gen_ctpop_i64(t, t); @@ -2370,7 +2372,7 @@ void tcg_gen_ctz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_op3_i64(INDEX_op_ctz, ret, arg1, arg2); return; } - if (TCG_TARGET_HAS_ctpop_i64) { + if (tcg_op_supported(INDEX_op_ctpop_i64, TCG_TYPE_I64, 0)) { t = tcg_temp_ebb_new_i64(); tcg_gen_subi_i64(t, arg1, 1); tcg_gen_andc_i64(t, t, arg1); @@ -2404,7 +2406,7 @@ void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) tcg_temp_free_i32(t32); } else if (arg2 == 64 && !tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I64, 0) - && TCG_TARGET_HAS_ctpop_i64) { + && tcg_op_supported(INDEX_op_ctpop_i64, TCG_TYPE_I64, 0)) { /* This equivalence has the advantage of not requiring a fixup. */ TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_subi_i64(t, arg1, 1); @@ -2432,16 +2434,21 @@ void tcg_gen_clrsb_i64(TCGv_i64 ret, TCGv_i64 arg) void tcg_gen_ctpop_i64(TCGv_i64 ret, TCGv_i64 arg1) { - if (TCG_TARGET_HAS_ctpop_i64) { - tcg_gen_op2_i64(INDEX_op_ctpop_i64, ret, arg1); - } else if (TCG_TARGET_REG_BITS == 32 && TCG_TARGET_HAS_ctpop_i32) { - tcg_gen_ctpop_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1)); - tcg_gen_ctpop_i32(TCGV_LOW(ret), TCGV_LOW(arg1)); - tcg_gen_add_i32(TCGV_LOW(ret), TCGV_LOW(ret), TCGV_HIGH(ret)); - tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + if (TCG_TARGET_REG_BITS == 64) { + if (tcg_op_supported(INDEX_op_ctpop_i64, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_ctpop_i64, ret, arg1); + return; + } } else { - gen_helper_ctpop_i64(ret, arg1); + if (tcg_op_supported(INDEX_op_ctpop_i32, TCG_TYPE_I32, 0)) { + tcg_gen_ctpop_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1)); + tcg_gen_ctpop_i32(TCGV_LOW(ret), TCGV_LOW(arg1)); + tcg_gen_add_i32(TCGV_LOW(ret), TCGV_LOW(ret), TCGV_HIGH(ret)); + tcg_gen_movi_i32(TCGV_HIGH(ret), 0); + return; + } } + gen_helper_ctpop_i64(ret, arg1); } void tcg_gen_rotl_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) diff --git a/tcg/tcg.c b/tcg/tcg.c index 18b28a670e..94997d8610 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1027,6 +1027,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), + OUTOP(INDEX_op_ctpop_i32, TCGOutOpUnary, outop_ctpop), + OUTOP(INDEX_op_ctpop_i64, TCGOutOpUnary, outop_ctpop), OUTOP(INDEX_op_ctz, TCGOutOpBinary, outop_ctz), OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), @@ -2290,8 +2292,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap16_i32; case INDEX_op_bswap32_i32: return TCG_TARGET_HAS_bswap32_i32; - case INDEX_op_ctpop_i32: - return TCG_TARGET_HAS_ctpop_i32; case INDEX_op_brcond2_i32: case INDEX_op_setcond2_i32: @@ -2331,8 +2331,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_bswap32_i64; case INDEX_op_bswap64_i64: return TCG_TARGET_HAS_bswap64_i64; - case INDEX_op_ctpop_i64: - return TCG_TARGET_HAS_ctpop_i64; case INDEX_op_add2_i64: return TCG_TARGET_HAS_add2_i64; case INDEX_op_sub2_i64: @@ -5449,6 +5447,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_ctpop_i32: + case INDEX_op_ctpop_i64: case INDEX_op_neg: case INDEX_op_not: { diff --git a/tcg/tci.c b/tcg/tci.c index 550f2014a8..8bcf48b251 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -26,6 +26,8 @@ #include +#define ctpop_tr glue(ctpop, TCG_TARGET_REG_BITS) + /* * Enable TCI assertions only when debugging TCG (and without NDEBUG defined). * Without assertions, the interpreter runs much faster. @@ -575,6 +577,11 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rr(insn, &r0, &r1); regs[r0] = ~regs[r1]; break; + case INDEX_op_ctpop_i32: + case INDEX_op_ctpop_i64: + tci_args_rr(insn, &r0, &r1); + regs[r0] = ctpop_tr(regs[r1]); + break; /* Arithmetic operations (32 bit). */ @@ -604,12 +611,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tmp32 = regs[r1]; regs[r0] = tmp32 ? ctz32(tmp32) : regs[r2]; break; -#if TCG_TARGET_HAS_ctpop_i32 - case INDEX_op_ctpop_i32: - tci_args_rr(insn, &r0, &r1); - regs[r0] = ctpop32(regs[r1]); - break; -#endif /* Shift/rotate operations. */ @@ -739,12 +740,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ? ctz64(regs[r1]) : regs[r2]; break; -#if TCG_TARGET_HAS_ctpop_i64 - case INDEX_op_ctpop_i64: - tci_args_rr(insn, &r0, &r1); - regs[r0] = ctpop64(regs[r1]); - break; -#endif #if TCG_TARGET_HAS_mulu2_i64 case INDEX_op_mulu2_i64: tci_args_rrrr(insn, &r0, &r1, &r2, &r3); diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index daa6db4799..774fb149fc 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -10,7 +10,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_ctpop_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 #define TCG_TARGET_HAS_muls2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -21,7 +20,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_ctpop_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_muls2_i64 1 #define TCG_TARGET_HAS_add2_i32 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index d8d45e2c4b..a931369a80 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -66,8 +66,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: - case INDEX_op_ctpop_i32: - case INDEX_op_ctpop_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: @@ -883,6 +881,22 @@ static const TCGOutOpBinary outop_xor = { .out_rrr = tgen_xor, }; +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_op_rr(s, glue(INDEX_op_ctpop_i,TCG_TARGET_REG_BITS), a0, a1); +} + +static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_REG ? C_O1_I1(r, r) : C_NotImplemented; +} + +static const TCGOutOpUnary outop_ctpop = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctpop, + .out_rr = tgen_ctpop, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tcg_out_op_rr(s, INDEX_op_neg, a0, a1); @@ -961,7 +975,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_rl(s, opc, TCG_REG_TMP, arg_label(args[3])); break; - CASE_32_64(ctpop) /* Optional (TCG_TARGET_HAS_ctpop_*). */ case INDEX_op_bswap32_i32: /* Optional (TCG_TARGET_HAS_bswap32_i32). */ case INDEX_op_bswap64_i64: /* Optional (TCG_TARGET_HAS_bswap64_i64). */ tcg_out_op_rr(s, opc, args[0], args[1]); From 97218ae918b1504a63623130f3dc8f4b423b5f1b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 18:37:43 -0800 Subject: [PATCH 0426/2760] tcg: Merge INDEX_op_ctpop_{i32,i64} Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 6 +++--- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 9 +++------ tcg/tcg-op.c | 21 ++++++++++----------- tcg/tcg.c | 6 ++---- tcg/tci.c | 6 ++---- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 22 insertions(+), 31 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 92344b8786..fb7764e3c0 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -366,12 +366,12 @@ Logical - | *t0* = *t1* ? ctz(*t1*) : *t2* - * - ctpop_i32/i64 *t0*, *t1* + * - ctpop *t0*, *t1* - | *t0* = number of bits set in *t1* | - | With *ctpop* short for "count population", matching - | the function name used in ``include/qemu/host-utils.h``. + | The name *ctpop* is short for "count population", and matches + the function name used in ``include/qemu/host-utils.h``. Shifts/Rotates diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 4dfd8708a5..f4ccde074b 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -43,6 +43,7 @@ DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(clz, 1, 2, 0, TCG_OPF_INT) +DEF(ctpop, 1, 1, 0, TCG_OPF_INT) DEF(ctz, 1, 2, 0, TCG_OPF_INT) DEF(divs, 1, 2, 0, TCG_OPF_INT) DEF(divs2, 2, 3, 0, TCG_OPF_INT) @@ -97,7 +98,6 @@ DEF(setcond2_i32, 1, 4, 1, 0) DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) -DEF(ctpop_i32, 1, 1, 0, 0) DEF(setcond_i64, 1, 2, 1, 0) DEF(negsetcond_i64, 1, 2, 1, 0) @@ -130,7 +130,6 @@ DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(bswap16_i64, 1, 1, 1, 0) DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) -DEF(ctpop_i64, 1, 1, 0, 0) DEF(add2_i64, 2, 4, 0, 0) DEF(sub2_i64, 2, 4, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index af4e76e81b..bf625f770c 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -515,11 +515,8 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, } return x ? ctz64(x) : y; - case INDEX_op_ctpop_i32: - return ctpop32(x); - - case INDEX_op_ctpop_i64: - return ctpop64(x); + case INDEX_op_ctpop: + return type == TCG_TYPE_I32 ? ctpop32(x) : ctpop64(x); CASE_OP_32_64(bswap16): x = bswap16(x); @@ -2902,7 +2899,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_ctz: done = fold_count_zeros(&ctx, op); break; - CASE_OP_32_64(ctpop): + case INDEX_op_ctpop: done = fold_ctpop(&ctx, op); break; CASE_OP_32_64(deposit): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index db0e79059b..0eeec47b83 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -765,8 +765,7 @@ void tcg_gen_ctz_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2) tcg_temp_free_i64(t2); return; } - if (tcg_op_supported(INDEX_op_ctpop_i32, TCG_TYPE_I32, 0) || - tcg_op_supported(INDEX_op_ctpop_i64, TCG_TYPE_I64, 0)) { + if (tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_REG, 0)) { t = tcg_temp_ebb_new_i32(); tcg_gen_subi_i32(t, arg1, 1); tcg_gen_andc_i32(t, t, arg1); @@ -791,7 +790,7 @@ void tcg_gen_ctzi_i32(TCGv_i32 ret, TCGv_i32 arg1, uint32_t arg2) { if (arg2 == 32 && !tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I32, 0) - && tcg_op_supported(INDEX_op_ctpop_i32, TCG_TYPE_I32, 0)) { + && tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_REG, 0)) { /* This equivalence has the advantage of not requiring a fixup. */ TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_subi_i32(t, arg1, 1); @@ -819,9 +818,9 @@ void tcg_gen_clrsb_i32(TCGv_i32 ret, TCGv_i32 arg) void tcg_gen_ctpop_i32(TCGv_i32 ret, TCGv_i32 arg1) { - if (tcg_op_supported(INDEX_op_ctpop_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op2_i32(INDEX_op_ctpop_i32, ret, arg1); - } else if (tcg_op_supported(INDEX_op_ctpop_i64, TCG_TYPE_I64, 0)) { + if (tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_I32, 0)) { + tcg_gen_op2_i32(INDEX_op_ctpop, ret, arg1); + } else if (tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_extu_i32_i64(t, arg1); tcg_gen_ctpop_i64(t, t); @@ -2372,7 +2371,7 @@ void tcg_gen_ctz_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_op3_i64(INDEX_op_ctz, ret, arg1, arg2); return; } - if (tcg_op_supported(INDEX_op_ctpop_i64, TCG_TYPE_I64, 0)) { + if (tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_I64, 0)) { t = tcg_temp_ebb_new_i64(); tcg_gen_subi_i64(t, arg1, 1); tcg_gen_andc_i64(t, t, arg1); @@ -2406,7 +2405,7 @@ void tcg_gen_ctzi_i64(TCGv_i64 ret, TCGv_i64 arg1, uint64_t arg2) tcg_temp_free_i32(t32); } else if (arg2 == 64 && !tcg_op_supported(INDEX_op_ctz, TCG_TYPE_I64, 0) - && tcg_op_supported(INDEX_op_ctpop_i64, TCG_TYPE_I64, 0)) { + && tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_I64, 0)) { /* This equivalence has the advantage of not requiring a fixup. */ TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_subi_i64(t, arg1, 1); @@ -2435,12 +2434,12 @@ void tcg_gen_clrsb_i64(TCGv_i64 ret, TCGv_i64 arg) void tcg_gen_ctpop_i64(TCGv_i64 ret, TCGv_i64 arg1) { if (TCG_TARGET_REG_BITS == 64) { - if (tcg_op_supported(INDEX_op_ctpop_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op2_i64(INDEX_op_ctpop_i64, ret, arg1); + if (tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_I64, 0)) { + tcg_gen_op2_i64(INDEX_op_ctpop, ret, arg1); return; } } else { - if (tcg_op_supported(INDEX_op_ctpop_i32, TCG_TYPE_I32, 0)) { + if (tcg_op_supported(INDEX_op_ctpop, TCG_TYPE_I32, 0)) { tcg_gen_ctpop_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1)); tcg_gen_ctpop_i32(TCGV_LOW(ret), TCGV_LOW(arg1)); tcg_gen_add_i32(TCGV_LOW(ret), TCGV_LOW(ret), TCGV_HIGH(ret)); diff --git a/tcg/tcg.c b/tcg/tcg.c index 94997d8610..5b6af803b2 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1027,8 +1027,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), - OUTOP(INDEX_op_ctpop_i32, TCGOutOpUnary, outop_ctpop), - OUTOP(INDEX_op_ctpop_i64, TCGOutOpUnary, outop_ctpop), + OUTOP(INDEX_op_ctpop, TCGOutOpUnary, outop_ctpop), OUTOP(INDEX_op_ctz, TCGOutOpBinary, outop_ctz), OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), @@ -5447,8 +5446,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_ctpop_i32: - case INDEX_op_ctpop_i64: + case INDEX_op_ctpop: case INDEX_op_neg: case INDEX_op_not: { diff --git a/tcg/tci.c b/tcg/tci.c index 8bcf48b251..d58a94ff28 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -577,8 +577,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rr(insn, &r0, &r1); regs[r0] = ~regs[r1]; break; - case INDEX_op_ctpop_i32: - case INDEX_op_ctpop_i64: + case INDEX_op_ctpop: tci_args_rr(insn, &r0, &r1); regs[r0] = ctpop_tr(regs[r1]); break; @@ -1023,6 +1022,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) op_name, str_r(r0), str_r(r1), s2); break; + case INDEX_op_ctpop: case INDEX_op_mov: case INDEX_op_neg: case INDEX_op_not: @@ -1033,8 +1033,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_bswap32_i32: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: - case INDEX_op_ctpop_i32: - case INDEX_op_ctpop_i64: tci_args_rr(insn, &r0, &r1); info->fprintf_func(info->stream, "%-12s %s, %s", op_name, str_r(r0), str_r(r1)); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index a931369a80..1d696a087e 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -883,7 +883,7 @@ static const TCGOutOpBinary outop_xor = { static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { - tcg_out_op_rr(s, glue(INDEX_op_ctpop_i,TCG_TARGET_REG_BITS), a0, a1); + tcg_out_op_rr(s, INDEX_op_ctpop, a0, a1); } static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) From 5641afdf9b496933596fd0bd5fa9cad0033405d4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 8 Jan 2025 21:52:03 -0800 Subject: [PATCH 0427/2760] tcg: Convert muls2 to TCGOutOpMul2 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 4 ++++ tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 25 +++++++++++++------------ tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 17 ++++++++++++----- tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 4 ++++ tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 28 ++++++++++++++++++++-------- tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 4 ++++ tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 4 ++++ tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 27 ++++++++++++++++++++------- tcg/sparc64/tcg-target-con-set.h | 1 + tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 29 ++++++++++++++++++++++++----- tcg/tcg-has.h | 1 - tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 26 ++++++++++++++++++++++---- tcg/tci.c | 23 ++++++++++------------- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target.c.inc | 21 ++++++++++++++++++--- 25 files changed, 158 insertions(+), 79 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 4f1840f44e..c351db223d 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -20,7 +20,6 @@ #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 -#define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -32,7 +31,6 @@ #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 /* * Without FEAT_LSE2, we must use LDXP+STXP to implement atomic 128-bit load, diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 0f01fa8c20..0996c6234b 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2205,6 +2205,10 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_NotImplemented, +}; + static TCGConstraintSetIndex cset_mulh(TCGType type, unsigned flags) { return type == TCG_TYPE_I64 ? C_O1_I2(r, r, r) : C_NotImplemented; diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 1cf3911613..e1f19ffbc9 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -29,7 +29,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 -#define TCG_TARGET_HAS_muls2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index e109c65965..8c0bc78be3 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -929,14 +929,6 @@ static void tcg_out_umull32(TCGContext *s, ARMCond cond, TCGReg rd0, (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); } -static void tcg_out_smull32(TCGContext *s, ARMCond cond, TCGReg rd0, - TCGReg rd1, TCGReg rn, TCGReg rm) -{ - /* smull */ - tcg_out32(s, (cond << 28) | 0x00c00090 | - (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); -} - static void tcg_out_ext8s(TCGContext *s, TCGType t, TCGReg rd, TCGReg rn) { /* sxtb */ @@ -1973,6 +1965,19 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static void tgen_muls2(TCGContext *s, TCGType type, + TCGReg rd0, TCGReg rd1, TCGReg rn, TCGReg rm) +{ + /* smull */ + tcg_out32(s, (COND_AL << 28) | 0x00c00090 | + (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); +} + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_O2_I2(r, r, r, r), + .out_rrrr = tgen_muls2, +}; + static const TCGOutOpBinary outop_mulsh = { .base.static_constraint = C_NotImplemented, }; @@ -2246,9 +2251,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_mulu2_i32: tcg_out_umull32(s, COND_AL, args[0], args[1], args[2], args[3]); break; - case INDEX_op_muls2_i32: - tcg_out_smull32(s, COND_AL, args[0], args[1], args[2], args[3]); - break; case INDEX_op_brcond_i32: c = tcg_out_cmp(s, args[2], args[0], args[1], const_args[1]); @@ -2373,7 +2375,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O1_I2(r, r, rIN); case INDEX_op_mulu2_i32: - case INDEX_op_muls2_i32: return C_O2_I2(r, r, r, r); case INDEX_op_brcond_i32: diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index a71f8c7370..d63b3a3a89 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -33,7 +33,6 @@ #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 -#define TCG_TARGET_HAS_muls2_i32 1 #if TCG_TARGET_REG_BITS == 64 /* Keep 32-bit values zero-extended in a register. */ @@ -46,7 +45,6 @@ #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 1 -#define TCG_TARGET_HAS_muls2_i64 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #else #define TCG_TARGET_HAS_qemu_st8_i32 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 318a30ebe0..43d63cab5c 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2743,6 +2743,18 @@ static const TCGOutOpBinary outop_mul = { .out_rri = tgen_muli, }; +static void tgen_muls2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IMUL, a3); +} + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_O2_I2(a, d, a, r), + .out_rrrr = tgen_muls2, +}; + static const TCGOutOpBinary outop_mulsh = { .base.static_constraint = C_NotImplemented, }; @@ -3136,9 +3148,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, OP_32_64(mulu2): tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_MUL, args[3]); break; - OP_32_64(muls2): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_IMUL, args[3]); - break; OP_32_64(add2): if (const_args[4]) { tgen_arithi(s, ARITH_ADD + rexw, a0, args[4], 1); @@ -3928,8 +3937,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_mulu2_i32: case INDEX_op_mulu2_i64: - case INDEX_op_muls2_i32: - case INDEX_op_muls2_i64: return C_O2_I2(a, d, a, r); case INDEX_op_add2_i32: diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 33a1cf2326..491ebf0d06 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -15,7 +15,6 @@ #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_mulu2_i32 0 -#define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -30,7 +29,6 @@ #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 (cpuinfo & CPUINFO_LSX) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 4ef7c6b945..95a0614e6e 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1421,6 +1421,10 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_mulsh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 470aa16452..fd0b674402 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -40,7 +40,6 @@ extern bool use_mips32r2_instructions; /* optional instructions */ #define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_muls2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 @@ -52,7 +51,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 (!use_mips32r6_instructions) -#define TCG_TARGET_HAS_muls2_i64 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_ext32s_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index a94c965046..a1f9efb18b 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1823,6 +1823,26 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static void tgen_muls2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_MULT : OPC_DMULT; + tcg_out_opc_reg(s, insn, 0, a2, a3); + tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); + tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0); +} + +static TCGConstraintSetIndex cset_mul2(TCGType type, unsigned flags) +{ + return use_mips32r6_instructions ? C_NotImplemented : C_O2_I2(r, r, r, r); +} + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul2, + .out_rrrr = tgen_muls2, +}; + static void tgen_mulsh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2161,15 +2181,9 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_muls2_i32: - i1 = OPC_MULT; - goto do_hilo2; case INDEX_op_mulu2_i32: i1 = OPC_MULTU; goto do_hilo2; - case INDEX_op_muls2_i64: - i1 = OPC_DMULT; - goto do_hilo2; case INDEX_op_mulu2_i64: i1 = OPC_DMULTU; do_hilo2: @@ -2347,9 +2361,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: return C_O1_I2(r, rz, rz); - case INDEX_op_muls2_i32: case INDEX_op_mulu2_i32: - case INDEX_op_muls2_i64: case INDEX_op_mulu2_i64: return C_O2_I2(r, r, r, r); diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index f071435d98..e711aa0731 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -22,7 +22,6 @@ #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 -#define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 @@ -37,7 +36,6 @@ #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 #endif #define TCG_TARGET_HAS_qemu_ldst_i128 \ diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index ab56c623c7..d4e34e3e7d 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3079,6 +3079,10 @@ static const TCGOutOpBinary outop_mul = { .out_rri = tgen_muli, }; +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_mulsh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index a3b634570b..7e260da61e 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -15,7 +15,6 @@ #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 -#define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -29,7 +28,6 @@ #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index a5cd18c99e..316621b285 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2117,6 +2117,10 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_NotImplemented, +}; + static TCGConstraintSetIndex cset_mulh(TCGType type, unsigned flags) { return type == TCG_TYPE_I32 ? C_NotImplemented : C_O1_I2(r, r, r); diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 87f117ce58..52a76fc0b5 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -36,7 +36,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 0 -#define TCG_TARGET_HAS_muls2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -48,7 +47,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 1 -#define TCG_TARGET_HAS_muls2_i64 HAVE_FACILITY(MISC_INSN_EXT2) #define TCG_TARGET_HAS_qemu_ldst_i128 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 71adb0964d..71f0eb40f8 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2391,6 +2391,26 @@ static const TCGOutOpBinary outop_mul = { .out_rri = tgen_muli, }; +static void tgen_muls2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + tcg_debug_assert((a1 & 1) == 0); + tcg_debug_assert(a0 == a1 + 1); + tcg_out_insn(s, RRFa, MGRK, a1, a2, a3); +} + +static TCGConstraintSetIndex cset_muls2(TCGType type, unsigned flags) +{ + return (type == TCG_TYPE_I64 && HAVE_FACILITY(MISC_INSN_EXT2) + ? C_O2_I2(o, m, r, r) : C_NotImplemented); +} + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_muls2, + .out_rrrr = tgen_muls2, +}; + static const TCGOutOpBinary outop_mulsh = { .base.static_constraint = C_NotImplemented, }; @@ -2846,11 +2866,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_debug_assert(args[0] == args[1] + 1); tcg_out_insn(s, RRE, MLGR, args[1], args[3]); break; - case INDEX_op_muls2_i64: - tcg_debug_assert((args[1] & 1) == 0); - tcg_debug_assert(args[0] == args[1] + 1); - tcg_out_insn(s, RRFa, MGRK, args[1], args[2], args[3]); - break; case INDEX_op_add2_i64: if (const_args[4]) { @@ -3451,8 +3466,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_mulu2_i64: return C_O2_I2(o, m, 0, r); - case INDEX_op_muls2_i64: - return C_O2_I2(o, m, r, r); case INDEX_op_add2_i32: case INDEX_op_sub2_i32: diff --git a/tcg/sparc64/tcg-target-con-set.h b/tcg/sparc64/tcg-target-con-set.h index d90ba11443..d2ea184fa2 100644 --- a/tcg/sparc64/tcg-target-con-set.h +++ b/tcg/sparc64/tcg-target-con-set.h @@ -17,5 +17,6 @@ C_O1_I2(r, r, r) C_O1_I2(r, r, rJ) C_O1_I2(r, rz, rJ) C_O1_I4(r, rz, rJ, rI, 0) +C_O2_I2(r, r, r, r) C_O2_I2(r, r, rz, rJ) C_O2_I4(r, r, rz, rz, rJ, rJ) diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 40e54e1543..dea0941cac 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -21,7 +21,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 -#define TCG_TARGET_HAS_muls2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 @@ -33,7 +32,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 43ca23f593..be2072c027 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1429,6 +1429,30 @@ static const TCGOutOpBinary outop_mul = { .out_rri = tgen_muli, }; +/* + * The 32-bit multiply insns produce a full 64-bit result. + * Supporting 32-bit mul[us]2 opcodes avoids sign/zero-extensions + * before the actual multiply; we only need extract the high part + * into the separate operand. + */ +static TCGConstraintSetIndex cset_mul2(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_I32 ? C_O2_I2(r, r, r, r) : C_NotImplemented; +} + +static void tgen_muls2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + tcg_out_arith(s, a0, a2, a3, ARITH_SMUL); + tcg_out_arithi(s, a1, a0, 32, SHIFT_SRLX); +} + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul2, + .out_rrrr = tgen_muls2, +}; + static const TCGOutOpBinary outop_mulsh = { .base.static_constraint = C_NotImplemented, }; @@ -1696,10 +1720,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_mulu2_i32: c = ARITH_UMUL; - goto do_mul2; - case INDEX_op_muls2_i32: - c = ARITH_SMUL; - do_mul2: /* The 32-bit multiply insns produce a full 64-bit result. */ tcg_out_arithc(s, a0, a2, args[3], const_args[3], c); tcg_out_arithi(s, a1, a0, 32, SHIFT_SRLX); @@ -1828,7 +1848,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub2_i64: return C_O2_I4(r, r, rz, rz, rJ, rJ); case INDEX_op_mulu2_i32: - case INDEX_op_muls2_i32: return C_O2_I2(r, r, rz, rJ); default: diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 97f4e83303..ac387b2544 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -20,7 +20,6 @@ #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_mulu2_i64 0 -#define TCG_TARGET_HAS_muls2_i64 0 /* Turn some undef macros into true macros. */ #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 0eeec47b83..8a0846a8d2 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1162,7 +1162,7 @@ void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_muls2_i32) { + if (tcg_op_supported(INDEX_op_muls2_i32, TCG_TYPE_I32, 0)) { tcg_gen_op4_i32(INDEX_op_muls2_i32, rl, rh, arg1, arg2); } else if (tcg_op_supported(INDEX_op_mulsh, TCG_TYPE_I32, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); @@ -2880,7 +2880,7 @@ void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) { - if (TCG_TARGET_HAS_muls2_i64) { + if (tcg_op_supported(INDEX_op_muls2_i64, TCG_TYPE_I64, 0)) { tcg_gen_op4_i64(INDEX_op_muls2_i64, rl, rh, arg1, arg2); } else if (tcg_op_supported(INDEX_op_mulsh, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index 5b6af803b2..b1efc44725 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -992,6 +992,12 @@ typedef struct TCGOutOpDivRem { TCGReg a0, TCGReg a1, TCGReg a4); } TCGOutOpDivRem; +typedef struct TCGOutOpMul2 { + TCGOutOp base; + void (*out_rrrr)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3); +} TCGOutOpMul2; + typedef struct TCGOutOpUnary { TCGOutOp base; void (*out_rr)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1); @@ -1035,6 +1041,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_divu2, TCGOutOpDivRem, outop_divu2), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), + OUTOP(INDEX_op_muls2_i32, TCGOutOpMul2, outop_muls2), + OUTOP(INDEX_op_muls2_i64, TCGOutOpMul2, outop_muls2), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), OUTOP(INDEX_op_muluh, TCGOutOpBinary, outop_muluh), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), @@ -2285,8 +2293,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_sub2_i32; case INDEX_op_mulu2_i32: return TCG_TARGET_HAS_mulu2_i32; - case INDEX_op_muls2_i32: - return TCG_TARGET_HAS_muls2_i32; case INDEX_op_bswap16_i32: return TCG_TARGET_HAS_bswap16_i32; case INDEX_op_bswap32_i32: @@ -2336,8 +2342,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_sub2_i64; case INDEX_op_mulu2_i64: return TCG_TARGET_HAS_mulu2_i64; - case INDEX_op_muls2_i64: - return TCG_TARGET_HAS_muls2_i64; case INDEX_op_mov_vec: case INDEX_op_dup_vec: @@ -5473,6 +5477,20 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_muls2_i32: + case INDEX_op_muls2_i64: + { + const TCGOutOpMul2 *out = + container_of(all_outop[op->opc], TCGOutOpMul2, base); + + tcg_debug_assert(!const_args[2]); + tcg_debug_assert(!const_args[3]); + out->out_rrrr(s, type, new_args[0], new_args[1], + new_args[2], new_args[3]); + } + break; + + default: if (def->flags & TCG_OPF_VECTOR) { tcg_out_vec_op(s, op->opc, type - TCG_TYPE_V64, diff --git a/tcg/tci.c b/tcg/tci.c index d58a94ff28..51cbb5760a 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -581,6 +581,16 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rr(insn, &r0, &r1); regs[r0] = ctpop_tr(regs[r1]); break; + case INDEX_op_muls2_i32: + case INDEX_op_muls2_i64: + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); +#if TCG_TARGET_REG_BITS == 32 + tmp64 = (int64_t)(int32_t)regs[r2] * (int32_t)regs[r3]; + tci_write_reg64(regs, r1, r0, tmp64); +#else + muls64(®s[r0], ®s[r1], regs[r2], regs[r3]); +#endif + break; /* Arithmetic operations (32 bit). */ @@ -675,13 +685,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_write_reg64(regs, r1, r0, tmp64); break; #endif -#if TCG_TARGET_HAS_muls2_i32 - case INDEX_op_muls2_i32: - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - tmp64 = (int64_t)(int32_t)regs[r2] * (int32_t)regs[r3]; - tci_write_reg64(regs, r1, r0, tmp64); - break; -#endif #if TCG_TARGET_HAS_bswap16_i32 || TCG_TARGET_HAS_bswap16_i64 CASE_32_64(bswap16) tci_args_rr(insn, &r0, &r1); @@ -745,12 +748,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, mulu64(®s[r0], ®s[r1], regs[r2], regs[r3]); break; #endif -#if TCG_TARGET_HAS_muls2_i64 - case INDEX_op_muls2_i64: - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - muls64(®s[r0], ®s[r1], regs[r2], regs[r3]); - break; -#endif #if TCG_TARGET_HAS_add2_i64 case INDEX_op_add2_i64: tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 774fb149fc..a3d04b0ee2 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -11,7 +11,6 @@ #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 0 -#define TCG_TARGET_HAS_muls2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 @@ -21,7 +20,6 @@ #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_negsetcond_i64 0 -#define TCG_TARGET_HAS_muls2_i64 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_mulu2_i32 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 1d696a087e..f568d4edb9 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -100,8 +100,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_mulu2_i32: case INDEX_op_mulu2_i64: - case INDEX_op_muls2_i32: - case INDEX_op_muls2_i64: return C_O2_I2(r, r, r, r); case INDEX_op_movcond_i32: @@ -710,6 +708,24 @@ static const TCGOutOpBinary outop_mul = { .out_rrr = tgen_mul, }; +static TCGConstraintSetIndex cset_mul2(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_REG ? C_O2_I2(r, r, r, r) : C_NotImplemented; +} + +static void tgen_muls2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + tcg_out_op_rrrr(s, glue(INDEX_op_muls2_i,TCG_TARGET_REG_BITS), + a0, a1, a2, a3); +} + +static const TCGOutOpMul2 outop_muls2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul2, + .out_rrrr = tgen_muls2, +}; + static const TCGOutOpBinary outop_mulsh = { .base.static_constraint = C_NotImplemented, }; @@ -1009,7 +1025,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, #endif CASE_32_64(mulu2) - CASE_32_64(muls2) tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], args[3]); break; From bfe964809bf6ce951b2e674929d7b730c754e298 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 9 Jan 2025 07:24:32 -0800 Subject: [PATCH 0428/2760] tcg: Merge INDEX_op_muls2_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 17 +++++++++-------- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 9 +++------ tcg/tci.c | 6 ++---- tcg/tci/tcg-target.c.inc | 3 +-- 7 files changed, 21 insertions(+), 27 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index fb7764e3c0..0394767291 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -604,7 +604,7 @@ Multiword arithmetic support - | Similar to mul, except two unsigned inputs *t1* and *t2* yielding the full double-word product *t0*. The latter is returned in two single-word outputs. - * - muls2_i32/i64 *t0_low*, *t0_high*, *t1*, *t2* + * - muls2 *t0_low*, *t0_high*, *t1*, *t2* - | Similar to mulu2, except the two inputs *t1* and *t2* are signed. diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index f4ccde074b..a45b22ca1a 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -51,6 +51,7 @@ DEF(divu, 1, 2, 0, TCG_OPF_INT) DEF(divu2, 2, 3, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(mul, 1, 2, 0, TCG_OPF_INT) +DEF(muls2, 2, 2, 0, TCG_OPF_INT) DEF(mulsh, 1, 2, 0, TCG_OPF_INT) DEF(muluh, 1, 2, 0, TCG_OPF_INT) DEF(nand, 1, 2, 0, TCG_OPF_INT) @@ -92,7 +93,6 @@ DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(add2_i32, 2, 4, 0, 0) DEF(sub2_i32, 2, 4, 0, 0) DEF(mulu2_i32, 2, 2, 0, 0) -DEF(muls2_i32, 2, 2, 0, 0) DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(setcond2_i32, 1, 4, 1, 0) @@ -134,7 +134,6 @@ DEF(bswap64_i64, 1, 1, 1, 0) DEF(add2_i64, 2, 4, 0, 0) DEF(sub2_i64, 2, 4, 0, 0) DEF(mulu2_i64, 2, 2, 0, 0) -DEF(muls2_i64, 2, 2, 0, 0) #define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) diff --git a/tcg/optimize.c b/tcg/optimize.c index bf625f770c..756f681e88 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2074,16 +2074,17 @@ static bool fold_multiply2(OptContext *ctx, TCGOp *op) h = (int32_t)(l >> 32); l = (int32_t)l; break; - case INDEX_op_muls2_i32: - l = (int64_t)(int32_t)a * (int32_t)b; - h = l >> 32; - l = (int32_t)l; - break; case INDEX_op_mulu2_i64: mulu64(&l, &h, a, b); break; - case INDEX_op_muls2_i64: - muls64(&l, &h, a, b); + case INDEX_op_muls2: + if (ctx->type == TCG_TYPE_I32) { + l = (int64_t)(int32_t)a * (int32_t)b; + h = l >> 32; + l = (int32_t)l; + } else { + muls64(&l, &h, a, b); + } break; default: g_assert_not_reached(); @@ -2973,7 +2974,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_muluh: done = fold_mul_highpart(&ctx, op); break; - CASE_OP_32_64(muls2): + case INDEX_op_muls2: CASE_OP_32_64(mulu2): done = fold_multiply2(&ctx, op); break; diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 8a0846a8d2..0f48484dfe 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1162,8 +1162,8 @@ void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_muls2_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op4_i32(INDEX_op_muls2_i32, rl, rh, arg1, arg2); + if (tcg_op_supported(INDEX_op_muls2, TCG_TYPE_I32, 0)) { + tcg_gen_op4_i32(INDEX_op_muls2, rl, rh, arg1, arg2); } else if (tcg_op_supported(INDEX_op_mulsh, TCG_TYPE_I32, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_mul, t, arg1, arg2); @@ -2880,8 +2880,8 @@ void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) { - if (tcg_op_supported(INDEX_op_muls2_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op4_i64(INDEX_op_muls2_i64, rl, rh, arg1, arg2); + if (tcg_op_supported(INDEX_op_muls2, TCG_TYPE_I64, 0)) { + tcg_gen_op4_i64(INDEX_op_muls2, rl, rh, arg1, arg2); } else if (tcg_op_supported(INDEX_op_mulsh, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_mul, t, arg1, arg2); diff --git a/tcg/tcg.c b/tcg/tcg.c index b1efc44725..5b22c75d2e 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1041,8 +1041,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_divu2, TCGOutOpDivRem, outop_divu2), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), - OUTOP(INDEX_op_muls2_i32, TCGOutOpMul2, outop_muls2), - OUTOP(INDEX_op_muls2_i64, TCGOutOpMul2, outop_muls2), + OUTOP(INDEX_op_muls2, TCGOutOpMul2, outop_muls2), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), OUTOP(INDEX_op_muluh, TCGOutOpBinary, outop_muluh), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), @@ -4008,8 +4007,7 @@ liveness_pass_1(TCGContext *s) } goto do_not_remove; - case INDEX_op_muls2_i32: - case INDEX_op_muls2_i64: + case INDEX_op_muls2: opc_new = INDEX_op_mul; opc_new2 = INDEX_op_mulsh; goto do_mul2; @@ -5477,8 +5475,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_muls2_i32: - case INDEX_op_muls2_i64: + case INDEX_op_muls2: { const TCGOutOpMul2 *out = container_of(all_outop[op->opc], TCGOutOpMul2, base); diff --git a/tcg/tci.c b/tcg/tci.c index 51cbb5760a..708ded34c7 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -581,8 +581,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rr(insn, &r0, &r1); regs[r0] = ctpop_tr(regs[r1]); break; - case INDEX_op_muls2_i32: - case INDEX_op_muls2_i64: + case INDEX_op_muls2: tci_args_rrrr(insn, &r0, &r1, &r2, &r3); #if TCG_TARGET_REG_BITS == 32 tmp64 = (int64_t)(int32_t)regs[r2] * (int32_t)regs[r3]; @@ -1095,10 +1094,9 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) str_r(r3), str_r(r4), str_c(c)); break; + case INDEX_op_muls2: case INDEX_op_mulu2_i32: case INDEX_op_mulu2_i64: - case INDEX_op_muls2_i32: - case INDEX_op_muls2_i64: tci_args_rrrr(insn, &r0, &r1, &r2, &r3); info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s", op_name, str_r(r0), str_r(r1), diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index f568d4edb9..aa3ce929b4 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -716,8 +716,7 @@ static TCGConstraintSetIndex cset_mul2(TCGType type, unsigned flags) static void tgen_muls2(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) { - tcg_out_op_rrrr(s, glue(INDEX_op_muls2_i,TCG_TARGET_REG_BITS), - a0, a1, a2, a3); + tcg_out_op_rrrr(s, INDEX_op_muls2, a0, a1, a2, a3); } static const TCGOutOpMul2 outop_muls2 = { From d37bc370fcad08698e4b6de99184361a2cf71ac0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 9 Jan 2025 08:59:52 -0800 Subject: [PATCH 0429/2760] tcg: Convert mulu2 to TCGOutOpMul2 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 4 ++++ tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 27 +++++++++++++-------------- tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 19 ++++++++++++------- tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 4 ++++ tcg/mips/tcg-target-has.h | 2 -- tcg/mips/tcg-target.c.inc | 29 +++++++++++++++-------------- tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 4 ++++ tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 4 ++++ tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 31 +++++++++++++++++++++---------- tcg/sparc64/tcg-target-con-set.h | 1 - tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 23 ++++++++++++++--------- tcg/tcg-has.h | 1 - tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 8 ++++---- tcg/tci.c | 23 ++++++++++------------- tcg/tci/tcg-target-has.h | 4 ---- tcg/tci/tcg-target.c.inc | 21 +++++++++++++-------- 25 files changed, 122 insertions(+), 106 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index c351db223d..0c370d7dda 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -19,7 +19,6 @@ #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -30,7 +29,6 @@ #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 0 /* * Without FEAT_LSE2, we must use LDXP+STXP to implement atomic 128-bit load, diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 0996c6234b..46ad91f40e 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2226,6 +2226,10 @@ static const TCGOutOpBinary outop_mulsh = { .out_rrr = tgen_mulsh, }; +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_muluh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index e1f19ffbc9..ccbc39a23e 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -28,7 +28,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 8c0bc78be3..55e9f66340 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -921,14 +921,6 @@ static void tcg_out_dat_rIN(TCGContext *s, ARMCond cond, ARMInsn opc, } } -static void tcg_out_umull32(TCGContext *s, ARMCond cond, TCGReg rd0, - TCGReg rd1, TCGReg rn, TCGReg rm) -{ - /* umull */ - tcg_out32(s, (cond << 28) | 0x00800090 | - (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); -} - static void tcg_out_ext8s(TCGContext *s, TCGType t, TCGReg rd, TCGReg rn) { /* sxtb */ @@ -1982,6 +1974,19 @@ static const TCGOutOpBinary outop_mulsh = { .base.static_constraint = C_NotImplemented, }; +static void tgen_mulu2(TCGContext *s, TCGType type, + TCGReg rd0, TCGReg rd1, TCGReg rn, TCGReg rm) +{ + /* umull */ + tcg_out32(s, (COND_AL << 28) | 0x00800090 | + (rd1 << 16) | (rd0 << 12) | (rm << 8) | rn); +} + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_O2_I2(r, r, r, r), + .out_rrrr = tgen_mulu2, +}; + static const TCGOutOpBinary outop_muluh = { .base.static_constraint = C_NotImplemented, }; @@ -2248,9 +2253,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } tcg_out_mov_reg(s, COND_AL, args[0], a0); break; - case INDEX_op_mulu2_i32: - tcg_out_umull32(s, COND_AL, args[0], args[1], args[2], args[3]); - break; case INDEX_op_brcond_i32: c = tcg_out_cmp(s, args[2], args[0], args[1], const_args[1]); @@ -2374,9 +2376,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond_i32: return C_O1_I2(r, r, rIN); - case INDEX_op_mulu2_i32: - return C_O2_I2(r, r, r, r); - case INDEX_op_brcond_i32: return C_O0_I2(r, rIN); case INDEX_op_deposit_i32: diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index d63b3a3a89..c92a049fd7 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -32,7 +32,6 @@ #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 1 #if TCG_TARGET_REG_BITS == 64 /* Keep 32-bit values zero-extended in a register. */ @@ -44,7 +43,6 @@ #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #else #define TCG_TARGET_HAS_qemu_st8_i32 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 43d63cab5c..d1b37c4388 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2763,6 +2763,18 @@ static const TCGOutOpBinary outop_muluh = { .base.static_constraint = C_NotImplemented, }; +static void tgen_mulu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_MUL, a3); +} + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_O2_I2(a, d, a, r), + .out_rrrr = tgen_mulu2, +}; + static const TCGOutOpBinary outop_nand = { .base.static_constraint = C_NotImplemented, }; @@ -3145,9 +3157,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_st(s, a0, a1, a2, args[3], TCG_TYPE_I128); break; - OP_32_64(mulu2): - tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_MUL, args[3]); - break; OP_32_64(add2): if (const_args[4]) { tgen_arithi(s, ARITH_ADD + rexw, a0, args[4], 1); @@ -3935,10 +3944,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_movcond_i64: return C_O1_I4(r, r, reT, r, 0); - case INDEX_op_mulu2_i32: - case INDEX_op_mulu2_i64: - return C_O2_I2(a, d, a, r); - case INDEX_op_add2_i32: case INDEX_op_add2_i64: case INDEX_op_sub2_i32: diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 491ebf0d06..12a721b4da 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -14,7 +14,6 @@ #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 -#define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -28,7 +27,6 @@ #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 -#define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 (cpuinfo & CPUINFO_LSX) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 95a0614e6e..fa9da82df8 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1440,6 +1440,10 @@ static const TCGOutOpBinary outop_mulsh = { .out_rrr = tgen_mulsh, }; +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_muluh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index fd0b674402..05701fd228 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -39,7 +39,6 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_mulu2_i32 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_negsetcond_i32 0 @@ -50,7 +49,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 -#define TCG_TARGET_HAS_mulu2_i64 (!use_mips32r6_instructions) #define TCG_TARGET_HAS_ext32s_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 #define TCG_TARGET_HAS_negsetcond_i64 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index a1f9efb18b..6a97264c7c 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1861,6 +1861,21 @@ static const TCGOutOpBinary outop_mulsh = { .out_rrr = tgen_mulsh, }; +static void tgen_mulu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + MIPSInsn insn = type == TCG_TYPE_I32 ? OPC_MULTU : OPC_DMULTU; + tcg_out_opc_reg(s, insn, 0, a2, a3); + tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); + tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0); +} + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul2, + .out_rrrr = tgen_mulu2, +}; + static void tgen_muluh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2181,17 +2196,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_mulu2_i32: - i1 = OPC_MULTU; - goto do_hilo2; - case INDEX_op_mulu2_i64: - i1 = OPC_DMULTU; - do_hilo2: - tcg_out_opc_reg(s, i1, 0, a2, args[3]); - tcg_out_opc_reg(s, OPC_MFLO, a0, 0, 0); - tcg_out_opc_reg(s, OPC_MFHI, a1, 0, 0); - break; - case INDEX_op_bswap16_i32: case INDEX_op_bswap16_i64: tcg_out_bswap16(s, a0, a1, a2); @@ -2361,9 +2365,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: return C_O1_I2(r, rz, rz); - case INDEX_op_mulu2_i32: - case INDEX_op_mulu2_i64: - return C_O2_I2(r, r, r, r); case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index e711aa0731..5cc059fe9a 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -21,7 +21,6 @@ #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_negsetcond_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 @@ -35,7 +34,6 @@ #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 0 #endif #define TCG_TARGET_HAS_qemu_ldst_i128 \ diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index d4e34e3e7d..f2cb45029f 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3095,6 +3095,10 @@ static const TCGOutOpBinary outop_mulsh = { .out_rrr = tgen_mulsh, }; +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_muluh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 7e260da61e..9b86b8bf48 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -14,7 +14,6 @@ #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -27,7 +26,6 @@ #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 316621b285..071be449f6 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2138,6 +2138,10 @@ static const TCGOutOpBinary outop_mulsh = { .out_rrr = tgen_mulsh, }; +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_muluh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 52a76fc0b5..894a9f64e0 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -35,7 +35,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -46,7 +45,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 1 #define TCG_TARGET_HAS_qemu_ldst_i128 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 71f0eb40f8..18b83d5899 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2415,6 +2415,27 @@ static const TCGOutOpBinary outop_mulsh = { .base.static_constraint = C_NotImplemented, }; +static void tgen_mulu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + tcg_debug_assert(a0 == a2); + tcg_debug_assert((a1 & 1) == 0); + tcg_debug_assert(a0 == a1 + 1); + tcg_out_insn(s, RRE, MLGR, a1, a3); +} + +static TCGConstraintSetIndex cset_mulu2(TCGType type, unsigned flags) +{ + return (type == TCG_TYPE_I64 && HAVE_FACILITY(MISC_INSN_EXT2) + ? C_O2_I2(o, m, 0, r) : C_NotImplemented); +} + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mulu2, + .out_rrrr = tgen_mulu2, +}; + static const TCGOutOpBinary outop_muluh = { .base.static_constraint = C_NotImplemented, }; @@ -2860,13 +2881,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_insn(s, RRE, LRVGR, args[0], args[1]); break; - case INDEX_op_mulu2_i64: - tcg_debug_assert(args[0] == args[2]); - tcg_debug_assert((args[1] & 1) == 0); - tcg_debug_assert(args[0] == args[1] + 1); - tcg_out_insn(s, RRE, MLGR, args[1], args[3]); - break; - case INDEX_op_add2_i64: if (const_args[4]) { if ((int64_t)args[4] >= 0) { @@ -3464,9 +3478,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_movcond_i64: return C_O1_I4(r, r, rC, rI, r); - case INDEX_op_mulu2_i64: - return C_O2_I2(o, m, 0, r); - case INDEX_op_add2_i32: case INDEX_op_sub2_i32: return C_N1_O1_I4(r, r, 0, 1, ri, r); diff --git a/tcg/sparc64/tcg-target-con-set.h b/tcg/sparc64/tcg-target-con-set.h index d2ea184fa2..85dcfbc375 100644 --- a/tcg/sparc64/tcg-target-con-set.h +++ b/tcg/sparc64/tcg-target-con-set.h @@ -18,5 +18,4 @@ C_O1_I2(r, r, rJ) C_O1_I2(r, rz, rJ) C_O1_I4(r, rz, rJ, rI, 0) C_O2_I2(r, r, r, r) -C_O2_I2(r, r, rz, rJ) C_O2_I4(r, r, rz, rz, rJ, rJ) diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index dea0941cac..258c978b5e 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -20,7 +20,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 @@ -31,7 +30,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index be2072c027..41c4e77466 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1457,6 +1457,19 @@ static const TCGOutOpBinary outop_mulsh = { .base.static_constraint = C_NotImplemented, }; +static void tgen_mulu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + tcg_out_arith(s, a0, a2, a3, ARITH_UMUL); + tcg_out_arithi(s, a1, a0, 32, SHIFT_SRLX); +} + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul2, + .out_rrrr = tgen_mulu2, +}; + static void tgen_muluh(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1646,7 +1659,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { TCGArg a0, a1, a2; - int c, c2; + int c2; /* Hoist the loads of the most common arguments. */ a0 = args[0]; @@ -1718,12 +1731,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[4], const_args[4], args[5], const_args[5], ARITH_SUBCC, ARITH_SUBC); break; - case INDEX_op_mulu2_i32: - c = ARITH_UMUL; - /* The 32-bit multiply insns produce a full 64-bit result. */ - tcg_out_arithc(s, a0, a2, args[3], const_args[3], c); - tcg_out_arithi(s, a1, a0, 32, SHIFT_SRLX); - break; case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); @@ -1847,8 +1854,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub2_i32: case INDEX_op_sub2_i64: return C_O2_I4(r, r, rz, rz, rJ, rJ); - case INDEX_op_mulu2_i32: - return C_O2_I2(r, r, rz, rJ); default: return C_NotImplemented; diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index ac387b2544..d4fc7148b4 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -19,7 +19,6 @@ #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 -#define TCG_TARGET_HAS_mulu2_i64 0 /* Turn some undef macros into true macros. */ #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 0f48484dfe..a4d976242a 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1138,7 +1138,7 @@ void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) { - if (TCG_TARGET_HAS_mulu2_i32) { + if (tcg_op_supported(INDEX_op_mulu2_i32, TCG_TYPE_I32, 0)) { tcg_gen_op4_i32(INDEX_op_mulu2_i32, rl, rh, arg1, arg2); } else if (tcg_op_supported(INDEX_op_muluh, TCG_TYPE_I32, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); @@ -1156,7 +1156,7 @@ void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); } else { - qemu_build_not_reached(); + g_assert_not_reached(); } } @@ -2861,7 +2861,7 @@ void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) { - if (TCG_TARGET_HAS_mulu2_i64) { + if (tcg_op_supported(INDEX_op_mulu2_i64, TCG_TYPE_I64, 0)) { tcg_gen_op4_i64(INDEX_op_mulu2_i64, rl, rh, arg1, arg2); } else if (tcg_op_supported(INDEX_op_muluh, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); @@ -2888,7 +2888,7 @@ void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_op3_i64(INDEX_op_mulsh, rh, arg1, arg2); tcg_gen_mov_i64(rl, t); tcg_temp_free_i64(t); - } else if (TCG_TARGET_HAS_mulu2_i64 || + } else if (tcg_op_supported(INDEX_op_mulu2_i64, TCG_TYPE_I64, 0) || tcg_op_supported(INDEX_op_muluh, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index 5b22c75d2e..d38b55d04f 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1043,6 +1043,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_muls2, TCGOutOpMul2, outop_muls2), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), + OUTOP(INDEX_op_mulu2_i32, TCGOutOpMul2, outop_mulu2), + OUTOP(INDEX_op_mulu2_i64, TCGOutOpMul2, outop_mulu2), OUTOP(INDEX_op_muluh, TCGOutOpBinary, outop_muluh), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), @@ -2290,8 +2292,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_add2_i32; case INDEX_op_sub2_i32: return TCG_TARGET_HAS_sub2_i32; - case INDEX_op_mulu2_i32: - return TCG_TARGET_HAS_mulu2_i32; case INDEX_op_bswap16_i32: return TCG_TARGET_HAS_bswap16_i32; case INDEX_op_bswap32_i32: @@ -2339,8 +2339,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_add2_i64; case INDEX_op_sub2_i64: return TCG_TARGET_HAS_sub2_i64; - case INDEX_op_mulu2_i64: - return TCG_TARGET_HAS_mulu2_i64; case INDEX_op_mov_vec: case INDEX_op_dup_vec: @@ -5476,6 +5474,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_muls2: + case INDEX_op_mulu2_i32: + case INDEX_op_mulu2_i64: { const TCGOutOpMul2 *out = container_of(all_outop[op->opc], TCGOutOpMul2, base); diff --git a/tcg/tci.c b/tcg/tci.c index 708ded34c7..5c8c62c0ef 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -588,6 +588,16 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_write_reg64(regs, r1, r0, tmp64); #else muls64(®s[r0], ®s[r1], regs[r2], regs[r3]); +#endif + break; + case INDEX_op_mulu2_i32: + case INDEX_op_mulu2_i64: + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); +#if TCG_TARGET_REG_BITS == 32 + tmp64 = (uint64_t)(uint32_t)regs[r2] * (uint32_t)regs[r3]; + tci_write_reg64(regs, r1, r0, tmp64); +#else + mulu64(®s[r0], ®s[r1], regs[r2], regs[r3]); #endif break; @@ -677,13 +687,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_write_reg64(regs, r1, r0, T1 - T2); break; #endif -#if TCG_TARGET_HAS_mulu2_i32 - case INDEX_op_mulu2_i32: - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - tmp64 = (uint64_t)(uint32_t)regs[r2] * (uint32_t)regs[r3]; - tci_write_reg64(regs, r1, r0, tmp64); - break; -#endif #if TCG_TARGET_HAS_bswap16_i32 || TCG_TARGET_HAS_bswap16_i64 CASE_32_64(bswap16) tci_args_rr(insn, &r0, &r1); @@ -741,12 +744,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ? ctz64(regs[r1]) : regs[r2]; break; -#if TCG_TARGET_HAS_mulu2_i64 - case INDEX_op_mulu2_i64: - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - mulu64(®s[r0], ®s[r1], regs[r2], regs[r3]); - break; -#endif #if TCG_TARGET_HAS_add2_i64 case INDEX_op_add2_i64: tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index a3d04b0ee2..2402889bec 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -22,12 +22,8 @@ #define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_mulu2_i32 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 -#define TCG_TARGET_HAS_mulu2_i64 1 -#else -#define TCG_TARGET_HAS_mulu2_i32 1 #endif /* TCG_TARGET_REG_BITS == 64 */ #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index aa3ce929b4..4bce206f80 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -98,10 +98,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O0_I4(r, r, r, r); #endif - case INDEX_op_mulu2_i32: - case INDEX_op_mulu2_i64: - return C_O2_I2(r, r, r, r); - case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: case INDEX_op_setcond2_i32: @@ -729,6 +725,19 @@ static const TCGOutOpBinary outop_mulsh = { .base.static_constraint = C_NotImplemented, }; +static void tgen_mulu2(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) +{ + tcg_out_op_rrrr(s, glue(INDEX_op_mulu2_i,TCG_TARGET_REG_BITS), + a0, a1, a2, a3); +} + +static const TCGOutOpMul2 outop_mulu2 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_mul2, + .out_rrrr = tgen_mulu2, +}; + static const TCGOutOpBinary outop_muluh = { .base.static_constraint = C_NotImplemented, }; @@ -1023,10 +1032,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; #endif - CASE_32_64(mulu2) - tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], args[3]); - break; - case INDEX_op_qemu_ld_i64: case INDEX_op_qemu_st_i64: if (TCG_TARGET_REG_BITS == 32) { From d776198cd31d1578c4b0239dc80cb2841e86f2f8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 9 Jan 2025 09:11:53 -0800 Subject: [PATCH 0430/2760] tcg: Merge INDEX_op_mulu2_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 17 +++++++++-------- tcg/tcg-op.c | 10 +++++----- tcg/tcg.c | 9 +++------ tcg/tci.c | 6 ++---- tcg/tci/tcg-target.c.inc | 3 +-- 7 files changed, 22 insertions(+), 28 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 0394767291..592e002971 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -599,7 +599,7 @@ Multiword arithmetic support formed from two single-word arguments, and the double-word output *t0* is returned in two single-word outputs. - * - mulu2_i32/i64 *t0_low*, *t0_high*, *t1*, *t2* + * - mulu2 *t0_low*, *t0_high*, *t1*, *t2* - | Similar to mul, except two unsigned inputs *t1* and *t2* yielding the full double-word product *t0*. The latter is returned in two single-word outputs. diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index a45b22ca1a..287bdf3473 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -53,6 +53,7 @@ DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(mul, 1, 2, 0, TCG_OPF_INT) DEF(muls2, 2, 2, 0, TCG_OPF_INT) DEF(mulsh, 1, 2, 0, TCG_OPF_INT) +DEF(mulu2, 2, 2, 0, TCG_OPF_INT) DEF(muluh, 1, 2, 0, TCG_OPF_INT) DEF(nand, 1, 2, 0, TCG_OPF_INT) DEF(neg, 1, 1, 0, TCG_OPF_INT) @@ -92,7 +93,6 @@ DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(add2_i32, 2, 4, 0, 0) DEF(sub2_i32, 2, 4, 0, 0) -DEF(mulu2_i32, 2, 2, 0, 0) DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(setcond2_i32, 1, 4, 1, 0) @@ -133,7 +133,6 @@ DEF(bswap64_i64, 1, 1, 1, 0) DEF(add2_i64, 2, 4, 0, 0) DEF(sub2_i64, 2, 4, 0, 0) -DEF(mulu2_i64, 2, 2, 0, 0) #define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) diff --git a/tcg/optimize.c b/tcg/optimize.c index 756f681e88..14d943cf97 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2069,13 +2069,14 @@ static bool fold_multiply2(OptContext *ctx, TCGOp *op) TCGOp *op2; switch (op->opc) { - case INDEX_op_mulu2_i32: - l = (uint64_t)(uint32_t)a * (uint32_t)b; - h = (int32_t)(l >> 32); - l = (int32_t)l; - break; - case INDEX_op_mulu2_i64: - mulu64(&l, &h, a, b); + case INDEX_op_mulu2: + if (ctx->type == TCG_TYPE_I32) { + l = (uint64_t)(uint32_t)a * (uint32_t)b; + h = (int32_t)(l >> 32); + l = (int32_t)l; + } else { + mulu64(&l, &h, a, b); + } break; case INDEX_op_muls2: if (ctx->type == TCG_TYPE_I32) { @@ -2975,7 +2976,7 @@ void tcg_optimize(TCGContext *s) done = fold_mul_highpart(&ctx, op); break; case INDEX_op_muls2: - CASE_OP_32_64(mulu2): + case INDEX_op_mulu2: done = fold_multiply2(&ctx, op); break; case INDEX_op_nand: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index a4d976242a..22af3b12bc 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1138,8 +1138,8 @@ void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2) { - if (tcg_op_supported(INDEX_op_mulu2_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op4_i32(INDEX_op_mulu2_i32, rl, rh, arg1, arg2); + if (tcg_op_supported(INDEX_op_mulu2, TCG_TYPE_I32, 0)) { + tcg_gen_op4_i32(INDEX_op_mulu2, rl, rh, arg1, arg2); } else if (tcg_op_supported(INDEX_op_muluh, TCG_TYPE_I32, 0)) { TCGv_i32 t = tcg_temp_ebb_new_i32(); tcg_gen_op3_i32(INDEX_op_mul, t, arg1, arg2); @@ -2861,8 +2861,8 @@ void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) { - if (tcg_op_supported(INDEX_op_mulu2_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op4_i64(INDEX_op_mulu2_i64, rl, rh, arg1, arg2); + if (tcg_op_supported(INDEX_op_mulu2, TCG_TYPE_I64, 0)) { + tcg_gen_op4_i64(INDEX_op_mulu2, rl, rh, arg1, arg2); } else if (tcg_op_supported(INDEX_op_muluh, TCG_TYPE_I64, 0)) { TCGv_i64 t = tcg_temp_ebb_new_i64(); tcg_gen_op3_i64(INDEX_op_mul, t, arg1, arg2); @@ -2888,7 +2888,7 @@ void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2) tcg_gen_op3_i64(INDEX_op_mulsh, rh, arg1, arg2); tcg_gen_mov_i64(rl, t); tcg_temp_free_i64(t); - } else if (tcg_op_supported(INDEX_op_mulu2_i64, TCG_TYPE_I64, 0) || + } else if (tcg_op_supported(INDEX_op_mulu2, TCG_TYPE_I64, 0) || tcg_op_supported(INDEX_op_muluh, TCG_TYPE_I64, 0)) { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index d38b55d04f..685408f0f9 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1043,8 +1043,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_muls2, TCGOutOpMul2, outop_muls2), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), - OUTOP(INDEX_op_mulu2_i32, TCGOutOpMul2, outop_mulu2), - OUTOP(INDEX_op_mulu2_i64, TCGOutOpMul2, outop_mulu2), + OUTOP(INDEX_op_mulu2, TCGOutOpMul2, outop_mulu2), OUTOP(INDEX_op_muluh, TCGOutOpBinary, outop_muluh), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), @@ -4009,8 +4008,7 @@ liveness_pass_1(TCGContext *s) opc_new = INDEX_op_mul; opc_new2 = INDEX_op_mulsh; goto do_mul2; - case INDEX_op_mulu2_i32: - case INDEX_op_mulu2_i64: + case INDEX_op_mulu2: opc_new = INDEX_op_mul; opc_new2 = INDEX_op_muluh; do_mul2: @@ -5474,8 +5472,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_muls2: - case INDEX_op_mulu2_i32: - case INDEX_op_mulu2_i64: + case INDEX_op_mulu2: { const TCGOutOpMul2 *out = container_of(all_outop[op->opc], TCGOutOpMul2, base); diff --git a/tcg/tci.c b/tcg/tci.c index 5c8c62c0ef..569b5c7ed0 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -590,8 +590,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, muls64(®s[r0], ®s[r1], regs[r2], regs[r3]); #endif break; - case INDEX_op_mulu2_i32: - case INDEX_op_mulu2_i64: + case INDEX_op_mulu2: tci_args_rrrr(insn, &r0, &r1, &r2, &r3); #if TCG_TARGET_REG_BITS == 32 tmp64 = (uint64_t)(uint32_t)regs[r2] * (uint32_t)regs[r3]; @@ -1092,8 +1091,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) break; case INDEX_op_muls2: - case INDEX_op_mulu2_i32: - case INDEX_op_mulu2_i64: + case INDEX_op_mulu2: tci_args_rrrr(insn, &r0, &r1, &r2, &r3); info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s", op_name, str_r(r0), str_r(r1), diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 4bce206f80..563529e055 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -728,8 +728,7 @@ static const TCGOutOpBinary outop_mulsh = { static void tgen_mulu2(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3) { - tcg_out_op_rrrr(s, glue(INDEX_op_mulu2_i,TCG_TARGET_REG_BITS), - a0, a1, a2, a3); + tcg_out_op_rrrr(s, INDEX_op_mulu2, a0, a1, a2, a3); } static const TCGOutOpMul2 outop_mulu2 = { From 84f57d50ac25a24870de6d19f5b588083a4899e2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 9 Jan 2025 20:22:55 +0000 Subject: [PATCH 0431/2760] tcg/loongarch64: Support negsetcond Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/loongarch64/tcg-target-con-set.h | 2 -- tcg/loongarch64/tcg-target-has.h | 4 ++-- tcg/loongarch64/tcg-target.c.inc | 34 ++++++++++++++++++++++------ 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/tcg/loongarch64/tcg-target-con-set.h b/tcg/loongarch64/tcg-target-con-set.h index da84e4d49c..c145d4ab66 100644 --- a/tcg/loongarch64/tcg-target-con-set.h +++ b/tcg/loongarch64/tcg-target-con-set.h @@ -29,8 +29,6 @@ C_O1_I2(r, r, rJ) C_O1_I2(r, r, rU) C_O1_I2(r, r, rW) C_O1_I2(r, 0, rz) -C_O1_I2(r, rz, ri) -C_O1_I2(r, rz, rJ) C_O1_I2(w, w, w) C_O1_I2(w, w, wM) C_O1_I2(w, w, wA) diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 12a721b4da..e9bb913961 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -10,7 +10,7 @@ #include "host/cpuinfo.h" /* optional instructions */ -#define TCG_TARGET_HAS_negsetcond_i32 0 +#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 @@ -19,7 +19,7 @@ #define TCG_TARGET_HAS_qemu_st8_i32 0 /* 64-bit operations */ -#define TCG_TARGET_HAS_negsetcond_i64 0 +#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_bswap16_i64 1 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index fa9da82df8..e7f97aaa5e 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -646,14 +646,29 @@ static int tcg_out_setcond_int(TCGContext *s, TCGCond cond, TCGReg ret, } static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg arg1, tcg_target_long arg2, bool c2) + TCGReg arg1, tcg_target_long arg2, + bool c2, bool neg) { int tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2, c2); + TCGReg tmp = tmpflags & ~SETCOND_FLAGS; - if (tmpflags != ret) { - TCGReg tmp = tmpflags & ~SETCOND_FLAGS; - + if (neg) { + /* If intermediate result is zero/non-zero: test != 0. */ + if (tmpflags & SETCOND_NEZ) { + tcg_out_opc_sltu(s, ret, TCG_REG_ZERO, tmp); + tmp = ret; + } + /* Produce the 0/-1 result. */ + if (tmpflags & SETCOND_INV) { + tcg_out_opc_addi_d(s, ret, tmp, -1); + } else { + tcg_out_opc_sub_d(s, ret, TCG_REG_ZERO, tmp); + } + } else { switch (tmpflags & SETCOND_FLAGS) { + case 0: + tcg_debug_assert(tmp == ret); + break; case SETCOND_INV: /* Intermediate result is boolean: simply invert. */ tcg_out_opc_xori(s, ret, tmp, 1); @@ -1800,7 +1815,11 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: - tcg_out_setcond(s, args[3], a0, a1, a2, c2); + tcg_out_setcond(s, args[3], a0, a1, a2, c2, false); + break; + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: + tcg_out_setcond(s, args[3], a0, a1, a2, c2, true); break; case INDEX_op_movcond_i32: @@ -2434,9 +2453,10 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O1_I2(r, 0, rz); case INDEX_op_setcond_i32: - return C_O1_I2(r, rz, ri); case INDEX_op_setcond_i64: - return C_O1_I2(r, rz, rJ); + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: + return C_O1_I2(r, r, rJ); case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: From ce27066de1bf9e44486834bfeb0aa44022f98ab9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 9 Jan 2025 12:36:32 -0800 Subject: [PATCH 0432/2760] tcg/mips: Support negsetcond Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/mips/tcg-target-has.h | 4 ++-- tcg/mips/tcg-target.c.inc | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 05701fd228..c77d4296cf 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -41,7 +41,7 @@ extern bool use_mips32r2_instructions; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_negsetcond_i32 0 +#define TCG_TARGET_HAS_negsetcond_i32 1 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_add2_i32 0 @@ -51,7 +51,7 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_ext32s_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_negsetcond_i64 0 +#define TCG_TARGET_HAS_negsetcond_i64 1 #endif /* optional instructions detected at runtime */ diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 6a97264c7c..759f152711 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -959,6 +959,25 @@ static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, tcg_out_setcond_end(s, ret, tmpflags); } +static void tcg_out_negsetcond(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg arg1, TCGReg arg2) +{ + int tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2); + TCGReg tmp = tmpflags & ~SETCOND_FLAGS; + + /* If intermediate result is zero/non-zero: test != 0. */ + if (tmpflags & SETCOND_NEZ) { + tcg_out_opc_reg(s, OPC_SLTU, ret, TCG_REG_ZERO, tmp); + tmp = ret; + } + /* Produce the 0/-1 result. */ + if (tmpflags & SETCOND_INV) { + tcg_out_opc_imm(s, OPC_ADDIU, ret, tmp, -1); + } else { + tcg_out_opc_reg(s, OPC_SUBU, ret, TCG_REG_ZERO, tmp); + } +} + static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, TCGReg arg2, TCGLabel *l) { @@ -2270,6 +2289,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_setcond_i64: tcg_out_setcond(s, args[3], a0, a1, a2); break; + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: + tcg_out_negsetcond(s, args[3], a0, a1, a2); + break; case INDEX_op_setcond2_i32: tcg_out_setcond2(s, args[5], a0, a1, a2, args[3], args[4]); break; @@ -2364,6 +2387,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: return C_O1_I2(r, rz, rz); case INDEX_op_deposit_i32: From 9204be8dec1a52e943185df392377d8853decf93 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 9 Jan 2025 12:42:13 -0800 Subject: [PATCH 0433/2760] tcg/tci: Support negsetcond MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/tci/tcg-target-has.h | 4 ++-- tcg/tci/tcg-target.c.inc | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 2402889bec..7787347e05 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -10,7 +10,7 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_negsetcond_i32 0 +#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 @@ -19,7 +19,7 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_negsetcond_i64 0 +#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 563529e055..2eb323b5c5 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -79,6 +79,8 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond_i32: case INDEX_op_setcond_i64: + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: return C_O1_I2(r, r, r); @@ -966,6 +968,17 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[3], args[4], args[5]); break; + case INDEX_op_negsetcond_i32: + tcg_out_op_rrrc(s, INDEX_op_setcond_i32, + args[0], args[1], args[2], args[3]); + tcg_out_op_rr(s, INDEX_op_neg, args[0], args[0]); + break; + case INDEX_op_negsetcond_i64: + tcg_out_op_rrrc(s, INDEX_op_setcond_i64, + args[0], args[1], args[2], args[3]); + tcg_out_op_rr(s, INDEX_op_neg, args[0], args[0]); + break; + CASE_32_64(ld8u) CASE_32_64(ld8s) CASE_32_64(ld16u) From f791458932a21f8d99694af412304abfbce83765 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 9 Jan 2025 12:48:21 -0800 Subject: [PATCH 0434/2760] tcg: Remove TCG_TARGET_HAS_negsetcond_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All targets now provide negsetcond, so remove the conditional. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/arm/tcg-target-has.h | 1 - tcg/i386/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target-has.h | 2 -- tcg/mips/tcg-target-has.h | 2 -- tcg/optimize.c | 24 +++++++++--------------- tcg/ppc/tcg-target-has.h | 2 -- tcg/riscv/tcg-target-has.h | 2 -- tcg/s390x/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target-has.h | 2 -- tcg/tcg-has.h | 1 - tcg/tcg-op.c | 12 +++--------- tcg/tcg.c | 6 ++---- tcg/tci/tcg-target-has.h | 2 -- 14 files changed, 14 insertions(+), 48 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 0c370d7dda..22a574e703 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -16,7 +16,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 -#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_extr_i64_i32 0 @@ -26,7 +25,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 -#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index ccbc39a23e..bfa3be8028 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -27,7 +27,6 @@ extern bool use_neon_instructions; #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 -#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index c92a049fd7..aaf8764cc9 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -29,7 +29,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 -#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 @@ -40,7 +39,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 -#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index e9bb913961..90f0a131ae 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -10,7 +10,6 @@ #include "host/cpuinfo.h" /* optional instructions */ -#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 @@ -19,7 +18,6 @@ #define TCG_TARGET_HAS_qemu_st8_i32 0 /* 64-bit operations */ -#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_bswap16_i64 1 diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index c77d4296cf..c6cecba28b 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -41,7 +41,6 @@ extern bool use_mips32r2_instructions; /* optional instructions */ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 -#define TCG_TARGET_HAS_negsetcond_i32 1 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_add2_i32 0 @@ -51,7 +50,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_ext32s_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 -#define TCG_TARGET_HAS_negsetcond_i64 1 #endif /* optional instructions detected at runtime */ diff --git a/tcg/optimize.c b/tcg/optimize.c index 14d943cf97..0affde323b 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1996,23 +1996,19 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) if (ti_is_const(tt) && ti_is_const(ft)) { uint64_t tv = ti_const_val(tt); uint64_t fv = ti_const_val(ft); - TCGOpcode opc, negopc = 0; + TCGOpcode opc, negopc; TCGCond cond = op->args[5]; switch (ctx->type) { case TCG_TYPE_I32: opc = INDEX_op_setcond_i32; - if (TCG_TARGET_HAS_negsetcond_i32) { - negopc = INDEX_op_negsetcond_i32; - } + negopc = INDEX_op_negsetcond_i32; tv = (int32_t)tv; fv = (int32_t)fv; break; case TCG_TYPE_I64: opc = INDEX_op_setcond_i64; - if (TCG_TARGET_HAS_negsetcond_i64) { - negopc = INDEX_op_negsetcond_i64; - } + negopc = INDEX_op_negsetcond_i64; break; default: g_assert_not_reached(); @@ -2024,14 +2020,12 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) } else if (fv == 1 && tv == 0) { op->opc = opc; op->args[3] = tcg_invert_cond(cond); - } else if (negopc) { - if (tv == -1 && fv == 0) { - op->opc = negopc; - op->args[3] = cond; - } else if (fv == -1 && tv == 0) { - op->opc = negopc; - op->args[3] = tcg_invert_cond(cond); - } + } else if (tv == -1 && fv == 0) { + op->opc = negopc; + op->args[3] = cond; + } else if (fv == -1 && tv == 0) { + op->opc = negopc; + op->args[3] = tcg_invert_cond(cond); } } diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 5cc059fe9a..5c4fc2bc34 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -20,7 +20,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 @@ -31,7 +30,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #endif diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 9b86b8bf48..e18b5cb8ec 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -10,7 +10,6 @@ #include "host/cpuinfo.h" /* optional instructions */ -#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 @@ -18,7 +17,6 @@ #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_bswap16_i64 (cpuinfo & CPUINFO_ZBB) diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 894a9f64e0..41cd8a1d0d 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -32,7 +32,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_extr_i64_i32 0 @@ -42,7 +41,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 258c978b5e..6ed27b8fcc 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -17,7 +17,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -27,7 +26,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index d4fc7148b4..315dfd05aa 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -16,7 +16,6 @@ #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_negsetcond_i64 0 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 /* Turn some undef macros into true macros. */ diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 22af3b12bc..413b68352d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -569,11 +569,8 @@ void tcg_gen_negsetcond_i32(TCGCond cond, TCGv_i32 ret, tcg_gen_movi_i32(ret, -1); } else if (cond == TCG_COND_NEVER) { tcg_gen_movi_i32(ret, 0); - } else if (TCG_TARGET_HAS_negsetcond_i32) { - tcg_gen_op4i_i32(INDEX_op_negsetcond_i32, ret, arg1, arg2, cond); } else { - tcg_gen_setcond_i32(cond, ret, arg1, arg2); - tcg_gen_neg_i32(ret, ret); + tcg_gen_op4i_i32(INDEX_op_negsetcond_i32, ret, arg1, arg2, cond); } } @@ -1950,17 +1947,14 @@ void tcg_gen_negsetcond_i64(TCGCond cond, TCGv_i64 ret, tcg_gen_movi_i64(ret, -1); } else if (cond == TCG_COND_NEVER) { tcg_gen_movi_i64(ret, 0); - } else if (TCG_TARGET_HAS_negsetcond_i64) { + } else if (TCG_TARGET_REG_BITS == 64) { tcg_gen_op4i_i64(INDEX_op_negsetcond_i64, ret, arg1, arg2, cond); - } else if (TCG_TARGET_REG_BITS == 32) { + } else { tcg_gen_op6i_i32(INDEX_op_setcond2_i32, TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2), cond); tcg_gen_neg_i32(TCGV_LOW(ret), TCGV_LOW(ret)); tcg_gen_mov_i32(TCGV_HIGH(ret), TCGV_LOW(ret)); - } else { - tcg_gen_setcond_i64(cond, ret, arg1, arg2); - tcg_gen_neg_i64(ret, ret); } } diff --git a/tcg/tcg.c b/tcg/tcg.c index 685408f0f9..38a329b876 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2268,6 +2268,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return has_type; case INDEX_op_setcond_i32: + case INDEX_op_negsetcond_i32: case INDEX_op_brcond_i32: case INDEX_op_movcond_i32: case INDEX_op_ld8u_i32: @@ -2283,8 +2284,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_deposit_i32: return true; - case INDEX_op_negsetcond_i32: - return TCG_TARGET_HAS_negsetcond_i32; case INDEX_op_extract2_i32: return TCG_TARGET_HAS_extract2_i32; case INDEX_op_add2_i32: @@ -2301,6 +2300,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_REG_BITS == 32; case INDEX_op_setcond_i64: + case INDEX_op_negsetcond_i64: case INDEX_op_brcond_i64: case INDEX_op_movcond_i64: case INDEX_op_ld8u_i64: @@ -2321,8 +2321,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_deposit_i64: return TCG_TARGET_REG_BITS == 64; - case INDEX_op_negsetcond_i64: - return TCG_TARGET_HAS_negsetcond_i64; case INDEX_op_extract2_i64: return TCG_TARGET_HAS_extract2_i64; case INDEX_op_extrl_i64_i32: diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 7787347e05..f45a0688f9 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -10,7 +10,6 @@ #define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 -#define TCG_TARGET_HAS_negsetcond_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 @@ -19,7 +18,6 @@ #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 -#define TCG_TARGET_HAS_negsetcond_i64 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_add2_i64 1 From 5a7b38c8ca056f1ffbb488c1b7c97636b1f3b22b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 09:12:06 -0800 Subject: [PATCH 0435/2760] tcg: Convert setcond, negsetcond to TCGOutOpSetcond Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 121 ++++++++++++++++++++----------- tcg/arm/tcg-target.c.inc | 117 +++++++++++++++++++++--------- tcg/i386/tcg-target.c.inc | 57 +++++++++++---- tcg/loongarch64/tcg-target.c.inc | 51 +++++++++---- tcg/mips/tcg-target-con-set.h | 2 +- tcg/mips/tcg-target.c.inc | 39 +++++----- tcg/ppc/tcg-target.c.inc | 61 ++++++++++------ tcg/riscv/tcg-target.c.inc | 52 +++++++++---- tcg/s390x/tcg-target.c.inc | 64 +++++++++------- tcg/sparc64/tcg-target-con-set.h | 1 - tcg/sparc64/tcg-target.c.inc | 69 +++++++++++++----- tcg/tcg.c | 31 ++++++++ tcg/tci/tcg-target.c.inc | 49 +++++++------ 13 files changed, 477 insertions(+), 237 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 46ad91f40e..2524e73ff4 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1356,25 +1356,37 @@ static inline void tcg_out_dep(TCGContext *s, TCGType ext, TCGReg rd, tcg_out_bfm(s, ext, rd, rn, a, b); } +static void tgen_cmp(TCGContext *s, TCGType ext, TCGCond cond, + TCGReg a, TCGReg b) +{ + if (is_tst_cond(cond)) { + tcg_out_insn(s, 3510, ANDS, ext, TCG_REG_XZR, a, b); + } else { + tcg_out_insn(s, 3502, SUBS, ext, TCG_REG_XZR, a, b); + } +} + +static void tgen_cmpi(TCGContext *s, TCGType ext, TCGCond cond, + TCGReg a, tcg_target_long b) +{ + if (is_tst_cond(cond)) { + tcg_out_logicali(s, I3404_ANDSI, ext, TCG_REG_XZR, a, b); + } else if (b >= 0) { + tcg_debug_assert(is_aimm(b)); + tcg_out_insn(s, 3401, SUBSI, ext, TCG_REG_XZR, a, b); + } else { + tcg_debug_assert(is_aimm(-b)); + tcg_out_insn(s, 3401, ADDSI, ext, TCG_REG_XZR, a, -b); + } +} + static void tcg_out_cmp(TCGContext *s, TCGType ext, TCGCond cond, TCGReg a, tcg_target_long b, bool const_b) { - if (is_tst_cond(cond)) { - if (!const_b) { - tcg_out_insn(s, 3510, ANDS, ext, TCG_REG_XZR, a, b); - } else { - tcg_out_logicali(s, I3404_ANDSI, ext, TCG_REG_XZR, a, b); - } + if (const_b) { + tgen_cmpi(s, ext, cond, a, b); } else { - if (!const_b) { - tcg_out_insn(s, 3502, SUBS, ext, TCG_REG_XZR, a, b); - } else if (b >= 0) { - tcg_debug_assert(is_aimm(b)); - tcg_out_insn(s, 3401, SUBSI, ext, TCG_REG_XZR, a, b); - } else { - tcg_debug_assert(is_aimm(-b)); - tcg_out_insn(s, 3401, ADDSI, ext, TCG_REG_XZR, a, -b); - } + tgen_cmp(s, ext, cond, a, b); } } @@ -2433,6 +2445,59 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_cset(TCGContext *s, TCGCond cond, TCGReg ret) +{ + /* Use CSET alias of CSINC Wd, WZR, WZR, invert(cond). */ + tcg_out_insn(s, 3506, CSINC, TCG_TYPE_I32, ret, TCG_REG_XZR, + TCG_REG_XZR, tcg_invert_cond(cond)); +} + +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_cmp(s, type, cond, a1, a2); + tgen_cset(s, cond, a0); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_cmpi(s, type, cond, a1, a2); + tgen_cset(s, cond, a0); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rC), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_csetm(TCGContext *s, TCGType ext, TCGCond cond, TCGReg ret) +{ + /* Use CSETM alias of CSINV Wd, WZR, WZR, invert(cond). */ + tcg_out_insn(s, 3506, CSINV, ext, ret, TCG_REG_XZR, + TCG_REG_XZR, tcg_invert_cond(cond)); +} + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tgen_cmp(s, type, cond, a1, a2); + tgen_csetm(s, type, cond, a0); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_cmpi(s, type, cond, a1, a2); + tgen_csetm(s, type, cond, a0); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rC), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2507,26 +2572,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_brcond(s, ext, a2, a0, a1, const_args[1], arg_label(args[3])); break; - case INDEX_op_setcond_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_setcond_i64: - tcg_out_cmp(s, ext, args[3], a1, a2, c2); - /* Use CSET alias of CSINC Wd, WZR, WZR, invert(cond). */ - tcg_out_insn(s, 3506, CSINC, TCG_TYPE_I32, a0, TCG_REG_XZR, - TCG_REG_XZR, tcg_invert_cond(args[3])); - break; - - case INDEX_op_negsetcond_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_negsetcond_i64: - tcg_out_cmp(s, ext, args[3], a1, a2, c2); - /* Use CSETM alias of CSINV Wd, WZR, WZR, invert(cond). */ - tcg_out_insn(s, 3506, CSINV, ext, a0, TCG_REG_XZR, - TCG_REG_XZR, tcg_invert_cond(args[3])); - break; - case INDEX_op_movcond_i32: a2 = (int32_t)a2; /* FALLTHRU */ @@ -3114,12 +3159,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - return C_O1_I2(r, r, rC); - case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: return C_O0_I2(r, rC); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 55e9f66340..0f2a029f6d 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1210,31 +1210,48 @@ static void tcg_out_mb(TCGContext *s, TCGArg a0) } } -static TCGCond tcg_out_cmp(TCGContext *s, TCGCond cond, TCGReg a, - TCGArg b, int b_const) +static TCGCond tgen_cmp(TCGContext *s, TCGCond cond, TCGReg a, TCGReg b) +{ + if (is_tst_cond(cond)) { + tcg_out_dat_reg(s, COND_AL, ARITH_TST, 0, a, b, SHIFT_IMM_LSL(0)); + return tcg_tst_eqne_cond(cond); + } + tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, a, b, SHIFT_IMM_LSL(0)); + return cond; +} + +static TCGCond tgen_cmpi(TCGContext *s, TCGCond cond, TCGReg a, TCGArg b) { + int imm12; + if (!is_tst_cond(cond)) { - tcg_out_dat_rIN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, a, b, b_const); + tcg_out_dat_IN(s, COND_AL, ARITH_CMP, ARITH_CMN, 0, a, b); return cond; } - cond = tcg_tst_eqne_cond(cond); - if (b_const) { - int imm12 = encode_imm(b); - - /* - * The compare constraints allow rIN, but TST does not support N. - * Be prepared to load the constant into a scratch register. - */ - if (imm12 >= 0) { - tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, a, imm12); - return cond; - } + /* + * The compare constraints allow rIN, but TST does not support N. + * Be prepared to load the constant into a scratch register. + */ + imm12 = encode_imm(b); + if (imm12 >= 0) { + tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, a, imm12); + } else { tcg_out_movi32(s, COND_AL, TCG_REG_TMP, b); - b = TCG_REG_TMP; + tcg_out_dat_reg(s, COND_AL, ARITH_TST, 0, + a, TCG_REG_TMP, SHIFT_IMM_LSL(0)); + } + return tcg_tst_eqne_cond(cond); +} + +static TCGCond tcg_out_cmp(TCGContext *s, TCGCond cond, TCGReg a, + TCGArg b, int b_const) +{ + if (b_const) { + return tgen_cmpi(s, cond, a, b); + } else { + return tgen_cmp(s, cond, a, b); } - tcg_out_dat_reg(s, COND_AL, ARITH_TST, 0, a, b, SHIFT_IMM_LSL(0)); - return cond; } static TCGCond tcg_out_cmp2(TCGContext *s, const TCGArg *args, @@ -2164,6 +2181,52 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void finish_setcond(TCGContext *s, TCGCond cond, TCGReg ret, bool neg) +{ + tcg_out_movi32(s, tcg_cond_to_arm_cond[tcg_invert_cond(cond)], ret, 0); + tcg_out_movi32(s, tcg_cond_to_arm_cond[cond], ret, neg ? -1 : 1); +} + +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + cond = tgen_cmp(s, cond, a1, a2); + finish_setcond(s, cond, a0, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + cond = tgen_cmpi(s, cond, a1, a2); + finish_setcond(s, cond, a0, false); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rIN), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + cond = tgen_cmp(s, cond, a1, a2); + finish_setcond(s, cond, a0, true); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + cond = tgen_cmpi(s, cond, a1, a2); + finish_setcond(s, cond, a0, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rIN), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2258,20 +2321,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, c = tcg_out_cmp(s, args[2], args[0], args[1], const_args[1]); tcg_out_goto_label(s, tcg_cond_to_arm_cond[c], arg_label(args[3])); break; - case INDEX_op_setcond_i32: - c = tcg_out_cmp(s, args[3], args[1], args[2], const_args[2]); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[c], - ARITH_MOV, args[0], 0, 1); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(c)], - ARITH_MOV, args[0], 0, 0); - break; - case INDEX_op_negsetcond_i32: - c = tcg_out_cmp(s, args[3], args[1], args[2], const_args[2]); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[c], - ARITH_MVN, args[0], 0, 0); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(c)], - ARITH_MOV, args[0], 0, 0); - break; case INDEX_op_brcond2_i32: c = tcg_out_cmp2(s, args, const_args); @@ -2372,10 +2421,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i32: return C_O0_I2(r, r); - case INDEX_op_setcond_i32: - case INDEX_op_negsetcond_i32: - return C_O1_I2(r, r, rIN); - case INDEX_op_brcond_i32: return C_O0_I2(r, rIN); case INDEX_op_deposit_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index d1b37c4388..d3a3f1f7fb 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1679,10 +1679,11 @@ static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, } #endif -static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, - TCGArg dest, TCGArg arg1, TCGArg arg2, - int const_arg2, bool neg) +static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGArg arg2, + bool const_arg2, bool neg) { + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; int cmp_rexw = rexw; bool inv = false; bool cleared; @@ -1757,7 +1758,7 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, case TCG_COND_LT: /* If arg2 is 0, extract the sign bit. */ if (const_arg2 && arg2 == 0) { - tcg_out_mov(s, rexw ? TCG_TYPE_I64 : TCG_TYPE_I32, dest, arg1); + tcg_out_mov(s, type, dest, arg1); if (inv) { tcg_out_modrm(s, OPC_GRP3_Ev + rexw, EXT3_NOT, dest); } @@ -1793,6 +1794,42 @@ static void tcg_out_setcond(TCGContext *s, int rexw, TCGCond cond, } } +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, false, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, true, false); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(q, r, reT), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, false, true); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, true, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(q, r, reT), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + #if TCG_TARGET_REG_BITS == 32 static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, const int *const_args) @@ -3091,12 +3128,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_brcond(s, rexw, a2, a0, a1, const_args[1], arg_label(args[3]), 0); break; - OP_32_64(setcond): - tcg_out_setcond(s, rexw, args[3], a0, a1, a2, const_a2, false); - break; - OP_32_64(negsetcond): - tcg_out_setcond(s, rexw, args[3], a0, a1, a2, const_a2, true); - break; OP_32_64(movcond): tcg_out_movcond(s, rexw, args[5], a0, a1, a2, const_a2, args[3]); break; @@ -3934,12 +3965,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_deposit_i64: return C_O1_I2(q, 0, qi); - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - return C_O1_I2(q, r, reT); - case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: return C_O1_I4(r, r, reT, r, 0); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index e7f97aaa5e..87e8b843f8 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -687,6 +687,42 @@ static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, } } +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, cond, dest, arg1, arg2, false, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, cond, dest, arg1, arg2, true, false); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, cond, dest, arg1, arg2, false, true); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, cond, dest, arg1, arg2, true, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg c1, tcg_target_long c2, bool const2, TCGReg v1, TCGReg v2) @@ -1813,15 +1849,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_revb_d(s, a0, a1); break; - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - tcg_out_setcond(s, args[3], a0, a1, a2, c2, false); - break; - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - tcg_out_setcond(s, args[3], a0, a1, a2, c2, true); - break; - case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: tcg_out_movcond(s, args[5], a0, a1, a2, c2, args[3], args[4]); @@ -2452,12 +2479,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) /* Must deposit into the same register as input */ return C_O1_I2(r, 0, rz); - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - return C_O1_I2(r, r, rJ); - case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: return C_O1_I4(r, rz, rJ, rz, rz); diff --git a/tcg/mips/tcg-target-con-set.h b/tcg/mips/tcg-target-con-set.h index 248bc95d9b..67dfab2aed 100644 --- a/tcg/mips/tcg-target-con-set.h +++ b/tcg/mips/tcg-target-con-set.h @@ -23,8 +23,8 @@ C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) C_O1_I2(r, r, rIK) C_O1_I2(r, r, rJ) +C_O1_I2(r, r, rz) C_O1_I2(r, r, rzW) -C_O1_I2(r, rz, rz) C_O1_I4(r, rz, rz, rz, 0) C_O1_I4(r, rz, rz, rz, rz) C_O2_I1(r, r, r) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 759f152711..51b3ea4bb0 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -952,15 +952,20 @@ static void tcg_out_setcond_end(TCGContext *s, TCGReg ret, int tmpflags) } } -static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg arg1, TCGReg arg2) +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg arg1, TCGReg arg2) { int tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2); tcg_out_setcond_end(s, ret, tmpflags); } -static void tcg_out_negsetcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg arg1, TCGReg arg2) +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rz), + .out_rrr = tgen_setcond, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg arg1, TCGReg arg2) { int tmpflags = tcg_out_setcond_int(s, cond, ret, arg1, arg2); TCGReg tmp = tmpflags & ~SETCOND_FLAGS; @@ -978,6 +983,11 @@ static void tcg_out_negsetcond(TCGContext *s, TCGCond cond, TCGReg ret, } } +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rz), + .out_rrr = tgen_negsetcond, +}; + static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, TCGReg arg2, TCGLabel *l) { @@ -1041,10 +1051,11 @@ static int tcg_out_setcond2_int(TCGContext *s, TCGCond cond, TCGReg ret, break; default: - tcg_out_setcond(s, TCG_COND_EQ, TCG_TMP0, ah, bh); - tcg_out_setcond(s, tcg_unsigned_cond(cond), TCG_TMP1, al, bl); + tgen_setcond(s, TCG_TYPE_I32, TCG_COND_EQ, TCG_TMP0, ah, bh); + tgen_setcond(s, TCG_TYPE_I32, tcg_unsigned_cond(cond), + TCG_TMP1, al, bl); tcg_out_opc_reg(s, OPC_AND, TCG_TMP1, TCG_TMP1, TCG_TMP0); - tcg_out_setcond(s, tcg_high_cond(cond), TCG_TMP0, ah, bh); + tgen_setcond(s, TCG_TYPE_I32, tcg_high_cond(cond), TCG_TMP0, ah, bh); tcg_out_opc_reg(s, OPC_OR, ret, TCG_TMP0, TCG_TMP1); break; } @@ -2285,14 +2296,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_movcond(s, args[5], a0, a1, a2, args[3], args[4]); break; - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - tcg_out_setcond(s, args[3], a0, a1, a2); - break; - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - tcg_out_negsetcond(s, args[3], a0, a1, a2); - break; case INDEX_op_setcond2_i32: tcg_out_setcond2(s, args[5], a0, a1, a2, args[3], args[4]); break; @@ -2385,12 +2388,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - return C_O1_I2(r, rz, rz); - case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: return C_O1_I2(r, 0, rz); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index f2cb45029f..0a66351124 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -1926,8 +1926,8 @@ static TCGReg tcg_gen_setcond_xor(TCGContext *s, TCGReg arg1, TCGArg arg2, } static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, - TCGArg arg0, TCGArg arg1, TCGArg arg2, - int const_arg2, bool neg) + TCGReg arg0, TCGReg arg1, TCGArg arg2, + bool const_arg2, bool neg) { int sh; bool inv; @@ -2072,6 +2072,42 @@ static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, } } +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, false, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, true, false); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rC), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, false, true); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, true, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rC), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + static void tcg_out_bc(TCGContext *s, TCGCond cond, int bd) { tcg_out32(s, tcg_to_bc[cond] | bd); @@ -3465,22 +3501,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; - case INDEX_op_setcond_i32: - tcg_out_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], - const_args[2], false); - break; - case INDEX_op_setcond_i64: - tcg_out_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], args[2], - const_args[2], false); - break; - case INDEX_op_negsetcond_i32: - tcg_out_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], args[2], - const_args[2], true); - break; - case INDEX_op_negsetcond_i64: - tcg_out_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], args[2], - const_args[2], true); - break; case INDEX_op_setcond2_i32: tcg_out_setcond2(s, args, const_args); break; @@ -4276,11 +4296,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: return C_O0_I2(r, rC); - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - return C_O1_I2(r, r, rC); case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: return C_O1_I4(r, r, rC, rZ, rZ); diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 071be449f6..05114b5c5f 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1325,6 +1325,24 @@ static void tcg_out_setcond(TCGContext *s, TCGCond cond, TCGReg ret, } } +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, cond, dest, arg1, arg2, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, cond, dest, arg1, arg2, true); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + static void tcg_out_negsetcond(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg arg1, tcg_target_long arg2, bool c2) { @@ -1363,6 +1381,24 @@ static void tcg_out_negsetcond(TCGContext *s, TCGCond cond, TCGReg ret, } } +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_negsetcond(s, cond, dest, arg1, arg2, false); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_negsetcond(s, cond, dest, arg1, arg2, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rI), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + static void tcg_out_movcond_zicond(TCGContext *s, TCGReg ret, TCGReg test_ne, int val1, bool c_val1, int val2, bool c_val2) @@ -2485,16 +2521,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_brcond(s, a2, a0, a1, arg_label(args[3])); break; - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - tcg_out_setcond(s, args[3], a0, a1, a2, c2); - break; - - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - tcg_out_negsetcond(s, args[3], a0, a1, a2, c2); - break; - case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: tcg_out_movcond(s, args[5], a0, a1, a2, c2, @@ -2837,12 +2863,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - return C_O1_I2(r, r, rI); - case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: return C_O0_I2(rz, rz); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 18b83d5899..3c04b87109 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1370,9 +1370,9 @@ static int tgen_cmp(TCGContext *s, TCGType type, TCGCond c, TCGReg r1, return tgen_cmp2(s, type, c, r1, c2, c2const, need_carry, &inv_cc); } -static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, - TCGReg dest, TCGReg c1, TCGArg c2, - bool c2const, bool neg) +static void tgen_setcond_int(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg c1, TCGArg c2, + bool c2const, bool neg) { int cc; @@ -1464,6 +1464,42 @@ static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, tcg_out_insn(s, RRFc, LOCGR, dest, TCG_TMP0, cc); } +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tgen_setcond_int(s, type, cond, dest, arg1, arg2, false, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tgen_setcond_int(s, type, cond, dest, arg1, arg2, true, false); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rC), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tgen_setcond_int(s, type, cond, dest, arg1, arg2, false, true); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tgen_setcond_int(s, type, cond, dest, arg1, arg2, true, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rC), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + static void tgen_movcond_int(TCGContext *s, TCGType type, TCGReg dest, TCGArg v3, int v3const, TCGReg v4, int cc, int inv_cc) @@ -2825,14 +2861,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tgen_brcond(s, TCG_TYPE_I32, args[2], args[0], args[1], const_args[1], arg_label(args[3])); break; - case INDEX_op_setcond_i32: - tgen_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], - args[2], const_args[2], false); - break; - case INDEX_op_negsetcond_i32: - tgen_setcond(s, TCG_TYPE_I32, args[3], args[0], args[1], - args[2], const_args[2], true); - break; case INDEX_op_movcond_i32: tgen_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], args[2], const_args[2], args[3], const_args[3], args[4]); @@ -2910,14 +2938,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tgen_brcond(s, TCG_TYPE_I64, args[2], args[0], args[1], const_args[1], arg_label(args[3])); break; - case INDEX_op_setcond_i64: - tgen_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], - args[2], const_args[2], false); - break; - case INDEX_op_negsetcond_i64: - tgen_setcond(s, TCG_TYPE_I64, args[3], args[0], args[1], - args[2], const_args[2], true); - break; case INDEX_op_movcond_i64: tgen_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], args[2], const_args[2], args[3], const_args[3], args[4]); @@ -3434,12 +3454,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_setcond_i32: - case INDEX_op_negsetcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i64: - return C_O1_I2(r, r, rC); - case INDEX_op_brcond_i32: return C_O0_I2(r, ri); case INDEX_op_brcond_i64: diff --git a/tcg/sparc64/tcg-target-con-set.h b/tcg/sparc64/tcg-target-con-set.h index 85dcfbc375..ca7bbf0a2f 100644 --- a/tcg/sparc64/tcg-target-con-set.h +++ b/tcg/sparc64/tcg-target-con-set.h @@ -15,7 +15,6 @@ C_O0_I2(rz, rJ) C_O1_I1(r, r) C_O1_I2(r, r, r) C_O1_I2(r, r, rJ) -C_O1_I2(r, rz, rJ) C_O1_I4(r, rz, rJ, rI, 0) C_O2_I2(r, r, r, r) C_O2_I4(r, r, rz, rz, rJ, rJ) diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 41c4e77466..dcbe6a8f47 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -714,7 +714,7 @@ static void tcg_out_movcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, } static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, int32_t c2, int c2const, bool neg) + TCGReg c1, int32_t c2, bool c2const, bool neg) { /* For 32-bit comparisons, we can play games with ADDC/SUBC. */ switch (cond) { @@ -788,7 +788,7 @@ static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, } static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, int32_t c2, int c2const, bool neg) + TCGReg c1, int32_t c2, bool c2const, bool neg) { int rcond; @@ -822,6 +822,53 @@ static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, } } +static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, + TCGArg c2, bool c2const, bool neg) +{ + if (type == TCG_TYPE_I32) { + tcg_out_setcond_i32(s, cond, ret, c1, c2, c2const, neg); + } else { + tcg_out_setcond_i64(s, cond, ret, c1, c2, c2const, neg); + } +} + +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, false, false); +} + +static void tgen_setcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, true, false); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_setcond, + .out_rri = tgen_setcondi, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, false, true); +} + +static void tgen_negsetcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, tcg_target_long arg2) +{ + tcg_out_setcond(s, type, cond, dest, arg1, arg2, true, true); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_negsetcond, + .out_rri = tgen_negsetcondi, +}; + static void tcg_out_addsub2_i32(TCGContext *s, TCGReg rl, TCGReg rh, TCGReg al, TCGReg ah, int32_t bl, int blconst, int32_t bh, int bhconst, int opl, int oph) @@ -1711,12 +1758,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_brcond_i32: tcg_out_brcond_i32(s, a2, a0, a1, const_args[1], arg_label(args[3])); break; - case INDEX_op_setcond_i32: - tcg_out_setcond_i32(s, args[3], a0, a1, a2, c2, false); - break; - case INDEX_op_negsetcond_i32: - tcg_out_setcond_i32(s, args[3], a0, a1, a2, c2, true); - break; case INDEX_op_movcond_i32: tcg_out_movcond_i32(s, args[5], a0, a1, a2, c2, args[3], const_args[3]); break; @@ -1758,12 +1799,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_brcond_i64: tcg_out_brcond_i64(s, a2, a0, a1, const_args[1], arg_label(args[3])); break; - case INDEX_op_setcond_i64: - tcg_out_setcond_i64(s, args[3], a0, a1, a2, c2, false); - break; - case INDEX_op_negsetcond_i64: - tcg_out_setcond_i64(s, args[3], a0, a1, a2, c2, true); - break; case INDEX_op_movcond_i64: tcg_out_movcond_i64(s, args[5], a0, a1, a2, c2, args[3], const_args[3]); break; @@ -1837,12 +1872,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: - return C_O1_I2(r, rz, rJ); - case INDEX_op_brcond_i32: case INDEX_op_brcond_i64: return C_O0_I2(rz, rJ); diff --git a/tcg/tcg.c b/tcg/tcg.c index 38a329b876..90e82e7ed0 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1003,6 +1003,14 @@ typedef struct TCGOutOpUnary { void (*out_rr)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1); } TCGOutOpUnary; +typedef struct TCGOutOpSetcond { + TCGOutOp base; + void (*out_rrr)(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg a1, TCGReg a2); + void (*out_rri)(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg a1, tcg_target_long a2); +} TCGOutOpSetcond; + typedef struct TCGOutOpSubtract { TCGOutOp base; void (*out_rrr)(TCGContext *s, TCGType type, @@ -1047,6 +1055,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_muluh, TCGOutOpBinary, outop_muluh), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), + OUTOP(INDEX_op_negsetcond_i32, TCGOutOpSetcond, outop_negsetcond), + OUTOP(INDEX_op_negsetcond_i64, TCGOutOpSetcond, outop_negsetcond), OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), OUTOP(INDEX_op_not, TCGOutOpUnary, outop_not), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), @@ -1056,6 +1066,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_rotl, TCGOutOpBinary, outop_rotl), OUTOP(INDEX_op_rotr, TCGOutOpBinary, outop_rotr), OUTOP(INDEX_op_sar, TCGOutOpBinary, outop_sar), + OUTOP(INDEX_op_setcond_i32, TCGOutOpSetcond, outop_setcond), + OUTOP(INDEX_op_setcond_i64, TCGOutOpSetcond, outop_setcond), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), @@ -5482,6 +5494,25 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_setcond_i32: + case INDEX_op_setcond_i64: + case INDEX_op_negsetcond_i32: + case INDEX_op_negsetcond_i64: + { + const TCGOutOpSetcond *out = + container_of(all_outop[op->opc], TCGOutOpSetcond, base); + TCGCond cond = new_args[3]; + + tcg_debug_assert(!const_args[1]); + if (const_args[2]) { + out->out_rri(s, type, cond, + new_args[0], new_args[1], new_args[2]); + } else { + out->out_rrr(s, type, cond, + new_args[0], new_args[1], new_args[2]); + } + } + break; default: if (def->flags & TCG_OPF_VECTOR) { diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 2eb323b5c5..1b75aba698 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -77,10 +77,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: return C_O1_I2(r, r, r); @@ -942,6 +938,32 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_setcond_i32 + : INDEX_op_setcond_i64); + tcg_out_op_rrrc(s, opc, dest, arg1, arg2, cond); +} + +static const TCGOutOpSetcond outop_setcond = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_setcond, +}; + +static void tgen_negsetcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg arg1, TCGReg arg2) +{ + tgen_setcond(s, type, cond, dest, arg1, arg2); + tgen_neg(s, type, dest, dest); +} + +static const TCGOutOpSetcond outop_negsetcond = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_negsetcond, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -958,27 +980,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_l(s, opc, arg_label(args[0])); break; - CASE_32_64(setcond) - tcg_out_op_rrrc(s, opc, args[0], args[1], args[2], args[3]); - break; - CASE_32_64(movcond) case INDEX_op_setcond2_i32: tcg_out_op_rrrrrc(s, opc, args[0], args[1], args[2], args[3], args[4], args[5]); break; - case INDEX_op_negsetcond_i32: - tcg_out_op_rrrc(s, INDEX_op_setcond_i32, - args[0], args[1], args[2], args[3]); - tcg_out_op_rr(s, INDEX_op_neg, args[0], args[0]); - break; - case INDEX_op_negsetcond_i64: - tcg_out_op_rrrc(s, INDEX_op_setcond_i64, - args[0], args[1], args[2], args[3]); - tcg_out_op_rr(s, INDEX_op_neg, args[0], args[0]); - break; - CASE_32_64(ld8u) CASE_32_64(ld8s) CASE_32_64(ld16u) @@ -1005,9 +1012,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; CASE_32_64(brcond) - tcg_out_op_rrrc(s, (opc == INDEX_op_brcond_i32 - ? INDEX_op_setcond_i32 : INDEX_op_setcond_i64), - TCG_REG_TMP, args[0], args[1], args[2]); + tgen_setcond(s, type, args[2], TCG_REG_TMP, args[0], args[1]); tcg_out_op_rl(s, opc, TCG_REG_TMP, arg_label(args[3])); break; From a363e1e179445102d7940e92d394d6c00c126f13 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 09:26:44 -0800 Subject: [PATCH 0436/2760] tcg: Merge INDEX_op_{neg}setcond_{i32,i64}` Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 4 ++-- include/tcg/tcg-opc.h | 6 ++---- target/sh4/translate.c | 6 +++--- tcg/optimize.c | 32 ++++++++------------------------ tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 30 ++++++++++-------------------- tcg/tci.c | 14 +++++++------- tcg/tci/tcg-target-opc.h.inc | 1 + tcg/tci/tcg-target.c.inc | 4 ++-- 9 files changed, 39 insertions(+), 66 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 592e002971..d3283265cd 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -499,13 +499,13 @@ Conditional moves .. list-table:: - * - setcond_i32/i64 *dest*, *t1*, *t2*, *cond* + * - setcond *dest*, *t1*, *t2*, *cond* - | *dest* = (*t1* *cond* *t2*) | | Set *dest* to 1 if (*t1* *cond* *t2*) is true, otherwise set to 0. - * - negsetcond_i32/i64 *dest*, *t1*, *t2*, *cond* + * - negsetcond *dest*, *t1*, *t2*, *cond* - | *dest* = -(*t1* *cond* *t2*) | diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 287bdf3473..f40bb5796a 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -57,6 +57,7 @@ DEF(mulu2, 2, 2, 0, TCG_OPF_INT) DEF(muluh, 1, 2, 0, TCG_OPF_INT) DEF(nand, 1, 2, 0, TCG_OPF_INT) DEF(neg, 1, 1, 0, TCG_OPF_INT) +DEF(negsetcond, 1, 2, 1, TCG_OPF_INT) DEF(nor, 1, 2, 0, TCG_OPF_INT) DEF(not, 1, 1, 0, TCG_OPF_INT) DEF(or, 1, 2, 0, TCG_OPF_INT) @@ -66,13 +67,12 @@ DEF(remu, 1, 2, 0, TCG_OPF_INT) DEF(rotl, 1, 2, 0, TCG_OPF_INT) DEF(rotr, 1, 2, 0, TCG_OPF_INT) DEF(sar, 1, 2, 0, TCG_OPF_INT) +DEF(setcond, 1, 2, 1, TCG_OPF_INT) DEF(shl, 1, 2, 0, TCG_OPF_INT) DEF(shr, 1, 2, 0, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) -DEF(setcond_i32, 1, 2, 1, 0) -DEF(negsetcond_i32, 1, 2, 1, 0) DEF(movcond_i32, 1, 4, 1, 0) /* load/store */ DEF(ld8u_i32, 1, 1, 1, 0) @@ -99,8 +99,6 @@ DEF(setcond2_i32, 1, 4, 1, 0) DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) -DEF(setcond_i64, 1, 2, 1, 0) -DEF(negsetcond_i64, 1, 2, 1, 0) DEF(movcond_i64, 1, 4, 1, 0) /* load/store */ DEF(ld8u_i64, 1, 1, 1, 0) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 8248648c0c..712a57fb54 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -1995,7 +1995,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) if ((ld_dst == B11_8) + (ld_dst == B7_4) != 1 || mv_src >= 0) { goto fail; } - op_opc = INDEX_op_setcond_i32; /* placeholder */ + op_opc = INDEX_op_setcond; /* placeholder */ op_src = (ld_dst == B11_8 ? B7_4 : B11_8); op_arg = REG(op_src); @@ -2030,7 +2030,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) if (ld_dst != B11_8 || ld_dst != B7_4 || mv_src >= 0) { goto fail; } - op_opc = INDEX_op_setcond_i32; + op_opc = INDEX_op_setcond; op_arg = tcg_constant_i32(0); NEXT_INSN; @@ -2147,7 +2147,7 @@ static void decode_gusa(DisasContext *ctx, CPUSH4State *env) } break; - case INDEX_op_setcond_i32: + case INDEX_op_setcond: if (st_src == ld_dst) { goto fail; } diff --git a/tcg/optimize.c b/tcg/optimize.c index 0affde323b..e53dbd4290 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1996,35 +1996,19 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) if (ti_is_const(tt) && ti_is_const(ft)) { uint64_t tv = ti_const_val(tt); uint64_t fv = ti_const_val(ft); - TCGOpcode opc, negopc; TCGCond cond = op->args[5]; - switch (ctx->type) { - case TCG_TYPE_I32: - opc = INDEX_op_setcond_i32; - negopc = INDEX_op_negsetcond_i32; - tv = (int32_t)tv; - fv = (int32_t)fv; - break; - case TCG_TYPE_I64: - opc = INDEX_op_setcond_i64; - negopc = INDEX_op_negsetcond_i64; - break; - default: - g_assert_not_reached(); - } - if (tv == 1 && fv == 0) { - op->opc = opc; + op->opc = INDEX_op_setcond; op->args[3] = cond; } else if (fv == 1 && tv == 0) { - op->opc = opc; + op->opc = INDEX_op_setcond; op->args[3] = tcg_invert_cond(cond); } else if (tv == -1 && fv == 0) { - op->opc = negopc; + op->opc = INDEX_op_negsetcond; op->args[3] = cond; } else if (fv == -1 && tv == 0) { - op->opc = negopc; + op->opc = INDEX_op_negsetcond; op->args[3] = tcg_invert_cond(cond); } } @@ -2526,14 +2510,14 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) do_setcond_low: op->args[2] = op->args[3]; op->args[3] = cond; - op->opc = INDEX_op_setcond_i32; + op->opc = INDEX_op_setcond; return fold_setcond(ctx, op); do_setcond_high: op->args[1] = op->args[2]; op->args[2] = op->args[4]; op->args[3] = cond; - op->opc = INDEX_op_setcond_i32; + op->opc = INDEX_op_setcond; return fold_setcond(ctx, op); } @@ -3025,10 +3009,10 @@ void tcg_optimize(TCGContext *s) case INDEX_op_shr: done = fold_shift(&ctx, op); break; - CASE_OP_32_64(setcond): + case INDEX_op_setcond: done = fold_setcond(&ctx, op); break; - CASE_OP_32_64(negsetcond): + case INDEX_op_negsetcond: done = fold_negsetcond(&ctx, op); break; case INDEX_op_setcond2_i32: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 413b68352d..477dfc25b7 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -552,7 +552,7 @@ void tcg_gen_setcond_i32(TCGCond cond, TCGv_i32 ret, } else if (cond == TCG_COND_NEVER) { tcg_gen_movi_i32(ret, 0); } else { - tcg_gen_op4i_i32(INDEX_op_setcond_i32, ret, arg1, arg2, cond); + tcg_gen_op4i_i32(INDEX_op_setcond, ret, arg1, arg2, cond); } } @@ -570,7 +570,7 @@ void tcg_gen_negsetcond_i32(TCGCond cond, TCGv_i32 ret, } else if (cond == TCG_COND_NEVER) { tcg_gen_movi_i32(ret, 0); } else { - tcg_gen_op4i_i32(INDEX_op_negsetcond_i32, ret, arg1, arg2, cond); + tcg_gen_op4i_i32(INDEX_op_negsetcond, ret, arg1, arg2, cond); } } @@ -1911,7 +1911,7 @@ void tcg_gen_setcond_i64(TCGCond cond, TCGv_i64 ret, TCGV_LOW(arg2), TCGV_HIGH(arg2), cond); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); } else { - tcg_gen_op4i_i64(INDEX_op_setcond_i64, ret, arg1, arg2, cond); + tcg_gen_op4i_i64(INDEX_op_setcond, ret, arg1, arg2, cond); } } } @@ -1948,7 +1948,7 @@ void tcg_gen_negsetcond_i64(TCGCond cond, TCGv_i64 ret, } else if (cond == TCG_COND_NEVER) { tcg_gen_movi_i64(ret, 0); } else if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op4i_i64(INDEX_op_negsetcond_i64, ret, arg1, arg2, cond); + tcg_gen_op4i_i64(INDEX_op_negsetcond, ret, arg1, arg2, cond); } else { tcg_gen_op6i_i32(INDEX_op_setcond2_i32, TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_HIGH(arg1), diff --git a/tcg/tcg.c b/tcg/tcg.c index 90e82e7ed0..49fbf1f561 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1055,8 +1055,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_muluh, TCGOutOpBinary, outop_muluh), OUTOP(INDEX_op_nand, TCGOutOpBinary, outop_nand), OUTOP(INDEX_op_neg, TCGOutOpUnary, outop_neg), - OUTOP(INDEX_op_negsetcond_i32, TCGOutOpSetcond, outop_negsetcond), - OUTOP(INDEX_op_negsetcond_i64, TCGOutOpSetcond, outop_negsetcond), + OUTOP(INDEX_op_negsetcond, TCGOutOpSetcond, outop_negsetcond), OUTOP(INDEX_op_nor, TCGOutOpBinary, outop_nor), OUTOP(INDEX_op_not, TCGOutOpUnary, outop_not), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), @@ -1066,8 +1065,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_rotl, TCGOutOpBinary, outop_rotl), OUTOP(INDEX_op_rotr, TCGOutOpBinary, outop_rotr), OUTOP(INDEX_op_sar, TCGOutOpBinary, outop_sar), - OUTOP(INDEX_op_setcond_i32, TCGOutOpSetcond, outop_setcond), - OUTOP(INDEX_op_setcond_i64, TCGOutOpSetcond, outop_setcond), + OUTOP(INDEX_op_setcond, TCGOutOpSetcond, outop_setcond), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), @@ -2275,12 +2273,12 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_add: case INDEX_op_and: case INDEX_op_mov: + case INDEX_op_negsetcond: case INDEX_op_or: + case INDEX_op_setcond: case INDEX_op_xor: return has_type; - case INDEX_op_setcond_i32: - case INDEX_op_negsetcond_i32: case INDEX_op_brcond_i32: case INDEX_op_movcond_i32: case INDEX_op_ld8u_i32: @@ -2311,8 +2309,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond2_i32: return TCG_TARGET_REG_BITS == 32; - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i64: case INDEX_op_brcond_i64: case INDEX_op_movcond_i64: case INDEX_op_ld8u_i64: @@ -2864,14 +2860,12 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) } switch (c) { case INDEX_op_brcond_i32: - case INDEX_op_setcond_i32: - case INDEX_op_negsetcond_i32: + case INDEX_op_setcond: + case INDEX_op_negsetcond: case INDEX_op_movcond_i32: case INDEX_op_brcond2_i32: case INDEX_op_setcond2_i32: case INDEX_op_brcond_i64: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i64: case INDEX_op_movcond_i64: case INDEX_op_cmp_vec: case INDEX_op_cmpsel_vec: @@ -5068,10 +5062,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_brcond_i64: op_cond = op->args[2]; break; - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: + case INDEX_op_setcond: + case INDEX_op_negsetcond: case INDEX_op_cmp_vec: op_cond = op->args[3]; break; @@ -5494,10 +5486,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: - case INDEX_op_negsetcond_i32: - case INDEX_op_negsetcond_i64: + case INDEX_op_setcond: + case INDEX_op_negsetcond: { const TCGOutOpSetcond *out = container_of(all_outop[op->opc], TCGOutOpSetcond, base); diff --git a/tcg/tci.c b/tcg/tci.c index 569b5c7ed0..d97ca1fade 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -438,10 +438,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_l(insn, tb_ptr, &ptr); tb_ptr = ptr; continue; - case INDEX_op_setcond_i32: - tci_args_rrrc(insn, &r0, &r1, &r2, &condition); - regs[r0] = tci_compare32(regs[r1], regs[r2], condition); - break; case INDEX_op_movcond_i32: tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition); tmp32 = tci_compare32(regs[r1], regs[r2], condition); @@ -455,7 +451,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = tci_compare64(T1, T2, condition); break; #elif TCG_TARGET_REG_BITS == 64 - case INDEX_op_setcond_i64: + case INDEX_op_setcond: tci_args_rrrc(insn, &r0, &r1, &r2, &condition); regs[r0] = tci_compare64(regs[r1], regs[r2], condition); break; @@ -628,6 +624,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tmp32 = regs[r1]; regs[r0] = tmp32 ? ctz32(tmp32) : regs[r2]; break; + case INDEX_op_tci_setcond32: + tci_args_rrrc(insn, &r0, &r1, &r2, &condition); + regs[r0] = tci_compare32(regs[r1], regs[r2], condition); + break; /* Shift/rotate operations. */ @@ -971,8 +971,8 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) op_name, str_r(r0), ptr); break; - case INDEX_op_setcond_i32: - case INDEX_op_setcond_i64: + case INDEX_op_setcond: + case INDEX_op_tci_setcond32: tci_args_rrrc(insn, &r0, &r1, &r2, &c); info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s", op_name, str_r(r0), str_r(r1), str_r(r2), str_c(c)); diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc index 2bb346f4c8..27b4574e4f 100644 --- a/tcg/tci/tcg-target-opc.h.inc +++ b/tcg/tci/tcg-target-opc.h.inc @@ -10,3 +10,4 @@ DEF(tci_rems32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_remu32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_rotl32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_rotr32, 1, 2, 0, TCG_OPF_NOT_PRESENT) +DEF(tci_setcond32, 1, 2, 1, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 1b75aba698..d49c767de5 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -942,8 +942,8 @@ static void tgen_setcond(TCGContext *s, TCGType type, TCGCond cond, TCGReg dest, TCGReg arg1, TCGReg arg2) { TCGOpcode opc = (type == TCG_TYPE_I32 - ? INDEX_op_setcond_i32 - : INDEX_op_setcond_i64); + ? INDEX_op_tci_setcond32 + : INDEX_op_setcond); tcg_out_op_rrrc(s, opc, dest, arg1, arg2, cond); } From 99ac4706b35a2c16e2bf4437e966fc39c41ed67d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 11:40:06 -0800 Subject: [PATCH 0437/2760] tcg: Convert brcond to TCGOutOpBrcond Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 39 +++++++++++++++------------- tcg/arm/tcg-target.c.inc | 27 ++++++++++++++----- tcg/i386/tcg-target.c.inc | 28 ++++++++++++++------ tcg/loongarch64/tcg-target-con-set.h | 2 +- tcg/loongarch64/tcg-target.c.inc | 18 +++++-------- tcg/mips/tcg-target-con-set.h | 4 +-- tcg/mips/tcg-target.c.inc | 20 +++++++------- tcg/ppc/tcg-target.c.inc | 31 +++++++++++----------- tcg/riscv/tcg-target-con-set.h | 2 +- tcg/riscv/tcg-target.c.inc | 18 +++++-------- tcg/s390x/tcg-target.c.inc | 31 ++++++++++++---------- tcg/sparc64/tcg-target-con-set.h | 2 +- tcg/sparc64/tcg-target.c.inc | 38 ++++++++++++++++++++------- tcg/tcg.c | 26 +++++++++++++++++++ tcg/tci.c | 9 ++----- tcg/tci/tcg-target.c.inc | 20 +++++++------- 16 files changed, 190 insertions(+), 125 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 2524e73ff4..e3d8e9090f 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1424,8 +1424,16 @@ static inline void tcg_out_goto_label(TCGContext *s, TCGLabel *l) } } -static void tcg_out_brcond(TCGContext *s, TCGType ext, TCGCond c, TCGArg a, - TCGArg b, bool b_const, TCGLabel *l) +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, + TCGReg a, TCGReg b, TCGLabel *l) +{ + tgen_cmp(s, type, c, a, b); + tcg_out_reloc(s, s->code_ptr, R_AARCH64_CONDBR19, l, 0); + tcg_out_insn(s, 3202, B_C, c, 0); +} + +static void tgen_brcondi(TCGContext *s, TCGType ext, TCGCond c, + TCGReg a, tcg_target_long b, TCGLabel *l) { int tbit = -1; bool need_cmp = true; @@ -1434,14 +1442,14 @@ static void tcg_out_brcond(TCGContext *s, TCGType ext, TCGCond c, TCGArg a, case TCG_COND_EQ: case TCG_COND_NE: /* cmp xN,0; b.ne L -> cbnz xN,L */ - if (b_const && b == 0) { + if (b == 0) { need_cmp = false; } break; case TCG_COND_LT: case TCG_COND_GE: /* cmp xN,0; b.mi L -> tbnz xN,63,L */ - if (b_const && b == 0) { + if (b == 0) { c = (c == TCG_COND_LT ? TCG_COND_TSTNE : TCG_COND_TSTEQ); tbit = ext ? 63 : 31; need_cmp = false; @@ -1450,14 +1458,14 @@ static void tcg_out_brcond(TCGContext *s, TCGType ext, TCGCond c, TCGArg a, case TCG_COND_TSTEQ: case TCG_COND_TSTNE: /* tst xN,0xffffffff; b.ne L -> cbnz wN,L */ - if (b_const && b == UINT32_MAX) { + if (b == UINT32_MAX) { c = tcg_tst_eqne_cond(c); ext = TCG_TYPE_I32; need_cmp = false; break; } /* tst xN,1< tbnz xN,B,L */ - if (b_const && is_power_of_2(b)) { + if (is_power_of_2(b)) { tbit = ctz64(b); need_cmp = false; } @@ -1467,7 +1475,7 @@ static void tcg_out_brcond(TCGContext *s, TCGType ext, TCGCond c, TCGArg a, } if (need_cmp) { - tcg_out_cmp(s, ext, c, a, b, b_const); + tgen_cmpi(s, ext, c, a, b); tcg_out_reloc(s, s->code_ptr, R_AARCH64_CONDBR19, l, 0); tcg_out_insn(s, 3202, B_C, c, 0); return; @@ -1500,6 +1508,12 @@ static void tcg_out_brcond(TCGContext *s, TCGType ext, TCGCond c, TCGArg a, } } +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rC), + .out_rr = tgen_brcond, + .out_ri = tgen_brcondi, +}; + static inline void tcg_out_rev(TCGContext *s, int ext, MemOp s_bits, TCGReg rd, TCGReg rn) { @@ -2565,13 +2579,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_brcond_i32: - a1 = (int32_t)a1; - /* FALLTHRU */ - case INDEX_op_brcond_i64: - tcg_out_brcond(s, ext, a2, a0, a1, const_args[1], arg_label(args[3])); - break; - case INDEX_op_movcond_i32: a2 = (int32_t)a2; /* FALLTHRU */ @@ -3159,10 +3166,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(r, rC); - case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: return C_O1_I4(r, r, rC, rz, rz); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 0f2a029f6d..4c7537cbeb 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2181,6 +2181,26 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, TCGReg a1, TCGLabel *l) +{ + cond = tgen_cmp(s, cond, a0, a1); + tcg_out_goto_label(s, tcg_cond_to_arm_cond[cond], l); +} + +static void tgen_brcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a0, tcg_target_long a1, TCGLabel *l) +{ + cond = tgen_cmpi(s, cond, a0, a1); + tcg_out_goto_label(s, tcg_cond_to_arm_cond[cond], l); +} + +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rIN), + .out_rr = tgen_brcond, + .out_ri = tgen_brcondi, +}; + static void finish_setcond(TCGContext *s, TCGCond cond, TCGReg ret, bool neg) { tcg_out_movi32(s, tcg_cond_to_arm_cond[tcg_invert_cond(cond)], ret, 0); @@ -2317,11 +2337,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mov_reg(s, COND_AL, args[0], a0); break; - case INDEX_op_brcond_i32: - c = tcg_out_cmp(s, args[2], args[0], args[1], const_args[1]); - tcg_out_goto_label(s, tcg_cond_to_arm_cond[c], arg_label(args[3])); - break; - case INDEX_op_brcond2_i32: c = tcg_out_cmp2(s, args, const_args); tcg_out_goto_label(s, tcg_cond_to_arm_cond[c], arg_label(args[5])); @@ -2421,8 +2436,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i32: return C_O0_I2(r, r); - case INDEX_op_brcond_i32: - return C_O0_I2(r, rIN); case INDEX_op_deposit_i32: return C_O1_I2(r, 0, rZ); case INDEX_op_extract2_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index d3a3f1f7fb..d2eff3b617 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1642,6 +1642,26 @@ static void tcg_out_brcond(TCGContext *s, int rexw, TCGCond cond, tcg_out_jxx(s, jcc, label, small); } +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGReg arg2, TCGLabel *label) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_brcond(s, rexw, cond, arg1, arg2, false, label, false); +} + +static void tgen_brcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, tcg_target_long arg2, TCGLabel *label) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_brcond(s, rexw, cond, arg1, arg2, true, label, false); +} + +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, reT), + .out_rr = tgen_brcond, + .out_ri = tgen_brcondi, +}; + #if TCG_TARGET_REG_BITS == 32 static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, const int *const_args, bool small) @@ -3124,10 +3144,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(brcond): - tcg_out_brcond(s, rexw, a2, a0, a1, const_args[1], - arg_label(args[3]), 0); - break; OP_32_64(movcond): tcg_out_movcond(s, rexw, args[5], a0, a1, a2, const_a2, args[3]); break; @@ -3936,10 +3952,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(r, reT); - case INDEX_op_bswap16_i32: case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i32: diff --git a/tcg/loongarch64/tcg-target-con-set.h b/tcg/loongarch64/tcg-target-con-set.h index c145d4ab66..dfe55c6fe8 100644 --- a/tcg/loongarch64/tcg-target-con-set.h +++ b/tcg/loongarch64/tcg-target-con-set.h @@ -16,7 +16,7 @@ */ C_O0_I1(r) C_O0_I2(rz, r) -C_O0_I2(rz, rz) +C_O0_I2(r, rz) C_O0_I2(w, r) C_O0_I3(r, r, r) C_O1_I1(r, r) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 87e8b843f8..53bba07c49 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -767,8 +767,8 @@ static const struct { [TCG_COND_GTU] = { OPC_BGTU, false } }; -static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, - TCGReg arg2, TCGLabel *l) +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGReg arg2, TCGLabel *l) { LoongArchInsn op = tcg_brcond_to_loongarch[cond].op; @@ -785,6 +785,11 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, tcg_out32(s, encode_djsk16_insn(op, arg1, arg2, 0)); } +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rz), + .out_rr = tgen_brcond, +}; + static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) { TCGReg link = tail ? TCG_REG_ZERO : TCG_REG_RA; @@ -1771,11 +1776,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_b(s, 0); break; - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - tcg_out_brcond(s, a2, a0, a1, arg_label(args[3])); - break; - case INDEX_op_extrh_i64_i32: tcg_out_opc_srai_d(s, a0, a1, 32); break; @@ -2441,10 +2441,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i128: return C_O0_I3(r, r, r); - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(rz, rz); - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: diff --git a/tcg/mips/tcg-target-con-set.h b/tcg/mips/tcg-target-con-set.h index 67dfab2aed..a80630a8b4 100644 --- a/tcg/mips/tcg-target-con-set.h +++ b/tcg/mips/tcg-target-con-set.h @@ -10,12 +10,10 @@ * tcg-target-con-str.h; the constraint combination is inclusive or. */ C_O0_I1(r) +C_O0_I2(r, rz) C_O0_I2(rz, r) -C_O0_I2(rz, rz) -C_O0_I3(rz, r, r) C_O0_I3(rz, rz, r) C_O0_I4(rz, rz, rz, rz) -C_O0_I4(rz, rz, r, r) C_O1_I1(r, r) C_O1_I2(r, 0, rz) C_O1_I2(r, r, r) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 51b3ea4bb0..a942905dc4 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -988,8 +988,8 @@ static const TCGOutOpSetcond outop_negsetcond = { .out_rrr = tgen_negsetcond, }; -static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, - TCGReg arg2, TCGLabel *l) +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGReg arg2, TCGLabel *l) { static const MIPSInsn b_zero[16] = { [TCG_COND_LT] = OPC_BLTZ, @@ -1034,6 +1034,11 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, tcg_out_nop(s); } +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rz), + .out_rr = tgen_brcond, +}; + static int tcg_out_setcond2_int(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) { @@ -2178,8 +2183,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; case INDEX_op_br: - tcg_out_brcond(s, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, - arg_label(a0)); + tgen_brcond(s, TCG_TYPE_I32, TCG_COND_EQ, + TCG_REG_ZERO, TCG_REG_ZERO, arg_label(a0)); break; case INDEX_op_ld8u_i32: @@ -2283,10 +2288,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - tcg_out_brcond(s, a2, a0, a1, arg_label(args[3])); - break; case INDEX_op_brcond2_i32: tcg_out_brcond2(s, args[4], a0, a1, a2, args[3], arg_label(args[5])); break; @@ -2391,9 +2392,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: return C_O1_I2(r, 0, rz); - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(rz, rz); case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: return (use_mips32r6_instructions diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 0a66351124..819abdc906 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2124,14 +2124,26 @@ static void tcg_out_bc_lab(TCGContext *s, TCGCond cond, TCGLabel *l) tcg_out_bc(s, cond, bd); } -static void tcg_out_brcond(TCGContext *s, TCGCond cond, - TCGArg arg1, TCGArg arg2, int const_arg2, - TCGLabel *l, TCGType type) +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGReg arg2, TCGLabel *l) { - tcg_out_cmp(s, cond, arg1, arg2, const_arg2, 0, type); + tcg_out_cmp(s, cond, arg1, arg2, false, 0, type); tcg_out_bc_lab(s, cond, l); } +static void tgen_brcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, tcg_target_long arg2, TCGLabel *l) +{ + tcg_out_cmp(s, cond, arg1, arg2, true, 0, type); + tcg_out_bc_lab(s, cond, l); +} + +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rC), + .out_rr = tgen_brcond, + .out_ri = tgen_brcondi, +}; + static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, TCGArg dest, TCGArg c1, TCGArg c2, TCGArg v1, TCGArg v2, bool const_c2) @@ -3457,14 +3469,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); break; - case INDEX_op_brcond_i32: - tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], - arg_label(args[3]), TCG_TYPE_I32); - break; - case INDEX_op_brcond_i64: - tcg_out_brcond(s, args[2], args[0], args[1], const_args[1], - arg_label(args[3]), TCG_TYPE_I64); - break; case INDEX_op_brcond2_i32: tcg_out_brcond2(s, args, const_args); break; @@ -4293,9 +4297,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(r, rC); case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: return C_O1_I4(r, r, rC, rZ, rZ); diff --git a/tcg/riscv/tcg-target-con-set.h b/tcg/riscv/tcg-target-con-set.h index f0d3cb81bd..5ff2c2db60 100644 --- a/tcg/riscv/tcg-target-con-set.h +++ b/tcg/riscv/tcg-target-con-set.h @@ -11,7 +11,7 @@ */ C_O0_I1(r) C_O0_I2(rz, r) -C_O0_I2(rz, rz) +C_O0_I2(r, rz) C_O1_I1(r, r) C_O1_I2(r, r, r) C_O1_I2(r, r, ri) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 05114b5c5f..1d7194e883 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1184,8 +1184,8 @@ static const struct { [TCG_COND_GTU] = { OPC_BLTU, true } }; -static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, - TCGReg arg2, TCGLabel *l) +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGReg arg2, TCGLabel *l) { RISCVInsn op = tcg_brcond_to_riscv[cond].op; @@ -1201,6 +1201,11 @@ static void tcg_out_brcond(TCGContext *s, TCGCond cond, TCGReg arg1, tcg_out_opc_branch(s, op, arg1, arg2, 0); } +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rz), + .out_rr = tgen_brcond, +}; + #define SETCOND_INV TCG_TARGET_NB_REGS #define SETCOND_NEZ (SETCOND_INV << 1) #define SETCOND_FLAGS (SETCOND_INV | SETCOND_NEZ) @@ -2516,11 +2521,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const_args[4], const_args[5], true, false); break; - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - tcg_out_brcond(s, a2, a0, a1, arg_label(args[3])); - break; - case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: tcg_out_movcond(s, args[5], a0, a1, a2, c2, @@ -2863,10 +2863,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(rz, rz); - case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: return C_O1_I4(r, r, rI, rM, rM); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 3c04b87109..d3650636aa 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1693,6 +1693,24 @@ static void tgen_brcond(TCGContext *s, TCGType type, TCGCond c, tgen_branch(s, cc, l); } +static void tgen_brcondr(TCGContext *s, TCGType type, TCGCond c, + TCGReg a0, TCGReg a1, TCGLabel *l) +{ + tgen_brcond(s, type, c, a0, a1, false, l); +} + +static void tgen_brcondi(TCGContext *s, TCGType type, TCGCond c, + TCGReg a0, tcg_target_long a1, TCGLabel *l) +{ + tgen_brcond(s, type, c, a0, a1, true, l); +} + +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rC), + .out_rr = tgen_brcondr, + .out_ri = tgen_brcondi, +}; + static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *dest) { ptrdiff_t off = tcg_pcrel_diff(s, dest) >> 1; @@ -2857,10 +2875,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tgen_branch(s, S390_CC_ALWAYS, arg_label(args[0])); break; - case INDEX_op_brcond_i32: - tgen_brcond(s, TCG_TYPE_I32, args[2], args[0], - args[1], const_args[1], arg_label(args[3])); - break; case INDEX_op_movcond_i32: tgen_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], args[2], const_args[2], args[3], const_args[3], args[4]); @@ -2934,10 +2948,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_insn(s, RRE, SLBGR, args[1], args[5]); break; - case INDEX_op_brcond_i64: - tgen_brcond(s, TCG_TYPE_I64, args[2], args[0], - args[1], const_args[1], arg_label(args[3])); - break; case INDEX_op_movcond_i64: tgen_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], args[2], const_args[2], args[3], const_args[3], args[4]); @@ -3454,11 +3464,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_brcond_i32: - return C_O0_I2(r, ri); - case INDEX_op_brcond_i64: - return C_O0_I2(r, rC); - case INDEX_op_bswap16_i32: case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i32: diff --git a/tcg/sparc64/tcg-target-con-set.h b/tcg/sparc64/tcg-target-con-set.h index ca7bbf0a2f..9f66e52ec6 100644 --- a/tcg/sparc64/tcg-target-con-set.h +++ b/tcg/sparc64/tcg-target-con-set.h @@ -11,7 +11,7 @@ */ C_O0_I1(r) C_O0_I2(rz, r) -C_O0_I2(rz, rJ) +C_O0_I2(r, rJ) C_O1_I1(r, r) C_O1_I2(r, r, r) C_O1_I2(r, r, rJ) diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index dcbe6a8f47..68f38b7d71 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -822,6 +822,35 @@ static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, } } +static void tcg_out_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGArg arg2, bool const_arg2, + TCGLabel *l) +{ + if (type == TCG_TYPE_I32) { + tcg_out_brcond_i32(s, cond, arg1, arg2, const_arg2, l); + } else { + tcg_out_brcond_i64(s, cond, arg1, arg2, const_arg2, l); + } +} + +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, TCGReg arg2, TCGLabel *l) +{ + tcg_out_brcond(s, type, cond, arg1, arg2, false, l); +} + +static void tgen_brcondi(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg1, tcg_target_long arg2, TCGLabel *l) +{ + tcg_out_brcond(s, type, cond, arg1, arg2, true, l); +} + +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, rJ), + .out_rr = tgen_brcond, + .out_ri = tgen_brcondi, +}; + static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, TCGReg ret, TCGReg c1, TCGArg c2, bool c2const, bool neg) @@ -1755,9 +1784,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, a0, a1, a2, STW); break; - case INDEX_op_brcond_i32: - tcg_out_brcond_i32(s, a2, a0, a1, const_args[1], arg_label(args[3])); - break; case INDEX_op_movcond_i32: tcg_out_movcond_i32(s, args[5], a0, a1, a2, c2, args[3], const_args[3]); break; @@ -1796,9 +1822,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, a0, a1, a2, STX); break; - case INDEX_op_brcond_i64: - tcg_out_brcond_i64(s, a2, a0, a1, const_args[1], arg_label(args[3])); - break; case INDEX_op_movcond_i64: tcg_out_movcond_i64(s, args[5], a0, a1, a2, c2, args[3], const_args[3]); break; @@ -1872,9 +1895,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(rz, rJ); case INDEX_op_movcond_i32: case INDEX_op_movcond_i64: return C_O1_I4(r, rz, rJ, rI, 0); diff --git a/tcg/tcg.c b/tcg/tcg.c index 49fbf1f561..dbaa574cb5 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -986,6 +986,14 @@ typedef struct TCGOutOpBinary { TCGReg a0, TCGReg a1, tcg_target_long a2); } TCGOutOpBinary; +typedef struct TCGOutOpBrcond { + TCGOutOp base; + void (*out_rr)(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a1, TCGReg a2, TCGLabel *label); + void (*out_ri)(TCGContext *s, TCGType type, TCGCond cond, + TCGReg a1, tcg_target_long a2, TCGLabel *label); +} TCGOutOpBrcond; + typedef struct TCGOutOpDivRem { TCGOutOp base; void (*out_rr01r)(TCGContext *s, TCGType type, @@ -1040,6 +1048,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), + OUTOP(INDEX_op_brcond_i32, TCGOutOpBrcond, outop_brcond), + OUTOP(INDEX_op_brcond_i64, TCGOutOpBrcond, outop_brcond), OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), OUTOP(INDEX_op_ctpop, TCGOutOpUnary, outop_ctpop), OUTOP(INDEX_op_ctz, TCGOutOpBinary, outop_ctz), @@ -5486,6 +5496,22 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_brcond_i32: + case INDEX_op_brcond_i64: + { + const TCGOutOpBrcond *out = &outop_brcond; + TCGCond cond = new_args[2]; + TCGLabel *label = arg_label(new_args[3]); + + tcg_debug_assert(!const_args[0]); + if (const_args[1]) { + out->out_ri(s, type, cond, new_args[0], new_args[1], label); + } else { + out->out_rr(s, type, cond, new_args[0], new_args[1], label); + } + } + break; + case INDEX_op_setcond: case INDEX_op_negsetcond: { diff --git a/tcg/tci.c b/tcg/tci.c index d97ca1fade..d431cad6fd 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -665,8 +665,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = sextract32(regs[r1], pos, len); break; case INDEX_op_brcond_i32: + case INDEX_op_brcond_i64: tci_args_rl(insn, tb_ptr, &r0, &ptr); - if ((uint32_t)regs[r0]) { + if (regs[r0]) { tb_ptr = ptr; } break; @@ -784,12 +785,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrbb(insn, &r0, &r1, &pos, &len); regs[r0] = sextract64(regs[r1], pos, len); break; - case INDEX_op_brcond_i64: - tci_args_rl(insn, tb_ptr, &r0, &ptr); - if (regs[r0]) { - tb_ptr = ptr; - } - break; case INDEX_op_ext_i32_i64: tci_args_rr(insn, &r0, &r1); regs[r0] = (int32_t)regs[r1]; diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index d49c767de5..2c7fb5d75f 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -81,10 +81,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_deposit_i64: return C_O1_I2(r, r, r); - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: - return C_O0_I2(r, r); - case INDEX_op_add2_i32: case INDEX_op_add2_i64: case INDEX_op_sub2_i32: @@ -964,6 +960,17 @@ static const TCGOutOpSetcond outop_negsetcond = { .out_rrr = tgen_negsetcond, }; +static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg arg0, TCGReg arg1, TCGLabel *l) +{ + tgen_setcond(s, type, cond, TCG_REG_TMP, arg0, arg1); + tcg_out_op_rl(s, INDEX_op_brcond_i32, TCG_REG_TMP, l); +} + +static const TCGOutOpBrcond outop_brcond = { + .base.static_constraint = C_O0_I2(r, r), + .out_rr = tgen_brcond, +}; static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1011,11 +1018,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_rrbb(s, opc, args[0], args[1], args[2], args[3]); break; - CASE_32_64(brcond) - tgen_setcond(s, type, args[2], TCG_REG_TMP, args[0], args[1]); - tcg_out_op_rl(s, opc, TCG_REG_TMP, arg_label(args[3])); - break; - case INDEX_op_bswap32_i32: /* Optional (TCG_TARGET_HAS_bswap32_i32). */ case INDEX_op_bswap64_i64: /* Optional (TCG_TARGET_HAS_bswap64_i64). */ tcg_out_op_rr(s, opc, args[0], args[1]); From b6d69fcefbd45ca33b896abfbc8e27e0f713bdf0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 11:49:22 -0800 Subject: [PATCH 0438/2760] tcg: Merge INDEX_op_brcond_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 4 +--- tcg/optimize.c | 6 +++--- tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 24 ++++++++---------------- tcg/tci.c | 6 ++---- tcg/tci/tcg-target.c.inc | 4 ++-- 7 files changed, 19 insertions(+), 31 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index d3283265cd..18f02c5122 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -239,7 +239,7 @@ Jumps/Labels - | Jump to label. - * - brcond_i32/i64 *t0*, *t1*, *cond*, *label* + * - brcond *t0*, *t1*, *cond*, *label* - | Conditional jump if *t0* *cond* *t1* is true. *cond* can be: | diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index f40bb5796a..d40ca001c2 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -34,6 +34,7 @@ DEF(set_label, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) DEF(call, 0, 0, 3, TCG_OPF_CALL_CLOBBER | TCG_OPF_NOT_PRESENT) DEF(br, 0, 0, 1, TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) +DEF(brcond, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH | TCG_OPF_INT) DEF(mb, 0, 0, 1, TCG_OPF_NOT_PRESENT) @@ -89,8 +90,6 @@ DEF(extract_i32, 1, 1, 2, 0) DEF(sextract_i32, 1, 1, 2, 0) DEF(extract2_i32, 1, 2, 1, 0) -DEF(brcond_i32, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) - DEF(add2_i32, 2, 4, 0, 0) DEF(sub2_i32, 2, 4, 0, 0) DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) @@ -124,7 +123,6 @@ DEF(extu_i32_i64, 1, 1, 0, 0) DEF(extrl_i64_i32, 1, 1, 0, 0) DEF(extrh_i64_i32, 1, 1, 0, 0) -DEF(brcond_i64, 0, 2, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(bswap16_i64, 1, 1, 1, 0) DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index e53dbd4290..d0cb4588ed 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1529,14 +1529,14 @@ static bool fold_brcond2(OptContext *ctx, TCGOp *op) break; do_brcond_low: - op->opc = INDEX_op_brcond_i32; + op->opc = INDEX_op_brcond; op->args[1] = op->args[2]; op->args[2] = cond; op->args[3] = label; return fold_brcond(ctx, op); do_brcond_high: - op->opc = INDEX_op_brcond_i32; + op->opc = INDEX_op_brcond; op->args[0] = op->args[1]; op->args[1] = op->args[3]; op->args[2] = cond; @@ -2864,7 +2864,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_andc_vec: done = fold_andc(&ctx, op); break; - CASE_OP_32_64(brcond): + case INDEX_op_brcond: done = fold_brcond(&ctx, op); break; case INDEX_op_brcond2_i32: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 477dfc25b7..041ca95f0d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -529,7 +529,7 @@ void tcg_gen_brcond_i32(TCGCond cond, TCGv_i32 arg1, TCGv_i32 arg2, TCGLabel *l) if (cond == TCG_COND_ALWAYS) { tcg_gen_br(l); } else if (cond != TCG_COND_NEVER) { - TCGOp *op = tcg_gen_op4ii_i32(INDEX_op_brcond_i32, + TCGOp *op = tcg_gen_op4ii_i32(INDEX_op_brcond, arg1, arg2, cond, label_arg(l)); add_as_label_use(l, op); } @@ -1874,7 +1874,7 @@ void tcg_gen_brcond_i64(TCGCond cond, TCGv_i64 arg1, TCGv_i64 arg2, TCGLabel *l) TCGV_HIGH(arg1), TCGV_LOW(arg2), TCGV_HIGH(arg2), cond, label_arg(l)); } else { - op = tcg_gen_op4ii_i64(INDEX_op_brcond_i64, arg1, arg2, cond, + op = tcg_gen_op4ii_i64(INDEX_op_brcond, arg1, arg2, cond, label_arg(l)); } add_as_label_use(l, op); diff --git a/tcg/tcg.c b/tcg/tcg.c index dbaa574cb5..4dc6995d00 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1048,8 +1048,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), - OUTOP(INDEX_op_brcond_i32, TCGOutOpBrcond, outop_brcond), - OUTOP(INDEX_op_brcond_i64, TCGOutOpBrcond, outop_brcond), + OUTOP(INDEX_op_brcond, TCGOutOpBrcond, outop_brcond), OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), OUTOP(INDEX_op_ctpop, TCGOutOpUnary, outop_ctpop), OUTOP(INDEX_op_ctz, TCGOutOpBinary, outop_ctz), @@ -2282,6 +2281,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_add: case INDEX_op_and: + case INDEX_op_brcond: case INDEX_op_mov: case INDEX_op_negsetcond: case INDEX_op_or: @@ -2289,7 +2289,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_xor: return has_type; - case INDEX_op_brcond_i32: case INDEX_op_movcond_i32: case INDEX_op_ld8u_i32: case INDEX_op_ld8s_i32: @@ -2319,7 +2318,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond2_i32: return TCG_TARGET_REG_BITS == 32; - case INDEX_op_brcond_i64: case INDEX_op_movcond_i64: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i64: @@ -2869,13 +2867,12 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) op->args[k++])); } switch (c) { - case INDEX_op_brcond_i32: + case INDEX_op_brcond: case INDEX_op_setcond: case INDEX_op_negsetcond: case INDEX_op_movcond_i32: case INDEX_op_brcond2_i32: case INDEX_op_setcond2_i32: - case INDEX_op_brcond_i64: case INDEX_op_movcond_i64: case INDEX_op_cmp_vec: case INDEX_op_cmpsel_vec: @@ -2961,8 +2958,7 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) switch (c) { case INDEX_op_set_label: case INDEX_op_br: - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: + case INDEX_op_brcond: case INDEX_op_brcond2_i32: col += ne_fprintf(f, "%s$L%d", k ? "," : "", arg_label(op->args[k])->id); @@ -3417,8 +3413,7 @@ void tcg_op_remove(TCGContext *s, TCGOp *op) case INDEX_op_br: remove_label_use(op, 0); break; - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: + case INDEX_op_brcond: remove_label_use(op, 3); break; case INDEX_op_brcond2_i32: @@ -3519,8 +3514,7 @@ static void move_label_uses(TCGLabel *to, TCGLabel *from) case INDEX_op_br: op->args[0] = label_arg(to); break; - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: + case INDEX_op_brcond: op->args[3] = label_arg(to); break; case INDEX_op_brcond2_i32: @@ -5068,8 +5062,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) o_allocated_regs = s->reserved_regs; switch (op->opc) { - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: + case INDEX_op_brcond: op_cond = op->args[2]; break; case INDEX_op_setcond: @@ -5496,8 +5489,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: + case INDEX_op_brcond: { const TCGOutOpBrcond *out = &outop_brcond; TCGCond cond = new_args[2]; diff --git a/tcg/tci.c b/tcg/tci.c index d431cad6fd..4c5dc16ecb 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -664,8 +664,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrbb(insn, &r0, &r1, &pos, &len); regs[r0] = sextract32(regs[r1], pos, len); break; - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: + case INDEX_op_brcond: tci_args_rl(insn, tb_ptr, &r0, &ptr); if (regs[r0]) { tb_ptr = ptr; @@ -959,8 +958,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) info->fprintf_func(info->stream, "%-12s %d, %p", op_name, len, ptr); break; - case INDEX_op_brcond_i32: - case INDEX_op_brcond_i64: + case INDEX_op_brcond: tci_args_rl(insn, tb_ptr, &r0, &ptr); info->fprintf_func(info->stream, "%-12s %s, 0, ne, %p", op_name, str_r(r0), ptr); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 2c7fb5d75f..18628b957a 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -964,7 +964,7 @@ static void tgen_brcond(TCGContext *s, TCGType type, TCGCond cond, TCGReg arg0, TCGReg arg1, TCGLabel *l) { tgen_setcond(s, type, cond, TCG_REG_TMP, arg0, arg1); - tcg_out_op_rl(s, INDEX_op_brcond_i32, TCG_REG_TMP, l); + tcg_out_op_rl(s, INDEX_op_brcond, TCG_REG_TMP, l); } static const TCGOutOpBrcond outop_brcond = { @@ -1047,7 +1047,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_brcond2_i32: tcg_out_op_rrrrrc(s, INDEX_op_setcond2_i32, TCG_REG_TMP, args[0], args[1], args[2], args[3], args[4]); - tcg_out_op_rl(s, INDEX_op_brcond_i32, TCG_REG_TMP, arg_label(args[5])); + tcg_out_op_rl(s, INDEX_op_brcond, TCG_REG_TMP, arg_label(args[5])); break; #endif From 1f406e46785e60e274a9c581d6fd07e05a6ff4e0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 13:29:39 -0800 Subject: [PATCH 0439/2760] tcg: Convert movcond to TCGOutOpMovcond Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 26 +++++++++++++------------- tcg/arm/tcg-target.c.inc | 24 ++++++++++++++---------- tcg/i386/tcg-target.c.inc | 23 +++++++++++------------ tcg/loongarch64/tcg-target-con-set.h | 2 +- tcg/loongarch64/tcg-target.c.inc | 23 +++++++++-------------- tcg/mips/tcg-target-con-set.h | 3 ++- tcg/mips/tcg-target.c.inc | 25 ++++++++++++------------- tcg/ppc/tcg-target.c.inc | 24 ++++++++---------------- tcg/riscv/tcg-target.c.inc | 26 ++++++++++---------------- tcg/s390x/tcg-target-con-set.h | 1 - tcg/s390x/tcg-target.c.inc | 26 ++++++++------------------ tcg/sparc64/tcg-target-con-set.h | 2 +- tcg/sparc64/tcg-target.c.inc | 28 ++++++++++++++++------------ tcg/tcg.c | 23 +++++++++++++++++++++++ tcg/tci.c | 12 ++++++------ tcg/tci/tcg-target-opc.h.inc | 1 + tcg/tci/tcg-target.c.inc | 18 +++++++++++++++--- 17 files changed, 150 insertions(+), 137 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index e3d8e9090f..ee45e7e244 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2513,6 +2513,19 @@ static const TCGOutOpSetcond outop_negsetcond = { .out_rri = tgen_negsetcondi, }; +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg vt, bool const_vt, TCGArg vf, bool const_vf) +{ + tcg_out_cmp(s, type, cond, c1, c2, const_c2); + tcg_out_insn(s, 3506, CSEL, type, ret, vt, vf, cond); +} + +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rC, rz, rz), + .out = tgen_movcond, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2521,7 +2534,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, TCGArg a0 = args[0]; TCGArg a1 = args[1]; TCGArg a2 = args[2]; - int c2 = const_args[2]; switch (opc) { case INDEX_op_goto_ptr: @@ -2579,14 +2591,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); break; - case INDEX_op_movcond_i32: - a2 = (int32_t)a2; - /* FALLTHRU */ - case INDEX_op_movcond_i64: - tcg_out_cmp(s, ext, args[5], a1, a2, c2); - tcg_out_insn(s, 3506, CSEL, ext, a0, args[3], args[4], args[5]); - break; - case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: tcg_out_qemu_ld(s, a0, a1, a2, ext); @@ -3166,10 +3170,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - return C_O1_I4(r, r, rC, rz, rz); - case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 4c7537cbeb..3d864c1c1e 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2247,6 +2247,20 @@ static const TCGOutOpSetcond outop_negsetcond = { .out_rri = tgen_negsetcondi, }; +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg vt, bool const_vt, TCGArg vf, bool consf_vf) +{ + cond = tcg_out_cmp(s, cond, c1, c2, const_c2); + tcg_out_dat_rIK(s, tcg_cond_to_arm_cond[cond], ARITH_MOV, ARITH_MVN, + ret, 0, vt, const_vt); +} + +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rIN, rIK, 0), + .out = tgen_movcond, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2288,14 +2302,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st32(s, COND_AL, args[0], args[1], args[2]); break; - case INDEX_op_movcond_i32: - /* Constraints mean that v2 is always in the same register as dest, - * so we only need to do "if condition passed, move v1 to dest". - */ - c = tcg_out_cmp(s, args[5], args[1], args[2], const_args[2]); - tcg_out_dat_rIK(s, tcg_cond_to_arm_cond[c], ARITH_MOV, - ARITH_MVN, args[0], 0, args[3], const_args[3]); - break; case INDEX_op_add2_i32: a0 = args[0], a1 = args[1], a2 = args[2]; a3 = args[3], a4 = args[4], a5 = args[5]; @@ -2440,8 +2446,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O1_I2(r, 0, rZ); case INDEX_op_extract2_i32: return C_O1_I2(r, rZ, rZ); - case INDEX_op_movcond_i32: - return C_O1_I4(r, r, rIN, rIK, 0); case INDEX_op_add2_i32: return C_O2_I4(r, r, r, r, rIN, rIK); case INDEX_op_sub2_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index d2eff3b617..ae3a53a18a 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1900,14 +1900,21 @@ static void tcg_out_cmov(TCGContext *s, int jcc, int rexw, tcg_out_modrm(s, OPC_CMOVCC | jcc | rexw, dest, v1); } -static void tcg_out_movcond(TCGContext *s, int rexw, TCGCond cond, - TCGReg dest, TCGReg c1, TCGArg c2, int const_c2, - TCGReg v1) +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg vt, bool const_vt, + TCGArg vf, bool consf_vf) { + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; int jcc = tcg_out_cmp(s, cond, c1, c2, const_c2, rexw); - tcg_out_cmov(s, jcc, rexw, dest, v1); + tcg_out_cmov(s, jcc, rexw, dest, vt); } +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, reT, r, 0), + .out = tgen_movcond, +}; + static void tcg_out_branch(TCGContext *s, int call, const tcg_insn_unit *dest) { intptr_t disp = tcg_pcrel_diff(s, dest) - 5; @@ -3144,10 +3151,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(movcond): - tcg_out_movcond(s, rexw, args[5], a0, a1, a2, const_a2, args[3]); - break; - OP_32_64(bswap16): if (a2 & TCG_BSWAP_OS) { /* Output must be sign-extended. */ @@ -3977,10 +3980,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_deposit_i64: return C_O1_I2(q, 0, qi); - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - return C_O1_I4(r, r, reT, r, 0); - case INDEX_op_add2_i32: case INDEX_op_add2_i64: case INDEX_op_sub2_i32: diff --git a/tcg/loongarch64/tcg-target-con-set.h b/tcg/loongarch64/tcg-target-con-set.h index dfe55c6fe8..fd731c0c0f 100644 --- a/tcg/loongarch64/tcg-target-con-set.h +++ b/tcg/loongarch64/tcg-target-con-set.h @@ -33,5 +33,5 @@ C_O1_I2(w, w, w) C_O1_I2(w, w, wM) C_O1_I2(w, w, wA) C_O1_I3(w, w, w, w) -C_O1_I4(r, rz, rJ, rz, rz) +C_O1_I4(r, r, rJ, rz, rz) C_N2_I1(r, r, r) diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 53bba07c49..c731096c64 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -723,11 +723,11 @@ static const TCGOutOpSetcond outop_negsetcond = { .out_rri = tgen_negsetcondi, }; -static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, tcg_target_long c2, bool const2, - TCGReg v1, TCGReg v2) +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg v1, bool const_v1, TCGArg v2, bool const_v2) { - int tmpflags = tcg_out_setcond_int(s, cond, TCG_REG_TMP0, c1, c2, const2); + int tmpflags = tcg_out_setcond_int(s, cond, TCG_REG_TMP0, c1, c2, const_c2); TCGReg t; /* Standardize the test below to t != 0. */ @@ -747,6 +747,11 @@ static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, } } +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rJ, rz, rz), + .out = tgen_movcond, +}; + /* * Branch helpers */ @@ -1759,7 +1764,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a1 = args[1]; TCGArg a2 = args[2]; TCGArg a3 = args[3]; - int c2 = const_args[2]; switch (opc) { case INDEX_op_mb: @@ -1849,11 +1853,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_revb_d(s, a0, a1); break; - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - tcg_out_movcond(s, args[5], a0, a1, a2, c2, args[3], args[4]); - break; - case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: tcg_out_ldst(s, OPC_LD_B, a0, a1, a2); @@ -2475,10 +2474,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) /* Must deposit into the same register as input */ return C_O1_I2(r, 0, rz); - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - return C_O1_I4(r, rz, rJ, rz, rz); - case INDEX_op_ld_vec: case INDEX_op_dupm_vec: case INDEX_op_dup_vec: diff --git a/tcg/mips/tcg-target-con-set.h b/tcg/mips/tcg-target-con-set.h index a80630a8b4..f5e4852b56 100644 --- a/tcg/mips/tcg-target-con-set.h +++ b/tcg/mips/tcg-target-con-set.h @@ -23,7 +23,8 @@ C_O1_I2(r, r, rIK) C_O1_I2(r, r, rJ) C_O1_I2(r, r, rz) C_O1_I2(r, r, rzW) -C_O1_I4(r, rz, rz, rz, 0) +C_O1_I4(r, r, rz, rz, 0) +C_O1_I4(r, r, rz, rz, rz) C_O1_I4(r, rz, rz, rz, rz) C_O2_I1(r, r, r) C_O2_I2(r, r, r, r) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index a942905dc4..3ce71a1c8d 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1086,8 +1086,9 @@ static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, tcg_out_nop(s); } -static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg c1, TCGReg c2, TCGReg v1, TCGReg v2) +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg v1, bool const_v1, TCGArg v2, bool const_v2) { int tmpflags; bool eqz; @@ -1133,6 +1134,13 @@ static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, } } +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = (use_mips32r6_instructions + ? C_O1_I4(r, r, rz, rz, rz) + : C_O1_I4(r, r, rz, rz, 0)), + .out = tgen_movcond, +}; + static void tcg_out_call_int(TCGContext *s, const tcg_insn_unit *arg, bool tail) { /* @@ -1726,7 +1734,8 @@ static void tgen_clz(TCGContext *s, TCGType type, if (use_mips32r6_instructions) { MIPSInsn opcv6 = type == TCG_TYPE_I32 ? OPC_CLZ_R6 : OPC_DCLZ_R6; tcg_out_opc_reg(s, opcv6, TCG_TMP0, a1, 0); - tcg_out_movcond(s, TCG_COND_EQ, a0, a1, 0, a2, TCG_TMP0); + tgen_movcond(s, TCG_TYPE_REG, TCG_COND_EQ, a0, a1, a2, false, + TCG_TMP0, false, TCG_REG_ZERO, false); } else { MIPSInsn opcv2 = type == TCG_TYPE_I32 ? OPC_CLZ : OPC_DCLZ; if (a0 == a2) { @@ -2292,11 +2301,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_brcond2(s, args[4], a0, a1, a2, args[3], arg_label(args[5])); break; - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - tcg_out_movcond(s, args[5], a0, a1, a2, args[3], args[4]); - break; - case INDEX_op_setcond2_i32: tcg_out_setcond2(s, args[5], a0, a1, a2, args[3], args[4]); break; @@ -2392,11 +2396,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: return C_O1_I2(r, 0, rz); - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - return (use_mips32r6_instructions - ? C_O1_I4(r, rz, rz, rz, rz) - : C_O1_I4(r, rz, rz, rz, 0)); case INDEX_op_add2_i32: case INDEX_op_sub2_i32: return C_O2_I4(r, r, rz, rz, rN, rN); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 819abdc906..339b3a0904 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2144,9 +2144,9 @@ static const TCGOutOpBrcond outop_brcond = { .out_ri = tgen_brcondi, }; -static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, - TCGArg dest, TCGArg c1, TCGArg c2, TCGArg v1, - TCGArg v2, bool const_c2) +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg dest, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg v1, bool const_v1, TCGArg v2, bool const_v2) { /* If for some reason both inputs are zero, don't produce bad code. */ if (v1 == 0 && v2 == 0) { @@ -2192,6 +2192,11 @@ static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, } } +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rC, rZ, rZ), + .out = tgen_movcond, +}; + static void tcg_out_cntxz(TCGContext *s, TCGType type, uint32_t opc, TCGArg a0, TCGArg a1, TCGArg a2, bool const_a2) { @@ -3578,15 +3583,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_movcond_i32: - tcg_out_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], args[2], - args[3], args[4], const_args[2]); - break; - case INDEX_op_movcond_i64: - tcg_out_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], args[2], - args[3], args[4], const_args[2]); - break; - #if TCG_TARGET_REG_BITS == 64 case INDEX_op_add2_i64: #else @@ -4297,10 +4293,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - return C_O1_I4(r, r, rC, rZ, rZ); - case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: return C_O1_I2(r, 0, rZ); diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 1d7194e883..8d106d7f28 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1501,10 +1501,10 @@ static void tcg_out_movcond_br2(TCGContext *s, TCGCond cond, TCGReg ret, tcg_out_mov(s, TCG_TYPE_REG, ret, tmp); } -static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg cmp1, int cmp2, bool c_cmp2, - TCGReg val1, bool c_val1, - TCGReg val2, bool c_val2) +static void tcg_out_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg cmp1, TCGArg cmp2, bool c_cmp2, + TCGArg val1, bool c_val1, + TCGArg val2, bool c_val2) { int tmpflags; TCGReg t; @@ -1531,6 +1531,11 @@ static void tcg_out_movcond(TCGContext *s, TCGCond cond, TCGReg ret, } } +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rI, rM, rM), + .out = tcg_out_movcond, +}; + static void tcg_out_cltz(TCGContext *s, TCGType type, RISCVInsn insn, TCGReg ret, TCGReg src1, int src2, bool c_src2) { @@ -1542,7 +1547,7 @@ static void tcg_out_cltz(TCGContext *s, TCGType type, RISCVInsn insn, * Note that constraints put 'ret' in a new register, so the * computation above did not clobber either 'src1' or 'src2'. */ - tcg_out_movcond(s, TCG_COND_EQ, ret, src1, 0, true, + tcg_out_movcond(s, type, TCG_COND_EQ, ret, src1, 0, true, src2, c_src2, ret, false); } } @@ -2425,7 +2430,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a0 = args[0]; TCGArg a1 = args[1]; TCGArg a2 = args[2]; - int c2 = const_args[2]; switch (opc) { case INDEX_op_goto_ptr: @@ -2521,12 +2525,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const_args[4], const_args[5], true, false); break; - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - tcg_out_movcond(s, args[5], a0, a1, a2, c2, - args[3], const_args[3], args[4], const_args[4]); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); break; @@ -2863,10 +2861,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - return C_O1_I4(r, r, rI, rM, rM); - case INDEX_op_add2_i32: case INDEX_op_add2_i64: case INDEX_op_sub2_i32: diff --git a/tcg/s390x/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h index 86af067965..78f06e3e52 100644 --- a/tcg/s390x/tcg-target-con-set.h +++ b/tcg/s390x/tcg-target-con-set.h @@ -38,7 +38,6 @@ C_O1_I2(v, v, v) C_O1_I3(v, v, v, v) C_O1_I4(v, v, v, vZ, v) C_O1_I4(v, v, v, vZM, v) -C_O1_I4(r, r, ri, rI, r) C_O1_I4(r, r, rC, rI, r) C_O2_I1(o, m, r) C_O2_I2(o, m, 0, r) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index d3650636aa..fbf39ca529 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1540,9 +1540,9 @@ static void tgen_movcond_int(TCGContext *s, TCGType type, TCGReg dest, tcg_out_insn(s, RRFc, LOCGR, dest, src, cc); } -static void tgen_movcond(TCGContext *s, TCGType type, TCGCond c, TCGReg dest, - TCGReg c1, TCGArg c2, int c2const, - TCGArg v3, int v3const, TCGReg v4) +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond c, + TCGReg dest, TCGReg c1, TCGArg c2, bool c2const, + TCGArg v3, bool v3const, TCGArg v4, bool v4const) { int cc, inv_cc; @@ -1550,6 +1550,11 @@ static void tgen_movcond(TCGContext *s, TCGType type, TCGCond c, TCGReg dest, tgen_movcond_int(s, type, dest, v3, v3const, v4, cc, inv_cc); } +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rC, rI, r), + .out = tgen_movcond, +}; + static void tgen_deposit(TCGContext *s, TCGReg dest, TCGReg src, int ofs, int len, int z) { @@ -2875,11 +2880,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tgen_branch(s, S390_CC_ALWAYS, arg_label(args[0])); break; - case INDEX_op_movcond_i32: - tgen_movcond(s, TCG_TYPE_I32, args[5], args[0], args[1], - args[2], const_args[2], args[3], const_args[3], args[4]); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], args[1], args[2], TCG_TYPE_I32); break; @@ -2948,11 +2948,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_insn(s, RRE, SLBGR, args[1], args[5]); break; - case INDEX_op_movcond_i64: - tgen_movcond(s, TCG_TYPE_I64, args[5], args[0], args[1], - args[2], const_args[2], args[3], const_args[3], args[4]); - break; - OP_32_64(deposit): a0 = args[0], a1 = args[1], a2 = args[2]; if (const_args[1]) { @@ -3492,11 +3487,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_deposit_i64: return C_O1_I2(r, rZ, r); - case INDEX_op_movcond_i32: - return C_O1_I4(r, r, ri, rI, r); - case INDEX_op_movcond_i64: - return C_O1_I4(r, r, rC, rI, r); - case INDEX_op_add2_i32: case INDEX_op_sub2_i32: return C_N1_O1_I4(r, r, 0, 1, ri, r); diff --git a/tcg/sparc64/tcg-target-con-set.h b/tcg/sparc64/tcg-target-con-set.h index 9f66e52ec6..8cec396173 100644 --- a/tcg/sparc64/tcg-target-con-set.h +++ b/tcg/sparc64/tcg-target-con-set.h @@ -15,6 +15,6 @@ C_O0_I2(r, rJ) C_O1_I1(r, r) C_O1_I2(r, r, r) C_O1_I2(r, r, rJ) -C_O1_I4(r, rz, rJ, rI, 0) +C_O1_I4(r, r, rJ, rI, 0) C_O2_I2(r, r, r, r) C_O2_I4(r, r, rz, rz, rJ, rJ) diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 68f38b7d71..d99b9e42ce 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -898,6 +898,22 @@ static const TCGOutOpSetcond outop_negsetcond = { .out_rri = tgen_negsetcondi, }; +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool c2const, + TCGArg v1, bool v1const, TCGArg v2, bool v2consf) +{ + if (type == TCG_TYPE_I32) { + tcg_out_movcond_i32(s, cond, ret, c1, c2, c2const, v1, v1const); + } else { + tcg_out_movcond_i64(s, cond, ret, c1, c2, c2const, v1, v1const); + } +} + +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, rJ, rI, 0), + .out = tgen_movcond, +}; + static void tcg_out_addsub2_i32(TCGContext *s, TCGReg rl, TCGReg rh, TCGReg al, TCGReg ah, int32_t bl, int blconst, int32_t bh, int bhconst, int opl, int oph) @@ -1735,13 +1751,11 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { TCGArg a0, a1, a2; - int c2; /* Hoist the loads of the most common arguments. */ a0 = args[0]; a1 = args[1]; a2 = args[2]; - c2 = const_args[2]; switch (opc) { case INDEX_op_goto_ptr: @@ -1784,10 +1798,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, a0, a1, a2, STW); break; - case INDEX_op_movcond_i32: - tcg_out_movcond_i32(s, args[5], a0, a1, a2, c2, args[3], const_args[3]); - break; - case INDEX_op_add2_i32: tcg_out_addsub2_i32(s, args[0], args[1], args[2], args[3], args[4], const_args[4], args[5], const_args[5], @@ -1822,9 +1832,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, a0, a1, a2, STX); break; - case INDEX_op_movcond_i64: - tcg_out_movcond_i64(s, args[5], a0, a1, a2, c2, args[3], const_args[3]); - break; case INDEX_op_add2_i64: tcg_out_addsub2_i64(s, args[0], args[1], args[2], args[3], args[4], const_args[4], args[5], const_args[5], false); @@ -1895,9 +1902,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: - return C_O1_I4(r, rz, rJ, rI, 0); case INDEX_op_add2_i32: case INDEX_op_add2_i64: case INDEX_op_sub2_i32: diff --git a/tcg/tcg.c b/tcg/tcg.c index 4dc6995d00..ba81a67e28 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1000,6 +1000,13 @@ typedef struct TCGOutOpDivRem { TCGReg a0, TCGReg a1, TCGReg a4); } TCGOutOpDivRem; +typedef struct TCGOutOpMovcond { + TCGOutOp base; + void (*out)(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg vt, bool const_vt, TCGArg vf, bool consf_vf); +} TCGOutOpMovcond; + typedef struct TCGOutOpMul2 { TCGOutOp base; void (*out_rrrr)(TCGContext *s, TCGType type, @@ -1057,6 +1064,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), OUTOP(INDEX_op_divu2, TCGOutOpDivRem, outop_divu2), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), + OUTOP(INDEX_op_movcond_i32, TCGOutOpMovcond, outop_movcond), + OUTOP(INDEX_op_movcond_i64, TCGOutOpMovcond, outop_movcond), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_muls2, TCGOutOpMul2, outop_muls2), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), @@ -5504,6 +5513,20 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_movcond_i32: + case INDEX_op_movcond_i64: + { + const TCGOutOpMovcond *out = &outop_movcond; + TCGCond cond = new_args[5]; + + tcg_debug_assert(!const_args[1]); + out->out(s, type, cond, new_args[0], + new_args[1], new_args[2], const_args[2], + new_args[3], const_args[3], + new_args[4], const_args[4]); + } + break; + case INDEX_op_setcond: case INDEX_op_negsetcond: { diff --git a/tcg/tci.c b/tcg/tci.c index 4c5dc16ecb..aef0023dc6 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -438,11 +438,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_l(insn, tb_ptr, &ptr); tb_ptr = ptr; continue; - case INDEX_op_movcond_i32: - tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition); - tmp32 = tci_compare32(regs[r1], regs[r2], condition); - regs[r0] = regs[tmp32 ? r3 : r4]; - break; #if TCG_TARGET_REG_BITS == 32 case INDEX_op_setcond2_i32: tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition); @@ -628,6 +623,11 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrrc(insn, &r0, &r1, &r2, &condition); regs[r0] = tci_compare32(regs[r1], regs[r2], condition); break; + case INDEX_op_tci_movcond32: + tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition); + tmp32 = tci_compare32(regs[r1], regs[r2], condition); + regs[r0] = regs[tmp32 ? r3 : r4]; + break; /* Shift/rotate operations. */ @@ -1074,7 +1074,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) op_name, str_r(r0), str_r(r1), pos, len); break; - case INDEX_op_movcond_i32: + case INDEX_op_tci_movcond32: case INDEX_op_movcond_i64: case INDEX_op_setcond2_i32: tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &c); diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc index 27b4574e4f..672d9b7323 100644 --- a/tcg/tci/tcg-target-opc.h.inc +++ b/tcg/tci/tcg-target-opc.h.inc @@ -11,3 +11,4 @@ DEF(tci_remu32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_rotl32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_rotr32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_setcond32, 1, 2, 1, TCG_OPF_NOT_PRESENT) +DEF(tci_movcond32, 1, 2, 1, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 18628b957a..79f9219187 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -92,8 +92,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O0_I4(r, r, r, r); #endif - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: case INDEX_op_setcond2_i32: return C_O1_I4(r, r, r, r, r); @@ -972,6 +970,21 @@ static const TCGOutOpBrcond outop_brcond = { .out_rr = tgen_brcond, }; +static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, + TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, + TCGArg vt, bool const_vt, TCGArg vf, bool consf_vf) +{ + TCGOpcode opc = (type == TCG_TYPE_I32 + ? INDEX_op_tci_movcond32 + : INDEX_op_movcond_i64); + tcg_out_op_rrrrrc(s, opc, ret, c1, c2, vt, vf, cond); +} + +static const TCGOutOpMovcond outop_movcond = { + .base.static_constraint = C_O1_I4(r, r, r, r, r), + .out = tgen_movcond, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -987,7 +1000,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_l(s, opc, arg_label(args[0])); break; - CASE_32_64(movcond) case INDEX_op_setcond2_i32: tcg_out_op_rrrrrc(s, opc, args[0], args[1], args[2], args[3], args[4], args[5]); From ea46c4bce8c8a8285e6715c1bac29f5b73f5062b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 13:41:25 -0800 Subject: [PATCH 0440/2760] tcg: Merge INDEX_op_movcond_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 2 +- tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 15 +++++---------- tcg/tci.c | 4 ++-- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 13 insertions(+), 19 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 18f02c5122..26dc3bad49 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -511,7 +511,7 @@ Conditional moves | | Set *dest* to -1 if (*t1* *cond* *t2*) is true, otherwise set to 0. - * - movcond_i32/i64 *dest*, *c1*, *c2*, *v1*, *v2*, *cond* + * - movcond *dest*, *c1*, *c2*, *v1*, *v2*, *cond* - | *dest* = (*c1* *cond* *c2* ? *v1* : *v2*) | diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index d40ca001c2..5e085607d5 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -51,6 +51,7 @@ DEF(divs2, 2, 3, 0, TCG_OPF_INT) DEF(divu, 1, 2, 0, TCG_OPF_INT) DEF(divu2, 2, 3, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) +DEF(movcond, 1, 4, 1, TCG_OPF_INT) DEF(mul, 1, 2, 0, TCG_OPF_INT) DEF(muls2, 2, 2, 0, TCG_OPF_INT) DEF(mulsh, 1, 2, 0, TCG_OPF_INT) @@ -74,7 +75,6 @@ DEF(shr, 1, 2, 0, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) -DEF(movcond_i32, 1, 4, 1, 0) /* load/store */ DEF(ld8u_i32, 1, 1, 1, 0) DEF(ld8s_i32, 1, 1, 1, 0) @@ -98,7 +98,6 @@ DEF(setcond2_i32, 1, 4, 1, 0) DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) -DEF(movcond_i64, 1, 4, 1, 0) /* load/store */ DEF(ld8u_i64, 1, 1, 1, 0) DEF(ld8s_i64, 1, 1, 1, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index d0cb4588ed..54606388cc 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2943,7 +2943,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_mov_vec: done = fold_mov(&ctx, op); break; - CASE_OP_32_64(movcond): + case INDEX_op_movcond: done = fold_movcond(&ctx, op); break; case INDEX_op_mul: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 041ca95f0d..3527952c66 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1095,7 +1095,7 @@ void tcg_gen_movcond_i32(TCGCond cond, TCGv_i32 ret, TCGv_i32 c1, } else if (cond == TCG_COND_NEVER) { tcg_gen_mov_i32(ret, v2); } else { - tcg_gen_op6i_i32(INDEX_op_movcond_i32, ret, c1, c2, v1, v2, cond); + tcg_gen_op6i_i32(INDEX_op_movcond, ret, c1, c2, v1, v2, cond); } } @@ -2799,7 +2799,7 @@ void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, TCGv_i64 c1, } else if (cond == TCG_COND_NEVER) { tcg_gen_mov_i64(ret, v2); } else if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_op6i_i64(INDEX_op_movcond_i64, ret, c1, c2, v1, v2, cond); + tcg_gen_op6i_i64(INDEX_op_movcond, ret, c1, c2, v1, v2, cond); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 zero = tcg_constant_i32(0); diff --git a/tcg/tcg.c b/tcg/tcg.c index ba81a67e28..3f57f6aafd 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1064,8 +1064,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), OUTOP(INDEX_op_divu2, TCGOutOpDivRem, outop_divu2), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), - OUTOP(INDEX_op_movcond_i32, TCGOutOpMovcond, outop_movcond), - OUTOP(INDEX_op_movcond_i64, TCGOutOpMovcond, outop_movcond), + OUTOP(INDEX_op_movcond, TCGOutOpMovcond, outop_movcond), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_muls2, TCGOutOpMul2, outop_muls2), OUTOP(INDEX_op_mulsh, TCGOutOpBinary, outop_mulsh), @@ -2292,13 +2291,13 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_and: case INDEX_op_brcond: case INDEX_op_mov: + case INDEX_op_movcond: case INDEX_op_negsetcond: case INDEX_op_or: case INDEX_op_setcond: case INDEX_op_xor: return has_type; - case INDEX_op_movcond_i32: case INDEX_op_ld8u_i32: case INDEX_op_ld8s_i32: case INDEX_op_ld16u_i32: @@ -2327,7 +2326,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond2_i32: return TCG_TARGET_REG_BITS == 32; - case INDEX_op_movcond_i64: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i64: case INDEX_op_ld16u_i64: @@ -2879,10 +2877,9 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) case INDEX_op_brcond: case INDEX_op_setcond: case INDEX_op_negsetcond: - case INDEX_op_movcond_i32: + case INDEX_op_movcond: case INDEX_op_brcond2_i32: case INDEX_op_setcond2_i32: - case INDEX_op_movcond_i64: case INDEX_op_cmp_vec: case INDEX_op_cmpsel_vec: if (op->args[k] < ARRAY_SIZE(cond_name) @@ -5082,8 +5079,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_brcond2_i32: op_cond = op->args[4]; break; - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: + case INDEX_op_movcond: case INDEX_op_setcond2_i32: case INDEX_op_cmpsel_vec: op_cond = op->args[5]; @@ -5513,8 +5509,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_movcond_i32: - case INDEX_op_movcond_i64: + case INDEX_op_movcond: { const TCGOutOpMovcond *out = &outop_movcond; TCGCond cond = new_args[5]; diff --git a/tcg/tci.c b/tcg/tci.c index aef0023dc6..9c3f58242e 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -450,7 +450,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrrc(insn, &r0, &r1, &r2, &condition); regs[r0] = tci_compare64(regs[r1], regs[r2], condition); break; - case INDEX_op_movcond_i64: + case INDEX_op_movcond: tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition); tmp32 = tci_compare64(regs[r1], regs[r2], condition); regs[r0] = regs[tmp32 ? r3 : r4]; @@ -1075,7 +1075,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) break; case INDEX_op_tci_movcond32: - case INDEX_op_movcond_i64: + case INDEX_op_movcond: case INDEX_op_setcond2_i32: tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &c); info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s, %s, %s", diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 79f9219187..99a5744ab4 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -976,7 +976,7 @@ static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, { TCGOpcode opc = (type == TCG_TYPE_I32 ? INDEX_op_tci_movcond32 - : INDEX_op_movcond_i64); + : INDEX_op_movcond); tcg_out_op_rrrrrc(s, opc, ret, c1, c2, vt, vf, cond); } From 298b3b548656ea89ff6fd4251bc7319986b79c67 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 13:52:49 -0800 Subject: [PATCH 0441/2760] tcg/ppc: Drop fallback constant loading in tcg_out_cmp Use U and C constraints for brcond2 and setcond2, so that tcg_out_cmp2 automatically passes in-range constants to tcg_out_cmp. Tested-by: Nicholas Piggin Reviewed-by: Nicholas Piggin Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/ppc/tcg-target-con-set.h | 4 +-- tcg/ppc/tcg-target.c.inc | 49 ++++++++++++------------------------ 2 files changed, 18 insertions(+), 35 deletions(-) diff --git a/tcg/ppc/tcg-target-con-set.h b/tcg/ppc/tcg-target-con-set.h index 77a1038d51..14cd217287 100644 --- a/tcg/ppc/tcg-target-con-set.h +++ b/tcg/ppc/tcg-target-con-set.h @@ -15,7 +15,7 @@ C_O0_I2(r, rC) C_O0_I2(v, r) C_O0_I3(r, r, r) C_O0_I3(o, m, r) -C_O0_I4(r, r, ri, ri) +C_O0_I4(r, r, rU, rC) C_O0_I4(r, r, r, r) C_O1_I1(r, r) C_O1_I1(v, r) @@ -34,7 +34,7 @@ C_O1_I2(v, v, v) C_O1_I3(v, v, v, v) C_O1_I4(v, v, v, vZM, v) C_O1_I4(r, r, rC, rZ, rZ) -C_O1_I4(r, r, r, ri, ri) +C_O1_I4(r, r, r, rU, rC) C_O2_I1(r, r, r) C_N1O1_I1(o, m, r) C_O2_I2(r, r, r, r) diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 339b3a0904..1782d05290 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -1777,9 +1777,8 @@ static void tcg_out_test(TCGContext *s, TCGReg dest, TCGReg arg1, TCGArg arg2, } static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, - int const_arg2, int cr, TCGType type) + bool const_arg2, int cr, TCGType type) { - int imm; uint32_t op; tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || type == TCG_TYPE_I32); @@ -1796,18 +1795,15 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, case TCG_COND_EQ: case TCG_COND_NE: if (const_arg2) { - if ((int16_t) arg2 == arg2) { + if ((int16_t)arg2 == arg2) { op = CMPI; - imm = 1; - break; - } else if ((uint16_t) arg2 == arg2) { - op = CMPLI; - imm = 1; break; } + tcg_debug_assert((uint16_t)arg2 == arg2); + op = CMPLI; + break; } op = CMPL; - imm = 0; break; case TCG_COND_TSTEQ: @@ -1821,14 +1817,11 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, case TCG_COND_LE: case TCG_COND_GT: if (const_arg2) { - if ((int16_t) arg2 == arg2) { - op = CMPI; - imm = 1; - break; - } + tcg_debug_assert((int16_t)arg2 == arg2); + op = CMPI; + break; } op = CMP; - imm = 0; break; case TCG_COND_LTU: @@ -1836,30 +1829,20 @@ static void tcg_out_cmp(TCGContext *s, int cond, TCGArg arg1, TCGArg arg2, case TCG_COND_LEU: case TCG_COND_GTU: if (const_arg2) { - if ((uint16_t) arg2 == arg2) { - op = CMPLI; - imm = 1; - break; - } + tcg_debug_assert((uint16_t)arg2 == arg2); + op = CMPLI; + break; } op = CMPL; - imm = 0; break; default: g_assert_not_reached(); } op |= BF(cr) | ((type == TCG_TYPE_I64) << 21); - - if (imm) { - tcg_out32(s, op | RA(arg1) | (arg2 & 0xffff)); - } else { - if (const_arg2) { - tcg_out_movi(s, type, TCG_REG_R0, arg2); - arg2 = TCG_REG_R0; - } - tcg_out32(s, op | RA(arg1) | RB(arg2)); - } + op |= RA(arg1); + op |= const_arg2 ? arg2 & 0xffff : RB(arg2); + tcg_out32(s, op); } static void tcg_out_setcond_eq0(TCGContext *s, TCGType type, @@ -4297,9 +4280,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_deposit_i64: return C_O1_I2(r, 0, rZ); case INDEX_op_brcond2_i32: - return C_O0_I4(r, r, ri, ri); + return C_O0_I4(r, r, rU, rC); case INDEX_op_setcond2_i32: - return C_O1_I4(r, r, r, ri, ri); + return C_O1_I4(r, r, r, rU, rC); case INDEX_op_add2_i64: case INDEX_op_add2_i32: return C_O2_I4(r, r, r, r, rI, rZM); From cd2d9b181a67517fb7bbe8f0c8a81e2284207241 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 14:52:41 -0800 Subject: [PATCH 0442/2760] tcg/arm: Expand arguments to tcg_out_cmp2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass explicit arguments instead of arrays. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 3d864c1c1e..cebd783285 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1254,17 +1254,9 @@ static TCGCond tcg_out_cmp(TCGContext *s, TCGCond cond, TCGReg a, } } -static TCGCond tcg_out_cmp2(TCGContext *s, const TCGArg *args, - const int *const_args) +static TCGCond tcg_out_cmp2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, TCGArg bh, bool const_bh) { - TCGReg al = args[0]; - TCGReg ah = args[1]; - TCGArg bl = args[2]; - TCGArg bh = args[3]; - TCGCond cond = args[4]; - int const_bl = const_args[2]; - int const_bh = const_args[3]; - switch (cond) { case TCG_COND_EQ: case TCG_COND_NE: @@ -2344,11 +2336,13 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_brcond2_i32: - c = tcg_out_cmp2(s, args, const_args); + c = tcg_out_cmp2(s, args[4], args[0], args[1], args[2], const_args[2], + args[3], const_args[3]); tcg_out_goto_label(s, tcg_cond_to_arm_cond[c], arg_label(args[5])); break; case INDEX_op_setcond2_i32: - c = tcg_out_cmp2(s, args + 1, const_args + 1); + c = tcg_out_cmp2(s, args[5], args[1], args[2], args[3], const_args[3], + args[4], const_args[4]); tcg_out_dat_imm(s, tcg_cond_to_arm_cond[c], ARITH_MOV, args[0], 0, 1); tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(c)], ARITH_MOV, args[0], 0, 0); From c1e84acb7a87c23494f706c7045956ffaff5435a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 16:41:26 -0800 Subject: [PATCH 0443/2760] tcg/ppc: Expand arguments to tcg_out_cmp2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Nicholas Piggin Reviewed-by: Nicholas Piggin Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/ppc/tcg-target.c.inc | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 1782d05290..669c5eae4a 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2206,8 +2206,8 @@ static void tcg_out_cntxz(TCGContext *s, TCGType type, uint32_t opc, } } -static void tcg_out_cmp2(TCGContext *s, const TCGArg *args, - const int *const_args) +static void tcg_out_cmp2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool blconst, TCGArg bh, bool bhconst) { static const struct { uint8_t bit1, bit2; } bits[] = { [TCG_COND_LT ] = { CR_LT, CR_LT }, @@ -2220,18 +2220,9 @@ static void tcg_out_cmp2(TCGContext *s, const TCGArg *args, [TCG_COND_GEU] = { CR_GT, CR_LT }, }; - TCGCond cond = args[4], cond2; - TCGArg al, ah, bl, bh; - int blconst, bhconst; + TCGCond cond2; int op, bit1, bit2; - al = args[0]; - ah = args[1]; - bl = args[2]; - bh = args[3]; - blconst = const_args[2]; - bhconst = const_args[3]; - switch (cond) { case TCG_COND_EQ: op = CRAND; @@ -2286,7 +2277,8 @@ static void tcg_out_cmp2(TCGContext *s, const TCGArg *args, static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, const int *const_args) { - tcg_out_cmp2(s, args + 1, const_args + 1); + tcg_out_cmp2(s, args[5], args[1], args[2], args[3], const_args[3], + args[4], const_args[4]); tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(0)); tcg_out_rlw(s, RLWINM, args[0], TCG_REG_R0, CR_EQ + 0*4 + 1, 31, 31); } @@ -2294,7 +2286,8 @@ static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, const int *const_args) { - tcg_out_cmp2(s, args, const_args); + tcg_out_cmp2(s, args[4], args[0], args[1], args[2], const_args[2], + args[3], const_args[3]); tcg_out_bc_lab(s, TCG_COND_EQ, arg_label(args[5])); } From f408df587a0459c0e44414d1c0c72a7926ce8f3c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 15:51:26 -0800 Subject: [PATCH 0444/2760] tcg: Convert brcond2_i32 to TCGOutOpBrcond2 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 20 +++++++---- tcg/i386/tcg-target.c.inc | 62 ++++++++++++++++++----------------- tcg/mips/tcg-target-con-set.h | 2 +- tcg/mips/tcg-target.c.inc | 19 ++++++----- tcg/ppc/tcg-target.c.inc | 25 +++++++------- tcg/tcg.c | 30 +++++++++++++++++ tcg/tci/tcg-target.c.inc | 30 +++++++++-------- 7 files changed, 118 insertions(+), 70 deletions(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index cebd783285..1c42df1092 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2253,6 +2253,19 @@ static const TCGOutOpMovcond outop_movcond = { .out = tgen_movcond, }; +static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, TCGArg bh, bool const_bh, + TCGLabel *l) +{ + cond = tcg_out_cmp2(s, cond, al, ah, bl, const_bl, bh, const_bh); + tcg_out_goto_label(s, tcg_cond_to_arm_cond[cond], l); +} + +static const TCGOutOpBrcond2 outop_brcond2 = { + .base.static_constraint = C_O0_I4(r, r, rI, rI), + .out = tgen_brcond2, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2335,11 +2348,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mov_reg(s, COND_AL, args[0], a0); break; - case INDEX_op_brcond2_i32: - c = tcg_out_cmp2(s, args[4], args[0], args[1], args[2], const_args[2], - args[3], const_args[3]); - tcg_out_goto_label(s, tcg_cond_to_arm_cond[c], arg_label(args[5])); - break; case INDEX_op_setcond2_i32: c = tcg_out_cmp2(s, args[5], args[1], args[2], args[3], const_args[3], args[4], const_args[4]); @@ -2444,8 +2452,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O2_I4(r, r, r, r, rIN, rIK); case INDEX_op_sub2_i32: return C_O2_I4(r, r, rI, rI, rIN, rIK); - case INDEX_op_brcond2_i32: - return C_O0_I4(r, r, rI, rI); case INDEX_op_setcond2_i32: return C_O1_I4(r, r, r, rI, rI); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index ae3a53a18a..b7708c945f 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1662,42 +1662,52 @@ static const TCGOutOpBrcond outop_brcond = { .out_ri = tgen_brcondi, }; -#if TCG_TARGET_REG_BITS == 32 -static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, - const int *const_args, bool small) +static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, + TCGReg ah, TCGArg bl, bool blconst, + TCGArg bh, bool bhconst, + TCGLabel *label_this, bool small) { TCGLabel *label_next = gen_new_label(); - TCGLabel *label_this = arg_label(args[5]); - TCGCond cond = args[4]; switch (cond) { case TCG_COND_EQ: case TCG_COND_TSTEQ: tcg_out_brcond(s, 0, tcg_invert_cond(cond), - args[0], args[2], const_args[2], label_next, 1); - tcg_out_brcond(s, 0, cond, args[1], args[3], const_args[3], - label_this, small); + al, bl, blconst, label_next, true); + tcg_out_brcond(s, 0, cond, ah, bh, bhconst, label_this, small); break; case TCG_COND_NE: case TCG_COND_TSTNE: - tcg_out_brcond(s, 0, cond, args[0], args[2], const_args[2], - label_this, small); - tcg_out_brcond(s, 0, cond, args[1], args[3], const_args[3], - label_this, small); + tcg_out_brcond(s, 0, cond, al, bl, blconst, label_this, small); + tcg_out_brcond(s, 0, cond, ah, bh, bhconst, label_this, small); break; default: - tcg_out_brcond(s, 0, tcg_high_cond(cond), args[1], - args[3], const_args[3], label_this, small); + tcg_out_brcond(s, 0, tcg_high_cond(cond), + ah, bh, bhconst, label_this, small); tcg_out_jxx(s, JCC_JNE, label_next, 1); - tcg_out_brcond(s, 0, tcg_unsigned_cond(cond), args[0], - args[2], const_args[2], label_this, small); + tcg_out_brcond(s, 0, tcg_unsigned_cond(cond), + al, bl, blconst, label_this, small); break; } tcg_out_label(s, label_next); } + +static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, + TCGReg ah, TCGArg bl, bool blconst, + TCGArg bh, bool bhconst, TCGLabel *l) +{ + tcg_out_brcond2(s, cond, al, ah, bl, blconst, bh, bhconst, l, false); +} + +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) #endif +static const TCGOutOpBrcond2 outop_brcond2 = { + .base.static_constraint = C_O0_I4(r, r, ri, ri), + .out = tgen_brcond2, +}; static void tcg_out_setcond(TCGContext *s, TCGType type, TCGCond cond, TCGReg dest, TCGReg arg1, TCGArg arg2, @@ -1854,11 +1864,8 @@ static const TCGOutOpSetcond outop_negsetcond = { static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, const int *const_args) { - TCGArg new_args[6]; TCGLabel *label_true, *label_over; - memcpy(new_args, args+1, 5*sizeof(TCGArg)); - if (args[0] == args[1] || args[0] == args[2] || (!const_args[3] && args[0] == args[3]) || (!const_args[4] && args[0] == args[4])) { @@ -1867,8 +1874,8 @@ static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, label_true = gen_new_label(); label_over = gen_new_label(); - new_args[5] = label_arg(label_true); - tcg_out_brcond2(s, new_args, const_args+1, 1); + tcg_out_brcond2(s, args[5], args[1], args[2], args[3], const_args[3], + args[4], const_args[4], label_true, true); tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); tcg_out_jxx(s, JCC_JMP, label_over, 1); @@ -1884,9 +1891,10 @@ static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); label_over = gen_new_label(); - new_args[4] = tcg_invert_cond(new_args[4]); - new_args[5] = label_arg(label_over); - tcg_out_brcond2(s, new_args, const_args+1, 1); + tcg_out_brcond2(s, tcg_invert_cond(args[5]), args[1], args[2], + args[3], const_args[3], + args[4], const_args[4], label_over, true); + tgen_arithi(s, ARITH_ADD, args[0], 1, 0); tcg_out_label(s, label_over); @@ -3233,9 +3241,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; #if TCG_TARGET_REG_BITS == 32 - case INDEX_op_brcond2_i32: - tcg_out_brcond2(s, args, const_args, 0); - break; case INDEX_op_setcond2_i32: tcg_out_setcond2(s, args, const_args); break; @@ -4007,9 +4012,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) tcg_debug_assert(TCG_TARGET_REG_BITS == 64); return C_O0_I3(L, L, L); - case INDEX_op_brcond2_i32: - return C_O0_I4(r, r, ri, ri); - case INDEX_op_setcond2_i32: return C_O1_I4(r, r, r, ri, ri); diff --git a/tcg/mips/tcg-target-con-set.h b/tcg/mips/tcg-target-con-set.h index f5e4852b56..9d0ea73f4f 100644 --- a/tcg/mips/tcg-target-con-set.h +++ b/tcg/mips/tcg-target-con-set.h @@ -13,7 +13,7 @@ C_O0_I1(r) C_O0_I2(r, rz) C_O0_I2(rz, r) C_O0_I3(rz, rz, r) -C_O0_I4(rz, rz, rz, rz) +C_O0_I4(r, r, rz, rz) C_O1_I1(r, r) C_O1_I2(r, 0, rz) C_O1_I2(r, r, r) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 3ce71a1c8d..9a9b1bb09a 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1074,8 +1074,9 @@ static void tcg_out_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, tcg_out_setcond_end(s, ret, tmpflags); } -static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, - TCGReg bl, TCGReg bh, TCGLabel *l) +static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh, TCGLabel *l) { int tmpflags = tcg_out_setcond2_int(s, cond, TCG_TMP0, al, ah, bl, bh); TCGReg tmp = tmpflags & ~SETCOND_FLAGS; @@ -1086,6 +1087,14 @@ static void tcg_out_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, tcg_out_nop(s); } +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) +#endif +static const TCGOutOpBrcond2 outop_brcond2 = { + .base.static_constraint = C_O0_I4(r, r, rz, rz), + .out = tgen_brcond2, +}; + static void tgen_movcond(TCGContext *s, TCGType type, TCGCond cond, TCGReg ret, TCGReg c1, TCGArg c2, bool const_c2, TCGArg v1, bool const_v1, TCGArg v2, bool const_v2) @@ -2297,10 +2306,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_brcond2_i32: - tcg_out_brcond2(s, args[4], a0, a1, a2, args[3], arg_label(args[5])); - break; - case INDEX_op_setcond2_i32: tcg_out_setcond2(s, args[5], a0, a1, a2, args[3], args[4]); break; @@ -2401,8 +2406,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O2_I4(r, r, rz, rz, rN, rN); case INDEX_op_setcond2_i32: return C_O1_I4(r, rz, rz, rz, rz); - case INDEX_op_brcond2_i32: - return C_O0_I4(rz, rz, rz, rz); case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 669c5eae4a..cde8a55918 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2283,14 +2283,23 @@ static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, tcg_out_rlw(s, RLWINM, args[0], TCG_REG_R0, CR_EQ + 0*4 + 1, 31, 31); } -static void tcg_out_brcond2(TCGContext *s, const TCGArg *args, - const int *const_args) +static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh, TCGLabel *l) { - tcg_out_cmp2(s, args[4], args[0], args[1], args[2], const_args[2], - args[3], const_args[3]); - tcg_out_bc_lab(s, TCG_COND_EQ, arg_label(args[5])); + assert(TCG_TARGET_REG_BITS == 32); + tcg_out_cmp2(s, cond, al, ah, bl, const_bl, bh, const_bh); + tcg_out_bc_lab(s, TCG_COND_EQ, l); } +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) +#endif +static const TCGOutOpBrcond2 outop_brcond2 = { + .base.static_constraint = C_O0_I4(r, r, rU, rC), + .out = tgen_brcond2, +}; + static void tcg_out_mb(TCGContext *s, TCGArg a0) { uint32_t insn; @@ -3450,10 +3459,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); break; - case INDEX_op_brcond2_i32: - tcg_out_brcond2(s, args, const_args); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; @@ -4272,8 +4277,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: return C_O1_I2(r, 0, rZ); - case INDEX_op_brcond2_i32: - return C_O0_I4(r, r, rU, rC); case INDEX_op_setcond2_i32: return C_O1_I4(r, r, r, rU, rC); case INDEX_op_add2_i64: diff --git a/tcg/tcg.c b/tcg/tcg.c index 3f57f6aafd..2a39ce3665 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -994,6 +994,13 @@ typedef struct TCGOutOpBrcond { TCGReg a1, tcg_target_long a2, TCGLabel *label); } TCGOutOpBrcond; +typedef struct TCGOutOpBrcond2 { + TCGOutOp base; + void (*out)(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh, TCGLabel *l); +} TCGOutOpBrcond2; + typedef struct TCGOutOpDivRem { TCGOutOp base; void (*out_rr01r)(TCGContext *s, TCGType type, @@ -1087,6 +1094,10 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), + +#if TCG_TARGET_REG_BITS == 32 + OUTOP(INDEX_op_brcond2_i32, TCGOutOpBrcond2, outop_brcond2), +#endif }; #undef OUTOP @@ -5540,6 +5551,25 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; +#if TCG_TARGET_REG_BITS == 32 + case INDEX_op_brcond2_i32: + { + const TCGOutOpBrcond2 *out = &outop_brcond2; + TCGCond cond = new_args[4]; + TCGLabel *label = arg_label(new_args[5]); + + tcg_debug_assert(!const_args[0]); + tcg_debug_assert(!const_args[1]); + out->out(s, cond, new_args[0], new_args[1], + new_args[2], const_args[2], + new_args[3], const_args[3], label); + } + break; +#else + case INDEX_op_brcond2_i32: + g_assert_not_reached(); +#endif + default: if (def->flags & TCG_OPF_VECTOR) { tcg_out_vec_op(s, op->opc, type - TCG_TYPE_V64, diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 99a5744ab4..0fe365e2d4 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -87,11 +87,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub2_i64: return C_O2_I4(r, r, r, r, r, r); -#if TCG_TARGET_REG_BITS == 32 - case INDEX_op_brcond2_i32: - return C_O0_I4(r, r, r, r); -#endif - case INDEX_op_setcond2_i32: return C_O1_I4(r, r, r, r, r); @@ -985,6 +980,23 @@ static const TCGOutOpMovcond outop_movcond = { .out = tgen_movcond, }; +static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh, TCGLabel *l) +{ + tcg_out_op_rrrrrc(s, INDEX_op_setcond2_i32, TCG_REG_TMP, + al, ah, bl, bh, cond); + tcg_out_op_rl(s, INDEX_op_brcond, TCG_REG_TMP, l); +} + +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) +#endif +static const TCGOutOpBrcond2 outop_brcond2 = { + .base.static_constraint = C_O0_I4(r, r, r, r), + .out = tgen_brcond2, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1055,14 +1067,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[3], args[4], args[5]); break; -#if TCG_TARGET_REG_BITS == 32 - case INDEX_op_brcond2_i32: - tcg_out_op_rrrrrc(s, INDEX_op_setcond2_i32, TCG_REG_TMP, - args[0], args[1], args[2], args[3], args[4]); - tcg_out_op_rl(s, INDEX_op_brcond, TCG_REG_TMP, arg_label(args[5])); - break; -#endif - case INDEX_op_qemu_ld_i64: case INDEX_op_qemu_st_i64: if (TCG_TARGET_REG_BITS == 32) { From e579c717cb1176d2cd8bd24d9b0e9c6a4b1a0d71 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 16:57:07 -0800 Subject: [PATCH 0445/2760] tcg: Convert setcond2_i32 to TCGOutOpSetcond2 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/arm/tcg-target.c.inc | 25 ++++++------ tcg/i386/tcg-target.c.inc | 71 +++++++++++++++++------------------ tcg/mips/tcg-target-con-set.h | 2 +- tcg/mips/tcg-target.c.inc | 20 ++++++---- tcg/ppc/tcg-target.c.inc | 25 ++++++------ tcg/tcg.c | 19 ++++++++++ tcg/tci/tcg-target.c.inc | 24 ++++++++---- 7 files changed, 110 insertions(+), 76 deletions(-) diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 1c42df1092..8cd82b8baf 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2266,13 +2266,25 @@ static const TCGOutOpBrcond2 outop_brcond2 = { .out = tgen_brcond2, }; +static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh) +{ + cond = tcg_out_cmp2(s, cond, al, ah, bl, const_bl, bh, const_bh); + finish_setcond(s, cond, ret, false); +} + +static const TCGOutOpSetcond2 outop_setcond2 = { + .base.static_constraint = C_O1_I4(r, r, r, rI, rI), + .out = tgen_setcond2, +}; static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { TCGArg a0, a1, a2, a3, a4, a5; - int c; switch (opc) { case INDEX_op_goto_ptr: @@ -2348,14 +2360,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mov_reg(s, COND_AL, args[0], a0); break; - case INDEX_op_setcond2_i32: - c = tcg_out_cmp2(s, args[5], args[1], args[2], args[3], const_args[3], - args[4], const_args[4]); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[c], ARITH_MOV, args[0], 0, 1); - tcg_out_dat_imm(s, tcg_cond_to_arm_cond[tcg_invert_cond(c)], - ARITH_MOV, args[0], 0, 0); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; @@ -2452,9 +2456,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O2_I4(r, r, r, r, rIN, rIK); case INDEX_op_sub2_i32: return C_O2_I4(r, r, rI, rI, rIN, rIK); - case INDEX_op_setcond2_i32: - return C_O1_I4(r, r, r, rI, rI); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, q); case INDEX_op_qemu_ld_i64: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index b7708c945f..6a42ffaf44 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1860,47 +1860,53 @@ static const TCGOutOpSetcond outop_negsetcond = { .out_rri = tgen_negsetcondi, }; -#if TCG_TARGET_REG_BITS == 32 -static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, - const int *const_args) +static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh) { - TCGLabel *label_true, *label_over; + TCGLabel *label_over = gen_new_label(); - if (args[0] == args[1] || args[0] == args[2] - || (!const_args[3] && args[0] == args[3]) - || (!const_args[4] && args[0] == args[4])) { - /* When the destination overlaps with one of the argument - registers, don't do anything tricky. */ - label_true = gen_new_label(); - label_over = gen_new_label(); + if (ret == al || ret == ah + || (!const_bl && ret == bl) + || (!const_bh && ret == bh)) { + /* + * When the destination overlaps with one of the argument + * registers, don't do anything tricky. + */ + TCGLabel *label_true = gen_new_label(); - tcg_out_brcond2(s, args[5], args[1], args[2], args[3], const_args[3], - args[4], const_args[4], label_true, true); + tcg_out_brcond2(s, cond, al, ah, bl, const_bl, + bh, const_bh, label_true, true); - tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); + tcg_out_movi(s, TCG_TYPE_I32, ret, 0); tcg_out_jxx(s, JCC_JMP, label_over, 1); tcg_out_label(s, label_true); - tcg_out_movi(s, TCG_TYPE_I32, args[0], 1); - tcg_out_label(s, label_over); + tcg_out_movi(s, TCG_TYPE_I32, ret, 1); } else { - /* When the destination does not overlap one of the arguments, - clear the destination first, jump if cond false, and emit an - increment in the true case. This results in smaller code. */ - - tcg_out_movi(s, TCG_TYPE_I32, args[0], 0); - - label_over = gen_new_label(); - tcg_out_brcond2(s, tcg_invert_cond(args[5]), args[1], args[2], - args[3], const_args[3], - args[4], const_args[4], label_over, true); + /* + * When the destination does not overlap one of the arguments, + * clear the destination first, jump if cond false, and emit an + * increment in the true case. This results in smaller code. + */ + tcg_out_movi(s, TCG_TYPE_I32, ret, 0); + tcg_out_brcond2(s, tcg_invert_cond(cond), al, ah, bl, const_bl, + bh, const_bh, label_over, true); - tgen_arithi(s, ARITH_ADD, args[0], 1, 0); - tcg_out_label(s, label_over); + tgen_arithi(s, ARITH_ADD, ret, 1, 0); } + tcg_out_label(s, label_over); } + +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) #endif +static const TCGOutOpSetcond2 outop_setcond2 = { + .base.static_constraint = C_O1_I4(r, r, r, ri, ri), + .out = tgen_setcond2, +}; static void tcg_out_cmov(TCGContext *s, int jcc, int rexw, TCGReg dest, TCGReg v1) @@ -3240,11 +3246,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; -#if TCG_TARGET_REG_BITS == 32 - case INDEX_op_setcond2_i32: - tcg_out_setcond2(s, args, const_args); - break; -#else /* TCG_TARGET_REG_BITS == 64 */ +#if TCG_TARGET_REG_BITS == 64 case INDEX_op_ld32s_i64: tcg_out_modrm_offset(s, OPC_MOVSLQ, a0, a1, a2); break; @@ -4012,9 +4014,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) tcg_debug_assert(TCG_TARGET_REG_BITS == 64); return C_O0_I3(L, L, L); - case INDEX_op_setcond2_i32: - return C_O1_I4(r, r, r, ri, ri); - case INDEX_op_ld_vec: case INDEX_op_dupm_vec: return C_O1_I1(x, r); diff --git a/tcg/mips/tcg-target-con-set.h b/tcg/mips/tcg-target-con-set.h index 9d0ea73f4f..4e09c9a400 100644 --- a/tcg/mips/tcg-target-con-set.h +++ b/tcg/mips/tcg-target-con-set.h @@ -25,7 +25,7 @@ C_O1_I2(r, r, rz) C_O1_I2(r, r, rzW) C_O1_I4(r, r, rz, rz, 0) C_O1_I4(r, r, rz, rz, rz) -C_O1_I4(r, rz, rz, rz, rz) +C_O1_I4(r, r, r, rz, rz) C_O2_I1(r, r, r) C_O2_I2(r, r, r, r) C_O2_I4(r, r, rz, rz, rN, rN) diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 9a9b1bb09a..e8ae65bccb 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1067,13 +1067,23 @@ static int tcg_out_setcond2_int(TCGContext *s, TCGCond cond, TCGReg ret, return ret | flags; } -static void tcg_out_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, - TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) +static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh) { int tmpflags = tcg_out_setcond2_int(s, cond, ret, al, ah, bl, bh); tcg_out_setcond_end(s, ret, tmpflags); } +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) +#endif +static const TCGOutOpSetcond2 outop_setcond2 = { + .base.static_constraint = C_O1_I4(r, r, r, rz, rz), + .out = tgen_setcond2, +}; + static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, TCGArg bl, bool const_bl, TCGArg bh, bool const_bh, TCGLabel *l) @@ -2306,10 +2316,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_setcond2_i32: - tcg_out_setcond2(s, args[5], a0, a1, a2, args[3], args[4]); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, 0, a1, a2, TCG_TYPE_I32); break; @@ -2404,8 +2410,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_add2_i32: case INDEX_op_sub2_i32: return C_O2_I4(r, r, rz, rz, rN, rN); - case INDEX_op_setcond2_i32: - return C_O1_I4(r, rz, rz, rz, rz); case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index cde8a55918..4cdbf246d2 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2274,15 +2274,24 @@ static void tcg_out_cmp2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, } } -static void tcg_out_setcond2(TCGContext *s, const TCGArg *args, - const int *const_args) +static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh) { - tcg_out_cmp2(s, args[5], args[1], args[2], args[3], const_args[3], - args[4], const_args[4]); + tcg_out_cmp2(s, cond, al, ah, bl, const_bl, bh, const_bh); tcg_out32(s, MFOCRF | RT(TCG_REG_R0) | FXM(0)); - tcg_out_rlw(s, RLWINM, args[0], TCG_REG_R0, CR_EQ + 0*4 + 1, 31, 31); + tcg_out_rlw(s, RLWINM, ret, TCG_REG_R0, CR_EQ + 0*4 + 1, 31, 31); } +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) +#endif +static const TCGOutOpSetcond2 outop_setcond2 = { + .base.static_constraint = C_O1_I4(r, r, r, rU, rC), + .out = tgen_setcond2, +}; + static void tgen_brcond2(TCGContext *s, TCGCond cond, TCGReg al, TCGReg ah, TCGArg bl, bool const_bl, TCGArg bh, bool const_bh, TCGLabel *l) @@ -3491,10 +3500,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; - case INDEX_op_setcond2_i32: - tcg_out_setcond2(s, args, const_args); - break; - case INDEX_op_bswap16_i32: case INDEX_op_bswap16_i64: tcg_out_bswap16(s, args[0], args[1], args[2]); @@ -4277,8 +4282,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_deposit_i32: case INDEX_op_deposit_i64: return C_O1_I2(r, 0, rZ); - case INDEX_op_setcond2_i32: - return C_O1_I4(r, r, r, rU, rC); case INDEX_op_add2_i64: case INDEX_op_add2_i32: return C_O2_I4(r, r, r, r, rI, rZM); diff --git a/tcg/tcg.c b/tcg/tcg.c index 2a39ce3665..735a7b95d4 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1033,6 +1033,12 @@ typedef struct TCGOutOpSetcond { TCGReg ret, TCGReg a1, tcg_target_long a2); } TCGOutOpSetcond; +typedef struct TCGOutOpSetcond2 { + TCGOutOp base; + void (*out)(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, TCGArg bh, bool const_bh); +} TCGOutOpSetcond2; + typedef struct TCGOutOpSubtract { TCGOutOp base; void (*out_rrr)(TCGContext *s, TCGType type, @@ -1097,6 +1103,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { #if TCG_TARGET_REG_BITS == 32 OUTOP(INDEX_op_brcond2_i32, TCGOutOpBrcond2, outop_brcond2), + OUTOP(INDEX_op_setcond2_i32, TCGOutOpSetcond2, outop_setcond2), #endif }; @@ -5565,8 +5572,20 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) new_args[3], const_args[3], label); } break; + case INDEX_op_setcond2_i32: + { + const TCGOutOpSetcond2 *out = &outop_setcond2; + TCGCond cond = new_args[5]; + + tcg_debug_assert(!const_args[1]); + tcg_debug_assert(!const_args[2]); + out->out(s, cond, new_args[0], new_args[1], new_args[2], + new_args[3], const_args[3], new_args[4], const_args[4]); + } + break; #else case INDEX_op_brcond2_i32: + case INDEX_op_setcond2_i32: g_assert_not_reached(); #endif diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 0fe365e2d4..88dc7e24e3 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -87,9 +87,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_sub2_i64: return C_O2_I4(r, r, r, r, r, r); - case INDEX_op_setcond2_i32: - return C_O1_I4(r, r, r, r, r); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); case INDEX_op_qemu_ld_i64: @@ -997,6 +994,22 @@ static const TCGOutOpBrcond2 outop_brcond2 = { .out = tgen_brcond2, }; +static void tgen_setcond2(TCGContext *s, TCGCond cond, TCGReg ret, + TCGReg al, TCGReg ah, + TCGArg bl, bool const_bl, + TCGArg bh, bool const_bh) +{ + tcg_out_op_rrrrrc(s, INDEX_op_setcond2_i32, ret, al, ah, bl, bh, cond); +} + +#if TCG_TARGET_REG_BITS != 32 +__attribute__((unused)) +#endif +static const TCGOutOpSetcond2 outop_setcond2 = { + .base.static_constraint = C_O1_I4(r, r, r, r, r), + .out = tgen_setcond2, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1012,11 +1025,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_l(s, opc, arg_label(args[0])); break; - case INDEX_op_setcond2_i32: - tcg_out_op_rrrrrc(s, opc, args[0], args[1], args[2], - args[3], args[4], args[5]); - break; - CASE_32_64(ld8u) CASE_32_64(ld8s) CASE_32_64(ld16u) From 5fa8e13872b0ecab7e1bc1f75c65281983df52e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 18:32:08 -0800 Subject: [PATCH 0446/2760] tcg: Convert bswap16 to TCGOutOpBswap Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 - tcg/aarch64/tcg-target.c.inc | 30 +++++++------ tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 42 +++++++++--------- tcg/i386/tcg-target-has.h | 2 - tcg/i386/tcg-target.c.inc | 48 +++++++++++--------- tcg/loongarch64/tcg-target-has.h | 2 - tcg/loongarch64/tcg-target.c.inc | 28 +++++++----- tcg/mips/tcg-target-has.h | 2 - tcg/mips/tcg-target.c.inc | 74 +++++++++++++++---------------- tcg/ppc/tcg-target-has.h | 2 - tcg/ppc/tcg-target.c.inc | 76 ++++++++++++++++---------------- tcg/riscv/tcg-target-has.h | 2 - tcg/riscv/tcg-target.c.inc | 33 +++++++++----- tcg/s390x/tcg-target-has.h | 2 - tcg/s390x/tcg-target.c.inc | 40 ++++++++--------- tcg/sparc64/tcg-target-has.h | 2 - tcg/sparc64/tcg-target.c.inc | 4 ++ tcg/tcg-has.h | 1 - tcg/tcg-op.c | 4 +- tcg/tcg.c | 23 ++++++++-- tcg/tci.c | 2 - tcg/tci/tcg-target-has.h | 2 - tcg/tci/tcg-target.c.inc | 21 ++++++--- 24 files changed, 235 insertions(+), 210 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 22a574e703..4797409467 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -13,7 +13,6 @@ #define have_lse2 (cpuinfo & CPUINFO_LSE2) /* optional instructions */ -#define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -21,7 +20,6 @@ #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index ee45e7e244..03961b34aa 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2438,6 +2438,23 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_rev(s, TCG_TYPE_I32, MO_16, a0, a1); + if (flags & TCG_BSWAP_OS) { + /* Output must be sign-extended. */ + tcg_out_ext16s(s, type, a0, a0); + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + /* Output must be zero-extended, but input isn't. */ + tcg_out_ext16u(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { @@ -2618,17 +2635,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, case INDEX_op_bswap32_i32: tcg_out_rev(s, TCG_TYPE_I32, MO_32, a0, a1); break; - case INDEX_op_bswap16_i64: - case INDEX_op_bswap16_i32: - tcg_out_rev(s, TCG_TYPE_I32, MO_16, a0, a1); - if (a2 & TCG_BSWAP_OS) { - /* Output must be sign-extended. */ - tcg_out_ext16s(s, ext, a0, a0); - } else if ((a2 & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - /* Output must be zero-extended, but input isn't. */ - tcg_out_ext16u(s, a0, a0); - } - break; case INDEX_op_deposit_i64: case INDEX_op_deposit_i32: @@ -3148,9 +3154,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: - case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: case INDEX_op_ext_i32_i64: diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index bfa3be8028..5972def558 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -24,7 +24,6 @@ extern bool use_neon_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 8cd82b8baf..6928f209d2 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -969,23 +969,6 @@ static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rn) g_assert_not_reached(); } -static void tcg_out_bswap16(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int flags) -{ - if (flags & TCG_BSWAP_OS) { - /* revsh */ - tcg_out32(s, 0x06ff0fb0 | (cond << 28) | (rd << 12) | rn); - return; - } - - /* rev16 */ - tcg_out32(s, 0x06bf0fb0 | (cond << 28) | (rd << 12) | rn); - if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - /* uxth */ - tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | rd); - } -} - static void tcg_out_bswap32(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn) { /* rev */ @@ -2153,6 +2136,27 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg rd, TCGReg rn, unsigned flags) +{ + if (flags & TCG_BSWAP_OS) { + /* revsh */ + tcg_out32(s, 0x06ff0fb0 | (COND_AL << 28) | (rd << 12) | rn); + return; + } + + /* rev16 */ + tcg_out32(s, 0x06bf0fb0 | (COND_AL << 28) | (rd << 12) | rn); + if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_ext16u(s, rd, rd); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_subfi(s, type, a0, 0, a1); @@ -2374,9 +2378,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); break; - case INDEX_op_bswap16_i32: - tcg_out_bswap16(s, COND_AL, args[0], args[1], args[2]); - break; case INDEX_op_bswap32_i32: tcg_out_bswap32(s, COND_AL, args[0], args[1]); break; @@ -2437,7 +2438,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: case INDEX_op_extract_i32: case INDEX_op_sextract_i32: diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index aaf8764cc9..fd44ed8168 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -26,7 +26,6 @@ #define have_avx512vbmi2 ((cpuinfo & CPUINFO_AVX512VBMI2) && have_avx512vl) /* optional instructions */ -#define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_add2_i32 1 @@ -35,7 +34,6 @@ #if TCG_TARGET_REG_BITS == 64 /* Keep 32-bit values zero-extended in a register. */ #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 6a42ffaf44..c74a718cee 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3062,6 +3062,34 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + if (flags & TCG_BSWAP_OS) { + /* Output must be sign-extended. */ + if (rexw) { + tcg_out_bswap64(s, a0); + tcg_out_shifti(s, SHIFT_SAR + rexw, a0, 48); + } else { + tcg_out_bswap32(s, a0); + tcg_out_shifti(s, SHIFT_SAR, a0, 16); + } + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + /* Output must be zero-extended, but input isn't. */ + tcg_out_bswap32(s, a0); + tcg_out_shifti(s, SHIFT_SHR, a0, 16); + } else { + tcg_out_rolw_8(s, a0); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, 0), + .out_rr = tgen_bswap16, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; @@ -3165,24 +3193,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(bswap16): - if (a2 & TCG_BSWAP_OS) { - /* Output must be sign-extended. */ - if (rexw) { - tcg_out_bswap64(s, a0); - tcg_out_shifti(s, SHIFT_SAR + rexw, a0, 48); - } else { - tcg_out_bswap32(s, a0); - tcg_out_shifti(s, SHIFT_SAR, a0, 16); - } - } else if ((a2 & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - /* Output must be zero-extended, but input isn't. */ - tcg_out_bswap32(s, a0); - tcg_out_shifti(s, SHIFT_SHR, a0, 16); - } else { - tcg_out_rolw_8(s, a0); - } - break; OP_32_64(bswap32): tcg_out_bswap32(s, a0); if (rexw && (a2 & TCG_BSWAP_OS)) { @@ -3962,8 +3972,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i32: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 90f0a131ae..11a93afd8b 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -13,14 +13,12 @@ #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 -#define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 /* 64-bit operations */ #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_add2_i64 0 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index c731096c64..8be6f69e3a 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1735,6 +1735,22 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_opc_revb_2h(s, a0, a1); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext16s(s, TCG_TYPE_REG, a0, a0); + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_ext16u(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); @@ -1826,16 +1842,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_bstrins_d(s, a0, a2, args[3], args[3] + args[4] - 1); break; - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - tcg_out_opc_revb_2h(s, a0, a1); - if (a2 & TCG_BSWAP_OS) { - tcg_out_ext16s(s, TCG_TYPE_REG, a0, a0); - } else if ((a2 & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - tcg_out_ext16u(s, a0, a0); - } - break; - case INDEX_op_bswap32_i32: /* All 32-bit values are computed sign-extended in the register. */ a2 = TCG_BSWAP_OS; @@ -2448,8 +2454,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i32: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index c6cecba28b..6c967d9c9f 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -39,7 +39,6 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #if TCG_TARGET_REG_BITS == 64 @@ -57,7 +56,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index e8ae65bccb..258b49f9db 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -702,39 +702,6 @@ static void tcg_out_addi_ptr(TCGContext *s, TCGReg rd, TCGReg rs, g_assert_not_reached(); } -static void tcg_out_bswap16(TCGContext *s, TCGReg ret, TCGReg arg, int flags) -{ - /* ret and arg can't be register tmp0 */ - tcg_debug_assert(ret != TCG_TMP0); - tcg_debug_assert(arg != TCG_TMP0); - - /* With arg = abcd: */ - if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); /* badc */ - if (flags & TCG_BSWAP_OS) { - tcg_out_opc_reg(s, OPC_SEH, ret, 0, ret); /* ssdc */ - } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xffff); /* 00dc */ - } - return; - } - - tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8); /* 0abc */ - if (!(flags & TCG_BSWAP_IZ)) { - tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, TCG_TMP0, 0x00ff); /* 000c */ - } - if (flags & TCG_BSWAP_OS) { - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); /* d000 */ - tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); /* ssd0 */ - } else { - tcg_out_opc_sa(s, OPC_SLL, ret, arg, 8); /* bcd0 */ - if (flags & TCG_BSWAP_OZ) { - tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xff00); /* 00d0 */ - } - } - tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); /* ssdc */ -} - static void tcg_out_bswap_subr(TCGContext *s, const tcg_insn_unit *sub) { if (!tcg_out_opc_jmp(s, OPC_JAL, sub)) { @@ -2168,6 +2135,41 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg ret, TCGReg arg, unsigned flags) +{ + /* With arg = abcd: */ + if (use_mips32r2_instructions) { + tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); /* badc */ + if (flags & TCG_BSWAP_OS) { + tcg_out_opc_reg(s, OPC_SEH, ret, 0, ret); /* ssdc */ + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xffff); /* 00dc */ + } + return; + } + + tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, arg, 8); /* 0abc */ + if (!(flags & TCG_BSWAP_IZ)) { + tcg_out_opc_imm(s, OPC_ANDI, TCG_TMP0, TCG_TMP0, 0x00ff); /* 000c */ + } + if (flags & TCG_BSWAP_OS) { + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 24); /* d000 */ + tcg_out_opc_sa(s, OPC_SRA, ret, ret, 16); /* ssd0 */ + } else { + tcg_out_opc_sa(s, OPC_SLL, ret, arg, 8); /* bcd0 */ + if (flags & TCG_BSWAP_OZ) { + tcg_out_opc_imm(s, OPC_ANDI, ret, ret, 0xff00); /* 00d0 */ + } + } + tcg_out_opc_reg(s, OPC_OR, ret, ret, TCG_TMP0); /* ssdc */ +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); @@ -2259,10 +2261,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - tcg_out_bswap16(s, a0, a1, a2); - break; case INDEX_op_bswap32_i32: tcg_out_bswap32(s, a0, a1, 0); break; @@ -2373,7 +2371,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: case INDEX_op_extract_i32: case INDEX_op_sextract_i32: @@ -2384,7 +2381,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: case INDEX_op_ext_i32_i64: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 5c4fc2bc34..b73fca9789 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -17,7 +17,6 @@ #define have_vsx (cpuinfo & CPUINFO_VSX) /* optional instructions */ -#define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -26,7 +25,6 @@ #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 4cdbf246d2..3454254624 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -1012,38 +1012,6 @@ static void tcg_out_addpcis(TCGContext *s, TCGReg dst, intptr_t imm) tcg_out32(s, ADDPCIS | RT(dst) | (d1 << 16) | (d0 << 6) | d2); } -static void tcg_out_bswap16(TCGContext *s, TCGReg dst, TCGReg src, int flags) -{ - TCGReg tmp = dst == src ? TCG_REG_R0 : dst; - - if (have_isa_3_10) { - tcg_out32(s, BRH | RA(dst) | RS(src)); - if (flags & TCG_BSWAP_OS) { - tcg_out_ext16s(s, TCG_TYPE_REG, dst, dst); - } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - tcg_out_ext16u(s, dst, dst); - } - return; - } - - /* - * In the following, - * dep(a, b, m) -> (a & ~m) | (b & m) - * - * Begin with: src = xxxxabcd - */ - /* tmp = rol32(src, 24) & 0x000000ff = 0000000c */ - tcg_out_rlw(s, RLWINM, tmp, src, 24, 24, 31); - /* tmp = dep(tmp, rol32(src, 8), 0x0000ff00) = 000000dc */ - tcg_out_rlw(s, RLWIMI, tmp, src, 8, 16, 23); - - if (flags & TCG_BSWAP_OS) { - tcg_out_ext16s(s, TCG_TYPE_REG, dst, tmp); - } else { - tcg_out_mov(s, TCG_TYPE_REG, dst, tmp); - } -} - static void tcg_out_bswap32(TCGContext *s, TCGReg dst, TCGReg src, int flags) { TCGReg tmp = dst == src ? TCG_REG_R0 : dst; @@ -3378,6 +3346,44 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg dst, TCGReg src, unsigned flags) +{ + TCGReg tmp = dst == src ? TCG_REG_R0 : dst; + + if (have_isa_3_10) { + tcg_out32(s, BRH | RA(dst) | RS(src)); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext16s(s, TCG_TYPE_REG, dst, dst); + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_ext16u(s, dst, dst); + } + return; + } + + /* + * In the following, + * dep(a, b, m) -> (a & ~m) | (b & m) + * + * Begin with: src = xxxxabcd + */ + /* tmp = rol32(src, 24) & 0x000000ff = 0000000c */ + tcg_out_rlw(s, RLWINM, tmp, src, 24, 24, 31); + /* tmp = dep(tmp, rol32(src, 8), 0x0000ff00) = 000000dc */ + tcg_out_rlw(s, RLWIMI, tmp, src, 8, 16, 23); + + if (flags & TCG_BSWAP_OS) { + tcg_out_ext16s(s, TCG_TYPE_REG, dst, tmp); + } else { + tcg_out_mov(s, TCG_TYPE_REG, dst, tmp); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tcg_out32(s, NEG | RT(a0) | RA(a1)); @@ -3500,10 +3506,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: - tcg_out_bswap16(s, args[0], args[1], args[2]); - break; case INDEX_op_bswap32_i32: tcg_out_bswap32(s, args[0], args[1], 0); break; @@ -4250,7 +4252,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: case INDEX_op_extract_i32: case INDEX_op_sextract_i32: @@ -4263,7 +4264,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: case INDEX_op_extract_i64: diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index e18b5cb8ec..85bb5cd591 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -13,13 +13,11 @@ #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_bswap16_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_bswap16_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 8d106d7f28..c6cd2100f8 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2402,6 +2402,28 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static TCGConstraintSetIndex cset_bswap(TCGType type, unsigned flags) +{ + return cpuinfo & CPUINFO_ZBB ? C_O1_I1(r, r) : C_NotImplemented; +} + +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); + if (flags & TCG_BSWAP_OZ) { + tcg_out_opc_imm(s, OPC_SRLI, a0, a0, 48); + } else { + tcg_out_opc_imm(s, OPC_SRAI, a0, a0, 48); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_bswap, + .out_rr = tgen_bswap16, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); @@ -2498,15 +2520,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_imm(s, OPC_SRAI, a0, a0, 32); } break; - case INDEX_op_bswap16_i64: - case INDEX_op_bswap16_i32: - tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); - if (a2 & TCG_BSWAP_OZ) { - tcg_out_opc_imm(s, OPC_SRLI, a0, a0, 48); - } else { - tcg_out_opc_imm(s, OPC_SRAI, a0, a0, 48); - } - break; case INDEX_op_add2_i32: tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], @@ -2845,9 +2858,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: - case INDEX_op_bswap16_i32: case INDEX_op_bswap32_i32: - case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: return C_O1_I1(r, r); diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 41cd8a1d0d..6cd92fa240 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -29,7 +29,6 @@ extern uint64_t s390_facilities[3]; ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) /* optional instructions */ -#define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 @@ -37,7 +36,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index fbf39ca529..e90c03628a 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2741,6 +2741,25 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori_3, }; +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRE, LRVR, a0, a1); + tcg_out_sh32(s, (flags & TCG_BSWAP_OS ? RS_SRA : RS_SRL), + a0, TCG_REG_NONE, 16); + } else { + tcg_out_insn(s, RRE, LRVGR, a0, a1); + tcg_out_sh64(s, (flags & TCG_BSWAP_OS ? RSY_SRAG : RSY_SRLG), + a0, a0, TCG_REG_NONE, 48); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { if (type == TCG_TYPE_I32) { @@ -2827,25 +2846,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_bswap16_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - tcg_out_insn(s, RRE, LRVR, a0, a1); - if (a2 & TCG_BSWAP_OS) { - tcg_out_sh32(s, RS_SRA, a0, TCG_REG_NONE, 16); - } else { - tcg_out_sh32(s, RS_SRL, a0, TCG_REG_NONE, 16); - } - break; - case INDEX_op_bswap16_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - tcg_out_insn(s, RRE, LRVGR, a0, a1); - if (a2 & TCG_BSWAP_OS) { - tcg_out_sh64(s, RSY_SRAG, a0, a0, TCG_REG_NONE, 48); - } else { - tcg_out_sh64(s, RSY_SRLG, a0, a0, TCG_REG_NONE, 48); - } - break; - case INDEX_op_bswap32_i32: tcg_out_insn(s, RRE, LRVR, args[0], args[1]); break; @@ -3459,8 +3459,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i32: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 6ed27b8fcc..eb1e16c0e2 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -14,7 +14,6 @@ extern bool use_vis3_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_bswap16_i32 0 #define TCG_TARGET_HAS_bswap32_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 @@ -22,7 +21,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index d99b9e42ce..5111f173e1 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1725,6 +1725,10 @@ static const TCGOutOpBinary outop_xor = { .out_rri = tgen_xori, }; +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_G0, a1); diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 315dfd05aa..3d1c805d59 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -12,7 +12,6 @@ #if TCG_TARGET_REG_BITS == 32 /* Turn some undef macros into false macros. */ #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap16_i64 0 #define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 3527952c66..c5b3bc8148 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1257,7 +1257,7 @@ void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags) /* Only one extension flag may be present. */ tcg_debug_assert(!(flags & TCG_BSWAP_OS) || !(flags & TCG_BSWAP_OZ)); - if (TCG_TARGET_HAS_bswap16_i32) { + if (tcg_op_supported(INDEX_op_bswap16_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3i_i32(INDEX_op_bswap16_i32, ret, arg, flags); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); @@ -2087,7 +2087,7 @@ void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) } else { tcg_gen_movi_i32(TCGV_HIGH(ret), 0); } - } else if (TCG_TARGET_HAS_bswap16_i64) { + } else if (tcg_op_supported(INDEX_op_bswap16_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3i_i64(INDEX_op_bswap16_i64, ret, arg, flags); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index 735a7b95d4..25834f40a0 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1001,6 +1001,12 @@ typedef struct TCGOutOpBrcond2 { TCGArg bh, bool const_bh, TCGLabel *l); } TCGOutOpBrcond2; +typedef struct TCGOutOpBswap { + TCGOutOp base; + void (*out_rr)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags); +} TCGOutOpBswap; + typedef struct TCGOutOpDivRem { TCGOutOp base; void (*out_rr01r)(TCGContext *s, TCGType type, @@ -1069,6 +1075,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_brcond, TCGOutOpBrcond, outop_brcond), + OUTOP(INDEX_op_bswap16_i32, TCGOutOpBswap, outop_bswap16), + OUTOP(INDEX_op_bswap16_i64, TCGOutOpBswap, outop_bswap16), OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), OUTOP(INDEX_op_ctpop, TCGOutOpUnary, outop_ctpop), OUTOP(INDEX_op_ctz, TCGOutOpBinary, outop_ctz), @@ -2335,8 +2343,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_add2_i32; case INDEX_op_sub2_i32: return TCG_TARGET_HAS_sub2_i32; - case INDEX_op_bswap16_i32: - return TCG_TARGET_HAS_bswap16_i32; case INDEX_op_bswap32_i32: return TCG_TARGET_HAS_bswap32_i32; @@ -2367,8 +2373,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: return TCG_TARGET_HAS_extr_i64_i32; - case INDEX_op_bswap16_i64: - return TCG_TARGET_HAS_bswap16_i64; case INDEX_op_bswap32_i64: return TCG_TARGET_HAS_bswap32_i64; case INDEX_op_bswap64_i64: @@ -5485,6 +5489,17 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_bswap16_i32: + case INDEX_op_bswap16_i64: + { + const TCGOutOpBswap *out = + container_of(all_outop[op->opc], TCGOutOpBswap, base); + + tcg_debug_assert(!const_args[1]); + out->out_rr(s, type, new_args[0], new_args[1], new_args[2]); + } + break; + case INDEX_op_divs2: case INDEX_op_divu2: { diff --git a/tcg/tci.c b/tcg/tci.c index 9c3f58242e..ae447e91bd 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -686,12 +686,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_write_reg64(regs, r1, r0, T1 - T2); break; #endif -#if TCG_TARGET_HAS_bswap16_i32 || TCG_TARGET_HAS_bswap16_i64 CASE_32_64(bswap16) tci_args_rr(insn, &r0, &r1); regs[r0] = bswap16(regs[r1]); break; -#endif #if TCG_TARGET_HAS_bswap32_i32 || TCG_TARGET_HAS_bswap32_i64 CASE_32_64(bswap32) tci_args_rr(insn, &r0, &r1); diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index f45a0688f9..d7228246ab 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -7,14 +7,12 @@ #ifndef TCG_TARGET_HAS_H #define TCG_TARGET_HAS_H -#define TCG_TARGET_HAS_bswap16_i32 1 #define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap16_i64 1 #define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 88dc7e24e3..2a8ba07e37 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -57,8 +57,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i32: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: @@ -904,6 +902,20 @@ static const TCGOutOpUnary outop_ctpop = { .out_rr = tgen_ctpop, }; +static void tgen_bswap16(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_op_rr(s, INDEX_op_bswap16_i32, a0, a1); + if (flags & TCG_BSWAP_OS) { + tcg_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 16); + } +} + +static const TCGOutOpBswap outop_bswap16 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap16, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tcg_out_op_rr(s, INDEX_op_neg, a0, a1); @@ -1055,13 +1067,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_rr(s, opc, args[0], args[1]); break; - case INDEX_op_bswap16_i32: /* Optional (TCG_TARGET_HAS_bswap16_i32). */ - case INDEX_op_bswap16_i64: /* Optional (TCG_TARGET_HAS_bswap16_i64). */ - width = 16; - goto do_bswap; case INDEX_op_bswap32_i64: /* Optional (TCG_TARGET_HAS_bswap32_i64). */ width = 32; - do_bswap: /* The base tci bswaps zero-extend, and ignore high bits. */ tcg_out_op_rr(s, opc, args[0], args[1]); if (args[2] & TCG_BSWAP_OS) { From 0dd07ee1122abaf1adb4f1e00a8e0b89937f53bd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 18:51:16 -0800 Subject: [PATCH 0447/2760] tcg: Merge INDEX_op_bswap16_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 7 +++---- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 9 +++------ tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 15 insertions(+), 21 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 26dc3bad49..509cfe7db1 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -415,7 +415,7 @@ Misc - | *t0* = *t1* | Move *t1* to *t0*. - * - bswap16_i32/i64 *t0*, *t1*, *flags* + * - bswap16 *t0*, *t1*, *flags* - | 16 bit byte swap on the low bits of a 32/64 bit input. | diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 5e085607d5..acfbaa05b4 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -43,6 +43,7 @@ DEF(mov, 1, 1, 0, TCG_OPF_INT | TCG_OPF_NOT_PRESENT) DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) +DEF(bswap16, 1, 1, 1, TCG_OPF_INT) DEF(clz, 1, 2, 0, TCG_OPF_INT) DEF(ctpop, 1, 1, 0, TCG_OPF_INT) DEF(ctz, 1, 2, 0, TCG_OPF_INT) @@ -95,7 +96,6 @@ DEF(sub2_i32, 2, 4, 0, 0) DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(setcond2_i32, 1, 4, 1, 0) -DEF(bswap16_i32, 1, 1, 1, 0) DEF(bswap32_i32, 1, 1, 1, 0) /* load/store */ @@ -122,7 +122,6 @@ DEF(extu_i32_i64, 1, 1, 0, 0) DEF(extrl_i64_i32, 1, 1, 0, 0) DEF(extrh_i64_i32, 1, 1, 0, 0) -DEF(bswap16_i64, 1, 1, 1, 0) DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index 54606388cc..1d535a9fae 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -518,7 +518,7 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, case INDEX_op_ctpop: return type == TCG_TYPE_I32 ? ctpop32(x) : ctpop64(x); - CASE_OP_32_64(bswap16): + case INDEX_op_bswap16: x = bswap16(x); return y & TCG_BSWAP_OS ? (int16_t)x : x; @@ -1572,8 +1572,7 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) z_mask = t1->z_mask; switch (op->opc) { - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: + case INDEX_op_bswap16: z_mask = bswap16(z_mask); sign = INT16_MIN; break; @@ -2870,7 +2869,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_brcond2_i32: done = fold_brcond2(&ctx, op); break; - CASE_OP_32_64(bswap16): + case INDEX_op_bswap16: CASE_OP_32_64(bswap32): case INDEX_op_bswap64_i64: done = fold_bswap(&ctx, op); diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index c5b3bc8148..917f52b04a 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1257,8 +1257,8 @@ void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags) /* Only one extension flag may be present. */ tcg_debug_assert(!(flags & TCG_BSWAP_OS) || !(flags & TCG_BSWAP_OZ)); - if (tcg_op_supported(INDEX_op_bswap16_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3i_i32(INDEX_op_bswap16_i32, ret, arg, flags); + if (tcg_op_supported(INDEX_op_bswap16, TCG_TYPE_I32, 0)) { + tcg_gen_op3i_i32(INDEX_op_bswap16, ret, arg, flags); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 t1 = tcg_temp_ebb_new_i32(); @@ -2087,8 +2087,8 @@ void tcg_gen_bswap16_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) } else { tcg_gen_movi_i32(TCGV_HIGH(ret), 0); } - } else if (tcg_op_supported(INDEX_op_bswap16_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3i_i64(INDEX_op_bswap16_i64, ret, arg, flags); + } else if (tcg_op_supported(INDEX_op_bswap16, TCG_TYPE_I64, 0)) { + tcg_gen_op3i_i64(INDEX_op_bswap16, ret, arg, flags); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index 25834f40a0..ae68ce88b7 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1075,8 +1075,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_brcond, TCGOutOpBrcond, outop_brcond), - OUTOP(INDEX_op_bswap16_i32, TCGOutOpBswap, outop_bswap16), - OUTOP(INDEX_op_bswap16_i64, TCGOutOpBswap, outop_bswap16), + OUTOP(INDEX_op_bswap16, TCGOutOpBswap, outop_bswap16), OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), OUTOP(INDEX_op_ctpop, TCGOutOpUnary, outop_ctpop), OUTOP(INDEX_op_ctz, TCGOutOpBinary, outop_ctz), @@ -2941,8 +2940,7 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) i = 1; } break; - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: + case INDEX_op_bswap16: case INDEX_op_bswap32_i32: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: @@ -5489,8 +5487,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: + case INDEX_op_bswap16: { const TCGOutOpBswap *out = container_of(all_outop[op->opc], TCGOutOpBswap, base); diff --git a/tcg/tci.c b/tcg/tci.c index ae447e91bd..905ca154fc 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -686,7 +686,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_write_reg64(regs, r1, r0, T1 - T2); break; #endif - CASE_32_64(bswap16) + case INDEX_op_bswap16: tci_args_rr(insn, &r0, &r1); regs[r0] = bswap16(regs[r1]); break; @@ -1005,14 +1005,13 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) op_name, str_r(r0), str_r(r1), s2); break; + case INDEX_op_bswap16: case INDEX_op_ctpop: case INDEX_op_mov: case INDEX_op_neg: case INDEX_op_not: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_bswap16_i32: - case INDEX_op_bswap16_i64: case INDEX_op_bswap32_i32: case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 2a8ba07e37..4d3d9569cc 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -905,7 +905,7 @@ static const TCGOutOpUnary outop_ctpop = { static void tgen_bswap16(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, unsigned flags) { - tcg_out_op_rr(s, INDEX_op_bswap16_i32, a0, a1); + tcg_out_op_rr(s, INDEX_op_bswap16, a0, a1); if (flags & TCG_BSWAP_OS) { tcg_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 16); } From d7b15a25a707f977e39af20c9f14a5ac4971c762 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 19:49:20 -0800 Subject: [PATCH 0448/2760] tcg: Convert bswap32 to TCGOutOpBswap Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 - tcg/aarch64/tcg-target.c.inc | 25 +++++----- tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 23 ++++----- tcg/i386/tcg-target-has.h | 2 - tcg/i386/tcg-target.c.inc | 23 +++++---- tcg/loongarch64/tcg-target-has.h | 2 - tcg/loongarch64/tcg-target.c.inc | 33 +++++++------ tcg/mips/tcg-target-has.h | 2 - tcg/mips/tcg-target.c.inc | 54 ++++++++++---------- tcg/ppc/tcg-target-has.h | 2 - tcg/ppc/tcg-target.c.inc | 84 ++++++++++++++++---------------- tcg/riscv/tcg-target-has.h | 2 - tcg/riscv/tcg-target.c.inc | 30 +++++++----- tcg/s390x/tcg-target-has.h | 2 - tcg/s390x/tcg-target.c.inc | 31 ++++++------ tcg/sparc64/tcg-target-has.h | 2 - tcg/sparc64/tcg-target.c.inc | 4 ++ tcg/tcg-has.h | 1 - tcg/tcg-op.c | 4 +- tcg/tcg.c | 8 +-- tcg/tci.c | 2 - tcg/tci/tcg-target-has.h | 2 - tcg/tci/tcg-target.c.inc | 28 +++++------ 24 files changed, 182 insertions(+), 187 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 4797409467..7c3d3fc637 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -13,14 +13,12 @@ #define have_lse2 (cpuinfo & CPUINFO_LSE2) /* optional instructions */ -#define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 03961b34aa..a2e45ca5c8 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2456,6 +2456,20 @@ static const TCGOutOpBswap outop_bswap16 = { .out_rr = tgen_bswap16, }; +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_rev(s, TCG_TYPE_I32, MO_32, a0, a1); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext32s(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_XZR, a1); @@ -2626,15 +2640,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, case INDEX_op_bswap64_i64: tcg_out_rev(s, TCG_TYPE_I64, MO_64, a0, a1); break; - case INDEX_op_bswap32_i64: - tcg_out_rev(s, TCG_TYPE_I32, MO_32, a0, a1); - if (a2 & TCG_BSWAP_OS) { - tcg_out_ext32s(s, a0, a0); - } - break; - case INDEX_op_bswap32_i32: - tcg_out_rev(s, TCG_TYPE_I32, MO_32, a0, a1); - break; case INDEX_op_deposit_i64: case INDEX_op_deposit_i32: @@ -3154,8 +3159,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 5972def558..c85b5da1e5 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -24,7 +24,6 @@ extern bool use_neon_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 6928f209d2..4ca23bb718 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -969,12 +969,6 @@ static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rn) g_assert_not_reached(); } -static void tcg_out_bswap32(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn) -{ - /* rev */ - tcg_out32(s, 0x06bf0f30 | (cond << 28) | (rd << 12) | rn); -} - static void tcg_out_deposit(TCGContext *s, ARMCond cond, TCGReg rd, TCGArg a1, int ofs, int len, bool const_a1) { @@ -2157,6 +2151,18 @@ static const TCGOutOpBswap outop_bswap16 = { .out_rr = tgen_bswap16, }; +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg rd, TCGReg rn, unsigned flags) +{ + /* rev */ + tcg_out32(s, 0x06bf0f30 | (COND_AL << 28) | (rd << 12) | rn); +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_subfi(s, type, a0, 0, a1); @@ -2378,10 +2384,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); break; - case INDEX_op_bswap32_i32: - tcg_out_bswap32(s, COND_AL, args[0], args[1]); - break; - case INDEX_op_deposit_i32: tcg_out_deposit(s, COND_AL, args[0], args[2], args[3], args[4], const_args[2]); @@ -2438,7 +2440,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_bswap32_i32: case INDEX_op_extract_i32: case INDEX_op_sextract_i32: return C_O1_I1(r, r); diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index fd44ed8168..ca533ab5cf 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -26,7 +26,6 @@ #define have_avx512vbmi2 ((cpuinfo & CPUINFO_AVX512VBMI2) && have_avx512vl) /* optional instructions */ -#define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 @@ -34,7 +33,6 @@ #if TCG_TARGET_REG_BITS == 64 /* Keep 32-bit values zero-extended in a register. */ #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index c74a718cee..6d90666ba7 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3090,6 +3090,20 @@ static const TCGOutOpBswap outop_bswap16 = { .out_rr = tgen_bswap16, }; +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_bswap32(s, a0); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext32s(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, 0), + .out_rr = tgen_bswap32, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; @@ -3193,13 +3207,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(bswap32): - tcg_out_bswap32(s, a0); - if (rexw && (a2 & TCG_BSWAP_OS)) { - tcg_out_ext32s(s, a0, a0); - } - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, -1, a1, a2, TCG_TYPE_I32); break; @@ -3972,8 +3979,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: case INDEX_op_extrh_i64_i32: return C_O1_I1(r, 0); diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 11a93afd8b..e66df31954 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -13,13 +13,11 @@ #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 -#define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 /* 64-bit operations */ #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 8be6f69e3a..d4bcb06a5f 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1751,6 +1751,24 @@ static const TCGOutOpBswap outop_bswap16 = { .out_rr = tgen_bswap16, }; +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_opc_revb_2w(s, a0, a1); + + /* All 32-bit values are computed sign-extended in the register. */ + if (type == TCG_TYPE_I32 || (flags & TCG_BSWAP_OS)) { + tcg_out_ext32s(s, a0, a0); + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_ext32u(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); @@ -1842,19 +1860,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_bstrins_d(s, a0, a2, args[3], args[3] + args[4] - 1); break; - case INDEX_op_bswap32_i32: - /* All 32-bit values are computed sign-extended in the register. */ - a2 = TCG_BSWAP_OS; - /* fallthrough */ - case INDEX_op_bswap32_i64: - tcg_out_opc_revb_2w(s, a0, a1); - if (a2 & TCG_BSWAP_OS) { - tcg_out_ext32s(s, a0, a0); - } else if ((a2 & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - tcg_out_ext32u(s, a0, a0); - } - break; - case INDEX_op_bswap64_i64: tcg_out_opc_revb_d(s, a0, a1); break; @@ -2454,8 +2459,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 6c967d9c9f..2391f5d8bf 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -39,7 +39,6 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_bswap32_i32 1 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_add2_i32 0 @@ -56,7 +55,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #endif diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 258b49f9db..ab8f8c9994 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -710,26 +710,6 @@ static void tcg_out_bswap_subr(TCGContext *s, const tcg_insn_unit *sub) } } -static void tcg_out_bswap32(TCGContext *s, TCGReg ret, TCGReg arg, int flags) -{ - if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); - tcg_out_opc_sa(s, OPC_ROTR, ret, ret, 16); - if (flags & TCG_BSWAP_OZ) { - tcg_out_opc_bf(s, OPC_DEXT, ret, ret, 31, 0); - } - } else { - if (flags & TCG_BSWAP_OZ) { - tcg_out_bswap_subr(s, bswap32u_addr); - } else { - tcg_out_bswap_subr(s, bswap32_addr); - } - /* delay slot -- never omit the insn, like tcg_out_mov might. */ - tcg_out_opc_reg(s, OPC_OR, TCG_TMP0, arg, TCG_REG_ZERO); - tcg_out_mov(s, TCG_TYPE_I32, ret, TCG_TMP3); - } -} - static void tcg_out_bswap64(TCGContext *s, TCGReg ret, TCGReg arg) { if (use_mips32r2_instructions) { @@ -2170,6 +2150,32 @@ static const TCGOutOpBswap outop_bswap16 = { .out_rr = tgen_bswap16, }; +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg ret, TCGReg arg, unsigned flags) +{ + if (use_mips32r2_instructions) { + tcg_out_opc_reg(s, OPC_WSBH, ret, 0, arg); + tcg_out_opc_sa(s, OPC_ROTR, ret, ret, 16); + if (flags & TCG_BSWAP_OZ) { + tcg_out_opc_bf(s, OPC_DEXT, ret, ret, 31, 0); + } + } else { + if (flags & TCG_BSWAP_OZ) { + tcg_out_bswap_subr(s, bswap32u_addr); + } else { + tcg_out_bswap_subr(s, bswap32_addr); + } + /* delay slot -- never omit the insn, like tcg_out_mov might. */ + tcg_out_opc_reg(s, OPC_OR, TCG_TMP0, arg, TCG_REG_ZERO); + tcg_out_mov(s, TCG_TYPE_I32, ret, TCG_TMP3); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); @@ -2261,12 +2267,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_bswap32_i32: - tcg_out_bswap32(s, a0, a1, 0); - break; - case INDEX_op_bswap32_i64: - tcg_out_bswap32(s, a0, a1, a2); - break; case INDEX_op_bswap64_i64: tcg_out_bswap64(s, a0, a1); break; @@ -2371,7 +2371,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_bswap32_i32: case INDEX_op_extract_i32: case INDEX_op_sextract_i32: case INDEX_op_ld8u_i64: @@ -2381,7 +2380,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index b73fca9789..ad0885d635 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -17,7 +17,6 @@ #define have_vsx (cpuinfo & CPUINFO_VSX) /* optional instructions */ -#define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 @@ -25,7 +24,6 @@ #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 3454254624..4527ed3eee 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -1012,41 +1012,6 @@ static void tcg_out_addpcis(TCGContext *s, TCGReg dst, intptr_t imm) tcg_out32(s, ADDPCIS | RT(dst) | (d1 << 16) | (d0 << 6) | d2); } -static void tcg_out_bswap32(TCGContext *s, TCGReg dst, TCGReg src, int flags) -{ - TCGReg tmp = dst == src ? TCG_REG_R0 : dst; - - if (have_isa_3_10) { - tcg_out32(s, BRW | RA(dst) | RS(src)); - if (flags & TCG_BSWAP_OS) { - tcg_out_ext32s(s, dst, dst); - } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - tcg_out_ext32u(s, dst, dst); - } - return; - } - - /* - * Stolen from gcc's builtin_bswap32. - * In the following, - * dep(a, b, m) -> (a & ~m) | (b & m) - * - * Begin with: src = xxxxabcd - */ - /* tmp = rol32(src, 8) & 0xffffffff = 0000bcda */ - tcg_out_rlw(s, RLWINM, tmp, src, 8, 0, 31); - /* tmp = dep(tmp, rol32(src, 24), 0xff000000) = 0000dcda */ - tcg_out_rlw(s, RLWIMI, tmp, src, 24, 0, 7); - /* tmp = dep(tmp, rol32(src, 24), 0x0000ff00) = 0000dcba */ - tcg_out_rlw(s, RLWIMI, tmp, src, 24, 16, 23); - - if (flags & TCG_BSWAP_OS) { - tcg_out_ext32s(s, dst, tmp); - } else { - tcg_out_mov(s, TCG_TYPE_REG, dst, tmp); - } -} - static void tcg_out_bswap64(TCGContext *s, TCGReg dst, TCGReg src) { TCGReg t0 = dst == src ? TCG_REG_R0 : dst; @@ -3384,6 +3349,47 @@ static const TCGOutOpBswap outop_bswap16 = { .out_rr = tgen_bswap16, }; +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg dst, TCGReg src, unsigned flags) +{ + TCGReg tmp = dst == src ? TCG_REG_R0 : dst; + + if (have_isa_3_10) { + tcg_out32(s, BRW | RA(dst) | RS(src)); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext32s(s, dst, dst); + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_ext32u(s, dst, dst); + } + return; + } + + /* + * Stolen from gcc's builtin_bswap32. + * In the following, + * dep(a, b, m) -> (a & ~m) | (b & m) + * + * Begin with: src = xxxxabcd + */ + /* tmp = rol32(src, 8) & 0xffffffff = 0000bcda */ + tcg_out_rlw(s, RLWINM, tmp, src, 8, 0, 31); + /* tmp = dep(tmp, rol32(src, 24), 0xff000000) = 0000dcda */ + tcg_out_rlw(s, RLWIMI, tmp, src, 24, 0, 7); + /* tmp = dep(tmp, rol32(src, 24), 0x0000ff00) = 0000dcba */ + tcg_out_rlw(s, RLWIMI, tmp, src, 24, 16, 23); + + if (flags & TCG_BSWAP_OS) { + tcg_out_ext32s(s, dst, tmp); + } else { + tcg_out_mov(s, TCG_TYPE_REG, dst, tmp); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tcg_out32(s, NEG | RT(a0) | RA(a1)); @@ -3506,12 +3512,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; - case INDEX_op_bswap32_i32: - tcg_out_bswap32(s, args[0], args[1], 0); - break; - case INDEX_op_bswap32_i64: - tcg_out_bswap32(s, args[0], args[1], args[2]); - break; case INDEX_op_bswap64_i64: tcg_out_bswap64(s, args[0], args[1]); break; @@ -4252,7 +4252,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_bswap32_i32: case INDEX_op_extract_i32: case INDEX_op_sextract_i32: case INDEX_op_ld8u_i64: @@ -4264,7 +4263,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: case INDEX_op_extract_i64: case INDEX_op_sextract_i64: diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 85bb5cd591..fbe294474a 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -13,12 +13,10 @@ #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 -#define TCG_TARGET_HAS_bswap32_i32 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_bswap32_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index c6cd2100f8..9b6ca54ae7 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2424,6 +2424,23 @@ static const TCGOutOpBswap outop_bswap16 = { .out_rr = tgen_bswap16, }; +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); + if (flags & TCG_BSWAP_OZ) { + tcg_out_opc_imm(s, OPC_SRLI, a0, a0, 32); + } else { + tcg_out_opc_imm(s, OPC_SRAI, a0, a0, 32); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_bswap, + .out_rr = tgen_bswap32, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); @@ -2509,17 +2526,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_bswap64_i64: tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); break; - case INDEX_op_bswap32_i32: - a2 = 0; - /* fall through */ - case INDEX_op_bswap32_i64: - tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); - if (a2 & TCG_BSWAP_OZ) { - tcg_out_opc_imm(s, OPC_SRLI, a0, a0, 32); - } else { - tcg_out_opc_imm(s, OPC_SRAI, a0, a0, 32); - } - break; case INDEX_op_add2_i32: tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], @@ -2858,8 +2864,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: return C_O1_I1(r, r); diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 6cd92fa240..76cfe4f323 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -29,14 +29,12 @@ extern uint64_t s390_facilities[3]; ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) /* optional instructions */ -#define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index e90c03628a..ed2da3f31d 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2760,6 +2760,22 @@ static const TCGOutOpBswap outop_bswap16 = { .out_rr = tgen_bswap16, }; +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_insn(s, RRE, LRVR, a0, a1); + if (flags & TCG_BSWAP_OS) { + tcg_out_ext32s(s, a0, a0); + } else if ((flags & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { + tcg_out_ext32u(s, a0, a0); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { if (type == TCG_TYPE_I32) { @@ -2846,19 +2862,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_bswap32_i32: - tcg_out_insn(s, RRE, LRVR, args[0], args[1]); - break; - case INDEX_op_bswap32_i64: - a0 = args[0], a1 = args[1], a2 = args[2]; - tcg_out_insn(s, RRE, LRVR, a0, a1); - if (a2 & TCG_BSWAP_OS) { - tcg_out_ext32s(s, a0, a0); - } else if ((a2 & (TCG_BSWAP_IZ | TCG_BSWAP_OZ)) == TCG_BSWAP_OZ) { - tcg_out_ext32u(s, a0, a0); - } - break; - case INDEX_op_add2_i32: if (const_args[4]) { tcg_out_insn(s, RIL, ALFI, args[0], args[4]); @@ -3459,8 +3462,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index eb1e16c0e2..22837beca9 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -14,14 +14,12 @@ extern bool use_vis3_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_bswap32_i32 0 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 5111f173e1..cbe9c759ec 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1729,6 +1729,10 @@ static const TCGOutOpBswap outop_bswap16 = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_G0, a1); diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 3d1c805d59..4034c73cca 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -12,7 +12,6 @@ #if TCG_TARGET_REG_BITS == 32 /* Turn some undef macros into false macros. */ #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap32_i64 0 #define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 917f52b04a..68e53a9c85 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1294,7 +1294,7 @@ void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags) */ void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (TCG_TARGET_HAS_bswap32_i32) { + if (tcg_op_supported(INDEX_op_bswap32_i32, TCG_TYPE_I32, 0)) { tcg_gen_op3i_i32(INDEX_op_bswap32_i32, ret, arg, 0); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); @@ -2137,7 +2137,7 @@ void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) } else { tcg_gen_movi_i32(TCGV_HIGH(ret), 0); } - } else if (TCG_TARGET_HAS_bswap32_i64) { + } else if (tcg_op_supported(INDEX_op_bswap32_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3i_i64(INDEX_op_bswap32_i64, ret, arg, flags); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index ae68ce88b7..89ef2ef89c 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1076,6 +1076,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_brcond, TCGOutOpBrcond, outop_brcond), OUTOP(INDEX_op_bswap16, TCGOutOpBswap, outop_bswap16), + OUTOP(INDEX_op_bswap32_i32, TCGOutOpBswap, outop_bswap32), + OUTOP(INDEX_op_bswap32_i64, TCGOutOpBswap, outop_bswap32), OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), OUTOP(INDEX_op_ctpop, TCGOutOpUnary, outop_ctpop), OUTOP(INDEX_op_ctz, TCGOutOpBinary, outop_ctz), @@ -2342,8 +2344,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return TCG_TARGET_HAS_add2_i32; case INDEX_op_sub2_i32: return TCG_TARGET_HAS_sub2_i32; - case INDEX_op_bswap32_i32: - return TCG_TARGET_HAS_bswap32_i32; case INDEX_op_brcond2_i32: case INDEX_op_setcond2_i32: @@ -2372,8 +2372,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: return TCG_TARGET_HAS_extr_i64_i32; - case INDEX_op_bswap32_i64: - return TCG_TARGET_HAS_bswap32_i64; case INDEX_op_bswap64_i64: return TCG_TARGET_HAS_bswap64_i64; case INDEX_op_add2_i64: @@ -5488,6 +5486,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_bswap16: + case INDEX_op_bswap32_i32: + case INDEX_op_bswap32_i64: { const TCGOutOpBswap *out = container_of(all_outop[op->opc], TCGOutOpBswap, base); diff --git a/tcg/tci.c b/tcg/tci.c index 905ca154fc..0cb89f3256 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -690,12 +690,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rr(insn, &r0, &r1); regs[r0] = bswap16(regs[r1]); break; -#if TCG_TARGET_HAS_bswap32_i32 || TCG_TARGET_HAS_bswap32_i64 CASE_32_64(bswap32) tci_args_rr(insn, &r0, &r1); regs[r0] = bswap32(regs[r1]); break; -#endif #if TCG_TARGET_REG_BITS == 64 /* Load/store operations (64 bit). */ diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index d7228246ab..c5c64f4f5d 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -7,13 +7,11 @@ #ifndef TCG_TARGET_HAS_H #define TCG_TARGET_HAS_H -#define TCG_TARGET_HAS_bswap32_i32 1 #define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap32_i64 1 #define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i32 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 4d3d9569cc..1b2f18e370 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -57,8 +57,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: case INDEX_op_extract_i32: case INDEX_op_extract_i64: @@ -916,6 +914,20 @@ static const TCGOutOpBswap outop_bswap16 = { .out_rr = tgen_bswap16, }; +static void tgen_bswap32(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, unsigned flags) +{ + tcg_out_op_rr(s, INDEX_op_bswap32_i32, a0, a1); + if (flags & TCG_BSWAP_OS) { + tcg_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 32); + } +} + +static const TCGOutOpBswap outop_bswap32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap32, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tcg_out_op_rr(s, INDEX_op_neg, a0, a1); @@ -1026,8 +1038,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - int width; - switch (opc) { case INDEX_op_goto_ptr: tcg_out_op_r(s, opc, args[0]); @@ -1062,20 +1072,10 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_rrbb(s, opc, args[0], args[1], args[2], args[3]); break; - case INDEX_op_bswap32_i32: /* Optional (TCG_TARGET_HAS_bswap32_i32). */ case INDEX_op_bswap64_i64: /* Optional (TCG_TARGET_HAS_bswap64_i64). */ tcg_out_op_rr(s, opc, args[0], args[1]); break; - case INDEX_op_bswap32_i64: /* Optional (TCG_TARGET_HAS_bswap32_i64). */ - width = 32; - /* The base tci bswaps zero-extend, and ignore high bits. */ - tcg_out_op_rr(s, opc, args[0], args[1]); - if (args[2] & TCG_BSWAP_OS) { - tcg_out_sextract(s, TCG_TYPE_REG, args[0], args[0], 0, width); - } - break; - CASE_32_64(add2) CASE_32_64(sub2) tcg_out_op_rrrrrr(s, opc, args[0], args[1], args[2], From 7498d882cbe39ae7df4315ea006830e640f0d47b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 19:53:51 -0800 Subject: [PATCH 0449/2760] tcg: Merge INDEX_op_bswap32_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 13 ++++++------- include/tcg/tcg-opc.h | 4 +--- tcg/optimize.c | 7 +++---- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 9 +++------ tcg/tci.c | 5 ++--- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 20 insertions(+), 28 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 509cfe7db1..e89ede54fa 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -425,16 +425,15 @@ Misc | | If neither ``TCG_BSWAP_OZ`` nor ``TCG_BSWAP_OS`` are set, then the bits of *t0* above bit 15 may contain any value. - * - bswap32_i64 *t0*, *t1*, *flags* + * - bswap32 *t0*, *t1*, *flags* - - | 32 bit byte swap on a 64-bit value. The flags are the same as for bswap16, - except they apply from bit 31 instead of bit 15. + - | 32 bit byte swap. The flags are the same as for bswap16, except + they apply from bit 31 instead of bit 15. On TCG_TYPE_I32, the + flags should be zero. - * - bswap32_i32 *t0*, *t1*, *flags* + * - bswap64_i64 *t0*, *t1*, *flags* - bswap64_i64 *t0*, *t1*, *flags* - - - | 32/64 bit byte swap. The flags are ignored, but still present + - | 64 bit byte swap. The flags are ignored, but still present for consistency with the other bswap opcodes. * - discard_i32/i64 *t0* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index acfbaa05b4..296dffe99a 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -44,6 +44,7 @@ DEF(add, 1, 2, 0, TCG_OPF_INT) DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(bswap16, 1, 1, 1, TCG_OPF_INT) +DEF(bswap32, 1, 1, 1, TCG_OPF_INT) DEF(clz, 1, 2, 0, TCG_OPF_INT) DEF(ctpop, 1, 1, 0, TCG_OPF_INT) DEF(ctz, 1, 2, 0, TCG_OPF_INT) @@ -96,8 +97,6 @@ DEF(sub2_i32, 2, 4, 0, 0) DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(setcond2_i32, 1, 4, 1, 0) -DEF(bswap32_i32, 1, 1, 1, 0) - /* load/store */ DEF(ld8u_i64, 1, 1, 1, 0) DEF(ld8s_i64, 1, 1, 1, 0) @@ -122,7 +121,6 @@ DEF(extu_i32_i64, 1, 1, 0, 0) DEF(extrl_i64_i32, 1, 1, 0, 0) DEF(extrh_i64_i32, 1, 1, 0, 0) -DEF(bswap32_i64, 1, 1, 1, 0) DEF(bswap64_i64, 1, 1, 1, 0) DEF(add2_i64, 2, 4, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index 1d535a9fae..6fa968624d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -522,7 +522,7 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, x = bswap16(x); return y & TCG_BSWAP_OS ? (int16_t)x : x; - CASE_OP_32_64(bswap32): + case INDEX_op_bswap32: x = bswap32(x); return y & TCG_BSWAP_OS ? (int32_t)x : x; @@ -1576,8 +1576,7 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) z_mask = bswap16(z_mask); sign = INT16_MIN; break; - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: + case INDEX_op_bswap32: z_mask = bswap32(z_mask); sign = INT32_MIN; break; @@ -2870,7 +2869,7 @@ void tcg_optimize(TCGContext *s) done = fold_brcond2(&ctx, op); break; case INDEX_op_bswap16: - CASE_OP_32_64(bswap32): + case INDEX_op_bswap32: case INDEX_op_bswap64_i64: done = fold_bswap(&ctx, op); break; diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 68e53a9c85..b1174f60cc 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1294,8 +1294,8 @@ void tcg_gen_bswap16_i32(TCGv_i32 ret, TCGv_i32 arg, int flags) */ void tcg_gen_bswap32_i32(TCGv_i32 ret, TCGv_i32 arg) { - if (tcg_op_supported(INDEX_op_bswap32_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op3i_i32(INDEX_op_bswap32_i32, ret, arg, 0); + if (tcg_op_supported(INDEX_op_bswap32, TCG_TYPE_I32, 0)) { + tcg_gen_op3i_i32(INDEX_op_bswap32, ret, arg, 0); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 t1 = tcg_temp_ebb_new_i32(); @@ -2137,8 +2137,8 @@ void tcg_gen_bswap32_i64(TCGv_i64 ret, TCGv_i64 arg, int flags) } else { tcg_gen_movi_i32(TCGV_HIGH(ret), 0); } - } else if (tcg_op_supported(INDEX_op_bswap32_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3i_i64(INDEX_op_bswap32_i64, ret, arg, flags); + } else if (tcg_op_supported(INDEX_op_bswap32, TCG_TYPE_I64, 0)) { + tcg_gen_op3i_i64(INDEX_op_bswap32, ret, arg, flags); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index 89ef2ef89c..571f15626c 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1076,8 +1076,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_brcond, TCGOutOpBrcond, outop_brcond), OUTOP(INDEX_op_bswap16, TCGOutOpBswap, outop_bswap16), - OUTOP(INDEX_op_bswap32_i32, TCGOutOpBswap, outop_bswap32), - OUTOP(INDEX_op_bswap32_i64, TCGOutOpBswap, outop_bswap32), + OUTOP(INDEX_op_bswap32, TCGOutOpBswap, outop_bswap32), OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), OUTOP(INDEX_op_ctpop, TCGOutOpUnary, outop_ctpop), OUTOP(INDEX_op_ctz, TCGOutOpBinary, outop_ctz), @@ -2939,8 +2938,7 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) } break; case INDEX_op_bswap16: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: + case INDEX_op_bswap32: case INDEX_op_bswap64_i64: { TCGArg flags = op->args[k]; @@ -5486,8 +5484,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_bswap16: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: + case INDEX_op_bswap32: { const TCGOutOpBswap *out = container_of(all_outop[op->opc], TCGOutOpBswap, base); diff --git a/tcg/tci.c b/tcg/tci.c index 0cb89f3256..f98c437100 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -690,7 +690,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rr(insn, &r0, &r1); regs[r0] = bswap16(regs[r1]); break; - CASE_32_64(bswap32) + case INDEX_op_bswap32: tci_args_rr(insn, &r0, &r1); regs[r0] = bswap32(regs[r1]); break; @@ -1004,14 +1004,13 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) break; case INDEX_op_bswap16: + case INDEX_op_bswap32: case INDEX_op_ctpop: case INDEX_op_mov: case INDEX_op_neg: case INDEX_op_not: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_bswap32_i32: - case INDEX_op_bswap32_i64: case INDEX_op_bswap64_i64: tci_args_rr(insn, &r0, &r1); info->fprintf_func(info->stream, "%-12s %s, %s", diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 1b2f18e370..7478ada393 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -917,7 +917,7 @@ static const TCGOutOpBswap outop_bswap16 = { static void tgen_bswap32(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, unsigned flags) { - tcg_out_op_rr(s, INDEX_op_bswap32_i32, a0, a1); + tcg_out_op_rr(s, INDEX_op_bswap32, a0, a1); if (flags & TCG_BSWAP_OS) { tcg_out_sextract(s, TCG_TYPE_REG, a0, a0, 0, 32); } From 613b571c93db4cec7d0023b857c1b857af7a3324 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 21:46:38 -0800 Subject: [PATCH 0450/2760] tcg: Convert bswap64 to TCGOutOpUnary Use TCGOutOpUnary instead of TCGOutOpBswap because the flags are not used with this opcode; they are merely present for uniformity with the smaller bswaps. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 1 - tcg/aarch64/tcg-target.c.inc | 15 ++++-- tcg/arm/tcg-target.c.inc | 4 ++ tcg/i386/tcg-target-has.h | 1 - tcg/i386/tcg-target.c.inc | 16 ++++-- tcg/loongarch64/tcg-target-has.h | 1 - tcg/loongarch64/tcg-target.c.inc | 15 ++++-- tcg/mips/tcg-target-has.h | 1 - tcg/mips/tcg-target.c.inc | 37 ++++++++------ tcg/ppc/tcg-target-has.h | 1 - tcg/ppc/tcg-target.c.inc | 88 ++++++++++++++++---------------- tcg/riscv/tcg-target-has.h | 1 - tcg/riscv/tcg-target.c.inc | 16 ++++-- tcg/s390x/tcg-target-has.h | 1 - tcg/s390x/tcg-target.c.inc | 15 ++++-- tcg/sparc64/tcg-target-has.h | 1 - tcg/sparc64/tcg-target.c.inc | 4 ++ tcg/tcg-has.h | 1 - tcg/tcg-op.c | 2 +- tcg/tcg.c | 7 ++- tcg/tci.c | 2 - tcg/tci/tcg-target-has.h | 1 - tcg/tci/tcg-target.c.inc | 17 ++++-- 23 files changed, 144 insertions(+), 104 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 7c3d3fc637..82d8cd5965 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -19,7 +19,6 @@ #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index a2e45ca5c8..79c0e2e097 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2470,6 +2470,16 @@ static const TCGOutOpBswap outop_bswap32 = { .out_rr = tgen_bswap32, }; +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_rev(s, TCG_TYPE_I64, MO_64, a0, a1); +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap64, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_XZR, a1); @@ -2637,10 +2647,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], false); break; - case INDEX_op_bswap64_i64: - tcg_out_rev(s, TCG_TYPE_I64, MO_64, a0, a1); - break; - case INDEX_op_deposit_i64: case INDEX_op_deposit_i32: tcg_out_dep(s, ext, a0, a2, args[3], args[4]); @@ -3159,7 +3165,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_bswap64_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extract_i32: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 4ca23bb718..3bbc28c63c 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2163,6 +2163,10 @@ static const TCGOutOpBswap outop_bswap32 = { .out_rr = tgen_bswap32, }; +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_subfi(s, type, a0, 0, a1); diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index ca533ab5cf..6b91b23fe8 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -33,7 +33,6 @@ #if TCG_TARGET_REG_BITS == 64 /* Keep 32-bit values zero-extended in a register. */ #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 6d90666ba7..347e01c076 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3104,6 +3104,18 @@ static const TCGOutOpBswap outop_bswap32 = { .out_rr = tgen_bswap32, }; +#if TCG_TARGET_REG_BITS == 64 +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_bswap64(s, a0); +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, 0), + .out_rr = tgen_bswap64, +}; +#endif + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; @@ -3279,9 +3291,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_bswap64_i64: - tcg_out_bswap64(s, a0); - break; case INDEX_op_extrh_i64_i32: tcg_out_shifti(s, SHIFT_SHR + P_REXW, a0, 32); break; @@ -3979,7 +3988,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_bswap64_i64: case INDEX_op_extrh_i64_i32: return C_O1_I1(r, 0); diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index e66df31954..10090102f7 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -18,7 +18,6 @@ /* 64-bit operations */ #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index d4bcb06a5f..f3b2f709d2 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1769,6 +1769,16 @@ static const TCGOutOpBswap outop_bswap32 = { .out_rr = tgen_bswap32, }; +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_opc_revb_d(s, a0, a1); +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap64, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); @@ -1860,10 +1870,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_bstrins_d(s, a0, a2, args[3], args[3] + args[4] - 1); break; - case INDEX_op_bswap64_i64: - tcg_out_opc_revb_d(s, a0, a1); - break; - case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: tcg_out_ldst(s, OPC_LD_B, a0, a1, a2); @@ -2459,7 +2465,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: - case INDEX_op_bswap64_i64: case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: case INDEX_op_ld8u_i32: diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 2391f5d8bf..24b00f1eec 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -55,7 +55,6 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #endif diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index ab8f8c9994..baaf0e416b 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -710,19 +710,6 @@ static void tcg_out_bswap_subr(TCGContext *s, const tcg_insn_unit *sub) } } -static void tcg_out_bswap64(TCGContext *s, TCGReg ret, TCGReg arg) -{ - if (use_mips32r2_instructions) { - tcg_out_opc_reg(s, OPC_DSBH, ret, 0, arg); - tcg_out_opc_reg(s, OPC_DSHD, ret, 0, ret); - } else { - tcg_out_bswap_subr(s, bswap64_addr); - /* delay slot -- never omit the insn, like tcg_out_mov might. */ - tcg_out_opc_reg(s, OPC_OR, TCG_TMP0, arg, TCG_REG_ZERO); - tcg_out_mov(s, TCG_TYPE_I32, ret, TCG_TMP3); - } -} - static void tcg_out_ext32u(TCGContext *s, TCGReg ret, TCGReg arg) { tcg_debug_assert(TCG_TARGET_REG_BITS == 64); @@ -2176,6 +2163,26 @@ static const TCGOutOpBswap outop_bswap32 = { .out_rr = tgen_bswap32, }; +#if TCG_TARGET_REG_BITS == 64 +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) +{ + if (use_mips32r2_instructions) { + tcg_out_opc_reg(s, OPC_DSBH, ret, 0, arg); + tcg_out_opc_reg(s, OPC_DSHD, ret, 0, ret); + } else { + tcg_out_bswap_subr(s, bswap64_addr); + /* delay slot -- never omit the insn, like tcg_out_mov might. */ + tcg_out_opc_reg(s, OPC_OR, TCG_TMP0, arg, TCG_REG_ZERO); + tcg_out_mov(s, TCG_TYPE_I32, ret, TCG_TMP3); + } +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap64, +}; +#endif /* TCG_TARGET_REG_BITS == 64 */ + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); @@ -2267,9 +2274,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_bswap64_i64: - tcg_out_bswap64(s, a0, a1); - break; case INDEX_op_extrh_i64_i32: tcg_out_dsra(s, a0, a1, 32); break; @@ -2380,7 +2384,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_bswap64_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index ad0885d635..bd9c3d92ed 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -24,7 +24,6 @@ #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 4527ed3eee..083137d211 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -1012,44 +1012,6 @@ static void tcg_out_addpcis(TCGContext *s, TCGReg dst, intptr_t imm) tcg_out32(s, ADDPCIS | RT(dst) | (d1 << 16) | (d0 << 6) | d2); } -static void tcg_out_bswap64(TCGContext *s, TCGReg dst, TCGReg src) -{ - TCGReg t0 = dst == src ? TCG_REG_R0 : dst; - TCGReg t1 = dst == src ? dst : TCG_REG_R0; - - if (have_isa_3_10) { - tcg_out32(s, BRD | RA(dst) | RS(src)); - return; - } - - /* - * In the following, - * dep(a, b, m) -> (a & ~m) | (b & m) - * - * Begin with: src = abcdefgh - */ - /* t0 = rol32(src, 8) & 0xffffffff = 0000fghe */ - tcg_out_rlw(s, RLWINM, t0, src, 8, 0, 31); - /* t0 = dep(t0, rol32(src, 24), 0xff000000) = 0000hghe */ - tcg_out_rlw(s, RLWIMI, t0, src, 24, 0, 7); - /* t0 = dep(t0, rol32(src, 24), 0x0000ff00) = 0000hgfe */ - tcg_out_rlw(s, RLWIMI, t0, src, 24, 16, 23); - - /* t0 = rol64(t0, 32) = hgfe0000 */ - tcg_out_rld(s, RLDICL, t0, t0, 32, 0); - /* t1 = rol64(src, 32) = efghabcd */ - tcg_out_rld(s, RLDICL, t1, src, 32, 0); - - /* t0 = dep(t0, rol32(t1, 24), 0xffffffff) = hgfebcda */ - tcg_out_rlw(s, RLWIMI, t0, t1, 8, 0, 31); - /* t0 = dep(t0, rol32(t1, 24), 0xff000000) = hgfedcda */ - tcg_out_rlw(s, RLWIMI, t0, t1, 24, 0, 7); - /* t0 = dep(t0, rol32(t1, 24), 0x0000ff00) = hgfedcba */ - tcg_out_rlw(s, RLWIMI, t0, t1, 24, 16, 23); - - tcg_out_mov(s, TCG_TYPE_REG, dst, t0); -} - /* Emit a move into ret of arg, if it can be done in one insn. */ static bool tcg_out_movi_one(TCGContext *s, TCGReg ret, tcg_target_long arg) { @@ -3390,6 +3352,51 @@ static const TCGOutOpBswap outop_bswap32 = { .out_rr = tgen_bswap32, }; +#if TCG_TARGET_REG_BITS == 64 +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg dst, TCGReg src) +{ + TCGReg t0 = dst == src ? TCG_REG_R0 : dst; + TCGReg t1 = dst == src ? dst : TCG_REG_R0; + + if (have_isa_3_10) { + tcg_out32(s, BRD | RA(dst) | RS(src)); + return; + } + + /* + * In the following, + * dep(a, b, m) -> (a & ~m) | (b & m) + * + * Begin with: src = abcdefgh + */ + /* t0 = rol32(src, 8) & 0xffffffff = 0000fghe */ + tcg_out_rlw(s, RLWINM, t0, src, 8, 0, 31); + /* t0 = dep(t0, rol32(src, 24), 0xff000000) = 0000hghe */ + tcg_out_rlw(s, RLWIMI, t0, src, 24, 0, 7); + /* t0 = dep(t0, rol32(src, 24), 0x0000ff00) = 0000hgfe */ + tcg_out_rlw(s, RLWIMI, t0, src, 24, 16, 23); + + /* t0 = rol64(t0, 32) = hgfe0000 */ + tcg_out_rld(s, RLDICL, t0, t0, 32, 0); + /* t1 = rol64(src, 32) = efghabcd */ + tcg_out_rld(s, RLDICL, t1, src, 32, 0); + + /* t0 = dep(t0, rol32(t1, 24), 0xffffffff) = hgfebcda */ + tcg_out_rlw(s, RLWIMI, t0, t1, 8, 0, 31); + /* t0 = dep(t0, rol32(t1, 24), 0xff000000) = hgfedcda */ + tcg_out_rlw(s, RLWIMI, t0, t1, 24, 0, 7); + /* t0 = dep(t0, rol32(t1, 24), 0x0000ff00) = hgfedcba */ + tcg_out_rlw(s, RLWIMI, t0, t1, 24, 16, 23); + + tcg_out_mov(s, TCG_TYPE_REG, dst, t0); +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap64, +}; +#endif /* TCG_TARGET_REG_BITS == 64 */ + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tcg_out32(s, NEG | RT(a0) | RA(a1)); @@ -3512,10 +3519,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; - case INDEX_op_bswap64_i64: - tcg_out_bswap64(s, args[0], args[1]); - break; - case INDEX_op_deposit_i32: if (const_args[2]) { uint32_t mask = ((2u << (args[4] - 1)) - 1) << args[3]; @@ -4263,7 +4266,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_bswap64_i64: case INDEX_op_extract_i64: case INDEX_op_sextract_i64: return C_O1_I1(r, r); diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index fbe294474a..88fadc2428 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -17,7 +17,6 @@ #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_bswap64_i64 (cpuinfo & CPUINFO_ZBB) #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 9b6ca54ae7..00b097d171 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2441,6 +2441,17 @@ static const TCGOutOpBswap outop_bswap32 = { .out_rr = tgen_bswap32, }; +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_bswap, + .out_rr = tgen_bswap64, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_ZERO, a1); @@ -2523,10 +2534,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_bswap64_i64: - tcg_out_opc_imm(s, OPC_REV8, a0, a1, 0); - break; - case INDEX_op_add2_i32: tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], const_args[4], const_args[5], false, true); @@ -2864,7 +2871,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: - case INDEX_op_bswap64_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 76cfe4f323..95407f61cf 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -35,7 +35,6 @@ extern uint64_t s390_facilities[3]; #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index ed2da3f31d..2ed288cfe0 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2776,6 +2776,16 @@ static const TCGOutOpBswap outop_bswap32 = { .out_rr = tgen_bswap32, }; +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_insn(s, RRE, LRVGR, a0, a1); +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap64, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { if (type == TCG_TYPE_I32) { @@ -2922,10 +2932,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); break; - case INDEX_op_bswap64_i64: - tcg_out_insn(s, RRE, LRVGR, args[0], args[1]); - break; - case INDEX_op_add2_i64: if (const_args[4]) { if ((int64_t)args[4] >= 0) { @@ -3462,7 +3468,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_bswap64_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extract_i32: diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 22837beca9..2ced6f7c1c 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -20,7 +20,6 @@ extern bool use_vis3_instructions; #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index cbe9c759ec..96ffba9af6 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1733,6 +1733,10 @@ static const TCGOutOpBswap outop_bswap32 = { .base.static_constraint = C_NotImplemented, }; +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tgen_sub(s, type, a0, TCG_REG_G0, a1); diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 4034c73cca..21bef070fe 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -12,7 +12,6 @@ #if TCG_TARGET_REG_BITS == 32 /* Turn some undef macros into false macros. */ #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap64_i64 0 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index b1174f60cc..27e700161f 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2184,7 +2184,7 @@ void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) tcg_gen_mov_i32(TCGV_HIGH(ret), t0); tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); - } else if (TCG_TARGET_HAS_bswap64_i64) { + } else if (tcg_op_supported(INDEX_op_bswap64_i64, TCG_TYPE_I64, 0)) { tcg_gen_op3i_i64(INDEX_op_bswap64_i64, ret, arg, 0); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index 571f15626c..f2f2c0dd74 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1112,6 +1112,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { #if TCG_TARGET_REG_BITS == 32 OUTOP(INDEX_op_brcond2_i32, TCGOutOpBrcond2, outop_brcond2), OUTOP(INDEX_op_setcond2_i32, TCGOutOpSetcond2, outop_setcond2), +#else + OUTOP(INDEX_op_bswap64_i64, TCGOutOpUnary, outop_bswap64), #endif }; @@ -2371,8 +2373,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: return TCG_TARGET_HAS_extr_i64_i32; - case INDEX_op_bswap64_i64: - return TCG_TARGET_HAS_bswap64_i64; case INDEX_op_add2_i64: return TCG_TARGET_HAS_add2_i64; case INDEX_op_sub2_i64: @@ -5470,6 +5470,9 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_bswap64_i64: + assert(TCG_TARGET_REG_BITS == 64); + /* fall through */ case INDEX_op_ctpop: case INDEX_op_neg: case INDEX_op_not: diff --git a/tcg/tci.c b/tcg/tci.c index f98c437100..903f996f02 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -788,12 +788,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rr(insn, &r0, &r1); regs[r0] = (uint32_t)regs[r1]; break; -#if TCG_TARGET_HAS_bswap64_i64 case INDEX_op_bswap64_i64: tci_args_rr(insn, &r0, &r1); regs[r0] = bswap64(regs[r1]); break; -#endif #endif /* TCG_TARGET_REG_BITS == 64 */ /* QEMU specific operations. */ diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index c5c64f4f5d..90aa5c8bbb 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -12,7 +12,6 @@ #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_bswap64_i64 1 #define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 7478ada393..cbfe92adf3 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -57,7 +57,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_bswap64_i64: case INDEX_op_extract_i32: case INDEX_op_extract_i64: case INDEX_op_sextract_i32: @@ -928,6 +927,18 @@ static const TCGOutOpBswap outop_bswap32 = { .out_rr = tgen_bswap32, }; +#if TCG_TARGET_REG_BITS == 64 +static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_op_rr(s, INDEX_op_bswap64_i64, a0, a1); +} + +static const TCGOutOpUnary outop_bswap64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_bswap64, +}; +#endif + static void tgen_neg(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { tcg_out_op_rr(s, INDEX_op_neg, a0, a1); @@ -1072,10 +1083,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_rrbb(s, opc, args[0], args[1], args[2], args[3]); break; - case INDEX_op_bswap64_i64: /* Optional (TCG_TARGET_HAS_bswap64_i64). */ - tcg_out_op_rr(s, opc, args[0], args[1]); - break; - CASE_32_64(add2) CASE_32_64(sub2) tcg_out_op_rrrrrr(s, opc, args[0], args[1], args[2], From 3ad5d4ccb4bdebdff4e90957bb2b8a93e5e418e2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 10 Jan 2025 21:54:44 -0800 Subject: [PATCH 0451/2760] tcg: Rename INDEX_op_bswap64_i64 to INDEX_op_bswap64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Even though bswap64 can only be used with TCG_TYPE_I64, rename the opcode to maintain uniformity. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 5 +++-- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 6 +++--- tcg/tcg-op.c | 4 ++-- tcg/tcg.c | 6 +++--- tcg/tci.c | 4 ++-- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 15 insertions(+), 15 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index e89ede54fa..72a23d6ea2 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -431,10 +431,11 @@ Misc they apply from bit 31 instead of bit 15. On TCG_TYPE_I32, the flags should be zero. - * - bswap64_i64 *t0*, *t1*, *flags* + * - bswap64 *t0*, *t1*, *flags* - | 64 bit byte swap. The flags are ignored, but still present - for consistency with the other bswap opcodes. + for consistency with the other bswap opcodes. For future + compatibility, the flags should be zero. * - discard_i32/i64 *t0* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 296dffe99a..1d27b882fe 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -45,6 +45,7 @@ DEF(and, 1, 2, 0, TCG_OPF_INT) DEF(andc, 1, 2, 0, TCG_OPF_INT) DEF(bswap16, 1, 1, 1, TCG_OPF_INT) DEF(bswap32, 1, 1, 1, TCG_OPF_INT) +DEF(bswap64, 1, 1, 1, TCG_OPF_INT) DEF(clz, 1, 2, 0, TCG_OPF_INT) DEF(ctpop, 1, 1, 0, TCG_OPF_INT) DEF(ctz, 1, 2, 0, TCG_OPF_INT) @@ -121,8 +122,6 @@ DEF(extu_i32_i64, 1, 1, 0, 0) DEF(extrl_i64_i32, 1, 1, 0, 0) DEF(extrh_i64_i32, 1, 1, 0, 0) -DEF(bswap64_i64, 1, 1, 1, 0) - DEF(add2_i64, 2, 4, 0, 0) DEF(sub2_i64, 2, 4, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index 6fa968624d..a860b62109 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -526,7 +526,7 @@ static uint64_t do_constant_folding_2(TCGOpcode op, TCGType type, x = bswap32(x); return y & TCG_BSWAP_OS ? (int32_t)x : x; - case INDEX_op_bswap64_i64: + case INDEX_op_bswap64: return bswap64(x); case INDEX_op_ext_i32_i64: @@ -1580,7 +1580,7 @@ static bool fold_bswap(OptContext *ctx, TCGOp *op) z_mask = bswap32(z_mask); sign = INT32_MIN; break; - case INDEX_op_bswap64_i64: + case INDEX_op_bswap64: z_mask = bswap64(z_mask); sign = INT64_MIN; break; @@ -2870,7 +2870,7 @@ void tcg_optimize(TCGContext *s) break; case INDEX_op_bswap16: case INDEX_op_bswap32: - case INDEX_op_bswap64_i64: + case INDEX_op_bswap64: done = fold_bswap(&ctx, op); break; case INDEX_op_clz: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 27e700161f..ba062191ac 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2184,8 +2184,8 @@ void tcg_gen_bswap64_i64(TCGv_i64 ret, TCGv_i64 arg) tcg_gen_mov_i32(TCGV_HIGH(ret), t0); tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); - } else if (tcg_op_supported(INDEX_op_bswap64_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op3i_i64(INDEX_op_bswap64_i64, ret, arg, 0); + } else if (tcg_op_supported(INDEX_op_bswap64, TCG_TYPE_I64, 0)) { + tcg_gen_op3i_i64(INDEX_op_bswap64, ret, arg, 0); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index f2f2c0dd74..1ba86dd515 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1113,7 +1113,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_brcond2_i32, TCGOutOpBrcond2, outop_brcond2), OUTOP(INDEX_op_setcond2_i32, TCGOutOpSetcond2, outop_setcond2), #else - OUTOP(INDEX_op_bswap64_i64, TCGOutOpUnary, outop_bswap64), + OUTOP(INDEX_op_bswap64, TCGOutOpUnary, outop_bswap64), #endif }; @@ -2939,7 +2939,7 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) break; case INDEX_op_bswap16: case INDEX_op_bswap32: - case INDEX_op_bswap64_i64: + case INDEX_op_bswap64: { TCGArg flags = op->args[k]; const char *name = NULL; @@ -5470,7 +5470,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_bswap64_i64: + case INDEX_op_bswap64: assert(TCG_TARGET_REG_BITS == 64); /* fall through */ case INDEX_op_ctpop: diff --git a/tcg/tci.c b/tcg/tci.c index 903f996f02..30928c3412 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -788,7 +788,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rr(insn, &r0, &r1); regs[r0] = (uint32_t)regs[r1]; break; - case INDEX_op_bswap64_i64: + case INDEX_op_bswap64: tci_args_rr(insn, &r0, &r1); regs[r0] = bswap64(regs[r1]); break; @@ -1009,7 +1009,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_not: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_bswap64_i64: + case INDEX_op_bswap64: tci_args_rr(insn, &r0, &r1); info->fprintf_func(info->stream, "%-12s %s, %s", op_name, str_r(r0), str_r(r1)); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index cbfe92adf3..4fc857ad35 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -930,7 +930,7 @@ static const TCGOutOpBswap outop_bswap32 = { #if TCG_TARGET_REG_BITS == 64 static void tgen_bswap64(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) { - tcg_out_op_rr(s, INDEX_op_bswap64_i64, a0, a1); + tcg_out_op_rr(s, INDEX_op_bswap64, a0, a1); } static const TCGOutOpUnary outop_bswap64 = { From 5a4d034f3cbc6d3bffc983b24a2746e9fe9b91cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 11 Jan 2025 07:55:47 -0800 Subject: [PATCH 0452/2760] tcg: Convert extract to TCGOutOpExtract Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 28 +++++++----- tcg/arm/tcg-target.c.inc | 23 +++++----- tcg/i386/tcg-target.c.inc | 77 +++++++++++++++++--------------- tcg/loongarch64/tcg-target.c.inc | 33 +++++++------- tcg/mips/tcg-target.c.inc | 35 +++++++-------- tcg/ppc/tcg-target.c.inc | 35 +++++++-------- tcg/riscv/tcg-target.c.inc | 54 +++++++++++----------- tcg/s390x/tcg-target.c.inc | 14 +++--- tcg/sparc64/tcg-target.c.inc | 16 ++++--- tcg/tcg.c | 20 +++++++++ tcg/tci/tcg-target.c.inc | 8 ++-- 11 files changed, 191 insertions(+), 152 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 79c0e2e097..6c9d6094a2 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2567,6 +2567,22 @@ static const TCGOutOpMovcond outop_movcond = { .out = tgen_movcond, }; +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + uint64_t mask = MAKE_64BIT_MASK(0, len); + tcg_out_logicali(s, I3404_ANDI, type, a0, a1, mask); + } else { + tcg_out_ubfm(s, type, a0, a1, ofs, ofs + len - 1); + } +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2652,16 +2668,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_dep(s, ext, a0, a2, args[3], args[4]); break; - case INDEX_op_extract_i64: - case INDEX_op_extract_i32: - if (a2 == 0) { - uint64_t mask = MAKE_64BIT_MASK(0, args[3]); - tcg_out_logicali(s, I3404_ANDI, ext, a0, a1, mask); - } else { - tcg_out_ubfm(s, ext, a0, a1, a2, a2 + args[3] - 1); - } - break; - case INDEX_op_sextract_i64: case INDEX_op_sextract_i32: tcg_out_sbfm(s, ext, a0, a1, a2, a2 + args[3] - 1); @@ -3167,8 +3173,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: return C_O1_I1(r, r); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 3bbc28c63c..bc060b20f2 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -981,19 +981,19 @@ static void tcg_out_deposit(TCGContext *s, ARMCond cond, TCGReg rd, | (ofs << 7) | ((ofs + len - 1) << 16)); } -static void tcg_out_extract(TCGContext *s, ARMCond cond, TCGReg rd, - TCGReg rn, int ofs, int len) +static void tgen_extract(TCGContext *s, TCGType type, TCGReg rd, TCGReg rn, + unsigned ofs, unsigned len) { /* According to gcc, AND can be faster. */ if (ofs == 0 && len <= 8) { - tcg_out_dat_imm(s, cond, ARITH_AND, rd, rn, + tcg_out_dat_imm(s, COND_AL, ARITH_AND, rd, rn, encode_imm_nofail((1 << len) - 1)); return; } if (use_armv7_instructions) { /* ubfx */ - tcg_out32(s, 0x07e00050 | (cond << 28) | (rd << 12) | rn + tcg_out32(s, 0x07e00050 | (COND_AL << 28) | (rd << 12) | rn | (ofs << 7) | ((len - 1) << 16)); return; } @@ -1002,17 +1002,24 @@ static void tcg_out_extract(TCGContext *s, ARMCond cond, TCGReg rd, switch (len) { case 8: /* uxtb */ - tcg_out32(s, 0x06ef0070 | (cond << 28) | (rd << 12) | (ofs << 7) | rn); + tcg_out32(s, 0x06ef0070 | (COND_AL << 28) | + (rd << 12) | (ofs << 7) | rn); break; case 16: /* uxth */ - tcg_out32(s, 0x06ff0070 | (cond << 28) | (rd << 12) | (ofs << 7) | rn); + tcg_out32(s, 0x06ff0070 | (COND_AL << 28) | + (rd << 12) | (ofs << 7) | rn); break; default: g_assert_not_reached(); } } +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; + static void tcg_out_sextract(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn, int ofs, int len) { @@ -2392,9 +2399,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_deposit(s, COND_AL, args[0], args[2], args[3], args[4], const_args[2]); break; - case INDEX_op_extract_i32: - tcg_out_extract(s, COND_AL, args[0], args[1], args[2], args[3]); - break; case INDEX_op_sextract_i32: tcg_out_sextract(s, COND_AL, args[0], args[1], args[2], args[3]); break; @@ -2444,7 +2448,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_extract_i32: case INDEX_op_sextract_i32: return C_O1_I1(r, r); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 347e01c076..b26c93bdb1 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3138,6 +3138,47 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8u(s, a0, a1); + return; + case 16: + tcg_out_ext16u(s, a0, a1); + return; + case 32: + tcg_out_ext32u(s, a0, a1); + return; + } + } else if (TCG_TARGET_REG_BITS == 64 && ofs + len == 32) { + /* This is a 32-bit zero-extending right shift. */ + tcg_out_mov(s, TCG_TYPE_I32, a0, a1); + tcg_out_shifti(s, SHIFT_SHR, a0, ofs); + return; + } else if (ofs == 8 && len == 8) { + /* + * On the off-chance that we can use the high-byte registers. + * Otherwise we emit the same ext16 + shift pattern that we + * would have gotten from the normal tcg-op.c expansion. + */ + if (a1 < 4 && (TCG_TARGET_REG_BITS == 32 || a0 < 8)) { + tcg_out_modrm(s, OPC_MOVZBL, a0, a1 + 4); + } else { + tcg_out_ext16u(s, a0, a1); + tcg_out_shifti(s, SHIFT_SHR, a0, 8); + } + return; + } + g_assert_not_reached(); +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3328,40 +3369,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_extract_i64: - if (a2 + args[3] == 32) { - if (a2 == 0) { - tcg_out_ext32u(s, a0, a1); - break; - } - /* This is a 32-bit zero-extending right shift. */ - tcg_out_mov(s, TCG_TYPE_I32, a0, a1); - tcg_out_shifti(s, SHIFT_SHR, a0, a2); - break; - } - /* FALLTHRU */ - case INDEX_op_extract_i32: - if (a2 == 0 && args[3] == 8) { - tcg_out_ext8u(s, a0, a1); - } else if (a2 == 0 && args[3] == 16) { - tcg_out_ext16u(s, a0, a1); - } else if (a2 == 8 && args[3] == 8) { - /* - * On the off-chance that we can use the high-byte registers. - * Otherwise we emit the same ext16 + shift pattern that we - * would have gotten from the normal tcg-op.c expansion. - */ - if (a1 < 4 && a0 < 8) { - tcg_out_modrm(s, OPC_MOVZBL, a0, a1 + 4); - } else { - tcg_out_ext16u(s, a0, a1); - tcg_out_shifti(s, SHIFT_SHR, a0, 8); - } - } else { - g_assert_not_reached(); - } - break; - case INDEX_op_sextract_i64: if (a2 == 0 && args[3] == 8) { tcg_out_ext8s(s, TCG_TYPE_I64, a0, a1); @@ -3994,8 +4001,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: return C_O1_I1(r, r); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index f3b2f709d2..f63d33e9ac 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1799,6 +1799,22 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0 && len <= 12) { + tcg_out_opc_andi(s, a0, a1, (1 << len) - 1); + } else if (type == TCG_TYPE_I32) { + tcg_out_opc_bstrpick_w(s, a0, a1, ofs, ofs + len - 1); + } else { + tcg_out_opc_bstrpick_d(s, a0, a1, ofs, ofs + len - 1); + } +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1828,21 +1844,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_srai_d(s, a0, a1, 32); break; - case INDEX_op_extract_i32: - if (a2 == 0 && args[3] <= 12) { - tcg_out_opc_andi(s, a0, a1, (1 << args[3]) - 1); - } else { - tcg_out_opc_bstrpick_w(s, a0, a1, a2, a2 + args[3] - 1); - } - break; - case INDEX_op_extract_i64: - if (a2 == 0 && args[3] <= 12) { - tcg_out_opc_andi(s, a0, a1, (1 << args[3]) - 1); - } else { - tcg_out_opc_bstrpick_d(s, a0, a1, a2, a2 + args[3] - 1); - } - break; - case INDEX_op_sextract_i64: if (a2 + args[3] == 32) { if (a2 == 0) { @@ -2461,8 +2462,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ext_i32_i64: - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: case INDEX_op_ld8s_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index baaf0e416b..dbb4b9355d 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2203,6 +2203,23 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0 && len <= 16) { + tcg_out_opc_imm(s, OPC_ANDI, a0, a1, (1 << len) - 1); + } else if (type == TCG_TYPE_I32) { + tcg_out_opc_bf(s, OPC_EXT, a0, a1, len - 1, ofs); + } else { + tcg_out_opc_bf64(s, OPC_DEXT, OPC_DEXTM, OPC_DEXTU, + a0, a1, len - 1, ofs); + } +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2286,22 +2303,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[3] + args[4] - 1, args[3]); break; - case INDEX_op_extract_i32: - if (a2 == 0 && args[3] <= 16) { - tcg_out_opc_imm(s, OPC_ANDI, a0, a1, (1 << args[3]) - 1); - } else { - tcg_out_opc_bf(s, OPC_EXT, a0, a1, args[3] - 1, a2); - } - break; - case INDEX_op_extract_i64: - if (a2 == 0 && args[3] <= 16) { - tcg_out_opc_imm(s, OPC_ANDI, a0, a1, (1 << args[3]) - 1); - } else { - tcg_out_opc_bf64(s, OPC_DEXT, OPC_DEXTM, OPC_DEXTU, - a0, a1, args[3] - 1, a2); - } - break; - case INDEX_op_sextract_i64: if (a2 == 0 && args[3] == 32) { tcg_out_ext32s(s, a0, a1); @@ -2375,7 +2376,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_extract_i32: case INDEX_op_sextract_i32: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i64: @@ -2388,7 +2388,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: - case INDEX_op_extract_i64: case INDEX_op_sextract_i64: return C_O1_I1(r, r); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 083137d211..a8558a47b7 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3417,6 +3417,23 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0 && len <= 16) { + tgen_andi(s, TCG_TYPE_I32, a0, a1, (1 << len) - 1); + } else if (type == TCG_TYPE_I32) { + tcg_out_rlw(s, RLWINM, a0, a1, 32 - ofs, 32 - len, 31); + } else { + tcg_out_rld(s, RLDICL, a0, a1, 64 - ofs, 64 - len); + } +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3538,22 +3555,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_extract_i32: - if (args[2] == 0 && args[3] <= 16) { - tcg_out32(s, ANDI | SAI(args[1], args[0], (1 << args[3]) - 1)); - break; - } - tcg_out_rlw(s, RLWINM, args[0], args[1], - 32 - args[2], 32 - args[3], 31); - break; - case INDEX_op_extract_i64: - if (args[2] == 0 && args[3] <= 16) { - tcg_out32(s, ANDI | SAI(args[1], args[0], (1 << args[3]) - 1)); - break; - } - tcg_out_rld(s, RLDICL, args[0], args[1], 64 - args[2], 64 - args[3]); - break; - case INDEX_op_sextract_i64: if (args[2] + args[3] == 32) { if (args[2] == 0) { @@ -4255,7 +4256,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_extract_i32: case INDEX_op_sextract_i32: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i64: @@ -4266,7 +4266,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_extract_i64: case INDEX_op_sextract_i64: return C_O1_I1(r, r); diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 00b097d171..85d978763c 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2472,6 +2472,34 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 16: + tcg_out_ext16u(s, a0, a1); + return; + case 32: + tcg_out_ext32u(s, a0, a1); + return; + } + } + if (ofs + len == 32) { + tgen_shli(s, TCG_TYPE_I32, a0, a1, ofs); + return; + } + if (len == 1) { + tcg_out_opc_imm(s, OPC_BEXTI, a0, a1, ofs); + return; + } + g_assert_not_reached(); +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2572,30 +2600,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mb(s, a0); break; - case INDEX_op_extract_i64: - if (a2 + args[3] == 32) { - if (a2 == 0) { - tcg_out_ext32u(s, a0, a1); - } else { - tcg_out_opc_imm(s, OPC_SRLIW, a0, a1, a2); - } - break; - } - /* FALLTHRU */ - case INDEX_op_extract_i32: - switch (args[3]) { - case 1: - tcg_out_opc_imm(s, OPC_BEXTI, a0, a1, a2); - break; - case 16: - tcg_debug_assert(a2 == 0); - tcg_out_ext16u(s, a0, a1); - break; - default: - g_assert_not_reached(); - } - break; - case INDEX_op_sextract_i64: if (a2 + args[3] == 32) { if (a2 == 0) { @@ -2867,8 +2871,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ext_i32_i64: - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: return C_O1_I1(r, r); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 2ed288cfe0..96e2dc0ad5 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1563,8 +1563,8 @@ static void tgen_deposit(TCGContext *s, TCGReg dest, TCGReg src, tcg_out_risbg(s, dest, src, msb, lsb, ofs, z); } -static void tgen_extract(TCGContext *s, TCGReg dest, TCGReg src, - int ofs, int len) +static void tgen_extract(TCGContext *s, TCGType type, TCGReg dest, + TCGReg src, unsigned ofs, unsigned len) { if (ofs == 0) { switch (len) { @@ -1582,6 +1582,11 @@ static void tgen_extract(TCGContext *s, TCGReg dest, TCGReg src, tcg_out_risbg(s, dest, src, 64 - len, 63, 64 - ofs, 1); } +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; + static void tgen_sextract(TCGContext *s, TCGReg dest, TCGReg src, int ofs, int len) { @@ -2975,9 +2980,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(extract): - tgen_extract(s, args[0], args[1], args[2], args[3]); - break; OP_32_64(sextract): tgen_sextract(s, args[0], args[1], args[2], args[3]); break; @@ -3470,8 +3472,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: return C_O1_I1(r, r); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 96ffba9af6..cba1dd009c 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1757,6 +1757,17 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + tcg_debug_assert(ofs + len == 32); + tcg_out_arithi(s, a0, a1, ofs, SHIFT_SRL); +} + +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extract, +}; static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1857,10 +1868,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mb(s, a0); break; - case INDEX_op_extract_i64: - tcg_debug_assert(a2 + args[3] == 32); - tcg_out_arithi(s, a0, a1, a2, SHIFT_SRL); - break; case INDEX_op_sextract_i64: tcg_debug_assert(a2 + args[3] == 32); tcg_out_arithi(s, a0, a1, a2, SHIFT_SRA); @@ -1897,7 +1904,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_extract_i64: case INDEX_op_sextract_i64: case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: diff --git a/tcg/tcg.c b/tcg/tcg.c index 1ba86dd515..36c5e9c847 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1013,6 +1013,12 @@ typedef struct TCGOutOpDivRem { TCGReg a0, TCGReg a1, TCGReg a4); } TCGOutOpDivRem; +typedef struct TCGOutOpExtract { + TCGOutOp base; + void (*out_rr)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len); +} TCGOutOpExtract; + typedef struct TCGOutOpMovcond { TCGOutOp base; void (*out)(TCGContext *s, TCGType type, TCGCond cond, @@ -1085,6 +1091,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), OUTOP(INDEX_op_divu2, TCGOutOpDivRem, outop_divu2), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), + OUTOP(INDEX_op_extract_i32, TCGOutOpExtract, outop_extract), + OUTOP(INDEX_op_extract_i64, TCGOutOpExtract, outop_extract), OUTOP(INDEX_op_movcond, TCGOutOpMovcond, outop_movcond), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_muls2, TCGOutOpMul2, outop_muls2), @@ -5511,6 +5519,18 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_extract_i32: + case INDEX_op_extract_i64: + { + const TCGOutOpExtract *out = + container_of(all_outop[op->opc], TCGOutOpExtract, base); + + tcg_debug_assert(!const_args[1]); + out->out_rr(s, type, new_args[0], new_args[1], + new_args[2], new_args[3]); + } + break; + case INDEX_op_muls2: case INDEX_op_mulu2: { diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 4fc857ad35..d8cf5d237b 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -57,8 +57,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: return C_O1_I1(r, r); @@ -444,6 +442,11 @@ static void tcg_out_extract(TCGContext *s, TCGType type, TCGReg rd, tcg_out_op_rrbb(s, opc, rd, rs, pos, len); } +static const TCGOutOpExtract outop_extract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tcg_out_extract, +}; + static void tcg_out_sextract(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs, unsigned pos, unsigned len) { @@ -1078,7 +1081,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_rrrbb(s, opc, args[0], args[1], args[2], args[3], args[4]); break; - CASE_32_64(extract) /* Optional (TCG_TARGET_HAS_extract_*). */ CASE_32_64(sextract) /* Optional (TCG_TARGET_HAS_sextract_*). */ tcg_out_op_rrbb(s, opc, args[0], args[1], args[2], args[3]); break; From 07d5d502f2b4a8eedda3c6bdfcab31dc36d1d1d5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 11 Jan 2025 09:01:46 -0800 Subject: [PATCH 0453/2760] tcg: Merge INDEX_op_extract_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 6 +++--- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 14 ++++---------- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 9 +++------ tcg/tci.c | 12 ++++-------- tcg/tci/tcg-target.c.inc | 5 +---- 7 files changed, 20 insertions(+), 37 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 72a23d6ea2..2843f88772 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -456,7 +456,7 @@ Misc | | *dest* = (*t1* & ~0x0f00) | ((*t2* << 8) & 0x0f00) - * - extract_i32/i64 *dest*, *t1*, *pos*, *len* + * - extract *dest*, *t1*, *pos*, *len* sextract_i32/i64 *dest*, *t1*, *pos*, *len* @@ -467,12 +467,12 @@ Misc to the left with zeros; for sextract_*, the result will be extended to the left with copies of the bitfield sign bit at *pos* + *len* - 1. | - | For example, "sextract_i32 dest, t1, 8, 4" indicates a 4-bit field + | For example, "sextract dest, t1, 8, 4" indicates a 4-bit field at bit 8. This operation would be equivalent to | | *dest* = (*t1* << 20) >> 28 | - | (using an arithmetic right shift). + | (using an arithmetic right shift) on TCG_TYPE_I32. * - extract2_i32/i64 *dest*, *t1*, *t2*, *pos* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 1d27b882fe..a8c304ca63 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -54,6 +54,7 @@ DEF(divs2, 2, 3, 0, TCG_OPF_INT) DEF(divu, 1, 2, 0, TCG_OPF_INT) DEF(divu2, 2, 3, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) +DEF(extract, 1, 1, 2, TCG_OPF_INT) DEF(movcond, 1, 4, 1, TCG_OPF_INT) DEF(mul, 1, 2, 0, TCG_OPF_INT) DEF(muls2, 2, 2, 0, TCG_OPF_INT) @@ -89,7 +90,6 @@ DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* shifts/rotates */ DEF(deposit_i32, 1, 2, 2, 0) -DEF(extract_i32, 1, 1, 2, 0) DEF(sextract_i32, 1, 1, 2, 0) DEF(extract2_i32, 1, 2, 1, 0) @@ -112,7 +112,6 @@ DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* shifts/rotates */ DEF(deposit_i64, 1, 2, 2, 0) -DEF(extract_i64, 1, 1, 2, 0) DEF(sextract_i64, 1, 1, 2, 0) DEF(extract2_i64, 1, 2, 1, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index a860b62109..fbfcbf23cd 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2317,7 +2317,7 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) { - TCGOpcode uext_opc = 0, sext_opc = 0; + TCGOpcode sext_opc = 0; TCGCond cond = op->args[3]; TCGArg ret, src1, src2; TCGOp *op2; @@ -2338,17 +2338,11 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) switch (ctx->type) { case TCG_TYPE_I32: - if (TCG_TARGET_extract_valid(TCG_TYPE_I32, sh, 1)) { - uext_opc = INDEX_op_extract_i32; - } if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, sh, 1)) { sext_opc = INDEX_op_sextract_i32; } break; case TCG_TYPE_I64: - if (TCG_TARGET_extract_valid(TCG_TYPE_I64, sh, 1)) { - uext_opc = INDEX_op_extract_i64; - } if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, sh, 1)) { sext_opc = INDEX_op_sextract_i64; } @@ -2367,8 +2361,8 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) op->args[2] = sh; op->args[3] = 1; return; - } else if (sh && uext_opc) { - op->opc = uext_opc; + } else if (sh && TCG_TARGET_extract_valid(ctx->type, sh, 1)) { + op->opc = INDEX_op_extract; op->args[1] = src1; op->args[2] = sh; op->args[3] = 1; @@ -2897,7 +2891,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_eqv_vec: done = fold_eqv(&ctx, op); break; - CASE_OP_32_64(extract): + case INDEX_op_extract: done = fold_extract(&ctx, op); break; CASE_OP_32_64(extract2): diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index ba062191ac..ddade73b7b 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -998,7 +998,7 @@ void tcg_gen_extract_i32(TCGv_i32 ret, TCGv_i32 arg, } if (TCG_TARGET_extract_valid(TCG_TYPE_I32, ofs, len)) { - tcg_gen_op4ii_i32(INDEX_op_extract_i32, ret, arg, ofs, len); + tcg_gen_op4ii_i32(INDEX_op_extract, ret, arg, ofs, len); return; } if (ofs == 0) { @@ -1008,7 +1008,7 @@ void tcg_gen_extract_i32(TCGv_i32 ret, TCGv_i32 arg, /* Assume that zero-extension, if available, is cheaper than a shift. */ if (TCG_TARGET_extract_valid(TCG_TYPE_I32, 0, ofs + len)) { - tcg_gen_op4ii_i32(INDEX_op_extract_i32, ret, arg, 0, ofs + len); + tcg_gen_op4ii_i32(INDEX_op_extract, ret, arg, 0, ofs + len); tcg_gen_shri_i32(ret, ret, ofs); return; } @@ -2670,7 +2670,7 @@ void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, } if (TCG_TARGET_extract_valid(TCG_TYPE_I64, ofs, len)) { - tcg_gen_op4ii_i64(INDEX_op_extract_i64, ret, arg, ofs, len); + tcg_gen_op4ii_i64(INDEX_op_extract, ret, arg, ofs, len); return; } if (ofs == 0) { @@ -2680,7 +2680,7 @@ void tcg_gen_extract_i64(TCGv_i64 ret, TCGv_i64 arg, /* Assume that zero-extension, if available, is cheaper than a shift. */ if (TCG_TARGET_extract_valid(TCG_TYPE_I64, 0, ofs + len)) { - tcg_gen_op4ii_i64(INDEX_op_extract_i64, ret, arg, 0, ofs + len); + tcg_gen_op4ii_i64(INDEX_op_extract, ret, arg, 0, ofs + len); tcg_gen_shri_i64(ret, ret, ofs); return; } diff --git a/tcg/tcg.c b/tcg/tcg.c index 36c5e9c847..ce0d862b19 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1091,8 +1091,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), OUTOP(INDEX_op_divu2, TCGOutOpDivRem, outop_divu2), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), - OUTOP(INDEX_op_extract_i32, TCGOutOpExtract, outop_extract), - OUTOP(INDEX_op_extract_i64, TCGOutOpExtract, outop_extract), + OUTOP(INDEX_op_extract, TCGOutOpExtract, outop_extract), OUTOP(INDEX_op_movcond, TCGOutOpMovcond, outop_movcond), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_muls2, TCGOutOpMul2, outop_muls2), @@ -2326,6 +2325,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_add: case INDEX_op_and: case INDEX_op_brcond: + case INDEX_op_extract: case INDEX_op_mov: case INDEX_op_movcond: case INDEX_op_negsetcond: @@ -2342,7 +2342,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: - case INDEX_op_extract_i32: case INDEX_op_sextract_i32: case INDEX_op_deposit_i32: return true; @@ -2371,7 +2370,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_extract_i64: case INDEX_op_sextract_i64: case INDEX_op_deposit_i64: return TCG_TARGET_REG_BITS == 64; @@ -5519,8 +5517,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: + case INDEX_op_extract: { const TCGOutOpExtract *out = container_of(all_outop[op->opc], TCGOutOpExtract, base); diff --git a/tcg/tci.c b/tcg/tci.c index 30928c3412..6345029802 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -27,6 +27,7 @@ #define ctpop_tr glue(ctpop, TCG_TARGET_REG_BITS) +#define extract_tr glue(extract, TCG_TARGET_REG_BITS) /* * Enable TCI assertions only when debugging TCG (and without NDEBUG defined). @@ -656,9 +657,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); regs[r0] = deposit32(regs[r1], pos, len, regs[r2]); break; - case INDEX_op_extract_i32: + case INDEX_op_extract: tci_args_rrbb(insn, &r0, &r1, &pos, &len); - regs[r0] = extract32(regs[r1], pos, len); + regs[r0] = extract_tr(regs[r1], pos, len); break; case INDEX_op_sextract_i32: tci_args_rrbb(insn, &r0, &r1, &pos, &len); @@ -772,10 +773,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); regs[r0] = deposit64(regs[r1], pos, len, regs[r2]); break; - case INDEX_op_extract_i64: - tci_args_rrbb(insn, &r0, &r1, &pos, &len); - regs[r0] = extract64(regs[r1], pos, len); - break; case INDEX_op_sextract_i64: tci_args_rrbb(insn, &r0, &r1, &pos, &len); regs[r0] = sextract64(regs[r1], pos, len); @@ -1057,8 +1054,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) op_name, str_r(r0), str_r(r1), str_r(r2), pos, len); break; - case INDEX_op_extract_i32: - case INDEX_op_extract_i64: + case INDEX_op_extract: case INDEX_op_sextract_i32: case INDEX_op_sextract_i64: tci_args_rrbb(insn, &r0, &r1, &pos, &len); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index d8cf5d237b..ede11d9e70 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -436,10 +436,7 @@ static void tcg_out_movi(TCGContext *s, TCGType type, static void tcg_out_extract(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs, unsigned pos, unsigned len) { - TCGOpcode opc = type == TCG_TYPE_I32 ? - INDEX_op_extract_i32 : - INDEX_op_extract_i64; - tcg_out_op_rrbb(s, opc, rd, rs, pos, len); + tcg_out_op_rrbb(s, INDEX_op_extract, rd, rs, pos, len); } static const TCGOutOpExtract outop_extract = { From 05a1129e23bfed1fe799a169e0eca0f676fa5016 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Jan 2025 11:44:30 -0800 Subject: [PATCH 0454/2760] tcg: Convert sextract to TCGOutOpExtract Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 18 +++++---- tcg/arm/tcg-target.c.inc | 21 ++++++----- tcg/i386/tcg-target.c.inc | 63 ++++++++++++++++---------------- tcg/loongarch64/tcg-target.c.inc | 49 ++++++++++++++----------- tcg/mips/tcg-target.c.inc | 42 ++++++++++++--------- tcg/ppc/tcg-target.c.inc | 49 ++++++++++++++----------- tcg/riscv/tcg-target.c.inc | 49 ++++++++++++++----------- tcg/s390x/tcg-target.c.inc | 15 ++++---- tcg/sparc64/tcg-target.c.inc | 18 ++++++--- tcg/tcg.c | 4 ++ tcg/tci/tcg-target.c.inc | 11 +++--- 11 files changed, 188 insertions(+), 151 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 6c9d6094a2..00400f6ea7 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2583,6 +2583,17 @@ static const TCGOutOpExtract outop_extract = { .out_rr = tgen_extract, }; +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + tcg_out_sbfm(s, type, a0, a1, ofs, ofs + len - 1); +} + +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2668,11 +2679,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_dep(s, ext, a0, a2, args[3], args[4]); break; - case INDEX_op_sextract_i64: - case INDEX_op_sextract_i32: - tcg_out_sbfm(s, ext, a0, a1, a2, a2 + args[3] - 1); - break; - case INDEX_op_extract2_i64: case INDEX_op_extract2_i32: tcg_out_extr(s, ext, a0, a2, a1, args[3]); @@ -3173,8 +3179,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_sextract_i32: - case INDEX_op_sextract_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index bc060b20f2..aebe48679c 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1020,12 +1020,12 @@ static const TCGOutOpExtract outop_extract = { .out_rr = tgen_extract, }; -static void tcg_out_sextract(TCGContext *s, ARMCond cond, TCGReg rd, - TCGReg rn, int ofs, int len) +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg rd, TCGReg rn, + unsigned ofs, unsigned len) { if (use_armv7_instructions) { /* sbfx */ - tcg_out32(s, 0x07a00050 | (cond << 28) | (rd << 12) | rn + tcg_out32(s, 0x07a00050 | (COND_AL << 28) | (rd << 12) | rn | (ofs << 7) | ((len - 1) << 16)); return; } @@ -1034,17 +1034,24 @@ static void tcg_out_sextract(TCGContext *s, ARMCond cond, TCGReg rd, switch (len) { case 8: /* sxtb */ - tcg_out32(s, 0x06af0070 | (cond << 28) | (rd << 12) | (ofs << 7) | rn); + tcg_out32(s, 0x06af0070 | (COND_AL << 28) | + (rd << 12) | (ofs << 7) | rn); break; case 16: /* sxth */ - tcg_out32(s, 0x06bf0070 | (cond << 28) | (rd << 12) | (ofs << 7) | rn); + tcg_out32(s, 0x06bf0070 | (COND_AL << 28) | + (rd << 12) | (ofs << 7) | rn); break; default: g_assert_not_reached(); } } +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + static void tcg_out_ld32u(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn, int32_t offset) @@ -2399,9 +2406,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_deposit(s, COND_AL, args[0], args[2], args[3], args[4], const_args[2]); break; - case INDEX_op_sextract_i32: - tcg_out_sextract(s, COND_AL, args[0], args[1], args[2], args[3]); - break; case INDEX_op_extract2_i32: /* ??? These optimization vs zero should be generic. */ /* ??? But we can't substitute 2 for 1 in the opcode stream yet. */ @@ -2448,7 +2452,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_sextract_i32: return C_O1_I1(r, r); case INDEX_op_st8_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index b26c93bdb1..6a5414ab3a 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3180,6 +3180,38 @@ static const TCGOutOpExtract outop_extract = { .out_rr = tgen_extract, }; +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8s(s, type, a0, a1); + return; + case 16: + tcg_out_ext16s(s, type, a0, a1); + return; + case 32: + tcg_out_ext32s(s, a0, a1); + return; + } + } else if (ofs == 8 && len == 8) { + if (type == TCG_TYPE_I32 && a1 < 4 && a0 < 8) { + tcg_out_modrm(s, OPC_MOVSBL, a0, a1 + 4); + } else { + tcg_out_ext16s(s, type, a0, a1); + tgen_sari(s, type, a0, a0, 8); + } + return; + } + g_assert_not_reached(); +} + +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -3369,35 +3401,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_sextract_i64: - if (a2 == 0 && args[3] == 8) { - tcg_out_ext8s(s, TCG_TYPE_I64, a0, a1); - } else if (a2 == 0 && args[3] == 16) { - tcg_out_ext16s(s, TCG_TYPE_I64, a0, a1); - } else if (a2 == 0 && args[3] == 32) { - tcg_out_ext32s(s, a0, a1); - } else { - g_assert_not_reached(); - } - break; - - case INDEX_op_sextract_i32: - if (a2 == 0 && args[3] == 8) { - tcg_out_ext8s(s, TCG_TYPE_I32, a0, a1); - } else if (a2 == 0 && args[3] == 16) { - tcg_out_ext16s(s, TCG_TYPE_I32, a0, a1); - } else if (a2 == 8 && args[3] == 8) { - if (a1 < 4 && a0 < 8) { - tcg_out_modrm(s, OPC_MOVSBL, a0, a1 + 4); - } else { - tcg_out_ext16s(s, TCG_TYPE_I32, a0, a1); - tcg_out_shifti(s, SHIFT_SAR, a0, 8); - } - } else { - g_assert_not_reached(); - } - break; - OP_32_64(extract2): /* Note that SHRD outputs to the r/m operand. */ tcg_out_modrm(s, OPC_SHRD_Ib + rexw, a2, a0); @@ -4001,8 +4004,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: - case INDEX_op_sextract_i32: - case INDEX_op_sextract_i64: return C_O1_I1(r, r); case INDEX_op_extract2_i32: diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index f63d33e9ac..2ea5234097 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1816,6 +1816,33 @@ static const TCGOutOpExtract outop_extract = { .out_rr = tgen_extract, }; +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8s(s, type, a0, a1); + return; + case 16: + tcg_out_ext16s(s, type, a0, a1); + return; + case 32: + tcg_out_ext32s(s, a0, a1); + return; + } + } else if (ofs + len == 32) { + tcg_out_opc_srai_w(s, a0, a1, ofs); + return; + } + g_assert_not_reached(); +} + +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1844,26 +1871,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_srai_d(s, a0, a1, 32); break; - case INDEX_op_sextract_i64: - if (a2 + args[3] == 32) { - if (a2 == 0) { - tcg_out_ext32s(s, a0, a1); - } else { - tcg_out_opc_srai_w(s, a0, a1, a2); - } - break; - } - /* FALLTHRU */ - case INDEX_op_sextract_i32: - if (a2 == 0 && args[3] == 8) { - tcg_out_ext8s(s, TCG_TYPE_REG, a0, a1); - } else if (a2 == 0 && args[3] == 16) { - tcg_out_ext16s(s, TCG_TYPE_REG, a0, a1); - } else { - g_assert_not_reached(); - } - break; - case INDEX_op_deposit_i32: tcg_out_opc_bstrins_w(s, a0, a2, args[3], args[3] + args[4] - 1); break; @@ -2462,8 +2469,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ext_i32_i64: - case INDEX_op_sextract_i32: - case INDEX_op_sextract_i64: case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: case INDEX_op_ld8u_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index dbb4b9355d..56c58bf82d 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2221,6 +2221,30 @@ static const TCGOutOpExtract outop_extract = { .out_rr = tgen_extract, }; +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8s(s, type, a0, a1); + return; + case 16: + tcg_out_ext16s(s, type, a0, a1); + return; + case 32: + tcg_out_ext32s(s, a0, a1); + return; + } + } + g_assert_not_reached(); +} + +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2303,22 +2327,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, args[3] + args[4] - 1, args[3]); break; - case INDEX_op_sextract_i64: - if (a2 == 0 && args[3] == 32) { - tcg_out_ext32s(s, a0, a1); - break; - } - /* FALLTHRU */ - case INDEX_op_sextract_i32: - if (a2 == 0 && args[3] == 8) { - tcg_out_ext8s(s, TCG_TYPE_REG, a0, a1); - } else if (a2 == 0 && args[3] == 16) { - tcg_out_ext16s(s, TCG_TYPE_REG, a0, a1); - } else { - g_assert_not_reached(); - } - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, 0, a1, a2, TCG_TYPE_I32); break; @@ -2376,7 +2384,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_sextract_i32: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i64: case INDEX_op_ld16u_i64: @@ -2388,7 +2395,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: - case INDEX_op_sextract_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index a8558a47b7..3d1ffa9130 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3434,6 +3434,33 @@ static const TCGOutOpExtract outop_extract = { .out_rr = tgen_extract, }; +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8s(s, type, a0, a1); + return; + case 16: + tcg_out_ext16s(s, type, a0, a1); + return; + case 32: + tcg_out_ext32s(s, a0, a1); + return; + } + } else if (ofs + len == 32) { + tcg_out_sari32(s, a0, a1, ofs); + return; + } + g_assert_not_reached(); +} + +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3555,26 +3582,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_sextract_i64: - if (args[2] + args[3] == 32) { - if (args[2] == 0) { - tcg_out_ext32s(s, args[0], args[1]); - } else { - tcg_out_sari32(s, args[0], args[1], args[2]); - } - break; - } - /* FALLTHRU */ - case INDEX_op_sextract_i32: - if (args[2] == 0 && args[3] == 8) { - tcg_out_ext8s(s, TCG_TYPE_I32, args[0], args[1]); - } else if (args[2] == 0 && args[3] == 16) { - tcg_out_ext16s(s, TCG_TYPE_I32, args[0], args[1]); - } else { - g_assert_not_reached(); - } - break; - #if TCG_TARGET_REG_BITS == 64 case INDEX_op_add2_i64: #else @@ -4256,7 +4263,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld16u_i32: case INDEX_op_ld16s_i32: case INDEX_op_ld_i32: - case INDEX_op_sextract_i32: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i64: case INDEX_op_ld16u_i64: @@ -4266,7 +4272,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_sextract_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 85d978763c..dc2b487844 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2501,6 +2501,33 @@ static const TCGOutOpExtract outop_extract = { .out_rr = tgen_extract, }; +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + if (ofs == 0) { + switch (len) { + case 8: + tcg_out_ext8s(s, type, a0, a1); + return; + case 16: + tcg_out_ext16s(s, type, a0, a1); + return; + case 32: + tcg_out_ext32s(s, a0, a1); + return; + } + } else if (ofs + len == 32) { + tgen_sari(s, TCG_TYPE_I32, a0, a1, ofs); + return; + } + g_assert_not_reached(); +} + +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2600,26 +2627,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mb(s, a0); break; - case INDEX_op_sextract_i64: - if (a2 + args[3] == 32) { - if (a2 == 0) { - tcg_out_ext32s(s, a0, a1); - } else { - tcg_out_opc_imm(s, OPC_SRAIW, a0, a1, a2); - } - break; - } - /* FALLTHRU */ - case INDEX_op_sextract_i32: - if (a2 == 0 && args[3] == 8) { - tcg_out_ext8s(s, TCG_TYPE_REG, a0, a1); - } else if (a2 == 0 && args[3] == 16) { - tcg_out_ext16s(s, TCG_TYPE_REG, a0, a1); - } else { - g_assert_not_reached(); - } - break; - case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ @@ -2871,8 +2878,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ext_i32_i64: - case INDEX_op_sextract_i32: - case INDEX_op_sextract_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 96e2dc0ad5..ab178bebc8 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1587,8 +1587,8 @@ static const TCGOutOpExtract outop_extract = { .out_rr = tgen_extract, }; -static void tgen_sextract(TCGContext *s, TCGReg dest, TCGReg src, - int ofs, int len) +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg dest, + TCGReg src, unsigned ofs, unsigned len) { if (ofs == 0) { switch (len) { @@ -1606,6 +1606,11 @@ static void tgen_sextract(TCGContext *s, TCGReg dest, TCGReg src, g_assert_not_reached(); } +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + static void tgen_gotoi(TCGContext *s, int cc, const tcg_insn_unit *dest) { ptrdiff_t off = tcg_pcrel_diff(s, dest) >> 1; @@ -2980,10 +2985,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - OP_32_64(sextract): - tgen_sextract(s, args[0], args[1], args[2], args[3]); - break; - case INDEX_op_mb: /* The host memory model is quite strong, we simply need to serialize the instruction stream. */ @@ -3472,8 +3473,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_sextract_i32: - case INDEX_op_sextract_i64: return C_O1_I1(r, r); case INDEX_op_qemu_ld_i32: diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index cba1dd009c..0f2bec21e9 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1769,6 +1769,18 @@ static const TCGOutOpExtract outop_extract = { .out_rr = tgen_extract, }; +static void tgen_sextract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + unsigned ofs, unsigned len) +{ + tcg_debug_assert(ofs + len == 32); + tcg_out_arithi(s, a0, a1, ofs, SHIFT_SRA); +} + +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_sextract, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1868,11 +1880,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_mb(s, a0); break; - case INDEX_op_sextract_i64: - tcg_debug_assert(a2 + args[3] == 32); - tcg_out_arithi(s, a0, a1, a2, SHIFT_SRA); - break; - case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ @@ -1904,7 +1911,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_sextract_i64: case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); diff --git a/tcg/tcg.c b/tcg/tcg.c index ce0d862b19..7f5fa25062 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1111,6 +1111,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_rotr, TCGOutOpBinary, outop_rotr), OUTOP(INDEX_op_sar, TCGOutOpBinary, outop_sar), OUTOP(INDEX_op_setcond, TCGOutOpSetcond, outop_setcond), + OUTOP(INDEX_op_sextract_i32, TCGOutOpExtract, outop_sextract), + OUTOP(INDEX_op_sextract_i64, TCGOutOpExtract, outop_sextract), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), @@ -5518,6 +5520,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_extract: + case INDEX_op_sextract_i32: + case INDEX_op_sextract_i64: { const TCGOutOpExtract *out = container_of(all_outop[op->opc], TCGOutOpExtract, base); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index ede11d9e70..e013321ac7 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -57,8 +57,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_sextract_i32: - case INDEX_op_sextract_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: @@ -453,6 +451,11 @@ static void tcg_out_sextract(TCGContext *s, TCGType type, TCGReg rd, tcg_out_op_rrbb(s, opc, rd, rs, pos, len); } +static const TCGOutOpExtract outop_sextract = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tcg_out_sextract, +}; + static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) { tcg_out_sextract(s, type, rd, rs, 0, 8); @@ -1078,10 +1081,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_rrrbb(s, opc, args[0], args[1], args[2], args[3], args[4]); break; - CASE_32_64(sextract) /* Optional (TCG_TARGET_HAS_sextract_*). */ - tcg_out_op_rrbb(s, opc, args[0], args[1], args[2], args[3]); - break; - CASE_32_64(add2) CASE_32_64(sub2) tcg_out_op_rrrrrr(s, opc, args[0], args[1], args[2], From fa361eefac24dcaa1d6dfbc433fce0652fdd8ba8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Jan 2025 11:50:09 -0800 Subject: [PATCH 0455/2760] tcg: Merge INDEX_op_sextract_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 2 +- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 22 +++------------------- tcg/tcg-op.c | 12 ++++++------ tcg/tcg.c | 9 +++------ tcg/tci.c | 12 ++++-------- tcg/tci/tcg-target.c.inc | 5 +---- 7 files changed, 19 insertions(+), 46 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 2843f88772..ca7550f68c 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -458,7 +458,7 @@ Misc * - extract *dest*, *t1*, *pos*, *len* - sextract_i32/i64 *dest*, *t1*, *pos*, *len* + sextract *dest*, *t1*, *pos*, *len* - | Extract a bitfield from *t1*, placing the result in *dest*. | diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index a8c304ca63..4ace1f85c4 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -74,6 +74,7 @@ DEF(rotl, 1, 2, 0, TCG_OPF_INT) DEF(rotr, 1, 2, 0, TCG_OPF_INT) DEF(sar, 1, 2, 0, TCG_OPF_INT) DEF(setcond, 1, 2, 1, TCG_OPF_INT) +DEF(sextract, 1, 1, 2, TCG_OPF_INT) DEF(shl, 1, 2, 0, TCG_OPF_INT) DEF(shr, 1, 2, 0, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) @@ -90,7 +91,6 @@ DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* shifts/rotates */ DEF(deposit_i32, 1, 2, 2, 0) -DEF(sextract_i32, 1, 1, 2, 0) DEF(extract2_i32, 1, 2, 1, 0) DEF(add2_i32, 2, 4, 0, 0) @@ -112,7 +112,6 @@ DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* shifts/rotates */ DEF(deposit_i64, 1, 2, 2, 0) -DEF(sextract_i64, 1, 1, 2, 0) DEF(extract2_i64, 1, 2, 1, 0) /* size changing ops */ diff --git a/tcg/optimize.c b/tcg/optimize.c index fbfcbf23cd..d324cbf7fe 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2317,7 +2317,6 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) { - TCGOpcode sext_opc = 0; TCGCond cond = op->args[3]; TCGArg ret, src1, src2; TCGOp *op2; @@ -2336,27 +2335,12 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) } sh = ctz64(val); - switch (ctx->type) { - case TCG_TYPE_I32: - if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, sh, 1)) { - sext_opc = INDEX_op_sextract_i32; - } - break; - case TCG_TYPE_I64: - if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, sh, 1)) { - sext_opc = INDEX_op_sextract_i64; - } - break; - default: - g_assert_not_reached(); - } - ret = op->args[0]; src1 = op->args[1]; inv = cond == TCG_COND_TSTEQ; - if (sh && sext_opc && neg && !inv) { - op->opc = sext_opc; + if (sh && neg && !inv && TCG_TARGET_sextract_valid(ctx->type, sh, 1)) { + op->opc = INDEX_op_sextract; op->args[1] = src1; op->args[2] = sh; op->args[3] = 1; @@ -3019,7 +3003,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_bitsel_vec: done = fold_bitsel_vec(&ctx, op); break; - CASE_OP_32_64(sextract): + case INDEX_op_sextract: done = fold_sextract(&ctx, op); break; case INDEX_op_sub: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index ddade73b7b..d3f3c9d248 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1043,19 +1043,19 @@ void tcg_gen_sextract_i32(TCGv_i32 ret, TCGv_i32 arg, } if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, ofs, len)) { - tcg_gen_op4ii_i32(INDEX_op_sextract_i32, ret, arg, ofs, len); + tcg_gen_op4ii_i32(INDEX_op_sextract, ret, arg, ofs, len); return; } /* Assume that sign-extension, if available, is cheaper than a shift. */ if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, 0, ofs + len)) { - tcg_gen_op4ii_i32(INDEX_op_sextract_i32, ret, arg, 0, ofs + len); + tcg_gen_op4ii_i32(INDEX_op_sextract, ret, arg, 0, ofs + len); tcg_gen_sari_i32(ret, ret, ofs); return; } if (TCG_TARGET_sextract_valid(TCG_TYPE_I32, 0, len)) { tcg_gen_shri_i32(ret, arg, ofs); - tcg_gen_op4ii_i32(INDEX_op_sextract_i32, ret, ret, 0, len); + tcg_gen_op4ii_i32(INDEX_op_sextract, ret, ret, 0, len); return; } @@ -2747,19 +2747,19 @@ void tcg_gen_sextract_i64(TCGv_i64 ret, TCGv_i64 arg, } if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, ofs, len)) { - tcg_gen_op4ii_i64(INDEX_op_sextract_i64, ret, arg, ofs, len); + tcg_gen_op4ii_i64(INDEX_op_sextract, ret, arg, ofs, len); return; } /* Assume that sign-extension, if available, is cheaper than a shift. */ if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, 0, ofs + len)) { - tcg_gen_op4ii_i64(INDEX_op_sextract_i64, ret, arg, 0, ofs + len); + tcg_gen_op4ii_i64(INDEX_op_sextract, ret, arg, 0, ofs + len); tcg_gen_sari_i64(ret, ret, ofs); return; } if (TCG_TARGET_sextract_valid(TCG_TYPE_I64, 0, len)) { tcg_gen_shri_i64(ret, arg, ofs); - tcg_gen_op4ii_i64(INDEX_op_sextract_i64, ret, ret, 0, len); + tcg_gen_op4ii_i64(INDEX_op_sextract, ret, ret, 0, len); return; } diff --git a/tcg/tcg.c b/tcg/tcg.c index 7f5fa25062..c7ce13cda0 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1111,8 +1111,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_rotr, TCGOutOpBinary, outop_rotr), OUTOP(INDEX_op_sar, TCGOutOpBinary, outop_sar), OUTOP(INDEX_op_setcond, TCGOutOpSetcond, outop_setcond), - OUTOP(INDEX_op_sextract_i32, TCGOutOpExtract, outop_sextract), - OUTOP(INDEX_op_sextract_i64, TCGOutOpExtract, outop_sextract), + OUTOP(INDEX_op_sextract, TCGOutOpExtract, outop_sextract), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), @@ -2333,6 +2332,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_negsetcond: case INDEX_op_or: case INDEX_op_setcond: + case INDEX_op_sextract: case INDEX_op_xor: return has_type; @@ -2344,7 +2344,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: - case INDEX_op_sextract_i32: case INDEX_op_deposit_i32: return true; @@ -2372,7 +2371,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: - case INDEX_op_sextract_i64: case INDEX_op_deposit_i64: return TCG_TARGET_REG_BITS == 64; @@ -5520,8 +5518,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_extract: - case INDEX_op_sextract_i32: - case INDEX_op_sextract_i64: + case INDEX_op_sextract: { const TCGOutOpExtract *out = container_of(all_outop[op->opc], TCGOutOpExtract, base); diff --git a/tcg/tci.c b/tcg/tci.c index 6345029802..5a07d65db8 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -28,6 +28,7 @@ #define ctpop_tr glue(ctpop, TCG_TARGET_REG_BITS) #define extract_tr glue(extract, TCG_TARGET_REG_BITS) +#define sextract_tr glue(sextract, TCG_TARGET_REG_BITS) /* * Enable TCI assertions only when debugging TCG (and without NDEBUG defined). @@ -661,9 +662,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrbb(insn, &r0, &r1, &pos, &len); regs[r0] = extract_tr(regs[r1], pos, len); break; - case INDEX_op_sextract_i32: + case INDEX_op_sextract: tci_args_rrbb(insn, &r0, &r1, &pos, &len); - regs[r0] = sextract32(regs[r1], pos, len); + regs[r0] = sextract_tr(regs[r1], pos, len); break; case INDEX_op_brcond: tci_args_rl(insn, tb_ptr, &r0, &ptr); @@ -773,10 +774,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); regs[r0] = deposit64(regs[r1], pos, len, regs[r2]); break; - case INDEX_op_sextract_i64: - tci_args_rrbb(insn, &r0, &r1, &pos, &len); - regs[r0] = sextract64(regs[r1], pos, len); - break; case INDEX_op_ext_i32_i64: tci_args_rr(insn, &r0, &r1); regs[r0] = (int32_t)regs[r1]; @@ -1055,8 +1052,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) break; case INDEX_op_extract: - case INDEX_op_sextract_i32: - case INDEX_op_sextract_i64: + case INDEX_op_sextract: tci_args_rrbb(insn, &r0, &r1, &pos, &len); info->fprintf_func(info->stream, "%-12s %s,%s,%d,%d", op_name, str_r(r0), str_r(r1), pos, len); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index e013321ac7..9ba108ef8d 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -445,10 +445,7 @@ static const TCGOutOpExtract outop_extract = { static void tcg_out_sextract(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs, unsigned pos, unsigned len) { - TCGOpcode opc = type == TCG_TYPE_I32 ? - INDEX_op_sextract_i32 : - INDEX_op_sextract_i64; - tcg_out_op_rrbb(s, opc, rd, rs, pos, len); + tcg_out_op_rrbb(s, INDEX_op_sextract, rd, rs, pos, len); } static const TCGOutOpExtract outop_sextract = { From b7b7347fe391f134c7ea616c0593d3ea835d5eea Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Jan 2025 12:22:45 -0800 Subject: [PATCH 0456/2760] tcg: Convert ext_i32_i64 to TCGOutOpUnary Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 2 -- tcg/i386/tcg-target.c.inc | 2 -- tcg/loongarch64/tcg-target.c.inc | 2 -- tcg/mips/tcg-target.c.inc | 2 -- tcg/ppc/tcg-target.c.inc | 2 -- tcg/riscv/tcg-target.c.inc | 2 -- tcg/s390x/tcg-target.c.inc | 2 -- tcg/sparc64/tcg-target.c.inc | 2 -- tcg/tcg.c | 22 +++++++++++++++++++--- tcg/tci/tcg-target.c.inc | 2 -- 10 files changed, 19 insertions(+), 21 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 00400f6ea7..68f7a1cec2 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2710,7 +2710,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -3177,7 +3176,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: return C_O1_I1(r, r); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 6a5414ab3a..14b912beb7 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3413,7 +3413,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -4001,7 +4000,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrh_i64_i32: return C_O1_I1(r, 0); - case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: return C_O1_I1(r, r); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 2ea5234097..2a9c7fc10a 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1943,7 +1943,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -2468,7 +2467,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: - case INDEX_op_ext_i32_i64: case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: case INDEX_op_ld8u_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 56c58bf82d..e992a468eb 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2364,7 +2364,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -2391,7 +2390,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 3d1ffa9130..fea767573c 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3640,7 +3640,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -4270,7 +4269,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: return C_O1_I1(r, r); diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index dc2b487844..e5fe15c338 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2630,7 +2630,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -2877,7 +2876,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: - case INDEX_op_ext_i32_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index ab178bebc8..5c5a38c2c8 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2997,7 +2997,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: @@ -3471,7 +3470,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: return C_O1_I1(r, r); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 0f2bec21e9..e93ef8e7f2 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1883,7 +1883,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: default: g_assert_not_reached(); @@ -1909,7 +1908,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: diff --git a/tcg/tcg.c b/tcg/tcg.c index c7ce13cda0..6bce097eac 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1068,6 +1068,23 @@ QEMU_BUILD_BUG_ON((int)(offsetof(CPUNegativeOffsetState, tlb.f[0]) - < MIN_TLB_MASK_TABLE_OFS); #endif +#if TCG_TARGET_REG_BITS == 64 +/* + * We require these functions for slow-path function calls. + * Adapt them generically for opcode output. + */ + +static void tgen_exts_i32_i64(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_exts_i32_i64(s, a0, a1); +} + +static const TCGOutOpUnary outop_exts_i32_i64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_exts_i32_i64, +}; +#endif + /* * Register V as the TCGOutOp for O. * This verifies that V is of type T, otherwise give a nice compiler error. @@ -1122,6 +1139,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_setcond2_i32, TCGOutOpSetcond2, outop_setcond2), #else OUTOP(INDEX_op_bswap64, TCGOutOpUnary, outop_bswap64), + OUTOP(INDEX_op_ext_i32_i64, TCGOutOpUnary, outop_exts_i32_i64), #endif }; @@ -5412,9 +5430,6 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) /* emit instruction */ TCGType type = TCGOP_TYPE(op); switch (op->opc) { - case INDEX_op_ext_i32_i64: - tcg_out_exts_i32_i64(s, new_args[0], new_args[1]); - break; case INDEX_op_extu_i32_i64: tcg_out_extu_i32_i64(s, new_args[0], new_args[1]); break; @@ -5477,6 +5492,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_bswap64: + case INDEX_op_ext_i32_i64: assert(TCG_TARGET_REG_BITS == 64); /* fall through */ case INDEX_op_ctpop: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 9ba108ef8d..ecff90404f 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -55,7 +55,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: return C_O1_I1(r, r); @@ -1109,7 +1108,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_ext_i32_i64: /* Always emitted via tcg_reg_alloc_op. */ case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: From c1ad25de11ccf47a650c860f490864dcf7fe7675 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Jan 2025 12:34:45 -0800 Subject: [PATCH 0457/2760] tcg: Convert extu_i32_i64 to TCGOutOpUnary Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 2 -- tcg/i386/tcg-target.c.inc | 2 -- tcg/loongarch64/tcg-target.c.inc | 2 -- tcg/mips/tcg-target.c.inc | 2 -- tcg/ppc/tcg-target.c.inc | 2 -- tcg/riscv/tcg-target.c.inc | 2 -- tcg/s390x/tcg-target.c.inc | 4 ---- tcg/sparc64/tcg-target.c.inc | 2 -- tcg/tcg.c | 15 ++++++++++++--- tcg/tci/tcg-target.c.inc | 2 -- 10 files changed, 12 insertions(+), 23 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 68f7a1cec2..44314f6a0f 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2710,7 +2710,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); @@ -3176,7 +3175,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_extu_i32_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 14b912beb7..8371cfaf5a 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3413,7 +3413,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); @@ -4000,7 +3999,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrh_i64_i32: return C_O1_I1(r, 0); - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: return C_O1_I1(r, r); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 2a9c7fc10a..60356d5dfd 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1943,7 +1943,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); @@ -2464,7 +2463,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i128: return C_O0_I3(r, r, r); - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ld8s_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index e992a468eb..b6b7070fbb 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2364,7 +2364,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); @@ -2390,7 +2389,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: return C_O1_I1(r, r); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index fea767573c..e1767f1d6c 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3640,7 +3640,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); @@ -4269,7 +4268,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_extu_i32_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index e5fe15c338..48d4325097 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2630,7 +2630,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); @@ -2873,7 +2872,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: return C_O1_I1(r, r); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 5c5a38c2c8..d81b8fb8f4 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2997,7 +2997,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); @@ -3470,9 +3469,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_extu_i32_i64: - return C_O1_I1(r, r); - case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index e93ef8e7f2..d52907f7e3 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1883,7 +1883,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extu_i32_i64: default: g_assert_not_reached(); } @@ -1908,7 +1907,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_extu_i32_i64: case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); diff --git a/tcg/tcg.c b/tcg/tcg.c index 6bce097eac..0b2dc17600 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1083,6 +1083,16 @@ static const TCGOutOpUnary outop_exts_i32_i64 = { .base.static_constraint = C_O1_I1(r, r), .out_rr = tgen_exts_i32_i64, }; + +static void tgen_extu_i32_i64(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_extu_i32_i64(s, a0, a1); +} + +static const TCGOutOpUnary outop_extu_i32_i64 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extu_i32_i64, +}; #endif /* @@ -1140,6 +1150,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { #else OUTOP(INDEX_op_bswap64, TCGOutOpUnary, outop_bswap64), OUTOP(INDEX_op_ext_i32_i64, TCGOutOpUnary, outop_exts_i32_i64), + OUTOP(INDEX_op_extu_i32_i64, TCGOutOpUnary, outop_extu_i32_i64), #endif }; @@ -5430,9 +5441,6 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) /* emit instruction */ TCGType type = TCGOP_TYPE(op); switch (op->opc) { - case INDEX_op_extu_i32_i64: - tcg_out_extu_i32_i64(s, new_args[0], new_args[1]); - break; case INDEX_op_extrl_i64_i32: tcg_out_extrl_i64_i32(s, new_args[0], new_args[1]); break; @@ -5493,6 +5501,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_bswap64: case INDEX_op_ext_i32_i64: + case INDEX_op_extu_i32_i64: assert(TCG_TARGET_REG_BITS == 64); /* fall through */ case INDEX_op_ctpop: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index ecff90404f..3cf2913acd 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -55,7 +55,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32u_i64: case INDEX_op_ld32s_i64: case INDEX_op_ld_i64: - case INDEX_op_extu_i32_i64: return C_O1_I1(r, r); case INDEX_op_st8_i32: @@ -1108,7 +1107,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); From 1e6fec9dd90756b22331a3c4c6859c51ad3a2c3e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Jan 2025 12:57:13 -0800 Subject: [PATCH 0458/2760] tcg: Convert extrl_i64_i32 to TCGOutOpUnary Drop the cast from TCGv_i64 to TCGv_i32 in tcg_gen_extrl_i64_i32 an emit extrl_i64_i32 unconditionally. Move that special case to tcg_gen_code when we find out if the output is live or dead. In this way even hosts that canonicalize truncations can make use of a store directly from the 64-bit host register. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 1 - tcg/i386/tcg-target.c.inc | 4 ---- tcg/loongarch64/tcg-target.c.inc | 2 -- tcg/mips/tcg-target.c.inc | 2 -- tcg/ppc/tcg-target.c.inc | 1 - tcg/riscv/tcg-target.c.inc | 2 -- tcg/s390x/tcg-target.c.inc | 1 - tcg/tcg-op.c | 4 +--- tcg/tcg.c | 35 +++++++++++++++++++++++++++----- tcg/tci/tcg-target.c.inc | 1 - 10 files changed, 31 insertions(+), 22 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 44314f6a0f..8abc5f26da 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2710,7 +2710,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); } diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 8371cfaf5a..9bae60d3b6 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3413,7 +3413,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); } @@ -3999,9 +3998,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrh_i64_i32: return C_O1_I1(r, 0); - case INDEX_op_extrl_i64_i32: - return C_O1_I1(r, r); - case INDEX_op_extract2_i32: case INDEX_op_extract2_i64: return C_O1_I2(r, 0, r); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 60356d5dfd..fae1a58c94 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1943,7 +1943,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); } @@ -2463,7 +2462,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i128: return C_O0_I3(r, r, r); - case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index b6b7070fbb..095eb8f672 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2364,7 +2364,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); } @@ -2389,7 +2388,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: return C_O1_I1(r, r); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index e1767f1d6c..bb03efe055 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3640,7 +3640,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); } diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 48d4325097..76ad2df410 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2630,7 +2630,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); } @@ -2872,7 +2871,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: return C_O1_I1(r, r); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index d81b8fb8f4..1ea041c75f 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2997,7 +2997,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); } diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index d3f3c9d248..7ecd1f6c8f 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2962,11 +2962,9 @@ void tcg_gen_extrl_i64_i32(TCGv_i32 ret, TCGv_i64 arg) { if (TCG_TARGET_REG_BITS == 32) { tcg_gen_mov_i32(ret, TCGV_LOW(arg)); - } else if (TCG_TARGET_HAS_extr_i64_i32) { + } else { tcg_gen_op2(INDEX_op_extrl_i64_i32, TCG_TYPE_I32, tcgv_i32_arg(ret), tcgv_i64_arg(arg)); - } else { - tcg_gen_mov_i32(ret, (TCGv_i32)arg); } } diff --git a/tcg/tcg.c b/tcg/tcg.c index 0b2dc17600..8fcaf54d32 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1093,6 +1093,16 @@ static const TCGOutOpUnary outop_extu_i32_i64 = { .base.static_constraint = C_O1_I1(r, r), .out_rr = tgen_extu_i32_i64, }; + +static void tgen_extrl_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_extrl_i64_i32(s, a0, a1); +} + +static const TCGOutOpUnary outop_extrl_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = TCG_TARGET_HAS_extr_i64_i32 ? tgen_extrl_i64_i32 : NULL, +}; #endif /* @@ -1151,6 +1161,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_bswap64, TCGOutOpUnary, outop_bswap64), OUTOP(INDEX_op_ext_i32_i64, TCGOutOpUnary, outop_exts_i32_i64), OUTOP(INDEX_op_extu_i32_i64, TCGOutOpUnary, outop_extu_i32_i64), + OUTOP(INDEX_op_extrl_i64_i32, TCGOutOpUnary, outop_extrl_i64_i32), #endif }; @@ -2400,12 +2411,12 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: case INDEX_op_deposit_i64: return TCG_TARGET_REG_BITS == 64; case INDEX_op_extract2_i64: return TCG_TARGET_HAS_extract2_i64; - case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: return TCG_TARGET_HAS_extr_i64_i32; case INDEX_op_add2_i64: @@ -5441,10 +5452,6 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) /* emit instruction */ TCGType type = TCGOP_TYPE(op); switch (op->opc) { - case INDEX_op_extrl_i64_i32: - tcg_out_extrl_i64_i32(s, new_args[0], new_args[1]); - break; - case INDEX_op_add: case INDEX_op_and: case INDEX_op_andc: @@ -5502,6 +5509,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_bswap64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: + case INDEX_op_extrl_i64_i32: assert(TCG_TARGET_REG_BITS == 64); /* fall through */ case INDEX_op_ctpop: @@ -6660,6 +6668,22 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) TCGOpcode opc = op->opc; switch (opc) { + case INDEX_op_extrl_i64_i32: + assert(TCG_TARGET_REG_BITS == 64); + /* + * If TCG_TYPE_I32 is represented in some canonical form, + * e.g. zero or sign-extended, then emit as a unary op. + * Otherwise we can treat this as a plain move. + * If the output dies, treat this as a plain move, because + * this will be implemented with a store. + */ + if (TCG_TARGET_HAS_extr_i64_i32) { + TCGLifeData arg_life = op->life; + if (!IS_DEAD_ARG(0)) { + goto do_default; + } + } + /* fall through */ case INDEX_op_mov: case INDEX_op_mov_vec: tcg_reg_alloc_mov(s, op); @@ -6702,6 +6726,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) } /* fall through */ default: + do_default: /* Sanity check that we've not introduced any unhandled opcodes. */ tcg_debug_assert(tcg_op_supported(opc, TCGOP_TYPE(op), TCGOP_FLAGS(op))); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 3cf2913acd..e9b46d5e66 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -1107,7 +1107,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - case INDEX_op_extrl_i64_i32: default: g_assert_not_reached(); } From b3b139766424d022dc3455b711837dc6acf0724b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Jan 2025 13:37:28 -0800 Subject: [PATCH 0459/2760] tcg: Convert extrh_i64_i32 to TCGOutOpUnary At the same time, make extrh_i64_i32 mandatory. This closes a hole in which move arguments could be cast between TCGv_i32 and TCGv_i64. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 10 ++++++++++ tcg/i386/tcg-target.c.inc | 20 +++++++++++++------- tcg/loongarch64/tcg-target.c.inc | 15 ++++++++++----- tcg/mips/tcg-target.c.inc | 17 ++++++++++++----- tcg/ppc/tcg-target.c.inc | 12 ++++++++++++ tcg/riscv/tcg-target.c.inc | 15 ++++++++++----- tcg/s390x/tcg-target.c.inc | 10 ++++++++++ tcg/sparc64/tcg-target.c.inc | 10 ++++++++++ tcg/tcg-op.c | 7 +------ tcg/tcg.c | 5 +++-- tcg/tci/tcg-target.c.inc | 12 ++++++++++++ 11 files changed, 103 insertions(+), 30 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 8abc5f26da..4ea1aebc5e 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2220,6 +2220,16 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_ubfm(s, TCG_TYPE_I64, a0, a1, 32, 63); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; + static void tgen_mul(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 9bae60d3b6..63c9aae26e 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2794,6 +2794,18 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +#if TCG_TARGET_REG_BITS == 64 +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_shifti(s, SHIFT_SHR + P_REXW, a0, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, 0), + .out_rr = tgen_extrh_i64_i32, +}; +#endif /* TCG_TARGET_REG_BITS == 64 */ + static void tgen_mul(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3212,6 +3224,7 @@ static const TCGOutOpExtract outop_sextract = { .out_rr = tgen_sextract, }; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -3363,10 +3376,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I64, a0, a1, a2); } break; - - case INDEX_op_extrh_i64_i32: - tcg_out_shifti(s, SHIFT_SHR + P_REXW, a0, 32); - break; #endif OP_32_64(deposit): @@ -3995,9 +4004,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_extrh_i64_i32: - return C_O1_I1(r, 0); - case INDEX_op_extract2_i32: case INDEX_op_extract2_i64: return C_O1_I2(r, 0, r); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index fae1a58c94..1062eb1883 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1467,6 +1467,16 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_opc_srai_d(s, a0, a1, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; + static void tgen_mul(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1867,10 +1877,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_b(s, 0); break; - case INDEX_op_extrh_i64_i32: - tcg_out_opc_srai_d(s, a0, a1, 32); - break; - case INDEX_op_deposit_i32: tcg_out_opc_bstrins_w(s, a0, a2, args[3], args[3] + args[4] - 1); break; @@ -2462,7 +2468,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i128: return C_O0_I3(r, r, r); - case INDEX_op_extrh_i64_i32: case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: case INDEX_op_ld8u_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 095eb8f672..ad0482902d 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1793,6 +1793,18 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +#if TCG_TARGET_REG_BITS == 64 +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_dsra(s, a0, a1, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; +#endif + static void tgen_mul(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2315,10 +2327,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_extrh_i64_i32: - tcg_out_dsra(s, a0, a1, 32); - break; - case INDEX_op_deposit_i32: tcg_out_opc_bf(s, OPC_INS, a0, a2, args[3] + args[4] - 1, args[3]); break; @@ -2388,7 +2396,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_extrh_i64_i32: return C_O1_I1(r, r); case INDEX_op_st8_i32: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index bb03efe055..ba6d7556f7 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2965,6 +2965,18 @@ static void tgen_eqv(TCGContext *s, TCGType type, tcg_out32(s, EQV | SAB(a1, a0, a2)); } +#if TCG_TARGET_REG_BITS == 64 +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_shri64(s, a0, a1, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; +#endif + static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 76ad2df410..46b4e1167c 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2151,6 +2151,16 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_opc_imm(s, OPC_SRAI, a0, a1, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; + static void tgen_mul(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2619,10 +2629,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); break; - case INDEX_op_extrh_i64_i32: - tcg_out_opc_imm(s, OPC_SRAI, a0, a1, 32); - break; - case INDEX_op_mb: tcg_out_mb(s, a0); break; @@ -2871,7 +2877,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ld32s_i64: case INDEX_op_ld32u_i64: case INDEX_op_ld_i64: - case INDEX_op_extrh_i64_i32: return C_O1_I1(r, r); case INDEX_op_st8_i32: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 1ea041c75f..3b3749efd3 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2409,6 +2409,16 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_sh64(s, RSY_SRLG, a0, a1, TCG_REG_NONE, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; + static void tgen_mul(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index d52907f7e3..c1cce7c196 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1501,6 +1501,16 @@ static const TCGOutOpBinary outop_eqv = { .base.static_constraint = C_NotImplemented, }; +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_arithi(s, a0, a1, 32, SHIFT_SRLX); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; + static void tgen_mul(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 7ecd1f6c8f..b88f411ece 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -2972,14 +2972,9 @@ void tcg_gen_extrh_i64_i32(TCGv_i32 ret, TCGv_i64 arg) { if (TCG_TARGET_REG_BITS == 32) { tcg_gen_mov_i32(ret, TCGV_HIGH(arg)); - } else if (TCG_TARGET_HAS_extr_i64_i32) { + } else { tcg_gen_op2(INDEX_op_extrh_i64_i32, TCG_TYPE_I32, tcgv_i32_arg(ret), tcgv_i64_arg(arg)); - } else { - TCGv_i64 t = tcg_temp_ebb_new_i64(); - tcg_gen_shri_i64(t, arg, 32); - tcg_gen_mov_i32(ret, (TCGv_i32)t); - tcg_temp_free_i64(t); } } diff --git a/tcg/tcg.c b/tcg/tcg.c index 8fcaf54d32..ce583b824c 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1162,6 +1162,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_ext_i32_i64, TCGOutOpUnary, outop_exts_i32_i64), OUTOP(INDEX_op_extu_i32_i64, TCGOutOpUnary, outop_extu_i32_i64), OUTOP(INDEX_op_extrl_i64_i32, TCGOutOpUnary, outop_extrl_i64_i32), + OUTOP(INDEX_op_extrh_i64_i32, TCGOutOpUnary, outop_extrh_i64_i32), #endif }; @@ -2412,13 +2413,12 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: + case INDEX_op_extrh_i64_i32: case INDEX_op_deposit_i64: return TCG_TARGET_REG_BITS == 64; case INDEX_op_extract2_i64: return TCG_TARGET_HAS_extract2_i64; - case INDEX_op_extrh_i64_i32: - return TCG_TARGET_HAS_extr_i64_i32; case INDEX_op_add2_i64: return TCG_TARGET_HAS_add2_i64; case INDEX_op_sub2_i64: @@ -5510,6 +5510,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: + case INDEX_op_extrh_i64_i32: assert(TCG_TARGET_REG_BITS == 64); /* fall through */ case INDEX_op_ctpop: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index e9b46d5e66..d84d01e098 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -670,6 +670,18 @@ static const TCGOutOpBinary outop_eqv = { .out_rrr = tgen_eqv, }; +#if TCG_TARGET_REG_BITS == 64 +static void tgen_extrh_i64_i32(TCGContext *s, TCGType t, TCGReg a0, TCGReg a1) +{ + tcg_out_extract(s, TCG_TYPE_I64, a0, a1, 32, 32); +} + +static const TCGOutOpUnary outop_extrh_i64_i32 = { + .base.static_constraint = C_O1_I1(r, r), + .out_rr = tgen_extrh_i64_i32, +}; +#endif + static void tgen_mul(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { From cf4905c03135f1181e86c618426f8d6c703b38c0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Jan 2025 20:29:41 -0800 Subject: [PATCH 0460/2760] tcg: Convert deposit to TCGOutOpDeposit Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 30 +++++-------- tcg/arm/tcg-target.c.inc | 29 ++++++------ tcg/i386/tcg-target.c.inc | 76 ++++++++++++++++---------------- tcg/loongarch64/tcg-target.c.inc | 27 +++++++----- tcg/mips/tcg-target.c.inc | 27 +++++++----- tcg/ppc/tcg-target.c.inc | 44 +++++++++--------- tcg/riscv/tcg-target.c.inc | 4 ++ tcg/s390x/tcg-target.c.inc | 60 +++++++++++++------------ tcg/sparc64/tcg-target.c.inc | 4 ++ tcg/tcg.c | 33 ++++++++++++++ tcg/tci.c | 8 ++-- tcg/tci/tcg-target.c.inc | 19 ++++---- 12 files changed, 206 insertions(+), 155 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 4ea1aebc5e..62b045c222 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1347,15 +1347,6 @@ static inline void tcg_out_extr(TCGContext *s, TCGType ext, TCGReg rd, tcg_out_insn(s, 3403, EXTR, ext, rd, rn, rm, a); } -static inline void tcg_out_dep(TCGContext *s, TCGType ext, TCGReg rd, - TCGReg rn, unsigned lsb, unsigned width) -{ - unsigned size = ext ? 64 : 32; - unsigned a = (size - lsb) & (size - 1); - unsigned b = width - 1; - tcg_out_bfm(s, ext, rd, rn, a, b); -} - static void tgen_cmp(TCGContext *s, TCGType ext, TCGCond cond, TCGReg a, TCGReg b) { @@ -2577,6 +2568,18 @@ static const TCGOutOpMovcond outop_movcond = { .out = tgen_movcond, }; +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) +{ + unsigned mask = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_bfm(s, type, a0, a2, -ofs & mask, len - 1); +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, 0, rz), + .out_rrr = tgen_deposit, +}; + static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, unsigned ofs, unsigned len) { @@ -2684,11 +2687,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], false); break; - case INDEX_op_deposit_i64: - case INDEX_op_deposit_i32: - tcg_out_dep(s, ext, a0, a2, args[3], args[4]); - break; - case INDEX_op_extract2_i64: case INDEX_op_extract2_i32: tcg_out_extr(s, ext, a0, a2, a1, args[3]); @@ -3206,10 +3204,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i128: return C_O0_I3(rz, rz, r); - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: - return C_O1_I2(r, 0, rz); - case INDEX_op_extract2_i32: case INDEX_op_extract2_i64: return C_O1_I2(r, rz, rz); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index aebe48679c..2bf6bfe274 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -969,18 +969,27 @@ static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rn) g_assert_not_reached(); } -static void tcg_out_deposit(TCGContext *s, ARMCond cond, TCGReg rd, - TCGArg a1, int ofs, int len, bool const_a1) +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) { - if (const_a1) { - /* bfi becomes bfc with rn == 15. */ - a1 = 15; - } /* bfi/bfc */ - tcg_out32(s, 0x07c00010 | (cond << 28) | (rd << 12) | a1 + tcg_out32(s, 0x07c00010 | (COND_AL << 28) | (a0 << 12) | a1 | (ofs << 7) | ((ofs + len - 1) << 16)); } +static void tgen_depositi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + tcg_target_long a2, unsigned ofs, unsigned len) +{ + /* bfi becomes bfc with rn == 15. */ + tgen_deposit(s, type, a0, a1, 15, ofs, len); +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, 0, rZ), + .out_rrr = tgen_deposit, + .out_rri = tgen_depositi, +}; + static void tgen_extract(TCGContext *s, TCGType type, TCGReg rd, TCGReg rn, unsigned ofs, unsigned len) { @@ -2402,10 +2411,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); break; - case INDEX_op_deposit_i32: - tcg_out_deposit(s, COND_AL, args[0], args[2], - args[3], args[4], const_args[2]); - break; case INDEX_op_extract2_i32: /* ??? These optimization vs zero should be generic. */ /* ??? But we can't substitute 2 for 1 in the opcode stream yet. */ @@ -2459,8 +2464,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i32: return C_O0_I2(r, r); - case INDEX_op_deposit_i32: - return C_O1_I2(r, 0, rZ); case INDEX_op_extract2_i32: return C_O1_I2(r, rZ, rZ); case INDEX_op_add2_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 63c9aae26e..1dd9741f45 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3150,6 +3150,43 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) +{ + if (ofs == 0 && len == 8) { + tcg_out_modrm(s, OPC_MOVB_EvGv | P_REXB_R | P_REXB_RM, a2, a0); + } else if (ofs == 0 && len == 16) { + tcg_out_modrm(s, OPC_MOVL_EvGv | P_DATA16, a2, a0); + } else if (TCG_TARGET_REG_BITS == 32 && ofs == 8 && len == 8) { + tcg_out_modrm(s, OPC_MOVB_EvGv, a2, a0 + 4); + } else { + g_assert_not_reached(); + } +} + +static void tgen_depositi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + tcg_target_long a2, unsigned ofs, unsigned len) +{ + if (ofs == 0 && len == 8) { + tcg_out_opc(s, OPC_MOVB_Ib | P_REXB_RM | LOWREGMASK(a0), 0, a0, 0); + tcg_out8(s, a2); + } else if (ofs == 0 && len == 16) { + tcg_out_opc(s, OPC_MOVL_Iv | P_DATA16 | LOWREGMASK(a0), 0, a0, 0); + tcg_out16(s, a2); + } else if (TCG_TARGET_REG_BITS == 32 && ofs == 8 && len == 8) { + tcg_out8(s, OPC_MOVB_Ib + a0 + 4); + tcg_out8(s, a2); + } else { + g_assert_not_reached(); + } +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(q, 0, qi), + .out_rrr = tgen_deposit, + .out_rri = tgen_depositi, +}; + static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, unsigned ofs, unsigned len) { @@ -3230,7 +3267,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { TCGArg a0, a1, a2; - int const_a2, rexw; + int rexw; #if TCG_TARGET_REG_BITS == 64 # define OP_32_64(x) \ @@ -3245,7 +3282,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a0 = args[0]; a1 = args[1]; a2 = args[2]; - const_a2 = const_args[2]; rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; switch (opc) { @@ -3378,38 +3414,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; #endif - OP_32_64(deposit): - if (args[3] == 0 && args[4] == 8) { - /* load bits 0..7 */ - if (const_a2) { - tcg_out_opc(s, OPC_MOVB_Ib | P_REXB_RM | LOWREGMASK(a0), - 0, a0, 0); - tcg_out8(s, a2); - } else { - tcg_out_modrm(s, OPC_MOVB_EvGv | P_REXB_R | P_REXB_RM, a2, a0); - } - } else if (TCG_TARGET_REG_BITS == 32 && args[3] == 8 && args[4] == 8) { - /* load bits 8..15 */ - if (const_a2) { - tcg_out8(s, OPC_MOVB_Ib + a0 + 4); - tcg_out8(s, a2); - } else { - tcg_out_modrm(s, OPC_MOVB_EvGv, a2, a0 + 4); - } - } else if (args[3] == 0 && args[4] == 16) { - /* load bits 0..15 */ - if (const_a2) { - tcg_out_opc(s, OPC_MOVL_Iv | P_DATA16 | LOWREGMASK(a0), - 0, a0, 0); - tcg_out16(s, a2); - } else { - tcg_out_modrm(s, OPC_MOVL_EvGv | P_DATA16, a2, a0); - } - } else { - g_assert_not_reached(); - } - break; - OP_32_64(extract2): /* Note that SHRD outputs to the r/m operand. */ tcg_out_modrm(s, OPC_SHRD_Ib + rexw, a2, a0); @@ -4008,10 +4012,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extract2_i64: return C_O1_I2(r, 0, r); - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: - return C_O1_I2(q, 0, qi); - case INDEX_op_add2_i32: case INDEX_op_add2_i64: case INDEX_op_sub2_i32: diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 1062eb1883..0b64efc078 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1809,6 +1809,21 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_bstrins_w(s, a0, a2, ofs, ofs + len - 1); + } else { + tcg_out_opc_bstrins_d(s, a0, a2, ofs, ofs + len - 1); + } +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, 0, rz), + .out_rrr = tgen_deposit, +}; + static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, unsigned ofs, unsigned len) { @@ -1877,13 +1892,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_b(s, 0); break; - case INDEX_op_deposit_i32: - tcg_out_opc_bstrins_w(s, a0, a2, args[3], args[3] + args[4] - 1); - break; - case INDEX_op_deposit_i64: - tcg_out_opc_bstrins_d(s, a0, a2, args[3], args[3] + args[4] - 1); - break; - case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: tcg_out_ldst(s, OPC_LD_B, a0, a1, a2); @@ -2484,11 +2492,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: - /* Must deposit into the same register as input */ - return C_O1_I2(r, 0, rz); - case INDEX_op_ld_vec: case INDEX_op_dupm_vec: case INDEX_op_dup_vec: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index ad0482902d..cd648ab1df 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2215,6 +2215,22 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I32) { + tcg_out_opc_bf(s, OPC_INS, a0, a2, ofs + len - 1, ofs); + } else { + tcg_out_opc_bf64(s, OPC_DINS, OPC_DINSM, OPC_DINSU, a0, a2, + ofs + len - 1, ofs); + } +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, 0, rz), + .out_rrr = tgen_deposit, +}; + static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, unsigned ofs, unsigned len) { @@ -2327,14 +2343,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, i1, a0, a1, a2); break; - case INDEX_op_deposit_i32: - tcg_out_opc_bf(s, OPC_INS, a0, a2, args[3] + args[4] - 1, args[3]); - break; - case INDEX_op_deposit_i64: - tcg_out_opc_bf64(s, OPC_DINS, OPC_DINSM, OPC_DINSU, a0, a2, - args[3] + args[4] - 1, args[3]); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, 0, a1, a2, TCG_TYPE_I32); break; @@ -2407,9 +2415,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: - return C_O1_I2(r, 0, rz); case INDEX_op_add2_i32: case INDEX_op_sub2_i32: return C_O2_I4(r, r, rz, rz, rN, rN); diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index ba6d7556f7..fc92a4896d 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3429,6 +3429,28 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) +{ + if (type == TCG_TYPE_I32) { + tcg_out_rlw(s, RLWIMI, a0, a2, ofs, 32 - ofs - len, 31 - ofs); + } else { + tcg_out_rld(s, RLDIMI, a0, a2, ofs, 64 - ofs - len); + } +} + +static void tgen_depositi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + tcg_target_long a2, unsigned ofs, unsigned len) +{ + tgen_andi(s, type, a0, a1, ~MAKE_64BIT_MASK(ofs, len)); +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, 0, rZ), + .out_rrr = tgen_deposit, + .out_rri = tgen_depositi, +}; + static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, unsigned ofs, unsigned len) { @@ -3575,25 +3597,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; - case INDEX_op_deposit_i32: - if (const_args[2]) { - uint32_t mask = ((2u << (args[4] - 1)) - 1) << args[3]; - tcg_out_andi32(s, args[0], args[0], ~mask); - } else { - tcg_out_rlw(s, RLWIMI, args[0], args[2], args[3], - 32 - args[3] - args[4], 31 - args[3]); - } - break; - case INDEX_op_deposit_i64: - if (const_args[2]) { - uint64_t mask = ((2ull << (args[4] - 1)) - 1) << args[3]; - tcg_out_andi64(s, args[0], args[0], ~mask); - } else { - tcg_out_rld(s, RLDIMI, args[0], args[2], args[3], - 64 - args[3] - args[4]); - } - break; - #if TCG_TARGET_REG_BITS == 64 case INDEX_op_add2_i64: #else @@ -4290,9 +4293,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: - return C_O1_I2(r, 0, rZ); case INDEX_op_add2_i64: case INDEX_op_add2_i32: return C_O2_I4(r, r, r, r, rI, rZM); diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 46b4e1167c..371e0c24c8 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2482,6 +2482,10 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, unsigned ofs, unsigned len) { diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 3b3749efd3..d72393315d 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1555,14 +1555,40 @@ static const TCGOutOpMovcond outop_movcond = { .out = tgen_movcond, }; -static void tgen_deposit(TCGContext *s, TCGReg dest, TCGReg src, - int ofs, int len, int z) +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) { - int lsb = (63 - ofs); - int msb = lsb - (len - 1); - tcg_out_risbg(s, dest, src, msb, lsb, ofs, z); + unsigned lsb = (63 - ofs); + unsigned msb = lsb - (len - 1); + + /* + * Since we can't support "0Z" as a constraint, we allow a1 in + * any register. Fix things up as if a matching constraint. + */ + if (a0 != a1) { + if (a0 == a2) { + tcg_out_mov(s, type, TCG_TMP0, a2); + a2 = TCG_TMP0; + } + tcg_out_mov(s, type, a0, a1); + } + tcg_out_risbg(s, a0, a2, msb, lsb, ofs, false); +} + +static void tgen_depositz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a2, + unsigned ofs, unsigned len) +{ + unsigned lsb = (63 - ofs); + unsigned msb = lsb - (len - 1); + tcg_out_risbg(s, a0, a2, msb, lsb, ofs, true); } +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, rZ, r), + .out_rrr = tgen_deposit, + .out_rzr = tgen_depositz, +}; + static void tgen_extract(TCGContext *s, TCGType type, TCGReg dest, TCGReg src, unsigned ofs, unsigned len) { @@ -2845,7 +2871,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - TCGArg a0, a1, a2; + TCGArg a0; switch (opc) { case INDEX_op_goto_ptr: @@ -2977,24 +3003,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_insn(s, RRE, SLBGR, args[1], args[5]); break; - OP_32_64(deposit): - a0 = args[0], a1 = args[1], a2 = args[2]; - if (const_args[1]) { - tgen_deposit(s, a0, a2, args[3], args[4], 1); - } else { - /* Since we can't support "0Z" as a constraint, we allow a1 in - any register. Fix things up as if a matching constraint. */ - if (a0 != a1) { - if (a0 == a2) { - tcg_out_mov(s, type, TCG_TMP0, a2); - a2 = TCG_TMP0; - } - tcg_out_mov(s, type, a0, a1); - } - tgen_deposit(s, a0, a2, args[3], args[4], 0); - } - break; - case INDEX_op_mb: /* The host memory model is quite strong, we simply need to serialize the instruction stream. */ @@ -3489,10 +3497,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i128: return C_O0_I3(o, m, r); - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: - return C_O1_I2(r, rZ, r); - case INDEX_op_add2_i32: case INDEX_op_sub2_i32: return C_N1_O1_I4(r, r, 0, 1, ri, r); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index c1cce7c196..741de260e9 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1767,6 +1767,10 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, unsigned ofs, unsigned len) { diff --git a/tcg/tcg.c b/tcg/tcg.c index ce583b824c..b9e9454654 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1007,6 +1007,16 @@ typedef struct TCGOutOpBswap { TCGReg a0, TCGReg a1, unsigned flags); } TCGOutOpBswap; +typedef struct TCGOutOpDeposit { + TCGOutOp base; + void (*out_rrr)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len); + void (*out_rri)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + tcg_target_long a2, unsigned ofs, unsigned len); + void (*out_rzr)(TCGContext *s, TCGType type, TCGReg a0, + TCGReg a2, unsigned ofs, unsigned len); +} TCGOutOpDeposit; + typedef struct TCGOutOpDivRem { TCGOutOp base; void (*out_rr01r)(TCGContext *s, TCGType type, @@ -1123,6 +1133,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), OUTOP(INDEX_op_ctpop, TCGOutOpUnary, outop_ctpop), OUTOP(INDEX_op_ctz, TCGOutOpBinary, outop_ctz), + OUTOP(INDEX_op_deposit_i32, TCGOutOpDeposit, outop_deposit), + OUTOP(INDEX_op_deposit_i64, TCGOutOpDeposit, outop_deposit), OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), @@ -5537,6 +5549,27 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_deposit_i32: + case INDEX_op_deposit_i64: + { + const TCGOutOpDeposit *out = &outop_deposit; + + if (const_args[2]) { + tcg_debug_assert(!const_args[1]); + out->out_rri(s, type, new_args[0], new_args[1], + new_args[2], new_args[3], new_args[4]); + } else if (const_args[1]) { + tcg_debug_assert(new_args[1] == 0); + tcg_debug_assert(!const_args[2]); + out->out_rzr(s, type, new_args[0], new_args[2], + new_args[3], new_args[4]); + } else { + out->out_rrr(s, type, new_args[0], new_args[1], + new_args[2], new_args[3], new_args[4]); + } + } + break; + case INDEX_op_divs2: case INDEX_op_divu2: { diff --git a/tcg/tci.c b/tcg/tci.c index 5a07d65db8..595416a192 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -27,6 +27,7 @@ #define ctpop_tr glue(ctpop, TCG_TARGET_REG_BITS) +#define deposit_tr glue(deposit, TCG_TARGET_REG_BITS) #define extract_tr glue(extract, TCG_TARGET_REG_BITS) #define sextract_tr glue(sextract, TCG_TARGET_REG_BITS) @@ -655,8 +656,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, regs[r0] = ror32(regs[r1], regs[r2] & 31); break; case INDEX_op_deposit_i32: + case INDEX_op_deposit_i64: tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); - regs[r0] = deposit32(regs[r1], pos, len, regs[r2]); + regs[r0] = deposit_tr(regs[r1], pos, len, regs[r2]); break; case INDEX_op_extract: tci_args_rrbb(insn, &r0, &r1, &pos, &len); @@ -770,10 +772,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ror64(regs[r1], regs[r2] & 63); break; - case INDEX_op_deposit_i64: - tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); - regs[r0] = deposit64(regs[r1], pos, len, regs[r2]); - break; case INDEX_op_ext_i32_i64: tci_args_rr(insn, &r0, &r1); regs[r0] = (int32_t)regs[r1]; diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index d84d01e098..566c2fb0d0 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -66,10 +66,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: - return C_O1_I2(r, r, r); - case INDEX_op_add2_i32: case INDEX_op_add2_i64: case INDEX_op_sub2_i32: @@ -623,6 +619,17 @@ static const TCGOutOpBinary outop_ctz = { .out_rrr = tgen_ctz, }; +static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned ofs, unsigned len) +{ + tcg_out_op_rrrbb(s, INDEX_op_deposit_i64, a0, a1, a2, ofs, len); +} + +static const TCGOutOpDeposit outop_deposit = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_deposit, +}; + static void tgen_divs(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1084,10 +1091,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, opc, args[0], args[1], args[2]); break; - CASE_32_64(deposit) - tcg_out_op_rrrbb(s, opc, args[0], args[1], args[2], args[3], args[4]); - break; - CASE_32_64(add2) CASE_32_64(sub2) tcg_out_op_rrrrrr(s, opc, args[0], args[1], args[2], From a5a8dd33aad1ada9b05968e8b93033bdbdfdbca2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Jan 2025 20:42:13 -0800 Subject: [PATCH 0461/2760] tcg/aarch64: Improve deposit Use ANDI for deposit 0 into a register. Use UBFIZ, aka UBFM, for deposit register into 0. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-con-set.h | 2 +- tcg/aarch64/tcg-target.c.inc | 29 ++++++++++++++++++++++++++++- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/tcg/aarch64/tcg-target-con-set.h b/tcg/aarch64/tcg-target-con-set.h index 1281e5efc0..2eda499cd3 100644 --- a/tcg/aarch64/tcg-target-con-set.h +++ b/tcg/aarch64/tcg-target-con-set.h @@ -18,7 +18,6 @@ C_O1_I1(r, r) C_O1_I1(w, r) C_O1_I1(w, w) C_O1_I1(w, wr) -C_O1_I2(r, 0, rz) C_O1_I2(r, r, r) C_O1_I2(r, r, rA) C_O1_I2(r, r, rAL) @@ -26,6 +25,7 @@ C_O1_I2(r, r, rC) C_O1_I2(r, r, ri) C_O1_I2(r, r, rL) C_O1_I2(r, rz, rz) +C_O1_I2(r, rZ, rZ) C_O1_I2(w, 0, w) C_O1_I2(w, w, w) C_O1_I2(w, w, wN) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 62b045c222..dee4afcce1 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2572,12 +2572,39 @@ static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2, unsigned ofs, unsigned len) { unsigned mask = type == TCG_TYPE_I32 ? 31 : 63; + + /* + * Since we can't support "0Z" as a constraint, we allow a1 in + * any register. Fix things up as if a matching constraint. + */ + if (a0 != a1) { + if (a0 == a2) { + tcg_out_mov(s, type, TCG_REG_TMP0, a2); + a2 = TCG_REG_TMP0; + } + tcg_out_mov(s, type, a0, a1); + } tcg_out_bfm(s, type, a0, a2, -ofs & mask, len - 1); } +static void tgen_depositi(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + tcg_target_long a2, unsigned ofs, unsigned len) +{ + tgen_andi(s, type, a0, a1, ~MAKE_64BIT_MASK(ofs, len)); +} + +static void tgen_depositz(TCGContext *s, TCGType type, TCGReg a0, TCGReg a2, + unsigned ofs, unsigned len) +{ + int max = type == TCG_TYPE_I32 ? 31 : 63; + tcg_out_ubfm(s, type, a0, a2, -ofs & max, len - 1); +} + static const TCGOutOpDeposit outop_deposit = { - .base.static_constraint = C_O1_I2(r, 0, rz), + .base.static_constraint = C_O1_I2(r, rZ, rZ), .out_rrr = tgen_deposit, + .out_rri = tgen_depositi, + .out_rzr = tgen_depositz, }; static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, From 4d137ff819bae33d045f13bb9186e3a2c71cb7e4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Jan 2025 20:48:57 -0800 Subject: [PATCH 0462/2760] tcg: Merge INDEX_op_deposit_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 6 ++++-- include/tcg/tcg-opc.h | 3 +-- tcg/optimize.c | 2 +- tcg/tcg-op.c | 8 ++++---- tcg/tcg.c | 9 +++------ tcg/tci.c | 6 ++---- tcg/tci/tcg-target.c.inc | 2 +- 7 files changed, 16 insertions(+), 20 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index ca7550f68c..aea8a897bd 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -442,7 +442,7 @@ Misc - | Indicate that the value of *t0* won't be used later. It is useful to force dead code elimination. - * - deposit_i32/i64 *dest*, *t1*, *t2*, *pos*, *len* + * - deposit *dest*, *t1*, *t2*, *pos*, *len* - | Deposit *t2* as a bitfield into *t1*, placing the result in *dest*. | @@ -451,10 +451,12 @@ Misc | *len* - the length of the bitfield | *pos* - the position of the first bit, counting from the LSB | - | For example, "deposit_i32 dest, t1, t2, 8, 4" indicates a 4-bit field + | For example, "deposit dest, t1, t2, 8, 4" indicates a 4-bit field at bit 8. This operation would be equivalent to | | *dest* = (*t1* & ~0x0f00) | ((*t2* << 8) & 0x0f00) + | + | on TCG_TYPE_I32. * - extract *dest*, *t1*, *pos*, *len* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 4ace1f85c4..c6848b3c63 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -49,6 +49,7 @@ DEF(bswap64, 1, 1, 1, TCG_OPF_INT) DEF(clz, 1, 2, 0, TCG_OPF_INT) DEF(ctpop, 1, 1, 0, TCG_OPF_INT) DEF(ctz, 1, 2, 0, TCG_OPF_INT) +DEF(deposit, 1, 2, 2, TCG_OPF_INT) DEF(divs, 1, 2, 0, TCG_OPF_INT) DEF(divs2, 2, 3, 0, TCG_OPF_INT) DEF(divu, 1, 2, 0, TCG_OPF_INT) @@ -90,7 +91,6 @@ DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) /* shifts/rotates */ -DEF(deposit_i32, 1, 2, 2, 0) DEF(extract2_i32, 1, 2, 1, 0) DEF(add2_i32, 2, 4, 0, 0) @@ -111,7 +111,6 @@ DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) /* shifts/rotates */ -DEF(deposit_i64, 1, 2, 2, 0) DEF(extract2_i64, 1, 2, 1, 0) /* size changing ops */ diff --git a/tcg/optimize.c b/tcg/optimize.c index d324cbf7fe..acc566ed76 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2858,7 +2858,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_ctpop: done = fold_ctpop(&ctx, op); break; - CASE_OP_32_64(deposit): + case INDEX_op_deposit: done = fold_deposit(&ctx, op); break; case INDEX_op_divs: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index b88f411ece..961a39f446 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -915,7 +915,7 @@ void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, return; } if (TCG_TARGET_deposit_valid(TCG_TYPE_I32, ofs, len)) { - tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, arg1, arg2, ofs, len); + tcg_gen_op5ii_i32(INDEX_op_deposit, ret, arg1, arg2, ofs, len); return; } @@ -961,7 +961,7 @@ void tcg_gen_deposit_z_i32(TCGv_i32 ret, TCGv_i32 arg, tcg_gen_andi_i32(ret, arg, (1u << len) - 1); } else if (TCG_TARGET_deposit_valid(TCG_TYPE_I32, ofs, len)) { TCGv_i32 zero = tcg_constant_i32(0); - tcg_gen_op5ii_i32(INDEX_op_deposit_i32, ret, zero, arg, ofs, len); + tcg_gen_op5ii_i32(INDEX_op_deposit, ret, zero, arg, ofs, len); } else { /* * To help two-operand hosts we prefer to zero-extend first, @@ -2533,7 +2533,7 @@ void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, if (TCG_TARGET_REG_BITS == 64) { if (TCG_TARGET_deposit_valid(TCG_TYPE_I64, ofs, len)) { - tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, arg1, arg2, ofs, len); + tcg_gen_op5ii_i64(INDEX_op_deposit, ret, arg1, arg2, ofs, len); return; } } else { @@ -2594,7 +2594,7 @@ void tcg_gen_deposit_z_i64(TCGv_i64 ret, TCGv_i64 arg, } else if (TCG_TARGET_REG_BITS == 64 && TCG_TARGET_deposit_valid(TCG_TYPE_I64, ofs, len)) { TCGv_i64 zero = tcg_constant_i64(0); - tcg_gen_op5ii_i64(INDEX_op_deposit_i64, ret, zero, arg, ofs, len); + tcg_gen_op5ii_i64(INDEX_op_deposit, ret, zero, arg, ofs, len); } else { if (TCG_TARGET_REG_BITS == 32) { if (ofs >= 32) { diff --git a/tcg/tcg.c b/tcg/tcg.c index b9e9454654..6b49fd4acf 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1133,8 +1133,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_clz, TCGOutOpBinary, outop_clz), OUTOP(INDEX_op_ctpop, TCGOutOpUnary, outop_ctpop), OUTOP(INDEX_op_ctz, TCGOutOpBinary, outop_ctz), - OUTOP(INDEX_op_deposit_i32, TCGOutOpDeposit, outop_deposit), - OUTOP(INDEX_op_deposit_i64, TCGOutOpDeposit, outop_deposit), + OUTOP(INDEX_op_deposit, TCGOutOpDeposit, outop_deposit), OUTOP(INDEX_op_divs, TCGOutOpBinary, outop_divs), OUTOP(INDEX_op_divu, TCGOutOpBinary, outop_divu), OUTOP(INDEX_op_divs2, TCGOutOpDivRem, outop_divs2), @@ -2379,6 +2378,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_add: case INDEX_op_and: case INDEX_op_brcond: + case INDEX_op_deposit: case INDEX_op_extract: case INDEX_op_mov: case INDEX_op_movcond: @@ -2397,7 +2397,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: - case INDEX_op_deposit_i32: return true; case INDEX_op_extract2_i32: @@ -2426,7 +2425,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: case INDEX_op_extrh_i64_i32: - case INDEX_op_deposit_i64: return TCG_TARGET_REG_BITS == 64; case INDEX_op_extract2_i64: @@ -5549,8 +5547,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: + case INDEX_op_deposit: { const TCGOutOpDeposit *out = &outop_deposit; diff --git a/tcg/tci.c b/tcg/tci.c index 595416a192..dc916eb112 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -655,8 +655,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = ror32(regs[r1], regs[r2] & 31); break; - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: + case INDEX_op_deposit: tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); regs[r0] = deposit_tr(regs[r1], pos, len, regs[r2]); break; @@ -1042,8 +1041,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) op_name, str_r(r0), str_r(r1), str_r(r2)); break; - case INDEX_op_deposit_i32: - case INDEX_op_deposit_i64: + case INDEX_op_deposit: tci_args_rrrbb(insn, &r0, &r1, &r2, &pos, &len); info->fprintf_func(info->stream, "%-12s %s, %s, %s, %d, %d", op_name, str_r(r0), str_r(r1), str_r(r2), pos, len); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 566c2fb0d0..ef14e81609 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -622,7 +622,7 @@ static const TCGOutOpBinary outop_ctz = { static void tgen_deposit(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2, unsigned ofs, unsigned len) { - tcg_out_op_rrrbb(s, INDEX_op_deposit_i64, a0, a1, a2, ofs, len); + tcg_out_op_rrrbb(s, INDEX_op_deposit, a0, a1, a2, ofs, len); } static const TCGOutOpDeposit outop_deposit = { From c8f9f70047e17ce70e016bc59fe7857123f3cbd5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Jan 2025 21:30:10 -0800 Subject: [PATCH 0463/2760] tcg: Convert extract2 to TCGOutOpExtract2 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-has.h | 2 -- tcg/aarch64/tcg-target.c.inc | 20 +++++++++-------- tcg/arm/tcg-target-has.h | 1 - tcg/arm/tcg-target.c.inc | 38 ++++++++++++-------------------- tcg/i386/tcg-target-has.h | 2 -- tcg/i386/tcg-target.c.inc | 25 ++++++++++++--------- tcg/loongarch64/tcg-target-has.h | 2 -- tcg/loongarch64/tcg-target.c.inc | 5 +++++ tcg/mips/tcg-target-has.h | 6 ----- tcg/mips/tcg-target.c.inc | 5 +++++ tcg/ppc/tcg-target-has.h | 2 -- tcg/ppc/tcg-target.c.inc | 4 ++++ tcg/riscv/tcg-target-has.h | 2 -- tcg/riscv/tcg-target.c.inc | 5 +++++ tcg/s390x/tcg-target-has.h | 2 -- tcg/s390x/tcg-target.c.inc | 4 ++++ tcg/sparc64/tcg-target-has.h | 2 -- tcg/sparc64/tcg-target.c.inc | 4 ++++ tcg/tcg-has.h | 1 - tcg/tcg-op.c | 12 +++++----- tcg/tcg.c | 24 ++++++++++++++++---- tcg/tci/tcg-target-has.h | 2 -- tcg/tci/tcg-target.c.inc | 4 ++++ 23 files changed, 97 insertions(+), 77 deletions(-) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 82d8cd5965..011a91c263 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -13,13 +13,11 @@ #define have_lse2 (cpuinfo & CPUINFO_LSE2) /* optional instructions */ -#define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index dee4afcce1..bece494c55 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2634,6 +2634,17 @@ static const TCGOutOpExtract outop_sextract = { .out_rr = tgen_sextract, }; +static void tgen_extract2(TCGContext *s, TCGType type, TCGReg a0, + TCGReg a1, TCGReg a2, unsigned shr) +{ + tcg_out_extr(s, type, a0, a2, a1, shr); +} + +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_O1_I2(r, rz, rz), + .out_rrr = tgen_extract2, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2714,11 +2725,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], false); break; - case INDEX_op_extract2_i64: - case INDEX_op_extract2_i32: - tcg_out_extr(s, ext, a0, a2, a1, args[3]); - break; - case INDEX_op_add2_i32: tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, a2, args[3], (int32_t)args[4], args[5], const_args[4], @@ -3231,10 +3237,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i128: return C_O0_I3(rz, rz, r); - case INDEX_op_extract2_i32: - case INDEX_op_extract2_i64: - return C_O1_I2(r, rz, rz); - case INDEX_op_add2_i32: case INDEX_op_add2_i64: case INDEX_op_sub2_i32: diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index c85b5da1e5..0d6a785542 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -24,7 +24,6 @@ extern bool use_neon_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 2bf6bfe274..f366424af5 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2317,6 +2317,20 @@ static const TCGOutOpSetcond2 outop_setcond2 = { .out = tgen_setcond2, }; +static void tgen_extract2(TCGContext *s, TCGType type, TCGReg a0, + TCGReg a1, TCGReg a2, unsigned shr) +{ + /* We can do extract2 in 2 insns, vs the 3 required otherwise. */ + tgen_shli(s, TCG_TYPE_I32, TCG_REG_TMP, a2, 32 - shr); + tcg_out_dat_reg(s, COND_AL, ARITH_ORR, a0, TCG_REG_TMP, + a1, SHIFT_IMM_LSR(shr)); +} + +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_O1_I2(r, r, r), + .out_rrr = tgen_extract2, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2411,28 +2425,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); break; - case INDEX_op_extract2_i32: - /* ??? These optimization vs zero should be generic. */ - /* ??? But we can't substitute 2 for 1 in the opcode stream yet. */ - if (const_args[1]) { - if (const_args[2]) { - tcg_out_movi(s, TCG_TYPE_REG, args[0], 0); - } else { - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, - args[2], SHIFT_IMM_LSL(32 - args[3])); - } - } else if (const_args[2]) { - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, args[0], 0, - args[1], SHIFT_IMM_LSR(args[3])); - } else { - /* We can do extract2 in 2 insns, vs the 3 required otherwise. */ - tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP, 0, - args[2], SHIFT_IMM_LSL(32 - args[3])); - tcg_out_dat_reg(s, COND_AL, ARITH_ORR, args[0], TCG_REG_TMP, - args[1], SHIFT_IMM_LSR(args[3])); - } - break; - case INDEX_op_mb: tcg_out_mb(s, args[0]); break; @@ -2464,8 +2456,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i32: return C_O0_I2(r, r); - case INDEX_op_extract2_i32: - return C_O1_I2(r, rZ, rZ); case INDEX_op_add2_i32: return C_O2_I4(r, r, r, r, rIN, rIK); case INDEX_op_sub2_i32: diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index 6b91b23fe8..0328102c2a 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -26,14 +26,12 @@ #define have_avx512vbmi2 ((cpuinfo & CPUINFO_AVX512VBMI2) && have_avx512vl) /* optional instructions */ -#define TCG_TARGET_HAS_extract2_i32 1 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #if TCG_TARGET_REG_BITS == 64 /* Keep 32-bit values zero-extended in a register. */ #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_extract2_i64 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 1dd9741f45..2b2ad9ca95 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3261,6 +3261,21 @@ static const TCGOutOpExtract outop_sextract = { .out_rr = tgen_sextract, }; +static void tgen_extract2(TCGContext *s, TCGType type, TCGReg a0, + TCGReg a1, TCGReg a2, unsigned shr) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + + /* Note that SHRD outputs to the r/m operand. */ + tcg_out_modrm(s, OPC_SHRD_Ib + rexw, a2, a0); + tcg_out8(s, shr); +} + +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_O1_I2(r, 0, r), + .out_rrr = tgen_extract2, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -3414,12 +3429,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; #endif - OP_32_64(extract2): - /* Note that SHRD outputs to the r/m operand. */ - tcg_out_modrm(s, OPC_SHRD_Ib + rexw, a2, a0); - tcg_out8(s, args[3]); - break; - case INDEX_op_mb: tcg_out_mb(s, a0); break; @@ -4008,10 +4017,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_extract2_i32: - case INDEX_op_extract2_i64: - return C_O1_I2(r, 0, r); - case INDEX_op_add2_i32: case INDEX_op_add2_i64: case INDEX_op_sub2_i32: diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 10090102f7..a1bd71db6a 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -10,13 +10,11 @@ #include "host/cpuinfo.h" /* optional instructions */ -#define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 /* 64-bit operations */ -#define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 0b64efc078..eb2143703d 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1868,6 +1868,11 @@ static const TCGOutOpExtract outop_sextract = { .out_rr = tgen_sextract, }; +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 24b00f1eec..48a1e68fbe 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -51,13 +51,7 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions detected at runtime */ -#define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 - -#if TCG_TARGET_REG_BITS == 64 -#define TCG_TARGET_HAS_extract2_i64 0 -#endif - #define TCG_TARGET_HAS_qemu_ldst_i128 0 #define TCG_TARGET_HAS_tst 0 diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index cd648ab1df..7fae1c51e9 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2273,6 +2273,11 @@ static const TCGOutOpExtract outop_sextract = { .out_rr = tgen_sextract, }; +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index bd9c3d92ed..033d58e095 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -17,14 +17,12 @@ #define have_vsx (cpuinfo & CPUINFO_VSX) /* optional instructions */ -#define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #endif diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index fc92a4896d..a964239aab 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3495,6 +3495,10 @@ static const TCGOutOpExtract outop_sextract = { .out_rr = tgen_sextract, }; +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 88fadc2428..b2814f8ef9 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -10,12 +10,10 @@ #include "host/cpuinfo.h" /* optional instructions */ -#define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 371e0c24c8..d74ac7587a 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2542,6 +2542,11 @@ static const TCGOutOpExtract outop_sextract = { .out_rr = tgen_sextract, }; +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 95407f61cf..4a2b71995d 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -29,13 +29,11 @@ extern uint64_t s390_facilities[3]; ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) /* optional instructions */ -#define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index d72393315d..ff06834e6e 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1637,6 +1637,10 @@ static const TCGOutOpExtract outop_sextract = { .out_rr = tgen_sextract, }; +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tgen_gotoi(TCGContext *s, int cc, const tcg_insn_unit *dest) { ptrdiff_t off = tcg_pcrel_diff(s, dest) >> 1; diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index 2ced6f7c1c..b8760dd154 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -14,13 +14,11 @@ extern bool use_vis3_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 741de260e9..4c7d916302 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1795,6 +1795,10 @@ static const TCGOutOpExtract outop_sextract = { .out_rr = tgen_sextract, }; +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 21bef070fe..6125ac677c 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -12,7 +12,6 @@ #if TCG_TARGET_REG_BITS == 32 /* Turn some undef macros into false macros. */ #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 /* Turn some undef macros into true macros. */ diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 961a39f446..5f95350d5d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -921,7 +921,7 @@ void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, t1 = tcg_temp_ebb_new_i32(); - if (TCG_TARGET_HAS_extract2_i32) { + if (tcg_op_supported(INDEX_op_extract2_i32, TCG_TYPE_I32, 0)) { if (ofs + len == 32) { tcg_gen_shli_i32(t1, arg1, len); tcg_gen_extract2_i32(ret, t1, arg2, len); @@ -1077,7 +1077,7 @@ void tcg_gen_extract2_i32(TCGv_i32 ret, TCGv_i32 al, TCGv_i32 ah, tcg_gen_mov_i32(ret, ah); } else if (al == ah) { tcg_gen_rotri_i32(ret, al, ofs); - } else if (TCG_TARGET_HAS_extract2_i32) { + } else if (tcg_op_supported(INDEX_op_extract2_i32, TCG_TYPE_I32, 0)) { tcg_gen_op4i_i32(INDEX_op_extract2_i32, ret, al, ah, ofs); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); @@ -1799,7 +1799,7 @@ static inline void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, tcg_gen_movi_i32(TCGV_LOW(ret), 0); } } else if (right) { - if (TCG_TARGET_HAS_extract2_i32) { + if (tcg_op_supported(INDEX_op_extract2_i32, TCG_TYPE_I32, 0)) { tcg_gen_extract2_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_HIGH(arg1), c); } else { @@ -1813,7 +1813,7 @@ static inline void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, tcg_gen_shri_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), c); } } else { - if (TCG_TARGET_HAS_extract2_i32) { + if (tcg_op_supported(INDEX_op_extract2_i32, TCG_TYPE_I32, 0)) { tcg_gen_extract2_i32(TCGV_HIGH(ret), TCGV_LOW(arg1), TCGV_HIGH(arg1), 32 - c); } else { @@ -2553,7 +2553,7 @@ void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, t1 = tcg_temp_ebb_new_i64(); - if (TCG_TARGET_HAS_extract2_i64) { + if (tcg_op_supported(INDEX_op_extract2_i64, TCG_TYPE_I64, 0)) { if (ofs + len == 64) { tcg_gen_shli_i64(t1, arg1, len); tcg_gen_extract2_i64(ret, t1, arg2, len); @@ -2781,7 +2781,7 @@ void tcg_gen_extract2_i64(TCGv_i64 ret, TCGv_i64 al, TCGv_i64 ah, tcg_gen_mov_i64(ret, ah); } else if (al == ah) { tcg_gen_rotri_i64(ret, al, ofs); - } else if (TCG_TARGET_HAS_extract2_i64) { + } else if (tcg_op_supported(INDEX_op_extract2_i64, TCG_TYPE_I64, 0)) { tcg_gen_op4i_i64(INDEX_op_extract2_i64, ret, al, ah, ofs); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index 6b49fd4acf..3fbbe3bd83 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1029,6 +1029,12 @@ typedef struct TCGOutOpExtract { unsigned ofs, unsigned len); } TCGOutOpExtract; +typedef struct TCGOutOpExtract2 { + TCGOutOp base; + void (*out_rrr)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, + TCGReg a2, unsigned shr); +} TCGOutOpExtract2; + typedef struct TCGOutOpMovcond { TCGOutOp base; void (*out)(TCGContext *s, TCGType type, TCGCond cond, @@ -1140,6 +1146,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_divu2, TCGOutOpDivRem, outop_divu2), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_extract, TCGOutOpExtract, outop_extract), + OUTOP(INDEX_op_extract2_i32, TCGOutOpExtract2, outop_extract2), + OUTOP(INDEX_op_extract2_i64, TCGOutOpExtract2, outop_extract2), OUTOP(INDEX_op_movcond, TCGOutOpMovcond, outop_movcond), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_muls2, TCGOutOpMul2, outop_muls2), @@ -2399,8 +2407,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i32: return true; - case INDEX_op_extract2_i32: - return TCG_TARGET_HAS_extract2_i32; case INDEX_op_add2_i32: return TCG_TARGET_HAS_add2_i32; case INDEX_op_sub2_i32: @@ -2427,8 +2433,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrh_i64_i32: return TCG_TARGET_REG_BITS == 64; - case INDEX_op_extract2_i64: - return TCG_TARGET_HAS_extract2_i64; case INDEX_op_add2_i64: return TCG_TARGET_HAS_add2_i64; case INDEX_op_sub2_i64: @@ -5593,6 +5597,18 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_extract2_i32: + case INDEX_op_extract2_i64: + { + const TCGOutOpExtract2 *out = &outop_extract2; + + tcg_debug_assert(!const_args[1]); + tcg_debug_assert(!const_args[2]); + out->out_rrr(s, type, new_args[0], new_args[1], + new_args[2], new_args[3]); + } + break; + case INDEX_op_muls2: case INDEX_op_mulu2: { diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 90aa5c8bbb..4cb2b529ae 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -7,12 +7,10 @@ #ifndef TCG_TARGET_HAS_H #define TCG_TARGET_HAS_H -#define TCG_TARGET_HAS_extract2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_extract2_i64 0 #define TCG_TARGET_HAS_add2_i32 1 #define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_add2_i64 1 diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index ef14e81609..9a5ca9c778 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -447,6 +447,10 @@ static const TCGOutOpExtract outop_sextract = { .out_rr = tcg_out_sextract, }; +static const TCGOutOpExtract2 outop_extract2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tcg_out_ext8s(TCGContext *s, TCGType type, TCGReg rd, TCGReg rs) { tcg_out_sextract(s, type, rd, rs, 0, 8); From 61d6a8767a5d4cd4fe5086ef98b53614ae099104 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 12 Jan 2025 21:40:43 -0800 Subject: [PATCH 0464/2760] tcg: Merge INDEX_op_extract2_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 4 ++-- include/tcg/tcg-opc.h | 5 +---- target/i386/tcg/emit.c.inc | 12 +----------- tcg/optimize.c | 10 +++++----- tcg/tcg-op.c | 16 ++++++++-------- tcg/tcg.c | 6 ++---- 6 files changed, 19 insertions(+), 34 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index aea8a897bd..9392d88069 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -476,9 +476,9 @@ Misc | | (using an arithmetic right shift) on TCG_TYPE_I32. - * - extract2_i32/i64 *dest*, *t1*, *t2*, *pos* + * - extract2 *dest*, *t1*, *t2*, *pos* - - | For N = {32,64}, extract an N-bit quantity from the concatenation + - | For TCG_TYPE_I{N}, extract an N-bit quantity from the concatenation of *t2*:*t1*, beginning at *pos*. The tcg_gen_extract2_{i32,i64} expander accepts 0 <= *pos* <= N as inputs. The backend code generator will not see either 0 or N as inputs for these opcodes. diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index c6848b3c63..1f995c54be 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -56,6 +56,7 @@ DEF(divu, 1, 2, 0, TCG_OPF_INT) DEF(divu2, 2, 3, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(extract, 1, 1, 2, TCG_OPF_INT) +DEF(extract2, 1, 2, 1, TCG_OPF_INT) DEF(movcond, 1, 4, 1, TCG_OPF_INT) DEF(mul, 1, 2, 0, TCG_OPF_INT) DEF(muls2, 2, 2, 0, TCG_OPF_INT) @@ -90,8 +91,6 @@ DEF(ld_i32, 1, 1, 1, 0) DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) -/* shifts/rotates */ -DEF(extract2_i32, 1, 2, 1, 0) DEF(add2_i32, 2, 4, 0, 0) DEF(sub2_i32, 2, 4, 0, 0) @@ -110,8 +109,6 @@ DEF(st8_i64, 0, 2, 1, 0) DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) DEF(st_i64, 0, 2, 1, 0) -/* shifts/rotates */ -DEF(extract2_i64, 1, 2, 1, 0) /* size changing ops */ DEF(ext_i32_i64, 1, 1, 0, 0) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index ca6bc2ea82..e3166e70a5 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -19,16 +19,6 @@ * License along with this library; if not, see . */ -/* - * Sometimes, knowing what the backend has can produce better code. - * The exact opcode to check depends on 32- vs. 64-bit. - */ -#ifdef TARGET_X86_64 -#define INDEX_op_extract2_tl INDEX_op_extract2_i64 -#else -#define INDEX_op_extract2_tl INDEX_op_extract2_i32 -#endif - #define MMX_OFFSET(reg) \ ({ assert((reg) >= 0 && (reg) <= 7); \ offsetof(CPUX86State, fpregs[reg].mmx); }) @@ -3023,7 +3013,7 @@ static void gen_PMOVMSKB(DisasContext *s, X86DecodedInsn *decode) tcg_gen_ld8u_tl(s->T0, tcg_env, offsetof(CPUX86State, xmm_t0.ZMM_B(vec_len - 1))); while (vec_len > 8) { vec_len -= 8; - if (tcg_op_supported(INDEX_op_extract2_tl, TCG_TYPE_TL, 0)) { + if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_TL, 0)) { /* * Load the next byte of the result into the high byte of T. * TCG does a similar expansion of deposit to shl+extract2; by diff --git a/tcg/optimize.c b/tcg/optimize.c index acc566ed76..a728a4b2fa 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1873,12 +1873,12 @@ static bool fold_extract2(OptContext *ctx, TCGOp *op) uint64_t v2 = arg_info(op->args[2])->val; int shr = op->args[3]; - if (op->opc == INDEX_op_extract2_i64) { - v1 >>= shr; - v2 <<= 64 - shr; - } else { + if (ctx->type == TCG_TYPE_I32) { v1 = (uint32_t)v1 >> shr; v2 = (uint64_t)((int32_t)v2 << (32 - shr)); + } else { + v1 >>= shr; + v2 <<= 64 - shr; } return tcg_opt_gen_movi(ctx, op, op->args[0], v1 | v2); } @@ -2878,7 +2878,7 @@ void tcg_optimize(TCGContext *s) case INDEX_op_extract: done = fold_extract(&ctx, op); break; - CASE_OP_32_64(extract2): + case INDEX_op_extract2: done = fold_extract2(&ctx, op); break; case INDEX_op_ext_i32_i64: diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 5f95350d5d..edbb214f7c 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -921,7 +921,7 @@ void tcg_gen_deposit_i32(TCGv_i32 ret, TCGv_i32 arg1, TCGv_i32 arg2, t1 = tcg_temp_ebb_new_i32(); - if (tcg_op_supported(INDEX_op_extract2_i32, TCG_TYPE_I32, 0)) { + if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_I32, 0)) { if (ofs + len == 32) { tcg_gen_shli_i32(t1, arg1, len); tcg_gen_extract2_i32(ret, t1, arg2, len); @@ -1077,8 +1077,8 @@ void tcg_gen_extract2_i32(TCGv_i32 ret, TCGv_i32 al, TCGv_i32 ah, tcg_gen_mov_i32(ret, ah); } else if (al == ah) { tcg_gen_rotri_i32(ret, al, ofs); - } else if (tcg_op_supported(INDEX_op_extract2_i32, TCG_TYPE_I32, 0)) { - tcg_gen_op4i_i32(INDEX_op_extract2_i32, ret, al, ah, ofs); + } else if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_I32, 0)) { + tcg_gen_op4i_i32(INDEX_op_extract2, ret, al, ah, ofs); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); tcg_gen_shri_i32(t0, al, ofs); @@ -1799,7 +1799,7 @@ static inline void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, tcg_gen_movi_i32(TCGV_LOW(ret), 0); } } else if (right) { - if (tcg_op_supported(INDEX_op_extract2_i32, TCG_TYPE_I32, 0)) { + if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_I32, 0)) { tcg_gen_extract2_i32(TCGV_LOW(ret), TCGV_LOW(arg1), TCGV_HIGH(arg1), c); } else { @@ -1813,7 +1813,7 @@ static inline void tcg_gen_shifti_i64(TCGv_i64 ret, TCGv_i64 arg1, tcg_gen_shri_i32(TCGV_HIGH(ret), TCGV_HIGH(arg1), c); } } else { - if (tcg_op_supported(INDEX_op_extract2_i32, TCG_TYPE_I32, 0)) { + if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_I32, 0)) { tcg_gen_extract2_i32(TCGV_HIGH(ret), TCGV_LOW(arg1), TCGV_HIGH(arg1), 32 - c); } else { @@ -2553,7 +2553,7 @@ void tcg_gen_deposit_i64(TCGv_i64 ret, TCGv_i64 arg1, TCGv_i64 arg2, t1 = tcg_temp_ebb_new_i64(); - if (tcg_op_supported(INDEX_op_extract2_i64, TCG_TYPE_I64, 0)) { + if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_I64, 0)) { if (ofs + len == 64) { tcg_gen_shli_i64(t1, arg1, len); tcg_gen_extract2_i64(ret, t1, arg2, len); @@ -2781,8 +2781,8 @@ void tcg_gen_extract2_i64(TCGv_i64 ret, TCGv_i64 al, TCGv_i64 ah, tcg_gen_mov_i64(ret, ah); } else if (al == ah) { tcg_gen_rotri_i64(ret, al, ofs); - } else if (tcg_op_supported(INDEX_op_extract2_i64, TCG_TYPE_I64, 0)) { - tcg_gen_op4i_i64(INDEX_op_extract2_i64, ret, al, ah, ofs); + } else if (tcg_op_supported(INDEX_op_extract2, TCG_TYPE_I64, 0)) { + tcg_gen_op4i_i64(INDEX_op_extract2, ret, al, ah, ofs); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); tcg_gen_shri_i64(t0, al, ofs); diff --git a/tcg/tcg.c b/tcg/tcg.c index 3fbbe3bd83..037b5a4664 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1146,8 +1146,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_divu2, TCGOutOpDivRem, outop_divu2), OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_extract, TCGOutOpExtract, outop_extract), - OUTOP(INDEX_op_extract2_i32, TCGOutOpExtract2, outop_extract2), - OUTOP(INDEX_op_extract2_i64, TCGOutOpExtract2, outop_extract2), + OUTOP(INDEX_op_extract2, TCGOutOpExtract2, outop_extract2), OUTOP(INDEX_op_movcond, TCGOutOpMovcond, outop_movcond), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_muls2, TCGOutOpMul2, outop_muls2), @@ -5597,8 +5596,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_extract2_i32: - case INDEX_op_extract2_i64: + case INDEX_op_extract2: { const TCGOutOpExtract2 *out = &outop_extract2; From 3ff7e44ef35924f76dbe36510c54c27e72af9121 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 13 Jan 2025 20:14:09 -0800 Subject: [PATCH 0465/2760] tcg: Expand fallback add2 with 32-bit operations No need to expand to i64 to perform the add. This is smaller on a loongarch64 host, e.g. bstrpick_d r28, r27, 31, 0 bstrpick_d r29, r24, 31, 0 add_d r28, r28, r29 addi_w r29, r28, 0 srai_d r28, r28, 32 --- add_w r28, r27, r24 sltu r29, r28, r24 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/tcg-op.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index edbb214f7c..8b1356c526 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1105,14 +1105,15 @@ void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, if (TCG_TARGET_HAS_add2_i32) { tcg_gen_op6_i32(INDEX_op_add2_i32, rl, rh, al, ah, bl, bh); } else { - TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - TCGv_i64 t1 = tcg_temp_ebb_new_i64(); - tcg_gen_concat_i32_i64(t0, al, ah); - tcg_gen_concat_i32_i64(t1, bl, bh); - tcg_gen_add_i64(t0, t0, t1); - tcg_gen_extr_i64_i32(rl, rh, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + tcg_gen_add_i32(t0, al, bl); + tcg_gen_setcond_i32(TCG_COND_LTU, t1, t0, al); + tcg_gen_add_i32(rh, ah, bh); + tcg_gen_add_i32(rh, rh, t1); + tcg_gen_mov_i32(rl, t0); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); } } From 3db22914ced9bb3683bffa03729608be0c42f666 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 13 Jan 2025 20:21:09 -0800 Subject: [PATCH 0466/2760] tcg: Expand fallback sub2 with 32-bit operations No need to expand to i64 to perform the subtract. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/tcg-op.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 8b1356c526..127338b994 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1123,14 +1123,15 @@ void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, if (TCG_TARGET_HAS_sub2_i32) { tcg_gen_op6_i32(INDEX_op_sub2_i32, rl, rh, al, ah, bl, bh); } else { - TCGv_i64 t0 = tcg_temp_ebb_new_i64(); - TCGv_i64 t1 = tcg_temp_ebb_new_i64(); - tcg_gen_concat_i32_i64(t0, al, ah); - tcg_gen_concat_i32_i64(t1, bl, bh); - tcg_gen_sub_i64(t0, t0, t1); - tcg_gen_extr_i64_i32(rl, rh, t0); - tcg_temp_free_i64(t0); - tcg_temp_free_i64(t1); + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + tcg_gen_sub_i32(t0, al, bl); + tcg_gen_setcond_i32(TCG_COND_LTU, t1, al, bl); + tcg_gen_sub_i32(rh, ah, bh); + tcg_gen_sub_i32(rh, rh, t1); + tcg_gen_mov_i32(rl, t0); + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); } } From 8dc1bdf79553594287b6c3f9185fe028307aedcb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 13 Jan 2025 21:13:54 -0800 Subject: [PATCH 0467/2760] tcg: Do not default add2/sub2_i32 for 32-bit hosts Require TCG_TARGET_HAS_{add2,sub2}_i32 be defined, one way or another. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/arm/tcg-target-has.h | 2 ++ tcg/mips/tcg-target-has.h | 3 +++ tcg/ppc/tcg-target-has.h | 3 +++ tcg/tcg-has.h | 3 --- tcg/tci/tcg-target-has.h | 4 ++-- 5 files changed, 10 insertions(+), 5 deletions(-) diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 0d6a785542..3973df1f12 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -24,6 +24,8 @@ extern bool use_neon_instructions; #endif /* optional instructions */ +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 48a1e68fbe..9f6fa194b9 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -48,6 +48,9 @@ extern bool use_mips32r2_instructions; #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_ext32s_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 +#else +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 #endif /* optional instructions detected at runtime */ diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 033d58e095..8d832ce99c 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -25,6 +25,9 @@ #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 +#else +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 #endif #define TCG_TARGET_HAS_qemu_ldst_i128 \ diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 6125ac677c..50e8d0cda4 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -14,9 +14,6 @@ #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 -/* Turn some undef macros into true macros. */ -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 #endif #if !defined(TCG_TARGET_HAS_v64) \ diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 4cb2b529ae..6063f32f7b 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -8,11 +8,11 @@ #define TCG_TARGET_HAS_H #define TCG_TARGET_HAS_qemu_st8_i32 0 +#define TCG_TARGET_HAS_add2_i32 1 +#define TCG_TARGET_HAS_sub2_i32 1 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_add2_i64 1 #define TCG_TARGET_HAS_sub2_i64 1 #endif /* TCG_TARGET_REG_BITS == 64 */ From 52c49c79b820ceeff10c7c414f747f9bbb39f6c9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 13 Jan 2025 21:16:40 -0800 Subject: [PATCH 0468/2760] tcg/mips: Drop support for add2/sub2 We now produce exactly the same code via generic expansion. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/mips/tcg-target-con-set.h | 1 - tcg/mips/tcg-target-con-str.h | 1 - tcg/mips/tcg-target-has.h | 7 ++-- tcg/mips/tcg-target.c.inc | 67 +---------------------------------- 4 files changed, 3 insertions(+), 73 deletions(-) diff --git a/tcg/mips/tcg-target-con-set.h b/tcg/mips/tcg-target-con-set.h index 4e09c9a400..5304691dc1 100644 --- a/tcg/mips/tcg-target-con-set.h +++ b/tcg/mips/tcg-target-con-set.h @@ -28,4 +28,3 @@ C_O1_I4(r, r, rz, rz, rz) C_O1_I4(r, r, r, rz, rz) C_O2_I1(r, r, r) C_O2_I2(r, r, r, r) -C_O2_I4(r, r, rz, rz, rN, rN) diff --git a/tcg/mips/tcg-target-con-str.h b/tcg/mips/tcg-target-con-str.h index dfe2b156df..db2b225e4a 100644 --- a/tcg/mips/tcg-target-con-str.h +++ b/tcg/mips/tcg-target-con-str.h @@ -17,5 +17,4 @@ REGS('r', ALL_GENERAL_REGS) CONST('I', TCG_CT_CONST_U16) CONST('J', TCG_CT_CONST_S16) CONST('K', TCG_CT_CONST_P2M1) -CONST('N', TCG_CT_CONST_N16) CONST('W', TCG_CT_CONST_WSZ) diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 9f6fa194b9..9d86906bf3 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -39,18 +39,15 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions */ - -#if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 + +#if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_add2_i64 0 #define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_ext32s_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 -#else -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 #endif /* optional instructions detected at runtime */ diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 7fae1c51e9..e69781b871 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -187,8 +187,7 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, #define TCG_CT_CONST_U16 0x100 /* Unsigned 16-bit: 0 - 0xffff. */ #define TCG_CT_CONST_S16 0x200 /* Signed 16-bit: -32768 - 32767 */ #define TCG_CT_CONST_P2M1 0x400 /* Power of 2 minus 1. */ -#define TCG_CT_CONST_N16 0x800 /* "Negatable" 16-bit: -32767 - 32767 */ -#define TCG_CT_CONST_WSZ 0x1000 /* word size */ +#define TCG_CT_CONST_WSZ 0x800 /* word size */ #define ALL_GENERAL_REGS 0xffffffffu @@ -207,8 +206,6 @@ static bool tcg_target_const_match(int64_t val, int ct, return 1; } else if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { return 1; - } else if ((ct & TCG_CT_CONST_N16) && val >= -32767 && val <= 32767) { - return 1; } else if ((ct & TCG_CT_CONST_P2M1) && use_mips32r2_instructions && is_p2m1(val)) { return 1; @@ -765,55 +762,6 @@ static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, return false; } -static void tcg_out_addsub2(TCGContext *s, TCGReg rl, TCGReg rh, TCGReg al, - TCGReg ah, TCGArg bl, TCGArg bh, bool cbl, - bool cbh, bool is_sub) -{ - TCGReg th = TCG_TMP1; - - /* If we have a negative constant such that negating it would - make the high part zero, we can (usually) eliminate one insn. */ - if (cbl && cbh && bh == -1 && bl != 0) { - bl = -bl; - bh = 0; - is_sub = !is_sub; - } - - /* By operating on the high part first, we get to use the final - carry operation to move back from the temporary. */ - if (!cbh) { - tcg_out_opc_reg(s, (is_sub ? OPC_SUBU : OPC_ADDU), th, ah, bh); - } else if (bh != 0 || ah == rl) { - tcg_out_opc_imm(s, OPC_ADDIU, th, ah, (is_sub ? -bh : bh)); - } else { - th = ah; - } - - /* Note that tcg optimization should eliminate the bl == 0 case. */ - if (is_sub) { - if (cbl) { - tcg_out_opc_imm(s, OPC_SLTIU, TCG_TMP0, al, bl); - tcg_out_opc_imm(s, OPC_ADDIU, rl, al, -bl); - } else { - tcg_out_opc_reg(s, OPC_SLTU, TCG_TMP0, al, bl); - tcg_out_opc_reg(s, OPC_SUBU, rl, al, bl); - } - tcg_out_opc_reg(s, OPC_SUBU, rh, th, TCG_TMP0); - } else { - if (cbl) { - tcg_out_opc_imm(s, OPC_ADDIU, rl, al, bl); - tcg_out_opc_imm(s, OPC_SLTIU, TCG_TMP0, rl, bl); - } else if (rl == al && rl == bl) { - tcg_out_opc_sa(s, OPC_SRL, TCG_TMP0, al, TCG_TARGET_REG_BITS - 1); - tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl); - } else { - tcg_out_opc_reg(s, OPC_ADDU, rl, al, bl); - tcg_out_opc_reg(s, OPC_SLTU, TCG_TMP0, rl, (rl == bl ? al : bl)); - } - tcg_out_opc_reg(s, OPC_ADDU, rh, th, TCG_TMP0); - } -} - #define SETCOND_INV TCG_TARGET_NB_REGS #define SETCOND_NEZ (SETCOND_INV << 1) #define SETCOND_FLAGS (SETCOND_INV | SETCOND_NEZ) @@ -2370,15 +2318,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_add2_i32: - tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], - const_args[4], const_args[5], false); - break; - case INDEX_op_sub2_i32: - tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], - const_args[4], const_args[5], true); - break; - case INDEX_op_mb: tcg_out_mb(s, a0); break; @@ -2420,10 +2359,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_add2_i32: - case INDEX_op_sub2_i32: - return C_O2_I4(r, r, rz, rz, rN, rN); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); case INDEX_op_qemu_st_i32: From 0e08be0f5482af78f7ed703f665287964d1650f5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 13 Jan 2025 21:24:25 -0800 Subject: [PATCH 0469/2760] tcg/riscv: Drop support for add2/sub2 We now produce exactly the same code via generic expansion. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/riscv/tcg-target-con-set.h | 1 - tcg/riscv/tcg-target-has.h | 6 +-- tcg/riscv/tcg-target.c.inc | 86 +--------------------------------- 3 files changed, 3 insertions(+), 90 deletions(-) diff --git a/tcg/riscv/tcg-target-con-set.h b/tcg/riscv/tcg-target-con-set.h index 5ff2c2db60..0fc26d3f98 100644 --- a/tcg/riscv/tcg-target-con-set.h +++ b/tcg/riscv/tcg-target-con-set.h @@ -18,7 +18,6 @@ C_O1_I2(r, r, ri) C_O1_I2(r, r, rI) C_N1_I2(r, r, rM) C_O1_I4(r, r, rI, rM, rM) -C_O2_I4(r, r, rz, rz, rM, rM) C_O0_I2(v, r) C_O1_I1(v, r) C_O1_I1(v, v) diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index b2814f8ef9..c95dc1921e 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -10,13 +10,11 @@ #include "host/cpuinfo.h" /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index d74ac7587a..dce46dcba6 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -401,7 +401,7 @@ static bool tcg_target_const_match(int64_t val, int ct, } /* * Sign extended from 12 bits, +/- matching: [-0x7ff, 0x7ff]. - * Used by addsub2 and movcond, which may need the negative value, + * Used by movcond, which may need the negative value, * and requires the modified constant to be representable. */ if ((ct & TCG_CT_CONST_M12) && val >= -0x7ff && val <= 0x7ff) { @@ -1073,67 +1073,6 @@ static bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, return false; } -static void tcg_out_addsub2(TCGContext *s, - TCGReg rl, TCGReg rh, - TCGReg al, TCGReg ah, - TCGArg bl, TCGArg bh, - bool cbl, bool cbh, bool is_sub, bool is32bit) -{ - const RISCVInsn opc_add = is32bit ? OPC_ADDW : OPC_ADD; - const RISCVInsn opc_addi = is32bit ? OPC_ADDIW : OPC_ADDI; - const RISCVInsn opc_sub = is32bit ? OPC_SUBW : OPC_SUB; - TCGReg th = TCG_REG_TMP1; - - /* If we have a negative constant such that negating it would - make the high part zero, we can (usually) eliminate one insn. */ - if (cbl && cbh && bh == -1 && bl != 0) { - bl = -bl; - bh = 0; - is_sub = !is_sub; - } - - /* By operating on the high part first, we get to use the final - carry operation to move back from the temporary. */ - if (!cbh) { - tcg_out_opc_reg(s, (is_sub ? opc_sub : opc_add), th, ah, bh); - } else if (bh != 0 || ah == rl) { - tcg_out_opc_imm(s, opc_addi, th, ah, (is_sub ? -bh : bh)); - } else { - th = ah; - } - - /* Note that tcg optimization should eliminate the bl == 0 case. */ - if (is_sub) { - if (cbl) { - tcg_out_opc_imm(s, OPC_SLTIU, TCG_REG_TMP0, al, bl); - tcg_out_opc_imm(s, opc_addi, rl, al, -bl); - } else { - tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_TMP0, al, bl); - tcg_out_opc_reg(s, opc_sub, rl, al, bl); - } - tcg_out_opc_reg(s, opc_sub, rh, th, TCG_REG_TMP0); - } else { - if (cbl) { - tcg_out_opc_imm(s, opc_addi, rl, al, bl); - tcg_out_opc_imm(s, OPC_SLTIU, TCG_REG_TMP0, rl, bl); - } else if (al == bl) { - /* - * If the input regs overlap, this is a simple doubling - * and carry-out is the input msb. This special case is - * required when the output reg overlaps the input, - * but we might as well use it always. - */ - tcg_out_opc_imm(s, OPC_SLTI, TCG_REG_TMP0, al, 0); - tcg_out_opc_reg(s, opc_add, rl, al, al); - } else { - tcg_out_opc_reg(s, opc_add, rl, al, bl); - tcg_out_opc_reg(s, OPC_SLTU, TCG_REG_TMP0, - rl, (rl == bl ? al : bl)); - } - tcg_out_opc_reg(s, opc_add, rh, th, TCG_REG_TMP0); - } -} - static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg dst, TCGReg src) { @@ -2608,23 +2547,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, OPC_SD, a0, a1, a2); break; - case INDEX_op_add2_i32: - tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], - const_args[4], const_args[5], false, true); - break; - case INDEX_op_add2_i64: - tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], - const_args[4], const_args[5], false, false); - break; - case INDEX_op_sub2_i32: - tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], - const_args[4], const_args[5], true, true); - break; - case INDEX_op_sub2_i64: - tcg_out_addsub2(s, a0, a1, a2, args[3], args[4], args[5], - const_args[4], const_args[5], true, false); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); break; @@ -2897,12 +2819,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(rz, r); - case INDEX_op_add2_i32: - case INDEX_op_add2_i64: - case INDEX_op_sub2_i32: - case INDEX_op_sub2_i64: - return C_O2_I4(r, r, rz, rz, rM, rM); - case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); From 7d10d8e076201e968d57f1c2f5ffd8c88ae1eec9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Jan 2025 13:04:15 -0800 Subject: [PATCH 0470/2760] tcg: Move i into each for loop in liveness_pass_1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use per-loop variables instead of one 'i' for the function. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/tcg.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tcg/tcg.c b/tcg/tcg.c index 037b5a4664..e2ca02eddf 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -3924,10 +3924,9 @@ liveness_pass_1(TCGContext *s) int nb_temps = s->nb_temps; TCGOp *op, *op_prev; TCGRegSet *prefs; - int i; prefs = tcg_malloc(sizeof(TCGRegSet) * nb_temps); - for (i = 0; i < nb_temps; ++i) { + for (int i = 0; i < nb_temps; ++i) { s->temps[i].state_ptr = prefs + i; } @@ -3954,7 +3953,7 @@ liveness_pass_1(TCGContext *s) /* pure functions can be removed if their result is unused */ if (call_flags & TCG_CALL_NO_SIDE_EFFECTS) { - for (i = 0; i < nb_oargs; i++) { + for (int i = 0; i < nb_oargs; i++) { ts = arg_temp(op->args[i]); if (ts->state != TS_DEAD) { goto do_not_remove_call; @@ -3965,7 +3964,7 @@ liveness_pass_1(TCGContext *s) do_not_remove_call: /* Output args are dead. */ - for (i = 0; i < nb_oargs; i++) { + for (int i = 0; i < nb_oargs; i++) { ts = arg_temp(op->args[i]); if (ts->state & TS_DEAD) { arg_life |= DEAD_ARG << i; @@ -3988,7 +3987,7 @@ liveness_pass_1(TCGContext *s) } /* Record arguments that die in this helper. */ - for (i = nb_oargs; i < nb_iargs + nb_oargs; i++) { + for (int i = nb_oargs; i < nb_iargs + nb_oargs; i++) { ts = arg_temp(op->args[i]); if (ts->state & TS_DEAD) { arg_life |= DEAD_ARG << i; @@ -4008,7 +4007,7 @@ liveness_pass_1(TCGContext *s) * order so that if a temp is used more than once, the stack * reset to max happens before the register reset to 0. */ - for (i = nb_iargs - 1; i >= 0; i--) { + for (int i = nb_iargs - 1; i >= 0; i--) { const TCGCallArgumentLoc *loc = &info->in[i]; ts = arg_temp(op->args[nb_oargs + i]); @@ -4036,7 +4035,7 @@ liveness_pass_1(TCGContext *s) * If a temp is used once, this produces a single set bit; * if a temp is used multiple times, this produces a set. */ - for (i = 0; i < nb_iargs; i++) { + for (int i = 0; i < nb_iargs; i++) { const TCGCallArgumentLoc *loc = &info->in[i]; ts = arg_temp(op->args[nb_oargs + i]); @@ -4135,7 +4134,7 @@ liveness_pass_1(TCGContext *s) its outputs are dead. We assume that nb_oargs == 0 implies side effects */ if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && nb_oargs != 0) { - for (i = 0; i < nb_oargs; i++) { + for (int i = 0; i < nb_oargs; i++) { if (arg_temp(op->args[i])->state != TS_DEAD) { goto do_not_remove; } @@ -4149,7 +4148,7 @@ liveness_pass_1(TCGContext *s) break; do_not_remove: - for (i = 0; i < nb_oargs; i++) { + for (int i = 0; i < nb_oargs; i++) { ts = arg_temp(op->args[i]); /* Remember the preference of the uses that followed. */ @@ -4183,7 +4182,7 @@ liveness_pass_1(TCGContext *s) } /* Record arguments that die in this opcode. */ - for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + for (int i = nb_oargs; i < nb_oargs + nb_iargs; i++) { ts = arg_temp(op->args[i]); if (ts->state & TS_DEAD) { arg_life |= DEAD_ARG << i; @@ -4191,7 +4190,7 @@ liveness_pass_1(TCGContext *s) } /* Input arguments are live for preceding opcodes. */ - for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + for (int i = nb_oargs; i < nb_oargs + nb_iargs; i++) { ts = arg_temp(op->args[i]); if (ts->state & TS_DEAD) { /* For operands that were dead, initially allow @@ -4215,7 +4214,7 @@ liveness_pass_1(TCGContext *s) default: args_ct = opcode_args_ct(op); - for (i = nb_oargs; i < nb_oargs + nb_iargs; i++) { + for (int i = nb_oargs; i < nb_oargs + nb_iargs; i++) { const TCGArgConstraint *ct = &args_ct[i]; TCGRegSet set, *pset; From 3e3689df4e05eb76b64a9e45247d87f9dad03177 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Jan 2025 13:12:35 -0800 Subject: [PATCH 0471/2760] tcg: Sink def, nb_iargs, nb_oargs loads in liveness_pass_1 Sink the sets of the def, nb_iargs, nb_oargs variables to the default and do_not_remove labels. They're not really needed beforehand, and it avoids preceding code from having to keep them up-to-date. Note that def had *not* been kept up-to-date; thankfully only def->flags had been used and those bits were constant between opcode changes. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/tcg.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/tcg/tcg.c b/tcg/tcg.c index e2ca02eddf..2849bba480 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -4071,8 +4071,6 @@ liveness_pass_1(TCGContext *s) case INDEX_op_sub2_i64: opc_new = INDEX_op_sub; do_addsub2: - nb_iargs = 4; - nb_oargs = 2; /* Test if the high part of the operation is dead, but not the low part. The result can be optimized to a simple add or sub. This happens often for x86_64 guest when the @@ -4087,8 +4085,6 @@ liveness_pass_1(TCGContext *s) op->args[1] = op->args[2]; op->args[2] = op->args[4]; /* Fall through and mark the single-word operation live. */ - nb_iargs = 2; - nb_oargs = 1; } goto do_not_remove; @@ -4100,8 +4096,6 @@ liveness_pass_1(TCGContext *s) opc_new = INDEX_op_mul; opc_new2 = INDEX_op_muluh; do_mul2: - nb_iargs = 2; - nb_oargs = 2; if (arg_temp(op->args[1])->state == TS_DEAD) { if (arg_temp(op->args[0])->state == TS_DEAD) { /* Both parts of the operation are dead. */ @@ -4122,19 +4116,15 @@ liveness_pass_1(TCGContext *s) goto do_not_remove; } /* Mark the single-word operation live. */ - nb_oargs = 1; goto do_not_remove; default: - /* XXX: optimize by hardcoding common cases (e.g. triadic ops) */ - nb_iargs = def->nb_iargs; - nb_oargs = def->nb_oargs; - /* Test if the operation can be removed because all its outputs are dead. We assume that nb_oargs == 0 implies side effects */ - if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && nb_oargs != 0) { - for (int i = 0; i < nb_oargs; i++) { + def = &tcg_op_defs[opc]; + if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && def->nb_oargs != 0) { + for (int i = def->nb_oargs - 1; i >= 0; i--) { if (arg_temp(op->args[i])->state != TS_DEAD) { goto do_not_remove; } @@ -4148,6 +4138,10 @@ liveness_pass_1(TCGContext *s) break; do_not_remove: + def = &tcg_op_defs[opc]; + nb_iargs = def->nb_iargs; + nb_oargs = def->nb_oargs; + for (int i = 0; i < nb_oargs; i++) { ts = arg_temp(op->args[i]); From 76f42780292c16a0d2f36cbbfbaf57495cd4d5e8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Jan 2025 13:58:39 -0800 Subject: [PATCH 0472/2760] tcg: Add add/sub with carry opcodes and infrastructure Liveness needs to track carry-live state in order to determine if the (hidden) output of the opcode is used. Code generation needs to track carry-live state in order to avoid clobbering cpu flags when loading constants. So far, output routines and backends are unchanged. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 61 +++++++++++++++++ include/tcg/tcg-opc.h | 10 +++ include/tcg/tcg.h | 13 +++- tcg/optimize.c | 11 +++ tcg/tcg.c | 150 ++++++++++++++++++++++++++++++++++++++--- 5 files changed, 235 insertions(+), 10 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 9392d88069..93bcc70639 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -593,6 +593,67 @@ Multiword arithmetic support .. list-table:: + * - addco *t0*, *t1*, *t2* + + - | Compute *t0* = *t1* + *t2* and in addition output to the + carry bit provided by the host architecture. + + * - addci *t0, *t1*, *t2* + + - | Compute *t0* = *t1* + *t2* + *C*, where *C* is the + input carry bit provided by the host architecture. + The output carry bit need not be computed. + + * - addcio *t0, *t1*, *t2* + + - | Compute *t0* = *t1* + *t2* + *C*, where *C* is the + input carry bit provided by the host architecture, + and also compute the output carry bit. + + * - addc1o *t0, *t1*, *t2* + + - | Compute *t0* = *t1* + *t2* + 1, and in addition output to the + carry bit provided by the host architecture. This is akin to + *addcio* with a fixed carry-in value of 1. + | This is intended to be used by the optimization pass, + intermediate to complete folding of the addition chain. + In some cases complete folding is not possible and this + opcode will remain until output. If this happens, the + code generator will use ``tcg_out_set_carry`` and then + the output routine for *addcio*. + + * - subbo *t0*, *t1*, *t2* + + - | Compute *t0* = *t1* - *t2* and in addition output to the + borrow bit provided by the host architecture. + | Depending on the host architecture, the carry bit may or may not be + identical to the borrow bit. Thus the addc\* and subb\* + opcodes must not be mixed. + + * - subbi *t0, *t1*, *t2* + + - | Compute *t0* = *t1* - *t2* - *B*, where *B* is the + input borrow bit provided by the host architecture. + The output borrow bit need not be computed. + + * - subbio *t0, *t1*, *t2* + + - | Compute *t0* = *t1* - *t2* - *B*, where *B* is the + input borrow bit provided by the host architecture, + and also compute the output borrow bit. + + * - subb1o *t0, *t1*, *t2* + + - | Compute *t0* = *t1* - *t2* - 1, and in addition output to the + borrow bit provided by the host architecture. This is akin to + *subbio* with a fixed borrow-in value of 1. + | This is intended to be used by the optimization pass, + intermediate to complete folding of the subtraction chain. + In some cases complete folding is not possible and this + opcode will remain until output. If this happens, the + code generator will use ``tcg_out_set_borrow`` and then + the output routine for *subbio*. + * - add2_i32/i64 *t0_low*, *t0_high*, *t1_low*, *t1_high*, *t2_low*, *t2_high* sub2_i32/i64 *t0_low*, *t0_high*, *t1_low*, *t1_high*, *t2_low*, *t2_high* diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 1f995c54be..9cc20cd62c 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -82,6 +82,16 @@ DEF(shr, 1, 2, 0, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) +DEF(addco, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT) +DEF(addc1o, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT) +DEF(addci, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_IN) +DEF(addcio, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_IN | TCG_OPF_CARRY_OUT) + +DEF(subbo, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT) +DEF(subb1o, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT) +DEF(subbi, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_IN) +DEF(subbio, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_IN | TCG_OPF_CARRY_OUT) + /* load/store */ DEF(ld8u_i32, 1, 1, 1, 0) DEF(ld8s_i32, 1, 1, 1, 0) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index c6b50b5226..aa300a2f8b 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -418,6 +418,11 @@ struct TCGContext { MemOp riscv_cur_vsew; TCGType riscv_cur_type; #endif + /* + * During the tcg_reg_alloc_op loop, we are within a sequence of + * carry-using opcodes like addco+addci. + */ + bool carry_live; GHashTable *const_table[TCG_TYPE_COUNT]; TCGTempSet free_temps[TCG_TYPE_COUNT]; @@ -749,13 +754,17 @@ enum { /* Instruction operands are vectors. */ TCG_OPF_VECTOR = 0x40, /* Instruction is a conditional branch. */ - TCG_OPF_COND_BRANCH = 0x80 + TCG_OPF_COND_BRANCH = 0x80, + /* Instruction produces carry out. */ + TCG_OPF_CARRY_OUT = 0x100, + /* Instruction consumes carry in. */ + TCG_OPF_CARRY_IN = 0x200, }; typedef struct TCGOpDef { const char *name; uint8_t nb_oargs, nb_iargs, nb_cargs, nb_args; - uint8_t flags; + uint16_t flags; } TCGOpDef; extern const TCGOpDef tcg_op_defs[]; diff --git a/tcg/optimize.c b/tcg/optimize.c index a728a4b2fa..8b00833f97 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1226,6 +1226,12 @@ static bool fold_add_vec(OptContext *ctx, TCGOp *op) return finish_folding(ctx, op); } +static bool fold_add_carry(OptContext *ctx, TCGOp *op) +{ + fold_commutative(ctx, op); + return finish_folding(ctx, op); +} + static bool fold_addsub2(OptContext *ctx, TCGOp *op, bool add) { bool a_const = arg_is_const(op->args[2]) && arg_is_const(op->args[3]); @@ -2829,6 +2835,11 @@ void tcg_optimize(TCGContext *s) case INDEX_op_add_vec: done = fold_add_vec(&ctx, op); break; + case INDEX_op_addci: + case INDEX_op_addco: + case INDEX_op_addcio: + done = fold_add_carry(&ctx, op); + break; CASE_OP_32_64(add2): done = fold_add2(&ctx, op); break; diff --git a/tcg/tcg.c b/tcg/tcg.c index 2849bba480..f04ad0afcf 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -3914,6 +3914,17 @@ liveness_pass_0(TCGContext *s) } } +static void assert_carry_dead(TCGContext *s) +{ + /* + * Carry operations can be separated by a few insns like mov, + * load or store, but they should always be "close", and + * carry-out operations should always be paired with carry-in. + * At various boundaries, carry must have been consumed. + */ + tcg_debug_assert(!s->carry_live); +} + /* Liveness analysis : update the opc_arg_life array to tell if a given input arguments is dead. Instructions updating dead temporaries are removed. */ @@ -3933,17 +3944,19 @@ liveness_pass_1(TCGContext *s) /* ??? Should be redundant with the exit_tb that ends the TB. */ la_func_end(s, nb_globals, nb_temps); + s->carry_live = false; QTAILQ_FOREACH_REVERSE_SAFE(op, &s->ops, link, op_prev) { int nb_iargs, nb_oargs; TCGOpcode opc_new, opc_new2; TCGLifeData arg_life = 0; TCGTemp *ts; TCGOpcode opc = op->opc; - const TCGOpDef *def = &tcg_op_defs[opc]; + const TCGOpDef *def; const TCGArgConstraint *args_ct; switch (opc) { case INDEX_op_call: + assert_carry_dead(s); { const TCGHelperInfo *info = tcg_call_info(op); int call_flags = tcg_call_flags(op); @@ -4055,6 +4068,7 @@ liveness_pass_1(TCGContext *s) } break; case INDEX_op_insn_start: + assert_carry_dead(s); break; case INDEX_op_discard: /* mark the temporary as dead */ @@ -4071,6 +4085,7 @@ liveness_pass_1(TCGContext *s) case INDEX_op_sub2_i64: opc_new = INDEX_op_sub; do_addsub2: + assert_carry_dead(s); /* Test if the high part of the operation is dead, but not the low part. The result can be optimized to a simple add or sub. This happens often for x86_64 guest when the @@ -4096,6 +4111,7 @@ liveness_pass_1(TCGContext *s) opc_new = INDEX_op_mul; opc_new2 = INDEX_op_muluh; do_mul2: + assert_carry_dead(s); if (arg_temp(op->args[1])->state == TS_DEAD) { if (arg_temp(op->args[0])->state == TS_DEAD) { /* Both parts of the operation are dead. */ @@ -4118,10 +4134,89 @@ liveness_pass_1(TCGContext *s) /* Mark the single-word operation live. */ goto do_not_remove; + case INDEX_op_addco: + if (s->carry_live) { + goto do_not_remove; + } + op->opc = opc = INDEX_op_add; + goto do_default; + + case INDEX_op_addcio: + if (s->carry_live) { + goto do_not_remove; + } + op->opc = opc = INDEX_op_addci; + goto do_default; + + case INDEX_op_subbo: + if (s->carry_live) { + goto do_not_remove; + } + /* Lower to sub, but this may also require canonicalization. */ + op->opc = opc = INDEX_op_sub; + ts = arg_temp(op->args[2]); + if (ts->kind == TEMP_CONST) { + ts = tcg_constant_internal(ts->type, -ts->val); + if (ts->state_ptr == NULL) { + tcg_debug_assert(temp_idx(ts) == nb_temps); + nb_temps++; + ts->state_ptr = tcg_malloc(sizeof(TCGRegSet)); + ts->state = TS_DEAD; + la_reset_pref(ts); + } + op->args[2] = temp_arg(ts); + op->opc = opc = INDEX_op_add; + } + goto do_default; + + case INDEX_op_subbio: + if (s->carry_live) { + goto do_not_remove; + } + op->opc = opc = INDEX_op_subbi; + goto do_default; + + case INDEX_op_addc1o: + if (s->carry_live) { + goto do_not_remove; + } + /* Lower to add, add +1. */ + op_prev = tcg_op_insert_before(s, op, INDEX_op_add, + TCGOP_TYPE(op), 3); + op_prev->args[0] = op->args[0]; + op_prev->args[1] = op->args[1]; + op_prev->args[2] = op->args[2]; + op->opc = opc = INDEX_op_add; + op->args[1] = op->args[0]; + ts = arg_temp(op->args[0]); + ts = tcg_constant_internal(ts->type, 1); + op->args[2] = temp_arg(ts); + goto do_default; + + case INDEX_op_subb1o: + if (s->carry_live) { + goto do_not_remove; + } + /* Lower to sub, add -1. */ + op_prev = tcg_op_insert_before(s, op, INDEX_op_sub, + TCGOP_TYPE(op), 3); + op_prev->args[0] = op->args[0]; + op_prev->args[1] = op->args[1]; + op_prev->args[2] = op->args[2]; + op->opc = opc = INDEX_op_add; + op->args[1] = op->args[0]; + ts = arg_temp(op->args[0]); + ts = tcg_constant_internal(ts->type, -1); + op->args[2] = temp_arg(ts); + goto do_default; + default: - /* Test if the operation can be removed because all - its outputs are dead. We assume that nb_oargs == 0 - implies side effects */ + do_default: + /* + * Test if the operation can be removed because all + * its outputs are dead. We assume that nb_oargs == 0 + * implies side effects. + */ def = &tcg_op_defs[opc]; if (!(def->flags & TCG_OPF_SIDE_EFFECTS) && def->nb_oargs != 0) { for (int i = def->nb_oargs - 1; i >= 0; i--) { @@ -4163,12 +4258,16 @@ liveness_pass_1(TCGContext *s) /* If end of basic block, update. */ if (def->flags & TCG_OPF_BB_EXIT) { + assert_carry_dead(s); la_func_end(s, nb_globals, nb_temps); } else if (def->flags & TCG_OPF_COND_BRANCH) { + assert_carry_dead(s); la_bb_sync(s, nb_globals, nb_temps); } else if (def->flags & TCG_OPF_BB_END) { + assert_carry_dead(s); la_bb_end(s, nb_globals, nb_temps); } else if (def->flags & TCG_OPF_SIDE_EFFECTS) { + assert_carry_dead(s); la_global_sync(s, nb_globals); if (def->flags & TCG_OPF_CALL_CLOBBER) { la_cross_call(s, nb_temps); @@ -4182,6 +4281,9 @@ liveness_pass_1(TCGContext *s) arg_life |= DEAD_ARG << i; } } + if (def->flags & TCG_OPF_CARRY_OUT) { + s->carry_live = false; + } /* Input arguments are live for preceding opcodes. */ for (int i = nb_oargs; i < nb_oargs + nb_iargs; i++) { @@ -4193,6 +4295,9 @@ liveness_pass_1(TCGContext *s) ts->state &= ~TS_DEAD; } } + if (def->flags & TCG_OPF_CARRY_IN) { + s->carry_live = true; + } /* Incorporate constraints for this operand. */ switch (opc) { @@ -4232,6 +4337,7 @@ liveness_pass_1(TCGContext *s) } op->life = arg_life; } + assert_carry_dead(s); } /* Liveness analysis: Convert indirect regs to direct temporaries. */ @@ -4820,9 +4926,8 @@ static void sync_globals(TCGContext *s, TCGRegSet allocated_regs) all globals are stored at their canonical location. */ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs) { - int i; - - for (i = s->nb_globals; i < s->nb_temps; i++) { + assert_carry_dead(s); + for (int i = s->nb_globals; i < s->nb_temps; i++) { TCGTemp *ts = &s->temps[i]; switch (ts->kind) { @@ -4853,6 +4958,7 @@ static void tcg_reg_alloc_bb_end(TCGContext *s, TCGRegSet allocated_regs) */ static void tcg_reg_alloc_cbranch(TCGContext *s, TCGRegSet allocated_regs) { + assert_carry_dead(s); sync_globals(s, allocated_regs); for (int i = s->nb_globals; i < s->nb_temps; i++) { @@ -5124,6 +5230,10 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) int const_args[TCG_MAX_OP_ARGS]; TCGCond op_cond; + if (def->flags & TCG_OPF_CARRY_IN) { + tcg_debug_assert(s->carry_live); + } + nb_oargs = def->nb_oargs; nb_iargs = def->nb_iargs; @@ -5380,6 +5490,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) tcg_reg_alloc_bb_end(s, i_allocated_regs); } else { if (def->flags & TCG_OPF_CALL_CLOBBER) { + assert_carry_dead(s); /* XXX: permit generic clobber register list ? */ for (i = 0; i < TCG_TARGET_NB_REGS; i++) { if (tcg_regset_test_reg(tcg_target_call_clobber_regs, i)) { @@ -5497,7 +5608,8 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_sub: { - const TCGOutOpSubtract *out = &outop_sub; + const TCGOutOpSubtract *out = + container_of(all_outop[op->opc], TCGOutOpSubtract, base); /* * Constants should never appear in the second source operand. @@ -5512,6 +5624,16 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_addco: + case INDEX_op_subbo: + case INDEX_op_addci: + case INDEX_op_subbi: + case INDEX_op_addcio: + case INDEX_op_subbio: + case INDEX_op_addc1o: + case INDEX_op_subb1o: + g_assert_not_reached(); + case INDEX_op_bswap64: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: @@ -5700,6 +5822,13 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; } + if (def->flags & TCG_OPF_CARRY_IN) { + s->carry_live = false; + } + if (def->flags & TCG_OPF_CARRY_OUT) { + s->carry_live = true; + } + /* move the outputs in the correct register if needed */ for(i = 0; i < nb_oargs; i++) { ts = arg_temp(op->args[i]); @@ -6702,6 +6831,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) tcg_out_tb_start(s); num_insns = -1; + s->carry_live = false; QTAILQ_FOREACH(op, &s->ops, link) { TCGOpcode opc = op->opc; @@ -6730,6 +6860,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) tcg_reg_alloc_dup(s, op); break; case INDEX_op_insn_start: + assert_carry_dead(s); if (num_insns >= 0) { size_t off = tcg_current_code_size(s); s->gen_insn_end_off[num_insns] = off; @@ -6750,6 +6881,7 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) tcg_out_label(s, arg_label(op->args[0])); break; case INDEX_op_call: + assert_carry_dead(s); tcg_reg_alloc_call(s, op); break; case INDEX_op_exit_tb: @@ -6786,6 +6918,8 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) return -2; } } + assert_carry_dead(s); + tcg_debug_assert(num_insns + 1 == s->gen_tb->icount); s->gen_insn_end_off[num_insns] = tcg_current_code_size(s); From ee60315210058c9b3d0869b2046eeb254ba33f3a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Jan 2025 23:27:53 -0800 Subject: [PATCH 0473/2760] tcg: Add TCGOutOp structures for add/sub carry opcodes Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 34 ++++++++++++++++++ tcg/arm/tcg-target.c.inc | 34 ++++++++++++++++++ tcg/i386/tcg-target.c.inc | 34 ++++++++++++++++++ tcg/loongarch64/tcg-target.c.inc | 34 ++++++++++++++++++ tcg/mips/tcg-target.c.inc | 34 ++++++++++++++++++ tcg/ppc/tcg-target.c.inc | 34 ++++++++++++++++++ tcg/riscv/tcg-target.c.inc | 34 ++++++++++++++++++ tcg/s390x/tcg-target.c.inc | 34 ++++++++++++++++++ tcg/sparc64/tcg-target.c.inc | 34 ++++++++++++++++++ tcg/tcg.c | 61 +++++++++++++++++++++++++++----- tcg/tci/tcg-target.c.inc | 34 ++++++++++++++++++ 11 files changed, 393 insertions(+), 8 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index bece494c55..87f8c98ed7 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2078,6 +2078,23 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_and(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2421,6 +2438,23 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index f366424af5..aa0397520d 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1826,6 +1826,23 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_and(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2135,6 +2152,23 @@ static const TCGOutOpSubtract outop_sub = { .out_rir = tgen_subfi, }; +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 2b2ad9ca95..04e31cae12 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2629,6 +2629,23 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_and(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3054,6 +3071,23 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index eb2143703d..4f640764ef 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1338,6 +1338,23 @@ static const TCGOutOpBinary outop_add = { .out_rri = tcg_out_addi, }; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_and(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1727,6 +1744,23 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index e69781b871..0c268cef42 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1593,6 +1593,23 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_and(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2044,6 +2061,23 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index a964239aab..5b04655f3b 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2863,6 +2863,23 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_and(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -3267,6 +3284,23 @@ static const TCGOutOpSubtract outop_sub = { .out_rir = tgen_subfi, }; +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index dce46dcba6..707ebb8f6d 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1947,6 +1947,23 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_and(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2333,6 +2350,23 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index ff06834e6e..a30afb455e 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2248,6 +2248,23 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_and(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -2766,6 +2783,23 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 4c7d916302..12f0dbd23d 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1381,6 +1381,23 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_and(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -1717,6 +1734,23 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { diff --git a/tcg/tcg.c b/tcg/tcg.c index f04ad0afcf..3b9f519ef6 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -133,6 +133,8 @@ static void tcg_out_addi_ptr(TCGContext *s, TCGReg, TCGReg, tcg_target_long); static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2); static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg); static void tcg_out_goto_tb(TCGContext *s, int which); +static void tcg_out_set_carry(TCGContext *s); +static void tcg_out_set_borrow(TCGContext *s); static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]); @@ -978,6 +980,18 @@ typedef struct TCGOutOp { TCGConstraintSetIndex (*dynamic_constraint)(TCGType type, unsigned flags); } TCGOutOp; +typedef struct TCGOutOpAddSubCarry { + TCGOutOp base; + void (*out_rrr)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2); + void (*out_rri)(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2); + void (*out_rir)(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2); + void (*out_rii)(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2); +} TCGOutOpAddSubCarry; + typedef struct TCGOutOpBinary { TCGOutOp base; void (*out_rrr)(TCGContext *s, TCGType type, @@ -1131,6 +1145,11 @@ static const TCGOutOpUnary outop_extrl_i64_i32 = { /* Register allocation descriptions for every TCGOpcode. */ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_add, TCGOutOpBinary, outop_add), + OUTOP(INDEX_op_addci, TCGOutOpAddSubCarry, outop_addci), + OUTOP(INDEX_op_addcio, TCGOutOpBinary, outop_addcio), + OUTOP(INDEX_op_addco, TCGOutOpBinary, outop_addco), + /* addc1o is implemented with set_carry + addcio */ + OUTOP(INDEX_op_addc1o, TCGOutOpBinary, outop_addcio), OUTOP(INDEX_op_and, TCGOutOpBinary, outop_and), OUTOP(INDEX_op_andc, TCGOutOpBinary, outop_andc), OUTOP(INDEX_op_brcond, TCGOutOpBrcond, outop_brcond), @@ -1170,6 +1189,11 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), + OUTOP(INDEX_op_subbi, TCGOutOpAddSubCarry, outop_subbi), + OUTOP(INDEX_op_subbio, TCGOutOpAddSubCarry, outop_subbio), + OUTOP(INDEX_op_subbo, TCGOutOpAddSubCarry, outop_subbo), + /* subb1o is implemented with set_borrow + subbio */ + OUTOP(INDEX_op_subb1o, TCGOutOpAddSubCarry, outop_subbio), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), #if TCG_TARGET_REG_BITS == 32 @@ -5569,7 +5593,12 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) /* emit instruction */ TCGType type = TCGOP_TYPE(op); switch (op->opc) { + case INDEX_op_addc1o: + tcg_out_set_carry(s); + /* fall through */ case INDEX_op_add: + case INDEX_op_addcio: + case INDEX_op_addco: case INDEX_op_and: case INDEX_op_andc: case INDEX_op_clz: @@ -5608,8 +5637,7 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) case INDEX_op_sub: { - const TCGOutOpSubtract *out = - container_of(all_outop[op->opc], TCGOutOpSubtract, base); + const TCGOutOpSubtract *out = &outop_sub; /* * Constants should never appear in the second source operand. @@ -5624,15 +5652,32 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_addco: - case INDEX_op_subbo: + case INDEX_op_subb1o: + tcg_out_set_borrow(s); + /* fall through */ case INDEX_op_addci: case INDEX_op_subbi: - case INDEX_op_addcio: case INDEX_op_subbio: - case INDEX_op_addc1o: - case INDEX_op_subb1o: - g_assert_not_reached(); + case INDEX_op_subbo: + { + const TCGOutOpAddSubCarry *out = + container_of(all_outop[op->opc], TCGOutOpAddSubCarry, base); + + if (const_args[2]) { + if (const_args[1]) { + out->out_rii(s, type, new_args[0], + new_args[1], new_args[2]); + } else { + out->out_rri(s, type, new_args[0], + new_args[1], new_args[2]); + } + } else if (const_args[1]) { + out->out_rir(s, type, new_args[0], new_args[1], new_args[2]); + } else { + out->out_rrr(s, type, new_args[0], new_args[1], new_args[2]); + } + } + break; case INDEX_op_bswap64: case INDEX_op_ext_i32_i64: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 9a5ca9c778..bba96d7a19 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -573,6 +573,23 @@ static const TCGOutOpBinary outop_add = { .out_rrr = tgen_add, }; +static const TCGOutOpBinary outop_addco = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpBinary outop_addcio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_carry(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_and(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { @@ -893,6 +910,23 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static const TCGOutOpAddSubCarry outop_subbo = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbi = { + .base.static_constraint = C_NotImplemented, +}; + +static const TCGOutOpAddSubCarry outop_subbio = { + .base.static_constraint = C_NotImplemented, +}; + +static void tcg_out_set_borrow(TCGContext *s) +{ + g_assert_not_reached(); +} + static void tgen_xor(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, TCGReg a2) { From aeb3514bd06b278dd026c90e8f71ca5b32762ab9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Jan 2025 18:28:15 -0800 Subject: [PATCH 0474/2760] tcg/optimize: Handle add/sub with carry opcodes Propagate known carry when possible, and simplify the opcodes to not require carry-in when known. The result will be cleaned up further by the subsequent liveness analysis pass. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 319 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 316 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 8b00833f97..cfcd0ab7f9 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -66,6 +66,7 @@ typedef struct OptContext { /* In flight values from optimization. */ TCGType type; + int carry_state; /* -1 = non-constant, {0,1} = constant carry-in */ } OptContext; static inline TempOptInfo *ts_info(TCGTemp *ts) @@ -1203,8 +1204,10 @@ static bool fold_xx_to_x(OptContext *ctx, TCGOp *op) * 3) those that produce information about the result value. */ +static bool fold_addco(OptContext *ctx, TCGOp *op); static bool fold_or(OptContext *ctx, TCGOp *op); static bool fold_orc(OptContext *ctx, TCGOp *op); +static bool fold_subbo(OptContext *ctx, TCGOp *op); static bool fold_xor(OptContext *ctx, TCGOp *op); static bool fold_add(OptContext *ctx, TCGOp *op) @@ -1226,9 +1229,167 @@ static bool fold_add_vec(OptContext *ctx, TCGOp *op) return finish_folding(ctx, op); } -static bool fold_add_carry(OptContext *ctx, TCGOp *op) +static void squash_prev_carryout(OptContext *ctx, TCGOp *op) +{ + TempOptInfo *t2; + + op = QTAILQ_PREV(op, link); + switch (op->opc) { + case INDEX_op_addco: + op->opc = INDEX_op_add; + fold_add(ctx, op); + break; + case INDEX_op_addcio: + op->opc = INDEX_op_addci; + break; + case INDEX_op_addc1o: + op->opc = INDEX_op_add; + t2 = arg_info(op->args[2]); + if (ti_is_const(t2)) { + op->args[2] = arg_new_constant(ctx, ti_const_val(t2) + 1); + /* Perform other constant folding, if needed. */ + fold_add(ctx, op); + } else { + TCGArg ret = op->args[0]; + op = opt_insert_after(ctx, op, INDEX_op_add, 3); + op->args[0] = ret; + op->args[1] = ret; + op->args[2] = arg_new_constant(ctx, 1); + } + break; + default: + g_assert_not_reached(); + } +} + +static bool fold_addci(OptContext *ctx, TCGOp *op) { fold_commutative(ctx, op); + + if (ctx->carry_state < 0) { + return finish_folding(ctx, op); + } + + squash_prev_carryout(ctx, op); + op->opc = INDEX_op_add; + + if (ctx->carry_state > 0) { + TempOptInfo *t2 = arg_info(op->args[2]); + + /* + * Propagate the known carry-in into a constant, if possible. + * Otherwise emit a second add +1. + */ + if (ti_is_const(t2)) { + op->args[2] = arg_new_constant(ctx, ti_const_val(t2) + 1); + } else { + TCGOp *op2 = opt_insert_before(ctx, op, INDEX_op_add, 3); + + op2->args[0] = op->args[0]; + op2->args[1] = op->args[1]; + op2->args[2] = op->args[2]; + fold_add(ctx, op2); + + op->args[1] = op->args[0]; + op->args[2] = arg_new_constant(ctx, 1); + } + } + + ctx->carry_state = -1; + return fold_add(ctx, op); +} + +static bool fold_addcio(OptContext *ctx, TCGOp *op) +{ + TempOptInfo *t1, *t2; + int carry_out = -1; + uint64_t sum, max; + + fold_commutative(ctx, op); + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + + /* + * The z_mask value is >= the maximum value that can be represented + * with the known zero bits. So adding the z_mask values will not + * overflow if and only if the true values cannot overflow. + */ + if (!uadd64_overflow(t1->z_mask, t2->z_mask, &sum) && + !uadd64_overflow(sum, ctx->carry_state != 0, &sum)) { + carry_out = 0; + } + + if (ctx->carry_state < 0) { + ctx->carry_state = carry_out; + return finish_folding(ctx, op); + } + + squash_prev_carryout(ctx, op); + if (ctx->carry_state == 0) { + goto do_addco; + } + + /* Propagate the known carry-in into a constant, if possible. */ + max = ctx->type == TCG_TYPE_I32 ? UINT32_MAX : UINT64_MAX; + if (ti_is_const(t2)) { + uint64_t v = ti_const_val(t2) & max; + if (v < max) { + op->args[2] = arg_new_constant(ctx, v + 1); + goto do_addco; + } + /* max + known carry in produces known carry out. */ + carry_out = 1; + } + if (ti_is_const(t1)) { + uint64_t v = ti_const_val(t1) & max; + if (v < max) { + op->args[1] = arg_new_constant(ctx, v + 1); + goto do_addco; + } + carry_out = 1; + } + + /* Adjust the opcode to remember the known carry-in. */ + op->opc = INDEX_op_addc1o; + ctx->carry_state = carry_out; + return finish_folding(ctx, op); + + do_addco: + op->opc = INDEX_op_addco; + return fold_addco(ctx, op); +} + +static bool fold_addco(OptContext *ctx, TCGOp *op) +{ + TempOptInfo *t1, *t2; + int carry_out = -1; + uint64_t ign; + + fold_commutative(ctx, op); + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + + if (ti_is_const(t2)) { + uint64_t v2 = ti_const_val(t2); + + if (ti_is_const(t1)) { + uint64_t v1 = ti_const_val(t1); + /* Given sign-extension of z_mask for I32, we need not truncate. */ + carry_out = uadd64_overflow(v1, v2, &ign); + } else if (v2 == 0) { + carry_out = 0; + } + } else { + /* + * The z_mask value is >= the maximum value that can be represented + * with the known zero bits. So adding the z_mask values will not + * overflow if and only if the true values cannot overflow. + */ + if (!uadd64_overflow(t1->z_mask, t2->z_mask, &ign)) { + carry_out = 0; + } + } + ctx->carry_state = carry_out; return finish_folding(ctx, op); } @@ -2649,6 +2810,145 @@ static bool fold_sub2(OptContext *ctx, TCGOp *op) return fold_addsub2(ctx, op, false); } +static void squash_prev_borrowout(OptContext *ctx, TCGOp *op) +{ + TempOptInfo *t2; + + op = QTAILQ_PREV(op, link); + switch (op->opc) { + case INDEX_op_subbo: + op->opc = INDEX_op_sub; + fold_sub(ctx, op); + break; + case INDEX_op_subbio: + op->opc = INDEX_op_subbi; + break; + case INDEX_op_subb1o: + t2 = arg_info(op->args[2]); + if (ti_is_const(t2)) { + op->opc = INDEX_op_add; + op->args[2] = arg_new_constant(ctx, -(ti_const_val(t2) + 1)); + /* Perform other constant folding, if needed. */ + fold_add(ctx, op); + } else { + TCGArg ret = op->args[0]; + op->opc = INDEX_op_sub; + op = opt_insert_after(ctx, op, INDEX_op_add, 3); + op->args[0] = ret; + op->args[1] = ret; + op->args[2] = arg_new_constant(ctx, -1); + } + break; + default: + g_assert_not_reached(); + } +} + +static bool fold_subbi(OptContext *ctx, TCGOp *op) +{ + TempOptInfo *t2; + int borrow_in = ctx->carry_state; + + if (borrow_in < 0) { + return finish_folding(ctx, op); + } + ctx->carry_state = -1; + + squash_prev_borrowout(ctx, op); + if (borrow_in == 0) { + op->opc = INDEX_op_sub; + return fold_sub(ctx, op); + } + + /* + * Propagate the known carry-in into any constant, then negate to + * transform from sub to add. If there is no constant, emit a + * separate add -1. + */ + t2 = arg_info(op->args[2]); + if (ti_is_const(t2)) { + op->args[2] = arg_new_constant(ctx, -(ti_const_val(t2) + 1)); + } else { + TCGOp *op2 = opt_insert_before(ctx, op, INDEX_op_sub, 3); + + op2->args[0] = op->args[0]; + op2->args[1] = op->args[1]; + op2->args[2] = op->args[2]; + fold_sub(ctx, op2); + + op->args[1] = op->args[0]; + op->args[2] = arg_new_constant(ctx, -1); + } + op->opc = INDEX_op_add; + return fold_add(ctx, op); +} + +static bool fold_subbio(OptContext *ctx, TCGOp *op) +{ + TempOptInfo *t1, *t2; + int borrow_out = -1; + + if (ctx->carry_state < 0) { + return finish_folding(ctx, op); + } + + squash_prev_borrowout(ctx, op); + if (ctx->carry_state == 0) { + goto do_subbo; + } + + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + + /* Propagate the known borrow-in into a constant, if possible. */ + if (ti_is_const(t2)) { + uint64_t max = ctx->type == TCG_TYPE_I32 ? UINT32_MAX : UINT64_MAX; + uint64_t v = ti_const_val(t2) & max; + + if (v < max) { + op->args[2] = arg_new_constant(ctx, v + 1); + goto do_subbo; + } + /* subtracting max + 1 produces known borrow out. */ + borrow_out = 1; + } + if (ti_is_const(t1)) { + uint64_t v = ti_const_val(t1); + if (v != 0) { + op->args[2] = arg_new_constant(ctx, v - 1); + goto do_subbo; + } + } + + /* Adjust the opcode to remember the known carry-in. */ + op->opc = INDEX_op_subb1o; + ctx->carry_state = borrow_out; + return finish_folding(ctx, op); + + do_subbo: + op->opc = INDEX_op_subbo; + return fold_subbo(ctx, op); +} + +static bool fold_subbo(OptContext *ctx, TCGOp *op) +{ + TempOptInfo *t1 = arg_info(op->args[1]); + TempOptInfo *t2 = arg_info(op->args[2]); + int borrow_out = -1; + + if (ti_is_const(t2)) { + uint64_t v2 = ti_const_val(t2); + if (v2 == 0) { + borrow_out = 0; + } else if (ti_is_const(t1)) { + uint64_t v1 = ti_const_val(t1); + borrow_out = v1 < v2; + } + } + ctx->carry_state = borrow_out; + return finish_folding(ctx, op); +} + static bool fold_tcg_ld(OptContext *ctx, TCGOp *op) { uint64_t z_mask = -1, s_mask = 0; @@ -2836,9 +3136,13 @@ void tcg_optimize(TCGContext *s) done = fold_add_vec(&ctx, op); break; case INDEX_op_addci: - case INDEX_op_addco: + done = fold_addci(&ctx, op); + break; case INDEX_op_addcio: - done = fold_add_carry(&ctx, op); + done = fold_addcio(&ctx, op); + break; + case INDEX_op_addco: + done = fold_addco(&ctx, op); break; CASE_OP_32_64(add2): done = fold_add2(&ctx, op); @@ -3020,6 +3324,15 @@ void tcg_optimize(TCGContext *s) case INDEX_op_sub: done = fold_sub(&ctx, op); break; + case INDEX_op_subbi: + done = fold_subbi(&ctx, op); + break; + case INDEX_op_subbio: + done = fold_subbio(&ctx, op); + break; + case INDEX_op_subbo: + done = fold_subbo(&ctx, op); + break; case INDEX_op_sub_vec: done = fold_sub_vec(&ctx, op); break; From e2f5ee36afb3b5306c99f40044534ae7d2580114 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Jan 2025 23:08:24 -0800 Subject: [PATCH 0475/2760] tcg/optimize: With two const operands, prefer 0 in arg1 For most binary operands, two const operands fold. However, the add/sub carry opcodes have a third input. Prefer "reg, zero, const" since many risc hosts have a zero register that can fit a "reg, reg, const" insn format. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index cfcd0ab7f9..95ec3b426d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -737,12 +737,18 @@ static int do_constant_folding_cond(TCGType type, TCGArg x, #define NO_DEST temp_arg(NULL) +static int pref_commutative(TempOptInfo *ti) +{ + /* Slight preference for non-zero constants second. */ + return !ti_is_const(ti) ? 0 : ti_const_val(ti) ? 3 : 2; +} + static bool swap_commutative(TCGArg dest, TCGArg *p1, TCGArg *p2) { TCGArg a1 = *p1, a2 = *p2; int sum = 0; - sum += arg_is_const(a1); - sum -= arg_is_const(a2); + sum += pref_commutative(arg_info(a1)); + sum -= pref_commutative(arg_info(a2)); /* Prefer the constant in second argument, and then the form op a, a, b, which is better handled on non-RISC hosts. */ @@ -757,10 +763,10 @@ static bool swap_commutative(TCGArg dest, TCGArg *p1, TCGArg *p2) static bool swap_commutative2(TCGArg *p1, TCGArg *p2) { int sum = 0; - sum += arg_is_const(p1[0]); - sum += arg_is_const(p1[1]); - sum -= arg_is_const(p2[0]); - sum -= arg_is_const(p2[1]); + sum += pref_commutative(arg_info(p1[0])); + sum += pref_commutative(arg_info(p1[1])); + sum -= pref_commutative(arg_info(p2[0])); + sum -= pref_commutative(arg_info(p2[1])); if (sum > 0) { TCGArg t; t = p1[0], p1[0] = p2[0], p2[0] = t; From c4d2e3d736197bb2d82bd37de0de1819222d9768 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 13 Jan 2025 23:29:42 -0800 Subject: [PATCH 0476/2760] tcg: Use add carry opcodes to expand add2 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/tcg-op.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 127338b994..f17ec658fb 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1102,7 +1102,13 @@ void tcg_gen_movcond_i32(TCGCond cond, TCGv_i32 ret, TCGv_i32 c1, void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh) { - if (TCG_TARGET_HAS_add2_i32) { + if (tcg_op_supported(INDEX_op_addci, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + tcg_gen_op3_i32(INDEX_op_addco, t0, al, bl); + tcg_gen_op3_i32(INDEX_op_addci, rh, ah, bh); + tcg_gen_mov_i32(rl, t0); + tcg_temp_free_i32(t0); + } else if (TCG_TARGET_HAS_add2_i32) { tcg_gen_op6_i32(INDEX_op_add2_i32, rl, rh, al, ah, bl, bh); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); @@ -2822,7 +2828,26 @@ void tcg_gen_movcond_i64(TCGCond cond, TCGv_i64 ret, TCGv_i64 c1, void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh) { - if (TCG_TARGET_HAS_add2_i64) { + if (tcg_op_supported(INDEX_op_addci, TCG_TYPE_REG, 0)) { + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_op3_i32(INDEX_op_addco, TCGV_LOW(t0), + TCGV_LOW(al), TCGV_LOW(bl)); + tcg_gen_op3_i32(INDEX_op_addcio, TCGV_HIGH(t0), + TCGV_HIGH(al), TCGV_HIGH(bl)); + tcg_gen_op3_i32(INDEX_op_addcio, TCGV_LOW(rh), + TCGV_LOW(ah), TCGV_LOW(bh)); + tcg_gen_op3_i32(INDEX_op_addci, TCGV_HIGH(rh), + TCGV_HIGH(ah), TCGV_HIGH(bh)); + } else { + tcg_gen_op3_i64(INDEX_op_addco, t0, al, bl); + tcg_gen_op3_i64(INDEX_op_addci, rh, ah, bh); + } + + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); + } else if (TCG_TARGET_HAS_add2_i64) { tcg_gen_op6_i64(INDEX_op_add2_i64, rl, rh, al, ah, bl, bh); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); From 7a89deae635ceaf687973dd95e4714af27a3f9d2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 14 Jan 2025 18:58:05 -0800 Subject: [PATCH 0477/2760] tcg: Use sub carry opcodes to expand sub2 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/tcg-op.c | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index f17ec658fb..447b0ebacd 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1126,7 +1126,13 @@ void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh) { - if (TCG_TARGET_HAS_sub2_i32) { + if (tcg_op_supported(INDEX_op_subbi, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + tcg_gen_op3_i32(INDEX_op_subbo, t0, al, bl); + tcg_gen_op3_i32(INDEX_op_subbi, rh, ah, bh); + tcg_gen_mov_i32(rl, t0); + tcg_temp_free_i32(t0); + } else if (TCG_TARGET_HAS_sub2_i32) { tcg_gen_op6_i32(INDEX_op_sub2_i32, rl, rh, al, ah, bl, bh); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); @@ -2865,7 +2871,26 @@ void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh) { - if (TCG_TARGET_HAS_sub2_i64) { + if (tcg_op_supported(INDEX_op_subbi, TCG_TYPE_REG, 0)) { + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + + if (TCG_TARGET_REG_BITS == 32) { + tcg_gen_op3_i32(INDEX_op_subbo, TCGV_LOW(t0), + TCGV_LOW(al), TCGV_LOW(bl)); + tcg_gen_op3_i32(INDEX_op_subbio, TCGV_HIGH(t0), + TCGV_HIGH(al), TCGV_HIGH(bl)); + tcg_gen_op3_i32(INDEX_op_subbio, TCGV_LOW(rh), + TCGV_LOW(ah), TCGV_LOW(bh)); + tcg_gen_op3_i32(INDEX_op_subbi, TCGV_HIGH(rh), + TCGV_HIGH(ah), TCGV_HIGH(bh)); + } else { + tcg_gen_op3_i64(INDEX_op_subbo, t0, al, bl); + tcg_gen_op3_i64(INDEX_op_subbi, rh, ah, bh); + } + + tcg_gen_mov_i64(rl, t0); + tcg_temp_free_i64(t0); + } else if (TCG_TARGET_HAS_sub2_i64) { tcg_gen_op6_i64(INDEX_op_sub2_i64, rl, rh, al, ah, bl, bh); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); From 1f049cc5fda405c46de94f9328f0e3f84c0c993b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Jan 2025 22:05:48 -0800 Subject: [PATCH 0478/2760] tcg/i386: Honor carry_live in tcg_out_movi Do not clobber flags if they're live. Required in order to perform register allocation on add/sub carry opcodes. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/i386/tcg-target.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 04e31cae12..8e0ccbc722 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1092,7 +1092,7 @@ static void tcg_out_movi_int(TCGContext *s, TCGType type, { tcg_target_long diff; - if (arg == 0) { + if (arg == 0 && !s->carry_live) { tgen_arithr(s, ARITH_XOR, ret, ret); return; } From e37e98b711975752d592e7b2daf11d320d0a8daf Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Jan 2025 22:24:56 -0800 Subject: [PATCH 0479/2760] tcg/i386: Implement add/sub carry opcodes Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/i386/tcg-target-con-set.h | 1 - tcg/i386/tcg-target-has.h | 8 +-- tcg/i386/tcg-target.c.inc | 115 +++++++++++++++++++++------------- 3 files changed, 75 insertions(+), 49 deletions(-) diff --git a/tcg/i386/tcg-target-con-set.h b/tcg/i386/tcg-target-con-set.h index 0ae9775944..85c93836bb 100644 --- a/tcg/i386/tcg-target-con-set.h +++ b/tcg/i386/tcg-target-con-set.h @@ -57,4 +57,3 @@ C_O2_I1(r, r, L) C_O2_I2(a, d, a, r) C_O2_I2(r, r, L, L) C_O2_I3(a, d, 0, 1, r) -C_N1_O1_I4(r, r, 0, 1, re, re) diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index 0328102c2a..a984a6af2e 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -26,14 +26,14 @@ #define have_avx512vbmi2 ((cpuinfo & CPUINFO_AVX512VBMI2) && have_avx512vl) /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 #if TCG_TARGET_REG_BITS == 64 /* Keep 32-bit values zero-extended in a register. */ #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #else #define TCG_TARGET_HAS_qemu_st8_i32 1 diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 8e0ccbc722..44f9afc0d6 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -424,6 +424,7 @@ static bool tcg_target_const_match(int64_t val, int ct, #define OPC_SHLX (0xf7 | P_EXT38 | P_DATA16) #define OPC_SHRX (0xf7 | P_EXT38 | P_SIMDF2) #define OPC_SHRD_Ib (0xac | P_EXT) +#define OPC_STC (0xf9) #define OPC_TESTB (0x84) #define OPC_TESTL (0x85) #define OPC_TZCNT (0xbc | P_EXT | P_SIMDF3) @@ -2629,21 +2630,55 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_addco(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_ADD + rexw, a0, a2); +} + +static void tgen_addco_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_ADD + rexw, a0, a2, true); +} + static const TCGOutOpBinary outop_addco = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_addco, + .out_rri = tgen_addco_imm, }; -static const TCGOutOpAddSubCarry outop_addci = { - .base.static_constraint = C_NotImplemented, -}; +static void tgen_addcio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_ADC + rexw, a0, a2); +} + +static void tgen_addcio_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_ADC + rexw, a0, a2, true); +} static const TCGOutOpBinary outop_addcio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_addcio, + .out_rri = tgen_addcio_imm, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_addcio, + .out_rri = tgen_addcio_imm, }; static void tcg_out_set_carry(TCGContext *s) { - g_assert_not_reached(); + tcg_out8(s, OPC_STC); } static void tgen_and(TCGContext *s, TCGType type, @@ -3060,7 +3095,7 @@ static const TCGOutOpBinary outop_shr = { }; static void tgen_sub(TCGContext *s, TCGType type, - TCGReg a0, TCGReg a1, TCGReg a2) + TCGReg a0, TCGReg a1, TCGReg a2) { int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; tgen_arithr(s, ARITH_SUB + rexw, a0, a2); @@ -3071,21 +3106,44 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static void tgen_subbo_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_SUB + rexw, a0, a2, 1); +} + static const TCGOutOpAddSubCarry outop_subbo = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_sub, + .out_rri = tgen_subbo_rri, }; -static const TCGOutOpAddSubCarry outop_subbi = { - .base.static_constraint = C_NotImplemented, -}; +static void tgen_subbio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithr(s, ARITH_SBB + rexw, a0, a2); +} + +static void tgen_subbio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tgen_arithi(s, ARITH_SBB + rexw, a0, a2, 1); +} static const TCGOutOpAddSubCarry outop_subbio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, 0, re), + .out_rrr = tgen_subbio_rrr, + .out_rri = tgen_subbio_rri, }; +#define outop_subbi outop_subbio + static void tcg_out_set_borrow(TCGContext *s) { - g_assert_not_reached(); + tcg_out8(s, OPC_STC); } static void tgen_xor(TCGContext *s, TCGType type, @@ -3421,31 +3479,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_st(s, a0, a1, a2, args[3], TCG_TYPE_I128); break; - OP_32_64(add2): - if (const_args[4]) { - tgen_arithi(s, ARITH_ADD + rexw, a0, args[4], 1); - } else { - tgen_arithr(s, ARITH_ADD + rexw, a0, args[4]); - } - if (const_args[5]) { - tgen_arithi(s, ARITH_ADC + rexw, a1, args[5], 1); - } else { - tgen_arithr(s, ARITH_ADC + rexw, a1, args[5]); - } - break; - OP_32_64(sub2): - if (const_args[4]) { - tgen_arithi(s, ARITH_SUB + rexw, a0, args[4], 1); - } else { - tgen_arithr(s, ARITH_SUB + rexw, a0, args[4]); - } - if (const_args[5]) { - tgen_arithi(s, ARITH_SBB + rexw, a1, args[5], 1); - } else { - tgen_arithr(s, ARITH_SBB + rexw, a1, args[5]); - } - break; - #if TCG_TARGET_REG_BITS == 64 case INDEX_op_ld32s_i64: tcg_out_modrm_offset(s, OPC_MOVSLQ, a0, a1, a2); @@ -4051,12 +4084,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(re, r); - case INDEX_op_add2_i32: - case INDEX_op_add2_i64: - case INDEX_op_sub2_i32: - case INDEX_op_sub2_i64: - return C_N1_O1_I4(r, r, 0, 1, re, re); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, L); From 0ad6d64b7be6287f8c07c72f8462ef5635b1e2da Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 17 Jan 2025 22:39:14 -0800 Subject: [PATCH 0480/2760] tcg/i386: Special case addci r, 0, 0 Using addci with two zeros as input in order to capture the value of the carry-in bit is common. Special case this with sbb+neg so that we do not have to load 0 into a register first. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/i386/tcg-target-con-set.h | 1 + tcg/i386/tcg-target.c.inc | 46 ++++++++++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 3 deletions(-) diff --git a/tcg/i386/tcg-target-con-set.h b/tcg/i386/tcg-target-con-set.h index 85c93836bb..458d69c3c0 100644 --- a/tcg/i386/tcg-target-con-set.h +++ b/tcg/i386/tcg-target-con-set.h @@ -45,6 +45,7 @@ C_O1_I2(r, L, L) C_O1_I2(r, r, r) C_O1_I2(r, r, re) C_O1_I2(r, r, ri) +C_O1_I2(r, rO, re) C_O1_I2(x, x, x) C_N1_I2(r, r, r) C_N1_I2(r, r, rW) diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 44f9afc0d6..da05f13b21 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2670,10 +2670,50 @@ static const TCGOutOpBinary outop_addcio = { .out_rri = tgen_addcio_imm, }; +static void tgen_addci_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + /* Because "0O" is not a valid constraint, we must match ourselves. */ + if (a0 == a2) { + tgen_addcio(s, type, a0, a0, a1); + } else { + tcg_out_mov(s, type, a0, a1); + tgen_addcio(s, type, a0, a0, a2); + } +} + +static void tgen_addci_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + tgen_addcio_imm(s, type, a0, a0, a2); +} + +static void tgen_addci_rir(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tgen_addci_rri(s, type, a0, a2, a1); +} + +static void tgen_addci_rii(TCGContext *s, TCGType type, TCGReg a0, + tcg_target_long a1, tcg_target_long a2) +{ + if (a2 == 0) { + /* Implement 0 + 0 + C with -(x - x - c). */ + tgen_arithr(s, ARITH_SBB, a0, a0); + tcg_out_modrm(s, OPC_GRP3_Ev, EXT3_NEG, a0); + } else { + tcg_out_movi(s, type, a0, a2); + tgen_addcio_imm(s, type, a0, a0, a1); + } +} + static const TCGOutOpAddSubCarry outop_addci = { - .base.static_constraint = C_O1_I2(r, 0, re), - .out_rrr = tgen_addcio, - .out_rri = tgen_addcio_imm, + .base.static_constraint = C_O1_I2(r, rO, re), + .out_rrr = tgen_addci_rrr, + .out_rri = tgen_addci_rri, + .out_rir = tgen_addci_rir, + .out_rii = tgen_addci_rii, }; static void tcg_out_set_carry(TCGContext *s) From db3feb02b82d388d85a0c0a14d62f1a625f626a4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 01:19:51 -0800 Subject: [PATCH 0481/2760] tcg: Add tcg_gen_addcio_{i32,i64,tl} Create a function for performing an add with carry-in and producing carry out. The carry-out result is boolean. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/tcg/tcg-op-common.h | 4 ++ include/tcg/tcg-op.h | 2 + tcg/tcg-op.c | 95 +++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index 009e2778c5..b439bdb385 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -135,6 +135,8 @@ void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh); void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh); +void tcg_gen_addcio_i32(TCGv_i32 r, TCGv_i32 co, + TCGv_i32 a, TCGv_i32 b, TCGv_i32 ci); void tcg_gen_mulu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_muls2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); void tcg_gen_mulsu2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 arg1, TCGv_i32 arg2); @@ -238,6 +240,8 @@ void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh); void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh); +void tcg_gen_addcio_i64(TCGv_i64 r, TCGv_i64 co, + TCGv_i64 a, TCGv_i64 b, TCGv_i64 ci); void tcg_gen_mulu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_muls2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); void tcg_gen_mulsu2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 arg1, TCGv_i64 arg2); diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index cded92a447..59d19755e6 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -253,6 +253,7 @@ DEF_ATOMIC2(tcg_gen_atomic_umax_fetch, i64) #define tcg_gen_movcond_tl tcg_gen_movcond_i64 #define tcg_gen_add2_tl tcg_gen_add2_i64 #define tcg_gen_sub2_tl tcg_gen_sub2_i64 +#define tcg_gen_addcio_tl tcg_gen_addcio_i64 #define tcg_gen_mulu2_tl tcg_gen_mulu2_i64 #define tcg_gen_muls2_tl tcg_gen_muls2_i64 #define tcg_gen_mulsu2_tl tcg_gen_mulsu2_i64 @@ -371,6 +372,7 @@ DEF_ATOMIC2(tcg_gen_atomic_umax_fetch, i64) #define tcg_gen_movcond_tl tcg_gen_movcond_i32 #define tcg_gen_add2_tl tcg_gen_add2_i32 #define tcg_gen_sub2_tl tcg_gen_sub2_i32 +#define tcg_gen_addcio_tl tcg_gen_addcio_i32 #define tcg_gen_mulu2_tl tcg_gen_mulu2_i32 #define tcg_gen_muls2_tl tcg_gen_muls2_i32 #define tcg_gen_mulsu2_tl tcg_gen_mulsu2_i32 diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 447b0ebacd..b0a29278ab 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1123,6 +1123,33 @@ void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, } } +void tcg_gen_addcio_i32(TCGv_i32 r, TCGv_i32 co, + TCGv_i32 a, TCGv_i32 b, TCGv_i32 ci) +{ + if (tcg_op_supported(INDEX_op_addci, TCG_TYPE_I32, 0)) { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + TCGv_i32 mone = tcg_constant_i32(-1); + + tcg_gen_op3_i32(INDEX_op_addco, t0, ci, mone); + tcg_gen_op3_i32(INDEX_op_addcio, r, a, b); + tcg_gen_op3_i32(INDEX_op_addci, co, zero, zero); + tcg_temp_free_i32(t0); + } else { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 t1 = tcg_temp_ebb_new_i32(); + + tcg_gen_add_i32(t0, a, b); + tcg_gen_setcond_i32(TCG_COND_LTU, t1, t0, a); + tcg_gen_add_i32(r, t0, ci); + tcg_gen_setcond_i32(TCG_COND_LTU, t0, r, t0); + tcg_gen_or_i32(co, t0, t1); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(t1); + } +} + void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, TCGv_i32 ah, TCGv_i32 bl, TCGv_i32 bh) { @@ -2868,6 +2895,74 @@ void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, } } +void tcg_gen_addcio_i64(TCGv_i64 r, TCGv_i64 co, + TCGv_i64 a, TCGv_i64 b, TCGv_i64 ci) +{ + if (TCG_TARGET_REG_BITS == 64) { + if (tcg_op_supported(INDEX_op_addci, TCG_TYPE_I64, 0)) { + TCGv_i64 discard = tcg_temp_ebb_new_i64(); + TCGv_i64 zero = tcg_constant_i64(0); + TCGv_i64 mone = tcg_constant_i64(-1); + + tcg_gen_op3_i64(INDEX_op_addco, discard, ci, mone); + tcg_gen_op3_i64(INDEX_op_addcio, r, a, b); + tcg_gen_op3_i64(INDEX_op_addci, co, zero, zero); + tcg_temp_free_i64(discard); + } else { + TCGv_i64 t0 = tcg_temp_ebb_new_i64(); + TCGv_i64 t1 = tcg_temp_ebb_new_i64(); + + tcg_gen_add_i64(t0, a, b); + tcg_gen_setcond_i64(TCG_COND_LTU, t1, t0, a); + tcg_gen_add_i64(r, t0, ci); + tcg_gen_setcond_i64(TCG_COND_LTU, t0, r, t0); + tcg_gen_or_i64(co, t0, t1); + + tcg_temp_free_i64(t0); + tcg_temp_free_i64(t1); + } + } else { + if (tcg_op_supported(INDEX_op_addci, TCG_TYPE_I32, 0)) { + TCGv_i32 discard = tcg_temp_ebb_new_i32(); + TCGv_i32 zero = tcg_constant_i32(0); + TCGv_i32 mone = tcg_constant_i32(-1); + + tcg_gen_op3_i32(INDEX_op_addco, discard, TCGV_LOW(ci), mone); + tcg_gen_op3_i32(INDEX_op_addcio, discard, TCGV_HIGH(ci), mone); + tcg_gen_op3_i32(INDEX_op_addcio, TCGV_LOW(r), + TCGV_LOW(a), TCGV_LOW(b)); + tcg_gen_op3_i32(INDEX_op_addcio, TCGV_HIGH(r), + TCGV_HIGH(a), TCGV_HIGH(b)); + tcg_gen_op3_i32(INDEX_op_addci, TCGV_LOW(co), zero, zero); + tcg_temp_free_i32(discard); + } else { + TCGv_i32 t0 = tcg_temp_ebb_new_i32(); + TCGv_i32 c0 = tcg_temp_ebb_new_i32(); + TCGv_i32 c1 = tcg_temp_ebb_new_i32(); + + tcg_gen_or_i32(c1, TCGV_LOW(ci), TCGV_HIGH(ci)); + tcg_gen_setcondi_i32(TCG_COND_NE, c1, c1, 0); + + tcg_gen_add_i32(t0, TCGV_LOW(a), TCGV_LOW(b)); + tcg_gen_setcond_i32(TCG_COND_LTU, c0, t0, TCGV_LOW(a)); + tcg_gen_add_i32(TCGV_LOW(r), t0, c1); + tcg_gen_setcond_i32(TCG_COND_LTU, c1, TCGV_LOW(r), c1); + tcg_gen_or_i32(c1, c1, c0); + + tcg_gen_add_i32(t0, TCGV_HIGH(a), TCGV_HIGH(b)); + tcg_gen_setcond_i32(TCG_COND_LTU, c0, t0, TCGV_HIGH(a)); + tcg_gen_add_i32(TCGV_HIGH(r), t0, c1); + tcg_gen_setcond_i32(TCG_COND_LTU, c1, TCGV_HIGH(r), c1); + tcg_gen_or_i32(TCGV_LOW(co), c0, c1); + + tcg_temp_free_i32(t0); + tcg_temp_free_i32(c0); + tcg_temp_free_i32(c1); + } + tcg_gen_movi_i32(TCGV_HIGH(co), 0); + } +} + void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, TCGv_i64 ah, TCGv_i64 bl, TCGv_i64 bh) { From eea0f7ea5170b47aa2cd6f6ad077e48702aad6f3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 01:27:41 -0800 Subject: [PATCH 0482/2760] target/arm: Use tcg_gen_addcio_* for ADCS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/arm/tcg/translate-a64.c | 8 ++------ target/arm/tcg/translate.c | 17 +++-------------- 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index e076d4aa05..d9305f9d26 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1076,11 +1076,9 @@ static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) TCGv_i64 cf_64 = tcg_temp_new_i64(); TCGv_i64 vf_64 = tcg_temp_new_i64(); TCGv_i64 tmp = tcg_temp_new_i64(); - TCGv_i64 zero = tcg_constant_i64(0); tcg_gen_extu_i32_i64(cf_64, cpu_CF); - tcg_gen_add2_i64(result, cf_64, t0, zero, cf_64, zero); - tcg_gen_add2_i64(result, cf_64, result, cf_64, t1, zero); + tcg_gen_addcio_i64(result, cf_64, t0, t1, cf_64); tcg_gen_extrl_i64_i32(cpu_CF, cf_64); gen_set_NZ64(result); @@ -1094,12 +1092,10 @@ static void gen_adc_CC(int sf, TCGv_i64 dest, TCGv_i64 t0, TCGv_i64 t1) TCGv_i32 t0_32 = tcg_temp_new_i32(); TCGv_i32 t1_32 = tcg_temp_new_i32(); TCGv_i32 tmp = tcg_temp_new_i32(); - TCGv_i32 zero = tcg_constant_i32(0); tcg_gen_extrl_i64_i32(t0_32, t0); tcg_gen_extrl_i64_i32(t1_32, t1); - tcg_gen_add2_i32(cpu_NF, cpu_CF, t0_32, zero, cpu_CF, zero); - tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1_32, zero); + tcg_gen_addcio_i32(cpu_NF, cpu_CF, t0_32, t1_32, cpu_CF); tcg_gen_mov_i32(cpu_ZF, cpu_NF); tcg_gen_xor_i32(cpu_VF, cpu_NF, t0_32); diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 273b860d57..88df9c482a 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -494,20 +494,9 @@ static void gen_add_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) static void gen_adc_CC(TCGv_i32 dest, TCGv_i32 t0, TCGv_i32 t1) { TCGv_i32 tmp = tcg_temp_new_i32(); - if (tcg_op_supported(INDEX_op_add2_i32, TCG_TYPE_I32, 0)) { - tcg_gen_movi_i32(tmp, 0); - tcg_gen_add2_i32(cpu_NF, cpu_CF, t0, tmp, cpu_CF, tmp); - tcg_gen_add2_i32(cpu_NF, cpu_CF, cpu_NF, cpu_CF, t1, tmp); - } else { - TCGv_i64 q0 = tcg_temp_new_i64(); - TCGv_i64 q1 = tcg_temp_new_i64(); - tcg_gen_extu_i32_i64(q0, t0); - tcg_gen_extu_i32_i64(q1, t1); - tcg_gen_add_i64(q0, q0, q1); - tcg_gen_extu_i32_i64(q1, cpu_CF); - tcg_gen_add_i64(q0, q0, q1); - tcg_gen_extr_i64_i32(cpu_NF, cpu_CF, q0); - } + + tcg_gen_addcio_i32(cpu_NF, cpu_CF, t0, t1, cpu_CF); + tcg_gen_mov_i32(cpu_ZF, cpu_NF); tcg_gen_xor_i32(cpu_VF, cpu_NF, t0); tcg_gen_xor_i32(tmp, t0, t1); From 14e9ff8514bcae95d45dc8603f1ef33ad2f1dce1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 01:35:49 -0800 Subject: [PATCH 0483/2760] target/hppa: Use tcg_gen_addcio_i64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use this in do_add, do_sub, and do_ds, all of which need add with carry-in and carry-out. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/hppa/translate.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 14f3833322..88a7d339eb 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -1209,10 +1209,10 @@ static void do_add(DisasContext *ctx, unsigned rt, TCGv_i64 orig_in1, cb_msb = tcg_temp_new_i64(); cb = tcg_temp_new_i64(); - tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, in2, ctx->zero); if (is_c) { - tcg_gen_add2_i64(dest, cb_msb, dest, cb_msb, - get_psw_carry(ctx, d), ctx->zero); + tcg_gen_addcio_i64(dest, cb_msb, in1, in2, get_psw_carry(ctx, d)); + } else { + tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, in2, ctx->zero); } tcg_gen_xor_i64(cb, in1, in2); tcg_gen_xor_i64(cb, cb, dest); @@ -1308,9 +1308,7 @@ static void do_sub(DisasContext *ctx, unsigned rt, TCGv_i64 in1, if (is_b) { /* DEST,C = IN1 + ~IN2 + C. */ tcg_gen_not_i64(cb, in2); - tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, - get_psw_carry(ctx, d), ctx->zero); - tcg_gen_add2_i64(dest, cb_msb, dest, cb_msb, cb, ctx->zero); + tcg_gen_addcio_i64(dest, cb_msb, in1, cb, get_psw_carry(ctx, d)); tcg_gen_xor_i64(cb, cb, in1); tcg_gen_xor_i64(cb, cb, dest); } else { @@ -3008,9 +3006,7 @@ static bool trans_ds(DisasContext *ctx, arg_rrr_cf *a) tcg_gen_xor_i64(add2, in2, addc); tcg_gen_andi_i64(addc, addc, 1); - tcg_gen_add2_i64(dest, cpu_psw_cb_msb, add1, ctx->zero, add2, ctx->zero); - tcg_gen_add2_i64(dest, cpu_psw_cb_msb, dest, cpu_psw_cb_msb, - addc, ctx->zero); + tcg_gen_addcio_i64(dest, cpu_psw_cb_msb, add1, add2, addc); /* Write back the result register. */ save_gpr(ctx, a->t, dest); @@ -3553,8 +3549,7 @@ static bool do_addb(DisasContext *ctx, unsigned r, TCGv_i64 in1, TCGv_i64 cb = tcg_temp_new_i64(); TCGv_i64 cb_msb = tcg_temp_new_i64(); - tcg_gen_movi_i64(cb_msb, 0); - tcg_gen_add2_i64(dest, cb_msb, in1, cb_msb, in2, cb_msb); + tcg_gen_add2_i64(dest, cb_msb, in1, ctx->zero, in2, ctx->zero); tcg_gen_xor_i64(cb, in1, in2); tcg_gen_xor_i64(cb, cb, dest); cb_cond = get_carry(ctx, d, cb, cb_msb); From fcfbd8f4a9c3cbc09376230093d28e14acf7854b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 01:47:53 -0800 Subject: [PATCH 0484/2760] target/microblaze: Use tcg_gen_addcio_i32 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use this in gen_addc and gen_rsubc, both of which need add with carry-in and carry-out. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/microblaze/translate.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 7dcad6cf0d..23f1037236 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -311,11 +311,7 @@ static void gen_add(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) /* Input and output carry. */ static void gen_addc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - TCGv_i32 zero = tcg_constant_i32(0); - TCGv_i32 tmp = tcg_temp_new_i32(); - - tcg_gen_add2_i32(tmp, cpu_msr_c, ina, zero, cpu_msr_c, zero); - tcg_gen_add2_i32(out, cpu_msr_c, tmp, cpu_msr_c, inb, zero); + tcg_gen_addcio_i32(out, cpu_msr_c, ina, inb, cpu_msr_c); } /* Input carry, but no output carry. */ @@ -544,12 +540,10 @@ static void gen_rsub(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) /* Input and output carry. */ static void gen_rsubc(TCGv_i32 out, TCGv_i32 ina, TCGv_i32 inb) { - TCGv_i32 zero = tcg_constant_i32(0); TCGv_i32 tmp = tcg_temp_new_i32(); tcg_gen_not_i32(tmp, ina); - tcg_gen_add2_i32(tmp, cpu_msr_c, tmp, zero, cpu_msr_c, zero); - tcg_gen_add2_i32(out, cpu_msr_c, tmp, cpu_msr_c, inb, zero); + tcg_gen_addcio_i32(out, cpu_msr_c, tmp, inb, cpu_msr_c); } /* No input or output carry. */ From 02b9d791be10ea9e4c8fdfe4cfdc2a6a0de7e810 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 01:50:50 -0800 Subject: [PATCH 0485/2760] target/openrisc: Use tcg_gen_addcio_* for ADDC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/openrisc/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index d4ce60188b..baadea4448 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -221,8 +221,7 @@ static void gen_addc(DisasContext *dc, TCGv dest, TCGv srca, TCGv srcb) TCGv t0 = tcg_temp_new(); TCGv res = tcg_temp_new(); - tcg_gen_add2_tl(res, cpu_sr_cy, srca, dc->zero, cpu_sr_cy, dc->zero); - tcg_gen_add2_tl(res, cpu_sr_cy, res, cpu_sr_cy, srcb, dc->zero); + tcg_gen_addcio_tl(res, cpu_sr_cy, srca, srcb, cpu_sr_cy); tcg_gen_xor_tl(cpu_sr_ov, srca, srcb); tcg_gen_xor_tl(t0, res, srcb); tcg_gen_andc_tl(cpu_sr_ov, t0, cpu_sr_ov); From 3a6ec7d71a4e268a6be319cf2efa780091d42d30 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 01:55:11 -0800 Subject: [PATCH 0486/2760] target/ppc: Use tcg_gen_addcio_tl for ADD and SUBF MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tested-by: Nicholas Piggin Reviewed-by: Nicholas Piggin Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/ppc/translate.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/target/ppc/translate.c b/target/ppc/translate.c index fea2f2ce23..62dd008e36 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -1746,11 +1746,10 @@ static inline void gen_op_arith_add(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_mov_tl(ca32, ca); } } else { - TCGv zero = tcg_constant_tl(0); if (add_ca) { - tcg_gen_add2_tl(t0, ca, arg1, zero, ca, zero); - tcg_gen_add2_tl(t0, ca, t0, ca, arg2, zero); + tcg_gen_addcio_tl(t0, ca, arg1, arg2, ca); } else { + TCGv zero = tcg_constant_tl(0); tcg_gen_add2_tl(t0, ca, arg1, zero, arg2, zero); } gen_op_arith_compute_ca32(ctx, t0, arg1, arg2, ca32, 0); @@ -1949,11 +1948,9 @@ static inline void gen_op_arith_subf(DisasContext *ctx, TCGv ret, TCGv arg1, tcg_gen_mov_tl(cpu_ca32, cpu_ca); } } else if (add_ca) { - TCGv zero, inv1 = tcg_temp_new(); + TCGv inv1 = tcg_temp_new(); tcg_gen_not_tl(inv1, arg1); - zero = tcg_constant_tl(0); - tcg_gen_add2_tl(t0, cpu_ca, arg2, zero, cpu_ca, zero); - tcg_gen_add2_tl(t0, cpu_ca, t0, cpu_ca, inv1, zero); + tcg_gen_addcio_tl(t0, cpu_ca, arg2, inv1, cpu_ca); gen_op_arith_compute_ca32(ctx, t0, inv1, arg2, cpu_ca32, 0); } else { tcg_gen_setcond_tl(TCG_COND_GEU, cpu_ca, arg2, arg1); From 206c23e4720cd2b7668197887d1694bd346e979e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 01:59:12 -0800 Subject: [PATCH 0487/2760] target/s390x: Use tcg_gen_addcio_i64 for op_addc64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/s390x/tcg/translate.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index 00073c5560..a714f9c0c2 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -1250,11 +1250,7 @@ static DisasJumpType op_addc32(DisasContext *s, DisasOps *o) static DisasJumpType op_addc64(DisasContext *s, DisasOps *o) { compute_carry(s); - - TCGv_i64 zero = tcg_constant_i64(0); - tcg_gen_add2_i64(o->out, cc_src, o->in1, zero, cc_src, zero); - tcg_gen_add2_i64(o->out, cc_src, o->out, cc_src, o->in2, zero); - + tcg_gen_addcio_i64(o->out, cc_src, o->in1, o->in2, cc_src); return DISAS_NEXT; } From 68188214d5b5dc80acc9143f6bdca25fc06f6e2b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 02:03:03 -0800 Subject: [PATCH 0488/2760] target/sh4: Use tcg_gen_addcio_i32 for addc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/sh4/translate.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 712a57fb54..712117be22 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -695,14 +695,8 @@ static void _decode_opc(DisasContext * ctx) tcg_gen_add_i32(REG(B11_8), REG(B11_8), REG(B7_4)); return; case 0x300e: /* addc Rm,Rn */ - { - TCGv t0, t1; - t0 = tcg_constant_tl(0); - t1 = tcg_temp_new(); - tcg_gen_add2_i32(t1, cpu_sr_t, cpu_sr_t, t0, REG(B7_4), t0); - tcg_gen_add2_i32(REG(B11_8), cpu_sr_t, - REG(B11_8), t0, t1, cpu_sr_t); - } + tcg_gen_addcio_i32(REG(B11_8), cpu_sr_t, + REG(B11_8), REG(B7_4), cpu_sr_t); return; case 0x300f: /* addv Rm,Rn */ { From 6ed4d97ff7fbd2ef3d0d3f1f06d89560955873b8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 02:05:19 -0800 Subject: [PATCH 0489/2760] target/sparc: Use tcg_gen_addcio_tl for gen_op_addcc_int Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/sparc/translate.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/sparc/translate.c b/target/sparc/translate.c index adebddf27b..63dd90447b 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -396,8 +396,7 @@ static void gen_op_addcc_int(TCGv dst, TCGv src1, TCGv src2, TCGv cin) TCGv z = tcg_constant_tl(0); if (cin) { - tcg_gen_add2_tl(cpu_cc_N, cpu_cc_C, src1, z, cin, z); - tcg_gen_add2_tl(cpu_cc_N, cpu_cc_C, cpu_cc_N, cpu_cc_C, src2, z); + tcg_gen_addcio_tl(cpu_cc_N, cpu_cc_C, src1, src2, cin); } else { tcg_gen_add2_tl(cpu_cc_N, cpu_cc_C, src1, z, src2, z); } From 867878c112a0c8bbf7590a948ea291f1f1d61209 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 02:08:55 -0800 Subject: [PATCH 0490/2760] target/tricore: Use tcg_gen_addcio_i32 for gen_addc_CC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/tricore/translate.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/target/tricore/translate.c b/target/tricore/translate.c index ede0c92c1e..ba36c9fcc8 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -1346,15 +1346,11 @@ static inline void gen_addi_CC(TCGv ret, TCGv r1, int32_t con) static inline void gen_addc_CC(TCGv ret, TCGv r1, TCGv r2) { - TCGv carry = tcg_temp_new_i32(); - TCGv t0 = tcg_temp_new_i32(); + TCGv t0 = tcg_temp_new_i32(); TCGv result = tcg_temp_new_i32(); - tcg_gen_movi_tl(t0, 0); - tcg_gen_setcondi_tl(TCG_COND_NE, carry, cpu_PSW_C, 0); /* Addition, carry and set C/V/SV bits */ - tcg_gen_add2_i32(result, cpu_PSW_C, r1, t0, carry, t0); - tcg_gen_add2_i32(result, cpu_PSW_C, result, cpu_PSW_C, r2, t0); + tcg_gen_addcio_i32(result, cpu_PSW_C, r1, r2, cpu_PSW_C); /* calc V bit */ tcg_gen_xor_tl(cpu_PSW_V, result, r1); tcg_gen_xor_tl(t0, r1, r2); From 75351891b85f5fe4bd3c1e281d0d9d2dae097e6a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 Jan 2025 02:41:58 -0800 Subject: [PATCH 0491/2760] tcg/aarch64: Implement add/sub carry opcodes Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target-con-set.h | 3 +- tcg/aarch64/tcg-target-has.h | 8 +- tcg/aarch64/tcg-target.c.inc | 227 ++++++++++++++++++++----------- 3 files changed, 150 insertions(+), 88 deletions(-) diff --git a/tcg/aarch64/tcg-target-con-set.h b/tcg/aarch64/tcg-target-con-set.h index 2eda499cd3..d0622e65fb 100644 --- a/tcg/aarch64/tcg-target-con-set.h +++ b/tcg/aarch64/tcg-target-con-set.h @@ -24,6 +24,8 @@ C_O1_I2(r, r, rAL) C_O1_I2(r, r, rC) C_O1_I2(r, r, ri) C_O1_I2(r, r, rL) +C_O1_I2(r, rZ, rA) +C_O1_I2(r, rz, rMZ) C_O1_I2(r, rz, rz) C_O1_I2(r, rZ, rZ) C_O1_I2(w, 0, w) @@ -34,4 +36,3 @@ C_O1_I2(w, w, wZ) C_O1_I3(w, w, w, w) C_O1_I4(r, r, rC, rz, rz) C_O2_I1(r, r, r) -C_O2_I4(r, r, rz, rz, rA, rMZ) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 011a91c263..695effd77c 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -13,13 +13,13 @@ #define have_lse2 (cpuinfo & CPUINFO_LSE2) /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 /* * Without FEAT_LSE2, we must use LDXP+STXP to implement atomic 128-bit load, diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 87f8c98ed7..75cf490fd2 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -508,7 +508,9 @@ typedef enum { /* Add/subtract with carry instructions. */ I3503_ADC = 0x1a000000, + I3503_ADCS = 0x3a000000, I3503_SBC = 0x5a000000, + I3503_SBCS = 0x7a000000, /* Conditional select instructions. */ I3506_CSEL = 0x1a800000, @@ -1573,56 +1575,6 @@ static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rn) tcg_out_mov(s, TCG_TYPE_I32, rd, rn); } -static void tcg_out_addsub2(TCGContext *s, TCGType ext, TCGReg rl, - TCGReg rh, TCGReg al, TCGReg ah, - tcg_target_long bl, tcg_target_long bh, - bool const_bl, bool const_bh, bool sub) -{ - TCGReg orig_rl = rl; - AArch64Insn insn; - - if (rl == ah || (!const_bh && rl == bh)) { - rl = TCG_REG_TMP0; - } - - if (const_bl) { - if (bl < 0) { - bl = -bl; - insn = sub ? I3401_ADDSI : I3401_SUBSI; - } else { - insn = sub ? I3401_SUBSI : I3401_ADDSI; - } - - if (unlikely(al == TCG_REG_XZR)) { - /* ??? We want to allow al to be zero for the benefit of - negation via subtraction. However, that leaves open the - possibility of adding 0+const in the low part, and the - immediate add instructions encode XSP not XZR. Don't try - anything more elaborate here than loading another zero. */ - al = TCG_REG_TMP0; - tcg_out_movi(s, ext, al, 0); - } - tcg_out_insn_3401(s, insn, ext, rl, al, bl); - } else { - tcg_out_insn_3502(s, sub ? I3502_SUBS : I3502_ADDS, ext, rl, al, bl); - } - - insn = I3503_ADC; - if (const_bh) { - /* Note that the only two constants we support are 0 and -1, and - that SBC = rn + ~rm + c, so adc -1 is sbc 0, and vice-versa. */ - if ((bh != 0) ^ sub) { - insn = I3503_SBC; - } - bh = TCG_REG_XZR; - } else if (sub) { - insn = I3503_SBC; - } - tcg_out_insn_3503(s, insn, ext, rh, ah, bh); - - tcg_out_mov(s, ext, orig_rl, rl); -} - static inline void tcg_out_mb(TCGContext *s, TCGArg a0) { static const uint32_t sync[] = { @@ -2078,21 +2030,81 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_addco(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3502, ADDS, type, a0, a1, a2); +} + +static void tgen_addco_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 >= 0) { + tcg_out_insn(s, 3401, ADDSI, type, a0, a1, a2); + } else { + tcg_out_insn(s, 3401, SUBSI, type, a0, a1, -a2); + } +} + static const TCGOutOpBinary outop_addco = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, r, rA), + .out_rrr = tgen_addco, + .out_rri = tgen_addco_imm, }; +static void tgen_addci_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3503, ADC, type, a0, a1, a2); +} + +static void tgen_addci_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* + * Note that the only two constants we support are 0 and -1, and + * that SBC = rn + ~rm + c, so adc -1 is sbc 0, and vice-versa. + */ + if (a2) { + tcg_out_insn(s, 3503, SBC, type, a0, a1, TCG_REG_XZR); + } else { + tcg_out_insn(s, 3503, ADC, type, a0, a1, TCG_REG_XZR); + } +} + static const TCGOutOpAddSubCarry outop_addci = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, rz, rMZ), + .out_rrr = tgen_addci_rrr, + .out_rri = tgen_addci_rri, }; +static void tgen_addcio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3503, ADCS, type, a0, a1, a2); +} + +static void tgen_addcio_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + /* Use SBCS w/0 for ADCS w/-1 -- see above. */ + if (a2) { + tcg_out_insn(s, 3503, SBCS, type, a0, a1, TCG_REG_XZR); + } else { + tcg_out_insn(s, 3503, ADCS, type, a0, a1, TCG_REG_XZR); + } +} + static const TCGOutOpBinary outop_addcio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, rz, rMZ), + .out_rrr = tgen_addcio, + .out_rri = tgen_addcio_imm, }; static void tcg_out_set_carry(TCGContext *s) { - g_assert_not_reached(); + tcg_out_insn(s, 3502, SUBS, TCG_TYPE_I32, + TCG_REG_XZR, TCG_REG_XZR, TCG_REG_XZR); } static void tgen_and(TCGContext *s, TCGType type, @@ -2438,21 +2450,95 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static void tgen_subbo_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3502, SUBS, type, a0, a1, a2); +} + +static void tgen_subbo_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 >= 0) { + tcg_out_insn(s, 3401, SUBSI, type, a0, a1, a2); + } else { + tcg_out_insn(s, 3401, ADDSI, type, a0, a1, -a2); + } +} + +static void tgen_subbo_rir(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tgen_subbo_rrr(s, type, a0, TCG_REG_XZR, a2); +} + +static void tgen_subbo_rii(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2) +{ + if (a2 == 0) { + tgen_subbo_rrr(s, type, a0, TCG_REG_XZR, TCG_REG_XZR); + return; + } + + /* + * We want to allow a1 to be zero for the benefit of negation via + * subtraction. However, that leaves open the possibility of + * adding 0 +/- const, and the immediate add/sub instructions + * encode XSP not XZR. Since we have 0 - non-zero, borrow is + * always set. + */ + tcg_out_movi(s, type, a0, -a2); + tcg_out_set_borrow(s); +} + static const TCGOutOpAddSubCarry outop_subbo = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, rZ, rA), + .out_rrr = tgen_subbo_rrr, + .out_rri = tgen_subbo_rri, + .out_rir = tgen_subbo_rir, + .out_rii = tgen_subbo_rii, }; +static void tgen_subbi_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3503, SBC, type, a0, a1, a2); +} + +static void tgen_subbi_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_addci_rri(s, type, a0, a1, ~a2); +} + static const TCGOutOpAddSubCarry outop_subbi = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, rz, rMZ), + .out_rrr = tgen_subbi_rrr, + .out_rri = tgen_subbi_rri, }; +static void tgen_subbio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_insn(s, 3503, SBCS, type, a0, a1, a2); +} + +static void tgen_subbio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_addcio_imm(s, type, a0, a1, ~a2); +} + static const TCGOutOpAddSubCarry outop_subbio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, rz, rMZ), + .out_rrr = tgen_subbio_rrr, + .out_rri = tgen_subbio_rri, }; static void tcg_out_set_borrow(TCGContext *s) { - g_assert_not_reached(); + tcg_out_insn(s, 3502, ADDS, TCG_TYPE_I32, + TCG_REG_XZR, TCG_REG_XZR, TCG_REG_XZR); } static void tgen_xor(TCGContext *s, TCGType type, @@ -2759,25 +2845,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], false); break; - case INDEX_op_add2_i32: - tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, a2, args[3], - (int32_t)args[4], args[5], const_args[4], - const_args[5], false); - break; - case INDEX_op_add2_i64: - tcg_out_addsub2(s, TCG_TYPE_I64, a0, a1, a2, args[3], args[4], - args[5], const_args[4], const_args[5], false); - break; - case INDEX_op_sub2_i32: - tcg_out_addsub2(s, TCG_TYPE_I32, a0, a1, a2, args[3], - (int32_t)args[4], args[5], const_args[4], - const_args[5], true); - break; - case INDEX_op_sub2_i64: - tcg_out_addsub2(s, TCG_TYPE_I64, a0, a1, a2, args[3], args[4], - args[5], const_args[4], const_args[5], true); - break; - case INDEX_op_mb: tcg_out_mb(s, a0); break; @@ -3271,12 +3338,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i128: return C_O0_I3(rz, rz, r); - case INDEX_op_add2_i32: - case INDEX_op_add2_i64: - case INDEX_op_sub2_i32: - case INDEX_op_sub2_i64: - return C_O2_I4(r, r, rz, rz, rA, rMZ); - case INDEX_op_add_vec: case INDEX_op_sub_vec: case INDEX_op_mul_vec: From b15c0d11a28e6be7156b6920a7a5b2179112b1fa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 15 Jan 2025 23:35:53 +0000 Subject: [PATCH 0492/2760] tcg/arm: Implement add/sub carry opcodes Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/arm/tcg-target-con-set.h | 4 +- tcg/arm/tcg-target-has.h | 4 +- tcg/arm/tcg-target.c.inc | 212 ++++++++++++++++++++++++++--------- 3 files changed, 161 insertions(+), 59 deletions(-) diff --git a/tcg/arm/tcg-target-con-set.h b/tcg/arm/tcg-target-con-set.h index f46a8444fb..16b1193228 100644 --- a/tcg/arm/tcg-target-con-set.h +++ b/tcg/arm/tcg-target-con-set.h @@ -31,6 +31,8 @@ C_O1_I2(r, r, rIK) C_O1_I2(r, r, rIN) C_O1_I2(r, r, ri) C_O1_I2(r, rI, r) +C_O1_I2(r, rI, rIK) +C_O1_I2(r, rI, rIN) C_O1_I2(r, rZ, rZ) C_O1_I2(w, 0, w) C_O1_I2(w, w, w) @@ -43,5 +45,3 @@ C_O1_I4(r, r, rIN, rIK, 0) C_O2_I1(e, p, q) C_O2_I2(e, p, q, q) C_O2_I2(r, r, r, r) -C_O2_I4(r, r, r, r, rIN, rIK) -C_O2_I4(r, r, rI, rI, rIN, rIK) diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 3973df1f12..f4bd15c68a 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -24,8 +24,8 @@ extern bool use_neon_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index aa0397520d..3c9042ebfa 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -178,6 +178,8 @@ typedef enum { INSN_DMB_ISH = 0xf57ff05b, INSN_DMB_MCR = 0xee070fba, + INSN_MSRI_CPSR = 0x0360f000, + /* Architected nop introduced in v6k. */ /* ??? This is an MSR (imm) 0,0,0 insn. Anyone know if this also Just So Happened to do nothing on pre-v6k so that we @@ -1826,21 +1828,74 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_addco(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_ADD | TO_CPSR, + a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_addco_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IN(s, COND_AL, ARITH_ADD | TO_CPSR, ARITH_SUB | TO_CPSR, + a0, a1, a2); +} + static const TCGOutOpBinary outop_addco = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, r, rIN), + .out_rrr = tgen_addco, + .out_rri = tgen_addco_imm, }; +static void tgen_addci(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_ADC, a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_addci_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IK(s, COND_AL, ARITH_ADC, ARITH_SBC, a0, a1, a2); +} + static const TCGOutOpAddSubCarry outop_addci = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, r, rIK), + .out_rrr = tgen_addci, + .out_rri = tgen_addci_imm, }; +static void tgen_addcio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_ADC | TO_CPSR, + a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_addcio_imm(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IK(s, COND_AL, ARITH_ADC | TO_CPSR, ARITH_SBC | TO_CPSR, + a0, a1, a2); +} + static const TCGOutOpBinary outop_addcio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, r, rIK), + .out_rrr = tgen_addcio, + .out_rri = tgen_addcio_imm, }; +/* Set C to @c; NZVQ all set to 0. */ +static void tcg_out_movi_apsr_c(TCGContext *s, bool c) +{ + int imm12 = encode_imm_nofail(c << 29); + tcg_out32(s, (COND_AL << 28) | INSN_MSRI_CPSR | 0x80000 | imm12); +} + static void tcg_out_set_carry(TCGContext *s) { - g_assert_not_reached(); + tcg_out_movi_apsr_c(s, 1); } static void tgen_and(TCGContext *s, TCGType type, @@ -2152,21 +2207,115 @@ static const TCGOutOpSubtract outop_sub = { .out_rir = tgen_subfi, }; +static void tgen_subbo_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_SUB | TO_CPSR, + a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_subbo_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IN(s, COND_AL, ARITH_SUB | TO_CPSR, ARITH_ADD | TO_CPSR, + a0, a1, a2); +} + +static void tgen_subbo_rir(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_RSB | TO_CPSR, + a0, a2, encode_imm_nofail(a1)); +} + +static void tgen_subbo_rii(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2) +{ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, a2); + tgen_subbo_rir(s, TCG_TYPE_I32, a0, a1, TCG_REG_TMP); +} + static const TCGOutOpAddSubCarry outop_subbo = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, rI, rIN), + .out_rrr = tgen_subbo_rrr, + .out_rri = tgen_subbo_rri, + .out_rir = tgen_subbo_rir, + .out_rii = tgen_subbo_rii, }; +static void tgen_subbi_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_SBC, + a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_subbi_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IK(s, COND_AL, ARITH_SBC, ARITH_ADC, a0, a1, a2); +} + +static void tgen_subbi_rir(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_RSC, a0, a2, encode_imm_nofail(a1)); +} + +static void tgen_subbi_rii(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2) +{ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, a2); + tgen_subbi_rir(s, TCG_TYPE_I32, a0, a1, TCG_REG_TMP); +} + static const TCGOutOpAddSubCarry outop_subbi = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, rI, rIK), + .out_rrr = tgen_subbi_rrr, + .out_rri = tgen_subbi_rri, + .out_rir = tgen_subbi_rir, + .out_rii = tgen_subbi_rii, }; +static void tgen_subbio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_dat_reg(s, COND_AL, ARITH_SBC | TO_CPSR, + a0, a1, a2, SHIFT_IMM_LSL(0)); +} + +static void tgen_subbio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_dat_IK(s, COND_AL, ARITH_SBC | TO_CPSR, ARITH_ADC | TO_CPSR, + a0, a1, a2); +} + +static void tgen_subbio_rir(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tcg_out_dat_imm(s, COND_AL, ARITH_RSC | TO_CPSR, + a0, a2, encode_imm_nofail(a1)); +} + +static void tgen_subbio_rii(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2) +{ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, a2); + tgen_subbio_rir(s, TCG_TYPE_I32, a0, a1, TCG_REG_TMP); +} + static const TCGOutOpAddSubCarry outop_subbio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, rI, rIK), + .out_rrr = tgen_subbio_rrr, + .out_rri = tgen_subbio_rri, + .out_rir = tgen_subbio_rir, + .out_rii = tgen_subbio_rii, }; static void tcg_out_set_borrow(TCGContext *s) { - g_assert_not_reached(); + tcg_out_movi_apsr_c(s, 0); /* borrow = !carry */ } static void tgen_xor(TCGContext *s, TCGType type, @@ -2369,8 +2518,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - TCGArg a0, a1, a2, a3, a4, a5; - switch (opc) { case INDEX_op_goto_ptr: tcg_out_b_reg(s, COND_AL, args[0]); @@ -2404,47 +2551,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st32(s, COND_AL, args[0], args[1], args[2]); break; - case INDEX_op_add2_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - a3 = args[3], a4 = args[4], a5 = args[5]; - if (a0 == a3 || (a0 == a5 && !const_args[5])) { - a0 = TCG_REG_TMP; - } - tcg_out_dat_rIN(s, COND_AL, ARITH_ADD | TO_CPSR, ARITH_SUB | TO_CPSR, - a0, a2, a4, const_args[4]); - tcg_out_dat_rIK(s, COND_AL, ARITH_ADC, ARITH_SBC, - a1, a3, a5, const_args[5]); - tcg_out_mov_reg(s, COND_AL, args[0], a0); - break; - case INDEX_op_sub2_i32: - a0 = args[0], a1 = args[1], a2 = args[2]; - a3 = args[3], a4 = args[4], a5 = args[5]; - if ((a0 == a3 && !const_args[3]) || (a0 == a5 && !const_args[5])) { - a0 = TCG_REG_TMP; - } - if (const_args[2]) { - if (const_args[4]) { - tcg_out_movi32(s, COND_AL, a0, a4); - a4 = a0; - } - tcg_out_dat_rI(s, COND_AL, ARITH_RSB | TO_CPSR, a0, a4, a2, 1); - } else { - tcg_out_dat_rIN(s, COND_AL, ARITH_SUB | TO_CPSR, - ARITH_ADD | TO_CPSR, a0, a2, a4, const_args[4]); - } - if (const_args[3]) { - if (const_args[5]) { - tcg_out_movi32(s, COND_AL, a1, a5); - a5 = a1; - } - tcg_out_dat_rI(s, COND_AL, ARITH_RSC, a1, a5, a3, 1); - } else { - tcg_out_dat_rIK(s, COND_AL, ARITH_SBC, ARITH_ADC, - a1, a3, a5, const_args[5]); - } - tcg_out_mov_reg(s, COND_AL, args[0], a0); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; @@ -2490,10 +2596,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i32: return C_O0_I2(r, r); - case INDEX_op_add2_i32: - return C_O2_I4(r, r, r, r, rIN, rIK); - case INDEX_op_sub2_i32: - return C_O2_I4(r, r, rI, rI, rIN, rIK); case INDEX_op_qemu_ld_i32: return C_O1_I1(r, q); case INDEX_op_qemu_ld_i64: From 2329da9605b0f1b7bd59f58afb5ab2154d0af137 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 00:38:13 +0000 Subject: [PATCH 0493/2760] tcg/ppc: Implement add/sub carry opcodes Tested-by: Nicholas Piggin Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/ppc/tcg-target-con-set.h | 5 +- tcg/ppc/tcg-target-con-str.h | 1 + tcg/ppc/tcg-target-has.h | 11 +- tcg/ppc/tcg-target.c.inc | 225 ++++++++++++++++++++++------------- 4 files changed, 153 insertions(+), 89 deletions(-) diff --git a/tcg/ppc/tcg-target-con-set.h b/tcg/ppc/tcg-target-con-set.h index 14cd217287..da7a383bff 100644 --- a/tcg/ppc/tcg-target-con-set.h +++ b/tcg/ppc/tcg-target-con-set.h @@ -29,7 +29,10 @@ C_O1_I2(r, r, rC) C_O1_I2(r, r, rI) C_O1_I2(r, r, rT) C_O1_I2(r, r, rU) +C_O1_I2(r, r, rZM) C_O1_I2(r, r, rZW) +C_O1_I2(r, rI, rN) +C_O1_I2(r, rZM, rZM) C_O1_I2(v, v, v) C_O1_I3(v, v, v, v) C_O1_I4(v, v, v, vZM, v) @@ -38,5 +41,3 @@ C_O1_I4(r, r, r, rU, rC) C_O2_I1(r, r, r) C_N1O1_I1(o, m, r) C_O2_I2(r, r, r, r) -C_O2_I4(r, r, rI, rZM, r, r) -C_O2_I4(r, r, r, r, rI, rZM) diff --git a/tcg/ppc/tcg-target-con-str.h b/tcg/ppc/tcg-target-con-str.h index 16b687216e..faf92da47f 100644 --- a/tcg/ppc/tcg-target-con-str.h +++ b/tcg/ppc/tcg-target-con-str.h @@ -19,6 +19,7 @@ REGS('v', ALL_VECTOR_REGS) CONST('C', TCG_CT_CONST_CMP) CONST('I', TCG_CT_CONST_S16) CONST('M', TCG_CT_CONST_MONE) +CONST('N', TCG_CT_CONST_N16) CONST('T', TCG_CT_CONST_S32) CONST('U', TCG_CT_CONST_U32) CONST('W', TCG_CT_CONST_WSZ) diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 8d832ce99c..4dda668706 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -18,16 +18,13 @@ /* optional instructions */ #define TCG_TARGET_HAS_qemu_st8_i32 0 - -#if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_add2_i32 0 #define TCG_TARGET_HAS_sub2_i32 0 + +#if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 -#else -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 #endif #define TCG_TARGET_HAS_qemu_ldst_i128 \ diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 5b04655f3b..91df9610ec 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -89,14 +89,15 @@ /* Shorthand for size of a register. */ #define SZR (TCG_TARGET_REG_BITS / 8) -#define TCG_CT_CONST_S16 0x100 -#define TCG_CT_CONST_U16 0x200 -#define TCG_CT_CONST_S32 0x400 -#define TCG_CT_CONST_U32 0x800 -#define TCG_CT_CONST_ZERO 0x1000 -#define TCG_CT_CONST_MONE 0x2000 -#define TCG_CT_CONST_WSZ 0x4000 -#define TCG_CT_CONST_CMP 0x8000 +#define TCG_CT_CONST_S16 0x00100 +#define TCG_CT_CONST_U16 0x00200 +#define TCG_CT_CONST_N16 0x00400 +#define TCG_CT_CONST_S32 0x00800 +#define TCG_CT_CONST_U32 0x01000 +#define TCG_CT_CONST_ZERO 0x02000 +#define TCG_CT_CONST_MONE 0x04000 +#define TCG_CT_CONST_WSZ 0x08000 +#define TCG_CT_CONST_CMP 0x10000 #define ALL_GENERAL_REGS 0xffffffffu #define ALL_VECTOR_REGS 0xffffffff00000000ull @@ -342,6 +343,9 @@ static bool tcg_target_const_match(int64_t sval, int ct, if ((ct & TCG_CT_CONST_U16) && uval == (uint16_t)uval) { return 1; } + if ((ct & TCG_CT_CONST_N16) && -sval == (int16_t)-sval) { + return 1; + } if ((ct & TCG_CT_CONST_S32) && sval == (int32_t)sval) { return 1; } @@ -2863,21 +2867,69 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_addco_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, ADDC | TAB(a0, a1, a2)); +} + +static void tgen_addco_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out32(s, ADDIC | TAI(a0, a1, a2)); +} + +static TCGConstraintSetIndex cset_addco(TCGType type, unsigned flags) +{ + /* + * Note that the CA bit is defined based on the word size of the + * environment. So in 64-bit mode it's always carry-out of bit 63. + * The fallback code using deposit works just as well for TCG_TYPE_I32. + */ + return type == TCG_TYPE_REG ? C_O1_I2(r, r, rI) : C_NotImplemented; +} + static const TCGOutOpBinary outop_addco = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addco, + .out_rrr = tgen_addco_rrr, + .out_rri = tgen_addco_rri, }; -static const TCGOutOpAddSubCarry outop_addci = { - .base.static_constraint = C_NotImplemented, -}; +static void tgen_addcio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, ADDE | TAB(a0, a1, a2)); +} + +static void tgen_addcio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out32(s, (a2 ? ADDME : ADDZE) | RT(a0) | RA(a1)); +} + +static TCGConstraintSetIndex cset_addcio(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_REG ? C_O1_I2(r, r, rZM) : C_NotImplemented; +} static const TCGOutOpBinary outop_addcio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addcio, + .out_rrr = tgen_addcio_rrr, + .out_rri = tgen_addcio_rri, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addcio, + .out_rrr = tgen_addcio_rrr, + .out_rri = tgen_addcio_rri, }; static void tcg_out_set_carry(TCGContext *s) { - g_assert_not_reached(); + tcg_out32(s, SUBFC | TAB(TCG_REG_R0, TCG_REG_R0, TCG_REG_R0)); } static void tgen_and(TCGContext *s, TCGType type, @@ -3284,21 +3336,94 @@ static const TCGOutOpSubtract outop_sub = { .out_rir = tgen_subfi, }; +static void tgen_subbo_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, SUBFC | TAB(a0, a2, a1)); +} + +static void tgen_subbo_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (a2 == 0) { + tcg_out_movi(s, type, TCG_REG_R0, 0); + tgen_subbo_rrr(s, type, a0, a1, TCG_REG_R0); + } else { + tgen_addco_rri(s, type, a0, a1, -a2); + } +} + +/* The underlying insn for subfi is subfic. */ +#define tgen_subbo_rir tgen_subfi + +static void tgen_subbo_rii(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2) +{ + tcg_out_movi(s, type, TCG_REG_R0, a2); + tgen_subbo_rir(s, type, a0, a1, TCG_REG_R0); +} + +static TCGConstraintSetIndex cset_subbo(TCGType type, unsigned flags) +{ + /* Recall that the CA bit is defined based on the host word size. */ + return type == TCG_TYPE_REG ? C_O1_I2(r, rI, rN) : C_NotImplemented; +} + static const TCGOutOpAddSubCarry outop_subbo = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_subbo, + .out_rrr = tgen_subbo_rrr, + .out_rri = tgen_subbo_rri, + .out_rir = tgen_subbo_rir, + .out_rii = tgen_subbo_rii, }; -static const TCGOutOpAddSubCarry outop_subbi = { - .base.static_constraint = C_NotImplemented, -}; +static void tgen_subbio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out32(s, SUBFE | TAB(a0, a2, a1)); +} + +static void tgen_subbio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tgen_addcio_rri(s, type, a0, a1, ~a2); +} + +static void tgen_subbio_rir(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, TCGReg a2) +{ + tcg_debug_assert(a1 == 0 || a1 == -1); + tcg_out32(s, (a1 ? SUBFME : SUBFZE) | RT(a0) | RA(a2)); +} + +static void tgen_subbio_rii(TCGContext *s, TCGType type, + TCGReg a0, tcg_target_long a1, tcg_target_long a2) +{ + tcg_out_movi(s, type, TCG_REG_R0, a2); + tgen_subbio_rir(s, type, a0, a1, TCG_REG_R0); +} + +static TCGConstraintSetIndex cset_subbio(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_REG ? C_O1_I2(r, rZM, rZM) : C_NotImplemented; +} static const TCGOutOpAddSubCarry outop_subbio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_subbio, + .out_rrr = tgen_subbio_rrr, + .out_rri = tgen_subbio_rri, + .out_rir = tgen_subbio_rir, + .out_rii = tgen_subbio_rii, }; +#define outop_subbi outop_subbio + static void tcg_out_set_borrow(TCGContext *s) { - g_assert_not_reached(); + /* borrow = !carry */ + tcg_out32(s, ADDIC | TAI(TCG_REG_R0, TCG_REG_R0, 0)); } static void tgen_xor(TCGContext *s, TCGType type, @@ -3538,8 +3663,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - TCGArg a0, a1; - switch (opc) { case INDEX_op_goto_ptr: tcg_out32(s, MTSPR | RS(args[0]) | CTR); @@ -3635,57 +3758,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; -#if TCG_TARGET_REG_BITS == 64 - case INDEX_op_add2_i64: -#else - case INDEX_op_add2_i32: -#endif - /* Note that the CA bit is defined based on the word size of the - environment. So in 64-bit mode it's always carry-out of bit 63. - The fallback code using deposit works just as well for 32-bit. */ - a0 = args[0], a1 = args[1]; - if (a0 == args[3] || (!const_args[5] && a0 == args[5])) { - a0 = TCG_REG_R0; - } - if (const_args[4]) { - tcg_out32(s, ADDIC | TAI(a0, args[2], args[4])); - } else { - tcg_out32(s, ADDC | TAB(a0, args[2], args[4])); - } - if (const_args[5]) { - tcg_out32(s, (args[5] ? ADDME : ADDZE) | RT(a1) | RA(args[3])); - } else { - tcg_out32(s, ADDE | TAB(a1, args[3], args[5])); - } - if (a0 != args[0]) { - tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); - } - break; - -#if TCG_TARGET_REG_BITS == 64 - case INDEX_op_sub2_i64: -#else - case INDEX_op_sub2_i32: -#endif - a0 = args[0], a1 = args[1]; - if (a0 == args[5] || (!const_args[3] && a0 == args[3])) { - a0 = TCG_REG_R0; - } - if (const_args[2]) { - tcg_out32(s, SUBFIC | TAI(a0, args[4], args[2])); - } else { - tcg_out32(s, SUBFC | TAB(a0, args[4], args[2])); - } - if (const_args[3]) { - tcg_out32(s, (args[3] ? SUBFME : SUBFZE) | RT(a1) | RA(args[5])); - } else { - tcg_out32(s, SUBFE | TAB(a1, args[5], args[3])); - } - if (a0 != args[0]) { - tcg_out_mov(s, TCG_TYPE_REG, args[0], a0); - } - break; - case INDEX_op_mb: tcg_out_mb(s, args[0]); break; @@ -4331,13 +4403,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_add2_i64: - case INDEX_op_add2_i32: - return C_O2_I4(r, r, r, r, rI, rZM); - case INDEX_op_sub2_i64: - case INDEX_op_sub2_i32: - return C_O2_I4(r, r, rI, rZM, r, r); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); case INDEX_op_qemu_ld_i64: From 1b6e25e300e78cf8d8581245c2f8339c5b423d30 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 13:26:43 -0800 Subject: [PATCH 0494/2760] tcg/s390x: Honor carry_live in tcg_out_movi Do not clobber flags if they're live. Required in order to perform register allocation on add/sub carry opcodes. LA and AGHI are the same size, so use LA unconditionally. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index a30afb455e..e262876614 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -951,25 +951,32 @@ static void tcg_out_movi(TCGContext *s, TCGType type, if (pc_off == (int32_t)pc_off) { tcg_out_insn(s, RIL, LARL, ret, pc_off); if (sval & 1) { - tcg_out_insn(s, RI, AGHI, ret, 1); + tcg_out_insn(s, RX, LA, ret, ret, TCG_REG_NONE, 1); } return; } - /* Otherwise, load it by parts. */ - i = is_const_p16((uint32_t)uval); - if (i >= 0) { - tcg_out_insn_RI(s, li_insns[i], ret, uval >> (i * 16)); - } else { - tcg_out_insn(s, RIL, LLILF, ret, uval); - } - uval >>= 32; - i = is_const_p16(uval); - if (i >= 0) { - tcg_out_insn_RI(s, oi_insns[i + 2], ret, uval >> (i * 16)); - } else { - tcg_out_insn(s, RIL, OIHF, ret, uval); + if (!s->carry_live) { + /* Load by parts, at most 2 instructions. */ + i = is_const_p16((uint32_t)uval); + if (i >= 0) { + tcg_out_insn_RI(s, li_insns[i], ret, uval >> (i * 16)); + } else { + tcg_out_insn(s, RIL, LLILF, ret, uval); + } + uval >>= 32; + i = is_const_p16(uval); + if (i >= 0) { + tcg_out_insn_RI(s, oi_insns[i + 2], ret, uval >> (i * 16)); + } else { + tcg_out_insn(s, RIL, OIHF, ret, uval); + } + return; } + + /* Otherwise, stuff it in the constant pool. */ + tcg_out_insn(s, RIL, LGRL, ret, 0); + new_pool_label(s, sval, R_390_PC32DBL, s->code_ptr - 2, 2); } /* Emit a load/store type instruction. Inputs are: From 19b9fc2a39c733f585388918e4e093f08e2c33eb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 18 Jan 2025 14:01:12 -0800 Subject: [PATCH 0495/2760] tcg/s390x: Add TCG_CT_CONST_N32 We were using S32 | U32 for add2/sub2. But the ALGFI and SLGFI insns that implement this both have uint32_t immediates. This makes the composite range balanced and enables use of -0xffffffff ... -0x80000001. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target-con-set.h | 2 +- tcg/s390x/tcg-target-con-str.h | 1 + tcg/s390x/tcg-target.c.inc | 8 ++++++-- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/tcg/s390x/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h index 78f06e3e52..f5d3878070 100644 --- a/tcg/s390x/tcg-target-con-set.h +++ b/tcg/s390x/tcg-target-con-set.h @@ -44,4 +44,4 @@ C_O2_I2(o, m, 0, r) C_O2_I2(o, m, r, r) C_O2_I3(o, m, 0, 1, r) C_N1_O1_I4(r, r, 0, 1, ri, r) -C_N1_O1_I4(r, r, 0, 1, rJU, r) +C_N1_O1_I4(r, r, 0, 1, rUV, r) diff --git a/tcg/s390x/tcg-target-con-str.h b/tcg/s390x/tcg-target-con-str.h index 3e574e0662..636a38a168 100644 --- a/tcg/s390x/tcg-target-con-str.h +++ b/tcg/s390x/tcg-target-con-str.h @@ -24,4 +24,5 @@ CONST('M', TCG_CT_CONST_M1) CONST('N', TCG_CT_CONST_INV) CONST('R', TCG_CT_CONST_INVRISBG) CONST('U', TCG_CT_CONST_U32) +CONST('V', TCG_CT_CONST_N32) CONST('Z', TCG_CT_CONST_ZERO) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index e262876614..9b28083945 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -43,6 +43,7 @@ #define TCG_CT_CONST_INVRISBG (1 << 14) #define TCG_CT_CONST_CMP (1 << 15) #define TCG_CT_CONST_M1 (1 << 16) +#define TCG_CT_CONST_N32 (1 << 17) #define ALL_GENERAL_REGS MAKE_64BIT_MASK(0, 16) #define ALL_VECTOR_REGS MAKE_64BIT_MASK(32, 32) @@ -613,7 +614,10 @@ static bool tcg_target_const_match(int64_t val, int ct, if ((ct & TCG_CT_CONST_S32) && val == (int32_t)val) { return true; } - if ((ct & TCG_CT_CONST_U32) && val == (uint32_t)val) { + if ((ct & TCG_CT_CONST_U32) && uval <= UINT32_MAX) { + return true; + } + if ((ct & TCG_CT_CONST_N32) && -uval <= UINT32_MAX) { return true; } if ((ct & TCG_CT_CONST_S16) && val == (int16_t)val) { @@ -3548,7 +3552,7 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_add2_i64: case INDEX_op_sub2_i64: - return C_N1_O1_I4(r, r, 0, 1, rJU, r); + return C_N1_O1_I4(r, r, 0, 1, rUV, r); case INDEX_op_st_vec: return C_O0_I2(v, r); From cda7f93fa2fbf52e0fc70e4078caf8e7f7d3bd6f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 19 Jan 2025 09:31:26 -0800 Subject: [PATCH 0496/2760] tcg/s390x: Implement add/sub carry opcodes Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target-con-set.h | 4 +- tcg/s390x/tcg-target-has.h | 8 +- tcg/s390x/tcg-target.c.inc | 151 +++++++++++++++++++-------------- 3 files changed, 95 insertions(+), 68 deletions(-) diff --git a/tcg/s390x/tcg-target-con-set.h b/tcg/s390x/tcg-target-con-set.h index f5d3878070..f67fd7898e 100644 --- a/tcg/s390x/tcg-target-con-set.h +++ b/tcg/s390x/tcg-target-con-set.h @@ -22,6 +22,7 @@ C_O1_I1(r, r) C_O1_I1(v, r) C_O1_I1(v, v) C_O1_I1(v, vr) +C_O1_I2(r, 0, r) C_O1_I2(r, 0, ri) C_O1_I2(r, 0, rI) C_O1_I2(r, 0, rJ) @@ -32,6 +33,7 @@ C_O1_I2(r, r, rI) C_O1_I2(r, r, rJ) C_O1_I2(r, r, rK) C_O1_I2(r, r, rNKR) +C_O1_I2(r, r, rUV) C_O1_I2(r, rZ, r) C_O1_I2(v, v, r) C_O1_I2(v, v, v) @@ -43,5 +45,3 @@ C_O2_I1(o, m, r) C_O2_I2(o, m, 0, r) C_O2_I2(o, m, r, r) C_O2_I3(o, m, 0, 1, r) -C_N1_O1_I4(r, r, 0, 1, ri, r) -C_N1_O1_I4(r, r, 0, 1, rUV, r) diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 4a2b71995d..17e61130cd 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -29,13 +29,13 @@ extern uint64_t s390_facilities[3]; ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 1 diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 9b28083945..67179de848 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -173,6 +173,8 @@ typedef enum S390Opcode { RRE_SLBGR = 0xb989, RRE_XGR = 0xb982, + RRFa_ALRK = 0xb9fa, + RRFa_ALGRK = 0xb9ea, RRFa_MGRK = 0xb9ec, RRFa_MSRKC = 0xb9fd, RRFa_MSGRKC = 0xb9ed, @@ -2259,21 +2261,60 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_addco_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, ALGRK, a0, a1, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, ALR, a0, a2); + } else { + tcg_out_insn(s, RRFa, ALRK, a0, a1, a2); + } +} + +static void tgen_addco_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, ALFI, a0, a2); + } else if (a2 >= 0) { + tcg_out_insn(s, RIL, ALGFI, a0, a2); + } else { + tcg_out_insn(s, RIL, SLGFI, a0, -a2); + } +} + static const TCGOutOpBinary outop_addco = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, r, rUV), + .out_rrr = tgen_addco_rrr, + .out_rri = tgen_addco_rri, }; -static const TCGOutOpAddSubCarry outop_addci = { - .base.static_constraint = C_NotImplemented, -}; +static void tgen_addcio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRE, ALCR, a0, a2); + } else { + tcg_out_insn(s, RRE, ALCGR, a0, a2); + } +} static const TCGOutOpBinary outop_addcio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, 0, r), + .out_rrr = tgen_addcio, +}; + +static const TCGOutOpAddSubCarry outop_addci = { + .base.static_constraint = C_O1_I2(r, 0, r), + .out_rrr = tgen_addcio, }; static void tcg_out_set_carry(TCGContext *s) { - g_assert_not_reached(); + tcg_out_insn(s, RR, SLR, TCG_REG_R0, TCG_REG_R0); /* cc = 2 */ } static void tgen_and(TCGContext *s, TCGType type, @@ -2794,21 +2835,57 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static void tgen_subbo_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_insn(s, RRFa, SLGRK, a0, a1, a2); + } else if (a0 == a1) { + tcg_out_insn(s, RR, SLR, a0, a2); + } else { + tcg_out_insn(s, RRFa, SLRK, a0, a1, a2); + } +} + +static void tgen_subbo_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_mov(s, type, a0, a1); + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIL, SLFI, a0, a2); + } else if (a2 >= 0) { + tcg_out_insn(s, RIL, SLGFI, a0, a2); + } else { + tcg_out_insn(s, RIL, ALGFI, a0, -a2); + } +} + static const TCGOutOpAddSubCarry outop_subbo = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, r, rUV), + .out_rrr = tgen_subbo_rrr, + .out_rri = tgen_subbo_rri, }; -static const TCGOutOpAddSubCarry outop_subbi = { - .base.static_constraint = C_NotImplemented, -}; +static void tgen_subbio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RRE, SLBR, a0, a2); + } else { + tcg_out_insn(s, RRE, SLBGR, a0, a2); + } +} static const TCGOutOpAddSubCarry outop_subbio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, 0, r), + .out_rrr = tgen_subbio, }; +#define outop_subbi outop_subbio + static void tcg_out_set_borrow(TCGContext *s) { - g_assert_not_reached(); + tcg_out_insn(s, RR, CLR, TCG_REG_R0, TCG_REG_R0); /* cc = 0 */ } static void tgen_xor(TCGContext *s, TCGType type, @@ -2967,23 +3044,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_add2_i32: - if (const_args[4]) { - tcg_out_insn(s, RIL, ALFI, args[0], args[4]); - } else { - tcg_out_insn(s, RR, ALR, args[0], args[4]); - } - tcg_out_insn(s, RRE, ALCR, args[1], args[5]); - break; - case INDEX_op_sub2_i32: - if (const_args[4]) { - tcg_out_insn(s, RIL, SLFI, args[0], args[4]); - } else { - tcg_out_insn(s, RR, SLR, args[0], args[4]); - } - tcg_out_insn(s, RRE, SLBR, args[1], args[5]); - break; - case INDEX_op_br: tgen_branch(s, S390_CC_ALWAYS, arg_label(args[0])); break; @@ -3027,31 +3087,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); break; - case INDEX_op_add2_i64: - if (const_args[4]) { - if ((int64_t)args[4] >= 0) { - tcg_out_insn(s, RIL, ALGFI, args[0], args[4]); - } else { - tcg_out_insn(s, RIL, SLGFI, args[0], -args[4]); - } - } else { - tcg_out_insn(s, RRE, ALGR, args[0], args[4]); - } - tcg_out_insn(s, RRE, ALCGR, args[1], args[5]); - break; - case INDEX_op_sub2_i64: - if (const_args[4]) { - if ((int64_t)args[4] >= 0) { - tcg_out_insn(s, RIL, SLGFI, args[0], args[4]); - } else { - tcg_out_insn(s, RIL, ALGFI, args[0], -args[4]); - } - } else { - tcg_out_insn(s, RRE, SLGR, args[0], args[4]); - } - tcg_out_insn(s, RRE, SLBGR, args[1], args[5]); - break; - case INDEX_op_mb: /* The host memory model is quite strong, we simply need to serialize the instruction stream. */ @@ -3546,14 +3581,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i128: return C_O0_I3(o, m, r); - case INDEX_op_add2_i32: - case INDEX_op_sub2_i32: - return C_N1_O1_I4(r, r, 0, 1, ri, r); - - case INDEX_op_add2_i64: - case INDEX_op_sub2_i64: - return C_N1_O1_I4(r, r, 0, 1, rUV, r); - case INDEX_op_st_vec: return C_O0_I2(v, r); case INDEX_op_ld_vec: From 41dd55c79c0a1b1a4bf573821b81d97e6d4c159a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 19 Jan 2025 10:01:18 -0800 Subject: [PATCH 0497/2760] tcg/s390x: Use ADD LOGICAL WITH SIGNED IMMEDIATE Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/s390x/tcg-target.c.inc | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 67179de848..09c7ca5b44 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -135,6 +135,9 @@ typedef enum S390Opcode { RIEc_CLGIJ = 0xec7d, RIEc_CLIJ = 0xec7f, + RIEd_ALHSIK = 0xecda, + RIEd_ALGHSIK = 0xecdb, + RIEf_RISBG = 0xec55, RIEg_LOCGHI = 0xec46, @@ -682,8 +685,16 @@ static void tcg_out_insn_RI(TCGContext *s, S390Opcode op, TCGReg r1, int i2) tcg_out32(s, (op << 16) | (r1 << 20) | (i2 & 0xffff)); } +static void tcg_out_insn_RIEd(TCGContext *s, S390Opcode op, + TCGReg r1, TCGReg r3, int i2) +{ + tcg_out16(s, (op & 0xff00) | (r1 << 4) | r3); + tcg_out16(s, i2); + tcg_out16(s, op & 0xff); +} + static void tcg_out_insn_RIEg(TCGContext *s, S390Opcode op, TCGReg r1, - int i2, int m3) + int i2, int m3) { tcg_out16(s, (op & 0xff00) | (r1 << 4) | m3); tcg_out32(s, (i2 << 16) | (op & 0xff)); @@ -2276,6 +2287,15 @@ static void tgen_addco_rrr(TCGContext *s, TCGType type, static void tgen_addco_rri(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, tcg_target_long a2) { + if (a2 == (int16_t)a2) { + if (type == TCG_TYPE_I32) { + tcg_out_insn(s, RIEd, ALHSIK, a0, a1, a2); + } else { + tcg_out_insn(s, RIEd, ALGHSIK, a0, a1, a2); + } + return; + } + tcg_out_mov(s, type, a0, a1); if (type == TCG_TYPE_I32) { tcg_out_insn(s, RIL, ALFI, a0, a2); From 809069eaa3d803092adaa06cec770d78db44211d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 20 Jan 2025 16:34:47 -0800 Subject: [PATCH 0498/2760] tcg/sparc64: Hoist tcg_cond_to_bcond lookup out of tcg_out_movcc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass the sparc COND_* value not the tcg TCG_COND_* value. This makes the usage within add2/sub2 clearer. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/sparc64/tcg-target.c.inc | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 12f0dbd23d..3f97261626 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -652,11 +652,10 @@ static void tcg_out_brcond_i32(TCGContext *s, TCGCond cond, TCGReg arg1, tcg_out_nop(s); } -static void tcg_out_movcc(TCGContext *s, TCGCond cond, int cc, TCGReg ret, +static void tcg_out_movcc(TCGContext *s, int scond, int cc, TCGReg ret, int32_t v1, int v1const) { - tcg_out32(s, ARITH_MOVCC | cc | INSN_RD(ret) - | INSN_RS1(tcg_cond_to_bcond[cond]) + tcg_out32(s, ARITH_MOVCC | cc | INSN_RD(ret) | INSN_RS1(scond) | (v1const ? INSN_IMM11(v1) : INSN_RS2(v1))); } @@ -665,7 +664,7 @@ static void tcg_out_movcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, int32_t v1, int v1const) { tcg_out_cmp(s, cond, c1, c2, c2const); - tcg_out_movcc(s, cond, MOVCC_ICC, ret, v1, v1const); + tcg_out_movcc(s, tcg_cond_to_bcond[cond], MOVCC_ICC, ret, v1, v1const); } static void tcg_out_brcond_i64(TCGContext *s, TCGCond cond, TCGReg arg1, @@ -709,7 +708,7 @@ static void tcg_out_movcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, tcg_out_movr(s, rcond, ret, c1, v1, v1const); } else { tcg_out_cmp(s, cond, c1, c2, c2const); - tcg_out_movcc(s, cond, MOVCC_XCC, ret, v1, v1const); + tcg_out_movcc(s, tcg_cond_to_bcond[cond], MOVCC_XCC, ret, v1, v1const); } } @@ -763,7 +762,8 @@ static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, default: tcg_out_cmp(s, cond, c1, c2, c2const); tcg_out_movi_s13(s, ret, 0); - tcg_out_movcc(s, cond, MOVCC_ICC, ret, neg ? -1 : 1, 1); + tcg_out_movcc(s, tcg_cond_to_bcond[cond], + MOVCC_ICC, ret, neg ? -1 : 1, 1); return; } @@ -818,7 +818,8 @@ static void tcg_out_setcond_i64(TCGContext *s, TCGCond cond, TCGReg ret, } else { tcg_out_cmp(s, cond, c1, c2, c2const); tcg_out_movi_s13(s, ret, 0); - tcg_out_movcc(s, cond, MOVCC_XCC, ret, neg ? -1 : 1, 1); + tcg_out_movcc(s, tcg_cond_to_bcond[cond], + MOVCC_XCC, ret, neg ? -1 : 1, 1); } } @@ -956,10 +957,10 @@ static void tcg_out_addsub2_i64(TCGContext *s, TCGReg rl, TCGReg rh, if (rh == ah) { tcg_out_arithi(s, TCG_REG_T2, ah, 1, is_sub ? ARITH_SUB : ARITH_ADD); - tcg_out_movcc(s, TCG_COND_LTU, MOVCC_XCC, rh, TCG_REG_T2, 0); + tcg_out_movcc(s, COND_CS, MOVCC_XCC, rh, TCG_REG_T2, 0); } else { tcg_out_arithi(s, rh, ah, 1, is_sub ? ARITH_SUB : ARITH_ADD); - tcg_out_movcc(s, TCG_COND_GEU, MOVCC_XCC, rh, ah, 0); + tcg_out_movcc(s, COND_CC, MOVCC_XCC, rh, ah, 0); } } else { /* @@ -974,7 +975,7 @@ static void tcg_out_addsub2_i64(TCGContext *s, TCGReg rl, TCGReg rh, is_sub ? ARITH_SUB : ARITH_ADD); } /* ... smoosh T2 back to original BH if carry is clear ... */ - tcg_out_movcc(s, TCG_COND_GEU, MOVCC_XCC, TCG_REG_T2, bh, bhconst); + tcg_out_movcc(s, COND_CC, MOVCC_XCC, TCG_REG_T2, bh, bhconst); /* ... and finally perform the arithmetic with the new operand. */ tcg_out_arith(s, rh, ah, TCG_REG_T2, is_sub ? ARITH_SUB : ARITH_ADD); } From 9dd1ea33b238d8b661b7541ebdb122b7cae6fd19 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 20 Jan 2025 18:48:06 -0800 Subject: [PATCH 0499/2760] tcg/sparc64: Implement add/sub carry opcodes Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/sparc64/tcg-target-con-set.h | 3 +- tcg/sparc64/tcg-target-has.h | 8 +- tcg/sparc64/tcg-target.c.inc | 300 ++++++++++++++++++++----------- 3 files changed, 201 insertions(+), 110 deletions(-) diff --git a/tcg/sparc64/tcg-target-con-set.h b/tcg/sparc64/tcg-target-con-set.h index 8cec396173..1a57adc0e8 100644 --- a/tcg/sparc64/tcg-target-con-set.h +++ b/tcg/sparc64/tcg-target-con-set.h @@ -15,6 +15,7 @@ C_O0_I2(r, rJ) C_O1_I1(r, r) C_O1_I2(r, r, r) C_O1_I2(r, r, rJ) +C_O1_I2(r, rz, rJ) +C_O1_I2(r, rz, rz) C_O1_I4(r, r, rJ, rI, 0) C_O2_I2(r, r, r, r) -C_O2_I4(r, r, rz, rz, rJ, rJ) diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index b8760dd154..caf7679595 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -14,13 +14,13 @@ extern bool use_vis3_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 3f97261626..c2251a6927 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -199,7 +199,9 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define ARITH_SUB (INSN_OP(2) | INSN_OP3(0x04)) #define ARITH_SUBCC (INSN_OP(2) | INSN_OP3(0x14)) #define ARITH_ADDC (INSN_OP(2) | INSN_OP3(0x08)) +#define ARITH_ADDCCC (INSN_OP(2) | INSN_OP3(0x18)) #define ARITH_SUBC (INSN_OP(2) | INSN_OP3(0x0c)) +#define ARITH_SUBCCC (INSN_OP(2) | INSN_OP3(0x1c)) #define ARITH_UMUL (INSN_OP(2) | INSN_OP3(0x0a)) #define ARITH_SMUL (INSN_OP(2) | INSN_OP3(0x0b)) #define ARITH_UDIV (INSN_OP(2) | INSN_OP3(0x0e)) @@ -211,6 +213,7 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define ARITH_MOVR (INSN_OP(2) | INSN_OP3(0x2f)) #define ARITH_ADDXC (INSN_OP(2) | INSN_OP3(0x36) | INSN_OPF(0x11)) +#define ARITH_ADDXCCC (INSN_OP(2) | INSN_OP3(0x36) | INSN_OPF(0x13)) #define ARITH_UMULXHI (INSN_OP(2) | INSN_OP3(0x36) | INSN_OPF(0x16)) #define SHIFT_SLL (INSN_OP(2) | INSN_OP3(0x25)) @@ -223,6 +226,7 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define RDY (INSN_OP(2) | INSN_OP3(0x28) | INSN_RS1(0)) #define WRY (INSN_OP(2) | INSN_OP3(0x30) | INSN_RD(0)) +#define WRCCR (INSN_OP(2) | INSN_OP3(0x30) | INSN_RD(2)) #define JMPL (INSN_OP(2) | INSN_OP3(0x38)) #define RETURN (INSN_OP(2) | INSN_OP3(0x39)) #define SAVE (INSN_OP(2) | INSN_OP3(0x3c)) @@ -366,7 +370,7 @@ static void tcg_out_arithi(TCGContext *s, TCGReg rd, TCGReg rs1, } static void tcg_out_arithc(TCGContext *s, TCGReg rd, TCGReg rs1, - int32_t val2, int val2const, int op) + int32_t val2, int val2const, int op) { tcg_out32(s, op | INSN_RD(rd) | INSN_RS1(rs1) | (val2const ? INSN_IMM13(val2) : INSN_RS2(val2))); @@ -733,7 +737,7 @@ static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, } c1 = TCG_REG_G0, c2const = 0; cond = (cond == TCG_COND_EQ ? TCG_COND_GEU : TCG_COND_LTU); - break; + break; case TCG_COND_TSTEQ: case TCG_COND_TSTNE: @@ -742,7 +746,7 @@ static void tcg_out_setcond_i32(TCGContext *s, TCGCond cond, TCGReg ret, c1 = TCG_REG_G0; c2 = TCG_REG_T1, c2const = 0; cond = (cond == TCG_COND_TSTEQ ? TCG_COND_GEU : TCG_COND_LTU); - break; + break; case TCG_COND_GTU: case TCG_COND_LEU: @@ -915,74 +919,6 @@ static const TCGOutOpMovcond outop_movcond = { .out = tgen_movcond, }; -static void tcg_out_addsub2_i32(TCGContext *s, TCGReg rl, TCGReg rh, - TCGReg al, TCGReg ah, int32_t bl, int blconst, - int32_t bh, int bhconst, int opl, int oph) -{ - TCGReg tmp = TCG_REG_T1; - - /* Note that the low parts are fully consumed before tmp is set. */ - if (rl != ah && (bhconst || rl != bh)) { - tmp = rl; - } - - tcg_out_arithc(s, tmp, al, bl, blconst, opl); - tcg_out_arithc(s, rh, ah, bh, bhconst, oph); - tcg_out_mov(s, TCG_TYPE_I32, rl, tmp); -} - -static void tcg_out_addsub2_i64(TCGContext *s, TCGReg rl, TCGReg rh, - TCGReg al, TCGReg ah, int32_t bl, int blconst, - int32_t bh, int bhconst, bool is_sub) -{ - TCGReg tmp = TCG_REG_T1; - - /* Note that the low parts are fully consumed before tmp is set. */ - if (rl != ah && (bhconst || rl != bh)) { - tmp = rl; - } - - tcg_out_arithc(s, tmp, al, bl, blconst, is_sub ? ARITH_SUBCC : ARITH_ADDCC); - - if (use_vis3_instructions && !is_sub) { - /* Note that ADDXC doesn't accept immediates. */ - if (bhconst && bh != 0) { - tcg_out_movi_s13(s, TCG_REG_T2, bh); - bh = TCG_REG_T2; - } - tcg_out_arith(s, rh, ah, bh, ARITH_ADDXC); - } else if (bh == TCG_REG_G0) { - /* If we have a zero, we can perform the operation in two insns, - with the arithmetic first, and a conditional move into place. */ - if (rh == ah) { - tcg_out_arithi(s, TCG_REG_T2, ah, 1, - is_sub ? ARITH_SUB : ARITH_ADD); - tcg_out_movcc(s, COND_CS, MOVCC_XCC, rh, TCG_REG_T2, 0); - } else { - tcg_out_arithi(s, rh, ah, 1, is_sub ? ARITH_SUB : ARITH_ADD); - tcg_out_movcc(s, COND_CC, MOVCC_XCC, rh, ah, 0); - } - } else { - /* - * Otherwise adjust BH as if there is carry into T2. - * Note that constant BH is constrained to 11 bits for the MOVCC, - * so the adjustment fits 12 bits. - */ - if (bhconst) { - tcg_out_movi_s13(s, TCG_REG_T2, bh + (is_sub ? -1 : 1)); - } else { - tcg_out_arithi(s, TCG_REG_T2, bh, 1, - is_sub ? ARITH_SUB : ARITH_ADD); - } - /* ... smoosh T2 back to original BH if carry is clear ... */ - tcg_out_movcc(s, COND_CC, MOVCC_XCC, TCG_REG_T2, bh, bhconst); - /* ... and finally perform the arithmetic with the new operand. */ - tcg_out_arith(s, rh, ah, TCG_REG_T2, is_sub ? ARITH_SUB : ARITH_ADD); - } - - tcg_out_mov(s, TCG_TYPE_I64, rl, tmp); -} - static void tcg_out_jmpl_const(TCGContext *s, const tcg_insn_unit *dest, bool in_prologue, bool tail_call) { @@ -1382,21 +1318,132 @@ static const TCGOutOpBinary outop_add = { .out_rri = tgen_addi, }; +static void tgen_addco_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_ADDCC); +} + +static void tgen_addco_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_arithi(s, a0, a1, a2, ARITH_ADDCC); +} + static const TCGOutOpBinary outop_addco = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, r, rJ), + .out_rrr = tgen_addco_rrr, + .out_rri = tgen_addco_rri, }; +static void tgen_addci_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_arith(s, a0, a1, a2, ARITH_ADDC); + } else if (use_vis3_instructions) { + tcg_out_arith(s, a0, a1, a2, ARITH_ADDXC); + } else { + tcg_out_arith(s, TCG_REG_T1, a1, a2, ARITH_ADD); /* for CC */ + tcg_out_arithi(s, a0, TCG_REG_T1, 1, ARITH_ADD); /* for CS */ + /* Select the correct result based on actual carry value. */ + tcg_out_movcc(s, COND_CC, MOVCC_XCC, a0, TCG_REG_T1, false); + } +} + +static void tgen_addci_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_arithi(s, a0, a1, a2, ARITH_ADDC); + return; + } + /* !use_vis3_instructions */ + if (a2 != 0) { + tcg_out_arithi(s, TCG_REG_T1, a1, a2, ARITH_ADD); /* for CC */ + tcg_out_arithi(s, a0, TCG_REG_T1, 1, ARITH_ADD); /* for CS */ + tcg_out_movcc(s, COND_CC, MOVCC_XCC, a0, TCG_REG_T1, false); + } else if (a0 == a1) { + tcg_out_arithi(s, TCG_REG_T1, a1, 1, ARITH_ADD); + tcg_out_movcc(s, COND_CS, MOVCC_XCC, a0, TCG_REG_T1, false); + } else { + tcg_out_arithi(s, a0, a1, 1, ARITH_ADD); + tcg_out_movcc(s, COND_CC, MOVCC_XCC, a0, a1, false); + } +} + +static TCGConstraintSetIndex cset_addci(TCGType type, unsigned flags) +{ + if (use_vis3_instructions && type == TCG_TYPE_I64) { + /* Note that ADDXC doesn't accept immediates. */ + return C_O1_I2(r, rz, rz); + } + return C_O1_I2(r, rz, rJ); +} + static const TCGOutOpAddSubCarry outop_addci = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addci, + .out_rrr = tgen_addci_rrr, + .out_rri = tgen_addci_rri, }; +/* Copy %xcc.c to %icc.c */ +static void tcg_out_dup_xcc_c(TCGContext *s) +{ + if (use_vis3_instructions) { + tcg_out_arith(s, TCG_REG_T1, TCG_REG_G0, TCG_REG_G0, ARITH_ADDXC); + } else { + tcg_out_movi_s13(s, TCG_REG_T1, 0); + tcg_out_movcc(s, COND_CS, MOVCC_XCC, TCG_REG_T1, 1, true); + } + /* Write carry-in into %icc via {0,1} + -1. */ + tcg_out_arithi(s, TCG_REG_G0, TCG_REG_T1, -1, ARITH_ADDCC); +} + +static void tgen_addcio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + if (use_vis3_instructions) { + tcg_out_arith(s, a0, a1, a2, ARITH_ADDXCCC); + return; + } + tcg_out_dup_xcc_c(s); + } + tcg_out_arith(s, a0, a1, a2, ARITH_ADDCCC); +} + +static void tgen_addcio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type != TCG_TYPE_I32) { + /* !use_vis3_instructions */ + tcg_out_dup_xcc_c(s); + } + tcg_out_arithi(s, a0, a1, a2, ARITH_ADDCCC); +} + +static TCGConstraintSetIndex cset_addcio(TCGType type, unsigned flags) +{ + if (use_vis3_instructions && type == TCG_TYPE_I64) { + /* Note that ADDXCCC doesn't accept immediates. */ + return C_O1_I2(r, rz, rz); + } + return C_O1_I2(r, rz, rJ); +} + static const TCGOutOpBinary outop_addcio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addcio, + .out_rrr = tgen_addcio_rrr, + .out_rri = tgen_addcio_rri, }; static void tcg_out_set_carry(TCGContext *s) { - g_assert_not_reached(); + /* 0x11 -> xcc = nzvC, icc = nzvC */ + tcg_out_arithi(s, 0, TCG_REG_G0, 0x11, WRCCR); } static void tgen_and(TCGContext *s, TCGType type, @@ -1735,21 +1782,90 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static void tgen_subbo_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_arith(s, a0, a1, a2, ARITH_SUBCC); +} + +static void tgen_subbo_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + tcg_out_arithi(s, a0, a1, a2, ARITH_SUBCC); +} + static const TCGOutOpAddSubCarry outop_subbo = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, rz, rJ), + .out_rrr = tgen_subbo_rrr, + .out_rri = tgen_subbo_rri, }; +static void tgen_subbi_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + /* TODO: OSA 2015 added SUBXC */ + if (type == TCG_TYPE_I32) { + tcg_out_arith(s, a0, a1, a2, ARITH_SUBC); + } else { + tcg_out_arith(s, TCG_REG_T1, a1, a2, ARITH_SUB); /* for CC */ + tcg_out_arithi(s, a0, TCG_REG_T1, 1, ARITH_SUB); /* for CS */ + /* Select the correct result based on actual borrow value. */ + tcg_out_movcc(s, COND_CC, MOVCC_XCC, a0, TCG_REG_T1, false); + } +} + +static void tgen_subbi_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type == TCG_TYPE_I32) { + tcg_out_arithi(s, a0, a1, a2, ARITH_SUBC); + } else if (a2 != 0) { + tcg_out_arithi(s, TCG_REG_T1, a1, a2, ARITH_SUB); /* for CC */ + tcg_out_arithi(s, a0, TCG_REG_T1, 1, ARITH_SUB); /* for CS */ + tcg_out_movcc(s, COND_CC, MOVCC_XCC, a0, TCG_REG_T1, false); + } else if (a0 == a1) { + tcg_out_arithi(s, TCG_REG_T1, a1, 1, ARITH_SUB); + tcg_out_movcc(s, COND_CS, MOVCC_XCC, a0, TCG_REG_T1, false); + } else { + tcg_out_arithi(s, a0, a1, 1, ARITH_SUB); + tcg_out_movcc(s, COND_CC, MOVCC_XCC, a0, a1, false); + } +} + static const TCGOutOpAddSubCarry outop_subbi = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, rz, rJ), + .out_rrr = tgen_subbi_rrr, + .out_rri = tgen_subbi_rri, }; +static void tgen_subbio_rrr(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + if (type != TCG_TYPE_I32) { + /* TODO: OSA 2015 added SUBXCCC */ + tcg_out_dup_xcc_c(s); + } + tcg_out_arith(s, a0, a1, a2, ARITH_SUBCCC); +} + +static void tgen_subbio_rri(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, tcg_target_long a2) +{ + if (type != TCG_TYPE_I32) { + tcg_out_dup_xcc_c(s); + } + tcg_out_arithi(s, a0, a1, a2, ARITH_SUBCCC); +} + static const TCGOutOpAddSubCarry outop_subbio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_O1_I2(r, rz, rJ), + .out_rrr = tgen_subbio_rrr, + .out_rri = tgen_subbio_rri, }; static void tcg_out_set_borrow(TCGContext *s) { - g_assert_not_reached(); + tcg_out_set_carry(s); /* borrow == carry */ } static void tgen_xor(TCGContext *s, TCGType type, @@ -1886,17 +2002,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, a0, a1, a2, STW); break; - case INDEX_op_add2_i32: - tcg_out_addsub2_i32(s, args[0], args[1], args[2], args[3], - args[4], const_args[4], args[5], const_args[5], - ARITH_ADDCC, ARITH_ADDC); - break; - case INDEX_op_sub2_i32: - tcg_out_addsub2_i32(s, args[0], args[1], args[2], args[3], - args[4], const_args[4], args[5], const_args[5], - ARITH_SUBCC, ARITH_SUBC); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); break; @@ -1920,15 +2025,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, a0, a1, a2, STX); break; - case INDEX_op_add2_i64: - tcg_out_addsub2_i64(s, args[0], args[1], args[2], args[3], args[4], - const_args[4], args[5], const_args[5], false); - break; - case INDEX_op_sub2_i64: - tcg_out_addsub2_i64(s, args[0], args[1], args[2], args[3], args[4], - const_args[4], args[5], const_args[5], true); - break; - case INDEX_op_mb: tcg_out_mb(s, a0); break; @@ -1975,12 +2071,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); - case INDEX_op_add2_i32: - case INDEX_op_add2_i64: - case INDEX_op_sub2_i32: - case INDEX_op_sub2_i64: - return C_O2_I4(r, r, rz, rz, rJ, rJ); - default: return C_NotImplemented; } From 4b0ee858be252ed272a758e5b6f894717e911d51 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 20 Jan 2025 19:46:04 -0800 Subject: [PATCH 0500/2760] tcg/tci: Implement add/sub carry opcodes Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/tci.c | 120 +++++++++++++++++------------------ tcg/tci/tcg-target-has.h | 8 +-- tcg/tci/tcg-target-opc.h.inc | 1 + tcg/tci/tcg-target.c.inc | 97 +++++++++++++++++----------- 4 files changed, 125 insertions(+), 101 deletions(-) diff --git a/tcg/tci.c b/tcg/tci.c index dc916eb112..a18478a07a 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -179,17 +179,6 @@ static void tci_args_rrrrrc(uint32_t insn, TCGReg *r0, TCGReg *r1, *c5 = extract32(insn, 28, 4); } -static void tci_args_rrrrrr(uint32_t insn, TCGReg *r0, TCGReg *r1, - TCGReg *r2, TCGReg *r3, TCGReg *r4, TCGReg *r5) -{ - *r0 = extract32(insn, 8, 4); - *r1 = extract32(insn, 12, 4); - *r2 = extract32(insn, 16, 4); - *r3 = extract32(insn, 20, 4); - *r4 = extract32(insn, 24, 4); - *r5 = extract32(insn, 28, 4); -} - static bool tci_compare32(uint32_t u0, uint32_t u1, TCGCond condition) { bool result = false; @@ -361,6 +350,7 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tcg_target_ulong regs[TCG_TARGET_NB_REGS]; uint64_t stack[(TCG_STATIC_CALL_ARGS_SIZE + TCG_STATIC_FRAME_SIZE) / sizeof(uint64_t)]; + bool carry = false; regs[TCG_AREG0] = (tcg_target_ulong)env; regs[TCG_REG_CALL_STACK] = (uintptr_t)stack; @@ -369,13 +359,12 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, for (;;) { uint32_t insn; TCGOpcode opc; - TCGReg r0, r1, r2, r3, r4, r5; + TCGReg r0, r1, r2, r3, r4; tcg_target_ulong t1; TCGCond condition; uint8_t pos, len; uint32_t tmp32; uint64_t tmp64, taddr; - uint64_t T1, T2; MemOpIdx oi; int32_t ofs; void *ptr; @@ -444,9 +433,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, #if TCG_TARGET_REG_BITS == 32 case INDEX_op_setcond2_i32: tci_args_rrrrrc(insn, &r0, &r1, &r2, &r3, &r4, &condition); - T1 = tci_uint64(regs[r2], regs[r1]); - T2 = tci_uint64(regs[r4], regs[r3]); - regs[r0] = tci_compare64(T1, T2, condition); + regs[r0] = tci_compare64(tci_uint64(regs[r2], regs[r1]), + tci_uint64(regs[r4], regs[r3]), + condition); break; #elif TCG_TARGET_REG_BITS == 64 case INDEX_op_setcond: @@ -471,6 +460,9 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rl(insn, tb_ptr, &r0, &ptr); regs[r0] = *(tcg_target_ulong *)ptr; break; + case INDEX_op_tci_setcarry: + carry = true; + break; /* Load/store operations (32 bit). */ @@ -575,6 +567,46 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rr(insn, &r0, &r1); regs[r0] = ctpop_tr(regs[r1]); break; + case INDEX_op_addco: + tci_args_rrr(insn, &r0, &r1, &r2); + t1 = regs[r1] + regs[r2]; + carry = t1 < regs[r1]; + regs[r0] = t1; + break; + case INDEX_op_addci: + tci_args_rrr(insn, &r0, &r1, &r2); + regs[r0] = regs[r1] + regs[r2] + carry; + break; + case INDEX_op_addcio: + tci_args_rrr(insn, &r0, &r1, &r2); + if (carry) { + t1 = regs[r1] + regs[r2] + 1; + carry = t1 <= regs[r1]; + } else { + t1 = regs[r1] + regs[r2]; + carry = t1 < regs[r1]; + } + regs[r0] = t1; + break; + case INDEX_op_subbo: + tci_args_rrr(insn, &r0, &r1, &r2); + carry = regs[r1] < regs[r2]; + regs[r0] = regs[r1] - regs[r2]; + break; + case INDEX_op_subbi: + tci_args_rrr(insn, &r0, &r1, &r2); + regs[r0] = regs[r1] - regs[r2] - carry; + break; + case INDEX_op_subbio: + tci_args_rrr(insn, &r0, &r1, &r2); + if (carry) { + carry = regs[r1] <= regs[r2]; + regs[r0] = regs[r1] - regs[r2] - 1; + } else { + carry = regs[r1] < regs[r2]; + regs[r0] = regs[r1] - regs[r2]; + } + break; case INDEX_op_muls2: tci_args_rrrr(insn, &r0, &r1, &r2, &r3); #if TCG_TARGET_REG_BITS == 32 @@ -673,22 +705,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tb_ptr = ptr; } break; -#if TCG_TARGET_REG_BITS == 32 || TCG_TARGET_HAS_add2_i32 - case INDEX_op_add2_i32: - tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); - T1 = tci_uint64(regs[r3], regs[r2]); - T2 = tci_uint64(regs[r5], regs[r4]); - tci_write_reg64(regs, r1, r0, T1 + T2); - break; -#endif -#if TCG_TARGET_REG_BITS == 32 || TCG_TARGET_HAS_sub2_i32 - case INDEX_op_sub2_i32: - tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); - T1 = tci_uint64(regs[r3], regs[r2]); - T2 = tci_uint64(regs[r5], regs[r4]); - tci_write_reg64(regs, r1, r0, T1 - T2); - break; -#endif case INDEX_op_bswap16: tci_args_rr(insn, &r0, &r1); regs[r0] = bswap16(regs[r1]); @@ -742,24 +758,6 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tci_args_rrr(insn, &r0, &r1, &r2); regs[r0] = regs[r1] ? ctz64(regs[r1]) : regs[r2]; break; -#if TCG_TARGET_HAS_add2_i64 - case INDEX_op_add2_i64: - tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); - T1 = regs[r2] + regs[r4]; - T2 = regs[r3] + regs[r5] + (T1 < regs[r2]); - regs[r0] = T1; - regs[r1] = T2; - break; -#endif -#if TCG_TARGET_HAS_add2_i64 - case INDEX_op_sub2_i64: - tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); - T1 = regs[r2] - regs[r4]; - T2 = regs[r3] - regs[r5] - (regs[r2] < regs[r4]); - regs[r0] = T1; - regs[r1] = T2; - break; -#endif /* Shift/rotate operations (64 bit). */ @@ -908,7 +906,7 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) const char *op_name; uint32_t insn; TCGOpcode op; - TCGReg r0, r1, r2, r3, r4, r5; + TCGReg r0, r1, r2, r3, r4; tcg_target_ulong i1; int32_t s2; TCGCond c; @@ -968,6 +966,10 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) op_name, str_r(r0), ptr); break; + case INDEX_op_tci_setcarry: + info->fprintf_func(info->stream, "%-12s", op_name); + break; + case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i32: @@ -1007,6 +1009,9 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) break; case INDEX_op_add: + case INDEX_op_addci: + case INDEX_op_addcio: + case INDEX_op_addco: case INDEX_op_and: case INDEX_op_andc: case INDEX_op_clz: @@ -1027,6 +1032,9 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_shl: case INDEX_op_shr: case INDEX_op_sub: + case INDEX_op_subbi: + case INDEX_op_subbio: + case INDEX_op_subbo: case INDEX_op_xor: case INDEX_op_tci_ctz32: case INDEX_op_tci_clz32: @@ -1071,16 +1079,6 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) str_r(r2), str_r(r3)); break; - case INDEX_op_add2_i32: - case INDEX_op_add2_i64: - case INDEX_op_sub2_i32: - case INDEX_op_sub2_i64: - tci_args_rrrrrr(insn, &r0, &r1, &r2, &r3, &r4, &r5); - info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s, %s, %s", - op_name, str_r(r0), str_r(r1), str_r(r2), - str_r(r3), str_r(r4), str_r(r5)); - break; - case INDEX_op_qemu_ld_i64: case INDEX_op_qemu_st_i64: if (TCG_TARGET_REG_BITS == 32) { diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 6063f32f7b..310d45ba62 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -8,13 +8,13 @@ #define TCG_TARGET_HAS_H #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_add2_i32 1 -#define TCG_TARGET_HAS_sub2_i32 1 +#define TCG_TARGET_HAS_add2_i32 0 +#define TCG_TARGET_HAS_sub2_i32 0 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_add2_i64 1 -#define TCG_TARGET_HAS_sub2_i64 1 +#define TCG_TARGET_HAS_add2_i64 0 +#define TCG_TARGET_HAS_sub2_i64 0 #endif /* TCG_TARGET_REG_BITS == 64 */ #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/tci/tcg-target-opc.h.inc b/tcg/tci/tcg-target-opc.h.inc index 672d9b7323..4eb32ed736 100644 --- a/tcg/tci/tcg-target-opc.h.inc +++ b/tcg/tci/tcg-target-opc.h.inc @@ -2,6 +2,7 @@ /* These opcodes for use between the tci generator and interpreter. */ DEF(tci_movi, 1, 0, 1, TCG_OPF_NOT_PRESENT) DEF(tci_movl, 1, 0, 1, TCG_OPF_NOT_PRESENT) +DEF(tci_setcarry, 0, 0, 0, TCG_OPF_NOT_PRESENT) DEF(tci_clz32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_ctz32, 1, 2, 0, TCG_OPF_NOT_PRESENT) DEF(tci_divs32, 1, 2, 0, TCG_OPF_NOT_PRESENT) diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index bba96d7a19..35c0c91f3e 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -66,12 +66,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i64: return C_O0_I2(r, r); - case INDEX_op_add2_i32: - case INDEX_op_add2_i64: - case INDEX_op_sub2_i32: - case INDEX_op_sub2_i64: - return C_O2_I4(r, r, r, r, r, r); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); case INDEX_op_qemu_ld_i64: @@ -346,22 +340,6 @@ static void tcg_out_op_rrrrrc(TCGContext *s, TCGOpcode op, tcg_out32(s, insn); } -static void tcg_out_op_rrrrrr(TCGContext *s, TCGOpcode op, - TCGReg r0, TCGReg r1, TCGReg r2, - TCGReg r3, TCGReg r4, TCGReg r5) -{ - tcg_insn_unit insn = 0; - - insn = deposit32(insn, 0, 8, op); - insn = deposit32(insn, 8, 4, r0); - insn = deposit32(insn, 12, 4, r1); - insn = deposit32(insn, 16, 4, r2); - insn = deposit32(insn, 20, 4, r3); - insn = deposit32(insn, 24, 4, r4); - insn = deposit32(insn, 28, 4, r5); - tcg_out32(s, insn); -} - static void tcg_out_ldst(TCGContext *s, TCGOpcode op, TCGReg val, TCGReg base, intptr_t offset) { @@ -573,21 +551,50 @@ static const TCGOutOpBinary outop_add = { .out_rrr = tgen_add, }; +static TCGConstraintSetIndex cset_addsubcarry(TCGType type, unsigned flags) +{ + return type == TCG_TYPE_REG ? C_O1_I2(r, r, r) : C_NotImplemented; +} + +static void tgen_addco(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_addco, a0, a1, a2); +} + static const TCGOutOpBinary outop_addco = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addsubcarry, + .out_rrr = tgen_addco, }; +static void tgen_addci(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_addci, a0, a1, a2); +} + static const TCGOutOpAddSubCarry outop_addci = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addsubcarry, + .out_rrr = tgen_addci, }; +static void tgen_addcio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_addcio, a0, a1, a2); +} + static const TCGOutOpBinary outop_addcio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addsubcarry, + .out_rrr = tgen_addcio, }; static void tcg_out_set_carry(TCGContext *s) { - g_assert_not_reached(); + tcg_out_op_v(s, INDEX_op_tci_setcarry); } static void tgen_and(TCGContext *s, TCGType type, @@ -910,21 +917,45 @@ static const TCGOutOpSubtract outop_sub = { .out_rrr = tgen_sub, }; +static void tgen_subbo(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_subbo, a0, a1, a2); +} + static const TCGOutOpAddSubCarry outop_subbo = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addsubcarry, + .out_rrr = tgen_subbo, }; +static void tgen_subbi(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_subbi, a0, a1, a2); +} + static const TCGOutOpAddSubCarry outop_subbi = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addsubcarry, + .out_rrr = tgen_subbi, }; +static void tgen_subbio(TCGContext *s, TCGType type, + TCGReg a0, TCGReg a1, TCGReg a2) +{ + tcg_out_op_rrr(s, INDEX_op_subbio, a0, a1, a2); +} + static const TCGOutOpAddSubCarry outop_subbio = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_addsubcarry, + .out_rrr = tgen_subbio, }; static void tcg_out_set_borrow(TCGContext *s) { - g_assert_not_reached(); + tcg_out_op_v(s, INDEX_op_tci_setcarry); /* borrow == carry */ } static void tgen_xor(TCGContext *s, TCGType type, @@ -1129,12 +1160,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, opc, args[0], args[1], args[2]); break; - CASE_32_64(add2) - CASE_32_64(sub2) - tcg_out_op_rrrrrr(s, opc, args[0], args[1], args[2], - args[3], args[4], args[5]); - break; - case INDEX_op_qemu_ld_i64: case INDEX_op_qemu_st_i64: if (TCG_TARGET_REG_BITS == 32) { From f2b1708e8080ab1beb0a2bf52a79a51e8de335cb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 20 Jan 2025 20:15:31 -0800 Subject: [PATCH 0501/2760] tcg: Remove add2/sub2 opcodes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All uses have been replaced by add/sub carry opcodes. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 14 ++--- include/tcg/tcg-opc.h | 5 -- tcg/aarch64/tcg-target-has.h | 5 -- tcg/arm/tcg-target-has.h | 4 -- tcg/i386/tcg-target-has.h | 5 -- tcg/loongarch64/tcg-target-has.h | 4 -- tcg/mips/tcg-target-has.h | 5 -- tcg/optimize.c | 87 -------------------------------- tcg/ppc/tcg-target-has.h | 4 -- tcg/riscv/tcg-target-has.h | 5 -- tcg/s390x/tcg-target-has.h | 7 --- tcg/sparc64/tcg-target-has.h | 7 --- tcg/tcg-has.h | 2 - tcg/tcg-op.c | 26 ---------- tcg/tcg.c | 36 ------------- tcg/tci/tcg-target-has.h | 4 -- 16 files changed, 3 insertions(+), 217 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index 93bcc70639..a7147407de 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -654,14 +654,6 @@ Multiword arithmetic support code generator will use ``tcg_out_set_borrow`` and then the output routine for *subbio*. - * - add2_i32/i64 *t0_low*, *t0_high*, *t1_low*, *t1_high*, *t2_low*, *t2_high* - - sub2_i32/i64 *t0_low*, *t0_high*, *t1_low*, *t1_high*, *t2_low*, *t2_high* - - - | Similar to add/sub, except that the double-word inputs *t1* and *t2* are - formed from two single-word arguments, and the double-word output *t0* - is returned in two single-word outputs. - * - mulu2 *t0_low*, *t0_high*, *t1*, *t2* - | Similar to mul, except two unsigned inputs *t1* and *t2* yielding the full @@ -952,9 +944,9 @@ Assumptions The target word size (``TCG_TARGET_REG_BITS``) is expected to be 32 bit or 64 bit. It is expected that the pointer has the same size as the word. -On a 32 bit target, all 64 bit operations are converted to 32 bits. A -few specific operations must be implemented to allow it (see add2_i32, -sub2_i32, brcond2_i32). +On a 32 bit target, all 64 bit operations are converted to 32 bits. +A few specific operations must be implemented to allow it +(see brcond2_i32, setcond2_i32). On a 64 bit target, the values are transferred between 32 and 64-bit registers using the following ops: diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 9cc20cd62c..30ba15723a 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -102,8 +102,6 @@ DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) -DEF(add2_i32, 2, 4, 0, 0) -DEF(sub2_i32, 2, 4, 0, 0) DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(setcond2_i32, 1, 4, 1, 0) @@ -126,9 +124,6 @@ DEF(extu_i32_i64, 1, 1, 0, 0) DEF(extrl_i64_i32, 1, 1, 0, 0) DEF(extrh_i64_i32, 1, 1, 0, 0) -DEF(add2_i64, 2, 4, 0, 0) -DEF(sub2_i64, 2, 4, 0, 0) - #define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) /* There are tcg_ctx->insn_start_words here, not just one. */ diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index 695effd77c..b155e37639 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -13,14 +13,9 @@ #define have_lse2 (cpuinfo & CPUINFO_LSE2) /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 - /* * Without FEAT_LSE2, we must use LDXP+STXP to implement atomic 128-bit load, * which requires writable pages. We must defer to the helper for user-only, diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index f4bd15c68a..187269e5bd 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -24,12 +24,8 @@ extern bool use_neon_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 - #define TCG_TARGET_HAS_qemu_ldst_i128 0 - #define TCG_TARGET_HAS_tst 1 #define TCG_TARGET_HAS_v64 use_neon_instructions diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index a984a6af2e..628e736de7 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -26,14 +26,9 @@ #define have_avx512vbmi2 ((cpuinfo & CPUINFO_AVX512VBMI2) && have_avx512vl) /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 - #if TCG_TARGET_REG_BITS == 64 /* Keep 32-bit values zero-extended in a register. */ #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 #else #define TCG_TARGET_HAS_qemu_st8_i32 1 diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index a1bd71db6a..9c118bd1f6 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -10,14 +10,10 @@ #include "host/cpuinfo.h" /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 /* 64-bit operations */ #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_qemu_ldst_i128 (cpuinfo & CPUINFO_LSX) diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index 9d86906bf3..d8f9f7beef 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -39,13 +39,8 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 - #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 #define TCG_TARGET_HAS_ext32s_i64 1 #define TCG_TARGET_HAS_ext32u_i64 1 #endif diff --git a/tcg/optimize.c b/tcg/optimize.c index 95ec3b426d..52e194aaa9 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1399,82 +1399,6 @@ static bool fold_addco(OptContext *ctx, TCGOp *op) return finish_folding(ctx, op); } -static bool fold_addsub2(OptContext *ctx, TCGOp *op, bool add) -{ - bool a_const = arg_is_const(op->args[2]) && arg_is_const(op->args[3]); - bool b_const = arg_is_const(op->args[4]) && arg_is_const(op->args[5]); - - if (a_const && b_const) { - uint64_t al = arg_info(op->args[2])->val; - uint64_t ah = arg_info(op->args[3])->val; - uint64_t bl = arg_info(op->args[4])->val; - uint64_t bh = arg_info(op->args[5])->val; - TCGArg rl, rh; - TCGOp *op2; - - if (ctx->type == TCG_TYPE_I32) { - uint64_t a = deposit64(al, 32, 32, ah); - uint64_t b = deposit64(bl, 32, 32, bh); - - if (add) { - a += b; - } else { - a -= b; - } - - al = sextract64(a, 0, 32); - ah = sextract64(a, 32, 32); - } else { - Int128 a = int128_make128(al, ah); - Int128 b = int128_make128(bl, bh); - - if (add) { - a = int128_add(a, b); - } else { - a = int128_sub(a, b); - } - - al = int128_getlo(a); - ah = int128_gethi(a); - } - - rl = op->args[0]; - rh = op->args[1]; - - /* The proper opcode is supplied by tcg_opt_gen_mov. */ - op2 = opt_insert_before(ctx, op, 0, 2); - - tcg_opt_gen_movi(ctx, op, rl, al); - tcg_opt_gen_movi(ctx, op2, rh, ah); - return true; - } - - /* Fold sub2 r,x,i to add2 r,x,-i */ - if (!add && b_const) { - uint64_t bl = arg_info(op->args[4])->val; - uint64_t bh = arg_info(op->args[5])->val; - - /* Negate the two parts without assembling and disassembling. */ - bl = -bl; - bh = ~bh + !bl; - - op->opc = (ctx->type == TCG_TYPE_I32 - ? INDEX_op_add2_i32 : INDEX_op_add2_i64); - op->args[4] = arg_new_constant(ctx, bl); - op->args[5] = arg_new_constant(ctx, bh); - } - return finish_folding(ctx, op); -} - -static bool fold_add2(OptContext *ctx, TCGOp *op) -{ - /* Note that the high and low parts may be independently swapped. */ - swap_commutative(op->args[0], &op->args[2], &op->args[4]); - swap_commutative(op->args[1], &op->args[3], &op->args[5]); - - return fold_addsub2(ctx, op, true); -} - static bool fold_and(OptContext *ctx, TCGOp *op) { uint64_t z1, z2, z_mask, s_mask; @@ -2811,11 +2735,6 @@ static bool fold_sub(OptContext *ctx, TCGOp *op) return finish_folding(ctx, op); } -static bool fold_sub2(OptContext *ctx, TCGOp *op) -{ - return fold_addsub2(ctx, op, false); -} - static void squash_prev_borrowout(OptContext *ctx, TCGOp *op) { TempOptInfo *t2; @@ -3150,9 +3069,6 @@ void tcg_optimize(TCGContext *s) case INDEX_op_addco: done = fold_addco(&ctx, op); break; - CASE_OP_32_64(add2): - done = fold_add2(&ctx, op); - break; case INDEX_op_and: case INDEX_op_and_vec: done = fold_and(&ctx, op); @@ -3342,9 +3258,6 @@ void tcg_optimize(TCGContext *s) case INDEX_op_sub_vec: done = fold_sub_vec(&ctx, op); break; - CASE_OP_32_64(sub2): - done = fold_sub2(&ctx, op); - break; case INDEX_op_xor: case INDEX_op_xor_vec: done = fold_xor(&ctx, op); diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index 4dda668706..b978c91a62 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -18,13 +18,9 @@ /* optional instructions */ #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 #endif #define TCG_TARGET_HAS_qemu_ldst_i128 \ diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index c95dc1921e..8cd099546f 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -11,13 +11,8 @@ /* optional instructions */ #define TCG_TARGET_HAS_qemu_st8_i32 0 - #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 - #define TCG_TARGET_HAS_qemu_ldst_i128 0 - #define TCG_TARGET_HAS_tst 0 /* vector instructions */ diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index 17e61130cd..c04cc4e377 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -29,16 +29,9 @@ extern uint64_t s390_facilities[3]; ((s390_facilities[FACILITY_##X / 64] >> (63 - FACILITY_##X % 64)) & 1) /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 - -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 - #define TCG_TARGET_HAS_qemu_ldst_i128 1 - #define TCG_TARGET_HAS_tst 1 #define TCG_TARGET_HAS_v64 HAVE_FACILITY(VECTOR) diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index caf7679595..d9f5ef3fc9 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -14,16 +14,9 @@ extern bool use_vis3_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 #define TCG_TARGET_HAS_qemu_st8_i32 0 - #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 - #define TCG_TARGET_HAS_qemu_ldst_i128 0 - #define TCG_TARGET_HAS_tst 1 #define TCG_TARGET_extract_valid(type, ofs, len) \ diff --git a/tcg/tcg-has.h b/tcg/tcg-has.h index 50e8d0cda4..2fc0e50d20 100644 --- a/tcg/tcg-has.h +++ b/tcg/tcg-has.h @@ -12,8 +12,6 @@ #if TCG_TARGET_REG_BITS == 32 /* Turn some undef macros into false macros. */ #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 #endif #if !defined(TCG_TARGET_HAS_v64) \ diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index b0a29278ab..b0139ce05d 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -249,24 +249,6 @@ static void DNI tcg_gen_op5ii_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, tcgv_i64_arg(a3), a4, a5); } -static void DNI tcg_gen_op6_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, - TCGv_i32 a3, TCGv_i32 a4, - TCGv_i32 a5, TCGv_i32 a6) -{ - tcg_gen_op6(opc, TCG_TYPE_I32, tcgv_i32_arg(a1), tcgv_i32_arg(a2), - tcgv_i32_arg(a3), tcgv_i32_arg(a4), tcgv_i32_arg(a5), - tcgv_i32_arg(a6)); -} - -static void DNI tcg_gen_op6_i64(TCGOpcode opc, TCGv_i64 a1, TCGv_i64 a2, - TCGv_i64 a3, TCGv_i64 a4, - TCGv_i64 a5, TCGv_i64 a6) -{ - tcg_gen_op6(opc, TCG_TYPE_I64, tcgv_i64_arg(a1), tcgv_i64_arg(a2), - tcgv_i64_arg(a3), tcgv_i64_arg(a4), tcgv_i64_arg(a5), - tcgv_i64_arg(a6)); -} - static void DNI tcg_gen_op6i_i32(TCGOpcode opc, TCGv_i32 a1, TCGv_i32 a2, TCGv_i32 a3, TCGv_i32 a4, TCGv_i32 a5, TCGArg a6) @@ -1108,8 +1090,6 @@ void tcg_gen_add2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, tcg_gen_op3_i32(INDEX_op_addci, rh, ah, bh); tcg_gen_mov_i32(rl, t0); tcg_temp_free_i32(t0); - } else if (TCG_TARGET_HAS_add2_i32) { - tcg_gen_op6_i32(INDEX_op_add2_i32, rl, rh, al, ah, bl, bh); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 t1 = tcg_temp_ebb_new_i32(); @@ -1159,8 +1139,6 @@ void tcg_gen_sub2_i32(TCGv_i32 rl, TCGv_i32 rh, TCGv_i32 al, tcg_gen_op3_i32(INDEX_op_subbi, rh, ah, bh); tcg_gen_mov_i32(rl, t0); tcg_temp_free_i32(t0); - } else if (TCG_TARGET_HAS_sub2_i32) { - tcg_gen_op6_i32(INDEX_op_sub2_i32, rl, rh, al, ah, bl, bh); } else { TCGv_i32 t0 = tcg_temp_ebb_new_i32(); TCGv_i32 t1 = tcg_temp_ebb_new_i32(); @@ -2880,8 +2858,6 @@ void tcg_gen_add2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, tcg_gen_mov_i64(rl, t0); tcg_temp_free_i64(t0); - } else if (TCG_TARGET_HAS_add2_i64) { - tcg_gen_op6_i64(INDEX_op_add2_i64, rl, rh, al, ah, bl, bh); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); @@ -2985,8 +2961,6 @@ void tcg_gen_sub2_i64(TCGv_i64 rl, TCGv_i64 rh, TCGv_i64 al, tcg_gen_mov_i64(rl, t0); tcg_temp_free_i64(t0); - } else if (TCG_TARGET_HAS_sub2_i64) { - tcg_gen_op6_i64(INDEX_op_sub2_i64, rl, rh, al, ah, bl, bh); } else { TCGv_i64 t0 = tcg_temp_ebb_new_i64(); TCGv_i64 t1 = tcg_temp_ebb_new_i64(); diff --git a/tcg/tcg.c b/tcg/tcg.c index 3b9f519ef6..5a498b48b6 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2430,11 +2430,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_st_i32: return true; - case INDEX_op_add2_i32: - return TCG_TARGET_HAS_add2_i32; - case INDEX_op_sub2_i32: - return TCG_TARGET_HAS_sub2_i32; - case INDEX_op_brcond2_i32: case INDEX_op_setcond2_i32: return TCG_TARGET_REG_BITS == 32; @@ -2456,11 +2451,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_extrh_i64_i32: return TCG_TARGET_REG_BITS == 64; - case INDEX_op_add2_i64: - return TCG_TARGET_HAS_add2_i64; - case INDEX_op_sub2_i64: - return TCG_TARGET_HAS_sub2_i64; - case INDEX_op_mov_vec: case INDEX_op_dup_vec: case INDEX_op_dupm_vec: @@ -4101,32 +4091,6 @@ liveness_pass_1(TCGContext *s) la_reset_pref(ts); break; - case INDEX_op_add2_i32: - case INDEX_op_add2_i64: - opc_new = INDEX_op_add; - goto do_addsub2; - case INDEX_op_sub2_i32: - case INDEX_op_sub2_i64: - opc_new = INDEX_op_sub; - do_addsub2: - assert_carry_dead(s); - /* Test if the high part of the operation is dead, but not - the low part. The result can be optimized to a simple - add or sub. This happens often for x86_64 guest when the - cpu mode is set to 32 bit. */ - if (arg_temp(op->args[1])->state == TS_DEAD) { - if (arg_temp(op->args[0])->state == TS_DEAD) { - goto do_remove; - } - /* Replace the opcode and adjust the args in place, - leaving 3 unused args at the end. */ - op->opc = opc = opc_new; - op->args[1] = op->args[2]; - op->args[2] = op->args[4]; - /* Fall through and mark the single-word operation live. */ - } - goto do_not_remove; - case INDEX_op_muls2: opc_new = INDEX_op_mul; opc_new2 = INDEX_op_mulsh; diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 310d45ba62..497e8152b7 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -8,13 +8,9 @@ #define TCG_TARGET_HAS_H #define TCG_TARGET_HAS_qemu_st8_i32 0 -#define TCG_TARGET_HAS_add2_i32 0 -#define TCG_TARGET_HAS_sub2_i32 0 #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_add2_i64 0 -#define TCG_TARGET_HAS_sub2_i64 0 #endif /* TCG_TARGET_REG_BITS == 64 */ #define TCG_TARGET_HAS_qemu_ldst_i128 0 From e038696c9293f34dc7ccd4adac2c2f221a65a8e3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 20 Jan 2025 20:47:42 -0800 Subject: [PATCH 0502/2760] tcg: Formalize tcg_out_mb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most tcg backends already have a function for this; the rest can split one out from tcg_out_op. Call it directly from tcg_gen_code. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 6 +----- tcg/arm/tcg-target.c.inc | 6 +----- tcg/i386/tcg-target.c.inc | 5 +---- tcg/loongarch64/tcg-target.c.inc | 6 +----- tcg/mips/tcg-target.c.inc | 5 +---- tcg/ppc/tcg-target.c.inc | 6 +----- tcg/riscv/tcg-target.c.inc | 6 +----- tcg/s390x/tcg-target.c.inc | 20 +++++++++++--------- tcg/sparc64/tcg-target.c.inc | 6 +----- tcg/tcg.c | 4 ++++ tcg/tci/tcg-target.c.inc | 9 +++++---- 11 files changed, 28 insertions(+), 51 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 75cf490fd2..d2babd9bab 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1575,7 +1575,7 @@ static void tcg_out_extrl_i64_i32(TCGContext *s, TCGReg rd, TCGReg rn) tcg_out_mov(s, TCG_TYPE_I32, rd, rn); } -static inline void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { static const uint32_t sync[] = { [0 ... TCG_MO_ALL] = DMB_ISH | DMB_LD | DMB_ST, @@ -2845,10 +2845,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], false); break; - case INDEX_op_mb: - tcg_out_mb(s, a0); - break; - case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 3c9042ebfa..131901dabc 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1203,7 +1203,7 @@ static void tcg_out_goto_label(TCGContext *s, ARMCond cond, TCGLabel *l) } } -static void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { if (use_armv7_instructions) { tcg_out32(s, INSN_DMB_ISH); @@ -2565,10 +2565,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); break; - case INDEX_op_mb: - tcg_out_mb(s, args[0]); - break; - case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index da05f13b21..bf84f9f455 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1168,7 +1168,7 @@ static inline void tcg_out_pushi(TCGContext *s, tcg_target_long val) } } -static inline void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { /* Given the strength of x86 memory ordering, we only need care for store-load ordering. Experimentally, "lock orl $0,0(%esp)" is @@ -3536,9 +3536,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; #endif - case INDEX_op_mb: - tcg_out_mb(s, a0); - break; case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 4f640764ef..1ad577ad8d 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -301,7 +301,7 @@ static bool patch_reloc(tcg_insn_unit *code_ptr, int type, * TCG intrinsics */ -static void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { /* Baseline LoongArch only has the full barrier, unfortunately. */ tcg_out_opc_dbar(s, 0); @@ -1917,10 +1917,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a3 = args[3]; switch (opc) { - case INDEX_op_mb: - tcg_out_mb(s, a0); - break; - case INDEX_op_goto_ptr: tcg_out_opc_jirl(s, TCG_REG_ZERO, a0, 0); break; diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 0c268cef42..b0da661561 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1491,7 +1491,7 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, } } -static void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { static const MIPSInsn sync[] = { /* Note that SYNC_MB is a slightly weaker than SYNC 0, @@ -2352,9 +2352,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_mb: - tcg_out_mb(s, a0); - break; case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 91df9610ec..ae18c84ae6 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2208,7 +2208,7 @@ static const TCGOutOpBrcond2 outop_brcond2 = { .out = tgen_brcond2, }; -static void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { uint32_t insn; @@ -3758,10 +3758,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; - case INDEX_op_mb: - tcg_out_mb(s, args[0]); - break; - case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 707ebb8f6d..df271752b7 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1582,7 +1582,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *arg, tcg_out_call_int(s, arg, false); } -static void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { tcg_insn_unit insn = OPC_FENCE; @@ -2594,10 +2594,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); break; - case INDEX_op_mb: - tcg_out_mb(s, a0); - break; - case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 09c7ca5b44..020d8ba73f 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -3008,6 +3008,17 @@ static const TCGOutOpUnary outop_not = { .out_rr = tgen_not, }; +static void tcg_out_mb(TCGContext *s, unsigned a0) +{ + /* + * The host memory model is quite strong, we simply need to + * serialize the instruction stream. + */ + if (a0 & TCG_MO_ST_LD) { + /* fast-bcr-serialization facility (45) is present */ + tcg_out_insn(s, RR, BCR, 14, 0); + } +} # define OP_32_64(x) \ case glue(glue(INDEX_op_,x),_i32): \ @@ -3107,15 +3118,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); break; - case INDEX_op_mb: - /* The host memory model is quite strong, we simply need to - serialize the instruction stream. */ - if (args[0] & TCG_MO_ST_LD) { - /* fast-bcr-serialization facility (45) is present */ - tcg_out_insn(s, RR, BCR, 14, 0); - } - break; - case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index c2251a6927..7754627a5d 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -949,7 +949,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *dest, tcg_out_nop(s); } -static void tcg_out_mb(TCGContext *s, TCGArg a0) +static void tcg_out_mb(TCGContext *s, unsigned a0) { /* Note that the TCG memory order constants mirror the Sparc MEMBAR. */ tcg_out32(s, MEMBAR | (a0 & TCG_MO_ALL)); @@ -2025,10 +2025,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_ldst(s, a0, a1, a2, STX); break; - case INDEX_op_mb: - tcg_out_mb(s, a0); - break; - case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ diff --git a/tcg/tcg.c b/tcg/tcg.c index 5a498b48b6..e7478bef77 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -133,6 +133,7 @@ static void tcg_out_addi_ptr(TCGContext *s, TCGReg, TCGReg, tcg_target_long); static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2); static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg); static void tcg_out_goto_tb(TCGContext *s, int which); +static void tcg_out_mb(TCGContext *s, unsigned bar); static void tcg_out_set_carry(TCGContext *s); static void tcg_out_set_borrow(TCGContext *s); static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, @@ -6899,6 +6900,9 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) case INDEX_op_goto_tb: tcg_out_goto_tb(s, op->args[0]); break; + case INDEX_op_mb: + tcg_out_mb(s, op->args[0]); + break; case INDEX_op_dup2_vec: if (tcg_reg_alloc_dup2(s, op)) { break; diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 35c0c91f3e..64d4ac07cd 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -1131,6 +1131,11 @@ static const TCGOutOpSetcond2 outop_setcond2 = { .out = tgen_setcond2, }; +static void tcg_out_mb(TCGContext *s, unsigned a0) +{ + tcg_out_op_v(s, INDEX_op_mb); +} + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1178,10 +1183,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, } break; - case INDEX_op_mb: - tcg_out_op_v(s, opc); - break; - case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ From 3752a5a5ba33448fe40556d781e8096b5b9dd916 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 20 Jan 2025 21:17:07 -0800 Subject: [PATCH 0503/2760] tcg: Formalize tcg_out_br Split these functions out from tcg_out_op. Call it directly from tcg_gen_code. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 6 +----- tcg/arm/tcg-target.c.inc | 8 +++++--- tcg/i386/tcg-target.c.inc | 8 +++++--- tcg/loongarch64/tcg-target.c.inc | 12 ++++++------ tcg/mips/tcg-target.c.inc | 10 +++++----- tcg/ppc/tcg-target.c.inc | 26 ++++++++++++-------------- tcg/riscv/tcg-target.c.inc | 11 ++++++----- tcg/s390x/tcg-target.c.inc | 9 +++++---- tcg/sparc64/tcg-target.c.inc | 10 ++++++---- tcg/tcg.c | 4 ++++ tcg/tci/tcg-target.c.inc | 9 +++++---- 11 files changed, 60 insertions(+), 53 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index d2babd9bab..fceb6e2796 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1407,7 +1407,7 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *target, tcg_out_call_int(s, target); } -static inline void tcg_out_goto_label(TCGContext *s, TCGLabel *l) +static void tcg_out_br(TCGContext *s, TCGLabel *l) { if (!l->has_value) { tcg_out_reloc(s, s->code_ptr, R_AARCH64_JUMP26, l, 0); @@ -2779,10 +2779,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, tcg_out_insn(s, 3207, BR, a0); break; - case INDEX_op_br: - tcg_out_goto_label(s, arg_label(a0)); - break; - case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: tcg_out_ldst(s, I3312_LDRB, a0, a1, a2, 0); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 131901dabc..327b01d377 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1203,6 +1203,11 @@ static void tcg_out_goto_label(TCGContext *s, ARMCond cond, TCGLabel *l) } } +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tcg_out_goto_label(s, COND_AL, l); +} + static void tcg_out_mb(TCGContext *s, unsigned a0) { if (use_armv7_instructions) { @@ -2522,9 +2527,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, case INDEX_op_goto_ptr: tcg_out_b_reg(s, COND_AL, args[0]); break; - case INDEX_op_br: - tcg_out_goto_label(s, COND_AL, arg_label(args[0])); - break; case INDEX_op_ld8u_i32: tcg_out_ld8u(s, COND_AL, args[0], args[1], args[2]); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index bf84f9f455..f89982378b 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -1546,6 +1546,11 @@ static void tcg_out_jxx(TCGContext *s, int opc, TCGLabel *l, bool small) } } +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tcg_out_jxx(s, JCC_JMP, l, 0); +} + static int tcg_out_cmp(TCGContext *s, TCGCond cond, TCGArg arg1, TCGArg arg2, int const_arg2, int rexw) { @@ -3436,9 +3441,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, /* jmp to the given host address (could be epilogue) */ tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, a0); break; - case INDEX_op_br: - tcg_out_jxx(s, JCC_JMP, arg_label(a0), 0); - break; OP_32_64(ld8u): /* Note that we can ignore REXW for the zero-extend to 64-bit. */ tcg_out_modrm_offset(s, OPC_MOVZBL, a0, a1, a2); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 1ad577ad8d..cbdc42c157 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -756,6 +756,12 @@ static const TCGOutOpMovcond outop_movcond = { * Branch helpers */ +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tcg_out_reloc(s, s->code_ptr, R_LOONGARCH_BR_SD10K16, l, 0); + tcg_out_opc_b(s, 0); +} + static const struct { LoongArchInsn op; bool swap; @@ -1921,12 +1927,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_jirl(s, TCG_REG_ZERO, a0, 0); break; - case INDEX_op_br: - tcg_out_reloc(s, s->code_ptr, R_LOONGARCH_BR_SD10K16, arg_label(a0), - 0); - tcg_out_opc_b(s, 0); - break; - case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: tcg_out_ldst(s, OPC_LD_B, a0, a1, a2); diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index b0da661561..f4d6ee10b9 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -921,6 +921,11 @@ static const TCGOutOpBrcond outop_brcond = { .out_rr = tgen_brcond, }; +void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tgen_brcond(s, TCG_TYPE_I32, TCG_COND_EQ, TCG_REG_ZERO, TCG_REG_ZERO, l); +} + static int tcg_out_setcond2_int(TCGContext *s, TCGCond cond, TCGReg ret, TCGReg al, TCGReg ah, TCGReg bl, TCGReg bh) { @@ -2281,11 +2286,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_nop(s); } break; - case INDEX_op_br: - tgen_brcond(s, TCG_TYPE_I32, TCG_COND_EQ, - TCG_REG_ZERO, TCG_REG_ZERO, arg_label(a0)); - break; - case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: i1 = OPC_LBU; diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index ae18c84ae6..d88ec8d690 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -1990,6 +1990,18 @@ static const TCGOutOpSetcond outop_negsetcond = { .out_rri = tgen_negsetcondi, }; +void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + uint32_t insn = B; + + if (l->has_value) { + insn |= reloc_pc24_val(tcg_splitwx_to_rx(s->code_ptr), l->u.value_ptr); + } else { + tcg_out_reloc(s, s->code_ptr, R_PPC_REL24, l, 0); + } + tcg_out32(s, insn); +} + static void tcg_out_bc(TCGContext *s, TCGCond cond, int bd) { tcg_out32(s, tcg_to_bc[cond] | bd); @@ -3669,20 +3681,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out32(s, ADDI | TAI(TCG_REG_R3, 0, 0)); tcg_out32(s, BCCTR | BO_ALWAYS); break; - case INDEX_op_br: - { - TCGLabel *l = arg_label(args[0]); - uint32_t insn = B; - - if (l->has_value) { - insn |= reloc_pc24_val(tcg_splitwx_to_rx(s->code_ptr), - l->u.value_ptr); - } else { - tcg_out_reloc(s, s->code_ptr, R_PPC_REL24, l, 0); - } - tcg_out32(s, insn); - } - break; case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index df271752b7..5d8d8213cb 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1107,6 +1107,12 @@ static void tcg_out_dupi_vec(TCGContext *s, TCGType type, unsigned vece, tcg_out_dup_vec(s, type, vece, dst, TCG_REG_TMP0); } +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tcg_out_reloc(s, s->code_ptr, R_RISCV_JAL, l, 0); + tcg_out_opc_jump(s, OPC_JAL, TCG_REG_ZERO, 0); +} + static const struct { RISCVInsn op; bool swap; @@ -2533,11 +2539,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, a0, 0); break; - case INDEX_op_br: - tcg_out_reloc(s, s->code_ptr, R_RISCV_JAL, arg_label(a0), 0); - tcg_out_opc_jump(s, OPC_JAL, TCG_REG_ZERO, 0); - break; - case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: tcg_out_ldst(s, OPC_LBU, a0, a1, a2); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 020d8ba73f..cdc61de4f8 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -1689,6 +1689,11 @@ static void tgen_branch(TCGContext *s, int cc, TCGLabel *l) } } +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tgen_branch(s, S390_CC_ALWAYS, l); +} + static void tgen_compare_branch(TCGContext *s, S390Opcode opc, int cc, TCGReg r1, TCGReg r2, TCGLabel *l) { @@ -3075,10 +3080,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; - case INDEX_op_br: - tgen_branch(s, S390_CC_ALWAYS, arg_label(args[0])); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], args[1], args[2], TCG_TYPE_I32); break; diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 7754627a5d..0cc7567786 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -641,6 +641,12 @@ static void tcg_out_bpcc(TCGContext *s, int scond, int flags, TCGLabel *l) tcg_out_bpcc0(s, scond, flags, off19); } +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tcg_out_bpcc(s, COND_A, BPCC_PT, l); + tcg_out_nop(s); +} + static void tcg_out_cmp(TCGContext *s, TCGCond cond, TCGReg c1, int32_t c2, int c2const) { @@ -1966,10 +1972,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_arithi(s, TCG_REG_G0, a0, 0, JMPL); tcg_out_mov_delay(s, TCG_REG_TB, a0); break; - case INDEX_op_br: - tcg_out_bpcc(s, COND_A, BPCC_PT, arg_label(a0)); - tcg_out_nop(s); - break; #define OP_32_64(x) \ glue(glue(case INDEX_op_, x), _i32): \ diff --git a/tcg/tcg.c b/tcg/tcg.c index e7478bef77..fbb1a43efc 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -134,6 +134,7 @@ static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2); static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg); static void tcg_out_goto_tb(TCGContext *s, int which); static void tcg_out_mb(TCGContext *s, unsigned bar); +static void tcg_out_br(TCGContext *s, TCGLabel *l); static void tcg_out_set_carry(TCGContext *s); static void tcg_out_set_borrow(TCGContext *s); static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, @@ -6900,6 +6901,9 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) case INDEX_op_goto_tb: tcg_out_goto_tb(s, op->args[0]); break; + case INDEX_op_br: + tcg_out_br(s, arg_label(op->args[0])); + break; case INDEX_op_mb: tcg_out_mb(s, op->args[0]); break; diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 64d4ac07cd..55a1a74fb6 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -1136,6 +1136,11 @@ static void tcg_out_mb(TCGContext *s, unsigned a0) tcg_out_op_v(s, INDEX_op_mb); } +static void tcg_out_br(TCGContext *s, TCGLabel *l) +{ + tcg_out_op_l(s, INDEX_op_br, l); +} + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1145,10 +1150,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_op_r(s, opc, args[0]); break; - case INDEX_op_br: - tcg_out_op_l(s, opc, arg_label(args[0])); - break; - CASE_32_64(ld8u) CASE_32_64(ld8s) CASE_32_64(ld16u) From fee03fdde32c3d5b26429b7e691f78d1ee03d631 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 20 Jan 2025 21:57:32 -0800 Subject: [PATCH 0504/2760] tcg: Formalize tcg_out_goto_ptr Split these functions out from tcg_out_op. Define outop_goto_ptr generically. Call tcg_out_goto_ptr from tcg_reg_alloc_op. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 12 +++++------- tcg/arm/tcg-target.c.inc | 12 +++++------- tcg/i386/tcg-target.c.inc | 13 ++++++------- tcg/loongarch64/tcg-target.c.inc | 12 +++++------- tcg/mips/tcg-target.c.inc | 22 ++++++++++------------ tcg/ppc/tcg-target.c.inc | 15 +++++++-------- tcg/riscv/tcg-target.c.inc | 12 +++++------- tcg/s390x/tcg-target.c.inc | 15 +++++---------- tcg/sparc64/tcg-target.c.inc | 14 ++++++-------- tcg/tcg.c | 12 ++++++++++++ tcg/tci/tcg-target.c.inc | 12 +++++------- 11 files changed, 71 insertions(+), 80 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index fceb6e2796..2678e1f176 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1986,6 +1986,11 @@ static void tcg_out_goto_tb(TCGContext *s, int which) tcg_out_bti(s, BTI_J); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_insn(s, 3207, BR, a0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -2775,10 +2780,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, TCGArg a2 = args[2]; switch (opc) { - case INDEX_op_goto_ptr: - tcg_out_insn(s, 3207, BR, a0); - break; - case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: tcg_out_ldst(s, I3312_LDRB, a0, a1, a2, 0); @@ -3293,9 +3294,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - case INDEX_op_ld8u_i32: case INDEX_op_ld8s_i32: case INDEX_op_ld16u_i32: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 327b01d377..64be0a7e6d 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1795,6 +1795,11 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_b_reg(s, COND_AL, a0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -2524,10 +2529,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_goto_ptr: - tcg_out_b_reg(s, COND_AL, args[0]); - break; - case INDEX_op_ld8u_i32: tcg_out_ld8u(s, COND_AL, args[0], args[1], args[2]); break; @@ -2579,9 +2580,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - case INDEX_op_ld8u_i32: case INDEX_op_ld8s_i32: case INDEX_op_ld16u_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index f89982378b..5ea4a44264 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2593,6 +2593,12 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + /* Jump to the given host address (could be epilogue) */ + tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, a0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -3437,10 +3443,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; switch (opc) { - case INDEX_op_goto_ptr: - /* jmp to the given host address (could be epilogue) */ - tcg_out_modrm(s, OPC_GRP5, EXT5_JMPN_Ev, a0); - break; OP_32_64(ld8u): /* Note that we can ignore REXW for the zero-extend to 64-bit. */ tcg_out_modrm_offset(s, OPC_MOVZBL, a0, a1, a2); @@ -4093,9 +4095,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i32: diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index cbdc42c157..d89c27c67f 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1307,6 +1307,11 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_opc_jirl(s, TCG_REG_ZERO, a0, 0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -1923,10 +1928,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a3 = args[3]; switch (opc) { - case INDEX_op_goto_ptr: - tcg_out_opc_jirl(s, TCG_REG_ZERO, a0, 0); - break; - case INDEX_op_ld8s_i32: case INDEX_op_ld8s_i64: tcg_out_ldst(s, OPC_LD_B, a0, a1, a2); @@ -2491,9 +2492,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - case INDEX_op_st8_i32: case INDEX_op_st8_i64: case INDEX_op_st16_i32: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index f4d6ee10b9..9455a0a17b 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1571,6 +1571,16 @@ static void tcg_out_goto_tb(TCGContext *s, int which) } } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_opc_reg(s, OPC_JR, 0, a0, 0); + if (TCG_TARGET_REG_BITS == 64) { + tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, a0); + } else { + tcg_out_nop(s); + } +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -2277,15 +2287,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a2 = args[2]; switch (opc) { - case INDEX_op_goto_ptr: - /* jmp to the given host address (could be epilogue) */ - tcg_out_opc_reg(s, OPC_JR, 0, a0, 0); - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_mov(s, TCG_TYPE_PTR, TCG_REG_TB, a0); - } else { - tcg_out_nop(s); - } - break; case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: i1 = OPC_LBU; @@ -2364,9 +2365,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - case INDEX_op_ld8u_i32: case INDEX_op_ld8s_i32: case INDEX_op_ld16u_i32: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index d88ec8d690..a2a5b1e570 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2843,6 +2843,13 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out32(s, MTSPR | RS(a0) | CTR); + tcg_out32(s, ADDI | TAI(TCG_REG_R3, 0, 0)); + tcg_out32(s, BCCTR | BO_ALWAYS); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -3676,11 +3683,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_goto_ptr: - tcg_out32(s, MTSPR | RS(args[0]) | CTR); - tcg_out32(s, ADDI | TAI(TCG_REG_R3, 0, 0)); - tcg_out32(s, BCCTR | BO_ALWAYS); - break; case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); @@ -4371,9 +4373,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - case INDEX_op_ld8u_i32: case INDEX_op_ld8s_i32: case INDEX_op_ld16u_i32: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 5d8d8213cb..c1bfd93569 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1915,6 +1915,11 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, a0, 0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -2535,10 +2540,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a2 = args[2]; switch (opc) { - case INDEX_op_goto_ptr: - tcg_out_opc_imm(s, OPC_JALR, TCG_REG_ZERO, a0, 0); - break; - case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: tcg_out_ldst(s, OPC_LBU, a0, a1, a2); @@ -2824,9 +2825,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - case INDEX_op_ld8u_i32: case INDEX_op_ld8s_i32: case INDEX_op_ld16u_i32: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index cdc61de4f8..2b2e00c609 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2213,6 +2213,11 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, a0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -3033,14 +3038,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - TCGArg a0; - switch (opc) { - case INDEX_op_goto_ptr: - a0 = args[0]; - tcg_out_insn(s, RR, BCR, S390_CC_ALWAYS, a0); - break; - OP_32_64(ld8u): /* ??? LLC (RXY format) is only present with the extended-immediate facility, whereas LLGC is always present. */ @@ -3567,9 +3565,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i32: diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 0cc7567786..208b96487e 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1300,6 +1300,12 @@ static void tcg_out_goto_tb(TCGContext *s, int which) } } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_arithi(s, TCG_REG_G0, a0, 0, JMPL); + tcg_out_mov_delay(s, TCG_REG_TB, a0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -1968,11 +1974,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a2 = args[2]; switch (opc) { - case INDEX_op_goto_ptr: - tcg_out_arithi(s, TCG_REG_G0, a0, 0, JMPL); - tcg_out_mov_delay(s, TCG_REG_TB, a0); - break; - #define OP_32_64(x) \ glue(glue(case INDEX_op_, x), _i32): \ glue(glue(case INDEX_op_, x), _i64) @@ -2039,9 +2040,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - case INDEX_op_ld8u_i32: case INDEX_op_ld8u_i64: case INDEX_op_ld8s_i32: diff --git a/tcg/tcg.c b/tcg/tcg.c index fbb1a43efc..5ab4f5e752 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -133,6 +133,7 @@ static void tcg_out_addi_ptr(TCGContext *s, TCGReg, TCGReg, tcg_target_long); static bool tcg_out_xchg(TCGContext *s, TCGType type, TCGReg r1, TCGReg r2); static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg); static void tcg_out_goto_tb(TCGContext *s, int which); +static void tcg_out_goto_ptr(TCGContext *s, TCGReg dest); static void tcg_out_mb(TCGContext *s, unsigned bar); static void tcg_out_br(TCGContext *s, TCGLabel *l); static void tcg_out_set_carry(TCGContext *s); @@ -1137,6 +1138,10 @@ static const TCGOutOpUnary outop_extrl_i64_i32 = { }; #endif +static const TCGOutOp outop_goto_ptr = { + .static_constraint = C_O0_I1(r), +}; + /* * Register V as the TCGOutOp for O. * This verifies that V is of type T, otherwise give a nice compiler error. @@ -1198,6 +1203,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_subb1o, TCGOutOpAddSubCarry, outop_subbio), OUTOP(INDEX_op_xor, TCGOutOpBinary, outop_xor), + [INDEX_op_goto_ptr] = &outop_goto_ptr, + #if TCG_TARGET_REG_BITS == 32 OUTOP(INDEX_op_brcond2_i32, TCGOutOpBrcond2, outop_brcond2), OUTOP(INDEX_op_setcond2_i32, TCGOutOpSetcond2, outop_setcond2), @@ -5823,6 +5830,11 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) g_assert_not_reached(); #endif + case INDEX_op_goto_ptr: + tcg_debug_assert(!const_args[0]); + tcg_out_goto_ptr(s, new_args[0]); + break; + default: if (def->flags & TCG_OPF_VECTOR) { tcg_out_vec_op(s, op->opc, type - TCG_TYPE_V64, diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 55a1a74fb6..d9cd62ed3d 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -40,9 +40,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_goto_ptr: - return C_O0_I1(r); - case INDEX_op_ld8u_i32: case INDEX_op_ld8s_i32: case INDEX_op_ld16u_i32: @@ -534,6 +531,11 @@ static void tcg_out_goto_tb(TCGContext *s, int which) set_jmp_reset_offset(s, which); } +static void tcg_out_goto_ptr(TCGContext *s, TCGReg a0) +{ + tcg_out_op_r(s, INDEX_op_goto_ptr, a0); +} + void tb_target_set_jmp_target(const TranslationBlock *tb, int n, uintptr_t jmp_rx, uintptr_t jmp_rw) { @@ -1146,10 +1148,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_goto_ptr: - tcg_out_op_r(s, opc, args[0]); - break; - CASE_32_64(ld8u) CASE_32_64(ld8s) CASE_32_64(ld16u) From 0de5c9d1f56332554c48152f535b47a1a0c2af7b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 21 Jan 2025 20:44:42 -0800 Subject: [PATCH 0505/2760] tcg: Convert ld to TCGOutOpLoad Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 113 ++++++++++++++++----------- tcg/arm/tcg-target.c.inc | 126 ++++++++++++++++--------------- tcg/i386/tcg-target.c.inc | 112 ++++++++++++++++----------- tcg/loongarch64/tcg-target.c.inc | 104 +++++++++++++++---------- tcg/mips/tcg-target.c.inc | 108 ++++++++++++++++---------- tcg/ppc/tcg-target.c.inc | 110 +++++++++++++++++---------- tcg/riscv/tcg-target.c.inc | 107 ++++++++++++++++---------- tcg/s390x/tcg-target.c.inc | 122 +++++++++++++++++------------- tcg/sparc64/tcg-target.c.inc | 101 ++++++++++++++++--------- tcg/tcg.c | 46 +++++++++++ tcg/tci/tcg-target.c.inc | 91 ++++++++++++++++------ 11 files changed, 721 insertions(+), 419 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 2678e1f176..903a95ad7e 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2770,6 +2770,74 @@ static const TCGOutOpExtract2 outop_extract2 = { .out_rrr = tgen_extract2, }; +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, I3312_LDRB, dest, base, offset, 0); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + AArch64Insn insn = type == TCG_TYPE_I32 ? I3312_LDRSBW : I3312_LDRSBX; + tcg_out_ldst(s, insn, dest, base, offset, 0); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, I3312_LDRH, dest, base, offset, 1); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + AArch64Insn insn = type == TCG_TYPE_I32 ? I3312_LDRSHW : I3312_LDRSHX; + tcg_out_ldst(s, insn, dest, base, offset, 1); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, I3312_LDRW, dest, base, offset, 2); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, I3312_LDRSWX, dest, base, offset, 2); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2780,37 +2848,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, TCGArg a2 = args[2]; switch (opc) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - tcg_out_ldst(s, I3312_LDRB, a0, a1, a2, 0); - break; - case INDEX_op_ld8s_i32: - tcg_out_ldst(s, I3312_LDRSBW, a0, a1, a2, 0); - break; - case INDEX_op_ld8s_i64: - tcg_out_ldst(s, I3312_LDRSBX, a0, a1, a2, 0); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - tcg_out_ldst(s, I3312_LDRH, a0, a1, a2, 1); - break; - case INDEX_op_ld16s_i32: - tcg_out_ldst(s, I3312_LDRSHW, a0, a1, a2, 1); - break; - case INDEX_op_ld16s_i64: - tcg_out_ldst(s, I3312_LDRSHX, a0, a1, a2, 1); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - tcg_out_ldst(s, I3312_LDRW, a0, a1, a2, 2); - break; - case INDEX_op_ld32s_i64: - tcg_out_ldst(s, I3312_LDRSWX, a0, a1, a2, 2); - break; - case INDEX_op_ld_i64: - tcg_out_ldst(s, I3312_LDRX, a0, a1, a2, 3); - break; - case INDEX_op_st8_i32: case INDEX_op_st8_i64: tcg_out_ldst(s, I3312_STRB, a0, a1, a2, 0); @@ -3294,20 +3331,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - return C_O1_I1(r, r); - case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 64be0a7e6d..2079dd3bdc 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1084,26 +1084,6 @@ static void tcg_out_st32(TCGContext *s, ARMCond cond, tcg_out_st32_12(s, cond, rd, rn, offset); } -static void tcg_out_ld16u(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int32_t offset) -{ - if (offset > 0xff || offset < -0xff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld16u_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld16u_8(s, cond, rd, rn, offset); -} - -static void tcg_out_ld16s(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int32_t offset) -{ - if (offset > 0xff || offset < -0xff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld16s_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld16s_8(s, cond, rd, rn, offset); -} - static void tcg_out_st16(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn, int32_t offset) { @@ -1114,26 +1094,6 @@ static void tcg_out_st16(TCGContext *s, ARMCond cond, tcg_out_st16_8(s, cond, rd, rn, offset); } -static void tcg_out_ld8u(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int32_t offset) -{ - if (offset > 0xfff || offset < -0xfff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld8_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld8_12(s, cond, rd, rn, offset); -} - -static void tcg_out_ld8s(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int32_t offset) -{ - if (offset > 0xff || offset < -0xff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_ld8s_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_ld8s_8(s, cond, rd, rn, offset); -} - static void tcg_out_st8(TCGContext *s, ARMCond cond, TCGReg rd, TCGReg rn, int32_t offset) { @@ -2524,26 +2484,75 @@ static const TCGOutOpExtract2 outop_extract2 = { .out_rrr = tgen_extract2, }; +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rn, ptrdiff_t offset) +{ + if (offset > 0xfff || offset < -0xfff) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, offset); + tcg_out_ld8_r(s, COND_AL, rd, rn, TCG_REG_TMP); + } else { + tcg_out_ld8_12(s, COND_AL, rd, rn, offset); + } +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rn, ptrdiff_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, offset); + tcg_out_ld8s_r(s, COND_AL, rd, rn, TCG_REG_TMP); + } else { + tcg_out_ld8s_8(s, COND_AL, rd, rn, offset); + } +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rn, ptrdiff_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, offset); + tcg_out_ld16u_r(s, COND_AL, rd, rn, TCG_REG_TMP); + } else { + tcg_out_ld16u_8(s, COND_AL, rd, rn, offset); + } +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rn, ptrdiff_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, offset); + tcg_out_ld16s_r(s, COND_AL, rd, rn, TCG_REG_TMP); + } else { + tcg_out_ld16s_8(s, COND_AL, rd, rn, offset); + } +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_ld8u_i32: - tcg_out_ld8u(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_ld8s_i32: - tcg_out_ld8s(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_ld16u_i32: - tcg_out_ld16u(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_ld16s_i32: - tcg_out_ld16s(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i32: - tcg_out_ld32u(s, COND_AL, args[0], args[1], args[2]); - break; case INDEX_op_st8_i32: tcg_out_st8(s, COND_AL, args[0], args[1], args[2]); break; @@ -2580,13 +2589,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - return C_O1_I1(r, r); - case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 5ea4a44264..d16ddcb940 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3419,13 +3419,81 @@ static const TCGOutOpExtract2 outop_extract2 = { .out_rrr = tgen_extract2, }; +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVZBL, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm_offset(s, OPC_MOVSBL + rexw, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVZWL, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + int rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; + tcg_out_modrm_offset(s, OPC_MOVSWL + rexw, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +#if TCG_TARGET_REG_BITS == 64 +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVL_GvEv, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVSLQ, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; +#endif static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { TCGArg a0, a1, a2; - int rexw; #if TCG_TARGET_REG_BITS == 64 # define OP_32_64(x) \ @@ -3440,30 +3508,8 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a0 = args[0]; a1 = args[1]; a2 = args[2]; - rexw = type == TCG_TYPE_I32 ? 0 : P_REXW; switch (opc) { - OP_32_64(ld8u): - /* Note that we can ignore REXW for the zero-extend to 64-bit. */ - tcg_out_modrm_offset(s, OPC_MOVZBL, a0, a1, a2); - break; - OP_32_64(ld8s): - tcg_out_modrm_offset(s, OPC_MOVSBL + rexw, a0, a1, a2); - break; - OP_32_64(ld16u): - /* Note that we can ignore REXW for the zero-extend to 64-bit. */ - tcg_out_modrm_offset(s, OPC_MOVZWL, a0, a1, a2); - break; - OP_32_64(ld16s): - tcg_out_modrm_offset(s, OPC_MOVSWL + rexw, a0, a1, a2); - break; -#if TCG_TARGET_REG_BITS == 64 - case INDEX_op_ld32u_i64: -#endif - case INDEX_op_ld_i32: - tcg_out_ld(s, TCG_TYPE_I32, a0, a1, a2); - break; - OP_32_64(st8): if (const_args[0]) { tcg_out_modrm_offset(s, OPC_MOVB_EvIz, 0, a1, a2); @@ -3524,12 +3570,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; #if TCG_TARGET_REG_BITS == 64 - case INDEX_op_ld32s_i64: - tcg_out_modrm_offset(s, OPC_MOVSLQ, a0, a1, a2); - break; - case INDEX_op_ld_i64: - tcg_out_ld(s, TCG_TYPE_I64, a0, a1, a2); - break; case INDEX_op_st_i64: if (const_args[0]) { tcg_out_modrm_offset(s, OPC_MOVL_EvIz | P_REXW, 0, a1, a2); @@ -4095,20 +4135,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - return C_O1_I1(r, r); - case INDEX_op_st8_i32: case INDEX_op_st8_i64: return C_O0_I2(qi, r); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index d89c27c67f..66555b8982 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1917,6 +1917,71 @@ static const TCGOutOpExtract2 outop_extract2 = { .base.static_constraint = C_NotImplemented, }; +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LD_BU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LD_B, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LD_HU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LD_H, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LD_WU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LD_W, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -1928,33 +1993,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a3 = args[3]; switch (opc) { - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - tcg_out_ldst(s, OPC_LD_B, a0, a1, a2); - break; - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - tcg_out_ldst(s, OPC_LD_BU, a0, a1, a2); - break; - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - tcg_out_ldst(s, OPC_LD_H, a0, a1, a2); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - tcg_out_ldst(s, OPC_LD_HU, a0, a1, a2); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32s_i64: - tcg_out_ldst(s, OPC_LD_W, a0, a1, a2); - break; - case INDEX_op_ld32u_i64: - tcg_out_ldst(s, OPC_LD_WU, a0, a1, a2); - break; - case INDEX_op_ld_i64: - tcg_out_ldst(s, OPC_LD_D, a0, a1, a2); - break; - case INDEX_op_st8_i32: case INDEX_op_st8_i64: tcg_out_ldst(s, OPC_ST_B, a0, a1, a2); @@ -2509,18 +2547,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i128: return C_O0_I3(r, r, r); - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld_i32: - case INDEX_op_ld_i64: case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 9455a0a17b..21ed11b78d 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2274,6 +2274,74 @@ static const TCGOutOpExtract2 outop_extract2 = { .base.static_constraint = C_NotImplemented, }; +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LBU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LB, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LHU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LH, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +#if TCG_TARGET_REG_BITS == 64 +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LWU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LW, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; +#endif + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2287,32 +2355,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a2 = args[2]; switch (opc) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - i1 = OPC_LBU; - goto do_ldst; - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - i1 = OPC_LB; - goto do_ldst; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - i1 = OPC_LHU; - goto do_ldst; - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - i1 = OPC_LH; - goto do_ldst; - case INDEX_op_ld_i32: - case INDEX_op_ld32s_i64: - i1 = OPC_LW; - goto do_ldst; - case INDEX_op_ld32u_i64: - i1 = OPC_LWU; - goto do_ldst; - case INDEX_op_ld_i64: - i1 = OPC_LD; - goto do_ldst; case INDEX_op_st8_i32: case INDEX_op_st8_i64: i1 = OPC_SB; @@ -2365,20 +2407,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld_i64: - return C_O1_I1(r, r); - case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index a2a5b1e570..275c5a90a5 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3677,39 +3677,81 @@ static const TCGOutOpExtract2 outop_extract2 = { .base.static_constraint = C_NotImplemented, }; +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, LBZ, LBZX, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tgen_ld8u(s, type, dest, base, offset); + tcg_out_ext8s(s, type, dest, dest); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, LHZ, LHZX, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, LHA, LHAX, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +#if TCG_TARGET_REG_BITS == 64 +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, LWZ, LWZX, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, LWA, LWAX, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; +#endif + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); - break; - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - tcg_out_mem_long(s, LBZ, LBZX, args[0], args[1], args[2]); - tcg_out_ext8s(s, TCG_TYPE_REG, args[0], args[0]); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - tcg_out_mem_long(s, LHZ, LHZX, args[0], args[1], args[2]); - break; - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - tcg_out_mem_long(s, LHA, LHAX, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - tcg_out_mem_long(s, LWZ, LWZX, args[0], args[1], args[2]); - break; - case INDEX_op_ld32s_i64: - tcg_out_mem_long(s, LWA, LWAX, args[0], args[1], args[2]); - break; - case INDEX_op_ld_i64: - tcg_out_mem_long(s, LD, LDX, args[0], args[1], args[2]); - break; case INDEX_op_st8_i32: case INDEX_op_st8_i64: tcg_out_mem_long(s, STB, STBX, args[0], args[1], args[2]); @@ -4373,20 +4415,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - return C_O1_I1(r, r); - case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index c1bfd93569..5b987c930f 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2530,6 +2530,72 @@ static const TCGOutOpExtract2 outop_extract2 = { .base.static_constraint = C_NotImplemented, }; +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LBU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LB, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LHU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LH, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LWU, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_LW, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2540,33 +2606,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a2 = args[2]; switch (opc) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - tcg_out_ldst(s, OPC_LBU, a0, a1, a2); - break; - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - tcg_out_ldst(s, OPC_LB, a0, a1, a2); - break; - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - tcg_out_ldst(s, OPC_LHU, a0, a1, a2); - break; - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - tcg_out_ldst(s, OPC_LH, a0, a1, a2); - break; - case INDEX_op_ld32u_i64: - tcg_out_ldst(s, OPC_LWU, a0, a1, a2); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32s_i64: - tcg_out_ldst(s, OPC_LW, a0, a1, a2); - break; - case INDEX_op_ld_i64: - tcg_out_ldst(s, OPC_LD, a0, a1, a2); - break; - case INDEX_op_st8_i32: case INDEX_op_st8_i64: tcg_out_ldst(s, OPC_SB, a0, a1, a2); @@ -2825,20 +2864,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld_i64: - return C_O1_I1(r, r); - case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 2b2e00c609..fe7665b21d 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -3030,6 +3030,76 @@ static void tcg_out_mb(TCGContext *s, unsigned a0) } } +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, 0, RXY_LLGC, dest, base, TCG_REG_NONE, offset); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, 0, RXY_LGB, dest, base, TCG_REG_NONE, offset); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, 0, RXY_LLGH, dest, base, TCG_REG_NONE, offset); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + if (type == TCG_TYPE_I32) { + tcg_out_mem(s, RX_LH, RXY_LHY, dest, base, TCG_REG_NONE, offset); + } else { + tcg_out_mem(s, 0, RXY_LGH, dest, base, TCG_REG_NONE, offset); + } +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, 0, RXY_LLGF, dest, base, TCG_REG_NONE, offset); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, 0, RXY_LGF, dest, base, TCG_REG_NONE, offset); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; + # define OP_32_64(x) \ case glue(glue(INDEX_op_,x),_i32): \ case glue(glue(INDEX_op_,x),_i64) @@ -3039,31 +3109,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - OP_32_64(ld8u): - /* ??? LLC (RXY format) is only present with the extended-immediate - facility, whereas LLGC is always present. */ - tcg_out_mem(s, 0, RXY_LLGC, args[0], args[1], TCG_REG_NONE, args[2]); - break; - - OP_32_64(ld8s): - /* ??? LB is no smaller than LGB, so no point to using it. */ - tcg_out_mem(s, 0, RXY_LGB, args[0], args[1], TCG_REG_NONE, args[2]); - break; - - OP_32_64(ld16u): - /* ??? LLH (RXY format) is only present with the extended-immediate - facility, whereas LLGH is always present. */ - tcg_out_mem(s, 0, RXY_LLGH, args[0], args[1], TCG_REG_NONE, args[2]); - break; - - case INDEX_op_ld16s_i32: - tcg_out_mem(s, RX_LH, RXY_LHY, args[0], args[1], TCG_REG_NONE, args[2]); - break; - - case INDEX_op_ld_i32: - tcg_out_ld(s, TCG_TYPE_I32, args[0], args[1], args[2]); - break; - OP_32_64(st8): tcg_out_mem(s, RX_STC, RXY_STCY, args[0], args[1], TCG_REG_NONE, args[2]); @@ -3097,19 +3142,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; - case INDEX_op_ld16s_i64: - tcg_out_mem(s, 0, RXY_LGH, args[0], args[1], TCG_REG_NONE, args[2]); - break; - case INDEX_op_ld32u_i64: - tcg_out_mem(s, 0, RXY_LLGF, args[0], args[1], TCG_REG_NONE, args[2]); - break; - case INDEX_op_ld32s_i64: - tcg_out_mem(s, 0, RXY_LGF, args[0], args[1], TCG_REG_NONE, args[2]); - break; - case INDEX_op_ld_i64: - tcg_out_ld(s, TCG_TYPE_I64, args[0], args[1], args[2]); - break; - case INDEX_op_st32_i64: tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); break; @@ -3565,20 +3597,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - return C_O1_I1(r, r); - case INDEX_op_st8_i32: case INDEX_op_st8_i64: case INDEX_op_st16_i32: diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 208b96487e..59a737dde4 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1962,6 +1962,73 @@ static const TCGOutOpExtract2 outop_extract2 = { .base.static_constraint = C_NotImplemented, }; +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, dest, base, offset, LDUB); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, dest, base, offset, LDSB); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, dest, base, offset, LDUH); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, dest, base, offset, LDSH); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, dest, base, offset, LDUW); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, dest, base, offset, LDSW); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1978,22 +2045,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, glue(glue(case INDEX_op_, x), _i32): \ glue(glue(case INDEX_op_, x), _i64) - OP_32_64(ld8u): - tcg_out_ldst(s, a0, a1, a2, LDUB); - break; - OP_32_64(ld8s): - tcg_out_ldst(s, a0, a1, a2, LDSB); - break; - OP_32_64(ld16u): - tcg_out_ldst(s, a0, a1, a2, LDUH); - break; - OP_32_64(ld16s): - tcg_out_ldst(s, a0, a1, a2, LDSH); - break; - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - tcg_out_ldst(s, a0, a1, a2, LDUW); - break; OP_32_64(st8): tcg_out_ldst(s, a0, a1, a2, STB); break; @@ -2018,12 +2069,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); break; - case INDEX_op_ld32s_i64: - tcg_out_ldst(s, a0, a1, a2, LDSW); - break; - case INDEX_op_ld_i64: - tcg_out_ldst(s, a0, a1, a2, LDX); - break; case INDEX_op_st_i64: tcg_out_ldst(s, a0, a1, a2, STX); break; @@ -2040,18 +2085,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - case INDEX_op_ld_i32: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); diff --git a/tcg/tcg.c b/tcg/tcg.c index 5ab4f5e752..4cff888b7e 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1052,6 +1052,12 @@ typedef struct TCGOutOpExtract2 { TCGReg a2, unsigned shr); } TCGOutOpExtract2; +typedef struct TCGOutOpLoad { + TCGOutOp base; + void (*out)(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, intptr_t offset); +} TCGOutOpLoad; + typedef struct TCGOutOpMovcond { TCGOutOp base; void (*out)(TCGContext *s, TCGType type, TCGCond cond, @@ -1142,6 +1148,11 @@ static const TCGOutOp outop_goto_ptr = { .static_constraint = C_O0_I1(r), }; +static const TCGOutOpLoad outop_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tcg_out_ld, +}; + /* * Register V as the TCGOutOp for O. * This verifies that V is of type T, otherwise give a nice compiler error. @@ -1173,6 +1184,16 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_extract, TCGOutOpExtract, outop_extract), OUTOP(INDEX_op_extract2, TCGOutOpExtract2, outop_extract2), + OUTOP(INDEX_op_ld8u_i32, TCGOutOpLoad, outop_ld8u), + OUTOP(INDEX_op_ld8u_i64, TCGOutOpLoad, outop_ld8u), + OUTOP(INDEX_op_ld8s_i32, TCGOutOpLoad, outop_ld8s), + OUTOP(INDEX_op_ld8s_i64, TCGOutOpLoad, outop_ld8s), + OUTOP(INDEX_op_ld16u_i32, TCGOutOpLoad, outop_ld16u), + OUTOP(INDEX_op_ld16u_i64, TCGOutOpLoad, outop_ld16u), + OUTOP(INDEX_op_ld16s_i32, TCGOutOpLoad, outop_ld16s), + OUTOP(INDEX_op_ld16s_i64, TCGOutOpLoad, outop_ld16s), + OUTOP(INDEX_op_ld_i32, TCGOutOpLoad, outop_ld), + OUTOP(INDEX_op_ld_i64, TCGOutOpLoad, outop_ld), OUTOP(INDEX_op_movcond, TCGOutOpMovcond, outop_movcond), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_muls2, TCGOutOpMul2, outop_muls2), @@ -1214,6 +1235,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_extu_i32_i64, TCGOutOpUnary, outop_extu_i32_i64), OUTOP(INDEX_op_extrl_i64_i32, TCGOutOpUnary, outop_extrl_i64_i32), OUTOP(INDEX_op_extrh_i64_i32, TCGOutOpUnary, outop_extrh_i64_i32), + OUTOP(INDEX_op_ld32u_i64, TCGOutOpLoad, outop_ld32u), + OUTOP(INDEX_op_ld32s_i64, TCGOutOpLoad, outop_ld32s), #endif }; @@ -5740,6 +5763,29 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_ld32u_i64: + case INDEX_op_ld32s_i64: + tcg_debug_assert(type == TCG_TYPE_I64); + /* fall through */ + case INDEX_op_ld8u_i32: + case INDEX_op_ld8u_i64: + case INDEX_op_ld8s_i32: + case INDEX_op_ld8s_i64: + case INDEX_op_ld16u_i32: + case INDEX_op_ld16u_i64: + case INDEX_op_ld16s_i32: + case INDEX_op_ld16s_i64: + case INDEX_op_ld_i32: + case INDEX_op_ld_i64: + { + const TCGOutOpLoad *out = + container_of(all_outop[op->opc], TCGOutOpLoad, base); + + tcg_debug_assert(!const_args[1]); + out->out(s, type, new_args[0], new_args[1], new_args[2]); + } + break; + case INDEX_op_muls2: case INDEX_op_mulu2: { diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index d9cd62ed3d..2dcd561b77 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -40,20 +40,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: - return C_O1_I1(r, r); - case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: @@ -1143,19 +1129,80 @@ static void tcg_out_br(TCGContext *s, TCGLabel *l) tcg_out_op_l(s, INDEX_op_br, l); } +static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_ld8u_i32, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8u, +}; + +static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_ld8s_i32, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld8s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld8s, +}; + +static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_ld16u_i32, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16u, +}; + +static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_ld16s_i32, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld16s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld16s, +}; + +#if TCG_TARGET_REG_BITS == 64 +static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_ld32u_i64, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32u = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32u, +}; + +static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_ld32s_i64, dest, base, offset); +} + +static const TCGOutOpLoad outop_ld32s = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_ld32s, +}; +#endif + + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - CASE_32_64(ld8u) - CASE_32_64(ld8s) - CASE_32_64(ld16u) - CASE_32_64(ld16s) - case INDEX_op_ld_i32: - CASE_64(ld32u) - CASE_64(ld32s) - CASE_64(ld) CASE_32_64(st8) CASE_32_64(st16) case INDEX_op_st_i32: From e996804d40c10572550a1d3ca936a5dfb29ca0fc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 21 Jan 2025 21:47:16 -0800 Subject: [PATCH 0506/2760] tcg: Merge INDEX_op_ld*_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/tcg/tcg-opc.h | 19 +++++------- tcg/optimize.c | 27 ++++++++--------- tcg/tcg-op.c | 24 +++++++-------- tcg/tcg.c | 64 ++++++++++++++-------------------------- tcg/tci.c | 39 ++++++++++-------------- tcg/tci/tcg-target.c.inc | 28 +++++++----------- 6 files changed, 81 insertions(+), 120 deletions(-) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 30ba15723a..6e8fcefaef 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -57,6 +57,13 @@ DEF(divu2, 2, 3, 0, TCG_OPF_INT) DEF(eqv, 1, 2, 0, TCG_OPF_INT) DEF(extract, 1, 1, 2, TCG_OPF_INT) DEF(extract2, 1, 2, 1, TCG_OPF_INT) +DEF(ld8u, 1, 1, 1, TCG_OPF_INT) +DEF(ld8s, 1, 1, 1, TCG_OPF_INT) +DEF(ld16u, 1, 1, 1, TCG_OPF_INT) +DEF(ld16s, 1, 1, 1, TCG_OPF_INT) +DEF(ld32u, 1, 1, 1, TCG_OPF_INT) +DEF(ld32s, 1, 1, 1, TCG_OPF_INT) +DEF(ld, 1, 1, 1, TCG_OPF_INT) DEF(movcond, 1, 4, 1, TCG_OPF_INT) DEF(mul, 1, 2, 0, TCG_OPF_INT) DEF(muls2, 2, 2, 0, TCG_OPF_INT) @@ -93,11 +100,6 @@ DEF(subbi, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_IN) DEF(subbio, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_IN | TCG_OPF_CARRY_OUT) /* load/store */ -DEF(ld8u_i32, 1, 1, 1, 0) -DEF(ld8s_i32, 1, 1, 1, 0) -DEF(ld16u_i32, 1, 1, 1, 0) -DEF(ld16s_i32, 1, 1, 1, 0) -DEF(ld_i32, 1, 1, 1, 0) DEF(st8_i32, 0, 2, 1, 0) DEF(st16_i32, 0, 2, 1, 0) DEF(st_i32, 0, 2, 1, 0) @@ -106,13 +108,6 @@ DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(setcond2_i32, 1, 4, 1, 0) /* load/store */ -DEF(ld8u_i64, 1, 1, 1, 0) -DEF(ld8s_i64, 1, 1, 1, 0) -DEF(ld16u_i64, 1, 1, 1, 0) -DEF(ld16s_i64, 1, 1, 1, 0) -DEF(ld32u_i64, 1, 1, 1, 0) -DEF(ld32s_i64, 1, 1, 1, 0) -DEF(ld_i64, 1, 1, 1, 0) DEF(st8_i64, 0, 2, 1, 0) DEF(st16_i64, 0, 2, 1, 0) DEF(st32_i64, 0, 2, 1, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index 52e194aaa9..d928a38e14 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2880,22 +2880,22 @@ static bool fold_tcg_ld(OptContext *ctx, TCGOp *op) /* We can't do any folding with a load, but we can record bits. */ switch (op->opc) { - CASE_OP_32_64(ld8s): + case INDEX_op_ld8s: s_mask = INT8_MIN; break; - CASE_OP_32_64(ld8u): + case INDEX_op_ld8u: z_mask = MAKE_64BIT_MASK(0, 8); break; - CASE_OP_32_64(ld16s): + case INDEX_op_ld16s: s_mask = INT16_MIN; break; - CASE_OP_32_64(ld16u): + case INDEX_op_ld16u: z_mask = MAKE_64BIT_MASK(0, 16); break; - case INDEX_op_ld32s_i64: + case INDEX_op_ld32s: s_mask = INT32_MIN; break; - case INDEX_op_ld32u_i64: + case INDEX_op_ld32u: z_mask = MAKE_64BIT_MASK(0, 32); break; default: @@ -3126,16 +3126,15 @@ void tcg_optimize(TCGContext *s) case INDEX_op_extrh_i64_i32: done = fold_extu(&ctx, op); break; - CASE_OP_32_64(ld8s): - CASE_OP_32_64(ld8u): - CASE_OP_32_64(ld16s): - CASE_OP_32_64(ld16u): - case INDEX_op_ld32s_i64: - case INDEX_op_ld32u_i64: + case INDEX_op_ld8s: + case INDEX_op_ld8u: + case INDEX_op_ld16s: + case INDEX_op_ld16u: + case INDEX_op_ld32s: + case INDEX_op_ld32u: done = fold_tcg_ld(&ctx, op); break; - case INDEX_op_ld_i32: - case INDEX_op_ld_i64: + case INDEX_op_ld: case INDEX_op_ld_vec: done = fold_tcg_ld_memcopy(&ctx, op); break; diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index b0139ce05d..680f752cf9 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1379,27 +1379,27 @@ void tcg_gen_abs_i32(TCGv_i32 ret, TCGv_i32 a) void tcg_gen_ld8u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ldst_op_i32(INDEX_op_ld8u_i32, ret, arg2, offset); + tcg_gen_ldst_op_i32(INDEX_op_ld8u, ret, arg2, offset); } void tcg_gen_ld8s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ldst_op_i32(INDEX_op_ld8s_i32, ret, arg2, offset); + tcg_gen_ldst_op_i32(INDEX_op_ld8s, ret, arg2, offset); } void tcg_gen_ld16u_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ldst_op_i32(INDEX_op_ld16u_i32, ret, arg2, offset); + tcg_gen_ldst_op_i32(INDEX_op_ld16u, ret, arg2, offset); } void tcg_gen_ld16s_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ldst_op_i32(INDEX_op_ld16s_i32, ret, arg2, offset); + tcg_gen_ldst_op_i32(INDEX_op_ld16s, ret, arg2, offset); } void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ldst_op_i32(INDEX_op_ld_i32, ret, arg2, offset); + tcg_gen_ldst_op_i32(INDEX_op_ld, ret, arg2, offset); } void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) @@ -1463,7 +1463,7 @@ void tcg_gen_movi_i64(TCGv_i64 ret, int64_t arg) void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_ldst_op_i64(INDEX_op_ld8u_i64, ret, arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_ld8u, ret, arg2, offset); } else { tcg_gen_ld8u_i32(TCGV_LOW(ret), arg2, offset); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); @@ -1473,7 +1473,7 @@ void tcg_gen_ld8u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_ldst_op_i64(INDEX_op_ld8s_i64, ret, arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_ld8s, ret, arg2, offset); } else { tcg_gen_ld8s_i32(TCGV_LOW(ret), arg2, offset); tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); @@ -1483,7 +1483,7 @@ void tcg_gen_ld8s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_ldst_op_i64(INDEX_op_ld16u_i64, ret, arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_ld16u, ret, arg2, offset); } else { tcg_gen_ld16u_i32(TCGV_LOW(ret), arg2, offset); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); @@ -1493,7 +1493,7 @@ void tcg_gen_ld16u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_ldst_op_i64(INDEX_op_ld16s_i64, ret, arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_ld16s, ret, arg2, offset); } else { tcg_gen_ld16s_i32(TCGV_LOW(ret), arg2, offset); tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); @@ -1503,7 +1503,7 @@ void tcg_gen_ld16s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_ldst_op_i64(INDEX_op_ld32u_i64, ret, arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_ld32u, ret, arg2, offset); } else { tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); tcg_gen_movi_i32(TCGV_HIGH(ret), 0); @@ -1513,7 +1513,7 @@ void tcg_gen_ld32u_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_ld32s_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_ldst_op_i64(INDEX_op_ld32s_i64, ret, arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_ld32s, ret, arg2, offset); } else { tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset); tcg_gen_sari_i32(TCGV_HIGH(ret), TCGV_LOW(ret), 31); @@ -1527,7 +1527,7 @@ void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) * they cannot be the same temporary -- no chance of overlap. */ if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_ldst_op_i64(INDEX_op_ld_i64, ret, arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_ld, ret, arg2, offset); } else if (HOST_BIG_ENDIAN) { tcg_gen_ld_i32(TCGV_HIGH(ret), arg2, offset); tcg_gen_ld_i32(TCGV_LOW(ret), arg2, offset + 4); diff --git a/tcg/tcg.c b/tcg/tcg.c index 4cff888b7e..a9d62d9e17 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1184,16 +1184,11 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_eqv, TCGOutOpBinary, outop_eqv), OUTOP(INDEX_op_extract, TCGOutOpExtract, outop_extract), OUTOP(INDEX_op_extract2, TCGOutOpExtract2, outop_extract2), - OUTOP(INDEX_op_ld8u_i32, TCGOutOpLoad, outop_ld8u), - OUTOP(INDEX_op_ld8u_i64, TCGOutOpLoad, outop_ld8u), - OUTOP(INDEX_op_ld8s_i32, TCGOutOpLoad, outop_ld8s), - OUTOP(INDEX_op_ld8s_i64, TCGOutOpLoad, outop_ld8s), - OUTOP(INDEX_op_ld16u_i32, TCGOutOpLoad, outop_ld16u), - OUTOP(INDEX_op_ld16u_i64, TCGOutOpLoad, outop_ld16u), - OUTOP(INDEX_op_ld16s_i32, TCGOutOpLoad, outop_ld16s), - OUTOP(INDEX_op_ld16s_i64, TCGOutOpLoad, outop_ld16s), - OUTOP(INDEX_op_ld_i32, TCGOutOpLoad, outop_ld), - OUTOP(INDEX_op_ld_i64, TCGOutOpLoad, outop_ld), + OUTOP(INDEX_op_ld8u, TCGOutOpLoad, outop_ld8u), + OUTOP(INDEX_op_ld8s, TCGOutOpLoad, outop_ld8s), + OUTOP(INDEX_op_ld16u, TCGOutOpLoad, outop_ld16u), + OUTOP(INDEX_op_ld16s, TCGOutOpLoad, outop_ld16s), + OUTOP(INDEX_op_ld, TCGOutOpLoad, outop_ld), OUTOP(INDEX_op_movcond, TCGOutOpMovcond, outop_movcond), OUTOP(INDEX_op_mul, TCGOutOpBinary, outop_mul), OUTOP(INDEX_op_muls2, TCGOutOpMul2, outop_muls2), @@ -1235,8 +1230,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_extu_i32_i64, TCGOutOpUnary, outop_extu_i32_i64), OUTOP(INDEX_op_extrl_i64_i32, TCGOutOpUnary, outop_extrl_i64_i32), OUTOP(INDEX_op_extrh_i64_i32, TCGOutOpUnary, outop_extrh_i64_i32), - OUTOP(INDEX_op_ld32u_i64, TCGOutOpLoad, outop_ld32u), - OUTOP(INDEX_op_ld32s_i64, TCGOutOpLoad, outop_ld32s), + OUTOP(INDEX_op_ld32u, TCGOutOpLoad, outop_ld32u), + OUTOP(INDEX_op_ld32s, TCGOutOpLoad, outop_ld32s), #endif }; @@ -2443,6 +2438,11 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_brcond: case INDEX_op_deposit: case INDEX_op_extract: + case INDEX_op_ld8u: + case INDEX_op_ld8s: + case INDEX_op_ld16u: + case INDEX_op_ld16s: + case INDEX_op_ld: case INDEX_op_mov: case INDEX_op_movcond: case INDEX_op_negsetcond: @@ -2452,11 +2452,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_xor: return has_type; - case INDEX_op_ld8u_i32: - case INDEX_op_ld8s_i32: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16s_i32: - case INDEX_op_ld_i32: case INDEX_op_st8_i32: case INDEX_op_st16_i32: case INDEX_op_st_i32: @@ -2466,13 +2461,8 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_setcond2_i32: return TCG_TARGET_REG_BITS == 32; - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i64: + case INDEX_op_ld32u: + case INDEX_op_ld32s: case INDEX_op_st8_i64: case INDEX_op_st16_i64: case INDEX_op_st32_i64: @@ -4428,10 +4418,7 @@ liveness_pass_2(TCGContext *s) arg_ts = arg_temp(op->args[i]); dir_ts = arg_ts->state_ptr; if (dir_ts && arg_ts->state == TS_DEAD) { - TCGOpcode lopc = (arg_ts->type == TCG_TYPE_I32 - ? INDEX_op_ld_i32 - : INDEX_op_ld_i64); - TCGOp *lop = tcg_op_insert_before(s, op, lopc, + TCGOp *lop = tcg_op_insert_before(s, op, INDEX_op_ld, arg_ts->type, 3); lop->args[0] = temp_arg(dir_ts); @@ -5763,20 +5750,13 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - tcg_debug_assert(type == TCG_TYPE_I64); - /* fall through */ - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - case INDEX_op_ld_i32: - case INDEX_op_ld_i64: + case INDEX_op_ld8u: + case INDEX_op_ld8s: + case INDEX_op_ld16u: + case INDEX_op_ld16s: + case INDEX_op_ld32u: + case INDEX_op_ld32s: + case INDEX_op_ld: { const TCGOutOpLoad *out = container_of(all_outop[op->opc], TCGOutOpLoad, base); diff --git a/tcg/tci.c b/tcg/tci.c index a18478a07a..890ccbe85b 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -466,31 +466,30 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, /* Load/store operations (32 bit). */ - CASE_32_64(ld8u) + case INDEX_op_ld8u: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); regs[r0] = *(uint8_t *)ptr; break; - CASE_32_64(ld8s) + case INDEX_op_ld8s: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); regs[r0] = *(int8_t *)ptr; break; - CASE_32_64(ld16u) + case INDEX_op_ld16u: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); regs[r0] = *(uint16_t *)ptr; break; - CASE_32_64(ld16s) + case INDEX_op_ld16s: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); regs[r0] = *(int16_t *)ptr; break; - case INDEX_op_ld_i32: - CASE_64(ld32u) + case INDEX_op_ld: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); - regs[r0] = *(uint32_t *)ptr; + regs[r0] = *(tcg_target_ulong *)ptr; break; CASE_32_64(st8) tci_args_rrs(insn, &r0, &r1, &ofs); @@ -716,15 +715,15 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, #if TCG_TARGET_REG_BITS == 64 /* Load/store operations (64 bit). */ - case INDEX_op_ld32s_i64: + case INDEX_op_ld32u: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); - regs[r0] = *(int32_t *)ptr; + regs[r0] = *(uint32_t *)ptr; break; - case INDEX_op_ld_i64: + case INDEX_op_ld32s: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); - regs[r0] = *(uint64_t *)ptr; + regs[r0] = *(int32_t *)ptr; break; case INDEX_op_st_i64: tci_args_rrs(insn, &r0, &r1, &ofs); @@ -970,18 +969,12 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) info->fprintf_func(info->stream, "%-12s", op_name); break; - case INDEX_op_ld8u_i32: - case INDEX_op_ld8u_i64: - case INDEX_op_ld8s_i32: - case INDEX_op_ld8s_i64: - case INDEX_op_ld16u_i32: - case INDEX_op_ld16u_i64: - case INDEX_op_ld16s_i32: - case INDEX_op_ld16s_i64: - case INDEX_op_ld32u_i64: - case INDEX_op_ld32s_i64: - case INDEX_op_ld_i32: - case INDEX_op_ld_i64: + case INDEX_op_ld8u: + case INDEX_op_ld8s: + case INDEX_op_ld16u: + case INDEX_op_ld16s: + case INDEX_op_ld32u: + case INDEX_op_ld: case INDEX_op_st8_i32: case INDEX_op_st8_i64: case INDEX_op_st16_i32: diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 2dcd561b77..d549dc90f5 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -339,18 +339,12 @@ static void tcg_out_ldst(TCGContext *s, TCGOpcode op, TCGReg val, static void tcg_out_ld(TCGContext *s, TCGType type, TCGReg val, TCGReg base, intptr_t offset) { - switch (type) { - case TCG_TYPE_I32: - tcg_out_ldst(s, INDEX_op_ld_i32, val, base, offset); - break; -#if TCG_TARGET_REG_BITS == 64 - case TCG_TYPE_I64: - tcg_out_ldst(s, INDEX_op_ld_i64, val, base, offset); - break; -#endif - default: - g_assert_not_reached(); + TCGOpcode op = INDEX_op_ld; + + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + op = INDEX_op_ld32u; } + tcg_out_ldst(s, op, val, base, offset); } static bool tcg_out_mov(TCGContext *s, TCGType type, TCGReg ret, TCGReg arg) @@ -1132,7 +1126,7 @@ static void tcg_out_br(TCGContext *s, TCGLabel *l) static void tgen_ld8u(TCGContext *s, TCGType type, TCGReg dest, TCGReg base, ptrdiff_t offset) { - tcg_out_ldst(s, INDEX_op_ld8u_i32, dest, base, offset); + tcg_out_ldst(s, INDEX_op_ld8u, dest, base, offset); } static const TCGOutOpLoad outop_ld8u = { @@ -1143,7 +1137,7 @@ static const TCGOutOpLoad outop_ld8u = { static void tgen_ld8s(TCGContext *s, TCGType type, TCGReg dest, TCGReg base, ptrdiff_t offset) { - tcg_out_ldst(s, INDEX_op_ld8s_i32, dest, base, offset); + tcg_out_ldst(s, INDEX_op_ld8s, dest, base, offset); } static const TCGOutOpLoad outop_ld8s = { @@ -1154,7 +1148,7 @@ static const TCGOutOpLoad outop_ld8s = { static void tgen_ld16u(TCGContext *s, TCGType type, TCGReg dest, TCGReg base, ptrdiff_t offset) { - tcg_out_ldst(s, INDEX_op_ld16u_i32, dest, base, offset); + tcg_out_ldst(s, INDEX_op_ld16u, dest, base, offset); } static const TCGOutOpLoad outop_ld16u = { @@ -1165,7 +1159,7 @@ static const TCGOutOpLoad outop_ld16u = { static void tgen_ld16s(TCGContext *s, TCGType type, TCGReg dest, TCGReg base, ptrdiff_t offset) { - tcg_out_ldst(s, INDEX_op_ld16s_i32, dest, base, offset); + tcg_out_ldst(s, INDEX_op_ld16s, dest, base, offset); } static const TCGOutOpLoad outop_ld16s = { @@ -1177,7 +1171,7 @@ static const TCGOutOpLoad outop_ld16s = { static void tgen_ld32u(TCGContext *s, TCGType type, TCGReg dest, TCGReg base, ptrdiff_t offset) { - tcg_out_ldst(s, INDEX_op_ld32u_i64, dest, base, offset); + tcg_out_ldst(s, INDEX_op_ld32u, dest, base, offset); } static const TCGOutOpLoad outop_ld32u = { @@ -1188,7 +1182,7 @@ static const TCGOutOpLoad outop_ld32u = { static void tgen_ld32s(TCGContext *s, TCGType type, TCGReg dest, TCGReg base, ptrdiff_t offset) { - tcg_out_ldst(s, INDEX_op_ld32s_i64, dest, base, offset); + tcg_out_ldst(s, INDEX_op_ld32s, dest, base, offset); } static const TCGOutOpLoad outop_ld32s = { From 4a686aa9d9dcf8805de654ae09788c4e264c1439 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 22 Jan 2025 12:49:41 -0800 Subject: [PATCH 0507/2760] tcg: Convert st to TCGOutOpStore Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 52 +++++++------- tcg/arm/tcg-target.c.inc | 72 +++++++++---------- tcg/i386/tcg-target.c.inc | 114 ++++++++++++++----------------- tcg/loongarch64/tcg-target.c.inc | 50 +++++++------- tcg/mips/tcg-target.c.inc | 55 ++++++++------- tcg/ppc/tcg-target.c.inc | 52 +++++++------- tcg/riscv/tcg-target.c.inc | 52 +++++++------- tcg/s390x/tcg-target.c.inc | 60 ++++++++-------- tcg/sparc64/tcg-target.c.inc | 53 +++++++------- tcg/tcg.c | 37 ++++++++++ tcg/tci/tcg-target.c.inc | 56 ++++++++------- 11 files changed, 341 insertions(+), 312 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 903a95ad7e..39a44507d1 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2838,6 +2838,33 @@ static const TCGOutOpLoad outop_ld32s = { .out = tgen_ld32s, }; +static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, I3312_STRB, data, base, offset, 0); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st8_r, +}; + +static void tgen_st16_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, I3312_STRH, data, base, offset, 1); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st16_r, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tcg_out_st, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -2848,22 +2875,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, TCGArg a2 = args[2]; switch (opc) { - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - tcg_out_ldst(s, I3312_STRB, a0, a1, a2, 0); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - tcg_out_ldst(s, I3312_STRH, a0, a1, a2, 1); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_ldst(s, I3312_STRW, a0, a1, a2, 2); - break; - case INDEX_op_st_i64: - tcg_out_ldst(s, I3312_STRX, a0, a1, a2, 3); - break; - case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: tcg_out_qemu_ld(s, a0, a1, a2, ext); @@ -3331,15 +3342,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(rz, r); - case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 2079dd3bdc..5b34f61ca1 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1084,26 +1084,6 @@ static void tcg_out_st32(TCGContext *s, ARMCond cond, tcg_out_st32_12(s, cond, rd, rn, offset); } -static void tcg_out_st16(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int32_t offset) -{ - if (offset > 0xff || offset < -0xff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_st16_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_st16_8(s, cond, rd, rn, offset); -} - -static void tcg_out_st8(TCGContext *s, ARMCond cond, - TCGReg rd, TCGReg rn, int32_t offset) -{ - if (offset > 0xfff || offset < -0xfff) { - tcg_out_movi32(s, cond, TCG_REG_TMP, offset); - tcg_out_st8_r(s, cond, rd, rn, TCG_REG_TMP); - } else - tcg_out_st8_12(s, cond, rd, rn, offset); -} - /* * The _goto case is normally between TBs within the same code buffer, and * with the code buffer limited to 16MB we wouldn't need the long case. @@ -2548,21 +2528,48 @@ static const TCGOutOpLoad outop_ld16s = { .out = tgen_ld16s, }; +static void tgen_st8(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rn, ptrdiff_t offset) +{ + if (offset > 0xfff || offset < -0xfff) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, offset); + tcg_out_st8_r(s, COND_AL, rd, rn, TCG_REG_TMP); + } else { + tcg_out_st8_12(s, COND_AL, rd, rn, offset); + } +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st8, +}; + +static void tgen_st16(TCGContext *s, TCGType type, TCGReg rd, + TCGReg rn, ptrdiff_t offset) +{ + if (offset > 0xff || offset < -0xff) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, offset); + tcg_out_st16_r(s, COND_AL, rd, rn, TCG_REG_TMP); + } else { + tcg_out_st16_8(s, COND_AL, rd, rn, offset); + } +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st16, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tcg_out_st, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_st8_i32: - tcg_out_st8(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_st16_i32: - tcg_out_st16(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_st_i32: - tcg_out_st32(s, COND_AL, args[0], args[1], args[2]); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; @@ -2589,11 +2596,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - return C_O0_I2(r, r); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, q); case INDEX_op_qemu_ld_i64: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index d16ddcb940..52285bcd54 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3489,55 +3489,69 @@ static const TCGOutOpLoad outop_ld32s = { }; #endif +static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVB_EvGv | P_REXB_R, data, base, offset); +} + +static void tgen_st8_i(TCGContext *s, TCGType type, tcg_target_long data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVB_EvIz, 0, base, offset); + tcg_out8(s, data); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(qi, r), + .out_r = tgen_st8_r, + .out_i = tgen_st8_i, +}; + +static void tgen_st16_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVL_EvGv | P_DATA16, data, base, offset); +} + +static void tgen_st16_i(TCGContext *s, TCGType type, tcg_target_long data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_modrm_offset(s, OPC_MOVL_EvIz | P_DATA16, 0, base, offset); + tcg_out16(s, data); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(ri, r), + .out_r = tgen_st16_r, + .out_i = tgen_st16_i, +}; + +static void tgen_st_i(TCGContext *s, TCGType type, tcg_target_long data, + TCGReg base, ptrdiff_t offset) +{ + bool ok = tcg_out_sti(s, type, data, base, offset); + tcg_debug_assert(ok); +} + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(re, r), + .out_r = tcg_out_st, + .out_i = tgen_st_i, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { TCGArg a0, a1, a2; -#if TCG_TARGET_REG_BITS == 64 -# define OP_32_64(x) \ - case glue(glue(INDEX_op_, x), _i64): \ - case glue(glue(INDEX_op_, x), _i32) -#else -# define OP_32_64(x) \ - case glue(glue(INDEX_op_, x), _i32) -#endif - /* Hoist the loads of the most common arguments. */ a0 = args[0]; a1 = args[1]; a2 = args[2]; switch (opc) { - OP_32_64(st8): - if (const_args[0]) { - tcg_out_modrm_offset(s, OPC_MOVB_EvIz, 0, a1, a2); - tcg_out8(s, a0); - } else { - tcg_out_modrm_offset(s, OPC_MOVB_EvGv | P_REXB_R, a0, a1, a2); - } - break; - OP_32_64(st16): - if (const_args[0]) { - tcg_out_modrm_offset(s, OPC_MOVL_EvIz | P_DATA16, 0, a1, a2); - tcg_out16(s, a0); - } else { - tcg_out_modrm_offset(s, OPC_MOVL_EvGv | P_DATA16, a0, a1, a2); - } - break; -#if TCG_TARGET_REG_BITS == 64 - case INDEX_op_st32_i64: -#endif - case INDEX_op_st_i32: - if (const_args[0]) { - tcg_out_modrm_offset(s, OPC_MOVL_EvIz, 0, a1, a2); - tcg_out32(s, a0); - } else { - tcg_out_st(s, TCG_TYPE_I32, a0, a1, a2); - } - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, -1, a1, a2, TCG_TYPE_I32); break; @@ -3569,25 +3583,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_st(s, a0, a1, a2, args[3], TCG_TYPE_I128); break; -#if TCG_TARGET_REG_BITS == 64 - case INDEX_op_st_i64: - if (const_args[0]) { - tcg_out_modrm_offset(s, OPC_MOVL_EvIz | P_REXW, 0, a1, a2); - tcg_out32(s, a0); - } else { - tcg_out_st(s, TCG_TYPE_I64, a0, a1, a2); - } - break; -#endif - case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ default: g_assert_not_reached(); } - -#undef OP_32_64 } static int const umin_insn[4] = { @@ -4135,19 +4136,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - return C_O0_I2(qi, r); - - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - return C_O0_I2(ri, r); - - case INDEX_op_st_i64: - return C_O0_I2(re, r); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, L); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 66555b8982..73a1196d8b 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1983,6 +1983,33 @@ static const TCGOutOpLoad outop_ld32s = { .out = tgen_ld32s, }; +static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_ST_B, data, base, offset); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st8_r, +}; + +static void tgen_st16_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_ST_H, data, base, offset); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st16_r, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tcg_out_st, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) @@ -1993,22 +2020,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a3 = args[3]; switch (opc) { - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - tcg_out_ldst(s, OPC_ST_B, a0, a1, a2); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - tcg_out_ldst(s, OPC_ST_H, a0, a1, a2); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_ldst(s, OPC_ST_W, a0, a1, a2); - break; - case INDEX_op_st_i64: - tcg_out_ldst(s, OPC_ST_D, a0, a1, a2); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); break; @@ -2530,13 +2541,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i32: - case INDEX_op_st_i64: case INDEX_op_qemu_st_i32: case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 21ed11b78d..5e41729d88 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2342,12 +2342,38 @@ static const TCGOutOpLoad outop_ld32s = { }; #endif +static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_SB, data, base, offset); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st8_r, +}; + +static void tgen_st16_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_SH, data, base, offset); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st16_r, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tcg_out_st, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - MIPSInsn i1; TCGArg a0, a1, a2; a0 = args[0]; @@ -2355,24 +2381,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a2 = args[2]; switch (opc) { - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - i1 = OPC_SB; - goto do_ldst; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - i1 = OPC_SH; - goto do_ldst; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - i1 = OPC_SW; - goto do_ldst; - case INDEX_op_st_i64: - i1 = OPC_SD; - do_ldst: - tcg_out_ldst(s, i1, a0, a1, a2); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, 0, a1, a2, TCG_TYPE_I32); break; @@ -2407,15 +2415,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(rz, r); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); case INDEX_op_qemu_st_i32: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 275c5a90a5..9cf24831df 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3746,28 +3746,39 @@ static const TCGOutOpLoad outop_ld32s = { }; #endif +static void tgen_st8(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, STB, STBX, data, base, offset); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st8, +}; + +static void tgen_st16(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem_long(s, STH, STHX, data, base, offset); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st16, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tcg_out_st, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - tcg_out_mem_long(s, STB, STBX, args[0], args[1], args[2]); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - tcg_out_mem_long(s, STH, STHX, args[0], args[1], args[2]); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_mem_long(s, STW, STWX, args[0], args[1], args[2]); - break; - case INDEX_op_st_i64: - tcg_out_mem_long(s, STD, STDX, args[0], args[1], args[2]); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; @@ -4415,15 +4426,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(r, r); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); case INDEX_op_qemu_ld_i64: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 5b987c930f..bcfdb6c545 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2596,6 +2596,33 @@ static const TCGOutOpLoad outop_ld32s = { .out = tgen_ld32s, }; +static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_SB, data, base, offset); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st8_r, +}; + +static void tgen_st16_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, OPC_SH, data, base, offset); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st16_r, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tcg_out_st, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2606,22 +2633,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a2 = args[2]; switch (opc) { - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - tcg_out_ldst(s, OPC_SB, a0, a1, a2); - break; - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - tcg_out_ldst(s, OPC_SH, a0, a1, a2); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_ldst(s, OPC_SW, a0, a1, a2); - break; - case INDEX_op_st_i64: - tcg_out_ldst(s, OPC_SD, a0, a1, a2); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); break; @@ -2864,15 +2875,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(rz, r); - case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index fe7665b21d..e266c19829 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -3100,29 +3100,39 @@ static const TCGOutOpLoad outop_ld32s = { .out = tgen_ld32s, }; -# define OP_32_64(x) \ - case glue(glue(INDEX_op_,x),_i32): \ - case glue(glue(INDEX_op_,x),_i64) +static void tgen_st8(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, RX_STC, RXY_STCY, data, base, TCG_REG_NONE, offset); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st8, +}; + +static void tgen_st16(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_mem(s, RX_STH, RXY_STHY, data, base, TCG_REG_NONE, offset); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st16, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tcg_out_st, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - OP_32_64(st8): - tcg_out_mem(s, RX_STC, RXY_STCY, args[0], args[1], - TCG_REG_NONE, args[2]); - break; - - OP_32_64(st16): - tcg_out_mem(s, RX_STH, RXY_STHY, args[0], args[1], - TCG_REG_NONE, args[2]); - break; - - case INDEX_op_st_i32: - tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, args[0], args[1], args[2], TCG_TYPE_I32); break; @@ -3142,13 +3152,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; - case INDEX_op_st32_i64: - tcg_out_st(s, TCG_TYPE_I32, args[0], args[1], args[2]); - break; - case INDEX_op_st_i64: - tcg_out_st(s, TCG_TYPE_I64, args[0], args[1], args[2]); - break; - case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ @@ -3597,15 +3600,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(r, r); - case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 59a737dde4..a0efeee98c 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -2028,6 +2028,33 @@ static const TCGOutOpLoad outop_ld32s = { .out = tgen_ld32s, }; +static void tgen_st8_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, data, base, offset, STB); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st8_r, +}; + +static void tgen_st16_r(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, data, base, offset, STH); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tgen_st16_r, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out_r = tcg_out_st, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], @@ -2041,21 +2068,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a2 = args[2]; switch (opc) { -#define OP_32_64(x) \ - glue(glue(case INDEX_op_, x), _i32): \ - glue(glue(case INDEX_op_, x), _i64) - - OP_32_64(st8): - tcg_out_ldst(s, a0, a1, a2, STB); - break; - OP_32_64(st16): - tcg_out_ldst(s, a0, a1, a2, STH); - break; - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - tcg_out_ldst(s, a0, a1, a2, STW); - break; - case INDEX_op_qemu_ld_i32: tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); break; @@ -2069,10 +2081,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); break; - case INDEX_op_st_i64: - tcg_out_ldst(s, a0, a1, a2, STX); - break; - case INDEX_op_call: /* Always emitted via tcg_out_call. */ case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ @@ -2089,13 +2097,6 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_ld_i64: return C_O1_I1(r, r); - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - case INDEX_op_st_i32: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: case INDEX_op_qemu_st_i32: case INDEX_op_qemu_st_i64: return C_O0_I2(rz, r); diff --git a/tcg/tcg.c b/tcg/tcg.c index a9d62d9e17..28791c6567 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1090,6 +1090,14 @@ typedef struct TCGOutOpSetcond2 { TCGArg bl, bool const_bl, TCGArg bh, bool const_bh); } TCGOutOpSetcond2; +typedef struct TCGOutOpStore { + TCGOutOp base; + void (*out_r)(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, intptr_t offset); + void (*out_i)(TCGContext *s, TCGType type, tcg_target_long data, + TCGReg base, intptr_t offset); +} TCGOutOpStore; + typedef struct TCGOutOpSubtract { TCGOutOp base; void (*out_rrr)(TCGContext *s, TCGType type, @@ -1211,6 +1219,12 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_sextract, TCGOutOpExtract, outop_sextract), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), + OUTOP(INDEX_op_st_i32, TCGOutOpStore, outop_st), + OUTOP(INDEX_op_st_i64, TCGOutOpStore, outop_st), + OUTOP(INDEX_op_st8_i32, TCGOutOpStore, outop_st8), + OUTOP(INDEX_op_st8_i64, TCGOutOpStore, outop_st8), + OUTOP(INDEX_op_st16_i32, TCGOutOpStore, outop_st16), + OUTOP(INDEX_op_st16_i64, TCGOutOpStore, outop_st16), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_subbi, TCGOutOpAddSubCarry, outop_subbi), OUTOP(INDEX_op_subbio, TCGOutOpAddSubCarry, outop_subbio), @@ -1232,6 +1246,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_extrh_i64_i32, TCGOutOpUnary, outop_extrh_i64_i32), OUTOP(INDEX_op_ld32u, TCGOutOpLoad, outop_ld32u), OUTOP(INDEX_op_ld32s, TCGOutOpLoad, outop_ld32s), + OUTOP(INDEX_op_st32_i64, TCGOutOpStore, outop_st), #endif }; @@ -5779,6 +5794,28 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_st32_i64: + /* Use tcg_op_st w/ I32. */ + type = TCG_TYPE_I32; + /* fall through */ + case INDEX_op_st_i32: + case INDEX_op_st_i64: + case INDEX_op_st8_i32: + case INDEX_op_st8_i64: + case INDEX_op_st16_i32: + case INDEX_op_st16_i64: + { + const TCGOutOpStore *out = + container_of(all_outop[op->opc], TCGOutOpStore, base); + + if (const_args[0]) { + out->out_i(s, type, new_args[0], new_args[1], new_args[2]); + } else { + out->out_r(s, type, new_args[0], new_args[1], new_args[2]); + } + } + break; + case INDEX_op_brcond: { const TCGOutOpBrcond *out = &outop_brcond; diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index d549dc90f5..be9270a861 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -40,15 +40,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: - return C_O0_I2(r, r); - case INDEX_op_qemu_ld_i32: return C_O1_I1(r, r); case INDEX_op_qemu_ld_i64: @@ -487,18 +478,6 @@ static void tcg_out_call(TCGContext *s, const tcg_insn_unit *func, tcg_out32(s, insn); } -#if TCG_TARGET_REG_BITS == 64 -# define CASE_32_64(x) \ - case glue(glue(INDEX_op_, x), _i64): \ - case glue(glue(INDEX_op_, x), _i32): -# define CASE_64(x) \ - case glue(glue(INDEX_op_, x), _i64): -#else -# define CASE_32_64(x) \ - case glue(glue(INDEX_op_, x), _i32): -# define CASE_64(x) -#endif - static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg) { tcg_out_op_p(s, INDEX_op_exit_tb, (void *)arg); @@ -1191,20 +1170,39 @@ static const TCGOutOpLoad outop_ld32s = { }; #endif +static void tgen_st8(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_st8_i32, data, base, offset); +} + +static const TCGOutOpStore outop_st8 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st8, +}; + +static void tgen_st16(TCGContext *s, TCGType type, TCGReg data, + TCGReg base, ptrdiff_t offset) +{ + tcg_out_ldst(s, INDEX_op_st16_i32, data, base, offset); +} + +static const TCGOutOpStore outop_st16 = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tgen_st16, +}; + +static const TCGOutOpStore outop_st = { + .base.static_constraint = C_O0_I2(r, r), + .out_r = tcg_out_st, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - CASE_32_64(st8) - CASE_32_64(st16) - case INDEX_op_st_i32: - CASE_64(st32) - CASE_64(st) - tcg_out_ldst(s, opc, args[0], args[1], args[2]); - break; - case INDEX_op_qemu_ld_i64: case INDEX_op_qemu_st_i64: if (TCG_TARGET_REG_BITS == 32) { From a28f151d61604feae1d6c75b79e67d1c6c6a8b18 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 22 Jan 2025 13:28:55 -0800 Subject: [PATCH 0508/2760] tcg: Merge INDEX_op_st*_{i32,i64} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/tcg/tcg-opc.h | 15 ++++---------- tcg/optimize.c | 28 +++++++------------------ tcg/tcg-op.c | 14 ++++++------- tcg/tcg.c | 45 +++++++++++++--------------------------- tcg/tci.c | 36 +++++++++----------------------- tcg/tci/tcg-target.c.inc | 20 +++++++----------- 6 files changed, 50 insertions(+), 108 deletions(-) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 6e8fcefaef..a22433d8b5 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -86,6 +86,10 @@ DEF(setcond, 1, 2, 1, TCG_OPF_INT) DEF(sextract, 1, 1, 2, TCG_OPF_INT) DEF(shl, 1, 2, 0, TCG_OPF_INT) DEF(shr, 1, 2, 0, TCG_OPF_INT) +DEF(st8, 0, 2, 1, TCG_OPF_INT) +DEF(st16, 0, 2, 1, TCG_OPF_INT) +DEF(st32, 0, 2, 1, TCG_OPF_INT) +DEF(st, 0, 2, 1, TCG_OPF_INT) DEF(sub, 1, 2, 0, TCG_OPF_INT) DEF(xor, 1, 2, 0, TCG_OPF_INT) @@ -99,20 +103,9 @@ DEF(subb1o, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_OUT) DEF(subbi, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_IN) DEF(subbio, 1, 2, 0, TCG_OPF_INT | TCG_OPF_CARRY_IN | TCG_OPF_CARRY_OUT) -/* load/store */ -DEF(st8_i32, 0, 2, 1, 0) -DEF(st16_i32, 0, 2, 1, 0) -DEF(st_i32, 0, 2, 1, 0) - DEF(brcond2_i32, 0, 4, 2, TCG_OPF_BB_END | TCG_OPF_COND_BRANCH) DEF(setcond2_i32, 1, 4, 1, 0) -/* load/store */ -DEF(st8_i64, 0, 2, 1, 0) -DEF(st16_i64, 0, 2, 1, 0) -DEF(st32_i64, 0, 2, 1, 0) -DEF(st_i64, 0, 2, 1, 0) - /* size changing ops */ DEF(ext_i32_i64, 1, 1, 0, 0) DEF(extu_i32_i64, 1, 1, 0, 0) diff --git a/tcg/optimize.c b/tcg/optimize.c index d928a38e14..cfb407c7fc 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -30,14 +30,6 @@ #include "tcg-internal.h" #include "tcg-has.h" -#define CASE_OP_32_64(x) \ - glue(glue(case INDEX_op_, x), _i32): \ - glue(glue(case INDEX_op_, x), _i64) - -#define CASE_OP_32_64_VEC(x) \ - glue(glue(case INDEX_op_, x), _i32): \ - glue(glue(case INDEX_op_, x), _i64): \ - glue(glue(case INDEX_op_, x), _vec) typedef struct MemCopyInfo { IntervalTreeNode itree; @@ -2938,19 +2930,16 @@ static bool fold_tcg_st(OptContext *ctx, TCGOp *op) } switch (op->opc) { - CASE_OP_32_64(st8): + case INDEX_op_st8: lm1 = 0; break; - CASE_OP_32_64(st16): + case INDEX_op_st16: lm1 = 1; break; - case INDEX_op_st32_i64: - case INDEX_op_st_i32: + case INDEX_op_st32: lm1 = 3; break; - case INDEX_op_st_i64: - lm1 = 7; - break; + case INDEX_op_st: case INDEX_op_st_vec: lm1 = tcg_type_size(ctx->type) - 1; break; @@ -3138,13 +3127,12 @@ void tcg_optimize(TCGContext *s) case INDEX_op_ld_vec: done = fold_tcg_ld_memcopy(&ctx, op); break; - CASE_OP_32_64(st8): - CASE_OP_32_64(st16): - case INDEX_op_st32_i64: + case INDEX_op_st8: + case INDEX_op_st16: + case INDEX_op_st32: done = fold_tcg_st(&ctx, op); break; - case INDEX_op_st_i32: - case INDEX_op_st_i64: + case INDEX_op_st: case INDEX_op_st_vec: done = fold_tcg_st_memcopy(&ctx, op); break; diff --git a/tcg/tcg-op.c b/tcg/tcg-op.c index 680f752cf9..dfa5c38728 100644 --- a/tcg/tcg-op.c +++ b/tcg/tcg-op.c @@ -1404,17 +1404,17 @@ void tcg_gen_ld_i32(TCGv_i32 ret, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_st8_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ldst_op_i32(INDEX_op_st8_i32, arg1, arg2, offset); + tcg_gen_ldst_op_i32(INDEX_op_st8, arg1, arg2, offset); } void tcg_gen_st16_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ldst_op_i32(INDEX_op_st16_i32, arg1, arg2, offset); + tcg_gen_ldst_op_i32(INDEX_op_st16, arg1, arg2, offset); } void tcg_gen_st_i32(TCGv_i32 arg1, TCGv_ptr arg2, tcg_target_long offset) { - tcg_gen_ldst_op_i32(INDEX_op_st_i32, arg1, arg2, offset); + tcg_gen_ldst_op_i32(INDEX_op_st, arg1, arg2, offset); } @@ -1540,7 +1540,7 @@ void tcg_gen_ld_i64(TCGv_i64 ret, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_ldst_op_i64(INDEX_op_st8_i64, arg1, arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_st8, arg1, arg2, offset); } else { tcg_gen_st8_i32(TCGV_LOW(arg1), arg2, offset); } @@ -1549,7 +1549,7 @@ void tcg_gen_st8_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_ldst_op_i64(INDEX_op_st16_i64, arg1, arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_st16, arg1, arg2, offset); } else { tcg_gen_st16_i32(TCGV_LOW(arg1), arg2, offset); } @@ -1558,7 +1558,7 @@ void tcg_gen_st16_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_ldst_op_i64(INDEX_op_st32_i64, arg1, arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_st32, arg1, arg2, offset); } else { tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset); } @@ -1567,7 +1567,7 @@ void tcg_gen_st32_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) void tcg_gen_st_i64(TCGv_i64 arg1, TCGv_ptr arg2, tcg_target_long offset) { if (TCG_TARGET_REG_BITS == 64) { - tcg_gen_ldst_op_i64(INDEX_op_st_i64, arg1, arg2, offset); + tcg_gen_ldst_op_i64(INDEX_op_st, arg1, arg2, offset); } else if (HOST_BIG_ENDIAN) { tcg_gen_st_i32(TCGV_HIGH(arg1), arg2, offset); tcg_gen_st_i32(TCGV_LOW(arg1), arg2, offset + 4); diff --git a/tcg/tcg.c b/tcg/tcg.c index 28791c6567..44b6b8319f 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1219,12 +1219,9 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_sextract, TCGOutOpExtract, outop_sextract), OUTOP(INDEX_op_shl, TCGOutOpBinary, outop_shl), OUTOP(INDEX_op_shr, TCGOutOpBinary, outop_shr), - OUTOP(INDEX_op_st_i32, TCGOutOpStore, outop_st), - OUTOP(INDEX_op_st_i64, TCGOutOpStore, outop_st), - OUTOP(INDEX_op_st8_i32, TCGOutOpStore, outop_st8), - OUTOP(INDEX_op_st8_i64, TCGOutOpStore, outop_st8), - OUTOP(INDEX_op_st16_i32, TCGOutOpStore, outop_st16), - OUTOP(INDEX_op_st16_i64, TCGOutOpStore, outop_st16), + OUTOP(INDEX_op_st, TCGOutOpStore, outop_st), + OUTOP(INDEX_op_st8, TCGOutOpStore, outop_st8), + OUTOP(INDEX_op_st16, TCGOutOpStore, outop_st16), OUTOP(INDEX_op_sub, TCGOutOpSubtract, outop_sub), OUTOP(INDEX_op_subbi, TCGOutOpAddSubCarry, outop_subbi), OUTOP(INDEX_op_subbio, TCGOutOpAddSubCarry, outop_subbio), @@ -1246,7 +1243,7 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_extrh_i64_i32, TCGOutOpUnary, outop_extrh_i64_i32), OUTOP(INDEX_op_ld32u, TCGOutOpLoad, outop_ld32u), OUTOP(INDEX_op_ld32s, TCGOutOpLoad, outop_ld32s), - OUTOP(INDEX_op_st32_i64, TCGOutOpStore, outop_st), + OUTOP(INDEX_op_st32, TCGOutOpStore, outop_st), #endif }; @@ -2464,24 +2461,19 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_or: case INDEX_op_setcond: case INDEX_op_sextract: + case INDEX_op_st8: + case INDEX_op_st16: + case INDEX_op_st: case INDEX_op_xor: return has_type; - case INDEX_op_st8_i32: - case INDEX_op_st16_i32: - case INDEX_op_st_i32: - return true; - case INDEX_op_brcond2_i32: case INDEX_op_setcond2_i32: return TCG_TARGET_REG_BITS == 32; case INDEX_op_ld32u: case INDEX_op_ld32s: - case INDEX_op_st8_i64: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i64: + case INDEX_op_st32: case INDEX_op_ext_i32_i64: case INDEX_op_extu_i32_i64: case INDEX_op_extrl_i64_i32: @@ -4494,10 +4486,7 @@ liveness_pass_2(TCGContext *s) arg_ts->state = 0; if (NEED_SYNC_ARG(0)) { - TCGOpcode sopc = (arg_ts->type == TCG_TYPE_I32 - ? INDEX_op_st_i32 - : INDEX_op_st_i64); - TCGOp *sop = tcg_op_insert_after(s, op, sopc, + TCGOp *sop = tcg_op_insert_after(s, op, INDEX_op_st, arg_ts->type, 3); TCGTemp *out_ts = dir_ts; @@ -4531,10 +4520,7 @@ liveness_pass_2(TCGContext *s) /* Sync outputs upon their last write. */ if (NEED_SYNC_ARG(i)) { - TCGOpcode sopc = (arg_ts->type == TCG_TYPE_I32 - ? INDEX_op_st_i32 - : INDEX_op_st_i64); - TCGOp *sop = tcg_op_insert_after(s, op, sopc, + TCGOp *sop = tcg_op_insert_after(s, op, INDEX_op_st, arg_ts->type, 3); sop->args[0] = temp_arg(dir_ts); @@ -5794,16 +5780,13 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; - case INDEX_op_st32_i64: + case INDEX_op_st32: /* Use tcg_op_st w/ I32. */ type = TCG_TYPE_I32; /* fall through */ - case INDEX_op_st_i32: - case INDEX_op_st_i64: - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: + case INDEX_op_st: + case INDEX_op_st8: + case INDEX_op_st16: { const TCGOutOpStore *out = container_of(all_outop[op->opc], TCGOutOpStore, base); diff --git a/tcg/tci.c b/tcg/tci.c index 890ccbe85b..b08288e7d3 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -325,18 +325,6 @@ static void tci_qemu_st(CPUArchState *env, uint64_t taddr, uint64_t val, } } -#if TCG_TARGET_REG_BITS == 64 -# define CASE_32_64(x) \ - case glue(glue(INDEX_op_, x), _i64): \ - case glue(glue(INDEX_op_, x), _i32): -# define CASE_64(x) \ - case glue(glue(INDEX_op_, x), _i64): -#else -# define CASE_32_64(x) \ - case glue(glue(INDEX_op_, x), _i32): -# define CASE_64(x) -#endif - /* Interpret pseudo code in tb. */ /* * Disable CFI checks. @@ -491,21 +479,20 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, ptr = (void *)(regs[r1] + ofs); regs[r0] = *(tcg_target_ulong *)ptr; break; - CASE_32_64(st8) + case INDEX_op_st8: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); *(uint8_t *)ptr = regs[r0]; break; - CASE_32_64(st16) + case INDEX_op_st16: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); *(uint16_t *)ptr = regs[r0]; break; - case INDEX_op_st_i32: - CASE_64(st32) + case INDEX_op_st: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); - *(uint32_t *)ptr = regs[r0]; + *(tcg_target_ulong *)ptr = regs[r0]; break; /* Arithmetic operations (mixed 32/64 bit). */ @@ -725,10 +712,10 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, ptr = (void *)(regs[r1] + ofs); regs[r0] = *(int32_t *)ptr; break; - case INDEX_op_st_i64: + case INDEX_op_st32: tci_args_rrs(insn, &r0, &r1, &ofs); ptr = (void *)(regs[r1] + ofs); - *(uint64_t *)ptr = regs[r0]; + *(uint32_t *)ptr = regs[r0]; break; /* Arithmetic operations (64 bit). */ @@ -975,13 +962,10 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) case INDEX_op_ld16s: case INDEX_op_ld32u: case INDEX_op_ld: - case INDEX_op_st8_i32: - case INDEX_op_st8_i64: - case INDEX_op_st16_i32: - case INDEX_op_st16_i64: - case INDEX_op_st32_i64: - case INDEX_op_st_i32: - case INDEX_op_st_i64: + case INDEX_op_st8: + case INDEX_op_st16: + case INDEX_op_st32: + case INDEX_op_st: tci_args_rrs(insn, &r0, &r1, &s2); info->fprintf_func(info->stream, "%-12s %s, %s, %d", op_name, str_r(r0), str_r(r1), s2); diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index be9270a861..1fb7575061 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -1173,7 +1173,7 @@ static const TCGOutOpLoad outop_ld32s = { static void tgen_st8(TCGContext *s, TCGType type, TCGReg data, TCGReg base, ptrdiff_t offset) { - tcg_out_ldst(s, INDEX_op_st8_i32, data, base, offset); + tcg_out_ldst(s, INDEX_op_st8, data, base, offset); } static const TCGOutOpStore outop_st8 = { @@ -1184,7 +1184,7 @@ static const TCGOutOpStore outop_st8 = { static void tgen_st16(TCGContext *s, TCGType type, TCGReg data, TCGReg base, ptrdiff_t offset) { - tcg_out_ldst(s, INDEX_op_st16_i32, data, base, offset); + tcg_out_ldst(s, INDEX_op_st16, data, base, offset); } static const TCGOutOpStore outop_st16 = { @@ -1232,18 +1232,12 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, static void tcg_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base, intptr_t offset) { - switch (type) { - case TCG_TYPE_I32: - tcg_out_ldst(s, INDEX_op_st_i32, val, base, offset); - break; -#if TCG_TARGET_REG_BITS == 64 - case TCG_TYPE_I64: - tcg_out_ldst(s, INDEX_op_st_i64, val, base, offset); - break; -#endif - default: - g_assert_not_reached(); + TCGOpcode op = INDEX_op_st; + + if (TCG_TARGET_REG_BITS == 64 && type == TCG_TYPE_I32) { + op = INDEX_op_st32; } + tcg_out_ldst(s, op, val, base, offset); } static inline bool tcg_out_sti(TCGContext *s, TCGType type, TCGArg val, From bf7ca5fb3032b95fd83dbb9883e904ee28baa229 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 23 Jan 2025 09:46:57 -0800 Subject: [PATCH 0509/2760] tcg: Stash MemOp size in TCGOP_FLAGS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will enable removing INDEX_op_qemu_st8_*_i32, by exposing the operand size to constraint selection. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/tcg-op-ldst.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 3b073b4ce0..9e4626e51d 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -91,11 +91,15 @@ static MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) static void gen_ldst(TCGOpcode opc, TCGType type, TCGTemp *vl, TCGTemp *vh, TCGTemp *addr, MemOpIdx oi) { + TCGOp *op; + if (vh) { - tcg_gen_op4(opc, type, temp_arg(vl), temp_arg(vh), temp_arg(addr), oi); + op = tcg_gen_op4(opc, type, temp_arg(vl), temp_arg(vh), + temp_arg(addr), oi); } else { - tcg_gen_op3(opc, type, temp_arg(vl), temp_arg(addr), oi); + op = tcg_gen_op3(opc, type, temp_arg(vl), temp_arg(addr), oi); } + TCGOP_FLAGS(op) = get_memop(oi) & MO_SIZE; } static void gen_ldst_i64(TCGOpcode opc, TCGv_i64 v, TCGTemp *addr, MemOpIdx oi) From 33aba058c8fcc9b1581b03a1fbac45d8d91baac6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 26 Jan 2025 17:34:19 -0800 Subject: [PATCH 0510/2760] tcg: Remove INDEX_op_qemu_st8_* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The i386 backend can now check TCGOP_FLAGS to select the correct set of constraints. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- docs/devel/tcg-ops.rst | 6 ------ include/tcg/tcg-opc.h | 4 ---- tcg/aarch64/tcg-target-has.h | 1 - tcg/arm/tcg-target-has.h | 1 - tcg/i386/tcg-target-con-str.h | 2 +- tcg/i386/tcg-target-has.h | 3 --- tcg/i386/tcg-target.c.inc | 9 ++++----- tcg/loongarch64/tcg-target-has.h | 3 --- tcg/mips/tcg-target-has.h | 1 - tcg/optimize.c | 1 - tcg/ppc/tcg-target-has.h | 2 -- tcg/riscv/tcg-target-has.h | 1 - tcg/s390x/tcg-target-has.h | 1 - tcg/sparc64/tcg-target-has.h | 1 - tcg/tcg-op-ldst.c | 9 ++------- tcg/tcg.c | 4 ---- tcg/tci/tcg-target-has.h | 2 -- 17 files changed, 7 insertions(+), 44 deletions(-) diff --git a/docs/devel/tcg-ops.rst b/docs/devel/tcg-ops.rst index a7147407de..f26b837a30 100644 --- a/docs/devel/tcg-ops.rst +++ b/docs/devel/tcg-ops.rst @@ -744,8 +744,6 @@ QEMU specific operations qemu_st_i32/i64/i128 *t0*, *t1*, *flags*, *memidx* - qemu_st8_i32 *t0*, *t1*, *flags*, *memidx* - - | Load data at the guest address *t1* into *t0*, or store data in *t0* at guest address *t1*. The _i32/_i64/_i128 size applies to the size of the input/output register *t0* only. The address *t1* is always sized according to the guest, @@ -763,10 +761,6 @@ QEMU specific operations 64-bit memory access specified in *flags*. | | For qemu_ld/st_i128, these are only supported for a 64-bit host. - | - | For i386, qemu_st8_i32 is exactly like qemu_st_i32, except the size of - the memory operation is known to be 8-bit. This allows the backend to - provide a different set of register constraints. Host vector operations diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index a22433d8b5..0ce8332aab 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -133,10 +133,6 @@ DEF(qemu_ld_i64, DATA64_ARGS, 1, 1, DEF(qemu_st_i64, 0, DATA64_ARGS + 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -/* Only used by i386 to cope with stupid register constraints. */ -DEF(qemu_st8_i32, 0, 1 + 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) - /* Only for 64-bit hosts at the moment. */ DEF(qemu_ld_i128, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) DEF(qemu_st_i128, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) diff --git a/tcg/aarch64/tcg-target-has.h b/tcg/aarch64/tcg-target-has.h index b155e37639..69e83efb69 100644 --- a/tcg/aarch64/tcg-target-has.h +++ b/tcg/aarch64/tcg-target-has.h @@ -14,7 +14,6 @@ /* optional instructions */ #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 /* * Without FEAT_LSE2, we must use LDXP+STXP to implement atomic 128-bit load, diff --git a/tcg/arm/tcg-target-has.h b/tcg/arm/tcg-target-has.h index 187269e5bd..3bbbde5d59 100644 --- a/tcg/arm/tcg-target-has.h +++ b/tcg/arm/tcg-target-has.h @@ -24,7 +24,6 @@ extern bool use_neon_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 #define TCG_TARGET_HAS_tst 1 diff --git a/tcg/i386/tcg-target-con-str.h b/tcg/i386/tcg-target-con-str.h index 52142ab121..dbedff1f54 100644 --- a/tcg/i386/tcg-target-con-str.h +++ b/tcg/i386/tcg-target-con-str.h @@ -20,7 +20,7 @@ REGS('r', ALL_GENERAL_REGS) REGS('x', ALL_VECTOR_REGS) REGS('q', ALL_BYTEL_REGS) /* regs that can be used as a byte operand */ REGS('L', ALL_GENERAL_REGS & ~SOFTMMU_RESERVE_REGS) /* qemu_ld/st */ -REGS('s', ALL_BYTEL_REGS & ~SOFTMMU_RESERVE_REGS) /* qemu_st8_i32 data */ +REGS('s', ALL_BYTEL_REGS & ~SOFTMMU_RESERVE_REGS) /* qemu_st MO_8 data */ /* * Define constraint letters for constants: diff --git a/tcg/i386/tcg-target-has.h b/tcg/i386/tcg-target-has.h index 628e736de7..42647fabbd 100644 --- a/tcg/i386/tcg-target-has.h +++ b/tcg/i386/tcg-target-has.h @@ -29,9 +29,6 @@ #if TCG_TARGET_REG_BITS == 64 /* Keep 32-bit values zero-extended in a register. */ #define TCG_TARGET_HAS_extr_i64_i32 1 -#define TCG_TARGET_HAS_qemu_st8_i32 0 -#else -#define TCG_TARGET_HAS_qemu_st8_i32 1 #endif #define TCG_TARGET_HAS_qemu_ldst_i128 \ diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 52285bcd54..6c4c2ebd0e 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2457,7 +2457,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, switch (memop & MO_SIZE) { case MO_8: - /* This is handled with constraints on INDEX_op_qemu_st8_i32. */ + /* This is handled with constraints on INDEX_op_qemu_st_i32. */ tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || datalo < 4); tcg_out_modrm_sib_offset(s, OPC_MOVB_EvGv + P_REXB_R + h.seg, datalo, h.base, h.index, 0, h.ofs); @@ -3568,7 +3568,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, break; case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st8_i32: tcg_out_qemu_st(s, a0, -1, a1, a2, TCG_TYPE_I32); break; case INDEX_op_qemu_st_i64: @@ -4140,9 +4139,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) return C_O1_I1(r, L); case INDEX_op_qemu_st_i32: - return C_O0_I2(L, L); - case INDEX_op_qemu_st8_i32: - return C_O0_I2(s, L); + return (TCG_TARGET_REG_BITS == 32 && flags == MO_8 + ? C_O0_I2(s, L) + : C_O0_I2(L, L)); case INDEX_op_qemu_ld_i64: return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, L) : C_O2_I1(r, r, L); diff --git a/tcg/loongarch64/tcg-target-has.h b/tcg/loongarch64/tcg-target-has.h index 9c118bd1f6..32abc6f457 100644 --- a/tcg/loongarch64/tcg-target-has.h +++ b/tcg/loongarch64/tcg-target-has.h @@ -9,9 +9,6 @@ #include "host/cpuinfo.h" -/* optional instructions */ -#define TCG_TARGET_HAS_qemu_st8_i32 0 - /* 64-bit operations */ #define TCG_TARGET_HAS_extr_i64_i32 1 diff --git a/tcg/mips/tcg-target-has.h b/tcg/mips/tcg-target-has.h index d8f9f7beef..b9eb338528 100644 --- a/tcg/mips/tcg-target-has.h +++ b/tcg/mips/tcg-target-has.h @@ -46,7 +46,6 @@ extern bool use_mips32r2_instructions; #endif /* optional instructions detected at runtime */ -#define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 #define TCG_TARGET_HAS_tst 0 diff --git a/tcg/optimize.c b/tcg/optimize.c index cfb407c7fc..4d2220664a 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -3192,7 +3192,6 @@ void tcg_optimize(TCGContext *s) case INDEX_op_qemu_ld_i128: done = fold_qemu_ld_2reg(&ctx, op); break; - case INDEX_op_qemu_st8_i32: case INDEX_op_qemu_st_i32: case INDEX_op_qemu_st_i64: case INDEX_op_qemu_st_i128: diff --git a/tcg/ppc/tcg-target-has.h b/tcg/ppc/tcg-target-has.h index b978c91a62..81ec5aece7 100644 --- a/tcg/ppc/tcg-target-has.h +++ b/tcg/ppc/tcg-target-has.h @@ -17,8 +17,6 @@ #define have_vsx (cpuinfo & CPUINFO_VSX) /* optional instructions */ -#define TCG_TARGET_HAS_qemu_st8_i32 0 - #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 0 #endif diff --git a/tcg/riscv/tcg-target-has.h b/tcg/riscv/tcg-target-has.h index 8cd099546f..aef10c2d9d 100644 --- a/tcg/riscv/tcg-target-has.h +++ b/tcg/riscv/tcg-target-has.h @@ -10,7 +10,6 @@ #include "host/cpuinfo.h" /* optional instructions */ -#define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 1 #define TCG_TARGET_HAS_qemu_ldst_i128 0 #define TCG_TARGET_HAS_tst 0 diff --git a/tcg/s390x/tcg-target-has.h b/tcg/s390x/tcg-target-has.h index c04cc4e377..0aeb5ba01a 100644 --- a/tcg/s390x/tcg-target-has.h +++ b/tcg/s390x/tcg-target-has.h @@ -30,7 +30,6 @@ extern uint64_t s390_facilities[3]; /* optional instructions */ #define TCG_TARGET_HAS_extr_i64_i32 0 -#define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_qemu_ldst_i128 1 #define TCG_TARGET_HAS_tst 1 diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index d9f5ef3fc9..af6a949da3 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -14,7 +14,6 @@ extern bool use_vis3_instructions; #endif /* optional instructions */ -#define TCG_TARGET_HAS_qemu_st8_i32 0 #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 #define TCG_TARGET_HAS_tst 1 diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index 9e4626e51d..ac1af9f77c 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -270,7 +270,6 @@ static void tcg_gen_qemu_st_i32_int(TCGv_i32 val, TCGTemp *addr, { TCGv_i32 swap = NULL; MemOpIdx orig_oi, oi; - TCGOpcode opc; tcg_gen_req_mo(TCG_MO_LD_ST | TCG_MO_ST_ST); memop = tcg_canonicalize_memop(memop, 0, 1); @@ -293,12 +292,8 @@ static void tcg_gen_qemu_st_i32_int(TCGv_i32 val, TCGTemp *addr, oi = make_memop_idx(memop, idx); } - if (TCG_TARGET_HAS_qemu_st8_i32 && (memop & MO_SIZE) == MO_8) { - opc = INDEX_op_qemu_st8_i32; - } else { - opc = INDEX_op_qemu_st_i32; - } - gen_ldst(opc, TCG_TYPE_I32, tcgv_i32_temp(val), NULL, addr, oi); + gen_ldst(INDEX_op_qemu_st_i32, TCG_TYPE_I32, + tcgv_i32_temp(val), NULL, addr, oi); plugin_gen_mem_callbacks_i32(val, NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); if (swap) { diff --git a/tcg/tcg.c b/tcg/tcg.c index 44b6b8319f..5c0cab205c 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2438,9 +2438,6 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_qemu_st_i64: return true; - case INDEX_op_qemu_st8_i32: - return TCG_TARGET_HAS_qemu_st8_i32; - case INDEX_op_qemu_ld_i128: case INDEX_op_qemu_st_i128: return TCG_TARGET_HAS_qemu_ldst_i128; @@ -3012,7 +3009,6 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) break; case INDEX_op_qemu_ld_i32: case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st8_i32: case INDEX_op_qemu_ld_i64: case INDEX_op_qemu_st_i64: case INDEX_op_qemu_ld_i128: diff --git a/tcg/tci/tcg-target-has.h b/tcg/tci/tcg-target-has.h index 497e8152b7..ab07ce1fcb 100644 --- a/tcg/tci/tcg-target-has.h +++ b/tcg/tci/tcg-target-has.h @@ -7,8 +7,6 @@ #ifndef TCG_TARGET_HAS_H #define TCG_TARGET_HAS_H -#define TCG_TARGET_HAS_qemu_st8_i32 0 - #if TCG_TARGET_REG_BITS == 64 #define TCG_TARGET_HAS_extr_i64_i32 0 #endif /* TCG_TARGET_REG_BITS == 64 */ From aae2456ac0b4eb91da7ee8a4b31052f2e8a77af8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 9 Feb 2025 12:55:15 -0800 Subject: [PATCH 0511/2760] tcg: Merge INDEX_op_{ld,st}_{i32,i64,i128} Merge into INDEX_op_{ld,st,ld2,st2}, where "2" indicates that two inputs or outputs are required. This simplifies the processing of i64/i128 depending on host word size. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/tcg/tcg-opc.h | 16 ++----- tcg/aarch64/tcg-target.c.inc | 20 ++++----- tcg/arm/tcg-target.c.inc | 16 +++---- tcg/i386/tcg-target.c.inc | 50 ++++++---------------- tcg/loongarch64/tcg-target.c.inc | 28 +++++-------- tcg/mips/tcg-target.c.inc | 38 +++++++---------- tcg/optimize.c | 15 ++----- tcg/ppc/tcg-target.c.inc | 47 ++++++++------------- tcg/riscv/tcg-target.c.inc | 20 +++------ tcg/s390x/tcg-target.c.inc | 28 +++++-------- tcg/sparc64/tcg-target.c.inc | 20 +++------ tcg/tcg-op-ldst.c | 71 +++++++++++++++++--------------- tcg/tcg.c | 28 +++++++------ tcg/tci.c | 69 ++++++++++++------------------- tcg/tci/tcg-target.c.inc | 36 +++++++--------- 15 files changed, 198 insertions(+), 304 deletions(-) diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 0ce8332aab..995b79383e 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -124,18 +124,10 @@ DEF(goto_ptr, 0, 1, 0, TCG_OPF_BB_EXIT | TCG_OPF_BB_END) DEF(plugin_cb, 0, 0, 1, TCG_OPF_NOT_PRESENT) DEF(plugin_mem_cb, 0, 1, 1, TCG_OPF_NOT_PRESENT) -DEF(qemu_ld_i32, 1, 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_st_i32, 0, 1 + 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_ld_i64, DATA64_ARGS, 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_st_i64, 0, DATA64_ARGS + 1, 1, - TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) - -/* Only for 64-bit hosts at the moment. */ -DEF(qemu_ld_i128, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) -DEF(qemu_st_i128, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS) +DEF(qemu_ld, 1, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_INT) +DEF(qemu_st, 0, 2, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_INT) +DEF(qemu_ld2, 2, 1, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_INT) +DEF(qemu_st2, 0, 3, 1, TCG_OPF_CALL_CLOBBER | TCG_OPF_SIDE_EFFECTS | TCG_OPF_INT) /* Host vector support. */ diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 39a44507d1..a48cb46799 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2875,18 +2875,16 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, TCGArg a2 = args[2]; switch (opc) { - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_ld: tcg_out_qemu_ld(s, a0, a1, a2, ext); break; - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_st: tcg_out_qemu_st(s, a0, a1, a2, ext); break; - case INDEX_op_qemu_ld_i128: + case INDEX_op_qemu_ld2: tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], true); break; - case INDEX_op_qemu_st_i128: + case INDEX_op_qemu_st2: tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], false); break; @@ -3342,15 +3340,13 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_ld: return C_O1_I1(r, r); - case INDEX_op_qemu_ld_i128: + case INDEX_op_qemu_ld2: return C_O2_I1(r, r, r); - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_st: return C_O0_I2(rz, r); - case INDEX_op_qemu_st_i128: + case INDEX_op_qemu_st2: return C_O0_I3(rz, rz, r); case INDEX_op_add_vec: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 5b34f61ca1..29fd82e9e0 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2570,17 +2570,17 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld: tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; - case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_ld2: tcg_out_qemu_ld(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); break; - case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st: tcg_out_qemu_st(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; - case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_st2: tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); break; @@ -2596,13 +2596,13 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld: return C_O1_I1(r, q); - case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_ld2: return C_O2_I1(e, p, q); - case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st: return C_O0_I2(q, q); - case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_st2: return C_O0_I3(Q, p, q); case INDEX_op_st_vec: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 6c4c2ebd0e..cb66f6c27f 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2457,7 +2457,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, switch (memop & MO_SIZE) { case MO_8: - /* This is handled with constraints on INDEX_op_qemu_st_i32. */ + /* This is handled with constraints on INDEX_op_qemu_st. */ tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || datalo < 4); tcg_out_modrm_sib_offset(s, OPC_MOVB_EvGv + P_REXB_R + h.seg, datalo, h.base, h.index, 0, h.ofs); @@ -3552,34 +3552,18 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a2 = args[2]; switch (opc) { - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, a0, -1, a1, a2, TCG_TYPE_I32); + case INDEX_op_qemu_ld: + tcg_out_qemu_ld(s, a0, -1, a1, a2, type); break; - case INDEX_op_qemu_ld_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_ld(s, a0, -1, a1, a2, TCG_TYPE_I64); - } else { - tcg_out_qemu_ld(s, a0, a1, a2, args[3], TCG_TYPE_I64); - } - break; - case INDEX_op_qemu_ld_i128: - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); - tcg_out_qemu_ld(s, a0, a1, a2, args[3], TCG_TYPE_I128); + case INDEX_op_qemu_ld2: + tcg_out_qemu_ld(s, a0, a1, a2, args[3], type); break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, a0, -1, a1, a2, TCG_TYPE_I32); - break; - case INDEX_op_qemu_st_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_st(s, a0, -1, a1, a2, TCG_TYPE_I64); - } else { - tcg_out_qemu_st(s, a0, a1, a2, args[3], TCG_TYPE_I64); - } + case INDEX_op_qemu_st: + tcg_out_qemu_st(s, a0, -1, a1, a2, type); break; - case INDEX_op_qemu_st_i128: - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); - tcg_out_qemu_st(s, a0, a1, a2, args[3], TCG_TYPE_I128); + case INDEX_op_qemu_st2: + tcg_out_qemu_st(s, a0, a1, a2, args[3], type); break; case INDEX_op_call: /* Always emitted via tcg_out_call. */ @@ -4135,25 +4119,17 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld: return C_O1_I1(r, L); - case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st: return (TCG_TARGET_REG_BITS == 32 && flags == MO_8 ? C_O0_I2(s, L) : C_O0_I2(L, L)); - case INDEX_op_qemu_ld_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, L) : C_O2_I1(r, r, L); - - case INDEX_op_qemu_st_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(L, L) : C_O0_I3(L, L, L); - - case INDEX_op_qemu_ld_i128: - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + case INDEX_op_qemu_ld2: return C_O2_I1(r, r, L); - case INDEX_op_qemu_st_i128: - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); + case INDEX_op_qemu_st2: return C_O0_I3(L, L, L); case INDEX_op_ld_vec: diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index 73a1196d8b..e4a8b43578 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -2020,22 +2020,16 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a3 = args[3]; switch (opc) { - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); + case INDEX_op_qemu_ld: + tcg_out_qemu_ld(s, a0, a1, a2, type); break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I64); - break; - case INDEX_op_qemu_ld_i128: + case INDEX_op_qemu_ld2: tcg_out_qemu_ldst_i128(s, a0, a1, a2, a3, true); break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I32); - break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); + case INDEX_op_qemu_st: + tcg_out_qemu_st(s, a0, a1, a2, type); break; - case INDEX_op_qemu_st_i128: + case INDEX_op_qemu_st2: tcg_out_qemu_ldst_i128(s, a0, a1, a2, a3, false); break; @@ -2541,18 +2535,16 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_st: return C_O0_I2(rz, r); - case INDEX_op_qemu_ld_i128: + case INDEX_op_qemu_ld2: return C_N2_I1(r, r, r); - case INDEX_op_qemu_st_i128: + case INDEX_op_qemu_st2: return C_O0_I3(r, r, r); - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_ld: return C_O1_I1(r, r); case INDEX_op_ld_vec: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 5e41729d88..eaaf0f2024 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2381,26 +2381,20 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a2 = args[2]; switch (opc) { - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, a0, 0, a1, a2, TCG_TYPE_I32); + case INDEX_op_qemu_ld: + tcg_out_qemu_ld(s, a0, 0, a1, a2, type); break; - case INDEX_op_qemu_ld_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_ld(s, a0, 0, a1, a2, TCG_TYPE_I64); - } else { - tcg_out_qemu_ld(s, a0, a1, a2, args[3], TCG_TYPE_I64); - } + case INDEX_op_qemu_ld2: + tcg_debug_assert(TCG_TARGET_REG_BITS == 32); + tcg_out_qemu_ld(s, a0, a1, a2, args[3], type); break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, a0, 0, a1, a2, TCG_TYPE_I32); + case INDEX_op_qemu_st: + tcg_out_qemu_st(s, a0, 0, a1, a2, type); break; - case INDEX_op_qemu_st_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_st(s, a0, 0, a1, a2, TCG_TYPE_I64); - } else { - tcg_out_qemu_st(s, a0, a1, a2, args[3], TCG_TYPE_I64); - } + case INDEX_op_qemu_st2: + tcg_debug_assert(TCG_TARGET_REG_BITS == 32); + tcg_out_qemu_st(s, a0, a1, a2, args[3], type); break; case INDEX_op_call: /* Always emitted via tcg_out_call. */ @@ -2415,14 +2409,14 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld: return C_O1_I1(r, r); - case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st: return C_O0_I2(rz, r); - case INDEX_op_qemu_ld_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I1(r, r, r); - case INDEX_op_qemu_st_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(rz, r) : C_O0_I3(rz, rz, r); + case INDEX_op_qemu_ld2: + return TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O2_I1(r, r, r); + case INDEX_op_qemu_st2: + return TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O0_I3(rz, rz, r); default: return C_NotImplemented; diff --git a/tcg/optimize.c b/tcg/optimize.c index 4d2220664a..10a76c5461 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -3180,21 +3180,14 @@ void tcg_optimize(TCGContext *s) case INDEX_op_orc_vec: done = fold_orc(&ctx, op); break; - case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld: done = fold_qemu_ld_1reg(&ctx, op); break; - case INDEX_op_qemu_ld_i64: - if (TCG_TARGET_REG_BITS == 64) { - done = fold_qemu_ld_1reg(&ctx, op); - break; - } - QEMU_FALLTHROUGH; - case INDEX_op_qemu_ld_i128: + case INDEX_op_qemu_ld2: done = fold_qemu_ld_2reg(&ctx, op); break; - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: - case INDEX_op_qemu_st_i128: + case INDEX_op_qemu_st: + case INDEX_op_qemu_st2: done = fold_qemu_st(&ctx, op); break; case INDEX_op_rems: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 9cf24831df..bb26769d53 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3779,35 +3779,27 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); + case INDEX_op_qemu_ld: + tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], type); break; - case INDEX_op_qemu_ld_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I64); - } else { + case INDEX_op_qemu_ld2: + if (TCG_TARGET_REG_BITS == 32) { tcg_out_qemu_ld(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); + break; } - break; - case INDEX_op_qemu_ld_i128: - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], true); break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); + case INDEX_op_qemu_st: + tcg_out_qemu_st(s, args[0], -1, args[1], args[2], type); break; - case INDEX_op_qemu_st_i64: - if (TCG_TARGET_REG_BITS == 64) { - tcg_out_qemu_st(s, args[0], -1, args[1], args[2], TCG_TYPE_I64); - } else { + case INDEX_op_qemu_st2: + if (TCG_TARGET_REG_BITS == 32) { tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); + break; } - break; - case INDEX_op_qemu_st_i128: - tcg_debug_assert(TCG_TARGET_REG_BITS == 64); tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; @@ -4426,20 +4418,17 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld: return C_O1_I1(r, r); - case INDEX_op_qemu_ld_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I1(r, r, r); + case INDEX_op_qemu_ld2: + return TCG_TARGET_REG_BITS == 64 + ? C_N1O1_I1(o, m, r) : C_O2_I1(r, r, r); - case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st: return C_O0_I2(r, r); - case INDEX_op_qemu_st_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I3(r, r, r); - - case INDEX_op_qemu_ld_i128: - return C_N1O1_I1(o, m, r); - case INDEX_op_qemu_st_i128: - return C_O0_I3(o, m, r); + case INDEX_op_qemu_st2: + return TCG_TARGET_REG_BITS == 64 + ? C_O0_I3(o, m, r) : C_O0_I3(r, r, r); case INDEX_op_add_vec: case INDEX_op_sub_vec: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index bcfdb6c545..89c7736f9a 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2633,17 +2633,11 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a2 = args[2]; switch (opc) { - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); + case INDEX_op_qemu_ld: + tcg_out_qemu_ld(s, a0, a1, a2, type); break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I64); - break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I32); - break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); + case INDEX_op_qemu_st: + tcg_out_qemu_st(s, a0, a1, a2, type); break; case INDEX_op_call: /* Always emitted via tcg_out_call. */ @@ -2875,11 +2869,9 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_ld: return C_O1_I1(r, r); - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_st: return C_O0_I2(rz, r); case INDEX_op_st_vec: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index e266c19829..652ce9023e 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -3133,22 +3133,16 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, args[0], args[1], args[2], TCG_TYPE_I32); + case INDEX_op_qemu_ld: + tcg_out_qemu_ld(s, args[0], args[1], args[2], type); break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, args[0], args[1], args[2], TCG_TYPE_I64); + case INDEX_op_qemu_st: + tcg_out_qemu_st(s, args[0], args[1], args[2], type); break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, args[0], args[1], args[2], TCG_TYPE_I32); - break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, args[0], args[1], args[2], TCG_TYPE_I64); - break; - case INDEX_op_qemu_ld_i128: + case INDEX_op_qemu_ld2: tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], true); break; - case INDEX_op_qemu_st_i128: + case INDEX_op_qemu_st2: tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; @@ -3600,15 +3594,13 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_ld: return C_O1_I1(r, r); - case INDEX_op_qemu_st_i64: - case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st: return C_O0_I2(r, r); - case INDEX_op_qemu_ld_i128: + case INDEX_op_qemu_ld2: return C_O2_I1(o, m, r); - case INDEX_op_qemu_st_i128: + case INDEX_op_qemu_st2: return C_O0_I3(o, m, r); case INDEX_op_st_vec: diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index a0efeee98c..bf27b6b54b 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -2068,17 +2068,11 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a2 = args[2]; switch (opc) { - case INDEX_op_qemu_ld_i32: - tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I32); + case INDEX_op_qemu_ld: + tcg_out_qemu_ld(s, a0, a1, a2, type); break; - case INDEX_op_qemu_ld_i64: - tcg_out_qemu_ld(s, a0, a1, a2, TCG_TYPE_I64); - break; - case INDEX_op_qemu_st_i32: - tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I32); - break; - case INDEX_op_qemu_st_i64: - tcg_out_qemu_st(s, a0, a1, a2, TCG_TYPE_I64); + case INDEX_op_qemu_st: + tcg_out_qemu_st(s, a0, a1, a2, type); break; case INDEX_op_call: /* Always emitted via tcg_out_call. */ @@ -2093,12 +2087,10 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_ld_i64: + case INDEX_op_qemu_ld: return C_O1_I1(r, r); - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_st_i64: + case INDEX_op_qemu_st: return C_O0_I2(rz, r); default: diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index ac1af9f77c..fa9e52277b 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -88,28 +88,40 @@ static MemOp tcg_canonicalize_memop(MemOp op, bool is64, bool st) return op; } -static void gen_ldst(TCGOpcode opc, TCGType type, TCGTemp *vl, TCGTemp *vh, - TCGTemp *addr, MemOpIdx oi) +static void gen_ldst1(TCGOpcode opc, TCGType type, TCGTemp *v, + TCGTemp *addr, MemOpIdx oi) { - TCGOp *op; + TCGOp *op = tcg_gen_op3(opc, type, temp_arg(v), temp_arg(addr), oi); + TCGOP_FLAGS(op) = get_memop(oi) & MO_SIZE; +} - if (vh) { - op = tcg_gen_op4(opc, type, temp_arg(vl), temp_arg(vh), - temp_arg(addr), oi); +static void gen_ldst2(TCGOpcode opc, TCGType type, TCGTemp *vl, TCGTemp *vh, + TCGTemp *addr, MemOpIdx oi) +{ + TCGOp *op = tcg_gen_op4(opc, type, temp_arg(vl), temp_arg(vh), + temp_arg(addr), oi); + TCGOP_FLAGS(op) = get_memop(oi) & MO_SIZE; +} + +static void gen_ld_i64(TCGv_i64 v, TCGTemp *addr, MemOpIdx oi) +{ + if (TCG_TARGET_REG_BITS == 32) { + gen_ldst2(INDEX_op_qemu_ld2, TCG_TYPE_I64, + tcgv_i32_temp(TCGV_LOW(v)), tcgv_i32_temp(TCGV_HIGH(v)), + addr, oi); } else { - op = tcg_gen_op3(opc, type, temp_arg(vl), temp_arg(addr), oi); + gen_ldst1(INDEX_op_qemu_ld, TCG_TYPE_I64, tcgv_i64_temp(v), addr, oi); } - TCGOP_FLAGS(op) = get_memop(oi) & MO_SIZE; } -static void gen_ldst_i64(TCGOpcode opc, TCGv_i64 v, TCGTemp *addr, MemOpIdx oi) +static void gen_st_i64(TCGv_i64 v, TCGTemp *addr, MemOpIdx oi) { if (TCG_TARGET_REG_BITS == 32) { - TCGTemp *vl = tcgv_i32_temp(TCGV_LOW(v)); - TCGTemp *vh = tcgv_i32_temp(TCGV_HIGH(v)); - gen_ldst(opc, TCG_TYPE_I64, vl, vh, addr, oi); + gen_ldst2(INDEX_op_qemu_st2, TCG_TYPE_I64, + tcgv_i32_temp(TCGV_LOW(v)), tcgv_i32_temp(TCGV_HIGH(v)), + addr, oi); } else { - gen_ldst(opc, TCG_TYPE_I64, tcgv_i64_temp(v), NULL, addr, oi); + gen_ldst1(INDEX_op_qemu_st, TCG_TYPE_I64, tcgv_i64_temp(v), addr, oi); } } @@ -236,8 +248,7 @@ static void tcg_gen_qemu_ld_i32_int(TCGv_i32 val, TCGTemp *addr, } copy_addr = plugin_maybe_preserve_addr(addr); - gen_ldst(INDEX_op_qemu_ld_i32, TCG_TYPE_I32, - tcgv_i32_temp(val), NULL, addr, oi); + gen_ldst1(INDEX_op_qemu_ld, TCG_TYPE_I32, tcgv_i32_temp(val), addr, oi); plugin_gen_mem_callbacks_i32(val, copy_addr, addr, orig_oi, QEMU_PLUGIN_MEM_R); @@ -292,8 +303,7 @@ static void tcg_gen_qemu_st_i32_int(TCGv_i32 val, TCGTemp *addr, oi = make_memop_idx(memop, idx); } - gen_ldst(INDEX_op_qemu_st_i32, TCG_TYPE_I32, - tcgv_i32_temp(val), NULL, addr, oi); + gen_ldst1(INDEX_op_qemu_st, TCG_TYPE_I32, tcgv_i32_temp(val), addr, oi); plugin_gen_mem_callbacks_i32(val, NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); if (swap) { @@ -340,7 +350,7 @@ static void tcg_gen_qemu_ld_i64_int(TCGv_i64 val, TCGTemp *addr, } copy_addr = plugin_maybe_preserve_addr(addr); - gen_ldst_i64(INDEX_op_qemu_ld_i64, val, addr, oi); + gen_ld_i64(val, addr, oi); plugin_gen_mem_callbacks_i64(val, copy_addr, addr, orig_oi, QEMU_PLUGIN_MEM_R); @@ -407,7 +417,7 @@ static void tcg_gen_qemu_st_i64_int(TCGv_i64 val, TCGTemp *addr, oi = make_memop_idx(memop, idx); } - gen_ldst_i64(INDEX_op_qemu_st_i64, val, addr, oi); + gen_st_i64(val, addr, oi); plugin_gen_mem_callbacks_i64(val, NULL, addr, orig_oi, QEMU_PLUGIN_MEM_W); if (swap) { @@ -546,8 +556,8 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, hi = TCGV128_HIGH(val); } - gen_ldst(INDEX_op_qemu_ld_i128, TCG_TYPE_I128, tcgv_i64_temp(lo), - tcgv_i64_temp(hi), addr, oi); + gen_ldst2(INDEX_op_qemu_ld2, TCG_TYPE_I128, tcgv_i64_temp(lo), + tcgv_i64_temp(hi), addr, oi); if (need_bswap) { tcg_gen_bswap64_i64(lo, lo); @@ -575,8 +585,7 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, y = TCGV128_LOW(val); } - gen_ldst_i64(INDEX_op_qemu_ld_i64, x, addr, - make_memop_idx(mop[0], idx)); + gen_ld_i64(x, addr, make_memop_idx(mop[0], idx)); if (need_bswap) { tcg_gen_bswap64_i64(x, x); @@ -592,8 +601,7 @@ static void tcg_gen_qemu_ld_i128_int(TCGv_i128 val, TCGTemp *addr, addr_p8 = tcgv_i64_temp(t); } - gen_ldst_i64(INDEX_op_qemu_ld_i64, y, addr_p8, - make_memop_idx(mop[1], idx)); + gen_ld_i64(y, addr_p8, make_memop_idx(mop[1], idx)); tcg_temp_free_internal(addr_p8); if (need_bswap) { @@ -657,8 +665,8 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, hi = TCGV128_HIGH(val); } - gen_ldst(INDEX_op_qemu_st_i128, TCG_TYPE_I128, - tcgv_i64_temp(lo), tcgv_i64_temp(hi), addr, oi); + gen_ldst2(INDEX_op_qemu_st2, TCG_TYPE_I128, + tcgv_i64_temp(lo), tcgv_i64_temp(hi), addr, oi); if (need_bswap) { tcg_temp_free_i64(lo); @@ -685,8 +693,7 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, x = b; } - gen_ldst_i64(INDEX_op_qemu_st_i64, x, addr, - make_memop_idx(mop[0], idx)); + gen_st_i64(x, addr, make_memop_idx(mop[0], idx)); if (tcg_ctx->addr_type == TCG_TYPE_I32) { TCGv_i32 t = tcg_temp_ebb_new_i32(); @@ -700,12 +707,10 @@ static void tcg_gen_qemu_st_i128_int(TCGv_i128 val, TCGTemp *addr, if (b) { tcg_gen_bswap64_i64(b, y); - gen_ldst_i64(INDEX_op_qemu_st_i64, b, addr_p8, - make_memop_idx(mop[1], idx)); + gen_st_i64(b, addr_p8, make_memop_idx(mop[1], idx)); tcg_temp_free_i64(b); } else { - gen_ldst_i64(INDEX_op_qemu_st_i64, y, addr_p8, - make_memop_idx(mop[1], idx)); + gen_st_i64(y, addr_p8, make_memop_idx(mop[1], idx)); } tcg_temp_free_internal(addr_p8); } else { diff --git a/tcg/tcg.c b/tcg/tcg.c index 5c0cab205c..6c0866d446 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2432,14 +2432,20 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) case INDEX_op_exit_tb: case INDEX_op_goto_tb: case INDEX_op_goto_ptr: - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_ld_i64: - case INDEX_op_qemu_st_i64: return true; - case INDEX_op_qemu_ld_i128: - case INDEX_op_qemu_st_i128: + case INDEX_op_qemu_ld: + case INDEX_op_qemu_st: + tcg_debug_assert(type <= TCG_TYPE_REG); + return true; + + case INDEX_op_qemu_ld2: + case INDEX_op_qemu_st2: + if (TCG_TARGET_REG_BITS == 32) { + tcg_debug_assert(type == TCG_TYPE_I64); + return true; + } + tcg_debug_assert(type == TCG_TYPE_I128); return TCG_TARGET_HAS_qemu_ldst_i128; case INDEX_op_add: @@ -3007,12 +3013,10 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) } i = 1; break; - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_st_i32: - case INDEX_op_qemu_ld_i64: - case INDEX_op_qemu_st_i64: - case INDEX_op_qemu_ld_i128: - case INDEX_op_qemu_st_i128: + case INDEX_op_qemu_ld: + case INDEX_op_qemu_st: + case INDEX_op_qemu_ld2: + case INDEX_op_qemu_st2: { const char *s_al, *s_op, *s_at; MemOpIdx oi = op->args[k++]; diff --git a/tcg/tci.c b/tcg/tci.c index b08288e7d3..700e672616 100644 --- a/tcg/tci.c +++ b/tcg/tci.c @@ -789,46 +789,33 @@ uintptr_t QEMU_DISABLE_CFI tcg_qemu_tb_exec(CPUArchState *env, tb_ptr = ptr; break; - case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld: tci_args_rrm(insn, &r0, &r1, &oi); taddr = regs[r1]; regs[r0] = tci_qemu_ld(env, taddr, oi, tb_ptr); break; - case INDEX_op_qemu_ld_i64: - if (TCG_TARGET_REG_BITS == 64) { - tci_args_rrm(insn, &r0, &r1, &oi); - taddr = regs[r1]; - } else { - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - taddr = regs[r2]; - oi = regs[r3]; - } - tmp64 = tci_qemu_ld(env, taddr, oi, tb_ptr); - if (TCG_TARGET_REG_BITS == 32) { - tci_write_reg64(regs, r1, r0, tmp64); - } else { - regs[r0] = tmp64; - } - break; - - case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st: tci_args_rrm(insn, &r0, &r1, &oi); taddr = regs[r1]; tci_qemu_st(env, taddr, regs[r0], oi, tb_ptr); break; - case INDEX_op_qemu_st_i64: - if (TCG_TARGET_REG_BITS == 64) { - tci_args_rrm(insn, &r0, &r1, &oi); - tmp64 = regs[r0]; - taddr = regs[r1]; - } else { - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - tmp64 = tci_uint64(regs[r1], regs[r0]); - taddr = regs[r2]; - oi = regs[r3]; - } + case INDEX_op_qemu_ld2: + tcg_debug_assert(TCG_TARGET_REG_BITS == 32); + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); + taddr = regs[r2]; + oi = regs[r3]; + tmp64 = tci_qemu_ld(env, taddr, oi, tb_ptr); + tci_write_reg64(regs, r1, r0, tmp64); + break; + + case INDEX_op_qemu_st2: + tcg_debug_assert(TCG_TARGET_REG_BITS == 32); + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); + tmp64 = tci_uint64(regs[r1], regs[r0]); + taddr = regs[r2]; + oi = regs[r3]; tci_qemu_st(env, taddr, tmp64, oi, tb_ptr); break; @@ -1056,23 +1043,21 @@ int print_insn_tci(bfd_vma addr, disassemble_info *info) str_r(r2), str_r(r3)); break; - case INDEX_op_qemu_ld_i64: - case INDEX_op_qemu_st_i64: - if (TCG_TARGET_REG_BITS == 32) { - tci_args_rrrr(insn, &r0, &r1, &r2, &r3); - info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s", - op_name, str_r(r0), str_r(r1), - str_r(r2), str_r(r3)); - break; - } - /* fall through */ - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_ld: + case INDEX_op_qemu_st: tci_args_rrm(insn, &r0, &r1, &oi); info->fprintf_func(info->stream, "%-12s %s, %s, %x", op_name, str_r(r0), str_r(r1), oi); break; + case INDEX_op_qemu_ld2: + case INDEX_op_qemu_st2: + tci_args_rrrr(insn, &r0, &r1, &r2, &r3); + info->fprintf_func(info->stream, "%-12s %s, %s, %s, %s", + op_name, str_r(r0), str_r(r1), + str_r(r2), str_r(r3)); + break; + case 0: /* tcg_out_nop_fill uses zeros */ if (insn == 0) { diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 1fb7575061..6b8f71f49e 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -40,14 +40,14 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld_i32: + case INDEX_op_qemu_ld: return C_O1_I1(r, r); - case INDEX_op_qemu_ld_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O1_I1(r, r) : C_O2_I1(r, r, r); - case INDEX_op_qemu_st_i32: + case INDEX_op_qemu_st: return C_O0_I2(r, r); - case INDEX_op_qemu_st_i64: - return TCG_TARGET_REG_BITS == 64 ? C_O0_I2(r, r) : C_O0_I3(r, r, r); + case INDEX_op_qemu_ld2: + return TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O2_I1(r, r, r); + case INDEX_op_qemu_st2: + return TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O0_I3(r, r, r); default: return C_NotImplemented; @@ -1203,22 +1203,14 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_qemu_ld_i64: - case INDEX_op_qemu_st_i64: - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, args[3]); - tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], TCG_REG_TMP); - break; - } - /* fall through */ - case INDEX_op_qemu_ld_i32: - case INDEX_op_qemu_st_i32: - if (TCG_TARGET_REG_BITS == 64 && s->addr_type == TCG_TYPE_I32) { - tcg_out_ext32u(s, TCG_REG_TMP, args[1]); - tcg_out_op_rrm(s, opc, args[0], TCG_REG_TMP, args[2]); - } else { - tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); - } + case INDEX_op_qemu_ld: + case INDEX_op_qemu_st: + tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); + break; + case INDEX_op_qemu_ld2: + case INDEX_op_qemu_st2: + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, args[3]); + tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], TCG_REG_TMP); break; case INDEX_op_call: /* Always emitted via tcg_out_call. */ From 3bedb9d3e2855606f2b4982ce07f8ae399957c3d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 11 Feb 2025 13:41:42 -0800 Subject: [PATCH 0512/2760] tcg: Convert qemu_ld{2} to TCGOutOpLoad{2} Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 30 +++++++++------ tcg/arm/tcg-target.c.inc | 63 +++++++++++++++++++++++--------- tcg/i386/tcg-target.c.inc | 47 ++++++++++++++++-------- tcg/loongarch64/tcg-target.c.inc | 37 ++++++++++--------- tcg/mips/tcg-target.c.inc | 57 +++++++++++++++++++++-------- tcg/ppc/tcg-target.c.inc | 45 ++++++++++++++--------- tcg/riscv/tcg-target.c.inc | 22 ++++++----- tcg/s390x/tcg-target.c.inc | 32 +++++++++------- tcg/sparc64/tcg-target.c.inc | 21 ++++++----- tcg/tcg.c | 32 +++++++++++++++- tcg/tci/tcg-target.c.inc | 30 ++++++++++++--- 11 files changed, 283 insertions(+), 133 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index a48cb46799..feda64afa3 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1806,8 +1806,8 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp memop, } } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType data_type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; @@ -1822,6 +1822,11 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, } } +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, MemOpIdx oi, TCGType data_type) { @@ -1940,6 +1945,17 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, } } +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr_reg, MemOpIdx oi) +{ + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr_reg, oi, true); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_O2_I1(r, r, r), + .out = tgen_qemu_ld2, +}; + static const tcg_insn_unit *tb_ret_addr; static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) @@ -2875,15 +2891,9 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, TCGArg a2 = args[2]; switch (opc) { - case INDEX_op_qemu_ld: - tcg_out_qemu_ld(s, a0, a1, a2, ext); - break; case INDEX_op_qemu_st: tcg_out_qemu_st(s, a0, a1, a2, ext); break; - case INDEX_op_qemu_ld2: - tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], true); - break; case INDEX_op_qemu_st2: tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], false); break; @@ -3340,10 +3350,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld: - return C_O1_I1(r, r); - case INDEX_op_qemu_ld2: - return C_O2_I1(r, r, r); case INDEX_op_qemu_st: return C_O0_I2(rz, r); case INDEX_op_qemu_st2: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 29fd82e9e0..681ecc3d7a 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1586,8 +1586,8 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, MemOp opc, TCGReg datalo, } } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addr, MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; @@ -1595,7 +1595,41 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, ldst = prepare_host_addr(s, &h, addr, oi, true); if (ldst) { - ldst->type = data_type; + ldst->type = type; + ldst->datalo_reg = data; + ldst->datahi_reg = -1; + + /* + * This a conditional BL only to load a pointer within this + * opcode into LR for the slow path. We will not be using + * the value for a tail call. + */ + ldst->label_ptr[0] = s->code_ptr; + tcg_out_bl_imm(s, COND_NE, 0); + } + + tcg_out_qemu_ld_direct(s, opc, data, -1, h); + + if (ldst) { + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, q), + .out = tgen_qemu_ld, +}; + +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addr, oi, true); + if (ldst) { + ldst->type = type; ldst->datalo_reg = datalo; ldst->datahi_reg = datahi; @@ -1606,14 +1640,20 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, */ ldst->label_ptr[0] = s->code_ptr; tcg_out_bl_imm(s, COND_NE, 0); + } + + tcg_out_qemu_ld_direct(s, opc, datalo, datahi, h); - tcg_out_qemu_ld_direct(s, opc, datalo, datahi, h); + if (ldst) { ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); - } else { - tcg_out_qemu_ld_direct(s, opc, datalo, datahi, h); } } +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_O2_I1(e, p, q), + .out = tgen_qemu_ld2, +}; + static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg datalo, TCGReg datahi, HostAddress h) { @@ -2570,13 +2610,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_qemu_ld: - tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); - break; - case INDEX_op_qemu_ld2: - tcg_out_qemu_ld(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); - break; - case INDEX_op_qemu_st: tcg_out_qemu_st(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); break; @@ -2596,10 +2629,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld: - return C_O1_I1(r, q); - case INDEX_op_qemu_ld2: - return C_O2_I1(e, p, q); case INDEX_op_qemu_st: return C_O0_I2(q, q); case INDEX_op_qemu_st2: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index cb66f6c27f..7ec06f57ee 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2422,23 +2422,50 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, } } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addr, MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; ldst = prepare_host_addr(s, &h, addr, oi, true); - tcg_out_qemu_ld_direct(s, datalo, datahi, h, data_type, get_memop(oi)); + tcg_out_qemu_ld_direct(s, data, -1, h, type, get_memop(oi)); if (ldst) { - ldst->type = data_type; + ldst->type = type; + ldst->datalo_reg = data; + ldst->datahi_reg = -1; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, L), + .out = tgen_qemu_ld, +}; + +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addr, oi, true); + tcg_out_qemu_ld_direct(s, datalo, datahi, h, type, get_memop(oi)); + + if (ldst) { + ldst->type = type; ldst->datalo_reg = datalo; ldst->datahi_reg = datahi; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_O2_I1(r, r, L), + .out = tgen_qemu_ld2, +}; + static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, HostAddress h, MemOp memop) { @@ -3552,13 +3579,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a2 = args[2]; switch (opc) { - case INDEX_op_qemu_ld: - tcg_out_qemu_ld(s, a0, -1, a1, a2, type); - break; - case INDEX_op_qemu_ld2: - tcg_out_qemu_ld(s, a0, a1, a2, args[3], type); - break; - case INDEX_op_qemu_st: tcg_out_qemu_st(s, a0, -1, a1, a2, type); break; @@ -4119,16 +4139,11 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld: - return C_O1_I1(r, L); - case INDEX_op_qemu_st: return (TCG_TARGET_REG_BITS == 32 && flags == MO_8 ? C_O0_I2(s, L) : C_O0_I2(L, L)); - case INDEX_op_qemu_ld2: - return C_O2_I1(r, r, L); case INDEX_op_qemu_st2: return C_O0_I3(L, L, L); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index e4a8b43578..e2f0b7f894 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1167,22 +1167,27 @@ static void tcg_out_qemu_ld_indexed(TCGContext *s, MemOp opc, TCGType type, } } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; ldst = prepare_host_addr(s, &h, addr_reg, oi, true); - tcg_out_qemu_ld_indexed(s, get_memop(oi), data_type, data_reg, h); + tcg_out_qemu_ld_indexed(s, get_memop(oi), type, data_reg, h); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data_reg; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + static void tcg_out_qemu_st_indexed(TCGContext *s, MemOp opc, TCGReg rd, HostAddress h) { @@ -1270,6 +1275,17 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg data_lo, TCGReg data_hi } } +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr_reg, MemOpIdx oi) +{ + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr_reg, oi, true); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_N2_I1(r, r, r), + .out = tgen_qemu_ld2, +}; + /* * Entry-points */ @@ -2020,12 +2036,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a3 = args[3]; switch (opc) { - case INDEX_op_qemu_ld: - tcg_out_qemu_ld(s, a0, a1, a2, type); - break; - case INDEX_op_qemu_ld2: - tcg_out_qemu_ldst_i128(s, a0, a1, a2, a3, true); - break; case INDEX_op_qemu_st: tcg_out_qemu_st(s, a0, a1, a2, type); break; @@ -2537,16 +2547,9 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) switch (op) { case INDEX_op_qemu_st: return C_O0_I2(rz, r); - - case INDEX_op_qemu_ld2: - return C_N2_I1(r, r, r); - case INDEX_op_qemu_st2: return C_O0_I3(r, r, r); - case INDEX_op_qemu_ld: - return C_O1_I1(r, r); - case INDEX_op_ld_vec: case INDEX_op_dupm_vec: case INDEX_op_dup_vec: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index eaaf0f2024..14bffcd404 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1387,8 +1387,8 @@ static void tcg_out_qemu_ld_unalign(TCGContext *s, TCGReg lo, TCGReg hi, } } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addr, MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; @@ -1397,19 +1397,56 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg datalo, TCGReg datahi, ldst = prepare_host_addr(s, &h, addr, oi, true); if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { - tcg_out_qemu_ld_direct(s, datalo, datahi, h.base, opc, data_type); + tcg_out_qemu_ld_direct(s, data, 0, h.base, opc, type); } else { - tcg_out_qemu_ld_unalign(s, datalo, datahi, h.base, opc, data_type); + tcg_out_qemu_ld_unalign(s, data, 0, h.base, opc, type); } if (ldst) { - ldst->type = data_type; + ldst->type = type; + ldst->datalo_reg = data; + ldst->datahi_reg = 0; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; + + tcg_debug_assert(TCG_TARGET_REG_BITS == 32); + ldst = prepare_host_addr(s, &h, addr, oi, true); + + if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { + tcg_out_qemu_ld_direct(s, datalo, datahi, h.base, opc, type); + } else { + tcg_out_qemu_ld_unalign(s, datalo, datahi, h.base, opc, type); + } + + if (ldst) { + ldst->type = type; ldst->datalo_reg = datalo; ldst->datahi_reg = datahi; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + /* Ensure that the mips32 code is compiled but discarded for mips64. */ + .base.static_constraint = + TCG_TARGET_REG_BITS == 32 ? C_O2_I1(r, r, r) : C_NotImplemented, + .out = + TCG_TARGET_REG_BITS == 32 ? tgen_qemu_ld2 : NULL, +}; + static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg lo, TCGReg hi, TCGReg base, MemOp opc) { @@ -2381,14 +2418,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a2 = args[2]; switch (opc) { - case INDEX_op_qemu_ld: - tcg_out_qemu_ld(s, a0, 0, a1, a2, type); - break; - case INDEX_op_qemu_ld2: - tcg_debug_assert(TCG_TARGET_REG_BITS == 32); - tcg_out_qemu_ld(s, a0, a1, a2, args[3], type); - break; - case INDEX_op_qemu_st: tcg_out_qemu_st(s, a0, 0, a1, a2, type); break; @@ -2409,8 +2438,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld: - return C_O1_I1(r, r); case INDEX_op_qemu_st: return C_O0_I2(rz, r); case INDEX_op_qemu_ld2: diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index bb26769d53..e4e6b7b2d9 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2695,6 +2695,33 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, } } +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + tcg_out_qemu_ld(s, data, -1, addr, oi, type); +} + +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_qemu_ld(s, datalo, datahi, addr, oi, type); + } else { + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr, oi, true); + } +} + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = + TCG_TARGET_REG_BITS == 64 ? C_N1O1_I1(o, m, r) : C_O2_I1(r, r, r), + .out = tgen_qemu_ld2, +}; + static void tcg_out_nop_fill(tcg_insn_unit *p, int count) { int i; @@ -3779,18 +3806,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_qemu_ld: - tcg_out_qemu_ld(s, args[0], -1, args[1], args[2], type); - break; - case INDEX_op_qemu_ld2: - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_qemu_ld(s, args[0], args[1], args[2], - args[3], TCG_TYPE_I64); - break; - } - tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], true); - break; - case INDEX_op_qemu_st: tcg_out_qemu_st(s, args[0], -1, args[1], args[2], type); break; @@ -4418,12 +4433,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld: - return C_O1_I1(r, r); - case INDEX_op_qemu_ld2: - return TCG_TARGET_REG_BITS == 64 - ? C_N1O1_I1(o, m, r) : C_O2_I1(r, r, r); - case INDEX_op_qemu_st: return C_O0_I2(r, r); case INDEX_op_qemu_st2: diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 89c7736f9a..94e6f04fa6 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1833,22 +1833,31 @@ static void tcg_out_qemu_ld_direct(TCGContext *s, TCGReg val, } } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; TCGReg base; ldst = prepare_host_addr(s, &base, addr_reg, oi, true); - tcg_out_qemu_ld_direct(s, data_reg, base, get_memop(oi), data_type); + tcg_out_qemu_ld_direct(s, data_reg, base, get_memop(oi), type); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data_reg; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg val, TCGReg base, MemOp opc) { @@ -2633,9 +2642,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, TCGArg a2 = args[2]; switch (opc) { - case INDEX_op_qemu_ld: - tcg_out_qemu_ld(s, a0, a1, a2, type); - break; case INDEX_op_qemu_st: tcg_out_qemu_st(s, a0, a1, a2, type); break; @@ -2869,8 +2875,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld: - return C_O1_I1(r, r); case INDEX_op_qemu_st: return C_O0_I2(rz, r); diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 652ce9023e..bf99b765cf 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2081,8 +2081,8 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, return ldst; } -static void tcg_out_qemu_ld(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; @@ -2091,12 +2091,17 @@ static void tcg_out_qemu_ld(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, tcg_out_qemu_ld_direct(s, get_memop(oi), data_reg, h); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data_reg; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + static void tcg_out_qemu_st(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, MemOpIdx oi, TCGType data_type) { @@ -2187,6 +2192,17 @@ static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, } } +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr_reg, MemOpIdx oi) +{ + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr_reg, oi, true); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_O2_I1(o, m, r), + .out = tgen_qemu_ld2, +}; + static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) { /* Reuse the zeroing that exists for goto_ptr. */ @@ -3133,15 +3149,9 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_qemu_ld: - tcg_out_qemu_ld(s, args[0], args[1], args[2], type); - break; case INDEX_op_qemu_st: tcg_out_qemu_st(s, args[0], args[1], args[2], type); break; - case INDEX_op_qemu_ld2: - tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], true); - break; case INDEX_op_qemu_st2: tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); break; @@ -3594,12 +3604,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld: - return C_O1_I1(r, r); case INDEX_op_qemu_st: return C_O0_I2(r, r); - case INDEX_op_qemu_ld2: - return C_O2_I1(o, m, r); case INDEX_op_qemu_st2: return C_O0_I3(o, m, r); diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index bf27b6b54b..4426168354 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1186,8 +1186,8 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, return ldst; } -static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) { static const int ld_opc[(MO_SSIZE | MO_BSWAP) + 1] = { [MO_UB] = LDUB, @@ -1219,12 +1219,21 @@ static void tcg_out_qemu_ld(TCGContext *s, TCGReg data, TCGReg addr, ld_opc[get_memop(oi) & (MO_BSWAP | MO_SSIZE)]); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr, MemOpIdx oi, TCGType data_type) { @@ -2068,9 +2077,6 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, a2 = args[2]; switch (opc) { - case INDEX_op_qemu_ld: - tcg_out_qemu_ld(s, a0, a1, a2, type); - break; case INDEX_op_qemu_st: tcg_out_qemu_st(s, a0, a1, a2, type); break; @@ -2087,9 +2093,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld: - return C_O1_I1(r, r); - case INDEX_op_qemu_st: return C_O0_I2(rz, r); diff --git a/tcg/tcg.c b/tcg/tcg.c index 6c0866d446..f338deb019 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1071,6 +1071,18 @@ typedef struct TCGOutOpMul2 { TCGReg a0, TCGReg a1, TCGReg a2, TCGReg a3); } TCGOutOpMul2; +typedef struct TCGOutOpQemuLdSt { + TCGOutOp base; + void (*out)(TCGContext *s, TCGType type, TCGReg dest, + TCGReg addr, MemOpIdx oi); +} TCGOutOpQemuLdSt; + +typedef struct TCGOutOpQemuLdSt2 { + TCGOutOp base; + void (*out)(TCGContext *s, TCGType type, TCGReg dlo, TCGReg dhi, + TCGReg addr, MemOpIdx oi); +} TCGOutOpQemuLdSt2; + typedef struct TCGOutOpUnary { TCGOutOp base; void (*out_rr)(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1); @@ -1210,6 +1222,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_not, TCGOutOpUnary, outop_not), OUTOP(INDEX_op_or, TCGOutOpBinary, outop_or), OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), + OUTOP(INDEX_op_qemu_ld, TCGOutOpQemuLdSt, outop_qemu_ld), + OUTOP(INDEX_op_qemu_ld2, TCGOutOpQemuLdSt2, outop_qemu_ld2), OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), OUTOP(INDEX_op_rotl, TCGOutOpBinary, outop_rotl), @@ -2446,7 +2460,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return true; } tcg_debug_assert(type == TCG_TYPE_I128); - return TCG_TARGET_HAS_qemu_ldst_i128; + goto do_lookup; case INDEX_op_add: case INDEX_op_and: @@ -2558,6 +2572,7 @@ bool tcg_op_supported(TCGOpcode op, TCGType type, unsigned flags) return false; } + do_lookup: outop = all_outop[op]; tcg_debug_assert(outop != NULL); @@ -5799,6 +5814,21 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) } break; + case INDEX_op_qemu_ld: + { + const TCGOutOpQemuLdSt *out = &outop_qemu_ld; + out->out(s, type, new_args[0], new_args[1], new_args[2]); + } + break; + + case INDEX_op_qemu_ld2: + { + const TCGOutOpQemuLdSt2 *out = &outop_qemu_ld2; + out->out(s, type, new_args[0], new_args[1], + new_args[2], new_args[3]); + } + break; + case INDEX_op_brcond: { const TCGOutOpBrcond *out = &outop_brcond; diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 6b8f71f49e..f69e35e6ce 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -40,12 +40,8 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_ld: - return C_O1_I1(r, r); case INDEX_op_qemu_st: return C_O0_I2(r, r); - case INDEX_op_qemu_ld2: - return TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O2_I1(r, r, r); case INDEX_op_qemu_st2: return TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O0_I3(r, r, r); @@ -1197,17 +1193,39 @@ static const TCGOutOpStore outop_st = { .out_r = tcg_out_st, }; +static void tgen_qemu_ld(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + tcg_out_op_rrm(s, INDEX_op_qemu_ld, data, addr, oi); +} + +static const TCGOutOpQemuLdSt outop_qemu_ld = { + .base.static_constraint = C_O1_I1(r, r), + .out = tgen_qemu_ld, +}; + +static void tgen_qemu_ld2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, oi); + tcg_out_op_rrrr(s, INDEX_op_qemu_ld2, datalo, datahi, addr, TCG_REG_TMP); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { + .base.static_constraint = + TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O2_I1(r, r, r), + .out = + TCG_TARGET_REG_BITS == 64 ? NULL : tgen_qemu_ld2, +}; static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { switch (opc) { - case INDEX_op_qemu_ld: case INDEX_op_qemu_st: tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); break; - case INDEX_op_qemu_ld2: case INDEX_op_qemu_st2: tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, args[3]); tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], TCG_REG_TMP); From 86fe5c2597ca165228ee9cd082886846de4c9ece Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 16 Feb 2025 14:02:00 -0800 Subject: [PATCH 0513/2760] tcg: Convert qemu_st{2} to TCGOutOpLdSt{2} Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 45 ++++++++----------- tcg/arm/tcg-target.c.inc | 61 ++++++++++++++++--------- tcg/i386/tcg-target.c.inc | 73 ++++++++++++++++-------------- tcg/loongarch64/tcg-target.c.inc | 47 +++++++++---------- tcg/mips/tcg-target.c.inc | 77 +++++++++++++++++--------------- tcg/ppc/tcg-target.c.inc | 47 +++++++++++-------- tcg/riscv/tcg-target.c.inc | 34 ++++++-------- tcg/s390x/tcg-target.c.inc | 42 +++++++++-------- tcg/sparc64/tcg-target.c.inc | 42 ++++++----------- tcg/tcg.c | 12 ++++- tcg/tci/tcg-target.c.inc | 51 +++++++++++---------- 11 files changed, 272 insertions(+), 259 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index feda64afa3..59e7992a5f 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1827,8 +1827,8 @@ static const TCGOutOpQemuLdSt outop_qemu_ld = { .out = tgen_qemu_ld, }; -static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType data_type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; @@ -1843,6 +1843,11 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, } } +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out = tgen_qemu_st, +}; + static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, TCGReg addr_reg, MemOpIdx oi, bool is_ld) { @@ -1956,6 +1961,17 @@ static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { .out = tgen_qemu_ld2, }; +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr_reg, MemOpIdx oi) +{ + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr_reg, oi, false); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_O0_I3(rz, rz, r), + .out = tgen_qemu_st2, +}; + static const tcg_insn_unit *tb_ret_addr; static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) @@ -2885,25 +2901,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - /* Hoist the loads of the most common arguments. */ - TCGArg a0 = args[0]; - TCGArg a1 = args[1]; - TCGArg a2 = args[2]; - - switch (opc) { - case INDEX_op_qemu_st: - tcg_out_qemu_st(s, a0, a1, a2, ext); - break; - case INDEX_op_qemu_st2: - tcg_out_qemu_ldst_i128(s, a0, a1, a2, args[3], false); - break; - - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ - case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - default: - g_assert_not_reached(); - } + g_assert_not_reached(); } static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, @@ -3350,11 +3348,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_st: - return C_O0_I2(rz, r); - case INDEX_op_qemu_st2: - return C_O0_I3(rz, rz, r); - case INDEX_op_add_vec: case INDEX_op_sub_vec: case INDEX_op_mul_vec: diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 681ecc3d7a..014a441420 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1711,8 +1711,8 @@ static void tcg_out_qemu_st_direct(TCGContext *s, MemOp opc, TCGReg datalo, } } -static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addr, MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; @@ -1720,7 +1720,37 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, ldst = prepare_host_addr(s, &h, addr, oi, false); if (ldst) { - ldst->type = data_type; + ldst->type = type; + ldst->datalo_reg = data; + ldst->datahi_reg = -1; + + h.cond = COND_EQ; + tcg_out_qemu_st_direct(s, opc, data, -1, h); + + /* The conditional call is last, as we're going to return here. */ + ldst->label_ptr[0] = s->code_ptr; + tcg_out_bl_imm(s, COND_NE, 0); + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } else { + tcg_out_qemu_st_direct(s, opc, data, -1, h); + } +} + +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(q, q), + .out = tgen_qemu_st, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addr, oi, false); + if (ldst) { + ldst->type = type; ldst->datalo_reg = datalo; ldst->datahi_reg = datahi; @@ -1736,6 +1766,11 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, } } +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_O0_I3(Q, p, q), + .out = tgen_qemu_st2, +}; + static void tcg_out_epilogue(TCGContext *s); static void tcg_out_exit_tb(TCGContext *s, uintptr_t arg) @@ -2609,31 +2644,13 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - switch (opc) { - case INDEX_op_qemu_st: - tcg_out_qemu_st(s, args[0], -1, args[1], args[2], TCG_TYPE_I32); - break; - case INDEX_op_qemu_st2: - tcg_out_qemu_st(s, args[0], args[1], args[2], args[3], TCG_TYPE_I64); - break; - - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ - case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - default: - g_assert_not_reached(); - } + g_assert_not_reached(); } static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_st: - return C_O0_I2(q, q); - case INDEX_op_qemu_st2: - return C_O0_I3(Q, p, q); - case INDEX_op_st_vec: return C_O0_I2(w, r); case INDEX_op_ld_vec: diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 7ec06f57ee..9f294f28ed 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2484,7 +2484,7 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, switch (memop & MO_SIZE) { case MO_8: - /* This is handled with constraints on INDEX_op_qemu_st. */ + /* This is handled with constraints in cset_qemu_st(). */ tcg_debug_assert(TCG_TARGET_REG_BITS == 64 || datalo < 4); tcg_out_modrm_sib_offset(s, OPC_MOVB_EvGv + P_REXB_R + h.seg, datalo, h.base, h.index, 0, h.ofs); @@ -2576,8 +2576,38 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg datalo, TCGReg datahi, } } -static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addr, MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + TCGLabelQemuLdst *ldst; + HostAddress h; + + ldst = prepare_host_addr(s, &h, addr, oi, false); + tcg_out_qemu_st_direct(s, data, -1, h, get_memop(oi)); + + if (ldst) { + ldst->type = type; + ldst->datalo_reg = data; + ldst->datahi_reg = -1; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static TCGConstraintSetIndex cset_qemu_st(TCGType type, unsigned flags) +{ + return flags == MO_8 ? C_O0_I2(s, L) : C_O0_I2(L, L); +} + +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = + TCG_TARGET_REG_BITS == 32 ? C_Dynamic : C_O0_I2(L, L), + .base.dynamic_constraint = + TCG_TARGET_REG_BITS == 32 ? cset_qemu_st : NULL, + .out = tgen_qemu_st, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; @@ -2586,13 +2616,18 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, tcg_out_qemu_st_direct(s, datalo, datahi, h, get_memop(oi)); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = datalo; ldst->datahi_reg = datahi; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_O0_I3(L, L, L), + .out = tgen_qemu_st2, +}; + static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) { /* Reuse the zeroing that exists for goto_ptr. */ @@ -3571,27 +3606,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - TCGArg a0, a1, a2; - - /* Hoist the loads of the most common arguments. */ - a0 = args[0]; - a1 = args[1]; - a2 = args[2]; - - switch (opc) { - case INDEX_op_qemu_st: - tcg_out_qemu_st(s, a0, -1, a1, a2, type); - break; - case INDEX_op_qemu_st2: - tcg_out_qemu_st(s, a0, a1, a2, args[3], type); - break; - - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ - case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - default: - g_assert_not_reached(); - } + g_assert_not_reached(); } static int const umin_insn[4] = { @@ -4139,14 +4154,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_st: - return (TCG_TARGET_REG_BITS == 32 && flags == MO_8 - ? C_O0_I2(s, L) - : C_O0_I2(L, L)); - - case INDEX_op_qemu_st2: - return C_O0_I3(L, L, L); - case INDEX_op_ld_vec: case INDEX_op_dupm_vec: return C_O1_I1(x, r); diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index e2f0b7f894..c74ddee644 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1212,8 +1212,8 @@ static void tcg_out_qemu_st_indexed(TCGContext *s, MemOp opc, } } -static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; @@ -1222,12 +1222,17 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, tcg_out_qemu_st_indexed(s, get_memop(oi), data_reg, h); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data_reg; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out = tgen_qemu_st, +}; + static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg data_lo, TCGReg data_hi, TCGReg addr_reg, MemOpIdx oi, bool is_ld) { @@ -1286,6 +1291,17 @@ static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { .out = tgen_qemu_ld2, }; +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr_reg, MemOpIdx oi) +{ + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr_reg, oi, false); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_O0_I3(r, r, r), + .out = tgen_qemu_st2, +}; + /* * Entry-points */ @@ -2030,25 +2046,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - TCGArg a0 = args[0]; - TCGArg a1 = args[1]; - TCGArg a2 = args[2]; - TCGArg a3 = args[3]; - - switch (opc) { - case INDEX_op_qemu_st: - tcg_out_qemu_st(s, a0, a1, a2, type); - break; - case INDEX_op_qemu_st2: - tcg_out_qemu_ldst_i128(s, a0, a1, a2, a3, false); - break; - - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ - case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - default: - g_assert_not_reached(); - } + g_assert_not_reached(); } static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, @@ -2545,11 +2543,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_st: - return C_O0_I2(rz, r); - case INDEX_op_qemu_st2: - return C_O0_I3(r, r, r); - case INDEX_op_ld_vec: case INDEX_op_dupm_vec: case INDEX_op_dup_vec: diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 14bffcd404..1f12500344 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1510,8 +1510,8 @@ static void tcg_out_qemu_st_unalign(TCGContext *s, TCGReg lo, TCGReg hi, } } -static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, - TCGReg addr, MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) { MemOp opc = get_memop(oi); TCGLabelQemuLdst *ldst; @@ -1519,6 +1519,35 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, ldst = prepare_host_addr(s, &h, addr, oi, false); + if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { + tcg_out_qemu_st_direct(s, data, 0, h.base, opc); + } else { + tcg_out_qemu_st_unalign(s, data, 0, h.base, opc); + } + + if (ldst) { + ldst->type = type; + ldst->datalo_reg = data; + ldst->datahi_reg = 0; + ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); + } +} + +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out = tgen_qemu_st, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + MemOp opc = get_memop(oi); + TCGLabelQemuLdst *ldst; + HostAddress h; + + tcg_debug_assert(TCG_TARGET_REG_BITS == 32); + ldst = prepare_host_addr(s, &h, addr, oi, false); + if (use_mips32r6_instructions || h.aa.align >= (opc & MO_SIZE)) { tcg_out_qemu_st_direct(s, datalo, datahi, h.base, opc); } else { @@ -1526,13 +1555,21 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg datalo, TCGReg datahi, } if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = datalo; ldst->datahi_reg = datahi; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + /* Ensure that the mips32 code is compiled but discarded for mips64. */ + .base.static_constraint = + TCG_TARGET_REG_BITS == 32 ? C_O0_I3(rz, rz, r) : C_NotImplemented, + .out = + TCG_TARGET_REG_BITS == 32 ? tgen_qemu_st2 : NULL, +}; + static void tcg_out_mb(TCGContext *s, unsigned a0) { static const MIPSInsn sync[] = { @@ -2411,43 +2448,13 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - TCGArg a0, a1, a2; - - a0 = args[0]; - a1 = args[1]; - a2 = args[2]; - - switch (opc) { - case INDEX_op_qemu_st: - tcg_out_qemu_st(s, a0, 0, a1, a2, type); - break; - case INDEX_op_qemu_st2: - tcg_debug_assert(TCG_TARGET_REG_BITS == 32); - tcg_out_qemu_st(s, a0, a1, a2, args[3], type); - break; - - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ - case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - default: - g_assert_not_reached(); - } + g_assert_not_reached(); } static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { - switch (op) { - case INDEX_op_qemu_st: - return C_O0_I2(rz, r); - case INDEX_op_qemu_ld2: - return TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O2_I1(r, r, r); - case INDEX_op_qemu_st2: - return TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O0_I3(rz, rz, r); - - default: - return C_NotImplemented; - } + return C_NotImplemented; } static const int tcg_target_callee_save_regs[] = { diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index e4e6b7b2d9..824cced94a 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2722,6 +2722,33 @@ static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { .out = tgen_qemu_ld2, }; +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + tcg_out_qemu_st(s, data, -1, addr, oi, type); +} + +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(r, r), + .out = tgen_qemu_st, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + if (TCG_TARGET_REG_BITS == 32) { + tcg_out_qemu_st(s, datalo, datahi, addr, oi, type); + } else { + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr, oi, false); + } +} + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = + TCG_TARGET_REG_BITS == 64 ? C_O0_I3(o, m, r) : C_O0_I3(r, r, r), + .out = tgen_qemu_st2, +}; + static void tcg_out_nop_fill(tcg_insn_unit *p, int count) { int i; @@ -3805,25 +3832,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - switch (opc) { - case INDEX_op_qemu_st: - tcg_out_qemu_st(s, args[0], -1, args[1], args[2], type); - break; - case INDEX_op_qemu_st2: - if (TCG_TARGET_REG_BITS == 32) { - tcg_out_qemu_st(s, args[0], args[1], args[2], - args[3], TCG_TYPE_I64); - break; - } - tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); - break; - - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ - case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - default: - g_assert_not_reached(); - } + g_assert_not_reached(); } int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 94e6f04fa6..eca1283742 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1882,8 +1882,8 @@ static void tcg_out_qemu_st_direct(TCGContext *s, TCGReg val, } } -static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; TCGReg base; @@ -1892,12 +1892,21 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data_reg, TCGReg addr_reg, tcg_out_qemu_st_direct(s, data_reg, base, get_memop(oi)); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data_reg; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out = tgen_qemu_st, +}; + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_NotImplemented, +}; + static const tcg_insn_unit *tb_ret_addr; static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) @@ -2637,21 +2646,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - TCGArg a0 = args[0]; - TCGArg a1 = args[1]; - TCGArg a2 = args[2]; - - switch (opc) { - case INDEX_op_qemu_st: - tcg_out_qemu_st(s, a0, a1, a2, type); - break; - - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ - case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - default: - g_assert_not_reached(); - } + g_assert_not_reached(); } static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, @@ -2875,9 +2870,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_st: - return C_O0_I2(rz, r); - case INDEX_op_st_vec: return C_O0_I2(v, r); case INDEX_op_dup_vec: diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index bf99b765cf..a316c8de41 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2102,8 +2102,8 @@ static const TCGOutOpQemuLdSt outop_qemu_ld = { .out = tgen_qemu_ld, }; -static void tcg_out_qemu_st(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data_reg, + TCGReg addr_reg, MemOpIdx oi) { TCGLabelQemuLdst *ldst; HostAddress h; @@ -2112,12 +2112,17 @@ static void tcg_out_qemu_st(TCGContext* s, TCGReg data_reg, TCGReg addr_reg, tcg_out_qemu_st_direct(s, get_memop(oi), data_reg, h); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data_reg; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(r, r), + .out = tgen_qemu_st, +}; + static void tcg_out_qemu_ldst_i128(TCGContext *s, TCGReg datalo, TCGReg datahi, TCGReg addr_reg, MemOpIdx oi, bool is_ld) { @@ -2203,6 +2208,17 @@ static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { .out = tgen_qemu_ld2, }; +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr_reg, MemOpIdx oi) +{ + tcg_out_qemu_ldst_i128(s, datalo, datahi, addr_reg, oi, false); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_O0_I3(o, m, r), + .out = tgen_qemu_st2, +}; + static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) { /* Reuse the zeroing that exists for goto_ptr. */ @@ -3148,20 +3164,7 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - switch (opc) { - case INDEX_op_qemu_st: - tcg_out_qemu_st(s, args[0], args[1], args[2], type); - break; - case INDEX_op_qemu_st2: - tcg_out_qemu_ldst_i128(s, args[0], args[1], args[2], args[3], false); - break; - - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ - case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - default: - g_assert_not_reached(); - } + g_assert_not_reached(); } static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, @@ -3604,11 +3607,6 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { switch (op) { - case INDEX_op_qemu_st: - return C_O0_I2(r, r); - case INDEX_op_qemu_st2: - return C_O0_I3(o, m, r); - case INDEX_op_st_vec: return C_O0_I2(v, r); case INDEX_op_ld_vec: diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 4426168354..d1dd0fa33c 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1234,8 +1234,8 @@ static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { .base.static_constraint = C_NotImplemented, }; -static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr, - MemOpIdx oi, TCGType data_type) +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) { static const int st_opc[(MO_SIZE | MO_BSWAP) + 1] = { [MO_UB] = STB, @@ -1258,12 +1258,21 @@ static void tcg_out_qemu_st(TCGContext *s, TCGReg data, TCGReg addr, st_opc[get_memop(oi) & (MO_BSWAP | MO_SIZE)]); if (ldst) { - ldst->type = data_type; + ldst->type = type; ldst->datalo_reg = data; ldst->raddr = tcg_splitwx_to_rx(s->code_ptr); } } +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(rz, r), + .out = tgen_qemu_st, +}; + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = C_NotImplemented, +}; + static void tcg_out_exit_tb(TCGContext *s, uintptr_t a0) { if (check_fit_ptr(a0, 13)) { @@ -2069,36 +2078,13 @@ static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - TCGArg a0, a1, a2; - - /* Hoist the loads of the most common arguments. */ - a0 = args[0]; - a1 = args[1]; - a2 = args[2]; - - switch (opc) { - case INDEX_op_qemu_st: - tcg_out_qemu_st(s, a0, a1, a2, type); - break; - - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ - case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - default: - g_assert_not_reached(); - } + g_assert_not_reached(); } static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { - switch (op) { - case INDEX_op_qemu_st: - return C_O0_I2(rz, r); - - default: - return C_NotImplemented; - } + return C_NotImplemented; } static void tcg_target_init(TCGContext *s) diff --git a/tcg/tcg.c b/tcg/tcg.c index f338deb019..302f7025e7 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1224,6 +1224,8 @@ static const TCGOutOp * const all_outop[NB_OPS] = { OUTOP(INDEX_op_orc, TCGOutOpBinary, outop_orc), OUTOP(INDEX_op_qemu_ld, TCGOutOpQemuLdSt, outop_qemu_ld), OUTOP(INDEX_op_qemu_ld2, TCGOutOpQemuLdSt2, outop_qemu_ld2), + OUTOP(INDEX_op_qemu_st, TCGOutOpQemuLdSt, outop_qemu_st), + OUTOP(INDEX_op_qemu_st2, TCGOutOpQemuLdSt2, outop_qemu_st2), OUTOP(INDEX_op_rems, TCGOutOpBinary, outop_rems), OUTOP(INDEX_op_remu, TCGOutOpBinary, outop_remu), OUTOP(INDEX_op_rotl, TCGOutOpBinary, outop_rotl), @@ -5815,15 +5817,21 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; case INDEX_op_qemu_ld: + case INDEX_op_qemu_st: { - const TCGOutOpQemuLdSt *out = &outop_qemu_ld; + const TCGOutOpQemuLdSt *out = + container_of(all_outop[op->opc], TCGOutOpQemuLdSt, base); + out->out(s, type, new_args[0], new_args[1], new_args[2]); } break; case INDEX_op_qemu_ld2: + case INDEX_op_qemu_st2: { - const TCGOutOpQemuLdSt2 *out = &outop_qemu_ld2; + const TCGOutOpQemuLdSt2 *out = + container_of(all_outop[op->opc], TCGOutOpQemuLdSt2, base); + out->out(s, type, new_args[0], new_args[1], new_args[2], new_args[3]); } diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index f69e35e6ce..50e205211d 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -39,15 +39,7 @@ static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { - switch (op) { - case INDEX_op_qemu_st: - return C_O0_I2(r, r); - case INDEX_op_qemu_st2: - return TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O0_I3(r, r, r); - - default: - return C_NotImplemented; - } + return C_NotImplemented; } static const int tcg_target_reg_alloc_order[] = { @@ -1218,25 +1210,36 @@ static const TCGOutOpQemuLdSt2 outop_qemu_ld2 = { TCG_TARGET_REG_BITS == 64 ? NULL : tgen_qemu_ld2, }; +static void tgen_qemu_st(TCGContext *s, TCGType type, TCGReg data, + TCGReg addr, MemOpIdx oi) +{ + tcg_out_op_rrm(s, INDEX_op_qemu_st, data, addr, oi); +} + +static const TCGOutOpQemuLdSt outop_qemu_st = { + .base.static_constraint = C_O0_I2(r, r), + .out = tgen_qemu_st, +}; + +static void tgen_qemu_st2(TCGContext *s, TCGType type, TCGReg datalo, + TCGReg datahi, TCGReg addr, MemOpIdx oi) +{ + tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, oi); + tcg_out_op_rrrr(s, INDEX_op_qemu_st2, datalo, datahi, addr, TCG_REG_TMP); +} + +static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { + .base.static_constraint = + TCG_TARGET_REG_BITS == 64 ? C_NotImplemented : C_O0_I3(r, r, r), + .out = + TCG_TARGET_REG_BITS == 64 ? NULL : tgen_qemu_st2, +}; + static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, const TCGArg args[TCG_MAX_OP_ARGS], const int const_args[TCG_MAX_OP_ARGS]) { - switch (opc) { - case INDEX_op_qemu_st: - tcg_out_op_rrm(s, opc, args[0], args[1], args[2]); - break; - case INDEX_op_qemu_st2: - tcg_out_movi(s, TCG_TYPE_I32, TCG_REG_TMP, args[3]); - tcg_out_op_rrrr(s, opc, args[0], args[1], args[2], TCG_REG_TMP); - break; - - case INDEX_op_call: /* Always emitted via tcg_out_call. */ - case INDEX_op_exit_tb: /* Always emitted via tcg_out_exit_tb. */ - case INDEX_op_goto_tb: /* Always emitted via tcg_out_goto_tb. */ - default: - g_assert_not_reached(); - } + g_assert_not_reached(); } static void tcg_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base, From eafecf08059953a2d1d06b6a6ded3c56916cbdd4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 16 Feb 2025 14:22:48 -0800 Subject: [PATCH 0514/2760] tcg: Remove tcg_out_op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All integer opcodes are now converted to TCGOutOp. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/aarch64/tcg-target.c.inc | 7 ------- tcg/arm/tcg-target.c.inc | 7 ------- tcg/i386/tcg-target.c.inc | 7 ------- tcg/loongarch64/tcg-target.c.inc | 7 ------- tcg/mips/tcg-target.c.inc | 7 ------- tcg/ppc/tcg-target.c.inc | 7 ------- tcg/riscv/tcg-target.c.inc | 7 ------- tcg/s390x/tcg-target.c.inc | 7 ------- tcg/sparc64/tcg-target.c.inc | 7 ------- tcg/tcg.c | 12 +++--------- tcg/tci/tcg-target.c.inc | 7 ------- 11 files changed, 3 insertions(+), 79 deletions(-) diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 59e7992a5f..4cb647cb34 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -2897,13 +2897,6 @@ static const TCGOutOpStore outop_st = { .out_r = tcg_out_st, }; -static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType ext, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) -{ - g_assert_not_reached(); -} - static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, unsigned vecl, unsigned vece, const TCGArg args[TCG_MAX_OP_ARGS], diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 014a441420..447e43583e 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -2640,13 +2640,6 @@ static const TCGOutOpStore outop_st = { .out_r = tcg_out_st, }; -static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) -{ - g_assert_not_reached(); -} - static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 9f294f28ed..09fce27b06 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -3602,13 +3602,6 @@ static const TCGOutOpStore outop_st = { .out_i = tgen_st_i, }; -static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) -{ - g_assert_not_reached(); -} - static int const umin_insn[4] = { OPC_PMINUB, OPC_PMINUW, OPC_PMINUD, OPC_VPMINUQ }; diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index c74ddee644..e5580d69a8 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -2042,13 +2042,6 @@ static const TCGOutOpStore outop_st = { .out_r = tcg_out_st, }; -static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) -{ - g_assert_not_reached(); -} - static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg rd, TCGReg rs) { diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 1f12500344..2c0457e588 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -2444,13 +2444,6 @@ static const TCGOutOpStore outop_st = { }; -static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) -{ - g_assert_not_reached(); -} - static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 824cced94a..2e94778104 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -3828,13 +3828,6 @@ static const TCGOutOpStore outop_st = { }; -static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) -{ - g_assert_not_reached(); -} - int tcg_can_emit_vec_op(TCGOpcode opc, TCGType type, unsigned vece) { switch (opc) { diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index eca1283742..f9417d15f7 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2642,13 +2642,6 @@ static const TCGOutOpStore outop_st = { }; -static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) -{ - g_assert_not_reached(); -} - static void tcg_out_vec_op(TCGContext *s, TCGOpcode opc, unsigned vecl, unsigned vece, const TCGArg args[TCG_MAX_OP_ARGS], diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index a316c8de41..7ca0071f24 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -3160,13 +3160,6 @@ static const TCGOutOpStore outop_st = { }; -static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) -{ - g_assert_not_reached(); -} - static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg dst, TCGReg src) { diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index d1dd0fa33c..83167aa29d 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -2074,13 +2074,6 @@ static const TCGOutOpStore outop_st = { }; -static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) -{ - g_assert_not_reached(); -} - static TCGConstraintSetIndex tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) { diff --git a/tcg/tcg.c b/tcg/tcg.c index 302f7025e7..c4e866e9c3 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -138,9 +138,6 @@ static void tcg_out_mb(TCGContext *s, unsigned bar); static void tcg_out_br(TCGContext *s, TCGLabel *l); static void tcg_out_set_carry(TCGContext *s); static void tcg_out_set_borrow(TCGContext *s); -static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]); #if TCG_TARGET_MAYBE_vec static bool tcg_out_dup_vec(TCGContext *s, TCGType type, unsigned vece, TCGReg dst, TCGReg src); @@ -5920,12 +5917,9 @@ static void tcg_reg_alloc_op(TCGContext *s, const TCGOp *op) break; default: - if (def->flags & TCG_OPF_VECTOR) { - tcg_out_vec_op(s, op->opc, type - TCG_TYPE_V64, - TCGOP_VECE(op), new_args, const_args); - } else { - tcg_out_op(s, op->opc, type, new_args, const_args); - } + tcg_debug_assert(def->flags & TCG_OPF_VECTOR); + tcg_out_vec_op(s, op->opc, type - TCG_TYPE_V64, + TCGOP_VECE(op), new_args, const_args); break; } diff --git a/tcg/tci/tcg-target.c.inc b/tcg/tci/tcg-target.c.inc index 50e205211d..35c66a4836 100644 --- a/tcg/tci/tcg-target.c.inc +++ b/tcg/tci/tcg-target.c.inc @@ -1235,13 +1235,6 @@ static const TCGOutOpQemuLdSt2 outop_qemu_st2 = { TCG_TARGET_REG_BITS == 64 ? NULL : tgen_qemu_st2, }; -static void tcg_out_op(TCGContext *s, TCGOpcode opc, TCGType type, - const TCGArg args[TCG_MAX_OP_ARGS], - const int const_args[TCG_MAX_OP_ARGS]) -{ - g_assert_not_reached(); -} - static void tcg_out_st(TCGContext *s, TCGType type, TCGReg val, TCGReg base, intptr_t offset) { From 33b6c61cce2403193f9fc3a221f8a8656ba9cc37 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 25 Apr 2025 11:59:58 -0700 Subject: [PATCH 0515/2760] tcg/sparc64: Unexport use_vis3_instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This variable is no longer used outside tcg-target.c.inc. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/sparc64/tcg-target-has.h | 6 ------ tcg/sparc64/tcg-target.c.inc | 6 ++++-- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/tcg/sparc64/tcg-target-has.h b/tcg/sparc64/tcg-target-has.h index af6a949da3..b29fd177f6 100644 --- a/tcg/sparc64/tcg-target-has.h +++ b/tcg/sparc64/tcg-target-has.h @@ -7,12 +7,6 @@ #ifndef TCG_TARGET_HAS_H #define TCG_TARGET_HAS_H -#if defined(__VIS__) && __VIS__ >= 0x300 -#define use_vis3_instructions 1 -#else -extern bool use_vis3_instructions; -#endif - /* optional instructions */ #define TCG_TARGET_HAS_extr_i64_i32 0 #define TCG_TARGET_HAS_qemu_ldst_i128 0 diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 83167aa29d..260dd461bd 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -274,8 +274,10 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define STW_LE (STWA | INSN_ASI(ASI_PRIMARY_LITTLE)) #define STX_LE (STXA | INSN_ASI(ASI_PRIMARY_LITTLE)) -#ifndef use_vis3_instructions -bool use_vis3_instructions; +#if defined(__VIS__) && __VIS__ >= 0x300 +#define use_vis3_instructions 1 +#else +static bool use_vis3_instructions; #endif static bool check_fit_i64(int64_t val, unsigned int bits) From 70ab4f4ed9bbe9bcfdb105681291b4695f151522 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 25 Apr 2025 12:57:11 -0700 Subject: [PATCH 0516/2760] tcg/sparc64: Implement CTPOP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- tcg/sparc64/tcg-target.c.inc | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 260dd461bd..9e004fb511 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -210,6 +210,7 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define ARITH_UDIVX (INSN_OP(2) | INSN_OP3(0x0d)) #define ARITH_SDIVX (INSN_OP(2) | INSN_OP3(0x2d)) #define ARITH_MOVCC (INSN_OP(2) | INSN_OP3(0x2c)) +#define ARITH_POPC (INSN_OP(2) | INSN_OP3(0x2e)) #define ARITH_MOVR (INSN_OP(2) | INSN_OP3(0x2f)) #define ARITH_ADDXC (INSN_OP(2) | INSN_OP3(0x36) | INSN_OPF(0x11)) @@ -274,6 +275,7 @@ static TCGReg tcg_target_call_oarg_reg(TCGCallReturnKind kind, int slot) #define STW_LE (STWA | INSN_ASI(ASI_PRIMARY_LITTLE)) #define STX_LE (STXA | INSN_ASI(ASI_PRIMARY_LITTLE)) +static bool use_popc_instructions; #if defined(__VIS__) && __VIS__ >= 0x300 #define use_vis3_instructions 1 #else @@ -1511,8 +1513,23 @@ static const TCGOutOpBinary outop_clz = { .base.static_constraint = C_NotImplemented, }; +static void tgen_ctpop(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1) +{ + tcg_out_arith(s, a0, TCG_REG_G0, a1, ARITH_POPC); +} + +static TCGConstraintSetIndex cset_ctpop(TCGType type, unsigned flags) +{ + if (use_popc_instructions && type == TCG_TYPE_I64) { + return C_O1_I1(r, r); + } + return C_NotImplemented; +} + static const TCGOutOpUnary outop_ctpop = { - .base.static_constraint = C_NotImplemented, + .base.static_constraint = C_Dynamic, + .base.dynamic_constraint = cset_ctpop, + .out_rr = tgen_ctpop, }; static const TCGOutOpBinary outop_ctz = { @@ -2084,15 +2101,15 @@ tcg_target_op_def(TCGOpcode op, TCGType type, unsigned flags) static void tcg_target_init(TCGContext *s) { + unsigned long hwcap = qemu_getauxval(AT_HWCAP); + /* * Only probe for the platform and capabilities if we haven't already * determined maximum values at compile time. */ + use_popc_instructions = (hwcap & HWCAP_SPARC_POPC) != 0; #ifndef use_vis3_instructions - { - unsigned long hwcap = qemu_getauxval(AT_HWCAP); - use_vis3_instructions = (hwcap & HWCAP_SPARC_VIS3) != 0; - } + use_vis3_instructions = (hwcap & HWCAP_SPARC_VIS3) != 0; #endif tcg_target_available_regs[TCG_TYPE_I32] = ALL_GENERAL_REGS; From 71365ee433125026d9744a0a37142c81ff312b53 Mon Sep 17 00:00:00 2001 From: Vincent Vanlaer Date: Sat, 26 Oct 2024 18:30:05 +0200 Subject: [PATCH 0517/2760] block: get type of block allocation in commit_run bdrv_co_common_block_status_above not only returns whether the block is allocated, but also if it contains zeroes. Signed-off-by: Vincent Vanlaer Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20241026163010.2865002-2-libvirt-e6954efa@volkihar.be> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/commit.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/block/commit.c b/block/commit.c index 5df3d05346..ba0ba59316 100644 --- a/block/commit.c +++ b/block/commit.c @@ -15,6 +15,8 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" #include "trace.h" +#include "block/block-common.h" +#include "block/coroutines.h" #include "block/block_int.h" #include "block/blockjob_int.h" #include "qapi/error.h" @@ -167,9 +169,13 @@ static int coroutine_fn commit_run(Job *job, Error **errp) break; } /* Copy if allocated above the base */ - ret = blk_co_is_allocated_above(s->top, s->base_overlay, true, - offset, COMMIT_BUFFER_SIZE, &n); - copy = (ret > 0); + WITH_GRAPH_RDLOCK_GUARD() { + ret = bdrv_co_common_block_status_above(blk_bs(s->top), + s->base_overlay, true, true, offset, COMMIT_BUFFER_SIZE, + &n, NULL, NULL, NULL); + } + + copy = (ret >= 0 && ret & BDRV_BLOCK_ALLOCATED); trace_commit_one_iteration(s, offset, n, ret); if (copy) { assert(n < SIZE_MAX); From 23743ab282af4fbb80fdc049bff2c93668c73c83 Mon Sep 17 00:00:00 2001 From: Vincent Vanlaer Date: Sat, 26 Oct 2024 18:30:06 +0200 Subject: [PATCH 0518/2760] block: move commit_run loop to separate function Signed-off-by: Vincent Vanlaer Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20241026163010.2865002-3-libvirt-e6954efa@volkihar.be> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/commit.c | 89 +++++++++++++++++++++++++++++--------------------- 1 file changed, 52 insertions(+), 37 deletions(-) diff --git a/block/commit.c b/block/commit.c index ba0ba59316..3ee0ade7df 100644 --- a/block/commit.c +++ b/block/commit.c @@ -128,6 +128,55 @@ static void commit_clean(Job *job) blk_unref(s->top); } +static int commit_iteration(CommitBlockJob *s, int64_t offset, + int64_t *n, void *buf) +{ + int ret = 0; + bool copy; + bool error_in_source = true; + + /* Copy if allocated above the base */ + WITH_GRAPH_RDLOCK_GUARD() { + ret = bdrv_co_common_block_status_above(blk_bs(s->top), + s->base_overlay, true, true, offset, COMMIT_BUFFER_SIZE, + n, NULL, NULL, NULL); + } + + copy = (ret >= 0 && ret & BDRV_BLOCK_ALLOCATED); + trace_commit_one_iteration(s, offset, *n, ret); + if (copy) { + assert(*n < SIZE_MAX); + + ret = blk_co_pread(s->top, offset, *n, buf, 0); + if (ret >= 0) { + ret = blk_co_pwrite(s->base, offset, *n, buf, 0); + if (ret < 0) { + error_in_source = false; + } + } + } + if (ret < 0) { + BlockErrorAction action = block_job_error_action(&s->common, + s->on_error, + error_in_source, + -ret); + if (action == BLOCK_ERROR_ACTION_REPORT) { + return ret; + } else { + *n = 0; + return 0; + } + } + /* Publish progress */ + job_progress_update(&s->common.job, *n); + + if (copy) { + block_job_ratelimit_processed_bytes(&s->common, *n); + } + + return 0; +} + static int coroutine_fn commit_run(Job *job, Error **errp) { CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); @@ -158,9 +207,6 @@ static int coroutine_fn commit_run(Job *job, Error **errp) buf = blk_blockalign(s->top, COMMIT_BUFFER_SIZE); for (offset = 0; offset < len; offset += n) { - bool copy; - bool error_in_source = true; - /* Note that even when no rate limit is applied we need to yield * with no pending I/O here so that bdrv_drain_all() returns. */ @@ -168,42 +214,11 @@ static int coroutine_fn commit_run(Job *job, Error **errp) if (job_is_cancelled(&s->common.job)) { break; } - /* Copy if allocated above the base */ - WITH_GRAPH_RDLOCK_GUARD() { - ret = bdrv_co_common_block_status_above(blk_bs(s->top), - s->base_overlay, true, true, offset, COMMIT_BUFFER_SIZE, - &n, NULL, NULL, NULL); - } - copy = (ret >= 0 && ret & BDRV_BLOCK_ALLOCATED); - trace_commit_one_iteration(s, offset, n, ret); - if (copy) { - assert(n < SIZE_MAX); - - ret = blk_co_pread(s->top, offset, n, buf, 0); - if (ret >= 0) { - ret = blk_co_pwrite(s->base, offset, n, buf, 0); - if (ret < 0) { - error_in_source = false; - } - } - } - if (ret < 0) { - BlockErrorAction action = - block_job_error_action(&s->common, s->on_error, - error_in_source, -ret); - if (action == BLOCK_ERROR_ACTION_REPORT) { - return ret; - } else { - n = 0; - continue; - } - } - /* Publish progress */ - job_progress_update(&s->common.job, n); + ret = commit_iteration(s, offset, &n, buf); - if (copy) { - block_job_ratelimit_processed_bytes(&s->common, n); + if (ret < 0) { + return ret; } } From 2e6a9f03ba1e8145cf71eced2b97611cfa754898 Mon Sep 17 00:00:00 2001 From: Gautam Gala Date: Wed, 23 Apr 2025 10:09:13 +0200 Subject: [PATCH 0519/2760] target/s390x: Introduce constant when checking if PV header couldn't be decrypted Introduce a named constant when checking the Set Secure Configuration parameters UV call return code for the case where no valid host key was found and therefore the PV header couldn't be decrypted (0x108). Reviewed-by: Steffen Eiden Reviewed-by: Janosch Frank Signed-off-by: Gautam Gala Message-ID: <20250423080915.1048123-2-ggala@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/kvm/pv.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/s390x/kvm/pv.c b/target/s390x/kvm/pv.c index fe0a72c416..1947a3d669 100644 --- a/target/s390x/kvm/pv.c +++ b/target/s390x/kvm/pv.c @@ -147,6 +147,7 @@ bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) return true; } +#define UV_RC_SSC_INVAL_HOSTKEY 0x0108 int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, Error **errp) { int ret, pvrc; @@ -158,7 +159,7 @@ int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, Error **errp) ret = s390_pv_cmd_pvrc(KVM_PV_SET_SEC_PARMS, &args, &pvrc); if (ret) { error_setg(errp, "Failed to set secure execution parameters"); - if (pvrc == 0x108) { + if (pvrc == UV_RC_SSC_INVAL_HOSTKEY) { error_append_hint(errp, "Please check whether the image is " "correctly encrypted for this host\n"); } From e27cbd17dd5d46f92e2e9610e4ed12b525c548be Mon Sep 17 00:00:00 2001 From: Gautam Gala Date: Wed, 23 Apr 2025 10:09:14 +0200 Subject: [PATCH 0520/2760] target/s390x: Introduce function when exiting PV Replace an existing macro (s390_pv_cmd_exit) that looks like a function with an actual function. The function will be used when exiting PV instead of the macro. Reviewed-by: Steffen Eiden Reviewed-by: Janosch Frank Signed-off-by: Gautam Gala Reviewed-by: Thomas Huth Message-ID: <20250423080915.1048123-3-ggala@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/kvm/pv.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/target/s390x/kvm/pv.c b/target/s390x/kvm/pv.c index 1947a3d669..30b64f7f22 100644 --- a/target/s390x/kvm/pv.c +++ b/target/s390x/kvm/pv.c @@ -59,14 +59,12 @@ static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data, */ #define s390_pv_cmd(cmd, data) __s390_pv_cmd(cmd, #cmd, data, NULL) #define s390_pv_cmd_pvrc(cmd, data, pvrc) __s390_pv_cmd(cmd, #cmd, data, pvrc) -#define s390_pv_cmd_exit(cmd, data) \ -{ \ - int rc; \ - \ - rc = __s390_pv_cmd(cmd, #cmd, data, NULL); \ - if (rc) { \ - exit(1); \ - } \ + +static void s390_pv_cmd_exit(uint32_t cmd, void *data) +{ + if (s390_pv_cmd(cmd, data)) { + exit(1); + } } int s390_pv_query_info(void) From 55a494e53e1f0c73ba5cfb1b072fed9035b7961b Mon Sep 17 00:00:00 2001 From: Gautam Gala Date: Wed, 23 Apr 2025 10:09:15 +0200 Subject: [PATCH 0521/2760] target/s390x: Return UVC cmd code, RC and RRC value when DIAG 308 Subcode 10 fails to enter secure mode Extend DIAG308 subcode 10 to return the UVC RC, RRC and command code in bit positions 32-47, 16-31, and 0-15 of register R1 + 1 if the function does not complete successfully (in addition to the previously returned diag response code in bit position 47-63). Reviewed-by: Janosch Frank Signed-off-by: Gautam Gala Reviewed-by: Steffen Eiden Message-ID: <20250423080915.1048123-4-ggala@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/s390x/ipl.c | 11 ++++---- hw/s390x/ipl.h | 6 +++-- hw/s390x/s390-virtio-ccw.c | 14 ++++++----- target/s390x/kvm/pv.c | 51 +++++++++++++++++++++++++++----------- target/s390x/kvm/pv.h | 26 +++++++++++++------ 5 files changed, 73 insertions(+), 35 deletions(-) diff --git a/hw/s390x/ipl.c b/hw/s390x/ipl.c index 716a6b7869..2f082396c7 100644 --- a/hw/s390x/ipl.c +++ b/hw/s390x/ipl.c @@ -26,7 +26,6 @@ #include "hw/s390x/vfio-ccw.h" #include "hw/s390x/css.h" #include "hw/s390x/ebcdic.h" -#include "target/s390x/kvm/pv.h" #include "hw/scsi/scsi.h" #include "hw/virtio/virtio-net.h" #include "ipl.h" @@ -676,7 +675,7 @@ static void s390_ipl_prepare_qipl(S390CPU *cpu) cpu_physical_memory_unmap(addr, len, 1, len); } -int s390_ipl_prepare_pv_header(Error **errp) +int s390_ipl_prepare_pv_header(struct S390PVResponse *pv_resp, Error **errp) { IplParameterBlock *ipib = s390_ipl_get_iplb_pv(); IPLBlockPV *ipib_pv = &ipib->pv; @@ -685,12 +684,13 @@ int s390_ipl_prepare_pv_header(Error **errp) cpu_physical_memory_read(ipib_pv->pv_header_addr, hdr, ipib_pv->pv_header_len); - rc = s390_pv_set_sec_parms((uintptr_t)hdr, ipib_pv->pv_header_len, errp); + rc = s390_pv_set_sec_parms((uintptr_t)hdr, ipib_pv->pv_header_len, + pv_resp, errp); g_free(hdr); return rc; } -int s390_ipl_pv_unpack(void) +int s390_ipl_pv_unpack(struct S390PVResponse *pv_resp) { IplParameterBlock *ipib = s390_ipl_get_iplb_pv(); IPLBlockPV *ipib_pv = &ipib->pv; @@ -699,7 +699,8 @@ int s390_ipl_pv_unpack(void) for (i = 0; i < ipib_pv->num_comp; i++) { rc = s390_pv_unpack(ipib_pv->components[i].addr, TARGET_PAGE_ALIGN(ipib_pv->components[i].size), - ipib_pv->components[i].tweak_pref); + ipib_pv->components[i].tweak_pref, + pv_resp); if (rc) { break; } diff --git a/hw/s390x/ipl.h b/hw/s390x/ipl.h index cb55101f06..505cded490 100644 --- a/hw/s390x/ipl.h +++ b/hw/s390x/ipl.h @@ -20,6 +20,7 @@ #include "hw/qdev-core.h" #include "hw/s390x/ipl/qipl.h" #include "qom/object.h" +#include "target/s390x/kvm/pv.h" #define DIAG308_FLAGS_LP_VALID 0x80 #define MAX_BOOT_DEVS 8 /* Max number of devices that may have a bootindex */ @@ -28,8 +29,9 @@ void s390_ipl_convert_loadparm(char *ascii_lp, uint8_t *ebcdic_lp); void s390_ipl_fmt_loadparm(uint8_t *loadparm, char *str, Error **errp); void s390_rebuild_iplb(uint16_t index, IplParameterBlock *iplb); void s390_ipl_update_diag308(IplParameterBlock *iplb); -int s390_ipl_prepare_pv_header(Error **errp); -int s390_ipl_pv_unpack(void); +int s390_ipl_prepare_pv_header(struct S390PVResponse *pv_resp, + Error **errp); +int s390_ipl_pv_unpack(struct S390PVResponse *pv_resp); void s390_ipl_prepare_cpu(S390CPU *cpu); IplParameterBlock *s390_ipl_get_iplb(void); IplParameterBlock *s390_ipl_get_iplb_pv(void); diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index 94edd42dd2..d5658afed9 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -365,7 +365,8 @@ static void s390_machine_unprotect(S390CcwMachineState *ms) ram_block_discard_disable(false); } -static int s390_machine_protect(S390CcwMachineState *ms) +static int s390_machine_protect(S390CcwMachineState *ms, + struct S390PVResponse *pv_resp) { Error *local_err = NULL; int rc; @@ -408,19 +409,19 @@ static int s390_machine_protect(S390CcwMachineState *ms) } /* Set SE header and unpack */ - rc = s390_ipl_prepare_pv_header(&local_err); + rc = s390_ipl_prepare_pv_header(pv_resp, &local_err); if (rc) { goto out_err; } /* Decrypt image */ - rc = s390_ipl_pv_unpack(); + rc = s390_ipl_pv_unpack(pv_resp); if (rc) { goto out_err; } /* Verify integrity */ - rc = s390_pv_verify(); + rc = s390_pv_verify(pv_resp); if (rc) { goto out_err; } @@ -452,6 +453,7 @@ static void s390_pv_prepare_reset(S390CcwMachineState *ms) static void s390_machine_reset(MachineState *machine, ResetType type) { S390CcwMachineState *ms = S390_CCW_MACHINE(machine); + struct S390PVResponse pv_resp; enum s390_reset reset_type; CPUState *cs, *t; S390CPU *cpu; @@ -540,8 +542,8 @@ static void s390_machine_reset(MachineState *machine, ResetType type) } run_on_cpu(cs, s390_do_cpu_reset, RUN_ON_CPU_NULL); - if (s390_machine_protect(ms)) { - s390_pv_inject_reset_error(cs); + if (s390_machine_protect(ms, &pv_resp)) { + s390_pv_inject_reset_error(cs, pv_resp); /* * Continue after the diag308 so the guest knows something * went wrong. diff --git a/target/s390x/kvm/pv.c b/target/s390x/kvm/pv.c index 30b64f7f22..2bc916a545 100644 --- a/target/s390x/kvm/pv.c +++ b/target/s390x/kvm/pv.c @@ -30,7 +30,7 @@ static struct kvm_s390_pv_info_vm info_vm; static struct kvm_s390_pv_info_dump info_dump; static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data, - int *pvrc) + struct S390PVResponse *pv_resp) { struct kvm_pv_cmd pv_cmd = { .cmd = cmd, @@ -47,8 +47,10 @@ static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data, "IOCTL rc: %d", cmd, cmdname, pv_cmd.rc, pv_cmd.rrc, rc); } - if (pvrc) { - *pvrc = pv_cmd.rc; + if (pv_resp) { + pv_resp->cmd = cmd; + pv_resp->rc = pv_cmd.rc; + pv_resp->rrc = pv_cmd.rrc; } return rc; } @@ -57,8 +59,9 @@ static int __s390_pv_cmd(uint32_t cmd, const char *cmdname, void *data, * This macro lets us pass the command as a string to the function so * we can print it on an error. */ -#define s390_pv_cmd(cmd, data) __s390_pv_cmd(cmd, #cmd, data, NULL) -#define s390_pv_cmd_pvrc(cmd, data, pvrc) __s390_pv_cmd(cmd, #cmd, data, pvrc) +#define s390_pv_cmd(cmd, data) __s390_pv_cmd(cmd, #cmd, data, NULL) +#define s390_pv_cmd_pv_resp(cmd, data, pv_resp) \ + __s390_pv_cmd(cmd, #cmd, data, pv_resp) static void s390_pv_cmd_exit(uint32_t cmd, void *data) { @@ -146,18 +149,19 @@ bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) } #define UV_RC_SSC_INVAL_HOSTKEY 0x0108 -int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, Error **errp) +int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, + struct S390PVResponse *pv_resp, Error **errp) { - int ret, pvrc; + int ret; struct kvm_s390_pv_sec_parm args = { .origin = origin, .length = length, }; - ret = s390_pv_cmd_pvrc(KVM_PV_SET_SEC_PARMS, &args, &pvrc); + ret = s390_pv_cmd_pv_resp(KVM_PV_SET_SEC_PARMS, &args, pv_resp); if (ret) { error_setg(errp, "Failed to set secure execution parameters"); - if (pvrc == UV_RC_SSC_INVAL_HOSTKEY) { + if (pv_resp->rc == UV_RC_SSC_INVAL_HOSTKEY) { error_append_hint(errp, "Please check whether the image is " "correctly encrypted for this host\n"); } @@ -169,7 +173,8 @@ int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, Error **errp) /* * Called for each component in the SE type IPL parameter block 0. */ -int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) +int s390_pv_unpack(uint64_t addr, uint64_t size, + uint64_t tweak, struct S390PVResponse *pv_resp) { struct kvm_s390_pv_unp args = { .addr = addr, @@ -177,7 +182,7 @@ int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) .tweak = tweak, }; - return s390_pv_cmd(KVM_PV_UNPACK, &args); + return s390_pv_cmd_pv_resp(KVM_PV_UNPACK, &args, pv_resp); } void s390_pv_prep_reset(void) @@ -185,9 +190,9 @@ void s390_pv_prep_reset(void) s390_pv_cmd_exit(KVM_PV_PREP_RESET, NULL); } -int s390_pv_verify(void) +int s390_pv_verify(struct S390PVResponse *pv_resp) { - return s390_pv_cmd(KVM_PV_VERIFY, NULL); + return s390_pv_cmd_pv_resp(KVM_PV_VERIFY, NULL, pv_resp); } void s390_pv_unshare(void) @@ -195,13 +200,29 @@ void s390_pv_unshare(void) s390_pv_cmd_exit(KVM_PV_UNSHARE_ALL, NULL); } -void s390_pv_inject_reset_error(CPUState *cs) +void s390_pv_inject_reset_error(CPUState *cs, + struct S390PVResponse pv_resp) { int r1 = (cs->kvm_run->s390_sieic.ipa & 0x00f0) >> 4; CPUS390XState *env = &S390_CPU(cs)->env; + union { + struct { + uint16_t pv_cmd; + uint16_t pv_rrc; + uint16_t pv_rc; + uint16_t diag_rc; + }; + uint64_t regs; + } resp = { + .pv_cmd = pv_resp.cmd, + .pv_rrc = pv_resp.rrc, + .pv_rc = pv_resp.rc, + .diag_rc = DIAG_308_RC_INVAL_FOR_PV + }; + /* Report that we are unable to enter protected mode */ - env->regs[r1 + 1] = DIAG_308_RC_INVAL_FOR_PV; + env->regs[r1 + 1] = resp.regs; } uint64_t kvm_s390_pv_dmp_get_size_cpu(void) diff --git a/target/s390x/kvm/pv.h b/target/s390x/kvm/pv.h index 5e9c8bd351..94e885e933 100644 --- a/target/s390x/kvm/pv.h +++ b/target/s390x/kvm/pv.h @@ -16,6 +16,12 @@ #include "system/kvm.h" #include "hw/s390x/s390-virtio-ccw.h" +struct S390PVResponse { + uint16_t cmd; + uint16_t rrc; + uint16_t rc; +}; + #ifdef CONFIG_KVM #include "cpu.h" @@ -42,12 +48,15 @@ int s390_pv_query_info(void); int s390_pv_vm_enable(void); void s390_pv_vm_disable(void); bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms); -int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, Error **errp); -int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak); +int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, + struct S390PVResponse *pv_resp, Error **errp); +int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak, + struct S390PVResponse *pv_resp); void s390_pv_prep_reset(void); -int s390_pv_verify(void); +int s390_pv_verify(struct S390PVResponse *pv_resp); void s390_pv_unshare(void); -void s390_pv_inject_reset_error(CPUState *cs); +void s390_pv_inject_reset_error(CPUState *cs, + struct S390PVResponse pv_resp); uint64_t kvm_s390_pv_dmp_get_size_cpu(void); uint64_t kvm_s390_pv_dmp_get_size_mem_state(void); uint64_t kvm_s390_pv_dmp_get_size_completion_data(void); @@ -63,12 +72,15 @@ static inline int s390_pv_vm_enable(void) { return 0; } static inline void s390_pv_vm_disable(void) {} static inline bool s390_pv_vm_try_disable_async(S390CcwMachineState *ms) { return false; } static inline int s390_pv_set_sec_parms(uint64_t origin, uint64_t length, + struct S390PVResponse *pv_resp, Error **errp) { return 0; } -static inline int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak) { return 0; } +static inline int s390_pv_unpack(uint64_t addr, uint64_t size, uint64_t tweak, + struct S390PVResponse *pv_resp) { return 0; } static inline void s390_pv_prep_reset(void) {} -static inline int s390_pv_verify(void) { return 0; } +static inline int s390_pv_verify(struct S390PVResponse *pv_resp) { return 0; } static inline void s390_pv_unshare(void) {} -static inline void s390_pv_inject_reset_error(CPUState *cs) {}; +static inline void s390_pv_inject_reset_error(CPUState *cs, + struct S390PVResponse pv_resp) {}; static inline uint64_t kvm_s390_pv_dmp_get_size_cpu(void) { return 0; } static inline uint64_t kvm_s390_pv_dmp_get_size_mem_state(void) { return 0; } static inline uint64_t kvm_s390_pv_dmp_get_size_completion_data(void) { return 0; } From 71a30d54e6ab1d5c102a8bee2c263414697402ea Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Tue, 29 Apr 2025 17:56:54 +0200 Subject: [PATCH 0522/2760] file-posix: Fix crash on discard_granularity == 0 Block devices that don't support discard have a discard_granularity of 0. Currently, this results in a division by zero when we try to make sure that it's a multiple of request_alignment. Only try to update bs->bl.pdiscard_alignment when we got a non-zero discard_granularity from sysfs. Fixes: f605796aae4 ('file-posix: probe discard alignment on Linux block devices') Signed-off-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi Reviewed-by: Eric Blake Message-ID: <20250429155654.102735-1-kwolf@redhat.com> Signed-off-by: Stefan Hajnoczi --- block/file-posix.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/file-posix.c b/block/file-posix.c index 0d6e12f880..0d85123d0f 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -1573,7 +1573,7 @@ static void raw_refresh_limits(BlockDriverState *bs, Error **errp) int ret; ret = hdev_get_pdiscard_alignment(&st, &dalign); - if (ret == 0) { + if (ret == 0 && dalign != 0) { uint32_t ralign = bs->bl.request_alignment; /* Probably never happens, but handle it just in case */ From 6b1c744ec0d66d6d568f9a156282153fc11a21cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 25 Apr 2025 13:17:12 +0100 Subject: [PATCH 0523/2760] meson/configure: add 'valgrind' option & --{en, dis}able-valgrind flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently valgrind debugging support for coroutine stacks is enabled unconditionally when valgrind/valgrind.h is found. There is no way to disable valgrind support if valgrind.h is present in the build env. This is bad for distros, as an dependency far down the chain may cause valgrind.h to become installed, inadvertently enabling QEMU's valgrind debugging support. It also means if a distro wants valgrind support there is no way to mandate this. The solution is to add a 'valgrind' build feature to meson and thus configure script. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250425121713.1913424-1-berrange@redhat.com> Signed-off-by: Thomas Huth --- meson.build | 13 ++++++++++++- meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 3 +++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/meson.build b/meson.build index 8ae70dbe45..ccd6c71577 100644 --- a/meson.build +++ b/meson.build @@ -2618,7 +2618,17 @@ config_host_data.set('CONFIG_FSTRIM', qga_fstrim) # has_header config_host_data.set('CONFIG_EPOLL', cc.has_header('sys/epoll.h')) config_host_data.set('CONFIG_LINUX_MAGIC_H', cc.has_header('linux/magic.h')) -config_host_data.set('CONFIG_VALGRIND_H', cc.has_header('valgrind/valgrind.h')) +valgrind = false +if get_option('valgrind').allowed() + if cc.has_header('valgrind/valgrind.h') + valgrind = true + else + if get_option('valgrind').enabled() + error('valgrind requested but valgrind.h not found') + endif + endif +endif +config_host_data.set('CONFIG_VALGRIND_H', valgrind) config_host_data.set('HAVE_BTRFS_H', cc.has_header('linux/btrfs.h')) config_host_data.set('HAVE_DRM_H', cc.has_header('libdrm/drm.h')) config_host_data.set('HAVE_OPENAT2_H', cc.has_header('linux/openat2.h')) @@ -4905,6 +4915,7 @@ endif if host_os == 'darwin' summary_info += {'ParavirtualizedGraphics support': pvg} endif +summary_info += {'valgrind': valgrind} summary(summary_info, bool_yn: true, section: 'Dependencies') if host_arch == 'unknown' diff --git a/meson_options.txt b/meson_options.txt index 59d973bca0..0b4115e733 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -115,6 +115,8 @@ option('dbus_display', type: 'feature', value: 'auto', description: '-display dbus support') option('tpm', type : 'feature', value : 'auto', description: 'TPM support') +option('valgrind', type : 'feature', value: 'auto', + description: 'valgrind debug support for coroutine stacks') # Do not enable it by default even for Mingw32, because it doesn't # work on Wine. diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 3e8e00852b..d76a239130 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -198,6 +198,7 @@ meson_options_help() { printf "%s\n" ' u2f U2F emulation support' printf "%s\n" ' uadk UADK Library support' printf "%s\n" ' usb-redir libusbredir support' + printf "%s\n" ' valgrind valgrind debug support for coroutine stacks' printf "%s\n" ' vde vde network backend support' printf "%s\n" ' vdi vdi image format support' printf "%s\n" ' vduse-blk-export' @@ -526,6 +527,8 @@ _meson_option_parse() { --disable-ubsan) printf "%s" -Dubsan=false ;; --enable-usb-redir) printf "%s" -Dusb_redir=enabled ;; --disable-usb-redir) printf "%s" -Dusb_redir=disabled ;; + --enable-valgrind) printf "%s" -Dvalgrind=enabled ;; + --disable-valgrind) printf "%s" -Dvalgrind=disabled ;; --enable-vde) printf "%s" -Dvde=enabled ;; --disable-vde) printf "%s" -Dvde=disabled ;; --enable-vdi) printf "%s" -Dvdi=enabled ;; From d64db833d6e3cbe9ea5f36342480f920f3675cea Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 25 Apr 2025 14:07:10 +0200 Subject: [PATCH 0524/2760] Drop support for Python 3.8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Python 3.8 went "end of life" in October 2024 and Fedora 42 dropped this version already, so the "python" CI job is currently failing. Thus it's time to drop support for this Python version in QEMU, too. While we're at it, also look for "python3.13" in the configure script. Message-ID: <20250425120710.879518-1-thuth@redhat.com> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Signed-off-by: Thomas Huth --- configure | 14 +++++++------- docs/about/build-platforms.rst | 2 +- python/Makefile | 8 ++++---- python/setup.cfg | 7 +++---- python/tests/minreqs.txt | 2 +- scripts/qapi/mypy.ini | 2 +- tests/docker/dockerfiles/python.docker | 1 - 7 files changed, 17 insertions(+), 19 deletions(-) diff --git a/configure b/configure index 000309cf61..40705afdf5 100755 --- a/configure +++ b/configure @@ -540,17 +540,17 @@ if test -n "$linux_arch" && ! test -d "$source_path/linux-headers/asm-$linux_arc fi check_py_version() { - # We require python >= 3.8. + # We require python >= 3.9. # NB: a True python conditional creates a non-zero return code (Failure) - "$1" -c 'import sys; sys.exit(sys.version_info < (3,8))' + "$1" -c 'import sys; sys.exit(sys.version_info < (3,9))' } first_python= if test -z "${PYTHON}"; then # A bare 'python' is traditionally python 2.x, but some distros # have it as python 3.x, so check in both places. - for binary in python3 python python3.12 python3.11 \ - python3.10 python3.9 python3.8; do + for binary in python3 python python3.13 python3.12 python3.11 \ + python3.10 python3.9 ; do if has "$binary"; then python=$(command -v "$binary") if check_py_version "$python"; then @@ -933,7 +933,7 @@ then # If first_python is set, there was a binary somewhere even though # it was not suitable. Use it for the error message. if test -n "$first_python"; then - error_exit "Cannot use '$first_python', Python >= 3.8 is required." \ + error_exit "Cannot use '$first_python', Python >= 3.9 is required." \ "Use --python=/path/to/python to specify a supported Python." else error_exit "Python not found. Use --python=/path/to/python" @@ -941,11 +941,11 @@ then fi if ! check_py_version "$python"; then - error_exit "Cannot use '$python', Python >= 3.8 is required." \ + error_exit "Cannot use '$python', Python >= 3.9 is required." \ "Use --python=/path/to/python to specify a supported Python." \ "Maybe try:" \ " openSUSE Leap 15.3+: zypper install python39" \ - " CentOS 8: dnf install python38" + " CentOS: dnf install python3.12" fi # Resolve PATH diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index 52521552c8..c3651871d2 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -101,7 +101,7 @@ Python runtime option of the ``configure`` script to point QEMU to a supported version of the Python runtime. - As of QEMU |version|, the minimum supported version of Python is 3.8. + As of QEMU |version|, the minimum supported version of Python is 3.9. Python build dependencies Some of QEMU's build dependencies are written in Python. Usually these diff --git a/python/Makefile b/python/Makefile index 1fa4ba2498..764b79ccb2 100644 --- a/python/Makefile +++ b/python/Makefile @@ -9,13 +9,13 @@ help: @echo "make check-minreqs:" @echo " Run tests in the minreqs virtual environment." @echo " These tests use the oldest dependencies." - @echo " Requires: Python 3.8" - @echo " Hint (Fedora): 'sudo dnf install python3.8'" + @echo " Requires: Python 3.9" + @echo " Hint (Fedora): 'sudo dnf install python3.9'" @echo "" @echo "make check-tox:" @echo " Run tests against multiple python versions." @echo " These tests use the newest dependencies." - @echo " Requires: Python 3.8 - 3.11, and tox." + @echo " Requires: Python 3.9 - 3.11, and tox." @echo " Hint (Fedora): 'sudo dnf install python3-tox python3.11'" @echo " The variable QEMU_TOX_EXTRA_ARGS can be use to pass extra" @echo " arguments to tox". @@ -59,7 +59,7 @@ PIP_INSTALL = pip install --disable-pip-version-check min-venv: $(QEMU_MINVENV_DIR) $(QEMU_MINVENV_DIR)/bin/activate $(QEMU_MINVENV_DIR) $(QEMU_MINVENV_DIR)/bin/activate: setup.cfg tests/minreqs.txt @echo "VENV $(QEMU_MINVENV_DIR)" - @python3.8 -m venv $(QEMU_MINVENV_DIR) + @python3.9 -m venv $(QEMU_MINVENV_DIR) @( \ echo "ACTIVATE $(QEMU_MINVENV_DIR)"; \ . $(QEMU_MINVENV_DIR)/bin/activate; \ diff --git a/python/setup.cfg b/python/setup.cfg index cf5af7e664..c48dff280a 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -14,7 +14,6 @@ classifiers = Natural Language :: English Operating System :: OS Independent Programming Language :: Python :: 3 :: Only - Programming Language :: Python :: 3.8 Programming Language :: Python :: 3.9 Programming Language :: Python :: 3.10 Programming Language :: Python :: 3.11 @@ -23,7 +22,7 @@ classifiers = Typing :: Typed [options] -python_requires = >= 3.8 +python_requires = >= 3.9 packages = qemu.qmp qemu.machine @@ -78,7 +77,7 @@ exclude = __pycache__, [mypy] strict = True -python_version = 3.8 +python_version = 3.9 warn_unused_configs = True namespace_packages = True warn_unused_ignores = False @@ -186,7 +185,7 @@ multi_line_output=3 # of python available on your system to run this test. [tox:tox] -envlist = py38, py39, py310, py311, py312, py313 +envlist = py39, py310, py311, py312, py313 skip_missing_interpreters = true [testenv] diff --git a/python/tests/minreqs.txt b/python/tests/minreqs.txt index a3f423efd8..6445407ba8 100644 --- a/python/tests/minreqs.txt +++ b/python/tests/minreqs.txt @@ -1,5 +1,5 @@ # This file lists the ***oldest possible dependencies*** needed to run -# "make check" successfully under ***Python 3.8***. It is used primarily +# "make check" successfully under ***Python 3.9***. It is used primarily # by GitLab CI to ensure that our stated minimum versions in setup.cfg # are truthful and regularly validated. # diff --git a/scripts/qapi/mypy.ini b/scripts/qapi/mypy.ini index 8109470a03..c9dbcec2db 100644 --- a/scripts/qapi/mypy.ini +++ b/scripts/qapi/mypy.ini @@ -1,4 +1,4 @@ [mypy] strict = True disallow_untyped_calls = False -python_version = 3.8 +python_version = 3.9 diff --git a/tests/docker/dockerfiles/python.docker b/tests/docker/dockerfiles/python.docker index 8f0af9ef25..59e70a0248 100644 --- a/tests/docker/dockerfiles/python.docker +++ b/tests/docker/dockerfiles/python.docker @@ -15,7 +15,6 @@ ENV PACKAGES \ python3.11 \ python3.12 \ python3.13 \ - python3.8 \ python3.9 RUN dnf install -y $PACKAGES From bcfee4938f8d4e8bf5f49981d3c8a78cf267cb4e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 24 Apr 2025 10:54:26 +0200 Subject: [PATCH 0525/2760] tests/functional/test_ppc64_pseries: Skip test_ppc64_linux_smt_boot if necessary The test_ppc64_linux_smt_boot function lacks the set_machine('pseries'), so this test is currently failing in case the 'pseries' machine has not been compiled into the binary. Add the check now to fix it. Message-ID: <20250424085426.663377-1-thuth@redhat.com> Reviewed-by: Harsh Prateek Bora Signed-off-by: Thomas Huth --- tests/functional/test_ppc64_pseries.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/test_ppc64_pseries.py b/tests/functional/test_ppc64_pseries.py index fdc404ed03..67057934e8 100755 --- a/tests/functional/test_ppc64_pseries.py +++ b/tests/functional/test_ppc64_pseries.py @@ -63,6 +63,7 @@ def test_ppc64_linux_hpt_smp_boot(self): wait_for_console_pattern(self, self.good_message, self.panic_message) def test_ppc64_linux_smt_boot(self): + self.set_machine('pseries') self.vm.add_args('-smp', '4,threads=4') self.do_test_ppc64_linux_boot() console_pattern = 'CPU maps initialized for 4 threads per core' From c6d82df70e0ad3477ea97e9dc99b97c2fbfa0c1a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 25 Mar 2025 06:51:25 +0100 Subject: [PATCH 0526/2760] meson.build: Put the D-Bus summary into the UI section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We've got a dedicated section for UI options nowadays, so the D-Bus display should get reported here, too. Message-ID: <20250325055125.253669-1-thuth@redhat.com> Reviewed-by: Marc-André Lureau Reviewed-by: Alex Bennée Signed-off-by: Thomas Huth --- meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/meson.build b/meson.build index ccd6c71577..6c61e1dcae 100644 --- a/meson.build +++ b/meson.build @@ -4619,7 +4619,6 @@ summary_info += {'Trace backends': ','.join(get_option('trace_backends'))} if 'simple' in get_option('trace_backends') summary_info += {'Trace output file': get_option('trace_file') + '-'} endif -summary_info += {'D-Bus display': dbus_display} summary_info += {'QOM debugging': get_option('qom_cast_debug')} summary_info += {'Relocatable install': get_option('relocatable')} summary_info += {'vhost-kernel support': have_vhost_kernel} @@ -4802,6 +4801,7 @@ summary_info = {} if host_os == 'darwin' summary_info += {'Cocoa support': cocoa} endif +summary_info += {'D-Bus display': dbus_display} summary_info += {'SDL support': sdl} summary_info += {'SDL image support': sdl_image} summary_info += {'GTK support': gtk} From 35817600089256a234eb23613564d94b80e93ac4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 15 Jan 2025 22:00:48 +0100 Subject: [PATCH 0527/2760] hw/rtc/mc146818rtc: Drop pre-v3 migration stream support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mc146818rtc's migration stream is at version 3 since commit 56038ef6234 ("RTC: Update the RTC clock only when reading it") from 12 years ago, released in QEMU v1.3.0! No versioned machines are that old, we can safely remove support for older streams and the qdev_set_legacy_instance_id() call. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Fabiano Rosas Message-ID: <20250115210048.25396-1-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/rtc/mc146818rtc.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/hw/rtc/mc146818rtc.c b/hw/rtc/mc146818rtc.c index 6f787be7af..f9f5cf396f 100644 --- a/hw/rtc/mc146818rtc.c +++ b/hw/rtc/mc146818rtc.c @@ -819,7 +819,7 @@ static const VMStateDescription vmstate_rtc_irq_reinject_on_ack_count = { static const VMStateDescription vmstate_rtc = { .name = "mc146818rtc", .version_id = 3, - .minimum_version_id = 1, + .minimum_version_id = 3, .pre_save = rtc_pre_save, .post_load = rtc_post_load, .fields = (const VMStateField[]) { @@ -829,13 +829,13 @@ static const VMStateDescription vmstate_rtc = { VMSTATE_TIMER_PTR(periodic_timer, MC146818RtcState), VMSTATE_INT64(next_periodic_time, MC146818RtcState), VMSTATE_UNUSED(3*8), - VMSTATE_UINT32_V(irq_coalesced, MC146818RtcState, 2), - VMSTATE_UINT32_V(period, MC146818RtcState, 2), - VMSTATE_UINT64_V(base_rtc, MC146818RtcState, 3), - VMSTATE_UINT64_V(last_update, MC146818RtcState, 3), - VMSTATE_INT64_V(offset, MC146818RtcState, 3), - VMSTATE_TIMER_PTR_V(update_timer, MC146818RtcState, 3), - VMSTATE_UINT64_V(next_alarm_time, MC146818RtcState, 3), + VMSTATE_UINT32(irq_coalesced, MC146818RtcState), + VMSTATE_UINT32(period, MC146818RtcState), + VMSTATE_UINT64(base_rtc, MC146818RtcState), + VMSTATE_UINT64(last_update, MC146818RtcState), + VMSTATE_INT64(offset, MC146818RtcState), + VMSTATE_TIMER_PTR(update_timer, MC146818RtcState), + VMSTATE_UINT64(next_alarm_time, MC146818RtcState), VMSTATE_END_OF_LIST() }, .subsections = (const VMStateDescription * const []) { @@ -929,8 +929,6 @@ static void rtc_realizefn(DeviceState *dev, Error **errp) memory_region_add_subregion(&s->io, 0, &s->coalesced_io); memory_region_add_coalescing(&s->coalesced_io, 0, 1); - qdev_set_legacy_instance_id(dev, s->io_base, 3); - object_property_add_tm(OBJECT(s), "date", rtc_get_date); qdev_init_gpio_out(dev, &s->irq, 1); From dce324fa06b358ccb01bf35611b6fc53b1f56b96 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 30 Apr 2025 11:10:47 -0700 Subject: [PATCH 0528/2760] docs/devel/build-environment: enhance MSYS2 instructions Add missing prerequisite packages, and use more explicit makepkg command. Signed-off-by: Pierrick Bouvier Message-ID: <20250430181047.2043492-1-pierrick.bouvier@linaro.org> Signed-off-by: Thomas Huth --- docs/devel/build-environment.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devel/build-environment.rst b/docs/devel/build-environment.rst index f133ef2e01..661f6ea850 100644 --- a/docs/devel/build-environment.rst +++ b/docs/devel/build-environment.rst @@ -97,11 +97,11 @@ build QEMU in MSYS2 itself. :: - pacman -S wget + pacman -S wget base-devel git wget https://raw.githubusercontent.com/msys2/MINGW-packages/refs/heads/master/mingw-w64-qemu/PKGBUILD # Some packages may be missing for your environment, installation will still # be done though. - makepkg -s PKGBUILD || true + makepkg --syncdeps --nobuild PKGBUILD || true Build on windows-aarch64 ++++++++++++++++++++++++ From 43625e35d9319821f6d51cbf2798991bca533b26 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 3 Apr 2025 16:59:29 -0700 Subject: [PATCH 0529/2760] accel/tcg: Add CPUState argument to page_unprotect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the next patch, page_unprotect will need to pass the CPUState to tb_invalidate_phys_page_unwind. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 8 +++++--- include/user/page-protection.h | 2 +- linux-user/elfload.c | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 5eef8e7f18..90b345a0cf 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -128,7 +128,7 @@ MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write) bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, uintptr_t host_pc, abi_ptr guest_addr) { - switch (page_unprotect(guest_addr, host_pc)) { + switch (page_unprotect(cpu, guest_addr, host_pc)) { case 0: /* * Fault not caused by a page marked unwritable to protect @@ -584,7 +584,7 @@ bool page_check_range(target_ulong start, target_ulong len, int flags) break; } /* Asking about writable, but has been protected: undo. */ - if (!page_unprotect(start, 0)) { + if (!page_unprotect(NULL, start, 0)) { ret = false; break; } @@ -704,11 +704,13 @@ void tb_lock_page0(tb_page_addr_t address) * immediately exited. (We can only return 2 if the 'pc' argument is * non-zero.) */ -int page_unprotect(tb_page_addr_t address, uintptr_t pc) +int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc) { PageFlagsNode *p; bool current_tb_invalidated; + assert((cpu == NULL) == (pc == 0)); + /* * Technically this isn't safe inside a signal handler. However we * know this only ever happens in a synchronous SEGV handler, so in diff --git a/include/user/page-protection.h b/include/user/page-protection.h index d5c8748d49..1de72e31e6 100644 --- a/include/user/page-protection.h +++ b/include/user/page-protection.h @@ -16,7 +16,7 @@ #include "exec/target_long.h" #include "exec/translation-block.h" -int page_unprotect(tb_page_addr_t address, uintptr_t pc); +int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc); int page_get_flags(target_ulong address); diff --git a/linux-user/elfload.c b/linux-user/elfload.c index fbfdec2f17..87c6d3ab9f 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -4260,7 +4260,7 @@ static int wmr_page_unprotect_regions(void *opaque, target_ulong start, size_t step = MAX(TARGET_PAGE_SIZE, qemu_real_host_page_size()); while (1) { - page_unprotect(start, 0); + page_unprotect(NULL, start, 0); if (end - start <= step) { break; } From 00f708841f00d8b7046d03ef88045908f394b27d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 3 Apr 2025 18:06:21 -0700 Subject: [PATCH 0530/2760] accel/tcg: Add CPUState argument to tb_invalidate_phys_page_unwind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace existing usage of current_cpu. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tb-internal.h | 3 ++- accel/tcg/tb-maint.c | 8 ++++---- accel/tcg/user-exec.c | 5 +++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/accel/tcg/tb-internal.h b/accel/tcg/tb-internal.h index 08538e2896..1078de6c99 100644 --- a/accel/tcg/tb-internal.h +++ b/accel/tcg/tb-internal.h @@ -50,6 +50,7 @@ void tb_invalidate_phys_range_fast(ram_addr_t ram_addr, uintptr_t retaddr); #endif /* CONFIG_SOFTMMU */ -bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc); +bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr, + uintptr_t pc); #endif diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index d479f53ae0..714dcaedc9 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -1045,7 +1045,8 @@ static void tb_invalidate_phys_page(tb_page_addr_t addr) * TB (because it was modified by this store and the guest CPU has * precise-SMC semantics). */ -bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc) +bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr, + uintptr_t pc) { TranslationBlock *current_tb; bool current_tb_modified; @@ -1083,15 +1084,14 @@ bool tb_invalidate_phys_page_unwind(tb_page_addr_t addr, uintptr_t pc) * the CPU state. */ current_tb_modified = true; - cpu_restore_state_from_tb(current_cpu, current_tb, pc); + cpu_restore_state_from_tb(cpu, current_tb, pc); } tb_phys_invalidate__locked(tb); } if (current_tb_modified) { /* Force execution of one insn next time. */ - CPUState *cpu = current_cpu; - cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu); + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); return true; } return false; diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 90b345a0cf..39b76d9654 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -749,7 +749,8 @@ int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc) len = TARGET_PAGE_SIZE; prot = p->flags | PAGE_WRITE; pageflags_set_clear(start, start + len - 1, PAGE_WRITE, 0); - current_tb_invalidated = tb_invalidate_phys_page_unwind(start, pc); + current_tb_invalidated = + tb_invalidate_phys_page_unwind(cpu, start, pc); } else { start = address & -host_page_size; len = host_page_size; @@ -772,7 +773,7 @@ int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc) * the corresponding translated code. */ current_tb_invalidated |= - tb_invalidate_phys_page_unwind(addr, pc); + tb_invalidate_phys_page_unwind(cpu, addr, pc); } } if (prot & PAGE_EXEC) { From e4ad80ceac03cc47d8351172f0e4625bb40e2b78 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Apr 2025 12:23:30 -0700 Subject: [PATCH 0531/2760] accel/tcg: Add CPUState arg to tb_invalidate_phys_page_range__locked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tb-maint.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 714dcaedc9..927e9c8ede 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -1100,9 +1100,12 @@ bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr, /* * @p must be non-NULL. * Call with all @pages locked. + * (@cpu, @retaddr) may be (NULL, 0) outside of a cpu context, + * in which case precise_smc need not be detected. */ static void -tb_invalidate_phys_page_range__locked(struct page_collection *pages, +tb_invalidate_phys_page_range__locked(CPUState *cpu, + struct page_collection *pages, PageDesc *p, tb_page_addr_t start, tb_page_addr_t last, uintptr_t retaddr) @@ -1194,7 +1197,7 @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last) page_start = index << TARGET_PAGE_BITS; page_last = page_start | ~TARGET_PAGE_MASK; page_last = MIN(page_last, last); - tb_invalidate_phys_page_range__locked(pages, pd, + tb_invalidate_phys_page_range__locked(NULL, pages, pd, page_start, page_last, 0); } page_collection_unlock(pages); @@ -1215,7 +1218,7 @@ static void tb_invalidate_phys_page_fast__locked(struct page_collection *pages, } assert_page_locked(p); - tb_invalidate_phys_page_range__locked(pages, p, start, start + len - 1, ra); + tb_invalidate_phys_page_range__locked(NULL, pages, p, start, start + len - 1, ra); } /* From 4af02681ff77bf105b11ee1a5ca289ca29b64a54 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Apr 2025 12:37:28 -0700 Subject: [PATCH 0532/2760] accel/tcg: Merge tb_invalidate_phys_range{__locked} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Merge tb_invalidate_phys_page_fast__locked into its only caller, tb_invalidate_phys_range_fast. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tb-maint.c | 36 +++++++++++------------------------- 1 file changed, 11 insertions(+), 25 deletions(-) diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 927e9c8ede..c893ea3073 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -1203,38 +1203,24 @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last) page_collection_unlock(pages); } -/* - * Call with all @pages in the range [@start, @start + len[ locked. - */ -static void tb_invalidate_phys_page_fast__locked(struct page_collection *pages, - tb_page_addr_t start, - unsigned len, uintptr_t ra) -{ - PageDesc *p; - - p = page_find(start >> TARGET_PAGE_BITS); - if (!p) { - return; - } - - assert_page_locked(p); - tb_invalidate_phys_page_range__locked(NULL, pages, p, start, start + len - 1, ra); -} - /* * len must be <= 8 and start must be a multiple of len. * Called via softmmu_template.h when code areas are written to with * iothread mutex not held. */ -void tb_invalidate_phys_range_fast(ram_addr_t ram_addr, - unsigned size, - uintptr_t retaddr) +void tb_invalidate_phys_range_fast(ram_addr_t start, + unsigned len, uintptr_t ra) { - struct page_collection *pages; + PageDesc *p = page_find(start >> TARGET_PAGE_BITS); - pages = page_collection_lock(ram_addr, ram_addr + size - 1); - tb_invalidate_phys_page_fast__locked(pages, ram_addr, size, retaddr); - page_collection_unlock(pages); + if (p) { + ram_addr_t last = start + len - 1; + struct page_collection *pages = page_collection_lock(start, last); + + tb_invalidate_phys_page_range__locked(NULL, pages, p, + start, last, ra); + page_collection_unlock(pages); + } } #endif /* CONFIG_USER_ONLY */ From 072e057ed90d6bbc4f01ac04e627e63f275f57f0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Apr 2025 13:06:12 -0700 Subject: [PATCH 0533/2760] accel/tcg: Add CPUState arg to tb_invalidate_phys_range MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tb-maint.c | 10 ++++++---- accel/tcg/translate-all.c | 2 +- accel/tcg/user-exec.c | 4 ++-- include/exec/exec-all.h | 3 ++- system/physmem.c | 2 +- target/arm/helper.c | 2 +- 6 files changed, 13 insertions(+), 10 deletions(-) diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index c893ea3073..c7600fc6ac 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -1012,7 +1012,8 @@ TranslationBlock *tb_link_page(TranslationBlock *tb) * Called with mmap_lock held for user-mode emulation. * NOTE: this function must not be called while a TB is running. */ -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last) +void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start, + tb_page_addr_t last) { TranslationBlock *tb; PageForEachNext n; @@ -1035,7 +1036,7 @@ static void tb_invalidate_phys_page(tb_page_addr_t addr) start = addr & TARGET_PAGE_MASK; last = addr | ~TARGET_PAGE_MASK; - tb_invalidate_phys_range(start, last); + tb_invalidate_phys_range(NULL, start, last); } /* @@ -1178,7 +1179,8 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu, * access: the virtual CPU will exit the current TB if code is modified inside * this TB. */ -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last) +void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start, + tb_page_addr_t last) { struct page_collection *pages; tb_page_addr_t index, index_last; @@ -1197,7 +1199,7 @@ void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last) page_start = index << TARGET_PAGE_BITS; page_last = page_start | ~TARGET_PAGE_MASK; page_last = MIN(page_last, last); - tb_invalidate_phys_page_range__locked(NULL, pages, pd, + tb_invalidate_phys_page_range__locked(cpu, pages, pd, page_start, page_last, 0); } page_collection_unlock(pages); diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index c007b9a190..9bf8728064 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -599,7 +599,7 @@ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr) cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); addr = get_page_addr_code(env, pc); if (addr != -1) { - tb_invalidate_phys_range(addr, addr); + tb_invalidate_phys_range(cpu, addr, addr); } } } diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 39b76d9654..2b12c077e9 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -529,7 +529,7 @@ void page_set_flags(target_ulong start, target_ulong last, int flags) ~(reset ? 0 : PAGE_STICKY)); } if (inval_tb) { - tb_invalidate_phys_range(start, last); + tb_invalidate_phys_range(NULL, start, last); } } @@ -1020,7 +1020,7 @@ int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, * be under mmap_lock() in order to prevent the creation of * another TranslationBlock in between. */ - tb_invalidate_phys_range(addr, addr + l - 1); + tb_invalidate_phys_range(NULL, addr, addr + l - 1); written = pwrite(fd, buf, l, (off_t)(uintptr_t)g2h_untagged(addr)); if (written != l) { diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 944b579d91..bee3416e7e 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -122,7 +122,8 @@ int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, /* TranslationBlock invalidate API */ void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); -void tb_invalidate_phys_range(tb_page_addr_t start, tb_page_addr_t last); +void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start, + tb_page_addr_t last); void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); #if !defined(CONFIG_USER_ONLY) diff --git a/system/physmem.c b/system/physmem.c index 16cf557d1a..637f2d8532 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -2830,7 +2830,7 @@ static void invalidate_and_set_dirty(MemoryRegion *mr, hwaddr addr, } if (dirty_log_mask & (1 << DIRTY_MEMORY_CODE)) { assert(tcg_enabled()); - tb_invalidate_phys_range(addr, addr + length - 1); + tb_invalidate_phys_range(NULL, addr, addr + length - 1); dirty_log_mask &= ~(1 << DIRTY_MEMORY_CODE); } cpu_physical_memory_set_dirty_range(addr, length, dirty_log_mask); diff --git a/target/arm/helper.c b/target/arm/helper.c index 7fb6e88630..c6fd290012 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -4987,7 +4987,7 @@ static void ic_ivau_write(CPUARMState *env, const ARMCPRegInfo *ri, mmap_lock(); - tb_invalidate_phys_range(start_address, end_address); + tb_invalidate_phys_range(env_cpu(env), start_address, end_address); mmap_unlock(); } From 7fa0f4a70c1550380b2a3ee1330f70ce6ee98072 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Apr 2025 13:10:45 -0700 Subject: [PATCH 0534/2760] accel/tcg: Add CPUState arg to tb_invalidate_phys_range_fast MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 2 +- accel/tcg/tb-internal.h | 5 ++--- accel/tcg/tb-maint.c | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index d9fb68d719..ed6de1e96e 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1340,7 +1340,7 @@ static void notdirty_write(CPUState *cpu, vaddr mem_vaddr, unsigned size, trace_memory_notdirty_write_access(mem_vaddr, ram_addr, size); if (!cpu_physical_memory_get_dirty_flag(ram_addr, DIRTY_MEMORY_CODE)) { - tb_invalidate_phys_range_fast(ram_addr, size, retaddr); + tb_invalidate_phys_range_fast(cpu, ram_addr, size, retaddr); } /* diff --git a/accel/tcg/tb-internal.h b/accel/tcg/tb-internal.h index 1078de6c99..40439f03c3 100644 --- a/accel/tcg/tb-internal.h +++ b/accel/tcg/tb-internal.h @@ -45,9 +45,8 @@ void tb_unlock_pages(TranslationBlock *); #endif #ifdef CONFIG_SOFTMMU -void tb_invalidate_phys_range_fast(ram_addr_t ram_addr, - unsigned size, - uintptr_t retaddr); +void tb_invalidate_phys_range_fast(CPUState *cpu, ram_addr_t ram_addr, + unsigned size, uintptr_t retaddr); #endif /* CONFIG_SOFTMMU */ bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr, diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index c7600fc6ac..3837f2f633 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -1210,7 +1210,7 @@ void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start, * Called via softmmu_template.h when code areas are written to with * iothread mutex not held. */ -void tb_invalidate_phys_range_fast(ram_addr_t start, +void tb_invalidate_phys_range_fast(CPUState *cpu, ram_addr_t start, unsigned len, uintptr_t ra) { PageDesc *p = page_find(start >> TARGET_PAGE_BITS); @@ -1219,7 +1219,7 @@ void tb_invalidate_phys_range_fast(ram_addr_t start, ram_addr_t last = start + len - 1; struct page_collection *pages = page_collection_lock(start, last); - tb_invalidate_phys_page_range__locked(NULL, pages, p, + tb_invalidate_phys_page_range__locked(cpu, pages, p, start, last, ra); page_collection_unlock(pages); } From 77ad412b326031687f0eeb7935350e597337c93b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 5 Apr 2025 08:43:44 -0700 Subject: [PATCH 0535/2760] accel/tcg: Convert TARGET_HAS_PRECISE_SMC to TCGCPUOps.precise_smc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of having a compile-time TARGET_HAS_PRECISE_SMC definition, have each target set the 'precise_smc' field in the TCGCPUOps structure. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tb-maint.c | 32 +++++++++++++------------------- accel/tcg/user-exec.c | 10 +++++----- include/accel/tcg/cpu-ops.h | 7 +++++++ include/exec/poison.h | 1 - target/i386/cpu.h | 4 ---- target/i386/tcg/tcg-cpu.c | 1 + target/s390x/cpu.c | 1 + target/s390x/cpu.h | 2 -- 8 files changed, 27 insertions(+), 31 deletions(-) diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 3837f2f633..1596767879 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -28,6 +28,7 @@ #include "exec/mmap-lock.h" #include "exec/tb-flush.h" #include "exec/target_page.h" +#include "accel/tcg/cpu-ops.h" #include "tb-internal.h" #include "system/tcg.h" #include "tcg/tcg.h" @@ -1042,9 +1043,7 @@ static void tb_invalidate_phys_page(tb_page_addr_t addr) /* * Called with mmap_lock held. If pc is not 0 then it indicates the * host PC of the faulting store instruction that caused this invalidate. - * Returns true if the caller needs to abort execution of the current - * TB (because it was modified by this store and the guest CPU has - * precise-SMC semantics). + * Returns true if the caller needs to abort execution of the current TB. */ bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr, uintptr_t pc) @@ -1059,10 +1058,7 @@ bool tb_invalidate_phys_page_unwind(CPUState *cpu, tb_page_addr_t addr, * Without precise smc semantics, or when outside of a TB, * we can skip to invalidate. */ -#ifndef TARGET_HAS_PRECISE_SMC - pc = 0; -#endif - if (!pc) { + if (!pc || !cpu || !cpu->cc->tcg_ops->precise_smc) { tb_invalidate_phys_page(addr); return false; } @@ -1113,14 +1109,16 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu, { TranslationBlock *tb; PageForEachNext n; -#ifdef TARGET_HAS_PRECISE_SMC bool current_tb_modified = false; - TranslationBlock *current_tb = retaddr ? tcg_tb_lookup(retaddr) : NULL; -#endif /* TARGET_HAS_PRECISE_SMC */ + TranslationBlock *current_tb = NULL; /* Range may not cross a page. */ tcg_debug_assert(((start ^ last) & TARGET_PAGE_MASK) == 0); + if (retaddr && cpu && cpu->cc->tcg_ops->precise_smc) { + current_tb = tcg_tb_lookup(retaddr); + } + /* * We remove all the TBs in the range [start, last]. * XXX: see if in some cases it could be faster to invalidate all the code @@ -1138,8 +1136,7 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu, tb_last = tb_start + (tb_last & ~TARGET_PAGE_MASK); } if (!(tb_last < start || tb_start > last)) { -#ifdef TARGET_HAS_PRECISE_SMC - if (current_tb == tb && + if (unlikely(current_tb == tb) && (tb_cflags(current_tb) & CF_COUNT_MASK) != 1) { /* * If we are modifying the current TB, we must stop @@ -1149,9 +1146,8 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu, * restore the CPU state. */ current_tb_modified = true; - cpu_restore_state_from_tb(current_cpu, current_tb, retaddr); + cpu_restore_state_from_tb(cpu, current_tb, retaddr); } -#endif /* TARGET_HAS_PRECISE_SMC */ tb_phys_invalidate__locked(tb); } } @@ -1161,15 +1157,13 @@ tb_invalidate_phys_page_range__locked(CPUState *cpu, tlb_unprotect_code(start); } -#ifdef TARGET_HAS_PRECISE_SMC - if (current_tb_modified) { + if (unlikely(current_tb_modified)) { page_collection_unlock(pages); /* Force execution of one insn next time. */ - current_cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(current_cpu); + cpu->cflags_next_tb = 1 | CF_NOIRQ | curr_cflags(cpu); mmap_unlock(); - cpu_loop_exit_noexc(current_cpu); + cpu_loop_exit_noexc(cpu); } -#endif } /* diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 2b12c077e9..112292b729 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -733,12 +733,12 @@ int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc) * this thread raced with another one which got here first and * set the page to PAGE_WRITE and did the TB invalidate for us. */ -#ifdef TARGET_HAS_PRECISE_SMC - TranslationBlock *current_tb = tcg_tb_lookup(pc); - if (current_tb) { - current_tb_invalidated = tb_cflags(current_tb) & CF_INVALID; + if (pc && cpu->cc->tcg_ops->precise_smc) { + TranslationBlock *current_tb = tcg_tb_lookup(pc); + if (current_tb) { + current_tb_invalidated = tb_cflags(current_tb) & CF_INVALID; + } } -#endif } else { int host_page_size = qemu_real_host_page_size(); target_ulong start, len, i; diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index 0e4352513d..60b5e97205 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -28,6 +28,13 @@ struct TCGCPUOps { */ bool mttcg_supported; + /** + * @precise_smc: Stores which modify code within the current TB force + * the TB to exit; the next executed instruction will see + * the result of the store. + */ + bool precise_smc; + /** * @guest_default_memory_order: default barrier that is required * for the guest memory ordering. diff --git a/include/exec/poison.h b/include/exec/poison.h index bc422719d8..a779adbb7a 100644 --- a/include/exec/poison.h +++ b/include/exec/poison.h @@ -37,7 +37,6 @@ #pragma GCC poison TARGET_NAME #pragma GCC poison TARGET_BIG_ENDIAN #pragma GCC poison TCG_GUEST_DEFAULT_MO -#pragma GCC poison TARGET_HAS_PRECISE_SMC #pragma GCC poison TARGET_LONG_BITS #pragma GCC poison TARGET_FMT_lx diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 54bf9639f1..3182ba413b 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -35,10 +35,6 @@ #define XEN_NR_VIRQS 24 -/* support for self modifying code even if the modified instruction is - close to the modifying instruction */ -#define TARGET_HAS_PRECISE_SMC - #ifdef TARGET_X86_64 #define I386_ELF_MACHINE EM_X86_64 #define ELF_MACHINE_UNAME "x86_64" diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index e53aaa31bf..192812656c 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -126,6 +126,7 @@ static bool x86_debug_check_breakpoint(CPUState *cs) const TCGCPUOps x86_tcg_ops = { .mttcg_supported = true, + .precise_smc = true, /* * The x86 has a strong memory model with some store-after-load re-ordering */ diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 3d644f5e23..99ff58affc 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -346,6 +346,7 @@ void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, static const TCGCPUOps s390_tcg_ops = { .mttcg_supported = true, + .precise_smc = true, /* * The z/Architecture has a strong memory model with some * store-after-load re-ordering. diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index d9ca2506e2..530d97ccf1 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -35,8 +35,6 @@ #define ELF_MACHINE_UNAME "S390X" -#define TARGET_HAS_PRECISE_SMC - #define MMU_USER_IDX 0 #define S390_MAX_CPUS 248 From 80e865668d953c012723c3af9fd1ff7258cef864 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 2 Apr 2025 08:40:31 -0700 Subject: [PATCH 0536/2760] accel/tcg: Simplify CPU_TLB_DYN_MAX_BITS Stop taking TARGET_VIRT_ADDR_SPACE_BITS into account. Since we currently bound CPU_TLB_DYN_MAX_BITS to 22, the new bound with a 4k page size is 20, which isn't so different. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tlb-bounds.h | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/accel/tcg/tlb-bounds.h b/accel/tcg/tlb-bounds.h index efd34d4793..f83d9ac9ee 100644 --- a/accel/tcg/tlb-bounds.h +++ b/accel/tcg/tlb-bounds.h @@ -7,26 +7,7 @@ #define ACCEL_TCG_TLB_BOUNDS_H #define CPU_TLB_DYN_MIN_BITS 6 +#define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS) #define CPU_TLB_DYN_DEFAULT_BITS 8 -# if HOST_LONG_BITS == 32 -/* Make sure we do not require a double-word shift for the TLB load */ -# define CPU_TLB_DYN_MAX_BITS (32 - TARGET_PAGE_BITS) -# else /* HOST_LONG_BITS == 64 */ -/* - * Assuming TARGET_PAGE_BITS==12, with 2**22 entries we can cover 2**(22+12) == - * 2**34 == 16G of address space. This is roughly what one would expect a - * TLB to cover in a modern (as of 2018) x86_64 CPU. For instance, Intel - * Skylake's Level-2 STLB has 16 1G entries. - * Also, make sure we do not size the TLB past the guest's address space. - */ -# ifdef TARGET_PAGE_BITS_VARY -# define CPU_TLB_DYN_MAX_BITS \ - MIN(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) -# else -# define CPU_TLB_DYN_MAX_BITS \ - MIN_CONST(22, TARGET_VIRT_ADDR_SPACE_BITS - TARGET_PAGE_BITS) -# endif -# endif - #endif /* ACCEL_TCG_TLB_BOUNDS_H */ From 2e8fe327eb67f90822a3e8a8fb6c914dd573f299 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 2 Apr 2025 12:30:39 -0700 Subject: [PATCH 0537/2760] accel/tcg: Simplify L1_MAP_ADDR_SPACE_BITS Stop taking TARGET_PHYS_ADDR_SPACE_BITS into account. Simply allow the entire ram_addr_t space. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/tb-maint.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 1596767879..13d0376bc7 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -160,11 +160,7 @@ static PageForEachNext foreach_tb_next(PageForEachNext tb, /* * In system mode we want L1_MAP to be based on ram offsets. */ -#if HOST_LONG_BITS < TARGET_PHYS_ADDR_SPACE_BITS -# define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS -#else -# define L1_MAP_ADDR_SPACE_BITS TARGET_PHYS_ADDR_SPACE_BITS -#endif +#define L1_MAP_ADDR_SPACE_BITS HOST_LONG_BITS /* Size of the L2 (and L3, etc) page tables. */ #define V_L2_BITS 10 From dfda9281266d57899dc03fc613a25587babd67aa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 5 Apr 2025 09:36:27 -0700 Subject: [PATCH 0538/2760] accel/tcg: Merge internal-target.h into internal-common.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There's nothing left in internal-target.h that is target specific. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 1 - accel/tcg/cputlb.c | 1 - accel/tcg/internal-common.h | 29 +++++++++++++++++++++++ accel/tcg/internal-target.h | 46 ------------------------------------- accel/tcg/tb-maint.c | 1 - accel/tcg/translate-all.c | 1 - accel/tcg/user-exec.c | 1 - 7 files changed, 29 insertions(+), 51 deletions(-) delete mode 100644 accel/tcg/internal-target.h diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 87eba83d7d..279df5fae7 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -46,7 +46,6 @@ #include "tb-context.h" #include "tb-internal.h" #include "internal-common.h" -#include "internal-target.h" /* -icount align implementation. */ diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index ed6de1e96e..ca69128232 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -43,7 +43,6 @@ #include "tb-internal.h" #include "tlb-bounds.h" #include "internal-common.h" -#include "internal-target.h" #ifdef CONFIG_PLUGIN #include "qemu/plugin-memory.h" #endif diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index 2f00560d10..573e8438c3 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -11,6 +11,7 @@ #include "exec/cpu-common.h" #include "exec/translation-block.h" +#include "exec/mmap-lock.h" extern int64_t max_delay; extern int64_t max_advance; @@ -108,4 +109,32 @@ static inline tb_page_addr_t get_page_addr_code(CPUArchState *env, return get_page_addr_code_hostp(env, addr, NULL); } +/* + * Access to the various translations structures need to be serialised + * via locks for consistency. In user-mode emulation access to the + * memory related structures are protected with mmap_lock. + * In !user-mode we use per-page locks. + */ +#ifdef CONFIG_USER_ONLY +#define assert_memory_lock() tcg_debug_assert(have_mmap_lock()) +#else +#define assert_memory_lock() +#endif + +#if defined(CONFIG_SOFTMMU) && defined(CONFIG_DEBUG_TCG) +void assert_no_pages_locked(void); +#else +static inline void assert_no_pages_locked(void) { } +#endif + +#ifdef CONFIG_USER_ONLY +static inline void page_table_config_init(void) { } +#else +void page_table_config_init(void); +#endif + +#ifndef CONFIG_USER_ONLY +G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); +#endif /* CONFIG_USER_ONLY */ + #endif diff --git a/accel/tcg/internal-target.h b/accel/tcg/internal-target.h deleted file mode 100644 index 9a9cef3140..0000000000 --- a/accel/tcg/internal-target.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Internal execution defines for qemu (target specific) - * - * Copyright (c) 2003 Fabrice Bellard - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef ACCEL_TCG_INTERNAL_TARGET_H -#define ACCEL_TCG_INTERNAL_TARGET_H - -#include "cpu-param.h" -#include "exec/exec-all.h" -#include "exec/translation-block.h" -#include "tb-internal.h" -#include "exec/mmap-lock.h" - -/* - * Access to the various translations structures need to be serialised - * via locks for consistency. In user-mode emulation access to the - * memory related structures are protected with mmap_lock. - * In !user-mode we use per-page locks. - */ -#ifdef CONFIG_USER_ONLY -#define assert_memory_lock() tcg_debug_assert(have_mmap_lock()) -#else -#define assert_memory_lock() -#endif - -#if defined(CONFIG_SOFTMMU) && defined(CONFIG_DEBUG_TCG) -void assert_no_pages_locked(void); -#else -static inline void assert_no_pages_locked(void) { } -#endif - -#ifdef CONFIG_USER_ONLY -static inline void page_table_config_init(void) { } -#else -void page_table_config_init(void); -#endif - -#ifndef CONFIG_USER_ONLY -G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); -#endif /* CONFIG_USER_ONLY */ - -#endif /* ACCEL_TCG_INTERNAL_H */ diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index 13d0376bc7..b144fcd4a0 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -36,7 +36,6 @@ #include "tb-context.h" #include "tb-internal.h" #include "internal-common.h" -#include "internal-target.h" #ifdef CONFIG_USER_ONLY #include "user/page-protection.h" #endif diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 9bf8728064..38819a507b 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -66,7 +66,6 @@ #include "tb-context.h" #include "tb-internal.h" #include "internal-common.h" -#include "internal-target.h" #include "tcg/perf.h" #include "tcg/insn-start-words.h" #include "cpu.h" diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 112292b729..17e3be337f 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -39,7 +39,6 @@ #include "tcg/tcg-ldst.h" #include "backend-ldst.h" #include "internal-common.h" -#include "internal-target.h" #include "tb-internal.h" __thread uintptr_t helper_retaddr; From 54bd0b135e53d3afe666c5c960d7b2a0c1767bf4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 5 Apr 2025 09:45:48 -0700 Subject: [PATCH 0539/2760] accel/tcg: Reduce scope of tb_phys_invalidate, tb_set_jmp_target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the declarations of these functions out of exec/exec-all.h to accel/tcg/internal-common.h. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/internal-common.h | 3 +++ include/exec/exec-all.h | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index 573e8438c3..98c702422f 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -137,4 +137,7 @@ void page_table_config_init(void); G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); #endif /* CONFIG_USER_ONLY */ +void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); +void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); + #endif diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index bee3416e7e..24383b6aba 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -121,10 +121,8 @@ int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, #endif /* CONFIG_TCG */ /* TranslationBlock invalidate API */ -void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start, tb_page_addr_t last); -void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); #if !defined(CONFIG_USER_ONLY) From e1c8eb8cfec059e882066403819288d036fbbe8e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 24 Apr 2025 22:24:00 +0200 Subject: [PATCH 0540/2760] accel/tcg: Use vaddr for walk_memory_regions callback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use vaddr instead of target_ulong. At the same time, use int instead of unsigned long for flags, to match page_set_flags(). Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 10 +++++----- include/user/page-protection.h | 5 ++--- linux-user/elfload.c | 19 +++++++++---------- linux-user/syscall.c | 8 ++++---- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 17e3be337f..25d86567e7 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -199,13 +199,13 @@ int walk_memory_regions(void *priv, walk_memory_regions_fn fn) return rc; } -static int dump_region(void *priv, target_ulong start, - target_ulong end, unsigned long prot) +static int dump_region(void *opaque, vaddr start, vaddr end, int prot) { - FILE *f = (FILE *)priv; + FILE *f = opaque; - fprintf(f, TARGET_FMT_lx"-"TARGET_FMT_lx" "TARGET_FMT_lx" %c%c%c\n", - start, end, end - start, + fprintf(f, TARGET_ABI_FMT_ptr "-" TARGET_ABI_FMT_ptr + " " TARGET_ABI_FMT_ptr " %c%c%c\n", + (abi_ptr)start, (abi_ptr)end, (abi_ptr)(end - start), ((prot & PAGE_READ) ? 'r' : '-'), ((prot & PAGE_WRITE) ? 'w' : '-'), ((prot & PAGE_EXEC) ? 'x' : '-')); diff --git a/include/user/page-protection.h b/include/user/page-protection.h index 1de72e31e6..8f0b769b13 100644 --- a/include/user/page-protection.h +++ b/include/user/page-protection.h @@ -14,6 +14,7 @@ #include "cpu-param.h" #include "exec/target_long.h" +#include "exec/vaddr.h" #include "exec/translation-block.h" int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc); @@ -88,9 +89,7 @@ target_ulong page_find_range_empty(target_ulong min, target_ulong max, __attribute__((returns_nonnull)) void *page_get_target_data(target_ulong address); -typedef int (*walk_memory_regions_fn)(void *, target_ulong, - target_ulong, unsigned long); - +typedef int (*walk_memory_regions_fn)(void *, vaddr, vaddr, int); int walk_memory_regions(void *, walk_memory_regions_fn); void page_dump(FILE *f); diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 87c6d3ab9f..82ebf6a212 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -4059,8 +4059,7 @@ static void bswap_note(struct elf_note *en) /* * Calculate file (dump) size of given memory region. */ -static size_t vma_dump_size(target_ulong start, target_ulong end, - unsigned long flags) +static size_t vma_dump_size(vaddr start, vaddr end, int flags) { /* The area must be readable. */ if (!(flags & PAGE_READ)) { @@ -4253,8 +4252,8 @@ static int dump_write(int fd, const void *ptr, size_t size) return (0); } -static int wmr_page_unprotect_regions(void *opaque, target_ulong start, - target_ulong end, unsigned long flags) +static int wmr_page_unprotect_regions(void *opaque, vaddr start, + vaddr end, int flags) { if ((flags & (PAGE_WRITE | PAGE_WRITE_ORG)) == PAGE_WRITE_ORG) { size_t step = MAX(TARGET_PAGE_SIZE, qemu_real_host_page_size()); @@ -4275,8 +4274,8 @@ typedef struct { size_t size; } CountAndSizeRegions; -static int wmr_count_and_size_regions(void *opaque, target_ulong start, - target_ulong end, unsigned long flags) +static int wmr_count_and_size_regions(void *opaque, vaddr start, + vaddr end, int flags) { CountAndSizeRegions *css = opaque; @@ -4290,8 +4289,8 @@ typedef struct { off_t offset; } FillRegionPhdr; -static int wmr_fill_region_phdr(void *opaque, target_ulong start, - target_ulong end, unsigned long flags) +static int wmr_fill_region_phdr(void *opaque, vaddr start, + vaddr end, int flags) { FillRegionPhdr *d = opaque; struct elf_phdr *phdr = d->phdr; @@ -4313,8 +4312,8 @@ static int wmr_fill_region_phdr(void *opaque, target_ulong start, return 0; } -static int wmr_write_region(void *opaque, target_ulong start, - target_ulong end, unsigned long flags) +static int wmr_write_region(void *opaque, vaddr start, + vaddr end, int flags) { int fd = *(int *)opaque; size_t size = vma_dump_size(start, end, flags); diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 5826ac3adb..23b901b713 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8135,8 +8135,8 @@ static void open_self_maps_4(const struct open_self_maps_data *d, * Callback for walk_memory_regions, when read_self_maps() fails. * Proceed without the benefit of host /proc/self/maps cross-check. */ -static int open_self_maps_3(void *opaque, target_ulong guest_start, - target_ulong guest_end, unsigned long flags) +static int open_self_maps_3(void *opaque, vaddr guest_start, + vaddr guest_end, int flags) { static const MapInfo mi = { .is_priv = true }; @@ -8147,8 +8147,8 @@ static int open_self_maps_3(void *opaque, target_ulong guest_start, /* * Callback for walk_memory_regions, when read_self_maps() succeeds. */ -static int open_self_maps_2(void *opaque, target_ulong guest_start, - target_ulong guest_end, unsigned long flags) +static int open_self_maps_2(void *opaque, vaddr guest_start, + vaddr guest_end, int flags) { const struct open_self_maps_data *d = opaque; uintptr_t host_start = (uintptr_t)g2h_untagged(guest_start); From 5627d5c00a256cc180b659f7c21383e36934a80c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 24 Apr 2025 22:24:01 +0200 Subject: [PATCH 0541/2760] accel/tcg: Use vaddr in user/page-protection.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Anton Johansson Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 51 ++++++++++++++++------------------ include/user/page-protection.h | 17 +++++------- 2 files changed, 31 insertions(+), 37 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 25d86567e7..43d005e24e 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -161,7 +161,7 @@ typedef struct PageFlagsNode { static IntervalTreeRoot pageflags_root; -static PageFlagsNode *pageflags_find(target_ulong start, target_ulong last) +static PageFlagsNode *pageflags_find(vaddr start, vaddr last) { IntervalTreeNode *n; @@ -169,8 +169,7 @@ static PageFlagsNode *pageflags_find(target_ulong start, target_ulong last) return n ? container_of(n, PageFlagsNode, itree) : NULL; } -static PageFlagsNode *pageflags_next(PageFlagsNode *p, target_ulong start, - target_ulong last) +static PageFlagsNode *pageflags_next(PageFlagsNode *p, vaddr start, vaddr last) { IntervalTreeNode *n; @@ -215,14 +214,14 @@ static int dump_region(void *opaque, vaddr start, vaddr end, int prot) /* dump memory mappings */ void page_dump(FILE *f) { - const int length = sizeof(target_ulong) * 2; + const int length = sizeof(abi_ptr) * 2; fprintf(f, "%-*s %-*s %-*s %s\n", length, "start", length, "end", length, "size", "prot"); walk_memory_regions(f, dump_region); } -int page_get_flags(target_ulong address) +int page_get_flags(vaddr address) { PageFlagsNode *p = pageflags_find(address, address); @@ -245,7 +244,7 @@ int page_get_flags(target_ulong address) } /* A subroutine of page_set_flags: insert a new node for [start,last]. */ -static void pageflags_create(target_ulong start, target_ulong last, int flags) +static void pageflags_create(vaddr start, vaddr last, int flags) { PageFlagsNode *p = g_new(PageFlagsNode, 1); @@ -256,13 +255,13 @@ static void pageflags_create(target_ulong start, target_ulong last, int flags) } /* A subroutine of page_set_flags: remove everything in [start,last]. */ -static bool pageflags_unset(target_ulong start, target_ulong last) +static bool pageflags_unset(vaddr start, vaddr last) { bool inval_tb = false; while (true) { PageFlagsNode *p = pageflags_find(start, last); - target_ulong p_last; + vaddr p_last; if (!p) { break; @@ -301,8 +300,7 @@ static bool pageflags_unset(target_ulong start, target_ulong last) * A subroutine of page_set_flags: nothing overlaps [start,last], * but check adjacent mappings and maybe merge into a single range. */ -static void pageflags_create_merge(target_ulong start, target_ulong last, - int flags) +static void pageflags_create_merge(vaddr start, vaddr last, int flags) { PageFlagsNode *next = NULL, *prev = NULL; @@ -353,11 +351,11 @@ static void pageflags_create_merge(target_ulong start, target_ulong last, #define PAGE_STICKY (PAGE_ANON | PAGE_PASSTHROUGH | PAGE_TARGET_STICKY) /* A subroutine of page_set_flags: add flags to [start,last]. */ -static bool pageflags_set_clear(target_ulong start, target_ulong last, +static bool pageflags_set_clear(vaddr start, vaddr last, int set_flags, int clear_flags) { PageFlagsNode *p; - target_ulong p_start, p_last; + vaddr p_start, p_last; int p_flags, merge_flags; bool inval_tb = false; @@ -492,7 +490,7 @@ static bool pageflags_set_clear(target_ulong start, target_ulong last, return inval_tb; } -void page_set_flags(target_ulong start, target_ulong last, int flags) +void page_set_flags(vaddr start, vaddr last, int flags) { bool reset = false; bool inval_tb = false; @@ -532,9 +530,9 @@ void page_set_flags(target_ulong start, target_ulong last, int flags) } } -bool page_check_range(target_ulong start, target_ulong len, int flags) +bool page_check_range(vaddr start, vaddr len, int flags) { - target_ulong last; + vaddr last; int locked; /* tri-state: =0: unlocked, +1: global, -1: local */ bool ret; @@ -610,17 +608,16 @@ bool page_check_range(target_ulong start, target_ulong len, int flags) return ret; } -bool page_check_range_empty(target_ulong start, target_ulong last) +bool page_check_range_empty(vaddr start, vaddr last) { assert(last >= start); assert_memory_lock(); return pageflags_find(start, last) == NULL; } -target_ulong page_find_range_empty(target_ulong min, target_ulong max, - target_ulong len, target_ulong align) +vaddr page_find_range_empty(vaddr min, vaddr max, vaddr len, vaddr align) { - target_ulong len_m1, align_m1; + vaddr len_m1, align_m1; assert(min <= max); assert(max <= GUEST_ADDR_MAX); @@ -661,7 +658,7 @@ target_ulong page_find_range_empty(target_ulong min, target_ulong max, void tb_lock_page0(tb_page_addr_t address) { PageFlagsNode *p; - target_ulong start, last; + vaddr start, last; int host_page_size = qemu_real_host_page_size(); int prot; @@ -740,7 +737,7 @@ int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc) } } else { int host_page_size = qemu_real_host_page_size(); - target_ulong start, len, i; + vaddr start, len, i; int prot; if (host_page_size <= TARGET_PAGE_SIZE) { @@ -756,7 +753,7 @@ int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc) prot = 0; for (i = 0; i < len; i += TARGET_PAGE_SIZE) { - target_ulong addr = start + i; + vaddr addr = start + i; p = pageflags_find(addr, addr); if (p) { @@ -883,7 +880,7 @@ typedef struct TargetPageDataNode { static IntervalTreeRoot targetdata_root; -void page_reset_target_data(target_ulong start, target_ulong last) +void page_reset_target_data(vaddr start, vaddr last) { IntervalTreeNode *n, *next; @@ -897,7 +894,7 @@ void page_reset_target_data(target_ulong start, target_ulong last) n != NULL; n = next, next = next ? interval_tree_iter_next(n, start, last) : NULL) { - target_ulong n_start, n_last, p_ofs, p_len; + vaddr n_start, n_last, p_ofs, p_len; TargetPageDataNode *t = container_of(n, TargetPageDataNode, itree); if (n->start >= start && n->last <= last) { @@ -921,11 +918,11 @@ void page_reset_target_data(target_ulong start, target_ulong last) } } -void *page_get_target_data(target_ulong address) +void *page_get_target_data(vaddr address) { IntervalTreeNode *n; TargetPageDataNode *t; - target_ulong page, region, p_ofs; + vaddr page, region, p_ofs; page = address & TARGET_PAGE_MASK; region = address & TBD_MASK; @@ -956,7 +953,7 @@ void *page_get_target_data(target_ulong address) return t->data + p_ofs * TARGET_PAGE_DATA_SIZE; } #else -void page_reset_target_data(target_ulong start, target_ulong last) { } +void page_reset_target_data(vaddr start, vaddr last) { } #endif /* TARGET_PAGE_DATA_SIZE */ /* The system-mode versions of these helpers are in cputlb.c. */ diff --git a/include/user/page-protection.h b/include/user/page-protection.h index 8f0b769b13..86143212fd 100644 --- a/include/user/page-protection.h +++ b/include/user/page-protection.h @@ -12,14 +12,12 @@ #error Cannot include this header from system emulation #endif -#include "cpu-param.h" -#include "exec/target_long.h" #include "exec/vaddr.h" #include "exec/translation-block.h" int page_unprotect(CPUState *cpu, tb_page_addr_t address, uintptr_t pc); -int page_get_flags(target_ulong address); +int page_get_flags(vaddr address); /** * page_set_flags: @@ -32,9 +30,9 @@ int page_get_flags(target_ulong address); * The flag PAGE_WRITE_ORG is positioned automatically depending * on PAGE_WRITE. The mmap_lock should already be held. */ -void page_set_flags(target_ulong start, target_ulong last, int flags); +void page_set_flags(vaddr start, vaddr last, int flags); -void page_reset_target_data(target_ulong start, target_ulong last); +void page_reset_target_data(vaddr start, vaddr last); /** * page_check_range @@ -46,7 +44,7 @@ void page_reset_target_data(target_ulong start, target_ulong last); * Return false if any page is unmapped. Thus testing flags == 0 is * equivalent to testing for flags == PAGE_VALID. */ -bool page_check_range(target_ulong start, target_ulong last, int flags); +bool page_check_range(vaddr start, vaddr last, int flags); /** * page_check_range_empty: @@ -58,7 +56,7 @@ bool page_check_range(target_ulong start, target_ulong last, int flags); * The memory lock must be held so that the caller will can ensure * the result stays true until a new mapping can be installed. */ -bool page_check_range_empty(target_ulong start, target_ulong last); +bool page_check_range_empty(vaddr start, vaddr last); /** * page_find_range_empty @@ -72,8 +70,7 @@ bool page_check_range_empty(target_ulong start, target_ulong last); * The memory lock must be held, as the caller will want to ensure * the returned range stays empty until a new mapping can be installed. */ -target_ulong page_find_range_empty(target_ulong min, target_ulong max, - target_ulong len, target_ulong align); +vaddr page_find_range_empty(vaddr min, vaddr max, vaddr len, vaddr align); /** * page_get_target_data(address) @@ -87,7 +84,7 @@ target_ulong page_find_range_empty(target_ulong min, target_ulong max, * e.g. with the munmap system call. */ __attribute__((returns_nonnull)) -void *page_get_target_data(target_ulong address); +void *page_get_target_data(vaddr address); typedef int (*walk_memory_regions_fn)(void *, vaddr, vaddr, int); int walk_memory_regions(void *, walk_memory_regions_fn); From 5f2446eb82bcb89c0969feffdd88c4eea05edfcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 Apr 2025 22:24:03 +0200 Subject: [PATCH 0542/2760] include/exec: Include missing headers in exec-all.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "exec/exec-all.h" declares prototypes such: void *probe_access(CPUArchState *env, vaddr addr, int size, ^^^^^ MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); MemoryRegionSection *iotlb_to_section(CPUState *cpu, hwaddr index, ^^^^^^ MemTxAttrs attrs); ^^^^^^^^^^ vaddr is defined in "exec/vaddr.h", hwaddr in "exec/hwaddr.h" and MemTxAttrs in "exec/memattrs.h". All these headers are indirectly pulled in via "exec/translation-block.h". Since we will remove "exec/translation-block.h" in the next commit, include the missing ones, otherwise we'd get errors such: include/exec/exec-all.h:51:1: error: unknown type name 'hwaddr' 51 | hwaddr memory_region_section_get_iotlb(CPUState *cpu, | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Signed-off-by: Richard Henderson Message-ID: <20250424202412.91612-5-philmd@linaro.org> --- include/exec/exec-all.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 24383b6aba..c46255e66e 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -20,8 +20,11 @@ #ifndef EXEC_ALL_H #define EXEC_ALL_H +#include "exec/hwaddr.h" +#include "exec/memattrs.h" #include "exec/mmu-access-type.h" #include "exec/translation-block.h" +#include "exec/vaddr.h" #if defined(CONFIG_TCG) #include "accel/tcg/getpc.h" From 0b87b740c27cabbd4f8447631d026c28ca83f7db Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 24 Apr 2025 22:24:04 +0200 Subject: [PATCH 0543/2760] include/exec: Move tb_invalidate_phys_range to translation-block.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Anton Johansson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/exec/exec-all.h | 5 ----- include/exec/translation-block.h | 4 ++++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index c46255e66e..4c5ad98c6a 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -23,7 +23,6 @@ #include "exec/hwaddr.h" #include "exec/memattrs.h" #include "exec/mmu-access-type.h" -#include "exec/translation-block.h" #include "exec/vaddr.h" #if defined(CONFIG_TCG) @@ -123,10 +122,6 @@ int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, #endif /* !CONFIG_USER_ONLY */ #endif /* CONFIG_TCG */ -/* TranslationBlock invalidate API */ -void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start, - tb_page_addr_t last); - #if !defined(CONFIG_USER_ONLY) /** diff --git a/include/exec/translation-block.h b/include/exec/translation-block.h index 8b8e730561..cdce399eba 100644 --- a/include/exec/translation-block.h +++ b/include/exec/translation-block.h @@ -207,4 +207,8 @@ static inline void tb_set_page_addr1(TranslationBlock *tb, #endif } +/* TranslationBlock invalidate API */ +void tb_invalidate_phys_range(CPUState *cpu, tb_page_addr_t start, + tb_page_addr_t last); + #endif /* EXEC_TRANSLATION_BLOCK_H */ From 7795eded0477f21c8518176492c4e19d103dde2c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 24 Apr 2025 22:24:05 +0200 Subject: [PATCH 0544/2760] accel/tcg: Compile tb-maint.c twice MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Anton Johansson Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 2 +- accel/tcg/tb-hash.h | 3 +-- accel/tcg/tb-maint.c | 2 -- 3 files changed, 2 insertions(+), 5 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 047afa49a2..3f7b127130 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -8,6 +8,7 @@ tcg_ss.add(files( 'cpu-exec-common.c', 'tcg-runtime.c', 'tcg-runtime-gvec.c', + 'tb-maint.c', 'translator.c', )) if get_option('plugins') @@ -21,7 +22,6 @@ tcg_specific_ss = ss.source_set() tcg_specific_ss.add(files( 'tcg-all.c', 'cpu-exec.c', - 'tb-maint.c', 'translate-all.c', )) tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) diff --git a/accel/tcg/tb-hash.h b/accel/tcg/tb-hash.h index 3bc5042d9d..f7b159f04c 100644 --- a/accel/tcg/tb-hash.h +++ b/accel/tcg/tb-hash.h @@ -20,8 +20,7 @@ #ifndef EXEC_TB_HASH_H #define EXEC_TB_HASH_H -#include "exec/cpu-defs.h" -#include "exec/exec-all.h" +#include "exec/vaddr.h" #include "exec/target_page.h" #include "exec/translation-block.h" #include "qemu/xxhash.h" diff --git a/accel/tcg/tb-maint.c b/accel/tcg/tb-maint.c index b144fcd4a0..0048316f99 100644 --- a/accel/tcg/tb-maint.c +++ b/accel/tcg/tb-maint.c @@ -20,10 +20,8 @@ #include "qemu/osdep.h" #include "qemu/interval-tree.h" #include "qemu/qtree.h" -#include "cpu.h" #include "exec/cputlb.h" #include "exec/log.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/mmap-lock.h" #include "exec/tb-flush.h" From 3ea423c27f4eabee5068fce27412761fa3db8b0f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 26 Apr 2025 19:35:00 +0000 Subject: [PATCH 0545/2760] accel/tcg: Remove #error for non-tcg in getpc.h Signed-off-by: Richard Henderson --- include/accel/tcg/getpc.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/include/accel/tcg/getpc.h b/include/accel/tcg/getpc.h index 8a97ce34e7..0fc08addcf 100644 --- a/include/accel/tcg/getpc.h +++ b/include/accel/tcg/getpc.h @@ -8,10 +8,6 @@ #ifndef ACCEL_TCG_GETPC_H #define ACCEL_TCG_GETPC_H -#ifndef CONFIG_TCG -#error Can only include this header with TCG -#endif - /* GETPC is the true target of the return instruction that we'll execute. */ #ifdef CONFIG_TCG_INTERPRETER extern __thread uintptr_t tci_tb_ptr; From 0f81774dd1c19de5dedb3c8f2d74e5b9a73d8c12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 Apr 2025 22:24:06 +0200 Subject: [PATCH 0546/2760] target/riscv: Include missing 'accel/tcg/getpc.h' in csr.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "accel/tcg/getpc.h" is pulled in indirectly. Include it explicitly to avoid when refactoring unrelated headers: target/riscv/csr.c:2117:25: error: call to undeclared function 'GETPC' [-Wimplicit-function-declaration] 2117 | if ((val & RVC) && (GETPC() & ~3) != 0) { | ^ Note the TODO comment around GETPC() added upon introduction in commit f18637cd611 ("RISC-V: Add misa runtime write support"): 2099 static RISCVException write_misa(CPURISCVState *env, int csrno, 2100 target_ulong val) 2101 { ... 2113 /* 2114 * Suppress 'C' if next instruction is not aligned 2115 * TODO: this should check next_pc 2116 */ 2117 if ((val & RVC) && (GETPC() & ~3) != 0) { 2118 val &= ~RVC; 2119 } Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Acked-by: Alistair Francis Signed-off-by: Richard Henderson Message-ID: <20250424202412.91612-8-philmd@linaro.org> --- target/riscv/csr.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index c52c87faae..1308643855 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -28,6 +28,7 @@ #include "exec/cputlb.h" #include "exec/tb-flush.h" #include "exec/icount.h" +#include "accel/tcg/getpc.h" #include "qemu/guest-random.h" #include "qapi/error.h" #include From 98db62318ae98be9a57b3c01f7f97a984ac1b79b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 Apr 2025 22:24:08 +0200 Subject: [PATCH 0547/2760] accel/tcg: Include 'accel/tcg/getpc.h' in 'exec/helper-proto' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Most files including "exec/helper-proto.h" call GETPC(). Include it there (in the common part) instead of the unspecific "exec/exec-all.h" header. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20250424202412.91612-10-philmd@linaro.org> --- accel/tcg/translate-all.c | 1 + include/exec/exec-all.h | 1 - include/exec/helper-proto-common.h | 2 ++ target/avr/helper.c | 1 - 4 files changed, 3 insertions(+), 2 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 38819a507b..0408e2522a 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -61,6 +61,7 @@ #include "system/tcg.h" #include "qapi/error.h" #include "accel/tcg/cpu-ops.h" +#include "accel/tcg/getpc.h" #include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 4c5ad98c6a..816274bf90 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -26,7 +26,6 @@ #include "exec/vaddr.h" #if defined(CONFIG_TCG) -#include "accel/tcg/getpc.h" /** * probe_access: diff --git a/include/exec/helper-proto-common.h b/include/exec/helper-proto-common.h index 16782ef46c..76e6c25bec 100644 --- a/include/exec/helper-proto-common.h +++ b/include/exec/helper-proto-common.h @@ -13,4 +13,6 @@ #include "exec/helper-proto.h.inc" #undef HELPER_H +#include "accel/tcg/getpc.h" + #endif /* HELPER_PROTO_COMMON_H */ diff --git a/target/avr/helper.c b/target/avr/helper.c index afa591470f..b9cd6d5ef2 100644 --- a/target/avr/helper.c +++ b/target/avr/helper.c @@ -23,7 +23,6 @@ #include "qemu/error-report.h" #include "cpu.h" #include "accel/tcg/cpu-ops.h" -#include "accel/tcg/getpc.h" #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/target_page.h" From 1381ea53a84234a0aac212baeae922137ad4bbda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 Apr 2025 22:24:09 +0200 Subject: [PATCH 0548/2760] physmem: Move TCG IOTLB methods around MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The next commit will restrict TCG specific code in physmem.c using some #ifdef'ry. In order to keep it simple, move iotlb_to_section() and memory_region_section_get_iotlb() around close together. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Mark Cave-Ayland Signed-off-by: Richard Henderson Message-ID: <20250424202412.91612-11-philmd@linaro.org> --- system/physmem.c | 50 ++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index 637f2d8532..ccbeae241c 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -746,6 +746,31 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr orig_addr, return &d->map.sections[PHYS_SECTION_UNASSIGNED]; } +MemoryRegionSection *iotlb_to_section(CPUState *cpu, + hwaddr index, MemTxAttrs attrs) +{ + int asidx = cpu_asidx_from_attrs(cpu, attrs); + CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx]; + AddressSpaceDispatch *d = cpuas->memory_dispatch; + int section_index = index & ~TARGET_PAGE_MASK; + MemoryRegionSection *ret; + + assert(section_index < d->map.sections_nb); + ret = d->map.sections + section_index; + assert(ret->mr); + assert(ret->mr->ops); + + return ret; +} + +/* Called from RCU critical section */ +hwaddr memory_region_section_get_iotlb(CPUState *cpu, + MemoryRegionSection *section) +{ + AddressSpaceDispatch *d = flatview_to_dispatch(section->fv); + return section - d->map.sections; +} + void cpu_address_space_init(CPUState *cpu, int asidx, const char *prefix, MemoryRegion *mr) { @@ -1002,14 +1027,6 @@ bool cpu_physical_memory_snapshot_get_dirty(DirtyBitmapSnapshot *snap, return false; } -/* Called from RCU critical section */ -hwaddr memory_region_section_get_iotlb(CPUState *cpu, - MemoryRegionSection *section) -{ - AddressSpaceDispatch *d = flatview_to_dispatch(section->fv); - return section - d->map.sections; -} - static int subpage_register(subpage_t *mmio, uint32_t start, uint32_t end, uint16_t section); static subpage_t *subpage_init(FlatView *fv, hwaddr base); @@ -2669,23 +2686,6 @@ static uint16_t dummy_section(PhysPageMap *map, FlatView *fv, MemoryRegion *mr) return phys_section_add(map, §ion); } -MemoryRegionSection *iotlb_to_section(CPUState *cpu, - hwaddr index, MemTxAttrs attrs) -{ - int asidx = cpu_asidx_from_attrs(cpu, attrs); - CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx]; - AddressSpaceDispatch *d = cpuas->memory_dispatch; - int section_index = index & ~TARGET_PAGE_MASK; - MemoryRegionSection *ret; - - assert(section_index < d->map.sections_nb); - ret = d->map.sections + section_index; - assert(ret->mr); - assert(ret->mr->ops); - - return ret; -} - static void io_mem_init(void) { memory_region_init_io(&io_mem_unassigned, NULL, &unassigned_mem_ops, NULL, From f12b717717c45627d667b609326fda54f0ad0394 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 Apr 2025 22:24:10 +0200 Subject: [PATCH 0549/2760] physmem: Restrict TCG IOTLB code to TCG accel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restrict iotlb_to_section(), address_space_translate_for_iotlb() and memory_region_section_get_iotlb() to TCG. Declare them in the new "accel/tcg/iommu.h" header. Declare iotlb_to_section() using the MemoryRegionSection typedef. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20250424202412.91612-12-philmd@linaro.org> --- MAINTAINERS | 2 +- accel/tcg/cputlb.c | 1 + include/accel/tcg/iommu.h | 41 +++++++++++++++++++++++++++++++++++++++ include/exec/exec-all.h | 26 ------------------------- system/physmem.c | 5 +++++ 5 files changed, 48 insertions(+), 27 deletions(-) create mode 100644 include/accel/tcg/iommu.h diff --git a/MAINTAINERS b/MAINTAINERS index b3f9f2680b..f3f491c8c2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -168,7 +168,7 @@ F: include/exec/helper*.h.inc F: include/exec/helper-info.c.inc F: include/exec/page-protection.h F: include/system/tcg.h -F: include/accel/tcg/cpu-ops.h +F: include/accel/tcg/ F: host/include/*/host/cpuinfo.h F: util/cpuinfo-*.c F: include/tcg/ diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index ca69128232..d11989f567 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "accel/tcg/cpu-ops.h" +#include "accel/tcg/iommu.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "system/memory.h" diff --git a/include/accel/tcg/iommu.h b/include/accel/tcg/iommu.h new file mode 100644 index 0000000000..90cfd6c0ed --- /dev/null +++ b/include/accel/tcg/iommu.h @@ -0,0 +1,41 @@ +/* + * TCG IOMMU translations. + * + * Copyright (c) 2003 Fabrice Bellard + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#ifndef ACCEL_TCG_IOMMU_H +#define ACCEL_TCG_IOMMU_H + +#ifdef CONFIG_USER_ONLY +#error Cannot include accel/tcg/iommu.h from user emulation +#endif + +#include "exec/hwaddr.h" +#include "exec/memattrs.h" + +/** + * iotlb_to_section: + * @cpu: CPU performing the access + * @index: TCG CPU IOTLB entry + * + * Given a TCG CPU IOTLB entry, return the MemoryRegionSection that + * it refers to. @index will have been initially created and returned + * by memory_region_section_get_iotlb(). + */ +MemoryRegionSection *iotlb_to_section(CPUState *cpu, + hwaddr index, MemTxAttrs attrs); + +MemoryRegionSection *address_space_translate_for_iotlb(CPUState *cpu, + int asidx, + hwaddr addr, + hwaddr *xlat, + hwaddr *plen, + MemTxAttrs attrs, + int *prot); + +hwaddr memory_region_section_get_iotlb(CPUState *cpu, + MemoryRegionSection *section); + +#endif + diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index 816274bf90..b9eb9bc4b6 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -21,7 +21,6 @@ #define EXEC_ALL_H #include "exec/hwaddr.h" -#include "exec/memattrs.h" #include "exec/mmu-access-type.h" #include "exec/vaddr.h" @@ -121,29 +120,4 @@ int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, #endif /* !CONFIG_USER_ONLY */ #endif /* CONFIG_TCG */ -#if !defined(CONFIG_USER_ONLY) - -/** - * iotlb_to_section: - * @cpu: CPU performing the access - * @index: TCG CPU IOTLB entry - * - * Given a TCG CPU IOTLB entry, return the MemoryRegionSection that - * it refers to. @index will have been initially created and returned - * by memory_region_section_get_iotlb(). - */ -struct MemoryRegionSection *iotlb_to_section(CPUState *cpu, - hwaddr index, MemTxAttrs attrs); -#endif - -#if !defined(CONFIG_USER_ONLY) - -MemoryRegionSection * -address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr addr, - hwaddr *xlat, hwaddr *plen, - MemTxAttrs attrs, int *prot); -hwaddr memory_region_section_get_iotlb(CPUState *cpu, - MemoryRegionSection *section); -#endif - #endif diff --git a/system/physmem.c b/system/physmem.c index ccbeae241c..f1ec0902c7 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -29,6 +29,7 @@ #ifdef CONFIG_TCG #include "accel/tcg/cpu-ops.h" +#include "accel/tcg/iommu.h" #endif /* CONFIG_TCG */ #include "exec/exec-all.h" @@ -587,6 +588,8 @@ MemoryRegion *flatview_translate(FlatView *fv, hwaddr addr, hwaddr *xlat, return mr; } +#ifdef CONFIG_TCG + typedef struct TCGIOMMUNotifier { IOMMUNotifier n; MemoryRegion *mr; @@ -771,6 +774,8 @@ hwaddr memory_region_section_get_iotlb(CPUState *cpu, return section - d->map.sections; } +#endif /* CONFIG_TCG */ + void cpu_address_space_init(CPUState *cpu, int asidx, const char *prefix, MemoryRegion *mr) { From fe1a3ace13a8b53fc20c74fb7e3337f754396e6b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 Apr 2025 22:24:11 +0200 Subject: [PATCH 0550/2760] accel/tcg: Extract probe API out of 'exec/exec-all.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declare probe methods in "accel/tcg/probe.h" to emphasize they are specific to TCG accelerator. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Signed-off-by: Richard Henderson Message-ID: <20250424202412.91612-13-philmd@linaro.org> --- accel/tcg/cputlb.c | 1 + accel/tcg/user-exec.c | 1 + include/accel/tcg/probe.h | 106 +++++++++++++++++++++++++++ include/exec/exec-all.h | 100 ------------------------- semihosting/uaccess.c | 1 + target/arm/helper.c | 1 + target/arm/ptw.c | 1 + target/arm/tcg/helper-a64.c | 1 + target/arm/tcg/mte_helper.c | 1 + target/arm/tcg/op_helper.c | 1 + target/arm/tcg/sve_helper.c | 1 + target/hexagon/mmvec/macros.h | 1 + target/hexagon/op_helper.c | 1 + target/hppa/mem_helper.c | 1 + target/hppa/op_helper.c | 1 + target/i386/tcg/access.c | 1 + target/i386/tcg/seg_helper.c | 1 + target/i386/tcg/system/excp_helper.c | 1 + target/mips/tcg/msa_helper.c | 1 + target/ppc/mem_helper.c | 1 + target/riscv/op_helper.c | 1 + target/riscv/vector_helper.c | 1 + target/s390x/tcg/mem_helper.c | 1 + target/xtensa/mmu_helper.c | 1 + 24 files changed, 128 insertions(+), 100 deletions(-) create mode 100644 include/accel/tcg/probe.h diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index d11989f567..b346af942a 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -21,6 +21,7 @@ #include "qemu/main-loop.h" #include "accel/tcg/cpu-ops.h" #include "accel/tcg/iommu.h" +#include "accel/tcg/probe.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "system/memory.h" diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 43d005e24e..697fdf1824 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -27,6 +27,7 @@ #include "qemu/bitops.h" #include "qemu/rcu.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "user/cpu_loop.h" #include "qemu/main-loop.h" #include "user/page-protection.h" diff --git a/include/accel/tcg/probe.h b/include/accel/tcg/probe.h new file mode 100644 index 0000000000..177bd1608d --- /dev/null +++ b/include/accel/tcg/probe.h @@ -0,0 +1,106 @@ +/* + * Probe guest virtual addresses for access permissions. + * + * Copyright (c) 2003 Fabrice Bellard + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#ifndef ACCEL_TCG_PROBE_H +#define ACCEL_TCG_PROBE_H + +#include "exec/mmu-access-type.h" +#include "exec/vaddr.h" + +/** + * probe_access: + * @env: CPUArchState + * @addr: guest virtual address to look up + * @size: size of the access + * @access_type: read, write or execute permission + * @mmu_idx: MMU index to use for lookup + * @retaddr: return address for unwinding + * + * Look up the guest virtual address @addr. Raise an exception if the + * page does not satisfy @access_type. Raise an exception if the + * access (@addr, @size) hits a watchpoint. For writes, mark a clean + * page as dirty. + * + * Finally, return the host address for a page that is backed by RAM, + * or NULL if the page requires I/O. + */ +void *probe_access(CPUArchState *env, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); + +static inline void *probe_write(CPUArchState *env, vaddr addr, int size, + int mmu_idx, uintptr_t retaddr) +{ + return probe_access(env, addr, size, MMU_DATA_STORE, mmu_idx, retaddr); +} + +static inline void *probe_read(CPUArchState *env, vaddr addr, int size, + int mmu_idx, uintptr_t retaddr) +{ + return probe_access(env, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr); +} + +/** + * probe_access_flags: + * @env: CPUArchState + * @addr: guest virtual address to look up + * @size: size of the access + * @access_type: read, write or execute permission + * @mmu_idx: MMU index to use for lookup + * @nonfault: suppress the fault + * @phost: return value for host address + * @retaddr: return address for unwinding + * + * Similar to probe_access, loosely returning the TLB_FLAGS_MASK for + * the page, and storing the host address for RAM in @phost. + * + * If @nonfault is set, do not raise an exception but return TLB_INVALID_MASK. + * Do not handle watchpoints, but include TLB_WATCHPOINT in the returned flags. + * Do handle clean pages, so exclude TLB_NOTDIRY from the returned flags. + * For simplicity, all "mmio-like" flags are folded to TLB_MMIO. + */ +int probe_access_flags(CPUArchState *env, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + bool nonfault, void **phost, uintptr_t retaddr); + +#ifndef CONFIG_USER_ONLY + +/** + * probe_access_full: + * Like probe_access_flags, except also return into @pfull. + * + * The CPUTLBEntryFull structure returned via @pfull is transient + * and must be consumed or copied immediately, before any further + * access or changes to TLB @mmu_idx. + * + * This function will not fault if @nonfault is set, but will + * return TLB_INVALID_MASK if the page is not mapped, or is not + * accessible with @access_type. + * + * This function will return TLB_MMIO in order to force the access + * to be handled out-of-line if plugins wish to instrument the access. + */ +int probe_access_full(CPUArchState *env, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + bool nonfault, void **phost, + CPUTLBEntryFull **pfull, uintptr_t retaddr); + +/** + * probe_access_full_mmu: + * Like probe_access_full, except: + * + * This function is intended to be used for page table accesses by + * the target mmu itself. Since such page walking happens while + * handling another potential mmu fault, this function never raises + * exceptions (akin to @nonfault true for probe_access_full). + * Likewise this function does not trigger plugin instrumentation. + */ +int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, + MMUAccessType access_type, int mmu_idx, + void **phost, CPUTLBEntryFull **pfull); + +#endif /* !CONFIG_USER_ONLY */ + +#endif diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h index b9eb9bc4b6..9ef7569a0b 100644 --- a/include/exec/exec-all.h +++ b/include/exec/exec-all.h @@ -20,104 +20,4 @@ #ifndef EXEC_ALL_H #define EXEC_ALL_H -#include "exec/hwaddr.h" -#include "exec/mmu-access-type.h" -#include "exec/vaddr.h" - -#if defined(CONFIG_TCG) - -/** - * probe_access: - * @env: CPUArchState - * @addr: guest virtual address to look up - * @size: size of the access - * @access_type: read, write or execute permission - * @mmu_idx: MMU index to use for lookup - * @retaddr: return address for unwinding - * - * Look up the guest virtual address @addr. Raise an exception if the - * page does not satisfy @access_type. Raise an exception if the - * access (@addr, @size) hits a watchpoint. For writes, mark a clean - * page as dirty. - * - * Finally, return the host address for a page that is backed by RAM, - * or NULL if the page requires I/O. - */ -void *probe_access(CPUArchState *env, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, uintptr_t retaddr); - -static inline void *probe_write(CPUArchState *env, vaddr addr, int size, - int mmu_idx, uintptr_t retaddr) -{ - return probe_access(env, addr, size, MMU_DATA_STORE, mmu_idx, retaddr); -} - -static inline void *probe_read(CPUArchState *env, vaddr addr, int size, - int mmu_idx, uintptr_t retaddr) -{ - return probe_access(env, addr, size, MMU_DATA_LOAD, mmu_idx, retaddr); -} - -/** - * probe_access_flags: - * @env: CPUArchState - * @addr: guest virtual address to look up - * @size: size of the access - * @access_type: read, write or execute permission - * @mmu_idx: MMU index to use for lookup - * @nonfault: suppress the fault - * @phost: return value for host address - * @retaddr: return address for unwinding - * - * Similar to probe_access, loosely returning the TLB_FLAGS_MASK for - * the page, and storing the host address for RAM in @phost. - * - * If @nonfault is set, do not raise an exception but return TLB_INVALID_MASK. - * Do not handle watchpoints, but include TLB_WATCHPOINT in the returned flags. - * Do handle clean pages, so exclude TLB_NOTDIRY from the returned flags. - * For simplicity, all "mmio-like" flags are folded to TLB_MMIO. - */ -int probe_access_flags(CPUArchState *env, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, - bool nonfault, void **phost, uintptr_t retaddr); - -#ifndef CONFIG_USER_ONLY - -/** - * probe_access_full: - * Like probe_access_flags, except also return into @pfull. - * - * The CPUTLBEntryFull structure returned via @pfull is transient - * and must be consumed or copied immediately, before any further - * access or changes to TLB @mmu_idx. - * - * This function will not fault if @nonfault is set, but will - * return TLB_INVALID_MASK if the page is not mapped, or is not - * accessible with @access_type. - * - * This function will return TLB_MMIO in order to force the access - * to be handled out-of-line if plugins wish to instrument the access. - */ -int probe_access_full(CPUArchState *env, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, - bool nonfault, void **phost, - CPUTLBEntryFull **pfull, uintptr_t retaddr); - -/** - * probe_access_full_mmu: - * Like probe_access_full, except: - * - * This function is intended to be used for page table accesses by - * the target mmu itself. Since such page walking happens while - * handling another potential mmu fault, this function never raises - * exceptions (akin to @nonfault true for probe_access_full). - * Likewise this function does not trigger plugin instrumentation. - */ -int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, - MMUAccessType access_type, int mmu_idx, - void **phost, CPUTLBEntryFull **pfull); - -#endif /* !CONFIG_USER_ONLY */ -#endif /* CONFIG_TCG */ - #endif diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index 81ffecaaba..ebbb300f86 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -9,6 +9,7 @@ #include "qemu/osdep.h" #include "accel/tcg/cpu-mmu-index.h" +#include "accel/tcg/probe.h" #include "exec/exec-all.h" #include "exec/target_page.h" #include "exec/tlb-flags.h" diff --git a/target/arm/helper.c b/target/arm/helper.c index c6fd290012..2f039b2db3 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -30,6 +30,7 @@ #include "qapi/error.h" #include "qemu/guest-random.h" #ifdef CONFIG_TCG +#include "accel/tcg/probe.h" #include "semihosting/common-semi.h" #endif #include "cpregs.h" diff --git a/target/arm/ptw.c b/target/arm/ptw.c index e0e82ae507..87d707b592 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -14,6 +14,7 @@ #include "exec/page-protection.h" #include "exec/target_page.h" #include "exec/tlb-flags.h" +#include "accel/tcg/probe.h" #include "cpu.h" #include "internals.h" #include "cpu-features.h" diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 842d9e6000..cfe5faba19 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -31,6 +31,7 @@ #include "exec/cpu-common.h" #include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/target_page.h" #include "exec/tlb-flags.h" #include "qemu/int128.h" diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 7dc5fb776b..8fbdcc8fb9 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -30,6 +30,7 @@ #include "system/ram_addr.h" #endif #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/helper-proto.h" #include "exec/tlb-flags.h" #include "accel/tcg/cpu-ops.h" diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index 38d49cbb9d..d50b8720ad 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -25,6 +25,7 @@ #include "cpu-features.h" #include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "cpregs.h" #define SIGNBIT (uint32_t)0x80000000 diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 87b6b4b3e6..50aca54eaa 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -32,6 +32,7 @@ #include "sve_ldst_internal.h" #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/cpu-ops.h" +#include "accel/tcg/probe.h" #ifdef CONFIG_USER_ONLY #include "user/page-protection.h" #endif diff --git a/target/hexagon/mmvec/macros.h b/target/hexagon/mmvec/macros.h index c1a88392c0..c7840fbf2e 100644 --- a/target/hexagon/mmvec/macros.h +++ b/target/hexagon/mmvec/macros.h @@ -22,6 +22,7 @@ #include "arch.h" #include "mmvec/system_ext_mmvec.h" #include "accel/tcg/getpc.h" +#include "accel/tcg/probe.h" #ifndef QEMU_GENERATE #define VdV (*(MMVector *restrict)(VdV_void)) diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index 3f3d86db2b..dd726b4318 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -19,6 +19,7 @@ #include "qemu/log.h" #include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" #include "cpu.h" diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index 554d7bf4d1..a5f73aedf8 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -23,6 +23,7 @@ #include "exec/exec-all.h" #include "exec/cputlb.h" #include "accel/tcg/cpu-mmu-index.h" +#include "accel/tcg/probe.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "exec/helper-proto.h" diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 2398ce2c64..32207c1a4c 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -23,6 +23,7 @@ #include "exec/exec-all.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "qemu/timer.h" #include "trace.h" #ifdef CONFIG_USER_ONLY diff --git a/target/i386/tcg/access.c b/target/i386/tcg/access.c index 0fdd587edd..ee5b451459 100644 --- a/target/i386/tcg/access.c +++ b/target/i386/tcg/access.c @@ -4,6 +4,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/exec-all.h" #include "exec/target_page.h" #include "access.h" diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index 3af902e0ec..e45d71fa1a 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -24,6 +24,7 @@ #include "exec/helper-proto.h" #include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/log.h" #include "helper-tcg.h" #include "seg_helper.h" diff --git a/target/i386/tcg/system/excp_helper.c b/target/i386/tcg/system/excp_helper.c index 93614aa3e5..c162621587 100644 --- a/target/i386/tcg/system/excp_helper.c +++ b/target/i386/tcg/system/excp_helper.c @@ -20,6 +20,7 @@ #include "qemu/osdep.h" #include "cpu.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/target_page.h" diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index e349344647..fde34a39e1 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -23,6 +23,7 @@ #include "tcg/tcg.h" #include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/helper-proto.h" #include "exec/memop.h" #include "exec/target_page.h" diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index d7e8d678f4..50f05a915e 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -25,6 +25,7 @@ #include "exec/helper-proto.h" #include "helper_regs.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "internal.h" #include "qemu/atomic128.h" diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 5b0db2c45a..abb1d284dc 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -24,6 +24,7 @@ #include "exec/exec-all.h" #include "exec/cputlb.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/helper-proto.h" #include "exec/tlb-flags.h" #include "trace.h" diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index b8ae704457..5ccb294e31 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -23,6 +23,7 @@ #include "exec/memop.h" #include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/page-protection.h" #include "exec/helper-proto.h" #include "exec/tlb-flags.h" diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 0cdfd380ce..9e77cde81b 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -29,6 +29,7 @@ #include "exec/cputlb.h" #include "exec/page-protection.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/probe.h" #include "exec/target_page.h" #include "exec/tlb-flags.h" #include "accel/tcg/cpu-ops.h" diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c index a7dd810055..182c6e35c1 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -34,6 +34,7 @@ #include "qemu/host-utils.h" #include "exec/cputlb.h" #include "accel/tcg/cpu-mmu-index.h" +#include "accel/tcg/probe.h" #include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" From 84307cd6027c4602913177ff09aeefa4743b7234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 24 Apr 2025 22:24:12 +0200 Subject: [PATCH 0551/2760] include: Remove 'exec/exec-all.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "exec/exec-all.h" is now fully empty, let's remove it. Mechanical change running: $ sed -i '/exec\/exec-all.h/d' $(git grep -wl exec/exec-all.h) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Signed-off-by: Richard Henderson Message-ID: <20250424202412.91612-14-philmd@linaro.org> --- MAINTAINERS | 1 - accel/hvf/hvf-accel-ops.c | 1 - accel/tcg/cputlb.c | 1 - accel/tcg/translate-all.c | 1 - accel/tcg/user-exec.c | 1 - bsd-user/main.c | 1 - bsd-user/qemu.h | 1 - hw/ppc/spapr_nested.c | 1 - hw/riscv/riscv-iommu-sys.c | 1 - hw/sh4/sh7750.c | 1 - include/exec/exec-all.h | 23 ----------------------- include/system/ram_addr.h | 1 - linux-user/main.c | 1 - linux-user/user-internals.h | 1 - semihosting/uaccess.c | 1 - system/physmem.c | 1 - target/alpha/cpu.c | 1 - target/alpha/fpu_helper.c | 1 - target/alpha/int_helper.c | 1 - target/alpha/mem_helper.c | 1 - target/alpha/translate.c | 1 - target/alpha/vax_helper.c | 1 - target/arm/cpu.c | 1 - target/arm/debug_helper.c | 1 - target/arm/helper.c | 1 - target/arm/ptw.c | 1 - target/arm/tcg/helper-a64.c | 1 - target/arm/tcg/m_helper.c | 1 - target/arm/tcg/mte_helper.c | 1 - target/arm/tcg/mve_helper.c | 1 - target/arm/tcg/op_helper.c | 1 - target/arm/tcg/pauth_helper.c | 1 - target/arm/tcg/sme_helper.c | 1 - target/arm/tcg/sve_helper.c | 1 - target/arm/tcg/tlb_helper.c | 1 - target/arm/tcg/translate-a64.c | 1 - target/arm/tcg/translate.h | 1 - target/avr/cpu.c | 1 - target/avr/translate.c | 1 - target/hexagon/cpu.c | 1 - target/hexagon/op_helper.c | 1 - target/hppa/cpu.c | 1 - target/hppa/fpu_helper.c | 1 - target/hppa/helper.c | 1 - target/hppa/mem_helper.c | 1 - target/hppa/op_helper.c | 1 - target/hppa/sys_helper.c | 1 - target/hppa/translate.c | 1 - target/i386/tcg/access.c | 1 - target/i386/tcg/excp_helper.c | 1 - target/i386/tcg/helper-tcg.h | 1 - target/i386/tcg/int_helper.c | 1 - target/i386/tcg/mem_helper.c | 1 - target/i386/tcg/mpx_helper.c | 1 - target/i386/tcg/seg_helper.c | 1 - target/i386/tcg/system/bpt_helper.c | 1 - target/i386/tcg/translate.c | 1 - target/i386/tcg/user/excp_helper.c | 1 - target/i386/tcg/user/seg_helper.c | 1 - target/loongarch/cpu.c | 1 - target/loongarch/tcg/fpu_helper.c | 1 - target/loongarch/tcg/iocsr_helper.c | 1 - target/loongarch/tcg/op_helper.c | 1 - target/loongarch/tcg/tlb_helper.c | 1 - target/loongarch/tcg/vec_helper.c | 1 - target/m68k/fpu_helper.c | 1 - target/m68k/helper.c | 1 - target/m68k/op_helper.c | 1 - target/m68k/translate.c | 1 - target/microblaze/cpu.c | 1 - target/microblaze/op_helper.c | 1 - target/microblaze/translate.c | 1 - target/mips/cpu.c | 1 - target/mips/system/physaddr.c | 1 - target/mips/tcg/exception.c | 1 - target/mips/tcg/fpu_helper.c | 1 - target/mips/tcg/ldst_helper.c | 1 - target/mips/tcg/msa_helper.c | 1 - target/mips/tcg/op_helper.c | 1 - target/mips/tcg/system/special_helper.c | 1 - target/mips/tcg/system/tlb_helper.c | 1 - target/openrisc/cpu.c | 1 - target/openrisc/exception.c | 1 - target/openrisc/exception_helper.c | 1 - target/openrisc/fpu_helper.c | 1 - target/openrisc/interrupt.c | 1 - target/openrisc/interrupt_helper.c | 1 - target/openrisc/sys_helper.c | 1 - target/openrisc/translate.c | 1 - target/ppc/excp_helper.c | 1 - target/ppc/fpu_helper.c | 1 - target/ppc/machine.c | 1 - target/ppc/mem_helper.c | 1 - target/ppc/misc_helper.c | 1 - target/ppc/mmu-hash32.c | 1 - target/ppc/mmu-hash64.c | 1 - target/ppc/mmu-radix64.c | 1 - target/ppc/mmu_common.c | 1 - target/ppc/mmu_helper.c | 1 - target/ppc/power8-pmu.c | 1 - target/ppc/tcg-excp_helper.c | 1 - target/ppc/timebase_helper.c | 1 - target/ppc/translate.c | 1 - target/ppc/user_only_helper.c | 1 - target/riscv/cpu.c | 1 - target/riscv/cpu_helper.c | 1 - target/riscv/crypto_helper.c | 1 - target/riscv/csr.c | 1 - target/riscv/debug.c | 1 - target/riscv/fpu_helper.c | 1 - target/riscv/m128_helper.c | 1 - target/riscv/op_helper.c | 1 - target/riscv/tcg/tcg-cpu.c | 1 - target/riscv/translate.c | 1 - target/riscv/vcrypto_helper.c | 1 - target/riscv/vector_helper.c | 1 - target/riscv/zce_helper.c | 1 - target/rx/op_helper.c | 1 - target/rx/translate.c | 1 - target/s390x/interrupt.c | 1 - target/s390x/mmu_helper.c | 1 - target/s390x/sigp.c | 1 - target/s390x/tcg/cc_helper.c | 1 - target/s390x/tcg/crypto_helper.c | 1 - target/s390x/tcg/excp_helper.c | 1 - target/s390x/tcg/fpu_helper.c | 1 - target/s390x/tcg/int_helper.c | 1 - target/s390x/tcg/mem_helper.c | 1 - target/s390x/tcg/misc_helper.c | 1 - target/s390x/tcg/translate.c | 1 - target/s390x/tcg/vec_fpu_helper.c | 1 - target/s390x/tcg/vec_helper.c | 1 - target/sh4/cpu.c | 1 - target/sh4/helper.c | 1 - target/sh4/op_helper.c | 1 - target/sh4/translate.c | 1 - target/sparc/cpu.c | 1 - target/sparc/fop_helper.c | 1 - target/sparc/helper.c | 1 - target/sparc/ldst_helper.c | 1 - target/sparc/machine.c | 1 - target/sparc/translate.c | 1 - target/sparc/win_helper.c | 1 - target/tricore/cpu.c | 1 - target/tricore/op_helper.c | 1 - target/tricore/translate.c | 1 - target/xtensa/dbg_helper.c | 1 - target/xtensa/exc_helper.c | 1 - target/xtensa/fpu_helper.c | 1 - target/xtensa/mmu_helper.c | 1 - target/xtensa/op_helper.c | 1 - target/xtensa/translate.c | 1 - target/xtensa/win_helper.c | 1 - 153 files changed, 175 deletions(-) delete mode 100644 include/exec/exec-all.h diff --git a/MAINTAINERS b/MAINTAINERS index f3f491c8c2..3706601ea5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -493,7 +493,6 @@ M: Richard Henderson R: Paolo Bonzini S: Maintained F: include/exec/cpu*.h -F: include/exec/exec-all.h F: include/exec/target_long.h F: include/qemu/accel.h F: include/system/accel-*.h diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 5375de7bcf..b8b6116bc8 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -51,7 +51,6 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "system/address-spaces.h" -#include "exec/exec-all.h" #include "gdbstub/enums.h" #include "hw/boards.h" #include "system/accel-ops.h" diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index b346af942a..5b6d6f7975 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -22,7 +22,6 @@ #include "accel/tcg/cpu-ops.h" #include "accel/tcg/iommu.h" #include "accel/tcg/probe.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "system/memory.h" #include "accel/tcg/cpu-ldst.h" diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 0408e2522a..31c7f9927f 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -21,7 +21,6 @@ #include "trace.h" #include "disas/disas.h" -#include "exec/exec-all.h" #include "tcg/tcg.h" #if defined(CONFIG_USER_ONLY) #include "qemu.h" diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 697fdf1824..70feee8df9 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -21,7 +21,6 @@ #include "disas/disas.h" #include "cpu.h" #include "exec/vaddr.h" -#include "exec/exec-all.h" #include "exec/tlb-flags.h" #include "tcg/tcg.h" #include "qemu/bitops.h" diff --git a/bsd-user/main.c b/bsd-user/main.c index fdb160bed0..fa7645a56e 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -36,7 +36,6 @@ #include "qemu/help_option.h" #include "qemu/module.h" #include "qemu/plugin.h" -#include "exec/exec-all.h" #include "user/guest-base.h" #include "user/page-protection.h" #include "tcg/startup.h" diff --git a/bsd-user/qemu.h b/bsd-user/qemu.h index 244670dd24..93388e7c34 100644 --- a/bsd-user/qemu.h +++ b/bsd-user/qemu.h @@ -23,7 +23,6 @@ #include "cpu.h" #include "qemu/units.h" #include "accel/tcg/cpu-ldst.h" -#include "exec/exec-all.h" #include "user/abitypes.h" #include "user/cpu_loop.h" diff --git a/hw/ppc/spapr_nested.c b/hw/ppc/spapr_nested.c index 820f752b9e..10cf634da1 100644 --- a/hw/ppc/spapr_nested.c +++ b/hw/ppc/spapr_nested.c @@ -1,6 +1,5 @@ #include "qemu/osdep.h" #include "qemu/cutils.h" -#include "exec/exec-all.h" #include "exec/cputlb.h" #include "exec/target_long.h" #include "helper_regs.h" diff --git a/hw/riscv/riscv-iommu-sys.c b/hw/riscv/riscv-iommu-sys.c index be2e3944dc..74e76b94a5 100644 --- a/hw/riscv/riscv-iommu-sys.c +++ b/hw/riscv/riscv-iommu-sys.c @@ -26,7 +26,6 @@ #include "qemu/host-utils.h" #include "qemu/module.h" #include "qom/object.h" -#include "exec/exec-all.h" #include "trace.h" #include "riscv-iommu.h" diff --git a/hw/sh4/sh7750.c b/hw/sh4/sh7750.c index 41306fb600..300eabc595 100644 --- a/hw/sh4/sh7750.c +++ b/hw/sh4/sh7750.c @@ -36,7 +36,6 @@ #include "sh7750_regnames.h" #include "hw/sh4/sh_intc.h" #include "hw/timer/tmu012.h" -#include "exec/exec-all.h" #include "exec/cputlb.h" #include "trace.h" diff --git a/include/exec/exec-all.h b/include/exec/exec-all.h deleted file mode 100644 index 9ef7569a0b..0000000000 --- a/include/exec/exec-all.h +++ /dev/null @@ -1,23 +0,0 @@ -/* - * internal execution defines for qemu - * - * Copyright (c) 2003 Fabrice Bellard - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - */ - -#ifndef EXEC_ALL_H -#define EXEC_ALL_H - -#endif diff --git a/include/system/ram_addr.h b/include/system/ram_addr.h index b4e4425acb..15a1b1a4fa 100644 --- a/include/system/ram_addr.h +++ b/include/system/ram_addr.h @@ -24,7 +24,6 @@ #include "exec/cputlb.h" #include "exec/ramlist.h" #include "system/ramblock.h" -#include "exec/exec-all.h" #include "system/memory.h" #include "exec/target_page.h" #include "qemu/rcu.h" diff --git a/linux-user/main.c b/linux-user/main.c index e2ec5970be..4af7f49f38 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -40,7 +40,6 @@ #include "qemu/plugin.h" #include "user/guest-base.h" #include "user/page-protection.h" -#include "exec/exec-all.h" #include "exec/gdbstub.h" #include "gdbstub/user.h" #include "tcg/startup.h" diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h index 4aa253b566..691b9a1775 100644 --- a/linux-user/user-internals.h +++ b/linux-user/user-internals.h @@ -19,7 +19,6 @@ #define LINUX_USER_USER_INTERNALS_H #include "user/thunk.h" -#include "exec/exec-all.h" #include "qemu/log.h" extern char *exec_path; diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index ebbb300f86..4554844e15 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -10,7 +10,6 @@ #include "qemu/osdep.h" #include "accel/tcg/cpu-mmu-index.h" #include "accel/tcg/probe.h" -#include "exec/exec-all.h" #include "exec/target_page.h" #include "exec/tlb-flags.h" #include "semihosting/uaccess.h" diff --git a/system/physmem.c b/system/physmem.c index f1ec0902c7..3f4fd69d9a 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -32,7 +32,6 @@ #include "accel/tcg/iommu.h" #endif /* CONFIG_TCG */ -#include "exec/exec-all.h" #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/target_page.h" diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 27e2008a4e..68414af8d3 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -23,7 +23,6 @@ #include "qapi/error.h" #include "qemu/qemu-print.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "exec/target_page.h" #include "fpu/softfloat.h" diff --git a/target/alpha/fpu_helper.c b/target/alpha/fpu_helper.c index 6aefb9b851..30f3c7fd18 100644 --- a/target/alpha/fpu_helper.c +++ b/target/alpha/fpu_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/target/alpha/int_helper.c b/target/alpha/int_helper.c index 5672696f6f..6bfe63500e 100644 --- a/target/alpha/int_helper.c +++ b/target/alpha/int_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" diff --git a/target/alpha/mem_helper.c b/target/alpha/mem_helper.c index a4d5adb40c..2113fe33ae 100644 --- a/target/alpha/mem_helper.c +++ b/target/alpha/mem_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" static void do_unaligned_access(CPUAlphaState *env, vaddr addr, uintptr_t retaddr) diff --git a/target/alpha/translate.c b/target/alpha/translate.c index 7f3195a5dc..cebab0318c 100644 --- a/target/alpha/translate.c +++ b/target/alpha/translate.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "system/cpus.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" diff --git a/target/alpha/vax_helper.c b/target/alpha/vax_helper.c index f94fb519db..c1d201e7b4 100644 --- a/target/alpha/vax_helper.c +++ b/target/alpha/vax_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 5e951675c6..7b801eb3aa 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -33,7 +33,6 @@ #endif /* CONFIG_TCG */ #include "internals.h" #include "cpu-features.h" -#include "exec/exec-all.h" #include "exec/target_page.h" #include "hw/qdev-properties.h" #if !defined(CONFIG_USER_ONLY) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index 473ee2af38..de7999f6a9 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -11,7 +11,6 @@ #include "internals.h" #include "cpu-features.h" #include "cpregs.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "exec/watchpoint.h" #include "system/tcg.h" diff --git a/target/arm/helper.c b/target/arm/helper.c index 2f039b2db3..8de4eb2c1f 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -20,7 +20,6 @@ #include "qemu/bitops.h" #include "qemu/qemu-print.h" #include "exec/cputlb.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "hw/irq.h" #include "system/cpu-timers.h" diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 87d707b592..1040a18962 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -10,7 +10,6 @@ #include "qemu/log.h" #include "qemu/range.h" #include "qemu/main-loop.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "exec/tlb-flags.h" diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index cfe5faba19..9cffda07cd 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -29,7 +29,6 @@ #include "internals.h" #include "qemu/crc32c.h" #include "exec/cpu-common.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/probe.h" #include "exec/target_page.h" diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index 37dc98dc35..6614719832 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -15,7 +15,6 @@ #include "qemu/main-loop.h" #include "qemu/bitops.h" #include "qemu/log.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #ifdef CONFIG_TCG #include "accel/tcg/cpu-ldst.h" diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 8fbdcc8fb9..13d7ac0097 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -21,7 +21,6 @@ #include "qemu/log.h" #include "cpu.h" #include "internals.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #ifdef CONFIG_USER_ONLY #include "user/cpu_loop.h" diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index f9f67d1f88..506d1c3475 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -23,7 +23,6 @@ #include "vec_internal.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst.h" -#include "exec/exec-all.h" #include "tcg/tcg.h" #include "fpu/softfloat.h" #include "crypto/clmul.h" diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index d50b8720ad..dc3f83c37d 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -23,7 +23,6 @@ #include "exec/target_page.h" #include "internals.h" #include "cpu-features.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/probe.h" #include "cpregs.h" diff --git a/target/arm/tcg/pauth_helper.c b/target/arm/tcg/pauth_helper.c index 59bf27541d..c591c3052c 100644 --- a/target/arm/tcg/pauth_helper.c +++ b/target/arm/tcg/pauth_helper.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "internals.h" #include "cpu-features.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 96b84c37a2..3226895cae 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -23,7 +23,6 @@ #include "tcg/tcg-gvec-desc.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst.h" -#include "exec/exec-all.h" #include "qemu/int128.h" #include "fpu/softfloat.h" #include "vec_internal.h" diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 50aca54eaa..9f20ecb51d 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/helper-proto.h" #include "exec/target_page.h" diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index 8841f039bc..5ea4d6590f 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -9,7 +9,6 @@ #include "cpu.h" #include "internals.h" #include "cpu-features.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d9305f9d26..52cf47e775 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -17,7 +17,6 @@ * License along with this library; if not, see . */ #include "qemu/osdep.h" -#include "exec/exec-all.h" #include "exec/target_page.h" #include "translate.h" #include "translate-a64.h" diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 53e485d28a..1bfdb0fb9b 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -4,7 +4,6 @@ #include "cpu.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" -#include "exec/exec-all.h" #include "exec/translator.h" #include "exec/translation-block.h" #include "exec/helper-gen.h" diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 3f261c6fec..69fface7e9 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/qemu-print.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "system/address-spaces.h" #include "cpu.h" diff --git a/target/avr/translate.c b/target/avr/translate.c index b9c592c899..804b0b21db 100644 --- a/target/avr/translate.c +++ b/target/avr/translate.c @@ -22,7 +22,6 @@ #include "qemu/qemu-print.h" #include "tcg/tcg.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index a5d31c33bd..c1bfa80252 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -19,7 +19,6 @@ #include "qemu/qemu-print.h" #include "cpu.h" #include "internal.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "qapi/error.h" #include "hw/qdev-properties.h" diff --git a/target/hexagon/op_helper.c b/target/hexagon/op_helper.c index dd726b4318..444799d3ad 100644 --- a/target/hexagon/op_helper.c +++ b/target/hexagon/op_helper.c @@ -17,7 +17,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/probe.h" #include "exec/helper-proto.h" diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index b792cb247a..b083693b57 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -24,7 +24,6 @@ #include "qemu/timer.h" #include "cpu.h" #include "qemu/module.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "exec/target_page.h" #include "fpu/softfloat.h" diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c index a62d9d3083..ddd0a343d6 100644 --- a/target/hppa/fpu_helper.c +++ b/target/hppa/fpu_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/target/hppa/helper.c b/target/hppa/helper.c index ac7f58f0af..d7f8495d98 100644 --- a/target/hppa/helper.c +++ b/target/hppa/helper.c @@ -21,7 +21,6 @@ #include "qemu/log.h" #include "cpu.h" #include "fpu/softfloat.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/qemu-print.h" #include "hw/hppa/hppa_hardware.h" diff --git a/target/hppa/mem_helper.c b/target/hppa/mem_helper.c index a5f73aedf8..9bdd0a6f23 100644 --- a/target/hppa/mem_helper.c +++ b/target/hppa/mem_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/cputlb.h" #include "accel/tcg/cpu-mmu-index.h" #include "accel/tcg/probe.h" diff --git a/target/hppa/op_helper.c b/target/hppa/op_helper.c index 32207c1a4c..0458378abb 100644 --- a/target/hppa/op_helper.c +++ b/target/hppa/op_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/probe.h" diff --git a/target/hppa/sys_helper.c b/target/hppa/sys_helper.c index 052a6a88a2..6e65fadcc7 100644 --- a/target/hppa/sys_helper.c +++ b/target/hppa/sys_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/timer.h" #include "system/runstate.h" diff --git a/target/hppa/translate.c b/target/hppa/translate.c index 88a7d339eb..7a81cfcb88 100644 --- a/target/hppa/translate.c +++ b/target/hppa/translate.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" diff --git a/target/i386/tcg/access.c b/target/i386/tcg/access.c index ee5b451459..97e3f0e756 100644 --- a/target/i386/tcg/access.c +++ b/target/i386/tcg/access.c @@ -5,7 +5,6 @@ #include "cpu.h" #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/probe.h" -#include "exec/exec-all.h" #include "exec/target_page.h" #include "access.h" diff --git a/target/i386/tcg/excp_helper.c b/target/i386/tcg/excp_helper.c index de71e68f98..6fb8036d98 100644 --- a/target/i386/tcg/excp_helper.c +++ b/target/i386/tcg/excp_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "qemu/log.h" #include "system/runstate.h" #include "exec/helper-proto.h" diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h index 54d845379c..6b3f19855f 100644 --- a/target/i386/tcg/helper-tcg.h +++ b/target/i386/tcg/helper-tcg.h @@ -20,7 +20,6 @@ #ifndef I386_HELPER_TCG_H #define I386_HELPER_TCG_H -#include "exec/exec-all.h" #include "qemu/host-utils.h" /* Maximum instruction code size */ diff --git a/target/i386/tcg/int_helper.c b/target/i386/tcg/int_helper.c index 1a02e9d843..46741d9f68 100644 --- a/target/i386/tcg/int_helper.c +++ b/target/i386/tcg/int_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "qapi/error.h" diff --git a/target/i386/tcg/mem_helper.c b/target/i386/tcg/mem_helper.c index 84a0815217..9e7c2d8029 100644 --- a/target/i386/tcg/mem_helper.c +++ b/target/i386/tcg/mem_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "qemu/int128.h" #include "qemu/atomic128.h" diff --git a/target/i386/tcg/mpx_helper.c b/target/i386/tcg/mpx_helper.c index a0f816dfae..fa8abcc482 100644 --- a/target/i386/tcg/mpx_helper.c +++ b/target/i386/tcg/mpx_helper.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst.h" -#include "exec/exec-all.h" #include "exec/target_page.h" #include "helper-tcg.h" diff --git a/target/i386/tcg/seg_helper.c b/target/i386/tcg/seg_helper.c index e45d71fa1a..0ca081b286 100644 --- a/target/i386/tcg/seg_helper.c +++ b/target/i386/tcg/seg_helper.c @@ -22,7 +22,6 @@ #include "cpu.h" #include "qemu/log.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/probe.h" #include "exec/log.h" diff --git a/target/i386/tcg/system/bpt_helper.c b/target/i386/tcg/system/bpt_helper.c index 08ccd3f5e6..aebb5caac3 100644 --- a/target/i386/tcg/system/bpt_helper.c +++ b/target/i386/tcg/system/bpt_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "exec/watchpoint.h" #include "tcg/helper-tcg.h" diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 8a641951cd..8c3023407d 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -21,7 +21,6 @@ #include "qemu/host-utils.h" #include "cpu.h" #include "accel/tcg/cpu-mmu-index.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" diff --git a/target/i386/tcg/user/excp_helper.c b/target/i386/tcg/user/excp_helper.c index b3bdb7831a..98fab4cbc3 100644 --- a/target/i386/tcg/user/excp_helper.c +++ b/target/i386/tcg/user/excp_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "tcg/helper-tcg.h" void x86_cpu_record_sigsegv(CPUState *cs, vaddr addr, diff --git a/target/i386/tcg/user/seg_helper.c b/target/i386/tcg/user/seg_helper.c index 5692dd5195..263f59937f 100644 --- a/target/i386/tcg/user/seg_helper.c +++ b/target/i386/tcg/user/seg_helper.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "tcg/helper-tcg.h" #include "tcg/seg_helper.h" diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 8ad45b453d..c083ad4fd9 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -15,7 +15,6 @@ #include "system/kvm.h" #include "kvm/kvm_loongarch.h" #include "hw/qdev-properties.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "cpu.h" #include "internals.h" diff --git a/target/loongarch/tcg/fpu_helper.c b/target/loongarch/tcg/fpu_helper.c index fc3fd0561e..fc9c64c20a 100644 --- a/target/loongarch/tcg/fpu_helper.c +++ b/target/loongarch/tcg/fpu_helper.c @@ -8,7 +8,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "fpu/softfloat.h" #include "internals.h" diff --git a/target/loongarch/tcg/iocsr_helper.c b/target/loongarch/tcg/iocsr_helper.c index e62170de3c..c155f48564 100644 --- a/target/loongarch/tcg/iocsr_helper.c +++ b/target/loongarch/tcg/iocsr_helper.c @@ -9,7 +9,6 @@ #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #define GET_MEMTXATTRS(cas) \ diff --git a/target/loongarch/tcg/op_helper.c b/target/loongarch/tcg/op_helper.c index 94e3b28016..16ac0d43bc 100644 --- a/target/loongarch/tcg/op_helper.c +++ b/target/loongarch/tcg/op_helper.c @@ -10,7 +10,6 @@ #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "internals.h" #include "qemu/crc32c.h" diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index af208d75ae..dc48b0f4d2 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -13,7 +13,6 @@ #include "internals.h" #include "exec/helper-proto.h" #include "exec/cputlb.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "accel/tcg/cpu-ldst.h" diff --git a/target/loongarch/tcg/vec_helper.c b/target/loongarch/tcg/vec_helper.c index 3faf52cbc4..a270998e63 100644 --- a/target/loongarch/tcg/vec_helper.c +++ b/target/loongarch/tcg/vec_helper.c @@ -7,7 +7,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" #include "internals.h" diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index ac4a0d85be..56012863c8 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "softfloat.h" diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 3b880dd4d9..15f110fa7a 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/cputlb.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "exec/gdbstub.h" diff --git a/target/m68k/op_helper.c b/target/m68k/op_helper.c index 242aecccbb..f29ae12af8 100644 --- a/target/m68k/op_helper.c +++ b/target/m68k/op_helper.c @@ -20,7 +20,6 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "semihosting/semihost.h" diff --git a/target/m68k/translate.c b/target/m68k/translate.c index b1266a7875..97afceb129 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "exec/target_page.h" #include "tcg/tcg-op.h" diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 00a2730de4..2720e5c1d2 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -27,7 +27,6 @@ #include "cpu.h" #include "qemu/module.h" #include "hw/qdev-properties.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "exec/gdbstub.h" #include "exec/translation-block.h" diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index 4624ce5b67..9e838dfa15 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -23,7 +23,6 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "fpu/softfloat.h" diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 23f1037236..671b1ae4db 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" diff --git a/target/mips/cpu.c b/target/mips/cpu.c index d13361a150..96fe4da255 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -29,7 +29,6 @@ #include "qemu/module.h" #include "system/kvm.h" #include "system/qtest.h" -#include "exec/exec-all.h" #include "hw/qdev-properties.h" #include "hw/qdev-clock.h" #include "fpu_helper.h" diff --git a/target/mips/system/physaddr.c b/target/mips/system/physaddr.c index 505781d84c..b8e1a5ac98 100644 --- a/target/mips/system/physaddr.c +++ b/target/mips/system/physaddr.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "../internal.h" diff --git a/target/mips/tcg/exception.c b/target/mips/tcg/exception.c index 1a8902ea1b..d32bcebf46 100644 --- a/target/mips/tcg/exception.c +++ b/target/mips/tcg/exception.c @@ -23,7 +23,6 @@ #include "cpu.h" #include "internal.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" target_ulong exception_resume_pc(CPUMIPSState *env) diff --git a/target/mips/tcg/fpu_helper.c b/target/mips/tcg/fpu_helper.c index 45d593de48..36af980802 100644 --- a/target/mips/tcg/fpu_helper.c +++ b/target/mips/tcg/fpu_helper.c @@ -24,7 +24,6 @@ #include "cpu.h" #include "internal.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "fpu/softfloat.h" #include "fpu_helper.h" diff --git a/target/mips/tcg/ldst_helper.c b/target/mips/tcg/ldst_helper.c index 2fb879fcbc..10319bf03a 100644 --- a/target/mips/tcg/ldst_helper.c +++ b/target/mips/tcg/ldst_helper.c @@ -23,7 +23,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "exec/memop.h" #include "internal.h" diff --git a/target/mips/tcg/msa_helper.c b/target/mips/tcg/msa_helper.c index fde34a39e1..f554b3d10e 100644 --- a/target/mips/tcg/msa_helper.c +++ b/target/mips/tcg/msa_helper.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "internal.h" #include "tcg/tcg.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/probe.h" #include "exec/helper-proto.h" diff --git a/target/mips/tcg/op_helper.c b/target/mips/tcg/op_helper.c index 65403f1a87..b906d10204 100644 --- a/target/mips/tcg/op_helper.c +++ b/target/mips/tcg/op_helper.c @@ -22,7 +22,6 @@ #include "cpu.h" #include "internal.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "exec/memop.h" #include "fpu_helper.h" diff --git a/target/mips/tcg/system/special_helper.c b/target/mips/tcg/system/special_helper.c index 3ce3ae1e12..b54cbe88a3 100644 --- a/target/mips/tcg/system/special_helper.c +++ b/target/mips/tcg/system/special_helper.c @@ -22,7 +22,6 @@ #include "qemu/log.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "internal.h" diff --git a/target/mips/tcg/system/tlb_helper.c b/target/mips/tcg/system/tlb_helper.c index e477ef812a..eccaf3624c 100644 --- a/target/mips/tcg/system/tlb_helper.c +++ b/target/mips/tcg/system/tlb_helper.c @@ -22,7 +22,6 @@ #include "cpu.h" #include "internal.h" #include "exec/cputlb.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "accel/tcg/cpu-ldst.h" diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 2ec267efec..8c8165d666 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -21,7 +21,6 @@ #include "qapi/error.h" #include "qemu/qemu-print.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "fpu/softfloat-helpers.h" #include "tcg/tcg.h" diff --git a/target/openrisc/exception.c b/target/openrisc/exception.c index 8699c3dcea..e213be36b6 100644 --- a/target/openrisc/exception.c +++ b/target/openrisc/exception.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exception.h" G_NORETURN void raise_exception(OpenRISCCPU *cpu, uint32_t excp) diff --git a/target/openrisc/exception_helper.c b/target/openrisc/exception_helper.c index 1f5be4bed9..c2c9d13652 100644 --- a/target/openrisc/exception_helper.c +++ b/target/openrisc/exception_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "exception.h" diff --git a/target/openrisc/fpu_helper.c b/target/openrisc/fpu_helper.c index 8b81d2f62f..dba997255c 100644 --- a/target/openrisc/fpu_helper.c +++ b/target/openrisc/fpu_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/target/openrisc/interrupt.c b/target/openrisc/interrupt.c index b3b5b40577..486823094c 100644 --- a/target/openrisc/interrupt.c +++ b/target/openrisc/interrupt.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" #include "gdbstub/helpers.h" #include "qemu/host-utils.h" #ifndef CONFIG_USER_ONLY diff --git a/target/openrisc/interrupt_helper.c b/target/openrisc/interrupt_helper.c index ab4ea88b69..1553ebc71b 100644 --- a/target/openrisc/interrupt_helper.c +++ b/target/openrisc/interrupt_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" void HELPER(rfe)(CPUOpenRISCState *env) diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index 92badf017f..951f8e247a 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/cputlb.h" #include "exec/target_page.h" #include "exec/helper-proto.h" diff --git a/target/openrisc/translate.c b/target/openrisc/translate.c index baadea4448..5ab3bc7021 100644 --- a/target/openrisc/translate.c +++ b/target/openrisc/translate.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "accel/tcg/cpu-mmu-index.h" -#include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/bitops.h" diff --git a/target/ppc/excp_helper.c b/target/ppc/excp_helper.c index da8b525a41..1efdc4066e 100644 --- a/target/ppc/excp_helper.c +++ b/target/ppc/excp_helper.c @@ -24,7 +24,6 @@ #include "system/system.h" #include "system/runstate.h" #include "cpu.h" -#include "exec/exec-all.h" #include "internal.h" #include "helper_regs.h" #include "hw/ppc/ppc.h" diff --git a/target/ppc/fpu_helper.c b/target/ppc/fpu_helper.c index d93cfed17b..07b782f971 100644 --- a/target/ppc/fpu_helper.c +++ b/target/ppc/fpu_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "internal.h" #include "fpu/softfloat.h" diff --git a/target/ppc/machine.c b/target/ppc/machine.c index 98df5b4a3a..d72e5ecb94 100644 --- a/target/ppc/machine.c +++ b/target/ppc/machine.c @@ -1,6 +1,5 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "system/kvm.h" #include "system/tcg.h" #include "helper_regs.h" diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index 50f05a915e..aa1af44d22 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/target_page.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" diff --git a/target/ppc/misc_helper.c b/target/ppc/misc_helper.c index 46ae454afd..e7d9462518 100644 --- a/target/ppc/misc_helper.c +++ b/target/ppc/misc_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/cputlb.h" #include "exec/helper-proto.h" #include "qemu/error-report.h" diff --git a/target/ppc/mmu-hash32.c b/target/ppc/mmu-hash32.c index 5bd3efe70e..8b980a5aa9 100644 --- a/target/ppc/mmu-hash32.c +++ b/target/ppc/mmu-hash32.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "system/kvm.h" diff --git a/target/ppc/mmu-hash64.c b/target/ppc/mmu-hash64.c index 3ba4810497..dd337558aa 100644 --- a/target/ppc/mmu-hash64.c +++ b/target/ppc/mmu-hash64.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "qemu/error-report.h" #include "qemu/qemu-print.h" diff --git a/target/ppc/mmu-radix64.c b/target/ppc/mmu-radix64.c index 4ab5f3bb92..33ac341290 100644 --- a/target/ppc/mmu-radix64.c +++ b/target/ppc/mmu-radix64.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "qemu/error-report.h" #include "system/kvm.h" diff --git a/target/ppc/mmu_common.c b/target/ppc/mmu_common.c index 394a0c9bb6..52d48615ac 100644 --- a/target/ppc/mmu_common.c +++ b/target/ppc/mmu_common.c @@ -24,7 +24,6 @@ #include "kvm_ppc.h" #include "mmu-hash64.h" #include "mmu-hash32.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "exec/log.h" diff --git a/target/ppc/mmu_helper.c b/target/ppc/mmu_helper.c index 2138666122..ac60705402 100644 --- a/target/ppc/mmu_helper.c +++ b/target/ppc/mmu_helper.c @@ -25,7 +25,6 @@ #include "mmu-hash64.h" #include "mmu-hash32.h" #include "exec/cputlb.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "exec/log.h" diff --git a/target/ppc/power8-pmu.c b/target/ppc/power8-pmu.c index db9ee8e96b..2a7a5b493a 100644 --- a/target/ppc/power8-pmu.c +++ b/target/ppc/power8-pmu.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "helper_regs.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/error-report.h" #include "qemu/timer.h" diff --git a/target/ppc/tcg-excp_helper.c b/target/ppc/tcg-excp_helper.c index 2b15e5f2f0..f835be5156 100644 --- a/target/ppc/tcg-excp_helper.c +++ b/target/ppc/tcg-excp_helper.c @@ -21,7 +21,6 @@ #include "qemu/log.h" #include "target/ppc/cpu.h" #include "accel/tcg/cpu-ldst.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "system/runstate.h" diff --git a/target/ppc/timebase_helper.c b/target/ppc/timebase_helper.c index 73120323b4..7209b418fb 100644 --- a/target/ppc/timebase_helper.c +++ b/target/ppc/timebase_helper.c @@ -20,7 +20,6 @@ #include "cpu.h" #include "hw/ppc/ppc.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "qemu/log.h" #include "qemu/main-loop.h" diff --git a/target/ppc/translate.c b/target/ppc/translate.c index 62dd008e36..27f90c3cc5 100644 --- a/target/ppc/translate.c +++ b/target/ppc/translate.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internal.h" -#include "exec/exec-all.h" #include "exec/target_page.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" diff --git a/target/ppc/user_only_helper.c b/target/ppc/user_only_helper.c index a4d07a0d0d..ae210eb847 100644 --- a/target/ppc/user_only_helper.c +++ b/target/ppc/user_only_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "internal.h" void ppc_cpu_record_sigsegv(CPUState *cs, vaddr address, diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index e0604f4c78..d92874baa0 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -24,7 +24,6 @@ #include "cpu.h" #include "cpu_vendorid.h" #include "internals.h" -#include "exec/exec-all.h" #include "qapi/error.h" #include "qapi/visitor.h" #include "qemu/error-report.h" diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 619c76cc00..f2e90a9889 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -24,7 +24,6 @@ #include "internals.h" #include "pmu.h" #include "exec/cputlb.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "system/memory.h" diff --git a/target/riscv/crypto_helper.c b/target/riscv/crypto_helper.c index bb084e00ef..a0fb54bc50 100644 --- a/target/riscv/crypto_helper.c +++ b/target/riscv/crypto_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "crypto/aes.h" #include "crypto/aes-round.h" diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 1308643855..a32e1455c9 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -24,7 +24,6 @@ #include "tcg/tcg-cpu.h" #include "pmu.h" #include "time_helper.h" -#include "exec/exec-all.h" #include "exec/cputlb.h" #include "exec/tb-flush.h" #include "exec/icount.h" diff --git a/target/riscv/debug.c b/target/riscv/debug.c index 8564f0b371..5664466749 100644 --- a/target/riscv/debug.c +++ b/target/riscv/debug.c @@ -28,7 +28,6 @@ #include "qapi/error.h" #include "cpu.h" #include "trace.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "exec/watchpoint.h" #include "system/cpu-timers.h" diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index 91b1a56d10..706bdfa61d 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" #include "internals.h" diff --git a/target/riscv/m128_helper.c b/target/riscv/m128_helper.c index ec14aaa901..7d9b83b1b2 100644 --- a/target/riscv/m128_helper.c +++ b/target/riscv/m128_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" target_ulong HELPER(divu_i128)(CPURISCVState *env, diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index abb1d284dc..05316f2088 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -21,7 +21,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "internals.h" -#include "exec/exec-all.h" #include "exec/cputlb.h" #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/probe.h" diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 54ac54f2e1..2f757c2a5e 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -18,7 +18,6 @@ */ #include "qemu/osdep.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "tcg-cpu.h" #include "cpu.h" diff --git a/target/riscv/translate.c b/target/riscv/translate.c index cef61b5b29..85128f997b 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -20,7 +20,6 @@ #include "qemu/log.h" #include "cpu.h" #include "tcg/tcg-op.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" #include "exec/target_page.h" diff --git a/target/riscv/vcrypto_helper.c b/target/riscv/vcrypto_helper.c index 1526de96f5..9a0d9b4f53 100644 --- a/target/riscv/vcrypto_helper.c +++ b/target/riscv/vcrypto_helper.c @@ -26,7 +26,6 @@ #include "crypto/aes-round.h" #include "crypto/sm4.h" #include "exec/memop.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "internals.h" #include "vector_internals.h" diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 5ccb294e31..8eea3e6df0 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -21,7 +21,6 @@ #include "qemu/bitops.h" #include "cpu.h" #include "exec/memop.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/probe.h" #include "exec/page-protection.h" diff --git a/target/riscv/zce_helper.c b/target/riscv/zce_helper.c index 50d65f386c..55221f5f37 100644 --- a/target/riscv/zce_helper.c +++ b/target/riscv/zce_helper.c @@ -18,7 +18,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst.h" diff --git a/target/rx/op_helper.c b/target/rx/op_helper.c index a2f1f3824d..2b190a4b2c 100644 --- a/target/rx/op_helper.c +++ b/target/rx/op_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "qemu/bitops.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst.h" #include "fpu/softfloat.h" diff --git a/target/rx/translate.c b/target/rx/translate.c index bbda703be8..19a9584a82 100644 --- a/target/rx/translate.c +++ b/target/rx/translate.c @@ -20,7 +20,6 @@ #include "qemu/bswap.h" #include "qemu/qemu-print.h" #include "cpu.h" -#include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" diff --git a/target/s390x/interrupt.c b/target/s390x/interrupt.c index 4ae6e2ddea..1dca835c5d 100644 --- a/target/s390x/interrupt.c +++ b/target/s390x/interrupt.c @@ -11,7 +11,6 @@ #include "cpu.h" #include "kvm/kvm_s390x.h" #include "s390x-internal.h" -#include "exec/exec-all.h" #include "system/kvm.h" #include "system/tcg.h" #include "hw/s390x/ioinst.h" diff --git a/target/s390x/mmu_helper.c b/target/s390x/mmu_helper.c index 0e133cb9a5..00946e9c0f 100644 --- a/target/s390x/mmu_helper.c +++ b/target/s390x/mmu_helper.c @@ -23,7 +23,6 @@ #include "kvm/kvm_s390x.h" #include "system/kvm.h" #include "system/tcg.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "hw/hw.h" diff --git a/target/s390x/sigp.c b/target/s390x/sigp.c index a3347f1236..5e95c4978f 100644 --- a/target/s390x/sigp.c +++ b/target/s390x/sigp.c @@ -16,7 +16,6 @@ #include "system/runstate.h" #include "system/address-spaces.h" #include "exec/cputlb.h" -#include "exec/exec-all.h" #include "system/tcg.h" #include "trace.h" #include "qapi/qapi-types-machine.h" diff --git a/target/s390x/tcg/cc_helper.c b/target/s390x/tcg/cc_helper.c index b36f8cdc8b..6595ac763c 100644 --- a/target/s390x/tcg/cc_helper.c +++ b/target/s390x/tcg/cc_helper.c @@ -22,7 +22,6 @@ #include "cpu.h" #include "s390x-internal.h" #include "tcg_s390x.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" diff --git a/target/s390x/tcg/crypto_helper.c b/target/s390x/tcg/crypto_helper.c index 642c1b18c4..4447bb66ee 100644 --- a/target/s390x/tcg/crypto_helper.c +++ b/target/s390x/tcg/crypto_helper.c @@ -17,7 +17,6 @@ #include "s390x-internal.h" #include "tcg_s390x.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" static uint64_t R(uint64_t x, int c) diff --git a/target/s390x/tcg/excp_helper.c b/target/s390x/tcg/excp_helper.c index 6cd813e1ab..e4c75d0ce0 100644 --- a/target/s390x/tcg/excp_helper.c +++ b/target/s390x/tcg/excp_helper.c @@ -23,7 +23,6 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "exec/cputlb.h" -#include "exec/exec-all.h" #include "exec/target_page.h" #include "exec/watchpoint.h" #include "s390x-internal.h" diff --git a/target/s390x/tcg/fpu_helper.c b/target/s390x/tcg/fpu_helper.c index 5041c13962..1ba43715ac 100644 --- a/target/s390x/tcg/fpu_helper.c +++ b/target/s390x/tcg/fpu_helper.c @@ -22,7 +22,6 @@ #include "cpu.h" #include "s390x-internal.h" #include "tcg_s390x.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/target/s390x/tcg/int_helper.c b/target/s390x/tcg/int_helper.c index 253c036415..fbda396f5b 100644 --- a/target/s390x/tcg/int_helper.c +++ b/target/s390x/tcg/int_helper.c @@ -22,7 +22,6 @@ #include "cpu.h" #include "s390x-internal.h" #include "tcg_s390x.h" -#include "exec/exec-all.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst.h" diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 9e77cde81b..857005b120 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -25,7 +25,6 @@ #include "tcg_s390x.h" #include "exec/helper-proto.h" #include "exec/cpu-common.h" -#include "exec/exec-all.h" #include "exec/cputlb.h" #include "exec/page-protection.h" #include "accel/tcg/cpu-ldst.h" diff --git a/target/s390x/tcg/misc_helper.c b/target/s390x/tcg/misc_helper.c index d5088493ea..f7101be574 100644 --- a/target/s390x/tcg/misc_helper.c +++ b/target/s390x/tcg/misc_helper.c @@ -26,7 +26,6 @@ #include "qemu/host-utils.h" #include "exec/helper-proto.h" #include "qemu/timer.h" -#include "exec/exec-all.h" #include "exec/cputlb.h" #include "accel/tcg/cpu-ldst.h" #include "exec/target_page.h" diff --git a/target/s390x/tcg/translate.c b/target/s390x/tcg/translate.c index a714f9c0c2..c7e8574438 100644 --- a/target/s390x/tcg/translate.c +++ b/target/s390x/tcg/translate.c @@ -31,7 +31,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "s390x-internal.h" -#include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" #include "qemu/log.h" diff --git a/target/s390x/tcg/vec_fpu_helper.c b/target/s390x/tcg/vec_fpu_helper.c index 1bbaa82fe8..744f800fb6 100644 --- a/target/s390x/tcg/vec_fpu_helper.c +++ b/target/s390x/tcg/vec_fpu_helper.c @@ -15,7 +15,6 @@ #include "vec.h" #include "tcg_s390x.h" #include "tcg/tcg-gvec-desc.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/target/s390x/tcg/vec_helper.c b/target/s390x/tcg/vec_helper.c index 781ccc565b..46ec4a947d 100644 --- a/target/s390x/tcg/vec_helper.c +++ b/target/s390x/tcg/vec_helper.c @@ -17,7 +17,6 @@ #include "tcg/tcg-gvec-desc.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst.h" -#include "exec/exec-all.h" void HELPER(gvec_vbperm)(void *v1, const void *v2, const void *v3, uint32_t desc) diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 57d7b5fbc8..1885e7d5b2 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -24,7 +24,6 @@ #include "qemu/qemu-print.h" #include "cpu.h" #include "migration/vmstate.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "fpu/softfloat-helpers.h" #include "tcg/tcg.h" diff --git a/target/sh4/helper.c b/target/sh4/helper.c index b41d14d5d7..fb7642bda1 100644 --- a/target/sh4/helper.c +++ b/target/sh4/helper.c @@ -21,7 +21,6 @@ #include "cpu.h" #include "exec/cputlb.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "exec/log.h" diff --git a/target/sh4/op_helper.c b/target/sh4/op_helper.c index e7fcad3c1b..557b1bf497 100644 --- a/target/sh4/op_helper.c +++ b/target/sh4/op_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include "fpu/softfloat.h" diff --git a/target/sh4/translate.c b/target/sh4/translate.c index 712117be22..bf8828fce8 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "exec/helper-proto.h" #include "exec/helper-gen.h" diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index bc753d5f62..690e74f109 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -23,7 +23,6 @@ #include "qemu/module.h" #include "qemu/qemu-print.h" #include "accel/tcg/cpu-mmu-index.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "hw/qdev-properties.h" #include "qapi/visitor.h" diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index c25097d07f..a49334150d 100644 --- a/target/sparc/fop_helper.c +++ b/target/sparc/fop_helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "fpu/softfloat.h" diff --git a/target/sparc/helper.c b/target/sparc/helper.c index 7846ddd6f6..9163b9d46a 100644 --- a/target/sparc/helper.c +++ b/target/sparc/helper.c @@ -19,7 +19,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "qemu/timer.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" diff --git a/target/sparc/ldst_helper.c b/target/sparc/ldst_helper.c index 4c5dba19d1..2c63eb9e03 100644 --- a/target/sparc/ldst_helper.c +++ b/target/sparc/ldst_helper.c @@ -23,7 +23,6 @@ #include "cpu.h" #include "tcg/tcg.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "exec/cputlb.h" #include "exec/page-protection.h" #include "exec/target_page.h" diff --git a/target/sparc/machine.c b/target/sparc/machine.c index 222e5709c5..4dd75aff74 100644 --- a/target/sparc/machine.c +++ b/target/sparc/machine.c @@ -1,6 +1,5 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "qemu/timer.h" #include "migration/cpu.h" diff --git a/target/sparc/translate.c b/target/sparc/translate.c index 63dd90447b..b922e53bf1 100644 --- a/target/sparc/translate.c +++ b/target/sparc/translate.c @@ -22,7 +22,6 @@ #include "cpu.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "exec/target_page.h" #include "tcg/tcg-op.h" #include "tcg/tcg-op-gvec.h" diff --git a/target/sparc/win_helper.c b/target/sparc/win_helper.c index 0c4b09f2c1..9ad9d01e8b 100644 --- a/target/sparc/win_helper.c +++ b/target/sparc/win_helper.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/helper-proto.h" #include "trace.h" diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 098cd06c54..9f19e903bc 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "cpu.h" -#include "exec/exec-all.h" #include "exec/translation-block.h" #include "qemu/error-report.h" #include "tcg/debug-assert.h" diff --git a/target/tricore/op_helper.c b/target/tricore/op_helper.c index ae559b6922..9910c13f4b 100644 --- a/target/tricore/op_helper.c +++ b/target/tricore/op_helper.c @@ -18,7 +18,6 @@ #include "cpu.h" #include "qemu/host-utils.h" #include "exec/helper-proto.h" -#include "exec/exec-all.h" #include "accel/tcg/cpu-ldst.h" #include /* for crc32 */ diff --git a/target/tricore/translate.c b/target/tricore/translate.c index ba36c9fcc8..3d0e7a10bd 100644 --- a/target/tricore/translate.c +++ b/target/tricore/translate.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "accel/tcg/cpu-ldst.h" #include "qemu/qemu-print.h" diff --git a/target/xtensa/dbg_helper.c b/target/xtensa/dbg_helper.c index c4f4298a50..3b91f7c38a 100644 --- a/target/xtensa/dbg_helper.c +++ b/target/xtensa/dbg_helper.c @@ -30,7 +30,6 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" #include "exec/watchpoint.h" #include "system/address-spaces.h" diff --git a/target/xtensa/exc_helper.c b/target/xtensa/exc_helper.c index ca629f071d..b611c9bf97 100644 --- a/target/xtensa/exc_helper.c +++ b/target/xtensa/exc_helper.c @@ -32,7 +32,6 @@ #include "exec/helper-proto.h" #include "qemu/host-utils.h" #include "qemu/atomic.h" -#include "exec/exec-all.h" void HELPER(exception)(CPUXtensaState *env, uint32_t excp) { diff --git a/target/xtensa/fpu_helper.c b/target/xtensa/fpu_helper.c index 53fc7cfd2a..5358060c50 100644 --- a/target/xtensa/fpu_helper.c +++ b/target/xtensa/fpu_helper.c @@ -30,7 +30,6 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" #include "fpu/softfloat.h" enum { diff --git a/target/xtensa/mmu_helper.c b/target/xtensa/mmu_helper.c index 182c6e35c1..71330fc84b 100644 --- a/target/xtensa/mmu_helper.c +++ b/target/xtensa/mmu_helper.c @@ -35,7 +35,6 @@ #include "exec/cputlb.h" #include "accel/tcg/cpu-mmu-index.h" #include "accel/tcg/probe.h" -#include "exec/exec-all.h" #include "exec/page-protection.h" #include "exec/target_page.h" #include "system/memory.h" diff --git a/target/xtensa/op_helper.c b/target/xtensa/op_helper.c index c125fa4946..fc47ebaaf5 100644 --- a/target/xtensa/op_helper.c +++ b/target/xtensa/op_helper.c @@ -30,7 +30,6 @@ #include "exec/helper-proto.h" #include "exec/page-protection.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" #include "system/memory.h" #include "qemu/atomic.h" #include "qemu/timer.h" diff --git a/target/xtensa/translate.c b/target/xtensa/translate.c index 2af83c07c2..34ae2f4e16 100644 --- a/target/xtensa/translate.c +++ b/target/xtensa/translate.c @@ -31,7 +31,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/exec-all.h" #include "tcg/tcg-op.h" #include "qemu/log.h" #include "qemu/qemu-print.h" diff --git a/target/xtensa/win_helper.c b/target/xtensa/win_helper.c index ec9ff44db0..4b25f8f4de 100644 --- a/target/xtensa/win_helper.c +++ b/target/xtensa/win_helper.c @@ -30,7 +30,6 @@ #include "cpu.h" #include "exec/helper-proto.h" #include "qemu/host-utils.h" -#include "exec/exec-all.h" static void copy_window_from_phys(CPUXtensaState *env, uint32_t window, uint32_t phys, uint32_t n) From 9a1e3713232f9641353e63fd279eb83cc723faf2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 26 Apr 2025 20:20:24 +0000 Subject: [PATCH 0552/2760] accel/tcg: Generalize fake_user_interrupt test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test for the hook being present instead of ifdef TARGET_I386. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 279df5fae7..8ff4a34509 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -732,10 +732,10 @@ static inline bool cpu_handle_exception(CPUState *cpu, int *ret) * If user mode only, we simulate a fake exception which will be * handled outside the cpu execution loop. */ -#if defined(TARGET_I386) const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; - tcg_ops->fake_user_interrupt(cpu); -#endif /* TARGET_I386 */ + if (tcg_ops->fake_user_interrupt) { + tcg_ops->fake_user_interrupt(cpu); + } *ret = cpu->exception_index; cpu->exception_index = -1; return true; From 81ef6a2295740c4b2de9db2f81ebb9ad346b8cfc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Apr 2025 11:02:25 -0700 Subject: [PATCH 0553/2760] accel/tcg: Unconditionally use CPU_DUMP_CCOP in log_cpu_exec This flag is only tested by target/i386, so including this makes no functional change. This is similar to other places like cpu-target.c which use CPU_DUMP_CCOP unconditionally. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 8ff4a34509..ff979a2c57 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -285,14 +285,11 @@ static void log_cpu_exec(vaddr pc, CPUState *cpu, if (qemu_loglevel_mask(CPU_LOG_TB_CPU)) { FILE *logfile = qemu_log_trylock(); if (logfile) { - int flags = 0; + int flags = CPU_DUMP_CCOP; if (qemu_loglevel_mask(CPU_LOG_TB_FPU)) { flags |= CPU_DUMP_FPU; } -#if defined(TARGET_I386) - flags |= CPU_DUMP_CCOP; -#endif if (qemu_loglevel_mask(CPU_LOG_TB_VPU)) { flags |= CPU_DUMP_VPU; } From 9181ab452893a3f45cdc0f6196fbb9e389a4e5cd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Apr 2025 11:31:30 -0700 Subject: [PATCH 0554/2760] accel/tcg: Introduce TCGCPUOps.cpu_exec_reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initialize all instances with cpu_reset(), so that there is no functional change. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 3 ++- include/accel/tcg/cpu-ops.h | 2 ++ target/alpha/cpu.c | 1 + target/arm/cpu.c | 1 + target/arm/tcg/cpu-v7m.c | 1 + target/avr/cpu.c | 1 + target/hppa/cpu.c | 1 + target/i386/tcg/tcg-cpu.c | 1 + target/loongarch/cpu.c | 1 + target/m68k/cpu.c | 1 + target/microblaze/cpu.c | 1 + target/mips/cpu.c | 1 + target/openrisc/cpu.c | 1 + target/ppc/cpu_init.c | 1 + target/riscv/tcg/tcg-cpu.c | 1 + target/rx/cpu.c | 1 + target/s390x/cpu.c | 1 + target/sh4/cpu.c | 1 + target/sparc/cpu.c | 1 + target/tricore/cpu.c | 1 + target/xtensa/cpu.c | 1 + 21 files changed, 23 insertions(+), 1 deletion(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index ff979a2c57..010f38edaa 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -834,7 +834,7 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, #else else if (interrupt_request & CPU_INTERRUPT_RESET) { replay_interrupt(); - cpu_reset(cpu); + cpu->cc->tcg_ops->cpu_exec_reset(cpu); bql_unlock(); return true; } @@ -1070,6 +1070,7 @@ bool tcg_exec_realizefn(CPUState *cpu, Error **errp) #ifndef CONFIG_USER_ONLY assert(tcg_ops->cpu_exec_halt); assert(tcg_ops->cpu_exec_interrupt); + assert(tcg_ops->cpu_exec_reset); #endif /* !CONFIG_USER_ONLY */ assert(tcg_ops->translate_code); assert(tcg_ops->mmu_index); diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index 60b5e97205..3ff72b8d9d 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -155,6 +155,8 @@ struct TCGCPUOps { void (*do_interrupt)(CPUState *cpu); /** @cpu_exec_interrupt: Callback for processing interrupts in cpu_exec */ bool (*cpu_exec_interrupt)(CPUState *cpu, int interrupt_request); + /** @cpu_exec_reset: Callback for reset in cpu_exec. */ + void (*cpu_exec_reset)(CPUState *cpu); /** * @cpu_exec_halt: Callback for handling halt in cpu_exec. * diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 68414af8d3..d4e66aa432 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -251,6 +251,7 @@ static const TCGCPUOps alpha_tcg_ops = { .tlb_fill = alpha_cpu_tlb_fill, .cpu_exec_interrupt = alpha_cpu_exec_interrupt, .cpu_exec_halt = alpha_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = alpha_cpu_do_interrupt, .do_transaction_failed = alpha_cpu_do_transaction_failed, .do_unaligned_access = alpha_cpu_do_unaligned_access, diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 7b801eb3aa..3dde70b04a 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2705,6 +2705,7 @@ static const TCGCPUOps arm_tcg_ops = { .tlb_fill_align = arm_cpu_tlb_fill_align, .cpu_exec_interrupt = arm_cpu_exec_interrupt, .cpu_exec_halt = arm_cpu_exec_halt, + .cpu_exec_reset = cpu_reset, .do_interrupt = arm_cpu_do_interrupt, .do_transaction_failed = arm_cpu_do_transaction_failed, .do_unaligned_access = arm_cpu_do_unaligned_access, diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index b34b657857..5c8c374885 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -250,6 +250,7 @@ static const TCGCPUOps arm_v7m_tcg_ops = { .tlb_fill_align = arm_cpu_tlb_fill_align, .cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt, .cpu_exec_halt = arm_cpu_exec_halt, + .cpu_exec_reset = cpu_reset, .do_interrupt = arm_v7m_cpu_do_interrupt, .do_transaction_failed = arm_cpu_do_transaction_failed, .do_unaligned_access = arm_cpu_do_unaligned_access, diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 69fface7e9..50b835e1ae 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -232,6 +232,7 @@ static const TCGCPUOps avr_tcg_ops = { .mmu_index = avr_cpu_mmu_index, .cpu_exec_interrupt = avr_cpu_exec_interrupt, .cpu_exec_halt = avr_cpu_has_work, + .cpu_exec_reset = cpu_reset, .tlb_fill = avr_cpu_tlb_fill, .do_interrupt = avr_cpu_do_interrupt, }; diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index b083693b57..60b618a22b 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -271,6 +271,7 @@ static const TCGCPUOps hppa_tcg_ops = { .tlb_fill_align = hppa_cpu_tlb_fill_align, .cpu_exec_interrupt = hppa_cpu_exec_interrupt, .cpu_exec_halt = hppa_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = hppa_cpu_do_interrupt, .do_unaligned_access = hppa_cpu_do_unaligned_access, .do_transaction_failed = hppa_cpu_do_transaction_failed, diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 192812656c..5d1c758ae3 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -147,6 +147,7 @@ const TCGCPUOps x86_tcg_ops = { .do_interrupt = x86_cpu_do_interrupt, .cpu_exec_halt = x86_cpu_exec_halt, .cpu_exec_interrupt = x86_cpu_exec_interrupt, + .cpu_exec_reset = cpu_reset, .do_unaligned_access = x86_cpu_do_unaligned_access, .debug_excp_handler = breakpoint_handler, .debug_check_breakpoint = x86_debug_check_breakpoint, diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index c083ad4fd9..c64cba72dd 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -877,6 +877,7 @@ static const TCGCPUOps loongarch_tcg_ops = { .tlb_fill = loongarch_cpu_tlb_fill, .cpu_exec_interrupt = loongarch_cpu_exec_interrupt, .cpu_exec_halt = loongarch_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = loongarch_cpu_do_interrupt, .do_transaction_failed = loongarch_cpu_do_transaction_failed, #endif diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 6f33b86c7d..f446c6c8f7 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -602,6 +602,7 @@ static const TCGCPUOps m68k_tcg_ops = { .tlb_fill = m68k_cpu_tlb_fill, .cpu_exec_interrupt = m68k_cpu_exec_interrupt, .cpu_exec_halt = m68k_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = m68k_cpu_do_interrupt, .do_transaction_failed = m68k_cpu_transaction_failed, #endif /* !CONFIG_USER_ONLY */ diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 2720e5c1d2..f305ed04f6 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -440,6 +440,7 @@ static const TCGCPUOps mb_tcg_ops = { .tlb_fill = mb_cpu_tlb_fill, .cpu_exec_interrupt = mb_cpu_exec_interrupt, .cpu_exec_halt = mb_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = mb_cpu_do_interrupt, .do_transaction_failed = mb_cpu_transaction_failed, .do_unaligned_access = mb_cpu_do_unaligned_access, diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 96fe4da255..09ed330027 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -563,6 +563,7 @@ static const TCGCPUOps mips_tcg_ops = { .tlb_fill = mips_cpu_tlb_fill, .cpu_exec_interrupt = mips_cpu_exec_interrupt, .cpu_exec_halt = mips_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = mips_cpu_do_interrupt, .do_transaction_failed = mips_cpu_do_transaction_failed, .do_unaligned_access = mips_cpu_do_unaligned_access, diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 8c8165d666..94776e0ad8 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -255,6 +255,7 @@ static const TCGCPUOps openrisc_tcg_ops = { .tlb_fill = openrisc_cpu_tlb_fill, .cpu_exec_interrupt = openrisc_cpu_exec_interrupt, .cpu_exec_halt = openrisc_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = openrisc_cpu_do_interrupt, #endif /* !CONFIG_USER_ONLY */ }; diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index b0973b6df9..3a01731402 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7492,6 +7492,7 @@ static const TCGCPUOps ppc_tcg_ops = { .tlb_fill = ppc_cpu_tlb_fill, .cpu_exec_interrupt = ppc_cpu_exec_interrupt, .cpu_exec_halt = ppc_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = ppc_cpu_do_interrupt, .cpu_exec_enter = ppc_cpu_exec_enter, .cpu_exec_exit = ppc_cpu_exec_exit, diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 2f757c2a5e..50782e0f0e 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -153,6 +153,7 @@ const TCGCPUOps riscv_tcg_ops = { .tlb_fill = riscv_cpu_tlb_fill, .cpu_exec_interrupt = riscv_cpu_exec_interrupt, .cpu_exec_halt = riscv_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = riscv_cpu_do_interrupt, .do_transaction_failed = riscv_cpu_do_transaction_failed, .do_unaligned_access = riscv_cpu_do_unaligned_access, diff --git a/target/rx/cpu.c b/target/rx/cpu.c index a51b543028..de2e6a22ff 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -217,6 +217,7 @@ static const TCGCPUOps rx_tcg_ops = { .cpu_exec_interrupt = rx_cpu_exec_interrupt, .cpu_exec_halt = rx_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = rx_cpu_do_interrupt, }; diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 99ff58affc..71338aae77 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -365,6 +365,7 @@ static const TCGCPUOps s390_tcg_ops = { .tlb_fill = s390_cpu_tlb_fill, .cpu_exec_interrupt = s390_cpu_exec_interrupt, .cpu_exec_halt = s390_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = s390_cpu_do_interrupt, .debug_excp_handler = s390x_cpu_debug_excp_handler, .do_unaligned_access = s390x_cpu_do_unaligned_access, diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 1885e7d5b2..681237c511 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -275,6 +275,7 @@ static const TCGCPUOps superh_tcg_ops = { .tlb_fill = superh_cpu_tlb_fill, .cpu_exec_interrupt = superh_cpu_exec_interrupt, .cpu_exec_halt = superh_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = superh_cpu_do_interrupt, .do_unaligned_access = superh_cpu_do_unaligned_access, .io_recompile_replay_branch = superh_io_recompile_replay_branch, diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 690e74f109..bbdea8556a 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -1034,6 +1034,7 @@ static const TCGCPUOps sparc_tcg_ops = { .tlb_fill = sparc_cpu_tlb_fill, .cpu_exec_interrupt = sparc_cpu_exec_interrupt, .cpu_exec_halt = sparc_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = sparc_cpu_do_interrupt, .do_transaction_failed = sparc_cpu_do_transaction_failed, .do_unaligned_access = sparc_cpu_do_unaligned_access, diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 9f19e903bc..0fcac697f6 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -182,6 +182,7 @@ static const TCGCPUOps tricore_tcg_ops = { .tlb_fill = tricore_cpu_tlb_fill, .cpu_exec_interrupt = tricore_cpu_exec_interrupt, .cpu_exec_halt = tricore_cpu_has_work, + .cpu_exec_reset = cpu_reset, }; static void tricore_cpu_class_init(ObjectClass *c, const void *data) diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 27d6e40195..9dcb883208 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -246,6 +246,7 @@ static const TCGCPUOps xtensa_tcg_ops = { .tlb_fill = xtensa_cpu_tlb_fill, .cpu_exec_interrupt = xtensa_cpu_exec_interrupt, .cpu_exec_halt = xtensa_cpu_has_work, + .cpu_exec_reset = cpu_reset, .do_interrupt = xtensa_cpu_do_interrupt, .do_transaction_failed = xtensa_cpu_do_transaction_failed, .do_unaligned_access = xtensa_cpu_do_unaligned_access, From c2d5897d3b712402d9543570c550a40cc0836522 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Apr 2025 11:44:23 -0700 Subject: [PATCH 0555/2760] target/i386: Split out x86_cpu_exec_reset Note that target/i386/cpu.h defines CPU_INTERRUPT_INIT as CPU_INTERRUPT_RESET. Therefore we can handle the new TCGCPUOps.cpu_exec_reset hook. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 39 ++++++++++++++------------------------- target/i386/tcg/tcg-cpu.c | 11 ++++++++++- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 010f38edaa..c21c5d202d 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -819,33 +819,22 @@ static inline bool cpu_handle_interrupt(CPUState *cpu, cpu->exception_index = EXCP_HLT; bql_unlock(); return true; - } -#if defined(TARGET_I386) - else if (interrupt_request & CPU_INTERRUPT_INIT) { - X86CPU *x86_cpu = X86_CPU(cpu); - CPUArchState *env = &x86_cpu->env; - replay_interrupt(); - cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0); - do_cpu_init(x86_cpu); - cpu->exception_index = EXCP_HALTED; - bql_unlock(); - return true; - } -#else - else if (interrupt_request & CPU_INTERRUPT_RESET) { - replay_interrupt(); - cpu->cc->tcg_ops->cpu_exec_reset(cpu); - bql_unlock(); - return true; - } -#endif /* !TARGET_I386 */ - /* The target hook has 3 exit conditions: - False when the interrupt isn't processed, - True when it is, and we should restart on a new TB, - and via longjmp via cpu_loop_exit. */ - else { + } else { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; + if (interrupt_request & CPU_INTERRUPT_RESET) { + replay_interrupt(); + tcg_ops->cpu_exec_reset(cpu); + bql_unlock(); + return true; + } + + /* + * The target hook has 3 exit conditions: + * False when the interrupt isn't processed, + * True when it is, and we should restart on a new TB, + * and via longjmp via cpu_loop_exit. + */ if (tcg_ops->cpu_exec_interrupt(cpu, interrupt_request)) { if (!tcg_ops->need_replay_interrupt || tcg_ops->need_replay_interrupt(interrupt_request)) { diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 5d1c758ae3..f3f0380e70 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -120,6 +120,15 @@ static bool x86_debug_check_breakpoint(CPUState *cs) /* RF disables all architectural breakpoints. */ return !(env->eflags & RF_MASK); } + +static void x86_cpu_exec_reset(CPUState *cs) +{ + CPUArchState *env = cpu_env(cs); + + cpu_svm_check_intercept_param(env, SVM_EXIT_INIT, 0, 0); + do_cpu_init(env_archcpu(env)); + cs->exception_index = EXCP_HALTED; +} #endif #include "accel/tcg/cpu-ops.h" @@ -147,7 +156,7 @@ const TCGCPUOps x86_tcg_ops = { .do_interrupt = x86_cpu_do_interrupt, .cpu_exec_halt = x86_cpu_exec_halt, .cpu_exec_interrupt = x86_cpu_exec_interrupt, - .cpu_exec_reset = cpu_reset, + .cpu_exec_reset = x86_cpu_exec_reset, .do_unaligned_access = x86_cpu_do_unaligned_access, .debug_excp_handler = breakpoint_handler, .debug_check_breakpoint = x86_debug_check_breakpoint, From a59a876999344be426144a3e6d163885220c1e93 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Apr 2025 15:15:14 -0700 Subject: [PATCH 0556/2760] accel/tcg: Hoist cpu_get_tb_cpu_state decl to accl/tcg/cpu-ops.h For some targets, simply remove the local definition. For other targets, move the inline definition out of line. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/accel/tcg/cpu-ops.h | 3 ++ target/alpha/cpu.c | 14 ++++++-- target/alpha/cpu.h | 11 ------ target/arm/cpu.h | 3 -- target/arm/helper.c | 1 + target/avr/cpu.c | 21 +++++++++-- target/avr/cpu.h | 18 ---------- target/hexagon/cpu.c | 18 ++++++++-- target/hexagon/cpu.h | 15 -------- target/hppa/cpu.c | 3 +- target/hppa/cpu.h | 3 -- target/i386/cpu.h | 14 -------- target/i386/tcg/tcg-cpu.c | 17 +++++++-- target/loongarch/cpu.c | 15 ++++++-- target/loongarch/cpu.h | 12 ------- target/m68k/cpu.c | 19 ++++++++-- target/m68k/cpu.h | 16 --------- target/microblaze/cpu.c | 11 ++++-- target/microblaze/cpu.h | 8 ----- target/mips/cpu.c | 9 +++++ target/mips/cpu.h | 9 ----- target/openrisc/cpu.c | 13 +++++-- target/openrisc/cpu.h | 10 ------ target/ppc/cpu.h | 13 ------- target/ppc/helper_regs.c | 16 ++++----- target/riscv/cpu.h | 3 -- target/rx/cpu.c | 12 +++++-- target/rx/cpu.h | 9 ----- target/s390x/cpu.c | 1 + target/s390x/cpu.h | 9 ----- target/sh4/cpu.c | 18 ++++++++-- target/sh4/cpu.h | 15 -------- target/sparc/cpu.h | 3 -- target/tricore/cpu.c | 15 ++++++-- target/tricore/cpu.h | 12 ------- target/xtensa/cpu.c | 71 +++++++++++++++++++++++++++++++++++-- target/xtensa/cpu.h | 68 ----------------------------------- 37 files changed, 243 insertions(+), 285 deletions(-) diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index 3ff72b8d9d..f5e5746976 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -18,6 +18,9 @@ #include "exec/vaddr.h" #include "tcg/tcg-mo.h" +void cpu_get_tb_cpu_state(CPUArchState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags); + struct TCGCPUOps { /** * mttcg_supported: multi-threaded TCG is supported diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index d4e66aa432..134806e755 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -25,6 +25,7 @@ #include "cpu.h" #include "exec/translation-block.h" #include "exec/target_page.h" +#include "accel/tcg/cpu-ops.h" #include "fpu/softfloat.h" @@ -40,6 +41,17 @@ static vaddr alpha_cpu_get_pc(CPUState *cs) return env->pc; } +void cpu_get_tb_cpu_state(CPUAlphaState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) +{ + *pc = env->pc; + *cs_base = 0; + *pflags = env->flags & ENV_FLAG_TB_MASK; +#ifdef CONFIG_USER_ONLY + *pflags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; +#endif +} + static void alpha_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { @@ -231,8 +243,6 @@ static const struct SysemuCPUOps alpha_sysemu_ops = { }; #endif -#include "accel/tcg/cpu-ops.h" - static const TCGCPUOps alpha_tcg_ops = { /* Alpha processors have a weak memory model */ .guest_default_memory_order = 0, diff --git a/target/alpha/cpu.h b/target/alpha/cpu.h index 849f673489..45944e46b5 100644 --- a/target/alpha/cpu.h +++ b/target/alpha/cpu.h @@ -464,17 +464,6 @@ void alpha_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, MemTxResult response, uintptr_t retaddr); #endif -static inline void cpu_get_tb_cpu_state(CPUAlphaState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) -{ - *pc = env->pc; - *cs_base = 0; - *pflags = env->flags & ENV_FLAG_TB_MASK; -#ifdef CONFIG_USER_ONLY - *pflags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; -#endif -} - #ifdef CONFIG_USER_ONLY /* Copied from linux ieee_swcr_to_fpcr. */ static inline uint64_t alpha_ieee_swcr_to_fpcr(uint64_t swcr) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index fdcf8cd1ae..be4449ca06 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3119,9 +3119,6 @@ static inline bool bswap_code(bool sctlr_b) #endif } -void cpu_get_tb_cpu_state(CPUARMState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags); - enum { QEMU_PSCI_CONDUIT_DISABLED = 0, QEMU_PSCI_CONDUIT_SMC = 1, diff --git a/target/arm/helper.c b/target/arm/helper.c index 8de4eb2c1f..98adeb7086 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -30,6 +30,7 @@ #include "qemu/guest-random.h" #ifdef CONFIG_TCG #include "accel/tcg/probe.h" +#include "accel/tcg/cpu-ops.h" #include "semihosting/common-semi.h" #endif #include "cpregs.h" diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 50b835e1ae..d9fecb272e 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -27,6 +27,7 @@ #include "disas/dis-asm.h" #include "tcg/debug-assert.h" #include "hw/qdev-properties.h" +#include "accel/tcg/cpu-ops.h" static void avr_cpu_set_pc(CPUState *cs, vaddr value) { @@ -53,6 +54,24 @@ static int avr_cpu_mmu_index(CPUState *cs, bool ifetch) return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX; } +void cpu_get_tb_cpu_state(CPUAVRState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) +{ + uint32_t flags = 0; + + *pc = env->pc_w * 2; + *cs_base = 0; + + if (env->fullacc) { + flags |= TB_FLAGS_FULL_ACCESS; + } + if (env->skip) { + flags |= TB_FLAGS_SKIP; + } + + *pflags = flags; +} + static void avr_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { @@ -220,8 +239,6 @@ static const struct SysemuCPUOps avr_sysemu_ops = { .get_phys_page_debug = avr_cpu_get_phys_page_debug, }; -#include "accel/tcg/cpu-ops.h" - static const TCGCPUOps avr_tcg_ops = { .guest_default_memory_order = 0, .mttcg_supported = false, diff --git a/target/avr/cpu.h b/target/avr/cpu.h index d6666175a9..518e243d81 100644 --- a/target/avr/cpu.h +++ b/target/avr/cpu.h @@ -205,24 +205,6 @@ enum { TB_FLAGS_SKIP = 2, }; -static inline void cpu_get_tb_cpu_state(CPUAVRState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) -{ - uint32_t flags = 0; - - *pc = env->pc_w * 2; - *cs_base = 0; - - if (env->fullacc) { - flags |= TB_FLAGS_FULL_ACCESS; - } - if (env->skip) { - flags |= TB_FLAGS_SKIP; - } - - *pflags = flags; -} - static inline int cpu_interrupts_enabled(CPUAVRState *env) { return env->sregI != 0; diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index c1bfa80252..2272f1222b 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -25,6 +25,7 @@ #include "fpu/softfloat-helpers.h" #include "tcg/tcg.h" #include "exec/gdbstub.h" +#include "accel/tcg/cpu-ops.h" static void hexagon_v66_cpu_init(Object *obj) { } static void hexagon_v67_cpu_init(Object *obj) { } @@ -254,6 +255,21 @@ static vaddr hexagon_cpu_get_pc(CPUState *cs) return cpu_env(cs)->gpr[HEX_REG_PC]; } +void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) +{ + uint32_t hex_flags = 0; + *pc = env->gpr[HEX_REG_PC]; + *cs_base = 0; + if (*pc == env->gpr[HEX_REG_SA0]) { + hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1); + } + *flags = hex_flags; + if (*pc & PCALIGN_MASK) { + hexagon_raise_exception_err(env, HEX_CAUSE_PC_NOT_ALIGNED, 0); + } +} + static void hexagon_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { @@ -321,8 +337,6 @@ static void hexagon_cpu_init(Object *obj) { } -#include "accel/tcg/cpu-ops.h" - static const TCGCPUOps hexagon_tcg_ops = { /* MTTCG not yet supported: require strict ordering */ .guest_default_memory_order = TCG_MO_ALL, diff --git a/target/hexagon/cpu.h b/target/hexagon/cpu.h index c065fa8ddc..43a854f517 100644 --- a/target/hexagon/cpu.h +++ b/target/hexagon/cpu.h @@ -137,21 +137,6 @@ G_NORETURN void hexagon_raise_exception_err(CPUHexagonState *env, uint32_t exception, uintptr_t pc); -static inline void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - uint32_t hex_flags = 0; - *pc = env->gpr[HEX_REG_PC]; - *cs_base = 0; - if (*pc == env->gpr[HEX_REG_SA0]) { - hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1); - } - *flags = hex_flags; - if (*pc & PCALIGN_MASK) { - hexagon_raise_exception_err(env, HEX_CAUSE_PC_NOT_ALIGNED, 0); - } -} - typedef HexagonCPU ArchCPU; void hexagon_translate_init(void); diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 60b618a22b..4cdaf98ab1 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -29,6 +29,7 @@ #include "fpu/softfloat.h" #include "tcg/tcg.h" #include "hw/hppa/hppa_hardware.h" +#include "accel/tcg/cpu-ops.h" static void hppa_cpu_set_pc(CPUState *cs, vaddr value) { @@ -249,8 +250,6 @@ static const struct SysemuCPUOps hppa_sysemu_ops = { }; #endif -#include "accel/tcg/cpu-ops.h" - static const TCGCPUOps hppa_tcg_ops = { /* PA-RISC 1.x processors have a strong memory model. */ /* diff --git a/target/hppa/cpu.h b/target/hppa/cpu.h index acc9937240..11d59d11ca 100644 --- a/target/hppa/cpu.h +++ b/target/hppa/cpu.h @@ -351,9 +351,6 @@ hwaddr hppa_abs_to_phys_pa2_w1(vaddr addr); #define CS_BASE_DIFFPAGE (1 << 12) #define CS_BASE_DIFFSPACE (1 << 13) -void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags); - target_ulong cpu_hppa_get_psw(CPUHPPAState *env); void cpu_hppa_put_psw(CPUHPPAState *env, target_ulong); void update_gva_offset_mask(CPUHPPAState *env); diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 3182ba413b..4f8ed8868e 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2599,20 +2599,6 @@ static inline bool is_mmu_index_32(int mmu_index) #include "hw/i386/apic.h" #endif -static inline void cpu_get_tb_cpu_state(CPUX86State *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *flags = env->hflags | - (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK | AC_MASK)); - if (env->hflags & HF_CS64_MASK) { - *cs_base = 0; - *pc = env->eip; - } else { - *cs_base = env->segs[R_CS].base; - *pc = (uint32_t)(*cs_base + env->eip); - } -} - void do_cpu_init(X86CPU *cpu); #define MCE_INJECT_BROADCAST 1 diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index f3f0380e70..bb6f82befb 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -24,6 +24,7 @@ #include "accel/accel-cpu-target.h" #include "exec/translation-block.h" #include "exec/target_page.h" +#include "accel/tcg/cpu-ops.h" #include "tcg-cpu.h" /* Frob eflags into and out of the CPU temporary format. */ @@ -47,6 +48,20 @@ static void x86_cpu_exec_exit(CPUState *cs) env->eflags = cpu_compute_eflags(env); } +void cpu_get_tb_cpu_state(CPUX86State *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) +{ + *flags = env->hflags | + (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK | AC_MASK)); + if (env->hflags & HF_CS64_MASK) { + *cs_base = 0; + *pc = env->eip; + } else { + *cs_base = env->segs[R_CS].base; + *pc = (uint32_t)(*cs_base + env->eip); + } +} + static void x86_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { @@ -131,8 +146,6 @@ static void x86_cpu_exec_reset(CPUState *cs) } #endif -#include "accel/tcg/cpu-ops.h" - const TCGCPUOps x86_tcg_ops = { .mttcg_supported = true, .precise_smc = true, diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index c64cba72dd..be770b7e19 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -29,6 +29,7 @@ #endif #ifdef CONFIG_TCG #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/cpu-ops.h" #include "tcg/tcg.h" #endif #include "tcg/tcg_loongarch.h" @@ -335,6 +336,18 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } #endif +void cpu_get_tb_cpu_state(CPULoongArchState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) +{ + *pc = env->pc; + *cs_base = 0; + *flags = env->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK); + *flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, FPE) * HW_FLAGS_EUEN_FPE; + *flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE) * HW_FLAGS_EUEN_SXE; + *flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE) * HW_FLAGS_EUEN_ASXE; + *flags |= is_va32(env) * HW_FLAGS_VA32; +} + static void loongarch_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { @@ -861,8 +874,6 @@ static void loongarch_cpu_dump_state(CPUState *cs, FILE *f, int flags) } #ifdef CONFIG_TCG -#include "accel/tcg/cpu-ops.h" - static const TCGCPUOps loongarch_tcg_ops = { .guest_default_memory_order = 0, .mttcg_supported = true, diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 70ff56e60c..262bf87f7b 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -492,18 +492,6 @@ static inline void set_pc(CPULoongArchState *env, uint64_t value) #define HW_FLAGS_VA32 0x20 #define HW_FLAGS_EUEN_ASXE 0x40 -static inline void cpu_get_tb_cpu_state(CPULoongArchState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->pc; - *cs_base = 0; - *flags = env->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK); - *flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, FPE) * HW_FLAGS_EUEN_FPE; - *flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE) * HW_FLAGS_EUEN_SXE; - *flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE) * HW_FLAGS_EUEN_ASXE; - *flags |= is_va32(env) * HW_FLAGS_VA32; -} - #define CPU_RESOLVING_TYPE TYPE_LOONGARCH_CPU void loongarch_cpu_post_init(Object *obj); diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index f446c6c8f7..2b4ec40509 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -23,6 +23,7 @@ #include "cpu.h" #include "migration/vmstate.h" #include "fpu/softfloat.h" +#include "accel/tcg/cpu-ops.h" static void m68k_cpu_set_pc(CPUState *cs, vaddr value) { @@ -38,6 +39,22 @@ static vaddr m68k_cpu_get_pc(CPUState *cs) return cpu->env.pc; } +void cpu_get_tb_cpu_state(CPUM68KState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) +{ + *pc = env->pc; + *cs_base = 0; + *flags = (env->macsr >> 4) & TB_FLAGS_MACSR; + if (env->sr & SR_S) { + *flags |= TB_FLAGS_MSR_S; + *flags |= (env->sfc << (TB_FLAGS_SFC_S_BIT - 2)) & TB_FLAGS_SFC_S; + *flags |= (env->dfc << (TB_FLAGS_DFC_S_BIT - 2)) & TB_FLAGS_DFC_S; + } + if (M68K_SR_TRACE(env->sr) == M68K_SR_TRACE_ANY_INS) { + *flags |= TB_FLAGS_TRACE; + } +} + static void m68k_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) @@ -586,8 +603,6 @@ static const struct SysemuCPUOps m68k_sysemu_ops = { }; #endif /* !CONFIG_USER_ONLY */ -#include "accel/tcg/cpu-ops.h" - static const TCGCPUOps m68k_tcg_ops = { /* MTTCG not yet supported: require strict ordering */ .guest_default_memory_order = TCG_MO_ALL, diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 39d0b9d6d7..d9db6a486a 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -605,22 +605,6 @@ void m68k_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, #define TB_FLAGS_TRACE 16 #define TB_FLAGS_TRACE_BIT (1 << TB_FLAGS_TRACE) -static inline void cpu_get_tb_cpu_state(CPUM68KState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->pc; - *cs_base = 0; - *flags = (env->macsr >> 4) & TB_FLAGS_MACSR; - if (env->sr & SR_S) { - *flags |= TB_FLAGS_MSR_S; - *flags |= (env->sfc << (TB_FLAGS_SFC_S_BIT - 2)) & TB_FLAGS_SFC_S; - *flags |= (env->dfc << (TB_FLAGS_DFC_S_BIT - 2)) & TB_FLAGS_DFC_S; - } - if (M68K_SR_TRACE(env->sr) == M68K_SR_TRACE_ANY_INS) { - *flags |= TB_FLAGS_TRACE; - } -} - void dump_mmu(CPUM68KState *env); #endif diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index f305ed04f6..105ede0b1e 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -31,6 +31,7 @@ #include "exec/gdbstub.h" #include "exec/translation-block.h" #include "fpu/softfloat-helpers.h" +#include "accel/tcg/cpu-ops.h" #include "tcg/tcg.h" static const struct { @@ -94,6 +95,14 @@ static vaddr mb_cpu_get_pc(CPUState *cs) return cpu->env.pc; } +void cpu_get_tb_cpu_state(CPUMBState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) +{ + *pc = env->pc; + *flags = (env->iflags & IFLAGS_TB_MASK) | (env->msr & MSR_TB_MASK); + *cs_base = (*flags & IMM_FLAG ? env->imm : 0); +} + static void mb_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { @@ -423,8 +432,6 @@ static const struct SysemuCPUOps mb_sysemu_ops = { }; #endif -#include "accel/tcg/cpu-ops.h" - static const TCGCPUOps mb_tcg_ops = { /* MicroBlaze is always in-order. */ .guest_default_memory_order = TCG_MO_ALL, diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index d511f22a55..6ad8643f2e 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -419,14 +419,6 @@ static inline bool mb_cpu_is_big_endian(CPUState *cs) return !cpu->cfg.endi; } -static inline void cpu_get_tb_cpu_state(CPUMBState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->pc; - *flags = (env->iflags & IFLAGS_TB_MASK) | (env->msr & MSR_TB_MASK); - *cs_base = (*flags & IMM_FLAG ? env->imm : 0); -} - #if !defined(CONFIG_USER_ONLY) bool mb_cpu_tlb_fill(CPUState *cs, vaddr address, int size, MMUAccessType access_type, int mmu_idx, diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 09ed330027..ab00adf86b 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -549,6 +549,15 @@ static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) return mips_env_mmu_index(cpu_env(cs)); } +void cpu_get_tb_cpu_state(CPUMIPSState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) +{ + *pc = env->active_tc.PC; + *cs_base = 0; + *flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK | + MIPS_HFLAG_HWRENA_ULR); +} + static const TCGCPUOps mips_tcg_ops = { .mttcg_supported = TARGET_LONG_BITS == 32, .guest_default_memory_order = 0, diff --git a/target/mips/cpu.h b/target/mips/cpu.h index d16f9a7220..5cd4c6c818 100644 --- a/target/mips/cpu.h +++ b/target/mips/cpu.h @@ -1366,15 +1366,6 @@ void cpu_mips_clock_init(MIPSCPU *cpu); /* helper.c */ target_ulong exception_resume_pc(CPUMIPSState *env); -static inline void cpu_get_tb_cpu_state(CPUMIPSState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->active_tc.PC; - *cs_base = 0; - *flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK | - MIPS_HFLAG_HWRENA_ULR); -} - /** * mips_cpu_create_with_clock: * @typename: a MIPS CPU type. diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 94776e0ad8..d798127d67 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -23,6 +23,7 @@ #include "cpu.h" #include "exec/translation-block.h" #include "fpu/softfloat-helpers.h" +#include "accel/tcg/cpu-ops.h" #include "tcg/tcg.h" static void openrisc_cpu_set_pc(CPUState *cs, vaddr value) @@ -40,6 +41,16 @@ static vaddr openrisc_cpu_get_pc(CPUState *cs) return cpu->env.pc; } +void cpu_get_tb_cpu_state(CPUOpenRISCState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) +{ + *pc = env->pc; + *cs_base = 0; + *flags = (env->dflag ? TB_FLAGS_DFLAG : 0) + | (cpu_get_gpr(env, 0) ? 0 : TB_FLAGS_R0_0) + | (env->sr & (SR_SM | SR_DME | SR_IME | SR_OVE)); +} + static void openrisc_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { @@ -239,8 +250,6 @@ static const struct SysemuCPUOps openrisc_sysemu_ops = { }; #endif -#include "accel/tcg/cpu-ops.h" - static const TCGCPUOps openrisc_tcg_ops = { .guest_default_memory_order = 0, .mttcg_supported = true, diff --git a/target/openrisc/cpu.h b/target/openrisc/cpu.h index 569819bfb0..f4bcf00b07 100644 --- a/target/openrisc/cpu.h +++ b/target/openrisc/cpu.h @@ -349,16 +349,6 @@ static inline void cpu_set_gpr(CPUOpenRISCState *env, int i, uint32_t val) env->shadow_gpr[0][i] = val; } -static inline void cpu_get_tb_cpu_state(CPUOpenRISCState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->pc; - *cs_base = 0; - *flags = (env->dflag ? TB_FLAGS_DFLAG : 0) - | (cpu_get_gpr(env, 0) ? 0 : TB_FLAGS_R0_0) - | (env->sr & (SR_SM | SR_DME | SR_IME | SR_OVE)); -} - static inline uint32_t cpu_get_sr(const CPUOpenRISCState *env) { return (env->sr diff --git a/target/ppc/cpu.h b/target/ppc/cpu.h index 13115a89ff..6b90543811 100644 --- a/target/ppc/cpu.h +++ b/target/ppc/cpu.h @@ -2751,19 +2751,6 @@ void cpu_write_xer(CPUPPCState *env, target_ulong xer); */ #define is_book3s_arch2x(ctx) (!!((ctx)->insns_flags & PPC_SEGMENT_64B)) -#ifdef CONFIG_DEBUG_TCG -void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags); -#else -static inline void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->nip; - *cs_base = 0; - *flags = env->hflags; -} -#endif - G_NORETURN void raise_exception_err_ra(CPUPPCState *env, uint32_t exception, uint32_t error_code, uintptr_t raddr); diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index f211bc9830..8d248bcbb9 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -27,6 +27,7 @@ #include "power8-pmu.h" #include "cpu-models.h" #include "spr_common.h" +#include "accel/tcg/cpu-ops.h" /* Swap temporary saved registers with GPRs */ void hreg_swap_gpr_tgpr(CPUPPCState *env) @@ -255,26 +256,25 @@ void hreg_update_pmu_hflags(CPUPPCState *env) env->hflags |= hreg_compute_pmu_hflags_value(env); } -#ifdef CONFIG_DEBUG_TCG void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc, uint64_t *cs_base, uint32_t *flags) { uint32_t hflags_current = env->hflags; - uint32_t hflags_rebuilt; - - *pc = env->nip; - *cs_base = 0; - *flags = hflags_current; - hflags_rebuilt = hreg_compute_hflags_value(env); +#ifdef CONFIG_DEBUG_TCG + uint32_t hflags_rebuilt = hreg_compute_hflags_value(env); if (unlikely(hflags_current != hflags_rebuilt)) { cpu_abort(env_cpu(env), "TCG hflags mismatch (current:0x%08x rebuilt:0x%08x)\n", hflags_current, hflags_rebuilt); } -} #endif + *pc = env->nip; + *cs_base = 0; + *flags = hflags_current; +} + void cpu_interrupt_exittb(CPUState *cs) { /* diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 167909c89b..c66ac3bc27 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -802,9 +802,6 @@ static inline uint32_t vext_get_vlmax(uint32_t vlenb, uint32_t vsew, return vlen >> (vsew + 3 - lmul); } -void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags); - bool riscv_cpu_is_32bit(RISCVCPU *cpu); bool riscv_cpu_virt_mem_enabled(CPURISCVState *env); diff --git a/target/rx/cpu.c b/target/rx/cpu.c index de2e6a22ff..e8b47be675 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -28,6 +28,7 @@ #include "hw/loader.h" #include "fpu/softfloat.h" #include "tcg/debug-assert.h" +#include "accel/tcg/cpu-ops.h" static void rx_cpu_set_pc(CPUState *cs, vaddr value) { @@ -43,6 +44,15 @@ static vaddr rx_cpu_get_pc(CPUState *cs) return cpu->env.pc; } +void cpu_get_tb_cpu_state(CPURXState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) +{ + *pc = env->pc; + *cs_base = 0; + *flags = FIELD_DP32(0, PSW, PM, env->psw_pm); + *flags = FIELD_DP32(*flags, PSW, U, env->psw_u); +} + static void rx_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { @@ -201,8 +211,6 @@ static const struct SysemuCPUOps rx_sysemu_ops = { .get_phys_page_debug = rx_cpu_get_phys_page_debug, }; -#include "accel/tcg/cpu-ops.h" - static const TCGCPUOps rx_tcg_ops = { /* MTTCG not yet supported: require strict ordering */ .guest_default_memory_order = TCG_MO_ALL, diff --git a/target/rx/cpu.h b/target/rx/cpu.h index 5c19c83219..ba5761b647 100644 --- a/target/rx/cpu.h +++ b/target/rx/cpu.h @@ -153,15 +153,6 @@ void rx_cpu_unpack_psw(CPURXState *env, uint32_t psw, int rte); #define RX_CPU_IRQ 0 #define RX_CPU_FIR 1 -static inline void cpu_get_tb_cpu_state(CPURXState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->pc; - *cs_base = 0; - *flags = FIELD_DP32(0, PSW, PM, env->psw_pm); - *flags = FIELD_DP32(*flags, PSW, U, env->psw_u); -} - static inline uint32_t rx_cpu_pack_psw(CPURXState *env) { uint32_t psw = 0; diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 71338aae77..435b2034ff 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -302,6 +302,7 @@ static const Property s390x_cpu_properties[] = { #ifdef CONFIG_TCG #include "accel/tcg/cpu-ops.h" +#include "tcg/tcg_s390x.h" static int s390x_cpu_mmu_index(CPUState *cs, bool ifetch) { diff --git a/target/s390x/cpu.h b/target/s390x/cpu.h index 530d97ccf1..aa931cb674 100644 --- a/target/s390x/cpu.h +++ b/target/s390x/cpu.h @@ -411,15 +411,6 @@ static inline int s390x_env_mmu_index(CPUS390XState *env, bool ifetch) #endif } -#ifdef CONFIG_TCG - -#include "tcg/tcg_s390x.h" - -void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags); - -#endif /* CONFIG_TCG */ - /* PER bits from control register 9 */ #define PER_CR9_EVENT_BRANCH 0x80000000 #define PER_CR9_EVENT_IFETCH 0x40000000 diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 681237c511..5fb18bf55e 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -26,6 +26,7 @@ #include "migration/vmstate.h" #include "exec/translation-block.h" #include "fpu/softfloat-helpers.h" +#include "accel/tcg/cpu-ops.h" #include "tcg/tcg.h" static void superh_cpu_set_pc(CPUState *cs, vaddr value) @@ -42,6 +43,21 @@ static vaddr superh_cpu_get_pc(CPUState *cs) return cpu->env.pc; } +void cpu_get_tb_cpu_state(CPUSH4State *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) +{ + *pc = env->pc; + /* For a gUSA region, notice the end of the region. */ + *cs_base = env->flags & TB_FLAG_GUSA_MASK ? env->gregs[0] : 0; + *flags = env->flags + | (env->fpscr & TB_FLAG_FPSCR_MASK) + | (env->sr & TB_FLAG_SR_MASK) + | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 3 */ +#ifdef CONFIG_USER_ONLY + *flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; +#endif +} + static void superh_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { @@ -258,8 +274,6 @@ static const struct SysemuCPUOps sh4_sysemu_ops = { }; #endif -#include "accel/tcg/cpu-ops.h" - static const TCGCPUOps superh_tcg_ops = { /* MTTCG not yet supported: require strict ordering */ .guest_default_memory_order = TCG_MO_ALL, diff --git a/target/sh4/cpu.h b/target/sh4/cpu.h index 906f99ddf0..c41ab70dd7 100644 --- a/target/sh4/cpu.h +++ b/target/sh4/cpu.h @@ -380,19 +380,4 @@ static inline void cpu_write_sr(CPUSH4State *env, target_ulong sr) env->sr = sr & ~((1u << SR_M) | (1u << SR_Q) | (1u << SR_T)); } -static inline void cpu_get_tb_cpu_state(CPUSH4State *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->pc; - /* For a gUSA region, notice the end of the region. */ - *cs_base = env->flags & TB_FLAG_GUSA_MASK ? env->gregs[0] : 0; - *flags = env->flags - | (env->fpscr & TB_FLAG_FPSCR_MASK) - | (env->sr & TB_FLAG_SR_MASK) - | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 3 */ -#ifdef CONFIG_USER_ONLY - *flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; -#endif -} - #endif /* SH4_CPU_H */ diff --git a/target/sparc/cpu.h b/target/sparc/cpu.h index 37fd1e066e..31cb3d97eb 100644 --- a/target/sparc/cpu.h +++ b/target/sparc/cpu.h @@ -741,9 +741,6 @@ trap_state* cpu_tsptr(CPUSPARCState* env); #define TB_FLAG_FSR_QNE (1 << 8) #define TB_FLAG_ASI_SHIFT 24 -void cpu_get_tb_cpu_state(CPUSPARCState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags); - static inline bool tb_fpu_enabled(int tb_flags) { #if defined(CONFIG_USER_ONLY) diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 0fcac697f6..81b3bb6362 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -23,6 +23,7 @@ #include "exec/translation-block.h" #include "qemu/error-report.h" #include "tcg/debug-assert.h" +#include "accel/tcg/cpu-ops.h" static inline void set_feature(CPUTriCoreState *env, int feature) { @@ -44,6 +45,18 @@ static vaddr tricore_cpu_get_pc(CPUState *cs) return cpu_env(cs)->PC; } +void cpu_get_tb_cpu_state(CPUTriCoreState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) +{ + uint32_t new_flags = 0; + *pc = env->PC; + *cs_base = 0; + + new_flags |= FIELD_DP32(new_flags, TB_FLAGS, PRIV, + extract32(env->PSW, 10, 2)); + *flags = new_flags; +} + static void tricore_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { @@ -168,8 +181,6 @@ static const struct SysemuCPUOps tricore_sysemu_ops = { .get_phys_page_debug = tricore_cpu_get_phys_page_debug, }; -#include "accel/tcg/cpu-ops.h" - static const TCGCPUOps tricore_tcg_ops = { /* MTTCG not yet supported: require strict ordering */ .guest_default_memory_order = TCG_MO_ALL, diff --git a/target/tricore/cpu.h b/target/tricore/cpu.h index c76e65f818..82085fbc32 100644 --- a/target/tricore/cpu.h +++ b/target/tricore/cpu.h @@ -258,18 +258,6 @@ void tricore_tcg_init(void); void tricore_translate_code(CPUState *cs, TranslationBlock *tb, int *max_insns, vaddr pc, void *host_pc); -static inline void cpu_get_tb_cpu_state(CPUTriCoreState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - uint32_t new_flags = 0; - *pc = env->PC; - *cs_base = 0; - - new_flags |= FIELD_DP32(new_flags, TB_FLAGS, PRIV, - extract32(env->PSW, 10, 2)); - *flags = new_flags; -} - #define CPU_RESOLVING_TYPE TYPE_TRICORE_CPU /* helpers.c */ diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 9dcb883208..c78ef9421c 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -35,6 +35,7 @@ #include "qemu/module.h" #include "migration/vmstate.h" #include "hw/qdev-clock.h" +#include "accel/tcg/cpu-ops.h" #ifndef CONFIG_USER_ONLY #include "system/memory.h" #endif @@ -54,6 +55,74 @@ static vaddr xtensa_cpu_get_pc(CPUState *cs) return cpu->env.pc; } +void cpu_get_tb_cpu_state(CPUXtensaState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *flags) +{ + *pc = env->pc; + *cs_base = 0; + *flags = 0; + *flags |= xtensa_get_ring(env); + if (env->sregs[PS] & PS_EXCM) { + *flags |= XTENSA_TBFLAG_EXCM; + } else if (xtensa_option_enabled(env->config, XTENSA_OPTION_LOOP)) { + target_ulong lend_dist = + env->sregs[LEND] - (env->pc & -(1u << TARGET_PAGE_BITS)); + + /* + * 0 in the csbase_lend field means that there may not be a loopback + * for any instruction that starts inside this page. Any other value + * means that an instruction that ends at this offset from the page + * start may loop back and will need loopback code to be generated. + * + * lend_dist is 0 when LEND points to the start of the page, but + * no instruction that starts inside this page may end at offset 0, + * so it's still correct. + * + * When an instruction ends at a page boundary it may only start in + * the previous page. lend_dist will be encoded as TARGET_PAGE_SIZE + * for the TB that contains this instruction. + */ + if (lend_dist < (1u << TARGET_PAGE_BITS) + env->config->max_insn_size) { + target_ulong lbeg_off = env->sregs[LEND] - env->sregs[LBEG]; + + *cs_base = lend_dist; + if (lbeg_off < 256) { + *cs_base |= lbeg_off << XTENSA_CSBASE_LBEG_OFF_SHIFT; + } + } + } + if (xtensa_option_enabled(env->config, XTENSA_OPTION_EXTENDED_L32R) && + (env->sregs[LITBASE] & 1)) { + *flags |= XTENSA_TBFLAG_LITBASE; + } + if (xtensa_option_enabled(env->config, XTENSA_OPTION_DEBUG)) { + if (xtensa_get_cintlevel(env) < env->config->debug_level) { + *flags |= XTENSA_TBFLAG_DEBUG; + } + if (xtensa_get_cintlevel(env) < env->sregs[ICOUNTLEVEL]) { + *flags |= XTENSA_TBFLAG_ICOUNT; + } + } + if (xtensa_option_enabled(env->config, XTENSA_OPTION_COPROCESSOR)) { + *flags |= env->sregs[CPENABLE] << XTENSA_TBFLAG_CPENABLE_SHIFT; + } + if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER) && + (env->sregs[PS] & (PS_WOE | PS_EXCM)) == PS_WOE) { + uint32_t windowstart = xtensa_replicate_windowstart(env) >> + (env->sregs[WINDOW_BASE] + 1); + uint32_t w = ctz32(windowstart | 0x8); + + *flags |= (w << XTENSA_TBFLAG_WINDOW_SHIFT) | XTENSA_TBFLAG_CWOE; + *flags |= extract32(env->sregs[PS], PS_CALLINC_SHIFT, + PS_CALLINC_LEN) << XTENSA_TBFLAG_CALLINC_SHIFT; + } else { + *flags |= 3 << XTENSA_TBFLAG_WINDOW_SHIFT; + } + if (env->yield_needed) { + *flags |= XTENSA_TBFLAG_YIELD; + } +} + static void xtensa_restore_state_to_opc(CPUState *cs, const TranslationBlock *tb, const uint64_t *data) @@ -229,8 +298,6 @@ static const struct SysemuCPUOps xtensa_sysemu_ops = { }; #endif -#include "accel/tcg/cpu-ops.h" - static const TCGCPUOps xtensa_tcg_ops = { /* Xtensa processors have a weak memory model */ .guest_default_memory_order = 0, diff --git a/target/xtensa/cpu.h b/target/xtensa/cpu.h index c03ed71c94..74122ebe15 100644 --- a/target/xtensa/cpu.h +++ b/target/xtensa/cpu.h @@ -733,74 +733,6 @@ static inline uint32_t xtensa_replicate_windowstart(CPUXtensaState *env) #define XTENSA_CSBASE_LBEG_OFF_MASK 0x00ff0000 #define XTENSA_CSBASE_LBEG_OFF_SHIFT 16 -static inline void cpu_get_tb_cpu_state(CPUXtensaState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->pc; - *cs_base = 0; - *flags = 0; - *flags |= xtensa_get_ring(env); - if (env->sregs[PS] & PS_EXCM) { - *flags |= XTENSA_TBFLAG_EXCM; - } else if (xtensa_option_enabled(env->config, XTENSA_OPTION_LOOP)) { - target_ulong lend_dist = - env->sregs[LEND] - (env->pc & -(1u << TARGET_PAGE_BITS)); - - /* - * 0 in the csbase_lend field means that there may not be a loopback - * for any instruction that starts inside this page. Any other value - * means that an instruction that ends at this offset from the page - * start may loop back and will need loopback code to be generated. - * - * lend_dist is 0 when LEND points to the start of the page, but - * no instruction that starts inside this page may end at offset 0, - * so it's still correct. - * - * When an instruction ends at a page boundary it may only start in - * the previous page. lend_dist will be encoded as TARGET_PAGE_SIZE - * for the TB that contains this instruction. - */ - if (lend_dist < (1u << TARGET_PAGE_BITS) + env->config->max_insn_size) { - target_ulong lbeg_off = env->sregs[LEND] - env->sregs[LBEG]; - - *cs_base = lend_dist; - if (lbeg_off < 256) { - *cs_base |= lbeg_off << XTENSA_CSBASE_LBEG_OFF_SHIFT; - } - } - } - if (xtensa_option_enabled(env->config, XTENSA_OPTION_EXTENDED_L32R) && - (env->sregs[LITBASE] & 1)) { - *flags |= XTENSA_TBFLAG_LITBASE; - } - if (xtensa_option_enabled(env->config, XTENSA_OPTION_DEBUG)) { - if (xtensa_get_cintlevel(env) < env->config->debug_level) { - *flags |= XTENSA_TBFLAG_DEBUG; - } - if (xtensa_get_cintlevel(env) < env->sregs[ICOUNTLEVEL]) { - *flags |= XTENSA_TBFLAG_ICOUNT; - } - } - if (xtensa_option_enabled(env->config, XTENSA_OPTION_COPROCESSOR)) { - *flags |= env->sregs[CPENABLE] << XTENSA_TBFLAG_CPENABLE_SHIFT; - } - if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER) && - (env->sregs[PS] & (PS_WOE | PS_EXCM)) == PS_WOE) { - uint32_t windowstart = xtensa_replicate_windowstart(env) >> - (env->sregs[WINDOW_BASE] + 1); - uint32_t w = ctz32(windowstart | 0x8); - - *flags |= (w << XTENSA_TBFLAG_WINDOW_SHIFT) | XTENSA_TBFLAG_CWOE; - *flags |= extract32(env->sregs[PS], PS_CALLINC_SHIFT, - PS_CALLINC_LEN) << XTENSA_TBFLAG_CALLINC_SHIFT; - } else { - *flags |= 3 << XTENSA_TBFLAG_WINDOW_SHIFT; - } - if (env->yield_needed) { - *flags |= XTENSA_TBFLAG_YIELD; - } -} - XtensaCPU *xtensa_cpu_create_with_clock(const char *cpu_type, Clock *cpu_refclk); From b6aeb8d243c5ab8b914b55f0036e8289a99322c8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Apr 2025 11:35:26 -0700 Subject: [PATCH 0557/2760] target/arm: Move cpu_get_tb_cpu_state to hflags.c This is a tcg-specific function, so move it to a tcg file. Also move mve_no_pred, a static function only used within cpu_get_tb_cpu_state. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/arm/helper.c | 110 ---------------------------------------- target/arm/tcg/hflags.c | 110 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 110 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 98adeb7086..360e6ac0f5 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -30,7 +30,6 @@ #include "qemu/guest-random.h" #ifdef CONFIG_TCG #include "accel/tcg/probe.h" -#include "accel/tcg/cpu-ops.h" #include "semihosting/common-semi.h" #endif #include "cpregs.h" @@ -11424,115 +11423,6 @@ ARMMMUIdx arm_mmu_idx(CPUARMState *env) return arm_mmu_idx_el(env, arm_current_el(env)); } -static bool mve_no_pred(CPUARMState *env) -{ - /* - * Return true if there is definitely no predication of MVE - * instructions by VPR or LTPSIZE. (Returning false even if there - * isn't any predication is OK; generated code will just be - * a little worse.) - * If the CPU does not implement MVE then this TB flag is always 0. - * - * NOTE: if you change this logic, the "recalculate s->mve_no_pred" - * logic in gen_update_fp_context() needs to be updated to match. - * - * We do not include the effect of the ECI bits here -- they are - * tracked in other TB flags. This simplifies the logic for - * "when did we emit code that changes the MVE_NO_PRED TB flag - * and thus need to end the TB?". - */ - if (cpu_isar_feature(aa32_mve, env_archcpu(env))) { - return false; - } - if (env->v7m.vpr) { - return false; - } - if (env->v7m.ltpsize < 4) { - return false; - } - return true; -} - -void cpu_get_tb_cpu_state(CPUARMState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) -{ - CPUARMTBFlags flags; - - assert_hflags_rebuild_correctly(env); - flags = env->hflags; - - if (EX_TBFLAG_ANY(flags, AARCH64_STATE)) { - *pc = env->pc; - if (cpu_isar_feature(aa64_bti, env_archcpu(env))) { - DP_TBFLAG_A64(flags, BTYPE, env->btype); - } - } else { - *pc = env->regs[15]; - - if (arm_feature(env, ARM_FEATURE_M)) { - if (arm_feature(env, ARM_FEATURE_M_SECURITY) && - FIELD_EX32(env->v7m.fpccr[M_REG_S], V7M_FPCCR, S) - != env->v7m.secure) { - DP_TBFLAG_M32(flags, FPCCR_S_WRONG, 1); - } - - if ((env->v7m.fpccr[env->v7m.secure] & R_V7M_FPCCR_ASPEN_MASK) && - (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) || - (env->v7m.secure && - !(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)))) { - /* - * ASPEN is set, but FPCA/SFPA indicate that there is no - * active FP context; we must create a new FP context before - * executing any FP insn. - */ - DP_TBFLAG_M32(flags, NEW_FP_CTXT_NEEDED, 1); - } - - bool is_secure = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; - if (env->v7m.fpccr[is_secure] & R_V7M_FPCCR_LSPACT_MASK) { - DP_TBFLAG_M32(flags, LSPACT, 1); - } - - if (mve_no_pred(env)) { - DP_TBFLAG_M32(flags, MVE_NO_PRED, 1); - } - } else { - /* - * Note that XSCALE_CPAR shares bits with VECSTRIDE. - * Note that VECLEN+VECSTRIDE are RES0 for M-profile. - */ - if (arm_feature(env, ARM_FEATURE_XSCALE)) { - DP_TBFLAG_A32(flags, XSCALE_CPAR, env->cp15.c15_cpar); - } else { - DP_TBFLAG_A32(flags, VECLEN, env->vfp.vec_len); - DP_TBFLAG_A32(flags, VECSTRIDE, env->vfp.vec_stride); - } - if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) { - DP_TBFLAG_A32(flags, VFPEN, 1); - } - } - - DP_TBFLAG_AM32(flags, THUMB, env->thumb); - DP_TBFLAG_AM32(flags, CONDEXEC, env->condexec_bits); - } - - /* - * The SS_ACTIVE and PSTATE_SS bits correspond to the state machine - * states defined in the ARM ARM for software singlestep: - * SS_ACTIVE PSTATE.SS State - * 0 x Inactive (the TB flag for SS is always 0) - * 1 0 Active-pending - * 1 1 Active-not-pending - * SS_ACTIVE is set in hflags; PSTATE__SS is computed every TB. - */ - if (EX_TBFLAG_ANY(flags, SS_ACTIVE) && (env->pstate & PSTATE_SS)) { - DP_TBFLAG_ANY(flags, PSTATE__SS, 1); - } - - *pflags = flags.flags; - *cs_base = flags.flags2; -} - #ifdef TARGET_AARCH64 /* * The manual says that when SVE is enabled and VQ is widened the diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index e51d9f7b15..e530f65ed7 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -10,6 +10,7 @@ #include "internals.h" #include "cpu-features.h" #include "exec/helper-proto.h" +#include "accel/tcg/cpu-ops.h" #include "cpregs.h" static inline bool fgt_svc(CPUARMState *env, int el) @@ -513,3 +514,112 @@ void assert_hflags_rebuild_correctly(CPUARMState *env) } #endif } + +static bool mve_no_pred(CPUARMState *env) +{ + /* + * Return true if there is definitely no predication of MVE + * instructions by VPR or LTPSIZE. (Returning false even if there + * isn't any predication is OK; generated code will just be + * a little worse.) + * If the CPU does not implement MVE then this TB flag is always 0. + * + * NOTE: if you change this logic, the "recalculate s->mve_no_pred" + * logic in gen_update_fp_context() needs to be updated to match. + * + * We do not include the effect of the ECI bits here -- they are + * tracked in other TB flags. This simplifies the logic for + * "when did we emit code that changes the MVE_NO_PRED TB flag + * and thus need to end the TB?". + */ + if (cpu_isar_feature(aa32_mve, env_archcpu(env))) { + return false; + } + if (env->v7m.vpr) { + return false; + } + if (env->v7m.ltpsize < 4) { + return false; + } + return true; +} + +void cpu_get_tb_cpu_state(CPUARMState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) +{ + CPUARMTBFlags flags; + + assert_hflags_rebuild_correctly(env); + flags = env->hflags; + + if (EX_TBFLAG_ANY(flags, AARCH64_STATE)) { + *pc = env->pc; + if (cpu_isar_feature(aa64_bti, env_archcpu(env))) { + DP_TBFLAG_A64(flags, BTYPE, env->btype); + } + } else { + *pc = env->regs[15]; + + if (arm_feature(env, ARM_FEATURE_M)) { + if (arm_feature(env, ARM_FEATURE_M_SECURITY) && + FIELD_EX32(env->v7m.fpccr[M_REG_S], V7M_FPCCR, S) + != env->v7m.secure) { + DP_TBFLAG_M32(flags, FPCCR_S_WRONG, 1); + } + + if ((env->v7m.fpccr[env->v7m.secure] & R_V7M_FPCCR_ASPEN_MASK) && + (!(env->v7m.control[M_REG_S] & R_V7M_CONTROL_FPCA_MASK) || + (env->v7m.secure && + !(env->v7m.control[M_REG_S] & R_V7M_CONTROL_SFPA_MASK)))) { + /* + * ASPEN is set, but FPCA/SFPA indicate that there is no + * active FP context; we must create a new FP context before + * executing any FP insn. + */ + DP_TBFLAG_M32(flags, NEW_FP_CTXT_NEEDED, 1); + } + + bool is_secure = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; + if (env->v7m.fpccr[is_secure] & R_V7M_FPCCR_LSPACT_MASK) { + DP_TBFLAG_M32(flags, LSPACT, 1); + } + + if (mve_no_pred(env)) { + DP_TBFLAG_M32(flags, MVE_NO_PRED, 1); + } + } else { + /* + * Note that XSCALE_CPAR shares bits with VECSTRIDE. + * Note that VECLEN+VECSTRIDE are RES0 for M-profile. + */ + if (arm_feature(env, ARM_FEATURE_XSCALE)) { + DP_TBFLAG_A32(flags, XSCALE_CPAR, env->cp15.c15_cpar); + } else { + DP_TBFLAG_A32(flags, VECLEN, env->vfp.vec_len); + DP_TBFLAG_A32(flags, VECSTRIDE, env->vfp.vec_stride); + } + if (env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) { + DP_TBFLAG_A32(flags, VFPEN, 1); + } + } + + DP_TBFLAG_AM32(flags, THUMB, env->thumb); + DP_TBFLAG_AM32(flags, CONDEXEC, env->condexec_bits); + } + + /* + * The SS_ACTIVE and PSTATE_SS bits correspond to the state machine + * states defined in the ARM ARM for software singlestep: + * SS_ACTIVE PSTATE.SS State + * 0 x Inactive (the TB flag for SS is always 0) + * 1 0 Active-pending + * 1 1 Active-not-pending + * SS_ACTIVE is set in hflags; PSTATE__SS is computed every TB. + */ + if (EX_TBFLAG_ANY(flags, SS_ACTIVE) && (env->pstate & PSTATE_SS)) { + DP_TBFLAG_ANY(flags, PSTATE__SS, 1); + } + + *pflags = flags.flags; + *cs_base = flags.flags2; +} From 9da84372c4e9efedc5326e71358197930ee06445 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Apr 2025 11:37:24 -0700 Subject: [PATCH 0558/2760] target/arm: Unexport assert_hflags_rebuild_correctly This function is no longer used outside of hflags.c. We can remove the stub as well. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/arm/internals.h | 2 -- target/arm/tcg-stubs.c | 4 ---- target/arm/tcg/hflags.c | 2 +- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 4d3d84ffeb..382a4d1015 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1906,8 +1906,6 @@ static inline bool arm_fgt_active(CPUARMState *env, int el) (!arm_feature(env, ARM_FEATURE_EL3) || (env->cp15.scr_el3 & SCR_FGTEN)); } -void assert_hflags_rebuild_correctly(CPUARMState *env); - /* * Although the ARM implementation of hardware assisted debugging * allows for different breakpoints per-core, the current GDB diff --git a/target/arm/tcg-stubs.c b/target/arm/tcg-stubs.c index 93a15cad61..5e5166c049 100644 --- a/target/arm/tcg-stubs.c +++ b/target/arm/tcg-stubs.c @@ -21,10 +21,6 @@ void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome, { g_assert_not_reached(); } -/* Temporarily while cpu_get_tb_cpu_state() is still in common code */ -void assert_hflags_rebuild_correctly(CPUARMState *env) -{ -} /* TLBI insns are only used by TCG, so we don't need to do anything for KVM */ void define_tlb_insn_regs(ARMCPU *cpu) diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index e530f65ed7..5315264c28 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -499,7 +499,7 @@ void HELPER(rebuild_hflags_a64)(CPUARMState *env, int el) env->hflags = rebuild_hflags_a64(env, el, fp_el, mmu_idx); } -void assert_hflags_rebuild_correctly(CPUARMState *env) +static void assert_hflags_rebuild_correctly(CPUARMState *env) { #ifdef CONFIG_DEBUG_TCG CPUARMTBFlags c = env->hflags; From 5b1c93be57ce6364eb7bbaaab6ecbf2b1d5d979e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Apr 2025 14:15:45 -0700 Subject: [PATCH 0559/2760] target/riscv: Move cpu_get_tb_cpu_state to tcg-cpu.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This function is only relevant to tcg. Move it to a tcg-specific file. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- target/riscv/cpu_helper.c | 97 ------------------------------------- target/riscv/tcg/tcg-cpu.c | 98 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+), 97 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index f2e90a9889..d5039f69a9 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -135,103 +135,6 @@ bool riscv_env_smode_dbltrp_enabled(CPURISCVState *env, bool virt) #endif } -void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) -{ - RISCVCPU *cpu = env_archcpu(env); - RISCVExtStatus fs, vs; - uint32_t flags = 0; - bool pm_signext = riscv_cpu_virt_mem_enabled(env); - - *pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc; - *cs_base = 0; - - if (cpu->cfg.ext_zve32x) { - /* - * If env->vl equals to VLMAX, we can use generic vector operation - * expanders (GVEC) to accerlate the vector operations. - * However, as LMUL could be a fractional number. The maximum - * vector size can be operated might be less than 8 bytes, - * which is not supported by GVEC. So we set vl_eq_vlmax flag to true - * only when maxsz >= 8 bytes. - */ - - /* lmul encoded as in DisasContext::lmul */ - int8_t lmul = sextract32(FIELD_EX64(env->vtype, VTYPE, VLMUL), 0, 3); - uint32_t vsew = FIELD_EX64(env->vtype, VTYPE, VSEW); - uint32_t vlmax = vext_get_vlmax(cpu->cfg.vlenb, vsew, lmul); - uint32_t maxsz = vlmax << vsew; - bool vl_eq_vlmax = (env->vstart == 0) && (vlmax == env->vl) && - (maxsz >= 8); - flags = FIELD_DP32(flags, TB_FLAGS, VILL, env->vill); - flags = FIELD_DP32(flags, TB_FLAGS, SEW, vsew); - flags = FIELD_DP32(flags, TB_FLAGS, LMUL, - FIELD_EX64(env->vtype, VTYPE, VLMUL)); - flags = FIELD_DP32(flags, TB_FLAGS, VL_EQ_VLMAX, vl_eq_vlmax); - flags = FIELD_DP32(flags, TB_FLAGS, VTA, - FIELD_EX64(env->vtype, VTYPE, VTA)); - flags = FIELD_DP32(flags, TB_FLAGS, VMA, - FIELD_EX64(env->vtype, VTYPE, VMA)); - flags = FIELD_DP32(flags, TB_FLAGS, VSTART_EQ_ZERO, env->vstart == 0); - } else { - flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1); - } - - if (cpu_get_fcfien(env)) { - /* - * For Forward CFI, only the expectation of a lpad at - * the start of the block is tracked via env->elp. env->elp - * is turned on during jalr translation. - */ - flags = FIELD_DP32(flags, TB_FLAGS, FCFI_LP_EXPECTED, env->elp); - flags = FIELD_DP32(flags, TB_FLAGS, FCFI_ENABLED, 1); - } - - if (cpu_get_bcfien(env)) { - flags = FIELD_DP32(flags, TB_FLAGS, BCFI_ENABLED, 1); - } - -#ifdef CONFIG_USER_ONLY - fs = EXT_STATUS_DIRTY; - vs = EXT_STATUS_DIRTY; -#else - flags = FIELD_DP32(flags, TB_FLAGS, PRIV, env->priv); - - flags |= riscv_env_mmu_index(env, 0); - fs = get_field(env->mstatus, MSTATUS_FS); - vs = get_field(env->mstatus, MSTATUS_VS); - - if (env->virt_enabled) { - flags = FIELD_DP32(flags, TB_FLAGS, VIRT_ENABLED, 1); - /* - * Merge DISABLED and !DIRTY states using MIN. - * We will set both fields when dirtying. - */ - fs = MIN(fs, get_field(env->mstatus_hs, MSTATUS_FS)); - vs = MIN(vs, get_field(env->mstatus_hs, MSTATUS_VS)); - } - - /* With Zfinx, floating point is enabled/disabled by Smstateen. */ - if (!riscv_has_ext(env, RVF)) { - fs = (smstateen_acc_ok(env, 0, SMSTATEEN0_FCSR) == RISCV_EXCP_NONE) - ? EXT_STATUS_DIRTY : EXT_STATUS_DISABLED; - } - - if (cpu->cfg.debug && !icount_enabled()) { - flags = FIELD_DP32(flags, TB_FLAGS, ITRIGGER, env->itrigger_enabled); - } -#endif - - flags = FIELD_DP32(flags, TB_FLAGS, FS, fs); - flags = FIELD_DP32(flags, TB_FLAGS, VS, vs); - flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl); - flags = FIELD_DP32(flags, TB_FLAGS, AXL, cpu_address_xl(env)); - flags = FIELD_DP32(flags, TB_FLAGS, PM_PMM, riscv_pm_get_pmm(env)); - flags = FIELD_DP32(flags, TB_FLAGS, PM_SIGNEXTEND, pm_signext); - - *pflags = flags; -} - RISCVPmPmm riscv_pm_get_pmm(CPURISCVState *env) { #ifndef CONFIG_USER_ONLY diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 50782e0f0e..e67de7dfe2 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -36,6 +36,7 @@ #ifndef CONFIG_USER_ONLY #include "hw/boards.h" #include "system/tcg.h" +#include "exec/icount.h" #endif /* Hash that stores user set extensions */ @@ -97,6 +98,103 @@ static int riscv_cpu_mmu_index(CPUState *cs, bool ifetch) return riscv_env_mmu_index(cpu_env(cs), ifetch); } +void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, + uint64_t *cs_base, uint32_t *pflags) +{ + RISCVCPU *cpu = env_archcpu(env); + RISCVExtStatus fs, vs; + uint32_t flags = 0; + bool pm_signext = riscv_cpu_virt_mem_enabled(env); + + *pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc; + *cs_base = 0; + + if (cpu->cfg.ext_zve32x) { + /* + * If env->vl equals to VLMAX, we can use generic vector operation + * expanders (GVEC) to accerlate the vector operations. + * However, as LMUL could be a fractional number. The maximum + * vector size can be operated might be less than 8 bytes, + * which is not supported by GVEC. So we set vl_eq_vlmax flag to true + * only when maxsz >= 8 bytes. + */ + + /* lmul encoded as in DisasContext::lmul */ + int8_t lmul = sextract32(FIELD_EX64(env->vtype, VTYPE, VLMUL), 0, 3); + uint32_t vsew = FIELD_EX64(env->vtype, VTYPE, VSEW); + uint32_t vlmax = vext_get_vlmax(cpu->cfg.vlenb, vsew, lmul); + uint32_t maxsz = vlmax << vsew; + bool vl_eq_vlmax = (env->vstart == 0) && (vlmax == env->vl) && + (maxsz >= 8); + flags = FIELD_DP32(flags, TB_FLAGS, VILL, env->vill); + flags = FIELD_DP32(flags, TB_FLAGS, SEW, vsew); + flags = FIELD_DP32(flags, TB_FLAGS, LMUL, + FIELD_EX64(env->vtype, VTYPE, VLMUL)); + flags = FIELD_DP32(flags, TB_FLAGS, VL_EQ_VLMAX, vl_eq_vlmax); + flags = FIELD_DP32(flags, TB_FLAGS, VTA, + FIELD_EX64(env->vtype, VTYPE, VTA)); + flags = FIELD_DP32(flags, TB_FLAGS, VMA, + FIELD_EX64(env->vtype, VTYPE, VMA)); + flags = FIELD_DP32(flags, TB_FLAGS, VSTART_EQ_ZERO, env->vstart == 0); + } else { + flags = FIELD_DP32(flags, TB_FLAGS, VILL, 1); + } + + if (cpu_get_fcfien(env)) { + /* + * For Forward CFI, only the expectation of a lpad at + * the start of the block is tracked via env->elp. env->elp + * is turned on during jalr translation. + */ + flags = FIELD_DP32(flags, TB_FLAGS, FCFI_LP_EXPECTED, env->elp); + flags = FIELD_DP32(flags, TB_FLAGS, FCFI_ENABLED, 1); + } + + if (cpu_get_bcfien(env)) { + flags = FIELD_DP32(flags, TB_FLAGS, BCFI_ENABLED, 1); + } + +#ifdef CONFIG_USER_ONLY + fs = EXT_STATUS_DIRTY; + vs = EXT_STATUS_DIRTY; +#else + flags = FIELD_DP32(flags, TB_FLAGS, PRIV, env->priv); + + flags |= riscv_env_mmu_index(env, 0); + fs = get_field(env->mstatus, MSTATUS_FS); + vs = get_field(env->mstatus, MSTATUS_VS); + + if (env->virt_enabled) { + flags = FIELD_DP32(flags, TB_FLAGS, VIRT_ENABLED, 1); + /* + * Merge DISABLED and !DIRTY states using MIN. + * We will set both fields when dirtying. + */ + fs = MIN(fs, get_field(env->mstatus_hs, MSTATUS_FS)); + vs = MIN(vs, get_field(env->mstatus_hs, MSTATUS_VS)); + } + + /* With Zfinx, floating point is enabled/disabled by Smstateen. */ + if (!riscv_has_ext(env, RVF)) { + fs = (smstateen_acc_ok(env, 0, SMSTATEEN0_FCSR) == RISCV_EXCP_NONE) + ? EXT_STATUS_DIRTY : EXT_STATUS_DISABLED; + } + + if (cpu->cfg.debug && !icount_enabled()) { + flags = FIELD_DP32(flags, TB_FLAGS, ITRIGGER, env->itrigger_enabled); + } +#endif + + flags = FIELD_DP32(flags, TB_FLAGS, FS, fs); + flags = FIELD_DP32(flags, TB_FLAGS, VS, vs); + flags = FIELD_DP32(flags, TB_FLAGS, XL, env->xl); + flags = FIELD_DP32(flags, TB_FLAGS, AXL, cpu_address_xl(env)); + flags = FIELD_DP32(flags, TB_FLAGS, PM_PMM, riscv_pm_get_pmm(env)); + flags = FIELD_DP32(flags, TB_FLAGS, PM_SIGNEXTEND, pm_signext); + + *pflags = flags; +} + static void riscv_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb) { From 4759aae43235cd00e1c9b67ff5bd920db89fddc5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Apr 2025 15:32:11 -0700 Subject: [PATCH 0560/2760] accel/tcg: Return TCGTBCPUState from cpu_get_tb_cpu_state Combine 3 different pointer returns into one structure return. Include a cflags field in TCGTBCPUState, not filled in by cpu_get_tb_cpu_state, but used by all callers. This fills a hole in the structure and is useful in some subroutines. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 56 +++++++++++++------------------- accel/tcg/translate-all.c | 8 ++--- include/accel/tcg/cpu-ops.h | 4 +-- include/accel/tcg/tb-cpu-state.h | 18 ++++++++++ target/alpha/cpu.c | 13 ++++---- target/arm/tcg/hflags.c | 17 ++++++---- target/avr/cpu.c | 9 ++--- target/hexagon/cpu.c | 15 +++++---- target/hppa/cpu.c | 10 +++--- target/i386/tcg/tcg-cpu.c | 19 +++++++---- target/loongarch/cpu.c | 22 +++++++------ target/m68k/cpu.c | 21 +++++++----- target/microblaze/cpu.c | 13 +++++--- target/mips/cpu.c | 14 ++++---- target/openrisc/cpu.c | 16 +++++---- target/ppc/helper_regs.c | 8 ++--- target/riscv/tcg/tcg-cpu.c | 12 +++---- target/rx/cpu.c | 14 ++++---- target/s390x/cpu.c | 14 ++++---- target/sh4/cpu.c | 22 +++++++++---- target/sparc/cpu.c | 17 ++++++---- target/tricore/cpu.c | 14 ++++---- target/xtensa/cpu.c | 40 +++++++++++++---------- 23 files changed, 219 insertions(+), 177 deletions(-) create mode 100644 include/accel/tcg/tb-cpu-state.h diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index c21c5d202d..f7e7e7949d 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -385,9 +385,6 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env) { CPUState *cpu = env_cpu(env); TranslationBlock *tb; - vaddr pc; - uint64_t cs_base; - uint32_t flags, cflags; /* * By definition we've just finished a TB, so I/O is OK. @@ -397,20 +394,21 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env) * The next TB, if we chain to it, will clear the flag again. */ cpu->neg.can_do_io = true; - cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); - cflags = curr_cflags(cpu); - if (check_for_breakpoints(cpu, pc, &cflags)) { + TCGTBCPUState s = cpu_get_tb_cpu_state(cpu); + s.cflags = curr_cflags(cpu); + + if (check_for_breakpoints(cpu, s.pc, &s.cflags)) { cpu_loop_exit(cpu); } - tb = tb_lookup(cpu, pc, cs_base, flags, cflags); + tb = tb_lookup(cpu, s.pc, s.cs_base, s.flags, s.cflags); if (tb == NULL) { return tcg_code_gen_epilogue; } if (qemu_loglevel_mask(CPU_LOG_TB_CPU | CPU_LOG_EXEC)) { - log_cpu_exec(pc, cpu, tb); + log_cpu_exec(s.pc, cpu, tb); } return tb->tc.ptr; @@ -560,11 +558,7 @@ static void cpu_exec_longjmp_cleanup(CPUState *cpu) void cpu_exec_step_atomic(CPUState *cpu) { - CPUArchState *env = cpu_env(cpu); TranslationBlock *tb; - vaddr pc; - uint64_t cs_base; - uint32_t flags, cflags; int tb_exit; if (sigsetjmp(cpu->jmp_env, 0) == 0) { @@ -573,13 +567,13 @@ void cpu_exec_step_atomic(CPUState *cpu) g_assert(!cpu->running); cpu->running = true; - cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); + TCGTBCPUState s = cpu_get_tb_cpu_state(cpu); + s.cflags = curr_cflags(cpu); - cflags = curr_cflags(cpu); /* Execute in a serial context. */ - cflags &= ~CF_PARALLEL; + s.cflags &= ~CF_PARALLEL; /* After 1 insn, return and release the exclusive lock. */ - cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | 1; + s.cflags |= CF_NO_GOTO_TB | CF_NO_GOTO_PTR | 1; /* * No need to check_for_breakpoints here. * We only arrive in cpu_exec_step_atomic after beginning execution @@ -587,16 +581,16 @@ void cpu_exec_step_atomic(CPUState *cpu) * Any breakpoint for this insn will have been recognized earlier. */ - tb = tb_lookup(cpu, pc, cs_base, flags, cflags); + tb = tb_lookup(cpu, s.pc, s.cs_base, s.flags, s.cflags); if (tb == NULL) { mmap_lock(); - tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); + tb = tb_gen_code(cpu, s.pc, s.cs_base, s.flags, s.cflags); mmap_unlock(); } cpu_exec_enter(cpu); /* execute the generated code */ - trace_exec_tb(tb, pc); + trace_exec_tb(tb, s.pc); cpu_tb_exec(cpu, tb, &tb_exit); cpu_exec_exit(cpu); } else { @@ -941,11 +935,8 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc) while (!cpu_handle_interrupt(cpu, &last_tb)) { TranslationBlock *tb; - vaddr pc; - uint64_t cs_base; - uint32_t flags, cflags; - - cpu_get_tb_cpu_state(cpu_env(cpu), &pc, &cs_base, &flags); + TCGTBCPUState s = cpu_get_tb_cpu_state(cpu); + s.cflags = cpu->cflags_next_tb; /* * When requested, use an exact setting for cflags for the next @@ -954,33 +945,32 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc) * have CF_INVALID set, -1 is a convenient invalid value that * does not require tcg headers for cpu_common_reset. */ - cflags = cpu->cflags_next_tb; - if (cflags == -1) { - cflags = curr_cflags(cpu); + if (s.cflags == -1) { + s.cflags = curr_cflags(cpu); } else { cpu->cflags_next_tb = -1; } - if (check_for_breakpoints(cpu, pc, &cflags)) { + if (check_for_breakpoints(cpu, s.pc, &s.cflags)) { break; } - tb = tb_lookup(cpu, pc, cs_base, flags, cflags); + tb = tb_lookup(cpu, s.pc, s.cs_base, s.flags, s.cflags); if (tb == NULL) { CPUJumpCache *jc; uint32_t h; mmap_lock(); - tb = tb_gen_code(cpu, pc, cs_base, flags, cflags); + tb = tb_gen_code(cpu, s.pc, s.cs_base, s.flags, s.cflags); mmap_unlock(); /* * We add the TB in the virtual pc hash table * for the fast lookup */ - h = tb_jmp_cache_hash_func(pc); + h = tb_jmp_cache_hash_func(s.pc); jc = cpu->tb_jmp_cache; - jc->array[h].pc = pc; + jc->array[h].pc = s.pc; qatomic_set(&jc->array[h].tb, tb); } @@ -1000,7 +990,7 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc) tb_add_jump(last_tb, tb_exit, tb); } - cpu_loop_exec_tb(cpu, tb, pc, &last_tb, &tb_exit); + cpu_loop_exec_tb(cpu, tb, s.pc, &last_tb, &tb_exit); /* Try to align the host and virtual clocks if the guest is in advance */ diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 31c7f9927f..f2766cedfc 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -590,13 +590,9 @@ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr) /* The exception probably happened in a helper. The CPU state should have been saved before calling it. Fetch the PC from there. */ CPUArchState *env = cpu_env(cpu); - vaddr pc; - uint64_t cs_base; - tb_page_addr_t addr; - uint32_t flags; + TCGTBCPUState s = cpu_get_tb_cpu_state(cpu); + tb_page_addr_t addr = get_page_addr_code(env, s.pc); - cpu_get_tb_cpu_state(env, &pc, &cs_base, &flags); - addr = get_page_addr_code(env, pc); if (addr != -1) { tb_invalidate_phys_range(cpu, addr, addr); } diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index f5e5746976..43a39c2e13 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -16,10 +16,10 @@ #include "exec/memop.h" #include "exec/mmu-access-type.h" #include "exec/vaddr.h" +#include "accel/tcg/tb-cpu-state.h" #include "tcg/tcg-mo.h" -void cpu_get_tb_cpu_state(CPUArchState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags); +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs); struct TCGCPUOps { /** diff --git a/include/accel/tcg/tb-cpu-state.h b/include/accel/tcg/tb-cpu-state.h new file mode 100644 index 0000000000..8f912900ca --- /dev/null +++ b/include/accel/tcg/tb-cpu-state.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +/* + * Definition of TCGTBCPUState. + */ + +#ifndef EXEC_TB_CPU_STATE_H +#define EXEC_TB_CPU_STATE_H + +#include "exec/vaddr.h" + +typedef struct TCGTBCPUState { + vaddr pc; + uint32_t flags; + uint32_t cflags; + uint64_t cs_base; +} TCGTBCPUState; + +#endif diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 134806e755..90e3a2e748 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -41,15 +41,16 @@ static vaddr alpha_cpu_get_pc(CPUState *cs) return env->pc; } -void cpu_get_tb_cpu_state(CPUAlphaState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { - *pc = env->pc; - *cs_base = 0; - *pflags = env->flags & ENV_FLAG_TB_MASK; + CPUAlphaState *env = cpu_env(cs); + uint32_t flags = env->flags & ENV_FLAG_TB_MASK; + #ifdef CONFIG_USER_ONLY - *pflags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; + flags |= TB_FLAG_UNALIGN * !cs->prctl_unalign_sigbus; #endif + + return (TCGTBCPUState){ .pc = env->pc, .flags = flags }; } static void alpha_cpu_synchronize_from_tb(CPUState *cs, diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 5315264c28..b49381924b 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -10,6 +10,7 @@ #include "internals.h" #include "cpu-features.h" #include "exec/helper-proto.h" +#include "exec/translation-block.h" #include "accel/tcg/cpu-ops.h" #include "cpregs.h" @@ -544,21 +545,22 @@ static bool mve_no_pred(CPUARMState *env) return true; } -void cpu_get_tb_cpu_state(CPUARMState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { + CPUARMState *env = cpu_env(cs); CPUARMTBFlags flags; + vaddr pc; assert_hflags_rebuild_correctly(env); flags = env->hflags; if (EX_TBFLAG_ANY(flags, AARCH64_STATE)) { - *pc = env->pc; + pc = env->pc; if (cpu_isar_feature(aa64_bti, env_archcpu(env))) { DP_TBFLAG_A64(flags, BTYPE, env->btype); } } else { - *pc = env->regs[15]; + pc = env->regs[15]; if (arm_feature(env, ARM_FEATURE_M)) { if (arm_feature(env, ARM_FEATURE_M_SECURITY) && @@ -620,6 +622,9 @@ void cpu_get_tb_cpu_state(CPUARMState *env, vaddr *pc, DP_TBFLAG_ANY(flags, PSTATE__SS, 1); } - *pflags = flags.flags; - *cs_base = flags.flags2; + return (TCGTBCPUState){ + .pc = pc, + .flags = flags.flags, + .cs_base = flags.flags2, + }; } diff --git a/target/avr/cpu.c b/target/avr/cpu.c index d9fecb272e..683195b61d 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -54,14 +54,11 @@ static int avr_cpu_mmu_index(CPUState *cs, bool ifetch) return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX; } -void cpu_get_tb_cpu_state(CPUAVRState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { + CPUAVRState *env = cpu_env(cs); uint32_t flags = 0; - *pc = env->pc_w * 2; - *cs_base = 0; - if (env->fullacc) { flags |= TB_FLAGS_FULL_ACCESS; } @@ -69,7 +66,7 @@ void cpu_get_tb_cpu_state(CPUAVRState *env, vaddr *pc, flags |= TB_FLAGS_SKIP; } - *pflags = flags; + return (TCGTBCPUState){ .pc = env->pc_w * 2, .flags = flags }; } static void avr_cpu_synchronize_from_tb(CPUState *cs, diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index 2272f1222b..a7f76dd089 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -255,19 +255,20 @@ static vaddr hexagon_cpu_get_pc(CPUState *cs) return cpu_env(cs)->gpr[HEX_REG_PC]; } -void cpu_get_tb_cpu_state(CPUHexagonState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { + CPUHexagonState *env = cpu_env(cs); + vaddr pc = env->gpr[HEX_REG_PC]; uint32_t hex_flags = 0; - *pc = env->gpr[HEX_REG_PC]; - *cs_base = 0; - if (*pc == env->gpr[HEX_REG_SA0]) { + + if (pc == env->gpr[HEX_REG_SA0]) { hex_flags = FIELD_DP32(hex_flags, TB_FLAGS, IS_TIGHT_LOOP, 1); } - *flags = hex_flags; - if (*pc & PCALIGN_MASK) { + if (pc & PCALIGN_MASK) { hexagon_raise_exception_err(env, HEX_CAUSE_PC_NOT_ALIGNED, 0); } + + return (TCGTBCPUState){ .pc = pc, .flags = hex_flags }; } static void hexagon_cpu_synchronize_from_tb(CPUState *cs, diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 4cdaf98ab1..40cbc191bb 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -51,11 +51,12 @@ static vaddr hppa_cpu_get_pc(CPUState *cs) env->iaoq_f & -4); } -void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, - uint64_t *pcsbase, uint32_t *pflags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { + CPUHPPAState *env = cpu_env(cs); uint32_t flags = 0; uint64_t cs_base = 0; + vaddr pc; /* * TB lookup assumes that PC contains the complete virtual address. @@ -63,7 +64,7 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, * incomplete virtual address. This also means that we must separate * out current cpu privilege from the low bits of IAOQ_F. */ - *pc = hppa_cpu_get_pc(env_cpu(env)); + pc = hppa_cpu_get_pc(env_cpu(env)); flags |= (env->iaoq_f & 3) << TB_FLAG_PRIV_SHIFT; /* @@ -99,8 +100,7 @@ void cpu_get_tb_cpu_state(CPUHPPAState *env, vaddr *pc, } #endif - *pcsbase = cs_base; - *pflags = flags; + return (TCGTBCPUState){ .pc = pc, .flags = flags, .cs_base = cs_base }; } static void hppa_cpu_synchronize_from_tb(CPUState *cs, diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index bb6f82befb..3004fb3023 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -48,18 +48,23 @@ static void x86_cpu_exec_exit(CPUState *cs) env->eflags = cpu_compute_eflags(env); } -void cpu_get_tb_cpu_state(CPUX86State *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { - *flags = env->hflags | + CPUX86State *env = cpu_env(cs); + uint32_t flags, cs_base; + vaddr pc; + + flags = env->hflags | (env->eflags & (IOPL_MASK | TF_MASK | RF_MASK | VM_MASK | AC_MASK)); if (env->hflags & HF_CS64_MASK) { - *cs_base = 0; - *pc = env->eip; + cs_base = 0; + pc = env->eip; } else { - *cs_base = env->segs[R_CS].base; - *pc = (uint32_t)(*cs_base + env->eip); + cs_base = env->segs[R_CS].base; + pc = (uint32_t)(cs_base + env->eip); } + + return (TCGTBCPUState){ .pc = pc, .flags = flags, .cs_base = cs_base }; } static void x86_cpu_synchronize_from_tb(CPUState *cs, diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index be770b7e19..446cf43914 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -336,16 +336,18 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } #endif -void cpu_get_tb_cpu_state(CPULoongArchState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) -{ - *pc = env->pc; - *cs_base = 0; - *flags = env->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK); - *flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, FPE) * HW_FLAGS_EUEN_FPE; - *flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE) * HW_FLAGS_EUEN_SXE; - *flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE) * HW_FLAGS_EUEN_ASXE; - *flags |= is_va32(env) * HW_FLAGS_VA32; +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +{ + CPULoongArchState *env = cpu_env(cs); + uint32_t flags; + + flags = env->CSR_CRMD & (R_CSR_CRMD_PLV_MASK | R_CSR_CRMD_PG_MASK); + flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, FPE) * HW_FLAGS_EUEN_FPE; + flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, SXE) * HW_FLAGS_EUEN_SXE; + flags |= FIELD_EX64(env->CSR_EUEN, CSR_EUEN, ASXE) * HW_FLAGS_EUEN_ASXE; + flags |= is_va32(env) * HW_FLAGS_VA32; + + return (TCGTBCPUState){ .pc = env->pc, .flags = flags }; } static void loongarch_cpu_synchronize_from_tb(CPUState *cs, diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 2b4ec40509..b75ed6e887 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -23,6 +23,7 @@ #include "cpu.h" #include "migration/vmstate.h" #include "fpu/softfloat.h" +#include "exec/translation-block.h" #include "accel/tcg/cpu-ops.h" static void m68k_cpu_set_pc(CPUState *cs, vaddr value) @@ -39,20 +40,22 @@ static vaddr m68k_cpu_get_pc(CPUState *cs) return cpu->env.pc; } -void cpu_get_tb_cpu_state(CPUM68KState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { - *pc = env->pc; - *cs_base = 0; - *flags = (env->macsr >> 4) & TB_FLAGS_MACSR; + CPUM68KState *env = cpu_env(cs); + uint32_t flags; + + flags = (env->macsr >> 4) & TB_FLAGS_MACSR; if (env->sr & SR_S) { - *flags |= TB_FLAGS_MSR_S; - *flags |= (env->sfc << (TB_FLAGS_SFC_S_BIT - 2)) & TB_FLAGS_SFC_S; - *flags |= (env->dfc << (TB_FLAGS_DFC_S_BIT - 2)) & TB_FLAGS_DFC_S; + flags |= TB_FLAGS_MSR_S; + flags |= (env->sfc << (TB_FLAGS_SFC_S_BIT - 2)) & TB_FLAGS_SFC_S; + flags |= (env->dfc << (TB_FLAGS_DFC_S_BIT - 2)) & TB_FLAGS_DFC_S; } if (M68K_SR_TRACE(env->sr) == M68K_SR_TRACE_ANY_INS) { - *flags |= TB_FLAGS_TRACE; + flags |= TB_FLAGS_TRACE; } + + return (TCGTBCPUState){ .pc = env->pc, .flags = flags }; } static void m68k_restore_state_to_opc(CPUState *cs, diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 105ede0b1e..72a0d0583c 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -95,12 +95,15 @@ static vaddr mb_cpu_get_pc(CPUState *cs) return cpu->env.pc; } -void cpu_get_tb_cpu_state(CPUMBState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { - *pc = env->pc; - *flags = (env->iflags & IFLAGS_TB_MASK) | (env->msr & MSR_TB_MASK); - *cs_base = (*flags & IMM_FLAG ? env->imm : 0); + CPUMBState *env = cpu_env(cs); + + return (TCGTBCPUState){ + .pc = env->pc, + .flags = (env->iflags & IFLAGS_TB_MASK) | (env->msr & MSR_TB_MASK), + .cs_base = (env->iflags & IMM_FLAG ? env->imm : 0), + }; } static void mb_cpu_synchronize_from_tb(CPUState *cs, diff --git a/target/mips/cpu.c b/target/mips/cpu.c index ab00adf86b..b0f7612a64 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -549,13 +549,15 @@ static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) return mips_env_mmu_index(cpu_env(cs)); } -void cpu_get_tb_cpu_state(CPUMIPSState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { - *pc = env->active_tc.PC; - *cs_base = 0; - *flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK | - MIPS_HFLAG_HWRENA_ULR); + CPUMIPSState *env = cpu_env(cs); + + return (TCGTBCPUState){ + .pc = env->active_tc.PC, + .flags = env->hflags & (MIPS_HFLAG_TMASK | MIPS_HFLAG_BMASK | + MIPS_HFLAG_HWRENA_ULR), + }; } static const TCGCPUOps mips_tcg_ops = { diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index d798127d67..aba4639bbb 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -41,14 +41,16 @@ static vaddr openrisc_cpu_get_pc(CPUState *cs) return cpu->env.pc; } -void cpu_get_tb_cpu_state(CPUOpenRISCState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { - *pc = env->pc; - *cs_base = 0; - *flags = (env->dflag ? TB_FLAGS_DFLAG : 0) - | (cpu_get_gpr(env, 0) ? 0 : TB_FLAGS_R0_0) - | (env->sr & (SR_SM | SR_DME | SR_IME | SR_OVE)); + CPUOpenRISCState *env = cpu_env(cs); + + return (TCGTBCPUState){ + .pc = env->pc, + .flags = ((env->dflag ? TB_FLAGS_DFLAG : 0) + | (cpu_get_gpr(env, 0) ? 0 : TB_FLAGS_R0_0) + | (env->sr & (SR_SM | SR_DME | SR_IME | SR_OVE))), + }; } static void openrisc_cpu_synchronize_from_tb(CPUState *cs, diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index 8d248bcbb9..ccaf2b0343 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -256,9 +256,9 @@ void hreg_update_pmu_hflags(CPUPPCState *env) env->hflags |= hreg_compute_pmu_hflags_value(env); } -void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { + CPUPPCState *env = cpu_env(cs); uint32_t hflags_current = env->hflags; #ifdef CONFIG_DEBUG_TCG @@ -270,9 +270,7 @@ void cpu_get_tb_cpu_state(CPUPPCState *env, vaddr *pc, } #endif - *pc = env->nip; - *cs_base = 0; - *flags = hflags_current; + return (TCGTBCPUState){ .pc = env->nip, .flags = hflags_current }; } void cpu_interrupt_exittb(CPUState *cs) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index e67de7dfe2..927153377e 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -98,17 +98,14 @@ static int riscv_cpu_mmu_index(CPUState *cs, bool ifetch) return riscv_env_mmu_index(cpu_env(cs), ifetch); } -void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { + CPURISCVState *env = cpu_env(cs); RISCVCPU *cpu = env_archcpu(env); RISCVExtStatus fs, vs; uint32_t flags = 0; bool pm_signext = riscv_cpu_virt_mem_enabled(env); - *pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc; - *cs_base = 0; - if (cpu->cfg.ext_zve32x) { /* * If env->vl equals to VLMAX, we can use generic vector operation @@ -192,7 +189,10 @@ void cpu_get_tb_cpu_state(CPURISCVState *env, vaddr *pc, flags = FIELD_DP32(flags, TB_FLAGS, PM_PMM, riscv_pm_get_pmm(env)); flags = FIELD_DP32(flags, TB_FLAGS, PM_SIGNEXTEND, pm_signext); - *pflags = flags; + return (TCGTBCPUState){ + .pc = env->xl == MXL_RV32 ? env->pc & UINT32_MAX : env->pc, + .flags = flags + }; } static void riscv_cpu_synchronize_from_tb(CPUState *cs, diff --git a/target/rx/cpu.c b/target/rx/cpu.c index e8b47be675..be778c9f65 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -44,13 +44,15 @@ static vaddr rx_cpu_get_pc(CPUState *cs) return cpu->env.pc; } -void cpu_get_tb_cpu_state(CPURXState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { - *pc = env->pc; - *cs_base = 0; - *flags = FIELD_DP32(0, PSW, PM, env->psw_pm); - *flags = FIELD_DP32(*flags, PSW, U, env->psw_u); + CPURXState *env = cpu_env(cs); + uint32_t flags = 0; + + flags = FIELD_DP32(flags, PSW, PM, env->psw_pm); + flags = FIELD_DP32(flags, PSW, U, env->psw_u); + + return (TCGTBCPUState){ .pc = env->pc, .flags = flags }; } static void rx_cpu_synchronize_from_tb(CPUState *cs, diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 435b2034ff..279289f265 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -309,9 +309,9 @@ static int s390x_cpu_mmu_index(CPUState *cs, bool ifetch) return s390x_env_mmu_index(cpu_env(cs), ifetch); } -void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { + CPUS390XState *env = cpu_env(cs); uint32_t flags; if (env->psw.addr & 1) { @@ -323,9 +323,6 @@ void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, tcg_s390_program_interrupt(env, PGM_SPECIFICATION, 0); } - *pc = env->psw.addr; - *cs_base = env->ex_value; - flags = (env->psw.mask >> FLAG_MASK_PSW_SHIFT) & FLAG_MASK_PSW; if (env->psw.mask & PSW_MASK_PER) { flags |= env->cregs[9] & (FLAG_MASK_PER_BRANCH | @@ -342,7 +339,12 @@ void cpu_get_tb_cpu_state(CPUS390XState *env, vaddr *pc, if (env->cregs[0] & CR0_VECTOR) { flags |= FLAG_MASK_VECTOR; } - *pflags = flags; + + return (TCGTBCPUState){ + .pc = env->psw.addr, + .flags = flags, + .cs_base = env->ex_value, + }; } static const TCGCPUOps s390_tcg_ops = { diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index 5fb18bf55e..cbd43b55e5 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -43,19 +43,27 @@ static vaddr superh_cpu_get_pc(CPUState *cs) return cpu->env.pc; } -void cpu_get_tb_cpu_state(CPUSH4State *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { - *pc = env->pc; - /* For a gUSA region, notice the end of the region. */ - *cs_base = env->flags & TB_FLAG_GUSA_MASK ? env->gregs[0] : 0; - *flags = env->flags + CPUSH4State *env = cpu_env(cs); + uint32_t flags; + + flags = env->flags | (env->fpscr & TB_FLAG_FPSCR_MASK) | (env->sr & TB_FLAG_SR_MASK) | (env->movcal_backup ? TB_FLAG_PENDING_MOVCA : 0); /* Bit 3 */ #ifdef CONFIG_USER_ONLY - *flags |= TB_FLAG_UNALIGN * !env_cpu(env)->prctl_unalign_sigbus; + flags |= TB_FLAG_UNALIGN * !cs->prctl_unalign_sigbus; +#endif + + return (TCGTBCPUState){ + .pc = env->pc, + .flags = flags, +#ifdef CONFIG_USER_ONLY + /* For a gUSA region, notice the end of the region. */ + .cs_base = flags & TB_FLAG_GUSA_MASK ? env->gregs[0] : 0, #endif + }; } static void superh_cpu_synchronize_from_tb(CPUState *cs, diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index bbdea8556a..6166b81f71 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -716,13 +716,11 @@ static void sparc_cpu_synchronize_from_tb(CPUState *cs, cpu->env.npc = tb->cs_base; } -void cpu_get_tb_cpu_state(CPUSPARCState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *pflags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { - uint32_t flags; - *pc = env->pc; - *cs_base = env->npc; - flags = cpu_mmu_index(env_cpu(env), false); + CPUSPARCState *env = cpu_env(cs); + uint32_t flags = cpu_mmu_index(cs, false); + #ifndef CONFIG_USER_ONLY if (cpu_supervisor_mode(env)) { flags |= TB_FLAG_SUPER; @@ -751,7 +749,12 @@ void cpu_get_tb_cpu_state(CPUSPARCState *env, vaddr *pc, } #endif /* !CONFIG_USER_ONLY */ #endif /* TARGET_SPARC64 */ - *pflags = flags; + + return (TCGTBCPUState){ + .pc = env->pc, + .flags = flags, + .cs_base = env->npc, + }; } static void sparc_restore_state_to_opc(CPUState *cs, diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 81b3bb6362..1151a812b6 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -45,16 +45,14 @@ static vaddr tricore_cpu_get_pc(CPUState *cs) return cpu_env(cs)->PC; } -void cpu_get_tb_cpu_state(CPUTriCoreState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { - uint32_t new_flags = 0; - *pc = env->PC; - *cs_base = 0; + CPUTriCoreState *env = cpu_env(cs); - new_flags |= FIELD_DP32(new_flags, TB_FLAGS, PRIV, - extract32(env->PSW, 10, 2)); - *flags = new_flags; + return (TCGTBCPUState){ + .pc = env->PC, + .flags = FIELD_DP32(0, TB_FLAGS, PRIV, extract32(env->PSW, 10, 2)), + }; } static void tricore_cpu_synchronize_from_tb(CPUState *cs, diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index c78ef9421c..431b7ebd7b 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -55,15 +55,15 @@ static vaddr xtensa_cpu_get_pc(CPUState *cs) return cpu->env.pc; } -void cpu_get_tb_cpu_state(CPUXtensaState *env, vaddr *pc, - uint64_t *cs_base, uint32_t *flags) +TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) { - *pc = env->pc; - *cs_base = 0; - *flags = 0; - *flags |= xtensa_get_ring(env); + CPUXtensaState *env = cpu_env(cs); + uint32_t flags = 0; + target_ulong cs_base = 0; + + flags |= xtensa_get_ring(env); if (env->sregs[PS] & PS_EXCM) { - *flags |= XTENSA_TBFLAG_EXCM; + flags |= XTENSA_TBFLAG_EXCM; } else if (xtensa_option_enabled(env->config, XTENSA_OPTION_LOOP)) { target_ulong lend_dist = env->sregs[LEND] - (env->pc & -(1u << TARGET_PAGE_BITS)); @@ -85,26 +85,26 @@ void cpu_get_tb_cpu_state(CPUXtensaState *env, vaddr *pc, if (lend_dist < (1u << TARGET_PAGE_BITS) + env->config->max_insn_size) { target_ulong lbeg_off = env->sregs[LEND] - env->sregs[LBEG]; - *cs_base = lend_dist; + cs_base = lend_dist; if (lbeg_off < 256) { - *cs_base |= lbeg_off << XTENSA_CSBASE_LBEG_OFF_SHIFT; + cs_base |= lbeg_off << XTENSA_CSBASE_LBEG_OFF_SHIFT; } } } if (xtensa_option_enabled(env->config, XTENSA_OPTION_EXTENDED_L32R) && (env->sregs[LITBASE] & 1)) { - *flags |= XTENSA_TBFLAG_LITBASE; + flags |= XTENSA_TBFLAG_LITBASE; } if (xtensa_option_enabled(env->config, XTENSA_OPTION_DEBUG)) { if (xtensa_get_cintlevel(env) < env->config->debug_level) { - *flags |= XTENSA_TBFLAG_DEBUG; + flags |= XTENSA_TBFLAG_DEBUG; } if (xtensa_get_cintlevel(env) < env->sregs[ICOUNTLEVEL]) { - *flags |= XTENSA_TBFLAG_ICOUNT; + flags |= XTENSA_TBFLAG_ICOUNT; } } if (xtensa_option_enabled(env->config, XTENSA_OPTION_COPROCESSOR)) { - *flags |= env->sregs[CPENABLE] << XTENSA_TBFLAG_CPENABLE_SHIFT; + flags |= env->sregs[CPENABLE] << XTENSA_TBFLAG_CPENABLE_SHIFT; } if (xtensa_option_enabled(env->config, XTENSA_OPTION_WINDOWED_REGISTER) && (env->sregs[PS] & (PS_WOE | PS_EXCM)) == PS_WOE) { @@ -112,15 +112,21 @@ void cpu_get_tb_cpu_state(CPUXtensaState *env, vaddr *pc, (env->sregs[WINDOW_BASE] + 1); uint32_t w = ctz32(windowstart | 0x8); - *flags |= (w << XTENSA_TBFLAG_WINDOW_SHIFT) | XTENSA_TBFLAG_CWOE; - *flags |= extract32(env->sregs[PS], PS_CALLINC_SHIFT, + flags |= (w << XTENSA_TBFLAG_WINDOW_SHIFT) | XTENSA_TBFLAG_CWOE; + flags |= extract32(env->sregs[PS], PS_CALLINC_SHIFT, PS_CALLINC_LEN) << XTENSA_TBFLAG_CALLINC_SHIFT; } else { - *flags |= 3 << XTENSA_TBFLAG_WINDOW_SHIFT; + flags |= 3 << XTENSA_TBFLAG_WINDOW_SHIFT; } if (env->yield_needed) { - *flags |= XTENSA_TBFLAG_YIELD; + flags |= XTENSA_TBFLAG_YIELD; } + + return (TCGTBCPUState){ + .pc = env->pc, + .flags = flags, + .cs_base = cs_base, + }; } static void xtensa_restore_state_to_opc(CPUState *cs, From c37f8978d9e23066132a0717b8cb4ed37a0cbd96 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 27 Apr 2025 17:22:04 -0700 Subject: [PATCH 0561/2760] accel/tcg: Move cpu_get_tb_cpu_state to TCGCPUOps Move the global function name to a hook on TCGCPUOps. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 7 ++++--- accel/tcg/translate-all.c | 2 +- include/accel/tcg/cpu-ops.h | 8 ++++++-- target/alpha/cpu.c | 3 ++- target/arm/cpu.c | 1 + target/arm/internals.h | 2 ++ target/arm/tcg/cpu-v7m.c | 1 + target/arm/tcg/hflags.c | 2 +- target/avr/cpu.c | 3 ++- target/hexagon/cpu.c | 3 ++- target/hppa/cpu.c | 3 ++- target/i386/tcg/tcg-cpu.c | 3 ++- target/loongarch/cpu.c | 3 ++- target/m68k/cpu.c | 3 ++- target/microblaze/cpu.c | 3 ++- target/mips/cpu.c | 3 ++- target/openrisc/cpu.c | 3 ++- target/ppc/cpu_init.c | 2 +- target/ppc/helper_regs.c | 3 ++- target/ppc/internal.h | 3 +++ target/riscv/tcg/tcg-cpu.c | 3 ++- target/rx/cpu.c | 3 ++- target/s390x/cpu.c | 3 ++- target/sh4/cpu.c | 3 ++- target/sparc/cpu.c | 3 ++- target/tricore/cpu.c | 3 ++- target/xtensa/cpu.c | 3 ++- 27 files changed, 56 insertions(+), 26 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index f7e7e7949d..4a405d7b56 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -395,7 +395,7 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env) */ cpu->neg.can_do_io = true; - TCGTBCPUState s = cpu_get_tb_cpu_state(cpu); + TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu); s.cflags = curr_cflags(cpu); if (check_for_breakpoints(cpu, s.pc, &s.cflags)) { @@ -567,7 +567,7 @@ void cpu_exec_step_atomic(CPUState *cpu) g_assert(!cpu->running); cpu->running = true; - TCGTBCPUState s = cpu_get_tb_cpu_state(cpu); + TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu); s.cflags = curr_cflags(cpu); /* Execute in a serial context. */ @@ -935,7 +935,7 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc) while (!cpu_handle_interrupt(cpu, &last_tb)) { TranslationBlock *tb; - TCGTBCPUState s = cpu_get_tb_cpu_state(cpu); + TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu); s.cflags = cpu->cflags_next_tb; /* @@ -1052,6 +1052,7 @@ bool tcg_exec_realizefn(CPUState *cpu, Error **errp) assert(tcg_ops->cpu_exec_reset); #endif /* !CONFIG_USER_ONLY */ assert(tcg_ops->translate_code); + assert(tcg_ops->get_tb_cpu_state); assert(tcg_ops->mmu_index); tcg_ops->initialize(); tcg_target_initialized = true; diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index f2766cedfc..97aadee52c 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -590,7 +590,7 @@ void tb_check_watchpoint(CPUState *cpu, uintptr_t retaddr) /* The exception probably happened in a helper. The CPU state should have been saved before calling it. Fetch the PC from there. */ CPUArchState *env = cpu_env(cpu); - TCGTBCPUState s = cpu_get_tb_cpu_state(cpu); + TCGTBCPUState s = cpu->cc->tcg_ops->get_tb_cpu_state(cpu); tb_page_addr_t addr = get_page_addr_code(env, s.pc); if (addr != -1) { diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index 43a39c2e13..23cd6af0b2 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -19,8 +19,6 @@ #include "accel/tcg/tb-cpu-state.h" #include "tcg/tcg-mo.h" -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs); - struct TCGCPUOps { /** * mttcg_supported: multi-threaded TCG is supported @@ -63,6 +61,12 @@ struct TCGCPUOps { */ void (*translate_code)(CPUState *cpu, TranslationBlock *tb, int *max_insns, vaddr pc, void *host_pc); + /** + * @get_tb_cpu_state: Extract CPU state for a TCG #TranslationBlock + * + * Fill in all data required to select or compile a TranslationBlock. + */ + TCGTBCPUState (*get_tb_cpu_state)(CPUState *cs); /** * @synchronize_from_tb: Synchronize state from a TCG #TranslationBlock * diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 90e3a2e748..890b84c032 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -41,7 +41,7 @@ static vaddr alpha_cpu_get_pc(CPUState *cs) return env->pc; } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState alpha_get_tb_cpu_state(CPUState *cs) { CPUAlphaState *env = cpu_env(cs); uint32_t flags = env->flags & ENV_FLAG_TB_MASK; @@ -251,6 +251,7 @@ static const TCGCPUOps alpha_tcg_ops = { .initialize = alpha_translate_init, .translate_code = alpha_translate_code, + .get_tb_cpu_state = alpha_get_tb_cpu_state, .synchronize_from_tb = alpha_cpu_synchronize_from_tb, .restore_state_to_opc = alpha_restore_state_to_opc, .mmu_index = alpha_cpu_mmu_index, diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 3dde70b04a..2020aec54a 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2693,6 +2693,7 @@ static const TCGCPUOps arm_tcg_ops = { .initialize = arm_translate_init, .translate_code = arm_translate_code, + .get_tb_cpu_state = arm_get_tb_cpu_state, .synchronize_from_tb = arm_cpu_synchronize_from_tb, .debug_excp_handler = arm_debug_excp_handler, .restore_state_to_opc = arm_restore_state_to_opc, diff --git a/target/arm/internals.h b/target/arm/internals.h index 382a4d1015..660d3a88e0 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -28,6 +28,7 @@ #include "exec/hwaddr.h" #include "exec/vaddr.h" #include "exec/breakpoint.h" +#include "accel/tcg/tb-cpu-state.h" #include "hw/registerfields.h" #include "tcg/tcg-gvec-desc.h" #include "system/memory.h" @@ -372,6 +373,7 @@ void arm_restore_state_to_opc(CPUState *cs, const uint64_t *data); #ifdef CONFIG_TCG +TCGTBCPUState arm_get_tb_cpu_state(CPUState *cs); void arm_cpu_synchronize_from_tb(CPUState *cs, const TranslationBlock *tb); /* Our implementation of TCGCPUOps::cpu_exec_halt */ diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 5c8c374885..95b23d9b55 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -238,6 +238,7 @@ static const TCGCPUOps arm_v7m_tcg_ops = { .initialize = arm_translate_init, .translate_code = arm_translate_code, + .get_tb_cpu_state = arm_get_tb_cpu_state, .synchronize_from_tb = arm_cpu_synchronize_from_tb, .debug_excp_handler = arm_debug_excp_handler, .restore_state_to_opc = arm_restore_state_to_opc, diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index b49381924b..fd407a7b28 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -545,7 +545,7 @@ static bool mve_no_pred(CPUARMState *env) return true; } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +TCGTBCPUState arm_get_tb_cpu_state(CPUState *cs) { CPUARMState *env = cpu_env(cs); CPUARMTBFlags flags; diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 683195b61d..250241541b 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -54,7 +54,7 @@ static int avr_cpu_mmu_index(CPUState *cs, bool ifetch) return ifetch ? MMU_CODE_IDX : MMU_DATA_IDX; } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState avr_get_tb_cpu_state(CPUState *cs) { CPUAVRState *env = cpu_env(cs); uint32_t flags = 0; @@ -241,6 +241,7 @@ static const TCGCPUOps avr_tcg_ops = { .mttcg_supported = false, .initialize = avr_cpu_tcg_init, .translate_code = avr_cpu_translate_code, + .get_tb_cpu_state = avr_get_tb_cpu_state, .synchronize_from_tb = avr_cpu_synchronize_from_tb, .restore_state_to_opc = avr_restore_state_to_opc, .mmu_index = avr_cpu_mmu_index, diff --git a/target/hexagon/cpu.c b/target/hexagon/cpu.c index a7f76dd089..a5a04173ab 100644 --- a/target/hexagon/cpu.c +++ b/target/hexagon/cpu.c @@ -255,7 +255,7 @@ static vaddr hexagon_cpu_get_pc(CPUState *cs) return cpu_env(cs)->gpr[HEX_REG_PC]; } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState hexagon_get_tb_cpu_state(CPUState *cs) { CPUHexagonState *env = cpu_env(cs); vaddr pc = env->gpr[HEX_REG_PC]; @@ -344,6 +344,7 @@ static const TCGCPUOps hexagon_tcg_ops = { .mttcg_supported = false, .initialize = hexagon_translate_init, .translate_code = hexagon_translate_code, + .get_tb_cpu_state = hexagon_get_tb_cpu_state, .synchronize_from_tb = hexagon_cpu_synchronize_from_tb, .restore_state_to_opc = hexagon_restore_state_to_opc, .mmu_index = hexagon_cpu_mmu_index, diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 40cbc191bb..6465181543 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -51,7 +51,7 @@ static vaddr hppa_cpu_get_pc(CPUState *cs) env->iaoq_f & -4); } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState hppa_get_tb_cpu_state(CPUState *cs) { CPUHPPAState *env = cpu_env(cs); uint32_t flags = 0; @@ -262,6 +262,7 @@ static const TCGCPUOps hppa_tcg_ops = { .initialize = hppa_translate_init, .translate_code = hppa_translate_code, + .get_tb_cpu_state = hppa_get_tb_cpu_state, .synchronize_from_tb = hppa_cpu_synchronize_from_tb, .restore_state_to_opc = hppa_restore_state_to_opc, .mmu_index = hppa_cpu_mmu_index, diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 3004fb3023..179dfdf064 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -48,7 +48,7 @@ static void x86_cpu_exec_exit(CPUState *cs) env->eflags = cpu_compute_eflags(env); } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState x86_get_tb_cpu_state(CPUState *cs) { CPUX86State *env = cpu_env(cs); uint32_t flags, cs_base; @@ -160,6 +160,7 @@ const TCGCPUOps x86_tcg_ops = { .guest_default_memory_order = TCG_MO_ALL & ~TCG_MO_ST_LD, .initialize = tcg_x86_init, .translate_code = x86_translate_code, + .get_tb_cpu_state = x86_get_tb_cpu_state, .synchronize_from_tb = x86_cpu_synchronize_from_tb, .restore_state_to_opc = x86_restore_state_to_opc, .mmu_index = x86_cpu_mmu_index, diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index 446cf43914..f7535d1be7 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -336,7 +336,7 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } #endif -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState loongarch_get_tb_cpu_state(CPUState *cs) { CPULoongArchState *env = cpu_env(cs); uint32_t flags; @@ -882,6 +882,7 @@ static const TCGCPUOps loongarch_tcg_ops = { .initialize = loongarch_translate_init, .translate_code = loongarch_translate_code, + .get_tb_cpu_state = loongarch_get_tb_cpu_state, .synchronize_from_tb = loongarch_cpu_synchronize_from_tb, .restore_state_to_opc = loongarch_restore_state_to_opc, .mmu_index = loongarch_cpu_mmu_index, diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index b75ed6e887..c5196a612e 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -40,7 +40,7 @@ static vaddr m68k_cpu_get_pc(CPUState *cs) return cpu->env.pc; } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState m68k_get_tb_cpu_state(CPUState *cs) { CPUM68KState *env = cpu_env(cs); uint32_t flags; @@ -613,6 +613,7 @@ static const TCGCPUOps m68k_tcg_ops = { .initialize = m68k_tcg_init, .translate_code = m68k_translate_code, + .get_tb_cpu_state = m68k_get_tb_cpu_state, .restore_state_to_opc = m68k_restore_state_to_opc, .mmu_index = m68k_cpu_mmu_index, diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 72a0d0583c..d069e40e70 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -95,7 +95,7 @@ static vaddr mb_cpu_get_pc(CPUState *cs) return cpu->env.pc; } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState mb_get_tb_cpu_state(CPUState *cs) { CPUMBState *env = cpu_env(cs); @@ -442,6 +442,7 @@ static const TCGCPUOps mb_tcg_ops = { .initialize = mb_tcg_init, .translate_code = mb_translate_code, + .get_tb_cpu_state = mb_get_tb_cpu_state, .synchronize_from_tb = mb_cpu_synchronize_from_tb, .restore_state_to_opc = mb_restore_state_to_opc, .mmu_index = mb_cpu_mmu_index, diff --git a/target/mips/cpu.c b/target/mips/cpu.c index b0f7612a64..4cbfb9435a 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -549,7 +549,7 @@ static int mips_cpu_mmu_index(CPUState *cs, bool ifunc) return mips_env_mmu_index(cpu_env(cs)); } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState mips_get_tb_cpu_state(CPUState *cs) { CPUMIPSState *env = cpu_env(cs); @@ -566,6 +566,7 @@ static const TCGCPUOps mips_tcg_ops = { .initialize = mips_tcg_init, .translate_code = mips_translate_code, + .get_tb_cpu_state = mips_get_tb_cpu_state, .synchronize_from_tb = mips_cpu_synchronize_from_tb, .restore_state_to_opc = mips_restore_state_to_opc, .mmu_index = mips_cpu_mmu_index, diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index aba4639bbb..054ad33360 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -41,7 +41,7 @@ static vaddr openrisc_cpu_get_pc(CPUState *cs) return cpu->env.pc; } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState openrisc_get_tb_cpu_state(CPUState *cs) { CPUOpenRISCState *env = cpu_env(cs); @@ -258,6 +258,7 @@ static const TCGCPUOps openrisc_tcg_ops = { .initialize = openrisc_translate_init, .translate_code = openrisc_translate_code, + .get_tb_cpu_state = openrisc_get_tb_cpu_state, .synchronize_from_tb = openrisc_cpu_synchronize_from_tb, .restore_state_to_opc = openrisc_restore_state_to_opc, .mmu_index = openrisc_cpu_mmu_index, diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 3a01731402..cf88a18244 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -45,7 +45,6 @@ #include "internal.h" #include "spr_common.h" #include "power8-pmu.h" - #ifndef CONFIG_USER_ONLY #include "hw/boards.h" #include "hw/intc/intc.h" @@ -7483,6 +7482,7 @@ static const TCGCPUOps ppc_tcg_ops = { .guest_default_memory_order = 0, .initialize = ppc_translate_init, .translate_code = ppc_translate_code, + .get_tb_cpu_state = ppc_get_tb_cpu_state, .restore_state_to_opc = ppc_restore_state_to_opc, .mmu_index = ppc_cpu_mmu_index, diff --git a/target/ppc/helper_regs.c b/target/ppc/helper_regs.c index ccaf2b0343..7e5726871e 100644 --- a/target/ppc/helper_regs.c +++ b/target/ppc/helper_regs.c @@ -28,6 +28,7 @@ #include "cpu-models.h" #include "spr_common.h" #include "accel/tcg/cpu-ops.h" +#include "internal.h" /* Swap temporary saved registers with GPRs */ void hreg_swap_gpr_tgpr(CPUPPCState *env) @@ -256,7 +257,7 @@ void hreg_update_pmu_hflags(CPUPPCState *env) env->hflags |= hreg_compute_pmu_hflags_value(env); } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +TCGTBCPUState ppc_get_tb_cpu_state(CPUState *cs) { CPUPPCState *env = cpu_env(cs); uint32_t hflags_current = env->hflags; diff --git a/target/ppc/internal.h b/target/ppc/internal.h index 9012d3809c..7723350227 100644 --- a/target/ppc/internal.h +++ b/target/ppc/internal.h @@ -21,6 +21,7 @@ #include "exec/breakpoint.h" #include "hw/registerfields.h" #include "exec/page-protection.h" +#include "accel/tcg/tb-cpu-state.h" /* PM instructions */ typedef enum { @@ -308,4 +309,6 @@ static inline int ger_pack_masks(int pmsk, int ymsk, int xmsk) return msk; } +TCGTBCPUState ppc_get_tb_cpu_state(CPUState *cs); + #endif /* PPC_INTERNAL_H */ diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 927153377e..55e00972b7 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -98,7 +98,7 @@ static int riscv_cpu_mmu_index(CPUState *cs, bool ifetch) return riscv_env_mmu_index(cpu_env(cs), ifetch); } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState riscv_get_tb_cpu_state(CPUState *cs) { CPURISCVState *env = cpu_env(cs); RISCVCPU *cpu = env_archcpu(env); @@ -243,6 +243,7 @@ const TCGCPUOps riscv_tcg_ops = { .initialize = riscv_translate_init, .translate_code = riscv_translate_code, + .get_tb_cpu_state = riscv_get_tb_cpu_state, .synchronize_from_tb = riscv_cpu_synchronize_from_tb, .restore_state_to_opc = riscv_restore_state_to_opc, .mmu_index = riscv_cpu_mmu_index, diff --git a/target/rx/cpu.c b/target/rx/cpu.c index be778c9f65..36eba75545 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -44,7 +44,7 @@ static vaddr rx_cpu_get_pc(CPUState *cs) return cpu->env.pc; } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState rx_get_tb_cpu_state(CPUState *cs) { CPURXState *env = cpu_env(cs); uint32_t flags = 0; @@ -220,6 +220,7 @@ static const TCGCPUOps rx_tcg_ops = { .initialize = rx_translate_init, .translate_code = rx_translate_code, + .get_tb_cpu_state = rx_get_tb_cpu_state, .synchronize_from_tb = rx_cpu_synchronize_from_tb, .restore_state_to_opc = rx_restore_state_to_opc, .mmu_index = rx_cpu_mmu_index, diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 279289f265..9c1158ebcc 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -309,7 +309,7 @@ static int s390x_cpu_mmu_index(CPUState *cs, bool ifetch) return s390x_env_mmu_index(cpu_env(cs), ifetch); } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState s390x_get_tb_cpu_state(CPUState *cs) { CPUS390XState *env = cpu_env(cs); uint32_t flags; @@ -358,6 +358,7 @@ static const TCGCPUOps s390_tcg_ops = { .initialize = s390x_translate_init, .translate_code = s390x_translate_code, + .get_tb_cpu_state = s390x_get_tb_cpu_state, .restore_state_to_opc = s390x_restore_state_to_opc, .mmu_index = s390x_cpu_mmu_index, diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index cbd43b55e5..b35f18e250 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -43,7 +43,7 @@ static vaddr superh_cpu_get_pc(CPUState *cs) return cpu->env.pc; } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState superh_get_tb_cpu_state(CPUState *cs) { CPUSH4State *env = cpu_env(cs); uint32_t flags; @@ -289,6 +289,7 @@ static const TCGCPUOps superh_tcg_ops = { .initialize = sh4_translate_init, .translate_code = sh4_translate_code, + .get_tb_cpu_state = superh_get_tb_cpu_state, .synchronize_from_tb = superh_cpu_synchronize_from_tb, .restore_state_to_opc = superh_restore_state_to_opc, .mmu_index = sh4_cpu_mmu_index, diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 6166b81f71..2a3e408923 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -716,7 +716,7 @@ static void sparc_cpu_synchronize_from_tb(CPUState *cs, cpu->env.npc = tb->cs_base; } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState sparc_get_tb_cpu_state(CPUState *cs) { CPUSPARCState *env = cpu_env(cs); uint32_t flags = cpu_mmu_index(cs, false); @@ -1029,6 +1029,7 @@ static const TCGCPUOps sparc_tcg_ops = { .initialize = sparc_tcg_init, .translate_code = sparc_translate_code, + .get_tb_cpu_state = sparc_get_tb_cpu_state, .synchronize_from_tb = sparc_cpu_synchronize_from_tb, .restore_state_to_opc = sparc_restore_state_to_opc, .mmu_index = sparc_cpu_mmu_index, diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index 1151a812b6..e56f90fde9 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -45,7 +45,7 @@ static vaddr tricore_cpu_get_pc(CPUState *cs) return cpu_env(cs)->PC; } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState tricore_get_tb_cpu_state(CPUState *cs) { CPUTriCoreState *env = cpu_env(cs); @@ -185,6 +185,7 @@ static const TCGCPUOps tricore_tcg_ops = { .mttcg_supported = false, .initialize = tricore_tcg_init, .translate_code = tricore_translate_code, + .get_tb_cpu_state = tricore_get_tb_cpu_state, .synchronize_from_tb = tricore_cpu_synchronize_from_tb, .restore_state_to_opc = tricore_restore_state_to_opc, .mmu_index = tricore_cpu_mmu_index, diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 431b7ebd7b..91b71b6caa 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -55,7 +55,7 @@ static vaddr xtensa_cpu_get_pc(CPUState *cs) return cpu->env.pc; } -TCGTBCPUState cpu_get_tb_cpu_state(CPUState *cs) +static TCGTBCPUState xtensa_get_tb_cpu_state(CPUState *cs) { CPUXtensaState *env = cpu_env(cs); uint32_t flags = 0; @@ -312,6 +312,7 @@ static const TCGCPUOps xtensa_tcg_ops = { .initialize = xtensa_translate_init, .translate_code = xtensa_translate_code, .debug_excp_handler = xtensa_breakpoint_handler, + .get_tb_cpu_state = xtensa_get_tb_cpu_state, .restore_state_to_opc = xtensa_restore_state_to_opc, .mmu_index = xtensa_cpu_mmu_index, From b46357db323bf21d2816970a2f15540dfff84ecc Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Apr 2025 12:13:18 -0700 Subject: [PATCH 0562/2760] accel/tcg: Pass TCGTBCPUState to tb_lookup Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 4a405d7b56..808983e461 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -232,35 +232,33 @@ static TranslationBlock *tb_htable_lookup(CPUState *cpu, vaddr pc, * * Returns: an existing translation block or NULL. */ -static inline TranslationBlock *tb_lookup(CPUState *cpu, vaddr pc, - uint64_t cs_base, uint32_t flags, - uint32_t cflags) +static inline TranslationBlock *tb_lookup(CPUState *cpu, TCGTBCPUState s) { TranslationBlock *tb; CPUJumpCache *jc; uint32_t hash; /* we should never be trying to look up an INVALID tb */ - tcg_debug_assert(!(cflags & CF_INVALID)); + tcg_debug_assert(!(s.cflags & CF_INVALID)); - hash = tb_jmp_cache_hash_func(pc); + hash = tb_jmp_cache_hash_func(s.pc); jc = cpu->tb_jmp_cache; tb = qatomic_read(&jc->array[hash].tb); if (likely(tb && - jc->array[hash].pc == pc && - tb->cs_base == cs_base && - tb->flags == flags && - tb_cflags(tb) == cflags)) { + jc->array[hash].pc == s.pc && + tb->cs_base == s.cs_base && + tb->flags == s.flags && + tb_cflags(tb) == s.cflags)) { goto hit; } - tb = tb_htable_lookup(cpu, pc, cs_base, flags, cflags); + tb = tb_htable_lookup(cpu, s.pc, s.cs_base, s.flags, s.cflags); if (tb == NULL) { return NULL; } - jc->array[hash].pc = pc; + jc->array[hash].pc = s.pc; qatomic_set(&jc->array[hash].tb, tb); hit: @@ -268,7 +266,7 @@ static inline TranslationBlock *tb_lookup(CPUState *cpu, vaddr pc, * As long as tb is not NULL, the contents are consistent. Therefore, * the virtual PC has to match for non-CF_PCREL translations. */ - assert((tb_cflags(tb) & CF_PCREL) || tb->pc == pc); + assert((tb_cflags(tb) & CF_PCREL) || tb->pc == s.pc); return tb; } @@ -402,7 +400,7 @@ const void *HELPER(lookup_tb_ptr)(CPUArchState *env) cpu_loop_exit(cpu); } - tb = tb_lookup(cpu, s.pc, s.cs_base, s.flags, s.cflags); + tb = tb_lookup(cpu, s); if (tb == NULL) { return tcg_code_gen_epilogue; } @@ -581,7 +579,7 @@ void cpu_exec_step_atomic(CPUState *cpu) * Any breakpoint for this insn will have been recognized earlier. */ - tb = tb_lookup(cpu, s.pc, s.cs_base, s.flags, s.cflags); + tb = tb_lookup(cpu, s); if (tb == NULL) { mmap_lock(); tb = tb_gen_code(cpu, s.pc, s.cs_base, s.flags, s.cflags); @@ -955,7 +953,7 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc) break; } - tb = tb_lookup(cpu, s.pc, s.cs_base, s.flags, s.cflags); + tb = tb_lookup(cpu, s); if (tb == NULL) { CPUJumpCache *jc; uint32_t h; From 088caf3de4e48e70d6716195afc0e47edd823ac0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Apr 2025 13:02:32 -0700 Subject: [PATCH 0563/2760] accel/tcg: Pass TCGTBCPUState to tb_htable_lookup Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 808983e461..8e6899950e 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -195,26 +195,24 @@ static bool tb_lookup_cmp(const void *p, const void *d) return false; } -static TranslationBlock *tb_htable_lookup(CPUState *cpu, vaddr pc, - uint64_t cs_base, uint32_t flags, - uint32_t cflags) +static TranslationBlock *tb_htable_lookup(CPUState *cpu, TCGTBCPUState s) { tb_page_addr_t phys_pc; struct tb_desc desc; uint32_t h; desc.env = cpu_env(cpu); - desc.cs_base = cs_base; - desc.flags = flags; - desc.cflags = cflags; - desc.pc = pc; - phys_pc = get_page_addr_code(desc.env, pc); + desc.cs_base = s.cs_base; + desc.flags = s.flags; + desc.cflags = s.cflags; + desc.pc = s.pc; + phys_pc = get_page_addr_code(desc.env, s.pc); if (phys_pc == -1) { return NULL; } desc.page_addr0 = phys_pc; - h = tb_hash_func(phys_pc, (cflags & CF_PCREL ? 0 : pc), - flags, cs_base, cflags); + h = tb_hash_func(phys_pc, (s.cflags & CF_PCREL ? 0 : s.pc), + s.flags, s.cs_base, s.cflags); return qht_lookup_custom(&tb_ctx.htable, &desc, h, tb_lookup_cmp); } @@ -253,7 +251,7 @@ static inline TranslationBlock *tb_lookup(CPUState *cpu, TCGTBCPUState s) goto hit; } - tb = tb_htable_lookup(cpu, s.pc, s.cs_base, s.flags, s.cflags); + tb = tb_htable_lookup(cpu, s); if (tb == NULL) { return NULL; } From cec7176a23bfb46ce54481f278e235f58eb9c456 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Apr 2025 18:39:08 -0700 Subject: [PATCH 0564/2760] accel/tcg: Use TCGTBCPUState in struct tb_desc Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 8e6899950e..4ad84c2db8 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -150,12 +150,9 @@ static void init_delay_params(SyncClocks *sc, const CPUState *cpu) #endif /* CONFIG USER ONLY */ struct tb_desc { - vaddr pc; - uint64_t cs_base; + TCGTBCPUState s; CPUArchState *env; tb_page_addr_t page_addr0; - uint32_t flags; - uint32_t cflags; }; static bool tb_lookup_cmp(const void *p, const void *d) @@ -163,11 +160,11 @@ static bool tb_lookup_cmp(const void *p, const void *d) const TranslationBlock *tb = p; const struct tb_desc *desc = d; - if ((tb_cflags(tb) & CF_PCREL || tb->pc == desc->pc) && + if ((tb_cflags(tb) & CF_PCREL || tb->pc == desc->s.pc) && tb_page_addr0(tb) == desc->page_addr0 && - tb->cs_base == desc->cs_base && - tb->flags == desc->flags && - tb_cflags(tb) == desc->cflags) { + tb->cs_base == desc->s.cs_base && + tb->flags == desc->s.flags && + tb_cflags(tb) == desc->s.cflags) { /* check next page if needed */ tb_page_addr_t tb_phys_page1 = tb_page_addr1(tb); if (tb_phys_page1 == -1) { @@ -185,7 +182,7 @@ static bool tb_lookup_cmp(const void *p, const void *d) * is different for the new TB. Therefore any exception raised * here by the faulting lookup is not premature. */ - virt_page1 = TARGET_PAGE_ALIGN(desc->pc); + virt_page1 = TARGET_PAGE_ALIGN(desc->s.pc); phys_page1 = get_page_addr_code(desc->env, virt_page1); if (tb_phys_page1 == phys_page1) { return true; @@ -201,11 +198,8 @@ static TranslationBlock *tb_htable_lookup(CPUState *cpu, TCGTBCPUState s) struct tb_desc desc; uint32_t h; + desc.s = s; desc.env = cpu_env(cpu); - desc.cs_base = s.cs_base; - desc.flags = s.flags; - desc.cflags = s.cflags; - desc.pc = s.pc; phys_pc = get_page_addr_code(desc.env, s.pc); if (phys_pc == -1) { return NULL; From 18a77386f15d4fed13b3d162e73e784e1da1f862 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 29 Apr 2025 18:59:26 -0700 Subject: [PATCH 0565/2760] accel/tcg: Pass TCGTBCPUState to tb_gen_code Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 4 ++-- accel/tcg/internal-common.h | 5 ++--- accel/tcg/translate-all.c | 28 +++++++++++++--------------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index 4ad84c2db8..a7436d2873 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -574,7 +574,7 @@ void cpu_exec_step_atomic(CPUState *cpu) tb = tb_lookup(cpu, s); if (tb == NULL) { mmap_lock(); - tb = tb_gen_code(cpu, s.pc, s.cs_base, s.flags, s.cflags); + tb = tb_gen_code(cpu, s); mmap_unlock(); } @@ -951,7 +951,7 @@ cpu_exec_loop(CPUState *cpu, SyncClocks *sc) uint32_t h; mmap_lock(); - tb = tb_gen_code(cpu, s.pc, s.cs_base, s.flags, s.cflags); + tb = tb_gen_code(cpu, s); mmap_unlock(); /* diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index 98c702422f..1dbc45dd95 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -12,6 +12,7 @@ #include "exec/cpu-common.h" #include "exec/translation-block.h" #include "exec/mmap-lock.h" +#include "accel/tcg/tb-cpu-state.h" extern int64_t max_delay; extern int64_t max_advance; @@ -46,9 +47,7 @@ static inline bool cpu_plugin_mem_cbs_enabled(const CPUState *cpu) #endif } -TranslationBlock *tb_gen_code(CPUState *cpu, vaddr pc, - uint64_t cs_base, uint32_t flags, - int cflags); +TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s); void page_init(void); void tb_htable_init(void); void tb_reset_jump(TranslationBlock *tb, int n); diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 97aadee52c..7b0bd50904 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -290,9 +290,7 @@ static int setjmp_gen_code(CPUArchState *env, TranslationBlock *tb, } /* Called with mmap_lock held for user mode emulation. */ -TranslationBlock *tb_gen_code(CPUState *cpu, - vaddr pc, uint64_t cs_base, - uint32_t flags, int cflags) +TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s) { CPUArchState *env = cpu_env(cpu); TranslationBlock *tb, *existing_tb; @@ -305,14 +303,14 @@ TranslationBlock *tb_gen_code(CPUState *cpu, assert_memory_lock(); qemu_thread_jit_write(); - phys_pc = get_page_addr_code_hostp(env, pc, &host_pc); + phys_pc = get_page_addr_code_hostp(env, s.pc, &host_pc); if (phys_pc == -1) { /* Generate a one-shot TB with 1 insn in it */ - cflags = (cflags & ~CF_COUNT_MASK) | 1; + s.cflags = (s.cflags & ~CF_COUNT_MASK) | 1; } - max_insns = cflags & CF_COUNT_MASK; + max_insns = s.cflags & CF_COUNT_MASK; if (max_insns == 0) { max_insns = TCG_MAX_INSNS; } @@ -332,12 +330,12 @@ TranslationBlock *tb_gen_code(CPUState *cpu, gen_code_buf = tcg_ctx->code_gen_ptr; tb->tc.ptr = tcg_splitwx_to_rx(gen_code_buf); - if (!(cflags & CF_PCREL)) { - tb->pc = pc; + if (!(s.cflags & CF_PCREL)) { + tb->pc = s.pc; } - tb->cs_base = cs_base; - tb->flags = flags; - tb->cflags = cflags; + tb->cs_base = s.cs_base; + tb->flags = s.flags; + tb->cflags = s.cflags; tb_set_page_addr0(tb, phys_pc); tb_set_page_addr1(tb, -1); if (phys_pc != -1) { @@ -355,9 +353,9 @@ TranslationBlock *tb_gen_code(CPUState *cpu, tcg_ctx->guest_mo = cpu->cc->tcg_ops->guest_default_memory_order; restart_translate: - trace_translate_block(tb, pc, tb->tc.ptr); + trace_translate_block(tb, s.pc, tb->tc.ptr); - gen_code_size = setjmp_gen_code(env, tb, pc, host_pc, &max_insns, &ti); + gen_code_size = setjmp_gen_code(env, tb, s.pc, host_pc, &max_insns, &ti); if (unlikely(gen_code_size < 0)) { switch (gen_code_size) { case -1: @@ -434,10 +432,10 @@ TranslationBlock *tb_gen_code(CPUState *cpu, * For CF_PCREL, attribute all executions of the generated code * to its first mapping. */ - perf_report_code(pc, tb, tcg_splitwx_to_rx(gen_code_buf)); + perf_report_code(s.pc, tb, tcg_splitwx_to_rx(gen_code_buf)); if (qemu_loglevel_mask(CPU_LOG_TB_OUT_ASM) && - qemu_log_in_addr_range(pc)) { + qemu_log_in_addr_range(s.pc)) { FILE *logfile = qemu_log_trylock(); if (logfile) { int code_size, data_size; From 0baf907b718e1602383b973de7822c25db4c4a36 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Apr 2025 10:16:52 -0700 Subject: [PATCH 0566/2760] accel/tcg: Split out accel/tcg/helper-retaddr.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move set_helper_retaddr and clear_helper_retaddr to a new header file. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 1 + accel/tcg/user-exec.c | 1 + include/accel/tcg/cpu-ldst.h | 34 ----------------------- include/accel/tcg/helper-retaddr.h | 43 ++++++++++++++++++++++++++++++ target/arm/tcg/helper-a64.c | 1 + target/arm/tcg/sme_helper.c | 1 + target/arm/tcg/sve_helper.c | 1 + target/ppc/mem_helper.c | 1 + target/s390x/tcg/mem_helper.c | 1 + 9 files changed, 50 insertions(+), 34 deletions(-) create mode 100644 include/accel/tcg/helper-retaddr.h diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index a7436d2873..a8fbda31ba 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -24,6 +24,7 @@ #include "hw/core/cpu.h" #include "accel/tcg/cpu-ldst.h" #include "accel/tcg/cpu-ops.h" +#include "accel/tcg/helper-retaddr.h" #include "trace.h" #include "disas/disas.h" #include "exec/cpu-common.h" diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 70feee8df9..68e01fc584 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -26,6 +26,7 @@ #include "qemu/bitops.h" #include "qemu/rcu.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/helper-retaddr.h" #include "accel/tcg/probe.h" #include "user/cpu_loop.h" #include "qemu/main-loop.h" diff --git a/include/accel/tcg/cpu-ldst.h b/include/accel/tcg/cpu-ldst.h index f97a730703..44a62b54da 100644 --- a/include/accel/tcg/cpu-ldst.h +++ b/include/accel/tcg/cpu-ldst.h @@ -526,38 +526,4 @@ void *tlb_vaddr_to_host(CPUArchState *env, vaddr addr, MMUAccessType access_type, int mmu_idx); #endif -/* - * For user-only, helpers that use guest to host address translation - * must protect the actual host memory access by recording 'retaddr' - * for the signal handler. This is required for a race condition in - * which another thread unmaps the page between a probe and the - * actual access. - */ -#ifdef CONFIG_USER_ONLY -extern __thread uintptr_t helper_retaddr; - -static inline void set_helper_retaddr(uintptr_t ra) -{ - helper_retaddr = ra; - /* - * Ensure that this write is visible to the SIGSEGV handler that - * may be invoked due to a subsequent invalid memory operation. - */ - signal_barrier(); -} - -static inline void clear_helper_retaddr(void) -{ - /* - * Ensure that previous memory operations have succeeded before - * removing the data visible to the signal handler. - */ - signal_barrier(); - helper_retaddr = 0; -} -#else -#define set_helper_retaddr(ra) do { } while (0) -#define clear_helper_retaddr() do { } while (0) -#endif - #endif /* ACCEL_TCG_CPU_LDST_H */ diff --git a/include/accel/tcg/helper-retaddr.h b/include/accel/tcg/helper-retaddr.h new file mode 100644 index 0000000000..037fda2b83 --- /dev/null +++ b/include/accel/tcg/helper-retaddr.h @@ -0,0 +1,43 @@ +/* + * Get user helper pc for memory unwinding. + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#ifndef ACCEL_TCG_HELPER_RETADDR_H +#define ACCEL_TCG_HELPER_RETADDR_H + +/* + * For user-only, helpers that use guest to host address translation + * must protect the actual host memory access by recording 'retaddr' + * for the signal handler. This is required for a race condition in + * which another thread unmaps the page between a probe and the + * actual access. + */ +#ifdef CONFIG_USER_ONLY +extern __thread uintptr_t helper_retaddr; + +static inline void set_helper_retaddr(uintptr_t ra) +{ + helper_retaddr = ra; + /* + * Ensure that this write is visible to the SIGSEGV handler that + * may be invoked due to a subsequent invalid memory operation. + */ + signal_barrier(); +} + +static inline void clear_helper_retaddr(void) +{ + /* + * Ensure that previous memory operations have succeeded before + * removing the data visible to the signal handler. + */ + signal_barrier(); + helper_retaddr = 0; +} +#else +#define set_helper_retaddr(ra) do { } while (0) +#define clear_helper_retaddr() do { } while (0) +#endif + +#endif /* ACCEL_TCG_HELPER_RETADDR_H */ diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 9cffda07cd..4f618ae390 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -30,6 +30,7 @@ #include "qemu/crc32c.h" #include "exec/cpu-common.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/helper-retaddr.h" #include "accel/tcg/probe.h" #include "exec/target_page.h" #include "exec/tlb-flags.h" diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 3226895cae..de0c6e54d4 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -23,6 +23,7 @@ #include "tcg/tcg-gvec-desc.h" #include "exec/helper-proto.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/helper-retaddr.h" #include "qemu/int128.h" #include "fpu/softfloat.h" #include "vec_internal.h" diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 9f20ecb51d..a2c363a4e1 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -30,6 +30,7 @@ #include "vec_internal.h" #include "sve_ldst_internal.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/helper-retaddr.h" #include "accel/tcg/cpu-ops.h" #include "accel/tcg/probe.h" #ifdef CONFIG_USER_ONLY diff --git a/target/ppc/mem_helper.c b/target/ppc/mem_helper.c index aa1af44d22..6ab71a6fcb 100644 --- a/target/ppc/mem_helper.c +++ b/target/ppc/mem_helper.c @@ -24,6 +24,7 @@ #include "exec/helper-proto.h" #include "helper_regs.h" #include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/helper-retaddr.h" #include "accel/tcg/probe.h" #include "internal.h" #include "qemu/atomic128.h" diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index 857005b120..a03609a140 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -32,6 +32,7 @@ #include "exec/target_page.h" #include "exec/tlb-flags.h" #include "accel/tcg/cpu-ops.h" +#include "accel/tcg/helper-retaddr.h" #include "qemu/int128.h" #include "qemu/atomic128.h" From 5e5a9aea793cfd67541153105b6046436f6ae4a8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 28 Apr 2025 10:20:41 -0700 Subject: [PATCH 0567/2760] accel/tcg: Compile cpu-exec.c twice Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 2 -- accel/tcg/meson.build | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index a8fbda31ba..cc5f362305 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -22,7 +22,6 @@ #include "qapi/error.h" #include "qapi/type-helpers.h" #include "hw/core/cpu.h" -#include "accel/tcg/cpu-ldst.h" #include "accel/tcg/cpu-ops.h" #include "accel/tcg/helper-retaddr.h" #include "trace.h" @@ -37,7 +36,6 @@ #include "qemu/rcu.h" #include "exec/log.h" #include "qemu/main-loop.h" -#include "cpu.h" #include "exec/icount.h" #include "exec/replay-core.h" #include "system/tcg.h" diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 3f7b127130..0bb089299b 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -5,6 +5,7 @@ endif tcg_ss = ss.source_set() tcg_ss.add(files( + 'cpu-exec.c', 'cpu-exec-common.c', 'tcg-runtime.c', 'tcg-runtime-gvec.c', @@ -21,7 +22,6 @@ libsystem_ss.add_all(tcg_ss) tcg_specific_ss = ss.source_set() tcg_specific_ss.add(files( 'tcg-all.c', - 'cpu-exec.c', 'translate-all.c', )) tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) From 28502121be7b1422af55bbed6f65a273b889ef01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 22:46:34 +0100 Subject: [PATCH 0568/2760] system/vl: Filter machine list available for a particular target binary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Binaries can register a QOM type to filter their machines by filling their TargetInfo::machine_typename field. This can be used by example by main() -> machine_help_func() to filter the machines list. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson --- include/qemu/target-info-impl.h | 2 ++ include/qemu/target-info.h | 8 ++++++++ system/vl.c | 3 ++- target-info-stub.c | 2 ++ target-info.c | 5 +++++ 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/include/qemu/target-info-impl.h b/include/qemu/target-info-impl.h index d30805f7f2..d0e8c86176 100644 --- a/include/qemu/target-info-impl.h +++ b/include/qemu/target-info-impl.h @@ -14,6 +14,8 @@ typedef struct TargetInfo { /* runtime equivalent of TARGET_NAME definition */ const char *target_name; + /* QOM typename machines for this binary must implement */ + const char *machine_typename; } TargetInfo; /** diff --git a/include/qemu/target-info.h b/include/qemu/target-info.h index 58d4136897..2b6ccabb11 100644 --- a/include/qemu/target-info.h +++ b/include/qemu/target-info.h @@ -16,6 +16,14 @@ */ const char *target_name(void); +/** + * target_machine_typename: + * + * Returns: Name of the QOM interface implemented by machines + * usable on this target binary. + */ +const char *target_machine_typename(void); + /** * target_cpu_type: * diff --git a/system/vl.c b/system/vl.c index 520956f4a1..7223f1ff17 100644 --- a/system/vl.c +++ b/system/vl.c @@ -27,6 +27,7 @@ #include "qemu/datadir.h" #include "qemu/units.h" #include "qemu/module.h" +#include "qemu/target-info.h" #include "exec/cpu-common.h" #include "exec/page-vary.h" #include "hw/qdev-properties.h" @@ -1564,7 +1565,7 @@ static void machine_help_func(const QDict *qdict) GSList *el; const char *type = qdict_get_try_str(qdict, "type"); - machines = object_class_get_list(TYPE_MACHINE, false); + machines = object_class_get_list(target_machine_typename(), false); if (type) { ObjectClass *machine_class = OBJECT_CLASS(find_machine(type, machines)); if (machine_class) { diff --git a/target-info-stub.c b/target-info-stub.c index 773a10188c..bcf834f71d 100644 --- a/target-info-stub.c +++ b/target-info-stub.c @@ -9,10 +9,12 @@ #include "qemu/osdep.h" #include "qemu/target-info.h" #include "qemu/target-info-impl.h" +#include "hw/boards.h" #include "cpu.h" static const TargetInfo target_info_stub = { .target_name = TARGET_NAME, + .machine_typename = TYPE_MACHINE, }; const TargetInfo *target_info(void) diff --git a/target-info.c b/target-info.c index 84b18931e7..0042769e3a 100644 --- a/target-info.c +++ b/target-info.c @@ -14,3 +14,8 @@ const char *target_name(void) { return target_info()->target_name; } + +const char *target_machine_typename(void) +{ + return target_info()->machine_typename; +} From b113dfa081a6a7e061551a70e6ede7af0941a845 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Apr 2025 20:18:03 +0200 Subject: [PATCH 0569/2760] qemu/target_info: Add %target_cpu_type field to TargetInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé --- include/qemu/target-info-impl.h | 2 ++ target-info-stub.c | 6 +----- target-info.c | 5 +++++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/qemu/target-info-impl.h b/include/qemu/target-info-impl.h index d0e8c86176..76766eeaae 100644 --- a/include/qemu/target-info-impl.h +++ b/include/qemu/target-info-impl.h @@ -14,6 +14,8 @@ typedef struct TargetInfo { /* runtime equivalent of TARGET_NAME definition */ const char *target_name; + /* runtime equivalent of CPU_RESOLVING_TYPE definition */ + const char *cpu_type; /* QOM typename machines for this binary must implement */ const char *machine_typename; } TargetInfo; diff --git a/target-info-stub.c b/target-info-stub.c index bcf834f71d..86da297277 100644 --- a/target-info-stub.c +++ b/target-info-stub.c @@ -14,6 +14,7 @@ static const TargetInfo target_info_stub = { .target_name = TARGET_NAME, + .cpu_type = CPU_RESOLVING_TYPE, .machine_typename = TYPE_MACHINE, }; @@ -21,8 +22,3 @@ const TargetInfo *target_info(void) { return &target_info_stub; } - -const char *target_cpu_type(void) -{ - return CPU_RESOLVING_TYPE; -} diff --git a/target-info.c b/target-info.c index 0042769e3a..5f5ef1f932 100644 --- a/target-info.c +++ b/target-info.c @@ -15,6 +15,11 @@ const char *target_name(void) return target_info()->target_name; } +const char *target_cpu_type(void) +{ + return target_info()->cpu_type; +} + const char *target_machine_typename(void) { return target_info()->machine_typename; From c1be135ad5b124b08715ca836b95b738c6b9d7d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 23 Mar 2025 13:20:24 +0100 Subject: [PATCH 0570/2760] qemu: Introduce target_long_bits() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé --- include/qemu/target-info-impl.h | 2 ++ include/qemu/target-info.h | 7 +++++++ target-info-stub.c | 1 + target-info.c | 5 +++++ 4 files changed, 15 insertions(+) diff --git a/include/qemu/target-info-impl.h b/include/qemu/target-info-impl.h index 76766eeaae..1b51cbcfe1 100644 --- a/include/qemu/target-info-impl.h +++ b/include/qemu/target-info-impl.h @@ -14,6 +14,8 @@ typedef struct TargetInfo { /* runtime equivalent of TARGET_NAME definition */ const char *target_name; + /* runtime equivalent of TARGET_LONG_BITS definition */ + unsigned long_bits; /* runtime equivalent of CPU_RESOLVING_TYPE definition */ const char *cpu_type; /* QOM typename machines for this binary must implement */ diff --git a/include/qemu/target-info.h b/include/qemu/target-info.h index 2b6ccabb11..850a2958b9 100644 --- a/include/qemu/target-info.h +++ b/include/qemu/target-info.h @@ -16,6 +16,13 @@ */ const char *target_name(void); +/** + * target_long_bits: + * + * Returns: number of bits in a long type for this target (i.e. 64). + */ +unsigned target_long_bits(void); + /** * target_machine_typename: * diff --git a/target-info-stub.c b/target-info-stub.c index 86da297277..fecc0e7128 100644 --- a/target-info-stub.c +++ b/target-info-stub.c @@ -14,6 +14,7 @@ static const TargetInfo target_info_stub = { .target_name = TARGET_NAME, + .long_bits = TARGET_LONG_BITS, .cpu_type = CPU_RESOLVING_TYPE, .machine_typename = TYPE_MACHINE, }; diff --git a/target-info.c b/target-info.c index 5f5ef1f932..16fdca7aaa 100644 --- a/target-info.c +++ b/target-info.c @@ -15,6 +15,11 @@ const char *target_name(void) return target_info()->target_name; } +unsigned target_long_bits(void) +{ + return target_info()->long_bits; +} + const char *target_cpu_type(void) { return target_info()->cpu_type; From 0648c76ad198e91515771fbbeaac3a3807669a4a Mon Sep 17 00:00:00 2001 From: Vincent Vanlaer Date: Sat, 26 Oct 2024 18:30:07 +0200 Subject: [PATCH 0571/2760] block: refactor error handling of commit_iteration Signed-off-by: Vincent Vanlaer Message-Id: <20241026163010.2865002-4-libvirt-e6954efa@volkihar.be> [vsementsov]: move action declaration to the top of the function Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/commit.c | 63 ++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/block/commit.c b/block/commit.c index 3ee0ade7df..5c6596a52e 100644 --- a/block/commit.c +++ b/block/commit.c @@ -129,51 +129,60 @@ static void commit_clean(Job *job) } static int commit_iteration(CommitBlockJob *s, int64_t offset, - int64_t *n, void *buf) + int64_t *requested_bytes, void *buf) { + BlockErrorAction action; + int64_t bytes = *requested_bytes; int ret = 0; - bool copy; bool error_in_source = true; /* Copy if allocated above the base */ WITH_GRAPH_RDLOCK_GUARD() { ret = bdrv_co_common_block_status_above(blk_bs(s->top), s->base_overlay, true, true, offset, COMMIT_BUFFER_SIZE, - n, NULL, NULL, NULL); + &bytes, NULL, NULL, NULL); } - copy = (ret >= 0 && ret & BDRV_BLOCK_ALLOCATED); - trace_commit_one_iteration(s, offset, *n, ret); - if (copy) { - assert(*n < SIZE_MAX); + trace_commit_one_iteration(s, offset, bytes, ret); - ret = blk_co_pread(s->top, offset, *n, buf, 0); - if (ret >= 0) { - ret = blk_co_pwrite(s->base, offset, *n, buf, 0); - if (ret < 0) { - error_in_source = false; - } - } - } if (ret < 0) { - BlockErrorAction action = block_job_error_action(&s->common, - s->on_error, - error_in_source, - -ret); - if (action == BLOCK_ERROR_ACTION_REPORT) { - return ret; - } else { - *n = 0; - return 0; + goto fail; + } + + if (ret & BDRV_BLOCK_ALLOCATED) { + assert(bytes < SIZE_MAX); + + ret = blk_co_pread(s->top, offset, bytes, buf, 0); + if (ret < 0) { + goto fail; } + + ret = blk_co_pwrite(s->base, offset, bytes, buf, 0); + if (ret < 0) { + error_in_source = false; + goto fail; + } + + block_job_ratelimit_processed_bytes(&s->common, bytes); } + /* Publish progress */ - job_progress_update(&s->common.job, *n); - if (copy) { - block_job_ratelimit_processed_bytes(&s->common, *n); + job_progress_update(&s->common.job, bytes); + + *requested_bytes = bytes; + + return 0; + +fail: + action = block_job_error_action(&s->common, s->on_error, + error_in_source, -ret); + if (action == BLOCK_ERROR_ACTION_REPORT) { + return ret; } + *requested_bytes = 0; + return 0; } From 6f3199f99600fe75f32f78574e507f347de80854 Mon Sep 17 00:00:00 2001 From: Vincent Vanlaer Date: Sat, 26 Oct 2024 18:30:08 +0200 Subject: [PATCH 0572/2760] block: allow commit to unmap zero blocks Non-active block commits do not discard blocks only containing zeros, causing images to lose sparseness after the commit. This commit fixes that by writing zero blocks using blk_co_pwrite_zeroes rather than writing them out as any other arbitrary data. Signed-off-by: Vincent Vanlaer Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20241026163010.2865002-5-libvirt-e6954efa@volkihar.be> Signed-off-by: Vladimir Sementsov-Ogievskiy --- block/commit.c | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/block/commit.c b/block/commit.c index 5c6596a52e..7cc8c0f0df 100644 --- a/block/commit.c +++ b/block/commit.c @@ -150,19 +150,39 @@ static int commit_iteration(CommitBlockJob *s, int64_t offset, } if (ret & BDRV_BLOCK_ALLOCATED) { - assert(bytes < SIZE_MAX); + if (ret & BDRV_BLOCK_ZERO) { + /* + * If the top (sub)clusters are smaller than the base + * (sub)clusters, this will not unmap unless the underlying device + * does some tracking of these requests. Ideally, we would find + * the maximal extent of the zero clusters. + */ + ret = blk_co_pwrite_zeroes(s->base, offset, bytes, + BDRV_REQ_MAY_UNMAP); + if (ret < 0) { + error_in_source = false; + goto fail; + } + } else { + assert(bytes < SIZE_MAX); - ret = blk_co_pread(s->top, offset, bytes, buf, 0); - if (ret < 0) { - goto fail; - } + ret = blk_co_pread(s->top, offset, bytes, buf, 0); + if (ret < 0) { + goto fail; + } - ret = blk_co_pwrite(s->base, offset, bytes, buf, 0); - if (ret < 0) { - error_in_source = false; - goto fail; + ret = blk_co_pwrite(s->base, offset, bytes, buf, 0); + if (ret < 0) { + error_in_source = false; + goto fail; + } } + /* + * Whether zeroes actually end up on disk depends on the details of + * the underlying driver. Therefore, this might rate limit more than + * is necessary. + */ block_job_ratelimit_processed_bytes(&s->common, bytes); } From 68aba2a9350345d109f8036f9eff68b81b1c2167 Mon Sep 17 00:00:00 2001 From: Vincent Vanlaer Date: Sat, 26 Oct 2024 18:30:09 +0200 Subject: [PATCH 0573/2760] block: add test non-active commit with zeroed data Signed-off-by: Vincent Vanlaer Tested-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Vladimir Sementsov-Ogievskiy Message-Id: <20241026163010.2865002-6-libvirt-e6954efa@volkihar.be> Signed-off-by: Vladimir Sementsov-Ogievskiy --- tests/qemu-iotests/tests/commit-zero-blocks | 96 +++++++++++++++++++ .../qemu-iotests/tests/commit-zero-blocks.out | 54 +++++++++++ 2 files changed, 150 insertions(+) create mode 100755 tests/qemu-iotests/tests/commit-zero-blocks create mode 100644 tests/qemu-iotests/tests/commit-zero-blocks.out diff --git a/tests/qemu-iotests/tests/commit-zero-blocks b/tests/qemu-iotests/tests/commit-zero-blocks new file mode 100755 index 0000000000..de00273e72 --- /dev/null +++ b/tests/qemu-iotests/tests/commit-zero-blocks @@ -0,0 +1,96 @@ +#!/usr/bin/env bash +# group: rw quick +# +# Test for commit of discarded blocks +# +# This tests committing a live snapshot where some of the blocks that +# are present in the base image are discarded in the intermediate image. +# This intends to check that these blocks are also discarded in the base +# image after the commit. +# +# Copyright (C) 2024 Vincent Vanlaer. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# creator +owner=libvirt-e6954efa@volkihar.be + +seq=`basename $0` +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_qemu + _rm_test_img "${TEST_IMG}.base" + _rm_test_img "${TEST_IMG}.mid" + _cleanup_test_img +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 +_supported_proto file + +size="1M" + +TEST_IMG="$TEST_IMG.base" _make_test_img $size +TEST_IMG="$TEST_IMG.mid" _make_test_img -b "$TEST_IMG.base" -F $IMGFMT $size +_make_test_img -b "${TEST_IMG}.mid" -F $IMGFMT $size + +$QEMU_IO -c "write -P 0x01 64k 128k" "$TEST_IMG.base" | _filter_qemu_io +$QEMU_IO -c "discard 64k 64k" "$TEST_IMG.mid" | _filter_qemu_io + +echo +echo "=== Base image info before commit ===" +TEST_IMG="${TEST_IMG}.base" _img_info | _filter_img_info +$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map + +echo +echo "=== Middle image info before commit ===" +TEST_IMG="${TEST_IMG}.mid" _img_info | _filter_img_info +$QEMU_IMG map --output=json "$TEST_IMG.mid" | _filter_qemu_img_map + +echo +echo === Running QEMU Live Commit Test === +echo + +qemu_comm_method="qmp" +_launch_qemu -drive file="${TEST_IMG}",if=virtio,id=test +h=$QEMU_HANDLE + +_send_qemu_cmd $h "{ 'execute': 'qmp_capabilities' }" "return" + +_send_qemu_cmd $h "{ 'execute': 'block-commit', + 'arguments': { 'device': 'test', + 'top': '"${TEST_IMG}.mid"', + 'base': '"${TEST_IMG}.base"'} }" '"status": "null"' + +_cleanup_qemu + +echo +echo "=== Base image info after commit ===" +TEST_IMG="${TEST_IMG}.base" _img_info | _filter_img_info +$QEMU_IMG map --output=json "$TEST_IMG.base" | _filter_qemu_img_map + +# success, all done +echo "*** done" +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/commit-zero-blocks.out b/tests/qemu-iotests/tests/commit-zero-blocks.out new file mode 100644 index 0000000000..85bdc46aaf --- /dev/null +++ b/tests/qemu-iotests/tests/commit-zero-blocks.out @@ -0,0 +1,54 @@ +QA output created by commit-zero-blocks +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=1048576 +Formatting 'TEST_DIR/t.IMGFMT.mid', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.base backing_fmt=IMGFMT +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=1048576 backing_file=TEST_DIR/t.IMGFMT.mid backing_fmt=IMGFMT +wrote 131072/131072 bytes at offset 65536 +128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +discard 65536/65536 bytes at offset 65536 +64 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) + +=== Base image info before commit === +image: TEST_DIR/t.IMGFMT.base +file format: IMGFMT +virtual size: 1 MiB (1048576 bytes) +[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 131072, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 196608, "length": 851968, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] + +=== Middle image info before commit === +image: TEST_DIR/t.IMGFMT.mid +file format: IMGFMT +virtual size: 1 MiB (1048576 bytes) +backing file: TEST_DIR/t.IMGFMT.base +backing file format: IMGFMT +[{ "start": 0, "length": 65536, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 131072, "length": 65536, "depth": 1, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 196608, "length": 851968, "depth": 1, "present": false, "zero": true, "data": false, "compressed": false}] + +=== Running QEMU Live Commit Test === + +{ 'execute': 'qmp_capabilities' } +{"return": {}} +{ 'execute': 'block-commit', + 'arguments': { 'device': 'test', + 'top': 'TEST_DIR/t.IMGFMT.mid', + 'base': 'TEST_DIR/t.IMGFMT.base'} } +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "test"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "test", "len": 1048576, "offset": 1048576, "speed": 0, "type": "commit"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "test"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "test"}} + +=== Base image info after commit === +image: TEST_DIR/t.IMGFMT.base +file format: IMGFMT +virtual size: 1 MiB (1048576 bytes) +[{ "start": 0, "length": 65536, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}, +{ "start": 65536, "length": 65536, "depth": 0, "present": true, "zero": true, "data": false, "compressed": false}, +{ "start": 131072, "length": 65536, "depth": 0, "present": true, "zero": false, "data": true, "compressed": false, "offset": OFFSET}, +{ "start": 196608, "length": 851968, "depth": 0, "present": false, "zero": true, "data": false, "compressed": false}] +*** done From da17dd5c5ee8e33124da0bf91aed11491d0c04de Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 9 Apr 2025 11:42:30 +0300 Subject: [PATCH 0574/2760] qapi: synchronize jobs and block-jobs documentation Actualize documentation and synchronize it for commands which actually call the same functions internally. Reviewed-by: Markus Armbruster Signed-off-by: Vladimir Sementsov-Ogievskiy Message-ID: <20250409084232.28201-2-vsementsov@yandex-team.ru> --- qapi/block-core.json | 61 ++++++++++++++++++++++++++------------------ qapi/job.json | 30 ++++++++++++++++++++-- 2 files changed, 64 insertions(+), 27 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index b1937780e1..6beab0dc12 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2956,13 +2956,14 @@ # # Pause an active background block operation. # -# This command returns immediately after marking the active background -# block operation for pausing. It is an error to call this command if -# no operation is in progress or if the job is already paused. +# This command returns immediately after marking the active job for +# pausing. Pausing an already paused job is an error. +# +# The job will pause as soon as possible, which means transitioning +# into the PAUSED state if it was RUNNING, or into STANDBY if it was +# READY. The corresponding JOB_STATUS_CHANGE event will be emitted. # -# The operation will pause as soon as possible. No event is emitted -# when the operation is actually paused. Cancelling a paused job -# automatically resumes it. +# Cancelling a paused job automatically resumes it. # # @device: The job identifier. This used to be a device name (hence # the name of the parameter), but since QEMU 2.7 it can have other @@ -2982,9 +2983,8 @@ # # Resume an active background block operation. # -# This command returns immediately after resuming a paused background -# block operation. It is an error to call this command if no -# operation is in progress or if the job is not paused. +# This command returns immediately after resuming a paused job. +# Resuming an already running job is an error. # # This command also clears the error status of the job. # @@ -3004,10 +3004,15 @@ ## # @block-job-complete: # -# Manually trigger completion of an active background block operation. -# This is supported for drive mirroring, where it also switches the -# device to write to the target path only. The ability to complete is -# signaled with a BLOCK_JOB_READY event. +# Manually trigger completion of an active job in the READY or STANDBY +# state. Completing the job in any other state is an error. +# +# This is supported only for drive mirroring, where it also switches +# the device to write to the target path only. Note that drive +# mirroring includes drive-mirror, blockdev-mirror and block-commit +# job (only in case of "active commit", when the node being commited +# is used by the guest). The ability to complete is signaled with a +# BLOCK_JOB_READY event. # # This command completes an active background block operation # synchronously. The ordering of this command's return with the @@ -3017,8 +3022,6 @@ # rerror/werror arguments that were specified when starting the # operation. # -# A cancelled or paused job cannot be completed. -# # @device: The job identifier. This used to be a device name (hence # the name of the parameter), but since QEMU 2.7 it can have other # values. @@ -3035,10 +3038,13 @@ ## # @block-job-dismiss: # -# For jobs that have already concluded, remove them from the -# block-job-query list. This command only needs to be run for jobs -# which were started with QEMU 2.12+ job lifetime management -# semantics. +# Deletes a job that is in the CONCLUDED state. This command only +# needs to be run explicitly for jobs that don't have automatic +# dismiss enabled. In turn, automatic dismiss may be enabled only +# for jobs that have @auto-dismiss option, which are drive-backup, +# blockdev-backup, drive-mirror, blockdev-mirror, block-commit and +# block-stream. @auto-dismiss is enabled by default for these +# jobs. # # This command will refuse to operate on any job that has not yet # reached its terminal state, JOB_STATUS_CONCLUDED. For jobs that @@ -3055,12 +3061,17 @@ ## # @block-job-finalize: # -# Once a job that has manual=true reaches the pending state, it can be -# instructed to finalize any graph changes and do any necessary -# cleanup via this command. For jobs in a transaction, instructing -# one job to finalize will force ALL jobs in the transaction to -# finalize, so it is only necessary to instruct a single member job to -# finalize. +# Instructs all jobs in a transaction (or a single job if it is not +# part of any transaction) to finalize any graph changes and do any +# necessary cleanup. This command requires that all involved jobs are +# in the PENDING state. +# +# For jobs in a transaction, instructing one job to finalize will +# force ALL jobs in the transaction to finalize, so it is only +# necessary to instruct a single member job to finalize. +# +# The command is applicable only to jobs which have @auto-finalize option +# and only when this option is set to false. # # @id: The job identifier. # diff --git a/qapi/job.json b/qapi/job.json index cfc3beedd2..b03f80bc84 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -156,6 +156,9 @@ # This command returns immediately after resuming a paused job. # Resuming an already running job is an error. # +# This command also clears the error status for block-jobs (stream, +# commit, mirror, backup). +# # @id: The job identifier. # # Since: 3.0 @@ -184,7 +187,23 @@ ## # @job-complete: # -# Manually trigger completion of an active job in the READY state. +# Manually trigger completion of an active job in the READY or STANDBY +# state. Completing the job in any other state is an error. +# +# This is supported only for drive mirroring, where it also switches +# the device to write to the target path only. Note that drive +# mirroring includes drive-mirror, blockdev-mirror and block-commit +# job (only in case of "active commit", when the node being commited +# is used by the guest). The ability to complete is signaled with a +# BLOCK_JOB_READY event. +# +# This command completes an active background block operation +# synchronously. The ordering of this command's return with the +# BLOCK_JOB_COMPLETED event is not defined. Note that if an I/O error +# occurs during the processing of this command: 1) the command itself +# will fail; 2) the error will be processed according to the +# rerror/werror arguments that were specified when starting the +# operation. # # @id: The job identifier. # @@ -197,7 +216,11 @@ # # Deletes a job that is in the CONCLUDED state. This command only # needs to be run explicitly for jobs that don't have automatic -# dismiss enabled. +# dismiss enabled. In turn, automatic dismiss may be enabled only +# for jobs that have @auto-dismiss option, which are drive-backup, +# blockdev-backup, drive-mirror, blockdev-mirror, block-commit and +# block-stream. @auto-dismiss is enabled by default for these +# jobs. # # This command will refuse to operate on any job that has not yet # reached its terminal state, JOB_STATUS_CONCLUDED. For jobs that @@ -222,6 +245,9 @@ # force ALL jobs in the transaction to finalize, so it is only # necessary to instruct a single member job to finalize. # +# The command is applicable only to jobs which have @auto-finalize option +# and only when this option is set to false. +# # @id: The identifier of any job in the transaction, or of a job that # is not part of any transaction. # From b836bf2ab68fbc1e253c10bee95fa36399762967 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 9 Apr 2025 11:42:31 +0300 Subject: [PATCH 0575/2760] qapi/block-core: deprecate some block-job- APIs For change, pause, resume, complete, dismiss and finalize actions corresponding job- and block-job commands are almost equal. The difference is in find_block_job_locked() vs find_job_locked() functions. What's different? 1. find_block_job_locked() checks whether the found job is a block-job. This is OK when moving to more generic API, no needs to document this change. 2. find_block_job_locked() reports DeviceNotActive on failure, when find_job_locked() reports GenericError. So, let's document this difference in deprecated.txt. Still, for dismiss and finalize errors are not documented at all, so be silent in deprecated.txt as well. ACKed-by: Peter Krempa Reviewed-by: Eric Blake Reviewed-by: Markus Armbruster Signed-off-by: Vladimir Sementsov-Ogievskiy Message-ID: <20250409084232.28201-3-vsementsov@yandex-team.ru> --- docs/about/deprecated.rst | 31 +++++++++++++++++++++++++++++++ qapi/block-core.json | 30 ++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 05381441a9..b0d3ae6b32 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -148,6 +148,37 @@ options are removed in favor of using explicit ``blockdev-create`` and ``blockdev-add`` calls. See :doc:`/interop/live-block-operations` for details. +``block-job-pause`` (since 10.1) +'''''''''''''''''''''''''''''''' + +Use ``job-pause`` instead. The only difference is that ``job-pause`` +always reports GenericError on failure when ``block-job-pause`` reports +DeviceNotActive when block-job is not found. + +``block-job-resume`` (since 10.1) +''''''''''''''''''''''''''''''''' + +Use ``job-resume`` instead. The only difference is that ``job-resume`` +always reports GenericError on failure when ``block-job-resume`` reports +DeviceNotActive when block-job is not found. + +``block-job-complete`` (since 10.1) +''''''''''''''''''''''''''''''''''' + +Use ``job-complete`` instead. The only difference is that ``job-complete`` +always reports GenericError on failure when ``block-job-complete`` reports +DeviceNotActive when block-job is not found. + +``block-job-dismiss`` (since 10.1) +'''''''''''''''''''''''''''''''''' + +Use ``job-dismiss`` instead. + +``block-job-finalize`` (since 10.1) +''''''''''''''''''''''''''''''''''' + +Use ``job-finalize`` instead. + ``query-migrationthreads`` (since 9.2) '''''''''''''''''''''''''''''''''''''' diff --git a/qapi/block-core.json b/qapi/block-core.json index 6beab0dc12..22061227ca 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2969,6 +2969,11 @@ # the name of the parameter), but since QEMU 2.7 it can have other # values. # +# Features: +# +# @deprecated: This command is deprecated. Use @job-pause +# instead. +# # Errors: # - If no background operation is active on this device, # DeviceNotActive @@ -2976,6 +2981,7 @@ # Since: 1.3 ## { 'command': 'block-job-pause', 'data': { 'device': 'str' }, + 'features': ['deprecated'], 'allow-preconfig': true } ## @@ -2992,6 +2998,11 @@ # the name of the parameter), but since QEMU 2.7 it can have other # values. # +# Features: +# +# @deprecated: This command is deprecated. Use @job-resume +# instead. +# # Errors: # - If no background operation is active on this device, # DeviceNotActive @@ -2999,6 +3010,7 @@ # Since: 1.3 ## { 'command': 'block-job-resume', 'data': { 'device': 'str' }, + 'features': ['deprecated'], 'allow-preconfig': true } ## @@ -3026,6 +3038,11 @@ # the name of the parameter), but since QEMU 2.7 it can have other # values. # +# Features: +# +# @deprecated: This command is deprecated. Use @job-complete +# instead. +# # Errors: # - If no background operation is active on this device, # DeviceNotActive @@ -3033,6 +3050,7 @@ # Since: 1.3 ## { 'command': 'block-job-complete', 'data': { 'device': 'str' }, + 'features': ['deprecated'], 'allow-preconfig': true } ## @@ -3053,9 +3071,15 @@ # # @id: The job identifier. # +# Features: +# +# @deprecated: This command is deprecated. Use @job-dismiss +# instead. +# # Since: 2.12 ## { 'command': 'block-job-dismiss', 'data': { 'id': 'str' }, + 'features': ['deprecated'], 'allow-preconfig': true } ## @@ -3075,9 +3099,15 @@ # # @id: The job identifier. # +# Features: +# +# @deprecated: This command is deprecated. Use @job-finalize +# instead. +# # Since: 2.12 ## { 'command': 'block-job-finalize', 'data': { 'id': 'str' }, + 'features': ['deprecated'], 'allow-preconfig': true } ## From e1d8fabc20adb6a766773adb4a9b5bfe93e329bb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 13:57:30 -0700 Subject: [PATCH 0576/2760] tcg: Define INSN_START_WORDS as constant 3 Use the same value for all targets. Rename TARGET_INSN_START_WORDS and do not depend on TARGET_INSN_START_EXTRA_WORDS. Remove TCGContext.insn_start_words. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 19 +++++++++---------- include/tcg/insn-start-words.h | 11 +++++------ include/tcg/tcg-op.h | 17 ++++++++++++++--- include/tcg/tcg-opc.h | 3 +-- include/tcg/tcg.h | 12 +++++++----- target/i386/helper.c | 2 +- target/openrisc/sys_helper.c | 2 +- tcg/perf.c | 5 ++--- tcg/tcg.c | 12 +++++------- 9 files changed, 45 insertions(+), 38 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 7b0bd50904..fa4998b341 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -120,7 +120,7 @@ static int64_t decode_sleb128(const uint8_t **pp) /* Encode the data collected about the instructions while compiling TB. Place the data at BLOCK, and return the number of bytes consumed. - The logical table consists of TARGET_INSN_START_WORDS target_ulong's, + The logical table consists of INSN_START_WORDS uint64_t's, which come from the target's insn_start data, followed by a uintptr_t which comes from the host pc of the end of the code implementing the insn. @@ -140,13 +140,13 @@ static int encode_search(TranslationBlock *tb, uint8_t *block) for (i = 0, n = tb->icount; i < n; ++i) { uint64_t prev, curr; - for (j = 0; j < TARGET_INSN_START_WORDS; ++j) { + for (j = 0; j < INSN_START_WORDS; ++j) { if (i == 0) { prev = (!(tb_cflags(tb) & CF_PCREL) && j == 0 ? tb->pc : 0); } else { - prev = insn_data[(i - 1) * TARGET_INSN_START_WORDS + j]; + prev = insn_data[(i - 1) * INSN_START_WORDS + j]; } - curr = insn_data[i * TARGET_INSN_START_WORDS + j]; + curr = insn_data[i * INSN_START_WORDS + j]; p = encode_sleb128(p, curr - prev); } prev = (i == 0 ? 0 : insn_end_off[i - 1]); @@ -178,7 +178,7 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc, return -1; } - memset(data, 0, sizeof(uint64_t) * TARGET_INSN_START_WORDS); + memset(data, 0, sizeof(uint64_t) * INSN_START_WORDS); if (!(tb_cflags(tb) & CF_PCREL)) { data[0] = tb->pc; } @@ -188,7 +188,7 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc, * at which the end of the insn exceeds host_pc. */ for (i = 0; i < num_insns; ++i) { - for (j = 0; j < TARGET_INSN_START_WORDS; ++j) { + for (j = 0; j < INSN_START_WORDS; ++j) { data[j] += decode_sleb128(&p); } iter_pc += decode_sleb128(&p); @@ -206,7 +206,7 @@ static int cpu_unwind_data_from_tb(TranslationBlock *tb, uintptr_t host_pc, void cpu_restore_state_from_tb(CPUState *cpu, TranslationBlock *tb, uintptr_t host_pc) { - uint64_t data[TARGET_INSN_START_WORDS]; + uint64_t data[INSN_START_WORDS]; int insns_left = cpu_unwind_data_from_tb(tb, host_pc, data); if (insns_left < 0) { @@ -349,7 +349,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s) tcg_ctx->page_mask = TARGET_PAGE_MASK; tcg_ctx->tlb_dyn_max_bits = CPU_TLB_DYN_MAX_BITS; #endif - tcg_ctx->insn_start_words = TARGET_INSN_START_WORDS; tcg_ctx->guest_mo = cpu->cc->tcg_ops->guest_default_memory_order; restart_translate: @@ -457,7 +456,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s) fprintf(logfile, "OUT: [size=%d]\n", gen_code_size); fprintf(logfile, " -- guest addr 0x%016" PRIx64 " + tb prologue\n", - tcg_ctx->gen_insn_data[insn * TARGET_INSN_START_WORDS]); + tcg_ctx->gen_insn_data[insn * INSN_START_WORDS]); chunk_start = tcg_ctx->gen_insn_end_off[insn]; disas(logfile, tb->tc.ptr, chunk_start); @@ -470,7 +469,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s) size_t chunk_end = tcg_ctx->gen_insn_end_off[insn]; if (chunk_end > chunk_start) { fprintf(logfile, " -- guest addr 0x%016" PRIx64 "\n", - tcg_ctx->gen_insn_data[insn * TARGET_INSN_START_WORDS]); + tcg_ctx->gen_insn_data[insn * INSN_START_WORDS]); disas(logfile, tb->tc.ptr + chunk_start, chunk_end - chunk_start); chunk_start = chunk_end; diff --git a/include/tcg/insn-start-words.h b/include/tcg/insn-start-words.h index d416d19bcf..c52aec50a7 100644 --- a/include/tcg/insn-start-words.h +++ b/include/tcg/insn-start-words.h @@ -1,13 +1,12 @@ /* SPDX-License-Identifier: MIT */ /* - * Define TARGET_INSN_START_WORDS + * Define INSN_START_WORDS * Copyright (c) 2008 Fabrice Bellard */ -#ifndef TARGET_INSN_START_WORDS +#ifndef TCG_INSN_START_WORDS +#define TCG_INSN_START_WORDS -#include "cpu-param.h" +#define INSN_START_WORDS 3 -# define TARGET_INSN_START_WORDS (1 + TARGET_INSN_START_EXTRA_WORDS) - -#endif /* TARGET_INSN_START_WORDS */ +#endif /* TCG_INSN_START_WORDS */ diff --git a/include/tcg/tcg-op.h b/include/tcg/tcg-op.h index 59d19755e6..c912578fdd 100644 --- a/include/tcg/tcg-op.h +++ b/include/tcg/tcg-op.h @@ -9,6 +9,7 @@ #define TCG_TCG_OP_H #include "tcg/tcg-op-common.h" +#include "tcg/insn-start-words.h" #include "exec/target_long.h" #ifndef TARGET_LONG_BITS @@ -23,24 +24,34 @@ # error #endif +#if INSN_START_WORDS != 3 +# error Mismatch with insn-start-words.h +#endif + #if TARGET_INSN_START_EXTRA_WORDS == 0 static inline void tcg_gen_insn_start(target_ulong pc) { - TCGOp *op = tcg_emit_op(INDEX_op_insn_start, 64 / TCG_TARGET_REG_BITS); + TCGOp *op = tcg_emit_op(INDEX_op_insn_start, + INSN_START_WORDS * 64 / TCG_TARGET_REG_BITS); tcg_set_insn_start_param(op, 0, pc); + tcg_set_insn_start_param(op, 1, 0); + tcg_set_insn_start_param(op, 2, 0); } #elif TARGET_INSN_START_EXTRA_WORDS == 1 static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1) { - TCGOp *op = tcg_emit_op(INDEX_op_insn_start, 2 * 64 / TCG_TARGET_REG_BITS); + TCGOp *op = tcg_emit_op(INDEX_op_insn_start, + INSN_START_WORDS * 64 / TCG_TARGET_REG_BITS); tcg_set_insn_start_param(op, 0, pc); tcg_set_insn_start_param(op, 1, a1); + tcg_set_insn_start_param(op, 2, 0); } #elif TARGET_INSN_START_EXTRA_WORDS == 2 static inline void tcg_gen_insn_start(target_ulong pc, target_ulong a1, target_ulong a2) { - TCGOp *op = tcg_emit_op(INDEX_op_insn_start, 3 * 64 / TCG_TARGET_REG_BITS); + TCGOp *op = tcg_emit_op(INDEX_op_insn_start, + INSN_START_WORDS * 64 / TCG_TARGET_REG_BITS); tcg_set_insn_start_param(op, 0, pc); tcg_set_insn_start_param(op, 1, a1); tcg_set_insn_start_param(op, 2, a2); diff --git a/include/tcg/tcg-opc.h b/include/tcg/tcg-opc.h index 995b79383e..e988edd93a 100644 --- a/include/tcg/tcg-opc.h +++ b/include/tcg/tcg-opc.h @@ -114,8 +114,7 @@ DEF(extrh_i64_i32, 1, 1, 0, 0) #define DATA64_ARGS (TCG_TARGET_REG_BITS == 64 ? 1 : 2) -/* There are tcg_ctx->insn_start_words here, not just one. */ -DEF(insn_start, 0, 0, DATA64_ARGS, TCG_OPF_NOT_PRESENT) +DEF(insn_start, 0, 0, DATA64_ARGS * INSN_START_WORDS, TCG_OPF_NOT_PRESENT) DEF(exit_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) DEF(goto_tb, 0, 0, 1, TCG_OPF_BB_EXIT | TCG_OPF_BB_END | TCG_OPF_NOT_PRESENT) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index aa300a2f8b..a8c00c72cc 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -34,6 +34,7 @@ #include "tcg-target-reg-bits.h" #include "tcg-target.h" #include "tcg/tcg-cond.h" +#include "tcg/insn-start-words.h" #include "tcg/debug-assert.h" /* XXX: make safe guess about sizes */ @@ -359,7 +360,6 @@ struct TCGContext { int page_mask; uint8_t page_bits; uint8_t tlb_dyn_max_bits; - uint8_t insn_start_words; TCGBar guest_mo; TCGRegSet reserved_regs; @@ -582,18 +582,19 @@ static inline TCGv_vec temp_tcgv_vec(TCGTemp *t) return (TCGv_vec)temp_tcgv_i32(t); } -static inline TCGArg tcg_get_insn_param(TCGOp *op, int arg) +static inline TCGArg tcg_get_insn_param(TCGOp *op, unsigned arg) { return op->args[arg]; } -static inline void tcg_set_insn_param(TCGOp *op, int arg, TCGArg v) +static inline void tcg_set_insn_param(TCGOp *op, unsigned arg, TCGArg v) { op->args[arg] = v; } -static inline uint64_t tcg_get_insn_start_param(TCGOp *op, int arg) +static inline uint64_t tcg_get_insn_start_param(TCGOp *op, unsigned arg) { + tcg_debug_assert(arg < INSN_START_WORDS); if (TCG_TARGET_REG_BITS == 64) { return tcg_get_insn_param(op, arg); } else { @@ -602,8 +603,9 @@ static inline uint64_t tcg_get_insn_start_param(TCGOp *op, int arg) } } -static inline void tcg_set_insn_start_param(TCGOp *op, int arg, uint64_t v) +static inline void tcg_set_insn_start_param(TCGOp *op, unsigned arg, uint64_t v) { + tcg_debug_assert(arg < INSN_START_WORDS); if (TCG_TARGET_REG_BITS == 64) { tcg_set_insn_param(op, arg, v); } else { diff --git a/target/i386/helper.c b/target/i386/helper.c index 197fdac7dd..e0aaed3c4c 100644 --- a/target/i386/helper.c +++ b/target/i386/helper.c @@ -526,7 +526,7 @@ void cpu_x86_inject_mce(Monitor *mon, X86CPU *cpu, int bank, static inline target_ulong get_memio_eip(CPUX86State *env) { #ifdef CONFIG_TCG - uint64_t data[TARGET_INSN_START_WORDS]; + uint64_t data[INSN_START_WORDS]; CPUState *cs = env_cpu(env); if (!cpu_unwind_state_data(cs, cs->mem_io_pc, data)) { diff --git a/target/openrisc/sys_helper.c b/target/openrisc/sys_helper.c index 951f8e247a..d96b41a01c 100644 --- a/target/openrisc/sys_helper.c +++ b/target/openrisc/sys_helper.c @@ -218,7 +218,7 @@ target_ulong HELPER(mfspr)(CPUOpenRISCState *env, target_ulong rd, { OpenRISCCPU *cpu = env_archcpu(env); #ifndef CONFIG_USER_ONLY - uint64_t data[TARGET_INSN_START_WORDS]; + uint64_t data[INSN_START_WORDS]; MachineState *ms = MACHINE(qdev_get_machine()); CPUState *cs = env_cpu(env); int idx; diff --git a/tcg/perf.c b/tcg/perf.c index 412a987d95..4e8d2c1bee 100644 --- a/tcg/perf.c +++ b/tcg/perf.c @@ -313,7 +313,7 @@ void perf_report_code(uint64_t guest_pc, TranslationBlock *tb, const void *start) { struct debuginfo_query *q; - size_t insn, start_words; + size_t insn; uint64_t *gen_insn_data; if (!perfmap && !jitdump) { @@ -329,11 +329,10 @@ void perf_report_code(uint64_t guest_pc, TranslationBlock *tb, /* Query debuginfo for each guest instruction. */ gen_insn_data = tcg_ctx->gen_insn_data; - start_words = tcg_ctx->insn_start_words; for (insn = 0; insn < tb->icount; insn++) { /* FIXME: This replicates the restore_state_to_opc() logic. */ - q[insn].address = gen_insn_data[insn * start_words + 0]; + q[insn].address = gen_insn_data[insn * INSN_START_WORDS + 0]; if (tb_cflags(tb) & CF_PCREL) { q[insn].address |= (guest_pc & qemu_target_page_mask()); } diff --git a/tcg/tcg.c b/tcg/tcg.c index c4e866e9c3..648333a9fb 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1989,7 +1989,6 @@ void tcg_func_start(TCGContext *s) QSIMPLEQ_INIT(&s->labels); tcg_debug_assert(s->addr_type <= TCG_TYPE_REG); - tcg_debug_assert(s->insn_start_words > 0); } static TCGTemp *tcg_temp_alloc(TCGContext *s) @@ -2943,7 +2942,7 @@ void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs) nb_oargs = 0; col += ne_fprintf(f, "\n ----"); - for (i = 0, k = s->insn_start_words; i < k; ++i) { + for (i = 0, k = INSN_START_WORDS; i < k; ++i) { col += ne_fprintf(f, " %016" PRIx64, tcg_get_insn_start_param(op, i)); } @@ -6835,7 +6834,7 @@ static void tcg_out_st_helper_args(TCGContext *s, const TCGLabelQemuLdst *ldst, int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) { - int i, start_words, num_insns; + int i, num_insns; TCGOp *op; if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP) @@ -6925,9 +6924,8 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) QSIMPLEQ_INIT(&s->ldst_labels); s->pool_labels = NULL; - start_words = s->insn_start_words; s->gen_insn_data = - tcg_malloc(sizeof(uint64_t) * s->gen_tb->icount * start_words); + tcg_malloc(sizeof(uint64_t) * s->gen_tb->icount * INSN_START_WORDS); tcg_out_tb_start(s); @@ -6969,8 +6967,8 @@ int tcg_gen_code(TCGContext *s, TranslationBlock *tb, uint64_t pc_start) assert(s->gen_insn_end_off[num_insns] == off); } num_insns++; - for (i = 0; i < start_words; ++i) { - s->gen_insn_data[num_insns * start_words + i] = + for (i = 0; i < INSN_START_WORDS; ++i) { + s->gen_insn_data[num_insns * INSN_START_WORDS + i] = tcg_get_insn_start_param(op, i); } break; From 9401f91b9b0c46886388735b3f2033a9c254895a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 14:35:47 -0700 Subject: [PATCH 0577/2760] accel/tcg: Don't use TARGET_LONG_BITS in decode_sleb128 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we changed decode_sleb128 from target_long to int64_t, we failed to adjust the shift limit. Cc: qemu-stable@nongnu.org Fixes: c9ad8d27caa ("tcg: Widen gen_insn_data to uint64_t") Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index fa4998b341..acf32e6c08 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -109,7 +109,7 @@ static int64_t decode_sleb128(const uint8_t **pp) val |= (int64_t)(byte & 0x7f) << shift; shift += 7; } while (byte & 0x80); - if (shift < TARGET_LONG_BITS && (byte & 0x40)) { + if (shift < 64 && (byte & 0x40)) { val |= -(int64_t)1 << shift; } From d2bbc0d6b90cd82b3cecb2d57bb317ba982a30a3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 14:40:30 -0700 Subject: [PATCH 0578/2760] accel/tcg: Use target_long_bits() in translate-all.c Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index acf32e6c08..6b6e10be9d 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -54,6 +54,7 @@ #include "qemu/qemu-print.h" #include "qemu/main-loop.h" #include "qemu/cacheinfo.h" +#include "qemu/target-info.h" #include "qemu/timer.h" #include "exec/log.h" #include "exec/icount.h" @@ -343,7 +344,7 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s) } tcg_ctx->gen_tb = tb; - tcg_ctx->addr_type = TARGET_LONG_BITS == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64; + tcg_ctx->addr_type = target_long_bits() == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64; #ifdef CONFIG_SOFTMMU tcg_ctx->page_bits = TARGET_PAGE_BITS; tcg_ctx->page_mask = TARGET_PAGE_MASK; From b5dee28732209eaf93656807810c9c5340e907e1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 15:10:46 -0700 Subject: [PATCH 0579/2760] accel/tcg: Build translate-all.c twice Remove lots and lots of unused headers. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 2 +- accel/tcg/translate-all.c | 32 -------------------------------- 2 files changed, 1 insertion(+), 33 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 0bb089299b..7eb4619aea 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -10,6 +10,7 @@ tcg_ss.add(files( 'tcg-runtime.c', 'tcg-runtime-gvec.c', 'tb-maint.c', + 'translate-all.c', 'translator.c', )) if get_option('plugins') @@ -22,7 +23,6 @@ libsystem_ss.add_all(tcg_ss) tcg_specific_ss = ss.source_set() tcg_specific_ss.add(files( 'tcg-all.c', - 'translate-all.c', )) tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 6b6e10be9d..451b383aa8 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -22,46 +22,15 @@ #include "trace.h" #include "disas/disas.h" #include "tcg/tcg.h" -#if defined(CONFIG_USER_ONLY) -#include "qemu.h" -#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) -#include -#if __FreeBSD_version >= 700104 -#define HAVE_KINFO_GETVMMAP -#define sigqueue sigqueue_freebsd /* avoid redefinition */ -#include -#include -#define _KERNEL -#include -#undef _KERNEL -#undef sigqueue -#include -#endif -#endif -#else -#include "system/ram_addr.h" -#endif - -#include "cpu-param.h" -#include "exec/cputlb.h" -#include "exec/page-protection.h" #include "exec/mmap-lock.h" #include "tb-internal.h" #include "tlb-bounds.h" -#include "exec/translator.h" #include "exec/tb-flush.h" -#include "qemu/bitmap.h" -#include "qemu/qemu-print.h" -#include "qemu/main-loop.h" #include "qemu/cacheinfo.h" #include "qemu/target-info.h" -#include "qemu/timer.h" #include "exec/log.h" #include "exec/icount.h" -#include "system/tcg.h" -#include "qapi/error.h" #include "accel/tcg/cpu-ops.h" -#include "accel/tcg/getpc.h" #include "tb-jmp-cache.h" #include "tb-hash.h" #include "tb-context.h" @@ -69,7 +38,6 @@ #include "internal-common.h" #include "tcg/perf.h" #include "tcg/insn-start-words.h" -#include "cpu.h" TBContext tb_ctx; From 97f0d52435ec9da8fd58dc73b6765181fcb25965 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 15:25:29 -0700 Subject: [PATCH 0580/2760] accel/tcg: Build tcg-all.c twice Remove some unused headers. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 4 +--- accel/tcg/tcg-all.c | 6 +----- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 7eb4619aea..d6bd304add 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -10,6 +10,7 @@ tcg_ss.add(files( 'tcg-runtime.c', 'tcg-runtime-gvec.c', 'tb-maint.c', + 'tcg-all.c', 'translate-all.c', 'translator.c', )) @@ -21,9 +22,6 @@ libuser_ss.add_all(tcg_ss) libsystem_ss.add_all(tcg_ss) tcg_specific_ss = ss.source_set() -tcg_specific_ss.add(files( - 'tcg-all.c', -)) tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 0ce34ac912..6e5dc333d5 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -36,15 +36,11 @@ #include "qapi/qapi-builtin-visit.h" #include "qemu/units.h" #include "qemu/target-info.h" -#if defined(CONFIG_USER_ONLY) -#include "hw/qdev-core.h" -#else +#ifndef CONFIG_USER_ONLY #include "hw/boards.h" -#include "system/tcg.h" #endif #include "accel/tcg/cpu-ops.h" #include "internal-common.h" -#include "cpu-param.h" struct TCGState { From e578dcc7e1590b20a84036afe5bdfa8d23a6048e Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 29 Apr 2025 14:28:20 +0800 Subject: [PATCH 0581/2760] pc-bios: Add AST27x0 vBootrom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The boot ROM is a minimal implementation designed to load an AST27x0 boot image. Its source code is available at: https://github.com/google/vbootrom Commit id: d6e3386709b3e49322a94ffadc2aaab9944ab77b Build Information: ``` Build Date : Apr 29 2025 01:23:18 FW Version : git-d6e3386 ``` Signed-off-by: Jamin Lin Reviewed-by: Nabih Estefan Reviewed-by: Cédric Le Goater Tested-by: Nabih Estefan Link: https://lore.kernel.org/qemu-devel/20250429062822.1184920-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + pc-bios/README | 6 ++++++ pc-bios/ast27x0_bootrom.bin | Bin 0 -> 15552 bytes pc-bios/meson.build | 1 + 4 files changed, 8 insertions(+) create mode 100644 pc-bios/ast27x0_bootrom.bin diff --git a/MAINTAINERS b/MAINTAINERS index b3f9f2680b..cc94e62be4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1174,6 +1174,7 @@ F: docs/system/arm/fby35.rst F: tests/*/*aspeed* F: tests/*/*ast2700* F: hw/arm/fby35.c +F: pc-bios/ast27x0_bootrom.bin NRF51 M: Joel Stanley diff --git a/pc-bios/README b/pc-bios/README index f0f13e15f2..d009c37895 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -89,6 +89,12 @@ more features over time as needed. The source code is available at: https://github.com/google/vbootrom +- ast27x0_bootrom.bin is a simplified, free (Apache 2.0) boot ROM for + ASPEED AST27x0 BMC SOC. It currently implements the bare minimum to + load, parse, initialize and run boot images stored in SPI flash, but may grow + more features over time as needed. The source code is available at: + https://github.com/google/vbootrom + - hppa-firmware.img (32-bit) and hppa-firmware64.img (64-bit) are firmware files for the HP-PARISC (hppa) architecture. They are built form the SeaBIOS-hppa sources, which is a fork of SeaBIOS diff --git a/pc-bios/ast27x0_bootrom.bin b/pc-bios/ast27x0_bootrom.bin new file mode 100644 index 0000000000000000000000000000000000000000..0b9b3a2360e375bb6007ecdf13b39d931870f6fa GIT binary patch literal 15552 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 F{ugK9U^D;# literal 0 HcmV?d00001 diff --git a/pc-bios/meson.build b/pc-bios/meson.build index 34d8cc4f33..79bb2e1800 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -28,6 +28,7 @@ if unpack_edk2_blobs endif blobs = [ + 'ast27x0_bootrom.bin', 'bios.bin', 'bios-256k.bin', 'bios-microvm.bin', From 57be554c2984de3261d1e5d446d797d8e5b2c997 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 5 Mar 2025 14:28:21 +0800 Subject: [PATCH 0582/2760] migration: check RDMA and capabilities are compatible on both sides Depending on the order of starting RDMA and setting capability, they can be categorized into the following scenarios: Source: S1: [set capabilities] -> [Start RDMA outgoing] Destination: D1: [set capabilities] -> [Start RDMA incoming] D2: [Start RDMA incoming] -> [set capabilities] Previously, compatibility between RDMA and capabilities was verified only in scenario D1, potentially causing migration failures in other situations. For scenarios S1 and D1, we can seamlessly incorporate migration_transport_compatible() to address compatibility between channels and capabilities vs transport. For scenario D2, ensure compatibility within migrate_caps_check(). Reviewed-by: Peter Xu Signed-off-by: Li Zhijian Message-ID: <20250305062825.772629-3-lizhijian@fujitsu.com> Signed-off-by: Fabiano Rosas --- migration/migration.c | 30 ++++++++++++++++++++---------- migration/options.c | 21 +++++++++++++++++++++ migration/options.h | 1 + 3 files changed, 42 insertions(+), 10 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 55ec4bfab6..54fce997aa 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -259,6 +259,24 @@ migration_channels_and_transport_compatible(MigrationAddress *addr, return true; } +static bool +migration_capabilities_and_transport_compatible(MigrationAddress *addr, + Error **errp) +{ + if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) { + return migrate_rdma_caps_check(migrate_get_current()->capabilities, + errp); + } + + return true; +} + +static bool migration_transport_compatible(MigrationAddress *addr, Error **errp) +{ + return migration_channels_and_transport_compatible(addr, errp) && + migration_capabilities_and_transport_compatible(addr, errp); +} + static gint page_request_addr_cmp(gconstpointer ap, gconstpointer bp) { uintptr_t a = (uintptr_t) ap, b = (uintptr_t) bp; @@ -750,7 +768,7 @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels, } /* transport mechanism not suitable for migration? */ - if (!migration_channels_and_transport_compatible(addr, errp)) { + if (!migration_transport_compatible(addr, errp)) { return; } @@ -769,14 +787,6 @@ static void qemu_start_incoming_migration(const char *uri, bool has_channels, } #ifdef CONFIG_RDMA } else if (addr->transport == MIGRATION_ADDRESS_TYPE_RDMA) { - if (migrate_xbzrle()) { - error_setg(errp, "RDMA and XBZRLE can't be used together"); - return; - } - if (migrate_multifd()) { - error_setg(errp, "RDMA and multifd can't be used together"); - return; - } rdma_start_incoming_migration(&addr->u.rdma, errp); #endif } else if (addr->transport == MIGRATION_ADDRESS_TYPE_EXEC) { @@ -2208,7 +2218,7 @@ void qmp_migrate(const char *uri, bool has_channels, } /* transport mechanism not suitable for migration? */ - if (!migration_channels_and_transport_compatible(addr, errp)) { + if (!migration_transport_compatible(addr, errp)) { return; } diff --git a/migration/options.c b/migration/options.c index b0ac2ea408..1f3602839d 100644 --- a/migration/options.c +++ b/migration/options.c @@ -448,6 +448,20 @@ static bool migrate_incoming_started(void) return !!migration_incoming_get_current()->transport_data; } +bool migrate_rdma_caps_check(bool *caps, Error **errp) +{ + if (caps[MIGRATION_CAPABILITY_XBZRLE]) { + error_setg(errp, "RDMA and XBZRLE can't be used together"); + return false; + } + if (caps[MIGRATION_CAPABILITY_MULTIFD]) { + error_setg(errp, "RDMA and multifd can't be used together"); + return false; + } + + return true; +} + /** * @migration_caps_check - check capability compatibility * @@ -611,6 +625,13 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) } } + /* + * On destination side, check the cases that capability is being set + * after incoming thread has started. + */ + if (migrate_rdma() && !migrate_rdma_caps_check(new_caps, errp)) { + return false; + } return true; } diff --git a/migration/options.h b/migration/options.h index 762be4e641..82d839709e 100644 --- a/migration/options.h +++ b/migration/options.h @@ -57,6 +57,7 @@ bool migrate_tls(void); /* capabilities helpers */ +bool migrate_rdma_caps_check(bool *caps, Error **errp); bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp); /* parameters */ From 103fa641953bfadfe88fbe5f16c0afc9f1e508db Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 5 Mar 2025 14:28:22 +0800 Subject: [PATCH 0583/2760] migration: disable RDMA + postcopy-ram It's believed that RDMA + postcopy-ram has been broken for a while. Rather than spending time re-enabling it, let's simply disable it as a trade-off. Reviewed-by: Peter Xu Signed-off-by: Li Zhijian Message-ID: <20250305062825.772629-4-lizhijian@fujitsu.com> Signed-off-by: Fabiano Rosas --- migration/options.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/migration/options.c b/migration/options.c index 1f3602839d..4ba8fcb7dc 100644 --- a/migration/options.c +++ b/migration/options.c @@ -458,6 +458,10 @@ bool migrate_rdma_caps_check(bool *caps, Error **errp) error_setg(errp, "RDMA and multifd can't be used together"); return false; } + if (caps[MIGRATION_CAPABILITY_POSTCOPY_RAM]) { + error_setg(errp, "RDMA and postcopy-ram can't be used together"); + return false; + } return true; } From 4ecd6beaf9ff4ad88a33eab78789335a6e929564 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 5 Mar 2025 14:28:23 +0800 Subject: [PATCH 0584/2760] migration/rdma: Remove redundant migration_in_postcopy checks Since we have disabled RDMA + postcopy, it's safe to remove the migration_in_postcopy() that follows the migrate_rdma(). Reviewed-by: Peter Xu Signed-off-by: Li Zhijian Message-ID: <20250305062825.772629-5-lizhijian@fujitsu.com> Signed-off-by: Fabiano Rosas --- migration/rdma.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/migration/rdma.c b/migration/rdma.c index b31652baac..a3c3b432d1 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -3284,7 +3284,7 @@ static int qemu_rdma_save_page(QEMUFile *f, ram_addr_t block_offset, int rdma_control_save_page(QEMUFile *f, ram_addr_t block_offset, ram_addr_t offset, size_t size) { - if (!migrate_rdma() || migration_in_postcopy()) { + if (!migrate_rdma()) { return RAM_SAVE_CONTROL_NOT_SUPP; } @@ -3829,7 +3829,7 @@ int rdma_block_notification_handle(QEMUFile *f, const char *name) int rdma_registration_start(QEMUFile *f, uint64_t flags) { - if (!migrate_rdma() || migration_in_postcopy()) { + if (!migrate_rdma()) { return 0; } @@ -3861,7 +3861,7 @@ int rdma_registration_stop(QEMUFile *f, uint64_t flags) RDMAControlHeader head = { .len = 0, .repeat = 1 }; int ret; - if (!migrate_rdma() || migration_in_postcopy()) { + if (!migrate_rdma()) { return 0; } From 5e7ca4a7d79da6c9e584886879002f24917c8d27 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Wed, 5 Mar 2025 14:28:24 +0800 Subject: [PATCH 0585/2760] migration: Unfold control_save_page() control_save_page() is for RDMA only, unfold it to make the code more clear. In addition: - Similar to other branches style in ram_save_target_page(), involve RDMA only if the condition 'migrate_rdma()' is true. - Further simplify the code by removing the RAM_SAVE_CONTROL_NOT_SUPP. Reviewed-by: Peter Xu Signed-off-by: Li Zhijian Message-ID: <20250305062825.772629-6-lizhijian@fujitsu.com> Signed-off-by: Fabiano Rosas --- migration/ram.c | 34 +++++++--------------------------- migration/rdma.c | 7 ++----- migration/rdma.h | 3 +-- 3 files changed, 10 insertions(+), 34 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index cb8b2ed493..1181a99cf6 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1143,32 +1143,6 @@ static int save_zero_page(RAMState *rs, PageSearchStatus *pss, return len; } -/* - * @pages: the number of pages written by the control path, - * < 0 - error - * > 0 - number of pages written - * - * Return true if the pages has been saved, otherwise false is returned. - */ -static bool control_save_page(PageSearchStatus *pss, - ram_addr_t offset, int *pages) -{ - int ret; - - ret = rdma_control_save_page(pss->pss_channel, pss->block->offset, offset, - TARGET_PAGE_SIZE); - if (ret == RAM_SAVE_CONTROL_NOT_SUPP) { - return false; - } - - if (ret == RAM_SAVE_CONTROL_DELAYED) { - *pages = 1; - return true; - } - *pages = ret; - return true; -} - /* * directly send the page to the stream * @@ -1965,7 +1939,13 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) int res; /* Hand over to RDMA first */ - if (control_save_page(pss, offset, &res)) { + if (migrate_rdma()) { + res = rdma_control_save_page(pss->pss_channel, pss->block->offset, + offset, TARGET_PAGE_SIZE); + + if (res == RAM_SAVE_CONTROL_DELAYED) { + res = 1; + } return res; } diff --git a/migration/rdma.c b/migration/rdma.c index a3c3b432d1..4875ca1987 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -3284,14 +3284,11 @@ static int qemu_rdma_save_page(QEMUFile *f, ram_addr_t block_offset, int rdma_control_save_page(QEMUFile *f, ram_addr_t block_offset, ram_addr_t offset, size_t size) { - if (!migrate_rdma()) { - return RAM_SAVE_CONTROL_NOT_SUPP; - } + assert(migrate_rdma()); int ret = qemu_rdma_save_page(f, block_offset, offset, size); - if (ret != RAM_SAVE_CONTROL_DELAYED && - ret != RAM_SAVE_CONTROL_NOT_SUPP) { + if (ret != RAM_SAVE_CONTROL_DELAYED) { if (ret < 0) { qemu_file_set_error(f, ret); } diff --git a/migration/rdma.h b/migration/rdma.h index 4d3386b84a..f74f16a459 100644 --- a/migration/rdma.h +++ b/migration/rdma.h @@ -33,7 +33,6 @@ void rdma_start_incoming_migration(InetSocketAddress *host_port, Error **errp); #define RAM_CONTROL_ROUND 1 #define RAM_CONTROL_FINISH 3 -#define RAM_SAVE_CONTROL_NOT_SUPP -1000 #define RAM_SAVE_CONTROL_DELAYED -2000 #ifdef CONFIG_RDMA @@ -56,7 +55,7 @@ static inline int rdma_control_save_page(QEMUFile *f, ram_addr_t block_offset, ram_addr_t offset, size_t size) { - return RAM_SAVE_CONTROL_NOT_SUPP; + g_assert_not_reached(); } #endif #endif From 7d9849c3c41463ab9ba40348a8606927dc0fb85d Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Tue, 11 Mar 2025 10:42:21 +0800 Subject: [PATCH 0586/2760] migration: Add qtest for migration over RDMA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This qtest requires there is a RDMA(RoCE) link in the host. In order to make the test work smoothly, introduce a scripts/rdma-migration-helper.sh to detect existing RoCE link before running the test. Test will be skipped if there is no available RoCE link. # Start of rdma tests # Running /x86_64/migration/precopy/rdma/plain ok 1 /x86_64/migration/precopy/rdma/plain # SKIP No rdma link available # To enable the test: # Run 'scripts/rdma-migration-helper.sh setup' with root to setup a new rdma/rxe link and rerun the test # Optional: run 'scripts/rdma-migration-helper.sh clean' to revert the 'setup' # End of rdma tests Cc: Philippe Mathieu-Daudé Cc: Stefan Hajnoczi Reviewed-by: Peter Xu Signed-off-by: Li Zhijian Message-ID: <20250311024221.363421-1-lizhijian@fujitsu.com> [add 'head -1' to script, reformat test message] Signed-off-by: Fabiano Rosas --- MAINTAINERS | 1 + scripts/rdma-migration-helper.sh | 70 +++++++++++++++++++++++++++ tests/qtest/migration/precopy-tests.c | 66 +++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100755 scripts/rdma-migration-helper.sh diff --git a/MAINTAINERS b/MAINTAINERS index b3f9f2680b..8cd96269b2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3538,6 +3538,7 @@ R: Li Zhijian R: Peter Xu S: Odd Fixes F: migration/rdma* +F: scripts/rdma-migration-helper.sh Migration dirty limit and dirty page rate M: Hyman Huang diff --git a/scripts/rdma-migration-helper.sh b/scripts/rdma-migration-helper.sh new file mode 100755 index 0000000000..a39f2fb0e5 --- /dev/null +++ b/scripts/rdma-migration-helper.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +# Copied from blktests +get_ipv4_addr() +{ + ip -4 -o addr show dev "$1" | + sed -n 's/.*[[:blank:]]inet[[:blank:]]*\([^[:blank:]/]*\).*/\1/p' | + head -1 | tr -d '\n' +} + +# existing rdma interfaces +rdma_interfaces() +{ + rdma link show | sed -nE 's/^link .* netdev ([^ ]+).*$/\1 /p' +} + +# existing valid ipv4 interfaces +ipv4_interfaces() +{ + ip -o addr show | awk '/inet / {print $2}' | grep -v -w lo +} + +rdma_rxe_detect() +{ + for r in $(rdma_interfaces) + do + ipv4_interfaces | grep -qw $r && get_ipv4_addr $r && return + done + + return 1 +} + +rdma_rxe_setup() +{ + for i in $(ipv4_interfaces) + do + rdma_interfaces | grep -qw $i && continue + rdma link add "${i}_rxe" type rxe netdev "$i" && { + echo "Setup new rdma/rxe ${i}_rxe for $i with $(get_ipv4_addr $i)" + return + } + done + + echo "Failed to setup any new rdma/rxe link" >&2 + return 1 +} + +rdma_rxe_clean() +{ + modprobe -r rdma_rxe +} + +operation=${1:-detect} + +command -v rdma >/dev/null || { + echo "Command 'rdma' is not available, please install it first." >&2 + exit 1 +} + +if [ "$operation" == "setup" ] || [ "$operation" == "clean" ]; then + [ "$UID" == 0 ] || { + echo "Root privilege is required to setup/clean a rdma/rxe link" >&2 + exit 1 + } + rdma_rxe_"$operation" +elif [ "$operation" == "detect" ]; then + rdma_rxe_detect +else + echo "Usage: $0 [setup | detect | clean]" +fi diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index ba273d10b9..565630dddf 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -99,6 +99,68 @@ static void test_precopy_unix_dirty_ring(void) test_precopy_common(&args); } +#ifdef CONFIG_RDMA + +#define RDMA_MIGRATION_HELPER "scripts/rdma-migration-helper.sh" +static int new_rdma_link(char *buffer) +{ + char cmd[256]; + bool verbose = g_getenv("QTEST_LOG"); + + snprintf(cmd, sizeof(cmd), "%s detect %s", RDMA_MIGRATION_HELPER, + verbose ? "" : "2>/dev/null"); + + FILE *pipe = popen(cmd, "r"); + if (pipe == NULL) { + perror("Failed to run script"); + return -1; + } + + int idx = 0; + while (fgets(buffer + idx, 128 - idx, pipe) != NULL) { + idx += strlen(buffer); + } + + int status = pclose(pipe); + if (status == -1) { + perror("Error reported by pclose()"); + return -1; + } else if (WIFEXITED(status)) { + return WEXITSTATUS(status); + } + + return -1; +} + +static void test_precopy_rdma_plain(void) +{ + char buffer[128] = {}; + + if (new_rdma_link(buffer)) { + g_test_skip("No rdma link available\n" + "# To enable the test:\n" + "# Run \'" RDMA_MIGRATION_HELPER " setup\' with root to " + "setup a new rdma/rxe link and rerun the test\n" + "# Optional: run 'scripts/rdma-migration-helper.sh clean' " + "to revert the 'setup'"); + return; + } + + /* + * TODO: query a free port instead of hard code. + * 29200=('R'+'D'+'M'+'A')*100 + **/ + g_autofree char *uri = g_strdup_printf("rdma:%s:29200", buffer); + + MigrateCommon args = { + .listen_uri = uri, + .connect_uri = uri, + }; + + test_precopy_common(&args); +} +#endif + static void test_precopy_tcp_plain(void) { MigrateCommon args = { @@ -1124,6 +1186,10 @@ static void migration_test_add_precopy_smoke(MigrationTestEnv *env) test_multifd_tcp_uri_none); migration_test_add("/migration/multifd/tcp/plain/cancel", test_multifd_tcp_cancel); +#ifdef CONFIG_RDMA + migration_test_add("/migration/precopy/rdma/plain", + test_precopy_rdma_plain); +#endif } void migration_test_add_precopy(MigrationTestEnv *env) From b407c9e74753ca0dbc00832bf5fcec44efd05308 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Mon, 7 Apr 2025 09:28:33 +0200 Subject: [PATCH 0587/2760] migration: Fix latent bug in migrate_params_test_apply() migrate_params_test_apply() neglects to apply tls_authz. Currently harmless, because migrate_params_check() doesn't care. Fix it anyway. Signed-off-by: Markus Armbruster Reviewed-by: Fabiano Rosas Message-ID: <20250407072833.2118928-1-armbru@redhat.com> Signed-off-by: Fabiano Rosas --- migration/options.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/migration/options.c b/migration/options.c index 4ba8fcb7dc..b6ae95358d 100644 --- a/migration/options.c +++ b/migration/options.c @@ -1218,6 +1218,11 @@ static void migrate_params_test_apply(MigrateSetParameters *params, dest->tls_hostname = params->tls_hostname->u.s; } + if (params->tls_authz) { + assert(params->tls_authz->type == QTYPE_QSTRING); + dest->tls_authz = params->tls_authz->u.s; + } + if (params->has_max_bandwidth) { dest->max_bandwidth = params->max_bandwidth; } From 56e3c89f44ecebc946fbe4ffed325d1a79b26e38 Mon Sep 17 00:00:00 2001 From: Prasad Pandit Date: Fri, 11 Apr 2025 17:15:28 +0530 Subject: [PATCH 0588/2760] migration/multifd: move macros to multifd header Move MULTIFD_ macros to the header file so that they are accessible from other source files. Reviewed-by: Fabiano Rosas Signed-off-by: Prasad Pandit Reviewed-by: Peter Xu Message-ID: <20250411114534.3370816-2-ppandit@redhat.com> Signed-off-by: Fabiano Rosas --- migration/multifd.c | 5 ----- migration/multifd.h | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/migration/multifd.c b/migration/multifd.c index 86c83e43c0..ec108af624 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -36,11 +36,6 @@ #include "io/channel-socket.h" #include "yank_functions.h" -/* Multiple fd's */ - -#define MULTIFD_MAGIC 0x11223344U -#define MULTIFD_VERSION 1 - typedef struct { uint32_t magic; uint32_t version; diff --git a/migration/multifd.h b/migration/multifd.h index 2d337e7b3b..9b6d81e7ed 100644 --- a/migration/multifd.h +++ b/migration/multifd.h @@ -49,6 +49,11 @@ bool multifd_queue_page(RAMBlock *block, ram_addr_t offset); bool multifd_recv(void); MultiFDRecvData *multifd_get_recv_data(void); +/* Multiple fd's */ + +#define MULTIFD_MAGIC 0x11223344U +#define MULTIFD_VERSION 1 + /* Multifd Compression flags */ #define MULTIFD_FLAG_SYNC (1 << 0) From 00f3fcef1981eb23f98b956d9cda2df528bfef40 Mon Sep 17 00:00:00 2001 From: Prasad Pandit Date: Fri, 11 Apr 2025 17:15:29 +0530 Subject: [PATCH 0589/2760] migration: refactor channel discovery mechanism The various logical migration channels don't have a standardized way of advertising themselves and their connections may be seen out of order by the migration destination. When a new connection arrives, the incoming migration currently make use of heuristics to determine which channel it belongs to. The next few patches will need to change how the multifd and postcopy capabilities interact and that affects the channel discovery heuristic. Refactor the channel discovery heuristic to make it less opaque and simplify the subsequent patches. Signed-off-by: Prasad Pandit Reviewed-by: Fabiano Rosas Message-ID: <20250411114534.3370816-3-ppandit@redhat.com> Signed-off-by: Fabiano Rosas --- migration/migration.c | 130 +++++++++++++++++++++++------------------- 1 file changed, 70 insertions(+), 60 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 54fce997aa..f18cadcc5e 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -95,6 +95,9 @@ enum mig_rp_message_type { MIG_RP_MSG_MAX }; +/* Migration channel types */ +enum { CH_MAIN, CH_MULTIFD, CH_POSTCOPY }; + /* When we add fault tolerance, we could have several migrations at once. For now we don't need to add dynamic creation of migration */ @@ -941,9 +944,8 @@ static void migration_incoming_setup(QEMUFile *f) { MigrationIncomingState *mis = migration_incoming_get_current(); - if (!mis->from_src_file) { - mis->from_src_file = f; - } + assert(!mis->from_src_file); + mis->from_src_file = f; qemu_file_set_blocking(f, false); } @@ -995,28 +997,19 @@ void migration_fd_process_incoming(QEMUFile *f) migration_incoming_process(); } -/* - * Returns true when we want to start a new incoming migration process, - * false otherwise. - */ -static bool migration_should_start_incoming(bool main_channel) +static bool migration_has_main_and_multifd_channels(void) { - /* Multifd doesn't start unless all channels are established */ - if (migrate_multifd()) { - return migration_has_all_channels(); + MigrationIncomingState *mis = migration_incoming_get_current(); + if (!mis->from_src_file) { + /* main channel not established */ + return false; } - /* Preempt channel only starts when the main channel is created */ - if (migrate_postcopy_preempt()) { - return main_channel; + if (migrate_multifd() && !multifd_recv_all_channels_created()) { + return false; } - /* - * For all the rest types of migration, we should only reach here when - * it's the main channel that's being created, and we should always - * proceed with this channel. - */ - assert(main_channel); + /* main and all multifd channels are established */ return true; } @@ -1025,59 +1018,81 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) MigrationIncomingState *mis = migration_incoming_get_current(); Error *local_err = NULL; QEMUFile *f; - bool default_channel = true; + uint8_t channel; uint32_t channel_magic = 0; int ret = 0; - if (migrate_multifd() && !migrate_mapped_ram() && - !migrate_postcopy_ram() && - qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_READ_MSG_PEEK)) { - /* - * With multiple channels, it is possible that we receive channels - * out of order on destination side, causing incorrect mapping of - * source channels on destination side. Check channel MAGIC to - * decide type of channel. Please note this is best effort, postcopy - * preempt channel does not send any magic number so avoid it for - * postcopy live migration. Also tls live migration already does - * tls handshake while initializing main channel so with tls this - * issue is not possible. - */ - ret = migration_channel_read_peek(ioc, (void *)&channel_magic, - sizeof(channel_magic), errp); + if (!migration_has_main_and_multifd_channels()) { + if (qio_channel_has_feature(ioc, QIO_CHANNEL_FEATURE_READ_MSG_PEEK)) { + /* + * With multiple channels, it is possible that we receive channels + * out of order on destination side, causing incorrect mapping of + * source channels on destination side. Check channel MAGIC to + * decide type of channel. Please note this is best effort, + * postcopy preempt channel does not send any magic number so + * avoid it for postcopy live migration. Also tls live migration + * already does tls handshake while initializing main channel so + * with tls this issue is not possible. + */ + ret = migration_channel_read_peek(ioc, (void *)&channel_magic, + sizeof(channel_magic), errp); + if (ret != 0) { + return; + } - if (ret != 0) { + channel_magic = be32_to_cpu(channel_magic); + if (channel_magic == QEMU_VM_FILE_MAGIC) { + channel = CH_MAIN; + } else if (channel_magic == MULTIFD_MAGIC) { + assert(migrate_multifd()); + channel = CH_MULTIFD; + } else if (!mis->from_src_file && + mis->state == MIGRATION_STATUS_POSTCOPY_PAUSED) { + /* reconnect main channel for postcopy recovery */ + channel = CH_MAIN; + } else { + error_setg(errp, "unknown channel magic: %u", channel_magic); + return; + } + } else if (mis->from_src_file && migrate_multifd()) { + /* + * Non-peekable channels like tls/file are processed as + * multifd channels when multifd is enabled. + */ + channel = CH_MULTIFD; + } else if (!mis->from_src_file) { + channel = CH_MAIN; + } else { + error_setg(errp, "non-peekable channel used without multifd"); return; } - - default_channel = (channel_magic == cpu_to_be32(QEMU_VM_FILE_MAGIC)); } else { - default_channel = !mis->from_src_file; + assert(migrate_postcopy_preempt()); + channel = CH_POSTCOPY; } if (multifd_recv_setup(errp) != 0) { return; } - if (default_channel) { + if (channel == CH_MAIN) { f = qemu_file_new_input(ioc); migration_incoming_setup(f); - } else { + } else if (channel == CH_MULTIFD) { /* Multiple connections */ - assert(migration_needs_multiple_sockets()); - if (migrate_multifd()) { - multifd_recv_new_channel(ioc, &local_err); - } else { - assert(migrate_postcopy_preempt()); - f = qemu_file_new_input(ioc); - postcopy_preempt_new_channel(mis, f); - } + multifd_recv_new_channel(ioc, &local_err); if (local_err) { error_propagate(errp, local_err); return; } + } else if (channel == CH_POSTCOPY) { + assert(!mis->postcopy_qemufile_dst); + f = qemu_file_new_input(ioc); + postcopy_preempt_new_channel(mis, f); + return; } - if (migration_should_start_incoming(default_channel)) { + if (migration_has_main_and_multifd_channels()) { /* If it's a recovery, we're done */ if (postcopy_try_recover()) { return; @@ -1094,18 +1109,13 @@ void migration_ioc_process_incoming(QIOChannel *ioc, Error **errp) */ bool migration_has_all_channels(void) { - MigrationIncomingState *mis = migration_incoming_get_current(); - - if (!mis->from_src_file) { + if (!migration_has_main_and_multifd_channels()) { return false; } - if (migrate_multifd()) { - return multifd_recv_all_channels_created(); - } - - if (migrate_postcopy_preempt()) { - return mis->postcopy_qemufile_dst != NULL; + MigrationIncomingState *mis = migration_incoming_get_current(); + if (migrate_postcopy_preempt() && !mis->postcopy_qemufile_dst) { + return false; } return true; From 1d481116015428c02f7e3635f9bc0b88b0978fdc Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 11 Apr 2025 17:15:30 +0530 Subject: [PATCH 0590/2760] migration: Add save_postcopy_prepare() savevm handler Add a savevm handler for a module to opt-in sending extra sections right before postcopy starts, and before VM is stopped. RAM will start to use this new savevm handler in the next patch to do flush and sync for multifd pages. Note that we choose to do it before VM stopped because the current only potential user is not sensitive to VM status, so doing it before VM is stopped is preferred to enlarge any postcopy downtime. It is still a bit unfortunate that we need to introduce such a new savevm handler just for the only use case, however it's so far the cleanest. Signed-off-by: Peter Xu Signed-off-by: Prasad Pandit Reviewed-by: Fabiano Rosas Message-ID: <20250411114534.3370816-4-ppandit@redhat.com> Signed-off-by: Fabiano Rosas --- include/migration/register.h | 15 +++++++++++++++ migration/migration.c | 4 ++++ migration/savevm.c | 33 +++++++++++++++++++++++++++++++++ migration/savevm.h | 1 + 4 files changed, 53 insertions(+) diff --git a/include/migration/register.h b/include/migration/register.h index c041ce32f2..b79dc81b8d 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -189,6 +189,21 @@ typedef struct SaveVMHandlers { /* This runs outside the BQL! */ + /** + * @save_postcopy_prepare + * + * This hook will be invoked on the source side right before switching + * to postcopy (before VM stopped). + * + * @f: QEMUFile where to send the data + * @opaque: Data pointer passed to register_savevm_live() + * @errp: Error** used to report error message + * + * Returns: true if succeeded, false if error occured. When false is + * returned, @errp must be set. + */ + bool (*save_postcopy_prepare)(QEMUFile *f, void *opaque, Error **errp); + /** * @state_pending_estimate * diff --git a/migration/migration.c b/migration/migration.c index f18cadcc5e..4697732bef 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -2727,6 +2727,10 @@ static int postcopy_start(MigrationState *ms, Error **errp) } } + if (!qemu_savevm_state_postcopy_prepare(ms->to_dst_file, errp)) { + return -1; + } + trace_postcopy_start(); bql_lock(); trace_postcopy_start_set_run(); diff --git a/migration/savevm.c b/migration/savevm.c index 0c12e373b4..006514c3e3 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1523,6 +1523,39 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) qemu_fflush(f); } +bool qemu_savevm_state_postcopy_prepare(QEMUFile *f, Error **errp) +{ + SaveStateEntry *se; + bool ret; + + QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { + if (!se->ops || !se->ops->save_postcopy_prepare) { + continue; + } + + if (se->ops->is_active) { + if (!se->ops->is_active(se->opaque)) { + continue; + } + } + + trace_savevm_section_start(se->idstr, se->section_id); + + save_section_header(f, se, QEMU_VM_SECTION_PART); + ret = se->ops->save_postcopy_prepare(f, se->opaque, errp); + save_section_footer(f, se); + + trace_savevm_section_end(se->idstr, se->section_id, ret); + + if (!ret) { + assert(*errp); + return false; + } + } + + return true; +} + int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) { int64_t start_ts_each, end_ts_each; diff --git a/migration/savevm.h b/migration/savevm.h index 138c39a7f9..2d5e9c7166 100644 --- a/migration/savevm.h +++ b/migration/savevm.h @@ -45,6 +45,7 @@ void qemu_savevm_state_pending_exact(uint64_t *must_precopy, void qemu_savevm_state_pending_estimate(uint64_t *must_precopy, uint64_t *can_postcopy); int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy); +bool qemu_savevm_state_postcopy_prepare(QEMUFile *f, Error **errp); void qemu_savevm_send_ping(QEMUFile *f, uint32_t value); void qemu_savevm_send_open_return_path(QEMUFile *f); int qemu_savevm_send_packaged(QEMUFile *f, const uint8_t *buf, size_t len); From ad8d82ffbb8b8034f58a570911e6e9c6328c9384 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 11 Apr 2025 17:15:31 +0530 Subject: [PATCH 0591/2760] migration/ram: Implement save_postcopy_prepare() Implement save_postcopy_prepare(), preparing for the enablement of both multifd and postcopy. Signed-off-by: Peter Xu Signed-off-by: Prasad Pandit Reviewed-by: Fabiano Rosas Message-ID: <20250411114534.3370816-5-ppandit@redhat.com> Signed-off-by: Fabiano Rosas --- migration/ram.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/migration/ram.c b/migration/ram.c index 1181a99cf6..a749e4a421 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -4398,6 +4398,42 @@ static int ram_resume_prepare(MigrationState *s, void *opaque) return 0; } +static bool ram_save_postcopy_prepare(QEMUFile *f, void *opaque, Error **errp) +{ + int ret; + + if (migrate_multifd()) { + /* + * When multifd is enabled, source QEMU needs to make sure all the + * pages queued before postcopy starts have been flushed. + * + * The load of these pages must happen before switching to postcopy. + * It's because loading of guest pages (so far) in multifd recv + * threads is still non-atomic, so the load cannot happen with vCPUs + * running on the destination side. + * + * This flush and sync will guarantee that those pages are loaded + * _before_ postcopy starts on the destination. The rationale is, + * this happens before VM stops (and before source QEMU sends all + * the rest of the postcopy messages). So when the destination QEMU + * receives the postcopy messages, it must have received the sync + * message on the main channel (either RAM_SAVE_FLAG_MULTIFD_FLUSH, + * or RAM_SAVE_FLAG_EOS), and such message would guarantee that + * all previous guest pages queued in the multifd channels are + * completely loaded. + */ + ret = multifd_ram_flush_and_sync(f); + if (ret < 0) { + error_setg(errp, "%s: multifd flush and sync failed", __func__); + return false; + } + } + + qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + + return true; +} + void postcopy_preempt_shutdown_file(MigrationState *s) { qemu_put_be64(s->postcopy_qemufile_src, RAM_SAVE_FLAG_EOS); @@ -4417,6 +4453,7 @@ static SaveVMHandlers savevm_ram_handlers = { .load_setup = ram_load_setup, .load_cleanup = ram_load_cleanup, .resume_prepare = ram_resume_prepare, + .save_postcopy_prepare = ram_save_postcopy_prepare, }; static void ram_mig_ram_block_resized(RAMBlockNotifier *n, void *host, From 115cec9d663c1a2f5a73df4a5ca02b3a676e8a2a Mon Sep 17 00:00:00 2001 From: Prasad Pandit Date: Fri, 11 Apr 2025 17:15:33 +0530 Subject: [PATCH 0592/2760] tests/qtest/migration: consolidate set capabilities Migration capabilities are set in multiple '.start_hook' functions for various tests. Instead, consolidate setting capabilities in 'migrate_start_set_capabilities()' function which is called from the 'migrate_start()' function. While simplifying the capabilities setting, it helps to declutter the qtest sources. Suggested-by: Fabiano Rosas Signed-off-by: Prasad Pandit Reviewed-by: Fabiano Rosas Message-ID: <20250411114534.3370816-7-ppandit@redhat.com> [fix open brace] Signed-off-by: Fabiano Rosas --- tests/qtest/migration/compression-tests.c | 22 +++++-- tests/qtest/migration/cpr-tests.c | 6 +- tests/qtest/migration/file-tests.c | 58 ++++++++---------- tests/qtest/migration/framework.c | 75 +++++++++++++++-------- tests/qtest/migration/framework.h | 9 ++- tests/qtest/migration/misc-tests.c | 4 +- tests/qtest/migration/postcopy-tests.c | 8 ++- tests/qtest/migration/precopy-tests.c | 29 +++++---- tests/qtest/migration/tls-tests.c | 23 ++++++- 9 files changed, 150 insertions(+), 84 deletions(-) diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c index 8b58401b84..41e79f031b 100644 --- a/tests/qtest/migration/compression-tests.c +++ b/tests/qtest/migration/compression-tests.c @@ -35,6 +35,9 @@ static void test_multifd_tcp_zstd(void) { MigrateCommon args = { .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, }; test_precopy_common(&args); @@ -56,6 +59,9 @@ static void test_multifd_tcp_qatzip(void) { MigrateCommon args = { .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, .start_hook = migrate_hook_start_precopy_tcp_multifd_qatzip, }; test_precopy_common(&args); @@ -74,6 +80,9 @@ static void test_multifd_tcp_qpl(void) { MigrateCommon args = { .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, .start_hook = migrate_hook_start_precopy_tcp_multifd_qpl, }; test_precopy_common(&args); @@ -92,6 +101,9 @@ static void test_multifd_tcp_uadk(void) { MigrateCommon args = { .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, .start_hook = migrate_hook_start_precopy_tcp_multifd_uadk, }; test_precopy_common(&args); @@ -103,10 +115,6 @@ migrate_hook_start_xbzrle(QTestState *from, QTestState *to) { migrate_set_parameter_int(from, "xbzrle-cache-size", 33554432); - - migrate_set_capability(from, "xbzrle", true); - migrate_set_capability(to, "xbzrle", true); - return NULL; } @@ -118,6 +126,9 @@ static void test_precopy_unix_xbzrle(void) .listen_uri = uri, .start_hook = migrate_hook_start_xbzrle, .iterations = 2, + .start = { + .caps[MIGRATION_CAPABILITY_XBZRLE] = true, + }, /* * XBZRLE needs pages to be modified when doing the 2nd+ round * iteration to have real data pushed to the stream. @@ -146,6 +157,9 @@ static void test_multifd_tcp_zlib(void) { MigrateCommon args = { .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, .start_hook = migrate_hook_start_precopy_tcp_multifd_zlib, }; test_precopy_common(&args); diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c index 4758841824..5536e14610 100644 --- a/tests/qtest/migration/cpr-tests.c +++ b/tests/qtest/migration/cpr-tests.c @@ -24,9 +24,6 @@ static void *migrate_hook_start_mode_reboot(QTestState *from, QTestState *to) migrate_set_parameter_str(from, "mode", "cpr-reboot"); migrate_set_parameter_str(to, "mode", "cpr-reboot"); - migrate_set_capability(from, "x-ignore-shared", true); - migrate_set_capability(to, "x-ignore-shared", true); - return NULL; } @@ -39,6 +36,9 @@ static void test_mode_reboot(void) .connect_uri = uri, .listen_uri = "defer", .start_hook = migrate_hook_start_mode_reboot, + .start = { + .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true, + }, }; test_file_common(&args, true); diff --git a/tests/qtest/migration/file-tests.c b/tests/qtest/migration/file-tests.c index f260e2871d..4d78ce0855 100644 --- a/tests/qtest/migration/file-tests.c +++ b/tests/qtest/migration/file-tests.c @@ -107,15 +107,6 @@ static void test_precopy_file_offset_bad(void) test_file_common(&args, false); } -static void *migrate_hook_start_mapped_ram(QTestState *from, - QTestState *to) -{ - migrate_set_capability(from, "mapped-ram", true); - migrate_set_capability(to, "mapped-ram", true); - - return NULL; -} - static void test_precopy_file_mapped_ram_live(void) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, @@ -123,7 +114,9 @@ static void test_precopy_file_mapped_ram_live(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = migrate_hook_start_mapped_ram, + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + }, }; test_file_common(&args, false); @@ -136,26 +129,14 @@ static void test_precopy_file_mapped_ram(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = migrate_hook_start_mapped_ram, + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + }, }; test_file_common(&args, true); } -static void *migrate_hook_start_multifd_mapped_ram(QTestState *from, - QTestState *to) -{ - migrate_hook_start_mapped_ram(from, to); - - migrate_set_parameter_int(from, "multifd-channels", 4); - migrate_set_parameter_int(to, "multifd-channels", 4); - - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - - return NULL; -} - static void test_multifd_file_mapped_ram_live(void) { g_autofree char *uri = g_strdup_printf("file:%s/%s", tmpfs, @@ -163,7 +144,10 @@ static void test_multifd_file_mapped_ram_live(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + }, }; test_file_common(&args, false); @@ -176,7 +160,10 @@ static void test_multifd_file_mapped_ram(void) MigrateCommon args = { .connect_uri = uri, .listen_uri = "defer", - .start_hook = migrate_hook_start_multifd_mapped_ram, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + }, }; test_file_common(&args, true); @@ -185,8 +172,6 @@ static void test_multifd_file_mapped_ram(void) static void *migrate_hook_start_multifd_mapped_ram_dio(QTestState *from, QTestState *to) { - migrate_hook_start_multifd_mapped_ram(from, to); - migrate_set_parameter_bool(from, "direct-io", true); migrate_set_parameter_bool(to, "direct-io", true); @@ -201,6 +186,10 @@ static void test_multifd_file_mapped_ram_dio(void) .connect_uri = uri, .listen_uri = "defer", .start_hook = migrate_hook_start_multifd_mapped_ram_dio, + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, }; if (!probe_o_direct_support(tmpfs)) { @@ -246,7 +235,6 @@ static void *migrate_hook_start_multifd_mapped_ram_fdset_dio(QTestState *from, fdset_add_fds(from, file, O_WRONLY, 2, true); fdset_add_fds(to, file, O_RDONLY, 2, true); - migrate_hook_start_multifd_mapped_ram(from, to); migrate_set_parameter_bool(from, "direct-io", true); migrate_set_parameter_bool(to, "direct-io", true); @@ -261,8 +249,6 @@ static void *migrate_hook_start_multifd_mapped_ram_fdset(QTestState *from, fdset_add_fds(from, file, O_WRONLY, 2, false); fdset_add_fds(to, file, O_RDONLY, 2, false); - migrate_hook_start_multifd_mapped_ram(from, to); - return NULL; } @@ -275,6 +261,10 @@ static void test_multifd_file_mapped_ram_fdset(void) .listen_uri = "defer", .start_hook = migrate_hook_start_multifd_mapped_ram_fdset, .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, }; test_file_common(&args, true); @@ -289,6 +279,10 @@ static void test_multifd_file_mapped_ram_fdset_dio(void) .listen_uri = "defer", .start_hook = migrate_hook_start_multifd_mapped_ram_fdset_dio, .end_hook = migrate_hook_end_multifd_mapped_ram_fdset, + .start = { + .caps[MIGRATION_CAPABILITY_MAPPED_RAM] = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, }; if (!probe_o_direct_support(tmpfs)) { diff --git a/tests/qtest/migration/framework.c b/tests/qtest/migration/framework.c index 10e1d04b58..e48b80a127 100644 --- a/tests/qtest/migration/framework.c +++ b/tests/qtest/migration/framework.c @@ -30,6 +30,7 @@ #define QEMU_VM_FILE_MAGIC 0x5145564d #define QEMU_ENV_SRC "QTEST_QEMU_BINARY_SRC" #define QEMU_ENV_DST "QTEST_QEMU_BINARY_DST" +#define MULTIFD_TEST_CHANNELS 4 unsigned start_address; unsigned end_address; @@ -207,6 +208,51 @@ static QList *migrate_start_get_qmp_capabilities(const MigrateStart *args) return capabilities; } +static void migrate_start_set_capabilities(QTestState *from, QTestState *to, + MigrateStart *args) +{ + /* + * MigrationCapability_lookup and MIGRATION_CAPABILITY_ constants + * are from qapi-types-migration.h. + */ + for (uint8_t i = 0; i < MIGRATION_CAPABILITY__MAX; i++) { + if (!args->caps[i]) { + continue; + } + if (from) { + migrate_set_capability(from, + MigrationCapability_lookup.array[i], true); + } + if (to) { + migrate_set_capability(to, + MigrationCapability_lookup.array[i], true); + } + } + + /* + * Always enable migration events. Libvirt always uses it, let's try + * to mimic as closer as that. + */ + migrate_set_capability(from, "events", true); + if (!args->defer_target_connect) { + migrate_set_capability(to, "events", true); + } + + /* + * Default number of channels should be fine for most + * tests. Individual tests can override by calling + * migrate_set_parameter() directly. + */ + if (args->caps[MIGRATION_CAPABILITY_MULTIFD]) { + migrate_set_parameter_int(from, "multifd-channels", + MULTIFD_TEST_CHANNELS); + migrate_set_parameter_int(to, "multifd-channels", + MULTIFD_TEST_CHANNELS); + } + + return; +} + int migrate_start(QTestState **from, QTestState **to, const char *uri, MigrateStart *args) { @@ -379,14 +425,7 @@ int migrate_start(QTestState **from, QTestState **to, const char *uri, unlink(shmem_path); } - /* - * Always enable migration events. Libvirt always uses it, let's try - * to mimic as closer as that. - */ - migrate_set_capability(*from, "events", true); - if (!args->defer_target_connect) { - migrate_set_capability(*to, "events", true); - } + migrate_start_set_capabilities(*from, *to, args); return 0; } @@ -432,6 +471,10 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, { QTestState *from, *to; + /* set postcopy capabilities */ + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME] = true; + args->start.caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true; + if (migrate_start(&from, &to, "defer", &args->start)) { return -1; } @@ -440,17 +483,7 @@ static int migrate_postcopy_prepare(QTestState **from_ptr, args->postcopy_data = args->start_hook(from, to); } - migrate_set_capability(from, "postcopy-ram", true); - migrate_set_capability(to, "postcopy-ram", true); - migrate_set_capability(to, "postcopy-blocktime", true); - - if (args->postcopy_preempt) { - migrate_set_capability(from, "postcopy-preempt", true); - migrate_set_capability(to, "postcopy-preempt", true); - } - migrate_ensure_non_converge(from); - migrate_prepare_for_dirty_mem(from); qtest_qmp_assert_success(to, "{ 'execute': 'migrate-incoming'," " 'arguments': { " @@ -948,15 +981,9 @@ void *migrate_hook_start_precopy_tcp_multifd_common(QTestState *from, QTestState *to, const char *method) { - migrate_set_parameter_int(from, "multifd-channels", 16); - migrate_set_parameter_int(to, "multifd-channels", 16); - migrate_set_parameter_str(from, "multifd-compression", method); migrate_set_parameter_str(to, "multifd-compression", method); - migrate_set_capability(from, "multifd", true); - migrate_set_capability(to, "multifd", true); - /* Start incoming migration from the 1st socket */ migrate_incoming_qmp(to, "tcp:127.0.0.1:0", NULL, "{}"); diff --git a/tests/qtest/migration/framework.h b/tests/qtest/migration/framework.h index e4a11870f6..01e425e64e 100644 --- a/tests/qtest/migration/framework.h +++ b/tests/qtest/migration/framework.h @@ -12,6 +12,7 @@ #define TEST_FRAMEWORK_H #include "libqtest.h" +#include #define FILE_TEST_FILENAME "migfile" #define FILE_TEST_OFFSET 0x1000 @@ -120,6 +121,13 @@ typedef struct { /* Do not connect to target monitor and qtest sockets in qtest_init */ bool defer_target_connect; + + /* + * Migration capabilities to be set in both source and + * destination. For unilateral capabilities, use + * migration_set_capabilities(). + */ + bool caps[MIGRATION_CAPABILITY__MAX]; } MigrateStart; typedef enum PostcopyRecoveryFailStage { @@ -207,7 +215,6 @@ typedef struct { /* Postcopy specific fields */ void *postcopy_data; - bool postcopy_preempt; PostcopyRecoveryFailStage postcopy_recovery_fail_stage; } MigrateCommon; diff --git a/tests/qtest/migration/misc-tests.c b/tests/qtest/migration/misc-tests.c index 2e612d9e38..54995256d8 100644 --- a/tests/qtest/migration/misc-tests.c +++ b/tests/qtest/migration/misc-tests.c @@ -98,6 +98,7 @@ static void test_ignore_shared(void) QTestState *from, *to; MigrateStart args = { .use_shmem = true, + .caps[MIGRATION_CAPABILITY_X_IGNORE_SHARED] = true, }; if (migrate_start(&from, &to, uri, &args)) { @@ -107,9 +108,6 @@ static void test_ignore_shared(void) migrate_ensure_non_converge(from); migrate_prepare_for_dirty_mem(from); - migrate_set_capability(from, "x-ignore-shared", true); - migrate_set_capability(to, "x-ignore-shared", true); - /* Wait for the first serial output from the source */ wait_for_serial("src_serial"); diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c index 982457bed1..483e3ff99f 100644 --- a/tests/qtest/migration/postcopy-tests.c +++ b/tests/qtest/migration/postcopy-tests.c @@ -39,7 +39,9 @@ static void test_postcopy_suspend(void) static void test_postcopy_preempt(void) { MigrateCommon args = { - .postcopy_preempt = true, + .start = { + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, }; test_postcopy_common(&args); @@ -73,7 +75,9 @@ static void test_postcopy_recovery_fail_reconnect(void) static void test_postcopy_preempt_recovery(void) { MigrateCommon args = { - .postcopy_preempt = true, + .start = { + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, }; test_postcopy_recovery_common(&args); diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index 565630dddf..87b0a7e8ef 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -170,23 +170,14 @@ static void test_precopy_tcp_plain(void) test_precopy_common(&args); } -static void *migrate_hook_start_switchover_ack(QTestState *from, QTestState *to) -{ - - migrate_set_capability(from, "return-path", true); - migrate_set_capability(to, "return-path", true); - - migrate_set_capability(from, "switchover-ack", true); - migrate_set_capability(to, "switchover-ack", true); - - return NULL; -} - static void test_precopy_tcp_switchover_ack(void) { MigrateCommon args = { .listen_uri = "tcp:127.0.0.1:0", - .start_hook = migrate_hook_start_switchover_ack, + .start = { + .caps[MIGRATION_CAPABILITY_RETURN_PATH] = true, + .caps[MIGRATION_CAPABILITY_SWITCHOVER_ACK] = true, + }, /* * Source VM must be running in order to consider the switchover ACK * when deciding to do switchover or not. @@ -455,6 +446,9 @@ static void test_multifd_tcp_uri_none(void) MigrateCommon args = { .listen_uri = "defer", .start_hook = migrate_hook_start_precopy_tcp_multifd, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, /* * Multifd is more complicated than most of the features, it * directly takes guest page buffers when sending, make sure @@ -470,6 +464,9 @@ static void test_multifd_tcp_zero_page_legacy(void) MigrateCommon args = { .listen_uri = "defer", .start_hook = migrate_hook_start_precopy_tcp_multifd_zero_page_legacy, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, /* * Multifd is more complicated than most of the features, it * directly takes guest page buffers when sending, make sure @@ -485,6 +482,9 @@ static void test_multifd_tcp_no_zero_page(void) MigrateCommon args = { .listen_uri = "defer", .start_hook = migrate_hook_start_precopy_tcp_multifd_no_zero_page, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, /* * Multifd is more complicated than most of the features, it * directly takes guest page buffers when sending, make sure @@ -501,6 +501,9 @@ static void test_multifd_tcp_channels_none(void) .listen_uri = "defer", .start_hook = migrate_hook_start_precopy_tcp_multifd, .live = true, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, .connect_channels = ("[ { 'channel-type': 'main'," " 'addr': { 'transport': 'socket'," " 'type': 'inet'," diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c index 2cb4a44bcd..72f44defbb 100644 --- a/tests/qtest/migration/tls-tests.c +++ b/tests/qtest/migration/tls-tests.c @@ -375,9 +375,11 @@ static void test_postcopy_tls_psk(void) static void test_postcopy_preempt_tls_psk(void) { MigrateCommon args = { - .postcopy_preempt = true, .start_hook = migrate_hook_start_tls_psk_match, .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, }; test_postcopy_common(&args); @@ -397,9 +399,11 @@ static void test_postcopy_recovery_tls_psk(void) static void test_postcopy_preempt_all(void) { MigrateCommon args = { - .postcopy_preempt = true, .start_hook = migrate_hook_start_tls_psk_match, .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, }; test_postcopy_recovery_common(&args); @@ -631,6 +635,9 @@ static void test_multifd_tcp_tls_psk_match(void) .listen_uri = "defer", .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, }; test_precopy_common(&args); } @@ -640,6 +647,7 @@ static void test_multifd_tcp_tls_psk_mismatch(void) MigrateCommon args = { .start = { .hide_stderr = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, }, .listen_uri = "defer", .start_hook = migrate_hook_start_multifd_tcp_tls_psk_mismatch, @@ -656,6 +664,9 @@ static void test_multifd_tcp_tls_x509_default_host(void) .listen_uri = "defer", .start_hook = migrate_hook_start_multifd_tls_x509_default_host, .end_hook = migrate_hook_end_tls_x509, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, }; test_precopy_common(&args); } @@ -666,6 +677,9 @@ static void test_multifd_tcp_tls_x509_override_host(void) .listen_uri = "defer", .start_hook = migrate_hook_start_multifd_tls_x509_override_host, .end_hook = migrate_hook_end_tls_x509, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, }; test_precopy_common(&args); } @@ -688,6 +702,7 @@ static void test_multifd_tcp_tls_x509_mismatch_host(void) MigrateCommon args = { .start = { .hide_stderr = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, }, .listen_uri = "defer", .start_hook = migrate_hook_start_multifd_tls_x509_mismatch_host, @@ -703,6 +718,9 @@ static void test_multifd_tcp_tls_x509_allow_anon_client(void) .listen_uri = "defer", .start_hook = migrate_hook_start_multifd_tls_x509_allow_anon_client, .end_hook = migrate_hook_end_tls_x509, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, }; test_precopy_common(&args); } @@ -712,6 +730,7 @@ static void test_multifd_tcp_tls_x509_reject_anon_client(void) MigrateCommon args = { .start = { .hide_stderr = true, + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, }, .listen_uri = "defer", .start_hook = migrate_hook_start_multifd_tls_x509_reject_anon_client, From 20d82622812d888478d04a2d0d8575d70eb5d749 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 24 Apr 2025 18:07:05 -0400 Subject: [PATCH 0593/2760] migration/postcopy: Spatial locality page hint for preempt mode The preempt mode postcopy has been introduced for a while. From latency POV, it should always win the vanilla postcopy. However there's one thing missing when preempt mode is enabled right now, which is the spatial locality hint when there're page requests from the destination side. In vanilla postcopy, as long as a page request was unqueued, it will update the PSS of the precopy background stream, so that after a page request the background thread will move the pages after whatever was requested. It's pretty much a natural behavior when there's only one channel anyway, and one scanner to send the pages. Preempt mode didn't follow that, because preempt mode has its own channel and its own PSS (which doesn't linearly scan the guest memory, but dedicated to resolve page requested from destination). So the page request process and the background migration process are completely separate. This patch adds the hint explicitly for preempt mode. With that, whenever the preempt mode receives a page request on the source, it will service the remote page fault in the return path, then it'll provide a hint to the background thread so that we'll start sending the pages right after the requested ones in the background, assuming the follow up pages have a higher chance to be accessed later. NOTE: since the background migration thread and return path thread run completely concurrently, it doesn't always mean the hint will be applied every single time. For example, it's possible that the return path thread receives multiple page requests in a row without the background thread getting the chance to consume one. In such case, the preempt thread only provide the hint if the previous hint has been consumed. After all, there's no point queuing hints when we only have one linear scanner. This could measureably improve the simple sequential memory access pattern during postcopy (when preempt is on). For random accesses, I can measure a slight increase of remote page fault latency from ~500us -> ~600us, that could be a trade-off to have such hint mechanism, and after all that's still greatly improved comparing to vanilla postcopy on random (~10ms). The patch is verified by our QE team in a video streaming test case, to reduce the pause of the video from ~1min to a few seconds when switching over to postcopy with preempt mode. Reported-by: Xiaohui Li Tested-by: Xiaohui Li Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250424220705.195544-1-peterx@redhat.com Signed-off-by: Peter Xu --- migration/ram.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index a749e4a421..e12913b43e 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -91,6 +91,36 @@ XBZRLECacheStats xbzrle_counters; +/* + * This structure locates a specific location of a guest page. In QEMU, + * it's described in a tuple of (ramblock, offset). + */ +struct PageLocation { + RAMBlock *block; + unsigned long offset; +}; +typedef struct PageLocation PageLocation; + +/** + * PageLocationHint: describes a hint to a page location + * + * @valid set if the hint is vaild and to be consumed + * @location: the hint content + * + * In postcopy preempt mode, the urgent channel may provide hints to the + * background channel, so that QEMU source can try to migrate whatever is + * right after the requested urgent pages. + * + * This is based on the assumption that the VM (already running on the + * destination side) tends to access the memory with spatial locality. + * This is also the default behavior of vanilla postcopy (preempt off). + */ +struct PageLocationHint { + bool valid; + PageLocation location; +}; +typedef struct PageLocationHint PageLocationHint; + /* used by the search for pages to send */ struct PageSearchStatus { /* The migration channel used for a specific host page */ @@ -395,6 +425,13 @@ struct RAMState { * RAM migration. */ unsigned int postcopy_bmap_sync_requested; + /* + * Page hint during postcopy when preempt mode is on. Return path + * thread sets it, while background migration thread consumes it. + * + * Protected by @bitmap_mutex. + */ + PageLocationHint page_hint; }; typedef struct RAMState RAMState; @@ -2019,6 +2056,21 @@ static void pss_host_page_finish(PageSearchStatus *pss) pss->host_page_start = pss->host_page_end = 0; } +static void ram_page_hint_update(RAMState *rs, PageSearchStatus *pss) +{ + PageLocationHint *hint = &rs->page_hint; + + /* If there's a pending hint not consumed, don't bother */ + if (hint->valid) { + return; + } + + /* Provide a hint to the background stream otherwise */ + hint->location.block = pss->block; + hint->location.offset = pss->page; + hint->valid = true; +} + /* * Send an urgent host page specified by `pss'. Need to be called with * bitmap_mutex held. @@ -2064,6 +2116,7 @@ static int ram_save_host_page_urgent(PageSearchStatus *pss) /* For urgent requests, flush immediately if sent */ if (sent) { qemu_fflush(pss->pss_channel); + ram_page_hint_update(rs, pss); } return ret; } @@ -2151,6 +2204,30 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) return (res < 0 ? res : pages); } +static bool ram_page_hint_valid(RAMState *rs) +{ + /* There's only page hint during postcopy preempt mode */ + if (!postcopy_preempt_active()) { + return false; + } + + return rs->page_hint.valid; +} + +static void ram_page_hint_collect(RAMState *rs, RAMBlock **block, + unsigned long *page) +{ + PageLocationHint *hint = &rs->page_hint; + + assert(hint->valid); + + *block = hint->location.block; + *page = hint->location.offset; + + /* Mark the hint consumed */ + hint->valid = false; +} + /** * ram_find_and_save_block: finds a dirty page and sends it to f * @@ -2167,6 +2244,8 @@ static int ram_save_host_page(RAMState *rs, PageSearchStatus *pss) static int ram_find_and_save_block(RAMState *rs) { PageSearchStatus *pss = &rs->pss[RAM_CHANNEL_PRECOPY]; + unsigned long next_page; + RAMBlock *next_block; int pages = 0; /* No dirty page as there is zero RAM */ @@ -2186,7 +2265,14 @@ static int ram_find_and_save_block(RAMState *rs) rs->last_page = 0; } - pss_init(pss, rs->last_seen_block, rs->last_page); + if (ram_page_hint_valid(rs)) { + ram_page_hint_collect(rs, &next_block, &next_page); + } else { + next_block = rs->last_seen_block; + next_page = rs->last_page; + } + + pss_init(pss, next_block, next_page); while (true){ if (!get_queued_page(rs, pss)) { @@ -2319,6 +2405,13 @@ static void ram_save_cleanup(void *opaque) ram_state_cleanup(rsp); } +static void ram_page_hint_reset(PageLocationHint *hint) +{ + hint->location.block = NULL; + hint->location.offset = 0; + hint->valid = false; +} + static void ram_state_reset(RAMState *rs) { int i; @@ -2331,6 +2424,8 @@ static void ram_state_reset(RAMState *rs) rs->last_page = 0; rs->last_version = ram_list.version; rs->xbzrle_started = false; + + ram_page_hint_reset(&rs->page_hint); } #define MAX_WAIT 50 /* ms, half buffered_file limit */ From 0bafd6e9cbcdb1d857240f9f8c5df98a472b1b27 Mon Sep 17 00:00:00 2001 From: Jack Wang Date: Wed, 2 Apr 2025 07:13:06 +0200 Subject: [PATCH 0594/2760] migration/rdma: Remove qemu_rdma_broken_ipv6_kernel I hit following error which testing migration in pure RoCE env: "-incoming rdma:[::]:8089: RDMA ERROR: You only have RoCE / iWARP devices in your systems and your management software has specified '[::]', but IPv6 over RoCE / iWARP is not supported in Linux.#012'." In our setup, we use rdma bind on ipv6 on target host, while connect from source with ipv4, remove the qemu_rdma_broken_ipv6_kernel, migration just work fine. Checking the git history, the function was added since introducing of rdma migration, which is more than 10 years ago. linux-rdma has improved support on RoCE/iWARP for ipv6 over past years. There are a few fixes back in 2016 seems related to the issue, eg: aeb76df46d11 ("IB/core: Set routable RoCE gid type for ipv4/ipv6 networks") other fixes back in 2018, eg: 052eac6eeb56 RDMA/cma: Update RoCE multicast routines to use net namespace 8d20a1f0ecd5 RDMA/cma: Fix rdma_cm raw IB path setting for RoCE 9327c7afdce3 RDMA/cma: Provide a function to set RoCE path record L2 parameters 5c181bda77f4 RDMA/cma: Set default GID type as RoCE when resolving RoCE route 3c7f67d1880d IB/cma: Fix default RoCE type setting be1d325a3358 IB/core: Set RoCEv2 MGID according to spec 63a5f483af0e IB/cma: Set default gid type to RoCEv2 So remove the outdated function and it's usage. Cc: Peter Xu Cc: Li Zhijian Cc: Yu Zhang Cc: Fabiano Rosas Cc: qemu-devel@nongnu.org Cc: linux-rdma@vger.kernel.org Cc: michael@flatgalaxy.com Signed-off-by: Jack Wang Tested-by: Li zhijian Reviewed-by: Michael Galaxy Link: https://lore.kernel.org/r/20250402051306.6509-1-jinpu.wang@ionos.com [peterx: some cosmetic changes] Signed-off-by: Peter Xu --- migration/rdma.c | 180 ++--------------------------------------------- 1 file changed, 4 insertions(+), 176 deletions(-) diff --git a/migration/rdma.c b/migration/rdma.c index 4875ca1987..2d839fce6c 100644 --- a/migration/rdma.c +++ b/migration/rdma.c @@ -767,149 +767,6 @@ static void qemu_rdma_dump_gid(const char *who, struct rdma_cm_id *id) trace_qemu_rdma_dump_gid(who, sgid, dgid); } -/* - * As of now, IPv6 over RoCE / iWARP is not supported by linux. - * We will try the next addrinfo struct, and fail if there are - * no other valid addresses to bind against. - * - * If user is listening on '[::]', then we will not have a opened a device - * yet and have no way of verifying if the device is RoCE or not. - * - * In this case, the source VM will throw an error for ALL types of - * connections (both IPv4 and IPv6) if the destination machine does not have - * a regular infiniband network available for use. - * - * The only way to guarantee that an error is thrown for broken kernels is - * for the management software to choose a *specific* interface at bind time - * and validate what time of hardware it is. - * - * Unfortunately, this puts the user in a fix: - * - * If the source VM connects with an IPv4 address without knowing that the - * destination has bound to '[::]' the migration will unconditionally fail - * unless the management software is explicitly listening on the IPv4 - * address while using a RoCE-based device. - * - * If the source VM connects with an IPv6 address, then we're OK because we can - * throw an error on the source (and similarly on the destination). - * - * But in mixed environments, this will be broken for a while until it is fixed - * inside linux. - * - * We do provide a *tiny* bit of help in this function: We can list all of the - * devices in the system and check to see if all the devices are RoCE or - * Infiniband. - * - * If we detect that we have a *pure* RoCE environment, then we can safely - * thrown an error even if the management software has specified '[::]' as the - * bind address. - * - * However, if there is are multiple hetergeneous devices, then we cannot make - * this assumption and the user just has to be sure they know what they are - * doing. - * - * Patches are being reviewed on linux-rdma. - */ -static int qemu_rdma_broken_ipv6_kernel(struct ibv_context *verbs, Error **errp) -{ - /* This bug only exists in linux, to our knowledge. */ -#ifdef CONFIG_LINUX - struct ibv_port_attr port_attr; - - /* - * Verbs are only NULL if management has bound to '[::]'. - * - * Let's iterate through all the devices and see if there any pure IB - * devices (non-ethernet). - * - * If not, then we can safely proceed with the migration. - * Otherwise, there are no guarantees until the bug is fixed in linux. - */ - if (!verbs) { - int num_devices; - struct ibv_device **dev_list = ibv_get_device_list(&num_devices); - bool roce_found = false; - bool ib_found = false; - - for (int x = 0; x < num_devices; x++) { - verbs = ibv_open_device(dev_list[x]); - /* - * ibv_open_device() is not documented to set errno. If - * it does, it's somebody else's doc bug. If it doesn't, - * the use of errno below is wrong. - * TODO Find out whether ibv_open_device() sets errno. - */ - if (!verbs) { - if (errno == EPERM) { - continue; - } else { - error_setg_errno(errp, errno, - "could not open RDMA device context"); - return -1; - } - } - - if (ibv_query_port(verbs, 1, &port_attr)) { - ibv_close_device(verbs); - error_setg(errp, - "RDMA ERROR: Could not query initial IB port"); - return -1; - } - - if (port_attr.link_layer == IBV_LINK_LAYER_INFINIBAND) { - ib_found = true; - } else if (port_attr.link_layer == IBV_LINK_LAYER_ETHERNET) { - roce_found = true; - } - - ibv_close_device(verbs); - - } - - if (roce_found) { - if (ib_found) { - warn_report("migrations may fail:" - " IPv6 over RoCE / iWARP in linux" - " is broken. But since you appear to have a" - " mixed RoCE / IB environment, be sure to only" - " migrate over the IB fabric until the kernel " - " fixes the bug."); - } else { - error_setg(errp, "RDMA ERROR: " - "You only have RoCE / iWARP devices in your systems" - " and your management software has specified '[::]'" - ", but IPv6 over RoCE / iWARP is not supported in Linux."); - return -1; - } - } - - return 0; - } - - /* - * If we have a verbs context, that means that some other than '[::]' was - * used by the management software for binding. In which case we can - * actually warn the user about a potentially broken kernel. - */ - - /* IB ports start with 1, not 0 */ - if (ibv_query_port(verbs, 1, &port_attr)) { - error_setg(errp, "RDMA ERROR: Could not query initial IB port"); - return -1; - } - - if (port_attr.link_layer == IBV_LINK_LAYER_ETHERNET) { - error_setg(errp, "RDMA ERROR: " - "Linux kernel's RoCE / iWARP does not support IPv6 " - "(but patches on linux-rdma in progress)"); - return -1; - } - -#endif - - return 0; -} - /* * Figure out which RDMA device corresponds to the requested IP hostname * Also create the initial connection manager identifiers for opening @@ -917,7 +774,6 @@ static int qemu_rdma_broken_ipv6_kernel(struct ibv_context *verbs, Error **errp) */ static int qemu_rdma_resolve_host(RDMAContext *rdma, Error **errp) { - Error *err = NULL; int ret; struct rdma_addrinfo *res; char port_str[16]; @@ -953,9 +809,8 @@ static int qemu_rdma_resolve_host(RDMAContext *rdma, Error **errp) goto err_resolve_get_addr; } - /* Try all addresses, saving the first error in @err */ + /* Try all addresses, exit loop on first success of resolving address */ for (struct rdma_addrinfo *e = res; e != NULL; e = e->ai_next) { - Error **local_errp = err ? NULL : &err; inet_ntop(e->ai_family, &((struct sockaddr_in *) e->ai_dst_addr)->sin_addr, ip, sizeof ip); @@ -964,25 +819,12 @@ static int qemu_rdma_resolve_host(RDMAContext *rdma, Error **errp) ret = rdma_resolve_addr(rdma->cm_id, NULL, e->ai_dst_addr, RDMA_RESOLVE_TIMEOUT_MS); if (ret >= 0) { - if (e->ai_family == AF_INET6) { - ret = qemu_rdma_broken_ipv6_kernel(rdma->cm_id->verbs, - local_errp); - if (ret < 0) { - continue; - } - } - error_free(err); goto route; } } rdma_freeaddrinfo(res); - if (err) { - error_propagate(errp, err); - } else { - error_setg(errp, "RDMA ERROR: could not resolve address %s", - rdma->host); - } + error_setg(errp, "RDMA ERROR: could not resolve address %s", rdma->host); goto err_resolve_get_addr; route: @@ -2611,7 +2453,6 @@ static int qemu_rdma_connect(RDMAContext *rdma, bool return_path, static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) { - Error *err = NULL; int ret; struct rdma_cm_id *listen_id; char ip[40] = "unknown"; @@ -2661,9 +2502,8 @@ static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) goto err_dest_init_bind_addr; } - /* Try all addresses, saving the first error in @err */ + /* Try all addresses */ for (e = res; e != NULL; e = e->ai_next) { - Error **local_errp = err ? NULL : &err; inet_ntop(e->ai_family, &((struct sockaddr_in *) e->ai_dst_addr)->sin_addr, ip, sizeof ip); @@ -2672,24 +2512,12 @@ static int qemu_rdma_dest_init(RDMAContext *rdma, Error **errp) if (ret < 0) { continue; } - if (e->ai_family == AF_INET6) { - ret = qemu_rdma_broken_ipv6_kernel(listen_id->verbs, - local_errp); - if (ret < 0) { - continue; - } - } - error_free(err); break; } rdma_freeaddrinfo(res); if (!e) { - if (err) { - error_propagate(errp, err); - } else { - error_setg(errp, "RDMA ERROR: Error: could not rdma_bind_addr!"); - } + error_setg(errp, "RDMA ERROR: Error: could not rdma_bind_addr!"); goto err_dest_init_bind_addr; } From e674fedbd1fd9953bc30026670aba6779848280a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 29 Apr 2025 17:21:39 +0200 Subject: [PATCH 0595/2760] scripts/vmstate-static-checker.py: Allow new name for ghes_addr_le field ghes_addr_le has been renamed to hw_error_le in commit 652f6d86cbb ("acpi/ghes: better name the offset of the hardware error firmware"). Adjust the checker script to allow that changed field name. Signed-off-by: Thomas Huth Reviewed-by: Peter Xu Link: https://lore.kernel.org/r/20250429152141.294380-3-thuth@redhat.com Signed-off-by: Peter Xu --- scripts/vmstate-static-checker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/vmstate-static-checker.py b/scripts/vmstate-static-checker.py index 9c0e6b81f2..25aca839a0 100755 --- a/scripts/vmstate-static-checker.py +++ b/scripts/vmstate-static-checker.py @@ -42,6 +42,7 @@ def check_fields_match(name, s_field, d_field): # Some fields changed names between qemu versions. This list # is used to allow such changes in each section / description. changed_names = { + 'acpi-ghes': ['ghes_addr_le', 'hw_error_le'], 'apic': ['timer', 'timer_expiry'], 'e1000': ['dev', 'parent_obj'], 'ehci': ['dev', 'pcidev'], From 6f8e6aed81277ec14d5a5dcafdd00dadf7ac465c Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 14 Apr 2025 22:49:35 +0800 Subject: [PATCH 0596/2760] rust/vmstate: Add support for field_exists checks Unfortunately, at present it's not possible to have a const "with_exist_check" method to append test_fn after vmstate_struct (due to error on "constant functions cannot evaluate destructors" for `F`). Before the vmstate builder, the only way to support "test_fn" is to extend vmstate_struct macro to add the such new optional member (and fortunately, Rust can still parse the current expansion!). Abstract the previous callback implementation of vmstate_validate into a separate macro, and moves it before vmstate_struct for vmstate_struct to call. Note that there's no need to add any extra flag for a new test_fn added in the VMStateField. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250414144943.1112885-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 70 +++++++++++++++++++----------------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 1b2b12eadd..8c4a5bee3c 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -200,13 +200,14 @@ pub const fn vmstate_varray_flag(_: PhantomData) -> VMStateFlags /// and [`impl_vmstate_forward!`](crate::impl_vmstate_forward) help with this. #[macro_export] macro_rules! vmstate_of { - ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(,)?) => { + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])? $(, $test_fn:expr)? $(,)?) => { $crate::bindings::VMStateField { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() .as_ptr() as *const ::std::os::raw::c_char, offset: $crate::offset_of!($struct_name, $field_name), $(num_offset: $crate::offset_of!($struct_name, $num),)? + $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? // The calls to `call_func_with_field!` are the magic that // computes most of the VMStateField from the type of the field. info: $crate::info_enum_to_ref!($crate::call_func_with_field!( @@ -435,6 +436,38 @@ macro_rules! vmstate_unused { }}; } +pub extern "C" fn rust_vms_test_field_exists FnCall<(&'a T, u8), bool>>( + opaque: *mut c_void, + version_id: c_int, +) -> bool { + // SAFETY: the opaque was passed as a reference to `T`. + let owner: &T = unsafe { &*(opaque.cast::()) }; + let version: u8 = version_id.try_into().unwrap(); + F::call((owner, version)) +} + +pub type VMSFieldExistCb = unsafe extern "C" fn( + opaque: *mut std::os::raw::c_void, + version_id: std::os::raw::c_int, +) -> bool; + +#[macro_export] +macro_rules! vmstate_exist_fn { + ($struct_name:ty, $test_fn:expr) => {{ + const fn test_cb_builder__ $crate::callbacks::FnCall<(&'a T, u8), bool>>( + _phantom: ::core::marker::PhantomData, + ) -> $crate::vmstate::VMSFieldExistCb { + let _: () = F::ASSERT_IS_SOME; + $crate::vmstate::rust_vms_test_field_exists:: + } + + const fn phantom__(_: &T) -> ::core::marker::PhantomData { + ::core::marker::PhantomData + } + Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn))) + }}; +} + // FIXME: including the `vmsd` field in a `const` is not possible without // the const_refs_static feature (stabilized in Rust 1.83.0). Without it, // it is not possible to use VMS_STRUCT in a transparent manner using @@ -445,7 +478,7 @@ macro_rules! vmstate_unused { #[doc(alias = "VMSTATE_STRUCT")] #[macro_export] macro_rules! vmstate_struct { - ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(,)?) => { + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?, $vmsd:expr, $type:ty $(, $test_fn:expr)? $(,)?) => { $crate::bindings::VMStateField { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() @@ -458,6 +491,7 @@ macro_rules! vmstate_struct { size: ::core::mem::size_of::<$type>(), flags: $crate::bindings::VMStateFlags::VMS_STRUCT, vmsd: $vmsd, + $(field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn),)? ..$crate::zeroable::Zeroable::ZERO } $(.with_varray_flag_unchecked( $crate::call_func_with_field!( @@ -514,43 +548,13 @@ macro_rules! vmstate_fields { }} } -pub extern "C" fn rust_vms_test_field_exists FnCall<(&'a T, u8), bool>>( - opaque: *mut c_void, - version_id: c_int, -) -> bool { - let owner: &T = unsafe { &*(opaque.cast::()) }; - let version: u8 = version_id.try_into().unwrap(); - // SAFETY: the opaque was passed as a reference to `T`. - F::call((owner, version)) -} - -pub type VMSFieldExistCb = unsafe extern "C" fn( - opaque: *mut std::os::raw::c_void, - version_id: std::os::raw::c_int, -) -> bool; - #[doc(alias = "VMSTATE_VALIDATE")] #[macro_export] macro_rules! vmstate_validate { ($struct_name:ty, $test_name:expr, $test_fn:expr $(,)?) => { $crate::bindings::VMStateField { name: ::std::ffi::CStr::as_ptr($test_name), - field_exists: { - const fn test_cb_builder__< - T, - F: for<'a> $crate::callbacks::FnCall<(&'a T, u8), bool>, - >( - _phantom: ::core::marker::PhantomData, - ) -> $crate::vmstate::VMSFieldExistCb { - let _: () = F::ASSERT_IS_SOME; - $crate::vmstate::rust_vms_test_field_exists:: - } - - const fn phantom__(_: &T) -> ::core::marker::PhantomData { - ::core::marker::PhantomData - } - Some(test_cb_builder__::<$struct_name, _>(phantom__(&$test_fn))) - }, + field_exists: $crate::vmstate_exist_fn!($struct_name, $test_fn), flags: $crate::bindings::VMStateFlags( $crate::bindings::VMStateFlags::VMS_MUST_EXIST.0 | $crate::bindings::VMStateFlags::VMS_ARRAY.0, From 756ea88fff96252dd76cf388fb8678cb97dd2658 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 2 May 2025 09:45:38 +0200 Subject: [PATCH 0597/2760] vmstate: support varray for vmstate_clock! Make vmstate_struct and vmstate_clock more similar; they are basically the same thing, except for the clock case having a built-in VMStateDescription. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/vmstate.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 8c4a5bee3c..9ae97c389c 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -507,7 +507,7 @@ macro_rules! vmstate_struct { #[doc(alias = "VMSTATE_CLOCK")] #[macro_export] macro_rules! vmstate_clock { - ($struct_name:ty, $field_name:ident) => {{ + ($struct_name:ty, $field_name:ident $([0 .. $num:ident $(* $factor:expr)?])?) => {{ $crate::bindings::VMStateField { name: ::core::concat!(::core::stringify!($field_name), "\0") .as_bytes() @@ -516,7 +516,7 @@ macro_rules! vmstate_clock { $crate::assert_field_type!( $struct_name, $field_name, - $crate::qom::Owned<$crate::qdev::Clock> + $crate::qom::Owned<$crate::qdev::Clock> $(, num = $num)? ); $crate::offset_of!($struct_name, $field_name) }, @@ -527,7 +527,14 @@ macro_rules! vmstate_clock { ), vmsd: unsafe { ::core::ptr::addr_of!($crate::bindings::vmstate_clock) }, ..$crate::zeroable::Zeroable::ZERO - } + } $(.with_varray_flag_unchecked( + $crate::call_func_with_field!( + $crate::vmstate::vmstate_varray_flag, + $struct_name, + $num + ) + ) + $(.with_varray_multiply($factor))?)? }}; } From fff99a88be34c82270c918b711f8fc3affd504d5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 2 May 2025 10:56:06 +0200 Subject: [PATCH 0598/2760] rust: assertions: Support index field wrapped in BqlCell Currently, if the `num` field of a varray is not a numeric type, such as being placed in a wrapper, the array variant of assert_field_type will fail the check. HPET currently wraps num_timers in BqlCell<>. Although BqlCell<> is not necessary from strictly speaking, it makes sense for vmstate to respect BqlCell. The failure of assert_field_type is because it cannot convert BqlCell into usize for use as the index. Use a constant 0 instead for the index, by avoiding $(...)? and extracting the common parts of assert_field_type! into an internal case. Commit message based on a patch by Zhao Liu . Link: https://lore.kernel.org/r/20250414144943.1112885-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/assertions.rs | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/rust/qemu-api/src/assertions.rs b/rust/qemu-api/src/assertions.rs index eb12e9499a..a2d38c877d 100644 --- a/rust/qemu-api/src/assertions.rs +++ b/rust/qemu-api/src/assertions.rs @@ -78,33 +78,26 @@ macro_rules! assert_same_type { /// ``` #[macro_export] macro_rules! assert_field_type { - ($t:ty, $i:tt, $ti:ty) => { + (@internal $param_name:ident, $ti:ty, $t:ty, $($field:tt)*) => { const _: () = { #[allow(unused)] - fn assert_field_type(v: $t) { - fn types_must_be_equal(_: T) + fn assert_field_type($param_name: &$t) { + fn types_must_be_equal(_: &T) where T: $crate::assertions::EqType, { } - types_must_be_equal::<_, $ti>(v.$i); + types_must_be_equal::<_, $ti>(&$($field)*); } }; }; + ($t:ty, $i:tt, $ti:ty) => { + $crate::assert_field_type!(@internal v, $ti, $t, v.$i); + }; + ($t:ty, $i:tt, $ti:ty, num = $num:ident) => { - const _: () = { - #[allow(unused)] - fn assert_field_type(v: $t) { - fn types_must_be_equal(_: T) - where - T: $crate::assertions::EqType, - { - } - let index: usize = v.$num.try_into().unwrap(); - types_must_be_equal::<_, &$ti>(&v.$i[index]); - } - }; + $crate::assert_field_type!(@internal v, $ti, $t, v.$i[0]); }; } From cff1ec67509cacc2ea30d19ba61fa0ab0772e119 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 14 Apr 2025 22:49:37 +0800 Subject: [PATCH 0599/2760] rust/vmstate_test: Test varray with num field wrapped in BqlCell Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250414144943.1112885-4-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/tests/vmstate_tests.rs | 41 ++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index 8b93492a68..f7a93117e1 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -28,7 +28,7 @@ const FOO_ARRAY_MAX: usize = 3; // - VMSTATE_VARRAY_UINT16_UNSAFE // - VMSTATE_VARRAY_MULTIPLY #[repr(C)] -#[derive(qemu_api_macros::offsets)] +#[derive(Default, qemu_api_macros::offsets)] struct FooA { arr: [u8; FOO_ARRAY_MAX], num: u16, @@ -147,8 +147,9 @@ fn test_vmstate_varray_multiply() { // - VMSTATE_STRUCT_VARRAY_UINT8 // - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32 // - VMSTATE_ARRAY +// - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn #[repr(C)] -#[derive(qemu_api_macros::offsets)] +#[derive(Default, qemu_api_macros::offsets)] struct FooB { arr_a: [FooA; FOO_ARRAY_MAX], num_a: u8, @@ -158,6 +159,12 @@ struct FooB { val: bool, // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test. arr_i64: [i64; FOO_ARRAY_MAX], + arr_a_wrap: [FooA; FOO_ARRAY_MAX], + num_a_wrap: BqlCell, +} + +fn validate_foob(_state: &FooB, _version_id: u8) -> bool { + true } static VMSTATE_FOOB: VMStateDescription = VMStateDescription { @@ -170,13 +177,14 @@ static VMSTATE_FOOB: VMStateDescription = VMStateDescription { vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1), vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2), vmstate_of!(FooB, arr_i64), + vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob), }, ..Zeroable::ZERO }; #[test] fn test_vmstate_bool_v() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V) assert_eq!( @@ -196,7 +204,7 @@ fn test_vmstate_bool_v() { #[test] fn test_vmstate_uint64() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64) assert_eq!( @@ -216,7 +224,7 @@ fn test_vmstate_uint64() { #[test] fn test_vmstate_struct_varray_uint8() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to // VMSTATE_STRUCT_VARRAY_UINT8) @@ -240,7 +248,7 @@ fn test_vmstate_struct_varray_uint8() { #[test] fn test_vmstate_struct_varray_uint32_multiply() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32) @@ -266,7 +274,7 @@ fn test_vmstate_struct_varray_uint32_multiply() { #[test] fn test_vmstate_macro_array() { - let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 6) }; + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to // VMSTATE_ARRAY) @@ -283,9 +291,26 @@ fn test_vmstate_macro_array() { assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY); assert!(foo_fields[4].vmsd.is_null()); assert!(foo_fields[4].field_exists.is_none()); +} + +#[test] +fn test_vmstate_struct_varray_uint8_wrapper() { + let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; + let mut foo_b: FooB = Default::default(); + let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::(); + + // 6th VMStateField ("arr_a_wrap") in VMSTATE_FOOB (corresponding to + // VMSTATE_STRUCT_VARRAY_UINT8). Other fields are checked in + // test_vmstate_struct_varray_uint8. + assert_eq!( + unsafe { CStr::from_ptr(foo_fields[5].name) }.to_bytes_with_nul(), + b"arr_a_wrap\0" + ); + assert_eq!(foo_fields[5].num_offset, 228); + assert!(unsafe { foo_fields[5].field_exists.unwrap()(foo_b_p, 0) }); // The last VMStateField in VMSTATE_FOOB. - assert_eq!(foo_fields[5].flags, VMStateFlags::VMS_END); + assert_eq!(foo_fields[6].flags, VMStateFlags::VMS_END); } // =========================== Test VMSTATE_FOOC =========================== From 8d9502b4e947a9cfcf1d1940cc70d1579b53ecaf Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 14 Apr 2025 22:49:39 +0800 Subject: [PATCH 0600/2760] rust/timer: Define NANOSECONDS_PER_SECOND binding as u64 NANOSECONDS_PER_SECOND is often used in operations with get_ns(), which currently returns a u64. Therefore, define a new NANOSECONDS_PER_SECOND binding is with u64 type to eliminate unnecessary type conversions (from u32 to u64). Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250414144943.1112885-6-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/timer.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs index f0b04ef95d..e769f8bc91 100644 --- a/rust/qemu-api/src/timer.rs +++ b/rust/qemu-api/src/timer.rs @@ -121,3 +121,5 @@ impl ClockType { pub const CLOCK_VIRTUAL: ClockType = ClockType { id: QEMUClockType::QEMU_CLOCK_VIRTUAL, }; + +pub const NANOSECONDS_PER_SECOND: u64 = 1000000000; From db46654af893abed53dce35ebab86056ac9b3004 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 14 Apr 2025 22:49:42 +0800 Subject: [PATCH 0601/2760] rust/hpet: Support migration Based on commit 1433e38cc8 ("hpet: do not overwrite properties on post_load"), add the basic migration support to Rust HPET. The current migration implementation introduces multiple unsafe callbacks. Before the vmstate builder, one possible cleanup approach is to wrap callbacks in the vmstate binding using a method similar to the vmstate_exist_fn macro. However, this approach would also create a lot of repetitive code (since vmstate has so many callbacks: pre_load, post_load, pre_save, post_save, needed and dev_unplug_pending). Although it would be cleaner, it would somewhat deviate from the path of the vmstate builder. Therefore, firstly focus on completing the functionality of HPET, and those current unsafe callbacks can at least clearly indicate the needed functionality of vmstate. The next step is to consider refactoring vmstate to move towards the vmstate builder direction. Additionally, update rust.rst about Rust HPET can support migration. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250414144943.1112885-9-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 3 +- rust/hw/timer/hpet/src/hpet.rs | 146 ++++++++++++++++++++++++++++++++- 2 files changed, 146 insertions(+), 3 deletions(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 88bdec1eb2..3cc2841d4d 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -153,8 +153,7 @@ QEMU includes four crates: .. [#issues] The ``pl011`` crate is synchronized with ``hw/char/pl011.c`` as of commit 02b1f7f61928. The ``hpet`` crate is synchronized as of - commit f32352ff9e. Both are lacking tracing functionality; ``hpet`` - is also lacking support for migration. + commit 1433e38cc8. Both are lacking tracing functionality. This section explains how to work with them. diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index cbd2ed4f6b..12de2ba59a 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -4,6 +4,7 @@ use std::{ ffi::CStr, + os::raw::{c_int, c_void}, pin::Pin, ptr::{addr_of_mut, null_mut, NonNull}, slice::from_ref, @@ -25,7 +26,10 @@ use qemu_api::{ qom::{ObjectImpl, ObjectType, ParentField}, qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, - timer::{Timer, CLOCK_VIRTUAL}, + timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, + vmstate::VMStateDescription, + vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate, + zeroable::Zeroable, }; use crate::fw_cfg::HPETFwConfig; @@ -561,6 +565,7 @@ pub struct HPETState { #[doc(alias = "timer")] timers: [BqlRefCell; HPET_MAX_TIMERS as usize], num_timers: BqlCell, + num_timers_save: BqlCell, /// Instance id (HPET timer block ID). hpet_id: BqlCell, @@ -839,6 +844,49 @@ impl HPETState { } } } + + fn pre_save(&self) -> i32 { + if self.is_hpet_enabled() { + self.counter.set(self.get_ticks()); + } + + /* + * The number of timers must match on source and destination, but it was + * also added to the migration stream. Check that it matches the value + * that was configured. + */ + self.num_timers_save.set(self.num_timers.get()); + 0 + } + + fn post_load(&self, _version_id: u8) -> i32 { + for timer in self.timers.iter().take(self.get_num_timers()) { + let mut t = timer.borrow_mut(); + + t.cmp64 = t.calculate_cmp64(t.get_state().counter.get(), t.cmp); + t.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 + .set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns()); + } + + 0 + } + + fn is_rtc_irq_level_needed(&self) -> bool { + self.rtc_irq_level.get() != 0 + } + + fn is_offset_needed(&self) -> bool { + self.is_hpet_enabled() && self.hpet_offset_saved + } + + fn validate_num_timers(&self, _version_id: u8) -> bool { + self.num_timers.get() == self.num_timers_save.get() + } } qom_isa!(HPETState: SysBusDevice, DeviceState, Object); @@ -895,11 +943,107 @@ qemu_api::declare_properties! { ), } +unsafe extern "C" fn hpet_rtc_irq_level_needed(opaque: *mut c_void) -> bool { + // SAFETY: + // the pointer is convertible to a reference + let state: &HPETState = unsafe { NonNull::new(opaque.cast::()).unwrap().as_ref() }; + state.is_rtc_irq_level_needed() +} + +unsafe extern "C" fn hpet_offset_needed(opaque: *mut c_void) -> bool { + // SAFETY: + // the pointer is convertible to a reference + let state: &HPETState = unsafe { NonNull::new(opaque.cast::()).unwrap().as_ref() }; + state.is_offset_needed() +} + +unsafe extern "C" fn hpet_pre_save(opaque: *mut c_void) -> c_int { + // SAFETY: + // the pointer is convertible to a reference + let state: &mut HPETState = + unsafe { NonNull::new(opaque.cast::()).unwrap().as_mut() }; + state.pre_save() as c_int +} + +unsafe extern "C" fn hpet_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { + // SAFETY: + // the pointer is convertible to a reference + let state: &mut HPETState = + unsafe { NonNull::new(opaque.cast::()).unwrap().as_mut() }; + let version: u8 = version_id.try_into().unwrap(); + state.post_load(version) as c_int +} + +static VMSTATE_HPET_RTC_IRQ_LEVEL: VMStateDescription = VMStateDescription { + name: c_str!("hpet/rtc_irq_level").as_ptr(), + version_id: 1, + minimum_version_id: 1, + needed: Some(hpet_rtc_irq_level_needed), + fields: vmstate_fields! { + vmstate_of!(HPETState, rtc_irq_level), + }, + ..Zeroable::ZERO +}; + +static VMSTATE_HPET_OFFSET: VMStateDescription = VMStateDescription { + name: c_str!("hpet/offset").as_ptr(), + version_id: 1, + minimum_version_id: 1, + needed: Some(hpet_offset_needed), + fields: vmstate_fields! { + vmstate_of!(HPETState, hpet_offset), + }, + ..Zeroable::ZERO +}; + +static VMSTATE_HPET_TIMER: VMStateDescription = VMStateDescription { + name: c_str!("hpet_timer").as_ptr(), + version_id: 1, + minimum_version_id: 1, + fields: vmstate_fields! { + vmstate_of!(HPETTimer, index), + vmstate_of!(HPETTimer, config), + vmstate_of!(HPETTimer, cmp), + vmstate_of!(HPETTimer, fsb), + vmstate_of!(HPETTimer, period), + vmstate_of!(HPETTimer, wrap_flag), + vmstate_of!(HPETTimer, qemu_timer), + }, + ..Zeroable::ZERO +}; + +const VALIDATE_TIMERS_NAME: &CStr = c_str!("num_timers must match"); + +static VMSTATE_HPET: VMStateDescription = VMStateDescription { + name: c_str!("hpet").as_ptr(), + version_id: 2, + minimum_version_id: 1, + pre_save: Some(hpet_pre_save), + post_load: Some(hpet_post_load), + fields: vmstate_fields! { + vmstate_of!(HPETState, config), + vmstate_of!(HPETState, int_status), + vmstate_of!(HPETState, counter), + vmstate_of!(HPETState, num_timers_save).with_version_id(2), + vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers), + vmstate_struct!(HPETState, timers[0 .. num_timers], &VMSTATE_HPET_TIMER, BqlRefCell, HPETState::validate_num_timers).with_version_id(0), + }, + subsections: vmstate_subsections! { + VMSTATE_HPET_RTC_IRQ_LEVEL, + VMSTATE_HPET_OFFSET, + }, + ..Zeroable::ZERO +}; + impl DeviceImpl for HPETState { fn properties() -> &'static [Property] { &HPET_PROPERTIES } + fn vmsd() -> Option<&'static VMStateDescription> { + Some(&VMSTATE_HPET) + } + const REALIZE: Option = Some(Self::realize); } From 7c93067fe7e44f89a720fa5f0faf180697d5ac7e Mon Sep 17 00:00:00 2001 From: Magnus Kulke Date: Tue, 29 Apr 2025 11:33:19 +0200 Subject: [PATCH 0602/2760] target/i386/emulate: remove rflags leftovers Fixes: c901905ea670 ("target/i386/emulate: remove flags_mask") In c901905ea670 rflags have been removed from `x86_decode`, but there were some leftovers. Signed-off-by: Magnus Kulke Link: https://lore.kernel.org/r/20250429093319.5010-1-magnuskulke@linux.microsoft.com Signed-off-by: Paolo Bonzini --- target/i386/emulate/x86_decode.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/target/i386/emulate/x86_decode.c b/target/i386/emulate/x86_decode.c index 7fee219687..7efa2f570e 100644 --- a/target/i386/emulate/x86_decode.c +++ b/target/i386/emulate/x86_decode.c @@ -1408,7 +1408,7 @@ struct decode_tbl _2op_inst[] = { }; struct decode_x87_tbl invl_inst_x87 = {0x0, 0, 0, 0, 0, false, false, NULL, - NULL, decode_invalid, 0}; + NULL, decode_invalid}; struct decode_x87_tbl _x87_inst[] = { {0xd8, 0, 3, X86_DECODE_CMD_FADD, 10, false, false, @@ -1456,8 +1456,7 @@ struct decode_x87_tbl _x87_inst[] = { decode_x87_modrm_st0, NULL, decode_d9_4}, {0xd9, 4, 0, X86_DECODE_CMD_INVL, 4, false, false, decode_x87_modrm_bytep, NULL, NULL}, - {0xd9, 5, 3, X86_DECODE_CMD_FLDxx, 10, false, false, NULL, NULL, NULL, - RFLAGS_MASK_NONE}, + {0xd9, 5, 3, X86_DECODE_CMD_FLDxx, 10, false, false, NULL, NULL, NULL}, {0xd9, 5, 0, X86_DECODE_CMD_FLDCW, 2, false, false, decode_x87_modrm_bytep, NULL, NULL}, @@ -1478,20 +1477,17 @@ struct decode_x87_tbl _x87_inst[] = { decode_x87_modrm_st0, NULL}, {0xda, 3, 3, X86_DECODE_CMD_FCMOV, 10, false, false, decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, - {0xda, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, - RFLAGS_MASK_NONE}, + {0xda, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL}, {0xda, 4, 0, X86_DECODE_CMD_FSUB, 4, false, false, decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, {0xda, 5, 3, X86_DECODE_CMD_FUCOM, 10, false, true, decode_x87_modrm_st0, decode_decode_x87_modrm_st0, NULL}, {0xda, 5, 0, X86_DECODE_CMD_FSUB, 4, true, false, decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, - {0xda, 6, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, - RFLAGS_MASK_NONE}, + {0xda, 6, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL}, {0xda, 6, 0, X86_DECODE_CMD_FDIV, 4, false, false, decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, - {0xda, 7, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, - RFLAGS_MASK_NONE}, + {0xda, 7, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL}, {0xda, 7, 0, X86_DECODE_CMD_FDIV, 4, true, false, decode_x87_modrm_st0, decode_x87_modrm_intp, NULL}, @@ -1511,8 +1507,7 @@ struct decode_x87_tbl _x87_inst[] = { decode_x87_modrm_intp, NULL, NULL}, {0xdb, 4, 3, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, decode_db_4}, - {0xdb, 4, 0, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL, - RFLAGS_MASK_NONE}, + {0xdb, 4, 0, X86_DECODE_CMD_INVL, 10, false, false, NULL, NULL, NULL}, {0xdb, 5, 3, X86_DECODE_CMD_FUCOMI, 10, false, false, decode_x87_modrm_st0, decode_x87_modrm_st0, NULL}, {0xdb, 5, 0, X86_DECODE_CMD_FLD, 10, false, false, From 785f945bd5ace415419d0fe4944fa5fce1bc1865 Mon Sep 17 00:00:00 2001 From: Wei Liu Date: Tue, 29 Apr 2025 06:24:51 +0000 Subject: [PATCH 0603/2760] target/i386/hvf: fix a compilation error Include exec/target_page.h to fix the following build error. x86_64-softmmu.a.p/target_i386_hvf_hvf.c.o -c ../target/i386/hvf/hvf.c ../target/i386/hvf/hvf.c:139:49: error: use of undeclared identifier 'TARGET_PAGE_SIZE' 139 | uint64_t dirty_page_start = gpa & ~(TARGET_PAGE_SIZE - 1u); | ^ ../target/i386/hvf/hvf.c:141:45: error: use of undeclared identifier 'TARGET_PAGE_SIZE' 141 | hv_vm_protect(dirty_page_start, TARGET_PAGE_SIZE, | ^ Signed-off-by: Wei Liu Link: https://lore.kernel.org/r/aBBws1ikCDfyC0RI@liuwe-devbox-ubuntu-v2.tail21d00.ts.net Signed-off-by: Paolo Bonzini --- target/i386/hvf/hvf.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 23ebf2550a..99e37a33e5 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -76,6 +76,7 @@ #include "qemu/main-loop.h" #include "qemu/accel.h" #include "target/i386/cpu.h" +#include "exec/target_page.h" static Error *invtsc_mig_blocker; From e54ef98c8a80d16158bab4341d9a898701270528 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 13 Feb 2025 19:37:07 +0100 Subject: [PATCH 0604/2760] target/i386: do not trigger IRQ shadow for LSS Because LSS need not trigger an IRQ shadow, gen_movl_seg can't just use the destination register to decide whether to inhibit IRQs. Add an argument. Cc: qemu-stable@nongnu.org Signed-off-by: Paolo Bonzini --- target/i386/tcg/emit.c.inc | 4 ++-- target/i386/tcg/translate.c | 27 ++++++++++++++++----------- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/target/i386/tcg/emit.c.inc b/target/i386/tcg/emit.c.inc index e3166e70a5..1a7fab9333 100644 --- a/target/i386/tcg/emit.c.inc +++ b/target/i386/tcg/emit.c.inc @@ -342,7 +342,7 @@ static void gen_writeback(DisasContext *s, X86DecodedInsn *decode, int opn, TCGv break; case X86_OP_SEG: /* Note that gen_movl_seg takes care of interrupt shadow and TF. */ - gen_movl_seg(s, op->n, s->T0); + gen_movl_seg(s, op->n, v, op->n == R_SS); break; case X86_OP_INT: if (op->has_ea) { @@ -2382,7 +2382,7 @@ static void gen_lxx_seg(DisasContext *s, X86DecodedInsn *decode, int seg) gen_op_ld_v(s, MO_16, s->T1, s->A0); /* load the segment here to handle exceptions properly */ - gen_movl_seg(s, seg, s->T1); + gen_movl_seg(s, seg, s->T1, false); } static void gen_LDS(DisasContext *s, X86DecodedInsn *decode) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 8a641951cd..a4e935b043 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2026,27 +2026,32 @@ static void gen_op_movl_seg_real(DisasContext *s, X86Seg seg_reg, TCGv seg) /* move SRC to seg_reg and compute if the CPU state may change. Never call this function with seg_reg == R_CS */ -static void gen_movl_seg(DisasContext *s, X86Seg seg_reg, TCGv src) +static void gen_movl_seg(DisasContext *s, X86Seg seg_reg, TCGv src, bool inhibit_irq) { if (PE(s) && !VM86(s)) { TCGv_i32 sel = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(sel, src); gen_helper_load_seg(tcg_env, tcg_constant_i32(seg_reg), sel); - /* abort translation because the addseg value may change or - because ss32 may change. For R_SS, translation must always - stop as a special handling must be done to disable hardware - interrupts for the next instruction */ - if (seg_reg == R_SS) { - s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; - } else if (CODE32(s) && seg_reg < R_FS) { + + /* For move to DS/ES/SS, the addseg or ss32 flags may change. */ + if (CODE32(s) && seg_reg < R_FS) { s->base.is_jmp = DISAS_EOB_NEXT; } } else { gen_op_movl_seg_real(s, seg_reg, src); - if (seg_reg == R_SS) { - s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; - } + } + + /* + * For MOV or POP to SS (but not LSS) translation must always + * stop as a special handling must be done to disable hardware + * interrupts for the next instruction. + * + * DISAS_EOB_INHIBIT_IRQ is a superset of DISAS_EOB_NEXT which + * might have been set above. + */ + if (inhibit_irq) { + s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; } } From 1e94ddc6854431064c94a7d8f2f2886def285829 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 15 Jul 2024 10:35:06 +0200 Subject: [PATCH 0605/2760] target/i386: do not block singlestep for STI STI will trigger a singlestep exception even if it has inhibit-IRQ behavior. Do not suppress single-step for all IRQ-inhibiting instructions, instead special case MOV SS and POP SS. Cc: qemu-stable@nongnu.org Fixes: f0f0136abba ("target/i386: no single-step exception after MOV or POP SS", 2024-05-25) Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index a4e935b043..ed43c95c1d 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2047,11 +2047,15 @@ static void gen_movl_seg(DisasContext *s, X86Seg seg_reg, TCGv src, bool inhibit * stop as a special handling must be done to disable hardware * interrupts for the next instruction. * + * This is the last instruction, so it's okay to overwrite + * HF_TF_MASK; the next TB will start with the flag set. + * * DISAS_EOB_INHIBIT_IRQ is a superset of DISAS_EOB_NEXT which * might have been set above. */ if (inhibit_irq) { s->base.is_jmp = DISAS_EOB_INHIBIT_IRQ; + s->flags &= ~HF_TF_MASK; } } @@ -2302,7 +2306,7 @@ gen_eob(DisasContext *s, int mode) if (mode == DISAS_EOB_RECHECK_TF) { gen_helper_rechecking_single_step(tcg_env); tcg_gen_exit_tb(NULL, 0); - } else if ((s->flags & HF_TF_MASK) && mode != DISAS_EOB_INHIBIT_IRQ) { + } else if (s->flags & HF_TF_MASK) { gen_helper_single_step(tcg_env); } else if (mode == DISAS_JUMP && /* give irqs a chance to happen */ From 8dc4f981004f17efe2d28f26b180f618f24f46de Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 2 May 2025 11:55:24 +0200 Subject: [PATCH 0606/2760] hw/char/serial: Remove unused prog_if compat property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This property was added to preserve previous value when this was fixed in version 2.1 but the last machine using it was already removed when adding diva-gsp leaving this property unused and unnecessary. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Helge Deller Link: https://lore.kernel.org/r/20250502095524.DE1F355D264@zero.eik.bme.hu Signed-off-by: Paolo Bonzini --- hw/char/diva-gsp.c | 6 ++---- hw/char/serial-pci-multi.c | 7 ++----- hw/char/serial-pci.c | 10 ++-------- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/hw/char/diva-gsp.c b/hw/char/diva-gsp.c index 60f933191d..e1f0713cb7 100644 --- a/hw/char/diva-gsp.c +++ b/hw/char/diva-gsp.c @@ -51,7 +51,6 @@ typedef struct PCIDivaSerialState { SerialState state[PCI_SERIAL_MAX_PORTS]; uint32_t level[PCI_SERIAL_MAX_PORTS]; qemu_irq *irqs; - uint8_t prog_if; bool disable; } PCIDivaSerialState; @@ -124,8 +123,8 @@ static void diva_pci_realize(PCIDevice *dev, Error **errp) size_t i, offset = 0; size_t portmask = di.omask; - pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; - pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; + pci->dev.config[PCI_CLASS_PROG] = 2; /* 16550 compatible */ + pci->dev.config[PCI_INTERRUPT_PIN] = 1; memory_region_init(&pci->membar, OBJECT(pci), "serial_ports", 4096); pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &pci->membar); pci->irqs = qemu_allocate_irqs(multi_serial_irq_mux, pci, di.nports); @@ -178,7 +177,6 @@ static const Property diva_serial_properties[] = { DEFINE_PROP_CHR("chardev2", PCIDivaSerialState, state[1].chr), DEFINE_PROP_CHR("chardev3", PCIDivaSerialState, state[2].chr), DEFINE_PROP_CHR("chardev4", PCIDivaSerialState, state[3].chr), - DEFINE_PROP_UINT8("prog_if", PCIDivaSerialState, prog_if, 0x02), DEFINE_PROP_UINT32("subvendor", PCIDivaSerialState, subvendor, PCI_DEVICE_ID_HP_DIVA_TOSCA1), }; diff --git a/hw/char/serial-pci-multi.c b/hw/char/serial-pci-multi.c index fb184c2e6d..13df272691 100644 --- a/hw/char/serial-pci-multi.c +++ b/hw/char/serial-pci-multi.c @@ -46,7 +46,6 @@ typedef struct PCIMultiSerialState { SerialState state[PCI_SERIAL_MAX_PORTS]; uint32_t level[PCI_SERIAL_MAX_PORTS]; IRQState irqs[PCI_SERIAL_MAX_PORTS]; - uint8_t prog_if; } PCIMultiSerialState; static void multi_serial_pci_exit(PCIDevice *dev) @@ -97,8 +96,8 @@ static void multi_serial_pci_realize(PCIDevice *dev, Error **errp) SerialState *s; size_t i, nports = multi_serial_get_port_count(pc); - pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; - pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; + pci->dev.config[PCI_CLASS_PROG] = 2; /* 16550 compatible */ + pci->dev.config[PCI_INTERRUPT_PIN] = 1; memory_region_init(&pci->iobar, OBJECT(pci), "multiserial", 8 * nports); pci_register_bar(&pci->dev, 0, PCI_BASE_ADDRESS_SPACE_IO, &pci->iobar); @@ -133,7 +132,6 @@ static const VMStateDescription vmstate_pci_multi_serial = { static const Property multi_2x_serial_pci_properties[] = { DEFINE_PROP_CHR("chardev1", PCIMultiSerialState, state[0].chr), DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), - DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), }; static const Property multi_4x_serial_pci_properties[] = { @@ -141,7 +139,6 @@ static const Property multi_4x_serial_pci_properties[] = { DEFINE_PROP_CHR("chardev2", PCIMultiSerialState, state[1].chr), DEFINE_PROP_CHR("chardev3", PCIMultiSerialState, state[2].chr), DEFINE_PROP_CHR("chardev4", PCIMultiSerialState, state[3].chr), - DEFINE_PROP_UINT8("prog_if", PCIMultiSerialState, prog_if, 0x02), }; static void multi_2x_serial_pci_class_initfn(ObjectClass *klass, diff --git a/hw/char/serial-pci.c b/hw/char/serial-pci.c index 8707e81914..46efabc4cb 100644 --- a/hw/char/serial-pci.c +++ b/hw/char/serial-pci.c @@ -38,7 +38,6 @@ struct PCISerialState { PCIDevice dev; SerialState state; - uint8_t prog_if; }; #define TYPE_PCI_SERIAL "pci-serial" @@ -53,8 +52,8 @@ static void serial_pci_realize(PCIDevice *dev, Error **errp) return; } - pci->dev.config[PCI_CLASS_PROG] = pci->prog_if; - pci->dev.config[PCI_INTERRUPT_PIN] = 0x01; + pci->dev.config[PCI_CLASS_PROG] = 2; /* 16550 compatible */ + pci->dev.config[PCI_INTERRUPT_PIN] = 1; s->irq = pci_allocate_irq(&pci->dev); memory_region_init_io(&s->io, OBJECT(pci), &serial_io_ops, s, "serial", 8); @@ -81,10 +80,6 @@ static const VMStateDescription vmstate_pci_serial = { } }; -static const Property serial_pci_properties[] = { - DEFINE_PROP_UINT8("prog_if", PCISerialState, prog_if, 0x02), -}; - static void serial_pci_class_initfn(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -96,7 +91,6 @@ static void serial_pci_class_initfn(ObjectClass *klass, const void *data) pc->revision = 1; pc->class_id = PCI_CLASS_COMMUNICATION_SERIAL; dc->vmsd = &vmstate_pci_serial; - device_class_set_props(dc, serial_pci_properties); set_bit(DEVICE_CATEGORY_INPUT, dc->categories); } From ffd5a60e9b67e14f7bac7ea29300ea46a944e508 Mon Sep 17 00:00:00 2001 From: Stefan Zabka Date: Fri, 2 May 2025 23:27:48 +0200 Subject: [PATCH 0607/2760] rust: centralize config in workspace root This commit bundles common config option in the workspace root and applies them through .workspace = true Signed-off-by: Stefan Zabka Link: https://lore.kernel.org/r/20250502212748.124953-1-git@zabka.it Signed-off-by: Paolo Bonzini --- rust/Cargo.toml | 7 +++++++ rust/hw/char/pl011/Cargo.toml | 11 ++++++----- rust/hw/timer/hpet/Cargo.toml | 9 ++++++--- rust/qemu-api-macros/Cargo.toml | 11 ++++++----- rust/qemu-api/Cargo.toml | 15 +++++++-------- 5 files changed, 32 insertions(+), 21 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index ab1185a814..5ace47c69b 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -7,6 +7,13 @@ members = [ "hw/timer/hpet", ] +[workspace.package] +edition = "2021" +homepage = "https://www.qemu.org" +license = "GPL-2.0-or-later" +repository = "https://gitlab.com/qemu-project/qemu/" +rust-version = "1.63.0" + [workspace.lints.rust] unexpected_cfgs = { level = "deny", check-cfg = [ 'cfg(MESON)', 'cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)', diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index f2296cad58..a1f431ab4a 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -1,15 +1,16 @@ [package] name = "pl011" version = "0.1.0" -edition = "2021" authors = ["Manos Pitsidianakis "] -license = "GPL-2.0-or-later" description = "pl011 device model for QEMU" resolver = "2" publish = false -keywords = [] -categories = [] -rust-version = "1.63.0" + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true [lib] crate-type = ["staticlib"] diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 147f216e72..6f07502784 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -1,11 +1,14 @@ [package] name = "hpet" version = "0.1.0" -edition = "2021" authors = ["Zhao Liu "] -license = "GPL-2.0-or-later" description = "IA-PC High Precision Event Timer emulation in Rust" -rust-version = "1.63.0" + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true [lib] crate-type = ["staticlib"] diff --git a/rust/qemu-api-macros/Cargo.toml b/rust/qemu-api-macros/Cargo.toml index 89dee1cfb3..0cd40c8e16 100644 --- a/rust/qemu-api-macros/Cargo.toml +++ b/rust/qemu-api-macros/Cargo.toml @@ -1,15 +1,16 @@ [package] name = "qemu_api_macros" version = "0.1.0" -edition = "2021" authors = ["Manos Pitsidianakis "] -license = "GPL-2.0-or-later" description = "Rust bindings for QEMU - Utility macros" resolver = "2" publish = false -keywords = [] -categories = [] -rust-version = "1.63.0" + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true [lib] proc-macro = true diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index 57747bc934..ca1b04269f 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -1,18 +1,17 @@ [package] name = "qemu_api" version = "0.1.0" -edition = "2021" authors = ["Manos Pitsidianakis "] -license = "GPL-2.0-or-later" -readme = "README.md" -homepage = "https://www.qemu.org" description = "Rust bindings for QEMU" -repository = "https://gitlab.com/qemu-project/qemu/" +readme = "README.md" resolver = "2" publish = false -keywords = [] -categories = [] -rust-version = "1.63.0" + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true [dependencies] qemu_api_macros = { path = "../qemu-api-macros" } From 03f50d7ee756eecbd4481c3008b5e01e999729c7 Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Sat, 3 May 2025 00:47:29 +0300 Subject: [PATCH 0608/2760] monitor: don't wake up qmp_dispatcher_co coroutine upon cleanup Since the commit 3e6bed61 ("monitor: cleanup detection of qmp_dispatcher_co shutting down"), coroutine pointer qmp_dispatcher_co is set to NULL upon cleanup. If a QMP command is sent after monitor_cleanup() (e.g. after shutdown), this may lead to SEGFAULT on aio_co_wake(NULL). As mentioned in the comment inside monitor_cleanup(), the intention is to allow incoming requests while shutting down, but simply leave them without any response. Let's do exactly that, and if qmp_dispatcher_co coroutine pointer has already been set to NULL, let's simply skip the aio_co_wake() part. Signed-off-by: Andrey Drobyshev Link: https://lore.kernel.org/r/20250502214729.928380-2-andrey.drobyshev@virtuozzo.com Signed-off-by: Paolo Bonzini --- monitor/qmp.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/monitor/qmp.c b/monitor/qmp.c index 2f46cf9e49..cb99a12d94 100644 --- a/monitor/qmp.c +++ b/monitor/qmp.c @@ -356,7 +356,8 @@ void qmp_dispatcher_co_wake(void) /* Write request before reading qmp_dispatcher_co_busy. */ smp_mb__before_rmw(); - if (!qatomic_xchg(&qmp_dispatcher_co_busy, true)) { + if (!qatomic_xchg(&qmp_dispatcher_co_busy, true) && + qatomic_read(&qmp_dispatcher_co)) { aio_co_wake(qmp_dispatcher_co); } } From ba27ba302a264117c8b8427f944ced1bed17c438 Mon Sep 17 00:00:00 2001 From: Troy Lee Date: Mon, 17 Mar 2025 14:59:38 +0800 Subject: [PATCH 0609/2760] hw/arm: ast27x0: Wire up EHCI controllers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST27x0 has 4 EHCI controllers, where each CPU and I/O die has 2 instances. This patch use existing TYPE_PLATFORM_EHCI. After wiring up the EHCI controller, the ast2700a1-evb can find up to 4 USB EHCI interfaces. ehci-platform 12061000.usb: EHCI Host Controller ehci-platform 12061000.usb: new USB bus registered, assigned bus number 2 ehci-platform 12063000.usb: EHCI Host Controller ehci-platform 12063000.usb: new USB bus registered, assigned bus number 3 ehci-platform 12061000.usb: irq 88, io mem 0x12061000 ehci-platform 12063000.usb: irq 90, io mem 0x12063000 ehci-platform 14121000.usb: EHCI Host Controller ehci-platform 14123000.usb: EHCI Host Controller ehci-platform 12061000.usb: USB 2.0 started, EHCI 1.00 ehci-platform 14121000.usb: new USB bus registered, assigned bus number 5 ehci-platform 14123000.usb: new USB bus registered, assigned bus number 6 ehci-platform 14121000.usb: irq 91, io mem 0x14121000 ehci-platform 14123000.usb: irq 92, io mem 0x14123000 ehci-platform 12063000.usb: USB 2.0 started, EHCI 1.00 usb usb2: Manufacturer: Linux 6.6.78-dirty-bafd2830c17c-gbafd2830c17c-dirty ehci_hcd usb usb3: Manufacturer: Linux 6.6.78-dirty-bafd2830c17c-gbafd2830c17c-dirty ehci_hcd ehci-platform 14121000.usb: USB 2.0 started, EHCI 1.00 usb usb5: Manufacturer: Linux 6.6.78-dirty-bafd2830c17c-gbafd2830c17c-dirty ehci_hcd ehci-platform 14123000.usb: USB 2.0 started, EHCI 1.00 usb usb6: Manufacturer: Linux 6.6.78-dirty-bafd2830c17c-gbafd2830c17c-dirty ehci_hcd Note that, AST27x0A0 only has 2 EHCI controllers due to hw issue. Signed-off-by: Troy Lee Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250317065938.1902272-2-troy_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 28 ++++++++++++++++++++++++++++ include/hw/arm/aspeed_soc.h | 4 +++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 63a366f7e8..ea4a611b90 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -25,6 +25,8 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_SRAM] = 0x10000000, + [ASPEED_DEV_EHCI1] = 0x12061000, + [ASPEED_DEV_EHCI2] = 0x12063000, [ASPEED_DEV_HACE] = 0x12070000, [ASPEED_DEV_EMMC] = 0x12090000, [ASPEED_DEV_INTC] = 0x12100000, @@ -47,6 +49,8 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_ETH2] = 0x14060000, [ASPEED_DEV_ETH3] = 0x14070000, [ASPEED_DEV_SDHCI] = 0x14080000, + [ASPEED_DEV_EHCI3] = 0x14121000, + [ASPEED_DEV_EHCI4] = 0x14123000, [ASPEED_DEV_ADC] = 0x14C00000, [ASPEED_DEV_SCUIO] = 0x14C02000, [ASPEED_DEV_GPIO] = 0x14C0B000, @@ -91,6 +95,8 @@ static const int aspeed_soc_ast2700a0_irqmap[] = { [ASPEED_DEV_TIMER7] = 22, [ASPEED_DEV_TIMER8] = 23, [ASPEED_DEV_DP] = 28, + [ASPEED_DEV_EHCI1] = 33, + [ASPEED_DEV_EHCI2] = 37, [ASPEED_DEV_LPC] = 128, [ASPEED_DEV_IBT] = 128, [ASPEED_DEV_KCS] = 128, @@ -137,6 +143,8 @@ static const int aspeed_soc_ast2700a1_irqmap[] = { [ASPEED_DEV_TIMER7] = 22, [ASPEED_DEV_TIMER8] = 23, [ASPEED_DEV_DP] = 28, + [ASPEED_DEV_EHCI1] = 33, + [ASPEED_DEV_EHCI2] = 37, [ASPEED_DEV_LPC] = 192, [ASPEED_DEV_IBT] = 192, [ASPEED_DEV_KCS] = 192, @@ -212,6 +220,8 @@ static const int ast2700_gic132_gic196_intcmap[] = { [ASPEED_DEV_UART10] = 16, [ASPEED_DEV_UART11] = 17, [ASPEED_DEV_UART12] = 18, + [ASPEED_DEV_EHCI3] = 28, + [ASPEED_DEV_EHCI4] = 29, }; /* GICINT 133 */ @@ -434,6 +444,11 @@ static void aspeed_soc_ast2700_init(Object *obj) object_initialize_child(obj, "spi[*]", &s->spi[i], typename); } + for (i = 0; i < sc->ehcis_num; i++) { + object_initialize_child(obj, "ehci[*]", &s->ehci[i], + TYPE_PLATFORM_EHCI); + } + snprintf(typename, sizeof(typename), "aspeed.sdmc-%s", socname); object_initialize_child(obj, "sdmc", &s->sdmc, typename); object_property_add_alias(obj, "ram-size", OBJECT(&s->sdmc), @@ -709,6 +724,17 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) ASPEED_SMC_GET_CLASS(&s->spi[i])->flash_window_base); } + /* EHCI */ + for (i = 0; i < sc->ehcis_num; i++) { + if (!sysbus_realize(SYS_BUS_DEVICE(&s->ehci[i]), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->ehci[i]), 0, + sc->memmap[ASPEED_DEV_EHCI1 + i]); + sysbus_connect_irq(SYS_BUS_DEVICE(&s->ehci[i]), 0, + aspeed_soc_get_irq(s, ASPEED_DEV_EHCI1 + i)); + } + /* * SDMC - SDRAM Memory Controller * The SDMC controller is unlocked at SPL stage. @@ -900,6 +926,7 @@ static void aspeed_soc_ast2700a0_class_init(ObjectClass *oc, const void *data) sc->silicon_rev = AST2700_A0_SILICON_REV; sc->sram_size = 0x20000; sc->spis_num = 3; + sc->ehcis_num = 2; sc->wdts_num = 8; sc->macs_num = 1; sc->uarts_num = 13; @@ -927,6 +954,7 @@ static void aspeed_soc_ast2700a1_class_init(ObjectClass *oc, const void *data) sc->silicon_rev = AST2700_A1_SILICON_REV; sc->sram_size = 0x20000; sc->spis_num = 3; + sc->ehcis_num = 4; sc->wdts_num = 8; sc->macs_num = 3; sc->uarts_num = 13; diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index f069d17d16..c1e80c8908 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -43,7 +43,7 @@ #include "hw/intc/arm_gicv3.h" #define ASPEED_SPIS_NUM 3 -#define ASPEED_EHCIS_NUM 2 +#define ASPEED_EHCIS_NUM 4 #define ASPEED_WDTS_NUM 8 #define ASPEED_CPUS_NUM 4 #define ASPEED_MACS_NUM 4 @@ -192,6 +192,8 @@ enum { ASPEED_DEV_SPI2, ASPEED_DEV_EHCI1, ASPEED_DEV_EHCI2, + ASPEED_DEV_EHCI3, + ASPEED_DEV_EHCI4, ASPEED_DEV_VIC, ASPEED_DEV_INTC, ASPEED_DEV_INTCIO, From 47cdaa46f3f2c6710911543f2ec836eeb624969e Mon Sep 17 00:00:00 2001 From: Joe Komlodi Date: Tue, 22 Apr 2025 00:27:47 +0000 Subject: [PATCH 0610/2760] hw/ssi/aspeed_smc: Allow 64-bit wide flash accesses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cde3247651dc998da5dc1005148302a90d72f21f fixed atomicity for LDRD, which ends up making accesses 64-bits wide. However, the AST2600 bootloader can sometimes compile with LDRD instructions, which causes the acceses to fail when accessing the memory-mapped SPI flash. To fix this, increase the MMIO region valid access size to allow for 64-bit accesses. Signed-off-by: Joe Komlodi Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250422002747.2593465-1-komlodi@google.com Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 0d38f95c7a..614528b8ef 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -359,7 +359,7 @@ static const MemoryRegionOps aspeed_smc_flash_default_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, - .max_access_size = 4, + .max_access_size = 8, }, }; @@ -670,7 +670,7 @@ static const MemoryRegionOps aspeed_smc_flash_ops = { .endianness = DEVICE_LITTLE_ENDIAN, .valid = { .min_access_size = 1, - .max_access_size = 4, + .max_access_size = 8, }, }; From 9b671ea9bd88e8ad14325eac4240850792458136 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 23 Apr 2025 09:40:05 +0800 Subject: [PATCH 0611/2760] tests/functional/aspeed: Update test ASPEED SDK v09.06 for AST2500 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250423014008.147542-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/test_arm_aspeed_ast2500.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_arm_aspeed_ast2500.py b/tests/functional/test_arm_aspeed_ast2500.py index a3b44572fc..6923fe8701 100755 --- a/tests/functional/test_arm_aspeed_ast2500.py +++ b/tests/functional/test_arm_aspeed_ast2500.py @@ -37,14 +37,14 @@ def test_arm_ast2500_evb_buildroot(self): self.do_test_arm_aspeed_buildroot_poweroff() - ASSET_SDK_V806_AST2500 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v08.06/ast2500-default-obmc.tar.gz', - 'e1755f3cadff69190438c688d52dd0f0d399b70a1e14b1d3d5540fc4851d38ca') + ASSET_SDK_V906_AST2500 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2500-default-obmc.tar.gz', + '542db84645b4efd8aed50385d7f4dd1caff379a987032311cfa7b563a3addb2a') def test_arm_ast2500_evb_sdk(self): self.set_machine('ast2500-evb') - self.archive_extract(self.ASSET_SDK_V806_AST2500) + self.archive_extract(self.ASSET_SDK_V906_AST2500) self.do_test_arm_aspeed_sdk_start( self.scratch_file("ast2500-default", "image-bmc")) From 88bff6d5b2955d4b629a664c21cb7d91ff8a236c Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 23 Apr 2025 09:40:06 +0800 Subject: [PATCH 0612/2760] tests/functional/aspeed: Update test ASPEED SDK v09.06 for AST2600 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update test for AST2600 production revision A3. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250423014008.147542-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/test_arm_aspeed_ast2600.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/tests/functional/test_arm_aspeed_ast2600.py b/tests/functional/test_arm_aspeed_ast2600.py index 5ef52f0659..fdae4c939d 100755 --- a/tests/functional/test_arm_aspeed_ast2600.py +++ b/tests/functional/test_arm_aspeed_ast2600.py @@ -97,26 +97,27 @@ def test_arm_ast2600_evb_buildroot_tpm(self): self.do_test_arm_aspeed_buildroot_poweroff() - ASSET_SDK_V806_AST2600_A2 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v08.06/ast2600-a2-obmc.tar.gz', - '9083506135f622d5e7351fcf7d4e1c7125cee5ba16141220c0ba88931f3681a4') + ASSET_SDK_V906_AST2600 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2600-default-obmc.tar.gz', + '768d76e247896ad78c154b9cff4f766da2ce65f217d620b286a4a03a8a4f68f5') def test_arm_ast2600_evb_sdk(self): self.set_machine('ast2600-evb') - self.archive_extract(self.ASSET_SDK_V806_AST2600_A2) + self.archive_extract(self.ASSET_SDK_V906_AST2600) self.vm.add_args('-device', 'tmp105,bus=aspeed.i2c.bus.5,address=0x4d,id=tmp-test') self.vm.add_args('-device', 'ds1338,bus=aspeed.i2c.bus.5,address=0x32') self.do_test_arm_aspeed_sdk_start( - self.scratch_file("ast2600-a2", "image-bmc")) + self.scratch_file("ast2600-default", "image-bmc")) - self.wait_for_console_pattern('ast2600-a2 login:') + self.wait_for_console_pattern('ast2600-default login:') exec_command_and_wait_for_pattern(self, 'root', 'Password:') - exec_command_and_wait_for_pattern(self, '0penBmc', 'root@ast2600-a2:~#') + exec_command_and_wait_for_pattern(self, '0penBmc', + 'root@ast2600-default:~#') exec_command_and_wait_for_pattern(self, 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-5/device/new_device', From 53f3285e11876aae97d4240d944d45a4fe6c4597 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 23 Apr 2025 09:40:07 +0800 Subject: [PATCH 0613/2760] tests/functional/aspeed: Update test ASPEED SDK v03.00 for AST1030 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250423014008.147542-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/test_arm_aspeed_ast1030.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/functional/test_arm_aspeed_ast1030.py b/tests/functional/test_arm_aspeed_ast1030.py index d45d9f7c1c..77037f0179 100755 --- a/tests/functional/test_arm_aspeed_ast1030.py +++ b/tests/functional/test_arm_aspeed_ast1030.py @@ -12,17 +12,17 @@ class AST1030Machine(LinuxKernelTest): - ASSET_ZEPHYR_1_04 = Asset( + ASSET_ZEPHYR_3_00 = Asset( ('https://github.com/AspeedTech-BMC' - '/zephyr/releases/download/v00.01.04/ast1030-evb-demo.zip'), - '4ac6210adcbc61294927918707c6762483fd844dde5e07f3ba834ad1f91434d3') + '/zephyr/releases/download/v00.03.00/ast1030-evb-demo.zip'), + '37fe3ecd4a1b9d620971a15b96492a81093435396eeac69b6f3e384262ff555f') - def test_ast1030_zephyros_1_04(self): + def test_ast1030_zephyros_3_00(self): self.set_machine('ast1030-evb') kernel_name = "ast1030-evb-demo/zephyr.elf" kernel_file = self.archive_extract( - self.ASSET_ZEPHYR_1_04, member=kernel_name) + self.ASSET_ZEPHYR_3_00, member=kernel_name) self.vm.set_console() self.vm.add_args('-kernel', kernel_file, '-nographic') From 8bc296c9b1122916166c021492c470c522e0fd16 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 23 Apr 2025 15:23:37 +0800 Subject: [PATCH 0614/2760] hw/arm/aspeed_ast27x0: Rename variable sram_name to name in ast2700 realize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The variable "sram_name" was only used for naming the SRAM memory region. Rename it to "name" for consistency with similar code and avoid unnecessary new local variable declarations. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Tested-by: Nabih Estefan Link: https://lore.kernel.org/qemu-devel/20250423072350.541742-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index ea4a611b90..2e21c3a98f 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -592,7 +592,7 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); AspeedINTCClass *ic = ASPEED_INTC_GET_CLASS(&a->intc[0]); AspeedINTCClass *icio = ASPEED_INTC_GET_CLASS(&a->intc[1]); - g_autofree char *sram_name = NULL; + g_autofree char *name = NULL; qemu_irq irq; /* Default boot region (SPI memory or ROMs) */ @@ -664,9 +664,9 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) } /* SRAM */ - sram_name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index); - if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, - errp)) { + name = g_strdup_printf("aspeed.sram.%d", CPU(&a->cpu[0])->cpu_index); + if (!memory_region_init_ram(&s->sram, OBJECT(s), name, sc->sram_size, + errp)) { return; } memory_region_add_subregion(s->memory, From 80c734ce92e3b68cdb8e82dfd094b3ba23900f17 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 23 Apr 2025 15:23:43 +0800 Subject: [PATCH 0615/2760] tests/functional/aspeed: Move I2C test into shared helper for AST2700 reuse MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move the I2C test case into a common helper function (do_ast2700_i2c_test) so it can be reused across multiple AST2700-based test cases. This reduces duplication and improves maintainability. Signed-off-by: Jamin Lin Reviewed-by: Nabih Estefan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250423072350.541742-8-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/test_aarch64_aspeed.py | 28 +++++++++++++------------ 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index c7f3b3b319..4b6851db2a 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -18,6 +18,8 @@ class AST2x00MachineSDK(QemuSystemTest): def do_test_aarch64_aspeed_sdk_start(self, image): self.require_netdev('user') self.vm.set_console() + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.1,address=0x4d,id=tmp-test') self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', '-net', 'nic', '-net', 'user', '-snapshot') @@ -35,6 +37,17 @@ def do_test_aarch64_aspeed_sdk_start(self, image): 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.05/ast2700-default-obmc.tar.gz', 'c1f4496aec06743c812a6e9a1a18d032f34d62f3ddb6956e924fef62aa2046a5') + def do_ast2700_i2c_test(self): + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-1/device/new_device ', + 'i2c i2c-1: new_device: Instantiated device lm75 at 0x4d') + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000) + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000') + def start_ast2700_test(self, name): num_cpu = 4 uboot_size = os.path.getsize(self.scratch_file(name, @@ -73,8 +86,6 @@ def start_ast2700_test(self, name): f'loader,addr=0x430000000,cpu-num={i}') self.vm.add_args('-smp', str(num_cpu)) - self.vm.add_args('-device', - 'tmp105,bus=aspeed.i2c.bus.1,address=0x4d,id=tmp-test') self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc')) @@ -83,28 +94,19 @@ def start_ast2700_test(self, name): exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') - exec_command_and_wait_for_pattern(self, - 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-1/device/new_device ', - 'i2c i2c-1: new_device: Instantiated device lm75 at 0x4d') - exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '0') - self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', - property='temperature', value=18000) - exec_command_and_wait_for_pattern(self, - 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000') - def test_aarch64_ast2700_evb_sdk_v09_05(self): self.set_machine('ast2700-evb') self.archive_extract(self.ASSET_SDK_V905_AST2700) self.start_ast2700_test('ast2700-a0-default') + self.do_ast2700_i2c_test() def test_aarch64_ast2700a1_evb_sdk_v09_05(self): self.set_machine('ast2700a1-evb') self.archive_extract(self.ASSET_SDK_V905_AST2700A1) self.start_ast2700_test('ast2700-default') - + self.do_ast2700_i2c_test() if __name__ == '__main__': QemuSystemTest.main() From b2a7c02a9a6d350005f722ca3df50df20d7f051e Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 23 Apr 2025 15:23:44 +0800 Subject: [PATCH 0616/2760] tests/functional/aspeed: Update test ASPEED SDK v09.06 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250423072350.541742-9-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/test_aarch64_aspeed.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index 4b6851db2a..b6e2be1f82 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -29,13 +29,13 @@ def do_test_aarch64_aspeed_sdk_start(self, image): wait_for_console_pattern(self, '## Loading kernel from FIT Image') wait_for_console_pattern(self, 'Starting kernel ...') - ASSET_SDK_V905_AST2700 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.05/ast2700-a0-default-obmc.tar.gz', - 'cfbbd1cce72f2a3b73b9080c41eecdadebb7077fba4f7806d72ac99f3e84b74a') + ASSET_SDK_V906_AST2700 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-a0-default-obmc.tar.gz', + '7247b6f19dbfb700686f8d9f723ac23f3eb229226c0589cb9b06b80d1b61f3cb') - ASSET_SDK_V905_AST2700A1 = Asset( - 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.05/ast2700-default-obmc.tar.gz', - 'c1f4496aec06743c812a6e9a1a18d032f34d62f3ddb6956e924fef62aa2046a5') + ASSET_SDK_V906_AST2700A1 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-default-obmc.tar.gz', + 'f1d53e0be8a404ecce3e105f72bc50fa4e090ad13160ffa91b10a6e0233a9dc6') def do_ast2700_i2c_test(self): exec_command_and_wait_for_pattern(self, @@ -94,17 +94,17 @@ def start_ast2700_test(self, name): exec_command_and_wait_for_pattern(self, 'root', 'Password:') exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') - def test_aarch64_ast2700_evb_sdk_v09_05(self): + def test_aarch64_ast2700_evb_sdk_v09_06(self): self.set_machine('ast2700-evb') - self.archive_extract(self.ASSET_SDK_V905_AST2700) + self.archive_extract(self.ASSET_SDK_V906_AST2700) self.start_ast2700_test('ast2700-a0-default') self.do_ast2700_i2c_test() - def test_aarch64_ast2700a1_evb_sdk_v09_05(self): + def test_aarch64_ast2700a1_evb_sdk_v09_06(self): self.set_machine('ast2700a1-evb') - self.archive_extract(self.ASSET_SDK_V905_AST2700A1) + self.archive_extract(self.ASSET_SDK_V906_AST2700A1) self.start_ast2700_test('ast2700-default') self.do_ast2700_i2c_test() From af93cef791b5976b9b6d977da36e25999a6cebe1 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 23 Apr 2025 15:23:45 +0800 Subject: [PATCH 0617/2760] tests/functional/aspeed: extract boot and login sequence into helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extracted repeated boot and login steps into a new helper function. No change in functional behavior. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250423072350.541742-10-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/test_aarch64_aspeed.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index b6e2be1f82..1e1f3f9ece 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -25,10 +25,15 @@ def do_test_aarch64_aspeed_sdk_start(self, image): self.vm.launch() + def verify_openbmc_boot_and_login(self, name): wait_for_console_pattern(self, 'U-Boot 2023.10') wait_for_console_pattern(self, '## Loading kernel from FIT Image') wait_for_console_pattern(self, 'Starting kernel ...') + wait_for_console_pattern(self, f'{name} login:') + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') + ASSET_SDK_V906_AST2700 = Asset( 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-a0-default-obmc.tar.gz', '7247b6f19dbfb700686f8d9f723ac23f3eb229226c0589cb9b06b80d1b61f3cb') @@ -89,16 +94,12 @@ def start_ast2700_test(self, name): self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc')) - wait_for_console_pattern(self, f'{name} login:') - - exec_command_and_wait_for_pattern(self, 'root', 'Password:') - exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') - def test_aarch64_ast2700_evb_sdk_v09_06(self): self.set_machine('ast2700-evb') self.archive_extract(self.ASSET_SDK_V906_AST2700) self.start_ast2700_test('ast2700-a0-default') + self.verify_openbmc_boot_and_login('ast2700-a0-default') self.do_ast2700_i2c_test() def test_aarch64_ast2700a1_evb_sdk_v09_06(self): @@ -106,6 +107,7 @@ def test_aarch64_ast2700a1_evb_sdk_v09_06(self): self.archive_extract(self.ASSET_SDK_V906_AST2700A1) self.start_ast2700_test('ast2700-default') + self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test() if __name__ == '__main__': From 2e143da2fbd92d9c3ae2d7a315efca5c6af24e69 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 24 Apr 2025 15:51:29 +0800 Subject: [PATCH 0618/2760] hw/arm/aspeed_ast27x0 Introduce vbootrom memory region MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new vbootrom memory region. The region is mapped at address "0x00000000" and has a size of 128KB, identical to the SRAM region size. This memory region is intended for loading a vbootrom image file as part of the boot process. The vbootrom registered in the SoC's address space using the ASPEED_DEV_VBOOTROM index. Signed-off-by: Jamin Lin Reviewed-by: Nabih Estefan Tested-by: Nabih Estefan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250424075135.3715128-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 9 +++++++++ include/hw/arm/aspeed_soc.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 2e21c3a98f..a289e65e49 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -24,6 +24,7 @@ #include "qemu/log.h" static const hwaddr aspeed_soc_ast2700_memmap[] = { + [ASPEED_DEV_VBOOTROM] = 0x00000000, [ASPEED_DEV_SRAM] = 0x10000000, [ASPEED_DEV_EHCI1] = 0x12061000, [ASPEED_DEV_EHCI2] = 0x12063000, @@ -672,6 +673,14 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) memory_region_add_subregion(s->memory, sc->memmap[ASPEED_DEV_SRAM], &s->sram); + /* VBOOTROM */ + if (!memory_region_init_ram(&s->vbootrom, OBJECT(s), "aspeed.vbootrom", + 0x20000, errp)) { + return; + } + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_VBOOTROM], &s->vbootrom); + /* SCU */ if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { return; diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index c1e80c8908..4dcb1010dc 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -59,6 +59,7 @@ struct AspeedSoCState { MemoryRegion sram; MemoryRegion spi_boot_container; MemoryRegion spi_boot; + MemoryRegion vbootrom; AddressSpace dram_as; AspeedRtcState rtc; AspeedTimerCtrlState timerctrl; @@ -169,6 +170,7 @@ struct AspeedSoCClass { const char *aspeed_soc_cpu_type(AspeedSoCClass *sc); enum { + ASPEED_DEV_VBOOTROM, ASPEED_DEV_SPI_BOOT, ASPEED_DEV_IOMEM, ASPEED_DEV_UART0, From ee447054404ed8bbdea0eece888da354afe38d97 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 24 Apr 2025 15:51:31 +0800 Subject: [PATCH 0619/2760] hw/arm/aspeed: Add support for loading vbootrom image via "-bios" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce "aspeed_load_vbootrom()" to support loading a virtual boot ROM image into the vbootrom memory region, using the "-bios" command-line option. Introduce a new "vbootrom" field in the AspeedMachineClass to indicate whether a machine supports the virtual boot ROM region. Set this field to true by default for the AST2700-A0 and AST2700-A1 EVB machines. Signed-off-by: Jamin Lin Reviewed-by: Nabih Estefan Tested-by: Nabih Estefan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250424075135.3715128-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 36 ++++++++++++++++++++++++++++++++++++ include/hw/arm/aspeed.h | 1 + 2 files changed, 37 insertions(+) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 20f418fb63..d0b333646e 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -27,6 +27,7 @@ #include "system/reset.h" #include "hw/loader.h" #include "qemu/error-report.h" +#include "qemu/datadir.h" #include "qemu/units.h" #include "hw/qdev-clock.h" #include "system/system.h" @@ -305,6 +306,33 @@ static void aspeed_install_boot_rom(AspeedMachineState *bmc, BlockBackend *blk, rom_size, &error_abort); } +#define VBOOTROM_FILE_NAME "ast27x0_bootrom.bin" + +/* + * This function locates the vbootrom image file specified via the command line + * using the -bios option. It loads the specified image into the vbootrom + * memory region and handles errors if the file cannot be found or loaded. + */ +static void aspeed_load_vbootrom(AspeedMachineState *bmc, const char *bios_name, + Error **errp) +{ + g_autofree char *filename = NULL; + AspeedSoCState *soc = bmc->soc; + int ret; + + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!filename) { + error_setg(errp, "Could not find vbootrom image '%s'", bios_name); + return; + } + + ret = load_image_mr(filename, &soc->vbootrom); + if (ret < 0) { + error_setg(errp, "Failed to load vbootrom image '%s'", bios_name); + return; + } +} + void aspeed_board_init_flashes(AspeedSMCState *s, const char *flashtype, unsigned int count, int unit0) { @@ -380,6 +408,7 @@ static void aspeed_machine_init(MachineState *machine) AspeedMachineClass *amc = ASPEED_MACHINE_GET_CLASS(machine); AspeedSoCClass *sc; int i; + const char *bios_name = NULL; DriveInfo *emmc0 = NULL; bool boot_emmc; @@ -482,6 +511,11 @@ static void aspeed_machine_init(MachineState *machine) } } + if (amc->vbootrom) { + bios_name = machine->firmware ?: VBOOTROM_FILE_NAME; + aspeed_load_vbootrom(bmc, bios_name, &error_abort); + } + arm_load_kernel(ARM_CPU(first_cpu), machine, &aspeed_board_binfo); } @@ -1701,6 +1735,7 @@ static void aspeed_machine_ast2700a0_evb_class_init(ObjectClass *oc, amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON | ASPEED_MAC2_ON; amc->uart_default = ASPEED_DEV_UART12; amc->i2c_init = ast2700_evb_i2c_init; + amc->vbootrom = true; mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); @@ -1722,6 +1757,7 @@ static void aspeed_machine_ast2700a1_evb_class_init(ObjectClass *oc, amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON | ASPEED_MAC2_ON; amc->uart_default = ASPEED_DEV_UART12; amc->i2c_init = ast2700_evb_i2c_init; + amc->vbootrom = true; mc->auto_create_sdcard = true; mc->default_ram_size = 1 * GiB; aspeed_machine_class_init_cpus_defaults(mc); diff --git a/include/hw/arm/aspeed.h b/include/hw/arm/aspeed.h index 9cae45a1c9..973277bea6 100644 --- a/include/hw/arm/aspeed.h +++ b/include/hw/arm/aspeed.h @@ -40,6 +40,7 @@ struct AspeedMachineClass { void (*i2c_init)(AspeedMachineState *bmc); uint32_t uart_default; bool sdhci_wp_inverted; + bool vbootrom; }; From 9e3d7afd7da40c9a1ba1a6b4e1103037f3a77da8 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 24 Apr 2025 15:51:32 +0800 Subject: [PATCH 0620/2760] tests/functional/aspeed: Add to test vbootrom for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the AST2700 functional test to boot using the vbootrom image instead of manually loading boot components with -device loader. The boot ROM binary is now passed via the -bios option, using the image located in pc-bios/ast27x0_bootrom.bin. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250424075135.3715128-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/functional/test_aarch64_aspeed.py | 26 +++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed.py index 1e1f3f9ece..d02dc7991c 100755 --- a/tests/functional/test_aarch64_aspeed.py +++ b/tests/functional/test_aarch64_aspeed.py @@ -25,6 +25,18 @@ def do_test_aarch64_aspeed_sdk_start(self, image): self.vm.launch() + def verify_vbootrom_firmware_flow(self): + wait_for_console_pattern(self, 'Found valid FIT image') + wait_for_console_pattern(self, '[uboot] loading') + wait_for_console_pattern(self, 'done') + wait_for_console_pattern(self, '[fdt] loading') + wait_for_console_pattern(self, 'done') + wait_for_console_pattern(self, '[tee] loading') + wait_for_console_pattern(self, 'done') + wait_for_console_pattern(self, '[atf] loading') + wait_for_console_pattern(self, 'done') + wait_for_console_pattern(self, 'Jumping to BL31 (Trusted Firmware-A)') + def verify_openbmc_boot_and_login(self, name): wait_for_console_pattern(self, 'U-Boot 2023.10') wait_for_console_pattern(self, '## Loading kernel from FIT Image') @@ -94,6 +106,11 @@ def start_ast2700_test(self, name): self.do_test_aarch64_aspeed_sdk_start( self.scratch_file(name, 'image-bmc')) + def start_ast2700_test_vbootrom(self, name): + self.vm.add_args('-bios', 'ast27x0_bootrom.bin') + self.do_test_aarch64_aspeed_sdk_start( + self.scratch_file(name, 'image-bmc')) + def test_aarch64_ast2700_evb_sdk_v09_06(self): self.set_machine('ast2700-evb') @@ -110,5 +127,14 @@ def test_aarch64_ast2700a1_evb_sdk_v09_06(self): self.verify_openbmc_boot_and_login('ast2700-default') self.do_ast2700_i2c_test() + def test_aarch64_ast2700a1_evb_sdk_vbootrom_v09_06(self): + self.set_machine('ast2700a1-evb') + + self.archive_extract(self.ASSET_SDK_V906_AST2700A1) + self.start_ast2700_test_vbootrom('ast2700-default') + self.verify_vbootrom_firmware_flow() + self.verify_openbmc_boot_and_login('ast2700-default') + self.do_ast2700_i2c_test() + if __name__ == '__main__': QemuSystemTest.main() From 3eb01cfe9b85cd64255f6ef93346a3de17fa9632 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 24 Apr 2025 15:51:33 +0800 Subject: [PATCH 0621/2760] docs/system/arm/aspeed: move AST2700 content to new section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moved AST2700-related content from the general Aspeed board list into a dedicated section for Aspeed 2700 family boards. Improves clarity and readability. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250424075135.3715128-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- docs/system/arm/aspeed.rst | 70 ++++++++++++++++++++++++++++++++++---- 1 file changed, 63 insertions(+), 7 deletions(-) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index 97fd6a0e7f..08a33b7008 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -1,12 +1,11 @@ -Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``, ``bletchley-bmc``, ``fuji-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) +Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``bletchley-bmc``, ``fuji-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) ================================================================================================================================================================================================================================================================================================================================================================================================================== The QEMU Aspeed machines model BMCs of various OpenPOWER systems and Aspeed evaluation boards. They are based on different releases of the Aspeed SoC : the AST2400 integrating an ARM926EJ-S CPU (400MHz), the AST2500 with an ARM1176JZS CPU (800MHz), the AST2600 -with dual cores ARM Cortex-A7 CPUs (1.2GHz) and more recently the AST2700 -with quad cores ARM Cortex-A35 64 bits CPUs (1.6GHz) +with dual cores ARM Cortex-A7 CPUs (1.2GHz). The SoC comes with RAM, Gigabit ethernet, USB, SD/MMC, USB, SPI, I2C, etc. @@ -39,10 +38,6 @@ AST2600 SoC based machines : - ``qcom-dc-scm-v1-bmc`` Qualcomm DC-SCM V1 BMC - ``qcom-firework-bmc`` Qualcomm Firework BMC -AST2700 SoC based machines : - -- ``ast2700-evb`` Aspeed AST2700 Evaluation board (Cortex-A35) - Supported devices ----------------- @@ -247,6 +242,67 @@ under Linux), use : -M ast2500-evb,bmc-console=uart3 +Aspeed 2700 family boards (``ast2700-evb``) +================================================================== + +The QEMU Aspeed machines model BMCs of Aspeed evaluation boards. +They are based on different releases of the Aspeed SoC : +the AST2700 with quad cores ARM Cortex-A35 64 bits CPUs (1.6GHz). + +The SoC comes with RAM, Gigabit ethernet, USB, SD/MMC, USB, SPI, I2C, +etc. + +AST2700 SoC based machines : + +- ``ast2700-evb`` Aspeed AST2700 Evaluation board (Cortex-A35) + +Supported devices +----------------- + * Interrupt Controller + * Timer Controller + * RTC Controller + * I2C Controller + * System Control Unit (SCU) + * SRAM mapping + * X-DMA Controller (basic interface) + * Static Memory Controller (SMC or FMC) - Only SPI Flash support + * SPI Memory Controller + * USB 2.0 Controller + * SD/MMC storage controllers + * SDRAM controller (dummy interface for basic settings and training) + * Watchdog Controller + * GPIO Controller (Master only) + * UART + * Ethernet controllers + * Front LEDs (PCA9552 on I2C bus) + * LPC Peripheral Controller (a subset of subdevices are supported) + * Hash/Crypto Engine (HACE) - Hash support only. TODO: Crypto + * ADC + * eMMC Boot Controller (dummy) + * PECI Controller (minimal) + * I3C Controller + * Internal Bridge Controller (SLI dummy) + +Missing devices +--------------- + * Coprocessor support + * PWM and Fan Controller + * Slave GPIO Controller + * Super I/O Controller + * PCI-Express 1 Controller + * Graphic Display Controller + * MCTP Controller + * Mailbox Controller + * Virtual UART + * eSPI Controller + +Boot options +------------ + +Images can be downloaded from the ASPEED Forked OpenBMC GitHub release repository : + + https://github.com/AspeedTech-BMC/openbmc/releases + Booting the ast2700-evb machine ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From d2b857ef9a997237ebedda53c3db50b264baf4fc Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 24 Apr 2025 15:51:34 +0800 Subject: [PATCH 0622/2760] docs/system/arm/aspeed: Support vbootrom for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using the vbootrom image support and the boot ROM binary is now passed via the -bios option, using the image located in pc-bios/ast27x0_bootrom.bin. Signed-off-by: Jamin Lin Reviewed-by: Nabih Estefan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250424075135.3715128-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- docs/system/arm/aspeed.rst | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index 08a33b7008..014545f444 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -306,7 +306,14 @@ Images can be downloaded from the ASPEED Forked OpenBMC GitHub release repositor Booting the ast2700-evb machine ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Boot the AST2700 machine from the flash image, use an MTD drive : +Boot the AST2700 machine from the flash image. + +There are two supported methods for booting the AST2700 machine with a flash image: + +Manual boot using ``-device loader``: + +It causes all 4 CPU cores to start execution from address ``0x430000000``, which +corresponds to the BL31 image load address. .. code-block:: bash @@ -326,6 +333,26 @@ Boot the AST2700 machine from the flash image, use an MTD drive : -drive file=${IMGDIR}/image-bmc,format=raw,if=mtd \ -nographic +Boot using a virtual boot ROM (``-bios``): + +If users do not specify the ``-bios option``, QEMU will attempt to load the +default vbootrom image ``ast27x0_bootrom.bin`` from either the current working +directory or the ``pc-bios`` directory within the QEMU source tree. + +.. code-block:: bash + + $ qemu-system-aarch64 -M ast2700-evb \ + -drive file=image-bmc,format=raw,if=mtd \ + -nographic + +The ``-bios`` option allows users to specify a custom path for the vbootrom +image to be loaded during boot. This will load the vbootrom image from the +specified path in the ${HOME} directory. + +.. code-block:: bash + + -bios ${HOME}/ast27x0_bootrom.bin + Aspeed minibmc family boards (``ast1030-evb``) ================================================================== From 91064bea6b2d747a981cb3bd2904e56f443e6c67 Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 2 May 2025 18:34:37 +0800 Subject: [PATCH 0623/2760] aspeed: ast27x0: Map unimplemented devices in SoC memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Maps following unimplemented devices in SoC memory - dpmcu - iomem - iomem0 - iomem1 - ltpi Iomem, Iomem0 and Iomem1 include unimplemented controllers in the memory ranges 0x0 - 0x1000000, 0x120000000 - 0x121000000 and 0x14000000 - 0x141000000. For instance: - USB hub at 0x12010000 - eSPI at 0x14C5000 - PWM at 0x140C0000 DPMCU stands for Display Port MCU controller. LTPI is used to connect to AST1700. AST1700 is an I/O expander that supports the DC-SCM 2.1 LTPI protocol. It provides AST2700 with additional GPIO, UART, I3C, and other interfaces. Signed-off-by: Steven Lee Change-Id: Iae4db49a4818af3e2c43c16a27fc76329d2405d6 Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250502103449.3091642-2-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 52 ++++++++++++++++++++++++++++++++----- include/hw/arm/aspeed_soc.h | 6 +++++ 2 files changed, 51 insertions(+), 7 deletions(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index a289e65e49..21769669df 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -23,9 +23,17 @@ #include "qobject/qlist.h" #include "qemu/log.h" +#define AST2700_SOC_IO_SIZE 0x01000000 +#define AST2700_SOC_IOMEM_SIZE 0x01000000 +#define AST2700_SOC_DPMCU_SIZE 0x00040000 +#define AST2700_SOC_LTPI_SIZE 0x01000000 + static const hwaddr aspeed_soc_ast2700_memmap[] = { + [ASPEED_DEV_IOMEM] = 0x00000000, [ASPEED_DEV_VBOOTROM] = 0x00000000, [ASPEED_DEV_SRAM] = 0x10000000, + [ASPEED_DEV_DPMCU] = 0x11000000, + [ASPEED_DEV_IOMEM0] = 0x12000000, [ASPEED_DEV_EHCI1] = 0x12061000, [ASPEED_DEV_EHCI2] = 0x12063000, [ASPEED_DEV_HACE] = 0x12070000, @@ -39,6 +47,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_TIMER1] = 0x12C10000, [ASPEED_DEV_SLI] = 0x12C17000, [ASPEED_DEV_UART4] = 0X12C1A000, + [ASPEED_DEV_IOMEM1] = 0x14000000, [ASPEED_DEV_FMC] = 0x14000000, [ASPEED_DEV_SPI0] = 0x14010000, [ASPEED_DEV_SPI1] = 0x14020000, @@ -73,6 +82,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_UART12] = 0X14C33B00, [ASPEED_DEV_WDT] = 0x14C37000, [ASPEED_DEV_SPI_BOOT] = 0x100000000, + [ASPEED_DEV_LTPI] = 0x300000000, [ASPEED_DEV_SDRAM] = 0x400000000, }; @@ -507,6 +517,16 @@ static void aspeed_soc_ast2700_init(Object *obj) snprintf(typename, sizeof(typename), "aspeed.hace-%s", socname); object_initialize_child(obj, "hace", &s->hace, typename); + object_initialize_child(obj, "dpmcu", &s->dpmcu, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "ltpi", &s->ltpi, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "iomem", &s->iomem, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "iomem0", &s->iomem0, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "iomem1", &s->iomem1, + TYPE_UNIMPLEMENTED_DEVICE); } /* @@ -542,8 +562,11 @@ static bool aspeed_soc_ast2700_gic_realize(DeviceState *dev, Error **errp) if (!sysbus_realize(gicbusdev, errp)) { return false; } - sysbus_mmio_map(gicbusdev, 0, sc->memmap[ASPEED_GIC_DIST]); - sysbus_mmio_map(gicbusdev, 1, sc->memmap[ASPEED_GIC_REDIST]); + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->gic), 0, + sc->memmap[ASPEED_GIC_DIST]); + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->gic), 1, + sc->memmap[ASPEED_GIC_REDIST]); for (i = 0; i < sc->num_cpus; i++) { DeviceState *cpudev = DEVICE(&a->cpu[i]); @@ -911,11 +934,26 @@ static void aspeed_soc_ast2700_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(SYS_BUS_DEVICE(&s->hace), 0, aspeed_soc_get_irq(s, ASPEED_DEV_HACE)); - create_unimplemented_device("ast2700.dpmcu", 0x11000000, 0x40000); - create_unimplemented_device("ast2700.iomem0", 0x12000000, 0x01000000); - create_unimplemented_device("ast2700.iomem1", 0x14000000, 0x01000000); - create_unimplemented_device("ast2700.ltpi", 0x30000000, 0x1000000); - create_unimplemented_device("ast2700.io", 0x0, 0x4000000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->dpmcu), + "aspeed.dpmcu", + sc->memmap[ASPEED_DEV_DPMCU], + AST2700_SOC_DPMCU_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->ltpi), + "aspeed.ltpi", + sc->memmap[ASPEED_DEV_LTPI], + AST2700_SOC_LTPI_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem), + "aspeed.io", + sc->memmap[ASPEED_DEV_IOMEM], + AST2700_SOC_IO_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem0), + "aspeed.iomem0", + sc->memmap[ASPEED_DEV_IOMEM0], + AST2700_SOC_IOMEM_SIZE); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->iomem1), + "aspeed.iomem1", + sc->memmap[ASPEED_DEV_IOMEM1], + AST2700_SOC_IOMEM_SIZE); } static void aspeed_soc_ast2700a0_class_init(ObjectClass *oc, const void *data) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 4dcb1010dc..5fcfd2fe2e 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -91,6 +91,8 @@ struct AspeedSoCState { SerialMM uart[ASPEED_UARTS_NUM]; Clock *sysclk; UnimplementedDeviceState iomem; + UnimplementedDeviceState iomem0; + UnimplementedDeviceState iomem1; UnimplementedDeviceState video; UnimplementedDeviceState emmc_boot_controller; UnimplementedDeviceState dpmcu; @@ -98,6 +100,7 @@ struct AspeedSoCState { UnimplementedDeviceState espi; UnimplementedDeviceState udc; UnimplementedDeviceState sgpiom; + UnimplementedDeviceState ltpi; UnimplementedDeviceState jtag[ASPEED_JTAG_NUM]; AspeedAPB2OPBState fsi[2]; }; @@ -173,6 +176,9 @@ enum { ASPEED_DEV_VBOOTROM, ASPEED_DEV_SPI_BOOT, ASPEED_DEV_IOMEM, + ASPEED_DEV_IOMEM0, + ASPEED_DEV_IOMEM1, + ASPEED_DEV_LTPI, ASPEED_DEV_UART0, ASPEED_DEV_UART1, ASPEED_DEV_UART2, From 78110f821aa0c372bb8141ce8ec9f543f8660c36 Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 2 May 2025 18:34:38 +0800 Subject: [PATCH 0624/2760] aspeed: ast27x0: Correct hex notation for device addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Corrected the hexadecimal notation for several device addresses in the aspeed_soc_ast2700_memmap array by changing the uppercase 'X' to lowercase 'x'. Signed-off-by: Steven Lee Change-Id: I45426e18ea8e68d7ccdf9b60c4ea235c4da33cc3 Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250502103449.3091642-3-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 21769669df..1974a25766 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -46,7 +46,7 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_RTC] = 0x12C0F000, [ASPEED_DEV_TIMER1] = 0x12C10000, [ASPEED_DEV_SLI] = 0x12C17000, - [ASPEED_DEV_UART4] = 0X12C1A000, + [ASPEED_DEV_UART4] = 0x12C1A000, [ASPEED_DEV_IOMEM1] = 0x14000000, [ASPEED_DEV_FMC] = 0x14000000, [ASPEED_DEV_SPI0] = 0x14010000, @@ -67,19 +67,19 @@ static const hwaddr aspeed_soc_ast2700_memmap[] = { [ASPEED_DEV_I2C] = 0x14C0F000, [ASPEED_DEV_INTCIO] = 0x14C18000, [ASPEED_DEV_SLIIO] = 0x14C1E000, - [ASPEED_DEV_VUART] = 0X14C30000, - [ASPEED_DEV_UART0] = 0X14C33000, - [ASPEED_DEV_UART1] = 0X14C33100, - [ASPEED_DEV_UART2] = 0X14C33200, - [ASPEED_DEV_UART3] = 0X14C33300, - [ASPEED_DEV_UART5] = 0X14C33400, - [ASPEED_DEV_UART6] = 0X14C33500, - [ASPEED_DEV_UART7] = 0X14C33600, - [ASPEED_DEV_UART8] = 0X14C33700, - [ASPEED_DEV_UART9] = 0X14C33800, - [ASPEED_DEV_UART10] = 0X14C33900, - [ASPEED_DEV_UART11] = 0X14C33A00, - [ASPEED_DEV_UART12] = 0X14C33B00, + [ASPEED_DEV_VUART] = 0x14C30000, + [ASPEED_DEV_UART0] = 0x14C33000, + [ASPEED_DEV_UART1] = 0x14C33100, + [ASPEED_DEV_UART2] = 0x14C33200, + [ASPEED_DEV_UART3] = 0x14C33300, + [ASPEED_DEV_UART5] = 0x14C33400, + [ASPEED_DEV_UART6] = 0x14C33500, + [ASPEED_DEV_UART7] = 0x14C33600, + [ASPEED_DEV_UART8] = 0x14C33700, + [ASPEED_DEV_UART9] = 0x14C33800, + [ASPEED_DEV_UART10] = 0x14C33900, + [ASPEED_DEV_UART11] = 0x14C33A00, + [ASPEED_DEV_UART12] = 0x14C33B00, [ASPEED_DEV_WDT] = 0x14C37000, [ASPEED_DEV_SPI_BOOT] = 0x100000000, [ASPEED_DEV_LTPI] = 0x300000000, From 8872b6717c37001e8f2e6c4ed0af20b1811d8f58 Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 2 May 2025 18:34:39 +0800 Subject: [PATCH 0625/2760] hw/intc/aspeed: Add support for AST2700 SSP INTC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Define new types for ast2700ssp INTC and INTCIO - Add register definitions for SSP INTC and INTCIO - Implement write handlers for SSP INTC and INTCIO - Register new types in aspeed_intc_register_types The design of the SSP INTC and INTCIO controllers is similar to AST2700, with the following differences: - AST2700 Support GICINT128 to GICINT136 in INTC The INTCIO GIC_192_201 has 10 output pins, mapped as follows: Bit 0 -> GIC 192 Bit 1 -> GIC 193 Bit 2 -> GIC 194 Bit 3 -> GIC 195 Bit 4 -> GIC 196 - AST2700-ssp Support SSPINT128 to SSPINT136 in INTC The INTCIO SSPINT_160_169 has 10 output pins, mapped as follows: Bit 0 -> SSPINT 160 Bit 1 -> SSPINT 161 Bit 2 -> SSPINT 162 Bit 3 -> SSPINT 163 Bit 4 -> SSPINT 164 Signed-off-by: Steven Lee Change-Id: Ib8cb0e264505cef48e17f173e057f3b2d1ea35c4 Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250502103449.3091642-4-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 211 ++++++++++++++++++++++++++++++++++ include/hw/intc/aspeed_intc.h | 3 + 2 files changed, 214 insertions(+) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index be7f516a3b..e889246951 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -62,6 +62,50 @@ REG32(GICINT196_STATUS, 0x44) REG32(GICINT197_EN, 0x50) REG32(GICINT197_STATUS, 0x54) +/* + * SSP INTC Registers + */ +REG32(SSPINT128_EN, 0x2000) +REG32(SSPINT128_STATUS, 0x2004) +REG32(SSPINT129_EN, 0x2100) +REG32(SSPINT129_STATUS, 0x2104) +REG32(SSPINT130_EN, 0x2200) +REG32(SSPINT130_STATUS, 0x2204) +REG32(SSPINT131_EN, 0x2300) +REG32(SSPINT131_STATUS, 0x2304) +REG32(SSPINT132_EN, 0x2400) +REG32(SSPINT132_STATUS, 0x2404) +REG32(SSPINT133_EN, 0x2500) +REG32(SSPINT133_STATUS, 0x2504) +REG32(SSPINT134_EN, 0x2600) +REG32(SSPINT134_STATUS, 0x2604) +REG32(SSPINT135_EN, 0x2700) +REG32(SSPINT135_STATUS, 0x2704) +REG32(SSPINT136_EN, 0x2800) +REG32(SSPINT136_STATUS, 0x2804) +REG32(SSPINT137_EN, 0x2900) +REG32(SSPINT137_STATUS, 0x2904) +REG32(SSPINT138_EN, 0x2A00) +REG32(SSPINT138_STATUS, 0x2A04) +REG32(SSPINT160_169_EN, 0x2B00) +REG32(SSPINT160_169_STATUS, 0x2B04) + +/* + * SSP INTCIO Registers + */ +REG32(SSPINT160_EN, 0x180) +REG32(SSPINT160_STATUS, 0x184) +REG32(SSPINT161_EN, 0x190) +REG32(SSPINT161_STATUS, 0x194) +REG32(SSPINT162_EN, 0x1A0) +REG32(SSPINT162_STATUS, 0x1A4) +REG32(SSPINT163_EN, 0x1B0) +REG32(SSPINT163_STATUS, 0x1B4) +REG32(SSPINT164_EN, 0x1C0) +REG32(SSPINT164_STATUS, 0x1C4) +REG32(SSPINT165_EN, 0x1D0) +REG32(SSPINT165_STATUS, 0x1D4) + static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic, uint32_t reg) { @@ -450,6 +494,50 @@ static void aspeed_intc_write(void *opaque, hwaddr offset, uint64_t data, } } +static void aspeed_ssp_intc_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + + trace_aspeed_intc_write(name, offset, size, data); + + switch (reg) { + case R_SSPINT128_EN: + case R_SSPINT129_EN: + case R_SSPINT130_EN: + case R_SSPINT131_EN: + case R_SSPINT132_EN: + case R_SSPINT133_EN: + case R_SSPINT134_EN: + case R_SSPINT135_EN: + case R_SSPINT136_EN: + case R_SSPINT160_169_EN: + aspeed_intc_enable_handler(s, offset, data); + break; + case R_SSPINT128_STATUS: + case R_SSPINT129_STATUS: + case R_SSPINT130_STATUS: + case R_SSPINT131_STATUS: + case R_SSPINT132_STATUS: + case R_SSPINT133_STATUS: + case R_SSPINT134_STATUS: + case R_SSPINT135_STATUS: + case R_SSPINT136_STATUS: + aspeed_intc_status_handler(s, offset, data); + break; + case R_SSPINT160_169_STATUS: + aspeed_intc_status_handler_multi_outpins(s, offset, data); + break; + default: + s->regs[reg] = data; + break; + } + + return; +} + static uint64_t aspeed_intcio_read(void *opaque, hwaddr offset, unsigned int size) { @@ -496,6 +584,39 @@ static void aspeed_intcio_write(void *opaque, hwaddr offset, uint64_t data, } } +static void aspeed_ssp_intcio_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + + trace_aspeed_intc_write(name, offset, size, data); + + switch (reg) { + case R_SSPINT160_EN: + case R_SSPINT161_EN: + case R_SSPINT162_EN: + case R_SSPINT163_EN: + case R_SSPINT164_EN: + case R_SSPINT165_EN: + aspeed_intc_enable_handler(s, offset, data); + break; + case R_SSPINT160_STATUS: + case R_SSPINT161_STATUS: + case R_SSPINT162_STATUS: + case R_SSPINT163_STATUS: + case R_SSPINT164_STATUS: + case R_SSPINT165_STATUS: + aspeed_intc_status_handler(s, offset, data); + break; + default: + s->regs[reg] = data; + break; + } + + return; +} static const MemoryRegionOps aspeed_intc_ops = { .read = aspeed_intc_read, @@ -517,6 +638,26 @@ static const MemoryRegionOps aspeed_intcio_ops = { } }; +static const MemoryRegionOps aspeed_ssp_intc_ops = { + .read = aspeed_intc_read, + .write = aspeed_ssp_intc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static const MemoryRegionOps aspeed_ssp_intcio_ops = { + .read = aspeed_intcio_read, + .write = aspeed_ssp_intcio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + static void aspeed_intc_instance_init(Object *obj) { AspeedINTCState *s = ASPEED_INTC(obj); @@ -674,11 +815,81 @@ static const TypeInfo aspeed_2700_intcio_info = { .class_init = aspeed_2700_intcio_class_init, }; +static AspeedINTCIRQ aspeed_2700ssp_intc_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 10, R_SSPINT160_169_EN, R_SSPINT160_169_STATUS}, + {1, 10, 1, R_SSPINT128_EN, R_SSPINT128_STATUS}, + {2, 11, 1, R_SSPINT129_EN, R_SSPINT129_STATUS}, + {3, 12, 1, R_SSPINT130_EN, R_SSPINT130_STATUS}, + {4, 13, 1, R_SSPINT131_EN, R_SSPINT131_STATUS}, + {5, 14, 1, R_SSPINT132_EN, R_SSPINT132_STATUS}, + {6, 15, 1, R_SSPINT133_EN, R_SSPINT133_STATUS}, + {7, 16, 1, R_SSPINT134_EN, R_SSPINT134_STATUS}, + {8, 17, 1, R_SSPINT135_EN, R_SSPINT135_STATUS}, + {9, 18, 1, R_SSPINT136_EN, R_SSPINT136_STATUS}, +}; + +static void aspeed_2700ssp_intc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 SSP INTC Controller"; + aic->num_lines = 32; + aic->num_inpins = 10; + aic->num_outpins = 19; + aic->mem_size = 0x4000; + aic->nr_regs = 0x2B08 >> 2; + aic->reg_offset = 0x0; + aic->reg_ops = &aspeed_ssp_intc_ops; + aic->irq_table = aspeed_2700ssp_intc_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700ssp_intc_irqs); +} + +static const TypeInfo aspeed_2700ssp_intc_info = { + .name = TYPE_ASPEED_2700SSP_INTC, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700ssp_intc_class_init, +}; + +static AspeedINTCIRQ aspeed_2700ssp_intcio_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 1, R_SSPINT160_EN, R_SSPINT160_STATUS}, + {1, 1, 1, R_SSPINT161_EN, R_SSPINT161_STATUS}, + {2, 2, 1, R_SSPINT162_EN, R_SSPINT162_STATUS}, + {3, 3, 1, R_SSPINT163_EN, R_SSPINT163_STATUS}, + {4, 4, 1, R_SSPINT164_EN, R_SSPINT164_STATUS}, + {5, 5, 1, R_SSPINT165_EN, R_SSPINT165_STATUS}, +}; + +static void aspeed_2700ssp_intcio_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 SSP INTC IO Controller"; + aic->num_lines = 32; + aic->num_inpins = 6; + aic->num_outpins = 6; + aic->mem_size = 0x400; + aic->nr_regs = 0x1d8 >> 2; + aic->reg_offset = 0; + aic->reg_ops = &aspeed_ssp_intcio_ops; + aic->irq_table = aspeed_2700ssp_intcio_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700ssp_intcio_irqs); +} + +static const TypeInfo aspeed_2700ssp_intcio_info = { + .name = TYPE_ASPEED_2700SSP_INTCIO, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700ssp_intcio_class_init, +}; + static void aspeed_intc_register_types(void) { type_register_static(&aspeed_intc_info); type_register_static(&aspeed_2700_intc_info); type_register_static(&aspeed_2700_intcio_info); + type_register_static(&aspeed_2700ssp_intc_info); + type_register_static(&aspeed_2700ssp_intcio_info); } type_init(aspeed_intc_register_types); diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h index 3727ba24be..746f159bf3 100644 --- a/include/hw/intc/aspeed_intc.h +++ b/include/hw/intc/aspeed_intc.h @@ -15,6 +15,9 @@ #define TYPE_ASPEED_INTC "aspeed.intc" #define TYPE_ASPEED_2700_INTC TYPE_ASPEED_INTC "-ast2700" #define TYPE_ASPEED_2700_INTCIO TYPE_ASPEED_INTC "io-ast2700" +#define TYPE_ASPEED_2700SSP_INTC TYPE_ASPEED_INTC "-ast2700ssp" +#define TYPE_ASPEED_2700SSP_INTCIO TYPE_ASPEED_INTC "io-ast2700ssp" + OBJECT_DECLARE_TYPE(AspeedINTCState, AspeedINTCClass, ASPEED_INTC) #define ASPEED_INTC_MAX_INPINS 10 From c528f10dce8eeaabe97f216d6488a6b8509b7067 Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 2 May 2025 18:34:40 +0800 Subject: [PATCH 0626/2760] hw/intc/aspeed: Add support for AST2700 TSP INTC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Define new types for ast2700tsp INTC and INTCIO - Add register definitions for TSP INTC and INTCIO - Implement write handlers for TSP INTC and INTCIO - Register new types in aspeed_intc_register_types The design of the TSP INTC and INTCIO controllers is similar to AST2700, with the following differences: - AST2700 Support GICINT128 to GICINT136 in INTC The INTCIO GIC_192_201 has 10 output pins, mapped as follows: Bit 0 -> GIC 192 Bit 1 -> GIC 193 Bit 2 -> GIC 194 Bit 3 -> GIC 195 Bit 4 -> GIC 196 - AST2700-tsp Support TSPINT128 to TSPINT136 in INTC The INTCIO TSPINT_160_169 has 10 output pins, mapped as follows: Bit 0 -> TSPINT 160 Bit 1 -> TSPINT 161 Bit 2 -> TSPINT 162 Bit 3 -> TSPINT 163 Bit 4 -> TSPINT 164 Signed-off-by: Steven Lee Change-Id: I3f3aca4b90129640369cf4a92deb4b9a12df5b70 Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250502103449.3091642-5-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 209 +++++++++++++++++++++++++++++++++- include/hw/intc/aspeed_intc.h | 2 + 2 files changed, 209 insertions(+), 2 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index e889246951..33fcbe729c 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -106,6 +106,51 @@ REG32(SSPINT164_STATUS, 0x1C4) REG32(SSPINT165_EN, 0x1D0) REG32(SSPINT165_STATUS, 0x1D4) +/* + * TSP INTC Registers + */ +REG32(TSPINT128_EN, 0x3000) +REG32(TSPINT128_STATUS, 0x3004) +REG32(TSPINT129_EN, 0x3100) +REG32(TSPINT129_STATUS, 0x3104) +REG32(TSPINT130_EN, 0x3200) +REG32(TSPINT130_STATUS, 0x3204) +REG32(TSPINT131_EN, 0x3300) +REG32(TSPINT131_STATUS, 0x3304) +REG32(TSPINT132_EN, 0x3400) +REG32(TSPINT132_STATUS, 0x3404) +REG32(TSPINT133_EN, 0x3500) +REG32(TSPINT133_STATUS, 0x3504) +REG32(TSPINT134_EN, 0x3600) +REG32(TSPINT134_STATUS, 0x3604) +REG32(TSPINT135_EN, 0x3700) +REG32(TSPINT135_STATUS, 0x3704) +REG32(TSPINT136_EN, 0x3800) +REG32(TSPINT136_STATUS, 0x3804) +REG32(TSPINT137_EN, 0x3900) +REG32(TSPINT137_STATUS, 0x3904) +REG32(TSPINT138_EN, 0x3A00) +REG32(TSPINT138_STATUS, 0x3A04) +REG32(TSPINT160_169_EN, 0x3B00) +REG32(TSPINT160_169_STATUS, 0x3B04) + +/* + * TSP INTCIO Registers + */ + +REG32(TSPINT160_EN, 0x200) +REG32(TSPINT160_STATUS, 0x204) +REG32(TSPINT161_EN, 0x210) +REG32(TSPINT161_STATUS, 0x214) +REG32(TSPINT162_EN, 0x220) +REG32(TSPINT162_STATUS, 0x224) +REG32(TSPINT163_EN, 0x230) +REG32(TSPINT163_STATUS, 0x234) +REG32(TSPINT164_EN, 0x240) +REG32(TSPINT164_STATUS, 0x244) +REG32(TSPINT165_EN, 0x250) +REG32(TSPINT165_STATUS, 0x254) + static const AspeedINTCIRQ *aspeed_intc_get_irq(AspeedINTCClass *aic, uint32_t reg) { @@ -534,8 +579,48 @@ static void aspeed_ssp_intc_write(void *opaque, hwaddr offset, uint64_t data, s->regs[reg] = data; break; } +} + +static void aspeed_tsp_intc_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + + trace_aspeed_intc_write(name, offset, size, data); - return; + switch (reg) { + case R_TSPINT128_EN: + case R_TSPINT129_EN: + case R_TSPINT130_EN: + case R_TSPINT131_EN: + case R_TSPINT132_EN: + case R_TSPINT133_EN: + case R_TSPINT134_EN: + case R_TSPINT135_EN: + case R_TSPINT136_EN: + case R_TSPINT160_169_EN: + aspeed_intc_enable_handler(s, offset, data); + break; + case R_TSPINT128_STATUS: + case R_TSPINT129_STATUS: + case R_TSPINT130_STATUS: + case R_TSPINT131_STATUS: + case R_TSPINT132_STATUS: + case R_TSPINT133_STATUS: + case R_TSPINT134_STATUS: + case R_TSPINT135_STATUS: + case R_TSPINT136_STATUS: + aspeed_intc_status_handler(s, offset, data); + break; + case R_TSPINT160_169_STATUS: + aspeed_intc_status_handler_multi_outpins(s, offset, data); + break; + default: + s->regs[reg] = data; + break; + } } static uint64_t aspeed_intcio_read(void *opaque, hwaddr offset, @@ -614,8 +699,38 @@ static void aspeed_ssp_intcio_write(void *opaque, hwaddr offset, uint64_t data, s->regs[reg] = data; break; } +} + +static void aspeed_tsp_intcio_write(void *opaque, hwaddr offset, uint64_t data, + unsigned size) +{ + AspeedINTCState *s = ASPEED_INTC(opaque); + const char *name = object_get_typename(OBJECT(s)); + uint32_t reg = offset >> 2; + + trace_aspeed_intc_write(name, offset, size, data); - return; + switch (reg) { + case R_TSPINT160_EN: + case R_TSPINT161_EN: + case R_TSPINT162_EN: + case R_TSPINT163_EN: + case R_TSPINT164_EN: + case R_TSPINT165_EN: + aspeed_intc_enable_handler(s, offset, data); + break; + case R_TSPINT160_STATUS: + case R_TSPINT161_STATUS: + case R_TSPINT162_STATUS: + case R_TSPINT163_STATUS: + case R_TSPINT164_STATUS: + case R_TSPINT165_STATUS: + aspeed_intc_status_handler(s, offset, data); + break; + default: + s->regs[reg] = data; + break; + } } static const MemoryRegionOps aspeed_intc_ops = { @@ -658,6 +773,26 @@ static const MemoryRegionOps aspeed_ssp_intcio_ops = { } }; +static const MemoryRegionOps aspeed_tsp_intc_ops = { + .read = aspeed_intc_read, + .write = aspeed_tsp_intc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + +static const MemoryRegionOps aspeed_tsp_intcio_ops = { + .read = aspeed_intcio_read, + .write = aspeed_tsp_intcio_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + } +}; + static void aspeed_intc_instance_init(Object *obj) { AspeedINTCState *s = ASPEED_INTC(obj); @@ -883,6 +1018,74 @@ static const TypeInfo aspeed_2700ssp_intcio_info = { .class_init = aspeed_2700ssp_intcio_class_init, }; +static AspeedINTCIRQ aspeed_2700tsp_intc_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 10, R_TSPINT160_169_EN, R_TSPINT160_169_STATUS}, + {1, 10, 1, R_TSPINT128_EN, R_TSPINT128_STATUS}, + {2, 11, 1, R_TSPINT129_EN, R_TSPINT129_STATUS}, + {3, 12, 1, R_TSPINT130_EN, R_TSPINT130_STATUS}, + {4, 13, 1, R_TSPINT131_EN, R_TSPINT131_STATUS}, + {5, 14, 1, R_TSPINT132_EN, R_TSPINT132_STATUS}, + {6, 15, 1, R_TSPINT133_EN, R_TSPINT133_STATUS}, + {7, 16, 1, R_TSPINT134_EN, R_TSPINT134_STATUS}, + {8, 17, 1, R_TSPINT135_EN, R_TSPINT135_STATUS}, + {9, 18, 1, R_TSPINT136_EN, R_TSPINT136_STATUS}, +}; + +static void aspeed_2700tsp_intc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 TSP INTC Controller"; + aic->num_lines = 32; + aic->num_inpins = 10; + aic->num_outpins = 19; + aic->mem_size = 0x4000; + aic->nr_regs = 0x3B08 >> 2; + aic->reg_offset = 0; + aic->reg_ops = &aspeed_tsp_intc_ops; + aic->irq_table = aspeed_2700tsp_intc_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700tsp_intc_irqs); +} + +static const TypeInfo aspeed_2700tsp_intc_info = { + .name = TYPE_ASPEED_2700TSP_INTC, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700tsp_intc_class_init, +}; + +static AspeedINTCIRQ aspeed_2700tsp_intcio_irqs[ASPEED_INTC_MAX_INPINS] = { + {0, 0, 1, R_TSPINT160_EN, R_TSPINT160_STATUS}, + {1, 1, 1, R_TSPINT161_EN, R_TSPINT161_STATUS}, + {2, 2, 1, R_TSPINT162_EN, R_TSPINT162_STATUS}, + {3, 3, 1, R_TSPINT163_EN, R_TSPINT163_STATUS}, + {4, 4, 1, R_TSPINT164_EN, R_TSPINT164_STATUS}, + {5, 5, 1, R_TSPINT165_EN, R_TSPINT165_STATUS}, +}; + +static void aspeed_2700tsp_intcio_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); + + dc->desc = "ASPEED 2700 TSP INTC IO Controller"; + aic->num_lines = 32; + aic->num_inpins = 6; + aic->num_outpins = 6; + aic->mem_size = 0x400; + aic->nr_regs = 0x258 >> 2; + aic->reg_offset = 0x0; + aic->reg_ops = &aspeed_tsp_intcio_ops; + aic->irq_table = aspeed_2700tsp_intcio_irqs; + aic->irq_table_count = ARRAY_SIZE(aspeed_2700tsp_intcio_irqs); +} + +static const TypeInfo aspeed_2700tsp_intcio_info = { + .name = TYPE_ASPEED_2700TSP_INTCIO, + .parent = TYPE_ASPEED_INTC, + .class_init = aspeed_2700tsp_intcio_class_init, +}; + static void aspeed_intc_register_types(void) { type_register_static(&aspeed_intc_info); @@ -890,6 +1093,8 @@ static void aspeed_intc_register_types(void) type_register_static(&aspeed_2700_intcio_info); type_register_static(&aspeed_2700ssp_intc_info); type_register_static(&aspeed_2700ssp_intcio_info); + type_register_static(&aspeed_2700tsp_intc_info); + type_register_static(&aspeed_2700tsp_intcio_info); } type_init(aspeed_intc_register_types); diff --git a/include/hw/intc/aspeed_intc.h b/include/hw/intc/aspeed_intc.h index 746f159bf3..51288384a5 100644 --- a/include/hw/intc/aspeed_intc.h +++ b/include/hw/intc/aspeed_intc.h @@ -17,6 +17,8 @@ #define TYPE_ASPEED_2700_INTCIO TYPE_ASPEED_INTC "io-ast2700" #define TYPE_ASPEED_2700SSP_INTC TYPE_ASPEED_INTC "-ast2700ssp" #define TYPE_ASPEED_2700SSP_INTCIO TYPE_ASPEED_INTC "io-ast2700ssp" +#define TYPE_ASPEED_2700TSP_INTC TYPE_ASPEED_INTC "-ast2700tsp" +#define TYPE_ASPEED_2700TSP_INTCIO TYPE_ASPEED_INTC "io-ast2700tsp" OBJECT_DECLARE_TYPE(AspeedINTCState, AspeedINTCClass, ASPEED_INTC) From 541da2604fe157d9db5995808097230a9f966b4a Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 2 May 2025 18:34:41 +0800 Subject: [PATCH 0627/2760] hw/arm/aspeed_ast27x0-ssp: Introduce AST27x0 A1 SSP SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AST2700 SSP (Secondary Service Processor) is a Cortex-M4 coprocessor. This patch adds support for A1 SSP with the following updates: - Introduce Aspeed27x0SSPSoCState structure in aspeed_soc.h - Define memory map and IRQ map for AST27x0 A1 SSP SoC - Implement initialization and realization functions - Add support for UART, INTC, and SCU devices - Map unimplemented devices for IPC and SCUIO The IRQ mapping is similar to AST2700 CA35 SoC, featuring a two-level interrupt controller. Difference from AST2700: - AST2700 - Support GICINT128 to GICINT136 in INTC - The INTCIO GIC_192_201 has 10 output pins, mapped as follows: Bit 0 -> GIC 192 Bit 1 -> GIC 193 Bit 2 -> GIC 194 Bit 3 -> GIC 195 Bit 4 -> GIC 196 - AST2700-ssp - Support SSPINT128 to SSPINT136 in INTC - The INTCIO SSPINT_160_169 has 10 output pins, mapped as follows: Bit 0 -> SSPINT 160 Bit 1 -> SSPINT 161 Bit 2 -> SSPINT 162 Bit 3 -> SSPINT 163 Bit 4 -> SSPINT 164 Signed-off-by: Steven Lee Change-Id: I924bf1a657f1e83f9e16d6673713f4a06ecdb496 Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250502103449.3091642-6-steven_lee@aspeedtech.com [ clg: removed local 'Error* err' in aspeed_soc_ast27x0ssp_realize() ] Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-ssp.c | 294 ++++++++++++++++++++++++++++++++++++ hw/arm/meson.build | 1 + include/hw/arm/aspeed_soc.h | 14 ++ 3 files changed, 309 insertions(+) create mode 100644 hw/arm/aspeed_ast27x0-ssp.c diff --git a/hw/arm/aspeed_ast27x0-ssp.c b/hw/arm/aspeed_ast27x0-ssp.c new file mode 100644 index 0000000000..80ec5996c1 --- /dev/null +++ b/hw/arm/aspeed_ast27x0-ssp.c @@ -0,0 +1,294 @@ +/* + * ASPEED Ast27x0 SSP SoC + * + * Copyright (C) 2025 ASPEED Technology Inc. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/qdev-clock.h" +#include "hw/misc/unimp.h" +#include "hw/arm/aspeed_soc.h" + +#define AST2700_SSP_RAM_SIZE (32 * MiB) + +static const hwaddr aspeed_soc_ast27x0ssp_memmap[] = { + [ASPEED_DEV_SRAM] = 0x00000000, + [ASPEED_DEV_INTC] = 0x72100000, + [ASPEED_DEV_SCU] = 0x72C02000, + [ASPEED_DEV_SCUIO] = 0x74C02000, + [ASPEED_DEV_UART0] = 0x74C33000, + [ASPEED_DEV_UART1] = 0x74C33100, + [ASPEED_DEV_UART2] = 0x74C33200, + [ASPEED_DEV_UART3] = 0x74C33300, + [ASPEED_DEV_UART4] = 0x72C1A000, + [ASPEED_DEV_INTCIO] = 0x74C18000, + [ASPEED_DEV_IPC0] = 0x72C1C000, + [ASPEED_DEV_IPC1] = 0x74C39000, + [ASPEED_DEV_UART5] = 0x74C33400, + [ASPEED_DEV_UART6] = 0x74C33500, + [ASPEED_DEV_UART7] = 0x74C33600, + [ASPEED_DEV_UART8] = 0x74C33700, + [ASPEED_DEV_UART9] = 0x74C33800, + [ASPEED_DEV_UART10] = 0x74C33900, + [ASPEED_DEV_UART11] = 0x74C33A00, + [ASPEED_DEV_UART12] = 0x74C33B00, + [ASPEED_DEV_TIMER1] = 0x72C10000, +}; + +static const int aspeed_soc_ast27x0ssp_irqmap[] = { + [ASPEED_DEV_SCU] = 12, + [ASPEED_DEV_UART0] = 164, + [ASPEED_DEV_UART1] = 164, + [ASPEED_DEV_UART2] = 164, + [ASPEED_DEV_UART3] = 164, + [ASPEED_DEV_UART4] = 8, + [ASPEED_DEV_UART5] = 164, + [ASPEED_DEV_UART6] = 164, + [ASPEED_DEV_UART7] = 164, + [ASPEED_DEV_UART8] = 164, + [ASPEED_DEV_UART9] = 164, + [ASPEED_DEV_UART10] = 164, + [ASPEED_DEV_UART11] = 164, + [ASPEED_DEV_UART12] = 164, + [ASPEED_DEV_TIMER1] = 16, +}; + +/* SSPINT 164 */ +static const int ast2700_ssp132_ssp164_intcmap[] = { + [ASPEED_DEV_UART0] = 7, + [ASPEED_DEV_UART1] = 8, + [ASPEED_DEV_UART2] = 9, + [ASPEED_DEV_UART3] = 10, + [ASPEED_DEV_UART5] = 11, + [ASPEED_DEV_UART6] = 12, + [ASPEED_DEV_UART7] = 13, + [ASPEED_DEV_UART8] = 14, + [ASPEED_DEV_UART9] = 15, + [ASPEED_DEV_UART10] = 16, + [ASPEED_DEV_UART11] = 17, + [ASPEED_DEV_UART12] = 18, +}; + +struct nvic_intc_irq_info { + int irq; + int intc_idx; + int orgate_idx; + const int *ptr; +}; + +static struct nvic_intc_irq_info ast2700_ssp_intcmap[] = { + {160, 1, 0, NULL}, + {161, 1, 1, NULL}, + {162, 1, 2, NULL}, + {163, 1, 3, NULL}, + {164, 1, 4, ast2700_ssp132_ssp164_intcmap}, + {165, 1, 5, NULL}, + {166, 1, 6, NULL}, + {167, 1, 7, NULL}, + {168, 1, 8, NULL}, + {169, 1, 9, NULL}, + {128, 0, 1, NULL}, + {129, 0, 2, NULL}, + {130, 0, 3, NULL}, + {131, 0, 4, NULL}, + {132, 0, 5, ast2700_ssp132_ssp164_intcmap}, + {133, 0, 6, NULL}, + {134, 0, 7, NULL}, + {135, 0, 8, NULL}, + {136, 0, 9, NULL}, +}; + +static qemu_irq aspeed_soc_ast27x0ssp_get_irq(AspeedSoCState *s, int dev) +{ + Aspeed27x0SSPSoCState *a = ASPEED27X0SSP_SOC(s); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + + int or_idx; + int idx; + int i; + + for (i = 0; i < ARRAY_SIZE(ast2700_ssp_intcmap); i++) { + if (sc->irqmap[dev] == ast2700_ssp_intcmap[i].irq) { + assert(ast2700_ssp_intcmap[i].ptr); + or_idx = ast2700_ssp_intcmap[i].orgate_idx; + idx = ast2700_ssp_intcmap[i].intc_idx; + return qdev_get_gpio_in(DEVICE(&a->intc[idx].orgates[or_idx]), + ast2700_ssp_intcmap[i].ptr[dev]); + } + } + + return qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[dev]); +} + +static void aspeed_soc_ast27x0ssp_init(Object *obj) +{ + Aspeed27x0SSPSoCState *a = ASPEED27X0SSP_SOC(obj); + AspeedSoCState *s = ASPEED_SOC(obj); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int i; + + object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M); + object_initialize_child(obj, "scu", &s->scu, TYPE_ASPEED_2700_SCU); + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); + qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", sc->silicon_rev); + + for (i = 0; i < sc->uarts_num; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); + } + + object_initialize_child(obj, "intc0", &a->intc[0], + TYPE_ASPEED_2700SSP_INTC); + object_initialize_child(obj, "intc1", &a->intc[1], + TYPE_ASPEED_2700SSP_INTCIO); + + object_initialize_child(obj, "timerctrl", &s->timerctrl, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "ipc0", &a->ipc[0], + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "ipc1", &a->ipc[1], + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "scuio", &a->scuio, + TYPE_UNIMPLEMENTED_DEVICE); +} + +static void aspeed_soc_ast27x0ssp_realize(DeviceState *dev_soc, Error **errp) +{ + Aspeed27x0SSPSoCState *a = ASPEED27X0SSP_SOC(dev_soc); + AspeedSoCState *s = ASPEED_SOC(dev_soc); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + DeviceState *armv7m; + g_autofree char *sram_name = NULL; + int i; + + if (!clock_has_source(s->sysclk)) { + error_setg(errp, "sysclk clock must be wired up by the board code"); + return; + } + + /* AST27X0 SSP Core */ + armv7m = DEVICE(&a->armv7m); + qdev_prop_set_uint32(armv7m, "num-irq", 256); + qdev_prop_set_string(armv7m, "cpu-type", aspeed_soc_cpu_type(sc)); + qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); + object_property_set_link(OBJECT(&a->armv7m), "memory", + OBJECT(s->memory), &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&a->armv7m), &error_abort); + + sram_name = g_strdup_printf("aspeed.dram.%d", + CPU(a->armv7m.cpu)->cpu_index); + + if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, + errp)) { + return; + } + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_SRAM], + &s->sram); + + /* SCU */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + + /* INTC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[0]), 0, + sc->memmap[ASPEED_DEV_INTC]); + + /* INTCIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[1]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[1]), 0, + sc->memmap[ASPEED_DEV_INTCIO]); + + /* irq source orgates -> INTC0 */ + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[0])->num_inpins; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc[0].orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc[0]), i)); + } + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[0])->num_outpins; i++) { + assert(i < ARRAY_SIZE(ast2700_ssp_intcmap)); + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[0]), i, + qdev_get_gpio_in(DEVICE(&a->armv7m), + ast2700_ssp_intcmap[i].irq)); + } + /* irq source orgates -> INTCIO */ + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[1])->num_inpins; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc[1].orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc[1]), i)); + } + /* INTCIO -> INTC */ + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[1])->num_outpins; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[1]), i, + qdev_get_gpio_in(DEVICE(&a->intc[0].orgates[0]), i)); + } + /* UART */ + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } + + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->timerctrl), + "aspeed.timerctrl", + sc->memmap[ASPEED_DEV_TIMER1], 0x200); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->ipc[0]), + "aspeed.ipc0", + sc->memmap[ASPEED_DEV_IPC0], 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->ipc[1]), + "aspeed.ipc1", + sc->memmap[ASPEED_DEV_IPC1], 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->scuio), + "aspeed.scuio", + sc->memmap[ASPEED_DEV_SCUIO], 0x1000); +} + +static void aspeed_soc_ast27x0ssp_class_init(ObjectClass *klass, const void *data) +{ + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m4"), /* TODO: cortex-m4f */ + NULL + }; + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSoCClass *sc = ASPEED_SOC_CLASS(dc); + + /* Reason: The Aspeed SoC can only be instantiated from a board */ + dc->user_creatable = false; + dc->realize = aspeed_soc_ast27x0ssp_realize; + + sc->valid_cpu_types = valid_cpu_types; + sc->silicon_rev = AST2700_A1_SILICON_REV; + sc->sram_size = AST2700_SSP_RAM_SIZE; + sc->spis_num = 0; + sc->ehcis_num = 0; + sc->wdts_num = 0; + sc->macs_num = 0; + sc->uarts_num = 13; + sc->uarts_base = ASPEED_DEV_UART0; + sc->irqmap = aspeed_soc_ast27x0ssp_irqmap; + sc->memmap = aspeed_soc_ast27x0ssp_memmap; + sc->num_cpus = 1; + sc->get_irq = aspeed_soc_ast27x0ssp_get_irq; +} + +static const TypeInfo aspeed_soc_ast27x0ssp_types[] = { + { + .name = TYPE_ASPEED27X0SSP_SOC, + .parent = TYPE_ASPEED_SOC, + .instance_size = sizeof(Aspeed27x0SSPSoCState), + .instance_init = aspeed_soc_ast27x0ssp_init, + .class_init = aspeed_soc_ast27x0ssp_class_init, + }, +}; + +DEFINE_TYPES(aspeed_soc_ast27x0ssp_types) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 09b1cfe5b5..39b74a89ed 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -44,6 +44,7 @@ arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_soc_common.c', 'aspeed_ast2400.c', 'aspeed_ast2600.c', + 'aspeed_ast27x0-ssp.c', 'aspeed_ast10x0.c', 'aspeed_eeprom.c', 'fby35.c')) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 5fcfd2fe2e..32be90bc35 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -146,6 +146,18 @@ struct Aspeed10x0SoCState { ARMv7MState armv7m; }; +struct Aspeed27x0SSPSoCState { + AspeedSoCState parent; + AspeedINTCState intc[2]; + UnimplementedDeviceState ipc[2]; + UnimplementedDeviceState scuio; + + ARMv7MState armv7m; +}; + +#define TYPE_ASPEED27X0SSP_SOC "aspeed27x0ssp-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0SSPSoCState, ASPEED27X0SSP_SOC) + #define TYPE_ASPEED10X0_SOC "aspeed10x0-soc" OBJECT_DECLARE_SIMPLE_TYPE(Aspeed10x0SoCState, ASPEED10X0_SOC) @@ -259,6 +271,8 @@ enum { ASPEED_DEV_SLIIO, ASPEED_GIC_DIST, ASPEED_GIC_REDIST, + ASPEED_DEV_IPC0, + ASPEED_DEV_IPC1, }; qemu_irq aspeed_soc_get_irq(AspeedSoCState *s, int dev); From 2d64e6a009e1efbf6d9ae63a3dc5d35f356641e7 Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 2 May 2025 18:34:42 +0800 Subject: [PATCH 0628/2760] hw/arm/aspeed_ast27x0-tsp: Introduce AST27x0 A1 TSP SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AST2700 TSP(Tertiary Service Processor) is a Cortex-M4 coprocessor The patch adds support for TSP with following update: - Introduce Aspeed27x0TSPSoCState structure in aspeed_soc.h - Implement initialization and realization functions - Add support for UART, INTC, and SCU devices - Map unimplemented devices for IPC and SCUIO - Defined memory map and IRQ maps for AST27x0 A1 TSP SoC The IRQ mapping is similar to AST2700 CA35 SoC, featuring a two-level interrupt controller. Difference from AST2700: - AST2700 - Support GICINT128 to GICINT136 in INTC - The INTCIO GIC_192_201 has 10 output pins, mapped as follows: Bit 0 -> GIC 192 Bit 1 -> GIC 193 Bit 2 -> GIC 194 Bit 3 -> GIC 195 Bit 4 -> GIC 196 - AST2700-tsp - Support TSPINT128 to TSPINT136 in INTC - The INTCIO TSPINT_160_169 has 10 output pins, mapped as follows: Bit 0 -> TSPINT 160 Bit 1 -> TSPINT 161 Bit 2 -> TSPINT 162 Bit 3 -> TSPINT 163 Bit 4 -> TSPINT 164 Signed-off-by: Steven Lee Change-Id: I69eec2b68b26ef04187b2922c5f2e584b9076c66 Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250502103449.3091642-7-steven_lee@aspeedtech.com [ clg: removed local 'Error* err' in aspeed_soc_ast27x0tsp_realize() ] Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-tsp.c | 294 ++++++++++++++++++++++++++++++++++++ hw/arm/meson.build | 1 + include/hw/arm/aspeed_soc.h | 12 ++ 3 files changed, 307 insertions(+) create mode 100644 hw/arm/aspeed_ast27x0-tsp.c diff --git a/hw/arm/aspeed_ast27x0-tsp.c b/hw/arm/aspeed_ast27x0-tsp.c new file mode 100644 index 0000000000..4e0efaef07 --- /dev/null +++ b/hw/arm/aspeed_ast27x0-tsp.c @@ -0,0 +1,294 @@ +/* + * ASPEED Ast27x0 TSP SoC + * + * Copyright (C) 2025 ASPEED Technology Inc. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/qdev-clock.h" +#include "hw/misc/unimp.h" +#include "hw/arm/aspeed_soc.h" + +#define AST2700_TSP_RAM_SIZE (32 * MiB) + +static const hwaddr aspeed_soc_ast27x0tsp_memmap[] = { + [ASPEED_DEV_SRAM] = 0x00000000, + [ASPEED_DEV_INTC] = 0x72100000, + [ASPEED_DEV_SCU] = 0x72C02000, + [ASPEED_DEV_SCUIO] = 0x74C02000, + [ASPEED_DEV_UART0] = 0x74C33000, + [ASPEED_DEV_UART1] = 0x74C33100, + [ASPEED_DEV_UART2] = 0x74C33200, + [ASPEED_DEV_UART3] = 0x74C33300, + [ASPEED_DEV_UART4] = 0x72C1A000, + [ASPEED_DEV_INTCIO] = 0x74C18000, + [ASPEED_DEV_IPC0] = 0x72C1C000, + [ASPEED_DEV_IPC1] = 0x74C39000, + [ASPEED_DEV_UART5] = 0x74C33400, + [ASPEED_DEV_UART6] = 0x74C33500, + [ASPEED_DEV_UART7] = 0x74C33600, + [ASPEED_DEV_UART8] = 0x74C33700, + [ASPEED_DEV_UART9] = 0x74C33800, + [ASPEED_DEV_UART10] = 0x74C33900, + [ASPEED_DEV_UART11] = 0x74C33A00, + [ASPEED_DEV_UART12] = 0x74C33B00, + [ASPEED_DEV_TIMER1] = 0x72C10000, +}; + +static const int aspeed_soc_ast27x0tsp_irqmap[] = { + [ASPEED_DEV_SCU] = 12, + [ASPEED_DEV_UART0] = 164, + [ASPEED_DEV_UART1] = 164, + [ASPEED_DEV_UART2] = 164, + [ASPEED_DEV_UART3] = 164, + [ASPEED_DEV_UART4] = 8, + [ASPEED_DEV_UART5] = 164, + [ASPEED_DEV_UART6] = 164, + [ASPEED_DEV_UART7] = 164, + [ASPEED_DEV_UART8] = 164, + [ASPEED_DEV_UART9] = 164, + [ASPEED_DEV_UART10] = 164, + [ASPEED_DEV_UART11] = 164, + [ASPEED_DEV_UART12] = 164, + [ASPEED_DEV_TIMER1] = 16, +}; + +/* TSPINT 164 */ +static const int ast2700_tsp132_tsp164_intcmap[] = { + [ASPEED_DEV_UART0] = 7, + [ASPEED_DEV_UART1] = 8, + [ASPEED_DEV_UART2] = 9, + [ASPEED_DEV_UART3] = 10, + [ASPEED_DEV_UART5] = 11, + [ASPEED_DEV_UART6] = 12, + [ASPEED_DEV_UART7] = 13, + [ASPEED_DEV_UART8] = 14, + [ASPEED_DEV_UART9] = 15, + [ASPEED_DEV_UART10] = 16, + [ASPEED_DEV_UART11] = 17, + [ASPEED_DEV_UART12] = 18, +}; + +struct nvic_intc_irq_info { + int irq; + int intc_idx; + int orgate_idx; + const int *ptr; +}; + +static struct nvic_intc_irq_info ast2700_tsp_intcmap[] = { + {160, 1, 0, NULL}, + {161, 1, 1, NULL}, + {162, 1, 2, NULL}, + {163, 1, 3, NULL}, + {164, 1, 4, ast2700_tsp132_tsp164_intcmap}, + {165, 1, 5, NULL}, + {166, 1, 6, NULL}, + {167, 1, 7, NULL}, + {168, 1, 8, NULL}, + {169, 1, 9, NULL}, + {128, 0, 1, NULL}, + {129, 0, 2, NULL}, + {130, 0, 3, NULL}, + {131, 0, 4, NULL}, + {132, 0, 5, ast2700_tsp132_tsp164_intcmap}, + {133, 0, 6, NULL}, + {134, 0, 7, NULL}, + {135, 0, 8, NULL}, + {136, 0, 9, NULL}, +}; + +static qemu_irq aspeed_soc_ast27x0tsp_get_irq(AspeedSoCState *s, int dev) +{ + Aspeed27x0TSPSoCState *a = ASPEED27X0TSP_SOC(s); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + + int or_idx; + int idx; + int i; + + for (i = 0; i < ARRAY_SIZE(ast2700_tsp_intcmap); i++) { + if (sc->irqmap[dev] == ast2700_tsp_intcmap[i].irq) { + assert(ast2700_tsp_intcmap[i].ptr); + or_idx = ast2700_tsp_intcmap[i].orgate_idx; + idx = ast2700_tsp_intcmap[i].intc_idx; + return qdev_get_gpio_in(DEVICE(&a->intc[idx].orgates[or_idx]), + ast2700_tsp_intcmap[i].ptr[dev]); + } + } + + return qdev_get_gpio_in(DEVICE(&a->armv7m), sc->irqmap[dev]); +} + +static void aspeed_soc_ast27x0tsp_init(Object *obj) +{ + Aspeed27x0TSPSoCState *a = ASPEED27X0TSP_SOC(obj); + AspeedSoCState *s = ASPEED_SOC(obj); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + int i; + + object_initialize_child(obj, "armv7m", &a->armv7m, TYPE_ARMV7M); + object_initialize_child(obj, "scu", &s->scu, TYPE_ASPEED_2700_SCU); + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); + qdev_prop_set_uint32(DEVICE(&s->scu), "silicon-rev", sc->silicon_rev); + + for (i = 0; i < sc->uarts_num; i++) { + object_initialize_child(obj, "uart[*]", &s->uart[i], TYPE_SERIAL_MM); + } + + object_initialize_child(obj, "intc0", &a->intc[0], + TYPE_ASPEED_2700TSP_INTC); + object_initialize_child(obj, "intc1", &a->intc[1], + TYPE_ASPEED_2700TSP_INTCIO); + + object_initialize_child(obj, "timerctrl", &s->timerctrl, + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "ipc0", &a->ipc[0], + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "ipc1", &a->ipc[1], + TYPE_UNIMPLEMENTED_DEVICE); + object_initialize_child(obj, "scuio", &a->scuio, + TYPE_UNIMPLEMENTED_DEVICE); +} + +static void aspeed_soc_ast27x0tsp_realize(DeviceState *dev_soc, Error **errp) +{ + Aspeed27x0TSPSoCState *a = ASPEED27X0TSP_SOC(dev_soc); + AspeedSoCState *s = ASPEED_SOC(dev_soc); + AspeedSoCClass *sc = ASPEED_SOC_GET_CLASS(s); + DeviceState *armv7m; + g_autofree char *sram_name = NULL; + int i; + + if (!clock_has_source(s->sysclk)) { + error_setg(errp, "sysclk clock must be wired up by the board code"); + return; + } + + /* AST27X0 TSP Core */ + armv7m = DEVICE(&a->armv7m); + qdev_prop_set_uint32(armv7m, "num-irq", 256); + qdev_prop_set_string(armv7m, "cpu-type", aspeed_soc_cpu_type(sc)); + qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); + object_property_set_link(OBJECT(&a->armv7m), "memory", + OBJECT(s->memory), &error_abort); + sysbus_realize(SYS_BUS_DEVICE(&a->armv7m), &error_abort); + + sram_name = g_strdup_printf("aspeed.dram.%d", + CPU(a->armv7m.cpu)->cpu_index); + + if (!memory_region_init_ram(&s->sram, OBJECT(s), sram_name, sc->sram_size, + errp)) { + return; + } + memory_region_add_subregion(s->memory, + sc->memmap[ASPEED_DEV_SRAM], + &s->sram); + + /* SCU */ + if (!sysbus_realize(SYS_BUS_DEVICE(&s->scu), errp)) { + return; + } + aspeed_mmio_map(s, SYS_BUS_DEVICE(&s->scu), 0, sc->memmap[ASPEED_DEV_SCU]); + + /* INTC */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[0]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[0]), 0, + sc->memmap[ASPEED_DEV_INTC]); + + /* INTCIO */ + if (!sysbus_realize(SYS_BUS_DEVICE(&a->intc[1]), errp)) { + return; + } + + aspeed_mmio_map(s, SYS_BUS_DEVICE(&a->intc[1]), 0, + sc->memmap[ASPEED_DEV_INTCIO]); + + /* irq source orgates -> INTC */ + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[0])->num_inpins; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc[0].orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc[0]), i)); + } + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[0])->num_outpins; i++) { + assert(i < ARRAY_SIZE(ast2700_tsp_intcmap)); + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[0]), i, + qdev_get_gpio_in(DEVICE(&a->armv7m), + ast2700_tsp_intcmap[i].irq)); + } + /* irq source orgates -> INTC */ + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[1])->num_inpins; i++) { + qdev_connect_gpio_out(DEVICE(&a->intc[1].orgates[i]), 0, + qdev_get_gpio_in(DEVICE(&a->intc[1]), i)); + } + /* INTCIO -> INTC */ + for (i = 0; i < ASPEED_INTC_GET_CLASS(&a->intc[1])->num_outpins; i++) { + sysbus_connect_irq(SYS_BUS_DEVICE(&a->intc[1]), i, + qdev_get_gpio_in(DEVICE(&a->intc[0].orgates[0]), i)); + } + /* UART */ + if (!aspeed_soc_uart_realize(s, errp)) { + return; + } + + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&s->timerctrl), + "aspeed.timerctrl", + sc->memmap[ASPEED_DEV_TIMER1], 0x200); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->ipc[0]), + "aspeed.ipc0", + sc->memmap[ASPEED_DEV_IPC0], 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->ipc[1]), + "aspeed.ipc1", + sc->memmap[ASPEED_DEV_IPC1], 0x1000); + aspeed_mmio_map_unimplemented(s, SYS_BUS_DEVICE(&a->scuio), + "aspeed.scuio", + sc->memmap[ASPEED_DEV_SCUIO], 0x1000); +} + +static void aspeed_soc_ast27x0tsp_class_init(ObjectClass *klass, const void *data) +{ + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m4"), /* TODO cortex-m4f */ + NULL + }; + DeviceClass *dc = DEVICE_CLASS(klass); + AspeedSoCClass *sc = ASPEED_SOC_CLASS(dc); + + /* Reason: The Aspeed SoC can only be instantiated from a board */ + dc->user_creatable = false; + dc->realize = aspeed_soc_ast27x0tsp_realize; + + sc->valid_cpu_types = valid_cpu_types; + sc->silicon_rev = AST2700_A1_SILICON_REV; + sc->sram_size = AST2700_TSP_RAM_SIZE; + sc->spis_num = 0; + sc->ehcis_num = 0; + sc->wdts_num = 0; + sc->macs_num = 0; + sc->uarts_num = 13; + sc->uarts_base = ASPEED_DEV_UART0; + sc->irqmap = aspeed_soc_ast27x0tsp_irqmap; + sc->memmap = aspeed_soc_ast27x0tsp_memmap; + sc->num_cpus = 1; + sc->get_irq = aspeed_soc_ast27x0tsp_get_irq; +} + +static const TypeInfo aspeed_soc_ast27x0tsp_types[] = { + { + .name = TYPE_ASPEED27X0TSP_SOC, + .parent = TYPE_ASPEED_SOC, + .instance_size = sizeof(Aspeed27x0TSPSoCState), + .instance_init = aspeed_soc_ast27x0tsp_init, + .class_init = aspeed_soc_ast27x0tsp_class_init, + }, +}; + +DEFINE_TYPES(aspeed_soc_ast27x0tsp_types) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 39b74a89ed..98c5631506 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -45,6 +45,7 @@ arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_ast2400.c', 'aspeed_ast2600.c', 'aspeed_ast27x0-ssp.c', + 'aspeed_ast27x0-tsp.c', 'aspeed_ast10x0.c', 'aspeed_eeprom.c', 'fby35.c')) diff --git a/include/hw/arm/aspeed_soc.h b/include/hw/arm/aspeed_soc.h index 32be90bc35..217ef0eafd 100644 --- a/include/hw/arm/aspeed_soc.h +++ b/include/hw/arm/aspeed_soc.h @@ -158,6 +158,18 @@ struct Aspeed27x0SSPSoCState { #define TYPE_ASPEED27X0SSP_SOC "aspeed27x0ssp-soc" OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0SSPSoCState, ASPEED27X0SSP_SOC) +struct Aspeed27x0TSPSoCState { + AspeedSoCState parent; + AspeedINTCState intc[2]; + UnimplementedDeviceState ipc[2]; + UnimplementedDeviceState scuio; + + ARMv7MState armv7m; +}; + +#define TYPE_ASPEED27X0TSP_SOC "aspeed27x0tsp-soc" +OBJECT_DECLARE_SIMPLE_TYPE(Aspeed27x0TSPSoCState, ASPEED27X0TSP_SOC) + #define TYPE_ASPEED10X0_SOC "aspeed10x0-soc" OBJECT_DECLARE_SIMPLE_TYPE(Aspeed10x0SoCState, ASPEED10X0_SOC) From a74faf35efc5befb95d57041c9321a90a0edbb6d Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 2 May 2025 18:34:43 +0800 Subject: [PATCH 0629/2760] hw/arm: Introduce ASPEED AST2700 A1 full core machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Added new machine type `ast2700fc` with full core support. - Defined `Ast2700FCState` structure for the new machine type. - Implemented initialization functions for CA35, SSP, and TSP components. - Updated `ast2700fc_types` to include the new machine type. - Set machine class properties for `ast2700fc`. Test Step: - Download ast2700-default-obmc.tar.gz from AspeedTech-BMC OpenBmc release page. - Run the following QEMU command: ``` IMGDIR=~/path/to/image UBOOT_SIZE=$(stat --format=%s -L ${IMGDIR}/u-boot-nodtb.bin) ./qemu-system-aarch64 -machine ast2700fc \ -device loader,force-raw=on,addr=0x400000000,file=${IMGDIR}/u-boot-nodtb.bin \ -device loader,force-raw=on,addr=$((0x400000000 + ${UBOOT_SIZE})),file=${IMGDIR}/u-boot.dtb \ -device loader,force-raw=on,addr=0x430000000,file=${IMGDIR}/bl31.bin \ -device loader,force-raw=on,addr=0x430080000,file=${IMGDIR}/tee-raw.bin \ -device loader,cpu-num=0,addr=0x430000000 \ -device loader,cpu-num=1,addr=0x430000000 \ -device loader,cpu-num=2,addr=0x430000000 \ -device loader,cpu-num=3,addr=0x430000000 \ -device loader,file=${IMGDIR}/ast2700-ssp.elf,cpu-num=4 \ -device loader,file=${IMGDIR}/ast2700-tsp.elf,cpu-num=5 \ -drive file=${IMGDIR}/image-bmc,if=mtd,format=raw \ -serial pty -serial pty -serial pty \ -snapshot \ -S -nographic ``` - After starting QEMU, serial devices will be redirected: char device redirected to /dev/pts/51 (label serial0) char device redirected to /dev/pts/52 (label serial1) char device redirected to /dev/pts/53 (label serial2) - serial0 is the console for the four Cortex-A35 primary processors, serial1 and serial2 are the consoles for the two Cortex-M4 coprocessors. - Connect to the consoles using a terminal emulator. Signed-off-by: Steven Lee Change-Id: I32447b9372a78eb53a07135afef59c2a19202328 Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250502103449.3091642-8-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 192 +++++++++++++++++++++++++++++++++++++ hw/arm/meson.build | 4 +- 2 files changed, 195 insertions(+), 1 deletion(-) create mode 100644 hw/arm/aspeed_ast27x0-fc.c diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c new file mode 100644 index 0000000000..125a3ade40 --- /dev/null +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -0,0 +1,192 @@ +/* + * ASPEED SoC 2700 family + * + * Copyright (C) 2025 ASPEED Technology Inc. + * + * This code is licensed under the GPL version 2 or later. See + * the COPYING file in the top-level directory. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/units.h" +#include "qapi/error.h" +#include "system/block-backend.h" +#include "system/system.h" +#include "hw/arm/aspeed.h" +#include "hw/boards.h" +#include "hw/qdev-clock.h" +#include "hw/arm/aspeed_soc.h" +#include "hw/loader.h" +#include "hw/arm/boot.h" +#include "hw/block/flash.h" + + +#define TYPE_AST2700A1FC MACHINE_TYPE_NAME("ast2700fc") +OBJECT_DECLARE_SIMPLE_TYPE(Ast2700FCState, AST2700A1FC); + +static struct arm_boot_info ast2700fc_board_info = { + .board_id = -1, /* device-tree-only board */ +}; + +struct Ast2700FCState { + MachineState parent_obj; + + MemoryRegion ca35_memory; + MemoryRegion ca35_dram; + MemoryRegion ssp_memory; + MemoryRegion tsp_memory; + + Clock *ssp_sysclk; + Clock *tsp_sysclk; + + Aspeed27x0SoCState ca35; + Aspeed27x0SSPSoCState ssp; + Aspeed27x0TSPSoCState tsp; + + bool mmio_exec; +}; + +#define AST2700FC_BMC_RAM_SIZE (2 * GiB) +#define AST2700FC_CM4_DRAM_SIZE (32 * MiB) + +#define AST2700FC_HW_STRAP1 0x000000C0 +#define AST2700FC_HW_STRAP2 0x00000003 +#define AST2700FC_FMC_MODEL "w25q01jvq" +#define AST2700FC_SPI_MODEL "w25q512jv" + +static void ast2700fc_ca35_init(MachineState *machine) +{ + Ast2700FCState *s = AST2700A1FC(machine); + AspeedSoCState *soc; + AspeedSoCClass *sc; + + object_initialize_child(OBJECT(s), "ca35", &s->ca35, "ast2700-a1"); + soc = ASPEED_SOC(&s->ca35); + sc = ASPEED_SOC_GET_CLASS(soc); + + memory_region_init(&s->ca35_memory, OBJECT(&s->ca35), "ca35-memory", + UINT64_MAX); + + if (!memory_region_init_ram(&s->ca35_dram, OBJECT(&s->ca35), "ca35-dram", + AST2700FC_BMC_RAM_SIZE, &error_abort)) { + return; + } + if (!object_property_set_link(OBJECT(&s->ca35), "memory", + OBJECT(&s->ca35_memory), + &error_abort)) { + return; + }; + if (!object_property_set_link(OBJECT(&s->ca35), "dram", + OBJECT(&s->ca35_dram), &error_abort)) { + return; + } + if (!object_property_set_int(OBJECT(&s->ca35), "ram-size", + AST2700FC_BMC_RAM_SIZE, &error_abort)) { + return; + } + if (!object_property_set_int(OBJECT(&s->ca35), "hw-strap1", + AST2700FC_HW_STRAP1, &error_abort)) { + return; + } + if (!object_property_set_int(OBJECT(&s->ca35), "hw-strap2", + AST2700FC_HW_STRAP2, &error_abort)) { + return; + } + aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART12, serial_hd(0)); + if (!qdev_realize(DEVICE(&s->ca35), NULL, &error_abort)) { + return; + } + + /* + * AST2700 EVB has a LM75 temperature sensor on I2C bus 0 at address 0x4d. + */ + i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, 0), "tmp105", 0x4d); + + aspeed_board_init_flashes(&soc->fmc, AST2700FC_FMC_MODEL, 2, 0); + aspeed_board_init_flashes(&soc->spi[0], AST2700FC_SPI_MODEL, 1, 2); + + ast2700fc_board_info.ram_size = machine->ram_size; + ast2700fc_board_info.loader_start = sc->memmap[ASPEED_DEV_SDRAM]; + + arm_load_kernel(ARM_CPU(first_cpu), machine, &ast2700fc_board_info); +} + +static void ast2700fc_ssp_init(MachineState *machine) +{ + AspeedSoCState *soc; + Ast2700FCState *s = AST2700A1FC(machine); + s->ssp_sysclk = clock_new(OBJECT(s), "SSP_SYSCLK"); + clock_set_hz(s->ssp_sysclk, 200000000ULL); + + object_initialize_child(OBJECT(s), "ssp", &s->ssp, TYPE_ASPEED27X0SSP_SOC); + memory_region_init(&s->ssp_memory, OBJECT(&s->ssp), "ssp-memory", + UINT64_MAX); + + qdev_connect_clock_in(DEVICE(&s->ssp), "sysclk", s->ssp_sysclk); + if (!object_property_set_link(OBJECT(&s->ssp), "memory", + OBJECT(&s->ssp_memory), &error_abort)) { + return; + } + + soc = ASPEED_SOC(&s->ssp); + aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART4, serial_hd(1)); + if (!qdev_realize(DEVICE(&s->ssp), NULL, &error_abort)) { + return; + } +} + +static void ast2700fc_tsp_init(MachineState *machine) +{ + AspeedSoCState *soc; + Ast2700FCState *s = AST2700A1FC(machine); + s->tsp_sysclk = clock_new(OBJECT(s), "TSP_SYSCLK"); + clock_set_hz(s->tsp_sysclk, 200000000ULL); + + object_initialize_child(OBJECT(s), "tsp", &s->tsp, TYPE_ASPEED27X0TSP_SOC); + memory_region_init(&s->tsp_memory, OBJECT(&s->tsp), "tsp-memory", + UINT64_MAX); + + qdev_connect_clock_in(DEVICE(&s->tsp), "sysclk", s->tsp_sysclk); + if (!object_property_set_link(OBJECT(&s->tsp), "memory", + OBJECT(&s->tsp_memory), &error_abort)) { + return; + } + + soc = ASPEED_SOC(&s->tsp); + aspeed_soc_uart_set_chr(soc, ASPEED_DEV_UART7, serial_hd(2)); + if (!qdev_realize(DEVICE(&s->tsp), NULL, &error_abort)) { + return; + } +} + +static void ast2700fc_init(MachineState *machine) +{ + ast2700fc_ca35_init(machine); + ast2700fc_ssp_init(machine); + ast2700fc_tsp_init(machine); +} + +static void ast2700fc_class_init(ObjectClass *oc, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + + mc->alias = "ast2700fc"; + mc->desc = "ast2700 full core support"; + mc->init = ast2700fc_init; + mc->no_floppy = 1; + mc->no_cdrom = 1; + mc->min_cpus = mc->max_cpus = mc->default_cpus = 6; +} + +static const TypeInfo ast2700fc_types[] = { + { + .name = MACHINE_TYPE_NAME("ast2700fc"), + .parent = TYPE_MACHINE, + .class_init = ast2700fc_class_init, + .instance_size = sizeof(Ast2700FCState), + }, +}; + +DEFINE_TYPES(ast2700fc_types) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 98c5631506..5098795f61 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -49,7 +49,9 @@ arm_ss.add(when: 'CONFIG_ASPEED_SOC', if_true: files( 'aspeed_ast10x0.c', 'aspeed_eeprom.c', 'fby35.c')) -arm_common_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: files('aspeed_ast27x0.c')) +arm_common_ss.add(when: ['CONFIG_ASPEED_SOC', 'TARGET_AARCH64'], if_true: files( + 'aspeed_ast27x0.c', + 'aspeed_ast27x0-fc.c',)) arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2.c')) arm_common_ss.add(when: 'CONFIG_MPS2', if_true: files('mps2-tz.c')) arm_common_ss.add(when: 'CONFIG_MSF2', if_true: files('msf2-soc.c')) From 81c74475433c69f6a4d67fcba463622c63667807 Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Mon, 5 May 2025 11:06:18 +0800 Subject: [PATCH 0630/2760] tests/function/aspeed: Add functional test for ast2700fc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a new test suite for ast2700fc machine. Rename the original test_aarch64_aspeed.py to test_aarch64_aspeed_ast2700.py. Signed-off-by: Steven Lee Change-Id: I3855f55c9f6e5cca1270c179445f549f8d81f36c Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250505030618.3612042-1-steven_lee@aspeedtech.com [ clg: Added new tests in meson.build ] Signed-off-by: Cédric Le Goater --- tests/functional/meson.build | 6 +- ...peed.py => test_aarch64_aspeed_ast2700.py} | 0 .../test_aarch64_aspeed_ast2700fc.py | 135 ++++++++++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) rename tests/functional/{test_aarch64_aspeed.py => test_aarch64_aspeed_ast2700.py} (100%) create mode 100755 tests/functional/test_aarch64_aspeed_ast2700fc.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index b317ad42c5..ab9df03b1f 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -11,7 +11,8 @@ endif # Timeouts for individual tests that can be slow e.g. with debugging enabled test_timeouts = { - 'aarch64_aspeed' : 600, + 'aarch64_aspeed_ast2700' : 600, + 'aarch64_aspeed_ast2700fc' : 600, 'aarch64_raspi4' : 480, 'aarch64_reverse_debug' : 180, 'aarch64_rme_virt' : 1200, @@ -79,7 +80,8 @@ tests_aarch64_system_quick = [ ] tests_aarch64_system_thorough = [ - 'aarch64_aspeed', + 'aarch64_aspeed_ast2700', + 'aarch64_aspeed_ast2700fc', 'aarch64_raspi3', 'aarch64_raspi4', 'aarch64_replay', diff --git a/tests/functional/test_aarch64_aspeed.py b/tests/functional/test_aarch64_aspeed_ast2700.py similarity index 100% rename from tests/functional/test_aarch64_aspeed.py rename to tests/functional/test_aarch64_aspeed_ast2700.py diff --git a/tests/functional/test_aarch64_aspeed_ast2700fc.py b/tests/functional/test_aarch64_aspeed_ast2700fc.py new file mode 100755 index 0000000000..b85370e182 --- /dev/null +++ b/tests/functional/test_aarch64_aspeed_ast2700fc.py @@ -0,0 +1,135 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED SoCs with firmware +# +# Copyright (C) 2022 ASPEED Technology Inc +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import QemuSystemTest, Asset +from qemu_test import wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern + + +class AST2x00MachineSDK(QemuSystemTest): + + def do_test_aarch64_aspeed_sdk_start(self, image): + self.require_netdev('user') + self.vm.set_console() + self.vm.add_args('-device', + 'tmp105,bus=aspeed.i2c.bus.1,address=0x4d,id=tmp-test') + self.vm.add_args('-drive', 'file=' + image + ',if=mtd,format=raw', + '-net', 'nic', '-net', 'user', '-snapshot') + + self.vm.launch() + + def verify_openbmc_boot_and_login(self, name): + wait_for_console_pattern(self, 'U-Boot 2023.10') + wait_for_console_pattern(self, '## Loading kernel from FIT Image') + wait_for_console_pattern(self, 'Starting kernel ...') + + wait_for_console_pattern(self, f'{name} login:') + exec_command_and_wait_for_pattern(self, 'root', 'Password:') + exec_command_and_wait_for_pattern(self, '0penBmc', f'root@{name}:~#') + + ASSET_SDK_V906_AST2700 = Asset( + 'https://github.com/AspeedTech-BMC/openbmc/releases/download/v09.06/ast2700-default-obmc.tar.gz', + 'f1d53e0be8a404ecce3e105f72bc50fa4e090ad13160ffa91b10a6e0233a9dc6') + + def do_ast2700_i2c_test(self): + exec_command_and_wait_for_pattern(self, + 'echo lm75 0x4d > /sys/class/i2c-dev/i2c-1/device/new_device ', + 'i2c i2c-1: new_device: Instantiated device lm75 at 0x4d') + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '0') + self.vm.cmd('qom-set', path='/machine/peripheral/tmp-test', + property='temperature', value=18000) + exec_command_and_wait_for_pattern(self, + 'cat /sys/bus/i2c/devices/1-004d/hwmon/hwmon*/temp1_input', '18000') + + def do_ast2700fc_ssp_test(self): + self.vm.shutdown() + self.vm.set_console(console_index=1) + self.vm.launch() + + exec_command_and_wait_for_pattern(self, '\012', 'ssp:~$') + exec_command_and_wait_for_pattern(self, 'version', + 'Zephyr version 3.7.1') + exec_command_and_wait_for_pattern(self, 'md 72c02000 1', + '[72c02000] 06010103') + + def do_ast2700fc_tsp_test(self): + self.vm.shutdown() + self.vm.set_console(console_index=2) + self.vm.launch() + + exec_command_and_wait_for_pattern(self, '\012', 'tsp:~$') + exec_command_and_wait_for_pattern(self, 'version', + 'Zephyr version 3.7.1') + exec_command_and_wait_for_pattern(self, 'md 72c02000 1', + '[72c02000] 06010103') + + def start_ast2700fc_test(self, name): + ca35_core = 4 + uboot_size = os.path.getsize(self.scratch_file(name, + 'u-boot-nodtb.bin')) + uboot_dtb_load_addr = hex(0x400000000 + uboot_size) + + load_images_list = [ + { + 'addr': '0x400000000', + 'file': self.scratch_file(name, + 'u-boot-nodtb.bin') + }, + { + 'addr': str(uboot_dtb_load_addr), + 'file': self.scratch_file(name, 'u-boot.dtb') + }, + { + 'addr': '0x430000000', + 'file': self.scratch_file(name, 'bl31.bin') + }, + { + 'addr': '0x430080000', + 'file': self.scratch_file(name, 'optee', + 'tee-raw.bin') + } + ] + + for load_image in load_images_list: + addr = load_image['addr'] + file = load_image['file'] + self.vm.add_args('-device', + f'loader,force-raw=on,addr={addr},file={file}') + + for i in range(ca35_core): + self.vm.add_args('-device', + f'loader,addr=0x430000000,cpu-num={i}') + + load_elf_list = { + 'ssp': self.scratch_file(name, 'zephyr-aspeed-ssp.elf'), + 'tsp': self.scratch_file(name, 'zephyr-aspeed-tsp.elf') + } + + for cpu_num, key in enumerate(load_elf_list, start=4): + file = load_elf_list[key] + self.vm.add_args('-device', + f'loader,file={file},cpu-num={cpu_num}') + + self.do_test_aarch64_aspeed_sdk_start( + self.scratch_file(name, 'image-bmc')) + + def test_aarch64_ast2700fc_sdk_v09_06(self): + self.set_machine('ast2700fc') + + self.archive_extract(self.ASSET_SDK_V906_AST2700) + self.start_ast2700fc_test('ast2700-default') + self.verify_openbmc_boot_and_login('ast2700-default') + self.do_ast2700_i2c_test() + self.do_ast2700fc_ssp_test() + self.do_ast2700fc_tsp_test() + +if __name__ == '__main__': + QemuSystemTest.main() From f32ef57f7de98ec36da52b0a13bd5d18ac4bd583 Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 2 May 2025 18:34:45 +0800 Subject: [PATCH 0631/2760] docs: Add support for ast2700fc machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated Aspeed family boards list to include `ast2700fc`. - Added boot instructions for the `ast2700fc` machine. - Detailed the configuration and loading of firmware for the Cortex-A35 and Cortex-M4 processors. Signed-off-by: Steven Lee Change-Id: Id41312e9c7cf79bc55c6f24a87a7ad9993dc7261 Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250502103449.3091642-10-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- docs/system/arm/aspeed.rst | 69 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index 014545f444..58a8020eec 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -1,5 +1,5 @@ -Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``bletchley-bmc``, ``fuji-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) -================================================================================================================================================================================================================================================================================================================================================================================================================== +Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``, ``ast2700fc``, ``bletchley-bmc``, ``fuji-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) +================================================================================================================================================================================================================================================================================================================================================================================================================================= The QEMU Aspeed machines model BMCs of various OpenPOWER systems and Aspeed evaluation boards. They are based on different releases of the @@ -255,6 +255,7 @@ etc. AST2700 SoC based machines : - ``ast2700-evb`` Aspeed AST2700 Evaluation board (Cortex-A35) +- ``ast2700fc`` Aspeed AST2700 Evaluation board (Cortex-A35 + Cortex-M4) Supported devices ----------------- @@ -285,7 +286,6 @@ Supported devices Missing devices --------------- - * Coprocessor support * PWM and Fan Controller * Slave GPIO Controller * Super I/O Controller @@ -353,6 +353,69 @@ specified path in the ${HOME} directory. -bios ${HOME}/ast27x0_bootrom.bin +Booting the ast2700fc machine +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +AST2700 features four Cortex-A35 primary processors and two Cortex-M4 coprocessors. +**ast2700-evb** machine focuses on emulating the four Cortex-A35 primary processors, +**ast2700fc** machine extends **ast2700-evb** by adding support for the two Cortex-M4 coprocessors. + +Steps to boot the AST2700fc machine: + +1. Ensure you have the following AST2700A1 binaries available in a directory + + * u-boot-nodtb.bin + * u-boot.dtb + * bl31.bin + * optee/tee-raw.bin + * image-bmc + * zephyr-aspeed-ssp.elf (for SSP firmware, CPU 5) + * zephyr-aspeed-tsp.elf (for TSP firmware, CPU 6) + +2. Execute the following command to start ``ast2700fc`` machine: + +.. code-block:: bash + + IMGDIR=ast2700-default + UBOOT_SIZE=$(stat --format=%s -L ${IMGDIR}/u-boot-nodtb.bin) + + $ qemu-system-aarch64 -M ast2700fc \ + -device loader,force-raw=on,addr=0x400000000,file=${IMGDIR}/u-boot-nodtb.bin \ + -device loader,force-raw=on,addr=$((0x400000000 + ${UBOOT_SIZE})),file=${IMGDIR}/u-boot.dtb \ + -device loader,force-raw=on,addr=0x430000000,file=${IMGDIR}/bl31.bin \ + -device loader,force-raw=on,addr=0x430080000,file=${IMGDIR}/optee/tee-raw.bin \ + -device loader,cpu-num=0,addr=0x430000000 \ + -device loader,cpu-num=1,addr=0x430000000 \ + -device loader,cpu-num=2,addr=0x430000000 \ + -device loader,cpu-num=3,addr=0x430000000 \ + -drive file=${IMGDIR}/image-bmc,if=mtd,format=raw \ + -device loader,file=${IMGDIR}/zephyr-aspeed-ssp.elf,cpu-num=4 \ + -device loader,file=${IMGDIR}/zephyr-aspeed-tsp.elf,cpu-num=5 \ + -serial pty -serial pty -serial pty \ + -snapshot \ + -S -nographic + +After launching QEMU, serial devices will be automatically redirected. +Example output: + +.. code-block:: bash + + char device redirected to /dev/pts/55 (label serial0) + char device redirected to /dev/pts/56 (label serial1) + char device redirected to /dev/pts/57 (label serial2) + +- serial0: Console for the four Cortex-A35 primary processors. +- serial1 and serial2: Consoles for the two Cortex-M4 coprocessors. + +Use ``tio`` or another terminal emulator to connect to the consoles: + +.. code-block:: bash + + $ tio /dev/pts/55 + $ tio /dev/pts/56 + $ tio /dev/pts/57 + + Aspeed minibmc family boards (``ast1030-evb``) ================================================================== From 61da38db70affd925226ce1e8a61d761c20d045b Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Fri, 7 Mar 2025 10:22:56 +0100 Subject: [PATCH 0632/2760] 9pfs: fix concurrent v9fs_reclaim_fd() calls Even though this function is serialized to be always called from main thread, v9fs_reclaim_fd() is dispatching the coroutine to a worker thread in between via its v9fs_co_*() calls, hence leading to the situation where v9fs_reclaim_fd() is effectively executed multiple times simultaniously, which renders its LRU algorithm useless and causes high latency. Fix this by adding a simple boolean variable to ensure this function is only called once at a time. No synchronization needed for this boolean variable as this function is only entered and returned on main thread. Fixes: 7a46274529c ('hw/9pfs: Add file descriptor reclaim support') Signed-off-by: Christian Schoenebeck Reviewed-by: Greg Kurz Message-Id: <5c622067efd66dd4ee5eca740dcf263f41db20b2.1741339452.git.qemu_oss@crudebyte.com> --- hw/9pfs/9p.c | 10 ++++++++++ hw/9pfs/9p.h | 1 + 2 files changed, 11 insertions(+) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 7cad2bce62..4f9c2dde9c 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -435,6 +435,12 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu) GHashTableIter iter; gpointer fid; + /* prevent multiple coroutines running this function simultaniously */ + if (s->reclaiming) { + return; + } + s->reclaiming = true; + g_hash_table_iter_init(&iter, s->fids); QSLIST_HEAD(, V9fsFidState) reclaim_list = @@ -510,6 +516,8 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu) */ put_fid(pdu, f); } + + s->reclaiming = false; } /* @@ -4324,6 +4332,8 @@ int v9fs_device_realize_common(V9fsState *s, const V9fsTransport *t, s->ctx.fst = &fse->fst; fsdev_throttle_init(s->ctx.fst); + s->reclaiming = false; + rc = 0; out: if (rc) { diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 5e041e1f60..259ad32ed1 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -362,6 +362,7 @@ struct V9fsState { uint64_t qp_ndevices; /* Amount of entries in qpd_table. */ uint16_t qp_affix_next; uint64_t qp_fullpath_next; + bool reclaiming; }; /* 9p2000.L open flags */ From 89f7b4da7662ecc6840ffb0846045f03f9714bc6 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Fri, 7 Mar 2025 10:23:02 +0100 Subject: [PATCH 0633/2760] 9pfs: fix FD leak and reduce latency of v9fs_reclaim_fd() This patch fixes two different bugs in v9fs_reclaim_fd(): 1. Reduce latency: This function calls v9fs_co_close() and v9fs_co_closedir() in a loop. Each one of the calls adds two thread hops (between main thread and a fs driver background thread). Each thread hop adds latency, which sums up in function's loop to a significant duration. Reduce overall latency by open coding what v9fs_co_close() and v9fs_co_closedir() do, executing those and the loop itself altogether in only one background thread block, hence reducing the total amount of thread hops to only two. 2. Fix file descriptor leak: The existing code called v9fs_co_close() and v9fs_co_closedir() to close file descriptors. Both functions check right at the beginning if the 9p request was cancelled: if (v9fs_request_cancelled(pdu)) { return -EINTR; } So if client sent a 'Tflush' message, v9fs_co_close() / v9fs_co_closedir() returned without having closed the file descriptor and v9fs_reclaim_fd() subsequently freed the FID without its file descriptor being closed, hence leaking those file descriptors. This 2nd bug is fixed by this patch as well by open coding v9fs_co_close() and v9fs_co_closedir() inside of v9fs_reclaim_fd() and not performing the v9fs_request_cancelled(pdu) check there. Fixes: 7a46274529c ('hw/9pfs: Add file descriptor reclaim support') Fixes: bccacf6c792 ('hw/9pfs: Implement TFLUSH operation') Signed-off-by: Christian Schoenebeck Reviewed-by: Greg Kurz Message-Id: <5747469d3f039c53147e850b456943a1d4b5485c.1741339452.git.qemu_oss@crudebyte.com> --- hw/9pfs/9p.c | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 4f9c2dde9c..80b190ff5b 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -434,6 +434,8 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu) V9fsFidState *f; GHashTableIter iter; gpointer fid; + int err; + int nclosed = 0; /* prevent multiple coroutines running this function simultaniously */ if (s->reclaiming) { @@ -446,10 +448,10 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu) QSLIST_HEAD(, V9fsFidState) reclaim_list = QSLIST_HEAD_INITIALIZER(reclaim_list); + /* Pick FIDs to be closed, collect them on reclaim_list. */ while (g_hash_table_iter_next(&iter, &fid, (gpointer *) &f)) { /* - * Unlink fids cannot be reclaimed. Check - * for them and skip them. Also skip fids + * Unlinked fids cannot be reclaimed, skip those, and also skip fids * currently being operated on. */ if (f->ref || f->flags & FID_NON_RECLAIMABLE) { @@ -499,17 +501,26 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu) } } /* - * Now close the fid in reclaim list. Free them if they - * are already clunked. + * Close the picked FIDs altogether on a background I/O driver thread. Do + * this all at once to keep latency (i.e. amount of thread hops between main + * thread <-> fs driver background thread) as low as possible. */ + v9fs_co_run_in_worker({ + QSLIST_FOREACH(f, &reclaim_list, reclaim_next) { + err = (f->fid_type == P9_FID_DIR) ? + s->ops->closedir(&s->ctx, &f->fs_reclaim) : + s->ops->close(&s->ctx, &f->fs_reclaim); + if (!err) { + /* total_open_fd must only be mutated on main thread */ + nclosed++; + } + } + }); + total_open_fd -= nclosed; + /* Free the closed FIDs. */ while (!QSLIST_EMPTY(&reclaim_list)) { f = QSLIST_FIRST(&reclaim_list); QSLIST_REMOVE(&reclaim_list, f, V9fsFidState, reclaim_next); - if (f->fid_type == P9_FID_FILE) { - v9fs_co_close(pdu, &f->fs_reclaim); - } else if (f->fid_type == P9_FID_DIR) { - v9fs_co_closedir(pdu, &f->fs_reclaim); - } /* * Now drop the fid reference, free it * if clunked. From 4f82ce8cd94f2601fb2b2e4cfe0cf5b44131817e Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Wed, 12 Mar 2025 16:29:27 +0100 Subject: [PATCH 0634/2760] 9pfs: local : Introduce local_fid_fd() helper Factor out duplicated code to a single helper. More users to come. Signed-off-by: Greg Kurz Reviewed-by: Christian Schoenebeck Message-Id: <20250312152933.383967-2-groug@kaod.org> Signed-off-by: Christian Schoenebeck --- hw/9pfs/9p-local.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 928523afcc..99b9560a52 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -766,16 +766,19 @@ static int local_mkdir(FsContext *fs_ctx, V9fsPath *dir_path, return err; } -static int local_fstat(FsContext *fs_ctx, int fid_type, - V9fsFidOpenState *fs, struct stat *stbuf) +static int local_fid_fd(int fid_type, V9fsFidOpenState *fs) { - int err, fd; - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir.stream); + return dirfd(fs->dir.stream); } else { - fd = fs->fd; + return fs->fd; } +} + +static int local_fstat(FsContext *fs_ctx, int fid_type, + V9fsFidOpenState *fs, struct stat *stbuf) +{ + int err, fd = local_fid_fd(fid_type, fs); err = fstat(fd, stbuf); if (err) { @@ -1167,13 +1170,7 @@ static int local_remove(FsContext *ctx, const char *path) static int local_fsync(FsContext *ctx, int fid_type, V9fsFidOpenState *fs, int datasync) { - int fd; - - if (fid_type == P9_FID_DIR) { - fd = dirfd(fs->dir.stream); - } else { - fd = fs->fd; - } + int fd = local_fid_fd(fid_type, fs); if (datasync) { return qemu_fdatasync(fd); From f2bb367d2b265c6c0ead1e0d4a8f7c43310b3107 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Wed, 12 Mar 2025 16:29:28 +0100 Subject: [PATCH 0635/2760] 9pfs: Don't use file descriptors in core code v9fs_getattr() currently peeks into V9fsFidOpenState to know if a fid has a valid file descriptor or directory stream. Even though the fields are accessible, this is an implementation detail of the local backend that should not be manipulated directly by the server code. Abstract that with a new has_valid_file_handle() backend operation. Signed-off-by: Greg Kurz Reviewed-by: Christian Schoenebeck Message-Id: <20250312152933.383967-3-groug@kaod.org> Signed-off-by: Christian Schoenebeck --- fsdev/file-op-9p.h | 1 + hw/9pfs/9p-local.c | 8 ++++++++ hw/9pfs/9p-synth.c | 6 ++++++ hw/9pfs/9p.c | 9 ++++++--- 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index 4997677460..b815cea44e 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -164,6 +164,7 @@ struct FileOperations { int (*renameat)(FsContext *ctx, V9fsPath *olddir, const char *old_name, V9fsPath *newdir, const char *new_name); int (*unlinkat)(FsContext *ctx, V9fsPath *dir, const char *name, int flags); + bool (*has_valid_file_handle)(int fid_type, V9fsFidOpenState *fs); }; #endif diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 99b9560a52..b16132299f 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -1572,6 +1572,13 @@ static int local_parse_opts(QemuOpts *opts, FsDriverEntry *fse, Error **errp) return 0; } +static bool local_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs) +{ + return + (fid_type == P9_FID_FILE && fs->fd != -1) || + (fid_type == P9_FID_DIR && fs->dir.stream != NULL); +} + FileOperations local_ops = { .parse_opts = local_parse_opts, .init = local_init, @@ -1609,4 +1616,5 @@ FileOperations local_ops = { .name_to_path = local_name_to_path, .renameat = local_renameat, .unlinkat = local_unlinkat, + .has_valid_file_handle = local_has_valid_file_handle, }; diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index 2abaf3a291..be0492b400 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -615,6 +615,11 @@ static int synth_init(FsContext *ctx, Error **errp) return 0; } +static bool synth_has_valid_file_handle(int fid_type, V9fsFidOpenState *fs) +{ + return false; +} + FileOperations synth_ops = { .init = synth_init, .lstat = synth_lstat, @@ -650,4 +655,5 @@ FileOperations synth_ops = { .name_to_path = synth_name_to_path, .renameat = synth_renameat, .unlinkat = synth_unlinkat, + .has_valid_file_handle = synth_has_valid_file_handle, }; diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 80b190ff5b..4586822d24 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1593,6 +1593,11 @@ static void coroutine_fn v9fs_stat(void *opaque) pdu_complete(pdu, err); } +static bool fid_has_valid_file_handle(V9fsState *s, V9fsFidState *fidp) +{ + return s->ops->has_valid_file_handle(fidp->fid_type, &fidp->fs); +} + static void coroutine_fn v9fs_getattr(void *opaque) { int32_t fid; @@ -1615,9 +1620,7 @@ static void coroutine_fn v9fs_getattr(void *opaque) retval = -ENOENT; goto out_nofid; } - if ((fidp->fid_type == P9_FID_FILE && fidp->fs.fd != -1) || - (fidp->fid_type == P9_FID_DIR && fidp->fs.dir.stream)) - { + if (fid_has_valid_file_handle(pdu->s, fidp)) { retval = v9fs_co_fstat(pdu, fidp, &stbuf); } else { retval = v9fs_co_lstat(pdu, &fidp->path, &stbuf); From 0c798dd52355f3489b29bba0dfd7df0e24cfa1dd Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Wed, 12 Mar 2025 16:29:29 +0100 Subject: [PATCH 0636/2760] 9pfs: Introduce ftruncate file op Add an ftruncate operation to the fs driver and use if when a fid has a valid file descriptor. This is required to support more cases where the client wants to do an action on an unlinked file which it still has an open file decriptor for. Only 9P2000.L was considered. Signed-off-by: Greg Kurz Reviewed-by: Christian Schoenebeck Message-Id: <20250312152933.383967-4-groug@kaod.org> Signed-off-by: Christian Schoenebeck --- fsdev/file-op-9p.h | 2 ++ hw/9pfs/9p-local.c | 9 +++++++++ hw/9pfs/9p-synth.c | 8 ++++++++ hw/9pfs/9p.c | 6 +++++- hw/9pfs/cofs.c | 18 ++++++++++++++++++ hw/9pfs/coth.h | 2 ++ 6 files changed, 44 insertions(+), 1 deletion(-) diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index b815cea44e..26ba1438c0 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -152,6 +152,8 @@ struct FileOperations { int (*fstat)(FsContext *, int, V9fsFidOpenState *, struct stat *); int (*rename)(FsContext *, const char *, const char *); int (*truncate)(FsContext *, V9fsPath *, off_t); + int (*ftruncate)(FsContext *ctx, int fid_type, V9fsFidOpenState *fs, + off_t size); int (*fsync)(FsContext *, int, V9fsFidOpenState *, int); int (*statfs)(FsContext *s, V9fsPath *path, struct statfs *stbuf); ssize_t (*lgetxattr)(FsContext *, V9fsPath *, diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index b16132299f..0b33da8d2a 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -1042,6 +1042,14 @@ static int local_truncate(FsContext *ctx, V9fsPath *fs_path, off_t size) return ret; } +static int local_ftruncate(FsContext *ctx, int fid_type, V9fsFidOpenState *fs, + off_t size) +{ + int fd = local_fid_fd(fid_type, fs); + + return ftruncate(fd, size); +} + static int local_chown(FsContext *fs_ctx, V9fsPath *fs_path, FsCred *credp) { char *dirpath = g_path_get_dirname(fs_path->data); @@ -1617,4 +1625,5 @@ FileOperations local_ops = { .renameat = local_renameat, .unlinkat = local_unlinkat, .has_valid_file_handle = local_has_valid_file_handle, + .ftruncate = local_ftruncate, }; diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index be0492b400..3d28afc4d0 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -356,6 +356,13 @@ static int synth_truncate(FsContext *ctx, V9fsPath *path, off_t offset) return -1; } +static int synth_ftruncate(FsContext *ctx, int fid_type, V9fsFidOpenState *fs, + off_t size) +{ + errno = ENOSYS; + return -1; +} + static int synth_chmod(FsContext *fs_ctx, V9fsPath *path, FsCred *credp) { errno = EPERM; @@ -656,4 +663,5 @@ FileOperations synth_ops = { .renameat = synth_renameat, .unlinkat = synth_unlinkat, .has_valid_file_handle = synth_has_valid_file_handle, + .ftruncate = synth_ftruncate, }; diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 4586822d24..c96f2d2d3d 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1752,7 +1752,11 @@ static void coroutine_fn v9fs_setattr(void *opaque) } } if (v9iattr.valid & (P9_ATTR_SIZE)) { - err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size); + if (fid_has_valid_file_handle(pdu->s, fidp)) { + err = v9fs_co_ftruncate(pdu, fidp, v9iattr.size); + } else { + err = v9fs_co_truncate(pdu, &fidp->path, v9iattr.size); + } if (err < 0) { goto out; } diff --git a/hw/9pfs/cofs.c b/hw/9pfs/cofs.c index 67e3ae5c5c..893466fb1a 100644 --- a/hw/9pfs/cofs.c +++ b/hw/9pfs/cofs.c @@ -184,6 +184,24 @@ int coroutine_fn v9fs_co_truncate(V9fsPDU *pdu, V9fsPath *path, off_t size) return err; } +int coroutine_fn v9fs_co_ftruncate(V9fsPDU *pdu, V9fsFidState *fidp, off_t size) +{ + int err; + V9fsState *s = pdu->s; + + if (v9fs_request_cancelled(pdu)) { + return -EINTR; + } + v9fs_co_run_in_worker( + { + err = s->ops->ftruncate(&s->ctx, fidp->fid_type, &fidp->fs, size); + if (err < 0) { + err = -errno; + } + }); + return err; +} + int coroutine_fn v9fs_co_mknod(V9fsPDU *pdu, V9fsFidState *fidp, V9fsString *name, uid_t uid, gid_t gid, dev_t dev, mode_t mode, struct stat *stbuf) diff --git a/hw/9pfs/coth.h b/hw/9pfs/coth.h index 2c54249b35..62e922dc12 100644 --- a/hw/9pfs/coth.h +++ b/hw/9pfs/coth.h @@ -73,6 +73,8 @@ int coroutine_fn v9fs_co_chmod(V9fsPDU *, V9fsPath *, mode_t); int coroutine_fn v9fs_co_utimensat(V9fsPDU *, V9fsPath *, struct timespec [2]); int coroutine_fn v9fs_co_chown(V9fsPDU *, V9fsPath *, uid_t, gid_t); int coroutine_fn v9fs_co_truncate(V9fsPDU *, V9fsPath *, off_t); +int coroutine_fn v9fs_co_ftruncate(V9fsPDU *pdu, V9fsFidState *fidp, + off_t size); int coroutine_fn v9fs_co_llistxattr(V9fsPDU *, V9fsPath *, void *, size_t); int coroutine_fn v9fs_co_lgetxattr(V9fsPDU *, V9fsPath *, V9fsString *, void *, size_t); From 371a269ff8ce561c28e4fa03bb49e4940f990637 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Wed, 12 Mar 2025 16:29:30 +0100 Subject: [PATCH 0637/2760] 9pfs: Introduce futimens file op Add an futimens operation to the fs driver and use if when a fid has a valid file descriptor. This is required to support more cases where the client wants to do an action on an unlinked file which it still has an open file decriptor for. Only 9P2000.L was considered. Signed-off-by: Greg Kurz Reviewed-by: Christian Schoenebeck Message-Id: <20250312152933.383967-5-groug@kaod.org> Signed-off-by: Christian Schoenebeck --- fsdev/file-op-9p.h | 2 ++ hw/9pfs/9p-local.c | 9 +++++++++ hw/9pfs/9p-synth.c | 8 ++++++++ hw/9pfs/9p-util.h | 1 + hw/9pfs/9p.c | 6 +++++- hw/9pfs/cofs.c | 19 +++++++++++++++++++ hw/9pfs/coth.h | 2 ++ 7 files changed, 46 insertions(+), 1 deletion(-) diff --git a/fsdev/file-op-9p.h b/fsdev/file-op-9p.h index 26ba1438c0..b9dae8c84c 100644 --- a/fsdev/file-op-9p.h +++ b/fsdev/file-op-9p.h @@ -129,6 +129,8 @@ struct FileOperations { int (*chown)(FsContext *, V9fsPath *, FsCred *); int (*mknod)(FsContext *, V9fsPath *, const char *, FsCred *); int (*utimensat)(FsContext *, V9fsPath *, const struct timespec *); + int (*futimens)(FsContext *ctx, int fid_type, V9fsFidOpenState *fs, + const struct timespec *times); int (*remove)(FsContext *, const char *); int (*symlink)(FsContext *, const char *, V9fsPath *, const char *, FsCred *); diff --git a/hw/9pfs/9p-local.c b/hw/9pfs/9p-local.c index 0b33da8d2a..31e216227c 100644 --- a/hw/9pfs/9p-local.c +++ b/hw/9pfs/9p-local.c @@ -1100,6 +1100,14 @@ static int local_utimensat(FsContext *s, V9fsPath *fs_path, return ret; } +static int local_futimens(FsContext *s, int fid_type, V9fsFidOpenState *fs, + const struct timespec *times) +{ + int fd = local_fid_fd(fid_type, fs); + + return qemu_futimens(fd, times); +} + static int local_unlinkat_common(FsContext *ctx, int dirfd, const char *name, int flags) { @@ -1626,4 +1634,5 @@ FileOperations local_ops = { .unlinkat = local_unlinkat, .has_valid_file_handle = local_has_valid_file_handle, .ftruncate = local_ftruncate, + .futimens = local_futimens, }; diff --git a/hw/9pfs/9p-synth.c b/hw/9pfs/9p-synth.c index 3d28afc4d0..9cd1884224 100644 --- a/hw/9pfs/9p-synth.c +++ b/hw/9pfs/9p-synth.c @@ -424,6 +424,13 @@ static int synth_utimensat(FsContext *fs_ctx, V9fsPath *path, return 0; } +static int synth_futimens(FsContext *fs_ctx, int fid_type, V9fsFidOpenState *fs, + const struct timespec *buf) +{ + errno = ENOSYS; + return -1; +} + static int synth_remove(FsContext *ctx, const char *path) { errno = EPERM; @@ -664,4 +671,5 @@ FileOperations synth_ops = { .unlinkat = synth_unlinkat, .has_valid_file_handle = synth_has_valid_file_handle, .ftruncate = synth_ftruncate, + .futimens = synth_futimens, }; diff --git a/hw/9pfs/9p-util.h b/hw/9pfs/9p-util.h index 7bc4ec8e85..a1924fe3f0 100644 --- a/hw/9pfs/9p-util.h +++ b/hw/9pfs/9p-util.h @@ -103,6 +103,7 @@ static inline int errno_to_dotl(int err) { #define qemu_renameat renameat #define qemu_utimensat utimensat #define qemu_unlinkat unlinkat +#define qemu_futimens futimens static inline void close_preserve_errno(int fd) { diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index c96f2d2d3d..b22df3aa2b 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -1727,7 +1727,11 @@ static void coroutine_fn v9fs_setattr(void *opaque) } else { times[1].tv_nsec = UTIME_OMIT; } - err = v9fs_co_utimensat(pdu, &fidp->path, times); + if (fid_has_valid_file_handle(pdu->s, fidp)) { + err = v9fs_co_futimens(pdu, fidp, times); + } else { + err = v9fs_co_utimensat(pdu, &fidp->path, times); + } if (err < 0) { goto out; } diff --git a/hw/9pfs/cofs.c b/hw/9pfs/cofs.c index 893466fb1a..12fa8c9fe9 100644 --- a/hw/9pfs/cofs.c +++ b/hw/9pfs/cofs.c @@ -139,6 +139,25 @@ int coroutine_fn v9fs_co_utimensat(V9fsPDU *pdu, V9fsPath *path, return err; } +int coroutine_fn v9fs_co_futimens(V9fsPDU *pdu, V9fsFidState *fidp, + struct timespec times[2]) +{ + int err; + V9fsState *s = pdu->s; + + if (v9fs_request_cancelled(pdu)) { + return -EINTR; + } + v9fs_co_run_in_worker( + { + err = s->ops->futimens(&s->ctx, fidp->fid_type, &fidp->fs, times); + if (err < 0) { + err = -errno; + } + }); + return err; +} + int coroutine_fn v9fs_co_chown(V9fsPDU *pdu, V9fsPath *path, uid_t uid, gid_t gid) { diff --git a/hw/9pfs/coth.h b/hw/9pfs/coth.h index 62e922dc12..7906fa7782 100644 --- a/hw/9pfs/coth.h +++ b/hw/9pfs/coth.h @@ -71,6 +71,8 @@ int coroutine_fn v9fs_co_statfs(V9fsPDU *, V9fsPath *, struct statfs *); int coroutine_fn v9fs_co_lstat(V9fsPDU *, V9fsPath *, struct stat *); int coroutine_fn v9fs_co_chmod(V9fsPDU *, V9fsPath *, mode_t); int coroutine_fn v9fs_co_utimensat(V9fsPDU *, V9fsPath *, struct timespec [2]); +int coroutine_fn v9fs_co_futimens(V9fsPDU *pdu, V9fsFidState *fidp, + struct timespec times[2]); int coroutine_fn v9fs_co_chown(V9fsPDU *, V9fsPath *, uid_t, gid_t); int coroutine_fn v9fs_co_truncate(V9fsPDU *, V9fsPath *, off_t); int coroutine_fn v9fs_co_ftruncate(V9fsPDU *pdu, V9fsFidState *fidp, From 4719a2d59176a6c850e2b4f1af44cecd25430fce Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Wed, 12 Mar 2025 16:29:31 +0100 Subject: [PATCH 0638/2760] tests/9p: add 'Tsetattr' request to test client Add and implement functions to 9pfs test client for sending a 9p2000.L 'Tsetattr' request and receiving its 'Rsetattr' response counterpart. Signed-off-by: Christian Schoenebeck Signed-off-by: Greg Kurz Message-Id: <20250312152933.383967-6-groug@kaod.org> --- tests/qtest/libqos/virtio-9p-client.c | 49 +++++++++++++++++++++++++++ tests/qtest/libqos/virtio-9p-client.h | 34 +++++++++++++++++++ tests/qtest/virtio-9p-test.c | 1 + 3 files changed, 84 insertions(+) diff --git a/tests/qtest/libqos/virtio-9p-client.c b/tests/qtest/libqos/virtio-9p-client.c index 98b77db51d..6ab4501c6e 100644 --- a/tests/qtest/libqos/virtio-9p-client.c +++ b/tests/qtest/libqos/virtio-9p-client.c @@ -557,6 +557,55 @@ void v9fs_rgetattr(P9Req *req, v9fs_attr *attr) v9fs_req_free(req); } +/* + * size[4] Tsetattr tag[2] fid[4] valid[4] mode[4] uid[4] gid[4] size[8] + * atime_sec[8] atime_nsec[8] mtime_sec[8] mtime_nsec[8] + */ +TSetAttrRes v9fs_tsetattr(TSetAttrOpt opt) +{ + P9Req *req; + uint32_t err; + + g_assert(opt.client); + + req = v9fs_req_init( + opt.client, 4/*fid*/ + 4/*valid*/ + 4/*mode*/ + 4/*uid*/ + 4/*gid*/ + + 8/*size*/ + 8/*atime_sec*/ + 8/*atime_nsec*/ + 8/*mtime_sec*/ + + 8/*mtime_nsec*/, P9_TSETATTR, opt.tag + ); + v9fs_uint32_write(req, opt.fid); + v9fs_uint32_write(req, (uint32_t) opt.attr.valid); + v9fs_uint32_write(req, opt.attr.mode); + v9fs_uint32_write(req, opt.attr.uid); + v9fs_uint32_write(req, opt.attr.gid); + v9fs_uint64_write(req, opt.attr.size); + v9fs_uint64_write(req, opt.attr.atime_sec); + v9fs_uint64_write(req, opt.attr.atime_nsec); + v9fs_uint64_write(req, opt.attr.mtime_sec); + v9fs_uint64_write(req, opt.attr.mtime_nsec); + v9fs_req_send(req); + + if (!opt.requestOnly) { + v9fs_req_wait_for_reply(req, NULL); + if (opt.expectErr) { + v9fs_rlerror(req, &err); + g_assert_cmpint(err, ==, opt.expectErr); + } else { + v9fs_rsetattr(req); + } + req = NULL; /* request was freed */ + } + + return (TSetAttrRes) { .req = req }; +} + +/* size[4] Rsetattr tag[2] */ +void v9fs_rsetattr(P9Req *req) +{ + v9fs_req_recv(req, P9_RSETATTR); + v9fs_req_free(req); +} + /* size[4] Treaddir tag[2] fid[4] offset[8] count[4] */ TReadDirRes v9fs_treaddir(TReadDirOpt opt) { diff --git a/tests/qtest/libqos/virtio-9p-client.h b/tests/qtest/libqos/virtio-9p-client.h index 78228eb97d..e3221a3104 100644 --- a/tests/qtest/libqos/virtio-9p-client.h +++ b/tests/qtest/libqos/virtio-9p-client.h @@ -65,6 +65,16 @@ typedef struct v9fs_attr { #define P9_GETATTR_BASIC 0x000007ffULL /* Mask for fields up to BLOCKS */ #define P9_GETATTR_ALL 0x00003fffULL /* Mask for ALL fields */ +#define P9_SETATTR_MODE 0x00000001UL +#define P9_SETATTR_UID 0x00000002UL +#define P9_SETATTR_GID 0x00000004UL +#define P9_SETATTR_SIZE 0x00000008UL +#define P9_SETATTR_ATIME 0x00000010UL +#define P9_SETATTR_MTIME 0x00000020UL +#define P9_SETATTR_CTIME 0x00000040UL +#define P9_SETATTR_ATIME_SET 0x00000080UL +#define P9_SETATTR_MTIME_SET 0x00000100UL + struct V9fsDirent { v9fs_qid qid; uint64_t offset; @@ -182,6 +192,28 @@ typedef struct TGetAttrRes { P9Req *req; } TGetAttrRes; +/* options for 'Tsetattr' 9p request */ +typedef struct TSetAttrOpt { + /* 9P client being used (mandatory) */ + QVirtio9P *client; + /* user supplied tag number being returned with response (optional) */ + uint16_t tag; + /* file ID of file/dir whose attributes shall be modified (required) */ + uint32_t fid; + /* new attribute values to be set by 9p server */ + v9fs_attr attr; + /* only send Tsetattr request but not wait for a reply? (optional) */ + bool requestOnly; + /* do we expect an Rlerror response, if yes which error code? (optional) */ + uint32_t expectErr; +} TSetAttrOpt; + +/* result of 'Tsetattr' 9p request */ +typedef struct TSetAttrRes { + /* if requestOnly was set: request object for further processing */ + P9Req *req; +} TSetAttrRes; + /* options for 'Treaddir' 9p request */ typedef struct TReadDirOpt { /* 9P client being used (mandatory) */ @@ -470,6 +502,8 @@ TWalkRes v9fs_twalk(TWalkOpt opt); void v9fs_rwalk(P9Req *req, uint16_t *nwqid, v9fs_qid **wqid); TGetAttrRes v9fs_tgetattr(TGetAttrOpt); void v9fs_rgetattr(P9Req *req, v9fs_attr *attr); +TSetAttrRes v9fs_tsetattr(TSetAttrOpt opt); +void v9fs_rsetattr(P9Req *req); TReadDirRes v9fs_treaddir(TReadDirOpt); void v9fs_rreaddir(P9Req *req, uint32_t *count, uint32_t *nentries, struct V9fsDirent **entries); diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c index ab3a12c816..f515a9bb15 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c @@ -20,6 +20,7 @@ #define tversion(...) v9fs_tversion((TVersionOpt) __VA_ARGS__) #define tattach(...) v9fs_tattach((TAttachOpt) __VA_ARGS__) #define tgetattr(...) v9fs_tgetattr((TGetAttrOpt) __VA_ARGS__) +#define tsetattr(...) v9fs_tsetattr((TSetAttrOpt) __VA_ARGS__) #define treaddir(...) v9fs_treaddir((TReadDirOpt) __VA_ARGS__) #define tlopen(...) v9fs_tlopen((TLOpenOpt) __VA_ARGS__) #define twrite(...) v9fs_twrite((TWriteOpt) __VA_ARGS__) From 610dc187e52605c8ea8d14c5e7d8e7384f8af290 Mon Sep 17 00:00:00 2001 From: Greg Kurz Date: Wed, 12 Mar 2025 16:29:32 +0100 Subject: [PATCH 0639/2760] tests/9p: Test `Tsetattr` can truncate unlinked file Enhance the `use-after-unlink` test with a new check for the case where the client wants to alter the size of an unlinked file for which it still has an active fid. Suggested-by: Christian Schoenebeck Signed-off-by: Greg Kurz Reviewed-by: Christian Schoenebeck Message-Id: <20250312152933.383967-7-groug@kaod.org> Signed-off-by: Christian Schoenebeck --- tests/qtest/virtio-9p-test.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/qtest/virtio-9p-test.c b/tests/qtest/virtio-9p-test.c index f515a9bb15..ac38ccf595 100644 --- a/tests/qtest/virtio-9p-test.c +++ b/tests/qtest/virtio-9p-test.c @@ -736,6 +736,20 @@ static void fs_use_after_unlink(void *obj, void *data, .data = buf }).count; g_assert_cmpint(count, ==, write_count); + + /* truncate file to (arbitrarily chosen) size 2001 */ + tsetattr({ + .client = v9p, .fid = fid_file, .attr = (v9fs_attr) { + .valid = P9_SETATTR_SIZE, + .size = 2001 + } + }); + /* truncate apparently succeeded, let's double-check the size */ + tgetattr({ + .client = v9p, .fid = fid_file, .request_mask = P9_GETATTR_BASIC, + .rgetattr.attr = &attr + }); + g_assert_cmpint(attr.size, ==, 2001); } static void cleanup_9p_local_driver(void *data) From cdafeda35709ddf8cd982a7eb653c2a5028c8074 Mon Sep 17 00:00:00 2001 From: Christian Schoenebeck Date: Thu, 20 Mar 2025 13:16:20 +0100 Subject: [PATCH 0640/2760] 9pfs: fix 'total_open_fd' decrementation According to 'man 2 close' errors returned by close() should only be used for either diagnostic purposes or for catching data loss due to a previous write error, as an error result of close() usually indicates a deferred error of a previous write operation. Therefore not decrementing 'total_open_fd' on a close() error is wrong and would yield in a higher open file descriptor count than actually the case, leading to 9p server reclaiming open file descriptors too soon. Based-on: <20250312152933.383967-7-groug@kaod.org> Signed-off-by: Christian Schoenebeck Reviewed-by: Greg Kurz Message-Id: --- hw/9pfs/9p.c | 10 +++++++++- hw/9pfs/codir.c | 7 ++++++- hw/9pfs/cofile.c | 7 ++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index b22df3aa2b..8b001b9112 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -510,7 +510,15 @@ void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu) err = (f->fid_type == P9_FID_DIR) ? s->ops->closedir(&s->ctx, &f->fs_reclaim) : s->ops->close(&s->ctx, &f->fs_reclaim); - if (!err) { + + /* 'man 2 close' suggests to ignore close() errors except of EBADF */ + if (unlikely(err && errno == EBADF)) { + /* + * unexpected case as FIDs were picked above by having a valid + * file descriptor + */ + error_report("9pfs: v9fs_reclaim_fd() WARNING: close() failed with EBADF"); + } else { /* total_open_fd must only be mutated on main thread */ nclosed++; } diff --git a/hw/9pfs/codir.c b/hw/9pfs/codir.c index 2068a4779d..bce7dd96e9 100644 --- a/hw/9pfs/codir.c +++ b/hw/9pfs/codir.c @@ -20,6 +20,7 @@ #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" #include "qemu/main-loop.h" +#include "qemu/error-report.h" #include "coth.h" #include "9p-xattr.h" #include "9p-util.h" @@ -353,7 +354,11 @@ int coroutine_fn v9fs_co_closedir(V9fsPDU *pdu, V9fsFidOpenState *fs) err = -errno; } }); - if (!err) { + /* 'man 2 close' suggests to ignore close() errors except of EBADF */ + if (unlikely(err && errno == EBADF)) { + /* unexpected case as we should have checked for a valid file handle */ + error_report("9pfs: WARNING: v9fs_co_closedir() failed with EBADF"); + } else { total_open_fd--; } return err; diff --git a/hw/9pfs/cofile.c b/hw/9pfs/cofile.c index 71174c3e4a..6e775c8e41 100644 --- a/hw/9pfs/cofile.c +++ b/hw/9pfs/cofile.c @@ -20,6 +20,7 @@ #include "fsdev/qemu-fsdev.h" #include "qemu/thread.h" #include "qemu/main-loop.h" +#include "qemu/error-report.h" #include "coth.h" int coroutine_fn v9fs_co_st_gen(V9fsPDU *pdu, V9fsPath *path, mode_t st_mode, @@ -197,7 +198,11 @@ int coroutine_fn v9fs_co_close(V9fsPDU *pdu, V9fsFidOpenState *fs) err = -errno; } }); - if (!err) { + /* 'man 2 close' suggests to ignore close() errors except of EBADF */ + if (unlikely(err && errno == EBADF)) { + /* unexpected case as we should have checked for a valid file handle */ + error_report("9pfs: WARNING: v9fs_co_close() failed with EBADF"); + } else { total_open_fd--; } return err; From d551b822f7ac329c763267659950992849d7e735 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 15:33:05 -0700 Subject: [PATCH 0641/2760] accel/tcg: Use vaddr in cpu_loop.h Use vaddr instead of abi_ptr or target_ulong for a guest address. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 2 +- bsd-user/signal.c | 4 ++-- include/user/cpu_loop.h | 12 +++++------- linux-user/signal.c | 4 ++-- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 68e01fc584..e1f4c4eacf 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -126,7 +126,7 @@ MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write) * guest, we'd end up in an infinite loop of retrying the faulting access. */ bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, - uintptr_t host_pc, abi_ptr guest_addr) + uintptr_t host_pc, vaddr guest_addr) { switch (page_unprotect(cpu, guest_addr, host_pc)) { case 0: diff --git a/bsd-user/signal.c b/bsd-user/signal.c index 1aa0fd79d6..dadcc037dc 100644 --- a/bsd-user/signal.c +++ b/bsd-user/signal.c @@ -1030,7 +1030,7 @@ void process_pending_signals(CPUArchState *env) ts->in_sigsuspend = false; } -void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, +void cpu_loop_exit_sigsegv(CPUState *cpu, vaddr addr, MMUAccessType access_type, bool maperr, uintptr_t ra) { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; @@ -1046,7 +1046,7 @@ void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, cpu_loop_exit_restore(cpu, ra); } -void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, +void cpu_loop_exit_sigbus(CPUState *cpu, vaddr addr, MMUAccessType access_type, uintptr_t ra) { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; diff --git a/include/user/cpu_loop.h b/include/user/cpu_loop.h index 589c66543f..ad8a1d711f 100644 --- a/include/user/cpu_loop.h +++ b/include/user/cpu_loop.h @@ -20,11 +20,9 @@ #ifndef USER_CPU_LOOP_H #define USER_CPU_LOOP_H -#include "exec/abi_ptr.h" +#include "exec/vaddr.h" #include "exec/mmu-access-type.h" -#include "exec/log.h" -#include "exec/target_long.h" -#include "special-errno.h" + /** * adjust_signal_pc: @@ -46,7 +44,7 @@ MMUAccessType adjust_signal_pc(uintptr_t *pc, bool is_write); * Return true if the write fault has been handled, and should be re-tried. */ bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, - uintptr_t host_pc, abi_ptr guest_addr); + uintptr_t host_pc, vaddr guest_addr); /** * cpu_loop_exit_sigsegv: @@ -59,7 +57,7 @@ bool handle_sigsegv_accerr_write(CPUState *cpu, sigset_t *old_set, * Use the TCGCPUOps hook to record cpu state, do guest operating system * specific things to raise SIGSEGV, and jump to the main cpu loop. */ -G_NORETURN void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, +G_NORETURN void cpu_loop_exit_sigsegv(CPUState *cpu, vaddr addr, MMUAccessType access_type, bool maperr, uintptr_t ra); @@ -73,7 +71,7 @@ G_NORETURN void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, * Use the TCGCPUOps hook to record cpu state, do guest operating system * specific things to raise SIGBUS, and jump to the main cpu loop. */ -G_NORETURN void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, +G_NORETURN void cpu_loop_exit_sigbus(CPUState *cpu, vaddr addr, MMUAccessType access_type, uintptr_t ra); diff --git a/linux-user/signal.c b/linux-user/signal.c index 4dafc2c3a2..cd0e7398aa 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -750,7 +750,7 @@ void force_sigsegv(int oldsig) } #endif -void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, +void cpu_loop_exit_sigsegv(CPUState *cpu, vaddr addr, MMUAccessType access_type, bool maperr, uintptr_t ra) { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; @@ -766,7 +766,7 @@ void cpu_loop_exit_sigsegv(CPUState *cpu, target_ulong addr, cpu_loop_exit_restore(cpu, ra); } -void cpu_loop_exit_sigbus(CPUState *cpu, target_ulong addr, +void cpu_loop_exit_sigbus(CPUState *cpu, vaddr addr, MMUAccessType access_type, uintptr_t ra) { const TCGCPUOps *tcg_ops = cpu->cc->tcg_ops; From 9b74d403b30e64256b4b94cbc01c76a0382ca5e8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 16:54:31 -0700 Subject: [PATCH 0642/2760] accel/tcg: Move user-only tlb_vaddr_to_host out of line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At the same time, fix a mis-match between user and system by using vaddr not abi_ptr for the address parameter. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 6 ++++++ include/accel/tcg/cpu-ldst.h | 8 -------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index e1f4c4eacf..adc5296ba5 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -850,6 +850,12 @@ void *probe_access(CPUArchState *env, vaddr addr, int size, return size ? g2h(env_cpu(env), addr) : NULL; } +void *tlb_vaddr_to_host(CPUArchState *env, vaddr addr, + MMUAccessType access_type, int mmu_idx) +{ + return g2h(env_cpu(env), addr); +} + tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, void **hostp) { diff --git a/include/accel/tcg/cpu-ldst.h b/include/accel/tcg/cpu-ldst.h index 44a62b54da..00e6419e13 100644 --- a/include/accel/tcg/cpu-ldst.h +++ b/include/accel/tcg/cpu-ldst.h @@ -515,15 +515,7 @@ static inline uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr) * Otherwise (TLB entry is for an I/O access, guest software * TLB fill required, etc) return NULL. */ -#ifdef CONFIG_USER_ONLY -static inline void *tlb_vaddr_to_host(CPUArchState *env, abi_ptr addr, - MMUAccessType access_type, int mmu_idx) -{ - return g2h(env_cpu(env), addr); -} -#else void *tlb_vaddr_to_host(CPUArchState *env, vaddr addr, MMUAccessType access_type, int mmu_idx); -#endif #endif /* ACCEL_TCG_CPU_LDST_H */ From a21959a8a835783b556d4a1d18aaa2fad4b7ea62 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 17:09:28 -0700 Subject: [PATCH 0643/2760] accel/tcg: Move tlb_vaddr_to_host declaration to probe.h This is a probing function, not a load/store function. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/accel/tcg/cpu-ldst.h | 16 ---------------- include/accel/tcg/probe.h | 16 ++++++++++++++++ 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/include/accel/tcg/cpu-ldst.h b/include/accel/tcg/cpu-ldst.h index 00e6419e13..0de7f5eaa6 100644 --- a/include/accel/tcg/cpu-ldst.h +++ b/include/accel/tcg/cpu-ldst.h @@ -502,20 +502,4 @@ static inline uint64_t cpu_ldq_code(CPUArchState *env, abi_ptr addr) return cpu_ldq_code_mmu(env, addr, oi, 0); } -/** - * tlb_vaddr_to_host: - * @env: CPUArchState - * @addr: guest virtual address to look up - * @access_type: 0 for read, 1 for write, 2 for execute - * @mmu_idx: MMU index to use for lookup - * - * Look up the specified guest virtual index in the TCG softmmu TLB. - * If we can translate a host virtual address suitable for direct RAM - * access, without causing a guest exception, then return it. - * Otherwise (TLB entry is for an I/O access, guest software - * TLB fill required, etc) return NULL. - */ -void *tlb_vaddr_to_host(CPUArchState *env, vaddr addr, - MMUAccessType access_type, int mmu_idx); - #endif /* ACCEL_TCG_CPU_LDST_H */ diff --git a/include/accel/tcg/probe.h b/include/accel/tcg/probe.h index 177bd1608d..dd9ecbbdf1 100644 --- a/include/accel/tcg/probe.h +++ b/include/accel/tcg/probe.h @@ -103,4 +103,20 @@ int probe_access_full_mmu(CPUArchState *env, vaddr addr, int size, #endif /* !CONFIG_USER_ONLY */ +/** + * tlb_vaddr_to_host: + * @env: CPUArchState + * @addr: guest virtual address to look up + * @access_type: 0 for read, 1 for write, 2 for execute + * @mmu_idx: MMU index to use for lookup + * + * Look up the specified guest virtual index in the TCG softmmu TLB. + * If we can translate a host virtual address suitable for direct RAM + * access, without causing a guest exception, then return it. + * Otherwise (TLB entry is for an I/O access, guest software + * TLB fill required, etc) return NULL. + */ +void *tlb_vaddr_to_host(CPUArchState *env, vaddr addr, + MMUAccessType access_type, int mmu_idx); + #endif From b5555a077f7a2e655b0a4aec9328d70497a7dd65 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 17:17:56 -0700 Subject: [PATCH 0644/2760] accel/tcg: Use target_long_bits() in cputlb.c Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 5b6d6f7975..35c467aace 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -19,6 +19,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" +#include "qemu/target-info.h" #include "accel/tcg/cpu-ops.h" #include "accel/tcg/iommu.h" #include "accel/tcg/probe.h" @@ -771,19 +772,19 @@ void tlb_flush_range_by_mmuidx(CPUState *cpu, vaddr addr, assert_cpu_is_self(cpu); + /* If no page bits are significant, this devolves to tlb_flush. */ + if (bits < TARGET_PAGE_BITS) { + tlb_flush_by_mmuidx(cpu, idxmap); + return; + } /* * If all bits are significant, and len is small, * this devolves to tlb_flush_page. */ - if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) { + if (len <= TARGET_PAGE_SIZE && bits >= target_long_bits()) { tlb_flush_page_by_mmuidx(cpu, addr, idxmap); return; } - /* If no page bits are significant, this devolves to tlb_flush. */ - if (bits < TARGET_PAGE_BITS) { - tlb_flush_by_mmuidx(cpu, idxmap); - return; - } /* This should already be page aligned */ d.addr = addr & TARGET_PAGE_MASK; @@ -809,19 +810,19 @@ void tlb_flush_range_by_mmuidx_all_cpus_synced(CPUState *src_cpu, TLBFlushRangeData d, *p; CPUState *dst_cpu; + /* If no page bits are significant, this devolves to tlb_flush. */ + if (bits < TARGET_PAGE_BITS) { + tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, idxmap); + return; + } /* * If all bits are significant, and len is small, * this devolves to tlb_flush_page. */ - if (bits >= TARGET_LONG_BITS && len <= TARGET_PAGE_SIZE) { + if (len <= TARGET_PAGE_SIZE && bits >= target_long_bits()) { tlb_flush_page_by_mmuidx_all_cpus_synced(src_cpu, addr, idxmap); return; } - /* If no page bits are significant, this devolves to tlb_flush. */ - if (bits < TARGET_PAGE_BITS) { - tlb_flush_by_mmuidx_all_cpus_synced(src_cpu, idxmap); - return; - } /* This should already be page aligned */ d.addr = addr & TARGET_PAGE_MASK; From 4b2de658f13df52ccbf41c3399ab4f7adbcc6080 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 17:26:28 -0700 Subject: [PATCH 0645/2760] accel/tcg: Use vaddr for plugin_{load,store}_cb MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid the use of abi_ptr within ldst_common.c.inc. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/ldst_common.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/tcg/ldst_common.c.inc b/accel/tcg/ldst_common.c.inc index 9791a4e9ef..57f3e06192 100644 --- a/accel/tcg/ldst_common.c.inc +++ b/accel/tcg/ldst_common.c.inc @@ -123,7 +123,7 @@ void helper_st_i128(CPUArchState *env, uint64_t addr, Int128 val, MemOpIdx oi) * Load helpers for cpu_ldst.h */ -static void plugin_load_cb(CPUArchState *env, abi_ptr addr, +static void plugin_load_cb(CPUArchState *env, vaddr addr, uint64_t value_low, uint64_t value_high, MemOpIdx oi) @@ -193,7 +193,7 @@ Int128 cpu_ld16_mmu(CPUArchState *env, vaddr addr, * Store helpers for cpu_ldst.h */ -static void plugin_store_cb(CPUArchState *env, abi_ptr addr, +static void plugin_store_cb(CPUArchState *env, vaddr addr, uint64_t value_low, uint64_t value_high, MemOpIdx oi) From 0566f364f79c452af99f437a7784397b03775c72 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 17:31:43 -0700 Subject: [PATCH 0646/2760] accel/tcg: Build cputlb.c once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 3 ++- accel/tcg/meson.build | 5 +---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 35c467aace..5f6d7c601c 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -25,7 +25,8 @@ #include "accel/tcg/probe.h" #include "exec/page-protection.h" #include "system/memory.h" -#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/cpu-ldst-common.h" +#include "accel/tcg/cpu-mmu-index.h" #include "exec/cputlb.h" #include "exec/tb-flush.h" #include "system/ram_addr.h" diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index d6bd304add..9b86051b82 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -25,15 +25,12 @@ tcg_specific_ss = ss.source_set() tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) -specific_ss.add(when: ['CONFIG_SYSTEM_ONLY', 'CONFIG_TCG'], if_true: files( - 'cputlb.c', -)) - libuser_ss.add(files( 'user-exec-stub.c', )) libsystem_ss.add(files( + 'cputlb.c', 'icount-common.c', 'monitor.c', 'tcg-accel-ops.c', From 30da476066d6470e1064eb564e348af945a1a656 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 18:07:47 -0700 Subject: [PATCH 0647/2760] include/user: Convert GUEST_ADDR_MAX to a variable Remove GUEST_ADDR_MAX and add guest_addr_max. Initialize it in *-user/main.c, after reserved_va. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 4 ++-- bsd-user/main.c | 8 ++++++++ include/user/guest-host.h | 27 +++++++-------------------- linux-user/main.c | 8 ++++++++ 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index adc5296ba5..f674fd875e 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -500,7 +500,7 @@ void page_set_flags(vaddr start, vaddr last, int flags) guest address space. If this assert fires, it probably indicates a missing call to h2g_valid. */ assert(start <= last); - assert(last <= GUEST_ADDR_MAX); + assert(last <= guest_addr_max); /* Only set PAGE_ANON with new mappings. */ assert(!(flags & PAGE_ANON) || (flags & PAGE_RESET)); assert_memory_lock(); @@ -621,7 +621,7 @@ vaddr page_find_range_empty(vaddr min, vaddr max, vaddr len, vaddr align) vaddr len_m1, align_m1; assert(min <= max); - assert(max <= GUEST_ADDR_MAX); + assert(max <= guest_addr_max); assert(len != 0); assert(is_power_of_2(align)); assert_memory_lock(); diff --git a/bsd-user/main.c b/bsd-user/main.c index fa7645a56e..603fc80ba7 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -89,6 +89,7 @@ bool have_guest_base; #endif unsigned long reserved_va; +unsigned long guest_addr_max; const char *interp_prefix = CONFIG_QEMU_INTERP_PREFIX; const char *qemu_uname_release; @@ -500,6 +501,13 @@ int main(int argc, char **argv) /* MAX_RESERVED_VA + 1 is a large power of 2, so is aligned. */ reserved_va = max_reserved_va; } + if (reserved_va != 0) { + guest_addr_max = reserved_va; + } else if (MIN(TARGET_VIRT_ADDR_SPACE_BITS, TARGET_ABI_BITS) <= 32) { + guest_addr_max = UINT32_MAX; + } else { + guest_addr_max = ~0ul; + } if (getenv("QEMU_STRACE")) { do_strace = 1; diff --git a/include/user/guest-host.h b/include/user/guest-host.h index 8d2079bbbb..8e10d36948 100644 --- a/include/user/guest-host.h +++ b/include/user/guest-host.h @@ -23,23 +23,11 @@ extern unsigned long reserved_va; /* - * Limit the guest addresses as best we can. - * - * When not using -R reserved_va, we cannot really limit the guest - * to less address space than the host. For 32-bit guests, this - * acts as a sanity check that we're not giving the guest an address - * that it cannot even represent. For 64-bit guests... the address - * might not be what the real kernel would give, but it is at least - * representable in the guest. - * - * TODO: Improve address allocation to avoid this problem, and to - * avoid setting bits at the top of guest addresses that might need - * to be used for tags. + * The last byte of the guest address space. + * If reserved_va is non-zero, guest_addr_max matches. + * If reserved_va is zero, guest_addr_max equals the full guest space. */ -#define GUEST_ADDR_MAX_ \ - ((MIN_CONST(TARGET_VIRT_ADDR_SPACE_BITS, TARGET_ABI_BITS) <= 32) ? \ - UINT32_MAX : ~0ul) -#define GUEST_ADDR_MAX (reserved_va ? : GUEST_ADDR_MAX_) +extern unsigned long guest_addr_max; #ifndef TARGET_TAGGED_ADDRESSES static inline abi_ptr cpu_untagged_addr(CPUState *cs, abi_ptr x) @@ -61,17 +49,16 @@ static inline void *g2h(CPUState *cs, abi_ptr x) static inline bool guest_addr_valid_untagged(abi_ulong x) { - return x <= GUEST_ADDR_MAX; + return x <= guest_addr_max; } static inline bool guest_range_valid_untagged(abi_ulong start, abi_ulong len) { - return len - 1 <= GUEST_ADDR_MAX && start <= GUEST_ADDR_MAX - len + 1; + return len - 1 <= guest_addr_max && start <= guest_addr_max - len + 1; } #define h2g_valid(x) \ - (HOST_LONG_BITS <= TARGET_VIRT_ADDR_SPACE_BITS || \ - (uintptr_t)(x) - guest_base <= GUEST_ADDR_MAX) + ((uintptr_t)(x) - guest_base <= guest_addr_max) #define h2g_nocheck(x) ({ \ uintptr_t __ret = (uintptr_t)(x) - guest_base; \ diff --git a/linux-user/main.c b/linux-user/main.c index 4af7f49f38..5ac5b55dc6 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -122,6 +122,7 @@ static const char *last_log_filename; #endif unsigned long reserved_va; +unsigned long guest_addr_max; static void usage(int exitcode); @@ -858,6 +859,13 @@ int main(int argc, char **argv, char **envp) /* MAX_RESERVED_VA + 1 is a large power of 2, so is aligned. */ reserved_va = max_reserved_va; } + if (reserved_va != 0) { + guest_addr_max = reserved_va; + } else if (MIN(TARGET_VIRT_ADDR_SPACE_BITS, TARGET_ABI_BITS) <= 32) { + guest_addr_max = UINT32_MAX; + } else { + guest_addr_max = ~0ul; + } /* * Temporarily disable From 7804c84a56f9265813eb87db0bdae063279af030 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 18:20:04 -0700 Subject: [PATCH 0648/2760] include/user: Use vaddr in guest-host.h Replace abi_ptr and abi_ulong with vaddr. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/user/guest-host.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/include/user/guest-host.h b/include/user/guest-host.h index 8e10d36948..0656f2e356 100644 --- a/include/user/guest-host.h +++ b/include/user/guest-host.h @@ -8,7 +8,7 @@ #ifndef USER_GUEST_HOST_H #define USER_GUEST_HOST_H -#include "user/abitypes.h" +#include "exec/vaddr.h" #include "user/guest-base.h" #include "cpu.h" @@ -30,29 +30,29 @@ extern unsigned long reserved_va; extern unsigned long guest_addr_max; #ifndef TARGET_TAGGED_ADDRESSES -static inline abi_ptr cpu_untagged_addr(CPUState *cs, abi_ptr x) +static inline vaddr cpu_untagged_addr(CPUState *cs, vaddr x) { return x; } #endif /* All direct uses of g2h and h2g need to go away for usermode softmmu. */ -static inline void *g2h_untagged(abi_ptr x) +static inline void *g2h_untagged(vaddr x) { return (void *)((uintptr_t)(x) + guest_base); } -static inline void *g2h(CPUState *cs, abi_ptr x) +static inline void *g2h(CPUState *cs, vaddr x) { return g2h_untagged(cpu_untagged_addr(cs, x)); } -static inline bool guest_addr_valid_untagged(abi_ulong x) +static inline bool guest_addr_valid_untagged(vaddr x) { return x <= guest_addr_max; } -static inline bool guest_range_valid_untagged(abi_ulong start, abi_ulong len) +static inline bool guest_range_valid_untagged(vaddr start, vaddr len) { return len - 1 <= guest_addr_max && start <= guest_addr_max - len + 1; } @@ -62,7 +62,7 @@ static inline bool guest_range_valid_untagged(abi_ulong start, abi_ulong len) #define h2g_nocheck(x) ({ \ uintptr_t __ret = (uintptr_t)(x) - guest_base; \ - (abi_ptr)__ret; \ + (vaddr)__ret; \ }) #define h2g(x) ({ \ From 2c0b261fcd259f0e027633c744d279d255b4ff49 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 18:46:41 -0700 Subject: [PATCH 0649/2760] accel/tcg: Move TARGET_TAGGED_ADDRESSES to TCGCPUOps.untagged_addr Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- include/accel/tcg/cpu-ops.h | 7 +++++++ include/user/guest-host.h | 8 +++++--- target/arm/cpu-param.h | 7 +------ target/arm/cpu.c | 27 ++++++++++++++++++++++++++- target/arm/cpu.h | 32 +------------------------------- 5 files changed, 40 insertions(+), 41 deletions(-) diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index 23cd6af0b2..cd22e5d5b9 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -157,6 +157,13 @@ struct TCGCPUOps { */ void (*record_sigbus)(CPUState *cpu, vaddr addr, MMUAccessType access_type, uintptr_t ra); + + /** + * untagged_addr: Remove an ignored tag from an address + * @cpu: cpu context + * @addr: tagged guest address + */ + vaddr (*untagged_addr)(CPUState *cs, vaddr addr); #else /** @do_interrupt: Callback for interrupt handling. */ void (*do_interrupt)(CPUState *cpu); diff --git a/include/user/guest-host.h b/include/user/guest-host.h index 0656f2e356..8f7ef75896 100644 --- a/include/user/guest-host.h +++ b/include/user/guest-host.h @@ -10,7 +10,7 @@ #include "exec/vaddr.h" #include "user/guest-base.h" -#include "cpu.h" +#include "accel/tcg/cpu-ops.h" /* * If non-zero, the guest virtual address space is a contiguous subset @@ -29,12 +29,14 @@ extern unsigned long reserved_va; */ extern unsigned long guest_addr_max; -#ifndef TARGET_TAGGED_ADDRESSES static inline vaddr cpu_untagged_addr(CPUState *cs, vaddr x) { + const TCGCPUOps *tcg_ops = cs->cc->tcg_ops; + if (tcg_ops->untagged_addr) { + return tcg_ops->untagged_addr(cs, x); + } return x; } -#endif /* All direct uses of g2h and h2g need to go away for usermode softmmu. */ static inline void *g2h_untagged(vaddr x) diff --git a/target/arm/cpu-param.h b/target/arm/cpu-param.h index 5c5bc8a009..8b46c7c570 100644 --- a/target/arm/cpu-param.h +++ b/target/arm/cpu-param.h @@ -17,14 +17,9 @@ #endif #ifdef CONFIG_USER_ONLY -# ifdef TARGET_AARCH64 -# define TARGET_TAGGED_ADDRESSES -# ifdef __FreeBSD__ -# define TARGET_PAGE_BITS 12 -# else +# if defined(TARGET_AARCH64) && defined(CONFIG_LINUX) /* Allow user-only to vary page size from 4k */ # define TARGET_PAGE_BITS_VARY -# endif # else # define TARGET_PAGE_BITS 12 # endif diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 2020aec54a..45cb6fd7ee 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2671,7 +2671,31 @@ static const char *arm_gdb_get_core_xml_file(CPUState *cs) return "arm-core.xml"; } -#ifndef CONFIG_USER_ONLY +#ifdef CONFIG_USER_ONLY +/** + * aarch64_untagged_addr: + * + * Remove any address tag from @x. This is explicitly related to the + * linux syscall TIF_TAGGED_ADDR setting, not TBI in general. + * + * There should be a better place to put this, but we need this in + * include/exec/cpu_ldst.h, and not some place linux-user specific. + * + * Note that arm-*-user will never set tagged_addr_enable. + */ +static vaddr aarch64_untagged_addr(CPUState *cs, vaddr x) +{ + CPUARMState *env = cpu_env(cs); + if (env->tagged_addr_enable) { + /* + * TBI is enabled for userspace but not kernelspace addresses. + * Only clear the tag if bit 55 is clear. + */ + x &= sextract64(x, 0, 56); + } + return x; +} +#else #include "hw/core/sysemu-cpu-ops.h" static const struct SysemuCPUOps arm_sysemu_ops = { @@ -2702,6 +2726,7 @@ static const TCGCPUOps arm_tcg_ops = { #ifdef CONFIG_USER_ONLY .record_sigsegv = arm_cpu_record_sigsegv, .record_sigbus = arm_cpu_record_sigbus, + .untagged_addr = aarch64_untagged_addr, #else .tlb_fill_align = arm_cpu_tlb_fill_align, .cpu_exec_interrupt = arm_cpu_exec_interrupt, diff --git a/target/arm/cpu.h b/target/arm/cpu.h index be4449ca06..23720b2b17 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -783,12 +783,9 @@ typedef struct CPUArchState { #else /* CONFIG_USER_ONLY */ /* For usermode syscall translation. */ bool eabi; -#endif /* CONFIG_USER_ONLY */ - -#ifdef TARGET_TAGGED_ADDRESSES /* Linux syscall tagged address support */ bool tagged_addr_enable; -#endif +#endif /* CONFIG_USER_ONLY */ } CPUARMState; static inline void set_feature(CPUARMState *env, int feature) @@ -3217,34 +3214,7 @@ extern const uint64_t pred_esz_masks[5]; #define TAG_GRANULE (1 << LOG2_TAG_GRANULE) #ifdef CONFIG_USER_ONLY - #define TARGET_PAGE_DATA_SIZE (TARGET_PAGE_SIZE >> (LOG2_TAG_GRANULE + 1)) - -#ifdef TARGET_TAGGED_ADDRESSES -/** - * cpu_untagged_addr: - * @cs: CPU context - * @x: tagged address - * - * Remove any address tag from @x. This is explicitly related to the - * linux syscall TIF_TAGGED_ADDR setting, not TBI in general. - * - * There should be a better place to put this, but we need this in - * include/exec/cpu_ldst.h, and not some place linux-user specific. - */ -static inline target_ulong cpu_untagged_addr(CPUState *cs, target_ulong x) -{ - CPUARMState *env = cpu_env(cs); - if (env->tagged_addr_enable) { - /* - * TBI is enabled for userspace but not kernelspace addresses. - * Only clear the tag if bit 55 is clear. - */ - x &= sextract64(x, 0, 56); - } - return x; -} -#endif /* TARGET_TAGGED_ADDRESSES */ #endif /* CONFIG_USER_ONLY */ #endif From 964080d3563f1211b70051c8ea5add752586da09 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 20:23:59 -0700 Subject: [PATCH 0650/2760] accel/tcg: Remove TARGET_PAGE_DATA_SIZE This macro is used by only one target, and even then under unusual conditions -- AArch64 with mmap's PROT_MTE flag. Since page size for aarch64-linux-user is variable, the per-page data size is also variable. Since page_reset_target_data via target_munmap does not have ready access to CPUState, simply pass in the size from the first allocation and remember that. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 26 ++++++++++++++++---------- include/user/page-protection.h | 8 +++++--- target/arm/cpu.h | 4 ---- target/arm/tcg/mte_helper.c | 4 ++-- 4 files changed, 23 insertions(+), 19 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index f674fd875e..46b1e97c30 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -870,7 +870,6 @@ tb_page_addr_t get_page_addr_code_hostp(CPUArchState *env, vaddr addr, return addr; } -#ifdef TARGET_PAGE_DATA_SIZE /* * Allocate chunks of target data together. For the only current user, * if we allocate one hunk per page, we have overhead of 40/128 or 40%. @@ -886,10 +885,16 @@ typedef struct TargetPageDataNode { } TargetPageDataNode; static IntervalTreeRoot targetdata_root; +static size_t target_page_data_size; void page_reset_target_data(vaddr start, vaddr last) { IntervalTreeNode *n, *next; + size_t size = target_page_data_size; + + if (likely(size == 0)) { + return; + } assert_memory_lock(); @@ -920,17 +925,22 @@ void page_reset_target_data(vaddr start, vaddr last) n_last = MIN(last, n->last); p_len = (n_last + 1 - n_start) >> TARGET_PAGE_BITS; - memset(t->data + p_ofs * TARGET_PAGE_DATA_SIZE, 0, - p_len * TARGET_PAGE_DATA_SIZE); + memset(t->data + p_ofs * size, 0, p_len * size); } } -void *page_get_target_data(vaddr address) +void *page_get_target_data(vaddr address, size_t size) { IntervalTreeNode *n; TargetPageDataNode *t; vaddr page, region, p_ofs; + /* Remember the size from the first call, and it should be constant. */ + if (unlikely(target_page_data_size != size)) { + assert(target_page_data_size == 0); + target_page_data_size = size; + } + page = address & TARGET_PAGE_MASK; region = address & TBD_MASK; @@ -945,8 +955,7 @@ void *page_get_target_data(vaddr address) mmap_lock(); n = interval_tree_iter_first(&targetdata_root, page, page); if (!n) { - t = g_malloc0(sizeof(TargetPageDataNode) - + TPD_PAGES * TARGET_PAGE_DATA_SIZE); + t = g_malloc0(sizeof(TargetPageDataNode) + TPD_PAGES * size); n = &t->itree; n->start = region; n->last = region | ~TBD_MASK; @@ -957,11 +966,8 @@ void *page_get_target_data(vaddr address) t = container_of(n, TargetPageDataNode, itree); p_ofs = (page - region) >> TARGET_PAGE_BITS; - return t->data + p_ofs * TARGET_PAGE_DATA_SIZE; + return t->data + p_ofs * size; } -#else -void page_reset_target_data(vaddr start, vaddr last) { } -#endif /* TARGET_PAGE_DATA_SIZE */ /* The system-mode versions of these helpers are in cputlb.c. */ diff --git a/include/user/page-protection.h b/include/user/page-protection.h index 86143212fd..4bde664e4a 100644 --- a/include/user/page-protection.h +++ b/include/user/page-protection.h @@ -73,18 +73,20 @@ bool page_check_range_empty(vaddr start, vaddr last); vaddr page_find_range_empty(vaddr min, vaddr max, vaddr len, vaddr align); /** - * page_get_target_data(address) + * page_get_target_data * @address: guest virtual address + * @size: per-page size * - * Return TARGET_PAGE_DATA_SIZE bytes of out-of-band data to associate + * Return @size bytes of out-of-band data to associate * with the guest page at @address, allocating it if necessary. The * caller should already have verified that the address is valid. + * The value of @size must be the same for every call. * * The memory will be freed when the guest page is deallocated, * e.g. with the munmap system call. */ __attribute__((returns_nonnull)) -void *page_get_target_data(vaddr address); +void *page_get_target_data(vaddr address, size_t size); typedef int (*walk_memory_regions_fn)(void *, vaddr, vaddr, int); int walk_memory_regions(void *, walk_memory_regions_fn); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 23720b2b17..6ed6409cb7 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -3213,8 +3213,4 @@ extern const uint64_t pred_esz_masks[5]; #define LOG2_TAG_GRANULE 4 #define TAG_GRANULE (1 << LOG2_TAG_GRANULE) -#ifdef CONFIG_USER_ONLY -#define TARGET_PAGE_DATA_SIZE (TARGET_PAGE_SIZE >> (LOG2_TAG_GRANULE + 1)) -#endif /* CONFIG_USER_ONLY */ - #endif diff --git a/target/arm/tcg/mte_helper.c b/target/arm/tcg/mte_helper.c index 13d7ac0097..0efc18a181 100644 --- a/target/arm/tcg/mte_helper.c +++ b/target/arm/tcg/mte_helper.c @@ -37,7 +37,6 @@ #include "qemu/guest-random.h" #include "mte_helper.h" - static int choose_nonexcluded_tag(int tag, int offset, uint16_t exclude) { if (exclude == 0xffff) { @@ -63,6 +62,7 @@ uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx, bool probe, uintptr_t ra) { #ifdef CONFIG_USER_ONLY + const size_t page_data_size = TARGET_PAGE_SIZE >> (LOG2_TAG_GRANULE + 1); uint64_t clean_ptr = useronly_clean_ptr(ptr); int flags = page_get_flags(clean_ptr); uint8_t *tags; @@ -83,7 +83,7 @@ uint8_t *allocation_tag_mem_probe(CPUARMState *env, int ptr_mmu_idx, return NULL; } - tags = page_get_target_data(clean_ptr); + tags = page_get_target_data(clean_ptr, page_data_size); index = extract32(ptr, LOG2_TAG_GRANULE + 1, TARGET_PAGE_BITS - LOG2_TAG_GRANULE - 1); From 03c981e7d63eaa03e6b7c4b9ef59b45b0b985876 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 20:30:22 -0700 Subject: [PATCH 0651/2760] accel/tcg: Avoid abi_ptr in user-exec.c In page_dump/dump_region, use guest_addr_max to check the size of the guest address space and size the output appropriately. This will change output with small values of -R reserved_va, but shouldn't affect anything else. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/user-exec.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 46b1e97c30..085da0c036 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -29,6 +29,7 @@ #include "accel/tcg/helper-retaddr.h" #include "accel/tcg/probe.h" #include "user/cpu_loop.h" +#include "user/guest-host.h" #include "qemu/main-loop.h" #include "user/page-protection.h" #include "exec/page-protection.h" @@ -202,10 +203,19 @@ int walk_memory_regions(void *priv, walk_memory_regions_fn fn) static int dump_region(void *opaque, vaddr start, vaddr end, int prot) { FILE *f = opaque; + uint64_t mask; + int width; - fprintf(f, TARGET_ABI_FMT_ptr "-" TARGET_ABI_FMT_ptr - " " TARGET_ABI_FMT_ptr " %c%c%c\n", - (abi_ptr)start, (abi_ptr)end, (abi_ptr)(end - start), + if (guest_addr_max <= UINT32_MAX) { + mask = UINT32_MAX, width = 8; + } else { + mask = UINT64_MAX, width = 16; + } + + fprintf(f, "%0*" PRIx64 "-%0*" PRIx64 " %0*" PRIx64 " %c%c%c\n", + width, start & mask, + width, end & mask, + width, (end - start) & mask, ((prot & PAGE_READ) ? 'r' : '-'), ((prot & PAGE_WRITE) ? 'w' : '-'), ((prot & PAGE_EXEC) ? 'x' : '-')); @@ -215,10 +225,10 @@ static int dump_region(void *opaque, vaddr start, vaddr end, int prot) /* dump memory mappings */ void page_dump(FILE *f) { - const int length = sizeof(abi_ptr) * 2; + int width = guest_addr_max <= UINT32_MAX ? 8 : 16; fprintf(f, "%-*s %-*s %-*s %s\n", - length, "start", length, "end", length, "size", "prot"); + width, "start", width, "end", width, "size", "prot"); walk_memory_regions(f, dump_region); } @@ -1135,7 +1145,7 @@ static uint64_t do_ld8_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, return ret; } -static Int128 do_ld16_mmu(CPUState *cpu, abi_ptr addr, +static Int128 do_ld16_mmu(CPUState *cpu, vaddr addr, MemOpIdx oi, uintptr_t ra) { void *haddr; From 768cb76d14f1a50c00d60fbb1d393996c76645d8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 30 Apr 2025 20:31:10 -0700 Subject: [PATCH 0652/2760] accel/tcg: Build user-exec.c once Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/meson.build | 5 +---- accel/tcg/user-exec.c | 5 ++--- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 9b86051b82..d6f533f9a1 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -21,11 +21,8 @@ endif libuser_ss.add_all(tcg_ss) libsystem_ss.add_all(tcg_ss) -tcg_specific_ss = ss.source_set() -tcg_specific_ss.add(when: 'CONFIG_USER_ONLY', if_true: files('user-exec.c')) -specific_ss.add_all(when: 'CONFIG_TCG', if_true: tcg_specific_ss) - libuser_ss.add(files( + 'user-exec.c', 'user-exec-stub.c', )) diff --git a/accel/tcg/user-exec.c b/accel/tcg/user-exec.c index 085da0c036..f25d80e2dc 100644 --- a/accel/tcg/user-exec.c +++ b/accel/tcg/user-exec.c @@ -19,13 +19,12 @@ #include "qemu/osdep.h" #include "accel/tcg/cpu-ops.h" #include "disas/disas.h" -#include "cpu.h" #include "exec/vaddr.h" #include "exec/tlb-flags.h" #include "tcg/tcg.h" #include "qemu/bitops.h" #include "qemu/rcu.h" -#include "accel/tcg/cpu-ldst.h" +#include "accel/tcg/cpu-ldst-common.h" #include "accel/tcg/helper-retaddr.h" #include "accel/tcg/probe.h" #include "user/cpu_loop.h" @@ -33,7 +32,7 @@ #include "qemu/main-loop.h" #include "user/page-protection.h" #include "exec/page-protection.h" -#include "exec/helper-proto.h" +#include "exec/helper-proto-common.h" #include "qemu/atomic128.h" #include "qemu/bswap.h" #include "qemu/int128.h" From 36ad84ecb26d6a28c78d079dc51063d972600592 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 7 Mar 2025 09:34:41 +0800 Subject: [PATCH 0653/2760] hw/intc/loongarch_ipi: Add reset support Add reset support with ipi object, register reset callback and clear internal registers when virt machine resets. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_ipi.c | 29 +++++++++++++++++++++++++++++ include/hw/intc/loongarch_ipi.h | 1 + 2 files changed, 30 insertions(+) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 2f8bb57828..74372a2039 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -93,6 +93,32 @@ static void loongarch_ipi_realize(DeviceState *dev, Error **errp) } } +static void loongarch_ipi_reset_hold(Object *obj, ResetType type) +{ + int i; + LoongarchIPIClass *lic = LOONGARCH_IPI_GET_CLASS(obj); + LoongsonIPICommonState *lics = LOONGSON_IPI_COMMON(obj); + IPICore *core; + + if (lic->parent_phases.hold) { + lic->parent_phases.hold(obj, type); + } + + for (i = 0; i < lics->num_cpu; i++) { + core = lics->cpu + i; + /* IPI with targeted CPU available however not present */ + if (!core->cpu) { + continue; + } + + core->status = 0; + core->en = 0; + core->set = 0; + core->clear = 0; + memset(core->buf, 0, sizeof(core->buf)); + } +} + static void loongarch_ipi_cpu_plug(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -145,10 +171,13 @@ static void loongarch_ipi_class_init(ObjectClass *klass, const void *data) LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); LoongarchIPIClass *lic = LOONGARCH_IPI_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); DeviceClass *dc = DEVICE_CLASS(klass); device_class_set_parent_realize(dc, loongarch_ipi_realize, &lic->parent_realize); + resettable_class_set_parent_phases(rc, NULL, loongarch_ipi_reset_hold, + NULL, &lic->parent_phases); licc->get_iocsr_as = get_iocsr_as; licc->cpu_by_arch_id = loongarch_cpu_by_arch_id; hc->plug = loongarch_ipi_cpu_plug; diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h index 923bf21ecb..a7c6bf85d3 100644 --- a/include/hw/intc/loongarch_ipi.h +++ b/include/hw/intc/loongarch_ipi.h @@ -21,6 +21,7 @@ struct LoongarchIPIState { struct LoongarchIPIClass { LoongsonIPICommonClass parent_class; DeviceRealize parent_realize; + ResettablePhases parent_phases; }; #endif From 86e4a64751a728aae24fa95d76d6c313aa82cf82 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 7 Mar 2025 10:12:09 +0800 Subject: [PATCH 0654/2760] hw/intc/loongarch_extioi: Add reset support Add reset support with extioi irqchip, and register reset callback support with new API resettable_class_set_parent_phases(). Clear internal HW registers and SW state when virt machine resets. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_extioi_common.c | 41 +++++++++++++++++++++++ include/hw/intc/loongarch_extioi_common.h | 1 + 2 files changed, 42 insertions(+) diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index 9e1589060c..4a904b3bc1 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c @@ -108,6 +108,43 @@ static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) } } +static void loongarch_extioi_common_reset_hold(Object *obj, ResetType type) +{ + LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(obj); + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(obj); + ExtIOICore *core; + int i; + + if (lecc->parent_phases.hold) { + lecc->parent_phases.hold(obj, type); + } + + /* Clear HW registers for the board */ + memset(s->nodetype, 0, sizeof(s->nodetype)); + memset(s->bounce, 0, sizeof(s->bounce)); + memset(s->isr, 0, sizeof(s->isr)); + memset(s->enable, 0, sizeof(s->enable)); + memset(s->ipmap, 0, sizeof(s->ipmap)); + memset(s->coremap, 0, sizeof(s->coremap)); + memset(s->sw_pending, 0, sizeof(s->sw_pending)); + memset(s->sw_ipmap, 0, sizeof(s->sw_ipmap)); + memset(s->sw_coremap, 0, sizeof(s->sw_coremap)); + + for (i = 0; i < s->num_cpu; i++) { + core = s->cpu + i; + /* EXTIOI with targeted CPU available however not present */ + if (!core->cpu) { + continue; + } + + /* Clear HW registers for CPUs */ + memset(core->coreisr, 0, sizeof(core->coreisr)); + memset(core->sw_isr, 0, sizeof(core->sw_isr)); + } + + s->status = 0; +} + static int loongarch_extioi_common_pre_save(void *opaque) { LoongArchExtIOICommonState *s = (LoongArchExtIOICommonState *)opaque; @@ -180,9 +217,13 @@ static void loongarch_extioi_common_class_init(ObjectClass *klass, DeviceClass *dc = DEVICE_CLASS(klass); LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_parent_realize(dc, loongarch_extioi_common_realize, &lecc->parent_realize); + resettable_class_set_parent_phases(rc, NULL, + loongarch_extioi_common_reset_hold, + NULL, &lecc->parent_phases); device_class_set_props(dc, extioi_properties); dc->vmsd = &vmstate_loongarch_extioi; hc->plug = loongarch_extioi_cpu_plug; diff --git a/include/hw/intc/loongarch_extioi_common.h b/include/hw/intc/loongarch_extioi_common.h index 22d7880977..735bfee80a 100644 --- a/include/hw/intc/loongarch_extioi_common.h +++ b/include/hw/intc/loongarch_extioi_common.h @@ -94,6 +94,7 @@ struct LoongArchExtIOICommonClass { SysBusDeviceClass parent_class; DeviceRealize parent_realize; + ResettablePhases parent_phases; int (*pre_save)(void *s); int (*post_load)(void *s, int version_id); }; From bba709ff694cc6f844ca32d333b6be7adc7bd6b4 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 7 Mar 2025 10:23:21 +0800 Subject: [PATCH 0655/2760] hw/intc/loongarch_extioi: Replace legacy reset callback with new api Replace legacy reset callback register device_class_set_legacy_reset() with new function resettable_class_set_parent_phases(). With new API, it will call reset callback of parent object and then itself. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_extioi.c | 12 ++++++++---- include/hw/intc/loongarch_extioi.h | 1 + 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index f4fe961a98..7c38c4c9b7 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -377,11 +377,13 @@ static void loongarch_extioi_unrealize(DeviceState *dev) g_free(s->cpu); } -static void loongarch_extioi_reset(DeviceState *d) +static void loongarch_extioi_reset_hold(Object *obj, ResetType type) { - LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(d); + LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_GET_CLASS(obj); - s->status = 0; + if (lec->parent_phases.hold) { + lec->parent_phases.hold(obj, type); + } } static int vmstate_extioi_post_load(void *opaque, int version_id) @@ -406,12 +408,14 @@ static void loongarch_extioi_class_init(ObjectClass *klass, const void *data) DeviceClass *dc = DEVICE_CLASS(klass); LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_CLASS(klass); LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_parent_realize(dc, loongarch_extioi_realize, &lec->parent_realize); device_class_set_parent_unrealize(dc, loongarch_extioi_unrealize, &lec->parent_unrealize); - device_class_set_legacy_reset(dc, loongarch_extioi_reset); + resettable_class_set_parent_phases(rc, NULL, loongarch_extioi_reset_hold, + NULL, &lec->parent_phases); lecc->post_load = vmstate_extioi_post_load; } diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index 351f18afcf..4a6ae903e9 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -22,6 +22,7 @@ struct LoongArchExtIOIClass { DeviceRealize parent_realize; DeviceUnrealize parent_unrealize; + ResettablePhases parent_phases; }; #endif /* LOONGARCH_EXTIOI_H */ From 5101435e6d784c6d5bb267ca019b721a028dbc47 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 7 Mar 2025 11:57:31 +0800 Subject: [PATCH 0656/2760] hw/intc/loongarch_pch: Add reset support Add reset support with LoongArch pci irqchip, and register reset callback support with new API resettable_class_set_parent_phases(). Clear internal HW registers and SW state when virt machine resets. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_pic_common.c | 25 +++++++++++++++++++++++++ include/hw/intc/loongarch_pic_common.h | 1 + 2 files changed, 26 insertions(+) diff --git a/hw/intc/loongarch_pic_common.c b/hw/intc/loongarch_pic_common.c index fdb250c418..6dccacc741 100644 --- a/hw/intc/loongarch_pic_common.c +++ b/hw/intc/loongarch_pic_common.c @@ -44,6 +44,27 @@ static void loongarch_pic_common_realize(DeviceState *dev, Error **errp) } } +static void loongarch_pic_common_reset_hold(Object *obj, ResetType type) +{ + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(obj); + int i; + + s->int_mask = UINT64_MAX; + s->htmsi_en = 0x0; + s->intedge = 0x0; + s->intclr = 0x0; + s->auto_crtl0 = 0x0; + s->auto_crtl1 = 0x0; + for (i = 0; i < 64; i++) { + s->route_entry[i] = 0x1; + s->htmsi_vector[i] = 0x0; + } + s->intirr = 0x0; + s->intisr = 0x0; + s->last_intirr = 0x0; + s->int_polarity = 0x0; +} + static const Property loongarch_pic_common_properties[] = { DEFINE_PROP_UINT32("pch_pic_irq_num", LoongArchPICCommonState, irq_num, 0), }; @@ -76,9 +97,13 @@ static void loongarch_pic_common_class_init(ObjectClass *klass, { DeviceClass *dc = DEVICE_CLASS(klass); LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); device_class_set_parent_realize(dc, loongarch_pic_common_realize, &lpcc->parent_realize); + resettable_class_set_parent_phases(rc, NULL, + loongarch_pic_common_reset_hold, + NULL, &lpcc->parent_phases); device_class_set_props(dc, loongarch_pic_common_properties); dc->vmsd = &vmstate_loongarch_pic_common; } diff --git a/include/hw/intc/loongarch_pic_common.h b/include/hw/intc/loongarch_pic_common.h index 43cce48978..d301377cd7 100644 --- a/include/hw/intc/loongarch_pic_common.h +++ b/include/hw/intc/loongarch_pic_common.h @@ -76,6 +76,7 @@ struct LoongArchPICCommonClass { SysBusDeviceClass parent_class; DeviceRealize parent_realize; + ResettablePhases parent_phases; int (*pre_save)(LoongArchPICCommonState *s); int (*post_load)(LoongArchPICCommonState *s, int version_id); }; From a41a74ca5323f4d30ac7eb48ec1d54a09fae5baa Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 7 Mar 2025 14:20:13 +0800 Subject: [PATCH 0657/2760] hw/intc/loongarch_pch: Replace legacy reset callback with new api Replace legacy reset callback register device_class_set_legacy_reset() with new function resettable_class_set_parent_phases(). With new API, it will call reset callback of parent object. The internal state has been cleared in parent object LOONGARCH_PIC_COMMON, here parent_phases.hold() is directly called. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 26 ++++++++------------------ include/hw/intc/loongarch_pch_pic.h | 1 + 2 files changed, 9 insertions(+), 18 deletions(-) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 6c2b6de3f0..834096265a 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -354,25 +354,13 @@ static const MemoryRegionOps loongarch_pch_pic_reg8_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static void loongarch_pch_pic_reset(DeviceState *d) +static void loongarch_pic_reset_hold(Object *obj, ResetType type) { - LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(d); - int i; - - s->int_mask = -1; - s->htmsi_en = 0x0; - s->intedge = 0x0; - s->intclr = 0x0; - s->auto_crtl0 = 0x0; - s->auto_crtl1 = 0x0; - for (i = 0; i < 64; i++) { - s->route_entry[i] = 0x1; - s->htmsi_vector[i] = 0x0; + LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(obj); + + if (lpc->parent_phases.hold) { + lpc->parent_phases.hold(obj, type); } - s->intirr = 0x0; - s->intisr = 0x0; - s->last_intirr = 0x0; - s->int_polarity = 0x0; } static void loongarch_pic_realize(DeviceState *dev, Error **errp) @@ -408,8 +396,10 @@ static void loongarch_pic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); - device_class_set_legacy_reset(dc, loongarch_pch_pic_reset); + resettable_class_set_parent_phases(rc, NULL, loongarch_pic_reset_hold, + NULL, &lpc->parent_phases); device_class_set_parent_realize(dc, loongarch_pic_realize, &lpc->parent_realize); } diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h index 481cc58aed..839a59a43b 100644 --- a/include/hw/intc/loongarch_pch_pic.h +++ b/include/hw/intc/loongarch_pch_pic.h @@ -22,6 +22,7 @@ struct LoongarchPICClass { LoongArchPICCommonClass parent_class; DeviceRealize parent_realize; + ResettablePhases parent_phases; }; #endif /* HW_LOONGARCH_PCH_PIC_H */ From 73047c825e25a18127dddb89eff0c0bf97a26aed Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 25 Apr 2025 10:05:04 +0800 Subject: [PATCH 0658/2760] hw/loongarch/virt: Get physical entry address with elf file With load_elf() api, image load low address and high address is converted to physical address if parameter translate_fn is provided. However executing entry address is still virtual address. Here convert entry address into physical address, since MMU is disabled when system power on, the first PC instruction should be physical address. Signed-off-by: Bibo Mao Reviewed-by: Richard Henderson Tested-by: Song Gao --- hw/loongarch/boot.c | 1 + tests/tcg/loongarch64/system/kernel.ld | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 354cf458c8..0324d6adcb 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -245,6 +245,7 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) &kernel_entry, &kernel_low, &kernel_high, NULL, ELFDATA2LSB, EM_LOONGARCH, 1, 0); + kernel_entry = cpu_loongarch_virt_to_phys(NULL, kernel_entry); if (kernel_size < 0) { kernel_size = load_loongarch_linux_image(info->kernel_filename, &kernel_entry, &kernel_low, diff --git a/tests/tcg/loongarch64/system/kernel.ld b/tests/tcg/loongarch64/system/kernel.ld index f1a7c0168c..56d8588f1a 100644 --- a/tests/tcg/loongarch64/system/kernel.ld +++ b/tests/tcg/loongarch64/system/kernel.ld @@ -3,7 +3,7 @@ ENTRY(_start) SECTIONS { /* Linux kernel legacy start address. */ - . = 0x9000000000200000; + . = 0x200000; _text = .; .text : { *(.text) From d0897c6970b3717fe707c8d5a807fe2baf836ddd Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 28 Feb 2025 09:44:12 +0800 Subject: [PATCH 0659/2760] hw/loongarch/virt: Replace RSDT with XSDT table XSDT table is introduced in ACPI Specification 5.0, it supports 64-bit address in the table. There is LoongArch system support from ACPI Specification 6.4 and later, XSDT is supported by LoongArch system. Here replace RSDT with XSDT table. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/loongarch/virt-acpi-build.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/loongarch/virt-acpi-build.c b/hw/loongarch/virt-acpi-build.c index fced6c445a..073b6de75c 100644 --- a/hw/loongarch/virt-acpi-build.c +++ b/hw/loongarch/virt-acpi-build.c @@ -514,7 +514,7 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(machine); GArray *table_offsets; AcpiFadtData fadt_data; - unsigned facs, rsdt, dsdt; + unsigned facs, xsdt, dsdt; uint8_t *u; GArray *tables_blob = tables->table_data; @@ -600,17 +600,17 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) } /* RSDT is pointed to by RSDP */ - rsdt = tables_blob->len; - build_rsdt(tables_blob, tables->linker, table_offsets, + xsdt = tables_blob->len; + build_xsdt(tables_blob, tables->linker, table_offsets, lvms->oem_id, lvms->oem_table_id); /* RSDP is in FSEG memory, so allocate it separately */ { AcpiRsdpData rsdp_data = { - .revision = 0, + .revision = 2, .oem_id = lvms->oem_id, - .xsdt_tbl_offset = NULL, - .rsdt_tbl_offset = &rsdt, + .xsdt_tbl_offset = &xsdt, + .rsdt_tbl_offset = NULL, }; build_rsdp(tables->rsdp, tables->linker, &rsdp_data); } From 445c9c645befa759b95b21108447704ab328ae03 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 4 Mar 2025 14:59:17 +0800 Subject: [PATCH 0660/2760] hw/loongarch/virt: Allow user to customize OEM ID and OEM table ID On LoongArch virt machine, the default OEM ID and OEM table ID is "BOCHS " and "BXPC ". Here property x-oem-id and x-oem-table-id is added on virt machine to set customized OEM ID and OEM table ID. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/loongarch/virt.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 779544fada..7ad7fb68ff 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -773,6 +773,48 @@ static void virt_set_acpi(Object *obj, Visitor *v, const char *name, visit_type_OnOffAuto(v, name, &lvms->acpi, errp); } +static char *virt_get_oem_id(Object *obj, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + return g_strdup(lvms->oem_id); +} + +static void virt_set_oem_id(Object *obj, const char *value, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + size_t len = strlen(value); + + if (len > 6) { + error_setg(errp, + "User specified oem-id value is bigger than 6 bytes in size"); + return; + } + + strncpy(lvms->oem_id, value, 6); +} + +static char *virt_get_oem_table_id(Object *obj, Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + + return g_strdup(lvms->oem_table_id); +} + +static void virt_set_oem_table_id(Object *obj, const char *value, + Error **errp) +{ + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); + size_t len = strlen(value); + + if (len > 8) { + error_setg(errp, + "User specified oem-table-id value is bigger than 8 bytes in size"); + return; + } + strncpy(lvms->oem_table_id, value, 8); +} + static void virt_initfn(Object *obj) { LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(obj); @@ -1177,6 +1219,22 @@ static void virt_class_init(ObjectClass *oc, const void *data) #ifdef CONFIG_TPM machine_class_allow_dynamic_sysbus_dev(mc, TYPE_TPM_TIS_SYSBUS); #endif + object_class_property_add_str(oc, "x-oem-id", + virt_get_oem_id, + virt_set_oem_id); + object_class_property_set_description(oc, "x-oem-id", + "Override the default value of field OEMID " + "in ACPI table header." + "The string may be up to 6 bytes in size"); + + + object_class_property_add_str(oc, "x-oem-table-id", + virt_get_oem_table_id, + virt_set_oem_table_id); + object_class_property_set_description(oc, "x-oem-table-id", + "Override the default value of field OEM Table ID " + "in ACPI table header." + "The string may be up to 8 bytes in size"); } static const TypeInfo virt_machine_types[] = { From bb5101aadc1675790983c7911092dd9abeec4651 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Thu, 27 Mar 2025 10:58:43 +0800 Subject: [PATCH 0661/2760] ui/dmabuf: extend QemuDmaBuf to support multi-plane MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit mesa/radeonsi is going to support explicit modifier which may export a multi-plane texture. For example, texture with DCC enabled (a compressed format) has two planes, one with compressed data, the other with meta data for compression. v2: * change API qemu_dmabuf_get_fd/offset/stride to qemu_dmabuf_get_fds/offsets/strides. * change API qemu_dmabuf_dup_fd to qemu_dmabuf_dup_fds. * add an extra arg to these API for the length of the array. Reviewed-by: Marc-André Lureau Signed-off-by: Qiang Yu [ Fix style ] Signed-off-by: Marc-André Lureau Message-ID: <20250327025848.46962-2-yuq825@gmail.com> --- hw/display/vhost-user-gpu.c | 6 ++- hw/display/virtio-gpu-udmabuf.c | 6 ++- hw/vfio/display.c | 7 +-- include/ui/dmabuf.h | 20 +++++---- ui/dbus-listener.c | 10 ++--- ui/dmabuf.c | 80 +++++++++++++++++++++++---------- ui/egl-helpers.c | 4 +- ui/spice-display.c | 4 +- 8 files changed, 89 insertions(+), 48 deletions(-) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 06c4e7e190..a367daac82 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -249,6 +249,8 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) case VHOST_USER_GPU_DMABUF_SCANOUT: { VhostUserGpuDMABUFScanout *m = &msg->payload.dmabuf_scanout; int fd = qemu_chr_fe_get_msgfd(&g->vhost_chr); + uint32_t offset = 0; + uint32_t stride = m->fd_stride; uint64_t modifier = 0; QemuDmaBuf *dmabuf; @@ -282,10 +284,10 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) } dmabuf = qemu_dmabuf_new(m->width, m->height, - m->fd_stride, 0, 0, + &offset, &stride, 0, 0, m->fd_width, m->fd_height, m->fd_drm_fourcc, modifier, - fd, false, m->fd_flags & + &fd, 1, false, m->fd_flags & VIRTIO_GPU_RESOURCE_FLAG_Y_0_TOP); dpy_gl_scanout_dmabuf(con, dmabuf); diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index 0510577475..01b0024ead 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -176,16 +176,18 @@ static VGPUDMABuf struct virtio_gpu_rect *r) { VGPUDMABuf *dmabuf; + uint32_t offset = 0; if (res->dmabuf_fd < 0) { return NULL; } dmabuf = g_new0(VGPUDMABuf, 1); - dmabuf->buf = qemu_dmabuf_new(r->width, r->height, fb->stride, + dmabuf->buf = qemu_dmabuf_new(r->width, r->height, + &offset, &fb->stride, r->x, r->y, fb->width, fb->height, qemu_pixman_to_drm_format(fb->format), - 0, res->dmabuf_fd, true, false); + 0, &res->dmabuf_fd, 1, true, false); dmabuf->scanout_id = scanout_id; QTAILQ_INSERT_HEAD(&g->dmabuf.bufs, dmabuf, next); diff --git a/hw/vfio/display.c b/hw/vfio/display.c index f3e6581f15..9c6f5aa265 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -213,6 +213,7 @@ static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, struct vfio_device_gfx_plane_info plane; VFIODMABuf *dmabuf; int fd, ret; + uint32_t offset = 0; memset(&plane, 0, sizeof(plane)); plane.argsz = sizeof(plane); @@ -245,10 +246,10 @@ static VFIODMABuf *vfio_display_get_dmabuf(VFIOPCIDevice *vdev, dmabuf = g_new0(VFIODMABuf, 1); dmabuf->dmabuf_id = plane.dmabuf_id; - dmabuf->buf = qemu_dmabuf_new(plane.width, plane.height, - plane.stride, 0, 0, plane.width, + dmabuf->buf = qemu_dmabuf_new(plane.width, plane.height, &offset, + &plane.stride, 0, 0, plane.width, plane.height, plane.drm_format, - plane.drm_format_mod, fd, false, false); + plane.drm_format_mod, &fd, 1, false, false); if (plane_type == DRM_PLANE_TYPE_CURSOR) { vfio_display_update_cursor(dmabuf, &plane); diff --git a/include/ui/dmabuf.h b/include/ui/dmabuf.h index dc74ba895a..3decdca497 100644 --- a/include/ui/dmabuf.h +++ b/include/ui/dmabuf.h @@ -10,24 +10,29 @@ #ifndef DMABUF_H #define DMABUF_H +#define DMABUF_MAX_PLANES 4 + typedef struct QemuDmaBuf QemuDmaBuf; QemuDmaBuf *qemu_dmabuf_new(uint32_t width, uint32_t height, - uint32_t stride, uint32_t x, - uint32_t y, uint32_t backing_width, - uint32_t backing_height, uint32_t fourcc, - uint64_t modifier, int dmabuf_fd, + const uint32_t *offset, const uint32_t *stride, + uint32_t x, uint32_t y, + uint32_t backing_width, uint32_t backing_height, + uint32_t fourcc, uint64_t modifier, + const int32_t *dmabuf_fd, uint32_t num_planes, bool allow_fences, bool y0_top); void qemu_dmabuf_free(QemuDmaBuf *dmabuf); G_DEFINE_AUTOPTR_CLEANUP_FUNC(QemuDmaBuf, qemu_dmabuf_free); -int qemu_dmabuf_get_fd(QemuDmaBuf *dmabuf); -int qemu_dmabuf_dup_fd(QemuDmaBuf *dmabuf); +const int *qemu_dmabuf_get_fds(QemuDmaBuf *dmabuf, int *nfds); +void qemu_dmabuf_dup_fds(QemuDmaBuf *dmabuf, int *fds, int nfds); void qemu_dmabuf_close(QemuDmaBuf *dmabuf); uint32_t qemu_dmabuf_get_width(QemuDmaBuf *dmabuf); uint32_t qemu_dmabuf_get_height(QemuDmaBuf *dmabuf); -uint32_t qemu_dmabuf_get_stride(QemuDmaBuf *dmabuf); +const uint32_t *qemu_dmabuf_get_offsets(QemuDmaBuf *dmabuf, int *noffsets); +const uint32_t *qemu_dmabuf_get_strides(QemuDmaBuf *dmabuf, int *nstrides); +uint32_t qemu_dmabuf_get_num_planes(QemuDmaBuf *dmabuf); uint32_t qemu_dmabuf_get_fourcc(QemuDmaBuf *dmabuf); uint64_t qemu_dmabuf_get_modifier(QemuDmaBuf *dmabuf); uint32_t qemu_dmabuf_get_texture(QemuDmaBuf *dmabuf); @@ -44,6 +49,5 @@ void qemu_dmabuf_set_texture(QemuDmaBuf *dmabuf, uint32_t texture); void qemu_dmabuf_set_fence_fd(QemuDmaBuf *dmabuf, int32_t fence_fd); void qemu_dmabuf_set_sync(QemuDmaBuf *dmabuf, void *sync); void qemu_dmabuf_set_draw_submitted(QemuDmaBuf *dmabuf, bool draw_submitted); -void qemu_dmabuf_set_fd(QemuDmaBuf *dmabuf, int32_t fd); #endif diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 51244c9240..65373d519c 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -299,7 +299,7 @@ static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, uint64_t modifier; bool y0_top; - fd = qemu_dmabuf_get_fd(dmabuf); + fd = qemu_dmabuf_get_fds(dmabuf, NULL)[0]; fd_list = g_unix_fd_list_new(); if (g_unix_fd_list_append(fd_list, fd, &err) != 0) { error_report("Failed to setup dmabuf fdlist: %s", err->message); @@ -310,7 +310,7 @@ static void dbus_scanout_dmabuf(DisplayChangeListener *dcl, width = qemu_dmabuf_get_width(dmabuf); height = qemu_dmabuf_get_height(dmabuf); - stride = qemu_dmabuf_get_stride(dmabuf); + stride = qemu_dmabuf_get_strides(dmabuf, NULL)[0]; fourcc = qemu_dmabuf_get_fourcc(dmabuf); modifier = qemu_dmabuf_get_modifier(dmabuf); y0_top = qemu_dmabuf_get_y0_top(dmabuf); @@ -505,7 +505,7 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl, #ifdef CONFIG_GBM g_autoptr(QemuDmaBuf) dmabuf = NULL; int fd; - uint32_t stride, fourcc; + uint32_t offset = 0, stride, fourcc; uint64_t modifier; assert(tex_id); @@ -515,8 +515,8 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl, error_report("%s: failed to get fd for texture", __func__); return; } - dmabuf = qemu_dmabuf_new(w, h, stride, x, y, backing_width, - backing_height, fourcc, modifier, fd, + dmabuf = qemu_dmabuf_new(w, h, &offset, &stride, x, y, backing_width, + backing_height, fourcc, modifier, &fd, 1, false, backing_y_0_top); dbus_scanout_dmabuf(dcl, dmabuf); diff --git a/ui/dmabuf.c b/ui/dmabuf.c index df7a09703f..7433a268f0 100644 --- a/ui/dmabuf.c +++ b/ui/dmabuf.c @@ -11,10 +11,12 @@ #include "ui/dmabuf.h" struct QemuDmaBuf { - int fd; + int fd[DMABUF_MAX_PLANES]; uint32_t width; uint32_t height; - uint32_t stride; + uint32_t offset[DMABUF_MAX_PLANES]; + uint32_t stride[DMABUF_MAX_PLANES]; + uint32_t num_planes; uint32_t fourcc; uint64_t modifier; uint32_t texture; @@ -30,28 +32,33 @@ struct QemuDmaBuf { }; QemuDmaBuf *qemu_dmabuf_new(uint32_t width, uint32_t height, - uint32_t stride, uint32_t x, - uint32_t y, uint32_t backing_width, - uint32_t backing_height, uint32_t fourcc, - uint64_t modifier, int32_t dmabuf_fd, + const uint32_t *offset, const uint32_t *stride, + uint32_t x, uint32_t y, + uint32_t backing_width, uint32_t backing_height, + uint32_t fourcc, uint64_t modifier, + const int32_t *dmabuf_fd, uint32_t num_planes, bool allow_fences, bool y0_top) { QemuDmaBuf *dmabuf; + assert(num_planes > 0 && num_planes <= DMABUF_MAX_PLANES); + dmabuf = g_new0(QemuDmaBuf, 1); dmabuf->width = width; dmabuf->height = height; - dmabuf->stride = stride; + memcpy(dmabuf->offset, offset, num_planes * sizeof(*offset)); + memcpy(dmabuf->stride, stride, num_planes * sizeof(*stride)); dmabuf->x = x; dmabuf->y = y; dmabuf->backing_width = backing_width; dmabuf->backing_height = backing_height; dmabuf->fourcc = fourcc; dmabuf->modifier = modifier; - dmabuf->fd = dmabuf_fd; + memcpy(dmabuf->fd, dmabuf_fd, num_planes * sizeof(*dmabuf_fd)); dmabuf->allow_fences = allow_fences; dmabuf->y0_top = y0_top; dmabuf->fence_fd = -1; + dmabuf->num_planes = num_planes; return dmabuf; } @@ -65,31 +72,40 @@ void qemu_dmabuf_free(QemuDmaBuf *dmabuf) g_free(dmabuf); } -int qemu_dmabuf_get_fd(QemuDmaBuf *dmabuf) +const int *qemu_dmabuf_get_fds(QemuDmaBuf *dmabuf, int *nfds) { assert(dmabuf != NULL); + if (nfds) { + *nfds = ARRAY_SIZE(dmabuf->fd); + } + return dmabuf->fd; } -int qemu_dmabuf_dup_fd(QemuDmaBuf *dmabuf) +void qemu_dmabuf_dup_fds(QemuDmaBuf *dmabuf, int *fds, int nfds) { + int i; + assert(dmabuf != NULL); + assert(nfds >= dmabuf->num_planes); - if (dmabuf->fd >= 0) { - return dup(dmabuf->fd); - } else { - return -1; + for (i = 0; i < dmabuf->num_planes; i++) { + fds[i] = dmabuf->fd[i] >= 0 ? dup(dmabuf->fd[i]) : -1; } } void qemu_dmabuf_close(QemuDmaBuf *dmabuf) { + int i; + assert(dmabuf != NULL); - if (dmabuf->fd >= 0) { - close(dmabuf->fd); - dmabuf->fd = -1; + for (i = 0; i < dmabuf->num_planes; i++) { + if (dmabuf->fd[i] >= 0) { + close(dmabuf->fd[i]); + dmabuf->fd[i] = -1; + } } } @@ -107,13 +123,35 @@ uint32_t qemu_dmabuf_get_height(QemuDmaBuf *dmabuf) return dmabuf->height; } -uint32_t qemu_dmabuf_get_stride(QemuDmaBuf *dmabuf) +const uint32_t *qemu_dmabuf_get_offsets(QemuDmaBuf *dmabuf, int *noffsets) +{ + assert(dmabuf != NULL); + + if (noffsets) { + *noffsets = ARRAY_SIZE(dmabuf->offset); + } + + return dmabuf->offset; +} + +const uint32_t *qemu_dmabuf_get_strides(QemuDmaBuf *dmabuf, int *nstrides) { assert(dmabuf != NULL); + if (nstrides) { + *nstrides = ARRAY_SIZE(dmabuf->stride); + } + return dmabuf->stride; } +uint32_t qemu_dmabuf_get_num_planes(QemuDmaBuf *dmabuf) +{ + assert(dmabuf != NULL); + + return dmabuf->num_planes; +} + uint32_t qemu_dmabuf_get_fourcc(QemuDmaBuf *dmabuf) { assert(dmabuf != NULL); @@ -221,9 +259,3 @@ void qemu_dmabuf_set_draw_submitted(QemuDmaBuf *dmabuf, bool draw_submitted) assert(dmabuf != NULL); dmabuf->draw_submitted = draw_submitted; } - -void qemu_dmabuf_set_fd(QemuDmaBuf *dmabuf, int32_t fd) -{ - assert(dmabuf != NULL); - dmabuf->fd = fd; -} diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index d591159480..d194d004b7 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -323,9 +323,9 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) attrs[i++] = qemu_dmabuf_get_fourcc(dmabuf); attrs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT; - attrs[i++] = qemu_dmabuf_get_fd(dmabuf); + attrs[i++] = qemu_dmabuf_get_fds(dmabuf, NULL)[0]; attrs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; - attrs[i++] = qemu_dmabuf_get_stride(dmabuf); + attrs[i++] = qemu_dmabuf_get_strides(dmabuf, NULL)[0]; attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; attrs[i++] = 0; #ifdef EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT diff --git a/ui/spice-display.c b/ui/spice-display.c index c794ae0649..40547edb5e 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -1075,10 +1075,10 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, stride, fourcc, false); } } else { - stride = qemu_dmabuf_get_stride(dmabuf); + stride = qemu_dmabuf_get_strides(dmabuf, NULL)[0]; fourcc = qemu_dmabuf_get_fourcc(dmabuf); y_0_top = qemu_dmabuf_get_y0_top(dmabuf); - fd = qemu_dmabuf_dup_fd(dmabuf); + qemu_dmabuf_dup_fds(dmabuf, &fd, 1); trace_qemu_spice_gl_forward_dmabuf(ssd->qxl.id, width, height); /* note: spice server will close the fd, so hand over a dup */ From 806c861dc6b92019765aed09a016db460cd345fb Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Thu, 27 Mar 2025 10:58:44 +0800 Subject: [PATCH 0662/2760] ui/egl: require EGL_EXT_image_dma_buf_import_modifiers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's used already, just check it explicitly. Reviewed-by: Marc-André Lureau Signed-off-by: Qiang Yu Message-ID: <20250327025848.46962-3-yuq825@gmail.com> --- ui/egl-helpers.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index d194d004b7..432863d702 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -257,6 +257,11 @@ int egl_rendernode_init(const char *rendernode, DisplayGLMode mode) error_report("egl: EGL_MESA_image_dma_buf_export not supported"); goto err; } + if (!epoxy_has_egl_extension(qemu_egl_display, + "EGL_EXT_image_dma_buf_import_modifiers")) { + error_report("egl: EGL_EXT_image_dma_buf_import_modifiers not supported"); + goto err; + } qemu_egl_rn_ctx = qemu_egl_init_ctx(); if (!qemu_egl_rn_ctx) { @@ -308,7 +313,7 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) EGLImageKHR image = EGL_NO_IMAGE_KHR; EGLint attrs[64]; int i = 0; - uint64_t modifier; + uint64_t modifier = qemu_dmabuf_get_modifier(dmabuf); uint32_t texture = qemu_dmabuf_get_texture(dmabuf); if (texture != 0) { @@ -328,15 +333,12 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) attrs[i++] = qemu_dmabuf_get_strides(dmabuf, NULL)[0]; attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; attrs[i++] = 0; -#ifdef EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT - modifier = qemu_dmabuf_get_modifier(dmabuf); if (modifier) { attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; attrs[i++] = (modifier >> 0) & 0xffffffff; attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; attrs[i++] = (modifier >> 32) & 0xffffffff; } -#endif attrs[i++] = EGL_NONE; image = eglCreateImageKHR(qemu_egl_display, From ac70568902c3fb77516d93b169cddd0bcaabfb4e Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Thu, 27 Mar 2025 10:58:45 +0800 Subject: [PATCH 0663/2760] ui/egl: use DRM_FORMAT_MOD_INVALID as default modifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 0 is used as DRM_FORMAT_MOD_LINEAR already. Reviewed-by: Marc-André Lureau Signed-off-by: Qiang Yu Message-ID: <20250327025848.46962-4-yuq825@gmail.com> --- hw/display/vhost-user-gpu.c | 3 ++- hw/display/virtio-gpu-udmabuf.c | 4 +++- ui/egl-helpers.c | 3 ++- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index a367daac82..43d4c08a2e 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -18,6 +18,7 @@ #include "chardev/char-fe.h" #include "qapi/error.h" #include "migration/blocker.h" +#include "standard-headers/drm/drm_fourcc.h" typedef enum VhostUserGpuRequest { VHOST_USER_GPU_NONE = 0, @@ -251,7 +252,7 @@ vhost_user_gpu_handle_display(VhostUserGPU *g, VhostUserGpuMsg *msg) int fd = qemu_chr_fe_get_msgfd(&g->vhost_chr); uint32_t offset = 0; uint32_t stride = m->fd_stride; - uint64_t modifier = 0; + uint64_t modifier = DRM_FORMAT_MOD_INVALID; QemuDmaBuf *dmabuf; if (m->scanout_id >= g->parent_obj.conf.max_outputs) { diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index 01b0024ead..d804f321aa 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -25,6 +25,7 @@ #include #include "qemu/memfd.h" #include "standard-headers/linux/udmabuf.h" +#include "standard-headers/drm/drm_fourcc.h" static void virtio_gpu_create_udmabuf(struct virtio_gpu_simple_resource *res) { @@ -187,7 +188,8 @@ static VGPUDMABuf &offset, &fb->stride, r->x, r->y, fb->width, fb->height, qemu_pixman_to_drm_format(fb->format), - 0, &res->dmabuf_fd, 1, true, false); + DRM_FORMAT_MOD_INVALID, &res->dmabuf_fd, + 1, true, false); dmabuf->scanout_id = scanout_id; QTAILQ_INSERT_HEAD(&g->dmabuf.bufs, dmabuf, next); diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 432863d702..8c0e394d2b 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -23,6 +23,7 @@ #include "system/system.h" #include "qapi/error.h" #include "trace.h" +#include "standard-headers/drm/drm_fourcc.h" EGLDisplay *qemu_egl_display; EGLConfig qemu_egl_config; @@ -333,7 +334,7 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) attrs[i++] = qemu_dmabuf_get_strides(dmabuf, NULL)[0]; attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; attrs[i++] = 0; - if (modifier) { + if (modifier != DRM_FORMAT_MOD_INVALID) { attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; attrs[i++] = (modifier >> 0) & 0xffffffff; attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; From 0e15d0b92700000db66e19c68ad2d50aace860d8 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Thu, 27 Mar 2025 10:58:46 +0800 Subject: [PATCH 0664/2760] ui/egl: support multi-plane dmabuf when egl export/import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v2: * use new dmabuf API and check length Reviewed-by: Marc-André Lureau Signed-off-by: Qiang Yu [ Fix style ] Signed-off-by: Marc-André Lureau Message-ID: <20250327025848.46962-5-yuq825@gmail.com> --- include/ui/egl-helpers.h | 5 ++- ui/dbus-listener.c | 19 +++++---- ui/egl-helpers.c | 92 ++++++++++++++++++++++++++++++---------- ui/spice-display.c | 60 +++++++++++++++++--------- 4 files changed, 124 insertions(+), 52 deletions(-) diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h index 4b8c0d2281..fb80e15142 100644 --- a/include/ui/egl-helpers.h +++ b/include/ui/egl-helpers.h @@ -46,8 +46,9 @@ extern int qemu_egl_rn_fd; extern struct gbm_device *qemu_egl_rn_gbm_dev; int egl_rendernode_init(const char *rendernode, DisplayGLMode mode); -int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc, - EGLuint64KHR *modifier); +bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset, + EGLint *stride, EGLint *fourcc, int *num_planes, + EGLuint64KHR *modifier); void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf); void egl_dmabuf_release_texture(QemuDmaBuf *dmabuf); diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 65373d519c..90147972cd 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -504,19 +504,22 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl, backing_width, backing_height, x, y, w, h); #ifdef CONFIG_GBM g_autoptr(QemuDmaBuf) dmabuf = NULL; - int fd; - uint32_t offset = 0, stride, fourcc; + int fd[DMABUF_MAX_PLANES], num_planes; + uint32_t offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc; uint64_t modifier; assert(tex_id); - fd = egl_get_fd_for_texture(tex_id, (EGLint *)&stride, (EGLint *)&fourcc, - &modifier); - if (fd < 0) { - error_report("%s: failed to get fd for texture", __func__); + if (!egl_dmabuf_export_texture(tex_id, fd, (EGLint *)offset, (EGLint *)stride, + (EGLint *)&fourcc, &num_planes, &modifier)) { + error_report("%s: failed to export dmabuf for texture", __func__); + return; + } + if (num_planes > 1) { + error_report("%s: does not support multi-plane dmabuf", __func__); return; } - dmabuf = qemu_dmabuf_new(w, h, &offset, &stride, x, y, backing_width, - backing_height, fourcc, modifier, &fd, 1, + dmabuf = qemu_dmabuf_new(w, h, offset, stride, x, y, backing_width, + backing_height, fourcc, modifier, fd, num_planes, false, backing_y_0_top); dbus_scanout_dmabuf(dcl, dmabuf); diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 8c0e394d2b..9cda2bbbee 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -283,44 +283,86 @@ int egl_rendernode_init(const char *rendernode, DisplayGLMode mode) return -1; } -int egl_get_fd_for_texture(uint32_t tex_id, EGLint *stride, EGLint *fourcc, - EGLuint64KHR *modifier) +bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset, + EGLint *stride, EGLint *fourcc, int *num_planes, + EGLuint64KHR *modifier) { EGLImageKHR image; - EGLint num_planes, fd; + EGLuint64KHR modifiers[DMABUF_MAX_PLANES]; image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(unsigned long)tex_id, NULL); if (!image) { - return -1; + return false; } eglExportDMABUFImageQueryMESA(qemu_egl_display, image, fourcc, - &num_planes, modifier); - if (num_planes != 1) { - eglDestroyImageKHR(qemu_egl_display, image); - return -1; - } - eglExportDMABUFImageMESA(qemu_egl_display, image, &fd, stride, NULL); + num_planes, modifiers); + eglExportDMABUFImageMESA(qemu_egl_display, image, fd, stride, offset); eglDestroyImageKHR(qemu_egl_display, image); - return fd; + /* Only first modifier matters. */ + if (modifier) { + *modifier = modifiers[0]; + } + + return true; } void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) { EGLImageKHR image = EGL_NO_IMAGE_KHR; EGLint attrs[64]; - int i = 0; + int i = 0, j; uint64_t modifier = qemu_dmabuf_get_modifier(dmabuf); uint32_t texture = qemu_dmabuf_get_texture(dmabuf); + int nfds, noffsets, nstrides; + const int *fds = qemu_dmabuf_get_fds(dmabuf, &nfds); + const uint32_t *offsets = qemu_dmabuf_get_offsets(dmabuf, &noffsets); + const uint32_t *strides = qemu_dmabuf_get_strides(dmabuf, &nstrides); + uint32_t num_planes = qemu_dmabuf_get_num_planes(dmabuf); + + EGLint fd_attrs[] = { + EGL_DMA_BUF_PLANE0_FD_EXT, + EGL_DMA_BUF_PLANE1_FD_EXT, + EGL_DMA_BUF_PLANE2_FD_EXT, + EGL_DMA_BUF_PLANE3_FD_EXT, + }; + EGLint offset_attrs[] = { + EGL_DMA_BUF_PLANE0_OFFSET_EXT, + EGL_DMA_BUF_PLANE1_OFFSET_EXT, + EGL_DMA_BUF_PLANE2_OFFSET_EXT, + EGL_DMA_BUF_PLANE3_OFFSET_EXT, + }; + EGLint stride_attrs[] = { + EGL_DMA_BUF_PLANE0_PITCH_EXT, + EGL_DMA_BUF_PLANE1_PITCH_EXT, + EGL_DMA_BUF_PLANE2_PITCH_EXT, + EGL_DMA_BUF_PLANE3_PITCH_EXT, + }; + EGLint modifier_lo_attrs[] = { + EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT, + }; + EGLint modifier_hi_attrs[] = { + EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, + EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, + EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, + EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT, + }; if (texture != 0) { return; } + assert(nfds >= num_planes); + assert(noffsets >= num_planes); + assert(nstrides >= num_planes); + attrs[i++] = EGL_WIDTH; attrs[i++] = qemu_dmabuf_get_backing_width(dmabuf); attrs[i++] = EGL_HEIGHT; @@ -328,18 +370,22 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT; attrs[i++] = qemu_dmabuf_get_fourcc(dmabuf); - attrs[i++] = EGL_DMA_BUF_PLANE0_FD_EXT; - attrs[i++] = qemu_dmabuf_get_fds(dmabuf, NULL)[0]; - attrs[i++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; - attrs[i++] = qemu_dmabuf_get_strides(dmabuf, NULL)[0]; - attrs[i++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; - attrs[i++] = 0; - if (modifier != DRM_FORMAT_MOD_INVALID) { - attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; - attrs[i++] = (modifier >> 0) & 0xffffffff; - attrs[i++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; - attrs[i++] = (modifier >> 32) & 0xffffffff; + for (j = 0; j < num_planes; j++) { + attrs[i++] = fd_attrs[j]; + /* fd[1-3] may be -1 if using a joint buffer for all planes */ + attrs[i++] = fds[j] >= 0 ? fds[j] : fds[0]; + attrs[i++] = stride_attrs[j]; + attrs[i++] = strides[j]; + attrs[i++] = offset_attrs[j]; + attrs[i++] = offsets[j]; + if (modifier != DRM_FORMAT_MOD_INVALID) { + attrs[i++] = modifier_lo_attrs[j]; + attrs[i++] = (modifier >> 0) & 0xffffffff; + attrs[i++] = modifier_hi_attrs[j]; + attrs[i++] = (modifier >> 32) & 0xffffffff; + } } + attrs[i++] = EGL_NONE; image = eglCreateImageKHR(qemu_egl_display, diff --git a/ui/spice-display.c b/ui/spice-display.c index 40547edb5e..a9fee87a72 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -876,19 +876,24 @@ static void spice_gl_switch(DisplayChangeListener *dcl, struct DisplaySurface *new_surface) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); - EGLint stride, fourcc; - int fd; if (ssd->ds) { surface_gl_destroy_texture(ssd->gls, ssd->ds); } ssd->ds = new_surface; if (ssd->ds) { + uint32_t offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES]; + int fd[DMABUF_MAX_PLANES], num_planes, fourcc; + surface_gl_create_texture(ssd->gls, ssd->ds); - fd = egl_get_fd_for_texture(ssd->ds->texture, - &stride, &fourcc, - NULL); - if (fd < 0) { + if (!egl_dmabuf_export_texture(ssd->ds->texture, fd, (EGLint *)offset, + (EGLint *)stride, &fourcc, &num_planes, NULL)) { + surface_gl_destroy_texture(ssd->gls, ssd->ds); + return; + } + + if (num_planes > 1) { + fprintf(stderr, "%s: does not support multi-plane texture\n", __func__); surface_gl_destroy_texture(ssd->gls, ssd->ds); return; } @@ -899,10 +904,10 @@ static void spice_gl_switch(DisplayChangeListener *dcl, fourcc); /* note: spice server will close the fd */ - spice_qxl_gl_scanout(&ssd->qxl, fd, + spice_qxl_gl_scanout(&ssd->qxl, fd[0], surface_width(ssd->ds), surface_height(ssd->ds), - stride, fourcc, false); + stride[0], fourcc, false); ssd->have_surface = true; ssd->have_scanout = false; @@ -941,20 +946,24 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, void *d3d_tex2d) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); - EGLint stride = 0, fourcc = 0; - int fd = -1; + EGLint offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc = 0; + int fd[DMABUF_MAX_PLANES], num_planes; assert(tex_id); - fd = egl_get_fd_for_texture(tex_id, &stride, &fourcc, NULL); - if (fd < 0) { - fprintf(stderr, "%s: failed to get fd for texture\n", __func__); + if (!egl_dmabuf_export_texture(tex_id, fd, offset, stride, &fourcc, + &num_planes, NULL)) { + fprintf(stderr, "%s: failed to export dmabuf for texture\n", __func__); + return; + } + if (num_planes > 1) { + fprintf(stderr, "%s: does not support multi-plane dmabuf\n", __func__); return; } trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc); /* note: spice server will close the fd */ - spice_qxl_gl_scanout(&ssd->qxl, fd, backing_width, backing_height, - stride, fourcc, y_0_top); + spice_qxl_gl_scanout(&ssd->qxl, fd[0], backing_width, backing_height, + stride[0], fourcc, y_0_top); qemu_spice_gl_monitor_config(ssd, x, y, w, h); ssd->have_surface = false; ssd->have_scanout = true; @@ -1064,15 +1073,28 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, /* dest framebuffer */ if (ssd->blit_fb.width != width || ssd->blit_fb.height != height) { + int fds[DMABUF_MAX_PLANES], num_planes; + uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES]; + trace_qemu_spice_gl_render_dmabuf(ssd->qxl.id, width, height); egl_fb_destroy(&ssd->blit_fb); egl_fb_setup_new_tex(&ssd->blit_fb, width, height); - fd = egl_get_fd_for_texture(ssd->blit_fb.texture, - &stride, &fourcc, NULL); - spice_qxl_gl_scanout(&ssd->qxl, fd, width, height, - stride, fourcc, false); + if (!egl_dmabuf_export_texture(ssd->blit_fb.texture, fds, + (EGLint *)offsets, (EGLint *)strides, + &fourcc, &num_planes, NULL)) { + fprintf(stderr, + "%s: failed to export dmabuf for texture\n", __func__); + return; + } + if (num_planes > 1) { + fprintf(stderr, + "%s: does not support multi-plane dmabuf\n", __func__); + return; + } + spice_qxl_gl_scanout(&ssd->qxl, fds[0], width, height, + strides[0], fourcc, false); } } else { stride = qemu_dmabuf_get_strides(dmabuf, NULL)[0]; From 10aaad0edc7ff827f91583cd537b6f11b77e6e79 Mon Sep 17 00:00:00 2001 From: Qiang Yu Date: Thu, 27 Mar 2025 10:58:47 +0800 Subject: [PATCH 0665/2760] ui/dbus: change dbus ScanoutDMABUF interface MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To handle multi plane. v3: * rename interface * add x/y/backing_width/backing_height arg v2: * use new dmabuf API and check length Reviewed-by: Marc-André Lureau Signed-off-by: Qiang Yu Message-ID: <20250327025848.46962-6-yuq825@gmail.com> [ Fix style ] Signed-off-by: Marc-André Lureau --- ui/dbus-display1.xml | 45 +++++++++++++++++ ui/dbus-listener.c | 114 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 152 insertions(+), 7 deletions(-) diff --git a/ui/dbus-display1.xml b/ui/dbus-display1.xml index 72deefa455..4a41a7e0f3 100644 --- a/ui/dbus-display1.xml +++ b/ui/dbus-display1.xml @@ -614,6 +614,51 @@ + + + + + + + + + + + + + + + + + + + + + + loongarch_pch_pic_ops loongarch_pch_pic_low_readw --> loongarch_pch_pic_read loongarch_pch_pic_low_writew --> loongarch_pch_pic_write Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20250507023754.1877445-3-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 26 +++++++------------------- include/hw/intc/loongarch_pic_common.h | 2 +- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index c06ef0df3f..076b984d93 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -230,18 +230,6 @@ static void loongarch_pch_pic_write(void *opaque, hwaddr addr, } } -static uint64_t loongarch_pch_pic_low_readw(void *opaque, hwaddr addr, - unsigned size) -{ - return loongarch_pch_pic_read(opaque, addr, size); -} - -static void loongarch_pch_pic_low_writew(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - loongarch_pch_pic_write(opaque, addr, value, size); -} - static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr, unsigned size) { @@ -270,9 +258,9 @@ static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr, loongarch_pch_pic_write(opaque, addr, data, size); } -static const MemoryRegionOps loongarch_pch_pic_reg32_low_ops = { - .read = loongarch_pch_pic_low_readw, - .write = loongarch_pch_pic_low_writew, +static const MemoryRegionOps loongarch_pch_pic_ops = { + .read = loongarch_pch_pic_read, + .write = loongarch_pch_pic_write, .valid = { .min_access_size = 4, .max_access_size = 8, @@ -336,15 +324,15 @@ static void loongarch_pic_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, s->parent_irq, s->irq_num); qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num); - memory_region_init_io(&s->iomem32_low, OBJECT(dev), - &loongarch_pch_pic_reg32_low_ops, - s, PCH_PIC_NAME(.reg32_part1), 0x100); + memory_region_init_io(&s->iomem, OBJECT(dev), + &loongarch_pch_pic_ops, + s, TYPE_LOONGARCH_PIC, 0x100); memory_region_init_io(&s->iomem8, OBJECT(dev), &loongarch_pch_pic_reg8_ops, s, PCH_PIC_NAME(.reg8), 0x2a0); memory_region_init_io(&s->iomem32_high, OBJECT(dev), &loongarch_pch_pic_reg32_high_ops, s, PCH_PIC_NAME(.reg32_part2), 0xc60); - sysbus_init_mmio(sbd, &s->iomem32_low); + sysbus_init_mmio(sbd, &s->iomem); sysbus_init_mmio(sbd, &s->iomem8); sysbus_init_mmio(sbd, &s->iomem32_high); diff --git a/include/hw/intc/loongarch_pic_common.h b/include/hw/intc/loongarch_pic_common.h index 7a9a2bdd46..dc03056227 100644 --- a/include/hw/intc/loongarch_pic_common.h +++ b/include/hw/intc/loongarch_pic_common.h @@ -65,7 +65,7 @@ struct LoongArchPICCommonState { uint8_t route_entry[64]; /* 0x100 - 0x138 */ uint8_t htmsi_vector[64]; /* 0x200 - 0x238 */ - MemoryRegion iomem32_low; + MemoryRegion iomem; MemoryRegion iomem32_high; MemoryRegion iomem8; unsigned int irq_num; From 2493ff01dc7c9b06a0579f6e66c3df69da4d5d23 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 7 May 2025 10:37:53 +0800 Subject: [PATCH 0853/2760] hw/intc/loongarch_pch: Set flexible memory access size with iomem region The original iomem region only supports 4 bytes access size, set it ok with 1/2/4/8 bytes. Also unaligned memory access is not supported. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20250507023754.1877445-4-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 076b984d93..e9126a0c1f 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -262,12 +262,19 @@ static const MemoryRegionOps loongarch_pch_pic_ops = { .read = loongarch_pch_pic_read, .write = loongarch_pch_pic_write, .valid = { - .min_access_size = 4, + .min_access_size = 1, .max_access_size = 8, + /* + * PCH PIC device would not work correctly if the guest was doing + * unaligned access. This might not be a limitation on the real + * device but in practice there is no reason for a guest to access + * this device unaligned. + */ + .unaligned = false, }, .impl = { - .min_access_size = 4, - .max_access_size = 4, + .min_access_size = 1, + .max_access_size = 8, }, .endianness = DEVICE_LITTLE_ENDIAN, }; From f4881c67ba8a852687566610949d8e9ab0542a31 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 7 May 2025 10:37:54 +0800 Subject: [PATCH 0854/2760] hw/intc/loongarch_pch: Merge three memory region into one Since memory region iomem supports memory access size with 1/2/4/8, it can be used for memory region iomem8 and iomem32_high. Now remove memory region iomem8 and iomem32_high, merge them into iomem together. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20250507023754.1877445-5-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 66 +------------------------- hw/loongarch/virt.c | 6 --- include/hw/intc/loongarch_pic_common.h | 2 - 3 files changed, 1 insertion(+), 73 deletions(-) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index e9126a0c1f..cbba2fc284 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -230,34 +230,6 @@ static void loongarch_pch_pic_write(void *opaque, hwaddr addr, } } -static uint64_t loongarch_pch_pic_high_readw(void *opaque, hwaddr addr, - unsigned size) -{ - addr += PCH_PIC_INT_STATUS; - return loongarch_pch_pic_read(opaque, addr, size); -} - -static void loongarch_pch_pic_high_writew(void *opaque, hwaddr addr, - uint64_t value, unsigned size) -{ - addr += PCH_PIC_INT_STATUS; - loongarch_pch_pic_write(opaque, addr, value, size); -} - -static uint64_t loongarch_pch_pic_readb(void *opaque, hwaddr addr, - unsigned size) -{ - addr += PCH_PIC_ROUTE_ENTRY; - return loongarch_pch_pic_read(opaque, addr, size); -} - -static void loongarch_pch_pic_writeb(void *opaque, hwaddr addr, - uint64_t data, unsigned size) -{ - addr += PCH_PIC_ROUTE_ENTRY; - loongarch_pch_pic_write(opaque, addr, data, size); -} - static const MemoryRegionOps loongarch_pch_pic_ops = { .read = loongarch_pch_pic_read, .write = loongarch_pch_pic_write, @@ -279,34 +251,6 @@ static const MemoryRegionOps loongarch_pch_pic_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -static const MemoryRegionOps loongarch_pch_pic_reg32_high_ops = { - .read = loongarch_pch_pic_high_readw, - .write = loongarch_pch_pic_high_writew, - .valid = { - .min_access_size = 4, - .max_access_size = 8, - }, - .impl = { - .min_access_size = 4, - .max_access_size = 4, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - -static const MemoryRegionOps loongarch_pch_pic_reg8_ops = { - .read = loongarch_pch_pic_readb, - .write = loongarch_pch_pic_writeb, - .valid = { - .min_access_size = 1, - .max_access_size = 1, - }, - .impl = { - .min_access_size = 1, - .max_access_size = 1, - }, - .endianness = DEVICE_LITTLE_ENDIAN, -}; - static void loongarch_pic_reset_hold(Object *obj, ResetType type) { LoongarchPICClass *lpc = LOONGARCH_PIC_GET_CLASS(obj); @@ -333,16 +277,8 @@ static void loongarch_pic_realize(DeviceState *dev, Error **errp) qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num); memory_region_init_io(&s->iomem, OBJECT(dev), &loongarch_pch_pic_ops, - s, TYPE_LOONGARCH_PIC, 0x100); - memory_region_init_io(&s->iomem8, OBJECT(dev), &loongarch_pch_pic_reg8_ops, - s, PCH_PIC_NAME(.reg8), 0x2a0); - memory_region_init_io(&s->iomem32_high, OBJECT(dev), - &loongarch_pch_pic_reg32_high_ops, - s, PCH_PIC_NAME(.reg32_part2), 0xc60); + s, TYPE_LOONGARCH_PIC, VIRT_PCH_REG_SIZE); sysbus_init_mmio(sbd, &s->iomem); - sysbus_init_mmio(sbd, &s->iomem8); - sysbus_init_mmio(sbd, &s->iomem32_high); - } static void loongarch_pic_class_init(ObjectClass *klass, const void *data) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index ebcef0a92b..1b504047db 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -429,12 +429,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) sysbus_realize_and_unref(d, &error_fatal); memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE, sysbus_mmio_get_region(d, 0)); - memory_region_add_subregion(get_system_memory(), - VIRT_IOAPIC_REG_BASE + PCH_PIC_ROUTE_ENTRY, - sysbus_mmio_get_region(d, 1)); - memory_region_add_subregion(get_system_memory(), - VIRT_IOAPIC_REG_BASE + PCH_PIC_INT_STATUS, - sysbus_mmio_get_region(d, 2)); /* Connect pch_pic irqs to extioi */ for (i = 0; i < num; i++) { diff --git a/include/hw/intc/loongarch_pic_common.h b/include/hw/intc/loongarch_pic_common.h index dc03056227..9349a055d0 100644 --- a/include/hw/intc/loongarch_pic_common.h +++ b/include/hw/intc/loongarch_pic_common.h @@ -66,8 +66,6 @@ struct LoongArchPICCommonState { uint8_t htmsi_vector[64]; /* 0x200 - 0x238 */ MemoryRegion iomem; - MemoryRegion iomem32_high; - MemoryRegion iomem8; unsigned int irq_num; }; From a3d5f62254a48b7c260d5aa7bd8e8467a0bb8ea3 Mon Sep 17 00:00:00 2001 From: Xianglai Li Date: Tue, 6 May 2025 16:09:46 +0800 Subject: [PATCH 0855/2760] hw/loongarch/boot: Adjust the loading position of the initrd When only the -kernel parameter is used to load the elf kernel, the initrd is loaded in the ram. If the initrd size is too large, the loading fails, resulting in a VM startup failure. This patch first loads initrd near the kernel. When the nearby memory space of the kernel is insufficient, it tries to load it to the starting position of high memory. If there is still not enough, qemu will report an error and ask the user to increase the memory space for the virtual machine to boot. Signed-off-by: Xianglai Li Message-Id: <20250506080946.817092-1-lixianglai@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/boot.c | 52 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 0324d6adcb..9b6292eaa1 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -235,6 +235,45 @@ static int64_t load_loongarch_linux_image(const char *filename, return size; } +static ram_addr_t alloc_initrd_memory(struct loongarch_boot_info *info, + uint64_t advice_start, ssize_t rd_size) +{ + hwaddr base, ram_size, gap, low_end; + ram_addr_t initrd_end, initrd_start; + + base = VIRT_LOWMEM_BASE; + gap = VIRT_LOWMEM_SIZE; + initrd_start = advice_start; + initrd_end = initrd_start + rd_size; + + ram_size = info->ram_size; + low_end = base + MIN(ram_size, gap); + if (initrd_end <= low_end) { + return initrd_start; + } + + if (ram_size <= gap) { + error_report("The low memory too small for initial ram disk '%s'," + "You need to expand the ram", + info->initrd_filename); + exit(1); + } + + /* + * Try to load initrd in the high memory + */ + ram_size -= gap; + initrd_start = VIRT_HIGHMEM_BASE; + if (rd_size <= ram_size) { + return initrd_start; + } + + error_report("The high memory too small for initial ram disk '%s'," + "You need to expand the ram", + info->initrd_filename); + exit(1); +} + static int64_t load_kernel_info(struct loongarch_boot_info *info) { uint64_t kernel_entry, kernel_low, kernel_high; @@ -263,15 +302,10 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) initrd_size = get_image_size(info->initrd_filename); if (initrd_size > 0) { initrd_offset = ROUND_UP(kernel_high + 4 * kernel_size, 64 * KiB); - - if (initrd_offset + initrd_size > info->ram_size) { - error_report("memory too small for initial ram disk '%s'", - info->initrd_filename); - exit(1); - } - - initrd_size = load_image_targphys(info->initrd_filename, initrd_offset, - info->ram_size - initrd_offset); + initrd_offset = alloc_initrd_memory(info, initrd_offset, + initrd_size); + initrd_size = load_image_targphys(info->initrd_filename, + initrd_offset, initrd_size); } if (initrd_size == (target_ulong)-1) { From 98cbac128f1c38fa62becf5b89bc662c9218a780 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Wed, 5 Mar 2025 09:24:52 +0000 Subject: [PATCH 0856/2760] hw/cxl: Support aborting background commands As of 3.1 spec, background commands can be canceled with a new abort command. Implement the support, which is advertised in the CEL. No ad-hoc context undoing is necessary as all the command logic of the running bg command is done upon completion. Arbitrarily, the on-going background cmd will not be aborted if already at least 85% done; A mutex is introduced to stabilize mbox request cancel command vs the timer callback being fired scenarios (as well as reading the mbox registers). While some operations under critical regions may be unnecessary (irq notifying, cmd callbacks), this is not a path where performance is important, so simplicity is preferred. Tested-by: Ajay Joshi Reviewed-by: Ajay Joshi Signed-off-by: Davidlohr Bueso Signed-off-by: Jonathan Cameron Message-Id: <20250305092501.191929-2-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-device-utils.c | 14 ++++--- hw/cxl/cxl-mailbox-utils.c | 72 ++++++++++++++++++++++++++++++++---- hw/mem/cxl_type3.c | 8 +++- include/hw/cxl/cxl_device.h | 4 ++ include/hw/cxl/cxl_mailbox.h | 1 + 5 files changed, 86 insertions(+), 13 deletions(-) diff --git a/hw/cxl/cxl-device-utils.c b/hw/cxl/cxl-device-utils.c index 52ad1e4c3f..e150d74457 100644 --- a/hw/cxl/cxl-device-utils.c +++ b/hw/cxl/cxl-device-utils.c @@ -95,11 +95,15 @@ static uint64_t mailbox_reg_read(void *opaque, hwaddr offset, unsigned size) } if (offset == A_CXL_DEV_MAILBOX_STS) { uint64_t status_reg = cxl_dstate->mbox_reg_state64[offset / size]; - if (cci->bg.complete_pct) { - status_reg = FIELD_DP64(status_reg, CXL_DEV_MAILBOX_STS, BG_OP, - 0); - cxl_dstate->mbox_reg_state64[offset / size] = status_reg; - } + int bgop; + + qemu_mutex_lock(&cci->bg.lock); + bgop = !(cci->bg.complete_pct == 100 || cci->bg.aborted); + + status_reg = FIELD_DP64(status_reg, CXL_DEV_MAILBOX_STS, BG_OP, + bgop); + cxl_dstate->mbox_reg_state64[offset / size] = status_reg; + qemu_mutex_unlock(&cci->bg.lock); } return cxl_dstate->mbox_reg_state64[offset / size]; default: diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 516c01d840..4401f446d9 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -56,6 +56,7 @@ enum { INFOSTAT = 0x00, #define IS_IDENTIFY 0x1 #define BACKGROUND_OPERATION_STATUS 0x2 + #define BACKGROUND_OPERATION_ABORT 0x5 EVENTS = 0x01, #define GET_RECORDS 0x0 #define CLEAR_RECORDS 0x1 @@ -636,6 +637,41 @@ static CXLRetCode cmd_infostat_bg_op_sts(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* + * CXL r3.1 Section 8.2.9.1.5: + * Request Abort Background Operation (Opcode 0005h) + */ +static CXLRetCode cmd_infostat_bg_op_abort(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + int bg_set = cci->bg.opcode >> 8; + int bg_cmd = cci->bg.opcode & 0xff; + const struct cxl_cmd *bg_c = &cci->cxl_cmd_set[bg_set][bg_cmd]; + + if (!(bg_c->effect & CXL_MBOX_BACKGROUND_OPERATION_ABORT)) { + return CXL_MBOX_REQUEST_ABORT_NOTSUP; + } + + qemu_mutex_lock(&cci->bg.lock); + if (cci->bg.runtime) { + /* operation is near complete, let it finish */ + if (cci->bg.complete_pct < 85) { + timer_del(cci->bg.timer); + cci->bg.ret_code = CXL_MBOX_ABORTED; + cci->bg.starttime = 0; + cci->bg.runtime = 0; + cci->bg.aborted = true; + } + } + qemu_mutex_unlock(&cci->bg.lock); + + return CXL_MBOX_SUCCESS; +} + #define CXL_FW_SLOTS 2 #define CXL_FW_SIZE 0x02000000 /* 32 mb */ @@ -2715,6 +2751,8 @@ static CXLRetCode cmd_dcd_release_dyn_cap(const struct cxl_cmd *cmd, } static const struct cxl_cmd cxl_cmd_set[256][256] = { + [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT", + cmd_infostat_bg_op_abort, 0, 0 }, [EVENTS][GET_RECORDS] = { "EVENTS_GET_RECORDS", cmd_events_get_records, 1, 0 }, [EVENTS][CLEAR_RECORDS] = { "EVENTS_CLEAR_RECORDS", @@ -2727,9 +2765,11 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { [FIRMWARE_UPDATE][GET_INFO] = { "FIRMWARE_UPDATE_GET_INFO", cmd_firmware_update_get_info, 0, 0 }, [FIRMWARE_UPDATE][TRANSFER] = { "FIRMWARE_UPDATE_TRANSFER", - cmd_firmware_update_transfer, ~0, CXL_MBOX_BACKGROUND_OPERATION }, + cmd_firmware_update_transfer, ~0, + CXL_MBOX_BACKGROUND_OPERATION | CXL_MBOX_BACKGROUND_OPERATION_ABORT }, [FIRMWARE_UPDATE][ACTIVATE] = { "FIRMWARE_UPDATE_ACTIVATE", - cmd_firmware_update_activate, 2, CXL_MBOX_BACKGROUND_OPERATION }, + cmd_firmware_update_activate, 2, + CXL_MBOX_BACKGROUND_OPERATION | CXL_MBOX_BACKGROUND_OPERATION_ABORT }, [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 8, CXL_MBOX_IMMEDIATE_POLICY_CHANGE }, @@ -2758,7 +2798,8 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { [SANITIZE][OVERWRITE] = { "SANITIZE_OVERWRITE", cmd_sanitize_overwrite, 0, (CXL_MBOX_IMMEDIATE_DATA_CHANGE | CXL_MBOX_SECURITY_STATE_CHANGE | - CXL_MBOX_BACKGROUND_OPERATION)}, + CXL_MBOX_BACKGROUND_OPERATION | + CXL_MBOX_BACKGROUND_OPERATION_ABORT)}, [PERSISTENT_MEM][GET_SECURITY_STATE] = { "GET_SECURITY_STATE", cmd_get_security_state, 0, 0 }, [MEDIA_AND_POISON][GET_POISON_LIST] = { "MEDIA_AND_POISON_GET_POISON_LIST", @@ -2771,7 +2812,8 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { "MEDIA_AND_POISON_GET_SCAN_MEDIA_CAPABILITIES", cmd_media_get_scan_media_capabilities, 16, 0 }, [MEDIA_AND_POISON][SCAN_MEDIA] = { "MEDIA_AND_POISON_SCAN_MEDIA", - cmd_media_scan_media, 17, CXL_MBOX_BACKGROUND_OPERATION }, + cmd_media_scan_media, 17, + (CXL_MBOX_BACKGROUND_OPERATION | CXL_MBOX_BACKGROUND_OPERATION_ABORT)}, [MEDIA_AND_POISON][GET_SCAN_MEDIA_RESULTS] = { "MEDIA_AND_POISON_GET_SCAN_MEDIA_RESULTS", cmd_media_get_scan_media_results, 0, 0 }, @@ -2795,6 +2837,8 @@ static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0 }, [INFOSTAT][BACKGROUND_OPERATION_STATUS] = { "BACKGROUND_OPERATION_STATUS", cmd_infostat_bg_op_sts, 0, 0 }, + [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT", + cmd_infostat_bg_op_abort, 0, 0 }, [TIMESTAMP][GET] = { "TIMESTAMP_GET", cmd_timestamp_get, 0, 0 }, [TIMESTAMP][SET] = { "TIMESTAMP_SET", cmd_timestamp_set, 8, CXL_MBOX_IMMEDIATE_POLICY_CHANGE }, @@ -2881,6 +2925,7 @@ int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, cci->bg.opcode = (set << 8) | cmd; cci->bg.complete_pct = 0; + cci->bg.aborted = false; cci->bg.ret_code = 0; now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); @@ -2894,10 +2939,12 @@ int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, static void bg_timercb(void *opaque) { CXLCCI *cci = opaque; - uint64_t now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); - uint64_t total_time = cci->bg.starttime + cci->bg.runtime; + uint64_t now, total_time; + + qemu_mutex_lock(&cci->bg.lock); - assert(cci->bg.runtime > 0); + now = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); + total_time = cci->bg.starttime + cci->bg.runtime; if (now >= total_time) { /* we are done */ uint16_t ret = CXL_MBOX_SUCCESS; @@ -2950,6 +2997,8 @@ static void bg_timercb(void *opaque) msi_notify(pdev, cxl_dstate->mbox_msi_n); } } + + qemu_mutex_unlock(&cci->bg.lock); } static void cxl_rebuild_cel(CXLCCI *cci) @@ -2978,12 +3027,21 @@ void cxl_init_cci(CXLCCI *cci, size_t payload_max) cci->bg.complete_pct = 0; cci->bg.starttime = 0; cci->bg.runtime = 0; + cci->bg.aborted = false; cci->bg.timer = timer_new_ms(QEMU_CLOCK_VIRTUAL, bg_timercb, cci); + qemu_mutex_init(&cci->bg.lock); memset(&cci->fw, 0, sizeof(cci->fw)); cci->fw.active_slot = 1; cci->fw.slot[cci->fw.active_slot - 1] = true; + cci->initialized = true; +} + +void cxl_destroy_cci(CXLCCI *cci) +{ + qemu_mutex_destroy(&cci->bg.lock); + cci->initialized = false; } static void cxl_copy_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmds)[256]) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index bba923f8ea..aacd078118 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -969,6 +969,7 @@ static void ct3_exit(PCIDevice *pci_dev) cxl_doe_cdat_release(cxl_cstate); msix_uninit_exclusive_bar(pci_dev); g_free(regs->special_ops); + cxl_destroy_cci(&ct3d->cci); if (ct3d->dc.host_dc) { cxl_destroy_dc_regions(ct3d); address_space_destroy(&ct3d->dc.host_dc_as); @@ -1224,12 +1225,17 @@ static void ct3d_reset(DeviceState *dev) * Bring up an endpoint to target with MCTP over VDM. * This device is emulating an MLD with single LD for now. */ + if (ct3d->vdm_fm_owned_ld_mctp_cci.initialized) { + cxl_destroy_cci(&ct3d->vdm_fm_owned_ld_mctp_cci); + } cxl_initialize_t3_fm_owned_ld_mctpcci(&ct3d->vdm_fm_owned_ld_mctp_cci, DEVICE(ct3d), DEVICE(ct3d), 512); /* Max payload made up */ + if (ct3d->ld0_cci.initialized) { + cxl_destroy_cci(&ct3d->ld0_cci); + } cxl_initialize_t3_ld_cci(&ct3d->ld0_cci, DEVICE(ct3d), DEVICE(ct3d), 512); /* Max payload made up */ - } static const Property ct3_props[] = { diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 3a0ee7e8e7..d21695507f 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -176,10 +176,12 @@ typedef struct CXLCCI { uint16_t opcode; uint16_t complete_pct; uint16_t ret_code; /* Current value of retcode */ + bool aborted; uint64_t starttime; /* set by each bg cmd, cleared by the bg_timer when complete */ uint64_t runtime; QEMUTimer *timer; + QemuMutex lock; /* serializes mbox abort vs timer cb */ } bg; /* firmware update */ @@ -201,6 +203,7 @@ typedef struct CXLCCI { DeviceState *d; /* Pointer to the device hosting the protocol conversion */ DeviceState *intf; + bool initialized; } CXLCCI; typedef struct cxl_device_state { @@ -316,6 +319,7 @@ void cxl_initialize_mailbox_t3(CXLCCI *cci, DeviceState *d, size_t payload_max); void cxl_initialize_mailbox_swcci(CXLCCI *cci, DeviceState *intf, DeviceState *d, size_t payload_max); void cxl_init_cci(CXLCCI *cci, size_t payload_max); +void cxl_destroy_cci(CXLCCI *cci); void cxl_add_cci_commands(CXLCCI *cci, const struct cxl_cmd (*cxl_cmd_set)[256], size_t payload_max); int cxl_process_cci_message(CXLCCI *cci, uint8_t set, uint8_t cmd, diff --git a/include/hw/cxl/cxl_mailbox.h b/include/hw/cxl/cxl_mailbox.h index beb048052e..9008402d1c 100644 --- a/include/hw/cxl/cxl_mailbox.h +++ b/include/hw/cxl/cxl_mailbox.h @@ -14,5 +14,6 @@ #define CXL_MBOX_IMMEDIATE_LOG_CHANGE (1 << 4) #define CXL_MBOX_SECURITY_STATE_CHANGE (1 << 5) #define CXL_MBOX_BACKGROUND_OPERATION (1 << 6) +#define CXL_MBOX_BACKGROUND_OPERATION_ABORT (1 << 7) #endif From 1000158f031818bf286454c8691da9b4f33c4d02 Mon Sep 17 00:00:00 2001 From: Davidlohr Bueso Date: Wed, 5 Mar 2025 09:24:53 +0000 Subject: [PATCH 0857/2760] hw/cxl: Support get/set mctp response payload size Add Get/Set Response Message Limit commands. Signed-off-by: Davidlohr Bueso Reviewed-by: Fan Ni Signed-off-by: Jonathan Cameron Message-Id: <20250305092501.191929-3-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 58 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 4401f446d9..bd25df033a 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -7,6 +7,8 @@ * COPYING file in the top-level directory. */ +#include + #include "qemu/osdep.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" @@ -56,6 +58,8 @@ enum { INFOSTAT = 0x00, #define IS_IDENTIFY 0x1 #define BACKGROUND_OPERATION_STATUS 0x2 + #define GET_RESPONSE_MSG_LIMIT 0x3 + #define SET_RESPONSE_MSG_LIMIT 0x4 #define BACKGROUND_OPERATION_ABORT 0x5 EVENTS = 0x01, #define GET_RECORDS 0x0 @@ -413,12 +417,58 @@ static CXLRetCode cmd_infostat_identify(const struct cxl_cmd *cmd, is_identify->component_type = 0x3; /* Type 3 */ } - /* TODO: Allow this to vary across different CCIs */ - is_identify->max_message_size = 9; /* 512 bytes - MCTP_CXL_MAILBOX_BYTES */ + is_identify->max_message_size = (uint8_t)log2(cci->payload_max); *len_out = sizeof(*is_identify); return CXL_MBOX_SUCCESS; } +/* CXL r3.1 section 8.2.9.1.3: Get Response Message Limit (Opcode 0003h) */ +static CXLRetCode cmd_get_response_msg_limit(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t rsp_limit; + } QEMU_PACKED *get_rsp_msg_limit = (void *)payload_out; + QEMU_BUILD_BUG_ON(sizeof(*get_rsp_msg_limit) != 1); + + get_rsp_msg_limit->rsp_limit = (uint8_t)log2(cci->payload_max); + + *len_out = sizeof(*get_rsp_msg_limit); + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.1 section 8.2.9.1.4: Set Response Message Limit (Opcode 0004h) */ +static CXLRetCode cmd_set_response_msg_limit(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t rsp_limit; + } QEMU_PACKED *in = (void *)payload_in; + QEMU_BUILD_BUG_ON(sizeof(*in) != 1); + struct { + uint8_t rsp_limit; + } QEMU_PACKED *out = (void *)payload_out; + QEMU_BUILD_BUG_ON(sizeof(*out) != 1); + + if (in->rsp_limit < 8 || in->rsp_limit > 10) { + return CXL_MBOX_INVALID_INPUT; + } + + cci->payload_max = 1 << in->rsp_limit; + out->rsp_limit = in->rsp_limit; + + *len_out = sizeof(*out); + return CXL_MBOX_SUCCESS; +} + static void cxl_set_dsp_active_bm(PCIBus *b, PCIDevice *d, void *private) { @@ -3105,6 +3155,10 @@ void cxl_initialize_t3_ld_cci(CXLCCI *cci, DeviceState *d, DeviceState *intf, static const struct cxl_cmd cxl_cmd_set_t3_fm_owned_ld_mctp[256][256] = { [INFOSTAT][IS_IDENTIFY] = { "IDENTIFY", cmd_infostat_identify, 0, 0}, + [INFOSTAT][GET_RESPONSE_MSG_LIMIT] = { "GET_RESPONSE_MSG_LIMIT", + cmd_get_response_msg_limit, 0, 0 }, + [INFOSTAT][SET_RESPONSE_MSG_LIMIT] = { "SET_RESPONSE_MSG_LIMIT", + cmd_set_response_msg_limit, 1, 0 }, [LOGS][GET_SUPPORTED] = { "LOGS_GET_SUPPORTED", cmd_logs_get_supported, 0, 0 }, [LOGS][GET_LOG] = { "LOGS_GET_LOG", cmd_logs_get_log, 0x18, 0 }, From 77a8e9fe0ecb71b260d17f43221df5b18769b359 Mon Sep 17 00:00:00 2001 From: Vinayak Holikatti Date: Wed, 5 Mar 2025 09:24:54 +0000 Subject: [PATCH 0858/2760] hw/cxl/cxl-mailbox-utils: Add support for Media operations discovery commands cxl r3.2 (8.2.10.9.5.3) CXL spec 3.2 section 8.2.10.9.5.3 describes media operations commands. CXL devices supports media operations discovery command. Signed-off-by: Vinayak Holikatti Signed-off-by: Jonathan Cameron Message-Id: <20250305092501.191929-4-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 125 +++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index bd25df033a..79b35d1405 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -89,6 +89,7 @@ enum { SANITIZE = 0x44, #define OVERWRITE 0x0 #define SECURE_ERASE 0x1 + #define MEDIA_OPERATIONS 0x2 PERSISTENT_MEM = 0x45, #define GET_SECURITY_STATE 0x0 MEDIA_AND_POISON = 0x43, @@ -1705,6 +1706,126 @@ static CXLRetCode cmd_sanitize_overwrite(const struct cxl_cmd *cmd, return CXL_MBOX_BG_STARTED; } +enum { + MEDIA_OP_CLASS_GENERAL = 0x0, + #define MEDIA_OP_GEN_SUBC_DISCOVERY 0x0 + MEDIA_OP_CLASS_SANITIZE = 0x1, + #define MEDIA_OP_SAN_SUBC_SANITIZE 0x0 + #define MEDIA_OP_SAN_SUBC_ZERO 0x1 +}; + +struct media_op_supported_list_entry { + uint8_t media_op_class; + uint8_t media_op_subclass; +}; + +struct media_op_discovery_out_pl { + uint64_t dpa_range_granularity; + uint16_t total_supported_operations; + uint16_t num_of_supported_operations; + struct media_op_supported_list_entry entry[]; +} QEMU_PACKED; + +static const struct media_op_supported_list_entry media_op_matrix[] = { + { MEDIA_OP_CLASS_GENERAL, MEDIA_OP_GEN_SUBC_DISCOVERY }, + { MEDIA_OP_CLASS_SANITIZE, MEDIA_OP_SAN_SUBC_SANITIZE }, + { MEDIA_OP_CLASS_SANITIZE, MEDIA_OP_SAN_SUBC_ZERO }, +}; + +static CXLRetCode media_operations_discovery(uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out) +{ + struct { + uint8_t media_operation_class; + uint8_t media_operation_subclass; + uint8_t rsvd[2]; + uint32_t dpa_range_count; + struct { + uint16_t start_index; + uint16_t num_ops; + } discovery_osa; + } QEMU_PACKED *media_op_in_disc_pl = (void *)payload_in; + struct media_op_discovery_out_pl *media_out_pl = + (struct media_op_discovery_out_pl *)payload_out; + int num_ops, start_index, i; + int count = 0; + + if (len_in < sizeof(*media_op_in_disc_pl)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + num_ops = media_op_in_disc_pl->discovery_osa.num_ops; + start_index = media_op_in_disc_pl->discovery_osa.start_index; + + /* + * As per spec CXL r3.2 8.2.10.9.5.3 dpa_range_count should be zero and + * start index should not exceed the total number of entries for discovery + * sub class command. + */ + if (media_op_in_disc_pl->dpa_range_count || + start_index > ARRAY_SIZE(media_op_matrix)) { + return CXL_MBOX_INVALID_INPUT; + } + + media_out_pl->dpa_range_granularity = CXL_CACHE_LINE_SIZE; + media_out_pl->total_supported_operations = + ARRAY_SIZE(media_op_matrix); + if (num_ops > 0) { + for (i = start_index; i < start_index + num_ops; i++) { + media_out_pl->entry[count].media_op_class = + media_op_matrix[i].media_op_class; + media_out_pl->entry[count].media_op_subclass = + media_op_matrix[i].media_op_subclass; + count++; + if (count == num_ops) { + break; + } + } + } + + media_out_pl->num_of_supported_operations = count; + *len_out = sizeof(*media_out_pl) + count * sizeof(*media_out_pl->entry); + return CXL_MBOX_SUCCESS; +} + +static CXLRetCode cmd_media_operations(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t media_operation_class; + uint8_t media_operation_subclass; + uint8_t rsvd[2]; + uint32_t dpa_range_count; + } QEMU_PACKED *media_op_in_common_pl = (void *)payload_in; + uint8_t media_op_cl = 0; + uint8_t media_op_subclass = 0; + + if (len_in < sizeof(*media_op_in_common_pl)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + media_op_cl = media_op_in_common_pl->media_operation_class; + media_op_subclass = media_op_in_common_pl->media_operation_subclass; + + switch (media_op_cl) { + case MEDIA_OP_CLASS_GENERAL: + if (media_op_subclass != MEDIA_OP_GEN_SUBC_DISCOVERY) { + return CXL_MBOX_UNSUPPORTED; + } + + return media_operations_discovery(payload_in, len_in, payload_out, + len_out); + default: + return CXL_MBOX_UNSUPPORTED; + } +} + static CXLRetCode cmd_get_security_state(const struct cxl_cmd *cmd, uint8_t *payload_in, size_t len_in, @@ -2850,6 +2971,10 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { CXL_MBOX_SECURITY_STATE_CHANGE | CXL_MBOX_BACKGROUND_OPERATION | CXL_MBOX_BACKGROUND_OPERATION_ABORT)}, + [SANITIZE][MEDIA_OPERATIONS] = { "MEDIA_OPERATIONS", cmd_media_operations, + ~0, + (CXL_MBOX_IMMEDIATE_DATA_CHANGE | + CXL_MBOX_BACKGROUND_OPERATION)}, [PERSISTENT_MEM][GET_SECURITY_STATE] = { "GET_SECURITY_STATE", cmd_get_security_state, 0, 0 }, [MEDIA_AND_POISON][GET_POISON_LIST] = { "MEDIA_AND_POISON_GET_POISON_LIST", From 484df0704ea592ebd5993c15d63ea38f309ec6e0 Mon Sep 17 00:00:00 2001 From: Vinayak Holikatti Date: Wed, 5 Mar 2025 09:24:55 +0000 Subject: [PATCH 0859/2760] hw/cxl: factor out calculation of sanitize duration from cmd_santize_overwrite Move the code of calculation of sanitize duration into function for usability by other sanitize routines Estimate times based on: https://pmem.io/documents/NVDIMM_DSM_Interface-V1.8.pdf Signed-off-by: Davidlohr Bueso Signed-off-by: Vinayak Holikatti Signed-off-by: Jonathan Cameron Message-Id: <20250305092501.191929-5-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 61 ++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 26 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 79b35d1405..9f9d475678 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -1640,34 +1640,10 @@ static void __do_sanitization(CXLType3Dev *ct3d) cxl_discard_all_event_records(&ct3d->cxl_dstate); } -/* - * CXL r3.1 Section 8.2.9.9.5.1: Sanitize (Opcode 4400h) - * - * Once the Sanitize command has started successfully, the device shall be - * placed in the media disabled state. If the command fails or is interrupted - * by a reset or power failure, it shall remain in the media disabled state - * until a successful Sanitize command has been completed. During this state: - * - * 1. Memory writes to the device will have no effect, and all memory reads - * will return random values (no user data returned, even for locations that - * the failed Sanitize operation didn’t sanitize yet). - * - * 2. Mailbox commands shall still be processed in the disabled state, except - * that commands that access Sanitized areas shall fail with the Media Disabled - * error code. - */ -static CXLRetCode cmd_sanitize_overwrite(const struct cxl_cmd *cmd, - uint8_t *payload_in, - size_t len_in, - uint8_t *payload_out, - size_t *len_out, - CXLCCI *cci) +static int get_sanitize_duration(uint64_t total_mem) { - CXLType3Dev *ct3d = CXL_TYPE3(cci->d); - uint64_t total_mem; /* in Mb */ - int secs; + int secs = 0; - total_mem = (ct3d->cxl_dstate.vmem_size + ct3d->cxl_dstate.pmem_size) >> 20; if (total_mem <= 512) { secs = 4; } else if (total_mem <= 1024) { @@ -1696,6 +1672,39 @@ static CXLRetCode cmd_sanitize_overwrite(const struct cxl_cmd *cmd, secs = 240 * 60; /* max 4 hrs */ } + return secs; +} + +/* + * CXL r3.1 Section 8.2.9.9.5.1: Sanitize (Opcode 4400h) + * + * Once the Sanitize command has started successfully, the device shall be + * placed in the media disabled state. If the command fails or is interrupted + * by a reset or power failure, it shall remain in the media disabled state + * until a successful Sanitize command has been completed. During this state: + * + * 1. Memory writes to the device will have no effect, and all memory reads + * will return random values (no user data returned, even for locations that + * the failed Sanitize operation didn’t sanitize yet). + * + * 2. Mailbox commands shall still be processed in the disabled state, except + * that commands that access Sanitized areas shall fail with the Media Disabled + * error code. + */ +static CXLRetCode cmd_sanitize_overwrite(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + uint64_t total_mem; /* in Mb */ + int secs; + + total_mem = (ct3d->cxl_dstate.vmem_size + ct3d->cxl_dstate.pmem_size) >> 20; + secs = get_sanitize_duration(total_mem); + /* EBUSY other bg cmds as of now */ cci->bg.runtime = secs * 1000UL; *len_out = 0; From 40ab4ed107757e1c5bdccc906e8a44cb4e2cb7a4 Mon Sep 17 00:00:00 2001 From: Vinayak Holikatti Date: Wed, 5 Mar 2025 09:24:56 +0000 Subject: [PATCH 0860/2760] hw/cxl/cxl-mailbox-utils: Media operations Sanitize and Write Zeros commands CXL r3.2(8.2.10.9.5.3) CXL spec 3.2 section 8.2.10.9.5.3 describes media operations commands. CXL devices supports media operations Sanitize and Write zero command. Signed-off-by: Vinayak Holikatti Signed-off-by: Jonathan Cameron Message-Id: <20250305092501.191929-6-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 204 ++++++++++++++++++++++++++++++++++++ include/hw/cxl/cxl_device.h | 4 + 2 files changed, 208 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 9f9d475678..2c6db70e5f 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -1715,6 +1715,131 @@ static CXLRetCode cmd_sanitize_overwrite(const struct cxl_cmd *cmd, return CXL_MBOX_BG_STARTED; } +struct dpa_range_list_entry { + uint64_t starting_dpa; + uint64_t length; +} QEMU_PACKED; + +struct CXLSanitizeInfo { + uint32_t dpa_range_count; + uint8_t fill_value; + struct dpa_range_list_entry dpa_range_list[]; +} QEMU_PACKED; + +static uint64_t get_vmr_size(CXLType3Dev *ct3d, MemoryRegion **vmr) +{ + MemoryRegion *mr; + if (ct3d->hostvmem) { + mr = host_memory_backend_get_memory(ct3d->hostvmem); + if (vmr) { + *vmr = mr; + } + return memory_region_size(mr); + } + return 0; +} + +static uint64_t get_pmr_size(CXLType3Dev *ct3d, MemoryRegion **pmr) +{ + MemoryRegion *mr; + if (ct3d->hostpmem) { + mr = host_memory_backend_get_memory(ct3d->hostpmem); + if (pmr) { + *pmr = mr; + } + return memory_region_size(mr); + } + return 0; +} + +static uint64_t get_dc_size(CXLType3Dev *ct3d, MemoryRegion **dc_mr) +{ + MemoryRegion *mr; + if (ct3d->dc.host_dc) { + mr = host_memory_backend_get_memory(ct3d->dc.host_dc); + if (dc_mr) { + *dc_mr = mr; + } + return memory_region_size(mr); + } + return 0; +} + +static int validate_dpa_addr(CXLType3Dev *ct3d, uint64_t dpa_addr, + size_t length) +{ + uint64_t vmr_size, pmr_size, dc_size; + + if ((dpa_addr % CXL_CACHE_LINE_SIZE) || + (length % CXL_CACHE_LINE_SIZE) || + (length <= 0)) { + return -EINVAL; + } + + vmr_size = get_vmr_size(ct3d, NULL); + pmr_size = get_pmr_size(ct3d, NULL); + dc_size = get_dc_size(ct3d, NULL); + + if (dpa_addr + length > vmr_size + pmr_size + dc_size) { + return -EINVAL; + } + + if (dpa_addr > vmr_size + pmr_size) { + if (!ct3_test_region_block_backed(ct3d, dpa_addr, length)) { + return -ENODEV; + } + } + + return 0; +} + +static int sanitize_range(CXLType3Dev *ct3d, uint64_t dpa_addr, size_t length, + uint8_t fill_value) +{ + + uint64_t vmr_size, pmr_size; + AddressSpace *as = NULL; + MemTxAttrs mem_attrs = {}; + + vmr_size = get_vmr_size(ct3d, NULL); + pmr_size = get_pmr_size(ct3d, NULL); + + if (dpa_addr < vmr_size) { + as = &ct3d->hostvmem_as; + } else if (dpa_addr < vmr_size + pmr_size) { + as = &ct3d->hostpmem_as; + } else { + if (!ct3_test_region_block_backed(ct3d, dpa_addr, length)) { + return -ENODEV; + } + as = &ct3d->dc.host_dc_as; + } + + return address_space_set(as, dpa_addr, fill_value, length, mem_attrs); +} + +/* Perform the actual device zeroing */ +static void __do_sanitize(CXLType3Dev *ct3d) +{ + struct CXLSanitizeInfo *san_info = ct3d->media_op_sanitize; + int dpa_range_count = san_info->dpa_range_count; + int rc = 0; + int i; + + for (i = 0; i < dpa_range_count; i++) { + rc = sanitize_range(ct3d, san_info->dpa_range_list[i].starting_dpa, + san_info->dpa_range_list[i].length, + san_info->fill_value); + if (rc) { + goto exit; + } + } +exit: + g_free(ct3d->media_op_sanitize); + ct3d->media_op_sanitize = NULL; + return; +} + enum { MEDIA_OP_CLASS_GENERAL = 0x0, #define MEDIA_OP_GEN_SUBC_DISCOVERY 0x0 @@ -1799,6 +1924,65 @@ static CXLRetCode media_operations_discovery(uint8_t *payload_in, return CXL_MBOX_SUCCESS; } +static CXLRetCode media_operations_sanitize(CXLType3Dev *ct3d, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + uint8_t fill_value, + CXLCCI *cci) +{ + struct media_operations_sanitize { + uint8_t media_operation_class; + uint8_t media_operation_subclass; + uint8_t rsvd[2]; + uint32_t dpa_range_count; + struct dpa_range_list_entry dpa_range_list[]; + } QEMU_PACKED *media_op_in_sanitize_pl = (void *)payload_in; + uint32_t dpa_range_count = media_op_in_sanitize_pl->dpa_range_count; + uint64_t total_mem = 0; + size_t dpa_range_list_size; + int secs = 0, i; + + if (dpa_range_count == 0) { + return CXL_MBOX_SUCCESS; + } + + dpa_range_list_size = dpa_range_count * sizeof(struct dpa_range_list_entry); + if (len_in < (sizeof(*media_op_in_sanitize_pl) + dpa_range_list_size)) { + return CXL_MBOX_INVALID_PAYLOAD_LENGTH; + } + + for (i = 0; i < dpa_range_count; i++) { + uint64_t start_dpa = + media_op_in_sanitize_pl->dpa_range_list[i].starting_dpa; + uint64_t length = media_op_in_sanitize_pl->dpa_range_list[i].length; + + if (validate_dpa_addr(ct3d, start_dpa, length)) { + return CXL_MBOX_INVALID_INPUT; + } + total_mem += length; + } + ct3d->media_op_sanitize = g_malloc0(sizeof(struct CXLSanitizeInfo) + + dpa_range_list_size); + + ct3d->media_op_sanitize->dpa_range_count = dpa_range_count; + ct3d->media_op_sanitize->fill_value = fill_value; + memcpy(ct3d->media_op_sanitize->dpa_range_list, + media_op_in_sanitize_pl->dpa_range_list, + dpa_range_list_size); + secs = get_sanitize_duration(total_mem >> 20); + + /* EBUSY other bg cmds as of now */ + cci->bg.runtime = secs * 1000UL; + *len_out = 0; + /* + * media op sanitize is targeted so no need to disable media or + * clear event logs + */ + return CXL_MBOX_BG_STARTED; +} + static CXLRetCode cmd_media_operations(const struct cxl_cmd *cmd, uint8_t *payload_in, size_t len_in, @@ -1812,6 +1996,7 @@ static CXLRetCode cmd_media_operations(const struct cxl_cmd *cmd, uint8_t rsvd[2]; uint32_t dpa_range_count; } QEMU_PACKED *media_op_in_common_pl = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); uint8_t media_op_cl = 0; uint8_t media_op_subclass = 0; @@ -1830,6 +2015,19 @@ static CXLRetCode cmd_media_operations(const struct cxl_cmd *cmd, return media_operations_discovery(payload_in, len_in, payload_out, len_out); + case MEDIA_OP_CLASS_SANITIZE: + switch (media_op_subclass) { + case MEDIA_OP_SAN_SUBC_SANITIZE: + return media_operations_sanitize(ct3d, payload_in, len_in, + payload_out, len_out, 0xF, + cci); + case MEDIA_OP_SAN_SUBC_ZERO: + return media_operations_sanitize(ct3d, payload_in, len_in, + payload_out, len_out, 0, + cci); + default: + return CXL_MBOX_UNSUPPORTED; + } default: return CXL_MBOX_UNSUPPORTED; } @@ -3147,6 +3345,12 @@ static void bg_timercb(void *opaque) cxl_dev_enable_media(&ct3d->cxl_dstate); } break; + case 0x4402: /* Media Operations sanitize */ + { + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + __do_sanitize(ct3d); + } + break; case 0x4304: /* scan media */ { CXLType3Dev *ct3d = CXL_TYPE3(cci->d); diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index d21695507f..3ec7be3809 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -540,6 +540,8 @@ typedef struct CXLSetFeatureInfo { size_t data_size; } CXLSetFeatureInfo; +struct CXLSanitizeInfo; + struct CXLType3Dev { /* Private */ PCIDevice parent_obj; @@ -606,6 +608,8 @@ struct CXLType3Dev { uint8_t num_regions; /* 0-8 regions */ CXLDCRegion regions[DCD_MAX_NUM_REGION]; } dc; + + struct CXLSanitizeInfo *media_op_sanitize; }; #define TYPE_CXL_TYPE3 "cxl-type3" From a3e0b1ff37e930095a417666ab3e12715394fb9b Mon Sep 17 00:00:00 2001 From: Sweta Kumari Date: Wed, 5 Mar 2025 09:24:57 +0000 Subject: [PATCH 0861/2760] hw/cxl/cxl-mailbox-utils: CXL CCI Get/Set alert config commands 1) get alert configuration(Opcode 4201h) 2) set alert configuration(Opcode 4202h) Signed-off-by: Sweta Kumari Signed-off-by: Jonathan Cameron Message-Id: <20250305092501.191929-7-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 105 ++++++++++++++++++++++++++++++++++++ hw/mem/cxl_type3.c | 14 +++++ include/hw/cxl/cxl_device.h | 15 ++++++ 3 files changed, 134 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 2c6db70e5f..299f232f26 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -28,6 +28,11 @@ #define CXL_DC_EVENT_LOG_SIZE 8 #define CXL_NUM_EXTENTS_SUPPORTED 512 #define CXL_NUM_TAGS_SUPPORTED 0 +#define CXL_ALERTS_LIFE_USED_WARN_THRESH (1 << 0) +#define CXL_ALERTS_OVER_TEMP_WARN_THRESH (1 << 1) +#define CXL_ALERTS_UNDER_TEMP_WARN_THRESH (1 << 2) +#define CXL_ALERTS_COR_VMEM_ERR_WARN_THRESH (1 << 3) +#define CXL_ALERTS_COR_PMEM_ERR_WARN_THRESH (1 << 4) /* * How to add a new command, example. The command set FOO, with cmd BAR. @@ -86,6 +91,9 @@ enum { #define GET_PARTITION_INFO 0x0 #define GET_LSA 0x2 #define SET_LSA 0x3 + HEALTH_INFO_ALERTS = 0x42, + #define GET_ALERT_CONFIG 0x1 + #define SET_ALERT_CONFIG 0x2 SANITIZE = 0x44, #define OVERWRITE 0x0 #define SECURE_ERASE 0x1 @@ -1610,6 +1618,97 @@ static CXLRetCode cmd_ccls_set_lsa(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* CXL r3.2 Section 8.2.10.9.3.2 Get Alert Configuration (Opcode 4201h) */ +static CXLRetCode cmd_get_alert_config(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLAlertConfig *out = (CXLAlertConfig *)payload_out; + + memcpy(out, &ct3d->alert_config, sizeof(ct3d->alert_config)); + *len_out = sizeof(ct3d->alert_config); + + return CXL_MBOX_SUCCESS; +} + +/* CXL r3.2 Section 8.2.10.9.3.3 Set Alert Configuration (Opcode 4202h) */ +static CXLRetCode cmd_set_alert_config(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLAlertConfig *alert_config = &ct3d->alert_config; + struct { + uint8_t valid_alert_actions; + uint8_t enable_alert_actions; + uint8_t life_used_warn_thresh; + uint8_t rsvd; + uint16_t over_temp_warn_thresh; + uint16_t under_temp_warn_thresh; + uint16_t cor_vmem_err_warn_thresh; + uint16_t cor_pmem_err_warn_thresh; + } QEMU_PACKED *in = (void *)payload_in; + + if (in->valid_alert_actions & CXL_ALERTS_LIFE_USED_WARN_THRESH) { + /* + * CXL r3.2 Table 8-149 The life used warning threshold shall be + * less than the life used critical alert value. + */ + if (in->life_used_warn_thresh >= + alert_config->life_used_crit_alert_thresh) { + return CXL_MBOX_INVALID_INPUT; + } + alert_config->life_used_warn_thresh = in->life_used_warn_thresh; + alert_config->enable_alerts |= CXL_ALERTS_LIFE_USED_WARN_THRESH; + } + + if (in->valid_alert_actions & CXL_ALERTS_OVER_TEMP_WARN_THRESH) { + /* + * CXL r3.2 Table 8-149 The Device Over-Temperature Warning Threshold + * shall be less than the the Device Over-Temperature Critical + * Alert Threshold. + */ + if (in->over_temp_warn_thresh >= + alert_config->over_temp_crit_alert_thresh) { + return CXL_MBOX_INVALID_INPUT; + } + alert_config->over_temp_warn_thresh = in->over_temp_warn_thresh; + alert_config->enable_alerts |= CXL_ALERTS_OVER_TEMP_WARN_THRESH; + } + + if (in->valid_alert_actions & CXL_ALERTS_UNDER_TEMP_WARN_THRESH) { + /* + * CXL r3.2 Table 8-149 The Device Under-Temperature Warning Threshold + * shall be higher than the the Device Under-Temperature Critical + * Alert Threshold. + */ + if (in->under_temp_warn_thresh <= + alert_config->under_temp_crit_alert_thresh) { + return CXL_MBOX_INVALID_INPUT; + } + alert_config->under_temp_warn_thresh = in->under_temp_warn_thresh; + alert_config->enable_alerts |= CXL_ALERTS_UNDER_TEMP_WARN_THRESH; + } + + if (in->valid_alert_actions & CXL_ALERTS_COR_VMEM_ERR_WARN_THRESH) { + alert_config->cor_vmem_err_warn_thresh = in->cor_vmem_err_warn_thresh; + alert_config->enable_alerts |= CXL_ALERTS_COR_VMEM_ERR_WARN_THRESH; + } + + if (in->valid_alert_actions & CXL_ALERTS_COR_PMEM_ERR_WARN_THRESH) { + alert_config->cor_pmem_err_warn_thresh = in->cor_pmem_err_warn_thresh; + alert_config->enable_alerts |= CXL_ALERTS_COR_PMEM_ERR_WARN_THRESH; + } + return CXL_MBOX_SUCCESS; +} + /* Perform the actual device zeroing */ static void __do_sanitization(CXLType3Dev *ct3d) { @@ -3173,6 +3272,12 @@ static const struct cxl_cmd cxl_cmd_set[256][256] = { [CCLS][GET_LSA] = { "CCLS_GET_LSA", cmd_ccls_get_lsa, 8, 0 }, [CCLS][SET_LSA] = { "CCLS_SET_LSA", cmd_ccls_set_lsa, ~0, CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | CXL_MBOX_IMMEDIATE_DATA_CHANGE }, + [HEALTH_INFO_ALERTS][GET_ALERT_CONFIG] = { + "HEALTH_INFO_ALERTS_GET_ALERT_CONFIG", + cmd_get_alert_config, 0, 0 }, + [HEALTH_INFO_ALERTS][SET_ALERT_CONFIG] = { + "HEALTH_INFO_ALERTS_SET_ALERT_CONFIG", + cmd_set_alert_config, 12, CXL_MBOX_IMMEDIATE_POLICY_CHANGE }, [SANITIZE][OVERWRITE] = { "SANITIZE_OVERWRITE", cmd_sanitize_overwrite, 0, (CXL_MBOX_IMMEDIATE_DATA_CHANGE | CXL_MBOX_SECURITY_STATE_CHANGE | diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index aacd078118..94e7274912 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -843,6 +843,19 @@ static DOEProtocol doe_cdat_prot[] = { { } }; +/* Initialize CXL device alerts with default threshold values. */ +static void init_alert_config(CXLType3Dev *ct3d) +{ + ct3d->alert_config = (CXLAlertConfig) { + .life_used_crit_alert_thresh = 75, + .life_used_warn_thresh = 40, + .over_temp_crit_alert_thresh = 35, + .under_temp_crit_alert_thresh = 10, + .over_temp_warn_thresh = 25, + .under_temp_warn_thresh = 20 + }; +} + static void ct3_realize(PCIDevice *pci_dev, Error **errp) { ERRP_GUARD(); @@ -910,6 +923,7 @@ static void ct3_realize(PCIDevice *pci_dev, Error **errp) goto err_msix_uninit; } + init_alert_config(ct3d); pcie_cap_deverr_init(pci_dev); /* Leave a bit of room for expansion */ rc = pcie_aer_init(pci_dev, PCI_ERR_VER, 0x200, PCI_ERR_SIZEOF, errp); diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 3ec7be3809..ed6cd50c67 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -542,6 +542,19 @@ typedef struct CXLSetFeatureInfo { struct CXLSanitizeInfo; +typedef struct CXLAlertConfig { + uint8_t valid_alerts; + uint8_t enable_alerts; + uint8_t life_used_crit_alert_thresh; + uint8_t life_used_warn_thresh; + uint16_t over_temp_crit_alert_thresh; + uint16_t under_temp_crit_alert_thresh; + uint16_t over_temp_warn_thresh; + uint16_t under_temp_warn_thresh; + uint16_t cor_vmem_err_warn_thresh; + uint16_t cor_pmem_err_warn_thresh; +} QEMU_PACKED CXLAlertConfig; + struct CXLType3Dev { /* Private */ PCIDevice parent_obj; @@ -563,6 +576,8 @@ struct CXLType3Dev { CXLCCI vdm_fm_owned_ld_mctp_cci; CXLCCI ld0_cci; + CXLAlertConfig alert_config; + /* PCIe link characteristics */ PCIExpLinkSpeed speed; PCIExpLinkWidth width; From abde58f8644491503c058c2ff0775613f251c8e6 Mon Sep 17 00:00:00 2001 From: Yuquan Wang Date: Wed, 5 Mar 2025 09:24:59 +0000 Subject: [PATCH 0862/2760] docs/cxl: Add serial number for persistent-memdev Add serial number parameter in the cxl persistent examples. Signed-off-by: Yuquan Wang Signed-off-by: Jonathan Cameron Message-Id: <20250305092501.191929-9-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/system/devices/cxl.rst | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/system/devices/cxl.rst b/docs/system/devices/cxl.rst index 882b036f5e..e307caf3f8 100644 --- a/docs/system/devices/cxl.rst +++ b/docs/system/devices/cxl.rst @@ -308,7 +308,7 @@ A very simple setup with just one directly attached CXL Type 3 Persistent Memory -object memory-backend-file,id=cxl-lsa1,share=on,mem-path=/tmp/lsa.raw,size=256M \ -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ -device cxl-rp,port=0,bus=cxl.1,id=root_port13,chassis=0,slot=2 \ - -device cxl-type3,bus=root_port13,persistent-memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem0 \ + -device cxl-type3,bus=root_port13,persistent-memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem0,sn=0x1 \ -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G A very simple setup with just one directly attached CXL Type 3 Volatile Memory device:: @@ -349,13 +349,13 @@ the CXL Type3 device directly attached (no switches).:: -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ -device pxb-cxl,bus_nr=222,bus=pcie.0,id=cxl.2 \ -device cxl-rp,port=0,bus=cxl.1,id=root_port13,chassis=0,slot=2 \ - -device cxl-type3,bus=root_port13,persistent-memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem0 \ + -device cxl-type3,bus=root_port13,persistent-memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem0,sn=0x1 \ -device cxl-rp,port=1,bus=cxl.1,id=root_port14,chassis=0,slot=3 \ - -device cxl-type3,bus=root_port14,persistent-memdev=cxl-mem2,lsa=cxl-lsa2,id=cxl-pmem1 \ + -device cxl-type3,bus=root_port14,persistent-memdev=cxl-mem2,lsa=cxl-lsa2,id=cxl-pmem1,sn=0x2 \ -device cxl-rp,port=0,bus=cxl.2,id=root_port15,chassis=0,slot=5 \ - -device cxl-type3,bus=root_port15,persistent-memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem2 \ + -device cxl-type3,bus=root_port15,persistent-memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem2,sn=0x3 \ -device cxl-rp,port=1,bus=cxl.2,id=root_port16,chassis=0,slot=6 \ - -device cxl-type3,bus=root_port16,persistent-memdev=cxl-mem4,lsa=cxl-lsa4,id=cxl-pmem3 \ + -device cxl-type3,bus=root_port16,persistent-memdev=cxl-mem4,lsa=cxl-lsa4,id=cxl-pmem3,sn=0x4 \ -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.targets.1=cxl.2,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=8k An example of 4 devices below a switch suitable for 1, 2 or 4 way interleave:: @@ -375,13 +375,13 @@ An example of 4 devices below a switch suitable for 1, 2 or 4 way interleave:: -device cxl-rp,port=1,bus=cxl.1,id=root_port1,chassis=0,slot=1 \ -device cxl-upstream,bus=root_port0,id=us0 \ -device cxl-downstream,port=0,bus=us0,id=swport0,chassis=0,slot=4 \ - -device cxl-type3,bus=swport0,persistent-memdev=cxl-mem0,lsa=cxl-lsa0,id=cxl-pmem0 \ + -device cxl-type3,bus=swport0,persistent-memdev=cxl-mem0,lsa=cxl-lsa0,id=cxl-pmem0,sn=0x1 \ -device cxl-downstream,port=1,bus=us0,id=swport1,chassis=0,slot=5 \ - -device cxl-type3,bus=swport1,persistent-memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem1 \ + -device cxl-type3,bus=swport1,persistent-memdev=cxl-mem1,lsa=cxl-lsa1,id=cxl-pmem1,sn=0x2 \ -device cxl-downstream,port=2,bus=us0,id=swport2,chassis=0,slot=6 \ - -device cxl-type3,bus=swport2,persistent-memdev=cxl-mem2,lsa=cxl-lsa2,id=cxl-pmem2 \ + -device cxl-type3,bus=swport2,persistent-memdev=cxl-mem2,lsa=cxl-lsa2,id=cxl-pmem2,sn=0x3 \ -device cxl-downstream,port=3,bus=us0,id=swport3,chassis=0,slot=7 \ - -device cxl-type3,bus=swport3,persistent-memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem3 \ + -device cxl-type3,bus=swport3,persistent-memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem3,sn=0x4 \ -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=4k Deprecations From 3a031e395dc65239d031890d038bc354af61dc35 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 14 Mar 2025 15:14:50 +0900 Subject: [PATCH 0863/2760] hw/pci: Do not add ROM BAR for SR-IOV VF A SR-IOV VF cannot have a ROM BAR. Co-developed-by: Yui Washizu Signed-off-by: Akihiko Odaki Message-Id: <20250314-sriov-v9-1-57dae8ae3ab5@daynix.com> Tested-by: Yui Washizu Tested-by: Pasha Tatashin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index fe38c4c028..6d9d3ce90f 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2522,6 +2522,14 @@ static void pci_add_option_rom(PCIDevice *pdev, bool is_default_rom, return; } + if (pci_is_vf(pdev)) { + if (pdev->rom_bar > 0) { + error_setg(errp, "ROM BAR cannot be enabled for SR-IOV VF"); + } + + return; + } + if (load_file || pdev->romsize == UINT32_MAX) { path = qemu_find_file(QEMU_FILE_TYPE_BIOS, pdev->romfile); if (path == NULL) { From a5745ac183e606937f22bfbf59a7f18e74f3c464 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 14 Mar 2025 15:14:51 +0900 Subject: [PATCH 0864/2760] hw/pci: Fix SR-IOV VF number calculation pci_config_get_bar_addr() had a division by vf_stride. vf_stride needs to be non-zero when there are multiple VFs, but the specification does not prohibit to make it zero when there is only one VF. Do not perform the division for the first VF to avoid division by zero. Signed-off-by: Akihiko Odaki Message-Id: <20250314-sriov-v9-2-57dae8ae3ab5@daynix.com> Tested-by: Yui Washizu Tested-by: Pasha Tatashin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 6d9d3ce90f..039cf1e256 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1594,7 +1594,11 @@ static pcibus_t pci_config_get_bar_addr(PCIDevice *d, int reg, pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_OFFSET); uint16_t vf_stride = pci_get_word(pf->config + sriov_cap + PCI_SRIOV_VF_STRIDE); - uint32_t vf_num = (d->devfn - (pf->devfn + vf_offset)) / vf_stride; + uint32_t vf_num = d->devfn - (pf->devfn + vf_offset); + + if (vf_num) { + vf_num /= vf_stride; + } if (type & PCI_BASE_ADDRESS_MEM_TYPE_64) { new_addr = pci_get_quad(pf->config + bar); From 92b6ce3dba26bd3734b528bda7f9a9dee2ee7bb4 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 14 Mar 2025 15:14:52 +0900 Subject: [PATCH 0865/2760] pcie_sriov: Ensure PF and VF are mutually exclusive A device cannot be a SR-IOV PF and a VF at the same time. Signed-off-by: Akihiko Odaki Message-Id: <20250314-sriov-v9-3-57dae8ae3ab5@daynix.com> Tested-by: Yui Washizu Tested-by: Pasha Tatashin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie_sriov.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 1eb4358256..109b2ebccc 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -42,6 +42,11 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, uint8_t *cfg = dev->config + offset; uint8_t *wmask; + if (pci_is_vf(dev)) { + error_setg(errp, "a device cannot be a SR-IOV PF and a VF at the same time"); + return false; + } + if (total_vfs && (uint32_t)devfn + (uint32_t)(total_vfs - 1) * vf_stride >= PCI_DEVFN_MAX) { error_setg(errp, "VF addr overflows"); From d2f5bb7849e463646173564fdadf40e32a32bf6e Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 14 Mar 2025 15:14:53 +0900 Subject: [PATCH 0866/2760] pcie_sriov: Check PCI Express for SR-IOV PF SR-IOV requires PCI Express. Signed-off-by: Akihiko Odaki Message-Id: <20250314-sriov-v9-4-57dae8ae3ab5@daynix.com> Tested-by: Yui Washizu Tested-by: Pasha Tatashin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie_sriov.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 109b2ebccc..a5b546abe8 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -42,6 +42,11 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, uint8_t *cfg = dev->config + offset; uint8_t *wmask; + if (!pci_is_express(dev)) { + error_setg(errp, "PCI Express is required for SR-IOV PF"); + return false; + } + if (pci_is_vf(dev)) { error_setg(errp, "a device cannot be a SR-IOV PF and a VF at the same time"); return false; From 19e55471d4e8a494cfda7470e701829e3a873bdc Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 14 Mar 2025 15:14:54 +0900 Subject: [PATCH 0867/2760] pcie_sriov: Allow user to create SR-IOV device A user can create a SR-IOV device by specifying the PF with the sriov-pf property of the VFs. The VFs must be added before the PF. A user-creatable VF must have PCIDeviceClass::sriov_vf_user_creatable set. Such a VF cannot refer to the PF because it is created before the PF. A PF that user-creatable VFs can be attached calls pcie_sriov_pf_init_from_user_created_vfs() during realization and pcie_sriov_pf_exit() when exiting. Signed-off-by: Akihiko Odaki Message-Id: <20250314-sriov-v9-5-57dae8ae3ab5@daynix.com> Tested-by: Yui Washizu Tested-by: Pasha Tatashin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 62 ++++---- hw/pci/pcie_sriov.c | 278 +++++++++++++++++++++++++++++------- include/hw/pci/pci_device.h | 6 +- include/hw/pci/pcie_sriov.h | 18 +++ 4 files changed, 286 insertions(+), 78 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 039cf1e256..de07096347 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -101,6 +101,7 @@ static const Property pci_props[] = { QEMU_PCIE_ARI_NEXTFN_1_BITNR, false), DEFINE_PROP_SIZE32("x-max-bounce-buffer-size", PCIDevice, max_bounce_buffer_size, DEFAULT_MAX_BOUNCE_BUFFER_SIZE), + DEFINE_PROP_STRING("sriov-pf", PCIDevice, sriov_pf), DEFINE_PROP_BIT("x-pcie-ext-tag", PCIDevice, cap_present, QEMU_PCIE_EXT_TAG_BITNR, true), { .name = "busnr", .info = &prop_pci_busnr }, @@ -1112,13 +1113,8 @@ static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp) dev->config[PCI_HEADER_TYPE] |= PCI_HEADER_TYPE_MULTI_FUNCTION; } - /* - * With SR/IOV and ARI, a device at function 0 need not be a multifunction - * device, as it may just be a VF that ended up with function 0 in - * the legacy PCI interpretation. Avoid failing in such cases: - */ - if (pci_is_vf(dev) && - dev->exp.sriov_vf.pf->cap_present & QEMU_PCI_CAP_MULTIFUNCTION) { + /* SR/IOV is not handled here. */ + if (pci_is_vf(dev)) { return; } @@ -1151,7 +1147,8 @@ static void pci_init_multifunction(PCIBus *bus, PCIDevice *dev, Error **errp) } /* function 0 indicates single function, so function > 0 must be NULL */ for (func = 1; func < PCI_FUNC_MAX; ++func) { - if (bus->devices[PCI_DEVFN(slot, func)]) { + PCIDevice *device = bus->devices[PCI_DEVFN(slot, func)]; + if (device && !pci_is_vf(device)) { error_setg(errp, "PCI: %x.0 indicates single function, " "but %x.%x is already populated.", slot, slot, func); @@ -1439,6 +1436,7 @@ static void pci_qdev_unrealize(DeviceState *dev) pci_unregister_io_regions(pci_dev); pci_del_option_rom(pci_dev); + pcie_sriov_unregister_device(pci_dev); if (pc->exit) { pc->exit(pci_dev); @@ -1470,7 +1468,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, pcibus_t size = memory_region_size(memory); uint8_t hdr_type; - assert(!pci_is_vf(pci_dev)); /* VFs must use pcie_sriov_vf_register_bar */ assert(region_num >= 0); assert(region_num < PCI_NUM_REGIONS); assert(is_power_of_2(size)); @@ -1482,7 +1479,6 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, r = &pci_dev->io_regions[region_num]; assert(!r->size); - r->addr = PCI_BAR_UNMAPPED; r->size = size; r->type = type; r->memory = memory; @@ -1490,22 +1486,35 @@ void pci_register_bar(PCIDevice *pci_dev, int region_num, ? pci_get_bus(pci_dev)->address_space_io : pci_get_bus(pci_dev)->address_space_mem; - wmask = ~(size - 1); - if (region_num == PCI_ROM_SLOT) { - /* ROM enable bit is writable */ - wmask |= PCI_ROM_ADDRESS_ENABLE; - } - - addr = pci_bar(pci_dev, region_num); - pci_set_long(pci_dev->config + addr, type); + if (pci_is_vf(pci_dev)) { + PCIDevice *pf = pci_dev->exp.sriov_vf.pf; + assert(!pf || type == pf->exp.sriov_pf.vf_bar_type[region_num]); - if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && - r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { - pci_set_quad(pci_dev->wmask + addr, wmask); - pci_set_quad(pci_dev->cmask + addr, ~0ULL); + r->addr = pci_bar_address(pci_dev, region_num, r->type, r->size); + if (r->addr != PCI_BAR_UNMAPPED) { + memory_region_add_subregion_overlap(r->address_space, + r->addr, r->memory, 1); + } } else { - pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); - pci_set_long(pci_dev->cmask + addr, 0xffffffff); + r->addr = PCI_BAR_UNMAPPED; + + wmask = ~(size - 1); + if (region_num == PCI_ROM_SLOT) { + /* ROM enable bit is writable */ + wmask |= PCI_ROM_ADDRESS_ENABLE; + } + + addr = pci_bar(pci_dev, region_num); + pci_set_long(pci_dev->config + addr, type); + + if (!(r->type & PCI_BASE_ADDRESS_SPACE_IO) && + r->type & PCI_BASE_ADDRESS_MEM_TYPE_64) { + pci_set_quad(pci_dev->wmask + addr, wmask); + pci_set_quad(pci_dev->cmask + addr, ~0ULL); + } else { + pci_set_long(pci_dev->wmask + addr, wmask & 0xffffffff); + pci_set_long(pci_dev->cmask + addr, 0xffffffff); + } } } @@ -2272,6 +2281,11 @@ static void pci_qdev_realize(DeviceState *qdev, Error **errp) } } + if (!pcie_sriov_register_device(pci_dev, errp)) { + pci_qdev_unrealize(DEVICE(pci_dev)); + return; + } + /* * A PCIe Downstream Port that do not have ARI Forwarding enabled must * associate only Device 0 with the device attached to the bus diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index a5b546abe8..08f707e847 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -15,11 +15,12 @@ #include "hw/pci/pcie.h" #include "hw/pci/pci_bus.h" #include "hw/qdev-properties.h" -#include "qemu/error-report.h" #include "qemu/range.h" #include "qapi/error.h" #include "trace.h" +static GHashTable *pfs; + static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs) { for (uint16_t i = 0; i < total_vfs; i++) { @@ -31,13 +32,43 @@ static void unparent_vfs(PCIDevice *dev, uint16_t total_vfs) dev->exp.sriov_pf.vf = NULL; } -bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, - const char *vfname, uint16_t vf_dev_id, - uint16_t init_vfs, uint16_t total_vfs, - uint16_t vf_offset, uint16_t vf_stride, - Error **errp) +static void register_vfs(PCIDevice *dev) +{ + uint16_t num_vfs; + uint16_t i; + uint16_t sriov_cap = dev->exp.sriov_cap; + + assert(sriov_cap > 0); + num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); + + trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn), num_vfs); + for (i = 0; i < num_vfs; i++) { + pci_set_enabled(dev->exp.sriov_pf.vf[i], true); + } + + pci_set_word(dev->wmask + sriov_cap + PCI_SRIOV_NUM_VF, 0); +} + +static void unregister_vfs(PCIDevice *dev) +{ + uint8_t *cfg = dev->config + dev->exp.sriov_cap; + uint16_t i; + + trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), + PCI_FUNC(dev->devfn)); + for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) { + pci_set_enabled(dev->exp.sriov_pf.vf[i], false); + } + + pci_set_word(dev->wmask + dev->exp.sriov_cap + PCI_SRIOV_NUM_VF, 0xffff); +} + +static bool pcie_sriov_pf_init_common(PCIDevice *dev, uint16_t offset, + uint16_t vf_dev_id, uint16_t init_vfs, + uint16_t total_vfs, uint16_t vf_offset, + uint16_t vf_stride, Error **errp) { - BusState *bus = qdev_get_parent_bus(&dev->qdev); int32_t devfn = dev->devfn + vf_offset; uint8_t *cfg = dev->config + offset; uint8_t *wmask; @@ -94,6 +125,28 @@ bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, qdev_prop_set_bit(&dev->qdev, "multifunction", true); + return true; +} + +bool pcie_sriov_pf_init(PCIDevice *dev, uint16_t offset, + const char *vfname, uint16_t vf_dev_id, + uint16_t init_vfs, uint16_t total_vfs, + uint16_t vf_offset, uint16_t vf_stride, + Error **errp) +{ + BusState *bus = qdev_get_parent_bus(&dev->qdev); + int32_t devfn = dev->devfn + vf_offset; + + if (pfs && g_hash_table_contains(pfs, dev->qdev.id)) { + error_setg(errp, "attaching user-created SR-IOV VF unsupported"); + return false; + } + + if (!pcie_sriov_pf_init_common(dev, offset, vf_dev_id, init_vfs, + total_vfs, vf_offset, vf_stride, errp)) { + return false; + } + dev->exp.sriov_pf.vf = g_new(PCIDevice *, total_vfs); for (uint16_t i = 0; i < total_vfs; i++) { @@ -123,7 +176,22 @@ void pcie_sriov_pf_exit(PCIDevice *dev) { uint8_t *cfg = dev->config + dev->exp.sriov_cap; - unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); + if (dev->exp.sriov_pf.vf_user_created) { + uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID); + uint16_t total_vfs = pci_get_word(dev->config + PCI_SRIOV_TOTAL_VF); + uint16_t vf_dev_id = pci_get_word(dev->config + PCI_SRIOV_VF_DID); + + unregister_vfs(dev); + + for (uint16_t i = 0; i < total_vfs; i++) { + dev->exp.sriov_pf.vf[i]->exp.sriov_vf.pf = NULL; + + pci_config_set_vendor_id(dev->exp.sriov_pf.vf[i]->config, ven_id); + pci_config_set_device_id(dev->exp.sriov_pf.vf[i]->config, vf_dev_id); + } + } else { + unparent_vfs(dev, pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)); + } } void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, @@ -156,69 +224,173 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, MemoryRegion *memory) { - PCIIORegion *r; - PCIBus *bus = pci_get_bus(dev); uint8_t type; - pcibus_t size = memory_region_size(memory); - assert(pci_is_vf(dev)); /* PFs must use pci_register_bar */ - assert(region_num >= 0); - assert(region_num < PCI_NUM_REGIONS); + assert(dev->exp.sriov_vf.pf); type = dev->exp.sriov_vf.pf->exp.sriov_pf.vf_bar_type[region_num]; - if (!is_power_of_2(size)) { - error_report("%s: PCI region size must be a power" - " of two - type=0x%x, size=0x%"FMT_PCIBUS, - __func__, type, size); - exit(1); - } + return pci_register_bar(dev, region_num, type, memory); +} - r = &dev->io_regions[region_num]; - r->memory = memory; - r->address_space = - type & PCI_BASE_ADDRESS_SPACE_IO - ? bus->address_space_io - : bus->address_space_mem; - r->size = size; - r->type = type; - - r->addr = pci_bar_address(dev, region_num, r->type, r->size); - if (r->addr != PCI_BAR_UNMAPPED) { - memory_region_add_subregion_overlap(r->address_space, - r->addr, r->memory, 1); - } +static gint compare_vf_devfns(gconstpointer a, gconstpointer b) +{ + return (*(PCIDevice **)a)->devfn - (*(PCIDevice **)b)->devfn; } -static void register_vfs(PCIDevice *dev) +int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, + uint16_t offset, + Error **errp) { - uint16_t num_vfs; + GPtrArray *pf; + PCIDevice **vfs; + BusState *bus = qdev_get_parent_bus(DEVICE(dev)); + uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID); + uint16_t vf_dev_id; + uint16_t vf_offset; + uint16_t vf_stride; uint16_t i; - uint16_t sriov_cap = dev->exp.sriov_cap; - assert(sriov_cap > 0); - num_vfs = pci_get_word(dev->config + sriov_cap + PCI_SRIOV_NUM_VF); + if (!pfs || !dev->qdev.id) { + return 0; + } - trace_sriov_register_vfs(dev->name, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn), num_vfs); - for (i = 0; i < num_vfs; i++) { - pci_set_enabled(dev->exp.sriov_pf.vf[i], true); + pf = g_hash_table_lookup(pfs, dev->qdev.id); + if (!pf) { + return 0; } - pci_set_word(dev->wmask + sriov_cap + PCI_SRIOV_NUM_VF, 0); + if (pf->len > UINT16_MAX) { + error_setg(errp, "too many VFs"); + return -1; + } + + g_ptr_array_sort(pf, compare_vf_devfns); + vfs = (void *)pf->pdata; + + if (vfs[0]->devfn <= dev->devfn) { + error_setg(errp, "a VF function number is less than the PF function number"); + return -1; + } + + vf_dev_id = pci_get_word(vfs[0]->config + PCI_DEVICE_ID); + vf_offset = vfs[0]->devfn - dev->devfn; + vf_stride = pf->len < 2 ? 0 : vfs[1]->devfn - vfs[0]->devfn; + + for (i = 0; i < pf->len; i++) { + if (bus != qdev_get_parent_bus(&vfs[i]->qdev)) { + error_setg(errp, "SR-IOV VF parent bus mismatches with PF"); + return -1; + } + + if (ven_id != pci_get_word(vfs[i]->config + PCI_VENDOR_ID)) { + error_setg(errp, "SR-IOV VF vendor ID mismatches with PF"); + return -1; + } + + if (vf_dev_id != pci_get_word(vfs[i]->config + PCI_DEVICE_ID)) { + error_setg(errp, "inconsistent SR-IOV VF device IDs"); + return -1; + } + + for (size_t j = 0; j < PCI_NUM_REGIONS; j++) { + if (vfs[i]->io_regions[j].size != vfs[0]->io_regions[j].size || + vfs[i]->io_regions[j].type != vfs[0]->io_regions[j].type) { + error_setg(errp, "inconsistent SR-IOV BARs"); + return -1; + } + } + + if (vfs[i]->devfn - vfs[0]->devfn != vf_stride * i) { + error_setg(errp, "inconsistent SR-IOV stride"); + return -1; + } + } + + if (!pcie_sriov_pf_init_common(dev, offset, vf_dev_id, pf->len, + pf->len, vf_offset, vf_stride, errp)) { + return -1; + } + + for (i = 0; i < pf->len; i++) { + vfs[i]->exp.sriov_vf.pf = dev; + vfs[i]->exp.sriov_vf.vf_number = i; + + /* set vid/did according to sr/iov spec - they are not used */ + pci_config_set_vendor_id(vfs[i]->config, 0xffff); + pci_config_set_device_id(vfs[i]->config, 0xffff); + } + + dev->exp.sriov_pf.vf = vfs; + dev->exp.sriov_pf.vf_user_created = true; + + for (i = 0; i < PCI_NUM_REGIONS; i++) { + PCIIORegion *region = &vfs[0]->io_regions[i]; + + if (region->size) { + pcie_sriov_pf_init_vf_bar(dev, i, region->type, region->size); + } + } + + return PCI_EXT_CAP_SRIOV_SIZEOF; } -static void unregister_vfs(PCIDevice *dev) +bool pcie_sriov_register_device(PCIDevice *dev, Error **errp) { - uint8_t *cfg = dev->config + dev->exp.sriov_cap; - uint16_t i; + if (!dev->exp.sriov_pf.vf && dev->qdev.id && + pfs && g_hash_table_contains(pfs, dev->qdev.id)) { + error_setg(errp, "attaching user-created SR-IOV VF unsupported"); + return false; + } - trace_sriov_unregister_vfs(dev->name, PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); - for (i = 0; i < pci_get_word(cfg + PCI_SRIOV_TOTAL_VF); i++) { - pci_set_enabled(dev->exp.sriov_pf.vf[i], false); + if (dev->sriov_pf) { + PCIDevice *pci_pf; + GPtrArray *pf; + + if (!PCI_DEVICE_GET_CLASS(dev)->sriov_vf_user_creatable) { + error_setg(errp, "user cannot create SR-IOV VF with this device type"); + return false; + } + + if (!pci_is_express(dev)) { + error_setg(errp, "PCI Express is required for SR-IOV VF"); + return false; + } + + if (!pci_qdev_find_device(dev->sriov_pf, &pci_pf)) { + error_setg(errp, "PCI device specified as SR-IOV PF already exists"); + return false; + } + + if (!pfs) { + pfs = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + } + + pf = g_hash_table_lookup(pfs, dev->sriov_pf); + if (!pf) { + pf = g_ptr_array_new(); + g_hash_table_insert(pfs, g_strdup(dev->sriov_pf), pf); + } + + g_ptr_array_add(pf, dev); } - pci_set_word(dev->wmask + dev->exp.sriov_cap + PCI_SRIOV_NUM_VF, 0xffff); + return true; +} + +void pcie_sriov_unregister_device(PCIDevice *dev) +{ + if (dev->sriov_pf && pfs) { + GPtrArray *pf = g_hash_table_lookup(pfs, dev->sriov_pf); + + if (pf) { + g_ptr_array_remove_fast(pf, dev); + + if (!pf->len) { + g_hash_table_remove(pfs, dev->sriov_pf); + g_ptr_array_free(pf, FALSE); + } + } + } } void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, @@ -314,7 +486,7 @@ void pcie_sriov_pf_add_sup_pgsize(PCIDevice *dev, uint16_t opt_sup_pgsize) uint16_t pcie_sriov_vf_number(PCIDevice *dev) { - assert(pci_is_vf(dev)); + assert(dev->exp.sriov_vf.pf); return dev->exp.sriov_vf.vf_number; } diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index 345b12eaac..e41d95b0b0 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -38,6 +38,8 @@ struct PCIDeviceClass { uint16_t subsystem_id; /* only for header type = 0 */ const char *romfile; /* rom bar */ + + bool sriov_vf_user_creatable; }; enum PCIReqIDType { @@ -177,6 +179,8 @@ struct PCIDevice { * realizing the device. */ uint32_t max_bounce_buffer_size; + + char *sriov_pf; }; static inline int pci_intx(PCIDevice *pci_dev) @@ -209,7 +213,7 @@ static inline int pci_is_express_downstream_port(const PCIDevice *d) static inline int pci_is_vf(const PCIDevice *d) { - return d->exp.sriov_vf.pf != NULL; + return d->sriov_pf || d->exp.sriov_vf.pf != NULL; } static inline uint32_t pci_config_size(const PCIDevice *d) diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index c5d2d318d3..f75b8f22ee 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -18,6 +18,7 @@ typedef struct PCIESriovPF { uint8_t vf_bar_type[PCI_NUM_REGIONS]; /* Store type for each VF bar */ PCIDevice **vf; /* Pointer to an array of num_vfs VF devices */ + bool vf_user_created; /* If VFs are created by user */ } PCIESriovPF; typedef struct PCIESriovVF { @@ -40,6 +41,23 @@ void pcie_sriov_pf_init_vf_bar(PCIDevice *dev, int region_num, void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, MemoryRegion *memory); +/** + * pcie_sriov_pf_init_from_user_created_vfs() - Initialize PF with user-created + * VFs. + * @dev: A PCIe device being realized. + * @offset: The offset of the SR-IOV capability. + * @errp: pointer to Error*, to store an error if it happens. + * + * Return: The size of added capability. 0 if the user did not create VFs. + * -1 if failed. + */ +int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, + uint16_t offset, + Error **errp); + +bool pcie_sriov_register_device(PCIDevice *dev, Error **errp); +void pcie_sriov_unregister_device(PCIDevice *dev); + /* * Default (minimal) page size support values * as required by the SR/IOV standard: From 3f9cfaa92c96d604e98f16ade5af4742460e4c0f Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 14 Mar 2025 15:14:55 +0900 Subject: [PATCH 0868/2760] virtio-pci: Implement SR-IOV PF Allow user to attach SR-IOV VF to a virtio-pci PF. Signed-off-by: Akihiko Odaki Message-Id: <20250314-sriov-v9-6-57dae8ae3ab5@daynix.com> Tested-by: Yui Washizu Tested-by: Pasha Tatashin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-pci.c | 20 +++++++++++++++----- include/hw/virtio/virtio-pci.h | 1 + 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 0fa8fe4955..fee65d3645 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1962,6 +1962,7 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) uint8_t *config; uint32_t size; VirtIODevice *vdev = virtio_bus_get_device(bus); + int16_t res; /* * Virtio capabilities present without @@ -2109,6 +2110,14 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) pci_register_bar(&proxy->pci_dev, proxy->legacy_io_bar_idx, PCI_BASE_ADDRESS_SPACE_IO, &proxy->bar); } + + res = pcie_sriov_pf_init_from_user_created_vfs(&proxy->pci_dev, + proxy->last_pcie_cap_offset, + errp); + if (res > 0) { + proxy->last_pcie_cap_offset += res; + virtio_add_feature(&vdev->host_features, VIRTIO_F_SR_IOV); + } } static void virtio_pci_device_unplugged(DeviceState *d) @@ -2199,7 +2208,7 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) if (pcie_port && pci_is_express(pci_dev)) { int pos; - uint16_t last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE; + proxy->last_pcie_cap_offset = PCI_CONFIG_SPACE_SIZE; pos = pcie_endpoint_cap_init(pci_dev, 0); assert(pos > 0); @@ -2216,9 +2225,9 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) pci_set_word(pci_dev->config + pos + PCI_PM_PMC, 0x3); if (proxy->flags & VIRTIO_PCI_FLAG_AER) { - pcie_aer_init(pci_dev, PCI_ERR_VER, last_pcie_cap_offset, + pcie_aer_init(pci_dev, PCI_ERR_VER, proxy->last_pcie_cap_offset, PCI_ERR_SIZEOF, NULL); - last_pcie_cap_offset += PCI_ERR_SIZEOF; + proxy->last_pcie_cap_offset += PCI_ERR_SIZEOF; } if (proxy->flags & VIRTIO_PCI_FLAG_INIT_DEVERR) { @@ -2243,9 +2252,9 @@ static void virtio_pci_realize(PCIDevice *pci_dev, Error **errp) } if (proxy->flags & VIRTIO_PCI_FLAG_ATS) { - pcie_ats_init(pci_dev, last_pcie_cap_offset, + pcie_ats_init(pci_dev, proxy->last_pcie_cap_offset, proxy->flags & VIRTIO_PCI_FLAG_ATS_PAGE_ALIGNED); - last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF; + proxy->last_pcie_cap_offset += PCI_EXT_CAP_ATS_SIZEOF; } if (proxy->flags & VIRTIO_PCI_FLAG_INIT_FLR) { @@ -2273,6 +2282,7 @@ static void virtio_pci_exit(PCIDevice *pci_dev) !pci_bus_is_root(pci_get_bus(pci_dev)); bool modern_pio = proxy->flags & VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY; + pcie_sriov_pf_exit(&proxy->pci_dev); msix_uninit_exclusive_bar(pci_dev); if (proxy->flags & VIRTIO_PCI_FLAG_AER && pcie_port && pci_is_express(pci_dev)) { diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h index 31ec144509..1dbc3851b0 100644 --- a/include/hw/virtio/virtio-pci.h +++ b/include/hw/virtio/virtio-pci.h @@ -155,6 +155,7 @@ struct VirtIOPCIProxy { uint32_t modern_io_bar_idx; uint32_t modern_mem_bar_idx; int config_cap; + uint16_t last_pcie_cap_offset; uint32_t flags; bool disable_modern; bool ignore_backend_features; From 49f7cb18db0790f7f32cb85dc35ffc25e44a828b Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 14 Mar 2025 15:14:56 +0900 Subject: [PATCH 0869/2760] virtio-net: Implement SR-IOV VF A virtio-net device can be added as a SR-IOV VF to another virtio-pci device that will be the PF. Signed-off-by: Akihiko Odaki Message-Id: <20250314-sriov-v9-7-57dae8ae3ab5@daynix.com> Tested-by: Yui Washizu Tested-by: Pasha Tatashin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio-net-pci.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/virtio/virtio-net-pci.c b/hw/virtio/virtio-net-pci.c index 8cf9788bc3..f857a84f11 100644 --- a/hw/virtio/virtio-net-pci.c +++ b/hw/virtio/virtio-net-pci.c @@ -74,6 +74,7 @@ static void virtio_net_pci_class_init(ObjectClass *klass, const void *data) k->device_id = PCI_DEVICE_ID_VIRTIO_NET; k->revision = VIRTIO_PCI_ABI_VERSION; k->class_id = PCI_CLASS_NETWORK_ETHERNET; + k->sriov_vf_user_creatable = true; set_bit(DEVICE_CATEGORY_NETWORK, dc->categories); device_class_set_props(dc, virtio_net_properties); vpciklass->realize = virtio_net_pci_realize; From 6f9bebf1dc6b54b63be739ea247b3942f841b9e3 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 14 Mar 2025 15:14:57 +0900 Subject: [PATCH 0870/2760] docs: Document composable SR-IOV device Signed-off-by: Akihiko Odaki Message-Id: <20250314-sriov-v9-8-57dae8ae3ab5@daynix.com> Tested-by: Yui Washizu Tested-by: Pasha Tatashin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 1 + docs/system/index.rst | 1 + docs/system/sriov.rst | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 docs/system/sriov.rst diff --git a/MAINTAINERS b/MAINTAINERS index 6dacd6d004..b579358cdd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2058,6 +2058,7 @@ F: hw/pci-bridge/* F: qapi/pci.json F: docs/pci* F: docs/specs/*pci* +F: docs/system/sriov.rst PCIE DOE M: Huai-Cheng Kuo diff --git a/docs/system/index.rst b/docs/system/index.rst index c21065e519..718e9d3c56 100644 --- a/docs/system/index.rst +++ b/docs/system/index.rst @@ -39,3 +39,4 @@ or Hypervisor.Framework. multi-process confidential-guest-support vm-templating + sriov diff --git a/docs/system/sriov.rst b/docs/system/sriov.rst new file mode 100644 index 0000000000..a851a66a4b --- /dev/null +++ b/docs/system/sriov.rst @@ -0,0 +1,36 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Compsable SR-IOV device +======================= + +SR-IOV (Single Root I/O Virtualization) is an optional extended capability of a +PCI Express device. It allows a single physical function (PF) to appear as +multiple virtual functions (VFs) for the main purpose of eliminating software +overhead in I/O from virtual machines. + +There are devices with predefined SR-IOV configurations, but it is also possible +to compose an SR-IOV device yourself. Composing an SR-IOV device is currently +only supported by virtio-net-pci. + +Users can configure an SR-IOV-capable virtio-net device by adding +virtio-net-pci functions to a bus. Below is a command line example: + +.. code-block:: shell + + -netdev user,id=n -netdev user,id=o + -netdev user,id=p -netdev user,id=q + -device pcie-root-port,id=b + -device virtio-net-pci,bus=b,addr=0x0.0x3,netdev=q,sriov-pf=f + -device virtio-net-pci,bus=b,addr=0x0.0x2,netdev=p,sriov-pf=f + -device virtio-net-pci,bus=b,addr=0x0.0x1,netdev=o,sriov-pf=f + -device virtio-net-pci,bus=b,addr=0x0.0x0,netdev=n,id=f + +The VFs specify the paired PF with ``sriov-pf`` property. The PF must be +added after all VFs. It is the user's responsibility to ensure that VFs have +function numbers larger than one of the PF, and that the function numbers +have a consistent stride. + +You may also need to perform additional steps to activate the SR-IOV feature on +your guest. For Linux, refer to [1]_. + +.. [1] https://docs.kernel.org/PCI/pci-iov-howto.html From d0c280d3fac644c26a86d2fb70c5920b3d5bef85 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 14 Mar 2025 15:14:58 +0900 Subject: [PATCH 0871/2760] pcie_sriov: Make a PCI device with user-created VF ARI-capable Signed-off-by: Akihiko Odaki Message-Id: <20250314-sriov-v9-9-57dae8ae3ab5@daynix.com> Tested-by: Yui Washizu Tested-by: Pasha Tatashin Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- docs/system/sriov.rst | 3 ++- hw/pci/pcie_sriov.c | 8 +++++++- hw/virtio/virtio-pci.c | 16 ++++++++++------ include/hw/pci/pcie_sriov.h | 7 +++++-- 4 files changed, 24 insertions(+), 10 deletions(-) diff --git a/docs/system/sriov.rst b/docs/system/sriov.rst index a851a66a4b..d12178f3c3 100644 --- a/docs/system/sriov.rst +++ b/docs/system/sriov.rst @@ -28,7 +28,8 @@ virtio-net-pci functions to a bus. Below is a command line example: The VFs specify the paired PF with ``sriov-pf`` property. The PF must be added after all VFs. It is the user's responsibility to ensure that VFs have function numbers larger than one of the PF, and that the function numbers -have a consistent stride. +have a consistent stride. Both the PF and VFs are ARI-capable so you can have +255 VFs at maximum. You may also need to perform additional steps to activate the SR-IOV feature on your guest. For Linux, refer to [1]_. diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 08f707e847..3ad18744f4 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -245,6 +245,7 @@ int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, PCIDevice **vfs; BusState *bus = qdev_get_parent_bus(DEVICE(dev)); uint16_t ven_id = pci_get_word(dev->config + PCI_VENDOR_ID); + uint16_t size = PCI_EXT_CAP_SRIOV_SIZEOF; uint16_t vf_dev_id; uint16_t vf_offset; uint16_t vf_stride; @@ -311,6 +312,11 @@ int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, return -1; } + if (!pcie_find_capability(dev, PCI_EXT_CAP_ID_ARI)) { + pcie_ari_init(dev, offset + size); + size += PCI_ARI_SIZEOF; + } + for (i = 0; i < pf->len; i++) { vfs[i]->exp.sriov_vf.pf = dev; vfs[i]->exp.sriov_vf.vf_number = i; @@ -331,7 +337,7 @@ int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, } } - return PCI_EXT_CAP_SRIOV_SIZEOF; + return size; } bool pcie_sriov_register_device(PCIDevice *dev, Error **errp) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index fee65d3645..9b48aa8c3e 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2111,12 +2111,16 @@ static void virtio_pci_device_plugged(DeviceState *d, Error **errp) PCI_BASE_ADDRESS_SPACE_IO, &proxy->bar); } - res = pcie_sriov_pf_init_from_user_created_vfs(&proxy->pci_dev, - proxy->last_pcie_cap_offset, - errp); - if (res > 0) { - proxy->last_pcie_cap_offset += res; - virtio_add_feature(&vdev->host_features, VIRTIO_F_SR_IOV); + if (pci_is_vf(&proxy->pci_dev)) { + pcie_ari_init(&proxy->pci_dev, proxy->last_pcie_cap_offset); + proxy->last_pcie_cap_offset += PCI_ARI_SIZEOF; + } else { + res = pcie_sriov_pf_init_from_user_created_vfs( + &proxy->pci_dev, proxy->last_pcie_cap_offset, errp); + if (res > 0) { + proxy->last_pcie_cap_offset += res; + virtio_add_feature(&vdev->host_features, VIRTIO_F_SR_IOV); + } } } diff --git a/include/hw/pci/pcie_sriov.h b/include/hw/pci/pcie_sriov.h index f75b8f22ee..aeaa38cf34 100644 --- a/include/hw/pci/pcie_sriov.h +++ b/include/hw/pci/pcie_sriov.h @@ -43,12 +43,15 @@ void pcie_sriov_vf_register_bar(PCIDevice *dev, int region_num, /** * pcie_sriov_pf_init_from_user_created_vfs() - Initialize PF with user-created - * VFs. + * VFs, adding ARI to PF * @dev: A PCIe device being realized. * @offset: The offset of the SR-IOV capability. * @errp: pointer to Error*, to store an error if it happens. * - * Return: The size of added capability. 0 if the user did not create VFs. + * Initializes a PF with user-created VFs, adding the ARI extended capability to + * the PF. The VFs should call pcie_ari_init() to form an ARI device. + * + * Return: The size of added capabilities. 0 if the user did not create VFs. * -1 if failed. */ int16_t pcie_sriov_pf_init_from_user_created_vfs(PCIDevice *dev, From 8717987fb528ff704e275a1a99f59a20e0b272f5 Mon Sep 17 00:00:00 2001 From: Stephen Bates Date: Tue, 15 Apr 2025 10:29:56 -0600 Subject: [PATCH 0872/2760] pci-testdev.c: Add membar-backed option for backing membar The pci-testdev device allows for an optional BAR. We have historically used this without backing to test that systems and OSes can accomodate large PCI BARs. However to help test p2pdma operations it is helpful to add an option to back this BAR with host memory. We add a membar-backed boolean parameter and when set to true or on we do a host RAM backing. The default is false which ensures backward compatability. Signed-off-by: Stephen Bates Message-Id: Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/misc/pci-testdev.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/hw/misc/pci-testdev.c b/hw/misc/pci-testdev.c index 3f6a8bba84..ba71c5069f 100644 --- a/hw/misc/pci-testdev.c +++ b/hw/misc/pci-testdev.c @@ -90,6 +90,7 @@ struct PCITestDevState { int current; uint64_t membar_size; + bool membar_backed; MemoryRegion membar; }; @@ -258,8 +259,14 @@ static void pci_testdev_realize(PCIDevice *pci_dev, Error **errp) pci_register_bar(pci_dev, 1, PCI_BASE_ADDRESS_SPACE_IO, &d->portio); if (d->membar_size) { - memory_region_init(&d->membar, OBJECT(d), "pci-testdev-membar", - d->membar_size); + if (d->membar_backed) + memory_region_init_ram(&d->membar, OBJECT(d), + "pci-testdev-membar-backed", + d->membar_size, NULL); + else + memory_region_init(&d->membar, OBJECT(d), + "pci-testdev-membar", + d->membar_size); pci_register_bar(pci_dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY | PCI_BASE_ADDRESS_MEM_PREFETCH | @@ -321,6 +328,7 @@ static void qdev_pci_testdev_reset(DeviceState *dev) static const Property pci_testdev_properties[] = { DEFINE_PROP_SIZE("membar", PCITestDevState, membar_size, 0), + DEFINE_PROP_BOOL("membar-backed", PCITestDevState, membar_backed, false), }; static void pci_testdev_class_init(ObjectClass *klass, const void *data) From e0f300b36da1ee794fd81aa95f56e7bc9f010d46 Mon Sep 17 00:00:00 2001 From: Haoqian He Date: Tue, 15 Apr 2025 22:47:26 -0400 Subject: [PATCH 0873/2760] system/runstate: add VM state change cb with return value This patch adds the new VM state change cb type `VMChangeStateHandlerWithRet`, which has return value for `VMChangeStateEntry`. Thus, we can register a new VM state change cb with return value for device. Note that `VMChangeStateHandler` and `VMChangeStateHandlerWithRet` are mutually exclusive and cannot be provided at the same time. This patch is the pre patch for 'vhost-user: return failure if backend crashes when live migration', which makes the live migration aware of the loss of connection with the vhost-user backend and aborts the live migration. Virtio device will use VMChangeStateHandlerWithRet. Signed-off-by: Haoqian He Message-Id: <20250416024729.3289157-2-haoqian.he@smartx.com> Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/virtio-blk.c | 2 +- hw/core/vm-change-state-handler.c | 18 ++++++++++------ hw/scsi/scsi-bus.c | 2 +- hw/vfio/migration.c | 2 +- hw/virtio/virtio.c | 2 +- include/system/runstate.h | 13 +++++++++--- system/cpus.c | 8 +++++-- system/runstate.c | 35 ++++++++++++++++++++++++++----- 8 files changed, 62 insertions(+), 20 deletions(-) diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index b54d01d3a2..ea948d18fd 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -1802,7 +1802,7 @@ static void virtio_blk_device_realize(DeviceState *dev, Error **errp) * called after ->start_ioeventfd() has already set blk's AioContext. */ s->change = - qdev_add_vm_change_state_handler(dev, virtio_blk_dma_restart_cb, s); + qdev_add_vm_change_state_handler(dev, virtio_blk_dma_restart_cb, NULL, s); blk_ram_registrar_init(&s->blk_ram_registrar, s->blk); blk_set_dev_ops(s->blk, &virtio_block_ops, s); diff --git a/hw/core/vm-change-state-handler.c b/hw/core/vm-change-state-handler.c index 7064995578..99c642b558 100644 --- a/hw/core/vm-change-state-handler.c +++ b/hw/core/vm-change-state-handler.c @@ -40,6 +40,7 @@ static int qdev_get_dev_tree_depth(DeviceState *dev) * qdev_add_vm_change_state_handler: * @dev: the device that owns this handler * @cb: the callback function to be invoked + * @cb_ret: the callback function with return value to be invoked * @opaque: user data passed to the callback function * * This function works like qemu_add_vm_change_state_handler() except callbacks @@ -50,25 +51,30 @@ static int qdev_get_dev_tree_depth(DeviceState *dev) * controller's callback is invoked before the children on its bus when the VM * starts running. The order is reversed when the VM stops running. * + * Note that the parameter `cb` and `cb_ret` are mutually exclusive. + * * Returns: an entry to be freed with qemu_del_vm_change_state_handler() */ VMChangeStateEntry *qdev_add_vm_change_state_handler(DeviceState *dev, VMChangeStateHandler *cb, + VMChangeStateHandlerWithRet *cb_ret, void *opaque) { - return qdev_add_vm_change_state_handler_full(dev, cb, NULL, opaque); + assert(!cb || !cb_ret); + return qdev_add_vm_change_state_handler_full(dev, cb, NULL, cb_ret, opaque); } /* * Exactly like qdev_add_vm_change_state_handler() but passes a prepare_cb - * argument too. + * and the cb_ret arguments too. */ VMChangeStateEntry *qdev_add_vm_change_state_handler_full( - DeviceState *dev, VMChangeStateHandler *cb, - VMChangeStateHandler *prepare_cb, void *opaque) + DeviceState *dev, VMChangeStateHandler *cb, VMChangeStateHandler *prepare_cb, + VMChangeStateHandlerWithRet *cb_ret, void *opaque) { int depth = qdev_get_dev_tree_depth(dev); - return qemu_add_vm_change_state_handler_prio_full(cb, prepare_cb, opaque, - depth); + assert(!cb || !cb_ret); + return qemu_add_vm_change_state_handler_prio_full(cb, prepare_cb, cb_ret, + opaque, depth); } diff --git a/hw/scsi/scsi-bus.c b/hw/scsi/scsi-bus.c index 70be4a7367..9b12ee7f1c 100644 --- a/hw/scsi/scsi-bus.c +++ b/hw/scsi/scsi-bus.c @@ -400,7 +400,7 @@ static void scsi_qdev_realize(DeviceState *qdev, Error **errp) return; } dev->vmsentry = qdev_add_vm_change_state_handler(DEVICE(dev), - scsi_dma_restart_cb, dev); + scsi_dma_restart_cb, NULL, dev); } static void scsi_qdev_unrealize(DeviceState *qdev) diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 1dceab1b19..b76697bd1a 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -1016,7 +1016,7 @@ static int vfio_migration_init(VFIODevice *vbasedev) vfio_vmstate_change_prepare : NULL; migration->vm_state = qdev_add_vm_change_state_handler_full( - vbasedev->dev, vfio_vmstate_change, prepare_cb, vbasedev); + vbasedev->dev, vfio_vmstate_change, prepare_cb, NULL, vbasedev); migration_add_notifier(&migration->migration_state, vfio_migration_state_notifier); diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 480c2e5036..e3b62522ec 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -3489,7 +3489,7 @@ void virtio_init(VirtIODevice *vdev, uint16_t device_id, size_t config_size) vdev->config = NULL; } vdev->vmstate = qdev_add_vm_change_state_handler(DEVICE(vdev), - virtio_vmstate_change, vdev); + virtio_vmstate_change, NULL, vdev); vdev->device_endian = virtio_default_endian(); vdev->use_guest_notifier_mask = true; } diff --git a/include/system/runstate.h b/include/system/runstate.h index bffc3719d4..fdd5c4a517 100644 --- a/include/system/runstate.h +++ b/include/system/runstate.h @@ -12,6 +12,7 @@ bool runstate_needs_reset(void); void runstate_replay_enable(void); typedef void VMChangeStateHandler(void *opaque, bool running, RunState state); +typedef int VMChangeStateHandlerWithRet(void *opaque, bool running, RunState state); VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, void *opaque); @@ -20,21 +21,27 @@ VMChangeStateEntry *qemu_add_vm_change_state_handler_prio( VMChangeStateEntry * qemu_add_vm_change_state_handler_prio_full(VMChangeStateHandler *cb, VMChangeStateHandler *prepare_cb, + VMChangeStateHandlerWithRet *cb_ret, void *opaque, int priority); VMChangeStateEntry *qdev_add_vm_change_state_handler(DeviceState *dev, VMChangeStateHandler *cb, + VMChangeStateHandlerWithRet *cb_ret, void *opaque); VMChangeStateEntry *qdev_add_vm_change_state_handler_full( - DeviceState *dev, VMChangeStateHandler *cb, - VMChangeStateHandler *prepare_cb, void *opaque); + DeviceState *dev, VMChangeStateHandler *cb, VMChangeStateHandler *prepare_cb, + VMChangeStateHandlerWithRet *cb_ret, void *opaque); void qemu_del_vm_change_state_handler(VMChangeStateEntry *e); /** * vm_state_notify: Notify the state of the VM * * @running: whether the VM is running or not. * @state: the #RunState of the VM. + * + * Return the result of the callback which has return value. + * If no callback has return value, still return 0 and the + * upper layer should not do additional processing. */ -void vm_state_notify(bool running, RunState state); +int vm_state_notify(bool running, RunState state); static inline bool shutdown_caused_by_guest(ShutdownCause cause) { diff --git a/system/cpus.c b/system/cpus.c index 2cc5f887ab..d16b0dff98 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -299,14 +299,18 @@ static int do_vm_stop(RunState state, bool send_stop) if (oldstate == RUN_STATE_RUNNING) { pause_all_vcpus(); } - vm_state_notify(0, state); + ret = vm_state_notify(0, state); if (send_stop) { qapi_event_send_stop(); } } bdrv_drain_all(); - ret = bdrv_flush_all(); + /* + * Even if vm_state_notify() return failure, + * it would be better to flush as before. + */ + ret |= bdrv_flush_all(); trace_vm_stop_flush_all(ret); return ret; diff --git a/system/runstate.c b/system/runstate.c index 272801d307..de74d962bc 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -297,6 +297,7 @@ void qemu_system_vmstop_request(RunState state) struct VMChangeStateEntry { VMChangeStateHandler *cb; VMChangeStateHandler *prepare_cb; + VMChangeStateHandlerWithRet *cb_ret; void *opaque; QTAILQ_ENTRY(VMChangeStateEntry) entries; int priority; @@ -320,14 +321,15 @@ static QTAILQ_HEAD(, VMChangeStateEntry) vm_change_state_head = VMChangeStateEntry *qemu_add_vm_change_state_handler_prio( VMChangeStateHandler *cb, void *opaque, int priority) { - return qemu_add_vm_change_state_handler_prio_full(cb, NULL, opaque, - priority); + return qemu_add_vm_change_state_handler_prio_full(cb, NULL, NULL, + opaque, priority); } /** * qemu_add_vm_change_state_handler_prio_full: * @cb: the main callback to invoke * @prepare_cb: a callback to invoke before the main callback + * @cb_ret: the main callback to invoke with return value * @opaque: user data passed to the callbacks * @priority: low priorities execute first when the vm runs and the reverse is * true when the vm stops @@ -344,6 +346,7 @@ VMChangeStateEntry *qemu_add_vm_change_state_handler_prio( VMChangeStateEntry * qemu_add_vm_change_state_handler_prio_full(VMChangeStateHandler *cb, VMChangeStateHandler *prepare_cb, + VMChangeStateHandlerWithRet *cb_ret, void *opaque, int priority) { VMChangeStateEntry *e; @@ -352,6 +355,7 @@ qemu_add_vm_change_state_handler_prio_full(VMChangeStateHandler *cb, e = g_malloc0(sizeof(*e)); e->cb = cb; e->prepare_cb = prepare_cb; + e->cb_ret = cb_ret; e->opaque = opaque; e->priority = priority; @@ -379,9 +383,10 @@ void qemu_del_vm_change_state_handler(VMChangeStateEntry *e) g_free(e); } -void vm_state_notify(bool running, RunState state) +int vm_state_notify(bool running, RunState state) { VMChangeStateEntry *e, *next; + int ret = 0; trace_vm_state_notify(running, state, RunState_str(state)); @@ -393,7 +398,17 @@ void vm_state_notify(bool running, RunState state) } QTAILQ_FOREACH_SAFE(e, &vm_change_state_head, entries, next) { - e->cb(e->opaque, running, state); + if (e->cb) { + e->cb(e->opaque, running, state); + } else if (e->cb_ret) { + /* + * Here ignore the return value of cb_ret because + * we only care about the stopping the device during + * the VM live migration to indicate whether the + * connection between qemu and backend is normal. + */ + e->cb_ret(e->opaque, running, state); + } } } else { QTAILQ_FOREACH_REVERSE_SAFE(e, &vm_change_state_head, entries, next) { @@ -403,9 +418,19 @@ void vm_state_notify(bool running, RunState state) } QTAILQ_FOREACH_REVERSE_SAFE(e, &vm_change_state_head, entries, next) { - e->cb(e->opaque, running, state); + if (e->cb) { + e->cb(e->opaque, running, state); + } else if (e->cb_ret) { + /* + * We should execute all registered callbacks even if + * one of them returns failure, otherwise, some cleanup + * work of the device will be skipped. + */ + ret |= e->cb_ret(e->opaque, running, state); + } } } + return ret; } static ShutdownCause reset_requested; From 5a317017b827e338358792cd07663f8ea25f1ffe Mon Sep 17 00:00:00 2001 From: Haoqian He Date: Tue, 15 Apr 2025 22:47:27 -0400 Subject: [PATCH 0874/2760] vhost: return failure if stop virtqueue failed in vhost_dev_stop This patch captures the error of vhost_virtqueue_stop() in vhost_dev_stop() and returns the error upward. Specifically, if QEMU is disconnected from the vhost backend, some actions in vhost_dev_stop() will fail, such as sending vhost-user messages to the backend (GET_VRING_BASE, SET_VRING_ENABLE) and vhost_reset_status. Considering that both set_vring_enable and vhost_reset_status require setting the specific virtio feature bit, we can capture vhost_virtqueue_stop()'s error to indicate that QEMU has lost connection with the backend. This patch is the pre patch for 'vhost-user: return failure if backend crashes when live migration', which makes the live migration aware of the loss of connection with the vhost-user backend and aborts the live migration. Signed-off-by: Haoqian He Message-Id: <20250416024729.3289157-3-haoqian.he@smartx.com> Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 23 +++++++++++++---------- include/hw/virtio/vhost.h | 8 +++++--- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 4cae7c1664..fc43853704 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1367,10 +1367,10 @@ int vhost_virtqueue_start(struct vhost_dev *dev, return r; } -void vhost_virtqueue_stop(struct vhost_dev *dev, - struct VirtIODevice *vdev, - struct vhost_virtqueue *vq, - unsigned idx) +int vhost_virtqueue_stop(struct vhost_dev *dev, + struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, + unsigned idx) { int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx); struct vhost_vring_state state = { @@ -1380,7 +1380,7 @@ void vhost_virtqueue_stop(struct vhost_dev *dev, if (virtio_queue_get_desc_addr(vdev, idx) == 0) { /* Don't stop the virtqueue which might have not been started */ - return; + return 0; } r = dev->vhost_ops->vhost_get_vring_base(dev, &state); @@ -1411,6 +1411,7 @@ void vhost_virtqueue_stop(struct vhost_dev *dev, 0, virtio_queue_get_avail_size(vdev, idx)); vhost_memory_unmap(dev, vq->desc, virtio_queue_get_desc_size(vdev, idx), 0, virtio_queue_get_desc_size(vdev, idx)); + return r; } static int vhost_virtqueue_set_busyloop_timeout(struct vhost_dev *dev, @@ -2135,9 +2136,10 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) } /* Host notifiers must be enabled at this point. */ -void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) +int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) { int i; + int rc = 0; /* should only be called after backend is connected */ assert(hdev->vhost_ops); @@ -2156,10 +2158,10 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) vhost_dev_set_vring_enable(hdev, false); } for (i = 0; i < hdev->nvqs; ++i) { - vhost_virtqueue_stop(hdev, - vdev, - hdev->vqs + i, - hdev->vq_index + i); + rc |= vhost_virtqueue_stop(hdev, + vdev, + hdev->vqs + i, + hdev->vq_index + i); } if (hdev->vhost_ops->vhost_reset_status) { hdev->vhost_ops->vhost_reset_status(hdev); @@ -2176,6 +2178,7 @@ void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) hdev->started = false; vdev->vhost_started = false; hdev->vdev = NULL; + return rc; } int vhost_net_set_backend(struct vhost_dev *hdev, diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index bb4b58e115..38800a7156 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -232,8 +232,10 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); * Stop the vhost device. After the device is stopped the notifiers * can be disabled (@vhost_dev_disable_notifiers) and the device can * be torn down (@vhost_dev_cleanup). + * + * Return: 0 on success, != 0 on error when stopping dev. */ -void vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); +int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); /** * DOC: vhost device configuration handling @@ -333,8 +335,8 @@ int vhost_device_iotlb_miss(struct vhost_dev *dev, uint64_t iova, int write); int vhost_virtqueue_start(struct vhost_dev *dev, struct VirtIODevice *vdev, struct vhost_virtqueue *vq, unsigned idx); -void vhost_virtqueue_stop(struct vhost_dev *dev, struct VirtIODevice *vdev, - struct vhost_virtqueue *vq, unsigned idx); +int vhost_virtqueue_stop(struct vhost_dev *dev, struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, unsigned idx); void vhost_dev_reset_inflight(struct vhost_inflight *inflight); void vhost_dev_free_inflight(struct vhost_inflight *inflight); From bc85aae4204509420f0a4403ca728801170d9351 Mon Sep 17 00:00:00 2001 From: Haoqian He Date: Tue, 15 Apr 2025 22:47:28 -0400 Subject: [PATCH 0875/2760] vhost-user: return failure if backend crash when live migration Live migration should be terminated if the vhost-user backend crashes before the migration completes. Specifically, since the vhost device will be stopped when VM is stopped before the end of the live migration, in current implementation if the backend crashes, vhost-user device set_status() won't return failure, live migration won't perceive the disconnection between QEMU and the backend. When the VM is migrated to the destination, the inflight IO will be resubmitted, and if the IO was completed out of order before, it will cause IO error. To fix this issue: 1. Add the return value to set_status() for VirtioDeviceClass. a. For the vhost-user device, return failure when the backend crashes. b. For other virtio devices, always return 0. 2. Return failure if vhost_dev_stop() failed for vhost-user device. If QEMU loses connection with the vhost-user backend, virtio set_status() can return failure to the upper layer, migration_completion() can handle the error, terminate the live migration, and restore the VM, so that inflight IO can be completed normally. Signed-off-by: Haoqian He Message-Id: <20250416024729.3289157-4-haoqian.he@smartx.com> Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- backends/vhost-user.c | 20 +++++++++---------- hw/block/vhost-user-blk.c | 27 ++++++++++++++------------ hw/block/virtio-blk.c | 5 +++-- hw/char/virtio-serial-bus.c | 3 ++- hw/display/vhost-user-gpu.c | 12 +++++++++--- hw/input/virtio-input.c | 3 ++- hw/net/virtio-net.c | 3 ++- hw/scsi/vhost-scsi-common.c | 13 +++++++------ hw/scsi/vhost-scsi.c | 5 +++-- hw/scsi/vhost-user-scsi.c | 18 ++++++++++------- hw/virtio/vdpa-dev.c | 5 +++-- hw/virtio/vhost-user-base.c | 23 +++++++++++++--------- hw/virtio/vhost-user-fs.c | 23 +++++++++++++--------- hw/virtio/vhost-user-scmi.c | 27 +++++++++++++++----------- hw/virtio/vhost-user-vsock.c | 15 +++++++++----- hw/virtio/vhost-vsock-common.c | 12 ++++++------ hw/virtio/vhost-vsock.c | 11 ++++++----- hw/virtio/virtio-balloon.c | 3 ++- hw/virtio/virtio-crypto.c | 3 ++- hw/virtio/virtio-iommu.c | 3 ++- hw/virtio/virtio-rng.c | 5 +++-- hw/virtio/virtio.c | 22 ++++++++++++++------- include/hw/virtio/vhost-scsi-common.h | 2 +- include/hw/virtio/vhost-vsock-common.h | 2 +- include/hw/virtio/virtio.h | 2 +- include/system/vhost-user-backend.h | 2 +- 26 files changed, 160 insertions(+), 109 deletions(-) diff --git a/backends/vhost-user.c b/backends/vhost-user.c index 94274a619d..42845329e7 100644 --- a/backends/vhost-user.c +++ b/backends/vhost-user.c @@ -97,30 +97,28 @@ vhost_user_backend_start(VhostUserBackend *b) vhost_dev_disable_notifiers(&b->dev, b->vdev); } -void +int vhost_user_backend_stop(VhostUserBackend *b) { BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(b->vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); - int ret = 0; + int ret; if (!b->started) { - return; + return 0; } - vhost_dev_stop(&b->dev, b->vdev, true); + ret = vhost_dev_stop(&b->dev, b->vdev, true); - if (k->set_guest_notifiers) { - ret = k->set_guest_notifiers(qbus->parent, - b->dev.nvqs, false); - if (ret < 0) { - error_report("vhost guest notifier cleanup failed: %d", ret); - } + if (k->set_guest_notifiers && + k->set_guest_notifiers(qbus->parent, b->dev.nvqs, false) < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return -1; } - assert(ret >= 0); vhost_dev_disable_notifiers(&b->dev, b->vdev); b->started = false; + return ret; } static void set_chardev(Object *obj, const char *value, Error **errp) diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 4bb5ed299e..0eebbcd80d 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -204,7 +204,7 @@ static int vhost_user_blk_start(VirtIODevice *vdev, Error **errp) return ret; } -static void vhost_user_blk_stop(VirtIODevice *vdev) +static int vhost_user_blk_stop(VirtIODevice *vdev) { VHostUserBlk *s = VHOST_USER_BLK(vdev); BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); @@ -212,26 +212,26 @@ static void vhost_user_blk_stop(VirtIODevice *vdev) int ret; if (!s->started_vu) { - return; + return 0; } s->started_vu = false; if (!k->set_guest_notifiers) { - return; + return 0; } - vhost_dev_stop(&s->dev, vdev, true); + ret = vhost_dev_stop(&s->dev, vdev, true); - ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); - if (ret < 0) { + if (k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false) < 0) { error_report("vhost guest notifier cleanup failed: %d", ret); - return; + return -1; } vhost_dev_disable_notifiers(&s->dev, vdev); + return ret; } -static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) +static int vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) { VHostUserBlk *s = VHOST_USER_BLK(vdev); bool should_start = virtio_device_should_start(vdev, status); @@ -239,11 +239,11 @@ static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) int ret; if (!s->connected) { - return; + return -1; } if (vhost_dev_is_started(&s->dev) == should_start) { - return; + return 0; } if (should_start) { @@ -253,9 +253,12 @@ static void vhost_user_blk_set_status(VirtIODevice *vdev, uint8_t status) qemu_chr_fe_disconnect(&s->chardev); } } else { - vhost_user_blk_stop(vdev); + ret = vhost_user_blk_stop(vdev); + if (ret < 0) { + return ret; + } } - + return 0; } static uint64_t vhost_user_blk_get_features(VirtIODevice *vdev, diff --git a/hw/block/virtio-blk.c b/hw/block/virtio-blk.c index ea948d18fd..9bab2716c1 100644 --- a/hw/block/virtio-blk.c +++ b/hw/block/virtio-blk.c @@ -1270,7 +1270,7 @@ static uint64_t virtio_blk_get_features(VirtIODevice *vdev, uint64_t features, return features; } -static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) +static int virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) { VirtIOBlock *s = VIRTIO_BLK(vdev); @@ -1279,7 +1279,7 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) } if (!(status & VIRTIO_CONFIG_S_DRIVER_OK)) { - return; + return 0; } /* A guest that supports VIRTIO_BLK_F_CONFIG_WCE must be able to send @@ -1302,6 +1302,7 @@ static void virtio_blk_set_status(VirtIODevice *vdev, uint8_t status) virtio_vdev_has_feature(vdev, VIRTIO_BLK_F_WCE)); } + return 0; } static void virtio_blk_save_device(VirtIODevice *vdev, QEMUFile *f) diff --git a/hw/char/virtio-serial-bus.c b/hw/char/virtio-serial-bus.c index eb79f5258b..673c50f0be 100644 --- a/hw/char/virtio-serial-bus.c +++ b/hw/char/virtio-serial-bus.c @@ -622,7 +622,7 @@ static void guest_reset(VirtIOSerial *vser) } } -static void set_status(VirtIODevice *vdev, uint8_t status) +static int set_status(VirtIODevice *vdev, uint8_t status) { VirtIOSerial *vser; VirtIOSerialPort *port; @@ -650,6 +650,7 @@ static void set_status(VirtIODevice *vdev, uint8_t status) vsc->enable_backend(port, vdev->vm_running); } } + return 0; } static void vser_reset(VirtIODevice *vdev) diff --git a/hw/display/vhost-user-gpu.c b/hw/display/vhost-user-gpu.c index 43d4c08a2e..9fc6bbcd2c 100644 --- a/hw/display/vhost-user-gpu.c +++ b/hw/display/vhost-user-gpu.c @@ -516,7 +516,7 @@ vhost_user_gpu_set_config(VirtIODevice *vdev, } } -static void +static int vhost_user_gpu_set_status(VirtIODevice *vdev, uint8_t val) { VhostUserGPU *g = VHOST_USER_GPU(vdev); @@ -525,18 +525,24 @@ vhost_user_gpu_set_status(VirtIODevice *vdev, uint8_t val) if (val & VIRTIO_CONFIG_S_DRIVER_OK && vdev->vm_running) { if (!vhost_user_gpu_do_set_socket(g, &err)) { error_report_err(err); - return; + return 0; } vhost_user_backend_start(g->vhost); } else { + int ret; + /* unblock any wait and stop processing */ if (g->vhost_gpu_fd != -1) { vhost_user_gpu_update_blocked(g, true); qemu_chr_fe_deinit(&g->vhost_chr, true); g->vhost_gpu_fd = -1; } - vhost_user_backend_stop(g->vhost); + ret = vhost_user_backend_stop(g->vhost); + if (ret < 0) { + return ret; + } } + return 0; } static bool diff --git a/hw/input/virtio-input.c b/hw/input/virtio-input.c index 1818cbddc7..a3f554f211 100644 --- a/hw/input/virtio-input.c +++ b/hw/input/virtio-input.c @@ -189,7 +189,7 @@ static uint64_t virtio_input_get_features(VirtIODevice *vdev, uint64_t f, return f; } -static void virtio_input_set_status(VirtIODevice *vdev, uint8_t val) +static int virtio_input_set_status(VirtIODevice *vdev, uint8_t val) { VirtIOInputClass *vic = VIRTIO_INPUT_GET_CLASS(vdev); VirtIOInput *vinput = VIRTIO_INPUT(vdev); @@ -202,6 +202,7 @@ static void virtio_input_set_status(VirtIODevice *vdev, uint8_t val) } } } + return 0; } static void virtio_input_reset(VirtIODevice *vdev) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 2de037c273..221252e00a 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -382,7 +382,7 @@ static void virtio_net_drop_tx_queue_data(VirtIODevice *vdev, VirtQueue *vq) } } -static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) +static int virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) { VirtIONet *n = VIRTIO_NET(vdev); VirtIONetQueue *q; @@ -437,6 +437,7 @@ static void virtio_net_set_status(struct VirtIODevice *vdev, uint8_t status) } } } + return 0; } static void virtio_net_set_link_status(NetClientState *nc) diff --git a/hw/scsi/vhost-scsi-common.c b/hw/scsi/vhost-scsi-common.c index 4c8637045d..43525ba46d 100644 --- a/hw/scsi/vhost-scsi-common.c +++ b/hw/scsi/vhost-scsi-common.c @@ -101,24 +101,25 @@ int vhost_scsi_common_start(VHostSCSICommon *vsc, Error **errp) return ret; } -void vhost_scsi_common_stop(VHostSCSICommon *vsc) +int vhost_scsi_common_stop(VHostSCSICommon *vsc) { VirtIODevice *vdev = VIRTIO_DEVICE(vsc); BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); int ret = 0; - vhost_dev_stop(&vsc->dev, vdev, true); + ret = vhost_dev_stop(&vsc->dev, vdev, true); if (k->set_guest_notifiers) { - ret = k->set_guest_notifiers(qbus->parent, vsc->dev.nvqs, false); - if (ret < 0) { - error_report("vhost guest notifier cleanup failed: %d", ret); + int r = k->set_guest_notifiers(qbus->parent, vsc->dev.nvqs, false); + if (r < 0) { + error_report("vhost guest notifier cleanup failed: %d", ret); + return r; } } - assert(ret >= 0); vhost_dev_disable_notifiers(&vsc->dev, vdev); + return ret; } uint64_t vhost_scsi_common_get_features(VirtIODevice *vdev, uint64_t features, diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index 10fde8eee0..dd4250ebe8 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -114,7 +114,7 @@ static void vhost_scsi_stop(VHostSCSI *s) vhost_scsi_common_stop(vsc); } -static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) +static int vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) { VHostSCSI *s = VHOST_SCSI(vdev); VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); @@ -125,7 +125,7 @@ static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) } if (vhost_dev_is_started(&vsc->dev) == start) { - return; + return 0; } if (start) { @@ -139,6 +139,7 @@ static void vhost_scsi_set_status(VirtIODevice *vdev, uint8_t val) } else { vhost_scsi_stop(s); } + return 0; } static void vhost_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) diff --git a/hw/scsi/vhost-user-scsi.c b/hw/scsi/vhost-user-scsi.c index 8298e8cc6d..25f2d894e7 100644 --- a/hw/scsi/vhost-user-scsi.c +++ b/hw/scsi/vhost-user-scsi.c @@ -52,19 +52,19 @@ static int vhost_user_scsi_start(VHostUserSCSI *s, Error **errp) return ret; } -static void vhost_user_scsi_stop(VHostUserSCSI *s) +static int vhost_user_scsi_stop(VHostUserSCSI *s) { VHostSCSICommon *vsc = VHOST_SCSI_COMMON(s); if (!s->started_vu) { - return; + return 0; } s->started_vu = false; - vhost_scsi_common_stop(vsc); + return vhost_scsi_common_stop(vsc); } -static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) +static int vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) { VHostUserSCSI *s = (VHostUserSCSI *)vdev; DeviceState *dev = DEVICE(vdev); @@ -75,11 +75,11 @@ static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) int ret; if (!s->connected) { - return; + return -1; } if (vhost_dev_is_started(&vsc->dev) == should_start) { - return; + return 0; } if (should_start) { @@ -91,8 +91,12 @@ static void vhost_user_scsi_set_status(VirtIODevice *vdev, uint8_t status) qemu_chr_fe_disconnect(&vs->conf.chardev); } } else { - vhost_user_scsi_stop(s); + ret = vhost_user_scsi_stop(s); + if (ret) { + return ret; + } } + return 0; } static void vhost_user_scsi_handle_output(VirtIODevice *vdev, VirtQueue *vq) diff --git a/hw/virtio/vdpa-dev.c b/hw/virtio/vdpa-dev.c index dd8837ce4e..d1da40afc8 100644 --- a/hw/virtio/vdpa-dev.c +++ b/hw/virtio/vdpa-dev.c @@ -312,7 +312,7 @@ static void vhost_vdpa_device_stop(VirtIODevice *vdev) vhost_dev_disable_notifiers(&s->dev, vdev); } -static void vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) +static int vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) { VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); bool should_start = virtio_device_started(vdev, status); @@ -324,7 +324,7 @@ static void vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) } if (s->started == should_start) { - return; + return 0; } if (should_start) { @@ -335,6 +335,7 @@ static void vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) } else { vhost_vdpa_device_stop(vdev); } + return 0; } static const Property vhost_vdpa_device_properties[] = { diff --git a/hw/virtio/vhost-user-base.c b/hw/virtio/vhost-user-base.c index 77143320a2..ff67a020b4 100644 --- a/hw/virtio/vhost-user-base.c +++ b/hw/virtio/vhost-user-base.c @@ -66,7 +66,7 @@ static void vub_start(VirtIODevice *vdev) vhost_dev_disable_notifiers(&vub->vhost_dev, vdev); } -static void vub_stop(VirtIODevice *vdev) +static int vub_stop(VirtIODevice *vdev) { VHostUserBase *vub = VHOST_USER_BASE(vdev); BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); @@ -74,34 +74,39 @@ static void vub_stop(VirtIODevice *vdev) int ret; if (!k->set_guest_notifiers) { - return; + return 0; } - vhost_dev_stop(&vub->vhost_dev, vdev, true); + ret = vhost_dev_stop(&vub->vhost_dev, vdev, true); - ret = k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false); - if (ret < 0) { + if (k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false) < 0) { error_report("vhost guest notifier cleanup failed: %d", ret); - return; + return -1; } vhost_dev_disable_notifiers(&vub->vhost_dev, vdev); + return ret; } -static void vub_set_status(VirtIODevice *vdev, uint8_t status) +static int vub_set_status(VirtIODevice *vdev, uint8_t status) { VHostUserBase *vub = VHOST_USER_BASE(vdev); bool should_start = virtio_device_should_start(vdev, status); if (vhost_dev_is_started(&vub->vhost_dev) == should_start) { - return; + return 0; } if (should_start) { vub_start(vdev); } else { - vub_stop(vdev); + int ret; + ret = vub_stop(vdev); + if (ret < 0) { + return ret; + } } + return 0; } /* diff --git a/hw/virtio/vhost-user-fs.c b/hw/virtio/vhost-user-fs.c index f6d1fc8804..e77c69eb12 100644 --- a/hw/virtio/vhost-user-fs.c +++ b/hw/virtio/vhost-user-fs.c @@ -100,7 +100,7 @@ static void vuf_start(VirtIODevice *vdev) vhost_dev_disable_notifiers(&fs->vhost_dev, vdev); } -static void vuf_stop(VirtIODevice *vdev) +static int vuf_stop(VirtIODevice *vdev) { VHostUserFS *fs = VHOST_USER_FS(vdev); BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); @@ -108,34 +108,39 @@ static void vuf_stop(VirtIODevice *vdev) int ret; if (!k->set_guest_notifiers) { - return; + return 0; } - vhost_dev_stop(&fs->vhost_dev, vdev, true); + ret = vhost_dev_stop(&fs->vhost_dev, vdev, true); - ret = k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false); - if (ret < 0) { + if (k->set_guest_notifiers(qbus->parent, fs->vhost_dev.nvqs, false) < 0) { error_report("vhost guest notifier cleanup failed: %d", ret); - return; + return -1; } vhost_dev_disable_notifiers(&fs->vhost_dev, vdev); + return ret; } -static void vuf_set_status(VirtIODevice *vdev, uint8_t status) +static int vuf_set_status(VirtIODevice *vdev, uint8_t status) { VHostUserFS *fs = VHOST_USER_FS(vdev); bool should_start = virtio_device_should_start(vdev, status); if (vhost_dev_is_started(&fs->vhost_dev) == should_start) { - return; + return 0; } if (should_start) { vuf_start(vdev); } else { - vuf_stop(vdev); + int ret; + ret = vuf_stop(vdev); + if (ret < 0) { + return ret; + } } + return 0; } static uint64_t vuf_get_features(VirtIODevice *vdev, diff --git a/hw/virtio/vhost-user-scmi.c b/hw/virtio/vhost-user-scmi.c index 7a0f622181..f9264c4374 100644 --- a/hw/virtio/vhost-user-scmi.c +++ b/hw/virtio/vhost-user-scmi.c @@ -83,7 +83,7 @@ static int vu_scmi_start(VirtIODevice *vdev) return ret; } -static void vu_scmi_stop(VirtIODevice *vdev) +static int vu_scmi_stop(VirtIODevice *vdev) { VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); @@ -93,41 +93,46 @@ static void vu_scmi_stop(VirtIODevice *vdev) /* vhost_dev_is_started() check in the callers is not fully reliable. */ if (!scmi->started_vu) { - return; + return 0; } scmi->started_vu = false; if (!k->set_guest_notifiers) { - return; + return 0; } - vhost_dev_stop(vhost_dev, vdev, true); + ret = vhost_dev_stop(vhost_dev, vdev, true); - ret = k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, false); - if (ret < 0) { + if (k->set_guest_notifiers(qbus->parent, vhost_dev->nvqs, false) < 0) { error_report("vhost guest notifier cleanup failed: %d", ret); - return; + return -1; } vhost_dev_disable_notifiers(vhost_dev, vdev); + return ret; } -static void vu_scmi_set_status(VirtIODevice *vdev, uint8_t status) +static int vu_scmi_set_status(VirtIODevice *vdev, uint8_t status) { VHostUserSCMI *scmi = VHOST_USER_SCMI(vdev); bool should_start = virtio_device_should_start(vdev, status); if (!scmi->connected) { - return; + return -1; } if (vhost_dev_is_started(&scmi->vhost_dev) == should_start) { - return; + return 0; } if (should_start) { vu_scmi_start(vdev); } else { - vu_scmi_stop(vdev); + int ret; + ret = vu_scmi_stop(vdev); + if (ret < 0) { + return ret; + } } + return 0; } static uint64_t vu_scmi_get_features(VirtIODevice *vdev, uint64_t features, diff --git a/hw/virtio/vhost-user-vsock.c b/hw/virtio/vhost-user-vsock.c index 2776792f59..993c287348 100644 --- a/hw/virtio/vhost-user-vsock.c +++ b/hw/virtio/vhost-user-vsock.c @@ -54,23 +54,28 @@ const VhostDevConfigOps vsock_ops = { .vhost_dev_config_notifier = vuv_handle_config_change, }; -static void vuv_set_status(VirtIODevice *vdev, uint8_t status) +static int vuv_set_status(VirtIODevice *vdev, uint8_t status) { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); bool should_start = virtio_device_should_start(vdev, status); + int ret; if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) { - return; + return 0; } if (should_start) { - int ret = vhost_vsock_common_start(vdev); + ret = vhost_vsock_common_start(vdev); if (ret < 0) { - return; + return ret; } } else { - vhost_vsock_common_stop(vdev); + ret = vhost_vsock_common_stop(vdev); + if (ret < 0) { + return ret; + } } + return 0; } static uint64_t vuv_get_features(VirtIODevice *vdev, diff --git a/hw/virtio/vhost-vsock-common.c b/hw/virtio/vhost-vsock-common.c index 4b4fbb45cc..c6c44d8989 100644 --- a/hw/virtio/vhost-vsock-common.c +++ b/hw/virtio/vhost-vsock-common.c @@ -95,7 +95,7 @@ int vhost_vsock_common_start(VirtIODevice *vdev) return ret; } -void vhost_vsock_common_stop(VirtIODevice *vdev) +int vhost_vsock_common_stop(VirtIODevice *vdev) { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); @@ -103,18 +103,18 @@ void vhost_vsock_common_stop(VirtIODevice *vdev) int ret; if (!k->set_guest_notifiers) { - return; + return 0; } - vhost_dev_stop(&vvc->vhost_dev, vdev, true); + ret = vhost_dev_stop(&vvc->vhost_dev, vdev, true); - ret = k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false); - if (ret < 0) { + if (k->set_guest_notifiers(qbus->parent, vvc->vhost_dev.nvqs, false) < 0) { error_report("vhost guest notifier cleanup failed: %d", ret); - return; + return -1; } vhost_dev_disable_notifiers(&vvc->vhost_dev, vdev); + return ret; } diff --git a/hw/virtio/vhost-vsock.c b/hw/virtio/vhost-vsock.c index b73dc723c2..6e4088831f 100644 --- a/hw/virtio/vhost-vsock.c +++ b/hw/virtio/vhost-vsock.c @@ -67,37 +67,38 @@ static int vhost_vsock_set_running(VirtIODevice *vdev, int start) } -static void vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status) +static int vhost_vsock_set_status(VirtIODevice *vdev, uint8_t status) { VHostVSockCommon *vvc = VHOST_VSOCK_COMMON(vdev); bool should_start = virtio_device_should_start(vdev, status); int ret; if (vhost_dev_is_started(&vvc->vhost_dev) == should_start) { - return; + return 0; } if (should_start) { ret = vhost_vsock_common_start(vdev); if (ret < 0) { - return; + return 0; } ret = vhost_vsock_set_running(vdev, 1); if (ret < 0) { vhost_vsock_common_stop(vdev); error_report("Error starting vhost vsock: %d", -ret); - return; + return 0; } } else { ret = vhost_vsock_set_running(vdev, 0); if (ret < 0) { error_report("vhost vsock set running failed: %d", ret); - return; + return 0; } vhost_vsock_common_stop(vdev); } + return 0; } static uint64_t vhost_vsock_get_features(VirtIODevice *vdev, diff --git a/hw/virtio/virtio-balloon.c b/hw/virtio/virtio-balloon.c index 91510ec2e2..db787d00b3 100644 --- a/hw/virtio/virtio-balloon.c +++ b/hw/virtio/virtio-balloon.c @@ -958,7 +958,7 @@ static void virtio_balloon_device_reset(VirtIODevice *vdev) s->poison_val = 0; } -static void virtio_balloon_set_status(VirtIODevice *vdev, uint8_t status) +static int virtio_balloon_set_status(VirtIODevice *vdev, uint8_t status) { VirtIOBalloon *s = VIRTIO_BALLOON(vdev); @@ -988,6 +988,7 @@ static void virtio_balloon_set_status(VirtIODevice *vdev, uint8_t status) qemu_mutex_unlock(&s->free_page_lock); } } + return 0; } static ResettableState *virtio_balloon_get_reset_state(Object *obj) diff --git a/hw/virtio/virtio-crypto.c b/hw/virtio/virtio-crypto.c index e24d6914b6..517f2089c5 100644 --- a/hw/virtio/virtio-crypto.c +++ b/hw/virtio/virtio-crypto.c @@ -1197,11 +1197,12 @@ static void virtio_crypto_vhost_status(VirtIOCrypto *c, uint8_t status) } } -static void virtio_crypto_set_status(VirtIODevice *vdev, uint8_t status) +static int virtio_crypto_set_status(VirtIODevice *vdev, uint8_t status) { VirtIOCrypto *vcrypto = VIRTIO_CRYPTO(vdev); virtio_crypto_vhost_status(vcrypto, status); + return 0; } static void virtio_crypto_guest_notifier_mask(VirtIODevice *vdev, int idx, diff --git a/hw/virtio/virtio-iommu.c b/hw/virtio/virtio-iommu.c index 54060988ef..3500f1b082 100644 --- a/hw/virtio/virtio-iommu.c +++ b/hw/virtio/virtio-iommu.c @@ -1522,9 +1522,10 @@ static void virtio_iommu_device_reset_exit(Object *obj, ResetType type) NULL, NULL, virtio_iommu_put_endpoint); } -static void virtio_iommu_set_status(VirtIODevice *vdev, uint8_t status) +static int virtio_iommu_set_status(VirtIODevice *vdev, uint8_t status) { trace_virtio_iommu_device_status(status); + return 0; } static void virtio_iommu_instance_init(Object *obj) diff --git a/hw/virtio/virtio-rng.c b/hw/virtio/virtio-rng.c index dcb3c71d6a..3df5d2576e 100644 --- a/hw/virtio/virtio-rng.c +++ b/hw/virtio/virtio-rng.c @@ -159,17 +159,18 @@ static void check_rate_limit(void *opaque) vrng->activate_timer = true; } -static void virtio_rng_set_status(VirtIODevice *vdev, uint8_t status) +static int virtio_rng_set_status(VirtIODevice *vdev, uint8_t status) { VirtIORNG *vrng = VIRTIO_RNG(vdev); if (!vdev->vm_running) { - return; + return 0; } vdev->status = status; /* Something changed, try to process buffers */ virtio_rng_process(vrng); + return 0; } static void virtio_rng_device_realize(DeviceState *dev, Error **errp) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index e3b62522ec..1b55d8d8a2 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2221,12 +2221,12 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) { VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); trace_virtio_set_status(vdev, val); + int ret = 0; if (virtio_vdev_has_feature(vdev, VIRTIO_F_VERSION_1)) { if (!(vdev->status & VIRTIO_CONFIG_S_FEATURES_OK) && val & VIRTIO_CONFIG_S_FEATURES_OK) { - int ret = virtio_validate_features(vdev); - + ret = virtio_validate_features(vdev); if (ret) { return ret; } @@ -2239,11 +2239,15 @@ int virtio_set_status(VirtIODevice *vdev, uint8_t val) } if (k->set_status) { - k->set_status(vdev, val); + ret = k->set_status(vdev, val); + if (ret) { + qemu_log("set %s status to %d failed, old status: %d\n", + vdev->name, val, vdev->status); + } } vdev->status = val; - return 0; + return ret; } static enum virtio_device_endian virtio_default_endian(void) @@ -3419,7 +3423,7 @@ void virtio_cleanup(VirtIODevice *vdev) qemu_del_vm_change_state_handler(vdev->vmstate); } -static void virtio_vmstate_change(void *opaque, bool running, RunState state) +static int virtio_vmstate_change(void *opaque, bool running, RunState state) { VirtIODevice *vdev = opaque; BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); @@ -3436,8 +3440,12 @@ static void virtio_vmstate_change(void *opaque, bool running, RunState state) } if (!backend_run) { - virtio_set_status(vdev, vdev->status); + int ret = virtio_set_status(vdev, vdev->status); + if (ret) { + return ret; + } } + return 0; } void virtio_instance_init_common(Object *proxy_obj, void *data, @@ -3489,7 +3497,7 @@ void virtio_init(VirtIODevice *vdev, uint16_t device_id, size_t config_size) vdev->config = NULL; } vdev->vmstate = qdev_add_vm_change_state_handler(DEVICE(vdev), - virtio_vmstate_change, NULL, vdev); + NULL, virtio_vmstate_change, vdev); vdev->device_endian = virtio_default_endian(); vdev->use_guest_notifier_mask = true; } diff --git a/include/hw/virtio/vhost-scsi-common.h b/include/hw/virtio/vhost-scsi-common.h index c5d2c09455..d54d9c916f 100644 --- a/include/hw/virtio/vhost-scsi-common.h +++ b/include/hw/virtio/vhost-scsi-common.h @@ -40,7 +40,7 @@ struct VHostSCSICommon { }; int vhost_scsi_common_start(VHostSCSICommon *vsc, Error **errp); -void vhost_scsi_common_stop(VHostSCSICommon *vsc); +int vhost_scsi_common_stop(VHostSCSICommon *vsc); char *vhost_scsi_common_get_fw_dev_path(FWPathProvider *p, BusState *bus, DeviceState *dev); void vhost_scsi_common_set_config(VirtIODevice *vdev, const uint8_t *config); diff --git a/include/hw/virtio/vhost-vsock-common.h b/include/hw/virtio/vhost-vsock-common.h index 75a74e8a99..01bf6062af 100644 --- a/include/hw/virtio/vhost-vsock-common.h +++ b/include/hw/virtio/vhost-vsock-common.h @@ -42,7 +42,7 @@ struct VHostVSockCommon { }; int vhost_vsock_common_start(VirtIODevice *vdev); -void vhost_vsock_common_stop(VirtIODevice *vdev); +int vhost_vsock_common_stop(VirtIODevice *vdev); int vhost_vsock_common_pre_save(void *opaque); int vhost_vsock_common_post_load(void *opaque, int version_id); void vhost_vsock_common_realize(VirtIODevice *vdev); diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 7e0c471ea4..214d4a77e9 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -186,7 +186,7 @@ struct VirtioDeviceClass { void (*get_config)(VirtIODevice *vdev, uint8_t *config); void (*set_config)(VirtIODevice *vdev, const uint8_t *config); void (*reset)(VirtIODevice *vdev); - void (*set_status)(VirtIODevice *vdev, uint8_t val); + int (*set_status)(VirtIODevice *vdev, uint8_t val); /* Device must validate queue_index. */ void (*queue_reset)(VirtIODevice *vdev, uint32_t queue_index); /* Device must validate queue_index. */ diff --git a/include/system/vhost-user-backend.h b/include/system/vhost-user-backend.h index 5ed953cd53..5634ebdb2e 100644 --- a/include/system/vhost-user-backend.h +++ b/include/system/vhost-user-backend.h @@ -43,6 +43,6 @@ struct VhostUserBackend { int vhost_user_backend_dev_init(VhostUserBackend *b, VirtIODevice *vdev, unsigned nvqs, Error **errp); void vhost_user_backend_start(VhostUserBackend *b); -void vhost_user_backend_stop(VhostUserBackend *b); +int vhost_user_backend_stop(VhostUserBackend *b); #endif From 1a5a2629eab94297a37e4adcc5fb69beb7bb0b0c Mon Sep 17 00:00:00 2001 From: Dongli Zhang Date: Sun, 2 Feb 2025 16:52:15 -0800 Subject: [PATCH 0876/2760] vhost-scsi: support VIRTIO_SCSI_F_HOTPLUG So far there isn't way to test host kernel vhost-scsi event queue path, because VIRTIO_SCSI_F_HOTPLUG isn't supported by QEMU. virtio-scsi.c and vhost-user-scsi.c already support VIRTIO_SCSI_F_HOTPLUG as property "hotplug". Add support to vhost-scsi.c to help evaluate and test event queue. To test the feature: 1. Create vhost-scsi target with targetcli. targetcli /backstores/fileio create name=storage file_or_dev=disk01.raw targetcli /vhost create naa.1123451234512345 targetcli /vhost/naa.1123451234512345/tpg1/luns create /backstores/fileio/storage 2. Create QEMU instance with vhost-scsi. -device vhost-scsi-pci,wwpn=naa.1123451234512345,hotplug=true 3. Once guest bootup, hotplug a new LUN from host. targetcli /backstores/fileio create name=storage02 file_or_dev=disk02.raw targetcli /vhost/naa.1123451234512345/tpg1/luns create /backstores/fileio/storage02 Signed-off-by: Dongli Zhang Message-Id: <20250203005215.1502-1-dongli.zhang@oracle.com> Acked-by: Stefano Garzarella --- hw/scsi/vhost-scsi.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/scsi/vhost-scsi.c b/hw/scsi/vhost-scsi.c index dd4250ebe8..cdf405b0f8 100644 --- a/hw/scsi/vhost-scsi.c +++ b/hw/scsi/vhost-scsi.c @@ -359,6 +359,9 @@ static const Property vhost_scsi_properties[] = { DEFINE_PROP_BIT64("t10_pi", VHostSCSICommon, host_features, VIRTIO_SCSI_F_T10_PI, false), + DEFINE_PROP_BIT64("hotplug", VHostSCSICommon, host_features, + VIRTIO_SCSI_F_HOTPLUG, + false), DEFINE_PROP_BOOL("migratable", VHostSCSICommon, migratable, false), DEFINE_PROP_BOOL("worker_per_virtqueue", VirtIOSCSICommon, conf.worker_per_virtqueue, false), From 0caed25cd171c611781589b5402161d27d57229c Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 21 Apr 2025 21:17:20 +0900 Subject: [PATCH 0877/2760] virtio: Call set_features during reset virtio-net expects set_features() will be called when the feature set used by the guest changes to update the number of virtqueues but it is not called during reset, which will clear all features, leaving the queues added for VIRTIO_NET_F_MQ or VIRTIO_NET_F_RSS. Not only these extra queues are visible to the guest, they will cause segmentation fault during migration. Call set_features() during reset to remove those queues for virtio-net as we call set_status(). It will also prevent similar bugs for virtio-net and other devices in the future. Fixes: f9d6dbf0bf6e ("virtio-net: remove virtio queues if the guest doesn't support multiqueue") Buglink: https://issues.redhat.com/browse/RHEL-73842 Cc: qemu-stable@nongnu.org Signed-off-by: Akihiko Odaki Message-Id: <20250421-reset-v2-1-e4c1ead88ea1@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 1b55d8d8a2..3300f4afb0 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2320,6 +2320,8 @@ void virtio_queue_enable(VirtIODevice *vdev, uint32_t queue_index) } } +static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val); + void virtio_reset(void *opaque) { VirtIODevice *vdev = opaque; @@ -2350,7 +2352,7 @@ void virtio_reset(void *opaque) vdev->start_on_kick = false; vdev->started = false; vdev->broken = false; - vdev->guest_features = 0; + virtio_set_features_nocheck(vdev, 0); vdev->queue_sel = 0; vdev->status = 0; vdev->disabled = false; From 77a9408fc774ad99dcd16ea08f31b96b590fbf99 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 21 Apr 2025 21:17:21 +0900 Subject: [PATCH 0878/2760] virtio: Move virtio_reset() Move virtio_reset() to a later part of the file to remove the forward declaration of virtio_set_features_nocheck() and to prepare the situation that we need more operations to perform during reset. Signed-off-by: Akihiko Odaki Message-Id: <20250421-reset-v2-2-e4c1ead88ea1@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 88 ++++++++++++++++++++++------------------------ 1 file changed, 43 insertions(+), 45 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 3300f4afb0..2e98cecf64 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -2320,51 +2320,6 @@ void virtio_queue_enable(VirtIODevice *vdev, uint32_t queue_index) } } -static int virtio_set_features_nocheck(VirtIODevice *vdev, uint64_t val); - -void virtio_reset(void *opaque) -{ - VirtIODevice *vdev = opaque; - VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); - int i; - - virtio_set_status(vdev, 0); - if (current_cpu) { - /* Guest initiated reset */ - vdev->device_endian = virtio_current_cpu_endian(); - } else { - /* System reset */ - vdev->device_endian = virtio_default_endian(); - } - - if (k->get_vhost) { - struct vhost_dev *hdev = k->get_vhost(vdev); - /* Only reset when vhost back-end is connected */ - if (hdev && hdev->vhost_ops) { - vhost_reset_device(hdev); - } - } - - if (k->reset) { - k->reset(vdev); - } - - vdev->start_on_kick = false; - vdev->started = false; - vdev->broken = false; - virtio_set_features_nocheck(vdev, 0); - vdev->queue_sel = 0; - vdev->status = 0; - vdev->disabled = false; - qatomic_set(&vdev->isr, 0); - vdev->config_vector = VIRTIO_NO_VECTOR; - virtio_notify_vector(vdev, vdev->config_vector); - - for(i = 0; i < VIRTIO_QUEUE_MAX; i++) { - __virtio_queue_reset(vdev, i); - } -} - void virtio_queue_set_addr(VirtIODevice *vdev, int n, hwaddr addr) { if (!vdev->vq[n].vring.num) { @@ -3175,6 +3130,49 @@ int virtio_set_features(VirtIODevice *vdev, uint64_t val) return ret; } +void virtio_reset(void *opaque) +{ + VirtIODevice *vdev = opaque; + VirtioDeviceClass *k = VIRTIO_DEVICE_GET_CLASS(vdev); + int i; + + virtio_set_status(vdev, 0); + if (current_cpu) { + /* Guest initiated reset */ + vdev->device_endian = virtio_current_cpu_endian(); + } else { + /* System reset */ + vdev->device_endian = virtio_default_endian(); + } + + if (k->get_vhost) { + struct vhost_dev *hdev = k->get_vhost(vdev); + /* Only reset when vhost back-end is connected */ + if (hdev && hdev->vhost_ops) { + vhost_reset_device(hdev); + } + } + + if (k->reset) { + k->reset(vdev); + } + + vdev->start_on_kick = false; + vdev->started = false; + vdev->broken = false; + virtio_set_features_nocheck(vdev, 0); + vdev->queue_sel = 0; + vdev->status = 0; + vdev->disabled = false; + qatomic_set(&vdev->isr, 0); + vdev->config_vector = VIRTIO_NO_VECTOR; + virtio_notify_vector(vdev, vdev->config_vector); + + for (i = 0; i < VIRTIO_QUEUE_MAX; i++) { + __virtio_queue_reset(vdev, i); + } +} + static void virtio_device_check_notification_compatibility(VirtIODevice *vdev, Error **errp) { From b1c84782bfddeaa0070f5ae57ac2e4e3992f9f19 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Wed, 30 Apr 2025 12:48:06 +0000 Subject: [PATCH 0879/2760] intel_iommu: Use BQL_LOCK_GUARD to manage cleanup automatically vtd_switch_address_space needs to take the BQL if not already held. Use BQL_LOCK_GUARD to make the iommu implementation more consistent. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250430124750.240412-2-clement.mathieu--drif@eviden.com> Reviewed-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 5f8ed1243d..b925e65b02 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1728,8 +1728,6 @@ static bool vtd_as_pt_enabled(VTDAddressSpace *as) static bool vtd_switch_address_space(VTDAddressSpace *as) { bool use_iommu, pt; - /* Whether we need to take the BQL on our own */ - bool take_bql = !bql_locked(); assert(as); @@ -1746,9 +1744,7 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) * from vtd_pt_enable_fast_path(). However the memory APIs need * it. We'd better make sure we have had it already, or, take it. */ - if (take_bql) { - bql_lock(); - } + BQL_LOCK_GUARD(); /* Turn off first then on the other */ if (use_iommu) { @@ -1801,10 +1797,6 @@ static bool vtd_switch_address_space(VTDAddressSpace *as) memory_region_set_enabled(&as->iommu_ir_fault, false); } - if (take_bql) { - bql_unlock(); - } - return use_iommu; } From 6ea7a5762aa1aacb7a5410e4b805bb8c99c6f133 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 8 May 2025 16:41:16 +0200 Subject: [PATCH 0880/2760] include/hw/dma/xlnx_dpdma: Remove dependency on console.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit console.h brings a dependency on the and the pixman header file (if available), so we should avoid to include this file if it is not really necessary. console.h does not seem to be necessary for the xlnx_dpdma code, so drop the include here. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250508144120.163009-2-thuth@redhat.com> --- include/hw/dma/xlnx_dpdma.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/hw/dma/xlnx_dpdma.h b/include/hw/dma/xlnx_dpdma.h index 1ec0d265be..484b2e377f 100644 --- a/include/hw/dma/xlnx_dpdma.h +++ b/include/hw/dma/xlnx_dpdma.h @@ -26,7 +26,6 @@ #define XLNX_DPDMA_H #include "hw/sysbus.h" -#include "ui/console.h" #include "system/dma.h" #include "qom/object.h" From 7c312d8539fe69338f7343b6514f2dc77ae2efa6 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 24 Apr 2025 11:06:40 +0200 Subject: [PATCH 0881/2760] tests/functional/test_s390x_tuxrun: Check whether the machine is available The s390x tuxrun test lacks the call to self.set_machine(), so this test is currently failing in case the 's390-ccw-virtio' machine has not been compiled into the binary. Add the check now to fix it. Signed-off-by: Thomas Huth Message-ID: <20250424090640.664217-1-thuth@redhat.com> --- tests/functional/test_s390x_tuxrun.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/test_s390x_tuxrun.py b/tests/functional/test_s390x_tuxrun.py index a7db4bfd84..8df3c6893b 100755 --- a/tests/functional/test_s390x_tuxrun.py +++ b/tests/functional/test_s390x_tuxrun.py @@ -24,6 +24,7 @@ class TuxRunS390xTest(TuxRunBaselineTest): 'bff7971fc2fef56372d98afe4557b82fd0a785a241e44c29b058e577ad1bbb44') def test_s390(self): + self.set_machine('s390-ccw-virtio') self.wait_for_shutdown=False self.common_tuxrun(kernel_asset=self.ASSET_S390X_KERNEL, rootfs_asset=self.ASSET_S390X_ROOTFS, From c23d3339ce8fc936d8c60a023ea2b052d847dc78 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 25 Mar 2025 09:17:13 +0100 Subject: [PATCH 0882/2760] tests/functional: Skip the screendump tests if the command is not available It is possible nowadays to compile QEMU without pixman support - in that case the screendump command is not available and the related tests fail. Thus skip these tests if the screendump command could not be executed. Signed-off-by: Thomas Huth Message-ID: <20250325081713.283490-2-thuth@redhat.com> --- tests/functional/test_arm_integratorcp.py | 6 ++++-- tests/functional/test_m68k_nextcube.py | 6 ++++-- tests/functional/test_mips64el_malta.py | 6 ++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/tests/functional/test_arm_integratorcp.py b/tests/functional/test_arm_integratorcp.py index a85b339d77..4f00924aa0 100755 --- a/tests/functional/test_arm_integratorcp.py +++ b/tests/functional/test_arm_integratorcp.py @@ -73,8 +73,10 @@ def test_framebuffer_tux_logo(self): framebuffer_ready = 'Console: switching to colour frame buffer device' wait_for_console_pattern(self, framebuffer_ready) self.vm.cmd('human-monitor-command', command_line='stop') - self.vm.cmd('human-monitor-command', - command_line='screendump %s' % screendump_path) + res = self.vm.cmd('human-monitor-command', + command_line='screendump %s' % screendump_path) + if 'unknown command' in res: + self.skipTest('screendump not available') logger = logging.getLogger('framebuffer') cpu_count = 1 diff --git a/tests/functional/test_m68k_nextcube.py b/tests/functional/test_m68k_nextcube.py index ff773a7994..13c72bd136 100755 --- a/tests/functional/test_m68k_nextcube.py +++ b/tests/functional/test_m68k_nextcube.py @@ -32,8 +32,10 @@ def check_bootrom_framebuffer(self, screenshot_path): # TODO: wait for the 'displaysurface_create 1120x832' trace-event. time.sleep(2) - self.vm.cmd('human-monitor-command', - command_line='screendump %s' % screenshot_path) + res = self.vm.cmd('human-monitor-command', + command_line='screendump %s' % screenshot_path) + if 'unknown command' in res: + self.skipTest('screendump not available') @skipIfMissingImports("PIL") def test_bootrom_framebuffer_size(self): diff --git a/tests/functional/test_mips64el_malta.py b/tests/functional/test_mips64el_malta.py index dd37212f9d..3cc79b74c1 100755 --- a/tests/functional/test_mips64el_malta.py +++ b/tests/functional/test_mips64el_malta.py @@ -155,8 +155,10 @@ def do_test_i6400_framebuffer_logo(self, cpu_cores_count): framebuffer_ready = 'Console: switching to colour frame buffer device' self.wait_for_console_pattern(framebuffer_ready) self.vm.cmd('human-monitor-command', command_line='stop') - self.vm.cmd('human-monitor-command', - command_line='screendump %s' % screendump_path) + res = self.vm.cmd('human-monitor-command', + command_line='screendump %s' % screendump_path) + if 'unknown command' in res: + self.skipTest('screendump not available') logger = logging.getLogger('framebuffer') match_threshold = 0.95 From 1b85dff5f0be30ddbcb7edbd3c084c9c5ee351ca Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Wed, 30 Apr 2025 12:48:06 +0000 Subject: [PATCH 0883/2760] intel_iommu: Take locks when looking for and creating address spaces vtd_find_add_as can be called by multiple threads which leads to a race condition. Taking the IOMMU lock ensures we avoid such a race. Moreover we also need to take the bql to avoid an assert to fail in memory_region_add_subregion_overlap when actually allocating a new address space. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250430124750.240412-3-clement.mathieu--drif@eviden.com> Reviewed-by: Zhenzhong Duan Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index b925e65b02..69d72ad35c 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -4205,9 +4205,30 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, VTDAddressSpace *vtd_dev_as; char name[128]; + vtd_iommu_lock(s); vtd_dev_as = g_hash_table_lookup(s->vtd_address_spaces, &key); + vtd_iommu_unlock(s); + if (!vtd_dev_as) { - struct vtd_as_key *new_key = g_malloc(sizeof(*new_key)); + struct vtd_as_key *new_key; + /* Slow path */ + + /* + * memory_region_add_subregion_overlap requires the bql, + * make sure we own it. + */ + BQL_LOCK_GUARD(); + vtd_iommu_lock(s); + + /* Check again as we released the lock for a moment */ + vtd_dev_as = g_hash_table_lookup(s->vtd_address_spaces, &key); + if (vtd_dev_as) { + vtd_iommu_unlock(s); + return vtd_dev_as; + } + + /* Still nothing, allocate a new address space */ + new_key = g_malloc(sizeof(*new_key)); new_key->bus = bus; new_key->devfn = devfn; @@ -4298,6 +4319,8 @@ VTDAddressSpace *vtd_find_add_as(IntelIOMMUState *s, PCIBus *bus, vtd_switch_address_space(vtd_dev_as); g_hash_table_insert(s->vtd_address_spaces, new_key, vtd_dev_as); + + vtd_iommu_unlock(s); } return vtd_dev_as; } From f864a3235ea1d1d714b3cde2d9a810ea6344a7b5 Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Sun, 4 May 2025 17:04:04 +0000 Subject: [PATCH 0884/2760] hw/i386/amd_iommu: Isolate AMDVI-PCI from amd-iommu device to allow full control over the PCI device creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current amd-iommu model internally creates an AMDVI-PCI device. Here is a snippet from info qtree: bus: main-system-bus type System dev: amd-iommu, id "" xtsup = false pci-id = "" intremap = "on" device-iotlb = false pt = true ... dev: q35-pcihost, id "" MCFG = -1 (0xffffffffffffffff) pci-hole64-size = 34359738368 (32 GiB) below-4g-mem-size = 134217728 (128 MiB) above-4g-mem-size = 0 (0 B) smm-ranges = true x-pci-hole64-fix = true x-config-reg-migration-enabled = true bypass-iommu = false bus: pcie.0 type PCIE dev: AMDVI-PCI, id "" addr = 01.0 romfile = "" romsize = 4294967295 (0xffffffff) rombar = -1 (0xffffffffffffffff) multifunction = false x-pcie-lnksta-dllla = true x-pcie-extcap-init = true failover_pair_id = "" acpi-index = 0 (0x0) x-pcie-err-unc-mask = true x-pcie-ari-nextfn-1 = false x-max-bounce-buffer-size = 4096 (4 KiB) x-pcie-ext-tag = true busnr = 0 (0x0) class Class 0806, addr 00:01.0, pci id 1022:0000 (sub 1af4:1100) ... This prohibits users from specifying the PCI topology for the amd-iommu device, which becomes a problem when trying to support VM migration since it does not guarantee the same enumeration of AMD IOMMU device. Therefore, allow the 'AMDVI-PCI' device to optionally be pre-created and associated with a 'amd-iommu' device via a new 'pci-id' parameter on the latter. For example: -device AMDVI-PCI,id=iommupci0,bus=pcie.0,addr=0x05 \ -device amd-iommu,intremap=on,pt=on,xtsup=on,pci-id=iommupci0 \ For backward-compatibility, internally create the AMDVI-PCI device if not specified on the CLI. Co-developed-by: Daniel P. Berrangé Reviewed-by: Daniel P. Berrangé Signed-off-by: Suravee Suthikulpanit Message-Id: <20250504170405.12623-2-suravee.suthikulpanit@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 8 +++---- hw/i386/amd_iommu.c | 53 ++++++++++++++++++++++++++------------------ hw/i386/amd_iommu.h | 3 ++- 3 files changed, 38 insertions(+), 26 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index f40ad062f9..61851cc840 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -2333,10 +2333,10 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker, const char *oem_id, build_append_int_noprefix(table_data, ivhd_blob->len + 24, 2); /* DeviceID */ build_append_int_noprefix(table_data, - object_property_get_int(OBJECT(&s->pci), "addr", + object_property_get_int(OBJECT(s->pci), "addr", &error_abort), 2); /* Capability offset */ - build_append_int_noprefix(table_data, s->pci.capab_offset, 2); + build_append_int_noprefix(table_data, s->pci->capab_offset, 2); /* IOMMU base address */ build_append_int_noprefix(table_data, s->mr_mmio.addr, 8); /* PCI Segment Group */ @@ -2368,10 +2368,10 @@ build_amd_iommu(GArray *table_data, BIOSLinker *linker, const char *oem_id, build_append_int_noprefix(table_data, ivhd_blob->len + 40, 2); /* DeviceID */ build_append_int_noprefix(table_data, - object_property_get_int(OBJECT(&s->pci), "addr", + object_property_get_int(OBJECT(s->pci), "addr", &error_abort), 2); /* Capability offset */ - build_append_int_noprefix(table_data, s->pci.capab_offset, 2); + build_append_int_noprefix(table_data, s->pci->capab_offset, 2); /* IOMMU base address */ build_append_int_noprefix(table_data, s->mr_mmio.addr, 8); /* PCI Segment Group */ diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 2cf7e24a21..f5466fdc98 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -167,11 +167,11 @@ static void amdvi_generate_msi_interrupt(AMDVIState *s) { MSIMessage msg = {}; MemTxAttrs attrs = { - .requester_id = pci_requester_id(&s->pci.dev) + .requester_id = pci_requester_id(&s->pci->dev) }; - if (msi_enabled(&s->pci.dev)) { - msg = msi_get_message(&s->pci.dev, 0); + if (msi_enabled(&s->pci->dev)) { + msg = msi_get_message(&s->pci->dev, 0); address_space_stl_le(&address_space_memory, msg.address, msg.data, attrs, NULL); } @@ -239,7 +239,7 @@ static void amdvi_page_fault(AMDVIState *s, uint16_t devid, info |= AMDVI_EVENT_IOPF_I | AMDVI_EVENT_IOPF; amdvi_encode_event(evt, devid, addr, info); amdvi_log_event(s, evt); - pci_word_test_and_set_mask(s->pci.dev.config + PCI_STATUS, + pci_word_test_and_set_mask(s->pci->dev.config + PCI_STATUS, PCI_STATUS_SIG_TARGET_ABORT); } /* @@ -256,7 +256,7 @@ static void amdvi_log_devtab_error(AMDVIState *s, uint16_t devid, amdvi_encode_event(evt, devid, devtab, info); amdvi_log_event(s, evt); - pci_word_test_and_set_mask(s->pci.dev.config + PCI_STATUS, + pci_word_test_and_set_mask(s->pci->dev.config + PCI_STATUS, PCI_STATUS_SIG_TARGET_ABORT); } /* log an event trying to access command buffer @@ -269,7 +269,7 @@ static void amdvi_log_command_error(AMDVIState *s, hwaddr addr) amdvi_encode_event(evt, 0, addr, info); amdvi_log_event(s, evt); - pci_word_test_and_set_mask(s->pci.dev.config + PCI_STATUS, + pci_word_test_and_set_mask(s->pci->dev.config + PCI_STATUS, PCI_STATUS_SIG_TARGET_ABORT); } /* log an illegal command event @@ -310,7 +310,7 @@ static void amdvi_log_pagetab_error(AMDVIState *s, uint16_t devid, info |= AMDVI_EVENT_PAGE_TAB_HW_ERROR; amdvi_encode_event(evt, devid, addr, info); amdvi_log_event(s, evt); - pci_word_test_and_set_mask(s->pci.dev.config + PCI_STATUS, + pci_word_test_and_set_mask(s->pci->dev.config + PCI_STATUS, PCI_STATUS_SIG_TARGET_ABORT); } @@ -1607,7 +1607,7 @@ static void amdvi_sysbus_reset(DeviceState *dev) { AMDVIState *s = AMD_IOMMU_DEVICE(dev); - msi_reset(&s->pci.dev); + msi_reset(&s->pci->dev); amdvi_init(s); } @@ -1619,14 +1619,32 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) X86MachineState *x86ms = X86_MACHINE(ms); PCIBus *bus = pcms->pcibus; - s->iotlb = g_hash_table_new_full(amdvi_uint64_hash, - amdvi_uint64_equal, g_free, g_free); + if (s->pci_id) { + PCIDevice *pdev = NULL; + int ret = pci_qdev_find_device(s->pci_id, &pdev); - /* This device should take care of IOMMU PCI properties */ - if (!qdev_realize(DEVICE(&s->pci), &bus->qbus, errp)) { - return; + if (ret) { + error_report("Cannot find PCI device '%s'", s->pci_id); + return; + } + + if (!object_dynamic_cast(OBJECT(pdev), TYPE_AMD_IOMMU_PCI)) { + error_report("Device '%s' must be an AMDVI-PCI device type", s->pci_id); + return; + } + + s->pci = AMD_IOMMU_PCI(pdev); + } else { + s->pci = AMD_IOMMU_PCI(object_new(TYPE_AMD_IOMMU_PCI)); + /* This device should take care of IOMMU PCI properties */ + if (!qdev_realize(DEVICE(s->pci), &bus->qbus, errp)) { + return; + } } + s->iotlb = g_hash_table_new_full(amdvi_uint64_hash, + amdvi_uint64_equal, g_free, g_free); + /* Pseudo address space under root PCI bus. */ x86ms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID); @@ -1663,6 +1681,7 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) static const Property amdvi_properties[] = { DEFINE_PROP_BOOL("xtsup", AMDVIState, xtsup, false), + DEFINE_PROP_STRING("pci-id", AMDVIState, pci_id), }; static const VMStateDescription vmstate_amdvi_sysbus = { @@ -1670,13 +1689,6 @@ static const VMStateDescription vmstate_amdvi_sysbus = { .unmigratable = 1 }; -static void amdvi_sysbus_instance_init(Object *klass) -{ - AMDVIState *s = AMD_IOMMU_DEVICE(klass); - - object_initialize(&s->pci, sizeof(s->pci), TYPE_AMD_IOMMU_PCI); -} - static void amdvi_sysbus_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -1696,7 +1708,6 @@ static const TypeInfo amdvi_sysbus = { .name = TYPE_AMD_IOMMU_DEVICE, .parent = TYPE_X86_IOMMU_DEVICE, .instance_size = sizeof(AMDVIState), - .instance_init = amdvi_sysbus_instance_init, .class_init = amdvi_sysbus_class_init }; diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 28125130c6..7a28181d9c 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -315,7 +315,8 @@ struct AMDVIPCIState { struct AMDVIState { X86IOMMUState iommu; /* IOMMU bus device */ - AMDVIPCIState pci; /* IOMMU PCI device */ + AMDVIPCIState *pci; /* IOMMU PCI device */ + char *pci_id; /* ID of AMDVI-PCI device, if user created */ uint32_t version; From 28931c2e1591deb4bfaaf744fdc8813e96c230f1 Mon Sep 17 00:00:00 2001 From: Suravee Suthikulpanit Date: Sun, 4 May 2025 17:04:05 +0000 Subject: [PATCH 0885/2760] hw/i386/amd_iommu: Allow migration when explicitly create the AMDVI-PCI device Add migration support for AMD IOMMU model by saving necessary AMDVIState parameters for MMIO registers, device table, command buffer, and event buffers. Also change devtab_len type from size_t to uint64_t to avoid 32-bit build issue. Signed-off-by: Suravee Suthikulpanit Message-Id: <20250504170405.12623-3-suravee.suthikulpanit@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 48 +++++++++++++++++++++++++++++++++++++++++++++ hw/i386/amd_iommu.h | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index f5466fdc98..0775c8f3bb 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1611,8 +1611,55 @@ static void amdvi_sysbus_reset(DeviceState *dev) amdvi_init(s); } +static const VMStateDescription vmstate_amdvi_sysbus_migratable = { + .name = "amd-iommu", + .version_id = 1, + .minimum_version_id = 1, + .priority = MIG_PRI_IOMMU, + .fields = (VMStateField[]) { + /* Updated in amdvi_handle_control_write() */ + VMSTATE_BOOL(enabled, AMDVIState), + VMSTATE_BOOL(ga_enabled, AMDVIState), + VMSTATE_BOOL(ats_enabled, AMDVIState), + VMSTATE_BOOL(cmdbuf_enabled, AMDVIState), + VMSTATE_BOOL(completion_wait_intr, AMDVIState), + VMSTATE_BOOL(evtlog_enabled, AMDVIState), + VMSTATE_BOOL(evtlog_intr, AMDVIState), + /* Updated in amdvi_handle_devtab_write() */ + VMSTATE_UINT64(devtab, AMDVIState), + VMSTATE_UINT64(devtab_len, AMDVIState), + /* Updated in amdvi_handle_cmdbase_write() */ + VMSTATE_UINT64(cmdbuf, AMDVIState), + VMSTATE_UINT64(cmdbuf_len, AMDVIState), + /* Updated in amdvi_handle_cmdhead_write() */ + VMSTATE_UINT32(cmdbuf_head, AMDVIState), + /* Updated in amdvi_handle_cmdtail_write() */ + VMSTATE_UINT32(cmdbuf_tail, AMDVIState), + /* Updated in amdvi_handle_evtbase_write() */ + VMSTATE_UINT64(evtlog, AMDVIState), + VMSTATE_UINT32(evtlog_len, AMDVIState), + /* Updated in amdvi_handle_evthead_write() */ + VMSTATE_UINT32(evtlog_head, AMDVIState), + /* Updated in amdvi_handle_evttail_write() */ + VMSTATE_UINT32(evtlog_tail, AMDVIState), + /* Updated in amdvi_handle_pprbase_write() */ + VMSTATE_UINT64(ppr_log, AMDVIState), + VMSTATE_UINT32(pprlog_len, AMDVIState), + /* Updated in amdvi_handle_pprhead_write() */ + VMSTATE_UINT32(pprlog_head, AMDVIState), + /* Updated in amdvi_handle_tailhead_write() */ + VMSTATE_UINT32(pprlog_tail, AMDVIState), + /* MMIO registers */ + VMSTATE_UINT8_ARRAY(mmior, AMDVIState, AMDVI_MMIO_SIZE), + VMSTATE_UINT8_ARRAY(romask, AMDVIState, AMDVI_MMIO_SIZE), + VMSTATE_UINT8_ARRAY(w1cmask, AMDVIState, AMDVI_MMIO_SIZE), + VMSTATE_END_OF_LIST() + } +}; + static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) { + DeviceClass *dc = (DeviceClass *) object_get_class(OBJECT(dev)); AMDVIState *s = AMD_IOMMU_DEVICE(dev); MachineState *ms = MACHINE(qdev_get_machine()); PCMachineState *pcms = PC_MACHINE(ms); @@ -1634,6 +1681,7 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) } s->pci = AMD_IOMMU_PCI(pdev); + dc->vmsd = &vmstate_amdvi_sysbus_migratable; } else { s->pci = AMD_IOMMU_PCI(object_new(TYPE_AMD_IOMMU_PCI)); /* This device should take care of IOMMU PCI properties */ diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 7a28181d9c..5672bdef89 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -329,7 +329,7 @@ struct AMDVIState { bool excl_enabled; hwaddr devtab; /* base address device table */ - size_t devtab_len; /* device table length */ + uint64_t devtab_len; /* device table length */ hwaddr cmdbuf; /* command buffer base address */ uint64_t cmdbuf_len; /* command buffer length */ From 9c6675e8a5824f4c41c3d122c4b848a67d9d0350 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 14 May 2025 14:29:45 +0100 Subject: [PATCH 0886/2760] target/microblaze: Use 'obj' in DEVICE() casts in mb_cpu_initfn() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're about to make a change that removes the only other use of the 'cpu' local variable in mb_cpu_initfn(); since the DEVICE() casts work fine with the Object*, use that instead, so that we can remove the local variable when we make the following change. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Reviewed-by: Richard Henderson Message-id: 20250429132200.605611-2-peter.maydell@linaro.org --- target/microblaze/cpu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index d069e40e70..d895d68395 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -344,11 +344,11 @@ static void mb_cpu_initfn(Object *obj) #ifndef CONFIG_USER_ONLY /* Inbound IRQ and FIR lines */ - qdev_init_gpio_in(DEVICE(cpu), microblaze_cpu_set_irq, 2); - qdev_init_gpio_in_named(DEVICE(cpu), mb_cpu_ns_axi_dp, "ns_axi_dp", 1); - qdev_init_gpio_in_named(DEVICE(cpu), mb_cpu_ns_axi_ip, "ns_axi_ip", 1); - qdev_init_gpio_in_named(DEVICE(cpu), mb_cpu_ns_axi_dc, "ns_axi_dc", 1); - qdev_init_gpio_in_named(DEVICE(cpu), mb_cpu_ns_axi_ic, "ns_axi_ic", 1); + qdev_init_gpio_in(DEVICE(obj), microblaze_cpu_set_irq, 2); + qdev_init_gpio_in_named(DEVICE(obj), mb_cpu_ns_axi_dp, "ns_axi_dp", 1); + qdev_init_gpio_in_named(DEVICE(obj), mb_cpu_ns_axi_ip, "ns_axi_ip", 1); + qdev_init_gpio_in_named(DEVICE(obj), mb_cpu_ns_axi_dc, "ns_axi_dc", 1); + qdev_init_gpio_in_named(DEVICE(obj), mb_cpu_ns_axi_ic, "ns_axi_ic", 1); #endif /* Restricted 'endianness' property is equivalent of 'little-endian' */ From 6222ae143da0408c7e5b727082975dc394f5a23f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 14 May 2025 14:29:45 +0100 Subject: [PATCH 0887/2760] target/microblaze: Delay gdb_register_coprocessor() to realize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the microblaze code calls gdb_register_coprocessor() in its initfn. This works, but we would like to delay setting up GDB registers until realize. All other target architectures only call gdb_register_coprocessor() in realize, after the call to cpu_exec_realizefn(). Move the microblaze gdb_register_coprocessor() use, bringing it in line with other targets. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250429132200.605611-3-peter.maydell@linaro.org --- target/microblaze/cpu.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index d895d68395..615a959200 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -263,6 +263,11 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) return; } + gdb_register_coprocessor(cs, mb_cpu_gdb_read_stack_protect, + mb_cpu_gdb_write_stack_protect, + gdb_find_static_feature("microblaze-stack-protect.xml"), + 0); + qemu_init_vcpu(cs); version = cpu->cfg.version ? cpu->cfg.version : DEFAULT_CPU_VERSION; @@ -335,13 +340,6 @@ static void mb_cpu_realizefn(DeviceState *dev, Error **errp) static void mb_cpu_initfn(Object *obj) { - MicroBlazeCPU *cpu = MICROBLAZE_CPU(obj); - - gdb_register_coprocessor(CPU(cpu), mb_cpu_gdb_read_stack_protect, - mb_cpu_gdb_write_stack_protect, - gdb_find_static_feature("microblaze-stack-protect.xml"), - 0); - #ifndef CONFIG_USER_ONLY /* Inbound IRQ and FIR lines */ qdev_init_gpio_in(DEVICE(obj), microblaze_cpu_set_irq, 2); From b4ae54989b911876540ba4ba9090901235e3047a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 14 May 2025 14:29:45 +0100 Subject: [PATCH 0888/2760] hw/core/cpu-common: Don't init gdbstub until cpu_exec_realizefn() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we call gdb_init_cpu() in cpu_common_initfn(), which is very early in the CPU object's init->realize creation sequence. In particular this happens before the architecture-specific subclass's init fn has even run. This means that gdb_init_cpu() can only do things that depend strictly on the class, not on the object, because the CPUState* that it is passed is currently half-initialized. In commit a1f728ecc90cf6c6 we accidentally broke this rule, by adding a call to the gdb_get_core_xml_file method which takes the CPUState. At the moment we get away with this because the only implementation doesn't actually look at the pointer it is passed. However the whole reason we created that method was so that we could make the "which XML file?" decision based on a property of the CPU object, and we currently can't change the Arm implementation of the method to do what we want without causing wrong behaviour or a crash. The ordering restrictions here are: * we must call gdb_init_cpu before: - any call to gdb_register_coprocessor() - any use of the gdb_num_regs field (this is only used in code that's about to call gdb_register_coprocessor() and wants to know the first register number of the set of registers it's about to add) * we must call gdb_init_cpu after CPU properties have been set, which is to say somewhere in realize The function cpu_exec_realizefn() meets both of these requirements, as it is called by the architecture-specific CPU realize function early in realize, before any calls ot gdb_register_coprocessor(). Move the gdb_init_cpu() call to there. Signed-off-by: Peter Maydell Reviewed-by: Edgar E. Iglesias Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250429132200.605611-4-peter.maydell@linaro.org --- hw/core/cpu-common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/core/cpu-common.c b/hw/core/cpu-common.c index 92c40b6bf8..39e674aca2 100644 --- a/hw/core/cpu-common.c +++ b/hw/core/cpu-common.c @@ -234,6 +234,8 @@ bool cpu_exec_realizefn(CPUState *cpu, Error **errp) return false; } + gdb_init_cpu(cpu); + /* Wait until cpu initialization complete before exposing cpu. */ cpu_list_add(cpu); @@ -304,7 +306,6 @@ static void cpu_common_initfn(Object *obj) /* cache the cpu class for the hotpath */ cpu->cc = CPU_GET_CLASS(cpu); - gdb_init_cpu(cpu); cpu->cpu_index = UNASSIGNED_CPU_INDEX; cpu->cluster_index = UNASSIGNED_CLUSTER_INDEX; cpu->as = NULL; From d2c655a5f42ef7466c70cb223a9b9fc292f6b593 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 14 May 2025 14:29:46 +0100 Subject: [PATCH 0889/2760] target/arm: Present AArch64 gdbstub based on ARM_FEATURE_AARCH64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we provide an AArch64 gdbstub for CPUs which are TYPE_AARCH64_CPU, and an AArch32 gdbstub for those which are only TYPE_ARM_CPU. This mostly does the right thing, except in the corner case of KVM with -cpu host,aarch64=off. That produces a CPU which is TYPE_AARCH64_CPU but which has ARM_FEATURE_AARCH64 removed and which to the guest is in AArch32 mode. Now we have moved all the handling of AArch64-vs-AArch32 gdbstub behaviour into TYPE_ARM_CPU we can change the condition we use for whether to select the AArch64 gdbstub to look at ARM_FEATURE_AARCH64. This will mean that we now correctly provide an AArch32 gdbstub for aarch64=off CPUs. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250429132200.605611-5-peter.maydell@linaro.org --- target/arm/internals.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 660d3a88e0..a396c0be3b 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1833,7 +1833,7 @@ void aarch64_add_sme_properties(Object *obj); /* Return true if the gdbstub is presenting an AArch64 CPU */ static inline bool arm_gdbstub_is_aarch64(ARMCPU *cpu) { - return object_dynamic_cast(OBJECT(cpu), TYPE_AARCH64_CPU); + return arm_feature(&cpu->env, ARM_FEATURE_AARCH64); } /* Read the CONTROL register as the MRS instruction would. */ From 0ab97bc070f9df7fd155707c7800667cbf26790f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 14 May 2025 14:29:46 +0100 Subject: [PATCH 0890/2760] target/arm: Move aarch64 CPU property code to TYPE_ARM_CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The only thing we have left in the TYPE_AARCH64_CPU class that makes it different to TYPE_ARM_CPU is that we register the handling of the "aarch64" property there. Move the handling of this property to the base class, where we make it a property of the object rather than of the class, and add it to the CPU if it has the ARM_FEATURE_AARCH64 property present at init. This is in line with how we handle other Arm CPU properties, and should not change which CPUs it's visible for. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250429132200.605611-6-peter.maydell@linaro.org --- target/arm/cpu.c | 36 ++++++++++++++++++++++++++++++++++++ target/arm/cpu64.c | 33 --------------------------------- 2 files changed, 36 insertions(+), 33 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 45cb6fd7ee..603f08d05a 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1609,6 +1609,35 @@ static void arm_set_pmu(Object *obj, bool value, Error **errp) cpu->has_pmu = value; } +static bool aarch64_cpu_get_aarch64(Object *obj, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + + return arm_feature(&cpu->env, ARM_FEATURE_AARCH64); +} + +static void aarch64_cpu_set_aarch64(Object *obj, bool value, Error **errp) +{ + ARMCPU *cpu = ARM_CPU(obj); + + /* + * At this time, this property is only allowed if KVM is enabled. This + * restriction allows us to avoid fixing up functionality that assumes a + * uniform execution state like do_interrupt. + */ + if (value == false) { + if (!kvm_enabled() || !kvm_arm_aarch32_supported()) { + error_setg(errp, "'aarch64' feature cannot be disabled " + "unless KVM is enabled and 32-bit EL1 " + "is supported"); + return; + } + unset_feature(&cpu->env, ARM_FEATURE_AARCH64); + } else { + set_feature(&cpu->env, ARM_FEATURE_AARCH64); + } +} + unsigned int gt_cntfrq_period_ns(ARMCPU *cpu) { /* @@ -1736,6 +1765,13 @@ void arm_cpu_post_init(Object *obj) */ arm_cpu_propagate_feature_implications(cpu); + if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { + object_property_add_bool(obj, "aarch64", aarch64_cpu_get_aarch64, + aarch64_cpu_set_aarch64); + object_property_set_description(obj, "aarch64", + "Set on/off to enable/disable aarch64 " + "execution state "); + } if (arm_feature(&cpu->env, ARM_FEATURE_CBAR) || arm_feature(&cpu->env, ARM_FEATURE_CBAR_RO)) { qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_cbar_property); diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 00629a5d1d..e527465a3c 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -781,45 +781,12 @@ static const ARMCPUInfo aarch64_cpus[] = { #endif }; -static bool aarch64_cpu_get_aarch64(Object *obj, Error **errp) -{ - ARMCPU *cpu = ARM_CPU(obj); - - return arm_feature(&cpu->env, ARM_FEATURE_AARCH64); -} - -static void aarch64_cpu_set_aarch64(Object *obj, bool value, Error **errp) -{ - ARMCPU *cpu = ARM_CPU(obj); - - /* At this time, this property is only allowed if KVM is enabled. This - * restriction allows us to avoid fixing up functionality that assumes a - * uniform execution state like do_interrupt. - */ - if (value == false) { - if (!kvm_enabled() || !kvm_arm_aarch32_supported()) { - error_setg(errp, "'aarch64' feature cannot be disabled " - "unless KVM is enabled and 32-bit EL1 " - "is supported"); - return; - } - unset_feature(&cpu->env, ARM_FEATURE_AARCH64); - } else { - set_feature(&cpu->env, ARM_FEATURE_AARCH64); - } -} - static void aarch64_cpu_finalizefn(Object *obj) { } static void aarch64_cpu_class_init(ObjectClass *oc, const void *data) { - object_class_property_add_bool(oc, "aarch64", aarch64_cpu_get_aarch64, - aarch64_cpu_set_aarch64); - object_class_property_set_description(oc, "aarch64", - "Set on/off to enable/disable aarch64 " - "execution state "); } static void aarch64_cpu_instance_init(Object *obj) From c6650a8c6cc18cdc4a46a6eef41b7d57e6bc0b2b Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 14 May 2025 14:29:46 +0100 Subject: [PATCH 0891/2760] target/arm/kvm: don't check TYPE_AARCH64_CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want to merge TYPE_AARCH64_CPU with TYPE_ARM_CPU, so enforcing in kvm_arch_init_vcpu() that the CPU class is a subclass of TYPE_AARCH64_CPU will no longer be possible. It's safe to just remove this test, because any purely-AArch32 CPU will fail the "kvm_target isn't set" check, because we no longer support the old AArch32-host KVM setup and so CPUs like the Cortex-A7 no longer set cpu->kvm_target. Only the 'host', 'max', and the odd special cases 'cortex-a53' and 'cortex-a57' set kvm_target. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250429132200.605611-7-peter.maydell@linaro.org --- target/arm/kvm.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 9c62d12b23..85911e3024 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -1843,8 +1843,7 @@ int kvm_arch_init_vcpu(CPUState *cs) CPUARMState *env = &cpu->env; uint64_t psciver; - if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE || - !object_dynamic_cast(OBJECT(cpu), TYPE_AARCH64_CPU)) { + if (cpu->kvm_target == QEMU_KVM_ARM_TARGET_NONE) { error_report("KVM is not supported for this guest CPU type"); return -EINVAL; } From ec7e5a90fea996f04ea24e81b680a87bc975354a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 14 May 2025 14:29:46 +0100 Subject: [PATCH 0892/2760] target/arm: Remove TYPE_AARCH64_CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The TYPE_AARCH64_CPU class is an abstract type that is the parent of all the AArch64 CPUs. It now has no special behaviour of its own, so we can eliminate it and make the AArch64 CPUs directly inherit from TYPE_ARM_CPU. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250429132200.605611-8-peter.maydell@linaro.org --- target/arm/cpu-qom.h | 5 ----- target/arm/cpu.h | 4 ---- target/arm/cpu64.c | 49 +----------------------------------------- target/arm/internals.h | 1 - target/arm/tcg/cpu64.c | 2 +- 5 files changed, 2 insertions(+), 59 deletions(-) diff --git a/target/arm/cpu-qom.h b/target/arm/cpu-qom.h index b497667d61..2fcb0e1252 100644 --- a/target/arm/cpu-qom.h +++ b/target/arm/cpu-qom.h @@ -28,11 +28,6 @@ OBJECT_DECLARE_CPU_TYPE(ARMCPU, ARMCPUClass, ARM_CPU) #define TYPE_ARM_MAX_CPU "max-" TYPE_ARM_CPU -#define TYPE_AARCH64_CPU "aarch64-cpu" -typedef struct AArch64CPUClass AArch64CPUClass; -DECLARE_CLASS_CHECKERS(AArch64CPUClass, AARCH64_CPU, - TYPE_AARCH64_CPU) - #define ARM_CPU_TYPE_SUFFIX "-" TYPE_ARM_CPU #define ARM_CPU_TYPE_NAME(name) (name ARM_CPU_TYPE_SUFFIX) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 6ed6409cb7..302c24e232 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1138,10 +1138,6 @@ struct ARMCPUClass { ResettablePhases parent_phases; }; -struct AArch64CPUClass { - ARMCPUClass parent_class; -}; - /* Callback functions for the generic timer's timers. */ void arm_gt_ptimer_cb(void *opaque); void arm_gt_vtimer_cb(void *opaque); diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index e527465a3c..200da1c489 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -781,59 +781,12 @@ static const ARMCPUInfo aarch64_cpus[] = { #endif }; -static void aarch64_cpu_finalizefn(Object *obj) -{ -} - -static void aarch64_cpu_class_init(ObjectClass *oc, const void *data) -{ -} - -static void aarch64_cpu_instance_init(Object *obj) -{ - ARMCPUClass *acc = ARM_CPU_GET_CLASS(obj); - - acc->info->initfn(obj); - arm_cpu_post_init(obj); -} - -static void cpu_register_class_init(ObjectClass *oc, const void *data) -{ - ARMCPUClass *acc = ARM_CPU_CLASS(oc); - - acc->info = data; -} - -void aarch64_cpu_register(const ARMCPUInfo *info) -{ - TypeInfo type_info = { - .parent = TYPE_AARCH64_CPU, - .instance_init = aarch64_cpu_instance_init, - .class_init = info->class_init ?: cpu_register_class_init, - .class_data = info, - }; - - type_info.name = g_strdup_printf("%s-" TYPE_ARM_CPU, info->name); - type_register_static(&type_info); - g_free((void *)type_info.name); -} - -static const TypeInfo aarch64_cpu_type_info = { - .name = TYPE_AARCH64_CPU, - .parent = TYPE_ARM_CPU, - .instance_finalize = aarch64_cpu_finalizefn, - .abstract = true, - .class_init = aarch64_cpu_class_init, -}; - static void aarch64_cpu_register_types(void) { size_t i; - type_register_static(&aarch64_cpu_type_info); - for (i = 0; i < ARRAY_SIZE(aarch64_cpus); ++i) { - aarch64_cpu_register(&aarch64_cpus[i]); + arm_cpu_register(&aarch64_cpus[i]); } } diff --git a/target/arm/internals.h b/target/arm/internals.h index a396c0be3b..702eb1a548 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -354,7 +354,6 @@ static inline int r14_bank_number(int mode) } void arm_cpu_register(const ARMCPUInfo *info); -void aarch64_cpu_register(const ARMCPUInfo *info); void register_cp_regs_for_features(ARMCPU *cpu); void init_cpreg_list(ARMCPU *cpu); diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 29ab0ac79d..5d8ed2794d 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1316,7 +1316,7 @@ static void aarch64_cpu_register_types(void) size_t i; for (i = 0; i < ARRAY_SIZE(aarch64_cpus); ++i) { - aarch64_cpu_register(&aarch64_cpus[i]); + arm_cpu_register(&aarch64_cpus[i]); } } From 6414b7709d404bf410da360bab865133832ade85 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 14 May 2025 14:29:47 +0100 Subject: [PATCH 0893/2760] rust: pl011: Cut down amount of text quoted from PL011 TRM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the comments in the Rust pl011 register.rs file include large amounts of text from the PL011 TRM. This is much more commentary than we typically quote from a device reference manual, and much of it is not relevant to QEMU. Compress and rephrase the comments so that we are not quoting such a large volume of TRM text. We add a URL for the TRM; readers who need more detail on the function of the register bits can find it there, presented in context with the overall description of the hardware. Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé --- rust/hw/char/pl011/src/registers.rs | 261 ++++++---------------------- 1 file changed, 51 insertions(+), 210 deletions(-) diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs index cd92fa2c30..690feb6378 100644 --- a/rust/hw/char/pl011/src/registers.rs +++ b/rust/hw/char/pl011/src/registers.rs @@ -5,13 +5,13 @@ //! Device registers exposed as typed structs which are backed by arbitrary //! integer bitmaps. [`Data`], [`Control`], [`LineControl`], etc. +// For more detail see the PL011 Technical Reference Manual DDI0183: +// https://developer.arm.com/documentation/ddi0183/latest/ + use bilge::prelude::*; use qemu_api::impl_vmstate_bitsized; /// Offset of each register from the base memory address of the device. -/// -/// # Source -/// ARM DDI 0183G, Table 3-1 p.3-3 #[doc(alias = "offset")] #[allow(non_camel_case_types)] #[repr(u64)] @@ -87,48 +87,11 @@ pub struct Errors { _reserved_unpredictable: u4, } -// TODO: FIFO Mode has different semantics /// Data Register, `UARTDR` /// -/// The `UARTDR` register is the data register. -/// -/// For words to be transmitted: -/// -/// - if the FIFOs are enabled, data written to this location is pushed onto the -/// transmit -/// FIFO -/// - if the FIFOs are not enabled, data is stored in the transmitter holding -/// register (the -/// bottom word of the transmit FIFO). -/// -/// The write operation initiates transmission from the UART. The data is -/// prefixed with a start bit, appended with the appropriate parity bit -/// (if parity is enabled), and a stop bit. The resultant word is then -/// transmitted. -/// -/// For received words: -/// -/// - if the FIFOs are enabled, the data byte and the 4-bit status (break, -/// frame, parity, -/// and overrun) is pushed onto the 12-bit wide receive FIFO -/// - if the FIFOs are not enabled, the data byte and status are stored in the -/// receiving -/// holding register (the bottom word of the receive FIFO). -/// -/// The received data byte is read by performing reads from the `UARTDR` -/// register along with the corresponding status information. The status -/// information can also be read by a read of the `UARTRSR/UARTECR` -/// register. -/// -/// # Note -/// -/// You must disable the UART before any of the control registers are -/// reprogrammed. When the UART is disabled in the middle of -/// transmission or reception, it completes the current character before -/// stopping. -/// -/// # Source -/// ARM DDI 0183G 3.3.1 Data Register, UARTDR +/// The `UARTDR` register is the data register; write for TX and +/// read for RX. It is a 12-bit register, where bits 7..0 are the +/// character and bits 11..8 are error bits. #[bitsize(32)] #[derive(Clone, Copy, Default, DebugBits, FromBits)] #[doc(alias = "UARTDR")] @@ -144,30 +107,17 @@ impl Data { pub const BREAK: Self = Self { value: 1 << 10 }; } -// TODO: FIFO Mode has different semantics /// Receive Status Register / Error Clear Register, `UARTRSR/UARTECR` /// -/// The UARTRSR/UARTECR register is the receive status register/error clear -/// register. Receive status can also be read from the `UARTRSR` -/// register. If the status is read from this register, then the status -/// information for break, framing and parity corresponds to the -/// data character read from the [Data register](Data), `UARTDR` prior to -/// reading the UARTRSR register. The status information for overrun is -/// set immediately when an overrun condition occurs. -/// -/// -/// # Note -/// The received data character must be read first from the [Data -/// Register](Data), `UARTDR` before reading the error status associated -/// with that data character from the `UARTRSR` register. This read -/// sequence cannot be reversed, because the `UARTRSR` register is -/// updated only when a read occurs from the `UARTDR` register. However, -/// the status information can also be obtained by reading the `UARTDR` -/// register +/// This register provides a different way to read the four receive +/// status error bits that can be found in bits 11..8 of the UARTDR +/// on a read. It gets updated when the guest reads UARTDR, and the +/// status bits correspond to that character that was just read. /// -/// # Source -/// ARM DDI 0183G 3.3.2 Receive Status Register/Error Clear Register, -/// UARTRSR/UARTECR +/// The TRM confusingly describes this offset as UARTRSR for reads +/// and UARTECR for writes, but really it's a single error status +/// register where writing anything to the register clears the error +/// bits. #[bitsize(32)] #[derive(Clone, Copy, DebugBits, FromBits)] pub struct ReceiveStatusErrorClear { @@ -196,54 +146,29 @@ impl Default for ReceiveStatusErrorClear { #[bitsize(32)] #[derive(Clone, Copy, DebugBits, FromBits)] /// Flag Register, `UARTFR` +/// +/// This has the usual inbound RS232 modem-control signals, plus flags +/// for RX and TX FIFO fill levels and a BUSY flag. #[doc(alias = "UARTFR")] pub struct Flags { - /// CTS Clear to send. This bit is the complement of the UART clear to - /// send, `nUARTCTS`, modem status input. That is, the bit is 1 - /// when `nUARTCTS` is LOW. + /// CTS: Clear to send pub clear_to_send: bool, - /// DSR Data set ready. This bit is the complement of the UART data set - /// ready, `nUARTDSR`, modem status input. That is, the bit is 1 when - /// `nUARTDSR` is LOW. + /// DSR: Data set ready pub data_set_ready: bool, - /// DCD Data carrier detect. This bit is the complement of the UART data - /// carrier detect, `nUARTDCD`, modem status input. That is, the bit is - /// 1 when `nUARTDCD` is LOW. + /// DCD: Data carrier detect pub data_carrier_detect: bool, - /// BUSY UART busy. If this bit is set to 1, the UART is busy - /// transmitting data. This bit remains set until the complete - /// byte, including all the stop bits, has been sent from the - /// shift register. This bit is set as soon as the transmit FIFO - /// becomes non-empty, regardless of whether the UART is enabled - /// or not. + /// BUSY: UART busy. In real hardware, set while the UART is + /// busy transmitting data. QEMU's implementation never sets BUSY. pub busy: bool, - /// RXFE Receive FIFO empty. The meaning of this bit depends on the - /// state of the FEN bit in the UARTLCR_H register. If the FIFO - /// is disabled, this bit is set when the receive holding - /// register is empty. If the FIFO is enabled, the RXFE bit is - /// set when the receive FIFO is empty. + /// RXFE: Receive FIFO empty pub receive_fifo_empty: bool, - /// TXFF Transmit FIFO full. The meaning of this bit depends on the - /// state of the FEN bit in the UARTLCR_H register. If the FIFO - /// is disabled, this bit is set when the transmit holding - /// register is full. If the FIFO is enabled, the TXFF bit is - /// set when the transmit FIFO is full. + /// TXFF: Transmit FIFO full pub transmit_fifo_full: bool, - /// RXFF Receive FIFO full. The meaning of this bit depends on the state - /// of the FEN bit in the UARTLCR_H register. If the FIFO is - /// disabled, this bit is set when the receive holding register - /// is full. If the FIFO is enabled, the RXFF bit is set when - /// the receive FIFO is full. + /// RXFF: Receive FIFO full pub receive_fifo_full: bool, - /// Transmit FIFO empty. The meaning of this bit depends on the state of - /// the FEN bit in the [Line Control register](LineControl), - /// `UARTLCR_H`. If the FIFO is disabled, this bit is set when the - /// transmit holding register is empty. If the FIFO is enabled, - /// the TXFE bit is set when the transmit FIFO is empty. This - /// bit does not indicate if there is data in the transmit shift - /// register. + /// TXFE: Transmit FIFO empty pub transmit_fifo_empty: bool, - /// `RI`, is `true` when `nUARTRI` is `LOW`. + /// RI: Ring indicator pub ring_indicator: bool, _reserved_zero_no_modify: u23, } @@ -270,54 +195,23 @@ impl Default for Flags { /// Line Control Register, `UARTLCR_H` #[doc(alias = "UARTLCR_H")] pub struct LineControl { - /// BRK Send break. - /// - /// If this bit is set to `1`, a low-level is continually output on the - /// `UARTTXD` output, after completing transmission of the - /// current character. For the proper execution of the break command, - /// the software must set this bit for at least two complete - /// frames. For normal use, this bit must be cleared to `0`. + /// BRK: Send break pub send_break: bool, - /// 1 PEN Parity enable: - /// - /// - 0 = parity is disabled and no parity bit added to the data frame - /// - 1 = parity checking and generation is enabled. - /// - /// See Table 3-11 on page 3-14 for the parity truth table. + /// PEN: Parity enable pub parity_enabled: bool, - /// EPS Even parity select. Controls the type of parity the UART uses - /// during transmission and reception: - /// - 0 = odd parity. The UART generates or checks for an odd number of 1s - /// in the data and parity bits. - /// - 1 = even parity. The UART generates or checks for an even number of 1s - /// in the data and parity bits. - /// This bit has no effect when the `PEN` bit disables parity checking - /// and generation. See Table 3-11 on page 3-14 for the parity - /// truth table. + /// EPS: Even parity select pub parity: Parity, - /// 3 STP2 Two stop bits select. If this bit is set to 1, two stop bits - /// are transmitted at the end of the frame. The receive - /// logic does not check for two stop bits being received. + /// STP2: Two stop bits select pub two_stops_bits: bool, - /// FEN Enable FIFOs: - /// 0 = FIFOs are disabled (character mode) that is, the FIFOs become - /// 1-byte-deep holding registers 1 = transmit and receive FIFO - /// buffers are enabled (FIFO mode). + /// FEN: Enable FIFOs pub fifos_enabled: Mode, - /// WLEN Word length. These bits indicate the number of data bits - /// transmitted or received in a frame as follows: b11 = 8 bits + /// WLEN: Word length in bits + /// b11 = 8 bits /// b10 = 7 bits /// b01 = 6 bits /// b00 = 5 bits. pub word_length: WordLength, - /// 7 SPS Stick parity select. - /// 0 = stick parity is disabled - /// 1 = either: - /// • if the EPS bit is 0 then the parity bit is transmitted and checked - /// as a 1 • if the EPS bit is 1 then the parity bit is - /// transmitted and checked as a 0. This bit has no effect when - /// the PEN bit disables parity checking and generation. See Table 3-11 - /// on page 3-14 for the parity truth table. + /// SPS Stick parity select pub sticky_parity: bool, /// 31:8 - Reserved, do not modify, read as zero. _reserved_zero_no_modify: u24, @@ -342,11 +236,7 @@ impl Default for LineControl { /// `EPS` "Even parity select", field of [Line Control /// register](LineControl). pub enum Parity { - /// - 0 = odd parity. The UART generates or checks for an odd number of 1s - /// in the data and parity bits. Odd = 0, - /// - 1 = even parity. The UART generates or checks for an even number of 1s - /// in the data and parity bits. Even = 1, } @@ -381,88 +271,39 @@ pub enum WordLength { /// Control Register, `UARTCR` /// -/// The `UARTCR` register is the control register. All the bits are cleared -/// to `0` on reset except for bits `9` and `8` that are set to `1`. -/// -/// # Source -/// ARM DDI 0183G, 3.3.8 Control Register, `UARTCR`, Table 3-12 +/// The `UARTCR` register is the control register. It contains various +/// enable bits, and the bits to write to set the usual outbound RS232 +/// modem control signals. All bits reset to 0 except TXE and RXE. #[bitsize(32)] #[doc(alias = "UARTCR")] #[derive(Clone, Copy, DebugBits, FromBits)] pub struct Control { - /// `UARTEN` UART enable: 0 = UART is disabled. If the UART is disabled - /// in the middle of transmission or reception, it completes the current - /// character before stopping. 1 = the UART is enabled. Data - /// transmission and reception occurs for either UART signals or SIR - /// signals depending on the setting of the SIREN bit. + /// `UARTEN` UART enable: 0 = UART is disabled. pub enable_uart: bool, - /// `SIREN` `SIR` enable: 0 = IrDA SIR ENDEC is disabled. `nSIROUT` - /// remains LOW (no light pulse generated), and signal transitions on - /// SIRIN have no effect. 1 = IrDA SIR ENDEC is enabled. Data is - /// transmitted and received on nSIROUT and SIRIN. UARTTXD remains HIGH, - /// in the marking state. Signal transitions on UARTRXD or modem status - /// inputs have no effect. This bit has no effect if the UARTEN bit - /// disables the UART. + /// `SIREN` `SIR` enable: disable or enable IrDA SIR ENDEC. + /// QEMU does not model this. pub enable_sir: bool, - /// `SIRLP` SIR low-power IrDA mode. This bit selects the IrDA encoding - /// mode. If this bit is cleared to 0, low-level bits are transmitted as - /// an active high pulse with a width of 3/ 16th of the bit period. If - /// this bit is set to 1, low-level bits are transmitted with a pulse - /// width that is 3 times the period of the IrLPBaud16 input signal, - /// regardless of the selected bit rate. Setting this bit uses less - /// power, but might reduce transmission distances. + /// `SIRLP` SIR low-power IrDA mode. QEMU does not model this. pub sir_lowpower_irda_mode: u1, /// Reserved, do not modify, read as zero. _reserved_zero_no_modify: u4, - /// `LBE` Loopback enable. If this bit is set to 1 and the SIREN bit is - /// set to 1 and the SIRTEST bit in the Test Control register, UARTTCR - /// on page 4-5 is set to 1, then the nSIROUT path is inverted, and fed - /// through to the SIRIN path. The SIRTEST bit in the test register must - /// be set to 1 to override the normal half-duplex SIR operation. This - /// must be the requirement for accessing the test registers during - /// normal operation, and SIRTEST must be cleared to 0 when loopback - /// testing is finished. This feature reduces the amount of external - /// coupling required during system test. If this bit is set to 1, and - /// the SIRTEST bit is set to 0, the UARTTXD path is fed through to the - /// UARTRXD path. In either SIR mode or UART mode, when this bit is set, - /// the modem outputs are also fed through to the modem inputs. This bit - /// is cleared to 0 on reset, to disable loopback. + /// `LBE` Loopback enable: feed UART output back to the input pub enable_loopback: bool, - /// `TXE` Transmit enable. If this bit is set to 1, the transmit section - /// of the UART is enabled. Data transmission occurs for either UART - /// signals, or SIR signals depending on the setting of the SIREN bit. - /// When the UART is disabled in the middle of transmission, it - /// completes the current character before stopping. + /// `TXE` Transmit enable pub enable_transmit: bool, - /// `RXE` Receive enable. If this bit is set to 1, the receive section - /// of the UART is enabled. Data reception occurs for either UART - /// signals or SIR signals depending on the setting of the SIREN bit. - /// When the UART is disabled in the middle of reception, it completes - /// the current character before stopping. + /// `RXE` Receive enable pub enable_receive: bool, - /// `DTR` Data transmit ready. This bit is the complement of the UART - /// data transmit ready, `nUARTDTR`, modem status output. That is, when - /// the bit is programmed to a 1 then `nUARTDTR` is LOW. + /// `DTR` Data transmit ready pub data_transmit_ready: bool, - /// `RTS` Request to send. This bit is the complement of the UART - /// request to send, `nUARTRTS`, modem status output. That is, when the - /// bit is programmed to a 1 then `nUARTRTS` is LOW. + /// `RTS` Request to send pub request_to_send: bool, - /// `Out1` This bit is the complement of the UART Out1 (`nUARTOut1`) - /// modem status output. That is, when the bit is programmed to a 1 the - /// output is 0. For DTE this can be used as Data Carrier Detect (DCD). + /// `Out1` UART Out1 signal; can be used as DCD pub out_1: bool, - /// `Out2` This bit is the complement of the UART Out2 (`nUARTOut2`) - /// modem status output. That is, when the bit is programmed to a 1, the - /// output is 0. For DTE this can be used as Ring Indicator (RI). + /// `Out2` UART Out2 signal; can be used as RI pub out_2: bool, - /// `RTSEn` RTS hardware flow control enable. If this bit is set to 1, - /// RTS hardware flow control is enabled. Data is only requested when - /// there is space in the receive FIFO for it to be received. + /// `RTSEn` RTS hardware flow control enable pub rts_hardware_flow_control_enable: bool, - /// `CTSEn` CTS hardware flow control enable. If this bit is set to 1, - /// CTS hardware flow control is enabled. Data is only transmitted when - /// the `nUARTCTS` signal is asserted. + /// `CTSEn` CTS hardware flow control enable pub cts_hardware_flow_control_enable: bool, /// 31:16 - Reserved, do not modify, read as zero. _reserved_zero_no_modify2: u16, From edf838289b7fc698013f18d7a8a83b6b50ec41bb Mon Sep 17 00:00:00 2001 From: Santiago Monserrat Campanello Date: Wed, 14 May 2025 14:29:47 +0100 Subject: [PATCH 0894/2760] hw/arm: Replace TABs for spaces in OMAP board and device code In hw/arm and include/hw/arm, some source files for the OMAP SoC and the sx1 boards that are our only remaining OMAP boards still have hard-coded tabs (almost entirely used for the indent on inline comments, not for actual code indent). Replace the tabs with spaces using vim :retab. I used 4 spaces except in some defines and comments where I tried to put everything aligned in the same column for better readability. This commit is a purely whitespace-only change. Signed-off-by: Santiago Monserrat Campanello Message-id: 20250505131130.82206-1-santimonserr@gmail.com Resolves: https://gitlab.com/qemu-project/qemu/-/issues/373 [PMM: expanded commit message] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/omap1.c | 1016 +++++++++++++++++++------------------- hw/arm/omap_sx1.c | 2 +- hw/dma/omap_dma.c | 334 ++++++------- hw/gpio/omap_gpio.c | 28 +- hw/i2c/omap_i2c.c | 178 +++---- hw/intc/omap_intc.c | 154 +++--- hw/misc/omap_clk.c | 470 +++++++++--------- hw/timer/pxa2xx_timer.c | 78 +-- include/hw/arm/omap.h | 536 ++++++++++---------- include/hw/arm/sharpsl.h | 2 +- include/hw/arm/soc_dma.h | 4 +- 11 files changed, 1401 insertions(+), 1401 deletions(-) diff --git a/hw/arm/omap1.c b/hw/arm/omap1.c index 91d7e3f04b..74458fb7c6 100644 --- a/hw/arm/omap1.c +++ b/hw/arm/omap1.c @@ -144,7 +144,7 @@ static inline void omap_timer_update(struct omap_mpu_timer_s *timer) int64_t expires; if (timer->enable && timer->st && timer->rate) { - timer->val = timer->reset_val; /* Should skip this on clk enable */ + timer->val = timer->reset_val; /* Should skip this on clk enable */ expires = muldiv64((uint64_t) timer->val << (timer->ptv + 1), NANOSECONDS_PER_SECOND, timer->rate); @@ -212,13 +212,13 @@ static uint64_t omap_mpu_timer_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* CNTL_TIMER */ + case 0x00: /* CNTL_TIMER */ return (s->enable << 5) | (s->ptv << 2) | (s->ar << 1) | s->st; - case 0x04: /* LOAD_TIM */ + case 0x04: /* LOAD_TIM */ break; - case 0x08: /* READ_TIM */ + case 0x08: /* READ_TIM */ return omap_timer_read(s); } @@ -237,7 +237,7 @@ static void omap_mpu_timer_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* CNTL_TIMER */ + case 0x00: /* CNTL_TIMER */ omap_timer_sync(s); s->enable = (value >> 5) & 1; s->ptv = (value >> 2) & 7; @@ -246,11 +246,11 @@ static void omap_mpu_timer_write(void *opaque, hwaddr addr, omap_timer_update(s); return; - case 0x04: /* LOAD_TIM */ + case 0x04: /* LOAD_TIM */ s->reset_val = value; return; - case 0x08: /* READ_TIM */ + case 0x08: /* READ_TIM */ OMAP_RO_REG(addr); break; @@ -318,14 +318,14 @@ static uint64_t omap_wd_timer_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* CNTL_TIMER */ + case 0x00: /* CNTL_TIMER */ return (s->timer.ptv << 9) | (s->timer.ar << 8) | (s->timer.st << 7) | (s->free << 1); - case 0x04: /* READ_TIMER */ + case 0x04: /* READ_TIMER */ return omap_timer_read(&s->timer); - case 0x08: /* TIMER_MODE */ + case 0x08: /* TIMER_MODE */ return s->mode << 15; } @@ -344,7 +344,7 @@ static void omap_wd_timer_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* CNTL_TIMER */ + case 0x00: /* CNTL_TIMER */ omap_timer_sync(&s->timer); s->timer.ptv = (value >> 9) & 7; s->timer.ar = (value >> 8) & 1; @@ -353,11 +353,11 @@ static void omap_wd_timer_write(void *opaque, hwaddr addr, omap_timer_update(&s->timer); break; - case 0x04: /* LOAD_TIMER */ + case 0x04: /* LOAD_TIMER */ s->timer.reset_val = value & 0xffff; break; - case 0x08: /* TIMER_MODE */ + case 0x08: /* TIMER_MODE */ if (!s->mode && ((value >> 15) & 1)) omap_clk_get(s->timer.clk); s->mode |= (value >> 15) & 1; @@ -442,13 +442,13 @@ static uint64_t omap_os_timer_read(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* TVR */ + case 0x00: /* TVR */ return s->timer.reset_val; - case 0x04: /* TCR */ + case 0x04: /* TCR */ return omap_timer_read(&s->timer); - case 0x08: /* CR */ + case 0x08: /* CR */ return (s->timer.ar << 3) | (s->timer.it_ena << 2) | s->timer.st; default: @@ -470,15 +470,15 @@ static void omap_os_timer_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* TVR */ + case 0x00: /* TVR */ s->timer.reset_val = value & 0x00ffffff; break; - case 0x04: /* TCR */ + case 0x04: /* TCR */ OMAP_RO_REG(addr); break; - case 0x08: /* CR */ + case 0x08: /* CR */ s->timer.ar = (value >> 3) & 1; s->timer.it_ena = (value >> 2) & 1; if (s->timer.st != (value & 1) || (value & 2)) { @@ -543,34 +543,34 @@ static uint64_t omap_ulpd_pm_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x14: /* IT_STATUS */ + case 0x14: /* IT_STATUS */ ret = s->ulpd_pm_regs[addr >> 2]; s->ulpd_pm_regs[addr >> 2] = 0; qemu_irq_lower(qdev_get_gpio_in(s->ih[1], OMAP_INT_GAUGE_32K)); return ret; - case 0x18: /* Reserved */ - case 0x1c: /* Reserved */ - case 0x20: /* Reserved */ - case 0x28: /* Reserved */ - case 0x2c: /* Reserved */ + case 0x18: /* Reserved */ + case 0x1c: /* Reserved */ + case 0x20: /* Reserved */ + case 0x28: /* Reserved */ + case 0x2c: /* Reserved */ OMAP_BAD_REG(addr); /* fall through */ - case 0x00: /* COUNTER_32_LSB */ - case 0x04: /* COUNTER_32_MSB */ - case 0x08: /* COUNTER_HIGH_FREQ_LSB */ - case 0x0c: /* COUNTER_HIGH_FREQ_MSB */ - case 0x10: /* GAUGING_CTRL */ - case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */ - case 0x30: /* CLOCK_CTRL */ - case 0x34: /* SOFT_REQ */ - case 0x38: /* COUNTER_32_FIQ */ - case 0x3c: /* DPLL_CTRL */ - case 0x40: /* STATUS_REQ */ + case 0x00: /* COUNTER_32_LSB */ + case 0x04: /* COUNTER_32_MSB */ + case 0x08: /* COUNTER_HIGH_FREQ_LSB */ + case 0x0c: /* COUNTER_HIGH_FREQ_MSB */ + case 0x10: /* GAUGING_CTRL */ + case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */ + case 0x30: /* CLOCK_CTRL */ + case 0x34: /* SOFT_REQ */ + case 0x38: /* COUNTER_32_FIQ */ + case 0x3c: /* DPLL_CTRL */ + case 0x40: /* STATUS_REQ */ /* XXX: check clk::usecount state for every clock */ - case 0x48: /* LOCL_TIME */ - case 0x4c: /* APLL_CTRL */ - case 0x50: /* POWER_CTRL */ + case 0x48: /* LOCL_TIME */ + case 0x4c: /* APLL_CTRL */ + case 0x50: /* POWER_CTRL */ return s->ulpd_pm_regs[addr >> 2]; } @@ -581,22 +581,22 @@ static uint64_t omap_ulpd_pm_read(void *opaque, hwaddr addr, static inline void omap_ulpd_clk_update(struct omap_mpu_state_s *s, uint16_t diff, uint16_t value) { - if (diff & (1 << 4)) /* USB_MCLK_EN */ + if (diff & (1 << 4)) /* USB_MCLK_EN */ omap_clk_onoff(omap_findclk(s, "usb_clk0"), (value >> 4) & 1); - if (diff & (1 << 5)) /* DIS_USB_PVCI_CLK */ + if (diff & (1 << 5)) /* DIS_USB_PVCI_CLK */ omap_clk_onoff(omap_findclk(s, "usb_w2fc_ck"), (~value >> 5) & 1); } static inline void omap_ulpd_req_update(struct omap_mpu_state_s *s, uint16_t diff, uint16_t value) { - if (diff & (1 << 0)) /* SOFT_DPLL_REQ */ + if (diff & (1 << 0)) /* SOFT_DPLL_REQ */ omap_clk_canidle(omap_findclk(s, "dpll4"), (~value >> 0) & 1); - if (diff & (1 << 1)) /* SOFT_COM_REQ */ + if (diff & (1 << 1)) /* SOFT_COM_REQ */ omap_clk_canidle(omap_findclk(s, "com_mclk_out"), (~value >> 1) & 1); - if (diff & (1 << 2)) /* SOFT_SDW_REQ */ + if (diff & (1 << 2)) /* SOFT_SDW_REQ */ omap_clk_canidle(omap_findclk(s, "bt_mclk_out"), (~value >> 2) & 1); - if (diff & (1 << 3)) /* SOFT_USB_REQ */ + if (diff & (1 << 3)) /* SOFT_USB_REQ */ omap_clk_canidle(omap_findclk(s, "usb_clk0"), (~value >> 3) & 1); } @@ -615,16 +615,16 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* COUNTER_32_LSB */ - case 0x04: /* COUNTER_32_MSB */ - case 0x08: /* COUNTER_HIGH_FREQ_LSB */ - case 0x0c: /* COUNTER_HIGH_FREQ_MSB */ - case 0x14: /* IT_STATUS */ - case 0x40: /* STATUS_REQ */ + case 0x00: /* COUNTER_32_LSB */ + case 0x04: /* COUNTER_32_MSB */ + case 0x08: /* COUNTER_HIGH_FREQ_LSB */ + case 0x0c: /* COUNTER_HIGH_FREQ_MSB */ + case 0x14: /* IT_STATUS */ + case 0x40: /* STATUS_REQ */ OMAP_RO_REG(addr); break; - case 0x10: /* GAUGING_CTRL */ + case 0x10: /* GAUGING_CTRL */ /* Bits 0 and 1 seem to be confused in the OMAP 310 TRM */ if ((s->ulpd_pm_regs[addr >> 2] ^ value) & 1) { now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); @@ -638,50 +638,50 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr, ticks = muldiv64(now, 32768, NANOSECONDS_PER_SECOND); s->ulpd_pm_regs[0x00 >> 2] = (ticks >> 0) & 0xffff; s->ulpd_pm_regs[0x04 >> 2] = (ticks >> 16) & 0xffff; - if (ticks >> 32) /* OVERFLOW_32K */ + if (ticks >> 32) /* OVERFLOW_32K */ s->ulpd_pm_regs[0x14 >> 2] |= 1 << 2; /* High frequency ticks */ ticks = muldiv64(now, 12000000, NANOSECONDS_PER_SECOND); s->ulpd_pm_regs[0x08 >> 2] = (ticks >> 0) & 0xffff; s->ulpd_pm_regs[0x0c >> 2] = (ticks >> 16) & 0xffff; - if (ticks >> 32) /* OVERFLOW_HI_FREQ */ + if (ticks >> 32) /* OVERFLOW_HI_FREQ */ s->ulpd_pm_regs[0x14 >> 2] |= 1 << 1; - s->ulpd_pm_regs[0x14 >> 2] |= 1 << 0; /* IT_GAUGING */ + s->ulpd_pm_regs[0x14 >> 2] |= 1 << 0; /* IT_GAUGING */ qemu_irq_raise(qdev_get_gpio_in(s->ih[1], OMAP_INT_GAUGE_32K)); } } s->ulpd_pm_regs[addr >> 2] = value; break; - case 0x18: /* Reserved */ - case 0x1c: /* Reserved */ - case 0x20: /* Reserved */ - case 0x28: /* Reserved */ - case 0x2c: /* Reserved */ + case 0x18: /* Reserved */ + case 0x1c: /* Reserved */ + case 0x20: /* Reserved */ + case 0x28: /* Reserved */ + case 0x2c: /* Reserved */ OMAP_BAD_REG(addr); /* fall through */ - case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */ - case 0x38: /* COUNTER_32_FIQ */ - case 0x48: /* LOCL_TIME */ - case 0x50: /* POWER_CTRL */ + case 0x24: /* SETUP_ANALOG_CELL3_ULPD1 */ + case 0x38: /* COUNTER_32_FIQ */ + case 0x48: /* LOCL_TIME */ + case 0x50: /* POWER_CTRL */ s->ulpd_pm_regs[addr >> 2] = value; break; - case 0x30: /* CLOCK_CTRL */ + case 0x30: /* CLOCK_CTRL */ diff = s->ulpd_pm_regs[addr >> 2] ^ value; s->ulpd_pm_regs[addr >> 2] = value & 0x3f; omap_ulpd_clk_update(s, diff, value); break; - case 0x34: /* SOFT_REQ */ + case 0x34: /* SOFT_REQ */ diff = s->ulpd_pm_regs[addr >> 2] ^ value; s->ulpd_pm_regs[addr >> 2] = value & 0x1f; omap_ulpd_req_update(s, diff, value); break; - case 0x3c: /* DPLL_CTRL */ + case 0x3c: /* DPLL_CTRL */ /* XXX: OMAP310 TRM claims bit 3 is PLL_ENABLE, and bit 4 is * omitted altogether, probably a typo. */ /* This register has identical semantics with DPLL(1:3) control @@ -689,11 +689,11 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr, diff = s->ulpd_pm_regs[addr >> 2] & value; s->ulpd_pm_regs[addr >> 2] = value & 0x2fff; if (diff & (0x3ff << 2)) { - if (value & (1 << 4)) { /* PLL_ENABLE */ - div = ((value >> 5) & 3) + 1; /* PLL_DIV */ - mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */ + if (value & (1 << 4)) { /* PLL_ENABLE */ + div = ((value >> 5) & 3) + 1; /* PLL_DIV */ + mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */ } else { - div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */ + div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */ mult = 1; } omap_clk_setrate(omap_findclk(s, "dpll4"), div, mult); @@ -708,10 +708,10 @@ static void omap_ulpd_pm_write(void *opaque, hwaddr addr, s->ulpd_pm_regs[addr >> 2] |= 2; break; - case 0x4c: /* APLL_CTRL */ + case 0x4c: /* APLL_CTRL */ diff = s->ulpd_pm_regs[addr >> 2] & value; s->ulpd_pm_regs[addr >> 2] = value & 0xf; - if (diff & (1 << 0)) /* APLL_NDPLL_SWITCH */ + if (diff & (1 << 0)) /* APLL_NDPLL_SWITCH */ omap_clk_reparent(omap_findclk(s, "ck_48m"), omap_findclk(s, (value & (1 << 0)) ? "apll" : "dpll4")); break; @@ -775,43 +775,43 @@ static uint64_t omap_pin_cfg_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* FUNC_MUX_CTRL_0 */ - case 0x04: /* FUNC_MUX_CTRL_1 */ - case 0x08: /* FUNC_MUX_CTRL_2 */ + case 0x00: /* FUNC_MUX_CTRL_0 */ + case 0x04: /* FUNC_MUX_CTRL_1 */ + case 0x08: /* FUNC_MUX_CTRL_2 */ return s->func_mux_ctrl[addr >> 2]; - case 0x0c: /* COMP_MODE_CTRL_0 */ + case 0x0c: /* COMP_MODE_CTRL_0 */ return s->comp_mode_ctrl[0]; - case 0x10: /* FUNC_MUX_CTRL_3 */ - case 0x14: /* FUNC_MUX_CTRL_4 */ - case 0x18: /* FUNC_MUX_CTRL_5 */ - case 0x1c: /* FUNC_MUX_CTRL_6 */ - case 0x20: /* FUNC_MUX_CTRL_7 */ - case 0x24: /* FUNC_MUX_CTRL_8 */ - case 0x28: /* FUNC_MUX_CTRL_9 */ - case 0x2c: /* FUNC_MUX_CTRL_A */ - case 0x30: /* FUNC_MUX_CTRL_B */ - case 0x34: /* FUNC_MUX_CTRL_C */ - case 0x38: /* FUNC_MUX_CTRL_D */ + case 0x10: /* FUNC_MUX_CTRL_3 */ + case 0x14: /* FUNC_MUX_CTRL_4 */ + case 0x18: /* FUNC_MUX_CTRL_5 */ + case 0x1c: /* FUNC_MUX_CTRL_6 */ + case 0x20: /* FUNC_MUX_CTRL_7 */ + case 0x24: /* FUNC_MUX_CTRL_8 */ + case 0x28: /* FUNC_MUX_CTRL_9 */ + case 0x2c: /* FUNC_MUX_CTRL_A */ + case 0x30: /* FUNC_MUX_CTRL_B */ + case 0x34: /* FUNC_MUX_CTRL_C */ + case 0x38: /* FUNC_MUX_CTRL_D */ return s->func_mux_ctrl[(addr >> 2) - 1]; - case 0x40: /* PULL_DWN_CTRL_0 */ - case 0x44: /* PULL_DWN_CTRL_1 */ - case 0x48: /* PULL_DWN_CTRL_2 */ - case 0x4c: /* PULL_DWN_CTRL_3 */ + case 0x40: /* PULL_DWN_CTRL_0 */ + case 0x44: /* PULL_DWN_CTRL_1 */ + case 0x48: /* PULL_DWN_CTRL_2 */ + case 0x4c: /* PULL_DWN_CTRL_3 */ return s->pull_dwn_ctrl[(addr & 0xf) >> 2]; - case 0x50: /* GATE_INH_CTRL_0 */ + case 0x50: /* GATE_INH_CTRL_0 */ return s->gate_inh_ctrl[0]; - case 0x60: /* VOLTAGE_CTRL_0 */ + case 0x60: /* VOLTAGE_CTRL_0 */ return s->voltage_ctrl[0]; - case 0x70: /* TEST_DBG_CTRL_0 */ + case 0x70: /* TEST_DBG_CTRL_0 */ return s->test_dbg_ctrl[0]; - case 0x80: /* MOD_CONF_CTRL_0 */ + case 0x80: /* MOD_CONF_CTRL_0 */ return s->mod_conf_ctrl[0]; } @@ -823,10 +823,10 @@ static inline void omap_pin_funcmux0_update(struct omap_mpu_state_s *s, uint32_t diff, uint32_t value) { if (s->compat1509) { - if (diff & (1 << 9)) /* BLUETOOTH */ + if (diff & (1 << 9)) /* BLUETOOTH */ omap_clk_onoff(omap_findclk(s, "bt_mclk_out"), (~value >> 9) & 1); - if (diff & (1 << 7)) /* USB.CLKO */ + if (diff & (1 << 7)) /* USB.CLKO */ omap_clk_onoff(omap_findclk(s, "usb.clko"), (value >> 7) & 1); } @@ -856,23 +856,23 @@ static inline void omap_pin_modconf1_update(struct omap_mpu_state_s *s, omap_findclk(s, ((value >> 31) & 1) ? "ck_48m" : "armper_ck")); } - if (diff & (1 << 30)) /* CONF_MOD_UART2_CLK_MODE_R */ + if (diff & (1 << 30)) /* CONF_MOD_UART2_CLK_MODE_R */ omap_clk_reparent(omap_findclk(s, "uart2_ck"), omap_findclk(s, ((value >> 30) & 1) ? "ck_48m" : "armper_ck")); - if (diff & (1 << 29)) /* CONF_MOD_UART1_CLK_MODE_R */ + if (diff & (1 << 29)) /* CONF_MOD_UART1_CLK_MODE_R */ omap_clk_reparent(omap_findclk(s, "uart1_ck"), omap_findclk(s, ((value >> 29) & 1) ? "ck_48m" : "armper_ck")); - if (diff & (1 << 23)) /* CONF_MOD_MMC_SD_CLK_REQ_R */ + if (diff & (1 << 23)) /* CONF_MOD_MMC_SD_CLK_REQ_R */ omap_clk_reparent(omap_findclk(s, "mmc_ck"), omap_findclk(s, ((value >> 23) & 1) ? "ck_48m" : "armper_ck")); - if (diff & (1 << 12)) /* CONF_MOD_COM_MCLK_12_48_S */ + if (diff & (1 << 12)) /* CONF_MOD_COM_MCLK_12_48_S */ omap_clk_reparent(omap_findclk(s, "com_mclk_out"), omap_findclk(s, ((value >> 12) & 1) ? "ck_48m" : "armper_ck")); - if (diff & (1 << 9)) /* CONF_MOD_USB_HOST_HHC_UHO */ + if (diff & (1 << 9)) /* CONF_MOD_USB_HOST_HHC_UHO */ omap_clk_onoff(omap_findclk(s, "usb_hhc_ck"), (value >> 9) & 1); } @@ -888,63 +888,63 @@ static void omap_pin_cfg_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* FUNC_MUX_CTRL_0 */ + case 0x00: /* FUNC_MUX_CTRL_0 */ diff = s->func_mux_ctrl[addr >> 2] ^ value; s->func_mux_ctrl[addr >> 2] = value; omap_pin_funcmux0_update(s, diff, value); return; - case 0x04: /* FUNC_MUX_CTRL_1 */ + case 0x04: /* FUNC_MUX_CTRL_1 */ diff = s->func_mux_ctrl[addr >> 2] ^ value; s->func_mux_ctrl[addr >> 2] = value; omap_pin_funcmux1_update(s, diff, value); return; - case 0x08: /* FUNC_MUX_CTRL_2 */ + case 0x08: /* FUNC_MUX_CTRL_2 */ s->func_mux_ctrl[addr >> 2] = value; return; - case 0x0c: /* COMP_MODE_CTRL_0 */ + case 0x0c: /* COMP_MODE_CTRL_0 */ s->comp_mode_ctrl[0] = value; s->compat1509 = (value != 0x0000eaef); omap_pin_funcmux0_update(s, ~0, s->func_mux_ctrl[0]); omap_pin_funcmux1_update(s, ~0, s->func_mux_ctrl[1]); return; - case 0x10: /* FUNC_MUX_CTRL_3 */ - case 0x14: /* FUNC_MUX_CTRL_4 */ - case 0x18: /* FUNC_MUX_CTRL_5 */ - case 0x1c: /* FUNC_MUX_CTRL_6 */ - case 0x20: /* FUNC_MUX_CTRL_7 */ - case 0x24: /* FUNC_MUX_CTRL_8 */ - case 0x28: /* FUNC_MUX_CTRL_9 */ - case 0x2c: /* FUNC_MUX_CTRL_A */ - case 0x30: /* FUNC_MUX_CTRL_B */ - case 0x34: /* FUNC_MUX_CTRL_C */ - case 0x38: /* FUNC_MUX_CTRL_D */ + case 0x10: /* FUNC_MUX_CTRL_3 */ + case 0x14: /* FUNC_MUX_CTRL_4 */ + case 0x18: /* FUNC_MUX_CTRL_5 */ + case 0x1c: /* FUNC_MUX_CTRL_6 */ + case 0x20: /* FUNC_MUX_CTRL_7 */ + case 0x24: /* FUNC_MUX_CTRL_8 */ + case 0x28: /* FUNC_MUX_CTRL_9 */ + case 0x2c: /* FUNC_MUX_CTRL_A */ + case 0x30: /* FUNC_MUX_CTRL_B */ + case 0x34: /* FUNC_MUX_CTRL_C */ + case 0x38: /* FUNC_MUX_CTRL_D */ s->func_mux_ctrl[(addr >> 2) - 1] = value; return; - case 0x40: /* PULL_DWN_CTRL_0 */ - case 0x44: /* PULL_DWN_CTRL_1 */ - case 0x48: /* PULL_DWN_CTRL_2 */ - case 0x4c: /* PULL_DWN_CTRL_3 */ + case 0x40: /* PULL_DWN_CTRL_0 */ + case 0x44: /* PULL_DWN_CTRL_1 */ + case 0x48: /* PULL_DWN_CTRL_2 */ + case 0x4c: /* PULL_DWN_CTRL_3 */ s->pull_dwn_ctrl[(addr & 0xf) >> 2] = value; return; - case 0x50: /* GATE_INH_CTRL_0 */ + case 0x50: /* GATE_INH_CTRL_0 */ s->gate_inh_ctrl[0] = value; return; - case 0x60: /* VOLTAGE_CTRL_0 */ + case 0x60: /* VOLTAGE_CTRL_0 */ s->voltage_ctrl[0] = value; return; - case 0x70: /* TEST_DBG_CTRL_0 */ + case 0x70: /* TEST_DBG_CTRL_0 */ s->test_dbg_ctrl[0] = value; return; - case 0x80: /* MOD_CONF_CTRL_0 */ + case 0x80: /* MOD_CONF_CTRL_0 */ diff = s->mod_conf_ctrl[0] ^ value; s->mod_conf_ctrl[0] = value; omap_pin_modconf1_update(s, diff, value); @@ -998,17 +998,17 @@ static uint64_t omap_id_read(void *opaque, hwaddr addr, } switch (addr) { - case 0xfffe1800: /* DIE_ID_LSB */ + case 0xfffe1800: /* DIE_ID_LSB */ return 0xc9581f0e; - case 0xfffe1804: /* DIE_ID_MSB */ + case 0xfffe1804: /* DIE_ID_MSB */ return 0xa8858bfa; - case 0xfffe2000: /* PRODUCT_ID_LSB */ + case 0xfffe2000: /* PRODUCT_ID_LSB */ return 0x00aaaafc; - case 0xfffe2004: /* PRODUCT_ID_MSB */ + case 0xfffe2004: /* PRODUCT_ID_MSB */ return 0xcafeb574; - case 0xfffed400: /* JTAG_ID_LSB */ + case 0xfffed400: /* JTAG_ID_LSB */ switch (s->mpu_model) { case omap310: return 0x03310315; @@ -1019,7 +1019,7 @@ static uint64_t omap_id_read(void *opaque, hwaddr addr, } break; - case 0xfffed404: /* JTAG_ID_MSB */ + case 0xfffed404: /* JTAG_ID_MSB */ switch (s->mpu_model) { case omap310: return 0xfb57402f; @@ -1080,22 +1080,22 @@ static uint64_t omap_mpui_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* CTRL */ + case 0x00: /* CTRL */ return s->mpui_ctrl; - case 0x04: /* DEBUG_ADDR */ + case 0x04: /* DEBUG_ADDR */ return 0x01ffffff; - case 0x08: /* DEBUG_DATA */ + case 0x08: /* DEBUG_DATA */ return 0xffffffff; - case 0x0c: /* DEBUG_FLAG */ + case 0x0c: /* DEBUG_FLAG */ return 0x00000800; - case 0x10: /* STATUS */ + case 0x10: /* STATUS */ return 0x00000000; /* Not in OMAP310 */ - case 0x14: /* DSP_STATUS */ - case 0x18: /* DSP_BOOT_CONFIG */ + case 0x14: /* DSP_STATUS */ + case 0x18: /* DSP_BOOT_CONFIG */ return 0x00000000; - case 0x1c: /* DSP_MPUI_CONFIG */ + case 0x1c: /* DSP_MPUI_CONFIG */ return 0x0000ffff; } @@ -1114,20 +1114,20 @@ static void omap_mpui_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* CTRL */ + case 0x00: /* CTRL */ s->mpui_ctrl = value & 0x007fffff; break; - case 0x04: /* DEBUG_ADDR */ - case 0x08: /* DEBUG_DATA */ - case 0x0c: /* DEBUG_FLAG */ - case 0x10: /* STATUS */ + case 0x04: /* DEBUG_ADDR */ + case 0x08: /* DEBUG_DATA */ + case 0x0c: /* DEBUG_FLAG */ + case 0x10: /* STATUS */ /* Not in OMAP310 */ - case 0x14: /* DSP_STATUS */ + case 0x14: /* DSP_STATUS */ OMAP_RO_REG(addr); break; - case 0x18: /* DSP_BOOT_CONFIG */ - case 0x1c: /* DSP_MPUI_CONFIG */ + case 0x18: /* DSP_BOOT_CONFIG */ + case 0x1c: /* DSP_MPUI_CONFIG */ break; default: @@ -1178,19 +1178,19 @@ static uint64_t omap_tipb_bridge_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* TIPB_CNTL */ + case 0x00: /* TIPB_CNTL */ return s->control; - case 0x04: /* TIPB_BUS_ALLOC */ + case 0x04: /* TIPB_BUS_ALLOC */ return s->alloc; - case 0x08: /* MPU_TIPB_CNTL */ + case 0x08: /* MPU_TIPB_CNTL */ return s->buffer; - case 0x0c: /* ENHANCED_TIPB_CNTL */ + case 0x0c: /* ENHANCED_TIPB_CNTL */ return s->enh_control; - case 0x10: /* ADDRESS_DBG */ - case 0x14: /* DATA_DEBUG_LOW */ - case 0x18: /* DATA_DEBUG_HIGH */ + case 0x10: /* ADDRESS_DBG */ + case 0x14: /* DATA_DEBUG_LOW */ + case 0x18: /* DATA_DEBUG_HIGH */ return 0xffff; - case 0x1c: /* DEBUG_CNTR_SIG */ + case 0x1c: /* DEBUG_CNTR_SIG */ return 0x00f8; } @@ -1209,27 +1209,27 @@ static void omap_tipb_bridge_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* TIPB_CNTL */ + case 0x00: /* TIPB_CNTL */ s->control = value & 0xffff; break; - case 0x04: /* TIPB_BUS_ALLOC */ + case 0x04: /* TIPB_BUS_ALLOC */ s->alloc = value & 0x003f; break; - case 0x08: /* MPU_TIPB_CNTL */ + case 0x08: /* MPU_TIPB_CNTL */ s->buffer = value & 0x0003; break; - case 0x0c: /* ENHANCED_TIPB_CNTL */ + case 0x0c: /* ENHANCED_TIPB_CNTL */ s->width_intr = !(value & 2); s->enh_control = value & 0x000f; break; - case 0x10: /* ADDRESS_DBG */ - case 0x14: /* DATA_DEBUG_LOW */ - case 0x18: /* DATA_DEBUG_HIGH */ - case 0x1c: /* DEBUG_CNTR_SIG */ + case 0x10: /* ADDRESS_DBG */ + case 0x14: /* DATA_DEBUG_LOW */ + case 0x18: /* DATA_DEBUG_HIGH */ + case 0x1c: /* DEBUG_CNTR_SIG */ OMAP_RO_REG(addr); break; @@ -1280,23 +1280,23 @@ static uint64_t omap_tcmi_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* IMIF_PRIO */ - case 0x04: /* EMIFS_PRIO */ - case 0x08: /* EMIFF_PRIO */ - case 0x0c: /* EMIFS_CONFIG */ - case 0x10: /* EMIFS_CS0_CONFIG */ - case 0x14: /* EMIFS_CS1_CONFIG */ - case 0x18: /* EMIFS_CS2_CONFIG */ - case 0x1c: /* EMIFS_CS3_CONFIG */ - case 0x24: /* EMIFF_MRS */ - case 0x28: /* TIMEOUT1 */ - case 0x2c: /* TIMEOUT2 */ - case 0x30: /* TIMEOUT3 */ - case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ - case 0x40: /* EMIFS_CFG_DYN_WAIT */ + case 0x00: /* IMIF_PRIO */ + case 0x04: /* EMIFS_PRIO */ + case 0x08: /* EMIFF_PRIO */ + case 0x0c: /* EMIFS_CONFIG */ + case 0x10: /* EMIFS_CS0_CONFIG */ + case 0x14: /* EMIFS_CS1_CONFIG */ + case 0x18: /* EMIFS_CS2_CONFIG */ + case 0x1c: /* EMIFS_CS3_CONFIG */ + case 0x24: /* EMIFF_MRS */ + case 0x28: /* TIMEOUT1 */ + case 0x2c: /* TIMEOUT2 */ + case 0x30: /* TIMEOUT3 */ + case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ + case 0x40: /* EMIFS_CFG_DYN_WAIT */ return s->tcmi_regs[addr >> 2]; - case 0x20: /* EMIFF_SDRAM_CONFIG */ + case 0x20: /* EMIFF_SDRAM_CONFIG */ ret = s->tcmi_regs[addr >> 2]; s->tcmi_regs[addr >> 2] &= ~1; /* XXX: Clear SLRF on SDRAM access */ /* XXX: We can try using the VGA_DIRTY flag for this */ @@ -1318,23 +1318,23 @@ static void omap_tcmi_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* IMIF_PRIO */ - case 0x04: /* EMIFS_PRIO */ - case 0x08: /* EMIFF_PRIO */ - case 0x10: /* EMIFS_CS0_CONFIG */ - case 0x14: /* EMIFS_CS1_CONFIG */ - case 0x18: /* EMIFS_CS2_CONFIG */ - case 0x1c: /* EMIFS_CS3_CONFIG */ - case 0x20: /* EMIFF_SDRAM_CONFIG */ - case 0x24: /* EMIFF_MRS */ - case 0x28: /* TIMEOUT1 */ - case 0x2c: /* TIMEOUT2 */ - case 0x30: /* TIMEOUT3 */ - case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ - case 0x40: /* EMIFS_CFG_DYN_WAIT */ + case 0x00: /* IMIF_PRIO */ + case 0x04: /* EMIFS_PRIO */ + case 0x08: /* EMIFF_PRIO */ + case 0x10: /* EMIFS_CS0_CONFIG */ + case 0x14: /* EMIFS_CS1_CONFIG */ + case 0x18: /* EMIFS_CS2_CONFIG */ + case 0x1c: /* EMIFS_CS3_CONFIG */ + case 0x20: /* EMIFF_SDRAM_CONFIG */ + case 0x24: /* EMIFF_MRS */ + case 0x28: /* TIMEOUT1 */ + case 0x2c: /* TIMEOUT2 */ + case 0x30: /* TIMEOUT3 */ + case 0x3c: /* EMIFF_SDRAM_CONFIG_2 */ + case 0x40: /* EMIFS_CFG_DYN_WAIT */ s->tcmi_regs[addr >> 2] = value; break; - case 0x0c: /* EMIFS_CONFIG */ + case 0x0c: /* EMIFS_CONFIG */ s->tcmi_regs[addr >> 2] = (value & 0xf) | (1 << 4); break; @@ -1393,7 +1393,7 @@ static uint64_t omap_dpll_read(void *opaque, hwaddr addr, return omap_badwidth_read16(opaque, addr); } - if (addr == 0x00) /* CTL_REG */ + if (addr == 0x00) /* CTL_REG */ return s->mode; OMAP_BAD_REG(addr); @@ -1413,16 +1413,16 @@ static void omap_dpll_write(void *opaque, hwaddr addr, return; } - if (addr == 0x00) { /* CTL_REG */ + if (addr == 0x00) { /* CTL_REG */ /* See omap_ulpd_pm_write() too */ diff = s->mode & value; s->mode = value & 0x2fff; if (diff & (0x3ff << 2)) { - if (value & (1 << 4)) { /* PLL_ENABLE */ - div = ((value >> 5) & 3) + 1; /* PLL_DIV */ - mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */ + if (value & (1 << 4)) { /* PLL_ENABLE */ + div = ((value >> 5) & 3) + 1; /* PLL_DIV */ + mult = MIN((value >> 7) & 0x1f, 1); /* PLL_MULT */ } else { - div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */ + div = bypass_div[((value >> 2) & 3)]; /* BYPASS_DIV */ mult = 1; } omap_clk_setrate(s->dpll, div, mult); @@ -1474,31 +1474,31 @@ static uint64_t omap_clkm_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* ARM_CKCTL */ + case 0x00: /* ARM_CKCTL */ return s->clkm.arm_ckctl; - case 0x04: /* ARM_IDLECT1 */ + case 0x04: /* ARM_IDLECT1 */ return s->clkm.arm_idlect1; - case 0x08: /* ARM_IDLECT2 */ + case 0x08: /* ARM_IDLECT2 */ return s->clkm.arm_idlect2; - case 0x0c: /* ARM_EWUPCT */ + case 0x0c: /* ARM_EWUPCT */ return s->clkm.arm_ewupct; - case 0x10: /* ARM_RSTCT1 */ + case 0x10: /* ARM_RSTCT1 */ return s->clkm.arm_rstct1; - case 0x14: /* ARM_RSTCT2 */ + case 0x14: /* ARM_RSTCT2 */ return s->clkm.arm_rstct2; - case 0x18: /* ARM_SYSST */ + case 0x18: /* ARM_SYSST */ return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start; - case 0x1c: /* ARM_CKOUT1 */ + case 0x1c: /* ARM_CKOUT1 */ return s->clkm.arm_ckout1; - case 0x20: /* ARM_CKOUT2 */ + case 0x20: /* ARM_CKOUT2 */ break; } @@ -1511,7 +1511,7 @@ static inline void omap_clkm_ckctl_update(struct omap_mpu_state_s *s, { omap_clk clk; - if (diff & (1 << 14)) { /* ARM_INTHCK_SEL */ + if (diff & (1 << 14)) { /* ARM_INTHCK_SEL */ if (value & (1 << 14)) /* Reserved */; else { @@ -1519,7 +1519,7 @@ static inline void omap_clkm_ckctl_update(struct omap_mpu_state_s *s, omap_clk_reparent(clk, omap_findclk(s, "tc_ck")); } } - if (diff & (1 << 12)) { /* ARM_TIMXO */ + if (diff & (1 << 12)) { /* ARM_TIMXO */ clk = omap_findclk(s, "armtim_ck"); if (value & (1 << 12)) omap_clk_reparent(clk, omap_findclk(s, "clkin")); @@ -1527,27 +1527,27 @@ static inline void omap_clkm_ckctl_update(struct omap_mpu_state_s *s, omap_clk_reparent(clk, omap_findclk(s, "ck_gen1")); } /* XXX: en_dspck */ - if (diff & (3 << 10)) { /* DSPMMUDIV */ + if (diff & (3 << 10)) { /* DSPMMUDIV */ clk = omap_findclk(s, "dspmmu_ck"); omap_clk_setrate(clk, 1 << ((value >> 10) & 3), 1); } - if (diff & (3 << 8)) { /* TCDIV */ + if (diff & (3 << 8)) { /* TCDIV */ clk = omap_findclk(s, "tc_ck"); omap_clk_setrate(clk, 1 << ((value >> 8) & 3), 1); } - if (diff & (3 << 6)) { /* DSPDIV */ + if (diff & (3 << 6)) { /* DSPDIV */ clk = omap_findclk(s, "dsp_ck"); omap_clk_setrate(clk, 1 << ((value >> 6) & 3), 1); } - if (diff & (3 << 4)) { /* ARMDIV */ + if (diff & (3 << 4)) { /* ARMDIV */ clk = omap_findclk(s, "arm_ck"); omap_clk_setrate(clk, 1 << ((value >> 4) & 3), 1); } - if (diff & (3 << 2)) { /* LCDDIV */ + if (diff & (3 << 2)) { /* LCDDIV */ clk = omap_findclk(s, "lcd_ck"); omap_clk_setrate(clk, 1 << ((value >> 2) & 3), 1); } - if (diff & (3 << 0)) { /* PERDIV */ + if (diff & (3 << 0)) { /* PERDIV */ clk = omap_findclk(s, "armper_ck"); omap_clk_setrate(clk, 1 << ((value >> 0) & 3), 1); } @@ -1566,25 +1566,25 @@ static inline void omap_clkm_idlect1_update(struct omap_mpu_state_s *s, qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } -#define SET_CANIDLE(clock, bit) \ - if (diff & (1 << bit)) { \ - clk = omap_findclk(s, clock); \ - omap_clk_canidle(clk, (value >> bit) & 1); \ +#define SET_CANIDLE(clock, bit) \ + if (diff & (1 << bit)) { \ + clk = omap_findclk(s, clock); \ + omap_clk_canidle(clk, (value >> bit) & 1); \ } - SET_CANIDLE("mpuwd_ck", 0) /* IDLWDT_ARM */ - SET_CANIDLE("armxor_ck", 1) /* IDLXORP_ARM */ - SET_CANIDLE("mpuper_ck", 2) /* IDLPER_ARM */ - SET_CANIDLE("lcd_ck", 3) /* IDLLCD_ARM */ - SET_CANIDLE("lb_ck", 4) /* IDLLB_ARM */ - SET_CANIDLE("hsab_ck", 5) /* IDLHSAB_ARM */ - SET_CANIDLE("tipb_ck", 6) /* IDLIF_ARM */ - SET_CANIDLE("dma_ck", 6) /* IDLIF_ARM */ - SET_CANIDLE("tc_ck", 6) /* IDLIF_ARM */ - SET_CANIDLE("dpll1", 7) /* IDLDPLL_ARM */ - SET_CANIDLE("dpll2", 7) /* IDLDPLL_ARM */ - SET_CANIDLE("dpll3", 7) /* IDLDPLL_ARM */ - SET_CANIDLE("mpui_ck", 8) /* IDLAPI_ARM */ - SET_CANIDLE("armtim_ck", 9) /* IDLTIM_ARM */ + SET_CANIDLE("mpuwd_ck", 0) /* IDLWDT_ARM */ + SET_CANIDLE("armxor_ck", 1) /* IDLXORP_ARM */ + SET_CANIDLE("mpuper_ck", 2) /* IDLPER_ARM */ + SET_CANIDLE("lcd_ck", 3) /* IDLLCD_ARM */ + SET_CANIDLE("lb_ck", 4) /* IDLLB_ARM */ + SET_CANIDLE("hsab_ck", 5) /* IDLHSAB_ARM */ + SET_CANIDLE("tipb_ck", 6) /* IDLIF_ARM */ + SET_CANIDLE("dma_ck", 6) /* IDLIF_ARM */ + SET_CANIDLE("tc_ck", 6) /* IDLIF_ARM */ + SET_CANIDLE("dpll1", 7) /* IDLDPLL_ARM */ + SET_CANIDLE("dpll2", 7) /* IDLDPLL_ARM */ + SET_CANIDLE("dpll3", 7) /* IDLDPLL_ARM */ + SET_CANIDLE("mpui_ck", 8) /* IDLAPI_ARM */ + SET_CANIDLE("armtim_ck", 9) /* IDLTIM_ARM */ } static inline void omap_clkm_idlect2_update(struct omap_mpu_state_s *s, @@ -1592,22 +1592,22 @@ static inline void omap_clkm_idlect2_update(struct omap_mpu_state_s *s, { omap_clk clk; -#define SET_ONOFF(clock, bit) \ - if (diff & (1 << bit)) { \ - clk = omap_findclk(s, clock); \ - omap_clk_onoff(clk, (value >> bit) & 1); \ +#define SET_ONOFF(clock, bit) \ + if (diff & (1 << bit)) { \ + clk = omap_findclk(s, clock); \ + omap_clk_onoff(clk, (value >> bit) & 1); \ } - SET_ONOFF("mpuwd_ck", 0) /* EN_WDTCK */ - SET_ONOFF("armxor_ck", 1) /* EN_XORPCK */ - SET_ONOFF("mpuper_ck", 2) /* EN_PERCK */ - SET_ONOFF("lcd_ck", 3) /* EN_LCDCK */ - SET_ONOFF("lb_ck", 4) /* EN_LBCK */ - SET_ONOFF("hsab_ck", 5) /* EN_HSABCK */ - SET_ONOFF("mpui_ck", 6) /* EN_APICK */ - SET_ONOFF("armtim_ck", 7) /* EN_TIMCK */ - SET_CANIDLE("dma_ck", 8) /* DMACK_REQ */ - SET_ONOFF("arm_gpio_ck", 9) /* EN_GPIOCK */ - SET_ONOFF("lbfree_ck", 10) /* EN_LBFREECK */ + SET_ONOFF("mpuwd_ck", 0) /* EN_WDTCK */ + SET_ONOFF("armxor_ck", 1) /* EN_XORPCK */ + SET_ONOFF("mpuper_ck", 2) /* EN_PERCK */ + SET_ONOFF("lcd_ck", 3) /* EN_LCDCK */ + SET_ONOFF("lb_ck", 4) /* EN_LBCK */ + SET_ONOFF("hsab_ck", 5) /* EN_HSABCK */ + SET_ONOFF("mpui_ck", 6) /* EN_APICK */ + SET_ONOFF("armtim_ck", 7) /* EN_TIMCK */ + SET_CANIDLE("dma_ck", 8) /* DMACK_REQ */ + SET_ONOFF("arm_gpio_ck", 9) /* EN_GPIOCK */ + SET_ONOFF("lbfree_ck", 10) /* EN_LBFREECK */ } static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s, @@ -1615,7 +1615,7 @@ static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s, { omap_clk clk; - if (diff & (3 << 4)) { /* TCLKOUT */ + if (diff & (3 << 4)) { /* TCLKOUT */ clk = omap_findclk(s, "tclk_out"); switch ((value >> 4) & 3) { case 1: @@ -1630,7 +1630,7 @@ static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s, omap_clk_onoff(clk, 0); } } - if (diff & (3 << 2)) { /* DCLKOUT */ + if (diff & (3 << 2)) { /* DCLKOUT */ clk = omap_findclk(s, "dclk_out"); switch ((value >> 2) & 3) { case 0: @@ -1647,7 +1647,7 @@ static inline void omap_clkm_ckout1_update(struct omap_mpu_state_s *s, break; } } - if (diff & (3 << 0)) { /* ACLKOUT */ + if (diff & (3 << 0)) { /* ACLKOUT */ clk = omap_findclk(s, "aclk_out"); switch ((value >> 0) & 3) { case 1: @@ -1685,51 +1685,51 @@ static void omap_clkm_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x00: /* ARM_CKCTL */ + case 0x00: /* ARM_CKCTL */ diff = s->clkm.arm_ckctl ^ value; s->clkm.arm_ckctl = value & 0x7fff; omap_clkm_ckctl_update(s, diff, value); return; - case 0x04: /* ARM_IDLECT1 */ + case 0x04: /* ARM_IDLECT1 */ diff = s->clkm.arm_idlect1 ^ value; s->clkm.arm_idlect1 = value & 0x0fff; omap_clkm_idlect1_update(s, diff, value); return; - case 0x08: /* ARM_IDLECT2 */ + case 0x08: /* ARM_IDLECT2 */ diff = s->clkm.arm_idlect2 ^ value; s->clkm.arm_idlect2 = value & 0x07ff; omap_clkm_idlect2_update(s, diff, value); return; - case 0x0c: /* ARM_EWUPCT */ + case 0x0c: /* ARM_EWUPCT */ s->clkm.arm_ewupct = value & 0x003f; return; - case 0x10: /* ARM_RSTCT1 */ + case 0x10: /* ARM_RSTCT1 */ diff = s->clkm.arm_rstct1 ^ value; s->clkm.arm_rstct1 = value & 0x0007; if (value & 9) { qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); s->clkm.cold_start = 0xa; } - if (diff & ~value & 4) { /* DSP_RST */ + if (diff & ~value & 4) { /* DSP_RST */ omap_mpui_reset(s); omap_tipb_bridge_reset(s->private_tipb); omap_tipb_bridge_reset(s->public_tipb); } - if (diff & 2) { /* DSP_EN */ + if (diff & 2) { /* DSP_EN */ clk = omap_findclk(s, "dsp_ck"); omap_clk_canidle(clk, (~value >> 1) & 1); } return; - case 0x14: /* ARM_RSTCT2 */ + case 0x14: /* ARM_RSTCT2 */ s->clkm.arm_rstct2 = value & 0x0001; return; - case 0x18: /* ARM_SYSST */ + case 0x18: /* ARM_SYSST */ if ((s->clkm.clocking_scheme ^ (value >> 11)) & 7) { s->clkm.clocking_scheme = (value >> 11) & 7; trace_omap1_pwl_clocking_scheme( @@ -1738,13 +1738,13 @@ static void omap_clkm_write(void *opaque, hwaddr addr, s->clkm.cold_start &= value & 0x3f; return; - case 0x1c: /* ARM_CKOUT1 */ + case 0x1c: /* ARM_CKOUT1 */ diff = s->clkm.arm_ckout1 ^ value; s->clkm.arm_ckout1 = value & 0x003f; omap_clkm_ckout1_update(s, diff, value); return; - case 0x20: /* ARM_CKOUT2 */ + case 0x20: /* ARM_CKOUT2 */ default: OMAP_BAD_REG(addr); } @@ -1767,16 +1767,16 @@ static uint64_t omap_clkdsp_read(void *opaque, hwaddr addr, } switch (addr) { - case 0x04: /* DSP_IDLECT1 */ + case 0x04: /* DSP_IDLECT1 */ return s->clkm.dsp_idlect1; - case 0x08: /* DSP_IDLECT2 */ + case 0x08: /* DSP_IDLECT2 */ return s->clkm.dsp_idlect2; - case 0x14: /* DSP_RSTCT2 */ + case 0x14: /* DSP_RSTCT2 */ return s->clkm.dsp_rstct2; - case 0x18: /* DSP_SYSST */ + case 0x18: /* DSP_SYSST */ return (s->clkm.clocking_scheme << 11) | s->clkm.cold_start | (cpu->halted << 6); /* Quite useless... */ } @@ -1790,7 +1790,7 @@ static inline void omap_clkdsp_idlect1_update(struct omap_mpu_state_s *s, { omap_clk clk; - SET_CANIDLE("dspxor_ck", 1); /* IDLXORP_DSP */ + SET_CANIDLE("dspxor_ck", 1); /* IDLXORP_DSP */ } static inline void omap_clkdsp_idlect2_update(struct omap_mpu_state_s *s, @@ -1798,7 +1798,7 @@ static inline void omap_clkdsp_idlect2_update(struct omap_mpu_state_s *s, { omap_clk clk; - SET_ONOFF("dspxor_ck", 1); /* EN_XORPCK */ + SET_ONOFF("dspxor_ck", 1); /* EN_XORPCK */ } static void omap_clkdsp_write(void *opaque, hwaddr addr, @@ -1813,23 +1813,23 @@ static void omap_clkdsp_write(void *opaque, hwaddr addr, } switch (addr) { - case 0x04: /* DSP_IDLECT1 */ + case 0x04: /* DSP_IDLECT1 */ diff = s->clkm.dsp_idlect1 ^ value; s->clkm.dsp_idlect1 = value & 0x01f7; omap_clkdsp_idlect1_update(s, diff, value); break; - case 0x08: /* DSP_IDLECT2 */ + case 0x08: /* DSP_IDLECT2 */ s->clkm.dsp_idlect2 = value & 0x0037; diff = s->clkm.dsp_idlect1 ^ value; omap_clkdsp_idlect2_update(s, diff, value); break; - case 0x14: /* DSP_RSTCT2 */ + case 0x14: /* DSP_RSTCT2 */ s->clkm.dsp_rstct2 = value & 0x0001; break; - case 0x18: /* DSP_SYSST */ + case 0x18: /* DSP_SYSST */ s->clkm.cold_start &= value & 0x3f; break; @@ -1928,8 +1928,8 @@ static void omap_mpuio_set(void *opaque, int line, int level) qemu_irq_raise(s->irq); /* TODO: wakeup */ } - if ((s->event & (1 << 0)) && /* SET_GPIO_EVENT_MODE */ - (s->event >> 1) == line) /* PIN_SELECT */ + if ((s->event & (1 << 0)) && /* SET_GPIO_EVENT_MODE */ + (s->event >> 1) == line) /* PIN_SELECT */ s->latch = s->inputs; } } @@ -1959,47 +1959,47 @@ static uint64_t omap_mpuio_read(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* INPUT_LATCH */ + case 0x00: /* INPUT_LATCH */ return s->inputs; - case 0x04: /* OUTPUT_REG */ + case 0x04: /* OUTPUT_REG */ return s->outputs; - case 0x08: /* IO_CNTL */ + case 0x08: /* IO_CNTL */ return s->dir; - case 0x10: /* KBR_LATCH */ + case 0x10: /* KBR_LATCH */ return s->row_latch; - case 0x14: /* KBC_REG */ + case 0x14: /* KBC_REG */ return s->cols; - case 0x18: /* GPIO_EVENT_MODE_REG */ + case 0x18: /* GPIO_EVENT_MODE_REG */ return s->event; - case 0x1c: /* GPIO_INT_EDGE_REG */ + case 0x1c: /* GPIO_INT_EDGE_REG */ return s->edge; - case 0x20: /* KBD_INT */ + case 0x20: /* KBD_INT */ return (~s->row_latch & 0x1f) && !s->kbd_mask; - case 0x24: /* GPIO_INT */ + case 0x24: /* GPIO_INT */ ret = s->ints; s->ints &= s->mask; if (ret) qemu_irq_lower(s->irq); return ret; - case 0x28: /* KBD_MASKIT */ + case 0x28: /* KBD_MASKIT */ return s->kbd_mask; - case 0x2c: /* GPIO_MASKIT */ + case 0x2c: /* GPIO_MASKIT */ return s->mask; - case 0x30: /* GPIO_DEBOUNCING_REG */ + case 0x30: /* GPIO_DEBOUNCING_REG */ return s->debounce; - case 0x34: /* GPIO_LATCH_REG */ + case 0x34: /* GPIO_LATCH_REG */ return s->latch; } @@ -2021,7 +2021,7 @@ static void omap_mpuio_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x04: /* OUTPUT_REG */ + case 0x04: /* OUTPUT_REG */ diff = (s->outputs ^ value) & ~s->dir; s->outputs = value; while ((ln = ctz32(diff)) != 32) { @@ -2031,7 +2031,7 @@ static void omap_mpuio_write(void *opaque, hwaddr addr, } break; - case 0x08: /* IO_CNTL */ + case 0x08: /* IO_CNTL */ diff = s->outputs & (s->dir ^ value); s->dir = value; @@ -2043,37 +2043,37 @@ static void omap_mpuio_write(void *opaque, hwaddr addr, } break; - case 0x14: /* KBC_REG */ + case 0x14: /* KBC_REG */ s->cols = value; omap_mpuio_kbd_update(s); break; - case 0x18: /* GPIO_EVENT_MODE_REG */ + case 0x18: /* GPIO_EVENT_MODE_REG */ s->event = value & 0x1f; break; - case 0x1c: /* GPIO_INT_EDGE_REG */ + case 0x1c: /* GPIO_INT_EDGE_REG */ s->edge = value; break; - case 0x28: /* KBD_MASKIT */ + case 0x28: /* KBD_MASKIT */ s->kbd_mask = value & 1; omap_mpuio_kbd_update(s); break; - case 0x2c: /* GPIO_MASKIT */ + case 0x2c: /* GPIO_MASKIT */ s->mask = value; break; - case 0x30: /* GPIO_DEBOUNCING_REG */ + case 0x30: /* GPIO_DEBOUNCING_REG */ s->debounce = value & 0x1ff; break; - case 0x00: /* INPUT_LATCH */ - case 0x10: /* KBR_LATCH */ - case 0x20: /* KBD_INT */ - case 0x24: /* GPIO_INT */ - case 0x34: /* GPIO_LATCH_REG */ + case 0x00: /* INPUT_LATCH */ + case 0x10: /* KBR_LATCH */ + case 0x20: /* KBD_INT */ + case 0x24: /* GPIO_INT */ + case 0x34: /* GPIO_LATCH_REG */ OMAP_RO_REG(addr); return; @@ -2176,24 +2176,24 @@ struct omap_uwire_s { static void omap_uwire_transfer_start(struct omap_uwire_s *s) { - int chipselect = (s->control >> 10) & 3; /* INDEX */ + int chipselect = (s->control >> 10) & 3; /* INDEX */ - if ((s->control >> 5) & 0x1f) { /* NB_BITS_WR */ + if ((s->control >> 5) & 0x1f) { /* NB_BITS_WR */ if (s->control & (1 << 12)) { /* CS_CMD */ qemu_log_mask(LOG_UNIMP, "uWireSlave TX CS:%d data:0x%04x\n", chipselect, s->txbuf >> (16 - ((s->control >> 5) & 0x1f))); } - s->control &= ~(1 << 14); /* CSRB */ + s->control &= ~(1 << 14); /* CSRB */ /* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or * a DRQ. When is the level IRQ supposed to be reset? */ } - if ((s->control >> 0) & 0x1f) { /* NB_BITS_RD */ + if ((s->control >> 0) & 0x1f) { /* NB_BITS_RD */ if (s->control & (1 << 12)) { /* CS_CMD */ qemu_log_mask(LOG_UNIMP, "uWireSlave RX CS:%d\n", chipselect); } - s->control |= 1 << 15; /* RDRB */ + s->control |= 1 << 15; /* RDRB */ /* TODO: depending on s->setup[4] bits [1:0] assert an IRQ or * a DRQ. When is the level IRQ supposed to be reset? */ } @@ -2209,22 +2209,22 @@ static uint64_t omap_uwire_read(void *opaque, hwaddr addr, unsigned size) } switch (offset) { - case 0x00: /* RDR */ - s->control &= ~(1 << 15); /* RDRB */ + case 0x00: /* RDR */ + s->control &= ~(1 << 15); /* RDRB */ return s->rxbuf; - case 0x04: /* CSR */ + case 0x04: /* CSR */ return s->control; - case 0x08: /* SR1 */ + case 0x08: /* SR1 */ return s->setup[0]; - case 0x0c: /* SR2 */ + case 0x0c: /* SR2 */ return s->setup[1]; - case 0x10: /* SR3 */ + case 0x10: /* SR3 */ return s->setup[2]; - case 0x14: /* SR4 */ + case 0x14: /* SR4 */ return s->setup[3]; - case 0x18: /* SR5 */ + case 0x18: /* SR5 */ return s->setup[4]; } @@ -2244,39 +2244,39 @@ static void omap_uwire_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* TDR */ - s->txbuf = value; /* TD */ - if ((s->setup[4] & (1 << 2)) && /* AUTO_TX_EN */ - ((s->setup[4] & (1 << 3)) || /* CS_TOGGLE_TX_EN */ - (s->control & (1 << 12)))) { /* CS_CMD */ - s->control |= 1 << 14; /* CSRB */ + case 0x00: /* TDR */ + s->txbuf = value; /* TD */ + if ((s->setup[4] & (1 << 2)) && /* AUTO_TX_EN */ + ((s->setup[4] & (1 << 3)) || /* CS_TOGGLE_TX_EN */ + (s->control & (1 << 12)))) { /* CS_CMD */ + s->control |= 1 << 14; /* CSRB */ omap_uwire_transfer_start(s); } break; - case 0x04: /* CSR */ + case 0x04: /* CSR */ s->control = value & 0x1fff; - if (value & (1 << 13)) /* START */ + if (value & (1 << 13)) /* START */ omap_uwire_transfer_start(s); break; - case 0x08: /* SR1 */ + case 0x08: /* SR1 */ s->setup[0] = value & 0x003f; break; - case 0x0c: /* SR2 */ + case 0x0c: /* SR2 */ s->setup[1] = value & 0x0fc0; break; - case 0x10: /* SR3 */ + case 0x10: /* SR3 */ s->setup[2] = value & 0x0003; break; - case 0x14: /* SR4 */ + case 0x14: /* SR4 */ s->setup[3] = value & 0x0001; break; - case 0x18: /* SR5 */ + case 0x18: /* SR5 */ s->setup[4] = value & 0x000f; break; @@ -2350,9 +2350,9 @@ static uint64_t omap_pwl_read(void *opaque, hwaddr addr, unsigned size) } switch (offset) { - case 0x00: /* PWL_LEVEL */ + case 0x00: /* PWL_LEVEL */ return s->level; - case 0x04: /* PWL_CTRL */ + case 0x04: /* PWL_CTRL */ return s->enable; } OMAP_BAD_REG(addr); @@ -2371,11 +2371,11 @@ static void omap_pwl_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* PWL_LEVEL */ + case 0x00: /* PWL_LEVEL */ s->level = value; omap_pwl_update(s); break; - case 0x04: /* PWL_CTRL */ + case 0x04: /* PWL_CTRL */ s->enable = value & 1; omap_pwl_update(s); break; @@ -2443,11 +2443,11 @@ static uint64_t omap_pwt_read(void *opaque, hwaddr addr, unsigned size) } switch (offset) { - case 0x00: /* FRC */ + case 0x00: /* FRC */ return s->frc; - case 0x04: /* VCR */ + case 0x04: /* VCR */ return s->vrc; - case 0x08: /* GCR */ + case 0x08: /* GCR */ return s->gcr; } OMAP_BAD_REG(addr); @@ -2466,10 +2466,10 @@ static void omap_pwt_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* FRC */ + case 0x00: /* FRC */ s->frc = value & 0x3f; break; - case 0x04: /* VRC */ + case 0x04: /* VRC */ if ((value ^ s->vrc) & 1) { if (value & 1) { trace_omap1_pwt_buzz( @@ -2494,7 +2494,7 @@ static void omap_pwt_write(void *opaque, hwaddr addr, } s->vrc = value & 0x7f; break; - case 0x08: /* GCR */ + case 0x08: /* GCR */ s->gcr = value & 3; break; default: @@ -2577,69 +2577,69 @@ static uint64_t omap_rtc_read(void *opaque, hwaddr addr, unsigned size) } switch (offset) { - case 0x00: /* SECONDS_REG */ + case 0x00: /* SECONDS_REG */ return to_bcd(s->current_tm.tm_sec); - case 0x04: /* MINUTES_REG */ + case 0x04: /* MINUTES_REG */ return to_bcd(s->current_tm.tm_min); - case 0x08: /* HOURS_REG */ + case 0x08: /* HOURS_REG */ if (s->pm_am) return ((s->current_tm.tm_hour > 11) << 7) | to_bcd(((s->current_tm.tm_hour - 1) % 12) + 1); else return to_bcd(s->current_tm.tm_hour); - case 0x0c: /* DAYS_REG */ + case 0x0c: /* DAYS_REG */ return to_bcd(s->current_tm.tm_mday); - case 0x10: /* MONTHS_REG */ + case 0x10: /* MONTHS_REG */ return to_bcd(s->current_tm.tm_mon + 1); - case 0x14: /* YEARS_REG */ + case 0x14: /* YEARS_REG */ return to_bcd(s->current_tm.tm_year % 100); - case 0x18: /* WEEK_REG */ + case 0x18: /* WEEK_REG */ return s->current_tm.tm_wday; - case 0x20: /* ALARM_SECONDS_REG */ + case 0x20: /* ALARM_SECONDS_REG */ return to_bcd(s->alarm_tm.tm_sec); - case 0x24: /* ALARM_MINUTES_REG */ + case 0x24: /* ALARM_MINUTES_REG */ return to_bcd(s->alarm_tm.tm_min); - case 0x28: /* ALARM_HOURS_REG */ + case 0x28: /* ALARM_HOURS_REG */ if (s->pm_am) return ((s->alarm_tm.tm_hour > 11) << 7) | to_bcd(((s->alarm_tm.tm_hour - 1) % 12) + 1); else return to_bcd(s->alarm_tm.tm_hour); - case 0x2c: /* ALARM_DAYS_REG */ + case 0x2c: /* ALARM_DAYS_REG */ return to_bcd(s->alarm_tm.tm_mday); - case 0x30: /* ALARM_MONTHS_REG */ + case 0x30: /* ALARM_MONTHS_REG */ return to_bcd(s->alarm_tm.tm_mon + 1); - case 0x34: /* ALARM_YEARS_REG */ + case 0x34: /* ALARM_YEARS_REG */ return to_bcd(s->alarm_tm.tm_year % 100); - case 0x40: /* RTC_CTRL_REG */ + case 0x40: /* RTC_CTRL_REG */ return (s->pm_am << 3) | (s->auto_comp << 2) | (s->round << 1) | s->running; - case 0x44: /* RTC_STATUS_REG */ + case 0x44: /* RTC_STATUS_REG */ i = s->status; s->status &= ~0x3d; return i; - case 0x48: /* RTC_INTERRUPTS_REG */ + case 0x48: /* RTC_INTERRUPTS_REG */ return s->interrupts; - case 0x4c: /* RTC_COMP_LSB_REG */ + case 0x4c: /* RTC_COMP_LSB_REG */ return ((uint16_t) s->comp_reg) & 0xff; - case 0x50: /* RTC_COMP_MSB_REG */ + case 0x50: /* RTC_COMP_MSB_REG */ return ((uint16_t) s->comp_reg) >> 8; } @@ -2661,17 +2661,17 @@ static void omap_rtc_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* SECONDS_REG */ + case 0x00: /* SECONDS_REG */ s->ti -= s->current_tm.tm_sec; s->ti += from_bcd(value); return; - case 0x04: /* MINUTES_REG */ + case 0x04: /* MINUTES_REG */ s->ti -= s->current_tm.tm_min * 60; s->ti += from_bcd(value) * 60; return; - case 0x08: /* HOURS_REG */ + case 0x08: /* HOURS_REG */ s->ti -= s->current_tm.tm_hour * 3600; if (s->pm_am) { s->ti += (from_bcd(value & 0x3f) & 12) * 3600; @@ -2680,12 +2680,12 @@ static void omap_rtc_write(void *opaque, hwaddr addr, s->ti += from_bcd(value & 0x3f) * 3600; return; - case 0x0c: /* DAYS_REG */ + case 0x0c: /* DAYS_REG */ s->ti -= s->current_tm.tm_mday * 86400; s->ti += from_bcd(value) * 86400; return; - case 0x10: /* MONTHS_REG */ + case 0x10: /* MONTHS_REG */ memcpy(&new_tm, &s->current_tm, sizeof(new_tm)); new_tm.tm_mon = from_bcd(value); ti[0] = mktimegm(&s->current_tm); @@ -2701,7 +2701,7 @@ static void omap_rtc_write(void *opaque, hwaddr addr, } return; - case 0x14: /* YEARS_REG */ + case 0x14: /* YEARS_REG */ memcpy(&new_tm, &s->current_tm, sizeof(new_tm)); new_tm.tm_year += from_bcd(value) - (new_tm.tm_year % 100); ti[0] = mktimegm(&s->current_tm); @@ -2717,20 +2717,20 @@ static void omap_rtc_write(void *opaque, hwaddr addr, } return; - case 0x18: /* WEEK_REG */ - return; /* Ignored */ + case 0x18: /* WEEK_REG */ + return; /* Ignored */ - case 0x20: /* ALARM_SECONDS_REG */ + case 0x20: /* ALARM_SECONDS_REG */ s->alarm_tm.tm_sec = from_bcd(value); omap_rtc_alarm_update(s); return; - case 0x24: /* ALARM_MINUTES_REG */ + case 0x24: /* ALARM_MINUTES_REG */ s->alarm_tm.tm_min = from_bcd(value); omap_rtc_alarm_update(s); return; - case 0x28: /* ALARM_HOURS_REG */ + case 0x28: /* ALARM_HOURS_REG */ if (s->pm_am) s->alarm_tm.tm_hour = ((from_bcd(value & 0x3f)) % 12) + @@ -2740,22 +2740,22 @@ static void omap_rtc_write(void *opaque, hwaddr addr, omap_rtc_alarm_update(s); return; - case 0x2c: /* ALARM_DAYS_REG */ + case 0x2c: /* ALARM_DAYS_REG */ s->alarm_tm.tm_mday = from_bcd(value); omap_rtc_alarm_update(s); return; - case 0x30: /* ALARM_MONTHS_REG */ + case 0x30: /* ALARM_MONTHS_REG */ s->alarm_tm.tm_mon = from_bcd(value); omap_rtc_alarm_update(s); return; - case 0x34: /* ALARM_YEARS_REG */ + case 0x34: /* ALARM_YEARS_REG */ s->alarm_tm.tm_year = from_bcd(value); omap_rtc_alarm_update(s); return; - case 0x40: /* RTC_CTRL_REG */ + case 0x40: /* RTC_CTRL_REG */ s->pm_am = (value >> 3) & 1; s->auto_comp = (value >> 2) & 1; s->round = (value >> 1) & 1; @@ -2764,21 +2764,21 @@ static void omap_rtc_write(void *opaque, hwaddr addr, s->status |= s->running << 1; return; - case 0x44: /* RTC_STATUS_REG */ + case 0x44: /* RTC_STATUS_REG */ s->status &= ~((value & 0xc0) ^ 0x80); omap_rtc_interrupts_update(s); return; - case 0x48: /* RTC_INTERRUPTS_REG */ + case 0x48: /* RTC_INTERRUPTS_REG */ s->interrupts = value; return; - case 0x4c: /* RTC_COMP_LSB_REG */ + case 0x4c: /* RTC_COMP_LSB_REG */ s->comp_reg &= 0xff00; s->comp_reg |= 0x00ff & value; return; - case 0x50: /* RTC_COMP_MSB_REG */ + case 0x50: /* RTC_COMP_MSB_REG */ s->comp_reg &= 0x00ff; s->comp_reg |= 0xff00 & (value << 8); return; @@ -2929,12 +2929,12 @@ static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) { int irq; - switch ((s->spcr[0] >> 4) & 3) { /* RINTM */ + switch ((s->spcr[0] >> 4) & 3) { /* RINTM */ case 0: - irq = (s->spcr[0] >> 1) & 1; /* RRDY */ + irq = (s->spcr[0] >> 1) & 1; /* RRDY */ break; case 3: - irq = (s->spcr[0] >> 3) & 1; /* RSYNCERR */ + irq = (s->spcr[0] >> 3) & 1; /* RSYNCERR */ break; default: irq = 0; @@ -2944,12 +2944,12 @@ static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) if (irq) qemu_irq_pulse(s->rxirq); - switch ((s->spcr[1] >> 4) & 3) { /* XINTM */ + switch ((s->spcr[1] >> 4) & 3) { /* XINTM */ case 0: - irq = (s->spcr[1] >> 1) & 1; /* XRDY */ + irq = (s->spcr[1] >> 1) & 1; /* XRDY */ break; case 3: - irq = (s->spcr[1] >> 3) & 1; /* XSYNCERR */ + irq = (s->spcr[1] >> 3) & 1; /* XSYNCERR */ break; default: irq = 0; @@ -2962,9 +2962,9 @@ static void omap_mcbsp_intr_update(struct omap_mcbsp_s *s) static void omap_mcbsp_rx_newdata(struct omap_mcbsp_s *s) { - if ((s->spcr[0] >> 1) & 1) /* RRDY */ - s->spcr[0] |= 1 << 2; /* RFULL */ - s->spcr[0] |= 1 << 1; /* RRDY */ + if ((s->spcr[0] >> 1) & 1) /* RRDY */ + s->spcr[0] |= 1 << 2; /* RFULL */ + s->spcr[0] |= 1 << 1; /* RRDY */ qemu_irq_raise(s->rxdrq); omap_mcbsp_intr_update(s); } @@ -3004,14 +3004,14 @@ static void omap_mcbsp_rx_stop(struct omap_mcbsp_s *s) static void omap_mcbsp_rx_done(struct omap_mcbsp_s *s) { - s->spcr[0] &= ~(1 << 1); /* RRDY */ + s->spcr[0] &= ~(1 << 1); /* RRDY */ qemu_irq_lower(s->rxdrq); omap_mcbsp_intr_update(s); } static void omap_mcbsp_tx_newdata(struct omap_mcbsp_s *s) { - s->spcr[1] |= 1 << 1; /* XRDY */ + s->spcr[1] |= 1 << 1; /* XRDY */ qemu_irq_raise(s->txdrq); omap_mcbsp_intr_update(s); } @@ -3046,7 +3046,7 @@ static void omap_mcbsp_tx_start(struct omap_mcbsp_s *s) static void omap_mcbsp_tx_done(struct omap_mcbsp_s *s) { - s->spcr[1] &= ~(1 << 1); /* XRDY */ + s->spcr[1] &= ~(1 << 1); /* XRDY */ qemu_irq_lower(s->txdrq); omap_mcbsp_intr_update(s); if (s->codec && s->codec->cts) @@ -3064,27 +3064,27 @@ static void omap_mcbsp_req_update(struct omap_mcbsp_s *s) { int prev_rx_rate, prev_tx_rate; int rx_rate = 0, tx_rate = 0; - int cpu_rate = 1500000; /* XXX */ + int cpu_rate = 1500000; /* XXX */ /* TODO: check CLKSTP bit */ - if (s->spcr[1] & (1 << 6)) { /* GRST */ - if (s->spcr[0] & (1 << 0)) { /* RRST */ - if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ - (s->pcr & (1 << 8))) { /* CLKRM */ - if (~s->pcr & (1 << 7)) /* SCLKME */ + if (s->spcr[1] & (1 << 6)) { /* GRST */ + if (s->spcr[0] & (1 << 0)) { /* RRST */ + if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ + (s->pcr & (1 << 8))) { /* CLKRM */ + if (~s->pcr & (1 << 7)) /* SCLKME */ rx_rate = cpu_rate / - ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ + ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ } else if (s->codec) rx_rate = s->codec->rx_rate; } - if (s->spcr[1] & (1 << 0)) { /* XRST */ - if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ - (s->pcr & (1 << 9))) { /* CLKXM */ - if (~s->pcr & (1 << 7)) /* SCLKME */ + if (s->spcr[1] & (1 << 0)) { /* XRST */ + if ((s->srgr[1] & (1 << 13)) && /* CLKSM */ + (s->pcr & (1 << 9))) { /* CLKXM */ + if (~s->pcr & (1 << 7)) /* SCLKME */ tx_rate = cpu_rate / - ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ + ((s->srgr[0] & 0xff) + 1); /* CLKGDV */ } else if (s->codec) tx_rate = s->codec->tx_rate; @@ -3121,11 +3121,11 @@ static uint64_t omap_mcbsp_read(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* DRR2 */ - if (((s->rcr[0] >> 5) & 7) < 3) /* RWDLEN1 */ + case 0x00: /* DRR2 */ + if (((s->rcr[0] >> 5) & 7) < 3) /* RWDLEN1 */ return 0x0000; /* Fall through. */ - case 0x02: /* DRR1 */ + case 0x02: /* DRR1 */ if (s->rx_req < 2) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Rx FIFO underrun\n", __func__); omap_mcbsp_rx_done(s); @@ -3143,63 +3143,63 @@ static uint64_t omap_mcbsp_read(void *opaque, hwaddr addr, } return 0x0000; - case 0x04: /* DXR2 */ - case 0x06: /* DXR1 */ + case 0x04: /* DXR2 */ + case 0x06: /* DXR1 */ return 0x0000; - case 0x08: /* SPCR2 */ + case 0x08: /* SPCR2 */ return s->spcr[1]; - case 0x0a: /* SPCR1 */ + case 0x0a: /* SPCR1 */ return s->spcr[0]; - case 0x0c: /* RCR2 */ + case 0x0c: /* RCR2 */ return s->rcr[1]; - case 0x0e: /* RCR1 */ + case 0x0e: /* RCR1 */ return s->rcr[0]; - case 0x10: /* XCR2 */ + case 0x10: /* XCR2 */ return s->xcr[1]; - case 0x12: /* XCR1 */ + case 0x12: /* XCR1 */ return s->xcr[0]; - case 0x14: /* SRGR2 */ + case 0x14: /* SRGR2 */ return s->srgr[1]; - case 0x16: /* SRGR1 */ + case 0x16: /* SRGR1 */ return s->srgr[0]; - case 0x18: /* MCR2 */ + case 0x18: /* MCR2 */ return s->mcr[1]; - case 0x1a: /* MCR1 */ + case 0x1a: /* MCR1 */ return s->mcr[0]; - case 0x1c: /* RCERA */ + case 0x1c: /* RCERA */ return s->rcer[0]; - case 0x1e: /* RCERB */ + case 0x1e: /* RCERB */ return s->rcer[1]; - case 0x20: /* XCERA */ + case 0x20: /* XCERA */ return s->xcer[0]; - case 0x22: /* XCERB */ + case 0x22: /* XCERB */ return s->xcer[1]; - case 0x24: /* PCR0 */ + case 0x24: /* PCR0 */ return s->pcr; - case 0x26: /* RCERC */ + case 0x26: /* RCERC */ return s->rcer[2]; - case 0x28: /* RCERD */ + case 0x28: /* RCERD */ return s->rcer[3]; - case 0x2a: /* XCERC */ + case 0x2a: /* XCERC */ return s->xcer[2]; - case 0x2c: /* XCERD */ + case 0x2c: /* XCERD */ return s->xcer[3]; - case 0x2e: /* RCERE */ + case 0x2e: /* RCERE */ return s->rcer[4]; - case 0x30: /* RCERF */ + case 0x30: /* RCERF */ return s->rcer[5]; - case 0x32: /* XCERE */ + case 0x32: /* XCERE */ return s->xcer[4]; - case 0x34: /* XCERF */ + case 0x34: /* XCERF */ return s->xcer[5]; - case 0x36: /* RCERG */ + case 0x36: /* RCERG */ return s->rcer[6]; - case 0x38: /* RCERH */ + case 0x38: /* RCERH */ return s->rcer[7]; - case 0x3a: /* XCERG */ + case 0x3a: /* XCERG */ return s->xcer[6]; - case 0x3c: /* XCERH */ + case 0x3c: /* XCERH */ return s->xcer[7]; } @@ -3214,16 +3214,16 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, int offset = addr & OMAP_MPUI_REG_MASK; switch (offset) { - case 0x00: /* DRR2 */ - case 0x02: /* DRR1 */ + case 0x00: /* DRR2 */ + case 0x02: /* DRR1 */ OMAP_RO_REG(addr); return; - case 0x04: /* DXR2 */ - if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ + case 0x04: /* DXR2 */ + if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ return; /* Fall through. */ - case 0x06: /* DXR1 */ + case 0x06: /* DXR1 */ if (s->tx_req > 1) { s->tx_req -= 2; if (s->codec && s->codec->cts) { @@ -3237,15 +3237,15 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, } return; - case 0x08: /* SPCR2 */ + case 0x08: /* SPCR2 */ s->spcr[1] &= 0x0002; s->spcr[1] |= 0x03f9 & value; - s->spcr[1] |= 0x0004 & (value << 2); /* XEMPTY := XRST */ - if (~value & 1) /* XRST */ + s->spcr[1] |= 0x0004 & (value << 2); /* XEMPTY := XRST */ + if (~value & 1) /* XRST */ s->spcr[1] &= ~6; omap_mcbsp_req_update(s); return; - case 0x0a: /* SPCR1 */ + case 0x0a: /* SPCR1 */ s->spcr[0] &= 0x0006; s->spcr[0] |= 0xf8f9 & value; if (value & (1 << 15)) { /* DLB */ @@ -3253,7 +3253,7 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, "%s: Digital Loopback mode enable attempt\n", __func__); } - if (~value & 1) { /* RRST */ + if (~value & 1) { /* RRST */ s->spcr[0] &= ~6; s->rx_req = 0; omap_mcbsp_rx_done(s); @@ -3261,27 +3261,27 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, omap_mcbsp_req_update(s); return; - case 0x0c: /* RCR2 */ + case 0x0c: /* RCR2 */ s->rcr[1] = value & 0xffff; return; - case 0x0e: /* RCR1 */ + case 0x0e: /* RCR1 */ s->rcr[0] = value & 0x7fe0; return; - case 0x10: /* XCR2 */ + case 0x10: /* XCR2 */ s->xcr[1] = value & 0xffff; return; - case 0x12: /* XCR1 */ + case 0x12: /* XCR1 */ s->xcr[0] = value & 0x7fe0; return; - case 0x14: /* SRGR2 */ + case 0x14: /* SRGR2 */ s->srgr[1] = value & 0xffff; omap_mcbsp_req_update(s); return; - case 0x16: /* SRGR1 */ + case 0x16: /* SRGR1 */ s->srgr[0] = value & 0xffff; omap_mcbsp_req_update(s); return; - case 0x18: /* MCR2 */ + case 0x18: /* MCR2 */ s->mcr[1] = value & 0x03e3; if (value & 3) { /* XMCM */ qemu_log_mask(LOG_UNIMP, @@ -3289,7 +3289,7 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, __func__); } return; - case 0x1a: /* MCR1 */ + case 0x1a: /* MCR1 */ s->mcr[0] = value & 0x03e1; if (value & 1) { /* RMCM */ qemu_log_mask(LOG_UNIMP, @@ -3297,55 +3297,55 @@ static void omap_mcbsp_writeh(void *opaque, hwaddr addr, __func__); } return; - case 0x1c: /* RCERA */ + case 0x1c: /* RCERA */ s->rcer[0] = value & 0xffff; return; - case 0x1e: /* RCERB */ + case 0x1e: /* RCERB */ s->rcer[1] = value & 0xffff; return; - case 0x20: /* XCERA */ + case 0x20: /* XCERA */ s->xcer[0] = value & 0xffff; return; - case 0x22: /* XCERB */ + case 0x22: /* XCERB */ s->xcer[1] = value & 0xffff; return; - case 0x24: /* PCR0 */ + case 0x24: /* PCR0 */ s->pcr = value & 0x7faf; return; - case 0x26: /* RCERC */ + case 0x26: /* RCERC */ s->rcer[2] = value & 0xffff; return; - case 0x28: /* RCERD */ + case 0x28: /* RCERD */ s->rcer[3] = value & 0xffff; return; - case 0x2a: /* XCERC */ + case 0x2a: /* XCERC */ s->xcer[2] = value & 0xffff; return; - case 0x2c: /* XCERD */ + case 0x2c: /* XCERD */ s->xcer[3] = value & 0xffff; return; - case 0x2e: /* RCERE */ + case 0x2e: /* RCERE */ s->rcer[4] = value & 0xffff; return; - case 0x30: /* RCERF */ + case 0x30: /* RCERF */ s->rcer[5] = value & 0xffff; return; - case 0x32: /* XCERE */ + case 0x32: /* XCERE */ s->xcer[4] = value & 0xffff; return; - case 0x34: /* XCERF */ + case 0x34: /* XCERF */ s->xcer[5] = value & 0xffff; return; - case 0x36: /* RCERG */ + case 0x36: /* RCERG */ s->rcer[6] = value & 0xffff; return; - case 0x38: /* RCERH */ + case 0x38: /* RCERH */ s->rcer[7] = value & 0xffff; return; - case 0x3a: /* XCERG */ + case 0x3a: /* XCERG */ s->xcer[6] = value & 0xffff; return; - case 0x3c: /* XCERH */ + case 0x3c: /* XCERH */ s->xcer[7] = value & 0xffff; return; } @@ -3359,8 +3359,8 @@ static void omap_mcbsp_writew(void *opaque, hwaddr addr, struct omap_mcbsp_s *s = opaque; int offset = addr & OMAP_MPUI_REG_MASK; - if (offset == 0x04) { /* DXR */ - if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ + if (offset == 0x04) { /* DXR */ + if (((s->xcr[0] >> 5) & 7) < 3) /* XWDLEN1 */ return; if (s->tx_req > 3) { s->tx_req -= 4; @@ -3504,15 +3504,15 @@ static void omap_lpg_update(struct omap_lpg_s *s) int64_t on, period = 1, ticks = 1000; static const int per[8] = { 1, 2, 4, 8, 12, 16, 20, 24 }; - if (~s->control & (1 << 6)) /* LPGRES */ + if (~s->control & (1 << 6)) /* LPGRES */ on = 0; - else if (s->control & (1 << 7)) /* PERM_ON */ + else if (s->control & (1 << 7)) /* PERM_ON */ on = period; else { - period = muldiv64(ticks, per[s->control & 7], /* PERCTRL */ + period = muldiv64(ticks, per[s->control & 7], /* PERCTRL */ 256 / 32); on = (s->clk && s->power) ? muldiv64(ticks, - per[(s->control >> 3) & 7], 256) : 0; /* ONCTRL */ + per[(s->control >> 3) & 7], 256) : 0; /* ONCTRL */ } timer_del(s->tm); @@ -3550,10 +3550,10 @@ static uint64_t omap_lpg_read(void *opaque, hwaddr addr, unsigned size) } switch (offset) { - case 0x00: /* LCR */ + case 0x00: /* LCR */ return s->control; - case 0x04: /* PMR */ + case 0x04: /* PMR */ return s->power; } @@ -3573,14 +3573,14 @@ static void omap_lpg_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* LCR */ - if (~value & (1 << 6)) /* LPGRES */ + case 0x00: /* LCR */ + if (~value & (1 << 6)) /* LPGRES */ omap_lpg_reset(s); s->control = value & 0xff; omap_lpg_update(s); return; - case 0x04: /* PMR */ + case 0x04: /* PMR */ s->power = value & 0x01; omap_lpg_update(s); return; @@ -3630,7 +3630,7 @@ static uint64_t omap_mpui_io_read(void *opaque, hwaddr addr, return omap_badwidth_read16(opaque, addr); } - if (addr == OMAP_MPUI_BASE) /* CMR */ + if (addr == OMAP_MPUI_BASE) /* CMR */ return 0xfe4d; OMAP_BAD_REG(addr); @@ -3703,25 +3703,25 @@ static const struct omap_map_s { const char *name; } omap15xx_dsp_mm[] = { /* Strobe 0 */ - { 0xe1010000, 0xfffb0000, 0x800, "UART1 BT" }, /* CS0 */ - { 0xe1010800, 0xfffb0800, 0x800, "UART2 COM" }, /* CS1 */ - { 0xe1011800, 0xfffb1800, 0x800, "McBSP1 audio" }, /* CS3 */ - { 0xe1012000, 0xfffb2000, 0x800, "MCSI2 communication" }, /* CS4 */ - { 0xe1012800, 0xfffb2800, 0x800, "MCSI1 BT u-Law" }, /* CS5 */ - { 0xe1013000, 0xfffb3000, 0x800, "uWire" }, /* CS6 */ - { 0xe1013800, 0xfffb3800, 0x800, "I^2C" }, /* CS7 */ - { 0xe1014000, 0xfffb4000, 0x800, "USB W2FC" }, /* CS8 */ - { 0xe1014800, 0xfffb4800, 0x800, "RTC" }, /* CS9 */ - { 0xe1015000, 0xfffb5000, 0x800, "MPUIO" }, /* CS10 */ - { 0xe1015800, 0xfffb5800, 0x800, "PWL" }, /* CS11 */ - { 0xe1016000, 0xfffb6000, 0x800, "PWT" }, /* CS12 */ - { 0xe1017000, 0xfffb7000, 0x800, "McBSP3" }, /* CS14 */ - { 0xe1017800, 0xfffb7800, 0x800, "MMC" }, /* CS15 */ - { 0xe1019000, 0xfffb9000, 0x800, "32-kHz timer" }, /* CS18 */ - { 0xe1019800, 0xfffb9800, 0x800, "UART3" }, /* CS19 */ - { 0xe101c800, 0xfffbc800, 0x800, "TIPB switches" }, /* CS25 */ + { 0xe1010000, 0xfffb0000, 0x800, "UART1 BT" }, /* CS0 */ + { 0xe1010800, 0xfffb0800, 0x800, "UART2 COM" }, /* CS1 */ + { 0xe1011800, 0xfffb1800, 0x800, "McBSP1 audio" }, /* CS3 */ + { 0xe1012000, 0xfffb2000, 0x800, "MCSI2 communication" }, /* CS4 */ + { 0xe1012800, 0xfffb2800, 0x800, "MCSI1 BT u-Law" }, /* CS5 */ + { 0xe1013000, 0xfffb3000, 0x800, "uWire" }, /* CS6 */ + { 0xe1013800, 0xfffb3800, 0x800, "I^2C" }, /* CS7 */ + { 0xe1014000, 0xfffb4000, 0x800, "USB W2FC" }, /* CS8 */ + { 0xe1014800, 0xfffb4800, 0x800, "RTC" }, /* CS9 */ + { 0xe1015000, 0xfffb5000, 0x800, "MPUIO" }, /* CS10 */ + { 0xe1015800, 0xfffb5800, 0x800, "PWL" }, /* CS11 */ + { 0xe1016000, 0xfffb6000, 0x800, "PWT" }, /* CS12 */ + { 0xe1017000, 0xfffb7000, 0x800, "McBSP3" }, /* CS14 */ + { 0xe1017800, 0xfffb7800, 0x800, "MMC" }, /* CS15 */ + { 0xe1019000, 0xfffb9000, 0x800, "32-kHz timer" }, /* CS18 */ + { 0xe1019800, 0xfffb9800, 0x800, "UART3" }, /* CS19 */ + { 0xe101c800, 0xfffbc800, 0x800, "TIPB switches" }, /* CS25 */ /* Strobe 1 */ - { 0xe101e000, 0xfffce000, 0x800, "GPIOs" }, /* CS28 */ + { 0xe101e000, 0xfffce000, 0x800, "GPIOs" }, /* CS28 */ { 0 } }; @@ -4025,18 +4025,18 @@ struct omap_mpu_state_s *omap310_mpu_init(MemoryRegion *dram, 0xfffbd800, omap_findclk(s, "clk32-kHz")); /* Register mappings not currently implemented: - * MCSI2 Comm fffb2000 - fffb27ff (not mapped on OMAP310) - * MCSI1 Bluetooth fffb2800 - fffb2fff (not mapped on OMAP310) - * USB W2FC fffb4000 - fffb47ff - * Camera Interface fffb6800 - fffb6fff - * USB Host fffba000 - fffba7ff - * FAC fffba800 - fffbafff - * HDQ/1-Wire fffbc000 - fffbc7ff - * TIPB switches fffbc800 - fffbcfff - * Mailbox fffcf000 - fffcf7ff - * Local bus IF fffec100 - fffec1ff - * Local bus MMU fffec200 - fffec2ff - * DSP MMU fffed200 - fffed2ff + * MCSI2 Comm fffb2000 - fffb27ff (not mapped on OMAP310) + * MCSI1 Bluetooth fffb2800 - fffb2fff (not mapped on OMAP310) + * USB W2FC fffb4000 - fffb47ff + * Camera Interface fffb6800 - fffb6fff + * USB Host fffba000 - fffba7ff + * FAC fffba800 - fffbafff + * HDQ/1-Wire fffbc000 - fffbc7ff + * TIPB switches fffbc800 - fffbcfff + * Mailbox fffcf000 - fffcf7ff + * Local bus IF fffec100 - fffec1ff + * Local bus MMU fffec200 - fffec2ff + * DSP MMU fffed200 - fffed2ff */ omap_setup_dsp_mapping(system_memory, omap15xx_dsp_mm); diff --git a/hw/arm/omap_sx1.c b/hw/arm/omap_sx1.c index 1d89a202bb..5d4a31b7ae 100644 --- a/hw/arm/omap_sx1.c +++ b/hw/arm/omap_sx1.c @@ -1,7 +1,7 @@ /* omap_sx1.c Support for the Siemens SX1 smartphone emulation. * * Copyright (C) 2008 - * Jean-Christophe PLAGNIOL-VILLARD + * Jean-Christophe PLAGNIOL-VILLARD * Copyright (C) 2007 Vladimir Ananiev * * based on PalmOne's (TM) PDAs support (palm.c) diff --git a/hw/dma/omap_dma.c b/hw/dma/omap_dma.c index 9a8c3c34a0..101f91f4a3 100644 --- a/hw/dma/omap_dma.c +++ b/hw/dma/omap_dma.c @@ -131,9 +131,9 @@ struct omap_dma_s { #define LAST_FRAME_INTR (1 << 4) #define END_BLOCK_INTR (1 << 5) #define SYNC (1 << 6) -#define END_PKT_INTR (1 << 7) -#define TRANS_ERR_INTR (1 << 8) -#define MISALIGN_INTR (1 << 11) +#define END_PKT_INTR (1 << 7) +#define TRANS_ERR_INTR (1 << 8) +#define MISALIGN_INTR (1 << 11) static inline void omap_dma_interrupts_update(struct omap_dma_s *s) { @@ -526,12 +526,12 @@ static void omap_dma_transfer_setup(struct soc_dma_ch_s *dma) /* Check all the conditions that terminate the transfer starting * with those that can occur the soonest. */ -#define INTR_CHECK(cond, id, nelements) \ - if (cond) { \ - elements[id] = nelements; \ - if (elements[id] < min_elems) \ - min_elems = elements[id]; \ - } else \ +#define INTR_CHECK(cond, id, nelements) \ + if (cond) { \ + elements[id] = nelements; \ + if (elements[id] < min_elems) \ + min_elems = elements[id]; \ + } else \ elements[id] = INT_MAX; /* Elements */ @@ -740,7 +740,7 @@ static int omap_dma_ch_reg_read(struct omap_dma_s *s, struct omap_dma_channel_s *ch, int reg, uint16_t *value) { switch (reg) { - case 0x00: /* SYS_DMA_CSDP_CH0 */ + case 0x00: /* SYS_DMA_CSDP_CH0 */ *value = (ch->burst[1] << 14) | (ch->pack[1] << 13) | (ch->port[1] << 9) | @@ -750,9 +750,9 @@ static int omap_dma_ch_reg_read(struct omap_dma_s *s, (ch->data_type >> 1); break; - case 0x02: /* SYS_DMA_CCR_CH0 */ + case 0x02: /* SYS_DMA_CCR_CH0 */ if (s->model <= omap_dma_3_1) - *value = 0 << 10; /* FIFO_FLUSH reads as 0 */ + *value = 0 << 10; /* FIFO_FLUSH reads as 0 */ else *value = ch->omap_3_1_compatible_disable << 10; *value |= (ch->mode[1] << 14) | @@ -765,11 +765,11 @@ static int omap_dma_ch_reg_read(struct omap_dma_s *s, (ch->fs << 5) | ch->sync; break; - case 0x04: /* SYS_DMA_CICR_CH0 */ + case 0x04: /* SYS_DMA_CICR_CH0 */ *value = ch->interrupts; break; - case 0x06: /* SYS_DMA_CSR_CH0 */ + case 0x06: /* SYS_DMA_CSR_CH0 */ *value = ch->status; ch->status &= SYNC; if (!ch->omap_3_1_compatible_disable && ch->sibling) { @@ -779,77 +779,77 @@ static int omap_dma_ch_reg_read(struct omap_dma_s *s, qemu_irq_lower(ch->irq); break; - case 0x08: /* SYS_DMA_CSSA_L_CH0 */ + case 0x08: /* SYS_DMA_CSSA_L_CH0 */ *value = ch->addr[0] & 0x0000ffff; break; - case 0x0a: /* SYS_DMA_CSSA_U_CH0 */ + case 0x0a: /* SYS_DMA_CSSA_U_CH0 */ *value = ch->addr[0] >> 16; break; - case 0x0c: /* SYS_DMA_CDSA_L_CH0 */ + case 0x0c: /* SYS_DMA_CDSA_L_CH0 */ *value = ch->addr[1] & 0x0000ffff; break; - case 0x0e: /* SYS_DMA_CDSA_U_CH0 */ + case 0x0e: /* SYS_DMA_CDSA_U_CH0 */ *value = ch->addr[1] >> 16; break; - case 0x10: /* SYS_DMA_CEN_CH0 */ + case 0x10: /* SYS_DMA_CEN_CH0 */ *value = ch->elements; break; - case 0x12: /* SYS_DMA_CFN_CH0 */ + case 0x12: /* SYS_DMA_CFN_CH0 */ *value = ch->frames; break; - case 0x14: /* SYS_DMA_CFI_CH0 */ + case 0x14: /* SYS_DMA_CFI_CH0 */ *value = ch->frame_index[0]; break; - case 0x16: /* SYS_DMA_CEI_CH0 */ + case 0x16: /* SYS_DMA_CEI_CH0 */ *value = ch->element_index[0]; break; - case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */ + case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */ if (ch->omap_3_1_compatible_disable) - *value = ch->active_set.src & 0xffff; /* CSAC */ + *value = ch->active_set.src & 0xffff; /* CSAC */ else *value = ch->cpc; break; - case 0x1a: /* DMA_CDAC */ - *value = ch->active_set.dest & 0xffff; /* CDAC */ + case 0x1a: /* DMA_CDAC */ + *value = ch->active_set.dest & 0xffff; /* CDAC */ break; - case 0x1c: /* DMA_CDEI */ + case 0x1c: /* DMA_CDEI */ *value = ch->element_index[1]; break; - case 0x1e: /* DMA_CDFI */ + case 0x1e: /* DMA_CDFI */ *value = ch->frame_index[1]; break; - case 0x20: /* DMA_COLOR_L */ + case 0x20: /* DMA_COLOR_L */ *value = ch->color & 0xffff; break; - case 0x22: /* DMA_COLOR_U */ + case 0x22: /* DMA_COLOR_U */ *value = ch->color >> 16; break; - case 0x24: /* DMA_CCR2 */ + case 0x24: /* DMA_CCR2 */ *value = (ch->bs << 2) | (ch->transparent_copy << 1) | ch->constant_fill; break; - case 0x28: /* DMA_CLNK_CTRL */ + case 0x28: /* DMA_CLNK_CTRL */ *value = (ch->link_enabled << 15) | (ch->link_next_ch & 0xf); break; - case 0x2a: /* DMA_LCH_CTRL */ + case 0x2a: /* DMA_LCH_CTRL */ *value = (ch->interleave_disabled << 15) | ch->type; break; @@ -864,7 +864,7 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s, struct omap_dma_channel_s *ch, int reg, uint16_t value) { switch (reg) { - case 0x00: /* SYS_DMA_CSDP_CH0 */ + case 0x00: /* SYS_DMA_CSDP_CH0 */ ch->burst[1] = (value & 0xc000) >> 14; ch->pack[1] = (value & 0x2000) >> 13; ch->port[1] = (enum omap_dma_port) ((value & 0x1e00) >> 9); @@ -887,7 +887,7 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s, } break; - case 0x02: /* SYS_DMA_CCR_CH0 */ + case 0x02: /* SYS_DMA_CCR_CH0 */ ch->mode[1] = (omap_dma_addressing_t) ((value & 0xc000) >> 14); ch->mode[0] = (omap_dma_addressing_t) ((value & 0x3000) >> 12); ch->end_prog = (value & 0x0800) >> 11; @@ -909,88 +909,88 @@ static int omap_dma_ch_reg_write(struct omap_dma_s *s, break; - case 0x04: /* SYS_DMA_CICR_CH0 */ + case 0x04: /* SYS_DMA_CICR_CH0 */ ch->interrupts = value & 0x3f; break; - case 0x06: /* SYS_DMA_CSR_CH0 */ + case 0x06: /* SYS_DMA_CSR_CH0 */ OMAP_RO_REG((hwaddr) reg); break; - case 0x08: /* SYS_DMA_CSSA_L_CH0 */ + case 0x08: /* SYS_DMA_CSSA_L_CH0 */ ch->addr[0] &= 0xffff0000; ch->addr[0] |= value; break; - case 0x0a: /* SYS_DMA_CSSA_U_CH0 */ + case 0x0a: /* SYS_DMA_CSSA_U_CH0 */ ch->addr[0] &= 0x0000ffff; ch->addr[0] |= (uint32_t) value << 16; break; - case 0x0c: /* SYS_DMA_CDSA_L_CH0 */ + case 0x0c: /* SYS_DMA_CDSA_L_CH0 */ ch->addr[1] &= 0xffff0000; ch->addr[1] |= value; break; - case 0x0e: /* SYS_DMA_CDSA_U_CH0 */ + case 0x0e: /* SYS_DMA_CDSA_U_CH0 */ ch->addr[1] &= 0x0000ffff; ch->addr[1] |= (uint32_t) value << 16; break; - case 0x10: /* SYS_DMA_CEN_CH0 */ + case 0x10: /* SYS_DMA_CEN_CH0 */ ch->elements = value; break; - case 0x12: /* SYS_DMA_CFN_CH0 */ + case 0x12: /* SYS_DMA_CFN_CH0 */ ch->frames = value; break; - case 0x14: /* SYS_DMA_CFI_CH0 */ + case 0x14: /* SYS_DMA_CFI_CH0 */ ch->frame_index[0] = (int16_t) value; break; - case 0x16: /* SYS_DMA_CEI_CH0 */ + case 0x16: /* SYS_DMA_CEI_CH0 */ ch->element_index[0] = (int16_t) value; break; - case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */ + case 0x18: /* SYS_DMA_CPC_CH0 or DMA_CSAC */ OMAP_RO_REG((hwaddr) reg); break; - case 0x1c: /* DMA_CDEI */ + case 0x1c: /* DMA_CDEI */ ch->element_index[1] = (int16_t) value; break; - case 0x1e: /* DMA_CDFI */ + case 0x1e: /* DMA_CDFI */ ch->frame_index[1] = (int16_t) value; break; - case 0x20: /* DMA_COLOR_L */ + case 0x20: /* DMA_COLOR_L */ ch->color &= 0xffff0000; ch->color |= value; break; - case 0x22: /* DMA_COLOR_U */ + case 0x22: /* DMA_COLOR_U */ ch->color &= 0xffff; ch->color |= (uint32_t)value << 16; break; - case 0x24: /* DMA_CCR2 */ + case 0x24: /* DMA_CCR2 */ ch->bs = (value >> 2) & 0x1; ch->transparent_copy = (value >> 1) & 0x1; ch->constant_fill = value & 0x1; break; - case 0x28: /* DMA_CLNK_CTRL */ + case 0x28: /* DMA_CLNK_CTRL */ ch->link_enabled = (value >> 15) & 0x1; - if (value & (1 << 14)) { /* Stop_Lnk */ + if (value & (1 << 14)) { /* Stop_Lnk */ ch->link_enabled = 0; omap_dma_disable_channel(s, ch); } ch->link_next_ch = value & 0x1f; break; - case 0x2a: /* DMA_LCH_CTRL */ + case 0x2a: /* DMA_LCH_CTRL */ ch->interleave_disabled = (value >> 15) & 0x1; ch->type = value & 0xf; break; @@ -1005,7 +1005,7 @@ static int omap_dma_3_2_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, uint16_t value) { switch (offset) { - case 0xbc0: /* DMA_LCD_CSDP */ + case 0xbc0: /* DMA_LCD_CSDP */ s->brust_f2 = (value >> 14) & 0x3; s->pack_f2 = (value >> 13) & 0x1; s->data_type_f2 = (1 << ((value >> 11) & 0x3)); @@ -1014,7 +1014,7 @@ static int omap_dma_3_2_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, s->data_type_f1 = (1 << ((value >> 0) & 0x3)); break; - case 0xbc2: /* DMA_LCD_CCR */ + case 0xbc2: /* DMA_LCD_CCR */ s->mode_f2 = (value >> 14) & 0x3; s->mode_f1 = (value >> 12) & 0x3; s->end_prog = (value >> 11) & 0x1; @@ -1026,7 +1026,7 @@ static int omap_dma_3_2_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, s->bs = (value >> 4) & 0x1; break; - case 0xbc4: /* DMA_LCD_CTRL */ + case 0xbc4: /* DMA_LCD_CTRL */ s->dst = (value >> 8) & 0x1; s->src = ((value >> 6) & 0x3) << 1; s->condition = 0; @@ -1035,91 +1035,91 @@ static int omap_dma_3_2_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, s->dual = value & 1; break; - case 0xbc8: /* TOP_B1_L */ + case 0xbc8: /* TOP_B1_L */ s->src_f1_top &= 0xffff0000; s->src_f1_top |= 0x0000ffff & value; break; - case 0xbca: /* TOP_B1_U */ + case 0xbca: /* TOP_B1_U */ s->src_f1_top &= 0x0000ffff; s->src_f1_top |= (uint32_t)value << 16; break; - case 0xbcc: /* BOT_B1_L */ + case 0xbcc: /* BOT_B1_L */ s->src_f1_bottom &= 0xffff0000; s->src_f1_bottom |= 0x0000ffff & value; break; - case 0xbce: /* BOT_B1_U */ + case 0xbce: /* BOT_B1_U */ s->src_f1_bottom &= 0x0000ffff; s->src_f1_bottom |= (uint32_t) value << 16; break; - case 0xbd0: /* TOP_B2_L */ + case 0xbd0: /* TOP_B2_L */ s->src_f2_top &= 0xffff0000; s->src_f2_top |= 0x0000ffff & value; break; - case 0xbd2: /* TOP_B2_U */ + case 0xbd2: /* TOP_B2_U */ s->src_f2_top &= 0x0000ffff; s->src_f2_top |= (uint32_t) value << 16; break; - case 0xbd4: /* BOT_B2_L */ + case 0xbd4: /* BOT_B2_L */ s->src_f2_bottom &= 0xffff0000; s->src_f2_bottom |= 0x0000ffff & value; break; - case 0xbd6: /* BOT_B2_U */ + case 0xbd6: /* BOT_B2_U */ s->src_f2_bottom &= 0x0000ffff; s->src_f2_bottom |= (uint32_t) value << 16; break; - case 0xbd8: /* DMA_LCD_SRC_EI_B1 */ + case 0xbd8: /* DMA_LCD_SRC_EI_B1 */ s->element_index_f1 = value; break; - case 0xbda: /* DMA_LCD_SRC_FI_B1_L */ + case 0xbda: /* DMA_LCD_SRC_FI_B1_L */ s->frame_index_f1 &= 0xffff0000; s->frame_index_f1 |= 0x0000ffff & value; break; - case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */ + case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */ s->frame_index_f1 &= 0x0000ffff; s->frame_index_f1 |= (uint32_t) value << 16; break; - case 0xbdc: /* DMA_LCD_SRC_EI_B2 */ + case 0xbdc: /* DMA_LCD_SRC_EI_B2 */ s->element_index_f2 = value; break; - case 0xbde: /* DMA_LCD_SRC_FI_B2_L */ + case 0xbde: /* DMA_LCD_SRC_FI_B2_L */ s->frame_index_f2 &= 0xffff0000; s->frame_index_f2 |= 0x0000ffff & value; break; - case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */ + case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */ s->frame_index_f2 &= 0x0000ffff; s->frame_index_f2 |= (uint32_t) value << 16; break; - case 0xbe0: /* DMA_LCD_SRC_EN_B1 */ + case 0xbe0: /* DMA_LCD_SRC_EN_B1 */ s->elements_f1 = value; break; - case 0xbe4: /* DMA_LCD_SRC_FN_B1 */ + case 0xbe4: /* DMA_LCD_SRC_FN_B1 */ s->frames_f1 = value; break; - case 0xbe2: /* DMA_LCD_SRC_EN_B2 */ + case 0xbe2: /* DMA_LCD_SRC_EN_B2 */ s->elements_f2 = value; break; - case 0xbe6: /* DMA_LCD_SRC_FN_B2 */ + case 0xbe6: /* DMA_LCD_SRC_FN_B2 */ s->frames_f2 = value; break; - case 0xbea: /* DMA_LCD_LCH_CTRL */ + case 0xbea: /* DMA_LCD_LCH_CTRL */ s->lch_type = value & 0xf; break; @@ -1133,7 +1133,7 @@ static int omap_dma_3_2_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, uint16_t *ret) { switch (offset) { - case 0xbc0: /* DMA_LCD_CSDP */ + case 0xbc0: /* DMA_LCD_CSDP */ *ret = (s->brust_f2 << 14) | (s->pack_f2 << 13) | ((s->data_type_f2 >> 1) << 11) | @@ -1142,7 +1142,7 @@ static int omap_dma_3_2_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, ((s->data_type_f1 >> 1) << 0); break; - case 0xbc2: /* DMA_LCD_CCR */ + case 0xbc2: /* DMA_LCD_CCR */ *ret = (s->mode_f2 << 14) | (s->mode_f1 << 12) | (s->end_prog << 11) | @@ -1154,7 +1154,7 @@ static int omap_dma_3_2_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, (s->bs << 4); break; - case 0xbc4: /* DMA_LCD_CTRL */ + case 0xbc4: /* DMA_LCD_CTRL */ qemu_irq_lower(s->irq); *ret = (s->dst << 8) | ((s->src & 0x6) << 5) | @@ -1163,79 +1163,79 @@ static int omap_dma_3_2_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, s->dual; break; - case 0xbc8: /* TOP_B1_L */ + case 0xbc8: /* TOP_B1_L */ *ret = s->src_f1_top & 0xffff; break; - case 0xbca: /* TOP_B1_U */ + case 0xbca: /* TOP_B1_U */ *ret = s->src_f1_top >> 16; break; - case 0xbcc: /* BOT_B1_L */ + case 0xbcc: /* BOT_B1_L */ *ret = s->src_f1_bottom & 0xffff; break; - case 0xbce: /* BOT_B1_U */ + case 0xbce: /* BOT_B1_U */ *ret = s->src_f1_bottom >> 16; break; - case 0xbd0: /* TOP_B2_L */ + case 0xbd0: /* TOP_B2_L */ *ret = s->src_f2_top & 0xffff; break; - case 0xbd2: /* TOP_B2_U */ + case 0xbd2: /* TOP_B2_U */ *ret = s->src_f2_top >> 16; break; - case 0xbd4: /* BOT_B2_L */ + case 0xbd4: /* BOT_B2_L */ *ret = s->src_f2_bottom & 0xffff; break; - case 0xbd6: /* BOT_B2_U */ + case 0xbd6: /* BOT_B2_U */ *ret = s->src_f2_bottom >> 16; break; - case 0xbd8: /* DMA_LCD_SRC_EI_B1 */ + case 0xbd8: /* DMA_LCD_SRC_EI_B1 */ *ret = s->element_index_f1; break; - case 0xbda: /* DMA_LCD_SRC_FI_B1_L */ + case 0xbda: /* DMA_LCD_SRC_FI_B1_L */ *ret = s->frame_index_f1 & 0xffff; break; - case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */ + case 0xbf4: /* DMA_LCD_SRC_FI_B1_U */ *ret = s->frame_index_f1 >> 16; break; - case 0xbdc: /* DMA_LCD_SRC_EI_B2 */ + case 0xbdc: /* DMA_LCD_SRC_EI_B2 */ *ret = s->element_index_f2; break; - case 0xbde: /* DMA_LCD_SRC_FI_B2_L */ + case 0xbde: /* DMA_LCD_SRC_FI_B2_L */ *ret = s->frame_index_f2 & 0xffff; break; - case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */ + case 0xbf6: /* DMA_LCD_SRC_FI_B2_U */ *ret = s->frame_index_f2 >> 16; break; - case 0xbe0: /* DMA_LCD_SRC_EN_B1 */ + case 0xbe0: /* DMA_LCD_SRC_EN_B1 */ *ret = s->elements_f1; break; - case 0xbe4: /* DMA_LCD_SRC_FN_B1 */ + case 0xbe4: /* DMA_LCD_SRC_FN_B1 */ *ret = s->frames_f1; break; - case 0xbe2: /* DMA_LCD_SRC_EN_B2 */ + case 0xbe2: /* DMA_LCD_SRC_EN_B2 */ *ret = s->elements_f2; break; - case 0xbe6: /* DMA_LCD_SRC_FN_B2 */ + case 0xbe6: /* DMA_LCD_SRC_FN_B2 */ *ret = s->frames_f2; break; - case 0xbea: /* DMA_LCD_LCH_CTRL */ + case 0xbea: /* DMA_LCD_LCH_CTRL */ *ret = s->lch_type; break; @@ -1249,7 +1249,7 @@ static int omap_dma_3_1_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, uint16_t value) { switch (offset) { - case 0x300: /* SYS_DMA_LCD_CTRL */ + case 0x300: /* SYS_DMA_LCD_CTRL */ s->src = (value & 0x40) ? imif : emiff; s->condition = 0; /* Assume no bus errors and thus no BUS_ERROR irq bits. */ @@ -1257,42 +1257,42 @@ static int omap_dma_3_1_lcd_write(struct omap_dma_lcd_channel_s *s, int offset, s->dual = value & 1; break; - case 0x302: /* SYS_DMA_LCD_TOP_F1_L */ + case 0x302: /* SYS_DMA_LCD_TOP_F1_L */ s->src_f1_top &= 0xffff0000; s->src_f1_top |= 0x0000ffff & value; break; - case 0x304: /* SYS_DMA_LCD_TOP_F1_U */ + case 0x304: /* SYS_DMA_LCD_TOP_F1_U */ s->src_f1_top &= 0x0000ffff; s->src_f1_top |= (uint32_t)value << 16; break; - case 0x306: /* SYS_DMA_LCD_BOT_F1_L */ + case 0x306: /* SYS_DMA_LCD_BOT_F1_L */ s->src_f1_bottom &= 0xffff0000; s->src_f1_bottom |= 0x0000ffff & value; break; - case 0x308: /* SYS_DMA_LCD_BOT_F1_U */ + case 0x308: /* SYS_DMA_LCD_BOT_F1_U */ s->src_f1_bottom &= 0x0000ffff; s->src_f1_bottom |= (uint32_t)value << 16; break; - case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */ + case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */ s->src_f2_top &= 0xffff0000; s->src_f2_top |= 0x0000ffff & value; break; - case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */ + case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */ s->src_f2_top &= 0x0000ffff; s->src_f2_top |= (uint32_t)value << 16; break; - case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */ + case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */ s->src_f2_bottom &= 0xffff0000; s->src_f2_bottom |= 0x0000ffff & value; break; - case 0x310: /* SYS_DMA_LCD_BOT_F2_U */ + case 0x310: /* SYS_DMA_LCD_BOT_F2_U */ s->src_f2_bottom &= 0x0000ffff; s->src_f2_bottom |= (uint32_t)value << 16; break; @@ -1309,7 +1309,7 @@ static int omap_dma_3_1_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, int i; switch (offset) { - case 0x300: /* SYS_DMA_LCD_CTRL */ + case 0x300: /* SYS_DMA_LCD_CTRL */ i = s->condition; s->condition = 0; qemu_irq_lower(s->irq); @@ -1317,35 +1317,35 @@ static int omap_dma_3_1_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, (s->interrupts << 1) | s->dual; break; - case 0x302: /* SYS_DMA_LCD_TOP_F1_L */ + case 0x302: /* SYS_DMA_LCD_TOP_F1_L */ *ret = s->src_f1_top & 0xffff; break; - case 0x304: /* SYS_DMA_LCD_TOP_F1_U */ + case 0x304: /* SYS_DMA_LCD_TOP_F1_U */ *ret = s->src_f1_top >> 16; break; - case 0x306: /* SYS_DMA_LCD_BOT_F1_L */ + case 0x306: /* SYS_DMA_LCD_BOT_F1_L */ *ret = s->src_f1_bottom & 0xffff; break; - case 0x308: /* SYS_DMA_LCD_BOT_F1_U */ + case 0x308: /* SYS_DMA_LCD_BOT_F1_U */ *ret = s->src_f1_bottom >> 16; break; - case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */ + case 0x30a: /* SYS_DMA_LCD_TOP_F2_L */ *ret = s->src_f2_top & 0xffff; break; - case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */ + case 0x30c: /* SYS_DMA_LCD_TOP_F2_U */ *ret = s->src_f2_top >> 16; break; - case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */ + case 0x30e: /* SYS_DMA_LCD_BOT_F2_L */ *ret = s->src_f2_bottom & 0xffff; break; - case 0x310: /* SYS_DMA_LCD_BOT_F2_U */ + case 0x310: /* SYS_DMA_LCD_BOT_F2_U */ *ret = s->src_f2_bottom >> 16; break; @@ -1358,18 +1358,18 @@ static int omap_dma_3_1_lcd_read(struct omap_dma_lcd_channel_s *s, int offset, static int omap_dma_sys_write(struct omap_dma_s *s, int offset, uint16_t value) { switch (offset) { - case 0x400: /* SYS_DMA_GCR */ + case 0x400: /* SYS_DMA_GCR */ s->gcr = value; break; - case 0x404: /* DMA_GSCR */ + case 0x404: /* DMA_GSCR */ if (value & 0x8) omap_dma_disable_3_1_mapping(s); else omap_dma_enable_3_1_mapping(s); break; - case 0x408: /* DMA_GRST */ + case 0x408: /* DMA_GRST */ if (value & 0x1) omap_dma_reset(s->dma); break; @@ -1384,57 +1384,57 @@ static int omap_dma_sys_read(struct omap_dma_s *s, int offset, uint16_t *ret) { switch (offset) { - case 0x400: /* SYS_DMA_GCR */ + case 0x400: /* SYS_DMA_GCR */ *ret = s->gcr; break; - case 0x404: /* DMA_GSCR */ + case 0x404: /* DMA_GSCR */ *ret = s->omap_3_1_mapping_disabled << 3; break; - case 0x408: /* DMA_GRST */ + case 0x408: /* DMA_GRST */ *ret = 0; break; - case 0x442: /* DMA_HW_ID */ - case 0x444: /* DMA_PCh2_ID */ - case 0x446: /* DMA_PCh0_ID */ - case 0x448: /* DMA_PCh1_ID */ - case 0x44a: /* DMA_PChG_ID */ - case 0x44c: /* DMA_PChD_ID */ + case 0x442: /* DMA_HW_ID */ + case 0x444: /* DMA_PCh2_ID */ + case 0x446: /* DMA_PCh0_ID */ + case 0x448: /* DMA_PCh1_ID */ + case 0x44a: /* DMA_PChG_ID */ + case 0x44c: /* DMA_PChD_ID */ *ret = 1; break; - case 0x44e: /* DMA_CAPS_0_U */ + case 0x44e: /* DMA_CAPS_0_U */ *ret = (s->caps[0] >> 16) & 0xffff; break; - case 0x450: /* DMA_CAPS_0_L */ + case 0x450: /* DMA_CAPS_0_L */ *ret = (s->caps[0] >> 0) & 0xffff; break; - case 0x452: /* DMA_CAPS_1_U */ + case 0x452: /* DMA_CAPS_1_U */ *ret = (s->caps[1] >> 16) & 0xffff; break; - case 0x454: /* DMA_CAPS_1_L */ + case 0x454: /* DMA_CAPS_1_L */ *ret = (s->caps[1] >> 0) & 0xffff; break; - case 0x456: /* DMA_CAPS_2 */ + case 0x456: /* DMA_CAPS_2 */ *ret = s->caps[2]; break; - case 0x458: /* DMA_CAPS_3 */ + case 0x458: /* DMA_CAPS_3 */ *ret = s->caps[3]; break; - case 0x45a: /* DMA_CAPS_4 */ + case 0x45a: /* DMA_CAPS_4 */ *ret = s->caps[4]; break; - case 0x460: /* DMA_PCh2_SR */ - case 0x480: /* DMA_PCh0_SR */ - case 0x482: /* DMA_PCh1_SR */ - case 0x4c0: /* DMA_PChD_SR_0 */ + case 0x460: /* DMA_PCh2_SR */ + case 0x480: /* DMA_PCh0_SR */ + case 0x482: /* DMA_PCh1_SR */ + case 0x4c0: /* DMA_PChD_SR_0 */ qemu_log_mask(LOG_UNIMP, "%s: Physical Channel Status Registers not implemented\n", __func__); @@ -1582,38 +1582,38 @@ static void omap_dma_setcaps(struct omap_dma_s *s) case omap_dma_3_2: /* XXX Only available for sDMA */ s->caps[0] = - (1 << 19) | /* Constant Fill Capability */ - (1 << 18); /* Transparent BLT Capability */ + (1 << 19) | /* Constant Fill Capability */ + (1 << 18); /* Transparent BLT Capability */ s->caps[1] = - (1 << 1); /* 1-bit palettized capability (DMA 3.2 only) */ + (1 << 1); /* 1-bit palettized capability (DMA 3.2 only) */ s->caps[2] = - (1 << 8) | /* SEPARATE_SRC_AND_DST_INDEX_CPBLTY */ - (1 << 7) | /* DST_DOUBLE_INDEX_ADRS_CPBLTY */ - (1 << 6) | /* DST_SINGLE_INDEX_ADRS_CPBLTY */ - (1 << 5) | /* DST_POST_INCRMNT_ADRS_CPBLTY */ - (1 << 4) | /* DST_CONST_ADRS_CPBLTY */ - (1 << 3) | /* SRC_DOUBLE_INDEX_ADRS_CPBLTY */ - (1 << 2) | /* SRC_SINGLE_INDEX_ADRS_CPBLTY */ - (1 << 1) | /* SRC_POST_INCRMNT_ADRS_CPBLTY */ - (1 << 0); /* SRC_CONST_ADRS_CPBLTY */ + (1 << 8) | /* SEPARATE_SRC_AND_DST_INDEX_CPBLTY */ + (1 << 7) | /* DST_DOUBLE_INDEX_ADRS_CPBLTY */ + (1 << 6) | /* DST_SINGLE_INDEX_ADRS_CPBLTY */ + (1 << 5) | /* DST_POST_INCRMNT_ADRS_CPBLTY */ + (1 << 4) | /* DST_CONST_ADRS_CPBLTY */ + (1 << 3) | /* SRC_DOUBLE_INDEX_ADRS_CPBLTY */ + (1 << 2) | /* SRC_SINGLE_INDEX_ADRS_CPBLTY */ + (1 << 1) | /* SRC_POST_INCRMNT_ADRS_CPBLTY */ + (1 << 0); /* SRC_CONST_ADRS_CPBLTY */ s->caps[3] = - (1 << 6) | /* BLOCK_SYNCHR_CPBLTY (DMA 4 only) */ - (1 << 7) | /* PKT_SYNCHR_CPBLTY (DMA 4 only) */ - (1 << 5) | /* CHANNEL_CHAINING_CPBLTY */ - (1 << 4) | /* LCh_INTERLEAVE_CPBLTY */ - (1 << 3) | /* AUTOINIT_REPEAT_CPBLTY (DMA 3.2 only) */ - (1 << 2) | /* AUTOINIT_ENDPROG_CPBLTY (DMA 3.2 only) */ - (1 << 1) | /* FRAME_SYNCHR_CPBLTY */ - (1 << 0); /* ELMNT_SYNCHR_CPBLTY */ + (1 << 6) | /* BLOCK_SYNCHR_CPBLTY (DMA 4 only) */ + (1 << 7) | /* PKT_SYNCHR_CPBLTY (DMA 4 only) */ + (1 << 5) | /* CHANNEL_CHAINING_CPBLTY */ + (1 << 4) | /* LCh_INTERLEAVE_CPBLTY */ + (1 << 3) | /* AUTOINIT_REPEAT_CPBLTY (DMA 3.2 only) */ + (1 << 2) | /* AUTOINIT_ENDPROG_CPBLTY (DMA 3.2 only) */ + (1 << 1) | /* FRAME_SYNCHR_CPBLTY */ + (1 << 0); /* ELMNT_SYNCHR_CPBLTY */ s->caps[4] = - (1 << 7) | /* PKT_INTERRUPT_CPBLTY (DMA 4 only) */ - (1 << 6) | /* SYNC_STATUS_CPBLTY */ - (1 << 5) | /* BLOCK_INTERRUPT_CPBLTY */ - (1 << 4) | /* LAST_FRAME_INTERRUPT_CPBLTY */ - (1 << 3) | /* FRAME_INTERRUPT_CPBLTY */ - (1 << 2) | /* HALF_FRAME_INTERRUPT_CPBLTY */ - (1 << 1) | /* EVENT_DROP_INTERRUPT_CPBLTY */ - (1 << 0); /* TIMEOUT_INTERRUPT_CPBLTY (DMA 3.2 only) */ + (1 << 7) | /* PKT_INTERRUPT_CPBLTY (DMA 4 only) */ + (1 << 6) | /* SYNC_STATUS_CPBLTY */ + (1 << 5) | /* BLOCK_INTERRUPT_CPBLTY */ + (1 << 4) | /* LAST_FRAME_INTERRUPT_CPBLTY */ + (1 << 3) | /* FRAME_INTERRUPT_CPBLTY */ + (1 << 2) | /* HALF_FRAME_INTERRUPT_CPBLTY */ + (1 << 1) | /* EVENT_DROP_INTERRUPT_CPBLTY */ + (1 << 0); /* TIMEOUT_INTERRUPT_CPBLTY (DMA 3.2 only) */ break; } } diff --git a/hw/gpio/omap_gpio.c b/hw/gpio/omap_gpio.c index 61ea7862af..f27806b774 100644 --- a/hw/gpio/omap_gpio.c +++ b/hw/gpio/omap_gpio.c @@ -80,25 +80,25 @@ static uint64_t omap_gpio_read(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* DATA_INPUT */ + case 0x00: /* DATA_INPUT */ return s->inputs & s->pins; - case 0x04: /* DATA_OUTPUT */ + case 0x04: /* DATA_OUTPUT */ return s->outputs; - case 0x08: /* DIRECTION_CONTROL */ + case 0x08: /* DIRECTION_CONTROL */ return s->dir; - case 0x0c: /* INTERRUPT_CONTROL */ + case 0x0c: /* INTERRUPT_CONTROL */ return s->edge; - case 0x10: /* INTERRUPT_MASK */ + case 0x10: /* INTERRUPT_MASK */ return s->mask; - case 0x14: /* INTERRUPT_STATUS */ + case 0x14: /* INTERRUPT_STATUS */ return s->ints; - case 0x18: /* PIN_CONTROL (not in OMAP310) */ + case 0x18: /* PIN_CONTROL (not in OMAP310) */ OMAP_BAD_REG(addr); return s->pins; } @@ -121,11 +121,11 @@ static void omap_gpio_write(void *opaque, hwaddr addr, } switch (offset) { - case 0x00: /* DATA_INPUT */ + case 0x00: /* DATA_INPUT */ OMAP_RO_REG(addr); return; - case 0x04: /* DATA_OUTPUT */ + case 0x04: /* DATA_OUTPUT */ diff = (s->outputs ^ value) & ~s->dir; s->outputs = value; while ((ln = ctz32(diff)) != 32) { @@ -135,7 +135,7 @@ static void omap_gpio_write(void *opaque, hwaddr addr, } break; - case 0x08: /* DIRECTION_CONTROL */ + case 0x08: /* DIRECTION_CONTROL */ diff = s->outputs & (s->dir ^ value); s->dir = value; @@ -147,21 +147,21 @@ static void omap_gpio_write(void *opaque, hwaddr addr, } break; - case 0x0c: /* INTERRUPT_CONTROL */ + case 0x0c: /* INTERRUPT_CONTROL */ s->edge = value; break; - case 0x10: /* INTERRUPT_MASK */ + case 0x10: /* INTERRUPT_MASK */ s->mask = value; break; - case 0x14: /* INTERRUPT_STATUS */ + case 0x14: /* INTERRUPT_STATUS */ s->ints &= ~value; if (!s->ints) qemu_irq_lower(s->irq); break; - case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */ + case 0x18: /* PIN_CONTROL (not in OMAP310 TRM) */ OMAP_BAD_REG(addr); s->pins = value; break; diff --git a/hw/i2c/omap_i2c.c b/hw/i2c/omap_i2c.c index 2e45266e74..751bf748fd 100644 --- a/hw/i2c/omap_i2c.c +++ b/hw/i2c/omap_i2c.c @@ -55,16 +55,16 @@ struct OMAPI2CState { uint16_t test; }; -#define OMAP2_INTR_REV 0x34 -#define OMAP2_GC_REV 0x34 +#define OMAP2_INTR_REV 0x34 +#define OMAP2_GC_REV 0x34 static void omap_i2c_interrupts_update(OMAPI2CState *s) { qemu_set_irq(s->irq, s->stat & s->mask); - if ((s->dma >> 15) & 1) /* RDMA_EN */ - qemu_set_irq(s->drq[0], (s->stat >> 3) & 1); /* RRDY */ - if ((s->dma >> 7) & 1) /* XDMA_EN */ - qemu_set_irq(s->drq[1], (s->stat >> 4) & 1); /* XRDY */ + if ((s->dma >> 15) & 1) /* RDMA_EN */ + qemu_set_irq(s->drq[0], (s->stat >> 3) & 1); /* RRDY */ + if ((s->dma >> 7) & 1) /* XDMA_EN */ + qemu_set_irq(s->drq[1], (s->stat >> 4) & 1); /* XRDY */ } static void omap_i2c_fifo_run(OMAPI2CState *s) @@ -74,25 +74,25 @@ static void omap_i2c_fifo_run(OMAPI2CState *s) if (!i2c_bus_busy(s->bus)) return; - if ((s->control >> 2) & 1) { /* RM */ - if ((s->control >> 1) & 1) { /* STP */ + if ((s->control >> 2) & 1) { /* RM */ + if ((s->control >> 1) & 1) { /* STP */ i2c_end_transfer(s->bus); - s->control &= ~(1 << 1); /* STP */ + s->control &= ~(1 << 1); /* STP */ s->count_cur = s->count; s->txlen = 0; - } else if ((s->control >> 9) & 1) { /* TRX */ + } else if ((s->control >> 9) & 1) { /* TRX */ while (ack && s->txlen) ack = (i2c_send(s->bus, (s->fifo >> ((-- s->txlen) << 3)) & 0xff) >= 0); - s->stat |= 1 << 4; /* XRDY */ + s->stat |= 1 << 4; /* XRDY */ } else { while (s->rxlen < 4) s->fifo |= i2c_recv(s->bus) << ((s->rxlen ++) << 3); - s->stat |= 1 << 3; /* RRDY */ + s->stat |= 1 << 3; /* RRDY */ } } else { - if ((s->control >> 9) & 1) { /* TRX */ + if ((s->control >> 9) & 1) { /* TRX */ while (ack && s->count_cur && s->txlen) { ack = (i2c_send(s->bus, (s->fifo >> ((-- s->txlen) << 3)) & @@ -100,12 +100,12 @@ static void omap_i2c_fifo_run(OMAPI2CState *s) s->count_cur --; } if (ack && s->count_cur) - s->stat |= 1 << 4; /* XRDY */ + s->stat |= 1 << 4; /* XRDY */ else - s->stat &= ~(1 << 4); /* XRDY */ + s->stat &= ~(1 << 4); /* XRDY */ if (!s->count_cur) { - s->stat |= 1 << 2; /* ARDY */ - s->control &= ~(1 << 10); /* MST */ + s->stat |= 1 << 2; /* ARDY */ + s->control &= ~(1 << 10); /* MST */ } } else { while (s->count_cur && s->rxlen < 4) { @@ -113,26 +113,26 @@ static void omap_i2c_fifo_run(OMAPI2CState *s) s->count_cur --; } if (s->rxlen) - s->stat |= 1 << 3; /* RRDY */ + s->stat |= 1 << 3; /* RRDY */ else - s->stat &= ~(1 << 3); /* RRDY */ + s->stat &= ~(1 << 3); /* RRDY */ } if (!s->count_cur) { - if ((s->control >> 1) & 1) { /* STP */ + if ((s->control >> 1) & 1) { /* STP */ i2c_end_transfer(s->bus); - s->control &= ~(1 << 1); /* STP */ + s->control &= ~(1 << 1); /* STP */ s->count_cur = s->count; s->txlen = 0; } else { - s->stat |= 1 << 2; /* ARDY */ - s->control &= ~(1 << 10); /* MST */ + s->stat |= 1 << 2; /* ARDY */ + s->control &= ~(1 << 10); /* MST */ } } } - s->stat |= (!ack) << 1; /* NACK */ + s->stat |= (!ack) << 1; /* NACK */ if (!ack) - s->control &= ~(1 << 1); /* STP */ + s->control &= ~(1 << 1); /* STP */ } static void omap_i2c_reset(DeviceState *dev) @@ -163,16 +163,16 @@ static uint32_t omap_i2c_read(void *opaque, hwaddr addr) uint16_t ret; switch (offset) { - case 0x00: /* I2C_REV */ - return s->revision; /* REV */ + case 0x00: /* I2C_REV */ + return s->revision; /* REV */ - case 0x04: /* I2C_IE */ + case 0x04: /* I2C_IE */ return s->mask; - case 0x08: /* I2C_STAT */ + case 0x08: /* I2C_STAT */ return s->stat | (i2c_bus_busy(s->bus) << 12); - case 0x0c: /* I2C_IV */ + case 0x0c: /* I2C_IV */ if (s->revision >= OMAP2_INTR_REV) break; ret = ctz32(s->stat & s->mask); @@ -185,18 +185,18 @@ static uint32_t omap_i2c_read(void *opaque, hwaddr addr) omap_i2c_interrupts_update(s); return ret; - case 0x10: /* I2C_SYSS */ - return (s->control >> 15) & 1; /* I2C_EN */ + case 0x10: /* I2C_SYSS */ + return (s->control >> 15) & 1; /* I2C_EN */ - case 0x14: /* I2C_BUF */ + case 0x14: /* I2C_BUF */ return s->dma; - case 0x18: /* I2C_CNT */ - return s->count_cur; /* DCOUNT */ + case 0x18: /* I2C_CNT */ + return s->count_cur; /* DCOUNT */ - case 0x1c: /* I2C_DATA */ + case 0x1c: /* I2C_DATA */ ret = 0; - if (s->control & (1 << 14)) { /* BE */ + if (s->control & (1 << 14)) { /* BE */ ret |= ((s->fifo >> 0) & 0xff) << 8; ret |= ((s->fifo >> 8) & 0xff) << 0; } else { @@ -204,7 +204,7 @@ static uint32_t omap_i2c_read(void *opaque, hwaddr addr) ret |= ((s->fifo >> 0) & 0xff) << 0; } if (s->rxlen == 1) { - s->stat |= 1 << 15; /* SBD */ + s->stat |= 1 << 15; /* SBD */ s->rxlen = 0; } else if (s->rxlen > 1) { if (s->rxlen > 2) @@ -214,41 +214,41 @@ static uint32_t omap_i2c_read(void *opaque, hwaddr addr) /* XXX: remote access (qualifier) error - what's that? */ } if (!s->rxlen) { - s->stat &= ~(1 << 3); /* RRDY */ - if (((s->control >> 10) & 1) && /* MST */ - ((~s->control >> 9) & 1)) { /* TRX */ - s->stat |= 1 << 2; /* ARDY */ - s->control &= ~(1 << 10); /* MST */ + s->stat &= ~(1 << 3); /* RRDY */ + if (((s->control >> 10) & 1) && /* MST */ + ((~s->control >> 9) & 1)) { /* TRX */ + s->stat |= 1 << 2; /* ARDY */ + s->control &= ~(1 << 10); /* MST */ } } - s->stat &= ~(1 << 11); /* ROVR */ + s->stat &= ~(1 << 11); /* ROVR */ omap_i2c_fifo_run(s); omap_i2c_interrupts_update(s); return ret; - case 0x20: /* I2C_SYSC */ + case 0x20: /* I2C_SYSC */ return 0; - case 0x24: /* I2C_CON */ + case 0x24: /* I2C_CON */ return s->control; - case 0x28: /* I2C_OA */ + case 0x28: /* I2C_OA */ return s->addr[0]; - case 0x2c: /* I2C_SA */ + case 0x2c: /* I2C_SA */ return s->addr[1]; - case 0x30: /* I2C_PSC */ + case 0x30: /* I2C_PSC */ return s->divider; - case 0x34: /* I2C_SCLL */ + case 0x34: /* I2C_SCLL */ return s->times[0]; - case 0x38: /* I2C_SCLH */ + case 0x38: /* I2C_SCLH */ return s->times[1]; - case 0x3c: /* I2C_SYSTEST */ - if (s->test & (1 << 15)) { /* ST_EN */ + case 0x3c: /* I2C_SYSTEST */ + if (s->test & (1 << 15)) { /* ST_EN */ s->test ^= 0xa; return s->test; } else @@ -267,17 +267,17 @@ static void omap_i2c_write(void *opaque, hwaddr addr, int nack; switch (offset) { - case 0x00: /* I2C_REV */ - case 0x0c: /* I2C_IV */ - case 0x10: /* I2C_SYSS */ + case 0x00: /* I2C_REV */ + case 0x0c: /* I2C_IV */ + case 0x10: /* I2C_SYSS */ OMAP_RO_REG(addr); return; - case 0x04: /* I2C_IE */ + case 0x04: /* I2C_IE */ s->mask = value & (s->revision < OMAP2_GC_REV ? 0x1f : 0x3f); break; - case 0x08: /* I2C_STAT */ + case 0x08: /* I2C_STAT */ if (s->revision < OMAP2_INTR_REV) { OMAP_RO_REG(addr); return; @@ -288,40 +288,40 @@ static void omap_i2c_write(void *opaque, hwaddr addr, omap_i2c_interrupts_update(s); break; - case 0x14: /* I2C_BUF */ + case 0x14: /* I2C_BUF */ s->dma = value & 0x8080; - if (value & (1 << 15)) /* RDMA_EN */ - s->mask &= ~(1 << 3); /* RRDY_IE */ - if (value & (1 << 7)) /* XDMA_EN */ - s->mask &= ~(1 << 4); /* XRDY_IE */ + if (value & (1 << 15)) /* RDMA_EN */ + s->mask &= ~(1 << 3); /* RRDY_IE */ + if (value & (1 << 7)) /* XDMA_EN */ + s->mask &= ~(1 << 4); /* XRDY_IE */ break; - case 0x18: /* I2C_CNT */ - s->count = value; /* DCOUNT */ + case 0x18: /* I2C_CNT */ + s->count = value; /* DCOUNT */ break; - case 0x1c: /* I2C_DATA */ + case 0x1c: /* I2C_DATA */ if (s->txlen > 2) { /* XXX: remote access (qualifier) error - what's that? */ break; } s->fifo <<= 16; s->txlen += 2; - if (s->control & (1 << 14)) { /* BE */ + if (s->control & (1 << 14)) { /* BE */ s->fifo |= ((value >> 8) & 0xff) << 8; s->fifo |= ((value >> 0) & 0xff) << 0; } else { s->fifo |= ((value >> 0) & 0xff) << 8; s->fifo |= ((value >> 8) & 0xff) << 0; } - s->stat &= ~(1 << 10); /* XUDF */ + s->stat &= ~(1 << 10); /* XUDF */ if (s->txlen > 2) - s->stat &= ~(1 << 4); /* XRDY */ + s->stat &= ~(1 << 4); /* XRDY */ omap_i2c_fifo_run(s); omap_i2c_interrupts_update(s); break; - case 0x20: /* I2C_SYSC */ + case 0x20: /* I2C_SYSC */ if (s->revision < OMAP2_INTR_REV) { OMAP_BAD_REG(addr); return; @@ -332,9 +332,9 @@ static void omap_i2c_write(void *opaque, hwaddr addr, } break; - case 0x24: /* I2C_CON */ + case 0x24: /* I2C_CON */ s->control = value & 0xcf87; - if (~value & (1 << 15)) { /* I2C_EN */ + if (~value & (1 << 15)) { /* I2C_EN */ if (s->revision < OMAP2_INTR_REV) { omap_i2c_reset(DEVICE(s)); } @@ -351,14 +351,14 @@ static void omap_i2c_write(void *opaque, hwaddr addr, __func__); break; } - if ((value & (1 << 15)) && value & (1 << 0)) { /* STT */ - nack = !!i2c_start_transfer(s->bus, s->addr[1], /* SA */ - (~value >> 9) & 1); /* TRX */ - s->stat |= nack << 1; /* NACK */ - s->control &= ~(1 << 0); /* STT */ + if ((value & (1 << 15)) && value & (1 << 0)) { /* STT */ + nack = !!i2c_start_transfer(s->bus, s->addr[1], /* SA */ + (~value >> 9) & 1); /* TRX */ + s->stat |= nack << 1; /* NACK */ + s->control &= ~(1 << 0); /* STT */ s->fifo = 0; if (nack) - s->control &= ~(1 << 1); /* STP */ + s->control &= ~(1 << 1); /* STP */ else { s->count_cur = s->count; omap_i2c_fifo_run(s); @@ -367,34 +367,34 @@ static void omap_i2c_write(void *opaque, hwaddr addr, } break; - case 0x28: /* I2C_OA */ + case 0x28: /* I2C_OA */ s->addr[0] = value & 0x3ff; break; - case 0x2c: /* I2C_SA */ + case 0x2c: /* I2C_SA */ s->addr[1] = value & 0x3ff; break; - case 0x30: /* I2C_PSC */ + case 0x30: /* I2C_PSC */ s->divider = value; break; - case 0x34: /* I2C_SCLL */ + case 0x34: /* I2C_SCLL */ s->times[0] = value; break; - case 0x38: /* I2C_SCLH */ + case 0x38: /* I2C_SCLH */ s->times[1] = value; break; - case 0x3c: /* I2C_SYSTEST */ + case 0x3c: /* I2C_SYSTEST */ s->test = value & 0xf80f; - if (value & (1 << 11)) /* SBB */ + if (value & (1 << 11)) /* SBB */ if (s->revision >= OMAP2_INTR_REV) { s->stat |= 0x3f; omap_i2c_interrupts_update(s); } - if (value & (1 << 15)) { /* ST_EN */ + if (value & (1 << 15)) { /* ST_EN */ qemu_log_mask(LOG_UNIMP, "%s: System Test not supported\n", __func__); } @@ -413,7 +413,7 @@ static void omap_i2c_writeb(void *opaque, hwaddr addr, int offset = addr & OMAP_MPUI_REG_MASK; switch (offset) { - case 0x1c: /* I2C_DATA */ + case 0x1c: /* I2C_DATA */ if (s->txlen > 2) { /* XXX: remote access (qualifier) error - what's that? */ break; @@ -421,9 +421,9 @@ static void omap_i2c_writeb(void *opaque, hwaddr addr, s->fifo <<= 8; s->txlen += 1; s->fifo |= value & 0xff; - s->stat &= ~(1 << 10); /* XUDF */ + s->stat &= ~(1 << 10); /* XUDF */ if (s->txlen > 2) - s->stat &= ~(1 << 4); /* XRDY */ + s->stat &= ~(1 << 4); /* XRDY */ omap_i2c_fifo_run(s); omap_i2c_interrupts_update(s); break; diff --git a/hw/intc/omap_intc.c b/hw/intc/omap_intc.c index 9e8737be33..c61158bddd 100644 --- a/hw/intc/omap_intc.c +++ b/hw/intc/omap_intc.c @@ -102,8 +102,8 @@ static inline void omap_inth_update(OMAPIntcState *s, int is_fiq) } } -#define INT_FALLING_EDGE 0 -#define INT_LOW_LEVEL 1 +#define INT_FALLING_EDGE 0 +#define INT_LOW_LEVEL 1 static void omap_set_intr(void *opaque, int irq, int req) { @@ -142,13 +142,13 @@ static uint64_t omap_inth_read(void *opaque, hwaddr addr, offset &= 0xff; switch (offset) { - case 0x00: /* ITR */ + case 0x00: /* ITR */ return bank->irqs; - case 0x04: /* MIR */ + case 0x04: /* MIR */ return bank->mask; - case 0x10: /* SIR_IRQ_CODE */ + case 0x10: /* SIR_IRQ_CODE */ case 0x14: /* SIR_FIQ_CODE */ if (bank_no != 0) break; @@ -159,49 +159,49 @@ static uint64_t omap_inth_read(void *opaque, hwaddr addr, bank->irqs &= ~(1 << i); return line_no; - case 0x18: /* CONTROL_REG */ + case 0x18: /* CONTROL_REG */ if (bank_no != 0) break; return 0; - case 0x1c: /* ILR0 */ - case 0x20: /* ILR1 */ - case 0x24: /* ILR2 */ - case 0x28: /* ILR3 */ - case 0x2c: /* ILR4 */ - case 0x30: /* ILR5 */ - case 0x34: /* ILR6 */ - case 0x38: /* ILR7 */ - case 0x3c: /* ILR8 */ - case 0x40: /* ILR9 */ - case 0x44: /* ILR10 */ - case 0x48: /* ILR11 */ - case 0x4c: /* ILR12 */ - case 0x50: /* ILR13 */ - case 0x54: /* ILR14 */ - case 0x58: /* ILR15 */ - case 0x5c: /* ILR16 */ - case 0x60: /* ILR17 */ - case 0x64: /* ILR18 */ - case 0x68: /* ILR19 */ - case 0x6c: /* ILR20 */ - case 0x70: /* ILR21 */ - case 0x74: /* ILR22 */ - case 0x78: /* ILR23 */ - case 0x7c: /* ILR24 */ - case 0x80: /* ILR25 */ - case 0x84: /* ILR26 */ - case 0x88: /* ILR27 */ - case 0x8c: /* ILR28 */ - case 0x90: /* ILR29 */ - case 0x94: /* ILR30 */ - case 0x98: /* ILR31 */ + case 0x1c: /* ILR0 */ + case 0x20: /* ILR1 */ + case 0x24: /* ILR2 */ + case 0x28: /* ILR3 */ + case 0x2c: /* ILR4 */ + case 0x30: /* ILR5 */ + case 0x34: /* ILR6 */ + case 0x38: /* ILR7 */ + case 0x3c: /* ILR8 */ + case 0x40: /* ILR9 */ + case 0x44: /* ILR10 */ + case 0x48: /* ILR11 */ + case 0x4c: /* ILR12 */ + case 0x50: /* ILR13 */ + case 0x54: /* ILR14 */ + case 0x58: /* ILR15 */ + case 0x5c: /* ILR16 */ + case 0x60: /* ILR17 */ + case 0x64: /* ILR18 */ + case 0x68: /* ILR19 */ + case 0x6c: /* ILR20 */ + case 0x70: /* ILR21 */ + case 0x74: /* ILR22 */ + case 0x78: /* ILR23 */ + case 0x7c: /* ILR24 */ + case 0x80: /* ILR25 */ + case 0x84: /* ILR26 */ + case 0x88: /* ILR27 */ + case 0x8c: /* ILR28 */ + case 0x90: /* ILR29 */ + case 0x94: /* ILR30 */ + case 0x98: /* ILR31 */ i = (offset - 0x1c) >> 2; return (bank->priority[i] << 2) | (((bank->sens_edge >> i) & 1) << 1) | ((bank->fiq >> i) & 1); - case 0x9c: /* ISR */ + case 0x9c: /* ISR */ return 0x00000000; } @@ -219,24 +219,24 @@ static void omap_inth_write(void *opaque, hwaddr addr, offset &= 0xff; switch (offset) { - case 0x00: /* ITR */ + case 0x00: /* ITR */ /* Important: ignore the clearing if the IRQ is level-triggered and the input bit is 1 */ bank->irqs &= value | (bank->inputs & bank->sens_edge); return; - case 0x04: /* MIR */ + case 0x04: /* MIR */ bank->mask = value; omap_inth_update(s, 0); omap_inth_update(s, 1); return; - case 0x10: /* SIR_IRQ_CODE */ - case 0x14: /* SIR_FIQ_CODE */ + case 0x10: /* SIR_IRQ_CODE */ + case 0x14: /* SIR_FIQ_CODE */ OMAP_RO_REG(addr); break; - case 0x18: /* CONTROL_REG */ + case 0x18: /* CONTROL_REG */ if (bank_no != 0) break; if (value & 2) { @@ -251,38 +251,38 @@ static void omap_inth_write(void *opaque, hwaddr addr, } return; - case 0x1c: /* ILR0 */ - case 0x20: /* ILR1 */ - case 0x24: /* ILR2 */ - case 0x28: /* ILR3 */ - case 0x2c: /* ILR4 */ - case 0x30: /* ILR5 */ - case 0x34: /* ILR6 */ - case 0x38: /* ILR7 */ - case 0x3c: /* ILR8 */ - case 0x40: /* ILR9 */ - case 0x44: /* ILR10 */ - case 0x48: /* ILR11 */ - case 0x4c: /* ILR12 */ - case 0x50: /* ILR13 */ - case 0x54: /* ILR14 */ - case 0x58: /* ILR15 */ - case 0x5c: /* ILR16 */ - case 0x60: /* ILR17 */ - case 0x64: /* ILR18 */ - case 0x68: /* ILR19 */ - case 0x6c: /* ILR20 */ - case 0x70: /* ILR21 */ - case 0x74: /* ILR22 */ - case 0x78: /* ILR23 */ - case 0x7c: /* ILR24 */ - case 0x80: /* ILR25 */ - case 0x84: /* ILR26 */ - case 0x88: /* ILR27 */ - case 0x8c: /* ILR28 */ - case 0x90: /* ILR29 */ - case 0x94: /* ILR30 */ - case 0x98: /* ILR31 */ + case 0x1c: /* ILR0 */ + case 0x20: /* ILR1 */ + case 0x24: /* ILR2 */ + case 0x28: /* ILR3 */ + case 0x2c: /* ILR4 */ + case 0x30: /* ILR5 */ + case 0x34: /* ILR6 */ + case 0x38: /* ILR7 */ + case 0x3c: /* ILR8 */ + case 0x40: /* ILR9 */ + case 0x44: /* ILR10 */ + case 0x48: /* ILR11 */ + case 0x4c: /* ILR12 */ + case 0x50: /* ILR13 */ + case 0x54: /* ILR14 */ + case 0x58: /* ILR15 */ + case 0x5c: /* ILR16 */ + case 0x60: /* ILR17 */ + case 0x64: /* ILR18 */ + case 0x68: /* ILR19 */ + case 0x6c: /* ILR20 */ + case 0x70: /* ILR21 */ + case 0x74: /* ILR22 */ + case 0x78: /* ILR23 */ + case 0x7c: /* ILR24 */ + case 0x80: /* ILR25 */ + case 0x84: /* ILR26 */ + case 0x88: /* ILR27 */ + case 0x8c: /* ILR28 */ + case 0x90: /* ILR29 */ + case 0x94: /* ILR30 */ + case 0x98: /* ILR31 */ i = (offset - 0x1c) >> 2; bank->priority[i] = (value >> 2) & 0x1f; bank->sens_edge &= ~(1 << i); @@ -291,7 +291,7 @@ static void omap_inth_write(void *opaque, hwaddr addr, bank->fiq |= (value & 1) << i; return; - case 0x9c: /* ISR */ + case 0x9c: /* ISR */ for (i = 0; i < 32; i ++) if (value & (1 << i)) { omap_set_intr(s, 32 * bank_no + i, 1); diff --git a/hw/misc/omap_clk.c b/hw/misc/omap_clk.c index 0157c9be75..da95c4ace5 100644 --- a/hw/misc/omap_clk.c +++ b/hw/misc/omap_clk.c @@ -30,170 +30,170 @@ struct clk { struct clk *parent; struct clk *child1; struct clk *sibling; -#define ALWAYS_ENABLED (1 << 0) -#define CLOCK_IN_OMAP310 (1 << 10) -#define CLOCK_IN_OMAP730 (1 << 11) -#define CLOCK_IN_OMAP1510 (1 << 12) -#define CLOCK_IN_OMAP16XX (1 << 13) +#define ALWAYS_ENABLED (1 << 0) +#define CLOCK_IN_OMAP310 (1 << 10) +#define CLOCK_IN_OMAP730 (1 << 11) +#define CLOCK_IN_OMAP1510 (1 << 12) +#define CLOCK_IN_OMAP16XX (1 << 13) uint32_t flags; int id; - int running; /* Is currently ticking */ - int enabled; /* Is enabled, regardless of its input clk */ - unsigned long rate; /* Current rate (if .running) */ - unsigned int divisor; /* Rate relative to input (if .enabled) */ - unsigned int multiplier; /* Rate relative to input (if .enabled) */ - qemu_irq users[16]; /* Who to notify on change */ - int usecount; /* Automatically idle when unused */ + int running; /* Is currently ticking */ + int enabled; /* Is enabled, regardless of its input clk */ + unsigned long rate; /* Current rate (if .running) */ + unsigned int divisor; /* Rate relative to input (if .enabled) */ + unsigned int multiplier; /* Rate relative to input (if .enabled) */ + qemu_irq users[16]; /* Who to notify on change */ + int usecount; /* Automatically idle when unused */ }; static struct clk xtal_osc12m = { - .name = "xtal_osc_12m", - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "xtal_osc_12m", + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk xtal_osc32k = { - .name = "xtal_osc_32k", - .rate = 32768, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "xtal_osc_32k", + .rate = 32768, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk ck_ref = { - .name = "ck_ref", - .alias = "clkin", - .parent = &xtal_osc12m, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "ck_ref", + .alias = "clkin", + .parent = &xtal_osc12m, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; /* If a dpll is disabled it becomes a bypass, child clocks don't stop */ static struct clk dpll1 = { - .name = "dpll1", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "dpll1", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk dpll2 = { - .name = "dpll2", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP310 | ALWAYS_ENABLED, + .name = "dpll2", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk dpll3 = { - .name = "dpll3", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP310 | ALWAYS_ENABLED, + .name = "dpll3", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk dpll4 = { - .name = "dpll4", - .parent = &ck_ref, - .multiplier = 4, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "dpll4", + .parent = &ck_ref, + .multiplier = 4, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk apll = { - .name = "apll", - .parent = &ck_ref, - .multiplier = 48, - .divisor = 12, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "apll", + .parent = &ck_ref, + .multiplier = 48, + .divisor = 12, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk ck_48m = { - .name = "ck_48m", - .parent = &dpll4, /* either dpll4 or apll */ - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "ck_48m", + .parent = &dpll4, /* either dpll4 or apll */ + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk ck_dpll1out = { - .name = "ck_dpll1out", - .parent = &dpll1, - .flags = CLOCK_IN_OMAP16XX, + .name = "ck_dpll1out", + .parent = &dpll1, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk sossi_ck = { - .name = "ck_sossi", - .parent = &ck_dpll1out, - .flags = CLOCK_IN_OMAP16XX, + .name = "ck_sossi", + .parent = &ck_dpll1out, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk clkm1 = { - .name = "clkm1", - .alias = "ck_gen1", - .parent = &dpll1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "clkm1", + .alias = "ck_gen1", + .parent = &dpll1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk clkm2 = { - .name = "clkm2", - .alias = "ck_gen2", - .parent = &dpll1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "clkm2", + .alias = "ck_gen2", + .parent = &dpll1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk clkm3 = { - .name = "clkm3", - .alias = "ck_gen3", - .parent = &dpll1, /* either dpll1 or ck_ref */ - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "clkm3", + .alias = "ck_gen3", + .parent = &dpll1, /* either dpll1 or ck_ref */ + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk arm_ck = { - .name = "arm_ck", - .alias = "mpu_ck", - .parent = &clkm1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "arm_ck", + .alias = "mpu_ck", + .parent = &clkm1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk armper_ck = { - .name = "armper_ck", - .alias = "mpuper_ck", - .parent = &clkm1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "armper_ck", + .alias = "mpuper_ck", + .parent = &clkm1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk arm_gpio_ck = { - .name = "arm_gpio_ck", - .alias = "mpu_gpio_ck", - .parent = &clkm1, - .divisor = 1, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, + .name = "arm_gpio_ck", + .alias = "mpu_gpio_ck", + .parent = &clkm1, + .divisor = 1, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; static struct clk armxor_ck = { - .name = "armxor_ck", - .alias = "mpuxor_ck", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "armxor_ck", + .alias = "mpuxor_ck", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk armtim_ck = { - .name = "armtim_ck", - .alias = "mputim_ck", - .parent = &ck_ref, /* either CLKIN or DPLL1 */ - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "armtim_ck", + .alias = "mputim_ck", + .parent = &ck_ref, /* either CLKIN or DPLL1 */ + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk armwdt_ck = { - .name = "armwdt_ck", - .alias = "mpuwd_ck", - .parent = &clkm1, - .divisor = 14, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "armwdt_ck", + .alias = "mpuwd_ck", + .parent = &clkm1, + .divisor = 14, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk arminth_ck16xx = { - .name = "arminth_ck", - .parent = &arm_ck, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .name = "arminth_ck", + .parent = &arm_ck, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, /* Note: On 16xx the frequency can be divided by 2 by programming * ARM_CKCTL:ARM_INTHCK_SEL(14) to 1 * @@ -202,48 +202,48 @@ static struct clk arminth_ck16xx = { }; static struct clk dsp_ck = { - .name = "dsp_ck", - .parent = &clkm2, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "dsp_ck", + .parent = &clkm2, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk dspmmu_ck = { - .name = "dspmmu_ck", - .parent = &clkm2, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + .name = "dspmmu_ck", + .parent = &clkm2, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, }; static struct clk dspper_ck = { - .name = "dspper_ck", - .parent = &clkm2, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "dspper_ck", + .parent = &clkm2, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk dspxor_ck = { - .name = "dspxor_ck", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "dspxor_ck", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk dsptim_ck = { - .name = "dsptim_ck", - .parent = &ck_ref, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "dsptim_ck", + .parent = &ck_ref, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk tc_ck = { - .name = "tc_ck", - .parent = &clkm3, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + .name = "tc_ck", + .parent = &clkm3, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk arminth_ck15xx = { - .name = "arminth_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, + .name = "arminth_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, /* Note: On 1510 the frequency follows TC_CK * * 16xx version is in MPU clocks. @@ -252,259 +252,259 @@ static struct clk arminth_ck15xx = { static struct clk tipb_ck = { /* No-idle controlled by "tc_ck" */ - .name = "tipb_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, + .name = "tipb_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk l3_ocpi_ck = { /* No-idle controlled by "tc_ck" */ - .name = "l3_ocpi_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, + .name = "l3_ocpi_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk tc1_ck = { - .name = "tc1_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, + .name = "tc1_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk tc2_ck = { - .name = "tc2_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX, + .name = "tc2_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk dma_ck = { /* No-idle controlled by "tc_ck" */ - .name = "dma_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .name = "dma_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk dma_lcdfree_ck = { - .name = "dma_lcdfree_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .name = "dma_lcdfree_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, }; static struct clk api_ck = { - .name = "api_ck", - .alias = "mpui_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .name = "api_ck", + .alias = "mpui_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk lb_ck = { - .name = "lb_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, + .name = "lb_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; static struct clk lbfree_ck = { - .name = "lbfree_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, + .name = "lbfree_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; static struct clk hsab_ck = { - .name = "hsab_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, + .name = "hsab_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; static struct clk rhea1_ck = { - .name = "rhea1_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .name = "rhea1_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, }; static struct clk rhea2_ck = { - .name = "rhea2_ck", - .parent = &tc_ck, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .name = "rhea2_ck", + .parent = &tc_ck, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, }; static struct clk lcd_ck_16xx = { - .name = "lcd_ck", - .parent = &clkm3, - .flags = CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730, + .name = "lcd_ck", + .parent = &clkm3, + .flags = CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP730, }; static struct clk lcd_ck_1510 = { - .name = "lcd_ck", - .parent = &clkm3, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, + .name = "lcd_ck", + .parent = &clkm3, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; static struct clk uart1_1510 = { - .name = "uart1_ck", + .name = "uart1_ck", /* Direct from ULPD, no real parent */ - .parent = &armper_ck, /* either armper_ck or dpll4 */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, + .parent = &armper_ck, /* either armper_ck or dpll4 */ + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk uart1_16xx = { - .name = "uart1_ck", + .name = "uart1_ck", /* Direct from ULPD, no real parent */ - .parent = &armper_ck, - .rate = 48000000, - .flags = CLOCK_IN_OMAP16XX, + .parent = &armper_ck, + .rate = 48000000, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk uart2_ck = { - .name = "uart2_ck", + .name = "uart2_ck", /* Direct from ULPD, no real parent */ - .parent = &armper_ck, /* either armper_ck or dpll4 */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | + .parent = &armper_ck, /* either armper_ck or dpll4 */ + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk uart3_1510 = { - .name = "uart3_ck", + .name = "uart3_ck", /* Direct from ULPD, no real parent */ - .parent = &armper_ck, /* either armper_ck or dpll4 */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, + .parent = &armper_ck, /* either armper_ck or dpll4 */ + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310 | ALWAYS_ENABLED, }; static struct clk uart3_16xx = { - .name = "uart3_ck", + .name = "uart3_ck", /* Direct from ULPD, no real parent */ - .parent = &armper_ck, - .rate = 48000000, - .flags = CLOCK_IN_OMAP16XX, + .parent = &armper_ck, + .rate = 48000000, + .flags = CLOCK_IN_OMAP16XX, }; -static struct clk usb_clk0 = { /* 6 MHz output on W4_USB_CLK0 */ - .name = "usb_clk0", - .alias = "usb.clko", +static struct clk usb_clk0 = { /* 6 MHz output on W4_USB_CLK0 */ + .name = "usb_clk0", + .alias = "usb.clko", /* Direct from ULPD, no parent */ - .rate = 6000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .rate = 6000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk usb_hhc_ck1510 = { - .name = "usb_hhc_ck", + .name = "usb_hhc_ck", /* Direct from ULPD, no parent */ - .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */ - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, + .rate = 48000000, /* Actually 2 clocks, 12MHz and 48MHz */ + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP310, }; static struct clk usb_hhc_ck16xx = { - .name = "usb_hhc_ck", + .name = "usb_hhc_ck", /* Direct from ULPD, no parent */ - .rate = 48000000, + .rate = 48000000, /* OTG_SYSCON_2.OTG_PADEN == 0 (not 1510-compatible) */ - .flags = CLOCK_IN_OMAP16XX, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk usb_w2fc_mclk = { - .name = "usb_w2fc_mclk", - .alias = "usb_w2fc_ck", - .parent = &ck_48m, - .rate = 48000000, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "usb_w2fc_mclk", + .alias = "usb_w2fc_ck", + .parent = &ck_48m, + .rate = 48000000, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk mclk_1510 = { - .name = "mclk", + .name = "mclk", /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510, + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510, }; static struct clk bclk_310 = { - .name = "bt_mclk_out", /* Alias midi_mclk_out? */ - .parent = &armper_ck, - .flags = CLOCK_IN_OMAP310, + .name = "bt_mclk_out", /* Alias midi_mclk_out? */ + .parent = &armper_ck, + .flags = CLOCK_IN_OMAP310, }; static struct clk mclk_310 = { - .name = "com_mclk_out", - .parent = &armper_ck, - .flags = CLOCK_IN_OMAP310, + .name = "com_mclk_out", + .parent = &armper_ck, + .flags = CLOCK_IN_OMAP310, }; static struct clk mclk_16xx = { - .name = "mclk", + .name = "mclk", /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .flags = CLOCK_IN_OMAP16XX, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk bclk_1510 = { - .name = "bclk", + .name = "bclk", /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .rate = 12000000, - .flags = CLOCK_IN_OMAP1510, + .rate = 12000000, + .flags = CLOCK_IN_OMAP1510, }; static struct clk bclk_16xx = { - .name = "bclk", + .name = "bclk", /* Direct from ULPD, no parent. May be enabled by ext hardware. */ - .flags = CLOCK_IN_OMAP16XX, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk mmc1_ck = { - .name = "mmc_ck", - .id = 1, + .name = "mmc_ck", + .id = 1, /* Functional clock is direct from ULPD, interface clock is ARMPER */ - .parent = &armper_ck, /* either armper_ck or dpll4 */ - .rate = 48000000, - .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, + .parent = &armper_ck, /* either armper_ck or dpll4 */ + .rate = 48000000, + .flags = CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | CLOCK_IN_OMAP310, }; static struct clk mmc2_ck = { - .name = "mmc_ck", - .id = 2, + .name = "mmc_ck", + .id = 2, /* Functional clock is direct from ULPD, interface clock is ARMPER */ - .parent = &armper_ck, - .rate = 48000000, - .flags = CLOCK_IN_OMAP16XX, + .parent = &armper_ck, + .rate = 48000000, + .flags = CLOCK_IN_OMAP16XX, }; static struct clk cam_mclk = { - .name = "cam.mclk", - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, - .rate = 12000000, + .name = "cam.mclk", + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .rate = 12000000, }; static struct clk cam_exclk = { - .name = "cam.exclk", - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "cam.exclk", + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, /* Either 12M from cam.mclk or 48M from dpll4 */ - .parent = &cam_mclk, + .parent = &cam_mclk, }; static struct clk cam_lclk = { - .name = "cam.lclk", - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, + .name = "cam.lclk", + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX, }; static struct clk i2c_fck = { - .name = "i2c_fck", - .id = 1, - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + .name = "i2c_fck", + .id = 1, + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, - .parent = &armxor_ck, + .parent = &armxor_ck, }; static struct clk i2c_ick = { - .name = "i2c_ick", - .id = 1, - .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, - .parent = &armper_ck, + .name = "i2c_ick", + .id = 1, + .flags = CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, + .parent = &armper_ck, }; static struct clk clk32k = { - .name = "clk32-kHz", - .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | + .name = "clk32-kHz", + .flags = CLOCK_IN_OMAP310 | CLOCK_IN_OMAP1510 | CLOCK_IN_OMAP16XX | ALWAYS_ENABLED, - .parent = &xtal_osc32k, + .parent = &xtal_osc32k, }; static struct clk *onchip_clks[] = { diff --git a/hw/timer/pxa2xx_timer.c b/hw/timer/pxa2xx_timer.c index 7a94366b0f..6d4ac31574 100644 --- a/hw/timer/pxa2xx_timer.c +++ b/hw/timer/pxa2xx_timer.c @@ -19,41 +19,41 @@ #include "qom/object.h" #include "system/watchdog.h" -#define OSMR0 0x00 -#define OSMR1 0x04 -#define OSMR2 0x08 -#define OSMR3 0x0c -#define OSMR4 0x80 -#define OSMR5 0x84 -#define OSMR6 0x88 -#define OSMR7 0x8c -#define OSMR8 0x90 -#define OSMR9 0x94 -#define OSMR10 0x98 -#define OSMR11 0x9c -#define OSCR 0x10 /* OS Timer Count */ -#define OSCR4 0x40 -#define OSCR5 0x44 -#define OSCR6 0x48 -#define OSCR7 0x4c -#define OSCR8 0x50 -#define OSCR9 0x54 -#define OSCR10 0x58 -#define OSCR11 0x5c -#define OSSR 0x14 /* Timer status register */ -#define OWER 0x18 -#define OIER 0x1c /* Interrupt enable register 3-0 to E3-E0 */ -#define OMCR4 0xc0 /* OS Match Control registers */ -#define OMCR5 0xc4 -#define OMCR6 0xc8 -#define OMCR7 0xcc -#define OMCR8 0xd0 -#define OMCR9 0xd4 -#define OMCR10 0xd8 -#define OMCR11 0xdc -#define OSNR 0x20 - -#define PXA25X_FREQ 3686400 /* 3.6864 MHz */ +#define OSMR0 0x00 +#define OSMR1 0x04 +#define OSMR2 0x08 +#define OSMR3 0x0c +#define OSMR4 0x80 +#define OSMR5 0x84 +#define OSMR6 0x88 +#define OSMR7 0x8c +#define OSMR8 0x90 +#define OSMR9 0x94 +#define OSMR10 0x98 +#define OSMR11 0x9c +#define OSCR 0x10 /* OS Timer Count */ +#define OSCR4 0x40 +#define OSCR5 0x44 +#define OSCR6 0x48 +#define OSCR7 0x4c +#define OSCR8 0x50 +#define OSCR9 0x54 +#define OSCR10 0x58 +#define OSCR11 0x5c +#define OSSR 0x14 /* Timer status register */ +#define OWER 0x18 +#define OIER 0x1c /* Interrupt enable register 3-0 to E3-E0 */ +#define OMCR4 0xc0 /* OS Match Control registers */ +#define OMCR5 0xc4 +#define OMCR6 0xc8 +#define OMCR7 0xcc +#define OMCR8 0xd0 +#define OMCR9 0xd4 +#define OMCR10 0xd8 +#define OMCR11 0xdc +#define OSNR 0x20 + +#define PXA25X_FREQ 3686400 /* 3.6864 MHz */ static int pxa2xx_timer4_freq[8] = { [0] = 0, @@ -106,7 +106,7 @@ struct PXA2xxTimerInfo { PXA2xxTimer4 tm4[8]; }; -#define PXA2XX_TIMER_HAVE_TM4 0 +#define PXA2XX_TIMER_HAVE_TM4 0 static inline int pxa2xx_timer_has_tm4(PXA2xxTimerInfo *s) { @@ -230,7 +230,7 @@ static uint64_t pxa2xx_timer_read(void *opaque, hwaddr offset, NANOSECONDS_PER_SECOND); case OIER: return s->irq_enabled; - case OSSR: /* Status register */ + case OSSR: /* Status register */ return s->events; case OWER: return s->reset3; @@ -336,7 +336,7 @@ static void pxa2xx_timer_write(void *opaque, hwaddr offset, case OIER: s->irq_enabled = value & 0xfff; break; - case OSSR: /* Status register */ + case OSSR: /* Status register */ value &= s->events; s->events &= ~value; for (i = 0; i < 4; i ++, value >>= 1) @@ -345,7 +345,7 @@ static void pxa2xx_timer_write(void *opaque, hwaddr offset, if (pxa2xx_timer_has_tm4(s) && !(s->events & 0xff0) && value) qemu_irq_lower(s->irq4); break; - case OWER: /* XXX: Reset on OSMR3 match? */ + case OWER: /* XXX: Reset on OSMR3 match? */ s->reset3 = value; break; case OMCR7: tm ++; diff --git a/include/hw/arm/omap.h b/include/hw/arm/omap.h index 6185507373..bdb2e887e4 100644 --- a/include/hw/arm/omap.h +++ b/include/hw/arm/omap.h @@ -25,24 +25,24 @@ #include "qemu/log.h" #include "qom/object.h" -# define OMAP_EMIFS_BASE 0x00000000 -# define OMAP_CS0_BASE 0x00000000 -# define OMAP_CS1_BASE 0x04000000 -# define OMAP_CS2_BASE 0x08000000 -# define OMAP_CS3_BASE 0x0c000000 -# define OMAP_EMIFF_BASE 0x10000000 -# define OMAP_IMIF_BASE 0x20000000 -# define OMAP_LOCALBUS_BASE 0x30000000 -# define OMAP_MPUI_BASE 0xe1000000 - -# define OMAP730_SRAM_SIZE 0x00032000 -# define OMAP15XX_SRAM_SIZE 0x00030000 -# define OMAP16XX_SRAM_SIZE 0x00004000 -# define OMAP1611_SRAM_SIZE 0x0003e800 -# define OMAP_CS0_SIZE 0x04000000 -# define OMAP_CS1_SIZE 0x04000000 -# define OMAP_CS2_SIZE 0x04000000 -# define OMAP_CS3_SIZE 0x04000000 +#define OMAP_EMIFS_BASE 0x00000000 +#define OMAP_CS0_BASE 0x00000000 +#define OMAP_CS1_BASE 0x04000000 +#define OMAP_CS2_BASE 0x08000000 +#define OMAP_CS3_BASE 0x0c000000 +#define OMAP_EMIFF_BASE 0x10000000 +#define OMAP_IMIF_BASE 0x20000000 +#define OMAP_LOCALBUS_BASE 0x30000000 +#define OMAP_MPUI_BASE 0xe1000000 + +#define OMAP730_SRAM_SIZE 0x00032000 +#define OMAP15XX_SRAM_SIZE 0x00030000 +#define OMAP16XX_SRAM_SIZE 0x00004000 +#define OMAP1611_SRAM_SIZE 0x0003e800 +#define OMAP_CS0_SIZE 0x04000000 +#define OMAP_CS1_SIZE 0x04000000 +#define OMAP_CS2_SIZE 0x04000000 +#define OMAP_CS3_SIZE 0x04000000 /* omap_clk.c */ struct omap_mpu_state_s; @@ -103,228 +103,228 @@ void omap_gpio_set_clk(Omap1GpioState *gpio, omap_clk clk); * Common IRQ numbers for level 1 interrupt handler * See /usr/include/asm-arm/arch-omap/irqs.h in Linux. */ -# define OMAP_INT_CAMERA 1 -# define OMAP_INT_FIQ 3 -# define OMAP_INT_RTDX 6 -# define OMAP_INT_DSP_MMU_ABORT 7 -# define OMAP_INT_HOST 8 -# define OMAP_INT_ABORT 9 -# define OMAP_INT_BRIDGE_PRIV 13 -# define OMAP_INT_GPIO_BANK1 14 -# define OMAP_INT_UART3 15 -# define OMAP_INT_TIMER3 16 -# define OMAP_INT_DMA_CH0_6 19 -# define OMAP_INT_DMA_CH1_7 20 -# define OMAP_INT_DMA_CH2_8 21 -# define OMAP_INT_DMA_CH3 22 -# define OMAP_INT_DMA_CH4 23 -# define OMAP_INT_DMA_CH5 24 -# define OMAP_INT_DMA_LCD 25 -# define OMAP_INT_TIMER1 26 -# define OMAP_INT_WD_TIMER 27 -# define OMAP_INT_BRIDGE_PUB 28 -# define OMAP_INT_TIMER2 30 -# define OMAP_INT_LCD_CTRL 31 +#define OMAP_INT_CAMERA 1 +#define OMAP_INT_FIQ 3 +#define OMAP_INT_RTDX 6 +#define OMAP_INT_DSP_MMU_ABORT 7 +#define OMAP_INT_HOST 8 +#define OMAP_INT_ABORT 9 +#define OMAP_INT_BRIDGE_PRIV 13 +#define OMAP_INT_GPIO_BANK1 14 +#define OMAP_INT_UART3 15 +#define OMAP_INT_TIMER3 16 +#define OMAP_INT_DMA_CH0_6 19 +#define OMAP_INT_DMA_CH1_7 20 +#define OMAP_INT_DMA_CH2_8 21 +#define OMAP_INT_DMA_CH3 22 +#define OMAP_INT_DMA_CH4 23 +#define OMAP_INT_DMA_CH5 24 +#define OMAP_INT_DMA_LCD 25 +#define OMAP_INT_TIMER1 26 +#define OMAP_INT_WD_TIMER 27 +#define OMAP_INT_BRIDGE_PUB 28 +#define OMAP_INT_TIMER2 30 +#define OMAP_INT_LCD_CTRL 31 /* * Common OMAP-15xx IRQ numbers for level 1 interrupt handler */ -# define OMAP_INT_15XX_IH2_IRQ 0 -# define OMAP_INT_15XX_LB_MMU 17 -# define OMAP_INT_15XX_LOCAL_BUS 29 +#define OMAP_INT_15XX_IH2_IRQ 0 +#define OMAP_INT_15XX_LB_MMU 17 +#define OMAP_INT_15XX_LOCAL_BUS 29 /* * OMAP-1510 specific IRQ numbers for level 1 interrupt handler */ -# define OMAP_INT_1510_SPI_TX 4 -# define OMAP_INT_1510_SPI_RX 5 -# define OMAP_INT_1510_DSP_MAILBOX1 10 -# define OMAP_INT_1510_DSP_MAILBOX2 11 +#define OMAP_INT_1510_SPI_TX 4 +#define OMAP_INT_1510_SPI_RX 5 +#define OMAP_INT_1510_DSP_MAILBOX1 10 +#define OMAP_INT_1510_DSP_MAILBOX2 11 /* * OMAP-310 specific IRQ numbers for level 1 interrupt handler */ -# define OMAP_INT_310_McBSP2_TX 4 -# define OMAP_INT_310_McBSP2_RX 5 -# define OMAP_INT_310_HSB_MAILBOX1 12 -# define OMAP_INT_310_HSAB_MMU 18 +#define OMAP_INT_310_McBSP2_TX 4 +#define OMAP_INT_310_McBSP2_RX 5 +#define OMAP_INT_310_HSB_MAILBOX1 12 +#define OMAP_INT_310_HSAB_MMU 18 /* * OMAP-1610 specific IRQ numbers for level 1 interrupt handler */ -# define OMAP_INT_1610_IH2_IRQ 0 -# define OMAP_INT_1610_IH2_FIQ 2 -# define OMAP_INT_1610_McBSP2_TX 4 -# define OMAP_INT_1610_McBSP2_RX 5 -# define OMAP_INT_1610_DSP_MAILBOX1 10 -# define OMAP_INT_1610_DSP_MAILBOX2 11 -# define OMAP_INT_1610_LCD_LINE 12 -# define OMAP_INT_1610_GPTIMER1 17 -# define OMAP_INT_1610_GPTIMER2 18 -# define OMAP_INT_1610_SSR_FIFO_0 29 +#define OMAP_INT_1610_IH2_IRQ 0 +#define OMAP_INT_1610_IH2_FIQ 2 +#define OMAP_INT_1610_McBSP2_TX 4 +#define OMAP_INT_1610_McBSP2_RX 5 +#define OMAP_INT_1610_DSP_MAILBOX1 10 +#define OMAP_INT_1610_DSP_MAILBOX2 11 +#define OMAP_INT_1610_LCD_LINE 12 +#define OMAP_INT_1610_GPTIMER1 17 +#define OMAP_INT_1610_GPTIMER2 18 +#define OMAP_INT_1610_SSR_FIFO_0 29 /* * OMAP-730 specific IRQ numbers for level 1 interrupt handler */ -# define OMAP_INT_730_IH2_FIQ 0 -# define OMAP_INT_730_IH2_IRQ 1 -# define OMAP_INT_730_USB_NON_ISO 2 -# define OMAP_INT_730_USB_ISO 3 -# define OMAP_INT_730_ICR 4 -# define OMAP_INT_730_EAC 5 -# define OMAP_INT_730_GPIO_BANK1 6 -# define OMAP_INT_730_GPIO_BANK2 7 -# define OMAP_INT_730_GPIO_BANK3 8 -# define OMAP_INT_730_McBSP2TX 10 -# define OMAP_INT_730_McBSP2RX 11 -# define OMAP_INT_730_McBSP2RX_OVF 12 -# define OMAP_INT_730_LCD_LINE 14 -# define OMAP_INT_730_GSM_PROTECT 15 -# define OMAP_INT_730_TIMER3 16 -# define OMAP_INT_730_GPIO_BANK5 17 -# define OMAP_INT_730_GPIO_BANK6 18 -# define OMAP_INT_730_SPGIO_WR 29 +#define OMAP_INT_730_IH2_FIQ 0 +#define OMAP_INT_730_IH2_IRQ 1 +#define OMAP_INT_730_USB_NON_ISO 2 +#define OMAP_INT_730_USB_ISO 3 +#define OMAP_INT_730_ICR 4 +#define OMAP_INT_730_EAC 5 +#define OMAP_INT_730_GPIO_BANK1 6 +#define OMAP_INT_730_GPIO_BANK2 7 +#define OMAP_INT_730_GPIO_BANK3 8 +#define OMAP_INT_730_McBSP2TX 10 +#define OMAP_INT_730_McBSP2RX 11 +#define OMAP_INT_730_McBSP2RX_OVF 12 +#define OMAP_INT_730_LCD_LINE 14 +#define OMAP_INT_730_GSM_PROTECT 15 +#define OMAP_INT_730_TIMER3 16 +#define OMAP_INT_730_GPIO_BANK5 17 +#define OMAP_INT_730_GPIO_BANK6 18 +#define OMAP_INT_730_SPGIO_WR 29 /* * Common IRQ numbers for level 2 interrupt handler */ -# define OMAP_INT_KEYBOARD 1 -# define OMAP_INT_uWireTX 2 -# define OMAP_INT_uWireRX 3 -# define OMAP_INT_I2C 4 -# define OMAP_INT_MPUIO 5 -# define OMAP_INT_USB_HHC_1 6 -# define OMAP_INT_McBSP3TX 10 -# define OMAP_INT_McBSP3RX 11 -# define OMAP_INT_McBSP1TX 12 -# define OMAP_INT_McBSP1RX 13 -# define OMAP_INT_UART1 14 -# define OMAP_INT_UART2 15 -# define OMAP_INT_USB_W2FC 20 -# define OMAP_INT_1WIRE 21 -# define OMAP_INT_OS_TIMER 22 -# define OMAP_INT_OQN 23 -# define OMAP_INT_GAUGE_32K 24 -# define OMAP_INT_RTC_TIMER 25 -# define OMAP_INT_RTC_ALARM 26 -# define OMAP_INT_DSP_MMU 28 +#define OMAP_INT_KEYBOARD 1 +#define OMAP_INT_uWireTX 2 +#define OMAP_INT_uWireRX 3 +#define OMAP_INT_I2C 4 +#define OMAP_INT_MPUIO 5 +#define OMAP_INT_USB_HHC_1 6 +#define OMAP_INT_McBSP3TX 10 +#define OMAP_INT_McBSP3RX 11 +#define OMAP_INT_McBSP1TX 12 +#define OMAP_INT_McBSP1RX 13 +#define OMAP_INT_UART1 14 +#define OMAP_INT_UART2 15 +#define OMAP_INT_USB_W2FC 20 +#define OMAP_INT_1WIRE 21 +#define OMAP_INT_OS_TIMER 22 +#define OMAP_INT_OQN 23 +#define OMAP_INT_GAUGE_32K 24 +#define OMAP_INT_RTC_TIMER 25 +#define OMAP_INT_RTC_ALARM 26 +#define OMAP_INT_DSP_MMU 28 /* * OMAP-1510 specific IRQ numbers for level 2 interrupt handler */ -# define OMAP_INT_1510_BT_MCSI1TX 16 -# define OMAP_INT_1510_BT_MCSI1RX 17 -# define OMAP_INT_1510_SoSSI_MATCH 19 -# define OMAP_INT_1510_MEM_STICK 27 -# define OMAP_INT_1510_COM_SPI_RO 31 +#define OMAP_INT_1510_BT_MCSI1TX 16 +#define OMAP_INT_1510_BT_MCSI1RX 17 +#define OMAP_INT_1510_SoSSI_MATCH 19 +#define OMAP_INT_1510_MEM_STICK 27 +#define OMAP_INT_1510_COM_SPI_RO 31 /* * OMAP-310 specific IRQ numbers for level 2 interrupt handler */ -# define OMAP_INT_310_FAC 0 -# define OMAP_INT_310_USB_HHC_2 7 -# define OMAP_INT_310_MCSI1_FE 16 -# define OMAP_INT_310_MCSI2_FE 17 -# define OMAP_INT_310_USB_W2FC_ISO 29 -# define OMAP_INT_310_USB_W2FC_NON_ISO 30 -# define OMAP_INT_310_McBSP2RX_OF 31 +#define OMAP_INT_310_FAC 0 +#define OMAP_INT_310_USB_HHC_2 7 +#define OMAP_INT_310_MCSI1_FE 16 +#define OMAP_INT_310_MCSI2_FE 17 +#define OMAP_INT_310_USB_W2FC_ISO 29 +#define OMAP_INT_310_USB_W2FC_NON_ISO 30 +#define OMAP_INT_310_McBSP2RX_OF 31 /* * OMAP-1610 specific IRQ numbers for level 2 interrupt handler */ -# define OMAP_INT_1610_FAC 0 -# define OMAP_INT_1610_USB_HHC_2 7 -# define OMAP_INT_1610_USB_OTG 8 -# define OMAP_INT_1610_SoSSI 9 -# define OMAP_INT_1610_BT_MCSI1TX 16 -# define OMAP_INT_1610_BT_MCSI1RX 17 -# define OMAP_INT_1610_SoSSI_MATCH 19 -# define OMAP_INT_1610_MEM_STICK 27 -# define OMAP_INT_1610_McBSP2RX_OF 31 -# define OMAP_INT_1610_STI 32 -# define OMAP_INT_1610_STI_WAKEUP 33 -# define OMAP_INT_1610_GPTIMER3 34 -# define OMAP_INT_1610_GPTIMER4 35 -# define OMAP_INT_1610_GPTIMER5 36 -# define OMAP_INT_1610_GPTIMER6 37 -# define OMAP_INT_1610_GPTIMER7 38 -# define OMAP_INT_1610_GPTIMER8 39 -# define OMAP_INT_1610_GPIO_BANK2 40 -# define OMAP_INT_1610_GPIO_BANK3 41 -# define OMAP_INT_1610_MMC2 42 -# define OMAP_INT_1610_CF 43 -# define OMAP_INT_1610_WAKE_UP_REQ 46 -# define OMAP_INT_1610_GPIO_BANK4 48 -# define OMAP_INT_1610_SPI 49 -# define OMAP_INT_1610_DMA_CH6 53 -# define OMAP_INT_1610_DMA_CH7 54 -# define OMAP_INT_1610_DMA_CH8 55 -# define OMAP_INT_1610_DMA_CH9 56 -# define OMAP_INT_1610_DMA_CH10 57 -# define OMAP_INT_1610_DMA_CH11 58 -# define OMAP_INT_1610_DMA_CH12 59 -# define OMAP_INT_1610_DMA_CH13 60 -# define OMAP_INT_1610_DMA_CH14 61 -# define OMAP_INT_1610_DMA_CH15 62 -# define OMAP_INT_1610_NAND 63 +#define OMAP_INT_1610_FAC 0 +#define OMAP_INT_1610_USB_HHC_2 7 +#define OMAP_INT_1610_USB_OTG 8 +#define OMAP_INT_1610_SoSSI 9 +#define OMAP_INT_1610_BT_MCSI1TX 16 +#define OMAP_INT_1610_BT_MCSI1RX 17 +#define OMAP_INT_1610_SoSSI_MATCH 19 +#define OMAP_INT_1610_MEM_STICK 27 +#define OMAP_INT_1610_McBSP2RX_OF 31 +#define OMAP_INT_1610_STI 32 +#define OMAP_INT_1610_STI_WAKEUP 33 +#define OMAP_INT_1610_GPTIMER3 34 +#define OMAP_INT_1610_GPTIMER4 35 +#define OMAP_INT_1610_GPTIMER5 36 +#define OMAP_INT_1610_GPTIMER6 37 +#define OMAP_INT_1610_GPTIMER7 38 +#define OMAP_INT_1610_GPTIMER8 39 +#define OMAP_INT_1610_GPIO_BANK2 40 +#define OMAP_INT_1610_GPIO_BANK3 41 +#define OMAP_INT_1610_MMC2 42 +#define OMAP_INT_1610_CF 43 +#define OMAP_INT_1610_WAKE_UP_REQ 46 +#define OMAP_INT_1610_GPIO_BANK4 48 +#define OMAP_INT_1610_SPI 49 +#define OMAP_INT_1610_DMA_CH6 53 +#define OMAP_INT_1610_DMA_CH7 54 +#define OMAP_INT_1610_DMA_CH8 55 +#define OMAP_INT_1610_DMA_CH9 56 +#define OMAP_INT_1610_DMA_CH10 57 +#define OMAP_INT_1610_DMA_CH11 58 +#define OMAP_INT_1610_DMA_CH12 59 +#define OMAP_INT_1610_DMA_CH13 60 +#define OMAP_INT_1610_DMA_CH14 61 +#define OMAP_INT_1610_DMA_CH15 62 +#define OMAP_INT_1610_NAND 63 /* * OMAP-730 specific IRQ numbers for level 2 interrupt handler */ -# define OMAP_INT_730_HW_ERRORS 0 -# define OMAP_INT_730_NFIQ_PWR_FAIL 1 -# define OMAP_INT_730_CFCD 2 -# define OMAP_INT_730_CFIREQ 3 -# define OMAP_INT_730_I2C 4 -# define OMAP_INT_730_PCC 5 -# define OMAP_INT_730_MPU_EXT_NIRQ 6 -# define OMAP_INT_730_SPI_100K_1 7 -# define OMAP_INT_730_SYREN_SPI 8 -# define OMAP_INT_730_VLYNQ 9 -# define OMAP_INT_730_GPIO_BANK4 10 -# define OMAP_INT_730_McBSP1TX 11 -# define OMAP_INT_730_McBSP1RX 12 -# define OMAP_INT_730_McBSP1RX_OF 13 -# define OMAP_INT_730_UART_MODEM_IRDA_2 14 -# define OMAP_INT_730_UART_MODEM_1 15 -# define OMAP_INT_730_MCSI 16 -# define OMAP_INT_730_uWireTX 17 -# define OMAP_INT_730_uWireRX 18 -# define OMAP_INT_730_SMC_CD 19 -# define OMAP_INT_730_SMC_IREQ 20 -# define OMAP_INT_730_HDQ_1WIRE 21 -# define OMAP_INT_730_TIMER32K 22 -# define OMAP_INT_730_MMC_SDIO 23 -# define OMAP_INT_730_UPLD 24 -# define OMAP_INT_730_USB_HHC_1 27 -# define OMAP_INT_730_USB_HHC_2 28 -# define OMAP_INT_730_USB_GENI 29 -# define OMAP_INT_730_USB_OTG 30 -# define OMAP_INT_730_CAMERA_IF 31 -# define OMAP_INT_730_RNG 32 -# define OMAP_INT_730_DUAL_MODE_TIMER 33 -# define OMAP_INT_730_DBB_RF_EN 34 -# define OMAP_INT_730_MPUIO_KEYPAD 35 -# define OMAP_INT_730_SHA1_MD5 36 -# define OMAP_INT_730_SPI_100K_2 37 -# define OMAP_INT_730_RNG_IDLE 38 -# define OMAP_INT_730_MPUIO 39 -# define OMAP_INT_730_LLPC_LCD_CTRL_OFF 40 -# define OMAP_INT_730_LLPC_OE_FALLING 41 -# define OMAP_INT_730_LLPC_OE_RISING 42 -# define OMAP_INT_730_LLPC_VSYNC 43 -# define OMAP_INT_730_WAKE_UP_REQ 46 -# define OMAP_INT_730_DMA_CH6 53 -# define OMAP_INT_730_DMA_CH7 54 -# define OMAP_INT_730_DMA_CH8 55 -# define OMAP_INT_730_DMA_CH9 56 -# define OMAP_INT_730_DMA_CH10 57 -# define OMAP_INT_730_DMA_CH11 58 -# define OMAP_INT_730_DMA_CH12 59 -# define OMAP_INT_730_DMA_CH13 60 -# define OMAP_INT_730_DMA_CH14 61 -# define OMAP_INT_730_DMA_CH15 62 -# define OMAP_INT_730_NAND 63 +#define OMAP_INT_730_HW_ERRORS 0 +#define OMAP_INT_730_NFIQ_PWR_FAIL 1 +#define OMAP_INT_730_CFCD 2 +#define OMAP_INT_730_CFIREQ 3 +#define OMAP_INT_730_I2C 4 +#define OMAP_INT_730_PCC 5 +#define OMAP_INT_730_MPU_EXT_NIRQ 6 +#define OMAP_INT_730_SPI_100K_1 7 +#define OMAP_INT_730_SYREN_SPI 8 +#define OMAP_INT_730_VLYNQ 9 +#define OMAP_INT_730_GPIO_BANK4 10 +#define OMAP_INT_730_McBSP1TX 11 +#define OMAP_INT_730_McBSP1RX 12 +#define OMAP_INT_730_McBSP1RX_OF 13 +#define OMAP_INT_730_UART_MODEM_IRDA_2 14 +#define OMAP_INT_730_UART_MODEM_1 15 +#define OMAP_INT_730_MCSI 16 +#define OMAP_INT_730_uWireTX 17 +#define OMAP_INT_730_uWireRX 18 +#define OMAP_INT_730_SMC_CD 19 +#define OMAP_INT_730_SMC_IREQ 20 +#define OMAP_INT_730_HDQ_1WIRE 21 +#define OMAP_INT_730_TIMER32K 22 +#define OMAP_INT_730_MMC_SDIO 23 +#define OMAP_INT_730_UPLD 24 +#define OMAP_INT_730_USB_HHC_1 27 +#define OMAP_INT_730_USB_HHC_2 28 +#define OMAP_INT_730_USB_GENI 29 +#define OMAP_INT_730_USB_OTG 30 +#define OMAP_INT_730_CAMERA_IF 31 +#define OMAP_INT_730_RNG 32 +#define OMAP_INT_730_DUAL_MODE_TIMER 33 +#define OMAP_INT_730_DBB_RF_EN 34 +#define OMAP_INT_730_MPUIO_KEYPAD 35 +#define OMAP_INT_730_SHA1_MD5 36 +#define OMAP_INT_730_SPI_100K_2 37 +#define OMAP_INT_730_RNG_IDLE 38 +#define OMAP_INT_730_MPUIO 39 +#define OMAP_INT_730_LLPC_LCD_CTRL_OFF 40 +#define OMAP_INT_730_LLPC_OE_FALLING 41 +#define OMAP_INT_730_LLPC_OE_RISING 42 +#define OMAP_INT_730_LLPC_VSYNC 43 +#define OMAP_INT_730_WAKE_UP_REQ 46 +#define OMAP_INT_730_DMA_CH6 53 +#define OMAP_INT_730_DMA_CH7 54 +#define OMAP_INT_730_DMA_CH8 55 +#define OMAP_INT_730_DMA_CH9 56 +#define OMAP_INT_730_DMA_CH10 57 +#define OMAP_INT_730_DMA_CH11 58 +#define OMAP_INT_730_DMA_CH12 59 +#define OMAP_INT_730_DMA_CH13 60 +#define OMAP_INT_730_DMA_CH14 61 +#define OMAP_INT_730_DMA_CH15 62 +#define OMAP_INT_730_NAND 63 /* omap_dma.c */ enum omap_dma_model { @@ -353,9 +353,9 @@ struct dma_irq_map { enum omap_dma_port { emiff = 0, emifs, - imif, /* omap16xx: ocp_t1 */ + imif, /* omap16xx: ocp_t1 */ tipb, - local, /* omap16xx: ocp_t2 */ + local, /* omap16xx: ocp_t2 */ tipb_mpui, __omap_dma_port_last, }; @@ -418,65 +418,65 @@ struct omap_dma_lcd_channel_s { * DMA request numbers for OMAP1 * See /usr/include/asm-arm/arch-omap/dma.h in Linux. */ -# define OMAP_DMA_NO_DEVICE 0 -# define OMAP_DMA_MCSI1_TX 1 -# define OMAP_DMA_MCSI1_RX 2 -# define OMAP_DMA_I2C_RX 3 -# define OMAP_DMA_I2C_TX 4 -# define OMAP_DMA_EXT_NDMA_REQ0 5 -# define OMAP_DMA_EXT_NDMA_REQ1 6 -# define OMAP_DMA_UWIRE_TX 7 -# define OMAP_DMA_MCBSP1_TX 8 -# define OMAP_DMA_MCBSP1_RX 9 -# define OMAP_DMA_MCBSP3_TX 10 -# define OMAP_DMA_MCBSP3_RX 11 -# define OMAP_DMA_UART1_TX 12 -# define OMAP_DMA_UART1_RX 13 -# define OMAP_DMA_UART2_TX 14 -# define OMAP_DMA_UART2_RX 15 -# define OMAP_DMA_MCBSP2_TX 16 -# define OMAP_DMA_MCBSP2_RX 17 -# define OMAP_DMA_UART3_TX 18 -# define OMAP_DMA_UART3_RX 19 -# define OMAP_DMA_CAMERA_IF_RX 20 -# define OMAP_DMA_MMC_TX 21 -# define OMAP_DMA_MMC_RX 22 -# define OMAP_DMA_NAND 23 /* Not in OMAP310 */ -# define OMAP_DMA_IRQ_LCD_LINE 24 /* Not in OMAP310 */ -# define OMAP_DMA_MEMORY_STICK 25 /* Not in OMAP310 */ -# define OMAP_DMA_USB_W2FC_RX0 26 -# define OMAP_DMA_USB_W2FC_RX1 27 -# define OMAP_DMA_USB_W2FC_RX2 28 -# define OMAP_DMA_USB_W2FC_TX0 29 -# define OMAP_DMA_USB_W2FC_TX1 30 -# define OMAP_DMA_USB_W2FC_TX2 31 +#define OMAP_DMA_NO_DEVICE 0 +#define OMAP_DMA_MCSI1_TX 1 +#define OMAP_DMA_MCSI1_RX 2 +#define OMAP_DMA_I2C_RX 3 +#define OMAP_DMA_I2C_TX 4 +#define OMAP_DMA_EXT_NDMA_REQ0 5 +#define OMAP_DMA_EXT_NDMA_REQ1 6 +#define OMAP_DMA_UWIRE_TX 7 +#define OMAP_DMA_MCBSP1_TX 8 +#define OMAP_DMA_MCBSP1_RX 9 +#define OMAP_DMA_MCBSP3_TX 10 +#define OMAP_DMA_MCBSP3_RX 11 +#define OMAP_DMA_UART1_TX 12 +#define OMAP_DMA_UART1_RX 13 +#define OMAP_DMA_UART2_TX 14 +#define OMAP_DMA_UART2_RX 15 +#define OMAP_DMA_MCBSP2_TX 16 +#define OMAP_DMA_MCBSP2_RX 17 +#define OMAP_DMA_UART3_TX 18 +#define OMAP_DMA_UART3_RX 19 +#define OMAP_DMA_CAMERA_IF_RX 20 +#define OMAP_DMA_MMC_TX 21 +#define OMAP_DMA_MMC_RX 22 +#define OMAP_DMA_NAND 23 /* Not in OMAP310 */ +#define OMAP_DMA_IRQ_LCD_LINE 24 /* Not in OMAP310 */ +#define OMAP_DMA_MEMORY_STICK 25 /* Not in OMAP310 */ +#define OMAP_DMA_USB_W2FC_RX0 26 +#define OMAP_DMA_USB_W2FC_RX1 27 +#define OMAP_DMA_USB_W2FC_RX2 28 +#define OMAP_DMA_USB_W2FC_TX0 29 +#define OMAP_DMA_USB_W2FC_TX1 30 +#define OMAP_DMA_USB_W2FC_TX2 31 /* These are only for 1610 */ -# define OMAP_DMA_CRYPTO_DES_IN 32 -# define OMAP_DMA_SPI_TX 33 -# define OMAP_DMA_SPI_RX 34 -# define OMAP_DMA_CRYPTO_HASH 35 -# define OMAP_DMA_CCP_ATTN 36 -# define OMAP_DMA_CCP_FIFO_NOT_EMPTY 37 -# define OMAP_DMA_CMT_APE_TX_CHAN_0 38 -# define OMAP_DMA_CMT_APE_RV_CHAN_0 39 -# define OMAP_DMA_CMT_APE_TX_CHAN_1 40 -# define OMAP_DMA_CMT_APE_RV_CHAN_1 41 -# define OMAP_DMA_CMT_APE_TX_CHAN_2 42 -# define OMAP_DMA_CMT_APE_RV_CHAN_2 43 -# define OMAP_DMA_CMT_APE_TX_CHAN_3 44 -# define OMAP_DMA_CMT_APE_RV_CHAN_3 45 -# define OMAP_DMA_CMT_APE_TX_CHAN_4 46 -# define OMAP_DMA_CMT_APE_RV_CHAN_4 47 -# define OMAP_DMA_CMT_APE_TX_CHAN_5 48 -# define OMAP_DMA_CMT_APE_RV_CHAN_5 49 -# define OMAP_DMA_CMT_APE_TX_CHAN_6 50 -# define OMAP_DMA_CMT_APE_RV_CHAN_6 51 -# define OMAP_DMA_CMT_APE_TX_CHAN_7 52 -# define OMAP_DMA_CMT_APE_RV_CHAN_7 53 -# define OMAP_DMA_MMC2_TX 54 -# define OMAP_DMA_MMC2_RX 55 -# define OMAP_DMA_CRYPTO_DES_OUT 56 +#define OMAP_DMA_CRYPTO_DES_IN 32 +#define OMAP_DMA_SPI_TX 33 +#define OMAP_DMA_SPI_RX 34 +#define OMAP_DMA_CRYPTO_HASH 35 +#define OMAP_DMA_CCP_ATTN 36 +#define OMAP_DMA_CCP_FIFO_NOT_EMPTY 37 +#define OMAP_DMA_CMT_APE_TX_CHAN_0 38 +#define OMAP_DMA_CMT_APE_RV_CHAN_0 39 +#define OMAP_DMA_CMT_APE_TX_CHAN_1 40 +#define OMAP_DMA_CMT_APE_RV_CHAN_1 41 +#define OMAP_DMA_CMT_APE_TX_CHAN_2 42 +#define OMAP_DMA_CMT_APE_RV_CHAN_2 43 +#define OMAP_DMA_CMT_APE_TX_CHAN_3 44 +#define OMAP_DMA_CMT_APE_RV_CHAN_3 45 +#define OMAP_DMA_CMT_APE_TX_CHAN_4 46 +#define OMAP_DMA_CMT_APE_RV_CHAN_4 47 +#define OMAP_DMA_CMT_APE_TX_CHAN_5 48 +#define OMAP_DMA_CMT_APE_RV_CHAN_5 49 +#define OMAP_DMA_CMT_APE_TX_CHAN_6 50 +#define OMAP_DMA_CMT_APE_RV_CHAN_6 51 +#define OMAP_DMA_CMT_APE_TX_CHAN_7 52 +#define OMAP_DMA_CMT_APE_RV_CHAN_7 53 +#define OMAP_DMA_MMC2_TX 54 +#define OMAP_DMA_MMC2_RX 55 +#define OMAP_DMA_CRYPTO_DES_OUT 56 struct omap_uart_s; struct omap_uart_s *omap_uart_init(hwaddr base, @@ -542,14 +542,14 @@ void omap_mmc_set_clk(DeviceState *dev, omap_clk clk); /* omap_i2c.c */ I2CBus *omap_i2c_bus(DeviceState *omap_i2c); -# define cpu_is_omap310(cpu) (cpu->mpu_model == omap310) -# define cpu_is_omap1510(cpu) (cpu->mpu_model == omap1510) -# define cpu_is_omap1610(cpu) (cpu->mpu_model == omap1610) -# define cpu_is_omap1710(cpu) (cpu->mpu_model == omap1710) +#define cpu_is_omap310(cpu) (cpu->mpu_model == omap310) +#define cpu_is_omap1510(cpu) (cpu->mpu_model == omap1510) +#define cpu_is_omap1610(cpu) (cpu->mpu_model == omap1610) +#define cpu_is_omap1710(cpu) (cpu->mpu_model == omap1710) -# define cpu_is_omap15xx(cpu) \ +#define cpu_is_omap15xx(cpu) \ (cpu_is_omap310(cpu) || cpu_is_omap1510(cpu)) -# define cpu_is_omap16xx(cpu) \ +#define cpu_is_omap16xx(cpu) \ (cpu_is_omap1610(cpu) || cpu_is_omap1710(cpu)) struct omap_mpu_state_s { @@ -685,14 +685,14 @@ void omap_badwidth_write32(void *opaque, hwaddr addr, void omap_mpu_wakeup(void *opaque, int irq, int req); -# define OMAP_BAD_REG(paddr) \ +#define OMAP_BAD_REG(paddr) \ qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad register %#08"HWADDR_PRIx"\n", \ __func__, paddr) -# define OMAP_RO_REG(paddr) \ +#define OMAP_RO_REG(paddr) \ qemu_log_mask(LOG_GUEST_ERROR, "%s: Read-only register %#08" \ HWADDR_PRIx "\n", \ __func__, paddr) -# define OMAP_MPUI_REG_MASK 0x000007ff +#define OMAP_MPUI_REG_MASK 0x000007ff #endif diff --git a/include/hw/arm/sharpsl.h b/include/hw/arm/sharpsl.h index e986b28c52..1e3992fcd0 100644 --- a/include/hw/arm/sharpsl.h +++ b/include/hw/arm/sharpsl.h @@ -11,7 +11,7 @@ /* zaurus.c */ -#define SL_PXA_PARAM_BASE 0xa0000a00 +#define SL_PXA_PARAM_BASE 0xa0000a00 void sl_bootparam_write(hwaddr ptr); #endif diff --git a/include/hw/arm/soc_dma.h b/include/hw/arm/soc_dma.h index e93a7499a8..bcdb91425a 100644 --- a/include/hw/arm/soc_dma.h +++ b/include/hw/arm/soc_dma.h @@ -54,7 +54,7 @@ struct soc_dma_ch_s { int bytes; /* Initialised by the DMA module, call soc_dma_ch_update after writing. */ enum soc_dma_access_type type[2]; - hwaddr vaddr[2]; /* Updated by .transfer_fn(). */ + hwaddr vaddr[2]; /* Updated by .transfer_fn(). */ /* Private */ void *paddr[2]; soc_dma_io_t io_fn[2]; @@ -70,7 +70,7 @@ struct soc_dma_ch_s { struct soc_dma_s { /* Following fields are set by the SoC DMA module and can be used * by anybody. */ - uint64_t drqbmp; /* Is zeroed by soc_dma_reset() */ + uint64_t drqbmp; /* Is zeroed by soc_dma_reset() */ qemu_irq *drq; void *opaque; int64_t freq; From 2a952e052ab0dab5dc309f9521e655e1011c92a8 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 14 May 2025 14:29:47 +0100 Subject: [PATCH 0895/2760] MAINTAINERS: Add an entry for the Bananapi machine This machine was still missing from the MAINTAINERS file. Since there is likely no active maintainer around for this machine (I didn't spot any contributions from Qianfan Zhao in the git log after 2023), I'm suggesting Peter as maintainer with status set to "Odd fixes". Signed-off-by: Thomas Huth Message-id: 20250508072706.114278-1-thuth@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- MAINTAINERS | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6dacd6d004..ca0ffc02d4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -732,6 +732,16 @@ F: include/hw/timer/armv7m_systick.h F: include/hw/misc/armv7m_ras.h F: tests/qtest/test-arm-mptimer.c +Bananapi M2U +M: Peter Maydell +L: qemu-arm@nongnu.org +S: Odd Fixes +F: docs/system/arm/bananapi_m2u.rst +F: hw/*/allwinner-r40*.c +F: hw/arm/bananapi_m2u.c +F: include/hw/*/allwinner-r40*.h +F: tests/functional/test_arm_bpim2u.py + B-L475E-IOT01A IoT Node M: Samuel Tardieu L: qemu-arm@nongnu.org From 133edc4f220c9c415f68832e6341f461bf04152a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 11:04:15 -0700 Subject: [PATCH 0896/2760] target/arm: Replace target_ulong -> vaddr for HWBreakpoint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Pierrick Bouvier Reviewed-by: Alex Bennée Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-2-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/hyp_gdbstub.c | 6 +++--- target/arm/internals.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/arm/hyp_gdbstub.c b/target/arm/hyp_gdbstub.c index 0512d67f8c..bb5969720c 100644 --- a/target/arm/hyp_gdbstub.c +++ b/target/arm/hyp_gdbstub.c @@ -54,7 +54,7 @@ GArray *hw_breakpoints, *hw_watchpoints; * here so future PC comparisons will work properly. */ -int insert_hw_breakpoint(target_ulong addr) +int insert_hw_breakpoint(vaddr addr) { HWBreakpoint brk = { .bcr = 0x1, /* BCR E=1, enable */ @@ -80,7 +80,7 @@ int insert_hw_breakpoint(target_ulong addr) * Delete a breakpoint and shuffle any above down */ -int delete_hw_breakpoint(target_ulong pc) +int delete_hw_breakpoint(vaddr pc) { int i; for (i = 0; i < hw_breakpoints->len; i++) { @@ -226,7 +226,7 @@ int delete_hw_watchpoint(vaddr addr, vaddr len, int type) return -ENOENT; } -bool find_hw_breakpoint(CPUState *cpu, target_ulong pc) +bool find_hw_breakpoint(CPUState *cpu, vaddr pc) { int i; diff --git a/target/arm/internals.h b/target/arm/internals.h index 702eb1a548..3360de9150 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1948,9 +1948,9 @@ extern GArray *hw_breakpoints, *hw_watchpoints; #define get_hw_bp(i) (&g_array_index(hw_breakpoints, HWBreakpoint, i)) #define get_hw_wp(i) (&g_array_index(hw_watchpoints, HWWatchpoint, i)) -bool find_hw_breakpoint(CPUState *cpu, target_ulong pc); -int insert_hw_breakpoint(target_ulong pc); -int delete_hw_breakpoint(target_ulong pc); +bool find_hw_breakpoint(CPUState *cpu, vaddr pc); +int insert_hw_breakpoint(vaddr pc); +int delete_hw_breakpoint(vaddr pc); bool check_watchpoint_in_range(int i, vaddr addr); CPUWatchpoint *find_hw_watchpoint(CPUState *cpu, vaddr addr); From af3ca6e7f09d8ed0e8c9a3fff55d7a68b5019033 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:16 -0700 Subject: [PATCH 0897/2760] include/system/hvf: missing vaddr include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On MacOS x86_64: In file included from ../target/i386/hvf/x86_task.c:13: /Users/runner/work/qemu/qemu/include/system/hvf.h:42:5: error: unknown type name 'vaddr' vaddr pc; ^ /Users/runner/work/qemu/qemu/include/system/hvf.h:43:5: error: unknown type name 'vaddr' vaddr saved_insn; ^ /Users/runner/work/qemu/qemu/include/system/hvf.h:45:5: error: type name requires a specifier or qualifier QTAILQ_ENTRY(hvf_sw_breakpoint) entry; ^ /Users/runner/work/qemu/qemu/include/system/hvf.h:45:18: error: a parameter list without types is only allowed in a function definition QTAILQ_ENTRY(hvf_sw_breakpoint) entry; ^ /Users/runner/work/qemu/qemu/include/system/hvf.h:45:36: error: expected ';' at end of declaration list QTAILQ_ENTRY(hvf_sw_breakpoint) entry; Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-3-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- include/system/hvf.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/system/hvf.h b/include/system/hvf.h index 7b45a2e198..a9a502f0c8 100644 --- a/include/system/hvf.h +++ b/include/system/hvf.h @@ -17,6 +17,7 @@ #include "qemu/queue.h" #include "exec/vaddr.h" #include "qom/object.h" +#include "exec/vaddr.h" #ifdef COMPILING_PER_TARGET # ifdef CONFIG_HVF From b2bb3f3576e5dc99218607dde09e25ac0e55693c Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:17 -0700 Subject: [PATCH 0898/2760] meson: add common libs for target and target_system Following what we did for hw/, we need target specific common libraries for target. We need 2 different libraries: - code common to a base architecture - system code common to a base architecture For user code, it can stay compiled per target for now. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-4-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- meson.build | 78 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 61 insertions(+), 17 deletions(-) diff --git a/meson.build b/meson.build index 7f91500bb7..ad2053f968 100644 --- a/meson.build +++ b/meson.build @@ -3709,6 +3709,8 @@ target_arch = {} target_system_arch = {} target_user_arch = {} hw_common_arch = {} +target_common_arch = {} +target_common_system_arch = {} # NOTE: the trace/ subdirectory needs the qapi_trace_events variable # that is filled in by qapi/. @@ -4107,29 +4109,59 @@ common_all = static_library('common', # construct common libraries per base architecture hw_common_arch_libs = {} +target_common_arch_libs = {} +target_common_system_arch_libs = {} foreach target : target_dirs config_target = config_target_mak[target] target_base_arch = config_target['TARGET_BASE_ARCH'] + target_inc = [include_directories('target' / target_base_arch)] + inc = [common_user_inc + target_inc] - # check if already generated - if target_base_arch in hw_common_arch_libs - continue - endif + # prevent common code to access cpu compile time definition, + # but still allow access to cpu.h + target_c_args = ['-DCPU_DEFS_H'] + target_system_c_args = target_c_args + ['-DCOMPILING_SYSTEM_VS_USER', '-DCONFIG_SOFTMMU'] if target_base_arch in hw_common_arch - target_inc = [include_directories('target' / target_base_arch)] - src = hw_common_arch[target_base_arch] - lib = static_library( - 'hw_' + target_base_arch, - build_by_default: false, - sources: src.all_sources() + genh, - include_directories: common_user_inc + target_inc, - implicit_include_directories: false, - # prevent common code to access cpu compile time - # definition, but still allow access to cpu.h - c_args: ['-DCPU_DEFS_H', '-DCOMPILING_SYSTEM_VS_USER', '-DCONFIG_SOFTMMU'], - dependencies: src.all_dependencies()) - hw_common_arch_libs += {target_base_arch: lib} + if target_base_arch not in hw_common_arch_libs + src = hw_common_arch[target_base_arch] + lib = static_library( + 'hw_' + target_base_arch, + build_by_default: false, + sources: src.all_sources() + genh, + include_directories: inc, + c_args: target_system_c_args, + dependencies: src.all_dependencies()) + hw_common_arch_libs += {target_base_arch: lib} + endif + endif + + if target_base_arch in target_common_arch + if target_base_arch not in target_common_arch_libs + src = target_common_arch[target_base_arch] + lib = static_library( + 'target_' + target_base_arch, + build_by_default: false, + sources: src.all_sources() + genh, + include_directories: inc, + c_args: target_c_args, + dependencies: src.all_dependencies()) + target_common_arch_libs += {target_base_arch: lib} + endif + endif + + if target_base_arch in target_common_system_arch + if target_base_arch not in target_common_system_arch_libs + src = target_common_system_arch[target_base_arch] + lib = static_library( + 'target_system_' + target_base_arch, + build_by_default: false, + sources: src.all_sources() + genh, + include_directories: inc, + c_args: target_system_c_args, + dependencies: src.all_dependencies()) + target_common_system_arch_libs += {target_base_arch: lib} + endif endif endforeach @@ -4300,12 +4332,24 @@ foreach target : target_dirs target_common = common_ss.apply(config_target, strict: false) objects = [common_all.extract_objects(target_common.sources())] arch_deps += target_common.dependencies() + if target_base_arch in target_common_arch_libs + src = target_common_arch[target_base_arch].apply(config_target, strict: false) + lib = target_common_arch_libs[target_base_arch] + objects += lib.extract_objects(src.sources()) + arch_deps += src.dependencies() + endif if target_type == 'system' and target_base_arch in hw_common_arch_libs src = hw_common_arch[target_base_arch].apply(config_target, strict: false) lib = hw_common_arch_libs[target_base_arch] objects += lib.extract_objects(src.sources()) arch_deps += src.dependencies() endif + if target_type == 'system' and target_base_arch in target_common_system_arch_libs + src = target_common_system_arch[target_base_arch].apply(config_target, strict: false) + lib = target_common_system_arch_libs[target_base_arch] + objects += lib.extract_objects(src.sources()) + arch_deps += src.dependencies() + endif target_specific = specific_ss.apply(config_target, strict: false) arch_srcs += target_specific.sources() From 9a27ee9ca4999cd9f299dfc33e8af7d789ddc6c6 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:18 -0700 Subject: [PATCH 0899/2760] target/arm: move kvm stubs and remove CONFIG_KVM from kvm_arm.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a forward decl for struct kvm_vcpu_init to avoid pulling all kvm headers. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-5-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/kvm-stub.c | 77 +++++++++++++++++++++++++++++++++++++++ target/arm/kvm_arm.h | 83 +------------------------------------------ 2 files changed, 78 insertions(+), 82 deletions(-) diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c index 965a486b32..2b73d0598c 100644 --- a/target/arm/kvm-stub.c +++ b/target/arm/kvm-stub.c @@ -22,3 +22,80 @@ bool write_list_to_kvmstate(ARMCPU *cpu, int level) { g_assert_not_reached(); } + +/* + * It's safe to call these functions without KVM support. + * They should either do nothing or return "not supported". + */ +bool kvm_arm_aarch32_supported(void) +{ + return false; +} + +bool kvm_arm_pmu_supported(void) +{ + return false; +} + +bool kvm_arm_sve_supported(void) +{ + return false; +} + +bool kvm_arm_mte_supported(void) +{ + return false; +} + +/* + * These functions should never actually be called without KVM support. + */ +void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void kvm_arm_add_vcpu_properties(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +int kvm_arm_get_max_vm_ipa_size(MachineState *ms, bool *fixed_ipa) +{ + g_assert_not_reached(); +} + +int kvm_arm_vgic_probe(void) +{ + g_assert_not_reached(); +} + +void kvm_arm_pmu_set_irq(ARMCPU *cpu, int irq) +{ + g_assert_not_reached(); +} + +void kvm_arm_pmu_init(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void kvm_arm_pvtime_init(ARMCPU *cpu, uint64_t ipa) +{ + g_assert_not_reached(); +} + +void kvm_arm_steal_time_finalize(ARMCPU *cpu, Error **errp) +{ + g_assert_not_reached(); +} + +uint32_t kvm_arm_sve_get_vls(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +void kvm_arm_enable_mte(Object *cpuobj, Error **errp) +{ + g_assert_not_reached(); +} diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 5f17fc2f3d..5bf5d56648 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -94,7 +94,7 @@ void kvm_arm_cpu_post_load(ARMCPU *cpu); */ void kvm_arm_reset_vcpu(ARMCPU *cpu); -#ifdef CONFIG_KVM +struct kvm_vcpu_init; /** * kvm_arm_create_scratch_host_vcpu: * @fdarray: filled in with kvmfd, vmfd, cpufd file descriptors in that order @@ -216,85 +216,4 @@ int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level); void kvm_arm_enable_mte(Object *cpuobj, Error **errp); -#else - -/* - * It's safe to call these functions without KVM support. - * They should either do nothing or return "not supported". - */ -static inline bool kvm_arm_aarch32_supported(void) -{ - return false; -} - -static inline bool kvm_arm_pmu_supported(void) -{ - return false; -} - -static inline bool kvm_arm_sve_supported(void) -{ - return false; -} - -static inline bool kvm_arm_mte_supported(void) -{ - return false; -} - -/* - * These functions should never actually be called without KVM support. - */ -static inline void kvm_arm_set_cpu_features_from_host(ARMCPU *cpu) -{ - g_assert_not_reached(); -} - -static inline void kvm_arm_add_vcpu_properties(ARMCPU *cpu) -{ - g_assert_not_reached(); -} - -static inline int kvm_arm_get_max_vm_ipa_size(MachineState *ms, bool *fixed_ipa) -{ - g_assert_not_reached(); -} - -static inline int kvm_arm_vgic_probe(void) -{ - g_assert_not_reached(); -} - -static inline void kvm_arm_pmu_set_irq(ARMCPU *cpu, int irq) -{ - g_assert_not_reached(); -} - -static inline void kvm_arm_pmu_init(ARMCPU *cpu) -{ - g_assert_not_reached(); -} - -static inline void kvm_arm_pvtime_init(ARMCPU *cpu, uint64_t ipa) -{ - g_assert_not_reached(); -} - -static inline void kvm_arm_steal_time_finalize(ARMCPU *cpu, Error **errp) -{ - g_assert_not_reached(); -} - -static inline uint32_t kvm_arm_sve_get_vls(ARMCPU *cpu) -{ - g_assert_not_reached(); -} - -static inline void kvm_arm_enable_mte(Object *cpuobj, Error **errp) -{ - g_assert_not_reached(); -} - -#endif - #endif From 2bff5f4715c85854bc133b56f5f09606f0ed9a5b Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:19 -0700 Subject: [PATCH 0900/2760] target/arm/kvm-stub: add kvm_arm_reset_vcpu stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Needed in target/arm/cpu.c once kvm is possible. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-6-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/kvm-stub.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c index 2b73d0598c..e34d3f5e6b 100644 --- a/target/arm/kvm-stub.c +++ b/target/arm/kvm-stub.c @@ -99,3 +99,8 @@ void kvm_arm_enable_mte(Object *cpuobj, Error **errp) { g_assert_not_reached(); } + +void kvm_arm_reset_vcpu(ARMCPU *cpu) +{ + g_assert_not_reached(); +} From 3a2964ccb4ebec97764d2b6168b60b778cc52b2f Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:20 -0700 Subject: [PATCH 0901/2760] target/arm/cpu: move arm_cpu_kvm_set_irq to kvm.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow to get rid of CONFIG_KVM in target/arm/cpu.c Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-7-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 31 ------------------------------- target/arm/kvm-stub.c | 5 +++++ target/arm/kvm.c | 29 +++++++++++++++++++++++++++++ target/arm/kvm_arm.h | 2 ++ 4 files changed, 36 insertions(+), 31 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 603f08d05a..6604769341 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1098,37 +1098,6 @@ static void arm_cpu_set_irq(void *opaque, int irq, int level) } } -static void arm_cpu_kvm_set_irq(void *opaque, int irq, int level) -{ -#ifdef CONFIG_KVM - ARMCPU *cpu = opaque; - CPUARMState *env = &cpu->env; - CPUState *cs = CPU(cpu); - uint32_t linestate_bit; - int irq_id; - - switch (irq) { - case ARM_CPU_IRQ: - irq_id = KVM_ARM_IRQ_CPU_IRQ; - linestate_bit = CPU_INTERRUPT_HARD; - break; - case ARM_CPU_FIQ: - irq_id = KVM_ARM_IRQ_CPU_FIQ; - linestate_bit = CPU_INTERRUPT_FIQ; - break; - default: - g_assert_not_reached(); - } - - if (level) { - env->irq_line_state |= linestate_bit; - } else { - env->irq_line_state &= ~linestate_bit; - } - kvm_arm_set_irq(cs->cpu_index, KVM_ARM_IRQ_TYPE_CPU, irq_id, !!level); -#endif -} - static bool arm_cpu_virtio_is_big_endian(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c index e34d3f5e6b..4806365cdc 100644 --- a/target/arm/kvm-stub.c +++ b/target/arm/kvm-stub.c @@ -104,3 +104,8 @@ void kvm_arm_reset_vcpu(ARMCPU *cpu) { g_assert_not_reached(); } + +void arm_cpu_kvm_set_irq(void *arm_cpu, int irq, int level) +{ + g_assert_not_reached(); +} diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 85911e3024..82668d6438 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -2428,3 +2428,32 @@ void kvm_arm_enable_mte(Object *cpuobj, Error **errp) cpu->kvm_mte = true; } } + +void arm_cpu_kvm_set_irq(void *arm_cpu, int irq, int level) +{ + ARMCPU *cpu = arm_cpu; + CPUARMState *env = &cpu->env; + CPUState *cs = CPU(cpu); + uint32_t linestate_bit; + int irq_id; + + switch (irq) { + case ARM_CPU_IRQ: + irq_id = KVM_ARM_IRQ_CPU_IRQ; + linestate_bit = CPU_INTERRUPT_HARD; + break; + case ARM_CPU_FIQ: + irq_id = KVM_ARM_IRQ_CPU_FIQ; + linestate_bit = CPU_INTERRUPT_FIQ; + break; + default: + g_assert_not_reached(); + } + + if (level) { + env->irq_line_state |= linestate_bit; + } else { + env->irq_line_state &= ~linestate_bit; + } + kvm_arm_set_irq(cs->cpu_index, KVM_ARM_IRQ_TYPE_CPU, irq_id, !!level); +} diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 5bf5d56648..b638e09a68 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -216,4 +216,6 @@ int kvm_arm_set_irq(int cpu, int irqtype, int irq, int level); void kvm_arm_enable_mte(Object *cpuobj, Error **errp); +void arm_cpu_kvm_set_irq(void *arm_cpu, int irq, int level); + #endif From 911a63dd25bf1f244ff8561f800a25b8033fbc87 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:21 -0700 Subject: [PATCH 0902/2760] target/arm/cpu: remove TARGET_BIG_ENDIAN dependency Reviewed-by: Richard Henderson Reviewed-by: Anton Johansson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-8-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 6604769341..c7e00e6432 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -23,6 +23,7 @@ #include "qemu/timer.h" #include "qemu/log.h" #include "exec/page-vary.h" +#include "exec/tswap.h" #include "target/arm/idau.h" #include "qemu/module.h" #include "qapi/error.h" @@ -1171,7 +1172,7 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) info->endian = BFD_ENDIAN_LITTLE; if (bswap_code(sctlr_b)) { - info->endian = TARGET_BIG_ENDIAN ? BFD_ENDIAN_LITTLE : BFD_ENDIAN_BIG; + info->endian = target_big_endian() ? BFD_ENDIAN_LITTLE : BFD_ENDIAN_BIG; } info->flags &= ~INSN_ARM_BE32; #ifndef CONFIG_USER_ONLY From 07e13d5fb5d289d4ae758cfd1f77356052789ad2 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:22 -0700 Subject: [PATCH 0903/2760] target/arm/cpu: remove TARGET_AARCH64 around aarch64_cpu_dump_state common Call is guarded by is_a64(env), so it's safe to expose without needing to assert anything. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-9-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index c7e00e6432..ec9bc72c3d 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1182,8 +1182,6 @@ static void arm_disas_set_info(CPUState *cpu, disassemble_info *info) #endif } -#ifdef TARGET_AARCH64 - static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) { ARMCPU *cpu = ARM_CPU(cs); @@ -1341,15 +1339,6 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) } } -#else - -static inline void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) -{ - g_assert_not_reached(); -} - -#endif - static void arm_cpu_dump_state(CPUState *cs, FILE *f, int flags) { ARMCPU *cpu = ARM_CPU(cs); From 2a028eab3909687f1799b53488cd199fe4775126 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:23 -0700 Subject: [PATCH 0904/2760] target/arm/cpu: remove TARGET_AARCH64 in arm_cpu_finalize_features MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Need to stub cpu64 finalize functions. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-10-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 2 -- target/arm/cpu32-stubs.c | 26 ++++++++++++++++++++++++++ target/arm/meson.build | 11 +++++++---- 3 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 target/arm/cpu32-stubs.c diff --git a/target/arm/cpu.c b/target/arm/cpu.c index ec9bc72c3d..ca5ed7892e 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1913,7 +1913,6 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) { Error *local_err = NULL; -#ifdef TARGET_AARCH64 if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { arm_cpu_sve_finalize(cpu, &local_err); if (local_err != NULL) { @@ -1949,7 +1948,6 @@ void arm_cpu_finalize_features(ARMCPU *cpu, Error **errp) return; } } -#endif if (kvm_enabled()) { kvm_arm_steal_time_finalize(cpu, &local_err); diff --git a/target/arm/cpu32-stubs.c b/target/arm/cpu32-stubs.c new file mode 100644 index 0000000000..81be44d846 --- /dev/null +++ b/target/arm/cpu32-stubs.c @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "target/arm/cpu.h" +#include "target/arm/internals.h" +#include + +void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp) +{ + g_assert_not_reached(); +} + +void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) +{ + g_assert_not_reached(); +} + +void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) +{ + g_assert_not_reached(); +} + +void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) +{ + g_assert_not_reached(); +} diff --git a/target/arm/meson.build b/target/arm/meson.build index 3065081d24..c39ddc4427 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -11,10 +11,13 @@ arm_ss.add(zlib) arm_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c'), if_false: files('kvm-stub.c')) arm_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c')) -arm_ss.add(when: 'TARGET_AARCH64', if_true: files( - 'cpu64.c', - 'gdbstub64.c', -)) +arm_ss.add(when: 'TARGET_AARCH64', + if_true: files( + 'cpu64.c', + 'gdbstub64.c'), + if_false: files( + 'cpu32-stubs.c'), +) arm_system_ss = ss.source_set() arm_system_ss.add(files( From 5ae609b629e9de4e4c6d18ecc33bdb1f02e431d6 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:24 -0700 Subject: [PATCH 0905/2760] target/arm/cpu: compile file twice (user, system) only Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-11-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index c39ddc4427..89e305eb56 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -1,6 +1,6 @@ arm_ss = ss.source_set() +arm_common_ss = ss.source_set() arm_ss.add(files( - 'cpu.c', 'debug_helper.c', 'gdbstub.c', 'helper.c', @@ -20,6 +20,7 @@ arm_ss.add(when: 'TARGET_AARCH64', ) arm_system_ss = ss.source_set() +arm_common_system_ss = ss.source_set() arm_system_ss.add(files( 'arch_dump.c', 'arm-powerctl.c', @@ -30,6 +31,9 @@ arm_system_ss.add(files( )) arm_user_ss = ss.source_set() +arm_user_ss.add(files('cpu.c')) + +arm_common_system_ss.add(files('cpu.c'), capstone) subdir('hvf') @@ -42,3 +46,5 @@ endif target_arch += {'arm': arm_ss} target_system_arch += {'arm': arm_system_ss} target_user_arch += {'arm': arm_user_ss} +target_common_arch += {'arm': arm_common_ss} +target_common_system_arch += {'arm': arm_common_system_ss} From b757ae80c6f389dd8d6f8069e413a52c7ef76c20 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:25 -0700 Subject: [PATCH 0906/2760] target/arm/cpu32-stubs.c: compile file twice (user, system) It could be squashed with commit introducing it, but I would prefer to introduce target/arm/cpu.c first. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-12-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index 89e305eb56..de214fe5d5 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -11,13 +11,9 @@ arm_ss.add(zlib) arm_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c'), if_false: files('kvm-stub.c')) arm_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c')) -arm_ss.add(when: 'TARGET_AARCH64', - if_true: files( - 'cpu64.c', - 'gdbstub64.c'), - if_false: files( - 'cpu32-stubs.c'), -) +arm_ss.add(when: 'TARGET_AARCH64', if_true: files( + 'cpu64.c', + 'gdbstub64.c')) arm_system_ss = ss.source_set() arm_common_system_ss = ss.source_set() @@ -32,8 +28,12 @@ arm_system_ss.add(files( arm_user_ss = ss.source_set() arm_user_ss.add(files('cpu.c')) +arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files( + 'cpu32-stubs.c')) arm_common_system_ss.add(files('cpu.c'), capstone) +arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files( + 'cpu32-stubs.c')) subdir('hvf') From 21a75f792f088d0aabebf6b7d96490e37fe8b779 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:26 -0700 Subject: [PATCH 0907/2760] tcg: add vaddr type for helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Defined as an alias of i32/i64 depending on host pointer size. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-13-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- include/exec/helper-head.h.inc | 11 +++++++++++ include/tcg/tcg-op-common.h | 1 + include/tcg/tcg.h | 14 ++++++++++++++ tcg/tcg.c | 5 +++++ 4 files changed, 31 insertions(+) diff --git a/include/exec/helper-head.h.inc b/include/exec/helper-head.h.inc index bce5db06ef..5b248fd713 100644 --- a/include/exec/helper-head.h.inc +++ b/include/exec/helper-head.h.inc @@ -58,6 +58,17 @@ # define dh_ctype_tl target_ulong #endif /* COMPILING_PER_TARGET */ +#if __SIZEOF_POINTER__ == 4 +# define dh_alias_vaddr i32 +# define dh_typecode_vaddr dh_typecode_i32 +#elif __SIZEOF_POINTER__ == 8 +# define dh_alias_vaddr i64 +# define dh_typecode_vaddr dh_typecode_i64 +#else +# error "sizeof pointer is different from {4,8}" +#endif /* __SIZEOF_POINTER__ */ +# define dh_ctype_vaddr uintptr_t + /* We can't use glue() here because it falls foul of C preprocessor recursive expansion rules. */ #define dh_retvar_decl0_void void diff --git a/include/tcg/tcg-op-common.h b/include/tcg/tcg-op-common.h index b439bdb385..e1071adebf 100644 --- a/include/tcg/tcg-op-common.h +++ b/include/tcg/tcg-op-common.h @@ -14,6 +14,7 @@ TCGv_i32 tcg_constant_i32(int32_t val); TCGv_i64 tcg_constant_i64(int64_t val); +TCGv_vaddr tcg_constant_vaddr(uintptr_t val); TCGv_vec tcg_constant_vec(TCGType type, unsigned vece, int64_t val); TCGv_vec tcg_constant_vec_matching(TCGv_vec match, unsigned vece, int64_t val); diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index a8c00c72cc..3fa5a7aed2 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -189,6 +189,7 @@ typedef tcg_target_ulong TCGArg; * TCGv_i64 : 64 bit integer type * TCGv_i128 : 128 bit integer type * TCGv_ptr : a host pointer type + * TCGv_vaddr: an integer type wide enough to hold a target pointer type * TCGv_vec : a host vector type; the exact size is not exposed to the CPU front-end code. * TCGv : an integer type the same size as target_ulong @@ -217,6 +218,14 @@ typedef struct TCGv_ptr_d *TCGv_ptr; typedef struct TCGv_vec_d *TCGv_vec; typedef TCGv_ptr TCGv_env; +#if __SIZEOF_POINTER__ == 4 +typedef TCGv_i32 TCGv_vaddr; +#elif __SIZEOF_POINTER__ == 8 +typedef TCGv_i64 TCGv_vaddr; +#else +# error "sizeof pointer is different from {4,8}" +#endif /* __SIZEOF_POINTER__ */ + /* call flags */ /* Helper does not read globals (either directly or through an exception). It implies TCG_CALL_NO_WRITE_GLOBALS. */ @@ -577,6 +586,11 @@ static inline TCGv_ptr temp_tcgv_ptr(TCGTemp *t) return (TCGv_ptr)temp_tcgv_i32(t); } +static inline TCGv_vaddr temp_tcgv_vaddr(TCGTemp *t) +{ + return (TCGv_vaddr)temp_tcgv_i32(t); +} + static inline TCGv_vec temp_tcgv_vec(TCGTemp *t) { return (TCGv_vec)temp_tcgv_i32(t); diff --git a/tcg/tcg.c b/tcg/tcg.c index 648333a9fb..ae27a2607d 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -2367,6 +2367,11 @@ TCGv_i64 tcg_constant_i64(int64_t val) return temp_tcgv_i64(tcg_constant_internal(TCG_TYPE_I64, val)); } +TCGv_vaddr tcg_constant_vaddr(uintptr_t val) +{ + return temp_tcgv_vaddr(tcg_constant_internal(TCG_TYPE_PTR, val)); +} + TCGv_ptr tcg_constant_ptr_int(intptr_t val) { return temp_tcgv_ptr(tcg_constant_internal(TCG_TYPE_PTR, val)); From a0307ea3dde1089d764a4449b62815023a1abbc6 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:27 -0700 Subject: [PATCH 0908/2760] target/arm/helper: use vaddr instead of target_ulong for exception_pc_alignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-14-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 2 +- target/arm/tcg/tlb_helper.c | 2 +- target/arm/tcg/translate-a64.c | 2 +- target/arm/tcg/translate.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 0907505839..95b9211c6f 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -49,7 +49,7 @@ DEF_HELPER_3(exception_with_syndrome, noreturn, env, i32, i32) DEF_HELPER_4(exception_with_syndrome_el, noreturn, env, i32, i32, i32) DEF_HELPER_2(exception_bkpt_insn, noreturn, env, i32) DEF_HELPER_2(exception_swstep, noreturn, env, i32) -DEF_HELPER_2(exception_pc_alignment, noreturn, env, tl) +DEF_HELPER_2(exception_pc_alignment, noreturn, env, vaddr) DEF_HELPER_1(setend, void, env) DEF_HELPER_2(wfi, void, env, i32) DEF_HELPER_1(wfe, void, env) diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index 5ea4d6590f..d9e6c827d4 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -276,7 +276,7 @@ void arm_cpu_do_unaligned_access(CPUState *cs, vaddr vaddr, arm_deliver_fault(cpu, vaddr, access_type, mmu_idx, &fi); } -void helper_exception_pc_alignment(CPUARMState *env, target_ulong pc) +void helper_exception_pc_alignment(CPUARMState *env, vaddr pc) { ARMMMUFaultInfo fi = { .type = ARMFault_Alignment }; int target_el = exception_target_el(env); diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 52cf47e775..ac80f572a2 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10242,7 +10242,7 @@ static void aarch64_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * start of the TB. */ assert(s->base.num_insns == 1); - gen_helper_exception_pc_alignment(tcg_env, tcg_constant_tl(pc)); + gen_helper_exception_pc_alignment(tcg_env, tcg_constant_vaddr(pc)); s->base.is_jmp = DISAS_NORETURN; s->base.pc_next = QEMU_ALIGN_UP(pc, 4); return; diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index e773ab7268..9962f43b1d 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -7791,7 +7791,7 @@ static void arm_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) * be possible after an indirect branch, at the start of the TB. */ assert(dc->base.num_insns == 1); - gen_helper_exception_pc_alignment(tcg_env, tcg_constant_tl(pc)); + gen_helper_exception_pc_alignment(tcg_env, tcg_constant_vaddr(pc)); dc->base.is_jmp = DISAS_NORETURN; dc->base.pc_next = QEMU_ALIGN_UP(pc, 4); return; From 5296a79b5a14d072af6fe2c8a8c00793862f18d4 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:28 -0700 Subject: [PATCH 0909/2760] target/arm/helper: use vaddr instead of target_ulong for probe_access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-15-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 2 +- target/arm/tcg/op_helper.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/helper.h b/target/arm/helper.h index 95b9211c6f..0a4fc90fa8 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -104,7 +104,7 @@ DEF_HELPER_FLAGS_1(rebuild_hflags_a32_newel, TCG_CALL_NO_RWG, void, env) DEF_HELPER_FLAGS_2(rebuild_hflags_a32, TCG_CALL_NO_RWG, void, env, int) DEF_HELPER_FLAGS_2(rebuild_hflags_a64, TCG_CALL_NO_RWG, void, env, int) -DEF_HELPER_FLAGS_5(probe_access, TCG_CALL_NO_WG, void, env, tl, i32, i32, i32) +DEF_HELPER_FLAGS_5(probe_access, TCG_CALL_NO_WG, void, env, vaddr, i32, i32, i32) DEF_HELPER_1(vfp_get_fpscr, i32, env) DEF_HELPER_2(vfp_set_fpscr, void, env, i32) diff --git a/target/arm/tcg/op_helper.c b/target/arm/tcg/op_helper.c index dc3f83c37d..575e566280 100644 --- a/target/arm/tcg/op_helper.c +++ b/target/arm/tcg/op_helper.c @@ -1222,7 +1222,7 @@ uint32_t HELPER(ror_cc)(CPUARMState *env, uint32_t x, uint32_t i) } } -void HELPER(probe_access)(CPUARMState *env, target_ulong ptr, +void HELPER(probe_access)(CPUARMState *env, vaddr ptr, uint32_t access_type, uint32_t mmu_idx, uint32_t size) { From a7a3ae9edc65032c6362bb3af6fe611df10aa705 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:29 -0700 Subject: [PATCH 0910/2760] target/arm/helper: extract common helpers Allow later commits to include only the "new" tcg/helper.h, thus preventing to pull aarch64 helpers (+ target/arm/helper.h contains a ifdef TARGET_AARCH64). Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-16-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.h | 1152 +------------------------------------- target/arm/tcg/helper.h | 1153 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1155 insertions(+), 1150 deletions(-) create mode 100644 target/arm/tcg/helper.h diff --git a/target/arm/helper.h b/target/arm/helper.h index 0a4fc90fa8..f340a49a28 100644 --- a/target/arm/helper.h +++ b/target/arm/helper.h @@ -1,1154 +1,6 @@ -DEF_HELPER_FLAGS_1(sxtb16, TCG_CALL_NO_RWG_SE, i32, i32) -DEF_HELPER_FLAGS_1(uxtb16, TCG_CALL_NO_RWG_SE, i32, i32) +/* SPDX-License-Identifier: GPL-2.0-or-later */ -DEF_HELPER_3(add_setq, i32, env, i32, i32) -DEF_HELPER_3(add_saturate, i32, env, i32, i32) -DEF_HELPER_3(sub_saturate, i32, env, i32, i32) -DEF_HELPER_3(add_usaturate, i32, env, i32, i32) -DEF_HELPER_3(sub_usaturate, i32, env, i32, i32) -DEF_HELPER_FLAGS_3(sdiv, TCG_CALL_NO_RWG, s32, env, s32, s32) -DEF_HELPER_FLAGS_3(udiv, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_1(rbit, TCG_CALL_NO_RWG_SE, i32, i32) - -#define PAS_OP(pfx) \ - DEF_HELPER_3(pfx ## add8, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## sub8, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## sub16, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## add16, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## addsubx, i32, i32, i32, ptr) \ - DEF_HELPER_3(pfx ## subaddx, i32, i32, i32, ptr) - -PAS_OP(s) -PAS_OP(u) -#undef PAS_OP - -#define PAS_OP(pfx) \ - DEF_HELPER_2(pfx ## add8, i32, i32, i32) \ - DEF_HELPER_2(pfx ## sub8, i32, i32, i32) \ - DEF_HELPER_2(pfx ## sub16, i32, i32, i32) \ - DEF_HELPER_2(pfx ## add16, i32, i32, i32) \ - DEF_HELPER_2(pfx ## addsubx, i32, i32, i32) \ - DEF_HELPER_2(pfx ## subaddx, i32, i32, i32) -PAS_OP(q) -PAS_OP(sh) -PAS_OP(uq) -PAS_OP(uh) -#undef PAS_OP - -DEF_HELPER_3(ssat, i32, env, i32, i32) -DEF_HELPER_3(usat, i32, env, i32, i32) -DEF_HELPER_3(ssat16, i32, env, i32, i32) -DEF_HELPER_3(usat16, i32, env, i32, i32) - -DEF_HELPER_FLAGS_2(usad8, TCG_CALL_NO_RWG_SE, i32, i32, i32) - -DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE, - i32, i32, i32, i32) -DEF_HELPER_2(exception_internal, noreturn, env, i32) -DEF_HELPER_3(exception_with_syndrome, noreturn, env, i32, i32) -DEF_HELPER_4(exception_with_syndrome_el, noreturn, env, i32, i32, i32) -DEF_HELPER_2(exception_bkpt_insn, noreturn, env, i32) -DEF_HELPER_2(exception_swstep, noreturn, env, i32) -DEF_HELPER_2(exception_pc_alignment, noreturn, env, vaddr) -DEF_HELPER_1(setend, void, env) -DEF_HELPER_2(wfi, void, env, i32) -DEF_HELPER_1(wfe, void, env) -DEF_HELPER_2(wfit, void, env, i64) -DEF_HELPER_1(yield, void, env) -DEF_HELPER_1(pre_hvc, void, env) -DEF_HELPER_2(pre_smc, void, env, i32) -DEF_HELPER_1(vesb, void, env) - -DEF_HELPER_3(cpsr_write, void, env, i32, i32) -DEF_HELPER_2(cpsr_write_eret, void, env, i32) -DEF_HELPER_1(cpsr_read, i32, env) - -DEF_HELPER_3(v7m_msr, void, env, i32, i32) -DEF_HELPER_2(v7m_mrs, i32, env, i32) - -DEF_HELPER_2(v7m_bxns, void, env, i32) -DEF_HELPER_2(v7m_blxns, void, env, i32) - -DEF_HELPER_3(v7m_tt, i32, env, i32, i32) - -DEF_HELPER_1(v7m_preserve_fp_state, void, env) - -DEF_HELPER_2(v7m_vlstm, void, env, i32) -DEF_HELPER_2(v7m_vlldm, void, env, i32) - -DEF_HELPER_2(v8m_stackcheck, void, env, i32) - -DEF_HELPER_FLAGS_2(check_bxj_trap, TCG_CALL_NO_WG, void, env, i32) - -DEF_HELPER_4(access_check_cp_reg, cptr, env, i32, i32, i32) -DEF_HELPER_FLAGS_2(lookup_cp_reg, TCG_CALL_NO_RWG_SE, cptr, env, i32) -DEF_HELPER_FLAGS_2(tidcp_el0, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_FLAGS_2(tidcp_el1, TCG_CALL_NO_WG, void, env, i32) -DEF_HELPER_3(set_cp_reg, void, env, cptr, i32) -DEF_HELPER_2(get_cp_reg, i32, env, cptr) -DEF_HELPER_3(set_cp_reg64, void, env, cptr, i64) -DEF_HELPER_2(get_cp_reg64, i64, env, cptr) - -DEF_HELPER_2(get_r13_banked, i32, env, i32) -DEF_HELPER_3(set_r13_banked, void, env, i32, i32) - -DEF_HELPER_3(mrs_banked, i32, env, i32, i32) -DEF_HELPER_4(msr_banked, void, env, i32, i32, i32) - -DEF_HELPER_2(get_user_reg, i32, env, i32) -DEF_HELPER_3(set_user_reg, void, env, i32, i32) - -DEF_HELPER_FLAGS_1(rebuild_hflags_m32_newel, TCG_CALL_NO_RWG, void, env) -DEF_HELPER_FLAGS_2(rebuild_hflags_m32, TCG_CALL_NO_RWG, void, env, int) -DEF_HELPER_FLAGS_1(rebuild_hflags_a32_newel, TCG_CALL_NO_RWG, void, env) -DEF_HELPER_FLAGS_2(rebuild_hflags_a32, TCG_CALL_NO_RWG, void, env, int) -DEF_HELPER_FLAGS_2(rebuild_hflags_a64, TCG_CALL_NO_RWG, void, env, int) - -DEF_HELPER_FLAGS_5(probe_access, TCG_CALL_NO_WG, void, env, vaddr, i32, i32, i32) - -DEF_HELPER_1(vfp_get_fpscr, i32, env) -DEF_HELPER_2(vfp_set_fpscr, void, env, i32) - -DEF_HELPER_3(vfp_addh, f16, f16, f16, fpst) -DEF_HELPER_3(vfp_adds, f32, f32, f32, fpst) -DEF_HELPER_3(vfp_addd, f64, f64, f64, fpst) -DEF_HELPER_3(vfp_subh, f16, f16, f16, fpst) -DEF_HELPER_3(vfp_subs, f32, f32, f32, fpst) -DEF_HELPER_3(vfp_subd, f64, f64, f64, fpst) -DEF_HELPER_3(vfp_mulh, f16, f16, f16, fpst) -DEF_HELPER_3(vfp_muls, f32, f32, f32, fpst) -DEF_HELPER_3(vfp_muld, f64, f64, f64, fpst) -DEF_HELPER_3(vfp_divh, f16, f16, f16, fpst) -DEF_HELPER_3(vfp_divs, f32, f32, f32, fpst) -DEF_HELPER_3(vfp_divd, f64, f64, f64, fpst) -DEF_HELPER_3(vfp_maxh, f16, f16, f16, fpst) -DEF_HELPER_3(vfp_maxs, f32, f32, f32, fpst) -DEF_HELPER_3(vfp_maxd, f64, f64, f64, fpst) -DEF_HELPER_3(vfp_minh, f16, f16, f16, fpst) -DEF_HELPER_3(vfp_mins, f32, f32, f32, fpst) -DEF_HELPER_3(vfp_mind, f64, f64, f64, fpst) -DEF_HELPER_3(vfp_maxnumh, f16, f16, f16, fpst) -DEF_HELPER_3(vfp_maxnums, f32, f32, f32, fpst) -DEF_HELPER_3(vfp_maxnumd, f64, f64, f64, fpst) -DEF_HELPER_3(vfp_minnumh, f16, f16, f16, fpst) -DEF_HELPER_3(vfp_minnums, f32, f32, f32, fpst) -DEF_HELPER_3(vfp_minnumd, f64, f64, f64, fpst) -DEF_HELPER_2(vfp_sqrth, f16, f16, fpst) -DEF_HELPER_2(vfp_sqrts, f32, f32, fpst) -DEF_HELPER_2(vfp_sqrtd, f64, f64, fpst) -DEF_HELPER_3(vfp_cmph, void, f16, f16, env) -DEF_HELPER_3(vfp_cmps, void, f32, f32, env) -DEF_HELPER_3(vfp_cmpd, void, f64, f64, env) -DEF_HELPER_3(vfp_cmpeh, void, f16, f16, env) -DEF_HELPER_3(vfp_cmpes, void, f32, f32, env) -DEF_HELPER_3(vfp_cmped, void, f64, f64, env) - -DEF_HELPER_2(vfp_fcvtds, f64, f32, fpst) -DEF_HELPER_2(vfp_fcvtsd, f32, f64, fpst) -DEF_HELPER_FLAGS_2(bfcvt, TCG_CALL_NO_RWG, i32, f32, fpst) -DEF_HELPER_FLAGS_2(bfcvt_pair, TCG_CALL_NO_RWG, i32, i64, fpst) - -DEF_HELPER_2(vfp_uitoh, f16, i32, fpst) -DEF_HELPER_2(vfp_uitos, f32, i32, fpst) -DEF_HELPER_2(vfp_uitod, f64, i32, fpst) -DEF_HELPER_2(vfp_sitoh, f16, i32, fpst) -DEF_HELPER_2(vfp_sitos, f32, i32, fpst) -DEF_HELPER_2(vfp_sitod, f64, i32, fpst) - -DEF_HELPER_2(vfp_touih, i32, f16, fpst) -DEF_HELPER_2(vfp_touis, i32, f32, fpst) -DEF_HELPER_2(vfp_touid, i32, f64, fpst) -DEF_HELPER_2(vfp_touizh, i32, f16, fpst) -DEF_HELPER_2(vfp_touizs, i32, f32, fpst) -DEF_HELPER_2(vfp_touizd, i32, f64, fpst) -DEF_HELPER_2(vfp_tosih, s32, f16, fpst) -DEF_HELPER_2(vfp_tosis, s32, f32, fpst) -DEF_HELPER_2(vfp_tosid, s32, f64, fpst) -DEF_HELPER_2(vfp_tosizh, s32, f16, fpst) -DEF_HELPER_2(vfp_tosizs, s32, f32, fpst) -DEF_HELPER_2(vfp_tosizd, s32, f64, fpst) - -DEF_HELPER_3(vfp_toshh_round_to_zero, i32, f16, i32, fpst) -DEF_HELPER_3(vfp_toslh_round_to_zero, i32, f16, i32, fpst) -DEF_HELPER_3(vfp_touhh_round_to_zero, i32, f16, i32, fpst) -DEF_HELPER_3(vfp_toulh_round_to_zero, i32, f16, i32, fpst) -DEF_HELPER_3(vfp_toshs_round_to_zero, i32, f32, i32, fpst) -DEF_HELPER_3(vfp_tosls_round_to_zero, i32, f32, i32, fpst) -DEF_HELPER_3(vfp_touhs_round_to_zero, i32, f32, i32, fpst) -DEF_HELPER_3(vfp_touls_round_to_zero, i32, f32, i32, fpst) -DEF_HELPER_3(vfp_toshd_round_to_zero, i64, f64, i32, fpst) -DEF_HELPER_3(vfp_tosld_round_to_zero, i64, f64, i32, fpst) -DEF_HELPER_3(vfp_tosqd_round_to_zero, i64, f64, i32, fpst) -DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, fpst) -DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, fpst) -DEF_HELPER_3(vfp_touqd_round_to_zero, i64, f64, i32, fpst) -DEF_HELPER_3(vfp_touhh, i32, f16, i32, fpst) -DEF_HELPER_3(vfp_toshh, i32, f16, i32, fpst) -DEF_HELPER_3(vfp_toulh, i32, f16, i32, fpst) -DEF_HELPER_3(vfp_toslh, i32, f16, i32, fpst) -DEF_HELPER_3(vfp_touqh, i64, f16, i32, fpst) -DEF_HELPER_3(vfp_tosqh, i64, f16, i32, fpst) -DEF_HELPER_3(vfp_toshs, i32, f32, i32, fpst) -DEF_HELPER_3(vfp_tosls, i32, f32, i32, fpst) -DEF_HELPER_3(vfp_tosqs, i64, f32, i32, fpst) -DEF_HELPER_3(vfp_touhs, i32, f32, i32, fpst) -DEF_HELPER_3(vfp_touls, i32, f32, i32, fpst) -DEF_HELPER_3(vfp_touqs, i64, f32, i32, fpst) -DEF_HELPER_3(vfp_toshd, i64, f64, i32, fpst) -DEF_HELPER_3(vfp_tosld, i64, f64, i32, fpst) -DEF_HELPER_3(vfp_tosqd, i64, f64, i32, fpst) -DEF_HELPER_3(vfp_touhd, i64, f64, i32, fpst) -DEF_HELPER_3(vfp_tould, i64, f64, i32, fpst) -DEF_HELPER_3(vfp_touqd, i64, f64, i32, fpst) -DEF_HELPER_3(vfp_shtos, f32, i32, i32, fpst) -DEF_HELPER_3(vfp_sltos, f32, i32, i32, fpst) -DEF_HELPER_3(vfp_sqtos, f32, i64, i32, fpst) -DEF_HELPER_3(vfp_uhtos, f32, i32, i32, fpst) -DEF_HELPER_3(vfp_ultos, f32, i32, i32, fpst) -DEF_HELPER_3(vfp_uqtos, f32, i64, i32, fpst) -DEF_HELPER_3(vfp_shtod, f64, i64, i32, fpst) -DEF_HELPER_3(vfp_sltod, f64, i64, i32, fpst) -DEF_HELPER_3(vfp_sqtod, f64, i64, i32, fpst) -DEF_HELPER_3(vfp_uhtod, f64, i64, i32, fpst) -DEF_HELPER_3(vfp_ultod, f64, i64, i32, fpst) -DEF_HELPER_3(vfp_uqtod, f64, i64, i32, fpst) -DEF_HELPER_3(vfp_shtoh, f16, i32, i32, fpst) -DEF_HELPER_3(vfp_uhtoh, f16, i32, i32, fpst) -DEF_HELPER_3(vfp_sltoh, f16, i32, i32, fpst) -DEF_HELPER_3(vfp_ultoh, f16, i32, i32, fpst) -DEF_HELPER_3(vfp_sqtoh, f16, i64, i32, fpst) -DEF_HELPER_3(vfp_uqtoh, f16, i64, i32, fpst) - -DEF_HELPER_3(vfp_shtos_round_to_nearest, f32, i32, i32, fpst) -DEF_HELPER_3(vfp_sltos_round_to_nearest, f32, i32, i32, fpst) -DEF_HELPER_3(vfp_uhtos_round_to_nearest, f32, i32, i32, fpst) -DEF_HELPER_3(vfp_ultos_round_to_nearest, f32, i32, i32, fpst) -DEF_HELPER_3(vfp_shtod_round_to_nearest, f64, i64, i32, fpst) -DEF_HELPER_3(vfp_sltod_round_to_nearest, f64, i64, i32, fpst) -DEF_HELPER_3(vfp_uhtod_round_to_nearest, f64, i64, i32, fpst) -DEF_HELPER_3(vfp_ultod_round_to_nearest, f64, i64, i32, fpst) -DEF_HELPER_3(vfp_shtoh_round_to_nearest, f16, i32, i32, fpst) -DEF_HELPER_3(vfp_uhtoh_round_to_nearest, f16, i32, i32, fpst) -DEF_HELPER_3(vfp_sltoh_round_to_nearest, f16, i32, i32, fpst) -DEF_HELPER_3(vfp_ultoh_round_to_nearest, f16, i32, i32, fpst) - -DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, fpst) - -DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f32, TCG_CALL_NO_RWG, f32, f16, fpst, i32) -DEF_HELPER_FLAGS_3(vfp_fcvt_f32_to_f16, TCG_CALL_NO_RWG, f16, f32, fpst, i32) -DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f64, TCG_CALL_NO_RWG, f64, f16, fpst, i32) -DEF_HELPER_FLAGS_3(vfp_fcvt_f64_to_f16, TCG_CALL_NO_RWG, f16, f64, fpst, i32) - -DEF_HELPER_4(vfp_muladdd, f64, f64, f64, f64, fpst) -DEF_HELPER_4(vfp_muladds, f32, f32, f32, f32, fpst) -DEF_HELPER_4(vfp_muladdh, f16, f16, f16, f16, fpst) - -DEF_HELPER_FLAGS_2(recpe_f16, TCG_CALL_NO_RWG, f16, f16, fpst) -DEF_HELPER_FLAGS_2(recpe_f32, TCG_CALL_NO_RWG, f32, f32, fpst) -DEF_HELPER_FLAGS_2(recpe_rpres_f32, TCG_CALL_NO_RWG, f32, f32, fpst) -DEF_HELPER_FLAGS_2(recpe_f64, TCG_CALL_NO_RWG, f64, f64, fpst) -DEF_HELPER_FLAGS_2(rsqrte_f16, TCG_CALL_NO_RWG, f16, f16, fpst) -DEF_HELPER_FLAGS_2(rsqrte_f32, TCG_CALL_NO_RWG, f32, f32, fpst) -DEF_HELPER_FLAGS_2(rsqrte_rpres_f32, TCG_CALL_NO_RWG, f32, f32, fpst) -DEF_HELPER_FLAGS_2(rsqrte_f64, TCG_CALL_NO_RWG, f64, f64, fpst) -DEF_HELPER_FLAGS_1(recpe_u32, TCG_CALL_NO_RWG, i32, i32) -DEF_HELPER_FLAGS_1(rsqrte_u32, TCG_CALL_NO_RWG, i32, i32) -DEF_HELPER_FLAGS_4(neon_tbl, TCG_CALL_NO_RWG, i64, env, i32, i64, i64) - -DEF_HELPER_3(shl_cc, i32, env, i32, i32) -DEF_HELPER_3(shr_cc, i32, env, i32, i32) -DEF_HELPER_3(sar_cc, i32, env, i32, i32) -DEF_HELPER_3(ror_cc, i32, env, i32, i32) - -DEF_HELPER_FLAGS_2(rinth_exact, TCG_CALL_NO_RWG, f16, f16, fpst) -DEF_HELPER_FLAGS_2(rints_exact, TCG_CALL_NO_RWG, f32, f32, fpst) -DEF_HELPER_FLAGS_2(rintd_exact, TCG_CALL_NO_RWG, f64, f64, fpst) -DEF_HELPER_FLAGS_2(rinth, TCG_CALL_NO_RWG, f16, f16, fpst) -DEF_HELPER_FLAGS_2(rints, TCG_CALL_NO_RWG, f32, f32, fpst) -DEF_HELPER_FLAGS_2(rintd, TCG_CALL_NO_RWG, f64, f64, fpst) - -DEF_HELPER_FLAGS_2(vjcvt, TCG_CALL_NO_RWG, i32, f64, env) -DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, fpst) - -DEF_HELPER_FLAGS_3(check_hcr_el2_trap, TCG_CALL_NO_WG, void, env, i32, i32) - -/* neon_helper.c */ -DEF_HELPER_2(neon_pmin_u8, i32, i32, i32) -DEF_HELPER_2(neon_pmin_s8, i32, i32, i32) -DEF_HELPER_2(neon_pmin_u16, i32, i32, i32) -DEF_HELPER_2(neon_pmin_s16, i32, i32, i32) -DEF_HELPER_2(neon_pmax_u8, i32, i32, i32) -DEF_HELPER_2(neon_pmax_s8, i32, i32, i32) -DEF_HELPER_2(neon_pmax_u16, i32, i32, i32) -DEF_HELPER_2(neon_pmax_s16, i32, i32, i32) - -DEF_HELPER_2(neon_shl_u16, i32, i32, i32) -DEF_HELPER_2(neon_shl_s16, i32, i32, i32) -DEF_HELPER_2(neon_rshl_u8, i32, i32, i32) -DEF_HELPER_2(neon_rshl_s8, i32, i32, i32) -DEF_HELPER_2(neon_rshl_u16, i32, i32, i32) -DEF_HELPER_2(neon_rshl_s16, i32, i32, i32) -DEF_HELPER_2(neon_rshl_u32, i32, i32, i32) -DEF_HELPER_2(neon_rshl_s32, i32, i32, i32) -DEF_HELPER_2(neon_rshl_u64, i64, i64, i64) -DEF_HELPER_2(neon_rshl_s64, i64, i64, i64) -DEF_HELPER_3(neon_qshl_u8, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_s8, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_u16, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_u32, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_s32, i32, env, i32, i32) -DEF_HELPER_3(neon_qshl_u64, i64, env, i64, i64) -DEF_HELPER_3(neon_qshl_s64, i64, env, i64, i64) -DEF_HELPER_3(neon_qshlu_s8, i32, env, i32, i32) -DEF_HELPER_3(neon_qshlu_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qshlu_s32, i32, env, i32, i32) -DEF_HELPER_3(neon_qshlu_s64, i64, env, i64, i64) -DEF_HELPER_3(neon_qrshl_u8, i32, env, i32, i32) -DEF_HELPER_3(neon_qrshl_s8, i32, env, i32, i32) -DEF_HELPER_3(neon_qrshl_u16, i32, env, i32, i32) -DEF_HELPER_3(neon_qrshl_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qrshl_u32, i32, env, i32, i32) -DEF_HELPER_3(neon_qrshl_s32, i32, env, i32, i32) -DEF_HELPER_3(neon_qrshl_u64, i64, env, i64, i64) -DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64) -DEF_HELPER_FLAGS_5(neon_sqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_sqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_sqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_sqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_uqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_uqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_uqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_uqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_sqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_sqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_sqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_sqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_uqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_uqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_uqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(neon_uqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(neon_sqshli_b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(neon_sqshli_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(neon_sqshli_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(neon_sqshli_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(neon_uqshli_b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(neon_uqshli_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(neon_uqshli_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(neon_uqshli_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(neon_sqshlui_b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(neon_sqshlui_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(neon_sqshlui_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(neon_sqshlui_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_4(gvec_srshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_srshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_srshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_srshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_urshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_urshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_urshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_urshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_2(neon_add_u8, i32, i32, i32) -DEF_HELPER_2(neon_add_u16, i32, i32, i32) -DEF_HELPER_2(neon_sub_u8, i32, i32, i32) -DEF_HELPER_2(neon_sub_u16, i32, i32, i32) -DEF_HELPER_2(neon_mul_u8, i32, i32, i32) -DEF_HELPER_2(neon_mul_u16, i32, i32, i32) - -DEF_HELPER_2(neon_tst_u8, i32, i32, i32) -DEF_HELPER_2(neon_tst_u16, i32, i32, i32) -DEF_HELPER_2(neon_tst_u32, i32, i32, i32) - -DEF_HELPER_1(neon_clz_u8, i32, i32) -DEF_HELPER_1(neon_clz_u16, i32, i32) -DEF_HELPER_1(neon_cls_s8, i32, i32) -DEF_HELPER_1(neon_cls_s16, i32, i32) -DEF_HELPER_1(neon_cls_s32, i32, i32) -DEF_HELPER_FLAGS_3(gvec_cnt_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_rbit_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32) -DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32) -DEF_HELPER_4(neon_qrdmlah_s16, i32, env, i32, i32, i32) -DEF_HELPER_4(neon_qrdmlsh_s16, i32, env, i32, i32, i32) -DEF_HELPER_3(neon_qdmulh_s32, i32, env, i32, i32) -DEF_HELPER_3(neon_qrdmulh_s32, i32, env, i32, i32) -DEF_HELPER_4(neon_qrdmlah_s32, i32, env, s32, s32, s32) -DEF_HELPER_4(neon_qrdmlsh_s32, i32, env, s32, s32, s32) - -DEF_HELPER_1(neon_narrow_u8, i64, i64) -DEF_HELPER_1(neon_narrow_u16, i64, i64) -DEF_HELPER_2(neon_unarrow_sat8, i64, env, i64) -DEF_HELPER_2(neon_narrow_sat_u8, i64, env, i64) -DEF_HELPER_2(neon_narrow_sat_s8, i64, env, i64) -DEF_HELPER_2(neon_unarrow_sat16, i64, env, i64) -DEF_HELPER_2(neon_narrow_sat_u16, i64, env, i64) -DEF_HELPER_2(neon_narrow_sat_s16, i64, env, i64) -DEF_HELPER_2(neon_unarrow_sat32, i64, env, i64) -DEF_HELPER_2(neon_narrow_sat_u32, i64, env, i64) -DEF_HELPER_2(neon_narrow_sat_s32, i64, env, i64) -DEF_HELPER_1(neon_narrow_high_u8, i32, i64) -DEF_HELPER_1(neon_narrow_high_u16, i32, i64) -DEF_HELPER_1(neon_narrow_round_high_u8, i32, i64) -DEF_HELPER_1(neon_narrow_round_high_u16, i32, i64) -DEF_HELPER_1(neon_widen_u8, i64, i32) -DEF_HELPER_1(neon_widen_s8, i64, i32) -DEF_HELPER_1(neon_widen_u16, i64, i32) -DEF_HELPER_1(neon_widen_s16, i64, i32) - -DEF_HELPER_FLAGS_1(neon_addlp_s8, TCG_CALL_NO_RWG_SE, i64, i64) -DEF_HELPER_FLAGS_1(neon_addlp_s16, TCG_CALL_NO_RWG_SE, i64, i64) -DEF_HELPER_3(neon_addl_saturate_s32, i64, env, i64, i64) -DEF_HELPER_3(neon_addl_saturate_s64, i64, env, i64, i64) -DEF_HELPER_2(neon_abdl_u16, i64, i32, i32) -DEF_HELPER_2(neon_abdl_s16, i64, i32, i32) -DEF_HELPER_2(neon_abdl_u32, i64, i32, i32) -DEF_HELPER_2(neon_abdl_s32, i64, i32, i32) -DEF_HELPER_2(neon_abdl_u64, i64, i32, i32) -DEF_HELPER_2(neon_abdl_s64, i64, i32, i32) -DEF_HELPER_2(neon_mull_u8, i64, i32, i32) -DEF_HELPER_2(neon_mull_s8, i64, i32, i32) -DEF_HELPER_2(neon_mull_u16, i64, i32, i32) -DEF_HELPER_2(neon_mull_s16, i64, i32, i32) - -DEF_HELPER_1(neon_negl_u16, i64, i64) -DEF_HELPER_1(neon_negl_u32, i64, i64) - -DEF_HELPER_FLAGS_2(neon_qabs_s8, TCG_CALL_NO_RWG, i32, env, i32) -DEF_HELPER_FLAGS_2(neon_qabs_s16, TCG_CALL_NO_RWG, i32, env, i32) -DEF_HELPER_FLAGS_2(neon_qabs_s32, TCG_CALL_NO_RWG, i32, env, i32) -DEF_HELPER_FLAGS_2(neon_qabs_s64, TCG_CALL_NO_RWG, i64, env, i64) -DEF_HELPER_FLAGS_2(neon_qneg_s8, TCG_CALL_NO_RWG, i32, env, i32) -DEF_HELPER_FLAGS_2(neon_qneg_s16, TCG_CALL_NO_RWG, i32, env, i32) -DEF_HELPER_FLAGS_2(neon_qneg_s32, TCG_CALL_NO_RWG, i32, env, i32) -DEF_HELPER_FLAGS_2(neon_qneg_s64, TCG_CALL_NO_RWG, i64, env, i64) - -DEF_HELPER_3(neon_ceq_f32, i32, i32, i32, fpst) -DEF_HELPER_3(neon_cge_f32, i32, i32, i32, fpst) -DEF_HELPER_3(neon_cgt_f32, i32, i32, i32, fpst) -DEF_HELPER_3(neon_acge_f32, i32, i32, i32, fpst) -DEF_HELPER_3(neon_acgt_f32, i32, i32, i32, fpst) -DEF_HELPER_3(neon_acge_f64, i64, i64, i64, fpst) -DEF_HELPER_3(neon_acgt_f64, i64, i64, i64, fpst) - -/* iwmmxt_helper.c */ -DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64) -DEF_HELPER_2(iwmmxt_madduq, i64, i64, i64) -DEF_HELPER_2(iwmmxt_sadb, i64, i64, i64) -DEF_HELPER_2(iwmmxt_sadw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mulslw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mulshw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_mululw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_muluhw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_macsw, i64, i64, i64) -DEF_HELPER_2(iwmmxt_macuw, i64, i64, i64) -DEF_HELPER_1(iwmmxt_setpsr_nz, i32, i64) - -#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \ -DEF_HELPER_3(iwmmxt_##name##b, i64, env, i64, i64) \ -DEF_HELPER_3(iwmmxt_##name##w, i64, env, i64, i64) \ -DEF_HELPER_3(iwmmxt_##name##l, i64, env, i64, i64) \ - -DEF_IWMMXT_HELPER_SIZE_ENV(unpackl) -DEF_IWMMXT_HELPER_SIZE_ENV(unpackh) - -DEF_HELPER_2(iwmmxt_unpacklub, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackluw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklul, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhub, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhuw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhul, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsb, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpacklsl, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsb, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsw, i64, env, i64) -DEF_HELPER_2(iwmmxt_unpackhsl, i64, env, i64) - -DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq) -DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu) -DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts) - -DEF_IWMMXT_HELPER_SIZE_ENV(mins) -DEF_IWMMXT_HELPER_SIZE_ENV(minu) -DEF_IWMMXT_HELPER_SIZE_ENV(maxs) -DEF_IWMMXT_HELPER_SIZE_ENV(maxu) - -DEF_IWMMXT_HELPER_SIZE_ENV(subn) -DEF_IWMMXT_HELPER_SIZE_ENV(addn) -DEF_IWMMXT_HELPER_SIZE_ENV(subu) -DEF_IWMMXT_HELPER_SIZE_ENV(addu) -DEF_IWMMXT_HELPER_SIZE_ENV(subs) -DEF_IWMMXT_HELPER_SIZE_ENV(adds) - -DEF_HELPER_3(iwmmxt_avgb0, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgb1, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgw0, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_avgw1, i64, env, i64, i64) - -DEF_HELPER_3(iwmmxt_align, i64, i64, i64, i32) -DEF_HELPER_4(iwmmxt_insr, i64, i64, i32, i32, i32) - -DEF_HELPER_1(iwmmxt_bcstb, i64, i32) -DEF_HELPER_1(iwmmxt_bcstw, i64, i32) -DEF_HELPER_1(iwmmxt_bcstl, i64, i32) - -DEF_HELPER_1(iwmmxt_addcb, i64, i64) -DEF_HELPER_1(iwmmxt_addcw, i64, i64) -DEF_HELPER_1(iwmmxt_addcl, i64, i64) - -DEF_HELPER_1(iwmmxt_msbb, i32, i64) -DEF_HELPER_1(iwmmxt_msbw, i32, i64) -DEF_HELPER_1(iwmmxt_msbl, i32, i64) - -DEF_HELPER_3(iwmmxt_srlw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_srll, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_srlq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sllw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_slll, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sllq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sraw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sral, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_sraq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorw, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorl, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_rorq, i64, env, i64, i32) -DEF_HELPER_3(iwmmxt_shufh, i64, env, i64, i32) - -DEF_HELPER_3(iwmmxt_packuw, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packul, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packuq, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsw, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsl, i64, env, i64, i64) -DEF_HELPER_3(iwmmxt_packsq, i64, env, i64, i64) - -DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32) -DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32) -DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32) - -DEF_HELPER_FLAGS_2(neon_unzip8, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_unzip16, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_qunzip8, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_qunzip16, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_qunzip32, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_zip8, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_zip16, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_qzip8, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_qzip16, TCG_CALL_NO_RWG, void, ptr, ptr) -DEF_HELPER_FLAGS_2(neon_qzip32, TCG_CALL_NO_RWG, void, ptr, ptr) - -DEF_HELPER_FLAGS_4(crypto_aese, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_aesd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(crypto_aesmc, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(crypto_aesimc, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(crypto_sha1su0, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sha1c, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sha1p, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sha1m, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(crypto_sha1h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(crypto_sha1su1, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(crypto_sha256h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sha256h2, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(crypto_sha256su0, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sha256su1, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(crypto_sha512h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sha512h2, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(crypto_sha512su0, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sha512su1, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(crypto_sm3tt1a, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sm3tt1b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sm3tt2a, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sm3tt2b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sm3partw1, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sm3partw2, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(crypto_sm4e, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(crypto_sm4ekey, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(crypto_rax1, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) -DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) - -DEF_HELPER_FLAGS_5(gvec_qrdmlah_s16, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_qrdmlsh_s16, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_qrdmlah_s32, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_qrdmlsh_s32, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(sve2_sqrdmlah_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlah_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlah_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlah_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_sdot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_udot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sdot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_udot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_usdot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_sdot_idx_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_udot_idx_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sdot_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_udot_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sudot_idx_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_usdot_idx_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_fcaddh, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fcadds, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fcaddd, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_6(gvec_fcmlah, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_6(gvec_fcmlah_idx, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_6(gvec_fcmlas, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_6(gvec_fcmlas_idx, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_6(gvec_fcmlad, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_sstoh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_sitos, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_ustoh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_uitos, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_tosszh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_tosizs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_touszh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_touizs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_vcvt_sf, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_uf, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rz_fs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rz_fu, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_vcvt_sh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_uh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hu, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_vcvt_sd, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_ud, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rz_ds, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rz_du, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sd, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ud, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ss, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_us, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vcvt_rm_uh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_vrint_rm_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vrint_rm_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_vrintx_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_vrintx_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_frecpe_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_frecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_frecpe_rpres_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_frecpe_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_frsqrte_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_frsqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_frsqrte_rpres_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_frsqrte_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_fcgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fcgt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fcgt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_fcge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fcge0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fcge0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_fceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fceq0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fceq0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_fcle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fcle0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fcle0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_fclt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fclt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_4(gvec_fclt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_ah_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_ah_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_ah_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fceq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fceq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fceq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fcge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fcge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fcge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fcgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fcgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fcgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_facge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_facge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_facge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_facgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_facgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_facgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fmin_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmin_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fmaxnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fminnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fminnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fminnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_recps_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_recps_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fmla_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmla_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fmls_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmls_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_vfma_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_vfma_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_vfma_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_ah_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_ah_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_ah_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_ftsmul_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_ftsmul_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_ftsmul_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fmul_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmul_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmul_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fmla_nf_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmla_nf_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fmls_nf_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmls_nf_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_6(gvec_fmla_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_6(gvec_fmla_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_6(gvec_fmla_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_6(gvec_fmls_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_6(gvec_fmls_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_6(gvec_fmls_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_uqadd_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqadd_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqadd_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqadd_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqadd_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqadd_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqadd_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqadd_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqsub_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqsub_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqsub_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uqsub_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqsub_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqsub_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqsub_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sqsub_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_usqadd_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_usqadd_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_usqadd_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_usqadd_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_suqadd_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_suqadd_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_suqadd_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_suqadd_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_fmlal_a32, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(gvec_fmlal_a64, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a32, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a64, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_2(frint32_s, TCG_CALL_NO_RWG, f32, f32, fpst) -DEF_HELPER_FLAGS_2(frint64_s, TCG_CALL_NO_RWG, f32, f32, fpst) -DEF_HELPER_FLAGS_2(frint32_d, TCG_CALL_NO_RWG, f64, f64, fpst) -DEF_HELPER_FLAGS_2(frint64_d, TCG_CALL_NO_RWG, f64, f64, fpst) - -DEF_HELPER_FLAGS_3(gvec_ceq0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_clt0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_clt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_cle0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_cle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_cgt0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_cgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_cge0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_cge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_smulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_smulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_smulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_smulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_umulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_umulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_umulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_umulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_sshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_sshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_ushl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_ushl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_pmul_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_pmull_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(neon_pmull_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_ssra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ssra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ssra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ssra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_usra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_usra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_usra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_usra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_srshr_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_srshr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_srshr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_srshr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_urshr_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_urshr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_urshr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_urshr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_srsra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_srsra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_srsra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_srsra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_ursra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ursra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ursra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ursra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_sri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_sri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_sri_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_sri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_sli_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_sli_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_sli_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_sli_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_sabd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_sabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_sabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_sabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_uabd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_saba_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_saba_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_saba_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_saba_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_uaba_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uaba_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uaba_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uaba_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_mul_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_mul_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_mul_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_mla_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_mla_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_mla_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_mls_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_mls_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_mls_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(neon_sqdmulh_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqdmulh_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(neon_sqrdmulh_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqrdmulh_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(neon_sqdmulh_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqdmulh_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(neon_sqrdmulh_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqrdmulh_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(neon_sqrdmlah_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqrdmlah_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(neon_sqrdmlsh_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(neon_sqrdmlsh_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(sve2_sqdmulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqdmulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqdmulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqdmulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(sve2_sqdmulh_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqdmulh_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqdmulh_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_6(sve2_fmlal_zzzw_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_6(sve2_fmlal_zzxw_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_4(gvec_xar_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_smmla_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_ummla_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_usmmla_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_6(gvec_bfdot, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_6(gvec_bfdot_idx, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_6(gvec_bfmmla, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, env, i32) - -DEF_HELPER_FLAGS_6(gvec_bfmlal, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_6(gvec_bfmlal_idx, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_sclamp_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sclamp_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sclamp_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sclamp_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_uclamp_b, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uclamp_h, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uclamp_s, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_uclamp_d, TCG_CALL_NO_RWG, - void, ptr, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_5(gvec_faddp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_faddp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_faddp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fmaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fminp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fmaxnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmaxnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_5(gvec_fminnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fminnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fminnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) - -DEF_HELPER_FLAGS_4(gvec_addp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_addp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_addp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_addp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_smaxp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_smaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_smaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_sminp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_sminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_sminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_umaxp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_umaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_umaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_4(gvec_uminp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(gvec_uminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) - -DEF_HELPER_FLAGS_3(gvec_urecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_3(gvec_ursqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +#include "tcg/helper.h" #ifdef TARGET_AARCH64 #include "tcg/helper-a64.h" diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h new file mode 100644 index 0000000000..80db7c2c37 --- /dev/null +++ b/target/arm/tcg/helper.h @@ -0,0 +1,1153 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +DEF_HELPER_FLAGS_1(sxtb16, TCG_CALL_NO_RWG_SE, i32, i32) +DEF_HELPER_FLAGS_1(uxtb16, TCG_CALL_NO_RWG_SE, i32, i32) + +DEF_HELPER_3(add_setq, i32, env, i32, i32) +DEF_HELPER_3(add_saturate, i32, env, i32, i32) +DEF_HELPER_3(sub_saturate, i32, env, i32, i32) +DEF_HELPER_3(add_usaturate, i32, env, i32, i32) +DEF_HELPER_3(sub_usaturate, i32, env, i32, i32) +DEF_HELPER_FLAGS_3(sdiv, TCG_CALL_NO_RWG, s32, env, s32, s32) +DEF_HELPER_FLAGS_3(udiv, TCG_CALL_NO_RWG, i32, env, i32, i32) +DEF_HELPER_FLAGS_1(rbit, TCG_CALL_NO_RWG_SE, i32, i32) + +#define PAS_OP(pfx) \ + DEF_HELPER_3(pfx ## add8, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## sub8, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## sub16, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## add16, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## addsubx, i32, i32, i32, ptr) \ + DEF_HELPER_3(pfx ## subaddx, i32, i32, i32, ptr) + +PAS_OP(s) +PAS_OP(u) +#undef PAS_OP + +#define PAS_OP(pfx) \ + DEF_HELPER_2(pfx ## add8, i32, i32, i32) \ + DEF_HELPER_2(pfx ## sub8, i32, i32, i32) \ + DEF_HELPER_2(pfx ## sub16, i32, i32, i32) \ + DEF_HELPER_2(pfx ## add16, i32, i32, i32) \ + DEF_HELPER_2(pfx ## addsubx, i32, i32, i32) \ + DEF_HELPER_2(pfx ## subaddx, i32, i32, i32) +PAS_OP(q) +PAS_OP(sh) +PAS_OP(uq) +PAS_OP(uh) +#undef PAS_OP + +DEF_HELPER_3(ssat, i32, env, i32, i32) +DEF_HELPER_3(usat, i32, env, i32, i32) +DEF_HELPER_3(ssat16, i32, env, i32, i32) +DEF_HELPER_3(usat16, i32, env, i32, i32) + +DEF_HELPER_FLAGS_2(usad8, TCG_CALL_NO_RWG_SE, i32, i32, i32) + +DEF_HELPER_FLAGS_3(sel_flags, TCG_CALL_NO_RWG_SE, + i32, i32, i32, i32) +DEF_HELPER_2(exception_internal, noreturn, env, i32) +DEF_HELPER_3(exception_with_syndrome, noreturn, env, i32, i32) +DEF_HELPER_4(exception_with_syndrome_el, noreturn, env, i32, i32, i32) +DEF_HELPER_2(exception_bkpt_insn, noreturn, env, i32) +DEF_HELPER_2(exception_swstep, noreturn, env, i32) +DEF_HELPER_2(exception_pc_alignment, noreturn, env, vaddr) +DEF_HELPER_1(setend, void, env) +DEF_HELPER_2(wfi, void, env, i32) +DEF_HELPER_1(wfe, void, env) +DEF_HELPER_2(wfit, void, env, i64) +DEF_HELPER_1(yield, void, env) +DEF_HELPER_1(pre_hvc, void, env) +DEF_HELPER_2(pre_smc, void, env, i32) +DEF_HELPER_1(vesb, void, env) + +DEF_HELPER_3(cpsr_write, void, env, i32, i32) +DEF_HELPER_2(cpsr_write_eret, void, env, i32) +DEF_HELPER_1(cpsr_read, i32, env) + +DEF_HELPER_3(v7m_msr, void, env, i32, i32) +DEF_HELPER_2(v7m_mrs, i32, env, i32) + +DEF_HELPER_2(v7m_bxns, void, env, i32) +DEF_HELPER_2(v7m_blxns, void, env, i32) + +DEF_HELPER_3(v7m_tt, i32, env, i32, i32) + +DEF_HELPER_1(v7m_preserve_fp_state, void, env) + +DEF_HELPER_2(v7m_vlstm, void, env, i32) +DEF_HELPER_2(v7m_vlldm, void, env, i32) + +DEF_HELPER_2(v8m_stackcheck, void, env, i32) + +DEF_HELPER_FLAGS_2(check_bxj_trap, TCG_CALL_NO_WG, void, env, i32) + +DEF_HELPER_4(access_check_cp_reg, cptr, env, i32, i32, i32) +DEF_HELPER_FLAGS_2(lookup_cp_reg, TCG_CALL_NO_RWG_SE, cptr, env, i32) +DEF_HELPER_FLAGS_2(tidcp_el0, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_FLAGS_2(tidcp_el1, TCG_CALL_NO_WG, void, env, i32) +DEF_HELPER_3(set_cp_reg, void, env, cptr, i32) +DEF_HELPER_2(get_cp_reg, i32, env, cptr) +DEF_HELPER_3(set_cp_reg64, void, env, cptr, i64) +DEF_HELPER_2(get_cp_reg64, i64, env, cptr) + +DEF_HELPER_2(get_r13_banked, i32, env, i32) +DEF_HELPER_3(set_r13_banked, void, env, i32, i32) + +DEF_HELPER_3(mrs_banked, i32, env, i32, i32) +DEF_HELPER_4(msr_banked, void, env, i32, i32, i32) + +DEF_HELPER_2(get_user_reg, i32, env, i32) +DEF_HELPER_3(set_user_reg, void, env, i32, i32) + +DEF_HELPER_FLAGS_1(rebuild_hflags_m32_newel, TCG_CALL_NO_RWG, void, env) +DEF_HELPER_FLAGS_2(rebuild_hflags_m32, TCG_CALL_NO_RWG, void, env, int) +DEF_HELPER_FLAGS_1(rebuild_hflags_a32_newel, TCG_CALL_NO_RWG, void, env) +DEF_HELPER_FLAGS_2(rebuild_hflags_a32, TCG_CALL_NO_RWG, void, env, int) +DEF_HELPER_FLAGS_2(rebuild_hflags_a64, TCG_CALL_NO_RWG, void, env, int) + +DEF_HELPER_FLAGS_5(probe_access, TCG_CALL_NO_WG, void, env, vaddr, i32, i32, i32) + +DEF_HELPER_1(vfp_get_fpscr, i32, env) +DEF_HELPER_2(vfp_set_fpscr, void, env, i32) + +DEF_HELPER_3(vfp_addh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_adds, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_addd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_subh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_subs, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_subd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_mulh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_muls, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_muld, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_divh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_divs, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_divd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_maxh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_maxs, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_maxd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_minh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_mins, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_mind, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_maxnumh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_maxnums, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_maxnumd, f64, f64, f64, fpst) +DEF_HELPER_3(vfp_minnumh, f16, f16, f16, fpst) +DEF_HELPER_3(vfp_minnums, f32, f32, f32, fpst) +DEF_HELPER_3(vfp_minnumd, f64, f64, f64, fpst) +DEF_HELPER_2(vfp_sqrth, f16, f16, fpst) +DEF_HELPER_2(vfp_sqrts, f32, f32, fpst) +DEF_HELPER_2(vfp_sqrtd, f64, f64, fpst) +DEF_HELPER_3(vfp_cmph, void, f16, f16, env) +DEF_HELPER_3(vfp_cmps, void, f32, f32, env) +DEF_HELPER_3(vfp_cmpd, void, f64, f64, env) +DEF_HELPER_3(vfp_cmpeh, void, f16, f16, env) +DEF_HELPER_3(vfp_cmpes, void, f32, f32, env) +DEF_HELPER_3(vfp_cmped, void, f64, f64, env) + +DEF_HELPER_2(vfp_fcvtds, f64, f32, fpst) +DEF_HELPER_2(vfp_fcvtsd, f32, f64, fpst) +DEF_HELPER_FLAGS_2(bfcvt, TCG_CALL_NO_RWG, i32, f32, fpst) +DEF_HELPER_FLAGS_2(bfcvt_pair, TCG_CALL_NO_RWG, i32, i64, fpst) + +DEF_HELPER_2(vfp_uitoh, f16, i32, fpst) +DEF_HELPER_2(vfp_uitos, f32, i32, fpst) +DEF_HELPER_2(vfp_uitod, f64, i32, fpst) +DEF_HELPER_2(vfp_sitoh, f16, i32, fpst) +DEF_HELPER_2(vfp_sitos, f32, i32, fpst) +DEF_HELPER_2(vfp_sitod, f64, i32, fpst) + +DEF_HELPER_2(vfp_touih, i32, f16, fpst) +DEF_HELPER_2(vfp_touis, i32, f32, fpst) +DEF_HELPER_2(vfp_touid, i32, f64, fpst) +DEF_HELPER_2(vfp_touizh, i32, f16, fpst) +DEF_HELPER_2(vfp_touizs, i32, f32, fpst) +DEF_HELPER_2(vfp_touizd, i32, f64, fpst) +DEF_HELPER_2(vfp_tosih, s32, f16, fpst) +DEF_HELPER_2(vfp_tosis, s32, f32, fpst) +DEF_HELPER_2(vfp_tosid, s32, f64, fpst) +DEF_HELPER_2(vfp_tosizh, s32, f16, fpst) +DEF_HELPER_2(vfp_tosizs, s32, f32, fpst) +DEF_HELPER_2(vfp_tosizd, s32, f64, fpst) + +DEF_HELPER_3(vfp_toshh_round_to_zero, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toslh_round_to_zero, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_touhh_round_to_zero, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toulh_round_to_zero, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toshs_round_to_zero, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_tosls_round_to_zero, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_touhs_round_to_zero, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_touls_round_to_zero, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_toshd_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tosld_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tosqd_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touhd_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tould_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touqd_round_to_zero, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touhh, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toshh, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toulh, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_toslh, i32, f16, i32, fpst) +DEF_HELPER_3(vfp_touqh, i64, f16, i32, fpst) +DEF_HELPER_3(vfp_tosqh, i64, f16, i32, fpst) +DEF_HELPER_3(vfp_toshs, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_tosls, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_tosqs, i64, f32, i32, fpst) +DEF_HELPER_3(vfp_touhs, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_touls, i32, f32, i32, fpst) +DEF_HELPER_3(vfp_touqs, i64, f32, i32, fpst) +DEF_HELPER_3(vfp_toshd, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tosld, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tosqd, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touhd, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_tould, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_touqd, i64, f64, i32, fpst) +DEF_HELPER_3(vfp_shtos, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_sltos, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_sqtos, f32, i64, i32, fpst) +DEF_HELPER_3(vfp_uhtos, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_ultos, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_uqtos, f32, i64, i32, fpst) +DEF_HELPER_3(vfp_shtod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_sltod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_sqtod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_uhtod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_ultod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_uqtod, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_shtoh, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_uhtoh, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_sltoh, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_ultoh, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_sqtoh, f16, i64, i32, fpst) +DEF_HELPER_3(vfp_uqtoh, f16, i64, i32, fpst) + +DEF_HELPER_3(vfp_shtos_round_to_nearest, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_sltos_round_to_nearest, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_uhtos_round_to_nearest, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_ultos_round_to_nearest, f32, i32, i32, fpst) +DEF_HELPER_3(vfp_shtod_round_to_nearest, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_sltod_round_to_nearest, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_uhtod_round_to_nearest, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_ultod_round_to_nearest, f64, i64, i32, fpst) +DEF_HELPER_3(vfp_shtoh_round_to_nearest, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_uhtoh_round_to_nearest, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_sltoh_round_to_nearest, f16, i32, i32, fpst) +DEF_HELPER_3(vfp_ultoh_round_to_nearest, f16, i32, i32, fpst) + +DEF_HELPER_FLAGS_2(set_rmode, TCG_CALL_NO_RWG, i32, i32, fpst) + +DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f32, TCG_CALL_NO_RWG, f32, f16, fpst, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f32_to_f16, TCG_CALL_NO_RWG, f16, f32, fpst, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f16_to_f64, TCG_CALL_NO_RWG, f64, f16, fpst, i32) +DEF_HELPER_FLAGS_3(vfp_fcvt_f64_to_f16, TCG_CALL_NO_RWG, f16, f64, fpst, i32) + +DEF_HELPER_4(vfp_muladdd, f64, f64, f64, f64, fpst) +DEF_HELPER_4(vfp_muladds, f32, f32, f32, f32, fpst) +DEF_HELPER_4(vfp_muladdh, f16, f16, f16, f16, fpst) + +DEF_HELPER_FLAGS_2(recpe_f16, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(recpe_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(recpe_rpres_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(recpe_f64, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_2(rsqrte_f16, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(rsqrte_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(rsqrte_rpres_f32, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(rsqrte_f64, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_1(recpe_u32, TCG_CALL_NO_RWG, i32, i32) +DEF_HELPER_FLAGS_1(rsqrte_u32, TCG_CALL_NO_RWG, i32, i32) +DEF_HELPER_FLAGS_4(neon_tbl, TCG_CALL_NO_RWG, i64, env, i32, i64, i64) + +DEF_HELPER_3(shl_cc, i32, env, i32, i32) +DEF_HELPER_3(shr_cc, i32, env, i32, i32) +DEF_HELPER_3(sar_cc, i32, env, i32, i32) +DEF_HELPER_3(ror_cc, i32, env, i32, i32) + +DEF_HELPER_FLAGS_2(rinth_exact, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(rints_exact, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(rintd_exact, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_2(rinth, TCG_CALL_NO_RWG, f16, f16, fpst) +DEF_HELPER_FLAGS_2(rints, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(rintd, TCG_CALL_NO_RWG, f64, f64, fpst) + +DEF_HELPER_FLAGS_2(vjcvt, TCG_CALL_NO_RWG, i32, f64, env) +DEF_HELPER_FLAGS_2(fjcvtzs, TCG_CALL_NO_RWG, i64, f64, fpst) + +DEF_HELPER_FLAGS_3(check_hcr_el2_trap, TCG_CALL_NO_WG, void, env, i32, i32) + +/* neon_helper.c */ +DEF_HELPER_2(neon_pmin_u8, i32, i32, i32) +DEF_HELPER_2(neon_pmin_s8, i32, i32, i32) +DEF_HELPER_2(neon_pmin_u16, i32, i32, i32) +DEF_HELPER_2(neon_pmin_s16, i32, i32, i32) +DEF_HELPER_2(neon_pmax_u8, i32, i32, i32) +DEF_HELPER_2(neon_pmax_s8, i32, i32, i32) +DEF_HELPER_2(neon_pmax_u16, i32, i32, i32) +DEF_HELPER_2(neon_pmax_s16, i32, i32, i32) + +DEF_HELPER_2(neon_shl_u16, i32, i32, i32) +DEF_HELPER_2(neon_shl_s16, i32, i32, i32) +DEF_HELPER_2(neon_rshl_u8, i32, i32, i32) +DEF_HELPER_2(neon_rshl_s8, i32, i32, i32) +DEF_HELPER_2(neon_rshl_u16, i32, i32, i32) +DEF_HELPER_2(neon_rshl_s16, i32, i32, i32) +DEF_HELPER_2(neon_rshl_u32, i32, i32, i32) +DEF_HELPER_2(neon_rshl_s32, i32, i32, i32) +DEF_HELPER_2(neon_rshl_u64, i64, i64, i64) +DEF_HELPER_2(neon_rshl_s64, i64, i64, i64) +DEF_HELPER_3(neon_qshl_u8, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_s8, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_u16, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_u32, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qshl_u64, i64, env, i64, i64) +DEF_HELPER_3(neon_qshl_s64, i64, env, i64, i64) +DEF_HELPER_3(neon_qshlu_s8, i32, env, i32, i32) +DEF_HELPER_3(neon_qshlu_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qshlu_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qshlu_s64, i64, env, i64, i64) +DEF_HELPER_3(neon_qrshl_u8, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_s8, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_u16, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_u32, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qrshl_u64, i64, env, i64, i64) +DEF_HELPER_3(neon_qrshl_s64, i64, env, i64, i64) +DEF_HELPER_FLAGS_5(neon_sqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_sqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(neon_uqrshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshli_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_uqshli_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(neon_sqshlui_d, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(gvec_srshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_srshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_srshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_srshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_urshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_urshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_urshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_urshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_2(neon_add_u8, i32, i32, i32) +DEF_HELPER_2(neon_add_u16, i32, i32, i32) +DEF_HELPER_2(neon_sub_u8, i32, i32, i32) +DEF_HELPER_2(neon_sub_u16, i32, i32, i32) +DEF_HELPER_2(neon_mul_u8, i32, i32, i32) +DEF_HELPER_2(neon_mul_u16, i32, i32, i32) + +DEF_HELPER_2(neon_tst_u8, i32, i32, i32) +DEF_HELPER_2(neon_tst_u16, i32, i32, i32) +DEF_HELPER_2(neon_tst_u32, i32, i32, i32) + +DEF_HELPER_1(neon_clz_u8, i32, i32) +DEF_HELPER_1(neon_clz_u16, i32, i32) +DEF_HELPER_1(neon_cls_s8, i32, i32) +DEF_HELPER_1(neon_cls_s16, i32, i32) +DEF_HELPER_1(neon_cls_s32, i32, i32) +DEF_HELPER_FLAGS_3(gvec_cnt_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_rbit_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_3(neon_qdmulh_s16, i32, env, i32, i32) +DEF_HELPER_3(neon_qrdmulh_s16, i32, env, i32, i32) +DEF_HELPER_4(neon_qrdmlah_s16, i32, env, i32, i32, i32) +DEF_HELPER_4(neon_qrdmlsh_s16, i32, env, i32, i32, i32) +DEF_HELPER_3(neon_qdmulh_s32, i32, env, i32, i32) +DEF_HELPER_3(neon_qrdmulh_s32, i32, env, i32, i32) +DEF_HELPER_4(neon_qrdmlah_s32, i32, env, s32, s32, s32) +DEF_HELPER_4(neon_qrdmlsh_s32, i32, env, s32, s32, s32) + +DEF_HELPER_1(neon_narrow_u8, i64, i64) +DEF_HELPER_1(neon_narrow_u16, i64, i64) +DEF_HELPER_2(neon_unarrow_sat8, i64, env, i64) +DEF_HELPER_2(neon_narrow_sat_u8, i64, env, i64) +DEF_HELPER_2(neon_narrow_sat_s8, i64, env, i64) +DEF_HELPER_2(neon_unarrow_sat16, i64, env, i64) +DEF_HELPER_2(neon_narrow_sat_u16, i64, env, i64) +DEF_HELPER_2(neon_narrow_sat_s16, i64, env, i64) +DEF_HELPER_2(neon_unarrow_sat32, i64, env, i64) +DEF_HELPER_2(neon_narrow_sat_u32, i64, env, i64) +DEF_HELPER_2(neon_narrow_sat_s32, i64, env, i64) +DEF_HELPER_1(neon_narrow_high_u8, i32, i64) +DEF_HELPER_1(neon_narrow_high_u16, i32, i64) +DEF_HELPER_1(neon_narrow_round_high_u8, i32, i64) +DEF_HELPER_1(neon_narrow_round_high_u16, i32, i64) +DEF_HELPER_1(neon_widen_u8, i64, i32) +DEF_HELPER_1(neon_widen_s8, i64, i32) +DEF_HELPER_1(neon_widen_u16, i64, i32) +DEF_HELPER_1(neon_widen_s16, i64, i32) + +DEF_HELPER_FLAGS_1(neon_addlp_s8, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_FLAGS_1(neon_addlp_s16, TCG_CALL_NO_RWG_SE, i64, i64) +DEF_HELPER_3(neon_addl_saturate_s32, i64, env, i64, i64) +DEF_HELPER_3(neon_addl_saturate_s64, i64, env, i64, i64) +DEF_HELPER_2(neon_abdl_u16, i64, i32, i32) +DEF_HELPER_2(neon_abdl_s16, i64, i32, i32) +DEF_HELPER_2(neon_abdl_u32, i64, i32, i32) +DEF_HELPER_2(neon_abdl_s32, i64, i32, i32) +DEF_HELPER_2(neon_abdl_u64, i64, i32, i32) +DEF_HELPER_2(neon_abdl_s64, i64, i32, i32) +DEF_HELPER_2(neon_mull_u8, i64, i32, i32) +DEF_HELPER_2(neon_mull_s8, i64, i32, i32) +DEF_HELPER_2(neon_mull_u16, i64, i32, i32) +DEF_HELPER_2(neon_mull_s16, i64, i32, i32) + +DEF_HELPER_1(neon_negl_u16, i64, i64) +DEF_HELPER_1(neon_negl_u32, i64, i64) + +DEF_HELPER_FLAGS_2(neon_qabs_s8, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(neon_qabs_s16, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(neon_qabs_s32, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(neon_qabs_s64, TCG_CALL_NO_RWG, i64, env, i64) +DEF_HELPER_FLAGS_2(neon_qneg_s8, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(neon_qneg_s16, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(neon_qneg_s32, TCG_CALL_NO_RWG, i32, env, i32) +DEF_HELPER_FLAGS_2(neon_qneg_s64, TCG_CALL_NO_RWG, i64, env, i64) + +DEF_HELPER_3(neon_ceq_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_cge_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_cgt_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_acge_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_acgt_f32, i32, i32, i32, fpst) +DEF_HELPER_3(neon_acge_f64, i64, i64, i64, fpst) +DEF_HELPER_3(neon_acgt_f64, i64, i64, i64, fpst) + +/* iwmmxt_helper.c */ +DEF_HELPER_2(iwmmxt_maddsq, i64, i64, i64) +DEF_HELPER_2(iwmmxt_madduq, i64, i64, i64) +DEF_HELPER_2(iwmmxt_sadb, i64, i64, i64) +DEF_HELPER_2(iwmmxt_sadw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_mulslw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_mulshw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_mululw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_muluhw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_macsw, i64, i64, i64) +DEF_HELPER_2(iwmmxt_macuw, i64, i64, i64) +DEF_HELPER_1(iwmmxt_setpsr_nz, i32, i64) + +#define DEF_IWMMXT_HELPER_SIZE_ENV(name) \ +DEF_HELPER_3(iwmmxt_##name##b, i64, env, i64, i64) \ +DEF_HELPER_3(iwmmxt_##name##w, i64, env, i64, i64) \ +DEF_HELPER_3(iwmmxt_##name##l, i64, env, i64, i64) \ + +DEF_IWMMXT_HELPER_SIZE_ENV(unpackl) +DEF_IWMMXT_HELPER_SIZE_ENV(unpackh) + +DEF_HELPER_2(iwmmxt_unpacklub, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackluw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklul, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhub, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhuw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhul, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklsb, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklsw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpacklsl, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhsb, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhsw, i64, env, i64) +DEF_HELPER_2(iwmmxt_unpackhsl, i64, env, i64) + +DEF_IWMMXT_HELPER_SIZE_ENV(cmpeq) +DEF_IWMMXT_HELPER_SIZE_ENV(cmpgtu) +DEF_IWMMXT_HELPER_SIZE_ENV(cmpgts) + +DEF_IWMMXT_HELPER_SIZE_ENV(mins) +DEF_IWMMXT_HELPER_SIZE_ENV(minu) +DEF_IWMMXT_HELPER_SIZE_ENV(maxs) +DEF_IWMMXT_HELPER_SIZE_ENV(maxu) + +DEF_IWMMXT_HELPER_SIZE_ENV(subn) +DEF_IWMMXT_HELPER_SIZE_ENV(addn) +DEF_IWMMXT_HELPER_SIZE_ENV(subu) +DEF_IWMMXT_HELPER_SIZE_ENV(addu) +DEF_IWMMXT_HELPER_SIZE_ENV(subs) +DEF_IWMMXT_HELPER_SIZE_ENV(adds) + +DEF_HELPER_3(iwmmxt_avgb0, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_avgb1, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_avgw0, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_avgw1, i64, env, i64, i64) + +DEF_HELPER_3(iwmmxt_align, i64, i64, i64, i32) +DEF_HELPER_4(iwmmxt_insr, i64, i64, i32, i32, i32) + +DEF_HELPER_1(iwmmxt_bcstb, i64, i32) +DEF_HELPER_1(iwmmxt_bcstw, i64, i32) +DEF_HELPER_1(iwmmxt_bcstl, i64, i32) + +DEF_HELPER_1(iwmmxt_addcb, i64, i64) +DEF_HELPER_1(iwmmxt_addcw, i64, i64) +DEF_HELPER_1(iwmmxt_addcl, i64, i64) + +DEF_HELPER_1(iwmmxt_msbb, i32, i64) +DEF_HELPER_1(iwmmxt_msbw, i32, i64) +DEF_HELPER_1(iwmmxt_msbl, i32, i64) + +DEF_HELPER_3(iwmmxt_srlw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_srll, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_srlq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sllw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_slll, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sllq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sraw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sral, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_sraq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_rorw, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_rorl, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_rorq, i64, env, i64, i32) +DEF_HELPER_3(iwmmxt_shufh, i64, env, i64, i32) + +DEF_HELPER_3(iwmmxt_packuw, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packul, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packuq, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packsw, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packsl, i64, env, i64, i64) +DEF_HELPER_3(iwmmxt_packsq, i64, env, i64, i64) + +DEF_HELPER_3(iwmmxt_muladdsl, i64, i64, i32, i32) +DEF_HELPER_3(iwmmxt_muladdsw, i64, i64, i32, i32) +DEF_HELPER_3(iwmmxt_muladdswl, i64, i64, i32, i32) + +DEF_HELPER_FLAGS_2(neon_unzip8, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_unzip16, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_qunzip8, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_qunzip16, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_qunzip32, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_zip8, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_zip16, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_qzip8, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_qzip16, TCG_CALL_NO_RWG, void, ptr, ptr) +DEF_HELPER_FLAGS_2(neon_qzip32, TCG_CALL_NO_RWG, void, ptr, ptr) + +DEF_HELPER_FLAGS_4(crypto_aese, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_aesd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(crypto_aesmc, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(crypto_aesimc, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(crypto_sha1su0, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sha1c, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sha1p, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sha1m, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(crypto_sha1h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(crypto_sha1su1, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(crypto_sha256h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sha256h2, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(crypto_sha256su0, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sha256su1, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(crypto_sha512h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sha512h2, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(crypto_sha512su0, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sha512su1, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(crypto_sm3tt1a, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sm3tt1b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sm3tt2a, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sm3tt2b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sm3partw1, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sm3partw2, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(crypto_sm4e, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(crypto_sm4ekey, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(crypto_rax1, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(crc32, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) +DEF_HELPER_FLAGS_3(crc32c, TCG_CALL_NO_RWG_SE, i32, i32, i32, i32) + +DEF_HELPER_FLAGS_5(gvec_qrdmlah_s16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_qrdmlsh_s16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_qrdmlah_s32, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_qrdmlsh_s32, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve2_sqrdmlah_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlah_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlah_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlah_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_sdot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_udot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sdot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_udot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usdot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_sdot_idx_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_udot_idx_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sdot_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_udot_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sudot_idx_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usdot_idx_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fcaddh, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcadds, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcaddd, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(gvec_fcmlah, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fcmlah_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fcmlas, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fcmlas_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fcmlad, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_sstoh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_sitos, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_ustoh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_uitos, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_tosszh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_tosizs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_touszh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_touizs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_vcvt_sf, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_uf, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_fs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_fu, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_vcvt_sh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_uh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hs, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_hu, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_vcvt_sd, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_ud, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_ds, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rz_du, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sd, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ud, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_ss, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_us, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_sh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vcvt_rm_uh, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_vrint_rm_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vrint_rm_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_vrintx_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_vrintx_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_frecpe_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_rpres_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frecpe_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_frsqrte_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_rpres_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_frsqrte_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_fcgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcgt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcgt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_fcge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcge0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcge0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_fceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fceq0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fceq0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_fcle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcle0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fcle0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_fclt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fclt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(gvec_fclt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_fabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fceq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fceq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fceq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fcge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fcgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fcgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_facge_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_facge_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_facge_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_facgt_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_facgt_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_facgt_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmax_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmin_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmin_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmaxnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fminnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnum_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_recps_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_recps_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmla_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmla_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmls_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmls_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_vfma_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_vfma_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_vfma_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ah_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_ftsmul_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ftsmul_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ftsmul_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmul_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmla_nf_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmla_nf_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmls_nf_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmls_nf_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(gvec_fmla_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fmla_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fmla_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(gvec_fmls_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fmls_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_fmls_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_uqadd_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqadd_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqsub_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqsub_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqsub_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uqsub_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqsub_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqsub_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqsub_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sqsub_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usqadd_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usqadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usqadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usqadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_suqadd_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_suqadd_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_suqadd_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_suqadd_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fmlal_a32, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(gvec_fmlal_a64, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a32, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_5(gvec_fmlal_idx_a64, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_2(frint32_s, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(frint64_s, TCG_CALL_NO_RWG, f32, f32, fpst) +DEF_HELPER_FLAGS_2(frint32_d, TCG_CALL_NO_RWG, f64, f64, fpst) +DEF_HELPER_FLAGS_2(frint64_d, TCG_CALL_NO_RWG, f64, f64, fpst) + +DEF_HELPER_FLAGS_3(gvec_ceq0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ceq0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_clt0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_clt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_cle0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_cle0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_cgt0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_cgt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_cge0_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_cge0_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_smulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_umulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_sshl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_ushl_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_ushl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_pmul_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_pmull_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(neon_pmull_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_ssra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ssra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ssra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ssra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_usra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_usra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_usra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_usra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_srshr_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_srshr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_srshr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_srshr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_urshr_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_urshr_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_urshr_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_urshr_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_srsra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_srsra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_srsra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_srsra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_ursra_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ursra_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ursra_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ursra_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_sri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_sri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_sri_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_sri_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_sli_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_sli_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_sli_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_sli_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_sabd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_uabd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uabd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uabd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uabd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_saba_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_saba_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_saba_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_saba_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_uaba_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uaba_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uaba_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uaba_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_mul_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_mul_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_mul_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_mla_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_mla_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_mla_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_mls_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_mls_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_mls_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqdmulh_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqdmulh_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqrdmulh_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrdmulh_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqdmulh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqdmulh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqrdmulh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrdmulh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqrdmlah_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrdmlah_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(neon_sqrdmlsh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(neon_sqrdmlsh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2_sqdmulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqdmulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqdmulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqdmulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2_sqdmulh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqdmulh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqdmulh_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2_sqrdmulh_idx_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sve2_fmlal_zzzw_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(sve2_fmlal_zzxw_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(gvec_xar_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_smmla_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_ummla_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usmmla_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(gvec_bfdot, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(gvec_bfdot_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_6(gvec_bfmmla, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_6(gvec_bfmlal, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_bfmlal_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_sclamp_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sclamp_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_uclamp_b, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uclamp_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uclamp_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_uclamp_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_faddp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_faddp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fmaxnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(gvec_fminnump_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnump_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnump_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_4(gvec_addp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_addp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_addp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_addp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_smaxp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_smaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_sminp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_sminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_umaxp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umaxp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_umaxp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(gvec_uminp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uminp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(gvec_uminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(gvec_urecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(gvec_ursqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) From f9fba6ddfc854f427e00d62c1e4888ce9a1a7c34 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:30 -0700 Subject: [PATCH 0911/2760] target/arm/debug_helper: only include common helpers Avoid pulling helper.h which contains TARGET_AARCH64. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-17-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/debug_helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index de7999f6a9..cad0a5db70 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -11,10 +11,12 @@ #include "internals.h" #include "cpu-features.h" #include "cpregs.h" -#include "exec/helper-proto.h" #include "exec/watchpoint.h" #include "system/tcg.h" +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" + #ifdef CONFIG_TCG /* Return the Exception Level targeted by debug exceptions. */ static int arm_debug_target_el(CPUARMState *env) From 61dcfb2ef255d17a23262170457568af52fb0516 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:31 -0700 Subject: [PATCH 0912/2760] target/arm/debug_helper: remove target_ulong MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-18-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/debug_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index cad0a5db70..69fb1d0d9f 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -380,7 +380,7 @@ bool arm_debug_check_breakpoint(CPUState *cs) { ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; - target_ulong pc; + vaddr pc; int n; /* From 64961a801500035b9a039c474b6225e0cfd3f44e Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:32 -0700 Subject: [PATCH 0913/2760] target/arm/debug_helper: compile file twice (user, system) Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-19-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index de214fe5d5..48a6bf5935 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -1,7 +1,6 @@ arm_ss = ss.source_set() arm_common_ss = ss.source_set() arm_ss.add(files( - 'debug_helper.c', 'gdbstub.c', 'helper.c', 'vfp_fpscr.c', @@ -29,11 +28,18 @@ arm_system_ss.add(files( arm_user_ss = ss.source_set() arm_user_ss.add(files('cpu.c')) arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files( - 'cpu32-stubs.c')) + 'cpu32-stubs.c', +)) +arm_user_ss.add(files( + 'debug_helper.c', +)) arm_common_system_ss.add(files('cpu.c'), capstone) arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files( 'cpu32-stubs.c')) +arm_common_system_ss.add(files( + 'debug_helper.c', +)) subdir('hvf') From 2fdfdeb2f2a46ee5b96918dfd18cdeedf3bba013 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:33 -0700 Subject: [PATCH 0914/2760] target/arm/helper: restrict include to common helpers Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-20-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 4a2d1ecbfe..3795dccd16 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -12,7 +12,6 @@ #include "cpu.h" #include "internals.h" #include "cpu-features.h" -#include "exec/helper-proto.h" #include "exec/page-protection.h" #include "exec/mmap-lock.h" #include "qemu/main-loop.h" @@ -35,6 +34,9 @@ #include "cpregs.h" #include "target/arm/gtimer.h" +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" + #define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ static void switch_mode(CPUARMState *env, int mode); From d626a26dd1ce0ede8e5e5a1c73f089f5e711f9e8 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:34 -0700 Subject: [PATCH 0915/2760] target/arm/helper: replace target_ulong by vaddr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-21-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 3795dccd16..d2607107eb 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -10621,7 +10621,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) ARMCPU *cpu = ARM_CPU(cs); CPUARMState *env = &cpu->env; unsigned int new_el = env->exception.target_el; - target_ulong addr = env->cp15.vbar_el[new_el]; + vaddr addr = env->cp15.vbar_el[new_el]; unsigned int new_mode = aarch64_pstate_mode(new_el, true); unsigned int old_mode; unsigned int cur_el = arm_current_el(env); From 8eb048210e150d9f4fb7225f598de605a3633f67 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:35 -0700 Subject: [PATCH 0916/2760] target/arm/helper: expose aarch64 cpu registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit associated define_arm_cp_regs are guarded by "cpu_isar_feature(aa64_*)", so it's safe to expose that code for arm target (32 bit). Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-22-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index d2607107eb..92a975bbf7 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6590,7 +6590,6 @@ static const ARMCPRegInfo zcr_reginfo[] = { .writefn = zcr_write, .raw_writefn = raw_write }, }; -#ifdef TARGET_AARCH64 static CPAccessResult access_tpidr2(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -6824,7 +6823,6 @@ static const ARMCPRegInfo nmi_reginfo[] = { .writefn = aa64_allint_write, .readfn = aa64_allint_read, .resetfn = arm_cp_reset_ignore }, }; -#endif /* TARGET_AARCH64 */ static void define_pmu_regs(ARMCPU *cpu) { @@ -7016,7 +7014,6 @@ static const ARMCPRegInfo lor_reginfo[] = { .type = ARM_CP_CONST, .resetvalue = 0 }, }; -#ifdef TARGET_AARCH64 static CPAccessResult access_pauth(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -7509,8 +7506,6 @@ static const ARMCPRegInfo nv2_reginfo[] = { .fieldoffset = offsetof(CPUARMState, cp15.vncr_el2) }, }; -#endif /* TARGET_AARCH64 */ - static CPAccessResult access_predinv(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) { @@ -8951,7 +8946,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_one_arm_cp_reg(cpu, &hcrx_el2_reginfo); } -#ifdef TARGET_AARCH64 if (cpu_isar_feature(aa64_sme, cpu)) { define_arm_cp_regs(cpu, sme_reginfo); } @@ -9012,7 +9006,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_nmi, cpu)) { define_arm_cp_regs(cpu, nmi_reginfo); } -#endif if (cpu_isar_feature(any_predinv, cpu)) { define_arm_cp_regs(cpu, predinv_reginfo); From 15e9ed7d7c376175285fe7694ef99b0a4c90f5b2 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:36 -0700 Subject: [PATCH 0917/2760] target/arm/helper: remove remaining TARGET_AARCH64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit They were hiding aarch64_sve_narrow_vq and aarch64_sve_change_el, which we can expose safely. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-23-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 92a975bbf7..aae8554e8f 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -29,6 +29,7 @@ #include "qemu/guest-random.h" #ifdef CONFIG_TCG #include "accel/tcg/probe.h" +#include "accel/tcg/getpc.h" #include "semihosting/common-semi.h" #endif #include "cpregs.h" @@ -6565,9 +6566,7 @@ static void zcr_write(CPUARMState *env, const ARMCPRegInfo *ri, */ new_len = sve_vqm1_for_el(env, cur_el); if (new_len < old_len) { -#ifdef TARGET_AARCH64 aarch64_sve_narrow_vq(env, new_len + 1); -#endif } } @@ -10625,9 +10624,7 @@ static void arm_cpu_do_interrupt_aarch64(CPUState *cs) * Note that new_el can never be 0. If cur_el is 0, then * el0_a64 is is_a64(), else el0_a64 is ignored. */ -#ifdef TARGET_AARCH64 aarch64_sve_change_el(env, cur_el, new_el, is_a64(env)); -#endif } if (cur_el < new_el) { @@ -11418,7 +11415,6 @@ ARMMMUIdx arm_mmu_idx(CPUARMState *env) return arm_mmu_idx_el(env, arm_current_el(env)); } -#ifdef TARGET_AARCH64 /* * The manual says that when SVE is enabled and VQ is widened the * implementation is allowed to zero the previously inaccessible @@ -11530,12 +11526,9 @@ void aarch64_sve_change_el(CPUARMState *env, int old_el, /* When changing vector length, clear inaccessible state. */ if (new_len < old_len) { -#ifdef TARGET_AARCH64 aarch64_sve_narrow_vq(env, new_len + 1); -#endif } } -#endif #ifndef CONFIG_USER_ONLY ARMSecuritySpace arm_security_space(CPUARMState *env) From 9cf590f214c77faa98f9d315519de0961187b8ff Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:37 -0700 Subject: [PATCH 0918/2760] target/arm/helper: compile file twice (user, system) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-24-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index 48a6bf5935..c8c80c3f96 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -2,7 +2,6 @@ arm_ss = ss.source_set() arm_common_ss = ss.source_set() arm_ss.add(files( 'gdbstub.c', - 'helper.c', 'vfp_fpscr.c', )) arm_ss.add(zlib) @@ -32,6 +31,7 @@ arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files( )) arm_user_ss.add(files( 'debug_helper.c', + 'helper.c', )) arm_common_system_ss.add(files('cpu.c'), capstone) @@ -39,6 +39,7 @@ arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files( 'cpu32-stubs.c')) arm_common_system_ss.add(files( 'debug_helper.c', + 'helper.c', )) subdir('hvf') From d4952244a9ce67a4fc85962ea50e8be0ec140e1e Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:38 -0700 Subject: [PATCH 0919/2760] target/arm/vfp_fpscr: compile file twice (user, system) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-25-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index c8c80c3f96..06d479570e 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -2,7 +2,6 @@ arm_ss = ss.source_set() arm_common_ss = ss.source_set() arm_ss.add(files( 'gdbstub.c', - 'vfp_fpscr.c', )) arm_ss.add(zlib) @@ -32,6 +31,7 @@ arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files( arm_user_ss.add(files( 'debug_helper.c', 'helper.c', + 'vfp_fpscr.c', )) arm_common_system_ss.add(files('cpu.c'), capstone) @@ -40,6 +40,7 @@ arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files( arm_common_system_ss.add(files( 'debug_helper.c', 'helper.c', + 'vfp_fpscr.c', )) subdir('hvf') From 89356e123bfe09459bf0c596d6dce660971f6541 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:39 -0700 Subject: [PATCH 0920/2760] target/arm/arch_dump: remove TARGET_AARCH64 conditionals MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Associated code is protected by cpu_isar_feature(aa64*) Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-26-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/arch_dump.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/target/arm/arch_dump.c b/target/arm/arch_dump.c index c40df4e7fd..1dd79849c1 100644 --- a/target/arm/arch_dump.c +++ b/target/arm/arch_dump.c @@ -143,7 +143,6 @@ static int aarch64_write_elf64_prfpreg(WriteCoreDumpFunction f, return 0; } -#ifdef TARGET_AARCH64 static off_t sve_zreg_offset(uint32_t vq, int n) { off_t off = sizeof(struct aarch64_user_sve_header); @@ -231,7 +230,6 @@ static int aarch64_write_elf64_sve(WriteCoreDumpFunction f, return 0; } -#endif int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, int cpuid, DumpState *s) @@ -273,11 +271,9 @@ int arm_cpu_write_elf64_note(WriteCoreDumpFunction f, CPUState *cs, return ret; } -#ifdef TARGET_AARCH64 if (cpu_isar_feature(aa64_sve, cpu)) { ret = aarch64_write_elf64_sve(f, env, cpuid, s); } -#endif return ret; } @@ -451,11 +447,9 @@ ssize_t cpu_get_note_size(int class, int machine, int nr_cpus) if (class == ELFCLASS64) { note_size = AARCH64_PRSTATUS_NOTE_SIZE; note_size += AARCH64_PRFPREG_NOTE_SIZE; -#ifdef TARGET_AARCH64 if (cpu_isar_feature(aa64_sve, cpu)) { note_size += AARCH64_SVE_NOTE_SIZE(&cpu->env); } -#endif } else { note_size = ARM_PRSTATUS_NOTE_SIZE; if (cpu_isar_feature(aa32_vfp_simd, cpu)) { From 55bb7a9ad097419b5406ed1e816339ed652f3017 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:40 -0700 Subject: [PATCH 0921/2760] target/arm/arch_dump: compile file once (system) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-27-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index 06d479570e..95a2b077dd 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -15,7 +15,6 @@ arm_ss.add(when: 'TARGET_AARCH64', if_true: files( arm_system_ss = ss.source_set() arm_common_system_ss = ss.source_set() arm_system_ss.add(files( - 'arch_dump.c', 'arm-powerctl.c', 'arm-qmp-cmds.c', 'cortex-regs.c', @@ -38,6 +37,7 @@ arm_common_system_ss.add(files('cpu.c'), capstone) arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files( 'cpu32-stubs.c')) arm_common_system_ss.add(files( + 'arch_dump.c', 'debug_helper.c', 'helper.c', 'vfp_fpscr.c', From 262a5ce86ebfd7975ed6b5075a512d33e0ef1d1d Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:41 -0700 Subject: [PATCH 0922/2760] target/arm/arm-powerctl: compile file once (system) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-28-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index 95a2b077dd..7db573f4a9 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -15,7 +15,6 @@ arm_ss.add(when: 'TARGET_AARCH64', if_true: files( arm_system_ss = ss.source_set() arm_common_system_ss = ss.source_set() arm_system_ss.add(files( - 'arm-powerctl.c', 'arm-qmp-cmds.c', 'cortex-regs.c', 'machine.c', @@ -38,6 +37,7 @@ arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files( 'cpu32-stubs.c')) arm_common_system_ss.add(files( 'arch_dump.c', + 'arm-powerctl.c', 'debug_helper.c', 'helper.c', 'vfp_fpscr.c', From 7d5df02e7a5a8cd3673850db8eace624314a363c Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:42 -0700 Subject: [PATCH 0923/2760] target/arm/cortex-regs: compile file once (system) Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-29-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index 7db573f4a9..6e0327b6f5 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -16,7 +16,6 @@ arm_system_ss = ss.source_set() arm_common_system_ss = ss.source_set() arm_system_ss.add(files( 'arm-qmp-cmds.c', - 'cortex-regs.c', 'machine.c', 'ptw.c', )) @@ -38,6 +37,7 @@ arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files( arm_common_system_ss.add(files( 'arch_dump.c', 'arm-powerctl.c', + 'cortex-regs.c', 'debug_helper.c', 'helper.c', 'vfp_fpscr.c', From 03bc7858fd87fc622b2671b319c2de8aad7aefd5 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:43 -0700 Subject: [PATCH 0924/2760] target/arm/ptw: replace target_ulong with int64_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sextract64 returns a signed value. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-30-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 89979c07e5..68ec3f5e75 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -1660,7 +1660,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, uint64_t ttbr; hwaddr descaddr, indexmask, indexmask_grainsize; uint32_t tableattrs; - target_ulong page_size; + uint64_t page_size; uint64_t attrs; int32_t stride; int addrsize, inputsize, outputsize; @@ -1733,7 +1733,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, * validation to do here. */ if (inputsize < addrsize) { - target_ulong top_bits = sextract64(address, inputsize, + uint64_t top_bits = sextract64(address, inputsize, addrsize - inputsize); if (-top_bits != param.select) { /* The gap between the two regions is a Translation fault */ From bdfbf92c34acec649fa7d8cd0389c2b6629c88ad Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:44 -0700 Subject: [PATCH 0925/2760] target/arm/ptw: replace TARGET_AARCH64 by CONFIG_ATOMIC64 from arm_casq_ptw This function needs 64 bit compare exchange, so we hide implementation for hosts not supporting it (some 32 bit target, which don't run 64 bit guests anyway). Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-31-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/ptw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 68ec3f5e75..44170d831c 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -737,7 +737,7 @@ static uint64_t arm_casq_ptw(CPUARMState *env, uint64_t old_val, uint64_t new_val, S1Translate *ptw, ARMMMUFaultInfo *fi) { -#if defined(TARGET_AARCH64) && defined(CONFIG_TCG) +#if defined(CONFIG_ATOMIC64) && defined(CONFIG_TCG) uint64_t cur_val; void *host = ptw->out_host; From aeacf43ae6f59139ab16adad7cd8c8e9f443536d Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:45 -0700 Subject: [PATCH 0926/2760] target/arm/ptw: compile file once (system) Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-32-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index 6e0327b6f5..151184da71 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -17,7 +17,6 @@ arm_common_system_ss = ss.source_set() arm_system_ss.add(files( 'arm-qmp-cmds.c', 'machine.c', - 'ptw.c', )) arm_user_ss = ss.source_set() @@ -40,6 +39,7 @@ arm_common_system_ss.add(files( 'cortex-regs.c', 'debug_helper.c', 'helper.c', + 'ptw.c', 'vfp_fpscr.c', )) From f86d42205c2eba2486fd305679f5963bb97b0371 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:46 -0700 Subject: [PATCH 0927/2760] target/arm/meson: accelerator files are not needed in user mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-33-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index 151184da71..29a36fb3c5 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -5,9 +5,6 @@ arm_ss.add(files( )) arm_ss.add(zlib) -arm_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c'), if_false: files('kvm-stub.c')) -arm_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c')) - arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'cpu64.c', 'gdbstub64.c')) @@ -18,6 +15,8 @@ arm_system_ss.add(files( 'arm-qmp-cmds.c', 'machine.c', )) +arm_system_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c'), if_false: files('kvm-stub.c')) +arm_system_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c')) arm_user_ss = ss.source_set() arm_user_ss.add(files('cpu.c')) From cee0762c2b6236639979ba42a179d9d015057d94 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:47 -0700 Subject: [PATCH 0928/2760] target/arm/kvm-stub: compile file once (system) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-34-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index 29a36fb3c5..bb1c09676d 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -15,7 +15,7 @@ arm_system_ss.add(files( 'arm-qmp-cmds.c', 'machine.c', )) -arm_system_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c'), if_false: files('kvm-stub.c')) +arm_system_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c')) arm_system_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c')) arm_user_ss = ss.source_set() @@ -32,6 +32,7 @@ arm_user_ss.add(files( arm_common_system_ss.add(files('cpu.c'), capstone) arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files( 'cpu32-stubs.c')) +arm_common_system_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) arm_common_system_ss.add(files( 'arch_dump.c', 'arm-powerctl.c', From d31eaa5bdbd5cf05d8313beb7149c33c4a6d750e Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:48 -0700 Subject: [PATCH 0929/2760] target/arm/machine: reduce migration include to avoid target specific definitions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-35-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/machine.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/machine.c b/target/arm/machine.c index 978249fb71..f7956898fa 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -6,7 +6,8 @@ #include "kvm_arm.h" #include "internals.h" #include "cpu-features.h" -#include "migration/cpu.h" +#include "migration/qemu-file-types.h" +#include "migration/vmstate.h" #include "target/arm/gtimer.h" static bool vfp_needed(void *opaque) From cb9f95996a5d2941da5a41cc168145aca3051a55 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:49 -0700 Subject: [PATCH 0930/2760] target/arm/machine: remove TARGET_AARCH64 from migration state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This exposes two new subsections for arm: vmstate_sve and vmstate_za. Those sections have a ".needed" callback, which already allow to skip them when not needed. vmstate_sve .needed is checking cpu_isar_feature(aa64_sve, cpu). vmstate_za .needed is checking ZA flag in cpu->env.svcr. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-36-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/machine.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/arm/machine.c b/target/arm/machine.c index f7956898fa..868246a98c 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -241,7 +241,6 @@ static const VMStateDescription vmstate_iwmmxt = { } }; -#ifdef TARGET_AARCH64 /* The expression ARM_MAX_VQ - 2 is 0 for pure AArch32 build, * and ARMPredicateReg is actively empty. This triggers errors * in the expansion of the VMSTATE macros. @@ -321,7 +320,6 @@ static const VMStateDescription vmstate_za = { VMSTATE_END_OF_LIST() } }; -#endif /* AARCH64 */ static bool serror_needed(void *opaque) { @@ -1102,10 +1100,8 @@ const VMStateDescription vmstate_arm_cpu = { &vmstate_pmsav7, &vmstate_pmsav8, &vmstate_m_security, -#ifdef TARGET_AARCH64 &vmstate_sve, &vmstate_za, -#endif &vmstate_serror, &vmstate_irq_line_state, &vmstate_wfxt_timer, From 1dfe5a0c2e3da5d315cb45dbd7336bf20ea9a0ae Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:50 -0700 Subject: [PATCH 0931/2760] target/arm/machine: move cpu_post_load kvm bits to kvm_arm_cpu_post_load function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-37-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/kvm.c | 13 ++++++++++++- target/arm/kvm_arm.h | 4 +++- target/arm/machine.c | 8 +------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 82668d6438..a2791aa866 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -938,13 +938,24 @@ void kvm_arm_cpu_pre_save(ARMCPU *cpu) } } -void kvm_arm_cpu_post_load(ARMCPU *cpu) +bool kvm_arm_cpu_post_load(ARMCPU *cpu) { + if (!write_list_to_kvmstate(cpu, KVM_PUT_FULL_STATE)) { + return false; + } + /* Note that it's OK for the TCG side not to know about + * every register in the list; KVM is authoritative if + * we're using it. + */ + write_list_to_cpustate(cpu); + /* KVM virtual time adjustment */ if (cpu->kvm_adjvtime) { cpu->kvm_vtime = *kvm_arm_get_cpreg_ptr(cpu, KVM_REG_ARM_TIMER_CNT); cpu->kvm_vtime_dirty = true; } + + return true; } void kvm_arm_reset_vcpu(ARMCPU *cpu) diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index b638e09a68..c4178d1327 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -83,8 +83,10 @@ void kvm_arm_cpu_pre_save(ARMCPU *cpu); * @cpu: ARMCPU * * Called from cpu_post_load() to update KVM CPU state from the cpreg list. + * + * Returns: true on success, or false if write_list_to_kvmstate failed. */ -void kvm_arm_cpu_post_load(ARMCPU *cpu); +bool kvm_arm_cpu_post_load(ARMCPU *cpu); /** * kvm_arm_reset_vcpu: diff --git a/target/arm/machine.c b/target/arm/machine.c index 868246a98c..e442d48524 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -976,15 +976,9 @@ static int cpu_post_load(void *opaque, int version_id) } if (kvm_enabled()) { - if (!write_list_to_kvmstate(cpu, KVM_PUT_FULL_STATE)) { + if (!kvm_arm_cpu_post_load(cpu)) { return -1; } - /* Note that it's OK for the TCG side not to know about - * every register in the list; KVM is authoritative if - * we're using it. - */ - write_list_to_cpustate(cpu); - kvm_arm_cpu_post_load(cpu); } else { if (!write_list_to_cpustate(cpu)) { return -1; From 1388256782014a7f6c730332f3561c6e1f94cc80 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:51 -0700 Subject: [PATCH 0932/2760] target/arm/kvm-stub: add missing stubs Those become needed once kvm_enabled can't be known at compile time. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-38-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/kvm-stub.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c index 4806365cdc..34e57fab01 100644 --- a/target/arm/kvm-stub.c +++ b/target/arm/kvm-stub.c @@ -109,3 +109,13 @@ void arm_cpu_kvm_set_irq(void *arm_cpu, int irq, int level) { g_assert_not_reached(); } + +void kvm_arm_cpu_pre_save(ARMCPU *cpu) +{ + g_assert_not_reached(); +} + +bool kvm_arm_cpu_post_load(ARMCPU *cpu) +{ + g_assert_not_reached(); +} From 2c5058cc1eb77ed5340448aa6dea13ae4f4f1593 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:52 -0700 Subject: [PATCH 0933/2760] target/arm/machine: compile file once (system) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-39-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index bb1c09676d..b404fa5486 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -13,7 +13,6 @@ arm_system_ss = ss.source_set() arm_common_system_ss = ss.source_set() arm_system_ss.add(files( 'arm-qmp-cmds.c', - 'machine.c', )) arm_system_ss.add(when: 'CONFIG_KVM', if_true: files('hyp_gdbstub.c', 'kvm.c')) arm_system_ss.add(when: 'CONFIG_HVF', if_true: files('hyp_gdbstub.c')) @@ -39,6 +38,7 @@ arm_common_system_ss.add(files( 'cortex-regs.c', 'debug_helper.c', 'helper.c', + 'machine.c', 'ptw.c', 'vfp_fpscr.c', )) From 368b42f6dd64521b9282ddefa9e267eca621271c Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:53 -0700 Subject: [PATCH 0934/2760] target/arm/tcg/vec_internal: use forward declaration for CPUARMState Needed so this header can be included without requiring cpu.h. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-40-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/vec_internal.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index 6b93b5aeb9..c02f9c37f8 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -22,6 +22,8 @@ #include "fpu/softfloat.h" +typedef struct CPUArchState CPUARMState; + /* * Note that vector data is stored in host-endian 64-bit chunks, * so addressing units smaller than that needs a host-endian fixup. From 9f8d002499e06f60878f803cb6ad70f1220a3ce4 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:54 -0700 Subject: [PATCH 0935/2760] target/arm/tcg/crypto_helper: compile file once Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-41-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/crypto_helper.c | 6 ++++-- target/arm/tcg/meson.build | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/crypto_helper.c b/target/arm/tcg/crypto_helper.c index 7cadd61e12..3428bd1bf0 100644 --- a/target/arm/tcg/crypto_helper.c +++ b/target/arm/tcg/crypto_helper.c @@ -10,14 +10,16 @@ */ #include "qemu/osdep.h" +#include "qemu/bitops.h" -#include "cpu.h" -#include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" #include "crypto/aes-round.h" #include "crypto/sm4.h" #include "vec_internal.h" +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" + union CRYPTO_STATE { uint8_t bytes[16]; uint32_t words[4]; diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index dd12ccedb1..2f73eefe38 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -30,7 +30,6 @@ arm_ss.add(files( 'translate-mve.c', 'translate-neon.c', 'translate-vfp.c', - 'crypto_helper.c', 'hflags.c', 'iwmmxt_helper.c', 'm_helper.c', @@ -63,3 +62,7 @@ arm_system_ss.add(files( arm_system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('cpu-v7m.c')) arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files('cpu-v7m.c')) + +arm_common_ss.add(files( + 'crypto_helper.c', +)) From 12ae629d231b01e065b60288e82573842654054c Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:55 -0700 Subject: [PATCH 0936/2760] target/arm/tcg/hflags: compile file twice (system, user) Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-42-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/hflags.c | 4 +++- target/arm/tcg/meson.build | 8 +++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index fd407a7b28..1ccec63bbd 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -9,11 +9,13 @@ #include "cpu.h" #include "internals.h" #include "cpu-features.h" -#include "exec/helper-proto.h" #include "exec/translation-block.h" #include "accel/tcg/cpu-ops.h" #include "cpregs.h" +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" + static inline bool fgt_svc(CPUARMState *env, int el) { /* diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 2f73eefe38..cee00b24cd 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -30,7 +30,6 @@ arm_ss.add(files( 'translate-mve.c', 'translate-neon.c', 'translate-vfp.c', - 'hflags.c', 'iwmmxt_helper.c', 'm_helper.c', 'mve_helper.c', @@ -66,3 +65,10 @@ arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files('cpu-v7m.c')) arm_common_ss.add(files( 'crypto_helper.c', )) + +arm_common_system_ss.add(files( + 'hflags.c', +)) +arm_user_ss.add(files( + 'hflags.c', +)) From d5f8252cd15bf0dc3ac458e7e78427905966cfbf Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:56 -0700 Subject: [PATCH 0937/2760] target/arm/tcg/iwmmxt_helper: compile file twice (system, user) Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-43-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/iwmmxt_helper.c | 4 +++- target/arm/tcg/meson.build | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/iwmmxt_helper.c b/target/arm/tcg/iwmmxt_helper.c index 610b1b2103..ba054b6b4d 100644 --- a/target/arm/tcg/iwmmxt_helper.c +++ b/target/arm/tcg/iwmmxt_helper.c @@ -22,7 +22,9 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/helper-proto.h" + +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" /* iwMMXt macros extracted from GNU gdb. */ diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index cee00b24cd..02dfe768c5 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -30,7 +30,6 @@ arm_ss.add(files( 'translate-mve.c', 'translate-neon.c', 'translate-vfp.c', - 'iwmmxt_helper.c', 'm_helper.c', 'mve_helper.c', 'neon_helper.c', @@ -68,7 +67,9 @@ arm_common_ss.add(files( arm_common_system_ss.add(files( 'hflags.c', + 'iwmmxt_helper.c', )) arm_user_ss.add(files( 'hflags.c', + 'iwmmxt_helper.c', )) From b586c86a8ea1bbf2bc7070153b38b8c75ff8b979 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:57 -0700 Subject: [PATCH 0938/2760] target/arm/tcg/neon_helper: compile file twice (system, user) Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-44-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/meson.build | 3 ++- target/arm/tcg/neon_helper.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 02dfe768c5..af786196d2 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -32,7 +32,6 @@ arm_ss.add(files( 'translate-vfp.c', 'm_helper.c', 'mve_helper.c', - 'neon_helper.c', 'op_helper.c', 'tlb_helper.c', 'vec_helper.c', @@ -68,8 +67,10 @@ arm_common_ss.add(files( arm_common_system_ss.add(files( 'hflags.c', 'iwmmxt_helper.c', + 'neon_helper.c', )) arm_user_ss.add(files( 'hflags.c', 'iwmmxt_helper.c', + 'neon_helper.c', )) diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index e2cc7cf4ee..2cc8241f1e 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -9,11 +9,13 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/helper-proto.h" #include "tcg/tcg-gvec-desc.h" #include "fpu/softfloat.h" #include "vec_internal.h" +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" + #define SIGNBIT (uint32_t)0x80000000 #define SIGNBIT64 ((uint64_t)1 << 63) From cbf565b00fb350eb2d2b79fce437ee88060fd3f9 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:58 -0700 Subject: [PATCH 0939/2760] target/arm/tcg/tlb_helper: compile file twice (system, user) Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-45-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/meson.build | 3 ++- target/arm/tcg/tlb_helper.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index af786196d2..49c8f4390a 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -33,7 +33,6 @@ arm_ss.add(files( 'm_helper.c', 'mve_helper.c', 'op_helper.c', - 'tlb_helper.c', 'vec_helper.c', 'tlb-insns.c', 'arith_helper.c', @@ -68,9 +67,11 @@ arm_common_system_ss.add(files( 'hflags.c', 'iwmmxt_helper.c', 'neon_helper.c', + 'tlb_helper.c', )) arm_user_ss.add(files( 'hflags.c', 'iwmmxt_helper.c', 'neon_helper.c', + 'tlb_helper.c', )) diff --git a/target/arm/tcg/tlb_helper.c b/target/arm/tcg/tlb_helper.c index d9e6c827d4..23c72a99f5 100644 --- a/target/arm/tcg/tlb_helper.c +++ b/target/arm/tcg/tlb_helper.c @@ -9,8 +9,9 @@ #include "cpu.h" #include "internals.h" #include "cpu-features.h" -#include "exec/helper-proto.h" +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" /* * Returns true if the stage 1 translation regime is using LPAE format page From c04f6b5908bb1eff09955e89c239ff0c03de4754 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:04:59 -0700 Subject: [PATCH 0940/2760] target/arm/helper: restrict define_tlb_insn_regs to system target Allows to include target/arm/tcg/tlb-insns.c only for system targets. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-46-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/arm/helper.c b/target/arm/helper.c index aae8554e8f..7631210287 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7764,7 +7764,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, not_v8_cp_reginfo); } +#ifndef CONFIG_USER_ONLY define_tlb_insn_regs(cpu); +#endif if (arm_feature(env, ARM_FEATURE_V6)) { /* The ID registers all have impdef reset values */ From 31f4a08971d84bf97cf598a67d95ba8338adad1e Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:05:00 -0700 Subject: [PATCH 0941/2760] target/arm/tcg/tlb-insns: compile file once (system) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit aarch64 specific code is guarded by cpu_isar_feature(aa64*), so it's safe to expose it. Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250512180502.2395029-47-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/meson.build | 2 +- target/arm/tcg/tlb-insns.c | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 49c8f4390a..5d32658540 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -34,7 +34,6 @@ arm_ss.add(files( 'mve_helper.c', 'op_helper.c', 'vec_helper.c', - 'tlb-insns.c', 'arith_helper.c', 'vfp_helper.c', )) @@ -68,6 +67,7 @@ arm_common_system_ss.add(files( 'iwmmxt_helper.c', 'neon_helper.c', 'tlb_helper.c', + 'tlb-insns.c', )) arm_user_ss.add(files( 'hflags.c', diff --git a/target/arm/tcg/tlb-insns.c b/target/arm/tcg/tlb-insns.c index 0407ad5542..95c26c6d46 100644 --- a/target/arm/tcg/tlb-insns.c +++ b/target/arm/tcg/tlb-insns.c @@ -35,7 +35,6 @@ static CPAccessResult access_ttlbis(CPUARMState *env, const ARMCPRegInfo *ri, return CP_ACCESS_OK; } -#ifdef TARGET_AARCH64 /* Check for traps from EL1 due to HCR_EL2.TTLB or TTLBOS. */ static CPAccessResult access_ttlbos(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) @@ -46,7 +45,6 @@ static CPAccessResult access_ttlbos(CPUARMState *env, const ARMCPRegInfo *ri, } return CP_ACCESS_OK; } -#endif /* IS variants of TLB operations must affect all cores */ static void tlbiall_is_write(CPUARMState *env, const ARMCPRegInfo *ri, @@ -802,7 +800,6 @@ static const ARMCPRegInfo tlbi_el3_cp_reginfo[] = { .writefn = tlbi_aa64_vae3_write }, }; -#ifdef TARGET_AARCH64 typedef struct { uint64_t base; uint64_t length; @@ -1270,8 +1267,6 @@ static const ARMCPRegInfo tlbi_rme_reginfo[] = { .writefn = tlbi_aa64_paallos_write }, }; -#endif - void define_tlb_insn_regs(ARMCPU *cpu) { CPUARMState *env = &cpu->env; @@ -1299,7 +1294,6 @@ void define_tlb_insn_regs(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_EL3)) { define_arm_cp_regs(cpu, tlbi_el3_cp_reginfo); } -#ifdef TARGET_AARCH64 if (cpu_isar_feature(aa64_tlbirange, cpu)) { define_arm_cp_regs(cpu, tlbirange_reginfo); } @@ -1309,5 +1303,4 @@ void define_tlb_insn_regs(ARMCPU *cpu) if (cpu_isar_feature(aa64_rme, cpu)) { define_arm_cp_regs(cpu, tlbi_rme_reginfo); } -#endif } From c0b623cb1c0d2de64631486d391c55f2e8874eda Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:05:01 -0700 Subject: [PATCH 0942/2760] target/arm/tcg/arith_helper: compile file once Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-48-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/arith_helper.c | 5 +++-- target/arm/tcg/meson.build | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/arith_helper.c b/target/arm/tcg/arith_helper.c index 9a555c7966..670139819d 100644 --- a/target/arm/tcg/arith_helper.c +++ b/target/arm/tcg/arith_helper.c @@ -6,11 +6,12 @@ * SPDX-License-Identifier: GPL-2.0-or-later */ #include "qemu/osdep.h" -#include "cpu.h" -#include "exec/helper-proto.h" #include "qemu/crc32c.h" #include /* for crc32 */ +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" + /* * Note that signed overflow is undefined in C. The following routines are * careful to use unsigned types where modulo arithmetic is required. diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 5d32658540..7502c5cded 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -34,7 +34,6 @@ arm_ss.add(files( 'mve_helper.c', 'op_helper.c', 'vec_helper.c', - 'arith_helper.c', 'vfp_helper.c', )) @@ -59,6 +58,7 @@ arm_system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('cpu-v7m.c')) arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files('cpu-v7m.c')) arm_common_ss.add(files( + 'arith_helper.c', 'crypto_helper.c', )) From 9eb5427ac0d56ca050e34776b6de428892609bd5 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 12 May 2025 11:05:02 -0700 Subject: [PATCH 0943/2760] target/arm/tcg/vfp_helper: compile file twice (system, user) Reviewed-by: Richard Henderson Signed-off-by: Pierrick Bouvier Message-id: 20250512180502.2395029-49-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/meson.build | 3 ++- target/arm/tcg/vfp_helper.c | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 7502c5cded..2d1502ba88 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -34,7 +34,6 @@ arm_ss.add(files( 'mve_helper.c', 'op_helper.c', 'vec_helper.c', - 'vfp_helper.c', )) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( @@ -68,10 +67,12 @@ arm_common_system_ss.add(files( 'neon_helper.c', 'tlb_helper.c', 'tlb-insns.c', + 'vfp_helper.c', )) arm_user_ss.add(files( 'hflags.c', 'iwmmxt_helper.c', 'neon_helper.c', 'tlb_helper.c', + 'vfp_helper.c', )) diff --git a/target/arm/tcg/vfp_helper.c b/target/arm/tcg/vfp_helper.c index b32e2f4e27..b1324c5c0a 100644 --- a/target/arm/tcg/vfp_helper.c +++ b/target/arm/tcg/vfp_helper.c @@ -19,12 +19,14 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/helper-proto.h" #include "internals.h" #include "cpu-features.h" #include "fpu/softfloat.h" #include "qemu/log.h" +#define HELPER_H "tcg/helper.h" +#include "exec/helper-proto.h.inc" + /* * Set the float_status behaviour to match the Arm defaults: * * tininess-before-rounding From c33159dec79069514f78faecfe268439226b0f5b Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:18 -0500 Subject: [PATCH 0944/2760] block: Expand block status mode from bool to flags This patch is purely mechanical, changing bool want_zero into an unsigned int for bitwise-or of flags. As of this patch, all implementations are unchanged (the old want_zero==true is now mode==BDRV_WANT_PRECISE which is a superset of BDRV_WANT_ZERO); but the callers in io.c that used to pass want_zero==false are now prepared for future driver changes that can now distinguish bewteen BDRV_WANT_ZERO vs. BDRV_WANT_ALLOCATED. The next patch will actually change the file-posix driver along those lines, now that we have more-specific hints. As for the background why this patch is useful: right now, the file-posix driver recognizes that if allocation is being queried, the entire image can be reported as allocated (there is no backing file to refer to) - but this throws away information on whether the entire image reads as zero (trivially true if lseek(SEEK_HOLE) at offset 0 returns -ENXIO, a bit more complicated to prove if the raw file was created with 'qemu-img create' since we intentionally allocate a small chunk of all-zero data to help with alignment probing). Later patches will add a generic algorithm for seeing if an entire file reads as zeroes. Signed-off-by: Eric Blake Reviewed-by: Stefan Hajnoczi Message-ID: <20250509204341.3553601-16-eblake@redhat.com> --- block/blkdebug.c | 6 ++-- block/copy-before-write.c | 4 +-- block/coroutines.h | 4 +-- block/file-posix.c | 4 +-- block/gluster.c | 4 +-- block/io.c | 51 ++++++++++++++++---------------- block/iscsi.c | 6 ++-- block/nbd.c | 4 +-- block/null.c | 6 ++-- block/parallels.c | 6 ++-- block/qcow.c | 2 +- block/qcow2.c | 6 ++-- block/qed.c | 6 ++-- block/quorum.c | 4 +-- block/raw-format.c | 4 +-- block/rbd.c | 6 ++-- block/snapshot-access.c | 4 +-- block/vdi.c | 4 +-- block/vmdk.c | 2 +- block/vpc.c | 2 +- block/vvfat.c | 6 ++-- include/block/block-common.h | 11 +++++++ include/block/block_int-common.h | 27 +++++++++-------- include/block/block_int-io.h | 4 +-- tests/unit/test-block-iothread.c | 2 +- 25 files changed, 99 insertions(+), 86 deletions(-) diff --git a/block/blkdebug.c b/block/blkdebug.c index 1c1967f8e0..c54aee0c84 100644 --- a/block/blkdebug.c +++ b/block/blkdebug.c @@ -751,9 +751,9 @@ blkdebug_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) } static int coroutine_fn GRAPH_RDLOCK -blkdebug_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, int64_t *map, - BlockDriverState **file) +blkdebug_co_block_status(BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file) { int err; diff --git a/block/copy-before-write.c b/block/copy-before-write.c index 00af0b18ac..36d5d3ed9b 100644 --- a/block/copy-before-write.c +++ b/block/copy-before-write.c @@ -291,8 +291,8 @@ cbw_co_preadv_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes, } static int coroutine_fn GRAPH_RDLOCK -cbw_co_snapshot_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, int64_t bytes, +cbw_co_snapshot_block_status(BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { diff --git a/block/coroutines.h b/block/coroutines.h index 79e5efbf75..892646bb7a 100644 --- a/block/coroutines.h +++ b/block/coroutines.h @@ -47,7 +47,7 @@ int coroutine_fn GRAPH_RDLOCK bdrv_co_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, - bool want_zero, + unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, @@ -78,7 +78,7 @@ int co_wrapper_mixed_bdrv_rdlock bdrv_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, - bool want_zero, + unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, diff --git a/block/file-posix.c b/block/file-posix.c index ef52ed9169..805a1a2949 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -3273,7 +3273,7 @@ static int find_allocation(BlockDriverState *bs, off_t start, * well exceed it. */ static int coroutine_fn raw_co_block_status(BlockDriverState *bs, - bool want_zero, + unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, @@ -3289,7 +3289,7 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs, return ret; } - if (!want_zero) { + if (mode != BDRV_WANT_PRECISE) { *pnum = bytes; *map = offset; *file = bs; diff --git a/block/gluster.c b/block/gluster.c index 8712aa606a..1a2ef53e9b 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -1461,7 +1461,7 @@ static int find_allocation(BlockDriverState *bs, off_t start, * (Based on raw_co_block_status() from file-posix.c.) */ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs, - bool want_zero, + unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, @@ -1478,7 +1478,7 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs, return ret; } - if (!want_zero) { + if (mode != BDRV_WANT_PRECISE) { *pnum = bytes; *map = offset; *file = bs; diff --git a/block/io.c b/block/io.c index 6d98b0abb9..b5b143cd1b 100644 --- a/block/io.c +++ b/block/io.c @@ -2364,10 +2364,8 @@ int bdrv_flush_all(void) * Drivers not implementing the functionality are assumed to not support * backing files, hence all their sectors are reported as allocated. * - * If 'want_zero' is true, the caller is querying for mapping - * purposes, with a focus on valid BDRV_BLOCK_OFFSET_VALID, _DATA, and - * _ZERO where possible; otherwise, the result favors larger 'pnum', - * with a focus on accurate BDRV_BLOCK_ALLOCATED. + * 'mode' serves as a hint as to which results are favored; see the + * BDRV_WANT_* macros for details. * * If 'offset' is beyond the end of the disk image the return value is * BDRV_BLOCK_EOF and 'pnum' is set to 0. @@ -2387,7 +2385,7 @@ int bdrv_flush_all(void) * set to the host mapping and BDS corresponding to the guest offset. */ static int coroutine_fn GRAPH_RDLOCK -bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero, +bdrv_co_do_block_status(BlockDriverState *bs, unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { @@ -2476,7 +2474,7 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero, local_file = bs; local_map = aligned_offset; } else { - ret = bs->drv->bdrv_co_block_status(bs, want_zero, aligned_offset, + ret = bs->drv->bdrv_co_block_status(bs, mode, aligned_offset, aligned_bytes, pnum, &local_map, &local_file); @@ -2488,10 +2486,10 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero, * the cache requires an RCU update, so double check here to avoid * such an update if possible. * - * Check want_zero, because we only want to update the cache when we + * Check mode, because we only want to update the cache when we * have accurate information about what is zero and what is data. */ - if (want_zero && + if (mode == BDRV_WANT_PRECISE && ret == (BDRV_BLOCK_DATA | BDRV_BLOCK_OFFSET_VALID) && QLIST_EMPTY(&bs->children)) { @@ -2548,7 +2546,7 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero, if (ret & BDRV_BLOCK_RAW) { assert(ret & BDRV_BLOCK_OFFSET_VALID && local_file); - ret = bdrv_co_do_block_status(local_file, want_zero, local_map, + ret = bdrv_co_do_block_status(local_file, mode, local_map, *pnum, pnum, &local_map, &local_file); goto out; } @@ -2560,7 +2558,7 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero, if (!cow_bs) { ret |= BDRV_BLOCK_ZERO; - } else if (want_zero) { + } else if (mode == BDRV_WANT_PRECISE) { int64_t size2 = bdrv_co_getlength(cow_bs); if (size2 >= 0 && offset >= size2) { @@ -2569,14 +2567,14 @@ bdrv_co_do_block_status(BlockDriverState *bs, bool want_zero, } } - if (want_zero && ret & BDRV_BLOCK_RECURSE && + if (mode == BDRV_WANT_PRECISE && ret & BDRV_BLOCK_RECURSE && local_file && local_file != bs && (ret & BDRV_BLOCK_DATA) && !(ret & BDRV_BLOCK_ZERO) && (ret & BDRV_BLOCK_OFFSET_VALID)) { int64_t file_pnum; int ret2; - ret2 = bdrv_co_do_block_status(local_file, want_zero, local_map, + ret2 = bdrv_co_do_block_status(local_file, mode, local_map, *pnum, &file_pnum, NULL, NULL); if (ret2 >= 0) { /* Ignore errors. This is just providing extra information, it @@ -2627,7 +2625,7 @@ int coroutine_fn bdrv_co_common_block_status_above(BlockDriverState *bs, BlockDriverState *base, bool include_base, - bool want_zero, + unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, @@ -2654,7 +2652,7 @@ bdrv_co_common_block_status_above(BlockDriverState *bs, return 0; } - ret = bdrv_co_do_block_status(bs, want_zero, offset, bytes, pnum, + ret = bdrv_co_do_block_status(bs, mode, offset, bytes, pnum, map, file); ++*depth; if (ret < 0 || *pnum == 0 || ret & BDRV_BLOCK_ALLOCATED || bs == base) { @@ -2671,7 +2669,7 @@ bdrv_co_common_block_status_above(BlockDriverState *bs, for (p = bdrv_filter_or_cow_bs(bs); include_base || p != base; p = bdrv_filter_or_cow_bs(p)) { - ret = bdrv_co_do_block_status(p, want_zero, offset, bytes, pnum, + ret = bdrv_co_do_block_status(p, mode, offset, bytes, pnum, map, file); ++*depth; if (ret < 0) { @@ -2734,7 +2732,8 @@ int coroutine_fn bdrv_co_block_status_above(BlockDriverState *bs, BlockDriverState **file) { IO_CODE(); - return bdrv_co_common_block_status_above(bs, base, false, true, offset, + return bdrv_co_common_block_status_above(bs, base, false, + BDRV_WANT_PRECISE, offset, bytes, pnum, map, file, NULL); } @@ -2765,8 +2764,9 @@ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, return 1; } - ret = bdrv_co_common_block_status_above(bs, NULL, false, false, offset, - bytes, &pnum, NULL, NULL, NULL); + ret = bdrv_co_common_block_status_above(bs, NULL, false, BDRV_WANT_ZERO, + offset, bytes, &pnum, NULL, NULL, + NULL); if (ret < 0) { return ret; @@ -2782,9 +2782,9 @@ int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset, int64_t dummy; IO_CODE(); - ret = bdrv_co_common_block_status_above(bs, bs, true, false, offset, - bytes, pnum ? pnum : &dummy, NULL, - NULL, NULL); + ret = bdrv_co_common_block_status_above(bs, bs, true, BDRV_WANT_ALLOCATED, + offset, bytes, pnum ? pnum : &dummy, + NULL, NULL, NULL); if (ret < 0) { return ret; } @@ -2817,7 +2817,8 @@ int coroutine_fn bdrv_co_is_allocated_above(BlockDriverState *bs, int ret; IO_CODE(); - ret = bdrv_co_common_block_status_above(bs, base, include_base, false, + ret = bdrv_co_common_block_status_above(bs, base, include_base, + BDRV_WANT_ALLOCATED, offset, bytes, pnum, NULL, NULL, &depth); if (ret < 0) { @@ -3698,8 +3699,8 @@ bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes, } int coroutine_fn -bdrv_co_snapshot_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, int64_t bytes, +bdrv_co_snapshot_block_status(BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { @@ -3717,7 +3718,7 @@ bdrv_co_snapshot_block_status(BlockDriverState *bs, } bdrv_inc_in_flight(bs); - ret = drv->bdrv_co_snapshot_block_status(bs, want_zero, offset, bytes, + ret = drv->bdrv_co_snapshot_block_status(bs, mode, offset, bytes, pnum, map, file); bdrv_dec_in_flight(bs); diff --git a/block/iscsi.c b/block/iscsi.c index 2f0f4dac09..15b96ee880 100644 --- a/block/iscsi.c +++ b/block/iscsi.c @@ -694,9 +694,9 @@ iscsi_co_writev(BlockDriverState *bs, int64_t sector_num, int nb_sectors, static int coroutine_fn iscsi_co_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, - int64_t *map, + unsigned int mode, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, BlockDriverState **file) { IscsiLun *iscsilun = bs->opaque; diff --git a/block/nbd.c b/block/nbd.c index 887841bc81..d5a2b21c6d 100644 --- a/block/nbd.c +++ b/block/nbd.c @@ -1397,8 +1397,8 @@ nbd_client_co_pdiscard(BlockDriverState *bs, int64_t offset, int64_t bytes) } static int coroutine_fn GRAPH_RDLOCK nbd_client_co_block_status( - BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, - int64_t *pnum, int64_t *map, BlockDriverState **file) + BlockDriverState *bs, unsigned int mode, int64_t offset, + int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { int ret, request_ret; NBDExtent64 extent = { 0 }; diff --git a/block/null.c b/block/null.c index dc0b1fdbd9..4e448d593d 100644 --- a/block/null.c +++ b/block/null.c @@ -227,9 +227,9 @@ static int null_reopen_prepare(BDRVReopenState *reopen_state, } static int coroutine_fn null_co_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, - int64_t *map, + unsigned int mode, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, BlockDriverState **file) { BDRVNullState *s = bs->opaque; diff --git a/block/parallels.c b/block/parallels.c index 347ca127f3..3a375e2a8a 100644 --- a/block/parallels.c +++ b/block/parallels.c @@ -416,9 +416,9 @@ parallels_co_flush_to_os(BlockDriverState *bs) } static int coroutine_fn GRAPH_RDLOCK -parallels_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, int64_t *map, - BlockDriverState **file) +parallels_co_block_status(BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file) { BDRVParallelsState *s = bs->opaque; int count; diff --git a/block/qcow.c b/block/qcow.c index da8ad4d243..8a3e7591a9 100644 --- a/block/qcow.c +++ b/block/qcow.c @@ -530,7 +530,7 @@ get_cluster_offset(BlockDriverState *bs, uint64_t offset, int allocate, } static int coroutine_fn GRAPH_RDLOCK -qcow_co_block_status(BlockDriverState *bs, bool want_zero, +qcow_co_block_status(BlockDriverState *bs, unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { diff --git a/block/qcow2.c b/block/qcow2.c index 7774e7f090..66fba89b41 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2141,9 +2141,9 @@ static void qcow2_join_options(QDict *options, QDict *old_options) } static int coroutine_fn GRAPH_RDLOCK -qcow2_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, - int64_t count, int64_t *pnum, int64_t *map, - BlockDriverState **file) +qcow2_co_block_status(BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t count, int64_t *pnum, + int64_t *map, BlockDriverState **file) { BDRVQcow2State *s = bs->opaque; uint64_t host_offset; diff --git a/block/qed.c b/block/qed.c index ac24449ffb..4a36fb3929 100644 --- a/block/qed.c +++ b/block/qed.c @@ -833,9 +833,9 @@ bdrv_qed_co_create_opts(BlockDriver *drv, const char *filename, } static int coroutine_fn GRAPH_RDLOCK -bdrv_qed_co_block_status(BlockDriverState *bs, bool want_zero, int64_t pos, - int64_t bytes, int64_t *pnum, int64_t *map, - BlockDriverState **file) +bdrv_qed_co_block_status(BlockDriverState *bs, unsigned int mode, + int64_t pos, int64_t bytes, int64_t *pnum, + int64_t *map, BlockDriverState **file) { BDRVQEDState *s = bs->opaque; size_t len = MIN(bytes, SIZE_MAX); diff --git a/block/quorum.c b/block/quorum.c index 30747a6df9..ed8ce801ee 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -1226,7 +1226,7 @@ static void quorum_child_perm(BlockDriverState *bs, BdrvChild *c, * region contains zeroes, and BDRV_BLOCK_DATA otherwise. */ static int coroutine_fn GRAPH_RDLOCK -quorum_co_block_status(BlockDriverState *bs, bool want_zero, +quorum_co_block_status(BlockDriverState *bs, unsigned int mode, int64_t offset, int64_t count, int64_t *pnum, int64_t *map, BlockDriverState **file) { @@ -1238,7 +1238,7 @@ quorum_co_block_status(BlockDriverState *bs, bool want_zero, for (i = 0; i < s->num_children; i++) { int64_t bytes; ret = bdrv_co_common_block_status_above(s->children[i]->bs, NULL, false, - want_zero, offset, count, + mode, offset, count, &bytes, NULL, NULL, NULL); if (ret < 0) { quorum_report_bad(QUORUM_OP_TYPE_READ, offset, count, diff --git a/block/raw-format.c b/block/raw-format.c index e08526e2ec..df16ac1ea2 100644 --- a/block/raw-format.c +++ b/block/raw-format.c @@ -283,8 +283,8 @@ raw_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, } static int coroutine_fn GRAPH_RDLOCK -raw_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, int64_t *map, +raw_co_block_status(BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { BDRVRawState *s = bs->opaque; diff --git a/block/rbd.c b/block/rbd.c index 7446e66659..951cd63f9a 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -1503,9 +1503,9 @@ static int qemu_rbd_diff_iterate_cb(uint64_t offs, size_t len, } static int coroutine_fn qemu_rbd_co_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, - int64_t *map, + unsigned int mode, + int64_t offset, int64_t bytes, + int64_t *pnum, int64_t *map, BlockDriverState **file) { BDRVRBDState *s = bs->opaque; diff --git a/block/snapshot-access.c b/block/snapshot-access.c index 71ac83c01f..17ed2402db 100644 --- a/block/snapshot-access.c +++ b/block/snapshot-access.c @@ -41,11 +41,11 @@ snapshot_access_co_preadv_part(BlockDriverState *bs, static int coroutine_fn GRAPH_RDLOCK snapshot_access_co_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, + unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { - return bdrv_co_snapshot_block_status(bs->file->bs, want_zero, offset, + return bdrv_co_snapshot_block_status(bs->file->bs, mode, offset, bytes, pnum, map, file); } diff --git a/block/vdi.c b/block/vdi.c index a2da6ecab0..3ddc62a569 100644 --- a/block/vdi.c +++ b/block/vdi.c @@ -523,8 +523,8 @@ static int vdi_reopen_prepare(BDRVReopenState *state, } static int coroutine_fn GRAPH_RDLOCK -vdi_co_block_status(BlockDriverState *bs, bool want_zero, int64_t offset, - int64_t bytes, int64_t *pnum, int64_t *map, +vdi_co_block_status(BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { BDRVVdiState *s = (BDRVVdiState *)bs->opaque; diff --git a/block/vmdk.c b/block/vmdk.c index 2adec49912..9c7ab037e1 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1777,7 +1777,7 @@ static inline uint64_t vmdk_find_offset_in_cluster(VmdkExtent *extent, } static int coroutine_fn GRAPH_RDLOCK -vmdk_co_block_status(BlockDriverState *bs, bool want_zero, +vmdk_co_block_status(BlockDriverState *bs, unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) { diff --git a/block/vpc.c b/block/vpc.c index 0309e319f6..801ff5793f 100644 --- a/block/vpc.c +++ b/block/vpc.c @@ -726,7 +726,7 @@ vpc_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, } static int coroutine_fn GRAPH_RDLOCK -vpc_co_block_status(BlockDriverState *bs, bool want_zero, +vpc_co_block_status(BlockDriverState *bs, unsigned int mode, int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file) diff --git a/block/vvfat.c b/block/vvfat.c index 91d69b3cc8..814796d918 100644 --- a/block/vvfat.c +++ b/block/vvfat.c @@ -3134,9 +3134,9 @@ vvfat_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, } static int coroutine_fn vvfat_co_block_status(BlockDriverState *bs, - bool want_zero, int64_t offset, - int64_t bytes, int64_t *n, - int64_t *map, + unsigned int mode, + int64_t offset, int64_t bytes, + int64_t *n, int64_t *map, BlockDriverState **file) { *n = bytes; diff --git a/include/block/block-common.h b/include/block/block-common.h index 0b831ef87b..c8c626daea 100644 --- a/include/block/block-common.h +++ b/include/block/block-common.h @@ -333,6 +333,17 @@ typedef enum { #define BDRV_BLOCK_RECURSE 0x40 #define BDRV_BLOCK_COMPRESSED 0x80 +/* + * Block status hints: the bitwise-or of these flags emphasize what + * the caller hopes to learn, and some drivers may be able to give + * faster answers by doing less work when the hint permits. + */ +#define BDRV_WANT_ZERO BDRV_BLOCK_ZERO +#define BDRV_WANT_OFFSET_VALID BDRV_BLOCK_OFFSET_VALID +#define BDRV_WANT_ALLOCATED BDRV_BLOCK_ALLOCATED +#define BDRV_WANT_PRECISE (BDRV_WANT_ZERO | BDRV_WANT_OFFSET_VALID | \ + BDRV_WANT_OFFSET_VALID) + typedef QTAILQ_HEAD(BlockReopenQueue, BlockReopenQueueEntry) BlockReopenQueue; typedef struct BDRVReopenState { diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 0d8187f656..2982dd3118 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -604,15 +604,16 @@ struct BlockDriver { * according to the current layer, and should only need to set * BDRV_BLOCK_DATA, BDRV_BLOCK_ZERO, BDRV_BLOCK_OFFSET_VALID, * and/or BDRV_BLOCK_RAW; if the current layer defers to a backing - * layer, the result should be 0 (and not BDRV_BLOCK_ZERO). See - * block.h for the overall meaning of the bits. As a hint, the - * flag want_zero is true if the caller cares more about precise - * mappings (favor accurate _OFFSET_VALID/_ZERO) or false for - * overall allocation (favor larger *pnum, perhaps by reporting - * _DATA instead of _ZERO). The block layer guarantees input - * clamped to bdrv_getlength() and aligned to request_alignment, - * as well as non-NULL pnum, map, and file; in turn, the driver - * must return an error or set pnum to an aligned non-zero value. + * layer, the result should be 0 (and not BDRV_BLOCK_ZERO). The + * caller will synthesize BDRV_BLOCK_ALLOCATED based on the + * non-zero results. See block.h for the overall meaning of the + * bits. As a hint, the flags in @mode may include a bitwise-or + * of BDRV_WANT_ALLOCATED, BDRV_WANT_OFFSET_VALID, or + * BDRV_WANT_ZERO based on what the caller is looking for in the + * results. The block layer guarantees input clamped to + * bdrv_getlength() and aligned to request_alignment, as well as + * non-NULL pnum, map, and file; in turn, the driver must return + * an error or set pnum to an aligned non-zero value. * * Note that @bytes is just a hint on how big of a region the * caller wants to inspect. It is not a limit on *pnum. @@ -624,8 +625,8 @@ struct BlockDriver { * to clamping *pnum for return to its caller. */ int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_block_status)( - BlockDriverState *bs, - bool want_zero, int64_t offset, int64_t bytes, int64_t *pnum, + BlockDriverState *bs, unsigned int mode, + int64_t offset, int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file); /* @@ -649,8 +650,8 @@ struct BlockDriver { QEMUIOVector *qiov, size_t qiov_offset); int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_snapshot_block_status)( - BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, - int64_t *pnum, int64_t *map, BlockDriverState **file); + BlockDriverState *bs, unsigned int mode, int64_t offset, + int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file); int coroutine_fn GRAPH_RDLOCK_PTR (*bdrv_co_pdiscard_snapshot)( BlockDriverState *bs, int64_t offset, int64_t bytes); diff --git a/include/block/block_int-io.h b/include/block/block_int-io.h index 4a7cf2b4fd..4f94eb3c5a 100644 --- a/include/block/block_int-io.h +++ b/include/block/block_int-io.h @@ -38,8 +38,8 @@ int coroutine_fn GRAPH_RDLOCK bdrv_co_preadv_snapshot(BdrvChild *child, int64_t offset, int64_t bytes, QEMUIOVector *qiov, size_t qiov_offset); int coroutine_fn GRAPH_RDLOCK bdrv_co_snapshot_block_status( - BlockDriverState *bs, bool want_zero, int64_t offset, int64_t bytes, - int64_t *pnum, int64_t *map, BlockDriverState **file); + BlockDriverState *bs, unsigned int mode, int64_t offset, + int64_t bytes, int64_t *pnum, int64_t *map, BlockDriverState **file); int coroutine_fn GRAPH_RDLOCK bdrv_co_pdiscard_snapshot(BlockDriverState *bs, int64_t offset, int64_t bytes); diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index 2b358eaaa8..e26b3be593 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -63,7 +63,7 @@ bdrv_test_co_truncate(BlockDriverState *bs, int64_t offset, bool exact, } static int coroutine_fn bdrv_test_co_block_status(BlockDriverState *bs, - bool want_zero, + unsigned int mode, int64_t offset, int64_t count, int64_t *pnum, int64_t *map, BlockDriverState **file) From a6a0a7fb0e327d17594c971b4a39de14e025b415 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:19 -0500 Subject: [PATCH 0945/2760] file-posix, gluster: Handle zero block status hint better Although the previous patch to change 'bool want_zero' into a bitmask made no semantic change, it is now time to differentiate. When the caller specifically wants to know what parts of the file read as zero, we need to use lseek and actually reporting holes, rather than short-circuiting and advertising full allocation. This change will be utilized in later patches to let mirroring optimize for the case when the destination already reads as zeroes. Signed-off-by: Eric Blake Reviewed-by: Stefan Hajnoczi Message-ID: <20250509204341.3553601-17-eblake@redhat.com> --- block/file-posix.c | 3 ++- block/gluster.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 805a1a2949..ec95b74869 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -3289,7 +3289,8 @@ static int coroutine_fn raw_co_block_status(BlockDriverState *bs, return ret; } - if (mode != BDRV_WANT_PRECISE) { + if (!(mode & BDRV_WANT_ZERO)) { + /* There is no backing file - all bytes are allocated in this file. */ *pnum = bytes; *map = offset; *file = bs; diff --git a/block/gluster.c b/block/gluster.c index 1a2ef53e9b..89abd40f31 100644 --- a/block/gluster.c +++ b/block/gluster.c @@ -1478,7 +1478,7 @@ static int coroutine_fn qemu_gluster_co_block_status(BlockDriverState *bs, return ret; } - if (mode != BDRV_WANT_PRECISE) { + if (!(mode & BDRV_WANT_ZERO)) { *pnum = bytes; *map = offset; *file = bs; From 31bf15d97dd1d205a3b264675f9a1b3bd1939068 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:20 -0500 Subject: [PATCH 0946/2760] block: Let bdrv_co_is_zero_fast consolidate adjacent extents Some BDS drivers have a cap on how much block status they can supply in one query (for example, NBD talking to an older server cannot inspect more than 4G per query; and qcow2 tends to cap its answers rather than cross a cluster boundary of an L1 table). Although the existing callers of bdrv_co_is_zero_fast are not passing in that large of a 'bytes' parameter, an upcoming caller wants to query the entire image at once, and will thus benefit from being able to treat adjacent zero regions in a coalesced manner, rather than claiming the region is non-zero merely because pnum was truncated and didn't match the incoming bytes. While refactoring this into a loop, note that there is no need to assign pnum prior to calling bdrv_co_common_block_status_above() (it is guaranteed to be assigned deeper in the callstack). Signed-off-by: Eric Blake Reviewed-by: Stefan Hajnoczi Message-ID: <20250509204341.3553601-18-eblake@redhat.com> --- block/io.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/block/io.c b/block/io.c index b5b143cd1b..50dc0e193f 100644 --- a/block/io.c +++ b/block/io.c @@ -2751,28 +2751,31 @@ int coroutine_fn bdrv_co_block_status(BlockDriverState *bs, int64_t offset, * by @offset and @bytes is known to read as zeroes. * Return 1 if that is the case, 0 otherwise and -errno on error. * This test is meant to be fast rather than accurate so returning 0 - * does not guarantee non-zero data. + * does not guarantee non-zero data; but a return of 1 is reliable. */ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, int64_t bytes) { int ret; - int64_t pnum = bytes; + int64_t pnum; IO_CODE(); - if (!bytes) { - return 1; - } - - ret = bdrv_co_common_block_status_above(bs, NULL, false, BDRV_WANT_ZERO, - offset, bytes, &pnum, NULL, NULL, - NULL); + while (bytes) { + ret = bdrv_co_common_block_status_above(bs, NULL, false, + BDRV_WANT_ZERO, offset, bytes, + &pnum, NULL, NULL, NULL); - if (ret < 0) { - return ret; + if (ret < 0) { + return ret; + } + if (!(ret & BDRV_BLOCK_ZERO)) { + return 0; + } + offset += pnum; + bytes -= pnum; } - return (pnum == bytes) && (ret & BDRV_BLOCK_ZERO); + return 1; } int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset, From 52726096707c5c8b90597c445de897fa64d56e73 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:21 -0500 Subject: [PATCH 0947/2760] block: Add new bdrv_co_is_all_zeroes() function There are some optimizations that require knowing if an image starts out as reading all zeroes, such as making blockdev-mirror faster by skipping the copying of source zeroes to the destination. The existing bdrv_co_is_zero_fast() is a good building block for answering this question, but it tends to give an answer of 0 for a file we just created via QMP 'blockdev-create' or similar (such as 'qemu-img create -f raw'). Why? Because file-posix.c insists on allocating a tiny header to any file rather than leaving it 100% sparse, due to some filesystems that are unable to answer alignment probes on a hole. But teaching file-posix.c to read the tiny header doesn't scale - the problem of a small header is also visible when libvirt sets up an NBD client to a just-created file on a migration destination host. So, we need a wrapper function that handles a bit more complexity in a common manner for all block devices - when the BDS is mostly a hole, but has a small non-hole header, it is still worth the time to read that header and check if it reads as all zeroes before giving up and returning a pessimistic answer. Signed-off-by: Eric Blake Reviewed-by: Stefan Hajnoczi Message-ID: <20250509204341.3553601-19-eblake@redhat.com> --- block/io.c | 62 ++++++++++++++++++++++++++++++++++++++++ include/block/block-io.h | 2 ++ 2 files changed, 64 insertions(+) diff --git a/block/io.c b/block/io.c index 50dc0e193f..4fd7768f9c 100644 --- a/block/io.c +++ b/block/io.c @@ -38,10 +38,14 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "system/replay.h" +#include "qemu/units.h" /* Maximum bounce buffer for copy-on-read and write zeroes, in bytes */ #define MAX_BOUNCE_BUFFER (32768 << BDRV_SECTOR_BITS) +/* Maximum read size for checking if data reads as zero, in bytes */ +#define MAX_ZERO_CHECK_BUFFER (128 * KiB) + static void coroutine_fn GRAPH_RDLOCK bdrv_parent_cb_resize(BlockDriverState *bs); @@ -2778,6 +2782,64 @@ int coroutine_fn bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, return 1; } +/* + * Check @bs (and its backing chain) to see if the entire image is known + * to read as zeroes. + * Return 1 if that is the case, 0 otherwise and -errno on error. + * This test is meant to be fast rather than accurate so returning 0 + * does not guarantee non-zero data; however, a return of 1 is reliable, + * and this function can report 1 in more cases than bdrv_co_is_zero_fast. + */ +int coroutine_fn bdrv_co_is_all_zeroes(BlockDriverState *bs) +{ + int ret; + int64_t pnum, bytes; + char *buf; + QEMUIOVector local_qiov; + IO_CODE(); + + bytes = bdrv_co_getlength(bs); + if (bytes < 0) { + return bytes; + } + + /* First probe - see if the entire image reads as zero */ + ret = bdrv_co_common_block_status_above(bs, NULL, false, BDRV_WANT_ZERO, + 0, bytes, &pnum, NULL, NULL, + NULL); + if (ret < 0) { + return ret; + } + if (ret & BDRV_BLOCK_ZERO) { + return bdrv_co_is_zero_fast(bs, pnum, bytes - pnum); + } + + /* + * Because of the way 'blockdev-create' works, raw files tend to + * be created with a non-sparse region at the front to make + * alignment probing easier. If the block starts with only a + * small allocated region, it is still worth the effort to see if + * the rest of the image is still sparse, coupled with manually + * reading the first region to see if it reads zero after all. + */ + if (pnum > MAX_ZERO_CHECK_BUFFER) { + return 0; + } + ret = bdrv_co_is_zero_fast(bs, pnum, bytes - pnum); + if (ret <= 0) { + return ret; + } + /* Only the head of the image is unknown, and it's small. Read it. */ + buf = qemu_blockalign(bs, pnum); + qemu_iovec_init_buf(&local_qiov, buf, pnum); + ret = bdrv_driver_preadv(bs, 0, pnum, &local_qiov, 0, 0); + if (ret >= 0) { + ret = buffer_is_zero(buf, pnum); + } + qemu_vfree(buf); + return ret; +} + int coroutine_fn bdrv_co_is_allocated(BlockDriverState *bs, int64_t offset, int64_t bytes, int64_t *pnum) { diff --git a/include/block/block-io.h b/include/block/block-io.h index b49e0537dd..b99cc98d26 100644 --- a/include/block/block-io.h +++ b/include/block/block-io.h @@ -161,6 +161,8 @@ bdrv_is_allocated_above(BlockDriverState *bs, BlockDriverState *base, int coroutine_fn GRAPH_RDLOCK bdrv_co_is_zero_fast(BlockDriverState *bs, int64_t offset, int64_t bytes); +int coroutine_fn GRAPH_RDLOCK +bdrv_co_is_all_zeroes(BlockDriverState *bs); int GRAPH_RDLOCK bdrv_apply_auto_read_only(BlockDriverState *bs, const char *errmsg, From eb89627899bb84148d272394e885725eff456ae9 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:22 -0500 Subject: [PATCH 0948/2760] iotests: Improve iotest 194 to mirror data Mirroring a completely sparse image to a sparse destination should be practically instantaneous. It isn't yet, but the test will be more realistic if it has some non-zero to mirror as well as the holes. Signed-off-by: Eric Blake Reviewed-by: Stefan Hajnoczi Message-ID: <20250509204341.3553601-20-eblake@redhat.com> --- tests/qemu-iotests/194 | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194 index c0ce82dd25..d0b9c084f5 100755 --- a/tests/qemu-iotests/194 +++ b/tests/qemu-iotests/194 @@ -34,6 +34,7 @@ with iotests.FilePath('source.img') as source_img_path, \ img_size = '1G' iotests.qemu_img_create('-f', iotests.imgfmt, source_img_path, img_size) + iotests.qemu_io('-f', iotests.imgfmt, '-c', 'write 512M 1M', source_img_path) iotests.qemu_img_create('-f', iotests.imgfmt, dest_img_path, img_size) iotests.log('Launching VMs...') From 870f8963cf1a84f8ec929b05a6d68906974a76c5 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:23 -0500 Subject: [PATCH 0949/2760] mirror: Minor refactoring Commit 5791ba52 (v9.2) pre-initialized ret in mirror_dirty_init to silence a false positive compiler warning, even though in all code paths where ret is used, it was guaranteed to be reassigned beforehand. But since the function returns -errno, and -1 is not always the right errno, it's better to initialize to -EIO. An upcoming patch wants to track two bitmaps in do_sync_target_write(); this will be easier if the current variables related to the dirty bitmap are renamed. Signed-off-by: Eric Blake Reviewed-by: Stefan Hajnoczi Message-ID: <20250509204341.3553601-21-eblake@redhat.com> --- block/mirror.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index a53582f17b..34c6c5252e 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -841,7 +841,7 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) int64_t offset; BlockDriverState *bs; BlockDriverState *target_bs = blk_bs(s->target); - int ret = -1; + int ret = -EIO; int64_t count; bdrv_graph_co_rdlock(); @@ -1341,7 +1341,7 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, { int ret; size_t qiov_offset = 0; - int64_t bitmap_offset, bitmap_end; + int64_t dirty_bitmap_offset, dirty_bitmap_end; if (!QEMU_IS_ALIGNED(offset, job->granularity) && bdrv_dirty_bitmap_get(job->dirty_bitmap, offset)) @@ -1388,11 +1388,11 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, * Tails are either clean or shrunk, so for bitmap resetting * we safely align the range down. */ - bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity); - bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity); - if (bitmap_offset < bitmap_end) { - bdrv_reset_dirty_bitmap(job->dirty_bitmap, bitmap_offset, - bitmap_end - bitmap_offset); + dirty_bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity); + dirty_bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity); + if (dirty_bitmap_offset < dirty_bitmap_end) { + bdrv_reset_dirty_bitmap(job->dirty_bitmap, dirty_bitmap_offset, + dirty_bitmap_end - dirty_bitmap_offset); } job_progress_increase_remaining(&job->common.job, bytes); @@ -1430,10 +1430,10 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, * at function start, and they must be still dirty, as we've locked * the region for in-flight op. */ - bitmap_offset = QEMU_ALIGN_DOWN(offset, job->granularity); - bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity); - bdrv_set_dirty_bitmap(job->dirty_bitmap, bitmap_offset, - bitmap_end - bitmap_offset); + dirty_bitmap_offset = QEMU_ALIGN_DOWN(offset, job->granularity); + dirty_bitmap_end = QEMU_ALIGN_UP(offset + bytes, job->granularity); + bdrv_set_dirty_bitmap(job->dirty_bitmap, dirty_bitmap_offset, + dirty_bitmap_end - dirty_bitmap_offset); qatomic_set(&job->actively_synced, false); action = mirror_error_action(job, false, -ret); From 9474d97bd7421b4fe7c806ab0949697514d11e88 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:24 -0500 Subject: [PATCH 0950/2760] mirror: Pass full sync mode rather than bool to internals Out of the five possible values for MirrorSyncMode, INCREMENTAL and BITMAP are already rejected up front in mirror_start, leaving NONE, TOP, and FULL as the remaining values that the code was collapsing into a single bool is_none_mode. Furthermore, mirror_dirty_init() is only reachable for modes TOP and FULL, as further guided by s->zero_target. However, upcoming patches want to further optimize the pre-zeroing pass of a sync=full mirror in mirror_dirty_init(), while avoiding that pass on a sync=top action. Instead of throwing away context by collapsing these two values into s->is_none_mode=false, it is better to pass s->sync_mode throughout the entire operation. For active commit, the desired semantics match sync mode TOP. Signed-off-by: Eric Blake Message-ID: <20250509204341.3553601-22-eblake@redhat.com> Reviewed-by: Sunny Zhu Reviewed-by: Stefan Hajnoczi --- block/mirror.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 34c6c5252e..2599b75d09 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -51,7 +51,7 @@ typedef struct MirrorBlockJob { BlockDriverState *to_replace; /* Used to block operations on the drive-mirror-replace target */ Error *replace_blocker; - bool is_none_mode; + MirrorSyncMode sync_mode; BlockMirrorBackingMode backing_mode; /* Whether the target image requires explicit zero-initialization */ bool zero_target; @@ -723,9 +723,10 @@ static int mirror_exit_common(Job *job) &error_abort); if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { - BlockDriverState *backing = s->is_none_mode ? src : s->base; + BlockDriverState *backing; BlockDriverState *unfiltered_target = bdrv_skip_filters(target_bs); + backing = s->sync_mode == MIRROR_SYNC_MODE_NONE ? src : s->base; if (bdrv_cow_bs(unfiltered_target) != backing) { bdrv_set_backing_hd(unfiltered_target, backing, &local_err); if (local_err) { @@ -1020,7 +1021,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) mirror_free_init(s); s->last_pause_ns = qemu_clock_get_ns(QEMU_CLOCK_REALTIME); - if (!s->is_none_mode) { + if (s->sync_mode != MIRROR_SYNC_MODE_NONE) { ret = mirror_dirty_init(s); if (ret < 0 || job_is_cancelled(&s->common.job)) { goto immediate_exit; @@ -1711,6 +1712,7 @@ static BlockJob *mirror_start_job( int creation_flags, BlockDriverState *target, const char *replaces, int64_t speed, uint32_t granularity, int64_t buf_size, + MirrorSyncMode sync_mode, BlockMirrorBackingMode backing_mode, bool zero_target, BlockdevOnError on_source_error, @@ -1719,7 +1721,7 @@ static BlockJob *mirror_start_job( BlockCompletionFunc *cb, void *opaque, const BlockJobDriver *driver, - bool is_none_mode, BlockDriverState *base, + BlockDriverState *base, bool auto_complete, const char *filter_node_name, bool is_mirror, MirrorCopyMode copy_mode, bool base_ro, @@ -1878,7 +1880,7 @@ static BlockJob *mirror_start_job( s->replaces = g_strdup(replaces); s->on_source_error = on_source_error; s->on_target_error = on_target_error; - s->is_none_mode = is_none_mode; + s->sync_mode = sync_mode; s->backing_mode = backing_mode; s->zero_target = zero_target; qatomic_set(&s->copy_mode, copy_mode); @@ -2015,7 +2017,6 @@ void mirror_start(const char *job_id, BlockDriverState *bs, bool unmap, const char *filter_node_name, MirrorCopyMode copy_mode, Error **errp) { - bool is_none_mode; BlockDriverState *base; GLOBAL_STATE_CODE(); @@ -2028,14 +2029,13 @@ void mirror_start(const char *job_id, BlockDriverState *bs, } bdrv_graph_rdlock_main_loop(); - is_none_mode = mode == MIRROR_SYNC_MODE_NONE; base = mode == MIRROR_SYNC_MODE_TOP ? bdrv_backing_chain_next(bs) : NULL; bdrv_graph_rdunlock_main_loop(); mirror_start_job(job_id, bs, creation_flags, target, replaces, - speed, granularity, buf_size, backing_mode, zero_target, - on_source_error, on_target_error, unmap, NULL, NULL, - &mirror_job_driver, is_none_mode, base, false, + speed, granularity, buf_size, mode, backing_mode, + zero_target, on_source_error, on_target_error, unmap, + NULL, NULL, &mirror_job_driver, base, false, filter_node_name, true, copy_mode, false, errp); } @@ -2061,9 +2061,9 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, job = mirror_start_job( job_id, bs, creation_flags, base, NULL, speed, 0, 0, - MIRROR_LEAVE_BACKING_CHAIN, false, + MIRROR_SYNC_MODE_TOP, MIRROR_LEAVE_BACKING_CHAIN, false, on_error, on_error, true, cb, opaque, - &commit_active_job_driver, false, base, auto_complete, + &commit_active_job_driver, base, auto_complete, filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, base_read_only, errp); if (!job) { From d17a34bfb94bda3a89d7320ae67255ded1d8c939 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:25 -0500 Subject: [PATCH 0951/2760] mirror: Allow QMP override to declare target already zero QEMU has an optimization for a just-created drive-mirror destination that is not possible for blockdev-mirror (which can't create the destination) - any time we know the destination starts life as all zeroes, we can skip a pre-zeroing pass on the destination. Recent patches have added an improved heuristic for detecting if a file contains all zeroes, and we plan to use that heuristic in upcoming patches. But since a heuristic cannot quickly detect all scenarios, and there may be cases where the caller is aware of information that QEMU cannot learn quickly, it makes sense to have a way to tell QEMU to assume facts about the destination that can make the mirror operation faster. Given our existing example of "qemu-img convert --target-is-zero", it is time to expose this override in QMP for blockdev-mirror as well. This patch results in some slight redundancy between the older s->zero_target (set any time mode==FULL and the destination image was not just created - ie. clear if drive-mirror is asking to skip the pre-zero pass) and the newly-introduced s->target_is_zero (in addition to the QMP override, it is set when drive-mirror creates the destination image); this will be cleaned up in the next patch. There is also a subtlety that we must consider. When drive-mirror is passing target_is_zero on behalf of a just-created image, we know the image is sparse (skipping the pre-zeroing keeps it that way), so it doesn't matter whether the destination also has "discard":"unmap" and "detect-zeroes":"unmap". But now that we are letting the user set the knob for target-is-zero, if the user passes a pre-existing file that is fully allocated, it is fine to leave the file fully allocated under "detect-zeroes":"on", but if the file is open with "detect-zeroes":"unmap", we should really be trying harder to punch holes in the destination for every region of zeroes copied from the source. The easiest way to do this is to still run the pre-zeroing pass (turning the entire destination file sparse before populating just the allocated portions of the source), even though that currently results in double I/O to the portions of the file that are allocated. A later patch will add further optimizations to reduce redundant zeroing I/O during the mirror operation. Since "target-is-zero":true is designed for optimizations, it is okay to silently ignore the parameter rather than erroring if the user ever sets the parameter in a scenario where the mirror job can't exploit it (for example, when doing "sync":"top" instead of "sync":"full", we can't pre-zero, so setting the parameter won't make a speed difference). Signed-off-by: Eric Blake Acked-by: Markus Armbruster Message-ID: <20250509204341.3553601-23-eblake@redhat.com> Reviewed-by: Sunny Zhu Reviewed-by: Stefan Hajnoczi --- block/mirror.c | 27 ++++++++++++++++++++++---- blockdev.c | 18 ++++++++++------- include/block/block_int-global-state.h | 3 ++- qapi/block-core.json | 8 +++++++- tests/unit/test-block-iothread.c | 2 +- 5 files changed, 44 insertions(+), 14 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 2599b75d09..4dcb50c81a 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -55,6 +55,8 @@ typedef struct MirrorBlockJob { BlockMirrorBackingMode backing_mode; /* Whether the target image requires explicit zero-initialization */ bool zero_target; + /* Whether the target should be assumed to be already zero initialized */ + bool target_is_zero; /* * To be accesssed with atomics. Written only under the BQL (required by the * current implementation of mirror_change()). @@ -844,12 +846,26 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) BlockDriverState *target_bs = blk_bs(s->target); int ret = -EIO; int64_t count; + bool punch_holes = + target_bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && + bdrv_can_write_zeroes_with_unmap(target_bs); bdrv_graph_co_rdlock(); bs = s->mirror_top_bs->backing->bs; bdrv_graph_co_rdunlock(); - if (s->zero_target) { + if (s->zero_target && (!s->target_is_zero || punch_holes)) { + /* + * Here, we are in FULL mode; our goal is to avoid writing + * zeroes if the destination already reads as zero, except + * when we are trying to punch holes. This is possible if + * zeroing happened externally (s->target_is_zero) or if we + * have a fast way to pre-zero the image (the dirty bitmap + * will be populated later by the non-zero portions, the same + * as for TOP mode). If pre-zeroing is not fast, or we need + * to punch holes, then our only recourse is to write the + * entire image. + */ if (!bdrv_can_write_zeroes_with_unmap(target_bs)) { bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length); return 0; @@ -1714,7 +1730,7 @@ static BlockJob *mirror_start_job( uint32_t granularity, int64_t buf_size, MirrorSyncMode sync_mode, BlockMirrorBackingMode backing_mode, - bool zero_target, + bool zero_target, bool target_is_zero, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, @@ -1883,6 +1899,7 @@ static BlockJob *mirror_start_job( s->sync_mode = sync_mode; s->backing_mode = backing_mode; s->zero_target = zero_target; + s->target_is_zero = target_is_zero; qatomic_set(&s->copy_mode, copy_mode); s->base = base; s->base_overlay = bdrv_find_overlay(bs, base); @@ -2011,7 +2028,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, int creation_flags, int64_t speed, uint32_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, - bool zero_target, + bool zero_target, bool target_is_zero, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, const char *filter_node_name, @@ -2034,7 +2051,8 @@ void mirror_start(const char *job_id, BlockDriverState *bs, mirror_start_job(job_id, bs, creation_flags, target, replaces, speed, granularity, buf_size, mode, backing_mode, - zero_target, on_source_error, on_target_error, unmap, + zero_target, + target_is_zero, on_source_error, on_target_error, unmap, NULL, NULL, &mirror_job_driver, base, false, filter_node_name, true, copy_mode, false, errp); } @@ -2062,6 +2080,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, job = mirror_start_job( job_id, bs, creation_flags, base, NULL, speed, 0, 0, MIRROR_SYNC_MODE_TOP, MIRROR_LEAVE_BACKING_CHAIN, false, + false, on_error, on_error, true, cb, opaque, &commit_active_job_driver, base, auto_complete, filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, diff --git a/blockdev.c b/blockdev.c index 818ec42511..97128feb27 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2804,7 +2804,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, const char *replaces, enum MirrorSyncMode sync, BlockMirrorBackingMode backing_mode, - bool zero_target, + bool zero_target, bool target_is_zero, bool has_speed, int64_t speed, bool has_granularity, uint32_t granularity, bool has_buf_size, int64_t buf_size, @@ -2915,11 +2915,10 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, /* pass the node name to replace to mirror start since it's loose coupling * and will allow to check whether the node still exist at mirror completion */ - mirror_start(job_id, bs, target, - replaces, job_flags, + mirror_start(job_id, bs, target, replaces, job_flags, speed, granularity, buf_size, sync, backing_mode, zero_target, - on_source_error, on_target_error, unmap, filter_node_name, - copy_mode, errp); + target_is_zero, on_source_error, on_target_error, unmap, + filter_node_name, copy_mode, errp); } void qmp_drive_mirror(DriveMirror *arg, Error **errp) @@ -2934,6 +2933,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) int64_t size; const char *format = arg->format; bool zero_target; + bool target_is_zero; int ret; bs = qmp_get_root_bs(arg->device, errp); @@ -3050,6 +3050,8 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL && (arg->mode == NEW_IMAGE_MODE_EXISTING || !bdrv_has_zero_init(target_bs))); + target_is_zero = (arg->mode != NEW_IMAGE_MODE_EXISTING && + bdrv_has_zero_init(target_bs)); bdrv_graph_rdunlock_main_loop(); @@ -3061,7 +3063,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) blockdev_mirror_common(arg->job_id, bs, target_bs, arg->replaces, arg->sync, - backing_mode, zero_target, + backing_mode, zero_target, target_is_zero, arg->has_speed, arg->speed, arg->has_granularity, arg->granularity, arg->has_buf_size, arg->buf_size, @@ -3091,6 +3093,7 @@ void qmp_blockdev_mirror(const char *job_id, bool has_copy_mode, MirrorCopyMode copy_mode, bool has_auto_finalize, bool auto_finalize, bool has_auto_dismiss, bool auto_dismiss, + bool has_target_is_zero, bool target_is_zero, Error **errp) { BlockDriverState *bs; @@ -3121,7 +3124,8 @@ void qmp_blockdev_mirror(const char *job_id, blockdev_mirror_common(job_id, bs, target_bs, replaces, sync, backing_mode, - zero_target, has_speed, speed, + zero_target, has_target_is_zero && target_is_zero, + has_speed, speed, has_granularity, granularity, has_buf_size, buf_size, has_on_source_error, on_source_error, diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h index 0d93783763..62a6c7e8e2 100644 --- a/include/block/block_int-global-state.h +++ b/include/block/block_int-global-state.h @@ -140,6 +140,7 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, * @mode: Whether to collapse all images in the chain to the target. * @backing_mode: How to establish the target's backing chain after completion. * @zero_target: Whether the target should be explicitly zero-initialized + * @target_is_zero: Whether the target already is zero-initialized. * @on_source_error: The action to take upon error reading from the source. * @on_target_error: The action to take upon error writing to the target. * @unmap: Whether to unmap target where source sectors only contain zeroes. @@ -159,7 +160,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, int creation_flags, int64_t speed, uint32_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, - bool zero_target, + bool zero_target, bool target_is_zero, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, const char *filter_node_name, diff --git a/qapi/block-core.json b/qapi/block-core.json index 91c70e24a7..b4115113d4 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2542,6 +2542,11 @@ # disappear from the query list without user intervention. # Defaults to true. (Since 3.1) # +# @target-is-zero: Assume the destination reads as all zeroes before +# the mirror started. Setting this to true can speed up the +# mirror. Setting this to true when the destination is not +# actually all zero can corrupt the destination. (Since 10.1) +# # Since: 2.6 # # .. qmp-example:: @@ -2561,7 +2566,8 @@ '*on-target-error': 'BlockdevOnError', '*filter-node-name': 'str', '*copy-mode': 'MirrorCopyMode', - '*auto-finalize': 'bool', '*auto-dismiss': 'bool' }, + '*auto-finalize': 'bool', '*auto-dismiss': 'bool', + '*target-is-zero': 'bool'}, 'allow-preconfig': true } ## diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index e26b3be593..54aed8252c 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -755,7 +755,7 @@ static void test_propagate_mirror(void) /* Start a mirror job */ mirror_start("job0", src, target, NULL, JOB_DEFAULT, 0, 0, 0, - MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false, + MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false, false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, false, "filter_node", MIRROR_COPY_MODE_BACKGROUND, &error_abort); From 253b43a29077de9266351e120c600a73b82e9c49 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:26 -0500 Subject: [PATCH 0952/2760] mirror: Drop redundant zero_target parameter The two callers to a mirror job (drive-mirror and blockdev-mirror) set zero_target precisely when sync mode == FULL, with the one exception that drive-mirror skips zeroing the target if it was newly created and reads as zero. But given the previous patch, that exception is equally captured by target_is_zero. Meanwhile, there is another slight wrinkle, fortunately caught by iotest 185: if the caller uses "sync":"top" but the source has no backing file, the code in blockdev.c was changing sync to be FULL, but only after it had set zero_target=false. In mirror.c, prior to recent patches, this didn't matter: the only places that inspected sync were setting is_none_mode (both TOP and FULL had set that to false), and mirror_start() setting base = mode == MIRROR_SYNC_MODE_TOP ? bdrv_backing_chain_next(bs) : NULL. But now that we are passing sync around, the slammed sync mode would result in a new pre-zeroing pass even when the user had passed "sync":"top" in an effort to skip pre-zeroing. Fortunately, the assignment of base when bs has no backing chain still works out to NULL if we don't slam things. So with the forced change of sync ripped out of blockdev.c, the sync mode is passed through the full callstack unmolested, and we can now reliably reconstruct the same settings as what used to be passed in by zero_target=false, without the redundant parameter. Signed-off-by: Eric Blake Message-ID: <20250509204341.3553601-24-eblake@redhat.com> Reviewed-by: Sunny Zhu Reviewed-by: Stefan Hajnoczi [eblake: Fix regression in iotest 185] Signed-off-by: Eric Blake --- block/mirror.c | 13 +++++-------- blockdev.c | 19 ++++--------------- include/block/block_int-global-state.h | 3 +-- tests/unit/test-block-iothread.c | 2 +- 4 files changed, 11 insertions(+), 26 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 4dcb50c81a..d04db85883 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -53,8 +53,6 @@ typedef struct MirrorBlockJob { Error *replace_blocker; MirrorSyncMode sync_mode; BlockMirrorBackingMode backing_mode; - /* Whether the target image requires explicit zero-initialization */ - bool zero_target; /* Whether the target should be assumed to be already zero initialized */ bool target_is_zero; /* @@ -854,7 +852,9 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) bs = s->mirror_top_bs->backing->bs; bdrv_graph_co_rdunlock(); - if (s->zero_target && (!s->target_is_zero || punch_holes)) { + if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { + /* In TOP mode, there is no benefit to a pre-zeroing pass. */ + } else if (!s->target_is_zero || punch_holes) { /* * Here, we are in FULL mode; our goal is to avoid writing * zeroes if the destination already reads as zero, except @@ -1730,7 +1730,7 @@ static BlockJob *mirror_start_job( uint32_t granularity, int64_t buf_size, MirrorSyncMode sync_mode, BlockMirrorBackingMode backing_mode, - bool zero_target, bool target_is_zero, + bool target_is_zero, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, @@ -1898,7 +1898,6 @@ static BlockJob *mirror_start_job( s->on_target_error = on_target_error; s->sync_mode = sync_mode; s->backing_mode = backing_mode; - s->zero_target = zero_target; s->target_is_zero = target_is_zero; qatomic_set(&s->copy_mode, copy_mode); s->base = base; @@ -2028,7 +2027,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, int creation_flags, int64_t speed, uint32_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, - bool zero_target, bool target_is_zero, + bool target_is_zero, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, const char *filter_node_name, @@ -2051,7 +2050,6 @@ void mirror_start(const char *job_id, BlockDriverState *bs, mirror_start_job(job_id, bs, creation_flags, target, replaces, speed, granularity, buf_size, mode, backing_mode, - zero_target, target_is_zero, on_source_error, on_target_error, unmap, NULL, NULL, &mirror_job_driver, base, false, filter_node_name, true, copy_mode, false, errp); @@ -2080,7 +2078,6 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, job = mirror_start_job( job_id, bs, creation_flags, base, NULL, speed, 0, 0, MIRROR_SYNC_MODE_TOP, MIRROR_LEAVE_BACKING_CHAIN, false, - false, on_error, on_error, true, cb, opaque, &commit_active_job_driver, base, auto_complete, filter_node_name, false, MIRROR_COPY_MODE_BACKGROUND, diff --git a/blockdev.c b/blockdev.c index 97128feb27..21443b4514 100644 --- a/blockdev.c +++ b/blockdev.c @@ -2804,7 +2804,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, const char *replaces, enum MirrorSyncMode sync, BlockMirrorBackingMode backing_mode, - bool zero_target, bool target_is_zero, + bool target_is_zero, bool has_speed, int64_t speed, bool has_granularity, uint32_t granularity, bool has_buf_size, int64_t buf_size, @@ -2871,10 +2871,6 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, return; } - if (!bdrv_backing_chain_next(bs) && sync == MIRROR_SYNC_MODE_TOP) { - sync = MIRROR_SYNC_MODE_FULL; - } - if (!replaces) { /* We want to mirror from @bs, but keep implicit filters on top */ unfiltered_bs = bdrv_skip_implicit_filters(bs); @@ -2916,7 +2912,7 @@ static void blockdev_mirror_common(const char *job_id, BlockDriverState *bs, * and will allow to check whether the node still exist at mirror completion */ mirror_start(job_id, bs, target, replaces, job_flags, - speed, granularity, buf_size, sync, backing_mode, zero_target, + speed, granularity, buf_size, sync, backing_mode, target_is_zero, on_source_error, on_target_error, unmap, filter_node_name, copy_mode, errp); } @@ -2932,7 +2928,6 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) int flags; int64_t size; const char *format = arg->format; - bool zero_target; bool target_is_zero; int ret; @@ -3047,9 +3042,6 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) } bdrv_graph_rdlock_main_loop(); - zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL && - (arg->mode == NEW_IMAGE_MODE_EXISTING || - !bdrv_has_zero_init(target_bs))); target_is_zero = (arg->mode != NEW_IMAGE_MODE_EXISTING && bdrv_has_zero_init(target_bs)); bdrv_graph_rdunlock_main_loop(); @@ -3063,7 +3055,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp) blockdev_mirror_common(arg->job_id, bs, target_bs, arg->replaces, arg->sync, - backing_mode, zero_target, target_is_zero, + backing_mode, target_is_zero, arg->has_speed, arg->speed, arg->has_granularity, arg->granularity, arg->has_buf_size, arg->buf_size, @@ -3100,7 +3092,6 @@ void qmp_blockdev_mirror(const char *job_id, BlockDriverState *target_bs; AioContext *aio_context; BlockMirrorBackingMode backing_mode = MIRROR_LEAVE_BACKING_CHAIN; - bool zero_target; int ret; bs = qmp_get_root_bs(device, errp); @@ -3113,8 +3104,6 @@ void qmp_blockdev_mirror(const char *job_id, return; } - zero_target = (sync == MIRROR_SYNC_MODE_FULL); - aio_context = bdrv_get_aio_context(bs); ret = bdrv_try_change_aio_context(target_bs, aio_context, NULL, errp); @@ -3124,7 +3113,7 @@ void qmp_blockdev_mirror(const char *job_id, blockdev_mirror_common(job_id, bs, target_bs, replaces, sync, backing_mode, - zero_target, has_target_is_zero && target_is_zero, + has_target_is_zero && target_is_zero, has_speed, speed, has_granularity, granularity, has_buf_size, buf_size, diff --git a/include/block/block_int-global-state.h b/include/block/block_int-global-state.h index 62a6c7e8e2..e7c8f1a856 100644 --- a/include/block/block_int-global-state.h +++ b/include/block/block_int-global-state.h @@ -139,7 +139,6 @@ BlockJob *commit_active_start(const char *job_id, BlockDriverState *bs, * @buf_size: The amount of data that can be in flight at one time. * @mode: Whether to collapse all images in the chain to the target. * @backing_mode: How to establish the target's backing chain after completion. - * @zero_target: Whether the target should be explicitly zero-initialized * @target_is_zero: Whether the target already is zero-initialized. * @on_source_error: The action to take upon error reading from the source. * @on_target_error: The action to take upon error writing to the target. @@ -160,7 +159,7 @@ void mirror_start(const char *job_id, BlockDriverState *bs, int creation_flags, int64_t speed, uint32_t granularity, int64_t buf_size, MirrorSyncMode mode, BlockMirrorBackingMode backing_mode, - bool zero_target, bool target_is_zero, + bool target_is_zero, BlockdevOnError on_source_error, BlockdevOnError on_target_error, bool unmap, const char *filter_node_name, diff --git a/tests/unit/test-block-iothread.c b/tests/unit/test-block-iothread.c index 54aed8252c..e26b3be593 100644 --- a/tests/unit/test-block-iothread.c +++ b/tests/unit/test-block-iothread.c @@ -755,7 +755,7 @@ static void test_propagate_mirror(void) /* Start a mirror job */ mirror_start("job0", src, target, NULL, JOB_DEFAULT, 0, 0, 0, - MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false, false, + MIRROR_SYNC_MODE_NONE, MIRROR_OPEN_BACKING_CHAIN, false, BLOCKDEV_ON_ERROR_REPORT, BLOCKDEV_ON_ERROR_REPORT, false, "filter_node", MIRROR_COPY_MODE_BACKGROUND, &error_abort); From 181a63667adf16c35b57e446def3e41c70f1fea6 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:27 -0500 Subject: [PATCH 0953/2760] mirror: Skip pre-zeroing destination if it is already zero When doing a sync=full mirroring, we can skip pre-zeroing the destination if it already reads as zeroes and we are not also trying to punch holes due to detect-zeroes. With this patch, there are fewer scenarios that have to pass in an explicit target-is-zero, while still resulting in a sparse destination remaining sparse. A later patch will then further improve things to skip writing to the destination for parts of the image where the source is zero; but even with just this patch, it is possible to see a difference for any source that does not report itself as fully allocated, coupled with a destination BDS that can quickly report that it already reads as zero. (For a source that reports as fully allocated, such as a file, the rest of mirror_dirty_init() still sets the entire dirty bitmap to true, so even though we avoided the pre-zeroing, we are not yet avoiding all redundant I/O). Iotest 194 detects the difference made by this patch: for a file source (where block status reports the entire image as allocated, and therefore we end up writing zeroes everywhere in the destination anyways), the job length remains the same. But for a qcow2 source and a destination that reads as all zeroes, the dirty bitmap changes to just tracking the allocated portions of the source, which results in faster completion and smaller job statistics. For the test to pass with both ./check -file and -qcow2, a new python filter is needed to mask out the now-varying job amounts (this matches the shell filters _filter_block_job_{offset,len} in common.filter). A later test will also be added which further validates expected sparseness, so it does not matter that 194 is no longer explicitly looking at how many bytes were copied. Signed-off-by: Eric Blake Message-ID: <20250509204341.3553601-25-eblake@redhat.com> Reviewed-by: Sunny Zhu Reviewed-by: Stefan Hajnoczi --- block/mirror.c | 24 ++++++++++++++++-------- tests/qemu-iotests/194 | 6 ++++-- tests/qemu-iotests/194.out | 4 ++-- tests/qemu-iotests/iotests.py | 12 +++++++++++- 4 files changed, 33 insertions(+), 13 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index d04db85883..bca99ec206 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -848,23 +848,31 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) target_bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && bdrv_can_write_zeroes_with_unmap(target_bs); + /* Determine if the image is already zero, regardless of sync mode. */ bdrv_graph_co_rdlock(); bs = s->mirror_top_bs->backing->bs; + if (s->target_is_zero) { + ret = 1; + } else { + ret = bdrv_co_is_all_zeroes(target_bs); + } bdrv_graph_co_rdunlock(); - if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { + /* Determine if a pre-zeroing pass is necessary. */ + if (ret < 0) { + return ret; + } else if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { /* In TOP mode, there is no benefit to a pre-zeroing pass. */ - } else if (!s->target_is_zero || punch_holes) { + } else if (ret == 0 || punch_holes) { /* * Here, we are in FULL mode; our goal is to avoid writing * zeroes if the destination already reads as zero, except * when we are trying to punch holes. This is possible if - * zeroing happened externally (s->target_is_zero) or if we - * have a fast way to pre-zero the image (the dirty bitmap - * will be populated later by the non-zero portions, the same - * as for TOP mode). If pre-zeroing is not fast, or we need - * to punch holes, then our only recourse is to write the - * entire image. + * zeroing happened externally (ret > 0) or if we have a fast + * way to pre-zero the image (the dirty bitmap will be + * populated later by the non-zero portions, the same as for + * TOP mode). If pre-zeroing is not fast, or we need to punch + * holes, then our only recourse is to write the entire image. */ if (!bdrv_can_write_zeroes_with_unmap(target_bs)) { bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length); diff --git a/tests/qemu-iotests/194 b/tests/qemu-iotests/194 index d0b9c084f5..e114c0b269 100755 --- a/tests/qemu-iotests/194 +++ b/tests/qemu-iotests/194 @@ -62,7 +62,8 @@ with iotests.FilePath('source.img') as source_img_path, \ iotests.log('Waiting for `drive-mirror` to complete...') iotests.log(source_vm.event_wait('BLOCK_JOB_READY'), - filters=[iotests.filter_qmp_event]) + filters=[iotests.filter_qmp_event, + iotests.filter_block_job]) iotests.log('Starting migration...') capabilities = [{'capability': 'events', 'state': True}, @@ -88,7 +89,8 @@ with iotests.FilePath('source.img') as source_img_path, \ while True: event2 = source_vm.event_wait('BLOCK_JOB_COMPLETED') - iotests.log(event2, filters=[iotests.filter_qmp_event]) + iotests.log(event2, filters=[iotests.filter_qmp_event, + iotests.filter_block_job]) if event2['event'] == 'BLOCK_JOB_COMPLETED': iotests.log('Stopping the NBD server on destination...') iotests.log(dest_vm.qmp('nbd-server-stop')) diff --git a/tests/qemu-iotests/194.out b/tests/qemu-iotests/194.out index 6940e809cd..d02655a514 100644 --- a/tests/qemu-iotests/194.out +++ b/tests/qemu-iotests/194.out @@ -7,7 +7,7 @@ Launching NBD server on destination... Starting `drive-mirror` on source... {"return": {}} Waiting for `drive-mirror` to complete... -{"data": {"device": "mirror-job0", "len": 1073741824, "offset": 1073741824, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "mirror-job0", "len": "LEN", "offset": "OFFSET", "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_READY", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Starting migration... {"return": {}} {"execute": "migrate-start-postcopy", "arguments": {}} @@ -18,7 +18,7 @@ Starting migration... {"data": {"status": "completed"}, "event": "MIGRATION", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Gracefully ending the `drive-mirror` job on source... {"return": {}} -{"data": {"device": "mirror-job0", "len": 1073741824, "offset": 1073741824, "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} +{"data": {"device": "mirror-job0", "len": "LEN", "offset": "OFFSET", "speed": 0, "type": "mirror"}, "event": "BLOCK_JOB_COMPLETED", "timestamp": {"microseconds": "USECS", "seconds": "SECS"}} Stopping the NBD server on destination... {"return": {}} Wait for migration completion on target... diff --git a/tests/qemu-iotests/iotests.py b/tests/qemu-iotests/iotests.py index 7292c8b342..05274772ce 100644 --- a/tests/qemu-iotests/iotests.py +++ b/tests/qemu-iotests/iotests.py @@ -601,13 +601,23 @@ def filter_chown(msg): return chown_re.sub("chown UID:GID", msg) def filter_qmp_event(event): - '''Filter a QMP event dict''' + '''Filter the timestamp of a QMP event dict''' event = dict(event) if 'timestamp' in event: event['timestamp']['seconds'] = 'SECS' event['timestamp']['microseconds'] = 'USECS' return event +def filter_block_job(event): + '''Filter the offset and length of a QMP block job event dict''' + event = dict(event) + if 'data' in event: + if 'offset' in event['data']: + event['data']['offset'] = 'OFFSET' + if 'len' in event['data']: + event['data']['len'] = 'LEN' + return event + def filter_qmp(qmsg, filter_fn): '''Given a string filter, filter a QMP object's values. filter_fn takes a (key, value) pair.''' From 7e277545b90874171128804e256a538fb0e8dd7e Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:28 -0500 Subject: [PATCH 0954/2760] mirror: Skip writing zeroes when target is already zero When mirroring, the goal is to ensure that the destination reads the same as the source; this goal is met whether the destination is sparse or fully-allocated (except when explicitly punching holes, then merely reading zero is not enough to know if it is sparse, so we still want to punch the hole). Avoiding a redundant write to zero (whether in the background because the zero cluster was marked in the dirty bitmap, or in the foreground because the guest is writing zeroes) when the destination already reads as zero makes mirroring faster, and avoids allocating the destination merely because the source reports as allocated. The effect is especially pronounced when the source is a raw file. That's because when the source is a qcow2 file, the dirty bitmap only visits the portions of the source that are allocated, which tend to be non-zero. But when the source is a raw file, bdrv_co_is_allocated_above() reports the entire file as allocated so mirror_dirty_init sets the entire dirty bitmap, and it is only later during mirror_iteration that we change to consulting the more precise bdrv_co_block_status_above() to learn where the source reads as zero. Remember that since a mirror operation can write a cluster more than once (every time the guest changes the source, the destination is also changed to keep up), and the guest can change whether a given cluster reads as zero, is discarded, or has non-zero data over the course of the mirror operation, we can't take the shortcut of relying on s->target_is_zero (which is static for the life of the job) in mirror_co_zero() to see if the destination is already zero, because that information may be stale. Any solution we use must be dynamic in the face of the guest writing or discarding a cluster while the mirror has been ongoing. We could just teach mirror_co_zero() to do a block_status() probe of the destination, and skip the zeroes if the destination already reads as zero, but we know from past experience that extra block_status() calls are not always cheap (tmpfs, anyone?), especially when they are random access rather than linear. Use of block_status() of the source by the background task in a linear fashion is not our bottleneck (it's a background task, after all); but since mirroring can be done while the source is actively being changed, we don't want a slow block_status() of the destination to occur on the hot path of the guest trying to do random-access writes to the source. So this patch takes a slightly different approach: any time we have to track dirty clusters, we can also track which clusters are known to read as zero. For sync=TOP or when we are punching holes from "detect-zeroes":"unmap", the zero bitmap starts out empty, but prevents a second write zero to a cluster that was already zero by an earlier pass; for sync=FULL when we are not punching holes, the zero bitmap starts out full if the destination reads as zero during initialization. Either way, I/O to the destination can now avoid redundant write zero to a cluster that already reads as zero, all without having to do a block_status() per write on the destination. With this patch, if I create a raw sparse destination file, connect it with QMP 'blockdev-add' while leaving it at the default "discard": "ignore", then run QMP 'blockdev-mirror' with "sync": "full", the destination remains sparse rather than fully allocated. Meanwhile, a destination image that is already fully allocated remains so unless it was opened with "detect-zeroes": "unmap". And any time writing zeroes is skipped, the job counters are not incremented. Signed-off-by: Eric Blake Message-ID: <20250509204341.3553601-26-eblake@redhat.com> Reviewed-by: Stefan Hajnoczi --- block/mirror.c | 107 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 93 insertions(+), 14 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index bca99ec206..724318f037 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -73,6 +73,7 @@ typedef struct MirrorBlockJob { size_t buf_size; int64_t bdev_length; unsigned long *cow_bitmap; + unsigned long *zero_bitmap; BdrvDirtyBitmap *dirty_bitmap; BdrvDirtyBitmapIter *dbi; uint8_t *buf; @@ -108,9 +109,12 @@ struct MirrorOp { int64_t offset; uint64_t bytes; - /* The pointee is set by mirror_co_read(), mirror_co_zero(), and - * mirror_co_discard() before yielding for the first time */ + /* + * These pointers are set by mirror_co_read(), mirror_co_zero(), and + * mirror_co_discard() before yielding for the first time + */ int64_t *bytes_handled; + bool *io_skipped; bool is_pseudo_op; bool is_active_write; @@ -408,15 +412,34 @@ static void coroutine_fn mirror_co_read(void *opaque) static void coroutine_fn mirror_co_zero(void *opaque) { MirrorOp *op = opaque; - int ret; + bool write_needed = true; + int ret = 0; op->s->in_flight++; op->s->bytes_in_flight += op->bytes; *op->bytes_handled = op->bytes; op->is_in_flight = true; - ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes, - op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0); + if (op->s->zero_bitmap) { + unsigned long end = DIV_ROUND_UP(op->offset + op->bytes, + op->s->granularity); + assert(QEMU_IS_ALIGNED(op->offset, op->s->granularity)); + assert(QEMU_IS_ALIGNED(op->bytes, op->s->granularity) || + op->offset + op->bytes == op->s->bdev_length); + if (find_next_zero_bit(op->s->zero_bitmap, end, + op->offset / op->s->granularity) == end) { + write_needed = false; + *op->io_skipped = true; + } + } + if (write_needed) { + ret = blk_co_pwrite_zeroes(op->s->target, op->offset, op->bytes, + op->s->unmap ? BDRV_REQ_MAY_UNMAP : 0); + } + if (ret >= 0 && op->s->zero_bitmap) { + bitmap_set(op->s->zero_bitmap, op->offset / op->s->granularity, + DIV_ROUND_UP(op->bytes, op->s->granularity)); + } mirror_write_complete(op, ret); } @@ -435,29 +458,43 @@ static void coroutine_fn mirror_co_discard(void *opaque) } static unsigned mirror_perform(MirrorBlockJob *s, int64_t offset, - unsigned bytes, MirrorMethod mirror_method) + unsigned bytes, MirrorMethod mirror_method, + bool *io_skipped) { MirrorOp *op; Coroutine *co; int64_t bytes_handled = -1; + assert(QEMU_IS_ALIGNED(offset, s->granularity)); + assert(QEMU_IS_ALIGNED(bytes, s->granularity) || + offset + bytes == s->bdev_length); op = g_new(MirrorOp, 1); *op = (MirrorOp){ .s = s, .offset = offset, .bytes = bytes, .bytes_handled = &bytes_handled, + .io_skipped = io_skipped, }; qemu_co_queue_init(&op->waiting_requests); switch (mirror_method) { case MIRROR_METHOD_COPY: + if (s->zero_bitmap) { + bitmap_clear(s->zero_bitmap, offset / s->granularity, + DIV_ROUND_UP(bytes, s->granularity)); + } co = qemu_coroutine_create(mirror_co_read, op); break; case MIRROR_METHOD_ZERO: + /* s->zero_bitmap handled in mirror_co_zero */ co = qemu_coroutine_create(mirror_co_zero, op); break; case MIRROR_METHOD_DISCARD: + if (s->zero_bitmap) { + bitmap_clear(s->zero_bitmap, offset / s->granularity, + DIV_ROUND_UP(bytes, s->granularity)); + } co = qemu_coroutine_create(mirror_co_discard, op); break; default: @@ -568,6 +605,7 @@ static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s) int ret = -1; int64_t io_bytes; int64_t io_bytes_acct; + bool io_skipped = false; MirrorMethod mirror_method = MIRROR_METHOD_COPY; assert(!(offset % s->granularity)); @@ -611,8 +649,10 @@ static void coroutine_fn GRAPH_UNLOCKED mirror_iteration(MirrorBlockJob *s) } io_bytes = mirror_clip_bytes(s, offset, io_bytes); - io_bytes = mirror_perform(s, offset, io_bytes, mirror_method); - if (mirror_method != MIRROR_METHOD_COPY && write_zeroes_ok) { + io_bytes = mirror_perform(s, offset, io_bytes, mirror_method, + &io_skipped); + if (io_skipped || + (mirror_method != MIRROR_METHOD_COPY && write_zeroes_ok)) { io_bytes_acct = 0; } else { io_bytes_acct = io_bytes; @@ -847,8 +887,10 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) bool punch_holes = target_bs->detect_zeroes == BLOCKDEV_DETECT_ZEROES_OPTIONS_UNMAP && bdrv_can_write_zeroes_with_unmap(target_bs); + int64_t bitmap_length = DIV_ROUND_UP(s->bdev_length, s->granularity); /* Determine if the image is already zero, regardless of sync mode. */ + s->zero_bitmap = bitmap_new(bitmap_length); bdrv_graph_co_rdlock(); bs = s->mirror_top_bs->backing->bs; if (s->target_is_zero) { @@ -862,7 +904,14 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) if (ret < 0) { return ret; } else if (s->sync_mode == MIRROR_SYNC_MODE_TOP) { - /* In TOP mode, there is no benefit to a pre-zeroing pass. */ + /* + * In TOP mode, there is no benefit to a pre-zeroing pass, but + * the zero bitmap can be set if the destination already reads + * as zero and we are not punching holes. + */ + if (ret > 0 && !punch_holes) { + bitmap_set(s->zero_bitmap, 0, bitmap_length); + } } else if (ret == 0 || punch_holes) { /* * Here, we are in FULL mode; our goal is to avoid writing @@ -871,8 +920,9 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) * zeroing happened externally (ret > 0) or if we have a fast * way to pre-zero the image (the dirty bitmap will be * populated later by the non-zero portions, the same as for - * TOP mode). If pre-zeroing is not fast, or we need to punch - * holes, then our only recourse is to write the entire image. + * TOP mode). If pre-zeroing is not fast, then our only + * recourse is to mark the entire image dirty. The act of + * pre-zeroing will populate the zero bitmap. */ if (!bdrv_can_write_zeroes_with_unmap(target_bs)) { bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length); @@ -883,6 +933,7 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) for (offset = 0; offset < s->bdev_length; ) { int bytes = MIN(s->bdev_length - offset, QEMU_ALIGN_DOWN(INT_MAX, s->granularity)); + bool ignored; mirror_throttle(s); @@ -898,12 +949,15 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) continue; } - mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO); + mirror_perform(s, offset, bytes, MIRROR_METHOD_ZERO, &ignored); offset += bytes; } mirror_wait_for_all_io(s); s->initial_zeroing_ongoing = false; + } else { + /* In FULL mode, and image already reads as zero. */ + bitmap_set(s->zero_bitmap, 0, bitmap_length); } /* First part, loop on the sectors and initialize the dirty bitmap. */ @@ -1188,6 +1242,7 @@ static int coroutine_fn mirror_run(Job *job, Error **errp) assert(s->in_flight == 0); qemu_vfree(s->buf); g_free(s->cow_bitmap); + g_free(s->zero_bitmap); g_free(s->in_flight_bitmap); bdrv_dirty_iter_free(s->dbi); @@ -1367,6 +1422,7 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, int ret; size_t qiov_offset = 0; int64_t dirty_bitmap_offset, dirty_bitmap_end; + int64_t zero_bitmap_offset, zero_bitmap_end; if (!QEMU_IS_ALIGNED(offset, job->granularity) && bdrv_dirty_bitmap_get(job->dirty_bitmap, offset)) @@ -1410,8 +1466,9 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, } /* - * Tails are either clean or shrunk, so for bitmap resetting - * we safely align the range down. + * Tails are either clean or shrunk, so for dirty bitmap resetting + * we safely align the range narrower. But for zero bitmap, round + * range wider for checking or clearing, and narrower for setting. */ dirty_bitmap_offset = QEMU_ALIGN_UP(offset, job->granularity); dirty_bitmap_end = QEMU_ALIGN_DOWN(offset + bytes, job->granularity); @@ -1419,22 +1476,44 @@ do_sync_target_write(MirrorBlockJob *job, MirrorMethod method, bdrv_reset_dirty_bitmap(job->dirty_bitmap, dirty_bitmap_offset, dirty_bitmap_end - dirty_bitmap_offset); } + zero_bitmap_offset = offset / job->granularity; + zero_bitmap_end = DIV_ROUND_UP(offset + bytes, job->granularity); job_progress_increase_remaining(&job->common.job, bytes); job->active_write_bytes_in_flight += bytes; switch (method) { case MIRROR_METHOD_COPY: + if (job->zero_bitmap) { + bitmap_clear(job->zero_bitmap, zero_bitmap_offset, + zero_bitmap_end - zero_bitmap_offset); + } ret = blk_co_pwritev_part(job->target, offset, bytes, qiov, qiov_offset, flags); break; case MIRROR_METHOD_ZERO: + if (job->zero_bitmap) { + if (find_next_zero_bit(job->zero_bitmap, zero_bitmap_end, + zero_bitmap_offset) == zero_bitmap_end) { + ret = 0; + break; + } + } assert(!qiov); ret = blk_co_pwrite_zeroes(job->target, offset, bytes, flags); + if (job->zero_bitmap && ret >= 0) { + bitmap_set(job->zero_bitmap, dirty_bitmap_offset / job->granularity, + (dirty_bitmap_end - dirty_bitmap_offset) / + job->granularity); + } break; case MIRROR_METHOD_DISCARD: + if (job->zero_bitmap) { + bitmap_clear(job->zero_bitmap, zero_bitmap_offset, + zero_bitmap_end - zero_bitmap_offset); + } assert(!qiov); ret = blk_co_pdiscard(job->target, offset, bytes); break; From be9bac072ede6e6aa27079f59efcf17b56bd7b26 Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Fri, 9 May 2025 15:40:29 -0500 Subject: [PATCH 0955/2760] iotests/common.rc: add disk_usage function Move the definition from iotests/250 to common.rc. This is used to detect real disk usage of sparse files. In particular, we want to use it for checking subclusters-based discards. Signed-off-by: Andrey Drobyshev Reviewed-by: Alexander Ivanov Reviewed-by: Alberto Garcia Message-ID: <20240913163942.423050-6-andrey.drobyshev@virtuozzo.com> Signed-off-by: Eric Blake Reviewed-by: Stefan Hajnoczi Message-ID: <20250509204341.3553601-27-eblake@redhat.com> --- tests/qemu-iotests/250 | 5 ----- tests/qemu-iotests/common.rc | 6 ++++++ 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/tests/qemu-iotests/250 b/tests/qemu-iotests/250 index af48f83aba..c0a0dbc0ff 100755 --- a/tests/qemu-iotests/250 +++ b/tests/qemu-iotests/250 @@ -52,11 +52,6 @@ _unsupported_imgopts data_file # bdrv_co_truncate(bs->file) call in qcow2_co_truncate(), which might succeed # anyway. -disk_usage() -{ - du --block-size=1 $1 | awk '{print $1}' -} - size=2100M _make_test_img -o "cluster_size=1M,preallocation=metadata" $size diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 95c12577dd..237f746af8 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -140,6 +140,12 @@ _optstr_add() fi } +# report real disk usage for sparse files +disk_usage() +{ + du --block-size=1 "$1" | awk '{print $1}' +} + # Set the variables to the empty string to turn Valgrind off # for specific processes, e.g. # $ VALGRIND_QEMU_IO= ./check -qcow2 -valgrind 015 From c0ddcb2cbc146e64f666eaae4edc7b5db7e5814d Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 9 May 2025 15:40:30 -0500 Subject: [PATCH 0956/2760] tests: Add iotest mirror-sparse for recent patches Prove that blockdev-mirror can now result in sparse raw destination files, regardless of whether the source is raw or qcow2. By making this a separate test, it was possible to test effects of individual patches for the various pieces that all have to work together for a sparse mirror to be successful. Note that ./check -file produces different job lengths than ./check -qcow2 (the test uses a filter to normalize); that's because when deciding how much of the image to be mirrored, the code looks at how much of the source image was allocated (for qcow2, this is only the written clusters; for raw, it is the entire file). But the important part is that the destination file ends up smaller than 3M, rather than the 20M it used to be before this patch series. Signed-off-by: Eric Blake Message-ID: <20250509204341.3553601-28-eblake@redhat.com> Reviewed-by: Stefan Hajnoczi --- tests/qemu-iotests/tests/mirror-sparse | 125 +++++++ tests/qemu-iotests/tests/mirror-sparse.out | 365 +++++++++++++++++++++ 2 files changed, 490 insertions(+) create mode 100755 tests/qemu-iotests/tests/mirror-sparse create mode 100644 tests/qemu-iotests/tests/mirror-sparse.out diff --git a/tests/qemu-iotests/tests/mirror-sparse b/tests/qemu-iotests/tests/mirror-sparse new file mode 100755 index 0000000000..8c52a4e244 --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-sparse @@ -0,0 +1,125 @@ +#!/usr/bin/env bash +# group: rw auto quick +# +# Test blockdev-mirror with raw sparse destination +# +# Copyright (C) 2025 Red Hat, Inc. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +seq="$(basename $0)" +echo "QA output created by $seq" + +status=1 # failure is the default! + +_cleanup() +{ + _cleanup_test_img + _cleanup_qemu +} +trap "_cleanup; exit \$status" 0 1 2 3 15 + +# get standard environment, filters and checks +cd .. +. ./common.rc +. ./common.filter +. ./common.qemu + +_supported_fmt qcow2 raw # Format of the source. dst is always raw file +_supported_proto file +_supported_os Linux + +echo +echo "=== Initial image setup ===" +echo + +TEST_IMG="$TEST_IMG.base" _make_test_img 20M +$QEMU_IO -c 'w 8M 2M' -f $IMGFMT "$TEST_IMG.base" | _filter_qemu_io + +_launch_qemu \ + -blockdev '{"driver":"file", "cache":{"direct":true, "no-flush":false}, + "filename":"'"$TEST_IMG.base"'", "node-name":"src-file"}' \ + -blockdev '{"driver":"'$IMGFMT'", "node-name":"src", "file":"src-file"}' +h1=$QEMU_HANDLE +_send_qemu_cmd $h1 '{"execute": "qmp_capabilities"}' 'return' + +# Check several combinations; most should result in a sparse destination; +# the destination should only be fully allocated if pre-allocated +# and not punching holes due to detect-zeroes +# do_test creation discard zeroes result +do_test() { + creation=$1 + discard=$2 + zeroes=$3 + expected=$4 + +echo +echo "=== Testing creation=$creation discard=$discard zeroes=$zeroes ===" +echo + +rm -f $TEST_IMG +if test $creation = external; then + truncate --size=20M $TEST_IMG +else + _send_qemu_cmd $h1 '{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"'$TEST_IMG'", + "size":'$((20*1024*1024))', "preallocation":"'$creation'"}, + "job-id":"job1"}}' 'concluded' + _send_qemu_cmd $h1 '{"execute": "job-dismiss", "arguments": + {"id": "job1"}}' 'return' +fi +_send_qemu_cmd $h1 '{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"'$TEST_IMG'", "aio":"threads", + "auto-read-only":true, "discard":"'$discard'", + "detect-zeroes":"'$zeroes'"}}' 'return' +_send_qemu_cmd $h1 '{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}}' 'return' +_timed_wait_for $h1 '"ready"' +_send_qemu_cmd $h1 '{"execute": "job-complete", "arguments": + {"id":"job2"}}' 'return' \ + | _filter_block_job_offset | _filter_block_job_len +_send_qemu_cmd $h1 '{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}}' 'return' \ + | _filter_block_job_offset | _filter_block_job_len +$QEMU_IMG compare -U -f $IMGFMT -F raw $TEST_IMG.base $TEST_IMG +result=$(disk_usage $TEST_IMG) +if test $result -lt $((3*1024*1024)); then + actual=sparse +elif test $result = $((20*1024*1024)); then + actual=full +else + actual=unknown +fi +echo "Destination is $actual; expected $expected" +} + +do_test external ignore off sparse +do_test external unmap off sparse +do_test external unmap unmap sparse +do_test off ignore off sparse +do_test off unmap off sparse +do_test off unmap unmap sparse +do_test full ignore off full +do_test full unmap off sparse +do_test full unmap unmap sparse + +_send_qemu_cmd $h1 '{"execute":"quit"}' '' + +# success, all done +echo '*** done' +rm -f $seq.full +status=0 diff --git a/tests/qemu-iotests/tests/mirror-sparse.out b/tests/qemu-iotests/tests/mirror-sparse.out new file mode 100644 index 0000000000..2103b891c3 --- /dev/null +++ b/tests/qemu-iotests/tests/mirror-sparse.out @@ -0,0 +1,365 @@ +QA output created by mirror-sparse + +=== Initial image setup === + +Formatting 'TEST_DIR/t.IMGFMT.base', fmt=IMGFMT size=20971520 +wrote 2097152/2097152 bytes at offset 8388608 +2 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +{"execute": "qmp_capabilities"} +{"return": {}} + +=== Testing creation=external discard=ignore zeroes=off === + +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"ignore", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=external discard=unmap zeroes=off === + +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=external discard=unmap zeroes=unmap === + +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"unmap"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=off discard=ignore zeroes=off === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"off"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"ignore", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=off discard=unmap zeroes=off === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"off"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=off discard=unmap zeroes=unmap === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"off"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"unmap"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=full discard=ignore zeroes=off === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"full"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"ignore", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is full; expected full + +=== Testing creation=full discard=unmap zeroes=off === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"full"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"off"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse + +=== Testing creation=full discard=unmap zeroes=unmap === + +{"execute": "blockdev-create", "arguments": + {"options": {"driver":"file", "filename":"TEST_DIR/t.IMGFMT", + "size":20971520, "preallocation":"full"}, + "job-id":"job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job1"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job1"}} +{"execute": "job-dismiss", "arguments": + {"id": "job1"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job1"}} +{"return": {}} +{"execute": "blockdev-add", "arguments": + {"node-name": "dst", "driver":"file", + "filename":"TEST_DIR/t.IMGFMT", "aio":"threads", + "auto-read-only":true, "discard":"unmap", + "detect-zeroes":"unmap"}} +{"return": {}} +{"execute":"blockdev-mirror", "arguments": + {"sync":"full", "device":"src", "target":"dst", + "job-id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "created", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "running", "id": "job2"}} +{"return": {}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "ready", "id": "job2"}} +{"execute": "job-complete", "arguments": + {"id":"job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_READY", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"return": {}} +{"execute": "blockdev-del", "arguments": + {"node-name": "dst"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "waiting", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "pending", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "BLOCK_JOB_COMPLETED", "data": {"device": "job2", "len": LEN, "offset": OFFSET, "speed": 0, "type": "mirror"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "concluded", "id": "job2"}} +{"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "JOB_STATUS_CHANGE", "data": {"status": "null", "id": "job2"}} +{"return": {}} +Images are identical. +Destination is sparse; expected sparse +{"execute":"quit"} +*** done From aff46b4bf556430dd3c12fa39a457f0487bb0053 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Tue, 13 May 2025 17:00:45 -0500 Subject: [PATCH 0957/2760] mirror: Reduce I/O when destination is detect-zeroes:unmap If we are going to punch holes in the mirror destination even for the portions where the source image is unallocated, it is nicer to treat the entire image as dirty and punch as we go, rather than pre-zeroing the entire image just to re-do I/O to the allocated portions of the image. Signed-off-by: Eric Blake Message-ID: <20250513220142.535200-2-eblake@redhat.com> Reviewed-by: Stefan Hajnoczi --- block/mirror.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 724318f037..c2c5099c95 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -920,11 +920,16 @@ static int coroutine_fn GRAPH_UNLOCKED mirror_dirty_init(MirrorBlockJob *s) * zeroing happened externally (ret > 0) or if we have a fast * way to pre-zero the image (the dirty bitmap will be * populated later by the non-zero portions, the same as for - * TOP mode). If pre-zeroing is not fast, then our only - * recourse is to mark the entire image dirty. The act of - * pre-zeroing will populate the zero bitmap. + * TOP mode). If pre-zeroing is not fast, or we need to visit + * the entire image in order to punch holes even in the + * non-allocated regions of the source, then just mark the + * entire image dirty and leave the zero bitmap clear at this + * point in time. Otherwise, it can be faster to pre-zero the + * image now, even if we re-write the allocated portions of + * the disk later, and the pre-zero pass will populate the + * zero bitmap. */ - if (!bdrv_can_write_zeroes_with_unmap(target_bs)) { + if (!bdrv_can_write_zeroes_with_unmap(target_bs) || punch_holes) { bdrv_set_dirty_bitmap(s->dirty_bitmap, 0, s->bdev_length); return 0; } From 0b1c23a582f7bc721a9b858c289a8d165152a6a0 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Wed, 7 May 2025 09:30:55 +0200 Subject: [PATCH 0958/2760] hw/nvme: fix nvme hotplugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit cd59f50ab017 caused a regression on nvme hotplugging for devices with an implicit nvm subsystem. The nvme-subsys device was incorrectly left with being marked as non-hotpluggable. Fix this. Cc: qemu-stable@nongnu.org Reported-by: Stéphane Graber Tested-by: Stéphane Graber Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2950 Fixes: cd59f50ab017 ("hw/nvme: always initialize a subsystem") Reviewed-by: Keith Busch Signed-off-by: Klaus Jensen --- hw/nvme/subsys.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/nvme/subsys.c b/hw/nvme/subsys.c index 38271d78c8..777e1c620f 100644 --- a/hw/nvme/subsys.c +++ b/hw/nvme/subsys.c @@ -226,7 +226,6 @@ static void nvme_subsys_class_init(ObjectClass *oc, const void *data) dc->realize = nvme_subsys_realize; dc->desc = "Virtual NVMe subsystem"; - dc->hotpluggable = false; device_class_set_props(dc, nvme_subsystem_props); } From 923976dfe367b0bfed45ff660c369f3fe65604a7 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sat, 17 May 2025 13:12:07 +0200 Subject: [PATCH 0959/2760] target/hppa: Copy instruction code into fr1 on FPU assist fault The hardware stores the instruction code in the lower bits of the FP exception register #1 on FP assist traps. This fixes the FP exception handler on Linux, as the Linux kernel uses the value to decide on the correct signal which should be pushed into userspace (see decode_fpu() in Linux kernel). Signed-off-by: Helge Deller --- target/hppa/int_helper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/hppa/int_helper.c b/target/hppa/int_helper.c index 7d48643bb6..191ae19404 100644 --- a/target/hppa/int_helper.c +++ b/target/hppa/int_helper.c @@ -177,6 +177,10 @@ void hppa_cpu_do_interrupt(CPUState *cs) } } env->cr[CR_IIR] = ldl_phys(cs->as, paddr); + if (i == EXCP_ASSIST) { + /* stuff insn code into bits of FP exception register #1 */ + env->fr[0] |= (env->cr[CR_IIR] & 0x03ffffff); + } } break; From b4b49cf39dba5f993ad925f204cb820aacfc8e45 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sat, 17 May 2025 13:20:17 +0200 Subject: [PATCH 0960/2760] linux-user/hppa: Send proper si_code on SIGFPE exception Improve the linux-user emulation to send the correct si_code depending on overflow (TARGET_FPE_FLTOVF), underflow (TARGET_FPE_FLTUND), ... Note that the hardware stores the relevant flags in FP exception register #1, which is actually the lower 32-bits of the 64-bit fr[0] register in qemu. Signed-off-by: Helge Deller --- linux-user/hppa/cpu_loop.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/linux-user/hppa/cpu_loop.c b/linux-user/hppa/cpu_loop.c index 890e758cd1..9abaad5ef8 100644 --- a/linux-user/hppa/cpu_loop.c +++ b/linux-user/hppa/cpu_loop.c @@ -112,7 +112,7 @@ static abi_ulong hppa_lws(CPUHPPAState *env) void cpu_loop(CPUHPPAState *env) { CPUState *cs = env_cpu(env); - abi_ulong ret; + abi_ulong ret, si_code = 0; int trapnr; while (1) { @@ -169,7 +169,15 @@ void cpu_loop(CPUHPPAState *env) force_sig_fault(TARGET_SIGFPE, TARGET_FPE_CONDTRAP, env->iaoq_f); break; case EXCP_ASSIST: - force_sig_fault(TARGET_SIGFPE, 0, env->iaoq_f); + #define set_si_code(mask, val) \ + if (env->fr[0] & mask) { si_code = val; } + set_si_code(R_FPSR_FLG_I_MASK, TARGET_FPE_FLTRES); + set_si_code(R_FPSR_FLG_U_MASK, TARGET_FPE_FLTUND); + set_si_code(R_FPSR_FLG_O_MASK, TARGET_FPE_FLTOVF); + set_si_code(R_FPSR_FLG_Z_MASK, TARGET_FPE_FLTDIV); + set_si_code(R_FPSR_FLG_V_MASK, TARGET_FPE_FLTINV); + #undef set_si_code + force_sig_fault(TARGET_SIGFPE, si_code, env->iaoq_f); break; case EXCP_BREAK: force_sig_fault(TARGET_SIGTRAP, TARGET_TRAP_BRKPT, env->iaoq_f); From ebd394948de4e868cb8fc5b265a8a18f0935dce1 Mon Sep 17 00:00:00 2001 From: Helge Deller Date: Sat, 17 May 2025 13:27:48 +0200 Subject: [PATCH 0961/2760] target/hppa: Fix FPE exceptions Implement FP exception register #1 (lower 32-bits of 64-bit fr[0]). A proper implementation is necessary to allow the Linux kernel in system mode and the qemu linux-user to send proper si_code values on SIGFPE signal. Always set the T-bit on taken exception, and merge over- and underflow in system mode to just set overflow bit to mimic the behaviour I tested on a physical machine. The test program below can be used to verify correct behaviour. Note that behaviour on SIGFPE may vary on different platforms. The program should always detect the correct signal, but it may or may not be able to sucessfully continue afterwards. #define _GNU_SOURCE #include #include #include #include static void fpe_func(int sig, siginfo_t *i, void *v) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGFPE); sigprocmask(SIG_UNBLOCK, &set, NULL); printf("GOT signal %d with si_code %ld\n", sig, i->si_code); } int main(int argc, char *argv[]) { struct sigaction action = { .sa_sigaction = fpe_func, .sa_flags = SA_RESTART|SA_SIGINFO }; sigaction(SIGFPE, &action, 0); feenableexcept(FE_OVERFLOW | FE_UNDERFLOW); double x = DBL_MIN; return printf("%lf\n", argc > 1 ? 1.7976931348623158E308*1.7976931348623158E308 : x / 10); } Signed-off-by: Helge Deller --- target/hppa/fpu_helper.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/target/hppa/fpu_helper.c b/target/hppa/fpu_helper.c index a62d9d3083..294ce0a970 100644 --- a/target/hppa/fpu_helper.c +++ b/target/hppa/fpu_helper.c @@ -95,7 +95,8 @@ static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) { uint32_t soft_exp = get_float_exception_flags(&env->fp_status); uint32_t hard_exp = 0; - uint32_t shadow = env->fr0_shadow; + uint32_t shadow = env->fr0_shadow & 0x3ffffff; + uint32_t fr1 = 0; if (likely(soft_exp == 0)) { env->fr[0] = (uint64_t)shadow << 32; @@ -108,9 +109,22 @@ static void update_fr0_op(CPUHPPAState *env, uintptr_t ra) hard_exp |= CONVERT_BIT(soft_exp, float_flag_overflow, R_FPSR_ENA_O_MASK); hard_exp |= CONVERT_BIT(soft_exp, float_flag_divbyzero, R_FPSR_ENA_Z_MASK); hard_exp |= CONVERT_BIT(soft_exp, float_flag_invalid, R_FPSR_ENA_V_MASK); - shadow |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT); + if (hard_exp & shadow) { + shadow = FIELD_DP32(shadow, FPSR, T, 1); + /* fill exception register #1, which is lower 32-bits of fr[0] */ +#if !defined(CONFIG_USER_ONLY) + if (hard_exp & (R_FPSR_ENA_O_MASK | R_FPSR_ENA_U_MASK)) { + /* over- and underflow both set overflow flag only */ + fr1 = FIELD_DP32(fr1, FPSR, C, 1); + fr1 = FIELD_DP32(fr1, FPSR, FLG_O, 1); + } else +#endif + { + fr1 |= hard_exp << (R_FPSR_FLAGS_SHIFT - R_FPSR_ENABLES_SHIFT); + } + } env->fr0_shadow = shadow; - env->fr[0] = (uint64_t)shadow << 32; + env->fr[0] = (uint64_t)shadow << 32 | fr1; if (hard_exp & shadow) { hppa_dynamic_excp(env, EXCP_ASSIST, ra); From d2a88aca766697e8490504313d23002c3315ee93 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Sat, 22 Mar 2025 10:01:37 +0530 Subject: [PATCH 0962/2760] hw/riscv/virt: Add the BDF of IOMMU to RISCVVirtState structure When the IOMMU is implemented as a PCI device, its BDF is created locally in virt.c. However, the same BDF is also required in virt-acpi-build.c to support ACPI. Therefore, make this information part of the global RISCVVirtState structure so that it can be accessed outside of virt.c as well. Signed-off-by: Sunil V L Reviewed-by: Daniel Henrique Barboza Message-ID: <20250322043139.2003479-2-sunilvl@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 1 + include/hw/riscv/virt.h | 1 + 2 files changed, 2 insertions(+) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index be1bf0f646..5958ad1f7d 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1117,6 +1117,7 @@ static void create_fdt_iommu(RISCVVirtState *s, uint16_t bdf) qemu_fdt_setprop_cells(fdt, pci_node, "iommu-map", 0, iommu_phandle, 0, bdf, bdf + 1, iommu_phandle, bdf + 1, 0xffff - bdf); + s->pci_iommu_bdf = bdf; } static void finalize_fdt(RISCVVirtState *s) diff --git a/include/hw/riscv/virt.h b/include/hw/riscv/virt.h index 48a14bea2e..7b4c2c8b7d 100644 --- a/include/hw/riscv/virt.h +++ b/include/hw/riscv/virt.h @@ -63,6 +63,7 @@ struct RISCVVirtState { const MemMapEntry *memmap; struct GPEXHost *gpex_host; OnOffAuto iommu_sys; + uint16_t pci_iommu_bdf; }; enum { From cd18dbbf9d23f309f3e46c38b99213dbe3d48d17 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Sat, 22 Mar 2025 10:01:38 +0530 Subject: [PATCH 0963/2760] hw/riscv/virt-acpi-build: Add support for RIMT RISC-V IO Mapping Table (RIMT) is a new static ACPI table used to communicate IOMMU information to the OS. Add support for creating this table when the IOMMU is present. The specification is frozen and available at [1]. [1] - https://github.com/riscv-non-isa/riscv-acpi-rimt/releases/download/v0.99/rimt-spec.pdf Signed-off-by: Sunil V L Reviewed-by: Daniel Henrique Barboza Message-ID: <20250322043139.2003479-3-sunilvl@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt-acpi-build.c | 215 +++++++++++++++++++++++++++++++++++++ 1 file changed, 215 insertions(+) diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index 1ad6800508..1eef2fb4eb 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -198,6 +198,32 @@ acpi_dsdt_add_uart(Aml *scope, const MemMapEntry *uart_memmap, aml_append(scope, dev); } +/* + * Add DSDT entry for the IOMMU platform device. + * ACPI ID for IOMMU is defined in the section 6.2 of RISC-V BRS spec. + * https://github.com/riscv-non-isa/riscv-brs/releases/download/v0.8/riscv-brs-spec.pdf + */ +static void acpi_dsdt_add_iommu_sys(Aml *scope, const MemMapEntry *iommu_memmap, + uint32_t iommu_irq) +{ + uint32_t i; + + Aml *dev = aml_device("IMU0"); + aml_append(dev, aml_name_decl("_HID", aml_string("RSCV0004"))); + aml_append(dev, aml_name_decl("_UID", aml_int(0))); + + Aml *crs = aml_resource_template(); + aml_append(crs, aml_memory32_fixed(iommu_memmap->base, + iommu_memmap->size, AML_READ_WRITE)); + for (i = iommu_irq; i < iommu_irq + 4; i++) { + aml_append(crs, aml_interrupt(AML_CONSUMER, AML_EDGE, AML_ACTIVE_LOW, + AML_EXCLUSIVE, &i, 1)); + } + + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); +} + /* * Serial Port Console Redirection Table (SPCR) * Rev: 1.10 @@ -450,6 +476,9 @@ static void build_dsdt(GArray *table_data, } acpi_dsdt_add_uart(scope, &memmap[VIRT_UART0], UART0_IRQ); + if (virt_is_iommu_sys_enabled(s)) { + acpi_dsdt_add_iommu_sys(scope, &memmap[VIRT_IOMMU_SYS], IOMMU_SYS_IRQ); + } if (socket_count == 1) { virtio_acpi_dsdt_add(scope, memmap[VIRT_VIRTIO].base, @@ -602,6 +631,187 @@ static void build_madt(GArray *table_data, acpi_table_end(linker, &table); } +#define ID_MAPPING_ENTRY_SIZE 20 +#define IOMMU_ENTRY_SIZE 40 +#define RISCV_INTERRUPT_WIRE_OFFSSET 40 +#define ROOT_COMPLEX_ENTRY_SIZE 20 +#define RIMT_NODE_OFFSET 48 + +/* + * ID Mapping Structure + */ +static void build_rimt_id_mapping(GArray *table_data, uint32_t source_id_base, + uint32_t num_ids, uint32_t dest_id_base) +{ + /* Source ID Base */ + build_append_int_noprefix(table_data, source_id_base, 4); + /* Number of IDs */ + build_append_int_noprefix(table_data, num_ids, 4); + /* Destination Device ID Base */ + build_append_int_noprefix(table_data, source_id_base, 4); + /* Destination IOMMU Offset */ + build_append_int_noprefix(table_data, dest_id_base, 4); + /* Flags */ + build_append_int_noprefix(table_data, 0, 4); +} + +struct AcpiRimtIdMapping { + uint32_t source_id_base; + uint32_t num_ids; +}; +typedef struct AcpiRimtIdMapping AcpiRimtIdMapping; + +/* Build the rimt ID mapping to IOMMU for a given PCI host bridge */ +static int rimt_host_bridges(Object *obj, void *opaque) +{ + GArray *idmap_blob = opaque; + + if (object_dynamic_cast(obj, TYPE_PCI_HOST_BRIDGE)) { + PCIBus *bus = PCI_HOST_BRIDGE(obj)->bus; + + if (bus && !pci_bus_bypass_iommu(bus)) { + int min_bus, max_bus; + + pci_bus_range(bus, &min_bus, &max_bus); + + AcpiRimtIdMapping idmap = { + .source_id_base = min_bus << 8, + .num_ids = (max_bus - min_bus + 1) << 8, + }; + g_array_append_val(idmap_blob, idmap); + } + } + + return 0; +} + +static int rimt_idmap_compare(gconstpointer a, gconstpointer b) +{ + AcpiRimtIdMapping *idmap_a = (AcpiRimtIdMapping *)a; + AcpiRimtIdMapping *idmap_b = (AcpiRimtIdMapping *)b; + + return idmap_a->source_id_base - idmap_b->source_id_base; +} + +/* + * RISC-V IO Mapping Table (RIMT) + * https://github.com/riscv-non-isa/riscv-acpi-rimt/releases/download/v0.99/rimt-spec.pdf + */ +static void build_rimt(GArray *table_data, BIOSLinker *linker, + RISCVVirtState *s) +{ + int i, nb_nodes, rc_mapping_count; + size_t node_size, iommu_offset = 0; + uint32_t id = 0; + g_autoptr(GArray) iommu_idmaps = g_array_new(false, true, + sizeof(AcpiRimtIdMapping)); + + AcpiTable table = { .sig = "RIMT", .rev = 1, .oem_id = s->oem_id, + .oem_table_id = s->oem_table_id }; + + acpi_table_begin(&table, table_data); + + object_child_foreach_recursive(object_get_root(), + rimt_host_bridges, iommu_idmaps); + + /* Sort the ID mapping by Source ID Base*/ + g_array_sort(iommu_idmaps, rimt_idmap_compare); + + nb_nodes = 2; /* RC, IOMMU */ + rc_mapping_count = iommu_idmaps->len; + /* Number of RIMT Nodes */ + build_append_int_noprefix(table_data, nb_nodes, 4); + + /* Offset to Array of RIMT Nodes */ + build_append_int_noprefix(table_data, RIMT_NODE_OFFSET, 4); + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + + iommu_offset = table_data->len - table.table_offset; + /* IOMMU Device Structure */ + build_append_int_noprefix(table_data, 0, 1); /* Type - IOMMU*/ + build_append_int_noprefix(table_data, 1, 1); /* Revision */ + node_size = IOMMU_ENTRY_SIZE; + build_append_int_noprefix(table_data, node_size, 2); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, id++, 2); /* ID */ + if (virt_is_iommu_sys_enabled(s)) { + /* Hardware ID */ + build_append_int_noprefix(table_data, 'R', 1); + build_append_int_noprefix(table_data, 'S', 1); + build_append_int_noprefix(table_data, 'C', 1); + build_append_int_noprefix(table_data, 'V', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '4', 1); + /* Base Address */ + build_append_int_noprefix(table_data, + s->memmap[VIRT_IOMMU_SYS].base, 8); + build_append_int_noprefix(table_data, 0, 4); /* Flags */ + } else { + /* Hardware ID */ + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '1', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '0', 1); + build_append_int_noprefix(table_data, '1', 1); + build_append_int_noprefix(table_data, '4', 1); + + build_append_int_noprefix(table_data, 0, 8); /* Base Address */ + build_append_int_noprefix(table_data, 1, 4); /* Flags */ + } + + build_append_int_noprefix(table_data, 0, 4); /* Proximity Domain */ + build_append_int_noprefix(table_data, 0, 2); /* PCI Segment number */ + /* PCIe B/D/F */ + if (virt_is_iommu_sys_enabled(s)) { + build_append_int_noprefix(table_data, 0, 2); + } else { + build_append_int_noprefix(table_data, s->pci_iommu_bdf, 2); + } + /* Number of interrupt wires */ + build_append_int_noprefix(table_data, 0, 2); + /* Interrupt wire array offset */ + build_append_int_noprefix(table_data, RISCV_INTERRUPT_WIRE_OFFSSET, 2); + + /* PCIe Root Complex Node */ + build_append_int_noprefix(table_data, 1, 1); /* Type */ + build_append_int_noprefix(table_data, 1, 1); /* Revision */ + node_size = ROOT_COMPLEX_ENTRY_SIZE + + ID_MAPPING_ENTRY_SIZE * rc_mapping_count; + build_append_int_noprefix(table_data, node_size, 2); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, id++, 2); /* ID */ + build_append_int_noprefix(table_data, 0, 4); /* Flags */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + /* PCI Segment number */ + build_append_int_noprefix(table_data, 0, 2); + /* ID mapping array offset */ + build_append_int_noprefix(table_data, ROOT_COMPLEX_ENTRY_SIZE, 2); + /* Number of ID mappings */ + build_append_int_noprefix(table_data, rc_mapping_count, 2); + + /* Output Reference */ + AcpiRimtIdMapping *range; + + /* ID mapping array */ + for (i = 0; i < iommu_idmaps->len; i++) { + range = &g_array_index(iommu_idmaps, AcpiRimtIdMapping, i); + if (virt_is_iommu_sys_enabled(s)) { + range->source_id_base = 0; + } else { + range->source_id_base = s->pci_iommu_bdf + 1; + } + range->num_ids = 0xffff - s->pci_iommu_bdf; + build_rimt_id_mapping(table_data, range->source_id_base, + range->num_ids, iommu_offset); + } + + acpi_table_end(linker, &table); +} + /* * ACPI spec, Revision 6.5+ * 5.2.16 System Resource Affinity Table (SRAT) @@ -679,6 +889,11 @@ static void virt_acpi_build(RISCVVirtState *s, AcpiBuildTables *tables) acpi_add_table(table_offsets, tables_blob); build_rhct(tables_blob, tables->linker, s); + if (virt_is_iommu_sys_enabled(s) || s->pci_iommu_bdf) { + acpi_add_table(table_offsets, tables_blob); + build_rimt(tables_blob, tables->linker, s); + } + acpi_add_table(table_offsets, tables_blob); spcr_setup(tables_blob, tables->linker, s); From 4541d205f03cf1529439f68d2ec5056685189399 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Thu, 13 Mar 2025 20:30:07 +0100 Subject: [PATCH 0964/2760] target/riscv: pmp: don't allow RLB to bypass rule privileges MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Smepmp is supported, mseccfg.RLB allows bypassing locks when writing CSRs but should not affect interpretation of actual PMP rules. This is not the case with the current implementation where pmp_hart_has_privs calls pmp_is_locked which implements mseccfg.RLB bypass. This commit implements the correct behavior by removing mseccfg.RLB bypass from pmp_is_locked. RLB bypass when writing CSRs is implemented by adding a new pmp_is_readonly function that calls pmp_is_locked and check mseccfg.RLB. pmp_write_cfg and pmpaddr_csr_write are changed to use this new function. Signed-off-by: Loïc Lefort Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei  Message-ID: <20250313193011.720075-2-loic@rivosinc.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/pmp.c | 43 +++++++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index c13a117e3f..d8e2949aaf 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -46,11 +46,6 @@ static inline uint8_t pmp_get_a_field(uint8_t cfg) */ static inline int pmp_is_locked(CPURISCVState *env, uint32_t pmp_index) { - /* mseccfg.RLB is set */ - if (MSECCFG_RLB_ISSET(env)) { - return 0; - } - if (env->pmp_state.pmp[pmp_index].cfg_reg & PMP_LOCK) { return 1; } @@ -63,6 +58,15 @@ static inline int pmp_is_locked(CPURISCVState *env, uint32_t pmp_index) return 0; } +/* + * Check whether a PMP is locked for writing or not. + * (i.e. has LOCK flag and mseccfg.RLB is unset) + */ +static int pmp_is_readonly(CPURISCVState *env, uint32_t pmp_index) +{ + return pmp_is_locked(env, pmp_index) && !MSECCFG_RLB_ISSET(env); +} + /* * Count the number of active rules. */ @@ -91,39 +95,38 @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) { if (pmp_index < MAX_RISCV_PMPS) { - bool locked = true; + bool readonly = true; if (riscv_cpu_cfg(env)->ext_smepmp) { /* mseccfg.RLB is set */ if (MSECCFG_RLB_ISSET(env)) { - locked = false; + readonly = false; } /* mseccfg.MML is not set */ - if (!MSECCFG_MML_ISSET(env) && !pmp_is_locked(env, pmp_index)) { - locked = false; + if (!MSECCFG_MML_ISSET(env) && !pmp_is_readonly(env, pmp_index)) { + readonly = false; } /* mseccfg.MML is set */ if (MSECCFG_MML_ISSET(env)) { /* not adding execute bit */ if ((val & PMP_LOCK) != 0 && (val & PMP_EXEC) != PMP_EXEC) { - locked = false; + readonly = false; } /* shared region and not adding X bit */ if ((val & PMP_LOCK) != PMP_LOCK && (val & 0x7) != (PMP_WRITE | PMP_EXEC)) { - locked = false; + readonly = false; } } } else { - if (!pmp_is_locked(env, pmp_index)) { - locked = false; - } + readonly = pmp_is_readonly(env, pmp_index); } - if (locked) { - qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - locked\n"); + if (readonly) { + qemu_log_mask(LOG_GUEST_ERROR, + "ignoring pmpcfg write - read only\n"); } else if (env->pmp_state.pmp[pmp_index].cfg_reg != val) { /* If !mseccfg.MML then ignore writes with encoding RW=01 */ if ((val & PMP_WRITE) && !(val & PMP_READ) && @@ -525,14 +528,14 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, uint8_t pmp_cfg = env->pmp_state.pmp[addr_index + 1].cfg_reg; is_next_cfg_tor = PMP_AMATCH_TOR == pmp_get_a_field(pmp_cfg); - if (pmp_is_locked(env, addr_index + 1) && is_next_cfg_tor) { + if (pmp_is_readonly(env, addr_index + 1) && is_next_cfg_tor) { qemu_log_mask(LOG_GUEST_ERROR, - "ignoring pmpaddr write - pmpcfg + 1 locked\n"); + "ignoring pmpaddr write - pmpcfg+1 read only\n"); return; } } - if (!pmp_is_locked(env, addr_index)) { + if (!pmp_is_readonly(env, addr_index)) { if (env->pmp_state.pmp[addr_index].addr_reg != val) { env->pmp_state.pmp[addr_index].addr_reg = val; pmp_update_rule_addr(env, addr_index); @@ -543,7 +546,7 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, } } else { qemu_log_mask(LOG_GUEST_ERROR, - "ignoring pmpaddr write - locked\n"); + "ignoring pmpaddr write - read only\n"); } } else { qemu_log_mask(LOG_GUEST_ERROR, From 915b203745540e908943758f78f5da49e0a15e45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Thu, 13 Mar 2025 20:30:08 +0100 Subject: [PATCH 0965/2760] target/riscv: pmp: move Smepmp operation conversion into a function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Loïc Lefort Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: LIU Zhiwei Message-ID: <20250313193011.720075-3-loic@rivosinc.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/pmp.c | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index d8e2949aaf..3afa16917e 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -32,6 +32,15 @@ static bool pmp_write_cfg(CPURISCVState *env, uint32_t addr_index, uint8_t val); static uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t addr_index); +/* + * Convert the PMP permissions to match the truth table in the Smepmp spec. + */ +static inline uint8_t pmp_get_smepmp_operation(uint8_t cfg) +{ + return ((cfg & PMP_LOCK) >> 4) | ((cfg & PMP_READ) << 2) | + (cfg & PMP_WRITE) | ((cfg & PMP_EXEC) >> 2); +} + /* * Accessor method to extract address matching type 'a field' from cfg reg */ @@ -356,16 +365,6 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr, const uint8_t a_field = pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); - /* - * Convert the PMP permissions to match the truth table in the - * Smepmp spec. - */ - const uint8_t smepmp_operation = - ((env->pmp_state.pmp[i].cfg_reg & PMP_LOCK) >> 4) | - ((env->pmp_state.pmp[i].cfg_reg & PMP_READ) << 2) | - (env->pmp_state.pmp[i].cfg_reg & PMP_WRITE) | - ((env->pmp_state.pmp[i].cfg_reg & PMP_EXEC) >> 2); - if (((s + e) == 2) && (PMP_AMATCH_OFF != a_field)) { /* * If the PMP entry is not off and the address is in range, @@ -384,6 +383,9 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr, /* * If mseccfg.MML Bit set, do the enhanced pmp priv check */ + const uint8_t smepmp_operation = + pmp_get_smepmp_operation(env->pmp_state.pmp[i].cfg_reg); + if (mode == PRV_M) { switch (smepmp_operation) { case 0: From 19cf1a7d9e59b71bf8d6571d4747e5c82667c3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Thu, 13 Mar 2025 20:30:09 +0100 Subject: [PATCH 0966/2760] target/riscv: pmp: fix checks on writes to pmpcfg in Smepmp MML mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With Machine Mode Lockdown (mseccfg.MML) set and RLB not set, checks on pmpcfg writes would match the wrong cases of Smepmp truth table. The existing code allows writes for the following cases: - L=1, X=0: cases 8, 10, 12, 14 - L=0, RWX!=WX: cases 0-2, 4-6 This leaves cases 3, 7, 9, 11, 13, 15 for which writes are ignored. From the Smepmp specification: "Adding a rule with executable privileges that either is M-mode-only or a locked Shared-Region is not possible (...)" This description matches cases 9-11, 13 of the truth table. This commit implements an explicit check for these cases by using pmp_get_epmp_operation to convert between PMP configuration and Smepmp truth table cases. Signed-off-by: Loïc Lefort Reviewed-by: Daniel Henrique Barboza Reviewed-by: LIU Zhiwei Message-ID: <20250313193011.720075-4-loic@rivosinc.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/pmp.c | 79 +++++++++++++++++++++++++--------------------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 3afa16917e..8fc313990a 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -76,6 +76,44 @@ static int pmp_is_readonly(CPURISCVState *env, uint32_t pmp_index) return pmp_is_locked(env, pmp_index) && !MSECCFG_RLB_ISSET(env); } +/* + * Check whether `val` is an invalid Smepmp config value + */ +static int pmp_is_invalid_smepmp_cfg(CPURISCVState *env, uint8_t val) +{ + /* No check if mseccfg.MML is not set or if mseccfg.RLB is set */ + if (!MSECCFG_MML_ISSET(env) || MSECCFG_RLB_ISSET(env)) { + return 0; + } + + /* + * Adding a rule with executable privileges that either is M-mode-only + * or a locked Shared-Region is not possible + */ + switch (pmp_get_smepmp_operation(val)) { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 12: + case 14: + case 15: + return 0; + case 9: + case 10: + case 11: + case 13: + return 1; + default: + g_assert_not_reached(); + } +} + /* * Count the number of active rules. */ @@ -104,44 +142,13 @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) { if (pmp_index < MAX_RISCV_PMPS) { - bool readonly = true; - - if (riscv_cpu_cfg(env)->ext_smepmp) { - /* mseccfg.RLB is set */ - if (MSECCFG_RLB_ISSET(env)) { - readonly = false; - } - - /* mseccfg.MML is not set */ - if (!MSECCFG_MML_ISSET(env) && !pmp_is_readonly(env, pmp_index)) { - readonly = false; - } - - /* mseccfg.MML is set */ - if (MSECCFG_MML_ISSET(env)) { - /* not adding execute bit */ - if ((val & PMP_LOCK) != 0 && (val & PMP_EXEC) != PMP_EXEC) { - readonly = false; - } - /* shared region and not adding X bit */ - if ((val & PMP_LOCK) != PMP_LOCK && - (val & 0x7) != (PMP_WRITE | PMP_EXEC)) { - readonly = false; - } - } - } else { - readonly = pmp_is_readonly(env, pmp_index); - } - - if (readonly) { + if (pmp_is_readonly(env, pmp_index)) { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - read only\n"); - } else if (env->pmp_state.pmp[pmp_index].cfg_reg != val) { - /* If !mseccfg.MML then ignore writes with encoding RW=01 */ - if ((val & PMP_WRITE) && !(val & PMP_READ) && - !MSECCFG_MML_ISSET(env)) { - return false; - } + } else if (pmp_is_invalid_smepmp_cfg(env, val)) { + qemu_log_mask(LOG_GUEST_ERROR, + "ignoring pmpcfg write - invalid\n"); + } else { env->pmp_state.pmp[pmp_index].cfg_reg = val; pmp_update_rule_addr(env, pmp_index); return true; From 0c60c531f53544117f96d508fd9918f78ecf0df5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Thu, 13 Mar 2025 20:30:10 +0100 Subject: [PATCH 0967/2760] target/riscv: pmp: exit csr writes early if value was not changed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Loïc Lefort Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: LIU Zhiwei Message-ID: <20250313193011.720075-5-loic@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 8fc313990a..4070e21ea3 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -142,6 +142,11 @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) { if (pmp_index < MAX_RISCV_PMPS) { + if (env->pmp_state.pmp[pmp_index].cfg_reg == val) { + /* no change */ + return false; + } + if (pmp_is_readonly(env, pmp_index)) { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpcfg write - read only\n"); @@ -529,6 +534,11 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, bool is_next_cfg_tor = false; if (addr_index < MAX_RISCV_PMPS) { + if (env->pmp_state.pmp[addr_index].addr_reg == val) { + /* no change */ + return; + } + /* * In TOR mode, need to check the lock bit of the next pmp * (if there is a next). @@ -545,14 +555,12 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, } if (!pmp_is_readonly(env, addr_index)) { - if (env->pmp_state.pmp[addr_index].addr_reg != val) { - env->pmp_state.pmp[addr_index].addr_reg = val; - pmp_update_rule_addr(env, addr_index); - if (is_next_cfg_tor) { - pmp_update_rule_addr(env, addr_index + 1); - } - tlb_flush(env_cpu(env)); + env->pmp_state.pmp[addr_index].addr_reg = val; + pmp_update_rule_addr(env, addr_index); + if (is_next_cfg_tor) { + pmp_update_rule_addr(env, addr_index + 1); } + tlb_flush(env_cpu(env)); } else { qemu_log_mask(LOG_GUEST_ERROR, "ignoring pmpaddr write - read only\n"); From 986222f0f994671190a94836a373fd64992cd271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Lefort?= Date: Thu, 13 Mar 2025 20:30:11 +0100 Subject: [PATCH 0968/2760] target/riscv: pmp: remove redundant check in pmp_is_locked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove useless check in pmp_is_locked, the function will return 0 in either case. Signed-off-by: Loïc Lefort Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: LIU Zhiwei Message-ID: <20250313193011.720075-6-loic@rivosinc.com> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 4070e21ea3..5af295e410 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -59,11 +59,6 @@ static inline int pmp_is_locked(CPURISCVState *env, uint32_t pmp_index) return 1; } - /* Top PMP has no 'next' to check */ - if ((pmp_index + 1u) >= MAX_RISCV_PMPS) { - return 0; - } - return 0; } From 28c12c1f2f50d7f7f1ebfc587c4777ecd50aac5b Mon Sep 17 00:00:00 2001 From: Paolo Savini Date: Wed, 12 Mar 2025 15:55:47 +0000 Subject: [PATCH 0969/2760] Generate strided vector loads/stores with tcg nodes. This commit improves the performance of QEMU when emulating strided vector loads and stores by substituting the call for the helper function with the generation of equivalent TCG operations. Signed-off-by: Paolo Savini Reviewed-by: Daniel Henrique Barboza Message-ID: <20250312155547.289642-2-paolo.savini@embecosm.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 323 ++++++++++++++++++++---- 1 file changed, 273 insertions(+), 50 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index b9883a5d32..7079f758ad 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -802,32 +802,286 @@ GEN_VEXT_TRANS(vlm_v, MO_8, vlm_v, ld_us_mask_op, ld_us_mask_check) GEN_VEXT_TRANS(vsm_v, MO_8, vsm_v, st_us_mask_op, st_us_mask_check) /* - *** stride load and store + * MAXSZ returns the maximum vector size can be operated in bytes, + * which is used in GVEC IR when vl_eq_vlmax flag is set to true + * to accelerate vector operation. + */ +static inline uint32_t MAXSZ(DisasContext *s) +{ + int max_sz = s->cfg_ptr->vlenb << 3; + return max_sz >> (3 - s->lmul); +} + +static inline uint32_t get_log2(uint32_t a) +{ + uint32_t i = 0; + for (; a > 0;) { + a >>= 1; + i++; + } + return i; +} + +typedef void gen_tl_ldst(TCGv, TCGv_ptr, tcg_target_long); + +/* + * Simulate the strided load/store main loop: + * + * for (i = env->vstart; i < env->vl; env->vstart = ++i) { + * k = 0; + * while (k < nf) { + * if (!vm && !vext_elem_mask(v0, i)) { + * vext_set_elems_1s(vd, vma, (i + k * max_elems) * esz, + * (i + k * max_elems + 1) * esz); + * k++; + * continue; + * } + * target_ulong addr = base + stride * i + (k << log2_esz); + * ldst(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); + * k++; + * } + * } + */ +static void gen_ldst_stride_main_loop(DisasContext *s, TCGv dest, uint32_t rs1, + uint32_t rs2, uint32_t vm, uint32_t nf, + gen_tl_ldst *ld_fn, gen_tl_ldst *st_fn, + bool is_load) +{ + TCGv addr = tcg_temp_new(); + TCGv base = get_gpr(s, rs1, EXT_NONE); + TCGv stride = get_gpr(s, rs2, EXT_NONE); + + TCGv i = tcg_temp_new(); + TCGv i_esz = tcg_temp_new(); + TCGv k = tcg_temp_new(); + TCGv k_esz = tcg_temp_new(); + TCGv k_max = tcg_temp_new(); + TCGv mask = tcg_temp_new(); + TCGv mask_offs = tcg_temp_new(); + TCGv mask_offs_64 = tcg_temp_new(); + TCGv mask_elem = tcg_temp_new(); + TCGv mask_offs_rem = tcg_temp_new(); + TCGv vreg = tcg_temp_new(); + TCGv dest_offs = tcg_temp_new(); + TCGv stride_offs = tcg_temp_new(); + + uint32_t max_elems = MAXSZ(s) >> s->sew; + + TCGLabel *start = gen_new_label(); + TCGLabel *end = gen_new_label(); + TCGLabel *start_k = gen_new_label(); + TCGLabel *inc_k = gen_new_label(); + TCGLabel *end_k = gen_new_label(); + + MemOp atomicity = MO_ATOM_NONE; + if (s->sew == 0) { + atomicity = MO_ATOM_NONE; + } else { + atomicity = MO_ATOM_IFALIGN_PAIR; + } + + mark_vs_dirty(s); + + tcg_gen_addi_tl(mask, (TCGv)tcg_env, vreg_ofs(s, 0)); + + /* Start of outer loop. */ + tcg_gen_mov_tl(i, cpu_vstart); + gen_set_label(start); + tcg_gen_brcond_tl(TCG_COND_GE, i, cpu_vl, end); + tcg_gen_shli_tl(i_esz, i, s->sew); + /* Start of inner loop. */ + tcg_gen_movi_tl(k, 0); + gen_set_label(start_k); + tcg_gen_brcond_tl(TCG_COND_GE, k, tcg_constant_tl(nf), end_k); + /* + * If we are in mask agnostic regime and the operation is not unmasked we + * set the inactive elements to 1. + */ + if (!vm && s->vma) { + TCGLabel *active_element = gen_new_label(); + /* (i + k * max_elems) * esz */ + tcg_gen_shli_tl(mask_offs, k, get_log2(max_elems << s->sew)); + tcg_gen_add_tl(mask_offs, mask_offs, i_esz); + + /* + * Check whether the i bit of the mask is 0 or 1. + * + * static inline int vext_elem_mask(void *v0, int index) + * { + * int idx = index / 64; + * int pos = index % 64; + * return (((uint64_t *)v0)[idx] >> pos) & 1; + * } + */ + tcg_gen_shri_tl(mask_offs_64, mask_offs, 3); + tcg_gen_add_tl(mask_offs_64, mask_offs_64, mask); + tcg_gen_ld_i64((TCGv_i64)mask_elem, (TCGv_ptr)mask_offs_64, 0); + tcg_gen_rem_tl(mask_offs_rem, mask_offs, tcg_constant_tl(8)); + tcg_gen_shr_tl(mask_elem, mask_elem, mask_offs_rem); + tcg_gen_andi_tl(mask_elem, mask_elem, 1); + tcg_gen_brcond_tl(TCG_COND_NE, mask_elem, tcg_constant_tl(0), + active_element); + /* + * Set masked-off elements in the destination vector register to 1s. + * Store instructions simply skip this bit as memory ops access memory + * only for active elements. + */ + if (is_load) { + tcg_gen_shli_tl(mask_offs, mask_offs, s->sew); + tcg_gen_add_tl(mask_offs, mask_offs, dest); + st_fn(tcg_constant_tl(-1), (TCGv_ptr)mask_offs, 0); + } + tcg_gen_br(inc_k); + gen_set_label(active_element); + } + /* + * The element is active, calculate the address with stride: + * target_ulong addr = base + stride * i + (k << log2_esz); + */ + tcg_gen_mul_tl(stride_offs, stride, i); + tcg_gen_shli_tl(k_esz, k, s->sew); + tcg_gen_add_tl(stride_offs, stride_offs, k_esz); + tcg_gen_add_tl(addr, base, stride_offs); + /* Calculate the offset in the dst/src vector register. */ + tcg_gen_shli_tl(k_max, k, get_log2(max_elems)); + tcg_gen_add_tl(dest_offs, i, k_max); + tcg_gen_shli_tl(dest_offs, dest_offs, s->sew); + tcg_gen_add_tl(dest_offs, dest_offs, dest); + if (is_load) { + tcg_gen_qemu_ld_tl(vreg, addr, s->mem_idx, MO_LE | s->sew | atomicity); + st_fn((TCGv)vreg, (TCGv_ptr)dest_offs, 0); + } else { + ld_fn((TCGv)vreg, (TCGv_ptr)dest_offs, 0); + tcg_gen_qemu_st_tl(vreg, addr, s->mem_idx, MO_LE | s->sew | atomicity); + } + /* + * We don't execute the load/store above if the element was inactive. + * We jump instead directly to incrementing k and continuing the loop. + */ + if (!vm && s->vma) { + gen_set_label(inc_k); + } + tcg_gen_addi_tl(k, k, 1); + tcg_gen_br(start_k); + /* End of the inner loop. */ + gen_set_label(end_k); + + tcg_gen_addi_tl(i, i, 1); + tcg_gen_mov_tl(cpu_vstart, i); + tcg_gen_br(start); + + /* End of the outer loop. */ + gen_set_label(end); + + return; +} + + +/* + * Set the tail bytes of the strided loads/stores to 1: + * + * for (k = 0; k < nf; ++k) { + * cnt = (k * max_elems + vl) * esz; + * tot = (k * max_elems + max_elems) * esz; + * for (i = cnt; i < tot; i += esz) { + * store_1s(-1, vd[vl+i]); + * } + * } */ -typedef void gen_helper_ldst_stride(TCGv_ptr, TCGv_ptr, TCGv, - TCGv, TCGv_env, TCGv_i32); +static void gen_ldst_stride_tail_loop(DisasContext *s, TCGv dest, uint32_t nf, + gen_tl_ldst *st_fn) +{ + TCGv i = tcg_temp_new(); + TCGv k = tcg_temp_new(); + TCGv tail_cnt = tcg_temp_new(); + TCGv tail_tot = tcg_temp_new(); + TCGv tail_addr = tcg_temp_new(); + + TCGLabel *start = gen_new_label(); + TCGLabel *end = gen_new_label(); + TCGLabel *start_i = gen_new_label(); + TCGLabel *end_i = gen_new_label(); + + uint32_t max_elems_b = MAXSZ(s); + uint32_t esz = 1 << s->sew; + + /* Start of the outer loop. */ + tcg_gen_movi_tl(k, 0); + tcg_gen_shli_tl(tail_cnt, cpu_vl, s->sew); + tcg_gen_movi_tl(tail_tot, max_elems_b); + tcg_gen_add_tl(tail_addr, dest, tail_cnt); + gen_set_label(start); + tcg_gen_brcond_tl(TCG_COND_GE, k, tcg_constant_tl(nf), end); + /* Start of the inner loop. */ + tcg_gen_mov_tl(i, tail_cnt); + gen_set_label(start_i); + tcg_gen_brcond_tl(TCG_COND_GE, i, tail_tot, end_i); + /* store_1s(-1, vd[vl+i]); */ + st_fn(tcg_constant_tl(-1), (TCGv_ptr)tail_addr, 0); + tcg_gen_addi_tl(tail_addr, tail_addr, esz); + tcg_gen_addi_tl(i, i, esz); + tcg_gen_br(start_i); + /* End of the inner loop. */ + gen_set_label(end_i); + /* Update the counts */ + tcg_gen_addi_tl(tail_cnt, tail_cnt, max_elems_b); + tcg_gen_addi_tl(tail_tot, tail_cnt, max_elems_b); + tcg_gen_addi_tl(k, k, 1); + tcg_gen_br(start); + /* End of the outer loop. */ + gen_set_label(end); + + return; +} static bool ldst_stride_trans(uint32_t vd, uint32_t rs1, uint32_t rs2, - uint32_t data, gen_helper_ldst_stride *fn, - DisasContext *s) + uint32_t data, DisasContext *s, bool is_load) { - TCGv_ptr dest, mask; - TCGv base, stride; - TCGv_i32 desc; + if (!s->vstart_eq_zero) { + return false; + } - dest = tcg_temp_new_ptr(); - mask = tcg_temp_new_ptr(); - base = get_gpr(s, rs1, EXT_NONE); - stride = get_gpr(s, rs2, EXT_NONE); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, - s->cfg_ptr->vlenb, data)); + TCGv dest = tcg_temp_new(); - tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); - tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); + uint32_t nf = FIELD_EX32(data, VDATA, NF); + uint32_t vm = FIELD_EX32(data, VDATA, VM); + + /* Destination register and mask register */ + tcg_gen_addi_tl(dest, (TCGv)tcg_env, vreg_ofs(s, vd)); + + /* + * Select the appropriate load/tore to retrieve data from the vector + * register given a specific sew. + */ + static gen_tl_ldst * const ld_fns[4] = { + tcg_gen_ld8u_tl, tcg_gen_ld16u_tl, + tcg_gen_ld32u_tl, tcg_gen_ld_tl + }; + + static gen_tl_ldst * const st_fns[4] = { + tcg_gen_st8_tl, tcg_gen_st16_tl, + tcg_gen_st32_tl, tcg_gen_st_tl + }; + + gen_tl_ldst *ld_fn = ld_fns[s->sew]; + gen_tl_ldst *st_fn = st_fns[s->sew]; + + if (ld_fn == NULL || st_fn == NULL) { + return false; + } mark_vs_dirty(s); - fn(dest, mask, base, stride, tcg_env, desc); + gen_ldst_stride_main_loop(s, dest, rs1, rs2, vm, nf, ld_fn, st_fn, is_load); + + tcg_gen_movi_tl(cpu_vstart, 0); + + /* + * Set the tail bytes to 1 if tail agnostic: + */ + if (s->vta != 0 && is_load) { + gen_ldst_stride_tail_loop(s, dest, nf, st_fn); + } finalize_rvv_inst(s); return true; @@ -836,16 +1090,6 @@ static bool ldst_stride_trans(uint32_t vd, uint32_t rs1, uint32_t rs2, static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) { uint32_t data = 0; - gen_helper_ldst_stride *fn; - static gen_helper_ldst_stride * const fns[4] = { - gen_helper_vlse8_v, gen_helper_vlse16_v, - gen_helper_vlse32_v, gen_helper_vlse64_v - }; - - fn = fns[eew]; - if (fn == NULL) { - return false; - } uint8_t emul = vext_get_emul(s, eew); data = FIELD_DP32(data, VDATA, VM, a->vm); @@ -853,7 +1097,7 @@ static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) data = FIELD_DP32(data, VDATA, NF, a->nf); data = FIELD_DP32(data, VDATA, VTA, s->vta); data = FIELD_DP32(data, VDATA, VMA, s->vma); - return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s); + return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, s, true); } static bool ld_stride_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) @@ -871,23 +1115,13 @@ GEN_VEXT_TRANS(vlse64_v, MO_64, rnfvm, ld_stride_op, ld_stride_check) static bool st_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) { uint32_t data = 0; - gen_helper_ldst_stride *fn; - static gen_helper_ldst_stride * const fns[4] = { - /* masked stride store */ - gen_helper_vsse8_v, gen_helper_vsse16_v, - gen_helper_vsse32_v, gen_helper_vsse64_v - }; uint8_t emul = vext_get_emul(s, eew); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); - fn = fns[eew]; - if (fn == NULL) { - return false; - } - return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s); + return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, s, false); } static bool st_stride_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) @@ -1169,17 +1403,6 @@ GEN_LDST_WHOLE_TRANS(vs8r_v, 8) *** Vector Integer Arithmetic Instructions */ -/* - * MAXSZ returns the maximum vector size can be operated in bytes, - * which is used in GVEC IR when vl_eq_vlmax flag is set to true - * to accelerate vector operation. - */ -static inline uint32_t MAXSZ(DisasContext *s) -{ - int max_sz = s->cfg_ptr->vlenb * 8; - return max_sz >> (3 - s->lmul); -} - static bool opivv_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && From 4c1a39eebc6a9f1db816ff6c23e3d42ba52e2601 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 19 Mar 2025 07:13:33 +0100 Subject: [PATCH 0970/2760] hw/misc: Add MPFS system reset support Signed-off-by: Sebastian Huber Acked-by: Alistair Francis Message-ID: <20250319061342.26435-2-sebastian.huber@embedded-brains.de> Signed-off-by: Alistair Francis --- hw/misc/mchp_pfsoc_sysreg.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/misc/mchp_pfsoc_sysreg.c b/hw/misc/mchp_pfsoc_sysreg.c index bfa78d3d2f..f47c835f80 100644 --- a/hw/misc/mchp_pfsoc_sysreg.c +++ b/hw/misc/mchp_pfsoc_sysreg.c @@ -27,7 +27,9 @@ #include "hw/irq.h" #include "hw/sysbus.h" #include "hw/misc/mchp_pfsoc_sysreg.h" +#include "system/runstate.h" +#define MSS_RESET_CR 0x18 #define ENVM_CR 0xb8 #define MESSAGE_INT 0x118c @@ -56,6 +58,11 @@ static void mchp_pfsoc_sysreg_write(void *opaque, hwaddr offset, { MchpPfSoCSysregState *s = opaque; switch (offset) { + case MSS_RESET_CR: + if (value == 0xdead) { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } + break; case MESSAGE_INT: qemu_irq_lower(s->irq); break; From cae44a92ab23811deeefa0a44b1bdec6cfa8e4b9 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 19 Mar 2025 07:13:34 +0100 Subject: [PATCH 0971/2760] hw/riscv: More flexible FDT placement for MPFS If the kernel entry is in the high DRAM area, place the FDT into this area. Signed-off-by: Sebastian Huber Reviewed-by: Alistair Francis Message-ID: <20250319061342.26435-3-sebastian.huber@embedded-brains.de> Signed-off-by: Alistair Francis --- hw/riscv/microchip_pfsoc.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index e39ee657cd..6bb44e3ac5 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -626,8 +626,15 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) kernel_entry = boot_info.image_low_addr; /* Compute the fdt load address in dram */ - fdt_load_addr = riscv_compute_fdt_addr(memmap[MICROCHIP_PFSOC_DRAM_LO].base, - memmap[MICROCHIP_PFSOC_DRAM_LO].size, + hwaddr kernel_ram_base = memmap[MICROCHIP_PFSOC_DRAM_LO].base; + hwaddr kernel_ram_size = memmap[MICROCHIP_PFSOC_DRAM_LO].size; + + if (kernel_entry - kernel_ram_base >= kernel_ram_size) { + kernel_ram_base = memmap[MICROCHIP_PFSOC_DRAM_HI].base; + kernel_ram_size = mem_high_size; + } + + fdt_load_addr = riscv_compute_fdt_addr(kernel_ram_base, kernel_ram_size, machine, &boot_info); riscv_load_fdt(fdt_load_addr, machine->fdt); From 0c2ca9e4d139acc762325d994614a42dba31be6a Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 19 Mar 2025 07:13:35 +0100 Subject: [PATCH 0972/2760] hw/riscv: Make FDT optional for MPFS Real-time kernels such as RTEMS or Zephyr may use a static device tree built into the kernel image. Do not require to use the -dtb option if -kernel is used for the microchip-icicle-kit machine. Issue a warning if no device tree is provided by the user since the machine does not generate one. Signed-off-by: Sebastian Huber Reviewed-by: Alistair Francis Message-ID: <20250319061342.26435-4-sebastian.huber@embedded-brains.de> Signed-off-by: Alistair Francis --- hw/riscv/microchip_pfsoc.c | 56 +++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 6bb44e3ac5..b8a8d5251d 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -516,7 +516,6 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) uint64_t mem_low_size, mem_high_size; hwaddr firmware_load_addr; const char *firmware_name; - bool kernel_as_payload = false; target_ulong firmware_end_addr, kernel_start_addr; uint64_t kernel_entry; uint64_t fdt_load_addr; @@ -589,25 +588,12 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) * * This ensures backwards compatibility with how we used to expose -bios * to users but allows them to run through direct kernel booting as well. - * - * When -kernel is used for direct boot, -dtb must be present to provide - * a valid device tree for the board, as we don't generate device tree. */ - if (machine->kernel_filename && machine->dtb) { - int fdt_size; - machine->fdt = load_device_tree(machine->dtb, &fdt_size); - if (!machine->fdt) { - error_report("load_device_tree() failed"); - exit(1); - } - + if (machine->kernel_filename) { firmware_name = RISCV64_BIOS_BIN; firmware_load_addr = memmap[MICROCHIP_PFSOC_DRAM_LO].base; - kernel_as_payload = true; - } - - if (!kernel_as_payload) { + } else { firmware_name = BIOS_FILENAME; firmware_load_addr = RESET_VECTOR; } @@ -617,7 +603,7 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) &firmware_load_addr, NULL); riscv_boot_info_init(&boot_info, &s->soc.u_cpus); - if (kernel_as_payload) { + if (machine->kernel_filename) { kernel_start_addr = riscv_calc_kernel_start_addr(&boot_info, firmware_end_addr); @@ -625,19 +611,33 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) true, NULL); kernel_entry = boot_info.image_low_addr; - /* Compute the fdt load address in dram */ - hwaddr kernel_ram_base = memmap[MICROCHIP_PFSOC_DRAM_LO].base; - hwaddr kernel_ram_size = memmap[MICROCHIP_PFSOC_DRAM_LO].size; - - if (kernel_entry - kernel_ram_base >= kernel_ram_size) { - kernel_ram_base = memmap[MICROCHIP_PFSOC_DRAM_HI].base; - kernel_ram_size = mem_high_size; + if (machine->dtb) { + int fdt_size; + machine->fdt = load_device_tree(machine->dtb, &fdt_size); + if (!machine->fdt) { + error_report("load_device_tree() failed"); + exit(1); + } + + /* Compute the FDT load address in DRAM */ + hwaddr kernel_ram_base = memmap[MICROCHIP_PFSOC_DRAM_LO].base; + hwaddr kernel_ram_size = memmap[MICROCHIP_PFSOC_DRAM_LO].size; + + if (kernel_entry - kernel_ram_base >= kernel_ram_size) { + kernel_ram_base = memmap[MICROCHIP_PFSOC_DRAM_HI].base; + kernel_ram_size = mem_high_size; + } + + fdt_load_addr = riscv_compute_fdt_addr(kernel_ram_base, kernel_ram_size, + machine, &boot_info); + riscv_load_fdt(fdt_load_addr, machine->fdt); + } else { + warn_report_once("The QEMU microchip-icicle-kit machine does not " + "generate a device tree, so no device tree is " + "being provided to the guest."); + fdt_load_addr = 0; } - fdt_load_addr = riscv_compute_fdt_addr(kernel_ram_base, kernel_ram_size, - machine, &boot_info); - riscv_load_fdt(fdt_load_addr, machine->fdt); - /* Load the reset vector */ riscv_setup_rom_reset_vec(machine, &s->soc.u_cpus, firmware_load_addr, memmap[MICROCHIP_PFSOC_ENVM_DATA].base, From 6dd6f11710c713bd21ac67ab93f6db33169c6b4d Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 19 Mar 2025 07:13:36 +0100 Subject: [PATCH 0973/2760] hw/riscv: Allow direct start of kernel for MPFS Further customize the -bios and -kernel options behaviour for the microchip-icicle-kit machine. If "-bios none -kernel filename" is specified, then do not load a firmware and instead only load and start the kernel image. For test runs, use an approach similar to riscv_find_and_load_firmware(). Signed-off-by: Sebastian Huber Reviewed-by: Alistair Francis Message-ID: <20250319061342.26435-5-sebastian.huber@embedded-brains.de> Signed-off-by: Alistair Francis --- hw/riscv/microchip_pfsoc.c | 59 +++++++++++++++++++++++++++----------- 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index b8a8d5251d..6e5b17c05f 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -578,29 +578,47 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) } /* - * We follow the following table to select which payload we execute. + * We follow the following table to select which firmware we use. * - * -bios | -kernel | payload - * -------+------------+-------- - * N | N | HSS - * Y | don't care | HSS - * N | Y | kernel - * - * This ensures backwards compatibility with how we used to expose -bios - * to users but allows them to run through direct kernel booting as well. + * -bios | -kernel | firmware + * --------------+------------+-------- + * none | N | error + * none | Y | kernel + * NULL, default | N | BIOS_FILENAME + * NULL, default | Y | RISCV64_BIOS_BIN + * other | don't care | other */ + if (machine->firmware && !strcmp(machine->firmware, "none")) { + if (!machine->kernel_filename) { + error_report("for -bios none, a kernel is required"); + exit(1); + } - if (machine->kernel_filename) { - firmware_name = RISCV64_BIOS_BIN; - firmware_load_addr = memmap[MICROCHIP_PFSOC_DRAM_LO].base; + firmware_name = NULL; + firmware_load_addr = RESET_VECTOR; + } else if (!machine->firmware || !strcmp(machine->firmware, "default")) { + if (machine->kernel_filename) { + firmware_name = RISCV64_BIOS_BIN; + firmware_load_addr = memmap[MICROCHIP_PFSOC_DRAM_LO].base; + } else { + firmware_name = BIOS_FILENAME; + firmware_load_addr = RESET_VECTOR; + } } else { - firmware_name = BIOS_FILENAME; + firmware_name = machine->firmware; firmware_load_addr = RESET_VECTOR; } - /* Load the firmware */ - firmware_end_addr = riscv_find_and_load_firmware(machine, firmware_name, - &firmware_load_addr, NULL); + /* Load the firmware if necessary */ + firmware_end_addr = firmware_load_addr; + if (firmware_name) { + char *filename = riscv_find_firmware(firmware_name, NULL); + if (filename) { + firmware_end_addr = riscv_load_firmware(filename, + &firmware_load_addr, NULL); + g_free(filename); + } + } riscv_boot_info_init(&boot_info, &s->soc.u_cpus); if (machine->kernel_filename) { @@ -638,8 +656,15 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) fdt_load_addr = 0; } + hwaddr start_addr; + if (firmware_name) { + start_addr = firmware_load_addr; + } else { + start_addr = kernel_entry; + } + /* Load the reset vector */ - riscv_setup_rom_reset_vec(machine, &s->soc.u_cpus, firmware_load_addr, + riscv_setup_rom_reset_vec(machine, &s->soc.u_cpus, start_addr, memmap[MICROCHIP_PFSOC_ENVM_DATA].base, memmap[MICROCHIP_PFSOC_ENVM_DATA].size, kernel_entry, fdt_load_addr); From e40b75fe5c94c6656e8ffcb0b9559b09882fa01b Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 19 Mar 2025 07:13:37 +0100 Subject: [PATCH 0974/2760] hw/riscv: Configurable MPFS CLINT timebase freq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This property enables the setting of the CLINT timebase frequency through the command line, for example: -machine microchip-icicle-kit,clint-timebase-frequency=10000000 Signed-off-by: Sebastian Huber Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-ID: <20250319061342.26435-6-sebastian.huber@embedded-brains.de> Signed-off-by: Alistair Francis --- hw/riscv/microchip_pfsoc.c | 49 +++++++++++++++++++++++++++--- include/hw/riscv/microchip_pfsoc.h | 1 + 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c index 6e5b17c05f..2e74783fce 100644 --- a/hw/riscv/microchip_pfsoc.c +++ b/hw/riscv/microchip_pfsoc.c @@ -39,6 +39,7 @@ #include "qemu/units.h" #include "qemu/cutils.h" #include "qapi/error.h" +#include "qapi/visitor.h" #include "hw/boards.h" #include "hw/loader.h" #include "hw/sysbus.h" @@ -61,9 +62,6 @@ #define BIOS_FILENAME "hss.bin" #define RESET_VECTOR 0x20220000 -/* CLINT timebase frequency */ -#define CLINT_TIMEBASE_FREQ 1000000 - /* GEM version */ #define GEM_REVISION 0x0107010c @@ -193,6 +191,7 @@ static void microchip_pfsoc_soc_instance_init(Object *obj) static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); + MicrochipIcicleKitState *iks = MICROCHIP_ICICLE_KIT_MACHINE(ms); MicrochipPFSoCState *s = MICROCHIP_PFSOC(dev); const MemMapEntry *memmap = microchip_pfsoc_memmap; MemoryRegion *system_memory = get_system_memory(); @@ -253,7 +252,7 @@ static void microchip_pfsoc_soc_realize(DeviceState *dev, Error **errp) memmap[MICROCHIP_PFSOC_CLINT].base + RISCV_ACLINT_SWI_SIZE, RISCV_ACLINT_DEFAULT_MTIMER_SIZE, 0, ms->smp.cpus, RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, - CLINT_TIMEBASE_FREQ, false); + iks->clint_timebase_freq, false); /* L2 cache controller */ create_unimplemented_device("microchip.pfsoc.l2cc", @@ -671,6 +670,40 @@ static void microchip_icicle_kit_machine_init(MachineState *machine) } } +static void microchip_icicle_kit_set_clint_timebase_freq(Object *obj, + Visitor *v, + const char *name, + void *opaque, + Error **errp) +{ + MicrochipIcicleKitState *s = MICROCHIP_ICICLE_KIT_MACHINE(obj); + uint32_t value; + + if (!visit_type_uint32(v, name, &value, errp)) { + return; + } + + s->clint_timebase_freq = value; +} + +static void microchip_icicle_kit_get_clint_timebase_freq(Object *obj, + Visitor *v, + const char *name, + void *opaque, + Error **errp) +{ + MicrochipIcicleKitState *s = MICROCHIP_ICICLE_KIT_MACHINE(obj); + uint32_t value = s->clint_timebase_freq; + + visit_type_uint32(v, name, &value, errp); +} + +static void microchip_icicle_kit_machine_instance_init(Object *obj) +{ + MicrochipIcicleKitState *m = MICROCHIP_ICICLE_KIT_MACHINE(obj); + m->clint_timebase_freq = 1000000; +} + static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, const void *data) { @@ -693,12 +726,20 @@ static void microchip_icicle_kit_machine_class_init(ObjectClass *oc, * See memory_tests() in mss_ddr.c in the HSS source code. */ mc->default_ram_size = 1537 * MiB; + + object_class_property_add(oc, "clint-timebase-frequency", "uint32_t", + microchip_icicle_kit_get_clint_timebase_freq, + microchip_icicle_kit_set_clint_timebase_freq, + NULL, NULL); + object_class_property_set_description(oc, "clint-timebase-frequency", + "Set CLINT timebase frequency in Hz."); } static const TypeInfo microchip_icicle_kit_machine_typeinfo = { .name = MACHINE_TYPE_NAME("microchip-icicle-kit"), .parent = TYPE_MACHINE, .class_init = microchip_icicle_kit_machine_class_init, + .instance_init = microchip_icicle_kit_machine_instance_init, .instance_size = sizeof(MicrochipIcicleKitState), }; diff --git a/include/hw/riscv/microchip_pfsoc.h b/include/hw/riscv/microchip_pfsoc.h index daef086da6..7ca9b976c1 100644 --- a/include/hw/riscv/microchip_pfsoc.h +++ b/include/hw/riscv/microchip_pfsoc.h @@ -67,6 +67,7 @@ typedef struct MicrochipIcicleKitState { MachineState parent_obj; /*< public >*/ + uint32_t clint_timebase_freq; MicrochipPFSoCState soc; } MicrochipIcicleKitState; From dd07ab1121ffd7003ae2cffde3046acd3123bbbd Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Wed, 19 Mar 2025 07:13:38 +0100 Subject: [PATCH 0975/2760] hw/riscv: microchip_pfsoc: Rework documentation Mention that running the HSS no longer works. Document the changed boot options. Reorder documentation blocks. Update URLs. Signed-off-by: Sebastian Huber Reviewed-by: Alistair Francis Message-ID: <20250319061342.26435-7-sebastian.huber@embedded-brains.de> Signed-off-by: Alistair Francis --- docs/system/riscv/microchip-icicle-kit.rst | 124 +++++++-------------- 1 file changed, 43 insertions(+), 81 deletions(-) diff --git a/docs/system/riscv/microchip-icicle-kit.rst b/docs/system/riscv/microchip-icicle-kit.rst index 40798b1aae..9809e94b84 100644 --- a/docs/system/riscv/microchip-icicle-kit.rst +++ b/docs/system/riscv/microchip-icicle-kit.rst @@ -5,10 +5,10 @@ Microchip PolarFire SoC Icicle Kit integrates a PolarFire SoC, with one SiFive's E51 plus four U54 cores and many on-chip peripherals and an FPGA. For more details about Microchip PolarFire SoC, please see: -https://www.microsemi.com/product-directory/soc-fpgas/5498-polarfire-soc-fpga +https://www.microchip.com/en-us/products/fpgas-and-plds/system-on-chip-fpgas/polarfire-soc-fpgas The Icicle Kit board information can be found here: -https://www.microsemi.com/existing-parts/parts/152514 +https://www.microchip.com/en-us/development-tool/mpfs-icicle-kit-es Supported devices ----------------- @@ -26,95 +26,48 @@ The ``microchip-icicle-kit`` machine supports the following devices: * 2 GEM Ethernet controllers * 1 SDHC storage controller +The memory is set to 1537 MiB by default. A sanity check on RAM size is +performed in the machine init routine to prompt user to increase the RAM size +to > 1537 MiB when less than 1537 MiB RAM is detected. + Boot options ------------ -The ``microchip-icicle-kit`` machine can start using the standard -bios -functionality for loading its BIOS image, aka Hart Software Services (HSS_). -HSS loads the second stage bootloader U-Boot from an SD card. Then a kernel -can be loaded from U-Boot. It also supports direct kernel booting via the --kernel option along with the device tree blob via -dtb. When direct kernel -boot is used, the OpenSBI fw_dynamic BIOS image is used to boot a payload -like U-Boot or OS kernel directly. - -The user provided DTB should have the following requirements: - -* The /cpus node should contain at least one subnode for E51 and the number - of subnodes should match QEMU's ``-smp`` option -* The /memory reg size should match QEMU’s selected ram_size via ``-m`` -* Should contain a node for the CLINT device with a compatible string - "riscv,clint0" - -QEMU follows below truth table to select which payload to execute: - -===== ========== ========== ======= --bios -kernel -dtb payload -===== ========== ========== ======= - N N don't care HSS - Y don't care don't care HSS - N Y Y kernel -===== ========== ========== ======= - -The memory is set to 1537 MiB by default which is the minimum required high -memory size by HSS. A sanity check on ram size is performed in the machine -init routine to prompt user to increase the RAM size to > 1537 MiB when less -than 1537 MiB ram is detected. - -Running HSS ------------ - -HSS 2020.12 release is tested at the time of writing. To build an HSS image -that can be booted by the ``microchip-icicle-kit`` machine, type the following -in the HSS source tree: - -.. code-block:: bash - - $ export CROSS_COMPILE=riscv64-linux- - $ cp boards/mpfs-icicle-kit-es/def_config .config - $ make BOARD=mpfs-icicle-kit-es - -Download the official SD card image released by Microchip and prepare it for -QEMU usage: - -.. code-block:: bash - - $ wget ftp://ftpsoc.microsemi.com/outgoing/core-image-minimal-dev-icicle-kit-es-sd-20201009141623.rootfs.wic.gz - $ gunzip core-image-minimal-dev-icicle-kit-es-sd-20201009141623.rootfs.wic.gz - $ qemu-img resize core-image-minimal-dev-icicle-kit-es-sd-20201009141623.rootfs.wic 4G - -Then we can boot the machine by: - -.. code-block:: bash - - $ qemu-system-riscv64 -M microchip-icicle-kit -smp 5 \ - -bios path/to/hss.bin -sd path/to/sdcard.img \ - -nic user,model=cadence_gem \ - -nic tap,ifname=tap,model=cadence_gem,script=no \ - -display none -serial stdio \ - -chardev socket,id=serial1,path=serial1.sock,server=on,wait=on \ - -serial chardev:serial1 +The ``microchip-icicle-kit`` machine provides some options to run a firmware +(BIOS) or a kernel image. QEMU follows below truth table to select the +firmware: -With above command line, current terminal session will be used for the first -serial port. Open another terminal window, and use ``minicom`` to connect the -second serial port. +============= =========== ====================================== +-bios -kernel firmware +============= =========== ====================================== +none N this is an error +none Y the kernel image +NULL, default N hss.bin +NULL, default Y opensbi-riscv64-generic-fw_dynamic.bin +other don't care the BIOS image +============= =========== ====================================== -.. code-block:: bash +Direct Kernel Boot +------------------ - $ minicom -D unix\#serial1.sock +Use the ``-kernel`` option to directly run a kernel image. When a direct +kernel boot is requested, a device tree blob may be specified via the ``-dtb`` +option. Unlike other QEMU machines, this machine does not generate a device +tree for the kernel. It shall be provided by the user. The user provided DTB +should meet the following requirements: -HSS output is on the first serial port (stdio) and U-Boot outputs on the -second serial port. U-Boot will automatically load the Linux kernel from -the SD card image. +* The ``/cpus`` node should contain at least one subnode for E51 and the number + of subnodes should match QEMU's ``-smp`` option. -Direct Kernel Boot ------------------- +* The ``/memory`` reg size should match QEMU’s selected RAM size via the ``-m`` + option. -Sometimes we just want to test booting a new kernel, and transforming the -kernel image to the format required by the HSS bootflow is tedious. We can -use '-kernel' for direct kernel booting just like other RISC-V machines do. +* It should contain a node for the CLINT device with a compatible string + "riscv,clint0". -In this mode, the OpenSBI fw_dynamic BIOS image for 'generic' platform is -used to boot an S-mode payload like U-Boot or OS kernel directly. +When ``-bios`` is not specified or set to ``default``, the OpenSBI +``fw_dynamic`` BIOS image for the ``generic`` platform is used to boot an +S-mode payload like U-Boot or OS kernel directly. For example, the following commands show building a U-Boot image from U-Boot mainline v2021.07 for the Microchip Icicle Kit board: @@ -146,4 +99,13 @@ CAVEATS: ``u-boot.bin`` has to be used which does contain one. To use the ELF image, we need to change to CONFIG_OF_EMBED or CONFIG_OF_PRIOR_STAGE. +Running HSS +----------- + +The machine ``microchip-icicle-kit`` used to run the Hart Software Services +(HSS_), however, the HSS development progressed and the QEMU machine +implementation lacks behind. Currently, running the HSS no longer works. +There is missing support in the clock and memory controller devices. In +particular, reading from the SD card does not work. + .. _HSS: https://github.com/polarfire-soc/hart-software-services From 9425790aceffc09b730a072cc3fc174d1b084f62 Mon Sep 17 00:00:00 2001 From: Paolo Savini Date: Thu, 13 Mar 2025 15:23:30 +0000 Subject: [PATCH 0976/2760] target/riscv: use tcg ops generation to emulate whole reg rvv loads/stores. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch replaces the use of a helper function with direct tcg ops generation in order to emulate whole register loads and stores. This is done in order to improve the performance of QEMU. We still use the helper function when vstart is not 0 at the beginning of the emulation of the whole register load or store or when we would end up generating partial loads or stores of vector elements (e.g. emulating 64 bits element loads with pairs of 32 bits loads on hosts with 32 bits registers). The latter condition ensures that we are not surprised by a trap in mid-element and consecutively that we can update vstart correctly. We also use the helper function when it performs better than tcg for specific combinations of vector length, number of fields and element size. Signed-off-by: Paolo Savini Reviewed-by: Daniel Henrique Barboza Reviewed-by: Richard Handerson Reviewed-by: Max Chou Reviewed-by: "Alex Bennée" Message-ID: <20250313152330.398396-2-paolo.savini@embecosm.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 155 +++++++++++++++++------- 1 file changed, 108 insertions(+), 47 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 7079f758ad..4ca7b15da1 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -1334,25 +1334,86 @@ GEN_VEXT_TRANS(vle64ff_v, MO_64, r2nfvm, ldff_op, ld_us_check) typedef void gen_helper_ldst_whole(TCGv_ptr, TCGv, TCGv_env, TCGv_i32); static bool ldst_whole_trans(uint32_t vd, uint32_t rs1, uint32_t nf, - gen_helper_ldst_whole *fn, - DisasContext *s) + uint32_t log2_esz, gen_helper_ldst_whole *fn, + DisasContext *s, bool is_load) { - TCGv_ptr dest; - TCGv base; - TCGv_i32 desc; - - uint32_t data = FIELD_DP32(0, VDATA, NF, nf); - data = FIELD_DP32(data, VDATA, VM, 1); - dest = tcg_temp_new_ptr(); - desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, - s->cfg_ptr->vlenb, data)); - - base = get_gpr(s, rs1, EXT_NONE); - tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); - mark_vs_dirty(s); - fn(dest, base, tcg_env, desc); + /* + * Load/store multiple bytes per iteration. + * When possible do this atomically. + * Update vstart with the number of processed elements. + * Use the helper function if either: + * - vstart is not 0. + * - the target has 32 bit registers and we are loading/storing 64 bit long + * elements. This is to ensure that we process every element with a single + * memory instruction. + */ + + bool use_helper_fn = !(s->vstart_eq_zero) || + (TCG_TARGET_REG_BITS == 32 && log2_esz == 3); + + if (!use_helper_fn) { + TCGv addr = tcg_temp_new(); + uint32_t size = s->cfg_ptr->vlenb * nf; + TCGv_i64 t8 = tcg_temp_new_i64(); + TCGv_i32 t4 = tcg_temp_new_i32(); + MemOp atomicity = MO_ATOM_NONE; + if (log2_esz == 0) { + atomicity = MO_ATOM_NONE; + } else { + atomicity = MO_ATOM_IFALIGN_PAIR; + } + if (TCG_TARGET_REG_BITS == 64) { + for (int i = 0; i < size; i += 8) { + addr = get_address(s, rs1, i); + if (is_load) { + tcg_gen_qemu_ld_i64(t8, addr, s->mem_idx, + MO_LE | MO_64 | atomicity); + tcg_gen_st_i64(t8, tcg_env, vreg_ofs(s, vd) + i); + } else { + tcg_gen_ld_i64(t8, tcg_env, vreg_ofs(s, vd) + i); + tcg_gen_qemu_st_i64(t8, addr, s->mem_idx, + MO_LE | MO_64 | atomicity); + } + if (i == size - 8) { + tcg_gen_movi_tl(cpu_vstart, 0); + } else { + tcg_gen_addi_tl(cpu_vstart, cpu_vstart, 8 >> log2_esz); + } + } + } else { + for (int i = 0; i < size; i += 4) { + addr = get_address(s, rs1, i); + if (is_load) { + tcg_gen_qemu_ld_i32(t4, addr, s->mem_idx, + MO_LE | MO_32 | atomicity); + tcg_gen_st_i32(t4, tcg_env, vreg_ofs(s, vd) + i); + } else { + tcg_gen_ld_i32(t4, tcg_env, vreg_ofs(s, vd) + i); + tcg_gen_qemu_st_i32(t4, addr, s->mem_idx, + MO_LE | MO_32 | atomicity); + } + if (i == size - 4) { + tcg_gen_movi_tl(cpu_vstart, 0); + } else { + tcg_gen_addi_tl(cpu_vstart, cpu_vstart, 4 >> log2_esz); + } + } + } + } else { + TCGv_ptr dest; + TCGv base; + TCGv_i32 desc; + uint32_t data = FIELD_DP32(0, VDATA, NF, nf); + data = FIELD_DP32(data, VDATA, VM, 1); + dest = tcg_temp_new_ptr(); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); + base = get_gpr(s, rs1, EXT_NONE); + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); + fn(dest, base, tcg_env, desc); + } finalize_rvv_inst(s); return true; @@ -1362,42 +1423,42 @@ static bool ldst_whole_trans(uint32_t vd, uint32_t rs1, uint32_t nf, * load and store whole register instructions ignore vtype and vl setting. * Thus, we don't need to check vill bit. (Section 7.9) */ -#define GEN_LDST_WHOLE_TRANS(NAME, ARG_NF) \ -static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \ -{ \ - if (require_rvv(s) && \ - QEMU_IS_ALIGNED(a->rd, ARG_NF)) { \ - return ldst_whole_trans(a->rd, a->rs1, ARG_NF, \ - gen_helper_##NAME, s); \ - } \ - return false; \ -} - -GEN_LDST_WHOLE_TRANS(vl1re8_v, 1) -GEN_LDST_WHOLE_TRANS(vl1re16_v, 1) -GEN_LDST_WHOLE_TRANS(vl1re32_v, 1) -GEN_LDST_WHOLE_TRANS(vl1re64_v, 1) -GEN_LDST_WHOLE_TRANS(vl2re8_v, 2) -GEN_LDST_WHOLE_TRANS(vl2re16_v, 2) -GEN_LDST_WHOLE_TRANS(vl2re32_v, 2) -GEN_LDST_WHOLE_TRANS(vl2re64_v, 2) -GEN_LDST_WHOLE_TRANS(vl4re8_v, 4) -GEN_LDST_WHOLE_TRANS(vl4re16_v, 4) -GEN_LDST_WHOLE_TRANS(vl4re32_v, 4) -GEN_LDST_WHOLE_TRANS(vl4re64_v, 4) -GEN_LDST_WHOLE_TRANS(vl8re8_v, 8) -GEN_LDST_WHOLE_TRANS(vl8re16_v, 8) -GEN_LDST_WHOLE_TRANS(vl8re32_v, 8) -GEN_LDST_WHOLE_TRANS(vl8re64_v, 8) +#define GEN_LDST_WHOLE_TRANS(NAME, ETYPE, ARG_NF, IS_LOAD) \ +static bool trans_##NAME(DisasContext *s, arg_##NAME * a) \ +{ \ + if (require_rvv(s) && \ + QEMU_IS_ALIGNED(a->rd, ARG_NF)) { \ + return ldst_whole_trans(a->rd, a->rs1, ARG_NF, ctzl(sizeof(ETYPE)), \ + gen_helper_##NAME, s, IS_LOAD); \ + } \ + return false; \ +} + +GEN_LDST_WHOLE_TRANS(vl1re8_v, int8_t, 1, true) +GEN_LDST_WHOLE_TRANS(vl1re16_v, int16_t, 1, true) +GEN_LDST_WHOLE_TRANS(vl1re32_v, int32_t, 1, true) +GEN_LDST_WHOLE_TRANS(vl1re64_v, int64_t, 1, true) +GEN_LDST_WHOLE_TRANS(vl2re8_v, int8_t, 2, true) +GEN_LDST_WHOLE_TRANS(vl2re16_v, int16_t, 2, true) +GEN_LDST_WHOLE_TRANS(vl2re32_v, int32_t, 2, true) +GEN_LDST_WHOLE_TRANS(vl2re64_v, int64_t, 2, true) +GEN_LDST_WHOLE_TRANS(vl4re8_v, int8_t, 4, true) +GEN_LDST_WHOLE_TRANS(vl4re16_v, int16_t, 4, true) +GEN_LDST_WHOLE_TRANS(vl4re32_v, int32_t, 4, true) +GEN_LDST_WHOLE_TRANS(vl4re64_v, int64_t, 4, true) +GEN_LDST_WHOLE_TRANS(vl8re8_v, int8_t, 8, true) +GEN_LDST_WHOLE_TRANS(vl8re16_v, int16_t, 8, true) +GEN_LDST_WHOLE_TRANS(vl8re32_v, int32_t, 8, true) +GEN_LDST_WHOLE_TRANS(vl8re64_v, int64_t, 8, true) /* * The vector whole register store instructions are encoded similar to * unmasked unit-stride store of elements with EEW=8. */ -GEN_LDST_WHOLE_TRANS(vs1r_v, 1) -GEN_LDST_WHOLE_TRANS(vs2r_v, 2) -GEN_LDST_WHOLE_TRANS(vs4r_v, 4) -GEN_LDST_WHOLE_TRANS(vs8r_v, 8) +GEN_LDST_WHOLE_TRANS(vs1r_v, int8_t, 1, false) +GEN_LDST_WHOLE_TRANS(vs2r_v, int8_t, 2, false) +GEN_LDST_WHOLE_TRANS(vs4r_v, int8_t, 4, false) +GEN_LDST_WHOLE_TRANS(vs8r_v, int8_t, 8, false) /* *** Vector Integer Arithmetic Instructions From d887736225984fcb3926e3f32f3cdc332c03ba8f Mon Sep 17 00:00:00 2001 From: Paolo Savini Date: Thu, 13 Mar 2025 12:39:26 +0000 Subject: [PATCH 0977/2760] Expand the probe_pages helper function to handle probe flags. This commit expands the probe_pages helper function in target/riscv/vector_helper.c to handle also the cases in which we need access to the flags raised while probing the memory and the host address. This is done in order to provide a unified interface to probe_access and probe_access_flags. The new version of probe_pages can now act as a regular call to probe_access as before and as a call to probe_access_flags. In the latter case the user need to pass pointers to flags and host address and a boolean value for nonfault. The flags and host address will be set and made available as for a direct call to probe_access_flags. Signed-off-by: Paolo Savini Reviewed-by: Daniel Henrique Barboza Message-ID: <20250313123926.374878-2-paolo.savini@embecosm.com> Signed-off-by: Alistair Francis --- target/riscv/vector_helper.c | 57 +++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 20 deletions(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 8eea3e6df0..3aec9a7731 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -117,25 +117,42 @@ static inline uint32_t vext_max_elems(uint32_t desc, uint32_t log2_esz) * It will trigger an exception if there is no mapping in TLB * and page table walk can't fill the TLB entry. Then the guest * software can return here after process the exception or never return. + * + * This function can also be used when direct access to probe_access_flags is + * needed in order to access the flags. If a pointer to a flags operand is + * provided the function will call probe_access_flags instead, use nonfault + * and update host and flags. */ -static void probe_pages(CPURISCVState *env, target_ulong addr, - target_ulong len, uintptr_t ra, - MMUAccessType access_type) +static void probe_pages(CPURISCVState *env, target_ulong addr, target_ulong len, + uintptr_t ra, MMUAccessType access_type, int mmu_index, + void **host, int *flags, bool nonfault) { target_ulong pagelen = -(addr | TARGET_PAGE_MASK); target_ulong curlen = MIN(pagelen, len); - int mmu_index = riscv_env_mmu_index(env, false); - probe_access(env, adjust_addr(env, addr), curlen, access_type, - mmu_index, ra); + if (flags != NULL) { + *flags = probe_access_flags(env, adjust_addr(env, addr), curlen, + access_type, mmu_index, nonfault, host, ra); + } else { + probe_access(env, adjust_addr(env, addr), curlen, access_type, + mmu_index, ra); + } + if (len > curlen) { addr += curlen; curlen = len - curlen; - probe_access(env, adjust_addr(env, addr), curlen, access_type, - mmu_index, ra); + if (flags != NULL) { + *flags = probe_access_flags(env, adjust_addr(env, addr), curlen, + access_type, mmu_index, nonfault, + host, ra); + } else { + probe_access(env, adjust_addr(env, addr), curlen, access_type, + mmu_index, ra); + } } } + static inline void vext_set_elem_mask(void *v0, int index, uint8_t value) { @@ -335,8 +352,8 @@ vext_page_ldst_us(CPURISCVState *env, void *vd, target_ulong addr, MMUAccessType access_type = is_load ? MMU_DATA_LOAD : MMU_DATA_STORE; /* Check page permission/pmp/watchpoint/etc. */ - flags = probe_access_flags(env, adjust_addr(env, addr), size, access_type, - mmu_index, true, &host, ra); + probe_pages(env, addr, size, ra, access_type, mmu_index, &host, &flags, + true); if (flags == 0) { if (nf == 1) { @@ -635,7 +652,7 @@ vext_ldff(void *vd, void *v0, target_ulong base, CPURISCVState *env, uint32_t vma = vext_vma(desc); target_ulong addr, addr_probe, addr_i, offset, remain, page_split, elems; int mmu_index = riscv_env_mmu_index(env, false); - int flags; + int flags, probe_flags; void *host; VSTART_CHECK_EARLY_EXIT(env, env->vl); @@ -649,15 +666,15 @@ vext_ldff(void *vd, void *v0, target_ulong base, CPURISCVState *env, } /* Check page permission/pmp/watchpoint/etc. */ - flags = probe_access_flags(env, adjust_addr(env, addr), elems * msize, - MMU_DATA_LOAD, mmu_index, true, &host, ra); + probe_pages(env, addr, elems * msize, ra, MMU_DATA_LOAD, mmu_index, &host, + &flags, true); /* If we are crossing a page check also the second page. */ if (env->vl > elems) { addr_probe = addr + (elems << log2_esz); - flags |= probe_access_flags(env, adjust_addr(env, addr_probe), - elems * msize, MMU_DATA_LOAD, mmu_index, - true, &host, ra); + probe_pages(env, addr_probe, elems * msize, ra, MMU_DATA_LOAD, + mmu_index, &host, &probe_flags, true); + flags |= probe_flags; } if (flags & ~TLB_WATCHPOINT) { @@ -669,16 +686,16 @@ vext_ldff(void *vd, void *v0, target_ulong base, CPURISCVState *env, addr_i = adjust_addr(env, base + i * (nf << log2_esz)); if (i == 0) { /* Allow fault on first element. */ - probe_pages(env, addr_i, nf << log2_esz, ra, MMU_DATA_LOAD); + probe_pages(env, addr_i, nf << log2_esz, ra, MMU_DATA_LOAD, + mmu_index, &host, NULL, false); } else { remain = nf << log2_esz; while (remain > 0) { offset = -(addr_i | TARGET_PAGE_MASK); /* Probe nonfault on subsequent elements. */ - flags = probe_access_flags(env, addr_i, offset, - MMU_DATA_LOAD, mmu_index, true, - &host, 0); + probe_pages(env, addr_i, offset, 0, MMU_DATA_LOAD, + mmu_index, &host, &flags, true); /* * Stop if invalid (unmapped) or mmio (transaction may From 56cde18d048e1e1f889e31f7553e1f39f03eeec5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 10 Apr 2025 18:17:22 +0200 Subject: [PATCH 0978/2760] hw/riscv: Fix type conflict of GLib function pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qtest_set_command_cb passed to g_once should match GThreadFunc, which it does not. But using g_once is actually unnecessary, because the function is called by riscv_harts_realize() under the Big QEMU Lock. Reported-by: Kohei Tokunaga Signed-off-by: Paolo Bonzini Reviewed-by: Alistair Francis Reviewed-by: Kohei Tokunaga Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250410161722.595634-1-pbonzini@redhat.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- hw/riscv/riscv_hart.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index ac6539bd3e..333083a4f1 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -104,8 +104,11 @@ static bool csr_qtest_callback(CharBackend *chr, gchar **words) static void riscv_cpu_register_csr_qtest_callback(void) { - static GOnce once; - g_once(&once, (GThreadFunc)qtest_set_command_cb, csr_qtest_callback); + static bool first = true; + if (first) { + first = false; + qtest_set_command_cb(csr_qtest_callback); + } } #endif From ad63158bdb33dab5704ea1cf740d2ea0387175df Mon Sep 17 00:00:00 2001 From: Ziqiao Kong Date: Tue, 15 Apr 2025 16:02:54 +0800 Subject: [PATCH 0979/2760] target/riscv: fix endless translation loop on big endian systems On big endian systems, pte and updated_pte hold big endian host data while pte_pa points to little endian target data. This means the branch at cpu_helper.c:1669 will be always satisfied and restart translation, causing an endless translation loop. The correctness of this patch can be deduced by: old_pte will hold value either from cpu_to_le32/64(pte) or cpu_to_le32/64(updated_pte), both of wich is litte endian. After that, an in-place conversion by le32/64_to_cpu(old_pte) ensures that old_pte now is in native endian, same with pte. Therefore, the endianness of the both side of if (old_pte != pte) is correct. Signed-off-by: Ziqiao Kong Reviewed-by: Alistair Francis Reviewed-by: Richard Henderson Message-ID: <20250415080254.3667878-2-ziqiaokong@gmail.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/cpu_helper.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index d5039f69a9..2ed69d7c2d 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1566,9 +1566,11 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, target_ulong *pte_pa = qemu_map_ram_ptr(mr->ram_block, addr1); target_ulong old_pte; if (riscv_cpu_sxl(env) == MXL_RV32) { - old_pte = qatomic_cmpxchg((uint32_t *)pte_pa, pte, updated_pte); + old_pte = qatomic_cmpxchg((uint32_t *)pte_pa, cpu_to_le32(pte), cpu_to_le32(updated_pte)); + old_pte = le32_to_cpu(old_pte); } else { - old_pte = qatomic_cmpxchg(pte_pa, pte, updated_pte); + old_pte = qatomic_cmpxchg(pte_pa, cpu_to_le64(pte), cpu_to_le64(updated_pte)); + old_pte = le64_to_cpu(old_pte); } if (old_pte != pte) { goto restart; From 22b448ccc6611a59d4aa54419f4d88c1f343cb35 Mon Sep 17 00:00:00 2001 From: Icenowy Zheng Date: Thu, 17 Apr 2025 15:22:06 +0800 Subject: [PATCH 0980/2760] common-user/host/riscv: use tail pseudoinstruction for calling tail The j pseudoinstruction maps to a JAL instruction, which can only handle a jump to somewhere with a signed 20-bit destination. In case of static linking and LTO'ing this easily leads to "relocation truncated to fit" error. Switch to use tail pseudoinstruction, which is the standard way to tail-call a function in medium code model (emits AUIPC+JALR). Signed-off-by: Icenowy Zheng Reviewed-by: Richard Henderson Message-ID: <20250417072206.364008-1-uwu@icenowy.me> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- common-user/host/riscv/safe-syscall.inc.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common-user/host/riscv/safe-syscall.inc.S b/common-user/host/riscv/safe-syscall.inc.S index dfe83c300e..c8b81e33d0 100644 --- a/common-user/host/riscv/safe-syscall.inc.S +++ b/common-user/host/riscv/safe-syscall.inc.S @@ -69,11 +69,11 @@ safe_syscall_end: /* code path setting errno */ 0: neg a0, a0 - j safe_syscall_set_errno_tail + tail safe_syscall_set_errno_tail /* code path when we didn't execute the syscall */ 2: li a0, QEMU_ERESTARTSYS - j safe_syscall_set_errno_tail + tail safe_syscall_set_errno_tail .cfi_endproc .size safe_syscall_base, .-safe_syscall_base From 3e8d1e4a628bb234c0b5d1ccd510900047181dbd Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 8 Apr 2025 18:39:29 +0800 Subject: [PATCH 0981/2760] target/riscv: rvv: Source vector registers cannot overlap mask register Add the relevant ISA paragraphs explaining why source (and destination) registers cannot overlap the mask register. Signed-off-by: Anton Blanchard Reviewed-by: Daniel Henrique Barboza Reviewed-by: Max Chou Signed-off-by: Max Chou Message-ID: <20250408103938.3623486-2-max.chou@sifive.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/insn_trans/trans_rvv.c.inc | 29 ++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 4ca7b15da1..2110392d1c 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -100,10 +100,33 @@ static bool require_scale_rvfmin(DisasContext *s) } } -/* Destination vector register group cannot overlap source mask register. */ -static bool require_vm(int vm, int vd) +/* + * Source and destination vector register groups cannot overlap source mask + * register: + * + * A vector register cannot be used to provide source operands with more than + * one EEW for a single instruction. A mask register source is considered to + * have EEW=1 for this constraint. An encoding that would result in the same + * vector register being read with two or more different EEWs, including when + * the vector register appears at different positions within two or more vector + * register groups, is reserved. + * (Section 5.2) + * + * A destination vector register group can overlap a source vector + * register group only if one of the following holds: + * 1. The destination EEW equals the source EEW. + * 2. The destination EEW is smaller than the source EEW and the overlap + * is in the lowest-numbered part of the source register group. + * 3. The destination EEW is greater than the source EEW, the source EMUL + * is at least 1, and the overlap is in the highest-numbered part of + * the destination register group. + * For the purpose of determining register group overlap constraints, mask + * elements have EEW=1. + * (Section 5.2) + */ +static bool require_vm(int vm, int v) { - return (vm != 0 || vd != 0); + return (vm != 0 || v != 0); } static bool require_nf(int vd, int nf, int lmul) From b0450a101d6c88789d0e8df2bcbef61bc7cd159a Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Tue, 8 Apr 2025 18:39:30 +0800 Subject: [PATCH 0982/2760] target/riscv: rvv: Add CHECK arg to GEN_OPFVF_WIDEN_TRANS Signed-off-by: Anton Blanchard Reviewed-by: Daniel Henrique Barboza Reviewed-by: Max Chou Signed-off-by: Max Chou Message-ID: <20250408103938.3623486-3-max.chou@sifive.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/insn_trans/trans_rvv.c.inc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 2110392d1c..d8333d8311 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -2687,10 +2687,10 @@ static bool opfvf_widen_check(DisasContext *s, arg_rmrr *a) } /* OPFVF with WIDEN */ -#define GEN_OPFVF_WIDEN_TRANS(NAME) \ +#define GEN_OPFVF_WIDEN_TRANS(NAME, CHECK) \ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ { \ - if (opfvf_widen_check(s, a)) { \ + if (CHECK(s, a)) { \ uint32_t data = 0; \ static gen_helper_opfvf *const fns[2] = { \ gen_helper_##NAME##_h, gen_helper_##NAME##_w, \ @@ -2706,8 +2706,8 @@ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ return false; \ } -GEN_OPFVF_WIDEN_TRANS(vfwadd_vf) -GEN_OPFVF_WIDEN_TRANS(vfwsub_vf) +GEN_OPFVF_WIDEN_TRANS(vfwadd_vf, opfvf_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwsub_vf, opfvf_widen_check) static bool opfwv_widen_check(DisasContext *s, arg_rmrr *a) { @@ -2789,7 +2789,7 @@ GEN_OPFVF_TRANS(vfrdiv_vf, opfvf_check) /* Vector Widening Floating-Point Multiply */ GEN_OPFVV_WIDEN_TRANS(vfwmul_vv, opfvv_widen_check) -GEN_OPFVF_WIDEN_TRANS(vfwmul_vf) +GEN_OPFVF_WIDEN_TRANS(vfwmul_vf, opfvf_widen_check) /* Vector Single-Width Floating-Point Fused Multiply-Add Instructions */ GEN_OPFVV_TRANS(vfmacc_vv, opfvv_check) @@ -2814,10 +2814,10 @@ GEN_OPFVV_WIDEN_TRANS(vfwmacc_vv, opfvv_widen_check) GEN_OPFVV_WIDEN_TRANS(vfwnmacc_vv, opfvv_widen_check) GEN_OPFVV_WIDEN_TRANS(vfwmsac_vv, opfvv_widen_check) GEN_OPFVV_WIDEN_TRANS(vfwnmsac_vv, opfvv_widen_check) -GEN_OPFVF_WIDEN_TRANS(vfwmacc_vf) -GEN_OPFVF_WIDEN_TRANS(vfwnmacc_vf) -GEN_OPFVF_WIDEN_TRANS(vfwmsac_vf) -GEN_OPFVF_WIDEN_TRANS(vfwnmsac_vf) +GEN_OPFVF_WIDEN_TRANS(vfwmacc_vf, opfvf_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwnmacc_vf, opfvf_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwmsac_vf, opfvf_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwnmsac_vf, opfvf_widen_check) /* Vector Floating-Point Square-Root Instruction */ From 629c2a8dd7506e1cb9b6b7127604641632ac453f Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 8 Apr 2025 18:39:31 +0800 Subject: [PATCH 0983/2760] target/riscv: rvv: Apply vext_check_input_eew to vrgather instructions to check mismatched input EEWs encoding constraint According to the v spec, a vector register cannot be used to provide source operands with more than one EEW for a single instruction. The vs1 EEW of vrgatherei16.vv is 16. Co-authored-by: Anton Blanchard Reviewed-by: Daniel Henrique Barboza Signed-off-by: Max Chou Message-ID: <20250408103938.3623486-4-max.chou@sifive.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/insn_trans/trans_rvv.c.inc | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index d8333d8311..04367e1bec 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -379,6 +379,35 @@ static bool vext_check_ld_index(DisasContext *s, int vd, int vs2, return ret; } +/* + * Check whether a vector register is used to provide source operands with + * more than one EEW for the vector instruction. + * Returns true if the instruction has valid encoding + * Returns false if encoding violates the mismatched input EEWs constraint + */ +static bool vext_check_input_eew(DisasContext *s, int vs1, uint8_t eew_vs1, + int vs2, uint8_t eew_vs2, int vm) +{ + bool is_valid = true; + int8_t emul_vs1 = eew_vs1 - s->sew + s->lmul; + int8_t emul_vs2 = eew_vs2 - s->sew + s->lmul; + + /* When vm is 0, vs1 & vs2(EEW!=1) group can't overlap v0 (EEW=1) */ + if ((vs1 != -1 && !require_vm(vm, vs1)) || + (vs2 != -1 && !require_vm(vm, vs2))) { + is_valid = false; + } + + /* When eew_vs1 != eew_vs2, check whether vs1 and vs2 are overlapped */ + if ((vs1 != -1 && vs2 != -1) && (eew_vs1 != eew_vs2) && + is_overlapped(vs1, 1 << MAX(emul_vs1, 0), + vs2, 1 << MAX(emul_vs2, 0))) { + is_valid = false; + } + + return is_valid; +} + static bool vext_check_ss(DisasContext *s, int vd, int vs, int vm) { return require_vm(vm, vd) && @@ -3733,6 +3762,7 @@ static bool vrgather_vv_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && vext_check_isa_ill(s) && + vext_check_input_eew(s, a->rs1, s->sew, a->rs2, s->sew, a->vm) && require_align(a->rd, s->lmul) && require_align(a->rs1, s->lmul) && require_align(a->rs2, s->lmul) && @@ -3745,6 +3775,7 @@ static bool vrgatherei16_vv_check(DisasContext *s, arg_rmrr *a) int8_t emul = MO_16 - s->sew + s->lmul; return require_rvv(s) && vext_check_isa_ill(s) && + vext_check_input_eew(s, a->rs1, MO_16, a->rs2, s->sew, a->vm) && (emul >= -3 && emul <= 3) && require_align(a->rd, s->lmul) && require_align(a->rs1, emul) && @@ -3764,6 +3795,7 @@ static bool vrgather_vx_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && vext_check_isa_ill(s) && + vext_check_input_eew(s, -1, MO_64, a->rs2, s->sew, a->vm) && require_align(a->rd, s->lmul) && require_align(a->rs2, s->lmul) && (a->rd != a->rs2) && From fbeaf35838768086b435833cb4dc5182c73ec2bc Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 8 Apr 2025 18:39:32 +0800 Subject: [PATCH 0984/2760] target/riscv: rvv: Apply vext_check_input_eew to OPIVI/OPIVX/OPFVF(vext_check_ss) instructions Handle the overlap of source registers with different EEWs. Co-authored-by: Anton Blanchard Reviewed-by: Daniel Henrique Barboza Signed-off-by: Max Chou Message-ID: <20250408103938.3623486-5-max.chou@sifive.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/insn_trans/trans_rvv.c.inc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 04367e1bec..b1e1db04a0 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -412,7 +412,8 @@ static bool vext_check_ss(DisasContext *s, int vd, int vs, int vm) { return require_vm(vm, vd) && require_align(vd, s->lmul) && - require_align(vs, s->lmul); + require_align(vs, s->lmul) && + vext_check_input_eew(s, vs, s->sew, -1, s->sew, vm); } /* From fda68acb7761af40df78db18e44ca1ff20195fe0 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 8 Apr 2025 18:39:33 +0800 Subject: [PATCH 0985/2760] target/riscv: rvv: Apply vext_check_input_eew to OPIVV/OPFVV(vext_check_sss) instructions Handle the overlap of source registers with different EEWs. Co-authored-by: Anton Blanchard Reviewed-by: Daniel Henrique Barboza Signed-off-by: Max Chou Message-ID: <20250408103938.3623486-6-max.chou@sifive.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/insn_trans/trans_rvv.c.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index b1e1db04a0..5de50422c9 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -432,6 +432,7 @@ static bool vext_check_ss(DisasContext *s, int vd, int vs, int vm) static bool vext_check_sss(DisasContext *s, int vd, int vs1, int vs2, int vm) { return vext_check_ss(s, vd, vs2, vm) && + vext_check_input_eew(s, vs1, s->sew, vs2, s->sew, vm) && require_align(vs1, s->lmul); } From b5480a693e3e657108746721ffe434b3bb6e7a72 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 8 Apr 2025 18:39:34 +0800 Subject: [PATCH 0986/2760] target/riscv: rvv: Apply vext_check_input_eew to vector slide instructions(OPIVI/OPIVX) Handle the overlap of source registers with different EEWs. Co-authored-by: Anton Blanchard Reviewed-by: Daniel Henrique Barboza Signed-off-by: Max Chou Message-ID: <20250408103938.3623486-7-max.chou@sifive.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/insn_trans/trans_rvv.c.inc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 5de50422c9..841692701c 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -638,7 +638,9 @@ static bool vext_check_slide(DisasContext *s, int vd, int vs2, { bool ret = require_align(vs2, s->lmul) && require_align(vd, s->lmul) && - require_vm(vm, vd); + require_vm(vm, vd) && + vext_check_input_eew(s, -1, 0, vs2, s->sew, vm); + if (is_over) { ret &= (vd != vs2); } From 411eefd56a3921ddbfdbadca596e1a8593ce834c Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 8 Apr 2025 18:39:35 +0800 Subject: [PATCH 0987/2760] target/riscv: rvv: Apply vext_check_input_eew to vector integer extension instructions(OPMVV) Handle the overlap of source registers with different EEWs. Co-authored-by: Anton Blanchard Reviewed-by: Daniel Henrique Barboza Signed-off-by: Max Chou Message-ID: <20250408103938.3623486-8-max.chou@sifive.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/insn_trans/trans_rvv.c.inc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 841692701c..954f03291b 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -3943,7 +3943,9 @@ static bool int_ext_check(DisasContext *s, arg_rmr *a, uint8_t div) require_align(a->rd, s->lmul) && require_align(a->rs2, s->lmul - div) && require_vm(a->vm, a->rd) && - require_noover(a->rd, s->lmul, a->rs2, s->lmul - div); + require_noover(a->rd, s->lmul, a->rs2, s->lmul - div) && + vext_check_input_eew(s, -1, 0, a->rs2, s->sew, a->vm); + return ret; } From 1f090a229f85e662394267680408bd31fd0a99c9 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 8 Apr 2025 18:39:36 +0800 Subject: [PATCH 0988/2760] target/riscv: rvv: Apply vext_check_input_eew to vector narrow/widen instructions Handle the overlap of source registers with different EEWs. The vd of vector widening mul-add instructions is one of the input operands. Co-authored-by: Anton Blanchard Reviewed-by: Daniel Henrique Barboza Signed-off-by: Max Chou Message-ID: <20250408103938.3623486-9-max.chou@sifive.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/insn_trans/trans_rvbf16.c.inc | 9 ++- target/riscv/insn_trans/trans_rvv.c.inc | 77 +++++++++++++++++----- 2 files changed, 68 insertions(+), 18 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvbf16.c.inc b/target/riscv/insn_trans/trans_rvbf16.c.inc index 0a9cd1ec31..066dc364c5 100644 --- a/target/riscv/insn_trans/trans_rvbf16.c.inc +++ b/target/riscv/insn_trans/trans_rvbf16.c.inc @@ -119,8 +119,11 @@ static bool trans_vfwmaccbf16_vv(DisasContext *ctx, arg_vfwmaccbf16_vv *a) REQUIRE_FPU; REQUIRE_ZVFBFWMA(ctx); + uint8_t sew = ctx->sew; if (require_rvv(ctx) && vext_check_isa_ill(ctx) && (ctx->sew == MO_16) && - vext_check_dss(ctx, a->rd, a->rs1, a->rs2, a->vm)) { + vext_check_dss(ctx, a->rd, a->rs1, a->rs2, a->vm) && + vext_check_input_eew(ctx, a->rd, sew + 1, a->rs1, sew, a->vm) && + vext_check_input_eew(ctx, a->rd, sew + 1, a->rs2, sew, a->vm)) { uint32_t data = 0; gen_set_rm_chkfrm(ctx, RISCV_FRM_DYN); @@ -146,8 +149,10 @@ static bool trans_vfwmaccbf16_vf(DisasContext *ctx, arg_vfwmaccbf16_vf *a) REQUIRE_FPU; REQUIRE_ZVFBFWMA(ctx); + uint8_t sew = ctx->sew; if (require_rvv(ctx) && (ctx->sew == MO_16) && vext_check_isa_ill(ctx) && - vext_check_ds(ctx, a->rd, a->rs2, a->vm)) { + vext_check_ds(ctx, a->rd, a->rs2, a->vm) && + vext_check_input_eew(ctx, a->rd, sew + 1, a->rs2, sew, a->vm)) { uint32_t data = 0; gen_set_rm(ctx, RISCV_FRM_DYN); diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 954f03291b..1d2b46fc44 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -528,6 +528,7 @@ static bool vext_narrow_check_common(DisasContext *s, int vd, int vs2, static bool vext_check_ds(DisasContext *s, int vd, int vs, int vm) { return vext_wide_check_common(s, vd, vm) && + vext_check_input_eew(s, vs, s->sew, -1, 0, vm) && require_align(vs, s->lmul) && require_noover(vd, s->lmul + 1, vs, s->lmul); } @@ -535,6 +536,7 @@ static bool vext_check_ds(DisasContext *s, int vd, int vs, int vm) static bool vext_check_dd(DisasContext *s, int vd, int vs, int vm) { return vext_wide_check_common(s, vd, vm) && + vext_check_input_eew(s, vs, s->sew + 1, -1, 0, vm) && require_align(vs, s->lmul + 1); } @@ -553,6 +555,7 @@ static bool vext_check_dd(DisasContext *s, int vd, int vs, int vm) static bool vext_check_dss(DisasContext *s, int vd, int vs1, int vs2, int vm) { return vext_check_ds(s, vd, vs2, vm) && + vext_check_input_eew(s, vs1, s->sew, vs2, s->sew, vm) && require_align(vs1, s->lmul) && require_noover(vd, s->lmul + 1, vs1, s->lmul); } @@ -575,12 +578,14 @@ static bool vext_check_dss(DisasContext *s, int vd, int vs1, int vs2, int vm) static bool vext_check_dds(DisasContext *s, int vd, int vs1, int vs2, int vm) { return vext_check_ds(s, vd, vs1, vm) && + vext_check_input_eew(s, vs1, s->sew, vs2, s->sew + 1, vm) && require_align(vs2, s->lmul + 1); } static bool vext_check_sd(DisasContext *s, int vd, int vs, int vm) { - bool ret = vext_narrow_check_common(s, vd, vs, vm); + bool ret = vext_narrow_check_common(s, vd, vs, vm) && + vext_check_input_eew(s, vs, s->sew + 1, -1, 0, vm); if (vd != vs) { ret &= require_noover(vd, s->lmul, vs, s->lmul + 1); } @@ -603,6 +608,7 @@ static bool vext_check_sd(DisasContext *s, int vd, int vs, int vm) static bool vext_check_sds(DisasContext *s, int vd, int vs1, int vs2, int vm) { return vext_check_sd(s, vd, vs2, vm) && + vext_check_input_eew(s, vs1, s->sew, vs2, s->sew + 1, vm) && require_align(vs1, s->lmul); } @@ -1815,6 +1821,16 @@ static bool opivv_widen_check(DisasContext *s, arg_rmrr *a) vext_check_dss(s, a->rd, a->rs1, a->rs2, a->vm); } +/* OPIVV with overwrite and WIDEN */ +static bool opivv_overwrite_widen_check(DisasContext *s, arg_rmrr *a) +{ + return require_rvv(s) && + vext_check_isa_ill(s) && + vext_check_dss(s, a->rd, a->rs1, a->rs2, a->vm) && + vext_check_input_eew(s, a->rd, s->sew + 1, a->rs1, s->sew, a->vm) && + vext_check_input_eew(s, a->rd, s->sew + 1, a->rs2, s->sew, a->vm); +} + static bool do_opivv_widen(DisasContext *s, arg_rmrr *a, gen_helper_gvec_4_ptr *fn, bool (*checkfn)(DisasContext *, arg_rmrr *)) @@ -1862,6 +1878,14 @@ static bool opivx_widen_check(DisasContext *s, arg_rmrr *a) vext_check_ds(s, a->rd, a->rs2, a->vm); } +static bool opivx_overwrite_widen_check(DisasContext *s, arg_rmrr *a) +{ + return require_rvv(s) && + vext_check_isa_ill(s) && + vext_check_ds(s, a->rd, a->rs2, a->vm) && + vext_check_input_eew(s, a->rd, s->sew + 1, a->rs2, s->sew, a->vm); +} + #define GEN_OPIVX_WIDEN_TRANS(NAME, CHECK) \ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ { \ @@ -2333,13 +2357,13 @@ GEN_OPIVX_TRANS(vmadd_vx, opivx_check) GEN_OPIVX_TRANS(vnmsub_vx, opivx_check) /* Vector Widening Integer Multiply-Add Instructions */ -GEN_OPIVV_WIDEN_TRANS(vwmaccu_vv, opivv_widen_check) -GEN_OPIVV_WIDEN_TRANS(vwmacc_vv, opivv_widen_check) -GEN_OPIVV_WIDEN_TRANS(vwmaccsu_vv, opivv_widen_check) -GEN_OPIVX_WIDEN_TRANS(vwmaccu_vx, opivx_widen_check) -GEN_OPIVX_WIDEN_TRANS(vwmacc_vx, opivx_widen_check) -GEN_OPIVX_WIDEN_TRANS(vwmaccsu_vx, opivx_widen_check) -GEN_OPIVX_WIDEN_TRANS(vwmaccus_vx, opivx_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwmaccu_vv, opivv_overwrite_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwmacc_vv, opivv_overwrite_widen_check) +GEN_OPIVV_WIDEN_TRANS(vwmaccsu_vv, opivv_overwrite_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmaccu_vx, opivx_overwrite_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmacc_vx, opivx_overwrite_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmaccsu_vx, opivx_overwrite_widen_check) +GEN_OPIVX_WIDEN_TRANS(vwmaccus_vx, opivx_overwrite_widen_check) /* Vector Integer Merge and Move Instructions */ static bool trans_vmv_v_v(DisasContext *s, arg_vmv_v_v *a) @@ -2680,6 +2704,17 @@ static bool opfvv_widen_check(DisasContext *s, arg_rmrr *a) vext_check_dss(s, a->rd, a->rs1, a->rs2, a->vm); } +static bool opfvv_overwrite_widen_check(DisasContext *s, arg_rmrr *a) +{ + return require_rvv(s) && + require_rvf(s) && + require_scale_rvf(s) && + vext_check_isa_ill(s) && + vext_check_dss(s, a->rd, a->rs1, a->rs2, a->vm) && + vext_check_input_eew(s, a->rd, s->sew + 1, a->rs1, s->sew, a->vm) && + vext_check_input_eew(s, a->rd, s->sew + 1, a->rs2, s->sew, a->vm); +} + /* OPFVV with WIDEN */ #define GEN_OPFVV_WIDEN_TRANS(NAME, CHECK) \ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ @@ -2719,6 +2754,16 @@ static bool opfvf_widen_check(DisasContext *s, arg_rmrr *a) vext_check_ds(s, a->rd, a->rs2, a->vm); } +static bool opfvf_overwrite_widen_check(DisasContext *s, arg_rmrr *a) +{ + return require_rvv(s) && + require_rvf(s) && + require_scale_rvf(s) && + vext_check_isa_ill(s) && + vext_check_ds(s, a->rd, a->rs2, a->vm) && + vext_check_input_eew(s, a->rd, s->sew + 1, a->rs2, s->sew, a->vm); +} + /* OPFVF with WIDEN */ #define GEN_OPFVF_WIDEN_TRANS(NAME, CHECK) \ static bool trans_##NAME(DisasContext *s, arg_rmrr *a) \ @@ -2843,14 +2888,14 @@ GEN_OPFVF_TRANS(vfmsub_vf, opfvf_check) GEN_OPFVF_TRANS(vfnmsub_vf, opfvf_check) /* Vector Widening Floating-Point Fused Multiply-Add Instructions */ -GEN_OPFVV_WIDEN_TRANS(vfwmacc_vv, opfvv_widen_check) -GEN_OPFVV_WIDEN_TRANS(vfwnmacc_vv, opfvv_widen_check) -GEN_OPFVV_WIDEN_TRANS(vfwmsac_vv, opfvv_widen_check) -GEN_OPFVV_WIDEN_TRANS(vfwnmsac_vv, opfvv_widen_check) -GEN_OPFVF_WIDEN_TRANS(vfwmacc_vf, opfvf_widen_check) -GEN_OPFVF_WIDEN_TRANS(vfwnmacc_vf, opfvf_widen_check) -GEN_OPFVF_WIDEN_TRANS(vfwmsac_vf, opfvf_widen_check) -GEN_OPFVF_WIDEN_TRANS(vfwnmsac_vf, opfvf_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwmacc_vv, opfvv_overwrite_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwnmacc_vv, opfvv_overwrite_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwmsac_vv, opfvv_overwrite_widen_check) +GEN_OPFVV_WIDEN_TRANS(vfwnmsac_vv, opfvv_overwrite_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwmacc_vf, opfvf_overwrite_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwnmacc_vf, opfvf_overwrite_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwmsac_vf, opfvf_overwrite_widen_check) +GEN_OPFVF_WIDEN_TRANS(vfwnmsac_vf, opfvf_overwrite_widen_check) /* Vector Floating-Point Square-Root Instruction */ From db21c3eb05504c4cedaad4f7b19e588361b02385 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 8 Apr 2025 18:39:37 +0800 Subject: [PATCH 0989/2760] target/riscv: rvv: Apply vext_check_input_eew to vector indexed load/store instructions Handle the overlap of source registers with different EEWs. Co-authored-by: Anton Blanchard Reviewed-by: Daniel Henrique Barboza Signed-off-by: Max Chou Message-ID: <20250408103938.3623486-10-max.chou@sifive.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/insn_trans/trans_rvv.c.inc | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 1d2b46fc44..2b6077ac06 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -1277,7 +1277,8 @@ static bool ld_index_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) { return require_rvv(s) && vext_check_isa_ill(s) && - vext_check_ld_index(s, a->rd, a->rs2, a->nf, a->vm, eew); + vext_check_ld_index(s, a->rd, a->rs2, a->nf, a->vm, eew) && + vext_check_input_eew(s, -1, 0, a->rs2, eew, a->vm); } GEN_VEXT_TRANS(vlxei8_v, MO_8, rnfvm, ld_index_op, ld_index_check) @@ -1329,7 +1330,8 @@ static bool st_index_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) { return require_rvv(s) && vext_check_isa_ill(s) && - vext_check_st_index(s, a->rd, a->rs2, a->nf, eew); + vext_check_st_index(s, a->rd, a->rs2, a->nf, eew) && + vext_check_input_eew(s, a->rd, s->sew, a->rs2, eew, a->vm); } GEN_VEXT_TRANS(vsxei8_v, MO_8, rnfvm, st_index_op, st_index_check) From 8539a1244bf240d28917effb88a140eb58e45e88 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Tue, 8 Apr 2025 18:39:38 +0800 Subject: [PATCH 0990/2760] target/riscv: Fix the rvv reserved encoding of unmasked instructions According to the v spec, the encodings of vcomoress.vm and vector mask-register logical instructions with vm=0 are reserved. Reviewed-by: Daniel Henrique Barboza Signed-off-by: Max Chou Message-ID: <20250408103938.3623486-11-max.chou@sifive.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/insn32.decode | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/target/riscv/insn32.decode b/target/riscv/insn32.decode index 6d1a13c826..cd23b1f3a9 100644 --- a/target/riscv/insn32.decode +++ b/target/riscv/insn32.decode @@ -703,14 +703,14 @@ vfredmax_vs 000111 . ..... ..... 001 ..... 1010111 @r_vm # Vector widening ordered and unordered float reduction sum vfwredusum_vs 110001 . ..... ..... 001 ..... 1010111 @r_vm vfwredosum_vs 110011 . ..... ..... 001 ..... 1010111 @r_vm -vmand_mm 011001 - ..... ..... 010 ..... 1010111 @r -vmnand_mm 011101 - ..... ..... 010 ..... 1010111 @r -vmandn_mm 011000 - ..... ..... 010 ..... 1010111 @r -vmxor_mm 011011 - ..... ..... 010 ..... 1010111 @r -vmor_mm 011010 - ..... ..... 010 ..... 1010111 @r -vmnor_mm 011110 - ..... ..... 010 ..... 1010111 @r -vmorn_mm 011100 - ..... ..... 010 ..... 1010111 @r -vmxnor_mm 011111 - ..... ..... 010 ..... 1010111 @r +vmand_mm 011001 1 ..... ..... 010 ..... 1010111 @r +vmnand_mm 011101 1 ..... ..... 010 ..... 1010111 @r +vmandn_mm 011000 1 ..... ..... 010 ..... 1010111 @r +vmxor_mm 011011 1 ..... ..... 010 ..... 1010111 @r +vmor_mm 011010 1 ..... ..... 010 ..... 1010111 @r +vmnor_mm 011110 1 ..... ..... 010 ..... 1010111 @r +vmorn_mm 011100 1 ..... ..... 010 ..... 1010111 @r +vmxnor_mm 011111 1 ..... ..... 010 ..... 1010111 @r vcpop_m 010000 . ..... 10000 010 ..... 1010111 @r2_vm vfirst_m 010000 . ..... 10001 010 ..... 1010111 @r2_vm vmsbf_m 010100 . ..... 00001 010 ..... 1010111 @r2_vm @@ -732,7 +732,7 @@ vrgather_vv 001100 . ..... ..... 000 ..... 1010111 @r_vm vrgatherei16_vv 001110 . ..... ..... 000 ..... 1010111 @r_vm vrgather_vx 001100 . ..... ..... 100 ..... 1010111 @r_vm vrgather_vi 001100 . ..... ..... 011 ..... 1010111 @r_vm -vcompress_vm 010111 - ..... ..... 010 ..... 1010111 @r +vcompress_vm 010111 1 ..... ..... 010 ..... 1010111 @r vmv1r_v 100111 1 ..... 00000 011 ..... 1010111 @r2rd vmv2r_v 100111 1 ..... 00001 011 ..... 1010111 @r2rd vmv4r_v 100111 1 ..... 00011 011 ..... 1010111 @r2rd From 2669b696e243b64f8ea1a6468dcee255de99f08d Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Mon, 14 Apr 2025 21:30:06 +0000 Subject: [PATCH 0991/2760] target/riscv: Fix vslidedown with rvv_ta_all_1s vslidedown always zeroes elements past vl, where it should use the tail policy. Signed-off-by: Anton Blanchard Reviewed-by: Alistair Francis Message-ID: <20250414213006.3509058-1-antonb@tenstorrent.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/vector_helper.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 3aec9a7731..5dc1c10012 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -5133,9 +5133,11 @@ void HELPER(NAME)(void *vd, void *v0, target_ulong s1, void *vs2, \ } \ \ for (i = i_max; i < vl; ++i) { \ - if (vm || vext_elem_mask(v0, i)) { \ - *((ETYPE *)vd + H(i)) = 0; \ + if (!vm && !vext_elem_mask(v0, i)) { \ + vext_set_elems_1s(vd, vma, i * esz, (i + 1) * esz); \ + continue; \ } \ + *((ETYPE *)vd + H(i)) = 0; \ } \ \ env->vstart = 0; \ From 355cdac7d86ca2bc1b3729a6dda0b98deb23a92b Mon Sep 17 00:00:00 2001 From: Alistair Francis Date: Tue, 22 Apr 2025 12:47:52 +1000 Subject: [PATCH 0992/2760] MAINTAINERS: Add common-user/host/riscv to RISC-V section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250422024752.2060289-1-alistair.francis@wdc.com> Signed-off-by: Alistair Francis --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 57dddcc80d..7060cf49b9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -328,6 +328,7 @@ F: include/hw/char/riscv_htif.h F: include/hw/riscv/ F: linux-user/host/riscv32/ F: linux-user/host/riscv64/ +F: common-user/host/riscv* F: tests/functional/test_riscv* F: tests/tcg/riscv64/ From 6c8954cb35b1898f94d0d1e60f1abc8d47ae9f02 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 25 Apr 2025 08:23:05 -0700 Subject: [PATCH 0993/2760] target/riscv: Pass ra to riscv_csr_write_fn MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250425152311.804338-2-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 3 +- target/riscv/csr.c | 226 +++++++++++++++++++++++---------------------- 2 files changed, 118 insertions(+), 111 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index c66ac3bc27..4265ce06ee 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -838,7 +838,8 @@ typedef RISCVException (*riscv_csr_predicate_fn)(CPURISCVState *env, typedef RISCVException (*riscv_csr_read_fn)(CPURISCVState *env, int csrno, target_ulong *ret_value); typedef RISCVException (*riscv_csr_write_fn)(CPURISCVState *env, int csrno, - target_ulong new_value); + target_ulong new_value, + uintptr_t ra); typedef RISCVException (*riscv_csr_op_fn)(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, diff --git a/target/riscv/csr.c b/target/riscv/csr.c index a32e1455c9..ba7620ef3d 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -830,13 +830,15 @@ static RISCVException seed(CPURISCVState *env, int csrno) } /* zicfiss CSR_SSP read and write */ -static int read_ssp(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_ssp(CPURISCVState *env, int csrno, + target_ulong *val) { *val = env->ssp; return RISCV_EXCP_NONE; } -static int write_ssp(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_ssp(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { env->ssp = val; return RISCV_EXCP_NONE; @@ -851,7 +853,7 @@ static RISCVException read_fflags(CPURISCVState *env, int csrno, } static RISCVException write_fflags(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) if (riscv_has_ext(env, RVF)) { @@ -870,7 +872,7 @@ static RISCVException read_frm(CPURISCVState *env, int csrno, } static RISCVException write_frm(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) if (riscv_has_ext(env, RVF)) { @@ -890,7 +892,7 @@ static RISCVException read_fcsr(CPURISCVState *env, int csrno, } static RISCVException write_fcsr(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) if (riscv_has_ext(env, RVF)) { @@ -942,7 +944,7 @@ static RISCVException read_vxrm(CPURISCVState *env, int csrno, } static RISCVException write_vxrm(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) env->mstatus |= MSTATUS_VS; @@ -959,7 +961,7 @@ static RISCVException read_vxsat(CPURISCVState *env, int csrno, } static RISCVException write_vxsat(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) env->mstatus |= MSTATUS_VS; @@ -976,7 +978,7 @@ static RISCVException read_vstart(CPURISCVState *env, int csrno, } static RISCVException write_vstart(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) env->mstatus |= MSTATUS_VS; @@ -997,7 +999,7 @@ static RISCVException read_vcsr(CPURISCVState *env, int csrno, } static RISCVException write_vcsr(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { #if !defined(CONFIG_USER_ONLY) env->mstatus |= MSTATUS_VS; @@ -1055,7 +1057,7 @@ static RISCVException read_mcyclecfg(CPURISCVState *env, int csrno, } static RISCVException write_mcyclecfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t inh_avail_mask; @@ -1084,7 +1086,7 @@ static RISCVException read_mcyclecfgh(CPURISCVState *env, int csrno, } static RISCVException write_mcyclecfgh(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { target_ulong inh_avail_mask = (target_ulong)(~MHPMEVENTH_FILTER_MASK | MCYCLECFGH_BIT_MINH); @@ -1109,7 +1111,7 @@ static RISCVException read_minstretcfg(CPURISCVState *env, int csrno, } static RISCVException write_minstretcfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t inh_avail_mask; @@ -1136,7 +1138,7 @@ static RISCVException read_minstretcfgh(CPURISCVState *env, int csrno, } static RISCVException write_minstretcfgh(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { target_ulong inh_avail_mask = (target_ulong)(~MHPMEVENTH_FILTER_MASK | MINSTRETCFGH_BIT_MINH); @@ -1163,7 +1165,7 @@ static RISCVException read_mhpmevent(CPURISCVState *env, int csrno, } static RISCVException write_mhpmevent(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { int evt_index = csrno - CSR_MCOUNTINHIBIT; uint64_t mhpmevt_val = val; @@ -1201,7 +1203,7 @@ static RISCVException read_mhpmeventh(CPURISCVState *env, int csrno, } static RISCVException write_mhpmeventh(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { int evt_index = csrno - CSR_MHPMEVENT3H + 3; uint64_t mhpmevth_val; @@ -1343,14 +1345,16 @@ static RISCVException riscv_pmu_write_ctrh(CPURISCVState *env, target_ulong val, return RISCV_EXCP_NONE; } -static int write_mhpmcounter(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_mhpmcounter(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { int ctr_idx = csrno - CSR_MCYCLE; return riscv_pmu_write_ctr(env, val, ctr_idx); } -static int write_mhpmcounterh(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_mhpmcounterh(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { int ctr_idx = csrno - CSR_MCYCLEH; @@ -1661,7 +1665,7 @@ static RISCVException read_vstimecmph(CPURISCVState *env, int csrno, } static RISCVException write_vstimecmp(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (riscv_cpu_mxl(env) == MXL_RV32) { env->vstimecmp = deposit64(env->vstimecmp, 0, 32, (uint64_t)val); @@ -1676,7 +1680,7 @@ static RISCVException write_vstimecmp(CPURISCVState *env, int csrno, } static RISCVException write_vstimecmph(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->vstimecmp = deposit64(env->vstimecmp, 32, 32, (uint64_t)val); riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp, @@ -1710,13 +1714,13 @@ static RISCVException read_stimecmph(CPURISCVState *env, int csrno, } static RISCVException write_stimecmp(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (env->virt_enabled) { if (env->hvictl & HVICTL_VTI) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } - return write_vstimecmp(env, csrno, val); + return write_vstimecmp(env, csrno, val, ra); } if (riscv_cpu_mxl(env) == MXL_RV32) { @@ -1731,13 +1735,13 @@ static RISCVException write_stimecmp(CPURISCVState *env, int csrno, } static RISCVException write_stimecmph(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (env->virt_enabled) { if (env->hvictl & HVICTL_VTI) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } - return write_vstimecmph(env, csrno, val); + return write_vstimecmph(env, csrno, val, ra); } env->stimecmp = deposit64(env->stimecmp, 32, 32, (uint64_t)val); @@ -1842,7 +1846,7 @@ static RISCVException read_zero(CPURISCVState *env, int csrno, } static RISCVException write_ignore(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { return RISCV_EXCP_NONE; } @@ -1963,7 +1967,7 @@ static target_ulong legalize_mpp(CPURISCVState *env, target_ulong old_mpp, } static RISCVException write_mstatus(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t mstatus = env->mstatus; uint64_t mask = 0; @@ -2042,7 +2046,7 @@ static RISCVException read_mstatush(CPURISCVState *env, int csrno, } static RISCVException write_mstatush(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t valh = (uint64_t)val << 32; uint64_t mask = riscv_has_ext(env, RVH) ? MSTATUS_MPV | MSTATUS_GVA : 0; @@ -2096,7 +2100,7 @@ static RISCVException read_misa(CPURISCVState *env, int csrno, } static RISCVException write_misa(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { RISCVCPU *cpu = env_archcpu(env); uint32_t orig_misa_ext = env->misa_ext; @@ -2160,7 +2164,7 @@ static RISCVException read_medeleg(CPURISCVState *env, int csrno, } static RISCVException write_medeleg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->medeleg = (env->medeleg & ~DELEGABLE_EXCPS) | (val & DELEGABLE_EXCPS); return RISCV_EXCP_NONE; @@ -2955,7 +2959,7 @@ static RISCVException read_mtvec(CPURISCVState *env, int csrno, } static RISCVException write_mtvec(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ if ((val & 3) < 2) { @@ -2974,7 +2978,7 @@ static RISCVException read_mcountinhibit(CPURISCVState *env, int csrno, } static RISCVException write_mcountinhibit(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { int cidx; PMUCTRState *counter; @@ -3049,10 +3053,9 @@ static RISCVException read_scountinhibit(CPURISCVState *env, int csrno, } static RISCVException write_scountinhibit(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { - write_mcountinhibit(env, csrno, val & env->mcounteren); - return RISCV_EXCP_NONE; + return write_mcountinhibit(env, csrno, val & env->mcounteren, ra); } static RISCVException read_mcounteren(CPURISCVState *env, int csrno, @@ -3063,7 +3066,7 @@ static RISCVException read_mcounteren(CPURISCVState *env, int csrno, } static RISCVException write_mcounteren(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { RISCVCPU *cpu = env_archcpu(env); @@ -3097,7 +3100,7 @@ static RISCVException read_mscratch(CPURISCVState *env, int csrno, } static RISCVException write_mscratch(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->mscratch = val; return RISCV_EXCP_NONE; @@ -3111,7 +3114,7 @@ static RISCVException read_mepc(CPURISCVState *env, int csrno, } static RISCVException write_mepc(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->mepc = val; return RISCV_EXCP_NONE; @@ -3125,7 +3128,7 @@ static RISCVException read_mcause(CPURISCVState *env, int csrno, } static RISCVException write_mcause(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->mcause = val; return RISCV_EXCP_NONE; @@ -3139,7 +3142,7 @@ static RISCVException read_mtval(CPURISCVState *env, int csrno, } static RISCVException write_mtval(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->mtval = val; return RISCV_EXCP_NONE; @@ -3154,9 +3157,9 @@ static RISCVException read_menvcfg(CPURISCVState *env, int csrno, } static RISCVException write_henvcfg(CPURISCVState *env, int csrno, - target_ulong val); + target_ulong val, uintptr_t ra); static RISCVException write_menvcfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | @@ -3188,9 +3191,7 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, } } env->menvcfg = (env->menvcfg & ~mask) | (val & mask); - write_henvcfg(env, CSR_HENVCFG, env->henvcfg); - - return RISCV_EXCP_NONE; + return write_henvcfg(env, CSR_HENVCFG, env->henvcfg, ra); } static RISCVException read_menvcfgh(CPURISCVState *env, int csrno, @@ -3201,9 +3202,9 @@ static RISCVException read_menvcfgh(CPURISCVState *env, int csrno, } static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, - target_ulong val); + target_ulong val, uintptr_t ra); static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); uint64_t mask = (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | @@ -3218,9 +3219,7 @@ static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, } env->menvcfg = (env->menvcfg & ~mask) | (valh & mask); - write_henvcfgh(env, CSR_HENVCFGH, env->henvcfg >> 32); - - return RISCV_EXCP_NONE; + return write_henvcfgh(env, CSR_HENVCFGH, env->henvcfg >> 32, ra); } static RISCVException read_senvcfg(CPURISCVState *env, int csrno, @@ -3238,7 +3237,7 @@ static RISCVException read_senvcfg(CPURISCVState *env, int csrno, } static RISCVException write_senvcfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t mask = SENVCFG_FIOM | SENVCFG_CBIE | SENVCFG_CBCFE | SENVCFG_CBZE; RISCVException ret; @@ -3295,7 +3294,7 @@ static RISCVException read_henvcfg(CPURISCVState *env, int csrno, } static RISCVException write_henvcfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE; RISCVException ret; @@ -3350,7 +3349,7 @@ static RISCVException read_henvcfgh(CPURISCVState *env, int csrno, } static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t mask = env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE | HENVCFG_DTE); @@ -3388,7 +3387,7 @@ static RISCVException write_mstateen(CPURISCVState *env, int csrno, } static RISCVException write_mstateen0(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; if (!riscv_has_ext(env, RVF)) { @@ -3420,7 +3419,7 @@ static RISCVException write_mstateen0(CPURISCVState *env, int csrno, } static RISCVException write_mstateen_1_3(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { return write_mstateen(env, csrno, SMSTATEEN_STATEEN, new_val); } @@ -3447,7 +3446,7 @@ static RISCVException write_mstateenh(CPURISCVState *env, int csrno, } static RISCVException write_mstateen0h(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; @@ -3463,7 +3462,7 @@ static RISCVException write_mstateen0h(CPURISCVState *env, int csrno, } static RISCVException write_mstateenh_1_3(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { return write_mstateenh(env, csrno, SMSTATEEN_STATEEN, new_val); } @@ -3492,7 +3491,7 @@ static RISCVException write_hstateen(CPURISCVState *env, int csrno, } static RISCVException write_hstateen0(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; @@ -3521,7 +3520,7 @@ static RISCVException write_hstateen0(CPURISCVState *env, int csrno, } static RISCVException write_hstateen_1_3(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { return write_hstateen(env, csrno, SMSTATEEN_STATEEN, new_val); } @@ -3552,7 +3551,7 @@ static RISCVException write_hstateenh(CPURISCVState *env, int csrno, } static RISCVException write_hstateen0h(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; @@ -3564,7 +3563,7 @@ static RISCVException write_hstateen0h(CPURISCVState *env, int csrno, } static RISCVException write_hstateenh_1_3(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { return write_hstateenh(env, csrno, SMSTATEEN_STATEEN, new_val); } @@ -3603,7 +3602,7 @@ static RISCVException write_sstateen(CPURISCVState *env, int csrno, } static RISCVException write_sstateen0(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { uint64_t wr_mask = SMSTATEEN_STATEEN | SMSTATEEN0_HSENVCFG; @@ -3615,7 +3614,7 @@ static RISCVException write_sstateen0(CPURISCVState *env, int csrno, } static RISCVException write_sstateen_1_3(CPURISCVState *env, int csrno, - target_ulong new_val) + target_ulong new_val, uintptr_t ra) { return write_sstateen(env, csrno, SMSTATEEN_STATEEN, new_val); } @@ -3866,7 +3865,7 @@ static RISCVException read_sstatus(CPURISCVState *env, int csrno, } static RISCVException write_sstatus(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { target_ulong mask = (sstatus_v1_10_mask); @@ -3883,7 +3882,7 @@ static RISCVException write_sstatus(CPURISCVState *env, int csrno, mask |= SSTATUS_SDT; } target_ulong newval = (env->mstatus & ~mask) | (val & mask); - return write_mstatus(env, CSR_MSTATUS, newval); + return write_mstatus(env, CSR_MSTATUS, newval, ra); } static RISCVException rmw_vsie64(CPURISCVState *env, int csrno, @@ -4035,7 +4034,7 @@ static RISCVException read_stvec(CPURISCVState *env, int csrno, } static RISCVException write_stvec(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ if ((val & 3) < 2) { @@ -4054,7 +4053,7 @@ static RISCVException read_scounteren(CPURISCVState *env, int csrno, } static RISCVException write_scounteren(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { RISCVCPU *cpu = env_archcpu(env); @@ -4088,7 +4087,7 @@ static RISCVException read_sscratch(CPURISCVState *env, int csrno, } static RISCVException write_sscratch(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->sscratch = val; return RISCV_EXCP_NONE; @@ -4102,7 +4101,7 @@ static RISCVException read_sepc(CPURISCVState *env, int csrno, } static RISCVException write_sepc(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->sepc = val; return RISCV_EXCP_NONE; @@ -4116,7 +4115,7 @@ static RISCVException read_scause(CPURISCVState *env, int csrno, } static RISCVException write_scause(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->scause = val; return RISCV_EXCP_NONE; @@ -4130,7 +4129,7 @@ static RISCVException read_stval(CPURISCVState *env, int csrno, } static RISCVException write_stval(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->stval = val; return RISCV_EXCP_NONE; @@ -4270,7 +4269,7 @@ static RISCVException read_satp(CPURISCVState *env, int csrno, } static RISCVException write_satp(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (!riscv_cpu_cfg(env)->mmu) { return RISCV_EXCP_NONE; @@ -4492,7 +4491,7 @@ static RISCVException read_hstatus(CPURISCVState *env, int csrno, } static RISCVException write_hstatus(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t mask = (target_ulong)-1; if (!env_archcpu(env)->cfg.ext_svukte) { @@ -4524,7 +4523,7 @@ static RISCVException read_hedeleg(CPURISCVState *env, int csrno, } static RISCVException write_hedeleg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->hedeleg = val & vs_delegable_excps; return RISCV_EXCP_NONE; @@ -4545,7 +4544,7 @@ static RISCVException read_hedelegh(CPURISCVState *env, int csrno, } static RISCVException write_hedelegh(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { RISCVException ret; ret = smstateen_acc_ok(env, 0, SMSTATEEN0_P1P13); @@ -4808,7 +4807,7 @@ static RISCVException read_hcounteren(CPURISCVState *env, int csrno, } static RISCVException write_hcounteren(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { RISCVCPU *cpu = env_archcpu(env); @@ -4828,7 +4827,7 @@ static RISCVException read_hgeie(CPURISCVState *env, int csrno, } static RISCVException write_hgeie(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { /* Only GEILEN:1 bits implemented and BIT0 is never implemented */ val &= ((((target_ulong)1) << env->geilen) - 1) << 1; @@ -4847,7 +4846,7 @@ static RISCVException read_htval(CPURISCVState *env, int csrno, } static RISCVException write_htval(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->htval = val; return RISCV_EXCP_NONE; @@ -4861,7 +4860,7 @@ static RISCVException read_htinst(CPURISCVState *env, int csrno, } static RISCVException write_htinst(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { return RISCV_EXCP_NONE; } @@ -4883,7 +4882,7 @@ static RISCVException read_hgatp(CPURISCVState *env, int csrno, } static RISCVException write_hgatp(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->hgatp = legalize_xatp(env, env->hgatp, val); return RISCV_EXCP_NONE; @@ -4901,7 +4900,7 @@ static RISCVException read_htimedelta(CPURISCVState *env, int csrno, } static RISCVException write_htimedelta(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (!env->rdtime_fn) { return RISCV_EXCP_ILLEGAL_INST; @@ -4933,7 +4932,7 @@ static RISCVException read_htimedeltah(CPURISCVState *env, int csrno, } static RISCVException write_htimedeltah(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (!env->rdtime_fn) { return RISCV_EXCP_ILLEGAL_INST; @@ -4957,7 +4956,7 @@ static RISCVException read_hvictl(CPURISCVState *env, int csrno, } static RISCVException write_hvictl(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->hvictl = val & HVICTL_VALID_MASK; return RISCV_EXCP_NONE; @@ -5022,7 +5021,7 @@ static RISCVException read_hviprio1(CPURISCVState *env, int csrno, } static RISCVException write_hviprio1(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { return write_hvipriox(env, 0, env->hviprio, val); } @@ -5034,7 +5033,7 @@ static RISCVException read_hviprio1h(CPURISCVState *env, int csrno, } static RISCVException write_hviprio1h(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { return write_hvipriox(env, 4, env->hviprio, val); } @@ -5046,7 +5045,7 @@ static RISCVException read_hviprio2(CPURISCVState *env, int csrno, } static RISCVException write_hviprio2(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { return write_hvipriox(env, 8, env->hviprio, val); } @@ -5058,7 +5057,7 @@ static RISCVException read_hviprio2h(CPURISCVState *env, int csrno, } static RISCVException write_hviprio2h(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { return write_hvipriox(env, 12, env->hviprio, val); } @@ -5072,7 +5071,7 @@ static RISCVException read_vsstatus(CPURISCVState *env, int csrno, } static RISCVException write_vsstatus(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint64_t mask = (target_ulong)-1; if ((val & VSSTATUS64_UXL) == 0) { @@ -5097,7 +5096,7 @@ static RISCVException read_vstvec(CPURISCVState *env, int csrno, } static RISCVException write_vstvec(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { /* bits [1:0] encode mode; 0 = direct, 1 = vectored, 2 >= reserved */ if ((val & 3) < 2) { @@ -5116,7 +5115,7 @@ static RISCVException read_vsscratch(CPURISCVState *env, int csrno, } static RISCVException write_vsscratch(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->vsscratch = val; return RISCV_EXCP_NONE; @@ -5130,7 +5129,7 @@ static RISCVException read_vsepc(CPURISCVState *env, int csrno, } static RISCVException write_vsepc(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->vsepc = val; return RISCV_EXCP_NONE; @@ -5144,7 +5143,7 @@ static RISCVException read_vscause(CPURISCVState *env, int csrno, } static RISCVException write_vscause(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->vscause = val; return RISCV_EXCP_NONE; @@ -5158,7 +5157,7 @@ static RISCVException read_vstval(CPURISCVState *env, int csrno, } static RISCVException write_vstval(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->vstval = val; return RISCV_EXCP_NONE; @@ -5172,7 +5171,7 @@ static RISCVException read_vsatp(CPURISCVState *env, int csrno, } static RISCVException write_vsatp(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->vsatp = legalize_xatp(env, env->vsatp, val); return RISCV_EXCP_NONE; @@ -5186,7 +5185,7 @@ static RISCVException read_mtval2(CPURISCVState *env, int csrno, } static RISCVException write_mtval2(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->mtval2 = val; return RISCV_EXCP_NONE; @@ -5200,7 +5199,7 @@ static RISCVException read_mtinst(CPURISCVState *env, int csrno, } static RISCVException write_mtinst(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->mtinst = val; return RISCV_EXCP_NONE; @@ -5215,7 +5214,7 @@ static RISCVException read_mseccfg(CPURISCVState *env, int csrno, } static RISCVException write_mseccfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { mseccfg_csr_write(env, val); return RISCV_EXCP_NONE; @@ -5231,7 +5230,7 @@ static RISCVException read_pmpcfg(CPURISCVState *env, int csrno, } static RISCVException write_pmpcfg(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { uint32_t reg_index = csrno - CSR_PMPCFG0; @@ -5247,7 +5246,7 @@ static RISCVException read_pmpaddr(CPURISCVState *env, int csrno, } static RISCVException write_pmpaddr(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { pmpaddr_csr_write(env, csrno - CSR_PMPADDR0, val); return RISCV_EXCP_NONE; @@ -5261,7 +5260,7 @@ static RISCVException read_tselect(CPURISCVState *env, int csrno, } static RISCVException write_tselect(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { tselect_csr_write(env, val); return RISCV_EXCP_NONE; @@ -5285,7 +5284,7 @@ static RISCVException read_tdata(CPURISCVState *env, int csrno, } static RISCVException write_tdata(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { if (!tdata_available(env, csrno - CSR_TDATA1)) { return RISCV_EXCP_ILLEGAL_INST; @@ -5310,7 +5309,7 @@ static RISCVException read_mcontext(CPURISCVState *env, int csrno, } static RISCVException write_mcontext(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { bool rv32 = riscv_cpu_mxl(env) == MXL_RV32 ? true : false; int32_t mask; @@ -5334,43 +5333,50 @@ static RISCVException read_mnscratch(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static int write_mnscratch(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_mnscratch(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { env->mnscratch = val; return RISCV_EXCP_NONE; } -static int read_mnepc(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_mnepc(CPURISCVState *env, int csrno, + target_ulong *val) { *val = env->mnepc; return RISCV_EXCP_NONE; } -static int write_mnepc(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_mnepc(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { env->mnepc = val; return RISCV_EXCP_NONE; } -static int read_mncause(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_mncause(CPURISCVState *env, int csrno, + target_ulong *val) { *val = env->mncause; return RISCV_EXCP_NONE; } -static int write_mncause(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_mncause(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { env->mncause = val; return RISCV_EXCP_NONE; } -static int read_mnstatus(CPURISCVState *env, int csrno, target_ulong *val) +static RISCVException read_mnstatus(CPURISCVState *env, int csrno, + target_ulong *val) { *val = env->mnstatus; return RISCV_EXCP_NONE; } -static int write_mnstatus(CPURISCVState *env, int csrno, target_ulong val) +static RISCVException write_mnstatus(CPURISCVState *env, int csrno, + target_ulong val, uintptr_t ra) { target_ulong mask = (MNSTATUS_NMIE | MNSTATUS_MNPP); @@ -5540,7 +5546,7 @@ static RISCVException riscv_csrrw_do64(CPURISCVState *env, int csrno, if (write_mask) { new_value = (old_value & ~write_mask) | (new_value & write_mask); if (csr_ops[csrno].write) { - ret = csr_ops[csrno].write(env, csrno, new_value); + ret = csr_ops[csrno].write(env, csrno, new_value, 0); if (ret != RISCV_EXCP_NONE) { return ret; } @@ -5603,7 +5609,7 @@ static RISCVException riscv_csrrw_do128(CPURISCVState *env, int csrno, } } else if (csr_ops[csrno].write) { /* avoids having to write wrappers for all registers */ - ret = csr_ops[csrno].write(env, csrno, int128_getlo(new_value)); + ret = csr_ops[csrno].write(env, csrno, int128_getlo(new_value), 0); if (ret != RISCV_EXCP_NONE) { return ret; } @@ -5714,7 +5720,7 @@ static RISCVException read_jvt(CPURISCVState *env, int csrno, } static RISCVException write_jvt(CPURISCVState *env, int csrno, - target_ulong val) + target_ulong val, uintptr_t ra) { env->jvt = val; return RISCV_EXCP_NONE; From bfc7936f42bac551bd859b8f32fb1f24dfcfc611 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 25 Apr 2025 08:23:06 -0700 Subject: [PATCH 0994/2760] target/riscv: Pass ra to riscv_csrrw_do64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250425152311.804338-3-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index ba7620ef3d..22149bd3fc 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -5516,7 +5516,8 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, static RISCVException riscv_csrrw_do64(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, - target_ulong write_mask) + target_ulong write_mask, + uintptr_t ra) { RISCVException ret; target_ulong old_value = 0; @@ -5546,7 +5547,7 @@ static RISCVException riscv_csrrw_do64(CPURISCVState *env, int csrno, if (write_mask) { new_value = (old_value & ~write_mask) | (new_value & write_mask); if (csr_ops[csrno].write) { - ret = csr_ops[csrno].write(env, csrno, new_value, 0); + ret = csr_ops[csrno].write(env, csrno, new_value, ra); if (ret != RISCV_EXCP_NONE) { return ret; } @@ -5569,7 +5570,7 @@ RISCVException riscv_csrr(CPURISCVState *env, int csrno, return ret; } - return riscv_csrrw_do64(env, csrno, ret_value, 0, 0); + return riscv_csrrw_do64(env, csrno, ret_value, 0, 0, 0); } RISCVException riscv_csrrw(CPURISCVState *env, int csrno, @@ -5581,7 +5582,7 @@ RISCVException riscv_csrrw(CPURISCVState *env, int csrno, return ret; } - return riscv_csrrw_do64(env, csrno, ret_value, new_value, write_mask); + return riscv_csrrw_do64(env, csrno, ret_value, new_value, write_mask, 0); } static RISCVException riscv_csrrw_do128(CPURISCVState *env, int csrno, @@ -5647,9 +5648,7 @@ RISCVException riscv_csrr_i128(CPURISCVState *env, int csrno, * accesses */ target_ulong old_value; - ret = riscv_csrrw_do64(env, csrno, &old_value, - (target_ulong)0, - (target_ulong)0); + ret = riscv_csrrw_do64(env, csrno, &old_value, 0, 0, 0); if (ret == RISCV_EXCP_NONE && ret_value) { *ret_value = int128_make64(old_value); } @@ -5681,7 +5680,7 @@ RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, target_ulong old_value; ret = riscv_csrrw_do64(env, csrno, &old_value, int128_getlo(new_value), - int128_getlo(write_mask)); + int128_getlo(write_mask), 0); if (ret == RISCV_EXCP_NONE && ret_value) { *ret_value = int128_make64(old_value); } From c26c4afd0ffde4f79216975ac34f419d0fcf6795 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 25 Apr 2025 08:23:07 -0700 Subject: [PATCH 0995/2760] target/riscv: Pass ra to riscv_csrrw_do128 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250425152311.804338-4-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 22149bd3fc..8af0304a36 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -5588,7 +5588,7 @@ RISCVException riscv_csrrw(CPURISCVState *env, int csrno, static RISCVException riscv_csrrw_do128(CPURISCVState *env, int csrno, Int128 *ret_value, Int128 new_value, - Int128 write_mask) + Int128 write_mask, uintptr_t ra) { RISCVException ret; Int128 old_value; @@ -5610,7 +5610,7 @@ static RISCVException riscv_csrrw_do128(CPURISCVState *env, int csrno, } } else if (csr_ops[csrno].write) { /* avoids having to write wrappers for all registers */ - ret = csr_ops[csrno].write(env, csrno, int128_getlo(new_value), 0); + ret = csr_ops[csrno].write(env, csrno, int128_getlo(new_value), ra); if (ret != RISCV_EXCP_NONE) { return ret; } @@ -5637,7 +5637,7 @@ RISCVException riscv_csrr_i128(CPURISCVState *env, int csrno, if (csr_ops[csrno].read128) { return riscv_csrrw_do128(env, csrno, ret_value, - int128_zero(), int128_zero()); + int128_zero(), int128_zero(), 0); } /* @@ -5667,7 +5667,8 @@ RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, } if (csr_ops[csrno].read128) { - return riscv_csrrw_do128(env, csrno, ret_value, new_value, write_mask); + return riscv_csrrw_do128(env, csrno, ret_value, + new_value, write_mask, 0); } /* From f1304836ea9399253c67b09513fca30f9f4b223e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 25 Apr 2025 08:23:08 -0700 Subject: [PATCH 0996/2760] target/riscv: Pass ra to riscv_csrrw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250425152311.804338-5-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- hw/riscv/riscv_hart.c | 2 +- target/riscv/cpu.h | 8 ++++---- target/riscv/csr.c | 8 ++++---- target/riscv/op_helper.c | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/hw/riscv/riscv_hart.c b/hw/riscv/riscv_hart.c index 333083a4f1..7f2676008c 100644 --- a/hw/riscv/riscv_hart.c +++ b/hw/riscv/riscv_hart.c @@ -72,7 +72,7 @@ static void csr_call(char *cmd, uint64_t cpu_num, int csrno, uint64_t *val) ret = riscv_csrr(env, csrno, (target_ulong *)val); } else if (strcmp(cmd, "set_csr") == 0) { ret = riscv_csrrw(env, csrno, NULL, *(target_ulong *)val, - MAKE_64BIT_MASK(0, TARGET_LONG_BITS)); + MAKE_64BIT_MASK(0, TARGET_LONG_BITS), 0); } g_assert(ret == RISCV_EXCP_NONE); diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 4265ce06ee..f674e93a4f 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -813,8 +813,8 @@ RISCVException riscv_csrr(CPURISCVState *env, int csrno, target_ulong *ret_value); RISCVException riscv_csrrw(CPURISCVState *env, int csrno, - target_ulong *ret_value, - target_ulong new_value, target_ulong write_mask); + target_ulong *ret_value, target_ulong new_value, + target_ulong write_mask, uintptr_t ra); RISCVException riscv_csrrw_debug(CPURISCVState *env, int csrno, target_ulong *ret_value, target_ulong new_value, @@ -823,13 +823,13 @@ RISCVException riscv_csrrw_debug(CPURISCVState *env, int csrno, static inline void riscv_csr_write(CPURISCVState *env, int csrno, target_ulong val) { - riscv_csrrw(env, csrno, NULL, val, MAKE_64BIT_MASK(0, TARGET_LONG_BITS)); + riscv_csrrw(env, csrno, NULL, val, MAKE_64BIT_MASK(0, TARGET_LONG_BITS), 0); } static inline target_ulong riscv_csr_read(CPURISCVState *env, int csrno) { target_ulong val = 0; - riscv_csrrw(env, csrno, &val, 0, 0); + riscv_csrrw(env, csrno, &val, 0, 0, 0); return val; } diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 8af0304a36..807a891e7d 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -5574,15 +5574,15 @@ RISCVException riscv_csrr(CPURISCVState *env, int csrno, } RISCVException riscv_csrrw(CPURISCVState *env, int csrno, - target_ulong *ret_value, - target_ulong new_value, target_ulong write_mask) + target_ulong *ret_value, target_ulong new_value, + target_ulong write_mask, uintptr_t ra) { RISCVException ret = riscv_csrrw_check(env, csrno, true); if (ret != RISCV_EXCP_NONE) { return ret; } - return riscv_csrrw_do64(env, csrno, ret_value, new_value, write_mask, 0); + return riscv_csrrw_do64(env, csrno, ret_value, new_value, write_mask, ra); } static RISCVException riscv_csrrw_do128(CPURISCVState *env, int csrno, @@ -5704,7 +5704,7 @@ RISCVException riscv_csrrw_debug(CPURISCVState *env, int csrno, if (!write_mask) { ret = riscv_csrr(env, csrno, ret_value); } else { - ret = riscv_csrrw(env, csrno, ret_value, new_value, write_mask); + ret = riscv_csrrw(env, csrno, ret_value, new_value, write_mask, 0); } #if !defined(CONFIG_USER_ONLY) env->debugger = false; diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 05316f2088..0672101637 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -71,7 +71,7 @@ target_ulong helper_csrr(CPURISCVState *env, int csr) void helper_csrw(CPURISCVState *env, int csr, target_ulong src) { target_ulong mask = env->xl == MXL_RV32 ? UINT32_MAX : (target_ulong)-1; - RISCVException ret = riscv_csrrw(env, csr, NULL, src, mask); + RISCVException ret = riscv_csrrw(env, csr, NULL, src, mask, GETPC()); if (ret != RISCV_EXCP_NONE) { riscv_raise_exception(env, ret, GETPC()); @@ -82,7 +82,7 @@ target_ulong helper_csrrw(CPURISCVState *env, int csr, target_ulong src, target_ulong write_mask) { target_ulong val = 0; - RISCVException ret = riscv_csrrw(env, csr, &val, src, write_mask); + RISCVException ret = riscv_csrrw(env, csr, &val, src, write_mask, GETPC()); if (ret != RISCV_EXCP_NONE) { riscv_raise_exception(env, ret, GETPC()); From 9ef792a78db6b89619c3ccc77ceb3e9d6271dd02 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 25 Apr 2025 08:23:09 -0700 Subject: [PATCH 0997/2760] target/riscv: Pass ra to riscv_csrrw_i128 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250425152311.804338-6-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 4 ++-- target/riscv/csr.c | 8 ++++---- target/riscv/op_helper.c | 9 +++++---- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index f674e93a4f..ff7ba2a0a1 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -848,8 +848,8 @@ typedef RISCVException (*riscv_csr_op_fn)(CPURISCVState *env, int csrno, RISCVException riscv_csrr_i128(CPURISCVState *env, int csrno, Int128 *ret_value); RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, - Int128 *ret_value, - Int128 new_value, Int128 write_mask); + Int128 *ret_value, Int128 new_value, + Int128 write_mask, uintptr_t ra); typedef RISCVException (*riscv_csr_read128_fn)(CPURISCVState *env, int csrno, Int128 *ret_value); diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 807a891e7d..53458491da 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -5656,8 +5656,8 @@ RISCVException riscv_csrr_i128(CPURISCVState *env, int csrno, } RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, - Int128 *ret_value, - Int128 new_value, Int128 write_mask) + Int128 *ret_value, Int128 new_value, + Int128 write_mask, uintptr_t ra) { RISCVException ret; @@ -5668,7 +5668,7 @@ RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, if (csr_ops[csrno].read128) { return riscv_csrrw_do128(env, csrno, ret_value, - new_value, write_mask, 0); + new_value, write_mask, ra); } /* @@ -5681,7 +5681,7 @@ RISCVException riscv_csrrw_i128(CPURISCVState *env, int csrno, target_ulong old_value; ret = riscv_csrrw_do64(env, csrno, &old_value, int128_getlo(new_value), - int128_getlo(write_mask), 0); + int128_getlo(write_mask), ra); if (ret == RISCV_EXCP_NONE && ret_value) { *ret_value = int128_make64(old_value); } diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 0672101637..557807ba4b 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -108,7 +108,7 @@ void helper_csrw_i128(CPURISCVState *env, int csr, { RISCVException ret = riscv_csrrw_i128(env, csr, NULL, int128_make128(srcl, srch), - UINT128_MAX); + UINT128_MAX, GETPC()); if (ret != RISCV_EXCP_NONE) { riscv_raise_exception(env, ret, GETPC()); @@ -116,13 +116,14 @@ void helper_csrw_i128(CPURISCVState *env, int csr, } target_ulong helper_csrrw_i128(CPURISCVState *env, int csr, - target_ulong srcl, target_ulong srch, - target_ulong maskl, target_ulong maskh) + target_ulong srcl, target_ulong srch, + target_ulong maskl, target_ulong maskh) { Int128 rv = int128_zero(); RISCVException ret = riscv_csrrw_i128(env, csr, &rv, int128_make128(srcl, srch), - int128_make128(maskl, maskh)); + int128_make128(maskl, maskh), + GETPC()); if (ret != RISCV_EXCP_NONE) { riscv_raise_exception(env, ret, GETPC()); From dd9953a5541441d283d0e5c53dffaf3938d6e097 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 25 Apr 2025 08:23:10 -0700 Subject: [PATCH 0998/2760] target/riscv: Move insn_len to internals.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250425152311.804338-7-richard.henderson@linaro.org> Signed-off-by: Alistair Francis --- target/riscv/internals.h | 5 +++++ target/riscv/translate.c | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/target/riscv/internals.h b/target/riscv/internals.h index 213aff31d8..4570bd50be 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -201,4 +201,9 @@ static inline target_ulong adjust_addr_virt(CPURISCVState *env, return adjust_addr_body(env, addr, true); } +static inline int insn_len(uint16_t first_word) +{ + return (first_word & 3) == 3 ? 4 : 2; +} + #endif diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 85128f997b..0d4f7d601c 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1209,11 +1209,6 @@ static uint32_t opcode_at(DisasContextBase *dcbase, target_ulong pc) /* The specification allows for longer insns, but not supported by qemu. */ #define MAX_INSN_LEN 4 -static inline int insn_len(uint16_t first_word) -{ - return (first_word & 3) == 3 ? 4 : 2; -} - const RISCVDecoder decoder_table[] = { { always_true_p, decode_insn32 }, { has_xthead_p, decode_xthead}, From 7b069906b6ac47dc905d187c72f07ef82b400501 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 25 Apr 2025 08:23:11 -0700 Subject: [PATCH 0999/2760] target/riscv: Fix write_misa vs aligned next_pc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not examine a random host return address, but properly compute the next pc for the guest cpu. Fixes: f18637cd611 ("RISC-V: Add misa runtime write support") Signed-off-by: Richard Henderson Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250425152311.804338-8-richard.henderson@linaro.org> [ Changes by AF: - Change `& ~3` to `& 3` ] Signed-off-by: Alistair Francis --- target/riscv/csr.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 53458491da..288edeedea 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -30,6 +30,8 @@ #include "accel/tcg/getpc.h" #include "qemu/guest-random.h" #include "qapi/error.h" +#include "tcg/insn-start-words.h" +#include "internals.h" #include /* CSR function table public API */ @@ -2099,6 +2101,19 @@ static RISCVException read_misa(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } +static target_ulong get_next_pc(CPURISCVState *env, uintptr_t ra) +{ + uint64_t data[INSN_START_WORDS]; + + /* Outside of a running cpu, env contains the next pc. */ + if (ra == 0 || !cpu_unwind_state_data(env_cpu(env), ra, data)) { + return env->pc; + } + + /* Within unwind data, [0] is pc and [1] is the opcode. */ + return data[0] + insn_len(data[1]); +} + static RISCVException write_misa(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { @@ -2114,11 +2129,8 @@ static RISCVException write_misa(CPURISCVState *env, int csrno, /* Mask extensions that are not supported by this hart */ val &= env->misa_ext_mask; - /* - * Suppress 'C' if next instruction is not aligned - * TODO: this should check next_pc - */ - if ((val & RVC) && (GETPC() & ~3) != 0) { + /* Suppress 'C' if next instruction is not aligned. */ + if ((val & RVC) && (get_next_pc(env, ra) & 3) != 0) { val &= ~RVC; } From 73f81da0a3628180409a0ae90ece19534bcdf09b Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:44:13 -0300 Subject: [PATCH 1000/2760] target/riscv/kvm: minor fixes/tweaks Remove an unused 'KVMScratchCPU' pointer argument in kvm_riscv_check_sbi_dbcn_support(). Put kvm_riscv_reset_regs_csr() after kvm_riscv_put_regs_csr(). This will make a future patch diff easier to read, when changes in kvm_riscv_reset_regs_csr() and kvm_riscv_get_regs_csr() will be made. Fixes: a6b53378f5 ("target/riscv/kvm: implement SBI debug console (DBCN) calls") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250429124421.223883-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/kvm/kvm-cpu.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 75724b6af4..cad54b720a 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -613,19 +613,6 @@ static int kvm_riscv_put_regs_core(CPUState *cs) return ret; } -static void kvm_riscv_reset_regs_csr(CPURISCVState *env) -{ - env->mstatus = 0; - env->mie = 0; - env->stvec = 0; - env->sscratch = 0; - env->sepc = 0; - env->scause = 0; - env->stval = 0; - env->mip = 0; - env->satp = 0; -} - static int kvm_riscv_get_regs_csr(CPUState *cs) { CPURISCVState *env = &RISCV_CPU(cs)->env; @@ -660,6 +647,19 @@ static int kvm_riscv_put_regs_csr(CPUState *cs) return 0; } +static void kvm_riscv_reset_regs_csr(CPURISCVState *env) +{ + env->mstatus = 0; + env->mie = 0; + env->stvec = 0; + env->sscratch = 0; + env->sepc = 0; + env->scause = 0; + env->stval = 0; + env->mip = 0; + env->satp = 0; +} + static int kvm_riscv_get_regs_fp(CPUState *cs) { int ret = 0; @@ -1078,7 +1078,6 @@ static int uint64_cmp(const void *a, const void *b) } static void kvm_riscv_check_sbi_dbcn_support(RISCVCPU *cpu, - KVMScratchCPU *kvmcpu, struct kvm_reg_list *reglist) { struct kvm_reg_list *reg_search; @@ -1197,7 +1196,7 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) kvm_riscv_read_vlenb(cpu, kvmcpu, reglist); } - kvm_riscv_check_sbi_dbcn_support(cpu, kvmcpu, reglist); + kvm_riscv_check_sbi_dbcn_support(cpu, reglist); } static void riscv_init_kvm_registers(Object *cpu_obj) From 906af6de9462c5192547cca0beac2c134659a437 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:44:14 -0300 Subject: [PATCH 1001/2760] target/riscv/kvm: fix leak in kvm_riscv_init_multiext_cfg() 'reglist' is being g-malloc'ed but never freed. Reported-by: Andrew Jones Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250429124421.223883-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/kvm/kvm-cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index cad54b720a..86eb3c2e3b 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1119,10 +1119,10 @@ static void kvm_riscv_read_vlenb(RISCVCPU *cpu, KVMScratchCPU *kvmcpu, static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) { + g_autofree struct kvm_reg_list *reglist = NULL; KVMCPUConfig *multi_ext_cfg; struct kvm_one_reg reg; struct kvm_reg_list rl_struct; - struct kvm_reg_list *reglist; uint64_t val, reg_id, *reg_search; int i, ret; From b6096103494506514d9bfa442f62fef36ffc8fba Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:44:15 -0300 Subject: [PATCH 1002/2760] target/riscv/kvm: turn u32/u64 reg functions into macros This change is motivated by a future change w.r.t CSRs management. We want to handle them the same way as KVM extensions, i.e. a static array with KVMCPUConfig objs that will be read/write during init and so on. But to do that properly we must be able to declare a static array that hold KVM regs. C does not allow to init static arrays and use functions as initializers, e.g. we can't do: .kvm_reg_id = kvm_riscv_reg_id_ulong(...) When instantiating the array. We can do that with macros though, so our goal is turn kvm_riscv_reg_ulong() in a macro. It is cleaner to turn every other reg_id_*() function in macros, and ulong will end up using the macros for u32 and u64, so we'll start with them. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250429124421.223883-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/kvm/kvm-cpu.c | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 86eb3c2e3b..b037cb2781 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -58,6 +58,12 @@ void riscv_kvm_aplic_request(void *opaque, int irq, int level) static bool cap_has_mp_state; +#define KVM_RISCV_REG_ID_U32(type, idx) (KVM_REG_RISCV | KVM_REG_SIZE_U32 | \ + type | idx) + +#define KVM_RISCV_REG_ID_U64(type, idx) (KVM_REG_RISCV | KVM_REG_SIZE_U64 | \ + type | idx) + static uint64_t kvm_riscv_reg_id_ulong(CPURISCVState *env, uint64_t type, uint64_t idx) { @@ -76,16 +82,6 @@ static uint64_t kvm_riscv_reg_id_ulong(CPURISCVState *env, uint64_t type, return id; } -static uint64_t kvm_riscv_reg_id_u32(uint64_t type, uint64_t idx) -{ - return KVM_REG_RISCV | KVM_REG_SIZE_U32 | type | idx; -} - -static uint64_t kvm_riscv_reg_id_u64(uint64_t type, uint64_t idx) -{ - return KVM_REG_RISCV | KVM_REG_SIZE_U64 | type | idx; -} - static uint64_t kvm_encode_reg_size_id(uint64_t id, size_t size_b) { uint64_t size_ctz = __builtin_ctz(size_b); @@ -119,12 +115,12 @@ static uint64_t kvm_riscv_vector_reg_id(RISCVCPU *cpu, kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CONFIG, \ KVM_REG_RISCV_CONFIG_REG(name)) -#define RISCV_TIMER_REG(name) kvm_riscv_reg_id_u64(KVM_REG_RISCV_TIMER, \ +#define RISCV_TIMER_REG(name) KVM_RISCV_REG_ID_U64(KVM_REG_RISCV_TIMER, \ KVM_REG_RISCV_TIMER_REG(name)) -#define RISCV_FP_F_REG(idx) kvm_riscv_reg_id_u32(KVM_REG_RISCV_FP_F, idx) +#define RISCV_FP_F_REG(idx) KVM_RISCV_REG_ID_U32(KVM_REG_RISCV_FP_F, idx) -#define RISCV_FP_D_REG(idx) kvm_riscv_reg_id_u64(KVM_REG_RISCV_FP_D, idx) +#define RISCV_FP_D_REG(idx) KVM_RISCV_REG_ID_U64(KVM_REG_RISCV_FP_D, idx) #define RISCV_VECTOR_CSR_REG(env, name) \ kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_VECTOR, \ From 11766e17616a5a4181d4a63f88adf67ac52c553b Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:44:16 -0300 Subject: [PATCH 1003/2760] target/riscv/kvm: turn kvm_riscv_reg_id_ulong() into a macro We need the reg_id_ulong() helper to be a macro to be able to create a static array of KVMCPUConfig that will hold CSR information. Despite the amount of changes all of them are tedious/trivial: - replace instances of "kvm_riscv_reg_id_ulong" with "KVM_RISCV_REG_ID_ULONG"; - RISCV_CORE_REG(), RISCV_CSR_REG(), RISCV_CONFIG_REG() and RISCV_VECTOR_CSR_REG() only receives one 'name' arg. Remove unneeded 'env' variables when applicable. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250429124421.223883-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/kvm/kvm-cpu.c | 99 ++++++++++++++++---------------------- 1 file changed, 41 insertions(+), 58 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index b037cb2781..509c875a19 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -64,23 +64,11 @@ static bool cap_has_mp_state; #define KVM_RISCV_REG_ID_U64(type, idx) (KVM_REG_RISCV | KVM_REG_SIZE_U64 | \ type | idx) -static uint64_t kvm_riscv_reg_id_ulong(CPURISCVState *env, uint64_t type, - uint64_t idx) -{ - uint64_t id = KVM_REG_RISCV | type | idx; - - switch (riscv_cpu_mxl(env)) { - case MXL_RV32: - id |= KVM_REG_SIZE_U32; - break; - case MXL_RV64: - id |= KVM_REG_SIZE_U64; - break; - default: - g_assert_not_reached(); - } - return id; -} +#if defined(TARGET_RISCV64) +#define KVM_RISCV_REG_ID_ULONG(type, idx) KVM_RISCV_REG_ID_U64(type, idx) +#else +#define KVM_RISCV_REG_ID_ULONG(type, idx) KVM_RISCV_REG_ID_U32(type, idx) +#endif static uint64_t kvm_encode_reg_size_id(uint64_t id, size_t size_b) { @@ -103,16 +91,16 @@ static uint64_t kvm_riscv_vector_reg_id(RISCVCPU *cpu, return kvm_encode_reg_size_id(id, size_b); } -#define RISCV_CORE_REG(env, name) \ - kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CORE, \ +#define RISCV_CORE_REG(name) \ + KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CORE, \ KVM_REG_RISCV_CORE_REG(name)) -#define RISCV_CSR_REG(env, name) \ - kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CSR, \ +#define RISCV_CSR_REG(name) \ + KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CSR, \ KVM_REG_RISCV_CSR_REG(name)) -#define RISCV_CONFIG_REG(env, name) \ - kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CONFIG, \ +#define RISCV_CONFIG_REG(name) \ + KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CONFIG, \ KVM_REG_RISCV_CONFIG_REG(name)) #define RISCV_TIMER_REG(name) KVM_RISCV_REG_ID_U64(KVM_REG_RISCV_TIMER, \ @@ -122,13 +110,13 @@ static uint64_t kvm_riscv_vector_reg_id(RISCVCPU *cpu, #define RISCV_FP_D_REG(idx) KVM_RISCV_REG_ID_U64(KVM_REG_RISCV_FP_D, idx) -#define RISCV_VECTOR_CSR_REG(env, name) \ - kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_VECTOR, \ +#define RISCV_VECTOR_CSR_REG(name) \ + KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_VECTOR, \ KVM_REG_RISCV_VECTOR_CSR_REG(name)) #define KVM_RISCV_GET_CSR(cs, env, csr, reg) \ do { \ - int _ret = kvm_get_one_reg(cs, RISCV_CSR_REG(env, csr), ®); \ + int _ret = kvm_get_one_reg(cs, RISCV_CSR_REG(csr), ®); \ if (_ret) { \ return _ret; \ } \ @@ -136,7 +124,7 @@ static uint64_t kvm_riscv_vector_reg_id(RISCVCPU *cpu, #define KVM_RISCV_SET_CSR(cs, env, csr, reg) \ do { \ - int _ret = kvm_set_one_reg(cs, RISCV_CSR_REG(env, csr), ®); \ + int _ret = kvm_set_one_reg(cs, RISCV_CSR_REG(csr), ®); \ if (_ret) { \ return _ret; \ } \ @@ -244,7 +232,7 @@ static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs) /* If we're here we're going to disable the MISA bit */ reg = 0; - id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_ISA_EXT, + id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_ISA_EXT, misa_cfg->kvm_reg_id); ret = kvm_set_one_reg(cs, id, ®); if (ret != 0) { @@ -430,7 +418,6 @@ static KVMCPUConfig kvm_sbi_dbcn = { static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs) { - CPURISCVState *env = &cpu->env; uint64_t id, reg; int i, ret; @@ -441,7 +428,7 @@ static void kvm_riscv_update_cpu_cfg_isa_ext(RISCVCPU *cpu, CPUState *cs) continue; } - id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_ISA_EXT, + id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_ISA_EXT, multi_ext_cfg->kvm_reg_id); reg = kvm_cpu_cfg_get(cpu, multi_ext_cfg); ret = kvm_set_one_reg(cs, id, ®); @@ -566,14 +553,14 @@ static int kvm_riscv_get_regs_core(CPUState *cs) target_ulong reg; CPURISCVState *env = &RISCV_CPU(cs)->env; - ret = kvm_get_one_reg(cs, RISCV_CORE_REG(env, regs.pc), ®); + ret = kvm_get_one_reg(cs, RISCV_CORE_REG(regs.pc), ®); if (ret) { return ret; } env->pc = reg; for (i = 1; i < 32; i++) { - uint64_t id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CORE, i); + uint64_t id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CORE, i); ret = kvm_get_one_reg(cs, id, ®); if (ret) { return ret; @@ -592,13 +579,13 @@ static int kvm_riscv_put_regs_core(CPUState *cs) CPURISCVState *env = &RISCV_CPU(cs)->env; reg = env->pc; - ret = kvm_set_one_reg(cs, RISCV_CORE_REG(env, regs.pc), ®); + ret = kvm_set_one_reg(cs, RISCV_CORE_REG(regs.pc), ®); if (ret) { return ret; } for (i = 1; i < 32; i++) { - uint64_t id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CORE, i); + uint64_t id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CORE, i); reg = env->gpr[i]; ret = kvm_set_one_reg(cs, id, ®); if (ret) { @@ -796,26 +783,26 @@ static int kvm_riscv_get_regs_vector(CPUState *cs) return 0; } - ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vstart), ®); + ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(vstart), ®); if (ret) { return ret; } env->vstart = reg; - ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vl), ®); + ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(vl), ®); if (ret) { return ret; } env->vl = reg; - ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vtype), ®); + ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(vtype), ®); if (ret) { return ret; } env->vtype = reg; if (kvm_v_vlenb.supported) { - ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vlenb), ®); + ret = kvm_get_one_reg(cs, RISCV_VECTOR_CSR_REG(vlenb), ®); if (ret) { return ret; } @@ -853,26 +840,26 @@ static int kvm_riscv_put_regs_vector(CPUState *cs) } reg = env->vstart; - ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vstart), ®); + ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(vstart), ®); if (ret) { return ret; } reg = env->vl; - ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vl), ®); + ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(vl), ®); if (ret) { return ret; } reg = env->vtype; - ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vtype), ®); + ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(vtype), ®); if (ret) { return ret; } if (kvm_v_vlenb.supported) { reg = cpu->cfg.vlenb; - ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(env, vlenb), ®); + ret = kvm_set_one_reg(cs, RISCV_VECTOR_CSR_REG(vlenb), ®); for (int i = 0; i < 32; i++) { /* @@ -951,25 +938,24 @@ static void kvm_riscv_destroy_scratch_vcpu(KVMScratchCPU *scratch) static void kvm_riscv_init_machine_ids(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) { - CPURISCVState *env = &cpu->env; struct kvm_one_reg reg; int ret; - reg.id = RISCV_CONFIG_REG(env, mvendorid); + reg.id = RISCV_CONFIG_REG(mvendorid); reg.addr = (uint64_t)&cpu->cfg.mvendorid; ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); if (ret != 0) { error_report("Unable to retrieve mvendorid from host, error %d", ret); } - reg.id = RISCV_CONFIG_REG(env, marchid); + reg.id = RISCV_CONFIG_REG(marchid); reg.addr = (uint64_t)&cpu->cfg.marchid; ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); if (ret != 0) { error_report("Unable to retrieve marchid from host, error %d", ret); } - reg.id = RISCV_CONFIG_REG(env, mimpid); + reg.id = RISCV_CONFIG_REG(mimpid); reg.addr = (uint64_t)&cpu->cfg.mimpid; ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); if (ret != 0) { @@ -984,7 +970,7 @@ static void kvm_riscv_init_misa_ext_mask(RISCVCPU *cpu, struct kvm_one_reg reg; int ret; - reg.id = RISCV_CONFIG_REG(env, isa); + reg.id = RISCV_CONFIG_REG(isa); reg.addr = (uint64_t)&env->misa_ext_mask; ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); @@ -1001,11 +987,10 @@ static void kvm_riscv_init_misa_ext_mask(RISCVCPU *cpu, static void kvm_riscv_read_cbomz_blksize(RISCVCPU *cpu, KVMScratchCPU *kvmcpu, KVMCPUConfig *cbomz_cfg) { - CPURISCVState *env = &cpu->env; struct kvm_one_reg reg; int ret; - reg.id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CONFIG, + reg.id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CONFIG, cbomz_cfg->kvm_reg_id); reg.addr = (uint64_t)kvmconfig_get_cfg_addr(cpu, cbomz_cfg); ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); @@ -1019,7 +1004,6 @@ static void kvm_riscv_read_cbomz_blksize(RISCVCPU *cpu, KVMScratchCPU *kvmcpu, static void kvm_riscv_read_multiext_legacy(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) { - CPURISCVState *env = &cpu->env; uint64_t val; int i, ret; @@ -1027,7 +1011,7 @@ static void kvm_riscv_read_multiext_legacy(RISCVCPU *cpu, KVMCPUConfig *multi_ext_cfg = &kvm_multi_ext_cfgs[i]; struct kvm_one_reg reg; - reg.id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_ISA_EXT, + reg.id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_ISA_EXT, multi_ext_cfg->kvm_reg_id); reg.addr = (uint64_t)&val; ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); @@ -1159,7 +1143,7 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) for (i = 0; i < ARRAY_SIZE(kvm_multi_ext_cfgs); i++) { multi_ext_cfg = &kvm_multi_ext_cfgs[i]; - reg_id = kvm_riscv_reg_id_ulong(&cpu->env, KVM_REG_RISCV_ISA_EXT, + reg_id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_ISA_EXT, multi_ext_cfg->kvm_reg_id); reg_search = bsearch(®_id, reglist->reg, reglist->n, sizeof(uint64_t), uint64_cmp); @@ -1338,12 +1322,11 @@ void kvm_arch_init_irq_routing(KVMState *s) static int kvm_vcpu_set_machine_ids(RISCVCPU *cpu, CPUState *cs) { - CPURISCVState *env = &cpu->env; target_ulong reg; uint64_t id; int ret; - id = RISCV_CONFIG_REG(env, mvendorid); + id = RISCV_CONFIG_REG(mvendorid); /* * cfg.mvendorid is an uint32 but a target_ulong will * be written. Assign it to a target_ulong var to avoid @@ -1355,13 +1338,13 @@ static int kvm_vcpu_set_machine_ids(RISCVCPU *cpu, CPUState *cs) return ret; } - id = RISCV_CONFIG_REG(env, marchid); + id = RISCV_CONFIG_REG(marchid); ret = kvm_set_one_reg(cs, id, &cpu->cfg.marchid); if (ret != 0) { return ret; } - id = RISCV_CONFIG_REG(env, mimpid); + id = RISCV_CONFIG_REG(mimpid); ret = kvm_set_one_reg(cs, id, &cpu->cfg.mimpid); return ret; @@ -1911,7 +1894,7 @@ void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp) if (cpu->cfg.ext_zicbom && riscv_cpu_option_set(kvm_cbom_blocksize.name)) { - reg.id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CONFIG, + reg.id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CONFIG, kvm_cbom_blocksize.kvm_reg_id); reg.addr = (uint64_t)&val; ret = ioctl(kvmcpu.cpufd, KVM_GET_ONE_REG, ®); @@ -1930,7 +1913,7 @@ void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp) if (cpu->cfg.ext_zicboz && riscv_cpu_option_set(kvm_cboz_blocksize.name)) { - reg.id = kvm_riscv_reg_id_ulong(env, KVM_REG_RISCV_CONFIG, + reg.id = KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_CONFIG, kvm_cboz_blocksize.kvm_reg_id); reg.addr = (uint64_t)&val; ret = ioctl(kvmcpu.cpufd, KVM_GET_ONE_REG, ®); From d3b6f1742c36e3a3c1e74cb60646ee98a4e39ea3 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:44:17 -0300 Subject: [PATCH 1004/2760] target/riscv/kvm: add kvm_csr_cfgs[] At this moment we're not checking if the host has support for any specific CSR before doing get/put regs. This will cause problems if the host KVM doesn't support it (see [1] as an example). We'll use the same approach done with the CPU extensions: read all known KVM CSRs during init() to check for availability, then read/write them if they are present. This will be made by either using get-reglist or by directly reading the CSRs. For now we'll just convert the CSRs to use a kvm_csr_cfg[] array, reusing the same KVMCPUConfig abstraction we use for extensions, and use the array in (get|put)_csr_regs() instead of manually listing them. A lot of boilerplate will be added but at least we'll automate the get/put procedure for CSRs, i.e. adding a new CSR in the future will be a matter of adding it in kvm_csr_regs[] and everything else will be taken care of. Despite all the code changes no behavioral change is made. [1] https://lore.kernel.org/qemu-riscv/CABJz62OfUDHYkQ0T3rGHStQprf1c7_E0qBLbLKhfv=+jb0SYAw@mail.gmail.com/ Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-ID: <20250429124421.223883-6-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/cpu.h | 1 + target/riscv/kvm/kvm-cpu.c | 121 ++++++++++++++++++++++++++----------- 2 files changed, 86 insertions(+), 36 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index ff7ba2a0a1..b56d3afa69 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -75,6 +75,7 @@ const char *riscv_get_misa_ext_name(uint32_t bit); const char *riscv_get_misa_ext_description(uint32_t bit); #define CPU_CFG_OFFSET(_prop) offsetof(struct RISCVCPUConfig, _prop) +#define ENV_CSR_OFFSET(_csr) offsetof(CPURISCVState, _csr) typedef struct riscv_cpu_profile { struct riscv_cpu_profile *u_parent; diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 509c875a19..3740514bba 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -114,22 +114,6 @@ static uint64_t kvm_riscv_vector_reg_id(RISCVCPU *cpu, KVM_RISCV_REG_ID_ULONG(KVM_REG_RISCV_VECTOR, \ KVM_REG_RISCV_VECTOR_CSR_REG(name)) -#define KVM_RISCV_GET_CSR(cs, env, csr, reg) \ - do { \ - int _ret = kvm_get_one_reg(cs, RISCV_CSR_REG(csr), ®); \ - if (_ret) { \ - return _ret; \ - } \ - } while (0) - -#define KVM_RISCV_SET_CSR(cs, env, csr, reg) \ - do { \ - int _ret = kvm_set_one_reg(cs, RISCV_CSR_REG(csr), ®); \ - if (_ret) { \ - return _ret; \ - } \ - } while (0) - #define KVM_RISCV_GET_TIMER(cs, name, reg) \ do { \ int ret = kvm_get_one_reg(cs, RISCV_TIMER_REG(name), ®); \ @@ -251,6 +235,53 @@ static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs) } } +#define KVM_CSR_CFG(_name, _env_prop, reg_id) \ + {.name = _name, .offset = ENV_CSR_OFFSET(_env_prop), \ + .kvm_reg_id = reg_id} + +static KVMCPUConfig kvm_csr_cfgs[] = { + KVM_CSR_CFG("sstatus", mstatus, RISCV_CSR_REG(sstatus)), + KVM_CSR_CFG("sie", mie, RISCV_CSR_REG(sie)), + KVM_CSR_CFG("stvec", stvec, RISCV_CSR_REG(stvec)), + KVM_CSR_CFG("sscratch", sscratch, RISCV_CSR_REG(sscratch)), + KVM_CSR_CFG("sepc", sepc, RISCV_CSR_REG(sepc)), + KVM_CSR_CFG("scause", scause, RISCV_CSR_REG(scause)), + KVM_CSR_CFG("stval", stval, RISCV_CSR_REG(stval)), + KVM_CSR_CFG("sip", mip, RISCV_CSR_REG(sip)), + KVM_CSR_CFG("satp", satp, RISCV_CSR_REG(satp)), +}; + +static void *kvmconfig_get_env_addr(RISCVCPU *cpu, KVMCPUConfig *csr_cfg) +{ + return (void *)&cpu->env + csr_cfg->offset; +} + +static uint32_t kvm_cpu_csr_get_u32(RISCVCPU *cpu, KVMCPUConfig *csr_cfg) +{ + uint32_t *val32 = kvmconfig_get_env_addr(cpu, csr_cfg); + return *val32; +} + +static uint64_t kvm_cpu_csr_get_u64(RISCVCPU *cpu, KVMCPUConfig *csr_cfg) +{ + uint64_t *val64 = kvmconfig_get_env_addr(cpu, csr_cfg); + return *val64; +} + +static void kvm_cpu_csr_set_u32(RISCVCPU *cpu, KVMCPUConfig *csr_cfg, + uint32_t val) +{ + uint32_t *val32 = kvmconfig_get_env_addr(cpu, csr_cfg); + *val32 = val; +} + +static void kvm_cpu_csr_set_u64(RISCVCPU *cpu, KVMCPUConfig *csr_cfg, + uint64_t val) +{ + uint64_t *val64 = kvmconfig_get_env_addr(cpu, csr_cfg); + *val64 = val; +} + #define KVM_EXT_CFG(_name, _prop, _reg_id) \ {.name = _name, .offset = CPU_CFG_OFFSET(_prop), \ .kvm_reg_id = _reg_id} @@ -598,34 +629,52 @@ static int kvm_riscv_put_regs_core(CPUState *cs) static int kvm_riscv_get_regs_csr(CPUState *cs) { - CPURISCVState *env = &RISCV_CPU(cs)->env; + RISCVCPU *cpu = RISCV_CPU(cs); + uint64_t reg; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(kvm_csr_cfgs); i++) { + KVMCPUConfig *csr_cfg = &kvm_csr_cfgs[i]; - KVM_RISCV_GET_CSR(cs, env, sstatus, env->mstatus); - KVM_RISCV_GET_CSR(cs, env, sie, env->mie); - KVM_RISCV_GET_CSR(cs, env, stvec, env->stvec); - KVM_RISCV_GET_CSR(cs, env, sscratch, env->sscratch); - KVM_RISCV_GET_CSR(cs, env, sepc, env->sepc); - KVM_RISCV_GET_CSR(cs, env, scause, env->scause); - KVM_RISCV_GET_CSR(cs, env, stval, env->stval); - KVM_RISCV_GET_CSR(cs, env, sip, env->mip); - KVM_RISCV_GET_CSR(cs, env, satp, env->satp); + ret = kvm_get_one_reg(cs, csr_cfg->kvm_reg_id, ®); + if (ret) { + return ret; + } + + if (KVM_REG_SIZE(csr_cfg->kvm_reg_id) == sizeof(uint32_t)) { + kvm_cpu_csr_set_u32(cpu, csr_cfg, reg); + } else if (KVM_REG_SIZE(csr_cfg->kvm_reg_id) == sizeof(uint64_t)) { + kvm_cpu_csr_set_u64(cpu, csr_cfg, reg); + } else { + g_assert_not_reached(); + } + } return 0; } static int kvm_riscv_put_regs_csr(CPUState *cs) { - CPURISCVState *env = &RISCV_CPU(cs)->env; + RISCVCPU *cpu = RISCV_CPU(cs); + uint64_t reg; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(kvm_csr_cfgs); i++) { + KVMCPUConfig *csr_cfg = &kvm_csr_cfgs[i]; + + if (KVM_REG_SIZE(csr_cfg->kvm_reg_id) == sizeof(uint32_t)) { + reg = kvm_cpu_csr_get_u32(cpu, csr_cfg); + } else if (KVM_REG_SIZE(csr_cfg->kvm_reg_id) == sizeof(uint64_t)) { + reg = kvm_cpu_csr_get_u64(cpu, csr_cfg); + } else { + g_assert_not_reached(); + } - KVM_RISCV_SET_CSR(cs, env, sstatus, env->mstatus); - KVM_RISCV_SET_CSR(cs, env, sie, env->mie); - KVM_RISCV_SET_CSR(cs, env, stvec, env->stvec); - KVM_RISCV_SET_CSR(cs, env, sscratch, env->sscratch); - KVM_RISCV_SET_CSR(cs, env, sepc, env->sepc); - KVM_RISCV_SET_CSR(cs, env, scause, env->scause); - KVM_RISCV_SET_CSR(cs, env, stval, env->stval); - KVM_RISCV_SET_CSR(cs, env, sip, env->mip); - KVM_RISCV_SET_CSR(cs, env, satp, env->satp); + ret = kvm_set_one_reg(cs, csr_cfg->kvm_reg_id, ®); + if (ret) { + return ret; + } + } return 0; } From f396c217a53d9b7960dd002fbb07cfe1d46b27aa Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:44:18 -0300 Subject: [PATCH 1005/2760] target/riscv/kvm: do not read unavailable CSRs [1] reports that commit 4db19d5b21 broke a KVM guest running kernel 6.6. This happens because the kernel does not know 'senvcfg', making it unable to boot because QEMU is reading/wriiting it without any checks. After converting the CSRs to do "automated" get/put reg procedures in the previous patch we can now scan for availability. Two functions are created: - kvm_riscv_read_csr_cfg_legacy() will check if the CSR exists by brute forcing KVM_GET_ONE_REG in each one of them, interpreting an EINVAL return as indication that the CSR isn't available. This will be use in absence of KVM_GET_REG_LIST; - kvm_riscv_read_csr_cfg() will use the existing result of get_reg_list to check if the CSRs ids are present. kvm_riscv_init_multiext_cfg() is now kvm_riscv_init_cfg() to reflect that the function is also dealing with CSRs. [1] https://lore.kernel.org/qemu-riscv/CABJz62OfUDHYkQ0T3rGHStQprf1c7_E0qBLbLKhfv=+jb0SYAw@mail.gmail.com/ Fixes: 4db19d5b21 ("target/riscv/kvm: add missing KVM CSRs") Reported-by: Andrea Bolognani Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-ID: <20250429124421.223883-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis Cc: qemu-stable@nongnu.org --- target/riscv/kvm/kvm-cpu.c | 62 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 59 insertions(+), 3 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 3740514bba..344616c1cc 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -636,6 +636,10 @@ static int kvm_riscv_get_regs_csr(CPUState *cs) for (i = 0; i < ARRAY_SIZE(kvm_csr_cfgs); i++) { KVMCPUConfig *csr_cfg = &kvm_csr_cfgs[i]; + if (!csr_cfg->supported) { + continue; + } + ret = kvm_get_one_reg(cs, csr_cfg->kvm_reg_id, ®); if (ret) { return ret; @@ -662,6 +666,10 @@ static int kvm_riscv_put_regs_csr(CPUState *cs) for (i = 0; i < ARRAY_SIZE(kvm_csr_cfgs); i++) { KVMCPUConfig *csr_cfg = &kvm_csr_cfgs[i]; + if (!csr_cfg->supported) { + continue; + } + if (KVM_REG_SIZE(csr_cfg->kvm_reg_id) == sizeof(uint32_t)) { reg = kvm_cpu_csr_get_u32(cpu, csr_cfg); } else if (KVM_REG_SIZE(csr_cfg->kvm_reg_id) == sizeof(uint64_t)) { @@ -1090,6 +1098,32 @@ static void kvm_riscv_read_multiext_legacy(RISCVCPU *cpu, } } +static void kvm_riscv_read_csr_cfg_legacy(KVMScratchCPU *kvmcpu) +{ + uint64_t val; + int i, ret; + + for (i = 0; i < ARRAY_SIZE(kvm_csr_cfgs); i++) { + KVMCPUConfig *csr_cfg = &kvm_csr_cfgs[i]; + struct kvm_one_reg reg; + + reg.id = csr_cfg->kvm_reg_id; + reg.addr = (uint64_t)&val; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + if (errno == EINVAL) { + csr_cfg->supported = false; + } else { + error_report("Unable to read KVM CSR %s: %s", + csr_cfg->name, strerror(errno)); + exit(EXIT_FAILURE); + } + } else { + csr_cfg->supported = true; + } + } +} + static int uint64_cmp(const void *a, const void *b) { uint64_t val1 = *(const uint64_t *)a; @@ -1146,7 +1180,26 @@ static void kvm_riscv_read_vlenb(RISCVCPU *cpu, KVMScratchCPU *kvmcpu, } } -static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) +static void kvm_riscv_read_csr_cfg(struct kvm_reg_list *reglist) +{ + struct kvm_reg_list *reg_search; + uint64_t reg_id; + + for (int i = 0; i < ARRAY_SIZE(kvm_csr_cfgs); i++) { + KVMCPUConfig *csr_cfg = &kvm_csr_cfgs[i]; + + reg_id = csr_cfg->kvm_reg_id; + reg_search = bsearch(®_id, reglist->reg, reglist->n, + sizeof(uint64_t), uint64_cmp); + if (!reg_search) { + continue; + } + + csr_cfg->supported = true; + } +} + +static void kvm_riscv_init_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) { g_autofree struct kvm_reg_list *reglist = NULL; KVMCPUConfig *multi_ext_cfg; @@ -1163,7 +1216,9 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) * (EINVAL). Use read_legacy() in this case. */ if (errno == EINVAL) { - return kvm_riscv_read_multiext_legacy(cpu, kvmcpu); + kvm_riscv_read_multiext_legacy(cpu, kvmcpu); + kvm_riscv_read_csr_cfg_legacy(kvmcpu); + return; } else if (errno != E2BIG) { /* * E2BIG is an expected error message for the API since we @@ -1226,6 +1281,7 @@ static void kvm_riscv_init_multiext_cfg(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) } kvm_riscv_check_sbi_dbcn_support(cpu, reglist); + kvm_riscv_read_csr_cfg(reglist); } static void riscv_init_kvm_registers(Object *cpu_obj) @@ -1239,7 +1295,7 @@ static void riscv_init_kvm_registers(Object *cpu_obj) kvm_riscv_init_machine_ids(cpu, &kvmcpu); kvm_riscv_init_misa_ext_mask(cpu, &kvmcpu); - kvm_riscv_init_multiext_cfg(cpu, &kvmcpu); + kvm_riscv_init_cfg(cpu, &kvmcpu); kvm_riscv_destroy_scratch_vcpu(&kvmcpu); } From 86b8c3821496898cd3bd8eaa1bac71f5c784a2db Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:44:19 -0300 Subject: [PATCH 1006/2760] target/riscv/kvm: add senvcfg CSR We're missing the senvcfg CSRs which is already present in the KVM UAPI. Reported-by: Andrew Jones Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Acked-by: Alistair Francis Message-ID: <20250429124421.223883-8-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 344616c1cc..0e34382163 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -249,6 +249,7 @@ static KVMCPUConfig kvm_csr_cfgs[] = { KVM_CSR_CFG("stval", stval, RISCV_CSR_REG(stval)), KVM_CSR_CFG("sip", mip, RISCV_CSR_REG(sip)), KVM_CSR_CFG("satp", satp, RISCV_CSR_REG(satp)), + KVM_CSR_CFG("senvcfg", senvcfg, RISCV_CSR_REG(senvcfg)), }; static void *kvmconfig_get_env_addr(RISCVCPU *cpu, KVMCPUConfig *csr_cfg) @@ -698,6 +699,7 @@ static void kvm_riscv_reset_regs_csr(CPURISCVState *env) env->stval = 0; env->mip = 0; env->satp = 0; + env->senvcfg = 0; } static int kvm_riscv_get_regs_fp(CPUState *cs) From 775ac57e0a54b9127bd2ad005675772870cd1932 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:44:20 -0300 Subject: [PATCH 1007/2760] target/riscv/kvm: read/write KVM regs via env size We're going to add support for scounteren in the next patch. KVM defines as a target_ulong CSR, while QEMU defines env->scounteren as a 32 bit field. This will cause the current code to read/write a 64 bit CSR in a 32 bit field when running in a 64 bit CPU. To prevent that, change the current logic to honor the size of the QEMU storage instead of the KVM CSR reg. Suggested-by: Andrew Jones Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Andrew Jones Message-ID: <20250429124421.223883-9-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 0e34382163..ca171d5457 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -135,6 +135,7 @@ typedef struct KVMCPUConfig { const char *description; target_ulong offset; uint64_t kvm_reg_id; + uint32_t prop_size; bool user_set; bool supported; } KVMCPUConfig; @@ -237,6 +238,7 @@ static void kvm_riscv_update_cpu_misa_ext(RISCVCPU *cpu, CPUState *cs) #define KVM_CSR_CFG(_name, _env_prop, reg_id) \ {.name = _name, .offset = ENV_CSR_OFFSET(_env_prop), \ + .prop_size = sizeof(((CPURISCVState *)0)->_env_prop), \ .kvm_reg_id = reg_id} static KVMCPUConfig kvm_csr_cfgs[] = { @@ -646,9 +648,9 @@ static int kvm_riscv_get_regs_csr(CPUState *cs) return ret; } - if (KVM_REG_SIZE(csr_cfg->kvm_reg_id) == sizeof(uint32_t)) { - kvm_cpu_csr_set_u32(cpu, csr_cfg, reg); - } else if (KVM_REG_SIZE(csr_cfg->kvm_reg_id) == sizeof(uint64_t)) { + if (csr_cfg->prop_size == sizeof(uint32_t)) { + kvm_cpu_csr_set_u32(cpu, csr_cfg, (uint32_t)reg); + } else if (csr_cfg->prop_size == sizeof(uint64_t)) { kvm_cpu_csr_set_u64(cpu, csr_cfg, reg); } else { g_assert_not_reached(); @@ -671,9 +673,9 @@ static int kvm_riscv_put_regs_csr(CPUState *cs) continue; } - if (KVM_REG_SIZE(csr_cfg->kvm_reg_id) == sizeof(uint32_t)) { + if (csr_cfg->prop_size == sizeof(uint32_t)) { reg = kvm_cpu_csr_get_u32(cpu, csr_cfg); - } else if (KVM_REG_SIZE(csr_cfg->kvm_reg_id) == sizeof(uint64_t)) { + } else if (csr_cfg->prop_size == sizeof(uint64_t)) { reg = kvm_cpu_csr_get_u64(cpu, csr_cfg); } else { g_assert_not_reached(); From 8ab99a05f34bab3f9fae49299ee407ead78f0470 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:44:21 -0300 Subject: [PATCH 1008/2760] target/riscv/kvm: add scounteren CSR Add support for the scounteren KVM CSR. Note that env->scounteren is a 32 bit and all KVM CSRs are target_ulong, so scounteren will be capped to 32 bits read/writes. Reported-by: Andrew Jones Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Alistair Francis Message-ID: <20250429124421.223883-10-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index ca171d5457..82f9728636 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -251,6 +251,7 @@ static KVMCPUConfig kvm_csr_cfgs[] = { KVM_CSR_CFG("stval", stval, RISCV_CSR_REG(stval)), KVM_CSR_CFG("sip", mip, RISCV_CSR_REG(sip)), KVM_CSR_CFG("satp", satp, RISCV_CSR_REG(satp)), + KVM_CSR_CFG("scounteren", scounteren, RISCV_CSR_REG(scounteren)), KVM_CSR_CFG("senvcfg", senvcfg, RISCV_CSR_REG(senvcfg)), }; @@ -701,6 +702,7 @@ static void kvm_riscv_reset_regs_csr(CPURISCVState *env) env->stval = 0; env->mip = 0; env->satp = 0; + env->scounteren = 0; env->senvcfg = 0; } From 221e96cb7ae4535bcef7d54d1620a44be88b655e Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:58:03 -0300 Subject: [PATCH 1009/2760] hw/riscv/virt.c: enforce s->memmap use in machine_init() Throughout the code we're accessing the board memmap, most of the time, by accessing it statically via 'virt_memmap'. This static map is also assigned in the machine state in s->memmap. We're also passing it as a variable to some fdt functions, which is unorthodox since we can spare a function argument by accessing it statically or via the machine state. All the current forms are valid but not all of the are scalable. In the future we will version this board, and then all this code will need rework because it should point to the updated memmap. In this case, we'll want to assign the adequate versioned memmap once during init, in s->memmap like it is being done today, and the rest of the code will access the updated map via s->memmap. We're also enforcing the pattern of using s->memmap instead of assigning it to a temp variable 'memmap'. Code is copy/pasted around all the time and being consistent is important. We'll start these rather mechanical changes with virt_machine_init(). Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Joel Stanley Message-ID: <20250429125811.224803-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 54 ++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 5958ad1f7d..0200679240 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1522,7 +1522,6 @@ static void virt_machine_done(Notifier *notifier, void *data) static void virt_machine_init(MachineState *machine) { - const MemMapEntry *memmap = virt_memmap; RISCVVirtState *s = RISCV_VIRT_MACHINE(machine); MemoryRegion *system_memory = get_system_memory(); MemoryRegion *mask_rom = g_new(MemoryRegion, 1); @@ -1530,6 +1529,8 @@ static void virt_machine_init(MachineState *machine) int i, base_hartid, hart_count; int socket_count = riscv_socket_count(machine); + s->memmap = virt_memmap; + /* Check socket count limit */ if (VIRT_SOCKETS_MAX < socket_count) { error_report("number of sockets/nodes should be less than %d", @@ -1577,7 +1578,7 @@ static void virt_machine_init(MachineState *machine) if (virt_aclint_allowed() && s->have_aclint) { if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { /* Per-socket ACLINT MTIMER */ - riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + + riscv_aclint_mtimer_create(s->memmap[VIRT_CLINT].base + i * RISCV_ACLINT_DEFAULT_MTIMER_SIZE, RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count, @@ -1586,28 +1587,28 @@ static void virt_machine_init(MachineState *machine) RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true); } else { /* Per-socket ACLINT MSWI, MTIMER, and SSWI */ - riscv_aclint_swi_create(memmap[VIRT_CLINT].base + - i * memmap[VIRT_CLINT].size, + riscv_aclint_swi_create(s->memmap[VIRT_CLINT].base + + i * s->memmap[VIRT_CLINT].size, base_hartid, hart_count, false); - riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + - i * memmap[VIRT_CLINT].size + + riscv_aclint_mtimer_create(s->memmap[VIRT_CLINT].base + + i * s->memmap[VIRT_CLINT].size + RISCV_ACLINT_SWI_SIZE, RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count, RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true); - riscv_aclint_swi_create(memmap[VIRT_ACLINT_SSWI].base + - i * memmap[VIRT_ACLINT_SSWI].size, + riscv_aclint_swi_create(s->memmap[VIRT_ACLINT_SSWI].base + + i * s->memmap[VIRT_ACLINT_SSWI].size, base_hartid, hart_count, true); } } else if (tcg_enabled()) { /* Per-socket SiFive CLINT */ riscv_aclint_swi_create( - memmap[VIRT_CLINT].base + i * memmap[VIRT_CLINT].size, + s->memmap[VIRT_CLINT].base + i * s->memmap[VIRT_CLINT].size, base_hartid, hart_count, false); - riscv_aclint_mtimer_create(memmap[VIRT_CLINT].base + - i * memmap[VIRT_CLINT].size + RISCV_ACLINT_SWI_SIZE, + riscv_aclint_mtimer_create(s->memmap[VIRT_CLINT].base + + i * s->memmap[VIRT_CLINT].size + RISCV_ACLINT_SWI_SIZE, RISCV_ACLINT_DEFAULT_MTIMER_SIZE, base_hartid, hart_count, RISCV_ACLINT_DEFAULT_MTIMECMP, RISCV_ACLINT_DEFAULT_MTIME, RISCV_ACLINT_DEFAULT_TIMEBASE_FREQ, true); @@ -1615,11 +1616,11 @@ static void virt_machine_init(MachineState *machine) /* Per-socket interrupt controller */ if (s->aia_type == VIRT_AIA_TYPE_NONE) { - s->irqchip[i] = virt_create_plic(memmap, i, + s->irqchip[i] = virt_create_plic(s->memmap, i, base_hartid, hart_count); } else { s->irqchip[i] = virt_create_aia(s->aia_type, s->aia_guests, - memmap, i, base_hartid, + s->memmap, i, base_hartid, hart_count); } @@ -1641,8 +1642,8 @@ static void virt_machine_init(MachineState *machine) if (kvm_enabled() && virt_use_kvm_aia_aplic_imsic(s->aia_type)) { kvm_riscv_aia_create(machine, IMSIC_MMIO_GROUP_MIN_SHIFT, VIRT_IRQCHIP_NUM_SOURCES, VIRT_IRQCHIP_NUM_MSIS, - memmap[VIRT_APLIC_S].base, - memmap[VIRT_IMSIC_S].base, + s->memmap[VIRT_APLIC_S].base, + s->memmap[VIRT_IMSIC_S].base, s->aia_guests); } @@ -1658,21 +1659,20 @@ static void virt_machine_init(MachineState *machine) virt_high_pcie_memmap.size = VIRT32_HIGH_PCIE_MMIO_SIZE; } else { virt_high_pcie_memmap.size = VIRT64_HIGH_PCIE_MMIO_SIZE; - virt_high_pcie_memmap.base = memmap[VIRT_DRAM].base + machine->ram_size; + virt_high_pcie_memmap.base = s->memmap[VIRT_DRAM].base + + machine->ram_size; virt_high_pcie_memmap.base = ROUND_UP(virt_high_pcie_memmap.base, virt_high_pcie_memmap.size); } - s->memmap = virt_memmap; - /* register system main memory (actual RAM) */ - memory_region_add_subregion(system_memory, memmap[VIRT_DRAM].base, - machine->ram); + memory_region_add_subregion(system_memory, s->memmap[VIRT_DRAM].base, + machine->ram); /* boot rom */ memory_region_init_rom(mask_rom, NULL, "riscv_virt_board.mrom", - memmap[VIRT_MROM].size, &error_fatal); - memory_region_add_subregion(system_memory, memmap[VIRT_MROM].base, + s->memmap[VIRT_MROM].size, &error_fatal); + memory_region_add_subregion(system_memory, s->memmap[VIRT_MROM].base, mask_rom); /* @@ -1683,12 +1683,12 @@ static void virt_machine_init(MachineState *machine) rom_set_fw(s->fw_cfg); /* SiFive Test MMIO device */ - sifive_test_create(memmap[VIRT_TEST].base); + sifive_test_create(s->memmap[VIRT_TEST].base); /* VirtIO MMIO devices */ for (i = 0; i < VIRTIO_COUNT; i++) { sysbus_create_simple("virtio-mmio", - memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, + s->memmap[VIRT_VIRTIO].base + i * s->memmap[VIRT_VIRTIO].size, qdev_get_gpio_in(virtio_irqchip, VIRTIO_IRQ + i)); } @@ -1696,11 +1696,11 @@ static void virt_machine_init(MachineState *machine) create_platform_bus(s, mmio_irqchip); - serial_mm_init(system_memory, memmap[VIRT_UART0].base, + serial_mm_init(system_memory, s->memmap[VIRT_UART0].base, 0, qdev_get_gpio_in(mmio_irqchip, UART0_IRQ), 399193, serial_hd(0), DEVICE_LITTLE_ENDIAN); - sysbus_create_simple("goldfish_rtc", memmap[VIRT_RTC].base, + sysbus_create_simple("goldfish_rtc", s->memmap[VIRT_RTC].base, qdev_get_gpio_in(mmio_irqchip, RTC_IRQ)); for (i = 0; i < ARRAY_SIZE(s->flash); i++) { @@ -1718,7 +1718,7 @@ static void virt_machine_init(MachineState *machine) exit(1); } } else { - create_fdt(s, memmap); + create_fdt(s, s->memmap); } if (virt_is_iommu_sys_enabled(s)) { From fb8cf3fd98d7640d2ea3feca65fb40fba09a0235 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:58:04 -0300 Subject: [PATCH 1010/2760] hw/riscv/virt.c: remove trivial virt_memmap references We should use s->memmap instead of virt_memmap to be able to use an updated memmap when we start versioning the board. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250429125811.224803-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 0200679240..843665a5bd 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -166,8 +166,8 @@ static void virt_flash_map1(PFlashCFI01 *flash, static void virt_flash_map(RISCVVirtState *s, MemoryRegion *sysmem) { - hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2; - hwaddr flashbase = virt_memmap[VIRT_FLASH].base; + hwaddr flashsize = s->memmap[VIRT_FLASH].size / 2; + hwaddr flashbase = s->memmap[VIRT_FLASH].base; virt_flash_map1(s->flash[0], flashbase, flashsize, sysmem); @@ -999,8 +999,8 @@ static void create_fdt_rtc(RISCVVirtState *s, const MemMapEntry *memmap, static void create_fdt_flash(RISCVVirtState *s, const MemMapEntry *memmap) { MachineState *ms = MACHINE(s); - hwaddr flashsize = virt_memmap[VIRT_FLASH].size / 2; - hwaddr flashbase = virt_memmap[VIRT_FLASH].base; + hwaddr flashsize = s->memmap[VIRT_FLASH].size / 2; + hwaddr flashbase = s->memmap[VIRT_FLASH].base; g_autofree char *name = g_strdup_printf("/flash@%" PRIx64, flashbase); qemu_fdt_add_subnode(ms->fdt, name); @@ -1035,7 +1035,7 @@ static void create_fdt_virtio_iommu(RISCVVirtState *s, uint16_t bdf) g_autofree char *pci_node = NULL; pci_node = g_strdup_printf("/soc/pci@%lx", - (long) virt_memmap[VIRT_PCIE_ECAM].base); + (long) s->memmap[VIRT_PCIE_ECAM].base); iommu_node = g_strdup_printf("%s/virtio_iommu@%x,%x", pci_node, PCI_SLOT(bdf), PCI_FUNC(bdf)); iommu_phandle = qemu_fdt_alloc_phandle(fdt); @@ -1104,7 +1104,7 @@ static void create_fdt_iommu(RISCVVirtState *s, uint16_t bdf) g_autofree char *pci_node = NULL; pci_node = g_strdup_printf("/soc/pci@%lx", - (long) virt_memmap[VIRT_PCIE_ECAM].base); + (long) s->memmap[VIRT_PCIE_ECAM].base); iommu_node = g_strdup_printf("%s/iommu@%x", pci_node, bdf); iommu_phandle = qemu_fdt_alloc_phandle(fdt); qemu_fdt_add_subnode(fdt, iommu_node); @@ -1126,24 +1126,24 @@ static void finalize_fdt(RISCVVirtState *s) uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1; uint32_t iommu_sys_phandle = 1; - create_fdt_sockets(s, virt_memmap, &phandle, &irq_mmio_phandle, + create_fdt_sockets(s, s->memmap, &phandle, &irq_mmio_phandle, &irq_pcie_phandle, &irq_virtio_phandle, &msi_pcie_phandle); - create_fdt_virtio(s, virt_memmap, irq_virtio_phandle); + create_fdt_virtio(s, s->memmap, irq_virtio_phandle); if (virt_is_iommu_sys_enabled(s)) { create_fdt_iommu_sys(s, irq_mmio_phandle, msi_pcie_phandle, &iommu_sys_phandle); } - create_fdt_pcie(s, virt_memmap, irq_pcie_phandle, msi_pcie_phandle, + create_fdt_pcie(s, s->memmap, irq_pcie_phandle, msi_pcie_phandle, iommu_sys_phandle); - create_fdt_reset(s, virt_memmap, &phandle); + create_fdt_reset(s, s->memmap, &phandle); - create_fdt_uart(s, virt_memmap, irq_mmio_phandle); + create_fdt_uart(s, s->memmap, irq_mmio_phandle); - create_fdt_rtc(s, virt_memmap, irq_mmio_phandle); + create_fdt_rtc(s, s->memmap, irq_mmio_phandle); } static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap) @@ -1361,14 +1361,13 @@ static void create_platform_bus(RISCVVirtState *s, DeviceState *irqchip) { DeviceState *dev; SysBusDevice *sysbus; - const MemMapEntry *memmap = virt_memmap; int i; MemoryRegion *sysmem = get_system_memory(); dev = qdev_new(TYPE_PLATFORM_BUS_DEVICE); dev->id = g_strdup(TYPE_PLATFORM_BUS_DEVICE); qdev_prop_set_uint32(dev, "num_irqs", VIRT_PLATFORM_BUS_NUM_IRQS); - qdev_prop_set_uint32(dev, "mmio_size", memmap[VIRT_PLATFORM_BUS].size); + qdev_prop_set_uint32(dev, "mmio_size", s->memmap[VIRT_PLATFORM_BUS].size); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); s->platform_bus_dev = dev; @@ -1379,7 +1378,7 @@ static void create_platform_bus(RISCVVirtState *s, DeviceState *irqchip) } memory_region_add_subregion(sysmem, - memmap[VIRT_PLATFORM_BUS].base, + s->memmap[VIRT_PLATFORM_BUS].base, sysbus_mmio_get_region(sysbus, 0)); } From 47e51e5d12740ebdcd797e4771b467ba8c2aba82 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:58:05 -0300 Subject: [PATCH 1011/2760] hw/riscv/virt.c: use s->memmap in virt_machine_done() Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250429125811.224803-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 843665a5bd..b349b2b1cf 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1425,9 +1425,8 @@ static void virt_machine_done(Notifier *notifier, void *data) { RISCVVirtState *s = container_of(notifier, RISCVVirtState, machine_done); - const MemMapEntry *memmap = virt_memmap; MachineState *machine = MACHINE(s); - hwaddr start_addr = memmap[VIRT_DRAM].base; + hwaddr start_addr = s->memmap[VIRT_DRAM].base; target_ulong firmware_end_addr, kernel_start_addr; const char *firmware_name = riscv_default_firmware_name(&s->soc[0]); uint64_t fdt_load_addr; @@ -1471,14 +1470,14 @@ static void virt_machine_done(Notifier *notifier, void *data) * let's overwrite the address we jump to after reset to * the base of the flash. */ - start_addr = virt_memmap[VIRT_FLASH].base; + start_addr = s->memmap[VIRT_FLASH].base; } else { /* * Pflash was supplied but either KVM guest or bios is not none. * In this case, base of the flash would contain S-mode payload. */ riscv_setup_firmware_boot(machine); - kernel_entry = virt_memmap[VIRT_FLASH].base; + kernel_entry = s->memmap[VIRT_FLASH].base; } } @@ -1492,15 +1491,15 @@ static void virt_machine_done(Notifier *notifier, void *data) kernel_entry = boot_info.image_low_addr; } - fdt_load_addr = riscv_compute_fdt_addr(memmap[VIRT_DRAM].base, - memmap[VIRT_DRAM].size, + fdt_load_addr = riscv_compute_fdt_addr(s->memmap[VIRT_DRAM].base, + s->memmap[VIRT_DRAM].size, machine, &boot_info); riscv_load_fdt(fdt_load_addr, machine->fdt); /* load the reset vector */ riscv_setup_rom_reset_vec(machine, &s->soc[0], start_addr, - virt_memmap[VIRT_MROM].base, - virt_memmap[VIRT_MROM].size, kernel_entry, + s->memmap[VIRT_MROM].base, + s->memmap[VIRT_MROM].size, kernel_entry, fdt_load_addr); /* From 6418ff383d53e5872f2746d490885f0a551385a4 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:58:06 -0300 Subject: [PATCH 1012/2760] hw/riscv/virt.c: add 'base' arg in create_fw_cfg() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function can receive the value via s->memmap[VIRT_FW_CFG].base from the caller, avoiding the use of virt_memmap. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-ID: <20250429125811.224803-5-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index b349b2b1cf..3d547f7c2b 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1262,9 +1262,8 @@ static inline DeviceState *gpex_pcie_init(MemoryRegion *sys_mem, return dev; } -static FWCfgState *create_fw_cfg(const MachineState *ms) +static FWCfgState *create_fw_cfg(const MachineState *ms, hwaddr base) { - hwaddr base = virt_memmap[VIRT_FW_CFG].base; FWCfgState *fw_cfg; fw_cfg = fw_cfg_init_mem_wide(base + 8, base, 8, base + 16, @@ -1677,7 +1676,7 @@ static void virt_machine_init(MachineState *machine) * Init fw_cfg. Must be done before riscv_load_fdt, otherwise the * device tree cannot be altered and we get FDT_ERR_NOSPACE. */ - s->fw_cfg = create_fw_cfg(machine); + s->fw_cfg = create_fw_cfg(machine, s->memmap[VIRT_FW_CFG].base); rom_set_fw(s->fw_cfg); /* SiFive Test MMIO device */ From 658e501969d948735669fcf82206a0ef92755f97 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:58:07 -0300 Subject: [PATCH 1013/2760] hw/riscv/virt.c: use s->memmap in create_fdt() path create_fdt(), create_fdt_flash() and create_fdt_fw_cfg() can access the memmap via their RISCVVirtState pointers. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250429125811.224803-6-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 3d547f7c2b..8a703a0233 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -996,7 +996,7 @@ static void create_fdt_rtc(RISCVVirtState *s, const MemMapEntry *memmap, } } -static void create_fdt_flash(RISCVVirtState *s, const MemMapEntry *memmap) +static void create_fdt_flash(RISCVVirtState *s) { MachineState *ms = MACHINE(s); hwaddr flashsize = s->memmap[VIRT_FLASH].size / 2; @@ -1011,11 +1011,11 @@ static void create_fdt_flash(RISCVVirtState *s, const MemMapEntry *memmap) qemu_fdt_setprop_cell(ms->fdt, name, "bank-width", 4); } -static void create_fdt_fw_cfg(RISCVVirtState *s, const MemMapEntry *memmap) +static void create_fdt_fw_cfg(RISCVVirtState *s) { MachineState *ms = MACHINE(s); - hwaddr base = memmap[VIRT_FW_CFG].base; - hwaddr size = memmap[VIRT_FW_CFG].size; + hwaddr base = s->memmap[VIRT_FW_CFG].base; + hwaddr size = s->memmap[VIRT_FW_CFG].size; g_autofree char *nodename = g_strdup_printf("/fw-cfg@%" PRIx64, base); qemu_fdt_add_subnode(ms->fdt, nodename); @@ -1146,7 +1146,7 @@ static void finalize_fdt(RISCVVirtState *s) create_fdt_rtc(s, s->memmap, irq_mmio_phandle); } -static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap) +static void create_fdt(RISCVVirtState *s) { MachineState *ms = MACHINE(s); uint8_t rng_seed[32]; @@ -1173,7 +1173,8 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap) * The "/soc/pci@..." node is needed for PCIE hotplugs * that might happen before finalize_fdt(). */ - name = g_strdup_printf("/soc/pci@%lx", (long) memmap[VIRT_PCIE_ECAM].base); + name = g_strdup_printf("/soc/pci@%lx", + (long) s->memmap[VIRT_PCIE_ECAM].base); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_add_subnode(ms->fdt, "/chosen"); @@ -1185,8 +1186,8 @@ static void create_fdt(RISCVVirtState *s, const MemMapEntry *memmap) qemu_fdt_add_subnode(ms->fdt, "/aliases"); - create_fdt_flash(s, memmap); - create_fdt_fw_cfg(s, memmap); + create_fdt_flash(s); + create_fdt_fw_cfg(s); create_fdt_pmu(s); } @@ -1715,7 +1716,7 @@ static void virt_machine_init(MachineState *machine) exit(1); } } else { - create_fdt(s, s->memmap); + create_fdt(s); } if (virt_is_iommu_sys_enabled(s)) { From 04c4f8d1ee373b9b8c413619f5600caa3e7b006c Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:58:08 -0300 Subject: [PATCH 1014/2760] hw/riscv/virt.c: use s->memmap in create_fdt_sockets() path create_fdt_sockets() and all its fdt helpers (create_fdt_socket_aplic(), create_fdt_imsic(), create_fdt_socket_plic(), create_fdt_socket_aclint() and create_fdt_socket_memory()) can use s->memmap from their RISCVVirtState pointer instead of having an extra memmap argument. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250429125811.224803-7-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 89 ++++++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 42 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 8a703a0233..e66a46f524 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -301,14 +301,13 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, } } -static void create_fdt_socket_memory(RISCVVirtState *s, - const MemMapEntry *memmap, int socket) +static void create_fdt_socket_memory(RISCVVirtState *s, int socket) { g_autofree char *mem_name = NULL; uint64_t addr, size; MachineState *ms = MACHINE(s); - addr = memmap[VIRT_DRAM].base + riscv_socket_mem_offset(ms, socket); + addr = s->memmap[VIRT_DRAM].base + riscv_socket_mem_offset(ms, socket); size = riscv_socket_mem_size(ms, socket); mem_name = g_strdup_printf("/memory@%lx", (long)addr); qemu_fdt_add_subnode(ms->fdt, mem_name); @@ -319,7 +318,7 @@ static void create_fdt_socket_memory(RISCVVirtState *s, } static void create_fdt_socket_clint(RISCVVirtState *s, - const MemMapEntry *memmap, int socket, + int socket, uint32_t *intc_phandles) { int cpu; @@ -340,21 +339,22 @@ static void create_fdt_socket_clint(RISCVVirtState *s, clint_cells[cpu * 4 + 3] = cpu_to_be32(IRQ_M_TIMER); } - clint_addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket); + clint_addr = s->memmap[VIRT_CLINT].base + + (s->memmap[VIRT_CLINT].size * socket); clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr); qemu_fdt_add_subnode(ms->fdt, clint_name); qemu_fdt_setprop_string_array(ms->fdt, clint_name, "compatible", (char **)&clint_compat, ARRAY_SIZE(clint_compat)); qemu_fdt_setprop_cells(ms->fdt, clint_name, "reg", - 0x0, clint_addr, 0x0, memmap[VIRT_CLINT].size); + 0x0, clint_addr, 0x0, s->memmap[VIRT_CLINT].size); qemu_fdt_setprop(ms->fdt, clint_name, "interrupts-extended", clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4); riscv_socket_fdt_write_id(ms, clint_name, socket); } static void create_fdt_socket_aclint(RISCVVirtState *s, - const MemMapEntry *memmap, int socket, + int socket, uint32_t *intc_phandles) { int cpu; @@ -381,8 +381,10 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, aclint_cells_size = s->soc[socket].num_harts * sizeof(uint32_t) * 2; if (s->aia_type != VIRT_AIA_TYPE_APLIC_IMSIC) { - addr = memmap[VIRT_CLINT].base + (memmap[VIRT_CLINT].size * socket); + addr = s->memmap[VIRT_CLINT].base + + (s->memmap[VIRT_CLINT].size * socket); name = g_strdup_printf("/soc/mswi@%lx", addr); + qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-mswi"); @@ -397,13 +399,13 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, } if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { - addr = memmap[VIRT_CLINT].base + + addr = s->memmap[VIRT_CLINT].base + (RISCV_ACLINT_DEFAULT_MTIMER_SIZE * socket); size = RISCV_ACLINT_DEFAULT_MTIMER_SIZE; } else { - addr = memmap[VIRT_CLINT].base + RISCV_ACLINT_SWI_SIZE + - (memmap[VIRT_CLINT].size * socket); - size = memmap[VIRT_CLINT].size - RISCV_ACLINT_SWI_SIZE; + addr = s->memmap[VIRT_CLINT].base + RISCV_ACLINT_SWI_SIZE + + (s->memmap[VIRT_CLINT].size * socket); + size = s->memmap[VIRT_CLINT].size - RISCV_ACLINT_SWI_SIZE; } name = g_strdup_printf("/soc/mtimer@%lx", addr); qemu_fdt_add_subnode(ms->fdt, name); @@ -420,14 +422,15 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, g_free(name); if (s->aia_type != VIRT_AIA_TYPE_APLIC_IMSIC) { - addr = memmap[VIRT_ACLINT_SSWI].base + - (memmap[VIRT_ACLINT_SSWI].size * socket); + addr = s->memmap[VIRT_ACLINT_SSWI].base + + (s->memmap[VIRT_ACLINT_SSWI].size * socket); + name = g_strdup_printf("/soc/sswi@%lx", addr); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-sswi"); qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, addr, 0x0, memmap[VIRT_ACLINT_SSWI].size); + 0x0, addr, 0x0, s->memmap[VIRT_ACLINT_SSWI].size); qemu_fdt_setprop(ms->fdt, name, "interrupts-extended", aclint_sswi_cells, aclint_cells_size); qemu_fdt_setprop(ms->fdt, name, "interrupt-controller", NULL, 0); @@ -438,7 +441,7 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, } static void create_fdt_socket_plic(RISCVVirtState *s, - const MemMapEntry *memmap, int socket, + int socket, uint32_t *phandle, uint32_t *intc_phandles, uint32_t *plic_phandles) { @@ -452,7 +455,8 @@ static void create_fdt_socket_plic(RISCVVirtState *s, }; plic_phandles[socket] = (*phandle)++; - plic_addr = memmap[VIRT_PLIC].base + (memmap[VIRT_PLIC].size * socket); + plic_addr = s->memmap[VIRT_PLIC].base + + (s->memmap[VIRT_PLIC].size * socket); plic_name = g_strdup_printf("/soc/plic@%lx", plic_addr); qemu_fdt_add_subnode(ms->fdt, plic_name); qemu_fdt_setprop_cell(ms->fdt, plic_name, @@ -491,7 +495,7 @@ static void create_fdt_socket_plic(RISCVVirtState *s, } qemu_fdt_setprop_cells(ms->fdt, plic_name, "reg", - 0x0, plic_addr, 0x0, memmap[VIRT_PLIC].size); + 0x0, plic_addr, 0x0, s->memmap[VIRT_PLIC].size); qemu_fdt_setprop_cell(ms->fdt, plic_name, "riscv,ndev", VIRT_IRQCHIP_NUM_SOURCES - 1); riscv_socket_fdt_write_id(ms, plic_name, socket); @@ -500,8 +504,8 @@ static void create_fdt_socket_plic(RISCVVirtState *s, if (!socket) { platform_bus_add_all_fdt_nodes(ms->fdt, plic_name, - memmap[VIRT_PLATFORM_BUS].base, - memmap[VIRT_PLATFORM_BUS].size, + s->memmap[VIRT_PLATFORM_BUS].base, + s->memmap[VIRT_PLATFORM_BUS].size, VIRT_PLATFORM_BUS_IRQ); } } @@ -588,7 +592,7 @@ static void create_fdt_one_imsic(RISCVVirtState *s, hwaddr base_addr, qemu_fdt_setprop_cell(ms->fdt, imsic_name, "phandle", msi_phandle); } -static void create_fdt_imsic(RISCVVirtState *s, const MemMapEntry *memmap, +static void create_fdt_imsic(RISCVVirtState *s, uint32_t *phandle, uint32_t *intc_phandles, uint32_t *msi_m_phandle, uint32_t *msi_s_phandle) { @@ -597,12 +601,12 @@ static void create_fdt_imsic(RISCVVirtState *s, const MemMapEntry *memmap, if (!kvm_enabled()) { /* M-level IMSIC node */ - create_fdt_one_imsic(s, memmap[VIRT_IMSIC_M].base, intc_phandles, + create_fdt_one_imsic(s, s->memmap[VIRT_IMSIC_M].base, intc_phandles, *msi_m_phandle, true, 0); } /* S-level IMSIC node */ - create_fdt_one_imsic(s, memmap[VIRT_IMSIC_S].base, intc_phandles, + create_fdt_one_imsic(s, s->memmap[VIRT_IMSIC_S].base, intc_phandles, *msi_s_phandle, false, imsic_num_bits(s->aia_guests + 1)); @@ -679,7 +683,7 @@ static void create_fdt_one_aplic(RISCVVirtState *s, int socket, } static void create_fdt_socket_aplic(RISCVVirtState *s, - const MemMapEntry *memmap, int socket, + int socket, uint32_t msi_m_phandle, uint32_t msi_s_phandle, uint32_t *phandle, @@ -696,18 +700,19 @@ static void create_fdt_socket_aplic(RISCVVirtState *s, if (!kvm_enabled()) { /* M-level APLIC node */ - aplic_addr = memmap[VIRT_APLIC_M].base + - (memmap[VIRT_APLIC_M].size * socket); - create_fdt_one_aplic(s, socket, aplic_addr, memmap[VIRT_APLIC_M].size, + aplic_addr = s->memmap[VIRT_APLIC_M].base + + (s->memmap[VIRT_APLIC_M].size * socket); + create_fdt_one_aplic(s, socket, aplic_addr, + s->memmap[VIRT_APLIC_M].size, msi_m_phandle, intc_phandles, aplic_m_phandle, aplic_s_phandle, true, num_harts); } /* S-level APLIC node */ - aplic_addr = memmap[VIRT_APLIC_S].base + - (memmap[VIRT_APLIC_S].size * socket); - create_fdt_one_aplic(s, socket, aplic_addr, memmap[VIRT_APLIC_S].size, + aplic_addr = s->memmap[VIRT_APLIC_S].base + + (s->memmap[VIRT_APLIC_S].size * socket); + create_fdt_one_aplic(s, socket, aplic_addr, s->memmap[VIRT_APLIC_S].size, msi_s_phandle, intc_phandles, aplic_s_phandle, 0, false, num_harts); @@ -715,8 +720,8 @@ static void create_fdt_socket_aplic(RISCVVirtState *s, if (!socket) { g_autofree char *aplic_name = fdt_get_aplic_nodename(aplic_addr); platform_bus_add_all_fdt_nodes(ms->fdt, aplic_name, - memmap[VIRT_PLATFORM_BUS].base, - memmap[VIRT_PLATFORM_BUS].size, + s->memmap[VIRT_PLATFORM_BUS].base, + s->memmap[VIRT_PLATFORM_BUS].size, VIRT_PLATFORM_BUS_IRQ); } @@ -734,7 +739,7 @@ static void create_fdt_pmu(RISCVVirtState *s) riscv_pmu_generate_fdt_node(ms->fdt, hart.pmu_avail_ctrs, pmu_name); } -static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, +static void create_fdt_sockets(RISCVVirtState *s, uint32_t *phandle, uint32_t *irq_mmio_phandle, uint32_t *irq_pcie_phandle, @@ -770,20 +775,20 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, create_fdt_socket_cpus(s, socket, clust_name, phandle, &intc_phandles[phandle_pos]); - create_fdt_socket_memory(s, memmap, socket); + create_fdt_socket_memory(s, socket); if (virt_aclint_allowed() && s->have_aclint) { - create_fdt_socket_aclint(s, memmap, socket, + create_fdt_socket_aclint(s, socket, &intc_phandles[phandle_pos]); } else if (tcg_enabled()) { - create_fdt_socket_clint(s, memmap, socket, + create_fdt_socket_clint(s, socket, &intc_phandles[phandle_pos]); } } if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { - create_fdt_imsic(s, memmap, phandle, intc_phandles, - &msi_m_phandle, &msi_s_phandle); + create_fdt_imsic(s, phandle, intc_phandles, + &msi_m_phandle, &msi_s_phandle); *msi_pcie_phandle = msi_s_phandle; } @@ -792,7 +797,7 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, * mode, we'll use only one APLIC instance. */ if (!virt_use_emulated_aplic(s->aia_type)) { - create_fdt_socket_aplic(s, memmap, 0, + create_fdt_socket_aplic(s, 0, msi_m_phandle, msi_s_phandle, phandle, &intc_phandles[0], xplic_phandles, ms->smp.cpus); @@ -806,11 +811,11 @@ static void create_fdt_sockets(RISCVVirtState *s, const MemMapEntry *memmap, phandle_pos -= s->soc[socket].num_harts; if (s->aia_type == VIRT_AIA_TYPE_NONE) { - create_fdt_socket_plic(s, memmap, socket, phandle, + create_fdt_socket_plic(s, socket, phandle, &intc_phandles[phandle_pos], xplic_phandles); } else { - create_fdt_socket_aplic(s, memmap, socket, + create_fdt_socket_aplic(s, socket, msi_m_phandle, msi_s_phandle, phandle, &intc_phandles[phandle_pos], xplic_phandles, @@ -1126,7 +1131,7 @@ static void finalize_fdt(RISCVVirtState *s) uint32_t irq_pcie_phandle = 1, irq_virtio_phandle = 1; uint32_t iommu_sys_phandle = 1; - create_fdt_sockets(s, s->memmap, &phandle, &irq_mmio_phandle, + create_fdt_sockets(s, &phandle, &irq_mmio_phandle, &irq_pcie_phandle, &irq_virtio_phandle, &msi_pcie_phandle); From a51a88fd5d2784c8f6eaaa386dbb5d621949f3e5 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:58:09 -0300 Subject: [PATCH 1015/2760] hw/riscv/virt.c: use s->memmap in create_fdt_virtio() create_fdt_virtio() can use s->memmap instead of having an extra argument for it. While we're at it rewrite it a little bit to avoid the clunky line in 'name' and code repetition: - declare 'virtio_base' out of the loop since it never changes; - declare a 'size' variable. Use it to calculate the address of the virtio device in an 'addr' variable; - use 'addr' in the 'name' g_strdup_printf(); - use 'addr' and 'size' when creating the 'reg' property. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250429125811.224803-8-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index e66a46f524..37bd720068 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -842,21 +842,24 @@ static void create_fdt_sockets(RISCVVirtState *s, riscv_socket_fdt_write_distance_matrix(ms); } -static void create_fdt_virtio(RISCVVirtState *s, const MemMapEntry *memmap, - uint32_t irq_virtio_phandle) +static void create_fdt_virtio(RISCVVirtState *s, uint32_t irq_virtio_phandle) { int i; MachineState *ms = MACHINE(s); + hwaddr virtio_base = s->memmap[VIRT_VIRTIO].base; for (i = 0; i < VIRTIO_COUNT; i++) { - g_autofree char *name = g_strdup_printf("/soc/virtio_mmio@%lx", - (long)(memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size)); + g_autofree char *name = NULL; + uint64_t size = s->memmap[VIRT_VIRTIO].size; + hwaddr addr = virtio_base + i * size; + + name = g_strdup_printf("/soc/virtio_mmio@%"HWADDR_PRIx, addr); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "virtio,mmio"); qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, memmap[VIRT_VIRTIO].base + i * memmap[VIRT_VIRTIO].size, - 0x0, memmap[VIRT_VIRTIO].size); + 0x0, addr, + 0x0, size); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_virtio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { @@ -1135,7 +1138,7 @@ static void finalize_fdt(RISCVVirtState *s) &irq_pcie_phandle, &irq_virtio_phandle, &msi_pcie_phandle); - create_fdt_virtio(s, s->memmap, irq_virtio_phandle); + create_fdt_virtio(s, irq_virtio_phandle); if (virt_is_iommu_sys_enabled(s)) { create_fdt_iommu_sys(s, irq_mmio_phandle, msi_pcie_phandle, From 621e45271f53d09789155ea89e16cdee34988654 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:58:10 -0300 Subject: [PATCH 1016/2760] hw/riscv/virt.c: use s->memmap in finalize_fdt() functions Change create_fdt_pcie(), create_fdt_reset(), create_fdt_uart() and create_fdt_rtc() to use s->memmap in their logic. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250429125811.224803-9-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 37bd720068..edb6973100 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -872,7 +872,7 @@ static void create_fdt_virtio(RISCVVirtState *s, uint32_t irq_virtio_phandle) } } -static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, +static void create_fdt_pcie(RISCVVirtState *s, uint32_t irq_pcie_phandle, uint32_t msi_pcie_phandle, uint32_t iommu_sys_phandle) @@ -881,7 +881,7 @@ static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, MachineState *ms = MACHINE(s); name = g_strdup_printf("/soc/pci@%lx", - (long) memmap[VIRT_PCIE_ECAM].base); + (long) s->memmap[VIRT_PCIE_ECAM].base); qemu_fdt_setprop_cell(ms->fdt, name, "#address-cells", FDT_PCI_ADDR_CELLS); qemu_fdt_setprop_cell(ms->fdt, name, "#interrupt-cells", @@ -892,19 +892,19 @@ static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, qemu_fdt_setprop_string(ms->fdt, name, "device_type", "pci"); qemu_fdt_setprop_cell(ms->fdt, name, "linux,pci-domain", 0); qemu_fdt_setprop_cells(ms->fdt, name, "bus-range", 0, - memmap[VIRT_PCIE_ECAM].size / PCIE_MMCFG_SIZE_MIN - 1); + s->memmap[VIRT_PCIE_ECAM].size / PCIE_MMCFG_SIZE_MIN - 1); qemu_fdt_setprop(ms->fdt, name, "dma-coherent", NULL, 0); if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { qemu_fdt_setprop_cell(ms->fdt, name, "msi-parent", msi_pcie_phandle); } qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0, - memmap[VIRT_PCIE_ECAM].base, 0, memmap[VIRT_PCIE_ECAM].size); + s->memmap[VIRT_PCIE_ECAM].base, 0, s->memmap[VIRT_PCIE_ECAM].size); qemu_fdt_setprop_sized_cells(ms->fdt, name, "ranges", 1, FDT_PCI_RANGE_IOPORT, 2, 0, - 2, memmap[VIRT_PCIE_PIO].base, 2, memmap[VIRT_PCIE_PIO].size, + 2, s->memmap[VIRT_PCIE_PIO].base, 2, s->memmap[VIRT_PCIE_PIO].size, 1, FDT_PCI_RANGE_MMIO, - 2, memmap[VIRT_PCIE_MMIO].base, - 2, memmap[VIRT_PCIE_MMIO].base, 2, memmap[VIRT_PCIE_MMIO].size, + 2, s->memmap[VIRT_PCIE_MMIO].base, + 2, s->memmap[VIRT_PCIE_MMIO].base, 2, s->memmap[VIRT_PCIE_MMIO].size, 1, FDT_PCI_RANGE_MMIO_64BIT, 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.base, 2, virt_high_pcie_memmap.size); @@ -918,8 +918,7 @@ static void create_fdt_pcie(RISCVVirtState *s, const MemMapEntry *memmap, create_pcie_irq_map(s, ms->fdt, name, irq_pcie_phandle); } -static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap, - uint32_t *phandle) +static void create_fdt_reset(RISCVVirtState *s, uint32_t *phandle) { char *name; uint32_t test_phandle; @@ -927,7 +926,7 @@ static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap, test_phandle = (*phandle)++; name = g_strdup_printf("/soc/test@%lx", - (long)memmap[VIRT_TEST].base); + (long)s->memmap[VIRT_TEST].base); qemu_fdt_add_subnode(ms->fdt, name); { static const char * const compat[3] = { @@ -937,7 +936,7 @@ static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap, (char **)&compat, ARRAY_SIZE(compat)); } qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, memmap[VIRT_TEST].base, 0x0, memmap[VIRT_TEST].size); + 0x0, s->memmap[VIRT_TEST].base, 0x0, s->memmap[VIRT_TEST].size); qemu_fdt_setprop_cell(ms->fdt, name, "phandle", test_phandle); test_phandle = qemu_fdt_get_phandle(ms->fdt, name); g_free(name); @@ -959,18 +958,19 @@ static void create_fdt_reset(RISCVVirtState *s, const MemMapEntry *memmap, g_free(name); } -static void create_fdt_uart(RISCVVirtState *s, const MemMapEntry *memmap, +static void create_fdt_uart(RISCVVirtState *s, uint32_t irq_mmio_phandle) { g_autofree char *name = NULL; MachineState *ms = MACHINE(s); - name = g_strdup_printf("/soc/serial@%lx", (long)memmap[VIRT_UART0].base); + name = g_strdup_printf("/soc/serial@%lx", + (long)s->memmap[VIRT_UART0].base); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "ns16550a"); qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, memmap[VIRT_UART0].base, - 0x0, memmap[VIRT_UART0].size); + 0x0, s->memmap[VIRT_UART0].base, + 0x0, s->memmap[VIRT_UART0].size); qemu_fdt_setprop_cell(ms->fdt, name, "clock-frequency", 3686400); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_mmio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { @@ -983,18 +983,18 @@ static void create_fdt_uart(RISCVVirtState *s, const MemMapEntry *memmap, qemu_fdt_setprop_string(ms->fdt, "/aliases", "serial0", name); } -static void create_fdt_rtc(RISCVVirtState *s, const MemMapEntry *memmap, +static void create_fdt_rtc(RISCVVirtState *s, uint32_t irq_mmio_phandle) { g_autofree char *name = NULL; MachineState *ms = MACHINE(s); - name = g_strdup_printf("/soc/rtc@%lx", (long)memmap[VIRT_RTC].base); + name = g_strdup_printf("/soc/rtc@%lx", (long)s->memmap[VIRT_RTC].base); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "google,goldfish-rtc"); qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, memmap[VIRT_RTC].base, 0x0, memmap[VIRT_RTC].size); + 0x0, s->memmap[VIRT_RTC].base, 0x0, s->memmap[VIRT_RTC].size); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_mmio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { @@ -1144,14 +1144,14 @@ static void finalize_fdt(RISCVVirtState *s) create_fdt_iommu_sys(s, irq_mmio_phandle, msi_pcie_phandle, &iommu_sys_phandle); } - create_fdt_pcie(s, s->memmap, irq_pcie_phandle, msi_pcie_phandle, + create_fdt_pcie(s, irq_pcie_phandle, msi_pcie_phandle, iommu_sys_phandle); - create_fdt_reset(s, s->memmap, &phandle); + create_fdt_reset(s, &phandle); - create_fdt_uart(s, s->memmap, irq_mmio_phandle); + create_fdt_uart(s, irq_mmio_phandle); - create_fdt_rtc(s, s->memmap, irq_mmio_phandle); + create_fdt_rtc(s, irq_mmio_phandle); } static void create_fdt(RISCVVirtState *s) From e7cb99bfd1afc5cf2265a122bcfeab36eff7489a Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Tue, 29 Apr 2025 09:58:11 -0300 Subject: [PATCH 1017/2760] hw/riscv/virt.c: remove 'long' casts in fmt strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can avoid the 'long' casts by using PRIx64 and HWADDR_PRIx on the fmt strings for uint64_t and hwaddr types. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Message-ID: <20250429125811.224803-10-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index edb6973100..0dcced1b49 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -304,12 +304,13 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, static void create_fdt_socket_memory(RISCVVirtState *s, int socket) { g_autofree char *mem_name = NULL; - uint64_t addr, size; + hwaddr addr; + uint64_t size; MachineState *ms = MACHINE(s); addr = s->memmap[VIRT_DRAM].base + riscv_socket_mem_offset(ms, socket); size = riscv_socket_mem_size(ms, socket); - mem_name = g_strdup_printf("/memory@%lx", (long)addr); + mem_name = g_strdup_printf("/memory@%"HWADDR_PRIx, addr); qemu_fdt_add_subnode(ms->fdt, mem_name); qemu_fdt_setprop_cells(ms->fdt, mem_name, "reg", addr >> 32, addr, size >> 32, size); @@ -880,8 +881,8 @@ static void create_fdt_pcie(RISCVVirtState *s, g_autofree char *name = NULL; MachineState *ms = MACHINE(s); - name = g_strdup_printf("/soc/pci@%lx", - (long) s->memmap[VIRT_PCIE_ECAM].base); + name = g_strdup_printf("/soc/pci@%"HWADDR_PRIx, + s->memmap[VIRT_PCIE_ECAM].base); qemu_fdt_setprop_cell(ms->fdt, name, "#address-cells", FDT_PCI_ADDR_CELLS); qemu_fdt_setprop_cell(ms->fdt, name, "#interrupt-cells", @@ -925,8 +926,8 @@ static void create_fdt_reset(RISCVVirtState *s, uint32_t *phandle) MachineState *ms = MACHINE(s); test_phandle = (*phandle)++; - name = g_strdup_printf("/soc/test@%lx", - (long)s->memmap[VIRT_TEST].base); + name = g_strdup_printf("/soc/test@%"HWADDR_PRIx, + s->memmap[VIRT_TEST].base); qemu_fdt_add_subnode(ms->fdt, name); { static const char * const compat[3] = { @@ -964,8 +965,8 @@ static void create_fdt_uart(RISCVVirtState *s, g_autofree char *name = NULL; MachineState *ms = MACHINE(s); - name = g_strdup_printf("/soc/serial@%lx", - (long)s->memmap[VIRT_UART0].base); + name = g_strdup_printf("/soc/serial@%"HWADDR_PRIx, + s->memmap[VIRT_UART0].base); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "ns16550a"); qemu_fdt_setprop_cells(ms->fdt, name, "reg", @@ -989,7 +990,8 @@ static void create_fdt_rtc(RISCVVirtState *s, g_autofree char *name = NULL; MachineState *ms = MACHINE(s); - name = g_strdup_printf("/soc/rtc@%lx", (long)s->memmap[VIRT_RTC].base); + name = g_strdup_printf("/soc/rtc@%"HWADDR_PRIx, + s->memmap[VIRT_RTC].base); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "google,goldfish-rtc"); @@ -1042,8 +1044,8 @@ static void create_fdt_virtio_iommu(RISCVVirtState *s, uint16_t bdf) g_autofree char *iommu_node = NULL; g_autofree char *pci_node = NULL; - pci_node = g_strdup_printf("/soc/pci@%lx", - (long) s->memmap[VIRT_PCIE_ECAM].base); + pci_node = g_strdup_printf("/soc/pci@%"HWADDR_PRIx, + s->memmap[VIRT_PCIE_ECAM].base); iommu_node = g_strdup_printf("%s/virtio_iommu@%x,%x", pci_node, PCI_SLOT(bdf), PCI_FUNC(bdf)); iommu_phandle = qemu_fdt_alloc_phandle(fdt); @@ -1111,8 +1113,8 @@ static void create_fdt_iommu(RISCVVirtState *s, uint16_t bdf) g_autofree char *iommu_node = NULL; g_autofree char *pci_node = NULL; - pci_node = g_strdup_printf("/soc/pci@%lx", - (long) s->memmap[VIRT_PCIE_ECAM].base); + pci_node = g_strdup_printf("/soc/pci@%"HWADDR_PRIx, + s->memmap[VIRT_PCIE_ECAM].base); iommu_node = g_strdup_printf("%s/iommu@%x", pci_node, bdf); iommu_phandle = qemu_fdt_alloc_phandle(fdt); qemu_fdt_add_subnode(fdt, iommu_node); @@ -1181,8 +1183,8 @@ static void create_fdt(RISCVVirtState *s) * The "/soc/pci@..." node is needed for PCIE hotplugs * that might happen before finalize_fdt(). */ - name = g_strdup_printf("/soc/pci@%lx", - (long) s->memmap[VIRT_PCIE_ECAM].base); + name = g_strdup_printf("/soc/pci@%"HWADDR_PRIx, + s->memmap[VIRT_PCIE_ECAM].base); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_add_subnode(ms->fdt, "/chosen"); From 5979f50fa9fdbb3fb49e2b498f84faa7503c8ed1 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Tue, 13 May 2025 23:16:51 -0400 Subject: [PATCH 1018/2760] i386/tcg: Make CPUID_HT and CPUID_EXT3_CMP_LEG supported Since commit c6bd2dd63420 ("i386/cpu: Set up CPUID_HT in x86_cpu_expand_features() instead of cpu_x86_cpuid()") and commit 99a637a86f55 ("i386/cpu: Set and track CPUID_EXT3_CMP_LEG in env->features[FEAT_8000_0001_ECX]"), it gets warnings when booting the VM with vcpus >= 2 and with tcg: qemu-system-x86_64: warning: TCG doesn't support requested feature: CPUID.01H:EDX.ht [bit 28] qemu-system-x86_64: warning: TCG doesn't support requested feature: CPUID.80000001H:ECX.cmp-legacy [bit 1] This is because, after the two commits, CPUID_HT and CPUID_EXT3_CMP_LEG are set in env->features[] when vcpus >=2 (in x86_cpu_expand_features()) later in x86_cpu_filter_features() it will check against the TCG supported bits. However, current TCG doesn't mark the two bits as supported, hence the warnings. Fix it by adding the two bits to the supported bits of TCG since multiple vcpus are supported by TCG. Fixes: c6bd2dd63420 ("i386/cpu: Set up CPUID_HT in x86_cpu_expand_features() instead of cpu_x86_cpuid()") Fixes: 99a637a86f55 ("i386/cpu: Set and track CPUID_EXT3_CMP_LEG in env->features[FEAT_8000_0001_ECX]") Reported-by: Ewan Hai Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250514031652.838763-2-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index ec908d7d36..9689f6374e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -776,11 +776,12 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | CPUID_SEP | \ CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | CPUID_PAT | \ CPUID_PSE36 | CPUID_CLFLUSH | CPUID_ACPI | CPUID_MMX | \ - CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS | CPUID_DE) + CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS | CPUID_DE | \ + CPUID_HT) /* partly implemented: CPUID_MTRR, CPUID_MCA, CPUID_CLFLUSH (needed for Win64) */ /* missing: - CPUID_VME, CPUID_DTS, CPUID_SS, CPUID_HT, CPUID_TM, CPUID_PBE */ + CPUID_VME, CPUID_DTS, CPUID_SS, CPUID_TM, CPUID_PBE */ /* * Kernel-only features that can be shown to usermode programs even if @@ -848,7 +849,8 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #define TCG_EXT3_FEATURES (CPUID_EXT3_LAHF_LM | CPUID_EXT3_SVM | \ CPUID_EXT3_CR8LEG | CPUID_EXT3_ABM | CPUID_EXT3_SSE4A | \ - CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_KERNEL_FEATURES) + CPUID_EXT3_3DNOWPREFETCH | CPUID_EXT3_KERNEL_FEATURES | \ + CPUID_EXT3_CMP_LEG) #define TCG_EXT4_FEATURES 0 From 7a48612306768833f8cc87418a5a53e712f26ac1 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Tue, 13 May 2025 23:16:52 -0400 Subject: [PATCH 1019/2760] i386/hvf: Make CPUID_HT supported Since Commit c6bd2dd63420 ("i386/cpu: Set up CPUID_HT in x86_cpu_expand_features() instead of cpu_x86_cpuid()"), CPUID_HT will be set in env->features[] in x86_cpu_expand_features() when vcpus >= 2. Later in x86_cpu_filter_features() it will check against the HVF supported bits. It will trigger the warning like qemu-system-x86_64: warning: host doesn't support requested feature: CPUID.01H:EDX.ht [bit 28] Add CPUID_HT to HVF supported CPUID bits to fix it. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250514031652.838763-3-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/hvf/x86_cpuid.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/hvf/x86_cpuid.c b/target/i386/hvf/x86_cpuid.c index fa131b18c6..0798a0cbaf 100644 --- a/target/i386/hvf/x86_cpuid.c +++ b/target/i386/hvf/x86_cpuid.c @@ -73,7 +73,7 @@ uint32_t hvf_get_supported_cpuid(uint32_t func, uint32_t idx, CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | CPUID_SEP | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | CPUID_PAT | CPUID_PSE36 | CPUID_CLFLUSH | CPUID_MMX | - CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS; + CPUID_FXSR | CPUID_SSE | CPUID_SSE2 | CPUID_SS | CPUID_HT; ecx &= CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_SSSE3 | CPUID_EXT_FMA | CPUID_EXT_CX16 | CPUID_EXT_PCID | CPUID_EXT_SSE41 | CPUID_EXT_SSE42 | CPUID_EXT_MOVBE | From e5894fd6f411c113e2b5f62811e96eeb5b084381 Mon Sep 17 00:00:00 2001 From: Rakesh Jeyasingh Date: Tue, 29 Apr 2025 22:33:53 +0530 Subject: [PATCH 1020/2760] hw/pci-host/gt64120: Fix endianness handling The GT-64120 PCI controller requires special handling where: 1. Host bridge(bus 0 ,device 0) must never be byte-swapped 2. Other devices follow MByteSwap bit in GT_PCI0_CMD The previous implementation incorrectly swapped all accesses, breaking host bridge detection (lspci -d 11ab:4620). Changes made: 1. Removed gt64120_update_pci_cfgdata_mapping() and moved data_mem initialization to gt64120_realize() for cleaner setup 2. Implemented custom read/write handlers that: - Preserve host bridge accesses (extract32(config_reg,11,13)==0) - apply swapping only for non-bridge devices in big-endian mode Fixes: 145e2198 ("hw/mips/gt64xxx_pci: Endian-swap using PCI_HOST_BRIDGE MemoryRegionOps") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2826 Signed-off-by: Rakesh Jeyasingh Tested-by: Thomas Huth Link: https://lore.kernel.org/r/20250429170354.150581-2-rakeshjb010@gmail.com Signed-off-by: Paolo Bonzini --- hw/pci-host/gt64120.c | 82 +++++++++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 34 deletions(-) diff --git a/hw/pci-host/gt64120.c b/hw/pci-host/gt64120.c index 56a6ef93b7..b12a25696c 100644 --- a/hw/pci-host/gt64120.c +++ b/hw/pci-host/gt64120.c @@ -320,38 +320,6 @@ static void gt64120_isd_mapping(GT64120State *s) memory_region_transaction_commit(); } -static void gt64120_update_pci_cfgdata_mapping(GT64120State *s) -{ - /* Indexed on MByteSwap bit, see Table 158: PCI_0 Command, Offset: 0xc00 */ - static const MemoryRegionOps *pci_host_data_ops[] = { - &pci_host_data_be_ops, &pci_host_data_le_ops - }; - PCIHostState *phb = PCI_HOST_BRIDGE(s); - - memory_region_transaction_begin(); - - /* - * The setting of the MByteSwap bit and MWordSwap bit in the PCI Internal - * Command Register determines how data transactions from the CPU to/from - * PCI are handled along with the setting of the Endianness bit in the CPU - * Configuration Register. See: - * - Table 16: 32-bit PCI Transaction Endianness - * - Table 158: PCI_0 Command, Offset: 0xc00 - */ - - if (memory_region_is_mapped(&phb->data_mem)) { - memory_region_del_subregion(&s->ISD_mem, &phb->data_mem); - object_unparent(OBJECT(&phb->data_mem)); - } - memory_region_init_io(&phb->data_mem, OBJECT(phb), - pci_host_data_ops[s->regs[GT_PCI0_CMD] & 1], - s, "pci-conf-data", 4); - memory_region_add_subregion_overlap(&s->ISD_mem, GT_PCI0_CFGDATA << 2, - &phb->data_mem, 1); - - memory_region_transaction_commit(); -} - static void gt64120_pci_mapping(GT64120State *s) { memory_region_transaction_begin(); @@ -645,7 +613,6 @@ static void gt64120_writel(void *opaque, hwaddr addr, case GT_PCI0_CMD: case GT_PCI1_CMD: s->regs[saddr] = val & 0x0401fc0f; - gt64120_update_pci_cfgdata_mapping(s); break; case GT_PCI0_TOR: case GT_PCI0_BS_SCS10: @@ -1024,6 +991,48 @@ static const MemoryRegionOps isd_mem_ops = { }, }; +static bool bswap(const GT64120State *s) +{ + PCIHostState *phb = PCI_HOST_BRIDGE(s); + /*check for bus == 0 && device == 0, Bits 11:15 = Device , Bits 16:23 = Bus*/ + bool is_phb_dev0 = extract32(phb->config_reg, 11, 13) == 0; + bool le_mode = FIELD_EX32(s->regs[GT_PCI0_CMD], GT_PCI0_CMD, MByteSwap); + /* Only swap for non-bridge devices in big-endian mode */ + return !le_mode && !is_phb_dev0; +} + +static uint64_t gt64120_pci_data_read(void *opaque, hwaddr addr, unsigned size) +{ + GT64120State *s = opaque; + uint32_t val = pci_host_data_le_ops.read(opaque, addr, size); + + if (bswap(s)) { + val = bswap32(val); + } + return val; +} + +static void gt64120_pci_data_write(void *opaque, hwaddr addr, + uint64_t val, unsigned size) +{ + GT64120State *s = opaque; + + if (bswap(s)) { + val = bswap32(val); + } + pci_host_data_le_ops.write(opaque, addr, val, size); +} + +static const MemoryRegionOps gt64120_pci_data_ops = { + .read = gt64120_pci_data_read, + .write = gt64120_pci_data_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid = { + .min_access_size = 4, + .max_access_size = 4, + }, +}; + static void gt64120_reset(DeviceState *dev) { GT64120State *s = GT64120_PCI_HOST_BRIDGE(dev); @@ -1178,7 +1187,6 @@ static void gt64120_reset(DeviceState *dev) gt64120_isd_mapping(s); gt64120_pci_mapping(s); - gt64120_update_pci_cfgdata_mapping(s); } static void gt64120_realize(DeviceState *dev, Error **errp) @@ -1202,6 +1210,12 @@ static void gt64120_realize(DeviceState *dev, Error **errp) memory_region_add_subregion_overlap(&s->ISD_mem, GT_PCI0_CFGADDR << 2, &phb->conf_mem, 1); + memory_region_init_io(&phb->data_mem, OBJECT(phb), + >64120_pci_data_ops, + s, "pci-conf-data", 4); + memory_region_add_subregion_overlap(&s->ISD_mem, GT_PCI0_CFGDATA << 2, + &phb->data_mem, 1); + /* * The whole address space decoded by the GT-64120A doesn't generate From 560375cff3ccedabf1fe5ca1bc7a31b13fdc68e5 Mon Sep 17 00:00:00 2001 From: Rakesh Jeyasingh Date: Tue, 29 Apr 2025 22:33:54 +0530 Subject: [PATCH 1021/2760] hw/pci-host: Remove unused pci_host_data_be_ops MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pci_host_data_be_ops became unused after endianness fixes Suggested-by: Paolo Bonzini Signed-off-by: Rakesh Jeyasingh Reviewed-by: Philippe Mathieu-Daudé Tested-by: Thomas Huth Link: https://lore.kernel.org/r/20250429170354.150581-3-rakeshjb010@gmail.com Signed-off-by: Paolo Bonzini --- hw/pci/pci_host.c | 6 ------ include/hw/pci-host/dino.h | 4 ---- include/hw/pci/pci_host.h | 1 - 3 files changed, 11 deletions(-) diff --git a/hw/pci/pci_host.c b/hw/pci/pci_host.c index abe83bbab8..7179d99178 100644 --- a/hw/pci/pci_host.c +++ b/hw/pci/pci_host.c @@ -217,12 +217,6 @@ const MemoryRegionOps pci_host_data_le_ops = { .endianness = DEVICE_LITTLE_ENDIAN, }; -const MemoryRegionOps pci_host_data_be_ops = { - .read = pci_host_data_read, - .write = pci_host_data_write, - .endianness = DEVICE_BIG_ENDIAN, -}; - static bool pci_host_needed(void *opaque) { PCIHostState *s = opaque; diff --git a/include/hw/pci-host/dino.h b/include/hw/pci-host/dino.h index fd7975c798..5dc8cdf610 100644 --- a/include/hw/pci-host/dino.h +++ b/include/hw/pci-host/dino.h @@ -109,10 +109,6 @@ static const uint32_t reg800_keep_bits[DINO800_REGS] = { struct DinoState { PCIHostState parent_obj; - /* - * PCI_CONFIG_ADDR is parent_obj.config_reg, via pci_host_conf_be_ops, - * so that we can map PCI_CONFIG_DATA to pci_host_data_be_ops. - */ uint32_t config_reg_dino; /* keep original copy, including 2 lowest bits */ uint32_t iar0; diff --git a/include/hw/pci/pci_host.h b/include/hw/pci/pci_host.h index e52d8ec2cd..954dd446fa 100644 --- a/include/hw/pci/pci_host.h +++ b/include/hw/pci/pci_host.h @@ -68,6 +68,5 @@ uint32_t pci_data_read(PCIBus *s, uint32_t addr, unsigned len); extern const MemoryRegionOps pci_host_conf_le_ops; extern const MemoryRegionOps pci_host_conf_be_ops; extern const MemoryRegionOps pci_host_data_le_ops; -extern const MemoryRegionOps pci_host_data_be_ops; #endif /* PCI_HOST_H */ From a1b3e82773c4b9f438c2d77f9101fca760b347e6 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 16 May 2025 17:11:29 +0800 Subject: [PATCH 1022/2760] qapi/misc-target: Rename SGXEPCSection to SgxEpcSection QAPI requires strict PascalCase naming style, i.e., only the first letter of a single word is allowed to be uppercase, which could help with readability. Rename SGXEPCSection to SgxEpcSection. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250516091130.2374221-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/sgx.c | 18 +++++++++--------- qapi/misc-target.json | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index 5685c4fb80..3c601689eb 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -84,10 +84,10 @@ static uint64_t sgx_calc_section_metric(uint64_t low, uint64_t high) ((high & MAKE_64BIT_MASK(0, 20)) << 32); } -static SGXEPCSectionList *sgx_calc_host_epc_sections(void) +static SgxEpcSectionList *sgx_calc_host_epc_sections(void) { - SGXEPCSectionList *head = NULL, **tail = &head; - SGXEPCSection *section; + SgxEpcSectionList *head = NULL, **tail = &head; + SgxEpcSection *section; uint32_t i, type; uint32_t eax, ebx, ecx, edx; uint32_t j = 0; @@ -104,7 +104,7 @@ static SGXEPCSectionList *sgx_calc_host_epc_sections(void) break; } - section = g_new0(SGXEPCSection, 1); + section = g_new0(SgxEpcSection, 1); section->node = j++; section->size = sgx_calc_section_metric(ecx, edx); QAPI_LIST_APPEND(tail, section); @@ -183,17 +183,17 @@ SGXInfo *qmp_query_sgx_capabilities(Error **errp) return info; } -static SGXEPCSectionList *sgx_get_epc_sections_list(void) +static SgxEpcSectionList *sgx_get_epc_sections_list(void) { GSList *device_list = sgx_epc_get_device_list(); - SGXEPCSectionList *head = NULL, **tail = &head; - SGXEPCSection *section; + SgxEpcSectionList *head = NULL, **tail = &head; + SgxEpcSection *section; for (; device_list; device_list = device_list->next) { DeviceState *dev = device_list->data; Object *obj = OBJECT(dev); - section = g_new0(SGXEPCSection, 1); + section = g_new0(SgxEpcSection, 1); section->node = object_property_get_uint(obj, SGX_EPC_NUMA_NODE_PROP, &error_abort); section->size = object_property_get_uint(obj, SGX_EPC_SIZE_PROP, @@ -237,7 +237,7 @@ SGXInfo *qmp_query_sgx(Error **errp) void hmp_info_sgx(Monitor *mon, const QDict *qdict) { Error *err = NULL; - SGXEPCSectionList *section_list, *section; + SgxEpcSectionList *section_list, *section; g_autoptr(SGXInfo) info = qmp_query_sgx(&err); uint64_t size = 0; diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 42e4a7417d..a1275d3873 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -319,7 +319,7 @@ 'if': 'TARGET_ARM' } ## -# @SGXEPCSection: +# @SgxEpcSection: # # Information about intel SGX EPC section info # @@ -329,7 +329,7 @@ # # Since: 7.0 ## -{ 'struct': 'SGXEPCSection', +{ 'struct': 'SgxEpcSection', 'data': { 'node': 'int', 'size': 'uint64'}} @@ -355,7 +355,7 @@ 'sgx1': 'bool', 'sgx2': 'bool', 'flc': 'bool', - 'sections': ['SGXEPCSection']}, + 'sections': ['SgxEpcSection']}, 'if': 'TARGET_I386' } ## From 88aea26d1880f105b00c352485d3bf1f21d55012 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 16 May 2025 17:11:30 +0800 Subject: [PATCH 1023/2760] qapi/misc-target: Rename SGXInfo to SgxInfo QAPI requires strict PascalCase naming style, i.e., only the first letter of a single word is allowed to be uppercase, which could help with readability. Rename SGXInfo to SgxInfo. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250516091130.2374221-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/sgx-stub.c | 4 ++-- hw/i386/sgx.c | 14 +++++++------- qapi/misc-target.json | 12 ++++++------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c index 38ff75e9f3..ccb21a975d 100644 --- a/hw/i386/sgx-stub.c +++ b/hw/i386/sgx-stub.c @@ -10,13 +10,13 @@ void sgx_epc_build_srat(GArray *table_data) { } -SGXInfo *qmp_query_sgx(Error **errp) +SgxInfo *qmp_query_sgx(Error **errp) { error_setg(errp, "SGX support is not compiled in"); return NULL; } -SGXInfo *qmp_query_sgx_capabilities(Error **errp) +SgxInfo *qmp_query_sgx_capabilities(Error **errp) { error_setg(errp, "SGX support is not compiled in"); return NULL; diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index 3c601689eb..c80203b438 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -153,9 +153,9 @@ static void sgx_epc_reset(void *opaque) } } -SGXInfo *qmp_query_sgx_capabilities(Error **errp) +SgxInfo *qmp_query_sgx_capabilities(Error **errp) { - SGXInfo *info = NULL; + SgxInfo *info = NULL; uint32_t eax, ebx, ecx, edx; Error *local_err = NULL; @@ -166,7 +166,7 @@ SGXInfo *qmp_query_sgx_capabilities(Error **errp) return NULL; } - info = g_new0(SGXInfo, 1); + info = g_new0(SgxInfo, 1); host_cpuid(0x7, 0, &eax, &ebx, &ecx, &edx); info->sgx = ebx & (1U << 2) ? true : false; @@ -205,9 +205,9 @@ static SgxEpcSectionList *sgx_get_epc_sections_list(void) return head; } -SGXInfo *qmp_query_sgx(Error **errp) +SgxInfo *qmp_query_sgx(Error **errp) { - SGXInfo *info = NULL; + SgxInfo *info = NULL; X86MachineState *x86ms; PCMachineState *pcms = (PCMachineState *)object_dynamic_cast(qdev_get_machine(), @@ -223,7 +223,7 @@ SGXInfo *qmp_query_sgx(Error **errp) return NULL; } - info = g_new0(SGXInfo, 1); + info = g_new0(SgxInfo, 1); info->sgx = true; info->sgx1 = true; @@ -238,7 +238,7 @@ void hmp_info_sgx(Monitor *mon, const QDict *qdict) { Error *err = NULL; SgxEpcSectionList *section_list, *section; - g_autoptr(SGXInfo) info = qmp_query_sgx(&err); + g_autoptr(SgxInfo) info = qmp_query_sgx(&err); uint64_t size = 0; if (err) { diff --git a/qapi/misc-target.json b/qapi/misc-target.json index a1275d3873..6b3c9d8bd5 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -334,7 +334,7 @@ 'size': 'uint64'}} ## -# @SGXInfo: +# @SgxInfo: # # Information about intel Safe Guard eXtension (SGX) support # @@ -350,7 +350,7 @@ # # Since: 6.2 ## -{ 'struct': 'SGXInfo', +{ 'struct': 'SgxInfo', 'data': { 'sgx': 'bool', 'sgx1': 'bool', 'sgx2': 'bool', @@ -363,7 +363,7 @@ # # Returns information about SGX # -# Returns: @SGXInfo +# Returns: @SgxInfo # # Since: 6.2 # @@ -375,14 +375,14 @@ # "sections": [{"node": 0, "size": 67108864}, # {"node": 1, "size": 29360128}]} } ## -{ 'command': 'query-sgx', 'returns': 'SGXInfo', 'if': 'TARGET_I386' } +{ 'command': 'query-sgx', 'returns': 'SgxInfo', 'if': 'TARGET_I386' } ## # @query-sgx-capabilities: # # Returns information from host SGX capabilities # -# Returns: @SGXInfo +# Returns: @SgxInfo # # Since: 6.2 # @@ -394,7 +394,7 @@ # "section" : [{"node": 0, "size": 67108864}, # {"node": 1, "size": 29360128}]} } ## -{ 'command': 'query-sgx-capabilities', 'returns': 'SGXInfo', 'if': 'TARGET_I386' } +{ 'command': 'query-sgx-capabilities', 'returns': 'SgxInfo', 'if': 'TARGET_I386' } ## From c6b8fb0fb1599cffdfe6603f93bba937c1ecb0b1 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 13 May 2025 22:31:30 +0800 Subject: [PATCH 1024/2760] qapi/misc-target: Fix the doc related SGXEPCSection The "sections" field of SGXInfo is used to gather EPC section information for both the guest and the host. Therefore, delete the "for guest" limitation. Additionally, avoid the abbreviation "info" and use "information" instead. And for SGXEPCSection, delete the redundant word "info". Reported-by: Markus Armbruster Signed-off-by: Zhao Liu Acked-by: Markus Armbruster Link: https://lore.kernel.org/r/20250513143131.2008078-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- qapi/misc-target.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 6b3c9d8bd5..6814708972 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -321,7 +321,7 @@ ## # @SgxEpcSection: # -# Information about intel SGX EPC section info +# Information about intel SGX EPC section # # @node: the numa node # @@ -346,7 +346,7 @@ # # @flc: true if FLC is supported # -# @sections: The EPC sections info for guest (Since: 7.0) +# @sections: The EPC sections information (Since: 7.0) # # Since: 6.2 ## From 7f2131c35c1781ca41c62dc26fd93282e1351323 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 13 May 2025 22:31:31 +0800 Subject: [PATCH 1025/2760] qapi/misc-target: Fix the doc to distinguish query-sgx and query-sgx-capabilities There're 2 QMP commands: query-sgx and query-sgx-capabilities, but their outputs are very similar and the documentation lacks clear differentiation. From the codes, query-sgx is used to gather guest's SGX capabilities (including SGX related CPUIDs and EPC sections' size, in SGXInfo), and if guest doesn't have SGX, then QEMU will report the error message. On the other hand, query-sgx-capabilities is used to gather host's SGX capabilities (descripted by SGXInfo as well). And if host doesn't support SGX, then QEMU will also report the error message. Considering that SGXInfo is already documented and both these 2 commands have enough error messages (for the exception case in their codes). Therefore the QAPI documentation for these two commands only needs to emphasize that one of them applies to the guest and the other to the host. Fix their documentation to reflect this difference. Reported-by: Markus Armbruster Suggested-by: Paolo Bonzini Signed-off-by: Zhao Liu Acked-by: Markus Armbruster Link: https://lore.kernel.org/r/20250513143131.2008078-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- qapi/misc-target.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qapi/misc-target.json b/qapi/misc-target.json index 6814708972..f7ec695caa 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -361,7 +361,7 @@ ## # @query-sgx: # -# Returns information about SGX +# Returns information about configured SGX capabilities of guest # # Returns: @SgxInfo # @@ -380,7 +380,7 @@ ## # @query-sgx-capabilities: # -# Returns information from host SGX capabilities +# Returns information about SGX capabilities of host # # Returns: @SgxInfo # From 82c81c07e83670befc61333e0bdf3d810e581219 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Feb 2025 12:00:14 +0100 Subject: [PATCH 1026/2760] hw/riscv: acpi: only create RHCT MMU entry for supported types Do not create the RHCT MMU type entry for RV32 CPUs, since it only has definitions for SV39/SV48/SV57. Likewise, check that satp_mode_max_from_map() will actually return a valid value, skipping the MMU type entry if all MMU types were disabled on the command line. Acked-by: Alistair Francis Signed-off-by: Paolo Bonzini --- hw/riscv/virt-acpi-build.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index 1eef2fb4eb..e693d529e1 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -288,6 +288,7 @@ static void build_rhct(GArray *table_data, RISCVCPU *cpu = &s->soc[0].harts[0]; uint32_t mmu_offset = 0; uint8_t satp_mode_max; + bool rv32 = riscv_cpu_is_32bit(cpu); g_autofree char *isa = NULL; AcpiTable table = { .sig = "RHCT", .rev = 1, .oem_id = s->oem_id, @@ -307,7 +308,8 @@ static void build_rhct(GArray *table_data, num_rhct_nodes++; } - if (cpu->cfg.satp_mode.supported != 0) { + if (!rv32 && cpu->cfg.satp_mode.supported != 0 && + (cpu->cfg.satp_mode.map & ~(1 << VM_1_10_MBARE))) { num_rhct_nodes++; } @@ -367,7 +369,8 @@ static void build_rhct(GArray *table_data, } /* MMU node structure */ - if (cpu->cfg.satp_mode.supported != 0) { + if (!rv32 && cpu->cfg.satp_mode.supported != 0 && + (cpu->cfg.satp_mode.map & ~(1 << VM_1_10_MBARE))) { satp_mode_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map); mmu_offset = table_data->len - table.table_offset; build_append_int_noprefix(table_data, 2, 2); /* Type */ @@ -382,7 +385,7 @@ static void build_rhct(GArray *table_data, } else if (satp_mode_max == VM_1_10_SV39) { build_append_int_noprefix(table_data, 0, 1); /* Sv39 */ } else { - assert(1); + g_assert_not_reached(); } } From b22cfa0f44e360d09595705cea8c97be692e2080 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Feb 2025 13:04:22 +0100 Subject: [PATCH 1027/2760] target/riscv: assert argument to set_satp_mode_max_supported is valid Check that the argument to set_satp_mode_max_supported is valid for the MXL value of the CPU. It would be a bug in the CPU definition if it weren't. In fact, there is such a bug in riscv_bare_cpu_init(): not just SV64 is not a valid VM mode for 32-bit CPUs, SV64 is not a valid VM mode at all, not yet at least. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index d92874baa0..0f7ce5305b 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -444,6 +444,8 @@ static void set_satp_mode_max_supported(RISCVCPU *cpu, cpu->cfg.satp_mode.supported |= (1 << i); } } + + assert(cpu->cfg.satp_mode.supported & (1 << satp_mode)); } /* Set the satp mode to the max supported */ @@ -1497,7 +1499,9 @@ static void riscv_bare_cpu_init(Object *obj) * satp_mode manually (see set_satp_mode_default()). */ #ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(cpu, VM_1_10_SV64); + set_satp_mode_max_supported(RISCV_CPU(obj), + riscv_cpu_mxl(&RISCV_CPU(obj)->env) == MXL_RV32 ? + VM_1_10_SV32 : VM_1_10_SV57); #endif } From 357ce8171a9c7581ba02475874c8c28ed5220d9e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Feb 2025 10:52:09 +0100 Subject: [PATCH 1028/2760] target/riscv: cpu: store max SATP mode as a single integer The maximum available SATP mode implies all the shorter virtual address sizes. Store it in RISCVCPUConfig and avoid recomputing it via satp_mode_max_from_map. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 11 +++++------ target/riscv/cpu_cfg.h | 1 + target/riscv/tcg/tcg-cpu.c | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 0f7ce5305b..32c283a662 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -446,6 +446,7 @@ static void set_satp_mode_max_supported(RISCVCPU *cpu, } assert(cpu->cfg.satp_mode.supported & (1 << satp_mode)); + cpu->cfg.max_satp_mode = satp_mode; } /* Set the satp mode to the max supported */ @@ -1172,16 +1173,13 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) { bool rv32 = riscv_cpu_is_32bit(cpu); - uint8_t satp_mode_map_max, satp_mode_supported_max; + uint8_t satp_mode_map_max; /* The CPU wants the OS to decide which satp mode to use */ if (cpu->cfg.satp_mode.supported == 0) { return; } - satp_mode_supported_max = - satp_mode_max_from_map(cpu->cfg.satp_mode.supported); - if (cpu->cfg.satp_mode.map == 0) { if (cpu->cfg.satp_mode.init == 0) { /* If unset by the user, we fallback to the default satp mode. */ @@ -1210,10 +1208,10 @@ static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) satp_mode_map_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map); /* Make sure the user asked for a supported configuration (HW and qemu) */ - if (satp_mode_map_max > satp_mode_supported_max) { + if (satp_mode_map_max > cpu->cfg.max_satp_mode) { error_setg(errp, "satp_mode %s is higher than hw max capability %s", satp_mode_str(satp_mode_map_max, rv32), - satp_mode_str(satp_mode_supported_max, rv32)); + satp_mode_str(cpu->cfg.max_satp_mode, rv32)); return; } @@ -1473,6 +1471,7 @@ static void riscv_cpu_init(Object *obj) cpu->cfg.cbop_blocksize = 64; cpu->cfg.cboz_blocksize = 64; cpu->env.vext_ver = VEXT_VERSION_1_00_0; + cpu->cfg.max_satp_mode = -1; } static void riscv_bare_cpu_init(Object *obj) diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index cfe371b829..c8ea5cdc87 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -196,6 +196,7 @@ struct RISCVCPUConfig { bool short_isa_string; + int8_t max_satp_mode; RISCVSATPMap satp_mode; }; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 55e00972b7..ab8659f304 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -816,8 +816,9 @@ static bool riscv_cpu_validate_profile_satp(RISCVCPU *cpu, RISCVCPUProfile *profile, bool send_warn) { - int satp_max = satp_mode_max_from_map(cpu->cfg.satp_mode.supported); + int satp_max = cpu->cfg.max_satp_mode; + assert(satp_max >= 0); if (profile->satp_mode > satp_max) { if (send_warn) { bool is_32bit = riscv_cpu_is_32bit(cpu); From 211c7f9bb817ca7bb7855535da4db5ca80a8aa1d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Feb 2025 11:09:15 +0100 Subject: [PATCH 1029/2760] target/riscv: update max_satp_mode based on QOM properties Almost all users of cpu->cfg.satp_mode care about the "max" value satp_mode_max_from_map(cpu->cfg.satp_mode.map). Convert the QOM properties back into it. For TCG, deduce the bitmap of supported modes from valid_vm[]. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- hw/riscv/virt-acpi-build.c | 14 +++++--------- hw/riscv/virt.c | 5 ++--- target/riscv/cpu.c | 27 ++++++++++----------------- target/riscv/cpu.h | 1 - target/riscv/csr.c | 9 +++++++-- 5 files changed, 24 insertions(+), 32 deletions(-) diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index e693d529e1..8b5683dbde 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -287,7 +287,6 @@ static void build_rhct(GArray *table_data, uint32_t isa_offset, num_rhct_nodes, cmo_offset = 0; RISCVCPU *cpu = &s->soc[0].harts[0]; uint32_t mmu_offset = 0; - uint8_t satp_mode_max; bool rv32 = riscv_cpu_is_32bit(cpu); g_autofree char *isa = NULL; @@ -308,8 +307,7 @@ static void build_rhct(GArray *table_data, num_rhct_nodes++; } - if (!rv32 && cpu->cfg.satp_mode.supported != 0 && - (cpu->cfg.satp_mode.map & ~(1 << VM_1_10_MBARE))) { + if (!rv32 && cpu->cfg.max_satp_mode >= VM_1_10_SV39) { num_rhct_nodes++; } @@ -369,20 +367,18 @@ static void build_rhct(GArray *table_data, } /* MMU node structure */ - if (!rv32 && cpu->cfg.satp_mode.supported != 0 && - (cpu->cfg.satp_mode.map & ~(1 << VM_1_10_MBARE))) { - satp_mode_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map); + if (!rv32 && cpu->cfg.max_satp_mode >= VM_1_10_SV39) { mmu_offset = table_data->len - table.table_offset; build_append_int_noprefix(table_data, 2, 2); /* Type */ build_append_int_noprefix(table_data, 8, 2); /* Length */ build_append_int_noprefix(table_data, 0x1, 2); /* Revision */ build_append_int_noprefix(table_data, 0, 1); /* Reserved */ /* MMU Type */ - if (satp_mode_max == VM_1_10_SV57) { + if (cpu->cfg.max_satp_mode == VM_1_10_SV57) { build_append_int_noprefix(table_data, 2, 1); /* Sv57 */ - } else if (satp_mode_max == VM_1_10_SV48) { + } else if (cpu->cfg.max_satp_mode == VM_1_10_SV48) { build_append_int_noprefix(table_data, 1, 1); /* Sv48 */ - } else if (satp_mode_max == VM_1_10_SV39) { + } else if (cpu->cfg.max_satp_mode == VM_1_10_SV39) { build_append_int_noprefix(table_data, 0, 1); /* Sv39 */ } else { g_assert_not_reached(); diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 0dcced1b49..cf280a92e5 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -237,10 +237,10 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, uint32_t cpu_phandle; MachineState *ms = MACHINE(s); bool is_32_bit = riscv_is_32bit(&s->soc[0]); - uint8_t satp_mode_max; for (cpu = s->soc[socket].num_harts - 1; cpu >= 0; cpu--) { RISCVCPU *cpu_ptr = &s->soc[socket].harts[cpu]; + int8_t satp_mode_max = cpu_ptr->cfg.max_satp_mode; g_autofree char *cpu_name = NULL; g_autofree char *core_name = NULL; g_autofree char *intc_name = NULL; @@ -252,8 +252,7 @@ static void create_fdt_socket_cpus(RISCVVirtState *s, int socket, s->soc[socket].hartid_base + cpu); qemu_fdt_add_subnode(ms->fdt, cpu_name); - if (cpu_ptr->cfg.satp_mode.supported != 0) { - satp_mode_max = satp_mode_max_from_map(cpu_ptr->cfg.satp_mode.map); + if (satp_mode_max != -1) { sv_name = g_strdup_printf("riscv,%s", satp_mode_str(satp_mode_max, is_32_bit)); qemu_fdt_setprop_string(ms->fdt, cpu_name, "mmu-type", sv_name); diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 32c283a662..48576bff92 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -389,7 +389,7 @@ static uint8_t satp_mode_from_str(const char *satp_mode_str) g_assert_not_reached(); } -uint8_t satp_mode_max_from_map(uint32_t map) +static uint8_t satp_mode_max_from_map(uint32_t map) { /* * 'map = 0' will make us return (31 - 32), which C will @@ -455,15 +455,13 @@ static void set_satp_mode_default_map(RISCVCPU *cpu) /* * Bare CPUs do not default to the max available. * Users must set a valid satp_mode in the command - * line. + * line. Otherwise, leave the existing max_satp_mode + * in place. */ if (object_dynamic_cast(OBJECT(cpu), TYPE_RISCV_BARE_CPU) != NULL) { warn_report("No satp mode set. Defaulting to 'bare'"); - cpu->cfg.satp_mode.map = (1 << VM_1_10_MBARE); - return; + cpu->cfg.max_satp_mode = VM_1_10_MBARE; } - - cpu->cfg.satp_mode.map = cpu->cfg.satp_mode.supported; } #endif @@ -1175,8 +1173,8 @@ static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) bool rv32 = riscv_cpu_is_32bit(cpu); uint8_t satp_mode_map_max; - /* The CPU wants the OS to decide which satp mode to use */ - if (cpu->cfg.satp_mode.supported == 0) { + if (cpu->cfg.max_satp_mode == -1) { + /* The CPU wants the hypervisor to decide which satp mode to allow */ return; } @@ -1195,14 +1193,14 @@ static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) (cpu->cfg.satp_mode.supported & (1 << i))) { for (int j = i - 1; j >= 0; --j) { if (cpu->cfg.satp_mode.supported & (1 << j)) { - cpu->cfg.satp_mode.map |= (1 << j); - break; + cpu->cfg.max_satp_mode = j; + return; } } - break; } } } + return; } satp_mode_map_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map); @@ -1232,12 +1230,7 @@ static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) } } - /* Finally expand the map so that all valid modes are set */ - for (int i = satp_mode_map_max - 1; i >= 0; --i) { - if (cpu->cfg.satp_mode.supported & (1 << i)) { - cpu->cfg.satp_mode.map |= (1 << i); - } - } + cpu->cfg.max_satp_mode = satp_mode_map_max; } #endif diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index b56d3afa69..3d89a4a83b 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -930,7 +930,6 @@ void riscv_cpu_register_gdb_regs_for_features(CPUState *cs); target_ulong riscv_new_csr_seed(target_ulong new_value, target_ulong write_mask); -uint8_t satp_mode_max_from_map(uint32_t map); const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit); /* Implemented in th_csr.c */ diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 288edeedea..9843fd2191 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -1912,8 +1912,13 @@ static RISCVException read_mstatus(CPURISCVState *env, int csrno, static bool validate_vm(CPURISCVState *env, target_ulong vm) { - uint64_t mode_supported = riscv_cpu_cfg(env)->satp_mode.map; - return get_field(mode_supported, (1 << vm)); + bool rv32 = riscv_cpu_mxl(env) == MXL_RV32; + RISCVCPU *cpu = env_archcpu(env); + int satp_mode_supported_max = cpu->cfg.max_satp_mode; + const bool *valid_vm = rv32 ? valid_vm_1_10_32 : valid_vm_1_10_64; + + assert(satp_mode_supported_max >= 0); + return vm <= satp_mode_supported_max && valid_vm[vm]; } static target_ulong legalize_xatp(CPURISCVState *env, target_ulong old_xatp, From dabb54c160b84d648f375d8f7688fb1099ba32ab Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Feb 2025 11:09:15 +0100 Subject: [PATCH 1030/2760] target/riscv: remove supported from RISCVSATPMap "supported" can be computed on the fly based on the max_satp_mode. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 30 ++++++++++++++++++++++-------- target/riscv/cpu_cfg.h | 4 +--- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 48576bff92..0326cd8e56 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -439,14 +439,27 @@ static void set_satp_mode_max_supported(RISCVCPU *cpu, bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32; const bool *valid_vm = rv32 ? valid_vm_1_10_32 : valid_vm_1_10_64; + assert(valid_vm[satp_mode]); + cpu->cfg.max_satp_mode = satp_mode; +} + +static bool get_satp_mode_supported(RISCVCPU *cpu, uint16_t *supported) +{ + bool rv32 = riscv_cpu_is_32bit(cpu); + const bool *valid_vm = rv32 ? valid_vm_1_10_32 : valid_vm_1_10_64; + int satp_mode = cpu->cfg.max_satp_mode; + + if (satp_mode == -1) { + return false; + } + + *supported = 0; for (int i = 0; i <= satp_mode; ++i) { if (valid_vm[i]) { - cpu->cfg.satp_mode.supported |= (1 << i); + *supported |= (1 << i); } } - - assert(cpu->cfg.satp_mode.supported & (1 << satp_mode)); - cpu->cfg.max_satp_mode = satp_mode; + return true; } /* Set the satp mode to the max supported */ @@ -1171,9 +1184,10 @@ static void riscv_cpu_disas_set_info(CPUState *s, disassemble_info *info) static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) { bool rv32 = riscv_cpu_is_32bit(cpu); + uint16_t supported; uint8_t satp_mode_map_max; - if (cpu->cfg.max_satp_mode == -1) { + if (!get_satp_mode_supported(cpu, &supported)) { /* The CPU wants the hypervisor to decide which satp mode to allow */ return; } @@ -1190,9 +1204,9 @@ static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) */ for (int i = 1; i < 16; ++i) { if ((cpu->cfg.satp_mode.init & (1 << i)) && - (cpu->cfg.satp_mode.supported & (1 << i))) { + supported & (1 << i)) { for (int j = i - 1; j >= 0; --j) { - if (cpu->cfg.satp_mode.supported & (1 << j)) { + if (supported & (1 << j)) { cpu->cfg.max_satp_mode = j; return; } @@ -1221,7 +1235,7 @@ static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) for (int i = satp_mode_map_max - 1; i >= 0; --i) { if (!(cpu->cfg.satp_mode.map & (1 << i)) && (cpu->cfg.satp_mode.init & (1 << i)) && - (cpu->cfg.satp_mode.supported & (1 << i))) { + (supported & (1 << i))) { error_setg(errp, "cannot disable %s satp mode if %s " "is enabled", satp_mode_str(i, false), satp_mode_str(satp_mode_map_max, false)); diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index c8ea5cdc87..8b80e03c9a 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -29,11 +29,9 @@ * * init is a 16-bit bitmap used to make sure the user selected a correct * configuration as per the specification. - * - * supported is a 16-bit bitmap used to reflect the hw capabilities. */ typedef struct { - uint16_t map, init, supported; + uint16_t map, init; } RISCVSATPMap; struct RISCVCPUConfig { From 80b22be3820f1076d9de1b1f1646ae6b352d7675 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Feb 2025 11:27:12 +0100 Subject: [PATCH 1031/2760] target/riscv: move satp_mode.{map,init} out of CPUConfig They are used to provide the nice QOM properties for svNN, but the canonical source of the CPU configuration is now cpu->cfg.max_satp_mode. Store them in the ArchCPU struct. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 32 ++++++++++++++++---------------- target/riscv/cpu.h | 14 ++++++++++++++ target/riscv/cpu_cfg.h | 14 -------------- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 0326cd8e56..54a996c292 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1192,8 +1192,8 @@ static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) return; } - if (cpu->cfg.satp_mode.map == 0) { - if (cpu->cfg.satp_mode.init == 0) { + if (cpu->satp_modes.map == 0) { + if (cpu->satp_modes.init == 0) { /* If unset by the user, we fallback to the default satp mode. */ set_satp_mode_default_map(cpu); } else { @@ -1203,7 +1203,7 @@ static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) * valid_vm_1_10_32/64. */ for (int i = 1; i < 16; ++i) { - if ((cpu->cfg.satp_mode.init & (1 << i)) && + if ((cpu->satp_modes.init & (1 << i)) && supported & (1 << i)) { for (int j = i - 1; j >= 0; --j) { if (supported & (1 << j)) { @@ -1217,7 +1217,7 @@ static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) return; } - satp_mode_map_max = satp_mode_max_from_map(cpu->cfg.satp_mode.map); + satp_mode_map_max = satp_mode_max_from_map(cpu->satp_modes.map); /* Make sure the user asked for a supported configuration (HW and qemu) */ if (satp_mode_map_max > cpu->cfg.max_satp_mode) { @@ -1233,8 +1233,8 @@ static void riscv_cpu_satp_mode_finalize(RISCVCPU *cpu, Error **errp) */ if (!rv32) { for (int i = satp_mode_map_max - 1; i >= 0; --i) { - if (!(cpu->cfg.satp_mode.map & (1 << i)) && - (cpu->cfg.satp_mode.init & (1 << i)) && + if (!(cpu->satp_modes.map & (1 << i)) && + (cpu->satp_modes.init & (1 << i)) && (supported & (1 << i))) { error_setg(errp, "cannot disable %s satp mode if %s " "is enabled", satp_mode_str(i, false), @@ -1322,11 +1322,11 @@ bool riscv_cpu_accelerator_compatible(RISCVCPU *cpu) static void cpu_riscv_get_satp(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - RISCVSATPMap *satp_map = opaque; + RISCVSATPModes *satp_modes = opaque; uint8_t satp = satp_mode_from_str(name); bool value; - value = satp_map->map & (1 << satp); + value = satp_modes->map & (1 << satp); visit_type_bool(v, name, &value, errp); } @@ -1334,7 +1334,7 @@ static void cpu_riscv_get_satp(Object *obj, Visitor *v, const char *name, static void cpu_riscv_set_satp(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { - RISCVSATPMap *satp_map = opaque; + RISCVSATPModes *satp_modes = opaque; uint8_t satp = satp_mode_from_str(name); bool value; @@ -1342,8 +1342,8 @@ static void cpu_riscv_set_satp(Object *obj, Visitor *v, const char *name, return; } - satp_map->map = deposit32(satp_map->map, satp, 1, value); - satp_map->init |= 1 << satp; + satp_modes->map = deposit32(satp_modes->map, satp, 1, value); + satp_modes->init |= 1 << satp; } void riscv_add_satp_mode_properties(Object *obj) @@ -1352,16 +1352,16 @@ void riscv_add_satp_mode_properties(Object *obj) if (cpu->env.misa_mxl == MXL_RV32) { object_property_add(obj, "sv32", "bool", cpu_riscv_get_satp, - cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode); + cpu_riscv_set_satp, NULL, &cpu->satp_modes); } else { object_property_add(obj, "sv39", "bool", cpu_riscv_get_satp, - cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode); + cpu_riscv_set_satp, NULL, &cpu->satp_modes); object_property_add(obj, "sv48", "bool", cpu_riscv_get_satp, - cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode); + cpu_riscv_set_satp, NULL, &cpu->satp_modes); object_property_add(obj, "sv57", "bool", cpu_riscv_get_satp, - cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode); + cpu_riscv_set_satp, NULL, &cpu->satp_modes); object_property_add(obj, "sv64", "bool", cpu_riscv_get_satp, - cpu_riscv_set_satp, NULL, &cpu->cfg.satp_mode); + cpu_riscv_set_satp, NULL, &cpu->satp_modes); } } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 3d89a4a83b..731ea2540c 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -499,6 +499,19 @@ struct CPUArchState { uint64_t rnmi_excpvec; }; +/* + * map is a 16-bit bitmap: the most significant set bit in map is the maximum + * satp mode that is supported. It may be chosen by the user and must respect + * what qemu implements (valid_1_10_32/64) and what the hw is capable of + * (supported bitmap below). + * + * init is a 16-bit bitmap used to make sure the user selected a correct + * configuration as per the specification. + */ +typedef struct { + uint16_t map, init; +} RISCVSATPModes; + /* * RISCVCPU: * @env: #CPURISCVState @@ -515,6 +528,7 @@ struct ArchCPU { /* Configuration Settings */ RISCVCPUConfig cfg; + RISCVSATPModes satp_modes; QEMUTimer *pmu_timer; /* A bitmask of Available programmable counters */ diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 8b80e03c9a..8fa73c8a07 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -21,19 +21,6 @@ #ifndef RISCV_CPU_CFG_H #define RISCV_CPU_CFG_H -/* - * map is a 16-bit bitmap: the most significant set bit in map is the maximum - * satp mode that is supported. It may be chosen by the user and must respect - * what qemu implements (valid_1_10_32/64) and what the hw is capable of - * (supported bitmap below). - * - * init is a 16-bit bitmap used to make sure the user selected a correct - * configuration as per the specification. - */ -typedef struct { - uint16_t map, init; -} RISCVSATPMap; - struct RISCVCPUConfig { bool ext_zba; bool ext_zbb; @@ -195,7 +182,6 @@ struct RISCVCPUConfig { bool short_isa_string; int8_t max_satp_mode; - RISCVSATPMap satp_mode; }; typedef struct RISCVCPUConfig RISCVCPUConfig; From 71fb3aa5ebba5ba822371f864a12dbcded08147d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 13:12:09 +0100 Subject: [PATCH 1032/2760] target/riscv: introduce RISCVCPUDef Start putting all the CPU definitions in a struct. Later this will replace instance_init functions with declarative code, for now just remove the ugly cast of class_data. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 27 ++++++++++++++++++--------- target/riscv/cpu.h | 4 ++++ 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 54a996c292..6e92fbb992 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3073,8 +3073,9 @@ static void riscv_cpu_common_class_init(ObjectClass *c, const void *data) static void riscv_cpu_class_init(ObjectClass *c, const void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); + const RISCVCPUDef *def = data; - mcc->misa_mxl_max = (RISCVMXL)GPOINTER_TO_UINT(data); + mcc->misa_mxl_max = def->misa_mxl_max; riscv_cpu_validate_misa_mxl(mcc); } @@ -3170,40 +3171,48 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) } #endif -#define DEFINE_DYNAMIC_CPU(type_name, misa_mxl_max, initfn) \ +#define DEFINE_DYNAMIC_CPU(type_name, misa_mxl_max_, initfn) \ { \ .name = (type_name), \ .parent = TYPE_RISCV_DYNAMIC_CPU, \ .instance_init = (initfn), \ .class_init = riscv_cpu_class_init, \ - .class_data = GUINT_TO_POINTER(misa_mxl_max) \ + .class_data = &(const RISCVCPUDef) { \ + .misa_mxl_max = (misa_mxl_max_), \ + }, \ } -#define DEFINE_VENDOR_CPU(type_name, misa_mxl_max, initfn) \ +#define DEFINE_VENDOR_CPU(type_name, misa_mxl_max_, initfn) \ { \ .name = (type_name), \ .parent = TYPE_RISCV_VENDOR_CPU, \ .instance_init = (initfn), \ .class_init = riscv_cpu_class_init, \ - .class_data = GUINT_TO_POINTER(misa_mxl_max) \ + .class_data = &(const RISCVCPUDef) { \ + .misa_mxl_max = (misa_mxl_max_), \ + }, \ } -#define DEFINE_BARE_CPU(type_name, misa_mxl_max, initfn) \ +#define DEFINE_BARE_CPU(type_name, misa_mxl_max_, initfn) \ { \ .name = (type_name), \ .parent = TYPE_RISCV_BARE_CPU, \ .instance_init = (initfn), \ .class_init = riscv_cpu_class_init, \ - .class_data = GUINT_TO_POINTER(misa_mxl_max) \ + .class_data = &(const RISCVCPUDef) { \ + .misa_mxl_max = (misa_mxl_max_), \ + }, \ } -#define DEFINE_PROFILE_CPU(type_name, misa_mxl_max, initfn) \ +#define DEFINE_PROFILE_CPU(type_name, misa_mxl_max_, initfn) \ { \ .name = (type_name), \ .parent = TYPE_RISCV_BARE_CPU, \ .instance_init = (initfn), \ .class_init = riscv_cpu_class_init, \ - .class_data = GUINT_TO_POINTER(misa_mxl_max) \ + .class_data = &(const RISCVCPUDef) { \ + .misa_mxl_max = (misa_mxl_max_), \ + }, \ } static const TypeInfo riscv_cpu_type_infos[] = { diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 731ea2540c..9de3f716ea 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -538,6 +538,10 @@ struct ArchCPU { const GPtrArray *decoders; }; +typedef struct RISCVCPUDef { + RISCVMXL misa_mxl_max; /* max mxl for this cpu */ +} RISCVCPUDef; + /** * RISCVCPUClass: * @parent_realize: The parent class' realize handler. From 5fd23f20e12a56e7ac2dabbe9570fb2f10d7c5b4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 13:13:23 +0100 Subject: [PATCH 1033/2760] target/riscv: store RISCVCPUDef struct directly in the class Prepare for adding more fields to RISCVCPUDef and reading them in riscv_cpu_init: instead of storing the misa_mxl_max field in RISCVCPUClass, ensure that there's always a valid RISCVCPUDef struct and go through it. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- hw/riscv/boot.c | 2 +- target/riscv/cpu.c | 23 ++++++++++++++++++----- target/riscv/cpu.h | 2 +- target/riscv/gdbstub.c | 6 +++--- target/riscv/kvm/kvm-cpu.c | 21 +++++++++------------ target/riscv/machine.c | 2 +- target/riscv/tcg/tcg-cpu.c | 10 +++++----- target/riscv/translate.c | 2 +- 8 files changed, 39 insertions(+), 29 deletions(-) diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c index 765b9e2b1a..828a867be3 100644 --- a/hw/riscv/boot.c +++ b/hw/riscv/boot.c @@ -37,7 +37,7 @@ bool riscv_is_32bit(RISCVHartArrayState *harts) { RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(&harts->harts[0]); - return mcc->misa_mxl_max == MXL_RV32; + return mcc->def->misa_mxl_max == MXL_RV32; } /* diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 6e92fbb992..22e3a2211e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -356,7 +356,7 @@ void riscv_cpu_set_misa_ext(CPURISCVState *env, uint32_t ext) int riscv_cpu_max_xlen(RISCVCPUClass *mcc) { - return 16 << mcc->misa_mxl_max; + return 16 << mcc->def->misa_mxl_max; } #ifndef CONFIG_USER_ONLY @@ -1047,7 +1047,7 @@ static void riscv_cpu_reset_hold(Object *obj, ResetType type) mcc->parent_phases.hold(obj, type); } #ifndef CONFIG_USER_ONLY - env->misa_mxl = mcc->misa_mxl_max; + env->misa_mxl = mcc->def->misa_mxl_max; env->priv = PRV_M; env->mstatus &= ~(MSTATUS_MIE | MSTATUS_MPRV); if (env->misa_mxl > MXL_RV32) { @@ -1449,7 +1449,7 @@ static void riscv_cpu_init(Object *obj) RISCVCPU *cpu = RISCV_CPU(obj); CPURISCVState *env = &cpu->env; - env->misa_mxl = mcc->misa_mxl_max; + env->misa_mxl = mcc->def->misa_mxl_max; #ifndef CONFIG_USER_ONLY qdev_init_gpio_in(DEVICE(obj), riscv_cpu_set_irq, @@ -1543,7 +1543,7 @@ static void riscv_cpu_validate_misa_mxl(RISCVCPUClass *mcc) CPUClass *cc = CPU_CLASS(mcc); /* Validate that MISA_MXL is set properly. */ - switch (mcc->misa_mxl_max) { + switch (mcc->def->misa_mxl_max) { #ifdef TARGET_RISCV64 case MXL_RV64: case MXL_RV128: @@ -3070,12 +3070,24 @@ static void riscv_cpu_common_class_init(ObjectClass *c, const void *data) device_class_set_props(dc, riscv_cpu_properties); } +static void riscv_cpu_class_base_init(ObjectClass *c, const void *data) +{ + RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); + RISCVCPUClass *pcc = RISCV_CPU_CLASS(object_class_get_parent(c)); + + if (pcc->def) { + mcc->def = g_memdup2(pcc->def, sizeof(*pcc->def)); + } else { + mcc->def = g_new0(RISCVCPUDef, 1); + } +} + static void riscv_cpu_class_init(ObjectClass *c, const void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); const RISCVCPUDef *def = data; - mcc->misa_mxl_max = def->misa_mxl_max; + mcc->def->misa_mxl_max = def->misa_mxl_max; riscv_cpu_validate_misa_mxl(mcc); } @@ -3226,6 +3238,7 @@ static const TypeInfo riscv_cpu_type_infos[] = { .abstract = true, .class_size = sizeof(RISCVCPUClass), .class_init = riscv_cpu_common_class_init, + .class_base_init = riscv_cpu_class_base_init, }, { .name = TYPE_RISCV_DYNAMIC_CPU, diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 9de3f716ea..d2d4db95c1 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -554,7 +554,7 @@ struct RISCVCPUClass { DeviceRealize parent_realize; ResettablePhases parent_phases; - RISCVMXL misa_mxl_max; /* max mxl for this cpu */ + RISCVCPUDef *def; }; static inline int riscv_has_ext(CPURISCVState *env, target_ulong ext) diff --git a/target/riscv/gdbstub.c b/target/riscv/gdbstub.c index 18e88f416a..1934f919c0 100644 --- a/target/riscv/gdbstub.c +++ b/target/riscv/gdbstub.c @@ -62,7 +62,7 @@ int riscv_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n) return 0; } - switch (mcc->misa_mxl_max) { + switch (mcc->def->misa_mxl_max) { case MXL_RV32: return gdb_get_reg32(mem_buf, tmp); case MXL_RV64: @@ -82,7 +82,7 @@ int riscv_cpu_gdb_write_register(CPUState *cs, uint8_t *mem_buf, int n) int length = 0; target_ulong tmp; - switch (mcc->misa_mxl_max) { + switch (mcc->def->misa_mxl_max) { case MXL_RV32: tmp = (int32_t)ldl_p(mem_buf); length = 4; @@ -359,7 +359,7 @@ void riscv_cpu_register_gdb_regs_for_features(CPUState *cs) ricsv_gen_dynamic_vector_feature(cs, cs->gdb_num_regs), 0); } - switch (mcc->misa_mxl_max) { + switch (mcc->def->misa_mxl_max) { case MXL_RV32: gdb_register_coprocessor(cs, riscv_gdb_get_virtual, riscv_gdb_set_virtual, diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 82f9728636..e77f612af3 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -2086,22 +2086,19 @@ static void kvm_cpu_accel_register_types(void) } type_init(kvm_cpu_accel_register_types); -static void riscv_host_cpu_class_init(ObjectClass *c, const void *data) -{ - RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); - -#if defined(TARGET_RISCV32) - mcc->misa_mxl_max = MXL_RV32; -#elif defined(TARGET_RISCV64) - mcc->misa_mxl_max = MXL_RV64; -#endif -} - static const TypeInfo riscv_kvm_cpu_type_infos[] = { { .name = TYPE_RISCV_CPU_HOST, .parent = TYPE_RISCV_CPU, - .class_init = riscv_host_cpu_class_init, +#if defined(TARGET_RISCV32) + .class_data = &(const RISCVCPUDef) { + .misa_mxl_max = MXL_RV32, + }, +#elif defined(TARGET_RISCV64) + .class_data = &(const RISCVCPUDef) { + .misa_mxl_max = MXL_RV64, + }, +#endif } }; diff --git a/target/riscv/machine.c b/target/riscv/machine.c index a1f70cc955..c97e9ce9df 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -170,7 +170,7 @@ static bool rv128_needed(void *opaque) { RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(opaque); - return mcc->misa_mxl_max == MXL_RV128; + return mcc->def->misa_mxl_max == MXL_RV128; } static const VMStateDescription vmstate_rv128 = { diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index ab8659f304..305912b8dd 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -691,7 +691,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - if (mcc->misa_mxl_max != MXL_RV32 && cpu->cfg.ext_zcf) { + if (mcc->def->misa_mxl_max != MXL_RV32 && cpu->cfg.ext_zcf) { error_setg(errp, "Zcf extension is only relevant to RV32"); return; } @@ -788,7 +788,7 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) return; } - if (mcc->misa_mxl_max == MXL_RV32 && cpu->cfg.ext_svukte) { + if (mcc->def->misa_mxl_max == MXL_RV32 && cpu->cfg.ext_svukte) { error_setg(errp, "svukte is not supported for RV32"); return; } @@ -1026,7 +1026,7 @@ static void cpu_enable_zc_implied_rules(RISCVCPU *cpu) cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmp), true); cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcmt), true); - if (riscv_has_ext(env, RVF) && mcc->misa_mxl_max == MXL_RV32) { + if (riscv_has_ext(env, RVF) && mcc->def->misa_mxl_max == MXL_RV32) { cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true); } } @@ -1035,7 +1035,7 @@ static void cpu_enable_zc_implied_rules(RISCVCPU *cpu) if (riscv_has_ext(env, RVC) && env->priv_ver >= PRIV_VERSION_1_12_0) { cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zca), true); - if (riscv_has_ext(env, RVF) && mcc->misa_mxl_max == MXL_RV32) { + if (riscv_has_ext(env, RVF) && mcc->def->misa_mxl_max == MXL_RV32) { cpu_cfg_ext_auto_update(cpu, CPU_CFG_OFFSET(ext_zcf), true); } @@ -1161,7 +1161,7 @@ static bool riscv_tcg_cpu_realize(CPUState *cs, Error **errp) #ifndef CONFIG_USER_ONLY RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(cpu); - if (mcc->misa_mxl_max >= MXL_RV128 && qemu_tcg_mttcg_enabled()) { + if (mcc->def->misa_mxl_max >= MXL_RV128 && qemu_tcg_mttcg_enabled()) { /* Missing 128-bit aligned atomics */ error_setg(errp, "128-bit RISC-V currently does not work with Multi " diff --git a/target/riscv/translate.c b/target/riscv/translate.c index 0d4f7d601c..d7a6de02df 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1276,7 +1276,7 @@ static void riscv_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->cfg_vta_all_1s = cpu->cfg.rvv_ta_all_1s; ctx->vstart_eq_zero = FIELD_EX32(tb_flags, TB_FLAGS, VSTART_EQ_ZERO); ctx->vl_eq_vlmax = FIELD_EX32(tb_flags, TB_FLAGS, VL_EQ_VLMAX); - ctx->misa_mxl_max = mcc->misa_mxl_max; + ctx->misa_mxl_max = mcc->def->misa_mxl_max; ctx->xl = FIELD_EX32(tb_flags, TB_FLAGS, XL); ctx->address_xl = FIELD_EX32(tb_flags, TB_FLAGS, AXL); ctx->cs = cs; From 7f9e15d82d21714a2c82aff8869b8ef9aa191c98 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 13:41:49 +0100 Subject: [PATCH 1034/2760] target/riscv: merge riscv_cpu_class_init with the class_base function Since all TYPE_RISCV_CPU subclasses support a class_data of type RISCVCPUDef, process it even before calling the .class_init function for the subclasses. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 22e3a2211e..334791eebd 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3080,15 +3080,18 @@ static void riscv_cpu_class_base_init(ObjectClass *c, const void *data) } else { mcc->def = g_new0(RISCVCPUDef, 1); } -} -static void riscv_cpu_class_init(ObjectClass *c, const void *data) -{ - RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); - const RISCVCPUDef *def = data; + if (data) { + const RISCVCPUDef *def = data; + if (def->misa_mxl_max) { + assert(def->misa_mxl_max <= MXL_RV128); + mcc->def->misa_mxl_max = def->misa_mxl_max; + } + } - mcc->def->misa_mxl_max = def->misa_mxl_max; - riscv_cpu_validate_misa_mxl(mcc); + if (!object_class_is_abstract(c)) { + riscv_cpu_validate_misa_mxl(mcc); + } } static void riscv_isa_string_ext(RISCVCPU *cpu, char **isa_str, @@ -3188,7 +3191,6 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) .name = (type_name), \ .parent = TYPE_RISCV_DYNAMIC_CPU, \ .instance_init = (initfn), \ - .class_init = riscv_cpu_class_init, \ .class_data = &(const RISCVCPUDef) { \ .misa_mxl_max = (misa_mxl_max_), \ }, \ @@ -3199,7 +3201,6 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) .name = (type_name), \ .parent = TYPE_RISCV_VENDOR_CPU, \ .instance_init = (initfn), \ - .class_init = riscv_cpu_class_init, \ .class_data = &(const RISCVCPUDef) { \ .misa_mxl_max = (misa_mxl_max_), \ }, \ @@ -3210,7 +3211,6 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) .name = (type_name), \ .parent = TYPE_RISCV_BARE_CPU, \ .instance_init = (initfn), \ - .class_init = riscv_cpu_class_init, \ .class_data = &(const RISCVCPUDef) { \ .misa_mxl_max = (misa_mxl_max_), \ }, \ @@ -3221,7 +3221,6 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) .name = (type_name), \ .parent = TYPE_RISCV_BARE_CPU, \ .instance_init = (initfn), \ - .class_init = riscv_cpu_class_init, \ .class_data = &(const RISCVCPUDef) { \ .misa_mxl_max = (misa_mxl_max_), \ }, \ From 12877e739b3afff2b6ff7b80d6a6b22e451671c2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Feb 2025 11:28:23 +0100 Subject: [PATCH 1035/2760] target/riscv: move RISCVCPUConfig fields to a header file To support merging a subclass's RISCVCPUDef into the superclass, a list of all the CPU features is needed. Put them into a header file that can be included multiple times, expanding the macros BOOL_FIELD and TYPE_FIELD to different operations. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu_cfg.h | 163 +--------------------------- target/riscv/cpu_cfg_fields.h.inc | 170 ++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+), 160 deletions(-) create mode 100644 target/riscv/cpu_cfg_fields.h.inc diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index 8fa73c8a07..e9bf75730a 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -22,166 +22,9 @@ #define RISCV_CPU_CFG_H struct RISCVCPUConfig { - bool ext_zba; - bool ext_zbb; - bool ext_zbc; - bool ext_zbkb; - bool ext_zbkc; - bool ext_zbkx; - bool ext_zbs; - bool ext_zca; - bool ext_zcb; - bool ext_zcd; - bool ext_zce; - bool ext_zcf; - bool ext_zcmp; - bool ext_zcmt; - bool ext_zk; - bool ext_zkn; - bool ext_zknd; - bool ext_zkne; - bool ext_zknh; - bool ext_zkr; - bool ext_zks; - bool ext_zksed; - bool ext_zksh; - bool ext_zkt; - bool ext_zifencei; - bool ext_zicntr; - bool ext_zicsr; - bool ext_zicbom; - bool ext_zicbop; - bool ext_zicboz; - bool ext_zicfilp; - bool ext_zicfiss; - bool ext_zicond; - bool ext_zihintntl; - bool ext_zihintpause; - bool ext_zihpm; - bool ext_zimop; - bool ext_zcmop; - bool ext_ztso; - bool ext_smstateen; - bool ext_sstc; - bool ext_smcdeleg; - bool ext_ssccfg; - bool ext_smcntrpmf; - bool ext_smcsrind; - bool ext_sscsrind; - bool ext_ssdbltrp; - bool ext_smdbltrp; - bool ext_svadu; - bool ext_svinval; - bool ext_svnapot; - bool ext_svpbmt; - bool ext_svvptc; - bool ext_svukte; - bool ext_zdinx; - bool ext_zaamo; - bool ext_zacas; - bool ext_zama16b; - bool ext_zabha; - bool ext_zalrsc; - bool ext_zawrs; - bool ext_zfa; - bool ext_zfbfmin; - bool ext_zfh; - bool ext_zfhmin; - bool ext_zfinx; - bool ext_zhinx; - bool ext_zhinxmin; - bool ext_zve32f; - bool ext_zve32x; - bool ext_zve64f; - bool ext_zve64d; - bool ext_zve64x; - bool ext_zvbb; - bool ext_zvbc; - bool ext_zvkb; - bool ext_zvkg; - bool ext_zvkned; - bool ext_zvknha; - bool ext_zvknhb; - bool ext_zvksed; - bool ext_zvksh; - bool ext_zvkt; - bool ext_zvkn; - bool ext_zvknc; - bool ext_zvkng; - bool ext_zvks; - bool ext_zvksc; - bool ext_zvksg; - bool ext_zmmul; - bool ext_zvfbfmin; - bool ext_zvfbfwma; - bool ext_zvfh; - bool ext_zvfhmin; - bool ext_smaia; - bool ext_ssaia; - bool ext_smctr; - bool ext_ssctr; - bool ext_sscofpmf; - bool ext_smepmp; - bool ext_smrnmi; - bool ext_ssnpm; - bool ext_smnpm; - bool ext_smmpm; - bool ext_sspm; - bool ext_supm; - bool rvv_ta_all_1s; - bool rvv_ma_all_1s; - bool rvv_vl_half_avl; - - uint32_t mvendorid; - uint64_t marchid; - uint64_t mimpid; - - /* Named features */ - bool ext_svade; - bool ext_zic64b; - bool ext_ssstateen; - bool ext_sha; - - /* - * Always 'true' booleans for named features - * TCG always implement/can't be user disabled, - * based on spec version. - */ - bool has_priv_1_13; - bool has_priv_1_12; - bool has_priv_1_11; - - /* Always enabled for TCG if has_priv_1_11 */ - bool ext_ziccrse; - - /* Vendor-specific custom extensions */ - bool ext_xtheadba; - bool ext_xtheadbb; - bool ext_xtheadbs; - bool ext_xtheadcmo; - bool ext_xtheadcondmov; - bool ext_xtheadfmemidx; - bool ext_xtheadfmv; - bool ext_xtheadmac; - bool ext_xtheadmemidx; - bool ext_xtheadmempair; - bool ext_xtheadsync; - bool ext_XVentanaCondOps; - - uint32_t pmu_mask; - uint16_t vlenb; - uint16_t elen; - uint16_t cbom_blocksize; - uint16_t cbop_blocksize; - uint16_t cboz_blocksize; - bool mmu; - bool pmp; - bool debug; - bool misa_w; - - bool short_isa_string; - - int8_t max_satp_mode; +#define BOOL_FIELD(x) bool x; +#define TYPED_FIELD(type, x) type x; +#include "cpu_cfg_fields.h.inc" }; typedef struct RISCVCPUConfig RISCVCPUConfig; diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc new file mode 100644 index 0000000000..cb86bfc5dc --- /dev/null +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -0,0 +1,170 @@ +/* + * Required definitions before including this file: + * + * #define BOOL_FIELD(x) + * #define TYPED_FIELD(type, x) + */ + +BOOL_FIELD(ext_zba) +BOOL_FIELD(ext_zbb) +BOOL_FIELD(ext_zbc) +BOOL_FIELD(ext_zbkb) +BOOL_FIELD(ext_zbkc) +BOOL_FIELD(ext_zbkx) +BOOL_FIELD(ext_zbs) +BOOL_FIELD(ext_zca) +BOOL_FIELD(ext_zcb) +BOOL_FIELD(ext_zcd) +BOOL_FIELD(ext_zce) +BOOL_FIELD(ext_zcf) +BOOL_FIELD(ext_zcmp) +BOOL_FIELD(ext_zcmt) +BOOL_FIELD(ext_zk) +BOOL_FIELD(ext_zkn) +BOOL_FIELD(ext_zknd) +BOOL_FIELD(ext_zkne) +BOOL_FIELD(ext_zknh) +BOOL_FIELD(ext_zkr) +BOOL_FIELD(ext_zks) +BOOL_FIELD(ext_zksed) +BOOL_FIELD(ext_zksh) +BOOL_FIELD(ext_zkt) +BOOL_FIELD(ext_zifencei) +BOOL_FIELD(ext_zicntr) +BOOL_FIELD(ext_zicsr) +BOOL_FIELD(ext_zicbom) +BOOL_FIELD(ext_zicbop) +BOOL_FIELD(ext_zicboz) +BOOL_FIELD(ext_zicfilp) +BOOL_FIELD(ext_zicfiss) +BOOL_FIELD(ext_zicond) +BOOL_FIELD(ext_zihintntl) +BOOL_FIELD(ext_zihintpause) +BOOL_FIELD(ext_zihpm) +BOOL_FIELD(ext_zimop) +BOOL_FIELD(ext_zcmop) +BOOL_FIELD(ext_ztso) +BOOL_FIELD(ext_smstateen) +BOOL_FIELD(ext_sstc) +BOOL_FIELD(ext_smcdeleg) +BOOL_FIELD(ext_ssccfg) +BOOL_FIELD(ext_smcntrpmf) +BOOL_FIELD(ext_smcsrind) +BOOL_FIELD(ext_sscsrind) +BOOL_FIELD(ext_ssdbltrp) +BOOL_FIELD(ext_smdbltrp) +BOOL_FIELD(ext_svadu) +BOOL_FIELD(ext_svinval) +BOOL_FIELD(ext_svnapot) +BOOL_FIELD(ext_svpbmt) +BOOL_FIELD(ext_svvptc) +BOOL_FIELD(ext_svukte) +BOOL_FIELD(ext_zdinx) +BOOL_FIELD(ext_zaamo) +BOOL_FIELD(ext_zacas) +BOOL_FIELD(ext_zama16b) +BOOL_FIELD(ext_zabha) +BOOL_FIELD(ext_zalrsc) +BOOL_FIELD(ext_zawrs) +BOOL_FIELD(ext_zfa) +BOOL_FIELD(ext_zfbfmin) +BOOL_FIELD(ext_zfh) +BOOL_FIELD(ext_zfhmin) +BOOL_FIELD(ext_zfinx) +BOOL_FIELD(ext_zhinx) +BOOL_FIELD(ext_zhinxmin) +BOOL_FIELD(ext_zve32f) +BOOL_FIELD(ext_zve32x) +BOOL_FIELD(ext_zve64f) +BOOL_FIELD(ext_zve64d) +BOOL_FIELD(ext_zve64x) +BOOL_FIELD(ext_zvbb) +BOOL_FIELD(ext_zvbc) +BOOL_FIELD(ext_zvkb) +BOOL_FIELD(ext_zvkg) +BOOL_FIELD(ext_zvkned) +BOOL_FIELD(ext_zvknha) +BOOL_FIELD(ext_zvknhb) +BOOL_FIELD(ext_zvksed) +BOOL_FIELD(ext_zvksh) +BOOL_FIELD(ext_zvkt) +BOOL_FIELD(ext_zvkn) +BOOL_FIELD(ext_zvknc) +BOOL_FIELD(ext_zvkng) +BOOL_FIELD(ext_zvks) +BOOL_FIELD(ext_zvksc) +BOOL_FIELD(ext_zvksg) +BOOL_FIELD(ext_zmmul) +BOOL_FIELD(ext_zvfbfmin) +BOOL_FIELD(ext_zvfbfwma) +BOOL_FIELD(ext_zvfh) +BOOL_FIELD(ext_zvfhmin) +BOOL_FIELD(ext_smaia) +BOOL_FIELD(ext_ssaia) +BOOL_FIELD(ext_smctr) +BOOL_FIELD(ext_ssctr) +BOOL_FIELD(ext_sscofpmf) +BOOL_FIELD(ext_smepmp) +BOOL_FIELD(ext_smrnmi) +BOOL_FIELD(ext_ssnpm) +BOOL_FIELD(ext_smnpm) +BOOL_FIELD(ext_smmpm) +BOOL_FIELD(ext_sspm) +BOOL_FIELD(ext_supm) +BOOL_FIELD(rvv_ta_all_1s) +BOOL_FIELD(rvv_ma_all_1s) +BOOL_FIELD(rvv_vl_half_avl) +/* Named features */ +BOOL_FIELD(ext_svade) +BOOL_FIELD(ext_zic64b) +BOOL_FIELD(ext_ssstateen) +BOOL_FIELD(ext_sha) + +/* + * Always 'true' booleans for named features + * TCG always implement/can't be user disabled, + * based on spec version. + */ +BOOL_FIELD(has_priv_1_13) +BOOL_FIELD(has_priv_1_12) +BOOL_FIELD(has_priv_1_11) + +/* Always enabled for TCG if has_priv_1_11 */ +BOOL_FIELD(ext_ziccrse) + +/* Vendor-specific custom extensions */ +BOOL_FIELD(ext_xtheadba) +BOOL_FIELD(ext_xtheadbb) +BOOL_FIELD(ext_xtheadbs) +BOOL_FIELD(ext_xtheadcmo) +BOOL_FIELD(ext_xtheadcondmov) +BOOL_FIELD(ext_xtheadfmemidx) +BOOL_FIELD(ext_xtheadfmv) +BOOL_FIELD(ext_xtheadmac) +BOOL_FIELD(ext_xtheadmemidx) +BOOL_FIELD(ext_xtheadmempair) +BOOL_FIELD(ext_xtheadsync) +BOOL_FIELD(ext_XVentanaCondOps) + +BOOL_FIELD(mmu) +BOOL_FIELD(pmp) +BOOL_FIELD(debug) +BOOL_FIELD(misa_w) + +BOOL_FIELD(short_isa_string) + +TYPED_FIELD(uint32_t, mvendorid) +TYPED_FIELD(uint64_t, marchid) +TYPED_FIELD(uint64_t, mimpid) + +TYPED_FIELD(uint32_t, pmu_mask) +TYPED_FIELD(uint16_t, vlenb) +TYPED_FIELD(uint16_t, elen) +TYPED_FIELD(uint16_t, cbom_blocksize) +TYPED_FIELD(uint16_t, cbop_blocksize) +TYPED_FIELD(uint16_t, cboz_blocksize) + +TYPED_FIELD(int8_t, max_satp_mode) + +#undef BOOL_FIELD +#undef TYPED_FIELD From 407254031edd649e6acde736e3f7e95bb0fdf299 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 5 Mar 2025 13:22:48 +0100 Subject: [PATCH 1036/2760] target/riscv: include default value in cpu_cfg_fields.h.inc In preparation for adding a function to merge two RISCVCPUConfigs (pulling values from the parent if they are not overridden) annotate cpu_cfg_fields.h.inc with the default value of the fields. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu_cfg.h | 2 +- target/riscv/cpu_cfg_fields.h.inc | 22 +++++++++++----------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/target/riscv/cpu_cfg.h b/target/riscv/cpu_cfg.h index e9bf75730a..aa28dc8d7e 100644 --- a/target/riscv/cpu_cfg.h +++ b/target/riscv/cpu_cfg.h @@ -23,7 +23,7 @@ struct RISCVCPUConfig { #define BOOL_FIELD(x) bool x; -#define TYPED_FIELD(type, x) type x; +#define TYPED_FIELD(type, x, default) type x; #include "cpu_cfg_fields.h.inc" }; diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index cb86bfc5dc..59f134a419 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -2,7 +2,7 @@ * Required definitions before including this file: * * #define BOOL_FIELD(x) - * #define TYPED_FIELD(type, x) + * #define TYPED_FIELD(type, x, default) */ BOOL_FIELD(ext_zba) @@ -153,18 +153,18 @@ BOOL_FIELD(misa_w) BOOL_FIELD(short_isa_string) -TYPED_FIELD(uint32_t, mvendorid) -TYPED_FIELD(uint64_t, marchid) -TYPED_FIELD(uint64_t, mimpid) +TYPED_FIELD(uint32_t, mvendorid, 0) +TYPED_FIELD(uint64_t, marchid, 0) +TYPED_FIELD(uint64_t, mimpid, 0) -TYPED_FIELD(uint32_t, pmu_mask) -TYPED_FIELD(uint16_t, vlenb) -TYPED_FIELD(uint16_t, elen) -TYPED_FIELD(uint16_t, cbom_blocksize) -TYPED_FIELD(uint16_t, cbop_blocksize) -TYPED_FIELD(uint16_t, cboz_blocksize) +TYPED_FIELD(uint32_t, pmu_mask, 0) +TYPED_FIELD(uint16_t, vlenb, 0) +TYPED_FIELD(uint16_t, elen, 0) +TYPED_FIELD(uint16_t, cbom_blocksize, 0) +TYPED_FIELD(uint16_t, cbop_blocksize, 0) +TYPED_FIELD(uint16_t, cboz_blocksize, 0) -TYPED_FIELD(int8_t, max_satp_mode) +TYPED_FIELD(int8_t, max_satp_mode, -1) #undef BOOL_FIELD #undef TYPED_FIELD From a6ba81424a7e751fbcee40dc1f5826ba29fddd30 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Feb 2025 11:31:30 +0100 Subject: [PATCH 1037/2760] target/riscv: add more RISCVCPUDef fields Allow using RISCVCPUDef to replicate all the logic of custom .instance_init functions. To simulate inheritance, merge the child's RISCVCPUDef with the parent and then finally move it to the CPUState at the end of TYPE_RISCV_CPU's own instance_init function. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 42 +++++++++++++++++++++++++++++++++++++- target/riscv/cpu.h | 4 ++++ target/riscv/kvm/kvm-cpu.c | 6 ++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 334791eebd..634216870e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -73,6 +73,13 @@ bool riscv_cpu_option_set(const char *optname) return g_hash_table_contains(general_user_opts, optname); } +static void riscv_cpu_cfg_merge(RISCVCPUConfig *dest, const RISCVCPUConfig *src) +{ +#define BOOL_FIELD(x) dest->x |= src->x; +#define TYPED_FIELD(type, x, default_) if (src->x != default_) dest->x = src->x; +#include "cpu_cfg_fields.h.inc" +} + #define ISA_EXT_DATA_ENTRY(_name, _min_ver, _prop) \ {#_name, _min_ver, CPU_CFG_OFFSET(_prop)} @@ -434,7 +441,7 @@ const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit) } static void set_satp_mode_max_supported(RISCVCPU *cpu, - uint8_t satp_mode) + int satp_mode) { bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32; const bool *valid_vm = rv32 ? valid_vm_1_10_32 : valid_vm_1_10_64; @@ -1479,6 +1486,16 @@ static void riscv_cpu_init(Object *obj) cpu->cfg.cboz_blocksize = 64; cpu->env.vext_ver = VEXT_VERSION_1_00_0; cpu->cfg.max_satp_mode = -1; + + env->misa_ext_mask = env->misa_ext = mcc->def->misa_ext; + riscv_cpu_cfg_merge(&cpu->cfg, &mcc->def->cfg); + + if (mcc->def->priv_spec != RISCV_PROFILE_ATTR_UNUSED) { + cpu->env.priv_ver = mcc->def->priv_spec; + } + if (mcc->def->vext_spec != RISCV_PROFILE_ATTR_UNUSED) { + cpu->env.vext_ver = mcc->def->vext_spec; + } } static void riscv_bare_cpu_init(Object *obj) @@ -3087,6 +3104,17 @@ static void riscv_cpu_class_base_init(ObjectClass *c, const void *data) assert(def->misa_mxl_max <= MXL_RV128); mcc->def->misa_mxl_max = def->misa_mxl_max; } + if (def->priv_spec != RISCV_PROFILE_ATTR_UNUSED) { + assert(def->priv_spec <= PRIV_VERSION_LATEST); + mcc->def->priv_spec = def->priv_spec; + } + if (def->vext_spec != RISCV_PROFILE_ATTR_UNUSED) { + assert(def->vext_spec != 0); + mcc->def->vext_spec = def->vext_spec; + } + mcc->def->misa_ext |= def->misa_ext; + + riscv_cpu_cfg_merge(&mcc->def->cfg, &def->cfg); } if (!object_class_is_abstract(c)) { @@ -3193,6 +3221,9 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) .instance_init = (initfn), \ .class_data = &(const RISCVCPUDef) { \ .misa_mxl_max = (misa_mxl_max_), \ + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .vext_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .cfg.max_satp_mode = -1, \ }, \ } @@ -3203,6 +3234,9 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) .instance_init = (initfn), \ .class_data = &(const RISCVCPUDef) { \ .misa_mxl_max = (misa_mxl_max_), \ + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .vext_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .cfg.max_satp_mode = -1, \ }, \ } @@ -3213,6 +3247,9 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) .instance_init = (initfn), \ .class_data = &(const RISCVCPUDef) { \ .misa_mxl_max = (misa_mxl_max_), \ + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .vext_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .cfg.max_satp_mode = -1, \ }, \ } @@ -3223,6 +3260,9 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) .instance_init = (initfn), \ .class_data = &(const RISCVCPUDef) { \ .misa_mxl_max = (misa_mxl_max_), \ + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .vext_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .cfg.max_satp_mode = -1, \ }, \ } diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index d2d4db95c1..29b01e9aa8 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -540,6 +540,10 @@ struct ArchCPU { typedef struct RISCVCPUDef { RISCVMXL misa_mxl_max; /* max mxl for this cpu */ + uint32_t misa_ext; + int priv_spec; + int32_t vext_spec; + RISCVCPUConfig cfg; } RISCVCPUDef; /** diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index e77f612af3..efb41fac53 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -2093,10 +2093,16 @@ static const TypeInfo riscv_kvm_cpu_type_infos[] = { #if defined(TARGET_RISCV32) .class_data = &(const RISCVCPUDef) { .misa_mxl_max = MXL_RV32, + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, + .vext_spec = RISCV_PROFILE_ATTR_UNUSED, + .cfg.max_satp_mode = -1, }, #elif defined(TARGET_RISCV64) .class_data = &(const RISCVCPUDef) { .misa_mxl_max = MXL_RV64, + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, + .vext_spec = RISCV_PROFILE_ATTR_UNUSED, + .cfg.max_satp_mode = -1, }, #endif } From 4e012d36c8654e7fa12762002150334bf591628a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 17:03:01 +0100 Subject: [PATCH 1038/2760] target/riscv: convert abstract CPU classes to RISCVCPUDef Start from the top of the hierarchy: dynamic and vendor CPUs are just markers, whereas bare CPUs can have their instance_init function replaced by RISCVCPUDef. The only difference is that the maximum supported SATP mode has to be specified separately for 32-bit and 64-bit modes. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 93 ++++++++++++++++++++++------------------------ target/riscv/cpu.h | 1 + 2 files changed, 46 insertions(+), 48 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 634216870e..7dcaf72fc1 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1474,8 +1474,8 @@ static void riscv_cpu_init(Object *obj) * for all CPUs. Each accelerator will decide what to do when * users disable them. */ - RISCV_CPU(obj)->cfg.ext_zicntr = true; - RISCV_CPU(obj)->cfg.ext_zihpm = true; + RISCV_CPU(obj)->cfg.ext_zicntr = !mcc->def->bare; + RISCV_CPU(obj)->cfg.ext_zihpm = !mcc->def->bare; /* Default values for non-bool cpu properties */ cpu->cfg.pmu_mask = MAKE_64BIT_MASK(3, 16); @@ -1498,36 +1498,6 @@ static void riscv_cpu_init(Object *obj) } } -static void riscv_bare_cpu_init(Object *obj) -{ - RISCVCPU *cpu = RISCV_CPU(obj); - - /* - * Bare CPUs do not inherit the timer and performance - * counters from the parent class (see riscv_cpu_init() - * for info on why the parent enables them). - * - * Users have to explicitly enable these counters for - * bare CPUs. - */ - cpu->cfg.ext_zicntr = false; - cpu->cfg.ext_zihpm = false; - - /* Set to QEMU's first supported priv version */ - cpu->env.priv_ver = PRIV_VERSION_1_10_0; - - /* - * Support all available satp_mode settings. The default - * value will be set to MBARE if the user doesn't set - * satp_mode manually (see set_satp_mode_default()). - */ -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(RISCV_CPU(obj), - riscv_cpu_mxl(&RISCV_CPU(obj)->env) == MXL_RV32 ? - VM_1_10_SV32 : VM_1_10_SV57); -#endif -} - typedef struct misa_ext_info { const char *name; const char *description; @@ -3100,6 +3070,7 @@ static void riscv_cpu_class_base_init(ObjectClass *c, const void *data) if (data) { const RISCVCPUDef *def = data; + mcc->def->bare |= def->bare; if (def->misa_mxl_max) { assert(def->misa_mxl_max <= MXL_RV128); mcc->def->misa_mxl_max = def->misa_mxl_max; @@ -3253,6 +3224,19 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) }, \ } +#define DEFINE_ABSTRACT_RISCV_CPU(type_name, parent_type_name, ...) \ + { \ + .name = (type_name), \ + .parent = (parent_type_name), \ + .abstract = true, \ + .class_data = &(const RISCVCPUDef) { \ + .priv_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .vext_spec = RISCV_PROFILE_ATTR_UNUSED, \ + .cfg.max_satp_mode = -1, \ + __VA_ARGS__ \ + }, \ + } + #define DEFINE_PROFILE_CPU(type_name, misa_mxl_max_, initfn) \ { \ .name = (type_name), \ @@ -3279,22 +3263,35 @@ static const TypeInfo riscv_cpu_type_infos[] = { .class_init = riscv_cpu_common_class_init, .class_base_init = riscv_cpu_class_base_init, }, - { - .name = TYPE_RISCV_DYNAMIC_CPU, - .parent = TYPE_RISCV_CPU, - .abstract = true, - }, - { - .name = TYPE_RISCV_VENDOR_CPU, - .parent = TYPE_RISCV_CPU, - .abstract = true, - }, - { - .name = TYPE_RISCV_BARE_CPU, - .parent = TYPE_RISCV_CPU, - .instance_init = riscv_bare_cpu_init, - .abstract = true, - }, + + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_DYNAMIC_CPU, TYPE_RISCV_CPU), + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_VENDOR_CPU, TYPE_RISCV_CPU), + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_BARE_CPU, TYPE_RISCV_CPU, + /* + * Bare CPUs do not inherit the timer and performance + * counters from the parent class (see riscv_cpu_init() + * for info on why the parent enables them). + * + * Users have to explicitly enable these counters for + * bare CPUs. + */ + .bare = true, + + /* Set to QEMU's first supported priv version */ + .priv_spec = PRIV_VERSION_1_10_0, + + /* + * Support all available satp_mode settings. By default + * only MBARE will be available if the user doesn't enable + * a mode manually (see riscv_cpu_satp_mode_finalize()). + */ +#ifdef TARGET_RISCV32 + .cfg.max_satp_mode = VM_1_10_SV32, +#else + .cfg.max_satp_mode = VM_1_10_SV57, +#endif + ), + #if defined(TARGET_RISCV32) DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_MAX, MXL_RV32, riscv_max_cpu_init), #elif defined(TARGET_RISCV64) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 29b01e9aa8..f3d70afb86 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -544,6 +544,7 @@ typedef struct RISCVCPUDef { int priv_spec; int32_t vext_spec; RISCVCPUConfig cfg; + bool bare; } RISCVCPUDef; /** From 198265df8a71e6743d42d5003c29a060fea7d019 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 27 Feb 2025 14:56:30 +0100 Subject: [PATCH 1039/2760] target/riscv: convert profile CPU models to RISCVCPUDef Profile CPUs reuse the instance_init function for bare CPUs; make them proper subclasses instead. Enabling a profile is now done based on the RISCVCPUDef struct: even though there is room for only one in RISCVCPUDef, subclasses check that the parent class's profile is enabled through the parent profile mechanism. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 85 +++++++++++++++++++++++++--------------------- target/riscv/cpu.h | 1 + 2 files changed, 48 insertions(+), 38 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 7dcaf72fc1..54fce76765 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1487,6 +1487,10 @@ static void riscv_cpu_init(Object *obj) cpu->env.vext_ver = VEXT_VERSION_1_00_0; cpu->cfg.max_satp_mode = -1; + if (mcc->def->profile) { + mcc->def->profile->enabled = true; + } + env->misa_ext_mask = env->misa_ext = mcc->def->misa_ext; riscv_cpu_cfg_merge(&cpu->cfg, &mcc->def->cfg); @@ -2959,36 +2963,6 @@ static const Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("x-misa-w", RISCVCPU, cfg.misa_w, false), }; -#if defined(TARGET_RISCV64) -static void rva22u64_profile_cpu_init(Object *obj) -{ - rv64i_bare_cpu_init(obj); - - RVA22U64.enabled = true; -} - -static void rva22s64_profile_cpu_init(Object *obj) -{ - rv64i_bare_cpu_init(obj); - - RVA22S64.enabled = true; -} - -static void rva23u64_profile_cpu_init(Object *obj) -{ - rv64i_bare_cpu_init(obj); - - RVA23U64.enabled = true; -} - -static void rva23s64_profile_cpu_init(Object *obj) -{ - rv64i_bare_cpu_init(obj); - - RVA23S64.enabled = true; -} -#endif - static const gchar *riscv_gdb_arch_name(CPUState *cs) { RISCVCPU *cpu = RISCV_CPU(cs); @@ -3057,6 +3031,32 @@ static void riscv_cpu_common_class_init(ObjectClass *c, const void *data) device_class_set_props(dc, riscv_cpu_properties); } +static bool profile_extends(RISCVCPUProfile *trial, RISCVCPUProfile *parent) +{ + RISCVCPUProfile *curr; + if (!parent) { + return true; + } + + curr = trial; + while (curr) { + if (curr == parent) { + return true; + } + curr = curr->u_parent; + } + + curr = trial; + while (curr) { + if (curr == parent) { + return true; + } + curr = curr->s_parent; + } + + return false; +} + static void riscv_cpu_class_base_init(ObjectClass *c, const void *data) { RISCVCPUClass *mcc = RISCV_CPU_CLASS(c); @@ -3071,6 +3071,11 @@ static void riscv_cpu_class_base_init(ObjectClass *c, const void *data) if (data) { const RISCVCPUDef *def = data; mcc->def->bare |= def->bare; + if (def->profile) { + assert(profile_extends(def->profile, mcc->def->profile)); + assert(mcc->def->bare); + mcc->def->profile = def->profile; + } if (def->misa_mxl_max) { assert(def->misa_mxl_max <= MXL_RV128); mcc->def->misa_mxl_max = def->misa_mxl_max; @@ -3237,19 +3242,22 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) }, \ } -#define DEFINE_PROFILE_CPU(type_name, misa_mxl_max_, initfn) \ +#define DEFINE_RISCV_CPU(type_name, parent_type_name, ...) \ { \ .name = (type_name), \ - .parent = TYPE_RISCV_BARE_CPU, \ - .instance_init = (initfn), \ + .parent = (parent_type_name), \ .class_data = &(const RISCVCPUDef) { \ - .misa_mxl_max = (misa_mxl_max_), \ .priv_spec = RISCV_PROFILE_ATTR_UNUSED, \ .vext_spec = RISCV_PROFILE_ATTR_UNUSED, \ .cfg.max_satp_mode = -1, \ + __VA_ARGS__ \ }, \ } +#define DEFINE_PROFILE_CPU(type_name, parent_type_name, profile_) \ + DEFINE_RISCV_CPU(type_name, parent_type_name, \ + .profile = &(profile_)) + static const TypeInfo riscv_cpu_type_infos[] = { { .name = TYPE_RISCV_CPU, @@ -3328,10 +3336,11 @@ static const TypeInfo riscv_cpu_type_infos[] = { #endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV64I, MXL_RV64, rv64i_bare_cpu_init), DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV64E, MXL_RV64, rv64e_bare_cpu_init), - DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22U64, MXL_RV64, rva22u64_profile_cpu_init), - DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22S64, MXL_RV64, rva22s64_profile_cpu_init), - DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA23U64, MXL_RV64, rva23u64_profile_cpu_init), - DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA23S64, MXL_RV64, rva23s64_profile_cpu_init), + + DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22U64, TYPE_RISCV_CPU_RV64I, RVA22U64), + DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22S64, TYPE_RISCV_CPU_RV64I, RVA22S64), + DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA23U64, TYPE_RISCV_CPU_RV64I, RVA23U64), + DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA23S64, TYPE_RISCV_CPU_RV64I, RVA23S64), #endif /* TARGET_RISCV64 */ }; diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index f3d70afb86..ed88adef45 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -540,6 +540,7 @@ struct ArchCPU { typedef struct RISCVCPUDef { RISCVMXL misa_mxl_max; /* max mxl for this cpu */ + RISCVCPUProfile *profile; uint32_t misa_ext; int priv_spec; int32_t vext_spec; From 37815d80be11f98636a4ed76fe6b1ecc7aefcaf3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 17:10:46 +0100 Subject: [PATCH 1040/2760] target/riscv: convert bare CPU models to RISCVCPUDef Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 58 ++++++++++++++-------------------------------- 1 file changed, 17 insertions(+), 41 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 54fce76765..8b82a1b7b3 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -732,18 +732,6 @@ static void rv128_base_cpu_init(Object *obj) } #endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ -static void rv64i_bare_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - riscv_cpu_set_misa_ext(env, RVI); -} - -static void rv64e_bare_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - riscv_cpu_set_misa_ext(env, RVE); -} - #endif /* !TARGET_RISCV64 */ #if defined(TARGET_RISCV32) || \ @@ -836,18 +824,6 @@ static void rv32_imafcu_nommu_cpu_init(Object *obj) cpu->cfg.ext_zicsr = true; cpu->cfg.pmp = true; } - -static void rv32i_bare_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - riscv_cpu_set_misa_ext(env, RVI); -} - -static void rv32e_bare_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - riscv_cpu_set_misa_ext(env, RVE); -} #endif static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model) @@ -3216,19 +3192,6 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) }, \ } -#define DEFINE_BARE_CPU(type_name, misa_mxl_max_, initfn) \ - { \ - .name = (type_name), \ - .parent = TYPE_RISCV_BARE_CPU, \ - .instance_init = (initfn), \ - .class_data = &(const RISCVCPUDef) { \ - .misa_mxl_max = (misa_mxl_max_), \ - .priv_spec = RISCV_PROFILE_ATTR_UNUSED, \ - .vext_spec = RISCV_PROFILE_ATTR_UNUSED, \ - .cfg.max_satp_mode = -1, \ - }, \ - } - #define DEFINE_ABSTRACT_RISCV_CPU(type_name, parent_type_name, ...) \ { \ .name = (type_name), \ @@ -3313,8 +3276,15 @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_E31, MXL_RV32, rv32_sifive_e_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_E34, MXL_RV32, rv32_imafcu_nommu_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_U34, MXL_RV32, rv32_sifive_u_cpu_init), - DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV32I, MXL_RV32, rv32i_bare_cpu_init), - DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV32E, MXL_RV32, rv32e_bare_cpu_init), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_RV32I, TYPE_RISCV_BARE_CPU, + .misa_mxl_max = MXL_RV32, + .misa_ext = RVI + ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_RV32E, TYPE_RISCV_BARE_CPU, + .misa_mxl_max = MXL_RV32, + .misa_ext = RVE + ), #endif #if (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) @@ -3334,8 +3304,14 @@ static const TypeInfo riscv_cpu_type_infos[] = { #if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128, MXL_RV128, rv128_base_cpu_init), #endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ - DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV64I, MXL_RV64, rv64i_bare_cpu_init), - DEFINE_BARE_CPU(TYPE_RISCV_CPU_RV64E, MXL_RV64, rv64e_bare_cpu_init), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_RV64I, TYPE_RISCV_BARE_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVI + ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_RV64E, TYPE_RISCV_BARE_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVE + ), DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22U64, TYPE_RISCV_CPU_RV64I, RVA22U64), DEFINE_PROFILE_CPU(TYPE_RISCV_CPU_RVA22S64, TYPE_RISCV_CPU_RV64I, RVA22S64), From 0edc2465ba76c0d2bbeb475b6d2491c92dccd27b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 14:59:05 +0100 Subject: [PATCH 1041/2760] target/riscv: convert dynamic CPU models to RISCVCPUDef Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 113 +++++++++++++-------------------------------- 1 file changed, 31 insertions(+), 82 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 8b82a1b7b3..2b26f23bd0 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -485,38 +485,7 @@ static void set_satp_mode_default_map(RISCVCPU *cpu) } #endif -static void riscv_max_cpu_init(Object *obj) -{ - RISCVCPU *cpu = RISCV_CPU(obj); - CPURISCVState *env = &cpu->env; - - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; - - env->priv_ver = PRIV_VERSION_LATEST; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(RISCV_CPU(obj), - riscv_cpu_mxl(&RISCV_CPU(obj)->env) == MXL_RV32 ? - VM_1_10_SV32 : VM_1_10_SV57); -#endif -} - #if defined(TARGET_RISCV64) -static void rv64_base_cpu_init(Object *obj) -{ - RISCVCPU *cpu = RISCV_CPU(obj); - CPURISCVState *env = &cpu->env; - - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; - - /* Set latest version of privileged specification */ - env->priv_ver = PRIV_VERSION_LATEST; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); -#endif -} - static void rv64_sifive_u_cpu_init(Object *obj) { RISCVCPU *cpu = RISCV_CPU(obj); @@ -717,41 +686,11 @@ static void rv64_xiangshan_nanhu_cpu_init(Object *obj) #endif } -#if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) -static void rv128_base_cpu_init(Object *obj) -{ - RISCVCPU *cpu = RISCV_CPU(obj); - CPURISCVState *env = &cpu->env; - - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; - - /* Set latest version of privileged specification */ - env->priv_ver = PRIV_VERSION_LATEST; - set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV57); -} -#endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ - #endif /* !TARGET_RISCV64 */ #if defined(TARGET_RISCV32) || \ (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) -static void rv32_base_cpu_init(Object *obj) -{ - RISCVCPU *cpu = RISCV_CPU(obj); - CPURISCVState *env = &cpu->env; - - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; - - /* Set latest version of privileged specification */ - env->priv_ver = PRIV_VERSION_LATEST; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32); -#endif -} - static void rv32_sifive_u_cpu_init(Object *obj) { RISCVCPU *cpu = RISCV_CPU(obj); @@ -3166,19 +3105,6 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) } #endif -#define DEFINE_DYNAMIC_CPU(type_name, misa_mxl_max_, initfn) \ - { \ - .name = (type_name), \ - .parent = TYPE_RISCV_DYNAMIC_CPU, \ - .instance_init = (initfn), \ - .class_data = &(const RISCVCPUDef) { \ - .misa_mxl_max = (misa_mxl_max_), \ - .priv_spec = RISCV_PROFILE_ATTR_UNUSED, \ - .vext_spec = RISCV_PROFILE_ATTR_UNUSED, \ - .cfg.max_satp_mode = -1, \ - }, \ - } - #define DEFINE_VENDOR_CPU(type_name, misa_mxl_max_, initfn) \ { \ .name = (type_name), \ @@ -3235,7 +3161,12 @@ static const TypeInfo riscv_cpu_type_infos[] = { .class_base_init = riscv_cpu_class_base_init, }, - DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_DYNAMIC_CPU, TYPE_RISCV_CPU), + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_DYNAMIC_CPU, TYPE_RISCV_CPU, + .cfg.mmu = true, + .cfg.pmp = true, + .priv_spec = PRIV_VERSION_LATEST, + ), + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_VENDOR_CPU, TYPE_RISCV_CPU), DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_BARE_CPU, TYPE_RISCV_CPU, /* @@ -3263,15 +3194,23 @@ static const TypeInfo riscv_cpu_type_infos[] = { #endif ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_MAX, TYPE_RISCV_DYNAMIC_CPU, #if defined(TARGET_RISCV32) - DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_MAX, MXL_RV32, riscv_max_cpu_init), + .misa_mxl_max = MXL_RV32, + .cfg.max_satp_mode = VM_1_10_SV32, #elif defined(TARGET_RISCV64) - DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_MAX, MXL_RV64, riscv_max_cpu_init), + .misa_mxl_max = MXL_RV64, + .cfg.max_satp_mode = VM_1_10_SV57, #endif + ), #if defined(TARGET_RISCV32) || \ (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) - DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE32, MXL_RV32, rv32_base_cpu_init), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE32, TYPE_RISCV_DYNAMIC_CPU, + .cfg.max_satp_mode = VM_1_10_SV32, + .misa_mxl_max = MXL_RV32, + ), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_IBEX, MXL_RV32, rv32_ibex_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_E31, MXL_RV32, rv32_sifive_e_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_E34, MXL_RV32, rv32_imafcu_nommu_cpu_init), @@ -3288,11 +3227,18 @@ static const TypeInfo riscv_cpu_type_infos[] = { #endif #if (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) - DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_MAX32, MXL_RV32, riscv_max_cpu_init), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_MAX32, TYPE_RISCV_DYNAMIC_CPU, + .cfg.max_satp_mode = VM_1_10_SV32, + .misa_mxl_max = MXL_RV32, + ), #endif #if defined(TARGET_RISCV64) - DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE64, MXL_RV64, rv64_base_cpu_init), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE64, TYPE_RISCV_DYNAMIC_CPU, + .cfg.max_satp_mode = VM_1_10_SV57, + .misa_mxl_max = MXL_RV64, + ), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_E51, MXL_RV64, rv64_sifive_e_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_U54, MXL_RV64, rv64_sifive_u_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SHAKTI_C, MXL_RV64, rv64_sifive_u_cpu_init), @@ -3302,8 +3248,11 @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_XIANGSHAN_NANHU, MXL_RV64, rv64_xiangshan_nanhu_cpu_init), #if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) - DEFINE_DYNAMIC_CPU(TYPE_RISCV_CPU_BASE128, MXL_RV128, rv128_base_cpu_init), -#endif /* CONFIG_TCG && !CONFIG_USER_ONLY */ + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE128, TYPE_RISCV_DYNAMIC_CPU, + .cfg.max_satp_mode = VM_1_10_SV57, + .misa_mxl_max = MXL_RV128, + ), +#endif /* CONFIG_TCG */ DEFINE_RISCV_CPU(TYPE_RISCV_CPU_RV64I, TYPE_RISCV_BARE_CPU, .misa_mxl_max = MXL_RV64, .misa_ext = RVI From e89d4931d0a15ff0481e9a6e7cbb9f7a28e91434 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 15:32:52 +0100 Subject: [PATCH 1042/2760] target/riscv: convert SiFive E CPU models to RISCVCPUDef Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu-qom.h | 1 + target/riscv/cpu.c | 74 ++++++++++++------------------------------ 2 files changed, 21 insertions(+), 54 deletions(-) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index 4cfdb74891..0f9be15e47 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -44,6 +44,7 @@ #define TYPE_RISCV_CPU_RVA23S64 RISCV_CPU_TYPE_NAME("rva23s64") #define TYPE_RISCV_CPU_IBEX RISCV_CPU_TYPE_NAME("lowrisc-ibex") #define TYPE_RISCV_CPU_SHAKTI_C RISCV_CPU_TYPE_NAME("shakti-c") +#define TYPE_RISCV_CPU_SIFIVE_E RISCV_CPU_TYPE_NAME("sifive-e") #define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31") #define TYPE_RISCV_CPU_SIFIVE_E34 RISCV_CPU_TYPE_NAME("sifive-e34") #define TYPE_RISCV_CPU_SIFIVE_E51 RISCV_CPU_TYPE_NAME("sifive-e51") diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 2b26f23bd0..17ad8b2ca1 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -503,23 +503,6 @@ static void rv64_sifive_u_cpu_init(Object *obj) cpu->cfg.pmp = true; } -static void rv64_sifive_e_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - RISCVCPU *cpu = RISCV_CPU(obj); - - riscv_cpu_set_misa_ext(env, RVI | RVM | RVA | RVC | RVU); - env->priv_ver = PRIV_VERSION_1_10_0; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(cpu, VM_1_10_MBARE); -#endif - - /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_zicsr = true; - cpu->cfg.pmp = true; -} - static void rv64_thead_c906_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; @@ -708,23 +691,6 @@ static void rv32_sifive_u_cpu_init(Object *obj) cpu->cfg.pmp = true; } -static void rv32_sifive_e_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - RISCVCPU *cpu = RISCV_CPU(obj); - - riscv_cpu_set_misa_ext(env, RVI | RVM | RVA | RVC | RVU); - env->priv_ver = PRIV_VERSION_1_10_0; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(cpu, VM_1_10_MBARE); -#endif - - /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_zicsr = true; - cpu->cfg.pmp = true; -} - static void rv32_ibex_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; @@ -746,23 +712,6 @@ static void rv32_ibex_cpu_init(Object *obj) cpu->cfg.ext_zbc = true; cpu->cfg.ext_zbs = true; } - -static void rv32_imafcu_nommu_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - RISCVCPU *cpu = RISCV_CPU(obj); - - riscv_cpu_set_misa_ext(env, RVI | RVM | RVA | RVF | RVC | RVU); - env->priv_ver = PRIV_VERSION_1_10_0; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(cpu, VM_1_10_MBARE); -#endif - - /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_zicsr = true; - cpu->cfg.pmp = true; -} #endif static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model) @@ -3204,6 +3153,15 @@ static const TypeInfo riscv_cpu_type_infos[] = { #endif ), + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E, TYPE_RISCV_VENDOR_CPU, + .misa_ext = RVI | RVM | RVA | RVC | RVU, + .priv_spec = PRIV_VERSION_1_10_0, + .cfg.max_satp_mode = VM_1_10_MBARE, + .cfg.ext_zifencei = true, + .cfg.ext_zicsr = true, + .cfg.pmp = true + ), + #if defined(TARGET_RISCV32) || \ (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE32, TYPE_RISCV_DYNAMIC_CPU, @@ -3212,8 +3170,14 @@ static const TypeInfo riscv_cpu_type_infos[] = { ), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_IBEX, MXL_RV32, rv32_ibex_cpu_init), - DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_E31, MXL_RV32, rv32_sifive_e_cpu_init), - DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_E34, MXL_RV32, rv32_imafcu_nommu_cpu_init), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E31, TYPE_RISCV_CPU_SIFIVE_E, + .misa_mxl_max = MXL_RV32 + ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E34, TYPE_RISCV_CPU_SIFIVE_E, + .misa_mxl_max = MXL_RV32, + .misa_ext = RVF, /* IMAFCU */ + ), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_U34, MXL_RV32, rv32_sifive_u_cpu_init), DEFINE_RISCV_CPU(TYPE_RISCV_CPU_RV32I, TYPE_RISCV_BARE_CPU, @@ -3239,7 +3203,9 @@ static const TypeInfo riscv_cpu_type_infos[] = { .misa_mxl_max = MXL_RV64, ), - DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_E51, MXL_RV64, rv64_sifive_e_cpu_init), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E51, TYPE_RISCV_CPU_SIFIVE_E, + .misa_mxl_max = MXL_RV64 + ), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_U54, MXL_RV64, rv64_sifive_u_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SHAKTI_C, MXL_RV64, rv64_sifive_u_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_THEAD_C906, MXL_RV64, rv64_thead_c906_cpu_init), From 5106b8ee9ac78505f872a956c931c1f965ad073e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 17:27:16 +0100 Subject: [PATCH 1043/2760] target/riscv: convert ibex CPU models to RISCVCPUDef Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 39 ++++++++++++++++----------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 17ad8b2ca1..689c33916e 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -690,28 +690,6 @@ static void rv32_sifive_u_cpu_init(Object *obj) cpu->cfg.mmu = true; cpu->cfg.pmp = true; } - -static void rv32_ibex_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - RISCVCPU *cpu = RISCV_CPU(obj); - - riscv_cpu_set_misa_ext(env, RVI | RVM | RVC | RVU); - env->priv_ver = PRIV_VERSION_1_12_0; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(cpu, VM_1_10_MBARE); -#endif - /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_zicsr = true; - cpu->cfg.pmp = true; - cpu->cfg.ext_smepmp = true; - - cpu->cfg.ext_zba = true; - cpu->cfg.ext_zbb = true; - cpu->cfg.ext_zbc = true; - cpu->cfg.ext_zbs = true; -} #endif static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model) @@ -3169,7 +3147,22 @@ static const TypeInfo riscv_cpu_type_infos[] = { .misa_mxl_max = MXL_RV32, ), - DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_IBEX, MXL_RV32, rv32_ibex_cpu_init), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_IBEX, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV32, + .misa_ext = RVI | RVM | RVC | RVU, + .priv_spec = PRIV_VERSION_1_12_0, + .cfg.max_satp_mode = VM_1_10_MBARE, + .cfg.ext_zifencei = true, + .cfg.ext_zicsr = true, + .cfg.pmp = true, + .cfg.ext_smepmp = true, + + .cfg.ext_zba = true, + .cfg.ext_zbb = true, + .cfg.ext_zbc = true, + .cfg.ext_zbs = true + ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E31, TYPE_RISCV_CPU_SIFIVE_E, .misa_mxl_max = MXL_RV32 ), From 5a62948c91a910de4f9c7332cf8803152c099eac Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 18 Feb 2025 13:05:25 +0100 Subject: [PATCH 1044/2760] target/riscv: convert SiFive U models to RISCVCPUDef Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu-qom.h | 1 + target/riscv/cpu.c | 79 +++++++++++++++++++----------------------- 2 files changed, 37 insertions(+), 43 deletions(-) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index 0f9be15e47..1ee05eb393 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -48,6 +48,7 @@ #define TYPE_RISCV_CPU_SIFIVE_E31 RISCV_CPU_TYPE_NAME("sifive-e31") #define TYPE_RISCV_CPU_SIFIVE_E34 RISCV_CPU_TYPE_NAME("sifive-e34") #define TYPE_RISCV_CPU_SIFIVE_E51 RISCV_CPU_TYPE_NAME("sifive-e51") +#define TYPE_RISCV_CPU_SIFIVE_U RISCV_CPU_TYPE_NAME("sifive-u") #define TYPE_RISCV_CPU_SIFIVE_U34 RISCV_CPU_TYPE_NAME("sifive-u34") #define TYPE_RISCV_CPU_SIFIVE_U54 RISCV_CPU_TYPE_NAME("sifive-u54") #define TYPE_RISCV_CPU_THEAD_C906 RISCV_CPU_TYPE_NAME("thead-c906") diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 689c33916e..01b028653a 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -440,8 +440,8 @@ const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit) g_assert_not_reached(); } -static void set_satp_mode_max_supported(RISCVCPU *cpu, - int satp_mode) +static void __attribute__((unused)) +set_satp_mode_max_supported(RISCVCPU *cpu, int satp_mode) { bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32; const bool *valid_vm = rv32 ? valid_vm_1_10_32 : valid_vm_1_10_64; @@ -486,23 +486,6 @@ static void set_satp_mode_default_map(RISCVCPU *cpu) #endif #if defined(TARGET_RISCV64) -static void rv64_sifive_u_cpu_init(Object *obj) -{ - RISCVCPU *cpu = RISCV_CPU(obj); - CPURISCVState *env = &cpu->env; - riscv_cpu_set_misa_ext(env, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - env->priv_ver = PRIV_VERSION_1_10_0; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV39); -#endif - - /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_zicsr = true; - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; -} - static void rv64_thead_c906_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; @@ -671,27 +654,6 @@ static void rv64_xiangshan_nanhu_cpu_init(Object *obj) #endif /* !TARGET_RISCV64 */ -#if defined(TARGET_RISCV32) || \ - (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) - -static void rv32_sifive_u_cpu_init(Object *obj) -{ - RISCVCPU *cpu = RISCV_CPU(obj); - CPURISCVState *env = &cpu->env; - riscv_cpu_set_misa_ext(env, RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU); - env->priv_ver = PRIV_VERSION_1_10_0; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(RISCV_CPU(obj), VM_1_10_SV32); -#endif - - /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_zicsr = true; - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; -} -#endif - static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model) { ObjectClass *oc; @@ -2921,6 +2883,17 @@ static void riscv_cpu_class_base_init(ObjectClass *c, const void *data) if (def->misa_mxl_max) { assert(def->misa_mxl_max <= MXL_RV128); mcc->def->misa_mxl_max = def->misa_mxl_max; + +#ifndef CONFIG_USER_ONLY + /* + * Hack to simplify CPU class hierarchies that include both 32- and + * 64-bit models: reduce SV39/48/57/64 to SV32 for 32-bit models. + */ + if (mcc->def->misa_mxl_max == MXL_RV32 && + !valid_vm_1_10_32[mcc->def->cfg.max_satp_mode]) { + mcc->def->cfg.max_satp_mode = VM_1_10_SV32; + } +#endif } if (def->priv_spec != RISCV_PROFILE_ATTR_UNUSED) { assert(def->priv_spec <= PRIV_VERSION_LATEST); @@ -3140,6 +3113,17 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.pmp = true ), + DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_U, TYPE_RISCV_VENDOR_CPU, + .misa_ext = RVI | RVM | RVA | RVF | RVD | RVC | RVS | RVU, + .priv_spec = PRIV_VERSION_1_10_0, + + .cfg.max_satp_mode = VM_1_10_SV39, + .cfg.ext_zifencei = true, + .cfg.ext_zicsr = true, + .cfg.mmu = true, + .cfg.pmp = true + ), + #if defined(TARGET_RISCV32) || \ (defined(TARGET_RISCV64) && !defined(CONFIG_USER_ONLY)) DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE32, TYPE_RISCV_DYNAMIC_CPU, @@ -3171,7 +3155,9 @@ static const TypeInfo riscv_cpu_type_infos[] = { .misa_ext = RVF, /* IMAFCU */ ), - DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_U34, MXL_RV32, rv32_sifive_u_cpu_init), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_U34, TYPE_RISCV_CPU_SIFIVE_U, + .misa_mxl_max = MXL_RV32, + ), DEFINE_RISCV_CPU(TYPE_RISCV_CPU_RV32I, TYPE_RISCV_BARE_CPU, .misa_mxl_max = MXL_RV32, @@ -3199,8 +3185,15 @@ static const TypeInfo riscv_cpu_type_infos[] = { DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_E51, TYPE_RISCV_CPU_SIFIVE_E, .misa_mxl_max = MXL_RV64 ), - DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SIFIVE_U54, MXL_RV64, rv64_sifive_u_cpu_init), - DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_SHAKTI_C, MXL_RV64, rv64_sifive_u_cpu_init), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_U54, TYPE_RISCV_CPU_SIFIVE_U, + .misa_mxl_max = MXL_RV64, + ), + + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_SHAKTI_C, TYPE_RISCV_CPU_SIFIVE_U, + .misa_mxl_max = MXL_RV64, + ), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_THEAD_C906, MXL_RV64, rv64_thead_c906_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_TT_ASCALON, MXL_RV64, rv64_tt_ascalon_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_VEYRON_V1, MXL_RV64, rv64_veyron_v1_cpu_init), From 1d84c2401c48617b8695d292602e2e777e0d1178 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 17:39:49 +0100 Subject: [PATCH 1045/2760] target/riscv: th: make CSR insertion test a bit more intuitive In preparation for generalizing the custom CSR functionality, make the test return bool instead of int. Make the insertion_test optional, too. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/th_csr.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/target/riscv/th_csr.c b/target/riscv/th_csr.c index 6c970d4e81..969a9fe3c8 100644 --- a/target/riscv/th_csr.c +++ b/target/riscv/th_csr.c @@ -29,7 +29,7 @@ typedef struct { int csrno; - int (*insertion_test)(RISCVCPU *cpu); + bool (*insertion_test)(RISCVCPU *cpu); riscv_csr_operations csr_ops; } riscv_csr; @@ -42,13 +42,9 @@ static RISCVException smode(CPURISCVState *env, int csrno) return RISCV_EXCP_ILLEGAL_INST; } -static int test_thead_mvendorid(RISCVCPU *cpu) +static bool test_thead_mvendorid(RISCVCPU *cpu) { - if (cpu->cfg.mvendorid != THEAD_VENDOR_ID) { - return -1; - } - - return 0; + return cpu->cfg.mvendorid == THEAD_VENDOR_ID; } static RISCVException read_th_sxstatus(CPURISCVState *env, int csrno, @@ -66,13 +62,12 @@ static riscv_csr th_csr_list[] = { .csr_ops = { "th.sxstatus", smode, read_th_sxstatus } } }; - void th_register_custom_csrs(RISCVCPU *cpu) { for (size_t i = 0; i < ARRAY_SIZE(th_csr_list); i++) { int csrno = th_csr_list[i].csrno; riscv_csr_operations *csr_ops = &th_csr_list[i].csr_ops; - if (!th_csr_list[i].insertion_test(cpu)) { + if (!th_csr_list[i].insertion_test || th_csr_list[i].insertion_test(cpu)) { riscv_set_csr_ops(csrno, csr_ops); } } From 1016b0364f981e7aa9304866d7d756813e8dc6c2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 27 Feb 2025 15:09:49 +0100 Subject: [PATCH 1046/2760] target/riscv: generalize custom CSR functionality While at it, constify it so that the RISCVCSR array in RISCVCPUDef can also be const. Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 25 ++++++++++++++++++++++++- target/riscv/cpu.h | 15 ++++++++++++--- target/riscv/csr.c | 2 +- target/riscv/th_csr.c | 21 +++------------------ 4 files changed, 40 insertions(+), 23 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 01b028653a..12f4bc4151 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -485,6 +485,19 @@ static void set_satp_mode_default_map(RISCVCPU *cpu) } #endif +#ifndef CONFIG_USER_ONLY +static void riscv_register_custom_csrs(RISCVCPU *cpu, const RISCVCSR *csr_list) +{ + for (size_t i = 0; csr_list[i].csr_ops.name; i++) { + int csrno = csr_list[i].csrno; + const riscv_csr_operations *csr_ops = &csr_list[i].csr_ops; + if (!csr_list[i].insertion_test || csr_list[i].insertion_test(cpu)) { + riscv_set_csr_ops(csrno, csr_ops); + } + } +} +#endif + #if defined(TARGET_RISCV64) static void rv64_thead_c906_cpu_init(Object *obj) { @@ -511,7 +524,7 @@ static void rv64_thead_c906_cpu_init(Object *obj) cpu->cfg.mvendorid = THEAD_VENDOR_ID; #ifndef CONFIG_USER_ONLY set_satp_mode_max_supported(cpu, VM_1_10_SV39); - th_register_custom_csrs(cpu); + riscv_register_custom_csrs(cpu, th_csr_list); #endif /* inherited from parent obj via riscv_cpu_init() */ @@ -1304,6 +1317,11 @@ static void riscv_cpu_init(Object *obj) if (mcc->def->vext_spec != RISCV_PROFILE_ATTR_UNUSED) { cpu->env.vext_ver = mcc->def->vext_spec; } +#ifndef CONFIG_USER_ONLY + if (mcc->def->custom_csrs) { + riscv_register_custom_csrs(cpu, mcc->def->custom_csrs); + } +#endif } typedef struct misa_ext_info { @@ -2906,6 +2924,11 @@ static void riscv_cpu_class_base_init(ObjectClass *c, const void *data) mcc->def->misa_ext |= def->misa_ext; riscv_cpu_cfg_merge(&mcc->def->cfg, &def->cfg); + + if (def->custom_csrs) { + assert(!mcc->def->custom_csrs); + mcc->def->custom_csrs = def->custom_csrs; + } } if (!object_class_is_abstract(c)) { diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index ed88adef45..229ade9ed9 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -538,6 +538,8 @@ struct ArchCPU { const GPtrArray *decoders; }; +typedef struct RISCVCSR RISCVCSR; + typedef struct RISCVCPUDef { RISCVMXL misa_mxl_max; /* max mxl for this cpu */ RISCVCPUProfile *profile; @@ -546,6 +548,7 @@ typedef struct RISCVCPUDef { int32_t vext_spec; RISCVCPUConfig cfg; bool bare; + const RISCVCSR *custom_csrs; } RISCVCPUDef; /** @@ -893,6 +896,12 @@ typedef struct { uint32_t min_priv_ver; } riscv_csr_operations; +struct RISCVCSR { + int csrno; + bool (*insertion_test)(RISCVCPU *cpu); + riscv_csr_operations csr_ops; +}; + /* CSR function table constants */ enum { CSR_TABLE_SIZE = 0x1000 @@ -947,7 +956,7 @@ extern riscv_csr_operations csr_ops[CSR_TABLE_SIZE]; extern const bool valid_vm_1_10_32[], valid_vm_1_10_64[]; void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops); -void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops); +void riscv_set_csr_ops(int csrno, const riscv_csr_operations *ops); void riscv_cpu_register_gdb_regs_for_features(CPUState *cs); @@ -956,8 +965,8 @@ target_ulong riscv_new_csr_seed(target_ulong new_value, const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit); -/* Implemented in th_csr.c */ -void th_register_custom_csrs(RISCVCPU *cpu); +/* In th_csr.c */ +extern const RISCVCSR th_csr_list[]; const char *priv_spec_to_str(int priv_version); #endif /* RISCV_CPU_H */ diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 9843fd2191..fb14972169 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -40,7 +40,7 @@ void riscv_get_csr_ops(int csrno, riscv_csr_operations *ops) *ops = csr_ops[csrno & (CSR_TABLE_SIZE - 1)]; } -void riscv_set_csr_ops(int csrno, riscv_csr_operations *ops) +void riscv_set_csr_ops(int csrno, const riscv_csr_operations *ops) { csr_ops[csrno & (CSR_TABLE_SIZE - 1)] = *ops; } diff --git a/target/riscv/th_csr.c b/target/riscv/th_csr.c index 969a9fe3c8..49eb7bbab5 100644 --- a/target/riscv/th_csr.c +++ b/target/riscv/th_csr.c @@ -27,12 +27,6 @@ #define TH_SXSTATUS_MAEE BIT(21) #define TH_SXSTATUS_THEADISAEE BIT(22) -typedef struct { - int csrno; - bool (*insertion_test)(RISCVCPU *cpu); - riscv_csr_operations csr_ops; -} riscv_csr; - static RISCVException smode(CPURISCVState *env, int csrno) { if (riscv_has_ext(env, RVS)) { @@ -55,20 +49,11 @@ static RISCVException read_th_sxstatus(CPURISCVState *env, int csrno, return RISCV_EXCP_NONE; } -static riscv_csr th_csr_list[] = { +const RISCVCSR th_csr_list[] = { { .csrno = CSR_TH_SXSTATUS, .insertion_test = test_thead_mvendorid, .csr_ops = { "th.sxstatus", smode, read_th_sxstatus } - } + }, + { } }; -void th_register_custom_csrs(RISCVCPU *cpu) -{ - for (size_t i = 0; i < ARRAY_SIZE(th_csr_list); i++) { - int csrno = th_csr_list[i].csrno; - riscv_csr_operations *csr_ops = &th_csr_list[i].csr_ops; - if (!th_csr_list[i].insertion_test || th_csr_list[i].insertion_test(cpu)) { - riscv_set_csr_ops(csrno, csr_ops); - } - } -} From 5f687d77ff93ae3dd2fb48600e4e6ae32eb7350e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 15:48:47 +0100 Subject: [PATCH 1047/2760] target/riscv: convert THead C906 to RISCVCPUDef Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 61 +++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 33 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 12f4bc4151..5d2ccf647d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -499,38 +499,6 @@ static void riscv_register_custom_csrs(RISCVCPU *cpu, const RISCVCSR *csr_list) #endif #if defined(TARGET_RISCV64) -static void rv64_thead_c906_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - RISCVCPU *cpu = RISCV_CPU(obj); - - riscv_cpu_set_misa_ext(env, RVG | RVC | RVS | RVU); - env->priv_ver = PRIV_VERSION_1_11_0; - - cpu->cfg.ext_zfa = true; - cpu->cfg.ext_zfh = true; - cpu->cfg.mmu = true; - cpu->cfg.ext_xtheadba = true; - cpu->cfg.ext_xtheadbb = true; - cpu->cfg.ext_xtheadbs = true; - cpu->cfg.ext_xtheadcmo = true; - cpu->cfg.ext_xtheadcondmov = true; - cpu->cfg.ext_xtheadfmemidx = true; - cpu->cfg.ext_xtheadmac = true; - cpu->cfg.ext_xtheadmemidx = true; - cpu->cfg.ext_xtheadmempair = true; - cpu->cfg.ext_xtheadsync = true; - - cpu->cfg.mvendorid = THEAD_VENDOR_ID; -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(cpu, VM_1_10_SV39); - riscv_register_custom_csrs(cpu, th_csr_list); -#endif - - /* inherited from parent obj via riscv_cpu_init() */ - cpu->cfg.pmp = true; -} - static void rv64_veyron_v1_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; @@ -3217,7 +3185,34 @@ static const TypeInfo riscv_cpu_type_infos[] = { .misa_mxl_max = MXL_RV64, ), - DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_THEAD_C906, MXL_RV64, rv64_thead_c906_cpu_init), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_THEAD_C906, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVG | RVC | RVS | RVU, + .priv_spec = PRIV_VERSION_1_11_0, + + .cfg.ext_zfa = true, + .cfg.ext_zfh = true, + .cfg.mmu = true, + .cfg.ext_xtheadba = true, + .cfg.ext_xtheadbb = true, + .cfg.ext_xtheadbs = true, + .cfg.ext_xtheadcmo = true, + .cfg.ext_xtheadcondmov = true, + .cfg.ext_xtheadfmemidx = true, + .cfg.ext_xtheadmac = true, + .cfg.ext_xtheadmemidx = true, + .cfg.ext_xtheadmempair = true, + .cfg.ext_xtheadsync = true, + .cfg.pmp = true, + + .cfg.mvendorid = THEAD_VENDOR_ID, + + .cfg.max_satp_mode = VM_1_10_SV39, +#ifndef CONFIG_USER_ONLY + .custom_csrs = th_csr_list, +#endif + ), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_TT_ASCALON, MXL_RV64, rv64_tt_ascalon_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_VEYRON_V1, MXL_RV64, rv64_veyron_v1_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_XIANGSHAN_NANHU, From 09ef7d97454a64e00d8dc9fd3a188d6c06003cc1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 15:53:28 +0100 Subject: [PATCH 1048/2760] target/riscv: convert TT Ascalon to RISCVCPUDef Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 127 +++++++++++++++++++++------------------------ 1 file changed, 60 insertions(+), 67 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 5d2ccf647d..48939adaaf 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -539,72 +539,6 @@ static void rv64_veyron_v1_cpu_init(Object *obj) #endif } -/* Tenstorrent Ascalon */ -static void rv64_tt_ascalon_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - RISCVCPU *cpu = RISCV_CPU(obj); - - riscv_cpu_set_misa_ext(env, RVG | RVC | RVS | RVU | RVH | RVV); - env->priv_ver = PRIV_VERSION_1_13_0; - - /* Enable ISA extensions */ - cpu->cfg.mmu = true; - cpu->cfg.vlenb = 256 >> 3; - cpu->cfg.elen = 64; - cpu->env.vext_ver = VEXT_VERSION_1_00_0; - cpu->cfg.rvv_ma_all_1s = true; - cpu->cfg.rvv_ta_all_1s = true; - cpu->cfg.misa_w = true; - cpu->cfg.pmp = true; - cpu->cfg.cbom_blocksize = 64; - cpu->cfg.cbop_blocksize = 64; - cpu->cfg.cboz_blocksize = 64; - cpu->cfg.ext_zic64b = true; - cpu->cfg.ext_zicbom = true; - cpu->cfg.ext_zicbop = true; - cpu->cfg.ext_zicboz = true; - cpu->cfg.ext_zicntr = true; - cpu->cfg.ext_zicond = true; - cpu->cfg.ext_zicsr = true; - cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_zihintntl = true; - cpu->cfg.ext_zihintpause = true; - cpu->cfg.ext_zihpm = true; - cpu->cfg.ext_zimop = true; - cpu->cfg.ext_zawrs = true; - cpu->cfg.ext_zfa = true; - cpu->cfg.ext_zfbfmin = true; - cpu->cfg.ext_zfh = true; - cpu->cfg.ext_zfhmin = true; - cpu->cfg.ext_zcb = true; - cpu->cfg.ext_zcmop = true; - cpu->cfg.ext_zba = true; - cpu->cfg.ext_zbb = true; - cpu->cfg.ext_zbs = true; - cpu->cfg.ext_zkt = true; - cpu->cfg.ext_zvbb = true; - cpu->cfg.ext_zvbc = true; - cpu->cfg.ext_zvfbfmin = true; - cpu->cfg.ext_zvfbfwma = true; - cpu->cfg.ext_zvfh = true; - cpu->cfg.ext_zvfhmin = true; - cpu->cfg.ext_zvkng = true; - cpu->cfg.ext_smaia = true; - cpu->cfg.ext_smstateen = true; - cpu->cfg.ext_ssaia = true; - cpu->cfg.ext_sscofpmf = true; - cpu->cfg.ext_sstc = true; - cpu->cfg.ext_svade = true; - cpu->cfg.ext_svinval = true; - cpu->cfg.ext_svnapot = true; - cpu->cfg.ext_svpbmt = true; - -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(cpu, VM_1_10_SV57); -#endif -} - static void rv64_xiangshan_nanhu_cpu_init(Object *obj) { CPURISCVState *env = &RISCV_CPU(obj)->env; @@ -3213,7 +3147,66 @@ static const TypeInfo riscv_cpu_type_infos[] = { #endif ), - DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_TT_ASCALON, MXL_RV64, rv64_tt_ascalon_cpu_init), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_TT_ASCALON, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVG | RVC | RVS | RVU | RVH | RVV, + .priv_spec = PRIV_VERSION_1_13_0, + .vext_spec = VEXT_VERSION_1_00_0, + + /* ISA extensions */ + .cfg.mmu = true, + .cfg.vlenb = 256 >> 3, + .cfg.elen = 64, + .cfg.rvv_ma_all_1s = true, + .cfg.rvv_ta_all_1s = true, + .cfg.misa_w = true, + .cfg.pmp = true, + .cfg.cbom_blocksize = 64, + .cfg.cbop_blocksize = 64, + .cfg.cboz_blocksize = 64, + .cfg.ext_zic64b = true, + .cfg.ext_zicbom = true, + .cfg.ext_zicbop = true, + .cfg.ext_zicboz = true, + .cfg.ext_zicntr = true, + .cfg.ext_zicond = true, + .cfg.ext_zicsr = true, + .cfg.ext_zifencei = true, + .cfg.ext_zihintntl = true, + .cfg.ext_zihintpause = true, + .cfg.ext_zihpm = true, + .cfg.ext_zimop = true, + .cfg.ext_zawrs = true, + .cfg.ext_zfa = true, + .cfg.ext_zfbfmin = true, + .cfg.ext_zfh = true, + .cfg.ext_zfhmin = true, + .cfg.ext_zcb = true, + .cfg.ext_zcmop = true, + .cfg.ext_zba = true, + .cfg.ext_zbb = true, + .cfg.ext_zbs = true, + .cfg.ext_zkt = true, + .cfg.ext_zvbb = true, + .cfg.ext_zvbc = true, + .cfg.ext_zvfbfmin = true, + .cfg.ext_zvfbfwma = true, + .cfg.ext_zvfh = true, + .cfg.ext_zvfhmin = true, + .cfg.ext_zvkng = true, + .cfg.ext_smaia = true, + .cfg.ext_smstateen = true, + .cfg.ext_ssaia = true, + .cfg.ext_sscofpmf = true, + .cfg.ext_sstc = true, + .cfg.ext_svade = true, + .cfg.ext_svinval = true, + .cfg.ext_svnapot = true, + .cfg.ext_svpbmt = true, + + .cfg.max_satp_mode = VM_1_10_SV57, + ), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_VEYRON_V1, MXL_RV64, rv64_veyron_v1_cpu_init), DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_XIANGSHAN_NANHU, MXL_RV64, rv64_xiangshan_nanhu_cpu_init), From 0927f7d55cd5a214eb2ef9671ddd271a3fb2a15c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 15:53:28 +0100 Subject: [PATCH 1049/2760] target/riscv: convert Ventana V1 to RISCVCPUDef Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 75 ++++++++++++++++++++++------------------------ 1 file changed, 35 insertions(+), 40 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 48939adaaf..000fcc6a1d 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -499,45 +499,6 @@ static void riscv_register_custom_csrs(RISCVCPU *cpu, const RISCVCSR *csr_list) #endif #if defined(TARGET_RISCV64) -static void rv64_veyron_v1_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - RISCVCPU *cpu = RISCV_CPU(obj); - - riscv_cpu_set_misa_ext(env, RVG | RVC | RVS | RVU | RVH); - env->priv_ver = PRIV_VERSION_1_12_0; - - /* Enable ISA extensions */ - cpu->cfg.mmu = true; - cpu->cfg.ext_zifencei = true; - cpu->cfg.ext_zicsr = true; - cpu->cfg.pmp = true; - cpu->cfg.ext_zicbom = true; - cpu->cfg.cbom_blocksize = 64; - cpu->cfg.cboz_blocksize = 64; - cpu->cfg.ext_zicboz = true; - cpu->cfg.ext_smaia = true; - cpu->cfg.ext_ssaia = true; - cpu->cfg.ext_sscofpmf = true; - cpu->cfg.ext_sstc = true; - cpu->cfg.ext_svinval = true; - cpu->cfg.ext_svnapot = true; - cpu->cfg.ext_svpbmt = true; - cpu->cfg.ext_smstateen = true; - cpu->cfg.ext_zba = true; - cpu->cfg.ext_zbb = true; - cpu->cfg.ext_zbc = true; - cpu->cfg.ext_zbs = true; - cpu->cfg.ext_XVentanaCondOps = true; - - cpu->cfg.mvendorid = VEYRON_V1_MVENDORID; - cpu->cfg.marchid = VEYRON_V1_MARCHID; - cpu->cfg.mimpid = VEYRON_V1_MIMPID; - -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(cpu, VM_1_10_SV48); -#endif -} static void rv64_xiangshan_nanhu_cpu_init(Object *obj) { @@ -3207,7 +3168,41 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.max_satp_mode = VM_1_10_SV57, ), - DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_VEYRON_V1, MXL_RV64, rv64_veyron_v1_cpu_init), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_VEYRON_V1, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVG | RVC | RVS | RVU | RVH, + .priv_spec = PRIV_VERSION_1_12_0, + + /* ISA extensions */ + .cfg.mmu = true, + .cfg.ext_zifencei = true, + .cfg.ext_zicsr = true, + .cfg.pmp = true, + .cfg.ext_zicbom = true, + .cfg.cbom_blocksize = 64, + .cfg.cboz_blocksize = 64, + .cfg.ext_zicboz = true, + .cfg.ext_smaia = true, + .cfg.ext_ssaia = true, + .cfg.ext_sscofpmf = true, + .cfg.ext_sstc = true, + .cfg.ext_svinval = true, + .cfg.ext_svnapot = true, + .cfg.ext_svpbmt = true, + .cfg.ext_smstateen = true, + .cfg.ext_zba = true, + .cfg.ext_zbb = true, + .cfg.ext_zbc = true, + .cfg.ext_zbs = true, + .cfg.ext_XVentanaCondOps = true, + + .cfg.mvendorid = VEYRON_V1_MVENDORID, + .cfg.marchid = VEYRON_V1_MARCHID, + .cfg.mimpid = VEYRON_V1_MIMPID, + + .cfg.max_satp_mode = VM_1_10_SV48, + ), + DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_XIANGSHAN_NANHU, MXL_RV64, rv64_xiangshan_nanhu_cpu_init), #if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) From 70f48d7fb19a88bca3cd4c388646d4cb9ee730f0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 17:36:06 +0100 Subject: [PATCH 1050/2760] target/riscv: convert Xiangshan Nanhu to RISCVCPUDef Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 80 +++++++++++++--------------------------------- 1 file changed, 23 insertions(+), 57 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 000fcc6a1d..640aa958fd 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -440,16 +440,6 @@ const char *satp_mode_str(uint8_t satp_mode, bool is_32_bit) g_assert_not_reached(); } -static void __attribute__((unused)) -set_satp_mode_max_supported(RISCVCPU *cpu, int satp_mode) -{ - bool rv32 = riscv_cpu_mxl(&cpu->env) == MXL_RV32; - const bool *valid_vm = rv32 ? valid_vm_1_10_32 : valid_vm_1_10_64; - - assert(valid_vm[satp_mode]); - cpu->cfg.max_satp_mode = satp_mode; -} - static bool get_satp_mode_supported(RISCVCPU *cpu, uint16_t *supported) { bool rv32 = riscv_cpu_is_32bit(cpu); @@ -498,38 +488,6 @@ static void riscv_register_custom_csrs(RISCVCPU *cpu, const RISCVCSR *csr_list) } #endif -#if defined(TARGET_RISCV64) - -static void rv64_xiangshan_nanhu_cpu_init(Object *obj) -{ - CPURISCVState *env = &RISCV_CPU(obj)->env; - RISCVCPU *cpu = RISCV_CPU(obj); - - riscv_cpu_set_misa_ext(env, RVG | RVC | RVB | RVS | RVU); - env->priv_ver = PRIV_VERSION_1_12_0; - - /* Enable ISA extensions */ - cpu->cfg.ext_zbc = true; - cpu->cfg.ext_zbkb = true; - cpu->cfg.ext_zbkc = true; - cpu->cfg.ext_zbkx = true; - cpu->cfg.ext_zknd = true; - cpu->cfg.ext_zkne = true; - cpu->cfg.ext_zknh = true; - cpu->cfg.ext_zksed = true; - cpu->cfg.ext_zksh = true; - cpu->cfg.ext_svinval = true; - - cpu->cfg.mmu = true; - cpu->cfg.pmp = true; - -#ifndef CONFIG_USER_ONLY - set_satp_mode_max_supported(cpu, VM_1_10_SV39); -#endif -} - -#endif /* !TARGET_RISCV64 */ - static ObjectClass *riscv_cpu_class_by_name(const char *cpu_model) { ObjectClass *oc; @@ -2891,19 +2849,6 @@ void riscv_isa_write_fdt(RISCVCPU *cpu, void *fdt, char *nodename) } #endif -#define DEFINE_VENDOR_CPU(type_name, misa_mxl_max_, initfn) \ - { \ - .name = (type_name), \ - .parent = TYPE_RISCV_VENDOR_CPU, \ - .instance_init = (initfn), \ - .class_data = &(const RISCVCPUDef) { \ - .misa_mxl_max = (misa_mxl_max_), \ - .priv_spec = RISCV_PROFILE_ATTR_UNUSED, \ - .vext_spec = RISCV_PROFILE_ATTR_UNUSED, \ - .cfg.max_satp_mode = -1, \ - }, \ - } - #define DEFINE_ABSTRACT_RISCV_CPU(type_name, parent_type_name, ...) \ { \ .name = (type_name), \ @@ -3203,8 +3148,29 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.max_satp_mode = VM_1_10_SV48, ), - DEFINE_VENDOR_CPU(TYPE_RISCV_CPU_XIANGSHAN_NANHU, - MXL_RV64, rv64_xiangshan_nanhu_cpu_init), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_XIANGSHAN_NANHU, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVG | RVC | RVB | RVS | RVU, + .priv_spec = PRIV_VERSION_1_12_0, + + /* ISA extensions */ + .cfg.ext_zbc = true, + .cfg.ext_zbkb = true, + .cfg.ext_zbkc = true, + .cfg.ext_zbkx = true, + .cfg.ext_zknd = true, + .cfg.ext_zkne = true, + .cfg.ext_zknh = true, + .cfg.ext_zksed = true, + .cfg.ext_zksh = true, + .cfg.ext_svinval = true, + + .cfg.mmu = true, + .cfg.pmp = true, + + .cfg.max_satp_mode = VM_1_10_SV39, + ), + #if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE128, TYPE_RISCV_DYNAMIC_CPU, .cfg.max_satp_mode = VM_1_10_SV57, From 42bc8af14033b9eeeb535449f243767c015f027e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 6 Feb 2025 12:57:12 +0100 Subject: [PATCH 1051/2760] target/riscv: remove .instance_post_init Unlike other uses of .instance_post_init, accel_cpu_instance_init() *registers* properties, and therefore must be run before device_post_init() which sets them to their values from -global. In order to move all registration of properties to .instance_init, call accel_cpu_instance_init() at the end of riscv_cpu_init(). Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- target/riscv/cpu.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 640aa958fd..629ac37501 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1083,11 +1083,6 @@ static bool riscv_cpu_is_dynamic(Object *cpu_obj) return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL; } -static void riscv_cpu_post_init(Object *obj) -{ - accel_cpu_instance_init(CPU(obj)); -} - static void riscv_cpu_init(Object *obj) { RISCVCPUClass *mcc = RISCV_CPU_GET_CLASS(obj); @@ -1143,6 +1138,8 @@ static void riscv_cpu_init(Object *obj) riscv_register_custom_csrs(cpu, mcc->def->custom_csrs); } #endif + + accel_cpu_instance_init(CPU(obj)); } typedef struct misa_ext_info { @@ -2885,7 +2882,6 @@ static const TypeInfo riscv_cpu_type_infos[] = { .instance_size = sizeof(RISCVCPU), .instance_align = __alignof(RISCVCPU), .instance_init = riscv_cpu_init, - .instance_post_init = riscv_cpu_post_init, .abstract = true, .class_size = sizeof(RISCVCPUClass), .class_init = riscv_cpu_common_class_init, From 220c739903cec99df032219ac94c45b5269a0ab5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 3 Feb 2025 12:35:39 +0100 Subject: [PATCH 1052/2760] qom: reverse order of instance_post_init calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the instance_post_init calls are performed from the leaf class and all the way up to Object. This is incorrect because the leaf class cannot observe property values applied by the superclasses; for example, a compat property will be set on a device *after* the class's post_init callback has run. In particular this makes it impossible for implementations of accel_cpu_instance_init() to operate based on the actual values of the properties, though it seems that cxl_dsp_instance_post_init and rp_instance_post_init might have similar issues. Follow instead the same order as instance_init, starting with Object and running the child class's instance_post_init after the parent. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Alistair Francis Signed-off-by: Paolo Bonzini --- include/qom/object.h | 3 ++- qom/object.c | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/include/qom/object.h b/include/qom/object.h index 1d5b033724..26df6137b9 100644 --- a/include/qom/object.h +++ b/include/qom/object.h @@ -445,7 +445,8 @@ struct Object * class will have already been initialized so the type is only responsible * for initializing its own members. * @instance_post_init: This function is called to finish initialization of - * an object, after all @instance_init functions were called. + * an object, after all @instance_init functions were called, as well as + * @instance_post_init functions for the parent classes. * @instance_finalize: This function is called during object destruction. This * is called before the parent @instance_finalize function has been called. * An object should only free the members that are unique to its type in this diff --git a/qom/object.c b/qom/object.c index 7b013f40a0..1856bb36c7 100644 --- a/qom/object.c +++ b/qom/object.c @@ -431,13 +431,13 @@ static void object_init_with_type(Object *obj, TypeImpl *ti) static void object_post_init_with_type(Object *obj, TypeImpl *ti) { - if (ti->instance_post_init) { - ti->instance_post_init(obj); - } - if (type_has_parent(ti)) { object_post_init_with_type(obj, type_get_parent(ti)); } + + if (ti->instance_post_init) { + ti->instance_post_init(obj); + } } bool object_apply_global_props(Object *obj, const GPtrArray *props, From c4f88b7136aa5aa5670a16c2f173ffb45da401b2 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Thu, 1 May 2025 11:12:35 -0400 Subject: [PATCH 1053/2760] scripts/vmstate-static-checker.py: Add new hpet entry for num_timers The old "num_timers" got a rename. See commit 1433e38cc8 ("hpet: do not overwrite properties on post_load") for more details. Teach the script to accept the new name. Cc: Paolo Bonzini Cc: Thomas Huth Reviewed-by: Thomas Huth Link: https://lore.kernel.org/r/20250501151235.636709-1-peterx@redhat.com Signed-off-by: Peter Xu --- scripts/vmstate-static-checker.py | 1 + 1 file changed, 1 insertion(+) diff --git a/scripts/vmstate-static-checker.py b/scripts/vmstate-static-checker.py index 25aca839a0..2335e25f94 100755 --- a/scripts/vmstate-static-checker.py +++ b/scripts/vmstate-static-checker.py @@ -91,6 +91,7 @@ def check_fields_match(name, s_field, d_field): 'mem_win_size', 'mig_mem_win_size', 'io_win_addr', 'mig_io_win_addr', 'io_win_size', 'mig_io_win_size'], + 'hpet': ['num_timers', 'num_timers_save'], } if not name in changed_names: From 7b2e4f788d60a8ec25efbf1e6bb6552ee0cef17c Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Fri, 9 May 2025 09:42:10 +0800 Subject: [PATCH 1054/2760] qtest/migration/rdma: Enforce RLIMIT_MEMLOCK >= 128MB requirement Ensure successful migration over RDMA by verifying that RLIMIT_MEMLOCK is set to at least 128MB. This allocation is necessary due to the requirement to pin significant portions of guest memory, typically exceeding 100MB in this test, while the remainder is transmitted as compressed zero pages. Otherwise, it will fail with: stderr: qemu-system-x86_64: cannot get rkey qemu-system-x86_64: error while loading state section id 2(ram) qemu-system-x86_64: load of migration failed: Operation not permitted qemu-system-x86_64: rdma migration: recv polling control error! qemu-system-x86_64: RDMA is in an error state waiting migration to abort! qemu-system-x86_64: failed to save SaveStateEntry with id(name): 2(ram): -1 qemu-system-x86_64: Channel error: Operation not permitted Reported-by: Peter Xu Signed-off-by: Li Zhijian Link: https://lore.kernel.org/r/20250509014211.1272640-1-lizhijian@fujitsu.com Signed-off-by: Peter Xu --- tests/qtest/migration/precopy-tests.c | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index 87b0a7e8ef..5be1cd5742 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -101,6 +101,35 @@ static void test_precopy_unix_dirty_ring(void) #ifdef CONFIG_RDMA +#include + +/* + * During migration over RDMA, it will try to pin portions of guest memory, + * typically exceeding 100MB in this test, while the remainder will be + * transmitted as compressed zero pages. + * + * REQUIRED_MEMLOCK_SZ indicates the minimal mlock size in the current context. + */ +#define REQUIRED_MEMLOCK_SZ (128 << 20) /* 128MB */ + +/* check 'ulimit -l' */ +static bool mlock_check(void) +{ + uid_t uid; + struct rlimit rlim; + + uid = getuid(); + if (uid == 0) { + return true; + } + + if (getrlimit(RLIMIT_MEMLOCK, &rlim) != 0) { + return false; + } + + return rlim.rlim_cur >= REQUIRED_MEMLOCK_SZ; +} + #define RDMA_MIGRATION_HELPER "scripts/rdma-migration-helper.sh" static int new_rdma_link(char *buffer) { @@ -136,6 +165,11 @@ static void test_precopy_rdma_plain(void) { char buffer[128] = {}; + if (!mlock_check()) { + g_test_skip("'ulimit -l' is too small, require >=128M"); + return; + } + if (new_rdma_link(buffer)) { g_test_skip("No rdma link available\n" "# To enable the test:\n" From 6b84c46e8e0ef6f83f33657a29a8abb2b8362d02 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Tue, 13 May 2025 09:22:07 +0800 Subject: [PATCH 1055/2760] qtest/migration/rdma: Add test for rdma migration with ipv6 Recently, we removed ipv6 restriction[0] from RDMA migration, add a test for it. [0] https://lore.kernel.org/qemu-devel/20250326095224.9918-1-jinpu.wang@ionos.com/ Cc: Jack Wang Cc: Michael R. Galaxy Cc: Peter Xu Cc: Yu Zhang Reviewed-by: Jack Wang Signed-off-by: Li Zhijian Link: https://lore.kernel.org/r/20250513012207.2867069-1-lizhijian@fujitsu.com [peterx: Fix over long lines] Signed-off-by: Peter Xu --- scripts/rdma-migration-helper.sh | 57 ++++++++++++++++++++++----- tests/qtest/migration/precopy-tests.c | 21 ++++++++-- 2 files changed, 65 insertions(+), 13 deletions(-) diff --git a/scripts/rdma-migration-helper.sh b/scripts/rdma-migration-helper.sh index a39f2fb0e5..d784d1566a 100755 --- a/scripts/rdma-migration-helper.sh +++ b/scripts/rdma-migration-helper.sh @@ -8,23 +8,44 @@ get_ipv4_addr() head -1 | tr -d '\n' } +get_ipv6_addr() { + ipv6=$(ip -6 -o addr show dev "$1" | + sed -n 's/.*[[:blank:]]inet6[[:blank:]]*\([^[:blank:]/]*\).*/\1/p' | + head -1 | tr -d '\n') + + [ $? -eq 0 ] || return + + if [[ "$ipv6" =~ ^fe80: ]]; then + echo -n "[$ipv6%$1]" + else + echo -n "[$ipv6]" + fi +} + # existing rdma interfaces rdma_interfaces() { - rdma link show | sed -nE 's/^link .* netdev ([^ ]+).*$/\1 /p' + rdma link show | sed -nE 's/^link .* netdev ([^ ]+).*$/\1 /p' | + grep -Ev '^(lo|tun|tap)' } # existing valid ipv4 interfaces ipv4_interfaces() { - ip -o addr show | awk '/inet / {print $2}' | grep -v -w lo + ip -o addr show | awk '/inet / {print $2}' | grep -Ev '^(lo|tun|tap)' +} + +ipv6_interfaces() +{ + ip -o addr show | awk '/inet6 / {print $2}' | grep -Ev '^(lo|tun|tap)' } rdma_rxe_detect() { + family=$1 for r in $(rdma_interfaces) do - ipv4_interfaces | grep -qw $r && get_ipv4_addr $r && return + "$family"_interfaces | grep -qw $r && get_"$family"_addr $r && return done return 1 @@ -32,16 +53,23 @@ rdma_rxe_detect() rdma_rxe_setup() { - for i in $(ipv4_interfaces) + family=$1 + for i in $("$family"_interfaces) do - rdma_interfaces | grep -qw $i && continue + if rdma_interfaces | grep -qw $i; then + echo "$family: Reuse the existing rdma/rxe ${i}_rxe" \ + "for $i with $(get_"$family"_addr $i)" + return + fi + rdma link add "${i}_rxe" type rxe netdev "$i" && { - echo "Setup new rdma/rxe ${i}_rxe for $i with $(get_ipv4_addr $i)" + echo "$family: Setup new rdma/rxe ${i}_rxe" \ + "for $i with $(get_"$family"_addr $i)" return } done - echo "Failed to setup any new rdma/rxe link" >&2 + echo "$family: Failed to setup any new rdma/rxe link" >&2 return 1 } @@ -50,6 +78,12 @@ rdma_rxe_clean() modprobe -r rdma_rxe } +IP_FAMILY=${IP_FAMILY:-ipv4} +if [ "$IP_FAMILY" != "ipv6" ] && [ "$IP_FAMILY" != "ipv4" ]; then + echo "Unknown ip family '$IP_FAMILY', only ipv4 or ipv6 is supported." >&2 + exit 1 +fi + operation=${1:-detect} command -v rdma >/dev/null || { @@ -62,9 +96,14 @@ if [ "$operation" == "setup" ] || [ "$operation" == "clean" ]; then echo "Root privilege is required to setup/clean a rdma/rxe link" >&2 exit 1 } - rdma_rxe_"$operation" + if [ "$operation" == "setup" ]; then + rdma_rxe_setup ipv4 + rdma_rxe_setup ipv6 + else + rdma_rxe_clean + fi elif [ "$operation" == "detect" ]; then - rdma_rxe_detect + rdma_rxe_detect "$IP_FAMILY" else echo "Usage: $0 [setup | detect | clean]" fi diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index 5be1cd5742..a62d3c5378 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -131,12 +131,13 @@ static bool mlock_check(void) } #define RDMA_MIGRATION_HELPER "scripts/rdma-migration-helper.sh" -static int new_rdma_link(char *buffer) +static int new_rdma_link(char *buffer, bool ipv6) { char cmd[256]; bool verbose = g_getenv("QTEST_LOG"); - snprintf(cmd, sizeof(cmd), "%s detect %s", RDMA_MIGRATION_HELPER, + snprintf(cmd, sizeof(cmd), "IP_FAMILY=%s %s detect %s", + ipv6 ? "ipv6" : "ipv4", RDMA_MIGRATION_HELPER, verbose ? "" : "2>/dev/null"); FILE *pipe = popen(cmd, "r"); @@ -161,7 +162,7 @@ static int new_rdma_link(char *buffer) return -1; } -static void test_precopy_rdma_plain(void) +static void __test_precopy_rdma_plain(bool ipv6) { char buffer[128] = {}; @@ -170,7 +171,7 @@ static void test_precopy_rdma_plain(void) return; } - if (new_rdma_link(buffer)) { + if (new_rdma_link(buffer, ipv6)) { g_test_skip("No rdma link available\n" "# To enable the test:\n" "# Run \'" RDMA_MIGRATION_HELPER " setup\' with root to " @@ -193,6 +194,16 @@ static void test_precopy_rdma_plain(void) test_precopy_common(&args); } + +static void test_precopy_rdma_plain(void) +{ + __test_precopy_rdma_plain(false); +} + +static void test_precopy_rdma_plain_ipv6(void) +{ + __test_precopy_rdma_plain(true); +} #endif static void test_precopy_tcp_plain(void) @@ -1226,6 +1237,8 @@ static void migration_test_add_precopy_smoke(MigrationTestEnv *env) #ifdef CONFIG_RDMA migration_test_add("/migration/precopy/rdma/plain", test_precopy_rdma_plain); + migration_test_add("/migration/precopy/rdma/plain/ipv6", + test_precopy_rdma_plain_ipv6); #endif } From e74598a981afe39d6f4951cd5d23101f537957bb Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Tue, 13 May 2025 10:33:51 -0300 Subject: [PATCH 1056/2760] ci: Re-enable python subtests in qtest migration suite The migration compatibility tests have been running with the PYTHON variable unset to avoid running a broken test. The faulty test has since been removed, so we can enable the python tests once again. Aside from the broken test, only one other test uses python and I have been running it locally ever since, so this commit should not expose any new bug. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250513133353.23022-2-farosas@suse.de Signed-off-by: Peter Xu --- .gitlab-ci.d/buildtest.yml | 8 -------- 1 file changed, 8 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index 248aaed137..fbad34138c 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -227,14 +227,6 @@ build-previous-qemu: # testing an old QEMU against new features/tests that it is not # compatible with. - cd build-previous - # Don't allow python-based tests to run. The - # vmstate-checker-script test has a race that causes it to fail - # sometimes. It cannot be fixed it because this job runs the test - # from the old QEMU version. The test will be removed on master, - # but this job will only see the change in the next release. - # - # TODO: remove this line after 9.2 release - - unset PYTHON # old to new - QTEST_QEMU_BINARY_SRC=./qemu-system-${TARGET} QTEST_QEMU_BINARY=../build/qemu-system-${TARGET} ./tests/qtest/migration-test From d5845228b41dbf44f85744e64c3c1744f76a368d Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Tue, 13 May 2025 10:33:52 -0300 Subject: [PATCH 1057/2760] ci: Fix build-previous-qemu when the version tag is absent Stefan reports that during QEMU release, pushing a series with the VERSION bump commit, but not pushing the new git tag in the same command will cause a failure of the build-previous-qemu job at the git fetch step. Since the job is intended to produce a build of the previous QEMU version for consumption by the migration-compat-* jobs, there's no reason to produce a build of the release commit because the migration job would end up testing the release against itself. Skip the job when VERSION contains the newly release version number. I'm opting for 'exit 0' for both the build and the test jobs because allow_failure would mask any real error in the jobs. It also avoids having an orange ! on every release pipeline. Reported-by: Stefan Hajnoczi Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250513133353.23022-3-farosas@suse.de Signed-off-by: Peter Xu --- .gitlab-ci.d/buildtest.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index fbad34138c..b4e39fd7c1 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -203,6 +203,11 @@ build-previous-qemu: GIT_FETCH_EXTRA_FLAGS: --prune --quiet before_script: - source scripts/ci/gitlab-ci-section + # Skip if this series contains the release bump commit. During the + # release process there might be a window of commits when the + # version tag is not yet present in the remote and git fetch would + # fail. + - if grep -q "\.0$" VERSION; then exit 0; fi - export QEMU_PREV_VERSION="$(sed 's/\([0-9.]*\)\.[0-9]*/v\1.0/' VERSION)" - git remote add upstream https://gitlab.com/qemu-project/qemu - git fetch upstream refs/tags/$QEMU_PREV_VERSION:refs/tags/$QEMU_PREV_VERSION @@ -223,6 +228,9 @@ build-previous-qemu: IMAGE: opensuse-leap MAKE_CHECK_ARGS: check-build script: + # Skip for round release numbers, this job is only relevant for + # testing a development tree. + - if grep -q "\.0$" VERSION; then exit 0; fi # Use the migration-tests from the older QEMU tree. This avoids # testing an old QEMU against new features/tests that it is not # compatible with. From 371650534d44c8b27249ca615d908a037f75b048 Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Tue, 13 May 2025 10:33:53 -0300 Subject: [PATCH 1058/2760] ci: Reduce the size of artifacts for build-previous-qemu The build-previous-qemu job is intented to produce a build of the previous QEMU release for consumption by the migration-compat-* jobs. Keep only the pieces of the build that are necessary. Reviewed-by: Peter Xu Signed-off-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250513133353.23022-4-farosas@suse.de Signed-off-by: Peter Xu --- .gitlab-ci.d/buildtest.yml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index b4e39fd7c1..ca1a9c6f70 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -188,12 +188,11 @@ build-previous-qemu: when: on_success expire_in: 2 days paths: - - build-previous - exclude: - - build-previous/**/*.p - - build-previous/**/*.a.p - - build-previous/**/*.c.o - - build-previous/**/*.c.o.d + - build-previous/qemu-bundle + - build-previous/qemu-system-aarch64 + - build-previous/qemu-system-x86_64 + - build-previous/tests/qtest/migration-test + - build-previous/scripts needs: job: amd64-opensuse-leap-container variables: From 249543d0c02d7645b8bcda552dad138769e96831 Mon Sep 17 00:00:00 2001 From: Prasad Pandit Date: Mon, 12 May 2025 18:21:22 +0530 Subject: [PATCH 1059/2760] migration: write zero pages when postcopy enabled During multifd migration, zero pages are written if they are migrated more than once. This may result in a migration thread hang issue when multifd and postcopy are enabled together. When postcopy is enabled, always write zero pages as and when they are migrated. Signed-off-by: Prasad Pandit Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250512125124.147064-2-ppandit@redhat.com Signed-off-by: Peter Xu --- migration/multifd-zero-page.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/migration/multifd-zero-page.c b/migration/multifd-zero-page.c index dbc1184921..4cde868159 100644 --- a/migration/multifd-zero-page.c +++ b/migration/multifd-zero-page.c @@ -85,9 +85,27 @@ void multifd_recv_zero_page_process(MultiFDRecvParams *p) { for (int i = 0; i < p->zero_num; i++) { void *page = p->host + p->zero[i]; - if (ramblock_recv_bitmap_test_byte_offset(p->block, p->zero[i])) { + bool received = + ramblock_recv_bitmap_test_byte_offset(p->block, p->zero[i]); + + /* + * During multifd migration zero page is written to the memory + * only if it is migrated more than once. + * + * It becomes a problem when both multifd & postcopy options are + * enabled. If the zero page which was skipped during multifd phase, + * is accessed during the postcopy phase of the migration, a page + * fault occurs. But this page fault is not served because the + * 'receivedmap' says the zero page is already received. Thus the + * thread accessing that page may hang. + * + * When postcopy is enabled, always write the zero page as and when + * it is migrated. + */ + if (migrate_postcopy_ram() || received) { memset(page, 0, multifd_ram_page_size()); - } else { + } + if (!received) { ramblock_recv_bitmap_set_offset(p->block, p->zero[i]); } } From e27418861288285d20352448fef4491a68223d39 Mon Sep 17 00:00:00 2001 From: Prasad Pandit Date: Mon, 12 May 2025 18:21:23 +0530 Subject: [PATCH 1060/2760] migration: enable multifd and postcopy together Enable Multifd and Postcopy migration together. The migration_ioc_process_incoming() routine checks magic value sent on each channel and helps to properly setup multifd and postcopy channels. The Precopy and Multifd threads work during the initial guest RAM transfer. When migration moves to the Postcopy phase, the multifd threads cease to send data on multifd channels and Postcopy threads on the destination request/pull data from the source side. Reviewed-by: Fabiano Rosas Signed-off-by: Prasad Pandit Link: https://lore.kernel.org/r/20250512125124.147064-3-ppandit@redhat.com Signed-off-by: Peter Xu --- migration/multifd-nocomp.c | 3 ++- migration/multifd.c | 7 +++++++ migration/options.c | 5 ----- migration/ram.c | 5 ++--- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/migration/multifd-nocomp.c b/migration/multifd-nocomp.c index 88fe0f99f2..b48eae3d86 100644 --- a/migration/multifd-nocomp.c +++ b/migration/multifd-nocomp.c @@ -17,6 +17,7 @@ #include "migration-stats.h" #include "multifd.h" #include "options.h" +#include "migration.h" #include "qapi/error.h" #include "qemu/cutils.h" #include "qemu/error-report.h" @@ -398,7 +399,7 @@ int multifd_ram_flush_and_sync(QEMUFile *f) MultiFDSyncReq req; int ret; - if (!migrate_multifd()) { + if (!migrate_multifd() || migration_in_postcopy()) { return 0; } diff --git a/migration/multifd.c b/migration/multifd.c index ec108af624..f18b166bcf 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -1379,6 +1379,13 @@ static void *multifd_recv_thread(void *opaque) } if (has_data) { + /* + * multifd thread should not be active and receive data + * when migration is in the Postcopy phase. Two threads + * writing the same memory area could easily corrupt + * the guest state. + */ + assert(!migration_in_postcopy()); if (is_device_state) { assert(use_packets); ret = multifd_device_state_recv(p, &local_err); diff --git a/migration/options.c b/migration/options.c index b6ae95358d..3fcd577cd7 100644 --- a/migration/options.c +++ b/migration/options.c @@ -509,11 +509,6 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) error_setg(errp, "Postcopy is not compatible with ignore-shared"); return false; } - - if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) { - error_setg(errp, "Postcopy is not yet compatible with multifd"); - return false; - } } if (new_caps[MIGRATION_CAPABILITY_BACKGROUND_SNAPSHOT]) { diff --git a/migration/ram.c b/migration/ram.c index e12913b43e..d26dbd37c4 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -1993,9 +1993,8 @@ static int ram_save_target_page(RAMState *rs, PageSearchStatus *pss) } } - if (migrate_multifd()) { - RAMBlock *block = pss->block; - return ram_save_multifd_page(block, offset); + if (migrate_multifd() && !migration_in_postcopy()) { + return ram_save_multifd_page(pss->block, offset); } return ram_save_page(rs, pss); From 766bbabac8f00bc5cf23ba90a8326678636280ed Mon Sep 17 00:00:00 2001 From: Prasad Pandit Date: Mon, 12 May 2025 18:21:24 +0530 Subject: [PATCH 1061/2760] tests/qtest/migration: add postcopy tests with multifd Add new qtests to run postcopy migration with multifd channels enabled. Signed-off-by: Prasad Pandit Link: https://lore.kernel.org/r/20250512125124.147064-4-ppandit@redhat.com [peterx: rename all new tests to be under /migration/multifd+postcopy/] Signed-off-by: Peter Xu --- tests/qtest/migration/compression-tests.c | 18 ++++++++ tests/qtest/migration/postcopy-tests.c | 27 ++++++++++++ tests/qtest/migration/precopy-tests.c | 28 ++++++++++++- tests/qtest/migration/tls-tests.c | 51 +++++++++++++++++++++++ 4 files changed, 122 insertions(+), 2 deletions(-) diff --git a/tests/qtest/migration/compression-tests.c b/tests/qtest/migration/compression-tests.c index 41e79f031b..b827665b8e 100644 --- a/tests/qtest/migration/compression-tests.c +++ b/tests/qtest/migration/compression-tests.c @@ -42,6 +42,20 @@ static void test_multifd_tcp_zstd(void) }; test_precopy_common(&args); } + +static void test_multifd_postcopy_tcp_zstd(void) +{ + MigrateCommon args = { + .listen_uri = "defer", + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true, + }, + .start_hook = migrate_hook_start_precopy_tcp_multifd_zstd, + }; + + test_precopy_common(&args); +} #endif /* CONFIG_ZSTD */ #ifdef CONFIG_QATZIP @@ -184,6 +198,10 @@ void migration_test_add_compression(MigrationTestEnv *env) #ifdef CONFIG_ZSTD migration_test_add("/migration/multifd/tcp/plain/zstd", test_multifd_tcp_zstd); + if (env->has_uffd) { + migration_test_add("/migration/multifd+postcopy/tcp/plain/zstd", + test_multifd_postcopy_tcp_zstd); + } #endif #ifdef CONFIG_QATZIP diff --git a/tests/qtest/migration/postcopy-tests.c b/tests/qtest/migration/postcopy-tests.c index 483e3ff99f..3773525843 100644 --- a/tests/qtest/migration/postcopy-tests.c +++ b/tests/qtest/migration/postcopy-tests.c @@ -94,6 +94,29 @@ static void migration_test_add_postcopy_smoke(MigrationTestEnv *env) } } +static void test_multifd_postcopy(void) +{ + MigrateCommon args = { + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + + test_postcopy_common(&args); +} + +static void test_multifd_postcopy_preempt(void) +{ + MigrateCommon args = { + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_common(&args); +} + void migration_test_add_postcopy(MigrationTestEnv *env) { migration_test_add_postcopy_smoke(env); @@ -114,6 +137,10 @@ void migration_test_add_postcopy(MigrationTestEnv *env) "/migration/postcopy/recovery/double-failures/reconnect", test_postcopy_recovery_fail_reconnect); + migration_test_add("/migration/multifd+postcopy/plain", + test_multifd_postcopy); + migration_test_add("/migration/multifd+postcopy/preempt/plain", + test_multifd_postcopy_preempt); if (env->is_x86) { migration_test_add("/migration/postcopy/suspend", test_postcopy_suspend); diff --git a/tests/qtest/migration/precopy-tests.c b/tests/qtest/migration/precopy-tests.c index a62d3c5378..bb38292550 100644 --- a/tests/qtest/migration/precopy-tests.c +++ b/tests/qtest/migration/precopy-tests.c @@ -569,7 +569,7 @@ static void test_multifd_tcp_channels_none(void) * * And see that it works */ -static void test_multifd_tcp_cancel(void) +static void test_multifd_tcp_cancel(bool postcopy_ram) { MigrateStart args = { .hide_stderr = true, @@ -583,6 +583,11 @@ static void test_multifd_tcp_cancel(void) migrate_ensure_non_converge(from); migrate_prepare_for_dirty_mem(from); + if (postcopy_ram) { + migrate_set_capability(from, "postcopy-ram", true); + migrate_set_capability(to, "postcopy-ram", true); + } + migrate_set_parameter_int(from, "multifd-channels", 16); migrate_set_parameter_int(to, "multifd-channels", 16); @@ -624,6 +629,10 @@ static void test_multifd_tcp_cancel(void) return; } + if (postcopy_ram) { + migrate_set_capability(to2, "postcopy-ram", true); + } + migrate_set_parameter_int(to2, "multifd-channels", 16); migrate_set_capability(to2, "multifd", true); @@ -647,6 +656,16 @@ static void test_multifd_tcp_cancel(void) migrate_end(from, to2, true); } +static void test_multifd_precopy_tcp_cancel(void) +{ + test_multifd_tcp_cancel(false); +} + +static void test_multifd_postcopy_tcp_cancel(void) +{ + test_multifd_tcp_cancel(true); +} + static void test_cancel_src_after_failed(QTestState *from, QTestState *to, const char *uri, const char *phase) { @@ -1233,7 +1252,12 @@ static void migration_test_add_precopy_smoke(MigrationTestEnv *env) migration_test_add("/migration/multifd/tcp/uri/plain/none", test_multifd_tcp_uri_none); migration_test_add("/migration/multifd/tcp/plain/cancel", - test_multifd_tcp_cancel); + test_multifd_precopy_tcp_cancel); + if (env->has_uffd) { + migration_test_add("/migration/multifd+postcopy/tcp/plain/cancel", + test_multifd_postcopy_tcp_cancel); + } + #ifdef CONFIG_RDMA migration_test_add("/migration/precopy/rdma/plain", test_precopy_rdma_plain); diff --git a/tests/qtest/migration/tls-tests.c b/tests/qtest/migration/tls-tests.c index 72f44defbb..21e9fec87d 100644 --- a/tests/qtest/migration/tls-tests.c +++ b/tests/qtest/migration/tls-tests.c @@ -395,6 +395,19 @@ static void test_postcopy_recovery_tls_psk(void) test_postcopy_recovery_common(&args); } +static void test_multifd_postcopy_recovery_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + }, + }; + + test_postcopy_recovery_common(&args); +} + /* This contains preempt+recovery+tls test altogether */ static void test_postcopy_preempt_all(void) { @@ -409,6 +422,20 @@ static void test_postcopy_preempt_all(void) test_postcopy_recovery_common(&args); } +static void test_multifd_postcopy_preempt_recovery_tls_psk(void) +{ + MigrateCommon args = { + .start_hook = migrate_hook_start_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_POSTCOPY_PREEMPT] = true, + }, + }; + + test_postcopy_recovery_common(&args); +} + static void test_precopy_unix_tls_psk(void) { g_autofree char *uri = g_strdup_printf("unix:%s/migsocket", tmpfs); @@ -657,6 +684,21 @@ static void test_multifd_tcp_tls_psk_mismatch(void) test_precopy_common(&args); } +static void test_multifd_postcopy_tcp_tls_psk_match(void) +{ + MigrateCommon args = { + .start = { + .caps[MIGRATION_CAPABILITY_MULTIFD] = true, + .caps[MIGRATION_CAPABILITY_POSTCOPY_RAM] = true, + }, + .listen_uri = "defer", + .start_hook = migrate_hook_start_multifd_tcp_tls_psk_match, + .end_hook = migrate_hook_end_tls_psk, + }; + + test_precopy_common(&args); +} + #ifdef CONFIG_TASN1 static void test_multifd_tcp_tls_x509_default_host(void) { @@ -774,6 +816,11 @@ void migration_test_add_tls(MigrationTestEnv *env) test_postcopy_preempt_tls_psk); migration_test_add("/migration/postcopy/preempt/recovery/tls/psk", test_postcopy_preempt_all); + migration_test_add("/migration/multifd+postcopy/recovery/tls/psk", + test_multifd_postcopy_recovery_tls_psk); + migration_test_add( + "/migration/multifd+postcopy/preempt/recovery/tls/psk", + test_multifd_postcopy_preempt_recovery_tls_psk); } #ifdef CONFIG_TASN1 migration_test_add("/migration/precopy/unix/tls/x509/default-host", @@ -805,6 +852,10 @@ void migration_test_add_tls(MigrationTestEnv *env) test_multifd_tcp_tls_psk_match); migration_test_add("/migration/multifd/tcp/tls/psk/mismatch", test_multifd_tcp_tls_psk_mismatch); + if (env->has_uffd) { + migration_test_add("/migration/multifd+postcopy/tcp/tls/psk/match", + test_multifd_postcopy_tcp_tls_psk_match); + } #ifdef CONFIG_TASN1 migration_test_add("/migration/multifd/tcp/tls/x509/default-host", test_multifd_tcp_tls_x509_default_host); From 6be7696129b302830a9cff7e30484e08c2d64b57 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Fri, 16 May 2025 15:53:03 +0200 Subject: [PATCH 1062/2760] migration/multifd: Don't send device state packets with zerocopy flag If zerocopy is enabled for multifd then QIO_CHANNEL_WRITE_FLAG_ZERO_COPY flag is forced into all multifd channel write calls via p->write_flags that was setup in multifd_nocomp_send_setup(). However, device state packets aren't compatible with zerocopy - the data buffer isn't getting kept pinned until multifd channel flush. Make sure to mask that QIO_CHANNEL_WRITE_FLAG_ZERO_COPY flag in a multifd send thread if the data being sent is device state. Fixes: 0525b91a0b99 ("migration/multifd: Device state transfer support - send side") Signed-off-by: Maciej S. Szmigiero Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/3bd5f48578e29f3a78f41b1e4fbea3d4b2d9b136.1747403393.git.maciej.szmigiero@oracle.com Signed-off-by: Peter Xu --- migration/multifd.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/migration/multifd.c b/migration/multifd.c index f18b166bcf..b255778855 100644 --- a/migration/multifd.c +++ b/migration/multifd.c @@ -690,6 +690,7 @@ static void *multifd_send_thread(void *opaque) if (qatomic_load_acquire(&p->pending_job)) { bool is_device_state = multifd_payload_device_state(p->data); size_t total_size; + int write_flags_masked = 0; p->flags = 0; p->iovs_num = 0; @@ -697,6 +698,9 @@ static void *multifd_send_thread(void *opaque) if (is_device_state) { multifd_device_state_send_prepare(p); + + /* Device state packets cannot be sent via zerocopy */ + write_flags_masked |= QIO_CHANNEL_WRITE_FLAG_ZERO_COPY; } else { ret = multifd_send_state->ops->send_prepare(p, &local_err); if (ret != 0) { @@ -718,7 +722,8 @@ static void *multifd_send_thread(void *opaque) &p->data->u.ram, &local_err); } else { ret = qio_channel_writev_full_all(p->c, p->iov, p->iovs_num, - NULL, 0, p->write_flags, + NULL, 0, + p->write_flags & ~write_flags_masked, &local_err); } From 17bec9235bb0775cf8dec4103c167757ee8898f3 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 13 May 2025 17:33:16 -0400 Subject: [PATCH 1063/2760] migration: Allow caps to be set when preempt or multifd cap enabled With commit 82137e6c8c ("migration: enforce multifd and postcopy preempt to be set before incoming"), and if postcopy preempt / multifd is enabled, one cannot setup any capability because these checks would always fail. (qemu) migrate_set_capability xbzrle off Error: Postcopy preempt must be set before incoming starts To fix it, check existing cap and only raise an error if the specific cap changed. Fixes: 82137e6c8c ("migration: enforce multifd and postcopy preempt to be set before incoming") Reviewed-by: Dr. David Alan Gilbert Reviewed-by: Juraj Marcin Signed-off-by: Peter Xu --- migration/options.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/migration/options.c b/migration/options.c index 3fcd577cd7..162c72cda4 100644 --- a/migration/options.c +++ b/migration/options.c @@ -568,7 +568,7 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) return false; } - if (migrate_incoming_started()) { + if (!migrate_postcopy_preempt() && migrate_incoming_started()) { error_setg(errp, "Postcopy preempt must be set before incoming starts"); return false; @@ -576,7 +576,7 @@ bool migrate_caps_check(bool *old_caps, bool *new_caps, Error **errp) } if (new_caps[MIGRATION_CAPABILITY_MULTIFD]) { - if (migrate_incoming_started()) { + if (!migrate_multifd() && migrate_incoming_started()) { error_setg(errp, "Multifd must be set before incoming starts"); return false; } From e09c6d837593aa1e12d92d7031c65a881eb2eb27 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Tue, 13 May 2025 17:08:17 -0400 Subject: [PATCH 1064/2760] migration/hmp: Add "info migrate -a", reorg the dump A new parameter "-a" is added to "info migrate" to dump all info, while when not specified it only dumps the important ones. When at it, reorg everything to make it easier to read for human. The general rule is: - Put important things at the top - Reuse a single line when things are very relevant, hence reducing lines needed to show the results - Remove almost useless ones (e.g. "normal_bytes", while we also have both "page size" and "normal" pages) - Regroup things, so that related fields will show together - etc. Before this change, it looks like (one example of a completed case): globals: store-global-state: on only-migratable: off send-configuration: on send-section-footer: on send-switchover-start: on clear-bitmap-shift: 18 Migration status: completed total time: 122952 ms downtime: 76 ms setup: 15 ms transferred ram: 130825923 kbytes throughput: 8717.68 mbps remaining ram: 0 kbytes total ram: 16777992 kbytes duplicate: 997263 pages normal: 32622225 pages normal bytes: 130488900 kbytes dirty sync count: 10 page size: 4 kbytes multifd bytes: 117134260 kbytes pages-per-second: 169431 postcopy request count: 5835 precopy ram: 15 kbytes postcopy ram: 13691151 kbytes After this change, sample output (default, no "-a" specified): Status: postcopy-active Time (ms): total=40504, setup=14, down=145 RAM info: Bandwidth (mbps): 6102.65 Sizes (KB): psize=4, total=16777992, transferred=37673019, remain=2136404, precopy=3, multifd=26108780, postcopy=11563855 Pages: normal=9394288, zero=600672, rate_per_sec=185875 Others: dirty_syncs=3, dirty_pages_rate=278378, postcopy_req=4078 Sample output when "-a" specified: Status: active Time (ms): total=3040, setup=4, exp_down=300 RAM info: Throughput (mbps): 10.51 Sizes (KB): psize=4, total=4211528, transferred=3979, remain=4206452, precopy=3978, multifd=0, postcopy=0 Pages: normal=992, zero=277, rate_per_sec=320 Others: dirty_syncs=1 Globals: store-global-state: on only-migratable: off send-configuration: on send-section-footer: on send-switchover-start: on clear-bitmap-shift: 18 XBZRLE: size=67108864, transferred=0, pages=0, miss=188451 miss_rate=0.00, encode_rate=0.00, overflow=0 CPU Throttle (%): 0 Dirty-limit Throttle (us): 0 Dirty-limit Ring Full (us): 0 Postcopy Blocktime (ms): 0 Postcopy vCPU Blocktime: ... Reviewed-by: Dr. David Alan Gilbert Tested-by: Mario Casquero [peterx: print "," too in 1st line of RAM info] Signed-off-by: Peter Xu --- hmp-commands-info.hx | 6 +- migration/migration-hmp-cmds.c | 186 +++++++++++++++++---------------- 2 files changed, 99 insertions(+), 93 deletions(-) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index c59cd6637b..639a450ee5 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -475,9 +475,9 @@ ERST { .name = "migrate", - .args_type = "", - .params = "", - .help = "show migration status", + .args_type = "all:-a", + .params = "[-a]", + .help = "show migration status (-a: all, dump all status)", .cmd = hmp_info_migrate, }, diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 49c26daed3..e8a563c7d8 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -37,29 +37,28 @@ static void migration_global_dump(Monitor *mon) { MigrationState *ms = migrate_get_current(); - monitor_printf(mon, "globals:\n"); - monitor_printf(mon, "store-global-state: %s\n", + monitor_printf(mon, "Globals:\n"); + monitor_printf(mon, " store-global-state: %s\n", ms->store_global_state ? "on" : "off"); - monitor_printf(mon, "only-migratable: %s\n", + monitor_printf(mon, " only-migratable: %s\n", only_migratable ? "on" : "off"); - monitor_printf(mon, "send-configuration: %s\n", + monitor_printf(mon, " send-configuration: %s\n", ms->send_configuration ? "on" : "off"); - monitor_printf(mon, "send-section-footer: %s\n", + monitor_printf(mon, " send-section-footer: %s\n", ms->send_section_footer ? "on" : "off"); - monitor_printf(mon, "send-switchover-start: %s\n", + monitor_printf(mon, " send-switchover-start: %s\n", ms->send_switchover_start ? "on" : "off"); - monitor_printf(mon, "clear-bitmap-shift: %u\n", + monitor_printf(mon, " clear-bitmap-shift: %u\n", ms->clear_bitmap_shift); } void hmp_info_migrate(Monitor *mon, const QDict *qdict) { + bool show_all = qdict_get_try_bool(qdict, "all", false); MigrationInfo *info; info = qmp_query_migrate(NULL); - migration_global_dump(mon); - if (info->blocked_reasons) { strList *reasons = info->blocked_reasons; monitor_printf(mon, "Outgoing migration blocked:\n"); @@ -70,7 +69,7 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) } if (info->has_status) { - monitor_printf(mon, "Migration status: %s", + monitor_printf(mon, "Status: %s", MigrationStatus_str(info->status)); if (info->status == MIGRATION_STATUS_FAILED && info->error_desc) { monitor_printf(mon, " (%s)\n", info->error_desc); @@ -78,107 +77,130 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) monitor_printf(mon, "\n"); } - monitor_printf(mon, "total time: %" PRIu64 " ms\n", - info->total_time); - if (info->has_expected_downtime) { - monitor_printf(mon, "expected downtime: %" PRIu64 " ms\n", - info->expected_downtime); - } - if (info->has_downtime) { - monitor_printf(mon, "downtime: %" PRIu64 " ms\n", - info->downtime); + if (info->total_time) { + monitor_printf(mon, "Time (ms): total=%" PRIu64, + info->total_time); + if (info->has_setup_time) { + monitor_printf(mon, ", setup=%" PRIu64, + info->setup_time); + } + if (info->has_expected_downtime) { + monitor_printf(mon, ", exp_down=%" PRIu64, + info->expected_downtime); + } + if (info->has_downtime) { + monitor_printf(mon, ", down=%" PRIu64, + info->downtime); + } + monitor_printf(mon, "\n"); } - if (info->has_setup_time) { - monitor_printf(mon, "setup: %" PRIu64 " ms\n", - info->setup_time); + } + + if (info->has_socket_address) { + SocketAddressList *addr; + + monitor_printf(mon, "Sockets: [\n"); + + for (addr = info->socket_address; addr; addr = addr->next) { + char *s = socket_uri(addr->value); + monitor_printf(mon, "\t%s\n", s); + g_free(s); } + monitor_printf(mon, "]\n"); } if (info->ram) { - monitor_printf(mon, "transferred ram: %" PRIu64 " kbytes\n", - info->ram->transferred >> 10); - monitor_printf(mon, "throughput: %0.2f mbps\n", + monitor_printf(mon, "RAM info:\n"); + monitor_printf(mon, " Throughput (Mbps): %0.2f\n", info->ram->mbps); - monitor_printf(mon, "remaining ram: %" PRIu64 " kbytes\n", - info->ram->remaining >> 10); - monitor_printf(mon, "total ram: %" PRIu64 " kbytes\n", + monitor_printf(mon, " Sizes (KiB): pagesize=%" PRIu64 + ", total=%" PRIu64 ",\n", + info->ram->page_size >> 10, info->ram->total >> 10); - monitor_printf(mon, "duplicate: %" PRIu64 " pages\n", - info->ram->duplicate); - monitor_printf(mon, "normal: %" PRIu64 " pages\n", - info->ram->normal); - monitor_printf(mon, "normal bytes: %" PRIu64 " kbytes\n", - info->ram->normal_bytes >> 10); - monitor_printf(mon, "dirty sync count: %" PRIu64 "\n", - info->ram->dirty_sync_count); - monitor_printf(mon, "page size: %" PRIu64 " kbytes\n", - info->ram->page_size >> 10); - monitor_printf(mon, "multifd bytes: %" PRIu64 " kbytes\n", - info->ram->multifd_bytes >> 10); - monitor_printf(mon, "pages-per-second: %" PRIu64 "\n", + monitor_printf(mon, " transferred=%" PRIu64 + ", remain=%" PRIu64 ",\n", + info->ram->transferred >> 10, + info->ram->remaining >> 10); + monitor_printf(mon, " precopy=%" PRIu64 + ", multifd=%" PRIu64 + ", postcopy=%" PRIu64, + info->ram->precopy_bytes >> 10, + info->ram->multifd_bytes >> 10, + info->ram->postcopy_bytes >> 10); + + if (info->vfio) { + monitor_printf(mon, ", vfio=%" PRIu64, + info->vfio->transferred >> 10); + } + monitor_printf(mon, "\n"); + + monitor_printf(mon, " Pages: normal=%" PRIu64 ", zero=%" PRIu64 + ", rate_per_sec=%" PRIu64 "\n", + info->ram->normal, + info->ram->duplicate, info->ram->pages_per_second); + monitor_printf(mon, " Others: dirty_syncs=%" PRIu64, + info->ram->dirty_sync_count); if (info->ram->dirty_pages_rate) { - monitor_printf(mon, "dirty pages rate: %" PRIu64 " pages\n", + monitor_printf(mon, ", dirty_pages_rate=%" PRIu64, info->ram->dirty_pages_rate); } if (info->ram->postcopy_requests) { - monitor_printf(mon, "postcopy request count: %" PRIu64 "\n", + monitor_printf(mon, ", postcopy_req=%" PRIu64, info->ram->postcopy_requests); } - if (info->ram->precopy_bytes) { - monitor_printf(mon, "precopy ram: %" PRIu64 " kbytes\n", - info->ram->precopy_bytes >> 10); - } if (info->ram->downtime_bytes) { - monitor_printf(mon, "downtime ram: %" PRIu64 " kbytes\n", - info->ram->downtime_bytes >> 10); - } - if (info->ram->postcopy_bytes) { - monitor_printf(mon, "postcopy ram: %" PRIu64 " kbytes\n", - info->ram->postcopy_bytes >> 10); + monitor_printf(mon, ", downtime_ram=%" PRIu64, + info->ram->downtime_bytes); } if (info->ram->dirty_sync_missed_zero_copy) { - monitor_printf(mon, - "Zero-copy-send fallbacks happened: %" PRIu64 " times\n", + monitor_printf(mon, ", zerocopy_fallbacks=%" PRIu64, info->ram->dirty_sync_missed_zero_copy); } + monitor_printf(mon, "\n"); + } + + if (!show_all) { + goto out; } + migration_global_dump(mon); + if (info->xbzrle_cache) { - monitor_printf(mon, "cache size: %" PRIu64 " bytes\n", - info->xbzrle_cache->cache_size); - monitor_printf(mon, "xbzrle transferred: %" PRIu64 " kbytes\n", - info->xbzrle_cache->bytes >> 10); - monitor_printf(mon, "xbzrle pages: %" PRIu64 " pages\n", - info->xbzrle_cache->pages); - monitor_printf(mon, "xbzrle cache miss: %" PRIu64 " pages\n", - info->xbzrle_cache->cache_miss); - monitor_printf(mon, "xbzrle cache miss rate: %0.2f\n", - info->xbzrle_cache->cache_miss_rate); - monitor_printf(mon, "xbzrle encoding rate: %0.2f\n", - info->xbzrle_cache->encoding_rate); - monitor_printf(mon, "xbzrle overflow: %" PRIu64 "\n", + monitor_printf(mon, "XBZRLE: size=%" PRIu64 + ", transferred=%" PRIu64 + ", pages=%" PRIu64 + ", miss=%" PRIu64 "\n" + " miss_rate=%0.2f" + ", encode_rate=%0.2f" + ", overflow=%" PRIu64 "\n", + info->xbzrle_cache->cache_size, + info->xbzrle_cache->bytes, + info->xbzrle_cache->pages, + info->xbzrle_cache->cache_miss, + info->xbzrle_cache->cache_miss_rate, + info->xbzrle_cache->encoding_rate, info->xbzrle_cache->overflow); } if (info->has_cpu_throttle_percentage) { - monitor_printf(mon, "cpu throttle percentage: %" PRIu64 "\n", + monitor_printf(mon, "CPU Throttle (%%): %" PRIu64 "\n", info->cpu_throttle_percentage); } if (info->has_dirty_limit_throttle_time_per_round) { - monitor_printf(mon, "dirty-limit throttle time: %" PRIu64 " us\n", + monitor_printf(mon, "Dirty-limit Throttle (us): %" PRIu64 "\n", info->dirty_limit_throttle_time_per_round); } if (info->has_dirty_limit_ring_full_time) { - monitor_printf(mon, "dirty-limit ring full time: %" PRIu64 " us\n", + monitor_printf(mon, "Dirty-limit Ring Full (us): %" PRIu64 "\n", info->dirty_limit_ring_full_time); } if (info->has_postcopy_blocktime) { - monitor_printf(mon, "postcopy blocktime: %u\n", + monitor_printf(mon, "Postcopy Blocktime (ms): %" PRIu32 "\n", info->postcopy_blocktime); } @@ -189,28 +211,12 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) visit_type_uint32List(v, NULL, &info->postcopy_vcpu_blocktime, &error_abort); visit_complete(v, &str); - monitor_printf(mon, "postcopy vcpu blocktime: %s\n", str); + monitor_printf(mon, "Postcopy vCPU Blocktime: %s\n", str); g_free(str); visit_free(v); } - if (info->has_socket_address) { - SocketAddressList *addr; - - monitor_printf(mon, "socket address: [\n"); - - for (addr = info->socket_address; addr; addr = addr->next) { - char *s = socket_uri(addr->value); - monitor_printf(mon, "\t%s\n", s); - g_free(s); - } - monitor_printf(mon, "]\n"); - } - - if (info->vfio) { - monitor_printf(mon, "vfio device transferred: %" PRIu64 " kbytes\n", - info->vfio->transferred >> 10); - } +out: qapi_free_MigrationInfo(info); } From e460991883d7209d52d0fdb534d9cd8cce0f9cce Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Fri, 2 May 2025 13:11:14 +0100 Subject: [PATCH 1065/2760] scsi-disk: Add native FUA write support Simply propagate the FUA flag on write requests to the driver. The block layer will emulate it if necessary. Signed-off-by: Alberto Faria Message-ID: <20250502121115.3613717-2-afaria@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- hw/scsi/scsi-disk.c | 53 +++++++++++++-------------------------------- 1 file changed, 15 insertions(+), 38 deletions(-) diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index cb4af1b715..738d8df8ec 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -74,7 +74,7 @@ struct SCSIDiskClass { */ DMAIOFunc *dma_readv; DMAIOFunc *dma_writev; - bool (*need_fua_emulation)(SCSICommand *cmd); + bool (*need_fua)(SCSICommand *cmd); void (*update_sense)(SCSIRequest *r); }; @@ -85,7 +85,7 @@ typedef struct SCSIDiskReq { uint32_t sector_count; uint32_t buflen; bool started; - bool need_fua_emulation; + bool need_fua; struct iovec iov; QEMUIOVector qiov; BlockAcctCookie acct; @@ -389,24 +389,6 @@ static bool scsi_is_cmd_fua(SCSICommand *cmd) } } -static void scsi_write_do_fua(SCSIDiskReq *r) -{ - SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - - assert(r->req.aiocb == NULL); - assert(!r->req.io_canceled); - - if (r->need_fua_emulation) { - block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, - BLOCK_ACCT_FLUSH); - r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_aio_complete, r); - return; - } - - scsi_req_complete(&r->req, GOOD); - scsi_req_unref(&r->req); -} - static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret) { assert(r->req.aiocb == NULL); @@ -416,12 +398,7 @@ static void scsi_dma_complete_noio(SCSIDiskReq *r, int ret) r->sector += r->sector_count; r->sector_count = 0; - if (r->req.cmd.mode == SCSI_XFER_TO_DEV) { - scsi_write_do_fua(r); - return; - } else { - scsi_req_complete(&r->req, GOOD); - } + scsi_req_complete(&r->req, GOOD); done: scsi_req_unref(&r->req); @@ -564,7 +541,7 @@ static void scsi_read_data(SCSIRequest *req) first = !r->started; r->started = true; - if (first && r->need_fua_emulation) { + if (first && r->need_fua) { block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, BLOCK_ACCT_FLUSH); r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, scsi_do_read_cb, r); @@ -589,8 +566,7 @@ static void scsi_write_complete_noio(SCSIDiskReq *r, int ret) r->sector += n; r->sector_count -= n; if (r->sector_count == 0) { - scsi_write_do_fua(r); - return; + scsi_req_complete(&r->req, GOOD); } else { scsi_init_iovec(r, SCSI_DMA_BUF_SIZE); trace_scsi_disk_write_complete_noio(r->req.tag, r->qiov.size); @@ -623,6 +599,7 @@ static void scsi_write_data(SCSIRequest *req) SCSIDiskReq *r = DO_UPCAST(SCSIDiskReq, req, req); SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); SCSIDiskClass *sdc = (SCSIDiskClass *) object_get_class(OBJECT(s)); + BlockCompletionFunc *cb; /* No data transfer may already be in progress */ assert(r->req.aiocb == NULL); @@ -648,11 +625,10 @@ static void scsi_write_data(SCSIRequest *req) if (r->req.cmd.buf[0] == VERIFY_10 || r->req.cmd.buf[0] == VERIFY_12 || r->req.cmd.buf[0] == VERIFY_16) { - if (r->req.sg) { - scsi_dma_complete_noio(r, 0); - } else { - scsi_write_complete_noio(r, 0); - } + block_acct_start(blk_get_stats(s->qdev.conf.blk), &r->acct, 0, + BLOCK_ACCT_FLUSH); + cb = r->req.sg ? scsi_dma_complete : scsi_write_complete; + r->req.aiocb = blk_aio_flush(s->qdev.conf.blk, cb, r); return; } @@ -2391,7 +2367,7 @@ static int32_t scsi_disk_dma_command(SCSIRequest *req, uint8_t *buf) scsi_check_condition(r, SENSE_CODE(LBA_OUT_OF_RANGE)); return 0; } - r->need_fua_emulation = sdc->need_fua_emulation(&r->req.cmd); + r->need_fua = sdc->need_fua(&r->req.cmd); if (r->sector_count == 0) { scsi_req_complete(&r->req, GOOD); } @@ -3137,7 +3113,8 @@ BlockAIOCB *scsi_dma_writev(int64_t offset, QEMUIOVector *iov, { SCSIDiskReq *r = opaque; SCSIDiskState *s = DO_UPCAST(SCSIDiskState, qdev, r->req.dev); - return blk_aio_pwritev(s->qdev.conf.blk, offset, iov, 0, cb, cb_opaque); + int flags = r->need_fua ? BDRV_REQ_FUA : 0; + return blk_aio_pwritev(s->qdev.conf.blk, offset, iov, flags, cb, cb_opaque); } static char *scsi_property_get_loadparm(Object *obj, Error **errp) @@ -3186,7 +3163,7 @@ static void scsi_disk_base_class_initfn(ObjectClass *klass, const void *data) device_class_set_legacy_reset(dc, scsi_disk_reset); sdc->dma_readv = scsi_dma_readv; sdc->dma_writev = scsi_dma_writev; - sdc->need_fua_emulation = scsi_is_cmd_fua; + sdc->need_fua = scsi_is_cmd_fua; } static const TypeInfo scsi_disk_base_info = { @@ -3338,7 +3315,7 @@ static void scsi_block_class_initfn(ObjectClass *klass, const void *data) sdc->dma_readv = scsi_block_dma_readv; sdc->dma_writev = scsi_block_dma_writev; sdc->update_sense = scsi_block_update_sense; - sdc->need_fua_emulation = scsi_block_no_fua; + sdc->need_fua = scsi_block_no_fua; dc->desc = "SCSI block device passthrough"; device_class_set_props(dc, scsi_block_properties); dc->vmsd = &vmstate_scsi_disk_state; From 5562e214e82ae4bcb0b642cc52b304bdc78a58c3 Mon Sep 17 00:00:00 2001 From: Alberto Faria Date: Fri, 2 May 2025 13:11:15 +0100 Subject: [PATCH 1066/2760] scsi-disk: Advertise FUA support by default Allow the guest to submit FUA requests directly, instead of forcing it to emulate them using a regular flush. Signed-off-by: Alberto Faria Message-ID: <20250502121115.3613717-3-afaria@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- hw/core/machine.c | 4 +++- hw/scsi/scsi-disk.c | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index b8ae155dfa..c3f3a5020d 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -37,7 +37,9 @@ #include "hw/virtio/virtio-iommu.h" #include "audio/audio.h" -GlobalProperty hw_compat_10_0[] = {}; +GlobalProperty hw_compat_10_0[] = { + { "scsi-hd", "dpofua", "off" }, +}; const size_t hw_compat_10_0_len = G_N_ELEMENTS(hw_compat_10_0); GlobalProperty hw_compat_9_2[] = { diff --git a/hw/scsi/scsi-disk.c b/hw/scsi/scsi-disk.c index 738d8df8ec..b4782c6248 100644 --- a/hw/scsi/scsi-disk.c +++ b/hw/scsi/scsi-disk.c @@ -3192,7 +3192,7 @@ static const Property scsi_hd_properties[] = { DEFINE_PROP_BIT("removable", SCSIDiskState, features, SCSI_DISK_F_REMOVABLE, false), DEFINE_PROP_BIT("dpofua", SCSIDiskState, features, - SCSI_DISK_F_DPOFUA, false), + SCSI_DISK_F_DPOFUA, true), DEFINE_PROP_UINT64("wwn", SCSIDiskState, qdev.wwn, 0), DEFINE_PROP_UINT64("port_wwn", SCSIDiskState, qdev.port_wwn, 0), DEFINE_PROP_UINT16("port_index", SCSIDiskState, port_index, 0), From 7ed96710e82c385c6cfc3d064eec7dde20f0f3fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 27 Jan 2025 18:45:47 +0000 Subject: [PATCH 1067/2760] ui/vnc.c: replace big endian flag with byte order value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will make it easier to do certain comparisons in future if we store G_BIG_ENDIAN/G_LITTLE_ENDIAN directly, instead of a boolean flag, as we can then compare directly to the G_BYTE_ORDER constant. Reviewed-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- ui/vnc-enc-tight.c | 2 +- ui/vnc-enc-zrle.c | 2 +- ui/vnc-jobs.c | 2 +- ui/vnc.c | 6 +++--- ui/vnc.h | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index 41f559eb83..f8aaa8f346 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -150,7 +150,7 @@ tight_detect_smooth_image24(VncState *vs, int w, int h) * If client is big-endian, color samples begin from the second * byte (offset 1) of a 32-bit pixel value. */ - off = vs->client_be; + off = vs->client_endian == G_BIG_ENDIAN ? 1 : 0; memset(stats, 0, sizeof (stats)); diff --git a/ui/vnc-enc-zrle.c b/ui/vnc-enc-zrle.c index bd33b89063..97ec6c7119 100644 --- a/ui/vnc-enc-zrle.c +++ b/ui/vnc-enc-zrle.c @@ -255,7 +255,7 @@ static void zrle_write_u8(VncState *vs, uint8_t value) static int zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) { - bool be = vs->client_be; + bool be = vs->client_endian == G_BIG_ENDIAN; size_t bytes; int zywrle_level; diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index fcca7ec632..d3486af9e2 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -188,7 +188,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->lossy_rect = orig->lossy_rect; local->write_pixels = orig->write_pixels; local->client_pf = orig->client_pf; - local->client_be = orig->client_be; + local->client_endian = orig->client_endian; local->tight = orig->tight; local->zlib = orig->zlib; local->hextile = orig->hextile; diff --git a/ui/vnc.c b/ui/vnc.c index 9e097dc4b4..ab18172c4d 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -891,7 +891,7 @@ void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) buf[0] = v; break; case 2: - if (vs->client_be) { + if (vs->client_endian == G_BIG_ENDIAN) { buf[0] = v >> 8; buf[1] = v; } else { @@ -901,7 +901,7 @@ void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v) break; default: case 4: - if (vs->client_be) { + if (vs->client_endian == G_BIG_ENDIAN) { buf[0] = v >> 24; buf[1] = v >> 16; buf[2] = v >> 8; @@ -2312,7 +2312,7 @@ static void set_pixel_format(VncState *vs, int bits_per_pixel, vs->client_pf.bits_per_pixel = bits_per_pixel; vs->client_pf.bytes_per_pixel = bits_per_pixel / 8; vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; - vs->client_be = big_endian_flag; + vs->client_endian = big_endian_flag ? G_BIG_ENDIAN : G_LITTLE_ENDIAN; if (!true_color_flag) { send_color_map(vs); diff --git a/ui/vnc.h b/ui/vnc.h index acc53a2cc1..02613aa63a 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -323,7 +323,7 @@ struct VncState VncWritePixels *write_pixels; PixelFormat client_pf; pixman_format_code_t client_format; - bool client_be; + int client_endian; /* G_LITTLE_ENDIAN or G_BIG_ENDIAN */ CaptureVoiceOut *audio_cap; struct audsettings as; From 70097442853c389a765c9f6502d861d182b092ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 27 Jan 2025 18:48:50 +0000 Subject: [PATCH 1068/2760] ui/vnc: take account of client byte order in pixman format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The set_pixel_conversion() method is responsible for determining whether the VNC client pixel format matches the server format, and thus whether we can use the fast path "copy" impl for sending pixels, or must use the generic impl with bit swizzling. The VNC server format is set at build time to VNC_SERVER_FB_FORMAT, which corresponds to PIXMAN_x8r8g8b8. The qemu_pixman_get_format() method is then responsible for converting the VNC pixel format into a pixman format. The VNC client pixel shifts are relative to the associated endianness. The pixman formats are always relative to the host native endianness. The qemu_pixman_get_format() method does not take into account the VNC client endianness, and is thus returning a pixman format that is only valid with the host endianness matches that of the VNC client. This has been broken since pixman was introduced to the VNC server: commit 9f64916da20eea67121d544698676295bbb105a7 Author: Gerd Hoffmann Date: Wed Oct 10 13:29:43 2012 +0200 pixman/vnc: use pixman images in vnc. The flaw can be demonstrated using the Tigervnc client by using vncviewer -AutoSelect=0 -PreferredEncoding=raw server:display connecting from a LE client to a QEMU on a BE server, or the reverse. The bug was masked, however, because almost all VNC clients will advertize support for the "tight" encoding and the QEMU VNC server will prefer "tight" if advertized. The tight_pack24 method is responsible for taking a set of pixels which have already been converted into client endianness and then repacking them into the TPIXEL format which the RFB spec defines as "TPIXEL is only 3 bytes long, where the first byte is the red component, the second byte is the green component, and the third byte is the blue component of the pixel color value" IOW, the TPIXEL format is fixed on the wire, regardless of what the VNC client declare as its endianness. Since the VNC pixel encoding code was failing to honour the endian flag of the client, the tight_pack24 method was always operating on data in native endianness. Its impl cancelled out the VNC pixel encoding bug. With the VNC pixel encoding code now fixed, the tight_pack24 method needs to take into account that it is operating on data in client endianness, not native endianness. It thus may need to invert the pixel shifts. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- include/ui/qemu-pixman.h | 4 ++-- ui/qemu-pixman.c | 15 ++++++++------- ui/vnc-enc-tight.c | 2 +- ui/vnc.c | 3 ++- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/include/ui/qemu-pixman.h b/include/ui/qemu-pixman.h index 193bc046d1..2ca0ed7029 100644 --- a/include/ui/qemu-pixman.h +++ b/include/ui/qemu-pixman.h @@ -75,12 +75,12 @@ PixelFormat qemu_pixelformat_from_pixman(pixman_format_code_t format); pixman_format_code_t qemu_default_pixman_format(int bpp, bool native_endian); pixman_format_code_t qemu_drm_format_to_pixman(uint32_t drm_format); uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman); -int qemu_pixman_get_type(int rshift, int gshift, int bshift); +int qemu_pixman_get_type(int rshift, int gshift, int bshift, int endian); bool qemu_pixman_check_format(DisplayChangeListener *dcl, pixman_format_code_t format); #ifdef CONFIG_PIXMAN -pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf); +pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf, int endian); pixman_image_t *qemu_pixman_linebuf_create(pixman_format_code_t format, int width); void qemu_pixman_linebuf_fill(pixman_image_t *linebuf, pixman_image_t *fb, diff --git a/ui/qemu-pixman.c b/ui/qemu-pixman.c index 6ef4376f4e..ef4e71da11 100644 --- a/ui/qemu-pixman.c +++ b/ui/qemu-pixman.c @@ -126,33 +126,34 @@ uint32_t qemu_pixman_to_drm_format(pixman_format_code_t pixman_format) return 0; } -int qemu_pixman_get_type(int rshift, int gshift, int bshift) +int qemu_pixman_get_type(int rshift, int gshift, int bshift, int endian) { int type = PIXMAN_TYPE_OTHER; + bool native_endian = (endian == G_BYTE_ORDER); if (rshift > gshift && gshift > bshift) { if (bshift == 0) { - type = PIXMAN_TYPE_ARGB; + type = native_endian ? PIXMAN_TYPE_ARGB : PIXMAN_TYPE_BGRA; } else { - type = PIXMAN_TYPE_RGBA; + type = native_endian ? PIXMAN_TYPE_RGBA : PIXMAN_TYPE_ABGR; } } else if (rshift < gshift && gshift < bshift) { if (rshift == 0) { - type = PIXMAN_TYPE_ABGR; + type = native_endian ? PIXMAN_TYPE_ABGR : PIXMAN_TYPE_RGBA; } else { - type = PIXMAN_TYPE_BGRA; + type = native_endian ? PIXMAN_TYPE_BGRA : PIXMAN_TYPE_ARGB; } } return type; } #ifdef CONFIG_PIXMAN -pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf) +pixman_format_code_t qemu_pixman_get_format(PixelFormat *pf, int endian) { pixman_format_code_t format; int type; - type = qemu_pixman_get_type(pf->rshift, pf->gshift, pf->bshift); + type = qemu_pixman_get_type(pf->rshift, pf->gshift, pf->bshift, endian); format = PIXMAN_FORMAT(pf->bits_per_pixel, type, pf->abits, pf->rbits, pf->gbits, pf->bbits); if (!pixman_format_supported_source(format)) { diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index f8aaa8f346..a5bdc19ebb 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -891,7 +891,7 @@ static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret) buf8 = buf; - if (1 /* FIXME */) { + if (vs->client_endian == G_BYTE_ORDER) { rshift = vs->client_pf.rshift; gshift = vs->client_pf.gshift; bshift = vs->client_pf.bshift; diff --git a/ui/vnc.c b/ui/vnc.c index ab18172c4d..d095cd7da3 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2240,7 +2240,8 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) static void set_pixel_conversion(VncState *vs) { - pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf); + pixman_format_code_t fmt = qemu_pixman_get_format(&vs->client_pf, + vs->client_endian); if (fmt == VNC_SERVER_FB_FORMAT) { vs->write_pixels = vnc_write_pixels_copy; From 63d320909220a90647c484263ae5e2f26eb54587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 28 Jan 2025 13:27:25 +0000 Subject: [PATCH 1069/2760] ui/vnc: fix tight palette pixel encoding for 8/16-bpp formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When sending a tight rectangle with the palette filter, if the client format was 8/16bpp, the colours on big endian hosts are not set as we're sending the wrong bytes. We must first cast the 32-bit colour to a 16/8-bit value, and then send the result. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Daniel P. Berrangé --- ui/vnc-enc-tight.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index a5bdc19ebb..25c7b2c788 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -1001,16 +1001,24 @@ static int send_mono_rect(VncState *vs, int x, int y, break; } case 2: - vnc_write(vs, &bg, 2); - vnc_write(vs, &fg, 2); + { + uint16_t bg16 = bg; + uint16_t fg16 = fg; + vnc_write(vs, &bg16, 2); + vnc_write(vs, &fg16, 2); tight_encode_mono_rect16(vs->tight->tight.buffer, w, h, bg, fg); break; + } default: - vnc_write_u8(vs, bg); - vnc_write_u8(vs, fg); + { + uint8_t bg8 = bg; + uint8_t fg8 = fg; + vnc_write_u8(vs, bg8); + vnc_write_u8(vs, fg8); tight_encode_mono_rect8(vs->tight->tight.buffer, w, h, bg, fg); break; } + } vs->tight->tight.offset = bytes; bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY); From 212c217f7d540fdbf1df4b65653ad5592073bb8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 7 May 2025 15:37:28 +0100 Subject: [PATCH 1070/2760] tests: skip encrypted secret tests if AES is not available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids test breakage when we drop support for using the built-in AES impl as a fallback for missing crypto libraries. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé --- tests/unit/test-crypto-secret.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/unit/test-crypto-secret.c b/tests/unit/test-crypto-secret.c index ffd13ff70e..fc32a01747 100644 --- a/tests/unit/test-crypto-secret.c +++ b/tests/unit/test-crypto-secret.c @@ -22,6 +22,7 @@ #include "crypto/init.h" #include "crypto/secret.h" +#include "crypto/cipher.h" #include "qapi/error.h" #include "qemu/module.h" #if defined(CONFIG_KEYUTILS) && defined(CONFIG_SECRET_KEYRING) @@ -597,18 +598,21 @@ int main(int argc, char **argv) g_test_add_func("/crypto/secret/conv/utf8/base64", test_secret_conv_utf8_base64); - g_test_add_func("/crypto/secret/crypt/raw", - test_secret_crypt_raw); - g_test_add_func("/crypto/secret/crypt/base64", - test_secret_crypt_base64); - g_test_add_func("/crypto/secret/crypt/shortkey", - test_secret_crypt_short_key); - g_test_add_func("/crypto/secret/crypt/shortiv", - test_secret_crypt_short_iv); - g_test_add_func("/crypto/secret/crypt/missingiv", - test_secret_crypt_missing_iv); - g_test_add_func("/crypto/secret/crypt/badiv", - test_secret_crypt_bad_iv); + if (qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_128, + QCRYPTO_CIPHER_MODE_CBC)) { + g_test_add_func("/crypto/secret/crypt/raw", + test_secret_crypt_raw); + g_test_add_func("/crypto/secret/crypt/base64", + test_secret_crypt_base64); + g_test_add_func("/crypto/secret/crypt/shortkey", + test_secret_crypt_short_key); + g_test_add_func("/crypto/secret/crypt/shortiv", + test_secret_crypt_short_iv); + g_test_add_func("/crypto/secret/crypt/missingiv", + test_secret_crypt_missing_iv); + g_test_add_func("/crypto/secret/crypt/badiv", + test_secret_crypt_bad_iv); + } return g_test_run(); } From 75134a32851e2d9f240097cc7a3bc3426667e147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 7 May 2025 15:47:18 +0100 Subject: [PATCH 1071/2760] tests: skip legacy qcow2 encryption test if AES is not available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids test breakage when we drop support for using the built-in AES impl as a fallback for missing crypto libraries. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé --- tests/unit/test-crypto-block.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/unit/test-crypto-block.c b/tests/unit/test-crypto-block.c index 9217b9a2ef..3ac7f17b2a 100644 --- a/tests/unit/test-crypto-block.c +++ b/tests/unit/test-crypto-block.c @@ -574,6 +574,13 @@ int main(int argc, char **argv) for (i = 0; i < G_N_ELEMENTS(test_data); i++) { if (test_data[i].open_opts->format == QCRYPTO_BLOCK_FORMAT_LUKS && !qcrypto_hash_supports(test_data[i].hash_alg)) { + g_printerr("# skip unsupported %s\n", + QCryptoHashAlgo_str(test_data[i].hash_alg)); + continue; + } + if (!qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_128, + QCRYPTO_CIPHER_MODE_CBC)) { + g_printerr("# skip unsupported aes-128:cbc\n"); continue; } if (!test_data[i].slow || From 01ce649e5d260d4d1a466d10dfaeabeeb2b7478d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 7 May 2025 15:48:31 +0100 Subject: [PATCH 1072/2760] tests: fix skipping cipher tests when AES is not available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoid tests breakage when we drop support for using the built-in AES impl as a fallback for missing crypto libraries. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé --- tests/unit/test-crypto-cipher.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/unit/test-crypto-cipher.c b/tests/unit/test-crypto-cipher.c index b328b482e1..1331d558cf 100644 --- a/tests/unit/test-crypto-cipher.c +++ b/tests/unit/test-crypto-cipher.c @@ -828,11 +828,16 @@ int main(int argc, char **argv) } } - g_test_add_func("/crypto/cipher/null-iv", - test_cipher_null_iv); + if (qcrypto_cipher_supports(QCRYPTO_CIPHER_ALGO_AES_256, + QCRYPTO_CIPHER_MODE_CBC)) { + g_test_add_func("/crypto/cipher/null-iv", + test_cipher_null_iv); - g_test_add_func("/crypto/cipher/short-plaintext", - test_cipher_short_plaintext); + g_test_add_func("/crypto/cipher/short-plaintext", + test_cipher_short_plaintext); + } else { + g_printerr("# skip unsupported aes-256:cbc\n"); + } return g_test_run(); } From 5a56f60d7c2e241bf972b8eca31286cf7f3cd7c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 6 May 2025 15:07:08 +0100 Subject: [PATCH 1073/2760] crypto: fully drop built-in cipher provider MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When originally creating the internal crypto cipher APIs, they were wired up to use the built-in D3DES and AES implementations, as a way to gracefully transition to the new APIs without introducing an immediate hard dep on any external crypto libraries for the VNC password auth (D3DES) or the qcow2 encryption (AES). In the 6.1.0 release we dropped the built-in D3DES impl, and also the XTS mode for the AES impl, leaving only AES with ECB/CBC modes. The rational was that with the system emulators, it is expected that 3rd party crypto libraries will be available. The qcow2 LUKS impl is preferred to the legacy raw AES impl, and by default that requires AES in XTS mode, limiting the usefulness of the built-in cipher provider. The built-in AES impl has known timing attacks and is only suitable for use cases where a security boundary is already not expected to be provided (TCG). Providing a built-in cipher impl thus potentially misleads users, should they configure a QEMU without any crypto library, and try to use it with the LUKS backend, even if that requires a non-default configuration choice. Complete what we started in 6.1.0 and purge the remaining AES support. Use of either gnutls, nettle, or libcrypt is now mandatory for any cipher support, except for TCG impls. Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Signed-off-by: Daniel P. Berrangé --- crypto/cipher-builtin.c.inc | 303 ------------------------------------ crypto/cipher-stub.c.inc | 30 ++++ crypto/cipher.c | 2 +- 3 files changed, 31 insertions(+), 304 deletions(-) delete mode 100644 crypto/cipher-builtin.c.inc create mode 100644 crypto/cipher-stub.c.inc diff --git a/crypto/cipher-builtin.c.inc b/crypto/cipher-builtin.c.inc deleted file mode 100644 index da5fcbd9a3..0000000000 --- a/crypto/cipher-builtin.c.inc +++ /dev/null @@ -1,303 +0,0 @@ -/* - * QEMU Crypto cipher built-in algorithms - * - * Copyright (c) 2015 Red Hat, Inc. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, see . - * - */ - -#include "crypto/aes.h" - -typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext; -struct QCryptoCipherBuiltinAESContext { - AES_KEY enc; - AES_KEY dec; -}; - -typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES; -struct QCryptoCipherBuiltinAES { - QCryptoCipher base; - QCryptoCipherBuiltinAESContext key; - uint8_t iv[AES_BLOCK_SIZE]; -}; - - -static inline bool qcrypto_length_check(size_t len, size_t blocksize, - Error **errp) -{ - if (unlikely(len & (blocksize - 1))) { - error_setg(errp, "Length %zu must be a multiple of block size %zu", - len, blocksize); - return false; - } - return true; -} - -static void qcrypto_cipher_ctx_free(QCryptoCipher *cipher) -{ - g_free(cipher); -} - -static int qcrypto_cipher_no_setiv(QCryptoCipher *cipher, - const uint8_t *iv, size_t niv, - Error **errp) -{ - error_setg(errp, "Setting IV is not supported"); - return -1; -} - -static void do_aes_encrypt_ecb(const void *vctx, - size_t len, - uint8_t *out, - const uint8_t *in) -{ - const QCryptoCipherBuiltinAESContext *ctx = vctx; - - /* We have already verified that len % AES_BLOCK_SIZE == 0. */ - while (len) { - AES_encrypt(in, out, &ctx->enc); - in += AES_BLOCK_SIZE; - out += AES_BLOCK_SIZE; - len -= AES_BLOCK_SIZE; - } -} - -static void do_aes_decrypt_ecb(const void *vctx, - size_t len, - uint8_t *out, - const uint8_t *in) -{ - const QCryptoCipherBuiltinAESContext *ctx = vctx; - - /* We have already verified that len % AES_BLOCK_SIZE == 0. */ - while (len) { - AES_decrypt(in, out, &ctx->dec); - in += AES_BLOCK_SIZE; - out += AES_BLOCK_SIZE; - len -= AES_BLOCK_SIZE; - } -} - -static void do_aes_encrypt_cbc(const AES_KEY *key, - size_t len, - uint8_t *out, - const uint8_t *in, - uint8_t *ivec) -{ - uint8_t tmp[AES_BLOCK_SIZE]; - size_t n; - - /* We have already verified that len % AES_BLOCK_SIZE == 0. */ - while (len) { - for (n = 0; n < AES_BLOCK_SIZE; ++n) { - tmp[n] = in[n] ^ ivec[n]; - } - AES_encrypt(tmp, out, key); - memcpy(ivec, out, AES_BLOCK_SIZE); - len -= AES_BLOCK_SIZE; - in += AES_BLOCK_SIZE; - out += AES_BLOCK_SIZE; - } -} - -static void do_aes_decrypt_cbc(const AES_KEY *key, - size_t len, - uint8_t *out, - const uint8_t *in, - uint8_t *ivec) -{ - uint8_t tmp[AES_BLOCK_SIZE]; - size_t n; - - /* We have already verified that len % AES_BLOCK_SIZE == 0. */ - while (len) { - memcpy(tmp, in, AES_BLOCK_SIZE); - AES_decrypt(in, out, key); - for (n = 0; n < AES_BLOCK_SIZE; ++n) { - out[n] ^= ivec[n]; - } - memcpy(ivec, tmp, AES_BLOCK_SIZE); - len -= AES_BLOCK_SIZE; - in += AES_BLOCK_SIZE; - out += AES_BLOCK_SIZE; - } -} - -static int qcrypto_cipher_aes_encrypt_ecb(QCryptoCipher *cipher, - const void *in, void *out, - size_t len, Error **errp) -{ - QCryptoCipherBuiltinAES *ctx - = container_of(cipher, QCryptoCipherBuiltinAES, base); - - if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { - return -1; - } - do_aes_encrypt_ecb(&ctx->key, len, out, in); - return 0; -} - -static int qcrypto_cipher_aes_decrypt_ecb(QCryptoCipher *cipher, - const void *in, void *out, - size_t len, Error **errp) -{ - QCryptoCipherBuiltinAES *ctx - = container_of(cipher, QCryptoCipherBuiltinAES, base); - - if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { - return -1; - } - do_aes_decrypt_ecb(&ctx->key, len, out, in); - return 0; -} - -static int qcrypto_cipher_aes_encrypt_cbc(QCryptoCipher *cipher, - const void *in, void *out, - size_t len, Error **errp) -{ - QCryptoCipherBuiltinAES *ctx - = container_of(cipher, QCryptoCipherBuiltinAES, base); - - if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { - return -1; - } - do_aes_encrypt_cbc(&ctx->key.enc, len, out, in, ctx->iv); - return 0; -} - -static int qcrypto_cipher_aes_decrypt_cbc(QCryptoCipher *cipher, - const void *in, void *out, - size_t len, Error **errp) -{ - QCryptoCipherBuiltinAES *ctx - = container_of(cipher, QCryptoCipherBuiltinAES, base); - - if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) { - return -1; - } - do_aes_decrypt_cbc(&ctx->key.dec, len, out, in, ctx->iv); - return 0; -} - -static int qcrypto_cipher_aes_setiv(QCryptoCipher *cipher, const uint8_t *iv, - size_t niv, Error **errp) -{ - QCryptoCipherBuiltinAES *ctx - = container_of(cipher, QCryptoCipherBuiltinAES, base); - - if (niv != AES_BLOCK_SIZE) { - error_setg(errp, "IV must be %d bytes not %zu", - AES_BLOCK_SIZE, niv); - return -1; - } - - memcpy(ctx->iv, iv, AES_BLOCK_SIZE); - return 0; -} - -static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_ecb = { - .cipher_encrypt = qcrypto_cipher_aes_encrypt_ecb, - .cipher_decrypt = qcrypto_cipher_aes_decrypt_ecb, - .cipher_setiv = qcrypto_cipher_no_setiv, - .cipher_free = qcrypto_cipher_ctx_free, -}; - -static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_cbc = { - .cipher_encrypt = qcrypto_cipher_aes_encrypt_cbc, - .cipher_decrypt = qcrypto_cipher_aes_decrypt_cbc, - .cipher_setiv = qcrypto_cipher_aes_setiv, - .cipher_free = qcrypto_cipher_ctx_free, -}; - -bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, - QCryptoCipherMode mode) -{ - switch (alg) { - case QCRYPTO_CIPHER_ALGO_AES_128: - case QCRYPTO_CIPHER_ALGO_AES_192: - case QCRYPTO_CIPHER_ALGO_AES_256: - switch (mode) { - case QCRYPTO_CIPHER_MODE_ECB: - case QCRYPTO_CIPHER_MODE_CBC: - return true; - default: - return false; - } - break; - default: - return false; - } -} - -static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, - QCryptoCipherMode mode, - const uint8_t *key, - size_t nkey, - Error **errp) -{ - if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { - return NULL; - } - - switch (alg) { - case QCRYPTO_CIPHER_ALGO_AES_128: - case QCRYPTO_CIPHER_ALGO_AES_192: - case QCRYPTO_CIPHER_ALGO_AES_256: - { - QCryptoCipherBuiltinAES *ctx; - const QCryptoCipherDriver *drv; - - switch (mode) { - case QCRYPTO_CIPHER_MODE_ECB: - drv = &qcrypto_cipher_aes_driver_ecb; - break; - case QCRYPTO_CIPHER_MODE_CBC: - drv = &qcrypto_cipher_aes_driver_cbc; - break; - default: - goto bad_mode; - } - - ctx = g_new0(QCryptoCipherBuiltinAES, 1); - ctx->base.driver = drv; - - if (AES_set_encrypt_key(key, nkey * 8, &ctx->key.enc)) { - error_setg(errp, "Failed to set encryption key"); - goto error; - } - if (AES_set_decrypt_key(key, nkey * 8, &ctx->key.dec)) { - error_setg(errp, "Failed to set decryption key"); - goto error; - } - - return &ctx->base; - - error: - g_free(ctx); - return NULL; - } - - default: - error_setg(errp, - "Unsupported cipher algorithm %s", - QCryptoCipherAlgo_str(alg)); - return NULL; - } - - bad_mode: - error_setg(errp, "Unsupported cipher mode %s", - QCryptoCipherMode_str(mode)); - return NULL; -} diff --git a/crypto/cipher-stub.c.inc b/crypto/cipher-stub.c.inc new file mode 100644 index 0000000000..1b7ea81eac --- /dev/null +++ b/crypto/cipher-stub.c.inc @@ -0,0 +1,30 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * QEMU Crypto cipher impl stub + * + * Copyright (c) 2025 Red Hat, Inc. + * + */ + +bool qcrypto_cipher_supports(QCryptoCipherAlgo alg, + QCryptoCipherMode mode) +{ + return false; +} + +static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgo alg, + QCryptoCipherMode mode, + const uint8_t *key, + size_t nkey, + Error **errp) +{ + if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) { + return NULL; + } + + error_setg(errp, + "Unsupported cipher algorithm %s, no crypto library enabled in build", + QCryptoCipherAlgo_str(alg)); + return NULL; +} diff --git a/crypto/cipher.c b/crypto/cipher.c index c14a8b8a11..229710f76b 100644 --- a/crypto/cipher.c +++ b/crypto/cipher.c @@ -145,7 +145,7 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgo alg, #elif defined CONFIG_GNUTLS_CRYPTO #include "cipher-gnutls.c.inc" #else -#include "cipher-builtin.c.inc" +#include "cipher-stub.c.inc" #endif QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgo alg, From 66411ab48b3676894903148406db12a446445adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 8 May 2025 17:40:33 +0100 Subject: [PATCH 1074/2760] Revert "scripts: mandate that new files have SPDX-License-Identifier" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit fa4d79c64dae03ffa269e42e21822453856618b7. The logic in this commit was flawed in two critical ways * It always failed to report SPDX validation on the last newly added file. IOW, it only worked if at least 2 new files were added in a commit * If an existing file change, followed a new file change, in the commit and the existing file context/changed lines included SPDX-License-Identifier, it would incorrectly associate this with the previous newly added file. Simply reverting this commit will make it significantly easier to understand the improved logic in the following commit. Reported-by: Peter Maydell Reviewed-by: Cédric Le Goater Reviewed-by: Peter Maydell Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 365892de04..d355c0dad5 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1442,8 +1442,6 @@ sub process { my $in_imported_file = 0; my $in_no_imported_file = 0; my $non_utf8_charset = 0; - my $expect_spdx = 0; - my $expect_spdx_file; our @report = (); our $cnt_lines = 0; @@ -1681,34 +1679,6 @@ sub process { WARN("added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); } -# All new files should have a SPDX-License-Identifier tag - if ($line =~ /^new file mode\s*\d+\s*$/) { - if ($expect_spdx) { - if ($expect_spdx_file =~ - /\.(c|h|py|pl|sh|json|inc|Makefile)$/) { - # source code files MUST have SPDX license declared - ERROR("New file '$expect_spdx_file' requires " . - "'SPDX-License-Identifier'"); - } else { - # Other files MAY have SPDX license if appropriate - WARN("Does new file '$expect_spdx_file' need " . - "'SPDX-License-Identifier'?"); - } - } - $expect_spdx = 1; - $expect_spdx_file = undef; - } elsif ($expect_spdx) { - $expect_spdx_file = $realfile unless - defined $expect_spdx_file; - - # SPDX tags may occurr in comments which were - # stripped from '$line', so use '$rawline' - if ($rawline =~ /SPDX-License-Identifier/) { - $expect_spdx = 0; - $expect_spdx_file = undef; - } - } - # Check SPDX-License-Identifier references a permitted license if ($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) { &checkspdx($realfile, $1); From 619bf37594e362131f12b42888e6a35ddee488e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 12 May 2025 18:11:58 +0100 Subject: [PATCH 1075/2760] scripts/checkpatch.pl: fix various indentation mistakes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Various checks in the code were under-indented relative to other surrounding code. Some places used 4-space indents instead of single tab, while other places simply used too few tabs. Reviewed-by: Cédric Le Goater Reviewed-by: Peter Maydell Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 101 ++++++++++++++++++++++-------------------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index d355c0dad5..2c19f6f0f2 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1681,19 +1681,20 @@ sub process { # Check SPDX-License-Identifier references a permitted license if ($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) { - &checkspdx($realfile, $1); + &checkspdx($realfile, $1); } if ($rawline =~ m,(SPDX-[a-zA-Z0-9-_]+):,) { - my $tag = $1; - my @permitted = qw( - SPDX-License-Identifier - ); - - unless (grep { /^$tag$/ } @permitted) { - ERROR("Tag $tag not permitted in QEMU code, valid " . - "choices are: " . join(", ", @permitted)); - } + my $tag = $1; + my @permitted = qw( + SPDX-License-Identifier + ); + + unless (grep { /^$tag$/ } @permitted) { + ERROR("Tag $tag not permitted in QEMU code, " . + "valid choices are: " . + join(", ", @permitted)); + } } # Check for wrappage within a valid hunk of the file @@ -2274,7 +2275,7 @@ sub process { # missing space after union, struct or enum definition if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?(?:\s+$Ident)?[=\{]/) { - ERROR("missing space after $1 definition\n" . $herecurr); + ERROR("missing space after $1 definition\n" . $herecurr); } # check for spacing round square brackets; allowed: @@ -2569,7 +2570,7 @@ sub process { if ($line =~ /^.\s*(Q(?:S?LIST|SIMPLEQ|TAILQ)_HEAD)\s*\(\s*[^,]/ && $line !~ /^.typedef/) { - ERROR("named $1 should be typedefed separately\n" . $herecurr); + ERROR("named $1 should be typedefed separately\n" . $herecurr); } # Need a space before open parenthesis after if, while etc @@ -3118,48 +3119,50 @@ sub process { # Qemu error function tests - # Find newlines in error messages - my $qemu_error_funcs = qr{error_setg| - error_setg_errno| - error_setg_win32| - error_setg_file_open| - error_set| - error_prepend| - warn_reportf_err| - error_reportf_err| - error_vreport| - warn_vreport| - info_vreport| - error_report| - warn_report| - info_report| - g_test_message}x; - - if ($rawline =~ /\b(?:$qemu_error_funcs)\s*\(.*\".*\\n/) { - ERROR("Error messages should not contain newlines\n" . $herecurr); - } + # Find newlines in error messages + my $qemu_error_funcs = qr{error_setg| + error_setg_errno| + error_setg_win32| + error_setg_file_open| + error_set| + error_prepend| + warn_reportf_err| + error_reportf_err| + error_vreport| + warn_vreport| + info_vreport| + error_report| + warn_report| + info_report| + g_test_message}x; + + if ($rawline =~ /\b(?:$qemu_error_funcs)\s*\(.*\".*\\n/) { + ERROR("Error messages should not contain newlines\n" . $herecurr); + } - # Continue checking for error messages that contains newlines. This - # check handles cases where string literals are spread over multiple lines. - # Example: - # error_report("Error msg line #1" - # "Error msg line #2\n"); - my $quoted_newline_regex = qr{\+\s*\".*\\n.*\"}; - my $continued_str_literal = qr{\+\s*\".*\"}; + # Continue checking for error messages that contains newlines. + # This check handles cases where string literals are spread + # over multiple lines. + # Example: + # error_report("Error msg line #1" + # "Error msg line #2\n"); + my $quoted_newline_regex = qr{\+\s*\".*\\n.*\"}; + my $continued_str_literal = qr{\+\s*\".*\"}; - if ($rawline =~ /$quoted_newline_regex/) { - # Backtrack to first line that does not contain only a quoted literal - # and assume that it is the start of the statement. - my $i = $linenr - 2; + if ($rawline =~ /$quoted_newline_regex/) { + # Backtrack to first line that does not contain only + # a quoted literal and assume that it is the start + # of the statement. + my $i = $linenr - 2; - while (($i >= 0) & $rawlines[$i] =~ /$continued_str_literal/) { - $i--; - } + while (($i >= 0) & $rawlines[$i] =~ /$continued_str_literal/) { + $i--; + } - if ($rawlines[$i] =~ /\b(?:$qemu_error_funcs)\s*\(/) { - ERROR("Error messages should not contain newlines\n" . $herecurr); + if ($rawlines[$i] =~ /\b(?:$qemu_error_funcs)\s*\(/) { + ERROR("Error messages should not contain newlines\n" . $herecurr); + } } - } # check for non-portable libc calls that have portable alternatives in QEMU if ($line =~ /\bffs\(/) { From 25374ba59bce2d8c714283c7bb18b1dc2cc89ebe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 12 May 2025 16:57:38 +0100 Subject: [PATCH 1076/2760] scripts/checkpatch: introduce tracking of file start/end MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some checks want to be performed either at the start of a new file within a patch, or at the end. This is complicated by the fact that the information relevant to the check may be spread across multiple lines. It is further complicated by a need to support both git and non-git diffs, and special handling for renames where there might not be any patch hunks. To handle this more sanely, introduce explicit tracking of file start/end, taking account of git metadata, and calling a hook function at each transition. Reviewed-by: Cédric Le Goater Reviewed-by: Peter Maydell Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 110 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 107 insertions(+), 3 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 2c19f6f0f2..bb3830d693 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1417,6 +1417,39 @@ sub checkspdx { } } +# All three of the methods below take a 'file info' record +# which is a hash ref containing +# +# 'isgit': 1 if an enhanced git diff or 0 for a plain diff +# 'githeader': 1 if still parsing git patch header, 0 otherwise +# 'linestart': line number of start of file diff +# 'lineend': line number of end of file diff +# 'filenew': the new filename +# 'fileold': the old filename (same as 'new filename' except +# for renames in git diffs) +# 'action': one of 'modified' (always) or 'new' or 'deleted' or +# 'renamed' (git diffs only) +# 'mode': file mode for new/deleted files (git diffs only) +# 'similarity': file similarity when renamed (git diffs only) +# 'facts': hash ref for storing any metadata related to checks +# + +# Called at the end of each patch, with the list of +# real filenames that were seen in the patch +sub process_file_list { + my @fileinfos = @_; +} + +# Called at the start of processing a diff hunk for a file +sub process_start_of_file { + my $fileinfo = shift; +} + +# Called at the end of processing a diff hunk for a file +sub process_end_of_file { + my $fileinfo = shift; +} + sub process { my $filename = shift; @@ -1453,7 +1486,10 @@ sub process { my $realfile = ''; my $realline = 0; my $realcnt = 0; + my $fileinfo; + my @fileinfolist; my $here = ''; + my $oldhere = ''; my $in_comment = 0; my $comment_edge = 0; my $first_line = 0; @@ -1591,17 +1627,56 @@ sub process { $prefix = "$filename:$realline: " if ($emacs && $file); $prefix = "$filename:$linenr: " if ($emacs && !$file); + $oldhere = $here; $here = "#$linenr: " if (!$file); $here = "#$realline: " if ($file); # extract the filename as it passes - if ($line =~ /^diff --git.*?(\S+)$/) { - $realfile = $1; - $realfile =~ s@^([^/]*)/@@ if (!$file); + if ($line =~ /^diff --git\s+(\S+)\s+(\S+)$/) { + my $fileold = $1; + my $filenew = $2; + + if (defined $fileinfo) { + $fileinfo->{lineend} = $oldhere; + process_end_of_file($fileinfo) + } + $fileold =~ s@^([^/]*)/@@ if (!$file); + $filenew =~ s@^([^/]*)/@@ if (!$file); + $realfile = $filenew; checkfilename($realfile, \$acpi_testexpected, \$acpi_nontestexpected); + + $fileinfo = { + "isgit" => 1, + "githeader" => 1, + "linestart" => $here, + "lineend" => 0, + "fileold" => $fileold, + "filenew" => $filenew, + "action" => "modified", + "mode" => 0, + "similarity" => 0, + "facts" => {}, + }; + push @fileinfolist, $fileinfo; + } elsif (defined $fileinfo && $fileinfo->{githeader} && + $line =~ /^(new|deleted) (?:file )?mode\s+([0-7]+)$/) { + $fileinfo->{action} = $1; + $fileinfo->{mode} = oct($2); + } elsif (defined $fileinfo && $fileinfo->{githeader} && + $line =~ /^similarity index (\d+)%/) { + $fileinfo->{similarity} = int($1); + } elsif (defined $fileinfo && $fileinfo->{githeader} && + $line =~ /^rename (from|to) [\w\/\.\-]+\s*$/) { + $fileinfo->{action} = "renamed"; + # For a no-change rename, we'll never have any "+++..." + # lines, so trigger actions now + if ($1 eq "to" && $fileinfo->{similarity} == 100) { + process_start_of_file($fileinfo); + } } elsif ($line =~ /^\+\+\+\s+(\S+)/) { $realfile = $1; $realfile =~ s@^([^/]*)/@@ if (!$file); + checkfilename($realfile, \$acpi_testexpected, \$acpi_nontestexpected); $p1_prefix = $1; @@ -1610,6 +1685,30 @@ sub process { WARN("patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n"); } + if (defined $fileinfo && !$fileinfo->{isgit}) { + $fileinfo->{lineend} = $oldhere; + process_end_of_file($fileinfo); + } + + if (!defined $fileinfo || !$fileinfo->{isgit}) { + $fileinfo = { + "isgit" => 0, + "githeader" => 0, + "linestart" => $here, + "lineend" => 0, + "fileold" => $realfile, + "filenew" => $realfile, + "action" => "modified", + "mode" => 0, + "similarity" => 0, + "facts" => {}, + }; + push @fileinfolist, $fileinfo; + } else { + $fileinfo->{githeader} = 0; + } + process_start_of_file($fileinfo); + next; } @@ -3216,6 +3315,11 @@ sub process { } } + if (defined $fileinfo) { + process_end_of_file($fileinfo); + } + process_file_list(@fileinfolist); + if ($is_patch && $chk_signoff && $signoff == 0) { ERROR("Missing Signed-off-by: line(s)\n"); } From 9bec5f93e794271b2dc643cdc1c43082af958fac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 12 May 2025 17:48:20 +0100 Subject: [PATCH 1077/2760] scripts/checkpatch: use new hook for ACPI test data check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ACPI test data check needs to analyse a list of all files in a commit, so can use the new hook for processing the file list. Reviewed-by: Peter Maydell Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 61 ++++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 32 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index bb3830d693..817df98231 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1330,29 +1330,6 @@ sub WARN { } } -# According to tests/qtest/bios-tables-test.c: do not -# change expected file in the same commit with adding test -sub checkfilename { - my ($name, $acpi_testexpected, $acpi_nontestexpected) = @_; - - # Note: shell script that rebuilds the expected files is in the same - # directory as files themselves. - # Note: allowed diff list can be changed both when changing expected - # files and when changing tests. - if ($name =~ m#^tests/data/acpi/# and not $name =~ m#^\.sh$#) { - $$acpi_testexpected = $name; - } elsif ($name !~ m#^tests/qtest/bios-tables-test-allowed-diff.h$#) { - $$acpi_nontestexpected = $name; - } - if (defined $$acpi_testexpected and defined $$acpi_nontestexpected) { - ERROR("Do not add expected files together with tests, " . - "follow instructions in " . - "tests/qtest/bios-tables-test.c: both " . - $$acpi_testexpected . " and " . - $$acpi_nontestexpected . " found\n"); - } -} - sub checkspdx { my ($file, $expr) = @_; @@ -1438,6 +1415,34 @@ sub checkspdx { # real filenames that were seen in the patch sub process_file_list { my @fileinfos = @_; + + # According to tests/qtest/bios-tables-test.c: do not + # change expected file in the same commit with adding test + my @acpi_testexpected; + my @acpi_nontestexpected; + + foreach my $fileinfo (@fileinfos) { + # Note: shell script that rebuilds the expected files is in + # the same directory as files themselves. + # Note: allowed diff list can be changed both when changing + # expected files and when changing tests. + if ($fileinfo->{filenew} =~ m#^tests/data/acpi/# && + $fileinfo->{filenew} !~ m#^\.sh$#) { + push @acpi_testexpected, $fileinfo->{filenew}; + } elsif ($fileinfo->{filenew} !~ + m#^tests/qtest/bios-tables-test-allowed-diff.h$#) { + push @acpi_nontestexpected, $fileinfo->{filenew}; + } + } + if (int(@acpi_testexpected) > 0 and int(@acpi_nontestexpected) > 0) { + ERROR("Do not add expected files together with tests, " . + "follow instructions in " . + "tests/qtest/bios-tables-test.c. Files\n\n " . + join("\n ", @acpi_testexpected) . + "\n\nand\n\n " . + join("\n ", @acpi_nontestexpected) . + "\n\nfound in the same patch\n"); + } } # Called at the start of processing a diff hunk for a file @@ -1502,9 +1507,6 @@ sub process { my %suppress_whiletrailers; my %suppress_export; - my $acpi_testexpected; - my $acpi_nontestexpected; - # Pre-scan the patch sanitizing the lines. sanitise_line_reset(); @@ -1643,7 +1645,6 @@ sub process { $fileold =~ s@^([^/]*)/@@ if (!$file); $filenew =~ s@^([^/]*)/@@ if (!$file); $realfile = $filenew; - checkfilename($realfile, \$acpi_testexpected, \$acpi_nontestexpected); $fileinfo = { "isgit" => 1, @@ -1677,8 +1678,6 @@ sub process { $realfile = $1; $realfile =~ s@^([^/]*)/@@ if (!$file); - checkfilename($realfile, \$acpi_testexpected, \$acpi_nontestexpected); - $p1_prefix = $1; if (!$file && $tree && $p1_prefix ne '' && -e "$root/$p1_prefix") { @@ -1771,9 +1770,7 @@ sub process { $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && (defined($1) || defined($2)))) && - !(($realfile ne '') && - defined($acpi_testexpected) && - ($realfile eq $acpi_testexpected))) { + $realfile !~ m#^tests/data/acpi/#) { $reported_maintainer_file = 1; WARN("added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); } From f48f16ec3d8cee0a3d843915451c5649fe3a8aaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 12 May 2025 17:11:55 +0100 Subject: [PATCH 1078/2760] scripts/checkpatch: use new hook for file permissions check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The file permissions check is the kind of check intended to be performed in the new start of file hook. Reviewed-by: Cédric Le Goater Reviewed-by: Peter Maydell Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 817df98231..881dcac29b 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1448,6 +1448,17 @@ sub process_file_list { # Called at the start of processing a diff hunk for a file sub process_start_of_file { my $fileinfo = shift; + + # Check for incorrect file permissions + if ($fileinfo->{action} eq "new" && ($fileinfo->{mode} & 0111)) { + my $permhere = $fileinfo->{linestart} . "FILE: " . + $fileinfo->{filenew} . "\n"; + if ($fileinfo->{filenew} =~ + /(\bMakefile(?:\.objs)?|\.(c|cc|cpp|h|mak|s|S))$/) { + ERROR("do not set execute permissions for source " . + "files\n" . $permhere); + } + } } # Called at the end of processing a diff hunk for a file @@ -1719,14 +1730,6 @@ sub process { $cnt_lines++ if ($realcnt != 0); -# Check for incorrect file permissions - if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) { - my $permhere = $here . "FILE: $realfile\n"; - if ($realfile =~ /(\bMakefile(?:\.objs)?|\.c|\.cc|\.cpp|\.h|\.mak|\.[sS])$/) { - ERROR("do not set execute permissions for source files\n" . $permhere); - } - } - # Only allow Python 3 interpreter if ($realline == 1 && $line =~ /^\+#!\ *\/usr\/bin\/(?:env )?python$/) { From 45abbf209f007db90d0a596139f653783cd1cb0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 15 May 2025 13:57:45 +0100 Subject: [PATCH 1079/2760] scripts/checkpatch: expand pattern for matching makefiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current regex matches Makefile & Makefile.objs, but the latter is no longer used, anjd we're missing coverage of Makefile.include and Makefile.target. Expand the pattern to match any suffix. Reviewed-by: Cédric Le Goater Reviewed-by: Peter Maydell Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 881dcac29b..82ec71e05d 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1454,7 +1454,7 @@ sub process_start_of_file { my $permhere = $fileinfo->{linestart} . "FILE: " . $fileinfo->{filenew} . "\n"; if ($fileinfo->{filenew} =~ - /(\bMakefile(?:\.objs)?|\.(c|cc|cpp|h|mak|s|S))$/) { + /(\bMakefile.*|\.(c|cc|cpp|h|mak|s|S))$/) { ERROR("do not set execute permissions for source " . "files\n" . $permhere); } From 1d745e6d96353af20f116672b87ae28b0d07eca9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 12 May 2025 18:01:00 +0100 Subject: [PATCH 1080/2760] scripts/checkpatch: use new hook for MAINTAINERS update check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When seeing a new/deleted/renamed file we check to see if MAINTAINERS is updated, but we don't give the user a list of files affected, as we don't want to repeat the same warning many times over. Using the new file list hook, we can give a single warning at the end with a list of filenames included. Reviewed-by: Cédric Le Goater Reviewed-by: Peter Maydell Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 82ec71e05d..c05559a108 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1443,6 +1443,25 @@ sub process_file_list { join("\n ", @acpi_nontestexpected) . "\n\nfound in the same patch\n"); } + + my $sawmaintainers = 0; + my @maybemaintainers; + foreach my $fileinfo (@fileinfos) { + if ($fileinfo->{action} ne "modified" && + $fileinfo->{filenew} !~ m#^tests/data/acpi/#) { + push @maybemaintainers, $fileinfo->{filenew}; + } + if ($fileinfo->{filenew} eq "MAINTAINERS") { + $sawmaintainers = 1; + } + } + + # If we don't see a MAINTAINERS update, prod the user to check + if (int(@maybemaintainers) > 0 && !$sawmaintainers) { + WARN("added, moved or deleted file(s):\n\n " . + join("\n ", @maybemaintainers) . + "\n\nDoes MAINTAINERS need updating?\n"); + } } # Called at the start of processing a diff hunk for a file @@ -1486,7 +1505,6 @@ sub process { my $in_header_lines = $file ? 0 : 1; my $in_commit_log = 0; #Scanning lines before patch - my $reported_maintainer_file = 0; my $reported_mixing_imported_file = 0; my $in_imported_file = 0; my $in_no_imported_file = 0; @@ -1761,23 +1779,6 @@ sub process { } } -# Check if MAINTAINERS is being updated. If so, there's probably no need to -# emit the "does MAINTAINERS need updating?" message on file add/move/delete - if ($line =~ /^\s*MAINTAINERS\s*\|/) { - $reported_maintainer_file = 1; - } - -# Check for added, moved or deleted files - if (!$reported_maintainer_file && !$in_commit_log && - ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ || - $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ || - ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ && - (defined($1) || defined($2)))) && - $realfile !~ m#^tests/data/acpi/#) { - $reported_maintainer_file = 1; - WARN("added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr); - } - # Check SPDX-License-Identifier references a permitted license if ($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) { &checkspdx($realfile, $1); From b36934063c5550a3fcb1b36027fd1a509142e4f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 8 May 2025 17:43:40 +0100 Subject: [PATCH 1081/2760] scripts/checkpatch: reimplement mandate for SPDX-License-Identifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Going forward we want all newly created source files to have an SPDX-License-Identifier tag present. Initially mandate this for C, Python, Perl, Shell source files, as well as JSON (QAPI) and Makefiles, while encouraging users to consider it for other file types. The new attempt at detecting missing SPDX-License-Identifier relies on the hooks for relying triggering logic at the end of scanning a new file in the diff. Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Reviewed-by: Peter Maydell Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index c05559a108..da13102eda 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1483,6 +1483,20 @@ sub process_start_of_file { # Called at the end of processing a diff hunk for a file sub process_end_of_file { my $fileinfo = shift; + + if ($fileinfo->{action} eq "new" && + !exists $fileinfo->{facts}->{sawspdx}) { + if ($fileinfo->{filenew} =~ + /(\.(c|h|py|pl|sh|json|inc)|Makefile.*)$/) { + # source code files MUST have SPDX license declared + ERROR("New file '" . $fileinfo->{filenew} . + "' requires 'SPDX-License-Identifier'"); + } else { + # Other files MAY have SPDX license if appropriate + WARN("Does new file '" . $fileinfo->{filenew} . + "' need 'SPDX-License-Identifier'?"); + } + } } sub process { @@ -1781,6 +1795,7 @@ sub process { # Check SPDX-License-Identifier references a permitted license if ($rawline =~ m,SPDX-License-Identifier: (.*?)(\*/)?\s*$,) { + $fileinfo->{facts}->{sawspdx} = 1; &checkspdx($realfile, $1); } From 1f59381d6c0126cf0348977caa73ab8f7cfa1269 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 15 May 2025 14:13:13 +0100 Subject: [PATCH 1082/2760] scripts/checkpatch: reject license boilerplate on new files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The previous commit mandates use of SPDX-License-Identifier on common source files, and encourages it on all other files. Some contributors are none the less still also including the license boilerplate text. This is redundant and will potentially cause trouble if inconsistent with the SPDX declaration. Match common boilerplate text blurbs and report them as invalid, for newly added files. Reviewed-by: Peter Maydell Reviewed-by: Cédric Le Goater Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index da13102eda..9291bc9209 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -365,6 +365,18 @@ sub help { qr{guintptr}, ); +# Match text found in common license boilerplate comments: +# for new files the SPDX-License-Identifier line is sufficient. +our @LICENSE_BOILERPLATE = ( + "licensed under the terms of the GNU GPL", + "under the terms of the GNU General Public License", + "under the terms of the GNU Lesser General Public", + "Permission is hereby granted, free of charge", + "GNU GPL, version 2 or later", + "See the COPYING file" +); +our $LICENSE_BOILERPLATE_RE = join("|", @LICENSE_BOILERPLATE); + # Load common spelling mistakes and build regular expression list. my $misspellings; my %spelling_fix; @@ -1497,6 +1509,13 @@ sub process_end_of_file { "' need 'SPDX-License-Identifier'?"); } } + if ($fileinfo->{action} eq "new" && + exists $fileinfo->{facts}->{sawboilerplate}) { + ERROR("New file '" . $fileinfo->{filenew} . "' must " . + "not have license boilerplate header text, only " . + "the SPDX-License-Identifier, unless this file was " . + "copied from existing code already having such text."); + } } sub process { @@ -1799,6 +1818,10 @@ sub process { &checkspdx($realfile, $1); } + if ($rawline =~ /$LICENSE_BOILERPLATE_RE/) { + $fileinfo->{facts}->{sawboilerplate} = 1; + } + if ($rawline =~ m,(SPDX-[a-zA-Z0-9-_]+):,) { my $tag = $1; my @permitted = qw( From 0dc051aa85e1bd68d5c5110fa8af69204e6dbd3d Mon Sep 17 00:00:00 2001 From: Juraj Marcin Date: Wed, 21 May 2025 15:52:30 +0200 Subject: [PATCH 1083/2760] io: Fix partial struct copy in qio_dns_resolver_lookup_sync_inet() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit aec21d3175 (qapi: Add InetSocketAddress member keep-alive) introduces the keep-alive flag, but this flag is not copied together with other options in qio_dns_resolver_lookup_sync_inet(). This patch fixes this issue and also prevents future ones by copying the entire structure first and only then overriding a few attributes that need to be different. Fixes: aec21d31756c (qapi: Add InetSocketAddress member keep-alive) Signed-off-by: Juraj Marcin Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé --- io/dns-resolver.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/io/dns-resolver.c b/io/dns-resolver.c index 53b0e8407a..3712438f82 100644 --- a/io/dns-resolver.c +++ b/io/dns-resolver.c @@ -111,22 +111,11 @@ static int qio_dns_resolver_lookup_sync_inet(QIODNSResolver *resolver, uaddr, INET6_ADDRSTRLEN, uport, 32, NI_NUMERICHOST | NI_NUMERICSERV); - newaddr->u.inet = (InetSocketAddress){ - .host = g_strdup(uaddr), - .port = g_strdup(uport), - .has_numeric = true, - .numeric = true, - .has_to = iaddr->has_to, - .to = iaddr->to, - .has_ipv4 = iaddr->has_ipv4, - .ipv4 = iaddr->ipv4, - .has_ipv6 = iaddr->has_ipv6, - .ipv6 = iaddr->ipv6, -#ifdef HAVE_IPPROTO_MPTCP - .has_mptcp = iaddr->has_mptcp, - .mptcp = iaddr->mptcp, -#endif - }; + newaddr->u.inet = *iaddr; + newaddr->u.inet.host = g_strdup(uaddr), + newaddr->u.inet.port = g_strdup(uport), + newaddr->u.inet.has_numeric = true, + newaddr->u.inet.numeric = true, (*addrs)[i] = newaddr; } From b8b5278aca78be4a1c2e7cbb11c6be176f63706d Mon Sep 17 00:00:00 2001 From: Juraj Marcin Date: Wed, 21 May 2025 15:52:31 +0200 Subject: [PATCH 1084/2760] util/qemu-sockets: Refactor setting client sockopts into a separate function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is done in preparation for enabling the SO_KEEPALIVE support for server sockets and adding settings for more TCP keep-alive socket options. Signed-off-by: Juraj Marcin Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé --- util/qemu-sockets.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 77477c1cd5..4a878e0527 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -205,6 +205,22 @@ static int try_bind(int socket, InetSocketAddress *saddr, struct addrinfo *e) #endif } +static int inet_set_sockopts(int sock, InetSocketAddress *saddr, Error **errp) +{ + if (saddr->keep_alive) { + int keep_alive = 1; + int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, + &keep_alive, sizeof(keep_alive)); + + if (ret < 0) { + error_setg_errno(errp, errno, + "Unable to set keep-alive option on socket"); + return -1; + } + } + return 0; +} + static int inet_listen_saddr(InetSocketAddress *saddr, int port_offset, int num, @@ -475,16 +491,9 @@ int inet_connect_saddr(InetSocketAddress *saddr, Error **errp) return sock; } - if (saddr->keep_alive) { - int val = 1; - int ret = setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, - &val, sizeof(val)); - - if (ret < 0) { - error_setg_errno(errp, errno, "Unable to set KEEPALIVE"); - close(sock); - return -1; - } + if (inet_set_sockopts(sock, saddr, errp) < 0) { + close(sock); + return -1; } return sock; From 911e0f2c6e2d00c985affa75ec188c8edcf480f2 Mon Sep 17 00:00:00 2001 From: Juraj Marcin Date: Wed, 21 May 2025 15:52:32 +0200 Subject: [PATCH 1085/2760] util/qemu-sockets: Refactor success and failure paths in inet_listen_saddr() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To get a listening socket, we need to first create a socket, try binding it to a certain port, and lastly starting listening to it. Each of these operations can fail due to various reasons, one of them being that the requested address/port is already in use. In such case, the function tries the same process with a new port number. This patch refactors the port number loop, so the success path is no longer buried inside the 'if' statements in the middle of the loop. Now, the success path is not nested and ends at the end of the iteration after successful socket creation, binding, and listening. In case any of the operations fails, it either continues to the next iteration (and the next port) or jumps out of the loop to handle the error and exits the function. Signed-off-by: Juraj Marcin Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé --- util/qemu-sockets.c | 51 ++++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 4a878e0527..329fdbfd97 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -303,11 +303,20 @@ static int inet_listen_saddr(InetSocketAddress *saddr, port_min = inet_getport(e); port_max = saddr->has_to ? saddr->to + port_offset : port_min; for (p = port_min; p <= port_max; p++) { + if (slisten >= 0) { + /* + * We have a socket we tried with the previous port. It cannot + * be rebound, we need to close it and create a new one. + */ + close(slisten); + slisten = -1; + } inet_setport(e, p); slisten = create_fast_reuse_socket(e); if (slisten < 0) { - /* First time we expect we might fail to create the socket + /* + * First time we expect we might fail to create the socket * eg if 'e' has AF_INET6 but ipv6 kmod is not loaded. * Later iterations should always succeed if first iteration * worked though, so treat that as fatal. @@ -317,40 +326,38 @@ static int inet_listen_saddr(InetSocketAddress *saddr, } else { error_setg_errno(errp, errno, "Failed to recreate failed listening socket"); - goto listen_failed; + goto fail; } } socket_created = true; rc = try_bind(slisten, saddr, e); if (rc < 0) { - if (errno != EADDRINUSE) { - error_setg_errno(errp, errno, "Failed to bind socket"); - goto listen_failed; - } - } else { - if (!listen(slisten, num)) { - goto listen_ok; + if (errno == EADDRINUSE) { + /* This port is already used, try the next one */ + continue; } - if (errno != EADDRINUSE) { - error_setg_errno(errp, errno, "Failed to listen on socket"); - goto listen_failed; + error_setg_errno(errp, errno, "Failed to bind socket"); + goto fail; + } + if (listen(slisten, num)) { + if (errno == EADDRINUSE) { + /* This port is already used, try the next one */ + continue; } + error_setg_errno(errp, errno, "Failed to listen on socket"); + goto fail; } - /* Someone else managed to bind to the same port and beat us - * to listen on it! Socket semantics does not allow us to - * recover from this situation, so we need to recreate the - * socket to allow bind attempts for subsequent ports: - */ - close(slisten); - slisten = -1; + /* We have a listening socket */ + freeaddrinfo(res); + return slisten; } } error_setg_errno(errp, errno, socket_created ? "Failed to find an available port" : "Failed to create a socket"); -listen_failed: +fail: saved_errno = errno; if (slisten >= 0) { close(slisten); @@ -358,10 +365,6 @@ static int inet_listen_saddr(InetSocketAddress *saddr, freeaddrinfo(res); errno = saved_errno; return -1; - -listen_ok: - freeaddrinfo(res); - return slisten; } #ifdef _WIN32 From 00064705ed1f3943d3634be25da434466c87e7d5 Mon Sep 17 00:00:00 2001 From: Juraj Marcin Date: Wed, 21 May 2025 15:52:33 +0200 Subject: [PATCH 1086/2760] util/qemu-sockets: Add support for keep-alive flag to passive sockets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit aec21d3175 (qapi: Add InetSocketAddress member keep-alive) introduces the keep-alive flag, which enables the SO_KEEPALIVE socket option, but only on client-side sockets. However, this option is also useful for server-side sockets, so they can check if a client is still reachable or drop the connection otherwise. This patch enables the SO_KEEPALIVE socket option on passive server-side sockets if the keep-alive flag is enabled. This socket option is then inherited by active server-side sockets communicating with connected clients. Signed-off-by: Juraj Marcin Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé --- qapi/sockets.json | 4 ++-- util/qemu-sockets.c | 9 +++------ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/qapi/sockets.json b/qapi/sockets.json index 6a95023315..62797cd027 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -56,8 +56,8 @@ # @ipv6: whether to accept IPv6 addresses, default try both IPv4 and # IPv6 # -# @keep-alive: enable keep-alive when connecting to this socket. Not -# supported for passive sockets. (Since 4.2) +# @keep-alive: enable keep-alive when connecting to/listening on this socket. +# (Since 4.2, not supported for listening sockets until 10.1) # # @mptcp: enable multi-path TCP. (Since 6.1) # diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 329fdbfd97..4fbf1ed5bf 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -236,12 +236,6 @@ static int inet_listen_saddr(InetSocketAddress *saddr, int saved_errno = 0; bool socket_created = false; - if (saddr->keep_alive) { - error_setg(errp, "keep-alive option is not supported for passive " - "sockets"); - return -1; - } - memset(&ai,0, sizeof(ai)); ai.ai_flags = AI_PASSIVE; if (saddr->has_numeric && saddr->numeric) { @@ -349,6 +343,9 @@ static int inet_listen_saddr(InetSocketAddress *saddr, goto fail; } /* We have a listening socket */ + if (inet_set_sockopts(slisten, saddr, errp) < 0) { + goto fail; + } freeaddrinfo(res); return slisten; } From 316e8ee8d614f049bfae697570a5e62af450491c Mon Sep 17 00:00:00 2001 From: Juraj Marcin Date: Wed, 21 May 2025 15:52:34 +0200 Subject: [PATCH 1087/2760] util/qemu-sockets: Refactor inet_parse() to use QemuOpts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the inet address parser cannot handle multiple options where one is prefixed with the name of the other. For example, with the 'keep-alive-idle' option added, the current parser cannot parse '127.0.0.1:5000,keep-alive-idle=60,keep-alive' correctly. Instead, it fails with "error parsing 'keep-alive' flag '-idle=60,keep-alive'". To resolve these issues, this patch rewrites the inet address parsing using the QemuOpts parser, which the inet_parse_flag() function tries to mimic. This new parser supports all previously supported options and on top of that the 'numeric' flag is now also supported. The only difference is, the new parser produces an error if an unknown option is passed, instead of silently ignoring it. Signed-off-by: Juraj Marcin Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé --- tests/unit/test-util-sockets.c | 196 +++++++++++++++++++++++++++++++++ util/qemu-sockets.c | 158 +++++++++++++------------- 2 files changed, 270 insertions(+), 84 deletions(-) diff --git a/tests/unit/test-util-sockets.c b/tests/unit/test-util-sockets.c index 4c9dd0b271..9e39b92e7c 100644 --- a/tests/unit/test-util-sockets.c +++ b/tests/unit/test-util-sockets.c @@ -332,6 +332,177 @@ static void test_socket_unix_abstract(void) #endif /* CONFIG_LINUX */ +static void inet_parse_test_helper(const char *str, + InetSocketAddress *exp_addr, bool success) +{ + InetSocketAddress addr; + Error *error = NULL; + + int rc = inet_parse(&addr, str, &error); + + if (success) { + g_assert_cmpint(rc, ==, 0); + } else { + g_assert_cmpint(rc, <, 0); + } + if (exp_addr != NULL) { + g_assert_cmpstr(addr.host, ==, exp_addr->host); + g_assert_cmpstr(addr.port, ==, exp_addr->port); + /* Own members: */ + g_assert_cmpint(addr.has_numeric, ==, exp_addr->has_numeric); + g_assert_cmpint(addr.numeric, ==, exp_addr->numeric); + g_assert_cmpint(addr.has_to, ==, exp_addr->has_to); + g_assert_cmpint(addr.to, ==, exp_addr->to); + g_assert_cmpint(addr.has_ipv4, ==, exp_addr->has_ipv4); + g_assert_cmpint(addr.ipv4, ==, exp_addr->ipv4); + g_assert_cmpint(addr.has_ipv6, ==, exp_addr->has_ipv6); + g_assert_cmpint(addr.ipv6, ==, exp_addr->ipv6); + g_assert_cmpint(addr.has_keep_alive, ==, exp_addr->has_keep_alive); + g_assert_cmpint(addr.keep_alive, ==, exp_addr->keep_alive); +#ifdef HAVE_IPPROTO_MPTCP + g_assert_cmpint(addr.has_mptcp, ==, exp_addr->has_mptcp); + g_assert_cmpint(addr.mptcp, ==, exp_addr->mptcp); +#endif + } + + g_free(addr.host); + g_free(addr.port); +} + +static void test_inet_parse_nohost_good(void) +{ + char host[] = ""; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + }; + inet_parse_test_helper(":5000", &exp_addr, true); +} + +static void test_inet_parse_empty_bad(void) +{ + inet_parse_test_helper("", NULL, false); +} + +static void test_inet_parse_only_colon_bad(void) +{ + inet_parse_test_helper(":", NULL, false); +} + +static void test_inet_parse_ipv4_good(void) +{ + char host[] = "127.0.0.1"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + }; + inet_parse_test_helper("127.0.0.1:5000", &exp_addr, true); +} + +static void test_inet_parse_ipv4_noport_bad(void) +{ + inet_parse_test_helper("127.0.0.1", NULL, false); +} + +static void test_inet_parse_ipv6_good(void) +{ + char host[] = "::1"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + }; + inet_parse_test_helper("[::1]:5000", &exp_addr, true); +} + +static void test_inet_parse_ipv6_noend_bad(void) +{ + inet_parse_test_helper("[::1", NULL, false); +} + +static void test_inet_parse_ipv6_noport_bad(void) +{ + inet_parse_test_helper("[::1]:", NULL, false); +} + +static void test_inet_parse_ipv6_empty_bad(void) +{ + inet_parse_test_helper("[]:5000", NULL, false); +} + +static void test_inet_parse_hostname_good(void) +{ + char host[] = "localhost"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + }; + inet_parse_test_helper("localhost:5000", &exp_addr, true); +} + +static void test_inet_parse_all_options_good(void) +{ + char host[] = "::1"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + .has_numeric = true, + .numeric = true, + .has_to = true, + .to = 5006, + .has_ipv4 = true, + .ipv4 = false, + .has_ipv6 = true, + .ipv6 = true, + .has_keep_alive = true, + .keep_alive = true, +#ifdef HAVE_IPPROTO_MPTCP + .has_mptcp = true, + .mptcp = false, +#endif + }; + inet_parse_test_helper( + "[::1]:5000,numeric=on,to=5006,ipv4=off,ipv6=on,keep-alive=on" +#ifdef HAVE_IPPROTO_MPTCP + ",mptcp=off" +#endif + , &exp_addr, true); +} + +static void test_inet_parse_all_implicit_bool_good(void) +{ + char host[] = "::1"; + char port[] = "5000"; + InetSocketAddress exp_addr = { + .host = host, + .port = port, + .has_numeric = true, + .numeric = true, + .has_to = true, + .to = 5006, + .has_ipv4 = true, + .ipv4 = true, + .has_ipv6 = true, + .ipv6 = true, + .has_keep_alive = true, + .keep_alive = true, +#ifdef HAVE_IPPROTO_MPTCP + .has_mptcp = true, + .mptcp = true, +#endif + }; + inet_parse_test_helper( + "[::1]:5000,numeric,to=5006,ipv4,ipv6,keep-alive" +#ifdef HAVE_IPPROTO_MPTCP + ",mptcp" +#endif + , &exp_addr, true); +} + int main(int argc, char **argv) { bool has_ipv4, has_ipv6; @@ -377,6 +548,31 @@ int main(int argc, char **argv) test_socket_unix_abstract); #endif + g_test_add_func("/util/socket/inet-parse/nohost-good", + test_inet_parse_nohost_good); + g_test_add_func("/util/socket/inet-parse/empty-bad", + test_inet_parse_empty_bad); + g_test_add_func("/util/socket/inet-parse/only-colon-bad", + test_inet_parse_only_colon_bad); + g_test_add_func("/util/socket/inet-parse/ipv4-good", + test_inet_parse_ipv4_good); + g_test_add_func("/util/socket/inet-parse/ipv4-noport-bad", + test_inet_parse_ipv4_noport_bad); + g_test_add_func("/util/socket/inet-parse/ipv6-good", + test_inet_parse_ipv6_good); + g_test_add_func("/util/socket/inet-parse/ipv6-noend-bad", + test_inet_parse_ipv6_noend_bad); + g_test_add_func("/util/socket/inet-parse/ipv6-noport-bad", + test_inet_parse_ipv6_noport_bad); + g_test_add_func("/util/socket/inet-parse/ipv6-empty-bad", + test_inet_parse_ipv6_empty_bad); + g_test_add_func("/util/socket/inet-parse/hostname-good", + test_inet_parse_hostname_good); + g_test_add_func("/util/socket/inet-parse/all-options-good", + test_inet_parse_all_options_good); + g_test_add_func("/util/socket/inet-parse/all-bare-bool-good", + test_inet_parse_all_implicit_bool_good); + end: return g_test_run(); } diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 4fbf1ed5bf..403dc26b36 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -30,6 +30,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/qobject-output-visitor.h" #include "qemu/cutils.h" +#include "qemu/option.h" #include "trace.h" #ifndef AI_ADDRCONFIG @@ -600,115 +601,104 @@ static int inet_dgram_saddr(InetSocketAddress *sraddr, return -1; } -/* compatibility wrapper */ -static int inet_parse_flag(const char *flagname, const char *optstr, bool *val, - Error **errp) -{ - char *end; - size_t len; - - end = strstr(optstr, ","); - if (end) { - if (end[1] == ',') { /* Reject 'ipv6=on,,foo' */ - error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr); - return -1; - } - len = end - optstr; - } else { - len = strlen(optstr); - } - if (len == 0 || (len == 3 && strncmp(optstr, "=on", len) == 0)) { - *val = true; - } else if (len == 4 && strncmp(optstr, "=off", len) == 0) { - *val = false; - } else { - error_setg(errp, "error parsing '%s' flag '%s'", flagname, optstr); - return -1; - } - return 0; -} +static QemuOptsList inet_opts = { + .name = "InetSocketAddress", + .head = QTAILQ_HEAD_INITIALIZER(inet_opts.head), + .implied_opt_name = "addr", + .desc = { + { + .name = "addr", + .type = QEMU_OPT_STRING, + }, + { + .name = "numeric", + .type = QEMU_OPT_BOOL, + }, + { + .name = "to", + .type = QEMU_OPT_NUMBER, + }, + { + .name = "ipv4", + .type = QEMU_OPT_BOOL, + }, + { + .name = "ipv6", + .type = QEMU_OPT_BOOL, + }, + { + .name = "keep-alive", + .type = QEMU_OPT_BOOL, + }, +#ifdef HAVE_IPPROTO_MPTCP + { + .name = "mptcp", + .type = QEMU_OPT_BOOL, + }, +#endif + { /* end of list */ } + }, +}; int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) { - const char *optstr, *h; - char host[65]; - char port[33]; - int to; - int pos; - char *begin; - + QemuOpts *opts = qemu_opts_parse(&inet_opts, str, true, errp); + if (!opts) { + return -1; + } memset(addr, 0, sizeof(*addr)); /* parse address */ - if (str[0] == ':') { - /* no host given */ - host[0] = '\0'; - if (sscanf(str, ":%32[^,]%n", port, &pos) != 1) { - error_setg(errp, "error parsing port in address '%s'", str); - return -1; - } - } else if (str[0] == '[') { + const char *addr_str = qemu_opt_get(opts, "addr"); + if (!addr_str) { + error_setg(errp, "error parsing address ''"); + return -1; + } + if (str[0] == '[') { /* IPv6 addr */ - if (sscanf(str, "[%64[^]]]:%32[^,]%n", host, port, &pos) != 2) { - error_setg(errp, "error parsing IPv6 address '%s'", str); + const char *ip_end = strstr(addr_str, "]:"); + if (!ip_end || ip_end - addr_str < 2 || strlen(ip_end) < 3) { + error_setg(errp, "error parsing IPv6 address '%s'", addr_str); return -1; } + addr->host = g_strndup(addr_str + 1, ip_end - addr_str - 1); + addr->port = g_strdup(ip_end + 2); } else { - /* hostname or IPv4 addr */ - if (sscanf(str, "%64[^:]:%32[^,]%n", host, port, &pos) != 2) { - error_setg(errp, "error parsing address '%s'", str); + /* no host, hostname or IPv4 addr */ + const char *port = strchr(addr_str, ':'); + if (!port || strlen(port) < 2) { + error_setg(errp, "error parsing address '%s'", addr_str); return -1; } + addr->host = g_strndup(addr_str, port - addr_str); + addr->port = g_strdup(port + 1); } - addr->host = g_strdup(host); - addr->port = g_strdup(port); - /* parse options */ - optstr = str + pos; - h = strstr(optstr, ",to="); - if (h) { - h += 4; - if (sscanf(h, "%d%n", &to, &pos) != 1 || - (h[pos] != '\0' && h[pos] != ',')) { - error_setg(errp, "error parsing to= argument"); - return -1; - } + if (qemu_opt_find(opts, "numeric")) { + addr->has_numeric = true, + addr->numeric = qemu_opt_get_bool(opts, "numeric", false); + } + if (qemu_opt_find(opts, "to")) { addr->has_to = true; - addr->to = to; + addr->to = qemu_opt_get_number(opts, "to", 0); } - begin = strstr(optstr, ",ipv4"); - if (begin) { - if (inet_parse_flag("ipv4", begin + 5, &addr->ipv4, errp) < 0) { - return -1; - } + if (qemu_opt_find(opts, "ipv4")) { addr->has_ipv4 = true; + addr->ipv4 = qemu_opt_get_bool(opts, "ipv4", false); } - begin = strstr(optstr, ",ipv6"); - if (begin) { - if (inet_parse_flag("ipv6", begin + 5, &addr->ipv6, errp) < 0) { - return -1; - } + if (qemu_opt_find(opts, "ipv6")) { addr->has_ipv6 = true; + addr->ipv6 = qemu_opt_get_bool(opts, "ipv6", false); } - begin = strstr(optstr, ",keep-alive"); - if (begin) { - if (inet_parse_flag("keep-alive", begin + strlen(",keep-alive"), - &addr->keep_alive, errp) < 0) - { - return -1; - } + if (qemu_opt_find(opts, "keep-alive")) { addr->has_keep_alive = true; + addr->keep_alive = qemu_opt_get_bool(opts, "keep-alive", false); } #ifdef HAVE_IPPROTO_MPTCP - begin = strstr(optstr, ",mptcp"); - if (begin) { - if (inet_parse_flag("mptcp", begin + strlen(",mptcp"), - &addr->mptcp, errp) < 0) - { - return -1; - } + if (qemu_opt_find(opts, "mptcp")) { addr->has_mptcp = true; + addr->mptcp = qemu_opt_get_bool(opts, "mptcp", 0); } #endif return 0; From 1bd4237cb1095d71c16afad3ce93b4a1e453173e Mon Sep 17 00:00:00 2001 From: Juraj Marcin Date: Wed, 21 May 2025 15:52:35 +0200 Subject: [PATCH 1088/2760] util/qemu-sockets: Introduce inet socket options controlling TCP keep-alive MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With the default TCP stack configuration, it could be even 2 hours before the connection times out due to the other side not being reachable. However, in some cases, the application needs to be aware of a connection issue much sooner. This is the case, for example, for postcopy live migration. If there is no traffic from the migration destination guest (server-side) to the migration source guest (client-side), the destination keeps waiting for pages indefinitely and does not switch to the postcopy-paused state. This can happen, for example, if the destination QEMU instance is started with the '-S' command line option and the machine is not started yet, or if the machine is idle and produces no new page faults for not-yet-migrated pages. This patch introduces new inet socket parameters that control count, idle period, and interval of TCP keep-alive packets before the connection is considered broken. These parameters are available on systems where the respective TCP socket options are defined, that includes Linux, Windows, macOS, but not OpenBSD. Additionally, macOS defines TCP_KEEPIDLE as TCP_KEEPALIVE instead, so the patch supplies its own definition. The default value for all is 0, which means the system configuration is used. Signed-off-by: Juraj Marcin Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé --- meson.build | 30 +++++++++++++ qapi/sockets.json | 19 ++++++++ tests/unit/test-util-sockets.c | 39 +++++++++++++++++ util/qemu-sockets.c | 80 ++++++++++++++++++++++++++++++++++ 4 files changed, 168 insertions(+) diff --git a/meson.build b/meson.build index ad2053f968..fdad3fb528 100644 --- a/meson.build +++ b/meson.build @@ -2760,6 +2760,36 @@ if linux_io_uring.found() config_host_data.set('HAVE_IO_URING_PREP_WRITEV2', cc.has_header_symbol('liburing.h', 'io_uring_prep_writev2')) endif +config_host_data.set('HAVE_TCP_KEEPCNT', + cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPCNT') or + cc.compiles(''' + #include + #ifndef TCP_KEEPCNT + #error + #endif + int main(void) { return 0; }''', + name: 'Win32 TCP_KEEPCNT')) +# On Darwin TCP_KEEPIDLE is available under different name, TCP_KEEPALIVE. +# https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/bsd/man/man4/tcp.4#L172 +config_host_data.set('HAVE_TCP_KEEPIDLE', + cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPIDLE') or + cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPALIVE') or + cc.compiles(''' + #include + #ifndef TCP_KEEPIDLE + #error + #endif + int main(void) { return 0; }''', + name: 'Win32 TCP_KEEPIDLE')) +config_host_data.set('HAVE_TCP_KEEPINTVL', + cc.has_header_symbol('netinet/tcp.h', 'TCP_KEEPINTVL') or + cc.compiles(''' + #include + #ifndef TCP_KEEPINTVL + #error + #endif + int main(void) { return 0; }''', + name: 'Win32 TCP_KEEPINTVL')) # has_member config_host_data.set('HAVE_SIGEV_NOTIFY_THREAD_ID', diff --git a/qapi/sockets.json b/qapi/sockets.json index 62797cd027..f9f559daba 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -59,6 +59,22 @@ # @keep-alive: enable keep-alive when connecting to/listening on this socket. # (Since 4.2, not supported for listening sockets until 10.1) # +# @keep-alive-count: number of keep-alive packets sent before the connection is +# closed. Only supported for TCP sockets on systems where TCP_KEEPCNT +# socket option is defined (this includes Linux, Windows, macOS, FreeBSD, +# but not OpenBSD). When set to 0, system setting is used. (Since 10.1) +# +# @keep-alive-idle: time in seconds the connection needs to be idle before +# sending a keepalive packet. Only supported for TCP sockets on systems +# where TCP_KEEPIDLE socket option is defined (this includes Linux, +# Windows, macOS, FreeBSD, but not OpenBSD). When set to 0, system setting +# is used. (Since 10.1) +# +# @keep-alive-interval: time in seconds between keep-alive packets. Only +# supported for TCP sockets on systems where TCP_KEEPINTVL is defined (this +# includes Linux, Windows, macOS, FreeBSD, but not OpenBSD). When set to +# 0, system setting is used. (Since 10.1) +# # @mptcp: enable multi-path TCP. (Since 6.1) # # Since: 1.3 @@ -71,6 +87,9 @@ '*ipv4': 'bool', '*ipv6': 'bool', '*keep-alive': 'bool', + '*keep-alive-count': { 'type': 'uint32', 'if': 'HAVE_TCP_KEEPCNT' }, + '*keep-alive-idle': { 'type': 'uint32', 'if': 'HAVE_TCP_KEEPIDLE' }, + '*keep-alive-interval': { 'type': 'uint32', 'if': 'HAVE_TCP_KEEPINTVL' }, '*mptcp': { 'type': 'bool', 'if': 'HAVE_IPPROTO_MPTCP' } } } ## diff --git a/tests/unit/test-util-sockets.c b/tests/unit/test-util-sockets.c index 9e39b92e7c..8492f4d68f 100644 --- a/tests/unit/test-util-sockets.c +++ b/tests/unit/test-util-sockets.c @@ -359,6 +359,24 @@ static void inet_parse_test_helper(const char *str, g_assert_cmpint(addr.ipv6, ==, exp_addr->ipv6); g_assert_cmpint(addr.has_keep_alive, ==, exp_addr->has_keep_alive); g_assert_cmpint(addr.keep_alive, ==, exp_addr->keep_alive); +#ifdef HAVE_TCP_KEEPCNT + g_assert_cmpint(addr.has_keep_alive_count, ==, + exp_addr->has_keep_alive_count); + g_assert_cmpint(addr.keep_alive_count, ==, + exp_addr->keep_alive_count); +#endif +#ifdef HAVE_TCP_KEEPIDLE + g_assert_cmpint(addr.has_keep_alive_idle, ==, + exp_addr->has_keep_alive_idle); + g_assert_cmpint(addr.keep_alive_idle, ==, + exp_addr->keep_alive_idle); +#endif +#ifdef HAVE_TCP_KEEPINTVL + g_assert_cmpint(addr.has_keep_alive_interval, ==, + exp_addr->has_keep_alive_interval); + g_assert_cmpint(addr.keep_alive_interval, ==, + exp_addr->keep_alive_interval); +#endif #ifdef HAVE_IPPROTO_MPTCP g_assert_cmpint(addr.has_mptcp, ==, exp_addr->has_mptcp); g_assert_cmpint(addr.mptcp, ==, exp_addr->mptcp); @@ -460,6 +478,18 @@ static void test_inet_parse_all_options_good(void) .ipv6 = true, .has_keep_alive = true, .keep_alive = true, +#ifdef HAVE_TCP_KEEPCNT + .has_keep_alive_count = true, + .keep_alive_count = 10, +#endif +#ifdef HAVE_TCP_KEEPIDLE + .has_keep_alive_idle = true, + .keep_alive_idle = 60, +#endif +#ifdef HAVE_TCP_KEEPINTVL + .has_keep_alive_interval = true, + .keep_alive_interval = 30, +#endif #ifdef HAVE_IPPROTO_MPTCP .has_mptcp = true, .mptcp = false, @@ -467,6 +497,15 @@ static void test_inet_parse_all_options_good(void) }; inet_parse_test_helper( "[::1]:5000,numeric=on,to=5006,ipv4=off,ipv6=on,keep-alive=on" +#ifdef HAVE_TCP_KEEPCNT + ",keep-alive-count=10" +#endif +#ifdef HAVE_TCP_KEEPIDLE + ",keep-alive-idle=60" +#endif +#ifdef HAVE_TCP_KEEPINTVL + ",keep-alive-interval=30" +#endif #ifdef HAVE_IPPROTO_MPTCP ",mptcp=off" #endif diff --git a/util/qemu-sockets.c b/util/qemu-sockets.c index 403dc26b36..4773755fd5 100644 --- a/util/qemu-sockets.c +++ b/util/qemu-sockets.c @@ -45,6 +45,14 @@ # define AI_NUMERICSERV 0 #endif +/* + * On macOS TCP_KEEPIDLE is available under a different name, TCP_KEEPALIVE. + * https://github.com/apple/darwin-xnu/blob/xnu-4570.1.46/bsd/man/man4/tcp.4#L172 + */ +#if defined(TCP_KEEPALIVE) && !defined(TCP_KEEPIDLE) +# define TCP_KEEPIDLE TCP_KEEPALIVE +#endif + static int inet_getport(struct addrinfo *e) { @@ -218,6 +226,42 @@ static int inet_set_sockopts(int sock, InetSocketAddress *saddr, Error **errp) "Unable to set keep-alive option on socket"); return -1; } +#ifdef HAVE_TCP_KEEPCNT + if (saddr->has_keep_alive_count && saddr->keep_alive_count) { + int keep_count = saddr->keep_alive_count; + ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPCNT, &keep_count, + sizeof(keep_count)); + if (ret < 0) { + error_setg_errno(errp, errno, + "Unable to set TCP keep-alive count option on socket"); + return -1; + } + } +#endif +#ifdef HAVE_TCP_KEEPIDLE + if (saddr->has_keep_alive_idle && saddr->keep_alive_idle) { + int keep_idle = saddr->keep_alive_idle; + ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPIDLE, &keep_idle, + sizeof(keep_idle)); + if (ret < 0) { + error_setg_errno(errp, errno, + "Unable to set TCP keep-alive idle option on socket"); + return -1; + } + } +#endif +#ifdef HAVE_TCP_KEEPINTVL + if (saddr->has_keep_alive_interval && saddr->keep_alive_interval) { + int keep_interval = saddr->keep_alive_interval; + ret = setsockopt(sock, IPPROTO_TCP, TCP_KEEPINTVL, &keep_interval, + sizeof(keep_interval)); + if (ret < 0) { + error_setg_errno(errp, errno, + "Unable to set TCP keep-alive interval option on socket"); + return -1; + } + } +#endif } return 0; } @@ -630,6 +674,24 @@ static QemuOptsList inet_opts = { .name = "keep-alive", .type = QEMU_OPT_BOOL, }, +#ifdef HAVE_TCP_KEEPCNT + { + .name = "keep-alive-count", + .type = QEMU_OPT_NUMBER, + }, +#endif +#ifdef HAVE_TCP_KEEPIDLE + { + .name = "keep-alive-idle", + .type = QEMU_OPT_NUMBER, + }, +#endif +#ifdef HAVE_TCP_KEEPINTVL + { + .name = "keep-alive-interval", + .type = QEMU_OPT_NUMBER, + }, +#endif #ifdef HAVE_IPPROTO_MPTCP { .name = "mptcp", @@ -695,6 +757,24 @@ int inet_parse(InetSocketAddress *addr, const char *str, Error **errp) addr->has_keep_alive = true; addr->keep_alive = qemu_opt_get_bool(opts, "keep-alive", false); } +#ifdef HAVE_TCP_KEEPCNT + if (qemu_opt_find(opts, "keep-alive-count")) { + addr->has_keep_alive_count = true; + addr->keep_alive_count = qemu_opt_get_number(opts, "keep-alive-count", 0); + } +#endif +#ifdef HAVE_TCP_KEEPIDLE + if (qemu_opt_find(opts, "keep-alive-idle")) { + addr->has_keep_alive_idle = true; + addr->keep_alive_idle = qemu_opt_get_number(opts, "keep-alive-idle", 0); + } +#endif +#ifdef HAVE_TCP_KEEPINTVL + if (qemu_opt_find(opts, "keep-alive-interval")) { + addr->has_keep_alive_interval = true; + addr->keep_alive_interval = qemu_opt_get_number(opts, "keep-alive-interval", 0); + } +#endif #ifdef HAVE_IPPROTO_MPTCP if (qemu_opt_find(opts, "mptcp")) { addr->has_mptcp = true; From 81941aa896b718c194f08cebb4303561b64c60b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 21 May 2025 09:47:00 +0100 Subject: [PATCH 1089/2760] scripts/checkpatch.pl: mandate SPDX tag for Rust src files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Manos Pitsidianakis Signed-off-by: Daniel P. Berrangé --- scripts/checkpatch.pl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 9291bc9209..833f20f555 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -1499,7 +1499,7 @@ sub process_end_of_file { if ($fileinfo->{action} eq "new" && !exists $fileinfo->{facts}->{sawspdx}) { if ($fileinfo->{filenew} =~ - /(\.(c|h|py|pl|sh|json|inc)|Makefile.*)$/) { + /(\.(c|h|py|pl|sh|json|inc|rs)|Makefile.*)$/) { # source code files MUST have SPDX license declared ERROR("New file '" . $fileinfo->{filenew} . "' requires 'SPDX-License-Identifier'"); From ff2ab634e4bb3bddcf5f5ee29e0b46f71e3f4b54 Mon Sep 17 00:00:00 2001 From: Denis Rastyogin Date: Tue, 6 May 2025 17:13:37 +0300 Subject: [PATCH 1090/2760] qemu-img: fix offset calculation in bench This error was discovered by fuzzing qemu-img. The current offset calculation leads to an EIO error in block/block-backend.c: blk_check_byte_request(): if (offset > len || len - offset < bytes) { return -EIO; } This triggers the error message: "qemu-img: Failed request: Input/output error". Example of the issue: offset: 260076 len: 260096 bytes: 4096 This fix ensures that offset remains within a valid range. Signed-off-by: Denis Rastyogin Message-ID: <20250506141410.100119-1-gerben@altlinux.org> [kwolf: Fixed up integer overflow] Signed-off-by: Kevin Wolf --- qemu-img.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 76ac5d3028..139eeb5039 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -4488,10 +4488,10 @@ static void bench_cb(void *opaque, int ret) */ b->in_flight++; b->offset += b->step; - if (b->image_size == 0) { + if (b->image_size <= b->bufsize) { b->offset = 0; } else { - b->offset %= b->image_size; + b->offset %= b->image_size - b->bufsize; } if (b->write) { acb = blk_aio_pwritev(b->blk, offset, b->qiov, 0, bench_cb, b); From 5634622bcb339f213469eceeff005640492fc902 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 17 Apr 2025 17:10:53 -0400 Subject: [PATCH 1091/2760] file-posix: allow BLKZEROOUT with -t writeback The Linux BLKZEROOUT ioctl is only invoked when BDRV_O_NOCACHE is set because old kernels did not invalidate the page cache. In that case mixing BLKZEROOUT with buffered I/O could lead to corruption. However, Linux 4.9 commit 22dd6d356628 ("block: invalidate the page cache when issuing BLKZEROOUT") made BLKZEROOUT coherent with the page cache. I have checked that Linux 4.9+ kernels are shipped at least as far back as Debian 10 (buster), openSUSE Leap 15.2, and RHEL/CentOS 8. Use BLKZEROOUT with buffered I/O, mostly so `qemu-img ... -t writeback` can offload write zeroes. Cc: Paolo Bonzini Cc: Christoph Hellwig Signed-off-by: Stefan Hajnoczi Message-ID: <20250417211053.98700-1-stefanha@redhat.com> Reviewed-by: Eric Blake Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/file-posix.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index ec95b74869..5a3532e40b 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -785,17 +785,6 @@ static int raw_open_common(BlockDriverState *bs, QDict *options, } #endif - if (S_ISBLK(st.st_mode)) { -#ifdef __linux__ - /* On Linux 3.10, BLKDISCARD leaves stale data in the page cache. Do - * not rely on the contents of discarded blocks unless using O_DIRECT. - * Same for BLKZEROOUT. - */ - if (!(bs->open_flags & BDRV_O_NOCACHE)) { - s->has_write_zeroes = false; - } -#endif - } #ifdef __FreeBSD__ if (S_ISCHR(st.st_mode)) { /* From bf627788ef17721955bfcfba84209a07ae5f54ea Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Thu, 22 May 2025 15:08:03 +0200 Subject: [PATCH 1092/2760] file-posix: Probe paths and retry SG_IO on potential path errors When scsi-block is used on a host multipath device, it runs into the problem that the kernel dm-mpath doesn't know anything about SCSI or SG_IO and therefore can't decide if a SG_IO request returned an error and needs to be retried on a different path. Instead of getting working failover, an error is returned to scsi-block and handled according to the configured error policy. Obviously, this is not what users want, they want working failover. QEMU can parse the SG_IO result and determine whether this could have been a path error, but just retrying the same request could just send it to the same failing path again and result in the same error. With a kernel that supports the DM_MPATH_PROBE_PATHS ioctl on dm-mpath block devices (queued in the device mapper tree for Linux 6.16), we can tell the kernel to probe all paths and tell us if any usable paths remained. If so, we can now retry the SG_IO ioctl and expect it to be sent to a working path. Signed-off-by: Kevin Wolf Message-ID: <20250522130803.34738-1-kwolf@redhat.com> Reviewed-by: Stefan Hajnoczi Reviewed-by: Hanna Czenczek Signed-off-by: Kevin Wolf --- block/file-posix.c | 115 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/block/file-posix.c b/block/file-posix.c index 5a3532e40b..9b5f08ccb2 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -41,6 +41,7 @@ #include "scsi/pr-manager.h" #include "scsi/constants.h" +#include "scsi/utils.h" #if defined(__APPLE__) && (__MACH__) #include @@ -72,6 +73,7 @@ #include #endif #include +#include #include #include #include @@ -138,6 +140,22 @@ #define RAW_LOCK_PERM_BASE 100 #define RAW_LOCK_SHARED_BASE 200 +/* + * Multiple retries are mostly meant for two separate scenarios: + * + * - DM_MPATH_PROBE_PATHS returns success, but before SG_IO completes, another + * path goes down. + * + * - DM_MPATH_PROBE_PATHS failed all paths in the current path group, so we have + * to send another SG_IO to switch to another path group to probe the paths in + * it. + * + * Even if each path is in a separate path group (path_grouping_policy set to + * failover), it's rare to have more than eight path groups - and even then + * pretty unlikely that only bad path groups would be chosen in eight retries. + */ +#define SG_IO_MAX_RETRIES 8 + typedef struct BDRVRawState { int fd; bool use_lock; @@ -165,6 +183,7 @@ typedef struct BDRVRawState { bool use_linux_aio:1; bool has_laio_fdsync:1; bool use_linux_io_uring:1; + bool use_mpath:1; int page_cache_inconsistent; /* errno from fdatasync failure */ bool has_fallocate; bool needs_alignment; @@ -4253,15 +4272,105 @@ static int hdev_open(BlockDriverState *bs, QDict *options, int flags, /* Since this does ioctl the device must be already opened */ bs->sg = hdev_is_sg(bs); + /* sg devices aren't even block devices and can't use dm-mpath */ + s->use_mpath = !bs->sg; + return ret; } #if defined(__linux__) +#if defined(DM_MPATH_PROBE_PATHS) +static bool coroutine_fn sgio_path_error(int ret, sg_io_hdr_t *io_hdr) +{ + if (ret < 0) { + switch (ret) { + case -ENODEV: + return true; + case -EAGAIN: + /* + * The device is probably suspended. This happens while the dm table + * is reloaded, e.g. because a path is added or removed. This is an + * operation that should complete within 1ms, so just wait a bit and + * retry. + * + * If the device was suspended for another reason, we'll wait and + * retry SG_IO_MAX_RETRIES times. This is a tolerable delay before + * we return an error and potentially stop the VM. + */ + qemu_co_sleep_ns(QEMU_CLOCK_REALTIME, 1000000); + return true; + default: + return false; + } + } + + if (io_hdr->host_status != SCSI_HOST_OK) { + return true; + } + + switch (io_hdr->status) { + case GOOD: + case CONDITION_GOOD: + case INTERMEDIATE_GOOD: + case INTERMEDIATE_C_GOOD: + case RESERVATION_CONFLICT: + case COMMAND_TERMINATED: + return false; + case CHECK_CONDITION: + return !scsi_sense_buf_is_guest_recoverable(io_hdr->sbp, + io_hdr->mx_sb_len); + default: + return true; + } +} + +static bool coroutine_fn hdev_co_ioctl_sgio_retry(RawPosixAIOData *acb, int ret) +{ + BDRVRawState *s = acb->bs->opaque; + RawPosixAIOData probe_acb; + + if (!s->use_mpath) { + return false; + } + + if (!sgio_path_error(ret, acb->ioctl.buf)) { + return false; + } + + probe_acb = (RawPosixAIOData) { + .bs = acb->bs, + .aio_type = QEMU_AIO_IOCTL, + .aio_fildes = s->fd, + .aio_offset = 0, + .ioctl = { + .buf = NULL, + .cmd = DM_MPATH_PROBE_PATHS, + }, + }; + + ret = raw_thread_pool_submit(handle_aiocb_ioctl, &probe_acb); + if (ret == -ENOTTY) { + s->use_mpath = false; + } else if (ret == -EAGAIN) { + /* The device might be suspended for a table reload, worth retrying */ + return true; + } + + return ret == 0; +} +#else +static bool coroutine_fn hdev_co_ioctl_sgio_retry(RawPosixAIOData *acb, int ret) +{ + return false; +} +#endif /* DM_MPATH_PROBE_PATHS */ + static int coroutine_fn hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) { BDRVRawState *s = bs->opaque; RawPosixAIOData acb; + int retries = SG_IO_MAX_RETRIES; int ret; ret = fd_open(bs); @@ -4289,7 +4398,11 @@ hdev_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf) }, }; - return raw_thread_pool_submit(handle_aiocb_ioctl, &acb); + do { + ret = raw_thread_pool_submit(handle_aiocb_ioctl, &acb); + } while (req == SG_IO && retries-- && hdev_co_ioctl_sgio_retry(&acb, ret)); + + return ret; } #endif /* linux */ From bf53e2023271cfea80d7cae1d92143181fa547b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 11 Mar 2025 17:51:10 +0400 Subject: [PATCH 1093/2760] ui/gtk: warn if setting the clipboard failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just in case. Reviewed-by: Daniel P. Berrangé Signed-off-by: Marc-André Lureau --- ui/gtk-clipboard.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/ui/gtk-clipboard.c b/ui/gtk-clipboard.c index 8d8a636fd1..65d89ec601 100644 --- a/ui/gtk-clipboard.c +++ b/ui/gtk-clipboard.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/main-loop.h" #include "ui/gtk.h" @@ -95,11 +96,13 @@ static void gd_clipboard_update_info(GtkDisplayState *gd, gtk_clipboard_clear(gd->gtkcb[s]); if (targets) { gd->cbowner[s] = true; - gtk_clipboard_set_with_data(gd->gtkcb[s], - targets, n_targets, - gd_clipboard_get_data, - gd_clipboard_clear, - gd); + if (!gtk_clipboard_set_with_data(gd->gtkcb[s], + targets, n_targets, + gd_clipboard_get_data, + gd_clipboard_clear, + gd)) { + warn_report("Failed to set GTK clipboard"); + } gtk_target_table_free(targets, n_targets); } From acc6a94a812cd1337785413b27580f8a8f4ac170 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 11 Mar 2025 17:52:14 +0400 Subject: [PATCH 1094/2760] ui/clipboard: use int for selection field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows to use a VMSTATE_INT32 field for migration purposes. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé --- include/ui/clipboard.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/ui/clipboard.h b/include/ui/clipboard.h index ab6acdbd8a..14b6099e73 100644 --- a/include/ui/clipboard.h +++ b/include/ui/clipboard.h @@ -112,7 +112,7 @@ struct QemuClipboardNotify { struct QemuClipboardInfo { uint32_t refcount; QemuClipboardPeer *owner; - QemuClipboardSelection selection; + int selection; /* QemuClipboardSelection */ bool has_serial; uint32_t serial; struct { From a3f59c70d6c943e3a4d9c44ed138fa15c5ded70b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 11 Mar 2025 17:53:06 +0400 Subject: [PATCH 1095/2760] ui/clipboard: split out QemuClipboardContent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows to use VMSTATE STRUCT in following migration support patch. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé --- include/ui/clipboard.h | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/include/ui/clipboard.h b/include/ui/clipboard.h index 14b6099e73..88cfff91ef 100644 --- a/include/ui/clipboard.h +++ b/include/ui/clipboard.h @@ -25,6 +25,7 @@ typedef enum QemuClipboardSelection QemuClipboardSelection; typedef struct QemuClipboardPeer QemuClipboardPeer; typedef struct QemuClipboardNotify QemuClipboardNotify; typedef struct QemuClipboardInfo QemuClipboardInfo; +typedef struct QemuClipboardContent QemuClipboardContent; /** * enum QemuClipboardType @@ -97,6 +98,24 @@ struct QemuClipboardNotify { }; }; + +/** + * struct QemuClipboardContent + * + * @available: whether the data is available + * @requested: whether the data was requested + * @size: the size of the @data + * @data: the clipboard data + * + * Clipboard content. + */ +struct QemuClipboardContent { + bool available; + bool requested; + uint32_t size; + void *data; +}; + /** * struct QemuClipboardInfo * @@ -115,12 +134,7 @@ struct QemuClipboardInfo { int selection; /* QemuClipboardSelection */ bool has_serial; uint32_t serial; - struct { - bool available; - bool requested; - size_t size; - void *data; - } types[QEMU_CLIPBOARD_TYPE__COUNT]; + QemuClipboardContent types[QEMU_CLIPBOARD_TYPE__COUNT]; }; /** From d0de94cbc053c4475fbf705cafa1ab488eae3a19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 11 Mar 2025 17:55:21 +0400 Subject: [PATCH 1096/2760] ui/clipboard: add vmstate_cbinfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a VMStateDescriptor for QemuClipboardInfo. Each clipboard owner will have to save its QemuClipboardInfo and reregister its owned clipboard after loading. (the global cbinfo has only pointers to owners, so it can't restore the relation with its owner if it was to handle migration) Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé --- include/ui/clipboard.h | 3 +++ ui/clipboard.c | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/include/ui/clipboard.h b/include/ui/clipboard.h index 88cfff91ef..62a96ce9ff 100644 --- a/include/ui/clipboard.h +++ b/include/ui/clipboard.h @@ -2,6 +2,7 @@ #define QEMU_CLIPBOARD_H #include "qemu/notify.h" +#include "migration/vmstate.h" /** * DOC: Introduction @@ -27,6 +28,8 @@ typedef struct QemuClipboardNotify QemuClipboardNotify; typedef struct QemuClipboardInfo QemuClipboardInfo; typedef struct QemuClipboardContent QemuClipboardContent; +extern const VMStateDescription vmstate_cbinfo; + /** * enum QemuClipboardType * diff --git a/ui/clipboard.c b/ui/clipboard.c index 132086eb13..f5db60c63d 100644 --- a/ui/clipboard.c +++ b/ui/clipboard.c @@ -7,6 +7,32 @@ static NotifierList clipboard_notifiers = static QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT]; +static const VMStateDescription vmstate_cbcontent = { + .name = "clipboard/content", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(available, QemuClipboardContent), + VMSTATE_BOOL(requested, QemuClipboardContent), + VMSTATE_UINT32(size, QemuClipboardContent), + VMSTATE_VBUFFER_ALLOC_UINT32(data, QemuClipboardContent, 0, 0, size), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_cbinfo = { + .name = "clipboard", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_INT32(selection, QemuClipboardInfo), + VMSTATE_BOOL(has_serial, QemuClipboardInfo), + VMSTATE_UINT32(serial, QemuClipboardInfo), + VMSTATE_STRUCT_ARRAY(types, QemuClipboardInfo, QEMU_CLIPBOARD_TYPE__COUNT, 0, vmstate_cbcontent, QemuClipboardContent), + VMSTATE_END_OF_LIST() + } +}; + void qemu_clipboard_peer_register(QemuClipboardPeer *peer) { notifier_list_add(&clipboard_notifiers, &peer->notifier); From c967ff606b99f5f4ecc16817b9d9604a9c943dd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 11 Mar 2025 18:51:08 +0400 Subject: [PATCH 1097/2760] ui/clipboard: delay clipboard update when not running MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When VM is paused, we shouldn't notify of clipboard changes, similar to how input are being treated. On unsuspend, notify of the current state. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé --- ui/clipboard.c | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/ui/clipboard.c b/ui/clipboard.c index f5db60c63d..ec00a0b8ec 100644 --- a/ui/clipboard.c +++ b/ui/clipboard.c @@ -1,4 +1,5 @@ #include "qemu/osdep.h" +#include "system/runstate.h" #include "ui/clipboard.h" #include "trace.h" @@ -7,6 +8,10 @@ static NotifierList clipboard_notifiers = static QemuClipboardInfo *cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT]; +static VMChangeStateEntry *cb_change_state_entry = NULL; + +static bool cb_reset_serial_on_resume = false; + static const VMStateDescription vmstate_cbcontent = { .name = "clipboard/content", .version_id = 0, @@ -33,8 +38,32 @@ const VMStateDescription vmstate_cbinfo = { } }; +static void qemu_clipboard_change_state(void *opaque, bool running, RunState state) +{ + int i; + + if (!running) { + return; + } + + if (cb_reset_serial_on_resume) { + qemu_clipboard_reset_serial(); + } + + for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { + if (cbinfo[i]) { + qemu_clipboard_update(cbinfo[i]); + } + } + +} + void qemu_clipboard_peer_register(QemuClipboardPeer *peer) { + if (cb_change_state_entry == NULL) { + cb_change_state_entry = qemu_add_vm_change_state_handler(qemu_clipboard_change_state, NULL); + } + notifier_list_add(&clipboard_notifiers, &peer->notifier); } @@ -109,7 +138,9 @@ void qemu_clipboard_update(QemuClipboardInfo *info) } } - notifier_list_notify(&clipboard_notifiers, ¬ify); + if (runstate_is_running() || runstate_check(RUN_STATE_SUSPENDED)) { + notifier_list_notify(&clipboard_notifiers, ¬ify); + } if (cbinfo[info->selection] != info) { qemu_clipboard_info_unref(cbinfo[info->selection]); @@ -189,7 +220,12 @@ void qemu_clipboard_reset_serial(void) info->serial = 0; } } - notifier_list_notify(&clipboard_notifiers, ¬ify); + + if (runstate_is_running() || runstate_check(RUN_STATE_SUSPENDED)) { + notifier_list_notify(&clipboard_notifiers, ¬ify); + } else { + cb_reset_serial_on_resume = true; + } } void qemu_clipboard_set_data(QemuClipboardPeer *peer, From ac5e2bc910d350cd0653b8d049518d642ee14a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 10 Mar 2025 15:26:25 +0400 Subject: [PATCH 1098/2760] ui/vdagent: replace Buffer with GByteArray MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Buffer is slightly more advanced than GByteArray, since it has a cursor/position. But vdagent code doesn't need it. This simplify a bit the code, and migration state. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé --- ui/vdagent.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/ui/vdagent.c b/ui/vdagent.c index 04513ded29..4027126b7d 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -47,7 +47,7 @@ struct VDAgentChardev { uint32_t msgsize; uint8_t *xbuf; uint32_t xoff, xsize; - Buffer outbuf; + GByteArray *outbuf; /* mouse */ DeviceState mouse_dev; @@ -142,16 +142,16 @@ static void vdagent_send_buf(VDAgentChardev *vd) { uint32_t len; - while (!buffer_empty(&vd->outbuf)) { + while (vd->outbuf->len) { len = qemu_chr_be_can_write(CHARDEV(vd)); if (len == 0) { return; } - if (len > vd->outbuf.offset) { - len = vd->outbuf.offset; + if (len > vd->outbuf->len) { + len = vd->outbuf->len; } - qemu_chr_be_write(CHARDEV(vd), vd->outbuf.buffer, len); - buffer_advance(&vd->outbuf, len); + qemu_chr_be_write(CHARDEV(vd), vd->outbuf->data, len); + g_byte_array_remove_range(vd->outbuf, 0, len); } } @@ -166,7 +166,7 @@ static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg) msg->protocol = VD_AGENT_PROTOCOL; - if (vd->outbuf.offset + msgsize > VDAGENT_BUFFER_LIMIT) { + if (vd->outbuf->len + msgsize > VDAGENT_BUFFER_LIMIT) { error_report("buffer full, dropping message"); return; } @@ -177,9 +177,8 @@ static void vdagent_send_msg(VDAgentChardev *vd, VDAgentMessage *msg) if (chunk.size > 1024) { chunk.size = 1024; } - buffer_reserve(&vd->outbuf, sizeof(chunk) + chunk.size); - buffer_append(&vd->outbuf, &chunk, sizeof(chunk)); - buffer_append(&vd->outbuf, msgbuf + msgoff, chunk.size); + g_byte_array_append(vd->outbuf, (void *)&chunk, sizeof(chunk)); + g_byte_array_append(vd->outbuf, msgbuf + msgoff, chunk.size); msgoff += chunk.size; } vdagent_send_buf(vd); @@ -859,7 +858,7 @@ static void vdagent_disconnect(VDAgentChardev *vd) { trace_vdagent_disconnect(); - buffer_reset(&vd->outbuf); + g_byte_array_set_size(vd->outbuf, 0); vdagent_reset_bufs(vd); vd->caps = 0; if (vd->mouse_hs) { @@ -920,7 +919,7 @@ static void vdagent_chr_init(Object *obj) { VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj); - buffer_init(&vd->outbuf, "vdagent-outbuf"); + vd->outbuf = g_byte_array_new(); error_setg(&vd->migration_blocker, "The vdagent chardev doesn't yet support migration"); } @@ -934,7 +933,7 @@ static void vdagent_chr_fini(Object *obj) if (vd->mouse_hs) { qemu_input_handler_unregister(vd->mouse_hs); } - buffer_free(&vd->outbuf); + g_clear_pointer(&vd->outbuf, g_byte_array_unref); } static const TypeInfo vdagent_chr_type_info = { From 688ff4cbdf4993c60ee250afc48e15c0880f28de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 11 Mar 2025 18:00:14 +0400 Subject: [PATCH 1099/2760] ui/vdagent: keep "connected" state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During post-load of migration, virtio will notify of fe_open state. However vdagent code will handle this as a reconnection. This will trigger a connection reset/caps with the agent. Check if the state actually changed before resetting the connection. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé --- ui/vdagent.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/ui/vdagent.c b/ui/vdagent.c index 4027126b7d..210b8c14ca 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -40,6 +40,7 @@ struct VDAgentChardev { bool clipboard; /* guest vdagent */ + bool connected; uint32_t caps; VDIChunkHeader chunk; uint32_t chunksize; @@ -858,6 +859,7 @@ static void vdagent_disconnect(VDAgentChardev *vd) { trace_vdagent_disconnect(); + vd->connected = false; g_byte_array_set_size(vd->outbuf, 0); vdagent_reset_bufs(vd); vd->caps = 0; @@ -876,6 +878,10 @@ static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) trace_vdagent_fe_open(fe_open); + if (vd->connected == fe_open) { + return; + } + if (!fe_open) { trace_vdagent_close(); vdagent_disconnect(vd); @@ -885,6 +891,7 @@ static void vdagent_chr_set_fe_open(struct Chardev *chr, int fe_open) return; } + vd->connected = true; vdagent_send_caps(vd, true); } From f626116f9897b95f68e5514a08098d590349c22e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 11 Mar 2025 18:03:47 +0400 Subject: [PATCH 1100/2760] ui/vdagent: factor out clipboard peer registration MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This allows common code reuse during migration. Note that resetting the serial is now done regardless if the clipboard peer was registered or not. This should still be correct. Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé --- ui/vdagent.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/ui/vdagent.c b/ui/vdagent.c index 210b8c14ca..fcbd7b167b 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -694,6 +694,18 @@ static void vdagent_chr_open(Chardev *chr, *be_opened = true; } +static void vdagent_clipboard_peer_register(VDAgentChardev *vd) +{ + if (vd->cbpeer.notifier.notify != NULL) { + return; + } + + vd->cbpeer.name = "vdagent"; + vd->cbpeer.notifier.notify = vdagent_clipboard_notify; + vd->cbpeer.request = vdagent_clipboard_request; + qemu_clipboard_peer_register(&vd->cbpeer); +} + static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg) { VDAgentAnnounceCapabilities *caps = (void *)msg->data; @@ -720,13 +732,9 @@ static void vdagent_chr_recv_caps(VDAgentChardev *vd, VDAgentMessage *msg) memset(vd->last_serial, 0, sizeof(vd->last_serial)); - if (have_clipboard(vd) && vd->cbpeer.notifier.notify == NULL) { + if (have_clipboard(vd)) { qemu_clipboard_reset_serial(); - - vd->cbpeer.name = "vdagent"; - vd->cbpeer.notifier.notify = vdagent_clipboard_notify; - vd->cbpeer.request = vdagent_clipboard_request; - qemu_clipboard_peer_register(&vd->cbpeer); + vdagent_clipboard_peer_register(vd); } } From 5d56bff11e3d7fdbbf7fda6c9a8ebd0d6b7a7bac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 11 Mar 2025 18:32:10 +0400 Subject: [PATCH 1101/2760] ui/vdagent: add migration support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé --- ui/vdagent.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) diff --git a/ui/vdagent.c b/ui/vdagent.c index fcbd7b167b..adc8755bd9 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -10,6 +10,7 @@ #include "ui/clipboard.h" #include "ui/console.h" #include "ui/input.h" +#include "migration/vmstate.h" #include "trace.h" #include "qapi/qapi-types-char.h" @@ -930,6 +931,146 @@ static void vdagent_chr_class_init(ObjectClass *oc, const void *data) cc->chr_accept_input = vdagent_chr_accept_input; } +static int post_load(void *opaque, int version_id) +{ + VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(opaque); + + if (have_mouse(vd) && vd->mouse_hs) { + qemu_input_handler_activate(vd->mouse_hs); + } + + if (have_clipboard(vd)) { + vdagent_clipboard_peer_register(vd); + } + + return 0; +} + +static const VMStateDescription vmstate_chunk = { + .name = "vdagent/chunk", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(port, VDIChunkHeader), + VMSTATE_UINT32(size, VDIChunkHeader), + VMSTATE_END_OF_LIST() + } +}; + +static const VMStateDescription vmstate_vdba = { + .name = "vdagent/bytearray", + .version_id = 0, + .minimum_version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(len, GByteArray), + VMSTATE_VBUFFER_ALLOC_UINT32(data, GByteArray, 0, 0, len), + VMSTATE_END_OF_LIST() + } +}; + +struct CBInfoArray { + uint32_t n; + QemuClipboardInfo cbinfo[QEMU_CLIPBOARD_SELECTION__COUNT]; +}; + +static const VMStateDescription vmstate_cbinfo_array = { + .name = "cbinfoarray", + .fields = (const VMStateField[]) { + VMSTATE_UINT32(n, struct CBInfoArray), + VMSTATE_STRUCT_VARRAY_UINT32(cbinfo, struct CBInfoArray, n, + 0, vmstate_cbinfo, QemuClipboardInfo), + VMSTATE_END_OF_LIST() + } +}; + +static int put_cbinfo(QEMUFile *f, void *pv, size_t size, + const VMStateField *field, JSONWriter *vmdesc) +{ + VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(pv); + struct CBInfoArray cbinfo = {}; + int i; + + if (!have_clipboard(vd)) { + return 0; + } + + for (i = 0; i < QEMU_CLIPBOARD_SELECTION__COUNT; i++) { + if (qemu_clipboard_peer_owns(&vd->cbpeer, i)) { + cbinfo.cbinfo[cbinfo.n++] = *qemu_clipboard_info(i); + } + } + + return vmstate_save_state(f, &vmstate_cbinfo_array, &cbinfo, vmdesc); +} + +static int get_cbinfo(QEMUFile *f, void *pv, size_t size, + const VMStateField *field) +{ + VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(pv); + struct CBInfoArray cbinfo = {}; + int i, ret; + + if (!have_clipboard(vd)) { + return 0; + } + + vdagent_clipboard_peer_register(vd); + + ret = vmstate_load_state(f, &vmstate_cbinfo_array, &cbinfo, 0); + if (ret) { + return ret; + } + + for (i = 0; i < cbinfo.n; i++) { + g_autoptr(QemuClipboardInfo) info = + qemu_clipboard_info_new(&vd->cbpeer, cbinfo.cbinfo[i].selection); + /* this will steal clipboard data pointer from cbinfo.types */ + memcpy(info->types, cbinfo.cbinfo[i].types, sizeof(cbinfo.cbinfo[i].types)); + qemu_clipboard_update(info); + } + + return 0; +} + +static const VMStateInfo vmstate_cbinfos = { + .name = "vdagent/cbinfos", + .get = get_cbinfo, + .put = put_cbinfo, +}; + +static const VMStateDescription vmstate_vdagent = { + .name = "vdagent", + .version_id = 0, + .minimum_version_id = 0, + .post_load = post_load, + .fields = (const VMStateField[]) { + VMSTATE_BOOL(connected, VDAgentChardev), + VMSTATE_UINT32(caps, VDAgentChardev), + VMSTATE_STRUCT(chunk, VDAgentChardev, 0, vmstate_chunk, VDIChunkHeader), + VMSTATE_UINT32(chunksize, VDAgentChardev), + VMSTATE_UINT32(msgsize, VDAgentChardev), + VMSTATE_VBUFFER_ALLOC_UINT32(msgbuf, VDAgentChardev, 0, 0, msgsize), + VMSTATE_UINT32(xsize, VDAgentChardev), + VMSTATE_UINT32(xoff, VDAgentChardev), + VMSTATE_VBUFFER_ALLOC_UINT32(xbuf, VDAgentChardev, 0, 0, xsize), + VMSTATE_STRUCT_POINTER(outbuf, VDAgentChardev, vmstate_vdba, GByteArray), + VMSTATE_UINT32(mouse_x, VDAgentChardev), + VMSTATE_UINT32(mouse_y, VDAgentChardev), + VMSTATE_UINT32(mouse_btn, VDAgentChardev), + VMSTATE_UINT32(mouse_display, VDAgentChardev), + VMSTATE_UINT32_ARRAY(last_serial, VDAgentChardev, + QEMU_CLIPBOARD_SELECTION__COUNT), + VMSTATE_UINT32_ARRAY(cbpending, VDAgentChardev, + QEMU_CLIPBOARD_SELECTION__COUNT), + { + .name = "cbinfos", + .info = &vmstate_cbinfos, + .flags = VMS_SINGLE, + }, + VMSTATE_END_OF_LIST() + } +}; + static void vdagent_chr_init(Object *obj) { VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj); @@ -937,6 +1078,7 @@ static void vdagent_chr_init(Object *obj) vd->outbuf = g_byte_array_new(); error_setg(&vd->migration_blocker, "The vdagent chardev doesn't yet support migration"); + vmstate_register_any(NULL, &vmstate_vdagent, vd); } static void vdagent_chr_fini(Object *obj) From 42000e0013709f21b54f71b63525c22cb6b92d5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 10 Mar 2025 11:46:13 +0400 Subject: [PATCH 1102/2760] ui/vdagent: remove migration blocker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: https://issues.redhat.com/browse/RHEL-81894 Signed-off-by: Marc-André Lureau Reviewed-by: Daniel P. Berrangé --- ui/vdagent.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/ui/vdagent.c b/ui/vdagent.c index adc8755bd9..c0746fe5b1 100644 --- a/ui/vdagent.c +++ b/ui/vdagent.c @@ -6,7 +6,6 @@ #include "qemu/option.h" #include "qemu/units.h" #include "hw/qdev-core.h" -#include "migration/blocker.h" #include "ui/clipboard.h" #include "ui/console.h" #include "ui/input.h" @@ -33,9 +32,6 @@ struct VDAgentChardev { Chardev parent; - /* TODO: migration isn't yet supported */ - Error *migration_blocker; - /* config */ bool mouse; bool clipboard; @@ -673,10 +669,6 @@ static void vdagent_chr_open(Chardev *chr, return; #endif - if (migrate_add_blocker(&vd->migration_blocker, errp) != 0) { - return; - } - vd->mouse = VDAGENT_MOUSE_DEFAULT; if (cfg->has_mouse) { vd->mouse = cfg->mouse; @@ -1076,8 +1068,6 @@ static void vdagent_chr_init(Object *obj) VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj); vd->outbuf = g_byte_array_new(); - error_setg(&vd->migration_blocker, - "The vdagent chardev doesn't yet support migration"); vmstate_register_any(NULL, &vmstate_vdagent, vd); } @@ -1085,7 +1075,6 @@ static void vdagent_chr_fini(Object *obj) { VDAgentChardev *vd = QEMU_VDAGENT_CHARDEV(obj); - migrate_del_blocker(&vd->migration_blocker); vdagent_disconnect(vd); if (vd->mouse_hs) { qemu_input_handler_unregister(vd->mouse_hs); From 9498e2f7e1a247557cfa0f830a86c398a23c6809 Mon Sep 17 00:00:00 2001 From: Weifeng Liu Date: Sun, 11 May 2025 15:33:11 +0800 Subject: [PATCH 1103/2760] ui/gtk: Document scale and coordinate handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The existence of multiple scaling factors forces us to deal with various coordinate systems and this would be confusing. It would be beneficial to define the concepts clearly and use consistent representation for variables in different coordinates. Signed-off-by: Weifeng Liu Message-ID: <20250511073337.876650-2-weifeng.liu.z@gmail.com> Acked-by: Gerd Hoffmann Acked-by: Marc-André Lureau --- ui/gtk.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/ui/gtk.c b/ui/gtk.c index 982037b2c0..9f3171abc5 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -800,6 +800,71 @@ void gd_update_monitor_refresh_rate(VirtualConsole *vc, GtkWidget *widget) #endif } +/** + * DOC: Coordinate handling. + * + * We are coping with sizes and positions in various coordinates and the + * handling of these coordinates is somewhat confusing. It would benefit us + * all if we define these coordinates explicitly and clearly. Besides, it's + * also helpful to follow the same naming convention for variables + * representing values in different coordinates. + * + * I. Definitions + * + * - (guest) buffer coordinate: this is the coordinates that the guest will + * see. The x/y offsets and width/height specified in commands sent by + * guest is basically in buffer coordinate. + * + * - (host) pixel coordinate: this is the coordinate in pixel level on the + * host destop. A window/widget of width 300 in pixel coordinate means it + * occupies 300 pixels horizontally. + * + * - (host) logical window coordinate: the existence of global scaling + * factor in desktop level makes this kind of coordinate play a role. It + * always holds that (logical window size) * (global scale factor) = + * (pixel size). + * + * - global scale factor: this is specified in desktop level and is + * typically invariant during the life cycle of the process. Users with + * high-DPI monitors might set this scale, for example, to 2, in order to + * make the UI look larger. + * + * - zooming scale: this can be freely controlled by the QEMU user to zoom + * in/out the guest content. + * + * II. Representation + * + * We'd like to use consistent representation for variables in different + * coordinates: + * - buffer coordinate: prefix fb + * - pixel coordinate: prefix p + * - logical window coordinate: prefix w + * + * For scales: + * - global scale factor: prefix gs + * - zooming scale: prefix scale/s + * + * Example: fbw, pw, ww for width in different coordinates + * + * III. Equation + * + * - fbw * gs * scale_x = pw + * - pw = gs * ww + * + * Consequently we have + * + * - fbw * scale_x = ww + * + * Example: assuming we are running QEMU on a 3840x2160 screen and have set + * global scaling factor to 2, if the guest buffer size is 1920x1080 and the + * zooming scale is 0.5, then we have: + * - fbw = 1920, fbh = 1080 + * - pw = 1920, ph = 1080 + * - ww = 960, wh = 540 + * A bonus of this configuration is that we can achieve pixel to pixel + * presentation of the guest content. + */ + static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) { VirtualConsole *vc = opaque; From 3a6b314409b42fe7c46c2bd80cfc2a6744d414fe Mon Sep 17 00:00:00 2001 From: Weifeng Liu Date: Sun, 11 May 2025 15:33:12 +0800 Subject: [PATCH 1104/2760] ui/gtk: Use consistent naming for variables in different coordinates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that we've documented definitions and presentation of various coordinates, let's enforce the rules. Signed-off-by: Weifeng Liu Message-ID: <20250511073337.876650-3-weifeng.liu.z@gmail.com> Acked-by: Gerd Hoffmann Acked-by: Marc-André Lureau --- ui/gtk-egl.c | 12 +++-- ui/gtk-gl-area.c | 14 ++--- ui/gtk.c | 133 ++++++++++++++++++++++++----------------------- 3 files changed, 82 insertions(+), 77 deletions(-) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index f7a428c86a..947c99334b 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -70,16 +70,18 @@ void gd_egl_draw(VirtualConsole *vc) QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; int fence_fd; #endif - int ww, wh, ws; + int ww, wh, pw, ph, gs; if (!vc->gfx.gls) { return; } window = gtk_widget_get_window(vc->gfx.drawing_area); - ws = gdk_window_get_scale_factor(window); - ww = gdk_window_get_width(window) * ws; - wh = gdk_window_get_height(window) * ws; + gs = gdk_window_get_scale_factor(window); + ww = gdk_window_get_width(window); + wh = gdk_window_get_height(window); + pw = ww * gs; + ph = wh * gs; if (vc->gfx.scanout_mode) { #ifdef CONFIG_GBM @@ -115,7 +117,7 @@ void gd_egl_draw(VirtualConsole *vc) eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, vc->gfx.esurface, vc->gfx.ectx); - surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); + surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, pw, ph); surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 2c9a0db425..ba9fbec432 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -42,16 +42,16 @@ void gd_gl_area_draw(VirtualConsole *vc) #ifdef CONFIG_GBM QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; #endif - int ww, wh, ws, y1, y2; + int pw, ph, gs, y1, y2; if (!vc->gfx.gls) { return; } gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); - ws = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area)); - ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area) * ws; - wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area) * ws; + gs = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area)); + pw = gtk_widget_get_allocated_width(vc->gfx.drawing_area) * gs; + ph = gtk_widget_get_allocated_height(vc->gfx.drawing_area) * gs; if (vc->gfx.scanout_mode) { if (!vc->gfx.guest_fb.framebuffer) { @@ -71,11 +71,11 @@ void gd_gl_area_draw(VirtualConsole *vc) glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.guest_fb.framebuffer); /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */ - glViewport(0, 0, ww, wh); + glViewport(0, 0, pw, ph); y1 = vc->gfx.y0_top ? 0 : vc->gfx.h; y2 = vc->gfx.y0_top ? vc->gfx.h : 0; glBlitFramebuffer(0, y1, vc->gfx.w, y2, - 0, 0, ww, wh, + 0, 0, pw, ph, GL_COLOR_BUFFER_BIT, GL_NEAREST); #ifdef CONFIG_GBM if (dmabuf) { @@ -101,7 +101,7 @@ void gd_gl_area_draw(VirtualConsole *vc) } gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); - surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, ww, wh); + surface_gl_setup_viewport(vc->gfx.gls, vc->gfx.ds, pw, ph); surface_gl_render_texture(vc->gfx.gls, vc->gfx.ds); } } diff --git a/ui/gtk.c b/ui/gtk.c index 9f3171abc5..8f5bb4b62e 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -387,16 +387,16 @@ static void *gd_win32_get_hwnd(VirtualConsole *vc) /** DisplayState Callbacks **/ static void gd_update(DisplayChangeListener *dcl, - int x, int y, int w, int h) + int fbx, int fby, int fbw, int fbh) { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkWindow *win; - int x1, x2, y1, y2; - int mx, my; - int fbw, fbh; - int ww, wh; + int wx1, wx2, wy1, wy2; + int wx_offset, wy_offset; + int ww_surface, wh_surface; + int ww_widget, wh_widget; - trace_gd_update(vc->label, x, y, w, h); + trace_gd_update(vc->label, fbx, fby, fbw, fbh); if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { return; @@ -405,35 +405,36 @@ static void gd_update(DisplayChangeListener *dcl, if (vc->gfx.convert) { pixman_image_composite(PIXMAN_OP_SRC, vc->gfx.ds->image, NULL, vc->gfx.convert, - x, y, 0, 0, x, y, w, h); + fbx, fby, 0, 0, fbx, fby, fbw, fbh); } - x1 = floor(x * vc->gfx.scale_x); - y1 = floor(y * vc->gfx.scale_y); + wx1 = floor(fbx * vc->gfx.scale_x); + wy1 = floor(fby * vc->gfx.scale_y); - x2 = ceil(x * vc->gfx.scale_x + w * vc->gfx.scale_x); - y2 = ceil(y * vc->gfx.scale_y + h * vc->gfx.scale_y); + wx2 = ceil(fbx * vc->gfx.scale_x + fbw * vc->gfx.scale_x); + wy2 = ceil(fby * vc->gfx.scale_y + fbh * vc->gfx.scale_y); - fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; - fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; + ww_surface = surface_width(vc->gfx.ds) * vc->gfx.scale_x; + wh_surface = surface_height(vc->gfx.ds) * vc->gfx.scale_y; win = gtk_widget_get_window(vc->gfx.drawing_area); if (!win) { return; } - ww = gdk_window_get_width(win); - wh = gdk_window_get_height(win); + ww_widget = gdk_window_get_width(win); + wh_widget = gdk_window_get_height(win); - mx = my = 0; - if (ww > fbw) { - mx = (ww - fbw) / 2; + wx_offset = wy_offset = 0; + if (ww_widget > ww_surface) { + wx_offset = (ww_widget - ww_surface) / 2; } - if (wh > fbh) { - my = (wh - fbh) / 2; + if (wh_widget > wh_surface) { + wy_offset = (wh_widget - wh_surface) / 2; } gtk_widget_queue_draw_area(vc->gfx.drawing_area, - mx + x1, my + y1, (x2 - x1), (y2 - y1)); + wx_offset + wx1, wy_offset + wy1, + (wx2 - wx1), (wy2 - wy1)); } static void gd_refresh(DisplayChangeListener *dcl) @@ -869,8 +870,8 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) { VirtualConsole *vc = opaque; GtkDisplayState *s = vc->s; - int mx, my; - int ww, wh; + int wx_offset, wy_offset; + int ww_widget, wh_widget, ww_surface, wh_surface; int fbw, fbh; #if defined(CONFIG_OPENGL) @@ -904,46 +905,47 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) fbw = surface_width(vc->gfx.ds); fbh = surface_height(vc->gfx.ds); - ww = gdk_window_get_width(gtk_widget_get_window(widget)); - wh = gdk_window_get_height(gtk_widget_get_window(widget)); + ww_widget = gdk_window_get_width(gtk_widget_get_window(widget)); + wh_widget = gdk_window_get_height(gtk_widget_get_window(widget)); if (s->full_screen) { - vc->gfx.scale_x = (double)ww / fbw; - vc->gfx.scale_y = (double)wh / fbh; + vc->gfx.scale_x = (double)ww_widget / fbw; + vc->gfx.scale_y = (double)wh_widget / fbh; } else if (s->free_scale) { double sx, sy; - sx = (double)ww / fbw; - sy = (double)wh / fbh; + sx = (double)ww_widget / fbw; + sy = (double)wh_widget / fbh; vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); } - fbw *= vc->gfx.scale_x; - fbh *= vc->gfx.scale_y; + ww_surface = fbw * vc->gfx.scale_x; + wh_surface = fbh * vc->gfx.scale_y; - mx = my = 0; - if (ww > fbw) { - mx = (ww - fbw) / 2; + wx_offset = wy_offset = 0; + if (ww_widget > ww_surface) { + wx_offset = (ww_widget - ww_surface) / 2; } - if (wh > fbh) { - my = (wh - fbh) / 2; + if (wh_widget > wh_surface) { + wy_offset = (wh_widget - wh_surface) / 2; } - cairo_rectangle(cr, 0, 0, ww, wh); + cairo_rectangle(cr, 0, 0, ww_widget, wh_widget); /* Optionally cut out the inner area where the pixmap will be drawn. This avoids 'flashing' since we're not double-buffering. Note we're using the undocumented behaviour of drawing the rectangle from right to left to cut out the whole */ - cairo_rectangle(cr, mx + fbw, my, - -1 * fbw, fbh); + cairo_rectangle(cr, wx_offset + ww_surface, wy_offset, + -1 * ww_surface, wh_surface); cairo_fill(cr); cairo_scale(cr, vc->gfx.scale_x, vc->gfx.scale_y); cairo_set_source_surface(cr, vc->gfx.surface, - mx / vc->gfx.scale_x, my / vc->gfx.scale_y); + wx_offset / vc->gfx.scale_x, + wy_offset / vc->gfx.scale_y); cairo_paint(cr); return TRUE; @@ -954,19 +956,19 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, { VirtualConsole *vc = opaque; GtkDisplayState *s = vc->s; - int x, y; - int mx, my; - int fbh, fbw; - int ww, wh; + int fbx, fby; + int wx_offset, wy_offset; + int wh_surface, ww_surface; + int ww_widget, wh_widget; if (!vc->gfx.ds) { return TRUE; } - fbw = surface_width(vc->gfx.ds) * vc->gfx.scale_x; - fbh = surface_height(vc->gfx.ds) * vc->gfx.scale_y; - ww = gtk_widget_get_allocated_width(widget); - wh = gtk_widget_get_allocated_height(widget); + ww_surface = surface_width(vc->gfx.ds) * vc->gfx.scale_x; + wh_surface = surface_height(vc->gfx.ds) * vc->gfx.scale_y; + ww_widget = gtk_widget_get_allocated_width(widget); + wh_widget = gtk_widget_get_allocated_height(widget); /* * `widget` may not have the same size with the frame buffer. @@ -974,41 +976,42 @@ static gboolean gd_motion_event(GtkWidget *widget, GdkEventMotion *motion, * To achieve that, `vc` will be displayed at (mx, my) * so that it is displayed at the center of the widget. */ - mx = my = 0; - if (ww > fbw) { - mx = (ww - fbw) / 2; + wx_offset = wy_offset = 0; + if (ww_widget > ww_surface) { + wx_offset = (ww_widget - ww_surface) / 2; } - if (wh > fbh) { - my = (wh - fbh) / 2; + if (wh_widget > wh_surface) { + wy_offset = (wh_widget - wh_surface) / 2; } /* * `motion` is reported in `widget` coordinates * so translating it to the coordinates in `vc`. */ - x = (motion->x - mx) / vc->gfx.scale_x; - y = (motion->y - my) / vc->gfx.scale_y; + fbx = (motion->x - wx_offset) / vc->gfx.scale_x; + fby = (motion->y - wy_offset) / vc->gfx.scale_y; - trace_gd_motion_event(ww, wh, gtk_widget_get_scale_factor(widget), x, y); + trace_gd_motion_event(ww_widget, wh_widget, + gtk_widget_get_scale_factor(widget), fbx, fby); if (qemu_input_is_absolute(vc->gfx.dcl.con)) { - if (x < 0 || y < 0 || - x >= surface_width(vc->gfx.ds) || - y >= surface_height(vc->gfx.ds)) { + if (fbx < 0 || fby < 0 || + fbx >= surface_width(vc->gfx.ds) || + fby >= surface_height(vc->gfx.ds)) { return TRUE; } - qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, x, + qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_X, fbx, 0, surface_width(vc->gfx.ds)); - qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, y, + qemu_input_queue_abs(vc->gfx.dcl.con, INPUT_AXIS_Y, fby, 0, surface_height(vc->gfx.ds)); qemu_input_event_sync(); } else if (s->last_set && s->ptr_owner == vc) { - qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, x - s->last_x); - qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, y - s->last_y); + qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_X, fbx - s->last_x); + qemu_input_queue_rel(vc->gfx.dcl.con, INPUT_AXIS_Y, fby - s->last_y); qemu_input_event_sync(); } - s->last_x = x; - s->last_y = y; + s->last_x = fbx; + s->last_y = fby; s->last_set = TRUE; if (!qemu_input_is_absolute(vc->gfx.dcl.con) && s->ptr_owner == vc) { From a19665448156f17b52b7f33e7960d57efcfca067 Mon Sep 17 00:00:00 2001 From: Weifeng Liu Date: Sun, 11 May 2025 15:33:13 +0800 Subject: [PATCH 1105/2760] gtk/ui: Introduce helper gd_update_scale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The code snippet updating scale_x/scale_y is general and will be used in next patch. Make it a function. Signed-off-by: Weifeng Liu Message-ID: <20250511073337.876650-4-weifeng.liu.z@gmail.com> Acked-by: Gerd Hoffmann Acked-by: Marc-André Lureau --- include/ui/gtk.h | 2 ++ ui/gtk.c | 30 +++++++++++++++++++----------- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/include/ui/gtk.h b/include/ui/gtk.h index aa3d637029..d3944046db 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -224,4 +224,6 @@ int gd_gl_area_make_current(DisplayGLCtx *dgc, /* gtk-clipboard.c */ void gd_clipboard_init(GtkDisplayState *gd); +void gd_update_scale(VirtualConsole *vc, int ww, int wh, int fbw, int fbh); + #endif /* UI_GTK_H */ diff --git a/ui/gtk.c b/ui/gtk.c index 8f5bb4b62e..47af49e387 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -801,6 +801,24 @@ void gd_update_monitor_refresh_rate(VirtualConsole *vc, GtkWidget *widget) #endif } +void gd_update_scale(VirtualConsole *vc, int ww, int wh, int fbw, int fbh) +{ + if (!vc) { + return; + } + + if (vc->s->full_screen) { + vc->gfx.scale_x = (double)ww / fbw; + vc->gfx.scale_y = (double)wh / fbh; + } else if (vc->s->free_scale) { + double sx, sy; + + sx = (double)ww / fbw; + sy = (double)wh / fbh; + + vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); + } +} /** * DOC: Coordinate handling. * @@ -908,17 +926,7 @@ static gboolean gd_draw_event(GtkWidget *widget, cairo_t *cr, void *opaque) ww_widget = gdk_window_get_width(gtk_widget_get_window(widget)); wh_widget = gdk_window_get_height(gtk_widget_get_window(widget)); - if (s->full_screen) { - vc->gfx.scale_x = (double)ww_widget / fbw; - vc->gfx.scale_y = (double)wh_widget / fbh; - } else if (s->free_scale) { - double sx, sy; - - sx = (double)ww_widget / fbw; - sy = (double)wh_widget / fbh; - - vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); - } + gd_update_scale(vc, ww_widget, wh_widget, fbw, fbh); ww_surface = fbw * vc->gfx.scale_x; wh_surface = fbh * vc->gfx.scale_y; From 8fb072472c38cb1778c5b0bebf535a8b13533857 Mon Sep 17 00:00:00 2001 From: Weifeng Liu Date: Sun, 11 May 2025 15:33:14 +0800 Subject: [PATCH 1106/2760] ui/gtk: Update scales in fixed-scale mode when rendering GL area MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When gl=on, scale_x and scale_y were set to 1 on startup that didn't reflect the real situation of the scan-out in free scale mode, resulting in incorrect cursor coordinates to be sent when moving the mouse pointer. Simply updating the scales before rendering the image fixes this issue. Signed-off-by: Weifeng Liu Message-ID: <20250511073337.876650-5-weifeng.liu.z@gmail.com> Acked-by: Gerd Hoffmann Acked-by: Marc-André Lureau --- ui/gtk-gl-area.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index ba9fbec432..db93cd6204 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -43,6 +43,8 @@ void gd_gl_area_draw(VirtualConsole *vc) QemuDmaBuf *dmabuf = vc->gfx.guest_fb.dmabuf; #endif int pw, ph, gs, y1, y2; + int ww, wh; + int fbw, fbh; if (!vc->gfx.gls) { return; @@ -50,8 +52,14 @@ void gd_gl_area_draw(VirtualConsole *vc) gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); gs = gdk_window_get_scale_factor(gtk_widget_get_window(vc->gfx.drawing_area)); - pw = gtk_widget_get_allocated_width(vc->gfx.drawing_area) * gs; - ph = gtk_widget_get_allocated_height(vc->gfx.drawing_area) * gs; + fbw = surface_width(vc->gfx.ds); + fbh = surface_height(vc->gfx.ds); + ww = gtk_widget_get_allocated_width(vc->gfx.drawing_area); + wh = gtk_widget_get_allocated_height(vc->gfx.drawing_area); + pw = ww * gs; + ph = wh * gs; + + gd_update_scale(vc, ww, wh, fbw, fbh); if (vc->gfx.scanout_mode) { if (!vc->gfx.guest_fb.framebuffer) { From 30aa105640b0a2a541744b6584d57c9a4b86debd Mon Sep 17 00:00:00 2001 From: Weifeng Liu Date: Sun, 11 May 2025 15:33:15 +0800 Subject: [PATCH 1107/2760] ui/sdl: Consider scaling in mouse event handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using sdl display backend, if the window is scaled, incorrect mouse positions will be reported since scaling is not properly handled. Fix it by transforming the positions from window coordinate to guest buffer coordinate. Signed-off-by: Weifeng Liu Message-ID: <20250511073337.876650-6-weifeng.liu.z@gmail.com> Acked-by: Gerd Hoffmann Acked-by: Marc-André Lureau --- ui/sdl2.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ui/sdl2.c b/ui/sdl2.c index cda4293a53..b00e421f7f 100644 --- a/ui/sdl2.c +++ b/ui/sdl2.c @@ -488,14 +488,14 @@ static void handle_mousemotion(SDL_Event *ev) { int max_x, max_y; struct sdl2_console *scon = get_scon_from_window(ev->motion.windowID); + int scr_w, scr_h, surf_w, surf_h, x, y, dx, dy; if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { return; } + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); if (qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { - int scr_w, scr_h; - SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); max_x = scr_w - 1; max_y = scr_h - 1; if (gui_grab && !gui_fullscreen @@ -509,9 +509,14 @@ static void handle_mousemotion(SDL_Event *ev) sdl_grab_start(scon); } } + surf_w = surface_width(scon->surface); + surf_h = surface_height(scon->surface); + x = (int64_t)ev->motion.x * surf_w / scr_w; + y = (int64_t)ev->motion.y * surf_h / scr_h; + dx = (int64_t)ev->motion.xrel * surf_w / scr_w; + dy = (int64_t)ev->motion.yrel * surf_h / scr_h; if (gui_grab || qemu_input_is_absolute(scon->dcl.con) || absolute_enabled) { - sdl_send_mouse_event(scon, ev->motion.xrel, ev->motion.yrel, - ev->motion.x, ev->motion.y, ev->motion.state); + sdl_send_mouse_event(scon, dx, dy, x, y, ev->motion.state); } } @@ -520,12 +525,17 @@ static void handle_mousebutton(SDL_Event *ev) int buttonstate = SDL_GetMouseState(NULL, NULL); SDL_MouseButtonEvent *bev; struct sdl2_console *scon = get_scon_from_window(ev->button.windowID); + int scr_w, scr_h, x, y; if (!scon || !qemu_console_is_graphic(scon->dcl.con)) { return; } bev = &ev->button; + SDL_GetWindowSize(scon->real_window, &scr_w, &scr_h); + x = (int64_t)bev->x * surface_width(scon->surface) / scr_w; + y = (int64_t)bev->y * surface_height(scon->surface) / scr_h; + if (!gui_grab && !qemu_input_is_absolute(scon->dcl.con)) { if (ev->type == SDL_MOUSEBUTTONUP && bev->button == SDL_BUTTON_LEFT) { /* start grabbing all events */ @@ -537,7 +547,7 @@ static void handle_mousebutton(SDL_Event *ev) } else { buttonstate &= ~SDL_BUTTON(bev->button); } - sdl_send_mouse_event(scon, 0, 0, bev->x, bev->y, buttonstate); + sdl_send_mouse_event(scon, 0, 0, x, y, buttonstate); } } From 02f25490879096b679ab3d7cb7f0facfef7c6484 Mon Sep 17 00:00:00 2001 From: Weifeng Liu Date: Sun, 11 May 2025 15:33:16 +0800 Subject: [PATCH 1108/2760] ui/gtk: Don't update scale in fixed scale mode in gtk-egl.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scale shouldn't be changed until user explicitly requests it in fixed scale mode (full-screen=false and free-scale=false). Use function gd_update_scale to complete scale updating instead. Signed-off-by: Weifeng Liu Message-ID: <20250511073337.876650-7-weifeng.liu.z@gmail.com> Acked-by: Gerd Hoffmann Acked-by: Marc-André Lureau --- ui/gtk-egl.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 947c99334b..f8e4f4bc70 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -95,8 +95,9 @@ void gd_egl_draw(VirtualConsole *vc) #endif gd_egl_scanout_flush(&vc->gfx.dcl, 0, 0, vc->gfx.w, vc->gfx.h); - vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); - vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds); + gd_update_scale(vc, ww, wh, + surface_width(vc->gfx.ds), + surface_height(vc->gfx.ds)); glFlush(); #ifdef CONFIG_GBM @@ -122,8 +123,9 @@ void gd_egl_draw(VirtualConsole *vc) eglSwapBuffers(qemu_egl_display, vc->gfx.esurface); - vc->gfx.scale_x = (double)ww / surface_width(vc->gfx.ds); - vc->gfx.scale_y = (double)wh / surface_height(vc->gfx.ds); + gd_update_scale(vc, ww, wh, + surface_width(vc->gfx.ds), + surface_height(vc->gfx.ds)); glFlush(); } From a1b28f71f7ff0fcafbe0672b788e8e57ee4fe8f6 Mon Sep 17 00:00:00 2001 From: Weifeng Liu Date: Sun, 11 May 2025 15:33:17 +0800 Subject: [PATCH 1109/2760] ui/gtk: Consider scaling when propagating ui info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ui width and height sent to guest is supposed to be in buffer coordinate. Hence conversion is required. If scaling (global window scale and zooming scale) is not respected in non-free-scale mode, window size could keep changing because of the existence of the iteration of the following steps: 1. In resize event or configure event, a size larger (or smaller) than the currently used one might be calculated due to not considering scaling. 2. On reception of the display size change event in guest, the guest might decide to do a mode setting and use the larger (or smaller) mode. 3. When the new guest scan-out command arrives, QEMU would request the window size to change to fit the new buffer size. This will trigger a resize event or a configure event, making us go back to step 1. Signed-off-by: Weifeng Liu Message-ID: <20250511073337.876650-8-weifeng.liu.z@gmail.com> Acked-by: Gerd Hoffmann Acked-by: Marc-André Lureau --- ui/gtk.c | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/ui/gtk.c b/ui/gtk.c index 47af49e387..8c4a94c8f6 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -772,8 +772,21 @@ static void gd_resize_event(GtkGLArea *area, gint width, gint height, gpointer *opaque) { VirtualConsole *vc = (void *)opaque; + double pw = width, ph = height; + double sx = vc->gfx.scale_x, sy = vc->gfx.scale_y; + GdkWindow *window = gtk_widget_get_window(GTK_WIDGET(area)); + const int gs = gdk_window_get_scale_factor(window); - gd_set_ui_size(vc, width, height); + if (!vc->s->free_scale && !vc->s->full_screen) { + pw /= sx; + ph /= sy; + } + + /** + * width and height here are in pixel coordinate, so we must divide it + * by global window scale (gs) + */ + gd_set_ui_size(vc, pw / gs, ph / gs); } #endif @@ -1836,8 +1849,16 @@ static gboolean gd_configure(GtkWidget *widget, GdkEventConfigure *cfg, gpointer opaque) { VirtualConsole *vc = opaque; + const double sx = vc->gfx.scale_x, sy = vc->gfx.scale_y; + double width = cfg->width, height = cfg->height; + + if (!vc->s->free_scale && !vc->s->full_screen) { + width /= sx; + height /= sy; + } + + gd_set_ui_size(vc, width, height); - gd_set_ui_size(vc, cfg->width, cfg->height); return FALSE; } From fdc09b028fde9d2ec70418735f354b51c8295d0f Mon Sep 17 00:00:00 2001 From: Weifeng Liu Date: Sun, 11 May 2025 15:33:18 +0800 Subject: [PATCH 1110/2760] ui/gtk-gl-area: Render guest content with padding in fixed-scale mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In fixed-scale mode (zoom-to-fit=false), we expect that scale should not change, meaning that if window size is larger than guest surface, padding is supposed to be added to preserve the scale. However, in OpenGL mode (gl=on), guest surface is always painted to the whole canvas without any padding. This change tries to fix this bug by adding appropriate padding when drawing surfaces. Signed-off-by: Weifeng Liu Message-ID: <20250511073337.876650-9-weifeng.liu.z@gmail.com> Acked-by: Gerd Hoffmann Acked-by: Marc-André Lureau --- ui/gtk-gl-area.c | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index db93cd6204..8151cc413c 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -44,7 +44,9 @@ void gd_gl_area_draw(VirtualConsole *vc) #endif int pw, ph, gs, y1, y2; int ww, wh; + int ww_surface, wh_surface; int fbw, fbh; + int wx_offset, wy_offset; if (!vc->gfx.gls) { return; @@ -61,6 +63,17 @@ void gd_gl_area_draw(VirtualConsole *vc) gd_update_scale(vc, ww, wh, fbw, fbh); + ww_surface = fbw * vc->gfx.scale_x; + wh_surface = fbh * vc->gfx.scale_y; + + wx_offset = wy_offset = 0; + if (ww > ww_surface) { + wx_offset = (ww - ww_surface) / 2; + } + if (wh > wh_surface) { + wy_offset = (wh - wh_surface) / 2; + } + if (vc->gfx.scanout_mode) { if (!vc->gfx.guest_fb.framebuffer) { return; @@ -79,11 +92,29 @@ void gd_gl_area_draw(VirtualConsole *vc) glBindFramebuffer(GL_READ_FRAMEBUFFER, vc->gfx.guest_fb.framebuffer); /* GtkGLArea sets GL_DRAW_FRAMEBUFFER for us */ + if (wx_offset > 0) { + glEnable(GL_SCISSOR_TEST); + glScissor(0, 0, wx_offset * gs, wh * gs); + glClear(GL_COLOR_BUFFER_BIT); + glScissor((ww - wx_offset) * gs, 0, wx_offset * gs, wh * gs); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + } + if (wy_offset > 0) { + glEnable(GL_SCISSOR_TEST); + glScissor(0, 0, ww * gs, wy_offset * gs); + glClear(GL_COLOR_BUFFER_BIT); + glScissor(0, (wh - wy_offset) * gs, ww * gs, wy_offset * gs); + glClear(GL_COLOR_BUFFER_BIT); + glDisable(GL_SCISSOR_TEST); + } + glViewport(0, 0, pw, ph); y1 = vc->gfx.y0_top ? 0 : vc->gfx.h; y2 = vc->gfx.y0_top ? vc->gfx.h : 0; glBlitFramebuffer(0, y1, vc->gfx.w, y2, - 0, 0, pw, ph, + wx_offset * gs, wy_offset * gs, + (ww - wx_offset) * gs, (wh - wy_offset) * gs, GL_COLOR_BUFFER_BIT, GL_NEAREST); #ifdef CONFIG_GBM if (dmabuf) { From f05e1a93f48729b568fdf86a12d56ee142cee5e1 Mon Sep 17 00:00:00 2001 From: Weifeng Liu Date: Sun, 11 May 2025 15:33:19 +0800 Subject: [PATCH 1111/2760] ui/gtk-egl: Render guest content with padding in fixed-scale mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scaling was not respected when rendering frames in gtk-egl.c (used if gl=on and X11 mode). To fix this, add fields x and y to struct egl_fb for x offset and y offset so we can add padding to window. Signed-off-by: Weifeng Liu Message-ID: <20250511073337.876650-10-weifeng.liu.z@gmail.com> Acked-by: Gerd Hoffmann Acked-by: Marc-André Lureau --- include/ui/egl-helpers.h | 4 +++- ui/egl-helpers.c | 10 ++++++++-- ui/gtk-egl.c | 36 +++++++++++++++++++++++++++++++----- ui/sdl2-gl.c | 2 +- 4 files changed, 43 insertions(+), 9 deletions(-) diff --git a/include/ui/egl-helpers.h b/include/ui/egl-helpers.h index fb80e15142..acf993fcf5 100644 --- a/include/ui/egl-helpers.h +++ b/include/ui/egl-helpers.h @@ -17,6 +17,8 @@ extern bool qemu_egl_angle_d3d; typedef struct egl_fb { int width; int height; + int x; + int y; GLuint texture; GLuint framebuffer; bool delete_texture; @@ -26,7 +28,7 @@ typedef struct egl_fb { #define EGL_FB_INIT { 0, } void egl_fb_destroy(egl_fb *fb); -void egl_fb_setup_default(egl_fb *fb, int width, int height); +void egl_fb_setup_default(egl_fb *fb, int width, int height, int x, int y); void egl_fb_setup_for_tex(egl_fb *fb, int width, int height, GLuint texture, bool delete); void egl_fb_setup_new_tex(egl_fb *fb, int width, int height); diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 9cda2bbbee..5503a795e4 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -93,14 +93,18 @@ void egl_fb_destroy(egl_fb *fb) fb->width = 0; fb->height = 0; + fb->x = 0; + fb->y = 0; fb->texture = 0; fb->framebuffer = 0; } -void egl_fb_setup_default(egl_fb *fb, int width, int height) +void egl_fb_setup_default(egl_fb *fb, int width, int height, int x, int y) { fb->width = width; fb->height = height; + fb->x = x; + fb->y = y; fb->framebuffer = 0; /* default framebuffer */ } @@ -145,6 +149,7 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) glBindFramebuffer(GL_READ_FRAMEBUFFER, src->framebuffer); glBindFramebuffer(GL_DRAW_FRAMEBUFFER, dst->framebuffer); glViewport(0, 0, dst->width, dst->height); + glClear(GL_COLOR_BUFFER_BIT); if (src->dmabuf) { x1 = qemu_dmabuf_get_x(src->dmabuf); @@ -161,7 +166,8 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) x2 = x1 + w; glBlitFramebuffer(x1, y1, x2, y2, - 0, 0, dst->width, dst->height, + dst->x, dst->y, + dst->x + dst->width, dst->y + dst->height, GL_COLOR_BUFFER_BIT, GL_LINEAR); } diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index f8e4f4bc70..0b787bea25 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -340,7 +340,11 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, { VirtualConsole *vc = container_of(dcl, VirtualConsole, gfx.dcl); GdkWindow *window; - int ww, wh, ws; + int px_offset, py_offset; + int gs; + int pw_widget, ph_widget, pw_surface, ph_surface; + int ww_widget, wh_widget, ww_surface, wh_surface; + int fbw, fbh; if (!vc->gfx.scanout_mode) { return; @@ -353,10 +357,32 @@ void gd_egl_scanout_flush(DisplayChangeListener *dcl, vc->gfx.esurface, vc->gfx.ectx); window = gtk_widget_get_window(vc->gfx.drawing_area); - ws = gdk_window_get_scale_factor(window); - ww = gdk_window_get_width(window) * ws; - wh = gdk_window_get_height(window) * ws; - egl_fb_setup_default(&vc->gfx.win_fb, ww, wh); + gs = gdk_window_get_scale_factor(window); + ww_widget = gdk_window_get_width(window); + wh_widget = gdk_window_get_height(window); + fbw = surface_width(vc->gfx.ds); + fbh = surface_height(vc->gfx.ds); + + gd_update_scale(vc, ww_widget, wh_widget, fbw, fbh); + + ww_surface = fbw * vc->gfx.scale_x; + wh_surface = fbh * vc->gfx.scale_y; + pw_widget = ww_widget * gs; + ph_widget = wh_widget * gs; + pw_surface = ww_surface * gs; + ph_surface = wh_surface * gs; + + px_offset = 0; + py_offset = 0; + if (pw_widget > pw_surface) { + px_offset = (pw_widget - pw_surface) / 2; + } + if (ph_widget > ph_surface) { + py_offset = (ph_widget - ph_surface) / 2; + } + + egl_fb_setup_default(&vc->gfx.win_fb, pw_surface, ph_surface, + px_offset, py_offset); if (vc->gfx.cursor_fb.texture) { egl_texture_blit(vc->gfx.gls, &vc->gfx.win_fb, &vc->gfx.guest_fb, vc->gfx.y0_top); diff --git a/ui/sdl2-gl.c b/ui/sdl2-gl.c index e01d9ab0c7..3be17d1079 100644 --- a/ui/sdl2-gl.c +++ b/ui/sdl2-gl.c @@ -241,7 +241,7 @@ void sdl2_gl_scanout_flush(DisplayChangeListener *dcl, SDL_GL_MakeCurrent(scon->real_window, scon->winctx); SDL_GetWindowSize(scon->real_window, &ww, &wh); - egl_fb_setup_default(&scon->win_fb, ww, wh); + egl_fb_setup_default(&scon->win_fb, ww, wh, 0, 0); egl_fb_blit(&scon->win_fb, &scon->guest_fb, !scon->y0_top); SDL_GL_SwapWindow(scon->real_window); From 9e9542a1771f5b44d198fbfa31b4b3fb947e46ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 15 May 2025 07:44:23 +0200 Subject: [PATCH 1112/2760] tests/functional: use 'none' audio driver for q800 tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit ac13a6b3fd ("audio: add Apple Sound Chip (ASC) emulation") the Quadra 800 machine has an audio device. It is not guaranteed that the default audio driver of the audio subsystem will work correctly on all host systems. Therefore, the 'none' audio driver should be used in all q800 tests. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2812 Reviewed-by: Marc-André Lureau Signed-off-by: Volker Rümelin Message-Id: <20250515054429.7385-1-vr_qemu@t-online.de> --- tests/functional/test_m68k_q800.py | 3 ++- tests/functional/test_m68k_replay.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_m68k_q800.py b/tests/functional/test_m68k_q800.py index 400b7aeb5d..b3e655346c 100755 --- a/tests/functional/test_m68k_q800.py +++ b/tests/functional/test_m68k_q800.py @@ -25,7 +25,8 @@ def test_m68k_q800(self): kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0 vga=off') self.vm.add_args('-kernel', kernel_path, - '-append', kernel_command_line) + '-append', kernel_command_line, + '-audio', 'none') self.vm.launch() console_pattern = 'Kernel command line: %s' % kernel_command_line self.wait_for_console_pattern(console_pattern) diff --git a/tests/functional/test_m68k_replay.py b/tests/functional/test_m68k_replay.py index 18c1db539c..213d6ae07e 100755 --- a/tests/functional/test_m68k_replay.py +++ b/tests/functional/test_m68k_replay.py @@ -24,7 +24,8 @@ def test_q800(self): kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + 'console=ttyS0 vga=off') console_pattern = 'No filesystem could mount root' - self.run_rr(kernel_path, kernel_command_line, console_pattern) + self.run_rr(kernel_path, kernel_command_line, console_pattern, + args=('-audio', 'none')) ASSET_MCF5208 = Asset( 'https://qemu-advcal.gitlab.io/qac-best-of-multiarch/download/day07.tar.xz', From 5ddd6c8dc849b4af44bd06840c9133d64e62c27c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 15 May 2025 07:44:24 +0200 Subject: [PATCH 1113/2760] audio: fix SIGSEGV in AUD_get_buffer_size_out() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As far as the emulated audio devices are concerned the pointer returned by AUD_open_out() is an opaque handle. This includes the NULL pointer. In this case, AUD_get_buffer_size_out() should return a sensible buffer size instead of triggering a segmentation fault. All other public AUD_*_out() and audio_*_out() functions handle this case. Reviewed-by: Marc-André Lureau Signed-off-by: Volker Rümelin Message-Id: <20250515054429.7385-2-vr_qemu@t-online.de> --- audio/audio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/audio/audio.c b/audio/audio.c index 41ee11aaad..70ef22b1a4 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -905,6 +905,10 @@ size_t AUD_read(SWVoiceIn *sw, void *buf, size_t size) int AUD_get_buffer_size_out(SWVoiceOut *sw) { + if (!sw) { + return 0; + } + return sw->hw->samples * sw->hw->info.bytes_per_frame; } From ccb4fec0e5f233cb61a83b3af59ae11716ea06c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 15 May 2025 07:44:25 +0200 Subject: [PATCH 1114/2760] audio: fix size calculation in AUD_get_buffer_size_out() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The buffer size calculated by AUD_get_buffer_size_out() is often incorrect. sw->hw->samples * sw->hw->info.bytes_per_frame is the size of the mixing engine buffer in audio frames multiplied by the size of one frame of the audio backend. Due to resampling or format conversion, the size of the frontend buffer can differ significantly. Return the correct buffer size when the mixing engine is used. Reviewed-by: Marc-André Lureau Signed-off-by: Volker Rümelin Message-Id: <20250515054429.7385-3-vr_qemu@t-online.de> --- audio/audio.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/audio/audio.c b/audio/audio.c index 70ef22b1a4..3f5baf0cc6 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -909,6 +909,10 @@ int AUD_get_buffer_size_out(SWVoiceOut *sw) return 0; } + if (audio_get_pdo_out(sw->s->dev)->mixing_engine) { + return sw->resample_buf.size * sw->info.bytes_per_frame; + } + return sw->hw->samples * sw->hw->info.bytes_per_frame; } From d009f26a54f573468be721590a19350c224bc730 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 15 May 2025 07:44:26 +0200 Subject: [PATCH 1115/2760] hw/audio/asc: fix SIGSEGV in asc_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AUD_open_out() may fail and return NULL. This may then lead to a segmentation fault in memset() below. The memset() behaviour is undefined if the pointer to the destination object is a null pointer. Add the missing error handling code. Reviewed-by: Marc-André Lureau Signed-off-by: Volker Rümelin Reviewed-by: Mark Cave-Ayland Message-Id: <20250515054429.7385-4-vr_qemu@t-online.de> --- hw/audio/asc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/audio/asc.c b/hw/audio/asc.c index 18382ccf6a..6721c0d9fb 100644 --- a/hw/audio/asc.c +++ b/hw/audio/asc.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qemu/timer.h" +#include "qapi/error.h" #include "hw/sysbus.h" #include "hw/irq.h" #include "audio/audio.h" @@ -653,6 +654,12 @@ static void asc_realize(DeviceState *dev, Error **errp) s->voice = AUD_open_out(&s->card, s->voice, "asc.out", s, asc_out_cb, &as); + if (!s->voice) { + AUD_remove_card(&s->card); + error_setg(errp, "Initializing audio stream failed"); + return; + } + s->shift = 1; s->samples = AUD_get_buffer_size_out(s->voice) >> s->shift; s->mixbuf = g_malloc0(s->samples << s->shift); From f4b1c3db11317c4bce18fa3bbb025df7c22ea54c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 15 May 2025 07:44:27 +0200 Subject: [PATCH 1116/2760] hw/audio/asc: replace g_malloc0() with g_malloc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no need to allocate initialized memory with g_malloc0() if it's directly followed by a memset() function call. g_malloc() is sufficient. Reviewed-by: Marc-André Lureau Reviewed-by: Mark Cave-Ayland Signed-off-by: Volker Rümelin Message-Id: <20250515054429.7385-5-vr_qemu@t-online.de> --- hw/audio/asc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/asc.c b/hw/audio/asc.c index 6721c0d9fb..edd42d6d91 100644 --- a/hw/audio/asc.c +++ b/hw/audio/asc.c @@ -664,7 +664,7 @@ static void asc_realize(DeviceState *dev, Error **errp) s->samples = AUD_get_buffer_size_out(s->voice) >> s->shift; s->mixbuf = g_malloc0(s->samples << s->shift); - s->silentbuf = g_malloc0(s->samples << s->shift); + s->silentbuf = g_malloc(s->samples << s->shift); memset(s->silentbuf, 0x80, s->samples << s->shift); /* Add easc registers if required */ From 9ddb7c85c965636f7abf91382dc40175ce121aa3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 15 May 2025 07:44:28 +0200 Subject: [PATCH 1117/2760] audio/mixeng: remove unnecessary pointer type casts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A simple assignment automatically converts a void pointer type to any other pointer type. Reviewed-by: Marc-André Lureau Signed-off-by: Volker Rümelin Message-Id: <20250515054429.7385-6-vr_qemu@t-online.de> --- audio/mixeng.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/audio/mixeng.c b/audio/mixeng.c index 69f6549224..13e1ff9b08 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -286,7 +286,7 @@ static const float float_scale_reciprocal = 1.f / ((int64_t)INT32_MAX + 1); static void conv_natural_float_to_mono(struct st_sample *dst, const void *src, int samples) { - float *in = (float *)src; + const float *in = src; while (samples--) { dst->r = dst->l = CONV_NATURAL_FLOAT(*in++); @@ -297,7 +297,7 @@ static void conv_natural_float_to_mono(struct st_sample *dst, const void *src, static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, int samples) { - float *in = (float *)src; + const float *in = src; while (samples--) { dst->l = CONV_NATURAL_FLOAT(*in++); @@ -314,7 +314,7 @@ t_sample *mixeng_conv_float[2] = { static void clip_natural_float_from_mono(void *dst, const struct st_sample *src, int samples) { - float *out = (float *)dst; + float *out = dst; while (samples--) { *out++ = CLIP_NATURAL_FLOAT(src->l + src->r); @@ -325,7 +325,7 @@ static void clip_natural_float_from_mono(void *dst, const struct st_sample *src, static void clip_natural_float_from_stereo( void *dst, const struct st_sample *src, int samples) { - float *out = (float *)dst; + float *out = dst; while (samples--) { *out++ = CLIP_NATURAL_FLOAT(src->l); From 5d978c5da7f7d85d3a74b7d4dbe9ba5c5584d560 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Volker=20R=C3=BCmelin?= Date: Thu, 15 May 2025 07:44:29 +0200 Subject: [PATCH 1118/2760] audio: add float sample endianness converters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit ed2a4a7941 ("audio: proper support for float samples in mixeng") added support for float audio samples. As there were no audio frontend devices with float support at that time, the code was limited to native endian float samples. When nobody was paying attention, an audio device that supports floating point samples crept in with commit eb9ad377bb ("virtio-sound: handle control messages and streams"). Add code for the audio subsystem to convert float samples to the correct endianness. The type punning code was taken from the PipeWire project. Reviewed-by: Marc-André Lureau Signed-off-by: Volker Rümelin Message-Id: <20250515054429.7385-7-vr_qemu@t-online.de> --- audio/audio.c | 3 +- audio/audio_template.h | 12 ++++--- audio/mixeng.c | 75 ++++++++++++++++++++++++++++++++++++++---- audio/mixeng.h | 6 ++-- 4 files changed, 82 insertions(+), 14 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index 3f5baf0cc6..b58ad74433 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -1892,7 +1892,8 @@ CaptureVoiceOut *AUD_add_capture( cap->buf = g_malloc0_n(hw->mix_buf.size, hw->info.bytes_per_frame); if (hw->info.is_float) { - hw->clip = mixeng_clip_float[hw->info.nchannels == 2]; + hw->clip = mixeng_clip_float[hw->info.nchannels == 2] + [hw->info.swap_endianness]; } else { hw->clip = mixeng_clip [hw->info.nchannels == 2] diff --git a/audio/audio_template.h b/audio/audio_template.h index 7ccfec0116..c29d79c443 100644 --- a/audio/audio_template.h +++ b/audio/audio_template.h @@ -174,9 +174,11 @@ static int glue (audio_pcm_sw_init_, TYPE) ( if (sw->info.is_float) { #ifdef DAC - sw->conv = mixeng_conv_float[sw->info.nchannels == 2]; + sw->conv = mixeng_conv_float[sw->info.nchannels == 2] + [sw->info.swap_endianness]; #else - sw->clip = mixeng_clip_float[sw->info.nchannels == 2]; + sw->clip = mixeng_clip_float[sw->info.nchannels == 2] + [sw->info.swap_endianness]; #endif } else { #ifdef DAC @@ -303,9 +305,11 @@ static HW *glue(audio_pcm_hw_add_new_, TYPE)(AudioState *s, if (hw->info.is_float) { #ifdef DAC - hw->clip = mixeng_clip_float[hw->info.nchannels == 2]; + hw->clip = mixeng_clip_float[hw->info.nchannels == 2] + [hw->info.swap_endianness]; #else - hw->conv = mixeng_conv_float[hw->info.nchannels == 2]; + hw->conv = mixeng_conv_float[hw->info.nchannels == 2] + [hw->info.swap_endianness]; #endif } else { #ifdef DAC diff --git a/audio/mixeng.c b/audio/mixeng.c index 13e1ff9b08..703ee5448f 100644 --- a/audio/mixeng.c +++ b/audio/mixeng.c @@ -283,6 +283,11 @@ static const float float_scale_reciprocal = 1.f / ((int64_t)INT32_MAX + 1); #endif #endif +#define F32_TO_F32S(v) \ + bswap32((union { uint32_t i; float f; }){ .f = (v) }.i) +#define F32S_TO_F32(v) \ + ((union { uint32_t i; float f; }){ .i = bswap32(v) }.f) + static void conv_natural_float_to_mono(struct st_sample *dst, const void *src, int samples) { @@ -294,6 +299,17 @@ static void conv_natural_float_to_mono(struct st_sample *dst, const void *src, } } +static void conv_swap_float_to_mono(struct st_sample *dst, const void *src, + int samples) +{ + const uint32_t *in_f32s = src; + + while (samples--) { + dst->r = dst->l = CONV_NATURAL_FLOAT(F32S_TO_F32(*in_f32s++)); + dst++; + } +} + static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, int samples) { @@ -306,9 +322,27 @@ static void conv_natural_float_to_stereo(struct st_sample *dst, const void *src, } } -t_sample *mixeng_conv_float[2] = { - conv_natural_float_to_mono, - conv_natural_float_to_stereo, +static void conv_swap_float_to_stereo(struct st_sample *dst, const void *src, + int samples) +{ + const uint32_t *in_f32s = src; + + while (samples--) { + dst->l = CONV_NATURAL_FLOAT(F32S_TO_F32(*in_f32s++)); + dst->r = CONV_NATURAL_FLOAT(F32S_TO_F32(*in_f32s++)); + dst++; + } +} + +t_sample *mixeng_conv_float[2][2] = { + { + conv_natural_float_to_mono, + conv_swap_float_to_mono, + }, + { + conv_natural_float_to_stereo, + conv_swap_float_to_stereo, + } }; static void clip_natural_float_from_mono(void *dst, const struct st_sample *src, @@ -322,6 +356,17 @@ static void clip_natural_float_from_mono(void *dst, const struct st_sample *src, } } +static void clip_swap_float_from_mono(void *dst, const struct st_sample *src, + int samples) +{ + uint32_t *out_f32s = dst; + + while (samples--) { + *out_f32s++ = F32_TO_F32S(CLIP_NATURAL_FLOAT(src->l + src->r)); + src++; + } +} + static void clip_natural_float_from_stereo( void *dst, const struct st_sample *src, int samples) { @@ -334,9 +379,27 @@ static void clip_natural_float_from_stereo( } } -f_sample *mixeng_clip_float[2] = { - clip_natural_float_from_mono, - clip_natural_float_from_stereo, +static void clip_swap_float_from_stereo( + void *dst, const struct st_sample *src, int samples) +{ + uint32_t *out_f32s = dst; + + while (samples--) { + *out_f32s++ = F32_TO_F32S(CLIP_NATURAL_FLOAT(src->l)); + *out_f32s++ = F32_TO_F32S(CLIP_NATURAL_FLOAT(src->r)); + src++; + } +} + +f_sample *mixeng_clip_float[2][2] = { + { + clip_natural_float_from_mono, + clip_swap_float_from_mono, + }, + { + clip_natural_float_from_stereo, + clip_swap_float_from_stereo, + } }; void audio_sample_to_uint64(const void *samples, int pos, diff --git a/audio/mixeng.h b/audio/mixeng.h index a5f56d2c26..ead93ac2f7 100644 --- a/audio/mixeng.h +++ b/audio/mixeng.h @@ -42,9 +42,9 @@ typedef void (f_sample) (void *dst, const struct st_sample *src, int samples); extern t_sample *mixeng_conv[2][2][2][3]; extern f_sample *mixeng_clip[2][2][2][3]; -/* indices: [stereo] */ -extern t_sample *mixeng_conv_float[2]; -extern f_sample *mixeng_clip_float[2]; +/* indices: [stereo][swap endianness] */ +extern t_sample *mixeng_conv_float[2][2]; +extern f_sample *mixeng_clip_float[2][2]; void *st_rate_start (int inrate, int outrate); void st_rate_flow(void *opaque, st_sample *ibuf, st_sample *obuf, From f6ccfd5d166acf495f5d6d320da503fae8dc14a4 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 16 Mar 2025 01:20:46 +0100 Subject: [PATCH 1119/2760] alsaaudio: Set try-poll to false by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Quoting Volker Rümelin: "try-poll=on tells the ALSA backend to try to use an event loop instead of the audio timer. This works most of the time. But the poll event handler in the ALSA backend has a bug. For example, if the guest can't provide enough audio frames in time, the ALSA buffer is only partly full and the event handler will be called again and again on every iteration of the main loop. This increases the processor load and the guest has less processor time to provide new audio frames in time. I have two examples where a guest can't recover from this situation and the guest seems to hang." One reproducer I've found is booting MorphOS demo iso on qemu-system-ppc -machine pegasos2 -audio alsa which should play a startup sound but instead it freezes. Even when it does not hang it plays choppy sound. Volker suggested using command line to set try-poll=off saying: "The try-poll=off arguments are typically necessary, because the alsa backend has a design issue with try-poll=on. If the guest can't provide enough audio frames, it's really unhelpful to ask for new audio frames on every main loop iteration until the guest can provide enough audio frames. Timer based playback doesn't have that problem." But users cannot easily find this option and having a non-working default is really unhelpful so to make life easier just set it to false by default which works until the issue with the alsa backend can be fixed. Signed-off-by: BALATON Zoltan Acked-by: Marc-André Lureau [ Marc-André - Updated QAPI and CLI doc ] Signed-off-by: Marc-André Lureau Message-Id: <20250316002046.D066A4E6004@zero.eik.bme.hu> --- audio/alsaaudio.c | 2 +- qapi/audio.json | 2 +- qemu-options.hx | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c index cacae1ea59..9b6c01c0ef 100644 --- a/audio/alsaaudio.c +++ b/audio/alsaaudio.c @@ -899,7 +899,7 @@ static void alsa_enable_in(HWVoiceIn *hw, bool enable) static void alsa_init_per_direction(AudiodevAlsaPerDirectionOptions *apdo) { if (!apdo->has_try_poll) { - apdo->try_poll = true; + apdo->try_poll = false; apdo->has_try_poll = true; } } diff --git a/qapi/audio.json b/qapi/audio.json index dd5a58d13e..49633cf317 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -96,7 +96,7 @@ # @period-length: the period length in microseconds # # @try-poll: attempt to use poll mode, falling back to non-polling -# access on failure (default true) +# access on failure (default false) # # Since: 4.0 ## diff --git a/qemu-options.hx b/qemu-options.hx index aab53bcfe8..7eb8e02b4b 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -965,7 +965,7 @@ SRST Sets the period length in microseconds. ``in|out.try-poll=on|off`` - Attempt to use poll mode with the device. Default is on. + Attempt to use poll mode with the device. Default is off. ``threshold=threshold`` Threshold (in microseconds) when playback starts. Default is 0. @@ -1002,7 +1002,7 @@ SRST ``in|out.buffer-count=count`` Sets the count of the buffers. - ``in|out.try-poll=on|of`` + ``in|out.try-poll=on|off`` Attempt to use poll mode with the device. Default is on. ``try-mmap=on|off`` From 2bccabe6df5e91145c1313bb79b98200aa13b5ff Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 17 Mar 2025 17:05:29 +0900 Subject: [PATCH 1120/2760] audio: Reset rate control when adding bytes Commit 90320051ea99 ("spiceaudio: add a pcm_ops buffer_get_free function") caused to emit messages saying "Resetting rate control" frequently when the guest generates no frames. audio_rate_peek_bytes() resets the rate control when frames < 0 || frames > 65536 where frames is the rate-limited number of frames. Resetting when frames < 0 is sensible as the number simply doesn't make sense. There is a problem when frames > 65536. It implies the guest stopped generating frames for a while so it makes sense to reset the rate control when the guest resumed generating frames. However, the commit mentioned earlier broke this assumption by letting spiceaudio call audio_rate_peek_bytes() whether the guest is generating frames or not. Reset the rate control in audio_rate_add_bytes(), which is called only when actually adding frames, according to the previous call to audio_rate_peek_bytes() to avoid frequent rate control resets even when the guest generates no frame. Signed-off-by: Akihiko Odaki Message-Id: <20250317-rate-v1-1-da9df062747c@daynix.com> --- audio/audio.c | 14 ++++++++------ audio/audio_int.h | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/audio/audio.c b/audio/audio.c index b58ad74433..89f091bc88 100644 --- a/audio/audio.c +++ b/audio/audio.c @@ -2283,17 +2283,19 @@ size_t audio_rate_peek_bytes(RateCtl *rate, struct audio_pcm_info *info) ticks = now - rate->start_ticks; bytes = muldiv64(ticks, info->bytes_per_second, NANOSECONDS_PER_SECOND); frames = (bytes - rate->bytes_sent) / info->bytes_per_frame; - if (frames < 0 || frames > 65536) { - AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n", frames); - audio_rate_start(rate); - frames = 0; - } + rate->peeked_frames = frames; - return frames * info->bytes_per_frame; + return frames < 0 ? 0 : frames * info->bytes_per_frame; } void audio_rate_add_bytes(RateCtl *rate, size_t bytes_used) { + if (rate->peeked_frames < 0 || rate->peeked_frames > 65536) { + AUD_log(NULL, "Resetting rate control (%" PRId64 " frames)\n", + rate->peeked_frames); + audio_rate_start(rate); + } + rate->bytes_sent += bytes_used; } diff --git a/audio/audio_int.h b/audio/audio_int.h index 2d079d00a2..f78ca05f92 100644 --- a/audio/audio_int.h +++ b/audio/audio_int.h @@ -255,6 +255,7 @@ const char *audio_application_name(void); typedef struct RateCtl { int64_t start_ticks; int64_t bytes_sent; + int64_t peeked_frames; } RateCtl; void audio_rate_start(RateCtl *rate); From d75cdf6883fac728540798e42a8104d016ec762c Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 9 May 2025 14:50:47 -0300 Subject: [PATCH 1121/2760] tests/qtest/ast2700-smc-test: Fix leak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ASAN spotted a leak of the memory used to hold the tmp_path: Direct leak of 35 byte(s) in 1 object(s) allocated from: #0 0x55e29aa96da9 in malloc ../projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:69:3 #1 0x7fe0cfb26518 in g_malloc ../glib/gmem.c:106 #2 0x7fe0cfb4146e in g_strconcat ../glib/gstrfuncs.c:629 #3 0x7fe0cfb0a78f in g_get_tmp_name ../glib/gfileutils.c:1742 #4 0x7fe0cfb0b00b in g_file_open_tmp ../glib/gfileutils.c:1802 #5 0x55e29ab53961 in test_ast2700_evb ../tests/qtest/ast2700-smc-test.c:20:10 #6 0x55e29ab53803 in main ../tests/qtest/ast2700-smc-test.c:65:5 #7 0x7fe0cf7bd24c in __libc_start_main ../csu/libc-start.c:308 #8 0x55e29a9f7759 in _start ../sysdeps/x86_64/start.S:120 Signed-off-by: Fabiano Rosas Reviewed-by: Jamin Lin Message-ID: <20250509175047.26066-1-farosas@suse.de> Signed-off-by: Cédric Le Goater --- tests/qtest/ast2700-smc-test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/ast2700-smc-test.c b/tests/qtest/ast2700-smc-test.c index d1c4856307..62d538d8a3 100644 --- a/tests/qtest/ast2700-smc-test.c +++ b/tests/qtest/ast2700-smc-test.c @@ -67,5 +67,6 @@ int main(int argc, char **argv) qtest_quit(ast2700_evb_data.s); unlink(ast2700_evb_data.tmp_path); + g_free(ast2700_evb_data.tmp_path); return ret; } From d09c0939c97061fca0faa842184171f2f94b3339 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Tue, 13 May 2025 16:08:06 +0800 Subject: [PATCH 1122/2760] tests/qtest/aspeed_smc-test: Fix memory leaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Link: https://patchwork.kernel.org/project/qemu-devel/patch/20250509175047.26066-1-farosas@suse.de/ Signed-off-by: Jamin Lin Reviewed-by: Laurent Vivier Link: https://lore.kernel.org/qemu-devel/20250513080806.1005996-1-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_smc-test.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/qtest/aspeed_smc-test.c b/tests/qtest/aspeed_smc-test.c index 4e1389385d..52a00e6f0a 100644 --- a/tests/qtest/aspeed_smc-test.c +++ b/tests/qtest/aspeed_smc-test.c @@ -228,5 +228,10 @@ int main(int argc, char **argv) unlink(ast2500_evb_data.tmp_path); unlink(ast2600_evb_data.tmp_path); unlink(ast1030_evb_data.tmp_path); + g_free(palmetto_data.tmp_path); + g_free(ast2500_evb_data.tmp_path); + g_free(ast2600_evb_data.tmp_path); + g_free(ast1030_evb_data.tmp_path); + return ret; } From f05cc69c6ce0242e2eeae3cd1513454006b8f040 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:33 +0800 Subject: [PATCH 1123/2760] hw/misc/aspeed_hace: Remove unused code for better readability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the previous design of the hash framework, accumulative hashing was not supported. To work around this limitation, commit 5cd7d85 introduced an iov_cache array to store all the hash data from firmware. Once the ASPEED HACE model collected all the data, it passed the iov_cache to the hash API to calculate the final digest. However, with commit e3c0752, the hash framework now supports accumulative hashing. This allows us to refactor the ASPEED HACE model, removing redundant logic and simplifying the implementation for better readability and maintainability. As a result, the iov_count variable is no longer needed—it was previously used to track how many cached entries were used for hashing. To maintain VMSTATE compatibility after removing this field, the VMSTATE_VERSION is bumped to 2 This cleanup follows significant changes in commit 4c1d0af4a28d, making the model more readable. - Deleted "iov_cache" and "iov_count" from "AspeedHACEState". - Removed "reconstruct_iov" function and related logic. - Simplified "do_hash_operation" by eliminating redundant checks. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 39 ++--------------------------------- include/hw/misc/aspeed_hace.h | 2 -- 2 files changed, 2 insertions(+), 39 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index f4bff32a00..9263739ea6 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -142,25 +142,6 @@ static bool has_padding(AspeedHACEState *s, struct iovec *iov, return false; } -static int reconstruct_iov(AspeedHACEState *s, struct iovec *iov, int id, - uint32_t *pad_offset) -{ - int i, iov_count; - if (*pad_offset != 0) { - s->iov_cache[s->iov_count].iov_base = iov[id].iov_base; - s->iov_cache[s->iov_count].iov_len = *pad_offset; - ++s->iov_count; - } - for (i = 0; i < s->iov_count; i++) { - iov[i].iov_base = s->iov_cache[i].iov_base; - iov[i].iov_len = s->iov_cache[i].iov_len; - } - iov_count = s->iov_count; - s->iov_count = 0; - s->total_req_len = 0; - return iov_count; -} - static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, bool acc_mode) { @@ -242,19 +223,6 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, iov[0].iov_base = haddr; iov[0].iov_len = len; i = 1; - - if (s->iov_count) { - /* - * In aspeed sdk kernel driver, sg_mode is disabled in hash_final(). - * Thus if we received a request with sg_mode disabled, it is - * required to check whether cache is empty. If no, we should - * combine cached iov and the current iov. - */ - s->total_req_len += len; - if (has_padding(s, iov, len, &total_msg_len, &pad_offset)) { - i = reconstruct_iov(s, iov, 0, &pad_offset); - } - } } if (acc_mode) { @@ -278,7 +246,6 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, qcrypto_hash_free(s->hash_ctx); s->hash_ctx = NULL; - s->iov_count = 0; s->total_req_len = 0; } } else if (qcrypto_hash_bytesv(algo, iov, i, &digest_buf, @@ -437,7 +404,6 @@ static void aspeed_hace_reset(DeviceState *dev) } memset(s->regs, 0, sizeof(s->regs)); - s->iov_count = 0; s->total_req_len = 0; } @@ -469,12 +435,11 @@ static const Property aspeed_hace_properties[] = { static const VMStateDescription vmstate_aspeed_hace = { .name = TYPE_ASPEED_HACE, - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(regs, AspeedHACEState, ASPEED_HACE_NR_REGS), VMSTATE_UINT32(total_req_len, AspeedHACEState), - VMSTATE_UINT32(iov_count, AspeedHACEState), VMSTATE_END_OF_LIST(), } }; diff --git a/include/hw/misc/aspeed_hace.h b/include/hw/misc/aspeed_hace.h index 5d4aa19cfe..b69a038d35 100644 --- a/include/hw/misc/aspeed_hace.h +++ b/include/hw/misc/aspeed_hace.h @@ -31,10 +31,8 @@ struct AspeedHACEState { MemoryRegion iomem; qemu_irq irq; - struct iovec iov_cache[ASPEED_HACE_MAX_SG]; uint32_t regs[ASPEED_HACE_NR_REGS]; uint32_t total_req_len; - uint32_t iov_count; MemoryRegion *dram_mr; AddressSpace dram_as; From c869da4edd7ad91975fd9887ec924ba621b3c7f8 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:34 +0800 Subject: [PATCH 1124/2760] hw/misc/aspeed_hace: Improve readability and consistency in variable naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, users define multiple local variables within different if-statements. To improve readability and maintain consistency in variable naming, rename the variables accordingly. Introduced "sg_addr" to clearly indicate the scatter-gather mode buffer address. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 67 +++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 9263739ea6..6be94963bc 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -145,15 +145,19 @@ static bool has_padding(AspeedHACEState *s, struct iovec *iov, static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, bool acc_mode) { + g_autofree uint8_t *digest_buf = NULL; struct iovec iov[ASPEED_HACE_MAX_SG]; + bool acc_final_request = false; + Error *local_err = NULL; uint32_t total_msg_len; - uint32_t pad_offset; - g_autofree uint8_t *digest_buf = NULL; size_t digest_len = 0; - bool sg_acc_mode_final_request = false; - int i; + uint32_t sg_addr = 0; + uint32_t pad_offset; + int iov_idx = 0; + uint32_t len = 0; + uint32_t src = 0; void *haddr; - Error *local_err = NULL; + hwaddr plen; if (acc_mode && s->hash_ctx == NULL) { s->hash_ctx = qcrypto_hash_new(algo, &local_err); @@ -166,74 +170,69 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, } if (sg_mode) { - uint32_t len = 0; - - for (i = 0; !(len & SG_LIST_LEN_LAST); i++) { - uint32_t addr, src; - hwaddr plen; - - if (i == ASPEED_HACE_MAX_SG) { + for (iov_idx = 0; !(len & SG_LIST_LEN_LAST); iov_idx++) { + if (iov_idx == ASPEED_HACE_MAX_SG) { qemu_log_mask(LOG_GUEST_ERROR, "aspeed_hace: guest failed to set end of sg list marker\n"); break; } - src = s->regs[R_HASH_SRC] + (i * SG_LIST_ENTRY_SIZE); + src = s->regs[R_HASH_SRC] + (iov_idx * SG_LIST_ENTRY_SIZE); len = address_space_ldl_le(&s->dram_as, src, MEMTXATTRS_UNSPECIFIED, NULL); - addr = address_space_ldl_le(&s->dram_as, src + SG_LIST_LEN_SIZE, - MEMTXATTRS_UNSPECIFIED, NULL); - addr &= SG_LIST_ADDR_MASK; + sg_addr = address_space_ldl_le(&s->dram_as, src + SG_LIST_LEN_SIZE, + MEMTXATTRS_UNSPECIFIED, NULL); + sg_addr &= SG_LIST_ADDR_MASK; plen = len & SG_LIST_LEN_MASK; - haddr = address_space_map(&s->dram_as, addr, &plen, false, + haddr = address_space_map(&s->dram_as, sg_addr, &plen, false, MEMTXATTRS_UNSPECIFIED); if (haddr == NULL) { qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto failed\n", __func__); return; } - iov[i].iov_base = haddr; + iov[iov_idx].iov_base = haddr; if (acc_mode) { s->total_req_len += plen; - if (has_padding(s, &iov[i], plen, &total_msg_len, + if (has_padding(s, &iov[iov_idx], plen, &total_msg_len, &pad_offset)) { /* Padding being present indicates the final request */ - sg_acc_mode_final_request = true; - iov[i].iov_len = pad_offset; + acc_final_request = true; + iov[iov_idx].iov_len = pad_offset; } else { - iov[i].iov_len = plen; + iov[iov_idx].iov_len = plen; } } else { - iov[i].iov_len = plen; + iov[iov_idx].iov_len = plen; } } } else { - hwaddr len = s->regs[R_HASH_SRC_LEN]; + plen = s->regs[R_HASH_SRC_LEN]; haddr = address_space_map(&s->dram_as, s->regs[R_HASH_SRC], - &len, false, MEMTXATTRS_UNSPECIFIED); + &plen, false, MEMTXATTRS_UNSPECIFIED); if (haddr == NULL) { qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto failed\n", __func__); return; } iov[0].iov_base = haddr; - iov[0].iov_len = len; - i = 1; + iov[0].iov_len = plen; + iov_idx = 1; } if (acc_mode) { - if (qcrypto_hash_updatev(s->hash_ctx, iov, i, &local_err) < 0) { + if (qcrypto_hash_updatev(s->hash_ctx, iov, iov_idx, &local_err) < 0) { qemu_log_mask(LOG_GUEST_ERROR, "qcrypto hash update failed : %s", error_get_pretty(local_err)); error_free(local_err); return; } - if (sg_acc_mode_final_request) { + if (acc_final_request) { if (qcrypto_hash_finalize_bytes(s->hash_ctx, &digest_buf, &digest_len, &local_err)) { qemu_log_mask(LOG_GUEST_ERROR, @@ -248,7 +247,7 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, s->hash_ctx = NULL; s->total_req_len = 0; } - } else if (qcrypto_hash_bytesv(algo, iov, i, &digest_buf, + } else if (qcrypto_hash_bytesv(algo, iov, iov_idx, &digest_buf, &digest_len, &local_err) < 0) { qemu_log_mask(LOG_GUEST_ERROR, "qcrypto hash bytesv failed : %s", error_get_pretty(local_err)); @@ -263,10 +262,10 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, "aspeed_hace: address space write failed\n"); } - for (; i > 0; i--) { - address_space_unmap(&s->dram_as, iov[i - 1].iov_base, - iov[i - 1].iov_len, false, - iov[i - 1].iov_len); + for (; iov_idx > 0; iov_idx--) { + address_space_unmap(&s->dram_as, iov[iov_idx - 1].iov_base, + iov[iov_idx - 1].iov_len, false, + iov[iov_idx - 1].iov_len); } /* From fb8e59abbe46957cd599bb9aa9221fad1e4e989e Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:35 +0800 Subject: [PATCH 1125/2760] hw/misc/aspeed_hace: Ensure HASH_IRQ is always set to prevent firmware hang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, if the program encounters an unsupported algorithm, it does not set the HASH_IRQ bit in the status register and send an interrupt to indicate command completion. As a result, the FW gets stuck waiting for a completion signal from the HACE module. Additionally, in do_hash_operation, if an error occurs within the conditional statement, the HASH_IRQ bit is not set in the status register. This causes the firmware to continuously send HASH commands, as it is unaware that the HACE model has completed processing the command. To fix this, the HASH_IRQ bit in the status register must always be set to ensure that the firmware receives an interrupt from the HACE module, preventing it from getting stuck or repeatedly sending HASH commands. Signed-off-by: Jamin Lin Fixes: c5475b3 ("hw: Model ASPEED's Hash and Crypto Engine") Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 6be94963bc..1256926d22 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -267,12 +267,6 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, iov[iov_idx - 1].iov_len, false, iov[iov_idx - 1].iov_len); } - - /* - * Set status bits to indicate completion. Testing shows hardware sets - * these irrespective of HASH_IRQ_EN. - */ - s->regs[R_STATUS] |= HASH_IRQ; } static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size) @@ -356,10 +350,16 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid hash algorithm selection 0x%"PRIx64"\n", __func__, data & ahc->hash_mask); - break; + } else { + do_hash_operation(s, algo, data & HASH_SG_EN, + ((data & HASH_HMAC_MASK) == HASH_DIGEST_ACCUM)); } - do_hash_operation(s, algo, data & HASH_SG_EN, - ((data & HASH_HMAC_MASK) == HASH_DIGEST_ACCUM)); + + /* + * Set status bits to indicate completion. Testing shows hardware sets + * these irrespective of HASH_IRQ_EN. + */ + s->regs[R_STATUS] |= HASH_IRQ; if (data & HASH_IRQ_EN) { qemu_irq_raise(s->irq); From 7328c48b57c97e13863fdc3a76d6d9c8fe07d6ae Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:36 +0800 Subject: [PATCH 1126/2760] hw/misc/aspeed_hace: Extract direct mode hash buffer setup into helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To improve code readability and maintainability of do_hash_operation(), this commit introduces a new helper function: hash_prepare_direct_iov(). This function encapsulates the logic for setting up the I/O vector (iov) in direct mode (non-scatter-gather). No functional changes are introduced. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-5-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 42 ++++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 1256926d22..42c6f29f82 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -142,6 +142,31 @@ static bool has_padding(AspeedHACEState *s, struct iovec *iov, return false; } +static int hash_prepare_direct_iov(AspeedHACEState *s, struct iovec *iov) +{ + uint32_t src; + void *haddr; + hwaddr plen; + int iov_idx; + + plen = s->regs[R_HASH_SRC_LEN]; + src = s->regs[R_HASH_SRC]; + haddr = address_space_map(&s->dram_as, src, &plen, false, + MEMTXATTRS_UNSPECIFIED); + if (haddr == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unable to map address, addr=0x%x, " + "plen=0x%" HWADDR_PRIx "\n", + __func__, src, plen); + return -1; + } + + iov[0].iov_base = haddr; + iov[0].iov_len = plen; + iov_idx = 1; + + return iov_idx; +} static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, bool acc_mode) { @@ -169,6 +194,7 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, } } + /* Prepares the iov for hashing operations based on the selected mode */ if (sg_mode) { for (iov_idx = 0; !(len & SG_LIST_LEN_LAST); iov_idx++) { if (iov_idx == ASPEED_HACE_MAX_SG) { @@ -211,17 +237,13 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, } } } else { - plen = s->regs[R_HASH_SRC_LEN]; + iov_idx = hash_prepare_direct_iov(s, iov); + } - haddr = address_space_map(&s->dram_as, s->regs[R_HASH_SRC], - &plen, false, MEMTXATTRS_UNSPECIFIED); - if (haddr == NULL) { - qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto failed\n", __func__); - return; - } - iov[0].iov_base = haddr; - iov[0].iov_len = plen; - iov_idx = 1; + if (iov_idx <= 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to prepare iov\n", __func__); + return; } if (acc_mode) { From 0b7dd5f9910c41d9a3faa51001b018a06f0c3ac7 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:37 +0800 Subject: [PATCH 1127/2760] hw/misc/aspeed_hace: Extract SG-mode hash buffer setup into helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To improve code readability and maintainability of do_hash_operation(), this commit introduces a new helper function: hash_prepare_sg_iov(). This function handles scatter-gather (SG) mode setup, including SG list parsing, address mapping, and optional accumulation mode support with padding detection. No functional changes are introduced. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-6-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 111 ++++++++++++++++++++++++------------------ 1 file changed, 63 insertions(+), 48 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 42c6f29f82..22eea62693 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -167,6 +167,67 @@ static int hash_prepare_direct_iov(AspeedHACEState *s, struct iovec *iov) return iov_idx; } + +static int hash_prepare_sg_iov(AspeedHACEState *s, struct iovec *iov, + bool acc_mode, bool *acc_final_request) +{ + uint32_t total_msg_len; + uint32_t pad_offset; + uint32_t len = 0; + uint32_t sg_addr; + uint32_t src; + int iov_idx; + hwaddr plen; + void *haddr; + + for (iov_idx = 0; !(len & SG_LIST_LEN_LAST); iov_idx++) { + if (iov_idx == ASPEED_HACE_MAX_SG) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to set end of sg list marker\n", + __func__); + return -1; + } + + src = s->regs[R_HASH_SRC] + (iov_idx * SG_LIST_ENTRY_SIZE); + + len = address_space_ldl_le(&s->dram_as, src, + MEMTXATTRS_UNSPECIFIED, NULL); + sg_addr = address_space_ldl_le(&s->dram_as, src + SG_LIST_LEN_SIZE, + MEMTXATTRS_UNSPECIFIED, NULL); + sg_addr &= SG_LIST_ADDR_MASK; + + plen = len & SG_LIST_LEN_MASK; + haddr = address_space_map(&s->dram_as, sg_addr, &plen, false, + MEMTXATTRS_UNSPECIFIED); + + if (haddr == NULL) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Unable to map address, sg_addr=0x%x, " + "plen=0x%" HWADDR_PRIx "\n", + __func__, sg_addr, plen); + return -1; + } + + iov[iov_idx].iov_base = haddr; + if (acc_mode) { + s->total_req_len += plen; + + if (has_padding(s, &iov[iov_idx], plen, &total_msg_len, + &pad_offset)) { + /* Padding being present indicates the final request */ + *acc_final_request = true; + iov[iov_idx].iov_len = pad_offset; + } else { + iov[iov_idx].iov_len = plen; + } + } else { + iov[iov_idx].iov_len = plen; + } + } + + return iov_idx; +} + static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, bool acc_mode) { @@ -174,15 +235,8 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, struct iovec iov[ASPEED_HACE_MAX_SG]; bool acc_final_request = false; Error *local_err = NULL; - uint32_t total_msg_len; size_t digest_len = 0; - uint32_t sg_addr = 0; - uint32_t pad_offset; - int iov_idx = 0; - uint32_t len = 0; - uint32_t src = 0; - void *haddr; - hwaddr plen; + int iov_idx = -1; if (acc_mode && s->hash_ctx == NULL) { s->hash_ctx = qcrypto_hash_new(algo, &local_err); @@ -196,46 +250,7 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, /* Prepares the iov for hashing operations based on the selected mode */ if (sg_mode) { - for (iov_idx = 0; !(len & SG_LIST_LEN_LAST); iov_idx++) { - if (iov_idx == ASPEED_HACE_MAX_SG) { - qemu_log_mask(LOG_GUEST_ERROR, - "aspeed_hace: guest failed to set end of sg list marker\n"); - break; - } - - src = s->regs[R_HASH_SRC] + (iov_idx * SG_LIST_ENTRY_SIZE); - - len = address_space_ldl_le(&s->dram_as, src, - MEMTXATTRS_UNSPECIFIED, NULL); - - sg_addr = address_space_ldl_le(&s->dram_as, src + SG_LIST_LEN_SIZE, - MEMTXATTRS_UNSPECIFIED, NULL); - sg_addr &= SG_LIST_ADDR_MASK; - - plen = len & SG_LIST_LEN_MASK; - haddr = address_space_map(&s->dram_as, sg_addr, &plen, false, - MEMTXATTRS_UNSPECIFIED); - if (haddr == NULL) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: qcrypto failed\n", __func__); - return; - } - iov[iov_idx].iov_base = haddr; - if (acc_mode) { - s->total_req_len += plen; - - if (has_padding(s, &iov[iov_idx], plen, &total_msg_len, - &pad_offset)) { - /* Padding being present indicates the final request */ - acc_final_request = true; - iov[iov_idx].iov_len = pad_offset; - } else { - iov[iov_idx].iov_len = plen; - } - } else { - iov[iov_idx].iov_len = plen; - } - } + iov_idx = hash_prepare_sg_iov(s, iov, acc_mode, &acc_final_request); } else { iov_idx = hash_prepare_direct_iov(s, iov); } From 047941978508e68215d01cc0e3e5408c430e9e9f Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:38 +0800 Subject: [PATCH 1128/2760] hw/misc/aspeed_hace: Extract digest write and iov unmap into helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To improve code readability and maintainability of do_hash_operation(), this commit introduces a new helper function: hash_write_digest_and_unmap_iov(). The helper consolidates the final digest writeback and subsequent unmapping of the I/O vectors into a single routine. No functional changes are introduced. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-7-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 22eea62693..7da781f864 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -228,6 +228,26 @@ static int hash_prepare_sg_iov(AspeedHACEState *s, struct iovec *iov, return iov_idx; } +static void hash_write_digest_and_unmap_iov(AspeedHACEState *s, + struct iovec *iov, + int iov_idx, + uint8_t *digest_buf, + size_t digest_len) +{ + if (address_space_write(&s->dram_as, s->regs[R_HASH_DEST], + MEMTXATTRS_UNSPECIFIED, digest_buf, digest_len)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Failed to write digest to 0x%x\n", + __func__, s->regs[R_HASH_DEST]); + } + + for (; iov_idx > 0; iov_idx--) { + address_space_unmap(&s->dram_as, iov[iov_idx - 1].iov_base, + iov[iov_idx - 1].iov_len, false, + iov[iov_idx - 1].iov_len); + } +} + static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, bool acc_mode) { @@ -292,18 +312,7 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, return; } - if (address_space_write(&s->dram_as, s->regs[R_HASH_DEST], - MEMTXATTRS_UNSPECIFIED, - digest_buf, digest_len)) { - qemu_log_mask(LOG_GUEST_ERROR, - "aspeed_hace: address space write failed\n"); - } - - for (; iov_idx > 0; iov_idx--) { - address_space_unmap(&s->dram_as, iov[iov_idx - 1].iov_base, - iov[iov_idx - 1].iov_len, false, - iov[iov_idx - 1].iov_len); - } + hash_write_digest_and_unmap_iov(s, iov, iov_idx, digest_buf, digest_len); } static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size) From 02c4c448460e1370f767c56749a1756a5c4dcc3a Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:39 +0800 Subject: [PATCH 1129/2760] hw/misc/aspeed_hace: Extract non-accumulation hash execution into helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To improve code readability and maintainability of do_hash_operation(), this commit introduces a new helper function: hash_execute_non_acc_mode(). The helper encapsulate the hashing logic for non-accumulation mode. No functional changes are introduced. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-8-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 7da781f864..c50e228cdf 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -248,6 +248,25 @@ static void hash_write_digest_and_unmap_iov(AspeedHACEState *s, } } +static void hash_execute_non_acc_mode(AspeedHACEState *s, int algo, + struct iovec *iov, int iov_idx) +{ + g_autofree uint8_t *digest_buf = NULL; + Error *local_err = NULL; + size_t digest_len = 0; + + if (qcrypto_hash_bytesv(algo, iov, iov_idx, &digest_buf, + &digest_len, &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: qcrypto hash bytesv failed : %s", + __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + + hash_write_digest_and_unmap_iov(s, iov, iov_idx, digest_buf, digest_len); +} + static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, bool acc_mode) { @@ -304,15 +323,12 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, s->hash_ctx = NULL; s->total_req_len = 0; } - } else if (qcrypto_hash_bytesv(algo, iov, iov_idx, &digest_buf, - &digest_len, &local_err) < 0) { - qemu_log_mask(LOG_GUEST_ERROR, "qcrypto hash bytesv failed : %s", - error_get_pretty(local_err)); - error_free(local_err); - return; - } - hash_write_digest_and_unmap_iov(s, iov, iov_idx, digest_buf, digest_len); + hash_write_digest_and_unmap_iov(s, iov, iov_idx, digest_buf, + digest_len); + } else { + hash_execute_non_acc_mode(s, algo, iov, iov_idx); + } } static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size) From b9ccbe212e2443294dd636cb17b4e436db8774a7 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:40 +0800 Subject: [PATCH 1130/2760] hw/misc/aspeed_hace: Extract accumulation-mode hash execution into helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To improve code readability and maintainability of do_hash_operation(), this commit introduces a new helper function: hash_execute_acc_mode(). This function encapsulates the full flow for accumulation mode, including context initialization, update, conditional finalization, and digest writeback with I/O vector unmapping. No functional changes are introduced. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-9-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 74 ++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 33 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index c50e228cdf..33e13974fe 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -267,26 +267,57 @@ static void hash_execute_non_acc_mode(AspeedHACEState *s, int algo, hash_write_digest_and_unmap_iov(s, iov, iov_idx, digest_buf, digest_len); } -static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, - bool acc_mode) +static void hash_execute_acc_mode(AspeedHACEState *s, int algo, + struct iovec *iov, int iov_idx, + bool final_request) { g_autofree uint8_t *digest_buf = NULL; - struct iovec iov[ASPEED_HACE_MAX_SG]; - bool acc_final_request = false; Error *local_err = NULL; size_t digest_len = 0; - int iov_idx = -1; - if (acc_mode && s->hash_ctx == NULL) { + if (s->hash_ctx == NULL) { s->hash_ctx = qcrypto_hash_new(algo, &local_err); if (s->hash_ctx == NULL) { - qemu_log_mask(LOG_GUEST_ERROR, "qcrypto hash failed : %s", - error_get_pretty(local_err)); + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto hash new failed : %s", + __func__, error_get_pretty(local_err)); error_free(local_err); return; } } + if (qcrypto_hash_updatev(s->hash_ctx, iov, iov_idx, &local_err) < 0) { + qemu_log_mask(LOG_GUEST_ERROR, "%s: qcrypto hash updatev failed : %s", + __func__, error_get_pretty(local_err)); + error_free(local_err); + return; + } + + if (final_request) { + if (qcrypto_hash_finalize_bytes(s->hash_ctx, &digest_buf, + &digest_len, &local_err)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: qcrypto hash finalize bytes failed : %s", + __func__, error_get_pretty(local_err)); + error_free(local_err); + local_err = NULL; + } + + qcrypto_hash_free(s->hash_ctx); + + s->hash_ctx = NULL; + s->total_req_len = 0; + } + + hash_write_digest_and_unmap_iov(s, iov, iov_idx, digest_buf, digest_len); +} + +static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, + bool acc_mode) +{ + struct iovec iov[ASPEED_HACE_MAX_SG]; + bool acc_final_request = false; + int iov_idx = -1; + /* Prepares the iov for hashing operations based on the selected mode */ if (sg_mode) { iov_idx = hash_prepare_sg_iov(s, iov, acc_mode, &acc_final_request); @@ -300,32 +331,9 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, return; } + /* Executes the hash operation */ if (acc_mode) { - if (qcrypto_hash_updatev(s->hash_ctx, iov, iov_idx, &local_err) < 0) { - qemu_log_mask(LOG_GUEST_ERROR, "qcrypto hash update failed : %s", - error_get_pretty(local_err)); - error_free(local_err); - return; - } - - if (acc_final_request) { - if (qcrypto_hash_finalize_bytes(s->hash_ctx, &digest_buf, - &digest_len, &local_err)) { - qemu_log_mask(LOG_GUEST_ERROR, - "qcrypto hash finalize failed : %s", - error_get_pretty(local_err)); - error_free(local_err); - local_err = NULL; - } - - qcrypto_hash_free(s->hash_ctx); - - s->hash_ctx = NULL; - s->total_req_len = 0; - } - - hash_write_digest_and_unmap_iov(s, iov, iov_idx, digest_buf, - digest_len); + hash_execute_acc_mode(s, algo, iov, iov_idx, acc_final_request); } else { hash_execute_non_acc_mode(s, algo, iov, iov_idx); } From c6c5fc5ab04533a22be11f377aa67d46e47056bf Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:41 +0800 Subject: [PATCH 1131/2760] hw/misc/aspeed_hace: Introduce 64-bit hash source address helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AST2700 CPU, based on the Cortex-A35, is a 64-bit processor, and its DRAM address space is also 64-bit. To support future AST2700 updates, the source hash buffer address data type is being updated to 64-bit. Introduces the "hash_get_source_addr()" helper function to extract the source hash buffer address. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-10-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 33e13974fe..b3c3af51fa 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -142,21 +142,30 @@ static bool has_padding(AspeedHACEState *s, struct iovec *iov, return false; } +static uint64_t hash_get_source_addr(AspeedHACEState *s) +{ + uint64_t src_addr = 0; + + src_addr = deposit64(src_addr, 0, 32, s->regs[R_HASH_SRC]); + + return src_addr; +} + static int hash_prepare_direct_iov(AspeedHACEState *s, struct iovec *iov) { - uint32_t src; + uint64_t src; void *haddr; hwaddr plen; int iov_idx; plen = s->regs[R_HASH_SRC_LEN]; - src = s->regs[R_HASH_SRC]; + src = hash_get_source_addr(s); haddr = address_space_map(&s->dram_as, src, &plen, false, MEMTXATTRS_UNSPECIFIED); if (haddr == NULL) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: Unable to map address, addr=0x%x, " - "plen=0x%" HWADDR_PRIx "\n", + "%s: Unable to map address, addr=0x%" HWADDR_PRIx + " ,plen=0x%" HWADDR_PRIx "\n", __func__, src, plen); return -1; } @@ -175,11 +184,12 @@ static int hash_prepare_sg_iov(AspeedHACEState *s, struct iovec *iov, uint32_t pad_offset; uint32_t len = 0; uint32_t sg_addr; - uint32_t src; + uint64_t src; int iov_idx; hwaddr plen; void *haddr; + src = hash_get_source_addr(s); for (iov_idx = 0; !(len & SG_LIST_LEN_LAST); iov_idx++) { if (iov_idx == ASPEED_HACE_MAX_SG) { qemu_log_mask(LOG_GUEST_ERROR, @@ -188,8 +198,6 @@ static int hash_prepare_sg_iov(AspeedHACEState *s, struct iovec *iov, return -1; } - src = s->regs[R_HASH_SRC] + (iov_idx * SG_LIST_ENTRY_SIZE); - len = address_space_ldl_le(&s->dram_as, src, MEMTXATTRS_UNSPECIFIED, NULL); sg_addr = address_space_ldl_le(&s->dram_as, src + SG_LIST_LEN_SIZE, @@ -208,6 +216,8 @@ static int hash_prepare_sg_iov(AspeedHACEState *s, struct iovec *iov, return -1; } + src += SG_LIST_ENTRY_SIZE; + iov[iov_idx].iov_base = haddr; if (acc_mode) { s->total_req_len += plen; From 973fab3b3099aa5f6a4510c4da03056d6baf09a7 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:42 +0800 Subject: [PATCH 1132/2760] hw/misc/aspeed_hace: Rename R_HASH_DEST to R_HASH_DIGEST and introduce 64-bit hash digest address helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Renaming R_HASH_DEST to R_HASH_DIGEST for better semantic clarity. The AST2700 CPU, based on the Cortex-A35, features a 64-bit DRAM address space. To prepare for future AST2700 support, this change introduces a new helper function hash_get_digest_addr() to encapsulate digest address extraction logic and improve code readability. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-11-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index b3c3af51fa..62649b5b27 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -27,7 +27,7 @@ #define TAG_IRQ BIT(15) #define R_HASH_SRC (0x20 / 4) -#define R_HASH_DEST (0x24 / 4) +#define R_HASH_DIGEST (0x24 / 4) #define R_HASH_KEY_BUFF (0x28 / 4) #define R_HASH_SRC_LEN (0x2c / 4) @@ -238,17 +238,30 @@ static int hash_prepare_sg_iov(AspeedHACEState *s, struct iovec *iov, return iov_idx; } +static uint64_t hash_get_digest_addr(AspeedHACEState *s) +{ + uint64_t digest_addr = 0; + + digest_addr = deposit64(digest_addr, 0, 32, s->regs[R_HASH_DIGEST]); + + return digest_addr; +} + static void hash_write_digest_and_unmap_iov(AspeedHACEState *s, struct iovec *iov, int iov_idx, uint8_t *digest_buf, size_t digest_len) { - if (address_space_write(&s->dram_as, s->regs[R_HASH_DEST], - MEMTXATTRS_UNSPECIFIED, digest_buf, digest_len)) { + uint64_t digest_addr = 0; + + digest_addr = hash_get_digest_addr(s); + if (address_space_write(&s->dram_as, digest_addr, + MEMTXATTRS_UNSPECIFIED, + digest_buf, digest_len)) { qemu_log_mask(LOG_GUEST_ERROR, - "%s: Failed to write digest to 0x%x\n", - __func__, s->regs[R_HASH_DEST]); + "%s: Failed to write digest to 0x%" HWADDR_PRIx "\n", + __func__, digest_addr); } for (; iov_idx > 0; iov_idx--) { @@ -402,7 +415,7 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, case R_HASH_SRC: data &= ahc->src_mask; break; - case R_HASH_DEST: + case R_HASH_DIGEST: data &= ahc->dest_mask; break; case R_HASH_KEY_BUFF: From 7b4e588000699701f5906746d1b5b845391705e6 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:43 +0800 Subject: [PATCH 1133/2760] hw/misc/aspeed_hace: Support accumulative mode for direct access mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable accumulative mode for direct access mode operations. In direct access mode, only a single source buffer is used, so the "iovec" count is set to 1. If "acc_mode" is enabled: 1. Accumulate "total_req_len" with the current request length ("plen"). 2. Check for padding and determine whether this is the final request. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-12-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 62649b5b27..049f732f99 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -151,8 +151,11 @@ static uint64_t hash_get_source_addr(AspeedHACEState *s) return src_addr; } -static int hash_prepare_direct_iov(AspeedHACEState *s, struct iovec *iov) +static int hash_prepare_direct_iov(AspeedHACEState *s, struct iovec *iov, + bool acc_mode, bool *acc_final_request) { + uint32_t total_msg_len; + uint32_t pad_offset; uint64_t src; void *haddr; hwaddr plen; @@ -171,9 +174,23 @@ static int hash_prepare_direct_iov(AspeedHACEState *s, struct iovec *iov) } iov[0].iov_base = haddr; - iov[0].iov_len = plen; iov_idx = 1; + if (acc_mode) { + s->total_req_len += plen; + + if (has_padding(s, &iov[0], plen, &total_msg_len, + &pad_offset)) { + /* Padding being present indicates the final request */ + *acc_final_request = true; + iov[0].iov_len = pad_offset; + } else { + iov[0].iov_len = plen; + } + } else { + iov[0].iov_len = plen; + } + return iov_idx; } @@ -345,7 +362,8 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, if (sg_mode) { iov_idx = hash_prepare_sg_iov(s, iov, acc_mode, &acc_final_request); } else { - iov_idx = hash_prepare_direct_iov(s, iov); + iov_idx = hash_prepare_direct_iov(s, iov, acc_mode, + &acc_final_request); } if (iov_idx <= 0) { From 89d2a9f3f7564c9421d61153bbf7e24af95d34ee Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:44 +0800 Subject: [PATCH 1134/2760] hw/misc/aspeed_hace: Move register size to instance class and dynamically allocate regs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dynamically allocate the register array by removing the hardcoded ASPEED_HACE_NR_REGS macro. To support different register sizes across SoC variants, introduce a new "nr_regs" class attribute and replace the static "regs" array with dynamically allocated memory. Add a new "aspeed_hace_unrealize" function to properly free the allocated "regs" memory during device cleanup. Remove the bounds checking in the MMIO read/write handlers since the MemoryRegion size now matches the (register array size << 2). This commit updates the VMState fields accordingly. The VMState version was already bumped in a previous patch of this series, so no further version change is needed. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-13-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 36 ++++++++++++++++++----------------- include/hw/misc/aspeed_hace.h | 5 +++-- 2 files changed, 22 insertions(+), 19 deletions(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 049f732f99..fef63eb488 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -386,13 +386,6 @@ static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size) addr >>= 2; - if (addr >= ASPEED_HACE_NR_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Out-of-bounds read at offset 0x%" HWADDR_PRIx "\n", - __func__, addr << 2); - return 0; - } - return s->regs[addr]; } @@ -404,13 +397,6 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, addr >>= 2; - if (addr >= ASPEED_HACE_NR_REGS) { - qemu_log_mask(LOG_GUEST_ERROR, - "%s: Out-of-bounds write at offset 0x%" HWADDR_PRIx "\n", - __func__, addr << 2); - return; - } - switch (addr) { case R_STATUS: if (data & HASH_IRQ) { @@ -507,13 +493,14 @@ static const MemoryRegionOps aspeed_hace_ops = { static void aspeed_hace_reset(DeviceState *dev) { struct AspeedHACEState *s = ASPEED_HACE(dev); + AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s); if (s->hash_ctx != NULL) { qcrypto_hash_free(s->hash_ctx); s->hash_ctx = NULL; } - memset(s->regs, 0, sizeof(s->regs)); + memset(s->regs, 0, ahc->nr_regs << 2); s->total_req_len = 0; } @@ -521,11 +508,13 @@ static void aspeed_hace_realize(DeviceState *dev, Error **errp) { AspeedHACEState *s = ASPEED_HACE(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s); sysbus_init_irq(sbd, &s->irq); + s->regs = g_new(uint32_t, ahc->nr_regs); memory_region_init_io(&s->iomem, OBJECT(s), &aspeed_hace_ops, s, - TYPE_ASPEED_HACE, 0x1000); + TYPE_ASPEED_HACE, ahc->nr_regs << 2); if (!s->dram_mr) { error_setg(errp, TYPE_ASPEED_HACE ": 'dram' link not set"); @@ -548,17 +537,25 @@ static const VMStateDescription vmstate_aspeed_hace = { .version_id = 2, .minimum_version_id = 2, .fields = (const VMStateField[]) { - VMSTATE_UINT32_ARRAY(regs, AspeedHACEState, ASPEED_HACE_NR_REGS), VMSTATE_UINT32(total_req_len, AspeedHACEState), VMSTATE_END_OF_LIST(), } }; +static void aspeed_hace_unrealize(DeviceState *dev) +{ + AspeedHACEState *s = ASPEED_HACE(dev); + + g_free(s->regs); + s->regs = NULL; +} + static void aspeed_hace_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); dc->realize = aspeed_hace_realize; + dc->unrealize = aspeed_hace_unrealize; device_class_set_legacy_reset(dc, aspeed_hace_reset); device_class_set_props(dc, aspeed_hace_properties); dc->vmsd = &vmstate_aspeed_hace; @@ -579,6 +576,7 @@ static void aspeed_ast2400_hace_class_init(ObjectClass *klass, const void *data) dc->desc = "AST2400 Hash and Crypto Engine"; + ahc->nr_regs = 0x64 >> 2; ahc->src_mask = 0x0FFFFFFF; ahc->dest_mask = 0x0FFFFFF8; ahc->key_mask = 0x0FFFFFC0; @@ -598,6 +596,7 @@ static void aspeed_ast2500_hace_class_init(ObjectClass *klass, const void *data) dc->desc = "AST2500 Hash and Crypto Engine"; + ahc->nr_regs = 0x64 >> 2; ahc->src_mask = 0x3fffffff; ahc->dest_mask = 0x3ffffff8; ahc->key_mask = 0x3FFFFFC0; @@ -617,6 +616,7 @@ static void aspeed_ast2600_hace_class_init(ObjectClass *klass, const void *data) dc->desc = "AST2600 Hash and Crypto Engine"; + ahc->nr_regs = 0x64 >> 2; ahc->src_mask = 0x7FFFFFFF; ahc->dest_mask = 0x7FFFFFF8; ahc->key_mask = 0x7FFFFFF8; @@ -636,6 +636,7 @@ static void aspeed_ast1030_hace_class_init(ObjectClass *klass, const void *data) dc->desc = "AST1030 Hash and Crypto Engine"; + ahc->nr_regs = 0x64 >> 2; ahc->src_mask = 0x7FFFFFFF; ahc->dest_mask = 0x7FFFFFF8; ahc->key_mask = 0x7FFFFFF8; @@ -655,6 +656,7 @@ static void aspeed_ast2700_hace_class_init(ObjectClass *klass, const void *data) dc->desc = "AST2700 Hash and Crypto Engine"; + ahc->nr_regs = 0x64 >> 2; ahc->src_mask = 0x7FFFFFFF; ahc->dest_mask = 0x7FFFFFF8; ahc->key_mask = 0x7FFFFFF8; diff --git a/include/hw/misc/aspeed_hace.h b/include/hw/misc/aspeed_hace.h index b69a038d35..f30d606559 100644 --- a/include/hw/misc/aspeed_hace.h +++ b/include/hw/misc/aspeed_hace.h @@ -22,7 +22,6 @@ OBJECT_DECLARE_TYPE(AspeedHACEState, AspeedHACEClass, ASPEED_HACE) -#define ASPEED_HACE_NR_REGS (0x64 >> 2) #define ASPEED_HACE_MAX_SG 256 /* max number of entries */ struct AspeedHACEState { @@ -31,7 +30,7 @@ struct AspeedHACEState { MemoryRegion iomem; qemu_irq irq; - uint32_t regs[ASPEED_HACE_NR_REGS]; + uint32_t *regs; uint32_t total_req_len; MemoryRegion *dram_mr; @@ -44,10 +43,12 @@ struct AspeedHACEState { struct AspeedHACEClass { SysBusDeviceClass parent_class; + const MemoryRegionOps *reg_ops; uint32_t src_mask; uint32_t dest_mask; uint32_t key_mask; uint32_t hash_mask; + uint64_t nr_regs; bool raise_crypt_interrupt_workaround; }; From 6262c8addc8ed586dfa5f11606f1598fca45b3eb Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:45 +0800 Subject: [PATCH 1135/2760] hw/misc/aspeed_hace: Add support for source, digest, key buffer 64 bit addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the AST2700 design, the data source address is 64-bit, with R_HASH_SRC_HI storing bits [63:32] and R_HASH_SRC storing bits [31:0]. Similarly, the digest address is 64-bit, with R_HASH_DIGEST_HI storing bits [63:32] and R_HASH_DIGEST storing bits [31:0]. The HMAC key buffer address is also 64-bit, with R_HASH_KEY_BUFF_HI storing bits [63:32] and R_HASH_KEY_BUFF storing bits [31:0]. The AST2700 supports a maximum DRAM size of 8 GB, with a DRAM addressable range from 0x0_0000_0000 to 0x1_FFFF_FFFF. Since this range fits within 34 bits, only bits [33:0] are needed to store the DRAM offset. To optimize address storage, the high physical address bits [1:0] of the source, digest and key buffer addresses are stored as dram_offset bits [33:32]. To achieve this, a src_hi_mask with a mask value of 0x3 is introduced, ensuring that src_addr_hi consists of bits [1:0]. The final src_addr is computed as (src_addr_hi[1:0] << 32) | src_addr[31:0], representing the DRAM offset within bits [33:0]. Similarly, a dest_hi_mask with a mask value of 0x3 is introduced to ensure that dest_addr_hi consists of bits [1:0]. The final dest_addr is calculated as (dest_addr_hi[1:0] << 32) | dest_addr[31:0], representing the DRAM offset within bits [33:0]. Additionally, a key_hi_mask with a mask value of 0x3 is introduced to ensure that key_buf_addr_hi consists of bits [1:0]. The final key_buf_addr is determined as (key_buf_addr_hi[1:0] << 32) | key_buf_addr[31:0], representing the DRAM offset within bits [33:0]. This approach eliminates the need to reduce the high part of the DRAM physical address for DMA operations. Previously, this was calculated as (high physical address bits [7:0] - 4), since the DRAM start address is 0x4_00000000, making the high part address [7:0] - 4. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-14-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 31 ++++++++++++++++++++++++++++++- include/hw/misc/aspeed_hace.h | 3 +++ 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index fef63eb488..d58645cabd 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -30,6 +30,9 @@ #define R_HASH_DIGEST (0x24 / 4) #define R_HASH_KEY_BUFF (0x28 / 4) #define R_HASH_SRC_LEN (0x2c / 4) +#define R_HASH_SRC_HI (0x90 / 4) +#define R_HASH_DIGEST_HI (0x94 / 4) +#define R_HASH_KEY_BUFF_HI (0x98 / 4) #define R_HASH_CMD (0x30 / 4) /* Hash algorithm selection */ @@ -473,6 +476,15 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, } } break; + case R_HASH_SRC_HI: + data &= ahc->src_hi_mask; + break; + case R_HASH_DIGEST_HI: + data &= ahc->dest_hi_mask; + break; + case R_HASH_KEY_BUFF_HI: + data &= ahc->key_hi_mask; + break; default: break; } @@ -656,12 +668,29 @@ static void aspeed_ast2700_hace_class_init(ObjectClass *klass, const void *data) dc->desc = "AST2700 Hash and Crypto Engine"; - ahc->nr_regs = 0x64 >> 2; + ahc->nr_regs = 0x9C >> 2; ahc->src_mask = 0x7FFFFFFF; ahc->dest_mask = 0x7FFFFFF8; ahc->key_mask = 0x7FFFFFF8; ahc->hash_mask = 0x00147FFF; + /* + * The AST2700 supports a maximum DRAM size of 8 GB, with a DRAM + * addressable range from 0x0_0000_0000 to 0x1_FFFF_FFFF. Since this range + * fits within 34 bits, only bits [33:0] are needed to store the DRAM + * offset. To optimize address storage, the high physical address bits + * [1:0] of the source, digest and key buffer addresses are stored as + * dram_offset bits [33:32]. + * + * This approach eliminates the need to reduce the high part of the DRAM + * physical address for DMA operations. Previously, this was calculated as + * (high physical address bits [7:0] - 4), since the DRAM start address is + * 0x4_00000000, making the high part address [7:0] - 4. + */ + ahc->src_hi_mask = 0x00000003; + ahc->dest_hi_mask = 0x00000003; + ahc->key_hi_mask = 0x00000003; + /* * Currently, it does not support the CRYPT command. Instead, it only * sends an interrupt to notify the firmware that the crypt command diff --git a/include/hw/misc/aspeed_hace.h b/include/hw/misc/aspeed_hace.h index f30d606559..9945b61863 100644 --- a/include/hw/misc/aspeed_hace.h +++ b/include/hw/misc/aspeed_hace.h @@ -50,6 +50,9 @@ struct AspeedHACEClass { uint32_t hash_mask; uint64_t nr_regs; bool raise_crypt_interrupt_workaround; + uint32_t src_hi_mask; + uint32_t dest_hi_mask; + uint32_t key_hi_mask; }; #endif /* ASPEED_HACE_H */ From 7e65aa39b37cb189c4d0bc923d4d778bdd626f4b Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:46 +0800 Subject: [PATCH 1136/2760] hw/misc/aspeed_hace: Support DMA 64 bits dram address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the AST2700 design, the data source address is 64-bit, with R_HASH_SRC_HI storing bits [63:32] and R_HASH_SRC storing bits [31:0]. Similarly, the digest address is 64-bit, with R_HASH_DEST_HI storing bits [63:32] and R_HASH_DEST storing bits [31:0]. To maintain compatibility with older SoCs such as the AST2600, the AST2700 HW automatically set bit 34 of the 64-bit sg_addr. As a result, the firmware only needs to provide a 32-bit sg_addr containing bits [31:0]. This is sufficient for the AST2700, as it uses a DRAM offset rather than a DRAM address. Introduce a has_dma64 class attribute and set it to true for the AST2700. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-15-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 17 ++++++++++++++++- include/hw/misc/aspeed_hace.h | 1 + 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index d58645cabd..764408716e 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -147,9 +147,13 @@ static bool has_padding(AspeedHACEState *s, struct iovec *iov, static uint64_t hash_get_source_addr(AspeedHACEState *s) { + AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s); uint64_t src_addr = 0; src_addr = deposit64(src_addr, 0, 32, s->regs[R_HASH_SRC]); + if (ahc->has_dma64) { + src_addr = deposit64(src_addr, 32, 32, s->regs[R_HASH_SRC_HI]); + } return src_addr; } @@ -223,7 +227,13 @@ static int hash_prepare_sg_iov(AspeedHACEState *s, struct iovec *iov, sg_addr = address_space_ldl_le(&s->dram_as, src + SG_LIST_LEN_SIZE, MEMTXATTRS_UNSPECIFIED, NULL); sg_addr &= SG_LIST_ADDR_MASK; - + /* + * To maintain compatibility with older SoCs such as the AST2600, + * the AST2700 HW automatically set bit 34 of the 64-bit sg_addr. + * As a result, the firmware only needs to provide a 32-bit sg_addr + * containing bits [31:0]. This is sufficient for the AST2700, as + * it uses a DRAM offset rather than a DRAM address. + */ plen = len & SG_LIST_LEN_MASK; haddr = address_space_map(&s->dram_as, sg_addr, &plen, false, MEMTXATTRS_UNSPECIFIED); @@ -260,9 +270,13 @@ static int hash_prepare_sg_iov(AspeedHACEState *s, struct iovec *iov, static uint64_t hash_get_digest_addr(AspeedHACEState *s) { + AspeedHACEClass *ahc = ASPEED_HACE_GET_CLASS(s); uint64_t digest_addr = 0; digest_addr = deposit64(digest_addr, 0, 32, s->regs[R_HASH_DIGEST]); + if (ahc->has_dma64) { + digest_addr = deposit64(digest_addr, 32, 32, s->regs[R_HASH_DIGEST_HI]); + } return digest_addr; } @@ -697,6 +711,7 @@ static void aspeed_ast2700_hace_class_init(ObjectClass *klass, const void *data) * has completed. It is a temporary workaround. */ ahc->raise_crypt_interrupt_workaround = true; + ahc->has_dma64 = true; } static const TypeInfo aspeed_ast2700_hace_info = { diff --git a/include/hw/misc/aspeed_hace.h b/include/hw/misc/aspeed_hace.h index 9945b61863..d5d07c6c02 100644 --- a/include/hw/misc/aspeed_hace.h +++ b/include/hw/misc/aspeed_hace.h @@ -53,6 +53,7 @@ struct AspeedHACEClass { uint32_t src_hi_mask; uint32_t dest_hi_mask; uint32_t key_hi_mask; + bool has_dma64; }; #endif /* ASPEED_HACE_H */ From 555167a8fde2bf6c27d0df035324743ed5bfbb0d Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:47 +0800 Subject: [PATCH 1137/2760] hw/misc/aspeed_hace: Add trace-events for better debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduced "trace_aspeed_hace_hash_addr", "trace_aspeed_hace_hash_sg", "trace_aspeed_hace_read", "trace_aspeed_hace_hash_execute_acc_mode", and "trace_aspeed_hace_write" trace events. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-16-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 10 ++++++++++ hw/misc/trace-events | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 764408716e..ee1d9ab58f 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -18,6 +18,7 @@ #include "crypto/hash.h" #include "hw/qdev-properties.h" #include "hw/irq.h" +#include "trace.h" #define R_CRYPT_CMD (0x10 / 4) @@ -170,6 +171,7 @@ static int hash_prepare_direct_iov(AspeedHACEState *s, struct iovec *iov, plen = s->regs[R_HASH_SRC_LEN]; src = hash_get_source_addr(s); + trace_aspeed_hace_hash_addr("src", src); haddr = address_space_map(&s->dram_as, src, &plen, false, MEMTXATTRS_UNSPECIFIED); if (haddr == NULL) { @@ -227,6 +229,7 @@ static int hash_prepare_sg_iov(AspeedHACEState *s, struct iovec *iov, sg_addr = address_space_ldl_le(&s->dram_as, src + SG_LIST_LEN_SIZE, MEMTXATTRS_UNSPECIFIED, NULL); sg_addr &= SG_LIST_ADDR_MASK; + trace_aspeed_hace_hash_sg(iov_idx, src, sg_addr, len); /* * To maintain compatibility with older SoCs such as the AST2600, * the AST2700 HW automatically set bit 34 of the 64-bit sg_addr. @@ -290,6 +293,7 @@ static void hash_write_digest_and_unmap_iov(AspeedHACEState *s, uint64_t digest_addr = 0; digest_addr = hash_get_digest_addr(s); + trace_aspeed_hace_hash_addr("digest", digest_addr); if (address_space_write(&s->dram_as, digest_addr, MEMTXATTRS_UNSPECIFIED, digest_buf, digest_len)) { @@ -332,6 +336,8 @@ static void hash_execute_acc_mode(AspeedHACEState *s, int algo, Error *local_err = NULL; size_t digest_len = 0; + trace_aspeed_hace_hash_execute_acc_mode(final_request); + if (s->hash_ctx == NULL) { s->hash_ctx = qcrypto_hash_new(algo, &local_err); if (s->hash_ctx == NULL) { @@ -403,6 +409,8 @@ static uint64_t aspeed_hace_read(void *opaque, hwaddr addr, unsigned int size) addr >>= 2; + trace_aspeed_hace_read(addr << 2, s->regs[addr]); + return s->regs[addr]; } @@ -414,6 +422,8 @@ static void aspeed_hace_write(void *opaque, hwaddr addr, uint64_t data, addr >>= 2; + trace_aspeed_hace_write(addr << 2, data); + switch (addr) { case R_STATUS: if (data & HASH_IRQ) { diff --git a/hw/misc/trace-events b/hw/misc/trace-events index 4383808d7a..b980d7fdd3 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -302,6 +302,13 @@ aspeed_peci_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" aspeed_peci_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 aspeed_peci_raise_interrupt(uint32_t ctrl, uint32_t status) "ctrl 0x%" PRIx32 " status 0x%" PRIx32 +# aspeed_hace.c +aspeed_hace_read(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_hace_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x%" PRIx64 +aspeed_hace_hash_sg(int index, uint64_t list_addr, uint64_t buf_addr, uint32_t len) "%d: list_addr 0x%" PRIx64 " buf_addr 0x%" PRIx64 " len 0x%" PRIx32 +aspeed_hace_hash_addr(const char *s, uint64_t addr) "%s: 0x%" PRIx64 +aspeed_hace_hash_execute_acc_mode(bool final_request) "final request: %d" + # bcm2835_property.c bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu" From 22370d29e83753e4072457541ab59189edcd3947 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:48 +0800 Subject: [PATCH 1138/2760] hw/misc/aspeed_hace: Support to dump plaintext and digest for better debugging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. Added "hace_hexdump()" to dump a contiguous buffer using qemu_hexdump. 2. Added "hace_iov_hexdump()" to flatten and dump scatter-gather source vectors. 3. Introduced a new trace event: "aspeed_hace_hexdump". Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-17-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_hace.c | 46 +++++++++++++++++++++++++++++++++++++++++++ hw/misc/trace-events | 1 + 2 files changed, 47 insertions(+) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index ee1d9ab58f..8924a30eff 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -10,8 +10,10 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qemu/log.h" #include "qemu/error-report.h" +#include "qemu/iov.h" #include "hw/misc/aspeed_hace.h" #include "qapi/error.h" #include "migration/vmstate.h" @@ -88,6 +90,42 @@ static const struct { QCRYPTO_HASH_ALGO_SHA256 }, }; +static void hace_hexdump(const char *desc, const char *buf, size_t size) +{ + g_autoptr(GString) str = g_string_sized_new(64); + size_t len; + size_t i; + + for (i = 0; i < size; i += len) { + len = MIN(16, size - i); + g_string_truncate(str, 0); + qemu_hexdump_line(str, buf + i, len, 1, 4); + trace_aspeed_hace_hexdump(desc, i, str->str); + } +} + +static void hace_iov_hexdump(const char *desc, const struct iovec *iov, + const unsigned int iov_cnt) +{ + size_t size = 0; + char *buf; + int i; + + for (i = 0; i < iov_cnt; i++) { + size += iov[i].iov_len; + } + + buf = g_malloc(size); + + if (!buf) { + return; + } + + iov_to_buf(iov, iov_cnt, 0, buf, size); + hace_hexdump(desc, buf, size); + g_free(buf); +} + static int hash_algo_lookup(uint32_t reg) { int i; @@ -302,6 +340,10 @@ static void hash_write_digest_and_unmap_iov(AspeedHACEState *s, __func__, digest_addr); } + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) { + hace_hexdump("digest", (char *)digest_buf, digest_len); + } + for (; iov_idx > 0; iov_idx--) { address_space_unmap(&s->dram_as, iov[iov_idx - 1].iov_base, iov[iov_idx - 1].iov_len, false, @@ -395,6 +437,10 @@ static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, return; } + if (trace_event_get_state_backends(TRACE_ASPEED_HACE_HEXDUMP)) { + hace_iov_hexdump("plaintext", iov, iov_idx); + } + /* Executes the hash operation */ if (acc_mode) { hash_execute_acc_mode(s, algo, iov, iov_idx, acc_final_request); diff --git a/hw/misc/trace-events b/hw/misc/trace-events index b980d7fdd3..e3f64c0ff6 100644 --- a/hw/misc/trace-events +++ b/hw/misc/trace-events @@ -308,6 +308,7 @@ aspeed_hace_write(uint64_t offset, uint64_t data) "offset 0x%" PRIx64 " data 0x% aspeed_hace_hash_sg(int index, uint64_t list_addr, uint64_t buf_addr, uint32_t len) "%d: list_addr 0x%" PRIx64 " buf_addr 0x%" PRIx64 " len 0x%" PRIx32 aspeed_hace_hash_addr(const char *s, uint64_t addr) "%s: 0x%" PRIx64 aspeed_hace_hash_execute_acc_mode(bool final_request) "final request: %d" +aspeed_hace_hexdump(const char *desc, uint32_t offset, char *s) "%s: 0x%08x: %s" # bcm2835_property.c bcm2835_mbox_property(uint32_t tag, uint32_t bufsize, size_t resplen) "mbox property tag:0x%08x in_sz:%u out_sz:%zu" From 408326af1f05776eae0f4fc91dcf643b54f9240c Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:49 +0800 Subject: [PATCH 1139/2760] tests/qtest: Reorder aspeed test list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reordered the aspeed test list to keep the alphabetical order. No functional changes in test behavior. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-18-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/meson.build | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 7daf619845..c3a24a3eb4 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -212,9 +212,9 @@ qtests_npcm7xx = \ 'npcm_gmac-test'] + \ (slirp.found() ? ['npcm7xx_emc-test'] : []) qtests_aspeed = \ - ['aspeed_hace-test', - 'aspeed_smc-test', - 'aspeed_gpio-test'] + ['aspeed_gpio-test', + 'aspeed_hace-test', + 'aspeed_smc-test'] qtests_aspeed64 = \ ['ast2700-gpio-test', 'ast2700-smc-test'] @@ -361,6 +361,8 @@ if gnutls.found() endif qtests = { + 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'), + 'ast2700-smc-test': files('aspeed-smc-utils.c', 'ast2700-smc-test.c'), 'bios-tables-test': [io, 'boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'], 'cdrom-test': files('boot-sector.c'), 'dbus-vmstate-test': files('migration/migration-qmp.c', @@ -382,8 +384,6 @@ qtests = { 'virtio-net-failover': migration_files, 'vmgenid-test': files('boot-sector.c', 'acpi-utils.c'), 'netdev-socket': files('netdev-socket.c', '../unit/socket-helpers.c'), - 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'), - 'ast2700-smc-test': files('aspeed-smc-utils.c', 'ast2700-smc-test.c'), } if vnc.found() From 70985b0ea776e0cd7d7ab2a173d9d35d31e8c21f Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:50 +0800 Subject: [PATCH 1140/2760] test/qtest: Introduce a new aspeed-hace-utils.c to place common testcases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test cases for the ASPEED HACE model were originally placed in aspeed_hace-test.c. However, this test file only supports ARM32. To enable compatibility with all ASPEED SoCs, including the AST2700, which uses the AArch64 architecture, this update introduces a new source file, aspeed-hace-utils.c. All common APIs and test cases have been moved from aspeed_hace-test.c to aspeed-hace-utils.c to facilitate reuse across different ASPEED SoCs. As a result, these test cases can now be reused for AST2700 and future ASPEED SoC testing. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-19-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-hace-utils.c | 455 ++++++++++++++++++++++++++++ tests/qtest/aspeed-hace-utils.h | 71 +++++ tests/qtest/aspeed_hace-test.c | 515 ++------------------------------ tests/qtest/meson.build | 1 + 4 files changed, 547 insertions(+), 495 deletions(-) create mode 100644 tests/qtest/aspeed-hace-utils.c create mode 100644 tests/qtest/aspeed-hace-utils.h diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c new file mode 100644 index 0000000000..8582847945 --- /dev/null +++ b/tests/qtest/aspeed-hace-utils.c @@ -0,0 +1,455 @@ +/* + * QTest testcase for the ASPEED Hash and Crypto Engine + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 IBM Corp. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" +#include "aspeed-hace-utils.h" + +/* + * Test vector is the ascii "abc" + * + * Expected results were generated using command line utitiles: + * + * echo -n -e 'abc' | dd of=/tmp/test + * for hash in sha512sum sha256sum md5sum; do $hash /tmp/test; done + * + */ +static const uint8_t test_vector[] = {0x61, 0x62, 0x63}; + +static const uint8_t test_result_sha512[] = { + 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, + 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, + 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, + 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, + 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, + 0xa5, 0x4c, 0xa4, 0x9f}; + +static const uint8_t test_result_sha256[] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; + +static const uint8_t test_result_md5[] = { + 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, + 0x28, 0xe1, 0x7f, 0x72}; + +/* + * The Scatter-Gather Test vector is the ascii "abc" "def" "ghi", broken + * into blocks of 3 characters as shown + * + * Expected results were generated using command line utitiles: + * + * echo -n -e 'abcdefghijkl' | dd of=/tmp/test + * for hash in sha512sum sha256sum; do $hash /tmp/test; done + * + */ +static const uint8_t test_vector_sg1[] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; +static const uint8_t test_vector_sg2[] = {0x67, 0x68, 0x69}; +static const uint8_t test_vector_sg3[] = {0x6a, 0x6b, 0x6c}; + +static const uint8_t test_result_sg_sha512[] = { + 0x17, 0x80, 0x7c, 0x72, 0x8e, 0xe3, 0xba, 0x35, 0xe7, 0xcf, 0x7a, 0xf8, + 0x23, 0x11, 0x6d, 0x26, 0xe4, 0x1e, 0x5d, 0x4d, 0x6c, 0x2f, 0xf1, 0xf3, + 0x72, 0x0d, 0x3d, 0x96, 0xaa, 0xcb, 0x6f, 0x69, 0xde, 0x64, 0x2e, 0x63, + 0xd5, 0xb7, 0x3f, 0xc3, 0x96, 0xc1, 0x2b, 0xe3, 0x8b, 0x2b, 0xd5, 0xd8, + 0x84, 0x25, 0x7c, 0x32, 0xc8, 0xf6, 0xd0, 0x85, 0x4a, 0xe6, 0xb5, 0x40, + 0xf8, 0x6d, 0xda, 0x2e}; + +static const uint8_t test_result_sg_sha256[] = { + 0xd6, 0x82, 0xed, 0x4c, 0xa4, 0xd9, 0x89, 0xc1, 0x34, 0xec, 0x94, 0xf1, + 0x55, 0x1e, 0x1e, 0xc5, 0x80, 0xdd, 0x6d, 0x5a, 0x6e, 0xcd, 0xe9, 0xf3, + 0xd3, 0x5e, 0x6e, 0x4a, 0x71, 0x7f, 0xbd, 0xe4}; + +/* + * The accumulative mode requires firmware to provide internal initial state + * and message padding (including length L at the end of padding). + * + * This test vector is a ascii text "abc" with padding message. + * + * Expected results were generated using command line utitiles: + * + * echo -n -e 'abc' | dd of=/tmp/test + * for hash in sha512sum sha256sum; do $hash /tmp/test; done + */ +static const uint8_t test_vector_accum_512[] = { + 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; + +static const uint8_t test_vector_accum_256[] = { + 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; + +static const uint8_t test_result_accum_sha512[] = { + 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, + 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, + 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, + 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, + 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, + 0xa5, 0x4c, 0xa4, 0x9f}; + +static const uint8_t test_result_accum_sha256[] = { + 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, + 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, + 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; + +static void write_regs(QTestState *s, uint32_t base, uint32_t src, + uint32_t length, uint32_t out, uint32_t method) +{ + qtest_writel(s, base + HACE_HASH_SRC, src); + qtest_writel(s, base + HACE_HASH_DIGEST, out); + qtest_writel(s, base + HACE_HASH_DATA_LEN, length); + qtest_writel(s, base + HACE_HASH_CMD, HACE_SHA_BE_EN | method); +} + +void aspeed_test_md5(const char *machine, const uint32_t base, + const uint32_t src_addr) + +{ + QTestState *s = qtest_init(machine); + + uint32_t digest_addr = src_addr + 0x01000000; + uint8_t digest[16] = {0}; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); + + write_regs(s, base, src_addr, sizeof(test_vector), + digest_addr, HACE_ALGO_MD5); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_md5, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha256(const char *machine, const uint32_t base, + const uint32_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint32_t digest_addr = src_addr + 0x1000000; + uint8_t digest[32] = {0}; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); + + write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, + HACE_ALGO_SHA256); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sha256, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha512(const char *machine, const uint32_t base, + const uint32_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint32_t digest_addr = src_addr + 0x1000000; + uint8_t digest[64] = {0}; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); + + write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, + HACE_ALGO_SHA512); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sha512, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha256_sg(const char *machine, const uint32_t base, + const uint32_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint32_t src_addr_1 = src_addr + 0x1000000; + const uint32_t src_addr_2 = src_addr + 0x2000000; + const uint32_t src_addr_3 = src_addr + 0x3000000; + const uint32_t digest_addr = src_addr + 0x4000000; + uint8_t digest[32] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_sg1)), + cpu_to_le32(src_addr_1) }, + { cpu_to_le32(sizeof(test_vector_sg2)), + cpu_to_le32(src_addr_2) }, + { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), + cpu_to_le32(src_addr_3) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); + qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); + qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, + (sizeof(test_vector_sg1) + + sizeof(test_vector_sg2) + + sizeof(test_vector_sg3)), + digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sg_sha256, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha512_sg(const char *machine, const uint32_t base, + const uint32_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint32_t src_addr_1 = src_addr + 0x1000000; + const uint32_t src_addr_2 = src_addr + 0x2000000; + const uint32_t src_addr_3 = src_addr + 0x3000000; + const uint32_t digest_addr = src_addr + 0x4000000; + uint8_t digest[64] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_sg1)), + cpu_to_le32(src_addr_1) }, + { cpu_to_le32(sizeof(test_vector_sg2)), + cpu_to_le32(src_addr_2) }, + { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), + cpu_to_le32(src_addr_3) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); + qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); + qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, + (sizeof(test_vector_sg1) + + sizeof(test_vector_sg2) + + sizeof(test_vector_sg3)), + digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sg_sha512, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha256_accum(const char *machine, const uint32_t base, + const uint32_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint32_t buffer_addr = src_addr + 0x1000000; + const uint32_t digest_addr = src_addr + 0x4000000; + uint8_t digest[32] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_accum_256) | SG_LIST_LEN_LAST), + cpu_to_le32(buffer_addr) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, buffer_addr, test_vector_accum_256, + sizeof(test_vector_accum_256)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, sizeof(test_vector_accum_256), + digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN | HACE_ACCUM_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_accum_sha256, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_sha512_accum(const char *machine, const uint32_t base, + const uint32_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint32_t buffer_addr = src_addr + 0x1000000; + const uint32_t digest_addr = src_addr + 0x4000000; + uint8_t digest[64] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_accum_512) | SG_LIST_LEN_LAST), + cpu_to_le32(buffer_addr) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, buffer_addr, test_vector_accum_512, + sizeof(test_vector_accum_512)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, sizeof(test_vector_accum_512), + digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN | HACE_ACCUM_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_accum_sha512, sizeof(digest)); + + qtest_quit(s); +} + +void aspeed_test_addresses(const char *machine, const uint32_t base, + const struct AspeedMasks *expected) +{ + QTestState *s = qtest_init(machine); + + /* + * Check command mode is zero, meaning engine is in direct access mode, + * as this affects the masking behavior of the HASH_SRC register. + */ + g_assert_cmphex(qtest_readl(s, base + HACE_CMD), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); + + + /* Check that the address masking is correct */ + qtest_writel(s, base + HACE_HASH_SRC, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, expected->src); + + qtest_writel(s, base + HACE_HASH_DIGEST, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, + expected->dest); + + qtest_writel(s, base + HACE_HASH_DATA_LEN, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, + expected->len); + + /* Reset to zero */ + qtest_writel(s, base + HACE_HASH_SRC, 0); + qtest_writel(s, base + HACE_HASH_DIGEST, 0); + qtest_writel(s, base + HACE_HASH_DATA_LEN, 0); + + /* Check that all bits are now zero */ + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); + + qtest_quit(s); +} + diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h new file mode 100644 index 0000000000..598577c69b --- /dev/null +++ b/tests/qtest/aspeed-hace-utils.h @@ -0,0 +1,71 @@ +/* + * QTest testcase for the ASPEED Hash and Crypto Engine + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2021 IBM Corp. + */ + +#ifndef TESTS_ASPEED_HACE_UTILS_H +#define TESTS_ASPEED_HACE_UTILS_H + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" + +#define HACE_CMD 0x10 +#define HACE_SHA_BE_EN BIT(3) +#define HACE_MD5_LE_EN BIT(2) +#define HACE_ALGO_MD5 0 +#define HACE_ALGO_SHA1 BIT(5) +#define HACE_ALGO_SHA224 BIT(6) +#define HACE_ALGO_SHA256 (BIT(4) | BIT(6)) +#define HACE_ALGO_SHA512 (BIT(5) | BIT(6)) +#define HACE_ALGO_SHA384 (BIT(5) | BIT(6) | BIT(10)) +#define HACE_SG_EN BIT(18) +#define HACE_ACCUM_EN BIT(8) + +#define HACE_STS 0x1c +#define HACE_RSA_ISR BIT(13) +#define HACE_CRYPTO_ISR BIT(12) +#define HACE_HASH_ISR BIT(9) +#define HACE_RSA_BUSY BIT(2) +#define HACE_CRYPTO_BUSY BIT(1) +#define HACE_HASH_BUSY BIT(0) +#define HACE_HASH_SRC 0x20 +#define HACE_HASH_DIGEST 0x24 +#define HACE_HASH_KEY_BUFF 0x28 +#define HACE_HASH_DATA_LEN 0x2c +#define HACE_HASH_CMD 0x30 + +/* Scatter-Gather Hash */ +#define SG_LIST_LEN_LAST BIT(31) +struct AspeedSgList { + uint32_t len; + uint32_t addr; +} __attribute__ ((__packed__)); + +struct AspeedMasks { + uint32_t src; + uint32_t dest; + uint32_t len; +}; + +void aspeed_test_md5(const char *machine, const uint32_t base, + const uint32_t src_addr); +void aspeed_test_sha256(const char *machine, const uint32_t base, + const uint32_t src_addr); +void aspeed_test_sha512(const char *machine, const uint32_t base, + const uint32_t src_addr); +void aspeed_test_sha256_sg(const char *machine, const uint32_t base, + const uint32_t src_addr); +void aspeed_test_sha512_sg(const char *machine, const uint32_t base, + const uint32_t src_addr); +void aspeed_test_sha256_accum(const char *machine, const uint32_t base, + const uint32_t src_addr); +void aspeed_test_sha512_accum(const char *machine, const uint32_t base, + const uint32_t src_addr); +void aspeed_test_addresses(const char *machine, const uint32_t base, + const struct AspeedMasks *expected); + +#endif /* TESTS_ASPEED_HACE_UTILS_H */ + diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c index ce86a44672..42a306af2a 100644 --- a/tests/qtest/aspeed_hace-test.c +++ b/tests/qtest/aspeed_hace-test.c @@ -6,584 +6,109 @@ */ #include "qemu/osdep.h" - #include "libqtest.h" #include "qemu/bitops.h" +#include "aspeed-hace-utils.h" -#define HACE_CMD 0x10 -#define HACE_SHA_BE_EN BIT(3) -#define HACE_MD5_LE_EN BIT(2) -#define HACE_ALGO_MD5 0 -#define HACE_ALGO_SHA1 BIT(5) -#define HACE_ALGO_SHA224 BIT(6) -#define HACE_ALGO_SHA256 (BIT(4) | BIT(6)) -#define HACE_ALGO_SHA512 (BIT(5) | BIT(6)) -#define HACE_ALGO_SHA384 (BIT(5) | BIT(6) | BIT(10)) -#define HACE_SG_EN BIT(18) -#define HACE_ACCUM_EN BIT(8) - -#define HACE_STS 0x1c -#define HACE_RSA_ISR BIT(13) -#define HACE_CRYPTO_ISR BIT(12) -#define HACE_HASH_ISR BIT(9) -#define HACE_RSA_BUSY BIT(2) -#define HACE_CRYPTO_BUSY BIT(1) -#define HACE_HASH_BUSY BIT(0) -#define HACE_HASH_SRC 0x20 -#define HACE_HASH_DIGEST 0x24 -#define HACE_HASH_KEY_BUFF 0x28 -#define HACE_HASH_DATA_LEN 0x2c -#define HACE_HASH_CMD 0x30 -/* Scatter-Gather Hash */ -#define SG_LIST_LEN_LAST BIT(31) -struct AspeedSgList { - uint32_t len; - uint32_t addr; -} __attribute__ ((__packed__)); - -/* - * Test vector is the ascii "abc" - * - * Expected results were generated using command line utitiles: - * - * echo -n -e 'abc' | dd of=/tmp/test - * for hash in sha512sum sha256sum md5sum; do $hash /tmp/test; done - * - */ -static const uint8_t test_vector[] = {0x61, 0x62, 0x63}; - -static const uint8_t test_result_sha512[] = { - 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, - 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, - 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, - 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, - 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, - 0xa5, 0x4c, 0xa4, 0x9f}; - -static const uint8_t test_result_sha256[] = { - 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, - 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, - 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; - -static const uint8_t test_result_md5[] = { - 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, - 0x28, 0xe1, 0x7f, 0x72}; - -/* - * The Scatter-Gather Test vector is the ascii "abc" "def" "ghi", broken - * into blocks of 3 characters as shown - * - * Expected results were generated using command line utitiles: - * - * echo -n -e 'abcdefghijkl' | dd of=/tmp/test - * for hash in sha512sum sha256sum; do $hash /tmp/test; done - * - */ -static const uint8_t test_vector_sg1[] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; -static const uint8_t test_vector_sg2[] = {0x67, 0x68, 0x69}; -static const uint8_t test_vector_sg3[] = {0x6a, 0x6b, 0x6c}; - -static const uint8_t test_result_sg_sha512[] = { - 0x17, 0x80, 0x7c, 0x72, 0x8e, 0xe3, 0xba, 0x35, 0xe7, 0xcf, 0x7a, 0xf8, - 0x23, 0x11, 0x6d, 0x26, 0xe4, 0x1e, 0x5d, 0x4d, 0x6c, 0x2f, 0xf1, 0xf3, - 0x72, 0x0d, 0x3d, 0x96, 0xaa, 0xcb, 0x6f, 0x69, 0xde, 0x64, 0x2e, 0x63, - 0xd5, 0xb7, 0x3f, 0xc3, 0x96, 0xc1, 0x2b, 0xe3, 0x8b, 0x2b, 0xd5, 0xd8, - 0x84, 0x25, 0x7c, 0x32, 0xc8, 0xf6, 0xd0, 0x85, 0x4a, 0xe6, 0xb5, 0x40, - 0xf8, 0x6d, 0xda, 0x2e}; - -static const uint8_t test_result_sg_sha256[] = { - 0xd6, 0x82, 0xed, 0x4c, 0xa4, 0xd9, 0x89, 0xc1, 0x34, 0xec, 0x94, 0xf1, - 0x55, 0x1e, 0x1e, 0xc5, 0x80, 0xdd, 0x6d, 0x5a, 0x6e, 0xcd, 0xe9, 0xf3, - 0xd3, 0x5e, 0x6e, 0x4a, 0x71, 0x7f, 0xbd, 0xe4}; - -/* - * The accumulative mode requires firmware to provide internal initial state - * and message padding (including length L at the end of padding). - * - * This test vector is a ascii text "abc" with padding message. - * - * Expected results were generated using command line utitiles: - * - * echo -n -e 'abc' | dd of=/tmp/test - * for hash in sha512sum sha256sum; do $hash /tmp/test; done - */ -static const uint8_t test_vector_accum_512[] = { - 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; - -static const uint8_t test_vector_accum_256[] = { - 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; - -static const uint8_t test_result_accum_sha512[] = { - 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, - 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, - 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, - 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, 0xfe, 0xeb, 0xbd, - 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, - 0xa5, 0x4c, 0xa4, 0x9f}; - -static const uint8_t test_result_accum_sha256[] = { - 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, - 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, - 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; - -static void write_regs(QTestState *s, uint32_t base, uint32_t src, - uint32_t length, uint32_t out, uint32_t method) -{ - qtest_writel(s, base + HACE_HASH_SRC, src); - qtest_writel(s, base + HACE_HASH_DIGEST, out); - qtest_writel(s, base + HACE_HASH_DATA_LEN, length); - qtest_writel(s, base + HACE_HASH_CMD, HACE_SHA_BE_EN | method); -} - -static void test_md5(const char *machine, const uint32_t base, - const uint32_t src_addr) - -{ - QTestState *s = qtest_init(machine); - - uint32_t digest_addr = src_addr + 0x01000000; - uint8_t digest[16] = {0}; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); - - write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, HACE_ALGO_MD5); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_md5, sizeof(digest)); - - qtest_quit(s); -} - -static void test_sha256(const char *machine, const uint32_t base, - const uint32_t src_addr) -{ - QTestState *s = qtest_init(machine); - - const uint32_t digest_addr = src_addr + 0x1000000; - uint8_t digest[32] = {0}; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); - - write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, HACE_ALGO_SHA256); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_sha256, sizeof(digest)); - - qtest_quit(s); -} - -static void test_sha512(const char *machine, const uint32_t base, - const uint32_t src_addr) -{ - QTestState *s = qtest_init(machine); - - const uint32_t digest_addr = src_addr + 0x1000000; - uint8_t digest[64] = {0}; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); - - write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, HACE_ALGO_SHA512); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_sha512, sizeof(digest)); - - qtest_quit(s); -} - -static void test_sha256_sg(const char *machine, const uint32_t base, - const uint32_t src_addr) -{ - QTestState *s = qtest_init(machine); - - const uint32_t src_addr_1 = src_addr + 0x1000000; - const uint32_t src_addr_2 = src_addr + 0x2000000; - const uint32_t src_addr_3 = src_addr + 0x3000000; - const uint32_t digest_addr = src_addr + 0x4000000; - uint8_t digest[32] = {0}; - struct AspeedSgList array[] = { - { cpu_to_le32(sizeof(test_vector_sg1)), - cpu_to_le32(src_addr_1) }, - { cpu_to_le32(sizeof(test_vector_sg2)), - cpu_to_le32(src_addr_2) }, - { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), - cpu_to_le32(src_addr_3) }, - }; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); - qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); - qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); - qtest_memwrite(s, src_addr, array, sizeof(array)); - - write_regs(s, base, src_addr, - (sizeof(test_vector_sg1) - + sizeof(test_vector_sg2) - + sizeof(test_vector_sg3)), - digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_sg_sha256, sizeof(digest)); - - qtest_quit(s); -} - -static void test_sha512_sg(const char *machine, const uint32_t base, - const uint32_t src_addr) -{ - QTestState *s = qtest_init(machine); - - const uint32_t src_addr_1 = src_addr + 0x1000000; - const uint32_t src_addr_2 = src_addr + 0x2000000; - const uint32_t src_addr_3 = src_addr + 0x3000000; - const uint32_t digest_addr = src_addr + 0x4000000; - uint8_t digest[64] = {0}; - struct AspeedSgList array[] = { - { cpu_to_le32(sizeof(test_vector_sg1)), - cpu_to_le32(src_addr_1) }, - { cpu_to_le32(sizeof(test_vector_sg2)), - cpu_to_le32(src_addr_2) }, - { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), - cpu_to_le32(src_addr_3) }, - }; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); - qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); - qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); - qtest_memwrite(s, src_addr, array, sizeof(array)); - - write_regs(s, base, src_addr, - (sizeof(test_vector_sg1) - + sizeof(test_vector_sg2) - + sizeof(test_vector_sg3)), - digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_sg_sha512, sizeof(digest)); - - qtest_quit(s); -} - -static void test_sha256_accum(const char *machine, const uint32_t base, - const uint32_t src_addr) -{ - QTestState *s = qtest_init(machine); - - const uint32_t buffer_addr = src_addr + 0x1000000; - const uint32_t digest_addr = src_addr + 0x4000000; - uint8_t digest[32] = {0}; - struct AspeedSgList array[] = { - { cpu_to_le32(sizeof(test_vector_accum_256) | SG_LIST_LEN_LAST), - cpu_to_le32(buffer_addr) }, - }; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, buffer_addr, test_vector_accum_256, - sizeof(test_vector_accum_256)); - qtest_memwrite(s, src_addr, array, sizeof(array)); - - write_regs(s, base, src_addr, sizeof(test_vector_accum_256), - digest_addr, HACE_ALGO_SHA256 | HACE_SG_EN | HACE_ACCUM_EN); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_accum_sha256, sizeof(digest)); - - qtest_quit(s); -} - -static void test_sha512_accum(const char *machine, const uint32_t base, - const uint32_t src_addr) -{ - QTestState *s = qtest_init(machine); - - const uint32_t buffer_addr = src_addr + 0x1000000; - const uint32_t digest_addr = src_addr + 0x4000000; - uint8_t digest[64] = {0}; - struct AspeedSgList array[] = { - { cpu_to_le32(sizeof(test_vector_accum_512) | SG_LIST_LEN_LAST), - cpu_to_le32(buffer_addr) }, - }; - - /* Check engine is idle, no busy or irq bits set */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Write test vector into memory */ - qtest_memwrite(s, buffer_addr, test_vector_accum_512, - sizeof(test_vector_accum_512)); - qtest_memwrite(s, src_addr, array, sizeof(array)); - - write_regs(s, base, src_addr, sizeof(test_vector_accum_512), - digest_addr, HACE_ALGO_SHA512 | HACE_SG_EN | HACE_ACCUM_EN); - - /* Check hash IRQ status is asserted */ - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); - - /* Clear IRQ status and check status is deasserted */ - qtest_writel(s, base + HACE_STS, 0x00000200); - g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); - - /* Read computed digest from memory */ - qtest_memread(s, digest_addr, digest, sizeof(digest)); - - /* Check result of computation */ - g_assert_cmpmem(digest, sizeof(digest), - test_result_accum_sha512, sizeof(digest)); - - qtest_quit(s); -} - -struct masks { - uint32_t src; - uint32_t dest; - uint32_t len; -}; - -static const struct masks ast2600_masks = { +static const struct AspeedMasks ast2600_masks = { .src = 0x7fffffff, .dest = 0x7ffffff8, .len = 0x0fffffff, }; -static const struct masks ast2500_masks = { +static const struct AspeedMasks ast2500_masks = { .src = 0x3fffffff, .dest = 0x3ffffff8, .len = 0x0fffffff, }; -static const struct masks ast2400_masks = { +static const struct AspeedMasks ast2400_masks = { .src = 0x0fffffff, .dest = 0x0ffffff8, .len = 0x0fffffff, }; -static void test_addresses(const char *machine, const uint32_t base, - const struct masks *expected) -{ - QTestState *s = qtest_init(machine); - - /* - * Check command mode is zero, meaning engine is in direct access mode, - * as this affects the masking behavior of the HASH_SRC register. - */ - g_assert_cmphex(qtest_readl(s, base + HACE_CMD), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); - - - /* Check that the address masking is correct */ - qtest_writel(s, base + HACE_HASH_SRC, 0xffffffff); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, expected->src); - - qtest_writel(s, base + HACE_HASH_DIGEST, 0xffffffff); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, expected->dest); - - qtest_writel(s, base + HACE_HASH_DATA_LEN, 0xffffffff); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, expected->len); - - /* Reset to zero */ - qtest_writel(s, base + HACE_HASH_SRC, 0); - qtest_writel(s, base + HACE_HASH_DIGEST, 0); - qtest_writel(s, base + HACE_HASH_DATA_LEN, 0); - - /* Check that all bits are now zero */ - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); - g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); - - qtest_quit(s); -} - /* ast2600 */ static void test_md5_ast2600(void) { - test_md5("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_md5("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha256_ast2600(void) { - test_sha256("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha256("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha256_sg_ast2600(void) { - test_sha256_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha256_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha512_ast2600(void) { - test_sha512("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha512("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha512_sg_ast2600(void) { - test_sha512_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha512_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha256_accum_ast2600(void) { - test_sha256_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha256_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_sha512_accum_ast2600(void) { - test_sha512_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); + aspeed_test_sha512_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } static void test_addresses_ast2600(void) { - test_addresses("-machine ast2600-evb", 0x1e6d0000, &ast2600_masks); + aspeed_test_addresses("-machine ast2600-evb", 0x1e6d0000, &ast2600_masks); } /* ast2500 */ static void test_md5_ast2500(void) { - test_md5("-machine ast2500-evb", 0x1e6e3000, 0x80000000); + aspeed_test_md5("-machine ast2500-evb", 0x1e6e3000, 0x80000000); } static void test_sha256_ast2500(void) { - test_sha256("-machine ast2500-evb", 0x1e6e3000, 0x80000000); + aspeed_test_sha256("-machine ast2500-evb", 0x1e6e3000, 0x80000000); } static void test_sha512_ast2500(void) { - test_sha512("-machine ast2500-evb", 0x1e6e3000, 0x80000000); + aspeed_test_sha512("-machine ast2500-evb", 0x1e6e3000, 0x80000000); } static void test_addresses_ast2500(void) { - test_addresses("-machine ast2500-evb", 0x1e6e3000, &ast2500_masks); + aspeed_test_addresses("-machine ast2500-evb", 0x1e6e3000, &ast2500_masks); } /* ast2400 */ static void test_md5_ast2400(void) { - test_md5("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); + aspeed_test_md5("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); } static void test_sha256_ast2400(void) { - test_sha256("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); + aspeed_test_sha256("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); } static void test_sha512_ast2400(void) { - test_sha512("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); + aspeed_test_sha512("-machine palmetto-bmc", 0x1e6e3000, 0x40000000); } static void test_addresses_ast2400(void) { - test_addresses("-machine palmetto-bmc", 0x1e6e3000, &ast2400_masks); + aspeed_test_addresses("-machine palmetto-bmc", 0x1e6e3000, &ast2400_masks); } int main(int argc, char **argv) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index c3a24a3eb4..26d83cd9dd 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -361,6 +361,7 @@ if gnutls.found() endif qtests = { + 'aspeed_hace-test': files('aspeed-hace-utils.c', 'aspeed_hace-test.c'), 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'), 'ast2700-smc-test': files('aspeed-smc-utils.c', 'ast2700-smc-test.c'), 'bios-tables-test': [io, 'boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'], From 33627ab237168fa624435eb8ae87f82a2ba9d7f5 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:51 +0800 Subject: [PATCH 1141/2760] test/qtest/hace: Specify explicit array sizes for test vectors and hash results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To enhance code readability and prevent potential buffer overflows or unintended size assumptions, this commit updates all fixed-size array declarations to use explicit array sizes. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-20-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-hace-utils.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c index 8582847945..777fa5b986 100644 --- a/tests/qtest/aspeed-hace-utils.c +++ b/tests/qtest/aspeed-hace-utils.c @@ -19,9 +19,9 @@ * for hash in sha512sum sha256sum md5sum; do $hash /tmp/test; done * */ -static const uint8_t test_vector[] = {0x61, 0x62, 0x63}; +static const uint8_t test_vector[3] = {0x61, 0x62, 0x63}; -static const uint8_t test_result_sha512[] = { +static const uint8_t test_result_sha512[64] = { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, @@ -29,12 +29,12 @@ static const uint8_t test_result_sha512[] = { 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f}; -static const uint8_t test_result_sha256[] = { +static const uint8_t test_result_sha256[32] = { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; -static const uint8_t test_result_md5[] = { +static const uint8_t test_result_md5[16] = { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72}; @@ -48,11 +48,11 @@ static const uint8_t test_result_md5[] = { * for hash in sha512sum sha256sum; do $hash /tmp/test; done * */ -static const uint8_t test_vector_sg1[] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; -static const uint8_t test_vector_sg2[] = {0x67, 0x68, 0x69}; -static const uint8_t test_vector_sg3[] = {0x6a, 0x6b, 0x6c}; +static const uint8_t test_vector_sg1[6] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; +static const uint8_t test_vector_sg2[3] = {0x67, 0x68, 0x69}; +static const uint8_t test_vector_sg3[3] = {0x6a, 0x6b, 0x6c}; -static const uint8_t test_result_sg_sha512[] = { +static const uint8_t test_result_sg_sha512[64] = { 0x17, 0x80, 0x7c, 0x72, 0x8e, 0xe3, 0xba, 0x35, 0xe7, 0xcf, 0x7a, 0xf8, 0x23, 0x11, 0x6d, 0x26, 0xe4, 0x1e, 0x5d, 0x4d, 0x6c, 0x2f, 0xf1, 0xf3, 0x72, 0x0d, 0x3d, 0x96, 0xaa, 0xcb, 0x6f, 0x69, 0xde, 0x64, 0x2e, 0x63, @@ -60,7 +60,7 @@ static const uint8_t test_result_sg_sha512[] = { 0x84, 0x25, 0x7c, 0x32, 0xc8, 0xf6, 0xd0, 0x85, 0x4a, 0xe6, 0xb5, 0x40, 0xf8, 0x6d, 0xda, 0x2e}; -static const uint8_t test_result_sg_sha256[] = { +static const uint8_t test_result_sg_sha256[32] = { 0xd6, 0x82, 0xed, 0x4c, 0xa4, 0xd9, 0x89, 0xc1, 0x34, 0xec, 0x94, 0xf1, 0x55, 0x1e, 0x1e, 0xc5, 0x80, 0xdd, 0x6d, 0x5a, 0x6e, 0xcd, 0xe9, 0xf3, 0xd3, 0x5e, 0x6e, 0x4a, 0x71, 0x7f, 0xbd, 0xe4}; @@ -76,7 +76,7 @@ static const uint8_t test_result_sg_sha256[] = { * echo -n -e 'abc' | dd of=/tmp/test * for hash in sha512sum sha256sum; do $hash /tmp/test; done */ -static const uint8_t test_vector_accum_512[] = { +static const uint8_t test_vector_accum_512[128] = { 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -94,7 +94,7 @@ static const uint8_t test_vector_accum_512[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; -static const uint8_t test_vector_accum_256[] = { +static const uint8_t test_vector_accum_256[64] = { 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -104,7 +104,7 @@ static const uint8_t test_vector_accum_256[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; -static const uint8_t test_result_accum_sha512[] = { +static const uint8_t test_result_accum_sha512[64] = { 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, @@ -112,7 +112,7 @@ static const uint8_t test_result_accum_sha512[] = { 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f}; -static const uint8_t test_result_accum_sha256[] = { +static const uint8_t test_result_accum_sha256[32] = { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; From a3e7f6d05d8bf38b0337fabc438d32c46948ccff Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:52 +0800 Subject: [PATCH 1142/2760] test/qtest/hace: Adjust test address range for AST1030 due to SRAM limitations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The digest_addr is set to "src_addr + 0x1000000", where src_addr is the DRAM base address. However, the value 0x1000000 (16MB) is too large because the AST1030 does not support DRAM, and its SRAM size is only 768KB. A range size of 0x10000 (64KB) is sufficient for HACE test cases, as the test vector size does not exceed 64KB. Updates: 1. Direct Access Mode Update digest_addr to "src_addr + 0x10000" in the following functions: aspeed_test_md5 aspeed_test_sha256 aspeed_test_sha512 2. Scatter-Gather (SG) Mode Update source address for different SG buffer addresses in the following functions: src_addr1 = src_addr + 0x10000 src_addr2 = src_addr + 0x20000 src_addr3 = src_addr + 0x30000 digest_addr = src_addr + 0x40000 aspeed_test_sha256_sg aspeed_test_sha512_sg 3. ACC Mode Update Update the SG List start address: src_addr + 0x10000 Update the SG List buffer size to 0x30000 (192KB). buffer_addr = src_addr + 0x10000 digest_addr = src_addr + 0x40000 Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-21-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-hace-utils.c | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c index 777fa5b986..539d06e4f8 100644 --- a/tests/qtest/aspeed-hace-utils.c +++ b/tests/qtest/aspeed-hace-utils.c @@ -132,7 +132,7 @@ void aspeed_test_md5(const char *machine, const uint32_t base, { QTestState *s = qtest_init(machine); - uint32_t digest_addr = src_addr + 0x01000000; + uint32_t digest_addr = src_addr + 0x010000; uint8_t digest[16] = {0}; /* Check engine is idle, no busy or irq bits set */ @@ -166,7 +166,7 @@ void aspeed_test_sha256(const char *machine, const uint32_t base, { QTestState *s = qtest_init(machine); - const uint32_t digest_addr = src_addr + 0x1000000; + const uint32_t digest_addr = src_addr + 0x10000; uint8_t digest[32] = {0}; /* Check engine is idle, no busy or irq bits set */ @@ -200,7 +200,7 @@ void aspeed_test_sha512(const char *machine, const uint32_t base, { QTestState *s = qtest_init(machine); - const uint32_t digest_addr = src_addr + 0x1000000; + const uint32_t digest_addr = src_addr + 0x10000; uint8_t digest[64] = {0}; /* Check engine is idle, no busy or irq bits set */ @@ -234,10 +234,10 @@ void aspeed_test_sha256_sg(const char *machine, const uint32_t base, { QTestState *s = qtest_init(machine); - const uint32_t src_addr_1 = src_addr + 0x1000000; - const uint32_t src_addr_2 = src_addr + 0x2000000; - const uint32_t src_addr_3 = src_addr + 0x3000000; - const uint32_t digest_addr = src_addr + 0x4000000; + const uint32_t src_addr_1 = src_addr + 0x10000; + const uint32_t src_addr_2 = src_addr + 0x20000; + const uint32_t src_addr_3 = src_addr + 0x30000; + const uint32_t digest_addr = src_addr + 0x40000; uint8_t digest[32] = {0}; struct AspeedSgList array[] = { { cpu_to_le32(sizeof(test_vector_sg1)), @@ -285,10 +285,10 @@ void aspeed_test_sha512_sg(const char *machine, const uint32_t base, { QTestState *s = qtest_init(machine); - const uint32_t src_addr_1 = src_addr + 0x1000000; - const uint32_t src_addr_2 = src_addr + 0x2000000; - const uint32_t src_addr_3 = src_addr + 0x3000000; - const uint32_t digest_addr = src_addr + 0x4000000; + const uint32_t src_addr_1 = src_addr + 0x10000; + const uint32_t src_addr_2 = src_addr + 0x20000; + const uint32_t src_addr_3 = src_addr + 0x30000; + const uint32_t digest_addr = src_addr + 0x40000; uint8_t digest[64] = {0}; struct AspeedSgList array[] = { { cpu_to_le32(sizeof(test_vector_sg1)), @@ -336,8 +336,8 @@ void aspeed_test_sha256_accum(const char *machine, const uint32_t base, { QTestState *s = qtest_init(machine); - const uint32_t buffer_addr = src_addr + 0x1000000; - const uint32_t digest_addr = src_addr + 0x4000000; + const uint32_t buffer_addr = src_addr + 0x10000; + const uint32_t digest_addr = src_addr + 0x40000; uint8_t digest[32] = {0}; struct AspeedSgList array[] = { { cpu_to_le32(sizeof(test_vector_accum_256) | SG_LIST_LEN_LAST), @@ -377,8 +377,8 @@ void aspeed_test_sha512_accum(const char *machine, const uint32_t base, { QTestState *s = qtest_init(machine); - const uint32_t buffer_addr = src_addr + 0x1000000; - const uint32_t digest_addr = src_addr + 0x4000000; + const uint32_t buffer_addr = src_addr + 0x10000; + const uint32_t digest_addr = src_addr + 0x40000; uint8_t digest[64] = {0}; struct AspeedSgList array[] = { { cpu_to_le32(sizeof(test_vector_accum_512) | SG_LIST_LEN_LAST), From 3c13be86ba181faea21a1b4f66f54df5328574f7 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:53 +0800 Subject: [PATCH 1143/2760] test/qtest/hace: Add SHA-384 test cases for ASPEED HACE model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduced SHA-384 test functions to verify hashing operations. Extended support for scatter-gather ("_sg") and accumulation ("_accum") tests. Updated test result vectors for SHA-384 validation. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-22-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-hace-utils.c | 168 +++++++++++++++++++++++++++++++- tests/qtest/aspeed-hace-utils.h | 6 ++ 2 files changed, 171 insertions(+), 3 deletions(-) diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c index 539d06e4f8..dad90ee81c 100644 --- a/tests/qtest/aspeed-hace-utils.c +++ b/tests/qtest/aspeed-hace-utils.c @@ -16,7 +16,7 @@ * Expected results were generated using command line utitiles: * * echo -n -e 'abc' | dd of=/tmp/test - * for hash in sha512sum sha256sum md5sum; do $hash /tmp/test; done + * for hash in sha512sum sha384sum sha256sum md5sum; do $hash /tmp/test; done * */ static const uint8_t test_vector[3] = {0x61, 0x62, 0x63}; @@ -29,6 +29,12 @@ static const uint8_t test_result_sha512[64] = { 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f}; +static const uint8_t test_result_sha384[48] = { + 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, 0xa0, 0x3d, 0x69, + 0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, + 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b, + 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7}; + static const uint8_t test_result_sha256[32] = { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, @@ -45,7 +51,7 @@ static const uint8_t test_result_md5[16] = { * Expected results were generated using command line utitiles: * * echo -n -e 'abcdefghijkl' | dd of=/tmp/test - * for hash in sha512sum sha256sum; do $hash /tmp/test; done + * for hash in sha512sum sha384sum sha256sum; do $hash /tmp/test; done * */ static const uint8_t test_vector_sg1[6] = {0x61, 0x62, 0x63, 0x64, 0x65, 0x66}; @@ -60,6 +66,12 @@ static const uint8_t test_result_sg_sha512[64] = { 0x84, 0x25, 0x7c, 0x32, 0xc8, 0xf6, 0xd0, 0x85, 0x4a, 0xe6, 0xb5, 0x40, 0xf8, 0x6d, 0xda, 0x2e}; +static const uint8_t test_result_sg_sha384[48] = { + 0x10, 0x3c, 0xa9, 0x6c, 0x06, 0xa1, 0xce, 0x79, 0x8f, 0x08, 0xf8, 0xef, + 0xf0, 0xdf, 0xb0, 0xcc, 0xdb, 0x56, 0x7d, 0x48, 0xb2, 0x85, 0xb2, 0x3d, + 0x0c, 0xd7, 0x73, 0x45, 0x46, 0x67, 0xa3, 0xc2, 0xfa, 0x5f, 0x1b, 0x58, + 0xd9, 0xcd, 0xf2, 0x32, 0x9b, 0xd9, 0x97, 0x97, 0x30, 0xbf, 0xaa, 0xff}; + static const uint8_t test_result_sg_sha256[32] = { 0xd6, 0x82, 0xed, 0x4c, 0xa4, 0xd9, 0x89, 0xc1, 0x34, 0xec, 0x94, 0xf1, 0x55, 0x1e, 0x1e, 0xc5, 0x80, 0xdd, 0x6d, 0x5a, 0x6e, 0xcd, 0xe9, 0xf3, @@ -74,7 +86,7 @@ static const uint8_t test_result_sg_sha256[32] = { * Expected results were generated using command line utitiles: * * echo -n -e 'abc' | dd of=/tmp/test - * for hash in sha512sum sha256sum; do $hash /tmp/test; done + * for hash in sha512sum sha384sum sha256sum; do $hash /tmp/test; done */ static const uint8_t test_vector_accum_512[128] = { 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, @@ -94,6 +106,24 @@ static const uint8_t test_vector_accum_512[128] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; +static const uint8_t test_vector_accum_384[128] = { + 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18}; + static const uint8_t test_vector_accum_256[64] = { 0x61, 0x62, 0x63, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -112,6 +142,12 @@ static const uint8_t test_result_accum_sha512[64] = { 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, 0x9f}; +static const uint8_t test_result_accum_sha384[48] = { + 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, 0xa0, 0x3d, 0x69, + 0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, + 0x1a, 0x8b, 0x60, 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b, + 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, 0xc8, 0x25, 0xa7}; + static const uint8_t test_result_accum_sha256[32] = { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, @@ -195,6 +231,40 @@ void aspeed_test_sha256(const char *machine, const uint32_t base, qtest_quit(s); } +void aspeed_test_sha384(const char *machine, const uint32_t base, + const uint32_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint32_t digest_addr = src_addr + 0x10000; + uint8_t digest[48] = {0}; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr, test_vector, sizeof(test_vector)); + + write_regs(s, base, src_addr, sizeof(test_vector), digest_addr, + HACE_ALGO_SHA384); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sha384, sizeof(digest)); + + qtest_quit(s); +} + void aspeed_test_sha512(const char *machine, const uint32_t base, const uint32_t src_addr) { @@ -280,6 +350,57 @@ void aspeed_test_sha256_sg(const char *machine, const uint32_t base, qtest_quit(s); } +void aspeed_test_sha384_sg(const char *machine, const uint32_t base, + const uint32_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint32_t src_addr_1 = src_addr + 0x10000; + const uint32_t src_addr_2 = src_addr + 0x20000; + const uint32_t src_addr_3 = src_addr + 0x30000; + const uint32_t digest_addr = src_addr + 0x40000; + uint8_t digest[48] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_sg1)), + cpu_to_le32(src_addr_1) }, + { cpu_to_le32(sizeof(test_vector_sg2)), + cpu_to_le32(src_addr_2) }, + { cpu_to_le32(sizeof(test_vector_sg3) | SG_LIST_LEN_LAST), + cpu_to_le32(src_addr_3) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, src_addr_1, test_vector_sg1, sizeof(test_vector_sg1)); + qtest_memwrite(s, src_addr_2, test_vector_sg2, sizeof(test_vector_sg2)); + qtest_memwrite(s, src_addr_3, test_vector_sg3, sizeof(test_vector_sg3)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, + (sizeof(test_vector_sg1) + + sizeof(test_vector_sg2) + + sizeof(test_vector_sg3)), + digest_addr, HACE_ALGO_SHA384 | HACE_SG_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_sg_sha384, sizeof(digest)); + + qtest_quit(s); +} + void aspeed_test_sha512_sg(const char *machine, const uint32_t base, const uint32_t src_addr) { @@ -372,6 +493,47 @@ void aspeed_test_sha256_accum(const char *machine, const uint32_t base, qtest_quit(s); } +void aspeed_test_sha384_accum(const char *machine, const uint32_t base, + const uint32_t src_addr) +{ + QTestState *s = qtest_init(machine); + + const uint32_t buffer_addr = src_addr + 0x10000; + const uint32_t digest_addr = src_addr + 0x40000; + uint8_t digest[48] = {0}; + struct AspeedSgList array[] = { + { cpu_to_le32(sizeof(test_vector_accum_384) | SG_LIST_LEN_LAST), + cpu_to_le32(buffer_addr) }, + }; + + /* Check engine is idle, no busy or irq bits set */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Write test vector into memory */ + qtest_memwrite(s, buffer_addr, test_vector_accum_384, + sizeof(test_vector_accum_384)); + qtest_memwrite(s, src_addr, array, sizeof(array)); + + write_regs(s, base, src_addr, sizeof(test_vector_accum_384), + digest_addr, HACE_ALGO_SHA384 | HACE_SG_EN | HACE_ACCUM_EN); + + /* Check hash IRQ status is asserted */ + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0x00000200); + + /* Clear IRQ status and check status is deasserted */ + qtest_writel(s, base + HACE_STS, 0x00000200); + g_assert_cmphex(qtest_readl(s, base + HACE_STS), ==, 0); + + /* Read computed digest from memory */ + qtest_memread(s, digest_addr, digest, sizeof(digest)); + + /* Check result of computation */ + g_assert_cmpmem(digest, sizeof(digest), + test_result_accum_sha384, sizeof(digest)); + + qtest_quit(s); +} + void aspeed_test_sha512_accum(const char *machine, const uint32_t base, const uint32_t src_addr) { diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h index 598577c69b..f4440561de 100644 --- a/tests/qtest/aspeed-hace-utils.h +++ b/tests/qtest/aspeed-hace-utils.h @@ -54,14 +54,20 @@ void aspeed_test_md5(const char *machine, const uint32_t base, const uint32_t src_addr); void aspeed_test_sha256(const char *machine, const uint32_t base, const uint32_t src_addr); +void aspeed_test_sha384(const char *machine, const uint32_t base, + const uint32_t src_addr); void aspeed_test_sha512(const char *machine, const uint32_t base, const uint32_t src_addr); void aspeed_test_sha256_sg(const char *machine, const uint32_t base, const uint32_t src_addr); +void aspeed_test_sha384_sg(const char *machine, const uint32_t base, + const uint32_t src_addr); void aspeed_test_sha512_sg(const char *machine, const uint32_t base, const uint32_t src_addr); void aspeed_test_sha256_accum(const char *machine, const uint32_t base, const uint32_t src_addr); +void aspeed_test_sha384_accum(const char *machine, const uint32_t base, + const uint32_t src_addr); void aspeed_test_sha512_accum(const char *machine, const uint32_t base, const uint32_t src_addr); void aspeed_test_addresses(const char *machine, const uint32_t base, From adf2fb3951625dfba7ff95433c82a449e464a578 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:54 +0800 Subject: [PATCH 1144/2760] test/qtest/hace: Add SHA-384 tests for AST2600 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduced "test_sha384_ast2600" to validate SHA-384 hashing. Added "test_sha384_sg_ast2600" for scatter-gather SHA-384 verification. Implemented "test_sha384_accum_ast2600" to test SHA-384 accumulation. Registered new test cases in "main" to ensure execution. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-23-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_hace-test.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c index 42a306af2a..ab0c98330e 100644 --- a/tests/qtest/aspeed_hace-test.c +++ b/tests/qtest/aspeed_hace-test.c @@ -44,6 +44,16 @@ static void test_sha256_sg_ast2600(void) aspeed_test_sha256_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } +static void test_sha384_ast2600(void) +{ + aspeed_test_sha384("-machine ast2600-evb", 0x1e6d0000, 0x80000000); +} + +static void test_sha384_sg_ast2600(void) +{ + aspeed_test_sha384_sg("-machine ast2600-evb", 0x1e6d0000, 0x80000000); +} + static void test_sha512_ast2600(void) { aspeed_test_sha512("-machine ast2600-evb", 0x1e6d0000, 0x80000000); @@ -59,6 +69,11 @@ static void test_sha256_accum_ast2600(void) aspeed_test_sha256_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); } +static void test_sha384_accum_ast2600(void) +{ + aspeed_test_sha384_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); +} + static void test_sha512_accum_ast2600(void) { aspeed_test_sha512_accum("-machine ast2600-evb", 0x1e6d0000, 0x80000000); @@ -117,13 +132,16 @@ int main(int argc, char **argv) qtest_add_func("ast2600/hace/addresses", test_addresses_ast2600); qtest_add_func("ast2600/hace/sha512", test_sha512_ast2600); + qtest_add_func("ast2600/hace/sha384", test_sha384_ast2600); qtest_add_func("ast2600/hace/sha256", test_sha256_ast2600); qtest_add_func("ast2600/hace/md5", test_md5_ast2600); qtest_add_func("ast2600/hace/sha512_sg", test_sha512_sg_ast2600); + qtest_add_func("ast2600/hace/sha384_sg", test_sha384_sg_ast2600); qtest_add_func("ast2600/hace/sha256_sg", test_sha256_sg_ast2600); qtest_add_func("ast2600/hace/sha512_accum", test_sha512_accum_ast2600); + qtest_add_func("ast2600/hace/sha384_accum", test_sha384_accum_ast2600); qtest_add_func("ast2600/hace/sha256_accum", test_sha256_accum_ast2600); qtest_add_func("ast2500/hace/addresses", test_addresses_ast2500); From 4f4e25507de719605936c955df3a5a5c560c9986 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:55 +0800 Subject: [PATCH 1145/2760] test/qtest/hace: Add tests for AST1030 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HACE model in AST2600 and AST1030 is identical. Referencing the AST2600 test cases, new tests have been created for AST1030. Implemented test functions for SHA-256, SHA-384, SHA-512, and MD5. Added scatter-gather and accumulation test variants. For AST1030, the HACE controller base address starts at "0x7e6d0000", and the SDRAM start address is "0x0". Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-24-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_hace-test.c | 76 ++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c index ab0c98330e..31890d574e 100644 --- a/tests/qtest/aspeed_hace-test.c +++ b/tests/qtest/aspeed_hace-test.c @@ -10,6 +10,12 @@ #include "qemu/bitops.h" #include "aspeed-hace-utils.h" +static const struct AspeedMasks ast1030_masks = { + .src = 0x7fffffff, + .dest = 0x7ffffff8, + .len = 0x0fffffff, +}; + static const struct AspeedMasks ast2600_masks = { .src = 0x7fffffff, .dest = 0x7ffffff8, @@ -28,6 +34,62 @@ static const struct AspeedMasks ast2400_masks = { .len = 0x0fffffff, }; +/* ast1030 */ +static void test_md5_ast1030(void) +{ + aspeed_test_md5("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} + +static void test_sha256_ast1030(void) +{ + aspeed_test_sha256("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} + +static void test_sha256_sg_ast1030(void) +{ + aspeed_test_sha256_sg("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} + +static void test_sha384_ast1030(void) +{ + aspeed_test_sha384("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} + +static void test_sha384_sg_ast1030(void) +{ + aspeed_test_sha384_sg("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} + +static void test_sha512_ast1030(void) +{ + aspeed_test_sha512("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} + +static void test_sha512_sg_ast1030(void) +{ + aspeed_test_sha512_sg("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} + +static void test_sha256_accum_ast1030(void) +{ + aspeed_test_sha256_accum("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} + +static void test_sha384_accum_ast1030(void) +{ + aspeed_test_sha384_accum("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} + +static void test_sha512_accum_ast1030(void) +{ + aspeed_test_sha512_accum("-machine ast1030-evb", 0x7e6d0000, 0x00000000); +} + +static void test_addresses_ast1030(void) +{ + aspeed_test_addresses("-machine ast1030-evb", 0x7e6d0000, &ast1030_masks); +} + /* ast2600 */ static void test_md5_ast2600(void) { @@ -130,6 +192,20 @@ int main(int argc, char **argv) { g_test_init(&argc, &argv, NULL); + qtest_add_func("ast1030/hace/addresses", test_addresses_ast1030); + qtest_add_func("ast1030/hace/sha512", test_sha512_ast1030); + qtest_add_func("ast1030/hace/sha384", test_sha384_ast1030); + qtest_add_func("ast1030/hace/sha256", test_sha256_ast1030); + qtest_add_func("ast1030/hace/md5", test_md5_ast1030); + + qtest_add_func("ast1030/hace/sha512_sg", test_sha512_sg_ast1030); + qtest_add_func("ast1030/hace/sha384_sg", test_sha384_sg_ast1030); + qtest_add_func("ast1030/hace/sha256_sg", test_sha256_sg_ast1030); + + qtest_add_func("ast1030/hace/sha512_accum", test_sha512_accum_ast1030); + qtest_add_func("ast1030/hace/sha384_accum", test_sha384_accum_ast1030); + qtest_add_func("ast1030/hace/sha256_accum", test_sha256_accum_ast1030); + qtest_add_func("ast2600/hace/addresses", test_addresses_ast2600); qtest_add_func("ast2600/hace/sha512", test_sha512_ast2600); qtest_add_func("ast2600/hace/sha384", test_sha384_ast2600); From dcdbbd45a86ad47327282aa3301622cbe65e9fdb Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:56 +0800 Subject: [PATCH 1146/2760] test/qtest/hace: Update source data and digest data type to 64-bit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the hash data source and digest result buffer addresses are set to 32-bit. However, the AST2700 CPU is a 64-bit Cortex-A35 architecture, and its DRAM base address is also 64-bit. To support AST2700, update the hash data source address and digest result buffer address to use 64-bit addressing. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-25-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-hace-utils.c | 72 ++++++++++++++++----------------- tests/qtest/aspeed-hace-utils.h | 20 ++++----- 2 files changed, 46 insertions(+), 46 deletions(-) diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c index dad90ee81c..1b54870dd4 100644 --- a/tests/qtest/aspeed-hace-utils.c +++ b/tests/qtest/aspeed-hace-utils.c @@ -153,22 +153,22 @@ static const uint8_t test_result_accum_sha256[32] = { 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}; -static void write_regs(QTestState *s, uint32_t base, uint32_t src, - uint32_t length, uint32_t out, uint32_t method) +static void write_regs(QTestState *s, uint32_t base, uint64_t src, + uint32_t length, uint64_t out, uint32_t method) { - qtest_writel(s, base + HACE_HASH_SRC, src); - qtest_writel(s, base + HACE_HASH_DIGEST, out); + qtest_writel(s, base + HACE_HASH_SRC, extract64(src, 0, 32)); + qtest_writel(s, base + HACE_HASH_DIGEST, extract64(out, 0, 32)); qtest_writel(s, base + HACE_HASH_DATA_LEN, length); qtest_writel(s, base + HACE_HASH_CMD, HACE_SHA_BE_EN | method); } void aspeed_test_md5(const char *machine, const uint32_t base, - const uint32_t src_addr) + const uint64_t src_addr) { QTestState *s = qtest_init(machine); - uint32_t digest_addr = src_addr + 0x010000; + uint64_t digest_addr = src_addr + 0x010000; uint8_t digest[16] = {0}; /* Check engine is idle, no busy or irq bits set */ @@ -198,11 +198,11 @@ void aspeed_test_md5(const char *machine, const uint32_t base, } void aspeed_test_sha256(const char *machine, const uint32_t base, - const uint32_t src_addr) + const uint64_t src_addr) { QTestState *s = qtest_init(machine); - const uint32_t digest_addr = src_addr + 0x10000; + const uint64_t digest_addr = src_addr + 0x10000; uint8_t digest[32] = {0}; /* Check engine is idle, no busy or irq bits set */ @@ -232,11 +232,11 @@ void aspeed_test_sha256(const char *machine, const uint32_t base, } void aspeed_test_sha384(const char *machine, const uint32_t base, - const uint32_t src_addr) + const uint64_t src_addr) { QTestState *s = qtest_init(machine); - const uint32_t digest_addr = src_addr + 0x10000; + const uint64_t digest_addr = src_addr + 0x10000; uint8_t digest[48] = {0}; /* Check engine is idle, no busy or irq bits set */ @@ -266,11 +266,11 @@ void aspeed_test_sha384(const char *machine, const uint32_t base, } void aspeed_test_sha512(const char *machine, const uint32_t base, - const uint32_t src_addr) + const uint64_t src_addr) { QTestState *s = qtest_init(machine); - const uint32_t digest_addr = src_addr + 0x10000; + const uint64_t digest_addr = src_addr + 0x10000; uint8_t digest[64] = {0}; /* Check engine is idle, no busy or irq bits set */ @@ -300,14 +300,14 @@ void aspeed_test_sha512(const char *machine, const uint32_t base, } void aspeed_test_sha256_sg(const char *machine, const uint32_t base, - const uint32_t src_addr) + const uint64_t src_addr) { QTestState *s = qtest_init(machine); - const uint32_t src_addr_1 = src_addr + 0x10000; - const uint32_t src_addr_2 = src_addr + 0x20000; - const uint32_t src_addr_3 = src_addr + 0x30000; - const uint32_t digest_addr = src_addr + 0x40000; + const uint64_t src_addr_1 = src_addr + 0x10000; + const uint64_t src_addr_2 = src_addr + 0x20000; + const uint64_t src_addr_3 = src_addr + 0x30000; + const uint64_t digest_addr = src_addr + 0x40000; uint8_t digest[32] = {0}; struct AspeedSgList array[] = { { cpu_to_le32(sizeof(test_vector_sg1)), @@ -351,14 +351,14 @@ void aspeed_test_sha256_sg(const char *machine, const uint32_t base, } void aspeed_test_sha384_sg(const char *machine, const uint32_t base, - const uint32_t src_addr) + const uint64_t src_addr) { QTestState *s = qtest_init(machine); - const uint32_t src_addr_1 = src_addr + 0x10000; - const uint32_t src_addr_2 = src_addr + 0x20000; - const uint32_t src_addr_3 = src_addr + 0x30000; - const uint32_t digest_addr = src_addr + 0x40000; + const uint64_t src_addr_1 = src_addr + 0x10000; + const uint64_t src_addr_2 = src_addr + 0x20000; + const uint64_t src_addr_3 = src_addr + 0x30000; + const uint64_t digest_addr = src_addr + 0x40000; uint8_t digest[48] = {0}; struct AspeedSgList array[] = { { cpu_to_le32(sizeof(test_vector_sg1)), @@ -402,14 +402,14 @@ void aspeed_test_sha384_sg(const char *machine, const uint32_t base, } void aspeed_test_sha512_sg(const char *machine, const uint32_t base, - const uint32_t src_addr) + const uint64_t src_addr) { QTestState *s = qtest_init(machine); - const uint32_t src_addr_1 = src_addr + 0x10000; - const uint32_t src_addr_2 = src_addr + 0x20000; - const uint32_t src_addr_3 = src_addr + 0x30000; - const uint32_t digest_addr = src_addr + 0x40000; + const uint64_t src_addr_1 = src_addr + 0x10000; + const uint64_t src_addr_2 = src_addr + 0x20000; + const uint64_t src_addr_3 = src_addr + 0x30000; + const uint64_t digest_addr = src_addr + 0x40000; uint8_t digest[64] = {0}; struct AspeedSgList array[] = { { cpu_to_le32(sizeof(test_vector_sg1)), @@ -453,12 +453,12 @@ void aspeed_test_sha512_sg(const char *machine, const uint32_t base, } void aspeed_test_sha256_accum(const char *machine, const uint32_t base, - const uint32_t src_addr) + const uint64_t src_addr) { QTestState *s = qtest_init(machine); - const uint32_t buffer_addr = src_addr + 0x10000; - const uint32_t digest_addr = src_addr + 0x40000; + const uint64_t buffer_addr = src_addr + 0x10000; + const uint64_t digest_addr = src_addr + 0x40000; uint8_t digest[32] = {0}; struct AspeedSgList array[] = { { cpu_to_le32(sizeof(test_vector_accum_256) | SG_LIST_LEN_LAST), @@ -494,12 +494,12 @@ void aspeed_test_sha256_accum(const char *machine, const uint32_t base, } void aspeed_test_sha384_accum(const char *machine, const uint32_t base, - const uint32_t src_addr) + const uint64_t src_addr) { QTestState *s = qtest_init(machine); - const uint32_t buffer_addr = src_addr + 0x10000; - const uint32_t digest_addr = src_addr + 0x40000; + const uint64_t buffer_addr = src_addr + 0x10000; + const uint64_t digest_addr = src_addr + 0x40000; uint8_t digest[48] = {0}; struct AspeedSgList array[] = { { cpu_to_le32(sizeof(test_vector_accum_384) | SG_LIST_LEN_LAST), @@ -535,12 +535,12 @@ void aspeed_test_sha384_accum(const char *machine, const uint32_t base, } void aspeed_test_sha512_accum(const char *machine, const uint32_t base, - const uint32_t src_addr) + const uint64_t src_addr) { QTestState *s = qtest_init(machine); - const uint32_t buffer_addr = src_addr + 0x10000; - const uint32_t digest_addr = src_addr + 0x40000; + const uint64_t buffer_addr = src_addr + 0x10000; + const uint64_t digest_addr = src_addr + 0x40000; uint8_t digest[64] = {0}; struct AspeedSgList array[] = { { cpu_to_le32(sizeof(test_vector_accum_512) | SG_LIST_LEN_LAST), diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h index f4440561de..0382570fa2 100644 --- a/tests/qtest/aspeed-hace-utils.h +++ b/tests/qtest/aspeed-hace-utils.h @@ -51,25 +51,25 @@ struct AspeedMasks { }; void aspeed_test_md5(const char *machine, const uint32_t base, - const uint32_t src_addr); + const uint64_t src_addr); void aspeed_test_sha256(const char *machine, const uint32_t base, - const uint32_t src_addr); + const uint64_t src_addr); void aspeed_test_sha384(const char *machine, const uint32_t base, - const uint32_t src_addr); + const uint64_t src_addr); void aspeed_test_sha512(const char *machine, const uint32_t base, - const uint32_t src_addr); + const uint64_t src_addr); void aspeed_test_sha256_sg(const char *machine, const uint32_t base, - const uint32_t src_addr); + const uint64_t src_addr); void aspeed_test_sha384_sg(const char *machine, const uint32_t base, - const uint32_t src_addr); + const uint64_t src_addr); void aspeed_test_sha512_sg(const char *machine, const uint32_t base, - const uint32_t src_addr); + const uint64_t src_addr); void aspeed_test_sha256_accum(const char *machine, const uint32_t base, - const uint32_t src_addr); + const uint64_t src_addr); void aspeed_test_sha384_accum(const char *machine, const uint32_t base, - const uint32_t src_addr); + const uint64_t src_addr); void aspeed_test_sha512_accum(const char *machine, const uint32_t base, - const uint32_t src_addr); + const uint64_t src_addr); void aspeed_test_addresses(const char *machine, const uint32_t base, const struct AspeedMasks *expected); From 5ced818e42ddb9516137e68556eb03c16a486758 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:57 +0800 Subject: [PATCH 1147/2760] test/qtest/hace: Support 64-bit source and digest addresses for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added "HACE_HASH_SRC_HI" and "HACE_HASH_DIGEST_HI", "HACE_HASH_KEY_BUFF_HI" registers to store upper 32 bits. Updated "write_regs" to handle 64-bit source and digest addresses. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-26-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-hace-utils.c | 2 ++ tests/qtest/aspeed-hace-utils.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c index 1b54870dd4..842bf5630d 100644 --- a/tests/qtest/aspeed-hace-utils.c +++ b/tests/qtest/aspeed-hace-utils.c @@ -157,7 +157,9 @@ static void write_regs(QTestState *s, uint32_t base, uint64_t src, uint32_t length, uint64_t out, uint32_t method) { qtest_writel(s, base + HACE_HASH_SRC, extract64(src, 0, 32)); + qtest_writel(s, base + HACE_HASH_SRC_HI, extract64(src, 32, 32)); qtest_writel(s, base + HACE_HASH_DIGEST, extract64(out, 0, 32)); + qtest_writel(s, base + HACE_HASH_DIGEST_HI, extract64(out, 32, 32)); qtest_writel(s, base + HACE_HASH_DATA_LEN, length); qtest_writel(s, base + HACE_HASH_CMD, HACE_SHA_BE_EN | method); } diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h index 0382570fa2..d8684d3f83 100644 --- a/tests/qtest/aspeed-hace-utils.h +++ b/tests/qtest/aspeed-hace-utils.h @@ -36,6 +36,9 @@ #define HACE_HASH_KEY_BUFF 0x28 #define HACE_HASH_DATA_LEN 0x2c #define HACE_HASH_CMD 0x30 +#define HACE_HASH_SRC_HI 0x90 +#define HACE_HASH_DIGEST_HI 0x94 +#define HACE_HASH_KEY_BUFF_HI 0x98 /* Scatter-Gather Hash */ #define SG_LIST_LEN_LAST BIT(31) From 88d8515fb76ca7b3de8a4cc89264e8494655567e Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:58 +0800 Subject: [PATCH 1148/2760] test/qtest/hace: Support to test upper 32 bits of digest and source addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added "src_hi" and "dest_hi" fields to "AspeedMasks" for 64-bit addresses test. Updated "aspeed_test_addresses" to validate "HACE_HASH_SRC_HI" and "HACE_HASH_DIGEST_HI". Ensured correct masking of 64-bit addresses by checking both lower and upper 32-bit registers. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-27-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-hace-utils.c | 15 ++++++++++++++- tests/qtest/aspeed-hace-utils.h | 2 ++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c index 842bf5630d..cb78f18117 100644 --- a/tests/qtest/aspeed-hace-utils.c +++ b/tests/qtest/aspeed-hace-utils.c @@ -588,30 +588,43 @@ void aspeed_test_addresses(const char *machine, const uint32_t base, */ g_assert_cmphex(qtest_readl(s, base + HACE_CMD), ==, 0); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), ==, 0); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, 0); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); - /* Check that the address masking is correct */ qtest_writel(s, base + HACE_HASH_SRC, 0xffffffff); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, expected->src); + qtest_writel(s, base + HACE_HASH_SRC_HI, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), + ==, expected->src_hi); + qtest_writel(s, base + HACE_HASH_DIGEST, 0xffffffff); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, expected->dest); + qtest_writel(s, base + HACE_HASH_DIGEST_HI, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, + expected->dest_hi); + qtest_writel(s, base + HACE_HASH_DATA_LEN, 0xffffffff); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, expected->len); /* Reset to zero */ qtest_writel(s, base + HACE_HASH_SRC, 0); + qtest_writel(s, base + HACE_HASH_SRC_HI, 0); qtest_writel(s, base + HACE_HASH_DIGEST, 0); + qtest_writel(s, base + HACE_HASH_DIGEST_HI, 0); qtest_writel(s, base + HACE_HASH_DATA_LEN, 0); /* Check that all bits are now zero */ g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), ==, 0); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, 0); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); qtest_quit(s); diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h index d8684d3f83..de8055a1db 100644 --- a/tests/qtest/aspeed-hace-utils.h +++ b/tests/qtest/aspeed-hace-utils.h @@ -51,6 +51,8 @@ struct AspeedMasks { uint32_t src; uint32_t dest; uint32_t len; + uint32_t src_hi; + uint32_t dest_hi; }; void aspeed_test_md5(const char *machine, const uint32_t base, From 823288fc136f8d4b165d2eb573306893e43bcdff Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:09:59 +0800 Subject: [PATCH 1149/2760] test/qtest/hace: Support to validate 64-bit hmac key buffer addresses MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added "key" and "key_hi" fields to "AspeedMasks" for 64-bit addresses test. Updated "aspeed_test_addresses" to validate "HACE_HASH_KEY_BUFF" and "HACE_HASH_KEY_BUFF_HI". Ensured correct masking of 64-bit addresses by checking both lower and upper 32-bit registers. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-28-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed-hace-utils.c | 14 ++++++++++++++ tests/qtest/aspeed-hace-utils.h | 2 ++ tests/qtest/aspeed_hace-test.c | 4 ++++ 3 files changed, 20 insertions(+) diff --git a/tests/qtest/aspeed-hace-utils.c b/tests/qtest/aspeed-hace-utils.c index cb78f18117..0f7f911e5e 100644 --- a/tests/qtest/aspeed-hace-utils.c +++ b/tests/qtest/aspeed-hace-utils.c @@ -591,6 +591,8 @@ void aspeed_test_addresses(const char *machine, const uint32_t base, g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), ==, 0); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF_HI), ==, 0); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); /* Check that the address masking is correct */ @@ -609,6 +611,14 @@ void aspeed_test_addresses(const char *machine, const uint32_t base, g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, expected->dest_hi); + qtest_writel(s, base + HACE_HASH_KEY_BUFF, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF), ==, + expected->key); + + qtest_writel(s, base + HACE_HASH_KEY_BUFF_HI, 0xffffffff); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF_HI), ==, + expected->key_hi); + qtest_writel(s, base + HACE_HASH_DATA_LEN, 0xffffffff); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, expected->len); @@ -618,6 +628,8 @@ void aspeed_test_addresses(const char *machine, const uint32_t base, qtest_writel(s, base + HACE_HASH_SRC_HI, 0); qtest_writel(s, base + HACE_HASH_DIGEST, 0); qtest_writel(s, base + HACE_HASH_DIGEST_HI, 0); + qtest_writel(s, base + HACE_HASH_KEY_BUFF, 0); + qtest_writel(s, base + HACE_HASH_KEY_BUFF_HI, 0); qtest_writel(s, base + HACE_HASH_DATA_LEN, 0); /* Check that all bits are now zero */ @@ -625,6 +637,8 @@ void aspeed_test_addresses(const char *machine, const uint32_t base, g_assert_cmphex(qtest_readl(s, base + HACE_HASH_SRC_HI), ==, 0); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST), ==, 0); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DIGEST_HI), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF), ==, 0); + g_assert_cmphex(qtest_readl(s, base + HACE_HASH_KEY_BUFF_HI), ==, 0); g_assert_cmphex(qtest_readl(s, base + HACE_HASH_DATA_LEN), ==, 0); qtest_quit(s); diff --git a/tests/qtest/aspeed-hace-utils.h b/tests/qtest/aspeed-hace-utils.h index de8055a1db..c8b2ec45af 100644 --- a/tests/qtest/aspeed-hace-utils.h +++ b/tests/qtest/aspeed-hace-utils.h @@ -50,9 +50,11 @@ struct AspeedSgList { struct AspeedMasks { uint32_t src; uint32_t dest; + uint32_t key; uint32_t len; uint32_t src_hi; uint32_t dest_hi; + uint32_t key_hi; }; void aspeed_test_md5(const char *machine, const uint32_t base, diff --git a/tests/qtest/aspeed_hace-test.c b/tests/qtest/aspeed_hace-test.c index 31890d574e..38777020ca 100644 --- a/tests/qtest/aspeed_hace-test.c +++ b/tests/qtest/aspeed_hace-test.c @@ -13,24 +13,28 @@ static const struct AspeedMasks ast1030_masks = { .src = 0x7fffffff, .dest = 0x7ffffff8, + .key = 0x7ffffff8, .len = 0x0fffffff, }; static const struct AspeedMasks ast2600_masks = { .src = 0x7fffffff, .dest = 0x7ffffff8, + .key = 0x7ffffff8, .len = 0x0fffffff, }; static const struct AspeedMasks ast2500_masks = { .src = 0x3fffffff, .dest = 0x3ffffff8, + .key = 0x3fffffc0, .len = 0x0fffffff, }; static const struct AspeedMasks ast2400_masks = { .src = 0x0fffffff, .dest = 0x0ffffff8, + .key = 0x0fffffc0, .len = 0x0fffffff, }; From 5071c229106dc6fc46fd1b7667ffa12e0bc47b1d Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 15 May 2025 16:10:00 +0800 Subject: [PATCH 1150/2760] test/qtest/hace: Add tests for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HACE models in AST2600 and AST2700 are nearly identical. Based on the AST2600 test cases, new tests have been added for AST2700. Implemented test functions for SHA-256, SHA-384, SHA-512, and MD5. Added scatter-gather and accumulation test variants. For AST2700, the HACE controller base address starts at "0x12070000", and the DRAM start address is "0x4_00000000". Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Acked-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250515081008.583578-29-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- tests/qtest/ast2700-hace-test.c | 98 +++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 2 + 2 files changed, 100 insertions(+) create mode 100644 tests/qtest/ast2700-hace-test.c diff --git a/tests/qtest/ast2700-hace-test.c b/tests/qtest/ast2700-hace-test.c new file mode 100644 index 0000000000..a400e2962b --- /dev/null +++ b/tests/qtest/ast2700-hace-test.c @@ -0,0 +1,98 @@ +/* + * QTest testcase for the ASPEED Hash and Crypto Engine + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2025 ASPEED Technology Inc. + */ + +#include "qemu/osdep.h" +#include "libqtest.h" +#include "qemu/bitops.h" +#include "aspeed-hace-utils.h" + +static const struct AspeedMasks as2700_masks = { + .src = 0x7fffffff, + .dest = 0x7ffffff8, + .key = 0x7ffffff8, + .len = 0x0fffffff, + .src_hi = 0x00000003, + .dest_hi = 0x00000003, + .key_hi = 0x00000003, +}; + +/* ast2700 */ +static void test_md5_ast2700(void) +{ + aspeed_test_md5("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha256_ast2700(void) +{ + aspeed_test_sha256("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha256_sg_ast2700(void) +{ + aspeed_test_sha256_sg("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha384_ast2700(void) +{ + aspeed_test_sha384("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha384_sg_ast2700(void) +{ + aspeed_test_sha384_sg("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha512_ast2700(void) +{ + aspeed_test_sha512("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha512_sg_ast2700(void) +{ + aspeed_test_sha512_sg("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha256_accum_ast2700(void) +{ + aspeed_test_sha256_accum("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha384_accum_ast2700(void) +{ + aspeed_test_sha384_accum("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_sha512_accum_ast2700(void) +{ + aspeed_test_sha512_accum("-machine ast2700a1-evb", 0x12070000, 0x400000000); +} + +static void test_addresses_ast2700(void) +{ + aspeed_test_addresses("-machine ast2700a1-evb", 0x12070000, &as2700_masks); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("ast2700/hace/addresses", test_addresses_ast2700); + qtest_add_func("ast2700/hace/sha512", test_sha512_ast2700); + qtest_add_func("ast2700/hace/sha384", test_sha384_ast2700); + qtest_add_func("ast2700/hace/sha256", test_sha256_ast2700); + qtest_add_func("ast2700/hace/md5", test_md5_ast2700); + + qtest_add_func("ast2700/hace/sha512_sg", test_sha512_sg_ast2700); + qtest_add_func("ast2700/hace/sha384_sg", test_sha384_sg_ast2700); + qtest_add_func("ast2700/hace/sha256_sg", test_sha256_sg_ast2700); + + qtest_add_func("ast2700/hace/sha512_accum", test_sha512_accum_ast2700); + qtest_add_func("ast2700/hace/sha384_accum", test_sha384_accum_ast2700); + qtest_add_func("ast2700/hace/sha256_accum", test_sha256_accum_ast2700); + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 26d83cd9dd..43e5a86699 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -217,6 +217,7 @@ qtests_aspeed = \ 'aspeed_smc-test'] qtests_aspeed64 = \ ['ast2700-gpio-test', + 'ast2700-hace-test', 'ast2700-smc-test'] qtests_stm32l4x5 = \ @@ -363,6 +364,7 @@ endif qtests = { 'aspeed_hace-test': files('aspeed-hace-utils.c', 'aspeed_hace-test.c'), 'aspeed_smc-test': files('aspeed-smc-utils.c', 'aspeed_smc-test.c'), + 'ast2700-hace-test': files('aspeed-hace-utils.c', 'ast2700-hace-test.c'), 'ast2700-smc-test': files('aspeed-smc-utils.c', 'ast2700-smc-test.c'), 'bios-tables-test': [io, 'boot-sector.c', 'acpi-utils.c', 'tpm-emu.c'], 'cdrom-test': files('boot-sector.c'), From 5c14d7cbd42326bf35d18cb765015edfc9883272 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 22 May 2025 10:33:02 +0800 Subject: [PATCH 1151/2760] hw/intc/aspeed: Set impl.min_access_size to 4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch explicitly sets ".impl.min_access_size = 4" to match the declared ".valid.min_access_size = 4", enforcing stricter access size checking and preventing inconsistent partial accesses to the interrupt controller registers. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250522023305.2486536-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 33fcbe729c..19f88853d8 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -737,6 +737,7 @@ static const MemoryRegionOps aspeed_intc_ops = { .read = aspeed_intc_read, .write = aspeed_intc_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -747,6 +748,7 @@ static const MemoryRegionOps aspeed_intcio_ops = { .read = aspeed_intcio_read, .write = aspeed_intcio_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -757,6 +759,7 @@ static const MemoryRegionOps aspeed_ssp_intc_ops = { .read = aspeed_intc_read, .write = aspeed_ssp_intc_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -767,6 +770,7 @@ static const MemoryRegionOps aspeed_ssp_intcio_ops = { .read = aspeed_intcio_read, .write = aspeed_ssp_intcio_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -777,6 +781,7 @@ static const MemoryRegionOps aspeed_tsp_intc_ops = { .read = aspeed_intc_read, .write = aspeed_tsp_intc_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, .valid = { .min_access_size = 4, .max_access_size = 4, @@ -787,6 +792,7 @@ static const MemoryRegionOps aspeed_tsp_intcio_ops = { .read = aspeed_intcio_read, .write = aspeed_tsp_intcio_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, .valid = { .min_access_size = 4, .max_access_size = 4, From 567accba673326a37b7c9210f613c86afe2965ff Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 22 May 2025 10:33:03 +0800 Subject: [PATCH 1152/2760] hw/intc/aspeed Fix coding style MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix coding style issues from checkpatch.pl. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250522023305.2486536-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/intc/aspeed_intc.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/intc/aspeed_intc.c b/hw/intc/aspeed_intc.c index 19f88853d8..5cd786dee6 100644 --- a/hw/intc/aspeed_intc.c +++ b/hw/intc/aspeed_intc.c @@ -1001,7 +1001,8 @@ static AspeedINTCIRQ aspeed_2700ssp_intcio_irqs[ASPEED_INTC_MAX_INPINS] = { {5, 5, 1, R_SSPINT165_EN, R_SSPINT165_STATUS}, }; -static void aspeed_2700ssp_intcio_class_init(ObjectClass *klass, const void *data) +static void aspeed_2700ssp_intcio_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); @@ -1069,7 +1070,8 @@ static AspeedINTCIRQ aspeed_2700tsp_intcio_irqs[ASPEED_INTC_MAX_INPINS] = { {5, 5, 1, R_TSPINT165_EN, R_TSPINT165_STATUS}, }; -static void aspeed_2700tsp_intcio_class_init(ObjectClass *klass, const void *data) +static void aspeed_2700tsp_intcio_class_init(ObjectClass *klass, + const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); AspeedINTCClass *aic = ASPEED_INTC_CLASS(klass); From e6941ac106190490d8b455eedc5b368e6d94d4cc Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 22 May 2025 10:33:04 +0800 Subject: [PATCH 1153/2760] hw/arm/aspeed_ast27x0: Fix RAM size detection failure on BE hosts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On big-endian hosts, the aspeed_ram_capacity_write() function previously passed the address of a 64-bit "data" variable directly to address_space_write(), assuming host and guest endianness matched. However, the data is expected to be written in little-endian format to DRAM. On big-endian hosts, this led to incorrect data being written into DRAM, which caused the guest firmware to misdetect the DRAM size. As a result, U-Boot fails to boot and hangs. - Replaces the "address_space_write()" call with "address_space_stl_le()", which performs an explicit 32-bit little-endian write. - Updating the MemoryRegionOps to restrict access to exactly 4 bytes using .valid.{min,max}_access_size = 4 and .impl.min_access_size = 4. Signed-off-by: Jamin Lin Fixes: 7436db1 ("aspeed/soc: fix incorrect dram size for AST2700") Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250522023305.2486536-4-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 1974a25766..82a5ecff04 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -346,8 +346,9 @@ static void aspeed_ram_capacity_write(void *opaque, hwaddr addr, uint64_t data, * If writes the data to the address which is beyond the ram size, * it would write the data to the "address % ram_size". */ - result = address_space_write(&s->dram_as, addr % ram_size, - MEMTXATTRS_UNSPECIFIED, &data, 4); + address_space_stl_le(&s->dram_as, addr % ram_size, data, + MEMTXATTRS_UNSPECIFIED, &result); + if (result != MEMTX_OK) { qemu_log_mask(LOG_GUEST_ERROR, "%s: DRAM write failed, addr:0x%" HWADDR_PRIx @@ -360,9 +361,10 @@ static const MemoryRegionOps aspeed_ram_capacity_ops = { .read = aspeed_ram_capacity_read, .write = aspeed_ram_capacity_write, .endianness = DEVICE_LITTLE_ENDIAN, + .impl.min_access_size = 4, .valid = { - .min_access_size = 1, - .max_access_size = 8, + .min_access_size = 4, + .max_access_size = 4, }, }; From 453b928ab93415ebf5519d946a77e905a2f1ec12 Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 23 May 2025 17:31:38 +0800 Subject: [PATCH 1154/2760] hw/arm/aspeed_ast2700-fc: Add network support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds network support to the ast2700fc machine by initializing the NIC device in the ca35. Signed-off-by: Steven Lee Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250523093144.991408-2-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 125a3ade40..7bf4f2a52d 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -86,6 +86,13 @@ static void ast2700fc_ca35_init(MachineState *machine) AST2700FC_BMC_RAM_SIZE, &error_abort)) { return; } + + for (int i = 0; i < sc->macs_num; i++) { + if (!qemu_configure_nic_device(DEVICE(&soc->ftgmac100[i]), + true, NULL)) { + break; + } + } if (!object_property_set_int(OBJECT(&s->ca35), "hw-strap1", AST2700FC_HW_STRAP1, &error_abort)) { return; From 61162c6f89d1e07788c5bd8a9b7f778102f8a1eb Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 23 May 2025 17:31:39 +0800 Subject: [PATCH 1155/2760] hw/arm/aspeed_ast2700-fc: Reduce ca35 ram size to align with ast2700a1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce ca35 ram size from 2GiB to 1GiB to align with ast2700a1-evb, where the ram-container is defined as 1GiB in its class. Signed-off-by: Steven Lee Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250523093144.991408-3-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index 7bf4f2a52d..f8cb632bca 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -48,7 +48,7 @@ struct Ast2700FCState { bool mmio_exec; }; -#define AST2700FC_BMC_RAM_SIZE (2 * GiB) +#define AST2700FC_BMC_RAM_SIZE (1 * GiB) #define AST2700FC_CM4_DRAM_SIZE (32 * MiB) #define AST2700FC_HW_STRAP1 0x000000C0 From 221d22d830eb1a96f780eec28e6a45286b85fe85 Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 23 May 2025 17:31:40 +0800 Subject: [PATCH 1156/2760] hw/arm/aspeed_ast27x0: Fix unimplemented region overlap with vbootrom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The unimplemented memory region overlaps with the VBootROM address range, causing incorrect memory layout. This patch adjusts the size and start address of the unimplemented region to avoid collision. The IO memory region (ASPEED_DEV_IOMEM) is now moved to 0x20000 to reserve space for VBootROM at 0x0. Although the memory range 0x20000 - 0x10000000 is undefined in the datasheet and should not be required, further testing shows OP-TEE or U-Boot may access 0x400000 during early boot. Removing the unimplemented region causes firmware hangs. To prevent unexpected accesses, retain the region as a safeguard. Signed-off-by: Steven Lee Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250523093144.991408-4-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/aspeed_ast27x0.c b/hw/arm/aspeed_ast27x0.c index 82a5ecff04..6aa3841b69 100644 --- a/hw/arm/aspeed_ast27x0.c +++ b/hw/arm/aspeed_ast27x0.c @@ -23,14 +23,14 @@ #include "qobject/qlist.h" #include "qemu/log.h" -#define AST2700_SOC_IO_SIZE 0x01000000 +#define AST2700_SOC_IO_SIZE 0x00FE0000 #define AST2700_SOC_IOMEM_SIZE 0x01000000 #define AST2700_SOC_DPMCU_SIZE 0x00040000 #define AST2700_SOC_LTPI_SIZE 0x01000000 static const hwaddr aspeed_soc_ast2700_memmap[] = { - [ASPEED_DEV_IOMEM] = 0x00000000, [ASPEED_DEV_VBOOTROM] = 0x00000000, + [ASPEED_DEV_IOMEM] = 0x00020000, [ASPEED_DEV_SRAM] = 0x10000000, [ASPEED_DEV_DPMCU] = 0x11000000, [ASPEED_DEV_IOMEM0] = 0x12000000, From bb1747a39b162bdfbf33d53846ed4e7a99f2e75f Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 23 May 2025 17:31:41 +0800 Subject: [PATCH 1157/2760] hw/arm/aspeed_ast27x0-fc: Map ca35 memory into system memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Map the CA35 memory region as a subregion of system_memory to ensure a valid FlatView. This prevents failures in APIs that rely on the global memory view, such as rom_check_and_register_reset(). Signed-off-by: Steven Lee Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250523093144.991408-5-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed_ast27x0-fc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/aspeed_ast27x0-fc.c b/hw/arm/aspeed_ast27x0-fc.c index f8cb632bca..7087be4288 100644 --- a/hw/arm/aspeed_ast27x0-fc.c +++ b/hw/arm/aspeed_ast27x0-fc.c @@ -68,6 +68,7 @@ static void ast2700fc_ca35_init(MachineState *machine) memory_region_init(&s->ca35_memory, OBJECT(&s->ca35), "ca35-memory", UINT64_MAX); + memory_region_add_subregion(get_system_memory(), 0, &s->ca35_memory); if (!memory_region_init_ram(&s->ca35_dram, OBJECT(&s->ca35), "ca35-dram", AST2700FC_BMC_RAM_SIZE, &error_abort)) { From b21d68c34ec25391f6c1a588bdb5c88bd24bb87f Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 23 May 2025 17:31:42 +0800 Subject: [PATCH 1158/2760] hw/arm/fby35: Map BMC memory into system memory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the BMC memory region as a subregion of system_memory so that modules relying on system memory can operate correctly. Signed-off-by: Steven Lee Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250523093144.991408-6-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/arm/fby35.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/fby35.c b/hw/arm/fby35.c index e123fa69e1..c14fc2efe9 100644 --- a/hw/arm/fby35.c +++ b/hw/arm/fby35.c @@ -77,6 +77,7 @@ static void fby35_bmc_init(Fby35State *s) memory_region_init(&s->bmc_memory, OBJECT(&s->bmc), "bmc-memory", UINT64_MAX); + memory_region_add_subregion(get_system_memory(), 0, &s->bmc_memory); memory_region_init_ram(&s->bmc_dram, OBJECT(&s->bmc), "bmc-dram", FBY35_BMC_RAM_SIZE, &error_abort); From 8eaea4012c215a610b2bd6dcc7812e805e14dd0c Mon Sep 17 00:00:00 2001 From: Steven Lee Date: Fri, 23 May 2025 17:31:43 +0800 Subject: [PATCH 1159/2760] docs: Remove ast2700fc from Aspeed family boards MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ast2700fc machine is now covered in the dedicated ast2700-evb section. Listing it in the general Aspeed board family list is redundant. Signed-off-by: Steven Lee Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250523093144.991408-7-steven_lee@aspeedtech.com Signed-off-by: Cédric Le Goater --- docs/system/arm/aspeed.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index 58a8020eec..43d27d83cb 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -1,4 +1,4 @@ -Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``, ``ast2700fc``, ``bletchley-bmc``, ``fuji-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) +Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``, ``bletchley-bmc``, ``fuji-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) ================================================================================================================================================================================================================================================================================================================================================================================================================================= The QEMU Aspeed machines model BMCs of various OpenPOWER systems and From 4fb54de823e9d5b88b7f708516a2a9bf997d15c2 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 21 May 2025 15:34:08 -0700 Subject: [PATCH 1160/2760] meson: build target libraries with common dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As mentioned in [1], dependencies were missing when compiling per target libraries, thus breaking compilation on certain host systems. We now explicitly add common dependencies to those libraries, so it solves the problem. [1] https://lore.kernel.org/qemu-devel/20250513115637.184940-1-thuth@redhat.com/ Tested-by: Thomas Huth Fixes: 6f4e8a92bbd ("hw/arm: make most of the compilation units common") Signed-off-by: Pierrick Bouvier Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250521223414.248276-2-pierrick.bouvier@linaro.org Signed-off-by: Paolo Bonzini --- meson.build | 75 ++++++++++++++++++++++++++++------------------------- 1 file changed, 40 insertions(+), 35 deletions(-) diff --git a/meson.build b/meson.build index ad2053f968..cbb22f60d1 100644 --- a/meson.build +++ b/meson.build @@ -3242,6 +3242,7 @@ config_devices_mak_list = [] config_devices_h = {} config_target_h = {} config_target_mak = {} +config_base_arch_mak = {} disassemblers = { 'alpha' : ['CONFIG_ALPHA_DIS'], @@ -3433,6 +3434,11 @@ foreach target : target_dirs config_all_devices += config_devices endif config_target_mak += {target: config_target} + + # build a merged config for all targets with the same TARGET_BASE_ARCH + target_base_arch = config_target['TARGET_BASE_ARCH'] + config_base_arch = config_base_arch_mak.get(target_base_arch, {}) + config_target + config_base_arch_mak += {target_base_arch: config_base_arch} endforeach target_dirs = actual_target_dirs @@ -4111,57 +4117,56 @@ common_all = static_library('common', hw_common_arch_libs = {} target_common_arch_libs = {} target_common_system_arch_libs = {} -foreach target : target_dirs +foreach target_base_arch, config_base_arch : config_base_arch_mak config_target = config_target_mak[target] - target_base_arch = config_target['TARGET_BASE_ARCH'] target_inc = [include_directories('target' / target_base_arch)] inc = [common_user_inc + target_inc] + target_common = common_ss.apply(config_target, strict: false) + common_deps = [] + foreach dep: target_common.dependencies() + common_deps += dep.partial_dependency(compile_args: true, includes: true) + endforeach + # prevent common code to access cpu compile time definition, # but still allow access to cpu.h target_c_args = ['-DCPU_DEFS_H'] target_system_c_args = target_c_args + ['-DCOMPILING_SYSTEM_VS_USER', '-DCONFIG_SOFTMMU'] if target_base_arch in hw_common_arch - if target_base_arch not in hw_common_arch_libs - src = hw_common_arch[target_base_arch] - lib = static_library( - 'hw_' + target_base_arch, - build_by_default: false, - sources: src.all_sources() + genh, - include_directories: inc, - c_args: target_system_c_args, - dependencies: src.all_dependencies()) - hw_common_arch_libs += {target_base_arch: lib} - endif + src = hw_common_arch[target_base_arch] + lib = static_library( + 'hw_' + target_base_arch, + build_by_default: false, + sources: src.all_sources() + genh, + include_directories: inc, + c_args: target_system_c_args, + dependencies: src.all_dependencies() + common_deps) + hw_common_arch_libs += {target_base_arch: lib} endif if target_base_arch in target_common_arch - if target_base_arch not in target_common_arch_libs - src = target_common_arch[target_base_arch] - lib = static_library( - 'target_' + target_base_arch, - build_by_default: false, - sources: src.all_sources() + genh, - include_directories: inc, - c_args: target_c_args, - dependencies: src.all_dependencies()) - target_common_arch_libs += {target_base_arch: lib} - endif + src = target_common_arch[target_base_arch] + lib = static_library( + 'target_' + target_base_arch, + build_by_default: false, + sources: src.all_sources() + genh, + include_directories: inc, + c_args: target_c_args, + dependencies: src.all_dependencies() + common_deps) + target_common_arch_libs += {target_base_arch: lib} endif if target_base_arch in target_common_system_arch - if target_base_arch not in target_common_system_arch_libs - src = target_common_system_arch[target_base_arch] - lib = static_library( - 'target_system_' + target_base_arch, - build_by_default: false, - sources: src.all_sources() + genh, - include_directories: inc, - c_args: target_system_c_args, - dependencies: src.all_dependencies()) - target_common_system_arch_libs += {target_base_arch: lib} - endif + src = target_common_system_arch[target_base_arch] + lib = static_library( + 'target_system_' + target_base_arch, + build_by_default: false, + sources: src.all_sources() + genh, + include_directories: inc, + c_args: target_system_c_args, + dependencies: src.all_dependencies() + common_deps) + target_common_system_arch_libs += {target_base_arch: lib} endif endforeach From 0ca26a51791f2601238129c6c2724cc4c604392c Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 21 May 2025 15:34:09 -0700 Subject: [PATCH 1161/2760] hw/arm: remove explicit dependencies listed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Reviewed-by: Thomas Huth Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250521223414.248276-3-pierrick.bouvier@linaro.org Signed-off-by: Paolo Bonzini --- hw/arm/meson.build | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index 5098795f61..d90be8f4c9 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -8,7 +8,7 @@ arm_common_ss.add(when: 'CONFIG_HIGHBANK', if_true: files('highbank.c')) arm_common_ss.add(when: 'CONFIG_INTEGRATOR', if_true: files('integratorcp.c')) arm_common_ss.add(when: 'CONFIG_MICROBIT', if_true: files('microbit.c')) arm_common_ss.add(when: 'CONFIG_MPS3R', if_true: files('mps3r.c')) -arm_common_ss.add(when: 'CONFIG_MUSICPAL', if_true: [pixman, files('musicpal.c')]) +arm_common_ss.add(when: 'CONFIG_MUSICPAL', if_true: [files('musicpal.c')]) arm_common_ss.add(when: 'CONFIG_NETDUINOPLUS2', if_true: files('netduinoplus2.c')) arm_common_ss.add(when: 'CONFIG_OLIMEX_STM32_H405', if_true: files('olimex-stm32-h405.c')) arm_common_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx.c', 'npcm7xx_boards.c')) @@ -79,7 +79,7 @@ arm_common_ss.add(when: 'CONFIG_SX1', if_true: files('omap_sx1.c')) arm_common_ss.add(when: 'CONFIG_VERSATILE', if_true: files('versatilepb.c')) arm_common_ss.add(when: 'CONFIG_VEXPRESS', if_true: files('vexpress.c')) -arm_common_ss.add(fdt, files('boot.c')) +arm_common_ss.add(files('boot.c')) hw_arch += {'arm': arm_ss} hw_common_arch += {'arm': arm_common_ss} From 598a0ba8e6df8c113c77e69ee18c3872fda7b6e9 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 21 May 2025 15:34:10 -0700 Subject: [PATCH 1162/2760] target/arm: remove explicit dependencies listed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Reviewed-by: Thomas Huth Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250521223414.248276-4-pierrick.bouvier@linaro.org Signed-off-by: Paolo Bonzini --- target/arm/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index b404fa5486..2ff7ed6e98 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -28,7 +28,7 @@ arm_user_ss.add(files( 'vfp_fpscr.c', )) -arm_common_system_ss.add(files('cpu.c'), capstone) +arm_common_system_ss.add(files('cpu.c')) arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files( 'cpu32-stubs.c')) arm_common_system_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) From b17b51d325130bc9d0f1189461a9a681fbd554e5 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 21 May 2025 15:34:11 -0700 Subject: [PATCH 1163/2760] meson: apply target config for picking files from lib{system, user} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit semihosting code needs to be included only if CONFIG_SEMIHOSTING is set. However, this is a target configuration, so we need to apply it to the lib{system, user}_ss. As well, this prepares merging lib{system, user}_ss with {system, user}_ss. Acked-by: Richard Henderson Signed-off-by: Pierrick Bouvier Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250521223414.248276-5-pierrick.bouvier@linaro.org Signed-off-by: Paolo Bonzini --- meson.build | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/meson.build b/meson.build index cbb22f60d1..79d123c50e 100644 --- a/meson.build +++ b/meson.build @@ -4081,27 +4081,19 @@ common_ss.add(qom, qemuutil) common_ss.add_all(when: 'CONFIG_SYSTEM_ONLY', if_true: [system_ss]) common_ss.add_all(when: 'CONFIG_USER_ONLY', if_true: user_ss) -libuser_ss = libuser_ss.apply({}) libuser = static_library('user', - libuser_ss.sources() + genh, + libuser_ss.all_sources() + genh, c_args: ['-DCONFIG_USER_ONLY', '-DCOMPILING_SYSTEM_VS_USER'], - dependencies: libuser_ss.dependencies(), + dependencies: libuser_ss.all_dependencies(), build_by_default: false) -libuser = declare_dependency(objects: libuser.extract_all_objects(recursive: false), - dependencies: libuser_ss.dependencies()) -common_ss.add(when: 'CONFIG_USER_ONLY', if_true: libuser) -libsystem_ss = libsystem_ss.apply({}) libsystem = static_library('system', - libsystem_ss.sources() + genh, + libsystem_ss.all_sources() + genh, c_args: ['-DCONFIG_SOFTMMU', '-DCOMPILING_SYSTEM_VS_USER'], - dependencies: libsystem_ss.dependencies(), + dependencies: libsystem_ss.all_dependencies(), build_by_default: false) -libsystem = declare_dependency(objects: libsystem.extract_all_objects(recursive: false), - dependencies: libsystem_ss.dependencies()) -common_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: libsystem) # Note that this library is never used directly (only through extract_objects) # and is not built by default; therefore, source files not used by the build @@ -4343,6 +4335,16 @@ foreach target : target_dirs objects += lib.extract_objects(src.sources()) arch_deps += src.dependencies() endif + if target_type == 'system' + src = libsystem_ss.apply(config_target, strict: false) + objects += libsystem.extract_objects(src.sources()) + arch_deps += src.dependencies() + endif + if target_type == 'user' + src = libuser_ss.apply(config_target, strict: false) + objects += libuser.extract_objects(src.sources()) + arch_deps += src.dependencies() + endif if target_type == 'system' and target_base_arch in hw_common_arch_libs src = hw_common_arch[target_base_arch].apply(config_target, strict: false) lib = hw_common_arch_libs[target_base_arch] From 7ca433244c6dde497bb36184f70b442b399fda3e Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 21 May 2025 15:34:12 -0700 Subject: [PATCH 1164/2760] meson: merge lib{system, user}_ss with {system, user}_ss MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that target configuration can be applied to lib{system, user}_ss, there is no reason to keep that separate from the existing {system, user}_ss. The only difference is that we'll now compile those files with -DCOMPILING_SYSTEM_VS_USER, which removes poison for CONFIG_USER_ONLY and CONFIG_SOFTMMU, without any other side effect. We extract existing system/user code common common libraries to lib{system, user}. To not break existing meson files, we alias libsystem_ss to system_ss and libuser_ss to user_ss, so we can do the cleanup in next commit. Signed-off-by: Pierrick Bouvier Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250521223414.248276-6-pierrick.bouvier@linaro.org Signed-off-by: Paolo Bonzini --- meson.build | 38 +++++++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/meson.build b/meson.build index 79d123c50e..ea54d15f4a 100644 --- a/meson.build +++ b/meson.build @@ -3694,14 +3694,14 @@ io_ss = ss.source_set() qmp_ss = ss.source_set() qom_ss = ss.source_set() system_ss = ss.source_set() -libsystem_ss = ss.source_set() +libsystem_ss = system_ss specific_fuzz_ss = ss.source_set() specific_ss = ss.source_set() rust_devices_ss = ss.source_set() stub_ss = ss.source_set() trace_ss = ss.source_set() user_ss = ss.source_set() -libuser_ss = ss.source_set() +libuser_ss = user_ss util_ss = ss.source_set() # accel modules @@ -4078,21 +4078,19 @@ common_ss.add(hwcore) system_ss.add(authz, blockdev, chardev, crypto, io, qmp) common_ss.add(qom, qemuutil) -common_ss.add_all(when: 'CONFIG_SYSTEM_ONLY', if_true: [system_ss]) -common_ss.add_all(when: 'CONFIG_USER_ONLY', if_true: user_ss) - libuser = static_library('user', - libuser_ss.all_sources() + genh, + user_ss.all_sources() + genh, c_args: ['-DCONFIG_USER_ONLY', '-DCOMPILING_SYSTEM_VS_USER'], - dependencies: libuser_ss.all_dependencies(), + include_directories: common_user_inc, + dependencies: user_ss.all_dependencies(), build_by_default: false) libsystem = static_library('system', - libsystem_ss.all_sources() + genh, + system_ss.all_sources() + genh, c_args: ['-DCONFIG_SOFTMMU', '-DCOMPILING_SYSTEM_VS_USER'], - dependencies: libsystem_ss.all_dependencies(), + dependencies: system_ss.all_dependencies(), build_by_default: false) # Note that this library is never used directly (only through extract_objects) @@ -4101,7 +4099,6 @@ libsystem = static_library('system', common_all = static_library('common', build_by_default: false, sources: common_ss.all_sources() + genh, - include_directories: common_user_inc, implicit_include_directories: false, dependencies: common_ss.all_dependencies()) @@ -4115,10 +4112,20 @@ foreach target_base_arch, config_base_arch : config_base_arch_mak inc = [common_user_inc + target_inc] target_common = common_ss.apply(config_target, strict: false) + target_system = system_ss.apply(config_target, strict: false) + target_user = user_ss.apply(config_target, strict: false) common_deps = [] + system_deps = [] + user_deps = [] foreach dep: target_common.dependencies() common_deps += dep.partial_dependency(compile_args: true, includes: true) endforeach + foreach dep: target_system.dependencies() + system_deps += dep.partial_dependency(compile_args: true, includes: true) + endforeach + foreach dep: target_user.dependencies() + user_deps += dep.partial_dependency(compile_args: true, includes: true) + endforeach # prevent common code to access cpu compile time definition, # but still allow access to cpu.h @@ -4133,7 +4140,7 @@ foreach target_base_arch, config_base_arch : config_base_arch_mak sources: src.all_sources() + genh, include_directories: inc, c_args: target_system_c_args, - dependencies: src.all_dependencies() + common_deps) + dependencies: src.all_dependencies() + common_deps + system_deps) hw_common_arch_libs += {target_base_arch: lib} endif @@ -4145,7 +4152,8 @@ foreach target_base_arch, config_base_arch : config_base_arch_mak sources: src.all_sources() + genh, include_directories: inc, c_args: target_c_args, - dependencies: src.all_dependencies() + common_deps) + dependencies: src.all_dependencies() + common_deps + + system_deps + user_deps) target_common_arch_libs += {target_base_arch: lib} endif @@ -4157,7 +4165,7 @@ foreach target_base_arch, config_base_arch : config_base_arch_mak sources: src.all_sources() + genh, include_directories: inc, c_args: target_system_c_args, - dependencies: src.all_dependencies() + common_deps) + dependencies: src.all_dependencies() + common_deps + system_deps) target_common_system_arch_libs += {target_base_arch: lib} endif endforeach @@ -4336,12 +4344,12 @@ foreach target : target_dirs arch_deps += src.dependencies() endif if target_type == 'system' - src = libsystem_ss.apply(config_target, strict: false) + src = system_ss.apply(config_target, strict: false) objects += libsystem.extract_objects(src.sources()) arch_deps += src.dependencies() endif if target_type == 'user' - src = libuser_ss.apply(config_target, strict: false) + src = user_ss.apply(config_target, strict: false) objects += libuser.extract_objects(src.sources()) arch_deps += src.dependencies() endif From d33717d7fca6a599aa991f771e3ea34b15978cee Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 21 May 2025 15:34:13 -0700 Subject: [PATCH 1165/2760] meson: remove lib{system, user}_ss aliases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250521223414.248276-7-pierrick.bouvier@linaro.org Signed-off-by: Paolo Bonzini --- accel/tcg/meson.build | 8 ++++---- gdbstub/meson.build | 4 ++-- hw/core/meson.build | 4 ++-- meson.build | 2 -- plugins/meson.build | 4 ++-- system/meson.build | 2 +- tcg/meson.build | 4 ++-- 7 files changed, 13 insertions(+), 15 deletions(-) diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 97d5e5a711..575e92bb9e 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -18,15 +18,15 @@ if get_option('plugins') tcg_ss.add(files('plugin-gen.c')) endif -libuser_ss.add_all(tcg_ss) -libsystem_ss.add_all(tcg_ss) +user_ss.add_all(tcg_ss) +system_ss.add_all(tcg_ss) -libuser_ss.add(files( +user_ss.add(files( 'user-exec.c', 'user-exec-stub.c', )) -libsystem_ss.add(files( +system_ss.add(files( 'cputlb.c', 'icount-common.c', 'monitor.c', diff --git a/gdbstub/meson.build b/gdbstub/meson.build index b25db86767..15c666f575 100644 --- a/gdbstub/meson.build +++ b/gdbstub/meson.build @@ -5,13 +5,13 @@ # # We build two versions of gdbstub, one for each mode -libuser_ss.add(files( +user_ss.add(files( 'gdbstub.c', 'syscalls.c', 'user.c' )) -libsystem_ss.add(files( +system_ss.add(files( 'gdbstub.c', 'syscalls.c', 'system.c' diff --git a/hw/core/meson.build b/hw/core/meson.build index 547de6527c..b5a545a0ed 100644 --- a/hw/core/meson.build +++ b/hw/core/meson.build @@ -26,7 +26,7 @@ system_ss.add(when: 'CONFIG_XILINX_AXI', if_true: files('stream.c')) system_ss.add(when: 'CONFIG_PLATFORM_BUS', if_true: files('sysbus-fdt.c')) system_ss.add(when: 'CONFIG_EIF', if_true: [files('eif.c'), zlib, libcbor, gnutls]) -libsystem_ss.add(files( +system_ss.add(files( 'cpu-system.c', 'fw-path-provider.c', 'gpio.c', @@ -46,7 +46,7 @@ libsystem_ss.add(files( 'vm-change-state-handler.c', 'clock-vmstate.c', )) -libuser_ss.add(files( +user_ss.add(files( 'cpu-user.c', 'qdev-user.c', )) diff --git a/meson.build b/meson.build index ea54d15f4a..1c9f1aa91e 100644 --- a/meson.build +++ b/meson.build @@ -3694,14 +3694,12 @@ io_ss = ss.source_set() qmp_ss = ss.source_set() qom_ss = ss.source_set() system_ss = ss.source_set() -libsystem_ss = system_ss specific_fuzz_ss = ss.source_set() specific_ss = ss.source_set() rust_devices_ss = ss.source_set() stub_ss = ss.source_set() trace_ss = ss.source_set() user_ss = ss.source_set() -libuser_ss = user_ss util_ss = ss.source_set() # accel modules diff --git a/plugins/meson.build b/plugins/meson.build index 5383c7b88b..b20edfbabc 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -61,8 +61,8 @@ endif user_ss.add(files('user.c', 'api-user.c')) system_ss.add(files('system.c', 'api-system.c')) -libuser_ss.add(files('api.c', 'core.c')) -libsystem_ss.add(files('api.c', 'core.c')) +user_ss.add(files('api.c', 'core.c')) +system_ss.add(files('api.c', 'core.c')) common_ss.add(files('loader.c')) diff --git a/system/meson.build b/system/meson.build index c2f0082766..7514bf3455 100644 --- a/system/meson.build +++ b/system/meson.build @@ -7,7 +7,7 @@ system_ss.add(files( 'vl.c', ), sdl, libpmem, libdaxctl) -libsystem_ss.add(files( +system_ss.add(files( 'balloon.c', 'bootdevice.c', 'cpus.c', diff --git a/tcg/meson.build b/tcg/meson.build index bd2821e4b5..706a6eb260 100644 --- a/tcg/meson.build +++ b/tcg/meson.build @@ -27,5 +27,5 @@ if host_os == 'linux' tcg_ss.add(files('perf.c')) endif -libuser_ss.add_all(tcg_ss) -libsystem_ss.add_all(tcg_ss) +user_ss.add_all(tcg_ss) +system_ss.add_all(tcg_ss) From ddc25eb40444f4bbcfea276d22beee6494c3f18e Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Wed, 21 May 2025 15:34:14 -0700 Subject: [PATCH 1166/2760] meson: merge hw_common_arch in target_common_system_arch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to keep two different libraries, as both are compiled with exact same flags. As well, rename target common libraries to common_{arch} and system_{arch}, to follow what exists for common and system libraries. Signed-off-by: Pierrick Bouvier Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250521223414.248276-8-pierrick.bouvier@linaro.org Signed-off-by: Paolo Bonzini --- meson.build | 33 ++++++++++++--------------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/meson.build b/meson.build index 1c9f1aa91e..f614d11219 100644 --- a/meson.build +++ b/meson.build @@ -4101,7 +4101,6 @@ common_all = static_library('common', dependencies: common_ss.all_dependencies()) # construct common libraries per base architecture -hw_common_arch_libs = {} target_common_arch_libs = {} target_common_system_arch_libs = {} foreach target_base_arch, config_base_arch : config_base_arch_mak @@ -4130,22 +4129,10 @@ foreach target_base_arch, config_base_arch : config_base_arch_mak target_c_args = ['-DCPU_DEFS_H'] target_system_c_args = target_c_args + ['-DCOMPILING_SYSTEM_VS_USER', '-DCONFIG_SOFTMMU'] - if target_base_arch in hw_common_arch - src = hw_common_arch[target_base_arch] - lib = static_library( - 'hw_' + target_base_arch, - build_by_default: false, - sources: src.all_sources() + genh, - include_directories: inc, - c_args: target_system_c_args, - dependencies: src.all_dependencies() + common_deps + system_deps) - hw_common_arch_libs += {target_base_arch: lib} - endif - if target_base_arch in target_common_arch src = target_common_arch[target_base_arch] lib = static_library( - 'target_' + target_base_arch, + 'common_' + target_base_arch, build_by_default: false, sources: src.all_sources() + genh, include_directories: inc, @@ -4155,10 +4142,20 @@ foreach target_base_arch, config_base_arch : config_base_arch_mak target_common_arch_libs += {target_base_arch: lib} endif + # merge hw_common_arch in target_common_system_arch + if target_base_arch in hw_common_arch + hw_src = hw_common_arch[target_base_arch] + if target_base_arch in target_common_system_arch + target_common_system_arch[target_base_arch].add_all(hw_src) + else + target_common_system_arch += {target_base_arch: hw_src} + endif + endif + if target_base_arch in target_common_system_arch src = target_common_system_arch[target_base_arch] lib = static_library( - 'target_system_' + target_base_arch, + 'system_' + target_base_arch, build_by_default: false, sources: src.all_sources() + genh, include_directories: inc, @@ -4351,12 +4348,6 @@ foreach target : target_dirs objects += libuser.extract_objects(src.sources()) arch_deps += src.dependencies() endif - if target_type == 'system' and target_base_arch in hw_common_arch_libs - src = hw_common_arch[target_base_arch].apply(config_target, strict: false) - lib = hw_common_arch_libs[target_base_arch] - objects += lib.extract_objects(src.sources()) - arch_deps += src.dependencies() - endif if target_type == 'system' and target_base_arch in target_common_system_arch_libs src = target_common_system_arch[target_base_arch].apply(config_target, strict: false) lib = target_common_system_arch_libs[target_base_arch] From d74169e09e1d424aaca138966f460520a0d4dd0d Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 20 May 2025 23:27:46 +0800 Subject: [PATCH 1167/2760] hw/timer/hpet: Reorganize register decoding For Rust HPET, since the commit 519088b7cf6d ("rust: hpet: decode HPET registers into enums"), it decodes register address by checking if the register belongs to global register space. And for C HPET, it checks timer register space first. While both approaches are fine, it's best to be as consistent as possible. Synchronize changes from the rust side to C side. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250520152750.2542612-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- hw/timer/hpet.c | 166 ++++++++++++++++++++++++------------------------ 1 file changed, 84 insertions(+), 82 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index d1b7bc52b7..0fd1337a15 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -426,30 +426,11 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, uint64_t cur_tick; trace_hpet_ram_read(addr); + addr &= ~4; - /*address range of all TN regs*/ - if (addr >= 0x100 && addr <= 0x3ff) { - uint8_t timer_id = (addr - 0x100) / 0x20; - HPETTimer *timer = &s->timer[timer_id]; - - if (timer_id > s->num_timers) { - trace_hpet_timer_id_out_of_range(timer_id); - return 0; - } - - switch (addr & 0x18) { - case HPET_TN_CFG: // including interrupt capabilities - return timer->config >> shift; - case HPET_TN_CMP: // comparator register - return timer->cmp >> shift; - case HPET_TN_ROUTE: - return timer->fsb >> shift; - default: - trace_hpet_ram_read_invalid(); - break; - } - } else { - switch (addr & ~4) { + /*address range of all global regs*/ + if (addr <= 0xff) { + switch (addr) { case HPET_ID: // including HPET_PERIOD return s->capability >> shift; case HPET_CFG: @@ -468,6 +449,26 @@ static uint64_t hpet_ram_read(void *opaque, hwaddr addr, trace_hpet_ram_read_invalid(); break; } + } else { + uint8_t timer_id = (addr - 0x100) / 0x20; + HPETTimer *timer = &s->timer[timer_id]; + + if (timer_id > s->num_timers) { + trace_hpet_timer_id_out_of_range(timer_id); + return 0; + } + + switch (addr & 0x1f) { + case HPET_TN_CFG: // including interrupt capabilities + return timer->config >> shift; + case HPET_TN_CMP: // comparator register + return timer->cmp >> shift; + case HPET_TN_ROUTE: + return timer->fsb >> shift; + default: + trace_hpet_ram_read_invalid(); + break; + } } return 0; } @@ -482,9 +483,67 @@ static void hpet_ram_write(void *opaque, hwaddr addr, uint64_t old_val, new_val, cleared; trace_hpet_ram_write(addr, value); + addr &= ~4; - /*address range of all TN regs*/ - if (addr >= 0x100 && addr <= 0x3ff) { + /*address range of all global regs*/ + if (addr <= 0xff) { + switch (addr) { + case HPET_ID: + return; + case HPET_CFG: + old_val = s->config; + new_val = deposit64(old_val, shift, len, value); + new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); + s->config = new_val; + if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { + /* Enable main counter and interrupt generation. */ + s->hpet_offset = + ticks_to_ns(s->hpet_counter) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); + for (i = 0; i < s->num_timers; i++) { + if (timer_enabled(&s->timer[i]) && (s->isr & (1 << i))) { + update_irq(&s->timer[i], 1); + } + hpet_set_timer(&s->timer[i]); + } + } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) { + /* Halt main counter and disable interrupt generation. */ + s->hpet_counter = hpet_get_ticks(s); + for (i = 0; i < s->num_timers; i++) { + hpet_del_timer(&s->timer[i]); + } + } + /* i8254 and RTC output pins are disabled + * when HPET is in legacy mode */ + if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { + qemu_set_irq(s->pit_enabled, 0); + qemu_irq_lower(s->irqs[0]); + qemu_irq_lower(s->irqs[RTC_ISA_IRQ]); + } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { + qemu_irq_lower(s->irqs[0]); + qemu_set_irq(s->pit_enabled, 1); + qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level); + } + break; + case HPET_STATUS: + new_val = value << shift; + cleared = new_val & s->isr; + for (i = 0; i < s->num_timers; i++) { + if (cleared & (1 << i)) { + update_irq(&s->timer[i], 0); + } + } + break; + case HPET_COUNTER: + if (hpet_enabled(s)) { + trace_hpet_ram_write_counter_write_while_enabled(); + } + s->hpet_counter = deposit64(s->hpet_counter, shift, len, value); + break; + default: + trace_hpet_ram_write_invalid(); + break; + } + } else { uint8_t timer_id = (addr - 0x100) / 0x20; HPETTimer *timer = &s->timer[timer_id]; @@ -550,63 +609,6 @@ static void hpet_ram_write(void *opaque, hwaddr addr, break; } return; - } else { - switch (addr & ~4) { - case HPET_ID: - return; - case HPET_CFG: - old_val = s->config; - new_val = deposit64(old_val, shift, len, value); - new_val = hpet_fixup_reg(new_val, old_val, HPET_CFG_WRITE_MASK); - s->config = new_val; - if (activating_bit(old_val, new_val, HPET_CFG_ENABLE)) { - /* Enable main counter and interrupt generation. */ - s->hpet_offset = - ticks_to_ns(s->hpet_counter) - qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); - for (i = 0; i < s->num_timers; i++) { - if (timer_enabled(&s->timer[i]) && (s->isr & (1 << i))) { - update_irq(&s->timer[i], 1); - } - hpet_set_timer(&s->timer[i]); - } - } else if (deactivating_bit(old_val, new_val, HPET_CFG_ENABLE)) { - /* Halt main counter and disable interrupt generation. */ - s->hpet_counter = hpet_get_ticks(s); - for (i = 0; i < s->num_timers; i++) { - hpet_del_timer(&s->timer[i]); - } - } - /* i8254 and RTC output pins are disabled - * when HPET is in legacy mode */ - if (activating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - qemu_set_irq(s->pit_enabled, 0); - qemu_irq_lower(s->irqs[0]); - qemu_irq_lower(s->irqs[RTC_ISA_IRQ]); - } else if (deactivating_bit(old_val, new_val, HPET_CFG_LEGACY)) { - qemu_irq_lower(s->irqs[0]); - qemu_set_irq(s->pit_enabled, 1); - qemu_set_irq(s->irqs[RTC_ISA_IRQ], s->rtc_irq_level); - } - break; - case HPET_STATUS: - new_val = value << shift; - cleared = new_val & s->isr; - for (i = 0; i < s->num_timers; i++) { - if (cleared & (1 << i)) { - update_irq(&s->timer[i], 0); - } - } - break; - case HPET_COUNTER: - if (hpet_enabled(s)) { - trace_hpet_ram_write_counter_write_while_enabled(); - } - s->hpet_counter = deposit64(s->hpet_counter, shift, len, value); - break; - default: - trace_hpet_ram_write_invalid(); - break; - } } } From 86c54a3a418e462e67444ac4db25b2757fd62079 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 20 May 2025 23:27:49 +0800 Subject: [PATCH 1168/2760] rust: Fix Zhao's email address No one could find Zhao Liu via zhai1.liu@intel.com. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250520152750.2542612-5-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/fw_cfg.rs | 2 +- rust/hw/timer/hpet/src/hpet.rs | 2 +- rust/hw/timer/hpet/src/lib.rs | 2 +- rust/qemu-api/src/bitops.rs | 2 +- rust/qemu-api/src/timer.rs | 2 +- rust/qemu-api/tests/vmstate_tests.rs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs index aa08d28351..6c10316104 100644 --- a/rust/hw/timer/hpet/src/fw_cfg.rs +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -1,5 +1,5 @@ // Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu +// Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later use std::ptr::addr_of_mut; diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/hpet.rs index 779681d650..e3ba62b287 100644 --- a/rust/hw/timer/hpet/src/hpet.rs +++ b/rust/hw/timer/hpet/src/hpet.rs @@ -1,5 +1,5 @@ // Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu +// Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later use std::{ diff --git a/rust/hw/timer/hpet/src/lib.rs b/rust/hw/timer/hpet/src/lib.rs index 1954584a87..141aae229d 100644 --- a/rust/hw/timer/hpet/src/lib.rs +++ b/rust/hw/timer/hpet/src/lib.rs @@ -1,5 +1,5 @@ // Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu +// Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later //! # HPET QEMU Device Model diff --git a/rust/qemu-api/src/bitops.rs b/rust/qemu-api/src/bitops.rs index 023ec1a998..b1e3a530ab 100644 --- a/rust/qemu-api/src/bitops.rs +++ b/rust/qemu-api/src/bitops.rs @@ -1,5 +1,5 @@ // Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu +// Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later //! This module provides bit operation extensions to integer types. diff --git a/rust/qemu-api/src/timer.rs b/rust/qemu-api/src/timer.rs index 868bd88575..0a2d111d49 100644 --- a/rust/qemu-api/src/timer.rs +++ b/rust/qemu-api/src/timer.rs @@ -1,5 +1,5 @@ // Copyright (C) 2024 Intel Corporation. -// Author(s): Zhao Liu +// Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later use std::{ diff --git a/rust/qemu-api/tests/vmstate_tests.rs b/rust/qemu-api/tests/vmstate_tests.rs index ad0fc5cd5d..bded836eb6 100644 --- a/rust/qemu-api/tests/vmstate_tests.rs +++ b/rust/qemu-api/tests/vmstate_tests.rs @@ -1,5 +1,5 @@ // Copyright (C) 2025 Intel Corporation. -// Author(s): Zhao Liu +// Author(s): Zhao Liu // SPDX-License-Identifier: GPL-2.0-or-later use std::{ From aef5ac8624c7b826ae2adde48bc6997286ee1303 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Tue, 20 May 2025 23:27:50 +0800 Subject: [PATCH 1169/2760] rust: Fix the typos in doc These typos are found by "cargo spellcheck". Though it outputs a lot of noise and false positives, there still are some real typos. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250520152750.2542612-6-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 4 ++-- rust/qemu-api/src/qom.rs | 4 ++-- rust/qemu-api/src/vmstate.rs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index bde3be65c5..bd5cee0464 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -480,13 +480,13 @@ impl PL011Registers { } impl PL011State { - /// Initializes a pre-allocated, unitialized instance of `PL011State`. + /// Initializes a pre-allocated, uninitialized instance of `PL011State`. /// /// # Safety /// /// `self` must point to a correctly sized and aligned location for the /// `PL011State` type. It must not be called more than once on the same - /// location/instance. All its fields are expected to hold unitialized + /// location/instance. All its fields are expected to hold uninitialized /// values with the sole exception of `parent_obj`. unsafe fn init(&mut self) { static PL011_OPS: MemoryRegionOps = MemoryRegionOpsBuilder::::new() diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 41e5a5e29a..14f98fee60 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -291,7 +291,7 @@ pub unsafe trait ObjectType: Sized { } /// Return the receiver as a const raw pointer to Object. - /// This is preferrable to `as_object_mut_ptr()` if a C + /// This is preferable to `as_object_mut_ptr()` if a C /// function only needs a `const Object *`. fn as_object_ptr(&self) -> *const bindings::Object { self.as_object().as_ptr() @@ -485,7 +485,7 @@ pub trait ObjectImpl: ObjectType + IsA { /// `INSTANCE_INIT` functions have been called. const INSTANCE_POST_INIT: Option = None; - /// Called on descendent classes after all parent class initialization + /// Called on descendant classes after all parent class initialization /// has occurred, but before the class itself is initialized. This /// is only useful if a class is not a leaf, and can be used to undo /// the effects of copying the contents of the parent's class struct diff --git a/rust/qemu-api/src/vmstate.rs b/rust/qemu-api/src/vmstate.rs index 9c8b2398e9..812f390d78 100644 --- a/rust/qemu-api/src/vmstate.rs +++ b/rust/qemu-api/src/vmstate.rs @@ -9,7 +9,7 @@ //! * [`vmstate_unused!`](crate::vmstate_unused) and //! [`vmstate_of!`](crate::vmstate_of), which are used to express the //! migration format for a struct. This is based on the [`VMState`] trait, -//! which is defined by all migrateable types. +//! which is defined by all migratable types. //! //! * [`impl_vmstate_forward`](crate::impl_vmstate_forward) and //! [`impl_vmstate_bitsized`](crate::impl_vmstate_bitsized), which help with From 734a1e9eeed2c791c8906d0ee08ad5c9b1f41fa0 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 13 May 2025 12:18:12 +0200 Subject: [PATCH 1170/2760] rust: hpet: rename hpet module to "device" Follow a similar convention as pl011. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/{hpet.rs => device.rs} | 0 rust/hw/timer/hpet/src/lib.rs | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename rust/hw/timer/hpet/src/{hpet.rs => device.rs} (100%) diff --git a/rust/hw/timer/hpet/src/hpet.rs b/rust/hw/timer/hpet/src/device.rs similarity index 100% rename from rust/hw/timer/hpet/src/hpet.rs rename to rust/hw/timer/hpet/src/device.rs diff --git a/rust/hw/timer/hpet/src/lib.rs b/rust/hw/timer/hpet/src/lib.rs index 141aae229d..a95cf14ac9 100644 --- a/rust/hw/timer/hpet/src/lib.rs +++ b/rust/hw/timer/hpet/src/lib.rs @@ -7,7 +7,7 @@ //! This library implements a device model for the IA-PC HPET (High //! Precision Event Timers) device in QEMU. +pub mod device; pub mod fw_cfg; -pub mod hpet; pub const TYPE_HPET: &::std::ffi::CStr = c"hpet"; From 341ed3eae4179273788d2f74579862a10e18cf81 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 20 May 2025 14:53:29 +0200 Subject: [PATCH 1171/2760] target/i386/emulate: more lflags cleanups Signed-off-by: Paolo Bonzini --- target/i386/emulate/x86_flags.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/i386/emulate/x86_flags.c b/target/i386/emulate/x86_flags.c index 47bc19778c..cc138c7749 100644 --- a/target/i386/emulate/x86_flags.c +++ b/target/i386/emulate/x86_flags.c @@ -255,19 +255,19 @@ void lflags_to_rflags(CPUX86State *env) void rflags_to_lflags(CPUX86State *env) { - target_ulong cf_xor_of; + target_ulong cf_af, cf_xor_of; + /* Leave the low byte zero so that parity is always even... */ + env->cc_dst = !(env->eflags & CC_Z) << 8; + + /* ... and therefore cc_src always uses opposite polarity. */ env->cc_src = CC_P; env->cc_src ^= env->eflags & (CC_S | CC_P); /* rotate right by one to move CF and AF into the carry-out positions */ - env->cc_src |= ( - (env->eflags >> 1) | - (env->eflags << (TARGET_LONG_BITS - 1))) & (CC_C | CC_A); + cf_af = env->eflags & (CC_C | CC_A); + env->cc_src |= ((cf_af >> 1) | (cf_af << (TARGET_LONG_BITS - 1))); - cf_xor_of = (env->eflags & (CC_C | CC_O)) + (CC_O - CC_C); + cf_xor_of = ((env->eflags & (CC_C | CC_O)) + (CC_O - CC_C)) & CC_O; env->cc_src |= -cf_xor_of & LF_MASK_PO; - - /* Leave the low byte zero so that parity is not affected. */ - env->cc_dst = !(env->eflags & CC_Z) << 8; } From 4ec799dd17dcbb0fa4e90e685d5d6fcf8f72338a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?= Date: Fri, 25 Apr 2025 11:35:13 +0200 Subject: [PATCH 1172/2760] target/sparc: don't set FSR_NVA when comparing unordered floats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FSR_NVA should be set when one of the operands is a signaling NaN or when using FCMPEx instructions. But those cases are already handled within check_ieee_exception or floatxx_compare functions. Otherwise, it should be left untouched. FTR, this was detected by inf-compare-[5678] tests within gcc testsuites. Signed-off-by: Clément Chigot Message-Id: <20250425093513.863289-1-chigot@adacore.com> Acked-by: Mark Cave-Ayland Reviewed-by: Richard Henderson Signed-off-by: Mark Cave-Ayland --- target/sparc/fop_helper.c | 1 - 1 file changed, 1 deletion(-) diff --git a/target/sparc/fop_helper.c b/target/sparc/fop_helper.c index a49334150d..29fd166438 100644 --- a/target/sparc/fop_helper.c +++ b/target/sparc/fop_helper.c @@ -445,7 +445,6 @@ static uint32_t finish_fcmp(CPUSPARCState *env, FloatRelation r, uintptr_t ra) case float_relation_greater: return 2; case float_relation_unordered: - env->fsr |= FSR_NVA; return 3; } g_assert_not_reached(); From 428d1789df911bc863e55eed2d8f33ce991cbd09 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 21 May 2025 08:37:08 +0200 Subject: [PATCH 1173/2760] docs/about: Belatedly document tightening of QMP device_add checking Commit 4d8b0f0a9536 (v6.2.0) deprecated incorrectly typed device_add arguments. Commit be93fd53723c (qdev-monitor: avoid QemuOpts in QMP device_add) fixed them for v9.2.0, but neglected to update documentation. Do that now. Cc: Stefan Hajnoczi Signed-off-by: Markus Armbruster Message-ID: <20250521063711.29840-2-armbru@redhat.com> Reviewed-by: Stefan Hajnoczi [Commit message typo corrected] --- docs/about/deprecated.rst | 14 -------------- docs/about/removed-features.rst | 9 +++++++++ 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 44d3427e98..9665bc6fcf 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -187,20 +187,6 @@ threads (for example, it only reports source side of multifd threads, without reporting any destination threads, or non-multifd source threads). For debugging purpose, please use ``-name $VM,debug-threads=on`` instead. -Incorrectly typed ``device_add`` arguments (since 6.2) -'''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Due to shortcomings in the internal implementation of ``device_add``, QEMU -incorrectly accepts certain invalid arguments: Any object or list arguments are -silently ignored. Other argument types are not checked, but an implicit -conversion happens, so that e.g. string values can be assigned to integer -device properties or vice versa. - -This is a bug in QEMU that will be fixed in the future so that previously -accepted incorrect commands will return an error. Users should make sure that -all arguments passed to ``device_add`` are consistent with the documented -property types. - Host Architectures ------------------ diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 063284d4f8..92b5ba6218 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -722,6 +722,15 @@ Use ``multifd-channels`` instead. Use ``multifd-compression`` instead. +Incorrectly typed ``device_add`` arguments (since 9.2) +'''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Due to shortcomings in the internal implementation of ``device_add``, +QEMU used to incorrectly accept certain invalid arguments. Any object +or list arguments were silently ignored. Other argument types were not +checked, but an implicit conversion happened, so that e.g. string +values could be assigned to integer device properties or vice versa. + QEMU Machine Protocol (QMP) events ---------------------------------- From c2fb6eaeb9d479a80b104914f459a0c6c32e5a88 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 21 May 2025 08:37:09 +0200 Subject: [PATCH 1174/2760] qapi/migration: Deprecate migrate argument @detach Argument @detach has always been ignored. Start the clock to get rid of it. Cc: Peter Xu Cc: Fabiano Rosas Signed-off-by: Markus Armbruster Message-ID: <20250521063711.29840-3-armbru@redhat.com> ACKed-by: Peter Krempa Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu --- docs/about/deprecated.rst | 5 +++++ qapi/migration.json | 18 +++++++++--------- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 9665bc6fcf..ef4ea84e69 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -179,6 +179,11 @@ Use ``job-dismiss`` instead. Use ``job-finalize`` instead. +``migrate`` argument ``detach`` (since 10.1) +'''''''''''''''''''''''''''''''''''''''''''' + +This argument has always been ignored. + ``query-migrationthreads`` (since 9.2) '''''''''''''''''''''''''''''''''''''' diff --git a/qapi/migration.json b/qapi/migration.json index 8b9c53595c..ecd266f98e 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -1660,6 +1660,10 @@ # # @resume: resume one paused migration, default "off". (since 3.0) # +# Features: +# +# @deprecated: Argument @detach is deprecated. +# # Since: 0.14 # # .. admonition:: Notes @@ -1668,19 +1672,14 @@ # migration's progress and final result (this information is # provided by the 'status' member). # -# 2. All boolean arguments default to false. -# -# 3. The user Monitor's "detach" argument is invalid in QMP and -# should not be used. -# -# 4. The uri argument should have the Uniform Resource Identifier +# 2. The uri argument should have the Uniform Resource Identifier # of default destination VM. This connection will be bound to # default network. # -# 5. For now, number of migration streams is restricted to one, +# 3. For now, number of migration streams is restricted to one, # i.e. number of items in 'channels' list is just 1. # -# 6. The 'uri' and 'channels' arguments are mutually exclusive; +# 4. The 'uri' and 'channels' arguments are mutually exclusive; # exactly one of the two should be present. # # .. qmp-example:: @@ -1724,7 +1723,8 @@ { 'command': 'migrate', 'data': {'*uri': 'str', '*channels': [ 'MigrationChannel' ], - '*detach': 'bool', '*resume': 'bool' } } + '*detach': { 'type': 'bool', 'features': [ 'deprecated' ] }, + '*resume': 'bool' } } ## # @migrate-incoming: From 977dfcd552d0bef725f89bcabcedeb51593000ab Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 21 May 2025 08:37:10 +0200 Subject: [PATCH 1175/2760] docs/about/deprecated: Move deprecation notes to tidy up order The deprecation notes within a section are mostly in version order. Move the few that aren't so they are. Signed-off-by: Markus Armbruster Message-ID: <20250521063711.29840-4-armbru@redhat.com> Reviewed-by: Eric Blake --- docs/about/deprecated.rst | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index ef4ea84e69..4715d1ede5 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -148,6 +148,14 @@ options are removed in favor of using explicit ``blockdev-create`` and ``blockdev-add`` calls. See :doc:`/interop/live-block-operations` for details. +``query-migrationthreads`` (since 9.2) +'''''''''''''''''''''''''''''''''''''' + +To be removed with no replacement, as it reports only a limited set of +threads (for example, it only reports source side of multifd threads, +without reporting any destination threads, or non-multifd source threads). +For debugging purpose, please use ``-name $VM,debug-threads=on`` instead. + ``block-job-pause`` (since 10.1) '''''''''''''''''''''''''''''''' @@ -184,14 +192,6 @@ Use ``job-finalize`` instead. This argument has always been ignored. -``query-migrationthreads`` (since 9.2) -'''''''''''''''''''''''''''''''''''''' - -To be removed with no replacement, as it reports only a limited set of -threads (for example, it only reports source side of multifd threads, -without reporting any destination threads, or non-multifd source threads). -For debugging purpose, please use ``-name $VM,debug-threads=on`` instead. - Host Architectures ------------------ @@ -522,14 +522,6 @@ PCIe passthrough shall be the mainline solution. CPU device properties ''''''''''''''''''''' -``pcommit`` on x86 (since 9.1) -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -The PCOMMIT instruction was never included in any physical processor. -It was implemented as a no-op instruction in TCG up to QEMU 9.0, but -only with ``-cpu max`` (which does not guarantee migration compatibility -across versions). - ``pmu-num=n`` on RISC-V CPUs (since 8.2) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -539,6 +531,14 @@ be calculated with ``((2 ^ n) - 1) << 3``. The least significant three bits must be left clear. +``pcommit`` on x86 (since 9.1) +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The PCOMMIT instruction was never included in any physical processor. +It was implemented as a no-op instruction in TCG up to QEMU 9.0, but +only with ``-cpu max`` (which does not guarantee migration compatibility +across versions). + Backwards compatibility ----------------------- From 662b85aae131e7cb8dd8b03c9e44a95bc87573ca Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 21 May 2025 08:37:11 +0200 Subject: [PATCH 1176/2760] docs/about/removed-features: Move removal notes to tidy up order The removal notes within a section are mostly in version order. Move the few that aren't so they are. Signed-off-by: Markus Armbruster Message-ID: <20250521063711.29840-5-armbru@redhat.com> Reviewed-by: Eric Blake --- docs/about/removed-features.rst | 60 ++++++++++++++++----------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 92b5ba6218..4819cb4665 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -162,6 +162,12 @@ specified with ``-mem-path`` can actually provide the guest RAM configured with The ``name`` parameter of the ``-net`` option was a synonym for the ``id`` parameter, which should now be used instead. +RISC-V firmware not booted by default (removed in 5.1) +'''''''''''''''''''''''''''''''''''''''''''''''''''''' + +QEMU 5.1 changes the default behaviour from ``-bios none`` to ``-bios default`` +for the RISC-V ``virt`` machine and ``sifive_u`` machine. + ``-numa node,mem=...`` (removed in 5.1) ''''''''''''''''''''''''''''''''''''''' @@ -324,12 +330,6 @@ devices. Drives the board doesn't pick up can no longer be used with This option was undocumented and not used in the field. Use ``-device usb-ccid`` instead. -RISC-V firmware not booted by default (removed in 5.1) -'''''''''''''''''''''''''''''''''''''''''''''''''''''' - -QEMU 5.1 changes the default behaviour from ``-bios none`` to ``-bios default`` -for the RISC-V ``virt`` machine and ``sifive_u`` machine. - ``-no-quit`` (removed in 7.0) ''''''''''''''''''''''''''''' @@ -911,14 +911,6 @@ The RISC-V no MMU cpus have been removed. The two CPUs: ``rv32imacu-nommu`` and ``rv64imacu-nommu`` can no longer be used. Instead the MMU status can be specified via the CPU ``mmu`` option when using the ``rv32`` or ``rv64`` CPUs. -RISC-V 'any' CPU type ``-cpu any`` (removed in 9.2) -''''''''''''''''''''''''''''''''''''''''''''''''''' - -The 'any' CPU type was introduced back in 2018 and was around since the -initial RISC-V QEMU port. Its usage was always been unclear: users don't know -what to expect from a CPU called 'any', and in fact the CPU does not do anything -special that isn't already done by the default CPUs rv32/rv64. - ``compat`` property of server class POWER CPUs (removed in 6.0) ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' @@ -965,6 +957,14 @@ The CRIS architecture was pulled from Linux in 4.17 and the compiler was no longer packaged in any distro making it harder to run the ``check-tcg`` tests. +RISC-V 'any' CPU type ``-cpu any`` (removed in 9.2) +''''''''''''''''''''''''''''''''''''''''''''''''''' + +The 'any' CPU type was introduced back in 2018 and was around since the +initial RISC-V QEMU port. Its usage was always been unclear: users don't know +what to expect from a CPU called 'any', and in fact the CPU does not do anything +special that isn't already done by the default CPUs rv32/rv64. + System accelerators ------------------- @@ -975,18 +975,18 @@ Userspace local APIC with KVM (x86, removed in 8.0) a local APIC. The ``split`` setting is supported, as is using ``-M kernel-irqchip=off`` when the CPU does not have a local APIC. -HAXM (``-accel hax``) (removed in 8.2) -'''''''''''''''''''''''''''''''''''''' - -The HAXM project has been retired (see https://github.com/intel/haxm#status). -Use "whpx" (on Windows) or "hvf" (on macOS) instead. - MIPS "Trap-and-Emulate" KVM support (removed in 8.0) '''''''''''''''''''''''''''''''''''''''''''''''''''' The MIPS "Trap-and-Emulate" KVM host and guest support was removed from Linux in 2021, and is not supported anymore by QEMU either. +HAXM (``-accel hax``) (removed in 8.2) +'''''''''''''''''''''''''''''''''''''' + +The HAXM project has been retired (see https://github.com/intel/haxm#status). +Use "whpx" (on Windows) or "hvf" (on macOS) instead. + System emulator machines ------------------------ @@ -1044,16 +1044,6 @@ Aspeed ``swift-bmc`` machine (removed in 7.0) This machine was removed because it was unused. Alternative AST2500 based OpenPOWER machines are ``witherspoon-bmc`` and ``romulus-bmc``. -Aspeed ``tacoma-bmc`` machine (removed in 10.0) -''''''''''''''''''''''''''''''''''''''''''''''' - -The ``tacoma-bmc`` machine was removed because it didn't bring much -compared to the ``rainier-bmc`` machine. Also, the ``tacoma-bmc`` was -a board used for bring up of the AST2600 SoC that never left the -labs. It can be easily replaced by the ``rainier-bmc`` machine, which -was the actual final product, or by the ``ast2600-evb`` with some -tweaks. - ppc ``taihu`` machine (removed in 7.2) ''''''''''''''''''''''''''''''''''''''''''''' @@ -1084,6 +1074,16 @@ for all machine types using the PXA2xx and OMAP2 SoCs. We are also dropping the ``cheetah`` OMAP1 board, because we don't have any test images for it and don't know of anybody who does. +Aspeed ``tacoma-bmc`` machine (removed in 10.0) +''''''''''''''''''''''''''''''''''''''''''''''' + +The ``tacoma-bmc`` machine was removed because it didn't bring much +compared to the ``rainier-bmc`` machine. Also, the ``tacoma-bmc`` was +a board used for bring up of the AST2600 SoC that never left the +labs. It can be easily replaced by the ``rainier-bmc`` machine, which +was the actual final product, or by the ``ast2600-evb`` with some +tweaks. + ppc ``ref405ep`` machine (removed in 10.0) '''''''''''''''''''''''''''''''''''''''''' From 319b0c8d077401f51bf6314039b82db20d5267ee Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 24 May 2025 15:40:12 +0100 Subject: [PATCH 1177/2760] accel/tcg: Fix atomic_mmu_lookup vs TLB_FORCE_SLOW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we moved TLB_MMIO and TLB_DISCARD_WRITE to TLB_SLOW_FLAGS_MASK, we failed to update atomic_mmu_lookup to properly reconstruct flags. Fixes: 24b5e0fdb543 ("include/exec: Move TLB_MMIO, TLB_DISCARD_WRITE to slow flags") Reported-by: Jonathan Cameron Tested-by: Jonathan Cameron Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 5f6d7c601c..86d0deb08c 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1871,8 +1871,12 @@ static void *atomic_mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi, goto stop_the_world; } - /* Collect tlb flags for read. */ + /* Finish collecting tlb flags for both read and write. */ + full = &cpu->neg.tlb.d[mmu_idx].fulltlb[index]; tlb_addr |= tlbe->addr_read; + tlb_addr &= TLB_FLAGS_MASK & ~TLB_FORCE_SLOW; + tlb_addr |= full->slow_flags[MMU_DATA_STORE]; + tlb_addr |= full->slow_flags[MMU_DATA_LOAD]; /* Notice an IO access or a needs-MMU-lookup access */ if (unlikely(tlb_addr & (TLB_MMIO | TLB_DISCARD_WRITE))) { @@ -1882,13 +1886,12 @@ static void *atomic_mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi, } hostaddr = (void *)((uintptr_t)addr + tlbe->addend); - full = &cpu->neg.tlb.d[mmu_idx].fulltlb[index]; if (unlikely(tlb_addr & TLB_NOTDIRTY)) { notdirty_write(cpu, addr, size, full, retaddr); } - if (unlikely(tlb_addr & TLB_FORCE_SLOW)) { + if (unlikely(tlb_addr & TLB_WATCHPOINT)) { int wp_flags = 0; if (full->slow_flags[MMU_DATA_STORE] & TLB_WATCHPOINT) { @@ -1897,10 +1900,8 @@ static void *atomic_mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi, if (full->slow_flags[MMU_DATA_LOAD] & TLB_WATCHPOINT) { wp_flags |= BP_MEM_READ; } - if (wp_flags) { - cpu_check_watchpoint(cpu, addr, size, - full->attrs, wp_flags, retaddr); - } + cpu_check_watchpoint(cpu, addr, size, + full->attrs, wp_flags, retaddr); } return hostaddr; From 556d05d1e2ac687463ce2877cb4acd1b0589deed Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 15 May 2025 10:46:41 -0700 Subject: [PATCH 1178/2760] system/main: comment lock rationale Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250515174641.4000309-1-pierrick.bouvier@linaro.org> --- system/main.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/system/main.c b/system/main.c index 1c02206734..b8f7157cc3 100644 --- a/system/main.c +++ b/system/main.c @@ -69,8 +69,21 @@ int (*qemu_main)(void) = os_darwin_cfrunloop_main; int main(int argc, char **argv) { qemu_init(argc, argv); + + /* + * qemu_init acquires the BQL and replay mutex lock. BQL is acquired when + * initializing cpus, to block associated threads until initialization is + * complete. Replay_mutex lock is acquired on initialization, because it + * must be held when configuring icount_mode. + * + * On MacOS, qemu main event loop runs in a background thread, as main + * thread must be reserved for UI. Thus, we need to transfer lock ownership, + * and the simplest way to do that is to release them, and reacquire them + * from qemu_default_main. + */ bql_unlock(); replay_mutex_unlock(); + if (qemu_main) { QemuThread main_loop_thread; qemu_thread_create(&main_loop_thread, "qemu_main", From 19f036726a416c9248c19befe544a2d30b099a25 Mon Sep 17 00:00:00 2001 From: Andreas Schwab Date: Tue, 20 May 2025 16:07:37 +0200 Subject: [PATCH 1179/2760] linux-user: implement pgid field of /proc/self/stat Signed-off-by: Andreas Schwab Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: --- linux-user/syscall.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 23b901b713..fc37028597 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -8235,6 +8235,9 @@ static int open_self_stat(CPUArchState *cpu_env, int fd) } else if (i == 3) { /* ppid */ g_string_printf(buf, FMT_pid " ", getppid()); + } else if (i == 4) { + /* pgid */ + g_string_printf(buf, FMT_pid " ", getpgrp()); } else if (i == 19) { /* num_threads */ int cpus = 0; From 67f2d507ca444cfff993c1a05df3aaa4346a372c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Feb 2025 12:30:21 -0800 Subject: [PATCH 1180/2760] target/microblaze: Split out mb_unaligned_access_internal Use an explicit 64-bit type for the address to store in EAR. Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- target/microblaze/helper.c | 64 +++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 28 deletions(-) diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index 9203192483..5fe81e4b16 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -27,6 +27,42 @@ #include "qemu/host-utils.h" #include "exec/log.h" + +G_NORETURN +static void mb_unaligned_access_internal(CPUState *cs, uint64_t addr, + uintptr_t retaddr) +{ + CPUMBState *env = cpu_env(cs); + uint32_t esr, iflags; + + /* Recover the pc and iflags from the corresponding insn_start. */ + cpu_restore_state(cs, retaddr); + iflags = env->iflags; + + qemu_log_mask(CPU_LOG_INT, + "Unaligned access addr=0x%" PRIx64 " pc=%x iflags=%x\n", + addr, env->pc, iflags); + + esr = ESR_EC_UNALIGNED_DATA; + if (likely(iflags & ESR_ESS_FLAG)) { + esr |= iflags & ESR_ESS_MASK; + } else { + qemu_log_mask(LOG_UNIMP, "Unaligned access without ESR_ESS_FLAG\n"); + } + + env->ear = addr; + env->esr = esr; + cs->exception_index = EXCP_HW_EXCP; + cpu_loop_exit(cs); +} + +void mb_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + mb_unaligned_access_internal(cs, addr, retaddr); +} + #ifndef CONFIG_USER_ONLY static bool mb_cpu_access_is_secure(MicroBlazeCPU *cpu, MMUAccessType access_type) @@ -269,31 +305,3 @@ bool mb_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } #endif /* !CONFIG_USER_ONLY */ - -void mb_cpu_do_unaligned_access(CPUState *cs, vaddr addr, - MMUAccessType access_type, - int mmu_idx, uintptr_t retaddr) -{ - MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - uint32_t esr, iflags; - - /* Recover the pc and iflags from the corresponding insn_start. */ - cpu_restore_state(cs, retaddr); - iflags = cpu->env.iflags; - - qemu_log_mask(CPU_LOG_INT, - "Unaligned access addr=" TARGET_FMT_lx " pc=%x iflags=%x\n", - (target_ulong)addr, cpu->env.pc, iflags); - - esr = ESR_EC_UNALIGNED_DATA; - if (likely(iflags & ESR_ESS_FLAG)) { - esr |= iflags & ESR_ESS_MASK; - } else { - qemu_log_mask(LOG_UNIMP, "Unaligned access without ESR_ESS_FLAG\n"); - } - - cpu->env.ear = addr; - cpu->env.esr = esr; - cs->exception_index = EXCP_HW_EXCP; - cpu_loop_exit(cs); -} From 3f8d6b432dbdd63eecfb454d59d36b08f76c0c95 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 25 May 2025 16:10:03 +0100 Subject: [PATCH 1181/2760] target/microblaze: Introduce helper_unaligned_access Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- target/microblaze/helper.c | 7 +++++++ target/microblaze/helper.h | 12 ++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/target/microblaze/helper.c b/target/microblaze/helper.c index 5fe81e4b16..ef0e2f973f 100644 --- a/target/microblaze/helper.c +++ b/target/microblaze/helper.c @@ -26,6 +26,7 @@ #include "exec/target_page.h" #include "qemu/host-utils.h" #include "exec/log.h" +#include "exec/helper-proto.h" G_NORETURN @@ -64,6 +65,12 @@ void mb_cpu_do_unaligned_access(CPUState *cs, vaddr addr, } #ifndef CONFIG_USER_ONLY + +void HELPER(unaligned_access)(CPUMBState *env, uint64_t addr) +{ + mb_unaligned_access_internal(env_cpu(env), addr, GETPC()); +} + static bool mb_cpu_access_is_secure(MicroBlazeCPU *cpu, MMUAccessType access_type) { diff --git a/target/microblaze/helper.h b/target/microblaze/helper.h index f740835fcb..41f56a5601 100644 --- a/target/microblaze/helper.h +++ b/target/microblaze/helper.h @@ -20,12 +20,12 @@ DEF_HELPER_FLAGS_3(fcmp_ne, TCG_CALL_NO_WG, i32, env, i32, i32) DEF_HELPER_FLAGS_3(fcmp_ge, TCG_CALL_NO_WG, i32, env, i32, i32) DEF_HELPER_FLAGS_2(pcmpbf, TCG_CALL_NO_RWG_SE, i32, i32, i32) -#if !defined(CONFIG_USER_ONLY) -DEF_HELPER_FLAGS_3(mmu_read, TCG_CALL_NO_RWG, i32, env, i32, i32) -DEF_HELPER_FLAGS_4(mmu_write, TCG_CALL_NO_RWG, void, env, i32, i32, i32) -#endif - DEF_HELPER_FLAGS_2(stackprot, TCG_CALL_NO_WG, void, env, tl) - DEF_HELPER_FLAGS_2(get, TCG_CALL_NO_RWG, i32, i32, i32) DEF_HELPER_FLAGS_3(put, TCG_CALL_NO_RWG, void, i32, i32, i32) + +#ifndef CONFIG_USER_ONLY +DEF_HELPER_FLAGS_3(mmu_read, TCG_CALL_NO_RWG, i32, env, i32, i32) +DEF_HELPER_FLAGS_4(mmu_write, TCG_CALL_NO_RWG, void, env, i32, i32, i32) +DEF_HELPER_FLAGS_2(unaligned_access, TCG_CALL_NO_WG, noreturn, env, i64) +#endif From 526b0d364af081792e469adabbc67aeaca8a4343 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Feb 2025 12:46:17 -0800 Subject: [PATCH 1182/2760] target/microblaze: Split out mb_transaction_failed_internal Use an explicit 64-bit type for the address to store in EAR. Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- target/microblaze/op_helper.c | 70 +++++++++++++++++++++-------------- 1 file changed, 42 insertions(+), 28 deletions(-) diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index 9e838dfa15..4c39207a55 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -393,38 +393,52 @@ void helper_mmu_write(CPUMBState *env, uint32_t ext, uint32_t rn, uint32_t v) mmu_write(env, ext, rn, v); } -void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, - unsigned size, MMUAccessType access_type, - int mmu_idx, MemTxAttrs attrs, - MemTxResult response, uintptr_t retaddr) +static void mb_transaction_failed_internal(CPUState *cs, hwaddr physaddr, + uint64_t addr, unsigned size, + MMUAccessType access_type, + uintptr_t retaddr) { - MicroBlazeCPU *cpu = MICROBLAZE_CPU(cs); - CPUMBState *env = &cpu->env; - - qemu_log_mask(CPU_LOG_INT, "Transaction failed: vaddr 0x%" VADDR_PRIx - " physaddr 0x" HWADDR_FMT_plx " size %d access type %s\n", - addr, physaddr, size, - access_type == MMU_INST_FETCH ? "INST_FETCH" : - (access_type == MMU_DATA_LOAD ? "DATA_LOAD" : "DATA_STORE")); - - if (!(env->msr & MSR_EE)) { - return; + CPUMBState *env = cpu_env(cs); + MicroBlazeCPU *cpu = env_archcpu(env); + const char *access_name = "INVALID"; + bool take = env->msr & MSR_EE; + uint32_t esr = ESR_EC_DATA_BUS; + + switch (access_type) { + case MMU_INST_FETCH: + access_name = "INST_FETCH"; + esr = ESR_EC_INSN_BUS; + take &= cpu->cfg.iopb_bus_exception; + break; + case MMU_DATA_LOAD: + access_name = "DATA_LOAD"; + take &= cpu->cfg.dopb_bus_exception; + break; + case MMU_DATA_STORE: + access_name = "DATA_STORE"; + take &= cpu->cfg.dopb_bus_exception; + break; } - if (access_type == MMU_INST_FETCH) { - if (!cpu->cfg.iopb_bus_exception) { - return; - } - env->esr = ESR_EC_INSN_BUS; - } else { - if (!cpu->cfg.dopb_bus_exception) { - return; - } - env->esr = ESR_EC_DATA_BUS; + qemu_log_mask(CPU_LOG_INT, "Transaction failed: addr 0x%" PRIx64 + "physaddr 0x" HWADDR_FMT_plx " size %d access-type %s (%s)\n", + addr, physaddr, size, access_name, + take ? "TAKEN" : "DROPPED"); + + if (take) { + env->esr = esr; + env->ear = addr; + cs->exception_index = EXCP_HW_EXCP; + cpu_loop_exit_restore(cs, retaddr); } +} - env->ear = addr; - cs->exception_index = EXCP_HW_EXCP; - cpu_loop_exit_restore(cs, retaddr); +void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, + unsigned size, MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr) +{ + mb_transaction_failed_internal(cs, physaddr, addr, size, + access_type, retaddr); } #endif From beea772666fb1bb86136042fd8ee7140a01bb36f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Feb 2025 13:20:58 -0800 Subject: [PATCH 1183/2760] target/microblaze: Implement extended address load/store out of line Use helpers and address_space_ld/st instead of inline loads and stores. This allows us to perform operations on physical addresses wider than virtual addresses. Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- target/microblaze/helper.h | 10 +++++++ target/microblaze/op_helper.c | 40 +++++++++++++++++++++++++++ target/microblaze/translate.c | 52 +++++++++++++++++++++++++++-------- 3 files changed, 90 insertions(+), 12 deletions(-) diff --git a/target/microblaze/helper.h b/target/microblaze/helper.h index 41f56a5601..ef4fad9b91 100644 --- a/target/microblaze/helper.h +++ b/target/microblaze/helper.h @@ -28,4 +28,14 @@ DEF_HELPER_FLAGS_3(put, TCG_CALL_NO_RWG, void, i32, i32, i32) DEF_HELPER_FLAGS_3(mmu_read, TCG_CALL_NO_RWG, i32, env, i32, i32) DEF_HELPER_FLAGS_4(mmu_write, TCG_CALL_NO_RWG, void, env, i32, i32, i32) DEF_HELPER_FLAGS_2(unaligned_access, TCG_CALL_NO_WG, noreturn, env, i64) +DEF_HELPER_FLAGS_2(lbuea, TCG_CALL_NO_WG, i32, env, i64) +DEF_HELPER_FLAGS_2(lhuea_be, TCG_CALL_NO_WG, i32, env, i64) +DEF_HELPER_FLAGS_2(lhuea_le, TCG_CALL_NO_WG, i32, env, i64) +DEF_HELPER_FLAGS_2(lwea_be, TCG_CALL_NO_WG, i32, env, i64) +DEF_HELPER_FLAGS_2(lwea_le, TCG_CALL_NO_WG, i32, env, i64) +DEF_HELPER_FLAGS_3(sbea, TCG_CALL_NO_WG, void, env, i32, i64) +DEF_HELPER_FLAGS_3(shea_be, TCG_CALL_NO_WG, void, env, i32, i64) +DEF_HELPER_FLAGS_3(shea_le, TCG_CALL_NO_WG, void, env, i32, i64) +DEF_HELPER_FLAGS_3(swea_be, TCG_CALL_NO_WG, void, env, i32, i64) +DEF_HELPER_FLAGS_3(swea_le, TCG_CALL_NO_WG, void, env, i32, i64) #endif diff --git a/target/microblaze/op_helper.c b/target/microblaze/op_helper.c index 4c39207a55..b8365b3b1d 100644 --- a/target/microblaze/op_helper.c +++ b/target/microblaze/op_helper.c @@ -382,6 +382,8 @@ void helper_stackprot(CPUMBState *env, target_ulong addr) } #if !defined(CONFIG_USER_ONLY) +#include "system/memory.h" + /* Writes/reads to the MMU's special regs end up here. */ uint32_t helper_mmu_read(CPUMBState *env, uint32_t ext, uint32_t rn) { @@ -441,4 +443,42 @@ void mb_cpu_transaction_failed(CPUState *cs, hwaddr physaddr, vaddr addr, mb_transaction_failed_internal(cs, physaddr, addr, size, access_type, retaddr); } + +#define LD_EA(NAME, TYPE, FUNC) \ +uint32_t HELPER(NAME)(CPUMBState *env, uint64_t ea) \ +{ \ + CPUState *cs = env_cpu(env); \ + MemTxResult txres; \ + TYPE ret = FUNC(cs->as, ea, MEMTXATTRS_UNSPECIFIED, &txres); \ + if (unlikely(txres != MEMTX_OK)) { \ + mb_transaction_failed_internal(cs, ea, ea, sizeof(TYPE), \ + MMU_DATA_LOAD, GETPC()); \ + } \ + return ret; \ +} + +LD_EA(lbuea, uint8_t, address_space_ldub) +LD_EA(lhuea_be, uint16_t, address_space_lduw_be) +LD_EA(lhuea_le, uint16_t, address_space_lduw_le) +LD_EA(lwea_be, uint32_t, address_space_ldl_be) +LD_EA(lwea_le, uint32_t, address_space_ldl_le) + +#define ST_EA(NAME, TYPE, FUNC) \ +void HELPER(NAME)(CPUMBState *env, uint32_t data, uint64_t ea) \ +{ \ + CPUState *cs = env_cpu(env); \ + MemTxResult txres; \ + FUNC(cs->as, ea, data, MEMTXATTRS_UNSPECIFIED, &txres); \ + if (unlikely(txres != MEMTX_OK)) { \ + mb_transaction_failed_internal(cs, ea, ea, sizeof(TYPE), \ + MMU_DATA_STORE, GETPC()); \ + } \ +} + +ST_EA(sbea, uint8_t, address_space_stb) +ST_EA(shea_be, uint16_t, address_space_stw_be) +ST_EA(shea_le, uint16_t, address_space_stw_le) +ST_EA(swea_be, uint32_t, address_space_stl_be) +ST_EA(swea_le, uint32_t, address_space_stl_le) + #endif diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 671b1ae4db..3d9756391e 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -700,6 +700,20 @@ static void record_unaligned_ess(DisasContext *dc, int rd, tcg_set_insn_start_param(dc->base.insn_start, 1, iflags); } + +static void gen_alignment_check_ea(DisasContext *dc, TCGv_i64 ea, int rb, + int rd, MemOp size, bool store) +{ + if (rb && (dc->tb_flags & MSR_EE) && dc->cfg->unaligned_exceptions) { + TCGLabel *over = gen_new_label(); + + record_unaligned_ess(dc, rd, size, store); + + tcg_gen_brcondi_i64(TCG_COND_TSTEQ, ea, (1 << size) - 1, over); + gen_helper_unaligned_access(tcg_env, ea); + gen_set_label(over); + } +} #endif static inline MemOp mo_endian(DisasContext *dc) @@ -765,10 +779,11 @@ static bool trans_lbuea(DisasContext *dc, arg_typea *arg) return true; } #ifdef CONFIG_USER_ONLY - return true; + g_assert_not_reached(); #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_UB, MMU_NOMMU_IDX, false); + gen_helper_lbuea(reg_for_write(dc, arg->rd), tcg_env, addr); + return true; #endif } @@ -796,10 +811,13 @@ static bool trans_lhuea(DisasContext *dc, arg_typea *arg) return true; } #ifdef CONFIG_USER_ONLY - return true; + g_assert_not_reached(); #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_UW, MMU_NOMMU_IDX, false); + gen_alignment_check_ea(dc, addr, arg->rb, arg->rd, MO_16, false); + (mo_endian(dc) == MO_BE ? gen_helper_lhuea_be : gen_helper_lhuea_le) + (reg_for_write(dc, arg->rd), tcg_env, addr); + return true; #endif } @@ -827,10 +845,13 @@ static bool trans_lwea(DisasContext *dc, arg_typea *arg) return true; } #ifdef CONFIG_USER_ONLY - return true; + g_assert_not_reached(); #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_load(dc, arg->rd, addr, MO_UL, MMU_NOMMU_IDX, false); + gen_alignment_check_ea(dc, addr, arg->rb, arg->rd, MO_32, false); + (mo_endian(dc) == MO_BE ? gen_helper_lwea_be : gen_helper_lwea_le) + (reg_for_write(dc, arg->rd), tcg_env, addr); + return true; #endif } @@ -918,10 +939,11 @@ static bool trans_sbea(DisasContext *dc, arg_typea *arg) return true; } #ifdef CONFIG_USER_ONLY - return true; + g_assert_not_reached(); #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_UB, MMU_NOMMU_IDX, false); + gen_helper_sbea(tcg_env, reg_for_read(dc, arg->rd), addr); + return true; #endif } @@ -949,10 +971,13 @@ static bool trans_shea(DisasContext *dc, arg_typea *arg) return true; } #ifdef CONFIG_USER_ONLY - return true; + g_assert_not_reached(); #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_UW, MMU_NOMMU_IDX, false); + gen_alignment_check_ea(dc, addr, arg->rb, arg->rd, MO_16, true); + (mo_endian(dc) == MO_BE ? gen_helper_shea_be : gen_helper_shea_le) + (tcg_env, reg_for_read(dc, arg->rd), addr); + return true; #endif } @@ -980,10 +1005,13 @@ static bool trans_swea(DisasContext *dc, arg_typea *arg) return true; } #ifdef CONFIG_USER_ONLY - return true; + g_assert_not_reached(); #else TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); - return do_store(dc, arg->rd, addr, MO_UL, MMU_NOMMU_IDX, false); + gen_alignment_check_ea(dc, addr, arg->rb, arg->rd, MO_32, true); + (mo_endian(dc) == MO_BE ? gen_helper_swea_be : gen_helper_swea_le) + (tcg_env, reg_for_read(dc, arg->rd), addr); + return true; #endif } From 8cea8bd4d3909b7828310a0f76d5194d1bf0095a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Feb 2025 13:24:08 -0800 Subject: [PATCH 1184/2760] target/microblaze: Use uint64_t for CPUMBState.ear Use an explicit 64-bit type for EAR. Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- target/microblaze/cpu.h | 2 +- target/microblaze/translate.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/microblaze/cpu.h b/target/microblaze/cpu.h index 6ad8643f2e..3ce28b302f 100644 --- a/target/microblaze/cpu.h +++ b/target/microblaze/cpu.h @@ -248,7 +248,7 @@ struct CPUArchState { uint32_t pc; uint32_t msr; /* All bits of MSR except MSR[C] and MSR[CC] */ uint32_t msr_c; /* MSR[C], in low bit; other bits must be 0 */ - target_ulong ear; + uint64_t ear; uint32_t esr; uint32_t fsr; uint32_t btr; diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 3d9756391e..b1fc9e5624 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -1857,7 +1857,7 @@ void mb_cpu_dump_state(CPUState *cs, FILE *f, int flags) } qemu_fprintf(f, "\nesr=0x%04x fsr=0x%02x btr=0x%08x edr=0x%x\n" - "ear=0x" TARGET_FMT_lx " slr=0x%x shr=0x%x\n", + "ear=0x%" PRIx64 " slr=0x%x shr=0x%x\n", env->esr, env->fsr, env->btr, env->edr, env->ear, env->slr, env->shr); From 14c1d81354c425d98423c41f60db5907f70cf216 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Feb 2025 13:28:18 -0800 Subject: [PATCH 1185/2760] target/microblaze: Use TCGv_i64 for compute_ldst_addr_ea Use an explicit 64-bit type for extended addresses. Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- target/microblaze/translate.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index b1fc9e5624..dc597b36e6 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -660,23 +660,23 @@ static TCGv compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm) } #ifndef CONFIG_USER_ONLY -static TCGv compute_ldst_addr_ea(DisasContext *dc, int ra, int rb) +static TCGv_i64 compute_ldst_addr_ea(DisasContext *dc, int ra, int rb) { int addr_size = dc->cfg->addr_size; - TCGv ret = tcg_temp_new(); + TCGv_i64 ret = tcg_temp_new_i64(); if (addr_size == 32 || ra == 0) { if (rb) { - tcg_gen_extu_i32_tl(ret, cpu_R[rb]); + tcg_gen_extu_i32_i64(ret, cpu_R[rb]); } else { - tcg_gen_movi_tl(ret, 0); + return tcg_constant_i64(0); } } else { if (rb) { tcg_gen_concat_i32_i64(ret, cpu_R[rb], cpu_R[ra]); } else { - tcg_gen_extu_i32_tl(ret, cpu_R[ra]); - tcg_gen_shli_tl(ret, ret, 32); + tcg_gen_extu_i32_i64(ret, cpu_R[ra]); + tcg_gen_shli_i64(ret, ret, 32); } if (addr_size < 64) { /* Mask off out of range bits. */ @@ -781,7 +781,7 @@ static bool trans_lbuea(DisasContext *dc, arg_typea *arg) #ifdef CONFIG_USER_ONLY g_assert_not_reached(); #else - TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + TCGv_i64 addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); gen_helper_lbuea(reg_for_write(dc, arg->rd), tcg_env, addr); return true; #endif @@ -813,7 +813,7 @@ static bool trans_lhuea(DisasContext *dc, arg_typea *arg) #ifdef CONFIG_USER_ONLY g_assert_not_reached(); #else - TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + TCGv_i64 addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); gen_alignment_check_ea(dc, addr, arg->rb, arg->rd, MO_16, false); (mo_endian(dc) == MO_BE ? gen_helper_lhuea_be : gen_helper_lhuea_le) (reg_for_write(dc, arg->rd), tcg_env, addr); @@ -847,7 +847,7 @@ static bool trans_lwea(DisasContext *dc, arg_typea *arg) #ifdef CONFIG_USER_ONLY g_assert_not_reached(); #else - TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + TCGv_i64 addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); gen_alignment_check_ea(dc, addr, arg->rb, arg->rd, MO_32, false); (mo_endian(dc) == MO_BE ? gen_helper_lwea_be : gen_helper_lwea_le) (reg_for_write(dc, arg->rd), tcg_env, addr); @@ -941,7 +941,7 @@ static bool trans_sbea(DisasContext *dc, arg_typea *arg) #ifdef CONFIG_USER_ONLY g_assert_not_reached(); #else - TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + TCGv_i64 addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); gen_helper_sbea(tcg_env, reg_for_read(dc, arg->rd), addr); return true; #endif @@ -973,7 +973,7 @@ static bool trans_shea(DisasContext *dc, arg_typea *arg) #ifdef CONFIG_USER_ONLY g_assert_not_reached(); #else - TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + TCGv_i64 addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); gen_alignment_check_ea(dc, addr, arg->rb, arg->rd, MO_16, true); (mo_endian(dc) == MO_BE ? gen_helper_shea_be : gen_helper_shea_le) (tcg_env, reg_for_read(dc, arg->rd), addr); @@ -1007,7 +1007,7 @@ static bool trans_swea(DisasContext *dc, arg_typea *arg) #ifdef CONFIG_USER_ONLY g_assert_not_reached(); #else - TCGv addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); + TCGv_i64 addr = compute_ldst_addr_ea(dc, arg->ra, arg->rb); gen_alignment_check_ea(dc, addr, arg->rb, arg->rd, MO_32, true); (mo_endian(dc) == MO_BE ? gen_helper_swea_be : gen_helper_swea_le) (tcg_env, reg_for_read(dc, arg->rd), addr); From 17ac97a9581fa9dd9c433d7562506a514f7292b3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Feb 2025 13:34:52 -0800 Subject: [PATCH 1186/2760] target/microblaze: Fix printf format in mmu_translate Use TARGET_FMT_lx to match the target_ulong type of vaddr. Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- target/microblaze/mmu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/microblaze/mmu.c b/target/microblaze/mmu.c index 95a12e16f8..8703ff5c65 100644 --- a/target/microblaze/mmu.c +++ b/target/microblaze/mmu.c @@ -172,7 +172,8 @@ unsigned int mmu_translate(MicroBlazeCPU *cpu, MicroBlazeMMULookup *lu, } done: qemu_log_mask(CPU_LOG_MMU, - "MMU vaddr=%" PRIx64 " rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", + "MMU vaddr=0x" TARGET_FMT_lx + " rw=%d tlb_wr=%d tlb_ex=%d hit=%d\n", vaddr, rw, tlb_wr, tlb_ex, hit); return hit; } From b52ee0c1a4205c8d698c37557401d2f55e071fba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Feb 2025 13:37:39 -0800 Subject: [PATCH 1187/2760] target/microblaze: Use TARGET_LONG_BITS == 32 for system mode Now that the extended address instructions are handled separately from virtual addresses, we can narrow the emulation to 32-bit. Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- configs/targets/microblaze-softmmu.mak | 4 +--- configs/targets/microblazeel-softmmu.mak | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/configs/targets/microblaze-softmmu.mak b/configs/targets/microblaze-softmmu.mak index 23457d0ae6..bab7b498c2 100644 --- a/configs/targets/microblaze-softmmu.mak +++ b/configs/targets/microblaze-softmmu.mak @@ -3,6 +3,4 @@ TARGET_BIG_ENDIAN=y # needed by boot.c TARGET_NEED_FDT=y TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml -# System mode can address up to 64 bits via lea/sea instructions. -# TODO: These bypass the mmu, so we could emulate these differently. -TARGET_LONG_BITS=64 +TARGET_LONG_BITS=32 diff --git a/configs/targets/microblazeel-softmmu.mak b/configs/targets/microblazeel-softmmu.mak index c82c509623..8aee7ebc5c 100644 --- a/configs/targets/microblazeel-softmmu.mak +++ b/configs/targets/microblazeel-softmmu.mak @@ -2,6 +2,4 @@ TARGET_ARCH=microblaze # needed by boot.c TARGET_NEED_FDT=y TARGET_XML_FILES=gdb-xml/microblaze-core.xml gdb-xml/microblaze-stack-protect.xml -# System mode can address up to 64 bits via lea/sea instructions. -# TODO: These bypass the mmu, so we could emulate these differently. -TARGET_LONG_BITS=64 +TARGET_LONG_BITS=32 From bd07403fc146a9bfc5312404a63f24cc48701c97 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Feb 2025 13:54:18 -0800 Subject: [PATCH 1188/2760] target/microblaze: Drop DisasContext.r0 Return a constant 0 from reg_for_read, and a new temporary from reg_for_write. Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- target/microblaze/translate.c | 24 ++---------------------- 1 file changed, 2 insertions(+), 22 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index dc597b36e6..047d97e2c5 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -63,9 +63,6 @@ typedef struct DisasContext { DisasContextBase base; const MicroBlazeCPUConfig *cfg; - TCGv_i32 r0; - bool r0_set; - /* Decoder. */ uint32_t ext_imm; unsigned int tb_flags; @@ -179,14 +176,7 @@ static TCGv_i32 reg_for_read(DisasContext *dc, int reg) if (likely(reg != 0)) { return cpu_R[reg]; } - if (!dc->r0_set) { - if (dc->r0 == NULL) { - dc->r0 = tcg_temp_new_i32(); - } - tcg_gen_movi_i32(dc->r0, 0); - dc->r0_set = true; - } - return dc->r0; + return tcg_constant_i32(0); } static TCGv_i32 reg_for_write(DisasContext *dc, int reg) @@ -194,10 +184,7 @@ static TCGv_i32 reg_for_write(DisasContext *dc, int reg) if (likely(reg != 0)) { return cpu_R[reg]; } - if (dc->r0 == NULL) { - dc->r0 = tcg_temp_new_i32(); - } - return dc->r0; + return tcg_temp_new_i32(); } static bool do_typea(DisasContext *dc, arg_typea *arg, bool side_effects, @@ -1635,8 +1622,6 @@ static void mb_tr_init_disas_context(DisasContextBase *dcb, CPUState *cs) dc->cfg = &cpu->cfg; dc->tb_flags = dc->base.tb->flags; dc->ext_imm = dc->base.tb->cs_base; - dc->r0 = NULL; - dc->r0_set = false; dc->mem_index = cpu_mmu_index(cs, false); dc->jmp_cond = dc->tb_flags & D_FLAG ? TCG_COND_ALWAYS : TCG_COND_NEVER; dc->jmp_dest = -1; @@ -1675,11 +1660,6 @@ static void mb_tr_translate_insn(DisasContextBase *dcb, CPUState *cs) trap_illegal(dc, true); } - if (dc->r0) { - dc->r0 = NULL; - dc->r0_set = false; - } - /* Discard the imm global when its contents cannot be used. */ if ((dc->tb_flags & ~dc->tb_flags_to_set) & IMM_FLAG) { tcg_gen_discard_i32(cpu_imm); From 36a9529e60e09b0d0b6b5ebad614255c97bf9322 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 12 Feb 2025 13:56:32 -0800 Subject: [PATCH 1189/2760] target/microblaze: Simplify compute_ldst_addr_type{a,b} Require TCGv_i32 and TCGv be identical, so drop the extensions. Return constants when possible instead of a mov into a temporary. Return register inputs unchanged when possible. Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- target/microblaze/translate.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/target/microblaze/translate.c b/target/microblaze/translate.c index 047d97e2c5..5098a1db4d 100644 --- a/target/microblaze/translate.c +++ b/target/microblaze/translate.c @@ -606,19 +606,18 @@ DO_TYPEBI(xori, false, tcg_gen_xori_i32) static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) { - TCGv ret = tcg_temp_new(); + TCGv ret; /* If any of the regs is r0, set t to the value of the other reg. */ if (ra && rb) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_add_i32(tmp, cpu_R[ra], cpu_R[rb]); - tcg_gen_extu_i32_tl(ret, tmp); + ret = tcg_temp_new_i32(); + tcg_gen_add_i32(ret, cpu_R[ra], cpu_R[rb]); } else if (ra) { - tcg_gen_extu_i32_tl(ret, cpu_R[ra]); + ret = cpu_R[ra]; } else if (rb) { - tcg_gen_extu_i32_tl(ret, cpu_R[rb]); + ret = cpu_R[rb]; } else { - tcg_gen_movi_tl(ret, 0); + ret = tcg_constant_i32(0); } if ((ra == 1 || rb == 1) && dc->cfg->stackprot) { @@ -629,15 +628,16 @@ static TCGv compute_ldst_addr_typea(DisasContext *dc, int ra, int rb) static TCGv compute_ldst_addr_typeb(DisasContext *dc, int ra, int imm) { - TCGv ret = tcg_temp_new(); + TCGv ret; /* If any of the regs is r0, set t to the value of the other reg. */ - if (ra) { - TCGv_i32 tmp = tcg_temp_new_i32(); - tcg_gen_addi_i32(tmp, cpu_R[ra], imm); - tcg_gen_extu_i32_tl(ret, tmp); + if (ra && imm) { + ret = tcg_temp_new_i32(); + tcg_gen_addi_i32(ret, cpu_R[ra], imm); + } else if (ra) { + ret = cpu_R[ra]; } else { - tcg_gen_movi_tl(ret, (uint32_t)imm); + ret = tcg_constant_i32(imm); } if (ra == 1 && dc->cfg->stackprot) { From 9cfcf8c3b7b7a95e754a9fce565a88c6c76ce128 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 1 May 2025 11:16:31 -0700 Subject: [PATCH 1190/2760] tcg: Drop TCGContext.tlb_dyn_max_bits This was an extremely minor optimization for aarch64 and x86_64, to use a 32-bit AND instruction when the guest softmmu tlb maximum was sufficiently small. Both hosts can simply use a 64-bit AND insn instead. Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 2 -- include/tcg/tcg.h | 1 - tcg/aarch64/tcg-target.c.inc | 6 +----- tcg/i386/tcg-target.c.inc | 6 ++---- 4 files changed, 3 insertions(+), 12 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 451b383aa8..6735a40ade 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -24,7 +24,6 @@ #include "tcg/tcg.h" #include "exec/mmap-lock.h" #include "tb-internal.h" -#include "tlb-bounds.h" #include "exec/tb-flush.h" #include "qemu/cacheinfo.h" #include "qemu/target-info.h" @@ -316,7 +315,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s) #ifdef CONFIG_SOFTMMU tcg_ctx->page_bits = TARGET_PAGE_BITS; tcg_ctx->page_mask = TARGET_PAGE_MASK; - tcg_ctx->tlb_dyn_max_bits = CPU_TLB_DYN_MAX_BITS; #endif tcg_ctx->guest_mo = cpu->cc->tcg_ops->guest_default_memory_order; diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 3fa5a7aed2..e440c889c8 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -368,7 +368,6 @@ struct TCGContext { int page_mask; uint8_t page_bits; - uint8_t tlb_dyn_max_bits; TCGBar guest_mo; TCGRegSet reserved_regs; diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 4cb647cb34..6356a81c2a 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1661,7 +1661,6 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, unsigned s_mask = (1u << s_bits) - 1; unsigned mem_index = get_mmuidx(oi); TCGReg addr_adj; - TCGType mask_type; uint64_t compare_mask; ldst = new_ldst_label(s); @@ -1669,9 +1668,6 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->oi = oi; ldst->addr_reg = addr_reg; - mask_type = (s->page_bits + s->tlb_dyn_max_bits > 32 - ? TCG_TYPE_I64 : TCG_TYPE_I32); - /* Load cpu->neg.tlb.f[mmu_idx].{mask,table} into {tmp0,tmp1}. */ QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, mask) != 0); QEMU_BUILD_BUG_ON(offsetof(CPUTLBDescFast, table) != 8); @@ -1679,7 +1675,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tlb_mask_table_ofs(s, mem_index), 1, 0); /* Extract the TLB index from the address into X0. */ - tcg_out_insn(s, 3502S, AND_LSR, mask_type == TCG_TYPE_I64, + tcg_out_insn(s, 3502S, AND_LSR, TCG_TYPE_I64, TCG_REG_TMP0, TCG_REG_TMP0, addr_reg, s->page_bits - CPU_TLB_ENTRY_BITS); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 09fce27b06..2990912080 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2199,10 +2199,8 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, trexw = (ttype == TCG_TYPE_I32 ? 0 : P_REXW); if (TCG_TYPE_PTR == TCG_TYPE_I64) { hrexw = P_REXW; - if (s->page_bits + s->tlb_dyn_max_bits > 32) { - tlbtype = TCG_TYPE_I64; - tlbrexw = P_REXW; - } + tlbtype = TCG_TYPE_I64; + tlbrexw = P_REXW; } } From 11efde54f248c2da9e164910b8b1945e78a7168e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 1 May 2025 11:38:03 -0700 Subject: [PATCH 1191/2760] tcg: Drop TCGContext.page_{mask,bits} Use exec/target_page.h instead of independent variables. Signed-off-by: Richard Henderson --- accel/tcg/translate-all.c | 4 ---- include/tcg/tcg.h | 3 --- tcg/aarch64/tcg-target.c.inc | 4 ++-- tcg/arm/tcg-target.c.inc | 10 +++++----- tcg/i386/tcg-target.c.inc | 4 ++-- tcg/loongarch64/tcg-target.c.inc | 4 ++-- tcg/mips/tcg-target.c.inc | 6 +++--- tcg/perf.c | 2 +- tcg/ppc/tcg-target.c.inc | 14 +++++++------- tcg/riscv/tcg-target.c.inc | 4 ++-- tcg/s390x/tcg-target.c.inc | 4 ++-- tcg/sparc64/tcg-target.c.inc | 4 ++-- tcg/tcg-op-ldst.c | 3 ++- tcg/tcg.c | 1 + 14 files changed, 31 insertions(+), 36 deletions(-) diff --git a/accel/tcg/translate-all.c b/accel/tcg/translate-all.c index 6735a40ade..d468667b0d 100644 --- a/accel/tcg/translate-all.c +++ b/accel/tcg/translate-all.c @@ -312,10 +312,6 @@ TranslationBlock *tb_gen_code(CPUState *cpu, TCGTBCPUState s) tcg_ctx->gen_tb = tb; tcg_ctx->addr_type = target_long_bits() == 32 ? TCG_TYPE_I32 : TCG_TYPE_I64; -#ifdef CONFIG_SOFTMMU - tcg_ctx->page_bits = TARGET_PAGE_BITS; - tcg_ctx->page_mask = TARGET_PAGE_MASK; -#endif tcg_ctx->guest_mo = cpu->cc->tcg_ops->guest_default_memory_order; restart_translate: diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index e440c889c8..125323f153 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -365,9 +365,6 @@ struct TCGContext { int nb_indirects; int nb_ops; TCGType addr_type; /* TCG_TYPE_I32 or TCG_TYPE_I64 */ - - int page_mask; - uint8_t page_bits; TCGBar guest_mo; TCGRegSet reserved_regs; diff --git a/tcg/aarch64/tcg-target.c.inc b/tcg/aarch64/tcg-target.c.inc index 6356a81c2a..3b088b7bd9 100644 --- a/tcg/aarch64/tcg-target.c.inc +++ b/tcg/aarch64/tcg-target.c.inc @@ -1677,7 +1677,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* Extract the TLB index from the address into X0. */ tcg_out_insn(s, 3502S, AND_LSR, TCG_TYPE_I64, TCG_REG_TMP0, TCG_REG_TMP0, addr_reg, - s->page_bits - CPU_TLB_ENTRY_BITS); + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); /* Add the tlb_table pointer, forming the CPUTLBEntry address. */ tcg_out_insn(s, 3502, ADD, 1, TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_TMP0); @@ -1703,7 +1703,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_insn(s, 3401, ADDI, addr_type, addr_adj, addr_reg, s_mask - a_mask); } - compare_mask = (uint64_t)s->page_mask | a_mask; + compare_mask = (uint64_t)TARGET_PAGE_MASK | a_mask; /* Store the page mask part of the address into TMP2. */ tcg_out_logicali(s, I3404_ANDI, addr_type, TCG_REG_TMP2, diff --git a/tcg/arm/tcg-target.c.inc b/tcg/arm/tcg-target.c.inc index 447e43583e..836894b16a 100644 --- a/tcg/arm/tcg-target.c.inc +++ b/tcg/arm/tcg-target.c.inc @@ -1427,7 +1427,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* Extract the tlb index from the address into R0. */ tcg_out_dat_reg(s, COND_AL, ARITH_AND, TCG_REG_R0, TCG_REG_R0, addr, - SHIFT_IMM_LSR(s->page_bits - CPU_TLB_ENTRY_BITS)); + SHIFT_IMM_LSR(TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS)); /* * Add the tlb_table pointer, creating the CPUTLBEntry address in R1. @@ -1463,8 +1463,8 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_dat_imm(s, COND_AL, ARITH_ADD, t_addr, addr, s_mask - a_mask); } - if (use_armv7_instructions && s->page_bits <= 16) { - tcg_out_movi32(s, COND_AL, TCG_REG_TMP, ~(s->page_mask | a_mask)); + if (use_armv7_instructions && TARGET_PAGE_BITS <= 16) { + tcg_out_movi32(s, COND_AL, TCG_REG_TMP, ~(TARGET_PAGE_MASK | a_mask)); tcg_out_dat_reg(s, COND_AL, ARITH_BIC, TCG_REG_TMP, t_addr, TCG_REG_TMP, 0); tcg_out_dat_reg(s, COND_AL, ARITH_CMP, 0, @@ -1475,10 +1475,10 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_dat_imm(s, COND_AL, ARITH_TST, 0, addr, a_mask); } tcg_out_dat_reg(s, COND_AL, ARITH_MOV, TCG_REG_TMP, 0, t_addr, - SHIFT_IMM_LSR(s->page_bits)); + SHIFT_IMM_LSR(TARGET_PAGE_BITS)); tcg_out_dat_reg(s, (a_mask ? COND_EQ : COND_AL), ARITH_CMP, 0, TCG_REG_R2, TCG_REG_TMP, - SHIFT_IMM_LSL(s->page_bits)); + SHIFT_IMM_LSL(TARGET_PAGE_BITS)); } } else if (a_mask) { ldst = new_ldst_label(s); diff --git a/tcg/i386/tcg-target.c.inc b/tcg/i386/tcg-target.c.inc index 2990912080..088c6c9264 100644 --- a/tcg/i386/tcg-target.c.inc +++ b/tcg/i386/tcg-target.c.inc @@ -2206,7 +2206,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_mov(s, tlbtype, TCG_REG_L0, addr); tcg_out_shifti(s, SHIFT_SHR + tlbrexw, TCG_REG_L0, - s->page_bits - CPU_TLB_ENTRY_BITS); + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); tcg_out_modrm_offset(s, OPC_AND_GvEv + trexw, TCG_REG_L0, TCG_AREG0, fast_ofs + offsetof(CPUTLBDescFast, mask)); @@ -2225,7 +2225,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_modrm_offset(s, OPC_LEA + trexw, TCG_REG_L1, addr, s_mask - a_mask); } - tlb_mask = s->page_mask | a_mask; + tlb_mask = TARGET_PAGE_MASK | a_mask; tgen_arithi(s, ARITH_AND + trexw, TCG_REG_L1, tlb_mask, 0); /* cmp 0(TCG_REG_L0), TCG_REG_L1 */ diff --git a/tcg/loongarch64/tcg-target.c.inc b/tcg/loongarch64/tcg-target.c.inc index e5580d69a8..10c69211ac 100644 --- a/tcg/loongarch64/tcg-target.c.inc +++ b/tcg/loongarch64/tcg-target.c.inc @@ -1065,7 +1065,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, table_ofs); tcg_out_opc_srli_d(s, TCG_REG_TMP2, addr_reg, - s->page_bits - CPU_TLB_ENTRY_BITS); + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); tcg_out_opc_and(s, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP0); tcg_out_opc_add_d(s, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP1); @@ -1091,7 +1091,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, tcg_out_mov(s, addr_type, TCG_REG_TMP1, addr_reg); } tcg_out_opc_bstrins_d(s, TCG_REG_TMP1, TCG_REG_ZERO, - a_bits, s->page_bits - 1); + a_bits, TARGET_PAGE_BITS - 1); /* Compare masked address with the TLB entry. */ ldst->label_ptr[0] = s->code_ptr; diff --git a/tcg/mips/tcg-target.c.inc b/tcg/mips/tcg-target.c.inc index 2c0457e588..400eafbab4 100644 --- a/tcg/mips/tcg-target.c.inc +++ b/tcg/mips/tcg-target.c.inc @@ -1199,9 +1199,9 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* Extract the TLB index from the address into TMP3. */ if (TCG_TARGET_REG_BITS == 32 || addr_type == TCG_TYPE_I32) { tcg_out_opc_sa(s, OPC_SRL, TCG_TMP3, addr, - s->page_bits - CPU_TLB_ENTRY_BITS); + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); } else { - tcg_out_dsrl(s, TCG_TMP3, addr, s->page_bits - CPU_TLB_ENTRY_BITS); + tcg_out_dsrl(s, TCG_TMP3, addr, TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); } tcg_out_opc_reg(s, OPC_AND, TCG_TMP3, TCG_TMP3, TCG_TMP0); @@ -1224,7 +1224,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, * For unaligned accesses, compare against the end of the access to * verify that it does not cross a page boundary. */ - tcg_out_movi(s, addr_type, TCG_TMP1, s->page_mask | a_mask); + tcg_out_movi(s, addr_type, TCG_TMP1, TARGET_PAGE_MASK | a_mask); if (a_mask < s_mask) { tcg_out_opc_imm(s, (TCG_TARGET_REG_BITS == 32 || addr_type == TCG_TYPE_I32 diff --git a/tcg/perf.c b/tcg/perf.c index 4e8d2c1bee..8fa5fa9991 100644 --- a/tcg/perf.c +++ b/tcg/perf.c @@ -334,7 +334,7 @@ void perf_report_code(uint64_t guest_pc, TranslationBlock *tb, /* FIXME: This replicates the restore_state_to_opc() logic. */ q[insn].address = gen_insn_data[insn * INSN_START_WORDS + 0]; if (tb_cflags(tb) & CF_PCREL) { - q[insn].address |= (guest_pc & qemu_target_page_mask()); + q[insn].address |= guest_pc & TARGET_PAGE_MASK; } q[insn].flags = DEBUGINFO_SYMBOL | (jitdump ? DEBUGINFO_LINE : 0); } diff --git a/tcg/ppc/tcg-target.c.inc b/tcg/ppc/tcg-target.c.inc index 2e94778104..b8b23d44d5 100644 --- a/tcg/ppc/tcg-target.c.inc +++ b/tcg/ppc/tcg-target.c.inc @@ -2440,10 +2440,10 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* Extract the page index, shifted into place for tlb index. */ if (TCG_TARGET_REG_BITS == 32) { tcg_out_shri32(s, TCG_REG_R0, addr, - s->page_bits - CPU_TLB_ENTRY_BITS); + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); } else { tcg_out_shri64(s, TCG_REG_R0, addr, - s->page_bits - CPU_TLB_ENTRY_BITS); + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); } tcg_out32(s, AND | SAB(TCG_REG_TMP1, TCG_REG_TMP1, TCG_REG_R0)); @@ -2480,7 +2480,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, a_bits = s_bits; } tcg_out_rlw(s, RLWINM, TCG_REG_R0, addr, 0, - (32 - a_bits) & 31, 31 - s->page_bits); + (32 - a_bits) & 31, 31 - TARGET_PAGE_BITS); } else { TCGReg t = addr; @@ -2501,13 +2501,13 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* Mask the address for the requested alignment. */ if (addr_type == TCG_TYPE_I32) { tcg_out_rlw(s, RLWINM, TCG_REG_R0, t, 0, - (32 - a_bits) & 31, 31 - s->page_bits); + (32 - a_bits) & 31, 31 - TARGET_PAGE_BITS); } else if (a_bits == 0) { - tcg_out_rld(s, RLDICR, TCG_REG_R0, t, 0, 63 - s->page_bits); + tcg_out_rld(s, RLDICR, TCG_REG_R0, t, 0, 63 - TARGET_PAGE_BITS); } else { tcg_out_rld(s, RLDICL, TCG_REG_R0, t, - 64 - s->page_bits, s->page_bits - a_bits); - tcg_out_rld(s, RLDICL, TCG_REG_R0, TCG_REG_R0, s->page_bits, 0); + 64 - TARGET_PAGE_BITS, TARGET_PAGE_BITS - a_bits); + tcg_out_rld(s, RLDICL, TCG_REG_R0, TCG_REG_R0, TARGET_PAGE_BITS, 0); } } diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index f9417d15f7..1800fd5077 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -1706,7 +1706,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, tcg_out_ld(s, TCG_TYPE_PTR, TCG_REG_TMP1, TCG_AREG0, table_ofs); tcg_out_opc_imm(s, OPC_SRLI, TCG_REG_TMP2, addr_reg, - s->page_bits - CPU_TLB_ENTRY_BITS); + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); tcg_out_opc_reg(s, OPC_AND, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP0); tcg_out_opc_reg(s, OPC_ADD, TCG_REG_TMP2, TCG_REG_TMP2, TCG_REG_TMP1); @@ -1722,7 +1722,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, TCGReg *pbase, tcg_out_opc_imm(s, addr_type == TCG_TYPE_I32 ? OPC_ADDIW : OPC_ADDI, addr_adj, addr_reg, s_mask - a_mask); } - compare_mask = s->page_mask | a_mask; + compare_mask = TARGET_PAGE_MASK | a_mask; if (compare_mask == sextreg(compare_mask, 0, 12)) { tcg_out_opc_imm(s, OPC_ANDI, TCG_REG_TMP1, addr_adj, compare_mask); } else { diff --git a/tcg/s390x/tcg-target.c.inc b/tcg/s390x/tcg-target.c.inc index 7ca0071f24..84a9e73a46 100644 --- a/tcg/s390x/tcg-target.c.inc +++ b/tcg/s390x/tcg-target.c.inc @@ -2004,7 +2004,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, ldst->addr_reg = addr_reg; tcg_out_sh64(s, RSY_SRLG, TCG_TMP0, addr_reg, TCG_REG_NONE, - s->page_bits - CPU_TLB_ENTRY_BITS); + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS); tcg_out_insn(s, RXY, NG, TCG_TMP0, TCG_AREG0, TCG_REG_NONE, mask_off); tcg_out_insn(s, RXY, AG, TCG_TMP0, TCG_AREG0, TCG_REG_NONE, table_off); @@ -2016,7 +2016,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, * byte of the access. */ a_off = (a_mask >= s_mask ? 0 : s_mask - a_mask); - tlb_mask = (uint64_t)s->page_mask | a_mask; + tlb_mask = (uint64_t)TARGET_PAGE_MASK | a_mask; if (a_off == 0) { tgen_andi_risbg(s, TCG_REG_R0, addr_reg, tlb_mask); } else { diff --git a/tcg/sparc64/tcg-target.c.inc b/tcg/sparc64/tcg-target.c.inc index 9e004fb511..5e5c3f1cda 100644 --- a/tcg/sparc64/tcg-target.c.inc +++ b/tcg/sparc64/tcg-target.c.inc @@ -1120,7 +1120,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, /* Extract the page index, shifted into place for tlb index. */ tcg_out_arithi(s, TCG_REG_T1, addr_reg, - s->page_bits - CPU_TLB_ENTRY_BITS, SHIFT_SRL); + TARGET_PAGE_BITS - CPU_TLB_ENTRY_BITS, SHIFT_SRL); tcg_out_arith(s, TCG_REG_T1, TCG_REG_T1, TCG_REG_T2, ARITH_AND); /* Add the tlb_table pointer, creating the CPUTLBEntry address into R2. */ @@ -1136,7 +1136,7 @@ static TCGLabelQemuLdst *prepare_host_addr(TCGContext *s, HostAddress *h, h->base = TCG_REG_T1; /* Mask out the page offset, except for the required alignment. */ - compare_mask = s->page_mask | a_mask; + compare_mask = TARGET_PAGE_MASK | a_mask; if (check_fit_tl(compare_mask, 13)) { tcg_out_arithi(s, TCG_REG_T3, addr_reg, compare_mask, ARITH_AND); } else { diff --git a/tcg/tcg-op-ldst.c b/tcg/tcg-op-ldst.c index fa9e52277b..548496002d 100644 --- a/tcg/tcg-op-ldst.c +++ b/tcg/tcg-op-ldst.c @@ -27,6 +27,7 @@ #include "tcg/tcg-temp-internal.h" #include "tcg/tcg-op-common.h" #include "tcg/tcg-mo.h" +#include "exec/target_page.h" #include "exec/translation-block.h" #include "exec/plugin-gen.h" #include "tcg-internal.h" @@ -40,7 +41,7 @@ static void check_max_alignment(unsigned a_bits) * FIXME: Must keep the count up-to-date with "exec/tlb-flags.h". */ if (tcg_use_softmmu) { - tcg_debug_assert(a_bits + 5 <= tcg_ctx->page_bits); + tcg_debug_assert(a_bits + 5 <= TARGET_PAGE_BITS); } } diff --git a/tcg/tcg.c b/tcg/tcg.c index ae27a2607d..d714ae2889 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -34,6 +34,7 @@ #include "qemu/cacheflush.h" #include "qemu/cacheinfo.h" #include "qemu/timer.h" +#include "exec/target_page.h" #include "exec/translation-block.h" #include "exec/tlb-common.h" #include "tcg/startup.h" From eb978e50e42f3439e7a7a104e76aafc81bc4a028 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 3 May 2025 13:40:26 -0700 Subject: [PATCH 1192/2760] target/sh4: Use MO_ALIGN for system UNALIGN() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This should have been done before removing TARGET_ALIGNED_ONLY, as we did for hppa and alpha. Cc: Yoshinori Sato Reviewed-by: Philippe Mathieu-Daudé Fixes: 8244189419f9 ("target/sh4: Remove TARGET_ALIGNED_ONLY") Signed-off-by: Richard Henderson --- target/sh4/translate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/sh4/translate.c b/target/sh4/translate.c index bf8828fce8..70fd13aa3f 100644 --- a/target/sh4/translate.c +++ b/target/sh4/translate.c @@ -54,7 +54,7 @@ typedef struct DisasContext { #define UNALIGN(C) (ctx->tbflags & TB_FLAG_UNALIGN ? MO_UNALN : MO_ALIGN) #else #define IS_USER(ctx) (!(ctx->tbflags & (1u << SR_MD))) -#define UNALIGN(C) 0 +#define UNALIGN(C) MO_ALIGN #endif /* Target-specific values for ctx->base.is_jmp. */ From bdf26b5d16dd2264553308aa6bbf24b4749fcc07 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 3 May 2025 13:17:17 -0700 Subject: [PATCH 1193/2760] accel/tcg: Add TCGCPUOps.pointer_wrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 6 ++++++ include/accel/tcg/cpu-ops.h | 7 +++++++ 2 files changed, 13 insertions(+) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 86d0deb08c..81ff725cbc 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1773,6 +1773,12 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi, l->page[1].size = l->page[0].size - size0; l->page[0].size = size0; + if (cpu->cc->tcg_ops->pointer_wrap) { + l->page[1].addr = cpu->cc->tcg_ops->pointer_wrap(cpu, l->mmu_idx, + l->page[1].addr, + addr); + } + /* * Lookup both pages, recognizing exceptions from either. If the * second lookup potentially resized, refresh first CPUTLBEntryFull. diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index cd22e5d5b9..83b2c2c864 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -222,6 +222,13 @@ struct TCGCPUOps { bool (*tlb_fill)(CPUState *cpu, vaddr address, int size, MMUAccessType access_type, int mmu_idx, bool probe, uintptr_t retaddr); + /** + * @pointer_wrap: + * + * We have incremented @base to @result, resulting in a page change. + * For the current cpu state, adjust @result for possible overflow. + */ + vaddr (*pointer_wrap)(CPUState *cpu, int mmu_idx, vaddr result, vaddr base); /** * @do_transaction_failed: Callback for handling failed memory transactions * (ie bus faults or external aborts; not MMU faults) From a4027ed7d4becb4cb67c912c75ecd4846b148829 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 3 May 2025 13:45:26 -0700 Subject: [PATCH 1194/2760] target: Use cpu_pointer_wrap_notreached for strict align targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Alpha, HPPA, and SH4 always use aligned addresses, and therefore never produce accesses that cross pages. Cc: Helge Deller Cc: Yoshinori Sato Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 13 +++++++++++++ include/accel/tcg/cpu-ops.h | 5 +++++ target/alpha/cpu.c | 1 + target/hppa/cpu.c | 1 + target/sh4/cpu.c | 1 + 5 files changed, 21 insertions(+) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 81ff725cbc..49ec3ee5dc 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2933,3 +2933,16 @@ uint64_t cpu_ldq_code_mmu(CPUArchState *env, vaddr addr, { return do_ld8_mmu(env_cpu(env), addr, oi, retaddr, MMU_INST_FETCH); } + +/* + * Common pointer_wrap implementations. + */ + +/* + * To be used for strict alignment targets. + * Because no accesses are unaligned, no accesses wrap either. + */ +vaddr cpu_pointer_wrap_notreached(CPUState *cs, int idx, vaddr res, vaddr base) +{ + g_assert_not_reached(); +} diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index 83b2c2c864..4f3b4fd3bc 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -322,6 +322,11 @@ void cpu_check_watchpoint(CPUState *cpu, vaddr addr, vaddr len, */ int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len); +/* + * Common pointer_wrap implementations. + */ +vaddr cpu_pointer_wrap_notreached(CPUState *, int, vaddr, vaddr); + #endif #endif /* TCG_CPU_OPS_H */ diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 890b84c032..2082db45ea 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -261,6 +261,7 @@ static const TCGCPUOps alpha_tcg_ops = { .record_sigbus = alpha_cpu_record_sigbus, #else .tlb_fill = alpha_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_notreached, .cpu_exec_interrupt = alpha_cpu_exec_interrupt, .cpu_exec_halt = alpha_cpu_has_work, .cpu_exec_reset = cpu_reset, diff --git a/target/hppa/cpu.c b/target/hppa/cpu.c index 6465181543..24777727e6 100644 --- a/target/hppa/cpu.c +++ b/target/hppa/cpu.c @@ -269,6 +269,7 @@ static const TCGCPUOps hppa_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill_align = hppa_cpu_tlb_fill_align, + .pointer_wrap = cpu_pointer_wrap_notreached, .cpu_exec_interrupt = hppa_cpu_exec_interrupt, .cpu_exec_halt = hppa_cpu_has_work, .cpu_exec_reset = cpu_reset, diff --git a/target/sh4/cpu.c b/target/sh4/cpu.c index b35f18e250..4f561e8c91 100644 --- a/target/sh4/cpu.c +++ b/target/sh4/cpu.c @@ -296,6 +296,7 @@ static const TCGCPUOps superh_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = superh_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_notreached, .cpu_exec_interrupt = superh_cpu_exec_interrupt, .cpu_exec_halt = superh_cpu_has_work, .cpu_exec_reset = cpu_reset, From 981f2beb161b9bcaeedc1f91ad22bff255856cb2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 3 May 2025 14:23:41 -0700 Subject: [PATCH 1195/2760] target: Use cpu_pointer_wrap_uint32 for 32-bit targets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit M68K, MicroBlaze, OpenRISC, RX, TriCore and Xtensa are all 32-bit targets. AVR is more complicated, but using a 32-bit wrap preserves current behaviour. Cc: Michael Rolnik Cc: Laurent Vivier Cc: Stafford Horne Cc: Yoshinori Sato Cc: Max Filippov Tested-by Bastian Koppelmann (tricore) Reviewed-by: Bastian Koppelmann Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Edgar E. Iglesias Signed-off-by: Richard Henderson --- accel/tcg/cputlb.c | 6 ++++++ include/accel/tcg/cpu-ops.h | 1 + target/avr/cpu.c | 6 ++++++ target/m68k/cpu.c | 1 + target/microblaze/cpu.c | 1 + target/openrisc/cpu.c | 1 + target/rx/cpu.c | 1 + target/tricore/cpu.c | 1 + target/xtensa/cpu.c | 1 + 9 files changed, 19 insertions(+) diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index 49ec3ee5dc..a734859396 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -2946,3 +2946,9 @@ vaddr cpu_pointer_wrap_notreached(CPUState *cs, int idx, vaddr res, vaddr base) { g_assert_not_reached(); } + +/* To be used for strict 32-bit targets. */ +vaddr cpu_pointer_wrap_uint32(CPUState *cs, int idx, vaddr res, vaddr base) +{ + return (uint32_t)res; +} diff --git a/include/accel/tcg/cpu-ops.h b/include/accel/tcg/cpu-ops.h index 4f3b4fd3bc..dd8ea30016 100644 --- a/include/accel/tcg/cpu-ops.h +++ b/include/accel/tcg/cpu-ops.h @@ -326,6 +326,7 @@ int cpu_watchpoint_address_matches(CPUState *cpu, vaddr addr, vaddr len); * Common pointer_wrap implementations. */ vaddr cpu_pointer_wrap_notreached(CPUState *, int, vaddr, vaddr); +vaddr cpu_pointer_wrap_uint32(CPUState *, int, vaddr, vaddr); #endif diff --git a/target/avr/cpu.c b/target/avr/cpu.c index 250241541b..6995de6a12 100644 --- a/target/avr/cpu.c +++ b/target/avr/cpu.c @@ -250,6 +250,12 @@ static const TCGCPUOps avr_tcg_ops = { .cpu_exec_reset = cpu_reset, .tlb_fill = avr_cpu_tlb_fill, .do_interrupt = avr_cpu_do_interrupt, + /* + * TODO: code and data wrapping are different, but for the most part + * AVR only references bytes or aligned code fetches. But we use + * non-aligned MO_16 accesses for stack push/pop. + */ + .pointer_wrap = cpu_pointer_wrap_uint32, }; static void avr_cpu_class_init(ObjectClass *oc, const void *data) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index c5196a612e..6a09db3a6f 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -619,6 +619,7 @@ static const TCGCPUOps m68k_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = m68k_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_uint32, .cpu_exec_interrupt = m68k_cpu_exec_interrupt, .cpu_exec_halt = m68k_cpu_has_work, .cpu_exec_reset = cpu_reset, diff --git a/target/microblaze/cpu.c b/target/microblaze/cpu.c index 615a959200..ee0a869a94 100644 --- a/target/microblaze/cpu.c +++ b/target/microblaze/cpu.c @@ -447,6 +447,7 @@ static const TCGCPUOps mb_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = mb_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_uint32, .cpu_exec_interrupt = mb_cpu_exec_interrupt, .cpu_exec_halt = mb_cpu_has_work, .cpu_exec_reset = cpu_reset, diff --git a/target/openrisc/cpu.c b/target/openrisc/cpu.c index 054ad33360..dfbb2df643 100644 --- a/target/openrisc/cpu.c +++ b/target/openrisc/cpu.c @@ -265,6 +265,7 @@ static const TCGCPUOps openrisc_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = openrisc_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_uint32, .cpu_exec_interrupt = openrisc_cpu_exec_interrupt, .cpu_exec_halt = openrisc_cpu_has_work, .cpu_exec_reset = cpu_reset, diff --git a/target/rx/cpu.c b/target/rx/cpu.c index 36eba75545..c6dd5d6f83 100644 --- a/target/rx/cpu.c +++ b/target/rx/cpu.c @@ -225,6 +225,7 @@ static const TCGCPUOps rx_tcg_ops = { .restore_state_to_opc = rx_restore_state_to_opc, .mmu_index = rx_cpu_mmu_index, .tlb_fill = rx_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_uint32, .cpu_exec_interrupt = rx_cpu_exec_interrupt, .cpu_exec_halt = rx_cpu_has_work, diff --git a/target/tricore/cpu.c b/target/tricore/cpu.c index e56f90fde9..4f035b6f76 100644 --- a/target/tricore/cpu.c +++ b/target/tricore/cpu.c @@ -190,6 +190,7 @@ static const TCGCPUOps tricore_tcg_ops = { .restore_state_to_opc = tricore_restore_state_to_opc, .mmu_index = tricore_cpu_mmu_index, .tlb_fill = tricore_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_uint32, .cpu_exec_interrupt = tricore_cpu_exec_interrupt, .cpu_exec_halt = tricore_cpu_has_work, .cpu_exec_reset = cpu_reset, diff --git a/target/xtensa/cpu.c b/target/xtensa/cpu.c index 91b71b6caa..ea9b6df3aa 100644 --- a/target/xtensa/cpu.c +++ b/target/xtensa/cpu.c @@ -318,6 +318,7 @@ static const TCGCPUOps xtensa_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = xtensa_cpu_tlb_fill, + .pointer_wrap = cpu_pointer_wrap_uint32, .cpu_exec_interrupt = xtensa_cpu_exec_interrupt, .cpu_exec_halt = xtensa_cpu_has_work, .cpu_exec_reset = cpu_reset, From d21144a48c1a9d1998c594d976d5e906276eca4c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 4 May 2025 09:27:42 -0700 Subject: [PATCH 1196/2760] target/arm: Fill in TCGCPUOps.pointer_wrap For a-profile, check A32 vs A64 state. For m-profile, use cpu_pointer_wrap_uint32. Cc: qemu-arm@nongnu.org Signed-off-by: Richard Henderson --- target/arm/cpu.c | 24 ++++++++++++++++++++++++ target/arm/tcg/cpu-v7m.c | 1 + 2 files changed, 25 insertions(+) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index ca5ed7892e..e025e241ed 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2703,6 +2703,29 @@ static const struct SysemuCPUOps arm_sysemu_ops = { #endif #ifdef CONFIG_TCG +#ifndef CONFIG_USER_ONLY +static vaddr aprofile_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + /* + * The Stage2 and Phys indexes are only used for ptw on arm32, + * and all pte's are aligned, so we never produce a wrap for these. + * Double check that we're not truncating a 40-bit physical address. + */ + assert((unsigned)mmu_idx < (ARMMMUIdx_Stage2_S & ARM_MMU_IDX_COREIDX_MASK)); + + if (!is_a64(cpu_env(cs))) { + return (uint32_t)result; + } + + /* + * TODO: For FEAT_CPA2, decide how to we want to resolve + * Unpredictable_CPACHECK in AddressIncrement. + */ + return result; +} +#endif /* !CONFIG_USER_ONLY */ + static const TCGCPUOps arm_tcg_ops = { .mttcg_supported = true, /* ARM processors have a weak memory model */ @@ -2722,6 +2745,7 @@ static const TCGCPUOps arm_tcg_ops = { .untagged_addr = aarch64_untagged_addr, #else .tlb_fill_align = arm_cpu_tlb_fill_align, + .pointer_wrap = aprofile_pointer_wrap, .cpu_exec_interrupt = arm_cpu_exec_interrupt, .cpu_exec_halt = arm_cpu_exec_halt, .cpu_exec_reset = cpu_reset, diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 95b23d9b55..8e1a083b91 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -249,6 +249,7 @@ static const TCGCPUOps arm_v7m_tcg_ops = { .record_sigbus = arm_cpu_record_sigbus, #else .tlb_fill_align = arm_cpu_tlb_fill_align, + .pointer_wrap = cpu_pointer_wrap_uint32, .cpu_exec_interrupt = arm_v7m_cpu_exec_interrupt, .cpu_exec_halt = arm_cpu_exec_halt, .cpu_exec_reset = cpu_reset, From 7174cd2eec67d9b7bf969cdc87e656b4c4c93465 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 4 May 2025 09:46:28 -0700 Subject: [PATCH 1197/2760] target/i386: Fill in TCGCPUOps.pointer_wrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check 32 vs 64-bit state. Cc: Paolo Bonzini Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/i386/tcg/tcg-cpu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/i386/tcg/tcg-cpu.c b/target/i386/tcg/tcg-cpu.c index 179dfdf064..6f5dc06b3b 100644 --- a/target/i386/tcg/tcg-cpu.c +++ b/target/i386/tcg/tcg-cpu.c @@ -149,6 +149,12 @@ static void x86_cpu_exec_reset(CPUState *cs) do_cpu_init(env_archcpu(env)); cs->exception_index = EXCP_HALTED; } + +static vaddr x86_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return cpu_env(cs)->hflags & HF_CS64_MASK ? result : (uint32_t)result; +} #endif const TCGCPUOps x86_tcg_ops = { @@ -172,6 +178,7 @@ const TCGCPUOps x86_tcg_ops = { .record_sigbus = x86_cpu_record_sigbus, #else .tlb_fill = x86_cpu_tlb_fill, + .pointer_wrap = x86_pointer_wrap, .do_interrupt = x86_cpu_do_interrupt, .cpu_exec_halt = x86_cpu_exec_halt, .cpu_exec_interrupt = x86_cpu_exec_interrupt, From 353f703cf1959228affc23b5bba8a18738736cf4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 4 May 2025 09:50:08 -0700 Subject: [PATCH 1198/2760] target/loongarch: Fill in TCGCPUOps.pointer_wrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check va32 state. Reviewed-by: Song Gao Reviewed-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/loongarch/cpu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/loongarch/cpu.c b/target/loongarch/cpu.c index f7535d1be7..abad84c054 100644 --- a/target/loongarch/cpu.c +++ b/target/loongarch/cpu.c @@ -334,6 +334,12 @@ static bool loongarch_cpu_exec_interrupt(CPUState *cs, int interrupt_request) } return false; } + +static vaddr loongarch_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return is_va32(cpu_env(cs)) ? (uint32_t)result : result; +} #endif static TCGTBCPUState loongarch_get_tb_cpu_state(CPUState *cs) @@ -889,6 +895,7 @@ static const TCGCPUOps loongarch_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = loongarch_cpu_tlb_fill, + .pointer_wrap = loongarch_pointer_wrap, .cpu_exec_interrupt = loongarch_cpu_exec_interrupt, .cpu_exec_halt = loongarch_cpu_has_work, .cpu_exec_reset = cpu_reset, From 396c12d00e3944e79159c9f3cb934f26f32ef861 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 4 May 2025 09:59:29 -0700 Subject: [PATCH 1199/2760] target/mips: Fill in TCGCPUOps.pointer_wrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check 32 vs 64-bit addressing state. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/mips/cpu.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/mips/cpu.c b/target/mips/cpu.c index 4cbfb9435a..1f6c41fd34 100644 --- a/target/mips/cpu.c +++ b/target/mips/cpu.c @@ -560,6 +560,14 @@ static TCGTBCPUState mips_get_tb_cpu_state(CPUState *cs) }; } +#ifndef CONFIG_USER_ONLY +static vaddr mips_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return cpu_env(cs)->hflags & MIPS_HFLAG_AWRAP ? (int32_t)result : result; +} +#endif + static const TCGCPUOps mips_tcg_ops = { .mttcg_supported = TARGET_LONG_BITS == 32, .guest_default_memory_order = 0, @@ -573,6 +581,7 @@ static const TCGCPUOps mips_tcg_ops = { #if !defined(CONFIG_USER_ONLY) .tlb_fill = mips_cpu_tlb_fill, + .pointer_wrap = mips_pointer_wrap, .cpu_exec_interrupt = mips_cpu_exec_interrupt, .cpu_exec_halt = mips_cpu_has_work, .cpu_exec_reset = cpu_reset, From 4031eb4facfd8793defeb83c05712643c161e32e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 4 May 2025 10:20:02 -0700 Subject: [PATCH 1200/2760] target/ppc: Fill in TCGCPUOps.pointer_wrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check 32 vs 64-bit state. Cc: qemu-ppc@nongnu.org Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/ppc/cpu_init.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/ppc/cpu_init.c b/target/ppc/cpu_init.c index 9642812a71..a0e77f2673 100644 --- a/target/ppc/cpu_init.c +++ b/target/ppc/cpu_init.c @@ -7386,6 +7386,12 @@ static void ppc_cpu_exec_exit(CPUState *cs) cpu->vhyp_class->cpu_exec_exit(cpu->vhyp, cpu); } } + +static vaddr ppc_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return (cpu_env(cs)->hflags >> HFLAGS_64) & 1 ? result : (uint32_t)result; +} #endif /* CONFIG_TCG */ #endif /* !CONFIG_USER_ONLY */ @@ -7490,6 +7496,7 @@ static const TCGCPUOps ppc_tcg_ops = { .record_sigsegv = ppc_cpu_record_sigsegv, #else .tlb_fill = ppc_cpu_tlb_fill, + .pointer_wrap = ppc_pointer_wrap, .cpu_exec_interrupt = ppc_cpu_exec_interrupt, .cpu_exec_halt = ppc_cpu_has_work, .cpu_exec_reset = cpu_reset, From 8024f004155ea5a3f492c35a792ea7863176e1a9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 4 May 2025 10:59:39 -0700 Subject: [PATCH 1201/2760] target/riscv: Fill in TCGCPUOps.pointer_wrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check 32 vs 64-bit and pointer masking state. Cc: qemu-riscv@nongnu.org Reviewed-by: Philippe Mathieu-Daudé Acked-by: Alistair Francis Signed-off-by: Richard Henderson --- target/riscv/tcg/tcg-cpu.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 305912b8dd..55fd9e5584 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -237,6 +237,31 @@ static void riscv_restore_state_to_opc(CPUState *cs, env->excp_uw2 = data[2]; } +#ifndef CONFIG_USER_ONLY +static vaddr riscv_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + CPURISCVState *env = cpu_env(cs); + uint32_t pm_len; + bool pm_signext; + + if (cpu_address_xl(env) == MXL_RV32) { + return (uint32_t)result; + } + + pm_len = riscv_pm_get_pmlen(riscv_pm_get_pmm(env)); + if (pm_len == 0) { + return result; + } + + pm_signext = riscv_cpu_virt_mem_enabled(env); + if (pm_signext) { + return sextract64(result, 0, 64 - pm_len); + } + return extract64(result, 0, 64 - pm_len); +} +#endif + const TCGCPUOps riscv_tcg_ops = { .mttcg_supported = true, .guest_default_memory_order = 0, @@ -250,6 +275,7 @@ const TCGCPUOps riscv_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = riscv_cpu_tlb_fill, + .pointer_wrap = riscv_pointer_wrap, .cpu_exec_interrupt = riscv_cpu_exec_interrupt, .cpu_exec_halt = riscv_cpu_has_work, .cpu_exec_reset = cpu_reset, From c2a0439f1ee99cb29883311f75ba08d9cca759c9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 4 May 2025 11:04:55 -0700 Subject: [PATCH 1202/2760] target/s390x: Fill in TCGCPUOps.pointer_wrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the existing wrap_address function. Cc: qemu-s390x@nongnu.org Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/s390x/cpu.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/s390x/cpu.c b/target/s390x/cpu.c index 9c1158ebcc..f05ce317da 100644 --- a/target/s390x/cpu.c +++ b/target/s390x/cpu.c @@ -347,6 +347,14 @@ static TCGTBCPUState s390x_get_tb_cpu_state(CPUState *cs) }; } +#ifndef CONFIG_USER_ONLY +static vaddr s390_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ + return wrap_address(cpu_env(cs), result); +} +#endif + static const TCGCPUOps s390_tcg_ops = { .mttcg_supported = true, .precise_smc = true, @@ -367,6 +375,7 @@ static const TCGCPUOps s390_tcg_ops = { .record_sigbus = s390_cpu_record_sigbus, #else .tlb_fill = s390_cpu_tlb_fill, + .pointer_wrap = s390_pointer_wrap, .cpu_exec_interrupt = s390_cpu_exec_interrupt, .cpu_exec_halt = s390_cpu_has_work, .cpu_exec_reset = cpu_reset, From 90f80e4b0fedfc78163c1c112bb74ccbfcae2365 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 4 May 2025 11:14:01 -0700 Subject: [PATCH 1203/2760] target/sparc: Fill in TCGCPUOps.pointer_wrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check address masking state for sparc64. Cc: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- target/sparc/cpu.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index 2a3e408923..ed7701b02f 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -1002,6 +1002,18 @@ static const struct SysemuCPUOps sparc_sysemu_ops = { #ifdef CONFIG_TCG #include "accel/tcg/cpu-ops.h" +#ifndef CONFIG_USER_ONLY +static vaddr sparc_pointer_wrap(CPUState *cs, int mmu_idx, + vaddr result, vaddr base) +{ +#ifdef TARGET_SPARC64 + return cpu_env(cs)->pstate & PS_AM ? (uint32_t)result : result; +#else + return (uint32_t)result; +#endif +} +#endif + static const TCGCPUOps sparc_tcg_ops = { /* * From Oracle SPARC Architecture 2015: @@ -1036,6 +1048,7 @@ static const TCGCPUOps sparc_tcg_ops = { #ifndef CONFIG_USER_ONLY .tlb_fill = sparc_cpu_tlb_fill, + .pointer_wrap = sparc_pointer_wrap, .cpu_exec_interrupt = sparc_cpu_exec_interrupt, .cpu_exec_halt = sparc_cpu_has_work, .cpu_exec_reset = cpu_reset, From 5c2891601ccdaa41427187ef95bc25c828b355e4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 4 May 2025 11:19:17 -0700 Subject: [PATCH 1204/2760] accel/tcg: Assert TCGCPUOps.pointer_wrap is set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All targets now provide the function, so we can make the call unconditional. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- accel/tcg/cpu-exec.c | 1 + accel/tcg/cputlb.c | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/accel/tcg/cpu-exec.c b/accel/tcg/cpu-exec.c index cc5f362305..713bdb2056 100644 --- a/accel/tcg/cpu-exec.c +++ b/accel/tcg/cpu-exec.c @@ -1039,6 +1039,7 @@ bool tcg_exec_realizefn(CPUState *cpu, Error **errp) assert(tcg_ops->cpu_exec_halt); assert(tcg_ops->cpu_exec_interrupt); assert(tcg_ops->cpu_exec_reset); + assert(tcg_ops->pointer_wrap); #endif /* !CONFIG_USER_ONLY */ assert(tcg_ops->translate_code); assert(tcg_ops->get_tb_cpu_state); diff --git a/accel/tcg/cputlb.c b/accel/tcg/cputlb.c index a734859396..87e14bde4f 100644 --- a/accel/tcg/cputlb.c +++ b/accel/tcg/cputlb.c @@ -1773,11 +1773,8 @@ static bool mmu_lookup(CPUState *cpu, vaddr addr, MemOpIdx oi, l->page[1].size = l->page[0].size - size0; l->page[0].size = size0; - if (cpu->cc->tcg_ops->pointer_wrap) { - l->page[1].addr = cpu->cc->tcg_ops->pointer_wrap(cpu, l->mmu_idx, - l->page[1].addr, - addr); - } + l->page[1].addr = cpu->cc->tcg_ops->pointer_wrap(cpu, l->mmu_idx, + l->page[1].addr, addr); /* * Lookup both pages, recognizing exceptions from either. If the From a78a91feeea66270badd87c65a7236e90baa5984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 22 May 2025 12:05:28 -0700 Subject: [PATCH 1205/2760] qapi: expose rtc-reset-reinjection command unconditionally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the TARGET_I386 condition from the rtc-reset-reinjection command. This requires providing a QMP command stub for non-i386 target. This in turn requires moving the command out of misc-target.json, since that will trigger symbol poisoning errors when built from target independent code. Rather than putting the command into misc.json, it is proposed to create misc-$TARGET.json files to hold commands whose impl is conceptually only applicable to a single target. This gives an obvious docs hint to consumers that the command is only useful in relation a specific target, while misc.json is for commands applicable to 2 or more targets. The current impl of qmp_rtc_reset_reinject() is a no-op if the i386 RTC is disabled in Kconfig, or if the running machine type lack any RTC device. The stub impl for non-i386 targets retains this no-op behaviour. However, it is now reporting an Error mentioning this command is not available for current target. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-2-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster --- hw/i386/monitor.c | 2 +- qapi/meson.build | 1 + qapi/misc-i386.json | 24 ++++++++++++++++++++++++ qapi/misc-target.json | 17 ----------------- qapi/qapi-schema.json | 1 + stubs/meson.build | 1 + stubs/monitor-i386-rtc.c | 12 ++++++++++++ 7 files changed, 40 insertions(+), 18 deletions(-) create mode 100644 qapi/misc-i386.json create mode 100644 stubs/monitor-i386-rtc.c diff --git a/hw/i386/monitor.c b/hw/i386/monitor.c index 1921e4d52e..79df96562f 100644 --- a/hw/i386/monitor.c +++ b/hw/i386/monitor.c @@ -26,7 +26,7 @@ #include "monitor/monitor.h" #include "qobject/qdict.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc-target.h" +#include "qapi/qapi-commands-misc-i386.h" #include "hw/i386/x86.h" #include "hw/rtc/mc146818rtc.h" diff --git a/qapi/meson.build b/qapi/meson.build index eadde4db30..3a9bd06104 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -64,6 +64,7 @@ if have_system 'qdev', 'pci', 'rocker', + 'misc-i386', 'tpm', 'uefi', ] diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json new file mode 100644 index 0000000000..d5bfd91405 --- /dev/null +++ b/qapi/misc-i386.json @@ -0,0 +1,24 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# SPDX-License-Identifier: GPL-2.0-or-later + +## +# @rtc-reset-reinjection: +# +# This command will reset the RTC interrupt reinjection backlog. Can +# be used if another mechanism to synchronize guest time is in effect, +# for example QEMU guest agent's guest-set-time command. +# +# Use of this command is only applicable for x86 machines with an RTC, +# and on other machines will silently return without performing any +# action. +# +# Since: 2.1 +# +# .. qmp-example:: +# +# -> { "execute": "rtc-reset-reinjection" } +# <- { "return": {} } +## +{ 'command': 'rtc-reset-reinjection' } diff --git a/qapi/misc-target.json b/qapi/misc-target.json index f7ec695caa..c5f9f6be7e 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -2,23 +2,6 @@ # vim: filetype=python # -## -# @rtc-reset-reinjection: -# -# This command will reset the RTC interrupt reinjection backlog. Can -# be used if another mechanism to synchronize guest time is in effect, -# for example QEMU guest agent's guest-set-time command. -# -# Since: 2.1 -# -# .. qmp-example:: -# -# -> { "execute": "rtc-reset-reinjection" } -# <- { "return": {} } -## -{ 'command': 'rtc-reset-reinjection', - 'if': 'TARGET_I386' } - ## # @SevState: # diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 7bc600bb76..96f6aa4413 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -61,6 +61,7 @@ { 'include': 'replay.json' } { 'include': 'yank.json' } { 'include': 'misc.json' } +{ 'include': 'misc-i386.json' } { 'include': 'misc-target.json' } { 'include': 'audio.json' } { 'include': 'acpi.json' } diff --git a/stubs/meson.build b/stubs/meson.build index 63392f5e78..9907b54c1e 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -77,6 +77,7 @@ if have_system stub_ss.add(files('target-monitor-defs.c')) stub_ss.add(files('win32-kbd-hook.c')) stub_ss.add(files('xen-hw-stub.c')) + stub_ss.add(files('monitor-i386-rtc.c')) endif if have_system or have_user diff --git a/stubs/monitor-i386-rtc.c b/stubs/monitor-i386-rtc.c new file mode 100644 index 0000000000..8420d7c93c --- /dev/null +++ b/stubs/monitor-i386-rtc.c @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-i386.h" + +void qmp_rtc_reset_reinjection(Error **errp) +{ + error_setg(errp, + "RTC interrupt reinjection backlog reset is not available for" + "this machine"); +} From 9215d072d2da3054b74080227a6a3e0a2daf44d2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 22 May 2025 12:05:29 -0700 Subject: [PATCH 1206/2760] qapi: expand docs for SEV commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This gives some more context about the behaviour of the commands in unsupported guest configuration or platform scenarios. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-3-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster [Tweak query-sev doc, turn error descriptions into Errors sections, delate a stray #, normalize whitespace, wrap lines] --- qapi/misc-target.json | 50 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 7 deletions(-) diff --git a/qapi/misc-target.json b/qapi/misc-target.json index c5f9f6be7e..e19a12e88a 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -110,7 +110,11 @@ ## # @query-sev: # -# Returns information about SEV +# Returns information about SEV/SEV-ES/SEV-SNP. +# +# If unavailable due to an incompatible configuration the returned +# @enabled field is set to 'false' and the state of all other fields +# is unspecified. # # Returns: @SevInfo # @@ -141,10 +145,19 @@ ## # @query-sev-launch-measure: # -# Query the SEV guest launch information. +# Query the SEV/SEV-ES guest launch information. +# +# This is only valid on x86 machines configured with KVM and the +# 'sev-guest' confidential virtualization object. The launch +# measurement for SEV-SNP guests is only available within the guest. # # Returns: The @SevLaunchMeasureInfo for the guest # +# Errors: +# - If the launch measurement is unavailable, either due to an +# invalid guest configuration or if the guest has not reached +# the required SEV state, GenericError +# # Since: 2.12 # # .. qmp-example:: @@ -185,11 +198,15 @@ ## # @query-sev-capabilities: # -# This command is used to get the SEV capabilities, and is supported -# on AMD X86 platforms only. +# Get SEV capabilities. +# +# This is only supported on AMD X86 platforms with KVM enabled. # # Returns: SevCapability objects. # +# Errors: +# - If SEV is not available on the platform, GenericError +# # Since: 2.12 # # .. qmp-example:: @@ -205,7 +222,12 @@ ## # @sev-inject-launch-secret: # -# This command injects a secret blob into memory of SEV guest. +# This command injects a secret blob into memory of a SEV/SEV-ES +# guest. +# +# This is only valid on x86 machines configured with KVM and the +# 'sev-guest' confidential virtualization object. SEV-SNP guests do +# not support launch secret injection. # # @packet-header: the launch secret packet header encoded in base64 # @@ -213,6 +235,11 @@ # # @gpa: the guest physical address where secret will be injected. # +# Errors: +# - If launch secret injection is not possible, either due to +# an invalid guest configuration, or if the guest has not +# reached the required SEV state, GenericError +# # Since: 6.0 ## { 'command': 'sev-inject-launch-secret', @@ -236,14 +263,23 @@ ## # @query-sev-attestation-report: # -# This command is used to get the SEV attestation report, and is -# supported on AMD X86 platforms only. +# This command is used to get the SEV attestation report. +# +# This is only valid on x86 machines configured with KVM and the +# 'sev-guest' confidential virtualization object. The attestation +# report for SEV-SNP guests is only available within the guest. # # @mnonce: a random 16 bytes value encoded in base64 (it will be # included in report) # # Returns: SevAttestationReport objects. # +# Errors: +# - This will return an error if the attestation report is +# unavailable, either due to an invalid guest configuration +# or if the guest has not reached the required SEV state, +# GenericError +# # Since: 6.1 # # .. qmp-example:: From 7373759583650fff6f724c6c21d4234bf31b2af0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 22 May 2025 12:05:30 -0700 Subject: [PATCH 1207/2760] qapi: make SEV commands unconditionally available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the TARGET_I386 condition from the SEV confidential virtualization commands, moving them to the recently introduced misc-i386.json QAPI file, given they are inherantly i386 specific commands. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-4-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster --- qapi/misc-i386.json | 278 ++++++++++++++++++++++++++++++++ qapi/misc-target.json | 291 ---------------------------------- stubs/meson.build | 1 + stubs/monitor-i386-sev.c | 36 +++++ target/i386/sev-system-stub.c | 32 ---- target/i386/sev.c | 2 +- 6 files changed, 316 insertions(+), 324 deletions(-) create mode 100644 stubs/monitor-i386-sev.c diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json index d5bfd91405..360adadfe0 100644 --- a/qapi/misc-i386.json +++ b/qapi/misc-i386.json @@ -22,3 +22,281 @@ # <- { "return": {} } ## { 'command': 'rtc-reset-reinjection' } + +## +# @SevState: +# +# An enumeration of SEV state information used during @query-sev. +# +# @uninit: The guest is uninitialized. +# +# @launch-update: The guest is currently being launched; plaintext +# data and register state is being imported. +# +# @launch-secret: The guest is currently being launched; ciphertext +# data is being imported. +# +# @running: The guest is fully launched or migrated in. +# +# @send-update: The guest is currently being migrated out to another +# machine. +# +# @receive-update: The guest is currently being migrated from another +# machine. +# +# Since: 2.12 +## +{ 'enum': 'SevState', + 'data': ['uninit', 'launch-update', 'launch-secret', 'running', + 'send-update', 'receive-update' ] } + +## +# @SevGuestType: +# +# An enumeration indicating the type of SEV guest being run. +# +# @sev: The guest is a legacy SEV or SEV-ES guest. +# +# @sev-snp: The guest is an SEV-SNP guest. +# +# Since: 6.2 +## +{ 'enum': 'SevGuestType', + 'data': [ 'sev', 'sev-snp' ] } + +## +# @SevGuestInfo: +# +# Information specific to legacy SEV/SEV-ES guests. +# +# @policy: SEV policy value +# +# @handle: SEV firmware handle +# +# Since: 2.12 +## +{ 'struct': 'SevGuestInfo', + 'data': { 'policy': 'uint32', + 'handle': 'uint32' } } + +## +# @SevSnpGuestInfo: +# +# Information specific to SEV-SNP guests. +# +# @snp-policy: SEV-SNP policy value +# +# Since: 9.1 +## +{ 'struct': 'SevSnpGuestInfo', + 'data': { 'snp-policy': 'uint64' } } + +## +# @SevInfo: +# +# Information about Secure Encrypted Virtualization (SEV) support +# +# @enabled: true if SEV is active +# +# @api-major: SEV API major version +# +# @api-minor: SEV API minor version +# +# @build-id: SEV FW build id +# +# @state: SEV guest state +# +# @sev-type: Type of SEV guest being run +# +# Since: 2.12 +## +{ 'union': 'SevInfo', + 'base': { 'enabled': 'bool', + 'api-major': 'uint8', + 'api-minor' : 'uint8', + 'build-id' : 'uint8', + 'state' : 'SevState', + 'sev-type' : 'SevGuestType' }, + 'discriminator': 'sev-type', + 'data': { + 'sev': 'SevGuestInfo', + 'sev-snp': 'SevSnpGuestInfo' } } + + +## +# @query-sev: +# +# Returns information about SEV/SEV-ES/SEV-SNP. +# +# If unavailable due to an incompatible configuration the returned +# @enabled field is set to 'false' and the state of all other fields +# is unspecified. +# +# Returns: @SevInfo +# +# Since: 2.12 +# +# .. qmp-example:: +# +# -> { "execute": "query-sev" } +# <- { "return": { "enabled": true, "api-major" : 0, "api-minor" : 0, +# "build-id" : 0, "policy" : 0, "state" : "running", +# "handle" : 1 } } +## +{ 'command': 'query-sev', 'returns': 'SevInfo' } + +## +# @SevLaunchMeasureInfo: +# +# SEV Guest Launch measurement information +# +# @data: the measurement value encoded in base64 +# +# Since: 2.12 +## +{ 'struct': 'SevLaunchMeasureInfo', 'data': {'data': 'str'} } + +## +# @query-sev-launch-measure: +# +# Query the SEV/SEV-ES guest launch information. +# +# This is only valid on x86 machines configured with KVM and the +# 'sev-guest' confidential virtualization object. The launch +# measurement for SEV-SNP guests is only available within the guest. +# +# Returns: The @SevLaunchMeasureInfo for the guest +# +# Errors: +# - If the launch measurement is unavailable, either due to an +# invalid guest configuration or if the guest has not reached +# the required SEV state, GenericError +# +# Since: 2.12 +# +# .. qmp-example:: +# +# -> { "execute": "query-sev-launch-measure" } +# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } +## +{ 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo' } + +## +# @SevCapability: +# +# The struct describes capability for a Secure Encrypted +# Virtualization feature. +# +# @pdh: Platform Diffie-Hellman key (base64 encoded) +# +# @cert-chain: PDH certificate chain (base64 encoded) +# +# @cpu0-id: Unique ID of CPU0 (base64 encoded) (since 7.1) +# +# @cbitpos: C-bit location in page table entry +# +# @reduced-phys-bits: Number of physical Address bit reduction when +# SEV is enabled +# +# Since: 2.12 +## +{ 'struct': 'SevCapability', + 'data': { 'pdh': 'str', + 'cert-chain': 'str', + 'cpu0-id': 'str', + 'cbitpos': 'int', + 'reduced-phys-bits': 'int'} } + +## +# @query-sev-capabilities: +# +# Get SEV capabilities. +# +# This is only supported on AMD X86 platforms with KVM enabled. +# +# Returns: SevCapability objects. +# +# Errors: +# - If SEV is not available on the platform, GenericError +# +# Since: 2.12 +# +# .. qmp-example:: +# +# -> { "execute": "query-sev-capabilities" } +# <- { "return": { "pdh": "8CCDD8DDD", "cert-chain": "888CCCDDDEE", +# "cpu0-id": "2lvmGwo+...61iEinw==", +# "cbitpos": 47, "reduced-phys-bits": 1}} +## +{ 'command': 'query-sev-capabilities', 'returns': 'SevCapability' } + +## +# @sev-inject-launch-secret: +# +# This command injects a secret blob into memory of a SEV/SEV-ES +# guest. +# +# This is only valid on x86 machines configured with KVM and the +# 'sev-guest' confidential virtualization object. SEV-SNP guests do +# not support launch secret injection. +# +# @packet-header: the launch secret packet header encoded in base64 +# +# @secret: the launch secret data to be injected encoded in base64 +# +# @gpa: the guest physical address where secret will be injected. +# +# Errors: +# - If launch secret injection is not possible, either due to +# an invalid guest configuration, or if the guest has not +# reached the required SEV state, GenericError +# +# Since: 6.0 +## +{ 'command': 'sev-inject-launch-secret', + 'data': { 'packet-header': 'str', 'secret': 'str', '*gpa': 'uint64' } } + +## +# @SevAttestationReport: +# +# The struct describes attestation report for a Secure Encrypted +# Virtualization feature. +# +# @data: guest attestation report (base64 encoded) +# +# Since: 6.1 +## +{ 'struct': 'SevAttestationReport', + 'data': { 'data': 'str'} } + +## +# @query-sev-attestation-report: +# +# This command is used to get the SEV attestation report. +# +# This is only valid on x86 machines configured with KVM and the +# 'sev-guest' confidential virtualization object. The attestation +# report for SEV-SNP guests is only available within the guest. +# +# @mnonce: a random 16 bytes value encoded in base64 (it will be +# included in report) +# +# Returns: SevAttestationReport objects. +# +# Errors: +# - This will return an error if the attestation report is +# unavailable, either due to an invalid guest configuration +# or if the guest has not reached the required SEV state, +# GenericError +# +# Since: 6.1 +# +# .. qmp-example:: +# +# -> { "execute" : "query-sev-attestation-report", +# "arguments": { "mnonce": "aaaaaaa" } } +# <- { "return" : { "data": "aaaaaaaabbbddddd"} } +## +{ 'command': 'query-sev-attestation-report', + 'data': { 'mnonce': 'str' }, + 'returns': 'SevAttestationReport' } diff --git a/qapi/misc-target.json b/qapi/misc-target.json index e19a12e88a..c0d7b311f3 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -2,297 +2,6 @@ # vim: filetype=python # -## -# @SevState: -# -# An enumeration of SEV state information used during @query-sev. -# -# @uninit: The guest is uninitialized. -# -# @launch-update: The guest is currently being launched; plaintext -# data and register state is being imported. -# -# @launch-secret: The guest is currently being launched; ciphertext -# data is being imported. -# -# @running: The guest is fully launched or migrated in. -# -# @send-update: The guest is currently being migrated out to another -# machine. -# -# @receive-update: The guest is currently being migrated from another -# machine. -# -# Since: 2.12 -## -{ 'enum': 'SevState', - 'data': ['uninit', 'launch-update', 'launch-secret', 'running', - 'send-update', 'receive-update' ], - 'if': 'TARGET_I386' } - -## -# @SevGuestType: -# -# An enumeration indicating the type of SEV guest being run. -# -# @sev: The guest is a legacy SEV or SEV-ES guest. -# -# @sev-snp: The guest is an SEV-SNP guest. -# -# Since: 6.2 -## -{ 'enum': 'SevGuestType', - 'data': [ 'sev', 'sev-snp' ], - 'if': 'TARGET_I386' } - -## -# @SevGuestInfo: -# -# Information specific to legacy SEV/SEV-ES guests. -# -# @policy: SEV policy value -# -# @handle: SEV firmware handle -# -# Since: 2.12 -## -{ 'struct': 'SevGuestInfo', - 'data': { 'policy': 'uint32', - 'handle': 'uint32' }, - 'if': 'TARGET_I386' } - -## -# @SevSnpGuestInfo: -# -# Information specific to SEV-SNP guests. -# -# @snp-policy: SEV-SNP policy value -# -# Since: 9.1 -## -{ 'struct': 'SevSnpGuestInfo', - 'data': { 'snp-policy': 'uint64' }, - 'if': 'TARGET_I386' } - -## -# @SevInfo: -# -# Information about Secure Encrypted Virtualization (SEV) support -# -# @enabled: true if SEV is active -# -# @api-major: SEV API major version -# -# @api-minor: SEV API minor version -# -# @build-id: SEV FW build id -# -# @state: SEV guest state -# -# @sev-type: Type of SEV guest being run -# -# Since: 2.12 -## -{ 'union': 'SevInfo', - 'base': { 'enabled': 'bool', - 'api-major': 'uint8', - 'api-minor' : 'uint8', - 'build-id' : 'uint8', - 'state' : 'SevState', - 'sev-type' : 'SevGuestType' }, - 'discriminator': 'sev-type', - 'data': { - 'sev': 'SevGuestInfo', - 'sev-snp': 'SevSnpGuestInfo' }, - 'if': 'TARGET_I386' } - - -## -# @query-sev: -# -# Returns information about SEV/SEV-ES/SEV-SNP. -# -# If unavailable due to an incompatible configuration the returned -# @enabled field is set to 'false' and the state of all other fields -# is unspecified. -# -# Returns: @SevInfo -# -# Since: 2.12 -# -# .. qmp-example:: -# -# -> { "execute": "query-sev" } -# <- { "return": { "enabled": true, "api-major" : 0, "api-minor" : 0, -# "build-id" : 0, "policy" : 0, "state" : "running", -# "handle" : 1 } } -## -{ 'command': 'query-sev', 'returns': 'SevInfo', - 'if': 'TARGET_I386' } - -## -# @SevLaunchMeasureInfo: -# -# SEV Guest Launch measurement information -# -# @data: the measurement value encoded in base64 -# -# Since: 2.12 -## -{ 'struct': 'SevLaunchMeasureInfo', 'data': {'data': 'str'}, - 'if': 'TARGET_I386' } - -## -# @query-sev-launch-measure: -# -# Query the SEV/SEV-ES guest launch information. -# -# This is only valid on x86 machines configured with KVM and the -# 'sev-guest' confidential virtualization object. The launch -# measurement for SEV-SNP guests is only available within the guest. -# -# Returns: The @SevLaunchMeasureInfo for the guest -# -# Errors: -# - If the launch measurement is unavailable, either due to an -# invalid guest configuration or if the guest has not reached -# the required SEV state, GenericError -# -# Since: 2.12 -# -# .. qmp-example:: -# -# -> { "execute": "query-sev-launch-measure" } -# <- { "return": { "data": "4l8LXeNlSPUDlXPJG5966/8%YZ" } } -## -{ 'command': 'query-sev-launch-measure', 'returns': 'SevLaunchMeasureInfo', - 'if': 'TARGET_I386' } - -## -# @SevCapability: -# -# The struct describes capability for a Secure Encrypted -# Virtualization feature. -# -# @pdh: Platform Diffie-Hellman key (base64 encoded) -# -# @cert-chain: PDH certificate chain (base64 encoded) -# -# @cpu0-id: Unique ID of CPU0 (base64 encoded) (since 7.1) -# -# @cbitpos: C-bit location in page table entry -# -# @reduced-phys-bits: Number of physical Address bit reduction when -# SEV is enabled -# -# Since: 2.12 -## -{ 'struct': 'SevCapability', - 'data': { 'pdh': 'str', - 'cert-chain': 'str', - 'cpu0-id': 'str', - 'cbitpos': 'int', - 'reduced-phys-bits': 'int'}, - 'if': 'TARGET_I386' } - -## -# @query-sev-capabilities: -# -# Get SEV capabilities. -# -# This is only supported on AMD X86 platforms with KVM enabled. -# -# Returns: SevCapability objects. -# -# Errors: -# - If SEV is not available on the platform, GenericError -# -# Since: 2.12 -# -# .. qmp-example:: -# -# -> { "execute": "query-sev-capabilities" } -# <- { "return": { "pdh": "8CCDD8DDD", "cert-chain": "888CCCDDDEE", -# "cpu0-id": "2lvmGwo+...61iEinw==", -# "cbitpos": 47, "reduced-phys-bits": 1}} -## -{ 'command': 'query-sev-capabilities', 'returns': 'SevCapability', - 'if': 'TARGET_I386' } - -## -# @sev-inject-launch-secret: -# -# This command injects a secret blob into memory of a SEV/SEV-ES -# guest. -# -# This is only valid on x86 machines configured with KVM and the -# 'sev-guest' confidential virtualization object. SEV-SNP guests do -# not support launch secret injection. -# -# @packet-header: the launch secret packet header encoded in base64 -# -# @secret: the launch secret data to be injected encoded in base64 -# -# @gpa: the guest physical address where secret will be injected. -# -# Errors: -# - If launch secret injection is not possible, either due to -# an invalid guest configuration, or if the guest has not -# reached the required SEV state, GenericError -# -# Since: 6.0 -## -{ 'command': 'sev-inject-launch-secret', - 'data': { 'packet-header': 'str', 'secret': 'str', '*gpa': 'uint64' }, - 'if': 'TARGET_I386' } - -## -# @SevAttestationReport: -# -# The struct describes attestation report for a Secure Encrypted -# Virtualization feature. -# -# @data: guest attestation report (base64 encoded) -# -# Since: 6.1 -## -{ 'struct': 'SevAttestationReport', - 'data': { 'data': 'str'}, - 'if': 'TARGET_I386' } - -## -# @query-sev-attestation-report: -# -# This command is used to get the SEV attestation report. -# -# This is only valid on x86 machines configured with KVM and the -# 'sev-guest' confidential virtualization object. The attestation -# report for SEV-SNP guests is only available within the guest. -# -# @mnonce: a random 16 bytes value encoded in base64 (it will be -# included in report) -# -# Returns: SevAttestationReport objects. -# -# Errors: -# - This will return an error if the attestation report is -# unavailable, either due to an invalid guest configuration -# or if the guest has not reached the required SEV state, -# GenericError -# -# Since: 6.1 -# -# .. qmp-example:: -# -# -> { "execute" : "query-sev-attestation-report", -# "arguments": { "mnonce": "aaaaaaa" } } -# <- { "return" : { "data": "aaaaaaaabbbddddd"} } -## -{ 'command': 'query-sev-attestation-report', - 'data': { 'mnonce': 'str' }, - 'returns': 'SevAttestationReport', - 'if': 'TARGET_I386' } - ## # @GICCapability: # diff --git a/stubs/meson.build b/stubs/meson.build index 9907b54c1e..9922ec7b88 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -78,6 +78,7 @@ if have_system stub_ss.add(files('win32-kbd-hook.c')) stub_ss.add(files('xen-hw-stub.c')) stub_ss.add(files('monitor-i386-rtc.c')) + stub_ss.add(files('monitor-i386-sev.c')) endif if have_system or have_user diff --git a/stubs/monitor-i386-sev.c b/stubs/monitor-i386-sev.c new file mode 100644 index 0000000000..d4f024128c --- /dev/null +++ b/stubs/monitor-i386-sev.c @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-i386.h" + +SevInfo *qmp_query_sev(Error **errp) +{ + error_setg(errp, "SEV is not available in this QEMU"); + return NULL; +} + +SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) +{ + error_setg(errp, "SEV is not available in this QEMU"); + return NULL; +} + +SevCapability *qmp_query_sev_capabilities(Error **errp) +{ + error_setg(errp, "SEV is not available in this QEMU"); + return NULL; +} + +void qmp_sev_inject_launch_secret(const char *packet_header, const char *secret, + bool has_gpa, uint64_t gpa, Error **errp) +{ + error_setg(errp, "SEV is not available in this QEMU"); +} + +SevAttestationReport *qmp_query_sev_attestation_report(const char *mnonce, + Error **errp) +{ + error_setg(errp, "SEV is not available in this QEMU"); + return NULL; +} diff --git a/target/i386/sev-system-stub.c b/target/i386/sev-system-stub.c index d5bf886e79..7c5c02a565 100644 --- a/target/i386/sev-system-stub.c +++ b/target/i386/sev-system-stub.c @@ -14,34 +14,9 @@ #include "qemu/osdep.h" #include "monitor/monitor.h" #include "monitor/hmp-target.h" -#include "qapi/qapi-commands-misc-target.h" #include "qapi/error.h" #include "sev.h" -SevInfo *qmp_query_sev(Error **errp) -{ - error_setg(errp, "SEV is not available in this QEMU"); - return NULL; -} - -SevLaunchMeasureInfo *qmp_query_sev_launch_measure(Error **errp) -{ - error_setg(errp, "SEV is not available in this QEMU"); - return NULL; -} - -SevCapability *qmp_query_sev_capabilities(Error **errp) -{ - error_setg(errp, "SEV is not available in this QEMU"); - return NULL; -} - -void qmp_sev_inject_launch_secret(const char *packet_header, const char *secret, - bool has_gpa, uint64_t gpa, Error **errp) -{ - error_setg(errp, "SEV is not available in this QEMU"); -} - int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) { g_assert_not_reached(); @@ -56,13 +31,6 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) g_assert_not_reached(); } -SevAttestationReport *qmp_query_sev_attestation_report(const char *mnonce, - Error **errp) -{ - error_setg(errp, "SEV is not available in this QEMU"); - return NULL; -} - void hmp_info_sev(Monitor *mon, const QDict *qdict) { monitor_printf(mon, "SEV is not available in this QEMU\n"); diff --git a/target/i386/sev.c b/target/i386/sev.c index 7ee700d6a3..56dd64e659 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -37,7 +37,7 @@ #include "qom/object.h" #include "monitor/monitor.h" #include "monitor/hmp-target.h" -#include "qapi/qapi-commands-misc-target.h" +#include "qapi/qapi-commands-misc-i386.h" #include "confidential-guest.h" #include "hw/i386/pc.h" #include "system/address-spaces.h" From 30fbb258717d81c32f11a0e4b7218442a43c4856 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 22 May 2025 12:05:31 -0700 Subject: [PATCH 1208/2760] qapi: expose query-gic-capability command unconditionally MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the TARGET_ARM condition from the query-gic-capability command. This requires providing a QMP command stub for non-ARM targets. This in turn requires moving the command out of misc-target.json, since that will trigger symbol poisoning errors when built from target independent code. Following the earlier precedent, this creates a misc-arm.json file to hold this ARM specific command. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-5-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster --- qapi/meson.build | 1 + qapi/misc-arm.json | 49 +++++++++++++++++++++++++++++++++++++++ qapi/misc-target.json | 44 ----------------------------------- qapi/qapi-schema.json | 1 + stubs/meson.build | 1 + stubs/monitor-arm-gic.c | 12 ++++++++++ target/arm/arm-qmp-cmds.c | 2 +- 7 files changed, 65 insertions(+), 45 deletions(-) create mode 100644 qapi/misc-arm.json create mode 100644 stubs/monitor-arm-gic.c diff --git a/qapi/meson.build b/qapi/meson.build index 3a9bd06104..5e93e6b8cf 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -64,6 +64,7 @@ if have_system 'qdev', 'pci', 'rocker', + 'misc-arm', 'misc-i386', 'tpm', 'uefi', diff --git a/qapi/misc-arm.json b/qapi/misc-arm.json new file mode 100644 index 0000000000..f5341372f5 --- /dev/null +++ b/qapi/misc-arm.json @@ -0,0 +1,49 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# SPDX-License-Identifier: GPL-2.0-or-later + +## +# @GICCapability: +# +# The struct describes capability for a specific GIC (Generic +# Interrupt Controller) version. These bits are not only decided by +# QEMU/KVM software version, but also decided by the hardware that the +# program is running upon. +# +# @version: version of GIC to be described. Currently, only 2 and 3 +# are supported. +# +# @emulated: whether current QEMU/hardware supports emulated GIC +# device in user space. +# +# @kernel: whether current QEMU/hardware supports hardware accelerated +# GIC device in kernel. +# +# Since: 2.6 +## +{ 'struct': 'GICCapability', + 'data': { 'version': 'int', + 'emulated': 'bool', + 'kernel': 'bool' } } + +## +# @query-gic-capabilities: +# +# It will return a list of GICCapability objects that describe its +# capability bits. +# +# On non-ARM targets this command will report an error as the GIC +# technology is not applicable. +# +# Returns: a list of GICCapability objects. +# +# Since: 2.6 +# +# .. qmp-example:: +# +# -> { "execute": "query-gic-capabilities" } +# <- { "return": [{ "version": 2, "emulated": true, "kernel": false }, +# { "version": 3, "emulated": false, "kernel": true } ] } +## +{ 'command': 'query-gic-capabilities', 'returns': ['GICCapability'] } diff --git a/qapi/misc-target.json b/qapi/misc-target.json index c0d7b311f3..cc472ce91c 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -2,50 +2,6 @@ # vim: filetype=python # -## -# @GICCapability: -# -# The struct describes capability for a specific GIC (Generic -# Interrupt Controller) version. These bits are not only decided by -# QEMU/KVM software version, but also decided by the hardware that the -# program is running upon. -# -# @version: version of GIC to be described. Currently, only 2 and 3 -# are supported. -# -# @emulated: whether current QEMU/hardware supports emulated GIC -# device in user space. -# -# @kernel: whether current QEMU/hardware supports hardware accelerated -# GIC device in kernel. -# -# Since: 2.6 -## -{ 'struct': 'GICCapability', - 'data': { 'version': 'int', - 'emulated': 'bool', - 'kernel': 'bool' }, - 'if': 'TARGET_ARM' } - -## -# @query-gic-capabilities: -# -# This command is ARM-only. It will return a list of GICCapability -# objects that describe its capability bits. -# -# Returns: a list of GICCapability objects. -# -# Since: 2.6 -# -# .. qmp-example:: -# -# -> { "execute": "query-gic-capabilities" } -# <- { "return": [{ "version": 2, "emulated": true, "kernel": false }, -# { "version": 3, "emulated": false, "kernel": true } ] } -## -{ 'command': 'query-gic-capabilities', 'returns': ['GICCapability'], - 'if': 'TARGET_ARM' } - ## # @SgxEpcSection: # diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index 96f6aa4413..e96bff8d38 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -61,6 +61,7 @@ { 'include': 'replay.json' } { 'include': 'yank.json' } { 'include': 'misc.json' } +{ 'include': 'misc-arm.json' } { 'include': 'misc-i386.json' } { 'include': 'misc-target.json' } { 'include': 'audio.json' } diff --git a/stubs/meson.build b/stubs/meson.build index 9922ec7b88..07e9d3799a 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -77,6 +77,7 @@ if have_system stub_ss.add(files('target-monitor-defs.c')) stub_ss.add(files('win32-kbd-hook.c')) stub_ss.add(files('xen-hw-stub.c')) + stub_ss.add(files('monitor-arm-gic.c')) stub_ss.add(files('monitor-i386-rtc.c')) stub_ss.add(files('monitor-i386-sev.c')) endif diff --git a/stubs/monitor-arm-gic.c b/stubs/monitor-arm-gic.c new file mode 100644 index 0000000000..b3429243ef --- /dev/null +++ b/stubs/monitor-arm-gic.c @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-arm.h" + + +GICCapabilityList *qmp_query_gic_capabilities(Error **errp) +{ + error_setg(errp, "GIC hardware is not available on this target"); + return NULL; +} diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c index a1a944adb4..ef18c867ca 100644 --- a/target/arm/arm-qmp-cmds.c +++ b/target/arm/arm-qmp-cmds.c @@ -27,7 +27,7 @@ #include "qapi/visitor.h" #include "qapi/qobject-input-visitor.h" #include "qapi/qapi-commands-machine-target.h" -#include "qapi/qapi-commands-misc-target.h" +#include "qapi/qapi-commands-misc-arm.h" #include "qobject/qdict.h" #include "qom/qom-qobject.h" From 28a6a99834cd126f80f426ac3fe00625f165ebea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 22 May 2025 12:05:32 -0700 Subject: [PATCH 1209/2760] qapi: make SGX commands unconditionally available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the TARGET_I386 condition from the SGX confidential virtualization commands, moving them to the recently introduced misc-i386.json QAPI file, given they are inherantly i386 specific commands. Observe a pre-existing bug that the "SGXEPCSection" struct lacked a TARGET_I386 condition, despite its only usage being behind a TARGET_I386 condition. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-6-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster --- hw/i386/sgx-stub.c | 2 +- hw/i386/sgx.c | 2 +- qapi/misc-i386.json | 77 +++++++++++++++++++++++++++++++++++++++ qapi/misc-target.json | 79 ---------------------------------------- stubs/meson.build | 1 + stubs/monitor-i386-sgx.c | 17 +++++++++ 6 files changed, 97 insertions(+), 81 deletions(-) create mode 100644 stubs/monitor-i386-sgx.c diff --git a/hw/i386/sgx-stub.c b/hw/i386/sgx-stub.c index ccb21a975d..d295e54d23 100644 --- a/hw/i386/sgx-stub.c +++ b/hw/i386/sgx-stub.c @@ -3,8 +3,8 @@ #include "monitor/hmp-target.h" #include "hw/i386/pc.h" #include "hw/i386/sgx-epc.h" +#include "qapi/qapi-commands-misc-i386.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc-target.h" void sgx_epc_build_srat(GArray *table_data) { diff --git a/hw/i386/sgx.c b/hw/i386/sgx.c index c80203b438..e2801546ad 100644 --- a/hw/i386/sgx.c +++ b/hw/i386/sgx.c @@ -19,7 +19,7 @@ #include "monitor/hmp-target.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "qapi/qapi-commands-misc-target.h" +#include "qapi/qapi-commands-misc-i386.h" #include "system/address-spaces.h" #include "system/hw_accel.h" #include "system/reset.h" diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json index 360adadfe0..d5d1af55a4 100644 --- a/qapi/misc-i386.json +++ b/qapi/misc-i386.json @@ -300,3 +300,80 @@ { 'command': 'query-sev-attestation-report', 'data': { 'mnonce': 'str' }, 'returns': 'SevAttestationReport' } + +## +# @SgxEpcSection: +# +# Information about intel SGX EPC section +# +# @node: the numa node +# +# @size: the size of EPC section +# +# Since: 7.0 +## +{ 'struct': 'SgxEpcSection', + 'data': { 'node': 'int', + 'size': 'uint64'}} + +## +# @SgxInfo: +# +# Information about intel Safe Guard eXtension (SGX) support +# +# @sgx: true if SGX is supported +# +# @sgx1: true if SGX1 is supported +# +# @sgx2: true if SGX2 is supported +# +# @flc: true if FLC is supported +# +# @sections: The EPC sections information (Since: 7.0) +# +# Since: 6.2 +## +{ 'struct': 'SgxInfo', + 'data': { 'sgx': 'bool', + 'sgx1': 'bool', + 'sgx2': 'bool', + 'flc': 'bool', + 'sections': ['SgxEpcSection']} } + +## +# @query-sgx: +# +# Returns information about configured SGX capabilities of guest +# +# Returns: @SgxInfo +# +# Since: 6.2 +# +# .. qmp-example:: +# +# -> { "execute": "query-sgx" } +# <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, +# "flc": true, +# "sections": [{"node": 0, "size": 67108864}, +# {"node": 1, "size": 29360128}]} } +## +{ 'command': 'query-sgx', 'returns': 'SgxInfo' } + +## +# @query-sgx-capabilities: +# +# Returns information about SGX capabilities of host +# +# Returns: @SgxInfo +# +# Since: 6.2 +# +# .. qmp-example:: +# +# -> { "execute": "query-sgx-capabilities" } +# <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, +# "flc": true, +# "section" : [{"node": 0, "size": 67108864}, +# {"node": 1, "size": 29360128}]} } +## +{ 'command': 'query-sgx-capabilities', 'returns': 'SgxInfo' } diff --git a/qapi/misc-target.json b/qapi/misc-target.json index cc472ce91c..d62db37d7c 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -2,85 +2,6 @@ # vim: filetype=python # -## -# @SgxEpcSection: -# -# Information about intel SGX EPC section -# -# @node: the numa node -# -# @size: the size of EPC section -# -# Since: 7.0 -## -{ 'struct': 'SgxEpcSection', - 'data': { 'node': 'int', - 'size': 'uint64'}} - -## -# @SgxInfo: -# -# Information about intel Safe Guard eXtension (SGX) support -# -# @sgx: true if SGX is supported -# -# @sgx1: true if SGX1 is supported -# -# @sgx2: true if SGX2 is supported -# -# @flc: true if FLC is supported -# -# @sections: The EPC sections information (Since: 7.0) -# -# Since: 6.2 -## -{ 'struct': 'SgxInfo', - 'data': { 'sgx': 'bool', - 'sgx1': 'bool', - 'sgx2': 'bool', - 'flc': 'bool', - 'sections': ['SgxEpcSection']}, - 'if': 'TARGET_I386' } - -## -# @query-sgx: -# -# Returns information about configured SGX capabilities of guest -# -# Returns: @SgxInfo -# -# Since: 6.2 -# -# .. qmp-example:: -# -# -> { "execute": "query-sgx" } -# <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, -# "flc": true, -# "sections": [{"node": 0, "size": 67108864}, -# {"node": 1, "size": 29360128}]} } -## -{ 'command': 'query-sgx', 'returns': 'SgxInfo', 'if': 'TARGET_I386' } - -## -# @query-sgx-capabilities: -# -# Returns information about SGX capabilities of host -# -# Returns: @SgxInfo -# -# Since: 6.2 -# -# .. qmp-example:: -# -# -> { "execute": "query-sgx-capabilities" } -# <- { "return": { "sgx": true, "sgx1" : true, "sgx2" : true, -# "flc": true, -# "section" : [{"node": 0, "size": 67108864}, -# {"node": 1, "size": 29360128}]} } -## -{ 'command': 'query-sgx-capabilities', 'returns': 'SgxInfo', 'if': 'TARGET_I386' } - - ## # @EvtchnPortType: # diff --git a/stubs/meson.build b/stubs/meson.build index 07e9d3799a..f2eb488018 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -80,6 +80,7 @@ if have_system stub_ss.add(files('monitor-arm-gic.c')) stub_ss.add(files('monitor-i386-rtc.c')) stub_ss.add(files('monitor-i386-sev.c')) + stub_ss.add(files('monitor-i386-sgx.c')) endif if have_system or have_user diff --git a/stubs/monitor-i386-sgx.c b/stubs/monitor-i386-sgx.c new file mode 100644 index 0000000000..00e081d52d --- /dev/null +++ b/stubs/monitor-i386-sgx.c @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-i386.h" + +SgxInfo *qmp_query_sgx(Error **errp) +{ + error_setg(errp, "SGX support is not compiled in"); + return NULL; +} + +SgxInfo *qmp_query_sgx_capabilities(Error **errp) +{ + error_setg(errp, "SGX support is not compiled in"); + return NULL; +} From 4b679a94c69db2526ddf77d4a0bd6b041b9fa133 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 22 May 2025 12:05:33 -0700 Subject: [PATCH 1210/2760] qapi: make Xen event commands unconditionally available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the TARGET_I386 condition from the Xen event channel commands, moving them to the recently introduced misc-i386.json QAPI file, given they are inherantly i386 specific commands. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé Acked-by: David Woodhouse Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-7-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster --- hw/i386/kvm/xen-stubs.c | 13 ----- hw/i386/kvm/xen_evtchn.c | 2 +- qapi/misc-i386.json | 107 +++++++++++++++++++++++++++++++++++++ qapi/misc-target.json | 111 --------------------------------------- stubs/meson.build | 1 + stubs/monitor-i386-xen.c | 16 ++++++ 6 files changed, 125 insertions(+), 125 deletions(-) create mode 100644 stubs/monitor-i386-xen.c diff --git a/hw/i386/kvm/xen-stubs.c b/hw/i386/kvm/xen-stubs.c index d03131e686..ce73119ee7 100644 --- a/hw/i386/kvm/xen-stubs.c +++ b/hw/i386/kvm/xen-stubs.c @@ -12,7 +12,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc-target.h" #include "xen_evtchn.h" #include "xen_primary_console.h" @@ -38,15 +37,3 @@ void xen_primary_console_create(void) void xen_primary_console_set_be_port(uint16_t port) { } -#ifdef TARGET_I386 -EvtchnInfoList *qmp_xen_event_list(Error **errp) -{ - error_setg(errp, "Xen event channel emulation not enabled"); - return NULL; -} - -void qmp_xen_event_inject(uint32_t port, Error **errp) -{ - error_setg(errp, "Xen event channel emulation not enabled"); -} -#endif diff --git a/hw/i386/kvm/xen_evtchn.c b/hw/i386/kvm/xen_evtchn.c index b5190549a8..dd566c4967 100644 --- a/hw/i386/kvm/xen_evtchn.c +++ b/hw/i386/kvm/xen_evtchn.c @@ -19,7 +19,7 @@ #include "monitor/monitor.h" #include "monitor/hmp.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc-target.h" +#include "qapi/qapi-commands-misc-i386.h" #include "qobject/qdict.h" #include "qom/object.h" #include "exec/target_page.h" diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json index d5d1af55a4..3f88a5b28e 100644 --- a/qapi/misc-i386.json +++ b/qapi/misc-i386.json @@ -377,3 +377,110 @@ # {"node": 1, "size": 29360128}]} } ## { 'command': 'query-sgx-capabilities', 'returns': 'SgxInfo' } + +## +# @EvtchnPortType: +# +# An enumeration of Xen event channel port types. +# +# @closed: The port is unused. +# +# @unbound: The port is allocated and ready to be bound. +# +# @interdomain: The port is connected as an interdomain interrupt. +# +# @pirq: The port is bound to a physical IRQ (PIRQ). +# +# @virq: The port is bound to a virtual IRQ (VIRQ). +# +# @ipi: The post is an inter-processor interrupt (IPI). +# +# Since: 8.0 +## +{ 'enum': 'EvtchnPortType', + 'data': ['closed', 'unbound', 'interdomain', 'pirq', 'virq', 'ipi'] } + +## +# @EvtchnInfo: +# +# Information about a Xen event channel port +# +# @port: the port number +# +# @vcpu: target vCPU for this port +# +# @type: the port type +# +# @remote-domain: remote domain for interdomain ports +# +# @target: remote port ID, or virq/pirq number +# +# @pending: port is currently active pending delivery +# +# @masked: port is masked +# +# Since: 8.0 +## +{ 'struct': 'EvtchnInfo', + 'data': {'port': 'uint16', + 'vcpu': 'uint32', + 'type': 'EvtchnPortType', + 'remote-domain': 'str', + 'target': 'uint16', + 'pending': 'bool', + 'masked': 'bool'} } + + +## +# @xen-event-list: +# +# Query the Xen event channels opened by the guest. +# +# Returns: list of open event channel ports. +# +# Since: 8.0 +# +# .. qmp-example:: +# +# -> { "execute": "xen-event-list" } +# <- { "return": [ +# { +# "pending": false, +# "port": 1, +# "vcpu": 1, +# "remote-domain": "qemu", +# "masked": false, +# "type": "interdomain", +# "target": 1 +# }, +# { +# "pending": false, +# "port": 2, +# "vcpu": 0, +# "remote-domain": "", +# "masked": false, +# "type": "virq", +# "target": 0 +# } +# ] +# } +## +{ 'command': 'xen-event-list', + 'returns': ['EvtchnInfo'] } + +## +# @xen-event-inject: +# +# Inject a Xen event channel port (interrupt) to the guest. +# +# @port: The port number +# +# Since: 8.0 +# +# .. qmp-example:: +# +# -> { "execute": "xen-event-inject", "arguments": { "port": 1 } } +# <- { "return": { } } +## +{ 'command': 'xen-event-inject', + 'data': { 'port': 'uint32' } } diff --git a/qapi/misc-target.json b/qapi/misc-target.json index d62db37d7c..c9ea1ab23e 100644 --- a/qapi/misc-target.json +++ b/qapi/misc-target.json @@ -1,114 +1,3 @@ # -*- Mode: Python -*- # vim: filetype=python # - -## -# @EvtchnPortType: -# -# An enumeration of Xen event channel port types. -# -# @closed: The port is unused. -# -# @unbound: The port is allocated and ready to be bound. -# -# @interdomain: The port is connected as an interdomain interrupt. -# -# @pirq: The port is bound to a physical IRQ (PIRQ). -# -# @virq: The port is bound to a virtual IRQ (VIRQ). -# -# @ipi: The post is an inter-processor interrupt (IPI). -# -# Since: 8.0 -## -{ 'enum': 'EvtchnPortType', - 'data': ['closed', 'unbound', 'interdomain', 'pirq', 'virq', 'ipi'], - 'if': 'TARGET_I386' } - -## -# @EvtchnInfo: -# -# Information about a Xen event channel port -# -# @port: the port number -# -# @vcpu: target vCPU for this port -# -# @type: the port type -# -# @remote-domain: remote domain for interdomain ports -# -# @target: remote port ID, or virq/pirq number -# -# @pending: port is currently active pending delivery -# -# @masked: port is masked -# -# Since: 8.0 -## -{ 'struct': 'EvtchnInfo', - 'data': {'port': 'uint16', - 'vcpu': 'uint32', - 'type': 'EvtchnPortType', - 'remote-domain': 'str', - 'target': 'uint16', - 'pending': 'bool', - 'masked': 'bool'}, - 'if': 'TARGET_I386' } - - -## -# @xen-event-list: -# -# Query the Xen event channels opened by the guest. -# -# Returns: list of open event channel ports. -# -# Since: 8.0 -# -# .. qmp-example:: -# -# -> { "execute": "xen-event-list" } -# <- { "return": [ -# { -# "pending": false, -# "port": 1, -# "vcpu": 1, -# "remote-domain": "qemu", -# "masked": false, -# "type": "interdomain", -# "target": 1 -# }, -# { -# "pending": false, -# "port": 2, -# "vcpu": 0, -# "remote-domain": "", -# "masked": false, -# "type": "virq", -# "target": 0 -# } -# ] -# } -## -{ 'command': 'xen-event-list', - 'returns': ['EvtchnInfo'], - 'if': 'TARGET_I386' } - -## -# @xen-event-inject: -# -# Inject a Xen event channel port (interrupt) to the guest. -# -# @port: The port number -# -# Since: 8.0 -# -# .. qmp-example:: -# -# -> { "execute": "xen-event-inject", "arguments": { "port": 1 } } -# <- { "return": { } } -## -{ 'command': 'xen-event-inject', - 'data': { 'port': 'uint32' }, - 'if': 'TARGET_I386' } diff --git a/stubs/meson.build b/stubs/meson.build index f2eb488018..0ef11976a2 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -81,6 +81,7 @@ if have_system stub_ss.add(files('monitor-i386-rtc.c')) stub_ss.add(files('monitor-i386-sev.c')) stub_ss.add(files('monitor-i386-sgx.c')) + stub_ss.add(files('monitor-i386-xen.c')) endif if have_system or have_user diff --git a/stubs/monitor-i386-xen.c b/stubs/monitor-i386-xen.c new file mode 100644 index 0000000000..95b826f979 --- /dev/null +++ b/stubs/monitor-i386-xen.c @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-misc-i386.h" + +EvtchnInfoList *qmp_xen_event_list(Error **errp) +{ + error_setg(errp, "Xen event channel emulation not enabled"); + return NULL; +} + +void qmp_xen_event_inject(uint32_t port, Error **errp) +{ + error_setg(errp, "Xen event channel emulation not enabled"); +} From 0a1f83c3fb63229a77e2d0c5f1fe5d7e09dbf3b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 22 May 2025 12:05:34 -0700 Subject: [PATCH 1211/2760] qapi: remove the misc-target.json file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This file is now empty and can thus be removed. Observe the pre-existing bug with s390-skeys.c and target/i386/monitor.c both including qapi-commands-misc-target.h despite not requiring it. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-8-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster --- hw/s390x/s390-skeys.c | 1 - qapi/meson.build | 1 - qapi/misc-target.json | 3 --- qapi/qapi-schema.json | 1 - target/i386/monitor.c | 1 - 5 files changed, 7 deletions(-) delete mode 100644 qapi/misc-target.json diff --git a/hw/s390x/s390-skeys.c b/hw/s390x/s390-skeys.c index aedb62b2d3..8eeecfd58f 100644 --- a/hw/s390x/s390-skeys.c +++ b/hw/s390x/s390-skeys.c @@ -17,7 +17,6 @@ #include "hw/s390x/storage-keys.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" -#include "qapi/qapi-commands-misc-target.h" #include "qobject/qdict.h" #include "qemu/error-report.h" #include "system/memory_mapping.h" diff --git a/qapi/meson.build b/qapi/meson.build index 5e93e6b8cf..ffe44f9e0b 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -42,7 +42,6 @@ qapi_all_modules = [ 'machine-target', 'migration', 'misc', - 'misc-target', 'net', 'pragma', 'qom', diff --git a/qapi/misc-target.json b/qapi/misc-target.json deleted file mode 100644 index c9ea1ab23e..0000000000 --- a/qapi/misc-target.json +++ /dev/null @@ -1,3 +0,0 @@ -# -*- Mode: Python -*- -# vim: filetype=python -# diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index e96bff8d38..d8eb79cfda 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -63,7 +63,6 @@ { 'include': 'misc.json' } { 'include': 'misc-arm.json' } { 'include': 'misc-i386.json' } -{ 'include': 'misc-target.json' } { 'include': 'audio.json' } { 'include': 'acpi.json' } { 'include': 'pci.json' } diff --git a/target/i386/monitor.c b/target/i386/monitor.c index 3ea92b066e..3c9b6ca62f 100644 --- a/target/i386/monitor.c +++ b/target/i386/monitor.c @@ -29,7 +29,6 @@ #include "monitor/hmp.h" #include "qobject/qdict.h" #include "qapi/error.h" -#include "qapi/qapi-commands-misc-target.h" #include "qapi/qapi-commands-misc.h" /* Perform linear address sign extension */ From 448553bb7c2a2fe518d7dc41c58a25d6d31831da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 22 May 2025 12:05:35 -0700 Subject: [PATCH 1212/2760] qapi: Make CpuModelExpansionInfo::deprecated-props optional and generic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We'd like to have some unified QAPI schema. Having a structure field conditional to a target being built in is not very practical. While @deprecated-props is only used by s390x target, it is generic enough and could be used by other targets (assuming we expand CpuModelExpansionType enum values). Let's always include this field, regardless of the target, but make it optional. This is not a compatibility break only because the field remains present always on S390x. Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-9-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster --- qapi/machine-target.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/qapi/machine-target.json b/qapi/machine-target.json index 426ce4ee82..d8dbda4b50 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -244,19 +244,18 @@ # # @model: the expanded CpuModelInfo. # -# @deprecated-props: a list of properties that are flagged as +# @deprecated-props: an optional list of properties that are flagged as # deprecated by the CPU vendor. The list depends on the # CpuModelExpansionType: "static" properties are a subset of the # enabled-properties for the expanded model; "full" properties are # a set of properties that are deprecated across all models for -# the architecture. (since: 9.1). +# the architecture. (since: 10.1 -- since 9.1 on s390x --). # # Since: 2.8 ## { 'struct': 'CpuModelExpansionInfo', 'data': { 'model': 'CpuModelInfo', - 'deprecated-props' : { 'type': ['str'], - 'if': 'TARGET_S390X' } }, + '*deprecated-props' : ['str'] }, 'if': { 'any': [ 'TARGET_S390X', 'TARGET_I386', 'TARGET_ARM', From d6758495d888e0ef8175824dadcdeb85274c14f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 22 May 2025 12:05:36 -0700 Subject: [PATCH 1213/2760] qapi: make most CPU commands unconditionally available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the TARGET_* conditions from all the CPU commands that are conceptually target independent. Top level stubs are provided to cope with targets which do not currently implement all of the commands. Adjust the doc comments accordingly. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-10-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster --- MAINTAINERS | 1 - qapi/machine-target.json | 397 -------------------------- qapi/machine.json | 363 +++++++++++++++++++++++ stubs/meson.build | 2 + stubs/monitor-cpu-s390x.c | 23 ++ stubs/monitor-cpu.c | 21 ++ target/arm/arm-qmp-cmds.c | 2 +- target/i386/cpu-system.c | 2 +- target/i386/cpu.c | 2 +- target/loongarch/loongarch-qmp-cmds.c | 2 +- target/mips/system/mips-qmp-cmds.c | 12 +- target/ppc/ppc-qmp-cmds.c | 12 +- target/riscv/riscv-qmp-cmds.c | 2 +- target/s390x/cpu_models_system.c | 2 +- 14 files changed, 437 insertions(+), 406 deletions(-) create mode 100644 stubs/monitor-cpu-s390x.c create mode 100644 stubs/monitor-cpu.c diff --git a/MAINTAINERS b/MAINTAINERS index 7060cf49b9..e27d1458c5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1938,7 +1938,6 @@ F: hw/core/numa.c F: hw/cpu/cluster.c F: qapi/machine.json F: qapi/machine-common.json -F: qapi/machine-target.json F: include/hw/boards.h F: include/hw/core/cpu.h F: include/hw/cpu/cluster.h diff --git a/qapi/machine-target.json b/qapi/machine-target.json index d8dbda4b50..f19e34adaf 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-target.json @@ -6,403 +6,6 @@ { 'include': 'machine-common.json' } -## -# @CpuModelInfo: -# -# Virtual CPU model. -# -# A CPU model consists of the name of a CPU definition, to which delta -# changes are applied (e.g. features added/removed). Most magic -# values that an architecture might require should be hidden behind -# the name. However, if required, architectures can expose relevant -# properties. -# -# @name: the name of the CPU definition the model is based on -# -# @props: a dictionary of QOM properties to be applied -# -# Since: 2.8 -## -{ 'struct': 'CpuModelInfo', - 'data': { 'name': 'str', - '*props': 'any' } } - -## -# @CpuModelExpansionType: -# -# An enumeration of CPU model expansion types. -# -# @static: Expand to a static CPU model, a combination of a static -# base model name and property delta changes. As the static base -# model will never change, the expanded CPU model will be the -# same, independent of QEMU version, machine type, machine -# options, and accelerator options. Therefore, the resulting -# model can be used by tooling without having to specify a -# compatibility machine - e.g. when displaying the "host" model. -# The @static CPU models are migration-safe. -# -# @full: Expand all properties. The produced model is not guaranteed -# to be migration-safe, but allows tooling to get an insight and -# work with model details. -# -# .. note:: When a non-migration-safe CPU model is expanded in static -# mode, some features enabled by the CPU model may be omitted, -# because they can't be implemented by a static CPU model -# definition (e.g. cache info passthrough and PMU passthrough in -# x86). If you need an accurate representation of the features -# enabled by a non-migration-safe CPU model, use @full. If you -# need a static representation that will keep ABI compatibility -# even when changing QEMU version or machine-type, use @static (but -# keep in mind that some features may be omitted). -# -# Since: 2.8 -## -{ 'enum': 'CpuModelExpansionType', - 'data': [ 'static', 'full' ] } - -## -# @CpuModelCompareResult: -# -# An enumeration of CPU model comparison results. The result is -# usually calculated using e.g. CPU features or CPU generations. -# -# @incompatible: If model A is incompatible to model B, model A is not -# guaranteed to run where model B runs and the other way around. -# -# @identical: If model A is identical to model B, model A is -# guaranteed to run where model B runs and the other way around. -# -# @superset: If model A is a superset of model B, model B is -# guaranteed to run where model A runs. There are no guarantees -# about the other way. -# -# @subset: If model A is a subset of model B, model A is guaranteed to -# run where model B runs. There are no guarantees about the other -# way. -# -# Since: 2.8 -## -{ 'enum': 'CpuModelCompareResult', - 'data': [ 'incompatible', 'identical', 'superset', 'subset' ] } - -## -# @CpuModelBaselineInfo: -# -# The result of a CPU model baseline. -# -# @model: the baselined CpuModelInfo. -# -# Since: 2.8 -## -{ 'struct': 'CpuModelBaselineInfo', - 'data': { 'model': 'CpuModelInfo' }, - 'if': 'TARGET_S390X' } - -## -# @CpuModelCompareInfo: -# -# The result of a CPU model comparison. -# -# @result: The result of the compare operation. -# -# @responsible-properties: List of properties that led to the -# comparison result not being identical. -# -# @responsible-properties is a list of QOM property names that led to -# both CPUs not being detected as identical. For identical models, -# this list is empty. If a QOM property is read-only, that means -# there's no known way to make the CPU models identical. If the -# special property name "type" is included, the models are by -# definition not identical and cannot be made identical. -# -# Since: 2.8 -## -{ 'struct': 'CpuModelCompareInfo', - 'data': { 'result': 'CpuModelCompareResult', - 'responsible-properties': ['str'] }, - 'if': 'TARGET_S390X' } - -## -# @query-cpu-model-comparison: -# -# Compares two CPU models, @modela and @modelb, returning how they -# compare in a specific configuration. The results indicates how -# both models compare regarding runnability. This result can be -# used by tooling to make decisions if a certain CPU model will -# run in a certain configuration or if a compatible CPU model has -# to be created by baselining. -# -# Usually, a CPU model is compared against the maximum possible CPU -# model of a certain configuration (e.g. the "host" model for KVM). -# If that CPU model is identical or a subset, it will run in that -# configuration. -# -# The result returned by this command may be affected by: -# -# * QEMU version: CPU models may look different depending on the QEMU -# version. (Except for CPU models reported as "static" in -# query-cpu-definitions.) -# * machine-type: CPU model may look different depending on the -# machine-type. (Except for CPU models reported as "static" in -# query-cpu-definitions.) -# * machine options (including accelerator): in some architectures, -# CPU models may look different depending on machine and accelerator -# options. (Except for CPU models reported as "static" in -# query-cpu-definitions.) -# * "-cpu" arguments and global properties: arguments to the -cpu -# option and global properties may affect expansion of CPU models. -# Using query-cpu-model-expansion while using these is not advised. -# -# Some architectures may not support comparing CPU models. s390x -# supports comparing CPU models. -# -# @modela: description of the first CPU model to compare, referred to -# as "model A" in CpuModelCompareResult -# -# @modelb: description of the second CPU model to compare, referred to -# as "model B" in CpuModelCompareResult -# -# Returns: a CpuModelCompareInfo describing how both CPU models -# compare -# -# Errors: -# - if comparing CPU models is not supported -# - if a model cannot be used -# - if a model contains an unknown cpu definition name, unknown -# properties or properties with wrong types. -# -# .. note:: This command isn't specific to s390x, but is only -# implemented on this architecture currently. -# -# Since: 2.8 -## -{ 'command': 'query-cpu-model-comparison', - 'data': { 'modela': 'CpuModelInfo', 'modelb': 'CpuModelInfo' }, - 'returns': 'CpuModelCompareInfo', - 'if': 'TARGET_S390X' } - -## -# @query-cpu-model-baseline: -# -# Baseline two CPU models, @modela and @modelb, creating a compatible -# third model. The created model will always be a static, -# migration-safe CPU model (see "static" CPU model expansion for -# details). -# -# This interface can be used by tooling to create a compatible CPU -# model out two CPU models. The created CPU model will be identical -# to or a subset of both CPU models when comparing them. Therefore, -# the created CPU model is guaranteed to run where the given CPU -# models run. -# -# The result returned by this command may be affected by: -# -# * QEMU version: CPU models may look different depending on the QEMU -# version. (Except for CPU models reported as "static" in -# query-cpu-definitions.) -# * machine-type: CPU model may look different depending on the -# machine-type. (Except for CPU models reported as "static" in -# query-cpu-definitions.) -# * machine options (including accelerator): in some architectures, -# CPU models may look different depending on machine and accelerator -# options. (Except for CPU models reported as "static" in -# query-cpu-definitions.) -# * "-cpu" arguments and global properties: arguments to the -cpu -# option and global properties may affect expansion of CPU models. -# Using query-cpu-model-expansion while using these is not advised. -# -# Some architectures may not support baselining CPU models. s390x -# supports baselining CPU models. -# -# @modela: description of the first CPU model to baseline -# -# @modelb: description of the second CPU model to baseline -# -# Returns: a CpuModelBaselineInfo describing the baselined CPU model -# -# Errors: -# - if baselining CPU models is not supported -# - if a model cannot be used -# - if a model contains an unknown cpu definition name, unknown -# properties or properties with wrong types. -# -# .. note:: This command isn't specific to s390x, but is only -# implemented on this architecture currently. -# -# Since: 2.8 -## -{ 'command': 'query-cpu-model-baseline', - 'data': { 'modela': 'CpuModelInfo', - 'modelb': 'CpuModelInfo' }, - 'returns': 'CpuModelBaselineInfo', - 'if': 'TARGET_S390X' } - -## -# @CpuModelExpansionInfo: -# -# The result of a cpu model expansion. -# -# @model: the expanded CpuModelInfo. -# -# @deprecated-props: an optional list of properties that are flagged as -# deprecated by the CPU vendor. The list depends on the -# CpuModelExpansionType: "static" properties are a subset of the -# enabled-properties for the expanded model; "full" properties are -# a set of properties that are deprecated across all models for -# the architecture. (since: 10.1 -- since 9.1 on s390x --). -# -# Since: 2.8 -## -{ 'struct': 'CpuModelExpansionInfo', - 'data': { 'model': 'CpuModelInfo', - '*deprecated-props' : ['str'] }, - 'if': { 'any': [ 'TARGET_S390X', - 'TARGET_I386', - 'TARGET_ARM', - 'TARGET_LOONGARCH64', - 'TARGET_RISCV' ] } } - -## -# @query-cpu-model-expansion: -# -# Expands a given CPU model, @model, (or a combination of CPU model + -# additional options) to different granularities, specified by @type, -# allowing tooling to get an understanding what a specific CPU model -# looks like in QEMU under a certain configuration. -# -# This interface can be used to query the "host" CPU model. -# -# The data returned by this command may be affected by: -# -# * QEMU version: CPU models may look different depending on the QEMU -# version. (Except for CPU models reported as "static" in -# query-cpu-definitions.) -# * machine-type: CPU model may look different depending on the -# machine-type. (Except for CPU models reported as "static" in -# query-cpu-definitions.) -# * machine options (including accelerator): in some architectures, -# CPU models may look different depending on machine and accelerator -# options. (Except for CPU models reported as "static" in -# query-cpu-definitions.) -# * "-cpu" arguments and global properties: arguments to the -cpu -# option and global properties may affect expansion of CPU models. -# Using query-cpu-model-expansion while using these is not advised. -# -# Some architectures may not support all expansion types. s390x -# supports "full" and "static". Arm only supports "full". -# -# @model: description of the CPU model to expand -# -# @type: expansion type, specifying how to expand the CPU model -# -# Returns: a CpuModelExpansionInfo describing the expanded CPU model -# -# Errors: -# - if expanding CPU models is not supported -# - if the model cannot be expanded -# - if the model contains an unknown CPU definition name, unknown -# properties or properties with a wrong type -# - if an expansion type is not supported -# -# Since: 2.8 -## -{ 'command': 'query-cpu-model-expansion', - 'data': { 'type': 'CpuModelExpansionType', - 'model': 'CpuModelInfo' }, - 'returns': 'CpuModelExpansionInfo', - 'if': { 'any': [ 'TARGET_S390X', - 'TARGET_I386', - 'TARGET_ARM', - 'TARGET_LOONGARCH64', - 'TARGET_RISCV' ] } } - -## -# @CpuDefinitionInfo: -# -# Virtual CPU definition. -# -# @name: the name of the CPU definition -# -# @migration-safe: whether a CPU definition can be safely used for -# migration in combination with a QEMU compatibility machine when -# migrating between different QEMU versions and between hosts with -# different sets of (hardware or software) capabilities. If not -# provided, information is not available and callers should not -# assume the CPU definition to be migration-safe. (since 2.8) -# -# @static: whether a CPU definition is static and will not change -# depending on QEMU version, machine type, machine options and -# accelerator options. A static model is always migration-safe. -# (since 2.8) -# -# @unavailable-features: List of properties that prevent the CPU model -# from running in the current host. (since 2.8) -# -# @typename: Type name that can be used as argument to -# @device-list-properties, to introspect properties configurable -# using -cpu or -global. (since 2.9) -# -# @alias-of: Name of CPU model this model is an alias for. The target -# of the CPU model alias may change depending on the machine type. -# Management software is supposed to translate CPU model aliases -# in the VM configuration, because aliases may stop being -# migration-safe in the future (since 4.1) -# -# @deprecated: If true, this CPU model is deprecated and may be -# removed in some future version of QEMU according to the QEMU -# deprecation policy. (since 5.2) -# -# @unavailable-features is a list of QOM property names that represent -# CPU model attributes that prevent the CPU from running. If the QOM -# property is read-only, that means there's no known way to make the -# CPU model run in the current host. Implementations that choose not -# to provide specific information return the property name "type". If -# the property is read-write, it means that it MAY be possible to run -# the CPU model in the current host if that property is changed. -# Management software can use it as hints to suggest or choose an -# alternative for the user, or just to generate meaningful error -# messages explaining why the CPU model can't be used. If -# @unavailable-features is an empty list, the CPU model is runnable -# using the current host and machine-type. If @unavailable-features -# is not present, runnability information for the CPU is not -# available. -# -# Since: 1.2 -## -{ 'struct': 'CpuDefinitionInfo', - 'data': { 'name': 'str', - '*migration-safe': 'bool', - 'static': 'bool', - '*unavailable-features': [ 'str' ], - 'typename': 'str', - '*alias-of' : 'str', - 'deprecated' : 'bool' }, - 'if': { 'any': [ 'TARGET_PPC', - 'TARGET_ARM', - 'TARGET_I386', - 'TARGET_S390X', - 'TARGET_MIPS', - 'TARGET_LOONGARCH64', - 'TARGET_RISCV' ] } } - -## -# @query-cpu-definitions: -# -# Return a list of supported virtual CPU definitions -# -# Returns: a list of CpuDefinitionInfo -# -# Since: 1.2 -## -{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'], - 'if': { 'any': [ 'TARGET_PPC', - 'TARGET_ARM', - 'TARGET_I386', - 'TARGET_S390X', - 'TARGET_MIPS', - 'TARGET_LOONGARCH64', - 'TARGET_RISCV' ] } } - ## # @S390CpuPolarization: # diff --git a/qapi/machine.json b/qapi/machine.json index c8feb9fe17..e6b4b2dfef 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1916,3 +1916,366 @@ ## { 'command': 'dump-skeys', 'data': { 'filename': 'str' } } + +## +# @CpuModelInfo: +# +# Virtual CPU model. +# +# A CPU model consists of the name of a CPU definition, to which delta +# changes are applied (e.g. features added/removed). Most magic +# values that an architecture might require should be hidden behind +# the name. However, if required, architectures can expose relevant +# properties. +# +# @name: the name of the CPU definition the model is based on +# +# @props: a dictionary of QOM properties to be applied +# +# Since: 2.8 +## +{ 'struct': 'CpuModelInfo', + 'data': { 'name': 'str', + '*props': 'any' } } + +## +# @CpuModelExpansionType: +# +# An enumeration of CPU model expansion types. +# +# @static: Expand to a static CPU model, a combination of a static +# base model name and property delta changes. As the static base +# model will never change, the expanded CPU model will be the +# same, independent of QEMU version, machine type, machine +# options, and accelerator options. Therefore, the resulting +# model can be used by tooling without having to specify a +# compatibility machine - e.g. when displaying the "host" model. +# The @static CPU models are migration-safe. +# +# @full: Expand all properties. The produced model is not guaranteed +# to be migration-safe, but allows tooling to get an insight and +# work with model details. +# +# .. note:: When a non-migration-safe CPU model is expanded in static +# mode, some features enabled by the CPU model may be omitted, +# because they can't be implemented by a static CPU model +# definition (e.g. cache info passthrough and PMU passthrough in +# x86). If you need an accurate representation of the features +# enabled by a non-migration-safe CPU model, use @full. If you +# need a static representation that will keep ABI compatibility +# even when changing QEMU version or machine-type, use @static (but +# keep in mind that some features may be omitted). +# +# Since: 2.8 +## +{ 'enum': 'CpuModelExpansionType', + 'data': [ 'static', 'full' ] } + +## +# @CpuModelCompareResult: +# +# An enumeration of CPU model comparison results. The result is +# usually calculated using e.g. CPU features or CPU generations. +# +# @incompatible: If model A is incompatible to model B, model A is not +# guaranteed to run where model B runs and the other way around. +# +# @identical: If model A is identical to model B, model A is +# guaranteed to run where model B runs and the other way around. +# +# @superset: If model A is a superset of model B, model B is +# guaranteed to run where model A runs. There are no guarantees +# about the other way. +# +# @subset: If model A is a subset of model B, model A is guaranteed to +# run where model B runs. There are no guarantees about the other +# way. +# +# Since: 2.8 +## +{ 'enum': 'CpuModelCompareResult', + 'data': [ 'incompatible', 'identical', 'superset', 'subset' ] } + +## +# @CpuModelBaselineInfo: +# +# The result of a CPU model baseline. +# +# @model: the baselined CpuModelInfo. +# +# Since: 2.8 +## +{ 'struct': 'CpuModelBaselineInfo', + 'data': { 'model': 'CpuModelInfo' } } + +## +# @CpuModelCompareInfo: +# +# The result of a CPU model comparison. +# +# @result: The result of the compare operation. +# +# @responsible-properties: List of properties that led to the +# comparison result not being identical. +# +# @responsible-properties is a list of QOM property names that led to +# both CPUs not being detected as identical. For identical models, +# this list is empty. If a QOM property is read-only, that means +# there's no known way to make the CPU models identical. If the +# special property name "type" is included, the models are by +# definition not identical and cannot be made identical. +# +# Since: 2.8 +## +{ 'struct': 'CpuModelCompareInfo', + 'data': { 'result': 'CpuModelCompareResult', + 'responsible-properties': ['str'] } } + +## +# @query-cpu-model-comparison: +# +# Compares two CPU models, @modela and @modelb, returning how they +# compare in a specific configuration. The results indicates how +# both models compare regarding runnability. This result can be +# used by tooling to make decisions if a certain CPU model will +# run in a certain configuration or if a compatible CPU model has +# to be created by baselining. +# +# Usually, a CPU model is compared against the maximum possible CPU +# model of a certain configuration (e.g. the "host" model for KVM). +# If that CPU model is identical or a subset, it will run in that +# configuration. +# +# The result returned by this command may be affected by: +# +# * QEMU version: CPU models may look different depending on the QEMU +# version. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * machine-type: CPU model may look different depending on the +# machine-type. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * machine options (including accelerator): in some architectures, +# CPU models may look different depending on machine and accelerator +# options. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * "-cpu" arguments and global properties: arguments to the -cpu +# option and global properties may affect expansion of CPU models. +# Using query-cpu-model-expansion while using these is not advised. +# +# Some architectures may not support comparing CPU models. s390x +# supports comparing CPU models. +# +# @modela: description of the first CPU model to compare, referred to +# as "model A" in CpuModelCompareResult +# +# @modelb: description of the second CPU model to compare, referred to +# as "model B" in CpuModelCompareResult +# +# Returns: a CpuModelCompareInfo describing how both CPU models +# compare +# +# Errors: +# - if comparing CPU models is not supported by the target +# - if a model cannot be used +# - if a model contains an unknown cpu definition name, unknown +# properties or properties with wrong types. +# +# Since: 2.8 +## +{ 'command': 'query-cpu-model-comparison', + 'data': { 'modela': 'CpuModelInfo', 'modelb': 'CpuModelInfo' }, + 'returns': 'CpuModelCompareInfo' } + +## +# @query-cpu-model-baseline: +# +# Baseline two CPU models, @modela and @modelb, creating a compatible +# third model. The created model will always be a static, +# migration-safe CPU model (see "static" CPU model expansion for +# details). +# +# This interface can be used by tooling to create a compatible CPU +# model out two CPU models. The created CPU model will be identical +# to or a subset of both CPU models when comparing them. Therefore, +# the created CPU model is guaranteed to run where the given CPU +# models run. +# +# The result returned by this command may be affected by: +# +# * QEMU version: CPU models may look different depending on the QEMU +# version. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * machine-type: CPU model may look different depending on the +# machine-type. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * machine options (including accelerator): in some architectures, +# CPU models may look different depending on machine and accelerator +# options. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * "-cpu" arguments and global properties: arguments to the -cpu +# option and global properties may affect expansion of CPU models. +# Using query-cpu-model-expansion while using these is not advised. +# +# Some architectures may not support baselining CPU models. s390x +# supports baselining CPU models. +# +# @modela: description of the first CPU model to baseline +# +# @modelb: description of the second CPU model to baseline +# +# Returns: a CpuModelBaselineInfo describing the baselined CPU model +# +# Errors: +# - if baselining CPU models is not supported by the target +# - if a model cannot be used +# - if a model contains an unknown cpu definition name, unknown +# properties or properties with wrong types. +# +# Since: 2.8 +## +{ 'command': 'query-cpu-model-baseline', + 'data': { 'modela': 'CpuModelInfo', + 'modelb': 'CpuModelInfo' }, + 'returns': 'CpuModelBaselineInfo' } + +## +# @CpuModelExpansionInfo: +# +# The result of a cpu model expansion. +# +# @model: the expanded CpuModelInfo. +# +# @deprecated-props: an optional list of properties that are flagged as +# deprecated by the CPU vendor. The list depends on the +# CpuModelExpansionType: "static" properties are a subset of the +# enabled-properties for the expanded model; "full" properties are +# a set of properties that are deprecated across all models for +# the architecture. (since: 10.1 -- since 9.1 on s390x --). +# +# Since: 2.8 +## +{ 'struct': 'CpuModelExpansionInfo', + 'data': { 'model': 'CpuModelInfo', + '*deprecated-props' : ['str'] } } + +## +# @query-cpu-model-expansion: +# +# Expands a given CPU model, @model, (or a combination of CPU model + +# additional options) to different granularities, specified by @type, +# allowing tooling to get an understanding what a specific CPU model +# looks like in QEMU under a certain configuration. +# +# This interface can be used to query the "host" CPU model. +# +# The data returned by this command may be affected by: +# +# * QEMU version: CPU models may look different depending on the QEMU +# version. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * machine-type: CPU model may look different depending on the +# machine-type. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * machine options (including accelerator): in some architectures, +# CPU models may look different depending on machine and accelerator +# options. (Except for CPU models reported as "static" in +# query-cpu-definitions.) +# * "-cpu" arguments and global properties: arguments to the -cpu +# option and global properties may affect expansion of CPU models. +# Using query-cpu-model-expansion while using these is not advised. +# +# Some architectures may not support all expansion types. s390x +# supports "full" and "static". Arm only supports "full". +# +# @model: description of the CPU model to expand +# +# @type: expansion type, specifying how to expand the CPU model +# +# Returns: a CpuModelExpansionInfo describing the expanded CPU model +# +# Errors: +# - if expanding CPU models is not supported +# - if the model cannot be expanded +# - if the model contains an unknown CPU definition name, unknown +# properties or properties with a wrong type +# - if an expansion type is not supported +# +# Since: 2.8 +## +{ 'command': 'query-cpu-model-expansion', + 'data': { 'type': 'CpuModelExpansionType', + 'model': 'CpuModelInfo' }, + 'returns': 'CpuModelExpansionInfo' } + +## +# @CpuDefinitionInfo: +# +# Virtual CPU definition. +# +# @name: the name of the CPU definition +# +# @migration-safe: whether a CPU definition can be safely used for +# migration in combination with a QEMU compatibility machine when +# migrating between different QEMU versions and between hosts with +# different sets of (hardware or software) capabilities. If not +# provided, information is not available and callers should not +# assume the CPU definition to be migration-safe. (since 2.8) +# +# @static: whether a CPU definition is static and will not change +# depending on QEMU version, machine type, machine options and +# accelerator options. A static model is always migration-safe. +# (since 2.8) +# +# @unavailable-features: List of properties that prevent the CPU model +# from running in the current host. (since 2.8) +# +# @typename: Type name that can be used as argument to +# @device-list-properties, to introspect properties configurable +# using -cpu or -global. (since 2.9) +# +# @alias-of: Name of CPU model this model is an alias for. The target +# of the CPU model alias may change depending on the machine type. +# Management software is supposed to translate CPU model aliases +# in the VM configuration, because aliases may stop being +# migration-safe in the future (since 4.1) +# +# @deprecated: If true, this CPU model is deprecated and may be +# removed in some future version of QEMU according to the QEMU +# deprecation policy. (since 5.2) +# +# @unavailable-features is a list of QOM property names that represent +# CPU model attributes that prevent the CPU from running. If the QOM +# property is read-only, that means there's no known way to make the +# CPU model run in the current host. Implementations that choose not +# to provide specific information return the property name "type". If +# the property is read-write, it means that it MAY be possible to run +# the CPU model in the current host if that property is changed. +# Management software can use it as hints to suggest or choose an +# alternative for the user, or just to generate meaningful error +# messages explaining why the CPU model can't be used. If +# @unavailable-features is an empty list, the CPU model is runnable +# using the current host and machine-type. If @unavailable-features +# is not present, runnability information for the CPU is not +# available. +# +# Since: 1.2 +## +{ 'struct': 'CpuDefinitionInfo', + 'data': { 'name': 'str', + '*migration-safe': 'bool', + 'static': 'bool', + '*unavailable-features': [ 'str' ], + 'typename': 'str', + '*alias-of' : 'str', + 'deprecated' : 'bool' } } + +## +# @query-cpu-definitions: +# +# Return a list of supported virtual CPU definitions +# +# Returns: a list of CpuDefinitionInfo +# +# Since: 1.2 +## +{ 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'] } diff --git a/stubs/meson.build b/stubs/meson.build index 0ef11976a2..3b2fad0824 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -82,6 +82,8 @@ if have_system stub_ss.add(files('monitor-i386-sev.c')) stub_ss.add(files('monitor-i386-sgx.c')) stub_ss.add(files('monitor-i386-xen.c')) + stub_ss.add(files('monitor-cpu.c')) + stub_ss.add(files('monitor-cpu-s390x.c')) endif if have_system or have_user diff --git a/stubs/monitor-cpu-s390x.c b/stubs/monitor-cpu-s390x.c new file mode 100644 index 0000000000..71e794482b --- /dev/null +++ b/stubs/monitor-cpu-s390x.c @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" + +CpuModelCompareInfo * +qmp_query_cpu_model_comparison(CpuModelInfo *infoa, + CpuModelInfo *infob, + Error **errp) +{ + error_setg(errp, "CPU model comparison is not supported on this target"); + return NULL; +} + +CpuModelBaselineInfo * +qmp_query_cpu_model_baseline(CpuModelInfo *infoa, + CpuModelInfo *infob, + Error **errp) +{ + error_setg(errp, "CPU model baseline is not supported on this target"); + return NULL; +} diff --git a/stubs/monitor-cpu.c b/stubs/monitor-cpu.c new file mode 100644 index 0000000000..a8c7ee89b9 --- /dev/null +++ b/stubs/monitor-cpu.c @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" + +CpuModelExpansionInfo * +qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + error_setg(errp, "CPU model expansion is not supported on this target"); + return NULL; +} + +CpuDefinitionInfoList * +qmp_query_cpu_definitions(Error **errp) +{ + error_setg(errp, "CPU model definitions are not supported on this target"); + return NULL; +} diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c index ef18c867ca..cca6b9722b 100644 --- a/target/arm/arm-qmp-cmds.c +++ b/target/arm/arm-qmp-cmds.c @@ -26,7 +26,7 @@ #include "qapi/error.h" #include "qapi/visitor.h" #include "qapi/qobject-input-visitor.h" -#include "qapi/qapi-commands-machine-target.h" +#include "qapi/qapi-commands-machine.h" #include "qapi/qapi-commands-misc-arm.h" #include "qobject/qdict.h" #include "qom/qom-qobject.h" diff --git a/target/i386/cpu-system.c b/target/i386/cpu-system.c index 55f192e819..b1494aa674 100644 --- a/target/i386/cpu-system.c +++ b/target/i386/cpu-system.c @@ -24,7 +24,7 @@ #include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" #include "qom/qom-qobject.h" -#include "qapi/qapi-commands-machine-target.h" +#include "qapi/qapi-commands-machine.h" #include "cpu-internal.h" diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 9689f6374e..33afc3ec60 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -38,7 +38,7 @@ #include "exec/watchpoint.h" #ifndef CONFIG_USER_ONLY #include "system/reset.h" -#include "qapi/qapi-commands-machine-target.h" +#include "qapi/qapi-commands-machine.h" #include "system/address-spaces.h" #include "hw/boards.h" #include "hw/i386/sgx-epc.h" diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c index 6f732d80f3..f5f1cd0009 100644 --- a/target/loongarch/loongarch-qmp-cmds.c +++ b/target/loongarch/loongarch-qmp-cmds.c @@ -8,7 +8,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qapi-commands-machine-target.h" +#include "qapi/qapi-commands-machine.h" #include "cpu.h" #include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" diff --git a/target/mips/system/mips-qmp-cmds.c b/target/mips/system/mips-qmp-cmds.c index 7340ac70ba..d98d6623f2 100644 --- a/target/mips/system/mips-qmp-cmds.c +++ b/target/mips/system/mips-qmp-cmds.c @@ -7,9 +7,19 @@ */ #include "qemu/osdep.h" -#include "qapi/qapi-commands-machine-target.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" #include "cpu.h" +CpuModelExpansionInfo * +qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + error_setg(errp, "CPU model expansion is not supported on this target"); + return NULL; +} + static void mips_cpu_add_definition(gpointer data, gpointer user_data) { ObjectClass *oc = data; diff --git a/target/ppc/ppc-qmp-cmds.c b/target/ppc/ppc-qmp-cmds.c index a25d86a8d1..7022564604 100644 --- a/target/ppc/ppc-qmp-cmds.c +++ b/target/ppc/ppc-qmp-cmds.c @@ -28,7 +28,8 @@ #include "qemu/ctype.h" #include "monitor/hmp-target.h" #include "monitor/hmp.h" -#include "qapi/qapi-commands-machine-target.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine.h" #include "cpu-models.h" #include "cpu-qom.h" @@ -175,6 +176,15 @@ int target_get_monitor_def(CPUState *cs, const char *name, uint64_t *pval) return -EINVAL; } +CpuModelExpansionInfo * +qmp_query_cpu_model_expansion(CpuModelExpansionType type, + CpuModelInfo *model, + Error **errp) +{ + error_setg(errp, "CPU model expansion is not supported on this target"); + return NULL; +} + static void ppc_cpu_defs_entry(gpointer data, gpointer user_data) { ObjectClass *oc = data; diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c index d0a324364d..8ba8aa0d5f 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -25,7 +25,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" -#include "qapi/qapi-commands-machine-target.h" +#include "qapi/qapi-commands-machine.h" #include "qobject/qbool.h" #include "qobject/qdict.h" #include "qapi/qobject-input-visitor.h" diff --git a/target/s390x/cpu_models_system.c b/target/s390x/cpu_models_system.c index 4351182f72..9d84faa3c9 100644 --- a/target/s390x/cpu_models_system.c +++ b/target/s390x/cpu_models_system.c @@ -19,7 +19,7 @@ #include "qapi/visitor.h" #include "qapi/qobject-input-visitor.h" #include "qobject/qdict.h" -#include "qapi/qapi-commands-machine-target.h" +#include "qapi/qapi-commands-machine.h" static void list_add_feat(const char *name, void *opaque); From f8d41d0511114edcde8c589ffc76887b3a2dc39b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Thu, 22 May 2025 12:05:37 -0700 Subject: [PATCH 1214/2760] qapi: make s390x specific CPU commands unconditionally available MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This removes the TARGET_S390X and CONFIG_KVM conditions from the CPU commands that are conceptually specific to s390x. Top level stubs are provided to cope with non-s390x targets, or builds without KVM. The removal of CONFIG_KVM is justified by the fact there is no conceptual difference between running 'qemu-system-s390x -accel tcg' on a build with and without KVM built-in, so apps only using TCG can't rely on the CONFIG_KVM in the schema. Reviewed-by: Richard Henderson Signed-off-by: Daniel P. Berrangé Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-11-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster --- hw/s390x/cpu-topology.c | 4 ++-- include/hw/s390x/cpu-topology.h | 2 +- ...machine-target.json => machine-s390x.json} | 16 +++++--------- qapi/meson.build | 2 +- qapi/qapi-schema.json | 2 +- stubs/meson.build | 1 + stubs/monitor-cpu-s390x-kvm.c | 22 +++++++++++++++++++ tests/qtest/qmp-cmd-test.c | 1 + 8 files changed, 35 insertions(+), 15 deletions(-) rename qapi/{machine-target.json => machine-s390x.json} (85%) create mode 100644 stubs/monitor-cpu-s390x-kvm.c diff --git a/hw/s390x/cpu-topology.c b/hw/s390x/cpu-topology.c index 7d4e1f5472..b513f8936e 100644 --- a/hw/s390x/cpu-topology.c +++ b/hw/s390x/cpu-topology.c @@ -23,8 +23,8 @@ #include "target/s390x/cpu.h" #include "hw/s390x/s390-virtio-ccw.h" #include "hw/s390x/cpu-topology.h" -#include "qapi/qapi-commands-machine-target.h" -#include "qapi/qapi-events-machine-target.h" +#include "qapi/qapi-commands-machine-s390x.h" +#include "qapi/qapi-events-machine-s390x.h" /* * s390_topology is used to keep the topology information. diff --git a/include/hw/s390x/cpu-topology.h b/include/hw/s390x/cpu-topology.h index 9283c948e3..d5e9aa43f8 100644 --- a/include/hw/s390x/cpu-topology.h +++ b/include/hw/s390x/cpu-topology.h @@ -13,7 +13,7 @@ #include "qemu/queue.h" #include "hw/boards.h" -#include "qapi/qapi-types-machine-target.h" +#include "qapi/qapi-types-machine-s390x.h" #define S390_TOPOLOGY_CPU_IFL 0x03 diff --git a/qapi/machine-target.json b/qapi/machine-s390x.json similarity index 85% rename from qapi/machine-target.json rename to qapi/machine-s390x.json index f19e34adaf..966dbd61d2 100644 --- a/qapi/machine-target.json +++ b/qapi/machine-s390x.json @@ -1,6 +1,7 @@ # -*- Mode: Python -*- # vim: filetype=python # +# SPDX-License-Identifier: GPL-2.0-or-later # This work is licensed under the terms of the GNU GPL, version 2 or later. # See the COPYING file in the top-level directory. @@ -15,8 +16,7 @@ # Since: 8.2 ## { 'enum': 'S390CpuPolarization', - 'data': [ 'horizontal', 'vertical' ], - 'if': 'TARGET_S390X' + 'data': [ 'horizontal', 'vertical' ] } ## @@ -54,8 +54,7 @@ '*entitlement': 'S390CpuEntitlement', '*dedicated': 'bool' }, - 'features': [ 'unstable' ], - 'if': { 'all': [ 'TARGET_S390X' , 'CONFIG_KVM' ] } + 'features': [ 'unstable' ] } ## @@ -90,8 +89,7 @@ ## { 'event': 'CPU_POLARIZATION_CHANGE', 'data': { 'polarization': 'S390CpuPolarization' }, - 'features': [ 'unstable' ], - 'if': { 'all': [ 'TARGET_S390X', 'CONFIG_KVM' ] } + 'features': [ 'unstable' ] } ## @@ -104,8 +102,7 @@ # Since: 8.2 ## { 'struct': 'CpuPolarizationInfo', - 'data': { 'polarization': 'S390CpuPolarization' }, - 'if': { 'all': [ 'TARGET_S390X', 'CONFIG_KVM' ] } + 'data': { 'polarization': 'S390CpuPolarization' } } ## @@ -120,6 +117,5 @@ # Since: 8.2 ## { 'command': 'query-s390x-cpu-polarization', 'returns': 'CpuPolarizationInfo', - 'features': [ 'unstable' ], - 'if': { 'all': [ 'TARGET_S390X', 'CONFIG_KVM' ] } + 'features': [ 'unstable' ] } diff --git a/qapi/meson.build b/qapi/meson.build index ffe44f9e0b..e038b636c9 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -39,7 +39,7 @@ qapi_all_modules = [ 'job', 'machine-common', 'machine', - 'machine-target', + 'machine-s390x', 'migration', 'misc', 'net', diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index d8eb79cfda..a8f66163cb 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -57,7 +57,7 @@ { 'include': 'qdev.json' } { 'include': 'machine-common.json' } { 'include': 'machine.json' } -{ 'include': 'machine-target.json' } +{ 'include': 'machine-s390x.json' } { 'include': 'replay.json' } { 'include': 'yank.json' } { 'include': 'misc.json' } diff --git a/stubs/meson.build b/stubs/meson.build index 3b2fad0824..cef046e685 100644 --- a/stubs/meson.build +++ b/stubs/meson.build @@ -84,6 +84,7 @@ if have_system stub_ss.add(files('monitor-i386-xen.c')) stub_ss.add(files('monitor-cpu.c')) stub_ss.add(files('monitor-cpu-s390x.c')) + stub_ss.add(files('monitor-cpu-s390x-kvm.c')) endif if have_system or have_user diff --git a/stubs/monitor-cpu-s390x-kvm.c b/stubs/monitor-cpu-s390x-kvm.c new file mode 100644 index 0000000000..8683dd2d4c --- /dev/null +++ b/stubs/monitor-cpu-s390x-kvm.c @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qapi/qapi-commands-machine-s390x.h" + +void qmp_set_cpu_topology(uint16_t core, + bool has_socket, uint16_t socket, + bool has_book, uint16_t book, + bool has_drawer, uint16_t drawer, + bool has_entitlement, S390CpuEntitlement entitlement, + bool has_dedicated, bool dedicated, + Error **errp) +{ + error_setg(errp, "CPU topology change is not supported on this target"); +} + +CpuPolarizationInfo *qmp_query_s390x_cpu_polarization(Error **errp) +{ + error_setg(errp, "CPU polarization is not supported on this target"); + return NULL; +} diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 15c88248b7..040d042810 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -100,6 +100,7 @@ static bool query_is_ignored(const char *cmd) /* Success depends on target arch: */ "query-cpu-definitions", /* arm, i386, ppc, s390x */ "query-gic-capabilities", /* arm */ + "query-s390x-cpu-polarization", /* s390x */ /* Success depends on target-specific build configuration: */ "query-pci", /* CONFIG_PCI */ "x-query-virtio", /* CONFIG_VIRTIO */ From d9cbcbff811bafc0e0081a07ce47c9e8f3051e0c Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 22 May 2025 12:05:38 -0700 Subject: [PATCH 1215/2760] qapi: remove qapi_specific_outputs from meson.build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no more QAPI files that need to be compiled per target, so we can remove this. qapi_specific_outputs is now empty, so we can remove the associated logic in meson. Reviewed-by: Daniel P. Berrangé Reviewed-by: Richard Henderson Reviewed-by: Markus Armbruster Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-12-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster --- qapi/meson.build | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/qapi/meson.build b/qapi/meson.build index e038b636c9..7582c2b5bc 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -92,7 +92,6 @@ qapi_util_outputs = [ ] qapi_inputs = [] -qapi_specific_outputs = [] foreach module : qapi_all_modules qapi_inputs += [ files(module + '.json') ] qapi_module_outputs = [ @@ -110,15 +109,11 @@ foreach module : qapi_all_modules 'qapi-commands-@0@.trace-events'.format(module), ] endif - if module.endswith('-target') - qapi_specific_outputs += qapi_module_outputs - else - qapi_util_outputs += qapi_module_outputs - endif + qapi_util_outputs += qapi_module_outputs endforeach qapi_files = custom_target('shared QAPI source files', - output: qapi_util_outputs + qapi_specific_outputs + qapi_nonmodule_outputs, + output: qapi_util_outputs + qapi_nonmodule_outputs, input: [ files('qapi-schema.json') ], command: [ qapi_gen, '-o', 'qapi', '-b', '@INPUT0@' ], depend_files: [ qapi_inputs, qapi_gen_depends ]) @@ -138,7 +133,7 @@ foreach output : qapi_util_outputs i = i + 1 endforeach -foreach output : qapi_specific_outputs + qapi_nonmodule_outputs +foreach output : qapi_nonmodule_outputs if output.endswith('.h') genh += qapi_files[i] endif From fdbb616f4dd55b4f3efd6cb56bf675046fb6e4f3 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 22 May 2025 12:05:39 -0700 Subject: [PATCH 1216/2760] qapi: make all generated files common Monolithic files (qapi_nonmodule_outputs) can now be compiled just once, so we can remove qapi_util_outputs logic. This removes the need for any specific_ss file. Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-13-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster --- qapi/meson.build | 23 ++++------------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/qapi/meson.build b/qapi/meson.build index 7582c2b5bc..3b035aea33 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -85,8 +85,7 @@ qapi_nonmodule_outputs = [ 'qapi-emit-events.c', 'qapi-emit-events.h', ] -# First build all sources -qapi_util_outputs = [ +qapi_outputs = qapi_nonmodule_outputs + [ 'qapi-builtin-types.c', 'qapi-builtin-visit.c', 'qapi-builtin-types.h', 'qapi-builtin-visit.h', ] @@ -109,20 +108,17 @@ foreach module : qapi_all_modules 'qapi-commands-@0@.trace-events'.format(module), ] endif - qapi_util_outputs += qapi_module_outputs + qapi_outputs += qapi_module_outputs endforeach qapi_files = custom_target('shared QAPI source files', - output: qapi_util_outputs + qapi_nonmodule_outputs, + output: qapi_outputs, input: [ files('qapi-schema.json') ], command: [ qapi_gen, '-o', 'qapi', '-b', '@INPUT0@' ], depend_files: [ qapi_inputs, qapi_gen_depends ]) -# Now go through all the outputs and add them to the right sourceset. -# These loops must be synchronized with the output of the above custom target. - i = 0 -foreach output : qapi_util_outputs +foreach output : qapi_outputs if output.endswith('.h') genh += qapi_files[i] endif @@ -132,14 +128,3 @@ foreach output : qapi_util_outputs util_ss.add(qapi_files[i]) i = i + 1 endforeach - -foreach output : qapi_nonmodule_outputs - if output.endswith('.h') - genh += qapi_files[i] - endif - if output.endswith('.trace-events') - qapi_trace_events += qapi_files[i] - endif - specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: qapi_files[i]) - i = i + 1 -endforeach From e1a80c3241631ee0e7a687a54f71407b6d3828b7 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 22 May 2025 12:05:40 -0700 Subject: [PATCH 1217/2760] qapi: use imperative style in documentation As requested by Markus: > We prefer imperative mood "Return" over "Returns". Signed-off-by: Pierrick Bouvier Message-ID: <20250522190542.588267-14-pierrick.bouvier@linaro.org> Reviewed-by: Markus Armbruster [Change several more] --- qapi/audio.json | 2 +- qapi/block.json | 2 +- qapi/char.json | 4 ++-- qapi/control.json | 2 +- qapi/cryptodev.json | 2 +- qapi/dump.json | 2 +- qapi/machine.json | 8 ++++---- qapi/migration.json | 10 +++++----- qapi/misc-i386.json | 6 +++--- qapi/misc.json | 2 +- qapi/ui.json | 10 +++++----- qapi/virtio.json | 2 +- 12 files changed, 26 insertions(+), 26 deletions(-) diff --git a/qapi/audio.json b/qapi/audio.json index 49633cf317..8de4430578 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -533,7 +533,7 @@ ## # @query-audiodevs: # -# Returns information about audiodev configuration +# Return information about audiodev configuration # # Returns: array of @Audiodev # diff --git a/qapi/block.json b/qapi/block.json index e66666f5c6..f5374bd86c 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -83,7 +83,7 @@ ## # @query-pr-managers: # -# Returns a list of information about each persistent reservation +# Return a list of information about each persistent reservation # manager. # # Returns: a list of @PRManagerInfo for each persistent reservation diff --git a/qapi/char.json b/qapi/char.json index dde2f9538f..447c10b91a 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -34,7 +34,7 @@ ## # @query-chardev: # -# Returns information about current character devices. +# Return information about current character devices. # # Returns: a list of @ChardevInfo # @@ -80,7 +80,7 @@ ## # @query-chardev-backends: # -# Returns information about character device backends. +# Return information about character device backends. # # Returns: a list of @ChardevBackendInfo # diff --git a/qapi/control.json b/qapi/control.json index 336386f79e..34b733f63b 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -91,7 +91,7 @@ ## # @query-version: # -# Returns the current version of QEMU. +# Return the current version of QEMU. # # Returns: A @VersionInfo object describing the current version of # QEMU. diff --git a/qapi/cryptodev.json b/qapi/cryptodev.json index 04d0e21d20..28b97eb3da 100644 --- a/qapi/cryptodev.json +++ b/qapi/cryptodev.json @@ -94,7 +94,7 @@ ## # @query-cryptodev: # -# Returns information about current crypto devices. +# Return information about current crypto devices. # # Returns: a list of @QCryptodevInfo # diff --git a/qapi/dump.json b/qapi/dump.json index d7826c0e32..f2835c0b47 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -195,7 +195,7 @@ ## # @query-dump-guest-memory-capability: # -# Returns the available formats for dump-guest-memory +# Return the available formats for dump-guest-memory # # Returns: A @DumpGuestMemoryCapability object listing available # formats for dump-guest-memory diff --git a/qapi/machine.json b/qapi/machine.json index e6b4b2dfef..5373e1368c 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -99,7 +99,7 @@ ## # @query-cpus-fast: # -# Returns information about all virtual CPUs. +# Return information about all virtual CPUs. # # Returns: list of @CpuInfoFast # @@ -467,7 +467,7 @@ ## # @query-kvm: # -# Returns information about KVM acceleration +# Return information about KVM acceleration # # Returns: @KvmInfo # @@ -930,7 +930,7 @@ ## # @query-memdev: # -# Returns information for all memory backends. +# Return information for all memory backends. # # Returns: a list of @Memdev. # @@ -1235,7 +1235,7 @@ ## # @query-hv-balloon-status-report: # -# Returns the hv-balloon driver data contained in the last received +# Return the hv-balloon driver data contained in the last received # "STATUS" message from the guest. # # Returns: diff --git a/qapi/migration.json b/qapi/migration.json index 8b9c53595c..ce8d1663d8 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -282,7 +282,7 @@ ## # @query-migrate: # -# Returns information about current migration process. If migration +# Return information about current migration process. If migration # is active there will be another json-object with RAM migration # status. # @@ -535,7 +535,7 @@ ## # @query-migrate-capabilities: # -# Returns information about the current migration capabilities status +# Return information about the current migration capabilities status # # Returns: @MigrationCapabilityStatus # @@ -1320,7 +1320,7 @@ ## # @query-migrate-parameters: # -# Returns information about the current migration parameters +# Return information about the current migration parameters # # Returns: @MigrationParameters # @@ -2294,7 +2294,7 @@ ## # @query-vcpu-dirty-limit: # -# Returns information about virtual CPU dirty page rate limits, if +# Return information about virtual CPU dirty page rate limits, if # any. # # Since: 7.1 @@ -2327,7 +2327,7 @@ ## # @query-migrationthreads: # -# Returns information of migration threads +# Return information of migration threads # # Features: # diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json index 3f88a5b28e..3b5346425a 100644 --- a/qapi/misc-i386.json +++ b/qapi/misc-i386.json @@ -126,7 +126,7 @@ ## # @query-sev: # -# Returns information about SEV/SEV-ES/SEV-SNP. +# Return information about SEV/SEV-ES/SEV-SNP. # # If unavailable due to an incompatible configuration the returned # @enabled field is set to 'false' and the state of all other fields @@ -343,7 +343,7 @@ ## # @query-sgx: # -# Returns information about configured SGX capabilities of guest +# Return information about configured SGX capabilities of guest # # Returns: @SgxInfo # @@ -362,7 +362,7 @@ ## # @query-sgx-capabilities: # -# Returns information about SGX capabilities of host +# Return information about SGX capabilities of host # # Returns: @SgxInfo # diff --git a/qapi/misc.json b/qapi/misc.json index 559b66f201..dcf9f7df5b 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -101,7 +101,7 @@ ## # @query-iothreads: # -# Returns a list of information about each iothread. +# Return a list of information about each iothread. # # .. note:: This list excludes the QEMU main loop thread, which is not # declared using the ``-object iothread`` command-line option. It diff --git a/qapi/ui.json b/qapi/ui.json index c536d4e524..3d0c853c9a 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -323,7 +323,7 @@ ## # @query-spice: # -# Returns information about the current SPICE server +# Return information about the current SPICE server # # Returns: @SpiceInfo # @@ -654,7 +654,7 @@ ## # @query-vnc: # -# Returns information about the current VNC server +# Return information about the current VNC server # # Returns: @VncInfo # @@ -685,7 +685,7 @@ ## # @query-vnc-servers: # -# Returns a list of vnc servers. The list can be empty. +# Return a list of vnc servers. The list can be empty. # # Returns: a list of @VncInfo2 # @@ -820,7 +820,7 @@ ## # @query-mice: # -# Returns information about each active mouse device +# Return information about each active mouse device # # Returns: a list of @MouseInfo for each device # @@ -1562,7 +1562,7 @@ ## # @query-display-options: # -# Returns information about display configuration +# Return information about display configuration # # Returns: @DisplayOptions # diff --git a/qapi/virtio.json b/qapi/virtio.json index d351d2166e..73df718a26 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -24,7 +24,7 @@ ## # @x-query-virtio: # -# Returns a list of all realized VirtIODevices +# Return a list of all realized VirtIODevices # # Features: # From 5150004ccf5fe72c35b3263fbed6f4d06ed3cc6a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 28 May 2025 11:20:13 +0200 Subject: [PATCH 1218/2760] rocker: do not pollute the namespace Do not leave the __le* macros defined, in fact do not use them at all. Fixes a build failure on Alpine with the TDX patches: In file included from ../hw/net/rocker/rocker_of_dpa.c:25: ../hw/net/rocker/rocker_hw.h:14:16: error: conflicting types for 'uint64_t'; have '__u64' {aka 'long long unsigned int'} 14 | #define __le64 uint64_t | ^~~~~~~~ In file included from /usr/include/stdint.h:20, from ../include/qemu/osdep.h:111, from ../hw/net/rocker/rocker_of_dpa.c:17: /usr/include/bits/alltypes.h:136:25: note: previous declaration of 'uint64_t' with type 'uint64_t' {aka 'long unsigned int'} 136 | typedef unsigned _Int64 uint64_t; | ^~~~~~~~ because the Linux headers include a typedef of __leNN. Signed-off-by: Paolo Bonzini --- hw/net/rocker/rocker.h | 14 +++--------- hw/net/rocker/rocker_hw.h | 20 +++++++----------- hw/net/rocker/rocker_of_dpa.c | 40 +++++++++++++++++------------------ 3 files changed, 31 insertions(+), 43 deletions(-) diff --git a/hw/net/rocker/rocker.h b/hw/net/rocker/rocker.h index 6e0962f47a..ae06c1c72a 100644 --- a/hw/net/rocker/rocker.h +++ b/hw/net/rocker/rocker.h @@ -36,15 +36,7 @@ static inline G_GNUC_PRINTF(1, 2) int DPRINTF(const char *fmt, ...) } #endif -#define __le16 uint16_t -#define __le32 uint32_t -#define __le64 uint64_t - -#define __be16 uint16_t -#define __be32 uint32_t -#define __be64 uint64_t - -static inline bool ipv4_addr_is_multicast(__be32 addr) +static inline bool ipv4_addr_is_multicast(uint32_t addr) { return (addr & htonl(0xf0000000)) == htonl(0xe0000000); } @@ -52,8 +44,8 @@ static inline bool ipv4_addr_is_multicast(__be32 addr) typedef struct ipv6_addr { union { uint8_t addr8[16]; - __be16 addr16[8]; - __be32 addr32[4]; + uint16_t addr16[8]; + uint32_t addr32[4]; }; } Ipv6Addr; diff --git a/hw/net/rocker/rocker_hw.h b/hw/net/rocker/rocker_hw.h index 1786323fa4..7ec6bfbcb9 100644 --- a/hw/net/rocker/rocker_hw.h +++ b/hw/net/rocker/rocker_hw.h @@ -9,10 +9,6 @@ #ifndef ROCKER_HW_H #define ROCKER_HW_H -#define __le16 uint16_t -#define __le32 uint32_t -#define __le64 uint64_t - /* * Return codes */ @@ -124,12 +120,12 @@ enum { */ typedef struct rocker_desc { - __le64 buf_addr; + uint64_t buf_addr; uint64_t cookie; - __le16 buf_size; - __le16 tlv_size; - __le16 rsvd[5]; /* pad to 32 bytes */ - __le16 comp_err; + uint16_t buf_size; + uint16_t tlv_size; + uint16_t rsvd[5]; /* pad to 32 bytes */ + uint16_t comp_err; } __attribute__((packed, aligned(8))) RockerDesc; /* @@ -137,9 +133,9 @@ typedef struct rocker_desc { */ typedef struct rocker_tlv { - __le32 type; - __le16 len; - __le16 rsvd; + uint32_t type; + uint16_t len; + uint16_t rsvd; } __attribute__((packed, aligned(8))) RockerTlv; /* cmd msg */ diff --git a/hw/net/rocker/rocker_of_dpa.c b/hw/net/rocker/rocker_of_dpa.c index 3378f63110..4aed178756 100644 --- a/hw/net/rocker/rocker_of_dpa.c +++ b/hw/net/rocker/rocker_of_dpa.c @@ -52,10 +52,10 @@ typedef struct of_dpa_flow_key { uint32_t tunnel_id; /* overlay tunnel id */ uint32_t tbl_id; /* table id */ struct { - __be16 vlan_id; /* 0 if no VLAN */ + uint16_t vlan_id; /* 0 if no VLAN */ MACAddr src; /* ethernet source address */ MACAddr dst; /* ethernet destination address */ - __be16 type; /* ethernet frame type */ + uint16_t type; /* ethernet frame type */ } eth; struct { uint8_t proto; /* IP protocol or ARP opcode */ @@ -66,14 +66,14 @@ typedef struct of_dpa_flow_key { union { struct { struct { - __be32 src; /* IP source address */ - __be32 dst; /* IP destination address */ + uint32_t src; /* IP source address */ + uint32_t dst; /* IP destination address */ } addr; union { struct { - __be16 src; /* TCP/UDP/SCTP source port */ - __be16 dst; /* TCP/UDP/SCTP destination port */ - __be16 flags; /* TCP flags */ + uint16_t src; /* TCP/UDP/SCTP source port */ + uint16_t dst; /* TCP/UDP/SCTP destination port */ + uint16_t flags; /* TCP flags */ } tp; struct { MACAddr sha; /* ARP source hardware address */ @@ -86,11 +86,11 @@ typedef struct of_dpa_flow_key { Ipv6Addr src; /* IPv6 source address */ Ipv6Addr dst; /* IPv6 destination address */ } addr; - __be32 label; /* IPv6 flow label */ + uint32_t label; /* IPv6 flow label */ struct { - __be16 src; /* TCP/UDP/SCTP source port */ - __be16 dst; /* TCP/UDP/SCTP destination port */ - __be16 flags; /* TCP flags */ + uint16_t src; /* TCP/UDP/SCTP source port */ + uint16_t dst; /* TCP/UDP/SCTP destination port */ + uint16_t flags; /* TCP flags */ } tp; struct { Ipv6Addr target; /* ND target address */ @@ -112,13 +112,13 @@ typedef struct of_dpa_flow_action { struct { uint32_t group_id; uint32_t tun_log_lport; - __be16 vlan_id; + uint16_t vlan_id; } write; struct { - __be16 new_vlan_id; + uint16_t new_vlan_id; uint32_t out_pport; uint8_t copy_to_cpu; - __be16 vlan_id; + uint16_t vlan_id; } apply; } OfDpaFlowAction; @@ -143,7 +143,7 @@ typedef struct of_dpa_flow { typedef struct of_dpa_flow_pkt_fields { uint32_t tunnel_id; struct eth_header *ethhdr; - __be16 *h_proto; + uint16_t *h_proto; struct vlan_header *vlanhdr; struct ip_header *ipv4hdr; struct ip6_header *ipv6hdr; @@ -180,7 +180,7 @@ typedef struct of_dpa_group { uint32_t group_id; MACAddr src_mac; MACAddr dst_mac; - __be16 vlan_id; + uint16_t vlan_id; } l2_rewrite; struct { uint16_t group_count; @@ -190,13 +190,13 @@ typedef struct of_dpa_group { uint32_t group_id; MACAddr src_mac; MACAddr dst_mac; - __be16 vlan_id; + uint16_t vlan_id; uint8_t ttl_check; } l3_unicast; }; } OfDpaGroup; -static int of_dpa_mask2prefix(__be32 mask) +static int of_dpa_mask2prefix(uint32_t mask) { int i; int count = 32; @@ -451,7 +451,7 @@ static void of_dpa_flow_pkt_parse(OfDpaFlowContext *fc, fc->iovcnt = iovcnt + 2; } -static void of_dpa_flow_pkt_insert_vlan(OfDpaFlowContext *fc, __be16 vlan_id) +static void of_dpa_flow_pkt_insert_vlan(OfDpaFlowContext *fc, uint16_t vlan_id) { OfDpaFlowPktFields *fields = &fc->fields; uint16_t h_proto = fields->ethhdr->h_proto; @@ -486,7 +486,7 @@ static void of_dpa_flow_pkt_strip_vlan(OfDpaFlowContext *fc) static void of_dpa_flow_pkt_hdr_rewrite(OfDpaFlowContext *fc, uint8_t *src_mac, uint8_t *dst_mac, - __be16 vlan_id) + uint16_t vlan_id) { OfDpaFlowPktFields *fields = &fc->fields; From 756e12e791771034ac105a5d2c9887bbbb6b7c73 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:08 -0400 Subject: [PATCH 1219/2760] i386: Introduce tdx-guest object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce tdx-guest object which inherits X86_CONFIDENTIAL_GUEST, and will be used to create TDX VMs (TDs) by qemu -machine ...,confidential-guest-support=tdx0 \ -object tdx-guest,id=tdx0 It has one QAPI member 'attributes' defined, which allows user to set TD's attributes directly. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Acked-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-3-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- configs/devices/i386-softmmu/default.mak | 1 + hw/i386/Kconfig | 5 +++ qapi/qom.json | 15 +++++++++ target/i386/kvm/meson.build | 2 ++ target/i386/kvm/tdx.c | 43 ++++++++++++++++++++++++ target/i386/kvm/tdx.h | 21 ++++++++++++ 6 files changed, 87 insertions(+) create mode 100644 target/i386/kvm/tdx.c create mode 100644 target/i386/kvm/tdx.h diff --git a/configs/devices/i386-softmmu/default.mak b/configs/devices/i386-softmmu/default.mak index 4faf2f0315..bc0479a7e0 100644 --- a/configs/devices/i386-softmmu/default.mak +++ b/configs/devices/i386-softmmu/default.mak @@ -18,6 +18,7 @@ #CONFIG_QXL=n #CONFIG_SEV=n #CONFIG_SGA=n +#CONFIG_TDX=n #CONFIG_TEST_DEVICES=n #CONFIG_TPM_CRB=n #CONFIG_TPM_TIS_ISA=n diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index d34ce07b21..cce9521ba9 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -10,6 +10,10 @@ config SGX bool depends on KVM +config TDX + bool + depends on KVM + config PC bool imply APPLESMC @@ -26,6 +30,7 @@ config PC imply QXL imply SEV imply SGX + imply TDX imply TEST_DEVICES imply TPM_CRB imply TPM_TIS_ISA diff --git a/qapi/qom.json b/qapi/qom.json index 04c118e4d6..3d7e11efc3 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -1047,6 +1047,19 @@ '*host-data': 'str', '*vcek-disabled': 'bool' } } +## +# @TdxGuestProperties: +# +# Properties for tdx-guest objects. +# +# @attributes: The 'attributes' of a TD guest that is passed to +# KVM_TDX_INIT_VM +# +# Since: 10.1 +## +{ 'struct': 'TdxGuestProperties', + 'data': { '*attributes': 'uint64' } } + ## # @ThreadContextProperties: # @@ -1132,6 +1145,7 @@ 'sev-snp-guest', 'thread-context', 's390-pv-guest', + 'tdx-guest', 'throttle-group', 'tls-creds-anon', 'tls-creds-psk', @@ -1204,6 +1218,7 @@ 'if': 'CONFIG_SECRET_KEYRING' }, 'sev-guest': 'SevGuestProperties', 'sev-snp-guest': 'SevSnpGuestProperties', + 'tdx-guest': 'TdxGuestProperties', 'thread-context': 'ThreadContextProperties', 'throttle-group': 'ThrottleGroupProperties', 'tls-creds-anon': 'TlsCredsAnonProperties', diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build index 3996cafaf2..466bccb9cb 100644 --- a/target/i386/kvm/meson.build +++ b/target/i386/kvm/meson.build @@ -8,6 +8,8 @@ i386_kvm_ss.add(files( i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c')) +i386_kvm_ss.add(when: 'CONFIG_TDX', if_true: files('tdx.c')) + i386_system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) i386_system_ss.add_all(when: 'CONFIG_KVM', if_true: i386_kvm_ss) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c new file mode 100644 index 0000000000..ab70566c7d --- /dev/null +++ b/target/i386/kvm/tdx.c @@ -0,0 +1,43 @@ +/* + * QEMU TDX support + * + * Copyright (c) 2025 Intel Corporation + * + * Author: + * Xiaoyao Li + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qom/object_interfaces.h" + +#include "tdx.h" + +/* tdx guest */ +OBJECT_DEFINE_TYPE_WITH_INTERFACES(TdxGuest, + tdx_guest, + TDX_GUEST, + X86_CONFIDENTIAL_GUEST, + { TYPE_USER_CREATABLE }, + { NULL }) + +static void tdx_guest_init(Object *obj) +{ + ConfidentialGuestSupport *cgs = CONFIDENTIAL_GUEST_SUPPORT(obj); + TdxGuest *tdx = TDX_GUEST(obj); + + cgs->require_guest_memfd = true; + tdx->attributes = 0; + + object_property_add_uint64_ptr(obj, "attributes", &tdx->attributes, + OBJ_PROP_FLAG_READWRITE); +} + +static void tdx_guest_finalize(Object *obj) +{ +} + +static void tdx_guest_class_init(ObjectClass *oc, const void *data) +{ +} diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h new file mode 100644 index 0000000000..f3b7253361 --- /dev/null +++ b/target/i386/kvm/tdx.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef QEMU_I386_TDX_H +#define QEMU_I386_TDX_H + +#include "confidential-guest.h" + +#define TYPE_TDX_GUEST "tdx-guest" +#define TDX_GUEST(obj) OBJECT_CHECK(TdxGuest, (obj), TYPE_TDX_GUEST) + +typedef struct TdxGuestClass { + X86ConfidentialGuestClass parent_class; +} TdxGuestClass; + +typedef struct TdxGuest { + X86ConfidentialGuest parent_obj; + + uint64_t attributes; /* TD attributes */ +} TdxGuest; + +#endif /* QEMU_I386_TDX_H */ From b455880e5515a9fc2b923bfc6c60bb54519b51d3 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:09 -0400 Subject: [PATCH 1220/2760] i386/tdx: Implement tdx_kvm_type() for TDX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TDX VM requires VM type to be KVM_X86_TDX_VM. Implement tdx_kvm_type() as X86ConfidentialGuestClass->kvm_type. Signed-off-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-4-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 1 + target/i386/kvm/tdx.c | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index c9a3c02e3e..653a8f46c2 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -192,6 +192,7 @@ static const char *vm_type_name[] = { [KVM_X86_SEV_VM] = "SEV", [KVM_X86_SEV_ES_VM] = "SEV-ES", [KVM_X86_SNP_VM] = "SEV-SNP", + [KVM_X86_TDX_VM] = "TDX", }; bool kvm_is_vm_type_supported(int type) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index ab70566c7d..21a4f87756 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -12,8 +12,17 @@ #include "qemu/osdep.h" #include "qom/object_interfaces.h" +#include "kvm_i386.h" #include "tdx.h" +static int tdx_kvm_type(X86ConfidentialGuest *cg) +{ + /* Do the object check */ + TDX_GUEST(cg); + + return KVM_X86_TDX_VM; +} + /* tdx guest */ OBJECT_DEFINE_TYPE_WITH_INTERFACES(TdxGuest, tdx_guest, @@ -40,4 +49,7 @@ static void tdx_guest_finalize(Object *obj) static void tdx_guest_class_init(ObjectClass *oc, const void *data) { + X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); + + x86_klass->kvm_type = tdx_kvm_type; } From 631a2ac5a4beab740b342367550562cd659b4c4a Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:10 -0400 Subject: [PATCH 1221/2760] i386/tdx: Implement tdx_kvm_init() to initialize TDX VM context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement TDX specific ConfidentialGuestSupportClass::kvm_init() callback, tdx_kvm_init(). Mark guest state is proctected for TDX VM. More TDX specific initialization will be added later. Signed-off-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-5-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 11 +---------- target/i386/kvm/tdx.c | 10 ++++++++++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 653a8f46c2..d29376c599 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -3207,16 +3207,7 @@ int kvm_arch_init(MachineState *ms, KVMState *s) Error *local_err = NULL; /* - * Initialize SEV context, if required - * - * If no memory encryption is requested (ms->cgs == NULL) this is - * a no-op. - * - * It's also a no-op if a non-SEV confidential guest support - * mechanism is selected. SEV is the only mechanism available to - * select on x86 at present, so this doesn't arise, but if new - * mechanisms are supported in future (e.g. TDX), they'll need - * their own initialization either here or elsewhere. + * Initialize confidential guest (SEV/TDX) context, if required */ if (ms->cgs) { ret = confidential_guest_kvm_init(ms->cgs, &local_err); diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 21a4f87756..2b060fb52b 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -12,9 +12,17 @@ #include "qemu/osdep.h" #include "qom/object_interfaces.h" +#include "hw/i386/x86.h" #include "kvm_i386.h" #include "tdx.h" +static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) +{ + kvm_mark_guest_state_protected(); + + return 0; +} + static int tdx_kvm_type(X86ConfidentialGuest *cg) { /* Do the object check */ @@ -49,7 +57,9 @@ static void tdx_guest_finalize(Object *obj) static void tdx_guest_class_init(ObjectClass *oc, const void *data) { + ConfidentialGuestSupportClass *klass = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); X86ConfidentialGuestClass *x86_klass = X86_CONFIDENTIAL_GUEST_CLASS(oc); + klass->kvm_init = tdx_kvm_init; x86_klass->kvm_type = tdx_kvm_type; } From 8eddedc3701d2190db976a05155a8263c8ec175b Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:11 -0400 Subject: [PATCH 1222/2760] i386/tdx: Get tdx_capabilities via KVM_TDX_CAPABILITIES KVM provides TDX capabilities via sub command KVM_TDX_CAPABILITIES of IOCTL(KVM_MEMORY_ENCRYPT_OP). Get the capabilities when initializing TDX context. It will be used to validate user's setting later. Since there is no interface reporting how many cpuid configs contains in KVM_TDX_CAPABILITIES, QEMU chooses to try starting with a known number and abort when it exceeds KVM_MAX_CPUID_ENTRIES. Besides, introduce the interfaces to invoke TDX "ioctls" at VCPU scope in preparation. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250508150002.689633-6-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 2 - target/i386/kvm/kvm_i386.h | 2 + target/i386/kvm/tdx.c | 107 ++++++++++++++++++++++++++++++++++++- 3 files changed, 108 insertions(+), 3 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index d29376c599..6d88495d47 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1780,8 +1780,6 @@ static int hyperv_init_vcpu(X86CPU *cpu) static Error *invtsc_mig_blocker; -#define KVM_MAX_CPUID_ENTRIES 100 - static void kvm_init_xsave(CPUX86State *env) { if (has_xsave2) { diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 88565e8dba..ed1e61fb8b 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -13,6 +13,8 @@ #include "system/kvm.h" +#define KVM_MAX_CPUID_ENTRIES 100 + /* always false if !CONFIG_KVM */ #define kvm_pit_in_kernel() \ (kvm_irqchip_in_kernel() && !kvm_irqchip_is_split()) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 2b060fb52b..f8ec4fa217 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -10,17 +10,122 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" #include "qom/object_interfaces.h" #include "hw/i386/x86.h" #include "kvm_i386.h" #include "tdx.h" +static struct kvm_tdx_capabilities *tdx_caps; + +enum tdx_ioctl_level { + TDX_VM_IOCTL, + TDX_VCPU_IOCTL, +}; + +static int tdx_ioctl_internal(enum tdx_ioctl_level level, void *state, + int cmd_id, __u32 flags, void *data, + Error **errp) +{ + struct kvm_tdx_cmd tdx_cmd = {}; + int r; + + const char *tdx_ioctl_name[] = { + [KVM_TDX_CAPABILITIES] = "KVM_TDX_CAPABILITIES", + [KVM_TDX_INIT_VM] = "KVM_TDX_INIT_VM", + [KVM_TDX_INIT_VCPU] = "KVM_TDX_INIT_VCPU", + [KVM_TDX_INIT_MEM_REGION] = "KVM_TDX_INIT_MEM_REGION", + [KVM_TDX_FINALIZE_VM] = "KVM_TDX_FINALIZE_VM", + [KVM_TDX_GET_CPUID] = "KVM_TDX_GET_CPUID", + }; + + tdx_cmd.id = cmd_id; + tdx_cmd.flags = flags; + tdx_cmd.data = (__u64)(unsigned long)data; + + switch (level) { + case TDX_VM_IOCTL: + r = kvm_vm_ioctl(kvm_state, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd); + break; + case TDX_VCPU_IOCTL: + r = kvm_vcpu_ioctl(state, KVM_MEMORY_ENCRYPT_OP, &tdx_cmd); + break; + default: + error_setg(errp, "Invalid tdx_ioctl_level %d", level); + return -EINVAL; + } + + if (r < 0) { + error_setg_errno(errp, -r, "TDX ioctl %s failed, hw_errors: 0x%llx", + tdx_ioctl_name[cmd_id], tdx_cmd.hw_error); + } + return r; +} + +static inline int tdx_vm_ioctl(int cmd_id, __u32 flags, void *data, + Error **errp) +{ + return tdx_ioctl_internal(TDX_VM_IOCTL, NULL, cmd_id, flags, data, errp); +} + +static inline int tdx_vcpu_ioctl(CPUState *cpu, int cmd_id, __u32 flags, + void *data, Error **errp) +{ + return tdx_ioctl_internal(TDX_VCPU_IOCTL, cpu, cmd_id, flags, data, errp); +} + +static int get_tdx_capabilities(Error **errp) +{ + struct kvm_tdx_capabilities *caps; + /* 1st generation of TDX reports 6 cpuid configs */ + int nr_cpuid_configs = 6; + size_t size; + int r; + + do { + Error *local_err = NULL; + size = sizeof(struct kvm_tdx_capabilities) + + nr_cpuid_configs * sizeof(struct kvm_cpuid_entry2); + caps = g_malloc0(size); + caps->cpuid.nent = nr_cpuid_configs; + + r = tdx_vm_ioctl(KVM_TDX_CAPABILITIES, 0, caps, &local_err); + if (r == -E2BIG) { + g_free(caps); + nr_cpuid_configs *= 2; + if (nr_cpuid_configs > KVM_MAX_CPUID_ENTRIES) { + error_report("KVM TDX seems broken that number of CPUID entries" + " in kvm_tdx_capabilities exceeds limit: %d", + KVM_MAX_CPUID_ENTRIES); + error_propagate(errp, local_err); + return r; + } + error_free(local_err); + } else if (r < 0) { + g_free(caps); + error_propagate(errp, local_err); + return r; + } + } while (r == -E2BIG); + + tdx_caps = caps; + + return 0; +} + static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { + int r = 0; + kvm_mark_guest_state_protected(); - return 0; + if (!tdx_caps) { + r = get_tdx_capabilities(errp); + } + + return r; } static int tdx_kvm_type(X86ConfidentialGuest *cg) From 1619d0e45be0d1e48a46d80963b4e77dc1b000a2 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:12 -0400 Subject: [PATCH 1223/2760] i386/tdx: Introduce is_tdx_vm() helper and cache tdx_guest object It will need special handling for TDX VMs all around the QEMU. Introduce is_tdx_vm() helper to query if it's a TDX VM. Cache tdx_guest object thus no need to cast from ms->cgs every time. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Isaku Yamahata Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-7-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 15 ++++++++++++++- target/i386/kvm/tdx.h | 10 ++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index f8ec4fa217..3750889453 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -18,8 +18,16 @@ #include "kvm_i386.h" #include "tdx.h" +static TdxGuest *tdx_guest; + static struct kvm_tdx_capabilities *tdx_caps; +/* Valid after kvm_arch_init()->confidential_guest_kvm_init()->tdx_kvm_init() */ +bool is_tdx_vm(void) +{ + return !!tdx_guest; +} + enum tdx_ioctl_level { TDX_VM_IOCTL, TDX_VCPU_IOCTL, @@ -117,15 +125,20 @@ static int get_tdx_capabilities(Error **errp) static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { + TdxGuest *tdx = TDX_GUEST(cgs); int r = 0; kvm_mark_guest_state_protected(); if (!tdx_caps) { r = get_tdx_capabilities(errp); + if (r) { + return r; + } } - return r; + tdx_guest = tdx; + return 0; } static int tdx_kvm_type(X86ConfidentialGuest *cg) diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index f3b7253361..de8ae91961 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -3,6 +3,10 @@ #ifndef QEMU_I386_TDX_H #define QEMU_I386_TDX_H +#ifndef CONFIG_USER_ONLY +#include CONFIG_DEVICES /* CONFIG_TDX */ +#endif + #include "confidential-guest.h" #define TYPE_TDX_GUEST "tdx-guest" @@ -18,4 +22,10 @@ typedef struct TdxGuest { uint64_t attributes; /* TD attributes */ } TdxGuest; +#ifdef CONFIG_TDX +bool is_tdx_vm(void); +#else +#define is_tdx_vm() 0 +#endif /* CONFIG_TDX */ + #endif /* QEMU_I386_TDX_H */ From a668268dc08f7f4d30cecd513054bb38ce48c0d6 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:13 -0400 Subject: [PATCH 1224/2760] kvm: Introduce kvm_arch_pre_create_vcpu() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce kvm_arch_pre_create_vcpu(), to perform arch-dependent work prior to create any vcpu. This is for i386 TDX because it needs call TDX_INIT_VM before creating any vcpu. The specific implementation for i386 will be added in the future patch. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-8-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 5 +++++ include/system/kvm.h | 1 + target/arm/kvm.c | 5 +++++ target/i386/kvm/kvm.c | 5 +++++ target/loongarch/kvm/kvm.c | 4 ++++ target/mips/kvm.c | 5 +++++ target/ppc/kvm.c | 5 +++++ target/riscv/kvm/kvm-cpu.c | 5 +++++ target/s390x/kvm/kvm.c | 5 +++++ 9 files changed, 40 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 278a50690c..42d239cf8f 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -545,6 +545,11 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp) trace_kvm_init_vcpu(cpu->cpu_index, kvm_arch_vcpu_id(cpu)); + ret = kvm_arch_pre_create_vcpu(cpu, errp); + if (ret < 0) { + goto err; + } + ret = kvm_create_vcpu(cpu); if (ret < 0) { error_setg_errno(errp, -ret, diff --git a/include/system/kvm.h b/include/system/kvm.h index b690dda137..62ec131d4d 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h @@ -376,6 +376,7 @@ int kvm_arch_get_default_type(MachineState *ms); int kvm_arch_init(MachineState *ms, KVMState *s); +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp); int kvm_arch_init_vcpu(CPUState *cpu); int kvm_arch_destroy_vcpu(CPUState *cpu); diff --git a/target/arm/kvm.c b/target/arm/kvm.c index a2791aa866..74fda8b809 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -1846,6 +1846,11 @@ static int kvm_arm_sve_set_vls(ARMCPU *cpu) #define ARM_CPU_ID_MPIDR 3, 0, 0, 0, 5 +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return 0; +} + int kvm_arch_init_vcpu(CPUState *cs) { int ret; diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 6d88495d47..446f0600d4 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2051,6 +2051,11 @@ static uint32_t kvm_x86_build_cpuid(CPUX86State *env, abort(); } +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return 0; +} + int kvm_arch_init_vcpu(CPUState *cs) { struct { diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index 1bda570482..c66bdd5302 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -1071,7 +1071,11 @@ static int kvm_cpu_check_pv_features(CPUState *cs, Error **errp) env->pv_features |= BIT(KVM_FEATURE_VIRT_EXTIOI); } } + return 0; +} +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) +{ return 0; } diff --git a/target/mips/kvm.c b/target/mips/kvm.c index d67b7c1a8e..ec53acb51a 100644 --- a/target/mips/kvm.c +++ b/target/mips/kvm.c @@ -61,6 +61,11 @@ int kvm_arch_irqchip_create(KVMState *s) return 0; } +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return 0; +} + int kvm_arch_init_vcpu(CPUState *cs) { CPUMIPSState *env = cpu_env(cs); diff --git a/target/ppc/kvm.c b/target/ppc/kvm.c index 8a957c3c7d..015658049e 100644 --- a/target/ppc/kvm.c +++ b/target/ppc/kvm.c @@ -479,6 +479,11 @@ static void kvmppc_hw_debug_points_init(CPUPPCState *cenv) } } +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return 0; +} + int kvm_arch_init_vcpu(CPUState *cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index efb41fac53..e1a04be20f 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1472,6 +1472,11 @@ static int kvm_vcpu_enable_sbi_dbcn(RISCVCPU *cpu, CPUState *cs) return kvm_set_one_reg(cs, kvm_sbi_dbcn.kvm_reg_id, ®); } +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return 0; +} + int kvm_arch_init_vcpu(CPUState *cs) { int ret = 0; diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 6cd2ebc5f1..67d9a1977c 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -398,6 +398,11 @@ unsigned long kvm_arch_vcpu_id(CPUState *cpu) return cpu->cpu_index; } +int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return 0; +} + int kvm_arch_init_vcpu(CPUState *cs) { unsigned int max_cpus = MACHINE(qdev_get_machine())->smp.max_cpus; From f15898b0f50609d66465326221aa54b6699da674 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:14 -0400 Subject: [PATCH 1225/2760] i386/tdx: Initialize TDX before creating TD vcpus Invoke KVM_TDX_INIT_VM in kvm_arch_pre_create_vcpu() that KVM_TDX_INIT_VM configures global TD configurations, e.g. the canonical CPUID config, and must be executed prior to creating vCPUs. Use kvm_x86_arch_cpuid() to setup the CPUID settings for TDX VM. Note, this doesn't address the fact that QEMU may change the CPUID configuration when creating vCPUs, i.e. punts on refactoring QEMU to provide a stable CPUID config prior to kvm_arch_init(). Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Acked-by: Markus Armbruster Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-9-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 16 +++--- target/i386/kvm/kvm_i386.h | 5 ++ target/i386/kvm/meson.build | 2 +- target/i386/kvm/tdx-stub.c | 10 ++++ target/i386/kvm/tdx.c | 105 ++++++++++++++++++++++++++++++++++++ target/i386/kvm/tdx.h | 6 +++ 6 files changed, 137 insertions(+), 7 deletions(-) create mode 100644 target/i386/kvm/tdx-stub.c diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 446f0600d4..e98f1ee26a 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -38,6 +38,7 @@ #include "kvm_i386.h" #include "../confidential-guest.h" #include "sev.h" +#include "tdx.h" #include "xen-emu.h" #include "hyperv.h" #include "hyperv-proto.h" @@ -415,9 +416,9 @@ static uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg) /* Find matching entry for function/index on kvm_cpuid2 struct */ -static struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid, - uint32_t function, - uint32_t index) +struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid, + uint32_t function, + uint32_t index) { int i; for (i = 0; i < cpuid->nent; ++i) { @@ -1822,9 +1823,8 @@ static void kvm_init_nested_state(CPUX86State *env) } } -static uint32_t kvm_x86_build_cpuid(CPUX86State *env, - struct kvm_cpuid_entry2 *entries, - uint32_t cpuid_i) +uint32_t kvm_x86_build_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries, + uint32_t cpuid_i) { uint32_t limit, i, j; uint32_t unused; @@ -2053,6 +2053,10 @@ static uint32_t kvm_x86_build_cpuid(CPUX86State *env, int kvm_arch_pre_create_vcpu(CPUState *cpu, Error **errp) { + if (is_tdx_vm()) { + return tdx_pre_create_vcpu(cpu, errp); + } + return 0; } diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index ed1e61fb8b..dc696cb723 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -59,6 +59,11 @@ uint64_t kvm_swizzle_msi_ext_dest_id(uint64_t address); void kvm_update_msi_routes_all(void *private, bool global, uint32_t index, uint32_t mask); +struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid, + uint32_t function, + uint32_t index); +uint32_t kvm_x86_build_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries, + uint32_t cpuid_i); #endif /* CONFIG_KVM */ void kvm_pc_setup_irq_routing(bool pci_enabled); diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build index 466bccb9cb..3f44cdedb7 100644 --- a/target/i386/kvm/meson.build +++ b/target/i386/kvm/meson.build @@ -8,7 +8,7 @@ i386_kvm_ss.add(files( i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c')) -i386_kvm_ss.add(when: 'CONFIG_TDX', if_true: files('tdx.c')) +i386_kvm_ss.add(when: 'CONFIG_TDX', if_true: files('tdx.c'), if_false: files('tdx-stub.c')) i386_system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) diff --git a/target/i386/kvm/tdx-stub.c b/target/i386/kvm/tdx-stub.c new file mode 100644 index 0000000000..2344433594 --- /dev/null +++ b/target/i386/kvm/tdx-stub.c @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" + +#include "tdx.h" + +int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + return -EINVAL; +} diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 3750889453..2d2d48c083 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -149,6 +149,109 @@ static int tdx_kvm_type(X86ConfidentialGuest *cg) return KVM_X86_TDX_VM; } +static int setup_td_xfam(X86CPU *x86cpu, Error **errp) +{ + CPUX86State *env = &x86cpu->env; + uint64_t xfam; + + xfam = env->features[FEAT_XSAVE_XCR0_LO] | + env->features[FEAT_XSAVE_XCR0_HI] | + env->features[FEAT_XSAVE_XSS_LO] | + env->features[FEAT_XSAVE_XSS_HI]; + + if (xfam & ~tdx_caps->supported_xfam) { + error_setg(errp, "Invalid XFAM 0x%lx for TDX VM (supported: 0x%llx))", + xfam, tdx_caps->supported_xfam); + return -1; + } + + tdx_guest->xfam = xfam; + return 0; +} + +static void tdx_filter_cpuid(struct kvm_cpuid2 *cpuids) +{ + int i, dest_cnt = 0; + struct kvm_cpuid_entry2 *src, *dest, *conf; + + for (i = 0; i < cpuids->nent; i++) { + src = cpuids->entries + i; + conf = cpuid_find_entry(&tdx_caps->cpuid, src->function, src->index); + if (!conf) { + continue; + } + dest = cpuids->entries + dest_cnt; + + dest->function = src->function; + dest->index = src->index; + dest->flags = src->flags; + dest->eax = src->eax & conf->eax; + dest->ebx = src->ebx & conf->ebx; + dest->ecx = src->ecx & conf->ecx; + dest->edx = src->edx & conf->edx; + + dest_cnt++; + } + cpuids->nent = dest_cnt++; +} + +int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) +{ + X86CPU *x86cpu = X86_CPU(cpu); + CPUX86State *env = &x86cpu->env; + g_autofree struct kvm_tdx_init_vm *init_vm = NULL; + Error *local_err = NULL; + int retry = 10000; + int r = 0; + + QEMU_LOCK_GUARD(&tdx_guest->lock); + if (tdx_guest->initialized) { + return r; + } + + init_vm = g_malloc0(sizeof(struct kvm_tdx_init_vm) + + sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES); + + r = setup_td_xfam(x86cpu, errp); + if (r) { + return r; + } + + init_vm->cpuid.nent = kvm_x86_build_cpuid(env, init_vm->cpuid.entries, 0); + tdx_filter_cpuid(&init_vm->cpuid); + + init_vm->attributes = tdx_guest->attributes; + init_vm->xfam = tdx_guest->xfam; + + /* + * KVM_TDX_INIT_VM gets -EAGAIN when KVM side SEAMCALL(TDH_MNG_CREATE) + * gets TDX_RND_NO_ENTROPY due to Random number generation (e.g., RDRAND or + * RDSEED) is busy. + * + * Retry for the case. + */ + do { + error_free(local_err); + local_err = NULL; + r = tdx_vm_ioctl(KVM_TDX_INIT_VM, 0, init_vm, &local_err); + } while (r == -EAGAIN && --retry); + + if (r < 0) { + if (!retry) { + error_append_hint(&local_err, "Hardware RNG (Random Number " + "Generator) is busy occupied by someone (via RDRAND/RDSEED) " + "maliciously, which leads to KVM_TDX_INIT_VM keeping failure " + "due to lack of entropy.\n"); + } + error_propagate(errp, local_err); + return r; + } + + tdx_guest->initialized = true; + + return 0; +} + /* tdx guest */ OBJECT_DEFINE_TYPE_WITH_INTERFACES(TdxGuest, tdx_guest, @@ -162,6 +265,8 @@ static void tdx_guest_init(Object *obj) ConfidentialGuestSupport *cgs = CONFIDENTIAL_GUEST_SUPPORT(obj); TdxGuest *tdx = TDX_GUEST(obj); + qemu_mutex_init(&tdx->lock); + cgs->require_guest_memfd = true; tdx->attributes = 0; diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index de8ae91961..4e2b5c61ff 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -19,7 +19,11 @@ typedef struct TdxGuestClass { typedef struct TdxGuest { X86ConfidentialGuest parent_obj; + QemuMutex lock; + + bool initialized; uint64_t attributes; /* TD attributes */ + uint64_t xfam; } TdxGuest; #ifdef CONFIG_TDX @@ -28,4 +32,6 @@ bool is_tdx_vm(void); #define is_tdx_vm() 0 #endif /* CONFIG_TDX */ +int tdx_pre_create_vcpu(CPUState *cpu, Error **errp); + #endif /* QEMU_I386_TDX_H */ From 6016e2972d94c90307b6caf55a8e3aee5424c09b Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:15 -0400 Subject: [PATCH 1226/2760] i386/tdx: Add property sept-ve-disable for tdx-guest object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bit 28 of TD attribute, named SEPT_VE_DISABLE. When set to 1, it disables EPT violation conversion to #VE on guest TD access of PENDING pages. Some guest OS (e.g., Linux TD guest) may require this bit as 1. Otherwise refuse to boot. Add sept-ve-disable property for tdx-guest object, for user to configure this bit. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Acked-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-10-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- qapi/qom.json | 8 +++++++- target/i386/kvm/tdx.c | 23 +++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/qapi/qom.json b/qapi/qom.json index 3d7e11efc3..5a88d36423 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -1055,10 +1055,16 @@ # @attributes: The 'attributes' of a TD guest that is passed to # KVM_TDX_INIT_VM # +# @sept-ve-disable: toggle bit 28 of TD attributes to control disabling +# of EPT violation conversion to #VE on guest TD access of PENDING +# pages. Some guest OS (e.g., Linux TD guest) may require this to +# be set, otherwise they refuse to boot. +# # Since: 10.1 ## { 'struct': 'TdxGuestProperties', - 'data': { '*attributes': 'uint64' } } + 'data': { '*attributes': 'uint64', + '*sept-ve-disable': 'bool' } } ## # @ThreadContextProperties: diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 2d2d48c083..32ba3982ff 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -18,6 +18,8 @@ #include "kvm_i386.h" #include "tdx.h" +#define TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE BIT_ULL(28) + static TdxGuest *tdx_guest; static struct kvm_tdx_capabilities *tdx_caps; @@ -252,6 +254,24 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) return 0; } +static bool tdx_guest_get_sept_ve_disable(Object *obj, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + return !!(tdx->attributes & TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE); +} + +static void tdx_guest_set_sept_ve_disable(Object *obj, bool value, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + if (value) { + tdx->attributes |= TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE; + } else { + tdx->attributes &= ~TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE; + } +} + /* tdx guest */ OBJECT_DEFINE_TYPE_WITH_INTERFACES(TdxGuest, tdx_guest, @@ -272,6 +292,9 @@ static void tdx_guest_init(Object *obj) object_property_add_uint64_ptr(obj, "attributes", &tdx->attributes, OBJ_PROP_FLAG_READWRITE); + object_property_add_bool(obj, "sept-ve-disable", + tdx_guest_get_sept_ve_disable, + tdx_guest_set_sept_ve_disable); } static void tdx_guest_finalize(Object *obj) From 714af52276e74a1829674d180ef26ecb6261834c Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Thu, 8 May 2025 10:59:16 -0400 Subject: [PATCH 1227/2760] i386/tdx: Make sept_ve_disable set by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For TDX KVM use case, Linux guest is the most major one. It requires sept_ve_disable set. Make it default for the main use case. For other use case, it can be enabled/disabled via qemu command line. Signed-off-by: Isaku Yamahata Signed-off-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-11-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 32ba3982ff..a30731b1a3 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -288,7 +288,7 @@ static void tdx_guest_init(Object *obj) qemu_mutex_init(&tdx->lock); cgs->require_guest_memfd = true; - tdx->attributes = 0; + tdx->attributes = TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE; object_property_add_uint64_ptr(obj, "attributes", &tdx->attributes, OBJ_PROP_FLAG_READWRITE); From bb3be394cf80d68251e5b89e823dddc679b6e644 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:17 -0400 Subject: [PATCH 1228/2760] i386/tdx: Wire CPU features up with attributes of TD guest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For QEMU VMs, - PKS is configured via CPUID_7_0_ECX_PKS, e.g., -cpu xxx,+pks and - PMU is configured by x86cpu->enable_pmu, e.g., -cpu xxx,pmu=on While the bit 30 (PKS) and bit 63 (PERFMON) of TD's attributes are also used to configure the PKS and PERFMON/PMU of TD, reuse the existing configuration interfaces of 'cpu' for TD's attributes. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-12-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index a30731b1a3..22d66bdb14 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -19,6 +19,8 @@ #include "tdx.h" #define TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE BIT_ULL(28) +#define TDX_TD_ATTRIBUTES_PKS BIT_ULL(30) +#define TDX_TD_ATTRIBUTES_PERFMON BIT_ULL(63) static TdxGuest *tdx_guest; @@ -151,6 +153,15 @@ static int tdx_kvm_type(X86ConfidentialGuest *cg) return KVM_X86_TDX_VM; } +static void setup_td_guest_attributes(X86CPU *x86cpu) +{ + CPUX86State *env = &x86cpu->env; + + tdx_guest->attributes |= (env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_PKS) ? + TDX_TD_ATTRIBUTES_PKS : 0; + tdx_guest->attributes |= x86cpu->enable_pmu ? TDX_TD_ATTRIBUTES_PERFMON : 0; +} + static int setup_td_xfam(X86CPU *x86cpu, Error **errp) { CPUX86State *env = &x86cpu->env; @@ -214,6 +225,8 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) init_vm = g_malloc0(sizeof(struct kvm_tdx_init_vm) + sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES); + setup_td_guest_attributes(x86cpu); + r = setup_td_xfam(x86cpu, errp); if (r) { return r; From 53b6f406b4f1a215fb3ec60e56ddba2e019a45ef Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:18 -0400 Subject: [PATCH 1229/2760] i386/tdx: Validate TD attributes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Validate TD attributes with tdx_caps that only supported bits are allowed by KVM. Besides, sanity check the attribute bits that have not been supported by QEMU yet. e.g., debug bit, it will be allowed in the future when debug TD support lands in QEMU. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Zhao Liu Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250508150002.689633-13-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 22d66bdb14..c78a0e8b5e 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -18,10 +18,15 @@ #include "kvm_i386.h" #include "tdx.h" +#define TDX_TD_ATTRIBUTES_DEBUG BIT_ULL(0) #define TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE BIT_ULL(28) #define TDX_TD_ATTRIBUTES_PKS BIT_ULL(30) #define TDX_TD_ATTRIBUTES_PERFMON BIT_ULL(63) +#define TDX_SUPPORTED_TD_ATTRS (TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE |\ + TDX_TD_ATTRIBUTES_PKS | \ + TDX_TD_ATTRIBUTES_PERFMON) + static TdxGuest *tdx_guest; static struct kvm_tdx_capabilities *tdx_caps; @@ -153,13 +158,34 @@ static int tdx_kvm_type(X86ConfidentialGuest *cg) return KVM_X86_TDX_VM; } -static void setup_td_guest_attributes(X86CPU *x86cpu) +static int tdx_validate_attributes(TdxGuest *tdx, Error **errp) +{ + if ((tdx->attributes & ~tdx_caps->supported_attrs)) { + error_setg(errp, "Invalid attributes 0x%lx for TDX VM " + "(KVM supported: 0x%llx)", tdx->attributes, + tdx_caps->supported_attrs); + return -1; + } + + if (tdx->attributes & ~TDX_SUPPORTED_TD_ATTRS) { + error_setg(errp, "Some QEMU unsupported TD attribute bits being " + "requested: 0x%lx (QEMU supported: 0x%llx)", + tdx->attributes, TDX_SUPPORTED_TD_ATTRS); + return -1; + } + + return 0; +} + +static int setup_td_guest_attributes(X86CPU *x86cpu, Error **errp) { CPUX86State *env = &x86cpu->env; tdx_guest->attributes |= (env->features[FEAT_7_0_ECX] & CPUID_7_0_ECX_PKS) ? TDX_TD_ATTRIBUTES_PKS : 0; tdx_guest->attributes |= x86cpu->enable_pmu ? TDX_TD_ATTRIBUTES_PERFMON : 0; + + return tdx_validate_attributes(tdx_guest, errp); } static int setup_td_xfam(X86CPU *x86cpu, Error **errp) @@ -225,7 +251,10 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) init_vm = g_malloc0(sizeof(struct kvm_tdx_init_vm) + sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES); - setup_td_guest_attributes(x86cpu); + r = setup_td_guest_attributes(x86cpu, errp); + if (r) { + return r; + } r = setup_td_xfam(x86cpu, errp); if (r) { From d05a0858cf876f79b57a622716fbad07f5b2ea08 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Thu, 8 May 2025 10:59:19 -0400 Subject: [PATCH 1230/2760] i386/tdx: Support user configurable mrconfigid/mrowner/mrownerconfig Three sha384 hash values, mrconfigid, mrowner and mrownerconfig, of a TD can be provided for TDX attestation. Detailed meaning of them can be found: https://lore.kernel.org/qemu-devel/31d6dbc1-f453-4cef-ab08-4813f4e0ff92@intel.com/ Allow user to specify those values via property mrconfigid, mrowner and mrownerconfig. They are all in base64 format. example -object tdx-guest, \ mrconfigid=ASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83v,\ mrowner=ASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83v,\ mrownerconfig=ASNFZ4mrze8BI0VniavN7wEjRWeJq83vASNFZ4mrze8BI0VniavN7wEjRWeJq83v Signed-off-by: Isaku Yamahata Co-developed-by: Xiaoyao Li Signed-off-by: Xiaoyao Li Acked-by: Markus Armbruster Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-14-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- qapi/qom.json | 16 +++++++- target/i386/kvm/tdx.c | 95 +++++++++++++++++++++++++++++++++++++++++++ target/i386/kvm/tdx.h | 3 ++ 3 files changed, 113 insertions(+), 1 deletion(-) diff --git a/qapi/qom.json b/qapi/qom.json index 5a88d36423..45cd47508b 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -1060,11 +1060,25 @@ # pages. Some guest OS (e.g., Linux TD guest) may require this to # be set, otherwise they refuse to boot. # +# @mrconfigid: ID for non-owner-defined configuration of the guest TD, +# e.g., run-time or OS configuration (base64 encoded SHA384 digest). +# Defaults to all zeros. +# +# @mrowner: ID for the guest TD’s owner (base64 encoded SHA384 digest). +# Defaults to all zeros. +# +# @mrownerconfig: ID for owner-defined configuration of the guest TD, +# e.g., specific to the workload rather than the run-time or OS +# (base64 encoded SHA384 digest). Defaults to all zeros. +# # Since: 10.1 ## { 'struct': 'TdxGuestProperties', 'data': { '*attributes': 'uint64', - '*sept-ve-disable': 'bool' } } + '*sept-ve-disable': 'bool', + '*mrconfigid': 'str', + '*mrowner': 'str', + '*mrownerconfig': 'str' } } ## # @ThreadContextProperties: diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index c78a0e8b5e..671f23d910 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -11,8 +11,10 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "qemu/base64.h" #include "qapi/error.h" #include "qom/object_interfaces.h" +#include "crypto/hash.h" #include "hw/i386/x86.h" #include "kvm_i386.h" @@ -240,6 +242,7 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) CPUX86State *env = &x86cpu->env; g_autofree struct kvm_tdx_init_vm *init_vm = NULL; Error *local_err = NULL; + size_t data_len; int retry = 10000; int r = 0; @@ -251,6 +254,45 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) init_vm = g_malloc0(sizeof(struct kvm_tdx_init_vm) + sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES); + if (tdx_guest->mrconfigid) { + g_autofree uint8_t *data = qbase64_decode(tdx_guest->mrconfigid, + strlen(tdx_guest->mrconfigid), &data_len, errp); + if (!data) { + return -1; + } + if (data_len != QCRYPTO_HASH_DIGEST_LEN_SHA384) { + error_setg(errp, "TDX: failed to decode mrconfigid"); + return -1; + } + memcpy(init_vm->mrconfigid, data, data_len); + } + + if (tdx_guest->mrowner) { + g_autofree uint8_t *data = qbase64_decode(tdx_guest->mrowner, + strlen(tdx_guest->mrowner), &data_len, errp); + if (!data) { + return -1; + } + if (data_len != QCRYPTO_HASH_DIGEST_LEN_SHA384) { + error_setg(errp, "TDX: failed to decode mrowner"); + return -1; + } + memcpy(init_vm->mrowner, data, data_len); + } + + if (tdx_guest->mrownerconfig) { + g_autofree uint8_t *data = qbase64_decode(tdx_guest->mrownerconfig, + strlen(tdx_guest->mrownerconfig), &data_len, errp); + if (!data) { + return -1; + } + if (data_len != QCRYPTO_HASH_DIGEST_LEN_SHA384) { + error_setg(errp, "TDX: failed to decode mrownerconfig"); + return -1; + } + memcpy(init_vm->mrownerconfig, data, data_len); + } + r = setup_td_guest_attributes(x86cpu, errp); if (r) { return r; @@ -314,6 +356,51 @@ static void tdx_guest_set_sept_ve_disable(Object *obj, bool value, Error **errp) } } +static char *tdx_guest_get_mrconfigid(Object *obj, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + return g_strdup(tdx->mrconfigid); +} + +static void tdx_guest_set_mrconfigid(Object *obj, const char *value, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + g_free(tdx->mrconfigid); + tdx->mrconfigid = g_strdup(value); +} + +static char *tdx_guest_get_mrowner(Object *obj, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + return g_strdup(tdx->mrowner); +} + +static void tdx_guest_set_mrowner(Object *obj, const char *value, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + g_free(tdx->mrowner); + tdx->mrowner = g_strdup(value); +} + +static char *tdx_guest_get_mrownerconfig(Object *obj, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + return g_strdup(tdx->mrownerconfig); +} + +static void tdx_guest_set_mrownerconfig(Object *obj, const char *value, Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + g_free(tdx->mrownerconfig); + tdx->mrownerconfig = g_strdup(value); +} + /* tdx guest */ OBJECT_DEFINE_TYPE_WITH_INTERFACES(TdxGuest, tdx_guest, @@ -337,6 +424,14 @@ static void tdx_guest_init(Object *obj) object_property_add_bool(obj, "sept-ve-disable", tdx_guest_get_sept_ve_disable, tdx_guest_set_sept_ve_disable); + object_property_add_str(obj, "mrconfigid", + tdx_guest_get_mrconfigid, + tdx_guest_set_mrconfigid); + object_property_add_str(obj, "mrowner", + tdx_guest_get_mrowner, tdx_guest_set_mrowner); + object_property_add_str(obj, "mrownerconfig", + tdx_guest_get_mrownerconfig, + tdx_guest_set_mrownerconfig); } static void tdx_guest_finalize(Object *obj) diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index 4e2b5c61ff..e472b11fb0 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -24,6 +24,9 @@ typedef struct TdxGuest { bool initialized; uint64_t attributes; /* TD attributes */ uint64_t xfam; + char *mrconfigid; /* base64 encoded sha348 digest */ + char *mrowner; /* base64 encoded sha348 digest */ + char *mrownerconfig; /* base64 encoded sha348 digest */ } TdxGuest; #ifdef CONFIG_TDX From d529a2ac5ef4620173439942f78ec668f9165fc1 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:20 -0400 Subject: [PATCH 1231/2760] i386/tdx: Set APIC bus rate to match with what TDX module enforces MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TDX advertises core crystal clock with cpuid[0x15] as 25MHz for TD guests and it's unchangeable from VMM. As a result, TDX guest reads the APIC timer at the same frequency, 25MHz. While KVM's default emulated frequency for APIC bus is 1GHz, set the APIC bus rate to match with TDX explicitly to ensure KVM provide correct emulated APIC timer for TD guest. Signed-off-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-15-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 13 +++++++++++++ target/i386/kvm/tdx.h | 3 +++ 2 files changed, 16 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 671f23d910..58983edd80 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -254,6 +254,19 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) init_vm = g_malloc0(sizeof(struct kvm_tdx_init_vm) + sizeof(struct kvm_cpuid_entry2) * KVM_MAX_CPUID_ENTRIES); + if (!kvm_check_extension(kvm_state, KVM_CAP_X86_APIC_BUS_CYCLES_NS)) { + error_setg(errp, "KVM doesn't support KVM_CAP_X86_APIC_BUS_CYCLES_NS"); + return -EOPNOTSUPP; + } + + r = kvm_vm_enable_cap(kvm_state, KVM_CAP_X86_APIC_BUS_CYCLES_NS, + 0, TDX_APIC_BUS_CYCLES_NS); + if (r < 0) { + error_setg_errno(errp, -r, + "Unable to set core crystal clock frequency to 25MHz"); + return r; + } + if (tdx_guest->mrconfigid) { g_autofree uint8_t *data = qbase64_decode(tdx_guest->mrconfigid, strlen(tdx_guest->mrconfigid), &data_len, errp); diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index e472b11fb0..d39e733d9f 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -16,6 +16,9 @@ typedef struct TdxGuestClass { X86ConfidentialGuestClass parent_class; } TdxGuestClass; +/* TDX requires bus frequency 25MHz */ +#define TDX_APIC_BUS_CYCLES_NS 40 + typedef struct TdxGuest { X86ConfidentialGuest parent_obj; From 0e73b843616e52882940ab89e1b0e86e22be2162 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:21 -0400 Subject: [PATCH 1232/2760] i386/tdx: Implement user specified tsc frequency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reuse "-cpu,tsc-frequency=" to get user wanted tsc frequency and call VM scope VM_SET_TSC_KHZ to set the tsc frequency of TD before KVM_TDX_INIT_VM. Besides, sanity check the tsc frequency to be in the legal range and legal granularity (required by TDX module). Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-16-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 9 +++++++++ target/i386/kvm/tdx.c | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index e98f1ee26a..fd1817fc5e 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -870,6 +870,15 @@ static int kvm_arch_set_tsc_khz(CPUState *cs) int r, cur_freq; bool set_ioctl = false; + /* + * TSC of TD vcpu is immutable, it cannot be set/changed via vcpu scope + * VM_SET_TSC_KHZ, but only be initialized via VM scope VM_SET_TSC_KHZ + * before ioctl KVM_TDX_INIT_VM in tdx_pre_create_vcpu() + */ + if (is_tdx_vm()) { + return 0; + } + if (!env->tsc_khz) { return 0; } diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 58983edd80..93a16a1aaa 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -20,6 +20,9 @@ #include "kvm_i386.h" #include "tdx.h" +#define TDX_MIN_TSC_FREQUENCY_KHZ (100 * 1000) +#define TDX_MAX_TSC_FREQUENCY_KHZ (10 * 1000 * 1000) + #define TDX_TD_ATTRIBUTES_DEBUG BIT_ULL(0) #define TDX_TD_ATTRIBUTES_SEPT_VE_DISABLE BIT_ULL(28) #define TDX_TD_ATTRIBUTES_PKS BIT_ULL(30) @@ -267,6 +270,28 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) return r; } + if (env->tsc_khz && (env->tsc_khz < TDX_MIN_TSC_FREQUENCY_KHZ || + env->tsc_khz > TDX_MAX_TSC_FREQUENCY_KHZ)) { + error_setg(errp, "Invalid TSC %ld KHz, must specify cpu_frequency " + "between [%d, %d] kHz", env->tsc_khz, + TDX_MIN_TSC_FREQUENCY_KHZ, TDX_MAX_TSC_FREQUENCY_KHZ); + return -EINVAL; + } + + if (env->tsc_khz % (25 * 1000)) { + error_setg(errp, "Invalid TSC %ld KHz, it must be multiple of 25MHz", + env->tsc_khz); + return -EINVAL; + } + + /* it's safe even env->tsc_khz is 0. KVM uses host's tsc_khz in this case */ + r = kvm_vm_ioctl(kvm_state, KVM_SET_TSC_KHZ, env->tsc_khz); + if (r < 0) { + error_setg_errno(errp, -r, "Unable to set TSC frequency to %ld kHz", + env->tsc_khz); + return r; + } + if (tdx_guest->mrconfigid) { g_autofree uint8_t *data = qbase64_decode(tdx_guest->mrconfigid, strlen(tdx_guest->mrconfigid), &data_len, errp); From 0dd5fe5ebeabefc7b3d7f043991b1edfe6b8eda9 Mon Sep 17 00:00:00 2001 From: Chao Peng Date: Thu, 8 May 2025 10:59:22 -0400 Subject: [PATCH 1233/2760] i386/tdx: load TDVF for TD guest TDVF(OVMF) needs to run at private memory for TD guest. TDX cannot support pflash device since it doesn't support read-only private memory. Thus load TDVF(OVMF) with -bios option for TDs. Use memory_region_init_ram_guest_memfd() to allocate the MemoryRegion for TDVF because it needs to be located at private memory. Also store the MemoryRegion pointer of TDVF since the shared ramblock of it can be discared after it gets copied to private ramblock. Signed-off-by: Chao Peng Co-developed-by: Xiaoyao Li Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-17-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/x86-common.c | 6 +++++- target/i386/kvm/tdx.c | 6 ++++++ target/i386/kvm/tdx.h | 3 +++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/hw/i386/x86-common.c b/hw/i386/x86-common.c index 1b0671c523..b1b5f11e73 100644 --- a/hw/i386/x86-common.c +++ b/hw/i386/x86-common.c @@ -44,6 +44,7 @@ #include "standard-headers/asm-x86/bootparam.h" #include CONFIG_DEVICES #include "kvm/kvm_i386.h" +#include "kvm/tdx.h" #ifdef CONFIG_XEN_EMU #include "hw/xen/xen.h" @@ -1035,11 +1036,14 @@ void x86_bios_rom_init(X86MachineState *x86ms, const char *default_firmware, if (machine_require_guest_memfd(MACHINE(x86ms))) { memory_region_init_ram_guest_memfd(&x86ms->bios, NULL, "pc.bios", bios_size, &error_fatal); + if (is_tdx_vm()) { + tdx_set_tdvf_region(&x86ms->bios); + } } else { memory_region_init_ram(&x86ms->bios, NULL, "pc.bios", bios_size, &error_fatal); } - if (sev_enabled()) { + if (sev_enabled() || is_tdx_vm()) { /* * The concept of a "reset" simply doesn't exist for * confidential computing guests, we have to destroy and diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 93a16a1aaa..0f5acbf980 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -137,6 +137,12 @@ static int get_tdx_capabilities(Error **errp) return 0; } +void tdx_set_tdvf_region(MemoryRegion *tdvf_mr) +{ + assert(!tdx_guest->tdvf_mr); + tdx_guest->tdvf_mr = tdvf_mr; +} + static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { TdxGuest *tdx = TDX_GUEST(cgs); diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index d39e733d9f..b73461b8d8 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -30,6 +30,8 @@ typedef struct TdxGuest { char *mrconfigid; /* base64 encoded sha348 digest */ char *mrowner; /* base64 encoded sha348 digest */ char *mrownerconfig; /* base64 encoded sha348 digest */ + + MemoryRegion *tdvf_mr; } TdxGuest; #ifdef CONFIG_TDX @@ -39,5 +41,6 @@ bool is_tdx_vm(void); #endif /* CONFIG_TDX */ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp); +void tdx_set_tdvf_region(MemoryRegion *tdvf_mr); #endif /* QEMU_I386_TDX_H */ From b65a6011d16c4f7cb2eb227ab1bc735850475288 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Thu, 8 May 2025 10:59:23 -0400 Subject: [PATCH 1234/2760] i386/tdvf: Introduce function to parse TDVF metadata TDX VM needs to boot with its specialized firmware, Trusted Domain Virtual Firmware (TDVF). QEMU needs to parse TDVF and map it in TD guest memory prior to running the TDX VM. A TDVF Metadata in TDVF image describes the structure of firmware. QEMU refers to it to setup memory for TDVF. Introduce function tdvf_parse_metadata() to parse the metadata from TDVF image and store the info of each TDVF section. TDX metadata is located by a TDX metadata offset block, which is a GUID-ed structure. The data portion of the GUID structure contains only an 4-byte field that is the offset of TDX metadata to the end of firmware file. Select X86_FW_OVMF when TDX is enable to leverage existing functions to parse and search OVMF's GUID-ed structures. Signed-off-by: Isaku Yamahata Co-developed-by: Xiaoyao Li Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-18-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/Kconfig | 1 + hw/i386/meson.build | 1 + hw/i386/tdvf.c | 188 +++++++++++++++++++++++++++++++++++++++++ include/hw/i386/tdvf.h | 38 +++++++++ 4 files changed, 228 insertions(+) create mode 100644 hw/i386/tdvf.c create mode 100644 include/hw/i386/tdvf.h diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index cce9521ba9..eb65bda6e0 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -12,6 +12,7 @@ config SGX config TDX bool + select X86_FW_OVMF depends on KVM config PC diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 10bdfde27c..3bc1da2b6e 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -32,6 +32,7 @@ i386_ss.add(when: 'CONFIG_PC', if_true: files( 'port92.c')) i386_ss.add(when: 'CONFIG_X86_FW_OVMF', if_true: files('pc_sysfw_ovmf.c'), if_false: files('pc_sysfw_ovmf-stubs.c')) +i386_ss.add(when: 'CONFIG_TDX', if_true: files('tdvf.c')) subdir('kvm') subdir('xen') diff --git a/hw/i386/tdvf.c b/hw/i386/tdvf.c new file mode 100644 index 0000000000..e2d486946a --- /dev/null +++ b/hw/i386/tdvf.c @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2025 Intel Corporation + * Author: Isaku Yamahata + * + * Xiaoyao Li + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" + +#include "hw/i386/pc.h" +#include "hw/i386/tdvf.h" +#include "system/kvm.h" + +#define TDX_METADATA_OFFSET_GUID "e47a6535-984a-4798-865e-4685a7bf8ec2" +#define TDX_METADATA_VERSION 1 +#define TDVF_SIGNATURE 0x46564454 /* TDVF as little endian */ +#define TDVF_ALIGNMENT 4096 + +/* + * the raw structs read from TDVF keeps the name convention in + * TDVF Design Guide spec. + */ +typedef struct { + uint32_t DataOffset; + uint32_t RawDataSize; + uint64_t MemoryAddress; + uint64_t MemoryDataSize; + uint32_t Type; + uint32_t Attributes; +} TdvfSectionEntry; + +typedef struct { + uint32_t Signature; + uint32_t Length; + uint32_t Version; + uint32_t NumberOfSectionEntries; + TdvfSectionEntry SectionEntries[]; +} TdvfMetadata; + +struct tdx_metadata_offset { + uint32_t offset; +}; + +static TdvfMetadata *tdvf_get_metadata(void *flash_ptr, int size) +{ + TdvfMetadata *metadata; + uint32_t offset = 0; + uint8_t *data; + + if ((uint32_t) size != size) { + return NULL; + } + + if (pc_system_ovmf_table_find(TDX_METADATA_OFFSET_GUID, &data, NULL)) { + offset = size - le32_to_cpu(((struct tdx_metadata_offset *)data)->offset); + + if (offset + sizeof(*metadata) > size) { + return NULL; + } + } else { + error_report("Cannot find TDX_METADATA_OFFSET_GUID"); + return NULL; + } + + metadata = flash_ptr + offset; + + /* Finally, verify the signature to determine if this is a TDVF image. */ + metadata->Signature = le32_to_cpu(metadata->Signature); + if (metadata->Signature != TDVF_SIGNATURE) { + error_report("Invalid TDVF signature in metadata!"); + return NULL; + } + + /* Sanity check that the TDVF doesn't overlap its own metadata. */ + metadata->Length = le32_to_cpu(metadata->Length); + if (offset + metadata->Length > size) { + return NULL; + } + + /* Only version 1 is supported/defined. */ + metadata->Version = le32_to_cpu(metadata->Version); + if (metadata->Version != TDX_METADATA_VERSION) { + return NULL; + } + + return metadata; +} + +static int tdvf_parse_and_check_section_entry(const TdvfSectionEntry *src, + TdxFirmwareEntry *entry) +{ + entry->data_offset = le32_to_cpu(src->DataOffset); + entry->data_len = le32_to_cpu(src->RawDataSize); + entry->address = le64_to_cpu(src->MemoryAddress); + entry->size = le64_to_cpu(src->MemoryDataSize); + entry->type = le32_to_cpu(src->Type); + entry->attributes = le32_to_cpu(src->Attributes); + + /* sanity check */ + if (entry->size < entry->data_len) { + error_report("Broken metadata RawDataSize 0x%x MemoryDataSize 0x%lx", + entry->data_len, entry->size); + return -1; + } + if (!QEMU_IS_ALIGNED(entry->address, TDVF_ALIGNMENT)) { + error_report("MemoryAddress 0x%lx not page aligned", entry->address); + return -1; + } + if (!QEMU_IS_ALIGNED(entry->size, TDVF_ALIGNMENT)) { + error_report("MemoryDataSize 0x%lx not page aligned", entry->size); + return -1; + } + + switch (entry->type) { + case TDVF_SECTION_TYPE_BFV: + case TDVF_SECTION_TYPE_CFV: + /* The sections that must be copied from firmware image to TD memory */ + if (entry->data_len == 0) { + error_report("%d section with RawDataSize == 0", entry->type); + return -1; + } + break; + case TDVF_SECTION_TYPE_TD_HOB: + case TDVF_SECTION_TYPE_TEMP_MEM: + /* The sections that no need to be copied from firmware image */ + if (entry->data_len != 0) { + error_report("%d section with RawDataSize 0x%x != 0", + entry->type, entry->data_len); + return -1; + } + break; + default: + error_report("TDVF contains unsupported section type %d", entry->type); + return -1; + } + + return 0; +} + +int tdvf_parse_metadata(TdxFirmware *fw, void *flash_ptr, int size) +{ + g_autofree TdvfSectionEntry *sections = NULL; + TdvfMetadata *metadata; + ssize_t entries_size; + int i; + + metadata = tdvf_get_metadata(flash_ptr, size); + if (!metadata) { + return -EINVAL; + } + + /* load and parse metadata entries */ + fw->nr_entries = le32_to_cpu(metadata->NumberOfSectionEntries); + if (fw->nr_entries < 2) { + error_report("Invalid number of fw entries (%u) in TDVF Metadata", + fw->nr_entries); + return -EINVAL; + } + + entries_size = fw->nr_entries * sizeof(TdvfSectionEntry); + if (metadata->Length != sizeof(*metadata) + entries_size) { + error_report("TDVF metadata len (0x%x) mismatch, expected (0x%x)", + metadata->Length, + (uint32_t)(sizeof(*metadata) + entries_size)); + return -EINVAL; + } + + fw->entries = g_new(TdxFirmwareEntry, fw->nr_entries); + sections = g_new(TdvfSectionEntry, fw->nr_entries); + + memcpy(sections, (void *)metadata + sizeof(*metadata), entries_size); + + for (i = 0; i < fw->nr_entries; i++) { + if (tdvf_parse_and_check_section_entry(§ions[i], &fw->entries[i])) { + goto err; + } + } + + return 0; + +err: + fw->entries = 0; + g_free(fw->entries); + return -EINVAL; +} diff --git a/include/hw/i386/tdvf.h b/include/hw/i386/tdvf.h new file mode 100644 index 0000000000..7ebcac42a3 --- /dev/null +++ b/include/hw/i386/tdvf.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2025 Intel Corporation + * Author: Isaku Yamahata + * + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_I386_TDVF_H +#define HW_I386_TDVF_H + +#include "qemu/osdep.h" + +#define TDVF_SECTION_TYPE_BFV 0 +#define TDVF_SECTION_TYPE_CFV 1 +#define TDVF_SECTION_TYPE_TD_HOB 2 +#define TDVF_SECTION_TYPE_TEMP_MEM 3 + +#define TDVF_SECTION_ATTRIBUTES_MR_EXTEND (1U << 0) +#define TDVF_SECTION_ATTRIBUTES_PAGE_AUG (1U << 1) + +typedef struct TdxFirmwareEntry { + uint32_t data_offset; + uint32_t data_len; + uint64_t address; + uint64_t size; + uint32_t type; + uint32_t attributes; +} TdxFirmwareEntry; + +typedef struct TdxFirmware { + uint32_t nr_entries; + TdxFirmwareEntry *entries; +} TdxFirmware; + +int tdvf_parse_metadata(TdxFirmware *fw, void *flash_ptr, int size); + +#endif /* HW_I386_TDVF_H */ From cb5d65a854e58abeb705a2ce14cc3eb28973c606 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:24 -0400 Subject: [PATCH 1235/2760] i386/tdx: Parse TDVF metadata for TDX VM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After TDVF is loaded to bios MemoryRegion, it needs parse TDVF metadata. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-19-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/pc_sysfw.c | 7 +++++++ target/i386/kvm/tdx-stub.c | 5 +++++ target/i386/kvm/tdx.c | 5 +++++ target/i386/kvm/tdx.h | 3 +++ 4 files changed, 20 insertions(+) diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 1eeb58ab37..821396c16e 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -37,6 +37,7 @@ #include "hw/block/flash.h" #include "system/kvm.h" #include "target/i386/sev.h" +#include "kvm/tdx.h" #define FLASH_SECTOR_SIZE 4096 @@ -280,5 +281,11 @@ void x86_firmware_configure(hwaddr gpa, void *ptr, int size) } sev_encrypt_flash(gpa, ptr, size, &error_fatal); + } else if (is_tdx_vm()) { + ret = tdx_parse_tdvf(ptr, size); + if (ret) { + error_report("failed to parse TDVF for TDX VM"); + exit(1); + } } } diff --git a/target/i386/kvm/tdx-stub.c b/target/i386/kvm/tdx-stub.c index 2344433594..7748b6d0a4 100644 --- a/target/i386/kvm/tdx-stub.c +++ b/target/i386/kvm/tdx-stub.c @@ -8,3 +8,8 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) { return -EINVAL; } + +int tdx_parse_tdvf(void *flash_ptr, int size) +{ + return -EINVAL; +} diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 0f5acbf980..18beba2f5c 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -382,6 +382,11 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) return 0; } +int tdx_parse_tdvf(void *flash_ptr, int size) +{ + return tdvf_parse_metadata(&tdx_guest->tdvf, flash_ptr, size); +} + static bool tdx_guest_get_sept_ve_disable(Object *obj, Error **errp) { TdxGuest *tdx = TDX_GUEST(obj); diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index b73461b8d8..28a03c2a7b 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -8,6 +8,7 @@ #endif #include "confidential-guest.h" +#include "hw/i386/tdvf.h" #define TYPE_TDX_GUEST "tdx-guest" #define TDX_GUEST(obj) OBJECT_CHECK(TdxGuest, (obj), TYPE_TDX_GUEST) @@ -32,6 +33,7 @@ typedef struct TdxGuest { char *mrownerconfig; /* base64 encoded sha348 digest */ MemoryRegion *tdvf_mr; + TdxFirmware tdvf; } TdxGuest; #ifdef CONFIG_TDX @@ -42,5 +44,6 @@ bool is_tdx_vm(void); int tdx_pre_create_vcpu(CPUState *cpu, Error **errp); void tdx_set_tdvf_region(MemoryRegion *tdvf_mr); +int tdx_parse_tdvf(void *flash_ptr, int size); #endif /* QEMU_I386_TDX_H */ From 49b1f0f812372129736c1df0421c8f67d86d362b Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:25 -0400 Subject: [PATCH 1236/2760] i386/tdx: Don't initialize pc.rom for TDX VMs For TDX, the address below 1MB are entirely general RAM. No need to initialize pc.rom memory region for TDs. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-20-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 70656157ca..a403987a64 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -44,6 +44,7 @@ #include "system/xen.h" #include "system/reset.h" #include "kvm/kvm_i386.h" +#include "kvm/tdx.h" #include "hw/xen/xen.h" #include "qobject/qlist.h" #include "qemu/error-report.h" @@ -976,21 +977,23 @@ void pc_memory_init(PCMachineState *pcms, /* Initialize PC system firmware */ pc_system_firmware_init(pcms, rom_memory); - option_rom_mr = g_malloc(sizeof(*option_rom_mr)); - if (machine_require_guest_memfd(machine)) { - memory_region_init_ram_guest_memfd(option_rom_mr, NULL, "pc.rom", - PC_ROM_SIZE, &error_fatal); - } else { - memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE, - &error_fatal); - if (pcmc->pci_enabled) { - memory_region_set_readonly(option_rom_mr, true); + if (!is_tdx_vm()) { + option_rom_mr = g_malloc(sizeof(*option_rom_mr)); + if (machine_require_guest_memfd(machine)) { + memory_region_init_ram_guest_memfd(option_rom_mr, NULL, "pc.rom", + PC_ROM_SIZE, &error_fatal); + } else { + memory_region_init_ram(option_rom_mr, NULL, "pc.rom", PC_ROM_SIZE, + &error_fatal); + if (pcmc->pci_enabled) { + memory_region_set_readonly(option_rom_mr, true); + } } + memory_region_add_subregion_overlap(rom_memory, + PC_ROM_MIN_VGA, + option_rom_mr, + 1); } - memory_region_add_subregion_overlap(rom_memory, - PC_ROM_MIN_VGA, - option_rom_mr, - 1); fw_cfg = fw_cfg_arch_create(machine, x86ms->boot_cpus, x86ms->apic_id_limit); From 4420ba0ebbf014acc68f78669e0767e288313ed6 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:26 -0400 Subject: [PATCH 1237/2760] i386/tdx: Track mem_ptr for each firmware entry of TDVF For each TDVF sections, QEMU needs to copy the content to guest private memory via KVM API (KVM_TDX_INIT_MEM_REGION). Introduce a field @mem_ptr for TdxFirmwareEntry to track the memory pointer of each TDVF sections. So that QEMU can add/copy them to guest private memory later. TDVF sections can be classified into two groups: - Firmware itself, e.g., TDVF BFV and CFV, that located separately from guest RAM. Its memory pointer is the bios pointer. - Sections located at guest RAM, e.g., TEMP_MEM and TD_HOB. mmap a new memory range for them. Register a machine_init_done callback to do the stuff. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-21-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/tdvf.c | 1 + include/hw/i386/tdvf.h | 7 +++++++ target/i386/kvm/tdx.c | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/hw/i386/tdvf.c b/hw/i386/tdvf.c index e2d486946a..bd993ea2f0 100644 --- a/hw/i386/tdvf.c +++ b/hw/i386/tdvf.c @@ -179,6 +179,7 @@ int tdvf_parse_metadata(TdxFirmware *fw, void *flash_ptr, int size) } } + fw->mem_ptr = flash_ptr; return 0; err: diff --git a/include/hw/i386/tdvf.h b/include/hw/i386/tdvf.h index 7ebcac42a3..e75c8d1acc 100644 --- a/include/hw/i386/tdvf.h +++ b/include/hw/i386/tdvf.h @@ -26,13 +26,20 @@ typedef struct TdxFirmwareEntry { uint64_t size; uint32_t type; uint32_t attributes; + + void *mem_ptr; } TdxFirmwareEntry; typedef struct TdxFirmware { + void *mem_ptr; + uint32_t nr_entries; TdxFirmwareEntry *entries; } TdxFirmware; +#define for_each_tdx_fw_entry(fw, e) \ + for (e = (fw)->entries; e != (fw)->entries + (fw)->nr_entries; e++) + int tdvf_parse_metadata(TdxFirmware *fw, void *flash_ptr, int size); #endif /* HW_I386_TDVF_H */ diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 18beba2f5c..bfdae4f1c0 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -12,10 +12,13 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/base64.h" +#include "qemu/mmap-alloc.h" #include "qapi/error.h" #include "qom/object_interfaces.h" #include "crypto/hash.h" +#include "system/system.h" +#include "hw/i386/tdvf.h" #include "hw/i386/x86.h" #include "kvm_i386.h" #include "tdx.h" @@ -143,6 +146,38 @@ void tdx_set_tdvf_region(MemoryRegion *tdvf_mr) tdx_guest->tdvf_mr = tdvf_mr; } +static void tdx_finalize_vm(Notifier *notifier, void *unused) +{ + TdxFirmware *tdvf = &tdx_guest->tdvf; + TdxFirmwareEntry *entry; + + for_each_tdx_fw_entry(tdvf, entry) { + switch (entry->type) { + case TDVF_SECTION_TYPE_BFV: + case TDVF_SECTION_TYPE_CFV: + entry->mem_ptr = tdvf->mem_ptr + entry->data_offset; + break; + case TDVF_SECTION_TYPE_TD_HOB: + case TDVF_SECTION_TYPE_TEMP_MEM: + entry->mem_ptr = qemu_ram_mmap(-1, entry->size, + qemu_real_host_page_size(), 0, 0); + if (entry->mem_ptr == MAP_FAILED) { + error_report("Failed to mmap memory for TDVF section %d", + entry->type); + exit(1); + } + break; + default: + error_report("Unsupported TDVF section %d", entry->type); + exit(1); + } + } +} + +static Notifier tdx_machine_done_notify = { + .notify = tdx_finalize_vm, +}; + static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { TdxGuest *tdx = TDX_GUEST(cgs); @@ -157,6 +192,8 @@ static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) } } + qemu_add_machine_init_done_notifier(&tdx_machine_done_notify); + tdx_guest = tdx; return 0; } From f18672e4cf91feed4b91ef85a264a500935a2865 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:27 -0400 Subject: [PATCH 1238/2760] i386/tdx: Track RAM entries for TDX VM The RAM of TDX VM can be classified into two types: - TDX_RAM_UNACCEPTED: default type of TDX memory, which needs to be accepted by TDX guest before it can be used and will be all-zeros after being accepted. - TDX_RAM_ADDED: the RAM that is ADD'ed to TD guest before running, and can be used directly. E.g., TD HOB and TEMP MEM that needed by TDVF. Maintain TdxRamEntries[] which grabs the initial RAM info from e820 table and mark each RAM range as default type TDX_RAM_UNACCEPTED. Then turn the range of TD HOB and TEMP MEM to TDX_RAM_ADDED since these ranges will be ADD'ed before TD runs and no need to be accepted runtime. The TdxRamEntries[] are later used to setup the memory TD resource HOB that passes memory info from QEMU to TDVF. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-22-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 109 ++++++++++++++++++++++++++++++++++++++++++ target/i386/kvm/tdx.h | 14 ++++++ 2 files changed, 123 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index bfdae4f1c0..e06f5d0bd4 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -18,6 +18,7 @@ #include "crypto/hash.h" #include "system/system.h" +#include "hw/i386/e820_memory_layout.h" #include "hw/i386/tdvf.h" #include "hw/i386/x86.h" #include "kvm_i386.h" @@ -146,11 +147,110 @@ void tdx_set_tdvf_region(MemoryRegion *tdvf_mr) tdx_guest->tdvf_mr = tdvf_mr; } +static void tdx_add_ram_entry(uint64_t address, uint64_t length, + enum TdxRamType type) +{ + uint32_t nr_entries = tdx_guest->nr_ram_entries; + tdx_guest->ram_entries = g_renew(TdxRamEntry, tdx_guest->ram_entries, + nr_entries + 1); + + tdx_guest->ram_entries[nr_entries].address = address; + tdx_guest->ram_entries[nr_entries].length = length; + tdx_guest->ram_entries[nr_entries].type = type; + tdx_guest->nr_ram_entries++; +} + +static int tdx_accept_ram_range(uint64_t address, uint64_t length) +{ + uint64_t head_start, tail_start, head_length, tail_length; + uint64_t tmp_address, tmp_length; + TdxRamEntry *e; + int i = 0; + + do { + if (i == tdx_guest->nr_ram_entries) { + return -1; + } + + e = &tdx_guest->ram_entries[i++]; + } while (address + length <= e->address || address >= e->address + e->length); + + /* + * The to-be-accepted ram range must be fully contained by one + * RAM entry. + */ + if (e->address > address || + e->address + e->length < address + length) { + return -1; + } + + if (e->type == TDX_RAM_ADDED) { + return 0; + } + + tmp_address = e->address; + tmp_length = e->length; + + e->address = address; + e->length = length; + e->type = TDX_RAM_ADDED; + + head_length = address - tmp_address; + if (head_length > 0) { + head_start = tmp_address; + tdx_add_ram_entry(head_start, head_length, TDX_RAM_UNACCEPTED); + } + + tail_start = address + length; + if (tail_start < tmp_address + tmp_length) { + tail_length = tmp_address + tmp_length - tail_start; + tdx_add_ram_entry(tail_start, tail_length, TDX_RAM_UNACCEPTED); + } + + return 0; +} + +static int tdx_ram_entry_compare(const void *lhs_, const void* rhs_) +{ + const TdxRamEntry *lhs = lhs_; + const TdxRamEntry *rhs = rhs_; + + if (lhs->address == rhs->address) { + return 0; + } + if (le64_to_cpu(lhs->address) > le64_to_cpu(rhs->address)) { + return 1; + } + return -1; +} + +static void tdx_init_ram_entries(void) +{ + unsigned i, j, nr_e820_entries; + + nr_e820_entries = e820_get_table(NULL); + tdx_guest->ram_entries = g_new(TdxRamEntry, nr_e820_entries); + + for (i = 0, j = 0; i < nr_e820_entries; i++) { + uint64_t addr, len; + + if (e820_get_entry(i, E820_RAM, &addr, &len)) { + tdx_guest->ram_entries[j].address = addr; + tdx_guest->ram_entries[j].length = len; + tdx_guest->ram_entries[j].type = TDX_RAM_UNACCEPTED; + j++; + } + } + tdx_guest->nr_ram_entries = j; +} + static void tdx_finalize_vm(Notifier *notifier, void *unused) { TdxFirmware *tdvf = &tdx_guest->tdvf; TdxFirmwareEntry *entry; + tdx_init_ram_entries(); + for_each_tdx_fw_entry(tdvf, entry) { switch (entry->type) { case TDVF_SECTION_TYPE_BFV: @@ -166,12 +266,21 @@ static void tdx_finalize_vm(Notifier *notifier, void *unused) entry->type); exit(1); } + if (tdx_accept_ram_range(entry->address, entry->size)) { + error_report("Failed to accept memory for TDVF section %d", + entry->type); + qemu_ram_munmap(-1, entry->mem_ptr, entry->size); + exit(1); + } break; default: error_report("Unsupported TDVF section %d", entry->type); exit(1); } } + + qsort(tdx_guest->ram_entries, tdx_guest->nr_ram_entries, + sizeof(TdxRamEntry), &tdx_ram_entry_compare); } static Notifier tdx_machine_done_notify = { diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index 28a03c2a7b..36a7400e74 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -20,6 +20,17 @@ typedef struct TdxGuestClass { /* TDX requires bus frequency 25MHz */ #define TDX_APIC_BUS_CYCLES_NS 40 +enum TdxRamType { + TDX_RAM_UNACCEPTED, + TDX_RAM_ADDED, +}; + +typedef struct TdxRamEntry { + uint64_t address; + uint64_t length; + enum TdxRamType type; +} TdxRamEntry; + typedef struct TdxGuest { X86ConfidentialGuest parent_obj; @@ -34,6 +45,9 @@ typedef struct TdxGuest { MemoryRegion *tdvf_mr; TdxFirmware tdvf; + + uint32_t nr_ram_entries; + TdxRamEntry *ram_entries; } TdxGuest; #ifdef CONFIG_TDX From 88aa6576e4ab40b538f543852128cb17fce37f87 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:28 -0400 Subject: [PATCH 1239/2760] headers: Add definitions from UEFI spec for volumes, resources, etc... Add UEFI definitions for literals, enums, structs, GUIDs, etc... that will be used by TDX to build the UEFI Hand-Off Block (HOB) that is passed to the Trusted Domain Virtual Firmware (TDVF). All values come from the UEFI specification [1], PI spec [2] and TDVF design guide[3]. [1] UEFI Specification v2.1.0 https://uefi.org/sites/default/files/resources/UEFI_Spec_2_10_Aug29.pdf [2] UEFI PI spec v1.8 https://uefi.org/sites/default/files/resources/UEFI_PI_Spec_1_8_March3.pdf [3] https://software.intel.com/content/dam/develop/external/us/en/documents/tdx-virtual-firmware-design-guide-rev-1.pdf Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-23-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- include/standard-headers/uefi/uefi.h | 187 +++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 include/standard-headers/uefi/uefi.h diff --git a/include/standard-headers/uefi/uefi.h b/include/standard-headers/uefi/uefi.h new file mode 100644 index 0000000000..5256349ec0 --- /dev/null +++ b/include/standard-headers/uefi/uefi.h @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2025 Intel Corporation + * + * Author: Isaku Yamahata + * + * Xiaoyao Li + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_I386_UEFI_H +#define HW_I386_UEFI_H + +/***************************************************************************/ +/* + * basic EFI definitions + * supplemented with UEFI Specification Version 2.8 (Errata A) + * released February 2020 + */ +/* UEFI integer is little endian */ + +typedef struct { + uint32_t Data1; + uint16_t Data2; + uint16_t Data3; + uint8_t Data4[8]; +} EFI_GUID; + +typedef enum { + EfiReservedMemoryType, + EfiLoaderCode, + EfiLoaderData, + EfiBootServicesCode, + EfiBootServicesData, + EfiRuntimeServicesCode, + EfiRuntimeServicesData, + EfiConventionalMemory, + EfiUnusableMemory, + EfiACPIReclaimMemory, + EfiACPIMemoryNVS, + EfiMemoryMappedIO, + EfiMemoryMappedIOPortSpace, + EfiPalCode, + EfiPersistentMemory, + EfiUnacceptedMemoryType, + EfiMaxMemoryType +} EFI_MEMORY_TYPE; + +#define EFI_HOB_HANDOFF_TABLE_VERSION 0x0009 + +#define EFI_HOB_TYPE_HANDOFF 0x0001 +#define EFI_HOB_TYPE_MEMORY_ALLOCATION 0x0002 +#define EFI_HOB_TYPE_RESOURCE_DESCRIPTOR 0x0003 +#define EFI_HOB_TYPE_GUID_EXTENSION 0x0004 +#define EFI_HOB_TYPE_FV 0x0005 +#define EFI_HOB_TYPE_CPU 0x0006 +#define EFI_HOB_TYPE_MEMORY_POOL 0x0007 +#define EFI_HOB_TYPE_FV2 0x0009 +#define EFI_HOB_TYPE_LOAD_PEIM_UNUSED 0x000A +#define EFI_HOB_TYPE_UEFI_CAPSULE 0x000B +#define EFI_HOB_TYPE_FV3 0x000C +#define EFI_HOB_TYPE_UNUSED 0xFFFE +#define EFI_HOB_TYPE_END_OF_HOB_LIST 0xFFFF + +typedef struct { + uint16_t HobType; + uint16_t HobLength; + uint32_t Reserved; +} EFI_HOB_GENERIC_HEADER; + +typedef uint64_t EFI_PHYSICAL_ADDRESS; +typedef uint32_t EFI_BOOT_MODE; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + uint32_t Version; + EFI_BOOT_MODE BootMode; + EFI_PHYSICAL_ADDRESS EfiMemoryTop; + EFI_PHYSICAL_ADDRESS EfiMemoryBottom; + EFI_PHYSICAL_ADDRESS EfiFreeMemoryTop; + EFI_PHYSICAL_ADDRESS EfiFreeMemoryBottom; + EFI_PHYSICAL_ADDRESS EfiEndOfHobList; +} EFI_HOB_HANDOFF_INFO_TABLE; + +#define EFI_RESOURCE_SYSTEM_MEMORY 0x00000000 +#define EFI_RESOURCE_MEMORY_MAPPED_IO 0x00000001 +#define EFI_RESOURCE_IO 0x00000002 +#define EFI_RESOURCE_FIRMWARE_DEVICE 0x00000003 +#define EFI_RESOURCE_MEMORY_MAPPED_IO_PORT 0x00000004 +#define EFI_RESOURCE_MEMORY_RESERVED 0x00000005 +#define EFI_RESOURCE_IO_RESERVED 0x00000006 +#define EFI_RESOURCE_MEMORY_UNACCEPTED 0x00000007 +#define EFI_RESOURCE_MAX_MEMORY_TYPE 0x00000008 + +#define EFI_RESOURCE_ATTRIBUTE_PRESENT 0x00000001 +#define EFI_RESOURCE_ATTRIBUTE_INITIALIZED 0x00000002 +#define EFI_RESOURCE_ATTRIBUTE_TESTED 0x00000004 +#define EFI_RESOURCE_ATTRIBUTE_SINGLE_BIT_ECC 0x00000008 +#define EFI_RESOURCE_ATTRIBUTE_MULTIPLE_BIT_ECC 0x00000010 +#define EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_1 0x00000020 +#define EFI_RESOURCE_ATTRIBUTE_ECC_RESERVED_2 0x00000040 +#define EFI_RESOURCE_ATTRIBUTE_READ_PROTECTED 0x00000080 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTED 0x00000100 +#define EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTED 0x00000200 +#define EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE 0x00000400 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_COMBINEABLE 0x00000800 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_THROUGH_CACHEABLE 0x00001000 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_BACK_CACHEABLE 0x00002000 +#define EFI_RESOURCE_ATTRIBUTE_16_BIT_IO 0x00004000 +#define EFI_RESOURCE_ATTRIBUTE_32_BIT_IO 0x00008000 +#define EFI_RESOURCE_ATTRIBUTE_64_BIT_IO 0x00010000 +#define EFI_RESOURCE_ATTRIBUTE_UNCACHED_EXPORTED 0x00020000 +#define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTED 0x00040000 +#define EFI_RESOURCE_ATTRIBUTE_READ_ONLY_PROTECTABLE 0x00080000 +#define EFI_RESOURCE_ATTRIBUTE_READ_PROTECTABLE 0x00100000 +#define EFI_RESOURCE_ATTRIBUTE_WRITE_PROTECTABLE 0x00200000 +#define EFI_RESOURCE_ATTRIBUTE_EXECUTION_PROTECTABLE 0x00400000 +#define EFI_RESOURCE_ATTRIBUTE_PERSISTENT 0x00800000 +#define EFI_RESOURCE_ATTRIBUTE_PERSISTABLE 0x01000000 +#define EFI_RESOURCE_ATTRIBUTE_MORE_RELIABLE 0x02000000 + +typedef uint32_t EFI_RESOURCE_TYPE; +typedef uint32_t EFI_RESOURCE_ATTRIBUTE_TYPE; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_GUID Owner; + EFI_RESOURCE_TYPE ResourceType; + EFI_RESOURCE_ATTRIBUTE_TYPE ResourceAttribute; + EFI_PHYSICAL_ADDRESS PhysicalStart; + uint64_t ResourceLength; +} EFI_HOB_RESOURCE_DESCRIPTOR; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_GUID Name; + + /* guid specific data follows */ +} EFI_HOB_GUID_TYPE; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_PHYSICAL_ADDRESS BaseAddress; + uint64_t Length; +} EFI_HOB_FIRMWARE_VOLUME; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_PHYSICAL_ADDRESS BaseAddress; + uint64_t Length; + EFI_GUID FvName; + EFI_GUID FileName; +} EFI_HOB_FIRMWARE_VOLUME2; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + EFI_PHYSICAL_ADDRESS BaseAddress; + uint64_t Length; + uint32_t AuthenticationStatus; + bool ExtractedFv; + EFI_GUID FvName; + EFI_GUID FileName; +} EFI_HOB_FIRMWARE_VOLUME3; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + uint8_t SizeOfMemorySpace; + uint8_t SizeOfIoSpace; + uint8_t Reserved[6]; +} EFI_HOB_CPU; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; +} EFI_HOB_MEMORY_POOL; + +typedef struct { + EFI_HOB_GENERIC_HEADER Header; + + EFI_PHYSICAL_ADDRESS BaseAddress; + uint64_t Length; +} EFI_HOB_UEFI_CAPSULE; + +#define EFI_HOB_OWNER_ZERO \ + ((EFI_GUID){ 0x00000000, 0x0000, 0x0000, \ + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } }) + +#endif From a731425980a4d3f8bb96fc41893b6437672875ee Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:29 -0400 Subject: [PATCH 1240/2760] i386/tdx: Setup the TD HOB list The TD HOB list is used to pass the information from VMM to TDVF. The TD HOB must include PHIT HOB and Resource Descriptor HOB. More details can be found in TDVF specification and PI specification. Build the TD HOB in TDX's machine_init_done callback. Co-developed-by: Isaku Yamahata Signed-off-by: Isaku Yamahata Co-developed-by: Sean Christopherson Signed-off-by: Sean Christopherson Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-24-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/meson.build | 2 +- hw/i386/tdvf-hob.c | 130 ++++++++++++++++++++++++++++++++++++++++++ hw/i386/tdvf-hob.h | 26 +++++++++ target/i386/kvm/tdx.c | 16 ++++++ 4 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 hw/i386/tdvf-hob.c create mode 100644 hw/i386/tdvf-hob.h diff --git a/hw/i386/meson.build b/hw/i386/meson.build index 3bc1da2b6e..7896f348cf 100644 --- a/hw/i386/meson.build +++ b/hw/i386/meson.build @@ -32,7 +32,7 @@ i386_ss.add(when: 'CONFIG_PC', if_true: files( 'port92.c')) i386_ss.add(when: 'CONFIG_X86_FW_OVMF', if_true: files('pc_sysfw_ovmf.c'), if_false: files('pc_sysfw_ovmf-stubs.c')) -i386_ss.add(when: 'CONFIG_TDX', if_true: files('tdvf.c')) +i386_ss.add(when: 'CONFIG_TDX', if_true: files('tdvf.c', 'tdvf-hob.c')) subdir('kvm') subdir('xen') diff --git a/hw/i386/tdvf-hob.c b/hw/i386/tdvf-hob.c new file mode 100644 index 0000000000..782b3d1578 --- /dev/null +++ b/hw/i386/tdvf-hob.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2025 Intel Corporation + * Author: Isaku Yamahata + * + * Xiaoyao Li + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "standard-headers/uefi/uefi.h" +#include "hw/pci/pcie_host.h" +#include "tdvf-hob.h" + +typedef struct TdvfHob { + hwaddr hob_addr; + void *ptr; + int size; + + /* working area */ + void *current; + void *end; +} TdvfHob; + +static uint64_t tdvf_current_guest_addr(const TdvfHob *hob) +{ + return hob->hob_addr + (hob->current - hob->ptr); +} + +static void tdvf_align(TdvfHob *hob, size_t align) +{ + hob->current = QEMU_ALIGN_PTR_UP(hob->current, align); +} + +static void *tdvf_get_area(TdvfHob *hob, uint64_t size) +{ + void *ret; + + if (hob->current + size > hob->end) { + error_report("TD_HOB overrun, size = 0x%" PRIx64, size); + exit(1); + } + + ret = hob->current; + hob->current += size; + tdvf_align(hob, 8); + return ret; +} + +static void tdvf_hob_add_memory_resources(TdxGuest *tdx, TdvfHob *hob) +{ + EFI_HOB_RESOURCE_DESCRIPTOR *region; + EFI_RESOURCE_ATTRIBUTE_TYPE attr; + EFI_RESOURCE_TYPE resource_type; + + TdxRamEntry *e; + int i; + + for (i = 0; i < tdx->nr_ram_entries; i++) { + e = &tdx->ram_entries[i]; + + if (e->type == TDX_RAM_UNACCEPTED) { + resource_type = EFI_RESOURCE_MEMORY_UNACCEPTED; + attr = EFI_RESOURCE_ATTRIBUTE_TDVF_UNACCEPTED; + } else if (e->type == TDX_RAM_ADDED) { + resource_type = EFI_RESOURCE_SYSTEM_MEMORY; + attr = EFI_RESOURCE_ATTRIBUTE_TDVF_PRIVATE; + } else { + error_report("unknown TDX_RAM_ENTRY type %d", e->type); + exit(1); + } + + region = tdvf_get_area(hob, sizeof(*region)); + *region = (EFI_HOB_RESOURCE_DESCRIPTOR) { + .Header = { + .HobType = EFI_HOB_TYPE_RESOURCE_DESCRIPTOR, + .HobLength = cpu_to_le16(sizeof(*region)), + .Reserved = cpu_to_le32(0), + }, + .Owner = EFI_HOB_OWNER_ZERO, + .ResourceType = cpu_to_le32(resource_type), + .ResourceAttribute = cpu_to_le32(attr), + .PhysicalStart = cpu_to_le64(e->address), + .ResourceLength = cpu_to_le64(e->length), + }; + } +} + +void tdvf_hob_create(TdxGuest *tdx, TdxFirmwareEntry *td_hob) +{ + TdvfHob hob = { + .hob_addr = td_hob->address, + .size = td_hob->size, + .ptr = td_hob->mem_ptr, + + .current = td_hob->mem_ptr, + .end = td_hob->mem_ptr + td_hob->size, + }; + + EFI_HOB_GENERIC_HEADER *last_hob; + EFI_HOB_HANDOFF_INFO_TABLE *hit; + + /* Note, Efi{Free}Memory{Bottom,Top} are ignored, leave 'em zeroed. */ + hit = tdvf_get_area(&hob, sizeof(*hit)); + *hit = (EFI_HOB_HANDOFF_INFO_TABLE) { + .Header = { + .HobType = EFI_HOB_TYPE_HANDOFF, + .HobLength = cpu_to_le16(sizeof(*hit)), + .Reserved = cpu_to_le32(0), + }, + .Version = cpu_to_le32(EFI_HOB_HANDOFF_TABLE_VERSION), + .BootMode = cpu_to_le32(0), + .EfiMemoryTop = cpu_to_le64(0), + .EfiMemoryBottom = cpu_to_le64(0), + .EfiFreeMemoryTop = cpu_to_le64(0), + .EfiFreeMemoryBottom = cpu_to_le64(0), + .EfiEndOfHobList = cpu_to_le64(0), /* initialized later */ + }; + + tdvf_hob_add_memory_resources(tdx, &hob); + + last_hob = tdvf_get_area(&hob, sizeof(*last_hob)); + *last_hob = (EFI_HOB_GENERIC_HEADER) { + .HobType = EFI_HOB_TYPE_END_OF_HOB_LIST, + .HobLength = cpu_to_le16(sizeof(*last_hob)), + .Reserved = cpu_to_le32(0), + }; + hit->EfiEndOfHobList = tdvf_current_guest_addr(&hob); +} diff --git a/hw/i386/tdvf-hob.h b/hw/i386/tdvf-hob.h new file mode 100644 index 0000000000..4fc6a3740a --- /dev/null +++ b/hw/i386/tdvf-hob.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef HW_I386_TD_HOB_H +#define HW_I386_TD_HOB_H + +#include "hw/i386/tdvf.h" +#include "target/i386/kvm/tdx.h" + +void tdvf_hob_create(TdxGuest *tdx, TdxFirmwareEntry *td_hob); + +#define EFI_RESOURCE_ATTRIBUTE_TDVF_PRIVATE \ + (EFI_RESOURCE_ATTRIBUTE_PRESENT | \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \ + EFI_RESOURCE_ATTRIBUTE_TESTED) + +#define EFI_RESOURCE_ATTRIBUTE_TDVF_UNACCEPTED \ + (EFI_RESOURCE_ATTRIBUTE_PRESENT | \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \ + EFI_RESOURCE_ATTRIBUTE_TESTED) + +#define EFI_RESOURCE_ATTRIBUTE_TDVF_MMIO \ + (EFI_RESOURCE_ATTRIBUTE_PRESENT | \ + EFI_RESOURCE_ATTRIBUTE_INITIALIZED | \ + EFI_RESOURCE_ATTRIBUTE_UNCACHEABLE) + +#endif diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index e06f5d0bd4..e20ffee955 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -21,6 +21,7 @@ #include "hw/i386/e820_memory_layout.h" #include "hw/i386/tdvf.h" #include "hw/i386/x86.h" +#include "hw/i386/tdvf-hob.h" #include "kvm_i386.h" #include "tdx.h" @@ -147,6 +148,19 @@ void tdx_set_tdvf_region(MemoryRegion *tdvf_mr) tdx_guest->tdvf_mr = tdvf_mr; } +static TdxFirmwareEntry *tdx_get_hob_entry(TdxGuest *tdx) +{ + TdxFirmwareEntry *entry; + + for_each_tdx_fw_entry(&tdx->tdvf, entry) { + if (entry->type == TDVF_SECTION_TYPE_TD_HOB) { + return entry; + } + } + error_report("TDVF metadata doesn't specify TD_HOB location."); + exit(1); +} + static void tdx_add_ram_entry(uint64_t address, uint64_t length, enum TdxRamType type) { @@ -281,6 +295,8 @@ static void tdx_finalize_vm(Notifier *notifier, void *unused) qsort(tdx_guest->ram_entries, tdx_guest->nr_ram_entries, sizeof(TdxRamEntry), &tdx_ram_entry_compare); + + tdvf_hob_create(tdx_guest, tdx_get_hob_entry(tdx_guest)); } static Notifier tdx_machine_done_notify = { From ebc2d2b497c59414ac3c91de32bc546d27940e74 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Thu, 8 May 2025 10:59:30 -0400 Subject: [PATCH 1241/2760] i386/tdx: Add TDVF memory via KVM_TDX_INIT_MEM_REGION TDVF firmware (CODE and VARS) needs to be copied to TD's private memory via KVM_TDX_INIT_MEM_REGION, as well as TD HOB and TEMP memory. If the TDVF section has TDVF_SECTION_ATTRIBUTES_MR_EXTEND set in the flag, calling KVM_TDX_EXTEND_MEMORY to extend the measurement. After populating the TDVF memory, the original image located in shared ramblock can be discarded. Signed-off-by: Isaku Yamahata Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-25-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index e20ffee955..43529a9e0e 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -17,6 +17,7 @@ #include "qom/object_interfaces.h" #include "crypto/hash.h" #include "system/system.h" +#include "system/ramblock.h" #include "hw/i386/e820_memory_layout.h" #include "hw/i386/tdvf.h" @@ -262,6 +263,9 @@ static void tdx_finalize_vm(Notifier *notifier, void *unused) { TdxFirmware *tdvf = &tdx_guest->tdvf; TdxFirmwareEntry *entry; + RAMBlock *ram_block; + Error *local_err = NULL; + int r; tdx_init_ram_entries(); @@ -297,6 +301,44 @@ static void tdx_finalize_vm(Notifier *notifier, void *unused) sizeof(TdxRamEntry), &tdx_ram_entry_compare); tdvf_hob_create(tdx_guest, tdx_get_hob_entry(tdx_guest)); + + for_each_tdx_fw_entry(tdvf, entry) { + struct kvm_tdx_init_mem_region region; + uint32_t flags; + + region = (struct kvm_tdx_init_mem_region) { + .source_addr = (uint64_t)entry->mem_ptr, + .gpa = entry->address, + .nr_pages = entry->size >> 12, + }; + + flags = entry->attributes & TDVF_SECTION_ATTRIBUTES_MR_EXTEND ? + KVM_TDX_MEASURE_MEMORY_REGION : 0; + + do { + error_free(local_err); + local_err = NULL; + r = tdx_vcpu_ioctl(first_cpu, KVM_TDX_INIT_MEM_REGION, flags, + ®ion, &local_err); + } while (r == -EAGAIN || r == -EINTR); + if (r < 0) { + error_report_err(local_err); + exit(1); + } + + if (entry->type == TDVF_SECTION_TYPE_TD_HOB || + entry->type == TDVF_SECTION_TYPE_TEMP_MEM) { + qemu_ram_munmap(-1, entry->mem_ptr, entry->size); + entry->mem_ptr = NULL; + } + } + + /* + * TDVF image has been copied into private region above via + * KVM_MEMORY_MAPPING. It becomes useless. + */ + ram_block = tdx_guest->tdvf_mr->ram_block; + ram_block_discard_range(ram_block, 0, ram_block->max_length); } static Notifier tdx_machine_done_notify = { From 41f7fd22073561a23229c0479d9d708dee9d3a1e Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:31 -0400 Subject: [PATCH 1242/2760] i386/tdx: Call KVM_TDX_INIT_VCPU to initialize TDX vcpu TDX vcpu needs to be initialized by SEAMCALL(TDH.VP.INIT) and KVM provides vcpu level IOCTL KVM_TDX_INIT_VCPU for it. KVM_TDX_INIT_VCPU needs the address of the HOB as input. Invoke it for each vcpu after HOB list is created. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-26-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 43529a9e0e..99d13bd844 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -259,6 +259,18 @@ static void tdx_init_ram_entries(void) tdx_guest->nr_ram_entries = j; } +static void tdx_post_init_vcpus(void) +{ + TdxFirmwareEntry *hob; + CPUState *cpu; + + hob = tdx_get_hob_entry(tdx_guest); + CPU_FOREACH(cpu) { + tdx_vcpu_ioctl(cpu, KVM_TDX_INIT_VCPU, 0, (void *)hob->address, + &error_fatal); + } +} + static void tdx_finalize_vm(Notifier *notifier, void *unused) { TdxFirmware *tdvf = &tdx_guest->tdvf; @@ -302,6 +314,8 @@ static void tdx_finalize_vm(Notifier *notifier, void *unused) tdvf_hob_create(tdx_guest, tdx_get_hob_entry(tdx_guest)); + tdx_post_init_vcpus(); + for_each_tdx_fw_entry(tdvf, entry) { struct kvm_tdx_init_mem_region region; uint32_t flags; From ae60ff4e9f9e5790f79abf866ec67270c28ca477 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:32 -0400 Subject: [PATCH 1243/2760] i386/tdx: Finalize TDX VM Invoke KVM_TDX_FINALIZE_VM to finalize the TD's measurement and make the TD vCPUs runnable once machine initialization is complete. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-27-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 99d13bd844..287bf7147e 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -353,6 +353,9 @@ static void tdx_finalize_vm(Notifier *notifier, void *unused) */ ram_block = tdx_guest->tdvf_mr->ram_block; ram_block_discard_range(ram_block, 0, ram_block->max_length); + + tdx_vm_ioctl(KVM_TDX_FINALIZE_VM, 0, NULL, &error_fatal); + CONFIDENTIAL_GUEST_SUPPORT(tdx_guest)->ready = true; } static Notifier tdx_machine_done_notify = { From 1ff5048d74e661943260c33e864c4118acb37ab4 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:33 -0400 Subject: [PATCH 1244/2760] i386/tdx: Enable user exit on KVM_HC_MAP_GPA_RANGE KVM translates TDG.VP.VMCALL to KVM_HC_MAP_GPA_RANGE, and QEMU needs to enable user exit on KVM_HC_MAP_GPA_RANGE in order to handle the memory conversion requested by TD guest. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-28-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 287bf7147e..971f4becfa 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -19,6 +19,8 @@ #include "system/system.h" #include "system/ramblock.h" +#include + #include "hw/i386/e820_memory_layout.h" #include "hw/i386/tdvf.h" #include "hw/i386/x86.h" @@ -376,6 +378,11 @@ static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) } } + /* TDX relies on KVM_HC_MAP_GPA_RANGE to handle TDG.VP.VMCALL */ + if (!kvm_enable_hypercall(BIT_ULL(KVM_HC_MAP_GPA_RANGE))) { + return -EOPNOTSUPP; + } + qemu_add_machine_init_done_notifier(&tdx_machine_done_notify); tdx_guest = tdx; From 98dbfd6849f117de02ac6f513f2a1f95563e60ae Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:34 -0400 Subject: [PATCH 1245/2760] i386/tdx: Handle KVM_SYSTEM_EVENT_TDX_FATAL TD guest can use TDG.VP.VMCALL to request termination. KVM translates such request into KVM_EXIT_SYSTEM_EVENT with type of KVM_SYSTEM_EVENT_TDX_FATAL. Add hanlder for such exit. Parse and print the error message, and terminate the TD guest in the handler. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-29-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 10 +++++++++ target/i386/kvm/tdx-stub.c | 5 +++++ target/i386/kvm/tdx.c | 46 ++++++++++++++++++++++++++++++++++++++ target/i386/kvm/tdx.h | 2 ++ 4 files changed, 63 insertions(+) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index fd1817fc5e..c5c692a034 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -6129,6 +6129,16 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) case KVM_EXIT_HYPERCALL: ret = kvm_handle_hypercall(run); break; + case KVM_EXIT_SYSTEM_EVENT: + switch (run->system_event.type) { + case KVM_SYSTEM_EVENT_TDX_FATAL: + ret = tdx_handle_report_fatal_error(cpu, run); + break; + default: + ret = -1; + break; + } + break; default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; diff --git a/target/i386/kvm/tdx-stub.c b/target/i386/kvm/tdx-stub.c index 7748b6d0a4..720a4ff046 100644 --- a/target/i386/kvm/tdx-stub.c +++ b/target/i386/kvm/tdx-stub.c @@ -13,3 +13,8 @@ int tdx_parse_tdvf(void *flash_ptr, int size) { return -EINVAL; } + +int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) +{ + return -EINVAL; +} diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 971f4becfa..16ad1af3c5 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -615,6 +615,52 @@ int tdx_parse_tdvf(void *flash_ptr, int size) return tdvf_parse_metadata(&tdx_guest->tdvf, flash_ptr, size); } +/* + * Only 8 registers can contain valid ASCII byte stream to form the fatal + * message, and their sequence is: R14, R15, RBX, RDI, RSI, R8, R9, RDX + */ +#define TDX_FATAL_MESSAGE_MAX 64 + +int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) +{ + uint64_t error_code = run->system_event.data[R_R12]; + uint64_t reg_mask = run->system_event.data[R_ECX]; + char *message = NULL; + uint64_t *tmp; + + if (error_code & 0xffff) { + error_report("TDX: REPORT_FATAL_ERROR: invalid error code: 0x%lx", + error_code); + return -1; + } + + if (reg_mask) { + message = g_malloc0(TDX_FATAL_MESSAGE_MAX + 1); + tmp = (uint64_t *)message; + +#define COPY_REG(REG) \ + do { \ + if (reg_mask & BIT_ULL(REG)) { \ + *(tmp++) = run->system_event.data[REG]; \ + } \ + } while (0) + + COPY_REG(R_R14); + COPY_REG(R_R15); + COPY_REG(R_EBX); + COPY_REG(R_EDI); + COPY_REG(R_ESI); + COPY_REG(R_R8); + COPY_REG(R_R9); + COPY_REG(R_EDX); + *((char *)tmp) = '\0'; + } +#undef COPY_REG + + error_report("TD guest reports fatal error. %s", message ? : ""); + return -1; +} + static bool tdx_guest_get_sept_ve_disable(Object *obj, Error **errp) { TdxGuest *tdx = TDX_GUEST(obj); diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index 36a7400e74..04b5afe199 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -8,6 +8,7 @@ #endif #include "confidential-guest.h" +#include "cpu.h" #include "hw/i386/tdvf.h" #define TYPE_TDX_GUEST "tdx-guest" @@ -59,5 +60,6 @@ bool is_tdx_vm(void); int tdx_pre_create_vcpu(CPUState *cpu, Error **errp); void tdx_set_tdvf_region(MemoryRegion *tdvf_mr); int tdx_parse_tdvf(void *flash_ptr, int size); +int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run); #endif /* QEMU_I386_TDX_H */ From 6e250463b08b4028123f201343ee72099ef81e68 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:35 -0400 Subject: [PATCH 1246/2760] i386/tdx: Wire TDX_REPORT_FATAL_ERROR with GuestPanic facility Integrate TDX's TDX_REPORT_FATAL_ERROR into QEMU GuestPanic facility Originated-from: Isaku Yamahata Signed-off-by: Xiaoyao Li Acked-by: Markus Armbruster Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-30-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- qapi/run-state.json | 31 +++++++++++++++++++-- system/runstate.c | 65 +++++++++++++++++++++++++++++++++++++++++++ target/i386/kvm/tdx.c | 25 ++++++++++++++++- 3 files changed, 118 insertions(+), 3 deletions(-) diff --git a/qapi/run-state.json b/qapi/run-state.json index ce95cfa46b..ee11adc508 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -501,10 +501,12 @@ # # @s390: s390 guest panic information type (Since: 2.12) # +# @tdx: tdx guest panic information type (Since: 10.1) +# # Since: 2.9 ## { 'enum': 'GuestPanicInformationType', - 'data': [ 'hyper-v', 's390' ] } + 'data': [ 'hyper-v', 's390', 'tdx' ] } ## # @GuestPanicInformation: @@ -519,7 +521,8 @@ 'base': {'type': 'GuestPanicInformationType'}, 'discriminator': 'type', 'data': {'hyper-v': 'GuestPanicInformationHyperV', - 's390': 'GuestPanicInformationS390'}} + 's390': 'GuestPanicInformationS390', + 'tdx' : 'GuestPanicInformationTdx'}} ## # @GuestPanicInformationHyperV: @@ -598,6 +601,30 @@ 'psw-addr': 'uint64', 'reason': 'S390CrashReason'}} +## +# @GuestPanicInformationTdx: +# +# TDX Guest panic information specific to TDX, as specified in the +# "Guest-Hypervisor Communication Interface (GHCI) Specification", +# section TDG.VP.VMCALL. +# +# @error-code: TD-specific error code +# +# @message: Human-readable error message provided by the guest. Not +# to be trusted. +# +# @gpa: guest-physical address of a page that contains more verbose +# error information, as zero-terminated string. Present when the +# "GPA valid" bit (bit 63) is set in @error-code. +# +# +# Since: 10.1 +## +{'struct': 'GuestPanicInformationTdx', + 'data': {'error-code': 'uint32', + 'message': 'str', + '*gpa': 'uint64'}} + ## # @MEMORY_FAILURE: # diff --git a/system/runstate.c b/system/runstate.c index de74d962bc..38900c935a 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -590,6 +590,58 @@ static void qemu_system_wakeup(void) } } +static char *tdx_parse_panic_message(char *message) +{ + bool printable = false; + char *buf = NULL; + int len = 0, i; + + /* + * Although message is defined as a json string, we shouldn't + * unconditionally treat it as is because the guest generated it and + * it's not necessarily trustable. + */ + if (message) { + /* The caller guarantees the NULL-terminated string. */ + len = strlen(message); + + printable = len > 0; + for (i = 0; i < len; i++) { + if (!(0x20 <= message[i] && message[i] <= 0x7e)) { + printable = false; + break; + } + } + } + + if (len == 0) { + buf = g_malloc(1); + buf[0] = '\0'; + } else { + if (!printable) { + /* 3 = length of "%02x " */ + buf = g_malloc(len * 3); + for (i = 0; i < len; i++) { + if (message[i] == '\0') { + break; + } else { + sprintf(buf + 3 * i, "%02x ", message[i]); + } + } + if (i > 0) { + /* replace the last ' '(space) to NULL */ + buf[i * 3 - 1] = '\0'; + } else { + buf[0] = '\0'; + } + } else { + buf = g_strdup(message); + } + } + + return buf; +} + void qemu_system_guest_panicked(GuestPanicInformation *info) { qemu_log_mask(LOG_GUEST_ERROR, "Guest crashed"); @@ -631,7 +683,20 @@ void qemu_system_guest_panicked(GuestPanicInformation *info) S390CrashReason_str(info->u.s390.reason), info->u.s390.psw_mask, info->u.s390.psw_addr); + } else if (info->type == GUEST_PANIC_INFORMATION_TYPE_TDX) { + char *message = tdx_parse_panic_message(info->u.tdx.message); + qemu_log_mask(LOG_GUEST_ERROR, + "\nTDX guest reports fatal error." + " error code: 0x%" PRIx32 " error message:\"%s\"\n", + info->u.tdx.error_code, message); + g_free(message); + if (info->u.tdx.gpa != -1ull) { + qemu_log_mask(LOG_GUEST_ERROR, "Additional error information " + "can be found at gpa page: 0x%" PRIx64 "\n", + info->u.tdx.gpa); + } } + qapi_free_GuestPanicInformation(info); } } diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 16ad1af3c5..ae3740a230 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -16,6 +16,7 @@ #include "qapi/error.h" #include "qom/object_interfaces.h" #include "crypto/hash.h" +#include "system/runstate.h" #include "system/system.h" #include "system/ramblock.h" @@ -615,18 +616,35 @@ int tdx_parse_tdvf(void *flash_ptr, int size) return tdvf_parse_metadata(&tdx_guest->tdvf, flash_ptr, size); } +static void tdx_panicked_on_fatal_error(X86CPU *cpu, uint64_t error_code, + char *message, uint64_t gpa) +{ + GuestPanicInformation *panic_info; + + panic_info = g_new0(GuestPanicInformation, 1); + panic_info->type = GUEST_PANIC_INFORMATION_TYPE_TDX; + panic_info->u.tdx.error_code = (uint32_t) error_code; + panic_info->u.tdx.message = message; + panic_info->u.tdx.gpa = gpa; + + qemu_system_guest_panicked(panic_info); +} + /* * Only 8 registers can contain valid ASCII byte stream to form the fatal * message, and their sequence is: R14, R15, RBX, RDI, RSI, R8, R9, RDX */ #define TDX_FATAL_MESSAGE_MAX 64 +#define TDX_REPORT_FATAL_ERROR_GPA_VALID BIT_ULL(63) + int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) { uint64_t error_code = run->system_event.data[R_R12]; uint64_t reg_mask = run->system_event.data[R_ECX]; char *message = NULL; uint64_t *tmp; + uint64_t gpa = -1ull; if (error_code & 0xffff) { error_report("TDX: REPORT_FATAL_ERROR: invalid error code: 0x%lx", @@ -657,7 +675,12 @@ int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) } #undef COPY_REG - error_report("TD guest reports fatal error. %s", message ? : ""); + if (error_code & TDX_REPORT_FATAL_ERROR_GPA_VALID) { + gpa = run->system_event.data[R_R13]; + } + + tdx_panicked_on_fatal_error(cpu, error_code, message, gpa); + return -1; } From 77b5403a0298a5460554f768a2098fd21588e555 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:36 -0400 Subject: [PATCH 1247/2760] kvm: Check KVM_CAP_MAX_VCPUS at vm level KVM with TDX support starts to report different KVM_CAP_MAX_VCPUS per different VM types. So switch to check the KVM_CAP_MAX_VCPUS at vm level. KVM still returns the global KVM_CAP_MAX_VCPUS when the KVM is old that doesn't report different value at vm level. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-31-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 42d239cf8f..71e6060458 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2431,7 +2431,7 @@ static int kvm_recommended_vcpus(KVMState *s) static int kvm_max_vcpus(KVMState *s) { - int ret = kvm_check_extension(s, KVM_CAP_MAX_VCPUS); + int ret = kvm_vm_check_extension(s, KVM_CAP_MAX_VCPUS); return (ret) ? ret : kvm_recommended_vcpus(s); } From 8583c53e2b619b1b9569d3f2d3f3cb2904a573ad Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:37 -0400 Subject: [PATCH 1248/2760] i386/cpu: introduce x86_confidential_guest_cpu_instance_init() To allow execute confidential guest specific cpu init operations. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-32-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/confidential-guest.h | 11 +++++++++++ target/i386/cpu.c | 8 ++++++++ 2 files changed, 19 insertions(+) diff --git a/target/i386/confidential-guest.h b/target/i386/confidential-guest.h index 164be7633a..a86c42a475 100644 --- a/target/i386/confidential-guest.h +++ b/target/i386/confidential-guest.h @@ -39,6 +39,7 @@ struct X86ConfidentialGuestClass { /* */ int (*kvm_type)(X86ConfidentialGuest *cg); + void (*cpu_instance_init)(X86ConfidentialGuest *cg, CPUState *cpu); uint32_t (*mask_cpuid_features)(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index, int reg, uint32_t value); }; @@ -59,6 +60,16 @@ static inline int x86_confidential_guest_kvm_type(X86ConfidentialGuest *cg) } } +static inline void x86_confidential_guest_cpu_instance_init(X86ConfidentialGuest *cg, + CPUState *cpu) +{ + X86ConfidentialGuestClass *klass = X86_CONFIDENTIAL_GUEST_GET_CLASS(cg); + + if (klass->cpu_instance_init) { + klass->cpu_instance_init(cg, cpu); + } +} + /** * x86_confidential_guest_mask_cpuid_features: * diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 9689f6374e..4a7c319bb9 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -37,6 +37,7 @@ #include "hw/i386/topology.h" #include "exec/watchpoint.h" #ifndef CONFIG_USER_ONLY +#include "confidential-guest.h" #include "system/reset.h" #include "qapi/qapi-commands-machine-target.h" #include "system/address-spaces.h" @@ -8543,6 +8544,13 @@ static void x86_cpu_post_initfn(Object *obj) } accel_cpu_instance_init(CPU(obj)); + +#ifndef CONFIG_USER_ONLY + if (current_machine && current_machine->cgs) { + x86_confidential_guest_cpu_instance_init( + X86_CONFIDENTIAL_GUEST(current_machine->cgs), (CPU(obj))); + } +#endif } static void x86_cpu_init_default_topo(X86CPU *cpu) From 7c615242671dbe65e198c20889dcaa9b4b9a1624 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:38 -0400 Subject: [PATCH 1249/2760] i386/tdx: implement tdx_cpu_instance_init() Currently, pmu is not supported for TDX by KVM. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-33-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index ae3740a230..7c5e59c559 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -398,6 +398,11 @@ static int tdx_kvm_type(X86ConfidentialGuest *cg) return KVM_X86_TDX_VM; } +static void tdx_cpu_instance_init(X86ConfidentialGuest *cg, CPUState *cpu) +{ + object_property_set_bool(OBJECT(cpu), "pmu", false, &error_abort); +} + static int tdx_validate_attributes(TdxGuest *tdx, Error **errp) { if ((tdx->attributes & ~tdx_caps->supported_attrs)) { @@ -791,4 +796,5 @@ static void tdx_guest_class_init(ObjectClass *oc, const void *data) klass->kvm_init = tdx_kvm_init; x86_klass->kvm_type = tdx_kvm_type; + x86_klass->cpu_instance_init = tdx_cpu_instance_init; } From ab8bd85adf75900edc2764d0ebe8b53867cc54aa Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:39 -0400 Subject: [PATCH 1250/2760] i386/cpu: Introduce enable_cpuid_0x1f to force exposing CPUID 0x1f Currently, QEMU exposes CPUID 0x1f to guest only when necessary, i.e., when topology level that cannot be enumerated by leaf 0xB, e.g., die or module level, are configured for the guest, e.g., -smp xx,dies=2. However, TDX architecture forces to require CPUID 0x1f to configure CPU topology. Introduce a bool flag, enable_cpuid_0x1f, in CPU for the case that requires CPUID leaf 0x1f to be exposed to guest. Introduce a new function x86_has_cpuid_0x1f(), which is the wrapper of cpu->enable_cpuid_0x1f and x86_has_extended_topo() to check if it needs to enable cpuid leaf 0x1f for the guest. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-34-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 4 ++-- target/i386/cpu.h | 9 +++++++++ target/i386/kvm/kvm.c | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4a7c319bb9..6a97d7549e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7045,7 +7045,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x1F: /* V2 Extended Topology Enumeration Leaf */ - if (!x86_has_extended_topo(env->avail_cpu_topo)) { + if (!x86_has_cpuid_0x1f(cpu)) { *eax = *ebx = *ecx = *edx = 0; break; } @@ -7909,7 +7909,7 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) * cpu->vendor_cpuid_only has been unset for compatibility with older * machine types. */ - if (x86_has_extended_topo(env->avail_cpu_topo) && + if (x86_has_cpuid_0x1f(cpu) && (IS_INTEL_CPU(env) || !cpu->vendor_cpuid_only)) { x86_cpu_adjust_level(cpu, &env->cpuid_min_level, 0x1F); } diff --git a/target/i386/cpu.h b/target/i386/cpu.h index c51e0a43d0..ad0e3d8cdd 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2239,6 +2239,9 @@ struct ArchCPU { /* Compatibility bits for old machine types: */ bool enable_cpuid_0xb; + /* Force to enable cpuid 0x1f */ + bool enable_cpuid_0x1f; + /* Enable auto level-increase for all CPUID leaves */ bool full_cpuid_auto_level; @@ -2500,6 +2503,12 @@ void host_cpuid(uint32_t function, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); bool cpu_has_x2apic_feature(CPUX86State *env); +static inline bool x86_has_cpuid_0x1f(X86CPU *cpu) +{ + return cpu->enable_cpuid_0x1f || + x86_has_extended_topo(cpu->env.avail_cpu_topo); +} + /* helper.c */ void x86_cpu_set_a20(X86CPU *cpu, int a20_state); void cpu_sync_avx_hflag(CPUX86State *env); diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index c5c692a034..ee33797fdd 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -1872,7 +1872,7 @@ uint32_t kvm_x86_build_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries, break; } case 0x1f: - if (!x86_has_extended_topo(env->avail_cpu_topo)) { + if (!x86_has_cpuid_0x1f(env_archcpu(env))) { cpuid_i--; break; } From 9002494f80b751a7655045c5f46bf90bc1d3bbd0 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:40 -0400 Subject: [PATCH 1251/2760] i386/tdx: Force exposing CPUID 0x1f TDX uses CPUID 0x1f to configure TD guest's CPU topology. So set enable_cpuid_0x1f for TDs. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-35-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 7c5e59c559..accaefb401 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -400,7 +400,11 @@ static int tdx_kvm_type(X86ConfidentialGuest *cg) static void tdx_cpu_instance_init(X86ConfidentialGuest *cg, CPUState *cpu) { + X86CPU *x86cpu = X86_CPU(cpu); + object_property_set_bool(OBJECT(cpu), "pmu", false, &error_abort); + + x86cpu->enable_cpuid_0x1f = true; } static int tdx_validate_attributes(TdxGuest *tdx, Error **errp) From da6728658bf63d6a3989f1587a33566b3e54bed8 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:41 -0400 Subject: [PATCH 1252/2760] i386/tdx: Set kvm_readonly_mem_enabled to false for TDX VM TDX only supports readonly for shared memory but not for private memory. In the view of QEMU, it has no idea whether a memslot is used as shared memory of private. Thus just mark kvm_readonly_mem_enabled to false to TDX VM for simplicity. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-36-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index accaefb401..344e560b4b 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -384,6 +384,15 @@ static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) return -EOPNOTSUPP; } + /* + * Set kvm_readonly_mem_allowed to false, because TDX only supports readonly + * memory for shared memory but not for private memory. Besides, whether a + * memslot is private or shared is not determined by QEMU. + * + * Thus, just mark readonly memory not supported for simplicity. + */ + kvm_readonly_mem_allowed = false; + qemu_add_machine_init_done_notifier(&tdx_machine_done_notify); tdx_guest = tdx; From 810d4e83d07ca0d072205453a42c324a51d5a5fa Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:42 -0400 Subject: [PATCH 1253/2760] i386/tdx: Disable SMM for TDX VMs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TDX doesn't support SMM and VMM cannot emulate SMM for TDX VMs because VMM cannot manipulate TDX VM's memory. Disable SMM for TDX VMs and error out if user requests to enable SMM. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-37-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 344e560b4b..87c5bf0496 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -367,11 +367,20 @@ static Notifier tdx_machine_done_notify = { static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { + MachineState *ms = MACHINE(qdev_get_machine()); + X86MachineState *x86ms = X86_MACHINE(ms); TdxGuest *tdx = TDX_GUEST(cgs); int r = 0; kvm_mark_guest_state_protected(); + if (x86ms->smm == ON_OFF_AUTO_AUTO) { + x86ms->smm = ON_OFF_AUTO_OFF; + } else if (x86ms->smm == ON_OFF_AUTO_ON) { + error_setg(errp, "TDX VM doesn't support SMM"); + return -EINVAL; + } + if (!tdx_caps) { r = get_tdx_capabilities(errp); if (r) { From e7ef60892c80a9ce5b8504ceb13a81f4e0d4b3f7 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:43 -0400 Subject: [PATCH 1254/2760] i386/tdx: Disable PIC for TDX VMs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Legacy PIC (8259) cannot be supported for TDX VMs since TDX module doesn't allow directly interrupt injection. Using posted interrupts for the PIC is not a viable option as the guest BIOS/kernel will not do EOI for PIC IRQs, i.e. will leave the vIRR bit set. Hence disable PIC for TDX VMs and error out if user wants PIC. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-38-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 87c5bf0496..32c3f3795d 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -381,6 +381,13 @@ static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) return -EINVAL; } + if (x86ms->pic == ON_OFF_AUTO_AUTO) { + x86ms->pic = ON_OFF_AUTO_OFF; + } else if (x86ms->pic == ON_OFF_AUTO_ON) { + error_setg(errp, "TDX VM doesn't support PIC"); + return -EINVAL; + } + if (!tdx_caps) { r = get_tdx_capabilities(errp); if (r) { From bb45580d842530d78b58179eaf80b6331b15324e Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:44 -0400 Subject: [PATCH 1255/2760] i386/tdx: Set and check kernel_irqchip mode for TDX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM mandates kernel_irqchip to be split mode. Set it to split mode automatically when users don't provide an explicit value, otherwise check it to be the split mode. Suggested-by: Daniel P. Berrangé Signed-off-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-39-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 32c3f3795d..68ed3b9f98 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -16,6 +16,7 @@ #include "qapi/error.h" #include "qom/object_interfaces.h" #include "crypto/hash.h" +#include "system/kvm_int.h" #include "system/runstate.h" #include "system/system.h" #include "system/ramblock.h" @@ -388,6 +389,13 @@ static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) return -EINVAL; } + if (kvm_state->kernel_irqchip_split == ON_OFF_AUTO_AUTO) { + kvm_state->kernel_irqchip_split = ON_OFF_AUTO_ON; + } else if (kvm_state->kernel_irqchip_split != ON_OFF_AUTO_ON) { + error_setg(errp, "TDX VM requires kernel_irqchip to be split"); + return -EINVAL; + } + if (!tdx_caps) { r = get_tdx_capabilities(errp); if (r) { From 0ed55865b49b703af93e160d48935812a7114e07 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Thu, 8 May 2025 10:59:45 -0400 Subject: [PATCH 1256/2760] i386/tdx: Don't synchronize guest tsc for TDs TSC of TDs is not accessible and KVM doesn't allow access of MSR_IA32_TSC for TDs. To avoid the assert() in kvm_get_tsc, make kvm_synchronize_all_tsc() noop for TDs, Signed-off-by: Isaku Yamahata Reviewed-by: Connor Kuehl Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-40-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index ee33797fdd..4fc37cc370 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -328,7 +328,7 @@ void kvm_synchronize_all_tsc(void) { CPUState *cpu; - if (kvm_enabled()) { + if (kvm_enabled() && !is_tdx_vm()) { CPU_FOREACH(cpu) { run_on_cpu(cpu, do_kvm_synchronize_tsc, RUN_ON_CPU_NULL); } From f9aaad3362a5886d78e7d4d50d563ac16c6acdde Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:46 -0400 Subject: [PATCH 1257/2760] i386/tdx: Only configure MSR_IA32_UCODE_REV in kvm_init_msrs() for TDs For TDs, only MSR_IA32_UCODE_REV in kvm_init_msrs() can be configured by VMM, while the features enumerated/controlled by other MSRs except MSR_IA32_UCODE_REV in kvm_init_msrs() are not under control of VMM. Only configure MSR_IA32_UCODE_REV for TDs. Signed-off-by: Xiaoyao Li Acked-by: Gerd Hoffmann Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-41-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 4fc37cc370..90a0dac4a1 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -3864,32 +3864,34 @@ static void kvm_init_msrs(X86CPU *cpu) CPUX86State *env = &cpu->env; kvm_msr_buf_reset(cpu); - if (has_msr_arch_capabs) { - kvm_msr_entry_add(cpu, MSR_IA32_ARCH_CAPABILITIES, - env->features[FEAT_ARCH_CAPABILITIES]); - } - if (has_msr_core_capabs) { - kvm_msr_entry_add(cpu, MSR_IA32_CORE_CAPABILITY, - env->features[FEAT_CORE_CAPABILITY]); - } + if (!is_tdx_vm()) { + if (has_msr_arch_capabs) { + kvm_msr_entry_add(cpu, MSR_IA32_ARCH_CAPABILITIES, + env->features[FEAT_ARCH_CAPABILITIES]); + } + + if (has_msr_core_capabs) { + kvm_msr_entry_add(cpu, MSR_IA32_CORE_CAPABILITY, + env->features[FEAT_CORE_CAPABILITY]); + } + + if (has_msr_perf_capabs && cpu->enable_pmu) { + kvm_msr_entry_add_perf(cpu, env->features); + } - if (has_msr_perf_capabs && cpu->enable_pmu) { - kvm_msr_entry_add_perf(cpu, env->features); + /* + * Older kernels do not include VMX MSRs in KVM_GET_MSR_INDEX_LIST, but + * all kernels with MSR features should have them. + */ + if (kvm_feature_msrs && cpu_has_vmx(env)) { + kvm_msr_entry_add_vmx(cpu, env->features); + } } if (has_msr_ucode_rev) { kvm_msr_entry_add(cpu, MSR_IA32_UCODE_REV, cpu->ucode_rev); } - - /* - * Older kernels do not include VMX MSRs in KVM_GET_MSR_INDEX_LIST, but - * all kernels with MSR features should have them. - */ - if (kvm_feature_msrs && cpu_has_vmx(env)) { - kvm_msr_entry_add_vmx(cpu, env->features); - } - assert(kvm_buf_set_msrs(cpu) == 0); } From 62a1a8b89d90cd3fbee0e6d38e6a4c0d833e978a Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:47 -0400 Subject: [PATCH 1258/2760] i386/apic: Skip kvm_apic_put() for TDX KVM neithers allow writing to MSR_IA32_APICBASE for TDs, nor allow for KVM_SET_LAPIC[*]. Note, KVM_GET_LAPIC is also disallowed for TDX. It is called in the path do_kvm_cpu_synchronize_state() -> kvm_arch_get_registers() -> kvm_get_apic() and it's already disllowed for confidential guest through guest_state_protected. [*] https://lore.kernel.org/all/Z3w4Ku4Jq0CrtXne@google.com/ Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-42-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/kvm/apic.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/i386/kvm/apic.c b/hw/i386/kvm/apic.c index 39035db042..1be9bfe36e 100644 --- a/hw/i386/kvm/apic.c +++ b/hw/i386/kvm/apic.c @@ -17,6 +17,7 @@ #include "system/hw_accel.h" #include "system/kvm.h" #include "kvm/kvm_i386.h" +#include "kvm/tdx.h" static inline void kvm_apic_set_reg(struct kvm_lapic_state *kapic, int reg_id, uint32_t val) @@ -141,6 +142,10 @@ static void kvm_apic_put(CPUState *cs, run_on_cpu_data data) struct kvm_lapic_state kapic; int ret; + if (is_tdx_vm()) { + return; + } + kvm_put_apicbase(s->cpu, s->apicbase); kvm_put_apic_state(s, &kapic); From b4b7fb5a773e1d2215c2aaa99789eca51914b78f Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:48 -0400 Subject: [PATCH 1259/2760] cpu: Don't set vcpu_dirty when guest_state_protected QEMU calls kvm_arch_put_registers() when vcpu_dirty is true in kvm_vcpu_exec(). However, for confidential guest, like TDX, putting registers is disallowed due to guest state is protected. Only set vcpu_dirty to true with guest state is not protected when creating the vcpu. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-43-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 71e6060458..51526d301b 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -471,7 +471,9 @@ int kvm_create_vcpu(CPUState *cpu) cpu->kvm_fd = kvm_fd; cpu->kvm_state = s; - cpu->vcpu_dirty = true; + if (!s->guest_state_protected) { + cpu->vcpu_dirty = true; + } cpu->dirty_pages = 0; cpu->throttle_us_per_full = 0; From 695bfaee7153153708228946aa26c6d879599c04 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:49 -0400 Subject: [PATCH 1260/2760] i386/cgs: Rename *mask_cpuid_features() to *adjust_cpuid_features() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because for TDX case, there are also fixed-1 bits that enforced by TDX module. Signed-off-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-44-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/confidential-guest.h | 20 ++++++++++---------- target/i386/kvm/kvm.c | 2 +- target/i386/sev.c | 4 ++-- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/target/i386/confidential-guest.h b/target/i386/confidential-guest.h index a86c42a475..777d43cc96 100644 --- a/target/i386/confidential-guest.h +++ b/target/i386/confidential-guest.h @@ -40,8 +40,8 @@ struct X86ConfidentialGuestClass { /* */ int (*kvm_type)(X86ConfidentialGuest *cg); void (*cpu_instance_init)(X86ConfidentialGuest *cg, CPUState *cpu); - uint32_t (*mask_cpuid_features)(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index, - int reg, uint32_t value); + uint32_t (*adjust_cpuid_features)(X86ConfidentialGuest *cg, uint32_t feature, + uint32_t index, int reg, uint32_t value); }; /** @@ -71,21 +71,21 @@ static inline void x86_confidential_guest_cpu_instance_init(X86ConfidentialGuest } /** - * x86_confidential_guest_mask_cpuid_features: + * x86_confidential_guest_adjust_cpuid_features: * - * Removes unsupported features from a confidential guest's CPUID values, returns - * the value with the bits removed. The bits removed should be those that KVM - * provides independent of host-supported CPUID features, but are not supported by - * the confidential computing firmware. + * Adjust the supported features from a confidential guest's CPUID values, + * returns the adjusted value. There are bits being removed that are not + * supported by the confidential computing firmware or bits being added that + * are forcibly exposed to guest by the confidential computing firmware. */ -static inline int x86_confidential_guest_mask_cpuid_features(X86ConfidentialGuest *cg, +static inline int x86_confidential_guest_adjust_cpuid_features(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index, int reg, uint32_t value) { X86ConfidentialGuestClass *klass = X86_CONFIDENTIAL_GUEST_GET_CLASS(cg); - if (klass->mask_cpuid_features) { - return klass->mask_cpuid_features(cg, feature, index, reg, value); + if (klass->adjust_cpuid_features) { + return klass->adjust_cpuid_features(cg, feature, index, reg, value); } else { return value; } diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 90a0dac4a1..0d47463431 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -574,7 +574,7 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, } if (current_machine->cgs) { - ret = x86_confidential_guest_mask_cpuid_features( + ret = x86_confidential_guest_adjust_cpuid_features( X86_CONFIDENTIAL_GUEST(current_machine->cgs), function, index, reg, ret); } diff --git a/target/i386/sev.c b/target/i386/sev.c index 7ee700d6a3..8b87b7cdec 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -947,7 +947,7 @@ sev_snp_launch_update(SevSnpGuestState *sev_snp_guest, } static uint32_t -sev_snp_mask_cpuid_features(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index, +sev_snp_adjust_cpuid_features(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index, int reg, uint32_t value) { switch (feature) { @@ -2405,7 +2405,7 @@ sev_snp_guest_class_init(ObjectClass *oc, const void *data) klass->launch_finish = sev_snp_launch_finish; klass->launch_update_data = sev_snp_launch_update_data; klass->kvm_init = sev_snp_kvm_init; - x86_klass->mask_cpuid_features = sev_snp_mask_cpuid_features; + x86_klass->adjust_cpuid_features = sev_snp_adjust_cpuid_features; x86_klass->kvm_type = sev_snp_kvm_type; object_class_property_add(oc, "policy", "uint64", From 75ec6189f5c65cab210dd9f16cf4eef368038d45 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:50 -0400 Subject: [PATCH 1261/2760] i386/tdx: Implement adjust_cpuid_features() for TDX Maintain a TDX specific supported CPUID set, and use it to mask the common supported CPUID value of KVM. It can avoid newly added supported features (reported via KVM_GET_SUPPORTED_CPUID) for common VMs being falsely reported as supported for TDX. As the first step, initialize the TDX supported CPUID set with all the configurable CPUID bits. It's not complete because there are other CPUID bits are supported for TDX but not reported as directly configurable. E.g. the XFAM related bits, attribute related bits and fixed-1 bits. They will be handled in the future. Also, what matters are the CPUID bits related to QEMU's feature word. Only mask the CPUID leafs which are feature word leaf. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-45-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 16 ++++++++++++++++ target/i386/cpu.h | 1 + target/i386/kvm/kvm.c | 2 +- target/i386/kvm/kvm_i386.h | 1 + target/i386/kvm/tdx.c | 34 ++++++++++++++++++++++++++++++++++ 5 files changed, 53 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 6a97d7549e..5aacb62f08 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1678,6 +1678,22 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { }, }; +bool is_feature_word_cpuid(uint32_t feature, uint32_t index, int reg) +{ + FeatureWordInfo *wi; + FeatureWord w; + + for (w = 0; w < FEATURE_WORDS; w++) { + wi = &feature_word_info[w]; + if (wi->type == CPUID_FEATURE_WORD && wi->cpuid.eax == feature && + (!wi->cpuid.needs_ecx || wi->cpuid.ecx == index) && + wi->cpuid.reg == reg) { + return true; + } + } + return false; +} + typedef struct FeatureMask { FeatureWord index; uint64_t mask; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index ad0e3d8cdd..7ffcf91b01 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2502,6 +2502,7 @@ void cpu_set_apic_feature(CPUX86State *env); void host_cpuid(uint32_t function, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); bool cpu_has_x2apic_feature(CPUX86State *env); +bool is_feature_word_cpuid(uint32_t feature, uint32_t index, int reg); static inline bool x86_has_cpuid_0x1f(X86CPU *cpu) { diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 0d47463431..cd87f5502a 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -394,7 +394,7 @@ static bool host_tsx_broken(void) /* Returns the value for a specific register on the cpuid entry */ -static uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg) +uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg) { uint32_t ret = 0; switch (reg) { diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index dc696cb723..484a1de84d 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -62,6 +62,7 @@ void kvm_update_msi_routes_all(void *private, bool global, struct kvm_cpuid_entry2 *cpuid_find_entry(struct kvm_cpuid2 *cpuid, uint32_t function, uint32_t index); +uint32_t cpuid_entry_get_reg(struct kvm_cpuid_entry2 *entry, int reg); uint32_t kvm_x86_build_cpuid(CPUX86State *env, struct kvm_cpuid_entry2 *entries, uint32_t cpuid_i); #endif /* CONFIG_KVM */ diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 68ed3b9f98..e3b7ad6d14 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -45,6 +45,7 @@ static TdxGuest *tdx_guest; static struct kvm_tdx_capabilities *tdx_caps; +static struct kvm_cpuid2 *tdx_supported_cpuid; /* Valid after kvm_arch_init()->confidential_guest_kvm_init()->tdx_kvm_init() */ bool is_tdx_vm(void) @@ -366,6 +367,20 @@ static Notifier tdx_machine_done_notify = { .notify = tdx_finalize_vm, }; +static void tdx_setup_supported_cpuid(void) +{ + if (tdx_supported_cpuid) { + return; + } + + tdx_supported_cpuid = g_malloc0(sizeof(*tdx_supported_cpuid) + + KVM_MAX_CPUID_ENTRIES * sizeof(struct kvm_cpuid_entry2)); + + memcpy(tdx_supported_cpuid->entries, tdx_caps->cpuid.entries, + tdx_caps->cpuid.nent * sizeof(struct kvm_cpuid_entry2)); + tdx_supported_cpuid->nent = tdx_caps->cpuid.nent; +} + static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { MachineState *ms = MACHINE(qdev_get_machine()); @@ -403,6 +418,8 @@ static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) } } + tdx_setup_supported_cpuid(); + /* TDX relies on KVM_HC_MAP_GPA_RANGE to handle TDG.VP.VMCALL */ if (!kvm_enable_hypercall(BIT_ULL(KVM_HC_MAP_GPA_RANGE))) { return -EOPNOTSUPP; @@ -440,6 +457,22 @@ static void tdx_cpu_instance_init(X86ConfidentialGuest *cg, CPUState *cpu) x86cpu->enable_cpuid_0x1f = true; } +static uint32_t tdx_adjust_cpuid_features(X86ConfidentialGuest *cg, + uint32_t feature, uint32_t index, + int reg, uint32_t value) +{ + struct kvm_cpuid_entry2 *e; + + if (is_feature_word_cpuid(feature, index, reg)) { + e = cpuid_find_entry(tdx_supported_cpuid, feature, index); + if (e) { + value &= cpuid_entry_get_reg(e, reg); + } + } + + return value; +} + static int tdx_validate_attributes(TdxGuest *tdx, Error **errp) { if ((tdx->attributes & ~tdx_caps->supported_attrs)) { @@ -834,4 +867,5 @@ static void tdx_guest_class_init(ObjectClass *oc, const void *data) klass->kvm_init = tdx_kvm_init; x86_klass->kvm_type = tdx_kvm_type; x86_klass->cpu_instance_init = tdx_cpu_instance_init; + x86_klass->adjust_cpuid_features = tdx_adjust_cpuid_features; } From 0ba06e46d09b84a2cb97a268da5576aaca3a24ca Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:51 -0400 Subject: [PATCH 1262/2760] i386/tdx: Add TDX fixed1 bits to supported CPUIDs TDX architecture forcibly sets some CPUID bits for TD guest that VMM cannot disable it. They are fixed1 bits. Fixed1 bits are not covered by tdx_caps.cpuid (which only contains the directly configurable bits), while fixed1 bits are supported for TD guest obviously. Add fixed1 bits to tdx_supported_cpuid. Besides, set all the fixed1 bits to the initial set of KVM's support since KVM might not report them as supported. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-46-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 2 + target/i386/kvm/kvm_i386.h | 7 ++ target/i386/kvm/tdx.c | 134 +++++++++++++++++++++++++++++++++++++ target/i386/sev.c | 8 --- 4 files changed, 143 insertions(+), 8 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 7ffcf91b01..342e4f2a57 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -920,6 +920,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_7_0_EDX_FSRM (1U << 4) /* AVX512 Vector Pair Intersection to a Pair of Mask Registers */ #define CPUID_7_0_EDX_AVX512_VP2INTERSECT (1U << 8) + /* "md_clear" VERW clears CPU buffers */ +#define CPUID_7_0_EDX_MD_CLEAR (1U << 10) /* SERIALIZE instruction */ #define CPUID_7_0_EDX_SERIALIZE (1U << 14) /* TSX Suspend Load Address Tracking instruction */ diff --git a/target/i386/kvm/kvm_i386.h b/target/i386/kvm/kvm_i386.h index 484a1de84d..5f83e8850a 100644 --- a/target/i386/kvm/kvm_i386.h +++ b/target/i386/kvm/kvm_i386.h @@ -44,6 +44,13 @@ void kvm_request_xsave_components(X86CPU *cpu, uint64_t mask); #ifdef CONFIG_KVM +#include + +typedef struct KvmCpuidInfo { + struct kvm_cpuid2 cpuid; + struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; +} KvmCpuidInfo; + bool kvm_is_vm_type_supported(int type); bool kvm_has_adjust_clock_stable(void); bool kvm_has_exception_payload(void); diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index e3b7ad6d14..9d92ff1484 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -367,6 +367,133 @@ static Notifier tdx_machine_done_notify = { .notify = tdx_finalize_vm, }; +/* + * Some CPUID bits change from fixed1 to configurable bits when TDX module + * supports TDX_FEATURES0.VE_REDUCTION. e.g., MCA/MCE/MTRR/CORE_CAPABILITY. + * + * To make QEMU work with all the versions of TDX module, keep the fixed1 bits + * here if they are ever fixed1 bits in any of the version though not fixed1 in + * the latest version. Otherwise, with the older version of TDX module, QEMU may + * treat the fixed1 bit as unsupported. + * + * For newer TDX module, it does no harm to keep them in tdx_fixed1_bits even + * though they changed to configurable bits. Because tdx_fixed1_bits is used to + * setup the supported bits. + */ +KvmCpuidInfo tdx_fixed1_bits = { + .cpuid.nent = 8, + .entries[0] = { + .function = 0x1, + .index = 0, + .ecx = CPUID_EXT_SSE3 | CPUID_EXT_PCLMULQDQ | CPUID_EXT_DTES64 | + CPUID_EXT_DSCPL | CPUID_EXT_SSSE3 | CPUID_EXT_CX16 | + CPUID_EXT_PDCM | CPUID_EXT_PCID | CPUID_EXT_SSE41 | + CPUID_EXT_SSE42 | CPUID_EXT_X2APIC | CPUID_EXT_MOVBE | + CPUID_EXT_POPCNT | CPUID_EXT_AES | CPUID_EXT_XSAVE | + CPUID_EXT_RDRAND | CPUID_EXT_HYPERVISOR, + .edx = CPUID_FP87 | CPUID_VME | CPUID_DE | CPUID_PSE | CPUID_TSC | + CPUID_MSR | CPUID_PAE | CPUID_MCE | CPUID_CX8 | CPUID_APIC | + CPUID_SEP | CPUID_MTRR | CPUID_PGE | CPUID_MCA | CPUID_CMOV | + CPUID_PAT | CPUID_CLFLUSH | CPUID_DTS | CPUID_MMX | CPUID_FXSR | + CPUID_SSE | CPUID_SSE2, + }, + .entries[1] = { + .function = 0x6, + .index = 0, + .eax = CPUID_6_EAX_ARAT, + }, + .entries[2] = { + .function = 0x7, + .index = 0, + .flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX, + .ebx = CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_FDP_EXCPTN_ONLY | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_INVPCID | + CPUID_7_0_EBX_ZERO_FCS_FDS | CPUID_7_0_EBX_RDSEED | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_CLFLUSHOPT | + CPUID_7_0_EBX_CLWB | CPUID_7_0_EBX_SHA_NI, + .ecx = CPUID_7_0_ECX_BUS_LOCK_DETECT | CPUID_7_0_ECX_MOVDIRI | + CPUID_7_0_ECX_MOVDIR64B, + .edx = CPUID_7_0_EDX_MD_CLEAR | CPUID_7_0_EDX_SPEC_CTRL | + CPUID_7_0_EDX_STIBP | CPUID_7_0_EDX_FLUSH_L1D | + CPUID_7_0_EDX_ARCH_CAPABILITIES | CPUID_7_0_EDX_CORE_CAPABILITY | + CPUID_7_0_EDX_SPEC_CTRL_SSBD, + }, + .entries[3] = { + .function = 0x7, + .index = 2, + .flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX, + .edx = CPUID_7_2_EDX_PSFD | CPUID_7_2_EDX_IPRED_CTRL | + CPUID_7_2_EDX_RRSBA_CTRL | CPUID_7_2_EDX_BHI_CTRL, + }, + .entries[4] = { + .function = 0xD, + .index = 0, + .flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX, + .eax = XSTATE_FP_MASK | XSTATE_SSE_MASK, + }, + .entries[5] = { + .function = 0xD, + .index = 1, + .flags = KVM_CPUID_FLAG_SIGNIFCANT_INDEX, + .eax = CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC| + CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES, + }, + .entries[6] = { + .function = 0x80000001, + .index = 0, + .ecx = CPUID_EXT3_LAHF_LM | CPUID_EXT3_ABM | CPUID_EXT3_3DNOWPREFETCH, + /* + * Strictly speaking, SYSCALL is not fixed1 bit since it depends on + * the CPU to be in 64-bit mode. But here fixed1 is used to serve the + * purpose of supported bits for TDX. In this sense, SYACALL is always + * supported. + */ + .edx = CPUID_EXT2_SYSCALL | CPUID_EXT2_NX | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_RDTSCP | CPUID_EXT2_LM, + }, + .entries[7] = { + .function = 0x80000007, + .index = 0, + .edx = CPUID_APM_INVTSC, + }, +}; + +static struct kvm_cpuid_entry2 *find_in_supported_entry(uint32_t function, + uint32_t index) +{ + struct kvm_cpuid_entry2 *e; + + e = cpuid_find_entry(tdx_supported_cpuid, function, index); + if (!e) { + if (tdx_supported_cpuid->nent >= KVM_MAX_CPUID_ENTRIES) { + error_report("tdx_supported_cpuid requries more space than %d entries", + KVM_MAX_CPUID_ENTRIES); + exit(1); + } + e = &tdx_supported_cpuid->entries[tdx_supported_cpuid->nent++]; + e->function = function; + e->index = index; + } + + return e; +} + +static void tdx_add_supported_cpuid_by_fixed1_bits(void) +{ + struct kvm_cpuid_entry2 *e, *e1; + int i; + + for (i = 0; i < tdx_fixed1_bits.cpuid.nent; i++) { + e = &tdx_fixed1_bits.entries[i]; + + e1 = find_in_supported_entry(e->function, e->index); + e1->eax |= e->eax; + e1->ebx |= e->ebx; + e1->ecx |= e->ecx; + e1->edx |= e->edx; + } +} + static void tdx_setup_supported_cpuid(void) { if (tdx_supported_cpuid) { @@ -379,6 +506,8 @@ static void tdx_setup_supported_cpuid(void) memcpy(tdx_supported_cpuid->entries, tdx_caps->cpuid.entries, tdx_caps->cpuid.nent * sizeof(struct kvm_cpuid_entry2)); tdx_supported_cpuid->nent = tdx_caps->cpuid.nent; + + tdx_add_supported_cpuid_by_fixed1_bits(); } static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) @@ -463,6 +592,11 @@ static uint32_t tdx_adjust_cpuid_features(X86ConfidentialGuest *cg, { struct kvm_cpuid_entry2 *e; + e = cpuid_find_entry(&tdx_fixed1_bits.cpuid, feature, index); + if (e) { + value |= cpuid_entry_get_reg(e, reg); + } + if (is_feature_word_cpuid(feature, index, reg)) { e = cpuid_find_entry(tdx_supported_cpuid, feature, index); if (e) { diff --git a/target/i386/sev.c b/target/i386/sev.c index 8b87b7cdec..adf787797e 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -212,14 +212,6 @@ static const char *const sev_fw_errlist[] = { #define SEV_FW_MAX_ERROR ARRAY_SIZE(sev_fw_errlist) -/* doesn't expose this, so re-use the max from kvm.c */ -#define KVM_MAX_CPUID_ENTRIES 100 - -typedef struct KvmCpuidInfo { - struct kvm_cpuid2 cpuid; - struct kvm_cpuid_entry2 entries[KVM_MAX_CPUID_ENTRIES]; -} KvmCpuidInfo; - #define SNP_CPUID_FUNCTION_MAXCOUNT 64 #define SNP_CPUID_FUNCTION_UNKNOWN 0xFFFFFFFF From 31df29c532a9ef473c6efd497950a620099bf1da Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:52 -0400 Subject: [PATCH 1263/2760] i386/tdx: Add supported CPUID bits related to TD Attributes For TDX, some CPUID feature bit is configured via TD attributes. They are not covered by tdx_caps.cpuid (which only contians the directly configurable CPUID bits), but they are actually supported when the related attributre bit is supported. Note, LASS and KeyLocker are not supported by KVM for TDX, nor does QEMU support it (see TDX_SUPPORTED_TD_ATTRS). They are defined in tdx_attrs_maps[] for the completeness of the existing TD Attribute bits that are related with CPUID features. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250508150002.689633-47-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 4 +++ target/i386/kvm/tdx.c | 60 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 342e4f2a57..e50c57264d 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -899,6 +899,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_7_0_ECX_LA57 (1U << 16) /* Read Processor ID */ #define CPUID_7_0_ECX_RDPID (1U << 22) +/* KeyLocker */ +#define CPUID_7_0_ECX_KeyLocker (1U << 23) /* Bus Lock Debug Exception */ #define CPUID_7_0_ECX_BUS_LOCK_DETECT (1U << 24) /* Cache Line Demote Instruction */ @@ -959,6 +961,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_7_1_EAX_AVX_VNNI (1U << 4) /* AVX512 BFloat16 Instruction */ #define CPUID_7_1_EAX_AVX512_BF16 (1U << 5) +/* Linear address space separation */ +#define CPUID_7_1_EAX_LASS (1U << 6) /* CMPCCXADD Instructions */ #define CPUID_7_1_EAX_CMPCCXADD (1U << 7) /* Fast Zero REP MOVS */ diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 9d92ff1484..fa161661fa 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -458,6 +458,34 @@ KvmCpuidInfo tdx_fixed1_bits = { }, }; +typedef struct TdxAttrsMap { + uint32_t attr_index; + uint32_t cpuid_leaf; + uint32_t cpuid_subleaf; + int cpuid_reg; + uint32_t feat_mask; +} TdxAttrsMap; + +static TdxAttrsMap tdx_attrs_maps[] = { + {.attr_index = 27, + .cpuid_leaf = 7, + .cpuid_subleaf = 1, + .cpuid_reg = R_EAX, + .feat_mask = CPUID_7_1_EAX_LASS,}, + + {.attr_index = 30, + .cpuid_leaf = 7, + .cpuid_subleaf = 0, + .cpuid_reg = R_ECX, + .feat_mask = CPUID_7_0_ECX_PKS,}, + + {.attr_index = 31, + .cpuid_leaf = 7, + .cpuid_subleaf = 0, + .cpuid_reg = R_ECX, + .feat_mask = CPUID_7_0_ECX_KeyLocker,}, +}; + static struct kvm_cpuid_entry2 *find_in_supported_entry(uint32_t function, uint32_t index) { @@ -494,6 +522,37 @@ static void tdx_add_supported_cpuid_by_fixed1_bits(void) } } +static void tdx_add_supported_cpuid_by_attrs(void) +{ + struct kvm_cpuid_entry2 *e; + TdxAttrsMap *map; + int i; + + for (i = 0; i < ARRAY_SIZE(tdx_attrs_maps); i++) { + map = &tdx_attrs_maps[i]; + if (!((1ULL << map->attr_index) & tdx_caps->supported_attrs)) { + continue; + } + + e = find_in_supported_entry(map->cpuid_leaf, map->cpuid_subleaf); + + switch(map->cpuid_reg) { + case R_EAX: + e->eax |= map->feat_mask; + break; + case R_EBX: + e->ebx |= map->feat_mask; + break; + case R_ECX: + e->ecx |= map->feat_mask; + break; + case R_EDX: + e->edx |= map->feat_mask; + break; + } + } +} + static void tdx_setup_supported_cpuid(void) { if (tdx_supported_cpuid) { @@ -508,6 +567,7 @@ static void tdx_setup_supported_cpuid(void) tdx_supported_cpuid->nent = tdx_caps->cpuid.nent; tdx_add_supported_cpuid_by_fixed1_bits(); + tdx_add_supported_cpuid_by_attrs(); } static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) From 8c94c84cb9e0140b48acc9c9d404525ca7ef7457 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:53 -0400 Subject: [PATCH 1264/2760] i386/tdx: Add supported CPUID bits relates to XFAM Some CPUID bits are controlled by XFAM. They are not covered by tdx_caps.cpuid (which only contians the directly configurable bits), but they are actually supported when the related XFAM bit is supported. Add these XFAM controlled bits to TDX supported CPUID bits based on the supported_xfam. Besides, incorporate the supported_xfam into the supported CPUID leaf of 0xD. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250508150002.689633-48-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 12 ------- target/i386/cpu.h | 16 ++++++++++ target/i386/kvm/tdx.c | 73 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 12 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 5aacb62f08..383c0b35d4 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1694,15 +1694,6 @@ bool is_feature_word_cpuid(uint32_t feature, uint32_t index, int reg) return false; } -typedef struct FeatureMask { - FeatureWord index; - uint64_t mask; -} FeatureMask; - -typedef struct FeatureDep { - FeatureMask from, to; -} FeatureDep; - static FeatureDep feature_dependencies[] = { { .from = { FEAT_7_0_EDX, CPUID_7_0_EDX_ARCH_CAPABILITIES }, @@ -1871,9 +1862,6 @@ static const X86RegisterInfo32 x86_reg_info_32[CPU_NB_REGS32] = { }; #undef REGISTER -/* CPUID feature bits available in XSS */ -#define CPUID_XSTATE_XSS_MASK (XSTATE_ARCH_LBR_MASK) - ExtSaveArea x86_ext_save_areas[XSAVE_STATE_AREA_COUNT] = { [XSTATE_FP_BIT] = { /* x87 FP state component is always enabled if XSAVE is supported */ diff --git a/target/i386/cpu.h b/target/i386/cpu.h index e50c57264d..b38e691f1a 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -584,6 +584,7 @@ typedef enum X86Seg { #define XSTATE_OPMASK_BIT 5 #define XSTATE_ZMM_Hi256_BIT 6 #define XSTATE_Hi16_ZMM_BIT 7 +#define XSTATE_PT_BIT 8 #define XSTATE_PKRU_BIT 9 #define XSTATE_ARCH_LBR_BIT 15 #define XSTATE_XTILE_CFG_BIT 17 @@ -597,6 +598,7 @@ typedef enum X86Seg { #define XSTATE_OPMASK_MASK (1ULL << XSTATE_OPMASK_BIT) #define XSTATE_ZMM_Hi256_MASK (1ULL << XSTATE_ZMM_Hi256_BIT) #define XSTATE_Hi16_ZMM_MASK (1ULL << XSTATE_Hi16_ZMM_BIT) +#define XSTATE_PT_MASK (1ULL << XSTATE_PT_BIT) #define XSTATE_PKRU_MASK (1ULL << XSTATE_PKRU_BIT) #define XSTATE_ARCH_LBR_MASK (1ULL << XSTATE_ARCH_LBR_BIT) #define XSTATE_XTILE_CFG_MASK (1ULL << XSTATE_XTILE_CFG_BIT) @@ -619,6 +621,11 @@ typedef enum X86Seg { XSTATE_Hi16_ZMM_MASK | XSTATE_PKRU_MASK | \ XSTATE_XTILE_CFG_MASK | XSTATE_XTILE_DATA_MASK) +/* CPUID feature bits available in XSS */ +#define CPUID_XSTATE_XSS_MASK (XSTATE_ARCH_LBR_MASK) + +#define CPUID_XSTATE_MASK (CPUID_XSTATE_XCR0_MASK | CPUID_XSTATE_XSS_MASK) + /* CPUID feature words */ typedef enum FeatureWord { FEAT_1_EDX, /* CPUID[1].EDX */ @@ -667,6 +674,15 @@ typedef enum FeatureWord { FEATURE_WORDS, } FeatureWord; +typedef struct FeatureMask { + FeatureWord index; + uint64_t mask; +} FeatureMask; + +typedef struct FeatureDep { + FeatureMask from, to; +} FeatureDep; + typedef uint64_t FeatureWordArray[FEATURE_WORDS]; uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index fa161661fa..188c2242d5 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -23,6 +23,8 @@ #include +#include "cpu.h" +#include "cpu-internal.h" #include "hw/i386/e820_memory_layout.h" #include "hw/i386/tdvf.h" #include "hw/i386/x86.h" @@ -486,6 +488,32 @@ static TdxAttrsMap tdx_attrs_maps[] = { .feat_mask = CPUID_7_0_ECX_KeyLocker,}, }; +typedef struct TdxXFAMDep { + int xfam_bit; + FeatureMask feat_mask; +} TdxXFAMDep; + +/* + * Note, only the CPUID bits whose virtualization type are "XFAM & Native" are + * defiend here. + * + * For those whose virtualization type are "XFAM & Configured & Native", they + * are reported as configurable bits. And they are not supported if not in the + * configureable bits list from KVM even if the corresponding XFAM bit is + * supported. + */ +TdxXFAMDep tdx_xfam_deps[] = { + { XSTATE_YMM_BIT, { FEAT_1_ECX, CPUID_EXT_FMA }}, + { XSTATE_YMM_BIT, { FEAT_7_0_EBX, CPUID_7_0_EBX_AVX2 }}, + { XSTATE_OPMASK_BIT, { FEAT_7_0_ECX, CPUID_7_0_ECX_AVX512_VBMI}}, + { XSTATE_OPMASK_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AVX512_FP16}}, + { XSTATE_PT_BIT, { FEAT_7_0_EBX, CPUID_7_0_EBX_INTEL_PT}}, + { XSTATE_PKRU_BIT, { FEAT_7_0_ECX, CPUID_7_0_ECX_PKU}}, + { XSTATE_XTILE_CFG_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AMX_BF16 }}, + { XSTATE_XTILE_CFG_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AMX_TILE }}, + { XSTATE_XTILE_CFG_BIT, { FEAT_7_0_EDX, CPUID_7_0_EDX_AMX_INT8 }}, +}; + static struct kvm_cpuid_entry2 *find_in_supported_entry(uint32_t function, uint32_t index) { @@ -553,6 +581,50 @@ static void tdx_add_supported_cpuid_by_attrs(void) } } +static void tdx_add_supported_cpuid_by_xfam(void) +{ + struct kvm_cpuid_entry2 *e; + int i; + + const TdxXFAMDep *xfam_dep; + const FeatureWordInfo *f; + for (i = 0; i < ARRAY_SIZE(tdx_xfam_deps); i++) { + xfam_dep = &tdx_xfam_deps[i]; + if (!((1ULL << xfam_dep->xfam_bit) & tdx_caps->supported_xfam)) { + continue; + } + + f = &feature_word_info[xfam_dep->feat_mask.index]; + if (f->type != CPUID_FEATURE_WORD) { + continue; + } + + e = find_in_supported_entry(f->cpuid.eax, f->cpuid.ecx); + switch(f->cpuid.reg) { + case R_EAX: + e->eax |= xfam_dep->feat_mask.mask; + break; + case R_EBX: + e->ebx |= xfam_dep->feat_mask.mask; + break; + case R_ECX: + e->ecx |= xfam_dep->feat_mask.mask; + break; + case R_EDX: + e->edx |= xfam_dep->feat_mask.mask; + break; + } + } + + e = find_in_supported_entry(0xd, 0); + e->eax |= (tdx_caps->supported_xfam & CPUID_XSTATE_XCR0_MASK); + e->edx |= (tdx_caps->supported_xfam & CPUID_XSTATE_XCR0_MASK) >> 32; + + e = find_in_supported_entry(0xd, 1); + e->ecx |= (tdx_caps->supported_xfam & CPUID_XSTATE_XSS_MASK); + e->edx |= (tdx_caps->supported_xfam & CPUID_XSTATE_XSS_MASK) >> 32; +} + static void tdx_setup_supported_cpuid(void) { if (tdx_supported_cpuid) { @@ -568,6 +640,7 @@ static void tdx_setup_supported_cpuid(void) tdx_add_supported_cpuid_by_fixed1_bits(); tdx_add_supported_cpuid_by_attrs(); + tdx_add_supported_cpuid_by_xfam(); } static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) From 9f5771c57dbe92d46361afd992a5851c846d0322 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:54 -0400 Subject: [PATCH 1265/2760] i386/tdx: Add XFD to supported bit of TDX Just mark XFD as always supported for TDX. This simple solution relies on the fact KVM will report XFD as 0 when it's not supported by the hardware. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-49-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 1 + target/i386/kvm/tdx.c | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index b38e691f1a..8a4b4217d0 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1122,6 +1122,7 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_XSAVE_XSAVEC (1U << 1) #define CPUID_XSAVE_XGETBV1 (1U << 2) #define CPUID_XSAVE_XSAVES (1U << 3) +#define CPUID_XSAVE_XFD (1U << 4) #define CPUID_6_EAX_ARAT (1U << 2) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 188c2242d5..0f7f47c6da 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -621,6 +621,12 @@ static void tdx_add_supported_cpuid_by_xfam(void) e->edx |= (tdx_caps->supported_xfam & CPUID_XSTATE_XCR0_MASK) >> 32; e = find_in_supported_entry(0xd, 1); + /* + * Mark XFD always support for TDX, it will be cleared finally in + * tdx_adjust_cpuid_features() if XFD is unavailable on the hardware + * because in this case the original data has it as 0. + */ + e->eax |= CPUID_XSAVE_XFD; e->ecx |= (tdx_caps->supported_xfam & CPUID_XSTATE_XSS_MASK); e->edx |= (tdx_caps->supported_xfam & CPUID_XSTATE_XSS_MASK) >> 32; } From 4d6e288a350a977b0fb0613db952087928ccd93e Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:55 -0400 Subject: [PATCH 1266/2760] i386/tdx: Define supported KVM features for TDX For TDX, only limited KVM PV features are supported. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-50-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 0f7f47c6da..e35983ad9b 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -32,6 +32,8 @@ #include "kvm_i386.h" #include "tdx.h" +#include "standard-headers/asm-x86/kvm_para.h" + #define TDX_MIN_TSC_FREQUENCY_KHZ (100 * 1000) #define TDX_MAX_TSC_FREQUENCY_KHZ (10 * 1000 * 1000) @@ -44,6 +46,14 @@ TDX_TD_ATTRIBUTES_PKS | \ TDX_TD_ATTRIBUTES_PERFMON) +#define TDX_SUPPORTED_KVM_FEATURES ((1U << KVM_FEATURE_NOP_IO_DELAY) | \ + (1U << KVM_FEATURE_PV_UNHALT) | \ + (1U << KVM_FEATURE_PV_TLB_FLUSH) | \ + (1U << KVM_FEATURE_PV_SEND_IPI) | \ + (1U << KVM_FEATURE_POLL_CONTROL) | \ + (1U << KVM_FEATURE_PV_SCHED_YIELD) | \ + (1U << KVM_FEATURE_MSI_EXT_DEST_ID)) + static TdxGuest *tdx_guest; static struct kvm_tdx_capabilities *tdx_caps; @@ -631,6 +641,14 @@ static void tdx_add_supported_cpuid_by_xfam(void) e->edx |= (tdx_caps->supported_xfam & CPUID_XSTATE_XSS_MASK) >> 32; } +static void tdx_add_supported_kvm_features(void) +{ + struct kvm_cpuid_entry2 *e; + + e = find_in_supported_entry(0x40000001, 0); + e->eax = TDX_SUPPORTED_KVM_FEATURES; +} + static void tdx_setup_supported_cpuid(void) { if (tdx_supported_cpuid) { @@ -647,6 +665,8 @@ static void tdx_setup_supported_cpuid(void) tdx_add_supported_cpuid_by_fixed1_bits(); tdx_add_supported_cpuid_by_attrs(); tdx_add_supported_cpuid_by_xfam(); + + tdx_add_supported_kvm_features(); } static int tdx_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) From dc0b08b303ad34983b43936a4c978672e0f9a9d8 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:56 -0400 Subject: [PATCH 1267/2760] i386/cgs: Introduce x86_confidential_guest_check_features() To do cgs specific feature checking. Note the feature checking in x86_cpu_filter_features() is valid for non-cgs VMs. For cgs VMs like TDX, what features can be supported has more restrictions. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-51-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/confidential-guest.h | 13 +++++++++++++ target/i386/kvm/kvm.c | 8 ++++++++ 2 files changed, 21 insertions(+) diff --git a/target/i386/confidential-guest.h b/target/i386/confidential-guest.h index 777d43cc96..48b88dbd31 100644 --- a/target/i386/confidential-guest.h +++ b/target/i386/confidential-guest.h @@ -42,6 +42,7 @@ struct X86ConfidentialGuestClass { void (*cpu_instance_init)(X86ConfidentialGuest *cg, CPUState *cpu); uint32_t (*adjust_cpuid_features)(X86ConfidentialGuest *cg, uint32_t feature, uint32_t index, int reg, uint32_t value); + int (*check_features)(X86ConfidentialGuest *cg, CPUState *cs); }; /** @@ -91,4 +92,16 @@ static inline int x86_confidential_guest_adjust_cpuid_features(X86ConfidentialGu } } +static inline int x86_confidential_guest_check_features(X86ConfidentialGuest *cg, + CPUState *cs) +{ + X86ConfidentialGuestClass *klass = X86_CONFIDENTIAL_GUEST_GET_CLASS(cg); + + if (klass->check_features) { + return klass->check_features(cg, cs); + } + + return 0; +} + #endif diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index cd87f5502a..a6bc089d02 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2093,6 +2093,14 @@ int kvm_arch_init_vcpu(CPUState *cs) int r; Error *local_err = NULL; + if (current_machine->cgs) { + r = x86_confidential_guest_check_features( + X86_CONFIDENTIAL_GUEST(current_machine->cgs), cs); + if (r < 0) { + return r; + } + } + memset(&cpuid_data, 0, sizeof(cpuid_data)); cpuid_i = 0; From 4a2fb19669bb41eee5b2fb8e5b5ba30e1daaeaf5 Mon Sep 17 00:00:00 2001 From: Lei Wang Date: Tue, 17 Dec 2024 07:39:31 -0500 Subject: [PATCH 1268/2760] i386: Remove unused parameter "uint32_t bit" in feature_word_description() Parameter "uint32_t bit" is not used in function feature_word_description(), so remove it. Signed-off-by: Lei Wang Reviewed-by: Igor Mammedov Reviewed-by: Xiaoyao Li Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20241217123932.948789-2-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 383c0b35d4..6258027ab1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5771,7 +5771,7 @@ static const TypeInfo max_x86_cpu_type_info = { .class_init = max_x86_cpu_class_init, }; -static char *feature_word_description(FeatureWordInfo *f, uint32_t bit) +static char *feature_word_description(FeatureWordInfo *f) { assert(f->type == CPUID_FEATURE_WORD || f->type == MSR_FEATURE_WORD); @@ -5810,6 +5810,7 @@ static void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask, CPUX86State *env = &cpu->env; FeatureWordInfo *f = &feature_word_info[w]; int i; + g_autofree char *feat_word_str = feature_word_description(f); if (!cpu->force_features) { env->features[w] &= ~mask; @@ -5822,7 +5823,6 @@ static void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask, for (i = 0; i < 64; ++i) { if ((1ULL << i) & mask) { - g_autofree char *feat_word_str = feature_word_description(f, i); warn_report("%s: %s%s%s [bit %d]", verbose_prefix, feat_word_str, From adf25ad70f2f989e63c2cd3e9de4e38152d05e84 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Tue, 17 Dec 2024 07:39:32 -0500 Subject: [PATCH 1269/2760] target/i386: Print CPUID subleaf info for unsupported feature Some CPUID leaves have meaningful subleaf index. Print the subleaf info in feature_word_description for CPUID features. Signed-off-by: Xiaoyao Li Reviewed-by: Eduardo Habkost Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20241217123932.948789-3-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 6258027ab1..be3812973f 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5780,11 +5780,15 @@ static char *feature_word_description(FeatureWordInfo *f) { const char *reg = get_register_name_32(f->cpuid.reg); assert(reg); - return g_strdup_printf("CPUID.%02XH:%s", - f->cpuid.eax, reg); + if (!f->cpuid.needs_ecx) { + return g_strdup_printf("CPUID[eax=%02Xh].%s", f->cpuid.eax, reg); + } else { + return g_strdup_printf("CPUID[eax=%02Xh,ecx=%02Xh].%s", + f->cpuid.eax, f->cpuid.ecx, reg); + } } case MSR_FEATURE_WORD: - return g_strdup_printf("MSR(%02XH)", + return g_strdup_printf("MSR(%02Xh)", f->msr.index); } From e3d1a4a6d1d61cf5fbd0e4b389cfb3976093739f Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:57 -0400 Subject: [PATCH 1270/2760] i386/tdx: Fetch and validate CPUID of TD guest Use KVM_TDX_GET_CPUID to get the CPUIDs that are managed and enfored by TDX module for TD guest. Check QEMU's configuration against the fetched data. Print wanring message when 1. a feature is not supported but requested by QEMU or 2. QEMU doesn't want to expose a feature while it is enforced enabled. - If cpu->enforced_cpuid is not set, prints the warning message of both 1) and 2) and tweak QEMU's configuration. - If cpu->enforced_cpuid is set, quit if any case of 1) or 2). Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250508150002.689633-52-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 33 +++++++++++++- target/i386/cpu.h | 7 +++ target/i386/kvm/tdx.c | 101 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 139 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index be3812973f..34364cf96a 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5808,8 +5808,8 @@ static bool x86_cpu_have_filtered_features(X86CPU *cpu) return false; } -static void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask, - const char *verbose_prefix) +void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask, + const char *verbose_prefix) { CPUX86State *env = &cpu->env; FeatureWordInfo *f = &feature_word_info[w]; @@ -5836,6 +5836,35 @@ static void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask, } } +void mark_forced_on_features(X86CPU *cpu, FeatureWord w, uint64_t mask, + const char *verbose_prefix) +{ + CPUX86State *env = &cpu->env; + FeatureWordInfo *f = &feature_word_info[w]; + int i; + + if (!cpu->force_features) { + env->features[w] |= mask; + } + + cpu->forced_on_features[w] |= mask; + + if (!verbose_prefix) { + return; + } + + for (i = 0; i < 64; ++i) { + if ((1ULL << i) & mask) { + g_autofree char *feat_word_str = feature_word_description(f); + warn_report("%s: %s%s%s [bit %d]", + verbose_prefix, + feat_word_str, + f->feat_names[i] ? "." : "", + f->feat_names[i] ? f->feat_names[i] : "", i); + } + } +} + static void x86_cpuid_version_get_family(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 8a4b4217d0..22e82444ae 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2215,6 +2215,9 @@ struct ArchCPU { /* Features that were filtered out because of missing host capabilities */ FeatureWordArray filtered_features; + /* Features that are forced enabled by underlying hypervisor, e.g., TDX */ + FeatureWordArray forced_on_features; + /* Enable PMU CPUID bits. This can't be enabled by default yet because * it doesn't have ABI stability guarantees, as it passes all PMU CPUID * bits returned by GET_SUPPORTED_CPUID (that depend on host CPU and kernel @@ -2526,6 +2529,10 @@ void host_cpuid(uint32_t function, uint32_t count, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx); bool cpu_has_x2apic_feature(CPUX86State *env); bool is_feature_word_cpuid(uint32_t feature, uint32_t index, int reg); +void mark_unavailable_features(X86CPU *cpu, FeatureWord w, uint64_t mask, + const char *verbose_prefix); +void mark_forced_on_features(X86CPU *cpu, FeatureWord w, uint64_t mask, + const char *verbose_prefix); static inline bool x86_has_cpuid_0x1f(X86CPU *cpu) { diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index e35983ad9b..e474abf3a6 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -766,6 +766,106 @@ static uint32_t tdx_adjust_cpuid_features(X86ConfidentialGuest *cg, return value; } +static struct kvm_cpuid2 *tdx_fetch_cpuid(CPUState *cpu, int *ret) +{ + struct kvm_cpuid2 *fetch_cpuid; + int size = KVM_MAX_CPUID_ENTRIES; + Error *local_err = NULL; + int r; + + do { + error_free(local_err); + local_err = NULL; + + fetch_cpuid = g_malloc0(sizeof(*fetch_cpuid) + + sizeof(struct kvm_cpuid_entry2) * size); + fetch_cpuid->nent = size; + r = tdx_vcpu_ioctl(cpu, KVM_TDX_GET_CPUID, 0, fetch_cpuid, &local_err); + if (r == -E2BIG) { + g_free(fetch_cpuid); + size = fetch_cpuid->nent; + } + } while (r == -E2BIG); + + if (r < 0) { + error_report_err(local_err); + *ret = r; + return NULL; + } + + return fetch_cpuid; +} + +static int tdx_check_features(X86ConfidentialGuest *cg, CPUState *cs) +{ + uint64_t actual, requested, unavailable, forced_on; + g_autofree struct kvm_cpuid2 *fetch_cpuid; + const char *forced_on_prefix = NULL; + const char *unav_prefix = NULL; + struct kvm_cpuid_entry2 *entry; + X86CPU *cpu = X86_CPU(cs); + CPUX86State *env = &cpu->env; + FeatureWordInfo *wi; + FeatureWord w; + bool mismatch = false; + int r; + + fetch_cpuid = tdx_fetch_cpuid(cs, &r); + if (!fetch_cpuid) { + return r; + } + + if (cpu->check_cpuid || cpu->enforce_cpuid) { + unav_prefix = "TDX doesn't support requested feature"; + forced_on_prefix = "TDX forcibly sets the feature"; + } + + for (w = 0; w < FEATURE_WORDS; w++) { + wi = &feature_word_info[w]; + actual = 0; + + switch (wi->type) { + case CPUID_FEATURE_WORD: + entry = cpuid_find_entry(fetch_cpuid, wi->cpuid.eax, wi->cpuid.ecx); + if (!entry) { + /* + * If KVM doesn't report it means it's totally configurable + * by QEMU + */ + continue; + } + + actual = cpuid_entry_get_reg(entry, wi->cpuid.reg); + break; + case MSR_FEATURE_WORD: + /* + * TODO: + * validate MSR features when KVM has interface report them. + */ + continue; + } + + requested = env->features[w]; + unavailable = requested & ~actual; + mark_unavailable_features(cpu, w, unavailable, unav_prefix); + if (unavailable) { + mismatch = true; + } + + forced_on = actual & ~requested; + mark_forced_on_features(cpu, w, forced_on, forced_on_prefix); + if (forced_on) { + mismatch = true; + } + } + + if (cpu->enforce_cpuid && mismatch) { + return -EINVAL; + } + + return 0; +} + static int tdx_validate_attributes(TdxGuest *tdx, Error **errp) { if ((tdx->attributes & ~tdx_caps->supported_attrs)) { @@ -1161,4 +1261,5 @@ static void tdx_guest_class_init(ObjectClass *oc, const void *data) x86_klass->kvm_type = tdx_kvm_type; x86_klass->cpu_instance_init = tdx_cpu_instance_init; x86_klass->adjust_cpuid_features = tdx_adjust_cpuid_features; + x86_klass->check_features = tdx_check_features; } From deb9db6fb789cfe80527b75983e86137589227a4 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:58 -0400 Subject: [PATCH 1271/2760] i386/tdx: Don't treat SYSCALL as unavailable On Intel CPU, the value of CPUID_EXT2_SYSCALL depends on the mode of the vcpu. It's 0 outside 64-bit mode and 1 in 64-bit mode. The initial state of TDX vcpu is 32-bit protected mode. At the time of calling KVM_TDX_GET_CPUID, vcpu hasn't started running so the value read is 0. In reality, 64-bit mode should always be supported. So mark CPUID_EXT2_SYSCALL always supported to avoid false warning. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-53-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index e474abf3a6..7629302991 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -845,6 +845,19 @@ static int tdx_check_features(X86ConfidentialGuest *cg, CPUState *cs) continue; } + /* Fixup for special cases */ + switch (w) { + case FEAT_8000_0001_EDX: + /* + * Intel enumerates SYSCALL bit as 1 only when processor in 64-bit + * mode and before vcpu running it's not in 64-bit mode. + */ + actual |= CPUID_EXT2_SYSCALL; + break; + default: + break; + } + requested = env->features[w]; unavailable = requested & ~actual; mark_unavailable_features(cpu, w, unavailable, unav_prefix); From ea4867b911fc2f6d4c8bd50ec62f0dc0fa190fab Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 10:59:59 -0400 Subject: [PATCH 1272/2760] i386/tdx: Make invtsc default on Because it's fixed1 bit that enforced by TDX module. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-54-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 7629302991..a55ab436ad 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -742,6 +742,9 @@ static void tdx_cpu_instance_init(X86ConfidentialGuest *cg, CPUState *cpu) object_property_set_bool(OBJECT(cpu), "pmu", false, &error_abort); + /* invtsc is fixed1 for TD guest */ + object_property_set_bool(OBJECT(cpu), "invtsc", true, &error_abort); + x86cpu->enable_cpuid_0x1f = true; } From 907ee7b67e50a7eea2768c66e3ad67c9aa4ffd3c Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 11:00:00 -0400 Subject: [PATCH 1273/2760] i386/tdx: Validate phys_bits against host value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For TDX guest, the phys_bits is not configurable and can only be host/native value. Validate phys_bits inside tdx_check_features(). Signed-off-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250508150002.689633-55-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/host-cpu.c | 2 +- target/i386/host-cpu.h | 1 + target/i386/kvm/tdx.c | 8 ++++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index a2d3830f5b..7512567298 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -15,7 +15,7 @@ #include "system/system.h" /* Note: Only safe for use on x86(-64) hosts */ -static uint32_t host_cpu_phys_bits(void) +uint32_t host_cpu_phys_bits(void) { uint32_t eax; uint32_t host_phys_bits; diff --git a/target/i386/host-cpu.h b/target/i386/host-cpu.h index 6a9bc918ba..b97ec01c9b 100644 --- a/target/i386/host-cpu.h +++ b/target/i386/host-cpu.h @@ -10,6 +10,7 @@ #ifndef HOST_CPU_H #define HOST_CPU_H +uint32_t host_cpu_phys_bits(void); void host_cpu_instance_init(X86CPU *cpu); void host_cpu_max_instance_init(X86CPU *cpu); bool host_cpu_realizefn(CPUState *cs, Error **errp); diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index a55ab436ad..0a21ae555c 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -25,6 +25,7 @@ #include "cpu.h" #include "cpu-internal.h" +#include "host-cpu.h" #include "hw/i386/e820_memory_layout.h" #include "hw/i386/tdvf.h" #include "hw/i386/x86.h" @@ -879,6 +880,13 @@ static int tdx_check_features(X86ConfidentialGuest *cg, CPUState *cs) return -EINVAL; } + if (cpu->phys_bits != host_cpu_phys_bits()) { + error_report("TDX requires guest CPU physical bits (%u) " + "to match host CPU physical bits (%u)", + cpu->phys_bits, host_cpu_phys_bits()); + return -EINVAL; + } + return 0; } From dc1424319311f86449c6825ceec2364ee645a363 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 8 May 2025 11:00:01 -0400 Subject: [PATCH 1274/2760] docs: Add TDX documentation Add docs/system/i386/tdx.rst for TDX support, and add tdx in confidential-guest-support.rst Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250508150002.689633-56-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- docs/system/confidential-guest-support.rst | 1 + docs/system/i386/tdx.rst | 161 +++++++++++++++++++++ docs/system/target-i386.rst | 1 + 3 files changed, 163 insertions(+) create mode 100644 docs/system/i386/tdx.rst diff --git a/docs/system/confidential-guest-support.rst b/docs/system/confidential-guest-support.rst index 0c490dbda2..66129fbab6 100644 --- a/docs/system/confidential-guest-support.rst +++ b/docs/system/confidential-guest-support.rst @@ -38,6 +38,7 @@ Supported mechanisms Currently supported confidential guest mechanisms are: * AMD Secure Encrypted Virtualization (SEV) (see :doc:`i386/amd-memory-encryption`) +* Intel Trust Domain Extension (TDX) (see :doc:`i386/tdx`) * POWER Protected Execution Facility (PEF) (see :ref:`power-papr-protected-execution-facility-pef`) * s390x Protected Virtualization (PV) (see :doc:`s390x/protvirt`) diff --git a/docs/system/i386/tdx.rst b/docs/system/i386/tdx.rst new file mode 100644 index 0000000000..8131750b64 --- /dev/null +++ b/docs/system/i386/tdx.rst @@ -0,0 +1,161 @@ +Intel Trusted Domain eXtension (TDX) +==================================== + +Intel Trusted Domain eXtensions (TDX) refers to an Intel technology that extends +Virtual Machine Extensions (VMX) and Multi-Key Total Memory Encryption (MKTME) +with a new kind of virtual machine guest called a Trust Domain (TD). A TD runs +in a CPU mode that is designed to protect the confidentiality of its memory +contents and its CPU state from any other software, including the hosting +Virtual Machine Monitor (VMM), unless explicitly shared by the TD itself. + +Prerequisites +------------- + +To run TD, the physical machine needs to have TDX module loaded and initialized +while KVM hypervisor has TDX support and has TDX enabled. If those requirements +are met, the ``KVM_CAP_VM_TYPES`` will report the support of ``KVM_X86_TDX_VM``. + +Trust Domain Virtual Firmware (TDVF) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Trust Domain Virtual Firmware (TDVF) is required to provide TD services to boot +TD Guest OS. TDVF needs to be copied to guest private memory and measured before +the TD boots. + +KVM vcpu ioctl ``KVM_TDX_INIT_MEM_REGION`` can be used to populate the TDVF +content into its private memory. + +Since TDX doesn't support readonly memslot, TDVF cannot be mapped as pflash +device and it actually works as RAM. "-bios" option is chosen to load TDVF. + +OVMF is the opensource firmware that implements the TDVF support. Thus the +command line to specify and load TDVF is ``-bios OVMF.fd`` + +Feature Configuration +--------------------- + +Unlike non-TDX VM, the CPU features (enumerated by CPU or MSR) of a TD are not +under full control of VMM. VMM can only configure part of features of a TD on +``KVM_TDX_INIT_VM`` command of VM scope ``MEMORY_ENCRYPT_OP`` ioctl. + +The configurable features have three types: + +- Attributes: + - PKS (bit 30) controls whether Supervisor Protection Keys is exposed to TD, + which determines related CPUID bit and CR4 bit; + - PERFMON (bit 63) controls whether PMU is exposed to TD. + +- XSAVE related features (XFAM): + XFAM is a 64b mask, which has the same format as XCR0 or IA32_XSS MSR. It + determines the set of extended features available for use by the guest TD. + +- CPUID features: + Only some bits of some CPUID leaves are directly configurable by VMM. + +What features can be configured is reported via TDX capabilities. + +TDX capabilities +~~~~~~~~~~~~~~~~ + +The VM scope ``MEMORY_ENCRYPT_OP`` ioctl provides command ``KVM_TDX_CAPABILITIES`` +to get the TDX capabilities from KVM. It returns a data structure of +``struct kvm_tdx_capabilities``, which tells the supported configuration of +attributes, XFAM and CPUIDs. + +TD attributes +~~~~~~~~~~~~~ + +QEMU supports configuring raw 64-bit TD attributes directly via "attributes" +property of "tdx-guest" object. Note, it's users' responsibility to provide a +valid value because some bits may not supported by current QEMU or KVM yet. + +QEMU also supports the configuration of individual attribute bits that are +supported by it, via properties of "tdx-guest" object. +E.g., "sept-ve-disable" (bit 28). + +MSR based features +~~~~~~~~~~~~~~~~~~ + +Current KVM doesn't support MSR based feature (e.g., MSR_IA32_ARCH_CAPABILITIES) +configuration for TDX, and it's a future work to enable it in QEMU when KVM adds +support of it. + +Feature check +~~~~~~~~~~~~~ + +QEMU checks if the final (CPU) features, determined by given cpu model and +explicit feature adjustment of "+featureA/-featureB", can be supported or not. +It can produce feature not supported warning like + + "warning: host doesn't support requested feature: CPUID.07H:EBX.intel-pt [bit 25]" + +It can also produce warning like + + "warning: TDX forcibly sets the feature: CPUID.80000007H:EDX.invtsc [bit 8]" + +if the fixed-1 feature is requested to be disabled explicitly. This is newly +added to QEMU for TDX because TDX has fixed-1 features that are forcibly enabled +by TDX module and VMM cannot disable them. + +Launching a TD (TDX VM) +----------------------- + +To launch a TD, the necessary command line options are tdx-guest object and +split kernel-irqchip, as below: + +.. parsed-literal:: + + |qemu_system_x86| \\ + -accel kvm \\ + -cpu host \\ + -object tdx-guest,id=tdx0 \\ + -machine ...,confidential-guest-support=tdx0 \\ + -bios OVMF.fd \\ + +Restrictions +------------ + + - kernel-irqchip must be split; + + This is set by default for TDX guest if kernel-irqchip is left on its default + 'auto' setting. + + - No readonly support for private memory; + + - No SMM support: SMM support requires manipulating the guest register states + which is not allowed; + +Debugging +--------- + +Bit 0 of TD attributes, is DEBUG bit, which decides if the TD runs in off-TD +debug mode. When in off-TD debug mode, TD's VCPU state and private memory are +accessible via given SEAMCALLs. This requires KVM to expose APIs to invoke those +SEAMCALLs and corresonponding QEMU change. + +It's targeted as future work. + +TD attestation +-------------- + +In TD guest, the attestation process is used to verify the TDX guest +trustworthiness to other entities before provisioning secrets to the guest. + +TD attestation is initiated first by calling TDG.MR.REPORT inside TD to get the +REPORT. Then the REPORT data needs to be converted into a remotely verifiable +Quote by SGX Quoting Enclave (QE). + +It's a future work in QEMU to add support of TD attestation since it lacks +support in current KVM. + +Live Migration +-------------- + +Future work. + +References +---------- + +- `TDX Homepage `__ + +- `SGX QE `__ diff --git a/docs/system/target-i386.rst b/docs/system/target-i386.rst index ab7af1a75d..43b09c79d6 100644 --- a/docs/system/target-i386.rst +++ b/docs/system/target-i386.rst @@ -31,6 +31,7 @@ Architectural features i386/kvm-pv i386/sgx i386/amd-memory-encryption + i386/tdx OS requirements ~~~~~~~~~~~~~~~ From 1297b285cc3ffbd06dc3208fbecdb2d582c535dc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 26 May 2025 09:22:20 +0200 Subject: [PATCH 1275/2760] rust: make declaration of dependent crates more consistent Crates like "bilge" and "libc" can be shared by more than one directory, so declare them directly in rust/meson.build. While at it, make their variable names end with "_rs" and always add a subproject() statement (as that pinpoints the error better if the subproject is missing and cannot be downloaded). Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/meson.build | 12 +++--------- rust/meson.build | 16 ++++++++++++++++ rust/qemu-api-macros/meson.build | 14 +++----------- rust/qemu-api/meson.build | 4 +--- 4 files changed, 23 insertions(+), 23 deletions(-) diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 547cca5a96..494b6c123c 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -1,17 +1,11 @@ -subproject('bilge-0.2-rs', required: true) -subproject('bilge-impl-0.2-rs', required: true) - -bilge_dep = dependency('bilge-0.2-rs') -bilge_impl_dep = dependency('bilge-impl-0.2-rs') - _libpl011_rs = static_library( 'pl011', files('src/lib.rs'), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', dependencies: [ - bilge_dep, - bilge_impl_dep, + bilge_rs, + bilge_impl_rs, qemu_api, qemu_api_macros, ], @@ -21,6 +15,6 @@ rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency( link_whole: [_libpl011_rs], # Putting proc macro crates in `dependencies` is necessary for Meson to find # them when compiling the root per-target static rust lib. - dependencies: [bilge_impl_dep, qemu_api_macros], + dependencies: [bilge_impl_rs, qemu_api_macros], variables: {'crate': 'pl011'}, )]) diff --git a/rust/meson.build b/rust/meson.build index 91e52b8fb8..1f0dcce7d0 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -1,3 +1,19 @@ +subproject('bilge-0.2-rs', required: true) +subproject('bilge-impl-0.2-rs', required: true) +subproject('libc-0.2-rs', required: true) + +bilge_rs = dependency('bilge-0.2-rs') +bilge_impl_rs = dependency('bilge-impl-0.2-rs') +libc_rs = dependency('libc-0.2-rs') + +subproject('proc-macro2-1-rs', required: true) +subproject('quote-1-rs', required: true) +subproject('syn-2-rs', required: true) + +quote_rs_native = dependency('quote-1-rs', native: true) +syn_rs_native = dependency('syn-2-rs', native: true) +proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true) + subdir('qemu-api-macros') subdir('qemu-api') diff --git a/rust/qemu-api-macros/meson.build b/rust/qemu-api-macros/meson.build index 6f94a4bb3c..8610ce1c84 100644 --- a/rust/qemu-api-macros/meson.build +++ b/rust/qemu-api-macros/meson.build @@ -1,11 +1,3 @@ -subproject('proc-macro2-1-rs', required: true) -subproject('quote-1-rs', required: true) -subproject('syn-2-rs', required: true) - -quote_dep = dependency('quote-1-rs', native: true) -syn_dep = dependency('syn-2-rs', native: true) -proc_macro2_dep = dependency('proc-macro2-1-rs', native: true) - _qemu_api_macros_rs = rust.proc_macro( 'qemu_api_macros', files('src/lib.rs'), @@ -16,9 +8,9 @@ _qemu_api_macros_rs = rust.proc_macro( '--cfg', 'feature="proc-macro"', ], dependencies: [ - proc_macro2_dep, - quote_dep, - syn_dep, + proc_macro2_rs_native, + quote_rs_native, + syn_rs_native, ], ) diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 1696df705b..1ea86b8bbf 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -2,8 +2,6 @@ _qemu_api_cfg = run_command(rustc_args, '--config-headers', config_host_h, '--features', files('Cargo.toml'), capture: true, check: true).stdout().strip().splitlines() -libc_dep = dependency('libc-0.2-rs') - # _qemu_api_cfg += ['--cfg', 'feature="allocator"'] if get_option('debug_mutex') _qemu_api_cfg += ['--cfg', 'feature="debug_cell"'] @@ -37,7 +35,7 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [libc_dep, qemu_api_macros], + dependencies: [libc_rs, qemu_api_macros], ) rust.test('rust-qemu-api-tests', _qemu_api_rs, From 397db937e85d7b9f5a6f0b30764786cef09d1ff3 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Thu, 8 May 2025 14:57:59 -0500 Subject: [PATCH 1276/2760] target/i386: Update EPYC CPU model for Cache property, RAS, SVM feature bits Found that some of the cache properties are not set correctly for EPYC models. l1d_cache.no_invd_sharing should not be true. l1i_cache.no_invd_sharing should not be true. L2.self_init should be true. L2.inclusive should be true. L3.inclusive should not be true. L3.no_invd_sharing should be true. Fix the cache properties. Also add the missing RAS and SVM features bits on AMD EPYC CPU models. The SVM feature bits are used in nested guests. succor : Software uncorrectable error containment and recovery capability. overflow-recov : MCA overflow recovery support. lbrv : LBR virtualization tsc-scale : MSR based TSC rate control vmcb-clean : VMCB clean bits flushbyasid : Flush by ASID pause-filter : Pause intercept filter pfthreshold : PAUSE filter threshold v-vmsave-vmload : Virtualized VMLOAD and VMSAVE vgif : Virtualized GIF Signed-off-by: Babu Moger Reviewed-by: Maksim Davydov Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/515941861700d7066186c9600bc5d96a1741ef0c.1746734284.git.babu.moger@amd.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 34364cf96a..b6c63b892e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -2211,6 +2211,60 @@ static CPUCaches epyc_v4_cache_info = { }, }; +static CPUCaches epyc_v5_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 64 * KiB, + .line_size = 64, + .associativity = 4, + .partitions = 1, + .sets = 256, + .lines_per_tag = 1, + .self_init = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 8 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 8192, + .lines_per_tag = 1, + .self_init = true, + .no_invd_sharing = true, + .complex_indexing = false, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + static const CPUCaches epyc_rome_cache_info = { .l1d_cache = &(CPUCacheInfo) { .type = DATA_CACHE, @@ -5238,6 +5292,25 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, .cache_info = &epyc_v4_cache_info }, + { + .version = 5, + .props = (PropValue[]) { + { "overflow-recov", "on" }, + { "succor", "on" }, + { "lbrv", "on" }, + { "tsc-scale", "on" }, + { "vmcb-clean", "on" }, + { "flushbyasid", "on" }, + { "pause-filter", "on" }, + { "pfthreshold", "on" }, + { "v-vmsave-vmload", "on" }, + { "vgif", "on" }, + { "model-id", + "AMD EPYC-v5 Processor" }, + { /* end of list */ } + }, + .cache_info = &epyc_v5_cache_info + }, { /* end of list */ } } }, From 83d940e9700527ff080416ce2fa52ee1f4771d72 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Thu, 8 May 2025 14:58:00 -0500 Subject: [PATCH 1277/2760] target/i386: Update EPYC-Rome CPU model for Cache property, RAS, SVM feature bits Found that some of the cache properties are not set correctly for EPYC models. l1d_cache.no_invd_sharing should not be true. l1i_cache.no_invd_sharing should not be true. L2.self_init should be true. L2.inclusive should be true. L3.inclusive should not be true. L3.no_invd_sharing should be true. Fix these cache properties. Also add the missing RAS and SVM features bits on AMD EPYC-Rome. The SVM feature bits are used in nested guests. succor : Software uncorrectable error containment and recovery capability. overflow-recov : MCA overflow recovery support. lbrv : LBR virtualization tsc-scale : MSR based TSC rate control vmcb-clean : VMCB clean bits flushbyasid : Flush by ASID pause-filter : Pause intercept filter pfthreshold : PAUSE filter threshold v-vmsave-vmload : Virtualized VMLOAD and VMSAVE vgif : Virtualized GIF Signed-off-by: Babu Moger Reviewed-by: Maksim Davydov Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/8265af72057b84c99ac3a02a5487e32759cc69b1.1746734284.git.babu.moger@amd.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index b6c63b892e..5b5324a35c 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -2373,6 +2373,60 @@ static const CPUCaches epyc_rome_v3_cache_info = { }, }; +static const CPUCaches epyc_rome_v5_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 16 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 16384, + .lines_per_tag = 1, + .self_init = true, + .no_invd_sharing = true, + .complex_indexing = false, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + static const CPUCaches epyc_milan_cache_info = { .l1d_cache = &(CPUCacheInfo) { .type = DATA_CACHE, @@ -5449,6 +5503,25 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } }, }, + { + .version = 5, + .props = (PropValue[]) { + { "overflow-recov", "on" }, + { "succor", "on" }, + { "lbrv", "on" }, + { "tsc-scale", "on" }, + { "vmcb-clean", "on" }, + { "flushbyasid", "on" }, + { "pause-filter", "on" }, + { "pfthreshold", "on" }, + { "v-vmsave-vmload", "on" }, + { "vgif", "on" }, + { "model-id", + "AMD EPYC-Rome-v5 Processor" }, + { /* end of list */ } + }, + .cache_info = &epyc_rome_v5_cache_info + }, { /* end of list */ } } }, From fc014d9ba5b26b27401e0e88a4e1ef827c68fe64 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Thu, 8 May 2025 14:58:01 -0500 Subject: [PATCH 1278/2760] target/i386: Update EPYC-Milan CPU model for Cache property, RAS, SVM feature bits Found that some of the cache properties are not set correctly for EPYC models. l1d_cache.no_invd_sharing should not be true. l1i_cache.no_invd_sharing should not be true. L2.self_init should be true. L2.inclusive should be true. L3.inclusive should not be true. L3.no_invd_sharing should be true. Fix these cache properties. Also add the missing RAS and SVM features bits on AMD EPYC-Milan model. The SVM feature bits are used in nested guests. succor : Software uncorrectable error containment and recovery capability. overflow-recov : MCA overflow recovery support. lbrv : LBR virtualization tsc-scale : MSR based TSC rate control vmcb-clean : VMCB clean bits flushbyasid : Flush by ASID pause-filter : Pause intercept filter pfthreshold : PAUSE filter threshold v-vmsave-vmload : Virtualized VMLOAD and VMSAVE vgif : Virtualized GIF Signed-off-by: Babu Moger Reviewed-by: Maksim Davydov Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/c619c0e09a9d5d496819ed48d69181d65f416891.1746734284.git.babu.moger@amd.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 5b5324a35c..d01a808e3a 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -2535,6 +2535,60 @@ static const CPUCaches epyc_milan_v2_cache_info = { }, }; +static const CPUCaches epyc_milan_v3_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 32 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 32768, + .lines_per_tag = 1, + .self_init = true, + .no_invd_sharing = true, + .complex_indexing = false, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + static const CPUCaches epyc_genoa_cache_info = { .l1d_cache = &(CPUCacheInfo) { .type = DATA_CACHE, @@ -5597,6 +5651,25 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, .cache_info = &epyc_milan_v2_cache_info }, + { + .version = 3, + .props = (PropValue[]) { + { "overflow-recov", "on" }, + { "succor", "on" }, + { "lbrv", "on" }, + { "tsc-scale", "on" }, + { "vmcb-clean", "on" }, + { "flushbyasid", "on" }, + { "pause-filter", "on" }, + { "pfthreshold", "on" }, + { "v-vmsave-vmload", "on" }, + { "vgif", "on" }, + { "model-id", + "AMD EPYC-Milan-v3 Processor" }, + { /* end of list */ } + }, + .cache_info = &epyc_milan_v3_cache_info + }, { /* end of list */ } } }, From dfd5b456108a75588ab094358ba5754787146d3d Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Thu, 8 May 2025 14:58:02 -0500 Subject: [PATCH 1279/2760] target/i386: Add couple of feature bits in CPUID_Fn80000021_EAX Add CPUID bit indicates that a WRMSR to MSR_FS_BASE, MSR_GS_BASE, or MSR_KERNEL_GS_BASE is non-serializing amd PREFETCHI that the indicates support for IC prefetch. CPUID_Fn80000021_EAX Bit Feature description 20 Indicates support for IC prefetch. 1 FsGsKernelGsBaseNonSerializing. WRMSR to FS_BASE, GS_BASE and KernelGSbase are non-serializing. Link: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/57238.zip Signed-off-by: Babu Moger Reviewed-by: Maksim Davydov Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/a5f6283a59579b09ac345b3f21ecb3b3b2d92451.1746734284.git.babu.moger@amd.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 4 ++-- target/i386/cpu.h | 4 ++++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index d01a808e3a..0d1b907778 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1253,12 +1253,12 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { [FEAT_8000_0021_EAX] = { .type = CPUID_FEATURE_WORD, .feat_names = { - "no-nested-data-bp", NULL, "lfence-always-serializing", NULL, + "no-nested-data-bp", "fs-gs-base-ns", "lfence-always-serializing", NULL, NULL, NULL, "null-sel-clr-base", NULL, "auto-ibrs", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, + "prefetchi", NULL, NULL, NULL, "eraps", NULL, NULL, "sbpb", "ibpb-brtype", "srso-no", "srso-user-kernel-no", NULL, }, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 22e82444ae..1146465c8c 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1092,12 +1092,16 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); /* Processor ignores nested data breakpoints */ #define CPUID_8000_0021_EAX_NO_NESTED_DATA_BP (1U << 0) +/* WRMSR to FS_BASE, GS_BASE, or KERNEL_GS_BASE is non-serializing */ +#define CPUID_8000_0021_EAX_FS_GS_BASE_NS (1U << 1) /* LFENCE is always serializing */ #define CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING (1U << 2) /* Null Selector Clears Base */ #define CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE (1U << 6) /* Automatic IBRS */ #define CPUID_8000_0021_EAX_AUTO_IBRS (1U << 8) +/* Indicates support for IC prefetch */ +#define CPUID_8000_0021_EAX_PREFETCHI (1U << 20) /* Enhanced Return Address Predictor Scurity */ #define CPUID_8000_0021_EAX_ERAPS (1U << 24) /* Selective Branch Predictor Barrier */ From abc92cc8488b5dbcc403b5be24d8092180605101 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Thu, 8 May 2025 14:58:03 -0500 Subject: [PATCH 1280/2760] target/i386: Update EPYC-Genoa for Cache property, perfmon-v2, RAS and SVM feature bits Found that some of the cache properties are not set correctly for EPYC models. l1d_cache.no_invd_sharing should not be true. l1i_cache.no_invd_sharing should not be true. L2.self_init should be true. L2.inclusive should be true. L3.inclusive should not be true. L3.no_invd_sharing should be true. Fix these cache properties. Also add the missing RAS and SVM features bits on AMD EPYC-Genoa model. The SVM feature bits are used in nested guests. perfmon-v2 : Allow guests to make use of the PerfMonV2 features. succor : Software uncorrectable error containment and recovery capability. overflow-recov : MCA overflow recovery support. lbrv : LBR virtualization tsc-scale : MSR based TSC rate control vmcb-clean : VMCB clean bits flushbyasid : Flush by ASID pause-filter : Pause intercept filter pfthreshold : PAUSE filter threshold v-vmsave-vmload: Virtualized VMLOAD and VMSAVE vgif : Virtualized GIF fs-gs-base-ns : WRMSR to {FS,GS,KERNEL_GS}_BASE is non-serializing The feature details are available in APM listed below [1]. [1] AMD64 Architecture Programmer's Manual Volume 2: System Programming Publication # 24593 Revision 3.41. Link: https://bugzilla.kernel.org/show_bug.cgi?id=206537 Signed-off-by: Babu Moger Reviewed-by: Maksim Davydov Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/afe3f05d4116124fd5795f28fc23d7b396140313.1746734284.git.babu.moger@amd.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 78 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 0d1b907778..a656b3c664 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -2643,6 +2643,59 @@ static const CPUCaches epyc_genoa_cache_info = { }, }; +static const CPUCaches epyc_genoa_v2_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 1 * MiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 2048, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 32 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 32768, + .lines_per_tag = 1, + .self_init = true, + .no_invd_sharing = true, + .complex_indexing = false, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; /* The following VMX features are not supported by KVM and are left out in the * CPU definitions: * @@ -5744,6 +5797,31 @@ static const X86CPUDefinition builtin_x86_defs[] = { .xlevel = 0x80000022, .model_id = "AMD EPYC-Genoa Processor", .cache_info = &epyc_genoa_cache_info, + .versions = (X86CPUVersionDefinition[]) { + { .version = 1 }, + { + .version = 2, + .props = (PropValue[]) { + { "overflow-recov", "on" }, + { "succor", "on" }, + { "lbrv", "on" }, + { "tsc-scale", "on" }, + { "vmcb-clean", "on" }, + { "flushbyasid", "on" }, + { "pause-filter", "on" }, + { "pfthreshold", "on" }, + { "v-vmsave-vmload", "on" }, + { "vgif", "on" }, + { "fs-gs-base-ns", "on" }, + { "perfmon-v2", "on" }, + { "model-id", + "AMD EPYC-Genoa-v2 Processor" }, + { /* end of list */ } + }, + .cache_info = &epyc_genoa_v2_cache_info + }, + { /* end of list */ } + } }, { .name = "YongFeng", From 3771a4daa273ba17cb27309984413790d1df5651 Mon Sep 17 00:00:00 2001 From: Babu Moger Date: Thu, 8 May 2025 14:58:04 -0500 Subject: [PATCH 1281/2760] target/i386: Add support for EPYC-Turin model Add the support for AMD EPYC zen 5 processors (EPYC-Turin). Add the following new feature bits on top of the feature bits from the previous generation EPYC models. movdiri : Move Doubleword as Direct Store Instruction movdir64b : Move 64 Bytes as Direct Store Instruction avx512-vp2intersect : AVX512 Vector Pair Intersection to a Pair of Mask Register avx-vnni : AVX VNNI Instruction prefetchi : Indicates support for IC prefetch sbpb : Selective Branch Predictor Barrier ibpb-brtype : IBPB includes branch type prediction flushing srso-user-kernel-no : Not vulnerable to SRSO at the user-kernel boundary Link: https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/57238.zip Link: https://www.amd.com/content/dam/amd/en/documents/corporate/cr/speculative-return-stack-overflow-whitepaper.pdf Signed-off-by: Babu Moger Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/b4fa7708a0e1453d2e9b8ec3dc881feb92eeca0b.1746734284.git.babu.moger@amd.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index a656b3c664..15439fdfa8 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -2696,6 +2696,61 @@ static const CPUCaches epyc_genoa_v2_cache_info = { .share_level = CPU_TOPOLOGY_LEVEL_DIE, }, }; + +static const CPUCaches epyc_turin_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 48 * KiB, + .line_size = 64, + .associativity = 12, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 32 * KiB, + .line_size = 64, + .associativity = 8, + .partitions = 1, + .sets = 64, + .lines_per_tag = 1, + .self_init = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 1 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 1024, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 32 * MiB, + .line_size = 64, + .associativity = 16, + .partitions = 1, + .sets = 32768, + .lines_per_tag = 1, + .self_init = true, + .no_invd_sharing = true, + .complex_indexing = false, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + /* The following VMX features are not supported by KVM and are left out in the * CPU definitions: * @@ -5959,6 +6014,89 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .name = "EPYC-Turin", + .level = 0xd, + .vendor = CPUID_VENDOR_AMD, + .family = 26, + .model = 0, + .stepping = 0, + .features[FEAT_1_ECX] = + CPUID_EXT_RDRAND | CPUID_EXT_F16C | CPUID_EXT_AVX | + CPUID_EXT_XSAVE | CPUID_EXT_AES | CPUID_EXT_POPCNT | + CPUID_EXT_MOVBE | CPUID_EXT_SSE42 | CPUID_EXT_SSE41 | + CPUID_EXT_PCID | CPUID_EXT_CX16 | CPUID_EXT_FMA | + CPUID_EXT_SSSE3 | CPUID_EXT_MONITOR | CPUID_EXT_PCLMULQDQ | + CPUID_EXT_SSE3, + .features[FEAT_1_EDX] = + CPUID_SSE2 | CPUID_SSE | CPUID_FXSR | CPUID_MMX | CPUID_CLFLUSH | + CPUID_PSE36 | CPUID_PAT | CPUID_CMOV | CPUID_MCA | CPUID_PGE | + CPUID_MTRR | CPUID_SEP | CPUID_APIC | CPUID_CX8 | CPUID_MCE | + CPUID_PAE | CPUID_MSR | CPUID_TSC | CPUID_PSE | CPUID_DE | + CPUID_VME | CPUID_FP87, + .features[FEAT_6_EAX] = + CPUID_6_EAX_ARAT, + .features[FEAT_7_0_EBX] = + CPUID_7_0_EBX_FSGSBASE | CPUID_7_0_EBX_BMI1 | CPUID_7_0_EBX_AVX2 | + CPUID_7_0_EBX_SMEP | CPUID_7_0_EBX_BMI2 | CPUID_7_0_EBX_ERMS | + CPUID_7_0_EBX_INVPCID | CPUID_7_0_EBX_AVX512F | + CPUID_7_0_EBX_AVX512DQ | CPUID_7_0_EBX_RDSEED | CPUID_7_0_EBX_ADX | + CPUID_7_0_EBX_SMAP | CPUID_7_0_EBX_AVX512IFMA | + CPUID_7_0_EBX_CLFLUSHOPT | CPUID_7_0_EBX_CLWB | + CPUID_7_0_EBX_AVX512CD | CPUID_7_0_EBX_SHA_NI | + CPUID_7_0_EBX_AVX512BW | CPUID_7_0_EBX_AVX512VL, + .features[FEAT_7_0_ECX] = + CPUID_7_0_ECX_AVX512_VBMI | CPUID_7_0_ECX_UMIP | CPUID_7_0_ECX_PKU | + CPUID_7_0_ECX_AVX512_VBMI2 | CPUID_7_0_ECX_GFNI | + CPUID_7_0_ECX_VAES | CPUID_7_0_ECX_VPCLMULQDQ | + CPUID_7_0_ECX_AVX512VNNI | CPUID_7_0_ECX_AVX512BITALG | + CPUID_7_0_ECX_AVX512_VPOPCNTDQ | CPUID_7_0_ECX_LA57 | + CPUID_7_0_ECX_RDPID | CPUID_7_0_ECX_MOVDIRI | + CPUID_7_0_ECX_MOVDIR64B, + .features[FEAT_7_0_EDX] = + CPUID_7_0_EDX_FSRM | CPUID_7_0_EDX_AVX512_VP2INTERSECT, + .features[FEAT_7_1_EAX] = + CPUID_7_1_EAX_AVX_VNNI | CPUID_7_1_EAX_AVX512_BF16, + .features[FEAT_8000_0001_ECX] = + CPUID_EXT3_OSVW | CPUID_EXT3_3DNOWPREFETCH | + CPUID_EXT3_MISALIGNSSE | CPUID_EXT3_SSE4A | CPUID_EXT3_ABM | + CPUID_EXT3_CR8LEG | CPUID_EXT3_SVM | CPUID_EXT3_LAHF_LM | + CPUID_EXT3_TOPOEXT | CPUID_EXT3_PERFCORE, + .features[FEAT_8000_0001_EDX] = + CPUID_EXT2_LM | CPUID_EXT2_RDTSCP | CPUID_EXT2_PDPE1GB | + CPUID_EXT2_FFXSR | CPUID_EXT2_MMXEXT | CPUID_EXT2_NX | + CPUID_EXT2_SYSCALL, + .features[FEAT_8000_0007_EBX] = + CPUID_8000_0007_EBX_OVERFLOW_RECOV | CPUID_8000_0007_EBX_SUCCOR, + .features[FEAT_8000_0008_EBX] = + CPUID_8000_0008_EBX_CLZERO | CPUID_8000_0008_EBX_XSAVEERPTR | + CPUID_8000_0008_EBX_WBNOINVD | CPUID_8000_0008_EBX_IBPB | + CPUID_8000_0008_EBX_IBRS | CPUID_8000_0008_EBX_STIBP | + CPUID_8000_0008_EBX_STIBP_ALWAYS_ON | + CPUID_8000_0008_EBX_AMD_SSBD | CPUID_8000_0008_EBX_AMD_PSFD, + .features[FEAT_8000_0021_EAX] = + CPUID_8000_0021_EAX_NO_NESTED_DATA_BP | + CPUID_8000_0021_EAX_FS_GS_BASE_NS | + CPUID_8000_0021_EAX_LFENCE_ALWAYS_SERIALIZING | + CPUID_8000_0021_EAX_NULL_SEL_CLR_BASE | + CPUID_8000_0021_EAX_AUTO_IBRS | CPUID_8000_0021_EAX_PREFETCHI | + CPUID_8000_0021_EAX_SBPB | CPUID_8000_0021_EAX_IBPB_BRTYPE | + CPUID_8000_0021_EAX_SRSO_USER_KERNEL_NO, + .features[FEAT_8000_0022_EAX] = + CPUID_8000_0022_EAX_PERFMON_V2, + .features[FEAT_XSAVE] = + CPUID_XSAVE_XSAVEOPT | CPUID_XSAVE_XSAVEC | + CPUID_XSAVE_XGETBV1 | CPUID_XSAVE_XSAVES, + .features[FEAT_SVM] = + CPUID_SVM_NPT | CPUID_SVM_LBRV | CPUID_SVM_NRIPSAVE | + CPUID_SVM_TSCSCALE | CPUID_SVM_VMCBCLEAN | CPUID_SVM_FLUSHASID | + CPUID_SVM_PAUSEFILTER | CPUID_SVM_PFTHRESHOLD | + CPUID_SVM_V_VMSAVE_VMLOAD | CPUID_SVM_VGIF | + CPUID_SVM_VNMI | CPUID_SVM_SVME_ADDR_CHK, + .xlevel = 0x80000022, + .model_id = "AMD EPYC-Turin Processor", + .cache_info = &epyc_turin_cache_info, + }, }; /* From 9bd24d8d2756a0771b6677b02c7f9b603ef6afe9 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Mon, 26 May 2025 13:44:47 +0200 Subject: [PATCH 1282/2760] target/i386/tcg/helper-tcg: fix file references in comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 32cad1ffb8 ("include: Rename sysemu/ -> system/") renamed target/i386/tcg/sysemu => target/i386/tcg/system. Signed-off-by: Fiona Ebner Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250526114447.1243840-1-f.ebner@proxmox.com Signed-off-by: Paolo Bonzini --- target/i386/tcg/helper-tcg.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/helper-tcg.h b/target/i386/tcg/helper-tcg.h index 6b3f19855f..be011b06b7 100644 --- a/target/i386/tcg/helper-tcg.h +++ b/target/i386/tcg/helper-tcg.h @@ -97,7 +97,7 @@ static inline unsigned int compute_pf(uint8_t x) /* misc_helper.c */ void cpu_load_eflags(CPUX86State *env, int eflags, int update_mask); -/* sysemu/svm_helper.c */ +/* system/svm_helper.c */ #ifndef CONFIG_USER_ONLY G_NORETURN void cpu_vmexit(CPUX86State *nenv, uint32_t exit_code, uint64_t exit_info_1, uintptr_t retaddr); @@ -115,7 +115,7 @@ int exception_has_error_code(int intno); /* smm_helper.c */ void do_smm_enter(X86CPU *cpu); -/* sysemu/bpt_helper.c */ +/* system/bpt_helper.c */ bool check_hw_breakpoints(CPUX86State *env, bool force_dr6_update); /* From 638422f5bcdf2c7bdb401b987b134322c5d6bd4d Mon Sep 17 00:00:00 2001 From: Hao Wu Date: Thu, 29 May 2025 17:45:09 +0100 Subject: [PATCH 1283/2760] hw/arm: Add GMAC devices to NPCM8XX SoC The GMAC was originally created for the 8xx machine. During upstreaming both the GMAC and the 8XX we removed it so they would not depend on each other for the process, that connection should be added back in. Signed-off-by: Hao Wu Signed-off-by: Nabih Estefan Message-id: 20250508220718.735415-2-nabihestefan@google.com Reviewed-by: Tyrone Ting Signed-off-by: Peter Maydell --- hw/arm/npcm8xx.c | 54 ++++++++++++++++++++++++++++++++++++---- include/hw/arm/npcm8xx.h | 5 +++- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/hw/arm/npcm8xx.c b/hw/arm/npcm8xx.c index d7ee306de7..d14bf55cd7 100644 --- a/hw/arm/npcm8xx.c +++ b/hw/arm/npcm8xx.c @@ -67,6 +67,9 @@ /* SDHCI Modules */ #define NPCM8XX_MMC_BA 0xf0842000 +/* PCS Module */ +#define NPCM8XX_PCS_BA 0xf0780000 + /* PSPI Modules */ #define NPCM8XX_PSPI_BA 0xf0201000 @@ -85,6 +88,10 @@ enum NPCM8xxInterrupt { NPCM8XX_ADC_IRQ = 0, NPCM8XX_PECI_IRQ = 6, NPCM8XX_KCS_HIB_IRQ = 9, + NPCM8XX_GMAC1_IRQ = 14, + NPCM8XX_GMAC2_IRQ, + NPCM8XX_GMAC3_IRQ, + NPCM8XX_GMAC4_IRQ, NPCM8XX_MMC_IRQ = 26, NPCM8XX_PSPI_IRQ = 28, NPCM8XX_TIMER0_IRQ = 32, /* Timer Module 0 */ @@ -260,6 +267,14 @@ static const hwaddr npcm8xx_smbus_addr[] = { 0xfff0a000, }; +/* Register base address for each GMAC Module */ +static const hwaddr npcm8xx_gmac_addr[] = { + 0xf0802000, + 0xf0804000, + 0xf0806000, + 0xf0808000, +}; + /* Register base address for each USB host EHCI registers */ static const hwaddr npcm8xx_ehci_addr[] = { 0xf0828100, @@ -444,6 +459,11 @@ static void npcm8xx_init(Object *obj) object_initialize_child(obj, "mft[*]", &s->mft[i], TYPE_NPCM7XX_MFT); } + for (i = 0; i < ARRAY_SIZE(s->gmac); i++) { + object_initialize_child(obj, "gmac[*]", &s->gmac[i], TYPE_NPCM_GMAC); + } + object_initialize_child(obj, "pcs", &s->pcs, TYPE_NPCM_PCS); + object_initialize_child(obj, "mmc", &s->mmc, TYPE_NPCM7XX_SDHCI); object_initialize_child(obj, "pspi", &s->pspi, TYPE_NPCM_PSPI); } @@ -668,6 +688,35 @@ static void npcm8xx_realize(DeviceState *dev, Error **errp) sysbus_connect_irq(sbd, 0, npcm8xx_irq(s, NPCM8XX_MFT0_IRQ + i)); } + /* + * GMAC Modules. Cannot fail. + */ + QEMU_BUILD_BUG_ON(ARRAY_SIZE(npcm8xx_gmac_addr) != ARRAY_SIZE(s->gmac)); + for (i = 0; i < ARRAY_SIZE(s->gmac); i++) { + SysBusDevice *sbd = SYS_BUS_DEVICE(&s->gmac[i]); + + /* This is used to make sure that the NIC can create the device */ + qemu_configure_nic_device(DEVICE(sbd), false, NULL); + + /* + * The device exists regardless of whether it's connected to a QEMU + * netdev backend. So always instantiate it even if there is no + * backend. + */ + sysbus_realize(sbd, &error_abort); + sysbus_mmio_map(sbd, 0, npcm8xx_gmac_addr[i]); + /* + * N.B. The values for the second argument sysbus_connect_irq are + * chosen to match the registration order in npcm7xx_emc_realize. + */ + sysbus_connect_irq(sbd, 0, npcm8xx_irq(s, NPCM8XX_GMAC1_IRQ + i)); + } + /* + * GMAC Physical Coding Sublayer(PCS) Module. Cannot fail. + */ + sysbus_realize(SYS_BUS_DEVICE(&s->pcs), &error_abort); + sysbus_mmio_map(SYS_BUS_DEVICE(&s->pcs), 0, NPCM8XX_PCS_BA); + /* * Flash Interface Unit (FIU). Can fail if incorrect number of chip selects * specified, but this is a programming error. @@ -741,12 +790,7 @@ static void npcm8xx_realize(DeviceState *dev, Error **errp) create_unimplemented_device("npcm8xx.ahbpci", 0xf0400000, 1 * MiB); create_unimplemented_device("npcm8xx.dap", 0xf0500000, 960 * KiB); create_unimplemented_device("npcm8xx.mcphy", 0xf05f0000, 64 * KiB); - create_unimplemented_device("npcm8xx.pcs", 0xf0780000, 256 * KiB); create_unimplemented_device("npcm8xx.tsgen", 0xf07fc000, 8 * KiB); - create_unimplemented_device("npcm8xx.gmac1", 0xf0802000, 8 * KiB); - create_unimplemented_device("npcm8xx.gmac2", 0xf0804000, 8 * KiB); - create_unimplemented_device("npcm8xx.gmac3", 0xf0806000, 8 * KiB); - create_unimplemented_device("npcm8xx.gmac4", 0xf0808000, 8 * KiB); create_unimplemented_device("npcm8xx.copctl", 0xf080c000, 4 * KiB); create_unimplemented_device("npcm8xx.tipctl", 0xf080d000, 4 * KiB); create_unimplemented_device("npcm8xx.rst", 0xf080e000, 4 * KiB); diff --git a/include/hw/arm/npcm8xx.h b/include/hw/arm/npcm8xx.h index 3436abff99..a8377db490 100644 --- a/include/hw/arm/npcm8xx.h +++ b/include/hw/arm/npcm8xx.h @@ -28,7 +28,8 @@ #include "hw/misc/npcm7xx_mft.h" #include "hw/misc/npcm7xx_pwm.h" #include "hw/misc/npcm7xx_rng.h" -#include "hw/net/npcm7xx_emc.h" +#include "hw/net/npcm_gmac.h" +#include "hw/net/npcm_pcs.h" #include "hw/nvram/npcm7xx_otp.h" #include "hw/sd/npcm7xx_sdhci.h" #include "hw/timer/npcm7xx_timer.h" @@ -99,6 +100,8 @@ struct NPCM8xxState { EHCISysBusState ehci[2]; OHCISysBusState ohci[2]; NPCM7xxFIUState fiu[3]; + NPCMGMACState gmac[4]; + NPCMPCSState pcs; NPCM7xxSDHCIState mmc; NPCMPSPIState pspi; }; From e7083b02f5628f0ec63d29f2208ca5d3e963e9a8 Mon Sep 17 00:00:00 2001 From: Nabih Estefan Date: Thu, 29 May 2025 17:45:09 +0100 Subject: [PATCH 1284/2760] tests/qtest: Migrate GMAC test from 7xx to 8xx For upstreaming we migrated this test to 7xx (since that was already upstream) move it back to 8xx where it can check the 4 GMACs since that is the board this test was originally created for. Signed-off-by: Nabih Estefan Message-id: 20250508220718.735415-3-nabihestefan@google.com Reviewed-by: Tyrone Ting Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- tests/qtest/meson.build | 6 ++- tests/qtest/npcm_gmac-test.c | 85 ++++++++++++++++++++++++++++++++++-- 2 files changed, 86 insertions(+), 5 deletions(-) diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 43e5a86699..8ad849054f 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -208,9 +208,10 @@ qtests_npcm7xx = \ 'npcm7xx_sdhci-test', 'npcm7xx_smbus-test', 'npcm7xx_timer-test', - 'npcm7xx_watchdog_timer-test', - 'npcm_gmac-test'] + \ + 'npcm7xx_watchdog_timer-test'] + \ (slirp.found() ? ['npcm7xx_emc-test'] : []) +qtests_npcm8xx = \ + ['npcm_gmac-test'] qtests_aspeed = \ ['aspeed_gpio-test', 'aspeed_hace-test', @@ -259,6 +260,7 @@ qtests_aarch64 = \ (config_all_accel.has_key('CONFIG_TCG') and \ config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \ (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \ + (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : []) + \ ['arm-cpu-features', 'numa-test', 'boot-serial-test', diff --git a/tests/qtest/npcm_gmac-test.c b/tests/qtest/npcm_gmac-test.c index c28b471ab2..1317da2cd7 100644 --- a/tests/qtest/npcm_gmac-test.c +++ b/tests/qtest/npcm_gmac-test.c @@ -36,7 +36,7 @@ typedef struct TestData { const GMACModule *module; } TestData; -/* Values extracted from hw/arm/npcm7xx.c */ +/* Values extracted from hw/arm/npcm8xx.c */ static const GMACModule gmac_module_list[] = { { .irq = 14, @@ -46,6 +46,14 @@ static const GMACModule gmac_module_list[] = { .irq = 15, .base_addr = 0xf0804000 }, + { + .irq = 16, + .base_addr = 0xf0806000 + }, + { + .irq = 17, + .base_addr = 0xf0808000 + } }; /* Returns the index of the GMAC module. */ @@ -174,18 +182,32 @@ static uint32_t gmac_read(QTestState *qts, const GMACModule *mod, return qtest_readl(qts, mod->base_addr + regno); } +static uint16_t pcs_read(QTestState *qts, const GMACModule *mod, + NPCMRegister regno) +{ + uint32_t write_value = (regno & 0x3ffe00) >> 9; + qtest_writel(qts, PCS_BASE_ADDRESS + NPCM_PCS_IND_AC_BA, write_value); + uint32_t read_offset = regno & 0x1ff; + return qtest_readl(qts, PCS_BASE_ADDRESS + read_offset); +} + /* Check that GMAC registers are reset to default value */ static void test_init(gconstpointer test_data) { const TestData *td = test_data; const GMACModule *mod = td->module; - QTestState *qts = qtest_init("-machine npcm750-evb"); + QTestState *qts = qtest_init("-machine npcm845-evb"); #define CHECK_REG32(regno, value) \ do { \ g_assert_cmphex(gmac_read(qts, mod, (regno)), ==, (value)); \ } while (0) +#define CHECK_REG_PCS(regno, value) \ + do { \ + g_assert_cmphex(pcs_read(qts, mod, (regno)), ==, (value)); \ + } while (0) + CHECK_REG32(NPCM_DMA_BUS_MODE, 0x00020100); CHECK_REG32(NPCM_DMA_XMT_POLL_DEMAND, 0); CHECK_REG32(NPCM_DMA_RCV_POLL_DEMAND, 0); @@ -235,6 +257,63 @@ static void test_init(gconstpointer test_data) CHECK_REG32(NPCM_GMAC_PTP_TAR, 0); CHECK_REG32(NPCM_GMAC_PTP_TTSR, 0); + if (mod->base_addr == 0xf0802000) { + CHECK_REG_PCS(NPCM_PCS_SR_CTL_ID1, 0x699e); + CHECK_REG_PCS(NPCM_PCS_SR_CTL_ID2, 0); + CHECK_REG_PCS(NPCM_PCS_SR_CTL_STS, 0x8000); + + CHECK_REG_PCS(NPCM_PCS_SR_MII_CTRL, 0x1140); + CHECK_REG_PCS(NPCM_PCS_SR_MII_STS, 0x0109); + CHECK_REG_PCS(NPCM_PCS_SR_MII_DEV_ID1, 0x699e); + CHECK_REG_PCS(NPCM_PCS_SR_MII_DEV_ID2, 0x0ced0); + CHECK_REG_PCS(NPCM_PCS_SR_MII_AN_ADV, 0x0020); + CHECK_REG_PCS(NPCM_PCS_SR_MII_LP_BABL, 0); + CHECK_REG_PCS(NPCM_PCS_SR_MII_AN_EXPN, 0); + CHECK_REG_PCS(NPCM_PCS_SR_MII_EXT_STS, 0xc000); + + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_ABL, 0x0003); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_LWR, 0x0038); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MAX_DLY_UPR, 0); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_LWR, 0x0038); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_TX_MIN_DLY_UPR, 0); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_LWR, 0x0058); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MAX_DLY_UPR, 0); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_LWR, 0x0048); + CHECK_REG_PCS(NPCM_PCS_SR_TIM_SYNC_RX_MIN_DLY_UPR, 0); + + CHECK_REG_PCS(NPCM_PCS_VR_MII_MMD_DIG_CTRL1, 0x2400); + CHECK_REG_PCS(NPCM_PCS_VR_MII_AN_CTRL, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_AN_INTR_STS, 0x000a); + CHECK_REG_PCS(NPCM_PCS_VR_MII_TC, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_DBG_CTRL, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_MCTRL0, 0x899c); + CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_TXTIMER, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_RXTIMER, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_LINK_TIMER_CTRL, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_EEE_MCTRL1, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_DIG_STS, 0x0010); + CHECK_REG_PCS(NPCM_PCS_VR_MII_ICG_ERRCNT1, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MISC_STS, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_RX_LSTS, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_BSTCTRL0, 0x00a); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_LVLCTRL0, 0x007f); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_GENCTRL0, 0x0001); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_GENCTRL1, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_TX_STS, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_RX_GENCTRL0, 0x0100); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_RX_GENCTRL1, 0x1100); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_RX_LOS_CTRL0, 0x000e); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MPLL_CTRL0, 0x0100); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MPLL_CTRL1, 0x0032); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MPLL_STS, 0x0001); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MISC_CTRL2, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_LVL_CTRL, 0x0019); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MISC_CTRL0, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_MP_MISC_CTRL1, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_DIG_CTRL2, 0); + CHECK_REG_PCS(NPCM_PCS_VR_MII_DIG_ERRCNT_SEL, 0); + } + qtest_quit(qts); } @@ -242,7 +321,7 @@ static void gmac_add_test(const char *name, const TestData* td, GTestDataFunc fn) { g_autofree char *full_name = g_strdup_printf( - "npcm7xx_gmac/gmac[%d]/%s", gmac_module_index(td->module), name); + "npcm8xx_gmac/gmac[%d]/%s", gmac_module_index(td->module), name); qtest_add_data_func(full_name, td, fn); } From e6bc01777e5a4b6ecf3414b21a2d7b4846bf4817 Mon Sep 17 00:00:00 2001 From: Guenter Roeck Date: Thu, 29 May 2025 17:45:10 +0100 Subject: [PATCH 1285/2760] hw/arm: Add missing psci_conduit to NPCM8XX SoC boot info Without psci_conduit, the Linux kernel crashes almost immediately. psci: probing for conduit method from DT. Internal error: Oops - Undefined instruction: 0000000002000000 [#1] PREEMPT SMP Fixes: ae0c4d1a1290 ("hw/arm: Add NPCM8XX SoC") Cc: qemu-stable@nongnu.org Cc: Hao Wu Cc: Peter Maydell Signed-off-by: Guenter Roeck Message-id: 20250315142050.3642741-1-linux@roeck-us.net Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/npcm8xx.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/npcm8xx.c b/hw/arm/npcm8xx.c index d14bf55cd7..a276fea698 100644 --- a/hw/arm/npcm8xx.c +++ b/hw/arm/npcm8xx.c @@ -365,6 +365,7 @@ static struct arm_boot_info npcm8xx_binfo = { .secure_boot = false, .board_id = -1, .board_setup_addr = NPCM8XX_BOARD_SETUP_ADDR, + .psci_conduit = QEMU_PSCI_CONDUIT_SMC, }; void npcm8xx_load_kernel(MachineState *machine, NPCM8xxState *soc) From 0a233da8a02a6126140b9dbd3af29e6763a390b1 Mon Sep 17 00:00:00 2001 From: Souleymane Conte Date: Thu, 29 May 2025 17:45:10 +0100 Subject: [PATCH 1286/2760] docs/interop: convert text files to restructuredText buglink: https://gitlab.com/qemu-project/qemu/-/issues/527 Signed-off-by: Souleymane Conte Reviewed-by: Peter Maydell Reviewed-by: Eric Blake Message-id: 20250522092622.40869-1-conte.souleymane@gmail.com [PMM: switched a few more bits of formatting to monospaced; updated references to qcow2.txt in MAINTAINERS, qcow2-cache.txt and bitmaps.rst] Signed-off-by: Peter Maydell --- MAINTAINERS | 2 +- docs/interop/bitmaps.rst | 2 +- docs/interop/index.rst | 1 + docs/interop/{qcow2.txt => qcow2.rst} | 187 +++++++++++++++----------- docs/qcow2-cache.txt | 2 +- 5 files changed, 113 insertions(+), 81 deletions(-) rename docs/interop/{qcow2.txt => qcow2.rst} (89%) diff --git a/MAINTAINERS b/MAINTAINERS index e27d1458c5..8e68333623 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4130,7 +4130,7 @@ M: Hanna Reitz L: qemu-block@nongnu.org S: Supported F: block/qcow2* -F: docs/interop/qcow2.txt +F: docs/interop/qcow2.rst qcow M: Kevin Wolf diff --git a/docs/interop/bitmaps.rst b/docs/interop/bitmaps.rst index ddf8947d54..7536f0ba5c 100644 --- a/docs/interop/bitmaps.rst +++ b/docs/interop/bitmaps.rst @@ -97,7 +97,7 @@ time. - Persistent storage formats may impose their own requirements on bitmap names and namespaces. Presently, only qcow2 supports persistent bitmaps. See - docs/interop/qcow2.txt for more details on restrictions. Notably: + :doc:`qcow2` for more details on restrictions. Notably: - qcow2 bitmap names are limited to between 1 and 1023 bytes long. diff --git a/docs/interop/index.rst b/docs/interop/index.rst index 999e44eae1..4b951ae416 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -17,6 +17,7 @@ are useful for making QEMU interoperate with other software. nbd parallels prl-xml + qcow2 pr-helper qmp-spec qemu-ga diff --git a/docs/interop/qcow2.txt b/docs/interop/qcow2.rst similarity index 89% rename from docs/interop/qcow2.txt rename to docs/interop/qcow2.rst index 2c4618375a..5948591107 100644 --- a/docs/interop/qcow2.txt +++ b/docs/interop/qcow2.rst @@ -1,6 +1,8 @@ -== General == +======================= +Qcow2 Image File Format +======================= -A qcow2 image file is organized in units of constant size, which are called +A ``qcow2`` image file is organized in units of constant size, which are called (host) clusters. A cluster is the unit in which all allocations are done, both for actual guest data and for image metadata. @@ -9,10 +11,10 @@ clusters of the same size. All numbers in qcow2 are stored in Big Endian byte order. +Header +------ -== Header == - -The first cluster of a qcow2 image contains the file header: +The first cluster of a qcow2 image contains the file header:: Byte 0 - 3: magic QCOW magic string ("QFI\xfb") @@ -38,7 +40,7 @@ The first cluster of a qcow2 image contains the file header: within a cluster (1 << cluster_bits is the cluster size). Must not be less than 9 (i.e. 512 byte clusters). - Note: qemu as of today has an implementation limit of 2 MB + Note: QEMU as of today has an implementation limit of 2 MB as the maximum cluster size and won't be able to open images with larger cluster sizes. @@ -48,7 +50,7 @@ The first cluster of a qcow2 image contains the file header: 24 - 31: size Virtual disk size in bytes. - Note: qemu has an implementation limit of 32 MB as + Note: QEMU has an implementation limit of 32 MB as the maximum L1 table size. With a 2 MB cluster size, it is unable to populate a virtual cluster beyond 2 EB (61 bits); with a 512 byte cluster @@ -87,7 +89,8 @@ The first cluster of a qcow2 image contains the file header: For version 2, the header is exactly 72 bytes in length, and finishes here. For version 3 or higher, the header length is at least 104 bytes, including -the next fields through header_length. +the next fields through ``header_length``. +:: 72 - 79: incompatible_features Bitmask of incompatible features. An implementation must @@ -185,7 +188,8 @@ the next fields through header_length. of 8. -=== Additional fields (version 3 and higher) === +Additional fields (version 3 and higher) +---------------------------------------- In general, these fields are optional and may be safely ignored by the software, as well as filled by zeros (which is equal to field absence), if software needs @@ -193,21 +197,25 @@ to set field B, but does not care about field A which precedes B. More formally, additional fields have the following compatibility rules: 1. If the value of the additional field must not be ignored for correct -handling of the file, it will be accompanied by a corresponding incompatible -feature bit. + handling of the file, it will be accompanied by a corresponding incompatible + feature bit. 2. If there are no unrecognized incompatible feature bits set, an unknown -additional field may be safely ignored other than preserving its value when -rewriting the image header. + additional field may be safely ignored other than preserving its value when + rewriting the image header. + +.. _ref_rules_3: 3. An explicit value of 0 will have the same behavior as when the field is not -present*, if not altered by a specific incompatible bit. + present*, if not altered by a specific incompatible bit. -*. A field is considered not present when header_length is less than or equal +(*) A field is considered not present when ``header_length`` is less than or equal to the field's offset. Also, all additional fields are not present for version 2. - 104: compression_type +:: + + 104: compression_type Defines the compression method used for compressed clusters. All compressed clusters in an image use the same compression @@ -219,8 +227,8 @@ version 2. or must be zero (which means deflate). Available compression type values: - 0: deflate - 1: zstd + - 0: deflate + - 1: zstd The deflate compression type is called "zlib" in QEMU. However, clusters with the @@ -228,19 +236,21 @@ version 2. 105 - 111: Padding, contents defined below. -=== Header padding === +Header padding +-------------- -@header_length must be a multiple of 8, which means that if the end of the last +``header_length`` must be a multiple of 8, which means that if the end of the last additional field is not aligned, some padding is needed. This padding must be zeroed, so that if some existing (or future) additional field will fall into -the padding, it will be interpreted accordingly to point [3.] of the previous +the padding, it will be interpreted accordingly to point `[3.] <#ref_rules_3>`_ of the previous paragraph, i.e. in the same manner as when this field is not present. -=== Header extensions === +Header extensions +----------------- Directly after the image header, optional sections called header extensions can -be stored. Each extension has a structure like the following: +be stored. Each extension has a structure like the following:: Byte 0 - 3: Header extension type: 0x00000000 - End of the header extension area @@ -270,17 +280,19 @@ data of compatible features that it doesn't support. Compatible features that need space for additional data can use a header extension. -== String header extensions == +String header extensions +------------------------ Some header extensions (such as the backing file format name and the external data file name) are just a single string. In this case, the header extension -length is the string length and the string is not '\0' terminated. (The header -extension padding can make it look like a string is '\0' terminated, but +length is the string length and the string is not ``\0`` terminated. (The header +extension padding can make it look like a string is ``\0`` terminated, but neither is padding always necessary nor is there a guarantee that zero bytes are used for padding.) -== Feature name table == +Feature name table +------------------ The feature name table is an optional header extension that contains the name for features used by the image. It can be used by applications that don't know @@ -288,7 +300,7 @@ the respective feature (e.g. because the feature was introduced only later) to display a useful error message. The number of entries in the feature name table is determined by the length of -the header extension data. Each entry look like this: +the header extension data. Each entry looks like this:: Byte 0: Type of feature (select feature bitmap) 0: Incompatible feature @@ -302,7 +314,8 @@ the header extension data. Each entry look like this: terminated if it has full length) -== Bitmaps extension == +Bitmaps extension +----------------- The bitmaps extension is an optional header extension. It provides the ability to store bitmaps related to a virtual disk. For now, there is only one bitmap @@ -310,9 +323,9 @@ type: the dirty tracking bitmap, which tracks virtual disk changes from some point in time. The data of the extension should be considered consistent only if the -corresponding auto-clear feature bit is set, see autoclear_features above. +corresponding auto-clear feature bit is set, see ``autoclear_features`` above. -The fields of the bitmaps extension are: +The fields of the bitmaps extension are:: Byte 0 - 3: nb_bitmaps The number of bitmaps contained in the image. Must be @@ -331,15 +344,17 @@ The fields of the bitmaps extension are: Offset into the image file at which the bitmap directory starts. Must be aligned to a cluster boundary. -== Full disk encryption header pointer == +Full disk encryption header pointer +----------------------------------- The full disk encryption header must be present if, and only if, the -'crypt_method' header requires metadata. Currently this is only true -of the 'LUKS' crypt method. The header extension must be absent for +``crypt_method`` header requires metadata. Currently this is only true +of the ``LUKS`` crypt method. The header extension must be absent for other methods. This header provides the offset at which the crypt method can store its additional data, as well as the length of such data. +:: Byte 0 - 7: Offset into the image file at which the encryption header starts in bytes. Must be aligned to a cluster @@ -357,10 +372,10 @@ The first 592 bytes of the header clusters will contain the LUKS partition header. This is then followed by the key material data areas. The size of the key material data areas is determined by the number of stripes in the key slot and key size. Refer to the LUKS format -specification ('docs/on-disk-format.pdf' in the cryptsetup source +specification (``docs/on-disk-format.pdf`` in the cryptsetup source package) for details of the LUKS partition header format. -In the LUKS partition header, the "payload-offset" field will be +In the LUKS partition header, the ``payload-offset`` field will be calculated as normal for the LUKS spec. ie the size of the LUKS header, plus key material regions, plus padding, relative to the start of the LUKS header. This offset value is not required to be @@ -369,11 +384,12 @@ context of qcow2, since the qcow2 file format itself defines where the real payload offset is, but none the less a valid payload offset should always be present. -In the LUKS key slots header, the "key-material-offset" is relative +In the LUKS key slots header, the ``key-material-offset`` is relative to the start of the LUKS header clusters in the qcow2 container, not the start of the qcow2 file. Logically the layout looks like +:: +-----------------------------+ | QCow2 header | @@ -405,7 +421,8 @@ Logically the layout looks like | | +-----------------------------+ -== Data encryption == +Data encryption +--------------- When an encryption method is requested in the header, the image payload data must be encrypted/decrypted on every write/read. The image headers @@ -413,7 +430,7 @@ and metadata are never encrypted. The algorithms used for encryption vary depending on the method - - AES: + - ``AES``: The AES cipher, in CBC mode, with 256 bit keys. @@ -425,7 +442,7 @@ The algorithms used for encryption vary depending on the method supported in the command line tools for the sake of back compatibility and data liberation. - - LUKS: + - ``LUKS``: The algorithms are specified in the LUKS header. @@ -433,7 +450,8 @@ The algorithms used for encryption vary depending on the method in the LUKS header, with the physical disk sector as the input tweak. -== Host cluster management == +Host cluster management +----------------------- qcow2 manages the allocation of host clusters by maintaining a reference count for each host cluster. A refcount of 0 means that the cluster is free, 1 means @@ -453,14 +471,15 @@ Although a large enough refcount table can reserve clusters past 64 PB large), note that some qcow2 metadata such as L1/L2 tables must point to clusters prior to that point. -Note: qemu has an implementation limit of 8 MB as the maximum refcount -table size. With a 2 MB cluster size and a default refcount_order of -4, it is unable to reference host resources beyond 2 EB (61 bits); in -the worst case, with a 512 cluster size and refcount_order of 6, it is -unable to access beyond 32 GB (35 bits). +.. note:: + QEMU has an implementation limit of 8 MB as the maximum refcount + table size. With a 2 MB cluster size and a default refcount_order of + 4, it is unable to reference host resources beyond 2 EB (61 bits); in + the worst case, with a 512 cluster size and refcount_order of 6, it is + unable to access beyond 32 GB (35 bits). Given an offset into the image file, the refcount of its cluster can be -obtained as follows: +obtained as follows:: refcount_block_entries = (cluster_size * 8 / refcount_bits) @@ -470,7 +489,7 @@ obtained as follows: refcount_block = load_cluster(refcount_table[refcount_table_index]); return refcount_block[refcount_block_index]; -Refcount table entry: +Refcount table entry:: Bit 0 - 8: Reserved (set to 0) @@ -482,14 +501,15 @@ Refcount table entry: been allocated. All refcounts managed by this refcount block are 0. -Refcount block entry (x = refcount_bits - 1): +Refcount block entry ``(x = refcount_bits - 1)``:: Bit 0 - x: Reference count of the cluster. If refcount_bits implies a sub-byte width, note that bit 0 means the least significant bit in this context. -== Cluster mapping == +Cluster mapping +--------------- Just as for refcounts, qcow2 uses a two-level structure for the mapping of guest clusters to host clusters. They are called L1 and L2 table. @@ -509,7 +529,7 @@ compressed clusters to reside below 512 TB (49 bits), and this limit cannot be relaxed without an incompatible layout change). Given an offset into the virtual disk, the offset into the image file can be -obtained as follows: +obtained as follows:: l2_entries = (cluster_size / sizeof(uint64_t)) [*] @@ -523,7 +543,7 @@ obtained as follows: [*] this changes if Extended L2 Entries are enabled, see next section -L1 table entry: +L1 table entry:: Bit 0 - 8: Reserved (set to 0) @@ -538,7 +558,7 @@ L1 table entry: refcount is exactly one. This information is only accurate in the active L1 table. -L2 table entry: +L2 table entry:: Bit 0 - 61: Cluster descriptor @@ -555,7 +575,7 @@ L2 table entry: mapping for guest cluster offsets), so this bit should be 1 for all allocated clusters. -Standard Cluster Descriptor: +Standard Cluster Descriptor:: Bit 0: If set to 1, the cluster reads as all zeros. The host cluster offset can be used to describe a preallocation, @@ -577,7 +597,7 @@ Standard Cluster Descriptor: 56 - 61: Reserved (set to 0) -Compressed Clusters Descriptor (x = 62 - (cluster_bits - 8)): +Compressed Clusters Descriptor ``(x = 62 - (cluster_bits - 8))``:: Bit 0 - x-1: Host cluster offset. This is usually _not_ aligned to a cluster or sector boundary! If cluster_bits is @@ -601,7 +621,8 @@ file (except if bit 0 in the Standard Cluster Descriptor is set). If there is no backing file or the backing file is smaller than the image, they shall read zeros for all parts that are not covered by the backing file. -== Extended L2 Entries == +Extended L2 Entries +------------------- An image uses Extended L2 Entries if bit 4 is set on the incompatible_features field of the header. @@ -615,6 +636,8 @@ subclusters so they are treated the same as in images without this feature. The size of an extended L2 entry is 128 bits so the number of entries per table is calculated using this formula: +.. code:: + l2_entries = (cluster_size / (2 * sizeof(uint64_t))) The first 64 bits have the same format as the standard L2 table entry described @@ -623,7 +646,7 @@ descriptor. The last 64 bits contain a subcluster allocation bitmap with this format: -Subcluster Allocation Bitmap (for standard clusters): +Subcluster Allocation Bitmap (for standard clusters):: Bit 0 - 31: Allocation status (one bit per subcluster) @@ -647,13 +670,14 @@ Subcluster Allocation Bitmap (for standard clusters): Bits are assigned starting from the least significant one (i.e. bit x is used for subcluster x - 32). -Subcluster Allocation Bitmap (for compressed clusters): +Subcluster Allocation Bitmap (for compressed clusters):: Bit 0 - 63: Reserved (set to 0) Compressed clusters don't have subclusters, so this field is not used. -== Snapshots == +Snapshots +--------- qcow2 supports internal snapshots. Their basic principle of operation is to switch the active L1 table, so that a different set of host clusters are @@ -672,7 +696,7 @@ in the image file, whose starting offset and length are given by the header fields snapshots_offset and nb_snapshots. The entries of the snapshot table have variable length, depending on the length of ID, name and extra data. -Snapshot table entry: +Snapshot table entry:: Byte 0 - 7: Offset into the image file at which the L1 table for the snapshot starts. Must be aligned to a cluster boundary. @@ -728,7 +752,8 @@ Snapshot table entry: next multiple of 8. -== Bitmaps == +Bitmaps +------- As mentioned above, the bitmaps extension provides the ability to store bitmaps related to a virtual disk. This section describes how these bitmaps are stored. @@ -739,20 +764,23 @@ each bitmap size is equal to the virtual disk size. Each bit of the bitmap is responsible for strictly defined range of the virtual disk. For bit number bit_nr the corresponding range (in bytes) will be: +.. code:: + [bit_nr * bitmap_granularity .. (bit_nr + 1) * bitmap_granularity - 1] Granularity is a property of the concrete bitmap, see below. -=== Bitmap directory === +Bitmap directory +---------------- Each bitmap saved in the image is described in a bitmap directory entry. The bitmap directory is a contiguous area in the image file, whose starting offset -and length are given by the header extension fields bitmap_directory_offset and -bitmap_directory_size. The entries of the bitmap directory have variable +and length are given by the header extension fields ``bitmap_directory_offset`` and +``bitmap_directory_size``. The entries of the bitmap directory have variable length, depending on the lengths of the bitmap name and extra data. -Structure of a bitmap directory entry: +Structure of a bitmap directory entry:: Byte 0 - 7: bitmap_table_offset Offset into the image file at which the bitmap table @@ -833,7 +861,8 @@ Structure of a bitmap directory entry: next multiple of 8. All bytes of the padding must be zero. -=== Bitmap table === +Bitmap table +------------ Each bitmap is stored using a one-level structure (as opposed to two-level structures like for refcounts and guest clusters mapping) for the mapping of @@ -843,7 +872,7 @@ Each bitmap table has a variable size (stored in the bitmap directory entry) and may use multiple clusters, however, it must be contiguous in the image file. -Structure of a bitmap table entry: +Structure of a bitmap table entry:: Bit 0: Reserved and must be zero if bits 9 - 55 are non-zero. If bits 9 - 55 are zero: @@ -860,11 +889,12 @@ Structure of a bitmap table entry: 56 - 63: Reserved and must be zero. -=== Bitmap data === +Bitmap data +----------- As noted above, bitmap data is stored in separate clusters, described by the bitmap table. Given an offset (in bytes) into the bitmap data, the offset into -the image file can be obtained as follows: +the image file can be obtained as follows:: image_offset(bitmap_data_offset) = bitmap_table[bitmap_data_offset / cluster_size] + @@ -875,7 +905,7 @@ above). Given an offset byte_nr into the virtual disk and the bitmap's granularity, the bit offset into the image file to the corresponding bit of the bitmap can be -calculated like this: +calculated like this:: bit_offset(byte_nr) = image_offset(byte_nr / granularity / 8) * 8 + @@ -886,21 +916,22 @@ last cluster of the bitmap data contains some unused tail bits. These bits must be zero. -=== Dirty tracking bitmaps === +Dirty tracking bitmaps +---------------------- -Bitmaps with 'type' field equal to one are dirty tracking bitmaps. +Bitmaps with ``type`` field equal to one are dirty tracking bitmaps. -When the virtual disk is in use dirty tracking bitmap may be 'enabled' or -'disabled'. While the bitmap is 'enabled', all writes to the virtual disk +When the virtual disk is in use dirty tracking bitmap may be ``enabled`` or +``disabled``. While the bitmap is ``enabled``, all writes to the virtual disk should be reflected in the bitmap. A set bit in the bitmap means that the corresponding range of the virtual disk (see above) was written to while the -bitmap was 'enabled'. An unset bit means that this range was not written to. +bitmap was ``enabled``. An unset bit means that this range was not written to. The software doesn't have to sync the bitmap in the image file with its -representation in RAM after each write or metadata change. Flag 'in_use' +representation in RAM after each write or metadata change. Flag ``in_use`` should be set while the bitmap is not synced. -In the image file the 'enabled' state is reflected by the 'auto' flag. If this -flag is set, the software must consider the bitmap as 'enabled' and start +In the image file the ``enabled`` state is reflected by the ``auto`` flag. If this +flag is set, the software must consider the bitmap as ``enabled`` and start tracking virtual disk changes to this bitmap from the first write to the virtual disk. If this flag is not set then the bitmap is disabled. diff --git a/docs/qcow2-cache.txt b/docs/qcow2-cache.txt index 5f763aa6bb..204a5741ad 100644 --- a/docs/qcow2-cache.txt +++ b/docs/qcow2-cache.txt @@ -15,7 +15,7 @@ not a straightforward operation. This document attempts to give an overview of the L2 and refcount caches, and how to configure them. -Please refer to the docs/interop/qcow2.txt file for an in-depth +Please refer to the docs/interop/qcow2.rst file for an in-depth technical description of the qcow2 file format. From 4f8599f20c4a54cefc626b50dc7392dd82b422f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 May 2025 17:45:10 +0100 Subject: [PATCH 1287/2760] target/arm/tcg-stubs: compile file once (system) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-id: 20250513173928.77376-2-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index b404fa5486..e568dfb706 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -48,7 +48,7 @@ subdir('hvf') if 'CONFIG_TCG' in config_all_accel subdir('tcg') else - arm_ss.add(files('tcg-stubs.c')) + arm_common_system_ss.add(files('tcg-stubs.c')) endif target_arch += {'arm': arm_ss} From f1bcfa81d84c2a7401f6dcbf8fc1c7b26ec18213 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 May 2025 17:45:10 +0100 Subject: [PATCH 1288/2760] target/arm/hvf_arm: Avoid using poisoned CONFIG_HVF definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to allow non-target specific code to include "hvf_arm.h", define the stubs in hvf-stub.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-id: 20250513173928.77376-3-philmd@linaro.org Signed-off-by: Peter Maydell --- MAINTAINERS | 1 + target/arm/hvf-stub.c | 20 ++++++++++++++++++++ target/arm/hvf_arm.h | 16 ---------------- target/arm/meson.build | 1 + 4 files changed, 22 insertions(+), 16 deletions(-) create mode 100644 target/arm/hvf-stub.c diff --git a/MAINTAINERS b/MAINTAINERS index 8e68333623..973254fae7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -510,6 +510,7 @@ Apple Silicon HVF CPUs M: Alexander Graf S: Maintained F: target/arm/hvf/ +F: target/arm/hvf-stub.c X86 HVF CPUs M: Cameron Esfahani diff --git a/target/arm/hvf-stub.c b/target/arm/hvf-stub.c new file mode 100644 index 0000000000..ff137267a0 --- /dev/null +++ b/target/arm/hvf-stub.c @@ -0,0 +1,20 @@ +/* + * QEMU Hypervisor.framework (HVF) stubs for ARM + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hvf_arm.h" + +uint32_t hvf_arm_get_default_ipa_bit_size(void) +{ + g_assert_not_reached(); +} + +uint32_t hvf_arm_get_max_ipa_bit_size(void) +{ + g_assert_not_reached(); +} diff --git a/target/arm/hvf_arm.h b/target/arm/hvf_arm.h index 26c717b382..7a44e09262 100644 --- a/target/arm/hvf_arm.h +++ b/target/arm/hvf_arm.h @@ -22,23 +22,7 @@ void hvf_arm_init_debug(void); void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu); -#ifdef CONFIG_HVF - uint32_t hvf_arm_get_default_ipa_bit_size(void); uint32_t hvf_arm_get_max_ipa_bit_size(void); -#else - -static inline uint32_t hvf_arm_get_default_ipa_bit_size(void) -{ - return 0; -} - -static inline uint32_t hvf_arm_get_max_ipa_bit_size(void) -{ - return 0; -} - -#endif - #endif diff --git a/target/arm/meson.build b/target/arm/meson.build index e568dfb706..2747f4b404 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -32,6 +32,7 @@ arm_common_system_ss.add(files('cpu.c'), capstone) arm_common_system_ss.add(when: 'TARGET_AARCH64', if_false: files( 'cpu32-stubs.c')) arm_common_system_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) +arm_common_system_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c')) arm_common_system_ss.add(files( 'arch_dump.c', 'arm-powerctl.c', From 54d1046f939c2c508a6b73531af83345306ecaa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 May 2025 17:45:11 +0100 Subject: [PATCH 1289/2760] target/arm: Only link with zlib when TCG is enabled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 538b764d341 ("target/arm: Move minor arithmetic helpers out of helper.c") we only use the zlib helpers under TCG. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-id: 20250513173928.77376-4-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/meson.build | 1 - target/arm/tcg/meson.build | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/meson.build b/target/arm/meson.build index 2747f4b404..dcba4ef379 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -3,7 +3,6 @@ arm_common_ss = ss.source_set() arm_ss.add(files( 'gdbstub.c', )) -arm_ss.add(zlib) arm_ss.add(when: 'TARGET_AARCH64', if_true: files( 'cpu64.c', diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index 2d1502ba88..c59f0f03a1 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -56,6 +56,8 @@ arm_system_ss.add(files( arm_system_ss.add(when: 'CONFIG_ARM_V7M', if_true: files('cpu-v7m.c')) arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files('cpu-v7m.c')) +arm_common_ss.add(zlib) + arm_common_ss.add(files( 'arith_helper.c', 'crypto_helper.c', From 982a42c773efc7767e33d618f0fe4fcaef716a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 May 2025 17:45:11 +0100 Subject: [PATCH 1290/2760] target/arm/cpregs: Include missing 'target/arm/cpu.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPReadFn type definitions use the CPUARMState type, itself declared in "cpu.h". Include this file in order to avoid when refactoring headers: ../target/arm/cpregs.h:241:27: error: unknown type name 'CPUARMState' typedef uint64_t CPReadFn(CPUARMState *env, const ARMCPRegInfo *opaque); ^ Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-id: 20250513173928.77376-5-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpregs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index 2183de8eda..c1a7ae3735 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -23,6 +23,7 @@ #include "hw/registerfields.h" #include "target/arm/kvm-consts.h" +#include "cpu.h" /* * ARMCPRegInfo type field bits: From c28900fbcdc36175a1fc81015e952133007ae864 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 May 2025 17:45:11 +0100 Subject: [PATCH 1291/2760] hw/arm/boot: Include missing 'system/memory.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit default_reset_secondary() uses address_space_stl_notdirty(), itself declared in "system/memory.h". Include this header in order to avoid when refactoring headers: ../hw/arm/boot.c:281:5: error: implicit declaration of function 'address_space_stl_notdirty' is invalid in C99 [-Werror,-Wimplicit-function-declaration] address_space_stl_notdirty(as, info->smp_bootreg_addr, ^ Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-id: 20250513173928.77376-6-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/boot.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index f94b940bc3..79afb51b8a 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -19,6 +19,7 @@ #include "system/kvm.h" #include "system/tcg.h" #include "system/system.h" +#include "system/memory.h" #include "system/numa.h" #include "hw/boards.h" #include "system/reset.h" From fe5aa1cfe22c71c80e544cf97741036cba0556d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 May 2025 17:45:11 +0100 Subject: [PATCH 1292/2760] target/arm/cpu-features: Include missing 'cpu.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "target/arm/cpu-features.h" dereferences the ARMISARegisters structure, which is defined in "cpu.h". Include the latter to avoid when refactoring unrelated headers: In file included from target/arm/internals.h:33: target/arm/cpu-features.h:45:54: error: unknown type name 'ARMISARegisters' 45 | static inline bool isar_feature_aa32_thumb_div(const ARMISARegisters *id) | ^ target/arm/cpu-features.h:47:12: error: use of undeclared identifier 'R_ID_ISAR0_DIVIDE_SHIFT' 47 | return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) != 0; | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-id: 20250513173928.77376-7-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 525e4cee12..4452e7c21e 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -22,6 +22,7 @@ #include "hw/registerfields.h" #include "qemu/host-utils.h" +#include "cpu.h" /* * Naming convention for isar_feature functions: From e0f224ec077d90c10288a4f73d01a264b0364e46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 May 2025 17:45:12 +0100 Subject: [PATCH 1293/2760] target/arm/qmp: Include missing 'cpu.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arm-qmp-cmds.c uses ARM_MAX_VQ, which is defined in "cpu.h". Include the latter to avoid when refactoring unrelated headers: target/arm/arm-qmp-cmds.c:83:19: error: use of undeclared identifier 'ARM_MAX_VQ' 83 | QEMU_BUILD_BUG_ON(ARM_MAX_VQ > 16); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-id: 20250513173928.77376-8-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/arm-qmp-cmds.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c index cca6b9722b..cefd235263 100644 --- a/target/arm/arm-qmp-cmds.c +++ b/target/arm/arm-qmp-cmds.c @@ -30,6 +30,7 @@ #include "qapi/qapi-commands-misc-arm.h" #include "qobject/qdict.h" #include "qom/qom-qobject.h" +#include "cpu.h" static GICCapability *gic_cap_new(int version) { From 3d28b2ce00e8b5e74e1f5cbba13cf306b8360762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 May 2025 17:45:12 +0100 Subject: [PATCH 1294/2760] target/arm/kvm: Include missing 'cpu-qom.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARMCPU typedef is declared in "cpu-qom.h". Include it in order to avoid when refactoring unrelated headers: target/arm/kvm_arm.h:54:29: error: unknown type name 'ARMCPU' 54 | bool write_list_to_kvmstate(ARMCPU *cpu, int level); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-id: 20250513173928.77376-9-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/kvm_arm.h | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index c4178d1327..7dc83caed5 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -12,6 +12,7 @@ #define QEMU_KVM_ARM_H #include "system/kvm.h" +#include "target/arm/cpu-qom.h" #define KVM_ARM_VGIC_V2 (1 << 0) #define KVM_ARM_VGIC_V3 (1 << 1) From c42300ef71ba103a9bf67ca64cb94d5d9b633175 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 29 May 2025 17:45:12 +0100 Subject: [PATCH 1295/2760] target/arm/hvf: Include missing 'cpu-qom.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ARMCPU typedef is declared in "cpu-qom.h". Include it in order to avoid when refactoring unrelated headers: target/arm/hvf_arm.h:23:41: error: unknown type name 'ARMCPU' 23 | void hvf_arm_set_cpu_features_from_host(ARMCPU *cpu); | ^ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-id: 20250513173928.77376-10-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/hvf_arm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/hvf_arm.h b/target/arm/hvf_arm.h index 7a44e09262..ea82f2691d 100644 --- a/target/arm/hvf_arm.h +++ b/target/arm/hvf_arm.h @@ -11,7 +11,7 @@ #ifndef QEMU_HVF_ARM_H #define QEMU_HVF_ARM_H -#include "cpu.h" +#include "target/arm/cpu-qom.h" /** * hvf_arm_init_debug() - initialize guest debug capabilities From 96778e69a2e391e5dc99d2318e7830695c607795 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 29 May 2025 17:45:12 +0100 Subject: [PATCH 1296/2760] tests/functional: Add a test for the Stellaris arm machines The 2023 edition of the QEMU advent calendar featured an image that we can use to test whether the lm3s6965evb machine is basically still working. And for the lm3s811evb there is a small test kernel on github which can be used to check its UART. Signed-off-by: Thomas Huth Message-id: 20250519170242.520805-1-thuth@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- MAINTAINERS | 1 + tests/functional/meson.build | 1 + tests/functional/test_arm_stellaris.py | 48 ++++++++++++++++++++++++++ 3 files changed, 50 insertions(+) create mode 100755 tests/functional/test_arm_stellaris.py diff --git a/MAINTAINERS b/MAINTAINERS index 973254fae7..16af37986a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1004,6 +1004,7 @@ F: hw/display/ssd03* F: include/hw/input/gamepad.h F: include/hw/timer/stellaris-gptm.h F: docs/system/arm/stellaris.rst +F: tests/functional/test_arm_stellaris.py STM32L4x5 SoC Family M: Samuel Tardieu diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 52b4706cfe..557d59ddf4 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -137,6 +137,7 @@ tests_arm_system_thorough = [ 'arm_raspi2', 'arm_replay', 'arm_smdkc210', + 'arm_stellaris', 'arm_sx1', 'arm_vexpress', 'arm_virt', diff --git a/tests/functional/test_arm_stellaris.py b/tests/functional/test_arm_stellaris.py new file mode 100755 index 0000000000..cbd21cb1a0 --- /dev/null +++ b/tests/functional/test_arm_stellaris.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# +# Functional test that checks the serial console of the stellaris machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern + + +class StellarisMachine(QemuSystemTest): + + ASSET_DAY22 = Asset( + 'https://www.qemu-advent-calendar.org/2023/download/day22.tar.gz', + 'ae3a63ef4b7a22c21bfc7fc0d85e402fe95e223308ed23ac854405016431ff51') + + def test_lm3s6965evb(self): + self.set_machine('lm3s6965evb') + kernel_path = self.archive_extract(self.ASSET_DAY22, + member='day22/day22.bin') + self.vm.set_console() + self.vm.add_args('-kernel', kernel_path) + self.vm.launch() + + wait_for_console_pattern(self, 'In a one horse open') + + ASSET_NOTMAIN = Asset( + 'https://github.com/Ahelion/QemuArmM4FDemoSw/raw/master/build/notmain.bin', + '6ceda031aa081a420fca2fca9e137fa681d6e3820d820ad1917736cb265e611a') + + def test_lm3s811evb(self): + self.set_machine('lm3s811evb') + kernel_path = self.ASSET_NOTMAIN.fetch() + + self.vm.set_console() + self.vm.add_args('-cpu', 'cortex-m4') + self.vm.add_args('-kernel', kernel_path) + self.vm.launch() + + # The test kernel emits an initial '!' and then waits for input. + # For each character that we send it responds with a certain + # other ASCII character. + wait_for_console_pattern(self, '!') + exec_command_and_wait_for_pattern(self, '789', 'cdf') + + +if __name__ == '__main__': + QemuSystemTest.main() From e86c1f967a323165d13bcadfad4b92d0d34cdb08 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 29 May 2025 17:45:13 +0100 Subject: [PATCH 1297/2760] hw/block: Drop unused nand.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nand.c device (TYPE_NAND) is an emulation of a NAND flash memory chip which was used by the old OMAP boards. No current QEMU board uses it, and although techically "-device nand,chip-id=0x6b" doesn't error out, it's not possible to usefully use it from the command line because the only interface it has is via calling C functions like nand_setpins() and nand_setio(). The "config OMAP" stanza (used only by the SX1 board) is the only thing that does "select NAND" to compile in this code, but the SX1 board doesn't actually use the NAND device. Remove the NAND device code entirely; this is effectively leftover cleanup from when we dropped the PXA boards and the OMAP boards other than the sx1. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250522142859.3122389-1-peter.maydell@linaro.org --- hw/arm/Kconfig | 1 - hw/block/Kconfig | 3 - hw/block/meson.build | 1 - hw/block/nand.c | 835 --------------------------------------- include/hw/block/flash.h | 18 - 5 files changed, 858 deletions(-) delete mode 100644 hw/block/nand.c diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index a55b44d7bd..f543d944c3 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -147,7 +147,6 @@ config OMAP bool select FRAMEBUFFER select I2C - select NAND select PFLASH_CFI01 select SD select SERIAL_MM diff --git a/hw/block/Kconfig b/hw/block/Kconfig index a898e04f03..737dbcdb3e 100644 --- a/hw/block/Kconfig +++ b/hw/block/Kconfig @@ -13,9 +13,6 @@ config FDC_SYSBUS config SSI_M25P80 bool -config NAND - bool - config PFLASH_CFI01 bool diff --git a/hw/block/meson.build b/hw/block/meson.build index 16a51bf8e2..655704471a 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -6,7 +6,6 @@ system_ss.add(files( system_ss.add(when: 'CONFIG_FDC', if_true: files('fdc.c')) system_ss.add(when: 'CONFIG_FDC_ISA', if_true: files('fdc-isa.c')) system_ss.add(when: 'CONFIG_FDC_SYSBUS', if_true: files('fdc-sysbus.c')) -system_ss.add(when: 'CONFIG_NAND', if_true: files('nand.c')) system_ss.add(when: 'CONFIG_PFLASH_CFI01', if_true: files('pflash_cfi01.c')) system_ss.add(when: 'CONFIG_PFLASH_CFI02', if_true: files('pflash_cfi02.c')) system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80.c')) diff --git a/hw/block/nand.c b/hw/block/nand.c deleted file mode 100644 index c80bf78fe5..0000000000 --- a/hw/block/nand.c +++ /dev/null @@ -1,835 +0,0 @@ -/* - * Flash NAND memory emulation. Based on "16M x 8 Bit NAND Flash - * Memory" datasheet for the KM29U128AT / K9F2808U0A chips from - * Samsung Electronic. - * - * Copyright (c) 2006 Openedhand Ltd. - * Written by Andrzej Zaborowski - * - * Support for additional features based on "MT29F2G16ABCWP 2Gx16" - * datasheet from Micron Technology and "NAND02G-B2C" datasheet - * from ST Microelectronics. - * - * This code is licensed under the GNU GPL v2. - * - * Contributions after 2012-01-13 are licensed under the terms of the - * GNU GPL, version 2 or (at your option) any later version. - */ - -#ifndef NAND_IO - -#include "qemu/osdep.h" -#include "hw/hw.h" -#include "hw/qdev-properties.h" -#include "hw/qdev-properties-system.h" -#include "hw/block/flash.h" -#include "system/block-backend.h" -#include "migration/vmstate.h" -#include "qapi/error.h" -#include "qemu/error-report.h" -#include "qemu/module.h" -#include "qom/object.h" - -# define NAND_CMD_READ0 0x00 -# define NAND_CMD_READ1 0x01 -# define NAND_CMD_READ2 0x50 -# define NAND_CMD_LPREAD2 0x30 -# define NAND_CMD_NOSERIALREAD2 0x35 -# define NAND_CMD_RANDOMREAD1 0x05 -# define NAND_CMD_RANDOMREAD2 0xe0 -# define NAND_CMD_READID 0x90 -# define NAND_CMD_RESET 0xff -# define NAND_CMD_PAGEPROGRAM1 0x80 -# define NAND_CMD_PAGEPROGRAM2 0x10 -# define NAND_CMD_CACHEPROGRAM2 0x15 -# define NAND_CMD_BLOCKERASE1 0x60 -# define NAND_CMD_BLOCKERASE2 0xd0 -# define NAND_CMD_READSTATUS 0x70 -# define NAND_CMD_COPYBACKPRG1 0x85 - -# define NAND_IOSTATUS_ERROR (1 << 0) -# define NAND_IOSTATUS_PLANE0 (1 << 1) -# define NAND_IOSTATUS_PLANE1 (1 << 2) -# define NAND_IOSTATUS_PLANE2 (1 << 3) -# define NAND_IOSTATUS_PLANE3 (1 << 4) -# define NAND_IOSTATUS_READY (1 << 6) -# define NAND_IOSTATUS_UNPROTCT (1 << 7) - -# define MAX_PAGE 0x800 -# define MAX_OOB 0x40 - -typedef struct NANDFlashState NANDFlashState; -struct NANDFlashState { - DeviceState parent_obj; - - uint8_t manf_id, chip_id; - uint8_t buswidth; /* in BYTES */ - int size, pages; - int page_shift, oob_shift, erase_shift, addr_shift; - uint8_t *storage; - BlockBackend *blk; - int mem_oob; - - uint8_t cle, ale, ce, wp, gnd; - - uint8_t io[MAX_PAGE + MAX_OOB + 0x400]; - uint8_t *ioaddr; - int iolen; - - uint32_t cmd; - uint64_t addr; - int addrlen; - int status; - int offset; - - void (*blk_write)(NANDFlashState *s); - void (*blk_erase)(NANDFlashState *s); - /* - * Returns %true when block containing (@addr + @offset) is - * successfully loaded, otherwise %false. - */ - bool (*blk_load)(NANDFlashState *s, uint64_t addr, unsigned offset); - - uint32_t ioaddr_vmstate; -}; - -#define TYPE_NAND "nand" - -OBJECT_DECLARE_SIMPLE_TYPE(NANDFlashState, NAND) - -static void mem_and(uint8_t *dest, const uint8_t *src, size_t n) -{ - /* Like memcpy() but we logical-AND the data into the destination */ - int i; - for (i = 0; i < n; i++) { - dest[i] &= src[i]; - } -} - -# define NAND_NO_AUTOINCR 0x00000001 -# define NAND_BUSWIDTH_16 0x00000002 -# define NAND_NO_PADDING 0x00000004 -# define NAND_CACHEPRG 0x00000008 -# define NAND_COPYBACK 0x00000010 -# define NAND_IS_AND 0x00000020 -# define NAND_4PAGE_ARRAY 0x00000040 -# define NAND_NO_READRDY 0x00000100 -# define NAND_SAMSUNG_LP (NAND_NO_PADDING | NAND_COPYBACK) - -# define NAND_IO - -# define PAGE(addr) ((addr) >> ADDR_SHIFT) -# define PAGE_START(page) (PAGE(page) * (NAND_PAGE_SIZE + OOB_SIZE)) -# define PAGE_MASK ((1 << ADDR_SHIFT) - 1) -# define OOB_SHIFT (PAGE_SHIFT - 5) -# define OOB_SIZE (1 << OOB_SHIFT) -# define SECTOR(addr) ((addr) >> (9 + ADDR_SHIFT - PAGE_SHIFT)) -# define SECTOR_OFFSET(addr) ((addr) & ((511 >> PAGE_SHIFT) << 8)) - -# define NAND_PAGE_SIZE 256 -# define PAGE_SHIFT 8 -# define PAGE_SECTORS 1 -# define ADDR_SHIFT 8 -# include "nand.c" -# define NAND_PAGE_SIZE 512 -# define PAGE_SHIFT 9 -# define PAGE_SECTORS 1 -# define ADDR_SHIFT 8 -# include "nand.c" -# define NAND_PAGE_SIZE 2048 -# define PAGE_SHIFT 11 -# define PAGE_SECTORS 4 -# define ADDR_SHIFT 16 -# include "nand.c" - -/* Information based on Linux drivers/mtd/nand/raw/nand_ids.c */ -static const struct { - int size; - int width; - int page_shift; - int erase_shift; - uint32_t options; -} nand_flash_ids[0x100] = { - [0 ... 0xff] = { 0 }, - - [0x6b] = { 4, 8, 9, 4, 0 }, - [0xe3] = { 4, 8, 9, 4, 0 }, - [0xe5] = { 4, 8, 9, 4, 0 }, - [0xd6] = { 8, 8, 9, 4, 0 }, - [0xe6] = { 8, 8, 9, 4, 0 }, - - [0x33] = { 16, 8, 9, 5, 0 }, - [0x73] = { 16, 8, 9, 5, 0 }, - [0x43] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x53] = { 16, 16, 9, 5, NAND_BUSWIDTH_16 }, - - [0x35] = { 32, 8, 9, 5, 0 }, - [0x75] = { 32, 8, 9, 5, 0 }, - [0x45] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x55] = { 32, 16, 9, 5, NAND_BUSWIDTH_16 }, - - [0x36] = { 64, 8, 9, 5, 0 }, - [0x76] = { 64, 8, 9, 5, 0 }, - [0x46] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x56] = { 64, 16, 9, 5, NAND_BUSWIDTH_16 }, - - [0x78] = { 128, 8, 9, 5, 0 }, - [0x39] = { 128, 8, 9, 5, 0 }, - [0x79] = { 128, 8, 9, 5, 0 }, - [0x72] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x49] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x74] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - [0x59] = { 128, 16, 9, 5, NAND_BUSWIDTH_16 }, - - [0x71] = { 256, 8, 9, 5, 0 }, - - /* - * These are the new chips with large page size. The pagesize and the - * erasesize is determined from the extended id bytes - */ -# define LP_OPTIONS (NAND_SAMSUNG_LP | NAND_NO_READRDY | NAND_NO_AUTOINCR) -# define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16) - - /* 512 Megabit */ - [0xa2] = { 64, 8, 0, 0, LP_OPTIONS }, - [0xf2] = { 64, 8, 0, 0, LP_OPTIONS }, - [0xb2] = { 64, 16, 0, 0, LP_OPTIONS16 }, - [0xc2] = { 64, 16, 0, 0, LP_OPTIONS16 }, - - /* 1 Gigabit */ - [0xa1] = { 128, 8, 0, 0, LP_OPTIONS }, - [0xf1] = { 128, 8, 0, 0, LP_OPTIONS }, - [0xb1] = { 128, 16, 0, 0, LP_OPTIONS16 }, - [0xc1] = { 128, 16, 0, 0, LP_OPTIONS16 }, - - /* 2 Gigabit */ - [0xaa] = { 256, 8, 0, 0, LP_OPTIONS }, - [0xda] = { 256, 8, 0, 0, LP_OPTIONS }, - [0xba] = { 256, 16, 0, 0, LP_OPTIONS16 }, - [0xca] = { 256, 16, 0, 0, LP_OPTIONS16 }, - - /* 4 Gigabit */ - [0xac] = { 512, 8, 0, 0, LP_OPTIONS }, - [0xdc] = { 512, 8, 0, 0, LP_OPTIONS }, - [0xbc] = { 512, 16, 0, 0, LP_OPTIONS16 }, - [0xcc] = { 512, 16, 0, 0, LP_OPTIONS16 }, - - /* 8 Gigabit */ - [0xa3] = { 1024, 8, 0, 0, LP_OPTIONS }, - [0xd3] = { 1024, 8, 0, 0, LP_OPTIONS }, - [0xb3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, - [0xc3] = { 1024, 16, 0, 0, LP_OPTIONS16 }, - - /* 16 Gigabit */ - [0xa5] = { 2048, 8, 0, 0, LP_OPTIONS }, - [0xd5] = { 2048, 8, 0, 0, LP_OPTIONS }, - [0xb5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, - [0xc5] = { 2048, 16, 0, 0, LP_OPTIONS16 }, -}; - -static void nand_reset(DeviceState *dev) -{ - NANDFlashState *s = NAND(dev); - s->cmd = NAND_CMD_READ0; - s->addr = 0; - s->addrlen = 0; - s->iolen = 0; - s->offset = 0; - s->status &= NAND_IOSTATUS_UNPROTCT; - s->status |= NAND_IOSTATUS_READY; -} - -static inline void nand_pushio_byte(NANDFlashState *s, uint8_t value) -{ - s->ioaddr[s->iolen++] = value; - for (value = s->buswidth; --value;) { - s->ioaddr[s->iolen++] = 0; - } -} - -/* - * nand_load_block: Load block containing (s->addr + @offset). - * Returns length of data available at @offset in this block. - */ -static unsigned nand_load_block(NANDFlashState *s, unsigned offset) -{ - unsigned iolen; - - if (!s->blk_load(s, s->addr, offset)) { - return 0; - } - - iolen = (1 << s->page_shift); - if (s->gnd) { - iolen += 1 << s->oob_shift; - } - assert(offset <= iolen); - iolen -= offset; - - return iolen; -} - -static void nand_command(NANDFlashState *s) -{ - switch (s->cmd) { - case NAND_CMD_READ0: - s->iolen = 0; - break; - - case NAND_CMD_READID: - s->ioaddr = s->io; - s->iolen = 0; - nand_pushio_byte(s, s->manf_id); - nand_pushio_byte(s, s->chip_id); - nand_pushio_byte(s, 'Q'); /* Don't-care byte (often 0xa5) */ - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - /* Page Size, Block Size, Spare Size; bit 6 indicates - * 8 vs 16 bit width NAND. - */ - nand_pushio_byte(s, (s->buswidth == 2) ? 0x55 : 0x15); - } else { - nand_pushio_byte(s, 0xc0); /* Multi-plane */ - } - break; - - case NAND_CMD_RANDOMREAD2: - case NAND_CMD_NOSERIALREAD2: - if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP)) - break; - s->iolen = nand_load_block(s, s->addr & ((1 << s->addr_shift) - 1)); - break; - - case NAND_CMD_RESET: - nand_reset(DEVICE(s)); - break; - - case NAND_CMD_PAGEPROGRAM1: - s->ioaddr = s->io; - s->iolen = 0; - break; - - case NAND_CMD_PAGEPROGRAM2: - if (s->wp) { - s->blk_write(s); - } - break; - - case NAND_CMD_BLOCKERASE1: - break; - - case NAND_CMD_BLOCKERASE2: - s->addr &= (1ull << s->addrlen * 8) - 1; - s->addr <<= nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP ? - 16 : 8; - - if (s->wp) { - s->blk_erase(s); - } - break; - - case NAND_CMD_READSTATUS: - s->ioaddr = s->io; - s->iolen = 0; - nand_pushio_byte(s, s->status); - break; - - default: - printf("%s: Unknown NAND command 0x%02x\n", __func__, s->cmd); - } -} - -static int nand_pre_save(void *opaque) -{ - NANDFlashState *s = NAND(opaque); - - s->ioaddr_vmstate = s->ioaddr - s->io; - - return 0; -} - -static int nand_post_load(void *opaque, int version_id) -{ - NANDFlashState *s = NAND(opaque); - - if (s->ioaddr_vmstate > sizeof(s->io)) { - return -EINVAL; - } - s->ioaddr = s->io + s->ioaddr_vmstate; - - return 0; -} - -static const VMStateDescription vmstate_nand = { - .name = "nand", - .version_id = 1, - .minimum_version_id = 1, - .pre_save = nand_pre_save, - .post_load = nand_post_load, - .fields = (const VMStateField[]) { - VMSTATE_UINT8(cle, NANDFlashState), - VMSTATE_UINT8(ale, NANDFlashState), - VMSTATE_UINT8(ce, NANDFlashState), - VMSTATE_UINT8(wp, NANDFlashState), - VMSTATE_UINT8(gnd, NANDFlashState), - VMSTATE_BUFFER(io, NANDFlashState), - VMSTATE_UINT32(ioaddr_vmstate, NANDFlashState), - VMSTATE_INT32(iolen, NANDFlashState), - VMSTATE_UINT32(cmd, NANDFlashState), - VMSTATE_UINT64(addr, NANDFlashState), - VMSTATE_INT32(addrlen, NANDFlashState), - VMSTATE_INT32(status, NANDFlashState), - VMSTATE_INT32(offset, NANDFlashState), - /* XXX: do we want to save s->storage too? */ - VMSTATE_END_OF_LIST() - } -}; - -static void nand_realize(DeviceState *dev, Error **errp) -{ - int pagesize; - NANDFlashState *s = NAND(dev); - int ret; - - - s->buswidth = nand_flash_ids[s->chip_id].width >> 3; - s->size = nand_flash_ids[s->chip_id].size << 20; - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - s->page_shift = 11; - s->erase_shift = 6; - } else { - s->page_shift = nand_flash_ids[s->chip_id].page_shift; - s->erase_shift = nand_flash_ids[s->chip_id].erase_shift; - } - - switch (1 << s->page_shift) { - case 256: - nand_init_256(s); - break; - case 512: - nand_init_512(s); - break; - case 2048: - nand_init_2048(s); - break; - default: - error_setg(errp, "Unsupported NAND block size %#x", - 1 << s->page_shift); - return; - } - - pagesize = 1 << s->oob_shift; - s->mem_oob = 1; - if (s->blk) { - if (!blk_supports_write_perm(s->blk)) { - error_setg(errp, "Can't use a read-only drive"); - return; - } - ret = blk_set_perm(s->blk, BLK_PERM_CONSISTENT_READ | BLK_PERM_WRITE, - BLK_PERM_ALL, errp); - if (ret < 0) { - return; - } - if (blk_getlength(s->blk) >= - (s->pages << s->page_shift) + (s->pages << s->oob_shift)) { - pagesize = 0; - s->mem_oob = 0; - } - } else { - pagesize += 1 << s->page_shift; - } - if (pagesize) { - s->storage = (uint8_t *) memset(g_malloc(s->pages * pagesize), - 0xff, s->pages * pagesize); - } - /* Give s->ioaddr a sane value in case we save state before it is used. */ - s->ioaddr = s->io; -} - -static const Property nand_properties[] = { - DEFINE_PROP_UINT8("manufacturer_id", NANDFlashState, manf_id, 0), - DEFINE_PROP_UINT8("chip_id", NANDFlashState, chip_id, 0), - DEFINE_PROP_DRIVE("drive", NANDFlashState, blk), -}; - -static void nand_class_init(ObjectClass *klass, const void *data) -{ - DeviceClass *dc = DEVICE_CLASS(klass); - - dc->realize = nand_realize; - device_class_set_legacy_reset(dc, nand_reset); - dc->vmsd = &vmstate_nand; - device_class_set_props(dc, nand_properties); - set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); -} - -static const TypeInfo nand_info = { - .name = TYPE_NAND, - .parent = TYPE_DEVICE, - .instance_size = sizeof(NANDFlashState), - .class_init = nand_class_init, -}; - -static void nand_register_types(void) -{ - type_register_static(&nand_info); -} - -/* - * Chip inputs are CLE, ALE, CE, WP, GND and eight I/O pins. Chip - * outputs are R/B and eight I/O pins. - * - * CE, WP and R/B are active low. - */ -void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale, - uint8_t ce, uint8_t wp, uint8_t gnd) -{ - NANDFlashState *s = NAND(dev); - - s->cle = cle; - s->ale = ale; - s->ce = ce; - s->wp = wp; - s->gnd = gnd; - if (wp) { - s->status |= NAND_IOSTATUS_UNPROTCT; - } else { - s->status &= ~NAND_IOSTATUS_UNPROTCT; - } -} - -void nand_getpins(DeviceState *dev, int *rb) -{ - *rb = 1; -} - -void nand_setio(DeviceState *dev, uint32_t value) -{ - int i; - NANDFlashState *s = NAND(dev); - - if (!s->ce && s->cle) { - if (nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) { - if (s->cmd == NAND_CMD_READ0 && value == NAND_CMD_LPREAD2) - return; - if (value == NAND_CMD_RANDOMREAD1) { - s->addr &= ~((1 << s->addr_shift) - 1); - s->addrlen = 0; - return; - } - } - if (value == NAND_CMD_READ0) { - s->offset = 0; - } else if (value == NAND_CMD_READ1) { - s->offset = 0x100; - value = NAND_CMD_READ0; - } else if (value == NAND_CMD_READ2) { - s->offset = 1 << s->page_shift; - value = NAND_CMD_READ0; - } - - s->cmd = value; - - if (s->cmd == NAND_CMD_READSTATUS || - s->cmd == NAND_CMD_PAGEPROGRAM2 || - s->cmd == NAND_CMD_BLOCKERASE1 || - s->cmd == NAND_CMD_BLOCKERASE2 || - s->cmd == NAND_CMD_NOSERIALREAD2 || - s->cmd == NAND_CMD_RANDOMREAD2 || - s->cmd == NAND_CMD_RESET) { - nand_command(s); - } - - if (s->cmd != NAND_CMD_RANDOMREAD2) { - s->addrlen = 0; - } - } - - if (s->ale) { - unsigned int shift = s->addrlen * 8; - uint64_t mask = ~(0xffull << shift); - uint64_t v = (uint64_t)value << shift; - - s->addr = (s->addr & mask) | v; - s->addrlen ++; - - switch (s->addrlen) { - case 1: - if (s->cmd == NAND_CMD_READID) { - nand_command(s); - } - break; - case 2: /* fix cache address as a byte address */ - s->addr <<= (s->buswidth - 1); - break; - case 3: - if (!(nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - (s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) { - nand_command(s); - } - break; - case 4: - if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - nand_flash_ids[s->chip_id].size < 256 && /* 1Gb or less */ - (s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) { - nand_command(s); - } - break; - case 5: - if ((nand_flash_ids[s->chip_id].options & NAND_SAMSUNG_LP) && - nand_flash_ids[s->chip_id].size >= 256 && /* 2Gb or more */ - (s->cmd == NAND_CMD_READ0 || - s->cmd == NAND_CMD_PAGEPROGRAM1)) { - nand_command(s); - } - break; - default: - break; - } - } - - if (!s->cle && !s->ale && s->cmd == NAND_CMD_PAGEPROGRAM1) { - if (s->iolen < (1 << s->page_shift) + (1 << s->oob_shift)) { - for (i = s->buswidth; i--; value >>= 8) { - s->io[s->iolen ++] = (uint8_t) (value & 0xff); - } - } - } else if (!s->cle && !s->ale && s->cmd == NAND_CMD_COPYBACKPRG1) { - if ((s->addr & ((1 << s->addr_shift) - 1)) < - (1 << s->page_shift) + (1 << s->oob_shift)) { - for (i = s->buswidth; i--; s->addr++, value >>= 8) { - s->io[s->iolen + (s->addr & ((1 << s->addr_shift) - 1))] = - (uint8_t) (value & 0xff); - } - } - } -} - -uint32_t nand_getio(DeviceState *dev) -{ - int offset; - uint32_t x = 0; - NANDFlashState *s = NAND(dev); - - /* Allow sequential reading */ - if (!s->iolen && s->cmd == NAND_CMD_READ0) { - offset = (int) (s->addr & ((1 << s->addr_shift) - 1)) + s->offset; - s->offset = 0; - s->iolen = nand_load_block(s, offset); - } - - if (s->ce || s->iolen <= 0) { - return 0; - } - - for (offset = s->buswidth; offset--;) { - x |= s->ioaddr[offset] << (offset << 3); - } - /* after receiving READ STATUS command all subsequent reads will - * return the status register value until another command is issued - */ - if (s->cmd != NAND_CMD_READSTATUS) { - s->addr += s->buswidth; - s->ioaddr += s->buswidth; - s->iolen -= s->buswidth; - } - return x; -} - -uint32_t nand_getbuswidth(DeviceState *dev) -{ - NANDFlashState *s = (NANDFlashState *) dev; - return s->buswidth << 3; -} - -DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id) -{ - DeviceState *dev; - - if (nand_flash_ids[chip_id].size == 0) { - hw_error("%s: Unsupported NAND chip ID.\n", __func__); - } - dev = qdev_new(TYPE_NAND); - qdev_prop_set_uint8(dev, "manufacturer_id", manf_id); - qdev_prop_set_uint8(dev, "chip_id", chip_id); - if (blk) { - qdev_prop_set_drive_err(dev, "drive", blk, &error_fatal); - } - - qdev_realize(dev, NULL, &error_fatal); - return dev; -} - -type_init(nand_register_types) - -#else - -/* Program a single page */ -static void glue(nand_blk_write_, NAND_PAGE_SIZE)(NANDFlashState *s) -{ - uint64_t off, page, sector, soff; - uint8_t iobuf[(PAGE_SECTORS + 2) * 0x200]; - if (PAGE(s->addr) >= s->pages) - return; - - if (!s->blk) { - mem_and(s->storage + PAGE_START(s->addr) + (s->addr & PAGE_MASK) + - s->offset, s->io, s->iolen); - } else if (s->mem_oob) { - sector = SECTOR(s->addr); - off = (s->addr & PAGE_MASK) + s->offset; - soff = SECTOR_OFFSET(s->addr); - if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, - PAGE_SECTORS << BDRV_SECTOR_BITS, iobuf, 0) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); - return; - } - - mem_and(iobuf + (soff | off), s->io, MIN(s->iolen, NAND_PAGE_SIZE - off)); - if (off + s->iolen > NAND_PAGE_SIZE) { - page = PAGE(s->addr); - mem_and(s->storage + (page << OOB_SHIFT), s->io + NAND_PAGE_SIZE - off, - MIN(OOB_SIZE, off + s->iolen - NAND_PAGE_SIZE)); - } - - if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, - PAGE_SECTORS << BDRV_SECTOR_BITS, iobuf, 0) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); - } - } else { - off = PAGE_START(s->addr) + (s->addr & PAGE_MASK) + s->offset; - sector = off >> 9; - soff = off & 0x1ff; - if (blk_pread(s->blk, sector << BDRV_SECTOR_BITS, - (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, iobuf, 0) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, sector); - return; - } - - mem_and(iobuf + soff, s->io, s->iolen); - - if (blk_pwrite(s->blk, sector << BDRV_SECTOR_BITS, - (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, iobuf, 0) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, sector); - } - } - s->offset = 0; -} - -/* Erase a single block */ -static void glue(nand_blk_erase_, NAND_PAGE_SIZE)(NANDFlashState *s) -{ - uint64_t i, page, addr; - uint8_t iobuf[0x200] = { [0 ... 0x1ff] = 0xff, }; - addr = s->addr & ~((1 << (ADDR_SHIFT + s->erase_shift)) - 1); - - if (PAGE(addr) >= s->pages) { - return; - } - - if (!s->blk) { - memset(s->storage + PAGE_START(addr), - 0xff, (NAND_PAGE_SIZE + OOB_SIZE) << s->erase_shift); - } else if (s->mem_oob) { - memset(s->storage + (PAGE(addr) << OOB_SHIFT), - 0xff, OOB_SIZE << s->erase_shift); - i = SECTOR(addr); - page = SECTOR(addr + (1 << (ADDR_SHIFT + s->erase_shift))); - for (; i < page; i ++) - if (blk_pwrite(s->blk, i << BDRV_SECTOR_BITS, - BDRV_SECTOR_SIZE, iobuf, 0) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, i); - } - } else { - addr = PAGE_START(addr); - page = addr >> 9; - if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, - BDRV_SECTOR_SIZE, iobuf, 0) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, page); - } - memset(iobuf + (addr & 0x1ff), 0xff, (~addr & 0x1ff) + 1); - if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, - BDRV_SECTOR_SIZE, iobuf, 0) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, page); - } - - memset(iobuf, 0xff, 0x200); - i = (addr & ~0x1ff) + 0x200; - for (addr += ((NAND_PAGE_SIZE + OOB_SIZE) << s->erase_shift) - 0x200; - i < addr; i += 0x200) { - if (blk_pwrite(s->blk, i, BDRV_SECTOR_SIZE, iobuf, 0) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", - __func__, i >> 9); - } - } - - page = i >> 9; - if (blk_pread(s->blk, page << BDRV_SECTOR_BITS, - BDRV_SECTOR_SIZE, iobuf, 0) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", __func__, page); - } - memset(iobuf, 0xff, ((addr - 1) & 0x1ff) + 1); - if (blk_pwrite(s->blk, page << BDRV_SECTOR_BITS, - BDRV_SECTOR_SIZE, iobuf, 0) < 0) { - printf("%s: write error in sector %" PRIu64 "\n", __func__, page); - } - } -} - -static bool glue(nand_blk_load_, NAND_PAGE_SIZE)(NANDFlashState *s, - uint64_t addr, unsigned offset) -{ - if (PAGE(addr) >= s->pages) { - return false; - } - - if (offset > NAND_PAGE_SIZE + OOB_SIZE) { - return false; - } - - if (s->blk) { - if (s->mem_oob) { - if (blk_pread(s->blk, SECTOR(addr) << BDRV_SECTOR_BITS, - PAGE_SECTORS << BDRV_SECTOR_BITS, s->io, 0) < 0) { - printf("%s: read error in sector %" PRIu64 "\n", - __func__, SECTOR(addr)); - } - memcpy(s->io + SECTOR_OFFSET(s->addr) + NAND_PAGE_SIZE, - s->storage + (PAGE(s->addr) << OOB_SHIFT), - OOB_SIZE); - s->ioaddr = s->io + SECTOR_OFFSET(s->addr) + offset; - } else { - if (blk_pread(s->blk, PAGE_START(addr), - (PAGE_SECTORS + 2) << BDRV_SECTOR_BITS, s->io, 0) - < 0) { - printf("%s: read error in sector %" PRIu64 "\n", - __func__, PAGE_START(addr) >> 9); - } - s->ioaddr = s->io + (PAGE_START(addr) & 0x1ff) + offset; - } - } else { - memcpy(s->io, s->storage + PAGE_START(s->addr) + - offset, NAND_PAGE_SIZE + OOB_SIZE - offset); - s->ioaddr = s->io; - } - - return true; -} - -static void glue(nand_init_, NAND_PAGE_SIZE)(NANDFlashState *s) -{ - s->oob_shift = PAGE_SHIFT - 5; - s->pages = s->size >> PAGE_SHIFT; - s->addr_shift = ADDR_SHIFT; - - s->blk_erase = glue(nand_blk_erase_, NAND_PAGE_SIZE); - s->blk_write = glue(nand_blk_write_, NAND_PAGE_SIZE); - s->blk_load = glue(nand_blk_load_, NAND_PAGE_SIZE); -} - -# undef NAND_PAGE_SIZE -# undef PAGE_SHIFT -# undef PAGE_SECTORS -# undef ADDR_SHIFT -#endif /* NAND_IO */ diff --git a/include/hw/block/flash.h b/include/hw/block/flash.h index 5fd67f5bb7..3671f0174d 100644 --- a/include/hw/block/flash.h +++ b/include/hw/block/flash.h @@ -44,24 +44,6 @@ PFlashCFI02 *pflash_cfi02_register(hwaddr base, uint16_t unlock_addr1, int be); -/* nand.c */ -DeviceState *nand_init(BlockBackend *blk, int manf_id, int chip_id); -void nand_setpins(DeviceState *dev, uint8_t cle, uint8_t ale, - uint8_t ce, uint8_t wp, uint8_t gnd); -void nand_getpins(DeviceState *dev, int *rb); -void nand_setio(DeviceState *dev, uint32_t value); -uint32_t nand_getio(DeviceState *dev); -uint32_t nand_getbuswidth(DeviceState *dev); - -#define NAND_MFR_TOSHIBA 0x98 -#define NAND_MFR_SAMSUNG 0xec -#define NAND_MFR_FUJITSU 0x04 -#define NAND_MFR_NATIONAL 0x8f -#define NAND_MFR_RENESAS 0x07 -#define NAND_MFR_STMICRO 0x20 -#define NAND_MFR_HYNIX 0xad -#define NAND_MFR_MICRON 0x2c - /* m25p80.c */ #define TYPE_M25P80 "m25p80-generic" From e2e360db7a55afd62e77577a965f6aa224132cef Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sat, 17 May 2025 23:11:52 +0300 Subject: [PATCH 1298/2760] io: Add helper for setting socket send buffer size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Testing reading and writing from qemu-nbd using a unix domain socket shows that the platform default send buffer size is too low, leading to poor performance and hight cpu usage. Add a helper for setting socket send buffer size to be used in NBD code. It can also be used in other contexts. We don't need a helper for receive buffer size since it is not used with unix domain sockets. This is documented for Linux, and not documented for macOS. Failing to set the socket buffer size is not a fatal error, but the caller may want to warn about the failure. Signed-off-by: Nir Soffer Message-ID: <20250517201154.88456-2-nirsof@gmail.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Eric Blake --- include/io/channel-socket.h | 13 +++++++++++++ io/channel-socket.c | 11 +++++++++++ 2 files changed, 24 insertions(+) diff --git a/include/io/channel-socket.h b/include/io/channel-socket.h index ab15577d38..a88cf8b3a9 100644 --- a/include/io/channel-socket.h +++ b/include/io/channel-socket.h @@ -261,5 +261,18 @@ QIOChannelSocket * qio_channel_socket_accept(QIOChannelSocket *ioc, Error **errp); +/** + * qio_channel_socket_set_send_buffer: + * @ioc: the socket channel object + * @size: buffer size + * @errp: pointer to a NULL-initialized error object + * + * Set the underlying socket send buffer size. + * + * Retruns: 0 on success, or -1 on error. + */ +int qio_channel_socket_set_send_buffer(QIOChannelSocket *ioc, + size_t size, + Error **errp); #endif /* QIO_CHANNEL_SOCKET_H */ diff --git a/io/channel-socket.c b/io/channel-socket.c index 088b49ffdb..3b7ca924ff 100644 --- a/io/channel-socket.c +++ b/io/channel-socket.c @@ -78,6 +78,17 @@ qio_channel_socket_new(void) return sioc; } +int qio_channel_socket_set_send_buffer(QIOChannelSocket *ioc, + size_t size, + Error **errp) +{ + if (setsockopt(ioc->fd, SOL_SOCKET, SO_SNDBUF, &size, sizeof(size)) < 0) { + error_setg_errno(errp, errno, "Unable to set socket send buffer size"); + return -1; + } + + return 0; +} static int qio_channel_socket_set_fd(QIOChannelSocket *sioc, From e9f4550b74a8a9774979a51caa4b1aaff9e1d055 Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sat, 17 May 2025 23:11:53 +0300 Subject: [PATCH 1299/2760] nbd: Set unix socket send buffer on macOS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On macOS we need to increase unix socket buffers size on the client and server to get good performance. We set socket buffers on macOS after connecting or accepting a client connection. Testing shows that setting socket receive buffer size (SO_RCVBUF) has no effect on performance, so we set only the send buffer size (SO_SNDBUF). It seems to work like Linux but not documented. Testing shows that optimal buffer size is 512k to 4 MiB, depending on the test case. The difference is very small, so I chose 2 MiB. I tested reading from qemu-nbd and writing to qemu-nbd with qemu-img and computing a blkhash with nbdcopy and blksum. To focus on NBD communication and get less noisy results, I tested reading and writing to null-co driver. I added a read-pattern option to the null-co driver to return data full of 0xff: NULL="json:{'driver': 'raw', 'file': {'driver': 'null-co', 'size': '10g', 'read-pattern': 255}}" For testing buffer size I added an environment variable for setting the socket buffer size. Read from qemu-nbd via qemu-img convert. In this test buffer size of 2m is optimal (12.6 times faster). qemu-nbd -r -t -e 0 -f raw -k /tmp/nbd.sock "$NULL" & qemu-img convert -f raw -O raw -W -n "nbd+unix:///?socket=/tmp/nbd.sock" "$NULL" | buffer size | time | user | system | |-------------|---------|---------|---------| | default | 13.361 | 2.653 | 5.702 | | 65536 | 2.283 | 0.204 | 1.318 | | 131072 | 1.673 | 0.062 | 1.008 | | 262144 | 1.592 | 0.053 | 0.952 | | 524288 | 1.496 | 0.049 | 0.887 | | 1048576 | 1.234 | 0.047 | 0.738 | | 2097152 | 1.060 | 0.080 | 0.602 | | 4194304 | 1.061 | 0.076 | 0.604 | Write to qemu-nbd with qemu-img convert. In this test buffer size of 2m is optimal (9.2 times faster). qemu-nbd -t -e 0 -f raw -k /tmp/nbd.sock "$NULL" & qemu-img convert -f raw -O raw -W -n "$NULL" "nbd+unix:///?socket=/tmp/nbd.sock" | buffer size | time | user | system | |-------------|---------|---------|---------| | default | 8.063 | 2.522 | 4.184 | | 65536 | 1.472 | 0.430 | 0.867 | | 131072 | 1.071 | 0.297 | 0.654 | | 262144 | 1.012 | 0.239 | 0.587 | | 524288 | 0.970 | 0.201 | 0.514 | | 1048576 | 0.895 | 0.184 | 0.454 | | 2097152 | 0.877 | 0.174 | 0.440 | | 4194304 | 0.944 | 0.231 | 0.535 | Compute a blkhash with nbdcopy, using 4 NBD connections and 256k request size. In this test buffer size of 4m is optimal (5.1 times faster). qemu-nbd -r -t -e 0 -f raw -k /tmp/nbd.sock "$NULL" & nbdcopy --blkhash "nbd+unix:///?socket=/tmp/nbd.sock" null: | buffer size | time | user | system | |-------------|---------|---------|---------| | default | 8.624 | 5.727 | 6.507 | | 65536 | 2.563 | 4.760 | 2.498 | | 131072 | 1.903 | 4.559 | 2.093 | | 262144 | 1.759 | 4.513 | 1.935 | | 524288 | 1.729 | 4.489 | 1.924 | | 1048576 | 1.696 | 4.479 | 1.884 | | 2097152 | 1.710 | 4.480 | 1.763 | | 4194304 | 1.687 | 4.479 | 1.712 | Compute a blkhash with blksum, using 1 NBD connection and 256k read size. In this test buffer size of 512k is optimal (10.3 times faster). qemu-nbd -r -t -e 0 -f raw -k /tmp/nbd.sock "$NULL" & blksum "nbd+unix:///?socket=/tmp/nbd.sock" | buffer size | time | user | system | |-------------|---------|---------|---------| | default | 13.085 | 5.664 | 6.461 | | 65536 | 3.299 | 5.106 | 2.515 | | 131072 | 2.396 | 4.989 | 2.069 | | 262144 | 1.607 | 4.724 | 1.555 | | 524288 | 1.271 | 4.528 | 1.224 | | 1048576 | 1.294 | 4.565 | 1.333 | | 2097152 | 1.299 | 4.569 | 1.344 | | 4194304 | 1.291 | 4.559 | 1.327 | Signed-off-by: Nir Soffer Message-ID: <20250517201154.88456-3-nirsof@gmail.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Eric Blake --- nbd/client-connection.c | 3 +++ nbd/common.c | 25 +++++++++++++++++++++++++ nbd/nbd-internal.h | 5 +++++ nbd/server.c | 2 ++ 4 files changed, 35 insertions(+) diff --git a/nbd/client-connection.c b/nbd/client-connection.c index b11e266807..79ea97e4cc 100644 --- a/nbd/client-connection.c +++ b/nbd/client-connection.c @@ -31,6 +31,8 @@ #include "qapi/clone-visitor.h" #include "qemu/coroutine.h" +#include "nbd/nbd-internal.h" + struct NBDClientConnection { /* Initialization constants, never change */ SocketAddress *saddr; /* address to connect to */ @@ -140,6 +142,7 @@ static int nbd_connect(QIOChannelSocket *sioc, SocketAddress *addr, return ret; } + nbd_set_socket_send_buffer(sioc); qio_channel_set_delay(QIO_CHANNEL(sioc), false); if (!info) { diff --git a/nbd/common.c b/nbd/common.c index 589a748cfe..9436e9d1d1 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -18,6 +18,9 @@ #include "qemu/osdep.h" #include "trace.h" +#include "io/channel-socket.h" +#include "qapi/error.h" +#include "qemu/units.h" #include "nbd-internal.h" /* Discard length bytes from channel. Return -errno on failure and 0 on @@ -264,3 +267,25 @@ const char *nbd_mode_lookup(NBDMode mode) return ""; } } + +/* + * Testing shows that 2m send buffer is optimal. Changing the receive buffer + * size has no effect on performance. + */ +#if defined(__APPLE__) +#define UNIX_STREAM_SOCKET_SEND_BUFFER_SIZE (2 * MiB) +#endif + +void nbd_set_socket_send_buffer(QIOChannelSocket *sioc) +{ +#ifdef UNIX_STREAM_SOCKET_SEND_BUFFER_SIZE + if (sioc->localAddr.ss_family == AF_UNIX) { + size_t size = UNIX_STREAM_SOCKET_SEND_BUFFER_SIZE; + Error *errp = NULL; + + if (qio_channel_socket_set_send_buffer(sioc, size, &errp) < 0) { + warn_report_err(errp); + } + } +#endif /* UNIX_STREAM_SOCKET_SEND_BUFFER_SIZE */ +} diff --git a/nbd/nbd-internal.h b/nbd/nbd-internal.h index 715d92d6ef..6bafeef5dd 100644 --- a/nbd/nbd-internal.h +++ b/nbd/nbd-internal.h @@ -74,4 +74,9 @@ static inline int nbd_write(QIOChannel *ioc, const void *buffer, size_t size, int nbd_drop(QIOChannel *ioc, size_t size, Error **errp); +/* nbd_set_socket_send_buffer + * Set the socket send buffer size for optimal performance. + */ +void nbd_set_socket_send_buffer(QIOChannelSocket *sioc); + #endif diff --git a/nbd/server.c b/nbd/server.c index 2076fb2666..d242be9811 100644 --- a/nbd/server.c +++ b/nbd/server.c @@ -3291,6 +3291,8 @@ void nbd_client_new(QIOChannelSocket *sioc, client->close_fn = close_fn; client->owner = owner; + nbd_set_socket_send_buffer(sioc); + co = qemu_coroutine_create(nbd_co_client_start, client); qemu_coroutine_enter(co); } From 479ec8106185d0e88d65539df5e940c781a82c53 Mon Sep 17 00:00:00 2001 From: Nir Soffer Date: Sat, 17 May 2025 23:11:54 +0300 Subject: [PATCH 1300/2760] nbd: Set unix socket send buffer on Linux MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like macOS we have similar issue on Linux. For TCP socket the send buffer size is 2626560 bytes (~2.5 MiB) and we get good performance. However for unix socket the default and maximum buffer size is 212992 bytes (208 KiB) and we see poor performance when using one NBD connection, up to 4 times slower than macOS on the same machine. Tracing shows that for every 2 MiB payload (qemu uses 2 MiB io size), we do 1 recvmsg call with TCP socket, and 10 recvmsg calls with unix socket. Fixing this issue requires changing the maximum send buffer size (the receive buffer size is ignored). This can be done using: $ cat /etc/sysctl.d/net-mem-max.conf net.core.wmem_max = 2097152 $ sudo sysctl -p /etc/sysctl.d/net-mem-max.conf With this we can set the socket buffer size to 2 MiB. With the defaults the value requested by qemu is clipped to the maximum size and has no effect. I tested on 2 machines: - Fedora 42 VM on MacBook Pro M2 Max - Dell PowerEdge R640 (Intel(R) Xeon(R) Gold 6230 CPU @ 2.10GHz) On the older Dell machine we see very little improvement, up to 1.03 higher throughput. On the M2 machine we see up to 2.67 times higher throughput. The following results are from the M2 machine. Reading from qemu-nbd with qemu-img convert. In this test buffer size of 4m is optimal (2.28 times faster). | buffer size | time | user | system | |-------------|---------|---------|---------| | default | 4.292 | 0.243 | 1.604 | | 524288 | 2.167 | 0.058 | 1.288 | | 1048576 | 2.041 | 0.060 | 1.238 | | 2097152 | 1.884 | 0.060 | 1.191 | | 4194304 | 1.881 | 0.054 | 1.196 | Writing to qemu-nbd with qemu-img convert. In this test buffer size of 1m is optimal (2.67 times faster). | buffer size | time | user | system | |-------------|---------|---------|---------| | default | 3.113 | 0.334 | 1.094 | | 524288 | 1.173 | 0.179 | 0.654 | | 1048576 | 1.164 | 0.164 | 0.670 | | 2097152 | 1.227 | 0.197 | 0.663 | | 4194304 | 1.227 | 0.198 | 0.666 | Computing a blkhash with nbdcopy. In this test buffer size of 512k is optimal (1.19 times faster). | buffer size | time | user | system | |-------------|---------|---------|---------| | default | 2.140 | 4.483 | 2.681 | | 524288 | 1.794 | 4.467 | 2.572 | | 1048576 | 1.807 | 4.447 | 2.644 | | 2097152 | 1.822 | 4.461 | 2.698 | | 4194304 | 1.827 | 4.465 | 2.700 | Computing a blkhash with blksum. In this test buffer size of 4m is optimal (2.65 times faster). | buffer size | time | user | system | |-------------|---------|---------|---------| | default | 3.582 | 4.595 | 2.392 | | 524288 | 1.499 | 4.384 | 1.482 | | 1048576 | 1.377 | 4.381 | 1.345 | | 2097152 | 1.388 | 4.389 | 1.354 | | 4194304 | 1.352 | 4.395 | 1.302 | Signed-off-by: Nir Soffer Message-ID: <20250517201154.88456-4-nirsof@gmail.com> Reviewed-by: Daniel P. Berrangé Signed-off-by: Eric Blake --- nbd/common.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nbd/common.c b/nbd/common.c index 9436e9d1d1..2a133a66c3 100644 --- a/nbd/common.c +++ b/nbd/common.c @@ -271,8 +271,9 @@ const char *nbd_mode_lookup(NBDMode mode) /* * Testing shows that 2m send buffer is optimal. Changing the receive buffer * size has no effect on performance. + * On Linux we need to increase net.core.wmem_max to make this effective. */ -#if defined(__APPLE__) +#if defined(__APPLE__) || defined(__linux__) #define UNIX_STREAM_SOCKET_SEND_BUFFER_SIZE (2 * MiB) #endif From d2b3e32bf7395c710ba44585520d837f6330fa70 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 23 May 2025 11:27:21 -0500 Subject: [PATCH 1301/2760] iotests: Use disk_usage in more places Commit be9bac07 added a utility disk_usage function, but there are a couple of other tests that could also use it. Signed-off-by: Eric Blake Message-ID: <20250523163041.2548675-6-eblake@redhat.com> Reviewed-by: Fiona Ebner Tested-by: Fiona Ebner --- tests/qemu-iotests/125 | 2 +- tests/qemu-iotests/308 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/qemu-iotests/125 b/tests/qemu-iotests/125 index 46279d6b38..708e7c5ba2 100755 --- a/tests/qemu-iotests/125 +++ b/tests/qemu-iotests/125 @@ -35,7 +35,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 get_image_size_on_host() { - echo $(($(stat -c '%b * %B' "$TEST_IMG_FILE"))) + disk_usage "$TEST_IMG_FILE" } # get standard environment and filters diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index ea81dc496a..437a9014da 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -290,7 +290,7 @@ echo '--- Try growing non-growable export ---' # Get the current size so we can write beyond the EOF orig_len=$(get_proto_len "$EXT_MP" "$TEST_IMG") -orig_disk_usage=$(stat -c '%b' "$TEST_IMG") +orig_disk_usage=$(disk_usage "$TEST_IMG") # Should fail (exports are non-growable by default) # (Note that qemu-io can never write beyond the EOF, so we have to use @@ -312,7 +312,7 @@ else echo 'OK: Post-truncate image size is as expected' fi -new_disk_usage=$(stat -c '%b' "$TEST_IMG") +new_disk_usage=$(disk_usage "$TEST_IMG") if [ "$new_disk_usage" -gt "$orig_disk_usage" ]; then echo 'OK: Disk usage grew with fallocate' else From ed1c336119c084159575ae9d4fd9171bd500f7e4 Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 23 May 2025 11:27:22 -0500 Subject: [PATCH 1302/2760] iotests: Improve mirror-sparse on ext4 and xfs Fiona reported that an ext4 filesystem on top of LVM can sometimes report over-allocation to du (based on the heuristics the filesystem is making while observing the contents being mirrored); even though the contents and actual size matched, about 50% of the time the size reported by disk_usage was too large by 4k, failing the test. In auditing other iotests, this is a common problem we've had to deal with. Meanwhile, Markus reported that an xfs filesystem reports disk usage at a default granularity of 1M (so the sparse file occupies 3M, since it has just over 2M data). Reported-by: Fiona Ebner Reported-by: Markus Armbruster Fixes: c0ddcb2c ("tests: Add iotest mirror-sparse for recent patches") Signed-off-by: Eric Blake Reviewed-by: Fiona Ebner Tested-by: Fiona Ebner Message-ID: <20250523163041.2548675-7-eblake@redhat.com> [eblake: Also fix xfs issue] Signed-off-by: Eric Blake --- tests/qemu-iotests/tests/mirror-sparse | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/qemu-iotests/tests/mirror-sparse b/tests/qemu-iotests/tests/mirror-sparse index 8c52a4e244..11418c0871 100755 --- a/tests/qemu-iotests/tests/mirror-sparse +++ b/tests/qemu-iotests/tests/mirror-sparse @@ -96,13 +96,15 @@ _send_qemu_cmd $h1 '{"execute": "blockdev-del", "arguments": {"node-name": "dst"}}' 'return' \ | _filter_block_job_offset | _filter_block_job_len $QEMU_IMG compare -U -f $IMGFMT -F raw $TEST_IMG.base $TEST_IMG +# Some filesystems can fudge allocations for various reasons; rather +# than expecting precise 2M and 20M images, it is better to allow for slop. result=$(disk_usage $TEST_IMG) -if test $result -lt $((3*1024*1024)); then +if test $result -lt $((4*1024*1024)); then actual=sparse -elif test $result = $((20*1024*1024)); then +elif test $result -gt $((19*1024*1024)); then actual=full else - actual=unknown + actual="unexpected size ($result)" fi echo "Destination is $actual; expected $expected" } From c49dda7254d43d9e1d4da59c55f02055ba7c4c1b Mon Sep 17 00:00:00 2001 From: Eric Blake Date: Fri, 23 May 2025 11:27:23 -0500 Subject: [PATCH 1303/2760] iotests: Filter out ZFS in several tests Fiona reported that ZFS makes sparse file testing awkward, since: - it has asynchronous allocation (not even 'fsync $file' makes du see the desired size; it takes the slower 'fsync -f $file' which is not appropriate for the tests) - for tests of fully allocated files, ZFS with compression enabled still reports smaller disk usage Add a new _require_disk_usage that quickly probes whether an attempt to create a sparse 5M file shows as less than 1M usage, while the same file with -o preallocation=full shows as more than 4M usage without sync, which should filter out ZFS behavior. Then use it in various affected tests. This does not add the new filter on all tests that Fiona is seeing ZFS failures on, but only those where I could quickly spot that there is at least one place where the test depends on the output of 'du -b' or 'stat -c %b'. Reported-by: Fiona Ebner Signed-off-by: Eric Blake Message-ID: <20250523163041.2548675-8-eblake@redhat.com> Reviewed-by: Fiona Ebner Tested-by: Fiona Ebner --- tests/qemu-iotests/106 | 1 + tests/qemu-iotests/175 | 1 + tests/qemu-iotests/221 | 1 + tests/qemu-iotests/253 | 1 + tests/qemu-iotests/308 | 1 + tests/qemu-iotests/common.rc | 30 +++++++++++++++++++++ tests/qemu-iotests/tests/mirror-sparse | 1 + tests/qemu-iotests/tests/write-zeroes-unmap | 1 + 8 files changed, 37 insertions(+) diff --git a/tests/qemu-iotests/106 b/tests/qemu-iotests/106 index ae0fc46691..55548439aa 100755 --- a/tests/qemu-iotests/106 +++ b/tests/qemu-iotests/106 @@ -40,6 +40,7 @@ trap "_cleanup; exit \$status" 0 1 2 3 15 _supported_fmt raw _supported_proto file fuse _supported_os Linux +_require_disk_usage # in kB CREATION_SIZE=128 diff --git a/tests/qemu-iotests/175 b/tests/qemu-iotests/175 index f74f053b71..bbbf550a5a 100755 --- a/tests/qemu-iotests/175 +++ b/tests/qemu-iotests/175 @@ -77,6 +77,7 @@ _supported_os Linux _default_cache_mode none _supported_cache_modes none directsync +_require_disk_usage size=$((1 * 1024 * 1024)) diff --git a/tests/qemu-iotests/221 b/tests/qemu-iotests/221 index c463fd4b11..eba00b80ad 100755 --- a/tests/qemu-iotests/221 +++ b/tests/qemu-iotests/221 @@ -41,6 +41,7 @@ _supported_os Linux _default_cache_mode writeback _supported_cache_modes writeback writethrough unsafe +_require_disk_usage echo echo "=== Check mapping of unaligned raw image ===" diff --git a/tests/qemu-iotests/253 b/tests/qemu-iotests/253 index 35039d20a8..6da85e6a11 100755 --- a/tests/qemu-iotests/253 +++ b/tests/qemu-iotests/253 @@ -41,6 +41,7 @@ _supported_os Linux _default_cache_mode none _supported_cache_modes none directsync +_require_disk_usage echo echo "=== Check mapping of unaligned raw image ===" diff --git a/tests/qemu-iotests/308 b/tests/qemu-iotests/308 index 437a9014da..6eced3aefb 100755 --- a/tests/qemu-iotests/308 +++ b/tests/qemu-iotests/308 @@ -51,6 +51,7 @@ _unsupported_fmt vpc _supported_proto file # We create the FUSE export manually _supported_os Linux # We need /dev/urandom +_require_disk_usage # $1: Export ID # $2: Options (beyond the node-name and ID) diff --git a/tests/qemu-iotests/common.rc b/tests/qemu-iotests/common.rc index 237f746af8..e977cb4eb6 100644 --- a/tests/qemu-iotests/common.rc +++ b/tests/qemu-iotests/common.rc @@ -996,6 +996,36 @@ _require_large_file() rm "$FILENAME" } +# Check whether disk_usage can be reliably used. +_require_disk_usage() +{ + local unusable=false + # ZFS triggers known failures on this front; it does not immediately + # allocate files, and then aggressively compresses writes even when full + # allocation was requested. + if [ -z "$TEST_IMG_FILE" ]; then + FILENAME="$TEST_IMG" + else + FILENAME="$TEST_IMG_FILE" + fi + if [ -e "FILENAME" ]; then + echo "unwilling to overwrite existing file" + exit 1 + fi + $QEMU_IMG create -f raw "$FILENAME" 5M > /dev/null + if [ $(disk_usage "$FILENAME") -gt $((1024*1024)) ]; then + unusable=true + fi + $QEMU_IMG create -f raw -o preallocation=full "$FILENAME" 5M > /dev/null + if [ $(disk_usage "$FILENAME") -lt $((4*1024*1024)) ]; then + unusable=true + fi + rm -f "$FILENAME" + if $unusable; then + _notrun "file system on $TEST_DIR does not handle sparse files nicely" + fi +} + # Check that a set of devices is available in the QEMU binary # _require_devices() diff --git a/tests/qemu-iotests/tests/mirror-sparse b/tests/qemu-iotests/tests/mirror-sparse index 11418c0871..cfcaa600ab 100755 --- a/tests/qemu-iotests/tests/mirror-sparse +++ b/tests/qemu-iotests/tests/mirror-sparse @@ -40,6 +40,7 @@ cd .. _supported_fmt qcow2 raw # Format of the source. dst is always raw file _supported_proto file _supported_os Linux +_require_disk_usage echo echo "=== Initial image setup ===" diff --git a/tests/qemu-iotests/tests/write-zeroes-unmap b/tests/qemu-iotests/tests/write-zeroes-unmap index 7cfeeaf839..f90fb8e8d2 100755 --- a/tests/qemu-iotests/tests/write-zeroes-unmap +++ b/tests/qemu-iotests/tests/write-zeroes-unmap @@ -32,6 +32,7 @@ cd .. _supported_fmt raw _supported_proto file _supported_os Linux +_require_disk_usage create_test_image() { _make_test_img -f $IMGFMT 1m From e1c9c801023d556d317256a414562634b1fe8b13 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 21 May 2025 16:51:12 +0200 Subject: [PATCH 1304/2760] tests/functional/test_sparc64_tuxrun: Explicitly set the 'sun4u' machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use self.set_machine() to set the machine instead of relying on the default machine of the binary. This way the test can be skipped in case the machine has not been compiled into the QEMU binary. Reviewed-by: Alex Bennée Tested-by: Alex Bennée Signed-off-by: Thomas Huth Message-ID: <20250521145112.142222-1-thuth@redhat.com> --- tests/functional/test_sparc64_tuxrun.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/test_sparc64_tuxrun.py b/tests/functional/test_sparc64_tuxrun.py index 3be08d6102..0d7b43dd74 100755 --- a/tests/functional/test_sparc64_tuxrun.py +++ b/tests/functional/test_sparc64_tuxrun.py @@ -24,6 +24,7 @@ class TuxRunSparc64Test(TuxRunBaselineTest): '479c3dc104c82b68be55e2c0c5c38cd473d0b37ad4badccde4775bb88ce34611') def test_sparc64(self): + self.set_machine('sun4u') self.root='sda' self.wait_for_shutdown=False self.common_tuxrun(kernel_asset=self.ASSET_SPARC64_KERNEL, From 644ded5c814055feb4d3a546628ccd28102f7acb Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 22 May 2025 10:02:08 +0200 Subject: [PATCH 1305/2760] tests/functional/test_mips_malta: Re-enable the check for the PCI host bridge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The problem with the PCI bridge has been fixed in commit e5894fd6f411c1 ("hw/pci-host/gt64120: Fix endianness handling"), so we can enable the corresponding test again. Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250522080208.205489-1-thuth@redhat.com> --- tests/functional/test_mips_malta.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_mips_malta.py b/tests/functional/test_mips_malta.py index 89b9556f30..30279f0ff2 100755 --- a/tests/functional/test_mips_malta.py +++ b/tests/functional/test_mips_malta.py @@ -80,10 +80,8 @@ def mips_check_wheezy(test, kernel_path, image_path, kernel_command_line, exec_command_and_wait_for_pattern(test, 'cat /proc/devices', 'usb') exec_command_and_wait_for_pattern(test, 'cat /proc/ioports', ' : piix4_smbus') - # lspci for the host bridge does not work on big endian targets: - # https://gitlab.com/qemu-project/qemu/-/issues/2826 - # exec_command_and_wait_for_pattern(test, 'lspci -d 11ab:4620', - # 'GT-64120') + exec_command_and_wait_for_pattern(test, 'lspci -d 11ab:4620', + 'GT-64120') exec_command_and_wait_for_pattern(test, 'cat /sys/bus/i2c/devices/i2c-0/name', 'SMBus PIIX4 adapter') From 9f7cf938efc6016f7ce323b064c2f3f46360c751 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 21 May 2025 16:37:32 +0200 Subject: [PATCH 1306/2760] tests/functional/test_mem_addr_space: Use set_machine() to select the machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By using self.set_machine() the tests get properly skipped in case the machine has not been compiled into the QEMU binary, e.g. when "configure" has been run with "--without-default-devices". Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Thomas Huth Message-ID: <20250521143732.140711-1-thuth@redhat.com> --- tests/functional/test_mem_addr_space.py | 63 +++++++++++++------------ 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/tests/functional/test_mem_addr_space.py b/tests/functional/test_mem_addr_space.py index 2d9d31efb5..61b4a190b4 100755 --- a/tests/functional/test_mem_addr_space.py +++ b/tests/functional/test_mem_addr_space.py @@ -58,8 +58,8 @@ def test_phybits_low_pse36(self): should start fine. """ self.ensure_64bit_binary() - self.vm.add_args('-S', '-machine', 'q35', '-m', - '512,slots=1,maxmem=59.6G', + self.set_machine('q35') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=59.6G', '-cpu', 'pentium,pse36=on', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -76,8 +76,8 @@ def test_phybits_low_pae(self): with pse36 above. """ self.ensure_64bit_binary() - self.vm.add_args('-S', '-machine', 'q35', '-m', - '512,slots=1,maxmem=59.6G', + self.set_machine('q35') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=59.6G', '-cpu', 'pentium,pae=on', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -93,8 +93,8 @@ def test_phybits_ok_pentium_pse36(self): same options as the failing case above with pse36 cpu feature. """ self.ensure_64bit_binary() - self.vm.add_args('-machine', 'q35', '-m', - '512,slots=1,maxmem=59.5G', + self.set_machine('q35') + self.vm.add_args('-m', '512,slots=1,maxmem=59.5G', '-cpu', 'pentium,pse36=on', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -111,8 +111,8 @@ def test_phybits_ok_pentium_pae(self): with the same options as the case above. """ self.ensure_64bit_binary() - self.vm.add_args('-machine', 'q35', '-m', - '512,slots=1,maxmem=59.5G', + self.set_machine('q35') + self.vm.add_args('-m', '512,slots=1,maxmem=59.5G', '-cpu', 'pentium,pae=on', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -128,8 +128,8 @@ def test_phybits_ok_pentium2(self): with pse36 ON. """ self.ensure_64bit_binary() - self.vm.add_args('-machine', 'q35', '-m', - '512,slots=1,maxmem=59.5G', + self.set_machine('q35') + self.vm.add_args('-m', '512,slots=1,maxmem=59.5G', '-cpu', 'pentium2', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -148,8 +148,8 @@ def test_phybits_low_nonpse36(self): above 4 GiB due to the PCI hole and simplicity. """ self.ensure_64bit_binary() - self.vm.add_args('-S', '-machine', 'q35', '-m', - '512,slots=1,maxmem=4G', + self.set_machine('q35') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=4G', '-cpu', 'pentium', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -176,8 +176,8 @@ def test_phybits_low_tcg_q35_70_amd(self): make QEMU fail with the error message. """ self.ensure_64bit_binary() - self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', - '512,slots=1,maxmem=988G', + self.set_machine('pc-q35-7.0') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=988G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -197,8 +197,8 @@ def test_phybits_low_tcg_q35_71_amd(self): than 988 GiB). """ self.ensure_64bit_binary() - self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=976G', + self.set_machine('pc-q35-7.1') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=976G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -214,8 +214,8 @@ def test_phybits_ok_tcg_q35_70_amd(self): successfully start when maxmem is < 988G. """ self.ensure_64bit_binary() - self.vm.add_args('-S', '-machine', 'pc-q35-7.0', '-m', - '512,slots=1,maxmem=987.5G', + self.set_machine('pc-q35-7.0') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=987.5G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -231,8 +231,8 @@ def test_phybits_ok_tcg_q35_71_amd(self): successfully start when maxmem is < 976G. """ self.ensure_64bit_binary() - self.vm.add_args('-S', '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=975.5G', + self.set_machine('pc-q35-7.1') + self.vm.add_args('-S', '-m', '512,slots=1,maxmem=975.5G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -249,9 +249,9 @@ def test_phybits_ok_tcg_q35_71_intel(self): "above_4G" memory starts at 4G. """ self.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') self.vm.add_args('-S', '-cpu', 'Skylake-Server', - '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=976G', + '-m', '512,slots=1,maxmem=976G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -274,9 +274,9 @@ def test_phybits_low_tcg_q35_71_amd_41bits(self): fail to start. """ self.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', - '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=992G', + '-m', '512,slots=1,maxmem=992G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -293,9 +293,9 @@ def test_phybits_ok_tcg_q35_71_amd_41bits(self): QEMU should start fine. """ self.ensure_64bit_binary() + self.set_machine('pc-q35-7.1') self.vm.add_args('-S', '-cpu', 'EPYC-v4,phys-bits=41', - '-machine', 'pc-q35-7.1', '-m', - '512,slots=1,maxmem=990G', + '-m', '512,slots=1,maxmem=990G', '-display', 'none', '-object', 'memory-backend-ram,id=mem1,size=1G', '-device', 'pc-dimm,id=vm0,memdev=mem1') @@ -314,12 +314,12 @@ def test_phybits_low_tcg_q35_intel_cxl(self): alignment constraints with 40 bits (1 TiB) of processor physical bits. """ self.ensure_64bit_binary() + self.set_machine('q35') self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', - '-machine', 'q35,cxl=on', '-m', - '512,slots=1,maxmem=987G', + '-m', '512,slots=1,maxmem=987G', '-display', 'none', '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1', - '-M', 'cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=1G') + '-M', 'cxl=on,cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=1G') self.vm.set_qmp_monitor(enabled=False) self.vm.launch() self.vm.wait() @@ -333,9 +333,10 @@ def test_phybits_ok_tcg_q35_intel_cxl(self): with cxl enabled. """ self.ensure_64bit_binary() + self.set_machine('q35') self.vm.add_args('-S', '-cpu', 'Skylake-Server,phys-bits=40', - '-machine', 'q35,cxl=on', '-m', - '512,slots=1,maxmem=987G', + '-machine', 'cxl=on', + '-m', '512,slots=1,maxmem=987G', '-display', 'none', '-device', 'pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1') self.vm.set_qmp_monitor(enabled=False) From 141ec228deb7f94fb713c4d7ce0276e088e59f15 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 15 May 2025 15:20:16 +0200 Subject: [PATCH 1307/2760] hw/microblaze: Add endianness property to the petalogix_s3adsp1800 machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since the microblaze target can now handle both endianness, big and little, we should provide a config knob for the user to select the desired endianness. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250515132019.569365-2-thuth@redhat.com> --- hw/microblaze/petalogix_s3adsp1800_mmu.c | 42 +++++++++++++++++++++--- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/hw/microblaze/petalogix_s3adsp1800_mmu.c b/hw/microblaze/petalogix_s3adsp1800_mmu.c index 032f6f70ea..e8d0ddfdf8 100644 --- a/hw/microblaze/petalogix_s3adsp1800_mmu.c +++ b/hw/microblaze/petalogix_s3adsp1800_mmu.c @@ -58,9 +58,20 @@ #define TYPE_PETALOGIX_S3ADSP1800_MACHINE \ MACHINE_TYPE_NAME("petalogix-s3adsp1800") +struct S3Adsp1800MachineState { + MachineState parent_class; + + EndianMode endianness; +}; + +OBJECT_DECLARE_TYPE(S3Adsp1800MachineState, MachineClass, + PETALOGIX_S3ADSP1800_MACHINE) + + static void petalogix_s3adsp1800_init(MachineState *machine) { + S3Adsp1800MachineState *psms = PETALOGIX_S3ADSP1800_MACHINE(machine); ram_addr_t ram_size = machine->ram_size; DeviceState *dev; MicroBlazeCPU *cpu; @@ -71,13 +82,12 @@ petalogix_s3adsp1800_init(MachineState *machine) MemoryRegion *phys_ram = g_new(MemoryRegion, 1); qemu_irq irq[32]; MemoryRegion *sysmem = get_system_memory(); - EndianMode endianness = TARGET_BIG_ENDIAN ? ENDIAN_MODE_BIG - : ENDIAN_MODE_LITTLE; + EndianMode endianness = psms->endianness; cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU)); object_property_set_str(OBJECT(cpu), "version", "7.10.d", &error_abort); object_property_set_bool(OBJECT(cpu), "little-endian", - !TARGET_BIG_ENDIAN, &error_abort); + endianness == ENDIAN_MODE_LITTLE, &error_abort); qdev_realize(DEVICE(cpu), NULL, &error_abort); /* Attach emulated BRAM through the LMB. */ @@ -135,20 +145,41 @@ petalogix_s3adsp1800_init(MachineState *machine) create_unimplemented_device("xps_gpio", GPIO_BASEADDR, 0x10000); - microblaze_load_kernel(cpu, !TARGET_BIG_ENDIAN, ddr_base, ram_size, - machine->initrd_filename, + microblaze_load_kernel(cpu, endianness == ENDIAN_MODE_LITTLE, ddr_base, + ram_size, machine->initrd_filename, BINARY_DEVICE_TREE_FILE, NULL); } +static int machine_get_endianness(Object *obj, Error **errp G_GNUC_UNUSED) +{ + S3Adsp1800MachineState *ms = PETALOGIX_S3ADSP1800_MACHINE(obj); + return ms->endianness; +} + +static void machine_set_endianness(Object *obj, int endianness, Error **errp) +{ + S3Adsp1800MachineState *ms = PETALOGIX_S3ADSP1800_MACHINE(obj); + ms->endianness = endianness; +} + static void petalogix_s3adsp1800_machine_class_init(ObjectClass *oc, const void *data) { MachineClass *mc = MACHINE_CLASS(oc); + ObjectProperty *prop; mc->desc = "PetaLogix linux refdesign for xilinx Spartan 3ADSP1800"; mc->init = petalogix_s3adsp1800_init; mc->is_default = true; + + prop = object_class_property_add_enum(oc, "endianness", "EndianMode", + &EndianMode_lookup, + machine_get_endianness, + machine_set_endianness); + object_property_set_default_str(prop, TARGET_BIG_ENDIAN ? "big" : "little"); + object_class_property_set_description(oc, "endianness", + "Defines whether the machine runs in big or little endian mode"); } static const TypeInfo petalogix_s3adsp1800_machine_types[] = { @@ -156,6 +187,7 @@ static const TypeInfo petalogix_s3adsp1800_machine_types[] = { .name = TYPE_PETALOGIX_S3ADSP1800_MACHINE, .parent = TYPE_MACHINE, .class_init = petalogix_s3adsp1800_machine_class_init, + .instance_size = sizeof(S3Adsp1800MachineState), }, }; From 6c5477558490cf76dc6d521fe906a2bfbc96ee27 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 15 May 2025 15:20:17 +0200 Subject: [PATCH 1308/2760] tests/functional: Test both microblaze s3adsp1800 endianness variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the endianness of the petalogix-s3adsp1800 can be configured, we should test that the cross-endianness also works as expected, thus test the big endian variant on the little endian target and vice versa. (based on an original idea from Philippe Mathieu-Daudé) Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250515132019.569365-3-thuth@redhat.com> --- tests/functional/test_microblaze_s3adsp1800.py | 18 +++++++++++++----- .../functional/test_microblazeel_s3adsp1800.py | 6 +++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/tests/functional/test_microblaze_s3adsp1800.py b/tests/functional/test_microblaze_s3adsp1800.py index c93fa14232..f093b162c0 100755 --- a/tests/functional/test_microblaze_s3adsp1800.py +++ b/tests/functional/test_microblaze_s3adsp1800.py @@ -25,12 +25,14 @@ class MicroblazeMachine(QemuSystemTest): ('http://www.qemu-advent-calendar.org/2023/download/day13.tar.gz'), 'b9b3d43c5dd79db88ada495cc6e0d1f591153fe41355e925d791fbf44de50c22') - def do_ballerina_be_test(self, machine): - self.set_machine(machine) + def do_ballerina_be_test(self, force_endianness=False): + self.set_machine('petalogix-s3adsp1800') self.archive_extract(self.ASSET_IMAGE_BE) self.vm.set_console() self.vm.add_args('-kernel', self.scratch_file('day17', 'ballerina.bin')) + if force_endianness: + self.vm.add_args('-M', 'endianness=big') self.vm.launch() wait_for_console_pattern(self, 'This architecture does not have ' 'kernel memory protection') @@ -39,12 +41,14 @@ def do_ballerina_be_test(self, machine): # message, that's why we don't test for a later string here. This # needs some investigation by a microblaze wizard one day... - def do_xmaton_le_test(self, machine): + def do_xmaton_le_test(self, force_endianness=False): self.require_netdev('user') - self.set_machine(machine) + self.set_machine('petalogix-s3adsp1800') self.archive_extract(self.ASSET_IMAGE_LE) self.vm.set_console() self.vm.add_args('-kernel', self.scratch_file('day13', 'xmaton.bin')) + if force_endianness: + self.vm.add_args('-M', 'endianness=little') tftproot = self.scratch_file('day13') self.vm.add_args('-nic', f'user,tftp={tftproot}') self.vm.launch() @@ -59,9 +63,13 @@ def do_xmaton_le_test(self, machine): class MicroblazeBigEndianMachine(MicroblazeMachine): ASSET_IMAGE_BE = MicroblazeMachine.ASSET_IMAGE_BE + ASSET_IMAGE_LE = MicroblazeMachine.ASSET_IMAGE_LE def test_microblaze_s3adsp1800_legacy_be(self): - self.do_ballerina_be_test('petalogix-s3adsp1800') + self.do_ballerina_be_test() + + def test_microblaze_s3adsp1800_legacy_le(self): + self.do_xmaton_le_test(force_endianness=True) if __name__ == '__main__': diff --git a/tests/functional/test_microblazeel_s3adsp1800.py b/tests/functional/test_microblazeel_s3adsp1800.py index ab59941d57..915902d48b 100755 --- a/tests/functional/test_microblazeel_s3adsp1800.py +++ b/tests/functional/test_microblazeel_s3adsp1800.py @@ -13,9 +13,13 @@ class MicroblazeLittleEndianMachine(MicroblazeMachine): ASSET_IMAGE_LE = MicroblazeMachine.ASSET_IMAGE_LE + ASSET_IMAGE_BE = MicroblazeMachine.ASSET_IMAGE_BE def test_microblaze_s3adsp1800_legacy_le(self): - self.do_xmaton_le_test('petalogix-s3adsp1800') + self.do_xmaton_le_test() + + def test_microblaze_s3adsp1800_legacy_be(self): + self.do_ballerina_be_test(force_endianness=True) if __name__ == '__main__': From 0e259fa5a13a3d0ff65aa4199b1e03832e51e1b2 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 15 May 2025 15:20:18 +0200 Subject: [PATCH 1309/2760] hw/microblaze: Remove the big-endian variants of ml605 and xlnx-zynqmp-pmu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both machines were added with little-endian in mind only (the "endianness" CPU property was hard-wired to "true", see commits 133d23b3ad1 and a88bbb006a52), so the variants that showed up on the big endian target likely never worked. We deprecated these non-working machine variants two releases ago, and so far nobody complained, so it should be fine now to disable them. Hard-wire the machines to little endian now. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250515132019.569365-4-thuth@redhat.com> --- docs/about/deprecated.rst | 6 ------ docs/about/removed-features.rst | 9 +++++++++ hw/microblaze/petalogix_ml605_mmu.c | 15 ++++----------- hw/microblaze/xlnx-zynqmp-pmu.c | 7 +------ 4 files changed, 14 insertions(+), 23 deletions(-) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 4715d1ede5..863779ae78 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -315,12 +315,6 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. -Big-Endian variants of MicroBlaze ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` machines (since 9.2) -'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' - -Both ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` were added for little endian -CPUs. Big endian support is not tested. - Mips ``mipssim`` machine (since 10.0) ''''''''''''''''''''''''''''''''''''' diff --git a/docs/about/removed-features.rst b/docs/about/removed-features.rst index 4819cb4665..d7c2113fc3 100644 --- a/docs/about/removed-features.rst +++ b/docs/about/removed-features.rst @@ -1091,6 +1091,15 @@ This machine was removed because PPC 405 CPU have no known users, firmware images are not available, OpenWRT dropped support in 2019, U-Boot in 2017, and Linux in 2024. +Big-Endian variants of ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` machines (removed in 10.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' + +Both the MicroBlaze ``petalogix-ml605`` and ``xlnx-zynqmp-pmu`` machines +were added for little endian CPUs. Big endian support was never tested +and likely never worked. Starting with QEMU v10.1, the machines are now +only available as little-endian machines. + + linux-user mode CPUs -------------------- diff --git a/hw/microblaze/petalogix_ml605_mmu.c b/hw/microblaze/petalogix_ml605_mmu.c index bea6b689fd..6e923c49cf 100644 --- a/hw/microblaze/petalogix_ml605_mmu.c +++ b/hw/microblaze/petalogix_ml605_mmu.c @@ -80,8 +80,6 @@ petalogix_ml605_init(MachineState *machine) MemoryRegion *phys_lmb_bram = g_new(MemoryRegion, 1); MemoryRegion *phys_ram = g_new(MemoryRegion, 1); qemu_irq irq[32]; - EndianMode endianness = TARGET_BIG_ENDIAN ? ENDIAN_MODE_BIG - : ENDIAN_MODE_LITTLE; /* init CPUs */ cpu = MICROBLAZE_CPU(object_new(TYPE_MICROBLAZE_CPU)); @@ -113,7 +111,7 @@ petalogix_ml605_init(MachineState *machine) dev = qdev_new("xlnx.xps-intc"); - qdev_prop_set_enum(dev, "endianness", endianness); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); qdev_prop_set_uint32(dev, "kind-of-intr", 1 << TIMER_IRQ); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, INTC_BASEADDR); @@ -129,7 +127,7 @@ petalogix_ml605_init(MachineState *machine) /* 2 timers at irq 2 @ 100 Mhz. */ dev = qdev_new("xlnx.xps-timer"); - qdev_prop_set_enum(dev, "endianness", endianness); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); qdev_prop_set_uint32(dev, "one-timer-only", 0); qdev_prop_set_uint32(dev, "clock-frequency", 100 * 1000000); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); @@ -177,7 +175,7 @@ petalogix_ml605_init(MachineState *machine) SSIBus *spi; dev = qdev_new("xlnx.xps-spi"); - qdev_prop_set_enum(dev, "endianness", endianness); + qdev_prop_set_enum(dev, "endianness", ENDIAN_MODE_LITTLE); qdev_prop_set_uint8(dev, "num-ss-bits", NUM_SPI_FLASHES); busdev = SYS_BUS_DEVICE(dev); sysbus_realize_and_unref(busdev, &error_fatal); @@ -218,12 +216,7 @@ petalogix_ml605_init(MachineState *machine) static void petalogix_ml605_machine_init(MachineClass *mc) { - if (TARGET_BIG_ENDIAN) { - mc->desc = "PetaLogix linux refdesign for xilinx ml605 (big endian)"; - mc->deprecation_reason = "big endian support is not tested"; - } else { - mc->desc = "PetaLogix linux refdesign for xilinx ml605 (little endian)"; - } + mc->desc = "PetaLogix linux refdesign for xilinx ml605 (little endian)"; mc->init = petalogix_ml605_init; } diff --git a/hw/microblaze/xlnx-zynqmp-pmu.c b/hw/microblaze/xlnx-zynqmp-pmu.c index ed40b5f2e0..e909802bb7 100644 --- a/hw/microblaze/xlnx-zynqmp-pmu.c +++ b/hw/microblaze/xlnx-zynqmp-pmu.c @@ -181,12 +181,7 @@ static void xlnx_zynqmp_pmu_init(MachineState *machine) static void xlnx_zynqmp_pmu_machine_init(MachineClass *mc) { - if (TARGET_BIG_ENDIAN) { - mc->desc = "Xilinx ZynqMP PMU machine (big endian)"; - mc->deprecation_reason = "big endian support is not tested"; - } else { - mc->desc = "Xilinx ZynqMP PMU machine (little endian)"; - } + mc->desc = "Xilinx ZynqMP PMU machine (little endian)"; mc->init = xlnx_zynqmp_pmu_init; } From 225e9e230efd5ae509b12fd191a8de6287f934ef Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 15 May 2025 15:20:19 +0200 Subject: [PATCH 1310/2760] docs: Deprecate the qemu-system-microblazeel binary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The (former big-endian only) binary qemu-system-microblaze can handle both endiannesses nowadays, so we don't need the separate qemu-system-microblazeel binary for little endian anymore. Let's deprecate it to avoid unnecessary compilation and test time in the future. Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250515132019.569365-5-thuth@redhat.com> --- docs/about/deprecated.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 863779ae78..42037131de 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -345,6 +345,19 @@ machine must ensure that they're setting the ``spike`` machine in the command line (``-M spike``). +System emulator binaries +------------------------ + +``qemu-system-microblazeel`` (since 10.1) +''''''''''''''''''''''''''''''''''''''''' + +The ``qemu-system-microblaze`` binary can emulate little-endian machines +now, too, so the separate binary ``qemu-system-microblazeel`` (with the +``el`` suffix) for little-endian targets is not required anymore. The +``petalogix-s3adsp1800`` machine can now be switched to little endian by +setting its ``endianness`` property to ``little``. + + Backend options --------------- From 07a2adeebbe522b6e1c5706db2bdba0b05d0b2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:30 +0200 Subject: [PATCH 1311/2760] hw/i386/pc: Remove deprecated pc-q35-2.4 and pc-i440fx-2.4 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. Remove the qtest in test-x86-cpuid-compat.c file. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-2-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/i386/pc_piix.c | 13 ------------- hw/i386/pc_q35.c | 13 ------------- tests/qtest/test-x86-cpuid-compat.c | 14 -------------- 3 files changed, 40 deletions(-) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 0dce512f18..04213b45b4 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -791,19 +791,6 @@ static void pc_i440fx_machine_2_5_options(MachineClass *m) DEFINE_I440FX_MACHINE(2, 5); -static void pc_i440fx_machine_2_4_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_i440fx_machine_2_5_options(m); - m->hw_version = "2.4.0"; - pcmc->broken_reserved_end = true; - compat_props_add(m->compat_props, hw_compat_2_4, hw_compat_2_4_len); - compat_props_add(m->compat_props, pc_compat_2_4, pc_compat_2_4_len); -} - -DEFINE_I440FX_MACHINE(2, 4); - #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index c538b3d05b..47e1260241 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -685,16 +685,3 @@ static void pc_q35_machine_2_5_options(MachineClass *m) } DEFINE_Q35_MACHINE(2, 5); - -static void pc_q35_machine_2_4_options(MachineClass *m) -{ - PCMachineClass *pcmc = PC_MACHINE_CLASS(m); - - pc_q35_machine_2_5_options(m); - m->hw_version = "2.4.0"; - pcmc->broken_reserved_end = true; - compat_props_add(m->compat_props, hw_compat_2_4, hw_compat_2_4_len); - compat_props_add(m->compat_props, pc_compat_2_4, pc_compat_2_4_len); -} - -DEFINE_Q35_MACHINE(2, 4); diff --git a/tests/qtest/test-x86-cpuid-compat.c b/tests/qtest/test-x86-cpuid-compat.c index c9de47bb26..456e2af665 100644 --- a/tests/qtest/test-x86-cpuid-compat.c +++ b/tests/qtest/test-x86-cpuid-compat.c @@ -365,20 +365,6 @@ int main(int argc, char **argv) "level", 10); } - /* - * xlevel doesn't have any feature that triggers auto-level - * code on old machine-types. Just check that the compat code - * is working correctly: - */ - if (qtest_has_machine("pc-i440fx-2.4")) { - add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-off", - "SandyBridge", NULL, "pc-i440fx-2.4", - "xlevel", 0x80000008); - add_cpuid_test("x86/cpuid/xlevel-compat/pc-i440fx-2.4/npt-on", - "SandyBridge", "svm=on,npt=on", "pc-i440fx-2.4", - "xlevel", 0x80000008); - } - /* Test feature parsing */ add_feature_test("x86/cpuid/features/plus", "486", "+arat", From 8b1c560937467d0d96c1d0948e99f86ce188c0bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:31 +0200 Subject: [PATCH 1312/2760] hw/i386/pc: Remove PCMachineClass::broken_reserved_end field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PCMachineClass::broken_reserved_end field was only used by the pc-q35-2.4 and pc-i440fx-2.4 machines, which got removed. Remove it and simplify pc_memory_init(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-3-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/i386/pc.c | 13 +++++-------- include/hw/i386/pc.h | 1 - 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 70656157ca..c8bb4a3ee4 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -999,14 +999,13 @@ void pc_memory_init(PCMachineState *pcms, if (machine->device_memory) { uint64_t *val = g_malloc(sizeof(*val)); - uint64_t res_mem_end = machine->device_memory->base; - - if (!pcmc->broken_reserved_end) { - res_mem_end += memory_region_size(&machine->device_memory->mr); - } + uint64_t res_mem_end; if (pcms->cxl_devices_state.is_enabled) { res_mem_end = cxl_resv_end; + } else { + res_mem_end = machine->device_memory->base + + memory_region_size(&machine->device_memory->mr); } *val = cpu_to_le64(ROUND_UP(res_mem_end, 1 * GiB)); fw_cfg_add_file(fw_cfg, "etc/reserved-memory-end", val, sizeof(*val)); @@ -1044,9 +1043,7 @@ uint64_t pc_pci_hole64_start(void) hole64_start = pc_get_cxl_range_end(pcms); } else if (pcmc->has_reserved_memory && (ms->ram_size < ms->maxram_size)) { pc_get_device_memory_range(pcms, &hole64_start, &size); - if (!pcmc->broken_reserved_end) { - hole64_start += size; - } + hole64_start += size; } else { hole64_start = pc_above_4g_end(pcms); } diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index 9563674e2d..f4a874b17f 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -107,7 +107,6 @@ struct PCMachineClass { /* RAM / address space compat: */ bool gigabyte_align; bool has_reserved_memory; - bool broken_reserved_end; bool enforce_amd_1tb_hole; bool isa_bios_alias; From 4c82e7b34b1bf35d97e026196f5bf10ea916512c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:32 +0200 Subject: [PATCH 1313/2760] hw/i386/pc: Remove pc_compat_2_4[] array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The pc_compat_2_4[] array was only used by the pc-q35-2.4 and pc-i440fx-2.4 machines, which got removed. Remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-4-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/i386/pc.c | 19 ------------------- include/hw/i386/pc.h | 3 --- 2 files changed, 22 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index c8bb4a3ee4..2b46714a5a 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -262,25 +262,6 @@ const size_t pc_compat_2_6_len = G_N_ELEMENTS(pc_compat_2_6); GlobalProperty pc_compat_2_5[] = {}; const size_t pc_compat_2_5_len = G_N_ELEMENTS(pc_compat_2_5); -GlobalProperty pc_compat_2_4[] = { - PC_CPU_MODEL_IDS("2.4.0") - { "Haswell-" TYPE_X86_CPU, "abm", "off" }, - { "Haswell-noTSX-" TYPE_X86_CPU, "abm", "off" }, - { "Broadwell-" TYPE_X86_CPU, "abm", "off" }, - { "Broadwell-noTSX-" TYPE_X86_CPU, "abm", "off" }, - { "host" "-" TYPE_X86_CPU, "host-cache-info", "on" }, - { TYPE_X86_CPU, "check", "off" }, - { "qemu64" "-" TYPE_X86_CPU, "sse4a", "on" }, - { "qemu64" "-" TYPE_X86_CPU, "abm", "on" }, - { "qemu64" "-" TYPE_X86_CPU, "popcnt", "on" }, - { "qemu32" "-" TYPE_X86_CPU, "popcnt", "on" }, - { "Opteron_G2" "-" TYPE_X86_CPU, "rdtscp", "on" }, - { "Opteron_G3" "-" TYPE_X86_CPU, "rdtscp", "on" }, - { "Opteron_G4" "-" TYPE_X86_CPU, "rdtscp", "on" }, - { "Opteron_G5" "-" TYPE_X86_CPU, "rdtscp", "on", } -}; -const size_t pc_compat_2_4_len = G_N_ELEMENTS(pc_compat_2_4); - /* * @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 f4a874b17f..b34aa25fdc 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -301,9 +301,6 @@ extern const size_t pc_compat_2_6_len; extern GlobalProperty pc_compat_2_5[]; extern const size_t pc_compat_2_5_len; -extern GlobalProperty pc_compat_2_4[]; -extern const size_t pc_compat_2_4_len; - #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, \ const void *data) \ From 60ce3f67bea0a782a58cf4f71840e8d20ef8ddfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:34 +0200 Subject: [PATCH 1314/2760] hw/core/machine: Remove hw_compat_2_4[] array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hw_compat_2_4[] array was only used by the pc-q35-2.4 and pc-i440fx-2.4 machines, which got removed. Remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-6-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/core/machine.c | 9 --------- include/hw/boards.h | 3 --- 2 files changed, 12 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index c3f3a5020d..15cd2bc3c4 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -294,15 +294,6 @@ GlobalProperty hw_compat_2_5[] = { }; const size_t hw_compat_2_5_len = G_N_ELEMENTS(hw_compat_2_5); -GlobalProperty hw_compat_2_4[] = { - { "e1000", "extra_mac_registers", "off" }, - { "virtio-pci", "x-disable-pcie", "on" }, - { "virtio-pci", "migrate-extra", "off" }, - { "fw_cfg_mem", "dma_enabled", "off" }, - { "fw_cfg_io", "dma_enabled", "off" } -}; -const size_t hw_compat_2_4_len = G_N_ELEMENTS(hw_compat_2_4); - MachineState *current_machine; static char *machine_get_kernel(Object *obj, Error **errp) diff --git a/include/hw/boards.h b/include/hw/boards.h index a7b1fcffae..03e7cbeae8 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -866,7 +866,4 @@ extern const size_t hw_compat_2_6_len; extern GlobalProperty hw_compat_2_5[]; extern const size_t hw_compat_2_5_len; -extern GlobalProperty hw_compat_2_4[]; -extern const size_t hw_compat_2_4_len; - #endif From 0bf8727696267a79658998d844a96e35e0353602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:35 +0200 Subject: [PATCH 1315/2760] hw/net/e1000: Remove unused E1000_FLAG_MAC flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit E1000_FLAG_MAC was only used by the hw_compat_2_4[] array, via the 'extra_mac_registers=off' property. We removed all machines using that array, lets remove all the code around E1000_FLAG_MAC, including the MAC_ACCESS_FLAG_NEEDED enum, similarly to commit fa4ec9ffda7 ("e1000: remove old compatibility code"). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Message-ID: <20250512083948.39294-7-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/net/e1000.c | 95 ++++++++++++++++++++++---------------------------- 1 file changed, 41 insertions(+), 54 deletions(-) diff --git a/hw/net/e1000.c b/hw/net/e1000.c index cba4999e6d..a80a7b0cdb 100644 --- a/hw/net/e1000.c +++ b/hw/net/e1000.c @@ -127,10 +127,8 @@ struct E1000State_st { QEMUTimer *flush_queue_timer; /* Compatibility flags for migration to/from qemu 1.3.0 and older */ -#define E1000_FLAG_MAC_BIT 2 #define E1000_FLAG_TSO_BIT 3 #define E1000_FLAG_VET_BIT 4 -#define E1000_FLAG_MAC (1 << E1000_FLAG_MAC_BIT) #define E1000_FLAG_TSO (1 << E1000_FLAG_TSO_BIT) #define E1000_FLAG_VET (1 << E1000_FLAG_VET_BIT) @@ -1212,52 +1210,51 @@ enum { NWRITEOPS = ARRAY_SIZE(macreg_writeops) }; enum { MAC_ACCESS_PARTIAL = 1, MAC_ACCESS_FLAG_NEEDED = 2 }; -#define markflag(x) ((E1000_FLAG_##x << 2) | MAC_ACCESS_FLAG_NEEDED) /* In the array below the meaning of the bits is: [f|f|f|f|f|f|n|p] * f - flag bits (up to 6 possible flags) * n - flag needed - * p - partially implenented */ + * p - partially implemented */ static const uint8_t mac_reg_access[0x8000] = { - [IPAV] = markflag(MAC), [WUC] = markflag(MAC), - [IP6AT] = markflag(MAC), [IP4AT] = markflag(MAC), - [FFVT] = markflag(MAC), [WUPM] = markflag(MAC), - [ECOL] = markflag(MAC), [MCC] = markflag(MAC), - [DC] = markflag(MAC), [TNCRS] = markflag(MAC), - [RLEC] = markflag(MAC), [XONRXC] = markflag(MAC), - [XOFFTXC] = markflag(MAC), [RFC] = markflag(MAC), - [TSCTFC] = markflag(MAC), [MGTPRC] = markflag(MAC), - [WUS] = markflag(MAC), [AIT] = markflag(MAC), - [FFLT] = markflag(MAC), [FFMT] = markflag(MAC), - [SCC] = markflag(MAC), [FCRUC] = markflag(MAC), - [LATECOL] = markflag(MAC), [COLC] = markflag(MAC), - [SEQEC] = markflag(MAC), [CEXTERR] = markflag(MAC), - [XONTXC] = markflag(MAC), [XOFFRXC] = markflag(MAC), - [RJC] = markflag(MAC), [RNBC] = markflag(MAC), - [MGTPDC] = markflag(MAC), [MGTPTC] = markflag(MAC), - [RUC] = markflag(MAC), [ROC] = markflag(MAC), - [GORCL] = markflag(MAC), [GORCH] = markflag(MAC), - [GOTCL] = markflag(MAC), [GOTCH] = markflag(MAC), - [BPRC] = markflag(MAC), [MPRC] = markflag(MAC), - [TSCTC] = markflag(MAC), [PRC64] = markflag(MAC), - [PRC127] = markflag(MAC), [PRC255] = markflag(MAC), - [PRC511] = markflag(MAC), [PRC1023] = markflag(MAC), - [PRC1522] = markflag(MAC), [PTC64] = markflag(MAC), - [PTC127] = markflag(MAC), [PTC255] = markflag(MAC), - [PTC511] = markflag(MAC), [PTC1023] = markflag(MAC), - [PTC1522] = markflag(MAC), [MPTC] = markflag(MAC), - [BPTC] = markflag(MAC), - - [TDFH] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [TDFT] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [TDFHS] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [TDFTS] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [TDFPC] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [RDFH] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [RDFT] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [RDFHS] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [RDFTS] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [RDFPC] = markflag(MAC) | MAC_ACCESS_PARTIAL, - [PBM] = markflag(MAC) | MAC_ACCESS_PARTIAL, + [IPAV] = MAC_ACCESS_FLAG_NEEDED, [WUC] = MAC_ACCESS_FLAG_NEEDED, + [IP6AT] = MAC_ACCESS_FLAG_NEEDED, [IP4AT] = MAC_ACCESS_FLAG_NEEDED, + [FFVT] = MAC_ACCESS_FLAG_NEEDED, [WUPM] = MAC_ACCESS_FLAG_NEEDED, + [ECOL] = MAC_ACCESS_FLAG_NEEDED, [MCC] = MAC_ACCESS_FLAG_NEEDED, + [DC] = MAC_ACCESS_FLAG_NEEDED, [TNCRS] = MAC_ACCESS_FLAG_NEEDED, + [RLEC] = MAC_ACCESS_FLAG_NEEDED, [XONRXC] = MAC_ACCESS_FLAG_NEEDED, + [XOFFTXC] = MAC_ACCESS_FLAG_NEEDED, [RFC] = MAC_ACCESS_FLAG_NEEDED, + [TSCTFC] = MAC_ACCESS_FLAG_NEEDED, [MGTPRC] = MAC_ACCESS_FLAG_NEEDED, + [WUS] = MAC_ACCESS_FLAG_NEEDED, [AIT] = MAC_ACCESS_FLAG_NEEDED, + [FFLT] = MAC_ACCESS_FLAG_NEEDED, [FFMT] = MAC_ACCESS_FLAG_NEEDED, + [SCC] = MAC_ACCESS_FLAG_NEEDED, [FCRUC] = MAC_ACCESS_FLAG_NEEDED, + [LATECOL] = MAC_ACCESS_FLAG_NEEDED, [COLC] = MAC_ACCESS_FLAG_NEEDED, + [SEQEC] = MAC_ACCESS_FLAG_NEEDED, [CEXTERR] = MAC_ACCESS_FLAG_NEEDED, + [XONTXC] = MAC_ACCESS_FLAG_NEEDED, [XOFFRXC] = MAC_ACCESS_FLAG_NEEDED, + [RJC] = MAC_ACCESS_FLAG_NEEDED, [RNBC] = MAC_ACCESS_FLAG_NEEDED, + [MGTPDC] = MAC_ACCESS_FLAG_NEEDED, [MGTPTC] = MAC_ACCESS_FLAG_NEEDED, + [RUC] = MAC_ACCESS_FLAG_NEEDED, [ROC] = MAC_ACCESS_FLAG_NEEDED, + [GORCL] = MAC_ACCESS_FLAG_NEEDED, [GORCH] = MAC_ACCESS_FLAG_NEEDED, + [GOTCL] = MAC_ACCESS_FLAG_NEEDED, [GOTCH] = MAC_ACCESS_FLAG_NEEDED, + [BPRC] = MAC_ACCESS_FLAG_NEEDED, [MPRC] = MAC_ACCESS_FLAG_NEEDED, + [TSCTC] = MAC_ACCESS_FLAG_NEEDED, [PRC64] = MAC_ACCESS_FLAG_NEEDED, + [PRC127] = MAC_ACCESS_FLAG_NEEDED, [PRC255] = MAC_ACCESS_FLAG_NEEDED, + [PRC511] = MAC_ACCESS_FLAG_NEEDED, [PRC1023] = MAC_ACCESS_FLAG_NEEDED, + [PRC1522] = MAC_ACCESS_FLAG_NEEDED, [PTC64] = MAC_ACCESS_FLAG_NEEDED, + [PTC127] = MAC_ACCESS_FLAG_NEEDED, [PTC255] = MAC_ACCESS_FLAG_NEEDED, + [PTC511] = MAC_ACCESS_FLAG_NEEDED, [PTC1023] = MAC_ACCESS_FLAG_NEEDED, + [PTC1522] = MAC_ACCESS_FLAG_NEEDED, [MPTC] = MAC_ACCESS_FLAG_NEEDED, + [BPTC] = MAC_ACCESS_FLAG_NEEDED, + + [TDFH] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [TDFT] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [TDFHS] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [TDFTS] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [TDFPC] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [RDFH] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [RDFT] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [RDFHS] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [RDFTS] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [RDFPC] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, + [PBM] = MAC_ACCESS_FLAG_NEEDED | MAC_ACCESS_PARTIAL, }; static void @@ -1419,13 +1416,6 @@ static int e1000_tx_tso_post_load(void *opaque, int version_id) return 0; } -static bool e1000_full_mac_needed(void *opaque) -{ - E1000State *s = opaque; - - return chkflag(MAC); -} - static bool e1000_tso_state_needed(void *opaque) { E1000State *s = opaque; @@ -1451,7 +1441,6 @@ static const VMStateDescription vmstate_e1000_full_mac_state = { .name = "e1000/full_mac_state", .version_id = 1, .minimum_version_id = 1, - .needed = e1000_full_mac_needed, .fields = (const VMStateField[]) { VMSTATE_UINT32_ARRAY(mac_reg, E1000State, 0x8000), VMSTATE_END_OF_LIST() @@ -1679,8 +1668,6 @@ static void pci_e1000_realize(PCIDevice *pci_dev, Error **errp) static const Property e1000_properties[] = { DEFINE_NIC_PROPERTIES(E1000State, conf), - DEFINE_PROP_BIT("extra_mac_registers", E1000State, - compat_flags, E1000_FLAG_MAC_BIT, true), DEFINE_PROP_BIT("migrate_tso_props", E1000State, compat_flags, E1000_FLAG_TSO_BIT, true), DEFINE_PROP_BIT("init-vet", E1000State, From fce42ccb51f115156d41eb3c8bd04bddf4a0ca8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:36 +0200 Subject: [PATCH 1316/2760] hw/virtio/virtio-pci: Remove VIRTIO_PCI_FLAG_MIGRATE_EXTRA definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VIRTIO_PCI_FLAG_MIGRATE_EXTRA was only used by the hw_compat_2_4[] array, via the 'migrate-extra=true' property. We removed all machines using that array, lets remove all the code around VIRTIO_PCI_FLAG_MIGRATE_EXTRA. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Message-ID: <20250512083948.39294-8-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/virtio/virtio-pci.c | 6 +----- include/hw/virtio/virtio-pci.h | 4 ---- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 9b48aa8c3e..f52fac663c 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -146,9 +146,7 @@ static const VMStateDescription vmstate_virtio_pci = { static bool virtio_pci_has_extra_state(DeviceState *d) { - VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - - return proxy->flags & VIRTIO_PCI_FLAG_MIGRATE_EXTRA; + return true; } static void virtio_pci_save_extra_state(DeviceState *d, QEMUFile *f) @@ -2363,8 +2361,6 @@ static void virtio_pci_bus_reset_hold(Object *obj, ResetType type) static const Property virtio_pci_properties[] = { DEFINE_PROP_BIT("virtio-pci-bus-master-bug-migration", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, false), - DEFINE_PROP_BIT("migrate-extra", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT, true), DEFINE_PROP_BIT("modern-pio-notify", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, false), DEFINE_PROP_BIT("x-disable-pcie", VirtIOPCIProxy, flags, diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h index 1dbc3851b0..eb22ed0a1d 100644 --- a/include/hw/virtio/virtio-pci.h +++ b/include/hw/virtio/virtio-pci.h @@ -32,7 +32,6 @@ DECLARE_OBJ_CHECKERS(VirtioPCIBusState, VirtioPCIBusClass, enum { VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, - VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT, VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT, VIRTIO_PCI_FLAG_PAGE_PER_VQ_BIT, @@ -57,9 +56,6 @@ enum { /* virtio version flags */ #define VIRTIO_PCI_FLAG_DISABLE_PCIE (1 << VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT) -/* migrate extra state */ -#define VIRTIO_PCI_FLAG_MIGRATE_EXTRA (1 << VIRTIO_PCI_FLAG_MIGRATE_EXTRA_BIT) - /* have pio notification for modern device ? */ #define VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY \ (1 << VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT) From 47d9e81f0ab44b59e99f4e87fe5409851e670de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:37 +0200 Subject: [PATCH 1317/2760] hw/virtio/virtio-pci: Remove VIRTIO_PCI_FLAG_DISABLE_PCIE definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VIRTIO_PCI_FLAG_DISABLE_PCIE was only used by the hw_compat_2_4[] array, via the 'x-disable-pcie=false' property. We removed all machines using that array, lets remove all the code around VIRTIO_PCI_FLAG_DISABLE_PCIE (see commit 9a4c0e220d8 for similar VIRTIO_PCI_FLAG_* enum removal). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-9-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/virtio/virtio-pci.c | 5 +---- include/hw/virtio/virtio-pci.h | 4 ---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index f52fac663c..e62ae1e5e0 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -2363,8 +2363,6 @@ static const Property virtio_pci_properties[] = { VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, false), DEFINE_PROP_BIT("modern-pio-notify", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, false), - DEFINE_PROP_BIT("x-disable-pcie", VirtIOPCIProxy, flags, - VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT, false), DEFINE_PROP_BIT("page-per-vq", VirtIOPCIProxy, flags, VIRTIO_PCI_FLAG_PAGE_PER_VQ_BIT, false), DEFINE_PROP_BOOL("x-ignore-backend-features", VirtIOPCIProxy, @@ -2393,8 +2391,7 @@ static void virtio_pci_dc_realize(DeviceState *qdev, Error **errp) VirtIOPCIProxy *proxy = VIRTIO_PCI(qdev); PCIDevice *pci_dev = &proxy->pci_dev; - if (!(proxy->flags & VIRTIO_PCI_FLAG_DISABLE_PCIE) && - virtio_pci_modern(proxy)) { + if (virtio_pci_modern(proxy)) { pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; } diff --git a/include/hw/virtio/virtio-pci.h b/include/hw/virtio/virtio-pci.h index eb22ed0a1d..eab5394898 100644 --- a/include/hw/virtio/virtio-pci.h +++ b/include/hw/virtio/virtio-pci.h @@ -33,7 +33,6 @@ enum { VIRTIO_PCI_FLAG_BUS_MASTER_BUG_MIGRATION_BIT, VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT, - VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT, VIRTIO_PCI_FLAG_PAGE_PER_VQ_BIT, VIRTIO_PCI_FLAG_ATS_BIT, VIRTIO_PCI_FLAG_INIT_DEVERR_BIT, @@ -53,9 +52,6 @@ enum { * vcpu thread using ioeventfd for some devices. */ #define VIRTIO_PCI_FLAG_USE_IOEVENTFD (1 << VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT) -/* virtio version flags */ -#define VIRTIO_PCI_FLAG_DISABLE_PCIE (1 << VIRTIO_PCI_FLAG_DISABLE_PCIE_BIT) - /* have pio notification for modern device ? */ #define VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY \ (1 << VIRTIO_PCI_FLAG_MODERN_PIO_NOTIFY_BIT) From ff63280a81144500726f1899b0ae374e692e1f39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:38 +0200 Subject: [PATCH 1318/2760] hw/i386/pc: Remove deprecated pc-q35-2.5 and pc-i440fx-2.5 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. Remove the now unused empty pc_compat_2_5[] array. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-10-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/i386/pc.c | 3 --- hw/i386/pc_piix.c | 13 ------------- hw/i386/pc_q35.c | 13 ------------- include/hw/i386/pc.h | 3 --- 4 files changed, 32 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 2b46714a5a..cb375aabdc 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -259,9 +259,6 @@ GlobalProperty pc_compat_2_6[] = { }; const size_t pc_compat_2_6_len = G_N_ELEMENTS(pc_compat_2_6); -GlobalProperty pc_compat_2_5[] = {}; -const size_t pc_compat_2_5_len = G_N_ELEMENTS(pc_compat_2_5); - /* * @PC_FW_DATA: * Size of the chunk of memory at the top of RAM for the BIOS ACPI tables diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 04213b45b4..7a62bb0650 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -778,19 +778,6 @@ static void pc_i440fx_machine_2_6_options(MachineClass *m) DEFINE_I440FX_MACHINE(2, 6); -static void pc_i440fx_machine_2_5_options(MachineClass *m) -{ - X86MachineClass *x86mc = X86_MACHINE_CLASS(m); - - pc_i440fx_machine_2_6_options(m); - x86mc->save_tsc_khz = false; - m->legacy_fw_cfg_order = 1; - compat_props_add(m->compat_props, hw_compat_2_5, hw_compat_2_5_len); - compat_props_add(m->compat_props, pc_compat_2_5, pc_compat_2_5_len); -} - -DEFINE_I440FX_MACHINE(2, 5); - #ifdef CONFIG_ISAPC static void isapc_machine_options(MachineClass *m) { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 47e1260241..33211b1876 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -672,16 +672,3 @@ static void pc_q35_machine_2_6_options(MachineClass *m) } DEFINE_Q35_MACHINE(2, 6); - -static void pc_q35_machine_2_5_options(MachineClass *m) -{ - X86MachineClass *x86mc = X86_MACHINE_CLASS(m); - - pc_q35_machine_2_6_options(m); - x86mc->save_tsc_khz = false; - m->legacy_fw_cfg_order = 1; - compat_props_add(m->compat_props, hw_compat_2_5, hw_compat_2_5_len); - compat_props_add(m->compat_props, pc_compat_2_5, pc_compat_2_5_len); -} - -DEFINE_Q35_MACHINE(2, 5); diff --git a/include/hw/i386/pc.h b/include/hw/i386/pc.h index b34aa25fdc..79b72c54dd 100644 --- a/include/hw/i386/pc.h +++ b/include/hw/i386/pc.h @@ -298,9 +298,6 @@ extern const size_t pc_compat_2_7_len; extern GlobalProperty pc_compat_2_6[]; extern const size_t pc_compat_2_6_len; -extern GlobalProperty pc_compat_2_5[]; -extern const size_t pc_compat_2_5_len; - #define DEFINE_PC_MACHINE(suffix, namestr, initfn, optsfn) \ static void pc_machine_##suffix##_class_init(ObjectClass *oc, \ const void *data) \ From 42cbccfcb0635257721aa6a0e55cb80e85756ecf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:39 +0200 Subject: [PATCH 1319/2760] hw/i386/x86: Remove X86MachineClass::save_tsc_khz field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The X86MachineClass::save_tsc_khz boolean was only used by the pc-q35-2.5 and pc-i440fx-2.5 machines, which got removed. Remove it and simplify tsc_khz_needed(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-11-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/i386/x86.c | 1 - include/hw/i386/x86.h | 5 ----- target/i386/machine.c | 5 ++--- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/hw/i386/x86.c b/hw/i386/x86.c index e2d0409299..f80533df1c 100644 --- a/hw/i386/x86.c +++ b/hw/i386/x86.c @@ -382,7 +382,6 @@ static void x86_machine_class_init(ObjectClass *oc, const void *data) mc->get_default_cpu_node_id = x86_get_default_cpu_node_id; mc->possible_cpu_arch_ids = x86_possible_cpu_arch_ids; mc->kvm_type = x86_kvm_type; - x86mc->save_tsc_khz = true; x86mc->fwcfg_dma_enabled = true; nc->nmi_monitor_handler = x86_nmi; diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index 258b1343a1..fc460b82f8 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -27,13 +27,8 @@ #include "qom/object.h" struct X86MachineClass { - /*< private >*/ MachineClass parent; - /*< public >*/ - - /* TSC rate migration: */ - bool save_tsc_khz; /* use DMA capable linuxboot option rom */ bool fwcfg_dma_enabled; /* CPU and apic information: */ diff --git a/target/i386/machine.c b/target/i386/machine.c index 6cb561c632..dd2dac1d44 100644 --- a/target/i386/machine.c +++ b/target/i386/machine.c @@ -1060,9 +1060,8 @@ static bool tsc_khz_needed(void *opaque) { X86CPU *cpu = opaque; CPUX86State *env = &cpu->env; - MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); - X86MachineClass *x86mc = X86_MACHINE_CLASS(mc); - return env->tsc_khz && x86mc->save_tsc_khz; + + return env->tsc_khz; } static const VMStateDescription vmstate_tsc_khz = { From 6160ce208419e6e218db644433528aaa5d4f5024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:40 +0200 Subject: [PATCH 1320/2760] hw/nvram/fw_cfg: Remove legacy FW_CFG_ORDER_OVERRIDE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The MachineClass::legacy_fw_cfg_order boolean was only used by the pc-q35-2.5 and pc-i440fx-2.5 machines, which got removed. Remove it along with: - FW_CFG_ORDER_OVERRIDE_* definitions - fw_cfg_set_order_override() - fw_cfg_reset_order_override() - fw_cfg_order[] - rom_set_order_override() - rom_reset_order_override() Simplify CLI and pc_vga_init() / pc_nic_init(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-12-philmd@linaro.org> [thuth: Fix error from check_patch.pl wrt to an empty "for" loop] Signed-off-by: Thomas Huth --- hw/core/loader.c | 14 ----- hw/i386/pc.c | 7 +-- hw/nvram/fw_cfg.c | 110 +++----------------------------------- include/hw/boards.h | 3 +- include/hw/loader.h | 2 - include/hw/nvram/fw_cfg.h | 10 ---- system/vl.c | 5 -- 7 files changed, 10 insertions(+), 141 deletions(-) diff --git a/hw/core/loader.c b/hw/core/loader.c index b792a54bb0..e7056ba4bd 100644 --- a/hw/core/loader.c +++ b/hw/core/loader.c @@ -1333,20 +1333,6 @@ void rom_set_fw(FWCfgState *f) fw_cfg = f; } -void rom_set_order_override(int order) -{ - if (!fw_cfg) - return; - fw_cfg_set_order_override(fw_cfg, order); -} - -void rom_reset_order_override(void) -{ - if (!fw_cfg) - return; - fw_cfg_reset_order_override(fw_cfg); -} - void rom_transaction_begin(void) { Rom *rom; diff --git a/hw/i386/pc.c b/hw/i386/pc.c index cb375aabdc..49632b69d2 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1033,7 +1033,6 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus) { DeviceState *dev = NULL; - rom_set_order_override(FW_CFG_ORDER_OVERRIDE_VGA); if (pci_bus) { PCIDevice *pcidev = pci_vga_init(pci_bus); dev = pcidev ? &pcidev->qdev : NULL; @@ -1041,7 +1040,7 @@ DeviceState *pc_vga_init(ISABus *isa_bus, PCIBus *pci_bus) ISADevice *isadev = isa_vga_init(isa_bus); dev = isadev ? DEVICE(isadev) : NULL; } - rom_reset_order_override(); + return dev; } @@ -1231,8 +1230,6 @@ void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) bool default_is_ne2k = g_str_equal(mc->default_nic, TYPE_ISA_NE2000); NICInfo *nd; - rom_set_order_override(FW_CFG_ORDER_OVERRIDE_NIC); - while ((nd = qemu_find_nic_info(TYPE_ISA_NE2000, default_is_ne2k, NULL))) { pc_init_ne2k_isa(isa_bus, nd, &error_fatal); } @@ -1241,8 +1238,6 @@ void pc_nic_init(PCMachineClass *pcmc, ISABus *isa_bus, PCIBus *pci_bus) if (pci_bus) { pci_init_nic_devices(pci_bus, mc->default_nic); } - - rom_reset_order_override(); } void pc_i8259_create(ISABus *isa_bus, qemu_irq *i8259_irqs) diff --git a/hw/nvram/fw_cfg.c b/hw/nvram/fw_cfg.c index 237b9f7d1f..aa24050493 100644 --- a/hw/nvram/fw_cfg.c +++ b/hw/nvram/fw_cfg.c @@ -817,62 +817,6 @@ void fw_cfg_modify_i64(FWCfgState *s, uint16_t key, uint64_t value) g_free(old); } -void fw_cfg_set_order_override(FWCfgState *s, int order) -{ - assert(s->fw_cfg_order_override == 0); - s->fw_cfg_order_override = order; -} - -void fw_cfg_reset_order_override(FWCfgState *s) -{ - assert(s->fw_cfg_order_override != 0); - s->fw_cfg_order_override = 0; -} - -/* - * This is the legacy order list. For legacy systems, files are in - * the fw_cfg in the order defined below, by the "order" value. Note - * that some entries (VGA ROMs, NIC option ROMS, etc.) go into a - * specific area, but there may be more than one and they occur in the - * order that the user specifies them on the command line. Those are - * handled in a special manner, using the order override above. - * - * For non-legacy, the files are sorted by filename to avoid this kind - * of complexity in the future. - * - * This is only for x86, other arches don't implement versioning so - * they won't set legacy mode. - */ -static struct { - const char *name; - int order; -} fw_cfg_order[] = { - { "etc/boot-menu-wait", 10 }, - { "bootsplash.jpg", 11 }, - { "bootsplash.bmp", 12 }, - { "etc/boot-fail-wait", 15 }, - { "etc/smbios/smbios-tables", 20 }, - { "etc/smbios/smbios-anchor", 30 }, - { "etc/e820", 40 }, - { "etc/reserved-memory-end", 50 }, - { "genroms/kvmvapic.bin", 55 }, - { "genroms/linuxboot.bin", 60 }, - { }, /* VGA ROMs from pc_vga_init come here, 70. */ - { }, /* NIC option ROMs from pc_nic_init come here, 80. */ - { "etc/system-states", 90 }, - { }, /* User ROMs come here, 100. */ - { }, /* Device FW comes here, 110. */ - { "etc/extra-pci-roots", 120 }, - { "etc/acpi/tables", 130 }, - { "etc/table-loader", 140 }, - { "etc/tpm/log", 150 }, - { "etc/acpi/rsdp", 160 }, - { "bootorder", 170 }, - { "etc/msr_feature_control", 180 }, - -#define FW_CFG_ORDER_OVERRIDE_LAST 200 -}; - /* * Any sub-page size update to these table MRs will be lost during migration, * as we use aligned size in ram_load_precopy() -> qemu_ram_resize() path. @@ -890,29 +834,6 @@ static void fw_cfg_acpi_mr_save(FWCfgState *s, const char *filename, size_t len) } } -static int get_fw_cfg_order(FWCfgState *s, const char *name) -{ - int i; - - if (s->fw_cfg_order_override > 0) { - return s->fw_cfg_order_override; - } - - for (i = 0; i < ARRAY_SIZE(fw_cfg_order); i++) { - if (fw_cfg_order[i].name == NULL) { - continue; - } - - if (strcmp(name, fw_cfg_order[i].name) == 0) { - return fw_cfg_order[i].order; - } - } - - /* Stick unknown stuff at the end. */ - warn_report("Unknown firmware file in legacy mode: %s", name); - return FW_CFG_ORDER_OVERRIDE_LAST; -} - void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, FWCfgCallback select_cb, FWCfgWriteCallback write_cb, @@ -921,7 +842,6 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, { int i, index, count; size_t dsize; - MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); int order = 0; if (!s->files) { @@ -933,22 +853,11 @@ void fw_cfg_add_file_callback(FWCfgState *s, const char *filename, count = be32_to_cpu(s->files->count); assert(count < fw_cfg_file_slots(s)); - /* Find the insertion point. */ - if (mc->legacy_fw_cfg_order) { - /* - * Sort by order. For files with the same order, we keep them - * in the sequence in which they were added. - */ - order = get_fw_cfg_order(s, filename); - for (index = count; - index > 0 && order < s->entry_order[index - 1]; - index--); - } else { - /* Sort by file name. */ - for (index = count; - index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0; - index--); - } + /* Find the insertion point, sorting by file name. */ + for (index = count; + index > 0 && strcmp(filename, s->files->f[index - 1].name) < 0; + index--) + ; /* * Move all the entries from the index point and after down one @@ -1058,7 +967,6 @@ bool fw_cfg_add_file_from_generator(FWCfgState *s, static void fw_cfg_machine_reset(void *opaque) { - MachineClass *mc = MACHINE_GET_CLASS(qdev_get_machine()); FWCfgState *s = opaque; void *ptr; size_t len; @@ -1068,11 +976,9 @@ static void fw_cfg_machine_reset(void *opaque) ptr = fw_cfg_modify_file(s, "bootorder", (uint8_t *)buf, len); g_free(ptr); - if (!mc->legacy_fw_cfg_order) { - buf = get_boot_devices_lchs_list(&len); - ptr = fw_cfg_modify_file(s, "bios-geometry", (uint8_t *)buf, len); - g_free(ptr); - } + buf = get_boot_devices_lchs_list(&len); + ptr = fw_cfg_modify_file(s, "bios-geometry", (uint8_t *)buf, len); + g_free(ptr); } static void fw_cfg_machine_ready(struct Notifier *n, void *data) diff --git a/include/hw/boards.h b/include/hw/boards.h index 03e7cbeae8..ab900dacab 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -286,8 +286,7 @@ struct MachineClass { no_parallel:1, no_floppy:1, no_cdrom:1, - pci_allow_0_address:1, - legacy_fw_cfg_order:1; + pci_allow_0_address:1; bool auto_create_sdcard; bool is_default; const char *default_machine_opts; diff --git a/include/hw/loader.h b/include/hw/loader.h index d280dc33e9..c96b5e141c 100644 --- a/include/hw/loader.h +++ b/include/hw/loader.h @@ -270,8 +270,6 @@ int rom_add_elf_program(const char *name, GMappedFile *mapped_file, void *data, AddressSpace *as); int rom_check_and_register_reset(void); void rom_set_fw(FWCfgState *f); -void rom_set_order_override(int order); -void rom_reset_order_override(void); /** * rom_transaction_begin: diff --git a/include/hw/nvram/fw_cfg.h b/include/hw/nvram/fw_cfg.h index 47578ccc7f..d41b9328fd 100644 --- a/include/hw/nvram/fw_cfg.h +++ b/include/hw/nvram/fw_cfg.h @@ -42,14 +42,6 @@ struct FWCfgDataGeneratorClass { typedef struct fw_cfg_file FWCfgFile; -#define FW_CFG_ORDER_OVERRIDE_VGA 70 -#define FW_CFG_ORDER_OVERRIDE_NIC 80 -#define FW_CFG_ORDER_OVERRIDE_USER 100 -#define FW_CFG_ORDER_OVERRIDE_DEVICE 110 - -void fw_cfg_set_order_override(FWCfgState *fw_cfg, int order); -void fw_cfg_reset_order_override(FWCfgState *fw_cfg); - typedef struct FWCfgFiles { uint32_t count; FWCfgFile f[]; @@ -75,8 +67,6 @@ struct FWCfgState { uint32_t cur_offset; Notifier machine_ready; - int fw_cfg_order_override; - bool dma_enabled; dma_addr_t dma_addr; AddressSpace *dma_as; diff --git a/system/vl.c b/system/vl.c index fd402b8ff8..3b7057e6c6 100644 --- a/system/vl.c +++ b/system/vl.c @@ -1192,10 +1192,7 @@ static int parse_fw_cfg(void *opaque, QemuOpts *opts, Error **errp) return -1; } } - /* For legacy, keep user files in a specific global order. */ - fw_cfg_set_order_override(fw_cfg, FW_CFG_ORDER_OVERRIDE_USER); fw_cfg_add_file(fw_cfg, name, buf, size); - fw_cfg_reset_order_override(fw_cfg); return 0; } @@ -2745,7 +2742,6 @@ static void qemu_create_cli_devices(void) } /* init generic devices */ - rom_set_order_override(FW_CFG_ORDER_OVERRIDE_DEVICE); qemu_opts_foreach(qemu_find_opts("device"), device_init_func, NULL, &error_fatal); QTAILQ_FOREACH(opt, &device_opts, next) { @@ -2756,7 +2752,6 @@ static void qemu_create_cli_devices(void) assert(ret_data == NULL); /* error_fatal aborts */ loc_pop(&opt->loc); } - rom_reset_order_override(); } static bool qemu_machine_creation_done(Error **errp) From 667e170d2cf033033dfd6656f871c41871107ee0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:41 +0200 Subject: [PATCH 1321/2760] hw/core/machine: Remove hw_compat_2_5[] array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hw_compat_2_5[] array was only used by the pc-q35-2.5 and pc-i440fx-2.5 machines, which got removed. Remove it. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-13-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/core/machine.c | 9 --------- include/hw/boards.h | 3 --- 2 files changed, 12 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index 15cd2bc3c4..e869821b22 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -285,15 +285,6 @@ GlobalProperty hw_compat_2_6[] = { }; const size_t hw_compat_2_6_len = G_N_ELEMENTS(hw_compat_2_6); -GlobalProperty hw_compat_2_5[] = { - { "isa-fdc", "fallback", "144" }, - { "pvscsi", "x-old-pci-configuration", "on" }, - { "pvscsi", "x-disable-pcie", "on" }, - { "vmxnet3", "x-old-msi-offsets", "on" }, - { "vmxnet3", "x-disable-pcie", "on" }, -}; -const size_t hw_compat_2_5_len = G_N_ELEMENTS(hw_compat_2_5); - MachineState *current_machine; static char *machine_get_kernel(Object *obj, Error **errp) diff --git a/include/hw/boards.h b/include/hw/boards.h index ab900dacab..f424b2b505 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -862,7 +862,4 @@ extern const size_t hw_compat_2_7_len; extern GlobalProperty hw_compat_2_6[]; extern const size_t hw_compat_2_6_len; -extern GlobalProperty hw_compat_2_5[]; -extern const size_t hw_compat_2_5_len; - #endif From 16c04166ae71c78ec36a9e2914c46c2289a58503 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:43 +0200 Subject: [PATCH 1322/2760] hw/scsi/vmw_pvscsi: Remove PVSCSI_COMPAT_OLD_PCI_CONFIGURATION definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PVSCSI_COMPAT_OLD_PCI_CONFIGURATION was only used by the hw_compat_2_5[] array, via the 'x-old-pci-configuration=on' property. We removed all machines using that array, lets remove all the code around PVSCSI_COMPAT_OLD_PCI_CONFIGURATION. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-15-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/scsi/vmw_pvscsi.c | 26 +++++++------------------- 1 file changed, 7 insertions(+), 19 deletions(-) diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index d5825b6786..34de59a7cf 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -69,17 +69,11 @@ OBJECT_DECLARE_TYPE(PVSCSIState, PVSCSIClass, PVSCSI) /* Compatibility flags for migration */ -#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT 0 -#define PVSCSI_COMPAT_OLD_PCI_CONFIGURATION \ - (1 << PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT) #define PVSCSI_COMPAT_DISABLE_PCIE_BIT 1 #define PVSCSI_COMPAT_DISABLE_PCIE \ (1 << PVSCSI_COMPAT_DISABLE_PCIE_BIT) -#define PVSCSI_USE_OLD_PCI_CONFIGURATION(s) \ - ((s)->compat_flags & PVSCSI_COMPAT_OLD_PCI_CONFIGURATION) -#define PVSCSI_MSI_OFFSET(s) \ - (PVSCSI_USE_OLD_PCI_CONFIGURATION(s) ? 0x50 : 0x7c) +#define PVSCSI_MSI_OFFSET (0x7c) #define PVSCSI_EXP_EP_OFFSET (0x40) typedef struct PVSCSIRingInfo { @@ -1110,7 +1104,7 @@ pvscsi_init_msi(PVSCSIState *s) int res; PCIDevice *d = PCI_DEVICE(s); - res = msi_init(d, PVSCSI_MSI_OFFSET(s), PVSCSI_MSIX_NUM_VECTORS, + res = msi_init(d, PVSCSI_MSI_OFFSET, PVSCSI_MSIX_NUM_VECTORS, PVSCSI_USE_64BIT, PVSCSI_PER_VECTOR_MASK, NULL); if (res < 0) { trace_pvscsi_init_msi_fail(res); @@ -1158,15 +1152,11 @@ pvscsi_realizefn(PCIDevice *pci_dev, Error **errp) trace_pvscsi_state("init"); /* PCI subsystem ID, subsystem vendor ID, revision */ - if (PVSCSI_USE_OLD_PCI_CONFIGURATION(s)) { - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, 0x1000); - } else { - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, - PCI_VENDOR_ID_VMWARE); - pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, - PCI_DEVICE_ID_VMWARE_PVSCSI); - pci_config_set_revision(pci_dev->config, 0x2); - } + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_VENDOR_ID, + PCI_VENDOR_ID_VMWARE); + pci_set_word(pci_dev->config + PCI_SUBSYSTEM_ID, + PCI_DEVICE_ID_VMWARE_PVSCSI); + pci_config_set_revision(pci_dev->config, 0x2); /* PCI latency timer = 255 */ pci_dev->config[PCI_LATENCY_TIMER] = 0xff; @@ -1298,8 +1288,6 @@ static const VMStateDescription vmstate_pvscsi = { static const Property pvscsi_properties[] = { DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1), - DEFINE_PROP_BIT("x-old-pci-configuration", PVSCSIState, compat_flags, - PVSCSI_COMPAT_OLD_PCI_CONFIGURATION_BIT, false), DEFINE_PROP_BIT("x-disable-pcie", PVSCSIState, compat_flags, PVSCSI_COMPAT_DISABLE_PCIE_BIT, false), }; From 404b27b739aa6f2c53fbed58101719564f614aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:44 +0200 Subject: [PATCH 1323/2760] hw/scsi/vmw_pvscsi: Remove PVSCSI_COMPAT_DISABLE_PCIE_BIT definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PVSCSI_COMPAT_DISABLE_PCIE_BIT was only used by the hw_compat_2_5[] array, via the 'x-disable-pcie=on' property. We removed all machines using that array, lets remove all the code around PVSCSI_COMPAT_DISABLE_PCIE_BIT, including the now unused PVSCSIState::compat_flags field. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-16-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/scsi/vmw_pvscsi.c | 30 +----------------------------- 1 file changed, 1 insertion(+), 29 deletions(-) diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index 34de59a7cf..e163023d14 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -68,11 +68,6 @@ struct PVSCSIClass { OBJECT_DECLARE_TYPE(PVSCSIState, PVSCSIClass, PVSCSI) -/* Compatibility flags for migration */ -#define PVSCSI_COMPAT_DISABLE_PCIE_BIT 1 -#define PVSCSI_COMPAT_DISABLE_PCIE \ - (1 << PVSCSI_COMPAT_DISABLE_PCIE_BIT) - #define PVSCSI_MSI_OFFSET (0x7c) #define PVSCSI_EXP_EP_OFFSET (0x40) @@ -123,8 +118,6 @@ struct PVSCSIState { uint8_t msi_used; /* For migration compatibility */ PVSCSIRingInfo rings; /* Data transfer rings manager */ uint32_t resetting; /* Reset in progress */ - - uint32_t compat_flags; }; typedef struct PVSCSIRequest { @@ -1224,21 +1217,8 @@ pvscsi_post_load(void *opaque, int version_id) return 0; } -static bool pvscsi_vmstate_need_pcie_device(void *opaque) -{ - PVSCSIState *s = PVSCSI(opaque); - - return !(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE); -} - -static bool pvscsi_vmstate_test_pci_device(void *opaque, int version_id) -{ - return !pvscsi_vmstate_need_pcie_device(opaque); -} - static const VMStateDescription vmstate_pvscsi_pcie_device = { .name = "pvscsi/pcie", - .needed = pvscsi_vmstate_need_pcie_device, .fields = (const VMStateField[]) { VMSTATE_PCI_DEVICE(parent_obj, PVSCSIState), VMSTATE_END_OF_LIST() @@ -1252,9 +1232,6 @@ static const VMStateDescription vmstate_pvscsi = { .pre_save = pvscsi_pre_save, .post_load = pvscsi_post_load, .fields = (const VMStateField[]) { - VMSTATE_STRUCT_TEST(parent_obj, PVSCSIState, - pvscsi_vmstate_test_pci_device, 0, - vmstate_pci_device, PCIDevice), VMSTATE_UINT8(msi_used, PVSCSIState), VMSTATE_UINT32(resetting, PVSCSIState), VMSTATE_UINT64(reg_interrupt_status, PVSCSIState), @@ -1288,19 +1265,14 @@ static const VMStateDescription vmstate_pvscsi = { static const Property pvscsi_properties[] = { DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1), - DEFINE_PROP_BIT("x-disable-pcie", PVSCSIState, compat_flags, - PVSCSI_COMPAT_DISABLE_PCIE_BIT, false), }; static void pvscsi_realize(DeviceState *qdev, Error **errp) { PVSCSIClass *pvs_c = PVSCSI_GET_CLASS(qdev); PCIDevice *pci_dev = PCI_DEVICE(qdev); - PVSCSIState *s = PVSCSI(qdev); - if (!(s->compat_flags & PVSCSI_COMPAT_DISABLE_PCIE)) { - pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; - } + pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; pvs_c->parent_dc_realize(qdev, errp); } From 3763d16370f1505faada6909bd92ebe3a242b1bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:45 +0200 Subject: [PATCH 1324/2760] hw/scsi/vmw_pvscsi: Convert DeviceRealize -> InstanceInit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify replacing pvscsi_realize() by pvscsi_instance_init(), removing the need for device_class_set_parent_realize(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-17-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/scsi/vmw_pvscsi.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/hw/scsi/vmw_pvscsi.c b/hw/scsi/vmw_pvscsi.c index e163023d14..7c98b1b8ea 100644 --- a/hw/scsi/vmw_pvscsi.c +++ b/hw/scsi/vmw_pvscsi.c @@ -1267,21 +1267,15 @@ static const Property pvscsi_properties[] = { DEFINE_PROP_UINT8("use_msg", PVSCSIState, use_msg, 1), }; -static void pvscsi_realize(DeviceState *qdev, Error **errp) +static void pvscsi_instance_init(Object *obj) { - PVSCSIClass *pvs_c = PVSCSI_GET_CLASS(qdev); - PCIDevice *pci_dev = PCI_DEVICE(qdev); - - pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; - - pvs_c->parent_dc_realize(qdev, errp); + PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS; } static void pvscsi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); - PVSCSIClass *pvs_k = PVSCSI_CLASS(klass); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(klass); k->realize = pvscsi_realizefn; @@ -1290,8 +1284,6 @@ static void pvscsi_class_init(ObjectClass *klass, const void *data) k->device_id = PCI_DEVICE_ID_VMWARE_PVSCSI; k->class_id = PCI_CLASS_STORAGE_SCSI; k->subsystem_id = 0x1000; - device_class_set_parent_realize(dc, pvscsi_realize, - &pvs_k->parent_dc_realize); device_class_set_legacy_reset(dc, pvscsi_reset); dc->vmsd = &vmstate_pvscsi; device_class_set_props(dc, pvscsi_properties); @@ -1306,6 +1298,7 @@ static const TypeInfo pvscsi_info = { .class_size = sizeof(PVSCSIClass), .instance_size = sizeof(PVSCSIState), .class_init = pvscsi_class_init, + .instance_init = pvscsi_instance_init, .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { INTERFACE_PCIE_DEVICE }, From 2531dfde0ac447b0d70a83c8a02f72e584b9c534 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:46 +0200 Subject: [PATCH 1325/2760] hw/net/vmxnet3: Remove VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS was only used by the hw_compat_2_5[] array, via the 'x-old-msi-offsets=on' property. We removed all machines using that array, lets remove all the code around VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-18-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/net/vmxnet3.c | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 83d942af17..3cf5d71f47 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -42,18 +42,13 @@ #define VMXNET3_MSIX_BAR_SIZE 0x2000 /* Compatibility flags for migration */ -#define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT 0 -#define VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS \ - (1 << VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT) #define VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT 1 #define VMXNET3_COMPAT_FLAG_DISABLE_PCIE \ (1 << VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT) #define VMXNET3_EXP_EP_OFFSET (0x48) -#define VMXNET3_MSI_OFFSET(s) \ - ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0x50 : 0x84) -#define VMXNET3_MSIX_OFFSET(s) \ - ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0 : 0x9c) +#define VMXNET3_MSI_OFFSET (0x84) +#define VMXNET3_MSIX_OFFSET (0x9c) #define VMXNET3_DSN_OFFSET (0x100) #define VMXNET3_BAR0_IDX (0) @@ -61,8 +56,7 @@ #define VMXNET3_MSIX_BAR_IDX (2) #define VMXNET3_OFF_MSIX_TABLE (0x000) -#define VMXNET3_OFF_MSIX_PBA(s) \ - ((s)->compat_flags & VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS ? 0x800 : 0x1000) +#define VMXNET3_OFF_MSIX_PBA (0x1000) /* Link speed in Mbps should be shifted by 16 */ #define VMXNET3_LINK_SPEED (1000 << 16) @@ -2122,8 +2116,8 @@ vmxnet3_init_msix(VMXNET3State *s) &s->msix_bar, VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_TABLE, &s->msix_bar, - VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA(s), - VMXNET3_MSIX_OFFSET(s), NULL); + VMXNET3_MSIX_BAR_IDX, VMXNET3_OFF_MSIX_PBA, + VMXNET3_MSIX_OFFSET, NULL); if (0 > res) { VMW_WRPRN("Failed to initialize MSI-X, error %d", res); @@ -2221,7 +2215,7 @@ static void vmxnet3_pci_realize(PCIDevice *pci_dev, Error **errp) /* Interrupt pin A */ pci_dev->config[PCI_INTERRUPT_PIN] = 0x01; - ret = msi_init(pci_dev, VMXNET3_MSI_OFFSET(s), VMXNET3_MAX_NMSIX_INTRS, + ret = msi_init(pci_dev, VMXNET3_MSI_OFFSET, VMXNET3_MAX_NMSIX_INTRS, VMXNET3_USE_64BIT, VMXNET3_PER_VECTOR_MASK, NULL); /* Any error other than -ENOTSUP(board's MSI support is broken) * is a programming error. Fall back to INTx silently on -ENOTSUP */ @@ -2472,8 +2466,6 @@ static const VMStateDescription vmstate_vmxnet3 = { static const Property vmxnet3_properties[] = { DEFINE_NIC_PROPERTIES(VMXNET3State, conf), - DEFINE_PROP_BIT("x-old-msi-offsets", VMXNET3State, compat_flags, - VMXNET3_COMPAT_FLAG_OLD_MSI_OFFSETS_BIT, false), DEFINE_PROP_BIT("x-disable-pcie", VMXNET3State, compat_flags, VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT, false), }; From 2db72323f2370ca69ba331f17c9a6bbd1f3bb118 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:47 +0200 Subject: [PATCH 1326/2760] hw/net/vmxnet3: Remove VMXNET3_COMPAT_FLAG_DISABLE_PCIE definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VMXNET3_COMPAT_FLAG_DISABLE_PCIE was only used by the hw_compat_2_5[] array, via the 'x-disable-pcie=on' property. We removed all machines using that array, lets remove all the code around VMXNET3_COMPAT_FLAG_DISABLE_PCIE. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-19-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/net/vmxnet3.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 3cf5d71f47..d080fe9b38 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -41,11 +41,6 @@ #define PCI_DEVICE_ID_VMWARE_VMXNET3_REVISION 0x1 #define VMXNET3_MSIX_BAR_SIZE 0x2000 -/* Compatibility flags for migration */ -#define VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT 1 -#define VMXNET3_COMPAT_FLAG_DISABLE_PCIE \ - (1 << VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT) - #define VMXNET3_EXP_EP_OFFSET (0x48) #define VMXNET3_MSI_OFFSET (0x84) #define VMXNET3_MSIX_OFFSET (0x9c) @@ -2466,8 +2461,6 @@ static const VMStateDescription vmstate_vmxnet3 = { static const Property vmxnet3_properties[] = { DEFINE_NIC_PROPERTIES(VMXNET3State, conf), - DEFINE_PROP_BIT("x-disable-pcie", VMXNET3State, compat_flags, - VMXNET3_COMPAT_FLAG_DISABLE_PCIE_BIT, false), }; static void vmxnet3_realize(DeviceState *qdev, Error **errp) @@ -2476,9 +2469,7 @@ static void vmxnet3_realize(DeviceState *qdev, Error **errp) PCIDevice *pci_dev = PCI_DEVICE(qdev); VMXNET3State *s = VMXNET3(qdev); - if (!(s->compat_flags & VMXNET3_COMPAT_FLAG_DISABLE_PCIE)) { - pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; - } + pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; vc->parent_dc_realize(qdev, errp); } From c4eb3f10a3573392c297f2124a81af9041300ddd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 12 May 2025 10:39:48 +0200 Subject: [PATCH 1327/2760] hw/net/vmxnet3: Merge DeviceRealize in InstanceInit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Simplify merging vmxnet3_realize() within vmxnet3_instance_init(), removing the need for device_class_set_parent_realize(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-ID: <20250512083948.39294-20-philmd@linaro.org> Signed-off-by: Thomas Huth --- hw/net/vmxnet3.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index d080fe9b38..7c0ca56b7c 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -2238,6 +2238,7 @@ static void vmxnet3_instance_init(Object *obj) device_add_bootindex_property(obj, &s->conf.bootindex, "bootindex", "/ethernet-phy@0", DEVICE(obj)); + PCI_DEVICE(obj)->cap_present |= QEMU_PCI_CAP_EXPRESS; } static void vmxnet3_pci_uninit(PCIDevice *pci_dev) @@ -2463,22 +2464,10 @@ static const Property vmxnet3_properties[] = { DEFINE_NIC_PROPERTIES(VMXNET3State, conf), }; -static void vmxnet3_realize(DeviceState *qdev, Error **errp) -{ - VMXNET3Class *vc = VMXNET3_DEVICE_GET_CLASS(qdev); - PCIDevice *pci_dev = PCI_DEVICE(qdev); - VMXNET3State *s = VMXNET3(qdev); - - pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; - - vc->parent_dc_realize(qdev, errp); -} - static void vmxnet3_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); PCIDeviceClass *c = PCI_DEVICE_CLASS(class); - VMXNET3Class *vc = VMXNET3_DEVICE_CLASS(class); c->realize = vmxnet3_pci_realize; c->exit = vmxnet3_pci_uninit; @@ -2489,8 +2478,6 @@ static void vmxnet3_class_init(ObjectClass *class, const void *data) c->class_id = PCI_CLASS_NETWORK_ETHERNET; c->subsystem_vendor_id = PCI_VENDOR_ID_VMWARE; c->subsystem_id = PCI_DEVICE_ID_VMWARE_VMXNET3; - device_class_set_parent_realize(dc, vmxnet3_realize, - &vc->parent_dc_realize); dc->desc = "VMWare Paravirtualized Ethernet v3"; device_class_set_legacy_reset(dc, vmxnet3_qdev_reset); dc->vmsd = &vmstate_vmxnet3; From 5c54a367265ec19ed94a535cd15d178c16b8cae0 Mon Sep 17 00:00:00 2001 From: Matheus Tavares Bernardino Date: Mon, 26 May 2025 10:20:55 -0700 Subject: [PATCH 1328/2760] tests/unit/test-util-sockets: fix mem-leak on error object The test fails with --enable-asan as the error struct is never freed. In the case where the test expects a success but it fails, let's also report the error for debugging (it will be freed internally). Fixes 316e8ee8d6 ("util/qemu-sockets: Refactor inet_parse() to use QemuOpts") Signed-off-by: Matheus Tavares Bernardino Reviewed-by: Juraj Marcin Message-ID: <518d94c7db20060b2a086cf55ee9bffab992a907.1748280011.git.matheus.bernardino@oss.qualcomm.com> Signed-off-by: Thomas Huth --- tests/unit/test-util-sockets.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/unit/test-util-sockets.c b/tests/unit/test-util-sockets.c index 8492f4d68f..ee66d727c3 100644 --- a/tests/unit/test-util-sockets.c +++ b/tests/unit/test-util-sockets.c @@ -341,8 +341,12 @@ static void inet_parse_test_helper(const char *str, int rc = inet_parse(&addr, str, &error); if (success) { + if (error) { + error_report_err(error); + } g_assert_cmpint(rc, ==, 0); } else { + error_free(error); g_assert_cmpint(rc, <, 0); } if (exp_addr != NULL) { From ac8fc4ccacd8a77d8d56dc3990bfb221c1f48fcd Mon Sep 17 00:00:00 2001 From: Yuri Benditovich Date: Thu, 15 May 2025 09:32:37 +0300 Subject: [PATCH 1329/2760] virtio: check for validity of indirect descriptors virtio processes indirect descriptors even if the respected feature VIRTIO_RING_F_INDIRECT_DESC was not negotiated. If qemu is used with reduced set of features to emulate the hardware device that does not support indirect descriptors, the will probably trigger problematic flows on the hardware setup but do not reveal the mistake on qemu. Add LOG_GUEST_ERROR for such case. This will issue logs with '-d guest_errors' in the command line Signed-off-by: Yuri Benditovich Message-Id: <20250515063237.808293-1-yuri.benditovich@daynix.com> Signed-off-by: Yuri Benditovich --- hw/virtio/virtio.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 2e98cecf64..5534251e01 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -205,6 +205,15 @@ static const char *virtio_id_to_name(uint16_t device_id) return name; } +static void virtio_check_indirect_feature(VirtIODevice *vdev) +{ + if (!virtio_vdev_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC)) { + qemu_log_mask(LOG_GUEST_ERROR, + "Device %s: indirect_desc was not negotiated!\n", + vdev->name); + } +} + /* Called within call_rcu(). */ static void virtio_free_region_cache(VRingMemoryRegionCaches *caches) { @@ -1733,6 +1742,7 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) virtio_error(vdev, "Invalid size for indirect buffer table"); goto done; } + virtio_check_indirect_feature(vdev); /* loop over the indirect descriptor table */ len = address_space_cache_init(&indirect_desc_cache, vdev->dma_as, @@ -1870,6 +1880,7 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) virtio_error(vdev, "Invalid size for indirect buffer table"); goto done; } + virtio_check_indirect_feature(vdev); /* loop over the indirect descriptor table */ len = address_space_cache_init(&indirect_desc_cache, vdev->dma_as, From 31753d5a336fbb4e9246397f4b90b6f611f27f22 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 16 May 2025 15:35:34 +0530 Subject: [PATCH 1330/2760] hw/i386/amd_iommu: Fix device setup failure when PT is on. Commit c1f46999ef506 ("amd_iommu: Add support for pass though mode") introduces the support for "pt" flag by enabling nodma memory when "pt=off". This allowed VFIO devices to successfully register notifiers by using nodma region. But, This also broke things when guest is booted with the iommu=nopt because, devices bypass the IOMMU and use untranslated addresses (IOVA) to perform DMA reads/writes to the nodma memory region, ultimately resulting in a failure to setup the devices in the guest. Fix the above issue by always enabling the amdvi_dev_as->iommu memory region. But this will once again cause VFIO devices to fail while registering the notifiers with AMD IOMMU memory region. Fixes: c1f46999ef506 ("amd_iommu: Add support for pass though mode") Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Message-Id: <20250516100535.4980-2-sarunkod@amd.com> Fixes: c1f46999ef506 ("amd_iommu: Add support for pass though mode") Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde --- hw/i386/amd_iommu.c | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 0775c8f3bb..17379db52a 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1426,7 +1426,6 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) AMDVIState *s = opaque; AMDVIAddressSpace **iommu_as, *amdvi_dev_as; int bus_num = pci_bus_num(bus); - X86IOMMUState *x86_iommu = X86_IOMMU_DEVICE(s); iommu_as = s->address_spaces[bus_num]; @@ -1486,15 +1485,8 @@ static AddressSpace *amdvi_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) AMDVI_INT_ADDR_FIRST, &amdvi_dev_as->iommu_ir, 1); - if (!x86_iommu->pt_supported) { - memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, false); - memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu), - true); - } else { - memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu), - false); - memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, true); - } + memory_region_set_enabled(&amdvi_dev_as->iommu_nodma, false); + memory_region_set_enabled(MEMORY_REGION(&amdvi_dev_as->iommu), true); } return &iommu_as[devfn]->as; } From 0f178860df3489a9d3c19a5f7f024e6aa6c26515 Mon Sep 17 00:00:00 2001 From: Vasant Hegde Date: Fri, 16 May 2025 15:35:35 +0530 Subject: [PATCH 1331/2760] hw/i386/amd_iommu: Fix xtsup when vcpus < 255 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If vCPUs > 255 then x86 common code (x86_cpus_init()) call kvm_enable_x2apic(). But if vCPUs <= 255 then the common code won't calls kvm_enable_x2apic(). This is because commit 8c6619f3e692 ("hw/i386/amd_iommu: Simplify non-KVM checks on XTSup feature") removed the call to kvm_enable_x2apic when xtsup is "on", which break things when guest is booted with x2apic mode and there are <= 255 vCPUs. Fix this by adding back kvm_enable_x2apic() call when xtsup=on. Fixes: 8c6619f3e692 ("hw/i386/amd_iommu: Simplify non-KVM checks on XTSup feature") Reported-by: Alejandro Jimenez Tested-by: Tested-by: Alejandro Jimenez Cc: Philippe Mathieu-Daudé Cc: Joao Martins Signed-off-by: Vasant Hegde Signed-off-by: Sairaj Kodilkar Message-Id: <20250516100535.4980-3-sarunkod@amd.com> Fixes: 8c6619f3e692 ("hw/i386/amd_iommu: Simplify non-KVM checks on XTSup feature") Reported-by: Alejandro Jimenez Tested-by: Tested-by: Alejandro Jimenez Cc: Philippe Mathieu-Daudé Cc: Joao Martins Signed-off-by: Vasant Hegde Signed-off-by: Sairaj Kodilkar --- hw/i386/amd_iommu.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 17379db52a..963aa2450c 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1715,6 +1715,14 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) exit(EXIT_FAILURE); } + if (s->xtsup) { + if (kvm_irqchip_is_split() && !kvm_enable_x2apic()) { + error_report("AMD IOMMU xtsup=on requires x2APIC support on " + "the KVM side"); + exit(EXIT_FAILURE); + } + } + pci_setup_iommu(bus, &amdvi_iommu_ops, s); amdvi_init(s); } From a8d178e1492a1a803898501a84829ac517ae2fb0 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:51 +0000 Subject: [PATCH 1332/2760] pcie: Add helper to declare PASID capability for a pcie device Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-2-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie.c | 25 +++++++++++++++++++++++++ include/hw/pci/pcie.h | 6 +++++- include/hw/pci/pcie_regs.h | 5 +++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 1b12db6fa2..4f935ff420 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -1214,3 +1214,28 @@ void pcie_acs_reset(PCIDevice *dev) pci_set_word(dev->config + dev->exp.acs_cap + PCI_ACS_CTRL, 0); } } + +/* PASID */ +void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, + bool exec_perm, bool priv_mod) +{ + static const uint16_t control_reg_rw_mask = 0x07; + uint16_t capability_reg; + + assert(pasid_width <= PCI_EXT_CAP_PASID_MAX_WIDTH); + + pcie_add_capability(dev, PCI_EXT_CAP_ID_PASID, PCI_PASID_VER, offset, + PCI_EXT_CAP_PASID_SIZEOF); + + capability_reg = ((uint16_t)pasid_width) << PCI_PASID_CAP_WIDTH_SHIFT; + capability_reg |= exec_perm ? PCI_PASID_CAP_EXEC : 0; + capability_reg |= priv_mod ? PCI_PASID_CAP_PRIV : 0; + pci_set_word(dev->config + offset + PCI_PASID_CAP, capability_reg); + + /* Everything is disabled by default */ + pci_set_word(dev->config + offset + PCI_PASID_CTRL, 0); + + pci_set_word(dev->wmask + offset + PCI_PASID_CTRL, control_reg_rw_mask); + + dev->exp.pasid_cap = offset; +} diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 70a5de09de..fe82e0a915 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -70,8 +70,9 @@ struct PCIExpressDevice { uint16_t aer_cap; PCIEAERLog aer_log; - /* Offset of ATS capability in config space */ + /* Offset of ATS and PASID capabilities in config space */ uint16_t ats_cap; + uint16_t pasid_cap; /* ACS */ uint16_t acs_cap; @@ -150,4 +151,7 @@ void pcie_cap_slot_unplug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp); + +void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, + bool exec_perm, bool priv_mod); #endif /* QEMU_PCIE_H */ diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h index 9d3b6868dc..4d9cf4a29c 100644 --- a/include/hw/pci/pcie_regs.h +++ b/include/hw/pci/pcie_regs.h @@ -86,6 +86,11 @@ typedef enum PCIExpLinkWidth { #define PCI_ARI_VER 1 #define PCI_ARI_SIZEOF 8 +/* PASID */ +#define PCI_PASID_VER 1 +#define PCI_EXT_CAP_PASID_MAX_WIDTH 20 +#define PCI_PASID_CAP_WIDTH_SHIFT 8 + /* AER */ #define PCI_ERR_VER 2 #define PCI_ERR_SIZEOF 0x48 From 1e82e8a828cf18a8abfeca7295322db91879de04 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:51 +0000 Subject: [PATCH 1333/2760] pcie: Helper functions to check if PASID is enabled pasid_enabled checks whether the capability is present or not. If so, we read the configuration space to get the status of the feature (enabled or not). Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-3-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie.c | 9 +++++++++ include/hw/pci/pcie.h | 2 ++ 2 files changed, 11 insertions(+) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 4f935ff420..db9756d861 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -1239,3 +1239,12 @@ void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, dev->exp.pasid_cap = offset; } + +bool pcie_pasid_enabled(const PCIDevice *dev) +{ + if (!pci_is_express(dev) || !dev->exp.pasid_cap) { + return false; + } + return (pci_get_word(dev->config + dev->exp.pasid_cap + PCI_PASID_CTRL) & + PCI_PASID_CTRL_ENABLE) != 0; +} diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index fe82e0a915..dff98ff2c6 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -154,4 +154,6 @@ void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, bool exec_perm, bool priv_mod); + +bool pcie_pasid_enabled(const PCIDevice *dev); #endif /* QEMU_PCIE_H */ From 6a3ae6a2440dead9dd8e3f84152dc53a5214c48d Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:52 +0000 Subject: [PATCH 1334/2760] pcie: Helper function to check if ATS is enabled ats_enabled checks whether the capability is present or not. If so, we read the configuration space to get the status of the feature (enabled or not). Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-4-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie.c | 9 +++++++++ include/hw/pci/pcie.h | 1 + 2 files changed, 10 insertions(+) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index db9756d861..36de709801 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -1248,3 +1248,12 @@ bool pcie_pasid_enabled(const PCIDevice *dev) return (pci_get_word(dev->config + dev->exp.pasid_cap + PCI_PASID_CTRL) & PCI_PASID_CTRL_ENABLE) != 0; } + +bool pcie_ats_enabled(const PCIDevice *dev) +{ + if (!pci_is_express(dev) || !dev->exp.ats_cap) { + return false; + } + return (pci_get_word(dev->config + dev->exp.ats_cap + PCI_ATS_CTRL) & + PCI_ATS_CTRL_ENABLE) != 0; +} diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index dff98ff2c6..497d0bc2d2 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -156,4 +156,5 @@ void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, bool exec_perm, bool priv_mod); bool pcie_pasid_enabled(const PCIDevice *dev); +bool pcie_ats_enabled(const PCIDevice *dev); #endif /* QEMU_PCIE_H */ From dcad6cb2abf4ffc4f911041d0547c4b54c2f92e2 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:54 +0000 Subject: [PATCH 1335/2760] pcie: Add a helper to declare the PRI capability for a pcie device Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-5-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie.c | 26 ++++++++++++++++++++++++++ include/hw/pci/pcie.h | 5 ++++- include/hw/pci/pcie_regs.h | 3 +++ 3 files changed, 33 insertions(+), 1 deletion(-) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 36de709801..542172b3fa 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -1240,6 +1240,32 @@ void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, dev->exp.pasid_cap = offset; } +/* PRI */ +void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap, + bool prg_response_pasid_req) +{ + static const uint16_t control_reg_rw_mask = 0x3; + static const uint16_t status_reg_rw1_mask = 0x3; + static const uint32_t pr_alloc_reg_rw_mask = 0xffffffff; + uint16_t status_reg; + + status_reg = prg_response_pasid_req ? PCI_PRI_STATUS_PASID : 0; + status_reg |= PCI_PRI_STATUS_STOPPED; /* Stopped by default */ + + pcie_add_capability(dev, PCI_EXT_CAP_ID_PRI, PCI_PRI_VER, offset, + PCI_EXT_CAP_PRI_SIZEOF); + /* Disabled by default */ + + pci_set_word(dev->config + offset + PCI_PRI_STATUS, status_reg); + pci_set_long(dev->config + offset + PCI_PRI_MAX_REQ, outstanding_pr_cap); + + pci_set_word(dev->wmask + offset + PCI_PRI_CTRL, control_reg_rw_mask); + pci_set_word(dev->w1cmask + offset + PCI_PRI_STATUS, status_reg_rw1_mask); + pci_set_long(dev->wmask + offset + PCI_PRI_ALLOC_REQ, pr_alloc_reg_rw_mask); + + dev->exp.pri_cap = offset; +} + bool pcie_pasid_enabled(const PCIDevice *dev) { if (!pci_is_express(dev) || !dev->exp.pasid_cap) { diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 497d0bc2d2..17f06cd5d6 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -70,9 +70,10 @@ struct PCIExpressDevice { uint16_t aer_cap; PCIEAERLog aer_log; - /* Offset of ATS and PASID capabilities in config space */ + /* Offset of ATS, PRI and PASID capabilities in config space */ uint16_t ats_cap; uint16_t pasid_cap; + uint16_t pri_cap; /* ACS */ uint16_t acs_cap; @@ -154,6 +155,8 @@ void pcie_cap_slot_unplug_request_cb(HotplugHandler *hotplug_dev, void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, bool exec_perm, bool priv_mod); +void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap, + bool prg_response_pasid_req); bool pcie_pasid_enabled(const PCIDevice *dev); bool pcie_ats_enabled(const PCIDevice *dev); diff --git a/include/hw/pci/pcie_regs.h b/include/hw/pci/pcie_regs.h index 4d9cf4a29c..33a22229fe 100644 --- a/include/hw/pci/pcie_regs.h +++ b/include/hw/pci/pcie_regs.h @@ -91,6 +91,9 @@ typedef enum PCIExpLinkWidth { #define PCI_EXT_CAP_PASID_MAX_WIDTH 20 #define PCI_PASID_CAP_WIDTH_SHIFT 8 +/* PRI */ +#define PCI_PRI_VER 1 + /* AER */ #define PCI_ERR_VER 2 #define PCI_ERR_SIZEOF 0x48 From 5be8cf79188a2a1c73d16f3a8b458d909ac976f4 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:57 +0000 Subject: [PATCH 1336/2760] pcie: Helper functions to check to check if PRI is enabled pri_enabled can be used to check whether the capability is present and enabled on a PCIe device Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-6-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie.c | 9 +++++++++ include/hw/pci/pcie.h | 1 + 2 files changed, 10 insertions(+) diff --git a/hw/pci/pcie.c b/hw/pci/pcie.c index 542172b3fa..eaeb68894e 100644 --- a/hw/pci/pcie.c +++ b/hw/pci/pcie.c @@ -1266,6 +1266,15 @@ void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap, dev->exp.pri_cap = offset; } +bool pcie_pri_enabled(const PCIDevice *dev) +{ + if (!pci_is_express(dev) || !dev->exp.pri_cap) { + return false; + } + return (pci_get_word(dev->config + dev->exp.pri_cap + PCI_PRI_CTRL) & + PCI_PRI_CTRL_ENABLE) != 0; +} + bool pcie_pasid_enabled(const PCIDevice *dev) { if (!pci_is_express(dev) || !dev->exp.pasid_cap) { diff --git a/include/hw/pci/pcie.h b/include/hw/pci/pcie.h index 17f06cd5d6..ff6ce08e13 100644 --- a/include/hw/pci/pcie.h +++ b/include/hw/pci/pcie.h @@ -158,6 +158,7 @@ void pcie_pasid_init(PCIDevice *dev, uint16_t offset, uint8_t pasid_width, void pcie_pri_init(PCIDevice *dev, uint16_t offset, uint32_t outstanding_pr_cap, bool prg_response_pasid_req); +bool pcie_pri_enabled(const PCIDevice *dev); bool pcie_pasid_enabled(const PCIDevice *dev); bool pcie_ats_enabled(const PCIDevice *dev); #endif /* QEMU_PCIE_H */ From 8ff9e1def0ef3388333b6cc639c9f958f97ebe05 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:58 +0000 Subject: [PATCH 1337/2760] pci: Cache the bus mastering status in the device The cached is_master value is necessary to know if a device is allowed to issue ATS/PRI requests or not as these operations do not go through the master_enable memory region. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-7-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 23 +++++++++++++---------- include/hw/pci/pci_device.h | 1 + 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index f5ab510697..1114ba8529 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -128,6 +128,12 @@ static GSequence *pci_acpi_index_list(void) return used_acpi_index_list; } +static void pci_set_master(PCIDevice *d, bool enable) +{ + memory_region_set_enabled(&d->bus_master_enable_region, enable); + d->is_master = enable; /* cache the status */ +} + static void pci_init_bus_master(PCIDevice *pci_dev) { AddressSpace *dma_as = pci_device_iommu_address_space(pci_dev); @@ -135,7 +141,7 @@ static void pci_init_bus_master(PCIDevice *pci_dev) memory_region_init_alias(&pci_dev->bus_master_enable_region, OBJECT(pci_dev), "bus master", dma_as->root, 0, memory_region_size(dma_as->root)); - memory_region_set_enabled(&pci_dev->bus_master_enable_region, false); + pci_set_master(pci_dev, false); memory_region_add_subregion(&pci_dev->bus_master_container_region, 0, &pci_dev->bus_master_enable_region); } @@ -804,9 +810,8 @@ static int get_pci_config_device(QEMUFile *f, void *pv, size_t size, pci_bridge_update_mappings(PCI_BRIDGE(s)); } - memory_region_set_enabled(&s->bus_master_enable_region, - pci_get_word(s->config + PCI_COMMAND) - & PCI_COMMAND_MASTER); + pci_set_master(s, pci_get_word(s->config + PCI_COMMAND) + & PCI_COMMAND_MASTER); g_free(config); return 0; @@ -1787,9 +1792,8 @@ void pci_default_write_config(PCIDevice *d, uint32_t addr, uint32_t val_in, int if (ranges_overlap(addr, l, PCI_COMMAND, 2)) { pci_update_irq_disabled(d, was_irq_disabled); - memory_region_set_enabled(&d->bus_master_enable_region, - (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->enabled); + pci_set_master(d, (pci_get_word(d->config + PCI_COMMAND) & + PCI_COMMAND_MASTER) && d->enabled); } msi_write_config(d, addr, val_in, l); @@ -3100,9 +3104,8 @@ void pci_set_enabled(PCIDevice *d, bool state) d->enabled = state; pci_update_mappings(d); - memory_region_set_enabled(&d->bus_master_enable_region, - (pci_get_word(d->config + PCI_COMMAND) - & PCI_COMMAND_MASTER) && d->enabled); + pci_set_master(d, (pci_get_word(d->config + PCI_COMMAND) + & PCI_COMMAND_MASTER) && d->enabled); if (qdev_is_realized(&d->qdev)) { pci_device_reset(d); } diff --git a/include/hw/pci/pci_device.h b/include/hw/pci/pci_device.h index e41d95b0b0..eee0338568 100644 --- a/include/hw/pci/pci_device.h +++ b/include/hw/pci/pci_device.h @@ -90,6 +90,7 @@ struct PCIDevice { char name[64]; PCIIORegion io_regions[PCI_NUM_REGIONS]; AddressSpace bus_master_as; + bool is_master; MemoryRegion bus_master_container_region; MemoryRegion bus_master_enable_region; From 042cbc9aec7caca639dcb1a8a996406a7c572706 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:18:59 +0000 Subject: [PATCH 1338/2760] pci: Add an API to get IOMMU's min page size and virtual address width This kind of information is needed by devices implementing ATS in order to initialize their translation cache. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-8-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 17 +++++++++++++++++ include/hw/pci/pci.h | 26 ++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 1114ba8529..fc4954ac81 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2970,6 +2970,23 @@ void pci_device_unset_iommu_device(PCIDevice *dev) } } +int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, + uint32_t *min_page_size) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->get_iotlb_info) { + iommu_bus->iommu_ops->get_iotlb_info(iommu_bus->iommu_opaque, + addr_width, min_page_size); + return 0; + } + + return -ENODEV; +} + void pci_setup_iommu(PCIBus *bus, const PCIIOMMUOps *ops, void *opaque) { /* diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index c2fe6caa2c..d67ffe12db 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -429,6 +429,19 @@ typedef struct PCIIOMMUOps { * @devfn: device and function number of the PCI device. */ void (*unset_iommu_device)(PCIBus *bus, void *opaque, int devfn); + /** + * @get_iotlb_info: get properties required to initialize a device IOTLB. + * + * Callback required if devices are allowed to cache translations. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @addr_width: the address width of the IOMMU (output parameter). + * + * @min_page_size: the page size of the IOMMU (output parameter). + */ + void (*get_iotlb_info)(void *opaque, uint8_t *addr_width, + uint32_t *min_page_size); } PCIIOMMUOps; AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); @@ -436,6 +449,19 @@ bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod, Error **errp); void pci_device_unset_iommu_device(PCIDevice *dev); +/** + * pci_iommu_get_iotlb_info: get properties required to initialize a + * device IOTLB. + * + * Returns 0 on success, or a negative errno otherwise. + * + * @dev: the device that wants to get the information. + * @addr_width: the address width of the IOMMU (output parameter). + * @min_page_size: the page size of the IOMMU (output parameter). + */ +int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, + uint32_t *min_page_size); + /** * pci_setup_iommu: Initialize specific IOMMU handlers for a PCIBus * From 7e94e45296d68982d448ae57e195efcf8f66649e Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:19:00 +0000 Subject: [PATCH 1339/2760] memory: Store user data pointer in the IOMMU notifiers This will help developers of ATS-capable devices to track a state. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-9-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/system/memory.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/system/memory.h b/include/system/memory.h index fbbf4cf911..fc35a0dcad 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -183,6 +183,7 @@ struct IOMMUNotifier { hwaddr start; hwaddr end; int iommu_idx; + void *opaque; QLIST_ENTRY(IOMMUNotifier) node; }; typedef struct IOMMUNotifier IOMMUNotifier; From a849ff5d6fa9d263beaecd6421fff8e21d2591c8 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:19:01 +0000 Subject: [PATCH 1340/2760] pci: Add a pci-level initialization function for IOMMU notifiers This is meant to be used by ATS-capable devices. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250520071823.764266-10-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 17 +++++++++++++++++ include/hw/pci/pci.h | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index fc4954ac81..dfa5a0259e 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2939,6 +2939,23 @@ AddressSpace *pci_device_iommu_address_space(PCIDevice *dev) return &address_space_memory; } +int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n, + IOMMUNotify fn, void *opaque) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->init_iotlb_notifier) { + iommu_bus->iommu_ops->init_iotlb_notifier(bus, iommu_bus->iommu_opaque, + devfn, n, fn, opaque); + return 0; + } + + return -ENODEV; +} + bool pci_device_set_iommu_device(PCIDevice *dev, HostIOMMUDevice *hiod, Error **errp) { diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index d67ffe12db..f3016fd76f 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -442,6 +442,26 @@ typedef struct PCIIOMMUOps { */ void (*get_iotlb_info)(void *opaque, uint8_t *addr_width, uint32_t *min_page_size); + /** + * @init_iotlb_notifier: initialize an IOMMU notifier. + * + * Optional callback. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @n: the notifier to be initialized. + * + * @fn: the callback to be installed. + * + * @user_opaque: a user pointer that can be used to track a state. + */ + void (*init_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn, + IOMMUNotifier *n, IOMMUNotify fn, + void *user_opaque); } PCIIOMMUOps; AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); @@ -462,6 +482,19 @@ void pci_device_unset_iommu_device(PCIDevice *dev); int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, uint32_t *min_page_size); +/** + * pci_iommu_init_iotlb_notifier: initialize an IOMMU notifier. + * + * This function is used by devices before registering an IOTLB notifier. + * + * @dev: the device. + * @n: the notifier to be initialized. + * @fn: the callback to be installed. + * @opaque: a user pointer that can be used to track a state. + */ +int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n, + IOMMUNotify fn, void *opaque); + /** * pci_setup_iommu: Initialize specific IOMMU handlers for a PCIBus * From e9b457500adb023229a08ece3a8d7f5866dd360e Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:19:03 +0000 Subject: [PATCH 1341/2760] pci: Add a pci-level API for ATS Devices implementing ATS can send translation requests using pci_ats_request_translation. The invalidation events are sent back to the device using the iommu notifier managed with pci_iommu_register_iotlb_notifier / pci_iommu_unregister_iotlb_notifier. Signed-off-by: Clement Mathieu--Drif Co-authored-by: Ethan Milon Message-Id: <20250520071823.764266-11-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 81 ++++++++++++++++++++++++++++ include/hw/pci/pci.h | 126 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 207 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index dfa5a0259e..0c63cb4bbe 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2987,6 +2987,87 @@ void pci_device_unset_iommu_device(PCIDevice *dev) } } +ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid, + bool priv_req, bool exec_req, + hwaddr addr, size_t length, + bool no_write, IOMMUTLBEntry *result, + size_t result_length, + uint32_t *err_count) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if (!dev->is_master || + ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) { + return -EPERM; + } + + if (result_length == 0) { + return -ENOSPC; + } + + if (!pcie_ats_enabled(dev)) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->ats_request_translation) { + return iommu_bus->iommu_ops->ats_request_translation(bus, + iommu_bus->iommu_opaque, + devfn, pasid, priv_req, + exec_req, addr, length, + no_write, result, + result_length, err_count); + } + + return -ENODEV; +} + +int pci_iommu_register_iotlb_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUNotifier *n) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev)) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->register_iotlb_notifier) { + iommu_bus->iommu_ops->register_iotlb_notifier(bus, + iommu_bus->iommu_opaque, devfn, + pasid, n); + return 0; + } + + return -ENODEV; +} + +int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUNotifier *n) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev)) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->unregister_iotlb_notifier) { + iommu_bus->iommu_ops->unregister_iotlb_notifier(bus, + iommu_bus->iommu_opaque, + devfn, pasid, n); + return 0; + } + + return -ENODEV; +} + int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, uint32_t *min_page_size) { diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index f3016fd76f..5d72607ed5 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -462,6 +462,80 @@ typedef struct PCIIOMMUOps { void (*init_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn, IOMMUNotifier *n, IOMMUNotify fn, void *user_opaque); + /** + * @register_iotlb_notifier: setup an IOTLB invalidation notifier. + * + * Callback required if devices are allowed to cache translations. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to watch. + * + * @n: the notifier to register. + */ + void (*register_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, IOMMUNotifier *n); + /** + * @unregister_iotlb_notifier: remove an IOTLB invalidation notifier. + * + * Callback required if devices are allowed to cache translations. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to stop watching. + * + * @n: the notifier to unregister. + */ + void (*unregister_iotlb_notifier)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, IOMMUNotifier *n); + /** + * @ats_request_translation: issue an ATS request. + * + * Callback required if devices are allowed to use the address + * translation service. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to use for the request. + * + * @priv_req: privileged mode bit (PASID TLP). + * + * @exec_req: execute request bit (PASID TLP). + * + * @addr: start address of the memory range to be translated. + * + * @length: length of the memory range in bytes. + * + * @no_write: request a read-only translation (if supported). + * + * @result: buffer in which the TLB entries will be stored. + * + * @result_length: result buffer length. + * + * @err_count: number of untranslated subregions. + * + * Returns: the number of translations stored in the result buffer, or + * -ENOMEM if the buffer is not large enough. + */ + ssize_t (*ats_request_translation)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, bool priv_req, + bool exec_req, hwaddr addr, + size_t length, bool no_write, + IOMMUTLBEntry *result, + size_t result_length, + uint32_t *err_count); } PCIIOMMUOps; AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); @@ -495,6 +569,58 @@ int pci_iommu_get_iotlb_info(PCIDevice *dev, uint8_t *addr_width, int pci_iommu_init_iotlb_notifier(PCIDevice *dev, IOMMUNotifier *n, IOMMUNotify fn, void *opaque); +/** + * pci_ats_request_translation: perform an ATS request. + * + * Returns the number of translations stored in @result in case of success, + * a negative error code otherwise. + * -ENOMEM is returned when the result buffer is not large enough to store + * all the translations. + * + * @dev: the ATS-capable PCI device. + * @pasid: the pasid of the address space in which the translation will be done. + * @priv_req: privileged mode bit (PASID TLP). + * @exec_req: execute request bit (PASID TLP). + * @addr: start address of the memory range to be translated. + * @length: length of the memory range in bytes. + * @no_write: request a read-only translation (if supported). + * @result: buffer in which the TLB entries will be stored. + * @result_length: result buffer length. + * @err_count: number of untranslated subregions. + */ +ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid, + bool priv_req, bool exec_req, + hwaddr addr, size_t length, + bool no_write, IOMMUTLBEntry *result, + size_t result_length, + uint32_t *err_count); + +/** + * pci_iommu_register_iotlb_notifier: register a notifier for changes to + * IOMMU translation entries in a specific address space. + * + * Returns 0 on success, or a negative errno otherwise. + * + * @dev: the device that wants to get notified. + * @pasid: the pasid of the address space to track. + * @n: the notifier to register. + */ +int pci_iommu_register_iotlb_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUNotifier *n); + +/** + * pci_iommu_unregister_iotlb_notifier: unregister a notifier that has been + * registerd with pci_iommu_register_iotlb_notifier. + * + * Returns 0 on success, or a negative errno otherwise. + * + * @dev: the device that wants to stop notifications. + * @pasid: the pasid of the address space to stop tracking. + * @n: the notifier to unregister. + */ +int pci_iommu_unregister_iotlb_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUNotifier *n); + /** * pci_setup_iommu: Initialize specific IOMMU handlers for a PCIBus * From f0f37daf8e67c7208641aec5e238197279ca7331 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Tue, 20 May 2025 07:19:04 +0000 Subject: [PATCH 1342/2760] pci: Add a PCI-level API for PRI A device can send a PRI request to the IOMMU using pci_pri_request_page. The PRI response is sent back using the notifier managed with pci_pri_register_notifier and pci_pri_unregister_notifier. Signed-off-by: Clement Mathieu--Drif Co-authored-by: Ethan Milon Message-Id: <20250520071823.764266-12-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 66 ++++++++++++++++++++++ include/hw/pci/pci.h | 130 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 196 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 0c63cb4bbe..c6b5768f3a 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -2987,6 +2987,72 @@ void pci_device_unset_iommu_device(PCIDevice *dev) } } +int pci_pri_request_page(PCIDevice *dev, uint32_t pasid, bool priv_req, + bool exec_req, hwaddr addr, bool lpig, + uint16_t prgi, bool is_read, bool is_write) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if (!dev->is_master || + ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) { + return -EPERM; + } + + if (!pcie_pri_enabled(dev)) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->pri_request_page) { + return iommu_bus->iommu_ops->pri_request_page(bus, + iommu_bus->iommu_opaque, + devfn, pasid, priv_req, + exec_req, addr, lpig, prgi, + is_read, is_write); + } + + return -ENODEV; +} + +int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUPRINotifier *notifier) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + if (!dev->is_master || + ((pasid != PCI_NO_PASID) && !pcie_pasid_enabled(dev))) { + return -EPERM; + } + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->pri_register_notifier) { + iommu_bus->iommu_ops->pri_register_notifier(bus, + iommu_bus->iommu_opaque, + devfn, pasid, notifier); + return 0; + } + + return -ENODEV; +} + +void pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid) +{ + PCIBus *bus; + PCIBus *iommu_bus; + int devfn; + + pci_device_get_iommu_bus_devfn(dev, &bus, &iommu_bus, &devfn); + if (iommu_bus && iommu_bus->iommu_ops->pri_unregister_notifier) { + iommu_bus->iommu_ops->pri_unregister_notifier(bus, + iommu_bus->iommu_opaque, + devfn, pasid); + } +} + ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid, bool priv_req, bool exec_req, hwaddr addr, size_t length, diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 5d72607ed5..a6854dad2b 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -375,6 +375,28 @@ void pci_bus_get_w64_range(PCIBus *bus, Range *range); void pci_device_deassert_intx(PCIDevice *dev); +/* Page Request Interface */ +typedef enum { + IOMMU_PRI_RESP_SUCCESS, + IOMMU_PRI_RESP_INVALID_REQUEST, + IOMMU_PRI_RESP_FAILURE, +} IOMMUPRIResponseCode; + +typedef struct IOMMUPRIResponse { + IOMMUPRIResponseCode response_code; + uint16_t prgi; +} IOMMUPRIResponse; + +struct IOMMUPRINotifier; + +typedef void (*IOMMUPRINotify)(struct IOMMUPRINotifier *notifier, + IOMMUPRIResponse *response); + +typedef struct IOMMUPRINotifier { + IOMMUPRINotify notify; +} IOMMUPRINotifier; + +#define PCI_PRI_PRGI_MASK 0x1ffU /** * struct PCIIOMMUOps: callbacks structure for specific IOMMU handlers @@ -536,6 +558,72 @@ typedef struct PCIIOMMUOps { IOMMUTLBEntry *result, size_t result_length, uint32_t *err_count); + /** + * @pri_register_notifier: setup the PRI completion callback. + * + * Callback required if devices are allowed to use the page request + * interface. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to track. + * + * @notifier: the notifier to register. + */ + void (*pri_register_notifier)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, IOMMUPRINotifier *notifier); + /** + * @pri_unregister_notifier: remove the PRI completion callback. + * + * Callback required if devices are allowed to use the page request + * interface. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to stop tracking. + */ + void (*pri_unregister_notifier)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid); + /** + * @pri_request_page: issue a PRI request. + * + * Callback required if devices are allowed to use the page request + * interface. + * + * @bus: the #PCIBus of the PCI device. + * + * @opaque: the data passed to pci_setup_iommu(). + * + * @devfn: device and function number of the PCI device. + * + * @pasid: the pasid of the address space to use for the request. + * + * @priv_req: privileged mode bit (PASID TLP). + * + * @exec_req: execute request bit (PASID TLP). + * + * @addr: untranslated address of the requested page. + * + * @lpig: last page in group. + * + * @prgi: page request group index. + * + * @is_read: request read access. + * + * @is_write: request write access. + */ + int (*pri_request_page)(PCIBus *bus, void *opaque, int devfn, + uint32_t pasid, bool priv_req, bool exec_req, + hwaddr addr, bool lpig, uint16_t prgi, bool is_read, + bool is_write); } PCIIOMMUOps; AddressSpace *pci_device_iommu_address_space(PCIDevice *dev); @@ -595,6 +683,48 @@ ssize_t pci_ats_request_translation(PCIDevice *dev, uint32_t pasid, size_t result_length, uint32_t *err_count); +/** + * pci_pri_request_page: perform a PRI request. + * + * Returns 0 if the PRI request has been sent to the guest OS, + * an error code otherwise. + * + * @dev: the PRI-capable PCI device. + * @pasid: the pasid of the address space in which the translation will be done. + * @priv_req: privileged mode bit (PASID TLP). + * @exec_req: execute request bit (PASID TLP). + * @addr: untranslated address of the requested page. + * @lpig: last page in group. + * @prgi: page request group index. + * @is_read: request read access. + * @is_write: request write access. + */ +int pci_pri_request_page(PCIDevice *dev, uint32_t pasid, bool priv_req, + bool exec_req, hwaddr addr, bool lpig, + uint16_t prgi, bool is_read, bool is_write); + +/** + * pci_pri_register_notifier: register the PRI callback for a given address + * space. + * + * Returns 0 on success, an error code otherwise. + * + * @dev: the PRI-capable PCI device. + * @pasid: the pasid of the address space to track. + * @notifier: the notifier to register. + */ +int pci_pri_register_notifier(PCIDevice *dev, uint32_t pasid, + IOMMUPRINotifier *notifier); + +/** + * pci_pri_unregister_notifier: remove the PRI callback from a given address + * space. + * + * @dev: the PRI-capable PCI device. + * @pasid: the pasid of the address space to stop tracking. + */ +void pci_pri_unregister_notifier(PCIDevice *dev, uint32_t pasid); + /** * pci_iommu_register_iotlb_notifier: register a notifier for changes to * IOMMU translation entries in a specific address space. From 75d4680c55498d6cf4f89b31e52d97f90d7fc46e Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 20 May 2025 21:01:51 +0800 Subject: [PATCH 1343/2760] uefi-test-tools:: Add LoongArch64 support Add support to build bios-tables-test iso image for LoongArch system. Signed-off-by: Bibo Mao Acked-by: Gerd Hoffmann Message-Id: <20250520130158.767083-2-maobibo@loongson.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/uefi-test-tools/Makefile | 5 +++-- .../UefiTestToolsPkg/UefiTestToolsPkg.dsc | 6 +++++- tests/uefi-test-tools/uefi-test-build.config | 10 ++++++++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/tests/uefi-test-tools/Makefile b/tests/uefi-test-tools/Makefile index f4eaebd8ff..8ee6fb3571 100644 --- a/tests/uefi-test-tools/Makefile +++ b/tests/uefi-test-tools/Makefile @@ -12,7 +12,7 @@ edk2_dir := ../../roms/edk2 images_dir := ../data/uefi-boot-images -emulation_targets := arm aarch64 i386 x86_64 riscv64 +emulation_targets := arm aarch64 i386 x86_64 riscv64 loongarch64 uefi_binaries := bios-tables-test intermediate_suffixes := .efi .fat .iso.raw @@ -56,7 +56,8 @@ Build/%.iso.raw: Build/%.fat # stripped from, the argument. map_arm_to_uefi = $(subst arm,ARM,$(1)) map_aarch64_to_uefi = $(subst aarch64,AA64,$(call map_arm_to_uefi,$(1))) -map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_aarch64_to_uefi,$(1))) +map_loongarch64_to_uefi = $(subst loongarch64,LOONGARCH64,$(call map_aarch64_to_uefi,$(1))) +map_riscv64_to_uefi = $(subst riscv64,RISCV64,$(call map_loongarch64_to_uefi,$(1))) map_i386_to_uefi = $(subst i386,IA32,$(call map_riscv64_to_uefi,$(1))) map_x86_64_to_uefi = $(subst x86_64,X64,$(call map_i386_to_uefi,$(1))) map_to_uefi = $(subst .,,$(call map_x86_64_to_uefi,$(1))) diff --git a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc index 0902fd3c73..facf8df1fa 100644 --- a/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc +++ b/tests/uefi-test-tools/UefiTestToolsPkg/UefiTestToolsPkg.dsc @@ -19,7 +19,7 @@ PLATFORM_VERSION = 0.1 PLATFORM_NAME = UefiTestTools SKUID_IDENTIFIER = DEFAULT - SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64|RISCV64 + SUPPORTED_ARCHITECTURES = ARM|AARCH64|IA32|X64|RISCV64|LOONGARCH64 BUILD_TARGETS = DEBUG [BuildOptions.IA32] @@ -65,6 +65,10 @@ [LibraryClasses.RISCV64] BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf +[LibraryClasses.LOONGARCH64] + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + StackCheckLib|MdePkg/Library/StackCheckLibNull/StackCheckLibNull.inf + [PcdsFixedAtBuild] gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8040004F gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x2F diff --git a/tests/uefi-test-tools/uefi-test-build.config b/tests/uefi-test-tools/uefi-test-build.config index a4c61fc97a..8bf4826634 100644 --- a/tests/uefi-test-tools/uefi-test-build.config +++ b/tests/uefi-test-tools/uefi-test-build.config @@ -21,6 +21,16 @@ dest = ./Build arch = AARCH64 cpy1 = AARCH64/BiosTablesTest.efi bios-tables-test.aarch64.efi +#################################################################################### +# loongarch64 + +[build.loongarch64] +conf = UefiTestToolsPkg/UefiTestToolsPkg.dsc +plat = UefiTestTools +dest = ./Build +arch = LOONGARCH64 +cpy1 = LOONGARCH64/BiosTablesTest.efi bios-tables-test.loongarch64.efi + #################################################################################### # riscv64 From 0265723eba8a612d8a94d190cef988cf8d616862 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 20 May 2025 21:01:52 +0800 Subject: [PATCH 1344/2760] tests/data/uefi-boot-images: Add ISO image for LoongArch system To test ACPI tables, edk2 needs to be booted with a disk image having EFI partition. This image is created using UefiTestToolsPkg. The image is generated with the following command: make -f tests/uefi-test-tools/Makefile Signed-off-by: Bibo Mao Acked-by: Gerd Hoffmann Message-Id: <20250520130158.767083-3-maobibo@loongson.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- .../bios-tables-test.loongarch64.iso.qcow2 | Bin 0 -> 12800 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 diff --git a/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 b/tests/data/uefi-boot-images/bios-tables-test.loongarch64.iso.qcow2 new file mode 100644 index 0000000000000000000000000000000000000000..18daee0c52fcc703a0645dbcdc598aa1f101d96b GIT binary patch literal 12800 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 G4EbN%8c56l literal 0 HcmV?d00001 From 82acc8c83f93814bb01669412a0a226c39fa8d77 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Tue, 20 May 2025 21:01:53 +0800 Subject: [PATCH 1345/2760] tests/qtest/bios-tables-test: Use MiB macro rather hardcode value Replace 1024 * 1024 with MiB macro. Signed-off-by: Bibo Mao Message-Id: <20250520130158.767083-4-maobibo@loongson.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 0a333ec435..0b2bdf9d0d 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1622,7 +1622,7 @@ static void test_acpi_aarch64_virt_tcg_memhp(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 256ULL * 1024 * 1024, + .scan_len = 256ULL * MiB, }; data.variant = ".memhp"; @@ -1717,7 +1717,7 @@ static void test_acpi_riscv64_virt_tcg_numamem(void) .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2", .ram_start = 0x80000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.variant = ".numamem"; @@ -1743,7 +1743,7 @@ static void test_acpi_aarch64_virt_tcg_numamem(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.variant = ".numamem"; @@ -1765,7 +1765,7 @@ static void test_acpi_aarch64_virt_tcg_pxb(void) .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; /* * While using -cdrom, the cdrom would auto plugged into pxb-pcie, @@ -1841,7 +1841,7 @@ static void test_acpi_aarch64_virt_tcg_acpi_hmat(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.variant = ".acpihmatvirt"; @@ -2095,7 +2095,7 @@ static void test_acpi_riscv64_virt_tcg(void) .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2", .ram_start = 0x80000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; /* @@ -2117,7 +2117,7 @@ static void test_acpi_aarch64_virt_tcg(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; data.smbios_cpu_max_speed = 2900; @@ -2138,7 +2138,7 @@ static void test_acpi_aarch64_virt_tcg_topology(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; test_acpi_one("-cpu cortex-a57 " @@ -2223,7 +2223,7 @@ static void test_acpi_aarch64_virt_viot(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; test_acpi_one("-cpu cortex-a57 " @@ -2407,7 +2407,7 @@ static void test_acpi_aarch64_virt_oem_fields(void) .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", .ram_start = 0x40000000ULL, - .scan_len = 128ULL * 1024 * 1024, + .scan_len = 128ULL * MiB, }; char *args; From a9403bfcd93025df7b1924d0cf34fbc408955b33 Mon Sep 17 00:00:00 2001 From: Huaitong Han Date: Thu, 22 May 2025 18:05:48 +0800 Subject: [PATCH 1346/2760] vhost: Don't set vring call if guest notifier is unused The vring call fd is set even when the guest does not use MSI-X (e.g., in the case of virtio PMD), leading to unnecessary CPU overhead for processing interrupts. The commit 96a3d98d2c("vhost: don't set vring call if no vector") optimized the case where MSI-X is enabled but the queue vector is unset. However, there's an additional case where the guest uses INTx and the INTx_DISABLED bit in the PCI config is set, meaning that no interrupt notifier will actually be used. In such cases, the vring call fd should also be cleared to avoid redundant interrupt handling. Fixes: 96a3d98d2c("vhost: don't set vring call if no vector") Reported-by: Zhiyuan Yuan Signed-off-by: Jidong Xia Signed-off-by: Huaitong Han Message-Id: <20250522100548.212740-1-hanht2@chinatelecom.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pci.c | 2 +- hw/virtio/virtio-pci.c | 7 ++++++- include/hw/pci/pci.h | 1 + 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index c6b5768f3a..9b4bf48439 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -1730,7 +1730,7 @@ static void pci_update_mappings(PCIDevice *d) pci_update_vga(d); } -static inline int pci_irq_disabled(PCIDevice *d) +int pci_irq_disabled(PCIDevice *d) { return pci_get_word(d->config + PCI_COMMAND) & PCI_COMMAND_INTX_DISABLE; } diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index 9b48aa8c3e..7e309d1d49 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -1215,7 +1215,12 @@ static int virtio_pci_set_guest_notifier(DeviceState *d, int n, bool assign, static bool virtio_pci_query_guest_notifiers(DeviceState *d) { VirtIOPCIProxy *proxy = to_virtio_pci_proxy(d); - return msix_enabled(&proxy->pci_dev); + + if (msix_enabled(&proxy->pci_dev)) { + return true; + } else { + return pci_irq_disabled(&proxy->pci_dev); + } } static int virtio_pci_set_guest_notifiers(DeviceState *d, int nvqs, bool assign) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index a6854dad2b..35d59d7672 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -983,6 +983,7 @@ void lsi53c8xx_handle_legacy_cmdline(DeviceState *lsi_dev); qemu_irq pci_allocate_irq(PCIDevice *pci_dev); void pci_set_irq(PCIDevice *pci_dev, int level); +int pci_irq_disabled(PCIDevice *d); static inline void pci_irq_assert(PCIDevice *pci_dev) { From 55cf1d4f10160e37440d9e727a160ac1141bbb53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:33 -0400 Subject: [PATCH 1347/2760] vdpa: check for iova tree initialized at net_client_start MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To map the guest memory while it is migrating we need to create the iova_tree, as long as the destination uses x-svq=on. Checking to not override it. The function vhost_vdpa_net_client_stop clear it if the device is stopped. If the guest starts the device again, the iova tree is recreated by vhost_vdpa_net_data_start_first or vhost_vdpa_net_cvq_start if needed, so old behavior is kept. Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-2-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-vdpa.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 7ca8b46eee..decb826868 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -366,7 +366,9 @@ static void vhost_vdpa_net_data_start_first(VhostVDPAState *s) migration_add_notifier(&s->migration_state, vdpa_net_migration_state_notifier); - if (v->shadow_vqs_enabled) { + + /* iova_tree may be initialized by vhost_vdpa_net_load_setup */ + if (v->shadow_vqs_enabled && !v->shared->iova_tree) { v->shared->iova_tree = vhost_iova_tree_new(v->shared->iova_range.first, v->shared->iova_range.last); } From 3312e6c8c9aa8f32019f14c74d209db17b9306eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:34 -0400 Subject: [PATCH 1348/2760] vdpa: reorder vhost_vdpa_set_backend_cap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It will be used directly by vhost_vdpa_init. Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-3-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 60 +++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 1ab2c11fa8..6b242ca56a 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -594,6 +594,36 @@ static void vhost_vdpa_init_svq(struct vhost_dev *hdev, struct vhost_vdpa *v) v->shadow_vqs = g_steal_pointer(&shadow_vqs); } +static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev) +{ + struct vhost_vdpa *v = dev->opaque; + + uint64_t features; + uint64_t f = 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2 | + 0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH | + 0x1ULL << VHOST_BACKEND_F_IOTLB_ASID | + 0x1ULL << VHOST_BACKEND_F_SUSPEND; + int r; + + if (vhost_vdpa_call(dev, VHOST_GET_BACKEND_FEATURES, &features)) { + return -EFAULT; + } + + features &= f; + + if (vhost_vdpa_first_dev(dev)) { + r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features); + if (r) { + return -EFAULT; + } + } + + dev->backend_cap = features; + v->shared->backend_cap = features; + + return 0; +} + static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) { struct vhost_vdpa *v = opaque; @@ -841,36 +871,6 @@ static int vhost_vdpa_set_features(struct vhost_dev *dev, return vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_FEATURES_OK); } -static int vhost_vdpa_set_backend_cap(struct vhost_dev *dev) -{ - struct vhost_vdpa *v = dev->opaque; - - uint64_t features; - uint64_t f = 0x1ULL << VHOST_BACKEND_F_IOTLB_MSG_V2 | - 0x1ULL << VHOST_BACKEND_F_IOTLB_BATCH | - 0x1ULL << VHOST_BACKEND_F_IOTLB_ASID | - 0x1ULL << VHOST_BACKEND_F_SUSPEND; - int r; - - if (vhost_vdpa_call(dev, VHOST_GET_BACKEND_FEATURES, &features)) { - return -EFAULT; - } - - features &= f; - - if (vhost_vdpa_first_dev(dev)) { - r = vhost_vdpa_call(dev, VHOST_SET_BACKEND_FEATURES, &features); - if (r) { - return -EFAULT; - } - } - - dev->backend_cap = features; - v->shared->backend_cap = features; - - return 0; -} - static int vhost_vdpa_get_device_id(struct vhost_dev *dev, uint32_t *device_id) { From 32f0c7ce4c948b2e275dca3cf7fa2a00677b9c1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:35 -0400 Subject: [PATCH 1349/2760] vdpa: set backend capabilities at vhost_vdpa_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The backend does not reset them until the vdpa file descriptor is closed so there is no harm in doing it only once. This allows the destination of a live migration to premap memory in batches, using VHOST_BACKEND_F_IOTLB_BATCH. Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-4-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 6b242ca56a..e9826ede2c 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -634,6 +634,12 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) v->dev = dev; dev->opaque = opaque ; v->shared->listener = vhost_vdpa_memory_listener; + + ret = vhost_vdpa_set_backend_cap(dev); + if (unlikely(ret != 0)) { + return ret; + } + vhost_vdpa_init_svq(dev, v); error_propagate(&dev->migration_blocker, v->migration_blocker); @@ -1563,7 +1569,6 @@ const VhostOps vdpa_ops = { .vhost_set_vring_kick = vhost_vdpa_set_vring_kick, .vhost_set_vring_call = vhost_vdpa_set_vring_call, .vhost_get_features = vhost_vdpa_get_features, - .vhost_set_backend_cap = vhost_vdpa_set_backend_cap, .vhost_set_owner = vhost_vdpa_set_owner, .vhost_set_vring_endian = NULL, .vhost_backend_memslots_limit = vhost_vdpa_memslots_limit, From be2e5fbefa7c1f8243bf6db2486b950b1ab19b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:36 -0400 Subject: [PATCH 1350/2760] vdpa: add listener_registered MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check if the listener has been registered or not, so it needs to be registered again at start. Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-5-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 7 ++++++- include/hw/virtio/vhost-vdpa.h | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index e9826ede2c..450f68f117 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -1379,7 +1379,10 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) "IOMMU and try again"); return -1; } - memory_listener_register(&v->shared->listener, dev->vdev->dma_as); + if (!v->shared->listener_registered) { + memory_listener_register(&v->shared->listener, dev->vdev->dma_as); + v->shared->listener_registered = true; + } return vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); } @@ -1399,6 +1402,8 @@ static void vhost_vdpa_reset_status(struct vhost_dev *dev) vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); memory_listener_unregister(&v->shared->listener); + v->shared->listener_registered = false; + } static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base, diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index 0a9575b469..221840987e 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -51,6 +51,12 @@ typedef struct vhost_vdpa_shared { bool iotlb_batch_begin_sent; + /* + * The memory listener has been registered, so DMA maps have been sent to + * the device. + */ + bool listener_registered; + /* Vdpa must send shadow addresses as IOTLB key for data queues, not GPA */ bool shadow_data; From 9344dcbd004f951155c020d01c1bdc881e0451c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:37 -0400 Subject: [PATCH 1351/2760] vdpa: reorder listener assignment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit f6fe3e333f ("vdpa: move memory listener to vhost_vdpa_shared") this piece of code repeatedly assign shared->listener members. This was not a problem as it was not used until device start. However next patches move the listener registration to this vhost_vdpa_init function. When the listener is registered it is added to an embedded linked list, so setting its members again will cause memory corruption to the linked list node. Do the right thing and only set it in the first vdpa device. Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-6-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index 450f68f117..de834f2ebd 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -633,7 +633,6 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) v->dev = dev; dev->opaque = opaque ; - v->shared->listener = vhost_vdpa_memory_listener; ret = vhost_vdpa_set_backend_cap(dev); if (unlikely(ret != 0)) { @@ -675,6 +674,7 @@ static int vhost_vdpa_init(struct vhost_dev *dev, void *opaque, Error **errp) vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); + v->shared->listener = vhost_vdpa_memory_listener; return 0; } From a400720365ea86602044b78dd8654911d0fe3977 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:38 -0400 Subject: [PATCH 1352/2760] vdpa: move iova_tree allocation to net_vhost_vdpa_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As we are moving to keep the mapping through all the vdpa device life instead of resetting it at VirtIO reset, we need to move all its dependencies to the initialization too. In particular devices with x-svq=on need a valid iova_tree from the beginning. Simplify the code also consolidating the two creation points: the first data vq in case of SVQ active and CVQ start in case only CVQ uses it. Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Suggested-by: Si-Wei Liu Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-7-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/hw/virtio/vhost-vdpa.h | 16 ++++++++++++++- net/vhost-vdpa.c | 36 +++------------------------------- 2 files changed, 18 insertions(+), 34 deletions(-) diff --git a/include/hw/virtio/vhost-vdpa.h b/include/hw/virtio/vhost-vdpa.h index 221840987e..449bf5c840 100644 --- a/include/hw/virtio/vhost-vdpa.h +++ b/include/hw/virtio/vhost-vdpa.h @@ -43,7 +43,21 @@ typedef struct vhost_vdpa_shared { struct vhost_vdpa_iova_range iova_range; QLIST_HEAD(, vdpa_iommu) iommu_list; - /* IOVA mapping used by the Shadow Virtqueue */ + /* + * IOVA mapping used by the Shadow Virtqueue + * + * It is shared among all ASID for simplicity, whether CVQ shares ASID with + * guest or not: + * - Memory listener need access to guest's memory addresses allocated in + * the IOVA tree. + * - There should be plenty of IOVA address space for both ASID not to + * worry about collisions between them. Guest's translations are still + * validated with virtio virtqueue_pop so there is no risk for the guest + * to access memory that it shouldn't. + * + * To allocate a iova tree per ASID is doable but it complicates the code + * and it is not worth it for the moment. + */ VhostIOVATree *iova_tree; /* Copy of backend features */ diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index decb826868..58d738945d 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -235,6 +235,7 @@ static void vhost_vdpa_cleanup(NetClientState *nc) return; } qemu_close(s->vhost_vdpa.shared->device_fd); + g_clear_pointer(&s->vhost_vdpa.shared->iova_tree, vhost_iova_tree_delete); g_free(s->vhost_vdpa.shared); } @@ -362,16 +363,8 @@ static int vdpa_net_migration_state_notifier(NotifierWithReturn *notifier, static void vhost_vdpa_net_data_start_first(VhostVDPAState *s) { - struct vhost_vdpa *v = &s->vhost_vdpa; - migration_add_notifier(&s->migration_state, vdpa_net_migration_state_notifier); - - /* iova_tree may be initialized by vhost_vdpa_net_load_setup */ - if (v->shadow_vqs_enabled && !v->shared->iova_tree) { - v->shared->iova_tree = vhost_iova_tree_new(v->shared->iova_range.first, - v->shared->iova_range.last); - } } static int vhost_vdpa_net_data_start(NetClientState *nc) @@ -418,19 +411,12 @@ static int vhost_vdpa_net_data_load(NetClientState *nc) static void vhost_vdpa_net_client_stop(NetClientState *nc) { VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); - struct vhost_dev *dev; assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); if (s->vhost_vdpa.index == 0) { migration_remove_notifier(&s->migration_state); } - - dev = s->vhost_vdpa.dev; - if (dev->vq_index + dev->nvqs == dev->vq_index_end) { - g_clear_pointer(&s->vhost_vdpa.shared->iova_tree, - vhost_iova_tree_delete); - } } static NetClientInfo net_vhost_vdpa_info = { @@ -602,24 +588,6 @@ static int vhost_vdpa_net_cvq_start(NetClientState *nc) return 0; } - /* - * If other vhost_vdpa already have an iova_tree, reuse it for simplicity, - * whether CVQ shares ASID with guest or not, because: - * - Memory listener need access to guest's memory addresses allocated in - * the IOVA tree. - * - There should be plenty of IOVA address space for both ASID not to - * worry about collisions between them. Guest's translations are still - * validated with virtio virtqueue_pop so there is no risk for the guest - * to access memory that it shouldn't. - * - * To allocate a iova tree per ASID is doable but it complicates the code - * and it is not worth it for the moment. - */ - if (!v->shared->iova_tree) { - v->shared->iova_tree = vhost_iova_tree_new(v->shared->iova_range.first, - v->shared->iova_range.last); - } - r = vhost_vdpa_cvq_map_buf(&s->vhost_vdpa, s->cvq_cmd_out_buffer, vhost_vdpa_net_cvq_cmd_page_len(), false); if (unlikely(r < 0)) { @@ -1728,6 +1696,8 @@ static NetClientState *net_vhost_vdpa_init(NetClientState *peer, s->vhost_vdpa.shared->device_fd = vdpa_device_fd; s->vhost_vdpa.shared->iova_range = iova_range; s->vhost_vdpa.shared->shadow_data = svq; + s->vhost_vdpa.shared->iova_tree = vhost_iova_tree_new(iova_range.first, + iova_range.last); } else if (!is_datapath) { s->cvq_cmd_out_buffer = mmap(NULL, vhost_vdpa_net_cvq_cmd_page_len(), PROT_READ | PROT_WRITE, From 494c50dcc0995ae6eb526d2848c33cbf910ab218 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Eugenio=20P=C3=A9rez?= Date: Thu, 22 May 2025 10:58:39 -0400 Subject: [PATCH 1353/2760] vdpa: move memory listener register to vhost_vdpa_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Current memory operations like pinning may take a lot of time at the destination. Currently they are done after the source of the migration is stopped, and before the workload is resumed at the destination. This is a period where neigher traffic can flow, nor the VM workload can continue (downtime). We can do better as we know the memory layout of the guest RAM at the destination from the moment that all devices are initializaed. So moving that operation allows QEMU to communicate the kernel the maps while the workload is still running in the source, so Linux can start mapping them. As a small drawback, there is a time in the initialization where QEMU cannot respond to QMP etc. By some testing, this time is about 0.2seconds. This may be further reduced (or increased) depending on the vdpa driver and the platform hardware, and it is dominated by the cost of memory pinning. This matches the time that we move out of the called downtime window. The downtime is measured as the elapsed trace time between the last vhost_vdpa_suspend on the source and the last vhost_vdpa_set_vring_enable_one on the destination. In other words, from "guest CPUs freeze" to the instant the final Rx/Tx queue-pair is able to start moving data. Using ConnectX-6 Dx (MLX5) NICs in vhost-vDPA mode with 8 queue-pairs, the series reduces guest-visible downtime during back-to-back live migrations by more than half: - 39G VM: 4.72s -> 2.09s (-2.63s, ~56% improvement) - 128G VM: 14.72s -> 5.83s (-8.89s, ~60% improvement) Tested-by: Lei Yang Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Signed-off-by: Eugenio Pérez Signed-off-by: Jonah Palmer Message-Id: <20250522145839.59974-8-jonah.palmer@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost-vdpa.c | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index de834f2ebd..e20da95f30 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -894,8 +894,14 @@ static int vhost_vdpa_reset_device(struct vhost_dev *dev) ret = vhost_vdpa_call(dev, VHOST_VDPA_SET_STATUS, &status); trace_vhost_vdpa_reset_device(dev); + if (ret) { + return ret; + } + + memory_listener_unregister(&v->shared->listener); + v->shared->listener_registered = false; v->suspended = false; - return ret; + return 0; } static int vhost_vdpa_get_vq_index(struct vhost_dev *dev, int idx) @@ -1379,6 +1385,11 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) "IOMMU and try again"); return -1; } + if (v->shared->listener_registered && + dev->vdev->dma_as != v->shared->listener.address_space) { + memory_listener_unregister(&v->shared->listener); + v->shared->listener_registered = false; + } if (!v->shared->listener_registered) { memory_listener_register(&v->shared->listener, dev->vdev->dma_as); v->shared->listener_registered = true; @@ -1392,8 +1403,6 @@ static int vhost_vdpa_dev_start(struct vhost_dev *dev, bool started) static void vhost_vdpa_reset_status(struct vhost_dev *dev) { - struct vhost_vdpa *v = dev->opaque; - if (!vhost_vdpa_last_dev(dev)) { return; } @@ -1401,9 +1410,6 @@ static void vhost_vdpa_reset_status(struct vhost_dev *dev) vhost_vdpa_reset_device(dev); vhost_vdpa_add_status(dev, VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER); - memory_listener_unregister(&v->shared->listener); - v->shared->listener_registered = false; - } static int vhost_vdpa_set_log_base(struct vhost_dev *dev, uint64_t base, @@ -1537,12 +1543,27 @@ static int vhost_vdpa_get_features(struct vhost_dev *dev, static int vhost_vdpa_set_owner(struct vhost_dev *dev) { + int r; + struct vhost_vdpa *v; + if (!vhost_vdpa_first_dev(dev)) { return 0; } trace_vhost_vdpa_set_owner(dev); - return vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL); + r = vhost_vdpa_call(dev, VHOST_SET_OWNER, NULL); + if (unlikely(r < 0)) { + return r; + } + + /* + * Being optimistic and listening address space memory. If the device + * uses vIOMMU, it is changed at vhost_vdpa_dev_start. + */ + v = dev->opaque; + memory_listener_register(&v->shared->listener, &address_space_memory); + v->shared->listener_registered = true; + return 0; } static int vhost_vdpa_vq_get_addr(struct vhost_dev *dev, From 0b006153b7ec66505cb2d231235aa19ca5d2ce37 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Mon, 26 May 2025 22:38:20 +0200 Subject: [PATCH 1354/2760] hw/i386/pc_piix: Fix RTC ISA IRQ wiring of isapc machine Commit 56b1f50e3c10 ("hw/i386/pc: Wire RTC ISA IRQs in south bridges") attempted to refactor RTC IRQ wiring which was previously done in pc_basic_device_init() but forgot about the isapc machine. Fix this by wiring in the code section dedicated exclusively to the isapc machine. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2961 Fixes: 56b1f50e3c10 ("hw/i386/pc: Wire RTC ISA IRQs in south bridges") cc: qemu-stable Signed-off-by: Bernhard Beschow Reviewed-by: Mark Cave-Ayland Message-Id: <20250526203820.1853-1-shentey@gmail.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/pc_piix.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 0dce512f18..6b6359ef65 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -285,6 +285,8 @@ static void pc_init1(MachineState *machine, const char *pci_type) pcms->idebus[0] = qdev_get_child_bus(dev, "ide.0"); pcms->idebus[1] = qdev_get_child_bus(dev, "ide.1"); } else { + uint32_t irq; + isa_bus = isa_bus_new(NULL, system_memory, system_io, &error_abort); isa_bus_register_input_irqs(isa_bus, x86ms->gsi); @@ -292,6 +294,9 @@ static void pc_init1(MachineState *machine, const char *pci_type) x86ms->rtc = isa_new(TYPE_MC146818_RTC); qdev_prop_set_int32(DEVICE(x86ms->rtc), "base_year", 2000); isa_realize_and_unref(x86ms->rtc, isa_bus, &error_fatal); + irq = object_property_get_uint(OBJECT(x86ms->rtc), "irq", + &error_fatal); + isa_connect_gpio_out(ISA_DEVICE(x86ms->rtc), 0, irq); i8257_dma_init(OBJECT(machine), isa_bus, 0); pcms->hpet_enabled = false; From ffcfb0faaa95fc6ca007f7dd989e390dacf936ca Mon Sep 17 00:00:00 2001 From: Tanish Desai Date: Wed, 28 May 2025 19:25:28 +0000 Subject: [PATCH 1355/2760] trace/simple: seperate hot paths of tracing fucntions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change improves performance by moving the hot path of the trace_vhost_commit()(or any other trace function) logic to the header file. Previously, even when the trace event was disabled, the function call chain:- trace_vhost_commit()(Or any other trace function) → _nocheck__trace_vhost_commit() → _simple_trace_vhost_commit() incurred a significant function prologue overhead before checking the trace state. Disassembly of _simple_trace_vhost_commit() (from the .c file) showed that 11 out of the first 14 instructions were prologue-related, including: 0x10 stp x29, x30, [sp, #-64]! Prologue: allocates 64-byte frame and saves old FP (x29) & LR (x30) 0x14 adrp x3, trace_events_enabled_count Prologue: computes page-base of the trace-enable counter 0x18 adrp x2, __stack_chk_guard Important (maybe prolog don't know?)(stack-protector): starts up the stack-canary load 0x1c mov x29, sp Prologue: sets new frame pointer 0x20 ldr x3, [x3] Prologue: loads the actual trace-enabled count 0x24 stp x19, x20, [sp, #16] Prologue: spills callee-saved regs used by this function (x19, x20) 0x28 and w20, w0, #0xff Tracepoint setup: extracts the low-8 bits of arg0 as the “event boolean” 0x2c ldr x2, [x2] Prologue (cont’d): completes loading of the stack-canary value 0x30 and w19, w1, #0xff Tracepoint setup: extracts low-8 bits of arg1 0x34 ldr w0, [x3] Important: loads the current trace-enabled flag from memory 0x38 ldr x1, [x2] Prologue (cont’d): reads the canary 0x3c str x1, [sp, #56] Prologue (cont’d): writes the canary into the new frame 0x40 mov x1, #0 Prologue (cont’d): zeroes out x1 for the upcoming branch test 0x44 cbnz w0, 0x88 Important: if tracing is disabled (w0==0) skip the heavy path entirely The trace-enabled check happens after the prologue. This is wasteful when tracing is disabled, which is often the case in production. To optimize this: _nocheck__trace_vhost_commit() is now fully inlined in the .h file with the hot path.It checks trace_event_get_state() before calling into _simple_trace_vhost_commit(), which remains in .c. This avoids calling into the .c function altogether when the tracepoint is disabled, thereby skipping unnecessary prologue instructions. This results in better performance by removing redundant instructions in the tracing fast path. Signed-off-by: Tanish Desai Message-id: 20250528192528.3968-1-tanishdesai37@gmail.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/simple.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index a74d61fcd6..2688d4b64b 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -36,8 +36,17 @@ def generate_h_begin(events, group): def generate_h(event, group): - out(' _simple_%(api)s(%(args)s);', + event_id = 'TRACE_' + event.name.upper() + if "vcpu" in event.properties: + # already checked on the generic format code + cond = "true" + else: + cond = "trace_event_get_state(%s)" % event_id + out(' if (%(cond)s) {', + ' _simple_%(api)s(%(args)s);', + ' }', api=event.api(), + cond=cond, args=", ".join(event.args.names())) @@ -72,22 +81,10 @@ def generate_c(event, group): if len(event.args) == 0: sizestr = '0' - event_id = 'TRACE_' + event.name.upper() - if "vcpu" in event.properties: - # already checked on the generic format code - cond = "true" - else: - cond = "trace_event_get_state(%s)" % event_id - out('', - ' if (!%(cond)s) {', - ' return;', - ' }', - '', ' if (trace_record_start(&rec, %(event_obj)s.id, %(size_str)s)) {', ' return; /* Trace Buffer Full, Event Dropped ! */', ' }', - cond=cond, event_obj=event.api(event.QEMU_EVENT), size_str=sizestr) From 5e203f73c22786c73214d48a6d1abcb4ae9a3be3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:04 +0200 Subject: [PATCH 1356/2760] qapi: Tidy up run-together sentences in doc comments Fixes: a937b6aa739f (qapi: Reformat doc comments to conform to current conventions) Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-2-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/acpi.json | 2 +- qapi/block-core.json | 14 +++++++------- qapi/crypto.json | 18 +++++++++--------- qapi/machine.json | 4 ++-- qapi/migration.json | 18 +++++++++--------- 5 files changed, 28 insertions(+), 28 deletions(-) diff --git a/qapi/acpi.json b/qapi/acpi.json index 045dab6228..2d53b82365 100644 --- a/qapi/acpi.json +++ b/qapi/acpi.json @@ -80,7 +80,7 @@ ## # @ACPIOSTInfo: # -# OSPM Status Indication for a device For description of possible +# OSPM Status Indication for a device. For description of possible # values of @source and @status fields see "_OST (OSPM Status # Indication)" chapter of ACPI5.0 spec. # diff --git a/qapi/block-core.json b/qapi/block-core.json index b4115113d4..29d7c1c2c9 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2667,7 +2667,7 @@ # @iops-total-max: I/O operations burst # # @iops-total-max-length: length of the iops-total-max burst period, -# in seconds It must only be set if @iops-total-max is set as +# in seconds. It must only be set if @iops-total-max is set as # well. # # @iops-read: limit read operations per second @@ -2675,14 +2675,14 @@ # @iops-read-max: I/O operations read burst # # @iops-read-max-length: length of the iops-read-max burst period, in -# seconds It must only be set if @iops-read-max is set as well. +# seconds. It must only be set if @iops-read-max is set as well. # # @iops-write: limit write operations per second # # @iops-write-max: I/O operations write burst # # @iops-write-max-length: length of the iops-write-max burst period, -# in seconds It must only be set if @iops-write-max is set as +# in seconds. It must only be set if @iops-write-max is set as # well. # # @bps-total: limit total bytes per second @@ -2697,14 +2697,14 @@ # @bps-read-max: total bytes read burst # # @bps-read-max-length: length of the bps-read-max burst period, in -# seconds It must only be set if @bps-read-max is set as well. +# seconds. It must only be set if @bps-read-max is set as well. # # @bps-write: limit write bytes per second # # @bps-write-max: total bytes write burst # # @bps-write-max-length: length of the bps-write-max burst period, in -# seconds It must only be set if @bps-write-max is set as well. +# seconds. It must only be set if @bps-write-max is set as well. # # @iops-size: when limiting by iops max size of an I/O in bytes # @@ -5580,7 +5580,7 @@ # @x-blockdev-amend: # # Starts a job to amend format specific options of an existing open -# block device The job is automatically finalized, but a manual +# block device. The job is automatically finalized, but a manual # job-dismiss is required. # # @job-id: Identifier for the newly created job. @@ -5589,7 +5589,7 @@ # # @options: Options (driver specific) # -# @force: Allow unsafe operations, format specific For luks that +# @force: Allow unsafe operations, format specific. For luks that # allows erase of the last active keyslot (permanent loss of # data), and replacement of an active keyslot (possible loss of # data if IO error happens) diff --git a/qapi/crypto.json b/qapi/crypto.json index c9d967d782..fc7e294966 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -202,19 +202,19 @@ # # The options that apply to LUKS encryption format initialization # -# @cipher-alg: the cipher algorithm for data encryption Currently +# @cipher-alg: the cipher algorithm for data encryption. Currently # defaults to 'aes-256'. # -# @cipher-mode: the cipher mode for data encryption Currently defaults -# to 'xts' +# @cipher-mode: the cipher mode for data encryption. Currently +# defaults to 'xts' # -# @ivgen-alg: the initialization vector generator Currently defaults +# @ivgen-alg: the initialization vector generator. Currently defaults # to 'plain64' # -# @ivgen-hash-alg: the initialization vector generator hash Currently -# defaults to 'sha256' +# @ivgen-hash-alg: the initialization vector generator hash. +# Currently defaults to 'sha256' # -# @hash-alg: the master key hash algorithm Currently defaults to +# @hash-alg: the master key hash algorithm. Currently defaults to # 'sha256' # # @iter-time: number of milliseconds to spend in PBKDF passphrase @@ -370,11 +370,11 @@ # @new-secret: The ID of a QCryptoSecret object providing the password # to be written into added active keyslots # -# @old-secret: Optional (for deactivation only) If given will +# @old-secret: Optional (for deactivation only). If given will # deactivate all keyslots that match password located in # QCryptoSecret with this ID # -# @iter-time: Optional (for activation only) Number of milliseconds to +# @iter-time: Optional (for activation only). Number of milliseconds to # spend in PBKDF passphrase processing for the newly activated # keyslot. Currently defaults to 2000. # diff --git a/qapi/machine.json b/qapi/machine.json index 5373e1368c..0af2e1e0bb 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1160,7 +1160,7 @@ # # Information about the guest balloon device. # -# @actual: the logical size of the VM in bytes Formula used: +# @actual: the logical size of the VM in bytes. Formula used: # logical_vm_size = vm_ram_size - balloon_size # # Since: 0.14 @@ -1199,7 +1199,7 @@ # is equivalent to the @actual field return by the 'query-balloon' # command # -# @actual: the logical size of the VM in bytes Formula used: +# @actual: the logical size of the VM in bytes. Formula used: # logical_vm_size = vm_ram_size - balloon_size # # .. note:: This event is rate-limited. diff --git a/qapi/migration.json b/qapi/migration.json index 41826bde45..f5a6b35de4 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -760,9 +760,9 @@ # auto-converge detects that migration is not making progress. # The default value is 10. (Since 2.7) # -# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At -# the tail stage of throttling, the Guest is very sensitive to CPU -# percentage while the @cpu-throttle -increment is excessive +# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage. +# At the tail stage of throttling, the Guest is very sensitive to +# CPU percentage while the @cpu-throttle -increment is excessive # usually at tail stage. If this parameter is true, we will # compute the ideal CPU percentage used by the Guest, which may # exactly make the dirty rate match the dirty rate threshold. @@ -941,9 +941,9 @@ # auto-converge detects that migration is not making progress. # The default value is 10. (Since 2.7) # -# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At -# the tail stage of throttling, the Guest is very sensitive to CPU -# percentage while the @cpu-throttle -increment is excessive +# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage. +# At the tail stage of throttling, the Guest is very sensitive to +# CPU percentage while the @cpu-throttle -increment is excessive # usually at tail stage. If this parameter is true, we will # compute the ideal CPU percentage used by the Guest, which may # exactly make the dirty rate match the dirty rate threshold. @@ -1155,9 +1155,9 @@ # auto-converge detects that migration is not making progress. # (Since 2.7) # -# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage At -# the tail stage of throttling, the Guest is very sensitive to CPU -# percentage while the @cpu-throttle -increment is excessive +# @cpu-throttle-tailslow: Make CPU throttling slower at tail stage. +# At the tail stage of throttling, the Guest is very sensitive to +# CPU percentage while the @cpu-throttle -increment is excessive # usually at tail stage. If this parameter is true, we will # compute the ideal CPU percentage used by the Guest, which may # exactly make the dirty rate match the dirty rate threshold. From 69d68fb34e1d96f60addfd50c9c654eb8c0584e0 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:05 +0200 Subject: [PATCH 1357/2760] qapi: Tidy up whitespace in doc comments Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-3-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/block-core.json | 16 ++++++++-------- qapi/block-export.json | 4 ++-- qapi/char.json | 2 +- qapi/crypto.json | 3 ++- qapi/job.json | 8 ++++---- qapi/machine.json | 2 +- qapi/migration.json | 12 ++++++------ qapi/qom.json | 2 +- 8 files changed, 25 insertions(+), 24 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 29d7c1c2c9..13223df9b4 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -488,7 +488,7 @@ # # @active: true if the backend is active; typical cases for inactive backends # are on the migration source instance after migration completes and on the -# destination before it completes. (since: 10.0) +# destination before it completes. (since: 10.0) # # @encrypted: true if the backing device is encrypted # @@ -3030,10 +3030,10 @@ # state. Completing the job in any other state is an error. # # This is supported only for drive mirroring, where it also switches -# the device to write to the target path only. Note that drive +# the device to write to the target path only. Note that drive # mirroring includes drive-mirror, blockdev-mirror and block-commit # job (only in case of "active commit", when the node being commited -# is used by the guest). The ability to complete is signaled with a +# is used by the guest). The ability to complete is signaled with a # BLOCK_JOB_READY event. # # This command completes an active background block operation @@ -3068,10 +3068,10 @@ # # Deletes a job that is in the CONCLUDED state. This command only # needs to be run explicitly for jobs that don't have automatic -# dismiss enabled. In turn, automatic dismiss may be enabled only +# dismiss enabled. In turn, automatic dismiss may be enabled only # for jobs that have @auto-dismiss option, which are drive-backup, # blockdev-backup, drive-mirror, blockdev-mirror, block-commit and -# block-stream. @auto-dismiss is enabled by default for these +# block-stream. @auto-dismiss is enabled by default for these # jobs. # # This command will refuse to operate on any job that has not yet @@ -4737,7 +4737,7 @@ # @active: whether the block node should be activated (default: true). # Having inactive block nodes is useful primarily for migration because it # allows opening an image on the destination while the source is still -# holding locks for it. (Since 10.0) +# holding locks for it. (Since 10.0) # # @read-only: whether the block device should be read-only (default: # false). Note that some block drivers support only read-only @@ -4999,14 +4999,14 @@ ## # @blockdev-set-active: # -# Activate or inactivate a block device. Use this to manage the handover of +# Activate or inactivate a block device. Use this to manage the handover of # block devices on migration with qemu-storage-daemon. # # Activating a node automatically activates all of its child nodes first. # Inactivating a node automatically inactivates any of its child nodes that are # not in use by a still active node. # -# @node-name: Name of the graph node to activate or inactivate. By default, all +# @node-name: Name of the graph node to activate or inactivate. By default, all # nodes are affected by the operation. # # @active: true if the nodes should be active when the command returns success, diff --git a/qapi/block-export.json b/qapi/block-export.json index c783e01a53..04190b503c 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -373,9 +373,9 @@ # (since: 5.2) # # @allow-inactive: If true, the export allows the exported node to be inactive. -# If it is created for an inactive block node, the node remains inactive. If +# If it is created for an inactive block node, the node remains inactive. If # the export type doesn't support running on an inactive node, an error is -# returned. If false, inactive block nodes are automatically activated before +# returned. If false, inactive block nodes are automatically activated before # creating the export and trying to inactivate them later fails. # (since: 10.0; default: false) # diff --git a/qapi/char.json b/qapi/char.json index 447c10b91a..f79216e4d2 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -274,7 +274,7 @@ # @reconnect: For a client socket, if a socket is disconnected, then # attempt a reconnect after the given number of seconds. Setting # this to zero disables this function. The use of this member is -# deprecated, use @reconnect-ms instead. (default: 0) (Since: 2.2) +# deprecated, use @reconnect-ms instead. (default: 0) (Since: 2.2) # # @reconnect-ms: For a client socket, if a socket is disconnected, # then attempt a reconnect after the given number of milliseconds. diff --git a/qapi/crypto.json b/qapi/crypto.json index fc7e294966..9ec6301e18 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -55,7 +55,8 @@ # @sha512: SHA-512. (since 2.7) # # @ripemd160: RIPEMD-160. (since 2.7) -# @sm3: SM3. (since 9.2.0) +# +# @sm3: SM3. (since 9.2.0) # # Since: 2.6 ## diff --git a/qapi/job.json b/qapi/job.json index b03f80bc84..c53c96cce8 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -191,10 +191,10 @@ # state. Completing the job in any other state is an error. # # This is supported only for drive mirroring, where it also switches -# the device to write to the target path only. Note that drive +# the device to write to the target path only. Note that drive # mirroring includes drive-mirror, blockdev-mirror and block-commit # job (only in case of "active commit", when the node being commited -# is used by the guest). The ability to complete is signaled with a +# is used by the guest). The ability to complete is signaled with a # BLOCK_JOB_READY event. # # This command completes an active background block operation @@ -216,10 +216,10 @@ # # Deletes a job that is in the CONCLUDED state. This command only # needs to be run explicitly for jobs that don't have automatic -# dismiss enabled. In turn, automatic dismiss may be enabled only +# dismiss enabled. In turn, automatic dismiss may be enabled only # for jobs that have @auto-dismiss option, which are drive-backup, # blockdev-backup, drive-mirror, blockdev-mirror, block-commit and -# block-stream. @auto-dismiss is enabled by default for these +# block-stream. @auto-dismiss is enabled by default for these # jobs. # # This command will refuse to operate on any job that has not yet diff --git a/qapi/machine.json b/qapi/machine.json index 0af2e1e0bb..069e87d16a 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -694,7 +694,7 @@ # Structure of HMAT (Heterogeneous Memory Attribute Table) # # For more information about @HmatLBDataType, see chapter 5.2.27.4: -# Table 5-146: Field "Data Type" of ACPI 6.3 spec. +# Table 5-146: Field "Data Type" of ACPI 6.3 spec. # # @access-latency: access latency (nanoseconds) # diff --git a/qapi/migration.json b/qapi/migration.json index f5a6b35de4..7c7b09c341 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -842,9 +842,9 @@ # more CPU. Defaults to 1. (Since 5.0) # # @multifd-qatzip-level: Set the compression level to be used in live -# migration. The level is an integer between 1 and 9, where 1 means +# migration. The level is an integer between 1 and 9, where 1 means # the best compression speed, and 9 means the best compression -# ratio which will consume more CPU. Defaults to 1. (Since 9.2) +# ratio which will consume more CPU. Defaults to 1. (Since 9.2) # # @multifd-zstd-level: Set the compression level to be used in live # migration, the compression level is an integer between 0 and 20, @@ -1023,9 +1023,9 @@ # more CPU. Defaults to 1. (Since 5.0) # # @multifd-qatzip-level: Set the compression level to be used in live -# migration. The level is an integer between 1 and 9, where 1 means +# migration. The level is an integer between 1 and 9, where 1 means # the best compression speed, and 9 means the best compression -# ratio which will consume more CPU. Defaults to 1. (Since 9.2) +# ratio which will consume more CPU. Defaults to 1. (Since 9.2) # # @multifd-zstd-level: Set the compression level to be used in live # migration, the compression level is an integer between 0 and 20, @@ -1233,9 +1233,9 @@ # more CPU. Defaults to 1. (Since 5.0) # # @multifd-qatzip-level: Set the compression level to be used in live -# migration. The level is an integer between 1 and 9, where 1 means +# migration. The level is an integer between 1 and 9, where 1 means # the best compression speed, and 9 means the best compression -# ratio which will consume more CPU. Defaults to 1. (Since 9.2) +# ratio which will consume more CPU. Defaults to 1. (Since 9.2) # # @multifd-zstd-level: Set the compression level to be used in live # migration, the compression level is an integer between 0 and 20, diff --git a/qapi/qom.json b/qapi/qom.json index 45cd47508b..3e8debf78c 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -870,7 +870,7 @@ # information read from devices and switches in conjunction with # link characteristics read from PCIe Configuration space. # To get the full path latency from CPU to CXL attached DRAM -# CXL device: Add the latency from CPU to Generic Port (from +# CXL device: Add the latency from CPU to Generic Port (from # HMAT indexed via the node ID in this SRAT structure) to # that for CXL bus links, the latency across intermediate switches # and from the EP port to the actual memory. Bandwidth is more From 73aaba61a07c4bb4cd683a450a47504274ea1274 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:06 +0200 Subject: [PATCH 1358/2760] qapi: Move (since X.Y) to end of description By convention, we put (since X.Y) at the end of the description. Move the ones that somehow ended up in the middle of the description to the end. Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-4-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/block-core.json | 18 +++++++++--------- qapi/net.json | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 13223df9b4..0700bd3d46 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1322,8 +1322,8 @@ # @incremental: only copy data described by the dirty bitmap. # (since: 2.4) # -# @bitmap: only copy data described by the dirty bitmap. (since: 4.2) -# Behavior on completion is determined by the BitmapSyncMode. +# @bitmap: only copy data described by the dirty bitmap. Behavior on +# completion is determined by the BitmapSyncMode. (since: 4.2) # # Since: 1.3 ## @@ -3415,8 +3415,8 @@ # Driver specific block device options for LUKS. # # @key-secret: the ID of a QCryptoSecret object providing the -# decryption key (since 2.6). Mandatory except when doing a -# metadata-only probe of the image. +# decryption key. Mandatory except when doing a metadata-only +# probe of the image. (since 2.6) # # @header: block device holding a detached LUKS header. (since 9.0) # @@ -4724,11 +4724,11 @@ # # @driver: block driver name # -# @node-name: the node name of the new node (Since 2.0). This option -# is required on the top level of blockdev-add. Valid node names -# start with an alphabetic character and may contain only -# alphanumeric characters, '-', '.' and '_'. Their maximum length -# is 31 characters. +# @node-name: the node name of the new node. This option is required +# on the top level of blockdev-add. Valid node names start with +# an alphabetic character and may contain only alphanumeric +# characters, '-', '.' and '_'. Their maximum length is 31 +# characters. (Since 2.0) # # @discard: discard-related options (default: ignore) # diff --git a/qapi/net.json b/qapi/net.json index 310cc4fd19..e670efd6b0 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -150,9 +150,9 @@ # @domainname: guest-visible domain name of the virtual nameserver # (since 3.0) # -# @ipv6-prefix: IPv6 network prefix (default is fec0::) (since 2.6). -# The network prefix is given in the usual hexadecimal IPv6 -# address notation. +# @ipv6-prefix: IPv6 network prefix (default is fec0::). The network +# prefix is given in the usual hexadecimal IPv6 address notation. +# (since 2.6) # # @ipv6-prefixlen: IPv6 network prefix length (default is 64) (since # 2.6) From c1a6aa1d443e17e1902417ee2e531394eaf4c2d4 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:07 +0200 Subject: [PATCH 1359/2760] qapi: Avoid breaking lines within (since X.Y) Easier on the eyes and for grep. Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-5-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/block-core.json | 56 ++++++++++++++++++++++---------------------- qapi/introspect.json | 4 ++-- qapi/job.json | 12 +++++----- qapi/machine.json | 4 ++-- qapi/migration.json | 36 ++++++++++++++-------------- qapi/net.json | 12 +++++----- qapi/run-state.json | 4 ++-- qapi/ui.json | 4 ++-- 8 files changed, 66 insertions(+), 66 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 0700bd3d46..cc48fc7122 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -31,8 +31,8 @@ # @icount: Current instruction count. Appears when execution # record/replay is enabled. Used for "time-traveling" to match # the moment in the recorded execution with the snapshots. This -# counter may be obtained through @query-replay command (since -# 5.2) +# counter may be obtained through @query-replay command +# (since 5.2) # # Since: 1.3 ## @@ -510,11 +510,11 @@ # # @bps_max: total throughput limit during bursts, in bytes (Since 1.7) # -# @bps_rd_max: read throughput limit during bursts, in bytes (Since -# 1.7) +# @bps_rd_max: read throughput limit during bursts, in bytes +# (Since 1.7) # -# @bps_wr_max: write throughput limit during bursts, in bytes (Since -# 1.7) +# @bps_wr_max: write throughput limit during bursts, in bytes +# (Since 1.7) # # @iops_max: total I/O operations per second during bursts, in bytes # (Since 1.7) @@ -951,11 +951,11 @@ # @unmap_operations: The number of unmap operations performed by the # device (Since 4.2) # -# @rd_total_time_ns: Total time spent on reads in nanoseconds (since -# 0.15). +# @rd_total_time_ns: Total time spent on reads in nanoseconds +# (since 0.15) # -# @wr_total_time_ns: Total time spent on writes in nanoseconds (since -# 0.15). +# @wr_total_time_ns: Total time spent on writes in nanoseconds +# (since 0.15) # # @zone_append_total_time_ns: Total time spent on zone append writes # in nanoseconds (since 8.1) @@ -1502,15 +1502,15 @@ # # @device: the name of the device to take a snapshot of. # -# @node-name: graph node name to generate the snapshot from (Since -# 2.0) +# @node-name: graph node name to generate the snapshot from +# (Since 2.0) # # @snapshot-file: the target of the new overlay image. If the file # exists, or if it is a device, the overlay will be created in the # existing file/device. Otherwise, a new file will be created. # -# @snapshot-node-name: the graph node name of the new image (Since -# 2.0) +# @snapshot-node-name: the graph node name of the new image +# (Since 2.0) # # @format: the format of the overlay image, default is 'qcow2'. # @@ -1785,8 +1785,8 @@ # If top == base, that is an error. If top has no overlays on top of # it, or if it is in use by a writer, the job will not be completed by # itself. The user needs to complete the job with the -# block-job-complete command after getting the ready event. (Since -# 2.0) +# block-job-complete command after getting the ready event. +# (Since 2.0) # # If the base image is smaller than top, then the base image will be # resized to be the same size as top. If top is smaller than the base @@ -2169,8 +2169,8 @@ # @format: the format of the new destination, default is to probe if # @mode is 'existing', else the format of the source # -# @node-name: the new block driver state node name in the graph (Since -# 2.1) +# @node-name: the new block driver state node name in the graph +# (Since 2.1) # # @replaces: with sync=full graph node name to be replaced by the new # image when a whole image copy is done. This can be used to @@ -2593,11 +2593,11 @@ # # @bps_max: total throughput limit during bursts, in bytes (Since 1.7) # -# @bps_rd_max: read throughput limit during bursts, in bytes (Since -# 1.7) +# @bps_rd_max: read throughput limit during bursts, in bytes +# (Since 1.7) # -# @bps_wr_max: write throughput limit during bursts, in bytes (Since -# 1.7) +# @bps_wr_max: write throughput limit during bursts, in bytes +# (Since 1.7) # # @iops_max: total I/O operations per second during bursts, in bytes # (Since 1.7) @@ -3655,8 +3655,8 @@ # this feature. (since 2.5) # # @encrypt: Image decryption options. Mandatory for encrypted images, -# except when doing a metadata-only probe of the image. (since -# 2.10) +# except when doing a metadata-only probe of the image. +# (since 2.10) # # @data-file: reference to or definition of the external data file. # This may only be specified for images that require an external @@ -4326,8 +4326,8 @@ # @user: Ceph id name. # # @auth-client-required: Acceptable authentication modes. This maps -# to Ceph configuration option "auth_client_required". (Since -# 3.0) +# to Ceph configuration option "auth_client_required". +# (Since 3.0) # # @key-secret: ID of a QCryptoSecret object providing a key for cephx # authentication. This maps to Ceph configuration option "key". @@ -4581,8 +4581,8 @@ # error. During the first @reconnect-delay seconds, all requests # are paused and will be rerun on a successful reconnect. After # that time, any delayed requests and all future requests before a -# successful reconnect will immediately fail. Default 0 (Since -# 4.2) +# successful reconnect will immediately fail. Default 0 +# (Since 4.2) # # @open-timeout: In seconds. If zero, the nbd driver tries the # connection only once, and fails to open if the connection fails. diff --git a/qapi/introspect.json b/qapi/introspect.json index 01bb242947..95724ee2d2 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -154,8 +154,8 @@ # # Additional SchemaInfo members for meta-type 'enum'. # -# @members: the enum type's members, in no particular order (since -# 6.2). +# @members: the enum type's members, in no particular order. +# (since 6.2) # # @values: the enumeration type's member names, in no particular # order. Redundant with @members. Just for backward diff --git a/qapi/job.json b/qapi/job.json index c53c96cce8..9ddba537db 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -20,14 +20,14 @@ # # @create: image creation job type, see "blockdev-create" (since 3.0) # -# @amend: image options amend job type, see "x-blockdev-amend" (since -# 5.1) +# @amend: image options amend job type, see "x-blockdev-amend" +# (since 5.1) # -# @snapshot-load: snapshot load job type, see "snapshot-load" (since -# 6.0) +# @snapshot-load: snapshot load job type, see "snapshot-load" +# (since 6.0) # -# @snapshot-save: snapshot save job type, see "snapshot-save" (since -# 6.0) +# @snapshot-save: snapshot save job type, see "snapshot-save" +# (since 6.0) # # @snapshot-delete: snapshot delete job type, see "snapshot-delete" # (since 6.0) diff --git a/qapi/machine.json b/qapi/machine.json index 069e87d16a..5eb67fc4e9 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -182,8 +182,8 @@ # @default-cpu-type: default CPU model typename if none is requested # via the -cpu argument. (since 4.2) # -# @default-ram-id: the default ID of initial RAM memory backend (since -# 5.2) +# @default-ram-id: the default ID of initial RAM memory backend +# (since 5.2) # # @acpi: machine type supports ACPI (since 8.0) # diff --git a/qapi/migration.json b/qapi/migration.json index 7c7b09c341..7d1ec06605 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -57,8 +57,8 @@ # # @dirty-sync-missed-zero-copy: Number of times dirty RAM # synchronization could not avoid copying dirty pages. This is -# between 0 and @dirty-sync-count * @multifd-channels. (since -# 7.1) +# between 0 and @dirty-sync-count * @multifd-channels. +# (since 7.1) # # Since: 0.14 ## @@ -137,16 +137,16 @@ # # @active: in the process of doing migration. # -# @postcopy-active: like active, but now in postcopy mode. (since -# 2.5) +# @postcopy-active: like active, but now in postcopy mode. +# (since 2.5) # # @postcopy-paused: during postcopy but paused. (since 3.0) # # @postcopy-recover-setup: setup phase for a postcopy recovery # process, preparing for a recovery phase to start. (since 9.1) # -# @postcopy-recover: trying to recover from a paused postcopy. (since -# 3.0) +# @postcopy-recover: trying to recover from a paused postcopy. +# (since 3.0) # # @completed: migration is finished. # @@ -422,8 +422,8 @@ # for precopy. (since 2.10) # # @pause-before-switchover: Pause outgoing migration before -# serialising device state and before disabling block IO (since -# 2.11) +# serialising device state and before disabling block IO +# (since 2.11) # # @multifd: Use more than one fd for migration (since 4.0) # @@ -697,8 +697,8 @@ # @alias: An alias name for migration (for example the bitmap name on # the opposite site). # -# @transform: Allows the modification of the migrated bitmap. (since -# 6.0) +# @transform: Allows the modification of the migrated bitmap. +# (since 6.0) # # Since: 5.2 ## @@ -770,8 +770,8 @@ # specified by @cpu-throttle-increment and the one generated by # ideal CPU percentage. Therefore, it is compatible to # traditional throttling, meanwhile the throttle increment won't -# be excessive at tail stage. The default value is false. (Since -# 5.1) +# be excessive at tail stage. The default value is false. +# (Since 5.1) # # @tls-creds: ID of the 'tls-creds' object that provides credentials # for establishing a TLS connection over the migration data @@ -951,8 +951,8 @@ # specified by @cpu-throttle-increment and the one generated by # ideal CPU percentage. Therefore, it is compatible to # traditional throttling, meanwhile the throttle increment won't -# be excessive at tail stage. The default value is false. (Since -# 5.1) +# be excessive at tail stage. The default value is false. +# (Since 5.1) # # @tls-creds: ID of the 'tls-creds' object that provides credentials # for establishing a TLS connection over the migration data @@ -1148,8 +1148,8 @@ # percentage. The default value is 50. (Since 5.0) # # @cpu-throttle-initial: Initial percentage of time guest cpus are -# throttled when migration auto-converge is activated. (Since -# 2.7) +# throttled when migration auto-converge is activated. +# (Since 2.7) # # @cpu-throttle-increment: throttle percentage increase each time # auto-converge detects that migration is not making progress. @@ -1165,8 +1165,8 @@ # specified by @cpu-throttle-increment and the one generated by # ideal CPU percentage. Therefore, it is compatible to # traditional throttling, meanwhile the throttle increment won't -# be excessive at tail stage. The default value is false. (Since -# 5.1) +# be excessive at tail stage. The default value is false. +# (Since 5.1) # # @tls-creds: ID of the 'tls-creds' object that provides credentials # for establishing a TLS connection over the migration data diff --git a/qapi/net.json b/qapi/net.json index e670efd6b0..97ea183981 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -154,8 +154,8 @@ # prefix is given in the usual hexadecimal IPv6 address notation. # (since 2.6) # -# @ipv6-prefixlen: IPv6 network prefix length (default is 64) (since -# 2.6) +# @ipv6-prefixlen: IPv6 network prefix length (default is 64) +# (since 2.6) # # @ipv6-host: guest-visible IPv6 address of the host (since 2.6) # @@ -387,8 +387,8 @@ # # @hubid: hub identifier number # -# @netdev: used to connect hub to a netdev instead of a device (since -# 2.12) +# @netdev: used to connect hub to a netdev instead of a device +# (since 2.12) # # Since: 1.2 ## @@ -510,8 +510,8 @@ # @queues: number of queues to be created for multiqueue vhost-vdpa # (default: 1) # -# @x-svq: Start device with (experimental) shadow virtqueue. (Since -# 7.1) (default: false) +# @x-svq: Start device with (experimental) shadow virtqueue. +# (Since 7.1) (default: false) # # Features: # diff --git a/qapi/run-state.json b/qapi/run-state.json index ee11adc508..ebfeb669ab 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -365,8 +365,8 @@ # @shutdown: Shutdown the VM and exit, according to the shutdown # action # -# @exit-failure: Shutdown the VM and exit with nonzero status (since -# 7.1) +# @exit-failure: Shutdown the VM and exit with nonzero status +# (since 7.1) # # Since: 6.0 ## diff --git a/qapi/ui.json b/qapi/ui.json index 3d0c853c9a..7cfedb3914 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -175,8 +175,8 @@ # @filename: the path of a new file to store the image # # @device: ID of the display device that should be dumped. If this -# parameter is missing, the primary display will be used. (Since -# 2.12) +# parameter is missing, the primary display will be used. +# (Since 2.12) # # @head: head to use in case the device supports multiple heads. If # this parameter is missing, head #0 will be used. Also note that From 2e2309b6be074a2087afc3aedabcee722fcd512f Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:08 +0200 Subject: [PATCH 1360/2760] qapi: Drop a problematic (Since: 2.11) from query-hotpluggable-cpus There is a (Since: 2.11) in a query-hotpluggable-cpus example. Versioning information ought to be in the command description, not examples. The command description is basically empty (there is a TODO about it). What exactly didn't work before 2.11 is not quite clear from the documentation. The example was added in commit 4dc3b151882 (s390x: implement query-hotpluggable-cpus), which suggests the command failed for the s390x target until then. This was almost eight years ago, and I doubt anyone still cares about this detail. Simply delete the problematic (Since: 2.11). Cc: David Hildenbrand Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-6-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/machine.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/machine.json b/qapi/machine.json index 5eb67fc4e9..230b9b20dd 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1089,7 +1089,7 @@ # :annotated: # # For s390x-virtio-ccw machine type started with -# ``-smp 1,maxcpus=2 -cpu qemu`` (Since: 2.11):: +# ``-smp 1,maxcpus=2 -cpu qemu``:: # # -> { "execute": "query-hotpluggable-cpus" } # <- {"return": [ From 6263225492bad6d0f3a4feea13cd949b977fb3c3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:09 +0200 Subject: [PATCH 1361/2760] qapi: Correct spelling of QEMU in doc comments Improve awkward phrasing in migrate-incoming While there. Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-7-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/audio.json | 8 ++++---- qapi/block-core.json | 10 +++++----- qapi/block-export.json | 2 +- qapi/char.json | 6 +++--- qapi/introspect.json | 4 ++-- qapi/migration.json | 8 ++++---- qapi/run-state.json | 6 +++--- qapi/transaction.json | 2 +- qapi/uefi.json | 2 +- qapi/ui.json | 4 ++-- 10 files changed, 26 insertions(+), 26 deletions(-) diff --git a/qapi/audio.json b/qapi/audio.json index 8de4430578..16de231f6d 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -309,9 +309,9 @@ # # @name: name of the sink/source to use # -# @stream-name: name of the PulseAudio stream created by qemu. Can be +# @stream-name: name of the PulseAudio stream created by QEMU. Can be # used to identify the stream in PulseAudio when you create -# multiple PulseAudio devices or run multiple qemu instances +# multiple PulseAudio devices or run multiple QEMU instances # (default: audiodev's id, since 4.2) # # @latency: latency you want PulseAudio to achieve in microseconds @@ -353,9 +353,9 @@ # # @name: name of the sink/source to use # -# @stream-name: name of the PipeWire stream created by qemu. Can be +# @stream-name: name of the PipeWire stream created by QEMU. Can be # used to identify the stream in PipeWire when you create multiple -# PipeWire devices or run multiple qemu instances (default: +# PipeWire devices or run multiple QEMU instances (default: # audiodev's id) # # @latency: latency you want PipeWire to achieve in microseconds diff --git a/qapi/block-core.json b/qapi/block-core.json index cc48fc7122..b6447e847e 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2030,7 +2030,7 @@ # # @id: Block graph node identifier. This @id is generated only for # x-debug-query-block-graph and does not relate to any other -# identifiers in Qemu. +# identifiers in QEMU. # # @type: Type of graph node. Can be one of block-backend, block-job # or block-driver-state. @@ -2794,7 +2794,7 @@ # The node that receives the data is called the top image, can be # located in any part of the chain (but always above the base image; # see below) and can be specified using its device or node name. -# Earlier qemu versions only allowed 'device' to name the top level +# Earlier QEMU versions only allowed 'device' to name the top level # node; presence of the 'base-node' parameter during introspection can # be used as a witness of the enhanced semantics of 'device'. # @@ -3196,7 +3196,7 @@ # # Selects the AIO backend to handle I/O requests # -# @threads: Use qemu's thread pool +# @threads: Use QEMU's thread pool # # @native: Use native AIO backend (only Linux and Windows) # @@ -5157,10 +5157,10 @@ ## # @BlockdevQcow2Version: # -# @v2: The original QCOW2 format as introduced in qemu 0.10 (version +# @v2: The original QCOW2 format as introduced in QEMU 0.10 (version # 2) # -# @v3: The extended QCOW2 format as introduced in qemu 1.1 (version 3) +# @v3: The extended QCOW2 format as introduced in QEMU 1.1 (version 3) # # Since: 2.12 ## diff --git a/qapi/block-export.json b/qapi/block-export.json index 04190b503c..ed4deb54db 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -169,7 +169,7 @@ # @growable: Whether writes beyond the EOF should grow the block node # accordingly. (default: false) # -# @allow-other: If this is off, only qemu's user is allowed access to +# @allow-other: If this is off, only QEMU's user is allowed access to # this export. That cannot be changed even with chmod or chown. # Enabling this option will allow other users access to the export # with the FUSE mount option "allow_other". Note that using diff --git a/qapi/char.json b/qapi/char.json index f79216e4d2..df6e325e2e 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -351,7 +351,7 @@ # Configuration info for stdio chardevs. # # @signal: Allow signals (such as SIGINT triggered by ^C) be delivered -# to qemu. Default: true. +# to QEMU. Default: true. # # Since: 1.5 ## @@ -443,7 +443,7 @@ ## # @ChardevQemuVDAgent: # -# Configuration info for qemu vdagent implementation. +# Configuration info for QEMU vdagent implementation. # # @mouse: enable/disable mouse, default is enabled. # @@ -656,7 +656,7 @@ ## # @ChardevQemuVDAgentWrapper: # -# @data: Configuration info for qemu vdagent implementation +# @data: Configuration info for QEMU vdagent implementation # # Since: 6.1 ## diff --git a/qapi/introspect.json b/qapi/introspect.json index 95724ee2d2..e9e0297282 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -26,9 +26,9 @@ # the QAPI schema. # # Furthermore, while we strive to keep the QMP wire format -# backwards-compatible across qemu versions, the introspection output +# backwards-compatible across QEMU versions, the introspection output # is not guaranteed to have the same stability. For example, one -# version of qemu may list an object member as an optional +# version of QEMU may list an object member as an optional # non-variant, while another lists the same member only through the # object's variants; or the type of a member may change from a generic # string into a specific enum or from one specific type into an diff --git a/qapi/migration.json b/qapi/migration.json index 7d1ec06605..84edcf81e4 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -415,7 +415,7 @@ # on secondary side, this process is called COarse-Grain LOck # Stepping (COLO) for Non-stop Service. (since 2.8) # -# @release-ram: if enabled, qemu will free the migrated ram pages on +# @release-ram: if enabled, QEMU will free the migrated ram pages on # the source during postcopy-ram migration. (since 2.9) # # @return-path: If enabled, migration will use the return path even @@ -1500,7 +1500,7 @@ ## # @x-colo-lost-heartbeat: # -# Tell qemu that heartbeat is lost, request it to do takeover +# Tell QEMU that heartbeat is lost, request it to do takeover # procedures. If this command is sent to the PVM, the Primary side # will exit COLO mode. If sent to the Secondary, the Secondary side # will run failover work, then takes over server operation to become @@ -1729,8 +1729,8 @@ ## # @migrate-incoming: # -# Start an incoming migration, the qemu must have been started with -# -incoming defer +# Start an incoming migration. QEMU must have been started with +# -incoming defer. # # @uri: The Uniform Resource Identifier identifying the source or # address to listen on diff --git a/qapi/run-state.json b/qapi/run-state.json index ebfeb669ab..fcc00c805b 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -135,19 +135,19 @@ ## # @SHUTDOWN: # -# Emitted when the virtual machine has shut down, indicating that qemu +# Emitted when the virtual machine has shut down, indicating that QEMU # is about to exit. # # @guest: If true, the shutdown was triggered by a guest request (such # as a guest-initiated ACPI shutdown request or other # hardware-specific action) rather than a host request (such as -# sending qemu a SIGINT). (since 2.10) +# sending QEMU a SIGINT). (since 2.10) # # @reason: The @ShutdownCause which resulted in the SHUTDOWN. # (since 4.0) # # .. note:: If the command-line option ``-no-shutdown`` has been -# specified, qemu will not exit, and a STOP event will eventually +# specified, QEMU will not exit, and a STOP event will eventually # follow the SHUTDOWN event. # # Since: 0.12 diff --git a/qapi/transaction.json b/qapi/transaction.json index 021e383496..5c3394919e 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -223,7 +223,7 @@ # exists, the request will be rejected. Only some image formats # support it, for example, qcow2, and rbd, # -# On failure, qemu will try delete the newly created internal snapshot +# On failure, QEMU will try delete the newly created internal snapshot # in the transaction. When an I/O error occurs during deletion, the # user needs to fix it later with qemu-img or other command. # diff --git a/qapi/uefi.json b/qapi/uefi.json index bdfcabe1df..6592183d6c 100644 --- a/qapi/uefi.json +++ b/qapi/uefi.json @@ -5,7 +5,7 @@ ## # = UEFI Variable Store # -# The qemu efi variable store implementation (hw/uefi/) uses this to +# The QEMU efi variable store implementation (hw/uefi/) uses this to # store non-volatile variables in json format on disk. # # This is an existing format already supported by (at least) two other diff --git a/qapi/ui.json b/qapi/ui.json index 7cfedb3914..514fa159b1 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1526,12 +1526,12 @@ # # Display (user interface) options. # -# @type: Which DisplayType qemu should use. +# @type: Which DisplayType QEMU should use. # # @full-screen: Start user interface in fullscreen mode # (default: off). # -# @window-close: Allow to quit qemu with window close button +# @window-close: Allow to quit QEMU with window close button # (default: on). # # @show-cursor: Force showing the mouse cursor (default: off). From 83691fa0698d658781a9ce81d4b8775d34ba6a8e Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:10 +0200 Subject: [PATCH 1362/2760] qapi: Fix capitalization in doc comments Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-8-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/block-core.json | 6 +++--- qapi/block.json | 2 +- qapi/cryptodev.json | 2 +- qapi/cxl.json | 2 +- qapi/machine.json | 2 +- qapi/misc-i386.json | 2 +- qapi/run-state.json | 2 +- qapi/transaction.json | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index b6447e847e..f0faca1054 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1337,7 +1337,7 @@ # bitmap when used for data copy operations. # # @on-success: The bitmap is only synced when the operation is -# successful. This is the behavior always used for 'INCREMENTAL' +# successful. This is the behavior always used for incremental # backups. # # @never: The bitmap is never synchronized with the operation, and is @@ -1589,7 +1589,7 @@ # # @bitmap-mode: Specifies the type of data the bitmap should contain # after the operation concludes. Must be present if a bitmap was -# provided, Must NOT be present otherwise. (Since 4.2) +# provided, must NOT be present otherwise. (Since 4.2) # # @compress: true to compress data, if the target format supports it. # (default: false) (since 2.8) @@ -1840,7 +1840,7 @@ # @speed: the maximum speed, in bytes per second # # @on-error: the action to take on an error. 'ignore' means that the -# request should be retried. (default: report; Since: 5.0) +# request should be retried. (default: report; since: 5.0) # # @filter-node-name: the node name that should be assigned to the # filter driver that the commit job inserts into the graph above diff --git a/qapi/block.json b/qapi/block.json index f5374bd86c..1490a1a17f 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -48,7 +48,7 @@ ## # @FloppyDriveType: # -# Type of Floppy drive to be emulated by the Floppy Disk Controller. +# Type of floppy drive to be emulated by the Floppy Disk Controller. # # @144: 1.44MB 3.5" drive # diff --git a/qapi/cryptodev.json b/qapi/cryptodev.json index 28b97eb3da..b13db26403 100644 --- a/qapi/cryptodev.json +++ b/qapi/cryptodev.json @@ -15,7 +15,7 @@ # # @sym: symmetric encryption # -# @asym: asymmetric Encryption +# @asym: asymmetric encryption # # Since: 8.0 ## diff --git a/qapi/cxl.json b/qapi/cxl.json index dd947d3bbc..8f2e9237b1 100644 --- a/qapi/cxl.json +++ b/qapi/cxl.json @@ -117,7 +117,7 @@ # @nibble-mask: Identifies one or more nibbles that the error affects # # @bank-group: Bank group of the memory event location, incorporating -# a number of Banks. +# a number of banks. # # @bank: Bank of the memory event location. A single bank is accessed # per read or write of the memory. diff --git a/qapi/machine.json b/qapi/machine.json index 230b9b20dd..0650b8de71 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -811,7 +811,7 @@ # # @policy: the write policy, none/write-back/write-through. # -# @line: the cache Line size in bytes. +# @line: the cache line size in bytes. # # Since: 5.0 ## diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json index 3b5346425a..5fefa0a484 100644 --- a/qapi/misc-i386.json +++ b/qapi/misc-i386.json @@ -195,7 +195,7 @@ # # @cbitpos: C-bit location in page table entry # -# @reduced-phys-bits: Number of physical Address bit reduction when +# @reduced-phys-bits: Number of physical address bit reduction when # SEV is enabled # # Since: 2.12 diff --git a/qapi/run-state.json b/qapi/run-state.json index fcc00c805b..fd09beb35c 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -62,7 +62,7 @@ ## # @ShutdownCause: # -# An enumeration of reasons for a Shutdown. +# An enumeration of reasons for a shutdown. # # @none: No shutdown request pending # diff --git a/qapi/transaction.json b/qapi/transaction.json index 5c3394919e..9d9e7af26c 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -21,7 +21,7 @@ ## # @ActionCompletionMode: # -# An enumeration of Transactional completion modes. +# An enumeration of transactional completion modes. # # @individual: Do not attempt to cancel any other Actions if any # Actions fail after the Transaction request succeeds. All From 188b31ad425c4122363a3af47343e0d3228687f8 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:11 +0200 Subject: [PATCH 1363/2760] qapi: Use proper markup instead of CAPS for emphasis in doc comments Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-9-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/block-core.json | 2 +- qapi/dump.json | 6 +++--- qapi/migration.json | 26 +++++++++++++------------- qapi/misc.json | 4 ++-- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index f0faca1054..7b0548dc2e 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1589,7 +1589,7 @@ # # @bitmap-mode: Specifies the type of data the bitmap should contain # after the operation concludes. Must be present if a bitmap was -# provided, must NOT be present otherwise. (Since 4.2) +# provided, must **not** be present otherwise. (Since 4.2) # # @compress: true to compress data, if the target format supports it. # (default: false) (since 2.8) diff --git a/qapi/dump.json b/qapi/dump.json index f2835c0b47..d0ba1f0596 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -54,9 +54,9 @@ # @paging: if true, do paging to get guest's memory mapping. This # allows using gdb to process the core file. # -# IMPORTANT: this option can make QEMU allocate several gigabytes -# of RAM. This can happen for a large guest, or a malicious guest -# pretending to be large. +# **Important**: this option can make QEMU allocate several +# gigabytes of RAM. This can happen for a large guest, or a +# malicious guest pretending to be large. # # Also, paging=true has the following limitations: # diff --git a/qapi/migration.json b/qapi/migration.json index 84edcf81e4..4963f6ca12 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -407,7 +407,7 @@ # @postcopy-ram: Start executing on the migration target before all of # RAM has been migrated, pulling the remaining pages along as # needed. The capacity must have the same setting on both source -# and target or migration will not even start. NOTE: If the +# and target or migration will not even start. **Note:** if the # migration fails during postcopy the VM will fail. (since 2.6) # # @x-colo: If enabled, migration will never end, and the state of the @@ -801,10 +801,10 @@ # (Since 2.8) # # @avail-switchover-bandwidth: to set the available bandwidth that -# migration can use during switchover phase. NOTE! This does not -# limit the bandwidth during switchover, but only for calculations -# when making decisions to switchover. By default, this value is -# zero, which means QEMU will estimate the bandwidth +# migration can use during switchover phase. **Note:** this does +# not limit the bandwidth during switchover, but only for +# calculations when making decisions to switchover. By default, +# this value is zero, which means QEMU will estimate the bandwidth # automatically. This can be set when the estimated value is not # accurate, while the user is able to guarantee such bandwidth is # available when switching over. When specified correctly, this @@ -982,10 +982,10 @@ # (Since 2.8) # # @avail-switchover-bandwidth: to set the available bandwidth that -# migration can use during switchover phase. NOTE! This does not -# limit the bandwidth during switchover, but only for calculations -# when making decisions to switchover. By default, this value is -# zero, which means QEMU will estimate the bandwidth +# migration can use during switchover phase. **Note:** this does +# not limit the bandwidth during switchover, but only for +# calculations when making decisions to switchover. By default, +# this value is zero, which means QEMU will estimate the bandwidth # automatically. This can be set when the estimated value is not # accurate, while the user is able to guarantee such bandwidth is # available when switching over. When specified correctly, this @@ -1192,10 +1192,10 @@ # (Since 2.8) # # @avail-switchover-bandwidth: to set the available bandwidth that -# migration can use during switchover phase. NOTE! This does not -# limit the bandwidth during switchover, but only for calculations -# when making decisions to switchover. By default, this value is -# zero, which means QEMU will estimate the bandwidth +# migration can use during switchover phase. **Note:** this does +# not limit the bandwidth during switchover, but only for +# calculations when making decisions to switchover. By default, +# this value is zero, which means QEMU will estimate the bandwidth # automatically. This can be set when the estimated value is not # accurate, while the user is able to guarantee such bandwidth is # available when switching over. When specified correctly, this diff --git a/qapi/misc.json b/qapi/misc.json index dcf9f7df5b..4b9e601cfa 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -222,8 +222,8 @@ # .. note:: This command only exists as a stop-gap. Its use is highly # discouraged. The semantics of this command are not guaranteed: # this means that command names, arguments and responses can change -# or be removed at ANY time. Applications that rely on long term -# stability guarantees should NOT use this command. +# or be removed at **any** time. Applications that rely on long +# term stability guarantees should **not** use this command. # # Known limitations: # From 51acba6fad54099dad5ba6d0ab2392d9b93ae284 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:12 +0200 Subject: [PATCH 1364/2760] qapi: Spell JSON null correctly in blockdev-reopen documentation The doc comment misspells JSON null as NULL. Fix that. Cc: Kevin Wolf Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-10-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/block-core.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 7b0548dc2e..f8f89ee2d7 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -4947,7 +4947,7 @@ # 3) A reference to a different node: the current child is replaced # with the specified one. # -# 4) NULL: the current child (if any) is detached. +# 4) null: the current child (if any) is detached. # # Options (1) and (2) are supported in all cases. Option (3) is # supported for @file and @backing, and option (4) for @backing only. From 5ca6400cadecc2556ad0852780896eb94bdf4018 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:13 +0200 Subject: [PATCH 1365/2760] qapi: Refer to job-FOO instead of deprecated block-job-FOO in docs We deprecated several block-job-FOO commands in commit b836bf2ab68 (qapi/block-core: deprecate some block-job- APIs). Update the doc comments to refer to their replacements instead. Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-11-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/block-core.json | 57 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index f8f89ee2d7..6e5b90d5df 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1606,16 +1606,16 @@ # copy-before-write jobs; defaults to break-guest-write. (Since 10.1) # # @auto-finalize: When false, this job will wait in a PENDING state -# after it has finished its work, waiting for @block-job-finalize -# before making any block graph changes. When true, this job will +# after it has finished its work, waiting for @job-finalize before +# making any block graph changes. When true, this job will # automatically perform its abort or commit actions. Defaults to # true. (Since 2.12) # # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits -# @block-job-dismiss. When true, this job will automatically -# disappear from the query list without user intervention. -# Defaults to true. (Since 2.12) +# @job-dismiss. When true, this job will automatically disappear +# from the query list without user intervention. Defaults to +# true. (Since 2.12) # # @filter-node-name: the node name that should be assigned to the # filter driver that the backup job inserts into the graph above @@ -1785,8 +1785,7 @@ # If top == base, that is an error. If top has no overlays on top of # it, or if it is in use by a writer, the job will not be completed by # itself. The user needs to complete the job with the -# block-job-complete command after getting the ready event. -# (Since 2.0) +# job-complete command after getting the ready event. (Since 2.0) # # If the base image is smaller than top, then the base image will be # resized to be the same size as top. If top is smaller than the base @@ -1848,16 +1847,16 @@ # autogenerated. (Since: 2.9) # # @auto-finalize: When false, this job will wait in a PENDING state -# after it has finished its work, waiting for @block-job-finalize -# before making any block graph changes. When true, this job will +# after it has finished its work, waiting for @job-finalize before +# making any block graph changes. When true, this job will # automatically perform its abort or commit actions. Defaults to # true. (Since 3.1) # # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits -# @block-job-dismiss. When true, this job will automatically -# disappear from the query list without user intervention. -# Defaults to true. (Since 3.1) +# @job-dismiss. When true, this job will automatically disappear +# from the query list without user intervention. Defaults to +# true. (Since 3.1) # # Features: # @@ -2212,16 +2211,16 @@ # 'background' (Since: 3.0) # # @auto-finalize: When false, this job will wait in a PENDING state -# after it has finished its work, waiting for @block-job-finalize -# before making any block graph changes. When true, this job will +# after it has finished its work, waiting for @job-finalize before +# making any block graph changes. When true, this job will # automatically perform its abort or commit actions. Defaults to # true. (Since 3.1) # # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits -# @block-job-dismiss. When true, this job will automatically -# disappear from the query list without user intervention. -# Defaults to true. (Since 3.1) +# @job-dismiss. When true, this job will automatically disappear +# from the query list without user intervention. Defaults to +# true. (Since 3.1) # # Since: 1.3 ## @@ -2531,16 +2530,16 @@ # 'background' (Since: 3.0) # # @auto-finalize: When false, this job will wait in a PENDING state -# after it has finished its work, waiting for @block-job-finalize -# before making any block graph changes. When true, this job will +# after it has finished its work, waiting for @job-finalize before +# making any block graph changes. When true, this job will # automatically perform its abort or commit actions. Defaults to # true. (Since 3.1) # # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits -# @block-job-dismiss. When true, this job will automatically -# disappear from the query list without user intervention. -# Defaults to true. (Since 3.1) +# @job-dismiss. When true, this job will automatically disappear +# from the query list without user intervention. Defaults to +# true. (Since 3.1) # # @target-is-zero: Assume the destination reads as all zeroes before # the mirror started. Setting this to true can speed up the @@ -2859,16 +2858,16 @@ # autogenerated. (Since: 6.0) # # @auto-finalize: When false, this job will wait in a PENDING state -# after it has finished its work, waiting for @block-job-finalize -# before making any block graph changes. When true, this job will +# after it has finished its work, waiting for @job-finalize before +# making any block graph changes. When true, this job will # automatically perform its abort or commit actions. Defaults to # true. (Since 3.1) # # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits -# @block-job-dismiss. When true, this job will automatically -# disappear from the query list without user intervention. -# Defaults to true. (Since 3.1) +# @job-dismiss. When true, this job will automatically disappear +# from the query list without user intervention. Defaults to +# true. (Since 3.1) # # Errors: # - If @device does not exist, DeviceNotFound. @@ -3077,7 +3076,7 @@ # This command will refuse to operate on any job that has not yet # reached its terminal state, JOB_STATUS_CONCLUDED. For jobs that # make use of the BLOCK_JOB_READY event, block-job-cancel or -# block-job-complete will still need to be used as appropriate. +# job-complete will still need to be used as appropriate. # # @id: The job identifier. # @@ -5866,7 +5865,7 @@ # @BLOCK_JOB_PENDING: # # Emitted when a block job is awaiting explicit authorization to -# finalize graph changes via @block-job-finalize. If this job is part +# finalize graph changes via @job-finalize. If this job is part # of a transaction, it will not emit this event until the transaction # has converged first. # From 901eb8b2d8a14a0378d1c59fe37733366d4da16c Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:14 +0200 Subject: [PATCH 1366/2760] qapi: Mention both job-cancel and block-job-cancel in doc comments Several doc comments mention block-job-cancel where the more generic job-cancel would also work. Adjust them to mention both. Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-12-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/block-core.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 6e5b90d5df..ad6de151c8 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1894,7 +1894,7 @@ # The status of ongoing drive-backup operations can be checked with # query-block-jobs where the BlockJobInfo.type field has the value # 'backup'. The operation can be stopped before it has completed -# using the block-job-cancel command. +# using the job-cancel or block-job-cancel command. # # Features: # @@ -1925,7 +1925,7 @@ # The status of ongoing blockdev-backup operations can be checked with # query-block-jobs where the BlockJobInfo.type field has the value # 'backup'. The operation can be stopped before it has completed -# using the block-job-cancel command. +# using the job-cancel or block-job-cancel command. # # Errors: # - If @device is not a valid block device, DeviceNotFound @@ -2788,7 +2788,7 @@ # immediately once streaming has started. The status of ongoing block # streaming operations can be checked with query-block-jobs. The # operation can be stopped before it has completed using the -# block-job-cancel command. +# job-cancel or block-job-cancel command. # # The node that receives the data is called the top image, can be # located in any part of the chain (but always above the base image; @@ -3075,8 +3075,8 @@ # # This command will refuse to operate on any job that has not yet # reached its terminal state, JOB_STATUS_CONCLUDED. For jobs that -# make use of the BLOCK_JOB_READY event, block-job-cancel or -# job-complete will still need to be used as appropriate. +# make use of the BLOCK_JOB_READY event, job-cancel, block-job-cancel +# or job-complete will still need to be used as appropriate. # # @id: The job identifier. # From feeb08c260171e8a15236d20f2978c68aae4f569 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:15 +0200 Subject: [PATCH 1367/2760] qapi: Tidy up references to job state CONCLUDED When talking about the job state machine, we refer to the states like READY, ABORTING, CONCLUDED, and so forth. Except in two places, where we use JOB_STATUS_CONCLUDED. Replace by CONCLUDED for consistency. We should arguably use the JobStatus enum values instead. Left for another day. Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-13-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/block-core.json | 6 +++--- qapi/job.json | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index ad6de151c8..da390f85ac 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -3074,9 +3074,9 @@ # jobs. # # This command will refuse to operate on any job that has not yet -# reached its terminal state, JOB_STATUS_CONCLUDED. For jobs that -# make use of the BLOCK_JOB_READY event, job-cancel, block-job-cancel -# or job-complete will still need to be used as appropriate. +# reached its terminal state, CONCLUDED. For jobs that make use of +# the BLOCK_JOB_READY event, job-cancel, block-job-cancel or +# job-complete will still need to be used as appropriate. # # @id: The job identifier. # diff --git a/qapi/job.json b/qapi/job.json index 9ddba537db..441cd7772b 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -223,9 +223,9 @@ # jobs. # # This command will refuse to operate on any job that has not yet -# reached its terminal state, JOB_STATUS_CONCLUDED. For jobs that -# make use of JOB_READY event, job-cancel or job-complete will still -# need to be used as appropriate. +# reached its terminal state, CONCLUDED. For jobs that make use of +# the JOB_READY event, job-cancel or job-complete will still need to +# be used as appropriate. # # @id: The job identifier. # From 8fa2020647041e9f01bc308589bb7fa00355ac9b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 27 May 2025 09:39:16 +0200 Subject: [PATCH 1368/2760] qapi: Improve documentation around job state @concluded We use "the query list" in a few places. It's not entirely obvious what that means. It's actually the output of query-jobs or query-block-jobs. Documentation of @auto-dismiss talks about the job disappearing from the query list when it reaches state @concluded. This is less than precise. The job doesn't merely disappear from the query list, it disappears, period. Documentation of JobStatus @concluded explains "the job will remain in the query list until it is dismissed". Again less than precise. It remains in state @concluded until dismissed. Rephrase without use of "the query list" for clarity and precision. Signed-off-by: Markus Armbruster Message-ID: <20250527073916.1243024-14-armbru@redhat.com> Reviewed-by: Eric Blake --- qapi/block-core.json | 19 +++++++------------ qapi/job.json | 2 +- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index da390f85ac..1df6644f0d 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1417,8 +1417,8 @@ # @auto-finalize: Job will finalize itself when PENDING, moving to the # CONCLUDED state. (since 2.12) # -# @auto-dismiss: Job will dismiss itself when CONCLUDED, moving to the -# NULL state and disappearing from the query list. (since 2.12) +# @auto-dismiss: Job will dismiss itself when CONCLUDED, and +# disappear. (since 2.12) # # @error: Error information if the job did not complete successfully. # Not set if the job completed successfully. (since 2.12.1) @@ -1614,8 +1614,7 @@ # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits # @job-dismiss. When true, this job will automatically disappear -# from the query list without user intervention. Defaults to -# true. (Since 2.12) +# without user intervention. Defaults to true. (Since 2.12) # # @filter-node-name: the node name that should be assigned to the # filter driver that the backup job inserts into the graph above @@ -1855,8 +1854,7 @@ # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits # @job-dismiss. When true, this job will automatically disappear -# from the query list without user intervention. Defaults to -# true. (Since 3.1) +# without user intervention. Defaults to true. (Since 3.1) # # Features: # @@ -2219,8 +2217,7 @@ # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits # @job-dismiss. When true, this job will automatically disappear -# from the query list without user intervention. Defaults to -# true. (Since 3.1) +# without user intervention. Defaults to true. (Since 3.1) # # Since: 1.3 ## @@ -2538,8 +2535,7 @@ # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits # @job-dismiss. When true, this job will automatically disappear -# from the query list without user intervention. Defaults to -# true. (Since 3.1) +# without user intervention. Defaults to true. (Since 3.1) # # @target-is-zero: Assume the destination reads as all zeroes before # the mirror started. Setting this to true can speed up the @@ -2866,8 +2862,7 @@ # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits # @job-dismiss. When true, this job will automatically disappear -# from the query list without user intervention. Defaults to -# true. (Since 3.1) +# without user intervention. Defaults to true. (Since 3.1) # # Errors: # - If @device does not exist, DeviceNotFound. diff --git a/qapi/job.json b/qapi/job.json index 441cd7772b..126fa5ce60 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -74,7 +74,7 @@ # process. # # @concluded: The job has finished all work. If auto-dismiss was set -# to false, the job will remain in the query list until it is +# to false, the job will remain in this state until it is # dismissed via @job-dismiss. # # @null: The job is in the process of being dismantled. This state From b652d512855997ec89c78aa540aceadd5af13724 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 11 Dec 2024 18:14:19 +0100 Subject: [PATCH 1369/2760] rust: bindings: allow ptr_offset_with_cast This is produced by recent versions of bindgen: warning: use of `offset` with a `usize` casted to an `isize` --> /builds/bonzini/qemu/rust/target/debug/build/qemu_api-35cb647f4db404b8/out/bindings.inc.rs:39:21 | 39 | let byte = *(core::ptr::addr_of!((*this).storage) as *const u8).offset(byte_index as isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(core::ptr::addr_of!((*this).storage) as *const u8).add(byte_index)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast = note: `#[warn(clippy::ptr_offset_with_cast)]` on by default warning: use of `offset` with a `usize` casted to an `isize` --> /builds/bonzini/qemu/rust/target/debug/build/qemu_api-35cb647f4db404b8/out/bindings.inc.rs:68:13 | 68 | (core::ptr::addr_of_mut!((*this).storage) as *mut u8).offset(byte_index as isize); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(core::ptr::addr_of_mut!((*this).storage) as *mut u8).add(byte_index)` | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#ptr_offset_with_cast This seems to be new in bindgen 0.71.0, possibly related to bindgen commit 33006185b7878 ("Add raw_ref_macros feature", 2024-11-22). Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/bindings.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 3c1d297581..057de4b646 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -11,6 +11,7 @@ clippy::restriction, clippy::style, clippy::missing_const_for_fn, + clippy::ptr_offset_with_cast, clippy::useless_transmute, clippy::missing_safety_doc )] From 0074a471477d56723bd6fd044b78c30ff5958f56 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 29 Apr 2025 10:43:16 +0200 Subject: [PATCH 1370/2760] meson: update to version 1.8.1 This adds several improvements to Rust support, including native clippy and rustdoc targets, the "objects" keyword, and running doctests. Require it only when Rust support is requested, to avoid putting a strict requirement on all build platforms for the sake of an experimental feature. Signed-off-by: Paolo Bonzini --- configure | 8 ++++++++ python/scripts/vendor.py | 4 ++-- python/wheels/meson-1.5.0-py3-none-any.whl | Bin 959846 -> 0 bytes python/wheels/meson-1.8.1-py3-none-any.whl | Bin 0 -> 1013001 bytes pythondeps.toml | 2 +- tests/lcitool/mappings.yml | 6 +++++- 6 files changed, 16 insertions(+), 4 deletions(-) delete mode 100644 python/wheels/meson-1.5.0-py3-none-any.whl create mode 100644 python/wheels/meson-1.8.1-py3-none-any.whl diff --git a/configure b/configure index 2ce8d29fac..74b3865e51 100755 --- a/configure +++ b/configure @@ -1178,6 +1178,14 @@ fi ########################################## # detect rust triple +meson_version=$($meson --version) +if test "$rust" != disabled && ! version_ge "$meson_version" 1.8.1; then + if test "$rust" = enabled; then + error_exit "Rust support needs Meson 1.8.1 or newer" + fi + echo "Rust needs Meson 1.8.1, disabling" 2>&1 + rust=disabled +fi if test "$rust" != disabled && has "$rustc" && $rustc -vV > "${TMPDIR1}/${TMPB}.out"; then rust_host_triple=$(sed -n 's/^host: //p' "${TMPDIR1}/${TMPB}.out") else diff --git a/python/scripts/vendor.py b/python/scripts/vendor.py index 0405e910b4..b47db00743 100755 --- a/python/scripts/vendor.py +++ b/python/scripts/vendor.py @@ -41,8 +41,8 @@ def main() -> int: parser.parse_args() packages = { - "meson==1.5.0": - "52b34f4903b882df52ad0d533146d4b992c018ea77399f825579737672ae7b20", + "meson==1.8.1": + "374bbf71247e629475fc10b0bd2ef66fc418c2d8f4890572f74de0f97d0d42da", } vendor_dir = Path(__file__, "..", "..", "wheels").resolve() diff --git a/python/wheels/meson-1.5.0-py3-none-any.whl b/python/wheels/meson-1.5.0-py3-none-any.whl deleted file mode 100644 index c7edeb37ade47cb8b6275cc7dfe6681d957a1dee..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 959846 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 z zTILYr6(5GM92EkT5v-^p{R4ezqY2I&s4r)(A_lRMV<^~AXPSjPvkqXagNB5s7`Sok zky@4MJXVtsD~F&_3YuNO6KNNz-QR=1O#^Aa4FhfEjH8trY&{?|Dh%K3RWasisv8%^ zYk5>-jyT>8*D{GT_ZQjH!Tz8bls^w(d%4h!xgWZT7!fb-D>LQS5$Z?SpktMb?t6{! zYo(UgRi_lUXEmO3Ny7I(C;|i}@y@P;-qwq0T(B!# z2%|qS?Gf%Tr@r-i${`&Rms&@rGkmZr01xaq?24!f-ryx0l*YSI}S2RkG8Q>!6P@24;zT6TVJ1a6`dP-XqZd#@6Y#FnV z6Nfc9Z{VNQy3KCh0Q@XH3#%^m4n*3HbbuaM_;)JiVhv+Vc@Pl1$CVM3<%$xbNgw@9 zla}B^n=__0Ms7xyl+Lh?fRzF6k=>>N-r4ONMjR~EV2fIDAN)PiKFeI6=<<3{M{}iI zTvwf|%Ttf(@H_6qTgcw3w3_H5{fh+Ft6}&Cd1UV+VeMzfc-qbq>U|v_1pF`eIx<#> zPx_EENmCBrTYE5vEwrkFw=W41za^vul*?ZSGgGB*P*JAZ11FwMH9Mf2+nf+a@6&bP zwf~O^odARL?O(}fBAQS_mMFqX{}n~=BN>PW(>nMwb{ZRwnPh8Wp-nRaszL$SC-TBK zF*ce&+0%88v|X7+84;kq5NCTcZ{F#)nk&Yi3oIT4ihypGKK(fLX_0Fe5Y^r8$BG5L z>RZpJ^JN^o8rdY})Wkctg63;f*gVD4Jkr3(i9%upRTI@7wGu7itF#fa~ih$ ztGMBHo*!=i;Do#dqSWmB%b;$__Q_kgcvp&6^B(zu(V3s$l*IiO#BuR2*(L6&EAHE7 zkCjaudf5bd1m$iaX3sBoai_@V_R91NLZO@~`nA%FCTgKiTy<*f2}Y#-I6 zubbg7Ims!2n0HiMShJ9gUVTWITyB1JJ6{9EQXwavDy2Lsj3(?U(BQGi^=m=u!LG$t~PrfmejFi;~NM9r{#fPLr{89n%sOpwR-C#}d`Z*0-pHEKjo{q40XU%s60yu#>?y^g}= zRWUQ>O6G|-r@!zPcQ=W|=nilJ7}6@cVo7K7Y=!>;S{F~-H%{74eb+3F+YdTB;#F)w6H(hBA0_YGgVml2L|~7?U9WK8$|vbGw%#WAs>$0?Bivew zzzVhC>G}U+Jn?@p^Bl9d;mDdFB&jV$?req@fbiA! z2eZdI?>DAl-|J!}=D)rgu|}||IMb(5 z#2kkvw~^hBnAN4Bx>9H$g;KEEjgOoa5t&`29kQRrnh`1H-O`CZgh&k}!q(&xQneS( zyd(}nBOsFMk^$f&N52{KLNa(K3f;mN5hXGM{bRgVaiz}Ot~Mp%)Lbbv8$3E0AUVi8 z=sc0t#@aHc?l2Aj-O-#;e^SasOH^UHe2*=G{@EzZIqMtAKjR))p`o%|GO5umKd~ z$+V#ntsdpYJ^fgb74DL7w7M39Iw07)c zmWf3T0V=wrG-3A2q}kKWU>WhYNLXg7j`7P(FD}gWB7EYk7x=@Q-y00T1c6!i`H1HL$NFHNBUMv)+1a&`$vC(Xm=OL_ z5VU!ICzjcSD)vb~Ck{xc1!`Qc1bl4DCMax)MNJUPKia#=Owy1FHnYlE)6-maEKn1d z%e{WSZO!@+-McmRrlCa#(uYtmuJHdEL;Z4(fz+p%?@qU(I{IL+OX5P9*iavTur0Cp zI2&+-ZoWaOecGU)|2XLrB3hesP&OC#@KgSk6QZnFpv0hQWeSc|V2jvHRiD;8i1(%$ zOov(_3HXk|k^sZg3=kiux%=}PyrC^mbRW`3>g^nPYok&UL4Tctm>7kvlE5gh@uW|!3_gT^S&RJHo74g_6KXZ!VMG4! z2NU!A8^Y3ufp4iaLtTjjTFcwV4{jxsy;e=pKr}t)p@pTMaNoewNndW3-abX7C27BV zA;E!w0nHMr()Q-X$Pr!Fji5*`x-dfC;x#E~a#Imcct5S3Y6mGFX&f&?%eQGFdo{C4 zb`$P32XwZ6ha1$br}&P@@A;6l>gTl-OM3e@^WaU z)&39u`TG@&nc4msplXzif9SH^^S2=l?2PDEs+JLT4mA=yb|aANPB)httNr#>5MB(| zu0!W?iO)Ph`Dt_;Pi<=f)xPxJ5LHjL!+HGtZ`_K9gQ{zTtRYHdvm(?d*RsWwMWY?M zLyJHG7rkn2u=BqXm917URhDg91nmrWbQVqR`ak6LGVX=9;&M>kb5kKQR^!Be%wUk&0YMf-pA+Bxl@ z_*@H}0KBViYBUu+e}Xfd*dEA%mWm*cuL7f4uKT@cot;qT&oRB`#V- zEUY=CMv2j1J-|(QD8x$k$ghZ9OQJl^wgg;cIs`k>Jcri$m}S&O0jQ#vS3)^2OEjxw zBD_a|vV`<^wS0;Z0R5difuGCkISQP5*N;fd_~1)jxv((_?t0v^Rky*aF_O0BzR&&8 zT-t-XHt>=rsWOzMoQ5${{y1%qE8Lssvc9+5*+R&MI)8(e=TY7eLEn6IgfOi5Rso%C zz^YMok39-yFD$xlKzS3G$+FuNle_5%tr!f19dNI8jPfF`l#pPve~g!(W%-oLujLFY z{8;1*Se9B@ah!BKNnVGRgyz1sh|ab}pL+`NR*gW8guGVT+%M(Ozyd0#o+p!{o4QO8 zN>FJVW3{m-L(<@Zk=!EM%?mvRs9n-)cZ$Y^yBe+8s3h$;V%C)QK0^s*jCzz2O`eeU zdsZ`)&2Yp=cjIU!&n5d2-3~*^V(d0tS$SMo?;)dGdZA6{E}(KN6gAoO(Cjw?7_w%7 zpUGBDle>Q{XMMhQ7c@z~>IM}md)A=QIXuV7!MK@@5M}&w%96juT>9pP&3nC6m~>vf zIB?T`t3$Z!)W!q>!tL1b!pQs>AySxGO07|QZ394;^xPH)RDx^HB@5@o6z*JGgevzq zAfLL{pt|OR=uB*O2-SL%MvvIyp^jVwoWf`%G135|hUw!wllYZ+TF%@E!;Uu~NplPT zM8`}Pm|kV9IT}ZR@QIzuajpWg5Jb=I+ors|9G>mjcv^H(tZJ6(&Xo$qgX7{7vE1X; zA~gXv5Lb77|2446{EmOiOrWvHW!p|yCvyT9&U- z$EIAA41_KB=dupeESLA}3)#Bw9n?2M;cQMscx%PnvEC8Bw2I_o%`>JwjD+~Hl3vaH z*XZQA@GoJr422u7^Z*s1Ehlkqee@^=L&?1~Xbn(!LQEUW!Bugbl55(qS0g}~8fbcs zB<%qUi_yJ^MUn*)tyIt+OTxSNB1lq2G-2*P(177 zCxjjEHj0DkkR$BnBXVqTh0{ zhoRoAA@A<$e|NLsv2>q23E?HRA?@R`H5xzoqdKe3AXiE1S> zEG;H54pyb)?RnaY#WTTbVJJxeW`Ajl#z1f=ccRJ&(?*^ns!O}~W0JlUXFD;Mjz zHB2Za_us3NVdjiG_}I$XP}2;je7Bt{b*w9$EJgI#l%23ygs7V{nX>C3u;qs;+8D?qFB#u>O(`AX(^h zoN8b8-n4wwr&7hN0$UMYiftLPNay%9nK)9GMR=j69?|IcbDCuES9Bj|p^Bgn&P?4p zs*U3|XDK@LbO&mRn;%2ID9R>LG@V0;P0r+m5W$UdgCp;Wjg=pKIv@+g6<`h(ia;a~ z{J{>=R$;pLNmb@1xL~VxQ3bjcIGXiH{JEF()Vf-SzrW)zxW#gs zE!ZzL8w+8gZz1bMvG14ok(4X~!90ARYch@uxi1+YFMjLLHWP4_nnJ{5ytpc9$w*d> z;>SIxoxrGiNRwh)MuCxcxh0l*AP&81U%4rZ>s4@`%>)*4lU;_nZ==p%d!$0DTk*j7s7<{ zK7H;f-(0=$&m7BI=>lqCmW&`BZ6r4TNo2hj-Jh+IOk0>#9d^nQ*bOxr8Lk}fYKVq_9;vEmnIQvcXvYV=Ur1eqLTF*+jMR4>V`8sCdPq0@LER*g z@pG6>Ye8O#6J^G0p}O0;Cf95!#k!^$D}A#c z26Yka5h*OOVYgw{~%HiT%nnqTXUmhep>xV-s@vcS?H> zlm7{BmIR27_CRl5ebCSI&w$jIUXYz7r~%2&IpKkyjNz3RV7%EIe!|-wm|LA@oRndV zW@&7Klpzo$rb;YjJdwV>iXz(NAO7q9ZdV+;r?@ynKxYwKL1 zT?k-_Tlf3%YhRVao+W~+wb$t^(c8|pW1ZLh&9K0p*n+D$7jB%% zxKyY}w>bZa>z<$d`PIT%SJDAEQI!Gd;Kc(I3iR+^a(Af>jo9R3>#HNc@4YGig3Bhy zpy6j5*=Lzbf0sv$(-A0tyS0Q7x#}+D$^|vP^ygB7n-kqda8*(67 z8uNx9gmAFfkG_28VeX$kT1J?LbM;6tyr&%q=h^OOa z?Zn%GDc#aRHLuD1tHjWQc873fQ<5SWRCM|PcuSU)+RWBbP^O>-SXC@2ldG& zEx}S4;+zoM_Xap_kRM0toElht4_>(9P`DVIQE6w)dP}75*f}7gLXH{`pr)zZgGz<5 z%xh+6n3TBu7rF8j!?QIP`&tw={8fYdjV=8H4jJnz)6x)gVugx&Zs)NYEwuQAh&UDxh44Xs-)#=ksfWHI<izeg#ju;2*^-x+?t$Yf4~rBIKcl0-s7Lvce@ zA>);B+|i%tQa-sm+l{`Oh`%IWz{n&-!e_L$Nx0jwqQ!6S5Vcr6 z|MzZ(iG{}_7_SCUUjS1sRt4<*jnTwWo_Ecf|mNdd$>Xx?L@te?zA zim2<68McuafoYXgU2gDx!WxEo(mrh4s5tO}T`9R>L=w=k3!+))BGGU@JLZ?ISnX{q zE0amf>r!uFDbskv7Ax$>QFk-`)3w>fNlogyc3o$MQ4=obWc=zuN2tMGZq9m@Cn&{> zUK_{t0WRY;5^J?B&aCNc1|3>p#GO*OwAxu$wYhIv%Fa-sI9NMU$5FR0ZOIT=7DpVJ zY9*k8w=XbnI54`0+P(H7iP<=)Ns7jz7_jb9p+wC9O9U}zfA(Dz$@EftN^DGzkED7@ z39kjzh~`N-&nC9dTqQS%0}-?-4GdWrxA!>O5;JhW`3o;&Le-v|0v#K-@#F^O(;KQZ zHul_7%OfKp6hPo?pj)0r@FjmIKg*(AcuTgU#m$D3lLzPa%uAE+Q9)~UEy)Vd+gXYK zRyQCJ6#2aXv{r=X_;N7z5~=V?r>=9pm%J9`XjEU0iJgUKR3Zb-e0S5Dn%80xw=gTc zzp_BKHh+mSphFQErA>x@K$adX#|jvlx`Mu`L-5ljm|cx27_sQ;scVskYFCe#_VB%x zo)6)Wy}UUG*MYosLF3ITJ@7cHdEj8waI-JV%rDPiV&i1FThx!=DQLO}CV!?3KHSFJ z(TO_fbg`EU!EncZ*)VDAHyavZyCq=tm0~g@h4Bs@w4G(q#C&Is;4gdo3WV@>Hkg;%L)0 zJX^f29rz7d=zJm;oek^_rmyxzRLI?k=yayGjeBuyJ2@gtG7)5j{g*$lQ50M}4eFcY zD68QR${;tAX+3f=>~GW3FaMX1kN4;PU$eD?f;F+@MlolJ$UP#$=Z=~QgC`5kzrMI` zhJqD7~|HJbRB zSsxfr7u80yAe>}bpZgHCaa|1W(Fv?5mjIs(*^R=_=0)h-O}veJQpsTg#-ZQatp2-5eSxZHBf`T|HIYM7PZ9M+IcfY zua+%@CjzuzY3T8XzS7n)jx)B1yo8ZA^TaNy?Q779Pvusz4Q|E?qP*8k#|* zc-Hsz?nEN^Dt0)Su6ecrk(}2Z3#s==iJ;SadRP0Qeq(CRPuq(GlWs303dVQ7 z6d{v*R}Rf^D3pP4TLVOe?2bSI%PgxtJdCYo_{?{gEHh$-Avv^7T4u7LBLOYDQM0h2 zoKsON_I!fCg#1xCSogJz5}-pmUxMBKaX|$0y15LE0rQOT27zRhdET5~Zk_5@~E!n8@GMme_d5W6Eg6 zojJ%q@MuMrOc{P>U}3ojeRMOcZAQO`dTM@<|6lHI!cTsYgAfEn%^d^;{eKl7SlPQe zJNyrICt%}nAep%PMTeUKE^TZ!nxsRIH0p``AiL5wF-?511+FbpP?9>sB4I5v*$UYE z@q0Su%1Y#4-{d13m;NfR{hy=JrXT*FJo{cfi#rls$>HzOo$O@ND6yRzsgiGKPbu3y zvY=ub&N6SZnabMK_i=x|e<{1|6AG`=q47)9Q;90+PE%{r60=yCGWKy{dM+%#C}nzYtmsNga_D_KK5xB*_|yp3(Xun8>y*&3_iC`#c6xixfui4J z#7dj*smu&hxXi3M%|d%yYa1!VGDDnMg-~(Nr6e=i)gtTOGQ6`r{g7d9K@CNxNYCG1Ds5J*oZqQHFd2n}>9)B-22%QCsbB zpOnP4*&2eQ!FLMt<88Uo6|4O$oc?0=)$wa}WM5%rO4kS2*owBP5Q(QX)p~k#Z@R>p zM_DLo+Jl<>dk|A8@}+9#tY^;?YT$_ZGw;S@$Be@hW%UG=SN zGi%8X=V*ihW`~=LVgI8A*#RO0boQW^n}8B3IZPM5cAbcdV7=pjCE3>>@$T=V`?0o% zDh;8Z+iUCZFQ%JaT|GaKA1hLQx38>H{nWL8Qipi7ojxDnOI=oP-ZAtF)B75Gy#BVo z@nwvPFz4jL`e=ENes?6m*eDNfR8Ts&tuqpbRNqXsRyYNfXD%w7b%N9?Aqwe|NqtsS zq(ULe3oQkgzgkp`X_85SNmZTSv~et4`ZgsqH?%SU-VbIlQT812ywWca{RDYvH1=ux z-f!Y=jvc-quV?a@c7GiGNcR*SHl9O6^I36K7&$;1ZB|X5<}NO!&dnd|qI3-Yi19rToA&+Q9v? zARM>4AA<*Gf|UNivGQc(WX}VRbM8yE>O_E!$oQ^EK`CE8xqwD(`SP_7ok@EU_Q=0A zz>rx`6#cfsO#LGV;YZBXlxzhHwDPx-(8#k!-A6RkmZo(%-S8KA2C}_#bH5(^5U$n* zkqk9-vgoo6YA>6R@HDUWDc@rSBM-kR++|L!ln@}gnMzeQy; z6iyhupu;XbtSf?vXLy%)25s1rZTz_n9^2zaUgMCkB0ZZ@!=?sU@zQpP z+KL`>HWqXl?TKbqr*Cz2qwl1!?nGtML9*lVUJ;P4KkzQZBg|`BG?EV%SE5Ut0x}s^ z@tsy{5C7&3+Lk8G!6>d+B}h24Un}z!K7-Ps?E=w?s!mWZ0RTZ1F(d;Sa6J5l_1ls& z>|2Y|31nj#r(Yr>WHRG+V~?_{Jo4!YH67QzuB)Fgq|uOESKKZ35(i!fc9Whp8+`0? zRc_9C-ZWNfw%ew)9tI^?`0VT`Qy~MTS~Kp#AxghGijSsM&Q>zHEAHSdu+5g=X`P8U zX~j&c9#qeTW{Yzu;J>*=tWa>(;jk2_n`$euf%v>DH#U3yf!u%d_B8O#XeO~X>hm}K zWI+-54USxZWHN}_PQCZ+?|qgpl!K}oy0im1tIT#;0jpDaK1u8)DmPOxOaiaoipcjW zDJ*0&|0q@J1Yuugb#Iiyf#d- z_)EGQa5O*9C_bPf(+HWFLk}uLw^B#2* z&}ukYaB&D0PqxivVn_O)Z~g}#;=dSk`HuK)321W+X+k#$>nyA^K@~B|)+9piiiNI( z=&HitKY>W3HLQb!MGsDtiG7EtJUd>E zCGt)$JbN*g)>VcXkIgdbUT6zqGvO*FdKe=1%et0<#=5eX5EHze3KoIym&~D2vnV;b zt@m~`?$PV;|1D_llK!4K+ThP<&z9i@3KF_+?5s{YL>}bDy%!|c#9DJ@$!WY*d6mfl z^zqrTe8v(JsbO$@^*kFi$K~@Xk29+Ft5|;#5$w&D$JoH1jPj)zIMZz8f#d6yfkpN) zDQ+~n)Fd_rJ(M6v+ibMcf6Gnj<_Me+3W1zj?U^(Pgr8nIo;|4PRhxx|o*F>mbHIU) z_t6qH@G-N1F3K;&h5k}OZ zo87sFDj#AHm~V^QT0Y2&d4T2;{G>U97|QmifI!C$6_h*?aEE)b*LVL=F=$Z{1A!oj zD`+e&!je?FGFqBzn7Ty(v$eP7dnE-Ur<3k z9MUDNxhg2ro?geJPl1*xDQKzoUseV|DeeEI6G3ZME>Y+*G=aJb(#{=J+Vv&z+pq%| zua6^6(FiIFh1s#JsK9`X>0g*rWXnEYcz0zW7j7h;;fIRm;MI3mh-hSB z`tIfqVuHat@jRr4z8mU1g4=*OgR1P>TwSkpBq9((%7yION%(a}!Kdv3i=5dkou&HI z2>uUO=hP#L5~$g>ZM%E7ZQHhO+qP}nwr$(CZO*xwhsoTeQcv{*DxX#hedL%wml4K@ zYqAXj2c$aLgTa1`Kh)an)@bTmH!K^DiS-mkE1&03gFA2g&geOh{`P?Vql1D?nqzHX z?WFN=U6`U)SKSG%+Fya1gJM%d= z3f~;B^sD!)X;NaroDQp%94{3#)Ni4SSn$&Nc^+BOE*th-c%cTJjRf*?gN3UI^!M3N zM!&?@-j$h9E9z?NU_2R;zf{5xKEzKl3nFD#u-s;%@w_* z_GP${0*V=~qsRP_xD^@bk;_>fl@ZSjW)?TB-%~FvT13$u!*F+Q3;|)4upaJ~1ZQFS2EGiz zdU8#P*-G24jrf0D`>eeIXq*F9Nc({eIc&L} z^i;bdCp|z>;x;0GC*@th(>>ii`te3K7hFNZfc<@Q3ty`H7^9FW*oWVR~+)_lqxdEz9bhX-O`bA>TD_k#Rr%JKbpiZ_&3%DjA!EVgc1YQ~)S#WKukMuuL zg<`S`MKvZJp4sfASxLN?pp?s7j}%}FAFUyB`>xb_N#6*#jn&gE4S63 z#zz(+Z`Ciuhbr-E*JHot-HIhXFUUVBh*^U*xe?r{z{NTreqze_UnYX!f8jW&0|e%Z zH0dGd>o z*B{8KC4tfx9`br$lM~V>U;iPh|1Uw@m*%vq4%RQEQ6v=Gv~K9`32SKniSTN05hO^` zlZcF7iM`$&A?D$>WnEPg+g-e&4ioA_t|u%@`?x&dz$~(C7S{SSUN4$H zf3DQZV-U!x??0qPnmc7y&YAC2lU8l-t{HMC7bTl$_|+Rw2EVr(r5nQesE%}E=RC52 z{u%{HFt=iYW0hkBz3*vW#Z+Ibf{kRNBxavKaW-q6&WeRGB)euE4D{tw-su((Z)bK| zYVs#~2p|al6Z>_bUN!Wu`3n|;M}-w--0|d>^nstofbIO4$T?R>r#G*!=iB4=fY@U; zy^cJA>->Ghfgs6Nk%dhs5tc^f>CE~3)1<9pl6N9kaN7S7vTjOFYqNe{fVP7!@eFDO z!m5S6oTuqu(!-s}f|dFL|(SCvKnaNnF;Ni}_j{dqqVYerW>AXet`%!Vzx&L{@P z9-ArB&V)|bcI4HGE^h|bY9>2+W~q7F9GPXBSgJGFnD&_A+^9^b1q%v%sKN$e(b9vo zqXEBYxnOdS)NF9L2r;BBJx+3-uhc*sZhIb(nxFetJIMX zUaovMYUzQYDAO%ROra8*tK$nbs1W9!C`renbs)~XWD1x?rgsNd81PYfk`RW#44Gag z51@R`$(LKemIQ1ww!Y;lA4pB{-Z=y^iWDWEPV)I8PnHFRN!S{mlkfnH>Cd?~8o^0+ zta&zvL_yzw81!L-Kx>1#VE!E?)Xe?0;iWdzr25_t%Bz$6Q3O!M784uztN|0nt4e-c zXy=!{gPuR$pG0=DESx$BR1$F+c368A(6`anz z^!upwiD1l?@x5%Q>dt5X{}7XqmENXWWfDjJ(#)}|y3mg3GzvG)AkN8%zf>BYkOgBiDGn?)KSt z{ZA0q3~wDB4{?!yD>oe2`1AA98hn)8AUb`_zw8F zS-+n$iQiu2zxmNDxZ9phpd#izC!pQ=Bd!5t!XH}(k|>Hc>Sh5HfY|i7$MHtrDF-S&-mxc zk}<7kalGNP5YMa2S2r_UP=|@XDcY-VA}WtQ zX0aZ=1Ao}jP5+Q5NorN)c@ zI(;KZG`hgzzQvov=khVXjg#`9vC7d{@FDbfr^;);`J9h+_ui(Qo$1q7|og~L( z_Afa)U>imn(DzsAwvAdHMF2LpZU)p#J9i-~kDM?=!91S3E^<<8&9%lGUTktiJi$RV zc%=>XJvZsKY&}=N(0&TMDK)HV&l&Vd-*`ZgMRt&i(@*$e@pd9*bBg>N!rd6rC)=IU z1oV8rg=d?xBfZBl)4YCA1v;tO9)HgWd7p;WdA|%>MLdM7t?~{)mqR|w4l;R(V8gAx zpkCS~7U>+f2`*>4E>B@lWO!OUHh*)(9)g?=(l}nsXZ<_@C=$ogIfJs~?TCAuj#cy; zBv+XZ_bP?*LJ`*+xN&J7?i1c*E4Y1`Ht-%=IOBBDpe^03R@PlpZfP((x4&@FT3H_O z$)sMzfjVXF3u2WQG#ezBZR~YJDREozeUVW?Zsvt$GgZuJG z&(inEgEtMKV{*h|Q?0Vog?0u%T0yog==GrKq#4Ilk<07Rbbdmm30h%Dz!s{4H^Un+j3AiWmygb9a7xuM)USmAZsVfrh%l!N4~Zj?5gZlIpTwS z^lNH>Y-%rlID8xkQu?byV8!UbfXDB7@D2vYF)$kK4)rWHF}LwtLq~fGlyN4@xPo8O zWy%6Yag(>K=Jz!=IJ51{t?2T+Tv4pYp=V9B>{G7>zZvHH_Vv|y)LliCR>Ha?!EcG1 zxGL$^9rZ%Fo8BB$_p4)lVNDG^tp2^nEEKBZYd(u(hs%TQ?|Ze0LF%gI95USpX*!@! zVMe<-&Ek~}g~I~Sh3}i}(ZW3j+8EOOvNz->`aC-Hu1^qCQoR$sY558;^h<}qC5_uL zsU@oA>ma8R=o!y;fnCh}Cduh}12+dzG9r_ z@qr;i+M#y+vy=->_u~jyfV(w1!tw5HM$gA7n`C}W%um`kQxK-PVgSdjy5W*ukX;#% zbsCmtUqwf@5dB2YB%A-VTx4Wl#DS0|gD;$!d%&(4=T%C-+JokN`DiZY~^c_Se`x4!j zn3$8E97C$R+dDs-%2`W=3Xn@xQw)HyA6<)e@_sE}3YVTUT}lg8MgNYA1o@2Q81)2i zs9nVB6rcoW&NH0PRR;~l7z5E99aBT$ZW<+hP0`FeCJRMQ)Z zJR{ud9rE_w{oW>c^2nR??!GBn;k4yLbThekzB|Vm)oo@4H@ddFpKm#J#YxipLv^;2*=`$F~j=e$nBULGI7GYbi z2S2m}kmGw7TP$huajsxM7K4~734=#No-?g?)-3x-!r22Y!;->3=pH;%dR9ialIyq2 z6rEZ9i$P!WlNEc^Co<`DjD_IS7@>Cxc4=oL}Dcdin-U zefPmTld1SP>dakb5Rj0j-pBowe>+usjqNFCa``t6RB93PJs(*4=vPWO6IiD9?!k?K z-_i02?YaV*X46%ZgcA1vSg``hngSnDYJ6cia;i8PWJFx1d@@dy0^70RoL9HQL)Z z2~R_MgZDJd@U#A7(N7vLrR`=5jj&VQ@L%M!s=rti0PWI|UDI7a9pQ;ccW0K@GH*z- z+jMdbubHv5rjQfG!E?TLrL+cN!#;$&8TkB;{|TUU@?1EvRUo#pC-k~>s{Rb7j(4+&Z(BZ{(K{rd0^-Dx$PMgBJ%CIN5% zo)sefD1J#@(G71S(PtbGB^4|D_0X0+ePDN~&XWpf((i7Vd+j9ZJR>~lb zB$KID%FwW_KJECsB`(9ZuZ>Dc{z2V!tIJi-gtuy%Nf zBLwCJ_Yn~Op)nQ;Zl}Lhwb6GEzMDs{KR9u4!u@A&A$nST>AzxippeP(@m8XX5 zcb2za#kJ3Wl&uu*AT~?S*{?^@wxYuFOH&eM*GInpXEP)JH}7)qzZ#?UpTGPo4FG`j zzieh}JG1{x<{NG+r=#|Sy$`DTM{q?hMy9x$<&?VQ$hD)4c1EKw>$_`R4^%W^K_o_$ ziIbsL#s@z+H((OL#6%vWI~`u(7Ov8JbLP(-ekHH#$L|}uFMMP2M8WGkVIqnX!N_la zTvKlCh@Z7zDYH1-k`1!7DU}J`KJOP({NIlNof?oY@L+6g zrOM^2V->~sYepStuo%o{IwghuZ=&fCaWdHepL-o4!-Q$f>WT6mJZ`zE6nG5ISt7YL zY6vtHjT0h>bZs*W-N9%Q8jFuX2j?vDN|&E(w3j65t1`nS=2S%XmYc+VQ zOfr~uY%xpy(Ww-`E1cdzU-awAzR$g zMw^M{tC|06CY6`vTW<4Y^Wae-VJiEM9SNFu;^yJ~z8|o^J3BhOEFFaHU%nq6O;B)W z>t*BO>gfh{6Vd7J{^XD4QL96yQXcb*$@?b6`}=enYQD&4qHB-pS1enCdU52|YXR0c zaGxERrI#9%=mVP!8nL2N`4TT}qktOMma%zobW3dK`VAdP*834f8u>%44x>-ROfLnPhPnI^2FeRuM=H+a+Vki1g8+<8F~0Hp|6qZ(abY!Ipinyw zRpPNTACB|ktQ<+(H6s->?llXQ)Y#jM5xn}d$qS?Te~k8T>y4|ERIuSC3EYvZ0W13F zb58;3)aGXcR&1AR&h0RK*;e9P&^Vw+RvV8!gIB9Y4a{FkW~RutOu@rk%iW&8vNXEk z2Md!CWo8Vr_oFA50C_TL#cQ_S765*Bu<~!)%UR3x<~f%WZyIF_LzS*6#P4en4JvxL z*>J>H2Wxk#(84bqzUINx$xf`lc*fGv&eDQy?&0O> z;o#%y=49u%dgk!40Sd+-dP+k8qaq>5f+9%b`it=p1D-K(o?`k7JXnkYNT`Ch?5z-! z@89!(x}&BI1cvPovMphVMrTEod!ac)2oO|ra9q&@44@7tTv7Y`JuPN7hb!s6no{dq zIoMA*CFd6|^ttMRdIsk^*txdx_o4xIT*&Sz3eV#q9pWhEVt3&9&a)J=gDe z$6$$TfG%jmHR-bEV2--1((b5&em^lIM{HSZd<4Op-37Uoh2h6JY$7&3A9RGFc|&?3HS z23BH$<`x(JkHpC!6lZG#oB&5r5oT-96ubVFa5ezID1#gTGw1@M)t#Hszx_1yHinLS zw^riAm62%NH*TomdGl@sYtl-!8O1=Ke#yPrr!!%H(y@NN>AcKUe1MT=4KM%DlxzY| z-t3}p**?DzmftFV7-G>19yH2pJVZnhb*0Ai3Vy08a(lSfA-z5DvGvAExULlM1&=@4 z({otPtB5vaKVD4*I1`=|eW%gJ6OEMd1^CF2X7RdkF%LUOy4eT`Jk$l$LZOBO)atEa zz*bx=0WKTV4>H6<&x9bOn!$-%P+jYP4i5nNsd;cP7>VA}@8TbzBS;=;%FAV)Is&&Z z;)Yxlg~(;TN^El;`wl85T%nkzexH3WKc*0_4pbZ_jVP#QJ%`n2?C&lX<(;a*1u`Kw z3nShdFO1&b6=IDYcPv`M?9z)=x-ohc;eQH71Z4?f^z{f3vu24Q0)^h!eWi%7!MO=9 zcCMu1Ei~|>`Z!;hhu3ucuVvrYyl!n>MkJZyN|ZO35N{vXgpoxoI~!jP4AtTbd?<{< za|BW!trWn4!==ix;ZKjYbzA!2PZw^!*vDOQ4Y?3`ayi{G9a)vMcvN4WenbJO3YmSx zt50kwEaso@GbFaC+h_(JZZ<2#E7^%QS|QW8R{I@0J7rF1!PknwIXW}Fp*Oti+&x*$ z-p1{JagELPw`g$RYe?RQRlQ-AhR07&OxP{3b2*C6#=jda0nPqkiWYMX>JySLeW-@ zK@6lFtv0&*Bf$9`2L!4;32CA&N@PggHQ;m= zqc-0pSH-80@;kyUCn)8|L*p1ib3pSOYzBcxXJ@xuMdcYm>j6|sdpkGTIhbH#HZgA^ zSm{Il6!eJg#SnMThYckWnsrJ+-)RPdRE+^~Ik5?7xG$DRV@I6=p4%Apaa?-f8!n+w zf0H^;h>$EIVy0Vs++gr(>Q~_TP}uU*fqgj#-TY*>0T~2M;s(;s7_sX=KzdUXv9{15 zumX3m3S=*ZQ3h6;IfN0wMqbtZ)uK+I#_{u8X_O+j6gzzh@xserW_kKb>PX6he#`Gb zQ4@QVr{J>>1!g zxL%13Tw}oP7xOKzRzEx~OF9ZKdGWAQkRS!D7{SJ&)M_@pm{a~;oBZOQpD zwo8cqZezeeT+W$MA_jE#;KV3PqZ8fc5SMAy<1&*#`i3~(JgYC-5L)j53r zH!^VXSw)Lk{VR}iD}GPB;+5vA1>C2(BtQo7(rb!%Q(qTrX~`&k6$U=y#vfG5sIJP7j|Tf+OGeeX|Que7$!lgoBZWD_Fes?uLZguhj8OZ zvoZ^NP3O0SYyC{&IOV_6=ATv%JQkhqUeLwYZ_w){zB1$8)v>%J=P}$lfoT}qe4aXV zdx^UX=I$)_I|uXcLv87)-x`69!tYqNk1W5eVJ90CBX70A3TjNpdre}8JUWnbQUAdo ziOlQLM~rmUpc#I-^T7hU)-xSVsIChD8=<$Ui4$g=>BFj73;g)M@nP zvy)J(74OOyOD|5vxbir&7J5&YE=s;5D}G{{`(d)~nC@8z4PYT$w1Ww(3Nd|G{g|MZ z_2P`PqXeq70E*QQTj2K;5g69 zkepMAP`&erMNLAB1;n7r`gtr3P`wC4UeQ-ry4p)lT!AqGtsPexJr$WYX!YpJ+mAE$ zfaZ#2?{mb`esek)h4Ua5J(;=aO0Sa*DqipMxDln*9%s*%`=zGOaNv!mwUoA?I3GAL z1k5oo|BOHL0?zA<{Q71lyp#MqDy=>`kvBQXEfx_((qc0fNg7FlTKP)+v~n_6;o)^z zcy;T?r&^+yu6>xVdU*ujNALG98U~n}pnUoH|^u&;sr$bb|;90 zkk-|!Cq&d=_b)3Hc?Kb{f|WKEyA|4oV2^!rtD~-tFV?0nsJLR_oT)F-*$ML}u&(St zMmCKhtAv<|ZXOMwmq~Z7t;ImUK?8O3y%RYL%ft!idF}71^2abc*FmM8s*pWzeY4UH zjjV^WKDbLjM(7-J@4Aq&TaUD=`X@13wDdm*!;L=Rt*oeE+=&<}E6AyT-RWDf?-H$H z3%ZYX%qLkEL)xaAo~~d{`q!peL?u-ZF)pcJBmgfVxMdQyJ7V}e?^t*TZ!Dp#Vz$|d_AOkhFB(TTY~l(ThmHEXznX7hn!Rw? z0A?~&+c@Wsocr^|x{T<*wcNzkew%lx1T7_8?fSQ>R{OLK<190CQkFJ;)(WU;N$tq~t-bMh-d0NLy<$X`(DWf$T54Na|=E9I;++^{3Z zz06<(T+Uk^;dcoMgwVd|Z^2cfE3p^E72%$Lj`zZ@S+TjN-?rw|_=4q&RwxBp@)HE> zVRU&KAg|h4Y;w&kBx41}r0u-B^Eq~{#@Vx~1N90!9UHg>In8*1aYHfvvvYBUP5E+x zF;dFEr|Ax|c`RhYb=$A;Yoj9k6t3k7@On0lvb;G6y&JVD;Rk-SV!OMP1aSGs0u#TR zT%zP8LLO9TWzuI7kN_4^ zF`WcJ0rm5SO^^uIG3sPw-*AS4Q?;(xD z4c`Q#YSBJ7!Vsi~_1M%fqn`=(Bi+r!UIxgA3=I2f)Ay-SPEzwVL`^Bj!w+n=CE7!I z+{}#3xWQTCvQ=^|j1`Dx&Yh2fMHE6g*0#Q^78nB$^={7WNhkTEi!9vpj)W|;$|qh} zx*7uKb?q}rP>j8D14E>axg_Txe!q(|82*B#*CZk63EUbY_T+(~DARja83fOKi@Eap z?^<&Rq^uJGub)TDJ@d};kzmplS0_DwYS|W> z9GON5-d9O*471__n|iFP{ZkhS!xysG&f8hAzw70iyL1Vj7Qh<#SDon*B$H;oz#jVI zk7G0+#uLCft>DeDT2I8NB%PV3^8z2Ci%!{ZK$@xdMFDcUAz|qOX@qg4v z9xJD<|0*vpRHi4A3C$F_$LtYBk=1^mJIr8GtUemc*7 zKnlWgjxW_)#Q~Ujcy|8hFvWl$?`+}!HM}UXmWar->CTxi7(Yd;b{&5(=HxEaj1}Gd z_rj#3ySj0yDU^G6*P{{Cua{WNEV;YDMI#>bNBGIH!+)~4;KIp9GsnR?# z-nxOhD)j%3Os9yI!B0y3qBqY(aEu~Hga%a;r^^`{T!e4-SmBbT?O^s*dn~w zH+Dw^>oW7xU58eGDXq>^&agQzC}^@hUOaVvkXAL7R=WqFTgbrYql+2CV|QsU>Md68 zDYbbCIV#phZsc~ZU|R!V$IddEH;}=`_ZF5t2To5#>a0|ewhEgCuP!-5vYn5i(vr$}dXEm5!lrZS}!db+8(`==&XcW?{*=xmjmV+zXwOu`|_ zLiLB8te0_;cmF7-IjNU^g8%dwr!^VZ*4CS)eFM`sT6q2Bk0YDK{3eNqqZi}XlgrJ? zD%nfNm`u;eBUR-LrCd2NK}xX*x&n)X4?J=zvf`aNZh7Zl%dw-_Qr!y1UrV@n_cL&n z7RGlAjMDyY@^^+FcJ~_4!o(S-5IjQ`)tr(0vazRw5X<&%1Y?~AGrr1njE_DRIXF)_ ztd8-}*}Zz+OsO{Y`>GG}J=PUOcv0hCh7^k;iHvP@Y@@lN3>)6d!wc4>tu2sl}Y$a zgx;s|4-w~%^xsWYV_@z$s&N!QDv%_ehF>7c*-ig8NGI}dkT*YKt+K&1 zG}_Tp@^s@zwkrD!AT{8z;_5tngCx(Uyt{w^1+16rSPdQzLERY}KV&k#v+5gTjgI}- zskuXLk0zh9re*MGLV$;qO!^9ck?fSD`DoT4xMy^AWa}gr$E4ZQgxQB zj?N#{pXZ}S-1E4Q(qq1OVB4qnxNXMVzNHyGJs9tdl_yg!r{L4rk)=9U=p|G%zcAeb z3>ROJZ%3ejnGHS4XbBtlR@SMxloq+BO-#AB3O@QHpEL(RnvmtJ{v+Z!^83e%_4|r6 z+75{xqE=yVrQgkg46UTMEr#ZhuQ_zO?{NIPi_#kFBcFrm> zz2bWCbm@6g8D^{ zWobGA{r(LKx!*9^j)#DrT^1EKj9bus@K)!%6w_tQ^&&-eqv+>(z2&Fs>H^Hbcnn`$ zgM*P!rSp7iT{?e&bI*kgW)j~*^k%4ZsO~BAp=(YbfaCku@P`7?kOlPAlc*uKiLW9x zxsr$tEtaR6vYvn-=Cs`~NMsI>W)SeAJuX`QLbMCeuI(+%bzGUI%uY1(yAjGMgGl{E zK{P7+LVen^RO+k=z>J!*;<;q3aQ1z+^NEJB6H_~_bYrirAXqnbe}g%NW_<7bv@;Wy zp$3N)t4dVR^d62$&foFV_q0f=W0N^HwdFGK&zv;?lQ=z-5Wj&J6MlnB;JHjdO4G|q zQ0-9RYZ)o~8R*z`NafG@uGTJsY@4co+S1#J8VS|%MEWQ!^3`vqe>(>Pk56Ag!1k|+ zYv2=GdGLQee`tNSwEla%(Euo(~e8-fN3uFaZ(LPOvpI$@zCiF{F0WpfM+HSkvvCB>LK?x9{GD*d{gOA z8H%^57HzK5u~`$u@plDECJmEW(G*o2ZiP9SSx)y4|NXmSqXvfh2Ipj6-(%p(HGBAe zTim?4KWhBExv^mIURi%SH>PV>vHxAH^5xn6ycuWvyHWe{!Q|^zqy1}k^y=c_^fVFZ;ndK2_e8!v3LP~iEM=vh)u-} zDUe%BnU|?XSU-@s7LeGK^nFxm-EGi_NsfIW4c;KyT5O9v#E(4Xtuh3Ymh;){+l(&Q z_bkB{+5bWq3Q+ozp=D1c9<_{ZJw`~VwP=YIKVAnUa^`|({-Xy(YVb`!FocSYykP+dBgd53|ImD6ESU^IT*`eev^Cz=EYLop4mD%giQntH);Y zN;+ni5E|2g-#;u=Sj^IOn=95QV%O&yM@v+|8SqIzm37pRT_|pW735Bx(^Zz~dHp{F zBR+ocsNTXbBlpJmf6|{`!5(C*Ob6fqH1m@e8gug+*FhpN-8{JY5y(v(*RSeXq|l*x zvF_EX1+2o};JOa<$!I0;B1pivx^ofGD2mq$=m&?%B4ft#l5m!hM!9HxeTYGBiW^m_T7e?xcY4|D2>SAn68t4 z)oW$>u^T2?pmze8%GS0;k9m|UH-O^FnSpF(o&&RyU{+{;lut`SN0bX-Juf7&RVUrr zW8VNM8you1VrGsEo%nwn#adktRqmV0+u>05_~{q`X-bz^R6GnH7+u6Xu;+fu^|x3>f~NsOJVigC#P7TK&JIg=sNg zw^`7tR#0as+Ovvg=r2$O?0e3*YoPcib-n=&TN=T$H+}e8KjzaO#UQz*K$`!K7 z+tYzw0jk&u=Eku)(9mGaaf((|g|*UCmaYGtF=dO9hBM52&5Vm=l8>>+7LC%yCjMu; zmdbqByGCH{oChDJ3BqfxDD5*f%IJG>@90~ir+;B^xkr8HWsqf@CN++!)kCYT~0lw`3kjC)+DhK z&7xtLnm@DsyhnNiiU0*SZJl})+TvAB1FCK&3(KxG*r(35xO%DXh+A{^`Y zddt{T*G$j?0I_fnvPu=8wu3j0vq`(mD|yM#E*VhF@0vDwU3KgXFlXSIwX~8O_pAJJ zR0`MQYb8S5fIfV00(4;dGTje^7BEzsoWE9U7uP2qkXc)+7DKWS1^Qj0&2I=L8U-EC z_E2Gh6dryT#u5~Ak>x+^10`@c7=+BXreE2d3CaUM_5gnRn1GVe>lprwxHGN7jXdDx zpNcYTTk4c(PrU6ArZ&Z7Bw1idi7r;cQ56&v_UbAwYg>E)iq3)_d{YlOzSN|-W=oU8 zOBrUeKL$S$3q>ovC+7vLBO{ix)JM;CQ1udd=Rwa6{oXM1kg>|Y~v&nhy0 z_UsT|#e_-dDwlt{i3AnuJ91f z1^`1po@`huUG{Pu%ZS|`ZcD)gIrut?ez>yTH4G6`{CK2l_?)F%zBZC7Z+6p}aPhQu zqKniQUbq+SA3iL%M=pd$eES%*)6`)k8eKgIBN5c~3c$f{uCCq_FVgaXzlD0Iwq}Gh zN2NEE?2hg(c->7QU1tL=Dw0`cu!V?Mr*o)8Q_-ChEP<6c4&l0g@UNK($tOG@U8;QM zZXn8RGY(@?Ew*moGO(S34l%{%GX^}@+am`##nKO&de!Mz3+TK{XWJaCs$DkmY?Vze z>Y93u9N!weLS^G}G9-V6eDWj$>2i&3|H6`P4!?+Q8u5~4cKQmhCW$9A6(-ar9MyC} zL)w~i$ij|F=vW6WMS@J!Vy^bxi#KrylSytMO+K%EQRv~k$l|IBlT}-mJx??A6XBN! z3i<=}gxQJg?b6Op{_weoJX^VdG5{%?QRX%-{gBhxi8hI-{~YDCzEM$|UvE{Jm^w67 z;q@2uoUHs#mtoQLwEPsBql>I<|1&n^J9>uZ<);M2v)A1J#WB-^d*5xAbl+C2HQO15 zb88L&gplZ{E;lIgH`=5x*j`Z;-0r4UjS4Q>!+$Tx;Qv<*N_wD-y|u4=CDhob&U%Uv z$;7Xc880?_6i)GKc6Cf#mDZ`mV#97)sJG2_8agho>2kv(9W~{<$A4=`ehJ?`3l^?O zc$WbE>(qaIBM3f>;jkT|PIbHd)fG-UA)7Em1^eZR+y%VIarEFv&n%Hd^WGQ{iR`dO zz9~8f^9}sSxsMO^27E4HjuDGk6%o1pQ7jwDn32110UmX#8BkN^8MZ2aoZerd0Y^=2 zm#TENFR2K|3PISi;}1b=38BG2Qxz$j6&%q9W@Hz^;zomU)W5tkA1c41z`QJud~}__ z_sTgNrs9d24RdmNQOf{4rGaEhZ0I4a1KIHqbhwfYg{-x(v&pWI#-e z`?E|~)H7LAS;+s1*yY`PbW4gJ$FNE2>&ZlLsr&6{EvTdPdp*p?3_NH(^loF8>k(1} z<0K{8eu8FYf{M24ZK!sYVXVT)UR?EcJzzGD{e3%$p%CUHrKLQ>N4C)*Y4-IbqU!(Q z*?#O5Ifzs2WK?LZPFa&<3gANv!#9`Fvc0iYLq zLN*@|_E+0?qXGGrNKz@Ffz`QTQO=qeD<+i^+<=7xy1#>#e&To5aH0_+$&F%g4 z`Y~6xhl|%8)i_4?nW0`f5MBuvhCzgpHg*?_EWVmeW1b|l7JmfD!TJ4QpLXLdeiC43 zis_Kd*ubW%(=tYPW7vn>fr2Dy-+-<&{_0zU6&~v`X4n~%giai173~A2XMZE#_9{Mp z;3faKF-xqaU*6}Jgj&@p^dK9w!(MsQ0HHl_ObhKPy>;Xp9{Ui=7tVF(tVQdwQK@S! zjVY&{_xKvcl&#!*RMwgt4MgKU#qeKGGwR(jZf{SE=7U{-TO=NC@x3#BAu6B5^cy7C zh*enT9R`pi#Ll?S>t;mSv#ca*>#2~GwDeIEqOvwQI{ zUAHEZLY80PVG#7`cxpU)ZTSoVd?nnlzLyw8XfIqUtoM~fSjSVb5a39d#?Q~fme1^8 zNZ}M_-r}U$Th5Fm`X$eLB2w{TQ77Sgd2JE&dWv!Aw+FuJ*_O8o7jr9}ouzE=;aG~^ z=|v^n-8KSvCUR1$!WQD^f4ame@!9wfovOn0i0Krko`_5>ytyd{g zByOh-bMvLjn{_mK*em7t(JADRXmpT_@O_v+@sXjLX3|Vog@s{03U@I5U>p5@jH= zfd0a{O#VIW@)Z55KDHXFvxtR{SiI(5IK66XL|4Y}92X^l+bX>#eJ!VBH|6oDY}yJ# zY-ZRh4?}-3Yv8vk>73)fsdi=9t!5PG_$3D85CWM1Fj?@>S!x6vH5(UL$+VPMuQ8uO z|MA^yY2+;+_@Yk>(@6o@; zV&#x=N>Dzs{f7#Z9^(XBkIQvZQ!2cs6Vf8g?7BMjEuz0MT7#Jft&q+)bQe0i`%iEe ziH#CZ?@2s9rL^k@rWcI+t&{UeGPBSr z_FfyhxwL;P z?SF1ZfZN-I6^~5^j|_lrKY$-tZR!JW$b}i8X?y%Zj<86yeWai~C(sV?<8IXKz%sBC zfg?H_6-e&)$|%C^95I?+ z_3Lvg>R^HS#hZX=>j-VJJ~A$t>On{)@i@x6gjY*EA_@K^l79t6OtgUuE)kt2UExg^6x59o{?RGoUsk&!IUX^&X|GUj2`$ znQR{hLt?Ko9ecrl{Lc~IZ$9Z{tlpoFh5hcgK9Bse!O z(LtE#Qk*x^!V-`6tx8#<1w$)OyS!HLaaGzYYsV;L=TvQbYr4BB0%iv+!$BIG68V0E zK`9zN&w+nDFC580zfEr57+6B0d^3c`i2boj5ni-38HiCvZ)4@?Sct#q=B43n-d z-bRGQZjF3D&6pfoz-<)4xDibm!MusWrUi0}`S`T`2NQjU#VhuX)n%dK7IQxmq^)m8-j*V0cjUJ`fX23|^a&qsE5+%XXIO`SKQ%iyD zzIuOgIn(8_FheCp@+)SFii`in(@ruYKD731#M7DMWU|WU&2<$3amU{Hud`(__t-$x z=mjsx$LI;+B54MQx&^!%{o{#$wMZaiYmoFkQdGrde&7k8<&)JZj~7)THF;cI7?589 zVRF6xZC;2YngQ%2B;A9G?DJ)o>S!7zMn2nLp1GGF7ePBAyEGLEDk|?NW8Qyy1JrF> zixB5uoHh9LP(LJZi<-i=lp%_^xlYSR4Hjl_I`Y|764=i-N?zBdM|yDf>8egHrf)Xn z<3;Rjoc$%5Z+ydm7R$0`7tmc3DA?@z2}poO(*YA)m+)TFIQS;bnW+y1y=a~Es-c%8 zsl;rNEau~u@~OQ53=!jV;(oo4z|V@SE#|aqkUZNUyAu?=lEWo3{#P`pvOi5FmneK3 zMF0b?%e-96hjke(o3|=aPe@?IzCBAgod!~$4N>ONEYy{#fB;}8r2RkysBcK*{64ii zRxgz##2znv6{=6j%$?`4_uIF{*V7l~w3DKU1H%XpvDuw%$?3F1XU*eTlS4eiUBcO=v}x(fT^XW7L(Bcnn({W0ATrQ@w(d434y$+_t-_W9PP3^DloWx6wpd9L#%1=yPbgq^tS*^AoPk9P*BiODZJ5l7P!XSS z!G=k(U&iU70Ydn7(>JIG^FYsSfd$p6Ki$rEJu3;=4XK)-a`Ms=E!h({vZRX$?`>=d zdws;ns@y)g45(vX>j&!$C84`5$qWobEUJIBu6HPEWqRg|8uJcLLk1?w4T-nD_=>3v z3Xq1+Lkw4+0O~cEA;GaWISa!^l1fY3qO->~QA^L8rBCn1;uN$A2y0#_%a;@LRi1+t(r-2>{1Dv^c7a%WXr3 zl4jk8%7y&KnnI@YXMH^*0(M!EW-G>~J&f}m#4+sGI^EB@+>6&Xn6sxLQAX!*y0A#) zYb9RR4-4V2*t_Y8AR^`n?E!Z+E2XID%0o-4;>-70zw5_>Weg_6n)yM%tK(4&u$`%` zrJfj2n4~?GngSi4d8CKzHMUZ4Nu>+Ug3k|n_y}8eWpZS@m+KJQSqlQquXAjvZbxXI zOsv@5foc^~&c3J^cO%w{$sWqX)>6`0ho|y*TtiaQCHl2i|2-UtJcc$=Qmaeo>8x9(dSr4Q!{Ujh*dE9@ z37!onR5_KxsQCa&g{m0KF7I+esf~L zSW$8oiXX#`7q3x@c{N}upD_OcNj6uokse?{)?nj>IhPMNLzhuF>E$1iE6KMDzbZy& z0NUn^9X4H5bCu_EsenBU=3>i4^Flm3BJJ^Mct}BiYL2DeF}Ie-S%%5M>UKp4k0#bd zfn+KD_W>dhFpa$eB`k5$L<@7v1?hW(0?+l(ZrW=JPBp%D5s(?sj?K5K7*095O}yxc z)9>-6WW8^*uEP=4#*(Y8(g4rK1DT323LXcN8&KC9v{0Yu-fR=`(Oh)O?*;120c3ri zmdV@@iT+#7x-@s?ggk!`m)uM)CA@^_AT4_-`%3>O_rAUg52jRq?A7T*aeH8iIFflV z6;6G#YrQhGZk4R>?@xgl(08r^RWyWp(v_#l5q%dzq;21ZOUgg<$baYW|5mOYku_RnTHnXyvQ|qKU9+fy{V{#CY zeN;nA8=kF9aXqJw%(&Lf`L+97Fgu)j=6G*FoE7dYH%7Rzzey^S%5)ECH6>hOLKqoHDD}84Rc5U&%;1B2 z*A~}F9cAE@XOpsb!6|!Q4v{qWq&h8&ioD6Lpl3`}1S z{VimolCx2Bz)vC1=iE7Mf^vOcd?Se0n3rmho+03_2ZQ$ci7(q%e9<*^OesBJ1}t&b zwJxUqI!~D*&_hUY-L+?}CKji!e6*~Xobvwn1=qAcq$nFwidBAJEEA;ipizxF=#bqs z?nxZd$1hhvy$CB^e5_V0@>t+DhHNM3ATGtA88#sr*&MDqtbUcSpqg%oR~lP9tcc?z z;lm>l4Jj^HswLC+DDNT;x&M%aYUXG-(s2;P=|x*ZkK$D`Jq+MdOfbjxFQU#06$Ux^ z-zo(Us)*{Ks^TXkpx!~tlxe1X755~RxDMg3$<4}4yq)c@w~MTQOF-7f*t3@-7hVl1 z1oDrS{#q(_R$mfG-a1;mFLBrPFi;MPl}x^PSSW-6wH1 zOu1zA%==qrSp?7MS6f+vyqgx-J7 z1E(I%XzCx=q^42nY>^vjtTd2kIXFv|b10u@GZ0Mx7}OS$(qebnLcvW*>5-}+F&MuL z)XQ_C9tliX9#n~dehW;*0MFbma@V25S|JnU=^8P+to}QlH zUuT|Kf19oL=bjPWOyliju?_TG*jsC}-jv-uI$T()nv(-xkPjI8UX>p>UWU`_zmCUK`k zll5ywFZy#XehL+ieaSOl7RmBlNxgUzvsHXMsEpimn~ROFfh0D5gXG0=ziO<*7 zf_WlC;ijhh0+Oo5`z#TVP5k!M=%yNdtB%W)mL0t6y)Q74b>1{f;vMlt|5Z>=pECkoQ+Y@q%evdV7}z9hx@=Us zC}5C4ge%|&H1~wHuv*D2`*@!}D=fxOAl%n|35-DyVY#YdS8?puR7SJ}q_9T_Wnf5B zFK`F8Hh-`e?J}9ynz*wGrU1Jl#@4uSZk{`FHjo>>8ubkOLk7EPtC+B-Wzfu*RG~L( z+Y}pO%*QQAEp^B;#VSF-LPnVL2B`b!T_nzPsOAZD1}3t(HGw&dknss|SR0^FPca7E zjAJFP@n^9w?f^Xv7o>B6mll8lqt$Q$4?Yspdn_cqCcZ%akm6I<;pZ88>T@SLwvHbU zt8WmU^wO!;OD~T3=@Eyf$TJ8WGWLZ_0zMwL5QK|`kg<#TC>>X&0>iK3#v#^b5s^}g z%b3|G%bpr9-E~ExGwChZX%|)>;)sZKL-rP}_3rMl(vW%_PCh}>5DS!`7J}!6Wi(lI zItU{G*vBOT`FK?;#LA2d$nc}-+DJNmTA4^!KFM7HVb7eLZ7G^^hcZ)`l^?6*pN)}r zxVrS)|YIe4RU!&S|i0;94o!Y%0 zv;d9A?PxPO{Fqnsqj}9^N>$;+5hWC0JG4MK+?m&c7Tty-G$avd+l-bsP&H>NEe=O$xYUH)S| zU5c#6W~Sx(qlL6*|37Rz3{14m;0B%GPhUbQgZNTzQ22d-#~?v+7EyAkqx-$<@xuW# zEtZayfRTFi6e8U9W;#DI3SHnbPw3=BJ-NRNxhB$4R5f4+KQW*~0KpW|j8rnt7QcpO ztL7hE@TVZp8?Oc~jg2mf5D^+5Uoe1hV!sW6=5`^#N|s>Pc6d=ax)iE+ki z4rrd~o)7rSpXipzA)QT#1u0h}G_$&5QKhE59Jk?grGXHRmVbjyG4ECptZg`qRvslh zRGceKot9$4$Ct?SVvdNUoe(}Onb^$U*snIS+Ek`lT8d#Z>HosDB_mqqbUM=6DdRbh zubOwI9=CrMl8d~&5oqhhX@BjAyb>Hembv0X>jJ?<7yQwg9Rlq7zZBLDb`ykIzr*`y zvbj-B)sUf)pWKv1n;fBOK8Vnp(|COXpHc=8Q%dY797YEfUd8TQ39@EFM41(LR^kkn z#`Im3feQ-EoW+hX?xmR5FZ z$7?0YP@?mT-`NOd1}P!UNXeY?n|3RTm2cvFBTRZ*HWHxGs$K!C*WH=lBNqF zh06D--@wHXA#1J+mu<^Z%lHQapoqpfQkl-Jy=Tr*tqH}r6QMdBw$lnYT{ z3tNf6wa+1VQksW4G`7cn(u;*gCwCUNI2E=JolQ?hQ?f$MT+2sHN`qz+Ox%Gm60%0I z9B+B2nbT;_qu%#+?*1k2W+AUNtgti$_K8HUjdqN3Zh+4voS!K zWp^!*5R*TF=423HA$S5yopU5ObLN8`??pKGPZ&6g*pXqxsEV32$n9JN(#=NrKbo`> z%FRE7sC@CX;nez6nGQ$UOD-dsmF)xXJb>HO<36pUeKBU$GWX?~p~Tu0nofhUKTtr? zyTTW+W5<~HXBs-y=hcORl~1}LnG2_Mlf2C=p0tOGYBt)-=GetfGP%QG7s~vLS9=?8 z_FDbCfYqmY9ldVj^5lk9FJd{V>SM+sWL68KS0n+R}_z&;DN{6KxLHnzxyvCdwx}hm_Y#Xt_4l&!$^BZ*T9y zkciW!@pSoCFH9M=s_64zSZGebcslG#abZI!i`ij|I^VJ_Cfafi4Vqj9zAvBl{h@+R zqQkYc4~oJ#4}n&uG_YwWfk#u+h@BRP797|pzOo??DQBP7!>y5asXU_ucjEQ~+11Ww zaHtl3IHije%ZJcLa)tZ5j1B(l_94|6vsSybrgITa;MF0-gM;s~adib*Bl*wu2^&gK zG46934lo=8X6;Z{6qV{!T_5qr(QGWQ6#XJc4SZCrDU6{BkTu9ACg&m zKqhWI39{%R?9E@WGJ-axzEH5}58hkp?`+0pp@n*Ml~9da+rNG^Y9Y%!)PcNg^@@6< zYLiK4iwJTXv+Wqv_s%^ zxqfVJ>m)!yPMAII>KcJ3opN+rX9qV(2ofw}Rox2r3W_A6P zK>&%(5*7<*3G|yR{7>Tkpx#>+*PlvMuFuJT@Hl8CT+ z8)^6}LW;UaqIcITjhn5d4MjEOe1|@ zADPnrGy^a7DBdBn;uaa(Th1urFh#UI0Rg{l#4wFXA>X{N&kjZQ5HkAs}W-FW=g%^dIw+a`RLHP+_*d2`!kG#yA25*qrY7dr0E8@1$V?CI*W}sd=qn3fbm>vZ6RdQy7;u zOg8Bv?*Nk!=UtXiwX3mENi}Gd6v4mZOnM3f-@S?5EPv5!>_2_A>*8G_huHkwWbRfi zQY>=l4vb%%LdJp+eBK+xjFi_zRYAQV!U`>hJK&M2p1rm;s{es-WY;S@TV(N`1NfI; zRIsBJ46C5Lzy^l`yCkISx8)!4U@`K#JckW^f=SM3*YXwDpLLA?3#o*wA|nex1mK@m zaIClviN`2HyrK>l(JETRdmhHGc3@lruf^-{F-q#Ad0dKgfAWknsr2_4 zsUsz!L|ff>=Inxs%*g0GUb?99vT$iwb=CHV!fld1I|r%_W~yG}u=PaSKf5I4+kfoJ z73Rkr9o9aHR^#mIg+*&N>!_t;eX!4u7KOk9S`a`*l-Fx@(1JdBM}d7A0hY4S`ciii zS01tZ!O5hlMz?5To4v&2=i)=KPHI1MQaBldb<1oUr9rOg>Bd@p1MLsac(DdT3r*wXR)JJ?8vmcgUz=~l+7v@g$W z9{%aC%l#=`E)L((W+-T7+oJr2Zv;!povdpBconA(6)hjR)?d)gmDh*7 zEq_H$+{vf+uw1WyKezRgU+9jG_s5QuMo($rB0ICr7tKSNrMyPuD=8={5UIYItH3Yr z`ioGk*iX@g#g5^%A-<;zh(|=OVT3t-qirYWPvbS3t*IDYn9Ym5KKDzxLfSEi7X|D0 z9*K5uYsswPzyzwhX>i~XJC(>vmtE^yM9G)4C0akY<c?+x$a^V`9iQ7OR%ks^1|<82T(?H;u}B~i?Up_;k4XRF0kkzvmb|XbeavB zmuXI&7P8Z@BD@+VYm&1()c41jq^0nvzP%iK6sMWEV1us*R*XNlFW78U!V!hr=1V*g zAvtEYE{e1xPj~H%}2XSd$ac4!7>7AST( z=#Cn$d7i?CRK?mphfpHy8!z4>_cC9Qcd6@6GG!h1HMmTBonZj&>gwKUwirvmX8ym; zJ4L!Umzot&b*F#Oy|Gh|t2Km?kkg7s5Nk`ZJDSua07^ErWiZAGRgG_fTiaJnq;ADG z;jUuc9Gwm99BeI&4V@hSy>>72e7kP4 zCiH&R>LpwjCW$*Wc5hyP$aqd48)mkeXP}jKx}JWZ{|zE$MJ|6BjJZ7h{XPK(0Q{3= zM7Di>(HtX;7}!6hN3RQzs@x8F;I__cO>)3K#=6;H5l}=vL2BO6P!=1L@+OtRJWeQk zp|Ny9;BAiBMcr^xE8$Fhn`BpRbGlif71p7mnrP81U7>Nd`>b3)_V>9_U6oLx(V&=? zdg40yL#K9=bP^+3M4fu00#O8sVaIvM8pYuDY&ElCD%Te|vW0@mrcFA-IcIv=&~~r% z=F0+_2l#J4mp4Uuih51`H_7J0KNScCerA`}xTM5(StVU$3FT$A8b0KHXjx0LU+=?s zblK!U-_a>6l62QZhQ!@zmHUr9LCcyQVi!egLD;1c1!=$Q+E|-wI*Pfw`Y8Xg1BPm5 z38LUYZ&d=#w>(bNj^N>!pi<(Vt=+oc!_ji(+xBmlyALM^6d3PN6M9&y+NgE1Y=8@N z6&i!fA0lbeeyF)xVOqpLvG?=xEigMG5G+0)*N>&X?(ZM7WFXmfh6Y^cM%zzym)$|5SC346fwX(9aWdk@MT)_cvl2Ywemhi8W+4DP3!hG;U zoDUavqx_W(;IJXqYUI z^s`W*x@ShX0gAh?ks9Nas&!*|HEf@#!rgJU&(S|q$I9+@2cJVE5@&(vKPjf85P2h+ z*ifbbQqHoMqh4MK&VAL0m2t?5>j#{XRKXqv+tYs|{W(!$Hhp`{iU)%Aixqb6)H1;f z#CSsU2ZQBGqH8p%PO^CFU4H@nlKckHQDzrahH*u~B};K>hzqROvdjuN#E-mVe84NF z8}pkWEh48%F^03kj<$s;8?)TcBT@DZ+DNq{blR&BC3E|eWn2k#MDb_1V95CLitDgzz)I& z92wRF-Dt!eP+-*xu{{bT>u-)tVZ*0grKPhGgf&g{iW1m}k1-+?X+sVyt{;bT>zMID zC}ilH+JvQUS_Htw(O=zeBetZDo;zJ%Bc=n2cOC67+^gJ^g@Vl-!ZCZlz7o#og7xu* zlO80G5KR5I7Da?FP2Z6MYdf;7*oiUfYgwNioF5xKNd0wxc=*1q3nRKhPlLr#Ikqxy z84;RW#P&4Ac^(-pGD9x07$0(uz$g|rf}=v=bSVNgqEmB_t2qzVt>BNc62P3DpN!f4 zX&Kc4Yz@&>p0f4W85Lrh7tLP5q(w&7A1e8U#-bUjFxy3(wf8$OWb<@K!1gFNSfWkB zdK|na!K+HZlK~nFPw+6$wo#4|o@+{nsD?G7aN7e)oU}8o4lb!ZMfoZ0E?_#bc7g(U z&wamEy(DfmxyDE;*)S=9737i4Cd?SqYP`J$g@;)Q$CKstr_1eqcbA6`GACI;kXD%< zEwZRO*aj4&n6A)1ZkhZ!TCK&s4q>`OO5a}`6L=#)OUMs!_d%=3s?>I)YuxX^fuo$r zvj?Hlhz@0nMVM%Suh63Fa2$eM2>KX?dm`)#Qb86a4aKnyE%0D2r+8Bh)ITBf7KQX#hXO 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 z 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%0Ad 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%0Di%!rDKcb7-^3;|a)R{`mzs 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≷nPJ?+nUmaMz=EQ0yj;GY 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 (!gK(udkT1}r_X-Rw3RL18 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 z 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

    ^XZU0Nied<4Yg$j!TQP+`JQ@%9 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

    LS}0NPNaD>+mmB*>o10Nr&MjkmMv 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¨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 z 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⁢-dxb+^ugsMOW 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�yEUe>lb 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%0AD>8;d`_)}=9} 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 IfrZ2V50f4auK)l5 literal 0 HcmV?d00001 diff --git a/pythondeps.toml b/pythondeps.toml index 7eaaa0fed1..7884ab521d 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -19,7 +19,7 @@ [meson] # The install key should match the version in python/wheels/ -meson = { accepted = ">=1.5.0", installed = "1.5.0", canary = "meson" } +meson = { accepted = ">=1.5.0", installed = "1.8.1", canary = "meson" } pycotap = { accepted = ">=1.1.0", installed = "1.3.1" } [docs] diff --git a/tests/lcitool/mappings.yml b/tests/lcitool/mappings.yml index 673baf3936..8f0e95e1c5 100644 --- a/tests/lcitool/mappings.yml +++ b/tests/lcitool/mappings.yml @@ -8,6 +8,10 @@ mappings: meson: OpenSUSELeap15: + # Use Meson from PyPI wherever Rust is enabled + Debian: + Fedora: + Ubuntu: python3: OpenSUSELeap15: python311-base @@ -72,7 +76,7 @@ mappings: pypi_mappings: # Request more recent version meson: - default: meson==1.5.0 + default: meson==1.8.1 # Drop packages that need devel headers python3-numpy: From 4f04a4eaf0576a5a5a1c347b63d17b4d5244c979 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 27 Feb 2025 13:36:49 +0100 Subject: [PATCH 1371/2760] rust: use "objects" for Rust executables as well libqemuutil is not meant be linked as a whole; if modules are enabled, doing so results in undefined symbols (corresponding to QMP commands) in rust/qemu-api/rust-qemu-api-integration. Support for "objects" in Rust executables is available in Meson 1.8.0; use it to switching to the same dependencies that C targets use: link_with for libqemuutil, and objects for everything else. Reported-by: Bernhard Beschow Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 2 -- rust/meson.build | 2 ++ rust/qemu-api/meson.build | 27 +++++---------------------- 3 files changed, 7 insertions(+), 24 deletions(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 171d908e0b..11328c05b4 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -66,8 +66,6 @@ be run via ``meson test`` or ``make``:: make check-rust -Building Rust code with ``--enable-modules`` is not supported yet. - Supported tools ''''''''''''''' diff --git a/rust/meson.build b/rust/meson.build index 1f0dcce7d0..801f4374df 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -14,6 +14,8 @@ quote_rs_native = dependency('quote-1-rs', native: true) syn_rs_native = dependency('syn-2-rs', native: true) proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true) +qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true) + subdir('qemu-api-macros') subdir('qemu-api') diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 1ea86b8bbf..62068352b0 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -35,32 +35,15 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [libc_rs, qemu_api_macros], + dependencies: [libc_rs, qemu_api_macros, qemuutil_rs, + qom, hwcore, chardev, migration], ) rust.test('rust-qemu-api-tests', _qemu_api_rs, suite: ['unit', 'rust']) -qemu_api = declare_dependency(link_with: _qemu_api_rs) - -# Rust executables do not support objects, so add an intermediate step. -rust_qemu_api_objs = static_library( - 'rust_qemu_api_objs', - objects: [libqom.extract_all_objects(recursive: false), - libhwcore.extract_all_objects(recursive: false), - libchardev.extract_all_objects(recursive: false), - libcrypto.extract_all_objects(recursive: false), - libauthz.extract_all_objects(recursive: false), - libio.extract_all_objects(recursive: false), - libmigration.extract_all_objects(recursive: false)]) -rust_qemu_api_deps = declare_dependency( - dependencies: [ - qom_ss.dependencies(), - chardev_ss.dependencies(), - crypto_ss.dependencies(), - authz_ss.dependencies(), - io_ss.dependencies()], - link_whole: [rust_qemu_api_objs, libqemuutil]) +qemu_api = declare_dependency(link_with: [_qemu_api_rs], + dependencies: [qemu_api_macros, qom, hwcore, chardev, migration]) test('rust-qemu-api-integration', executable( @@ -69,7 +52,7 @@ test('rust-qemu-api-integration', override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_args: ['--test'], install: false, - dependencies: [qemu_api, qemu_api_macros, rust_qemu_api_deps]), + dependencies: [qemu_api]), args: [ '--test', '--test-threads', '1', '--format', 'pretty', From 53de966c3e8e6b9db3a81e8081be8e8275a0c6ee Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 2 May 2025 19:06:43 +0200 Subject: [PATCH 1372/2760] build, dockerfiles: add support for detecting rustdoc rustdoc is effectively a custom version of rustc, and it is necessary to specify it in order to run doctests from Meson. Add the relevant configure option and environment variables. Signed-off-by: Paolo Bonzini --- configure | 8 ++++++++ meson.build | 2 ++ tests/docker/dockerfiles/fedora-rust-nightly.docker | 2 ++ tests/docker/dockerfiles/ubuntu2204.docker | 1 + tests/lcitool/refresh | 3 +++ 5 files changed, 16 insertions(+) diff --git a/configure b/configure index 74b3865e51..2b2b3d6597 100755 --- a/configure +++ b/configure @@ -209,6 +209,8 @@ for opt do ;; --rustc=*) RUSTC="$optarg" ;; + --rustdoc=*) RUSTDOC="$optarg" + ;; --cpu=*) cpu="$optarg" ;; --extra-cflags=*) @@ -323,6 +325,7 @@ pkg_config="${PKG_CONFIG-${cross_prefix}pkg-config}" sdl2_config="${SDL2_CONFIG-${cross_prefix}sdl2-config}" rustc="${RUSTC-rustc}" +rustdoc="${RUSTDOC-rustdoc}" check_define() { cat > $TMPC <> $cross + echo "rustdoc = [$(meson_quote $rustdoc --target "$rust_target_triple")]" >> $cross else echo "rust = [$(meson_quote $rustc)]" >> $cross + echo "rustdoc = [$(meson_quote $rustdoc)]" >> $cross fi fi echo "ar = [$(meson_quote $ar)]" >> $cross diff --git a/meson.build b/meson.build index ef994676fe..e85b358d63 100644 --- a/meson.build +++ b/meson.build @@ -106,6 +106,7 @@ if have_rust endif if have_rust + rustdoc = find_program('rustdoc', required: get_option('rust')) bindgen = find_program('bindgen', required: get_option('rust')) if not bindgen.found() or bindgen.version().version_compare('<0.60.0') if get_option('rust').enabled() @@ -4757,6 +4758,7 @@ if have_rust summary_info += {'Rust target': config_host['RUST_TARGET_TRIPLE']} summary_info += {'rustc': ' '.join(rustc.cmd_array())} summary_info += {'rustc version': rustc.version()} + summary_info += {'rustdoc': rustdoc} summary_info += {'bindgen': bindgen.full_path()} summary_info += {'bindgen version': bindgen.version()} endif diff --git a/tests/docker/dockerfiles/fedora-rust-nightly.docker b/tests/docker/dockerfiles/fedora-rust-nightly.docker index fe4a6ed48d..4a033309b3 100644 --- a/tests/docker/dockerfiles/fedora-rust-nightly.docker +++ b/tests/docker/dockerfiles/fedora-rust-nightly.docker @@ -156,6 +156,7 @@ ENV PYTHON "/usr/bin/python3" RUN dnf install -y wget ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc +ENV RUSTDOC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustdoc ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo RUN set -eux && \ rustArch='x86_64-unknown-linux-gnu' && \ @@ -170,6 +171,7 @@ RUN set -eux && \ /usr/local/cargo/bin/rustup run nightly cargo --version && \ /usr/local/cargo/bin/rustup run nightly rustc --version && \ test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \ + test "$RUSTDOC" = "$(/usr/local/cargo/bin/rustup +nightly which rustdoc)" && \ test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)" ENV PATH=$CARGO_HOME/bin:$PATH RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index 4a1cf2bdff..28a6f93243 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -151,6 +151,7 @@ ENV MAKE "/usr/bin/make" ENV NINJA "/usr/bin/ninja" ENV PYTHON "/usr/bin/python3" ENV RUSTC=/usr/bin/rustc-1.77 +ENV RUSTDOC=/usr/bin/rustdoc-1.77 ENV CARGO_HOME=/usr/local/cargo ENV PATH=$CARGO_HOME/bin:$PATH RUN DEBIAN_FRONTEND=noninteractive eatmydata \ diff --git a/tests/lcitool/refresh b/tests/lcitool/refresh index 8474ea822f..d3488b2679 100755 --- a/tests/lcitool/refresh +++ b/tests/lcitool/refresh @@ -121,6 +121,7 @@ fedora_rustup_nightly_extras = [ "RUN dnf install -y wget\n", "ENV RUSTUP_HOME=/usr/local/rustup CARGO_HOME=/usr/local/cargo\n", "ENV RUSTC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustc\n", + "ENV RUSTDOC=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/rustdoc\n", "ENV CARGO=/usr/local/rustup/toolchains/nightly-x86_64-unknown-linux-gnu/bin/cargo\n", "RUN set -eux && \\\n", " rustArch='x86_64-unknown-linux-gnu' && \\\n", @@ -135,6 +136,7 @@ fedora_rustup_nightly_extras = [ " /usr/local/cargo/bin/rustup run nightly cargo --version && \\\n", " /usr/local/cargo/bin/rustup run nightly rustc --version && \\\n", ' test "$CARGO" = "$(/usr/local/cargo/bin/rustup +nightly which cargo)" && \\\n', + ' test "$RUSTDOC" = "$(/usr/local/cargo/bin/rustup +nightly which rustdoc)" && \\\n', ' test "$RUSTC" = "$(/usr/local/cargo/bin/rustup +nightly which rustc)"\n', 'ENV PATH=$CARGO_HOME/bin:$PATH\n', 'RUN /usr/local/cargo/bin/rustup run nightly cargo install bindgen-cli\n', @@ -143,6 +145,7 @@ fedora_rustup_nightly_extras = [ ubuntu2204_rust_extras = [ "ENV RUSTC=/usr/bin/rustc-1.77\n", + "ENV RUSTDOC=/usr/bin/rustdoc-1.77\n", "ENV CARGO_HOME=/usr/local/cargo\n", 'ENV PATH=$CARGO_HOME/bin:$PATH\n', "RUN DEBIAN_FRONTEND=noninteractive eatmydata \\\n", From f620cadc0c24ae414b46204e57a82f7bf586d2c4 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 5 Apr 2025 10:33:09 +0200 Subject: [PATCH 1373/2760] rust: add qemu-api doctests to "meson test" Doctests are weird. They are essentially integration tests, but they're "ran" by executing rustdoc --test, which takes a compiler-ish command line. This is supported by Meson 1.8.0. Because they run the linker and need all the .o files, run them in the build jobs rather than the test jobs. Signed-off-by: Paolo Bonzini --- .gitlab-ci.d/buildtest-template.yml | 3 ++- .gitlab-ci.d/buildtest.yml | 11 +++-------- docs/devel/rust.rst | 2 ++ rust/qemu-api/meson.build | 9 +++++++++ 4 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index 118371e377..fea4e8da2f 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -76,7 +76,8 @@ fi - section_end buildenv - section_start test "Running tests" - - $MAKE NINJA=":" $MAKE_CHECK_ARGS + # doctests need all the compilation artifacts + - $MAKE NINJA=":" MTESTARGS="--no-suite doc" $MAKE_CHECK_ARGS - section_end test .native_test_job_template: diff --git a/.gitlab-ci.d/buildtest.yml b/.gitlab-ci.d/buildtest.yml index ca1a9c6f70..d888a60063 100644 --- a/.gitlab-ci.d/buildtest.yml +++ b/.gitlab-ci.d/buildtest.yml @@ -41,7 +41,7 @@ build-system-ubuntu: IMAGE: ubuntu2204 CONFIGURE_ARGS: --enable-docs --enable-rust TARGETS: alpha-softmmu microblazeel-softmmu mips64el-softmmu - MAKE_CHECK_ARGS: check-build + MAKE_CHECK_ARGS: check-build check-doc check-system-ubuntu: extends: .native_test_job_template @@ -115,7 +115,7 @@ build-system-fedora: CONFIGURE_ARGS: --disable-gcrypt --enable-nettle --enable-docs --enable-crypto-afalg --enable-rust TARGETS: microblaze-softmmu mips-softmmu xtensa-softmmu m68k-softmmu riscv32-softmmu ppc-softmmu sparc64-softmmu - MAKE_CHECK_ARGS: check-build + MAKE_CHECK_ARGS: check-build check-doc build-system-fedora-rust-nightly: extends: @@ -127,12 +127,7 @@ build-system-fedora-rust-nightly: IMAGE: fedora-rust-nightly CONFIGURE_ARGS: --disable-docs --enable-rust --enable-strict-rust-lints TARGETS: aarch64-softmmu - MAKE_CHECK_ARGS: check-build - after_script: - - source scripts/ci/gitlab-ci-section - - section_start test "Running Rust doctests" - - cd build - - pyvenv/bin/meson devenv -w ../rust ${CARGO-cargo} test --doc -p qemu_api + MAKE_CHECK_ARGS: check-build check-doc allow_failure: true diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 11328c05b4..f66f2bef60 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -66,6 +66,8 @@ be run via ``meson test`` or ``make``:: make check-rust +Note that doctests require all ``.o`` files from the build to be available. + Supported tools ''''''''''''''' diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 62068352b0..b532281e8c 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -45,6 +45,15 @@ rust.test('rust-qemu-api-tests', _qemu_api_rs, qemu_api = declare_dependency(link_with: [_qemu_api_rs], dependencies: [qemu_api_macros, qom, hwcore, chardev, migration]) +# Doctests are essentially integration tests, so they need the same dependencies. +# Note that running them requires the object files for C code, so place them +# in a separate suite that is run by the "build" CI jobs rather than "check". +rust.doctest('rust-qemu-api-doctests', + _qemu_api_rs, + protocol: 'rust', + dependencies: qemu_api, + suite: ['doc', 'rust']) + test('rust-qemu-api-integration', executable( 'rust-qemu-api-integration', From 18c9f4a1729db1389218983379f6d62a9e550754 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 5 Apr 2025 12:05:21 +0200 Subject: [PATCH 1374/2760] rust: cell: remove support for running doctests with "cargo test --doc" This is not needed anymore now that tests link with libqemuutil. Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/cell.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/rust/qemu-api/src/cell.rs b/rust/qemu-api/src/cell.rs index 05ce09f6cb..27063b049d 100644 --- a/rust/qemu-api/src/cell.rs +++ b/rust/qemu-api/src/cell.rs @@ -225,27 +225,23 @@ use crate::bindings; /// An internal function that is used by doctests. pub fn bql_start_test() { - if cfg!(MESON) { - // SAFETY: integration tests are run with --test-threads=1, while - // unit tests and doctests are not multithreaded and do not have - // any BQL-protected data. Just set bql_locked to true. - unsafe { - bindings::rust_bql_mock_lock(); - } + // SAFETY: integration tests are run with --test-threads=1, while + // unit tests and doctests are not multithreaded and do not have + // any BQL-protected data. Just set bql_locked to true. + unsafe { + bindings::rust_bql_mock_lock(); } } pub fn bql_locked() -> bool { // SAFETY: the function does nothing but return a thread-local bool - !cfg!(MESON) || unsafe { bindings::bql_locked() } + unsafe { bindings::bql_locked() } } fn bql_block_unlock(increase: bool) { - if cfg!(MESON) { - // SAFETY: this only adjusts a counter - unsafe { - bindings::bql_block_unlock(increase); - } + // SAFETY: this only adjusts a counter + unsafe { + bindings::bql_block_unlock(increase); } } From 2409089b87692700deb38fc0b8ac94e31b70ffc3 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 5 Apr 2025 14:18:17 +0200 Subject: [PATCH 1375/2760] rust: use native Meson support for clippy and rustdoc Meson has support for invoking clippy and rustdoc on all crates (1.7.0 for clippy, 1.8.0 for rustdoc). Use it instead of the homegrown version; this requires disabling the multiple_crate_versions lint (the only one that was enabled from the "cargo" group)---which was not particularly useful anyway because all dependencies are converted by hand into Meson subprojects. rustfmt is still not supported. Signed-off-by: Paolo Bonzini --- rust/clippy.toml => clippy.toml | 2 +- docs/devel/rust.rst | 10 +++++++--- meson.build | 2 +- rust/Cargo.toml | 1 - rust/meson.build | 12 ------------ scripts/rust/rustc_args.py | 5 +---- 6 files changed, 10 insertions(+), 22 deletions(-) rename rust/clippy.toml => clippy.toml (55%) diff --git a/rust/clippy.toml b/clippy.toml similarity index 55% rename from rust/clippy.toml rename to clippy.toml index 58a62c0e63..9016172983 100644 --- a/rust/clippy.toml +++ b/clippy.toml @@ -1,3 +1,3 @@ -doc-valid-idents = ["PrimeCell", ".."] +doc-valid-idents = ["IrDA", "PrimeCell", ".."] allow-mixed-uninlined-format-args = false msrv = "1.77.0" diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index f66f2bef60..34d9c7945b 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -37,12 +37,16 @@ output directory (typically ``rust/target/``). A vanilla invocation of Cargo will complain that it cannot find the generated sources, which can be fixed in different ways: -* by using special shorthand targets in the QEMU build directory:: +* by using Makefile targets, provided by Meson, that run ``clippy`` or + ``rustdoc``: make clippy - make rustfmt make rustdoc +A target for ``rustfmt`` is also declared in ``rust/meson.build``: + + make rustfmt + * by invoking ``cargo`` through the Meson `development environment`__ feature:: @@ -50,7 +54,7 @@ which can be fixed in different ways: pyvenv/bin/meson devenv -w ../rust cargo fmt If you are going to use ``cargo`` repeatedly, ``pyvenv/bin/meson devenv`` - will enter a shell where commands like ``cargo clippy`` just work. + will enter a shell where commands like ``cargo fmt`` just work. __ https://mesonbuild.com/Commands.html#devenv diff --git a/meson.build b/meson.build index e85b358d63..651bd4f727 100644 --- a/meson.build +++ b/meson.build @@ -4404,7 +4404,7 @@ foreach target : target_dirs build_by_default: true, build_always_stale: true) rlib = static_library('rust_' + target.underscorify(), - rlib_rs, + structured_sources([], {'.': rlib_rs}), dependencies: target_rust.dependencies(), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'c') diff --git a/rust/Cargo.toml b/rust/Cargo.toml index d9faeecb10..a00b8ad0bc 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -63,7 +63,6 @@ ignored_unit_patterns = "deny" implicit_clone = "deny" macro_use_imports = "deny" missing_safety_doc = "deny" -multiple_crate_versions = "deny" mut_mut = "deny" needless_bitwise_bool = "deny" needless_pass_by_ref_mut = "deny" diff --git a/rust/meson.build b/rust/meson.build index 801f4374df..afce62f477 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -24,21 +24,9 @@ subdir('hw') cargo = find_program('cargo', required: false) if cargo.found() - run_target('clippy', - command: [config_host['MESON'], 'devenv', - '--workdir', '@CURRENT_SOURCE_DIR@', - cargo, 'clippy', '--tests'], - depends: bindings_rs) - run_target('rustfmt', command: [config_host['MESON'], 'devenv', '--workdir', '@CURRENT_SOURCE_DIR@', cargo, 'fmt'], depends: bindings_rs) - - run_target('rustdoc', - command: [config_host['MESON'], 'devenv', - '--workdir', '@CURRENT_SOURCE_DIR@', - cargo, 'doc', '--no-deps', '--document-private-items'], - depends: bindings_rs) endif diff --git a/scripts/rust/rustc_args.py b/scripts/rust/rustc_args.py index 2633157df2..63b0748e0d 100644 --- a/scripts/rust/rustc_args.py +++ b/scripts/rust/rustc_args.py @@ -104,10 +104,7 @@ def generate_lint_flags(cargo_toml: CargoTOML, strict_lints: bool) -> Iterable[s else: raise Exception(f"invalid level {level} for {prefix}{lint}") - # This may change if QEMU ever invokes clippy-driver or rustdoc by - # hand. For now, check the syntax but do not add non-rustc lints to - # the command line. - if k == "rust" and not (strict_lints and lint in STRICT_LINTS): + if not (strict_lints and lint in STRICT_LINTS): lint_list.append(LintFlag(flags=[flag, prefix + lint], priority=priority)) if strict_lints: From 0b901459a87a7fdbed36e574aae33e0635a3e9af Mon Sep 17 00:00:00 2001 From: "Xin Li (Intel)" Date: Fri, 3 Jan 2025 00:48:25 -0800 Subject: [PATCH 1376/2760] target/i386: Remove FRED dependency on WRMSRNS WRMSRNS doesn't become a required feature for FERD, and Linux has removed the dependency, as such remove it from Qemu. Cc: qemu-stable@nongnu.org Signed-off-by: Xin Li (Intel) Reviewed-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250103084827.1820007-2-xin@zytor.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index c9bd3444e4..73403505c5 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1803,10 +1803,6 @@ static FeatureDep feature_dependencies[] = { .from = { FEAT_7_1_EAX, CPUID_7_1_EAX_LKGS }, .to = { FEAT_7_1_EAX, CPUID_7_1_EAX_FRED }, }, - { - .from = { FEAT_7_1_EAX, CPUID_7_1_EAX_WRMSRNS }, - .to = { FEAT_7_1_EAX, CPUID_7_1_EAX_FRED }, - }, { .from = { FEAT_7_0_EBX, CPUID_7_0_EBX_SGX }, .to = { FEAT_7_0_ECX, CPUID_7_0_ECX_SGX_LC }, From 99216748fd4e2b25dc0a7609e8a4fecea4c3eaf4 Mon Sep 17 00:00:00 2001 From: "Xin Li (Intel)" Date: Fri, 3 Jan 2025 00:48:26 -0800 Subject: [PATCH 1377/2760] target/i386: Add a new CPU feature word for CPUID.7.1.ECX The immediate form of MSR access instructions will use this new CPU feature word. Signed-off-by: Xin Li (Intel) Link: https://lore.kernel.org/r/20250103084827.1820007-3-xin@zytor.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 23 ++++++++++++++++++++++- target/i386/cpu.h | 1 + 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 73403505c5..b05f465e2e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -900,6 +900,7 @@ void x86_cpu_vendor_words2str(char *dst, uint32_t vendor1, #define TCG_7_1_EAX_FEATURES (CPUID_7_1_EAX_FZRM | CPUID_7_1_EAX_FSRS | \ CPUID_7_1_EAX_FSRC | CPUID_7_1_EAX_CMPCCXADD) +#define TCG_7_1_ECX_FEATURES 0 #define TCG_7_1_EDX_FEATURES 0 #define TCG_7_2_EDX_FEATURES 0 #define TCG_APM_FEATURES 0 @@ -1150,6 +1151,25 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { }, .tcg_features = TCG_7_1_EAX_FEATURES, }, + [FEAT_7_1_ECX] = { + .type = CPUID_FEATURE_WORD, + .feat_names = { + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, + }, + .cpuid = { + .eax = 7, + .needs_ecx = true, .ecx = 1, + .reg = R_ECX, + }, + .tcg_features = TCG_7_1_ECX_FEATURES, + }, [FEAT_7_1_EDX] = { .type = CPUID_FEATURE_WORD, .feat_names = { @@ -7442,9 +7462,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = env->features[FEAT_7_0_EDX]; /* Feature flags */ } else if (count == 1) { *eax = env->features[FEAT_7_1_EAX]; + *ecx = env->features[FEAT_7_1_ECX]; *edx = env->features[FEAT_7_1_EDX]; *ebx = 0; - *ecx = 0; } else if (count == 2) { *edx = env->features[FEAT_7_2_EDX]; *eax = 0; @@ -8349,6 +8369,7 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) x86_cpu_adjust_feat_level(cpu, FEAT_6_EAX); x86_cpu_adjust_feat_level(cpu, FEAT_7_0_ECX); x86_cpu_adjust_feat_level(cpu, FEAT_7_1_EAX); + x86_cpu_adjust_feat_level(cpu, FEAT_7_1_ECX); x86_cpu_adjust_feat_level(cpu, FEAT_7_1_EDX); x86_cpu_adjust_feat_level(cpu, FEAT_7_2_EDX); x86_cpu_adjust_feat_level(cpu, FEAT_8000_0001_EDX); diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 1146465c8c..0ec1cbd9f4 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -668,6 +668,7 @@ typedef enum FeatureWord { FEAT_SGX_12_1_EAX, /* CPUID[EAX=0x12,ECX=1].EAX (SGX ATTRIBUTES[31:0]) */ FEAT_XSAVE_XSS_LO, /* CPUID[EAX=0xd,ECX=1].ECX */ FEAT_XSAVE_XSS_HI, /* CPUID[EAX=0xd,ECX=1].EDX */ + FEAT_7_1_ECX, /* CPUID[EAX=7,ECX=1].ECX */ FEAT_7_1_EDX, /* CPUID[EAX=7,ECX=1].EDX */ FEAT_7_2_EDX, /* CPUID[EAX=7,ECX=2].EDX */ FEAT_24_0_EBX, /* CPUID[EAX=0x24,ECX=0].EBX */ From 91084f3b44b1da4935eec85b79c1f97d1c140ada Mon Sep 17 00:00:00 2001 From: "Xin Li (Intel)" Date: Fri, 3 Jan 2025 00:48:27 -0800 Subject: [PATCH 1378/2760] target/i386: Add the immediate form MSR access instruction support The immediate form of MSR access instructions are primarily motivated by performance, not code size: by having the MSR number in an immediate, it is available *much* earlier in the pipeline, which allows the hardware much more leeway about how a particular MSR is handled. Signed-off-by: Xin Li (Intel) Link: https://lore.kernel.org/r/20250103084827.1820007-4-xin@zytor.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 +- target/i386/cpu.h | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index b05f465e2e..40aefb38f6 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -1155,7 +1155,7 @@ FeatureWordInfo feature_word_info[FEATURE_WORDS] = { .type = CPUID_FEATURE_WORD, .feat_names = { NULL, NULL, NULL, NULL, - NULL, NULL, NULL, NULL, + NULL, "msr-imm", NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 0ec1cbd9f4..545851cbde 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1001,6 +1001,9 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); /* Linear Address Masking */ #define CPUID_7_1_EAX_LAM (1U << 26) +/* The immediate form of MSR access instructions */ +#define CPUID_7_1_ECX_MSR_IMM (1U << 5) + /* Support for VPDPB[SU,UU,SS]D[,S] */ #define CPUID_7_1_EDX_AVX_VNNI_INT8 (1U << 4) /* AVX NE CONVERT Instructions */ @@ -1024,6 +1027,7 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); #define CPUID_7_2_EDX_DDPD_U (1U << 3) /* Indicate bit 10 of the IA32_SPEC_CTRL MSR is supported */ #define CPUID_7_2_EDX_BHI_CTRL (1U << 4) + /* Do not exhibit MXCSR Configuration Dependent Timing (MCDT) behavior */ #define CPUID_7_2_EDX_MCDT_NO (1U << 5) From 34d697f427d853e4cc14bb369b9134fe4b663cda Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Mon, 2 Jun 2025 16:38:01 -0700 Subject: [PATCH 1379/2760] meson: use config_base_arch for target libraries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixed commit introduced common dependencies for target libraries. Alas, it wrongly reused the 'target' variable, which was previously set from another loop. Thus, some dependencies were missing depending on order of target list, as found here [1]. The fix is to use the correct config_base_arch instead. Kudos to Thomas Huth who had this right, before I reimplement it, and introduce this bug. [1] https://lore.kernel.org/qemu-devel/c54469ce-0385-4aea-b345-47711e9e61de@linaro.org/ Fixes: 4fb54de823e9 (meson: build target libraries with common dependencies) Signed-off-by: Pierrick Bouvier Reviewed-by: Thomas Huth Tested-by: Cédric Le Goater Link: https://lore.kernel.org/r/20250602233801.2699961-1-pierrick.bouvier@linaro.org Signed-off-by: Paolo Bonzini --- meson.build | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/meson.build b/meson.build index 651bd4f727..967a10e80b 100644 --- a/meson.build +++ b/meson.build @@ -4135,13 +4135,12 @@ common_all = static_library('common', target_common_arch_libs = {} target_common_system_arch_libs = {} foreach target_base_arch, config_base_arch : config_base_arch_mak - config_target = config_target_mak[target] target_inc = [include_directories('target' / target_base_arch)] inc = [common_user_inc + target_inc] - target_common = common_ss.apply(config_target, strict: false) - target_system = system_ss.apply(config_target, strict: false) - target_user = user_ss.apply(config_target, strict: false) + target_common = common_ss.apply(config_base_arch, strict: false) + target_system = system_ss.apply(config_base_arch, strict: false) + target_user = user_ss.apply(config_base_arch, strict: false) common_deps = [] system_deps = [] user_deps = [] From e7f926eb7f5b81c709313974b476ed181c9c76d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 2 Jun 2025 19:31:00 +0200 Subject: [PATCH 1380/2760] i386/tdx: Fix build on 32-bit host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use PRI formats where required and fix pointer cast. Cc: Xiaoyao Li Signed-off-by: Cédric Le Goater Link: https://lore.kernel.org/r/20250602173101.1052983-2-clg@redhat.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 0a21ae555c..820ca3614e 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -284,7 +284,7 @@ static void tdx_post_init_vcpus(void) hob = tdx_get_hob_entry(tdx_guest); CPU_FOREACH(cpu) { - tdx_vcpu_ioctl(cpu, KVM_TDX_INIT_VCPU, 0, (void *)hob->address, + tdx_vcpu_ioctl(cpu, KVM_TDX_INIT_VCPU, 0, (void *)(uintptr_t)hob->address, &error_fatal); } } @@ -339,7 +339,7 @@ static void tdx_finalize_vm(Notifier *notifier, void *unused) uint32_t flags; region = (struct kvm_tdx_init_mem_region) { - .source_addr = (uint64_t)entry->mem_ptr, + .source_addr = (uintptr_t)entry->mem_ptr, .gpa = entry->address, .nr_pages = entry->size >> 12, }; @@ -893,16 +893,16 @@ static int tdx_check_features(X86ConfidentialGuest *cg, CPUState *cs) static int tdx_validate_attributes(TdxGuest *tdx, Error **errp) { if ((tdx->attributes & ~tdx_caps->supported_attrs)) { - error_setg(errp, "Invalid attributes 0x%lx for TDX VM " - "(KVM supported: 0x%llx)", tdx->attributes, - tdx_caps->supported_attrs); + error_setg(errp, "Invalid attributes 0x%"PRIx64" for TDX VM " + "(KVM supported: 0x%"PRIx64")", tdx->attributes, + (uint64_t)tdx_caps->supported_attrs); return -1; } if (tdx->attributes & ~TDX_SUPPORTED_TD_ATTRS) { error_setg(errp, "Some QEMU unsupported TD attribute bits being " - "requested: 0x%lx (QEMU supported: 0x%llx)", - tdx->attributes, TDX_SUPPORTED_TD_ATTRS); + "requested: 0x%"PRIx64" (QEMU supported: 0x%"PRIx64")", + tdx->attributes, (uint64_t)TDX_SUPPORTED_TD_ATTRS); return -1; } @@ -931,8 +931,8 @@ static int setup_td_xfam(X86CPU *x86cpu, Error **errp) env->features[FEAT_XSAVE_XSS_HI]; if (xfam & ~tdx_caps->supported_xfam) { - error_setg(errp, "Invalid XFAM 0x%lx for TDX VM (supported: 0x%llx))", - xfam, tdx_caps->supported_xfam); + error_setg(errp, "Invalid XFAM 0x%"PRIx64" for TDX VM (supported: 0x%"PRIx64"))", + xfam, (uint64_t)tdx_caps->supported_xfam); return -1; } @@ -999,14 +999,14 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) if (env->tsc_khz && (env->tsc_khz < TDX_MIN_TSC_FREQUENCY_KHZ || env->tsc_khz > TDX_MAX_TSC_FREQUENCY_KHZ)) { - error_setg(errp, "Invalid TSC %ld KHz, must specify cpu_frequency " + error_setg(errp, "Invalid TSC %"PRId64" KHz, must specify cpu_frequency " "between [%d, %d] kHz", env->tsc_khz, TDX_MIN_TSC_FREQUENCY_KHZ, TDX_MAX_TSC_FREQUENCY_KHZ); return -EINVAL; } if (env->tsc_khz % (25 * 1000)) { - error_setg(errp, "Invalid TSC %ld KHz, it must be multiple of 25MHz", + error_setg(errp, "Invalid TSC %"PRId64" KHz, it must be multiple of 25MHz", env->tsc_khz); return -EINVAL; } @@ -1014,7 +1014,7 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) /* it's safe even env->tsc_khz is 0. KVM uses host's tsc_khz in this case */ r = kvm_vm_ioctl(kvm_state, KVM_SET_TSC_KHZ, env->tsc_khz); if (r < 0) { - error_setg_errno(errp, -r, "Unable to set TSC frequency to %ld kHz", + error_setg_errno(errp, -r, "Unable to set TSC frequency to %"PRId64" kHz", env->tsc_khz); return r; } @@ -1139,7 +1139,7 @@ int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) uint64_t gpa = -1ull; if (error_code & 0xffff) { - error_report("TDX: REPORT_FATAL_ERROR: invalid error code: 0x%lx", + error_report("TDX: REPORT_FATAL_ERROR: invalid error code: 0x%"PRIx64, error_code); return -1; } From 6f1035fc65406c4e72e1dbd76e64924415edd616 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 2 Jun 2025 19:31:01 +0200 Subject: [PATCH 1381/2760] i386/tdvf: Fix build on 32-bit host MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use PRI formats where required. Cc: Isaku Yamahata Signed-off-by: Cédric Le Goater Link: https://lore.kernel.org/r/20250602173101.1052983-3-clg@redhat.com Signed-off-by: Paolo Bonzini --- hw/i386/tdvf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/i386/tdvf.c b/hw/i386/tdvf.c index bd993ea2f0..645d9d1294 100644 --- a/hw/i386/tdvf.c +++ b/hw/i386/tdvf.c @@ -101,16 +101,16 @@ static int tdvf_parse_and_check_section_entry(const TdvfSectionEntry *src, /* sanity check */ if (entry->size < entry->data_len) { - error_report("Broken metadata RawDataSize 0x%x MemoryDataSize 0x%lx", + error_report("Broken metadata RawDataSize 0x%x MemoryDataSize 0x%"PRIx64, entry->data_len, entry->size); return -1; } if (!QEMU_IS_ALIGNED(entry->address, TDVF_ALIGNMENT)) { - error_report("MemoryAddress 0x%lx not page aligned", entry->address); + error_report("MemoryAddress 0x%"PRIx64" not page aligned", entry->address); return -1; } if (!QEMU_IS_ALIGNED(entry->size, TDVF_ALIGNMENT)) { - error_report("MemoryDataSize 0x%lx not page aligned", entry->size); + error_report("MemoryDataSize 0x%"PRIx64" not page aligned", entry->size); return -1; } From 648fe157d33436f042d6b6434b9b88079f67fa33 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 3 Jun 2025 17:44:54 +0200 Subject: [PATCH 1382/2760] rust: add "bits", a custom bitflags implementation One common thing that device emulation does is manipulate bitmasks, for example to check whether two bitmaps have common bits. One example in the pl011 crate is the checks for pending interrupts, where an interrupt cause corresponds to at least one interrupt source from a fixed set. Unfortunately, this is one case where Rust *can* provide some kind of abstraction but it does so with a rather Perl-ish There Is More Way To Do It. It is not something where a crate like "bilge" helps, because it only covers the packing of bits in a structure; operations like "are all bits of Y set in X" almost never make sense for bit-packed structs; you need something else, there are several crates that do it and of course we're going to roll our own. In particular I examined three: - bitmask (https://docs.rs/bitmask/0.5.0/bitmask/) does not support const at all. This is a showstopper because one of the ugly things in the current pl011 code is the ugliness of code that defines interrupt masks at compile time: pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0); or even worse: const IRQMASK: [u32; 6] = [ Interrupt::E.0 | Interrupt::MS.0 | Interrupt::RT.0 | Interrupt::TX.0 | Interrupt::RX.0, ... } You would have to use roughly the same code---"bitmask" only helps with defining the struct. - bitmask_enum (https://docs.rs/bitmask-enum/2.2.5/bitmask_enum/) does not have a good separation of "valid" and "invalid" bits, so for example "!x" will invert all 16 bits if you choose u16 as the representation -- even if you only defined 10 bits. This makes it easier to introduce subtle bugs in comparisons. - bitflags (https://docs.rs/bitflags/2.6.0/bitflags/) is generally the most used such crate and is the one that I took most inspiration from with respect to the syntax. It's a pretty sophisticated implementation, with a lot of bells and whistles such as an implementation of "Iter" that returns the bits one at a time. The main thing that all of them lack, however, is a way to simplify the ugly definitions like the above. "bitflags" includes const methods that perform AND/OR/XOR of masks (these are necessary because Rust operator overloading does not support const yet, and therefore overloaded operators cannot be used in the definition of a "static" variable), but they become even more verbose and unmanageable, like Interrupt::E.union(Interrupt::MS).union(Interrupt::RT).union(Interrupt::TX).union(Interrupt::RX) This was the main reason to create "bits", which allows something like bits!(Interrupt: E | MS | RT | TX | RX) and expands it 1) add "Interrupt::" in front of all identifiers 2) convert operators to the wordy const functions like "union". It supports boolean operators "&", "|", "^", "!" and parentheses, with a relatively simple recursive descent parser that's implemented in qemu_api_macros. Since I don't remember exactly how the macro was developed, I cannot exclude that it contains code from "bitflags". Therefore, I am conservatively leaving in the MIT and Apache 2.0 licenses from bitflags. In fact, I think there would be a benefit in being able to push code back to "bitflags" anyway whenever applicable, so that the two libraries do not diverge too much, so that's another reason to use this. Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 7 + rust/Cargo.toml | 1 + rust/bits/Cargo.toml | 19 ++ rust/bits/meson.build | 16 ++ rust/bits/src/lib.rs | 443 +++++++++++++++++++++++++++++++ rust/meson.build | 1 + rust/qemu-api-macros/src/bits.rs | 229 ++++++++++++++++ rust/qemu-api-macros/src/lib.rs | 12 + 8 files changed, 728 insertions(+) create mode 100644 rust/bits/Cargo.toml create mode 100644 rust/bits/meson.build create mode 100644 rust/bits/src/lib.rs create mode 100644 rust/qemu-api-macros/src/bits.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 13d580c693..0dfe0fb6ce 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -31,6 +31,13 @@ dependencies = [ "syn", ] +[[package]] +name = "bits" +version = "0.1.0" +dependencies = [ + "qemu_api_macros", +] + [[package]] name = "either" version = "1.12.0" diff --git a/rust/Cargo.toml b/rust/Cargo.toml index a00b8ad0bc..fd4c2fbf84 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -1,6 +1,7 @@ [workspace] resolver = "2" members = [ + "bits", "qemu-api-macros", "qemu-api", "hw/char/pl011", diff --git a/rust/bits/Cargo.toml b/rust/bits/Cargo.toml new file mode 100644 index 0000000000..1ff38a4117 --- /dev/null +++ b/rust/bits/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "bits" +version = "0.1.0" +authors = ["Paolo Bonzini "] +description = "const-friendly bit flags" +resolver = "2" +publish = false + +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +qemu_api_macros = { path = "../qemu-api-macros" } + +[lints] +workspace = true diff --git a/rust/bits/meson.build b/rust/bits/meson.build new file mode 100644 index 0000000000..2a41e138c5 --- /dev/null +++ b/rust/bits/meson.build @@ -0,0 +1,16 @@ +_bits_rs = static_library( + 'bits', + 'src/lib.rs', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + dependencies: [qemu_api_macros], +) + +bits_rs = declare_dependency(link_with: _bits_rs) + +rust.test('rust-bits-tests', _bits_rs, + suite: ['unit', 'rust']) + +rust.doctest('rust-bits-doctests', _bits_rs, + dependencies: bits_rs, + suite: ['doc', 'rust']) diff --git a/rust/bits/src/lib.rs b/rust/bits/src/lib.rs new file mode 100644 index 0000000000..d485d6bd11 --- /dev/null +++ b/rust/bits/src/lib.rs @@ -0,0 +1,443 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later + +/// # Definition entry point +/// +/// Define a struct with a single field of type $type. Include public constants +/// for each element listed in braces. +/// +/// The unnamed element at the end, if present, can be used to enlarge the set +/// of valid bits. Bits that are valid but not listed are treated normally for +/// the purpose of arithmetic operations, and are printed with their hexadecimal +/// value. +/// +/// The struct implements the following traits: [`BitAnd`](std::ops::BitAnd), +/// [`BitOr`](std::ops::BitOr), [`BitXor`](std::ops::BitXor), +/// [`Not`](std::ops::Not), [`Sub`](std::ops::Sub); [`Debug`](std::fmt::Debug), +/// [`Display`](std::fmt::Display), [`Binary`](std::fmt::Binary), +/// [`Octal`](std::fmt::Octal), [`LowerHex`](std::fmt::LowerHex), +/// [`UpperHex`](std::fmt::UpperHex); [`From`]``/[`Into`]`` where +/// type is the type specified in the definition. +/// +/// ## Example +/// +/// ``` +/// # use bits::bits; +/// bits! { +/// pub struct Colors(u8) { +/// BLACK = 0, +/// RED = 1, +/// GREEN = 1 << 1, +/// BLUE = 1 << 2, +/// WHITE = (1 << 0) | (1 << 1) | (1 << 2), +/// } +/// } +/// ``` +/// +/// ``` +/// # use bits::bits; +/// # bits! { pub struct Colors(u8) { BLACK = 0, RED = 1, GREEN = 1 << 1, BLUE = 1 << 2, } } +/// +/// bits! { +/// pub struct Colors8(u8) { +/// BLACK = 0, +/// RED = 1, +/// GREEN = 1 << 1, +/// BLUE = 1 << 2, +/// WHITE = (1 << 0) | (1 << 1) | (1 << 2), +/// +/// _ = 255, +/// } +/// } +/// +/// // The previously defined struct ignores bits not explicitly defined. +/// assert_eq!( +/// Colors::from(255).into_bits(), +/// (Colors::RED | Colors::GREEN | Colors::BLUE).into_bits() +/// ); +/// +/// // Adding "_ = 255" makes it retain other bits as well. +/// assert_eq!(Colors8::from(255).into_bits(), 255); +/// +/// // all() does not include the additional bits, valid_bits() does +/// assert_eq!(Colors8::all().into_bits(), Colors::all().into_bits()); +/// assert_eq!(Colors8::valid_bits().into_bits(), 255); +/// ``` +/// +/// # Evaluation entry point +/// +/// Return a constant corresponding to the boolean expression `$expr`. +/// Identifiers in the expression correspond to values defined for the +/// type `$type`. Supported operators are `!` (unary), `-`, `&`, `^`, `|`. +/// +/// ## Examples +/// +/// ``` +/// # use bits::bits; +/// bits! { +/// pub struct Colors(u8) { +/// BLACK = 0, +/// RED = 1, +/// GREEN = 1 << 1, +/// BLUE = 1 << 2, +/// // same as "WHITE = 7", +/// WHITE = bits!(Self as u8: RED | GREEN | BLUE), +/// } +/// } +/// +/// let rgb = bits! { Colors: RED | GREEN | BLUE }; +/// assert_eq!(rgb, Colors::WHITE); +/// ``` +#[macro_export] +macro_rules! bits { + { + $(#[$struct_meta:meta])* + $struct_vis:vis struct $struct_name:ident($field_vis:vis $type:ty) { + $($(#[$const_meta:meta])* $const:ident = $val:expr),+ + $(,_ = $mask:expr)? + $(,)? + } + } => { + $(#[$struct_meta])* + #[derive(Clone, Copy, PartialEq, Eq)] + #[repr(transparent)] + $struct_vis struct $struct_name($field_vis $type); + + impl $struct_name { + $( #[allow(dead_code)] $(#[$const_meta])* + pub const $const: $struct_name = $struct_name($val); )+ + + #[doc(hidden)] + const VALID__: $type = $( Self::$const.0 )|+ $(|$mask)?; + + #[allow(dead_code)] + #[inline(always)] + pub const fn empty() -> Self { + Self(0) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn all() -> Self { + Self($( Self::$const.0 )|+) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn valid_bits() -> Self { + Self(Self::VALID__) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn valid(val: $type) -> bool { + (val & !Self::VALID__) == 0 + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn any_set(self, mask: Self) -> bool { + (self.0 & mask.0) != 0 + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn all_set(self, mask: Self) -> bool { + (self.0 & mask.0) == mask.0 + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn none_set(self, mask: Self) -> bool { + (self.0 & mask.0) == 0 + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn from_bits(value: $type) -> Self { + $struct_name(value) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn into_bits(self) -> $type { + self.0 + } + + #[allow(dead_code)] + #[inline(always)] + pub fn set(&mut self, rhs: Self) { + self.0 |= rhs.0; + } + + #[allow(dead_code)] + #[inline(always)] + pub fn clear(&mut self, rhs: Self) { + self.0 &= !rhs.0; + } + + #[allow(dead_code)] + #[inline(always)] + pub fn toggle(&mut self, rhs: Self) { + self.0 ^= rhs.0; + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn intersection(self, rhs: Self) -> Self { + $struct_name(self.0 & rhs.0) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn difference(self, rhs: Self) -> Self { + $struct_name(self.0 & !rhs.0) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn symmetric_difference(self, rhs: Self) -> Self { + $struct_name(self.0 ^ rhs.0) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn union(self, rhs: Self) -> Self { + $struct_name(self.0 | rhs.0) + } + + #[allow(dead_code)] + #[inline(always)] + pub const fn invert(self) -> Self { + $struct_name(self.0 ^ Self::VALID__) + } + } + + impl ::std::fmt::Binary for $struct_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + // If no width, use the highest valid bit + let width = f.width().unwrap_or((Self::VALID__.ilog2() + 1) as usize); + write!(f, "{:0>width$.precision$b}", self.0, + width = width, + precision = f.precision().unwrap_or(width)) + } + } + + impl ::std::fmt::LowerHex for $struct_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + <$type as ::std::fmt::LowerHex>::fmt(&self.0, f) + } + } + + impl ::std::fmt::Octal for $struct_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + <$type as ::std::fmt::Octal>::fmt(&self.0, f) + } + } + + impl ::std::fmt::UpperHex for $struct_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + <$type as ::std::fmt::UpperHex>::fmt(&self.0, f) + } + } + + impl ::std::fmt::Debug for $struct_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + write!(f, "{}({})", stringify!($struct_name), self) + } + } + + impl ::std::fmt::Display for $struct_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result { + use ::std::fmt::Display; + let mut first = true; + let mut left = self.0; + $(if Self::$const.0.is_power_of_two() && (self & Self::$const).0 != 0 { + if first { first = false } else { Display::fmt(&'|', f)?; } + Display::fmt(stringify!($const), f)?; + left -= Self::$const.0; + })+ + if first { + Display::fmt(&'0', f) + } else if left != 0 { + write!(f, "|{left:#x}") + } else { + Ok(()) + } + } + } + + impl ::std::cmp::PartialEq<$type> for $struct_name { + fn eq(&self, rhs: &$type) -> bool { + self.0 == *rhs + } + } + + impl ::std::ops::BitAnd<$struct_name> for &$struct_name { + type Output = $struct_name; + fn bitand(self, rhs: $struct_name) -> Self::Output { + $struct_name(self.0 & rhs.0) + } + } + + impl ::std::ops::BitAndAssign<$struct_name> for $struct_name { + fn bitand_assign(&mut self, rhs: $struct_name) { + self.0 = self.0 & rhs.0 + } + } + + impl ::std::ops::BitXor<$struct_name> for &$struct_name { + type Output = $struct_name; + fn bitxor(self, rhs: $struct_name) -> Self::Output { + $struct_name(self.0 ^ rhs.0) + } + } + + impl ::std::ops::BitXorAssign<$struct_name> for $struct_name { + fn bitxor_assign(&mut self, rhs: $struct_name) { + self.0 = self.0 ^ rhs.0 + } + } + + impl ::std::ops::BitOr<$struct_name> for &$struct_name { + type Output = $struct_name; + fn bitor(self, rhs: $struct_name) -> Self::Output { + $struct_name(self.0 | rhs.0) + } + } + + impl ::std::ops::BitOrAssign<$struct_name> for $struct_name { + fn bitor_assign(&mut self, rhs: $struct_name) { + self.0 = self.0 | rhs.0 + } + } + + impl ::std::ops::Sub<$struct_name> for &$struct_name { + type Output = $struct_name; + fn sub(self, rhs: $struct_name) -> Self::Output { + $struct_name(self.0 & !rhs.0) + } + } + + impl ::std::ops::SubAssign<$struct_name> for $struct_name { + fn sub_assign(&mut self, rhs: $struct_name) { + self.0 = self.0 - rhs.0 + } + } + + impl ::std::ops::Not for &$struct_name { + type Output = $struct_name; + fn not(self) -> Self::Output { + $struct_name(self.0 ^ $struct_name::VALID__) + } + } + + impl ::std::ops::BitAnd<$struct_name> for $struct_name { + type Output = Self; + fn bitand(self, rhs: Self) -> Self::Output { + $struct_name(self.0 & rhs.0) + } + } + + impl ::std::ops::BitXor<$struct_name> for $struct_name { + type Output = Self; + fn bitxor(self, rhs: Self) -> Self::Output { + $struct_name(self.0 ^ rhs.0) + } + } + + impl ::std::ops::BitOr<$struct_name> for $struct_name { + type Output = Self; + fn bitor(self, rhs: Self) -> Self::Output { + $struct_name(self.0 | rhs.0) + } + } + + impl ::std::ops::Sub<$struct_name> for $struct_name { + type Output = Self; + fn sub(self, rhs: Self) -> Self::Output { + $struct_name(self.0 & !rhs.0) + } + } + + impl ::std::ops::Not for $struct_name { + type Output = Self; + fn not(self) -> Self::Output { + $struct_name(self.0 ^ Self::VALID__) + } + } + + impl From<$struct_name> for $type { + fn from(x: $struct_name) -> $type { + x.0 + } + } + + impl From<$type> for $struct_name { + fn from(x: $type) -> Self { + $struct_name(x & Self::VALID__) + } + } + }; + + { $type:ty: $expr:expr } => { + ::qemu_api_macros::bits_const_internal! { $type @ ($expr) } + }; + + { $type:ty as $int_type:ty: $expr:expr } => { + (::qemu_api_macros::bits_const_internal! { $type @ ($expr) }.into_bits()) as $int_type + }; +} + +#[cfg(test)] +mod test { + bits! { + pub struct InterruptMask(u32) { + OE = 1 << 10, + BE = 1 << 9, + PE = 1 << 8, + FE = 1 << 7, + RT = 1 << 6, + TX = 1 << 5, + RX = 1 << 4, + DSR = 1 << 3, + DCD = 1 << 2, + CTS = 1 << 1, + RI = 1 << 0, + + E = bits!(Self as u32: OE | BE | PE | FE), + MS = bits!(Self as u32: RI | DSR | DCD | CTS), + } + } + + #[test] + pub fn test_not() { + assert_eq!( + !InterruptMask::from(InterruptMask::RT.0), + InterruptMask::E | InterruptMask::MS | InterruptMask::TX | InterruptMask::RX + ); + } + + #[test] + pub fn test_and() { + assert_eq!( + InterruptMask::from(0), + InterruptMask::MS & InterruptMask::OE + ) + } + + #[test] + pub fn test_or() { + assert_eq!( + InterruptMask::E, + InterruptMask::OE | InterruptMask::BE | InterruptMask::PE | InterruptMask::FE + ); + } + + #[test] + pub fn test_xor() { + assert_eq!( + InterruptMask::E ^ InterruptMask::BE, + InterruptMask::OE | InterruptMask::PE | InterruptMask::FE + ); + } +} diff --git a/rust/meson.build b/rust/meson.build index afce62f477..b1b3315be9 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -17,6 +17,7 @@ proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true) qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true) subdir('qemu-api-macros') +subdir('bits') subdir('qemu-api') subdir('hw') diff --git a/rust/qemu-api-macros/src/bits.rs b/rust/qemu-api-macros/src/bits.rs new file mode 100644 index 0000000000..5ba84757ee --- /dev/null +++ b/rust/qemu-api-macros/src/bits.rs @@ -0,0 +1,229 @@ +// SPDX-License-Identifier: MIT or Apache-2.0 or GPL-2.0-or-later + +// shadowing is useful together with "if let" +#![allow(clippy::shadow_unrelated)] + +use proc_macro2::{ + Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, TokenTree as TT, +}; + +use crate::utils::MacroError; + +pub struct BitsConstInternal { + typ: TokenTree, +} + +fn paren(ts: TokenStream) -> TokenTree { + TT::Group(Group::new(Delimiter::Parenthesis, ts)) +} + +fn ident(s: &'static str) -> TokenTree { + TT::Ident(Ident::new(s, Span::call_site())) +} + +fn punct(ch: char) -> TokenTree { + TT::Punct(Punct::new(ch, Spacing::Alone)) +} + +/// Implements a recursive-descent parser that translates Boolean expressions on +/// bitmasks to invocations of `const` functions defined by the `bits!` macro. +impl BitsConstInternal { + // primary ::= '(' or ')' + // | ident + // | '!' ident + fn parse_primary( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, MacroError> { + let next = match tok { + TT::Group(ref g) => { + if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None { + return Err(MacroError::Message("expected parenthesis".into(), g.span())); + } + let mut stream = g.stream().into_iter(); + let Some(first_tok) = stream.next() else { + return Err(MacroError::Message( + "expected operand, found ')'".into(), + g.span(), + )); + }; + let mut output = TokenStream::new(); + // start from the lowest precedence + let next = self.parse_or(first_tok, &mut stream, &mut output)?; + if let Some(tok) = next { + return Err(MacroError::Message( + format!("unexpected token {tok}"), + tok.span(), + )); + } + out.extend(Some(paren(output))); + it.next() + } + TT::Ident(_) => { + let mut output = TokenStream::new(); + output.extend([ + self.typ.clone(), + TT::Punct(Punct::new(':', Spacing::Joint)), + TT::Punct(Punct::new(':', Spacing::Joint)), + tok, + ]); + out.extend(Some(paren(output))); + it.next() + } + TT::Punct(ref p) => { + if p.as_char() != '!' { + return Err(MacroError::Message("expected operand".into(), p.span())); + } + let Some(rhs_tok) = it.next() else { + return Err(MacroError::Message( + "expected operand at end of input".into(), + p.span(), + )); + }; + let next = self.parse_primary(rhs_tok, it, out)?; + out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]); + next + } + _ => { + return Err(MacroError::Message("unexpected literal".into(), tok.span())); + } + }; + Ok(next) + } + + fn parse_binop< + F: Fn( + &Self, + TokenTree, + &mut dyn Iterator, + &mut TokenStream, + ) -> Result, MacroError>, + >( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ch: char, + f: F, + method: &'static str, + ) -> Result, MacroError> { + let mut next = f(self, tok, it, out)?; + while next.is_some() { + let op = next.as_ref().unwrap(); + let TT::Punct(ref p) = op else { break }; + if p.as_char() != ch { + break; + } + + let Some(rhs_tok) = it.next() else { + return Err(MacroError::Message( + "expected operand at end of input".into(), + p.span(), + )); + }; + let mut rhs = TokenStream::new(); + next = f(self, rhs_tok, it, &mut rhs)?; + out.extend([punct('.'), ident(method), paren(rhs)]); + } + Ok(next) + } + + // sub ::= primary ('-' primary)* + pub fn parse_sub( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, MacroError> { + self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference") + } + + // and ::= sub ('&' sub)* + fn parse_and( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, MacroError> { + self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection") + } + + // xor ::= and ('&' and)* + fn parse_xor( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, MacroError> { + self.parse_binop(tok, it, out, '^', Self::parse_and, "symmetric_difference") + } + + // or ::= xor ('|' xor)* + pub fn parse_or( + &self, + tok: TokenTree, + it: &mut dyn Iterator, + out: &mut TokenStream, + ) -> Result, MacroError> { + self.parse_binop(tok, it, out, '|', Self::parse_xor, "union") + } + + pub fn parse( + it: &mut dyn Iterator, + ) -> Result { + let mut pos = Span::call_site(); + let mut typ = proc_macro2::TokenStream::new(); + + // Gobble everything up to an `@` sign, which is followed by a + // parenthesized expression; that is, all token trees except the + // last two form the type. + let next = loop { + let tok = it.next(); + if let Some(ref t) = tok { + pos = t.span(); + } + match tok { + None => break None, + Some(TT::Punct(ref p)) if p.as_char() == '@' => { + let tok = it.next(); + if let Some(ref t) = tok { + pos = t.span(); + } + break tok; + } + Some(x) => typ.extend(Some(x)), + } + }; + + let Some(tok) = next else { + return Err(MacroError::Message( + "expected expression, do not call this macro directly".into(), + pos, + )); + }; + let TT::Group(ref _group) = tok else { + return Err(MacroError::Message( + "expected parenthesis, do not call this macro directly".into(), + tok.span(), + )); + }; + let mut out = TokenStream::new(); + let state = Self { + typ: TT::Group(Group::new(Delimiter::None, typ)), + }; + + let next = state.parse_primary(tok, it, &mut out)?; + + // A parenthesized expression is a single production of the grammar, + // so the input must have reached the last token. + if let Some(tok) = next { + return Err(MacroError::Message( + format!("unexpected token {tok}"), + tok.span(), + )); + } + Ok(out) + } +} diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index f97449bb30..ceb79f09f9 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -12,6 +12,9 @@ use syn::{ mod utils; use utils::MacroError; +mod bits; +use bits::BitsConstInternal; + fn get_fields<'a>( input: &'a DeriveInput, msg: &str, @@ -219,3 +222,12 @@ pub fn derive_tryinto(input: TokenStream) -> TokenStream { TokenStream::from(expanded) } + +#[proc_macro] +pub fn bits_const_internal(ts: TokenStream) -> TokenStream { + let ts = proc_macro2::TokenStream::from(ts); + let mut it = ts.into_iter(); + + let expanded = BitsConstInternal::parse(&mut it).unwrap_or_else(Into::into); + TokenStream::from(expanded) +} From 9c8ff2a1ed51b52ac64b80d35bdbd239b7b5d8e5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 3 Jun 2025 17:45:05 +0200 Subject: [PATCH 1383/2760] rust: pl011: use the bits macro This avoids the repeated ".0" when using the Interrupt struct. Signed-off-by: Paolo Bonzini --- rust/Cargo.lock | 1 + rust/hw/char/pl011/Cargo.toml | 1 + rust/hw/char/pl011/meson.build | 1 + rust/hw/char/pl011/src/device.rs | 51 ++++++++++++++--------------- rust/hw/char/pl011/src/registers.rs | 39 ++++++++++++---------- 5 files changed, 49 insertions(+), 44 deletions(-) diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 0dfe0fb6ce..bccfe855a7 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -73,6 +73,7 @@ version = "0.1.0" dependencies = [ "bilge", "bilge-impl", + "bits", "qemu_api", "qemu_api_macros", ] diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index a1f431ab4a..003ef9613d 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -18,6 +18,7 @@ crate-type = ["staticlib"] [dependencies] bilge = { version = "0.2.0" } bilge-impl = { version = "0.2.0" } +bits = { path = "../../../bits" } qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build index 494b6c123c..2a1be329ab 100644 --- a/rust/hw/char/pl011/meson.build +++ b/rust/hw/char/pl011/meson.build @@ -6,6 +6,7 @@ _libpl011_rs = static_library( dependencies: [ bilge_rs, bilge_impl_rs, + bits_rs, qemu_api, qemu_api_macros, ], diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index bd5cee0464..0501fa5be9 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -85,8 +85,8 @@ pub struct PL011Registers { #[doc(alias = "cr")] pub control: registers::Control, pub dmacr: u32, - pub int_enabled: u32, - pub int_level: u32, + pub int_enabled: Interrupt, + pub int_level: Interrupt, pub read_fifo: Fifo, pub ilpr: u32, pub ibrd: u32, @@ -199,9 +199,9 @@ impl PL011Registers { LCR_H => u32::from(self.line_control), CR => u32::from(self.control), FLS => self.ifl, - IMSC => self.int_enabled, - RIS => self.int_level, - MIS => self.int_level & self.int_enabled, + IMSC => u32::from(self.int_enabled), + RIS => u32::from(self.int_level), + MIS => u32::from(self.int_level & self.int_enabled), ICR => { // "The UARTICR Register is the interrupt clear register and is write-only" // Source: ARM DDI 0183G 3.3.13 Interrupt Clear Register, UARTICR @@ -263,13 +263,13 @@ impl PL011Registers { self.set_read_trigger(); } IMSC => { - self.int_enabled = value; + self.int_enabled = Interrupt::from(value); return true; } RIS => {} MIS => {} ICR => { - self.int_level &= !value; + self.int_level &= !Interrupt::from(value); return true; } DMACR => { @@ -295,7 +295,7 @@ impl PL011Registers { self.flags.set_receive_fifo_empty(true); } if self.read_count + 1 == self.read_trigger { - self.int_level &= !Interrupt::RX.0; + self.int_level &= !Interrupt::RX; } self.receive_status_error_clear.set_from_data(c); *update = true; @@ -305,7 +305,7 @@ impl PL011Registers { fn write_data_register(&mut self, value: u32) -> bool { // interrupts always checked let _ = self.loopback_tx(value.into()); - self.int_level |= Interrupt::TX.0; + self.int_level |= Interrupt::TX; true } @@ -361,19 +361,19 @@ impl PL011Registers { // Change interrupts based on updated FR let mut il = self.int_level; - il &= !Interrupt::MS.0; + il &= !Interrupt::MS; if self.flags.data_set_ready() { - il |= Interrupt::DSR.0; + il |= Interrupt::DSR; } if self.flags.data_carrier_detect() { - il |= Interrupt::DCD.0; + il |= Interrupt::DCD; } if self.flags.clear_to_send() { - il |= Interrupt::CTS.0; + il |= Interrupt::CTS; } if self.flags.ring_indicator() { - il |= Interrupt::RI.0; + il |= Interrupt::RI; } self.int_level = il; true @@ -391,8 +391,8 @@ impl PL011Registers { self.line_control.reset(); self.receive_status_error_clear.reset(); self.dmacr = 0; - self.int_enabled = 0; - self.int_level = 0; + self.int_enabled = 0.into(); + self.int_level = 0.into(); self.ilpr = 0; self.ibrd = 0; self.fbrd = 0; @@ -451,7 +451,7 @@ impl PL011Registers { } if self.read_count == self.read_trigger { - self.int_level |= Interrupt::RX.0; + self.int_level |= Interrupt::RX; return true; } false @@ -632,7 +632,7 @@ impl PL011State { let regs = self.regs.borrow(); let flags = regs.int_level & regs.int_enabled; for (irq, i) in self.interrupts.iter().zip(IRQMASK) { - irq.set(flags & i != 0); + irq.set(flags.any_set(i)); } } @@ -642,14 +642,13 @@ impl PL011State { } /// Which bits in the interrupt status matter for each outbound IRQ line ? -const IRQMASK: [u32; 6] = [ - /* combined IRQ */ - Interrupt::E.0 | Interrupt::MS.0 | Interrupt::RT.0 | Interrupt::TX.0 | Interrupt::RX.0, - Interrupt::RX.0, - Interrupt::TX.0, - Interrupt::RT.0, - Interrupt::MS.0, - Interrupt::E.0, +const IRQMASK: [Interrupt; 6] = [ + Interrupt::all(), + Interrupt::RX, + Interrupt::TX, + Interrupt::RT, + Interrupt::MS, + Interrupt::E, ]; /// # Safety diff --git a/rust/hw/char/pl011/src/registers.rs b/rust/hw/char/pl011/src/registers.rs index 690feb6378..7ececd39f8 100644 --- a/rust/hw/char/pl011/src/registers.rs +++ b/rust/hw/char/pl011/src/registers.rs @@ -9,7 +9,8 @@ // https://developer.arm.com/documentation/ddi0183/latest/ use bilge::prelude::*; -use qemu_api::impl_vmstate_bitsized; +use bits::bits; +use qemu_api::{impl_vmstate_bitsized, impl_vmstate_forward}; /// Offset of each register from the base memory address of the device. #[doc(alias = "offset")] @@ -326,22 +327,24 @@ impl Default for Control { } } -/// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC -pub struct Interrupt(pub u32); +bits! { + /// Interrupt status bits in UARTRIS, UARTMIS, UARTIMSC + #[derive(Default)] + pub struct Interrupt(u32) { + OE = 1 << 10, + BE = 1 << 9, + PE = 1 << 8, + FE = 1 << 7, + RT = 1 << 6, + TX = 1 << 5, + RX = 1 << 4, + DSR = 1 << 3, + DCD = 1 << 2, + CTS = 1 << 1, + RI = 1 << 0, -impl Interrupt { - pub const OE: Self = Self(1 << 10); - pub const BE: Self = Self(1 << 9); - pub const PE: Self = Self(1 << 8); - pub const FE: Self = Self(1 << 7); - pub const RT: Self = Self(1 << 6); - pub const TX: Self = Self(1 << 5); - pub const RX: Self = Self(1 << 4); - pub const DSR: Self = Self(1 << 3); - pub const DCD: Self = Self(1 << 2); - pub const CTS: Self = Self(1 << 1); - pub const RI: Self = Self(1 << 0); - - pub const E: Self = Self(Self::OE.0 | Self::BE.0 | Self::PE.0 | Self::FE.0); - pub const MS: Self = Self(Self::RI.0 | Self::DSR.0 | Self::DCD.0 | Self::CTS.0); + E = bits!(Self as u32: OE | BE | PE | FE), + MS = bits!(Self as u32: RI | DSR | DCD | CTS), + } } +impl_vmstate_forward!(Interrupt); From 214518614c1ce7eb7a002452cd43a7597f90d543 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 13 May 2025 13:04:41 +0200 Subject: [PATCH 1384/2760] rust: qemu-api-macros: add from_bits and into_bits to #[derive(TryInto)] These const functions make it possible to use enums easily together with the bitfield-struct crate. Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/lib.rs | 44 +++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index ceb79f09f9..103470785e 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -192,24 +192,52 @@ fn get_variants(input: &DeriveInput) -> Result<&Punctuated, Macr Ok(&e.variants) } +#[rustfmt::skip::macros(quote)] +fn derive_tryinto_body( + name: &Ident, + variants: &Punctuated, + repr: &Path, +) -> Result { + let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect(); + + Ok(quote! { + #(const #discriminants: #repr = #name::#discriminants as #repr;)*; + match value { + #(#discriminants => Ok(#name::#discriminants),)* + _ => Err(value), + } + }) +} + #[rustfmt::skip::macros(quote)] fn derive_tryinto_or_error(input: DeriveInput) -> Result { let repr = get_repr_uN(&input, "#[derive(TryInto)]")?; - let name = &input.ident; - let variants = get_variants(&input)?; - let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect(); + let body = derive_tryinto_body(name, get_variants(&input)?, &repr)?; + let errmsg = format!("invalid value for {name}"); Ok(quote! { + impl #name { + #[allow(dead_code)] + pub const fn into_bits(self) -> #repr { + self as #repr + } + + #[allow(dead_code)] + pub const fn from_bits(value: #repr) -> Self { + match ({ + #body + }) { + Ok(x) => x, + Err(_) => panic!(#errmsg) + } + } + } impl core::convert::TryFrom<#repr> for #name { type Error = #repr; fn try_from(value: #repr) -> Result { - #(const #discriminants: #repr = #name::#discriminants as #repr;)*; - match value { - #(#discriminants => Ok(Self::#discriminants),)* - _ => Err(value), - } + #body } } }) From f1bf3be14bd5d6e6a2cfbbe64cdd4d58a8595d68 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:38 +0200 Subject: [PATCH 1385/2760] block: remove outdated comments about AioContext locking AioContext locking was removed in commit b49f4755c7 ("block: remove AioContext locking"). Signed-off-by: Fiona Ebner Reviewed-by: Kevin Wolf Message-ID: <20250530151125.955508-2-f.ebner@proxmox.com> Signed-off-by: Kevin Wolf --- block.c | 7 ------- 1 file changed, 7 deletions(-) diff --git a/block.c b/block.c index f222e1a50a..a5399888ba 100644 --- a/block.c +++ b/block.c @@ -4359,8 +4359,6 @@ bdrv_recurse_has_child(BlockDriverState *bs, BlockDriverState *child) * bs_queue, or the existing bs_queue being used. * * bs is drained here and undrained by bdrv_reopen_queue_free(). - * - * To be called with bs->aio_context locked. */ static BlockReopenQueue * GRAPH_RDLOCK bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs, @@ -4519,7 +4517,6 @@ bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs, return bs_queue; } -/* To be called with bs->aio_context locked */ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, QDict *options, bool keep_old_opts) @@ -7278,10 +7275,6 @@ bool bdrv_op_blocker_is_empty(BlockDriverState *bs) return true; } -/* - * Must not be called while holding the lock of an AioContext other than the - * current one. - */ void bdrv_img_create(const char *filename, const char *fmt, const char *base_filename, const char *base_fmt, char *options, uint64_t img_size, int flags, bool quiet, From e1d681b3e1d8256047dbfc6d2c796028b9694eaf Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:39 +0200 Subject: [PATCH 1386/2760] block: move drain outside of read-locked bdrv_reopen_queue_child() This is in preparation to mark bdrv_drained_begin() as GRAPH_UNLOCKED. More granular draining is not trivially possible, because bdrv_reopen_queue_child() can recursively call itself. Signed-off-by: Fiona Ebner Reviewed-by: Kevin Wolf Message-ID: <20250530151125.955508-3-f.ebner@proxmox.com> Signed-off-by: Kevin Wolf --- block.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/block.c b/block.c index a5399888ba..3065d5c91e 100644 --- a/block.c +++ b/block.c @@ -4358,7 +4358,7 @@ bdrv_recurse_has_child(BlockDriverState *bs, BlockDriverState *child) * returns a pointer to bs_queue, which is either the newly allocated * bs_queue, or the existing bs_queue being used. * - * bs is drained here and undrained by bdrv_reopen_queue_free(). + * bs must be drained. */ static BlockReopenQueue * GRAPH_RDLOCK bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs, @@ -4377,12 +4377,7 @@ bdrv_reopen_queue_child(BlockReopenQueue *bs_queue, BlockDriverState *bs, GLOBAL_STATE_CODE(); - /* - * Strictly speaking, draining is illegal under GRAPH_RDLOCK. We know that - * we've been called with bdrv_graph_rdlock_main_loop(), though, so it's ok - * in practice. - */ - bdrv_drained_begin(bs); + assert(bs->quiesce_counter > 0); if (bs_queue == NULL) { bs_queue = g_new0(BlockReopenQueue, 1); @@ -4522,6 +4517,12 @@ BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, QDict *options, bool keep_old_opts) { GLOBAL_STATE_CODE(); + + if (bs_queue == NULL) { + /* Paired with bdrv_drain_all_end() in bdrv_reopen_queue_free(). */ + bdrv_drain_all_begin(); + } + GRAPH_RDLOCK_GUARD_MAINLOOP(); return bdrv_reopen_queue_child(bs_queue, bs, options, NULL, 0, false, @@ -4534,12 +4535,14 @@ void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue) if (bs_queue) { BlockReopenQueueEntry *bs_entry, *next; QTAILQ_FOREACH_SAFE(bs_entry, bs_queue, entry, next) { - bdrv_drained_end(bs_entry->state.bs); qobject_unref(bs_entry->state.explicit_options); qobject_unref(bs_entry->state.options); g_free(bs_entry); } g_free(bs_queue); + + /* Paired with bdrv_drain_all_begin() in bdrv_reopen_queue(). */ + bdrv_drain_all_end(); } } From d4c5f8c980f1073356d2f18d51dc68d42bebb59d Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:40 +0200 Subject: [PATCH 1387/2760] block/snapshot: move drain outside of read-locked bdrv_snapshot_delete() This is in preparation to mark bdrv_drained_begin() as GRAPH_UNLOCKED. More granular draining is not trivially possible, because bdrv_snapshot_delete() can recursively call itself. The return value of bdrv_all_delete_snapshot() changes from -1 to -errno propagated from failed sub-calls. This is fine for the existing callers of bdrv_all_delete_snapshot(). Signed-off-by: Fiona Ebner Reviewed-by: Kevin Wolf Message-ID: <20250530151125.955508-4-f.ebner@proxmox.com> Signed-off-by: Kevin Wolf --- block/snapshot.c | 26 +++++++++++++++----------- blockdev.c | 25 +++++++++++++++++-------- qemu-img.c | 2 ++ 3 files changed, 34 insertions(+), 19 deletions(-) diff --git a/block/snapshot.c b/block/snapshot.c index 22567f1fb9..9f300a78bd 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -327,7 +327,7 @@ int bdrv_snapshot_goto(BlockDriverState *bs, /** * Delete an internal snapshot by @snapshot_id and @name. - * @bs: block device used in the operation + * @bs: block device used in the operation, must be drained * @snapshot_id: unique snapshot ID, or NULL * @name: snapshot name, or NULL * @errp: location to store error @@ -358,6 +358,8 @@ int bdrv_snapshot_delete(BlockDriverState *bs, GLOBAL_STATE_CODE(); + assert(bs->quiesce_counter > 0); + if (!drv) { error_setg(errp, "Device '%s' has no medium", bdrv_get_device_name(bs)); @@ -368,9 +370,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs, return -EINVAL; } - /* drain all pending i/o before deleting snapshot */ - bdrv_drained_begin(bs); - if (drv->bdrv_snapshot_delete) { ret = drv->bdrv_snapshot_delete(bs, snapshot_id, name, errp); } else if (fallback_bs) { @@ -382,7 +381,6 @@ int bdrv_snapshot_delete(BlockDriverState *bs, ret = -ENOTSUP; } - bdrv_drained_end(bs); return ret; } @@ -571,19 +569,22 @@ int bdrv_all_delete_snapshot(const char *name, ERRP_GUARD(); g_autoptr(GList) bdrvs = NULL; GList *iterbdrvs; + int ret = 0; GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); - if (bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp) < 0) { - return -1; + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); + + ret = bdrv_all_get_snapshot_devices(has_devices, devices, &bdrvs, errp); + if (ret < 0) { + goto out; } iterbdrvs = bdrvs; while (iterbdrvs) { BlockDriverState *bs = iterbdrvs->data; QEMUSnapshotInfo sn1, *snapshot = &sn1; - int ret = 0; if ((devices || bdrv_all_snapshots_includes_bs(bs)) && bdrv_snapshot_find(bs, snapshot, name) >= 0) @@ -594,13 +595,16 @@ int bdrv_all_delete_snapshot(const char *name, if (ret < 0) { error_prepend(errp, "Could not delete snapshot '%s' on '%s': ", name, bdrv_get_device_or_node_name(bs)); - return -1; + goto out; } iterbdrvs = iterbdrvs->next; } - return 0; +out: + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); + return ret; } diff --git a/blockdev.c b/blockdev.c index 21443b4514..3982f9776b 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1132,39 +1132,41 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, int ret; GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); + + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); bs = qmp_get_root_bs(device, errp); if (!bs) { - return NULL; + goto error; } if (!id && !name) { error_setg(errp, "Name or id must be provided"); - return NULL; + goto error; } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, errp)) { - return NULL; + goto error; } ret = bdrv_snapshot_find_by_id_and_name(bs, id, name, &sn, &local_err); if (local_err) { error_propagate(errp, local_err); - return NULL; + goto error; } if (!ret) { error_setg(errp, "Snapshot with id '%s' and name '%s' does not exist on " "device '%s'", STR_OR_NULL(id), STR_OR_NULL(name), device); - return NULL; + goto error; } bdrv_snapshot_delete(bs, id, name, &local_err); if (local_err) { error_propagate(errp, local_err); - return NULL; + goto error; } info = g_new0(SnapshotInfo, 1); @@ -1180,6 +1182,9 @@ SnapshotInfo *qmp_blockdev_snapshot_delete_internal_sync(const char *device, info->has_icount = true; } +error: + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); return info; } @@ -1295,12 +1300,14 @@ static void internal_snapshot_abort(void *opaque) Error *local_error = NULL; GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!state->created) { return; } + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); + if (bdrv_snapshot_delete(bs, sn->id_str, sn->name, &local_error) < 0) { error_reportf_err(local_error, "Failed to delete snapshot with id '%s' and " @@ -1308,6 +1315,8 @@ static void internal_snapshot_abort(void *opaque) sn->id_str, sn->name, bdrv_get_device_name(bs)); } + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); } static void internal_snapshot_clean(void *opaque) diff --git a/qemu-img.c b/qemu-img.c index 139eeb5039..e75707180d 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3505,6 +3505,7 @@ static int img_snapshot(int argc, char **argv) break; case SNAPSHOT_DELETE: + bdrv_drain_all_begin(); bdrv_graph_rdlock_main_loop(); ret = bdrv_snapshot_find(bs, &sn, snapshot_name); if (ret < 0) { @@ -3520,6 +3521,7 @@ static int img_snapshot(int argc, char **argv) } } bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); break; } From 841998e08650f5b4476fa2d1eb84a592ab405f51 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:41 +0200 Subject: [PATCH 1388/2760] block: move drain outside of read-locked bdrv_inactivate_recurse() This is in preparation to mark bdrv_drained_begin() as GRAPH_UNLOCKED. More granular draining is not trivially possible, because bdrv_inactivate_recurse() can recursively call itself. Signed-off-by: Fiona Ebner Reviewed-by: Kevin Wolf Message-ID: <20250530151125.955508-5-f.ebner@proxmox.com> Signed-off-by: Kevin Wolf --- block.c | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/block.c b/block.c index 3065d5c91e..fa55dfba68 100644 --- a/block.c +++ b/block.c @@ -6989,6 +6989,8 @@ bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level) GLOBAL_STATE_CODE(); + assert(bs->quiesce_counter > 0); + if (!bs->drv) { return -ENOMEDIUM; } @@ -7032,9 +7034,7 @@ bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level) return -EPERM; } - bdrv_drained_begin(bs); bs->open_flags |= BDRV_O_INACTIVE; - bdrv_drained_end(bs); /* * Update permissions, they may differ for inactive nodes. @@ -7059,20 +7059,26 @@ int bdrv_inactivate(BlockDriverState *bs, Error **errp) int ret; GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); + + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); if (bdrv_has_bds_parent(bs, true)) { error_setg(errp, "Node has active parent node"); - return -EPERM; + ret = -EPERM; + goto out; } ret = bdrv_inactivate_recurse(bs, true); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to inactivate node"); - return ret; + goto out; } - return 0; +out: + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); + return ret; } int bdrv_inactivate_all(void) @@ -7082,7 +7088,9 @@ int bdrv_inactivate_all(void) int ret = 0; GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); + + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); for (bs = bdrv_first(&it); bs; bs = bdrv_next(&it)) { /* Nodes with BDS parents are covered by recursion from the last @@ -7098,6 +7106,9 @@ int bdrv_inactivate_all(void) } } + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); + return ret; } From 3758733959af93b5eb3283659d868ad5b24152b4 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:42 +0200 Subject: [PATCH 1389/2760] block: mark bdrv_parent_change_aio_context() GRAPH_RDLOCK This is a small step in preparation to mark bdrv_drained_begin() as GRAPH_UNLOCKED. More concretely, it allows marking the change_aio_ctx() callback GRAPH_RDLOCK_PTR, which is the next step. Signed-off-by: Fiona Ebner Reviewed-by: Kevin Wolf Message-ID: <20250530151125.955508-6-f.ebner@proxmox.com> Signed-off-by: Kevin Wolf --- block.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/block.c b/block.c index fa55dfba68..7207978e53 100644 --- a/block.c +++ b/block.c @@ -7575,10 +7575,10 @@ typedef struct BdrvStateSetAioContext { BlockDriverState *bs; } BdrvStateSetAioContext; -static bool bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, - GHashTable *visited, - Transaction *tran, - Error **errp) +static bool GRAPH_RDLOCK +bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp) { GLOBAL_STATE_CODE(); if (g_hash_table_contains(visited, c)) { From 844d550d09ac29ff2b1b49069587ae6a989df31d Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:43 +0200 Subject: [PATCH 1390/2760] block: mark change_aio_ctx() callback and instances as GRAPH_RDLOCK(_PTR) This is a small step in preparation to mark bdrv_drained_begin() as GRAPH_UNLOCKED. More concretely, it is in preparation to move the drain out of bdrv_change_aio_context() and marking that function as GRAPH_RDLOCK. Signed-off-by: Fiona Ebner Reviewed-by: Kevin Wolf Message-ID: <20250530151125.955508-7-f.ebner@proxmox.com> Signed-off-by: Kevin Wolf --- block.c | 7 ++++--- block/block-backend.c | 6 +++--- blockjob.c | 6 +++--- include/block/block_int-common.h | 6 +++--- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/block.c b/block.c index 7207978e53..01144c895e 100644 --- a/block.c +++ b/block.c @@ -1226,9 +1226,10 @@ static int bdrv_child_cb_inactivate(BdrvChild *child) return 0; } -static bool bdrv_child_cb_change_aio_ctx(BdrvChild *child, AioContext *ctx, - GHashTable *visited, Transaction *tran, - Error **errp) +static bool GRAPH_RDLOCK +bdrv_child_cb_change_aio_ctx(BdrvChild *child, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp) { BlockDriverState *bs = child->opaque; return bdrv_change_aio_context(bs, ctx, visited, tran, errp); diff --git a/block/block-backend.c b/block/block-backend.c index a402db13f2..6a6949edeb 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -136,9 +136,9 @@ static void blk_root_drained_end(BdrvChild *child); static void blk_root_change_media(BdrvChild *child, bool load); static void blk_root_resize(BdrvChild *child); -static bool blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx, - GHashTable *visited, Transaction *tran, - Error **errp); +static bool GRAPH_RDLOCK +blk_root_change_aio_ctx(BdrvChild *child, AioContext *ctx, GHashTable *visited, + Transaction *tran, Error **errp); static char *blk_root_get_parent_desc(BdrvChild *child) { diff --git a/blockjob.c b/blockjob.c index 32007f31a9..34185d7715 100644 --- a/blockjob.c +++ b/blockjob.c @@ -144,9 +144,9 @@ static TransactionActionDrv change_child_job_context = { .clean = g_free, }; -static bool child_job_change_aio_ctx(BdrvChild *c, AioContext *ctx, - GHashTable *visited, Transaction *tran, - Error **errp) +static bool GRAPH_RDLOCK +child_job_change_aio_ctx(BdrvChild *c, AioContext *ctx, GHashTable *visited, + Transaction *tran, Error **errp) { BlockJob *job = c->opaque; BdrvStateChildJobContext *s; diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 2982dd3118..37466c7841 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -983,9 +983,9 @@ struct BdrvChildClass { bool backing_mask_protocol, Error **errp); - bool (*change_aio_ctx)(BdrvChild *child, AioContext *ctx, - GHashTable *visited, Transaction *tran, - Error **errp); + bool GRAPH_RDLOCK_PTR (*change_aio_ctx)(BdrvChild *child, AioContext *ctx, + GHashTable *visited, + Transaction *tran, Error **errp); /* * I/O API functions. These functions are thread-safe. From 469422c45b3a816eaf36e7edc895c81e0f3d38bb Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:44 +0200 Subject: [PATCH 1391/2760] block: mark bdrv_child_change_aio_context() GRAPH_RDLOCK This is a small step in preparation to mark bdrv_drained_begin() as GRAPH_UNLOCKED. More concretely, it is in preparation to move the drain out of bdrv_change_aio_context() and marking that function as GRAPH_RDLOCK. Signed-off-by: Fiona Ebner Reviewed-by: Kevin Wolf Message-ID: <20250530151125.955508-8-f.ebner@proxmox.com> Signed-off-by: Kevin Wolf --- include/block/block-global-state.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 9be34b3c99..aad160956a 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -274,9 +274,10 @@ int bdrv_debug_remove_breakpoint(BlockDriverState *bs, const char *tag); int bdrv_debug_resume(BlockDriverState *bs, const char *tag); bool bdrv_debug_is_suspended(BlockDriverState *bs, const char *tag); -bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, - GHashTable *visited, Transaction *tran, - Error **errp); +bool GRAPH_RDLOCK +bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, + GHashTable *visited, Transaction *tran, + Error **errp); int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, BdrvChild *ignore_child, Error **errp); From 91ba0e1c382bd4a4b9c6a200f8a175d6ff30ab99 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:45 +0200 Subject: [PATCH 1392/2760] block: move drain outside of bdrv_change_aio_context() and mark GRAPH_RDLOCK This is in preparation to mark bdrv_drained_begin() as GRAPH_UNLOCKED. Note that even if bdrv_drained_begin() were already marked as GRAPH_UNLOCKED, TSA would not complain about the instance in bdrv_change_aio_context() before this change, because it is preceded by a bdrv_graph_rdunlock_main_loop() call. It is not correct to release the lock here, and in case the caller holds a write lock, it wouldn't actually release the lock. In combination with block-stream, there is a deadlock that can happen because of this [0]. In particular, it can happen that main thread IO thread 1. acquires write lock in blk_co_do_preadv_part(): 2. have non-zero blk->in_flight 3. try to acquire read lock 4. begin drain Steps 3 and 4 might be switched. Draining will poll and get stuck, because it will see the non-zero in_flight counter. But the IO thread will not make any progress either, because it cannot acquire the read lock. After this change, all paths to bdrv_change_aio_context() drain: bdrv_change_aio_context() is called by: 1. bdrv_child_cb_change_aio_ctx() which is only called via the change_aio_ctx() callback, see below. 2. bdrv_child_change_aio_context(), see below. 3. bdrv_try_change_aio_context(), where a drained section is introduced. The change_aio_ctx() callback is called by: 1. bdrv_attach_child_common_abort(), where a drained section is introduced. 2. bdrv_attach_child_common(), where a drained section is introduced. 3. bdrv_parent_change_aio_context(), see below. bdrv_child_change_aio_context() is called by: 1. bdrv_change_aio_context(), i.e. recursive, so being in a drained section is invariant. 2. child_job_change_aio_ctx(), which is only called via the change_aio_ctx() callback, see above. bdrv_parent_change_aio_context() is called by: 1. bdrv_change_aio_context(), i.e. recursive, so being in a drained section is invariant. This resolves all code paths. Note that bdrv_attach_child_common() and bdrv_attach_child_common_abort() hold the graph write lock and callers of bdrv_try_change_aio_context() might too, so they are not actually allowed to drain either. This will be addressed in the following commits. More granular draining is not trivially possible, because bdrv_change_aio_context() can recursively call itself e.g. via bdrv_child_change_aio_context(). [0]: https://lore.kernel.org/qemu-devel/73839c04-7616-407e-b057-80ca69e63f51@virtuozzo.com/ Reported-by: Andrey Drobyshev Signed-off-by: Fiona Ebner Reviewed-by: Kevin Wolf Message-ID: <20250530151125.955508-9-f.ebner@proxmox.com> Signed-off-by: Kevin Wolf --- block.c | 57 +++++++++++++++++++++++--------- include/block/block_int-common.h | 12 +++++++ 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/block.c b/block.c index 01144c895e..6f42c0f1ab 100644 --- a/block.c +++ b/block.c @@ -106,9 +106,9 @@ static void bdrv_reopen_abort(BDRVReopenState *reopen_state); static bool bdrv_backing_overridden(BlockDriverState *bs); -static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, - GHashTable *visited, Transaction *tran, - Error **errp); +static bool GRAPH_RDLOCK +bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, + GHashTable *visited, Transaction *tran, Error **errp); /* If non-zero, use only whitelisted block drivers */ static int use_bdrv_whitelist; @@ -3040,8 +3040,10 @@ static void GRAPH_WRLOCK bdrv_attach_child_common_abort(void *opaque) /* No need to visit `child`, because it has been detached already */ visited = g_hash_table_new(NULL, NULL); + bdrv_drain_all_begin(); ret = s->child->klass->change_aio_ctx(s->child, s->old_parent_ctx, visited, tran, &error_abort); + bdrv_drain_all_end(); g_hash_table_destroy(visited); /* transaction is supposed to always succeed */ @@ -3122,9 +3124,11 @@ bdrv_attach_child_common(BlockDriverState *child_bs, bool ret_child; g_hash_table_add(visited, new_child); + bdrv_drain_all_begin(); ret_child = child_class->change_aio_ctx(new_child, child_ctx, visited, aio_ctx_tran, NULL); + bdrv_drain_all_end(); if (ret_child == true) { error_free(local_err); ret = 0; @@ -7576,6 +7580,17 @@ typedef struct BdrvStateSetAioContext { BlockDriverState *bs; } BdrvStateSetAioContext; +/* + * Changes the AioContext of @child to @ctx and recursively for the associated + * block nodes and all their children and parents. Returns true if the change is + * possible and the transaction @tran can be continued. Returns false and sets + * @errp if not and the transaction must be aborted. + * + * @visited will accumulate all visited BdrvChild objects. The caller is + * responsible for freeing the list afterwards. + * + * Must be called with the affected block nodes drained. + */ static bool GRAPH_RDLOCK bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, GHashTable *visited, Transaction *tran, @@ -7604,6 +7619,17 @@ bdrv_parent_change_aio_context(BdrvChild *c, AioContext *ctx, return true; } +/* + * Changes the AioContext of @c->bs to @ctx and recursively for all its children + * and parents. Returns true if the change is possible and the transaction @tran + * can be continued. Returns false and sets @errp if not and the transaction + * must be aborted. + * + * @visited will accumulate all visited BdrvChild objects. The caller is + * responsible for freeing the list afterwards. + * + * Must be called with the affected block nodes drained. + */ bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, GHashTable *visited, Transaction *tran, Error **errp) @@ -7619,10 +7645,6 @@ bool bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, static void bdrv_set_aio_context_clean(void *opaque) { BdrvStateSetAioContext *state = (BdrvStateSetAioContext *) opaque; - BlockDriverState *bs = (BlockDriverState *) state->bs; - - /* Paired with bdrv_drained_begin in bdrv_change_aio_context() */ - bdrv_drained_end(bs); g_free(state); } @@ -7650,10 +7672,12 @@ static TransactionActionDrv set_aio_context = { * * @visited will accumulate all visited BdrvChild objects. The caller is * responsible for freeing the list afterwards. + * + * @bs must be drained. */ -static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, - GHashTable *visited, Transaction *tran, - Error **errp) +static bool GRAPH_RDLOCK +bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, + GHashTable *visited, Transaction *tran, Error **errp) { BdrvChild *c; BdrvStateSetAioContext *state; @@ -7664,21 +7688,17 @@ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, return true; } - bdrv_graph_rdlock_main_loop(); QLIST_FOREACH(c, &bs->parents, next_parent) { if (!bdrv_parent_change_aio_context(c, ctx, visited, tran, errp)) { - bdrv_graph_rdunlock_main_loop(); return false; } } QLIST_FOREACH(c, &bs->children, next) { if (!bdrv_child_change_aio_context(c, ctx, visited, tran, errp)) { - bdrv_graph_rdunlock_main_loop(); return false; } } - bdrv_graph_rdunlock_main_loop(); state = g_new(BdrvStateSetAioContext, 1); *state = (BdrvStateSetAioContext) { @@ -7686,8 +7706,7 @@ static bool bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, .bs = bs, }; - /* Paired with bdrv_drained_end in bdrv_set_aio_context_clean() */ - bdrv_drained_begin(bs); + assert(bs->quiesce_counter > 0); tran_add(tran, &set_aio_context, state); @@ -7720,6 +7739,8 @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, if (ignore_child) { g_hash_table_add(visited, ignore_child); } + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); ret = bdrv_change_aio_context(bs, ctx, visited, tran, errp); g_hash_table_destroy(visited); @@ -7733,10 +7754,14 @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, if (!ret) { /* Just run clean() callbacks. No AioContext changed. */ tran_abort(tran); + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); return -EPERM; } tran_commit(tran); + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); return 0; } diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 37466c7841..168f703fa1 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -983,6 +983,18 @@ struct BdrvChildClass { bool backing_mask_protocol, Error **errp); + /* + * Notifies the parent that the child is trying to change its AioContext. + * The parent may in turn change the AioContext of other nodes in the same + * transaction. Returns true if the change is possible and the transaction + * can be continued. Returns false and sets @errp if not and the transaction + * must be aborted. + * + * @visited will accumulate all visited BdrvChild objects. The caller is + * responsible for freeing the list afterwards. + * + * Must be called with the affected block nodes drained. + */ bool GRAPH_RDLOCK_PTR (*change_aio_ctx)(BdrvChild *child, AioContext *ctx, GHashTable *visited, Transaction *tran, Error **errp); From a1ea8eb5912256c0b2be16fae5d3786aebc80cb1 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:46 +0200 Subject: [PATCH 1393/2760] block: move drain outside of bdrv_try_change_aio_context() This is part of resolving the deadlock mentioned in commit "block: move draining out of bdrv_change_aio_context() and mark GRAPH_RDLOCK". Convert the function to a _locked() version that has to be called with the graph lock held and add a convenience wrapper that has to be called with the graph unlocked, which drains and takes the lock itself. Since bdrv_try_change_aio_context() is global state code, the wrapper is too. Callers are adapted to use the appropriate variant, depending on whether the caller already holds the lock. In the test_set_aio_context() unit test, prior drains can be removed, because draining already happens inside the new wrapper. Note that bdrv_attach_child_common_abort(), bdrv_attach_child_common() and bdrv_root_unref_child() hold the graph lock and are not actually allowed to drain either. This will be addressed in the following commits. Functions like qmp_blockdev_mirror() query the nodes to act on before draining and locking. In theory, draining could invalidate those nodes. This kind of issue is not addressed by these commits. Signed-off-by: Fiona Ebner Reviewed-by: Kevin Wolf Message-ID: <20250530151125.955508-10-f.ebner@proxmox.com> Signed-off-by: Kevin Wolf --- block.c | 58 ++++++++++++++++++++++-------- blockdev.c | 15 +++++--- include/block/block-global-state.h | 8 +++-- tests/unit/test-bdrv-drain.c | 4 --- 4 files changed, 59 insertions(+), 26 deletions(-) diff --git a/block.c b/block.c index 6f42c0f1ab..3aaacabf7f 100644 --- a/block.c +++ b/block.c @@ -3028,7 +3028,10 @@ static void GRAPH_WRLOCK bdrv_attach_child_common_abort(void *opaque) bdrv_replace_child_noperm(s->child, NULL); if (bdrv_get_aio_context(bs) != s->old_child_ctx) { - bdrv_try_change_aio_context(bs, s->old_child_ctx, NULL, &error_abort); + bdrv_drain_all_begin(); + bdrv_try_change_aio_context_locked(bs, s->old_child_ctx, NULL, + &error_abort); + bdrv_drain_all_end(); } if (bdrv_child_get_parent_aio_context(s->child) != s->old_parent_ctx) { @@ -3115,8 +3118,10 @@ bdrv_attach_child_common(BlockDriverState *child_bs, parent_ctx = bdrv_child_get_parent_aio_context(new_child); if (child_ctx != parent_ctx) { Error *local_err = NULL; - int ret = bdrv_try_change_aio_context(child_bs, parent_ctx, NULL, - &local_err); + bdrv_drain_all_begin(); + int ret = bdrv_try_change_aio_context_locked(child_bs, parent_ctx, NULL, + &local_err); + bdrv_drain_all_end(); if (ret < 0 && child_class->change_aio_ctx) { Transaction *aio_ctx_tran = tran_new(); @@ -3319,8 +3324,10 @@ void bdrv_root_unref_child(BdrvChild *child) * When the parent requiring a non-default AioContext is removed, the * node moves back to the main AioContext */ - bdrv_try_change_aio_context(child_bs, qemu_get_aio_context(), NULL, - NULL); + bdrv_drain_all_begin(); + bdrv_try_change_aio_context_locked(child_bs, qemu_get_aio_context(), + NULL, NULL); + bdrv_drain_all_end(); } bdrv_schedule_unref(child_bs); @@ -7719,9 +7726,13 @@ bdrv_change_aio_context(BlockDriverState *bs, AioContext *ctx, * * If ignore_child is not NULL, that child (and its subgraph) will not * be touched. + * + * Called with the graph lock held. + * + * Called while all bs are drained. */ -int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, - BdrvChild *ignore_child, Error **errp) +int bdrv_try_change_aio_context_locked(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp) { Transaction *tran; GHashTable *visited; @@ -7730,17 +7741,15 @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, /* * Recursion phase: go through all nodes of the graph. - * Take care of checking that all nodes support changing AioContext - * and drain them, building a linear list of callbacks to run if everything - * is successful (the transaction itself). + * Take care of checking that all nodes support changing AioContext, + * building a linear list of callbacks to run if everything is successful + * (the transaction itself). */ tran = tran_new(); visited = g_hash_table_new(NULL, NULL); if (ignore_child) { g_hash_table_add(visited, ignore_child); } - bdrv_drain_all_begin(); - bdrv_graph_rdlock_main_loop(); ret = bdrv_change_aio_context(bs, ctx, visited, tran, errp); g_hash_table_destroy(visited); @@ -7754,15 +7763,34 @@ int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, if (!ret) { /* Just run clean() callbacks. No AioContext changed. */ tran_abort(tran); - bdrv_graph_rdunlock_main_loop(); - bdrv_drain_all_end(); return -EPERM; } tran_commit(tran); + return 0; +} + +/* + * Change bs's and recursively all of its parents' and children's AioContext + * to the given new context, returning an error if that isn't possible. + * + * If ignore_child is not NULL, that child (and its subgraph) will not + * be touched. + */ +int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp) +{ + int ret; + + GLOBAL_STATE_CODE(); + + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); + ret = bdrv_try_change_aio_context_locked(bs, ctx, ignore_child, errp); bdrv_graph_rdunlock_main_loop(); bdrv_drain_all_end(); - return 0; + + return ret; } void bdrv_add_aio_context_notifier(BlockDriverState *bs, diff --git a/blockdev.c b/blockdev.c index 3982f9776b..750beba41f 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3601,12 +3601,13 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, AioContext *new_context; BlockDriverState *bs; - GRAPH_RDLOCK_GUARD_MAINLOOP(); + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); bs = bdrv_find_node(node_name); if (!bs) { error_setg(errp, "Failed to find node with node-name='%s'", node_name); - return; + goto out; } /* Protects against accidents. */ @@ -3614,14 +3615,14 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, error_setg(errp, "Node %s is associated with a BlockBackend and could " "be in use (use force=true to override this check)", node_name); - return; + goto out; } if (iothread->type == QTYPE_QSTRING) { IOThread *obj = iothread_by_id(iothread->u.s); if (!obj) { error_setg(errp, "Cannot find iothread %s", iothread->u.s); - return; + goto out; } new_context = iothread_get_aio_context(obj); @@ -3629,7 +3630,11 @@ void qmp_x_blockdev_set_iothread(const char *node_name, StrOrNull *iothread, new_context = qemu_get_aio_context(); } - bdrv_try_change_aio_context(bs, new_context, NULL, errp); + bdrv_try_change_aio_context_locked(bs, new_context, NULL, errp); + +out: + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_end(); } QemuOptsList qemu_common_drive_opts = { diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index aad160956a..91f249b5ad 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -278,8 +278,12 @@ bool GRAPH_RDLOCK bdrv_child_change_aio_context(BdrvChild *c, AioContext *ctx, GHashTable *visited, Transaction *tran, Error **errp); -int bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, - BdrvChild *ignore_child, Error **errp); +int GRAPH_UNLOCKED +bdrv_try_change_aio_context(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp); +int GRAPH_RDLOCK +bdrv_try_change_aio_context_locked(BlockDriverState *bs, AioContext *ctx, + BdrvChild *ignore_child, Error **errp); int GRAPH_RDLOCK bdrv_probe_blocksizes(BlockDriverState *bs, BlockSizes *bsz); int bdrv_probe_geometry(BlockDriverState *bs, HDGeometry *geo); diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 290cd2a70e..3185f3f429 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -1396,14 +1396,10 @@ static void test_set_aio_context(void) bs = bdrv_new_open_driver(&bdrv_test, "test-node", BDRV_O_RDWR, &error_abort); - bdrv_drained_begin(bs); bdrv_try_change_aio_context(bs, ctx_a, NULL, &error_abort); - bdrv_drained_end(bs); - bdrv_drained_begin(bs); bdrv_try_change_aio_context(bs, ctx_b, NULL, &error_abort); bdrv_try_change_aio_context(bs, qemu_get_aio_context(), NULL, &error_abort); - bdrv_drained_end(bs); bdrv_unref(bs); iothread_join(a); From 2b833595aa21679145cfe67ba720113b165c19ef Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:47 +0200 Subject: [PATCH 1394/2760] block: move drain outside of bdrv_attach_child_common(_abort)() This is part of resolving the deadlock mentioned in commit "block: move draining out of bdrv_change_aio_context() and mark GRAPH_RDLOCK". The function bdrv_attach_child_common_abort() is used only as the abort callback in bdrv_attach_child_common_drv transactions, so the tran_finalize() calls of such transactions need to be in drained sections too. All code paths are covered: The bdrv_attach_child_common_drv transactions are only used in bdrv_attach_child_common(), so it is enough to check callers of bdrv_attach_child_common() following the transactions. bdrv_attach_child_common() is called by: 1. bdrv_attach_child_noperm(), which does not finalize the transaction yet. 2. bdrv_root_attach_child(), where a drained section is introduced. bdrv_attach_child_noperm() is called by: 1. bdrv_attach_child(), where a drained section is introduced. 2. bdrv_set_file_or_backing_noperm(), which does not finalize the transaction yet. 3. bdrv_append(), where a drained section is introduced. bdrv_set_file_or_backing_noperm() is called by: 1. bdrv_set_backing_hd_drained(), where a drained section is introduced. 2. bdrv_reopen_parse_file_or_backing(), which does not finalize the transaction yet. Draining the old child bs currently happens under the graph lock there. This is replaced with an assertion, because the drain will be moved further up to the caller. bdrv_reopen_parse_file_or_backing() is called by: 1. bdrv_reopen_prepare(), which does not finalize the transaction yet. bdrv_reopen_prepare() is called by: 1. bdrv_reopen_multiple(), which does finalize the transaction. It is called after bdrv_reopen_queue(), which starts a drained section. The drained section ends, when bdrv_reopen_queue_free() is called at the end of bdrv_reopen_multiple(). This resolves all code paths. The functions bdrv_set_backing_hd_drained(), bdrv_attach_child() and bdrv_root_attach_child() run under the graph lock, so they are not actually allowed to drain. This will be addressed in the following commits. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-11-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/block.c b/block.c index 3aaacabf7f..46eb2fe449 100644 --- a/block.c +++ b/block.c @@ -3028,10 +3028,8 @@ static void GRAPH_WRLOCK bdrv_attach_child_common_abort(void *opaque) bdrv_replace_child_noperm(s->child, NULL); if (bdrv_get_aio_context(bs) != s->old_child_ctx) { - bdrv_drain_all_begin(); bdrv_try_change_aio_context_locked(bs, s->old_child_ctx, NULL, &error_abort); - bdrv_drain_all_end(); } if (bdrv_child_get_parent_aio_context(s->child) != s->old_parent_ctx) { @@ -3043,10 +3041,8 @@ static void GRAPH_WRLOCK bdrv_attach_child_common_abort(void *opaque) /* No need to visit `child`, because it has been detached already */ visited = g_hash_table_new(NULL, NULL); - bdrv_drain_all_begin(); ret = s->child->klass->change_aio_ctx(s->child, s->old_parent_ctx, visited, tran, &error_abort); - bdrv_drain_all_end(); g_hash_table_destroy(visited); /* transaction is supposed to always succeed */ @@ -3075,6 +3071,9 @@ static TransactionActionDrv bdrv_attach_child_common_drv = { * * Both @parent_bs and @child_bs can move to a different AioContext in this * function. + * + * All block nodes must be drained before this function is called until after + * the transaction is finalized. */ static BdrvChild * GRAPH_WRLOCK bdrv_attach_child_common(BlockDriverState *child_bs, @@ -3118,10 +3117,8 @@ bdrv_attach_child_common(BlockDriverState *child_bs, parent_ctx = bdrv_child_get_parent_aio_context(new_child); if (child_ctx != parent_ctx) { Error *local_err = NULL; - bdrv_drain_all_begin(); int ret = bdrv_try_change_aio_context_locked(child_bs, parent_ctx, NULL, &local_err); - bdrv_drain_all_end(); if (ret < 0 && child_class->change_aio_ctx) { Transaction *aio_ctx_tran = tran_new(); @@ -3129,11 +3126,9 @@ bdrv_attach_child_common(BlockDriverState *child_bs, bool ret_child; g_hash_table_add(visited, new_child); - bdrv_drain_all_begin(); ret_child = child_class->change_aio_ctx(new_child, child_ctx, visited, aio_ctx_tran, NULL); - bdrv_drain_all_end(); if (ret_child == true) { error_free(local_err); ret = 0; @@ -3189,6 +3184,9 @@ bdrv_attach_child_common(BlockDriverState *child_bs, * * After calling this function, the transaction @tran may only be completed * while holding a writer lock for the graph. + * + * All block nodes must be drained before this function is called until after + * the transaction is finalized. */ static BdrvChild * GRAPH_WRLOCK bdrv_attach_child_noperm(BlockDriverState *parent_bs, @@ -3244,6 +3242,7 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, GLOBAL_STATE_CODE(); + bdrv_drain_all_begin(); child = bdrv_attach_child_common(child_bs, child_name, child_class, child_role, perm, shared_perm, opaque, tran, errp); @@ -3256,6 +3255,7 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, out: tran_finalize(tran, ret); + bdrv_drain_all_end(); bdrv_schedule_unref(child_bs); @@ -3283,6 +3283,7 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, GLOBAL_STATE_CODE(); + bdrv_drain_all_begin(); child = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, child_class, child_role, tran, errp); if (!child) { @@ -3297,6 +3298,7 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, out: tran_finalize(tran, ret); + bdrv_drain_all_end(); bdrv_schedule_unref(child_bs); @@ -3465,6 +3467,9 @@ static BdrvChildRole bdrv_backing_role(BlockDriverState *bs) * * After calling this function, the transaction @tran may only be completed * while holding a writer lock for the graph. + * + * All block nodes must be drained before this function is called until after + * the transaction is finalized. */ static int GRAPH_WRLOCK bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, @@ -3573,6 +3578,7 @@ int bdrv_set_backing_hd_drained(BlockDriverState *bs, assert(bs->backing->bs->quiesce_counter > 0); } + bdrv_drain_all_begin(); ret = bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp); if (ret < 0) { goto out; @@ -3581,6 +3587,7 @@ int bdrv_set_backing_hd_drained(BlockDriverState *bs, ret = bdrv_refresh_perms(bs, tran, errp); out: tran_finalize(tran, ret); + bdrv_drain_all_end(); return ret; } @@ -4721,6 +4728,9 @@ int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, * Return 0 on success, otherwise return < 0 and set @errp. * * @reopen_state->bs can move to a different AioContext in this function. + * + * All block nodes must be drained before this function is called until after + * the transaction is finalized. */ static int GRAPH_UNLOCKED bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, @@ -4814,7 +4824,7 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, if (old_child_bs) { bdrv_ref(old_child_bs); - bdrv_drained_begin(old_child_bs); + assert(old_child_bs->quiesce_counter > 0); } bdrv_graph_rdunlock_main_loop(); @@ -4826,7 +4836,6 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, bdrv_graph_wrunlock(); if (old_child_bs) { - bdrv_drained_end(old_child_bs); bdrv_unref(old_child_bs); } @@ -4855,6 +4864,9 @@ bdrv_reopen_parse_file_or_backing(BDRVReopenState *reopen_state, * * After calling this function, the transaction @change_child_tran may only be * completed while holding a writer lock for the graph. + * + * All block nodes must be drained before this function is called until after + * the transaction is finalized. */ static int GRAPH_UNLOCKED bdrv_reopen_prepare(BDRVReopenState *reopen_state, BlockReopenQueue *queue, @@ -5501,9 +5513,7 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, assert(!bs_new->backing); bdrv_graph_rdunlock_main_loop(); - bdrv_drained_begin(bs_top); - bdrv_drained_begin(bs_new); - + bdrv_drain_all_begin(); bdrv_graph_wrlock(); child = bdrv_attach_child_noperm(bs_new, bs_top, "backing", @@ -5525,9 +5535,7 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, bdrv_refresh_limits(bs_top, NULL, NULL); bdrv_graph_wrunlock(); - - bdrv_drained_end(bs_top); - bdrv_drained_end(bs_new); + bdrv_drain_all_end(); return ret; } From e66dbda11eab2b4a091d470f3508a4d6ca60eaf5 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:48 +0200 Subject: [PATCH 1395/2760] block: move drain outside of bdrv_set_backing_hd_drained() This is part of resolving the deadlock mentioned in commit "block: move draining out of bdrv_change_aio_context() and mark GRAPH_RDLOCK". The function bdrv_set_backing_hd_drained() holds the graph lock, so it is not allowed to drain. It is called by: 1. bdrv_set_backing_hd(), where a drained section is introduced, replacing the previously present bs-specific drains. 2. stream_prepare(), where a drained section is introduced replacing the previously present bs-specific drains. The drain_bs variable in bdrv_set_backing_hd_drained() is now superfluous and thus dropped. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-12-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 16 +++------------- block/stream.c | 6 ++---- 2 files changed, 5 insertions(+), 17 deletions(-) diff --git a/block.c b/block.c index 46eb2fe449..e53a88e1b6 100644 --- a/block.c +++ b/block.c @@ -3562,8 +3562,7 @@ bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, * Both @bs and @backing_hd can move to a different AioContext in this * function. * - * If a backing child is already present (i.e. we're detaching a node), that - * child node must be drained. + * All block nodes must be drained. */ int bdrv_set_backing_hd_drained(BlockDriverState *bs, BlockDriverState *backing_hd, @@ -3578,7 +3577,6 @@ int bdrv_set_backing_hd_drained(BlockDriverState *bs, assert(bs->backing->bs->quiesce_counter > 0); } - bdrv_drain_all_begin(); ret = bdrv_set_file_or_backing_noperm(bs, backing_hd, true, tran, errp); if (ret < 0) { goto out; @@ -3587,28 +3585,20 @@ int bdrv_set_backing_hd_drained(BlockDriverState *bs, ret = bdrv_refresh_perms(bs, tran, errp); out: tran_finalize(tran, ret); - bdrv_drain_all_end(); return ret; } int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, Error **errp) { - BlockDriverState *drain_bs; int ret; GLOBAL_STATE_CODE(); - bdrv_graph_rdlock_main_loop(); - drain_bs = bs->backing ? bs->backing->bs : bs; - bdrv_graph_rdunlock_main_loop(); - - bdrv_ref(drain_bs); - bdrv_drained_begin(drain_bs); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp); bdrv_graph_wrunlock(); - bdrv_drained_end(drain_bs); - bdrv_unref(drain_bs); + bdrv_drain_all_end(); return ret; } diff --git a/block/stream.c b/block/stream.c index 999d9e56d4..6ba49cffd3 100644 --- a/block/stream.c +++ b/block/stream.c @@ -80,11 +80,10 @@ static int stream_prepare(Job *job) * may end up working with the wrong base node (or it might even have gone * away by the time we want to use it). */ - bdrv_drained_begin(unfiltered_bs); if (unfiltered_bs_cow) { bdrv_ref(unfiltered_bs_cow); - bdrv_drained_begin(unfiltered_bs_cow); } + bdrv_drain_all_begin(); bdrv_graph_rdlock_main_loop(); base = bdrv_filter_or_cow_bs(s->above_base); @@ -123,11 +122,10 @@ static int stream_prepare(Job *job) } out: + bdrv_drain_all_end(); if (unfiltered_bs_cow) { - bdrv_drained_end(unfiltered_bs_cow); bdrv_unref(unfiltered_bs_cow); } - bdrv_drained_end(unfiltered_bs); return ret; } From ffdcd081f52544f065020c780a6c522dace6b0af Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:49 +0200 Subject: [PATCH 1396/2760] block: move drain outside of bdrv_root_attach_child() This is part of resolving the deadlock mentioned in commit "block: move draining out of bdrv_change_aio_context() and mark GRAPH_RDLOCK". The function bdrv_root_attach_child() runs under the graph lock, so it is not allowed to drain. It is called by: 1. blk_insert_bs(), where a drained section is introduced. 2. block_job_add_bdrv(), which holds the graph lock itself. block_job_add_bdrv() is called by: 1. mirror_start_job() 2. stream_start() 3. commit_start() 4. backup_job_create() 5. block_job_create() 6. In the test_blockjob_common_drain_node() unit test In all callers, a drained section is introduced. Signed-off-by: Fiona Ebner Reviewed-by: Kevin Wolf Message-ID: <20250530151125.955508-13-f.ebner@proxmox.com> Signed-off-by: Kevin Wolf --- block.c | 4 ++-- block/backup.c | 2 ++ block/block-backend.c | 2 ++ block/commit.c | 4 ++++ block/mirror.c | 5 +++++ block/stream.c | 4 ++++ blockjob.c | 4 ++++ include/block/blockjob.h | 2 ++ tests/unit/test-bdrv-drain.c | 2 ++ 9 files changed, 27 insertions(+), 2 deletions(-) diff --git a/block.c b/block.c index e53a88e1b6..17c34dc240 100644 --- a/block.c +++ b/block.c @@ -3228,6 +3228,8 @@ bdrv_attach_child_noperm(BlockDriverState *parent_bs, * * On failure NULL is returned, errp is set and the reference to * child_bs is also dropped. + * + * All block nodes must be drained. */ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, const char *child_name, @@ -3242,7 +3244,6 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, GLOBAL_STATE_CODE(); - bdrv_drain_all_begin(); child = bdrv_attach_child_common(child_bs, child_name, child_class, child_role, perm, shared_perm, opaque, tran, errp); @@ -3255,7 +3256,6 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, out: tran_finalize(tran, ret); - bdrv_drain_all_end(); bdrv_schedule_unref(child_bs); diff --git a/block/backup.c b/block/backup.c index 0151e84395..909027c17a 100644 --- a/block/backup.c +++ b/block/backup.c @@ -498,10 +498,12 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, block_copy_set_speed(bcs, speed); /* Required permissions are taken by copy-before-write filter target */ + bdrv_drain_all_begin(); bdrv_graph_wrlock(); block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); return &job->common; diff --git a/block/block-backend.c b/block/block-backend.c index 6a6949edeb..24cae3cb55 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -904,6 +904,7 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) GLOBAL_STATE_CODE(); bdrv_ref(bs); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); if ((bs->open_flags & BDRV_O_INACTIVE) && blk_can_inactivate(blk)) { @@ -919,6 +920,7 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, perm, shared_perm, blk, errp); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); if (blk->root == NULL) { return -EPERM; } diff --git a/block/commit.c b/block/commit.c index 7cc8c0f0df..6c4b736ff8 100644 --- a/block/commit.c +++ b/block/commit.c @@ -392,6 +392,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, * this is the responsibility of the interface (i.e. whoever calls * commit_start()). */ + bdrv_drain_all_begin(); bdrv_graph_wrlock(); s->base_overlay = bdrv_find_overlay(top, base); assert(s->base_overlay); @@ -424,18 +425,21 @@ void commit_start(const char *job_id, BlockDriverState *bs, iter_shared_perms, errp); if (ret < 0) { bdrv_graph_wrunlock(); + bdrv_drain_all_end(); goto fail; } } if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) { bdrv_graph_wrunlock(); + bdrv_drain_all_end(); goto fail; } s->chain_frozen = true; ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); if (ret < 0) { goto fail; diff --git a/block/mirror.c b/block/mirror.c index c2c5099c95..6e8caf4b49 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -2014,6 +2014,7 @@ static BlockJob *mirror_start_job( */ bdrv_disable_dirty_bitmap(s->dirty_bitmap); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); ret = block_job_add_bdrv(&s->common, "source", bs, 0, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | @@ -2021,6 +2022,7 @@ static BlockJob *mirror_start_job( errp); if (ret < 0) { bdrv_graph_wrunlock(); + bdrv_drain_all_end(); goto fail; } @@ -2066,16 +2068,19 @@ static BlockJob *mirror_start_job( iter_shared_perms, errp); if (ret < 0) { bdrv_graph_wrunlock(); + bdrv_drain_all_end(); goto fail; } } if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) { bdrv_graph_wrunlock(); + bdrv_drain_all_end(); goto fail; } } bdrv_graph_wrunlock(); + bdrv_drain_all_end(); QTAILQ_INIT(&s->ops_in_flight); diff --git a/block/stream.c b/block/stream.c index 6ba49cffd3..f5441f27f4 100644 --- a/block/stream.c +++ b/block/stream.c @@ -371,10 +371,12 @@ void stream_start(const char *job_id, BlockDriverState *bs, * already have our own plans. Also don't allow resize as the image size is * queried only at the job start and then cached. */ + bdrv_drain_all_begin(); bdrv_graph_wrlock(); if (block_job_add_bdrv(&s->common, "active node", bs, 0, basic_flags | BLK_PERM_WRITE, errp)) { bdrv_graph_wrunlock(); + bdrv_drain_all_end(); goto fail; } @@ -395,10 +397,12 @@ void stream_start(const char *job_id, BlockDriverState *bs, basic_flags, errp); if (ret < 0) { bdrv_graph_wrunlock(); + bdrv_drain_all_end(); goto fail; } } bdrv_graph_wrunlock(); + bdrv_drain_all_end(); s->base_overlay = base_overlay; s->above_base = above_base; diff --git a/blockjob.c b/blockjob.c index 34185d7715..44991e3ff7 100644 --- a/blockjob.c +++ b/blockjob.c @@ -496,6 +496,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, int ret; GLOBAL_STATE_CODE(); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); if (job_id == NULL && !(flags & JOB_INTERNAL)) { @@ -506,6 +507,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, flags, cb, opaque, errp); if (job == NULL) { bdrv_graph_wrunlock(); + bdrv_drain_all_end(); return NULL; } @@ -544,10 +546,12 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, } bdrv_graph_wrunlock(); + bdrv_drain_all_end(); return job; fail: bdrv_graph_wrunlock(); + bdrv_drain_all_end(); job_early_fail(&job->job); return NULL; } diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 7061ab7201..990f3e179a 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -137,6 +137,8 @@ BlockJob *block_job_get_locked(const char *id); * Add @bs to the list of BlockDriverState that are involved in * @job. This means that all operations will be blocked on @bs while * @job exists. + * + * All block nodes must be drained. */ int GRAPH_WRLOCK block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 3185f3f429..4f3057844b 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -772,9 +772,11 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, tjob->bs = src; job = &tjob->common; + bdrv_drain_all_begin(); bdrv_graph_wrlock(); block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); switch (result) { case TEST_JOB_SUCCESS: From 77f3965ba7fed5b35212171a1e41c20c05a7ef11 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:50 +0200 Subject: [PATCH 1397/2760] block: move drain outside of bdrv_attach_child() This is part of resolving the deadlock mentioned in commit "block: move draining out of bdrv_change_aio_context() and mark GRAPH_RDLOCK". The function bdrv_attach_child() runs under the graph lock, so it is not allowed to drain. It is called by: 1. replication_start() 2. quorum_add_child() 3. bdrv_open_child_common() 4. Throughout test-bdrv-graph-mod.c and test-bdrv-drain.c unit tests. In all callers, a drained section is introduced. The function quorum_add_child() runs under the graph lock, so it is not actually allowed to drain. This will be addressed by the following commit. Signed-off-by: Fiona Ebner Reviewed-by: Kevin Wolf Message-ID: <20250530151125.955508-14-f.ebner@proxmox.com> Signed-off-by: Kevin Wolf --- block.c | 6 ++++-- block/quorum.c | 2 ++ block/replication.c | 5 +++++ tests/unit/test-bdrv-drain.c | 14 ++++++++++++++ tests/unit/test-bdrv-graph-mod.c | 10 ++++++++++ 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/block.c b/block.c index 17c34dc240..6fc87aa318 100644 --- a/block.c +++ b/block.c @@ -3269,6 +3269,8 @@ BdrvChild *bdrv_root_attach_child(BlockDriverState *child_bs, * * On failure NULL is returned, errp is set and the reference to * child_bs is also dropped. + * + * All block nodes must be drained. */ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, @@ -3283,7 +3285,6 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, GLOBAL_STATE_CODE(); - bdrv_drain_all_begin(); child = bdrv_attach_child_noperm(parent_bs, child_bs, child_name, child_class, child_role, tran, errp); if (!child) { @@ -3298,7 +3299,6 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, out: tran_finalize(tran, ret); - bdrv_drain_all_end(); bdrv_schedule_unref(child_bs); @@ -3789,10 +3789,12 @@ static BdrvChild *bdrv_open_child_common(const char *filename, return NULL; } + bdrv_drain_all_begin(); bdrv_graph_wrlock(); child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, errp); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); return child; } diff --git a/block/quorum.c b/block/quorum.c index ed8ce801ee..ea17b0ec13 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -1096,8 +1096,10 @@ quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, Error **errp) /* We can safely add the child now */ bdrv_ref(child_bs); + bdrv_drain_all_begin(); child = bdrv_attach_child(bs, child_bs, indexstr, &child_of_bds, BDRV_CHILD_DATA, errp); + bdrv_drain_all_end(); if (child == NULL) { s->next_child_index--; return; diff --git a/block/replication.c b/block/replication.c index 07f274de9e..54cbd03e00 100644 --- a/block/replication.c +++ b/block/replication.c @@ -540,6 +540,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, return; } + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_ref(hidden_disk->bs); @@ -549,6 +550,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, if (local_err) { error_propagate(errp, local_err); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); return; } @@ -559,6 +561,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, if (local_err) { error_propagate(errp, local_err); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); return; } @@ -571,12 +574,14 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, !check_top_bs(top_bs, bs)) { error_setg(errp, "No top_bs or it is invalid"); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); reopen_backing_file(bs, false, NULL); return; } bdrv_op_block_all(top_bs, s->blocker); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); s->backup_job = backup_job_create( NULL, s->secondary_disk->bs, s->hidden_disk->bs, diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 4f3057844b..ac76525e5a 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -1049,10 +1049,12 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, &error_abort); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); /* This child will be the one to pass to requests through to, and * it will stall until a drain occurs */ @@ -1060,21 +1062,25 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, &error_abort); child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; /* Takes our reference to child_bs */ + bdrv_drain_all_begin(); bdrv_graph_wrlock(); tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", &child_of_bds, BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); /* This child is just there to be deleted * (for detach_instead_of_delete == true) */ null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, &error_abort); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); blk_insert_bs(blk, bs, &error_abort); @@ -1157,6 +1163,7 @@ static void no_coroutine_fn detach_indirect_bh(void *opaque) bdrv_dec_in_flight(data->child_b->bs); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_unref_child(data->parent_b, data->child_b); @@ -1165,6 +1172,7 @@ static void no_coroutine_fn detach_indirect_bh(void *opaque) &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); } static void coroutine_mixed_fn detach_by_parent_aio_cb(void *opaque, int ret) @@ -1262,6 +1270,7 @@ static void TSA_NO_TSA test_detach_indirect(bool by_parent_cb) /* Set child relationships */ bdrv_ref(b); bdrv_ref(a); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_of_bds, BDRV_CHILD_DATA, &error_abort); @@ -1273,6 +1282,7 @@ static void TSA_NO_TSA test_detach_indirect(bool by_parent_cb) by_parent_cb ? &child_of_bds : &detach_by_driver_cb_class, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); g_assert_cmpint(parent_a->refcnt, ==, 1); g_assert_cmpint(parent_b->refcnt, ==, 1); @@ -1685,6 +1695,7 @@ static void test_drop_intermediate_poll(void) * Establish the chain last, so the chain links are the first * elements in the BDS.parents lists */ + bdrv_drain_all_begin(); bdrv_graph_wrlock(); for (i = 0; i < 3; i++) { if (i) { @@ -1694,6 +1705,7 @@ static void test_drop_intermediate_poll(void) } } bdrv_graph_wrunlock(); + bdrv_drain_all_end(); job = block_job_create("job", &test_simple_job_driver, NULL, job_node, 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); @@ -1940,10 +1952,12 @@ static void do_test_replace_child_mid_drain(int old_drain_count, new_child_bs->total_sectors = 1; bdrv_ref(old_child_bs); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds, BDRV_CHILD_COW, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); parent_s->setup_completed = true; for (i = 0; i < old_drain_count; i++) { diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c index d743abb4bb..7b03ebe4b0 100644 --- a/tests/unit/test-bdrv-graph-mod.c +++ b/tests/unit/test-bdrv-graph-mod.c @@ -137,10 +137,12 @@ static void test_update_perm_tree(void) blk_insert_bs(root, bs, &error_abort); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(filter, bs, "child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); ret = bdrv_append(filter, bs, NULL); g_assert_cmpint(ret, <, 0); @@ -204,11 +206,13 @@ static void test_should_update_child(void) bdrv_set_backing_hd(target, bs, &error_abort); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); g_assert(target->backing->bs == bs); bdrv_attach_child(filter, target, "target", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); bdrv_append(filter, bs, &error_abort); bdrv_graph_rdlock_main_loop(); @@ -244,6 +248,7 @@ static void test_parallel_exclusive_write(void) bdrv_ref(base); bdrv_ref(fl1); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(top, fl1, "backing", &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, @@ -257,6 +262,7 @@ static void test_parallel_exclusive_write(void) bdrv_replace_node(fl1, fl2, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); bdrv_drained_end(fl2); bdrv_drained_end(fl1); @@ -363,6 +369,7 @@ static void test_parallel_perm_update(void) */ bdrv_ref(base); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(top, ws, "file", &child_of_bds, BDRV_CHILD_DATA, &error_abort); @@ -377,6 +384,7 @@ static void test_parallel_perm_update(void) BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); /* Select fl1 as first child to be active */ s->selected = c_fl1; @@ -430,11 +438,13 @@ static void test_append_greedy_filter(void) BlockDriverState *base = no_perm_node("base"); BlockDriverState *fl = exclusive_writer_node("fl1"); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_attach_child(top, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); bdrv_append(fl, base, &error_abort); bdrv_unref(fl); From 0414930d3adfa89299eaea5ce92accab15d9fba5 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:51 +0200 Subject: [PATCH 1398/2760] block: move drain outside of quorum_add_child() This is part of resolving the deadlock mentioned in commit "block: move draining out of bdrv_change_aio_context() and mark GRAPH_RDLOCK". The quorum_add_child() callback runs under the graph lock, so it is not allowed to drain. It is only called as the .bdrv_add_child() callback, which is only called in the bdrv_add_child() function, which also runs under the graph lock. The bdrv_add_child() function is called by qmp_x_blockdev_change(), where a drained section is introduced. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-15-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 10 ++++++++-- block/quorum.c | 2 -- blockdev.c | 2 ++ include/block/block_int-common.h | 7 +++++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/block.c b/block.c index 6fc87aa318..f6c2f7e208 100644 --- a/block.c +++ b/block.c @@ -8220,8 +8220,10 @@ char *bdrv_dirname(BlockDriverState *bs, Error **errp) } /* - * Hot add/remove a BDS's child. So the user can take a child offline when - * it is broken and take a new child online + * Hot add a BDS's child. Used in combination with bdrv_del_child, so the user + * can take a child offline when it is broken and take a new child online. + * + * All block nodes must be drained. */ void bdrv_add_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, Error **errp) @@ -8261,6 +8263,10 @@ void bdrv_add_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, parent_bs->drv->bdrv_add_child(parent_bs, child_bs, errp); } +/* + * Hot remove a BDS's child. Used in combination with bdrv_add_child, so the + * user can take a child offline when it is broken and take a new child online. + */ void bdrv_del_child(BlockDriverState *parent_bs, BdrvChild *child, Error **errp) { BdrvChild *tmp; diff --git a/block/quorum.c b/block/quorum.c index ea17b0ec13..ed8ce801ee 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -1096,10 +1096,8 @@ quorum_add_child(BlockDriverState *bs, BlockDriverState *child_bs, Error **errp) /* We can safely add the child now */ bdrv_ref(child_bs); - bdrv_drain_all_begin(); child = bdrv_attach_child(bs, child_bs, indexstr, &child_of_bds, BDRV_CHILD_DATA, errp); - bdrv_drain_all_end(); if (child == NULL) { s->next_child_index--; return; diff --git a/blockdev.c b/blockdev.c index 750beba41f..bd5ca77619 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3531,6 +3531,7 @@ void qmp_x_blockdev_change(const char *parent, const char *child, BlockDriverState *parent_bs, *new_bs = NULL; BdrvChild *p_child; + bdrv_drain_all_begin(); bdrv_graph_wrlock(); parent_bs = bdrv_lookup_bs(parent, parent, errp); @@ -3568,6 +3569,7 @@ void qmp_x_blockdev_change(const char *parent, const char *child, out: bdrv_graph_wrunlock(); + bdrv_drain_all_end(); } BlockJobInfoList *qmp_query_block_jobs(Error **errp) diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 168f703fa1..f9e742f812 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -396,6 +396,13 @@ struct BlockDriver { int GRAPH_RDLOCK_PTR (*bdrv_probe_geometry)( BlockDriverState *bs, HDGeometry *geo); + /** + * Hot add a BDS's child. Used in combination with bdrv_del_child, so the + * user can take a child offline when it is broken and take a new child + * online. + * + * All block nodes must be drained. + */ void GRAPH_WRLOCK_PTR (*bdrv_add_child)( BlockDriverState *parent, BlockDriverState *child, Error **errp); From b13f54654546cbc0661d3fe9d25f7543535c2bee Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:52 +0200 Subject: [PATCH 1399/2760] block: move drain outside of bdrv_root_unref_child() This is part of resolving the deadlock mentioned in commit "block: move draining out of bdrv_change_aio_context() and mark GRAPH_RDLOCK". bdrv_root_unref_child() is called by: 1. blk_remove_bs(), where a drained section is introduced. 2. bdrv_unref_child(), which runs under the graph lock, so the drain will be moved further up to its callers. 3. block_job_remove_all_bdrv(), where a drained section is introduced. For all callers of bdrv_unref_child() and its generated bdrv_co_unref_child() coroutine variant, a drained section is introduced, they are not explicilty listed here. The caller quorum_del_child() holds the graph lock, so it is not actually allowed to drain. This will be addressed in the next commit. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-16-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 18 ++++++++++++++---- block/blklogwrites.c | 4 ++++ block/blkverify.c | 2 ++ block/block-backend.c | 2 ++ block/qcow2.c | 4 ++++ block/quorum.c | 6 ++++++ block/replication.c | 2 ++ block/snapshot.c | 2 ++ block/vmdk.c | 10 ++++++++++ blockjob.c | 2 ++ tests/unit/test-bdrv-drain.c | 4 ++++ 11 files changed, 52 insertions(+), 4 deletions(-) diff --git a/block.c b/block.c index f6c2f7e208..15a8ccb822 100644 --- a/block.c +++ b/block.c @@ -1721,12 +1721,14 @@ bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, open_failed: bs->drv = NULL; + bdrv_drain_all_begin(); bdrv_graph_wrlock(); if (bs->file != NULL) { bdrv_unref_child(bs, bs->file); assert(!bs->file); } bdrv_graph_wrunlock(); + bdrv_drain_all_end(); g_free(bs->opaque); bs->opaque = NULL; @@ -3305,7 +3307,11 @@ BdrvChild *bdrv_attach_child(BlockDriverState *parent_bs, return ret < 0 ? NULL : child; } -/* Callers must ensure that child->frozen is false. */ +/* + * Callers must ensure that child->frozen is false. + * + * All block nodes must be drained. + */ void bdrv_root_unref_child(BdrvChild *child) { BlockDriverState *child_bs = child->bs; @@ -3326,10 +3332,8 @@ void bdrv_root_unref_child(BdrvChild *child) * When the parent requiring a non-default AioContext is removed, the * node moves back to the main AioContext */ - bdrv_drain_all_begin(); bdrv_try_change_aio_context_locked(child_bs, qemu_get_aio_context(), NULL, NULL); - bdrv_drain_all_end(); } bdrv_schedule_unref(child_bs); @@ -3402,7 +3406,11 @@ bdrv_unset_inherits_from(BlockDriverState *root, BdrvChild *child, } } -/* Callers must ensure that child->frozen is false. */ +/* + * Callers must ensure that child->frozen is false. + * + * All block nodes must be drained. + */ void bdrv_unref_child(BlockDriverState *parent, BdrvChild *child) { GLOBAL_STATE_CODE(); @@ -5172,6 +5180,7 @@ static void bdrv_close(BlockDriverState *bs) bs->drv = NULL; } + bdrv_drain_all_begin(); bdrv_graph_wrlock(); QLIST_FOREACH_SAFE(child, &bs->children, next, next) { bdrv_unref_child(bs, child); @@ -5180,6 +5189,7 @@ static void bdrv_close(BlockDriverState *bs) assert(!bs->backing); assert(!bs->file); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); g_free(bs->opaque); bs->opaque = NULL; diff --git a/block/blklogwrites.c b/block/blklogwrites.c index b0f78c4bc7..70ac76f401 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -281,9 +281,11 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, ret = 0; fail_log: if (ret < 0) { + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_unref_child(bs, s->log_file); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); s->log_file = NULL; qemu_mutex_destroy(&s->mutex); } @@ -296,10 +298,12 @@ static void blk_log_writes_close(BlockDriverState *bs) { BDRVBlkLogWritesState *s = bs->opaque; + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_unref_child(bs, s->log_file); s->log_file = NULL; bdrv_graph_wrunlock(); + bdrv_drain_all_end(); qemu_mutex_destroy(&s->mutex); } diff --git a/block/blkverify.c b/block/blkverify.c index db79a36681..3a71f7498c 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -151,10 +151,12 @@ static void blkverify_close(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_unref_child(bs, s->test_file); s->test_file = NULL; bdrv_graph_wrunlock(); + bdrv_drain_all_end(); } static int64_t coroutine_fn GRAPH_RDLOCK diff --git a/block/block-backend.c b/block/block-backend.c index 24cae3cb55..68209bb2f7 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -889,9 +889,11 @@ void blk_remove_bs(BlockBackend *blk) root = blk->root; blk->root = NULL; + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_root_unref_child(root); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); } /* diff --git a/block/qcow2.c b/block/qcow2.c index 66fba89b41..45451a7ee8 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -1895,7 +1895,9 @@ qcow2_do_open(BlockDriverState *bs, QDict *options, int flags, g_free(s->image_data_file); if (open_data_file && has_data_file(bs)) { bdrv_graph_co_rdunlock(); + bdrv_drain_all_begin(); bdrv_co_unref_child(bs, s->data_file); + bdrv_drain_all_end(); bdrv_graph_co_rdlock(); s->data_file = NULL; } @@ -2821,9 +2823,11 @@ qcow2_do_close(BlockDriverState *bs, bool close_data_file) if (close_data_file && has_data_file(bs)) { GLOBAL_STATE_CODE(); bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_unref_child(bs, s->data_file); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); s->data_file = NULL; bdrv_graph_rdlock_main_loop(); } diff --git a/block/quorum.c b/block/quorum.c index ed8ce801ee..81407a38ee 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -1037,6 +1037,7 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, close_exit: /* cleanup on error */ + bdrv_drain_all_begin(); bdrv_graph_wrlock(); for (i = 0; i < s->num_children; i++) { if (!opened[i]) { @@ -1045,6 +1046,7 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, bdrv_unref_child(bs, s->children[i]); } bdrv_graph_wrunlock(); + bdrv_drain_all_end(); g_free(s->children); g_free(opened); exit: @@ -1057,11 +1059,13 @@ static void quorum_close(BlockDriverState *bs) BDRVQuorumState *s = bs->opaque; int i; + bdrv_drain_all_begin(); bdrv_graph_wrlock(); for (i = 0; i < s->num_children; i++) { bdrv_unref_child(bs, s->children[i]); } bdrv_graph_wrunlock(); + bdrv_drain_all_end(); g_free(s->children); } @@ -1143,7 +1147,9 @@ quorum_del_child(BlockDriverState *bs, BdrvChild *child, Error **errp) (s->num_children - i - 1) * sizeof(BdrvChild *)); s->children = g_renew(BdrvChild *, s->children, --s->num_children); + bdrv_drain_all_begin(); bdrv_unref_child(bs, child); + bdrv_drain_all_end(); quorum_refresh_flags(bs); } diff --git a/block/replication.c b/block/replication.c index 54cbd03e00..0879718854 100644 --- a/block/replication.c +++ b/block/replication.c @@ -656,12 +656,14 @@ static void replication_done(void *opaque, int ret) if (ret == 0) { s->stage = BLOCK_REPLICATION_DONE; + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_unref_child(bs, s->secondary_disk); s->secondary_disk = NULL; bdrv_unref_child(bs, s->hidden_disk); s->hidden_disk = NULL; bdrv_graph_wrunlock(); + bdrv_drain_all_end(); s->error = 0; } else { diff --git a/block/snapshot.c b/block/snapshot.c index 9f300a78bd..28c9c43621 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -291,9 +291,11 @@ int bdrv_snapshot_goto(BlockDriverState *bs, } /* .bdrv_open() will re-attach it */ + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_unref_child(bs, fallback); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp); memset(bs->opaque, 0, drv->instance_size); diff --git a/block/vmdk.c b/block/vmdk.c index 9c7ab037e1..89a7250120 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -271,6 +271,7 @@ static void vmdk_free_extents(BlockDriverState *bs) BDRVVmdkState *s = bs->opaque; VmdkExtent *e; + bdrv_drain_all_begin(); bdrv_graph_wrlock(); for (i = 0; i < s->num_extents; i++) { e = &s->extents[i]; @@ -283,6 +284,7 @@ static void vmdk_free_extents(BlockDriverState *bs) } } bdrv_graph_wrunlock(); + bdrv_drain_all_end(); g_free(s->extents); } @@ -1247,9 +1249,11 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, 0, 0, 0, 0, 0, &extent, errp); if (ret < 0) { bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_unref_child(bs, extent_file); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); bdrv_graph_rdlock_main_loop(); goto out; } @@ -1266,9 +1270,11 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, g_free(buf); if (ret) { bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_unref_child(bs, extent_file); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); bdrv_graph_rdlock_main_loop(); goto out; } @@ -1277,9 +1283,11 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, ret = vmdk_open_se_sparse(bs, extent_file, bs->open_flags, errp); if (ret) { bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_unref_child(bs, extent_file); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); bdrv_graph_rdlock_main_loop(); goto out; } @@ -1287,9 +1295,11 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, } else { error_setg(errp, "Unsupported extent type '%s'", type); bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_begin(); bdrv_graph_wrlock(); bdrv_unref_child(bs, extent_file); bdrv_graph_wrunlock(); + bdrv_drain_all_end(); bdrv_graph_rdlock_main_loop(); ret = -ENOTSUP; goto out; diff --git a/blockjob.c b/blockjob.c index 44991e3ff7..e68181a35b 100644 --- a/blockjob.c +++ b/blockjob.c @@ -198,6 +198,7 @@ void block_job_remove_all_bdrv(BlockJob *job) * one to make sure that such a concurrent access does not attempt * to process an already freed BdrvChild. */ + bdrv_drain_all_begin(); bdrv_graph_wrlock(); while (job->nodes) { GSList *l = job->nodes; @@ -211,6 +212,7 @@ void block_job_remove_all_bdrv(BlockJob *job) g_slist_free_1(l); } bdrv_graph_wrunlock(); + bdrv_drain_all_end(); } bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index ac76525e5a..59c2793725 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -955,11 +955,13 @@ static void bdrv_test_top_close(BlockDriverState *bs) { BdrvChild *c, *next_c; + bdrv_drain_all_begin(); bdrv_graph_wrlock(); QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { bdrv_unref_child(bs, c); } bdrv_graph_wrunlock(); + bdrv_drain_all_end(); } static int coroutine_fn GRAPH_RDLOCK @@ -1016,7 +1018,9 @@ static void coroutine_fn test_co_delete_by_drain(void *opaque) bdrv_graph_co_rdlock(); QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { bdrv_graph_co_rdunlock(); + bdrv_drain_all_begin(); bdrv_co_unref_child(bs, c); + bdrv_drain_all_end(); bdrv_graph_co_rdlock(); } bdrv_graph_co_rdunlock(); From d75f8ed1d7fc27cf1643e549cd006a68d3bf6ef1 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:53 +0200 Subject: [PATCH 1400/2760] block: move drain outside of quorum_del_child() The quorum_del_child() callback runs under the graph lock, so it is not allowed to drain. It is only called as the .bdrv_del_child() callback, which is only called in the bdrv_del_child() function, which also runs under the graph lock. The bdrv_del_child() function is called by qmp_x_blockdev_change(). A drained section was already introduced there by commit "block: move drain out of quorum_add_child()". This finally finishes moving out the drain to places that are not under the graph lock started in "block: move draining out of bdrv_change_aio_context() and mark GRAPH_RDLOCK". Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-17-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 2 ++ block/quorum.c | 2 -- include/block/block_int-common.h | 7 +++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/block.c b/block.c index 15a8ccb822..bfd4340b24 100644 --- a/block.c +++ b/block.c @@ -8276,6 +8276,8 @@ void bdrv_add_child(BlockDriverState *parent_bs, BlockDriverState *child_bs, /* * Hot remove a BDS's child. Used in combination with bdrv_add_child, so the * user can take a child offline when it is broken and take a new child online. + * + * All block nodes must be drained. */ void bdrv_del_child(BlockDriverState *parent_bs, BdrvChild *child, Error **errp) { diff --git a/block/quorum.c b/block/quorum.c index 81407a38ee..cc3bc5f4e7 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -1147,9 +1147,7 @@ quorum_del_child(BlockDriverState *bs, BdrvChild *child, Error **errp) (s->num_children - i - 1) * sizeof(BdrvChild *)); s->children = g_renew(BdrvChild *, s->children, --s->num_children); - bdrv_drain_all_begin(); bdrv_unref_child(bs, child); - bdrv_drain_all_end(); quorum_refresh_flags(bs); } diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index f9e742f812..925a3e7353 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -406,6 +406,13 @@ struct BlockDriver { void GRAPH_WRLOCK_PTR (*bdrv_add_child)( BlockDriverState *parent, BlockDriverState *child, Error **errp); + /** + * Hot remove a BDS's child. Used in combination with bdrv_add_child, so the + * user can take a child offline when it is broken and take a new child + * online. + * + * All block nodes must be drained. + */ void GRAPH_WRLOCK_PTR (*bdrv_del_child)( BlockDriverState *parent, BdrvChild *child, Error **errp); From 6f101614f95c889399352b8301917c0ac7919ae7 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:54 +0200 Subject: [PATCH 1401/2760] blockdev: drain while unlocked in internal_snapshot_action() This is in preparation to mark bdrv_drained_begin() as GRAPH_UNLOCKED. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-18-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- blockdev.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/blockdev.c b/blockdev.c index bd5ca77619..506755bef1 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1208,7 +1208,7 @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal, Error *local_err = NULL; const char *device; const char *name; - BlockDriverState *bs; + BlockDriverState *bs, *check_bs; QEMUSnapshotInfo old_sn, *sn; bool ret; int64_t rt; @@ -1216,7 +1216,7 @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal, int ret1; GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); + bdrv_graph_rdlock_main_loop(); tran_add(tran, &internal_snapshot_drv, state); @@ -1225,14 +1225,29 @@ static void internal_snapshot_action(BlockdevSnapshotInternal *internal, bs = qmp_get_root_bs(device, errp); if (!bs) { + bdrv_graph_rdunlock_main_loop(); return; } state->bs = bs; + /* Need to drain while unlocked. */ + bdrv_graph_rdunlock_main_loop(); /* Paired with .clean() */ bdrv_drained_begin(bs); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + /* Make sure the root bs did not change with the drain. */ + check_bs = qmp_get_root_bs(device, errp); + if (bs != check_bs) { + if (check_bs) { + error_setg(errp, "Block node of device '%s' unexpectedly changed", + device); + } /* else errp is already set */ + return; + } + if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, errp)) { return; } From 195a8a946a8681dfe7e8aa8d49db415693db5311 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:55 +0200 Subject: [PATCH 1402/2760] blockdev: drain while unlocked in external_snapshot_action() This is in preparation to mark bdrv_drained_begin() as GRAPH_UNLOCKED. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-19-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- blockdev.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/blockdev.c b/blockdev.c index 506755bef1..2e7fda6780 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1377,9 +1377,10 @@ static void external_snapshot_action(TransactionAction *action, const char *new_image_file; ExternalSnapshotState *state = g_new0(ExternalSnapshotState, 1); uint64_t perm, shared; + BlockDriverState *check_bs; /* TODO We'll eventually have to take a writer lock in this function */ - GRAPH_RDLOCK_GUARD_MAINLOOP(); + bdrv_graph_rdlock_main_loop(); tran_add(tran, &external_snapshot_drv, state); @@ -1412,11 +1413,25 @@ static void external_snapshot_action(TransactionAction *action, state->old_bs = bdrv_lookup_bs(device, node_name, errp); if (!state->old_bs) { + bdrv_graph_rdunlock_main_loop(); return; } + /* Need to drain while unlocked. */ + bdrv_graph_rdunlock_main_loop(); /* Paired with .clean() */ bdrv_drained_begin(state->old_bs); + GRAPH_RDLOCK_GUARD_MAINLOOP(); + + /* Make sure the associated bs did not change with the drain. */ + check_bs = bdrv_lookup_bs(device, node_name, errp); + if (state->old_bs != check_bs) { + if (check_bs) { + error_setg(errp, "Block node of device '%s' unexpectedly changed", + device); + } /* else errp is already set */ + return; + } if (!bdrv_is_inserted(state->old_bs)) { error_setg(errp, "Device '%s' has no medium", From fc1d2f3eac7946658b160db0b813b81288fb1778 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:56 +0200 Subject: [PATCH 1403/2760] block: mark bdrv_drained_begin() and friends as GRAPH_UNLOCKED All of bdrv_drain_all_begin(), bdrv_drain_all() and bdrv_drained_begin() poll and are not allowed to be called with the block graph lock held. Mark the function as such. Suggested-by: Kevin Wolf Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-20-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/block-global-state.h | 4 ++-- include/block/block-io.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 91f249b5ad..84a2a4ecd5 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -192,10 +192,10 @@ int bdrv_inactivate_all(void); int bdrv_flush_all(void); void bdrv_close_all(void); -void bdrv_drain_all_begin(void); +void GRAPH_UNLOCKED bdrv_drain_all_begin(void); void bdrv_drain_all_begin_nopoll(void); void bdrv_drain_all_end(void); -void bdrv_drain_all(void); +void GRAPH_UNLOCKED bdrv_drain_all(void); void bdrv_aio_cancel(BlockAIOCB *acb); diff --git a/include/block/block-io.h b/include/block/block-io.h index b99cc98d26..4cf83fb367 100644 --- a/include/block/block-io.h +++ b/include/block/block-io.h @@ -431,7 +431,7 @@ bdrv_drain_poll(BlockDriverState *bs, BdrvChild *ignore_parent, * * This function can be recursive. */ -void bdrv_drained_begin(BlockDriverState *bs); +void GRAPH_UNLOCKED bdrv_drained_begin(BlockDriverState *bs); /** * bdrv_do_drained_begin_quiesce: From ed8c62927e8facebb1e41b417daee3109e398712 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:57 +0200 Subject: [PATCH 1404/2760] iotests/graph-changes-while-io: remove image file after test Suggested-by: Kevin Wolf Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-21-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/tests/graph-changes-while-io | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qemu-iotests/tests/graph-changes-while-io b/tests/qemu-iotests/tests/graph-changes-while-io index 194fda500e..35489e3b5e 100755 --- a/tests/qemu-iotests/tests/graph-changes-while-io +++ b/tests/qemu-iotests/tests/graph-changes-while-io @@ -57,6 +57,7 @@ class TestGraphChangesWhileIO(QMPTestCase): def tearDown(self) -> None: self.qsd.stop() + os.remove(top) def test_blockdev_add_while_io(self) -> None: # Run qemu-img bench in the background From 09d98a018e1fd2db0bb73bbe9b4a7110c8ae354f Mon Sep 17 00:00:00 2001 From: Andrey Drobyshev Date: Fri, 30 May 2025 17:10:58 +0200 Subject: [PATCH 1405/2760] iotests/graph-changes-while-io: add test case with removal of lower snapshot This case is catching potential deadlock which takes place when job-dismiss is issued when I/O requests are processed in a separate iothread. See https://mail.gnu.org/archive/html/qemu-devel/2025-04/msg04421.html Signed-off-by: Andrey Drobyshev [FE: re-use top image and rename snap1->mid as suggested by Kevin Wolf remove image file after test as suggested by Kevin Wolf add type annotation for function argument to make mypy happy] Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-22-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- .../qemu-iotests/tests/graph-changes-while-io | 101 ++++++++++++++++-- .../tests/graph-changes-while-io.out | 4 +- 2 files changed, 96 insertions(+), 9 deletions(-) diff --git a/tests/qemu-iotests/tests/graph-changes-while-io b/tests/qemu-iotests/tests/graph-changes-while-io index 35489e3b5e..dca1167b6d 100755 --- a/tests/qemu-iotests/tests/graph-changes-while-io +++ b/tests/qemu-iotests/tests/graph-changes-while-io @@ -27,6 +27,7 @@ from iotests import imgfmt, qemu_img, qemu_img_create, qemu_io, \ top = os.path.join(iotests.test_dir, 'top.img') +mid = os.path.join(iotests.test_dir, 'mid.img') nbd_sock = os.path.join(iotests.sock_dir, 'nbd.sock') @@ -59,6 +60,15 @@ class TestGraphChangesWhileIO(QMPTestCase): self.qsd.stop() os.remove(top) + def _wait_for_blockjob(self, status: str) -> None: + done = False + while not done: + for event in self.qsd.get_qmp().get_events(wait=10.0): + if event['event'] != 'JOB_STATUS_CHANGE': + continue + if event['data']['status'] == status: + done = True + def test_blockdev_add_while_io(self) -> None: # Run qemu-img bench in the background bench_thr = Thread(target=do_qemu_img_bench) @@ -117,15 +127,92 @@ class TestGraphChangesWhileIO(QMPTestCase): 'device': 'job0', }) - cancelled = False - while not cancelled: - for event in self.qsd.get_qmp().get_events(wait=10.0): - if event['event'] != 'JOB_STATUS_CHANGE': - continue - if event['data']['status'] == 'null': - cancelled = True + self._wait_for_blockjob('null') + + bench_thr.join() + + def test_remove_lower_snapshot_while_io(self) -> None: + # Run qemu-img bench in the background + bench_thr = Thread(target=do_qemu_img_bench, args=(100000, )) + bench_thr.start() + + # While I/O is performed on 'node0' node, consequently add 2 snapshots + # on top of it, then remove (commit) them starting from lower one. + while bench_thr.is_alive(): + # Recreate snapshot images on every iteration + qemu_img_create('-f', imgfmt, mid, '1G') + qemu_img_create('-f', imgfmt, top, '1G') + + self.qsd.cmd('blockdev-add', { + 'driver': imgfmt, + 'node-name': 'mid', + 'file': { + 'driver': 'file', + 'filename': mid + } + }) + + self.qsd.cmd('blockdev-snapshot', { + 'node': 'node0', + 'overlay': 'mid', + }) + + self.qsd.cmd('blockdev-add', { + 'driver': imgfmt, + 'node-name': 'top', + 'file': { + 'driver': 'file', + 'filename': top + } + }) + + self.qsd.cmd('blockdev-snapshot', { + 'node': 'mid', + 'overlay': 'top', + }) + + self.qsd.cmd('block-commit', { + 'job-id': 'commit-mid', + 'device': 'top', + 'top-node': 'mid', + 'base-node': 'node0', + 'auto-finalize': True, + 'auto-dismiss': False, + }) + + self._wait_for_blockjob('concluded') + self.qsd.cmd('job-dismiss', { + 'id': 'commit-mid', + }) + + self.qsd.cmd('block-commit', { + 'job-id': 'commit-top', + 'device': 'top', + 'top-node': 'top', + 'base-node': 'node0', + 'auto-finalize': True, + 'auto-dismiss': False, + }) + + self._wait_for_blockjob('ready') + self.qsd.cmd('job-complete', { + 'id': 'commit-top', + }) + + self._wait_for_blockjob('concluded') + self.qsd.cmd('job-dismiss', { + 'id': 'commit-top', + }) + + self.qsd.cmd('blockdev-del', { + 'node-name': 'mid' + }) + self.qsd.cmd('blockdev-del', { + 'node-name': 'top' + }) bench_thr.join() + os.remove(mid) if __name__ == '__main__': # Format must support raw backing files diff --git a/tests/qemu-iotests/tests/graph-changes-while-io.out b/tests/qemu-iotests/tests/graph-changes-while-io.out index fbc63e62f8..8d7e996700 100644 --- a/tests/qemu-iotests/tests/graph-changes-while-io.out +++ b/tests/qemu-iotests/tests/graph-changes-while-io.out @@ -1,5 +1,5 @@ -.. +... ---------------------------------------------------------------------- -Ran 2 tests +Ran 3 tests OK From b04b7c79c478743d414a684673519431b022c808 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:10:59 +0200 Subject: [PATCH 1406/2760] block/io: remove duplicate GLOBAL_STATE_CODE() in bdrv_do_drained_end() Both commit ab61335025 ("block: drain from main loop thread in bdrv_co_yield_to_drain()") and commit d05ab380db ("block: Mark drain related functions GRAPH_RDLOCK") introduced a GLOBAL_STATE_CODE() macro in bdrv_do_drained_end(). The assertion of being in the main thread cannot change here, so keep only the earlier instance. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-23-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/io.c | 1 - 1 file changed, 1 deletion(-) diff --git a/block/io.c b/block/io.c index 4fd7768f9c..ac5c7174f6 100644 --- a/block/io.c +++ b/block/io.c @@ -413,7 +413,6 @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) /* At this point, we should be always running in the main loop. */ GLOBAL_STATE_CODE(); assert(bs->quiesce_counter > 0); - GLOBAL_STATE_CODE(); /* Re-enable things in child-to-parent order */ old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter); From 2e887187454e57d04522099d4f04d17137d6e05c Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Thu, 29 May 2025 16:31:47 -0400 Subject: [PATCH 1407/2760] iotests: fix 240 Commit 2e8e18c2e463 ("virtio-scsi: add iothread-vq-mapping parameter") removed the limitation that virtio-scsi devices must successfully set the AioContext on their BlockBackends. This was made possible thanks to the QEMU multi-queue block layer. This change broke qemu-iotests 240, which checks that adding a virtio-scsi device with a drive that is already in another AioContext will fail. Update the test to take the relaxed behavior into account. I considered removing this test case entirely, but the code coverage still seems valuable. Fixes: 2e8e18c2e463 ("virtio-scsi: add iothread-vq-mapping parameter") Reported-by: Thomas Huth Signed-off-by: Stefan Hajnoczi Reviewed-by: Eric Blake Tested-by: Eric Blake Message-ID: <20250529203147.180338-1-stefanha@redhat.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/240 | 2 -- tests/qemu-iotests/240.out | 4 +--- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/tests/qemu-iotests/240 b/tests/qemu-iotests/240 index 9b281e1dc0..f8af9ff648 100755 --- a/tests/qemu-iotests/240 +++ b/tests/qemu-iotests/240 @@ -81,8 +81,6 @@ class TestCase(iotests.QMPTestCase): self.vm.qmp_log('device_del', id='scsi-hd0') self.vm.event_wait('DEVICE_DELETED') - self.vm.qmp_log('device_add', id='scsi-hd1', driver='scsi-hd', drive='hd0', bus="scsi1.0") - self.vm.qmp_log('device_del', id='scsi-hd1') self.vm.event_wait('DEVICE_DELETED') self.vm.qmp_log('blockdev-del', node_name='hd0') diff --git a/tests/qemu-iotests/240.out b/tests/qemu-iotests/240.out index 89ed25e506..10dcc42e06 100644 --- a/tests/qemu-iotests/240.out +++ b/tests/qemu-iotests/240.out @@ -46,10 +46,8 @@ {"execute": "device_add", "arguments": {"bus": "scsi0.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd0"}} {"return": {}} {"execute": "device_add", "arguments": {"bus": "scsi1.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd1"}} -{"error": {"class": "GenericError", "desc": "Cannot change iothread of active block backend"}} -{"execute": "device_del", "arguments": {"id": "scsi-hd0"}} {"return": {}} -{"execute": "device_add", "arguments": {"bus": "scsi1.0", "drive": "hd0", "driver": "scsi-hd", "id": "scsi-hd1"}} +{"execute": "device_del", "arguments": {"id": "scsi-hd0"}} {"return": {}} {"execute": "device_del", "arguments": {"id": "scsi-hd1"}} {"return": {}} From eef2dd03f948a512499775043bdc0c5c88d8a2dd Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 23 May 2025 09:02:11 +0200 Subject: [PATCH 1408/2760] hw/core/qdev-properties-system: Add missing return in set_drive_helper() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, changing the 'drive' property of e.g. a scsi-hd object will result in an assertion failure if the aio context of the block node it's replaced with doesn't match the current aio context: > bdrv_replace_child_noperm: Assertion `bdrv_get_aio_context(old_bs) == > bdrv_get_aio_context(new_bs)' failed. The problematic scenario is already detected, but a 'return' statement was missing. Cc: qemu-stable@nongnu.org Fixes: d1a58c176a ("qdev: allow setting drive property for realized device") Signed-off-by: Fiona Ebner Message-ID: <20250523070211.280498-1-f.ebner@proxmox.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- hw/core/qdev-properties-system.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 8e11e6388b..24e145d870 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -145,6 +145,7 @@ static void set_drive_helper(Object *obj, Visitor *v, const char *name, if (ctx != bdrv_get_aio_context(bs)) { error_setg(errp, "Different aio context is not supported for new " "node"); + return; } blk_replace_bs(blk, bs, errp); From aca0a504522ec2e5d077bf78a2acdb165f1e0ae2 Mon Sep 17 00:00:00 2001 From: Edmund Raile Date: Mon, 19 May 2025 11:24:23 +0000 Subject: [PATCH 1409/2760] vfio/igd: OpRegion not found fix error typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Edmund Raile Reviewed-by: Tomita Moeko Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/MFFbQoTpea_CK5ELq8oJ-a3Q57wo7ywQlrIqDvdIDKhUuCm59VUz2QzvdojO5r_wb_7SHifU0Kym3loj4eASPhdzYpjtiMCTePzyg1zrroo=@protonmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index e7952d15a0..5b6341c5bf 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -203,7 +203,7 @@ static bool vfio_pci_igd_opregion_detect(VFIOPCIDevice *vdev, VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, opregion); if (ret) { error_setg_errno(errp, -ret, - "Device does not supports IGD OpRegion feature"); + "Device does not support IGD OpRegion feature"); return false; } From 493a06a2eda3346923f2aab760d8280c78039dc7 Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 20 May 2025 17:25:30 +0100 Subject: [PATCH 1410/2760] vfio: add more VFIOIOMMUClass docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some additional doc comments for these class methods. Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250520162530.2194548-1-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- include/hw/vfio/vfio-container-base.h | 75 +++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 3d392b0fd8..f9e561cb08 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -115,13 +115,56 @@ OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU) struct VFIOIOMMUClass { ObjectClass parent_class; - /* basic feature */ + /** + * @setup + * + * Perform basic setup of the container, including configuring IOMMU + * capabilities, IOVA ranges, supported page sizes, etc. + * + * @bcontainer: #VFIOContainerBase + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns true to indicate success and false for error. + */ bool (*setup)(VFIOContainerBase *bcontainer, Error **errp); + + /** + * @listener_begin + * + * Called at the beginning of an address space update transaction. + * See #MemoryListener. + * + * @bcontainer: #VFIOContainerBase + */ void (*listener_begin)(VFIOContainerBase *bcontainer); + + /** + * @listener_commit + * + * Called at the end of an address space update transaction, + * See #MemoryListener. + * + * @bcontainer: #VFIOContainerBase + */ void (*listener_commit)(VFIOContainerBase *bcontainer); + + /** + * @dma_map + * + * Map an address range into the container. + * + * @bcontainer: #VFIOContainerBase to use + * @iova: start address to map + * @size: size of the range to map + * @vaddr: process virtual address of mapping + * @readonly: true if mapping should be readonly + * + * Returns 0 to indicate success and -errno otherwise. + */ int (*dma_map)(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly); + /** * @dma_unmap * @@ -132,12 +175,38 @@ struct VFIOIOMMUClass { * @size: size of the range to unmap * @iotlb: The IOMMU TLB mapping entry (or NULL) * @unmap_all: if set, unmap the entire address space + * + * Returns 0 to indicate success and -errno otherwise. */ int (*dma_unmap)(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all); + + + /** + * @attach_device + * + * Associate the given device with a container and do some related + * initialization of the device context. + * + * @name: name of the device + * @vbasedev: the device + * @as: address space to use + * @errp: pointer to Error*, to store an error if it happens. + * + * Returns true to indicate success and false for error. + */ bool (*attach_device)(const char *name, VFIODevice *vbasedev, AddressSpace *as, Error **errp); + + /* + * @detach_device + * + * Detach the given device from its container and clean up any necessary + * state. + * + * @vbasedev: the device to disassociate + */ void (*detach_device)(VFIODevice *vbasedev); /* migration feature */ @@ -152,7 +221,7 @@ struct VFIOIOMMUClass { * @start: indicates whether to start or stop dirty pages tracking * @errp: pointer to Error*, to store an error if it happens. * - * Returns zero to indicate success and negative for error + * Returns zero to indicate success and negative for error. */ int (*set_dirty_page_tracking)(const VFIOContainerBase *bcontainer, bool start, Error **errp); @@ -167,7 +236,7 @@ struct VFIOIOMMUClass { * @size: size of iova range * @errp: pointer to Error*, to store an error if it happens. * - * Returns zero to indicate success and negative for error + * Returns zero to indicate success and negative for error. */ int (*query_dirty_bitmap)(const VFIOContainerBase *bcontainer, VFIOBitmap *vbmap, hwaddr iova, hwaddr size, Error **errp); From 33528f255a710d394ec692de54baebff1f494b5b Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 20 May 2025 16:03:51 +0100 Subject: [PATCH 1411/2760] vfio: move more cleanup into vfio_pci_put_device() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All of the cleanup can be done in the same place, and vfio-user will want to do the same. Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250520150419.2172078-3-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index a1bfdfe375..d96b55f80c 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2854,6 +2854,18 @@ static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) static void vfio_pci_put_device(VFIOPCIDevice *vdev) { + vfio_display_finalize(vdev); + vfio_bars_finalize(vdev); + g_free(vdev->emulated_config_bits); + g_free(vdev->rom); + /* + * XXX Leaking igd_opregion is not an oversight, we can't remove the + * fw_cfg entry therefore leaking this allocation seems like the safest + * option. + * + * g_free(vdev->igd_opregion); + */ + vfio_device_detach(&vdev->vbasedev); g_free(vdev->vbasedev.name); @@ -3302,17 +3314,6 @@ static void vfio_instance_finalize(Object *obj) { VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); - vfio_display_finalize(vdev); - vfio_bars_finalize(vdev); - g_free(vdev->emulated_config_bits); - g_free(vdev->rom); - /* - * XXX Leaking igd_opregion is not an oversight, we can't remove the - * fw_cfg entry therefore leaking this allocation seems like the safest - * option. - * - * g_free(vdev->igd_opregion); - */ vfio_pci_put_device(vdev); } From 15399459467ff62fff7e28a554f44364fbf4204d Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 20 May 2025 16:03:52 +0100 Subject: [PATCH 1412/2760] vfio: move config space read into vfio_pci_config_setup() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Small cleanup that reduces duplicate code for vfio-user and reduces the size of vfio_realize(); while we're here, correct that name to vfio_pci_realize(). Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250520150419.2172078-4-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index d96b55f80c..a873f82aeb 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3017,6 +3017,19 @@ static bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) { PCIDevice *pdev = &vdev->pdev; VFIODevice *vbasedev = &vdev->vbasedev; + uint32_t config_space_size; + int ret; + + config_space_size = MIN(pci_config_size(&vdev->pdev), vdev->config_size); + + /* Get a copy of config space */ + ret = vfio_pci_config_space_read(vdev, 0, config_space_size, + vdev->pdev.config); + if (ret < (int)config_space_size) { + ret = ret < 0 ? -ret : EFAULT; + error_setg_errno(errp, ret, "failed to read device config space"); + return false; + } /* vfio emulates a lot for us, but some bits need extra love */ vdev->emulated_config_bits = g_malloc0(vdev->config_size); @@ -3138,15 +3151,14 @@ static bool vfio_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) return true; } -static void vfio_realize(PCIDevice *pdev, Error **errp) +static void vfio_pci_realize(PCIDevice *pdev, Error **errp) { ERRP_GUARD(); VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); VFIODevice *vbasedev = &vdev->vbasedev; - int i, ret; + int i; char uuid[UUID_STR_LEN]; g_autofree char *name = NULL; - uint32_t config_space_size; if (vbasedev->fd < 0 && !vbasedev->sysfsdev) { if (!(~vdev->host.domain || ~vdev->host.bus || @@ -3201,17 +3213,6 @@ static void vfio_realize(PCIDevice *pdev, Error **errp) goto error; } - config_space_size = MIN(pci_config_size(&vdev->pdev), vdev->config_size); - - /* Get a copy of config space */ - ret = vfio_pci_config_space_read(vdev, 0, config_space_size, - vdev->pdev.config); - if (ret < (int)config_space_size) { - ret = ret < 0 ? -ret : EFAULT; - error_setg_errno(errp, ret, "failed to read device config space"); - goto error; - } - if (!vfio_pci_config_setup(vdev, errp)) { goto error; } @@ -3515,7 +3516,7 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, const void *data) object_class_property_add_str(klass, "fd", NULL, vfio_pci_set_fd); #endif dc->desc = "VFIO-based PCI device assignment"; - pdc->realize = vfio_realize; + pdc->realize = vfio_pci_realize; object_class_property_set_description(klass, /* 1.3 */ "host", From a483ad534734ad0c9e1fea34d959d0e62838b89e Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 20 May 2025 16:03:53 +0100 Subject: [PATCH 1413/2760] vfio: refactor out IRQ signalling setup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This makes for a slightly more readable vfio_msix_vector_do_use() implementation, and we will rely on this shortly. Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250520150419.2172078-5-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index a873f82aeb..b1250d85bf 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -511,6 +511,25 @@ static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg, kvm_irqchip_commit_routes(kvm_state); } +static void set_irq_signalling(VFIODevice *vbasedev, VFIOMSIVector *vector, + unsigned int nr) +{ + Error *err = NULL; + int32_t fd; + + if (vector->virq >= 0) { + fd = event_notifier_get_fd(&vector->kvm_interrupt); + } else { + fd = event_notifier_get_fd(&vector->interrupt); + } + + if (!vfio_device_irq_set_signaling(vbasedev, VFIO_PCI_MSIX_IRQ_INDEX, nr, + VFIO_IRQ_SET_ACTION_TRIGGER, + fd, &err)) { + error_reportf_err(err, VFIO_MSG_PREFIX, vbasedev->name); + } +} + static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, MSIMessage *msg, IOHandler *handler) { @@ -583,21 +602,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, strerror(-ret)); } } else { - Error *err = NULL; - int32_t fd; - - if (vector->virq >= 0) { - fd = event_notifier_get_fd(&vector->kvm_interrupt); - } else { - fd = event_notifier_get_fd(&vector->interrupt); - } - - if (!vfio_device_irq_set_signaling(&vdev->vbasedev, - VFIO_PCI_MSIX_IRQ_INDEX, nr, - VFIO_IRQ_SET_ACTION_TRIGGER, fd, - &err)) { - error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); - } + set_irq_signalling(&vdev->vbasedev, vector, nr); } } From 1c729ca8860a5197da62d1fa68d6085a6ec0acd3 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 21 May 2025 19:03:01 +0800 Subject: [PATCH 1414/2760] vfio/iommufd: Add comment emphasizing no movement of hiod->realize() call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The nested IOMMU support needs device and hwpt id which are generated only after attachment. Hiod encapsulates these information in realize() and passes to vIOMMU. Suggested-by: Cédric Le Goater Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250521110301.3313877-1-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index af1c7ab10a..6b2696793f 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -592,6 +592,10 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, goto err_listener_register; } + /* + * Do not move this code before attachment! The nested IOMMU support + * needs device and hwpt id which are generated only after attachment. + */ if (!vfio_device_hiod_create_and_realize(vbasedev, TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO, errp)) { goto err_listener_register; From 0992ea07dbb58d347c37d31e65d87893280b7f23 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Thu, 22 May 2025 23:16:36 +0800 Subject: [PATCH 1415/2760] vfio/igd: Fix incorrect error propagation in vfio_pci_igd_opregion_detect() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In vfio_pci_igd_opregion_detect(), errp will be set when the device does not have OpRegion or is hotplugged. This errp will be propagated to pci_qdev_realize(), which interprets it as failure, causing unexpected termination on devices without OpRegion like SR-IOV VFs or discrete GPUs. Fix it by not setting errp in vfio_pci_igd_opregion_detect(). This patch also checks if the device has OpRegion before hotplug status to prevent unwanted warning messages on non-IGD devices. Fixes: c0273e77f2d7 ("vfio/igd: Detect IGD device by OpRegion") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2968 Reported-by: Edmund Raile Link: https://lore.kernel.org/qemu-devel/30044d14-17ec-46e3-b9c3-63d27a5bde27@gmail.com Tested-by: Edmund Raile Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Reviewed-by: Corvin Köhne Link: https://lore.kernel.org/qemu-devel/20250522151636.20001-1-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 5b6341c5bf..e7a9d1ffc1 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -187,23 +187,21 @@ static bool vfio_pci_igd_opregion_init(VFIOPCIDevice *vdev, } static bool vfio_pci_igd_opregion_detect(VFIOPCIDevice *vdev, - struct vfio_region_info **opregion, - Error **errp) + struct vfio_region_info **opregion) { int ret; - /* Hotplugging is not supported for opregion access */ - if (vdev->pdev.qdev.hotplugged) { - error_setg(errp, "IGD OpRegion is not supported on hotplugged device"); - return false; - } - ret = vfio_device_get_region_info_type(&vdev->vbasedev, VFIO_REGION_TYPE_PCI_VENDOR_TYPE | PCI_VENDOR_ID_INTEL, VFIO_REGION_SUBTYPE_INTEL_IGD_OPREGION, opregion); if (ret) { - error_setg_errno(errp, -ret, - "Device does not support IGD OpRegion feature"); + return false; + } + + /* Hotplugging is not supported for opregion access */ + if (vdev->pdev.qdev.hotplugged) { + warn_report("IGD device detected, but OpRegion is not supported " + "on hotplugged device."); return false; } @@ -524,7 +522,7 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) } /* IGD device always comes with OpRegion */ - if (!vfio_pci_igd_opregion_detect(vdev, &opregion, errp)) { + if (!vfio_pci_igd_opregion_detect(vdev, &opregion)) { return true; } info_report("OpRegion detected on Intel display %x.", vdev->device_id); @@ -695,7 +693,7 @@ static bool vfio_pci_kvmgt_config_quirk(VFIOPCIDevice *vdev, Error **errp) return true; } - if (!vfio_pci_igd_opregion_detect(vdev, &opregion, errp)) { + if (!vfio_pci_igd_opregion_detect(vdev, &opregion)) { /* Should never reach here, KVMGT always emulates OpRegion */ return false; } From e3353d63e15d0b1109257d55e265a889e8c508f8 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Mon, 19 May 2025 06:26:43 -0700 Subject: [PATCH 1416/2760] vfio: return mr from vfio_get_xlat_addr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modify memory_get_xlat_addr and vfio_get_xlat_addr to return the memory region that the translated address is found in. This will be needed by CPR in a subsequent patch to map blocks using IOMMU_IOAS_MAP_FILE. Also return the xlat offset, so we can simplify the interface by removing the out parameters that can be trivially derived from mr and xlat. Lastly, rename the functions to to memory_translate_iotlb() and vfio_translate_iotlb(). Signed-off-by: Steve Sistare Acked-by: David Hildenbrand Reviewed-by: Cédric Le Goater Reviewed-by: John Levon Acked-by: Michael S. Tsirkin Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1747661203-136490-1-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/listener.c | 33 ++++++++++++++++++++++----------- hw/virtio/vhost-vdpa.c | 9 +++++++-- include/system/memory.h | 19 +++++++++---------- system/memory.c | 32 +++++++------------------------- 4 files changed, 45 insertions(+), 48 deletions(-) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index bfacb3d8d9..38e3dc82cf 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -90,16 +90,17 @@ static bool vfio_listener_skipped_section(MemoryRegionSection *section) section->offset_within_address_space & (1ULL << 63); } -/* Called with rcu_read_lock held. */ -static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, - ram_addr_t *ram_addr, bool *read_only, - Error **errp) +/* + * Called with rcu_read_lock held. + * The returned MemoryRegion must not be accessed after calling rcu_read_unlock. + */ +static MemoryRegion *vfio_translate_iotlb(IOMMUTLBEntry *iotlb, hwaddr *xlat_p, + Error **errp) { - bool ret, mr_has_discard_manager; + MemoryRegion *mr; - ret = memory_get_xlat_addr(iotlb, vaddr, ram_addr, read_only, - &mr_has_discard_manager, errp); - if (ret && mr_has_discard_manager) { + mr = memory_translate_iotlb(iotlb, xlat_p, errp); + if (mr && memory_region_has_ram_discard_manager(mr)) { /* * Malicious VMs might trigger discarding of IOMMU-mapped memory. The * pages will remain pinned inside vfio until unmapped, resulting in a @@ -118,7 +119,7 @@ static bool vfio_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, " intended via an IOMMU. It's possible to mitigate " " by setting/adjusting RLIMIT_MEMLOCK."); } - return ret; + return mr; } static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) @@ -126,6 +127,8 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) VFIOGuestIOMMU *giommu = container_of(n, VFIOGuestIOMMU, n); VFIOContainerBase *bcontainer = giommu->bcontainer; hwaddr iova = iotlb->iova + giommu->iommu_offset; + MemoryRegion *mr; + hwaddr xlat; void *vaddr; int ret; Error *local_err = NULL; @@ -150,10 +153,14 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) { bool read_only; - if (!vfio_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, &local_err)) { + mr = vfio_translate_iotlb(iotlb, &xlat, &local_err); + if (!mr) { error_report_err(local_err); goto out; } + vaddr = memory_region_get_ram_ptr(mr) + xlat; + read_only = !(iotlb->perm & IOMMU_WO) || mr->readonly; + /* * vaddr is only valid until rcu_read_unlock(). But after * vfio_dma_map has set up the mapping the pages will be @@ -1010,6 +1017,8 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) ram_addr_t translated_addr; Error *local_err = NULL; int ret = -EINVAL; + MemoryRegion *mr; + hwaddr xlat; trace_vfio_iommu_map_dirty_notify(iova, iova + iotlb->addr_mask); @@ -1021,9 +1030,11 @@ static void vfio_iommu_map_dirty_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) } rcu_read_lock(); - if (!vfio_get_xlat_addr(iotlb, NULL, &translated_addr, NULL, &local_err)) { + mr = vfio_translate_iotlb(iotlb, &xlat, &local_err); + if (!mr) { goto out_unlock; } + translated_addr = memory_region_get_ram_addr(mr) + xlat; ret = vfio_container_query_dirty_bitmap(bcontainer, iova, iotlb->addr_mask + 1, translated_addr, &local_err); diff --git a/hw/virtio/vhost-vdpa.c b/hw/virtio/vhost-vdpa.c index e20da95f30..7061b6e1a3 100644 --- a/hw/virtio/vhost-vdpa.c +++ b/hw/virtio/vhost-vdpa.c @@ -209,6 +209,8 @@ static void vhost_vdpa_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) int ret; Int128 llend; Error *local_err = NULL; + MemoryRegion *mr; + hwaddr xlat; if (iotlb->target_as != &address_space_memory) { error_report("Wrong target AS \"%s\", only system memory is allowed", @@ -228,11 +230,14 @@ static void vhost_vdpa_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) if ((iotlb->perm & IOMMU_RW) != IOMMU_NONE) { bool read_only; - if (!memory_get_xlat_addr(iotlb, &vaddr, NULL, &read_only, NULL, - &local_err)) { + mr = memory_translate_iotlb(iotlb, &xlat, &local_err); + if (!mr) { error_report_err(local_err); return; } + vaddr = memory_region_get_ram_ptr(mr) + xlat; + read_only = !(iotlb->perm & IOMMU_WO) || mr->readonly; + ret = vhost_vdpa_dma_map(s, VHOST_VDPA_GUEST_PA_ASID, iova, iotlb->addr_mask + 1, vaddr, read_only); if (ret) { diff --git a/include/system/memory.h b/include/system/memory.h index fc35a0dcad..0848690ea4 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -739,21 +739,20 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, RamDiscardListener *rdl); /** - * memory_get_xlat_addr: Extract addresses from a TLB entry + * memory_translate_iotlb: Extract addresses from a TLB entry. + * Called with rcu_read_lock held. * * @iotlb: pointer to an #IOMMUTLBEntry - * @vaddr: virtual address - * @ram_addr: RAM address - * @read_only: indicates if writes are allowed - * @mr_has_discard_manager: indicates memory is controlled by a - * RamDiscardManager + * @xlat_p: return the offset of the entry from the start of the returned + * MemoryRegion. * @errp: pointer to Error*, to store an error if it happens. * - * Return: true on success, else false setting @errp with error. + * Return: On success, return the MemoryRegion containing the @iotlb translated + * addr. The MemoryRegion must not be accessed after rcu_read_unlock. + * On failure, return NULL, setting @errp with error. */ -bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, - ram_addr_t *ram_addr, bool *read_only, - bool *mr_has_discard_manager, Error **errp); +MemoryRegion *memory_translate_iotlb(IOMMUTLBEntry *iotlb, hwaddr *xlat_p, + Error **errp); typedef struct CoalescedMemoryRange CoalescedMemoryRange; typedef struct MemoryRegionIoeventfd MemoryRegionIoeventfd; diff --git a/system/memory.c b/system/memory.c index 63b983efcd..306e9ff9eb 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2174,18 +2174,14 @@ void ram_discard_manager_unregister_listener(RamDiscardManager *rdm, } /* Called with rcu_read_lock held. */ -bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, - ram_addr_t *ram_addr, bool *read_only, - bool *mr_has_discard_manager, Error **errp) +MemoryRegion *memory_translate_iotlb(IOMMUTLBEntry *iotlb, hwaddr *xlat_p, + Error **errp) { MemoryRegion *mr; hwaddr xlat; hwaddr len = iotlb->addr_mask + 1; bool writable = iotlb->perm & IOMMU_WO; - if (mr_has_discard_manager) { - *mr_has_discard_manager = false; - } /* * The IOMMU TLB entry we have just covers translation through * this IOMMU to its immediate target. We need to translate @@ -2195,7 +2191,7 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, &xlat, &len, writable, MEMTXATTRS_UNSPECIFIED); if (!memory_region_is_ram(mr)) { error_setg(errp, "iommu map to non memory area %" HWADDR_PRIx "", xlat); - return false; + return NULL; } else if (memory_region_has_ram_discard_manager(mr)) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(mr); MemoryRegionSection tmp = { @@ -2203,9 +2199,6 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, .offset_within_region = xlat, .size = int128_make64(len), }; - if (mr_has_discard_manager) { - *mr_has_discard_manager = true; - } /* * Malicious VMs can map memory into the IOMMU, which is expected * to remain discarded. vfio will pin all pages, populating memory. @@ -2216,7 +2209,7 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, error_setg(errp, "iommu map to discarded memory (e.g., unplugged" " via virtio-mem): %" HWADDR_PRIx "", iotlb->translated_addr); - return false; + return NULL; } } @@ -2226,22 +2219,11 @@ bool memory_get_xlat_addr(IOMMUTLBEntry *iotlb, void **vaddr, */ if (len & iotlb->addr_mask) { error_setg(errp, "iommu has granularity incompatible with target AS"); - return false; - } - - if (vaddr) { - *vaddr = memory_region_get_ram_ptr(mr) + xlat; - } - - if (ram_addr) { - *ram_addr = memory_region_get_ram_addr(mr) + xlat; - } - - if (read_only) { - *read_only = !writable || mr->readonly; + return NULL; } - return true; + *xlat_p = xlat; + return mr; } void memory_region_set_log(MemoryRegion *mr, bool log, unsigned client) From 44d0acf834b090c2717934983ac7678a602b2da5 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 21 May 2025 22:55:34 +0100 Subject: [PATCH 1417/2760] vfio/container: pass MemoryRegion to DMA operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass through the MemoryRegion to DMA operation handlers of vfio containers. The vfio-user container will need this later, to translate the vaddr into an offset for the dma map vfio-user message; CPR will also will need this. Originally-by: John Johnson Signed-off-by: Jagannathan Raman Signed-off-by: Elena Ufimtseva Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250521215534.2688540-1-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/container-base.c | 4 ++-- hw/vfio/container.c | 3 ++- hw/vfio/iommufd.c | 3 ++- hw/vfio/listener.c | 6 +++--- include/hw/vfio/vfio-container-base.h | 9 +++++---- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index 1c6ca94b60..d834bd4822 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -75,12 +75,12 @@ void vfio_address_space_insert(VFIOAddressSpace *space, int vfio_container_dma_map(VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, - void *vaddr, bool readonly) + void *vaddr, bool readonly, MemoryRegion *mr) { VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); g_assert(vioc->dma_map); - return vioc->dma_map(bcontainer, iova, size, vaddr, readonly); + return vioc->dma_map(bcontainer, iova, size, vaddr, readonly, mr); } int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, diff --git a/hw/vfio/container.c b/hw/vfio/container.c index a9f0dbaec4..a8c76eb481 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -207,7 +207,8 @@ static int vfio_legacy_dma_unmap(const VFIOContainerBase *bcontainer, } static int vfio_legacy_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, - ram_addr_t size, void *vaddr, bool readonly) + ram_addr_t size, void *vaddr, bool readonly, + MemoryRegion *mr) { const VFIOContainer *container = container_of(bcontainer, VFIOContainer, bcontainer); diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 6b2696793f..46a3b36301 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -34,7 +34,8 @@ TYPE_HOST_IOMMU_DEVICE_IOMMUFD "-vfio" static int iommufd_cdev_map(const VFIOContainerBase *bcontainer, hwaddr iova, - ram_addr_t size, void *vaddr, bool readonly) + ram_addr_t size, void *vaddr, bool readonly, + MemoryRegion *mr) { const VFIOIOMMUFDContainer *container = container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index 38e3dc82cf..7495866123 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -170,7 +170,7 @@ static void vfio_iommu_map_notify(IOMMUNotifier *n, IOMMUTLBEntry *iotlb) */ ret = vfio_container_dma_map(bcontainer, iova, iotlb->addr_mask + 1, vaddr, - read_only); + read_only, mr); if (ret) { error_report("vfio_container_dma_map(%p, 0x%"HWADDR_PRIx", " "0x%"HWADDR_PRIx", %p) = %d (%s)", @@ -240,7 +240,7 @@ static int vfio_ram_discard_notify_populate(RamDiscardListener *rdl, vaddr = memory_region_get_ram_ptr(section->mr) + start; ret = vfio_container_dma_map(bcontainer, iova, next - start, - vaddr, section->readonly); + vaddr, section->readonly, section->mr); if (ret) { /* Rollback */ vfio_ram_discard_notify_discard(rdl, section); @@ -564,7 +564,7 @@ static void vfio_listener_region_add(MemoryListener *listener, } ret = vfio_container_dma_map(bcontainer, iova, int128_get64(llsize), - vaddr, section->readonly); + vaddr, section->readonly, section->mr); if (ret) { error_setg(&err, "vfio_container_dma_map(%p, 0x%"HWADDR_PRIx", " "0x%"HWADDR_PRIx", %p) = %d (%s)", diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index f9e561cb08..3feb773e5f 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -78,7 +78,7 @@ void vfio_address_space_insert(VFIOAddressSpace *space, int vfio_container_dma_map(VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, - void *vaddr, bool readonly); + void *vaddr, bool readonly, MemoryRegion *mr); int vfio_container_dma_unmap(VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all); @@ -151,20 +151,21 @@ struct VFIOIOMMUClass { /** * @dma_map * - * Map an address range into the container. + * Map an address range into the container. Note that the memory region is + * referenced within an RCU read lock region across this call. * * @bcontainer: #VFIOContainerBase to use * @iova: start address to map * @size: size of the range to map * @vaddr: process virtual address of mapping * @readonly: true if mapping should be readonly + * @mr: the memory region for this mapping * * Returns 0 to indicate success and -errno otherwise. */ int (*dma_map)(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, - void *vaddr, bool readonly); - + void *vaddr, bool readonly, MemoryRegion *mr); /** * @dma_unmap * From 5c47679cb6e6d07cdc47b611f719df88308673aa Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 4 Jun 2025 14:21:12 +0800 Subject: [PATCH 1418/2760] backends/iommufd: Add a helper to invalidate user-managed HWPT MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This helper passes cache invalidation request from guest to invalidate stage-1 page table cache in host hardware. Signed-off-by: Nicolin Chen Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Reviewed-by: Eric Auger Link: https://lore.kernel.org/qemu-devel/20250604062115.4004200-2-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- backends/iommufd.c | 36 ++++++++++++++++++++++++++++++++++++ backends/trace-events | 1 + include/system/iommufd.h | 4 ++++ 3 files changed, 41 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index b73f75cd0b..8bcdb60fe7 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -311,6 +311,42 @@ bool iommufd_backend_get_device_info(IOMMUFDBackend *be, uint32_t devid, return true; } +bool iommufd_backend_invalidate_cache(IOMMUFDBackend *be, uint32_t id, + uint32_t data_type, uint32_t entry_len, + uint32_t *entry_num, void *data, + Error **errp) +{ + int ret, fd = be->fd; + uint32_t total_entries = *entry_num; + struct iommu_hwpt_invalidate cache = { + .size = sizeof(cache), + .hwpt_id = id, + .data_type = data_type, + .entry_len = entry_len, + .entry_num = total_entries, + .data_uptr = (uintptr_t)data, + }; + + ret = ioctl(fd, IOMMU_HWPT_INVALIDATE, &cache); + trace_iommufd_backend_invalidate_cache(fd, id, data_type, entry_len, + total_entries, cache.entry_num, + (uintptr_t)data, ret ? errno : 0); + *entry_num = cache.entry_num; + + if (ret) { + error_setg_errno(errp, errno, "IOMMU_HWPT_INVALIDATE failed:" + " total %d entries, processed %d entries", + total_entries, cache.entry_num); + } else if (total_entries != cache.entry_num) { + error_setg(errp, "IOMMU_HWPT_INVALIDATE succeed but with unprocessed" + " entries: total %d entries, processed %d entries." + " Kernel BUG?!", total_entries, cache.entry_num); + return false; + } + + return !ret; +} + static int hiod_iommufd_get_cap(HostIOMMUDevice *hiod, int cap, Error **errp) { HostIOMMUDeviceCaps *caps = &hiod->caps; diff --git a/backends/trace-events b/backends/trace-events index 40811a3162..7278214ea5 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -18,3 +18,4 @@ iommufd_backend_alloc_hwpt(int iommufd, uint32_t dev_id, uint32_t pt_id, uint32_ iommufd_backend_free_id(int iommufd, uint32_t id, int ret) " iommufd=%d id=%d (%d)" iommufd_backend_set_dirty(int iommufd, uint32_t hwpt_id, bool start, int ret) " iommufd=%d hwpt=%u enable=%d (%d)" iommufd_backend_get_dirty_bitmap(int iommufd, uint32_t hwpt_id, uint64_t iova, uint64_t size, uint64_t page_size, int ret) " iommufd=%d hwpt=%u iova=0x%"PRIx64" size=0x%"PRIx64" page_size=0x%"PRIx64" (%d)" +iommufd_backend_invalidate_cache(int iommufd, uint32_t id, uint32_t data_type, uint32_t entry_len, uint32_t entry_num, uint32_t done_num, uint64_t data_ptr, int ret) " iommufd=%d id=%u data_type=%u entry_len=%u entry_num=%u done_num=%u data_ptr=0x%"PRIx64" (%d)" diff --git a/include/system/iommufd.h b/include/system/iommufd.h index cbab75bfbf..83ab8e1e4c 100644 --- a/include/system/iommufd.h +++ b/include/system/iommufd.h @@ -61,6 +61,10 @@ bool iommufd_backend_get_dirty_bitmap(IOMMUFDBackend *be, uint32_t hwpt_id, uint64_t iova, ram_addr_t size, uint64_t page_size, uint64_t *data, Error **errp); +bool iommufd_backend_invalidate_cache(IOMMUFDBackend *be, uint32_t id, + uint32_t data_type, uint32_t entry_len, + uint32_t *entry_num, void *data, + Error **errp); #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd" #endif From 98962d62984440a3905e83492c33a854861def0f Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 4 Jun 2025 14:21:13 +0800 Subject: [PATCH 1419/2760] vfio/iommufd: Add properties and handlers to TYPE_HOST_IOMMU_DEVICE_IOMMUFD MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enhance HostIOMMUDeviceIOMMUFD object with 3 new members, specific to the iommufd BE + 2 new class functions. IOMMUFD BE includes IOMMUFD handle, devid and hwpt_id. IOMMUFD handle and devid are used to allocate/free ioas and hwpt. hwpt_id is used to re-attach IOMMUFD backed device to its default VFIO sub-system created hwpt, i.e., when vIOMMU is disabled by guest. These properties are initialized in hiod::realize() after attachment. 2 new class functions are [at|de]tach_hwpt(). They are used to attach/detach hwpt. VFIO and VDPA can have different implementions, so implementation will be in sub-class instead of HostIOMMUDeviceIOMMUFD, e.g., in HostIOMMUDeviceIOMMUFDVFIO. Add two wrappers host_iommu_device_iommufd_[at|de]tach_hwpt to wrap the two functions. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Reviewed-by: Eric Auger Link: https://lore.kernel.org/qemu-devel/20250604062115.4004200-3-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- backends/iommufd.c | 22 ++++++++++++++++++ hw/vfio/iommufd.c | 6 +++++ include/system/iommufd.h | 50 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index 8bcdb60fe7..c2c47abf7e 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -347,6 +347,26 @@ bool iommufd_backend_invalidate_cache(IOMMUFDBackend *be, uint32_t id, return !ret; } +bool host_iommu_device_iommufd_attach_hwpt(HostIOMMUDeviceIOMMUFD *idev, + uint32_t hwpt_id, Error **errp) +{ + HostIOMMUDeviceIOMMUFDClass *idevc = + HOST_IOMMU_DEVICE_IOMMUFD_GET_CLASS(idev); + + g_assert(idevc->attach_hwpt); + return idevc->attach_hwpt(idev, hwpt_id, errp); +} + +bool host_iommu_device_iommufd_detach_hwpt(HostIOMMUDeviceIOMMUFD *idev, + Error **errp) +{ + HostIOMMUDeviceIOMMUFDClass *idevc = + HOST_IOMMU_DEVICE_IOMMUFD_GET_CLASS(idev); + + g_assert(idevc->detach_hwpt); + return idevc->detach_hwpt(idev, errp); +} + static int hiod_iommufd_get_cap(HostIOMMUDevice *hiod, int cap, Error **errp) { HostIOMMUDeviceCaps *caps = &hiod->caps; @@ -385,6 +405,8 @@ static const TypeInfo types[] = { }, { .name = TYPE_HOST_IOMMU_DEVICE_IOMMUFD, .parent = TYPE_HOST_IOMMU_DEVICE, + .instance_size = sizeof(HostIOMMUDeviceIOMMUFD), + .class_size = sizeof(HostIOMMUDeviceIOMMUFDClass), .class_init = hiod_iommufd_class_init, .abstract = true, } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 46a3b36301..625b1747a2 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -819,6 +819,7 @@ static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, Error **errp) { VFIODevice *vdev = opaque; + HostIOMMUDeviceIOMMUFD *idev; HostIOMMUDeviceCaps *caps = &hiod->caps; enum iommu_hw_info_type type; union { @@ -838,6 +839,11 @@ static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, caps->type = type; caps->hw_caps = hw_caps; + idev = HOST_IOMMU_DEVICE_IOMMUFD(hiod); + idev->iommufd = vdev->iommufd; + idev->devid = vdev->devid; + idev->hwpt_id = vdev->hwpt->hwpt_id; + return true; } diff --git a/include/system/iommufd.h b/include/system/iommufd.h index 83ab8e1e4c..283861b924 100644 --- a/include/system/iommufd.h +++ b/include/system/iommufd.h @@ -67,4 +67,54 @@ bool iommufd_backend_invalidate_cache(IOMMUFDBackend *be, uint32_t id, Error **errp); #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd" +OBJECT_DECLARE_TYPE(HostIOMMUDeviceIOMMUFD, HostIOMMUDeviceIOMMUFDClass, + HOST_IOMMU_DEVICE_IOMMUFD) + +/* Overload of the host IOMMU device for the iommufd backend */ +struct HostIOMMUDeviceIOMMUFD { + HostIOMMUDevice parent_obj; + + IOMMUFDBackend *iommufd; + uint32_t devid; + uint32_t hwpt_id; +}; + +struct HostIOMMUDeviceIOMMUFDClass { + HostIOMMUDeviceClass parent_class; + + /** + * @attach_hwpt: attach host IOMMU device to IOMMUFD hardware page table. + * VFIO and VDPA device can have different implementation. + * + * Mandatory callback. + * + * @idev: host IOMMU device backed by IOMMUFD backend. + * + * @hwpt_id: ID of IOMMUFD hardware page table. + * + * @errp: pass an Error out when attachment fails. + * + * Returns: true on success, false on failure. + */ + bool (*attach_hwpt)(HostIOMMUDeviceIOMMUFD *idev, uint32_t hwpt_id, + Error **errp); + /** + * @detach_hwpt: detach host IOMMU device from IOMMUFD hardware page table. + * VFIO and VDPA device can have different implementation. + * + * Mandatory callback. + * + * @idev: host IOMMU device backed by IOMMUFD backend. + * + * @errp: pass an Error out when attachment fails. + * + * Returns: true on success, false on failure. + */ + bool (*detach_hwpt)(HostIOMMUDeviceIOMMUFD *idev, Error **errp); +}; + +bool host_iommu_device_iommufd_attach_hwpt(HostIOMMUDeviceIOMMUFD *idev, + uint32_t hwpt_id, Error **errp); +bool host_iommu_device_iommufd_detach_hwpt(HostIOMMUDeviceIOMMUFD *idev, + Error **errp); #endif From e50a3ead976d23bd1b4fdf50baea8df2303128a5 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 4 Jun 2025 14:21:14 +0800 Subject: [PATCH 1420/2760] vfio/iommufd: Implement [at|de]tach_hwpt handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement [at|de]tach_hwpt handlers in VFIO subsystem. vIOMMU utilizes them to attach to or detach from hwpt on host side. Signed-off-by: Yi Liu Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Reviewed-by: Eric Auger Reviewed-by: Nicolin Chen Link: https://lore.kernel.org/qemu-devel/20250604062115.4004200-4-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 625b1747a2..5903302837 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -815,6 +815,24 @@ static void vfio_iommu_iommufd_class_init(ObjectClass *klass, const void *data) vioc->query_dirty_bitmap = iommufd_query_dirty_bitmap; }; +static bool +host_iommu_device_iommufd_vfio_attach_hwpt(HostIOMMUDeviceIOMMUFD *idev, + uint32_t hwpt_id, Error **errp) +{ + VFIODevice *vbasedev = HOST_IOMMU_DEVICE(idev)->agent; + + return !iommufd_cdev_attach_ioas_hwpt(vbasedev, hwpt_id, errp); +} + +static bool +host_iommu_device_iommufd_vfio_detach_hwpt(HostIOMMUDeviceIOMMUFD *idev, + Error **errp) +{ + VFIODevice *vbasedev = HOST_IOMMU_DEVICE(idev)->agent; + + return iommufd_cdev_detach_ioas_hwpt(vbasedev, errp); +} + static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, Error **errp) { @@ -869,10 +887,14 @@ hiod_iommufd_vfio_get_page_size_mask(HostIOMMUDevice *hiod) static void hiod_iommufd_vfio_class_init(ObjectClass *oc, const void *data) { HostIOMMUDeviceClass *hiodc = HOST_IOMMU_DEVICE_CLASS(oc); + HostIOMMUDeviceIOMMUFDClass *idevc = HOST_IOMMU_DEVICE_IOMMUFD_CLASS(oc); hiodc->realize = hiod_iommufd_vfio_realize; hiodc->get_iova_ranges = hiod_iommufd_vfio_get_iova_ranges; hiodc->get_page_size_mask = hiod_iommufd_vfio_get_page_size_mask; + + idevc->attach_hwpt = host_iommu_device_iommufd_vfio_attach_hwpt; + idevc->detach_hwpt = host_iommu_device_iommufd_vfio_detach_hwpt; }; static const TypeInfo types[] = { From 1ab3d93fd24b9ca0f415bb7ef38c4a4e2e9e62ef Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 4 Jun 2025 14:21:15 +0800 Subject: [PATCH 1421/2760] vfio/iommufd: Save vendor specific device info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some device information returned by ioctl(IOMMU_GET_HW_INFO) are vendor specific. Save them as raw data in a union supporting different vendors, then vendor IOMMU can query the raw data with its fixed format for capability directly. Because IOMMU_GET_HW_INFO is only supported in linux, so declare those capability related structures with CONFIG_LINUX. Suggested-by: Eric Auger Suggested-by: Nicolin Chen Signed-off-by: Zhenzhong Duan Reviewed-by: Nicolin Chen Reviewed-by: Eric Auger Link: https://lore.kernel.org/qemu-devel/20250604062115.4004200-5-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 8 +++----- include/system/host_iommu_device.h | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 5903302837..c4bbf36241 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -839,16 +839,14 @@ static bool hiod_iommufd_vfio_realize(HostIOMMUDevice *hiod, void *opaque, VFIODevice *vdev = opaque; HostIOMMUDeviceIOMMUFD *idev; HostIOMMUDeviceCaps *caps = &hiod->caps; + VendorCaps *vendor_caps = &caps->vendor_caps; enum iommu_hw_info_type type; - union { - struct iommu_hw_info_vtd vtd; - } data; uint64_t hw_caps; hiod->agent = opaque; - if (!iommufd_backend_get_device_info(vdev->iommufd, vdev->devid, - &type, &data, sizeof(data), + if (!iommufd_backend_get_device_info(vdev->iommufd, vdev->devid, &type, + vendor_caps, sizeof(*vendor_caps), &hw_caps, errp)) { return false; } diff --git a/include/system/host_iommu_device.h b/include/system/host_iommu_device.h index 809cced4ba..ab849a4a82 100644 --- a/include/system/host_iommu_device.h +++ b/include/system/host_iommu_device.h @@ -14,6 +14,13 @@ #include "qom/object.h" #include "qapi/error.h" +#ifdef CONFIG_LINUX +#include "linux/iommufd.h" + +typedef union VendorCaps { + struct iommu_hw_info_vtd vtd; + struct iommu_hw_info_arm_smmuv3 smmuv3; +} VendorCaps; /** * struct HostIOMMUDeviceCaps - Define host IOMMU device capabilities. @@ -22,11 +29,17 @@ * * @hw_caps: host platform IOMMU capabilities (e.g. on IOMMUFD this represents * the @out_capabilities value returned from IOMMU_GET_HW_INFO ioctl) + * + * @vendor_caps: host platform IOMMU vendor specific capabilities (e.g. on + * IOMMUFD this represents a user-space buffer filled by kernel + * with host IOMMU @type specific hardware information data) */ typedef struct HostIOMMUDeviceCaps { uint32_t type; uint64_t hw_caps; + VendorCaps vendor_caps; } HostIOMMUDeviceCaps; +#endif #define TYPE_HOST_IOMMU_DEVICE "host-iommu-device" OBJECT_DECLARE_TYPE(HostIOMMUDevice, HostIOMMUDeviceClass, HOST_IOMMU_DEVICE) @@ -38,7 +51,9 @@ struct HostIOMMUDevice { void *agent; /* pointer to agent device, ie. VFIO or VDPA device */ PCIBus *aliased_bus; int aliased_devfn; +#ifdef CONFIG_LINUX HostIOMMUDeviceCaps caps; +#endif }; /** From 9942c711835f06805e82fbe9d1da98f6ff7b9311 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Thu, 29 May 2025 12:23:57 -0700 Subject: [PATCH 1422/2760] MAINTAINERS: Add reviewer for CPR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPR is integrated with live migration, and has the same maintainers. But, add a CPR section to add a reviewer. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1748546679-154091-2-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 16af37986a..b189701357 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3032,6 +3032,15 @@ F: include/qemu/co-shared-resource.h T: git https://gitlab.com/jsnow/qemu.git jobs T: git https://gitlab.com/vsementsov/qemu.git block +CheckPoint and Restart (CPR) +R: Steve Sistare +S: Supported +F: hw/vfio/cpr* +F: include/migration/cpr.h +F: migration/cpr* +F: tests/qtest/migration/cpr* +F: docs/devel/migration/CPR.rst + Compute Express Link M: Jonathan Cameron R: Fan Ni From 2372f8d94ae27b1b8a1c5704c668baabd48d1c99 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Thu, 29 May 2025 12:24:03 -0700 Subject: [PATCH 1423/2760] vfio: vfio_find_ram_discard_listener MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define vfio_find_ram_discard_listener as a subroutine so additional calls to it may be added in a subsequent patch. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1748546679-154091-8-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/listener.c | 35 +++++++++++++++++---------- include/hw/vfio/vfio-container-base.h | 3 +++ 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index 7495866123..203ed0314e 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -456,6 +456,26 @@ static void vfio_device_error_append(VFIODevice *vbasedev, Error **errp) } } +VFIORamDiscardListener *vfio_find_ram_discard_listener( + VFIOContainerBase *bcontainer, MemoryRegionSection *section) +{ + VFIORamDiscardListener *vrdl = NULL; + + QLIST_FOREACH(vrdl, &bcontainer->vrdl_list, next) { + if (vrdl->mr == section->mr && + vrdl->offset_within_address_space == + section->offset_within_address_space) { + break; + } + } + + if (!vrdl) { + hw_error("vfio: Trying to sync missing RAM discard listener"); + /* does not return */ + } + return vrdl; +} + static void vfio_listener_region_add(MemoryListener *listener, MemoryRegionSection *section) { @@ -1086,19 +1106,8 @@ vfio_sync_ram_discard_listener_dirty_bitmap(VFIOContainerBase *bcontainer, MemoryRegionSection *section) { RamDiscardManager *rdm = memory_region_get_ram_discard_manager(section->mr); - VFIORamDiscardListener *vrdl = NULL; - - QLIST_FOREACH(vrdl, &bcontainer->vrdl_list, next) { - if (vrdl->mr == section->mr && - vrdl->offset_within_address_space == - section->offset_within_address_space) { - break; - } - } - - if (!vrdl) { - hw_error("vfio: Trying to sync missing RAM discard listener"); - } + VFIORamDiscardListener *vrdl = + vfio_find_ram_discard_listener(bcontainer, section); /* * We only want/can synchronize the bitmap for actually mapped parts - diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 3feb773e5f..9d37f86115 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -253,4 +253,7 @@ struct VFIOIOMMUClass { void (*release)(VFIOContainerBase *bcontainer); }; +VFIORamDiscardListener *vfio_find_ram_discard_listener( + VFIOContainerBase *bcontainer, MemoryRegionSection *section); + #endif /* HW_VFIO_VFIO_CONTAINER_BASE_H */ From 3ed34463a2d8ab8aabfa1d612f12b56600c87983 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Thu, 29 May 2025 12:24:04 -0700 Subject: [PATCH 1424/2760] vfio: move vfio-cpr.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move vfio-cpr.h to include/hw/vfio, because it will need to be included by other files there. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1748546679-154091-9-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + hw/vfio/container.c | 2 +- hw/vfio/cpr.c | 2 +- hw/vfio/iommufd.c | 2 +- hw/vfio/vfio-cpr.h | 15 --------------- include/hw/vfio/vfio-cpr.h | 18 ++++++++++++++++++ 6 files changed, 22 insertions(+), 18 deletions(-) delete mode 100644 hw/vfio/vfio-cpr.h create mode 100644 include/hw/vfio/vfio-cpr.h diff --git a/MAINTAINERS b/MAINTAINERS index b189701357..aa6763077e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3036,6 +3036,7 @@ CheckPoint and Restart (CPR) R: Steve Sistare S: Supported F: hw/vfio/cpr* +F: include/hw/vfio/vfio-cpr.h F: include/migration/cpr.h F: migration/cpr* F: tests/qtest/migration/cpr* diff --git a/hw/vfio/container.c b/hw/vfio/container.c index a8c76eb481..0f948d0247 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -33,8 +33,8 @@ #include "qapi/error.h" #include "pci.h" #include "hw/vfio/vfio-container.h" +#include "hw/vfio/vfio-cpr.h" #include "vfio-helpers.h" -#include "vfio-cpr.h" #include "vfio-listener.h" #define TYPE_HOST_IOMMU_DEVICE_LEGACY_VFIO TYPE_HOST_IOMMU_DEVICE "-legacy-vfio" diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index 3214184f97..0210e76a10 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -8,9 +8,9 @@ #include "qemu/osdep.h" #include "hw/vfio/vfio-device.h" #include "migration/misc.h" +#include "hw/vfio/vfio-cpr.h" #include "qapi/error.h" #include "system/runstate.h" -#include "vfio-cpr.h" static int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier, MigrationEvent *e, Error **errp) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index c4bbf36241..d3efef71af 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -21,13 +21,13 @@ #include "qapi/error.h" #include "system/iommufd.h" #include "hw/qdev-core.h" +#include "hw/vfio/vfio-cpr.h" #include "system/reset.h" #include "qemu/cutils.h" #include "qemu/chardev_open.h" #include "pci.h" #include "vfio-iommufd.h" #include "vfio-helpers.h" -#include "vfio-cpr.h" #include "vfio-listener.h" #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD_VFIO \ diff --git a/hw/vfio/vfio-cpr.h b/hw/vfio/vfio-cpr.h deleted file mode 100644 index 134b83a624..0000000000 --- a/hw/vfio/vfio-cpr.h +++ /dev/null @@ -1,15 +0,0 @@ -/* - * VFIO CPR - * - * Copyright (c) 2025 Oracle and/or its affiliates. - * - * SPDX-License-Identifier: GPL-2.0-or-later - */ - -#ifndef HW_VFIO_CPR_H -#define HW_VFIO_CPR_H - -bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp); -void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer); - -#endif /* HW_VFIO_CPR_H */ diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h new file mode 100644 index 0000000000..750ea5bcea --- /dev/null +++ b/include/hw/vfio/vfio-cpr.h @@ -0,0 +1,18 @@ +/* + * VFIO CPR + * + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_VFIO_CPR_H +#define HW_VFIO_VFIO_CPR_H + +struct VFIOContainerBase; + +bool vfio_cpr_register_container(struct VFIOContainerBase *bcontainer, + Error **errp); +void vfio_cpr_unregister_container(struct VFIOContainerBase *bcontainer); + +#endif /* HW_VFIO_VFIO_CPR_H */ From dc955052b400572dcfed896532d2b0dcf9d111f5 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 4 Jun 2025 16:03:49 -0400 Subject: [PATCH 1425/2760] qapi: Add some pylint ignores This restores the linting baseline in QAPI. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Message-id: 20250604200354.459501-2-jsnow@redhat.com --- scripts/qapi/backend.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/qapi/backend.py b/scripts/qapi/backend.py index 14e60aa67a..49ae6ecdd3 100644 --- a/scripts/qapi/backend.py +++ b/scripts/qapi/backend.py @@ -13,6 +13,7 @@ class QAPIBackend(ABC): + # pylint: disable=too-few-public-methods @abstractmethod def generate(self, @@ -36,6 +37,7 @@ def generate(self, class QAPICBackend(QAPIBackend): + # pylint: disable=too-few-public-methods def generate(self, schema: QAPISchema, From a738817c1ddb1730cf144e444d367b5d37e4a4dd Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 4 Jun 2025 16:03:50 -0400 Subject: [PATCH 1426/2760] docs/qapidoc: linting fixes This restores the linting baseline in qapidoc. The order of some imports change slightly here due to configuring isort a little better: previously, isort was having difficulty understanding that "compat" and "qapidoc_legacy" were local modules because docs/sphinx "isn't a python package". Configuring this manually, isort chooses a different import ordering, which _is_ intentional here. Also: extra ignores are added for pylint. The most recent versions of pylint don't require these ignores, but the oldest versions we support do, so in the extra ignores go. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Message-id: 20250604200354.459501-3-jsnow@redhat.com --- docs/sphinx/qapi_domain.py | 25 ++++++++++++++----------- docs/sphinx/qapidoc.py | 5 +++-- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index c94af5719c..ebc46a72c6 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -20,16 +20,6 @@ from docutils import nodes from docutils.parsers.rst import directives - -from compat import ( - CompatField, - CompatGroupedField, - CompatTypedField, - KeywordNode, - ParserFix, - Signature, - SpaceNode, -) from sphinx import addnodes from sphinx.directives import ObjectDescription from sphinx.domains import ( @@ -44,6 +34,16 @@ from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import make_id, make_refnode +from compat import ( + CompatField, + CompatGroupedField, + CompatTypedField, + KeywordNode, + ParserFix, + Signature, + SpaceNode, +) + if TYPE_CHECKING: from typing import ( @@ -56,7 +56,6 @@ ) from docutils.nodes import Element, Node - from sphinx.addnodes import desc_signature, pending_xref from sphinx.application import Sphinx from sphinx.builders import Builder @@ -168,6 +167,8 @@ class QAPIDescription(ParserFix): """ def handle_signature(self, sig: str, signode: desc_signature) -> Signature: + # pylint: disable=unused-argument + # Do nothing. The return value here is the "name" of the entity # being documented; for QAPI, this is the same as the # "signature", which is just a name. @@ -210,6 +211,8 @@ def _get_fqn(self, name: Signature) -> str: def add_target_and_index( self, name: Signature, sig: str, signode: desc_signature ) -> None: + # pylint: disable=unused-argument + # name is the return value of handle_signature. # sig is the original, raw text argument to handle_signature. # For QAPI, these are identical, currently. diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 661b2c4ed0..8011ac9efa 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -27,6 +27,7 @@ from __future__ import annotations + __version__ = "2.0" from contextlib import contextmanager @@ -56,8 +57,6 @@ QAPISchemaVisitor, ) from qapi.source import QAPISourceInfo - -from qapidoc_legacy import QAPISchemaGenRSTVisitor # type: ignore from sphinx import addnodes from sphinx.directives.code import CodeBlock from sphinx.errors import ExtensionError @@ -65,6 +64,8 @@ from sphinx.util.docutils import SphinxDirective, switch_source_input from sphinx.util.nodes import nested_parse_with_titles +from qapidoc_legacy import QAPISchemaGenRSTVisitor # type: ignore + if TYPE_CHECKING: from typing import ( From 4b77e5d7b8b6377f7433de886b98bf64a5fcc663 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 4 Jun 2025 16:03:51 -0400 Subject: [PATCH 1427/2760] python: update missing dependencies from minreqs We pin all dependencies for the "check-minreqs" test because pip lacks a dependency resolver that installs "the oldest possible package that meets dependency criteria". So, in order to test our stated minimum requirements, we pin all of our dependencies (and their dependencies, transitively) at the oldest possible versions that still work and pass tests; proving that our minimum requirements are correct. (It also ensures no new features accidentally sneak in from developers on newer platforms.) A few transitive dependencies were omitted from the pinned dependency file by accident; as a result, pip's dependency solver can pull in newer dependencies, which we don't want. This patch corrects the previous oversight and pins the missing dependencies. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Message-id: 20250604200354.459501-4-jsnow@redhat.com --- python/tests/minreqs.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/python/tests/minreqs.txt b/python/tests/minreqs.txt index 6445407ba8..d3d53e0da8 100644 --- a/python/tests/minreqs.txt +++ b/python/tests/minreqs.txt @@ -38,10 +38,14 @@ pyflakes==2.5.0 # Transitive mypy dependencies mypy-extensions==1.0.0 +tomli==1.1.0 typing-extensions==4.7.1 # Transitive pylint dependencies astroid==2.15.4 +dill==0.2 lazy-object-proxy==1.4.0 +platformdirs==2.2.0 toml==0.10.0 +tomlkit==0.10.1 wrapt==1.14.0 From 65aa0a1780d59ea2b07da6e3626b98eca8b163d1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 4 Jun 2025 16:03:52 -0400 Subject: [PATCH 1428/2760] python: add qapi static analysis tests Update the python tests to also check QAPI and the QAPI Sphinx extensions. The docs/sphinx/qapidoc_legacy.py file is not included in these checks, as it is destined for removal soon. mypy is also not called on the QAPI Sphinx extensions, owing to difficulties supporting Sphinx 3.x - 8.x while maintaining static type checking support. mypy *is* called on all of the QAPI tools themselves, though. flake8, isort and mypy use the tool configuration from the existing python directory (in setup.cfg). pylint continues to use the special configuration located in scripts/qapi/ - that configuration is more permissive. If we wish to unify the two configurations, that's a separate series and a discussion for a later date. The list of pylint ignores is also updated, owing again to the wide window of pylint version support: newer versions require pragmas to occasionally silence the "too many positional arguments" warning, but older versions do not have such a warning category and will instead yelp about an unrecognized option. Silence that warning, too. As a result of this patch, one would be able to run any of the following tests locally from the qemu.git/python directory and have it cover the QAPI tooling as well. All of the following options run the python tests, static analysis tests, and linter checks; but with different combinations of dependencies and interpreters. - "make check-minreqs" Run tests specifically under our oldest supported Python and our oldest supported dependencies. This is the test that runs on GitLab as "check-python-minreqs". This helps ensure we do not regress support on older platforms accidentally. - "make check-tox" Runs the tests under the newest supported dependencies, but under each supported version of Python in turn. At time of writing, this is Python 3.8 to 3.13 inclusive. This test helps catch bleeding-edge problems before they become problems for developer workstations. This is the GitLab test "check-python-tox" and is an optionally run, may-fail test due to the unpredictable nature of new dependencies being released into the ecosystem that may cause regressions. - "make check-dev" Runs the tests under the newest supported dependencies using whatever version of Python the user happens to have installed. This is a quick convenience check that does not map to any particular GitLab test. (Note! check-dev may be busted on Fedora 41 and bleeding edge versions of setuptools. That's unrelated to this patch and I'll address it separately and soon. Thank you for your patience, --mgmt) Finally, finally, finally: this means that QAPI tooling will be linted and type-checked from the GitLab pipelines. Signed-off-by: John Snow Reviewed-by: Markus Armbruster Message-id: 20250604200354.459501-5-jsnow@redhat.com [Edited license choice per review --js] Signed-off-by: John Snow --- python/setup.cfg | 1 + python/tests/minreqs.txt | 27 +++++++++++++++++++++++++++ python/tests/qapi-flake8.sh | 6 ++++++ python/tests/qapi-isort.sh | 8 ++++++++ python/tests/qapi-mypy.sh | 4 ++++ python/tests/qapi-pylint.sh | 8 ++++++++ scripts/qapi/pylintrc | 1 + 7 files changed, 55 insertions(+) create mode 100755 python/tests/qapi-flake8.sh create mode 100755 python/tests/qapi-isort.sh create mode 100755 python/tests/qapi-mypy.sh create mode 100755 python/tests/qapi-pylint.sh diff --git a/python/setup.cfg b/python/setup.cfg index c48dff280a..d21304cadd 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -46,6 +46,7 @@ devel = urwid >= 2.1.2 urwid-readline >= 0.13 Pygments >= 2.9.0 + sphinx >= 3.4.3 # Provides qom-fuse functionality fuse = diff --git a/python/tests/minreqs.txt b/python/tests/minreqs.txt index d3d53e0da8..cd2e2a81c3 100644 --- a/python/tests/minreqs.txt +++ b/python/tests/minreqs.txt @@ -11,6 +11,15 @@ # When adding new dependencies, pin the very oldest non-yanked version # on PyPI that allows the test suite to pass. +# For some reason, the presence of packaging==14.0 below requires us to +# also pin setuptools to version 70 or below. Otherwise, the +# installation of the QEMU package itself fails, failing to find +# setuptools. +setuptools<=70 + +# Dependencies for qapidoc/qapi_domain et al +sphinx==3.4.3 + # Dependencies for the TUI addon (Required for successful linting) urwid==2.1.2 urwid-readline==0.13 @@ -49,3 +58,21 @@ platformdirs==2.2.0 toml==0.10.0 tomlkit==0.10.1 wrapt==1.14.0 + +# Transitive sphinx dependencies +Jinja2==2.7 +MarkupSafe==1.1.0 +alabaster==0.7.1 +babel==1.3 +docutils==0.12 +imagesize==0.5.0 +packaging==14.0 +pytz==2011b0 +requests==2.5.0 +snowballstemmer==1.1 +sphinxcontrib-applehelp==1.0.0 +sphinxcontrib-devhelp==1.0.0 +sphinxcontrib-htmlhelp==1.0.0 +sphinxcontrib-jsmath==1.0.0 +sphinxcontrib-qthelp==1.0.0 +sphinxcontrib-serializinghtml==1.0.0 diff --git a/python/tests/qapi-flake8.sh b/python/tests/qapi-flake8.sh new file mode 100755 index 0000000000..c69f9ea2e0 --- /dev/null +++ b/python/tests/qapi-flake8.sh @@ -0,0 +1,6 @@ +#!/bin/sh -e +# SPDX-License-Identifier: GPL-2.0-or-later + +python3 -m flake8 ../scripts/qapi/ \ + ../docs/sphinx/qapidoc.py \ + ../docs/sphinx/qapi_domain.py diff --git a/python/tests/qapi-isort.sh b/python/tests/qapi-isort.sh new file mode 100755 index 0000000000..78dd947f68 --- /dev/null +++ b/python/tests/qapi-isort.sh @@ -0,0 +1,8 @@ +#!/bin/sh -e +# SPDX-License-Identifier: GPL-2.0-or-later + +python3 -m isort --sp . -c ../scripts/qapi/ +# Force isort to recognize "compat" as a local module and not third-party +python3 -m isort --sp . -c -p compat -p qapidoc_legacy \ + ../docs/sphinx/qapi_domain.py \ + ../docs/sphinx/qapidoc.py diff --git a/python/tests/qapi-mypy.sh b/python/tests/qapi-mypy.sh new file mode 100755 index 0000000000..363dbaf8c0 --- /dev/null +++ b/python/tests/qapi-mypy.sh @@ -0,0 +1,4 @@ +#!/bin/sh -e +# SPDX-License-Identifier: GPL-2.0-or-later + +python3 -m mypy ../scripts/qapi diff --git a/python/tests/qapi-pylint.sh b/python/tests/qapi-pylint.sh new file mode 100755 index 0000000000..8767d9d2a2 --- /dev/null +++ b/python/tests/qapi-pylint.sh @@ -0,0 +1,8 @@ +#!/bin/sh -e +# SPDX-License-Identifier: GPL-2.0-or-later + +SETUPTOOLS_USE_DISTUTILS=stdlib python3 -m pylint \ + --rcfile=../scripts/qapi/pylintrc \ + ../scripts/qapi/ \ + ../docs/sphinx/qapidoc.py \ + ../docs/sphinx/qapi_domain.py diff --git a/scripts/qapi/pylintrc b/scripts/qapi/pylintrc index d24eece741..e16283ada3 100644 --- a/scripts/qapi/pylintrc +++ b/scripts/qapi/pylintrc @@ -19,6 +19,7 @@ disable=consider-using-f-string, too-many-instance-attributes, too-many-positional-arguments, too-many-statements, + unknown-option-value, useless-option-value, [REPORTS] From 781e730556f3621a3f5f83e9b6afdc7d306f0094 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Wed, 4 Jun 2025 16:03:53 -0400 Subject: [PATCH 1429/2760] python: Drop redundant warn_unused_configs = True strict = True implies warn_unused_configs = True. Signed-off-by: Markus Armbruster Signed-off-by: John Snow Reviewed-by: Markus Armbruster Message-id: 20250604200354.459501-6-jsnow@redhat.com --- python/setup.cfg | 1 - 1 file changed, 1 deletion(-) diff --git a/python/setup.cfg b/python/setup.cfg index d21304cadd..d7f5dc7baf 100644 --- a/python/setup.cfg +++ b/python/setup.cfg @@ -79,7 +79,6 @@ exclude = __pycache__, [mypy] strict = True python_version = 3.9 -warn_unused_configs = True namespace_packages = True warn_unused_ignores = False From 678868eee3947fa25e13ccf3abb004c9c418e8a2 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 4 Jun 2025 16:03:54 -0400 Subject: [PATCH 1430/2760] qapi: delete un-needed python static analysis configs Since the previous commit, python/setup.cfg applies to scripts/qapi/ as well. Configuration files in scripts/qapi/ override python/setup.cfg. scripts/qapi/.flake8 and scripts/qapi/.isort.cfg actually match python/setup.cfg exactly, and can go. The differences between scripts/qapi/mypy.ini and python/setup.cfg are harmless: namespace_packages being set to True is a requirement for the PEP420 nested package structure of QEMU but not for scripts/qapi, but has no effect on type checking the QAPI code. warn_unused_ignores is used in python/ to be able to target a wide variety of mypy versions; some of which that have added new ignore categories that are not present in older versions. Ultimately, scripts/qapi/mypy.ini can be removed without any real change in behavior to how mypy enforces type safety there. The pylint config is being left in place because the settings differ enough from the python/ directory settings that we need a chit-chat on how to merge them O:-) Signed-off-by: John Snow Reviewed-by: Markus Armbruster Message-id: 20250604200354.459501-7-jsnow@redhat.com --- scripts/qapi/.flake8 | 3 --- scripts/qapi/.isort.cfg | 7 ------- scripts/qapi/mypy.ini | 4 ---- 3 files changed, 14 deletions(-) delete mode 100644 scripts/qapi/.flake8 delete mode 100644 scripts/qapi/.isort.cfg delete mode 100644 scripts/qapi/mypy.ini diff --git a/scripts/qapi/.flake8 b/scripts/qapi/.flake8 deleted file mode 100644 index a873ff6730..0000000000 --- a/scripts/qapi/.flake8 +++ /dev/null @@ -1,3 +0,0 @@ -[flake8] -# Prefer pylint's bare-except checks to flake8's -extend-ignore = E722 diff --git a/scripts/qapi/.isort.cfg b/scripts/qapi/.isort.cfg deleted file mode 100644 index 643caa1fbd..0000000000 --- a/scripts/qapi/.isort.cfg +++ /dev/null @@ -1,7 +0,0 @@ -[settings] -force_grid_wrap=4 -force_sort_within_sections=True -include_trailing_comma=True -line_length=72 -lines_after_imports=2 -multi_line_output=3 diff --git a/scripts/qapi/mypy.ini b/scripts/qapi/mypy.ini deleted file mode 100644 index c9dbcec2db..0000000000 --- a/scripts/qapi/mypy.ini +++ /dev/null @@ -1,4 +0,0 @@ -[mypy] -strict = True -disallow_untyped_calls = False -python_version = 3.9 From a95ad49bbfac2a5080c5761688465bdbb1969c24 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 26 May 2025 12:10:18 +0200 Subject: [PATCH 1431/2760] subprojects: add the anyhow crate This is a standard replacement for Box which is more efficient (it only occcupies one word) and provides a backtrace of the error. This could be plumbed into &error_abort in the future. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/meson.build | 2 ++ rust/qemu-api/meson.build | 2 +- scripts/archive-source.sh | 2 +- scripts/make-release | 2 +- subprojects/.gitignore | 1 + subprojects/anyhow-1-rs.wrap | 7 ++++ .../packagefiles/anyhow-1-rs/meson.build | 33 +++++++++++++++++++ 7 files changed, 46 insertions(+), 3 deletions(-) create mode 100644 subprojects/anyhow-1-rs.wrap create mode 100644 subprojects/packagefiles/anyhow-1-rs/meson.build diff --git a/rust/meson.build b/rust/meson.build index b1b3315be9..f752a06464 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -1,7 +1,9 @@ +subproject('anyhow-1-rs', required: true) subproject('bilge-0.2-rs', required: true) subproject('bilge-impl-0.2-rs', required: true) subproject('libc-0.2-rs', required: true) +anyhow_rs = dependency('anyhow-1-rs') bilge_rs = dependency('bilge-0.2-rs') bilge_impl_rs = dependency('bilge-impl-0.2-rs') libc_rs = dependency('libc-0.2-rs') diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index b532281e8c..da70720e4c 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -35,7 +35,7 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [libc_rs, qemu_api_macros, qemuutil_rs, + dependencies: [anyhow_rs, libc_rs, qemu_api_macros, qemuutil_rs, qom, hwcore, chardev, migration], ) diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index e461c1531e..816062fee9 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -27,7 +27,7 @@ sub_file="${sub_tdir}/submodule.tar" # in their checkout, because the build environment is completely # different to the host OS. subprojects="keycodemapdb libvfio-user berkeley-softfloat-3 - berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs + berkeley-testfloat-3 anyhow-1-rs arbitrary-int-1-rs bilge-0.2-rs bilge-impl-0.2-rs either-1-rs itertools-0.11-rs libc-0.2-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" diff --git a/scripts/make-release b/scripts/make-release index 8c3594a1a4..ea65bdcc0c 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -40,7 +40,7 @@ fi # Only include wraps that are invoked with subproject() SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3 - berkeley-testfloat-3 arbitrary-int-1-rs bilge-0.2-rs + berkeley-testfloat-3 anyhow-1-rs arbitrary-int-1-rs bilge-0.2-rs bilge-impl-0.2-rs either-1-rs itertools-0.11-rs libc-0.2-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" diff --git a/subprojects/.gitignore b/subprojects/.gitignore index d12d34618c..b9ae507b85 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -6,6 +6,7 @@ /keycodemapdb /libvfio-user /slirp +/anyhow-1.0.98 /arbitrary-int-1.2.7 /bilge-0.2.0 /bilge-impl-0.2.0 diff --git a/subprojects/anyhow-1-rs.wrap b/subprojects/anyhow-1-rs.wrap new file mode 100644 index 0000000000..a69a3645b4 --- /dev/null +++ b/subprojects/anyhow-1-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = anyhow-1.0.98 +source_url = https://crates.io/api/v1/crates/anyhow/1.0.98/download +source_filename = anyhow-1.0.98.tar.gz +source_hash = e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487 +#method = cargo +patch_directory = anyhow-1-rs diff --git a/subprojects/packagefiles/anyhow-1-rs/meson.build b/subprojects/packagefiles/anyhow-1-rs/meson.build new file mode 100644 index 0000000000..348bab98b9 --- /dev/null +++ b/subprojects/packagefiles/anyhow-1-rs/meson.build @@ -0,0 +1,33 @@ +project('anyhow-1-rs', 'rust', + meson_version: '>=1.5.0', + version: '1.0.98', + license: 'MIT OR Apache-2.0', + default_options: []) + +rustc = meson.get_compiler('rust') + +rust_args = ['--cap-lints', 'allow'] +rust_args += ['--cfg', 'feature="std"'] +if rustc.version().version_compare('<1.65.0') + error('rustc version ' + rustc.version() + ' is unsupported. Please upgrade to at least 1.65.0') +endif +rust_args += [ '--cfg', 'std_backtrace' ] # >= 1.65.0 +if rustc.version().version_compare('<1.81.0') + rust_args += [ '--cfg', 'anyhow_no_core_error' ] +endif + +_anyhow_rs = static_library( + 'anyhow', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2018', 'build.rust_std=2018'], + rust_abi: 'rust', + rust_args: rust_args, + dependencies: [], +) + +anyhow_dep = declare_dependency( + link_with: _anyhow_rs, +) + +meson.override_dependency('anyhow-1-rs', anyhow_dep) From bfe0f6b02a4d76bcdc05f50e03667447f6069445 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 23 May 2025 17:59:52 +0200 Subject: [PATCH 1432/2760] subprojects: add the foreign crate This is a cleaned up and separated version of the patches at https://lore.kernel.org/all/20240701145853.1394967-4-pbonzini@redhat.com/ https://lore.kernel.org/all/20240701145853.1394967-5-pbonzini@redhat.com/ Its first user will be the Error bindings; for example a QEMU Error ** can be converted to a Rust Option using unsafe { Option::::from_foreign(c_error) } Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/meson.build | 2 ++ rust/qemu-api/meson.build | 2 +- scripts/archive-source.sh | 3 ++- scripts/make-release | 3 ++- subprojects/.gitignore | 1 + subprojects/foreign-0.3-rs.wrap | 7 +++++ .../packagefiles/foreign-0.3-rs/meson.build | 26 +++++++++++++++++++ 7 files changed, 41 insertions(+), 3 deletions(-) create mode 100644 subprojects/foreign-0.3-rs.wrap create mode 100644 subprojects/packagefiles/foreign-0.3-rs/meson.build diff --git a/rust/meson.build b/rust/meson.build index f752a06464..99ae7956cd 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -1,11 +1,13 @@ subproject('anyhow-1-rs', required: true) subproject('bilge-0.2-rs', required: true) subproject('bilge-impl-0.2-rs', required: true) +subproject('foreign-0.3-rs', required: true) subproject('libc-0.2-rs', required: true) anyhow_rs = dependency('anyhow-1-rs') bilge_rs = dependency('bilge-0.2-rs') bilge_impl_rs = dependency('bilge-impl-0.2-rs') +foreign_rs = dependency('foreign-0.3-rs') libc_rs = dependency('libc-0.2-rs') subproject('proc-macro2-1-rs', required: true) diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index da70720e4c..2f0f3b2aae 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -35,7 +35,7 @@ _qemu_api_rs = static_library( override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', rust_args: _qemu_api_cfg, - dependencies: [anyhow_rs, libc_rs, qemu_api_macros, qemuutil_rs, + dependencies: [anyhow_rs, foreign_rs, libc_rs, qemu_api_macros, qemuutil_rs, qom, hwcore, chardev, migration], ) diff --git a/scripts/archive-source.sh b/scripts/archive-source.sh index 816062fee9..035828c532 100755 --- a/scripts/archive-source.sh +++ b/scripts/archive-source.sh @@ -28,7 +28,8 @@ sub_file="${sub_tdir}/submodule.tar" # different to the host OS. subprojects="keycodemapdb libvfio-user berkeley-softfloat-3 berkeley-testfloat-3 anyhow-1-rs arbitrary-int-1-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs itertools-0.11-rs libc-0.2-rs proc-macro2-1-rs + bilge-impl-0.2-rs either-1-rs foreign-0.3-rs itertools-0.11-rs + libc-0.2-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" sub_deinit="" diff --git a/scripts/make-release b/scripts/make-release index ea65bdcc0c..4509a9fabf 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -41,7 +41,8 @@ fi # Only include wraps that are invoked with subproject() SUBPROJECTS="libvfio-user keycodemapdb berkeley-softfloat-3 berkeley-testfloat-3 anyhow-1-rs arbitrary-int-1-rs bilge-0.2-rs - bilge-impl-0.2-rs either-1-rs itertools-0.11-rs libc-0.2-rs proc-macro2-1-rs + bilge-impl-0.2-rs either-1-rs foreign-0.3-rs itertools-0.11-rs + libc-0.2-rs proc-macro2-1-rs proc-macro-error-1-rs proc-macro-error-attr-1-rs quote-1-rs syn-2-rs unicode-ident-1-rs" diff --git a/subprojects/.gitignore b/subprojects/.gitignore index b9ae507b85..f4281934ce 100644 --- a/subprojects/.gitignore +++ b/subprojects/.gitignore @@ -11,6 +11,7 @@ /bilge-0.2.0 /bilge-impl-0.2.0 /either-1.12.0 +/foreign-0.3.1 /itertools-0.11.0 /libc-0.2.162 /proc-macro-error-1.0.4 diff --git a/subprojects/foreign-0.3-rs.wrap b/subprojects/foreign-0.3-rs.wrap new file mode 100644 index 0000000000..0d218ec2c2 --- /dev/null +++ b/subprojects/foreign-0.3-rs.wrap @@ -0,0 +1,7 @@ +[wrap-file] +directory = foreign-0.3.1 +source_url = https://crates.io/api/v1/crates/foreign/0.3.1/download +source_filename = foreign-0.3.1.tar.gz +source_hash = 17ca1b5be8c9d320daf386f1809c7acc0cb09accbae795c2001953fa50585846 +#method = cargo +patch_directory = foreign-0.3-rs diff --git a/subprojects/packagefiles/foreign-0.3-rs/meson.build b/subprojects/packagefiles/foreign-0.3-rs/meson.build new file mode 100644 index 0000000000..0901c02c52 --- /dev/null +++ b/subprojects/packagefiles/foreign-0.3-rs/meson.build @@ -0,0 +1,26 @@ +project('foreign-0.3-rs', 'rust', + meson_version: '>=1.5.0', + version: '0.2.0', + license: 'MIT OR Apache-2.0', + default_options: []) + +subproject('libc-0.2-rs', required: true) +libc_rs = dependency('libc-0.2-rs') + +_foreign_rs = static_library( + 'foreign', + files('src/lib.rs'), + gnu_symbol_visibility: 'hidden', + override_options: ['rust_std=2021', 'build.rust_std=2021'], + rust_abi: 'rust', + rust_args: [ + '--cap-lints', 'allow', + ], + dependencies: [libc_rs], +) + +foreign_dep = declare_dependency( + link_with: _foreign_rs, +) + +meson.override_dependency('foreign-0.3-rs', foreign_dep) From 8714d366e7e29d3ca8cebc8504e18c4cd7b5cf48 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 23 May 2025 19:41:40 +0200 Subject: [PATCH 1433/2760] util/error: expose Error definition to Rust code This is used to preserve the file and line in a roundtrip from C Error to Rust and back to C. Signed-off-by: Paolo Bonzini --- include/qapi/error-internal.h | 26 ++++++++++++++++++++++++++ rust/wrapper.h | 1 + util/error.c | 10 +--------- 3 files changed, 28 insertions(+), 9 deletions(-) create mode 100644 include/qapi/error-internal.h diff --git a/include/qapi/error-internal.h b/include/qapi/error-internal.h new file mode 100644 index 0000000000..d5c3904ade --- /dev/null +++ b/include/qapi/error-internal.h @@ -0,0 +1,26 @@ +/* + * QEMU Error Objects - struct definition + * + * Copyright IBM, Corp. 2011 + * Copyright (C) 2011-2015 Red Hat, Inc. + * + * Authors: + * Anthony Liguori + * Markus Armbruster , + * + * This work is licensed under the terms of the GNU LGPL, version 2. See + * the COPYING.LIB file in the top-level directory. + */ + +#ifndef QAPI_ERROR_INTERNAL_H + +struct Error +{ + char *msg; + ErrorClass err_class; + const char *src, *func; + int line; + GString *hint; +}; + +#endif diff --git a/rust/wrapper.h b/rust/wrapper.h index beddd9aab2..6060d3ba1a 100644 --- a/rust/wrapper.h +++ b/rust/wrapper.h @@ -60,6 +60,7 @@ typedef enum memory_order { #include "hw/qdev-properties-system.h" #include "hw/irq.h" #include "qapi/error.h" +#include "qapi/error-internal.h" #include "migration/vmstate.h" #include "chardev/char-serial.h" #include "exec/memattrs.h" diff --git a/util/error.c b/util/error.c index 673011b89e..e5bcb7c022 100644 --- a/util/error.c +++ b/util/error.c @@ -15,15 +15,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/error-report.h" - -struct Error -{ - char *msg; - ErrorClass err_class; - const char *src, *func; - int line; - GString *hint; -}; +#include "qapi/error-internal.h" Error *error_abort; Error *error_fatal; From 230a4894f45eac5fbd6bea8dc0dd54f84cf6c0fa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 26 May 2025 09:25:50 +0200 Subject: [PATCH 1434/2760] util/error: allow non-NUL-terminated err->src Rust makes the current file available as a statically-allocated string, but without a NUL terminator. Allow this by storing an optional maximum length in the Error. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- include/qapi/error-internal.h | 9 ++++++++- util/error.c | 5 +++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/qapi/error-internal.h b/include/qapi/error-internal.h index d5c3904ade..1ec3ceb40f 100644 --- a/include/qapi/error-internal.h +++ b/include/qapi/error-internal.h @@ -18,7 +18,14 @@ struct Error { char *msg; ErrorClass err_class; - const char *src, *func; + const char *func; + + /* + * src might be NUL-terminated or not. If it is, src_len is negative. + * If it is not, src_len is the length. + */ + const char *src; + int src_len; int line; GString *hint; }; diff --git a/util/error.c b/util/error.c index e5bcb7c022..3449ecc0b9 100644 --- a/util/error.c +++ b/util/error.c @@ -24,8 +24,8 @@ Error *error_warn; static void error_handle(Error **errp, Error *err) { if (errp == &error_abort) { - fprintf(stderr, "Unexpected error in %s() at %s:%d:\n", - err->func, err->src, err->line); + fprintf(stderr, "Unexpected error in %s() at %.*s:%d:\n", + err->func, err->src_len, err->src, err->line); error_report("%s", error_get_pretty(err)); if (err->hint) { error_printf("%s", err->hint->str); @@ -67,6 +67,7 @@ static void error_setv(Error **errp, g_free(msg); } err->err_class = err_class; + err->src_len = -1; err->src = src; err->line = line; err->func = func; From e8fb9c91a3ea437bdddd1819e180f36112e41ae1 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 28 May 2025 14:27:47 +0200 Subject: [PATCH 1435/2760] util/error: make func optional The function name is not available in Rust, so make it optional. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- include/qapi/error-internal.h | 2 ++ util/error.c | 9 +++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/qapi/error-internal.h b/include/qapi/error-internal.h index 1ec3ceb40f..ff18a2086d 100644 --- a/include/qapi/error-internal.h +++ b/include/qapi/error-internal.h @@ -18,6 +18,8 @@ struct Error { char *msg; ErrorClass err_class; + + /* Used for error_abort only, may be NULL. */ const char *func; /* diff --git a/util/error.c b/util/error.c index 3449ecc0b9..daea2142f3 100644 --- a/util/error.c +++ b/util/error.c @@ -24,8 +24,13 @@ Error *error_warn; static void error_handle(Error **errp, Error *err) { if (errp == &error_abort) { - fprintf(stderr, "Unexpected error in %s() at %.*s:%d:\n", - err->func, err->src_len, err->src, err->line); + if (err->func) { + fprintf(stderr, "Unexpected error in %s() at %.*s:%d:\n", + err->func, err->src_len, err->src, err->line); + } else { + fprintf(stderr, "Unexpected error at %.*s:%d:\n", + err->src_len, err->src, err->line); + } error_report("%s", error_get_pretty(err)); if (err->hint) { error_printf("%s", err->hint->str); From b4ff3cf34fa09b28a480870b98377bac183833c8 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 3 Jun 2025 17:45:39 +0200 Subject: [PATCH 1436/2760] rust: qemu-api: add bindings to Error Provide an implementation of std::error::Error that bridges the Rust anyhow::Error and std::panic::Location types with QEMU's Error*. It also has several utility methods, analogous to error_propagate(), that convert a Result into a return value + Error** pair. One important difference is that these propagation methods *panic* if *errp is NULL, unlike error_propagate() which eats subsequent errors[1]. The reason for this is that in C you have an error_set*() call at the site where the error is created, and calls to error_propagate() are relatively rare. In Rust instead, even though these functions do "propagate" a qemu_api::Error into a C Error**, there is no error_setg() anywhere that could check for non-NULL errp and call abort(). error_propagate()'s behavior of ignoring subsequent errors is generally considered weird, and there would be a bigger risk of triggering it from Rust code. [1] This is actually a violation of the preconditions of error_propagate(), so it should not happen. But you never know... Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 5 + rust/Cargo.lock | 17 ++ rust/Cargo.toml | 1 + rust/qemu-api/Cargo.toml | 2 + rust/qemu-api/meson.build | 1 + rust/qemu-api/src/error.rs | 312 +++++++++++++++++++++++++++++++++++++ rust/qemu-api/src/lib.rs | 3 + 7 files changed, 341 insertions(+) create mode 100644 rust/qemu-api/src/error.rs diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 34d9c7945b..d60d56d0a6 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -96,6 +96,11 @@ are missing: architecture (VMState). Right now, VMState lacks type safety because it is hard to place the ``VMStateField`` definitions in traits. +* NUL-terminated file names with ``#[track_caller]`` are scheduled for + inclusion as ``#![feature(location_file_nul)]``, but it will be a while + before QEMU can use them. For now, there is special code in + ``util/error.c`` to support non-NUL-terminated file names. + * associated const equality would be nice to have for some users of ``callbacks::FnCall``, but is still experimental. ``ASSERT_IS_SOME`` replaces it. diff --git a/rust/Cargo.lock b/rust/Cargo.lock index bccfe855a7..b785c718f3 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "anyhow" +version = "1.0.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" + [[package]] name = "arbitrary-int" version = "1.2.7" @@ -44,6 +50,15 @@ version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" +[[package]] +name = "foreign" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17ca1b5be8c9d320daf386f1809c7acc0cb09accbae795c2001953fa50585846" +dependencies = [ + "libc", +] + [[package]] name = "hpet" version = "0.1.0" @@ -114,6 +129,8 @@ dependencies = [ name = "qemu_api" version = "0.1.0" dependencies = [ + "anyhow", + "foreign", "libc", "qemu_api_macros", ] diff --git a/rust/Cargo.toml b/rust/Cargo.toml index fd4c2fbf84..0868e1b426 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -67,6 +67,7 @@ missing_safety_doc = "deny" mut_mut = "deny" needless_bitwise_bool = "deny" needless_pass_by_ref_mut = "deny" +needless_update = "deny" no_effect_underscore_binding = "deny" option_option = "deny" or_fun_call = "deny" diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml index c96cf50e7a..db7000dee4 100644 --- a/rust/qemu-api/Cargo.toml +++ b/rust/qemu-api/Cargo.toml @@ -15,7 +15,9 @@ rust-version.workspace = true [dependencies] qemu_api_macros = { path = "../qemu-api-macros" } +anyhow = "~1.0" libc = "0.2.162" +foreign = "~0.3.1" [features] default = ["debug_cell"] diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 2f0f3b2aae..cac8595a14 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -19,6 +19,7 @@ _qemu_api_rs = static_library( 'src/cell.rs', 'src/chardev.rs', 'src/errno.rs', + 'src/error.rs', 'src/irq.rs', 'src/memory.rs', 'src/module.rs', diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs new file mode 100644 index 0000000000..80157f6ea1 --- /dev/null +++ b/rust/qemu-api/src/error.rs @@ -0,0 +1,312 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Error propagation for QEMU Rust code +//! +//! This module contains [`Error`], the bridge between Rust errors and +//! [`Result`](std::result::Result)s and QEMU's C [`Error`](bindings::Error) +//! struct. +//! +//! For FFI code, [`Error`] provides functions to simplify conversion between +//! the Rust ([`Result<>`](std::result::Result)) and C (`Error**`) conventions: +//! +//! * [`ok_or_propagate`](crate::Error::ok_or_propagate), +//! [`bool_or_propagate`](crate::Error::bool_or_propagate), +//! [`ptr_or_propagate`](crate::Error::ptr_or_propagate) can be used to build +//! a C return value while also propagating an error condition +//! +//! * [`err_or_else`](crate::Error::err_or_else) and +//! [`err_or_unit`](crate::Error::err_or_unit) can be used to build a `Result` +//! +//! This module is most commonly used at the boundary between C and Rust code; +//! other code will usually access it through the +//! [`qemu_api::Result`](crate::Result) type alias, and will use the +//! [`std::error::Error`] interface to let C errors participate in Rust's error +//! handling functionality. +//! +//! Rust code can also create use this module to create an error object that +//! will be passed up to C code, though in most cases this will be done +//! transparently through the `?` operator. Errors can be constructed from a +//! simple error string, from an [`anyhow::Error`] to pass any other Rust error +//! type up to C code, or from a combination of the two. +//! +//! The third case, corresponding to [`Error::with_error`], is the only one that +//! requires mentioning [`qemu_api::Error`](crate::Error) explicitly. Similar +//! to how QEMU's C code handles errno values, the string and the +//! `anyhow::Error` object will be concatenated with `:` as the separator. + +use std::{ + borrow::Cow, + ffi::{c_char, c_int, c_void, CStr}, + fmt::{self, Display}, + panic, ptr, +}; + +use foreign::{prelude::*, OwnedPointer}; + +use crate::bindings; + +pub type Result = std::result::Result; + +#[derive(Debug)] +pub struct Error { + msg: Option>, + /// Appends the print string of the error to the msg if not None + cause: Option, + file: &'static str, + line: u32, +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.cause.as_ref().map(AsRef::as_ref) + } + + #[allow(deprecated)] + fn description(&self) -> &str { + self.msg + .as_deref() + .or_else(|| self.cause.as_deref().map(std::error::Error::description)) + .expect("no message nor cause?") + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut prefix = ""; + if let Some(ref msg) = self.msg { + write!(f, "{msg}")?; + prefix = ": "; + } + if let Some(ref cause) = self.cause { + write!(f, "{prefix}{cause}")?; + } else if prefix.is_empty() { + panic!("no message nor cause?"); + } + Ok(()) + } +} + +impl From for Error { + #[track_caller] + fn from(msg: String) -> Self { + let location = panic::Location::caller(); + Error { + msg: Some(Cow::Owned(msg)), + cause: None, + file: location.file(), + line: location.line(), + } + } +} + +impl From<&'static str> for Error { + #[track_caller] + fn from(msg: &'static str) -> Self { + let location = panic::Location::caller(); + Error { + msg: Some(Cow::Borrowed(msg)), + cause: None, + file: location.file(), + line: location.line(), + } + } +} + +impl From for Error { + #[track_caller] + fn from(error: anyhow::Error) -> Self { + let location = panic::Location::caller(); + Error { + msg: None, + cause: Some(error), + file: location.file(), + line: location.line(), + } + } +} + +impl Error { + /// Create a new error, prepending `msg` to the + /// description of `cause` + #[track_caller] + pub fn with_error(msg: impl Into>, cause: impl Into) -> Self { + let location = panic::Location::caller(); + Error { + msg: Some(msg.into()), + cause: Some(cause.into()), + file: location.file(), + line: location.line(), + } + } + + /// Consume a result, returning `false` if it is an error and + /// `true` if it is successful. The error is propagated into + /// `errp` like the C API `error_propagate` would do. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; + /// typically it is received from C code and need not be + /// checked further at the Rust↔C boundary. + pub unsafe fn bool_or_propagate(result: Result<()>, errp: *mut *mut bindings::Error) -> bool { + // SAFETY: caller guarantees errp is valid + unsafe { Self::ok_or_propagate(result, errp) }.is_some() + } + + /// Consume a result, returning a `NULL` pointer if it is an error and + /// a C representation of the contents if it is successful. This is + /// similar to the C API `error_propagate`, but it panics if `*errp` + /// is not `NULL`. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; + /// typically it is received from C code and need not be + /// checked further at the Rust↔C boundary. + /// + /// See [`propagate`](Error::propagate) for more information. + #[must_use] + pub unsafe fn ptr_or_propagate( + result: Result, + errp: *mut *mut bindings::Error, + ) -> *mut T::Foreign { + // SAFETY: caller guarantees errp is valid + unsafe { Self::ok_or_propagate(result, errp) }.clone_to_foreign_ptr() + } + + /// Consume a result in the same way as `self.ok()`, but also propagate + /// a possible error into `errp`. This is similar to the C API + /// `error_propagate`, but it panics if `*errp` is not `NULL`. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; + /// typically it is received from C code and need not be + /// checked further at the Rust↔C boundary. + /// + /// See [`propagate`](Error::propagate) for more information. + pub unsafe fn ok_or_propagate( + result: Result, + errp: *mut *mut bindings::Error, + ) -> Option { + result.map_err(|err| unsafe { err.propagate(errp) }).ok() + } + + /// Equivalent of the C function `error_propagate`. Fill `*errp` + /// with the information container in `self` if `errp` is not NULL; + /// then consume it. + /// + /// This is similar to the C API `error_propagate`, but it panics if + /// `*errp` is not `NULL`. + /// + /// # Safety + /// + /// `errp` must be a valid argument to `error_propagate`; it can be + /// `NULL` or it can point to any of: + /// * `error_abort` + /// * `error_fatal` + /// * a local variable of (C) type `Error *` + /// + /// Typically `errp` is received from C code and need not be + /// checked further at the Rust↔C boundary. + pub unsafe fn propagate(self, errp: *mut *mut bindings::Error) { + if errp.is_null() { + return; + } + + // SAFETY: caller guarantees that errp and *errp are valid + unsafe { + assert_eq!(*errp, ptr::null_mut()); + bindings::error_propagate(errp, self.clone_to_foreign_ptr()); + } + } + + /// Convert a C `Error*` into a Rust `Result`, using + /// `Ok(())` if `c_error` is NULL. Free the `Error*`. + /// + /// # Safety + /// + /// `c_error` must be `NULL` or valid; typically it was initialized + /// with `ptr::null_mut()` and passed by reference to a C function. + pub unsafe fn err_or_unit(c_error: *mut bindings::Error) -> Result<()> { + // SAFETY: caller guarantees c_error is valid + unsafe { Self::err_or_else(c_error, || ()) } + } + + /// Convert a C `Error*` into a Rust `Result`, calling `f()` to + /// obtain an `Ok` value if `c_error` is NULL. Free the `Error*`. + /// + /// # Safety + /// + /// `c_error` must be `NULL` or point to a valid C [`struct + /// Error`](bindings::Error); typically it was initialized with + /// `ptr::null_mut()` and passed by reference to a C function. + pub unsafe fn err_or_else T>( + c_error: *mut bindings::Error, + f: F, + ) -> Result { + // SAFETY: caller guarantees c_error is valid + let err = unsafe { Option::::from_foreign(c_error) }; + match err { + None => Ok(f()), + Some(err) => Err(err), + } + } +} + +impl FreeForeign for Error { + type Foreign = bindings::Error; + + unsafe fn free_foreign(p: *mut bindings::Error) { + // SAFETY: caller guarantees p is valid + unsafe { + bindings::error_free(p); + } + } +} + +impl CloneToForeign for Error { + fn clone_to_foreign(&self) -> OwnedPointer { + // SAFETY: all arguments are controlled by this function + unsafe { + let err: *mut c_void = libc::malloc(std::mem::size_of::()); + let err: &mut bindings::Error = &mut *err.cast(); + *err = bindings::Error { + msg: format!("{self}").clone_to_foreign_ptr(), + err_class: bindings::ERROR_CLASS_GENERIC_ERROR, + src_len: self.file.len() as c_int, + src: self.file.as_ptr().cast::(), + line: self.line as c_int, + func: ptr::null_mut(), + hint: ptr::null_mut(), + }; + OwnedPointer::new(err) + } + } +} + +impl FromForeign for Error { + unsafe fn cloned_from_foreign(c_error: *const bindings::Error) -> Self { + // SAFETY: caller guarantees c_error is valid + unsafe { + let error = &*c_error; + let file = if error.src_len < 0 { + // NUL-terminated + CStr::from_ptr(error.src).to_str() + } else { + // Can become str::from_utf8 with Rust 1.87.0 + std::str::from_utf8(std::slice::from_raw_parts( + &*error.src.cast::(), + error.src_len as usize, + )) + }; + + Error { + msg: FromForeign::cloned_from_foreign(error.msg), + cause: None, + file: file.unwrap(), + line: error.line as u32, + } + } + } +} diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 234a94e3c2..93902fc94b 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -19,6 +19,7 @@ pub mod callbacks; pub mod cell; pub mod chardev; pub mod errno; +pub mod error; pub mod irq; pub mod memory; pub mod module; @@ -34,6 +35,8 @@ use std::{ ffi::c_void, }; +pub use error::{Error, Result}; + #[cfg(HAVE_GLIB_WITH_ALIGNED_ALLOC)] extern "C" { fn g_aligned_alloc0( From 9a33f49f44453b2914dc2e9be2633c0dfcb9e267 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 29 May 2025 11:52:00 +0200 Subject: [PATCH 1437/2760] rust: qemu-api: add tests for Error bindings Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/error.rs | 104 +++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/rust/qemu-api/src/error.rs b/rust/qemu-api/src/error.rs index 80157f6ea1..e114fc4178 100644 --- a/rust/qemu-api/src/error.rs +++ b/rust/qemu-api/src/error.rs @@ -310,3 +310,107 @@ impl FromForeign for Error { } } } + +#[cfg(test)] +mod tests { + use std::ffi::CStr; + + use anyhow::anyhow; + use foreign::OwnedPointer; + + use super::*; + use crate::{assert_match, bindings}; + + #[track_caller] + fn error_for_test(msg: &CStr) -> OwnedPointer { + // SAFETY: all arguments are controlled by this function + let location = panic::Location::caller(); + unsafe { + let err: *mut c_void = libc::malloc(std::mem::size_of::()); + let err: &mut bindings::Error = &mut *err.cast(); + *err = bindings::Error { + msg: msg.clone_to_foreign_ptr(), + err_class: bindings::ERROR_CLASS_GENERIC_ERROR, + src_len: location.file().len() as c_int, + src: location.file().as_ptr().cast::(), + line: location.line() as c_int, + func: ptr::null_mut(), + hint: ptr::null_mut(), + }; + OwnedPointer::new(err) + } + } + + unsafe fn error_get_pretty<'a>(local_err: *mut bindings::Error) -> &'a CStr { + unsafe { CStr::from_ptr(bindings::error_get_pretty(local_err)) } + } + + #[test] + #[allow(deprecated)] + fn test_description() { + use std::error::Error; + + assert_eq!(super::Error::from("msg").description(), "msg"); + assert_eq!(super::Error::from("msg".to_owned()).description(), "msg"); + } + + #[test] + fn test_display() { + assert_eq!(&*format!("{}", Error::from("msg")), "msg"); + assert_eq!(&*format!("{}", Error::from("msg".to_owned())), "msg"); + assert_eq!(&*format!("{}", Error::from(anyhow!("msg"))), "msg"); + + assert_eq!( + &*format!("{}", Error::with_error("msg", anyhow!("cause"))), + "msg: cause" + ); + } + + #[test] + fn test_bool_or_propagate() { + unsafe { + let mut local_err: *mut bindings::Error = ptr::null_mut(); + + assert!(Error::bool_or_propagate(Ok(()), &mut local_err)); + assert_eq!(local_err, ptr::null_mut()); + + let my_err = Error::from("msg"); + assert!(!Error::bool_or_propagate(Err(my_err), &mut local_err)); + assert_ne!(local_err, ptr::null_mut()); + assert_eq!(error_get_pretty(local_err), c"msg"); + bindings::error_free(local_err); + } + } + + #[test] + fn test_ptr_or_propagate() { + unsafe { + let mut local_err: *mut bindings::Error = ptr::null_mut(); + + let ret = Error::ptr_or_propagate(Ok("abc".to_owned()), &mut local_err); + assert_eq!(String::from_foreign(ret), "abc"); + assert_eq!(local_err, ptr::null_mut()); + + let my_err = Error::from("msg"); + assert_eq!( + Error::ptr_or_propagate(Err::(my_err), &mut local_err), + ptr::null_mut() + ); + assert_ne!(local_err, ptr::null_mut()); + assert_eq!(error_get_pretty(local_err), c"msg"); + bindings::error_free(local_err); + } + } + + #[test] + fn test_err_or_unit() { + unsafe { + let result = Error::err_or_unit(ptr::null_mut()); + assert_match!(result, Ok(())); + + let err = error_for_test(c"msg"); + let err = Error::err_or_unit(err.into_inner()).unwrap_err(); + assert_eq!(&*format!("{err}"), "msg"); + } + } +} From 4b66abead9c9c7b637fb87e6bf782a45eee2a621 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 23 May 2025 18:31:29 +0200 Subject: [PATCH 1438/2760] rust: qdev: support returning errors from realize Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 5 +++-- rust/hw/timer/hpet/src/device.rs | 5 +++-- rust/qemu-api/src/qdev.rs | 12 ++++++++---- 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 0501fa5be9..be8387f6f2 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -175,7 +175,7 @@ impl DeviceImpl for PL011State { fn vmsd() -> Option<&'static VMStateDescription> { Some(&device_class::VMSTATE_PL011) } - const REALIZE: Option = Some(Self::realize); + const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); } impl ResettablePhasesImpl for PL011State { @@ -619,9 +619,10 @@ impl PL011State { } } - fn realize(&self) { + fn realize(&self) -> qemu_api::Result<()> { self.char_backend .enable_handlers(self, Self::can_receive, Self::receive, Self::event); + Ok(()) } fn reset_hold(&self, _type: ResetType) { diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index e3ba62b287..68c82b09b6 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -724,7 +724,7 @@ impl HPETState { } } - fn realize(&self) { + fn realize(&self) -> qemu_api::Result<()> { if self.int_route_cap == 0 { // TODO: Add error binding: warn_report() println!("Hpet's hpet-intcap property not initialized"); @@ -751,6 +751,7 @@ impl HPETState { self.init_gpio_in(2, HPETState::handle_legacy_irq); self.init_gpio_out(from_ref(&self.pit_enabled)); + Ok(()) } fn reset_hold(&self, _type: ResetType) { @@ -1042,7 +1043,7 @@ impl DeviceImpl for HPETState { Some(&VMSTATE_HPET) } - const REALIZE: Option = Some(Self::realize); + const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); } impl ResettablePhasesImpl for HPETState { diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 1279d7a58d..0610959f46 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -12,10 +12,11 @@ use std::{ pub use bindings::{ClockEvent, DeviceClass, Property, ResetType}; use crate::{ - bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, Error, ResettableClass}, + bindings::{self, qdev_init_gpio_in, qdev_init_gpio_out, ResettableClass}, callbacks::FnCall, cell::{bql_locked, Opaque}, chardev::Chardev, + error::{Error, Result}, irq::InterruptSource, prelude::*, qom::{ObjectClass, ObjectImpl, Owned}, @@ -108,7 +109,7 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA { /// /// If not `None`, the parent class's `realize` method is overridden /// with the function pointed to by `REALIZE`. - const REALIZE: Option = None; + const REALIZE: Option Result<()>> = None; /// An array providing the properties that the user can set on the /// device. Not a `const` because referencing statics in constants @@ -134,10 +135,13 @@ pub trait DeviceImpl: ObjectImpl + ResettablePhasesImpl + IsA { /// readable/writeable from one thread at any time. unsafe extern "C" fn rust_realize_fn( dev: *mut bindings::DeviceState, - _errp: *mut *mut Error, + errp: *mut *mut bindings::Error, ) { let state = NonNull::new(dev).unwrap().cast::(); - T::REALIZE.unwrap()(unsafe { state.as_ref() }); + let result = T::REALIZE.unwrap()(unsafe { state.as_ref() }); + unsafe { + Error::ok_or_propagate(result, errp); + } } unsafe impl InterfaceType for ResettableClass { From b3bf86b8936dbca8689623d22b8955e071143a5e Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 26 May 2025 13:52:11 +0200 Subject: [PATCH 1439/2760] rust/hpet: change type of num_timers to usize Remove the need to convert after every read of the BqlCell. Because the vmstate uses a u8 as the size of the VARRAY, this requires switching the VARRAY to use num_timers_save; which in turn requires ensuring that the num_timers_save is always there. For simplicity do this by removing support for version 1, which QEMU has not been producing for ~15 years. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/device.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 68c82b09b6..a957de1e76 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -12,7 +12,7 @@ use std::{ use qemu_api::{ bindings::{ address_space_memory, address_space_stl_le, qdev_prop_bit, qdev_prop_bool, - qdev_prop_uint32, qdev_prop_uint8, + qdev_prop_uint32, qdev_prop_usize, }, cell::{BqlCell, BqlRefCell}, irq::InterruptSource, @@ -36,9 +36,9 @@ use crate::fw_cfg::HPETFwConfig; const HPET_REG_SPACE_LEN: u64 = 0x400; // 1024 bytes /// Minimum recommended hardware implementation. -const HPET_MIN_TIMERS: u8 = 3; +const HPET_MIN_TIMERS: usize = 3; /// Maximum timers in each timer block. -const HPET_MAX_TIMERS: u8 = 32; +const HPET_MAX_TIMERS: usize = 32; /// Flags that HPETState.flags supports. const HPET_FLAG_MSI_SUPPORT_SHIFT: usize = 0; @@ -561,8 +561,8 @@ pub struct HPETState { /// HPET timer array managed by this timer block. #[doc(alias = "timer")] - timers: [BqlRefCell; HPET_MAX_TIMERS as usize], - num_timers: BqlCell, + timers: [BqlRefCell; HPET_MAX_TIMERS], + num_timers: BqlCell, num_timers_save: BqlCell, /// Instance id (HPET timer block ID). @@ -572,7 +572,7 @@ pub struct HPETState { impl HPETState { // Get num_timers with `usize` type, which is useful to play with array index. fn get_num_timers(&self) -> usize { - self.num_timers.get().into() + self.num_timers.get() } const fn has_msi_flag(&self) -> bool { @@ -854,7 +854,7 @@ impl HPETState { * also added to the migration stream. Check that it matches the value * that was configured. */ - self.num_timers_save.set(self.num_timers.get()); + self.num_timers_save.set(self.num_timers.get() as u8); 0 } @@ -884,7 +884,7 @@ impl HPETState { } fn validate_num_timers(&self, _version_id: u8) -> bool { - self.num_timers.get() == self.num_timers_save.get() + self.num_timers.get() == self.num_timers_save.get().into() } } @@ -911,7 +911,7 @@ qemu_api::declare_properties! { c"timers", HPETState, num_timers, - unsafe { &qdev_prop_uint8 }, + unsafe { &qdev_prop_usize }, u8, default = HPET_MIN_TIMERS ), @@ -1016,16 +1016,16 @@ const VALIDATE_TIMERS_NAME: &CStr = c"num_timers must match"; static VMSTATE_HPET: VMStateDescription = VMStateDescription { name: c"hpet".as_ptr(), version_id: 2, - minimum_version_id: 1, + minimum_version_id: 2, pre_save: Some(hpet_pre_save), post_load: Some(hpet_post_load), fields: vmstate_fields! { vmstate_of!(HPETState, config), vmstate_of!(HPETState, int_status), vmstate_of!(HPETState, counter), - vmstate_of!(HPETState, num_timers_save).with_version_id(2), + vmstate_of!(HPETState, num_timers_save), vmstate_validate!(HPETState, VALIDATE_TIMERS_NAME, HPETState::validate_num_timers), - vmstate_struct!(HPETState, timers[0 .. num_timers], &VMSTATE_HPET_TIMER, BqlRefCell, HPETState::validate_num_timers).with_version_id(0), + vmstate_struct!(HPETState, timers[0 .. num_timers_save], &VMSTATE_HPET_TIMER, BqlRefCell, HPETState::validate_num_timers).with_version_id(0), }, subsections: vmstate_subsections! { VMSTATE_HPET_RTC_IRQ_LEVEL, From 6e85cfe44c0e0311d6395526a064eb5af761a879 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 29 May 2025 10:58:28 +0200 Subject: [PATCH 1440/2760] hpet: adjust VMState for consistency with Rust version No functional change intended. Suggested-by: Zhao Liu Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- hw/timer/hpet.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 0fd1337a15..9db027cf76 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -328,16 +328,16 @@ static const VMStateDescription vmstate_hpet_timer = { static const VMStateDescription vmstate_hpet = { .name = "hpet", .version_id = 2, - .minimum_version_id = 1, + .minimum_version_id = 2, .pre_save = hpet_pre_save, .post_load = hpet_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT64(config, HPETState), VMSTATE_UINT64(isr, HPETState), VMSTATE_UINT64(hpet_counter, HPETState), - VMSTATE_UINT8_V(num_timers_save, HPETState, 2), + VMSTATE_UINT8(num_timers_save, HPETState), VMSTATE_VALIDATE("num_timers must match", hpet_validate_num_timers), - VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers, 0, + VMSTATE_STRUCT_VARRAY_UINT8(timer, HPETState, num_timers_save, 0, vmstate_hpet_timer, HPETTimer), VMSTATE_END_OF_LIST() }, From 14b5a799339d2d21826eac5ab1e98d00b1f1f89f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 26 May 2025 14:49:13 +0200 Subject: [PATCH 1441/2760] hpet: return errors from realize if properties are incorrect Do not silently adjust num_timers, and fail if intcap is 0. Reviewed-by: Markus Armbruster Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- hw/timer/hpet.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/timer/hpet.c b/hw/timer/hpet.c index 9db027cf76..cb48cc151f 100644 --- a/hw/timer/hpet.c +++ b/hw/timer/hpet.c @@ -691,8 +691,14 @@ static void hpet_realize(DeviceState *dev, Error **errp) int i; HPETTimer *timer; + if (s->num_timers < HPET_MIN_TIMERS || s->num_timers > HPET_MAX_TIMERS) { + error_setg(errp, "hpet.num_timers must be between %d and %d", + HPET_MIN_TIMERS, HPET_MAX_TIMERS); + return; + } if (!s->intcap) { - warn_report("Hpet's intcap not initialized"); + error_setg(errp, "hpet.hpet-intcap not initialized"); + return; } if (hpet_fw_cfg.count == UINT8_MAX) { /* first instance */ @@ -700,7 +706,7 @@ static void hpet_realize(DeviceState *dev, Error **errp) } if (hpet_fw_cfg.count == 8) { - error_setg(errp, "Only 8 instances of HPET is allowed"); + error_setg(errp, "Only 8 instances of HPET are allowed"); return; } @@ -710,11 +716,6 @@ static void hpet_realize(DeviceState *dev, Error **errp) sysbus_init_irq(sbd, &s->irqs[i]); } - if (s->num_timers < HPET_MIN_TIMERS) { - s->num_timers = HPET_MIN_TIMERS; - } else if (s->num_timers > HPET_MAX_TIMERS) { - s->num_timers = HPET_MAX_TIMERS; - } for (i = 0; i < HPET_MAX_TIMERS; i++) { timer = &s->timer[i]; timer->qemu_timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, hpet_timer, timer); From 4d2fec89cb8651ee760ff9296c8d3e2ade4cc063 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 23 May 2025 18:37:55 +0200 Subject: [PATCH 1442/2760] rust/hpet: return errors from realize if properties are incorrect Match the code in hpet.c; this also allows removing the BqlCell from the num_timers field. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/device.rs | 16 +++++++--------- rust/hw/timer/hpet/src/fw_cfg.rs | 7 +++---- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index a957de1e76..cd439f90b7 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -725,18 +725,16 @@ impl HPETState { } fn realize(&self) -> qemu_api::Result<()> { + if self.num_timers.get() < HPET_MIN_TIMERS || self.num_timers.get() > HPET_MAX_TIMERS { + Err(format!( + "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}" + ))?; + } if self.int_route_cap == 0 { - // TODO: Add error binding: warn_report() - println!("Hpet's hpet-intcap property not initialized"); + Err("hpet.hpet-intcap property not initialized")?; } - self.hpet_id.set(HPETFwConfig::assign_hpet_id()); - - if self.num_timers.get() < HPET_MIN_TIMERS { - self.num_timers.set(HPET_MIN_TIMERS); - } else if self.num_timers.get() > HPET_MAX_TIMERS { - self.num_timers.set(HPET_MAX_TIMERS); - } + self.hpet_id.set(HPETFwConfig::assign_hpet_id()?); self.init_timer(); // 64-bit General Capabilities and ID Register; LegacyReplacementRoute. diff --git a/rust/hw/timer/hpet/src/fw_cfg.rs b/rust/hw/timer/hpet/src/fw_cfg.rs index 6c10316104..619d662ee1 100644 --- a/rust/hw/timer/hpet/src/fw_cfg.rs +++ b/rust/hw/timer/hpet/src/fw_cfg.rs @@ -36,7 +36,7 @@ pub static mut hpet_fw_cfg: HPETFwConfig = HPETFwConfig { }; impl HPETFwConfig { - pub(crate) fn assign_hpet_id() -> usize { + pub(crate) fn assign_hpet_id() -> Result { assert!(bql_locked()); // SAFETY: all accesses go through these methods, which guarantee // that the accesses are protected by the BQL. @@ -48,13 +48,12 @@ impl HPETFwConfig { } if fw_cfg.count == 8 { - // TODO: Add error binding: error_setg() - panic!("Only 8 instances of HPET is allowed"); + Err("Only 8 instances of HPET are allowed")?; } let id: usize = fw_cfg.count.into(); fw_cfg.count += 1; - id + Ok(id) } pub(crate) fn update_hpet_cfg(hpet_id: usize, timer_block_id: u32, address: u64) { From 869b0afa4fafb73ba5a3c556b0ef5e5c1a39e717 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 26 May 2025 13:54:21 +0200 Subject: [PATCH 1443/2760] rust/hpet: Drop BqlCell wrapper for num_timers Now that the num_timers field is initialized as a property, someone may change its default value using qdev_prop_set_uint8(), but the value is fixed after the Rust code sees it first. Since there is no need to modify it after realize(), it is not to be necessary to have a BqlCell wrapper. Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250520152750.2542612-4-zhao1.liu@intel.com [Remove .into() as well. - Paolo] Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/device.rs | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index cd439f90b7..735b2fbef7 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -562,7 +562,7 @@ pub struct HPETState { /// HPET timer array managed by this timer block. #[doc(alias = "timer")] timers: [BqlRefCell; HPET_MAX_TIMERS], - num_timers: BqlCell, + num_timers: usize, num_timers_save: BqlCell, /// Instance id (HPET timer block ID). @@ -570,11 +570,6 @@ pub struct HPETState { } impl HPETState { - // Get num_timers with `usize` type, which is useful to play with array index. - fn get_num_timers(&self) -> usize { - self.num_timers.get() - } - const fn has_msi_flag(&self) -> bool { self.flags & (1 << HPET_FLAG_MSI_SUPPORT_SHIFT) != 0 } @@ -636,7 +631,7 @@ impl HPETState { self.hpet_offset .set(ticks_to_ns(self.counter.get()) - CLOCK_VIRTUAL.get_ns()); - for timer in self.timers.iter().take(self.get_num_timers()) { + for timer in self.timers.iter().take(self.num_timers) { let mut t = timer.borrow_mut(); if t.is_int_enabled() && t.is_int_active() { @@ -648,7 +643,7 @@ impl HPETState { // Halt main counter and disable interrupt generation. self.counter.set(self.get_ticks()); - for timer in self.timers.iter().take(self.get_num_timers()) { + for timer in self.timers.iter().take(self.num_timers) { timer.borrow_mut().del_timer(); } } @@ -671,7 +666,7 @@ impl HPETState { let new_val = val << shift; let cleared = new_val & self.int_status.get(); - for (index, timer) in self.timers.iter().take(self.get_num_timers()).enumerate() { + for (index, timer) in self.timers.iter().take(self.num_timers).enumerate() { if cleared & (1 << index) != 0 { timer.borrow_mut().update_irq(false); } @@ -725,7 +720,7 @@ impl HPETState { } fn realize(&self) -> qemu_api::Result<()> { - if self.num_timers.get() < HPET_MIN_TIMERS || self.num_timers.get() > HPET_MAX_TIMERS { + if self.num_timers < HPET_MIN_TIMERS || self.num_timers > HPET_MAX_TIMERS { Err(format!( "hpet.num_timers must be between {HPET_MIN_TIMERS} and {HPET_MAX_TIMERS}" ))?; @@ -743,7 +738,7 @@ impl HPETState { 1 << HPET_CAP_COUNT_SIZE_CAP_SHIFT | 1 << HPET_CAP_LEG_RT_CAP_SHIFT | HPET_CAP_VENDER_ID_VALUE << HPET_CAP_VENDER_ID_SHIFT | - ((self.get_num_timers() - 1) as u64) << HPET_CAP_NUM_TIM_SHIFT | // indicate the last timer + ((self.num_timers - 1) as u64) << HPET_CAP_NUM_TIM_SHIFT | // indicate the last timer (HPET_CLK_PERIOD * FS_PER_NS) << HPET_CAP_CNT_CLK_PERIOD_SHIFT, // 10 ns ); @@ -753,7 +748,7 @@ impl HPETState { } fn reset_hold(&self, _type: ResetType) { - for timer in self.timers.iter().take(self.get_num_timers()) { + for timer in self.timers.iter().take(self.num_timers) { timer.borrow_mut().reset(); } @@ -781,7 +776,7 @@ impl HPETState { GlobalRegister::try_from(addr).map(HPETRegister::Global) } else { let timer_id: usize = ((addr - 0x100) / 0x20) as usize; - if timer_id <= self.get_num_timers() { + if timer_id < self.num_timers { // TODO: Add trace point - trace_hpet_ram_[read|write]_timer_id(timer_id) TimerRegister::try_from(addr & 0x18) .map(|reg| HPETRegister::Timer(&self.timers[timer_id], reg)) @@ -852,12 +847,12 @@ impl HPETState { * also added to the migration stream. Check that it matches the value * that was configured. */ - self.num_timers_save.set(self.num_timers.get() as u8); + self.num_timers_save.set(self.num_timers as u8); 0 } fn post_load(&self, _version_id: u8) -> i32 { - for timer in self.timers.iter().take(self.get_num_timers()) { + for timer in self.timers.iter().take(self.num_timers) { let mut t = timer.borrow_mut(); t.cmp64 = t.calculate_cmp64(t.get_state().counter.get(), t.cmp); @@ -882,7 +877,7 @@ impl HPETState { } fn validate_num_timers(&self, _version_id: u8) -> bool { - self.num_timers.get() == self.num_timers_save.get().into() + self.num_timers == self.num_timers_save.get().into() } } From 9c00ef6248bd6545f262f18f31b15fc681e88972 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 26 May 2025 16:21:29 +0200 Subject: [PATCH 1444/2760] docs: update Rust module status error is new; offset_of is gone. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index d60d56d0a6..47e9677fcb 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -160,10 +160,10 @@ module status ``callbacks`` complete ``cell`` stable ``errno`` complete +``error`` stable ``irq`` complete ``memory`` stable ``module`` complete -``offset_of`` stable ``qdev`` stable ``qom`` stable ``sysbus`` stable From bc2a48d647752e4f99247184f5dfe00e67a2de8f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 5 Jun 2025 11:12:15 +0200 Subject: [PATCH 1445/2760] rust: make TryFrom macro more resilient If the enum includes values such as "Ok", "Err", or "Error", the TryInto macro can cause errors. Be careful and qualify identifiers with the full path, or in the case of TryFrom<>::Error do not use the associated type at all. Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/lib.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 103470785e..c18bb4e036 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -203,8 +203,8 @@ fn derive_tryinto_body( Ok(quote! { #(const #discriminants: #repr = #name::#discriminants as #repr;)*; match value { - #(#discriminants => Ok(#name::#discriminants),)* - _ => Err(value), + #(#discriminants => core::result::Result::Ok(#name::#discriminants),)* + _ => core::result::Result::Err(value), } }) } @@ -236,7 +236,8 @@ fn derive_tryinto_or_error(input: DeriveInput) -> Result for #name { type Error = #repr; - fn try_from(value: #repr) -> Result { + #[allow(ambiguous_associated_items)] + fn try_from(value: #repr) -> Result { #body } } From 4cdc489eb9c25f76255a550a4a0b19cda3435a76 Mon Sep 17 00:00:00 2001 From: Tom Lendacky Date: Fri, 28 Mar 2025 15:30:24 -0500 Subject: [PATCH 1446/2760] i386/kvm: Prefault memory on page state change A page state change is typically followed by an access of the page(s) and results in another VMEXIT in order to map the page into the nested page table. Depending on the size of page state change request, this can generate a number of additional VMEXITs. For example, under SNP, when Linux is utilizing lazy memory acceptance, memory is typically accepted in 4M chunks. A page state change request is submitted to mark the pages as private, followed by validation of the memory. Since the guest_memfd currently only supports 4K pages, each page validation will result in VMEXIT to map the page, resulting in 1024 additional exits. When performing a page state change, invoke KVM_PRE_FAULT_MEMORY for the size of the page state change in order to pre-map the pages and avoid the additional VMEXITs. This helps speed up boot times. Signed-off-by: Tom Lendacky Link: https://lore.kernel.org/r/f5411c42340bd2f5c14972551edb4e959995e42b.1743193824.git.thomas.lendacky@amd.com Signed-off-by: Paolo Bonzini --- accel/kvm/kvm-all.c | 2 ++ include/system/kvm.h | 1 + target/i386/kvm/kvm.c | 31 ++++++++++++++++++++++++++----- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 51526d301b..a31778341c 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -99,6 +99,7 @@ bool kvm_allowed; bool kvm_readonly_mem_allowed; bool kvm_vm_attributes_allowed; bool kvm_msi_use_devid; +bool kvm_pre_fault_memory_supported; static bool kvm_has_guest_debug; static int kvm_sstep_flags; static bool kvm_immediate_exit; @@ -2745,6 +2746,7 @@ static int kvm_init(MachineState *ms) kvm_check_extension(s, KVM_CAP_GUEST_MEMFD) && kvm_check_extension(s, KVM_CAP_USER_MEMORY2) && (kvm_supported_memory_attributes & KVM_MEMORY_ATTRIBUTE_PRIVATE); + kvm_pre_fault_memory_supported = kvm_vm_check_extension(s, KVM_CAP_PRE_FAULT_MEMORY); if (s->kernel_irqchip_split == ON_OFF_AUTO_AUTO) { s->kernel_irqchip_split = mc->default_kernel_irqchip_split ? ON_OFF_AUTO_ON : ON_OFF_AUTO_OFF; diff --git a/include/system/kvm.h b/include/system/kvm.h index 62ec131d4d..7cc60d26f2 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h @@ -42,6 +42,7 @@ extern bool kvm_gsi_routing_allowed; extern bool kvm_gsi_direct_mapping; extern bool kvm_readonly_mem_allowed; extern bool kvm_msi_use_devid; +extern bool kvm_pre_fault_memory_supported; #define kvm_enabled() (kvm_allowed) /** diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index a6bc089d02..56a6b9b638 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -6018,9 +6018,11 @@ static bool host_supports_vmx(void) * because private/shared page tracking is already provided through other * means, these 2 use-cases should be treated as being mutually-exclusive. */ -static int kvm_handle_hc_map_gpa_range(struct kvm_run *run) +static int kvm_handle_hc_map_gpa_range(X86CPU *cpu, struct kvm_run *run) { + struct kvm_pre_fault_memory mem; uint64_t gpa, size, attributes; + int ret; if (!machine_require_guest_memfd(current_machine)) return -EINVAL; @@ -6031,13 +6033,32 @@ static int kvm_handle_hc_map_gpa_range(struct kvm_run *run) trace_kvm_hc_map_gpa_range(gpa, size, attributes, run->hypercall.flags); - return kvm_convert_memory(gpa, size, attributes & KVM_MAP_GPA_RANGE_ENCRYPTED); + ret = kvm_convert_memory(gpa, size, attributes & KVM_MAP_GPA_RANGE_ENCRYPTED); + if (ret || !kvm_pre_fault_memory_supported) { + return ret; + } + + /* + * Opportunistically pre-fault memory in. Failures are ignored so that any + * errors in faulting in the memory will get captured in KVM page fault + * path when the guest first accesses the page. + */ + memset(&mem, 0, sizeof(mem)); + mem.gpa = gpa; + mem.size = size; + while (mem.size) { + if (kvm_vcpu_ioctl(CPU(cpu), KVM_PRE_FAULT_MEMORY, &mem)) { + break; + } + } + + return 0; } -static int kvm_handle_hypercall(struct kvm_run *run) +static int kvm_handle_hypercall(X86CPU *cpu, struct kvm_run *run) { if (run->hypercall.nr == KVM_HC_MAP_GPA_RANGE) - return kvm_handle_hc_map_gpa_range(run); + return kvm_handle_hc_map_gpa_range(cpu, run); return -EINVAL; } @@ -6137,7 +6158,7 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) break; #endif case KVM_EXIT_HYPERCALL: - ret = kvm_handle_hypercall(run); + ret = kvm_handle_hypercall(cpu, run); break; case KVM_EXIT_SYSTEM_EVENT: switch (run->system_event.type) { From 6e2d11bf04fb18e60afc8551871d9acb7b56983d Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 29 May 2025 14:45:50 +0900 Subject: [PATCH 1447/2760] futex: Check value after qemu_futex_wait() futex(2) - Linux manual page https://man7.org/linux/man-pages/man2/futex.2.html > Note that a wake-up can also be caused by common futex usage patterns > in unrelated code that happened to have previously used the futex > word's memory location (e.g., typical futex-based implementations of > Pthreads mutexes can cause this under some conditions). Therefore, > callers should always conservatively assume that a return value of 0 > can mean a spurious wake-up, and use the futex word's value (i.e., > the user-space synchronization scheme) to decide whether to continue > to block or not. Signed-off-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250529-event-v5-1-53b285203794@daynix.com Signed-off-by: Paolo Bonzini --- include/qemu/futex.h | 9 +++++++++ tests/unit/test-aio-multithread.c | 4 +++- util/qemu-thread-posix.c | 24 ++++++++++++------------ 3 files changed, 24 insertions(+), 13 deletions(-) diff --git a/include/qemu/futex.h b/include/qemu/futex.h index 91ae88966e..f577740053 100644 --- a/include/qemu/futex.h +++ b/include/qemu/futex.h @@ -24,6 +24,15 @@ static inline void qemu_futex_wake(void *f, int n) qemu_futex(f, FUTEX_WAKE, n, NULL, NULL, 0); } +/* + * Note that a wake-up can also be caused by common futex usage patterns in + * unrelated code that happened to have previously used the futex word's + * memory location (e.g., typical futex-based implementations of Pthreads + * mutexes can cause this under some conditions). Therefore, callers should + * always conservatively assume that it is a spurious wake-up, and use the futex + * word's value (i.e., the user-space synchronization scheme) to decide whether + * to continue to block or not. + */ static inline void qemu_futex_wait(void *f, unsigned val) { while (qemu_futex(f, FUTEX_WAIT, (int) val, NULL, NULL, 0)) { diff --git a/tests/unit/test-aio-multithread.c b/tests/unit/test-aio-multithread.c index 08d4570ccb..8c2e41545a 100644 --- a/tests/unit/test-aio-multithread.c +++ b/tests/unit/test-aio-multithread.c @@ -305,7 +305,9 @@ static void mcs_mutex_lock(void) prev = qatomic_xchg(&mutex_head, id); if (prev != -1) { qatomic_set(&nodes[prev].next, id); - qemu_futex_wait(&nodes[id].locked, 1); + while (qatomic_read(&nodes[id].locked) == 1) { + qemu_futex_wait(&nodes[id].locked, 1); + } } } diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index b2e26e2120..f81fca5c11 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -428,17 +428,17 @@ void qemu_event_wait(QemuEvent *ev) assert(ev->initialized); - /* - * qemu_event_wait must synchronize with qemu_event_set even if it does - * not go down the slow path, so this load-acquire is needed that - * synchronizes with the first memory barrier in qemu_event_set(). - * - * If we do go down the slow path, there is no requirement at all: we - * might miss a qemu_event_set() here but ultimately the memory barrier in - * qemu_futex_wait() will ensure the check is done correctly. - */ - value = qatomic_load_acquire(&ev->value); - if (value != EV_SET) { + while (true) { + /* + * qemu_event_wait must synchronize with qemu_event_set even if it does + * not go down the slow path, so this load-acquire is needed that + * synchronizes with the first memory barrier in qemu_event_set(). + */ + value = qatomic_load_acquire(&ev->value); + if (value == EV_SET) { + break; + } + if (value == EV_FREE) { /* * Leave the event reset and tell qemu_event_set that there are @@ -452,7 +452,7 @@ void qemu_event_wait(QemuEvent *ev) * like the load above. */ if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) { - return; + break; } } From 1bc2c495395bfad526b50d84f4db5687ce1d3963 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 29 May 2025 14:45:51 +0900 Subject: [PATCH 1448/2760] futex: Support Windows Windows supports futex-like APIs since Windows 8 and Windows Server 2012. Signed-off-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250529-event-v5-2-53b285203794@daynix.com Signed-off-by: Paolo Bonzini --- include/qemu/futex.h | 53 ++++++++++++++++++++++++------- meson.build | 7 ++++ tests/unit/test-aio-multithread.c | 2 +- util/lockcnt.c | 2 +- util/meson.build | 2 +- util/qemu-thread-posix.c | 4 +-- 6 files changed, 53 insertions(+), 17 deletions(-) diff --git a/include/qemu/futex.h b/include/qemu/futex.h index f577740053..607613eec8 100644 --- a/include/qemu/futex.h +++ b/include/qemu/futex.h @@ -1,5 +1,5 @@ /* - * Wrappers around Linux futex syscall + * Wrappers around Linux futex syscall and similar * * Copyright Red Hat, Inc. 2017 * @@ -11,28 +11,37 @@ * */ +/* + * Note that a wake-up can also be caused by common futex usage patterns in + * unrelated code that happened to have previously used the futex word's + * memory location (e.g., typical futex-based implementations of Pthreads + * mutexes can cause this under some conditions). Therefore, qemu_futex_wait() + * callers should always conservatively assume that it is a spurious wake-up, + * and use the futex word's value (i.e., the user-space synchronization scheme) + * to decide whether to continue to block or not. + */ + #ifndef QEMU_FUTEX_H #define QEMU_FUTEX_H +#define HAVE_FUTEX + +#ifdef CONFIG_LINUX #include #include #define qemu_futex(...) syscall(__NR_futex, __VA_ARGS__) -static inline void qemu_futex_wake(void *f, int n) +static inline void qemu_futex_wake_all(void *f) { - qemu_futex(f, FUTEX_WAKE, n, NULL, NULL, 0); + qemu_futex(f, FUTEX_WAKE, INT_MAX, NULL, NULL, 0); +} + +static inline void qemu_futex_wake_single(void *f) +{ + qemu_futex(f, FUTEX_WAKE, 1, NULL, NULL, 0); } -/* - * Note that a wake-up can also be caused by common futex usage patterns in - * unrelated code that happened to have previously used the futex word's - * memory location (e.g., typical futex-based implementations of Pthreads - * mutexes can cause this under some conditions). Therefore, callers should - * always conservatively assume that it is a spurious wake-up, and use the futex - * word's value (i.e., the user-space synchronization scheme) to decide whether - * to continue to block or not. - */ static inline void qemu_futex_wait(void *f, unsigned val) { while (qemu_futex(f, FUTEX_WAIT, (int) val, NULL, NULL, 0)) { @@ -46,5 +55,25 @@ static inline void qemu_futex_wait(void *f, unsigned val) } } } +#elif defined(CONFIG_WIN32) +#include + +static inline void qemu_futex_wake_all(void *f) +{ + WakeByAddressAll(f); +} + +static inline void qemu_futex_wake_single(void *f) +{ + WakeByAddressSingle(f); +} + +static inline void qemu_futex_wait(void *f, unsigned val) +{ + WaitOnAddress(f, &val, sizeof(val), INFINITE); +} +#else +#undef HAVE_FUTEX +#endif #endif /* QEMU_FUTEX_H */ diff --git a/meson.build b/meson.build index 967a10e80b..34729c2a3d 100644 --- a/meson.build +++ b/meson.build @@ -838,11 +838,18 @@ emulator_link_args = [] midl = not_found widl = not_found pathcch = not_found +synchronization = not_found host_dsosuf = '.so' if host_os == 'windows' midl = find_program('midl', required: false) widl = find_program('widl', required: false) pathcch = cc.find_library('pathcch') + synchronization = cc.find_library('Synchronization', required: false) + if not synchronization.found() + # The library name is lowercase on mingw + synchronization = cc.find_library('synchronization', required: true) + endif + socket = cc.find_library('ws2_32') winmm = cc.find_library('winmm') diff --git a/tests/unit/test-aio-multithread.c b/tests/unit/test-aio-multithread.c index 8c2e41545a..0ead6bf34a 100644 --- a/tests/unit/test-aio-multithread.c +++ b/tests/unit/test-aio-multithread.c @@ -330,7 +330,7 @@ static void mcs_mutex_unlock(void) /* Wake up the next in line. */ next = qatomic_read(&nodes[id].next); nodes[next].locked = 0; - qemu_futex_wake(&nodes[next].locked, 1); + qemu_futex_wake_single(&nodes[next].locked); } static void test_multi_fair_mutex_entry(void *opaque) diff --git a/util/lockcnt.c b/util/lockcnt.c index d07c6cc5ce..ca27d8e61a 100644 --- a/util/lockcnt.c +++ b/util/lockcnt.c @@ -106,7 +106,7 @@ static bool qemu_lockcnt_cmpxchg_or_wait(QemuLockCnt *lockcnt, int *val, static void lockcnt_wake(QemuLockCnt *lockcnt) { trace_lockcnt_futex_wake(lockcnt); - qemu_futex_wake(&lockcnt->count, 1); + qemu_futex_wake_single(&lockcnt->count); } void qemu_lockcnt_inc(QemuLockCnt *lockcnt) diff --git a/util/meson.build b/util/meson.build index 1adff96ebd..5735f65f19 100644 --- a/util/meson.build +++ b/util/meson.build @@ -27,7 +27,7 @@ else util_ss.add(files('event_notifier-win32.c')) util_ss.add(files('oslib-win32.c')) util_ss.add(files('qemu-thread-win32.c')) - util_ss.add(winmm, pathcch) + util_ss.add(winmm, pathcch, synchronization) endif util_ss.add(when: linux_io_uring, if_true: files('fdmon-io_uring.c')) if glib_has_gslice diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index f81fca5c11..f80f564e76 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -345,7 +345,7 @@ static inline void qemu_futex_wait(QemuEvent *ev, unsigned val) /* Valid transitions: * - free->set, when setting the event - * - busy->set, when setting the event, followed by qemu_futex_wake + * - busy->set, when setting the event, followed by qemu_futex_wake_all * - set->free, when resetting the event * - free->busy, when waiting * @@ -400,7 +400,7 @@ void qemu_event_set(QemuEvent *ev) smp_mb__after_rmw(); if (old == EV_BUSY) { /* There were waiters, wake them up. */ - qemu_futex_wake(ev, INT_MAX); + qemu_futex_wake_all(ev); } } } From 32da70a88780a167affde071fb2410ca2da58eaa Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 26 May 2025 14:29:14 +0900 Subject: [PATCH 1449/2760] qemu-thread: Replace __linux__ with CONFIG_LINUX MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit scripts/checkpatch.pl warns for __linux__ saying "architecture specific defines should be avoided". Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250526-event-v4-4-5b784cc8e1de@daynix.com Signed-off-by: Paolo Bonzini --- include/qemu/thread-posix.h | 2 +- util/qemu-thread-posix.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index 5f2f3d1386..c412623a91 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -33,7 +33,7 @@ struct QemuSemaphore { }; struct QemuEvent { -#ifndef __linux__ +#ifndef CONFIG_LINUX pthread_mutex_t lock; pthread_cond_t cond; #endif diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index f80f564e76..3dc4d30052 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -317,7 +317,7 @@ void qemu_sem_wait(QemuSemaphore *sem) qemu_mutex_unlock(&sem->mutex); } -#ifdef __linux__ +#ifdef CONFIG_LINUX #include "qemu/futex.h" #else static inline void qemu_futex_wake(QemuEvent *ev, int n) @@ -363,7 +363,7 @@ static inline void qemu_futex_wait(QemuEvent *ev, unsigned val) void qemu_event_init(QemuEvent *ev, bool init) { -#ifndef __linux__ +#ifndef CONFIG_LINUX pthread_mutex_init(&ev->lock, NULL); pthread_cond_init(&ev->cond, NULL); #endif @@ -376,7 +376,7 @@ void qemu_event_destroy(QemuEvent *ev) { assert(ev->initialized); ev->initialized = false; -#ifndef __linux__ +#ifndef CONFIG_LINUX pthread_mutex_destroy(&ev->lock); pthread_cond_destroy(&ev->cond); #endif From d1895f4c17fdeee35a9b86099bb64d4ed3333658 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 26 May 2025 14:29:13 +0900 Subject: [PATCH 1450/2760] qemu-thread: Avoid futex abstraction for non-Linux qemu-thread used to abstract pthread primitives into futex for the QemuEvent implementation of POSIX systems other than Linux. However, this abstraction has one key difference: unlike futex, pthread primitives require an explicit destruction, and it must be ordered after wait and wake operations. It would be easier to perform destruction if a wait operation ensures the corresponding wake operation finishes as POSIX semaphore does, but that requires to protect state accesses in qemu_event_set() and qemu_event_wait() with a mutex. On the other hand, real futex does not need such a protection but needs complex barrier and atomic operations to ensure ordering between the two functions. Add special implementations of qemu_event_set() and qemu_event_wait() using pthread primitives. qemu_event_wait() will ensure qemu_event_set() finishes, and these functions will avoid complex barrier and atomic operations to ensure ordering between them. Signed-off-by: Akihiko Odaki Tested-by: Phil Dennis-Jordan Reviewed-by: Phil Dennis-Jordan Link: https://lore.kernel.org/r/20250526-event-v4-5-5b784cc8e1de@daynix.com Signed-off-by: Paolo Bonzini --- util/qemu-thread-posix.c | 84 +++++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 31 deletions(-) diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index 3dc4d30052..7fafbedbc4 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -319,38 +319,23 @@ void qemu_sem_wait(QemuSemaphore *sem) #ifdef CONFIG_LINUX #include "qemu/futex.h" -#else -static inline void qemu_futex_wake(QemuEvent *ev, int n) -{ - assert(ev->initialized); - pthread_mutex_lock(&ev->lock); - if (n == 1) { - pthread_cond_signal(&ev->cond); - } else { - pthread_cond_broadcast(&ev->cond); - } - pthread_mutex_unlock(&ev->lock); -} - -static inline void qemu_futex_wait(QemuEvent *ev, unsigned val) -{ - assert(ev->initialized); - pthread_mutex_lock(&ev->lock); - if (ev->value == val) { - pthread_cond_wait(&ev->cond, &ev->lock); - } - pthread_mutex_unlock(&ev->lock); -} #endif /* Valid transitions: - * - free->set, when setting the event - * - busy->set, when setting the event, followed by qemu_futex_wake_all - * - set->free, when resetting the event - * - free->busy, when waiting + * - FREE -> SET (qemu_event_set) + * - BUSY -> SET (qemu_event_set) + * - SET -> FREE (qemu_event_reset) + * - FREE -> BUSY (qemu_event_wait) + * + * With futex, the waking and blocking operations follow + * BUSY -> SET and FREE -> BUSY, respectively. * - * set->busy does not happen (it can be observed from the outside but - * it really is set->free->busy). + * Without futex, BUSY -> SET and FREE -> BUSY never happen. Instead, the waking + * operation follows FREE -> SET and the blocking operation will happen in + * qemu_event_wait() if the event is not SET. + * + * SET->BUSY does not happen (it can be observed from the outside but + * it really is SET->FREE->BUSY). * * busy->free provably cannot happen; to enforce it, the set->free transition * is done with an OR, which becomes a no-op if the event has concurrently @@ -386,6 +371,7 @@ void qemu_event_set(QemuEvent *ev) { assert(ev->initialized); +#ifdef CONFIG_LINUX /* * Pairs with both qemu_event_reset() and qemu_event_wait(). * @@ -403,12 +389,20 @@ void qemu_event_set(QemuEvent *ev) qemu_futex_wake_all(ev); } } +#else + pthread_mutex_lock(&ev->lock); + /* Pairs with qemu_event_reset()'s load acquire. */ + qatomic_store_release(&ev->value, EV_SET); + pthread_cond_broadcast(&ev->cond); + pthread_mutex_unlock(&ev->lock); +#endif } void qemu_event_reset(QemuEvent *ev) { assert(ev->initialized); +#ifdef CONFIG_LINUX /* * If there was a concurrent reset (or even reset+wait), * do nothing. Otherwise change EV_SET->EV_FREE. @@ -420,21 +414,42 @@ void qemu_event_reset(QemuEvent *ev) * Pairs with the first memory barrier in qemu_event_set(). */ smp_mb__after_rmw(); +#else + /* + * If futexes are not available, there are no EV_FREE->EV_BUSY + * transitions because wakeups are done entirely through the + * condition variable. Since qatomic_set() only writes EV_FREE, + * the load seems useless but in reality, the acquire synchronizes + * with qemu_event_set()'s store release: if qemu_event_reset() + * sees EV_SET here, then the caller will certainly see a + * successful condition and skip qemu_event_wait(): + * + * done = 1; if (done == 0) + * qemu_event_set() { qemu_event_reset() { + * lock(); + * ev->value = EV_SET -----> load ev->value + * ev->value = old value | EV_FREE + * cond_broadcast() + * unlock(); } + * } if (done == 0) + * // qemu_event_wait() not called + */ + qatomic_set(&ev->value, qatomic_load_acquire(&ev->value) | EV_FREE); +#endif } void qemu_event_wait(QemuEvent *ev) { - unsigned value; - assert(ev->initialized); +#ifdef CONFIG_LINUX while (true) { /* * qemu_event_wait must synchronize with qemu_event_set even if it does * not go down the slow path, so this load-acquire is needed that * synchronizes with the first memory barrier in qemu_event_set(). */ - value = qatomic_load_acquire(&ev->value); + unsigned value = qatomic_load_acquire(&ev->value); if (value == EV_SET) { break; } @@ -463,6 +478,13 @@ void qemu_event_wait(QemuEvent *ev) */ qemu_futex_wait(ev, EV_BUSY); } +#else + pthread_mutex_lock(&ev->lock); + while (qatomic_read(&ev->value) != EV_SET) { + pthread_cond_wait(&ev->cond, &ev->lock); + } + pthread_mutex_unlock(&ev->lock); +#endif } static __thread NotifierList thread_exit; From 69e10db83ea69cae74b59d27d87cfc61d9dd2b51 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 26 May 2025 14:29:13 +0900 Subject: [PATCH 1451/2760] qemu-thread: Use futex for QemuEvent on Windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the futex-based implementation of QemuEvent on Windows to remove code duplication and remove the overhead of event object construction and destruction. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250526-event-v4-6-5b784cc8e1de@daynix.com Signed-off-by: Paolo Bonzini --- include/qemu/thread-posix.h | 9 -- include/qemu/thread-win32.h | 6 -- include/qemu/thread.h | 11 ++- util/event.c | 171 ++++++++++++++++++++++++++++++++++++ util/meson.build | 1 + util/qemu-thread-posix.c | 170 ----------------------------------- util/qemu-thread-win32.c | 129 --------------------------- 7 files changed, 182 insertions(+), 315 deletions(-) create mode 100644 util/event.c diff --git a/include/qemu/thread-posix.h b/include/qemu/thread-posix.h index c412623a91..758808b705 100644 --- a/include/qemu/thread-posix.h +++ b/include/qemu/thread-posix.h @@ -32,15 +32,6 @@ struct QemuSemaphore { unsigned int count; }; -struct QemuEvent { -#ifndef CONFIG_LINUX - pthread_mutex_t lock; - pthread_cond_t cond; -#endif - unsigned value; - bool initialized; -}; - struct QemuThread { pthread_t thread; }; diff --git a/include/qemu/thread-win32.h b/include/qemu/thread-win32.h index d95af4498f..da9e732299 100644 --- a/include/qemu/thread-win32.h +++ b/include/qemu/thread-win32.h @@ -28,12 +28,6 @@ struct QemuSemaphore { bool initialized; }; -struct QemuEvent { - int value; - HANDLE event; - bool initialized; -}; - typedef struct QemuThreadData QemuThreadData; struct QemuThread { QemuThreadData *data; diff --git a/include/qemu/thread.h b/include/qemu/thread.h index 6f800aad31..573f8c9ede 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -3,13 +3,22 @@ #include "qemu/processor.h" #include "qemu/atomic.h" +#include "qemu/futex.h" typedef struct QemuCond QemuCond; typedef struct QemuSemaphore QemuSemaphore; -typedef struct QemuEvent QemuEvent; typedef struct QemuLockCnt QemuLockCnt; typedef struct QemuThread QemuThread; +typedef struct QemuEvent { +#ifndef HAVE_FUTEX + pthread_mutex_t lock; + pthread_cond_t cond; +#endif + unsigned value; + bool initialized; +} QemuEvent; + #ifdef _WIN32 #include "qemu/thread-win32.h" #else diff --git a/util/event.c b/util/event.c new file mode 100644 index 0000000000..5a8141cd0e --- /dev/null +++ b/util/event.c @@ -0,0 +1,171 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include "qemu/osdep.h" +#include "qemu/thread.h" + +/* + * Valid transitions: + * - FREE -> SET (qemu_event_set) + * - BUSY -> SET (qemu_event_set) + * - SET -> FREE (qemu_event_reset) + * - FREE -> BUSY (qemu_event_wait) + * + * With futex, the waking and blocking operations follow + * BUSY -> SET and FREE -> BUSY, respectively. + * + * Without futex, BUSY -> SET and FREE -> BUSY never happen. Instead, the waking + * operation follows FREE -> SET and the blocking operation will happen in + * qemu_event_wait() if the event is not SET. + * + * SET->BUSY does not happen (it can be observed from the outside but + * it really is SET->FREE->BUSY). + * + * busy->free provably cannot happen; to enforce it, the set->free transition + * is done with an OR, which becomes a no-op if the event has concurrently + * transitioned to free or busy. + */ + +#define EV_SET 0 +#define EV_FREE 1 +#define EV_BUSY -1 + +void qemu_event_init(QemuEvent *ev, bool init) +{ +#ifndef HAVE_FUTEX + pthread_mutex_init(&ev->lock, NULL); + pthread_cond_init(&ev->cond, NULL); +#endif + + ev->value = (init ? EV_SET : EV_FREE); + ev->initialized = true; +} + +void qemu_event_destroy(QemuEvent *ev) +{ + assert(ev->initialized); + ev->initialized = false; +#ifndef HAVE_FUTEX + pthread_mutex_destroy(&ev->lock); + pthread_cond_destroy(&ev->cond); +#endif +} + +void qemu_event_set(QemuEvent *ev) +{ + assert(ev->initialized); + +#ifdef HAVE_FUTEX + /* + * Pairs with both qemu_event_reset() and qemu_event_wait(). + * + * qemu_event_set has release semantics, but because it *loads* + * ev->value we need a full memory barrier here. + */ + smp_mb(); + if (qatomic_read(&ev->value) != EV_SET) { + int old = qatomic_xchg(&ev->value, EV_SET); + + /* Pairs with memory barrier in kernel futex_wait system call. */ + smp_mb__after_rmw(); + if (old == EV_BUSY) { + /* There were waiters, wake them up. */ + qemu_futex_wake_all(ev); + } + } +#else + pthread_mutex_lock(&ev->lock); + /* Pairs with qemu_event_reset()'s load acquire. */ + qatomic_store_release(&ev->value, EV_SET); + pthread_cond_broadcast(&ev->cond); + pthread_mutex_unlock(&ev->lock); +#endif +} + +void qemu_event_reset(QemuEvent *ev) +{ + assert(ev->initialized); + +#ifdef HAVE_FUTEX + /* + * If there was a concurrent reset (or even reset+wait), + * do nothing. Otherwise change EV_SET->EV_FREE. + */ + qatomic_or(&ev->value, EV_FREE); + + /* + * Order reset before checking the condition in the caller. + * Pairs with the first memory barrier in qemu_event_set(). + */ + smp_mb__after_rmw(); +#else + /* + * If futexes are not available, there are no EV_FREE->EV_BUSY + * transitions because wakeups are done entirely through the + * condition variable. Since qatomic_set() only writes EV_FREE, + * the load seems useless but in reality, the acquire synchronizes + * with qemu_event_set()'s store release: if qemu_event_reset() + * sees EV_SET here, then the caller will certainly see a + * successful condition and skip qemu_event_wait(): + * + * done = 1; if (done == 0) + * qemu_event_set() { qemu_event_reset() { + * lock(); + * ev->value = EV_SET -----> load ev->value + * ev->value = old value | EV_FREE + * cond_broadcast() + * unlock(); } + * } if (done == 0) + * // qemu_event_wait() not called + */ + qatomic_set(&ev->value, qatomic_load_acquire(&ev->value) | EV_FREE); +#endif +} + +void qemu_event_wait(QemuEvent *ev) +{ + assert(ev->initialized); + +#ifdef HAVE_FUTEX + while (true) { + /* + * qemu_event_wait must synchronize with qemu_event_set even if it does + * not go down the slow path, so this load-acquire is needed that + * synchronizes with the first memory barrier in qemu_event_set(). + */ + unsigned value = qatomic_load_acquire(&ev->value); + if (value == EV_SET) { + break; + } + + if (value == EV_FREE) { + /* + * Leave the event reset and tell qemu_event_set that there are + * waiters. No need to retry, because there cannot be a concurrent + * busy->free transition. After the CAS, the event will be either + * set or busy. + * + * This cmpxchg doesn't have particular ordering requirements if it + * succeeds (moving the store earlier can only cause + * qemu_event_set() to issue _more_ wakeups), the failing case needs + * acquire semantics like the load above. + */ + if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) { + break; + } + } + + /* + * This is the final check for a concurrent set, so it does need + * a smp_mb() pairing with the second barrier of qemu_event_set(). + * The barrier is inside the FUTEX_WAIT system call. + */ + qemu_futex_wait(ev, EV_BUSY); + } +#else + pthread_mutex_lock(&ev->lock); + while (qatomic_read(&ev->value) != EV_SET) { + pthread_cond_wait(&ev->cond, &ev->lock); + } + pthread_mutex_unlock(&ev->lock); +#endif +} diff --git a/util/meson.build b/util/meson.build index 5735f65f19..35029380a3 100644 --- a/util/meson.build +++ b/util/meson.build @@ -35,6 +35,7 @@ if glib_has_gslice endif util_ss.add(files('defer-call.c')) util_ss.add(files('envlist.c', 'path.c', 'module.c')) +util_ss.add(files('event.c')) util_ss.add(files('host-utils.c')) util_ss.add(files('bitmap.c', 'bitops.c')) util_ss.add(files('fifo8.c')) diff --git a/util/qemu-thread-posix.c b/util/qemu-thread-posix.c index 7fafbedbc4..ba725444ba 100644 --- a/util/qemu-thread-posix.c +++ b/util/qemu-thread-posix.c @@ -317,176 +317,6 @@ void qemu_sem_wait(QemuSemaphore *sem) qemu_mutex_unlock(&sem->mutex); } -#ifdef CONFIG_LINUX -#include "qemu/futex.h" -#endif - -/* Valid transitions: - * - FREE -> SET (qemu_event_set) - * - BUSY -> SET (qemu_event_set) - * - SET -> FREE (qemu_event_reset) - * - FREE -> BUSY (qemu_event_wait) - * - * With futex, the waking and blocking operations follow - * BUSY -> SET and FREE -> BUSY, respectively. - * - * Without futex, BUSY -> SET and FREE -> BUSY never happen. Instead, the waking - * operation follows FREE -> SET and the blocking operation will happen in - * qemu_event_wait() if the event is not SET. - * - * SET->BUSY does not happen (it can be observed from the outside but - * it really is SET->FREE->BUSY). - * - * busy->free provably cannot happen; to enforce it, the set->free transition - * is done with an OR, which becomes a no-op if the event has concurrently - * transitioned to free or busy. - */ - -#define EV_SET 0 -#define EV_FREE 1 -#define EV_BUSY -1 - -void qemu_event_init(QemuEvent *ev, bool init) -{ -#ifndef CONFIG_LINUX - pthread_mutex_init(&ev->lock, NULL); - pthread_cond_init(&ev->cond, NULL); -#endif - - ev->value = (init ? EV_SET : EV_FREE); - ev->initialized = true; -} - -void qemu_event_destroy(QemuEvent *ev) -{ - assert(ev->initialized); - ev->initialized = false; -#ifndef CONFIG_LINUX - pthread_mutex_destroy(&ev->lock); - pthread_cond_destroy(&ev->cond); -#endif -} - -void qemu_event_set(QemuEvent *ev) -{ - assert(ev->initialized); - -#ifdef CONFIG_LINUX - /* - * Pairs with both qemu_event_reset() and qemu_event_wait(). - * - * qemu_event_set has release semantics, but because it *loads* - * ev->value we need a full memory barrier here. - */ - smp_mb(); - if (qatomic_read(&ev->value) != EV_SET) { - int old = qatomic_xchg(&ev->value, EV_SET); - - /* Pairs with memory barrier in kernel futex_wait system call. */ - smp_mb__after_rmw(); - if (old == EV_BUSY) { - /* There were waiters, wake them up. */ - qemu_futex_wake_all(ev); - } - } -#else - pthread_mutex_lock(&ev->lock); - /* Pairs with qemu_event_reset()'s load acquire. */ - qatomic_store_release(&ev->value, EV_SET); - pthread_cond_broadcast(&ev->cond); - pthread_mutex_unlock(&ev->lock); -#endif -} - -void qemu_event_reset(QemuEvent *ev) -{ - assert(ev->initialized); - -#ifdef CONFIG_LINUX - /* - * If there was a concurrent reset (or even reset+wait), - * do nothing. Otherwise change EV_SET->EV_FREE. - */ - qatomic_or(&ev->value, EV_FREE); - - /* - * Order reset before checking the condition in the caller. - * Pairs with the first memory barrier in qemu_event_set(). - */ - smp_mb__after_rmw(); -#else - /* - * If futexes are not available, there are no EV_FREE->EV_BUSY - * transitions because wakeups are done entirely through the - * condition variable. Since qatomic_set() only writes EV_FREE, - * the load seems useless but in reality, the acquire synchronizes - * with qemu_event_set()'s store release: if qemu_event_reset() - * sees EV_SET here, then the caller will certainly see a - * successful condition and skip qemu_event_wait(): - * - * done = 1; if (done == 0) - * qemu_event_set() { qemu_event_reset() { - * lock(); - * ev->value = EV_SET -----> load ev->value - * ev->value = old value | EV_FREE - * cond_broadcast() - * unlock(); } - * } if (done == 0) - * // qemu_event_wait() not called - */ - qatomic_set(&ev->value, qatomic_load_acquire(&ev->value) | EV_FREE); -#endif -} - -void qemu_event_wait(QemuEvent *ev) -{ - assert(ev->initialized); - -#ifdef CONFIG_LINUX - while (true) { - /* - * qemu_event_wait must synchronize with qemu_event_set even if it does - * not go down the slow path, so this load-acquire is needed that - * synchronizes with the first memory barrier in qemu_event_set(). - */ - unsigned value = qatomic_load_acquire(&ev->value); - if (value == EV_SET) { - break; - } - - if (value == EV_FREE) { - /* - * Leave the event reset and tell qemu_event_set that there are - * waiters. No need to retry, because there cannot be a concurrent - * busy->free transition. After the CAS, the event will be either - * set or busy. - * - * This cmpxchg doesn't have particular ordering requirements if it - * succeeds (moving the store earlier can only cause qemu_event_set() - * to issue _more_ wakeups), the failing case needs acquire semantics - * like the load above. - */ - if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) { - break; - } - } - - /* - * This is the final check for a concurrent set, so it does need - * a smp_mb() pairing with the second barrier of qemu_event_set(). - * The barrier is inside the FUTEX_WAIT system call. - */ - qemu_futex_wait(ev, EV_BUSY); - } -#else - pthread_mutex_lock(&ev->lock); - while (qatomic_read(&ev->value) != EV_SET) { - pthread_cond_wait(&ev->cond, &ev->lock); - } - pthread_mutex_unlock(&ev->lock); -#endif -} - static __thread NotifierList thread_exit; /* diff --git a/util/qemu-thread-win32.c b/util/qemu-thread-win32.c index a7fe3cc345..ca2e0b512e 100644 --- a/util/qemu-thread-win32.c +++ b/util/qemu-thread-win32.c @@ -231,135 +231,6 @@ void qemu_sem_wait(QemuSemaphore *sem) } } -/* Wrap a Win32 manual-reset event with a fast userspace path. The idea - * is to reset the Win32 event lazily, as part of a test-reset-test-wait - * sequence. Such a sequence is, indeed, how QemuEvents are used by - * RCU and other subsystems! - * - * Valid transitions: - * - free->set, when setting the event - * - busy->set, when setting the event, followed by SetEvent - * - set->free, when resetting the event - * - free->busy, when waiting - * - * set->busy does not happen (it can be observed from the outside but - * it really is set->free->busy). - * - * busy->free provably cannot happen; to enforce it, the set->free transition - * is done with an OR, which becomes a no-op if the event has concurrently - * transitioned to free or busy (and is faster than cmpxchg). - */ - -#define EV_SET 0 -#define EV_FREE 1 -#define EV_BUSY -1 - -void qemu_event_init(QemuEvent *ev, bool init) -{ - /* Manual reset. */ - ev->event = CreateEvent(NULL, TRUE, TRUE, NULL); - ev->value = (init ? EV_SET : EV_FREE); - ev->initialized = true; -} - -void qemu_event_destroy(QemuEvent *ev) -{ - assert(ev->initialized); - ev->initialized = false; - CloseHandle(ev->event); -} - -void qemu_event_set(QemuEvent *ev) -{ - assert(ev->initialized); - - /* - * Pairs with both qemu_event_reset() and qemu_event_wait(). - * - * qemu_event_set has release semantics, but because it *loads* - * ev->value we need a full memory barrier here. - */ - smp_mb(); - if (qatomic_read(&ev->value) != EV_SET) { - int old = qatomic_xchg(&ev->value, EV_SET); - - /* Pairs with memory barrier after ResetEvent. */ - smp_mb__after_rmw(); - if (old == EV_BUSY) { - /* There were waiters, wake them up. */ - SetEvent(ev->event); - } - } -} - -void qemu_event_reset(QemuEvent *ev) -{ - assert(ev->initialized); - - /* - * If there was a concurrent reset (or even reset+wait), - * do nothing. Otherwise change EV_SET->EV_FREE. - */ - qatomic_or(&ev->value, EV_FREE); - - /* - * Order reset before checking the condition in the caller. - * Pairs with the first memory barrier in qemu_event_set(). - */ - smp_mb__after_rmw(); -} - -void qemu_event_wait(QemuEvent *ev) -{ - unsigned value; - - assert(ev->initialized); - - /* - * qemu_event_wait must synchronize with qemu_event_set even if it does - * not go down the slow path, so this load-acquire is needed that - * synchronizes with the first memory barrier in qemu_event_set(). - * - * If we do go down the slow path, there is no requirement at all: we - * might miss a qemu_event_set() here but ultimately the memory barrier in - * qemu_futex_wait() will ensure the check is done correctly. - */ - value = qatomic_load_acquire(&ev->value); - if (value != EV_SET) { - if (value == EV_FREE) { - /* - * Here the underlying kernel event is reset, but qemu_event_set is - * not yet going to call SetEvent. However, there will be another - * check for EV_SET below when setting EV_BUSY. At that point it - * is safe to call WaitForSingleObject. - */ - ResetEvent(ev->event); - - /* - * It is not clear whether ResetEvent provides this barrier; kernel - * APIs (KeResetEvent/KeClearEvent) do not. Better safe than sorry! - */ - smp_mb(); - - /* - * Leave the event reset and tell qemu_event_set that there are - * waiters. No need to retry, because there cannot be a concurrent - * busy->free transition. After the CAS, the event will be either - * set or busy. - */ - if (qatomic_cmpxchg(&ev->value, EV_FREE, EV_BUSY) == EV_SET) { - return; - } - } - - /* - * ev->value is now EV_BUSY. Since we didn't observe EV_SET, - * qemu_event_set() must observe EV_BUSY and call SetEvent(). - */ - WaitForSingleObject(ev->event, INFINITE); - } -} - struct QemuThreadData { /* Passed to win32_start_routine. */ void *(*start_routine)(void *); From 0a765ca850125e0202c366cb4aab032ac9670870 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 29 May 2025 14:45:55 +0900 Subject: [PATCH 1452/2760] qemu-thread: Use futex if available for QemuLockCnt This unlocks the futex-based implementation of QemuLockCnt to Windows. Signed-off-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250529-event-v5-6-53b285203794@daynix.com Signed-off-by: Paolo Bonzini --- include/qemu/lockcnt.h | 2 +- util/lockcnt.c | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/include/qemu/lockcnt.h b/include/qemu/lockcnt.h index f4b62a3f70..5a2800e3f1 100644 --- a/include/qemu/lockcnt.h +++ b/include/qemu/lockcnt.h @@ -17,7 +17,7 @@ typedef struct QemuLockCnt QemuLockCnt; struct QemuLockCnt { -#ifndef CONFIG_LINUX +#ifndef HAVE_FUTEX QemuMutex mutex; #endif unsigned count; diff --git a/util/lockcnt.c b/util/lockcnt.c index ca27d8e61a..92c9f8ceca 100644 --- a/util/lockcnt.c +++ b/util/lockcnt.c @@ -12,10 +12,11 @@ #include "qemu/atomic.h" #include "trace.h" -#ifdef CONFIG_LINUX -#include "qemu/futex.h" +#ifdef HAVE_FUTEX -/* On Linux, bits 0-1 are a futex-based lock, bits 2-31 are the counter. +/* + * When futex is available, bits 0-1 are a futex-based lock, bits 2-31 are the + * counter. * For the mutex algorithm see Ulrich Drepper's "Futexes Are Tricky" (ok, * this is not the most relaxing citation I could make...). It is similar * to mutex2 in the paper. From 5e2312f75adcba8cfee343a03343bf298c5bc937 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 29 May 2025 14:46:01 +0900 Subject: [PATCH 1453/2760] qemu-thread: Document QemuEvent Document QemuEvent to help choose an appropriate synchronization primitive. Signed-off-by: Akihiko Odaki Link: https://lore.kernel.org/r/20250529-event-v5-12-53b285203794@daynix.com Signed-off-by: Paolo Bonzini --- include/qemu/thread.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/qemu/thread.h b/include/qemu/thread.h index 573f8c9ede..f0302ed01f 100644 --- a/include/qemu/thread.h +++ b/include/qemu/thread.h @@ -10,6 +10,16 @@ typedef struct QemuSemaphore QemuSemaphore; typedef struct QemuLockCnt QemuLockCnt; typedef struct QemuThread QemuThread; +/* + * QemuEvent + * ========= + * + * QemuEvent is an implementation of Win32 manual-reset event object. + * For details, refer to: + * https://learn.microsoft.com/en-us/windows/win32/sync/using-event-objects + * + * QemuEvent is more lightweight than QemuSemaphore when HAVE_FUTEX is defined. + */ typedef struct QemuEvent { #ifndef HAVE_FUTEX pthread_mutex_t lock; From 952691b7a6c93f4f5b9ae9bb178021511374967a Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 29 May 2025 14:45:56 +0900 Subject: [PATCH 1454/2760] migration: Replace QemuSemaphore with QemuEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pause_event can utilize qemu_event_reset() to discard events. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250529-event-v5-7-53b285203794@daynix.com Signed-off-by: Paolo Bonzini --- migration/migration.c | 21 +++++++++------------ migration/migration.h | 2 +- 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 4697732bef..4098870bce 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -1630,7 +1630,7 @@ void migration_cancel(void) } /* If the migration is paused, kick it out of the pause */ if (old_state == MIGRATION_STATUS_PRE_SWITCHOVER) { - qemu_sem_post(&s->pause_sem); + qemu_event_set(&s->pause_event); } migrate_set_state(&s->state, old_state, MIGRATION_STATUS_CANCELLING); } while (s->state != MIGRATION_STATUS_CANCELLING); @@ -2342,7 +2342,7 @@ void qmp_migrate_continue(MigrationStatus state, Error **errp) MigrationStatus_str(s->state)); return; } - qemu_sem_post(&s->pause_sem); + qemu_event_set(&s->pause_event); } int migration_rp_wait(MigrationState *s) @@ -2911,21 +2911,18 @@ static bool migration_switchover_prepare(MigrationState *s) return true; } - /* Since leaving this state is not atomic with posting the semaphore + /* + * Since leaving this state is not atomic with setting the event * it's possible that someone could have issued multiple migrate_continue - * and the semaphore is incorrectly positive at this point; - * the docs say it's undefined to reinit a semaphore that's already - * init'd, so use timedwait to eat up any existing posts. + * and the event is incorrectly set at this point so reset it. */ - while (qemu_sem_timedwait(&s->pause_sem, 1) == 0) { - /* This block intentionally left blank */ - } + qemu_event_reset(&s->pause_event); /* Update [POSTCOPY_]ACTIVE to PRE_SWITCHOVER */ migrate_set_state(&s->state, s->state, MIGRATION_STATUS_PRE_SWITCHOVER); bql_unlock(); - qemu_sem_wait(&s->pause_sem); + qemu_event_wait(&s->pause_event); bql_lock(); /* @@ -4057,7 +4054,7 @@ static void migration_instance_finalize(Object *obj) qemu_mutex_destroy(&ms->qemu_file_lock); qemu_sem_destroy(&ms->wait_unplug_sem); qemu_sem_destroy(&ms->rate_limit_sem); - qemu_sem_destroy(&ms->pause_sem); + qemu_event_destroy(&ms->pause_event); qemu_sem_destroy(&ms->postcopy_pause_sem); qemu_sem_destroy(&ms->rp_state.rp_sem); qemu_sem_destroy(&ms->rp_state.rp_pong_acks); @@ -4072,7 +4069,7 @@ static void migration_instance_init(Object *obj) ms->state = MIGRATION_STATUS_NONE; ms->mbps = -1; ms->pages_per_second = -1; - qemu_sem_init(&ms->pause_sem, 0); + qemu_event_init(&ms->pause_event, false); qemu_mutex_init(&ms->error_mutex); migrate_params_init(&ms->parameters); diff --git a/migration/migration.h b/migration/migration.h index d53f7cad84..21aa6a3c8f 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -379,7 +379,7 @@ struct MigrationState { QemuSemaphore wait_unplug_sem; /* Migration is paused due to pause-before-switchover */ - QemuSemaphore pause_sem; + QemuEvent pause_event; /* The semaphore is used to notify COLO thread that failover is finished */ QemuSemaphore colo_exit_sem; From da926a8b9dd67be3432c525a6db998141be444e2 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 29 May 2025 14:45:57 +0900 Subject: [PATCH 1455/2760] migration/colo: Replace QemuSemaphore with QemuEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit colo_exit_sem and colo_incoming_sem represent one-shot events so they can be converted into QemuEvent, which is more lightweight. Signed-off-by: Akihiko Odaki Reviewed-by: Fabiano Rosas Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250529-event-v5-8-53b285203794@daynix.com Signed-off-by: Paolo Bonzini --- migration/colo.c | 20 ++++++++++---------- migration/migration.h | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/migration/colo.c b/migration/colo.c index c976b3ff34..e0f713c837 100644 --- a/migration/colo.c +++ b/migration/colo.c @@ -146,7 +146,7 @@ static void secondary_vm_do_failover(void) return; } /* Notify COLO incoming thread that failover work is finished */ - qemu_sem_post(&mis->colo_incoming_sem); + qemu_event_set(&mis->colo_incoming_event); /* For Secondary VM, jump to incoming co */ if (mis->colo_incoming_co) { @@ -195,7 +195,7 @@ static void primary_vm_do_failover(void) } /* Notify COLO thread that failover work is finished */ - qemu_sem_post(&s->colo_exit_sem); + qemu_event_set(&s->colo_exit_event); } COLOMode get_colo_mode(void) @@ -620,8 +620,8 @@ static void colo_process_checkpoint(MigrationState *s) } /* Hope this not to be too long to wait here */ - qemu_sem_wait(&s->colo_exit_sem); - qemu_sem_destroy(&s->colo_exit_sem); + qemu_event_wait(&s->colo_exit_event); + qemu_event_destroy(&s->colo_exit_event); /* * It is safe to unregister notifier after failover finished. @@ -651,7 +651,7 @@ void migrate_start_colo_process(MigrationState *s) s->colo_delay_timer = timer_new_ms(QEMU_CLOCK_HOST, colo_checkpoint_notify_timer, NULL); - qemu_sem_init(&s->colo_exit_sem, 0); + qemu_event_init(&s->colo_exit_event, false); colo_process_checkpoint(s); bql_lock(); } @@ -808,11 +808,11 @@ void colo_shutdown(void) case COLO_MODE_PRIMARY: s = migrate_get_current(); qemu_event_set(&s->colo_checkpoint_event); - qemu_sem_post(&s->colo_exit_sem); + qemu_event_set(&s->colo_exit_event); break; case COLO_MODE_SECONDARY: mis = migration_incoming_get_current(); - qemu_sem_post(&mis->colo_incoming_sem); + qemu_event_set(&mis->colo_incoming_event); break; default: break; @@ -827,7 +827,7 @@ static void *colo_process_incoming_thread(void *opaque) Error *local_err = NULL; rcu_register_thread(); - qemu_sem_init(&mis->colo_incoming_sem, 0); + qemu_event_init(&mis->colo_incoming_event, false); migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_COLO); @@ -923,8 +923,8 @@ static void *colo_process_incoming_thread(void *opaque) } /* Hope this not to be too long to loop here */ - qemu_sem_wait(&mis->colo_incoming_sem); - qemu_sem_destroy(&mis->colo_incoming_sem); + qemu_event_wait(&mis->colo_incoming_event); + qemu_event_destroy(&mis->colo_incoming_event); rcu_unregister_thread(); return NULL; diff --git a/migration/migration.h b/migration/migration.h index 21aa6a3c8f..aaec471c00 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -186,7 +186,7 @@ struct MigrationIncomingState { /* The coroutine we should enter (back) after failover */ Coroutine *colo_incoming_co; - QemuSemaphore colo_incoming_sem; + QemuEvent colo_incoming_event; /* Optional load threads pool and its thread exit request flag */ ThreadPool *load_threads; @@ -381,8 +381,8 @@ struct MigrationState { /* Migration is paused due to pause-before-switchover */ QemuEvent pause_event; - /* The semaphore is used to notify COLO thread that failover is finished */ - QemuSemaphore colo_exit_sem; + /* The event is used to notify COLO thread that failover is finished */ + QemuEvent colo_exit_event; /* The event is used to notify COLO thread to do checkpoint */ QemuEvent colo_checkpoint_event; From d401b7aed83af96fa0e68f46b15446838f6f5e0f Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 29 May 2025 14:45:58 +0900 Subject: [PATCH 1456/2760] migration/postcopy: Replace QemuSemaphore with QemuEvent thread_sync_sem is an one-shot event so it can be converted into QemuEvent, which is more lightweight. Signed-off-by: Akihiko Odaki Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250529-event-v5-9-53b285203794@daynix.com Signed-off-by: Paolo Bonzini --- migration/migration.h | 4 ++-- migration/postcopy-ram.c | 10 +++++----- migration/savevm.c | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/migration/migration.h b/migration/migration.h index aaec471c00..739289de93 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -98,9 +98,9 @@ struct MigrationIncomingState { void (*transport_cleanup)(void *data); /* * Used to sync thread creations. Note that we can't create threads in - * parallel with this sem. + * parallel with this event. */ - QemuSemaphore thread_sync_sem; + QemuEvent thread_sync_event; /* * Free at the start of the main state load, set as the main thread finishes * loading state. diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 995614b38c..75fd310fb2 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -90,10 +90,10 @@ void postcopy_thread_create(MigrationIncomingState *mis, QemuThread *thread, const char *name, void *(*fn)(void *), int joinable) { - qemu_sem_init(&mis->thread_sync_sem, 0); + qemu_event_init(&mis->thread_sync_event, false); qemu_thread_create(thread, name, fn, mis, joinable); - qemu_sem_wait(&mis->thread_sync_sem); - qemu_sem_destroy(&mis->thread_sync_sem); + qemu_event_wait(&mis->thread_sync_event); + qemu_event_destroy(&mis->thread_sync_event); } /* Postcopy needs to detect accesses to pages that haven't yet been copied @@ -964,7 +964,7 @@ static void *postcopy_ram_fault_thread(void *opaque) trace_postcopy_ram_fault_thread_entry(); rcu_register_thread(); mis->last_rb = NULL; /* last RAMBlock we sent part of */ - qemu_sem_post(&mis->thread_sync_sem); + qemu_event_set(&mis->thread_sync_event); struct pollfd *pfd; size_t pfd_len = 2 + mis->postcopy_remote_fds->len; @@ -1716,7 +1716,7 @@ void *postcopy_preempt_thread(void *opaque) rcu_register_thread(); - qemu_sem_post(&mis->thread_sync_sem); + qemu_event_set(&mis->thread_sync_event); /* * The preempt channel is established in asynchronous way. Wait diff --git a/migration/savevm.c b/migration/savevm.c index 006514c3e3..52105dd2f1 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -2078,7 +2078,7 @@ static void *postcopy_ram_listen_thread(void *opaque) migrate_set_state(&mis->state, MIGRATION_STATUS_ACTIVE, MIGRATION_STATUS_POSTCOPY_ACTIVE); - qemu_sem_post(&mis->thread_sync_sem); + qemu_event_set(&mis->thread_sync_event); trace_postcopy_ram_listen_thread_start(); rcu_register_thread(); From 23aec0d0e80a31d3fc3061e5c83432007502948a Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 29 May 2025 14:45:59 +0900 Subject: [PATCH 1457/2760] hw/display/apple-gfx: Replace QemuSemaphore with QemuEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sem in AppleGFXReadMemoryJob is an one-shot event so it can be converted into QemuEvent, which is more specialized for such a use case. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250529-event-v5-10-53b285203794@daynix.com Signed-off-by: Paolo Bonzini --- hw/display/apple-gfx.m | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hw/display/apple-gfx.m b/hw/display/apple-gfx.m index 8dde1f138d..174d56ae05 100644 --- a/hw/display/apple-gfx.m +++ b/hw/display/apple-gfx.m @@ -454,7 +454,7 @@ static void set_cursor_glyph(void *opaque) /* ------ DMA (device reading system memory) ------ */ typedef struct AppleGFXReadMemoryJob { - QemuSemaphore sem; + QemuEvent event; hwaddr physical_address; uint64_t length; void *dst; @@ -470,7 +470,7 @@ static void apple_gfx_do_read_memory(void *opaque) job->dst, job->length, MEMTXATTRS_UNSPECIFIED); job->success = (r == MEMTX_OK); - qemu_sem_post(&job->sem); + qemu_event_set(&job->event); } static bool apple_gfx_read_memory(AppleGFXState *s, hwaddr physical_address, @@ -483,11 +483,11 @@ static bool apple_gfx_read_memory(AppleGFXState *s, hwaddr physical_address, trace_apple_gfx_read_memory(physical_address, length, dst); /* Performing DMA requires BQL, so do it in a BH. */ - qemu_sem_init(&job.sem, 0); + qemu_event_init(&job.event, 0); aio_bh_schedule_oneshot(qemu_get_aio_context(), apple_gfx_do_read_memory, &job); - qemu_sem_wait(&job.sem); - qemu_sem_destroy(&job.sem); + qemu_event_wait(&job.event); + qemu_event_destroy(&job.event); return job.success; } From bc40e4fe6263f2c939ac8455033bfec18c9b3a0a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 May 2025 15:51:11 +0100 Subject: [PATCH 1458/2760] target/i386: Detect flush-to-zero after rounding The Intel SDM section 10.2.3.3 on the MXCSR.FTZ bit says that we flush outputs to zero when we detect underflow, which is after rounding. Set the detect_ftz flag accordingly. This allows us to enable the test in fma.c which checks this behaviour. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250519145114.2786534-2-peter.maydell@linaro.org Signed-off-by: Paolo Bonzini --- target/i386/tcg/fpu_helper.c | 8 ++++---- tests/tcg/x86_64/fma.c | 5 ----- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 1cbadb1453..9ea67ea76c 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -189,13 +189,13 @@ void cpu_init_fp_statuses(CPUX86State *env) set_float_default_nan_pattern(0b11000000, &env->mmx_status); set_float_default_nan_pattern(0b11000000, &env->sse_status); /* - * TODO: x86 does flush-to-zero detection after rounding (the SDM + * x86 does flush-to-zero detection after rounding (the SDM * section 10.2.3.3 on the FTZ bit of MXCSR says that we flush * when we detect underflow, which x86 does after rounding). */ - set_float_ftz_detection(float_ftz_before_rounding, &env->fp_status); - set_float_ftz_detection(float_ftz_before_rounding, &env->mmx_status); - set_float_ftz_detection(float_ftz_before_rounding, &env->sse_status); + set_float_ftz_detection(float_ftz_after_rounding, &env->fp_status); + set_float_ftz_detection(float_ftz_after_rounding, &env->mmx_status); + set_float_ftz_detection(float_ftz_after_rounding, &env->sse_status); } static inline uint8_t save_exception_flags(CPUX86State *env) diff --git a/tests/tcg/x86_64/fma.c b/tests/tcg/x86_64/fma.c index 09c622ebc0..46f863005e 100644 --- a/tests/tcg/x86_64/fma.c +++ b/tests/tcg/x86_64/fma.c @@ -79,14 +79,9 @@ static testdata tests[] = { /* * Flushing of denormal outputs to zero should also happen after * rounding, so setting FTZ should not affect the result or the flags. - * QEMU currently does not emulate this correctly because we do the - * flush-to-zero check before rounding, so we incorrectly produce a - * zero result and set Underflow as well as Precision. */ -#ifdef ENABLE_FAILING_TESTS { 0x3fdfffffffffffff, 0x001fffffffffffff, 0x801fffffffffffff, true, 0x8010000000000000, 0x20 }, /* Enabling FTZ shouldn't change flags */ -#endif }; int main(void) From 397ef415cad11c91318b512ecfaa27679ea7e351 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 May 2025 15:51:12 +0100 Subject: [PATCH 1459/2760] target/i386: Use correct type for get_float_exception_flags() values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The softfloat get_float_exception_flags() function returns 'int', but in various places in target/i386 we incorrectly store the returned value into a uint8_t. This currently has no ill effects because i386 doesn't care about any of the float_flag enum values above 0x40. However, we want to start using float_flag_input_denormal_used, which is 0x4000. Switch to using 'int' so that we can handle all the possible valid float_flag_* values. This includes changing the return type of save_exception_flags() and the argument to merge_exception_flags(). Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250519145114.2786534-3-peter.maydell@linaro.org Signed-off-by: Paolo Bonzini --- target/i386/ops_sse.h | 16 +++---- target/i386/tcg/fpu_helper.c | 82 ++++++++++++++++++------------------ 2 files changed, 49 insertions(+), 49 deletions(-) diff --git a/target/i386/ops_sse.h b/target/i386/ops_sse.h index f0aa1894aa..a2e4d48039 100644 --- a/target/i386/ops_sse.h +++ b/target/i386/ops_sse.h @@ -842,7 +842,7 @@ int64_t helper_cvttsd2sq(CPUX86State *env, ZMMReg *s) void glue(helper_rsqrtps, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); int i; for (i = 0; i < 2 << SHIFT; i++) { d->ZMM_S(i) = float32_div(float32_one, @@ -855,7 +855,7 @@ void glue(helper_rsqrtps, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) #if SHIFT == 1 void helper_rsqrtss(CPUX86State *env, ZMMReg *d, ZMMReg *v, ZMMReg *s) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); int i; d->ZMM_S(0) = float32_div(float32_one, float32_sqrt(s->ZMM_S(0), &env->sse_status), @@ -869,7 +869,7 @@ void helper_rsqrtss(CPUX86State *env, ZMMReg *d, ZMMReg *v, ZMMReg *s) void glue(helper_rcpps, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); int i; for (i = 0; i < 2 << SHIFT; i++) { d->ZMM_S(i) = float32_div(float32_one, s->ZMM_S(i), &env->sse_status); @@ -880,7 +880,7 @@ void glue(helper_rcpps, SUFFIX)(CPUX86State *env, ZMMReg *d, ZMMReg *s) #if SHIFT == 1 void helper_rcpss(CPUX86State *env, ZMMReg *d, ZMMReg *v, ZMMReg *s) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); int i; d->ZMM_S(0) = float32_div(float32_one, s->ZMM_S(0), &env->sse_status); for (i = 1; i < 2 << SHIFT; i++) { @@ -1714,7 +1714,7 @@ void glue(helper_phminposuw, SUFFIX)(CPUX86State *env, Reg *d, Reg *s) void glue(helper_roundps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mode) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; int i; @@ -1738,7 +1738,7 @@ void glue(helper_roundps, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, uint32_t mode) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; int i; @@ -1763,7 +1763,7 @@ void glue(helper_roundpd, SUFFIX)(CPUX86State *env, Reg *d, Reg *s, void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, uint32_t mode) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; int i; @@ -1788,7 +1788,7 @@ void glue(helper_roundss, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, void glue(helper_roundsd, SUFFIX)(CPUX86State *env, Reg *d, Reg *v, Reg *s, uint32_t mode) { - uint8_t old_flags = get_float_exception_flags(&env->sse_status); + int old_flags = get_float_exception_flags(&env->sse_status); signed char prev_rounding_mode; int i; diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 9ea67ea76c..4732b71812 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -198,16 +198,16 @@ void cpu_init_fp_statuses(CPUX86State *env) set_float_ftz_detection(float_ftz_after_rounding, &env->sse_status); } -static inline uint8_t save_exception_flags(CPUX86State *env) +static inline int save_exception_flags(CPUX86State *env) { - uint8_t old_flags = get_float_exception_flags(&env->fp_status); + int old_flags = get_float_exception_flags(&env->fp_status); set_float_exception_flags(0, &env->fp_status); return old_flags; } -static void merge_exception_flags(CPUX86State *env, uint8_t old_flags) +static void merge_exception_flags(CPUX86State *env, int old_flags) { - uint8_t new_flags = get_float_exception_flags(&env->fp_status); + int new_flags = get_float_exception_flags(&env->fp_status); float_raise(old_flags, &env->fp_status); fpu_set_exception(env, ((new_flags & float_flag_invalid ? FPUS_IE : 0) | @@ -220,7 +220,7 @@ static void merge_exception_flags(CPUX86State *env, uint8_t old_flags) static inline floatx80 helper_fdiv(CPUX86State *env, floatx80 a, floatx80 b) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); floatx80 ret = floatx80_div(a, b, &env->fp_status); merge_exception_flags(env, old_flags); return ret; @@ -240,7 +240,7 @@ static void fpu_raise_exception(CPUX86State *env, uintptr_t retaddr) void helper_flds_FT0(CPUX86State *env, uint32_t val) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); union { float32 f; uint32_t i; @@ -253,7 +253,7 @@ void helper_flds_FT0(CPUX86State *env, uint32_t val) void helper_fldl_FT0(CPUX86State *env, uint64_t val) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); union { float64 f; uint64_t i; @@ -271,7 +271,7 @@ void helper_fildl_FT0(CPUX86State *env, int32_t val) void helper_flds_ST0(CPUX86State *env, uint32_t val) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int new_fpstt; union { float32 f; @@ -288,7 +288,7 @@ void helper_flds_ST0(CPUX86State *env, uint32_t val) void helper_fldl_ST0(CPUX86State *env, uint64_t val) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int new_fpstt; union { float64 f; @@ -338,7 +338,7 @@ void helper_fildll_ST0(CPUX86State *env, int64_t val) uint32_t helper_fsts_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); union { float32 f; uint32_t i; @@ -351,7 +351,7 @@ uint32_t helper_fsts_ST0(CPUX86State *env) uint64_t helper_fstl_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); union { float64 f; uint64_t i; @@ -364,7 +364,7 @@ uint64_t helper_fstl_ST0(CPUX86State *env) int32_t helper_fist_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int32_t val; val = floatx80_to_int32(ST0, &env->fp_status); @@ -378,7 +378,7 @@ int32_t helper_fist_ST0(CPUX86State *env) int32_t helper_fistl_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int32_t val; val = floatx80_to_int32(ST0, &env->fp_status); @@ -391,7 +391,7 @@ int32_t helper_fistl_ST0(CPUX86State *env) int64_t helper_fistll_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int64_t val; val = floatx80_to_int64(ST0, &env->fp_status); @@ -404,7 +404,7 @@ int64_t helper_fistll_ST0(CPUX86State *env) int32_t helper_fistt_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int32_t val; val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); @@ -418,7 +418,7 @@ int32_t helper_fistt_ST0(CPUX86State *env) int32_t helper_fisttl_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int32_t val; val = floatx80_to_int32_round_to_zero(ST0, &env->fp_status); @@ -431,7 +431,7 @@ int32_t helper_fisttl_ST0(CPUX86State *env) int64_t helper_fisttll_ST0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int64_t val; val = floatx80_to_int64_round_to_zero(ST0, &env->fp_status); @@ -527,7 +527,7 @@ static const int fcom_ccval[4] = {0x0100, 0x4000, 0x0000, 0x4500}; void helper_fcom_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); FloatRelation ret; ret = floatx80_compare(ST0, FT0, &env->fp_status); @@ -537,7 +537,7 @@ void helper_fcom_ST0_FT0(CPUX86State *env) void helper_fucom_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); FloatRelation ret; ret = floatx80_compare_quiet(ST0, FT0, &env->fp_status); @@ -549,7 +549,7 @@ static const int fcomi_ccval[4] = {CC_C, CC_Z, 0, CC_Z | CC_P | CC_C}; void helper_fcomi_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int eflags; FloatRelation ret; @@ -562,7 +562,7 @@ void helper_fcomi_ST0_FT0(CPUX86State *env) void helper_fucomi_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int eflags; FloatRelation ret; @@ -575,28 +575,28 @@ void helper_fucomi_ST0_FT0(CPUX86State *env) void helper_fadd_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_add(ST0, FT0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fmul_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_mul(ST0, FT0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fsub_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_sub(ST0, FT0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fsubr_ST0_FT0(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_sub(FT0, ST0, &env->fp_status); merge_exception_flags(env, old_flags); } @@ -615,28 +615,28 @@ void helper_fdivr_ST0_FT0(CPUX86State *env) void helper_fadd_STN_ST0(CPUX86State *env, int st_index) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST(st_index) = floatx80_add(ST(st_index), ST0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fmul_STN_ST0(CPUX86State *env, int st_index) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST(st_index) = floatx80_mul(ST(st_index), ST0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fsub_STN_ST0(CPUX86State *env, int st_index) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST(st_index) = floatx80_sub(ST(st_index), ST0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fsubr_STN_ST0(CPUX86State *env, int st_index) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST(st_index) = floatx80_sub(ST0, ST(st_index), &env->fp_status); merge_exception_flags(env, old_flags); } @@ -861,7 +861,7 @@ void helper_fbld_ST0(CPUX86State *env, target_ulong ptr) void helper_fbst_ST0(CPUX86State *env, target_ulong ptr) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); int v; target_ulong mem_ref, mem_end; int64_t val; @@ -1136,7 +1136,7 @@ static const struct f2xm1_data f2xm1_table[65] = { void helper_f2xm1(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t sig = extractFloatx80Frac(ST0); int32_t exp = extractFloatx80Exp(ST0); bool sign = extractFloatx80Sign(ST0); @@ -1369,7 +1369,7 @@ static const struct fpatan_data fpatan_table[9] = { void helper_fpatan(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t arg0_sig = extractFloatx80Frac(ST0); int32_t arg0_exp = extractFloatx80Exp(ST0); bool arg0_sign = extractFloatx80Sign(ST0); @@ -1808,7 +1808,7 @@ void helper_fpatan(CPUX86State *env) void helper_fxtract(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); CPU_LDoubleU temp; temp.d = ST0; @@ -1857,7 +1857,7 @@ void helper_fxtract(CPUX86State *env) static void helper_fprem_common(CPUX86State *env, bool mod) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t quotient; CPU_LDoubleU temp0, temp1; int exp0, exp1, expdiff; @@ -2053,7 +2053,7 @@ static void helper_fyl2x_common(CPUX86State *env, floatx80 arg, int32_t *exp, void helper_fyl2xp1(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t arg0_sig = extractFloatx80Frac(ST0); int32_t arg0_exp = extractFloatx80Exp(ST0); bool arg0_sign = extractFloatx80Sign(ST0); @@ -2151,7 +2151,7 @@ void helper_fyl2xp1(CPUX86State *env) void helper_fyl2x(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); uint64_t arg0_sig = extractFloatx80Frac(ST0); int32_t arg0_exp = extractFloatx80Exp(ST0); bool arg0_sign = extractFloatx80Sign(ST0); @@ -2298,7 +2298,7 @@ void helper_fyl2x(CPUX86State *env) void helper_fsqrt(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); if (floatx80_is_neg(ST0)) { env->fpus &= ~0x4700; /* (C3,C2,C1,C0) <-- 0000 */ env->fpus |= 0x400; @@ -2324,14 +2324,14 @@ void helper_fsincos(CPUX86State *env) void helper_frndint(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); ST0 = floatx80_round_to_int(ST0, &env->fp_status); merge_exception_flags(env, old_flags); } void helper_fscale(CPUX86State *env) { - uint8_t old_flags = save_exception_flags(env); + int old_flags = save_exception_flags(env); if (floatx80_invalid_encoding(ST1, &env->fp_status) || floatx80_invalid_encoding(ST0, &env->fp_status)) { float_raise(float_flag_invalid, &env->fp_status); @@ -2369,7 +2369,7 @@ void helper_fscale(CPUX86State *env) } else { int n; FloatX80RoundPrec save = env->fp_status.floatx80_rounding_precision; - uint8_t save_flags = get_float_exception_flags(&env->fp_status); + int save_flags = get_float_exception_flags(&env->fp_status); set_float_exception_flags(0, &env->fp_status); n = floatx80_to_int32_round_to_zero(ST1, &env->fp_status); set_float_exception_flags(save_flags, &env->fp_status); @@ -3269,7 +3269,7 @@ void update_mxcsr_status(CPUX86State *env) void update_mxcsr_from_sse_status(CPUX86State *env) { - uint8_t flags = get_float_exception_flags(&env->sse_status); + int flags = get_float_exception_flags(&env->sse_status); /* * The MXCSR denormal flag has opposite semantics to * float_flag_input_denormal_flushed (the softfloat code sets that flag From 57df511180ae015c4f6fac2a8260649a79e8ead9 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 May 2025 15:51:13 +0100 Subject: [PATCH 1460/2760] target/i386: Wire up MXCSR.DE and FPUS.DE correctly The x86 DE bit in the FPU and MXCSR status is supposed to be set when an input denormal is consumed. We didn't previously report this from softfloat, so the x86 code either simply didn't set the DE bit or else incorrectly wired it up to denormal_flushed, depending on which register you looked at. Now we have input_denormal_used we can wire up these DE bits with the semantics they are supposed to have. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Link: https://lore.kernel.org/r/20250519145114.2786534-4-peter.maydell@linaro.org Signed-off-by: Paolo Bonzini --- target/i386/tcg/fpu_helper.c | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/target/i386/tcg/fpu_helper.c b/target/i386/tcg/fpu_helper.c index 4732b71812..b3b23823fd 100644 --- a/target/i386/tcg/fpu_helper.c +++ b/target/i386/tcg/fpu_helper.c @@ -215,7 +215,7 @@ static void merge_exception_flags(CPUX86State *env, int old_flags) (new_flags & float_flag_overflow ? FPUS_OE : 0) | (new_flags & float_flag_underflow ? FPUS_UE : 0) | (new_flags & float_flag_inexact ? FPUS_PE : 0) | - (new_flags & float_flag_input_denormal_flushed ? FPUS_DE : 0))); + (new_flags & float_flag_input_denormal_used ? FPUS_DE : 0))); } static inline floatx80 helper_fdiv(CPUX86State *env, floatx80 a, floatx80 b) @@ -3254,6 +3254,7 @@ void update_mxcsr_status(CPUX86State *env) /* Set exception flags. */ set_float_exception_flags((mxcsr & FPUS_IE ? float_flag_invalid : 0) | + (mxcsr & FPUS_DE ? float_flag_input_denormal_used : 0) | (mxcsr & FPUS_ZE ? float_flag_divbyzero : 0) | (mxcsr & FPUS_OE ? float_flag_overflow : 0) | (mxcsr & FPUS_UE ? float_flag_underflow : 0) | @@ -3270,14 +3271,8 @@ void update_mxcsr_status(CPUX86State *env) void update_mxcsr_from_sse_status(CPUX86State *env) { int flags = get_float_exception_flags(&env->sse_status); - /* - * The MXCSR denormal flag has opposite semantics to - * float_flag_input_denormal_flushed (the softfloat code sets that flag - * only when flushing input denormals to zero, but SSE sets it - * only when not flushing them to zero), so is not converted - * here. - */ env->mxcsr |= ((flags & float_flag_invalid ? FPUS_IE : 0) | + (flags & float_flag_input_denormal_used ? FPUS_DE : 0) | (flags & float_flag_divbyzero ? FPUS_ZE : 0) | (flags & float_flag_overflow ? FPUS_OE : 0) | (flags & float_flag_underflow ? FPUS_UE : 0) | From 3f9bdfb0dc8162cbc080c868625336178ddcda56 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 19 May 2025 15:51:14 +0100 Subject: [PATCH 1461/2760] tests/tcg/x86_64/fma: add test for exact-denormal output Add some fma test cases that check for correct handling of FTZ and for the flag that indicates that the input denormal was consumed. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250519145114.2786534-5-peter.maydell@linaro.org Signed-off-by: Paolo Bonzini --- tests/tcg/x86_64/fma.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/tcg/x86_64/fma.c b/tests/tcg/x86_64/fma.c index 46f863005e..34219614c0 100644 --- a/tests/tcg/x86_64/fma.c +++ b/tests/tcg/x86_64/fma.c @@ -82,6 +82,18 @@ static testdata tests[] = { */ { 0x3fdfffffffffffff, 0x001fffffffffffff, 0x801fffffffffffff, true, 0x8010000000000000, 0x20 }, /* Enabling FTZ shouldn't change flags */ + /* + * normal * 0 + a denormal. With FTZ disabled this gives an exact + * result (equal to the input denormal) that has consumed the denormal. + */ + { 0x3cc8000000000000, 0x0000000000000000, 0x8008000000000000, false, + 0x8008000000000000, 0x2 }, /* Denormal */ + /* + * With FTZ enabled, this consumes the denormal, returns zero (because + * flushed) and indicates also Underflow and Precision. + */ + { 0x3cc8000000000000, 0x0000000000000000, 0x8008000000000000, true, + 0x8000000000000000, 0x32 }, /* Precision, Underflow, Denormal */ }; int main(void) From 8a1975e4d44b56124dcf37e972e645ba21b3722b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 3 Jun 2025 12:01:48 +0100 Subject: [PATCH 1462/2760] tests/docker: expose $HOME/.cache/qemu as docker volume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If you want to run functional tests we should share .cache/qemu so we don't force containers to continually re-download images. We also move ccache to use this shared area. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-ID: <20250603110204.838117-2-alex.bennee@linaro.org> --- tests/docker/Makefile.include | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/docker/Makefile.include b/tests/docker/Makefile.include index fa1cbb6726..3959d8a028 100644 --- a/tests/docker/Makefile.include +++ b/tests/docker/Makefile.include @@ -185,8 +185,10 @@ docker: docker-help: docker +# Where QEMU caches build artefacts +DOCKER_QEMU_CACHE_DIR := $$HOME/.cache/qemu # Use a global constant ccache directory to speed up repetitive builds -DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache +DOCKER_QEMU_CCACHE_DIR := DOCKER_QEMU_CACHE_DIR/docker-ccache # This rule if for directly running against an arbitrary docker target. # It is called by the expanded docker targets (e.g. make @@ -195,7 +197,7 @@ DOCKER_CCACHE_DIR := $$HOME/.cache/qemu-docker-ccache # For example: make docker-run TEST="test-quick" IMAGE="debian:arm64" EXECUTABLE=./aarch64-linux-user/qemu-aarch64 # docker-run: docker-qemu-src - @mkdir -p "$(DOCKER_CCACHE_DIR)" + @mkdir -p "$(DOCKER_QEMU_CCACHE_DIR)" @if test -z "$(IMAGE)" || test -z "$(TEST)"; \ then echo "Invalid target $(IMAGE)/$(TEST)"; exit 1; \ fi @@ -222,8 +224,8 @@ docker-run: docker-qemu-src -e V=$V -e J=$J -e DEBUG=$(DEBUG) \ -e SHOW_ENV=$(SHOW_ENV) \ $(if $(NOUSER),, \ - -e CCACHE_DIR=/var/tmp/ccache \ - -v $(DOCKER_CCACHE_DIR):/var/tmp/ccache:z \ + -v $(DOCKER_QEMU_CACHE_DIR):$(DOCKER_QEMU_CACHE_DIR) \ + -e CCACHE_DIR=$(DOCKER_QEMU_CCACHE_DIR) \ ) \ -v $$(readlink -e $(DOCKER_SRC_COPY)):/var/tmp/qemu:z$(COMMA)ro \ $(IMAGE) \ From cfd7ebd9b0dc18168cd97f12aeb0782ac0adf869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 3 Jun 2025 12:01:49 +0100 Subject: [PATCH 1463/2760] gitlab: disable debug info on CI builds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Our default build enables debug info which adds hugely to the size of the builds as well as the size of cached objects. Disable debug info across the board to save space and reduce pressure on the CI system. We still have a number of builds which explicitly enable debug and related extra asserts like --enable-debug-tcg. Reviewed-by: Thomas Huth Signed-off-by: Alex Bennée Message-ID: <20250603110204.838117-3-alex.bennee@linaro.org> --- .gitlab-ci.d/buildtest-template.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.d/buildtest-template.yml b/.gitlab-ci.d/buildtest-template.yml index fea4e8da2f..038c3c9540 100644 --- a/.gitlab-ci.d/buildtest-template.yml +++ b/.gitlab-ci.d/buildtest-template.yml @@ -24,6 +24,7 @@ - ccache --zero-stats - section_start configure "Running configure" - ../configure --enable-werror --disable-docs --enable-fdt=system + --disable-debug-info ${TARGETS:+--target-list="$TARGETS"} $CONFIGURE_ARGS || { cat config.log meson-logs/meson-log.txt && exit 1; } From 4f5c81844c3b9185ad1cf80c1e17b05ec22e944a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 3 Jun 2025 12:01:50 +0100 Subject: [PATCH 1464/2760] tests/tcg: make aarch64 boot.S handle different starting modes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the boot.S code assumes everything starts at EL1. This will break things like the memory test which will barf on unaligned memory access when run at a higher level. Adapt the boot code to do some basic verification of the starting mode and the minimal configuration to move to the lower exception levels. With this we can run the memory test with: -M virt,secure=on -M virt,secure=on,virtualization=on -M virt,virtualisation=on If a test needs to be at a particular EL it can use the semihosting command line to indicate the level we should execute in. Cc: Julian Armistead Cc: Jim MacArthur Signed-off-by: Alex Bennée Message-ID: <20250603110204.838117-4-alex.bennee@linaro.org> --- tests/tcg/aarch64/Makefile.softmmu-target | 3 +- tests/tcg/aarch64/system/boot.S | 172 +++++++++++++++++++++- 2 files changed, 169 insertions(+), 6 deletions(-) diff --git a/tests/tcg/aarch64/Makefile.softmmu-target b/tests/tcg/aarch64/Makefile.softmmu-target index 9c52475b7a..f7a7d2b800 100644 --- a/tests/tcg/aarch64/Makefile.softmmu-target +++ b/tests/tcg/aarch64/Makefile.softmmu-target @@ -68,7 +68,8 @@ run-plugin-semiconsole-with-%: semiconsole # vtimer test needs EL2 QEMU_EL2_MACHINE=-machine virt,virtualization=on,gic-version=2 -cpu cortex-a57 -smp 4 -run-vtimer: QEMU_OPTS=$(QEMU_EL2_MACHINE) $(QEMU_BASE_ARGS) -kernel +QEMU_EL2_BASE_ARGS=-semihosting-config enable=on,target=native,chardev=output,arg="2" +run-vtimer: QEMU_OPTS=$(QEMU_EL2_MACHINE) $(QEMU_EL2_BASE_ARGS) -kernel # Simple Record/Replay Test .PHONY: memory-record diff --git a/tests/tcg/aarch64/system/boot.S b/tests/tcg/aarch64/system/boot.S index a5df9c173d..8bfa4e4efc 100644 --- a/tests/tcg/aarch64/system/boot.S +++ b/tests/tcg/aarch64/system/boot.S @@ -16,6 +16,7 @@ #define semihosting_call hlt 0xf000 #define SYS_WRITEC 0x03 /* character to debug channel */ #define SYS_WRITE0 0x04 /* string to debug channel */ +#define SYS_GET_CMDLINE 0x15 /* get command line */ #define SYS_EXIT 0x18 .align 12 @@ -70,21 +71,172 @@ lower_a32_sync: lower_a32_irq: lower_a32_fiq: lower_a32_serror: + adr x1, .unexp_excp +exit_msg: mov x0, SYS_WRITE0 - adr x1, .error semihosting_call mov x0, 1 /* EXIT_FAILURE */ bl _exit /* never returns */ .section .rodata -.error: - .string "Terminated by exception.\n" +.unexp_excp: + .string "Unexpected exception.\n" +.high_el_msg: + .string "Started in lower EL than requested.\n" +.unexp_el0: + .string "Started in invalid EL.\n" + + .align 8 +.get_cmd: + .quad cmdline + .quad 128 .text .align 4 .global __start __start: + /* + * Initialise the stack for whatever EL we are in before + * anything else, we need it to be able to _exit cleanly. + * It's smaller than the stack we pass to the C code but we + * don't need much. + */ + adrp x0, system_stack_end + add x0, x0, :lo12:system_stack_end + mov sp, x0 + + /* + * The test can set the semihosting command line to the target + * EL needed for the test. However if no semihosting args are set we will + * end up with -kernel/-append data (see semihosting_arg_fallback). + * Keep the normalised target in w11. + */ + mov x0, SYS_GET_CMDLINE + adr x1, .get_cmd + semihosting_call + adrp x10, cmdline + add x10, x10, :lo12:cmdline + ldrb w11, [x10] + + /* sanity check, normalise char to EL, clamp to 1 if outside range */ + subs w11, w11, #'0' + b.lt el_default + cmp w11, #3 + b.gt el_default + b 1f + +el_high: + adr x1, .high_el_msg + b exit_msg + +el_default: + mov w11, #1 + +1: + /* Determine current Exception Level */ + mrs x0, CurrentEL + lsr x0, x0, #2 /* CurrentEL[3:2] contains the current EL */ + + /* Are we already in a lower EL than we want? */ + cmp w11, w0 + bgt el_high + + /* Branch based on current EL */ + cmp x0, #3 + b.eq setup_el3 + cmp x0, #2 + b.eq setup_el2 + cmp x0, #1 + b.eq at_testel /* Already at EL1, skip transition */ + + /* Should not be at EL0 - error out */ + adr x1, .unexp_el0 + b exit_msg + +setup_el3: + /* Ensure we trap if we get anything wrong */ + adr x0, vector_table + msr vbar_el3, x0 + + /* Does the test want to be at EL3? */ + cmp w11, #3 + beq at_testel + + /* Configure EL3 to for lower states (EL2 or EL1) */ + mrs x0, scr_el3 + orr x0, x0, #(1 << 10) /* RW = 1: EL2/EL1 execution state is AArch64 */ + orr x0, x0, #(1 << 0) /* NS = 1: Non-secure state */ + msr scr_el3, x0 + + /* + * We need to check if EL2 is actually enabled via ID_AA64PFR0_EL1, + * otherwise we should just jump straight to EL1. + */ + mrs x0, id_aa64pfr0_el1 + ubfx x0, x0, #8, #4 /* Extract EL2 field (bits 11:8) */ + cbz x0, el2_not_present /* If field is 0 no EL2 */ + + + /* Prepare SPSR for exception return to EL2 */ + mov x0, #0x3c9 /* DAIF bits and EL2h mode (9) */ + msr spsr_el3, x0 + + /* Set EL2 entry point */ + adr x0, setup_el2 + msr elr_el3, x0 + + /* Return to EL2 */ + eret + +el2_not_present: + /* Initialize SCTLR_EL1 with reset value */ + msr sctlr_el1, xzr + + /* Set EL1 entry point */ + adr x0, at_testel + msr elr_el3, x0 + + /* Prepare SPSR for exception return to EL1h with interrupts masked */ + mov x0, #0x3c5 /* DAIF bits and EL1h mode (5) */ + msr spsr_el3, x0 + + isb /* Synchronization barrier */ + eret /* Jump to EL1 */ + +setup_el2: + /* Ensure we trap if we get anything wrong */ + adr x0, vector_table + msr vbar_el2, x0 + + /* Does the test want to be at EL2? */ + cmp w11, #2 + beq at_testel + + /* Configure EL2 to allow transition to EL1 */ + mrs x0, hcr_el2 + orr x0, x0, #(1 << 31) /* RW = 1: EL1 execution state is AArch64 */ + msr hcr_el2, x0 + + /* Initialize SCTLR_EL1 with reset value */ + msr sctlr_el1, xzr + + /* Set EL1 entry point */ + adr x0, at_testel + msr elr_el2, x0 + + /* Prepare SPSR for exception return to EL1 */ + mov x0, #(0x5 << 0) /* EL1h (SPx), with interrupts disabled */ + msr spsr_el2, x0 + + /* Return to EL1 */ + eret + + /* + * At the target EL for the test, usually EL1. Note we still + * set everything up as if we were at EL1. + */ +at_testel: /* Installs a table of exception vectors to catch and handle all exceptions by terminating the process with a diagnostic. */ adr x0, vector_table @@ -100,7 +252,7 @@ __start: * maps RAM to the first Gb. The stage2 tables have two 2mb * translation block entries covering a series of adjacent * 4k pages. - */ + */ /* Stage 1 entry: indexed by IA[38:30] */ adr x1, . /* phys address */ @@ -198,7 +350,8 @@ __start: orr x0, x0, #(3 << 16) msr cpacr_el1, x0 - /* Setup some stack space and enter the test code. + /* + * Setup some stack space before we enter the test code. * Assume everything except the return value is garbage when we * return, we won't need it. */ @@ -233,6 +386,11 @@ __sys_outc: ret .data + + .align 8 +cmdline: + .space 128, 0 + .align 12 /* Translation table @@ -246,6 +404,10 @@ ttb_stage2: .space 4096, 0 .align 12 +system_stack: + .space 4096, 0 +system_stack_end: + stack: .space 65536, 0 stack_end: From 26b20dc20745d68c7a425326f189ae0ca91bf1d6 Mon Sep 17 00:00:00 2001 From: Nabih Estefan Date: Tue, 3 Jun 2025 12:01:51 +0100 Subject: [PATCH 1465/2760] tests/qtest: Avoid unaligned access in IGB test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ../tests/qtest/libqos/igb.c:106:5: runtime error: load of misaligned address 0x562040be8e33 for type 'uint32_t', which requires 4 byte alignment Instead of straight casting the uint8_t array, we can use ldl_le_p and lduw_l_p to assure the unaligned access works properly against uint32_t and uint16_t. Signed-off-by: Nabih Estefan Reviewed-by: Laurent Vivier Tested-by: Laurent Vivier Reviewed-by: Richard Henderson Message-Id: <20250429155621.2028198-1-nabihestefan@google.com> [AJB: fix commit message, remove unneeded casts] Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Reviewed-by: Akihiko Odaki Message-ID: <20250603110204.838117-5-alex.bennee@linaro.org> --- tests/qtest/libqos/igb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/qtest/libqos/igb.c b/tests/qtest/libqos/igb.c index f40c4ec4cd..ab3ef6f0c3 100644 --- a/tests/qtest/libqos/igb.c +++ b/tests/qtest/libqos/igb.c @@ -104,10 +104,10 @@ static void igb_pci_start_hw(QOSGraphObject *obj) e1000e_macreg_write(&d->e1000e, E1000_RDT(0), 0); e1000e_macreg_write(&d->e1000e, E1000_RDH(0), 0); e1000e_macreg_write(&d->e1000e, E1000_RA, - le32_to_cpu(*(uint32_t *)address)); + ldl_le_p(address)); e1000e_macreg_write(&d->e1000e, E1000_RA + 4, E1000_RAH_AV | E1000_RAH_POOL_1 | - le16_to_cpu(*(uint16_t *)(address + 4))); + lduw_le_p(address + 4)); /* Set supported receive descriptor mode */ e1000e_macreg_write(&d->e1000e, From 0dd99ef2502be9097afd969aa1071fec57db600a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 3 Jun 2025 12:01:52 +0100 Subject: [PATCH 1466/2760] contrib/plugins: add a scaling factor to the ips arg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's easy to get lost in zeros while setting the numbers of instructions per second. Add a scaling suffix to make things simpler. Reviewed-by: Pierrick Bouvier Signed-off-by: Alex Bennée Message-ID: <20250603110204.838117-6-alex.bennee@linaro.org> --- contrib/plugins/ips.c | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c index e5297dbb01..d067971135 100644 --- a/contrib/plugins/ips.c +++ b/contrib/plugins/ips.c @@ -129,6 +129,18 @@ static void plugin_exit(qemu_plugin_id_t id, void *udata) qemu_plugin_scoreboard_free(vcpus); } +typedef struct { + const char *suffix; + unsigned long multipler; +} ScaleEntry; + +/* a bit like units.h but not binary */ +static const ScaleEntry scales[] = { + { "k", 1000 }, + { "m", 1000 * 1000 }, + { "g", 1000 * 1000 * 1000 }, +}; + QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) @@ -137,12 +149,32 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, char *opt = argv[i]; g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); if (g_strcmp0(tokens[0], "ips") == 0) { - max_insn_per_second = g_ascii_strtoull(tokens[1], NULL, 10); + char *endptr = NULL; + max_insn_per_second = g_ascii_strtoull(tokens[1], &endptr, 10); if (!max_insn_per_second && errno) { fprintf(stderr, "%s: couldn't parse %s (%s)\n", __func__, tokens[1], g_strerror(errno)); return -1; } + + if (endptr && *endptr != 0) { + g_autofree gchar *lower = g_utf8_strdown(endptr, -1); + unsigned long scale = 0; + + for (int j = 0; j < G_N_ELEMENTS(scales); j++) { + if (g_strcmp0(lower, scales[j].suffix) == 0) { + scale = scales[j].multipler; + break; + } + } + + if (scale) { + max_insn_per_second *= scale; + } else { + fprintf(stderr, "bad suffix: %s\n", endptr); + return -1; + } + } } else { fprintf(stderr, "option parsing failed: %s\n", opt); return -1; From 002655381f126a9266e0d378ebebbe87cb257a6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 3 Jun 2025 12:01:53 +0100 Subject: [PATCH 1467/2760] contrib/plugins: allow setting of instructions per quantum MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The default is we update time every 1/10th of a second or so. However for some cases we might want to update time more frequently. Allow this to be set via the command line through the ipq argument. Reviewed-by: Pierrick Bouvier Signed-off-by: Alex Bennée Message-ID: <20250603110204.838117-7-alex.bennee@linaro.org> --- contrib/plugins/ips.c | 15 ++++++++++++++- docs/about/emulation.rst | 4 ++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/contrib/plugins/ips.c b/contrib/plugins/ips.c index d067971135..f110c565bc 100644 --- a/contrib/plugins/ips.c +++ b/contrib/plugins/ips.c @@ -145,6 +145,8 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, const qemu_info_t *info, int argc, char **argv) { + bool ipq_set = false; + for (int i = 0; i < argc; i++) { char *opt = argv[i]; g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); @@ -175,6 +177,14 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, return -1; } } + } else if (g_strcmp0(tokens[0], "ipq") == 0) { + max_insn_per_quantum = g_ascii_strtoull(tokens[1], NULL, 10); + + if (!max_insn_per_quantum) { + fprintf(stderr, "bad ipq value: %s\n", tokens[0]); + return -1; + } + ipq_set = true; } else { fprintf(stderr, "option parsing failed: %s\n", opt); return -1; @@ -182,7 +192,10 @@ QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, } vcpus = qemu_plugin_scoreboard_new(sizeof(vCPUTime)); - max_insn_per_quantum = max_insn_per_second / NUM_TIME_UPDATE_PER_SEC; + + if (!ipq_set) { + max_insn_per_quantum = max_insn_per_second / NUM_TIME_UPDATE_PER_SEC; + } if (max_insn_per_quantum == 0) { fprintf(stderr, "minimum of %d instructions per second needed\n", diff --git a/docs/about/emulation.rst b/docs/about/emulation.rst index a72591ee4d..456d01d5b0 100644 --- a/docs/about/emulation.rst +++ b/docs/about/emulation.rst @@ -811,6 +811,10 @@ This plugin can limit the number of Instructions Per Second that are executed:: * - ips=N - Maximum number of instructions per cpu that can be executed in one second. The plugin will sleep when the given number of instructions is reached. + * - ipq=N + - Instructions per quantum. How many instructions before we re-calculate time. + The lower the number the more accurate time will be, but the less efficient the plugin. + Defaults to ips/10 Other emulation features ------------------------ From 7aabb6dbba30f0c2bcedbd5593e1292d4d791244 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 3 Jun 2025 12:02:00 +0100 Subject: [PATCH 1468/2760] include/exec: fix assert in size_memop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can handle larger sized memops now, expand the range of the assert. Fixes: 4b473e0c60 (tcg: Expand MO_SIZE to 3 bits) Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-ID: <20250603110204.838117-14-alex.bennee@linaro.org> --- include/exec/memop.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/exec/memop.h b/include/exec/memop.h index 407a47d82c..cf7da3362e 100644 --- a/include/exec/memop.h +++ b/include/exec/memop.h @@ -162,8 +162,8 @@ static inline unsigned memop_size(MemOp op) static inline MemOp size_memop(unsigned size) { #ifdef CONFIG_DEBUG_TCG - /* Power of 2 up to 8. */ - assert((size & (size - 1)) == 0 && size >= 1 && size <= 8); + /* Power of 2 up to 1024 */ + assert(is_power_of_2(size) && size >= 1 && size <= (1 << MO_SIZE)); #endif return (MemOp)ctz32(size); } From 46b17eb9e261f28697a6fa5cb09c5fc505ef68ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 3 Jun 2025 12:02:01 +0100 Subject: [PATCH 1469/2760] include/gdbstub: fix include guard in commands.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-ID: <20250603110204.838117-15-alex.bennee@linaro.org> --- include/gdbstub/commands.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/gdbstub/commands.h b/include/gdbstub/commands.h index 40f0514fe9..bff3674872 100644 --- a/include/gdbstub/commands.h +++ b/include/gdbstub/commands.h @@ -1,5 +1,5 @@ #ifndef GDBSTUB_COMMANDS_H -#define GDBSTUB +#define GDBSTUB_COMMANDS_H typedef void (*GdbCmdHandler)(GArray *params, void *user_ctx); From 3bb69b1953c1a829152ff5c2599269bc129e05ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Tue, 3 Jun 2025 12:02:02 +0100 Subject: [PATCH 1470/2760] gdbstub: assert earlier in handle_read_all_regs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When things go wrong we want to assert on the register that failed to be able to figure out what went wrong. Reviewed-by: Pierrick Bouvier Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-ID: <20250603110204.838117-16-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 565f6b33a9..6023c80d25 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -1343,8 +1343,8 @@ static void handle_read_all_regs(GArray *params, void *user_ctx) len += gdb_read_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf, reg_id); + g_assert(len == gdbserver_state.mem_buf->len); } - g_assert(len == gdbserver_state.mem_buf->len); gdb_memtohex(gdbserver_state.str_buf, gdbserver_state.mem_buf->data, len); gdb_put_strbuf(); From b2654598b3330aaa58ab0cec2114843bfa96ddaa Mon Sep 17 00:00:00 2001 From: Dominik 'Disconnect3d' Czarnota Date: Tue, 3 Jun 2025 12:02:03 +0100 Subject: [PATCH 1471/2760] gdbstub: Implement qGDBServerVersion packet MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit adds support for the `qGDBServerVersion` packet to the qemu gdbstub which could be used by clients to detect the QEMU version (and, e.g., use a workaround for known bugs). This packet is not documented/standarized by GDB but it was implemented by LLDB gdbstub [0] and is helpful for projects like Pwndbg [1]. This has been implemented by Patryk, who I included in Co-authored-by and who asked me to send the patch. [0] https://lldb.llvm.org/resources/lldbgdbremote.html#qgdbserverversion [1] https://github.com/pwndbg/pwndbg/issues/2648 Co-authored-by: Patryk 'patryk4815' Sondej Signed-off-by: Dominik 'Disconnect3d' Czarnota Message-Id: <20250403191340.53343-1-dominik.b.czarnota@gmail.com> [AJB: fix include, checkpatch linewrap] Signed-off-by: Alex Bennée Message-ID: <20250603110204.838117-17-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index 6023c80d25..def0b7e877 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -28,6 +28,7 @@ #include "qemu/cutils.h" #include "qemu/module.h" #include "qemu/error-report.h" +#include "qemu/target-info.h" #include "trace.h" #include "exec/gdbstub.h" #include "gdbstub/commands.h" @@ -1597,6 +1598,18 @@ static void handle_query_threads(GArray *params, void *user_ctx) gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu); } +static void handle_query_gdb_server_version(GArray *params, void *user_ctx) +{ +#if defined(CONFIG_USER_ONLY) + g_string_printf(gdbserver_state.str_buf, "name:qemu-%s;version:%s;", + target_name(), QEMU_VERSION); +#else + g_string_printf(gdbserver_state.str_buf, "name:qemu-system-%s;version:%s;", + target_name(), QEMU_VERSION); +#endif + gdb_put_strbuf(); +} + static void handle_query_first_threads(GArray *params, void *user_ctx) { gdbserver_state.query_cpu = gdb_first_attached_cpu(); @@ -1842,6 +1855,10 @@ static const GdbCmdParseEntry gdb_gen_query_table[] = { .handler = handle_query_threads, .cmd = "sThreadInfo", }, + { + .handler = handle_query_gdb_server_version, + .cmd = "GDBServerVersion", + }, { .handler = handle_query_first_threads, .cmd = "fThreadInfo", From 63070ce368e1a2d430b9022a9db46f1817628efc Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Tue, 3 Jun 2025 12:02:04 +0100 Subject: [PATCH 1472/2760] gdbstub: update aarch64-core.xml MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update aarch64-core.xml to include field definitions for PSTATE, which in gdb is modelled in the cpsr (current program status register) pseudo-register, named after the actual cpsr register in armv7. Defining the fields layout of the register allows easy inspection of for example, the current exception level (EL): For example. Before booting a Linux guest, EL=2, but after booting and Ctrl-C'ing in gdb, we get EL=0: (gdb) info registers $cpsr cpsr 0x20402009 [ SP EL=2 BTYPE=0 PAN C ] (gdb) cont Continuing. ^C Thread 2 received signal SIGINT, Interrupt. 0x0000ffffaaff286c in ?? () (gdb) info registers $cpsr cpsr 0x20001000 [ EL=0 BTYPE=0 SSBS C ] The aarch64-core.xml has been updated to match exactly the version retrieved from upstream gdb, retrieved in 2025-05-19 from HEAD commit 9f4dc0b137c86f6ff2098cb1ab69442c69d6023d. Link: https://sourceware.org/git/?p=binutils-gdb.git;a=blob;f=gdb/features/aarch64-core.xml;h=b8046510b9a085d30463d37b3ecc8d435f5fb7a4;hb=HEAD Signed-off-by: Manos Pitsidianakis Message-Id: <20250519-gdbstub-aarch64-pstate-xml-v1-1-b4dbe87fe7c6@linaro.org> [AJB: expanded upstream link] Signed-off-by: Alex Bennée Message-ID: <20250603110204.838117-18-alex.bennee@linaro.org> --- gdb-xml/aarch64-core.xml | 52 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/gdb-xml/aarch64-core.xml b/gdb-xml/aarch64-core.xml index e1e9dc3f91..b8046510b9 100644 --- a/gdb-xml/aarch64-core.xml +++ b/gdb-xml/aarch64-core.xml @@ -1,5 +1,5 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 9c55c03c05c1899521ff0c991b9296633d759890 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 4 Jun 2025 14:55:01 +0800 Subject: [PATCH 1473/2760] hw/loongarch/virt: Fix big endian support with MCFG table With API build_mcfg(), it is not necessary with parameter structure AcpiMcfgInfo to convert to little endian since it is directly used with host native endian. Here remove endian conversion before calling function build_mcfg(). With this patch, bios-tables-test passes to run on big endian host machine S390. Fixes: 735143f10d3e ("hw/loongarch: Add acpi ged support") Cc: qemu-stable@nongnu.org Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20250604065502.1114098-2-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/virt-acpi-build.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/loongarch/virt-acpi-build.c b/hw/loongarch/virt-acpi-build.c index 073b6de75c..2cd2d9d842 100644 --- a/hw/loongarch/virt-acpi-build.c +++ b/hw/loongarch/virt-acpi-build.c @@ -575,8 +575,8 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_add_table(table_offsets, tables_blob); { AcpiMcfgInfo mcfg = { - .base = cpu_to_le64(VIRT_PCI_CFG_BASE), - .size = cpu_to_le64(VIRT_PCI_CFG_SIZE), + .base = VIRT_PCI_CFG_BASE, + .size = VIRT_PCI_CFG_SIZE, }; build_mcfg(tables_blob, tables->linker, &mcfg, lvms->oem_id, lvms->oem_table_id); From 095e6fcf624e9778ca455d3e654bfd7a2f43d653 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 4 Jun 2025 14:55:02 +0800 Subject: [PATCH 1474/2760] hw/intc/loongarch_pch: Convert to little endian with ID register With PCH ID register, it is defined as union type as follows: union LoongArchPIC_ID { struct { uint8_t _reserved_0[3]; uint8_t id; uint8_t version; uint8_t _reserved_1; uint8_t irq_num; uint8_t _reserved_2; } QEMU_PACKED desc; uint64_t data; } And with pch driver in virt machine irq_number is parsed with little endian method: vec_count = ((readq(priv->base) >> 48) & 0xff) + 1 So the value of ID register should be converted to little endian. With this patch, linux kernel passes to run on S390 big endian host machine with TCG method. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20250604065502.1114098-3-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index cbba2fc284..ebb33ed0b0 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -82,7 +82,7 @@ static uint64_t pch_pic_read(void *opaque, hwaddr addr, uint64_t field_mask) addr -= offset; switch (addr) { case PCH_PIC_INT_ID: - val = s->id.data; + val = cpu_to_le64(s->id.data); break; case PCH_PIC_INT_MASK: val = s->int_mask; From 1e043baf15241f18a35f050fb2a2beb3fd7c7906 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 5 Jun 2025 17:28:48 +0800 Subject: [PATCH 1475/2760] hw/intc/loongarch_extioi: Fix typo issue about register EXTIOI_COREISR_END Interrupt controller extioi supports 256 vectors, register EXTIOI_COREISR records pending interrupt status with bitmap method. Size of EXTIOI_COREISR is 256 / 8 = 0x20 bytes, EXTIOI_COREISR_END should be EXTIOI_COREISR_START + 0x20 rather than 0xB20. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20250605092848.1550985-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- include/hw/intc/loongarch_extioi_common.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/intc/loongarch_extioi_common.h b/include/hw/intc/loongarch_extioi_common.h index 735bfee80a..dca25ff893 100644 --- a/include/hw/intc/loongarch_extioi_common.h +++ b/include/hw/intc/loongarch_extioi_common.h @@ -35,7 +35,7 @@ #define EXTIOI_ISR_START (0x700 - APIC_OFFSET) #define EXTIOI_ISR_END (0x720 - APIC_OFFSET) #define EXTIOI_COREISR_START (0x800 - APIC_OFFSET) -#define EXTIOI_COREISR_END (0xB20 - APIC_OFFSET) +#define EXTIOI_COREISR_END (0x820 - APIC_OFFSET) #define EXTIOI_COREMAP_START (0xC00 - APIC_OFFSET) #define EXTIOI_COREMAP_END (0xD00 - APIC_OFFSET) #define EXTIOI_SIZE 0x800 From 93282c8a890b1d318668d9a0cf82c0af793ae441 Mon Sep 17 00:00:00 2001 From: Qiang Ma Date: Tue, 3 Jun 2025 11:18:13 +0800 Subject: [PATCH 1476/2760] hw/loongarch/virt: inform guest of kvm Commit bab27ea2e3 ("hw/arm/virt: smbios: inform guest of kvm") fixes the same issue on arm. without this patch: [root@localhost ~]# virt-what qemu with this patch: [root@localhost ~]# virt-what kvm Signed-off-by: Qiang Ma Reviewed-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20250603031813.31794-1-maqianga@uniontech.com> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 1b504047db..a3d449ca8b 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -136,6 +136,10 @@ static void virt_build_smbios(LoongArchVirtMachineState *lvms) return; } + if (kvm_enabled()) { + product = "KVM Virtual Machine"; + } + smbios_set_defaults("QEMU", product, mc->name); smbios_get_tables(ms, SMBIOS_ENTRY_POINT_TYPE_64, From e7788da9860c97920c19fa1150806186513ef256 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Tue, 3 Jun 2025 10:48:09 +0800 Subject: [PATCH 1477/2760] target/loongarch: add check for fcond fcond only has 22 types, add a check for fcond. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2972 Signed-off-by: Song Gao Reviewed-by: Richard Henderson Message-Id: <20250603024810.350510-1-gaosong@loongson.cn> --- .../loongarch/tcg/insn_trans/trans_fcmp.c.inc | 25 +++++++++++++------ .../loongarch/tcg/insn_trans/trans_vec.c.inc | 16 +++++++++--- 2 files changed, 30 insertions(+), 11 deletions(-) diff --git a/target/loongarch/tcg/insn_trans/trans_fcmp.c.inc b/target/loongarch/tcg/insn_trans/trans_fcmp.c.inc index 3babf69e4a..6a2c030a6b 100644 --- a/target/loongarch/tcg/insn_trans/trans_fcmp.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_fcmp.c.inc @@ -4,10 +4,15 @@ */ /* bit0(signaling/quiet) bit1(lt) bit2(eq) bit3(un) bit4(neq) */ -static uint32_t get_fcmp_flags(int cond) +static uint32_t get_fcmp_flags(DisasContext *ctx, int cond) { uint32_t flags = 0; + /*check cond , cond =[0-8,10,12] */ + if ((cond > 8) &&(cond != 10) && (cond != 12)) { + return -1; + } + if (cond & 0x1) { flags |= FCMP_LT; } @@ -26,9 +31,14 @@ static uint32_t get_fcmp_flags(int cond) static bool trans_fcmp_cond_s(DisasContext *ctx, arg_fcmp_cond_s *a) { TCGv var, src1, src2; - uint32_t flags; + uint32_t flags = get_fcmp_flags(ctx, a->fcond >>1); void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); + if (flags == -1) { + generate_exception(ctx, EXCCODE_INE); + return true; + } + if (!avail_FP_SP(ctx)) { return false; } @@ -39,8 +49,6 @@ static bool trans_fcmp_cond_s(DisasContext *ctx, arg_fcmp_cond_s *a) src1 = get_fpr(ctx, a->fj); src2 = get_fpr(ctx, a->fk); fn = (a->fcond & 1 ? gen_helper_fcmp_s_s : gen_helper_fcmp_c_s); - flags = get_fcmp_flags(a->fcond >> 1); - fn(var, tcg_env, src1, src2, tcg_constant_i32(flags)); tcg_gen_st8_tl(var, tcg_env, offsetof(CPULoongArchState, cf[a->cd])); @@ -50,9 +58,14 @@ static bool trans_fcmp_cond_s(DisasContext *ctx, arg_fcmp_cond_s *a) static bool trans_fcmp_cond_d(DisasContext *ctx, arg_fcmp_cond_d *a) { TCGv var, src1, src2; - uint32_t flags; + uint32_t flags = get_fcmp_flags(ctx, a->fcond >> 1); void (*fn)(TCGv, TCGv_env, TCGv, TCGv, TCGv_i32); + if (flags == -1) { + generate_exception(ctx, EXCCODE_INE); + return true; + } + if (!avail_FP_DP(ctx)) { return false; } @@ -63,8 +76,6 @@ static bool trans_fcmp_cond_d(DisasContext *ctx, arg_fcmp_cond_d *a) src1 = get_fpr(ctx, a->fj); src2 = get_fpr(ctx, a->fk); fn = (a->fcond & 1 ? gen_helper_fcmp_s_d : gen_helper_fcmp_c_d); - flags = get_fcmp_flags(a->fcond >> 1); - fn(var, tcg_env, src1, src2, tcg_constant_i32(flags)); tcg_gen_st8_tl(var, tcg_env, offsetof(CPULoongArchState, cf[a->cd])); diff --git a/target/loongarch/tcg/insn_trans/trans_vec.c.inc b/target/loongarch/tcg/insn_trans/trans_vec.c.inc index dff92772ad..d6f0560349 100644 --- a/target/loongarch/tcg/insn_trans/trans_vec.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_vec.c.inc @@ -4655,19 +4655,23 @@ TRANS(xvslti_du, LASX, do_xcmpi, MO_64, TCG_COND_LTU) static bool do_vfcmp_cond_s(DisasContext *ctx, arg_vvv_fcond *a, uint32_t sz) { - uint32_t flags; + uint32_t flags = get_fcmp_flags(ctx, a->fcond >> 1); void (*fn)(TCGv_env, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); TCGv_i32 vd = tcg_constant_i32(a->vd); TCGv_i32 vj = tcg_constant_i32(a->vj); TCGv_i32 vk = tcg_constant_i32(a->vk); TCGv_i32 oprsz = tcg_constant_i32(sz); + if(flags == -1){ + generate_exception(ctx, EXCCODE_INE); + return true; + } + if (!check_vec(ctx, sz)) { return true; } fn = (a->fcond & 1 ? gen_helper_vfcmp_s_s : gen_helper_vfcmp_c_s); - flags = get_fcmp_flags(a->fcond >> 1); fn(tcg_env, oprsz, vd, vj, vk, tcg_constant_i32(flags)); return true; @@ -4675,19 +4679,23 @@ static bool do_vfcmp_cond_s(DisasContext *ctx, arg_vvv_fcond *a, uint32_t sz) static bool do_vfcmp_cond_d(DisasContext *ctx, arg_vvv_fcond *a, uint32_t sz) { - uint32_t flags; + uint32_t flags = get_fcmp_flags(ctx, a->fcond >> 1); void (*fn)(TCGv_env, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32, TCGv_i32); TCGv_i32 vd = tcg_constant_i32(a->vd); TCGv_i32 vj = tcg_constant_i32(a->vj); TCGv_i32 vk = tcg_constant_i32(a->vk); TCGv_i32 oprsz = tcg_constant_i32(sz); + if (flags == -1) { + generate_exception(ctx, EXCCODE_INE); + return true; + } + if (!check_vec(ctx, sz)) { return true; } fn = (a->fcond & 1 ? gen_helper_vfcmp_s_d : gen_helper_vfcmp_c_d); - flags = get_fcmp_flags(a->fcond >> 1); fn(tcg_env, oprsz, vd, vj, vk, tcg_constant_i32(flags)); return true; From 4332a641e220ca969810fac12b25cbf2037f7c3f Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 30 Apr 2025 17:47:37 +0800 Subject: [PATCH 1478/2760] hw/loongarch/virt: Remove global variables about initrd MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Global variables initrd_offset and initrd_size records loading information about initrd, it can be moved to structure loongarch_boot_info. Signed-off-by: Bibo Mao Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250430094738.1556670-2-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/boot.c | 21 ++++++++++----------- include/hw/loongarch/boot.h | 2 ++ 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index 9b6292eaa1..a343547acd 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -38,9 +38,6 @@ struct loongarch_linux_hdr { struct memmap_entry *memmap_table; unsigned memmap_entries; -ram_addr_t initrd_offset; -uint64_t initrd_size; - static const unsigned int slave_boot_code[] = { /* Configure reset ebase. */ 0x0400302c, /* csrwr $t0, LOONGARCH_CSR_EENTRY */ @@ -121,7 +118,8 @@ static void init_efi_boot_memmap(struct efi_system_table *systab, } } -static void init_efi_initrd_table(struct efi_system_table *systab, +static void init_efi_initrd_table(struct loongarch_boot_info *info, + struct efi_system_table *systab, void *p, void *start) { efi_guid_t tbl_guid = LINUX_EFI_INITRD_MEDIA_GUID; @@ -132,8 +130,8 @@ static void init_efi_initrd_table(struct efi_system_table *systab, systab->tables[1].table = (struct efi_configuration_table *)(p - start); systab->nr_tables = 2; - initrd_table->base = initrd_offset; - initrd_table->size = initrd_size; + initrd_table->base = info->initrd_addr; + initrd_table->size = info->initrd_size; } static void init_efi_fdt_table(struct efi_system_table *systab) @@ -169,7 +167,7 @@ static void init_systab(struct loongarch_boot_info *info, void *p, void *start) init_efi_boot_memmap(systab, p, start); p += ROUND_UP(sizeof(struct efi_boot_memmap) + sizeof(efi_memory_desc_t) * memmap_entries, 64 * KiB); - init_efi_initrd_table(systab, p, start); + init_efi_initrd_table(info, systab, p, start); p += ROUND_UP(sizeof(struct efi_initrd), 64 * KiB); init_efi_fdt_table(systab); @@ -276,8 +274,8 @@ static ram_addr_t alloc_initrd_memory(struct loongarch_boot_info *info, static int64_t load_kernel_info(struct loongarch_boot_info *info) { - uint64_t kernel_entry, kernel_low, kernel_high; - ssize_t kernel_size; + uint64_t kernel_entry, kernel_low, kernel_high, initrd_offset = 0; + ssize_t kernel_size, initrd_size; kernel_size = load_elf(info->kernel_filename, NULL, cpu_loongarch_virt_to_phys, NULL, @@ -313,8 +311,9 @@ static int64_t load_kernel_info(struct loongarch_boot_info *info) info->initrd_filename); exit(1); } - } else { - initrd_size = 0; + + info->initrd_addr = initrd_offset; + info->initrd_size = initrd_size; } return kernel_entry; diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.h index b3b870df1f..27399de99c 100644 --- a/include/hw/loongarch/boot.h +++ b/include/hw/loongarch/boot.h @@ -102,6 +102,8 @@ struct loongarch_boot_info { const char *kernel_cmdline; const char *initrd_filename; uint64_t a0, a1, a2; + uint64_t initrd_addr; + uint64_t initrd_size; }; extern struct memmap_entry *memmap_table; From ffe89c1762d879fd39ba1be853d154677dbfbc7b Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 30 Apr 2025 17:47:38 +0800 Subject: [PATCH 1479/2760] hw/loongarch/virt: Remove global variables about memmap tables Global variables memmap_table and memmap_entries stores UEFI memory map table informations. It can be moved into structure LoongArchVirtMachineState. Signed-off-by: Bibo Mao Reviewed-by: Song Gao Message-Id: <20250430094738.1556670-3-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/boot.c | 31 +++++++++++++++++++------------ hw/loongarch/virt.c | 23 ++++++++++++++++------- include/hw/loongarch/boot.h | 3 --- include/hw/loongarch/virt.h | 2 ++ 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/hw/loongarch/boot.c b/hw/loongarch/boot.c index a343547acd..14d6c52d4e 100644 --- a/hw/loongarch/boot.c +++ b/hw/loongarch/boot.c @@ -35,9 +35,6 @@ struct loongarch_linux_hdr { uint32_t pe_header_offset; } QEMU_PACKED; -struct memmap_entry *memmap_table; -unsigned memmap_entries; - static const unsigned int slave_boot_code[] = { /* Configure reset ebase. */ 0x0400302c, /* csrwr $t0, LOONGARCH_CSR_EENTRY */ @@ -91,12 +88,16 @@ static inline void *guidcpy(void *dst, const void *src) return memcpy(dst, src, sizeof(efi_guid_t)); } -static void init_efi_boot_memmap(struct efi_system_table *systab, +static void init_efi_boot_memmap(MachineState *ms, + struct efi_system_table *systab, void *p, void *start) { unsigned i; struct efi_boot_memmap *boot_memmap = p; efi_guid_t tbl_guid = LINUX_EFI_BOOT_MEMMAP_GUID; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); + struct memmap_entry *memmap_table; + unsigned int memmap_entries; /* efi_configuration_table 1 */ guidcpy(&systab->tables[0].guid, &tbl_guid); @@ -108,6 +109,8 @@ static void init_efi_boot_memmap(struct efi_system_table *systab, boot_memmap->map_size = 0; efi_memory_desc_t *map = p + sizeof(struct efi_boot_memmap); + memmap_table = lvms->memmap_table; + memmap_entries = lvms->memmap_entries; for (i = 0; i < memmap_entries; i++) { map = (void *)boot_memmap + sizeof(*map); map[i].type = memmap_table[i].type; @@ -144,10 +147,12 @@ static void init_efi_fdt_table(struct efi_system_table *systab) systab->nr_tables = 3; } -static void init_systab(struct loongarch_boot_info *info, void *p, void *start) +static void init_systab(MachineState *ms, + struct loongarch_boot_info *info, void *p, void *start) { void *bp_tables_start; struct efi_system_table *systab = p; + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); info->a2 = p - start; @@ -164,9 +169,9 @@ static void init_systab(struct loongarch_boot_info *info, void *p, void *start) systab->tables = p; bp_tables_start = p; - init_efi_boot_memmap(systab, p, start); + init_efi_boot_memmap(ms, systab, p, start); p += ROUND_UP(sizeof(struct efi_boot_memmap) + - sizeof(efi_memory_desc_t) * memmap_entries, 64 * KiB); + sizeof(efi_memory_desc_t) * lvms->memmap_entries, 64 * KiB); init_efi_initrd_table(info, systab, p, start); p += ROUND_UP(sizeof(struct efi_initrd), 64 * KiB); init_efi_fdt_table(systab); @@ -368,17 +373,19 @@ static void loongarch_firmware_boot(LoongArchVirtMachineState *lvms, fw_cfg_add_kernel_info(info, lvms->fw_cfg); } -static void init_boot_rom(struct loongarch_boot_info *info, void *p) +static void init_boot_rom(MachineState *ms, + struct loongarch_boot_info *info, void *p) { void *start = p; init_cmdline(info, p, start); p += COMMAND_LINE_SIZE; - init_systab(info, p, start); + init_systab(ms, info, p, start); } -static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) +static void loongarch_direct_kernel_boot(MachineState *ms, + struct loongarch_boot_info *info) { void *p, *bp; int64_t kernel_addr = VIRT_FLASH0_BASE; @@ -396,7 +403,7 @@ static void loongarch_direct_kernel_boot(struct loongarch_boot_info *info) /* Load cmdline and system tables at [0 - 1 MiB] */ p = g_malloc0(1 * MiB); bp = p; - init_boot_rom(info, p); + init_boot_rom(ms, info, p); rom_add_blob_fixed_as("boot_info", bp, 1 * MiB, 0, &address_space_memory); /* Load slave boot code at pflash0 . */ @@ -436,6 +443,6 @@ void loongarch_load_kernel(MachineState *ms, struct loongarch_boot_info *info) if (lvms->bios_loaded) { loongarch_firmware_boot(lvms, info); } else { - loongarch_direct_kernel_boot(info); + loongarch_direct_kernel_boot(ms, info); } } diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index a3d449ca8b..34dfbd13e5 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -172,8 +172,15 @@ static void virt_powerdown_req(Notifier *notifier, void *opaque) acpi_send_event(s->acpi_ged, ACPI_POWER_DOWN_STATUS); } -static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) +static void memmap_add_entry(MachineState *ms, uint64_t address, + uint64_t length, uint32_t type) { + LoongArchVirtMachineState *lvms = LOONGARCH_VIRT_MACHINE(ms); + struct memmap_entry *memmap_table; + unsigned int memmap_entries; + + memmap_table = lvms->memmap_table; + memmap_entries = lvms->memmap_entries; /* Ensure there are no duplicate entries. */ for (unsigned i = 0; i < memmap_entries; i++) { assert(memmap_table[i].address != address); @@ -186,6 +193,8 @@ static void memmap_add_entry(uint64_t address, uint64_t length, uint32_t type) memmap_table[memmap_entries].type = cpu_to_le32(type); memmap_table[memmap_entries].reserved = 0; memmap_entries++; + lvms->memmap_table = memmap_table; + lvms->memmap_entries = memmap_entries; } static DeviceState *create_acpi_ged(DeviceState *pch_pic, @@ -623,13 +632,13 @@ static void fw_cfg_add_memory(MachineState *ms) } if (size >= gap) { - memmap_add_entry(base, gap, 1); + memmap_add_entry(ms, base, gap, 1); size -= gap; base = VIRT_HIGHMEM_BASE; } if (size) { - memmap_add_entry(base, size, 1); + memmap_add_entry(ms, base, size, 1); base += size; } @@ -644,7 +653,7 @@ static void fw_cfg_add_memory(MachineState *ms) * lowram: [base, +(gap - numa_info[0].node_mem)) * highram: [VIRT_HIGHMEM_BASE, +(ram_size - gap)) */ - memmap_add_entry(base, gap - numa_info[0].node_mem, 1); + memmap_add_entry(ms, base, gap - numa_info[0].node_mem, 1); size = ram_size - gap; base = VIRT_HIGHMEM_BASE; } else { @@ -652,7 +661,7 @@ static void fw_cfg_add_memory(MachineState *ms) } if (size) { - memmap_add_entry(base, size, 1); + memmap_add_entry(ms, base, size, 1); } } @@ -738,8 +747,8 @@ static void virt_init(MachineState *machine) rom_set_fw(lvms->fw_cfg); if (lvms->fw_cfg != NULL) { fw_cfg_add_file(lvms->fw_cfg, "etc/memmap", - memmap_table, - sizeof(struct memmap_entry) * (memmap_entries)); + lvms->memmap_table, + sizeof(struct memmap_entry) * lvms->memmap_entries); } /* Initialize the IO interrupt subsystem */ diff --git a/include/hw/loongarch/boot.h b/include/hw/loongarch/boot.h index 27399de99c..9819f7fbe3 100644 --- a/include/hw/loongarch/boot.h +++ b/include/hw/loongarch/boot.h @@ -106,9 +106,6 @@ struct loongarch_boot_info { uint64_t initrd_size; }; -extern struct memmap_entry *memmap_table; -extern unsigned memmap_entries; - struct memmap_entry { uint64_t address; uint64_t length; diff --git a/include/hw/loongarch/virt.h b/include/hw/loongarch/virt.h index 2b7d19953f..602feab0f0 100644 --- a/include/hw/loongarch/virt.h +++ b/include/hw/loongarch/virt.h @@ -63,6 +63,8 @@ struct LoongArchVirtMachineState { struct loongarch_boot_info bootinfo; DeviceState *ipi; DeviceState *extioi; + struct memmap_entry *memmap_table; + unsigned int memmap_entries; }; #define TYPE_LOONGARCH_VIRT_MACHINE MACHINE_TYPE_NAME("virt") From a5844ea5530b664ea70852418fb751d63c15dba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 24 Jan 2025 18:43:36 +0100 Subject: [PATCH 1480/2760] hw/char/sh_serial: Delete fifo_timeout_timer in DeviceUnrealize MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fifo_timeout_timer is created in the DeviceRealize handler, not in the instance_init one. For parity, delete it in DeviceUnrealize, rather than instance_finalize. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-Id: <20250124175053.74461-2-philmd@linaro.org> --- hw/char/sh_serial.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index 6abd80386f..cdaeac7b70 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -434,9 +434,9 @@ static void sh_serial_realize(DeviceState *d, Error **errp) s->etu = NANOSECONDS_PER_SECOND / 9600; } -static void sh_serial_finalize(Object *obj) +static void sh_serial_unrealize(DeviceState *dev) { - SHSerialState *s = SH_SERIAL(obj); + SHSerialState *s = SH_SERIAL(dev); timer_del(&s->fifo_timeout_timer); } @@ -445,6 +445,10 @@ static void sh_serial_init(Object *obj) { } +static void sh_serial_finalize(Object *obj) +{ +} + static const Property sh_serial_properties[] = { DEFINE_PROP_CHR("chardev", SHSerialState, chr), DEFINE_PROP_UINT8("features", SHSerialState, feat, 0), @@ -456,6 +460,7 @@ static void sh_serial_class_init(ObjectClass *oc, const void *data) device_class_set_props(dc, sh_serial_properties); dc->realize = sh_serial_realize; + dc->unrealize = sh_serial_unrealize; device_class_set_legacy_reset(dc, sh_serial_reset); /* Reason: part of SuperH CPU/SoC, needs to be wired up */ dc->user_creatable = false; From 65561d9393cfa21eb696bb759122641a569de63a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 24 Jan 2025 18:46:27 +0100 Subject: [PATCH 1481/2760] hw/char/sh_serial: Convert to TypeInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QOM types are now registered using as TypeInfo via DEFINE_TYPES() or type_init(). Update TYPE_SH_SERIAL, removing the empty QOM instance_init/finalize handlers. This was definitely wrong, because OBJECT_DEFINE_TYPE() is only for cases where the class needs its own virtual methods or some other per-class state in its own class struct. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-Id: <20250124175053.74461-3-philmd@linaro.org> --- hw/char/sh_serial.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/hw/char/sh_serial.c b/hw/char/sh_serial.c index cdaeac7b70..30447fa018 100644 --- a/hw/char/sh_serial.c +++ b/hw/char/sh_serial.c @@ -78,10 +78,6 @@ struct SHSerialState { qemu_irq bri; }; -typedef struct {} SHSerialStateClass; - -OBJECT_DEFINE_TYPE(SHSerialState, sh_serial, SH_SERIAL, SYS_BUS_DEVICE) - static void sh_serial_clear_fifo(SHSerialState *s) { memset(s->rx_fifo, 0, SH_RX_FIFO_LENGTH); @@ -441,14 +437,6 @@ static void sh_serial_unrealize(DeviceState *dev) timer_del(&s->fifo_timeout_timer); } -static void sh_serial_init(Object *obj) -{ -} - -static void sh_serial_finalize(Object *obj) -{ -} - static const Property sh_serial_properties[] = { DEFINE_PROP_CHR("chardev", SHSerialState, chr), DEFINE_PROP_UINT8("features", SHSerialState, feat, 0), @@ -465,3 +453,14 @@ static void sh_serial_class_init(ObjectClass *oc, const void *data) /* Reason: part of SuperH CPU/SoC, needs to be wired up */ dc->user_creatable = false; } + +static const TypeInfo sh_serial_types[] = { + { + .name = TYPE_SH_SERIAL, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(SHSerialState), + .class_init = sh_serial_class_init, + }, +}; + +DEFINE_TYPES(sh_serial_types) From 8a486e3902177b1d0faaed6f372909767a019124 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 4 May 2025 18:01:27 +0200 Subject: [PATCH 1482/2760] hw/pci-host/raven: Remove is-legacy-prep property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was a workaround for the prep machine that was removed 5 years ago so this is no longer needed. Fixes: b2ce76a073 (hw/ppc/prep: Remove the deprecated "prep" machine and the OpenHackware BIOS) Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <0d41c18a8831bd4c8b0948eda3ef8f60f5a311f3.1746374076.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/pci-host/raven.c | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index 21f7ca65e0..b78a8f32d3 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -75,7 +75,6 @@ struct PRePPCIState { RavenPCIState pci_dev; int contiguous_map; - bool is_legacy_prep; }; #define BIOS_SIZE (1 * MiB) @@ -243,22 +242,18 @@ static void raven_pcihost_realizefn(DeviceState *d, Error **errp) MemoryRegion *address_space_mem = get_system_memory(); int i; - if (s->is_legacy_prep) { - for (i = 0; i < PCI_NUM_PINS; i++) { - sysbus_init_irq(dev, &s->pci_irqs[i]); - } - } else { - /* According to PReP specification section 6.1.6 "System Interrupt - * Assignments", all PCI interrupts are routed via IRQ 15 */ - s->or_irq = OR_IRQ(object_new(TYPE_OR_IRQ)); - object_property_set_int(OBJECT(s->or_irq), "num-lines", PCI_NUM_PINS, - &error_fatal); - qdev_realize(DEVICE(s->or_irq), NULL, &error_fatal); - sysbus_init_irq(dev, &s->or_irq->out_irq); - - for (i = 0; i < PCI_NUM_PINS; i++) { - s->pci_irqs[i] = qdev_get_gpio_in(DEVICE(s->or_irq), i); - } + /* + * According to PReP specification section 6.1.6 "System Interrupt + * Assignments", all PCI interrupts are routed via IRQ 15 + */ + s->or_irq = OR_IRQ(object_new(TYPE_OR_IRQ)); + object_property_set_int(OBJECT(s->or_irq), "num-lines", PCI_NUM_PINS, + &error_fatal); + qdev_realize(DEVICE(s->or_irq), NULL, &error_fatal); + sysbus_init_irq(dev, &s->or_irq->out_irq); + + for (i = 0; i < PCI_NUM_PINS; i++) { + s->pci_irqs[i] = qdev_get_gpio_in(DEVICE(s->or_irq), i); } qdev_init_gpio_in(d, raven_change_gpio, 1); @@ -426,9 +421,6 @@ static const Property raven_pcihost_properties[] = { DEFINE_PROP_UINT32("elf-machine", PREPPCIState, pci_dev.elf_machine, EM_NONE), DEFINE_PROP_STRING("bios-name", PREPPCIState, pci_dev.bios_name), - /* Temporary workaround until legacy prep machine is removed */ - DEFINE_PROP_BOOL("is-legacy-prep", PREPPCIState, is_legacy_prep, - false), }; static void raven_pcihost_class_init(ObjectClass *klass, const void *data) From 63151361fdf14e9dcaa463fe06e0b23f51c689eb Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Sun, 4 May 2025 18:01:28 +0200 Subject: [PATCH 1483/2760] hw/pci-host/raven: Revert "raven: Move BIOS loading from board code to PCI host" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit d0b25425749d5525b2ba6d9d966d8800a5643b35. Loading firmware from the PCI host is unusual and raven is only used by one board so this does not simplify anything but rather complicates it. Revert to loading firmware from board code as that is the usual way and also because raven has nothing to do with ROM so it is not a good place for this. Signed-off-by: BALATON Zoltan Reviewed-by: Philippe Mathieu-Daudé Message-ID: <4ca4f71bf661923d9a91b7e6776a0e40726e2337.1746374076.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/pci-host/raven.c | 55 --------------------------------------------- hw/ppc/prep.c | 27 ++++++++++++++++++++-- 2 files changed, 25 insertions(+), 57 deletions(-) diff --git a/hw/pci-host/raven.c b/hw/pci-host/raven.c index b78a8f32d3..f8c0be5d21 100644 --- a/hw/pci-host/raven.c +++ b/hw/pci-host/raven.c @@ -24,7 +24,6 @@ */ #include "qemu/osdep.h" -#include "qemu/datadir.h" #include "qemu/units.h" #include "qemu/log.h" #include "qapi/error.h" @@ -35,9 +34,7 @@ #include "migration/vmstate.h" #include "hw/intc/i8259.h" #include "hw/irq.h" -#include "hw/loader.h" #include "hw/or-irq.h" -#include "elf.h" #include "qom/object.h" #define TYPE_RAVEN_PCI_DEVICE "raven" @@ -47,10 +44,6 @@ OBJECT_DECLARE_SIMPLE_TYPE(RavenPCIState, RAVEN_PCI_DEVICE) struct RavenPCIState { PCIDevice dev; - - uint32_t elf_machine; - char *bios_name; - MemoryRegion bios; }; typedef struct PRePPCIState PREPPCIState; @@ -77,8 +70,6 @@ struct PRePPCIState { int contiguous_map; }; -#define BIOS_SIZE (1 * MiB) - #define PCI_IO_BASE_ADDR 0x80000000 /* Physical address on main bus */ static inline uint32_t raven_pci_io_config(hwaddr addr) @@ -333,48 +324,9 @@ static void raven_pcihost_initfn(Object *obj) static void raven_realize(PCIDevice *d, Error **errp) { - RavenPCIState *s = RAVEN_PCI_DEVICE(d); - char *filename; - int bios_size = -1; - d->config[PCI_CACHE_LINE_SIZE] = 0x08; d->config[PCI_LATENCY_TIMER] = 0x10; d->config[PCI_CAPABILITY_LIST] = 0x00; - - if (!memory_region_init_rom_nomigrate(&s->bios, OBJECT(s), "bios", - BIOS_SIZE, errp)) { - return; - } - memory_region_add_subregion(get_system_memory(), (uint32_t)(-BIOS_SIZE), - &s->bios); - if (s->bios_name) { - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, s->bios_name); - if (filename) { - if (s->elf_machine != EM_NONE) { - bios_size = load_elf(filename, NULL, NULL, NULL, NULL, - NULL, NULL, NULL, - ELFDATA2MSB, s->elf_machine, 0, 0); - } - if (bios_size < 0) { - bios_size = get_image_size(filename); - if (bios_size > 0 && bios_size <= BIOS_SIZE) { - hwaddr bios_addr; - bios_size = (bios_size + 0xfff) & ~0xfff; - bios_addr = (uint32_t)(-BIOS_SIZE); - bios_size = load_image_targphys(filename, bios_addr, - bios_size); - } - } - } - g_free(filename); - if (bios_size < 0 || bios_size > BIOS_SIZE) { - memory_region_del_subregion(get_system_memory(), &s->bios); - error_setg(errp, "Could not load bios image '%s'", s->bios_name); - return; - } - } - - vmstate_register_ram_global(&s->bios); } static const VMStateDescription vmstate_raven = { @@ -417,19 +369,12 @@ static const TypeInfo raven_info = { }, }; -static const Property raven_pcihost_properties[] = { - DEFINE_PROP_UINT32("elf-machine", PREPPCIState, pci_dev.elf_machine, - EM_NONE), - DEFINE_PROP_STRING("bios-name", PREPPCIState, pci_dev.bios_name), -}; - static void raven_pcihost_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); set_bit(DEVICE_CATEGORY_BRIDGE, dc->categories); dc->realize = raven_pcihost_realizefn; - device_class_set_props(dc, raven_pcihost_properties); dc->fw_name = "pci"; } diff --git a/hw/ppc/prep.c b/hw/ppc/prep.c index 739526335c..982e40e53e 100644 --- a/hw/ppc/prep.c +++ b/hw/ppc/prep.c @@ -35,6 +35,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/log.h" +#include "qemu/datadir.h" #include "hw/loader.h" #include "hw/rtc/mc146818rtc.h" #include "hw/isa/pc87312.h" @@ -55,6 +56,8 @@ #define KERNEL_LOAD_ADDR 0x01000000 #define INITRD_LOAD_ADDR 0x01800000 +#define BIOS_ADDR 0xfff00000 +#define BIOS_SIZE (1 * MiB) #define NVRAM_SIZE 0x2000 static void fw_cfg_boot_set(void *opaque, const char *boot_device, @@ -241,6 +244,9 @@ static void ibm_40p_init(MachineState *machine) ISADevice *isa_dev; ISABus *isa_bus; void *fw_cfg; + MemoryRegion *bios = g_new(MemoryRegion, 1); + char *filename; + ssize_t bios_size = -1; uint32_t kernel_base = 0, initrd_base = 0; long kernel_size = 0, initrd_size = 0; char boot_device; @@ -263,10 +269,27 @@ static void ibm_40p_init(MachineState *machine) cpu_ppc_tb_init(env, 100UL * 1000UL * 1000UL); qemu_register_reset(ppc_prep_reset, cpu); + /* allocate and load firmware */ + filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name); + if (!filename) { + error_report("Could not find bios image '%s'", bios_name); + exit(1); + } + memory_region_init_rom(bios, NULL, "bios", BIOS_SIZE, &error_fatal); + memory_region_add_subregion(get_system_memory(), BIOS_ADDR, bios); + bios_size = load_elf(filename, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + ELFDATA2MSB, PPC_ELF_MACHINE, 0, 0); + if (bios_size < 0) { + bios_size = load_image_targphys(filename, BIOS_ADDR, BIOS_SIZE); + } + if (bios_size < 0 || bios_size > BIOS_SIZE) { + error_report("Could not load bios image '%s'", filename); + return; + } + g_free(filename); + /* PCI host */ dev = qdev_new("raven-pcihost"); - qdev_prop_set_string(dev, "bios-name", bios_name); - qdev_prop_set_uint32(dev, "elf-machine", PPC_ELF_MACHINE); pcihost = SYS_BUS_DEVICE(dev); object_property_add_child(qdev_get_machine(), "raven", OBJECT(dev)); sysbus_realize_and_unref(pcihost, &error_fatal); From 17612f972f02fcafae2c70aa96c9d2e7a4c6d36d Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 14 May 2025 16:49:55 +0800 Subject: [PATCH 1484/2760] hw/core/resetcontainer: Consolidate OBJECT_DECLARE_SIMPLE_TYPE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QOM type of ResettableContainer is defined by OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES, which means it doesn't need the class! Therefore, use OBJECT_DECLARE_SIMPLE_TYPE to declare the type, then there's no need for class definition. Cc: Peter Maydell Signed-off-by: Zhao Liu Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250514084957.2221975-8-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/core/resetcontainer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/core/resetcontainer.h b/include/hw/core/resetcontainer.h index 23db0c7a88..daeb18c1ea 100644 --- a/include/hw/core/resetcontainer.h +++ b/include/hw/core/resetcontainer.h @@ -20,7 +20,7 @@ #include "qom/object.h" #define TYPE_RESETTABLE_CONTAINER "resettable-container" -OBJECT_DECLARE_TYPE(ResettableContainer, ResettableContainerClass, RESETTABLE_CONTAINER) +OBJECT_DECLARE_SIMPLE_TYPE(ResettableContainer, RESETTABLE_CONTAINER) /** * resettable_container_add: Add a resettable object to the container From 8cab2354a2ff77c0977ac337a93ad6f645e5086a Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Wed, 14 May 2025 16:49:53 +0800 Subject: [PATCH 1485/2760] hw/hyperv/balloon: Consolidate OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QOM type of HvBalloon is declared by OBJECT_DECLARE_SIMPLE_TYPE, which means it doesn't need the class! Therefore, use OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES to implement the type, then there's no need for class definition. Cc: "Maciej S. Szmigiero" Signed-off-by: Zhao Liu Acked-by: Maciej S. Szmigiero Message-ID: <20250514084957.2221975-6-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/hyperv/hv-balloon.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/hw/hyperv/hv-balloon.c b/hw/hyperv/hv-balloon.c index 94b0abbd68..6dbcb2d9a2 100644 --- a/hw/hyperv/hv-balloon.c +++ b/hw/hyperv/hv-balloon.c @@ -67,10 +67,6 @@ * these requests */ -struct HvBalloonClass { - VMBusDeviceClass parent_class; -} HvBalloonClass; - typedef enum State { /* not a real state */ S_NO_CHANGE = 0, @@ -162,8 +158,9 @@ typedef struct HvBalloon { MemoryRegion *mr; } HvBalloon; -OBJECT_DEFINE_TYPE_WITH_INTERFACES(HvBalloon, hv_balloon, HV_BALLOON, VMBUS_DEVICE, \ - { TYPE_MEMORY_DEVICE }, { }) +OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(HvBalloon, hv_balloon, \ + HV_BALLOON, VMBUS_DEVICE, \ + { TYPE_MEMORY_DEVICE }, { }) #define HV_BALLOON_SET_STATE(hvb, news) \ do { \ From ea585b1022f7c1ac6e465aa1fe869de4c20ca943 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 23 May 2025 17:02:11 +0200 Subject: [PATCH 1486/2760] hw/ppc/e500: Move clock and TB frequency to machine class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Different machines have different frequencies so make this configurable in machine class instead of using a hard coded constant. Signed-off-by: BALATON Zoltan Acked-by: Bernhard Beschow Message-ID: <431166f96ff12ff3dbc670d40544974415f11305.1748012109.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/ppc/e500.c | 18 +++++++++--------- hw/ppc/e500.h | 4 ++++ hw/ppc/e500plat.c | 2 ++ hw/ppc/mpc8544ds.c | 2 ++ 4 files changed, 17 insertions(+), 9 deletions(-) diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 809078a2c3..dedd96b057 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -79,8 +79,6 @@ #define MPC85XX_ESDHC_IRQ 72 #define RTC_REGS_OFFSET 0x68 -#define PLATFORM_CLK_FREQ_HZ (400 * 1000 * 1000) - struct boot_info { uint32_t dt_base; @@ -120,7 +118,7 @@ static uint32_t *pci_map_create(void *fdt, uint32_t mpic, int first_slot, } static void dt_serial_create(void *fdt, unsigned long long offset, - const char *soc, const char *mpic, + const char *soc, uint32_t freq, const char *mpic, const char *alias, int idx, bool defcon) { char *ser; @@ -131,7 +129,7 @@ static void dt_serial_create(void *fdt, unsigned long long offset, qemu_fdt_setprop_string(fdt, ser, "compatible", "ns16550"); qemu_fdt_setprop_cells(fdt, ser, "reg", offset, 0x100); qemu_fdt_setprop_cell(fdt, ser, "cell-index", idx); - qemu_fdt_setprop_cell(fdt, ser, "clock-frequency", PLATFORM_CLK_FREQ_HZ); + qemu_fdt_setprop_cell(fdt, ser, "clock-frequency", freq); qemu_fdt_setprop_cells(fdt, ser, "interrupts", 42, 2); qemu_fdt_setprop_phandle(fdt, ser, "interrupt-parent", mpic); qemu_fdt_setprop_string(fdt, "/aliases", alias, ser); @@ -382,8 +380,7 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, int fdt_size; void *fdt; uint8_t hypercall[16]; - uint32_t clock_freq = PLATFORM_CLK_FREQ_HZ; - uint32_t tb_freq = PLATFORM_CLK_FREQ_HZ; + uint32_t clock_freq, tb_freq; int i; char compatible_sb[] = "fsl,mpc8544-immr\0simple-bus"; char *soc; @@ -484,6 +481,9 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, if (kvmppc_get_hasidle(env)) { qemu_fdt_setprop(fdt, "/hypervisor", "has-idle", NULL, 0); } + } else { + clock_freq = pmc->clock_freq; + tb_freq = pmc->tb_freq; } /* Create CPU nodes */ @@ -564,12 +564,12 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, */ if (serial_hd(1)) { dt_serial_create(fdt, MPC8544_SERIAL1_REGS_OFFSET, - soc, mpic, "serial1", 1, false); + soc, pmc->clock_freq, mpic, "serial1", 1, false); } if (serial_hd(0)) { dt_serial_create(fdt, MPC8544_SERIAL0_REGS_OFFSET, - soc, mpic, "serial0", 0, true); + soc, pmc->clock_freq, mpic, "serial0", 0, true); } /* i2c */ @@ -968,7 +968,7 @@ void ppce500_init(MachineState *machine) env->spr_cb[SPR_BOOKE_PIR].default_value = cs->cpu_index = i; env->mpic_iack = pmc->ccsrbar_base + MPC8544_MPIC_REGS_OFFSET + 0xa0; - ppc_booke_timers_init(cpu, PLATFORM_CLK_FREQ_HZ, PPC_TIMER_E500); + ppc_booke_timers_init(cpu, pmc->tb_freq, PPC_TIMER_E500); /* Register reset handler */ if (!i) { diff --git a/hw/ppc/e500.h b/hw/ppc/e500.h index 01db102625..00f490519c 100644 --- a/hw/ppc/e500.h +++ b/hw/ppc/e500.h @@ -5,6 +5,8 @@ #include "hw/platform-bus.h" #include "qom/object.h" +#define PLATFORM_CLK_FREQ_HZ (400 * 1000 * 1000) + struct PPCE500MachineState { /*< private >*/ MachineState parent_obj; @@ -37,6 +39,8 @@ struct PPCE500MachineClass { hwaddr pci_mmio_base; hwaddr pci_mmio_bus_base; hwaddr spin_base; + uint32_t clock_freq; + uint32_t tb_freq; }; void ppce500_init(MachineState *machine); diff --git a/hw/ppc/e500plat.c b/hw/ppc/e500plat.c index 775b9d8da0..4f1d659e72 100644 --- a/hw/ppc/e500plat.c +++ b/hw/ppc/e500plat.c @@ -93,6 +93,8 @@ static void e500plat_machine_class_init(ObjectClass *oc, const void *data) pmc->pci_mmio_base = 0xC00000000ULL; pmc->pci_mmio_bus_base = 0xE0000000ULL; pmc->spin_base = 0xFEF000000ULL; + pmc->clock_freq = PLATFORM_CLK_FREQ_HZ; + pmc->tb_freq = PLATFORM_CLK_FREQ_HZ; mc->desc = "generic paravirt e500 platform"; mc->init = e500plat_init; diff --git a/hw/ppc/mpc8544ds.c b/hw/ppc/mpc8544ds.c index 97fb0f35ba..582698559d 100644 --- a/hw/ppc/mpc8544ds.c +++ b/hw/ppc/mpc8544ds.c @@ -55,6 +55,8 @@ static void mpc8544ds_machine_class_init(ObjectClass *oc, const void *data) pmc->pci_mmio_bus_base = 0xC0000000ULL; pmc->pci_pio_base = 0xE1000000ULL; pmc->spin_base = 0xEF000000ULL; + pmc->clock_freq = PLATFORM_CLK_FREQ_HZ; + pmc->tb_freq = PLATFORM_CLK_FREQ_HZ; mc->desc = "mpc8544ds"; mc->init = mpc8544ds_init; From 3c32fa9fb2300bbe435fc417dbb521981e230639 Mon Sep 17 00:00:00 2001 From: BALATON Zoltan Date: Fri, 23 May 2025 17:02:12 +0200 Subject: [PATCH 1487/2760] hw/net/fsl_etsec: Set default MAC address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use default MAC address if none is specified by property as done by most other network interface models. Signed-off-by: BALATON Zoltan Reviewed-by: Bernhard Beschow Acked-by: Bernhard Beschow Message-ID: <8bd7ca691bd502b5fd761615d9af805e783fba36.1748012109.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/fsl_etsec/etsec.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/net/fsl_etsec/etsec.c b/hw/net/fsl_etsec/etsec.c index d14cb2a101..846f6cbc5d 100644 --- a/hw/net/fsl_etsec/etsec.c +++ b/hw/net/fsl_etsec/etsec.c @@ -389,6 +389,7 @@ static void etsec_realize(DeviceState *dev, Error **errp) { eTSEC *etsec = ETSEC_COMMON(dev); + qemu_macaddr_default_if_unset(&etsec->conf.macaddr); etsec->nic = qemu_new_nic(&net_etsec_info, &etsec->conf, object_get_typename(OBJECT(dev)), dev->id, &dev->mem_reentrancy_guard, etsec); From a35391ba87651daeafa02231e842ae28c349cac0 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Fri, 23 May 2025 17:02:13 +0200 Subject: [PATCH 1488/2760] hw/ppc/e500: Use SysBusDevice API to access TYPE_CCSR's internal resources MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than accessing the attributes of TYPE_CCSR directly, use the SysBusDevice API which exists exactly for that purpose. Furthermore, registering the memory region with the SysBusDevice API makes it show up in QMP's `info qom-tree` command. Signed-off-by: Bernhard Beschow Reviewed-by: BALATON Zoltan [balaton: rebased] Signed-off-by: BALATON Zoltan Acked-by: Bernhard Beschow Message-ID: <619a58d1f83d2aad5b4feec930d46c64abff0977.1748012109.git.balaton@eik.bme.hu> Signed-off-by: Philippe Mathieu-Daudé --- hw/pci-host/ppce500.c | 8 ++++---- hw/ppc/e500.c | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index e97a515d5f..52269b05bb 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -16,7 +16,6 @@ #include "qemu/osdep.h" #include "hw/irq.h" -#include "hw/ppc/e500-ccsr.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "hw/pci/pci_device.h" @@ -418,11 +417,12 @@ static const VMStateDescription vmstate_ppce500_pci = { static void e500_pcihost_bridge_realize(PCIDevice *d, Error **errp) { PPCE500PCIBridgeState *b = PPC_E500_PCI_BRIDGE(d); - PPCE500CCSRState *ccsr = CCSR( + SysBusDevice *ccsr = SYS_BUS_DEVICE( object_resolve_path_component(qdev_get_machine(), "e500-ccsr")); + MemoryRegion *ccsr_space = sysbus_mmio_get_region(ccsr, 0); - memory_region_init_alias(&b->bar0, OBJECT(ccsr), "e500-pci-bar0", &ccsr->ccsr_space, - 0, int128_get64(ccsr->ccsr_space.size)); + memory_region_init_alias(&b->bar0, OBJECT(ccsr), "e500-pci-bar0", + ccsr_space, 0, int128_get64(ccsr_space->size)); pci_register_bar(d, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &b->bar0); } diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index dedd96b057..6899802bed 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -931,7 +931,6 @@ void ppce500_init(MachineState *machine) CPUPPCState *firstenv = NULL; MemoryRegion *ccsr_addr_space; SysBusDevice *s; - PPCE500CCSRState *ccsr; I2CBus *i2c; irqs = g_new0(IrqLines, smp_cpus); @@ -993,10 +992,10 @@ void ppce500_init(MachineState *machine) memory_region_add_subregion(address_space_mem, 0, machine->ram); dev = qdev_new("e500-ccsr"); + s = SYS_BUS_DEVICE(dev); object_property_add_child(OBJECT(machine), "e500-ccsr", OBJECT(dev)); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); - ccsr = CCSR(dev); - ccsr_addr_space = &ccsr->ccsr_space; + sysbus_realize_and_unref(s, &error_fatal); + ccsr_addr_space = sysbus_mmio_get_region(s, 0); memory_region_add_subregion(address_space_mem, pmc->ccsrbar_base, ccsr_addr_space); @@ -1284,6 +1283,7 @@ static void e500_ccsr_initfn(Object *obj) PPCE500CCSRState *ccsr = CCSR(obj); memory_region_init(&ccsr->ccsr_space, obj, "e500-ccsr", MPC8544_CCSRBAR_SIZE); + sysbus_init_mmio(SYS_BUS_DEVICE(ccsr), &ccsr->ccsr_space); } static const TypeInfo e500_ccsr_info = { From bebcf2cff4091281ddf67240d5651ed7938ea83a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 30 May 2025 16:21:18 +0100 Subject: [PATCH 1489/2760] pc-bios: ensure installed ROMs don't have execute permissions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have been inconsistent about whether ROMS stored in git have execute permission set, and by default meson will preserve source file permissions when installing files. This has caused periodic problems in RPM packaging as executable binary files get analysed by various tools/linters, which can trip up on the ROMs. Tell meson explicitly that all the ROMs should be without execute permission when installed. Signed-off-by: Daniel P. Berrangé Reviewed-by: Helge Deller Tested-by: Helge Deller Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250530152118.65030-1-berrange@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- pc-bios/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/meson.build b/pc-bios/meson.build index 79bb2e1800..3c41620044 100644 --- a/pc-bios/meson.build +++ b/pc-bios/meson.build @@ -88,7 +88,7 @@ blobs = [ ] if get_option('install_blobs') - install_data(blobs, install_dir: qemu_datadir) + install_data(blobs, install_dir: qemu_datadir, install_mode: 'rw-r--r--') endif subdir('descriptors') From f37efe50d4a234f30cf6dde0bdcf4e75a798a123 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 31 May 2025 16:00:25 +0900 Subject: [PATCH 1490/2760] MAINTAINERS: Update Akihiko Odaki's affiliation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit My contract with Daynix Computing Ltd. will expire by the end of May, 2025. As I may contribute to QEMU for my research, use my email address at the lab. As I'm the only maintainer of igb and no longer financially supported to maintain it, change its status to Odd Fixes until someone steps up. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250531-rsg-v1-1-e0bae1e1d90e@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Philippe Mathieu-Daudé --- .mailmap | 3 ++- MAINTAINERS | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.mailmap b/.mailmap index 33fe75400f..e7271852dc 100644 --- a/.mailmap +++ b/.mailmap @@ -67,7 +67,8 @@ Andrey Drobyshev Andrey Drobyshev via BALATON Zoltan via # Next, replace old addresses by a more recent one. -Akihiko Odaki +Akihiko Odaki +Akihiko Odaki Aleksandar Markovic Aleksandar Markovic Aleksandar Markovic diff --git a/MAINTAINERS b/MAINTAINERS index aa6763077e..76399ad1e0 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2519,7 +2519,7 @@ F: tests/qtest/fuzz-megasas-test.c Network packet abstractions M: Dmitry Fleytman -R: Akihiko Odaki +R: Akihiko Odaki S: Maintained F: include/net/eth.h F: net/eth.c @@ -2549,13 +2549,13 @@ F: docs/specs/rocker.rst e1000x M: Dmitry Fleytman -R: Akihiko Odaki +R: Akihiko Odaki S: Maintained F: hw/net/e1000x* e1000e M: Dmitry Fleytman -R: Akihiko Odaki +R: Akihiko Odaki S: Maintained F: hw/net/e1000e* F: tests/qtest/fuzz-e1000e-test.c @@ -2563,9 +2563,9 @@ F: tests/qtest/e1000e-test.c F: tests/qtest/libqos/e1000e.* igb -M: Akihiko Odaki +M: Akihiko Odaki R: Sriram Yagnaraman -S: Maintained +S: Odd Fixes F: docs/system/devices/igb.rst F: hw/net/igb* F: tests/functional/test_netdev_ethtool.py @@ -2910,7 +2910,7 @@ Core Audio framework backend M: Gerd Hoffmann M: Philippe Mathieu-Daudé R: Christian Schoenebeck -R: Akihiko Odaki +R: Akihiko Odaki S: Odd Fixes F: audio/coreaudio.m @@ -3211,7 +3211,7 @@ F: tests/functional/test_vnc.py Cocoa graphics M: Peter Maydell M: Philippe Mathieu-Daudé -R: Akihiko Odaki +R: Akihiko Odaki S: Odd Fixes F: ui/cocoa.m @@ -3738,7 +3738,7 @@ F: util/iova-tree.c elf2dmp M: Viktor Prutyanov -R: Akihiko Odaki +R: Akihiko Odaki S: Maintained F: contrib/elf2dmp/ From ff1bc6f4c5be7315dcd3970f7bff6b4f986db877 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 3 Jun 2025 20:40:05 +0200 Subject: [PATCH 1491/2760] tests/functional: Add a test for the Arduino UNO machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check whether we can run a kernel that prints something to the serial console. Signed-off-by: Thomas Huth Reviewed-by: Mark Cave-Ayland Message-ID: <20250603184007.24521-1-thuth@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- MAINTAINERS | 3 ++- tests/functional/meson.build | 1 + tests/functional/test_avr_uno.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) create mode 100755 tests/functional/test_avr_uno.py diff --git a/MAINTAINERS b/MAINTAINERS index 76399ad1e0..a6f210dba5 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -219,7 +219,7 @@ S: Maintained F: docs/system/target-avr.rst F: gdb-xml/avr-cpu.xml F: target/avr/ -F: tests/functional/test_avr_mega2560.py +F: tests/functional/test_avr_*.py Hexagon TCG CPUs M: Brian Cain @@ -1236,6 +1236,7 @@ Arduino M: Philippe Mathieu-Daudé S: Maintained F: hw/avr/arduino.c +F: tests/functional/test_avr_uno.py HP-PARISC Machines ------------------ diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 557d59ddf4..e406451cd3 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -150,6 +150,7 @@ tests_arm_linuxuser_thorough = [ tests_avr_system_thorough = [ 'avr_mega2560', + 'avr_uno', ] tests_hppa_system_quick = [ diff --git a/tests/functional/test_avr_uno.py b/tests/functional/test_avr_uno.py new file mode 100755 index 0000000000..adb3b73da4 --- /dev/null +++ b/tests/functional/test_avr_uno.py @@ -0,0 +1,32 @@ +#!/usr/bin/env python3 +# +# QEMU AVR Arduino UNO functional test +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern + + +class UnoMachine(QemuSystemTest): + + ASSET_UNO = Asset( + ('https://github.com/RahulRNandan/LED_Blink_AVR/raw/' + 'c6d602cbb974a193/build/main.elf'), + '3009a4e2cf5c5b65142f538abdf66d4dc6bc6beab7e552fff9ae314583761b72') + + def test_uno(self): + """ + The binary constantly prints out 'LED Blink' + """ + self.set_machine('arduino-uno') + rom_path = self.ASSET_UNO.fetch() + + self.vm.add_args('-bios', rom_path) + self.vm.set_console() + self.vm.launch() + + wait_for_console_pattern(self, 'LED Blink') + + +if __name__ == '__main__': + QemuSystemTest.main() From 6cfe590c6b7876ba2471a4357274b33072d62214 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 5 Jun 2025 08:20:37 +0200 Subject: [PATCH 1492/2760] accel/hvf: Fix TYPE_HVF_ACCEL instance size MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: c97d6d2cdf9 ("i386: hvf: add code base from Google repo") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250606164418.98655-7-philmd@linaro.org> --- accel/hvf/hvf-accel-ops.c | 1 + include/system/hvf_int.h | 1 + 2 files changed, 2 insertions(+) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index b8b6116bc8..d60446b85b 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -366,6 +366,7 @@ static void hvf_accel_class_init(ObjectClass *oc, const void *data) static const TypeInfo hvf_accel_type = { .name = TYPE_HVF_ACCEL, .parent = TYPE_ACCEL, + .instance_size = sizeof(HVFState), .class_init = hvf_accel_class_init, }; diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h index 8c8b84012d..d774e58df9 100644 --- a/include/system/hvf_int.h +++ b/include/system/hvf_int.h @@ -44,6 +44,7 @@ typedef struct hvf_vcpu_caps { struct HVFState { AccelState parent; + hvf_slot slots[32]; int num_slots; From 59a4757bb4d01c9dfb50e872a9d04ad1f1c38049 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 5 Jun 2025 21:27:22 +0800 Subject: [PATCH 1493/2760] hw/core/cpu: Move CacheType to general cpu.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I386 has already defined cache types in target/i386/cpu.h. Move CacheType to hw/core/cpu.h, so that ARM and other architectures could use it. Cc: Alireza Sanaee Signed-off-by: Zhao Liu Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250605132722.3597593-1-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/core/cpu.h | 6 ++++++ target/i386/cpu.h | 6 ------ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 1e87f7d393..33296a1c08 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -1126,4 +1126,10 @@ extern const VMStateDescription vmstate_cpu_common; #define UNASSIGNED_CPU_INDEX -1 #define UNASSIGNED_CLUSTER_INDEX -1 +enum CacheType { + DATA_CACHE, + INSTRUCTION_CACHE, + UNIFIED_CACHE +}; + #endif diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 545851cbde..5910dcf74d 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1773,12 +1773,6 @@ typedef enum TPRAccess { /* Cache information data structures: */ -enum CacheType { - DATA_CACHE, - INSTRUCTION_CACHE, - UNIFIED_CACHE -}; - typedef struct CPUCacheInfo { enum CacheType type; uint8_t level; From c7785e243195799749242b5fc741b8fbf1053f82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 5 Jun 2025 19:56:52 +0200 Subject: [PATCH 1494/2760] hw/gpio/pca9552: Avoid using g_newa() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have pin_count <= PCA955X_PIN_COUNT_MAX. Having PCA955X_PIN_COUNT_MAX = 16, it is safe to explicitly allocate the char buffer on the stack, without g_newa(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Glenn Miles Reviewed-by: Stefan Hajnoczi Message-Id: <20250605193540.59874-2-philmd@linaro.org> --- hw/gpio/pca9552.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/gpio/pca9552.c b/hw/gpio/pca9552.c index d65c0a2e90..1e10238b2e 100644 --- a/hw/gpio/pca9552.c +++ b/hw/gpio/pca9552.c @@ -76,7 +76,7 @@ static void pca955x_display_pins_status(PCA955xState *s, return; } if (trace_event_get_state_backends(TRACE_PCA955X_GPIO_STATUS)) { - char *buf = g_newa(char, k->pin_count + 1); + char buf[PCA955X_PIN_COUNT_MAX + 1]; for (i = 0; i < k->pin_count; i++) { if (extract32(pins_status, i, 1)) { From 0ff9cd9a6af54ccaa293e252aa356fb150788099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 5 Jun 2025 19:54:58 +0200 Subject: [PATCH 1495/2760] backends/tpm: Avoid using g_alloca() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit tpm_emulator_ctrlcmd() is not in hot path. Use the heap instead of the stack, removing the g_alloca() call. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Thomas Huth Reviewed-by: Stefan Berger Reviewed-by: Stefan Hajnoczi Message-Id: <20250605193540.59874-3-philmd@linaro.org> --- backends/tpm/tpm_emulator.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backends/tpm/tpm_emulator.c b/backends/tpm/tpm_emulator.c index 43d350e895..4a234ab2c0 100644 --- a/backends/tpm/tpm_emulator.c +++ b/backends/tpm/tpm_emulator.c @@ -129,11 +129,11 @@ static int tpm_emulator_ctrlcmd(TPMEmulator *tpm, unsigned long cmd, void *msg, CharBackend *dev = &tpm->ctrl_chr; uint32_t cmd_no = cpu_to_be32(cmd); ssize_t n = sizeof(uint32_t) + msg_len_in; - uint8_t *buf = NULL; ptm_res res; WITH_QEMU_LOCK_GUARD(&tpm->mutex) { - buf = g_alloca(n); + g_autofree uint8_t *buf = g_malloc(n); + memcpy(buf, &cmd_no, sizeof(cmd_no)); memcpy(buf + sizeof(cmd_no), msg, msg_len_in); From 2f014eabc1acc64c403557f5a51fdb1ba43bbdd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 5 Jun 2025 21:20:23 +0200 Subject: [PATCH 1496/2760] tests/unit/test-char: Avoid using g_alloca() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not use g_alloca(), simply allocate the CharBackend structure on the stack. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Stefan Hajnoczi Message-Id: <20250605193540.59874-4-philmd@linaro.org> --- tests/unit/test-char.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/unit/test-char.c b/tests/unit/test-char.c index 60a843b79d..f30a39f61f 100644 --- a/tests/unit/test-char.c +++ b/tests/unit/test-char.c @@ -993,7 +993,7 @@ static void char_udp_test_internal(Chardev *reuse_chr, int sock) struct sockaddr_in other; SocketIdleData d = { 0, }; Chardev *chr; - CharBackend *be; + CharBackend stack_be, *be = &stack_be; socklen_t alen = sizeof(other); int ret; char buf[10]; @@ -1009,7 +1009,6 @@ static void char_udp_test_internal(Chardev *reuse_chr, int sock) chr = qemu_chr_new("client", tmp, NULL); g_assert_nonnull(chr); - be = g_alloca(sizeof(CharBackend)); qemu_chr_fe_init(be, chr, &error_abort); } From 61c4c2558df16262db34397cf50a16ad7b725731 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Fri, 6 Jun 2025 17:24:02 +0800 Subject: [PATCH 1497/2760] hw/virtio/virtio-mem: Fix definition of VirtIOMEMClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parent of VirtIOMEMClass is VirtioDeviceClass rather than VirtIODevice. This isn't catastrophic only because sizeof(VirtIODevice) > sizeof(VirtioDeviceClass). Fixes: 910b25766b33 ("virtio-mem: Paravirtualized memory hot(un)plug") Signed-off-by: Zhenzhong Duan Reviewed-by: David Hildenbrand Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250606092406.229833-2-zhenzhong.duan@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/virtio/virtio-mem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/virtio/virtio-mem.h b/include/hw/virtio/virtio-mem.h index bc4f787772..e0ab31b45a 100644 --- a/include/hw/virtio/virtio-mem.h +++ b/include/hw/virtio/virtio-mem.h @@ -134,7 +134,7 @@ struct VirtioMemSystemReset { struct VirtIOMEMClass { /* private */ - VirtIODevice parent; + VirtioDeviceClass parent_class; /* public */ void (*fill_device_info)(const VirtIOMEM *vmen, VirtioMEMDeviceInfo *vi); From 747a7ee3741fa9876583cbbbc8dd8b8b351a199c Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Fri, 6 Jun 2025 17:24:03 +0800 Subject: [PATCH 1498/2760] hw/virtio/virtio-pmem: Fix definition of VirtIOPMEMClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VirtIOPMEMClass's parent is VirtioDeviceClass rather than VirtIODevice. This isn't catastrophic only because sizeof(VirtIODevice) > sizeof(VirtioDeviceClass). Fixes: 5f503cd9f388 ("virtio-pmem: add virtio device") Closes: https://lists.gnu.org/archive/html/qemu-devel/2025-06/msg00586.html Reported-by: David Hildenbrand Reviewed-by: David Hildenbrand Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Zhenzhong Duan Message-ID: <20250606092406.229833-3-zhenzhong.duan@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/virtio/virtio-pmem.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/virtio/virtio-pmem.h b/include/hw/virtio/virtio-pmem.h index fc4fd1f7fe..9cce600d0b 100644 --- a/include/hw/virtio/virtio-pmem.h +++ b/include/hw/virtio/virtio-pmem.h @@ -36,7 +36,7 @@ struct VirtIOPMEM { struct VirtIOPMEMClass { /* private */ - VirtIODevice parent; + VirtioDeviceClass parent_class; /* public */ void (*fill_device_info)(const VirtIOPMEM *pmem, VirtioPMEMDeviceInfo *vi); From 2f8f01ae3d6b7acae187d700d7358d9bb248a696 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Fri, 6 Jun 2025 17:24:04 +0800 Subject: [PATCH 1499/2760] hw/gpio/aspeed: Fix definition of AspeedGPIOClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit AspeedGPIOClass's parent is SysBusDeviceClass rather than SysBusDevice. This isn't catastrophic only because sizeof(SysBusDevice) > sizeof(SysBusDeviceClass). Fixes: 4b7f956862dc ("hw/gpio: Add basic Aspeed GPIO model for AST2400 and AST2500") Closes: https://lists.gnu.org/archive/html/qemu-devel/2025-06/msg00586.html Suggested-by: David Hildenbrand Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Zhenzhong Duan Message-ID: <20250606092406.229833-4-zhenzhong.duan@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/gpio/aspeed_gpio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/hw/gpio/aspeed_gpio.h b/include/hw/gpio/aspeed_gpio.h index e1e6c54333..e6b2fe71b5 100644 --- a/include/hw/gpio/aspeed_gpio.h +++ b/include/hw/gpio/aspeed_gpio.h @@ -70,7 +70,7 @@ typedef struct AspeedGPIOReg { } AspeedGPIOReg; struct AspeedGPIOClass { - SysBusDevice parent_obj; + SysBusDeviceClass parent_class; const GPIOSetProperties *props; uint32_t nr_gpio_pins; uint32_t nr_gpio_sets; From 860bb8b925418f3ea55305da2a7387d8318ac00c Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Fri, 6 Jun 2025 17:24:06 +0800 Subject: [PATCH 1500/2760] hw/riscv/riscv-iommu: Remove definition of RISCVIOMMU[Pci|Sys]Class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RISCVIOMMUPciClass and RISCVIOMMUSysClass are defined with missed parent class, class_init on them may corrupt their parent class fields. It's lucky that parent_realize and parent_phases are not initialized or used until now, so just remove the definitions. They can be added back when really necessary. Signed-off-by: Zhenzhong Duan Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20250606092406.229833-6-zhenzhong.duan@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/riscv/riscv-iommu-pci.c | 6 ------ hw/riscv/riscv-iommu-sys.c | 6 ------ include/hw/riscv/iommu.h | 6 ++---- 3 files changed, 2 insertions(+), 16 deletions(-) diff --git a/hw/riscv/riscv-iommu-pci.c b/hw/riscv/riscv-iommu-pci.c index 1f44eef74e..cdb4a7a8f0 100644 --- a/hw/riscv/riscv-iommu-pci.c +++ b/hw/riscv/riscv-iommu-pci.c @@ -68,12 +68,6 @@ typedef struct RISCVIOMMUStatePci { RISCVIOMMUState iommu; /* common IOMMU state */ } RISCVIOMMUStatePci; -struct RISCVIOMMUPciClass { - /*< public >*/ - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - /* interrupt delivery callback */ static void riscv_iommu_pci_notify(RISCVIOMMUState *iommu, unsigned vector) { diff --git a/hw/riscv/riscv-iommu-sys.c b/hw/riscv/riscv-iommu-sys.c index 74e76b94a5..e34d00aef6 100644 --- a/hw/riscv/riscv-iommu-sys.c +++ b/hw/riscv/riscv-iommu-sys.c @@ -53,12 +53,6 @@ struct RISCVIOMMUStateSys { uint8_t *msix_pba; }; -struct RISCVIOMMUSysClass { - /*< public >*/ - DeviceRealize parent_realize; - ResettablePhases parent_phases; -}; - static uint64_t msix_table_mmio_read(void *opaque, hwaddr addr, unsigned size) { diff --git a/include/hw/riscv/iommu.h b/include/hw/riscv/iommu.h index b03339d75c..8a8acfc3f0 100644 --- a/include/hw/riscv/iommu.h +++ b/include/hw/riscv/iommu.h @@ -30,14 +30,12 @@ typedef struct RISCVIOMMUState RISCVIOMMUState; typedef struct RISCVIOMMUSpace RISCVIOMMUSpace; #define TYPE_RISCV_IOMMU_PCI "riscv-iommu-pci" -OBJECT_DECLARE_TYPE(RISCVIOMMUStatePci, RISCVIOMMUPciClass, RISCV_IOMMU_PCI) +OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStatePci, RISCV_IOMMU_PCI) typedef struct RISCVIOMMUStatePci RISCVIOMMUStatePci; -typedef struct RISCVIOMMUPciClass RISCVIOMMUPciClass; #define TYPE_RISCV_IOMMU_SYS "riscv-iommu-device" -OBJECT_DECLARE_TYPE(RISCVIOMMUStateSys, RISCVIOMMUSysClass, RISCV_IOMMU_SYS) +OBJECT_DECLARE_SIMPLE_TYPE(RISCVIOMMUStateSys, RISCV_IOMMU_SYS) typedef struct RISCVIOMMUStateSys RISCVIOMMUStateSys; -typedef struct RISCVIOMMUSysClass RISCVIOMMUSysClass; #define FDT_IRQ_TYPE_EDGE_LOW 1 From 3938180c9c58b61b38c7fc304eb48b903c7dfd87 Mon Sep 17 00:00:00 2001 From: Philippe Michaud-Boudreault Date: Sat, 7 Jun 2025 09:33:01 -0400 Subject: [PATCH 1501/2760] hw/misc/stm32_rcc: Fix stm32_rcc_write() arguments order MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tracing function for the write case incorrectly has parameters switched around. So order them in the correct way. Signed-off-by: Philippe Michaud-Boudreault Reviewed-by: Philippe Mathieu-Daudé Message-Id: Signed-off-by: Philippe Mathieu-Daudé --- hw/misc/stm32_rcc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/stm32_rcc.c b/hw/misc/stm32_rcc.c index 94e8dae441..5815b3efa5 100644 --- a/hw/misc/stm32_rcc.c +++ b/hw/misc/stm32_rcc.c @@ -60,7 +60,7 @@ static void stm32_rcc_write(void *opaque, hwaddr addr, uint32_t value = val64; uint32_t prev_value, new_value, irq_offset; - trace_stm32_rcc_write(value, addr); + trace_stm32_rcc_write(addr, value); if (addr > STM32_RCC_DCKCFGR2) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%"HWADDR_PRIx"\n", From 32e02fd3886c01a5c1c888ecd1e5035f851717a1 Mon Sep 17 00:00:00 2001 From: Soumyajyotii Ssarkar Date: Sat, 7 Jun 2025 20:57:12 +0530 Subject: [PATCH 1502/2760] hw/net/i82596: Update datasheet URL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change the asset link to one which is working from the PARISC website. Signed-off-by: Soumyajyotii Ssarkar Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250607152711.108914-2-soumyajyotisarkar23@gmail.com> [PMD: Split patch in 2] Signed-off-by: Philippe Mathieu-Daudé --- hw/net/i82596.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/i82596.c b/hw/net/i82596.c index 64ed3c8390..fc33a00d49 100644 --- a/hw/net/i82596.c +++ b/hw/net/i82596.c @@ -5,7 +5,7 @@ * This work is licensed under the GNU GPL license version 2 or later. * * This software was written to be compatible with the specification: - * https://www.intel.com/assets/pdf/general/82596ca.pdf + * https://parisc.docs.kernel.org/en/latest/_downloads/96672be0650d9fc046bbcea40b92482f/82596CA.pdf */ #include "qemu/osdep.h" From fb8449def4a22d8e65e9773c923174ae2c1db893 Mon Sep 17 00:00:00 2001 From: Soumyajyotii Ssarkar Date: Sat, 7 Jun 2025 20:57:12 +0530 Subject: [PATCH 1503/2760] hw/net/i82596: Factor configure function out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Abstract the configure function. Signed-off-by: Soumyajyotii Ssarkar Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250607152711.108914-2-soumyajyotisarkar23@gmail.com> [PMD: Split patch in 2] Signed-off-by: Philippe Mathieu-Daudé --- hw/net/i82596.c | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/hw/net/i82596.c b/hw/net/i82596.c index fc33a00d49..c1ff3e6c56 100644 --- a/hw/net/i82596.c +++ b/hw/net/i82596.c @@ -177,6 +177,26 @@ static void set_individual_address(I82596State *s, uint32_t addr) trace_i82596_new_mac(nc->info_str); } +static void i82596_configure(I82596State *s, uint32_t addr) +{ + uint8_t byte_cnt; + byte_cnt = get_byte(addr + 8) & 0x0f; + + byte_cnt = MAX(byte_cnt, 4); + byte_cnt = MIN(byte_cnt, sizeof(s->config)); + /* copy byte_cnt max. */ + address_space_read(&address_space_memory, addr + 8, + MEMTXATTRS_UNSPECIFIED, s->config, byte_cnt); + /* config byte according to page 35ff */ + s->config[2] &= 0x82; /* mask valid bits */ + s->config[2] |= 0x40; + s->config[7] &= 0xf7; /* clear zero bit */ + assert(I596_NOCRC_INS == 0); /* do CRC insertion */ + s->config[10] = MAX(s->config[10], 5); /* min frame length */ + s->config[12] &= 0x40; /* only full duplex field valid */ + s->config[13] |= 0x3f; /* set ones in byte 13 */ +} + static void set_multicast_list(I82596State *s, uint32_t addr) { uint16_t mc_count, i; @@ -234,7 +254,6 @@ static void command_loop(I82596State *s) { uint16_t cmd; uint16_t status; - uint8_t byte_cnt; DBG(printf("STARTING COMMAND LOOP cmd_p=%08x\n", s->cmd_p)); @@ -254,20 +273,7 @@ static void command_loop(I82596State *s) set_individual_address(s, s->cmd_p); break; case CmdConfigure: - byte_cnt = get_byte(s->cmd_p + 8) & 0x0f; - byte_cnt = MAX(byte_cnt, 4); - byte_cnt = MIN(byte_cnt, sizeof(s->config)); - /* copy byte_cnt max. */ - address_space_read(&address_space_memory, s->cmd_p + 8, - MEMTXATTRS_UNSPECIFIED, s->config, byte_cnt); - /* config byte according to page 35ff */ - s->config[2] &= 0x82; /* mask valid bits */ - s->config[2] |= 0x40; - s->config[7] &= 0xf7; /* clear zero bit */ - assert(I596_NOCRC_INS == 0); /* do CRC insertion */ - s->config[10] = MAX(s->config[10], 5); /* min frame length */ - s->config[12] &= 0x40; /* only full duplex field valid */ - s->config[13] |= 0x3f; /* set ones in byte 13 */ + i82596_configure(s, s->cmd_p); break; case CmdTDR: /* get signal LINK */ From 832cd70452e25c56309450fb10ff012513a7a410 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 11 Jun 2025 09:36:41 +0200 Subject: [PATCH 1504/2760] seabios: update submodule to 1.17.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit git shortlog rel-1.16.3..rel-1.17.0 ----------------------------------- Andrej Kruták (1): Add AHCI Power ON + ICC_ACTIVE into port setup code Daniel Khodabakhsh (2): boot: Force display of the boot menu when boot-menu-wait is a negative number usb-hid: Support multiple USB HID devices by storing them in a linked list Daniel Verkamp (3): vbe: Add VBE 2.0+ OemData field to struct vbe_info vgasrc: round up save/restore size vbe: implement function 09h (get/set palette data) Daniil Tatianin (1): pciinit: don't misalign large BARs Gerd Hoffmann (6): limit address space used for pci devices, part two drop obsolete acpi table code drop acpi tables and hex includes add romfile_loadbool() update pci_pad_mem64 handling ahci: add controller reset Igor Mammedov (1): fix smbios blob length overflow Jiaxun Yang (1): ahci: Fix hangs due to controller reset Kevin O'Connor (14): vgasrc: Use curmode_g instead of vmode_g when mode is the current video mode vgasrc: Rename vgahw_get_linesize() to vgahw_minimum_linelength() stdvgamodes: No need to store pelmask in vga_modes[] stdvgamodes: Improve naming of dac palette tables stdvga: Rename CGA palette functions stdvga: Add comments to interface functions in stdvga.c stdvga: Rename stdvga_toggle_intensity() to stdvga_set_palette_blinking() stdvga: Rework stdvga palette index paging interface functions stdvga: Rename stdvga_set_text_block_specifier() to stdvga_set_font_location() stdvga: Rename stdvga_set_scan_lines() to stdvga_set_character_height() stdvga: Rename stdvga_get_vde() to stdvga_get_vertical_size() stdvga: Add stdvga_set_vertical_size() helper function stdvgaio: Only read/write one color palette entry at a time docs: Note v1.17.0 release Mark Cave-Ayland (2): esp-scsi: terminate DMA transfer when ESP data transfer completes esp-scsi: indicate acceptance of MESSAGE IN phase data Max Tottenham (1): Add LBA 64bit support for reads beyond 2TB. Steven Price (1): vgabios: Fix generating modes list for static_functionality nikolar via SeaBIOS (1): kconfig: fix the check-lxdialog.sh to work with gcc 14+ Signed-off-by: Gerd Hoffmann --- roms/seabios | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roms/seabios b/roms/seabios index a6ed6b701f..b52ca86e09 160000 --- a/roms/seabios +++ b/roms/seabios @@ -1 +1 @@ -Subproject commit a6ed6b701f0a57db0569ab98b0661c12a6ec3ff8 +Subproject commit b52ca86e094d19b58e2304417787e96b940e39c6 From cba36cf3881e907553ba2de38abd5edf7f952de1 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Wed, 11 Jun 2025 09:43:13 +0200 Subject: [PATCH 1505/2760] seabios: update binaries to 1.17.0 Signed-off-by: Gerd Hoffmann --- pc-bios/bios-256k.bin | Bin 262144 -> 262144 bytes pc-bios/bios-microvm.bin | Bin 131072 -> 131072 bytes pc-bios/bios.bin | Bin 131072 -> 131072 bytes pc-bios/vgabios-ati.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios-bochs-display.bin | Bin 28672 -> 28672 bytes pc-bios/vgabios-cirrus.bin | Bin 38912 -> 39424 bytes pc-bios/vgabios-qxl.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios-ramfb.bin | Bin 28672 -> 28672 bytes pc-bios/vgabios-stdvga.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios-virtio.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios-vmware.bin | Bin 39424 -> 39424 bytes pc-bios/vgabios.bin | Bin 38912 -> 38912 bytes 12 files changed, 0 insertions(+), 0 deletions(-) diff --git a/pc-bios/bios-256k.bin b/pc-bios/bios-256k.bin index 48c3707d55d3884c54efa9b3c70ab5c3382139c0..509f3988e49dcf37028a1c88ce9204a0956301d5 100644 GIT binary patch literal 262144 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

    i zJV9;@5-Lxw&^X+#82!Ez3z>@eGyY# z|J6!q4W*I85|^slY2 zV3XQJ&c4Ct6l#)rV^XNWSFkr|O$hNkIQEYY6HQrYRglu_ih6N+v&d6i;{Umn`4h_2 z>n@Lo&59SH`NzE0?ht*mixc^Kh;@>83ckm|DviS7uQykN&3`fr7 zD;k{m^T#CB!bB>Uq>@Tomyjxko6jffeZTyfTg+-~uD$R-w2&`Z&V%3nqdT6=!wAr; z-+~RBPLWmT&|kYA;SfPc63e;R=lE5lHZFQ#I!)&wT>w!C_*R;|v%4TT(K{n%6R z#?rLran5B}pRV9RgfoChuBieRF zS=wpYWf#Z#0QZxs#4LIHJ8!S}d17@j1&WknQObhO904u*+t@BGYl@YF}{%M$q#9GrAJN z@(Sx=5{WfbCWmdqRX@Q-wZld@s6ur2wP3$jHF`&vPe@s^a25j zRlRBu4rc@+I}=v4oBI(zqAk|l9ceCs@Yi$&xb3YwOs*#Y-1q3xq3 zPS&*gon=g?Eolu*@xJ1#r$s6Vtd}?Ig&X;hblOKehDL8g@oB*;%FO}IyqAYg3|u}~ zuiI@WG)Rb#n6ub#MkHtb52X@YK4EuK^3`k5w64U(ZoIBmcGG2Lmq$6)PjInC_sGh8 zsJs?Ks6{I-y0*BvFx}Zw=o)){(WI6_cS%w4jLjnJYrRVVDErmWloq3AhEu(j* zcMOXgi|rxt{yc`=9Hx-zTCf+>{&X!}nU8*9aV&!c*CJ0aL;2ZaJ>@Xa?5p)xw~tW) zyFcGB4AKU-b8j1JoXFokvb%6=*|o~!g`_=s15T4vt!A=gozquSlv1;;!AfIkW_qjh zNVxkz=o3fvc9v^9TQhgm6v_Fq=^U4n0g7)f)Z}%19eau^X~!CmrTlFB+i$(~mhoX| zw5T5~a87uc?W3ZcF_eR+)1kE#rEuy!4|h#^Ufvk{xJ{dNj#MMvF=Q_XpO^{UEW_P3 z6C5oQQm!r;L*fY}4o|SDS>OE+?VsRE(Rz{q;c7S)9+N_@q2c)+rONp14K3y@gfCZd z$|usw6%p3b+vyv$q=_GlNwK`qa_QAC6DEh8owllOxBAajrwY9&XNK=^TS2#5PJ-Ek zk#$#K860q)*N)gRE&76-F{CgoQ?I^p+tSilH>h`1K=qOT_Dv-ZEQ`EwBf07;dfdyo366`nh|P}YhQf4$8KEoVw$8z(j*bkNG%fCf^jZ- z7IyFYF_NFhNf4>dFe!zP0SixyoHdgb(VEE_T+HdkRhjhWqKuT7D|9R+m>S}@$;BqA zC-@S6X|g49wG^d6g7YOsnNr_wT1}Cgm+o2|m!TtE;u&&a$l=Blsi`cy%8hyFlp8bT zs``(p$Kdf9XsBsrjbR<5$Pz}e`dfj;G zQZDt_8|OM(m(p$rz9)|P%(dP`X(Wy_Op&7i*HBw)7qe)rP>vC38BbP@BBQ zM~5HhBm!5}xW~yFtr>dvTMK>)(*%S3cdPglCR2lm(+uirRJi= zdR-Mq-&M#$<*uyqt>?8kT;;1!ztgd$+pHa|+vhJ3N5UmS1(O;HU5wF*L@gXG#}k%& zBg2FzV?-r?g4ZJ|M*)m`Re9xxU5a7?swZIG!IVKCtki3#`)&yx@K9bA;@Na~D3BvhA$>esc@xIFY)_mm(w; zrKTH&obtT+4@+2!AIY)R`TTA4BZEUJ^g>bS=oZW!))>)uvQX%v^x>x4T15fMz$nAO zSSUf#U1=xRQXVQ61upi?jl~qb1PA0tcxQ$1f_cJ*JEeOT`~2Z~Iqa9nnGJ42IAMyl zSkl>>8G$@@RCddbN{+pwQpAo*c;4AvCpc;t3H#LU4JBMgPPWq7iOG}08td3f;RO;+ zF})?Vi$bU=y=DeSIL32?gWRGe$o=g{UFDvm@ycL@c)> zoXz9Cj_|d;BfafPE$9sHO;Pst3#lh{2KR}FuRSYrmYq7kGx)4H^BAJ<nU~p&1&Z;t8awJ+7uH2xWe21`-eUk2dF}p~AOmPwzvmq4;sO(3 z=jNv>qzy-`YH?AT1DcBGDq;^2T`hiZqRhhSq)wWf-%e#N?TpDHrdvctqMc_{XG}L@+{(#n$DGp{<4y@r?jGshna5?v zq_M%WsC(x|sVe9Y>tCA%r)`v~nBAQ*z1UPtY`dwLO`S2>7;db^0;v-ibQ2hK&Gw*c z8?yJbPR5C&PD~XH5c_lKg#Q?UN)bK_Rhi3z=i++zs}t|r(Qs-IZyEAU!E`l<`73I- zDE7kXE}GOUWZA|S>$oKKbh~^7$C)Osug3NsoItQ`>#hMWQ@(W-#o5FCVP1{H`qL-8 zQgBPUM`wriI{O1J^(yT)JOPSH6WBh#F0p;CVxH}cVf*~L#P+$0xwkVWJzcHYIO!c9 zsI5qorC^pZAp^sfd-&zXFS_%@Y!_IxG6zVR#AYS<|X=u@DjV!h|R_9ZJ1+oQop6D_;ZON9%Z#ngp;OOx)IXWTs zATyURNmk?w>t&bK>IaQE^Pw@$UQjn45O=D-Ma$*`53F$~IU7I8y2dDWvAEGZ7>9)( zKCSp)+d;OFa;M4nOh=1%4MRFnqCIbRmipMfM9oXohN0P8HVlnM{pp>Zr8}})3Xz&Z zt?}cmN_H5o(u_&}?<#Q`qipprXKk!}ic}4q{BI5QW^3MmZ)i`A(rGQzU7?ru_U`C6 zuibAlnTe2gwD+<^dxJwxFFfG8vD98YzJyPB4wA1|%c<>X9ob(K&w%v%nzzE^6Mf!B zpCj1evW`CIBs;^`O-S<-!dFK3tP0cPHipqMnXaj>0|K?mW7U`*_5SH(m7-(Ggwq%lwHJS z3|>XCaD(b~esF|+UzV~W!&XSHyMdRF7AN)2l-s*>A+F3oM zIkXq4tGX0Ek;Kotn@wSUya8|Iq#Vk6Ur0WMVHb7SiQJ7=M%ei}FI?Gu6&mUcb|UpB z5;}`=qPv+Ws5>VgWXsAK_B-oOA|Ds=4Edpi4RwfJ=GO1NPm+#5$u_lK6{1sDWQ$3g zD|ZyC_CYYZm;|zIT=h2@9rGpmZ6m@HaW1+ZbS(^RPjW+9s zS7@Z%OwuEt3P)y=G#X%yVB!hsI3BA$L(J9Gg4=~sKt50MXIAC0?gkfQoxZph{WCdi z0yheQ-w1)*SSP@Z09a|tL3Jzk7bz?}egv=9hw`$86;c6a)D`9g>KnMkoGn`exp-O6 z)obSkjAM>{OZ%EXD7ae+Udl&#;uU->nR7Dn>U{w{vQ4U7DJ2e~L`Qp=WhWl@eH^gn zb2AR!LO9M5Uss4|dPV&zx{HF?}ZA;9l1_wsfXlR zVz`tq17_%V_U2h-#lKShhF+bgenpI4_ZwO6i;}=U;WLg| zWkl(L^r2h*^!Bqlaxhfg&bGq8irn_7VFFMq=2(1z+5E@#l&s}wt#$` zKKv}ZtFsFe-vFUI?6tr0>#78cNicgB3ErhJ=7uuH+gv*PM#0R|aFMInf3wtaBz5F4 zNGPr?&t%8yQNs8f*>`68=;c3K8VGm^dsWh9t8{Mp7o0TnI%jx{oWWe)Ybx=r5`P%Q zL~CMx6IbbX9Lo!}_gmq?mD^ejuXY8~R3O&|(m7~!bufdc?@5Kd$H^2FF?2H4LFXw& zCx^~Q0|$D@j!AFhK>g!Gs87MTv**0>2&ww*nkLmvv$^N;+OV-e8<#8qy6l=gNW=0kP8%lB@s zcJpDY9+8`c{AWm^(G9Q9kmxt!vkm;z_`MgyDcH6*B)1VeTd zmspRHhL7`Y^>EXmCG;pMNwGy9F^)T{zY+6*X#b? z@txX9=rX-_PU34gTOUzYr#BLiR;;HL_NSTt@>jM@S7I!Lt5W4(R5(wLw{4Vwu5T^b zVuxq8&3fP=D#dLR9#2A1>KC}sx*sAud?a3VzG10-8>i2W+&!`lxQ7F^Tm{Ocb7Px3 zB{_7^2URA0^dBEdmfhnCo?`#&^zBVOIgNU5iIu_5Bnq%uig}$qY6{4(hEh^2FElzA z?dn+T;o>kX;i{5DuiDBJ)#_d+b)(Y$f+DMT3cwVJJj(> zJB$@q=+pN2ij(%Y(>XnledZr*lGjPzh{kKUx{bNY`d|{R9U$FiZ)h($IV*JPWMHs4 z!RuSg4P^e^htQf_bW}Jj=`l>(tN?;4k5okUP!Nfs zH~Ld9gTYnVq~Yp`eax9mz5t(@)+suUy+-w?l%Urw>*x5e@=7I^gIcN&5(WuziDlyg zu;}#$quL_WzBHklfxNtLZN^c+_4AlomU%+kQ{`M)y8SgWe;=x+3_9zD7not9yCG?_ zfSgq-C!d&NOX|9Q^*u;ij!5z`sPo!5pXg{eUzGwIzwl%p;}g|FbRirouCIWK^G)sA^Fuwb+k;KX$wMu<$UtmVQWldOrNqcKeeAeCV+LS?Q!_eos04vo7o}zb5Z5 z$UEPkL>t~}f1Q=X7E9E*Ci_0j>`fc7c@tN`svB+)w+dAL{Ean!7NQXF3tJiyr>FYcFV zoG|Hj7saKj;_|wbROLZAi)-+$tRjj37dDkz!$kbhv5%|&7TV(T;wuXeVHMQNda`n> zWD~s<{eu|%O)9+=W|yK_`>}i(q{tgC8V27`xs#Y9Dpr+idX>~29$Dv$l4f|LQCw^n z2fLN}=TdxB%<>NXA8MVzzt|xM1SH2MawvbMyc~PfwlgFAa^{}gVaYAmy(M?3HKp^U zE!X+4v+rm!@HikhhQ=28>{QPGl8Vh?%{o;msRoA^UAIYcG3+htfC_UPC2DCJYAsZm zWe3QGJEEVK67$*Rhy#W65z=$_q90l$g@2l)uAw-3oEZBoj>k@Ozn$jnO;lV{w@t`c zX$R)of!TJ-#dcsGo&aaJ80SEIPW-q-dJTRcaM3DpN$c!<7rYnsGzhIVm_i_=OPp&BJsF)=XjkA%rn=lbqu~BDqz{7145I zazBaWtKjhEo|A##kbxi(&k>;pg1ofRY4ZC#zYGLd zssD27#J^4$zGM{%u%vc zg{e^@dH-{eTz1AFxsT-Zf;~t$K_ee%TO&1{Guf7sClK5Cvcd6XzW6e?Uu3e>|5r)y zBNCi3maef$ojI1SumjH=OL=x6GWIo;kg=lg;5D|AuhgG*cfv}>v*9hZB68HT&3Z;u zuq}K2o`(G_*BMO1D!1lzbm>p&72)^e{E8rb^cXo1q|QEnhXBlbFZu)iaO_>yLb4r{=R5QZf zI})b$*60x`;-yX^qHL7wq=Se};I}q`yU01JSS^8WI@=oYE%k1)#@wSujkG{&Y<~@t z6#)F_pfdKid6zGTm+(tI(Jql<{=lcZ*;BybC_xM5z#))4q0o z%qpK(s8#vC<*nSf%Xdsmlo^~Uv7;hfMo-%{903Il(d+JFt` zen*?B6Q}$57vQYhpMWQL)cDfE6S4}B(QG}rQMohR*yr)+Z*dPx%05UIWJc3ren|SQ z%W-3=eH3g`tn2D|Bs4RuFGsP-WThHS1?%~6%X;XK6#HTH z7ZkudpYR-4=DMR*>_7bO&fTrQ%d|h$#V4Jd!4GUZJddNM9{EQba`_OyN3Z%WjjrF1 z?_;=+COc!b*10TyxV~|h>XOjgahAkIdfn5C`?mV5^2w6VYT}qT%lsMssW6yTrJLST ztI9vUhN#nflIUJWgIZ;Db;&R?>2>!iB781jKDpJhGyJs5GQ%n-KhxZ-MAn?p_wnH1 zihnr*=c=4`@x_$z|FHKi;89lB;`ckrgj@)j2oVEK!Sh-6AdIF$So3XiW)EdWBWf++d>G63Bd%=DqvOAs)&}p!+@Z+0>Luh zZ|!#`35adK=lP!RdA{!{I(heAd+qz$Yp;D-Yaf?BcRWG@#@ySKloT9e$|gc?rfhOf z?E=y_SGLv&r`WFX%s_@?n~&>Dx@hr#)ltEI~Kb|IHURxj*U#e~EfrZ|~BMO#~Y>8~9 z-BRG_5{i36sAe$ID3{SDHlek)dzdiF7;|4=32ayvQKPpjFIgx>l2|TDOb#cJv{6kC zqv#H?n3`%TgIcg_cgUC^iggBE>=Xha8!tXe`TDlXWuTkv=VGAUznC=kE9QKdKfN!0TI~E zv=@`MzcKAcpc_|`EC#e>G6y77<$ppEmGR|GESKa6_Il+EBgrFR-ZqOUK7^&j^GJ!Q zOU&+@D-EzT&y)d#--evY5dpx&SSc8jz@QS-&C6b5u*JC9A08U!{j%OFeog^Z74M3J z^Uvm9Vt(3u?aD|sp;0L`ocn9Fz0$00IT8nqhUd4QkXMql_9N)bHKzTSI1k6g@3(FJ zotR(N;yYdAZCf8t`w4DTY_Ij_CCba5yy&#`tnE3B=bb*y#wx<66Onh0$iH?qidgOqxv=N-2?frX9E!4&WcVu?Bbm(u2Cvf_wh=6lO9XQ$UrKTJQ?rN2ax8-ity& z&vo0DIJQe!N+=7rGc(nl8KT>Wmw6&Hw3HPyI~%^w*nYoltT4 z#Ih*Vh(Q>d-#^8dofbT`SzeZHl|vob)TUwOkD*UuHV_~e_~rc7C?*l(2_Jt}a7Mr= zOT;KsA)8NuV>HeEV&^r9qP8Uc2guv#tNVEiwl&HFNL^gq8H1SA)y9uP*iLrM!(JwWR@jT!a%HnIs?lB?)Gm*;>FhW`Ln=W?)iW<=G)Q+M4CIt$B&! z?@Sl6qRwNgnXb7}Sy+^oe}M@nWa6d9o9xwd3rXe1yH&xfF}*#%PhmlMUZG$=m!f&u zL~4~1P~NeW7mg(GT%sBo;Zo;fGeo^_W3KjwP$c8~l!gY)HBMy`7f0rMH{YF>>zvS< zOE6_$X3RBFmKlE#6fv!K_hTsX9whL-JJ3LRk(o8`p5UIA`=l#A{smn@8lZG^JStO{6xrVa6Er!0deh!UwgJZoQP6zQ`buVOD zWC3iAG~bOfi&O1o?6ke`75C4dmKZNQW;WBa0(o8_&xRpC5{Se$ekM##PK^^=hshG> z=KzEDF6|7XuSMoX;}OLM!ANP16AY0Tji3L3NoXOdv?`qM&})%5Dq2INs z#(bS3=0yyU{Z{0O3*M**B#@5oV~v^{#fE;HHAf?k`$UT!%+`+&u^rqe(0eA=eX^yE z7Hti1MA0`W=der)Ct5m=0N<=ua$Pap$>x}#Wf3ez-<*P*wjHcsFP5E>`02SOaJmAv z%sV59`6g_p37e*1vrO3SChYvjf`=R=2_h3qCRu+GSb+&+ku9)0Roq1;Y>5dQX9ynM zC~@yHVFynN>~0fwuL%oD-8bK7!d93tIhWBlmzpr$gjJmvn8SqKZ^G_8E-;xU$wNJ! zd8);Ok5j2sY2zPDTYb&}!hlb3sdK;9yX|Mh9faf2s8+cd(V~47uNhB+ z&>166i#bR@P$3MGU;GflIuuIM(&bI@&3|O#*wP~7gOP)!=s4!B%kP7-x4ak`MJlxY zuyrKUgBHKP_dua5Mt1q-XrU`cL}_ugmQgu*DA2}%i1%!?Yak~2IH^G$Ww1{@zY6KeXP*9K^nVrwoEtNOSX3%uX0Fc}D;f7}@4Y{uYw&ijfB+s4cf# zh$0T#)|=Ez_XB*Nd%6?c(Ye2|+!j2Bh!?wAaDy=l3vC?ZRwl{KmE_BK8PWQxoI&;{ z1;b@ZD7$19Z!;s^=J;4LUpWOyAoI&j5~(?Ri*JNHu;}WaonUKBW(znkcoN0xSmS>@ zlreTDi;hzgYZ9?&QuHk`klS)bK$S8-b6Q|)JgRCT?`Qn0wGiB%T3C%W^x|fDbwkyn zMQZUq(lq|p6fC|%#-SD}(j$(3-y`#1AnGs6MDtH#SaE4JfstDDTc6Au=y9HI zyDK&~h(H%)YsAHqnHbesVm1EyEoP%7amI1=O*B5?8>&kT)y0MCtf9${!IDun8F{;s zWexnEdt+Bj+A}t7#@k35$rP}~j?egfEH{W^uE*K(%9}C>-lnf12C?*Jc%<#Iq$&PdbOmPD{1~zq0YkD8S?|^9cC3XlBehtAH)T{6g+S=5viN7xDSU zk(yQ{7lyH4I#j0Jf}T*bG(QI6&gEYclI;4yC&f)2ywit0qNBxIwj9l)?9;cz9i48= zWUc%5C&ka$UER3ZoNhXFv{;_J8*^$%x39Be-;+O`%tL?tIepZ!Pn~qK*b5tJSU*4> z<6#9K*Mtp>%n%nML#WLvh53{`b6p6k<-jquBNzP8$8$cegr?yt+TCe!G20NOceLdi=>Ajx4tV8SC9<>?EX!=NNu0FcOBkjs;{w?|X zI=3_`znC)Nq+#X31dLZ$cC9>^?K{Q$mCW)h4<&ey+OULY*}t+ayJ=r9;zu^iKFj_J zZiUddsu_;Nh-#JD2=bbPs>Akh9X{JEZ!d0nUh;eTaTul}S4v$tv6nLL6hQQ&49YE# zPJ~__5a@}uKz}VzC4}y6X%gt$0+l2G2=31W+Ah#%z%`z}mid~Q`>&xExo~2-dcgz@?uyB#lchkNF%xiE0uPd0 z>&rce-!l=fXnmyzvo>0FrV-`O|Cq7%H87Nq}F&k)=bPR$jDx<>m<29t`8?C zPZQ8LT&{~mi{3nuY=XBszMvvT)&wb>WE`W4DPd5MRusHS&w0$_PQAGKsbeNM*l<+j zK)1@v9>Fq4+7l#qjByZA>c3Zv0L&AueowSJ2gFlPgo~I_=(t_5UjX|;P5$+bvT_+@ zo-dv!fR}ZCe+2W7y$XFUSKxmMbGv zU-wPY`EjAWy+#WnmH;x-pX zZ;AdC1Cm=TpE?Xl;lD{C^2qAl#_O9h{C&piu7#!qRX0$ng4Y^`%+o}iq9~HaK$5@4x zsz#DFS`MLlZnoIg$t#xY+RKxMw-HuKIb;MLjyg zT|n(6IId4{Ho>vpb1`dgT)gy-g{ayYttdM1y<;AN~ZUX zWBM&(lkeS@UtfuT8N1+N%NkpZ(tnn`RV6>GZ}QV4$^TN4w~#!0PLlkS*W*9?cyIC| zM8XY?4-qrv`I0wU&cMEn{dg_dtbU_KCNrbG*DImVhrJyIj&~)4rx5x#iNxdi zi$A1q7N7Avt4>4Iiz2g;+RV8T0#wSbjlYEixjgC~v*~VF0{qbKouav~bbKa>&LL5U z{Rg-b7|T4KyAJHu-JCZdGF>C^WA!wAS0wF80(x6Oo$ErY=fiE<$v)TpB~`pl!t&*a zp`)eqyNdRre22^&2f*URZkq_ zjdkpUNy(d+ow_G8;kfoi+kK7eXSSW|r94@CT=CP*f&`B+Air(KeUi9hf&XbI zTs!1@C{8_?DUeM-?lsDs(lrE32)DP1AGyCzh;})y#?Y_nK*h{41xy9~I z^UqK5?P)inylq+DJxZLU*}We{TlT(j#(OwgSq1gaWZNIUN*bI2z*zlj?Xq#h`*}~a zi38J5Pwa}{&jmQXar@mXWQR>yDOdDB$B}Zn6hqwbT23rDZ+)9hEc@vrSu+#;^OLb8 zn5mLWu16(FQnS|g*S(=zZ9l{)NAL>nT&CEb!&pJXSqt)+?Z<7oL8h^$nM3?*DHsvB z2zZs0NzSJIYZLjT_?IMSqigdY)RUL98sC*K3OCv4+ib?onRBvl51L$wttS~%fmeaC zlW$C!3`wC*Ic&P=QBU)!(sjNr-GpkKkAq_zFTiD7K1I<|s5E?QN{)Fz`19ecrf1I8H%UpPsG9pSCOs>ICkFwkAK#!2Kq`)T z0EY5qY%xDzg-wR4ok_k$V|*HqvY_`bwle&Ui7*iVjO4(d&%s*Ek!aX~sd&a zsJZIBIh71T%^ki0AlGQk^M|txOBNuBt%nzp0~au~;PB0Y+k$k(MSmCr!I!n}mSDGl zK4ozo9JSd2j%j%3?O`kcv|M%`xyFZ1DvQw&s+#hVoiW)Z@C}gd1EYpLLtlrw`0aal z%r4_6#KcC;;%A|6^i~cDImhS7mEF3SR-x*qLY|8nAe(3CO8WLZd4JSSO-s1fWY~Yd1ASz;h!A*n_tYf#QAyX@KG6ialUTy$|NrWLN$q@nz+KyBoUk) zTZN*=A!=^=u}Vd*d-uc8q!#>=^v0mo;lDR=Oh?1VVzrOEq89QkG-AlN(DA;+{hYIEswQ76gOyPq7O@dsNDGHBsW)t zUh2p8llSk|`dEMV|Cz-Z+R{515b80uh3r?Q{E$S~k?2fsM~Zj1J@~7xm}ejdp^#R& zSfEkQdl-0z25^IDR}7GG9KeF@82ghRx+k z^y%^JfGZwS6t@r|v-Pbh!Zl7c&twbb3{Uae$kkjaw;9)}utSOxKuy7m7=e%^+CBa7 z=?$U34z;a&4~k~|vn)J-%R4|ULtB^&ZW`!H;?p+9o#I$RC4(`JY^m#$X|fix6)thC z67PDvbTXSU!wM0{hb?7CW*DM)D!JT1E(|$QDVxGKjc+zzJKx;S{Iq;t9?krcLC1S^ z!wY*NnwvEz*&T!pq+3KBCL?6p@fqlNw=fJZjOZ4nc*=+bH!(V z*`m@<=*_iNvfeI#f>$YMW1CqTJEgHT4k1O~uI&=5pm3o&j4N;?7cykF08)|_cS2;H z(KK9)VP~G$LA+eXJM*I1L73tc(`c%f5DC|@gGsNX-gRfzq1r+YjKo%G248vcbRu6d z-#CT-%9gt<8B4fw9h9s#BE+@YegqFWWo%+5^P+jv=1%YpKTfvvOfpIVP*kwdn3YzGLM_(xb_DtTE+LIF~DJV&9+RK`F;z z$^okhe4p&@w|p&Ph>e%Nk;LuzUy1m>od&^{CE`pj`Q($&EZRlMT4)))i~gq_laXm8 z?mef~r)Gw-Vzgy8ZOGfsv3|5__G}hk&@Nk~3$kM5AaK)H{26k_ndnoeem-T?U`8N{ zB5e7cZ2HMr;_t;FXNZSW<9G4E1ANhKXV*0x{2Mbk0fDmp@IVtISyOqz*m)~WthpPb zrCQ3eh2wpxOfr>$!-roPvI?X9N=`Df*)_O7q%5(<2rvO&X)W{&eGrm>};-P{0H2-P2~9wszDi^q~>IEM-Nt1vi~ zM~YIQs}5#Vs}_vsl14jD_S84h)JD>yq0oZBBHCg*CrZpQHAoCkNXTDlAeJ{*Hz(f6 zB3R~4<0C*m%yV;xK<4E?Q(>6(i@Ee-!wInGq@nw-0v|xM21!Ojg4>_DfpVXf(mVS%r^Gd! zm7JV0tpd+%3msI<+IJvuq&pPq<*15}bJ3_hun)L7v&NaP-m{T5OX{#2G4P&cy7xSa zV~ZKCxRC!n;$p5?*M!6GjMDvu3BLV0#@ux}-<|l9w3=%BV8Tn%-b!-A4#f=a46%HQ za$UyXXx9+^c3Ay;0nrlio$>CDQxm_n9NrbuWx39Z{7W2J9X79Ba5@4*__QCjuKRLr zmw#nHwWsyvE~{&>?S>i{NUeptB zIPtg{=qh1GdAm$;&}BS#zR0_p*?qpxkrJHkpA&_Va9iW&=S5`mTfhG>P3D&!5!=nV zaqhp}JZrT3lz-W<;0TU2W85DE_JZioi!y#edJN4r#QX4D_Te_{&^MOuOwv~IyZ4{` zrVu`Z-$H)FI+C=z_&vz)Kl#1L?}fLLv^IWk@H@osefb*Z?*QMvzw)D||J&j3KEfB^ z{*dq9Fih~@!#&_HN!p6=ua57xAvZt2C-^fs(%8N_ZgmG5YxQbU$4>N|ovA%Ydv$n{&s)h(snaZMy^3rf$bvOQCV7~s^rvq+eVe5ZwqN9)irC1!j+f! z%C9@i%FW2CPGwc9X;zi3T2U;WD)kA&DymJ8g4NVkuP6)Cjud7^b+yav4YQ&`Bh%@n z+sdj}mC;F(pMrt6pce80pqj3_hT&I5YiMS9#073?Sxsg2nn){2Se3K4(ab!)1}mcX zh#YBJ$&Yd=I(QTMf195O@bB?`pPvZ(eQ{NzlQ*y;($*_#DN1db8XLtzltoo#_0-u_ zT%ppesB)IoLT*zgWN5-aAb}cT7*4oJah0}Fi?+t}i;asfDU}p!2$fdSHp;*#t*xk& zgu_I&qS{?mQdYFWtpj_os-~8jh;!p(9!%&D*8kcUJlH6(HqKTfuu&f;Nzm8+ z3EL4|5mh3%pS@|uPk!L*u+%Cp<3-el4f*fkfyBs~cirEK{= zG4w)yD(LbrA)$YNdB^@RbnpqkZ(`Az6K8`6^z2-rEBh#hon@gpkGP8J8J3QtYM^9e#FYdlG9~7VYV8xjaO3r*hSaOet02)5k z3^B2yG|diLPZTOSy2nF+Rns#1#*fjCzL8|M$5TD|9`14BaPTYVr}qUaccLb8sNBSr z4J8q|MAW*}zf09S0=pFcKZcnx|1nPA&`3^~{-V#)eEU}%MGV3mhT~2|*u_4(|LS`> zkOezBY&#Ez>;%Yjmz}-?o%w0~MI|obr37+NE6UT}T^UV-yl3OUYmb=`f32~tc0bkOT$I$nCyW{I)nipmS6Pgz;MKdS5x!@-9!Z1gGTf!^} zxH;~7L-v7Zd(ZZ(kL7jB;}Bu{v0+%@X$8agZu_xR?-3jCX`V9vCk#l50Dcq(Bu4<8 zE~zMq5kOrSfHt(52BMt8utor~lJS0Q!yX@5Q1Cz=fQXQ8zCLJYLypTY!<0zhevCKm zX9mxV;hC_r>)u;Ca1It`P#ZQ{L|ViVJcfLV=*uaG?^s0G9!R2vEUMnfY+IjVnVJ&q zN>uwyk0f%NAsCz6c+mZq%(mqnh5q3!EC@M|XC>9Y{cxh?4NMrs1+PLCqT$F_^hW&t z0sQr-x42HWew8Twb5b6Xr5z_l;(qB$l(-*QI8~1eUVG`t-jAc2zh@?(pX34Ai<3~< zlSETo@S1N*A=*r#Y0sx5;2#jlpF2g8{{hP$zt(Z-xT@$UI(!HGmq%h9G;7TD9&T(% zG~d2U)YRi4CN|BoU^n~{%qY?3SVXe)b-WMySUDIxPD_v`E(rTq9# z^8Oqyqb>Z&kMFwUCz<GA2fkGfl zNZ^#YbCoUNQ^t0nSo4?L)vOQuq={X5m4u|}F>#v#VB5c;U4y%Wt8V3x#@VmPivwt_ z^`JuJPodpwz~KdbKuyJU%?FO(QILwt->mfh9+tU>)%(gD8x?M7<*vEgbV z+|>TY$MM6yFBbZP!x_t5?@O?|25o1*omVnEuUc&x+oPE0Z4=m$K;PH*XNpxwS3@0J zYcNfRjlAjE(XI#7W8hz$f5oLey=N4KM^=bnkRhi7>C(q zJrNvzLK!|d9{3Ef!WZn}_3>a|Y1L9+QK1ms4P&i6YCr70@#m^fbdbwnro-n4C< zMPn71jg@OPI@#0dQx{*rm;%Iih8GL^Hqx~U$f<6mkuLY0^PpfnoC^6qQ$tep%xv3U z1E9RuON=jUTVII)?gk(s@Jt(b0Brkwdpc};I=ne@-ot|wTfTD?6NX10Fb{xaICRJa zi(a|^TZyBoQYbgd^UzJ)PW68)m7AJRE-Wy&s|%!hhXNcIakptQoBTh(<$rz{IJW1U z6V~Sqf=EX|q+`zSv90@02~-Klew5{N+Va~yplD};?&}v^14#{}s+XB0m1I11HWVUE z4E_mF=qkyu0W|$WNJ&Zu$LKyIt?(Twpcy74kwm=%LqKoF3dVW~^S#o3Zbac7rrr+n zFE0_R!3ttWc&A=-r`j@YTh*1=3rz)fY&m!|?~`1*=#yOk<&E4rW3q|g1A+0D;C?=t z`?L1TgD|kB%@W*U*_F9BYriWdDlRv8x@k`b7Z77_@3bas!M`EtwEKUQOat4tc?R=g z|NS>szWzmTSp84N;eulvwptE-nVV?g{?)eJq~J;2|6~dwx^G#M?)$ECh8iUH@(#~x z`Y6%tn)|3(aD->|WFX$Q6vgB+4+aN#+eVuVk8>a`@Jq%=c5?k?t-qnbaHM0t{ipYH z=G38NjE*MeQ+0&LS%S;*Df6`4iDlnwa^XL?LECc+xS1Jd#}=&QtAmC8%R8Vu=&Jph zSa$*KHWIm78^C-Vkx9=(}PKYBbeqaK1&V{>-vA$9_#wU zwBR4)fyq7L_w2pL<9u(}w(V*ED9-m*!%-#wyw>oVB=Mp=M(R7|J7x+&CxoRQk{i`+ z){zRYy#*H?+h zeeXWQ^$vI3_Rm)^xI-T;4{%7^BYwV&Nr+c$JFjTO7^*xO;prY}TX!vv=6Xw~HO-z0 zALg>meh*^yP~H!*W@otKSME({o@?13><30h=H8jX_c9M=SGotTpJ~ai6wx(q{mdv# zgFkP{E_FE`ot{0;*0=#~@#v6jQg6f6I+BLNl)%w1P(x}5kiE>-DE2wCOKj_Y#uu5N z;4TSXAYiAh5n~V9qw(1_t|fs-1mt#QX0Nt23JVTY0`T9Ah#TYWu(E^N5w|Y1CaIOH zXOfGDWKMtR>kcOSqx<480T_lu^0-CC_pG%PxR=iKbCdF|`%N1qAZ!Prb*h=tWZdho zXgkIfcd!@Zhk_yJ&S0nD_Fv&W63clcrV{XRb^UR|S~E<9FFpr(wdwu~Zjy-;UpMIU zK`#mx(-`KHsa<|~AjfLkX-#7VG@d6enM*myXONBd2I7_$5a+kVS*nsia*+4H;{mWj z3SLfAU@v580q{iN>w(|7oEbS-D2+6q+B)5{W;SD=_{xjcS{b0vp=_+RRIs@M8wzxi z361pMD7?P&!6@Ik=Czi<@8FLS`b6A`BV%ie198d_Nv!*?!N0RUIh-S#By|anE|zx2 zo6urwD)n#q6G}%$ENq5FTx@5IcrBeVAIL||zhXeIL!?!|M=?A^SpN>0bZpza!rU-5 z#Cd76^$#Q_r&)6dq!5w=Nln17lTryX{J zS-vD9a5E>-q9krAF-+NA^@HG>85jJ$R4bY`4JSrLE$w>Ow8Uu5ALX+kS5LG0r(1o~ z;?yiOO_Jl7Y>I_@(wxIWXPRGLFgka~HZ=uF*(8syvZG*wm~lZ%Hf>+L znTAIivVCQYC=JEL{DqCF9dfx>rq1SJaYix0N<*g2m`wh`Pf7X(iE>6HoG4@;bL%0S zR+3ZO)ndvmg`vQ82-d3pJLGPnde4$Qnn6*tP6~iD8f~-)L4FH987yOU|I(qhZPOCu zoN=G+xprU~V9T}hiC`;2m#2ZByKOWLHCQBr%P8b$t+IuewQI*8^Rz2BisG%p(;@^4D|%PX`GX3PL(TY^BoHQ)Y1JdO7;NM zM#*5y-?+T*Pm#EVE~XXa?Y;p~?xBwRyu_VE+^(2h*7PzTEDP-6Z?E6ajX7qY94miY z9(z-+!=ibcc+9i!)Mg3sT`nP;PVw5G=T*yQ`F6z=L?1q{u}(q*S6q$aN)2bw6{87k zMj|kACC@}@OvgTsaY8K#58XUm%HkT~8=1n_j7X8RFE*aCwWdolTJ0CXcZ`o&A^BA4 ze5!PzN%F)?p~EMYx(^CM@=zSS2r~2!S9ZJKvo&rdsfLHFVi4T6?!U!zvsekYt@|(B zevJG)G)zNlCNEhI!QJ!Jau}xZX~UFdh53uzN7-Ap?2<=W_dDP9XLwgzHTSjL02|S8 z%gkwc1@0@BIOOYiUP|#U#7ZfAUj$J{j0?WnFomoZxhMBYW3)dNN{?%}Sz@_j5a@$} zLNke1B#9rINd&QytEgOdhR}n$kHSKKt;e9hQq%pvxP^gli3I)xfV!EZZE<`;S`I@J zoJoRRF|rHEzb(>0@_dIWAeD-MRQXp1@{+<$qSnksd|qMgkFjp!ZHWKQ;N{ze!9RKW zY=Ofr&~_OTcPU)sKZG<{p-V)rLG@8EIJ7;yqWY|%dnu|}bx8(q7>0s>GOm)(S;u@y z%S~E532x5)#*<7G#>d~$G)2^-Wv>!NZQ<~{3K$Iqj-QJi-_=NI5q_6s6nmmW!I+i*nh* zuvQuLrXNWw%4i6u1qW}sJ8iI}Vr}F79_nQgo<7vlTG6d00%H&3fq^~B;)-iX+E-FI*~tXNV52yz=szus;E?1xdmzo|u-CkScYJBxq3EBf(eCZ8h z;dBqaz>WNAo?eq}u|1YAp~7}N-Hm~9c-9x7(;ekX^mIqNQJ7na;CwHVOY9MXFP3#~SD0om;M#G@{&5TSk-g6yF|W6?cfq z8U@b0gvSmGMco>lV7xB4>m#^LCNB4RCWf&QZf;?dC&3tJ0;b!x%^MmVNd8J(7>PtI*6#t=A8 z@KZ_3_z6&z)h9SOK3bGklCUUkh5L%&NKbc->x#M|jR#i`4U)6|| zj3(Q*ngm#_ZQH`3w&&iG#fn8+%ad2R2~eGEx(A0~rZPr^jW$=3G-uXZZXOKeaUh5- zywqdB8$Y6|6_<;Y+r$gexPW7JWT3QifT^`b2%GdZF1CLq`i!%9|G{R^` zQwrp1zl=sv_o?L}j1=QKPKTj3hN0YM<#GL&BY=bLL{(x?QI8wr6eYwwX?BN39dJ zLtoAdY@WhhW-~xzbjHc;a-BJ*wJnyseV4yVC>jO(_~54$^YzWI;o$_Xz?Z&TTnkcs zuOB$+JM(l0KJP~s-8WzlE_oz)zPBs7J*1|!ek?iU%#1;3+jgw|&sN`99`F(ErGA=A z7yC^8yxsP-F|BsV2!3-FKc#9bBa3p0>X*8PteW(X2n5nZUxH0wvOb0cIHk5zur$lt z-ZzyIfAUO}o!$OiTTTi72W=Fixd$}QJpA-7N*H*V$le^$hS6+|zr`a6(e*PrV zp2~d1G=WCB2C6{TVO=r%3ANaQDG}$uplDqvXj*5?K|&VvZ z-z<3e*Lz;Z`?O%w{4r0r;|NYxCsx7nJ7|3^IC=;4ss*p!p@#j?9gJ`-IA{l^AGBcN z4z;q2*&$0Du~sXzt(IXj-)F@fXK`-Z@SX%>Mf_z=$wkqw<&W}AiN!TlT`S*3sG~zh z({qd_HF33gfCbZYUrB_uM8Ft6q8Gx!S*Ew;!FvC&H?3sj z_&^Zu0fF(}NI%J=wY)+6rGRGwMhABe+ucH1YTLcxh(;bPurRtWr{}hwi$l| zysY8ec~!To=m?`3elF{5e=dB{Wowv%Y+LA#_1B=D#P$9O66+<26*x&GV;Te1n8udi zrUF@RwXJ67tIc@)2!KU>05=O@ZXdwg0+`hYaF~3(Iby*=b6vYh>;RDcP~&5=!f_d5 zvv3X-VEhs(Y?C}h)V%C7@43%h!~7fzZYvb+^dw|EV>*%i%WdAEZCUU`<1>7{jdT1c z$H9)-QjIgR8+8hU9y0x3-~K6zc&HVq`N{ZC7+$Y9nP7Cm(eEvClu5ic;%$0{J08m^ z+0EuIS+x6V$DMLld@}R{F0KGXI@g5;8f%L0Jh7a*w;7c zw||8&-4Kw#oxdipmwt`wi=Sg}M?kG-A^R;+pKj^*>8r;vPI~yPcTd!(#;^ZOjl$FR zgs<=EG!v|*ZR-~sgP&3LJ^fTdpBhey*kNu4nGw}mLH1cJ9me7d;=Stly;O7sD-?!U ztF|!?ySQoWlblIC8j8W0cBbaxHCW zT)U5eYzeqa0_F%_nYgCm+9ak3b2rOh)@XrbhLvM?UIl1Eu{7{5D= z4z^>JB=%QIWIT*Nq*l?(?&=slfAQS8W3&?P4`xm-s;(=mt*t06v)5O+9QKN;y5h=; zQoGYtT(UADjC0ALrPCJ9pFRIJdpdECZA}`qSZS{{Zn+u&G3XM%ky@@0c-f zac?Wo9A&k2W%iQd8k$({N84xT+e@pU}$)`FVdWⅈ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⁢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�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 diff --git a/pc-bios/bios.bin b/pc-bios/bios.bin index 7e2d062af6789d4d055e8d064312897f8b424a79..4b81a969a9d63b6deac2a38ec5729625dae87d12 100644 GIT binary patch literal 131072 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 KDkB3X@qYkQnTv1$ literal 131072 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

    DhU(^zh*t%WHr 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 diff --git a/pc-bios/vgabios-ati.bin b/pc-bios/vgabios-ati.bin index e10cd263b8112d25e533df1c3c9771cfdb0a6ddf..011359e7c853b40727c706f1b12a0bd8c7ba7c71 100644 GIT binary patch delta 14751 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 Y%agNaJoUt!yPnR-nz@Gmam(WW2K=iyh5!Hn delta 14829 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 diff --git a/pc-bios/vgabios-bochs-display.bin b/pc-bios/vgabios-bochs-display.bin index 416036d98689ff7b567f34ad56a0b22f08ad6be3..1d233afe69c123417397453d3c6f1f55a82b2743 100644 GIT binary patch delta 10142 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 delta 13754 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 diff --git a/pc-bios/vgabios-stdvga.bin b/pc-bios/vgabios-stdvga.bin index 0d541c5530d1a1b186e43eec06b494c149ce824a..5b48ca860af0da4d43127c45e3b33e088212e345 100644 GIT binary patch delta 15018 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 delta 15269 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 diff --git a/pc-bios/vgabios-virtio.bin b/pc-bios/vgabios-virtio.bin index 2ce35576243f4d70927491b596fbfadd8bc325a6..f580c33794a5c5be464719ecb563b1eaf468b07d 100644 GIT binary patch delta 15018 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 delta 15269 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 diff --git a/pc-bios/vgabios.bin b/pc-bios/vgabios.bin index ee748f6599de37f90a6273c8e9bace7bd74a7e03..3f71aae1715b2f3082fc973297d8193fd5163526 100644 GIT binary patch delta 14868 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 Date: Fri, 30 May 2025 13:54:54 +0200 Subject: [PATCH 1506/2760] travis.yml: Remove the aarch64 job According to: https://docs.travis-ci.com/user/billing-overview/#partner-queue-solution only s390x and ppc64le are still part of the free OSS tier in Travis. aarch64 has been removed sometime during the last year. Thus remove the aarch64 job from our .travis.yml file now to avoid that someone burns non-OSS CI credits with this job by accident now. Signed-off-by: Thomas Huth Message-ID: <20250530115454.187727-1-thuth@redhat.com> --- .travis.yml | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/.travis.yml b/.travis.yml index 8fc1ae0cf2..0a634d7b4a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -79,41 +79,6 @@ after_script: jobs: include: - - name: "[aarch64] GCC check-tcg" - arch: arm64 - addons: - apt_packages: - - libaio-dev - - libattr1-dev - - libbrlapi-dev - - libcacard-dev - - libcap-ng-dev - - libfdt-dev - - libgcrypt20-dev - - libgnutls28-dev - - libgtk-3-dev - - libiscsi-dev - - liblttng-ust-dev - - libncurses5-dev - - libnfs-dev - - libpixman-1-dev - - libpng-dev - - librados-dev - - libsdl2-dev - - libseccomp-dev - - liburcu-dev - - libusb-1.0-0-dev - - libvdeplug-dev - - libvte-2.91-dev - - ninja-build - - python3-tomli - # Tests dependencies - - genisoimage - env: - - TEST_CMD="make check check-tcg V=1" - - CONFIG="--disable-containers --enable-fdt=system - --target-list=${MAIN_SYSTEM_TARGETS} --cxx=/bin/false" - - name: "[ppc64] Clang check-tcg" arch: ppc64le compiler: clang From 6ad3a47f15624176434490e895ac244375322607 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 19 May 2025 07:47:44 +0200 Subject: [PATCH 1507/2760] hw/s390x/s390-virtio-ccw: Remove the deprecated 4.1 machine type With the upcoming release of QEMU 10.1, the s390-ccw-virtio-4.1 machine will be older than 6 years, so according to our machine support policy, it can be removed now. The V4_1 CPU feature group gets merged into the minimum CPU feature group now. Signed-off-by: Thomas Huth Message-ID: <20250519054744.36715-1-thuth@redhat.com> --- hw/s390x/s390-virtio-ccw.c | 14 -------------- target/s390x/gen-features.c | 4 ---- 2 files changed, 18 deletions(-) diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index f69a4d8ed3..ce3c13defb 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -1145,20 +1145,6 @@ static void ccw_machine_4_2_class_options(MachineClass *mc) } DEFINE_CCW_MACHINE(4, 2); -static void ccw_machine_4_1_instance_options(MachineState *machine) -{ - static const S390FeatInit qemu_cpu_feat = { S390_FEAT_LIST_QEMU_V4_1 }; - ccw_machine_4_2_instance_options(machine); - s390_set_qemu_cpu_model(0x2964, 13, 2, qemu_cpu_feat); -} - -static void ccw_machine_4_1_class_options(MachineClass *mc) -{ - ccw_machine_4_2_class_options(mc); - compat_props_add(mc->compat_props, hw_compat_4_1, hw_compat_4_1_len); -} -DEFINE_CCW_MACHINE(4, 1); - static void ccw_machine_register_types(void) { type_register_static(&ccw_machine_info); diff --git a/target/s390x/gen-features.c b/target/s390x/gen-features.c index a814ece82f..8218e6470e 100644 --- a/target/s390x/gen-features.c +++ b/target/s390x/gen-features.c @@ -884,9 +884,6 @@ static uint16_t qemu_MIN[] = { */ S390_FEAT_FLOATING_POINT_EXT, S390_FEAT_ZPCI, -}; - -static uint16_t qemu_V4_1[] = { S390_FEAT_STFLE_53, S390_FEAT_VECTOR, }; @@ -1049,7 +1046,6 @@ static FeatGroupDefSpec FeatGroupDef[] = { *******************************/ static FeatGroupDefSpec QemuFeatDef[] = { QEMU_FEAT_INITIALIZER(MIN), - QEMU_FEAT_INITIALIZER(V4_1), QEMU_FEAT_INITIALIZER(V6_0), QEMU_FEAT_INITIALIZER(V6_2), QEMU_FEAT_INITIALIZER(V7_0), From b894cc23f8fe6681b8bfc9679a6df1c976e9757a Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 3 Jun 2025 12:34:49 +0200 Subject: [PATCH 1508/2760] tests/functional: Use the 'none' machine for the VNC test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The VNC test currently fails if the default machine ("pc" for x86) has not been compiled into the binary. Since we also can test VNC when QEMU just shows the default monitor, let's avoid this problem by simply using the "none" machine (which is always available) here instead. Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250603103449.32499-1-thuth@redhat.com> --- tests/functional/test_vnc.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/functional/test_vnc.py b/tests/functional/test_vnc.py index 5c0ee5f927..f1dd1597cf 100755 --- a/tests/functional/test_vnc.py +++ b/tests/functional/test_vnc.py @@ -31,6 +31,7 @@ def check_connect(port: int) -> bool: class Vnc(QemuSystemTest): def test_no_vnc_change_password(self): + self.set_machine('none') self.vm.add_args('-nodefaults', '-S') self.vm.launch() @@ -62,6 +63,7 @@ def launch_guarded(self): raise excp def test_change_password_requires_a_password(self): + self.set_machine('none') self.vm.add_args('-nodefaults', '-S', '-vnc', ':1,to=999') self.launch_guarded() self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) @@ -74,6 +76,7 @@ def test_change_password_requires_a_password(self): 'Could not set password') def test_change_password(self): + self.set_machine('none') self.vm.add_args('-nodefaults', '-S', '-vnc', ':1,to=999,password=on') self.launch_guarded() self.assertTrue(self.vm.qmp('query-vnc')['return']['enabled']) @@ -103,6 +106,7 @@ def do_test_change_listen(self, a, b, c): self.assertTrue(check_connect(c)) def test_change_listen(self): + self.set_machine('none') with Ports() as ports: a, b, c = ports.find_free_ports(3) self.do_test_change_listen(a, b, c) From 3f11e5f4009f32261806905c37b2d260a9a194f0 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 3 Jun 2025 20:47:10 +0200 Subject: [PATCH 1509/2760] tests/functional: Speed up the avr_mega2560 test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We can simply check for the expected pattern on the console, no need to wait for two seconds here to search for the pattern in the log at the end. While we're at it, also remove the obsolete "timeout" variable from this test. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Mark Cave-Ayland Signed-off-by: Thomas Huth Message-ID: <20250603184710.25651-1-thuth@redhat.com> --- tests/functional/test_avr_mega2560.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/functional/test_avr_mega2560.py b/tests/functional/test_avr_mega2560.py index 8e47b4200b..6359b72af3 100755 --- a/tests/functional/test_avr_mega2560.py +++ b/tests/functional/test_avr_mega2560.py @@ -18,12 +18,10 @@ # along with this program. If not, see . # -import time +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern -from qemu_test import QemuSystemTest, Asset class AVR6Machine(QemuSystemTest): - timeout = 5 ASSET_ROM = Asset(('https://github.com/seharris/qemu-avr-tests' '/raw/36c3e67b8755dcf/free-rtos/Demo' @@ -40,13 +38,12 @@ def test_freertos(self): self.set_machine('arduino-mega-2560-v3') self.vm.add_args('-bios', rom_path) self.vm.add_args('-nographic') + self.vm.set_console() self.vm.launch() - time.sleep(2) - self.vm.shutdown() + wait_for_console_pattern(self, + 'XABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWXA') - self.assertIn('ABCDEFGHIJKLMNOPQRSTUVWXABCDEFGHIJKLMNOPQRSTUVWX', - self.vm.get_log()) if __name__ == '__main__': QemuSystemTest.main() From 23c90999f64598439990f9a599d6cb25df90eccf Mon Sep 17 00:00:00 2001 From: Alexandr Moshkov Date: Thu, 5 Jun 2025 11:59:09 +0500 Subject: [PATCH 1510/2760] tests/functional: add skipLockedMemoryTest decorator Used in future commit to skipping execution of a tests if the system's locked memory limit is below the required threshold. Signed-off-by: Alexandr Moshkov Reviewed-by: Thomas Huth Message-ID: <20250605065908.299979-2-dtalexundeer@yandex-team.ru> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/__init__.py | 2 +- tests/functional/qemu_test/decorators.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/__init__.py b/tests/functional/qemu_test/__init__.py index af41c2c6a2..6e666a059f 100644 --- a/tests/functional/qemu_test/__init__.py +++ b/tests/functional/qemu_test/__init__.py @@ -15,6 +15,6 @@ from .linuxkernel import LinuxKernelTest from .decorators import skipIfMissingCommands, skipIfNotMachine, \ skipFlakyTest, skipUntrustedTest, skipBigDataTest, skipSlowTest, \ - skipIfMissingImports, skipIfOperatingSystem + skipIfMissingImports, skipIfOperatingSystem, skipLockedMemoryTest from .archive import archive_extract from .uncompress import uncompress diff --git a/tests/functional/qemu_test/decorators.py b/tests/functional/qemu_test/decorators.py index 50d29de533..c0d1567b14 100644 --- a/tests/functional/qemu_test/decorators.py +++ b/tests/functional/qemu_test/decorators.py @@ -5,6 +5,7 @@ import importlib import os import platform +import resource from unittest import skipIf, skipUnless from .cmd import which @@ -131,3 +132,20 @@ def skipIfMissingImports(*args): return skipUnless(has_imports, 'required import(s) "%s" not installed' % ", ".join(args)) + +''' +Decorator to skip execution of a test if the system's +locked memory limit is below the required threshold. +Takes required locked memory threshold in kB. +Example: + + @skipLockedMemoryTest(2_097_152) +''' +def skipLockedMemoryTest(locked_memory): + # get memlock hard limit in bytes + _, ulimit_memory = resource.getrlimit(resource.RLIMIT_MEMLOCK) + + return skipUnless( + ulimit_memory == resource.RLIM_INFINITY or ulimit_memory >= locked_memory * 1024, + f'Test required {locked_memory} kB of available locked memory', + ) From ec3a7a7150462b2d1be5b71c2e4fc6338366da25 Mon Sep 17 00:00:00 2001 From: Alexandr Moshkov Date: Thu, 5 Jun 2025 11:59:11 +0500 Subject: [PATCH 1511/2760] tests/functional: add memlock tests Add new tests to check the correctness of the `-overcommit memlock` option (possible values: off, on, on-fault) by using `/proc/{qemu_pid}/status` file to check in VmSize, VmRSS and VmLck values: * if `memlock=off`, then VmLck = 0; * if `memlock=on`, then VmLck > 0 and almost all memory is resident; * if `memlock=on-fault`, then VmLck > 0 and only few memory is resident. Signed-off-by: Alexandr Moshkov Message-ID: <20250605065908.299979-3-dtalexundeer@yandex-team.ru> Signed-off-by: Thomas Huth --- tests/functional/meson.build | 1 + tests/functional/test_memlock.py | 79 ++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100755 tests/functional/test_memlock.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 557d59ddf4..c3fca446cf 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -312,6 +312,7 @@ tests_x86_64_system_quick = [ 'virtio_version', 'x86_cpu_model_versions', 'vnc', + 'memlock', ] tests_x86_64_system_thorough = [ diff --git a/tests/functional/test_memlock.py b/tests/functional/test_memlock.py new file mode 100755 index 0000000000..2b515ff979 --- /dev/null +++ b/tests/functional/test_memlock.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +# +# Functional test that check overcommit memlock options +# +# Copyright (c) Yandex Technologies LLC, 2025 +# +# Author: +# Alexandr Moshkov +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import re + +from typing import Dict + +from qemu_test import QemuSystemTest +from qemu_test import skipLockedMemoryTest + + +STATUS_VALUE_PATTERN = re.compile(r'^(\w+):\s+(\d+) kB', re.MULTILINE) + + +@skipLockedMemoryTest(2_097_152) # 2GB +class MemlockTest(QemuSystemTest): + """ + Runs a guest with memlock options. + Then verify, that this options is working correctly + by checking the status file of the QEMU process. + """ + + def common_vm_setup_with_memlock(self, memlock): + self.vm.add_args('-overcommit', f'mem-lock={memlock}') + self.vm.launch() + + def test_memlock_off(self): + self.common_vm_setup_with_memlock('off') + + status = self.get_process_status_values(self.vm.get_pid()) + + self.assertTrue(status['VmLck'] == 0) + + def test_memlock_on(self): + self.common_vm_setup_with_memlock('on') + + status = self.get_process_status_values(self.vm.get_pid()) + + # VmLck > 0 kB and almost all memory is resident + self.assertTrue(status['VmLck'] > 0) + self.assertTrue(status['VmRSS'] >= status['VmSize'] * 0.70) + + def test_memlock_onfault(self): + self.common_vm_setup_with_memlock('on-fault') + + status = self.get_process_status_values(self.vm.get_pid()) + + # VmLck > 0 kB and only few memory is resident + self.assertTrue(status['VmLck'] > 0) + self.assertTrue(status['VmRSS'] <= status['VmSize'] * 0.30) + + def get_process_status_values(self, pid: int) -> Dict[str, int]: + result = {} + raw_status = self._get_raw_process_status(pid) + + for line in raw_status.split('\n'): + if m := STATUS_VALUE_PATTERN.match(line): + result[m.group(1)] = int(m.group(2)) + + return result + + def _get_raw_process_status(self, pid: int) -> str: + try: + with open(f'/proc/{pid}/status', 'r') as f: + return f.read() + except FileNotFoundError: + self.skipTest("Can't open status file of the process") + + +if __name__ == '__main__': + MemlockTest.main() From e317f0ce722f2d36b1ea61de6d85f2e9f2282a0b Mon Sep 17 00:00:00 2001 From: Haseung Bong Date: Sat, 7 Jun 2025 15:04:56 +0900 Subject: [PATCH 1512/2760] tests/vm/README: fix documentation path in tests/vm/README The README file in tests/vm/ points to a non-existent file, docs/devel/testing.rst. Update the README to point to docs/devel/testing/main.rst, which now contains information about VM testing. Signed-off-by: Haseung Bong Fixes: ff41da50308 ("docs/devel: Split testing docs from the build docs and move to separate folder") Reviewed-by: Thomas Huth Message-ID: <20250607060456.28902-1-hasueng@gmail.com> Signed-off-by: Thomas Huth --- tests/vm/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/vm/README b/tests/vm/README index f9c04cc0e7..14ac323309 100644 --- a/tests/vm/README +++ b/tests/vm/README @@ -1 +1 @@ -See docs/devel/testing.rst for help. +See docs/devel/testing/main.rst for help. From 97b091c88f05df6feaeef58b2357621ed3186fb8 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 10 Jun 2025 07:37:34 +0200 Subject: [PATCH 1513/2760] MAINTAINERS: Update the paths to the testing documentation files When the testing docs were moved to a separate subfolder, the entries in the MAINTAINERS file were missed. Update them now. Fixes: ff41da50308 ("docs/devel: Split testing docs from the build docs and move to separate folder") Reviewed-by: Ani Sinha Signed-off-by: Thomas Huth Message-ID: <20250610053734.10417-1-thuth@redhat.com> --- MAINTAINERS | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index aa6763077e..eb2b338fb1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2124,7 +2124,7 @@ M: Michael S. Tsirkin S: Supported F: tests/functional/acpi-bits/* F: tests/functional/test_acpi_bits.py -F: docs/devel/acpi-bits.rst +F: docs/devel/testing/acpi-bits.rst ACPI/HEST/GHES R: Dongjiu Geng @@ -3440,8 +3440,8 @@ F: system/qtest.c F: include/system/qtest.h F: accel/qtest/ F: tests/qtest/ -F: docs/devel/qgraph.rst -F: docs/devel/qtest.rst +F: docs/devel/testing/qgraph.rst +F: docs/devel/testing/qtest.rst X: tests/qtest/bios-tables-test* X: tests/qtest/migration-* @@ -3459,7 +3459,7 @@ F: tests/qtest/fuzz-*test.c F: tests/docker/test-fuzz F: scripts/oss-fuzz/ F: hw/mem/sparse-mem.c -F: docs/devel/fuzzing.rst +F: docs/devel/testing/fuzzing.rst Register API M: Alistair Francis @@ -4078,7 +4078,7 @@ M: Stefan Hajnoczi L: qemu-block@nongnu.org S: Supported F: block/blkverify.c -F: docs/devel/blkverify.rst +F: docs/devel/testing/blkverify.rst bochs M: Stefan Hajnoczi @@ -4156,7 +4156,7 @@ M: Hanna Reitz L: qemu-block@nongnu.org S: Supported F: block/blkdebug.c -F: docs/devel/blkdebug.rst +F: docs/devel/testing/blkdebug.rst vpc M: Kevin Wolf @@ -4276,7 +4276,8 @@ F: tests/vm/ F: tests/lcitool/ F: tests/functional/test_*_tuxrun.py F: scripts/archive-source.sh -F: docs/devel/testing.rst +F: docs/devel/testing/ci* +F: docs/devel/testing/main.rst W: https://gitlab.com/qemu-project/qemu/pipelines W: https://travis-ci.org/qemu/qemu From 3784b6e0771fd2278fc1a13045cde63cb6e4dcb3 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 31 May 2025 16:00:25 +0900 Subject: [PATCH 1514/2760] MAINTAINERS: Update Akihiko Odaki's affiliation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit My contract with Daynix Computing Ltd. will expire by the end of May, 2025. As I may contribute to QEMU for my research, use my email address at the lab. As I'm the only maintainer of igb and no longer financially supported to maintain it, change its status to Odd Fixes until someone steps up. Signed-off-by: Akihiko Odaki Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250531-rsg-v1-1-e0bae1e1d90e@rsg.ci.i.u-tokyo.ac.jp> Signed-off-by: Thomas Huth --- .mailmap | 3 ++- MAINTAINERS | 16 ++++++++-------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.mailmap b/.mailmap index 33fe75400f..e7271852dc 100644 --- a/.mailmap +++ b/.mailmap @@ -67,7 +67,8 @@ Andrey Drobyshev Andrey Drobyshev via BALATON Zoltan via # Next, replace old addresses by a more recent one. -Akihiko Odaki +Akihiko Odaki +Akihiko Odaki Aleksandar Markovic Aleksandar Markovic Aleksandar Markovic diff --git a/MAINTAINERS b/MAINTAINERS index eb2b338fb1..8d2fd4d57d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2519,7 +2519,7 @@ F: tests/qtest/fuzz-megasas-test.c Network packet abstractions M: Dmitry Fleytman -R: Akihiko Odaki +R: Akihiko Odaki S: Maintained F: include/net/eth.h F: net/eth.c @@ -2549,13 +2549,13 @@ F: docs/specs/rocker.rst e1000x M: Dmitry Fleytman -R: Akihiko Odaki +R: Akihiko Odaki S: Maintained F: hw/net/e1000x* e1000e M: Dmitry Fleytman -R: Akihiko Odaki +R: Akihiko Odaki S: Maintained F: hw/net/e1000e* F: tests/qtest/fuzz-e1000e-test.c @@ -2563,9 +2563,9 @@ F: tests/qtest/e1000e-test.c F: tests/qtest/libqos/e1000e.* igb -M: Akihiko Odaki +M: Akihiko Odaki R: Sriram Yagnaraman -S: Maintained +S: Odd Fixes F: docs/system/devices/igb.rst F: hw/net/igb* F: tests/functional/test_netdev_ethtool.py @@ -2910,7 +2910,7 @@ Core Audio framework backend M: Gerd Hoffmann M: Philippe Mathieu-Daudé R: Christian Schoenebeck -R: Akihiko Odaki +R: Akihiko Odaki S: Odd Fixes F: audio/coreaudio.m @@ -3211,7 +3211,7 @@ F: tests/functional/test_vnc.py Cocoa graphics M: Peter Maydell M: Philippe Mathieu-Daudé -R: Akihiko Odaki +R: Akihiko Odaki S: Odd Fixes F: ui/cocoa.m @@ -3738,7 +3738,7 @@ F: util/iova-tree.c elf2dmp M: Viktor Prutyanov -R: Akihiko Odaki +R: Akihiko Odaki S: Maintained F: contrib/elf2dmp/ From 1da0025ecd527c833697822511057f1b93579045 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Tue, 10 Jun 2025 22:41:29 +0200 Subject: [PATCH 1515/2760] scripts/meson-buildoptions: Sort coroutine_backend choices lexicographically When changing meson_options.txt, this script gets updated automatically by QEMU tooling which sorts the choices lexicographically. Fixes: ccc403ed5844 ("meson: Add wasm build in build scripts") Signed-off-by: Bernhard Beschow Message-ID: <20250610204131.2862-4-shentey@gmail.com> Tested-by: Thomas Huth Signed-off-by: Thomas Huth --- scripts/meson-buildoptions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index f09ef9604f..73e0770f42 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -80,7 +80,7 @@ meson_options_help() { printf "%s\n" ' --tls-priority=VALUE Default TLS protocol/cipher priority string' printf "%s\n" ' [NORMAL]' printf "%s\n" ' --with-coroutine=CHOICE coroutine backend to use (choices:' - printf "%s\n" ' auto/sigaltstack/ucontext/windows/wasm)' + printf "%s\n" ' auto/sigaltstack/ucontext/wasm/windows)' printf "%s\n" ' --with-pkgversion=VALUE use specified string as sub-version of the' printf "%s\n" ' package' printf "%s\n" ' --with-suffix=VALUE Suffix for QEMU data/modules/config directories' From 144227dbc46f82d76de065e507a256a4d723b9e4 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Mon, 9 Jun 2025 19:54:33 +0800 Subject: [PATCH 1516/2760] vfio/container: Fix vfio_listener_commit() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's wrong to call into listener_begin callback in vfio_listener_commit(). Currently this impacts vfio-user. Fixes: d9b7d8b6993b ("vfio/container: pass listener_begin/commit callbacks") Signed-off-by: Zhenzhong Duan Reviewed-by: John Levon Link: https://lore.kernel.org/qemu-devel/20250609115433.401775-1-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/listener.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index 203ed0314e..735b5f21b7 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -437,7 +437,7 @@ static void vfio_listener_commit(MemoryListener *listener) listener); void (*listener_commit)(VFIOContainerBase *bcontainer); - listener_commit = VFIO_IOMMU_GET_CLASS(bcontainer)->listener_begin; + listener_commit = VFIO_IOMMU_GET_CLASS(bcontainer)->listener_commit; if (listener_commit) { listener_commit(bcontainer); From 6c4f752ea7c57b9782da0669599659a9d63df431 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 11 Jun 2025 10:42:28 +0800 Subject: [PATCH 1517/2760] vfio/pci: Fix instance_size of VFIO_PCI_BASE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the final instance_size of VFIO_PCI_BASE is sizeof(PCIDevice). It should be sizeof(VFIOPCIDevice), VFIO_PCI uses same structure as base class VFIO_PCI_BASE, so no need to set its instance_size explicitly. This isn't catastrophic only because VFIO_PCI_BASE is an abstract class. Fixes: d4e392d0a99b ("vfio: add vfio-pci-base class") Signed-off-by: Zhenzhong Duan Reviewed-by: John Levon Reviewed-by: Cédric Le Goater Reviewed-by: Yi Liu Link: https://lore.kernel.org/qemu-devel/20250611024228.423666-1-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b1250d85bf..6748f4e876 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3425,7 +3425,7 @@ static void vfio_pci_base_dev_class_init(ObjectClass *klass, const void *data) static const TypeInfo vfio_pci_base_dev_info = { .name = TYPE_VFIO_PCI_BASE, .parent = TYPE_PCI_DEVICE, - .instance_size = 0, + .instance_size = sizeof(VFIOPCIDevice), .abstract = true, .class_init = vfio_pci_base_dev_class_init, .interfaces = (const InterfaceInfo[]) { @@ -3647,7 +3647,6 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, const void *data) static const TypeInfo vfio_pci_dev_info = { .name = TYPE_VFIO_PCI, .parent = TYPE_VFIO_PCI_BASE, - .instance_size = sizeof(VFIOPCIDevice), .class_init = vfio_pci_dev_class_init, .instance_init = vfio_instance_init, .instance_finalize = vfio_instance_finalize, From 0fb8a62fe46ca0cdcc75479658301b7e1c3aa797 Mon Sep 17 00:00:00 2001 From: Rorie Reyes Date: Mon, 9 Jun 2025 12:44:15 -0400 Subject: [PATCH 1518/2760] hw/vfio/ap: notification handler for AP config changed event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register an event notifier handler to process AP configuration change events by queuing the event and generating a CRW to let the guest know its AP configuration has changed Signed-off-by: Rorie Reyes Reviewed-by: Anthony Krowiak Link: https://lore.kernel.org/qemu-devel/20250609164418.17585-2-rreyes@linux.ibm.com Signed-off-by: Cédric Le Goater --- hw/vfio/ap.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 785c0a0197..93c74ebedb 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -18,6 +18,7 @@ #include "hw/vfio/vfio-device.h" #include "system/iommufd.h" #include "hw/s390x/ap-device.h" +#include "hw/s390x/css.h" #include "qemu/error-report.h" #include "qemu/event_notifier.h" #include "qemu/main-loop.h" @@ -37,6 +38,7 @@ struct VFIOAPDevice { APDevice apdev; VFIODevice vdev; EventNotifier req_notifier; + EventNotifier cfg_notifier; }; OBJECT_DECLARE_SIMPLE_TYPE(VFIOAPDevice, VFIO_AP_DEVICE) @@ -70,6 +72,18 @@ static void vfio_ap_req_notifier_handler(void *opaque) } } +static void vfio_ap_cfg_chg_notifier_handler(void *opaque) +{ + VFIOAPDevice *vapdev = opaque; + + if (!event_notifier_test_and_clear(&vapdev->cfg_notifier)) { + return; + } + + css_generate_css_crws(0); + +} + static bool vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, unsigned int irq, Error **errp) { @@ -85,6 +99,10 @@ static bool vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, notifier = &vapdev->req_notifier; fd_read = vfio_ap_req_notifier_handler; break; + case VFIO_AP_CFG_CHG_IRQ_INDEX: + notifier = &vapdev->cfg_notifier; + fd_read = vfio_ap_cfg_chg_notifier_handler; + break; default: error_setg(errp, "vfio: Unsupported device irq(%d)", irq); return false; @@ -137,6 +155,9 @@ static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev, case VFIO_AP_REQ_IRQ_INDEX: notifier = &vapdev->req_notifier; break; + case VFIO_AP_CFG_CHG_IRQ_INDEX: + notifier = &vapdev->cfg_notifier; + break; default: error_report("vfio: Unsupported device irq(%d)", irq); return; @@ -176,6 +197,15 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) warn_report_err(err); } + if (!vfio_ap_register_irq_notifier(vapdev, VFIO_AP_CFG_CHG_IRQ_INDEX, &err)) + { + /* + * Report this error, but do not make it a failing condition. + * Lack of this IRQ in the host does not prevent normal operation. + */ + warn_report_err(err); + } + return; error: @@ -188,6 +218,7 @@ static void vfio_ap_unrealize(DeviceState *dev) VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev); vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX); + vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_CFG_CHG_IRQ_INDEX); vfio_device_detach(&vapdev->vdev); g_free(vapdev->vdev.name); } From bc36d14e13ee9bfc17a6818c5e1664fb8e316621 Mon Sep 17 00:00:00 2001 From: Rorie Reyes Date: Mon, 9 Jun 2025 12:44:16 -0400 Subject: [PATCH 1519/2760] hw/vfio/ap: store object indicating AP config changed in a queue MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creates an object indicating that an AP configuration change event has been received and stores it in a queue. These objects will later be used to store event information for an AP configuration change when the CHSC instruction is intercepted. Signed-off-by: Rorie Reyes Reviewed-by: Anthony Krowiak Link: https://lore.kernel.org/qemu-devel/20250609164418.17585-3-rreyes@linux.ibm.com Signed-off-by: Cédric Le Goater --- hw/vfio/ap.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 93c74ebedb..681fd4a4f1 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -21,6 +21,7 @@ #include "hw/s390x/css.h" #include "qemu/error-report.h" #include "qemu/event_notifier.h" +#include "qemu/lockable.h" #include "qemu/main-loop.h" #include "qemu/module.h" #include "qemu/option.h" @@ -41,6 +42,15 @@ struct VFIOAPDevice { EventNotifier cfg_notifier; }; +typedef struct APConfigChgEvent { + QTAILQ_ENTRY(APConfigChgEvent) next; +} APConfigChgEvent; + +static QTAILQ_HEAD(, APConfigChgEvent) cfg_chg_events = + QTAILQ_HEAD_INITIALIZER(cfg_chg_events); + +static QemuMutex cfg_chg_events_lock; + OBJECT_DECLARE_SIMPLE_TYPE(VFIOAPDevice, VFIO_AP_DEVICE) static void vfio_ap_compute_needs_reset(VFIODevice *vdev) @@ -74,12 +84,19 @@ static void vfio_ap_req_notifier_handler(void *opaque) static void vfio_ap_cfg_chg_notifier_handler(void *opaque) { + APConfigChgEvent *cfg_chg_event; VFIOAPDevice *vapdev = opaque; if (!event_notifier_test_and_clear(&vapdev->cfg_notifier)) { return; } + cfg_chg_event = g_new0(APConfigChgEvent, 1); + + WITH_QEMU_LOCK_GUARD(&cfg_chg_events_lock) { + QTAILQ_INSERT_TAIL(&cfg_chg_events, cfg_chg_event, next); + } + css_generate_css_crws(0); } From fd0336021553dfcccb92522552d0ac9475e831d8 Mon Sep 17 00:00:00 2001 From: Rorie Reyes Date: Mon, 9 Jun 2025 12:44:17 -0400 Subject: [PATCH 1520/2760] hw/vfio/ap: Storing event information for an AP configuration change event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These functions can be invoked by the function that handles interception of the CHSC SEI instruction for requests indicating the accessibility of one or more adjunct processors has changed. Signed-off-by: Rorie Reyes Reviewed-by: Anthony Krowiak Link: https://lore.kernel.org/qemu-devel/20250609164418.17585-4-rreyes@linux.ibm.com Signed-off-by: Cédric Le Goater --- hw/vfio/ap.c | 40 ++++++++++++++++++++++++++++++++++++ include/hw/s390x/ap-bridge.h | 39 +++++++++++++++++++++++++++++++++++ 2 files changed, 79 insertions(+) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 681fd4a4f1..874e0d1eaf 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -10,6 +10,7 @@ * directory. */ +#include #include "qemu/osdep.h" #include CONFIG_DEVICES /* CONFIG_IOMMUFD */ #include @@ -101,6 +102,38 @@ static void vfio_ap_cfg_chg_notifier_handler(void *opaque) } +int ap_chsc_sei_nt0_get_event(void *res) +{ + ChscSeiNt0Res *nt0_res = (ChscSeiNt0Res *)res; + APConfigChgEvent *cfg_chg_event; + + WITH_QEMU_LOCK_GUARD(&cfg_chg_events_lock) { + if (QTAILQ_EMPTY(&cfg_chg_events)) { + return EVENT_INFORMATION_NOT_STORED; + } + + cfg_chg_event = QTAILQ_FIRST(&cfg_chg_events); + QTAILQ_REMOVE(&cfg_chg_events, cfg_chg_event, next); + } + + memset(nt0_res, 0, sizeof(*nt0_res)); + g_free(cfg_chg_event); + nt0_res->flags |= PENDING_EVENT_INFO_BITMASK; + nt0_res->length = sizeof(ChscSeiNt0Res); + nt0_res->code = NT0_RES_RESPONSE_CODE; + nt0_res->nt = NT0_RES_NT_DEFAULT; + nt0_res->rs = NT0_RES_RS_AP_CHANGE; + nt0_res->cc = NT0_RES_CC_AP_CHANGE; + + return EVENT_INFORMATION_STORED; +} + +bool ap_chsc_sei_nt0_have_event(void) +{ + QEMU_LOCK_GUARD(&cfg_chg_events_lock); + return !QTAILQ_EMPTY(&cfg_chg_events); +} + static bool vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev, unsigned int irq, Error **errp) { @@ -197,6 +230,13 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev); VFIODevice *vbasedev = &vapdev->vdev; + static bool lock_initialized; + + if (!lock_initialized) { + qemu_mutex_init(&cfg_chg_events_lock); + lock_initialized = true; + } + if (!vfio_device_get_name(vbasedev, errp)) { return; } diff --git a/include/hw/s390x/ap-bridge.h b/include/hw/s390x/ap-bridge.h index 470e439a98..7efc52928d 100644 --- a/include/hw/s390x/ap-bridge.h +++ b/include/hw/s390x/ap-bridge.h @@ -16,4 +16,43 @@ void s390_init_ap(void); +typedef struct ChscSeiNt0Res { + uint16_t length; + uint16_t code; + uint8_t reserved1; + uint16_t reserved2; + uint8_t nt; +#define PENDING_EVENT_INFO_BITMASK 0x80; + uint8_t flags; + uint8_t reserved3; + uint8_t rs; + uint8_t cc; +} QEMU_PACKED ChscSeiNt0Res; + +#define NT0_RES_RESPONSE_CODE 1 +#define NT0_RES_NT_DEFAULT 0 +#define NT0_RES_RS_AP_CHANGE 5 +#define NT0_RES_CC_AP_CHANGE 3 + +#define EVENT_INFORMATION_NOT_STORED 1 +#define EVENT_INFORMATION_STORED 0 + +/** + * ap_chsc_sei_nt0_get_event - Retrieve the next pending AP config + * change event + * @res: Pointer to a ChscSeiNt0Res struct to be filled with event + * data + * + * This function checks for any pending AP config change events and, + * if present, populates the provided response structure with the + * appropriate SEI NT0 fields. + * + * Return: + * EVENT_INFORMATION_STORED - An event was available and written to @res + * EVENT_INFORMATION_NOT_STORED - No event was available + */ +int ap_chsc_sei_nt0_get_event(void *res); + +bool ap_chsc_sei_nt0_have_event(void); + #endif From c393e6d181c98b76b09fb5120c68f458d5a05d4b Mon Sep 17 00:00:00 2001 From: Rorie Reyes Date: Mon, 9 Jun 2025 12:44:18 -0400 Subject: [PATCH 1521/2760] s390: implementing CHSC SEI for AP config change MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Handle interception of the CHSC SEI instruction for requests indicating the guest's AP configuration has changed. If configuring --without-default-devices, hw/s390x/ap-stub.c was created to handle such circumstance. Also added the following to hw/s390x/meson.build if CONFIG_VFIO_AP is false, it will use the stub file. Signed-off-by: Rorie Reyes Reviewed-by: Anthony Krowiak Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250609164418.17585-5-rreyes@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + hw/s390x/ap-stub.c | 21 +++++++++++++++++++++ hw/s390x/meson.build | 1 + target/s390x/ioinst.c | 11 +++++++++-- 4 files changed, 32 insertions(+), 2 deletions(-) create mode 100644 hw/s390x/ap-stub.c diff --git a/MAINTAINERS b/MAINTAINERS index aa6763077e..1e84bfeaee 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -112,6 +112,7 @@ F: hw/intc/s390_flic.c F: hw/intc/s390_flic_kvm.c F: hw/s390x/ F: hw/vfio/ap.c +F: hw/s390x/ap-stub.c F: hw/vfio/ccw.c F: hw/watchdog/wdt_diag288.c F: include/hw/s390x/ diff --git a/hw/s390x/ap-stub.c b/hw/s390x/ap-stub.c new file mode 100644 index 0000000000..001fe5f8b0 --- /dev/null +++ b/hw/s390x/ap-stub.c @@ -0,0 +1,21 @@ +/* + * VFIO based AP matrix device assignment + * + * Copyright 2025 IBM Corp. + * Author(s): Rorie Reyes + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/s390x/ap-bridge.h" + +int ap_chsc_sei_nt0_get_event(void *res) +{ + return EVENT_INFORMATION_NOT_STORED; +} + +bool ap_chsc_sei_nt0_have_event(void) +{ + return false; +} diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build index 3bbebfd817..99cbcbd7d6 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -33,6 +33,7 @@ s390x_ss.add(when: 'CONFIG_S390_CCW_VIRTIO', if_true: files( )) s390x_ss.add(when: 'CONFIG_TERMINAL3270', if_true: files('3270-ccw.c')) s390x_ss.add(when: 'CONFIG_VFIO', if_true: files('s390-pci-vfio.c')) +s390x_ss.add(when: 'CONFIG_VFIO_AP', if_false: files('ap-stub.c')) virtio_ss = ss.source_set() virtio_ss.add(files('virtio-ccw.c')) diff --git a/target/s390x/ioinst.c b/target/s390x/ioinst.c index fe62ba5b06..2320dd4c12 100644 --- a/target/s390x/ioinst.c +++ b/target/s390x/ioinst.c @@ -18,6 +18,7 @@ #include "trace.h" #include "hw/s390x/s390-pci-bus.h" #include "target/s390x/kvm/pv.h" +#include "hw/s390x/ap-bridge.h" /* All I/O instructions but chsc use the s format */ static uint64_t get_address_from_regs(CPUS390XState *env, uint32_t ipb, @@ -574,13 +575,19 @@ static void ioinst_handle_chsc_sda(ChscReq *req, ChscResp *res) static int chsc_sei_nt0_get_event(void *res) { - /* no events yet */ + if (s390_has_feat(S390_FEAT_AP)) { + return ap_chsc_sei_nt0_get_event(res); + } + return 1; } static int chsc_sei_nt0_have_event(void) { - /* no events yet */ + if (s390_has_feat(S390_FEAT_AP)) { + return ap_chsc_sei_nt0_have_event(); + } + return 0; } From 7163c0bca72ae70a5be2d455c5348255a6016f04 Mon Sep 17 00:00:00 2001 From: John Levon Date: Fri, 6 Jun 2025 17:10:33 -0700 Subject: [PATCH 1522/2760] vfio: export PCI helpers needed for vfio-user MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vfio-user code will need to re-use various parts of the vfio PCI code. Export them in hw/vfio/pci.h, and rename them to the vfio_pci_* namespace. Signed-off-by: John Levon Reviewed-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250607001056.335310-2-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 48 ++++++++++++++++++++++---------------------- hw/vfio/pci.h | 11 ++++++++++ hw/vfio/trace-events | 6 +++--- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 6748f4e876..2901cedf6f 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -103,7 +103,7 @@ static void vfio_intx_interrupt(void *opaque) } } -static void vfio_intx_eoi(VFIODevice *vbasedev) +void vfio_pci_intx_eoi(VFIODevice *vbasedev) { VFIOPCIDevice *vdev = container_of(vbasedev, VFIOPCIDevice, vbasedev); @@ -111,7 +111,7 @@ static void vfio_intx_eoi(VFIODevice *vbasedev) return; } - trace_vfio_intx_eoi(vbasedev->name); + trace_vfio_pci_intx_eoi(vbasedev->name); vdev->intx.pending = false; pci_irq_deassert(&vdev->pdev); @@ -236,7 +236,7 @@ static void vfio_intx_update(VFIOPCIDevice *vdev, PCIINTxRoute *route) } /* Re-enable the interrupt in cased we missed an EOI */ - vfio_intx_eoi(&vdev->vbasedev); + vfio_pci_intx_eoi(&vdev->vbasedev); } static void vfio_intx_routing_notifier(PCIDevice *pdev) @@ -1743,7 +1743,7 @@ static bool vfio_msix_setup(VFIOPCIDevice *vdev, int pos, Error **errp) return true; } -static void vfio_teardown_msi(VFIOPCIDevice *vdev) +void vfio_pci_teardown_msi(VFIOPCIDevice *vdev) { msi_uninit(&vdev->pdev); @@ -1839,7 +1839,7 @@ static void vfio_bars_register(VFIOPCIDevice *vdev) } } -static void vfio_bars_exit(VFIOPCIDevice *vdev) +void vfio_pci_bars_exit(VFIOPCIDevice *vdev) { int i; @@ -2430,7 +2430,7 @@ static void vfio_add_ext_cap(VFIOPCIDevice *vdev) g_free(config); } -static bool vfio_add_capabilities(VFIOPCIDevice *vdev, Error **errp) +bool vfio_pci_add_capabilities(VFIOPCIDevice *vdev, Error **errp) { PCIDevice *pdev = &vdev->pdev; @@ -2706,7 +2706,7 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) static VFIODeviceOps vfio_pci_ops = { .vfio_compute_needs_reset = vfio_pci_compute_needs_reset, .vfio_hot_reset_multi = vfio_pci_hot_reset_multi, - .vfio_eoi = vfio_intx_eoi, + .vfio_eoi = vfio_pci_intx_eoi, .vfio_get_object = vfio_pci_get_object, .vfio_save_config = vfio_pci_save_config, .vfio_load_config = vfio_pci_load_config, @@ -2777,7 +2777,7 @@ bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) return true; } -static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) +bool vfio_pci_populate_device(VFIOPCIDevice *vdev, Error **errp) { VFIODevice *vbasedev = &vdev->vbasedev; struct vfio_region_info *reg_info = NULL; @@ -2823,7 +2823,7 @@ static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) return false; } - trace_vfio_populate_device_config(vdev->vbasedev.name, + trace_vfio_pci_populate_device_config(vdev->vbasedev.name, (unsigned long)reg_info->size, (unsigned long)reg_info->offset, (unsigned long)reg_info->flags); @@ -2845,7 +2845,7 @@ static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) ret = vfio_device_get_irq_info(vbasedev, VFIO_PCI_ERR_IRQ_INDEX, &irq_info); if (ret) { /* This can fail for an old kernel or legacy PCI dev */ - trace_vfio_populate_device_get_irq_info_failure(strerror(-ret)); + trace_vfio_pci_populate_device_get_irq_info_failure(strerror(-ret)); } else if (irq_info.count == 1) { vdev->pci_aer = true; } else { @@ -2857,7 +2857,7 @@ static bool vfio_populate_device(VFIOPCIDevice *vdev, Error **errp) return true; } -static void vfio_pci_put_device(VFIOPCIDevice *vdev) +void vfio_pci_put_device(VFIOPCIDevice *vdev) { vfio_display_finalize(vdev); vfio_bars_finalize(vdev); @@ -2905,7 +2905,7 @@ static void vfio_err_notifier_handler(void *opaque) * and continue after disabling error recovery support for the * device. */ -static void vfio_register_err_notifier(VFIOPCIDevice *vdev) +void vfio_pci_register_err_notifier(VFIOPCIDevice *vdev) { Error *err = NULL; int32_t fd; @@ -2964,7 +2964,7 @@ static void vfio_req_notifier_handler(void *opaque) } } -static void vfio_register_req_notifier(VFIOPCIDevice *vdev) +void vfio_pci_register_req_notifier(VFIOPCIDevice *vdev) { struct vfio_irq_info irq_info; Error *err = NULL; @@ -3018,7 +3018,7 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) vdev->req_enabled = false; } -static bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) +bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) { PCIDevice *pdev = &vdev->pdev; VFIODevice *vbasedev = &vdev->vbasedev; @@ -3124,7 +3124,7 @@ static bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) return true; } -static bool vfio_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) +bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) { PCIDevice *pdev = &vdev->pdev; @@ -3214,7 +3214,7 @@ static void vfio_pci_realize(PCIDevice *pdev, Error **errp) goto error; } - if (!vfio_populate_device(vdev, errp)) { + if (!vfio_pci_populate_device(vdev, errp)) { goto error; } @@ -3228,7 +3228,7 @@ static void vfio_pci_realize(PCIDevice *pdev, Error **errp) goto out_teardown; } - if (!vfio_add_capabilities(vdev, errp)) { + if (!vfio_pci_add_capabilities(vdev, errp)) { goto out_unset_idev; } @@ -3244,7 +3244,7 @@ static void vfio_pci_realize(PCIDevice *pdev, Error **errp) vfio_bar_quirk_setup(vdev, i); } - if (!vfio_interrupt_setup(vdev, errp)) { + if (!vfio_pci_interrupt_setup(vdev, errp)) { goto out_unset_idev; } @@ -3288,8 +3288,8 @@ static void vfio_pci_realize(PCIDevice *pdev, Error **errp) } } - vfio_register_err_notifier(vdev); - vfio_register_req_notifier(vdev); + vfio_pci_register_err_notifier(vdev); + vfio_pci_register_req_notifier(vdev); vfio_setup_resetfn_quirk(vdev); return; @@ -3310,8 +3310,8 @@ static void vfio_pci_realize(PCIDevice *pdev, Error **errp) pci_device_unset_iommu_device(pdev); } out_teardown: - vfio_teardown_msi(vdev); - vfio_bars_exit(vdev); + vfio_pci_teardown_msi(vdev); + vfio_pci_bars_exit(vdev); error: error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->name); } @@ -3338,9 +3338,9 @@ static void vfio_exitfn(PCIDevice *pdev) if (vdev->intx.mmap_timer) { timer_free(vdev->intx.mmap_timer); } - vfio_teardown_msi(vdev); + vfio_pci_teardown_msi(vdev); vfio_pci_disable_rp_atomics(vdev); - vfio_bars_exit(vdev); + vfio_pci_bars_exit(vdev); vfio_migration_exit(vbasedev); if (!vbasedev->mdev) { pci_device_unset_iommu_device(pdev); diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 5ce0fb916f..d4c6b2e7b7 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -248,4 +248,15 @@ void vfio_display_finalize(VFIOPCIDevice *vdev); extern const VMStateDescription vfio_display_vmstate; +void vfio_pci_bars_exit(VFIOPCIDevice *vdev); +bool vfio_pci_add_capabilities(VFIOPCIDevice *vdev, Error **errp); +bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp); +bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp); +void vfio_pci_intx_eoi(VFIODevice *vbasedev); +void vfio_pci_put_device(VFIOPCIDevice *vdev); +bool vfio_pci_populate_device(VFIOPCIDevice *vdev, Error **errp); +void vfio_pci_register_err_notifier(VFIOPCIDevice *vdev); +void vfio_pci_register_req_notifier(VFIOPCIDevice *vdev); +void vfio_pci_teardown_msi(VFIOPCIDevice *vdev); + #endif /* HW_VFIO_VFIO_PCI_H */ diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index e90ec9bff8..f06236f37b 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -2,7 +2,7 @@ # pci.c vfio_intx_interrupt(const char *name, char line) " (%s) Pin %c" -vfio_intx_eoi(const char *name) " (%s) EOI" +vfio_pci_intx_eoi(const char *name) " (%s) EOI" vfio_intx_enable_kvm(const char *name) " (%s) KVM INTx accel enabled" vfio_intx_disable_kvm(const char *name) " (%s) KVM INTx accel disabled" vfio_intx_update(const char *name, int new_irq, int target_irq) " (%s) IRQ moved %d -> %d" @@ -35,8 +35,8 @@ vfio_pci_hot_reset(const char *name, const char *type) " (%s) %s" vfio_pci_hot_reset_has_dep_devices(const char *name) "%s: hot reset dependent devices:" vfio_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int group_id) "\t%04x:%02x:%02x.%x group %d" vfio_pci_hot_reset_result(const char *name, const char *result) "%s hot reset: %s" -vfio_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device '%s' config: size: 0x%lx, offset: 0x%lx, flags: 0x%lx" -vfio_populate_device_get_irq_info_failure(const char *errstr) "VFIO_DEVICE_GET_IRQ_INFO failure: %s" +vfio_pci_populate_device_config(const char *name, unsigned long size, unsigned long offset, unsigned long flags) "Device '%s' config: size: 0x%lx, offset: 0x%lx, flags: 0x%lx" +vfio_pci_populate_device_get_irq_info_failure(const char *errstr) "VFIO_DEVICE_GET_IRQ_INFO failure: %s" vfio_mdev(const char *name, bool is_mdev) " (%s) is_mdev %d" vfio_add_ext_cap_dropped(const char *name, uint16_t cap, uint16_t offset) "%s 0x%x@0x%x" vfio_pci_reset(const char *name) " (%s)" From 59adfc6f1843538d78373296fd05a57ced1f3ecb Mon Sep 17 00:00:00 2001 From: John Levon Date: Fri, 6 Jun 2025 17:10:35 -0700 Subject: [PATCH 1523/2760] vfio: add per-region fd support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For vfio-user, each region has its own fd rather than sharing vbasedev's. Add the necessary plumbing to support this, and use the correct fd in vfio_region_mmap(). Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250607001056.335310-4-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/device.c | 29 +++++++++++++++++++++++++---- hw/vfio/region.c | 9 +++++++-- include/hw/vfio/vfio-device.h | 7 +++++-- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 9fba2c7272..a4bdde8e8b 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -200,6 +200,7 @@ int vfio_device_get_region_info(VFIODevice *vbasedev, int index, struct vfio_region_info **info) { size_t argsz = sizeof(struct vfio_region_info); + int fd = -1; int ret; /* check cache */ @@ -214,7 +215,7 @@ int vfio_device_get_region_info(VFIODevice *vbasedev, int index, retry: (*info)->argsz = argsz; - ret = vbasedev->io_ops->get_region_info(vbasedev, *info); + ret = vbasedev->io_ops->get_region_info(vbasedev, *info, &fd); if (ret != 0) { g_free(*info); *info = NULL; @@ -225,11 +226,19 @@ int vfio_device_get_region_info(VFIODevice *vbasedev, int index, argsz = (*info)->argsz; *info = g_realloc(*info, argsz); + if (fd != -1) { + close(fd); + fd = -1; + } + goto retry; } /* fill cache */ vbasedev->reginfo[index] = *info; + if (vbasedev->region_fds != NULL) { + vbasedev->region_fds[index] = fd; + } return 0; } @@ -334,6 +343,7 @@ void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops, vbasedev->io_ops = &vfio_device_io_ops_ioctl; vbasedev->dev = dev; vbasedev->fd = -1; + vbasedev->use_region_fds = false; vbasedev->ram_block_discard_allowed = ram_discard; } @@ -444,6 +454,9 @@ void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainerBase *bcontainer, vbasedev->reginfo = g_new0(struct vfio_region_info *, vbasedev->num_regions); + if (vbasedev->use_region_fds) { + vbasedev->region_fds = g_new0(int, vbasedev->num_regions); + } } void vfio_device_unprepare(VFIODevice *vbasedev) @@ -452,9 +465,14 @@ void vfio_device_unprepare(VFIODevice *vbasedev) for (i = 0; i < vbasedev->num_regions; i++) { g_free(vbasedev->reginfo[i]); + if (vbasedev->region_fds != NULL && vbasedev->region_fds[i] != -1) { + close(vbasedev->region_fds[i]); + } + } - g_free(vbasedev->reginfo); - vbasedev->reginfo = NULL; + + g_clear_pointer(&vbasedev->reginfo, g_free); + g_clear_pointer(&vbasedev->region_fds, g_free); QLIST_REMOVE(vbasedev, container_next); QLIST_REMOVE(vbasedev, global_next); @@ -476,10 +494,13 @@ static int vfio_device_io_device_feature(VFIODevice *vbasedev, } static int vfio_device_io_get_region_info(VFIODevice *vbasedev, - struct vfio_region_info *info) + struct vfio_region_info *info, + int *fd) { int ret; + *fd = -1; + ret = ioctl(vbasedev->fd, VFIO_DEVICE_GET_REGION_INFO, info); return ret < 0 ? -errno : ret; diff --git a/hw/vfio/region.c b/hw/vfio/region.c index 34752c3f65..cb172f2136 100644 --- a/hw/vfio/region.c +++ b/hw/vfio/region.c @@ -241,6 +241,7 @@ int vfio_region_mmap(VFIORegion *region) { int i, ret, prot = 0; char *name; + int fd; if (!region->mem) { return 0; @@ -271,14 +272,18 @@ int vfio_region_mmap(VFIORegion *region) goto no_mmap; } + /* Use the per-region fd if set, or the shared fd. */ + fd = region->vbasedev->region_fds ? + region->vbasedev->region_fds[region->nr] : + region->vbasedev->fd, + map_align = (void *)ROUND_UP((uintptr_t)map_base, (uintptr_t)align); munmap(map_base, map_align - map_base); munmap(map_align + region->mmaps[i].size, align - (map_align - map_base)); region->mmaps[i].mmap = mmap(map_align, region->mmaps[i].size, prot, - MAP_SHARED | MAP_FIXED, - region->vbasedev->fd, + MAP_SHARED | MAP_FIXED, fd, region->fd_offset + region->mmaps[i].offset); if (region->mmaps[i].mmap == MAP_FAILED) { diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 8bcb3c19f6..bf54fc6920 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -66,6 +66,7 @@ typedef struct VFIODevice { OnOffAuto enable_migration; OnOffAuto migration_multifd_transfer; bool migration_events; + bool use_region_fds; VFIODeviceOps *ops; VFIODeviceIOOps *io_ops; unsigned int num_irqs; @@ -84,6 +85,7 @@ typedef struct VFIODevice { VFIOIOASHwpt *hwpt; QLIST_ENTRY(VFIODevice) hwpt_next; struct vfio_region_info **reginfo; + int *region_fds; } VFIODevice; struct VFIODeviceOps { @@ -170,10 +172,11 @@ struct VFIODeviceIOOps { /** * @get_region_info * - * Fill in @info with information on the region given by @info->index. + * Fill in @info (and optionally @fd) with information on the region given + * by @info->index. */ int (*get_region_info)(VFIODevice *vdev, - struct vfio_region_info *info); + struct vfio_region_info *info, int *fd); /** * @get_irq_info From a574b0614449760a83310226b548e21b08a805fa Mon Sep 17 00:00:00 2001 From: John Levon Date: Fri, 6 Jun 2025 17:10:36 -0700 Subject: [PATCH 1524/2760] vfio: mark posted writes in region write callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For vfio-user, the region write implementation needs to know if the write is posted; add the necessary plumbing to support this. Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250607001056.335310-5-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/device.c | 3 ++- hw/vfio/pci.c | 5 ++++- hw/vfio/region.c | 3 ++- include/hw/vfio/vfio-device.h | 4 ++-- include/hw/vfio/vfio-region.h | 1 + 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/hw/vfio/device.c b/hw/vfio/device.c index a4bdde8e8b..d32600eda1 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -543,7 +543,8 @@ static int vfio_device_io_region_read(VFIODevice *vbasedev, uint8_t index, } static int vfio_device_io_region_write(VFIODevice *vbasedev, uint8_t index, - off_t off, uint32_t size, void *data) + off_t off, uint32_t size, void *data, + bool post) { struct vfio_region_info *info; int ret; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 2901cedf6f..89f9246416 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -989,7 +989,7 @@ static int vfio_pci_config_space_write(VFIOPCIDevice *vdev, off_t offset, { return vdev->vbasedev.io_ops->region_write(&vdev->vbasedev, VFIO_PCI_CONFIG_REGION_INDEX, - offset, size, data); + offset, size, data, false); } static uint64_t vfio_rom_read(void *opaque, hwaddr addr, unsigned size) @@ -1793,6 +1793,9 @@ static void vfio_bar_prepare(VFIOPCIDevice *vdev, int nr) bar->type = pci_bar & (bar->ioport ? ~PCI_BASE_ADDRESS_IO_MASK : ~PCI_BASE_ADDRESS_MEM_MASK); bar->size = bar->region.size; + + /* IO regions are sync, memory can be async */ + bar->region.post_wr = (bar->ioport == 0); } static void vfio_bars_prepare(VFIOPCIDevice *vdev) diff --git a/hw/vfio/region.c b/hw/vfio/region.c index cb172f2136..f5b8e3cbf1 100644 --- a/hw/vfio/region.c +++ b/hw/vfio/region.c @@ -66,7 +66,7 @@ void vfio_region_write(void *opaque, hwaddr addr, } ret = vbasedev->io_ops->region_write(vbasedev, region->nr, - addr, size, &buf); + addr, size, &buf, region->post_wr); if (ret != size) { error_report("%s(%s:region%d+0x%"HWADDR_PRIx", 0x%"PRIx64 ",%d) failed: %s", @@ -200,6 +200,7 @@ int vfio_region_setup(Object *obj, VFIODevice *vbasedev, VFIORegion *region, region->size = info->size; region->fd_offset = info->offset; region->nr = index; + region->post_wr = false; if (region->size) { region->mem = g_new0(MemoryRegion, 1); diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index bf54fc6920..9793b2dba0 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -205,10 +205,10 @@ struct VFIODeviceIOOps { * @region_write * * Write @size bytes to the region @nr at offset @off from the buffer - * @data. + * @data; if @post, the write is posted. */ int (*region_write)(VFIODevice *vdev, uint8_t nr, off_t off, uint32_t size, - void *data); + void *data, bool post); }; void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainerBase *bcontainer, diff --git a/include/hw/vfio/vfio-region.h b/include/hw/vfio/vfio-region.h index cbffb26962..ede6e0c8f9 100644 --- a/include/hw/vfio/vfio-region.h +++ b/include/hw/vfio/vfio-region.h @@ -29,6 +29,7 @@ typedef struct VFIORegion { uint32_t nr_mmaps; VFIOMmap *mmaps; uint8_t nr; /* cache the region number for debug */ + bool post_wr; /* writes can be posted */ } VFIORegion; From f95fd60ac164a75c586ce29ebcc94bb6df85b52a Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:14 -0700 Subject: [PATCH 1525/2760] migration: cpr helpers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the cpr_incoming_needed, cpr_open_fd, and cpr_resave_fd helpers, for use when adding cpr support for vfio and iommufd. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-2-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- include/migration/cpr.h | 5 +++++ migration/cpr.c | 36 ++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/include/migration/cpr.h b/include/migration/cpr.h index 7561fc75ad..07858e93fa 100644 --- a/include/migration/cpr.h +++ b/include/migration/cpr.h @@ -18,6 +18,9 @@ void cpr_save_fd(const char *name, int id, int fd); void cpr_delete_fd(const char *name, int id); int cpr_find_fd(const char *name, int id); +void cpr_resave_fd(const char *name, int id, int fd); +int cpr_open_fd(const char *path, int flags, const char *name, int id, + Error **errp); MigMode cpr_get_incoming_mode(void); void cpr_set_incoming_mode(MigMode mode); @@ -28,6 +31,8 @@ int cpr_state_load(MigrationChannel *channel, Error **errp); void cpr_state_close(void); struct QIOChannel *cpr_state_ioc(void); +bool cpr_incoming_needed(void *opaque); + QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp); QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp); diff --git a/migration/cpr.c b/migration/cpr.c index 42c46563e5..a50a57edca 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -95,6 +95,36 @@ int cpr_find_fd(const char *name, int id) trace_cpr_find_fd(name, id, fd); return fd; } + +void cpr_resave_fd(const char *name, int id, int fd) +{ + CprFd *elem = find_fd(&cpr_state.fds, name, id); + int old_fd = elem ? elem->fd : -1; + + if (old_fd < 0) { + cpr_save_fd(name, id, fd); + } else if (old_fd != fd) { + error_setg(&error_fatal, + "internal error: cpr fd '%s' id %d value %d " + "already saved with a different value %d", + name, id, fd, old_fd); + } +} + +int cpr_open_fd(const char *path, int flags, const char *name, int id, + Error **errp) +{ + int fd = cpr_find_fd(name, id); + + if (fd < 0) { + fd = qemu_open(path, flags, errp); + if (fd >= 0) { + cpr_save_fd(name, id, fd); + } + } + return fd; +} + /*************************************************************************/ #define CPR_STATE "CprState" @@ -228,3 +258,9 @@ void cpr_state_close(void) cpr_state_file = NULL; } } + +bool cpr_incoming_needed(void *opaque) +{ + MigMode mode = migrate_mode(); + return mode == MIG_MODE_CPR_TRANSFER; +} From 081c09dc52bab0facfdd1d34fd466479af6ac531 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:15 -0700 Subject: [PATCH 1526/2760] migration: lower handler priority MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define a vmstate priority that is lower than the default, so its handlers run after all default priority handlers. Since 0 is no longer the default priority, translate an uninitialized priority of 0 to MIG_PRI_DEFAULT. CPR for vfio will use this to install handlers for containers that run after handlers for the devices that they contain. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Reviewed-by: Peter Xu Link: https://lore.kernel.org/qemu-devel/1749569991-25171-3-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- include/migration/vmstate.h | 6 +++++- migration/savevm.c | 4 ++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/include/migration/vmstate.h b/include/migration/vmstate.h index a1dfab4460..1ff7bd9ac4 100644 --- a/include/migration/vmstate.h +++ b/include/migration/vmstate.h @@ -155,7 +155,11 @@ enum VMStateFlags { }; typedef enum { - MIG_PRI_DEFAULT = 0, + MIG_PRI_UNINITIALIZED = 0, /* An uninitialized priority field maps to */ + /* MIG_PRI_DEFAULT in save_state_priority */ + + MIG_PRI_LOW, /* Must happen after default */ + MIG_PRI_DEFAULT, MIG_PRI_IOMMU, /* Must happen before PCI devices */ MIG_PRI_PCI_BUS, /* Must happen before IOMMU */ MIG_PRI_VIRTIO_MEM, /* Must happen before IOMMU */ diff --git a/migration/savevm.c b/migration/savevm.c index 52105dd2f1..bb04a4520d 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -266,7 +266,7 @@ typedef struct SaveState { static SaveState savevm_state = { .handlers = QTAILQ_HEAD_INITIALIZER(savevm_state.handlers), - .handler_pri_head = { [MIG_PRI_DEFAULT ... MIG_PRI_MAX] = NULL }, + .handler_pri_head = { [0 ... MIG_PRI_MAX] = NULL }, .global_section_id = 0, }; @@ -737,7 +737,7 @@ static int calculate_compat_instance_id(const char *idstr) static inline MigrationPriority save_state_priority(SaveStateEntry *se) { - if (se->vmsd) { + if (se->vmsd && se->vmsd->priority) { return se->vmsd->priority; } return MIG_PRI_DEFAULT; From 54857b08168a0a74711d1f773c16a7122499027b Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:16 -0700 Subject: [PATCH 1527/2760] vfio/container: register container for cpr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register a legacy container for cpr-transfer, replacing the generic CPR register call with a more specific legacy container register call. Add a blocker if the kernel does not support VFIO_UPDATE_VADDR or VFIO_UNMAP_ALL. This is mostly boiler plate. The fields to to saved and restored are added in subsequent patches. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-4-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 7 ++-- hw/vfio/cpr-legacy.c | 68 ++++++++++++++++++++++++++++++++ hw/vfio/cpr.c | 5 +-- hw/vfio/meson.build | 1 + include/hw/vfio/vfio-container.h | 2 + include/hw/vfio/vfio-cpr.h | 15 +++++++ 6 files changed, 91 insertions(+), 7 deletions(-) create mode 100644 hw/vfio/cpr-legacy.c diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 0f948d0247..93cdf80e13 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -33,7 +33,6 @@ #include "qapi/error.h" #include "pci.h" #include "hw/vfio/vfio-container.h" -#include "hw/vfio/vfio-cpr.h" #include "vfio-helpers.h" #include "vfio-listener.h" @@ -643,7 +642,7 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, new_container = true; bcontainer = &container->bcontainer; - if (!vfio_cpr_register_container(bcontainer, errp)) { + if (!vfio_legacy_cpr_register_container(container, errp)) { goto fail; } @@ -679,7 +678,7 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, vioc->release(bcontainer); } if (new_container) { - vfio_cpr_unregister_container(bcontainer); + vfio_legacy_cpr_unregister_container(container); object_unref(container); } if (fd >= 0) { @@ -720,7 +719,7 @@ static void vfio_container_disconnect(VFIOGroup *group) VFIOAddressSpace *space = bcontainer->space; trace_vfio_container_disconnect(container->fd); - vfio_cpr_unregister_container(bcontainer); + vfio_legacy_cpr_unregister_container(container); close(container->fd); object_unref(container); diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c new file mode 100644 index 0000000000..dd7ac84074 --- /dev/null +++ b/hw/vfio/cpr-legacy.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2021-2025 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include "qemu/osdep.h" +#include "hw/vfio/vfio-container.h" +#include "migration/blocker.h" +#include "migration/cpr.h" +#include "migration/migration.h" +#include "migration/vmstate.h" +#include "qapi/error.h" + +static bool vfio_cpr_supported(VFIOContainer *container, Error **errp) +{ + if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UPDATE_VADDR)) { + error_setg(errp, "VFIO container does not support VFIO_UPDATE_VADDR"); + return false; + + } else if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UNMAP_ALL)) { + error_setg(errp, "VFIO container does not support VFIO_UNMAP_ALL"); + return false; + + } else { + return true; + } +} + +static const VMStateDescription vfio_container_vmstate = { + .name = "vfio-container", + .version_id = 0, + .minimum_version_id = 0, + .needed = cpr_incoming_needed, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; + +bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) +{ + VFIOContainerBase *bcontainer = &container->bcontainer; + Error **cpr_blocker = &container->cpr.blocker; + + migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, + vfio_cpr_reboot_notifier, + MIG_MODE_CPR_REBOOT); + + if (!vfio_cpr_supported(container, cpr_blocker)) { + return migrate_add_blocker_modes(cpr_blocker, errp, + MIG_MODE_CPR_TRANSFER, -1) == 0; + } + + vmstate_register(NULL, -1, &vfio_container_vmstate, container); + + return true; +} + +void vfio_legacy_cpr_unregister_container(VFIOContainer *container) +{ + VFIOContainerBase *bcontainer = &container->bcontainer; + + migration_remove_notifier(&bcontainer->cpr_reboot_notifier); + migrate_del_blocker(&container->cpr.blocker); + vmstate_unregister(NULL, &vfio_container_vmstate, container); +} diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index 0210e76a10..0e59612228 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -7,13 +7,12 @@ #include "qemu/osdep.h" #include "hw/vfio/vfio-device.h" -#include "migration/misc.h" #include "hw/vfio/vfio-cpr.h" #include "qapi/error.h" #include "system/runstate.h" -static int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier, - MigrationEvent *e, Error **errp) +int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) { if (e->type == MIG_EVENT_PRECOPY_SETUP && !runstate_check(RUN_STATE_SUSPENDED) && !vm_get_suspended()) { diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index bccb05098c..73d29f925f 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -21,6 +21,7 @@ system_ss.add(when: 'CONFIG_VFIO_XGMAC', if_true: files('calxeda-xgmac.c')) system_ss.add(when: 'CONFIG_VFIO_AMD_XGBE', if_true: files('amd-xgbe.c')) system_ss.add(when: 'CONFIG_VFIO', if_true: files( 'cpr.c', + 'cpr-legacy.c', 'device.c', 'migration.c', 'migration-multifd.c', diff --git a/include/hw/vfio/vfio-container.h b/include/hw/vfio/vfio-container.h index afc498da49..21e5807e48 100644 --- a/include/hw/vfio/vfio-container.h +++ b/include/hw/vfio/vfio-container.h @@ -10,6 +10,7 @@ #define HW_VFIO_CONTAINER_H #include "hw/vfio/vfio-container-base.h" +#include "hw/vfio/vfio-cpr.h" typedef struct VFIOContainer VFIOContainer; typedef struct VFIODevice VFIODevice; @@ -29,6 +30,7 @@ typedef struct VFIOContainer { int fd; /* /dev/vfio/vfio, empowered by the attached groups */ unsigned iommu_type; QLIST_HEAD(, VFIOGroup) group_list; + VFIOContainerCPR cpr; } VFIOContainer; OBJECT_DECLARE_SIMPLE_TYPE(VFIOContainer, VFIO_IOMMU_LEGACY); diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 750ea5bcea..d4e0bd53c9 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -9,8 +9,23 @@ #ifndef HW_VFIO_VFIO_CPR_H #define HW_VFIO_VFIO_CPR_H +#include "migration/misc.h" + +struct VFIOContainer; struct VFIOContainerBase; +typedef struct VFIOContainerCPR { + Error *blocker; +} VFIOContainerCPR; + + +bool vfio_legacy_cpr_register_container(struct VFIOContainer *container, + Error **errp); +void vfio_legacy_cpr_unregister_container(struct VFIOContainer *container); + +int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier, MigrationEvent *e, + Error **errp); + bool vfio_cpr_register_container(struct VFIOContainerBase *bcontainer, Error **errp); void vfio_cpr_unregister_container(struct VFIOContainerBase *bcontainer); From c29a65ed68535288551d2bf70ae32f3df371a57d Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:17 -0700 Subject: [PATCH 1528/2760] vfio/container: preserve descriptors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit At vfio creation time, save the value of vfio container, group, and device descriptors in CPR state. On qemu restart, vfio_realize() finds and uses the saved descriptors. During reuse, device and iommu state is already configured, so operations in vfio_realize that would modify the configuration, such as vfio ioctl's, are skipped. The result is that vfio_realize constructs qemu data structures that reflect the current state of the device. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1749569991-25171-5-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 67 +++++++++++++++++++++++++++++--------- hw/vfio/cpr-legacy.c | 42 ++++++++++++++++++++++++ include/hw/vfio/vfio-cpr.h | 6 ++++ 3 files changed, 100 insertions(+), 15 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 93cdf80e13..5caae4ccae 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -31,6 +31,8 @@ #include "system/reset.h" #include "trace.h" #include "qapi/error.h" +#include "migration/cpr.h" +#include "migration/blocker.h" #include "pci.h" #include "hw/vfio/vfio-container.h" #include "vfio-helpers.h" @@ -425,7 +427,12 @@ static VFIOContainer *vfio_create_container(int fd, VFIOGroup *group, return NULL; } - if (!vfio_set_iommu(fd, group->fd, &iommu_type, errp)) { + /* + * During CPR, just set the container type and skip the ioctls, as the + * container and group are already configured in the kernel. + */ + if (!cpr_is_incoming() && + !vfio_set_iommu(fd, group->fd, &iommu_type, errp)) { return NULL; } @@ -592,6 +599,11 @@ static bool vfio_container_group_add(VFIOContainer *container, VFIOGroup *group, group->container = container; QLIST_INSERT_HEAD(&container->group_list, group, container_next); vfio_group_add_kvm_device(group); + /* + * Remember the container fd for each group, so we can attach to the same + * container after CPR. + */ + cpr_resave_fd("vfio_container_for_group", group->groupid, container->fd); return true; } @@ -601,6 +613,7 @@ static void vfio_container_group_del(VFIOContainer *container, VFIOGroup *group) group->container = NULL; vfio_group_del_kvm_device(group); vfio_ram_block_discard_disable(container, false); + cpr_delete_fd("vfio_container_for_group", group->groupid); } static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, @@ -615,17 +628,34 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, bool group_was_added = false; space = vfio_address_space_get(as); + fd = cpr_find_fd("vfio_container_for_group", group->groupid); - QLIST_FOREACH(bcontainer, &space->containers, next) { - container = container_of(bcontainer, VFIOContainer, bcontainer); - if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { - return vfio_container_group_add(container, group, errp); + if (!cpr_is_incoming()) { + QLIST_FOREACH(bcontainer, &space->containers, next) { + container = container_of(bcontainer, VFIOContainer, bcontainer); + if (!ioctl(group->fd, VFIO_GROUP_SET_CONTAINER, &container->fd)) { + return vfio_container_group_add(container, group, errp); + } } - } - fd = qemu_open("/dev/vfio/vfio", O_RDWR, errp); - if (fd < 0) { - goto fail; + fd = qemu_open("/dev/vfio/vfio", O_RDWR, errp); + if (fd < 0) { + goto fail; + } + } else { + /* + * For incoming CPR, the group is already attached in the kernel. + * If a container with matching fd is found, then update the + * userland group list and return. If not, then after the loop, + * create the container struct and group list. + */ + QLIST_FOREACH(bcontainer, &space->containers, next) { + container = container_of(bcontainer, VFIOContainer, bcontainer); + + if (vfio_cpr_container_match(container, group, fd)) { + return vfio_container_group_add(container, group, errp); + } + } } ret = ioctl(fd, VFIO_GET_API_VERSION); @@ -697,6 +727,7 @@ static void vfio_container_disconnect(VFIOGroup *group) QLIST_REMOVE(group, container_next); group->container = NULL; + cpr_delete_fd("vfio_container_for_group", group->groupid); /* * Explicitly release the listener first before unset container, @@ -750,7 +781,7 @@ static VFIOGroup *vfio_group_get(int groupid, AddressSpace *as, Error **errp) group = g_malloc0(sizeof(*group)); snprintf(path, sizeof(path), "/dev/vfio/%d", groupid); - group->fd = qemu_open(path, O_RDWR, errp); + group->fd = cpr_open_fd(path, O_RDWR, "vfio_group", groupid, errp); if (group->fd < 0) { goto free_group_exit; } @@ -782,6 +813,7 @@ static VFIOGroup *vfio_group_get(int groupid, AddressSpace *as, Error **errp) return group; close_fd_exit: + cpr_delete_fd("vfio_group", groupid); close(group->fd); free_group_exit: @@ -803,6 +835,7 @@ static void vfio_group_put(VFIOGroup *group) vfio_container_disconnect(group); QLIST_REMOVE(group, next); trace_vfio_group_put(group->fd); + cpr_delete_fd("vfio_group", group->groupid); close(group->fd); g_free(group); } @@ -813,7 +846,7 @@ static bool vfio_device_get(VFIOGroup *group, const char *name, g_autofree struct vfio_device_info *info = NULL; int fd; - fd = ioctl(group->fd, VFIO_GROUP_GET_DEVICE_FD, name); + fd = vfio_cpr_group_get_device_fd(group->fd, name); if (fd < 0) { error_setg_errno(errp, errno, "error getting device from group %d", group->groupid); @@ -826,8 +859,7 @@ static bool vfio_device_get(VFIOGroup *group, const char *name, info = vfio_get_device_info(fd); if (!info) { error_setg_errno(errp, errno, "error getting device info"); - close(fd); - return false; + goto fail; } /* @@ -841,8 +873,7 @@ static bool vfio_device_get(VFIOGroup *group, const char *name, if (!QLIST_EMPTY(&group->device_list)) { error_setg(errp, "Inconsistent setting of support for discarding " "RAM (e.g., balloon) within group"); - close(fd); - return false; + goto fail; } if (!group->ram_block_discard_allowed) { @@ -860,6 +891,11 @@ static bool vfio_device_get(VFIOGroup *group, const char *name, trace_vfio_device_get(name, info->flags, info->num_regions, info->num_irqs); return true; + +fail: + close(fd); + cpr_delete_fd(name, 0); + return false; } static void vfio_device_put(VFIODevice *vbasedev) @@ -870,6 +906,7 @@ static void vfio_device_put(VFIODevice *vbasedev) QLIST_REMOVE(vbasedev, next); vbasedev->group = NULL; trace_vfio_device_put(vbasedev->fd); + cpr_delete_fd(vbasedev->name, 0); close(vbasedev->fd); } diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index dd7ac84074..ac4a9ab315 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -8,6 +8,7 @@ #include #include "qemu/osdep.h" #include "hw/vfio/vfio-container.h" +#include "hw/vfio/vfio-device.h" #include "migration/blocker.h" #include "migration/cpr.h" #include "migration/migration.h" @@ -66,3 +67,44 @@ void vfio_legacy_cpr_unregister_container(VFIOContainer *container) migrate_del_blocker(&container->cpr.blocker); vmstate_unregister(NULL, &vfio_container_vmstate, container); } + +int vfio_cpr_group_get_device_fd(int d, const char *name) +{ + const int id = 0; + int fd = cpr_find_fd(name, id); + + if (fd < 0) { + fd = ioctl(d, VFIO_GROUP_GET_DEVICE_FD, name); + if (fd >= 0) { + cpr_save_fd(name, id, fd); + } + } + return fd; +} + +static bool same_device(int fd1, int fd2) +{ + struct stat st1, st2; + + return !fstat(fd1, &st1) && !fstat(fd2, &st2) && st1.st_dev == st2.st_dev; +} + +bool vfio_cpr_container_match(VFIOContainer *container, VFIOGroup *group, + int fd) +{ + if (container->fd == fd) { + return true; + } + if (!same_device(container->fd, fd)) { + return false; + } + /* + * Same device, different fd. This occurs when the container fd is + * cpr_save'd multiple times, once for each groupid, so SCM_RIGHTS + * produces duplicates. De-dup it. + */ + cpr_delete_fd("vfio_container_for_group", group->groupid); + close(fd); + cpr_save_fd("vfio_container_for_group", group->groupid, container->fd); + return true; +} diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index d4e0bd53c9..5a2e5f6b21 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -13,6 +13,7 @@ struct VFIOContainer; struct VFIOContainerBase; +struct VFIOGroup; typedef struct VFIOContainerCPR { Error *blocker; @@ -30,4 +31,9 @@ bool vfio_cpr_register_container(struct VFIOContainerBase *bcontainer, Error **errp); void vfio_cpr_unregister_container(struct VFIOContainerBase *bcontainer); +int vfio_cpr_group_get_device_fd(int d, const char *name); + +bool vfio_cpr_container_match(struct VFIOContainer *container, + struct VFIOGroup *group, int fd); + #endif /* HW_VFIO_VFIO_CPR_H */ From 1faadd96306b428f2e8e35f71761c333a6777f55 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:18 -0700 Subject: [PATCH 1529/2760] vfio/container: discard old DMA vaddr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the container pre_save handler, discard the virtual addresses in DMA mappings with VFIO_DMA_UNMAP_FLAG_VADDR, because guest RAM will be remapped at a different VA after in new QEMU. DMA to already-mapped pages continues. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-6-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-legacy.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index ac4a9ab315..ef106d0ea8 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -15,6 +15,22 @@ #include "migration/vmstate.h" #include "qapi/error.h" +static bool vfio_dma_unmap_vaddr_all(VFIOContainer *container, Error **errp) +{ + struct vfio_iommu_type1_dma_unmap unmap = { + .argsz = sizeof(unmap), + .flags = VFIO_DMA_UNMAP_FLAG_VADDR | VFIO_DMA_UNMAP_FLAG_ALL, + .iova = 0, + .size = 0, + }; + if (ioctl(container->fd, VFIO_IOMMU_UNMAP_DMA, &unmap)) { + error_setg_errno(errp, errno, "vfio_dma_unmap_vaddr_all"); + return false; + } + return true; +} + + static bool vfio_cpr_supported(VFIOContainer *container, Error **errp) { if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UPDATE_VADDR)) { @@ -30,10 +46,23 @@ static bool vfio_cpr_supported(VFIOContainer *container, Error **errp) } } +static int vfio_container_pre_save(void *opaque) +{ + VFIOContainer *container = opaque; + Error *local_err = NULL; + + if (!vfio_dma_unmap_vaddr_all(container, &local_err)) { + error_report_err(local_err); + return -1; + } + return 0; +} + static const VMStateDescription vfio_container_vmstate = { .name = "vfio-container", .version_id = 0, .minimum_version_id = 0, + .pre_save = vfio_container_pre_save, .needed = cpr_incoming_needed, .fields = (VMStateField[]) { VMSTATE_END_OF_LIST() From 7e9f21411302d823e9ee563ff6a2a40b1ffc1266 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:19 -0700 Subject: [PATCH 1530/2760] vfio/container: restore DMA vaddr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In new QEMU, do not register the memory listener at device creation time. Register it later, in the container post_load handler, after all vmstate that may affect regions and mapping boundaries has been loaded. The post_load registration will cause the listener to invoke its callback on each flat section, and the calls will match the mappings remembered by the kernel. The listener calls a special dma_map handler that passes the new VA of each section to the kernel using VFIO_DMA_MAP_FLAG_VADDR. Restore the normal handler at the end. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-7-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 15 ++++++++-- hw/vfio/cpr-legacy.c | 57 ++++++++++++++++++++++++++++++++++++++ include/hw/vfio/vfio-cpr.h | 3 ++ 3 files changed, 73 insertions(+), 2 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 5caae4ccae..936ce37f19 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -136,6 +136,8 @@ static int vfio_legacy_dma_unmap_one(const VFIOContainerBase *bcontainer, int ret; Error *local_err = NULL; + g_assert(!cpr_is_incoming()); + if (iotlb && vfio_container_dirty_tracking_is_started(bcontainer)) { if (!vfio_container_devices_dirty_tracking_is_supported(bcontainer) && bcontainer->dirty_pages_supported) { @@ -690,8 +692,17 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, } group_was_added = true; - if (!vfio_listener_register(bcontainer, errp)) { - goto fail; + /* + * If CPR, register the listener later, after all state that may + * affect regions and mapping boundaries has been cpr load'ed. Later, + * the listener will invoke its callback on each flat section and call + * dma_map to supply the new vaddr, and the calls will match the mappings + * remembered by the kernel. + */ + if (!cpr_is_incoming()) { + if (!vfio_listener_register(bcontainer, errp)) { + goto fail; + } } bcontainer->initialized = true; diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index ef106d0ea8..2fd8348c7c 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -9,11 +9,13 @@ #include "qemu/osdep.h" #include "hw/vfio/vfio-container.h" #include "hw/vfio/vfio-device.h" +#include "hw/vfio/vfio-listener.h" #include "migration/blocker.h" #include "migration/cpr.h" #include "migration/migration.h" #include "migration/vmstate.h" #include "qapi/error.h" +#include "qemu/error-report.h" static bool vfio_dma_unmap_vaddr_all(VFIOContainer *container, Error **errp) { @@ -30,6 +32,32 @@ static bool vfio_dma_unmap_vaddr_all(VFIOContainer *container, Error **errp) return true; } +/* + * Set the new @vaddr for any mappings registered during cpr load. + * The incoming state is cleared thereafter. + */ +static int vfio_legacy_cpr_dma_map(const VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, void *vaddr, + bool readonly, MemoryRegion *mr) +{ + const VFIOContainer *container = container_of(bcontainer, VFIOContainer, + bcontainer); + struct vfio_iommu_type1_dma_map map = { + .argsz = sizeof(map), + .flags = VFIO_DMA_MAP_FLAG_VADDR, + .vaddr = (__u64)(uintptr_t)vaddr, + .iova = iova, + .size = size, + }; + + g_assert(cpr_is_incoming()); + + if (ioctl(container->fd, VFIO_IOMMU_MAP_DMA, &map)) { + return -errno; + } + + return 0; +} static bool vfio_cpr_supported(VFIOContainer *container, Error **errp) { @@ -58,11 +86,34 @@ static int vfio_container_pre_save(void *opaque) return 0; } +static int vfio_container_post_load(void *opaque, int version_id) +{ + VFIOContainer *container = opaque; + VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOGroup *group; + Error *local_err = NULL; + + if (!vfio_listener_register(bcontainer, &local_err)) { + error_report_err(local_err); + return -1; + } + + QLIST_FOREACH(group, &container->group_list, container_next) { + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + /* Restore original dma_map function */ + vioc->dma_map = container->cpr.saved_dma_map; + } + return 0; +} + static const VMStateDescription vfio_container_vmstate = { .name = "vfio-container", .version_id = 0, .minimum_version_id = 0, + .priority = MIG_PRI_LOW, /* Must happen after devices and groups */ .pre_save = vfio_container_pre_save, + .post_load = vfio_container_post_load, .needed = cpr_incoming_needed, .fields = (VMStateField[]) { VMSTATE_END_OF_LIST() @@ -85,6 +136,12 @@ bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) vmstate_register(NULL, -1, &vfio_container_vmstate, container); + /* During incoming CPR, divert calls to dma_map. */ + if (cpr_is_incoming()) { + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + container->cpr.saved_dma_map = vioc->dma_map; + vioc->dma_map = vfio_legacy_cpr_dma_map; + } return true; } diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 5a2e5f6b21..04624475f8 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -17,6 +17,9 @@ struct VFIOGroup; typedef struct VFIOContainerCPR { Error *blocker; + int (*saved_dma_map)(const struct VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + void *vaddr, bool readonly, MemoryRegion *mr); } VFIOContainerCPR; From dac0dd68d9b150a6aa334ab8ee9aeba011d54b32 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:20 -0700 Subject: [PATCH 1531/2760] vfio/container: mdev cpr blocker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During CPR, after VFIO_DMA_UNMAP_FLAG_VADDR, the vaddr is temporarily invalid, so mediated devices cannot be supported. Add a blocker for them. This restriction will not apply to iommufd containers when CPR is added for them in a future patch. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-8-git-send-email-steven.sistare@oracle.com [ clg: Fixed context change in VFIODevice ] Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 8 ++++++++ include/hw/vfio/vfio-cpr.h | 3 +++ include/hw/vfio/vfio-device.h | 2 ++ 3 files changed, 13 insertions(+) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 936ce37f19..3e8d645ebb 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -987,6 +987,13 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, goto device_put_exit; } + if (vbasedev->mdev) { + error_setg(&vbasedev->cpr.mdev_blocker, + "CPR does not support vfio mdev %s", vbasedev->name); + migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker, &error_fatal, + MIG_MODE_CPR_TRANSFER, -1); + } + return true; device_put_exit: @@ -1004,6 +1011,7 @@ static void vfio_legacy_detach_device(VFIODevice *vbasedev) vfio_device_unprepare(vbasedev); + migrate_del_blocker(&vbasedev->cpr.mdev_blocker); object_unref(vbasedev->hiod); vfio_device_put(vbasedev); vfio_group_put(group); diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 04624475f8..b83dd42751 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -22,6 +22,9 @@ typedef struct VFIOContainerCPR { void *vaddr, bool readonly, MemoryRegion *mr); } VFIOContainerCPR; +typedef struct VFIODeviceCPR { + Error *mdev_blocker; +} VFIODeviceCPR; bool vfio_legacy_cpr_register_container(struct VFIOContainer *container, Error **errp); diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 9793b2dba0..f39259406b 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -28,6 +28,7 @@ #endif #include "system/system.h" #include "hw/vfio/vfio-container-base.h" +#include "hw/vfio/vfio-cpr.h" #include "system/host_iommu_device.h" #include "system/iommufd.h" @@ -86,6 +87,7 @@ typedef struct VFIODevice { QLIST_ENTRY(VFIODevice) hwpt_next; struct vfio_region_info **reginfo; int *region_fds; + VFIODeviceCPR cpr; } VFIODevice; struct VFIODeviceOps { From eba1f657cbb1b525e2b069626de655da21084e3e Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:21 -0700 Subject: [PATCH 1532/2760] vfio/container: recover from unmap-all-vaddr failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If there are multiple containers and unmap-all fails for some container, we need to remap vaddr for the other containers for which unmap-all succeeded. Recover by walking all address ranges of all containers to restore the vaddr for each. Do so by invoking the vfio listener callback, and passing a new "remap" flag that tells it to restore a mapping without re-allocating new userland data structures. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-9-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-legacy.c | 91 +++++++++++++++++++++++++++ hw/vfio/listener.c | 19 +++++- include/hw/vfio/vfio-container-base.h | 3 + include/hw/vfio/vfio-cpr.h | 10 +++ 4 files changed, 122 insertions(+), 1 deletion(-) diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index 2fd8348c7c..a84c3247b7 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -29,6 +29,7 @@ static bool vfio_dma_unmap_vaddr_all(VFIOContainer *container, Error **errp) error_setg_errno(errp, errno, "vfio_dma_unmap_vaddr_all"); return false; } + container->cpr.vaddr_unmapped = true; return true; } @@ -59,6 +60,14 @@ static int vfio_legacy_cpr_dma_map(const VFIOContainerBase *bcontainer, return 0; } +static void vfio_region_remap(MemoryListener *listener, + MemoryRegionSection *section) +{ + VFIOContainer *container = container_of(listener, VFIOContainer, + cpr.remap_listener); + vfio_container_region_add(&container->bcontainer, section, true); +} + static bool vfio_cpr_supported(VFIOContainer *container, Error **errp) { if (!ioctl(container->fd, VFIO_CHECK_EXTENSION, VFIO_UPDATE_VADDR)) { @@ -120,6 +129,40 @@ static const VMStateDescription vfio_container_vmstate = { } }; +static int vfio_cpr_fail_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) +{ + VFIOContainer *container = + container_of(notifier, VFIOContainer, cpr.transfer_notifier); + VFIOContainerBase *bcontainer = &container->bcontainer; + + if (e->type != MIG_EVENT_PRECOPY_FAILED) { + return 0; + } + + if (container->cpr.vaddr_unmapped) { + /* + * Force a call to vfio_region_remap for each mapped section by + * temporarily registering a listener, and temporarily diverting + * dma_map to vfio_legacy_cpr_dma_map. The latter restores vaddr. + */ + + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + vioc->dma_map = vfio_legacy_cpr_dma_map; + + container->cpr.remap_listener = (MemoryListener) { + .name = "vfio cpr recover", + .region_add = vfio_region_remap + }; + memory_listener_register(&container->cpr.remap_listener, + bcontainer->space->as); + memory_listener_unregister(&container->cpr.remap_listener); + container->cpr.vaddr_unmapped = false; + vioc->dma_map = container->cpr.saved_dma_map; + } + return 0; +} + bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) { VFIOContainerBase *bcontainer = &container->bcontainer; @@ -142,6 +185,10 @@ bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) container->cpr.saved_dma_map = vioc->dma_map; vioc->dma_map = vfio_legacy_cpr_dma_map; } + + migration_add_notifier_mode(&container->cpr.transfer_notifier, + vfio_cpr_fail_notifier, + MIG_MODE_CPR_TRANSFER); return true; } @@ -152,6 +199,50 @@ void vfio_legacy_cpr_unregister_container(VFIOContainer *container) migration_remove_notifier(&bcontainer->cpr_reboot_notifier); migrate_del_blocker(&container->cpr.blocker); vmstate_unregister(NULL, &vfio_container_vmstate, container); + migration_remove_notifier(&container->cpr.transfer_notifier); +} + +/* + * In old QEMU, VFIO_DMA_UNMAP_FLAG_VADDR may fail on some mapping after + * succeeding for others, so the latter have lost their vaddr. Call this + * to restore vaddr for a section with a giommu. + * + * The giommu already exists. Find it and replay it, which calls + * vfio_legacy_cpr_dma_map further down the stack. + */ +void vfio_cpr_giommu_remap(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) +{ + VFIOGuestIOMMU *giommu = NULL; + hwaddr as_offset = section->offset_within_address_space; + hwaddr iommu_offset = as_offset - section->offset_within_region; + + QLIST_FOREACH(giommu, &bcontainer->giommu_list, giommu_next) { + if (giommu->iommu_mr == IOMMU_MEMORY_REGION(section->mr) && + giommu->iommu_offset == iommu_offset) { + break; + } + } + g_assert(giommu); + memory_region_iommu_replay(giommu->iommu_mr, &giommu->n); +} + +/* + * In old QEMU, VFIO_DMA_UNMAP_FLAG_VADDR may fail on some mapping after + * succeeding for others, so the latter have lost their vaddr. Call this + * to restore vaddr for a section with a RamDiscardManager. + * + * The ram discard listener already exists. Call its populate function + * directly, which calls vfio_legacy_cpr_dma_map. + */ +bool vfio_cpr_ram_discard_register_listener(VFIOContainerBase *bcontainer, + MemoryRegionSection *section) +{ + VFIORamDiscardListener *vrdl = + vfio_find_ram_discard_listener(bcontainer, section); + + g_assert(vrdl); + return vrdl->listener.notify_populate(&vrdl->listener, section) == 0; } int vfio_cpr_group_get_device_fd(int d, const char *name) diff --git a/hw/vfio/listener.c b/hw/vfio/listener.c index 735b5f21b7..f498e23a93 100644 --- a/hw/vfio/listener.c +++ b/hw/vfio/listener.c @@ -481,6 +481,13 @@ static void vfio_listener_region_add(MemoryListener *listener, { VFIOContainerBase *bcontainer = container_of(listener, VFIOContainerBase, listener); + vfio_container_region_add(bcontainer, section, false); +} + +void vfio_container_region_add(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, + bool cpr_remap) +{ hwaddr iova, end; Int128 llend, llsize; void *vaddr; @@ -516,6 +523,11 @@ static void vfio_listener_region_add(MemoryListener *listener, int iommu_idx; trace_vfio_listener_region_add_iommu(section->mr->name, iova, end); + + if (cpr_remap) { + vfio_cpr_giommu_remap(bcontainer, section); + } + /* * FIXME: For VFIO iommu types which have KVM acceleration to * avoid bouncing all map/unmaps through qemu this way, this @@ -558,7 +570,12 @@ static void vfio_listener_region_add(MemoryListener *listener, * about changes. */ if (memory_region_has_ram_discard_manager(section->mr)) { - vfio_ram_discard_register_listener(bcontainer, section); + if (!cpr_remap) { + vfio_ram_discard_register_listener(bcontainer, section); + } else if (!vfio_cpr_ram_discard_register_listener(bcontainer, + section)) { + goto fail; + } return; } diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 9d37f86115..f0232654ee 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -256,4 +256,7 @@ struct VFIOIOMMUClass { VFIORamDiscardListener *vfio_find_ram_discard_listener( VFIOContainerBase *bcontainer, MemoryRegionSection *section); +void vfio_container_region_add(VFIOContainerBase *bcontainer, + MemoryRegionSection *section, bool cpr_remap); + #endif /* HW_VFIO_VFIO_CONTAINER_BASE_H */ diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index b83dd42751..56ede049ad 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -10,6 +10,7 @@ #define HW_VFIO_VFIO_CPR_H #include "migration/misc.h" +#include "system/memory.h" struct VFIOContainer; struct VFIOContainerBase; @@ -17,6 +18,9 @@ struct VFIOGroup; typedef struct VFIOContainerCPR { Error *blocker; + bool vaddr_unmapped; + NotifierWithReturn transfer_notifier; + MemoryListener remap_listener; int (*saved_dma_map)(const struct VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr); @@ -42,4 +46,10 @@ int vfio_cpr_group_get_device_fd(int d, const char *name); bool vfio_cpr_container_match(struct VFIOContainer *container, struct VFIOGroup *group, int fd); +void vfio_cpr_giommu_remap(struct VFIOContainerBase *bcontainer, + MemoryRegionSection *section); + +bool vfio_cpr_ram_discard_register_listener( + struct VFIOContainerBase *bcontainer, MemoryRegionSection *section); + #endif /* HW_VFIO_VFIO_CPR_H */ From 8df3fa3d6753332a64eca53780517972075966e1 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:22 -0700 Subject: [PATCH 1533/2760] pci: export msix_is_pending MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Export msix_is_pending for use by cpr. No functional change. Signed-off-by: Steve Sistare Acked-by: Michael S. Tsirkin Link: https://lore.kernel.org/qemu-devel/1749569991-25171-10-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/pci/msix.c | 2 +- include/hw/pci/msix.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/pci/msix.c b/hw/pci/msix.c index 66f27b9d71..8c7f6709e2 100644 --- a/hw/pci/msix.c +++ b/hw/pci/msix.c @@ -72,7 +72,7 @@ static uint8_t *msix_pending_byte(PCIDevice *dev, int vector) return dev->msix_pba + vector / 8; } -static int msix_is_pending(PCIDevice *dev, int vector) +int msix_is_pending(PCIDevice *dev, unsigned int vector) { return *msix_pending_byte(dev, vector) & msix_pending_mask(vector); } diff --git a/include/hw/pci/msix.h b/include/hw/pci/msix.h index 0e6f257e45..11ef9454c1 100644 --- a/include/hw/pci/msix.h +++ b/include/hw/pci/msix.h @@ -32,6 +32,7 @@ int msix_present(PCIDevice *dev); bool msix_is_masked(PCIDevice *dev, unsigned vector); void msix_set_pending(PCIDevice *dev, unsigned vector); void msix_clr_pending(PCIDevice *dev, int vector); +int msix_is_pending(PCIDevice *dev, unsigned vector); void msix_vector_use(PCIDevice *dev, unsigned vector); void msix_vector_unuse(PCIDevice *dev, unsigned vector); From 24c156dcd94299f64994170ce84efac9ae001f41 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 10:26:43 -0700 Subject: [PATCH 1534/2760] pci: skip reset during cpr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not reset a vfio-pci device during CPR. Signed-off-by: Steve Sistare Acked-by: Michael S. Tsirkin Link: https://lore.kernel.org/qemu-devel/1749576403-25355-1-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/pci/pci.c | 5 +++++ hw/vfio/pci.c | 7 +++++++ include/hw/pci/pci.h | 2 ++ 3 files changed, 14 insertions(+) diff --git a/hw/pci/pci.c b/hw/pci/pci.c index 9b4bf48439..c70b5ceeba 100644 --- a/hw/pci/pci.c +++ b/hw/pci/pci.c @@ -32,6 +32,7 @@ #include "hw/pci/pci_host.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" +#include "migration/cpr.h" #include "migration/qemu-file-types.h" #include "migration/vmstate.h" #include "net/net.h" @@ -537,6 +538,10 @@ static void pci_reset_regions(PCIDevice *dev) static void pci_do_device_reset(PCIDevice *dev) { + if ((dev->cap_present & QEMU_PCI_SKIP_RESET_ON_CPR) && cpr_is_incoming()) { + return; + } + pci_device_deassert_intx(dev); assert(dev->irq_state == 0); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 89f9246416..b97261c61b 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3411,6 +3411,13 @@ static void vfio_instance_init(Object *obj) /* QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command * line, therefore, no need to wait to realize like other devices */ pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; + + /* + * A device that is resuming for cpr is already configured, so do not + * reset it during qemu_system_reset prior to cpr load, else interrupts + * may be lost. + */ + pci_dev->cap_present |= QEMU_PCI_SKIP_RESET_ON_CPR; } static void vfio_pci_base_dev_class_init(ObjectClass *klass, const void *data) diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index 35d59d7672..df3cc7b875 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -222,6 +222,8 @@ enum { QEMU_PCIE_EXT_TAG = (1 << QEMU_PCIE_EXT_TAG_BITNR), #define QEMU_PCI_CAP_PM_BITNR 14 QEMU_PCI_CAP_PM = (1 << QEMU_PCI_CAP_PM_BITNR), +#define QEMU_PCI_SKIP_RESET_ON_CPR_BITNR 15 + QEMU_PCI_SKIP_RESET_ON_CPR = (1 << QEMU_PCI_SKIP_RESET_ON_CPR_BITNR), }; typedef struct PCIINTxRoute { From 031fbb7110a183053b431fb667867a5244910e75 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:24 -0700 Subject: [PATCH 1535/2760] vfio-pci: skip reset during cpr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not reset a vfio-pci device during CPR, and do not complain if the kernel's PCI config space changes for non-emulated bits between the vmstate save and load, which can happen due to ongoing interrupt activity. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-12-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 31 +++++++++++++++++++++++++++++++ hw/vfio/pci.c | 7 +++++++ include/hw/vfio/vfio-cpr.h | 2 ++ 3 files changed, 40 insertions(+) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index 0e59612228..fdbb58e203 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -8,6 +8,8 @@ #include "qemu/osdep.h" #include "hw/vfio/vfio-device.h" #include "hw/vfio/vfio-cpr.h" +#include "hw/vfio/pci.h" +#include "migration/cpr.h" #include "qapi/error.h" #include "system/runstate.h" @@ -37,3 +39,32 @@ void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer) { migration_remove_notifier(&bcontainer->cpr_reboot_notifier); } + +/* + * The kernel may change non-emulated config bits. Exclude them from the + * changed-bits check in get_pci_config_device. + */ +static int vfio_cpr_pci_pre_load(void *opaque) +{ + VFIOPCIDevice *vdev = opaque; + PCIDevice *pdev = &vdev->pdev; + int size = MIN(pci_config_size(pdev), vdev->config_size); + int i; + + for (i = 0; i < size; i++) { + pdev->cmask[i] &= vdev->emulated_config_bits[i]; + } + + return 0; +} + +const VMStateDescription vfio_cpr_pci_vmstate = { + .name = "vfio-cpr-pci", + .version_id = 0, + .minimum_version_id = 0, + .pre_load = vfio_cpr_pci_pre_load, + .needed = cpr_incoming_needed, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index b97261c61b..2da5989581 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -30,6 +30,7 @@ #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" #include "migration/vmstate.h" +#include "migration/cpr.h" #include "qobject/qdict.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" @@ -3354,6 +3355,11 @@ static void vfio_pci_reset(DeviceState *dev) { VFIOPCIDevice *vdev = VFIO_PCI_BASE(dev); + /* Do not reset the device during qemu_system_reset prior to cpr load */ + if (cpr_is_incoming()) { + return; + } + trace_vfio_pci_reset(vdev->vbasedev.name); vfio_pci_pre_reset(vdev); @@ -3530,6 +3536,7 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, const void *data) #ifdef CONFIG_IOMMUFD object_class_property_add_str(klass, "fd", NULL, vfio_pci_set_fd); #endif + dc->vmsd = &vfio_cpr_pci_vmstate; dc->desc = "VFIO-based PCI device assignment"; pdc->realize = vfio_pci_realize; diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 56ede049ad..8bf85b9f4e 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -52,4 +52,6 @@ void vfio_cpr_giommu_remap(struct VFIOContainerBase *bcontainer, bool vfio_cpr_ram_discard_register_listener( struct VFIOContainerBase *bcontainer, MemoryRegionSection *section); +extern const VMStateDescription vfio_cpr_pci_vmstate; + #endif /* HW_VFIO_VFIO_CPR_H */ From 906f524ef19fa46140c3a5ae74d5e2e7d7b37ab2 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:25 -0700 Subject: [PATCH 1536/2760] vfio/pci: vfio_pci_vector_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract a subroutine vfio_pci_vector_init. No functional change. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-13-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 2da5989581..7a33c19ea2 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -531,6 +531,22 @@ static void set_irq_signalling(VFIODevice *vbasedev, VFIOMSIVector *vector, } } +static void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr) +{ + VFIOMSIVector *vector = &vdev->msi_vectors[nr]; + PCIDevice *pdev = &vdev->pdev; + + vector->vdev = vdev; + vector->virq = -1; + if (event_notifier_init(&vector->interrupt, 0)) { + error_report("vfio: Error: event_notifier_init failed"); + } + vector->use = true; + if (vdev->interrupt == VFIO_INT_MSIX) { + msix_vector_use(pdev, nr); + } +} + static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, MSIMessage *msg, IOHandler *handler) { @@ -544,13 +560,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, vector = &vdev->msi_vectors[nr]; if (!vector->use) { - vector->vdev = vdev; - vector->virq = -1; - if (event_notifier_init(&vector->interrupt, 0)) { - error_report("vfio: Error: event_notifier_init failed"); - } - vector->use = true; - msix_vector_use(pdev, nr); + vfio_pci_vector_init(vdev, nr); } qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), From 8f5c6960269124674895032f344fa71f8be5d49c Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:26 -0700 Subject: [PATCH 1537/2760] vfio/pci: vfio_notifier_init MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move event_notifier_init calls to a helper vfio_notifier_init. This version is trivial, but it will be expanded to support CPR in subsequent patches. No functional change. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-14-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 7a33c19ea2..dc0d9a7fbb 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -57,6 +57,16 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev); static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled); static void vfio_msi_disable_common(VFIOPCIDevice *vdev); +static bool vfio_notifier_init(EventNotifier *e, const char *name, Error **errp) +{ + int ret = event_notifier_init(e, 0); + + if (ret) { + error_setg_errno(errp, -ret, "vfio_notifier_init %s failed", name); + } + return !ret; +} + /* * Disabling BAR mmaping can be slow, but toggling it around INTx can * also be a huge overhead. We try to get the best of both worlds by @@ -137,8 +147,7 @@ static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) pci_irq_deassert(&vdev->pdev); /* Get an eventfd for resample/unmask */ - if (event_notifier_init(&vdev->intx.unmask, 0)) { - error_setg(errp, "event_notifier_init failed eoi"); + if (!vfio_notifier_init(&vdev->intx.unmask, "intx-unmask", errp)) { goto fail; } @@ -269,7 +278,6 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) uint8_t pin = vfio_pci_read_config(&vdev->pdev, PCI_INTERRUPT_PIN, 1); Error *err = NULL; int32_t fd; - int ret; if (!pin) { @@ -292,9 +300,7 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) } #endif - ret = event_notifier_init(&vdev->intx.interrupt, 0); - if (ret) { - error_setg_errno(errp, -ret, "event_notifier_init failed"); + if (!vfio_notifier_init(&vdev->intx.interrupt, "intx-interrupt", errp)) { return false; } fd = event_notifier_get_fd(&vdev->intx.interrupt); @@ -474,11 +480,13 @@ static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector) { + const char *name = "kvm_interrupt"; + if (vector->virq < 0) { return; } - if (event_notifier_init(&vector->kvm_interrupt, 0)) { + if (!vfio_notifier_init(&vector->kvm_interrupt, name, NULL)) { goto fail_notifier; } @@ -535,11 +543,12 @@ static void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr) { VFIOMSIVector *vector = &vdev->msi_vectors[nr]; PCIDevice *pdev = &vdev->pdev; + Error *err = NULL; vector->vdev = vdev; vector->virq = -1; - if (event_notifier_init(&vector->interrupt, 0)) { - error_report("vfio: Error: event_notifier_init failed"); + if (!vfio_notifier_init(&vector->interrupt, "interrupt", &err)) { + error_report_err(err); } vector->use = true; if (vdev->interrupt == VFIO_INT_MSIX) { @@ -755,13 +764,14 @@ static void vfio_msi_enable(VFIOPCIDevice *vdev) for (i = 0; i < vdev->nr_vectors; i++) { VFIOMSIVector *vector = &vdev->msi_vectors[i]; + Error *err = NULL; vector->vdev = vdev; vector->virq = -1; vector->use = true; - if (event_notifier_init(&vector->interrupt, 0)) { - error_report("vfio: Error: event_notifier_init failed"); + if (!vfio_notifier_init(&vector->interrupt, "interrupt", &err)) { + error_report_err(err); } qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), @@ -2928,8 +2938,8 @@ void vfio_pci_register_err_notifier(VFIOPCIDevice *vdev) return; } - if (event_notifier_init(&vdev->err_notifier, 0)) { - error_report("vfio: Unable to init event notifier for error detection"); + if (!vfio_notifier_init(&vdev->err_notifier, "err_notifier", &err)) { + error_report_err(err); vdev->pci_aer = false; return; } @@ -2995,8 +3005,8 @@ void vfio_pci_register_req_notifier(VFIOPCIDevice *vdev) return; } - if (event_notifier_init(&vdev->req_notifier, 0)) { - error_report("vfio: Unable to init event notifier for device request"); + if (!vfio_notifier_init(&vdev->req_notifier, "req_notifier", &err)) { + error_report_err(err); return; } From d364d802fe4a5d73b2e88cb9b80799692f1ee8a5 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:27 -0700 Subject: [PATCH 1538/2760] vfio/pci: pass vector to virq functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass the vector number to vfio_connect_kvm_msi_virq and vfio_remove_kvm_msi_virq, so it can be passed to their subroutines in a subsequent patch. No functional change. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-15-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index dc0d9a7fbb..1d22c41efb 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -478,7 +478,7 @@ static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, vector_n, &vdev->pdev); } -static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector) +static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector, int nr) { const char *name = "kvm_interrupt"; @@ -504,7 +504,8 @@ static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector) vector->virq = -1; } -static void vfio_remove_kvm_msi_virq(VFIOMSIVector *vector) +static void vfio_remove_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, + int nr) { kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &vector->kvm_interrupt, vector->virq); @@ -581,7 +582,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, */ if (vector->virq >= 0) { if (!msg) { - vfio_remove_kvm_msi_virq(vector); + vfio_remove_kvm_msi_virq(vdev, vector, nr); } else { vfio_update_kvm_msi_virq(vector, *msg, pdev); } @@ -593,7 +594,7 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, vfio_route_change = kvm_irqchip_begin_route_changes(kvm_state); vfio_add_kvm_msi_virq(vdev, vector, nr, true); kvm_irqchip_commit_route_changes(&vfio_route_change); - vfio_connect_kvm_msi_virq(vector); + vfio_connect_kvm_msi_virq(vector, nr); } } } @@ -687,7 +688,7 @@ static void vfio_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev) kvm_irqchip_commit_route_changes(&vfio_route_change); for (i = 0; i < vdev->nr_vectors; i++) { - vfio_connect_kvm_msi_virq(&vdev->msi_vectors[i]); + vfio_connect_kvm_msi_virq(&vdev->msi_vectors[i], i); } } @@ -827,7 +828,7 @@ static void vfio_msi_disable_common(VFIOPCIDevice *vdev) VFIOMSIVector *vector = &vdev->msi_vectors[i]; if (vdev->msi_vectors[i].use) { if (vector->virq >= 0) { - vfio_remove_kvm_msi_virq(vector); + vfio_remove_kvm_msi_virq(vdev, vector, i); } qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), NULL, NULL, NULL); From c2559182c88ab997727e0f4ef2c0f7faeb24d5a9 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:28 -0700 Subject: [PATCH 1539/2760] vfio/pci: vfio_notifier_init cpr parameters MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pass vdev and nr to vfio_notifier_init, for use by CPR in a subsequent patch. No functional change. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-16-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 1d22c41efb..9ee36da626 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -57,7 +57,8 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev); static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled); static void vfio_msi_disable_common(VFIOPCIDevice *vdev); -static bool vfio_notifier_init(EventNotifier *e, const char *name, Error **errp) +static bool vfio_notifier_init(VFIOPCIDevice *vdev, EventNotifier *e, + const char *name, int nr, Error **errp) { int ret = event_notifier_init(e, 0); @@ -147,7 +148,7 @@ static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) pci_irq_deassert(&vdev->pdev); /* Get an eventfd for resample/unmask */ - if (!vfio_notifier_init(&vdev->intx.unmask, "intx-unmask", errp)) { + if (!vfio_notifier_init(vdev, &vdev->intx.unmask, "intx-unmask", 0, errp)) { goto fail; } @@ -300,7 +301,8 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) } #endif - if (!vfio_notifier_init(&vdev->intx.interrupt, "intx-interrupt", errp)) { + if (!vfio_notifier_init(vdev, &vdev->intx.interrupt, "intx-interrupt", 0, + errp)) { return false; } fd = event_notifier_get_fd(&vdev->intx.interrupt); @@ -486,7 +488,8 @@ static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector, int nr) return; } - if (!vfio_notifier_init(&vector->kvm_interrupt, name, NULL)) { + if (!vfio_notifier_init(vector->vdev, &vector->kvm_interrupt, name, nr, + NULL)) { goto fail_notifier; } @@ -544,12 +547,13 @@ static void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr) { VFIOMSIVector *vector = &vdev->msi_vectors[nr]; PCIDevice *pdev = &vdev->pdev; - Error *err = NULL; + Error *local_err = NULL; vector->vdev = vdev; vector->virq = -1; - if (!vfio_notifier_init(&vector->interrupt, "interrupt", &err)) { - error_report_err(err); + if (!vfio_notifier_init(vdev, &vector->interrupt, "interrupt", nr, + &local_err)) { + error_report_err(local_err); } vector->use = true; if (vdev->interrupt == VFIO_INT_MSIX) { @@ -765,14 +769,15 @@ static void vfio_msi_enable(VFIOPCIDevice *vdev) for (i = 0; i < vdev->nr_vectors; i++) { VFIOMSIVector *vector = &vdev->msi_vectors[i]; - Error *err = NULL; + Error *local_err = NULL; vector->vdev = vdev; vector->virq = -1; vector->use = true; - if (!vfio_notifier_init(&vector->interrupt, "interrupt", &err)) { - error_report_err(err); + if (!vfio_notifier_init(vdev, &vector->interrupt, "interrupt", i, + &local_err)) { + error_report_err(local_err); } qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), @@ -2939,7 +2944,8 @@ void vfio_pci_register_err_notifier(VFIOPCIDevice *vdev) return; } - if (!vfio_notifier_init(&vdev->err_notifier, "err_notifier", &err)) { + if (!vfio_notifier_init(vdev, &vdev->err_notifier, "err_notifier", 0, + &err)) { error_report_err(err); vdev->pci_aer = false; return; @@ -3006,7 +3012,8 @@ void vfio_pci_register_req_notifier(VFIOPCIDevice *vdev) return; } - if (!vfio_notifier_init(&vdev->req_notifier, "req_notifier", &err)) { + if (!vfio_notifier_init(vdev, &vdev->req_notifier, "req_notifier", 0, + &err)) { error_report_err(err); return; } From 6d7696f329c77332e4db1d18fc5ab5df23363e01 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:29 -0700 Subject: [PATCH 1540/2760] vfio/pci: vfio_notifier_cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Move event_notifier_cleanup calls to a helper vfio_notifier_cleanup. This version is trivial, and does not yet use the vdev and nr parameters. No functional change. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-17-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 9ee36da626..06a7a63cf5 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -68,6 +68,12 @@ static bool vfio_notifier_init(VFIOPCIDevice *vdev, EventNotifier *e, return !ret; } +static void vfio_notifier_cleanup(VFIOPCIDevice *vdev, EventNotifier *e, + const char *name, int nr) +{ + event_notifier_cleanup(e); +} + /* * Disabling BAR mmaping can be slow, but toggling it around INTx can * also be a huge overhead. We try to get the best of both worlds by @@ -180,7 +186,7 @@ static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) kvm_irqchip_remove_irqfd_notifier_gsi(kvm_state, &vdev->intx.interrupt, vdev->intx.route.irq); fail_irqfd: - event_notifier_cleanup(&vdev->intx.unmask); + vfio_notifier_cleanup(vdev, &vdev->intx.unmask, "intx-unmask", 0); fail: qemu_set_fd_handler(irq_fd, vfio_intx_interrupt, NULL, vdev); vfio_device_irq_unmask(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX); @@ -212,7 +218,7 @@ static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) } /* We only need to close the eventfd for VFIO to cleanup the kernel side */ - event_notifier_cleanup(&vdev->intx.unmask); + vfio_notifier_cleanup(vdev, &vdev->intx.unmask, "intx-unmask", 0); /* QEMU starts listening for interrupt events. */ qemu_set_fd_handler(event_notifier_get_fd(&vdev->intx.interrupt), @@ -311,7 +317,7 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { qemu_set_fd_handler(fd, NULL, NULL, vdev); - event_notifier_cleanup(&vdev->intx.interrupt); + vfio_notifier_cleanup(vdev, &vdev->intx.interrupt, "intx-interrupt", 0); return false; } @@ -338,7 +344,7 @@ static void vfio_intx_disable(VFIOPCIDevice *vdev) fd = event_notifier_get_fd(&vdev->intx.interrupt); qemu_set_fd_handler(fd, NULL, NULL, vdev); - event_notifier_cleanup(&vdev->intx.interrupt); + vfio_notifier_cleanup(vdev, &vdev->intx.interrupt, "intx-interrupt", 0); vdev->interrupt = VFIO_INT_NONE; @@ -501,7 +507,7 @@ static void vfio_connect_kvm_msi_virq(VFIOMSIVector *vector, int nr) return; fail_kvm: - event_notifier_cleanup(&vector->kvm_interrupt); + vfio_notifier_cleanup(vector->vdev, &vector->kvm_interrupt, name, nr); fail_notifier: kvm_irqchip_release_virq(kvm_state, vector->virq); vector->virq = -1; @@ -514,7 +520,7 @@ static void vfio_remove_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, vector->virq); kvm_irqchip_release_virq(kvm_state, vector->virq); vector->virq = -1; - event_notifier_cleanup(&vector->kvm_interrupt); + vfio_notifier_cleanup(vdev, &vector->kvm_interrupt, "kvm_interrupt", nr); } static void vfio_update_kvm_msi_virq(VFIOMSIVector *vector, MSIMessage msg, @@ -837,7 +843,7 @@ static void vfio_msi_disable_common(VFIOPCIDevice *vdev) } qemu_set_fd_handler(event_notifier_get_fd(&vector->interrupt), NULL, NULL, NULL); - event_notifier_cleanup(&vector->interrupt); + vfio_notifier_cleanup(vdev, &vector->interrupt, "interrupt", i); } } @@ -2958,7 +2964,7 @@ void vfio_pci_register_err_notifier(VFIOPCIDevice *vdev) VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); qemu_set_fd_handler(fd, NULL, NULL, vdev); - event_notifier_cleanup(&vdev->err_notifier); + vfio_notifier_cleanup(vdev, &vdev->err_notifier, "err_notifier", 0); vdev->pci_aer = false; } } @@ -2977,7 +2983,7 @@ static void vfio_unregister_err_notifier(VFIOPCIDevice *vdev) } qemu_set_fd_handler(event_notifier_get_fd(&vdev->err_notifier), NULL, NULL, vdev); - event_notifier_cleanup(&vdev->err_notifier); + vfio_notifier_cleanup(vdev, &vdev->err_notifier, "err_notifier", 0); } static void vfio_req_notifier_handler(void *opaque) @@ -3025,7 +3031,7 @@ void vfio_pci_register_req_notifier(VFIOPCIDevice *vdev) VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); qemu_set_fd_handler(fd, NULL, NULL, vdev); - event_notifier_cleanup(&vdev->req_notifier); + vfio_notifier_cleanup(vdev, &vdev->req_notifier, "req_notifier", 0); } else { vdev->req_enabled = true; } @@ -3045,7 +3051,7 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) } qemu_set_fd_handler(event_notifier_get_fd(&vdev->req_notifier), NULL, NULL, vdev); - event_notifier_cleanup(&vdev->req_notifier); + vfio_notifier_cleanup(vdev, &vdev->req_notifier, "req_notifier", 0); vdev->req_enabled = false; } From 6f06e3729a6a8b6fc3cae6e91321014ea4c7f85f Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Tue, 10 Jun 2025 08:39:30 -0700 Subject: [PATCH 1541/2760] vfio/pci: export MSI functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Export various MSI functions, renamed with a vfio_pci prefix, for use by CPR in subsequent patches. No functional change. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1749569991-25171-18-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 29 +++++++++++++++++------------ hw/vfio/pci.h | 8 ++++++++ 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 06a7a63cf5..fa25bded25 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -351,6 +351,11 @@ static void vfio_intx_disable(VFIOPCIDevice *vdev) trace_vfio_intx_disable(vdev->vbasedev.name); } +bool vfio_pci_intx_enable(VFIOPCIDevice *vdev, Error **errp) +{ + return vfio_intx_enable(vdev, errp); +} + /* * MSI/X */ @@ -475,8 +480,8 @@ static int vfio_enable_vectors(VFIOPCIDevice *vdev, bool msix) return ret; } -static void vfio_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, - int vector_n, bool msix) +void vfio_pci_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, + int vector_n, bool msix) { if ((msix && vdev->no_kvm_msix) || (!msix && vdev->no_kvm_msi)) { return; @@ -549,7 +554,7 @@ static void set_irq_signalling(VFIODevice *vbasedev, VFIOMSIVector *vector, } } -static void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr) +void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr) { VFIOMSIVector *vector = &vdev->msi_vectors[nr]; PCIDevice *pdev = &vdev->pdev; @@ -599,10 +604,10 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, } else { if (msg) { if (vdev->defer_kvm_irq_routing) { - vfio_add_kvm_msi_virq(vdev, vector, nr, true); + vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true); } else { vfio_route_change = kvm_irqchip_begin_route_changes(kvm_state); - vfio_add_kvm_msi_virq(vdev, vector, nr, true); + vfio_pci_add_kvm_msi_virq(vdev, vector, nr, true); kvm_irqchip_commit_route_changes(&vfio_route_change); vfio_connect_kvm_msi_virq(vector, nr); } @@ -681,14 +686,14 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) } } -static void vfio_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev) +void vfio_pci_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev) { assert(!vdev->defer_kvm_irq_routing); vdev->defer_kvm_irq_routing = true; vfio_route_change = kvm_irqchip_begin_route_changes(kvm_state); } -static void vfio_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev) +void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev) { int i; @@ -718,14 +723,14 @@ static void vfio_msix_enable(VFIOPCIDevice *vdev) * routes once rather than per vector provides a substantial * performance improvement. */ - vfio_prepare_kvm_msi_virq_batch(vdev); + vfio_pci_prepare_kvm_msi_virq_batch(vdev); if (msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use, vfio_msix_vector_release, NULL)) { error_report("vfio: msix_set_vector_notifiers failed"); } - vfio_commit_kvm_msi_virq_batch(vdev); + vfio_pci_commit_kvm_msi_virq_batch(vdev); if (vdev->nr_vectors) { ret = vfio_enable_vectors(vdev, true); @@ -769,7 +774,7 @@ static void vfio_msi_enable(VFIOPCIDevice *vdev) * Deferring to commit the KVM routes once rather than per vector * provides a substantial performance improvement. */ - vfio_prepare_kvm_msi_virq_batch(vdev); + vfio_pci_prepare_kvm_msi_virq_batch(vdev); vdev->msi_vectors = g_new0(VFIOMSIVector, vdev->nr_vectors); @@ -793,10 +798,10 @@ static void vfio_msi_enable(VFIOPCIDevice *vdev) * Attempt to enable route through KVM irqchip, * default to userspace handling if unavailable. */ - vfio_add_kvm_msi_virq(vdev, vector, i, false); + vfio_pci_add_kvm_msi_virq(vdev, vector, i, false); } - vfio_commit_kvm_msi_virq_batch(vdev); + vfio_pci_commit_kvm_msi_virq_batch(vdev); /* Set interrupt type prior to possible interrupts */ vdev->interrupt = VFIO_INT_MSI; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index d4c6b2e7b7..d3dc2274a9 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -210,6 +210,14 @@ static inline bool vfio_is_vga(VFIOPCIDevice *vdev) return class == PCI_CLASS_DISPLAY_VGA; } +/* MSI/MSI-X/INTx */ +void vfio_pci_vector_init(VFIOPCIDevice *vdev, int nr); +void vfio_pci_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, + int vector_n, bool msix); +void vfio_pci_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev); +void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev); +bool vfio_pci_intx_enable(VFIOPCIDevice *vdev, Error **errp); + uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); void vfio_pci_write_config(PCIDevice *pdev, uint32_t addr, uint32_t val, int len); From 079e7216debd767e78a77aefc88e2e7335f49b26 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 11 Jun 2025 03:47:53 -0700 Subject: [PATCH 1542/2760] vfio: improve VFIODeviceIOOps docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Explicitly describe every parameter rather than summarizing. Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250611104753.1199796-1-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- include/hw/vfio/vfio-device.h | 52 +++++++++++++++++++++++++++++------ 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index f39259406b..d45e5a68a2 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -168,14 +168,25 @@ struct VFIODeviceIOOps { * @device_feature * * Fill in feature info for the given device. + * + * @vdev: #VFIODevice to use + * @feat: feature information to fill in + * + * Returns 0 on success or -errno. */ - int (*device_feature)(VFIODevice *vdev, struct vfio_device_feature *); + int (*device_feature)(VFIODevice *vdev, struct vfio_device_feature *feat); /** * @get_region_info * - * Fill in @info (and optionally @fd) with information on the region given - * by @info->index. + * Get the information for a given region on the device. + * + * @vdev: #VFIODevice to use + * @info: set @info->index to the region index to look up; the rest of the + * struct will be filled in on success + * @fd: pointer to the fd for the region; will be -1 if not found + * + * Returns 0 on success or -errno. */ int (*get_region_info)(VFIODevice *vdev, struct vfio_region_info *info, int *fd); @@ -183,22 +194,38 @@ struct VFIODeviceIOOps { /** * @get_irq_info * - * Fill in @irq with information on the IRQ given by @info->index. + * @vdev: #VFIODevice to use + * @irq: set @irq->index to the IRQ index to look up; the rest of the struct + * will be filled in on success + * + * Returns 0 on success or -errno. */ int (*get_irq_info)(VFIODevice *vdev, struct vfio_irq_info *irq); /** * @set_irqs * - * Configure IRQs as defined by @irqs. + * Configure IRQs. + * + * @vdev: #VFIODevice to use + * @irqs: IRQ configuration as defined by VFIO docs. + * + * Returns 0 on success or -errno. */ int (*set_irqs)(VFIODevice *vdev, struct vfio_irq_set *irqs); /** * @region_read * - * Read @size bytes from the region @nr at offset @off into the buffer - * @data. + * Read part of a region. + * + * @vdev: #VFIODevice to use + * @nr: region index + * @off: offset within the region + * @size: size in bytes to read + * @data: buffer to read into + * + * Returns number of bytes read on success or -errno. */ int (*region_read)(VFIODevice *vdev, uint8_t nr, off_t off, uint32_t size, void *data); @@ -206,8 +233,15 @@ struct VFIODeviceIOOps { /** * @region_write * - * Write @size bytes to the region @nr at offset @off from the buffer - * @data; if @post, the write is posted. + * Write part of a region. + * + * @vdev: #VFIODevice to use + * @nr: region index + * @off: offset within the region + * @size: size in bytes to write + * @data: buffer to write from + * + * Returns number of bytes write on success or -errno. */ int (*region_write)(VFIODevice *vdev, uint8_t nr, off_t off, uint32_t size, void *data, bool post); From 6f0273d6ae21bb2e4222789af0e6d480aa9430b1 Mon Sep 17 00:00:00 2001 From: Konstantin Kostiuk Date: Mon, 24 Mar 2025 15:17:28 +0200 Subject: [PATCH 1543/2760] qga-win: implement a 'guest-get-load' command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Windows has no native equivalent API, but it would be possible to simulate it as illustrated here (BSD-3-Clause): https://github.com/giampaolo/psutil/pull/1485 Reviewed-by: Daniel P. Berrangé Tested-by: Dehan Meng Reviewed-by: Yan Vugenfirer Signed-off-by: Konstantin Kostiuk --- qga/commands-win32.c | 148 +++++++++++++++++++++++++++++++++++++++++ qga/guest-agent-core.h | 10 +++ qga/main.c | 39 +++++++++++ qga/meson.build | 2 +- qga/qapi-schema.json | 9 ++- 5 files changed, 205 insertions(+), 3 deletions(-) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index d4482538ec..8227480810 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "guest-agent-core.h" #include "vss-win32.h" @@ -119,6 +120,28 @@ static OpenFlags guest_file_open_modes[] = { {"a+b", FILE_GENERIC_APPEND | GENERIC_READ, OPEN_ALWAYS } }; +/* + * We use an exponentially weighted moving average, just like Unix systems do + * https://en.wikipedia.org/wiki/Load_(computing)#Unix-style_load_calculation + * + * These constants serve as the damping factor and are calculated with + * 1 / exp(sampling interval in seconds / window size in seconds) + * + * This formula comes from linux's include/linux/sched/loadavg.h + * https://github.com/torvalds/linux/blob/345671ea0f9258f410eb057b9ced9cefbbe5dc78/include/linux/sched/loadavg.h#L20-L23 + */ +#define LOADAVG_FACTOR_1F 0.9200444146293232478931553241 +#define LOADAVG_FACTOR_5F 0.9834714538216174894737477501 +#define LOADAVG_FACTOR_15F 0.9944598480048967508795473394 +/* + * The time interval in seconds between taking load counts, same as Linux + */ +#define LOADAVG_SAMPLING_INTERVAL 5 + +double load_avg_1m; +double load_avg_5m; +double load_avg_15m; + #define debug_error(msg) do { \ char *suffix = g_win32_error_message(GetLastError()); \ g_debug("%s: %s", (msg), suffix); \ @@ -2444,3 +2467,128 @@ char *qga_get_host_name(Error **errp) return g_utf16_to_utf8(tmp, size, NULL, NULL, NULL); } + + +static VOID CALLBACK load_avg_callback(PVOID hCounter, BOOLEAN timedOut) +{ + PDH_FMT_COUNTERVALUE displayValue; + double currentLoad; + PDH_STATUS err; + + err = PdhGetFormattedCounterValue( + (PDH_HCOUNTER)hCounter, PDH_FMT_DOUBLE, 0, &displayValue); + /* Skip updating the load if we can't get the value successfully */ + if (err != ERROR_SUCCESS) { + slog("PdhGetFormattedCounterValue failed to get load value with 0x%lx", + err); + return; + } + currentLoad = displayValue.doubleValue; + + load_avg_1m = load_avg_1m * LOADAVG_FACTOR_1F + currentLoad * \ + (1.0 - LOADAVG_FACTOR_1F); + load_avg_5m = load_avg_5m * LOADAVG_FACTOR_5F + currentLoad * \ + (1.0 - LOADAVG_FACTOR_5F); + load_avg_15m = load_avg_15m * LOADAVG_FACTOR_15F + currentLoad * \ + (1.0 - LOADAVG_FACTOR_15F); +} + +static BOOL init_load_avg_counter(Error **errp) +{ + CONST WCHAR *szCounterPath = L"\\System\\Processor Queue Length"; + PDH_STATUS status; + BOOL ret; + HQUERY hQuery; + HCOUNTER hCounter; + HANDLE event; + HANDLE waitHandle; + + status = PdhOpenQueryW(NULL, 0, &hQuery); + if (status != ERROR_SUCCESS) { + /* + * If the function fails, the return value is a system error code or + * a PDH error code. error_setg_win32 cant translate PDH error code + * properly, so just report it as is. + */ + error_setg_win32(errp, (DWORD)status, + "PdhOpenQueryW failed with 0x%lx", status); + return FALSE; + } + + status = PdhAddEnglishCounterW(hQuery, szCounterPath, 0, &hCounter); + if (status != ERROR_SUCCESS) { + error_setg_win32(errp, (DWORD)status, + "PdhAddEnglishCounterW failed with 0x%lx. Performance counters may be disabled.", + status); + PdhCloseQuery(hQuery); + return FALSE; + } + + event = CreateEventW(NULL, FALSE, FALSE, L"LoadUpdateEvent"); + if (event == NULL) { + error_setg_win32(errp, GetLastError(), "Create LoadUpdateEvent failed"); + PdhCloseQuery(hQuery); + return FALSE; + } + + status = PdhCollectQueryDataEx(hQuery, LOADAVG_SAMPLING_INTERVAL, event); + if (status != ERROR_SUCCESS) { + error_setg_win32(errp, (DWORD)status, + "PdhCollectQueryDataEx failed with 0x%lx", status); + CloseHandle(event); + PdhCloseQuery(hQuery); + return FALSE; + } + + ret = RegisterWaitForSingleObject( + &waitHandle, + event, + (WAITORTIMERCALLBACK)load_avg_callback, + (PVOID)hCounter, + INFINITE, + WT_EXECUTEDEFAULT); + + if (ret == 0) { + error_setg_win32(errp, GetLastError(), + "RegisterWaitForSingleObject failed"); + CloseHandle(event); + PdhCloseQuery(hQuery); + return FALSE; + } + + ga_set_load_avg_wait_handle(ga_state, waitHandle); + ga_set_load_avg_event(ga_state, event); + ga_set_load_avg_pdh_query(ga_state, hQuery); + + return TRUE; +} + +GuestLoadAverage *qmp_guest_get_load(Error **errp) +{ + /* + * The load average logic calls PerformaceCounterAPI, which can result + * in a performance penalty. This avoids running the load average logic + * until a management application actually requests it. The load average + * will not initially be very accurate, but assuming that any interested + * management application will request it repeatedly throughout the lifetime + * of the VM, this seems like a good mitigation. + */ + if (ga_get_load_avg_pdh_query(ga_state) == NULL) { + /* set initial values */ + load_avg_1m = 0; + load_avg_5m = 0; + load_avg_15m = 0; + + if (init_load_avg_counter(errp) == false) { + return NULL; + } + } + + GuestLoadAverage *ret = NULL; + + ret = g_new0(GuestLoadAverage, 1); + ret->load1m = load_avg_1m; + ret->load5m = load_avg_5m; + ret->load15m = load_avg_15m; + return ret; +} diff --git a/qga/guest-agent-core.h b/qga/guest-agent-core.h index a536d07d0d..d9f3922adf 100644 --- a/qga/guest-agent-core.h +++ b/qga/guest-agent-core.h @@ -13,6 +13,10 @@ #ifndef GUEST_AGENT_CORE_H #define GUEST_AGENT_CORE_H +#ifdef _WIN32 +#include +#endif + #include "qapi/qmp-registry.h" #include "qga-qapi-types.h" @@ -41,6 +45,12 @@ void ga_set_response_delimited(GAState *s); bool ga_is_frozen(GAState *s); void ga_set_frozen(GAState *s); void ga_unset_frozen(GAState *s); +#ifdef _WIN32 +void ga_set_load_avg_event(GAState *s, HANDLE event); +void ga_set_load_avg_wait_handle(GAState *s, HANDLE wait_handle); +void ga_set_load_avg_pdh_query(GAState *s, HQUERY query); +HQUERY ga_get_load_avg_pdh_query(GAState *s); +#endif const char *ga_fsfreeze_hook(GAState *s); int64_t ga_get_fd_handle(GAState *s, Error **errp); int ga_parse_whence(GuestFileWhence *whence, Error **errp); diff --git a/qga/main.c b/qga/main.c index 72c39b042f..6c02f3ec38 100644 --- a/qga/main.c +++ b/qga/main.c @@ -33,6 +33,7 @@ #include "qemu-version.h" #ifdef _WIN32 #include +#include #include "qga/service-win32.h" #include "qga/vss-win32.h" #endif @@ -105,6 +106,9 @@ struct GAState { GAService service; HANDLE wakeup_event; HANDLE event_log; + HANDLE load_avg_wait_handle; + HANDLE load_avg_event; + HQUERY load_avg_pdh_query; #endif bool delimit_response; bool frozen; @@ -582,6 +586,25 @@ const char *ga_fsfreeze_hook(GAState *s) } #endif +#ifdef _WIN32 +void ga_set_load_avg_wait_handle(GAState *s, HANDLE wait_handle) +{ + s->load_avg_wait_handle = wait_handle; +} +void ga_set_load_avg_event(GAState *s, HANDLE event) +{ + s->load_avg_event = event; +} +void ga_set_load_avg_pdh_query(GAState *s, HQUERY query) +{ + s->load_avg_pdh_query = query; +} +HQUERY ga_get_load_avg_pdh_query(GAState *s) +{ + return s->load_avg_pdh_query; +} +#endif + static void become_daemon(const char *pidfile) { #ifndef _WIN32 @@ -1402,6 +1425,10 @@ static GAState *initialize_agent(GAConfig *config, int socket_activation) g_debug("Guest agent version %s started", QEMU_FULL_VERSION); #ifdef _WIN32 + s->load_avg_wait_handle = INVALID_HANDLE_VALUE; + s->load_avg_event = INVALID_HANDLE_VALUE; + s->load_avg_pdh_query = NULL; + s->event_log = RegisterEventSource(NULL, "qemu-ga"); if (!s->event_log) { g_autofree gchar *errmsg = g_win32_error_message(GetLastError()); @@ -1506,6 +1533,18 @@ static void cleanup_agent(GAState *s) #ifdef _WIN32 CloseHandle(s->wakeup_event); CloseHandle(s->event_log); + + if (s->load_avg_wait_handle != INVALID_HANDLE_VALUE) { + UnregisterWait(s->load_avg_wait_handle); + } + + if (s->load_avg_event != INVALID_HANDLE_VALUE) { + CloseHandle(s->load_avg_event); + } + + if (s->load_avg_pdh_query) { + PdhCloseQuery(s->load_avg_pdh_query); + } #endif if (s->command_state) { ga_command_state_cleanup_all(s->command_state); diff --git a/qga/meson.build b/qga/meson.build index 587ec4e5e8..89a4a8f713 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -95,7 +95,7 @@ gen_tlb = [] qga_libs = [] if host_os == 'windows' qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', '-lwininet', '-liphlpapi', '-lnetapi32', - '-lsetupapi', '-lcfgmgr32', '-luserenv'] + '-lsetupapi', '-lcfgmgr32', '-luserenv', '-lpdh' ] if have_qga_vss qga_libs += ['-lole32', '-loleaut32', '-lshlwapi', '-lstdc++', '-Wl,--enable-stdcall-fixup'] subdir('vss-win32') diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 5316bfacbf..6d770f7b8e 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1880,7 +1880,7 @@ 'load5m': 'number', 'load15m': 'number' }, - 'if': 'CONFIG_GETLOADAVG' + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_GETLOADAVG'] } } ## @@ -1888,13 +1888,18 @@ # # Retrieve CPU process load information # +# .. note:: Windows does not have load average API, so QGA emulates it by +# calculating the average CPU usage in the last 1, 5, 15 minutes +# similar as Linux does this. +# Calculation starts from the first time this command is called. +# # Returns: load information # # Since: 10.0 ## { 'command': 'guest-get-load', 'returns': 'GuestLoadAverage', - 'if': 'CONFIG_GETLOADAVG' + 'if': { 'any': ['CONFIG_WIN32', 'CONFIG_GETLOADAVG'] } } ## From 1ebbc8b774d8b44697740b108ad060612828b58f Mon Sep 17 00:00:00 2001 From: Konstantin Kostiuk Date: Mon, 24 Mar 2025 15:17:29 +0200 Subject: [PATCH 1544/2760] qga: Add tests for guest-get-load command Tested-by: Dehan Meng Reviewed-by: Yan Vugenfirer Signed-off-by: Konstantin Kostiuk --- tests/unit/test-qga.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/unit/test-qga.c b/tests/unit/test-qga.c index 541b08a5e7..587e30c7e4 100644 --- a/tests/unit/test-qga.c +++ b/tests/unit/test-qga.c @@ -332,6 +332,22 @@ static void test_qga_get_fsinfo(gconstpointer fix) } } +static void test_qga_get_load(gconstpointer fix) +{ + const TestFixture *fixture = fix; + g_autoptr(QDict) ret = NULL; + QDict *load; + + ret = qmp_fd(fixture->fd, "{'execute': 'guest-get-load'}"); + g_assert_nonnull(ret); + qmp_assert_no_error(ret); + + load = qdict_get_qdict(ret, "return"); + g_assert(qdict_haskey(load, "load1m")); + g_assert(qdict_haskey(load, "load5m")); + g_assert(qdict_haskey(load, "load15m")); +} + static void test_qga_get_memory_block_info(gconstpointer fix) { const TestFixture *fixture = fix; @@ -1105,6 +1121,7 @@ int main(int argc, char **argv) g_test_add_data_func("/qga/get-vcpus", &fix, test_qga_get_vcpus); } g_test_add_data_func("/qga/get-fsinfo", &fix, test_qga_get_fsinfo); + g_test_add_data_func("/qga/get-load", &fix, test_qga_get_load); g_test_add_data_func("/qga/get-memory-block-info", &fix, test_qga_get_memory_block_info); g_test_add_data_func("/qga/get-memory-blocks", &fix, From f5ec751ee70d7960a97c6c675f69e924d82dc60d Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Mon, 2 Jun 2025 12:46:55 +0100 Subject: [PATCH 1545/2760] hw/arm/virt: Check bypass iommu is not set for iommu-map DT property default_bus_bypass_iommu tells us whether the bypass_iommu is set for the default PCIe root bus. Make sure we check that before adding the "iommu-map" DT property. Cc: qemu-stable@nongnu.org Fixes: 6d7a85483a06 ("hw/arm/virt: Add default_bus_bypass_iommu machine option") Suggested-by: Eric Auger Signed-off-by: Shameer Kolothum Reviewed-by: Donald Dutile Reviewed-by: Eric Auger Message-id: 20250602114655.42920-1-shameerali.kolothum.thodi@huawei.com Signed-off-by: Peter Maydell --- hw/arm/virt.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 9a6cd085a3..99fde5836c 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -1487,9 +1487,12 @@ static void create_virtio_iommu_dt_bindings(VirtMachineState *vms) qemu_fdt_setprop_cell(ms->fdt, node, "phandle", vms->iommu_phandle); g_free(node); - qemu_fdt_setprop_cells(ms->fdt, vms->pciehb_nodename, "iommu-map", - 0x0, vms->iommu_phandle, 0x0, bdf, - bdf + 1, vms->iommu_phandle, bdf + 1, 0xffff - bdf); + if (!vms->default_bus_bypass_iommu) { + qemu_fdt_setprop_cells(ms->fdt, vms->pciehb_nodename, "iommu-map", + 0x0, vms->iommu_phandle, 0x0, bdf, + bdf + 1, vms->iommu_phandle, bdf + 1, + 0xffff - bdf); + } } static void create_pcie(VirtMachineState *vms) @@ -1612,8 +1615,10 @@ static void create_pcie(VirtMachineState *vms) switch (vms->iommu) { case VIRT_IOMMU_SMMUV3: create_smmu(vms, vms->bus); - qemu_fdt_setprop_cells(ms->fdt, nodename, "iommu-map", - 0x0, vms->iommu_phandle, 0x0, 0x10000); + if (!vms->default_bus_bypass_iommu) { + qemu_fdt_setprop_cells(ms->fdt, nodename, "iommu-map", + 0x0, vms->iommu_phandle, 0x0, 0x10000); + } break; default: g_assert_not_reached(); From 16a9b55021519fb1d4a4585952e03eafd4ed1947 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 3 Jun 2025 12:15:26 +0200 Subject: [PATCH 1546/2760] tests/functional: Add a test for the realview-eb-mpcore machine Check that we can boot a Linux kernel here and that we can at least send one ping network packet. Signed-off-by: Thomas Huth Message-id: 20250603101526.21217-1-thuth@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- MAINTAINERS | 1 + tests/functional/meson.build | 1 + tests/functional/test_arm_realview.py | 47 +++++++++++++++++++++++++++ 3 files changed, 49 insertions(+) create mode 100755 tests/functional/test_arm_realview.py diff --git a/MAINTAINERS b/MAINTAINERS index 28b3dd2684..84cfef835e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -955,6 +955,7 @@ F: hw/cpu/realview_mpcore.c F: hw/intc/realview_gic.c F: include/hw/intc/realview_gic.h F: docs/system/arm/realview.rst +F: tests/functional/test_arm_realview.py SABRELITE / i.MX6 M: Peter Maydell diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 7faa2b6e3c..e7e051ef3c 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -135,6 +135,7 @@ tests_arm_system_thorough = [ 'arm_orangepi', 'arm_quanta_gsj', 'arm_raspi2', + 'arm_realview', 'arm_replay', 'arm_smdkc210', 'arm_stellaris', diff --git a/tests/functional/test_arm_realview.py b/tests/functional/test_arm_realview.py new file mode 100755 index 0000000000..82cc964333 --- /dev/null +++ b/tests/functional/test_arm_realview.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a realview arm machine +# and checks the console +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, exec_command_and_wait_for_pattern +from qemu_test import Asset + + +class RealviewMachine(LinuxKernelTest): + + ASSET_REALVIEW_MPCORE = Asset( + ('https://archive.openwrt.org/chaos_calmer/15.05.1/realview/generic/' + 'openwrt-15.05.1-realview-vmlinux-initramfs.elf'), + 'd3a01037f33e7512d46d50975588d5c3a0e0cbf25f37afab44775c2a2be523e6') + + def test_realview_ep_mpcore(self): + self.require_netdev('user') + self.set_machine('realview-eb-mpcore') + kernel_path = self.ASSET_REALVIEW_MPCORE.fetch() + self.vm.set_console() + kernel_param = 'console=ttyAMA0 mem=128M quiet' + self.vm.add_args('-kernel', kernel_path, + '-append', kernel_param) + self.vm.launch() + self.wait_for_console_pattern('Please press Enter to activate') + prompt = ':/#' + exec_command_and_wait_for_pattern(self, '', prompt) + exec_command_and_wait_for_pattern(self, 'dmesg', kernel_param) + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, + ('while ! dmesg | grep "br-lan: port 1(eth0) entered" ;' + ' do sleep 1 ; done'), + 'entered forwarding state') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, + 'while ! ifconfig | grep "10.0.2.15" ; do sleep 1 ; done', + 'addr:10.0.2.15') + self.wait_for_console_pattern(prompt) + exec_command_and_wait_for_pattern(self, 'ping -c 1 10.0.2.2', + '1 packets received, 0% packet loss') + + +if __name__ == '__main__': + LinuxKernelTest.main() From c653b67d1863b7ebfa67f7c9f4aec209d7b5ced5 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 10 Jun 2025 13:36:39 +0100 Subject: [PATCH 1547/2760] include/qemu/compiler: add QEMU_UNINITIALIZED attribute macro MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QEMU_UNINITIALIZED macro is to be used to skip the default compiler variable initialization done by -ftrivial-auto-var-init=zero. Use this in cases where there a method in the device I/O path (or other important hot paths), that has large variables on the stack. A rule of thumb is that "large" means a method with 4kb data in the local stack frame. Any variables which are KB in size, should be annotated with this attribute, to pre-emptively eliminate any potential overhead from the compiler zero'ing memory. Given that this turns off a security hardening feature, when using this to flag variables, it is important that the code is double-checked to ensure there is no possible use of uninitialized data in the method. Signed-off-by: Stefan Hajnoczi Reviewed-by: Stefan Hajnoczi Signed-off-by: Daniel P. Berrangé Message-id: 20250610123709.835102-2-berrange@redhat.com [DB: split off patch & rewrite guidance on when to use the annotation] Signed-off-by: Daniel P. Berrangé Signed-off-by: Stefan Hajnoczi --- include/qemu/compiler.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 496dac5ac1..65b89958d3 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -207,6 +207,26 @@ # define QEMU_USED #endif +/* + * Disable -ftrivial-auto-var-init on a local variable. + * + * Use this in cases where there a method in the device I/O path (or other + * important hot paths), that has large variables on the stack. A rule of + * thumb is that "large" means a method with 4kb data in the local stack + * frame. Any variables which are KB in size, should be annotated with this + * attribute, to pre-emptively eliminate any potential overhead from the + * compiler's implicit zero'ing of memory. + * + * Given that this turns off a security hardening feature, when using this + * to flag variables, it is important that the code is double-checked to + * ensure there is no possible use of uninitialized data in the method. + */ +#if __has_attribute(uninitialized) +# define QEMU_UNINITIALIZED __attribute__((uninitialized)) +#else +# define QEMU_UNINITIALIZED +#endif + /* * http://clang.llvm.org/docs/ThreadSafetyAnalysis.html * From ba2868ce091cd4abe4be6de4b7e44b3be303b352 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 10 Jun 2025 13:36:40 +0100 Subject: [PATCH 1548/2760] hw/virtio/virtio: avoid cost of -ftrivial-auto-var-init in hot path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 7ff9ff039380 ("meson: mitigate against use of uninitialize stack for exploits") the -ftrivial-auto-var-init=zero compiler option is used to zero local variables. While this reduces security risks associated with uninitialized stack data, it introduced a measurable bottleneck in the virtqueue_split_pop() and virtqueue_packed_pop() functions. These virtqueue functions are in the hot path. They are called for each element (request) that is popped from a VIRTIO device's virtqueue. Using __attribute__((uninitialized)) on large stack variables in these functions improves fio randread bs=4k iodepth=64 performance from 304k to 332k IOPS (+9%). This issue was found using perf-top(1). virtqueue_split_pop() was one of the top CPU consumers and the "annotate" feature showed that the memory zeroing instructions at the beginning of the functions were hot. Fixes: 7ff9ff039380 ("meson: mitigate against use of uninitialize stack for exploits") Cc: Daniel P. Berrangé Signed-off-by: Stefan Hajnoczi Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-3-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/virtio/virtio.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 5534251e01..82a285a31d 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -1689,8 +1689,8 @@ static void *virtqueue_split_pop(VirtQueue *vq, size_t sz) VirtIODevice *vdev = vq->vdev; VirtQueueElement *elem = NULL; unsigned out_num, in_num, elem_entries; - hwaddr addr[VIRTQUEUE_MAX_SIZE]; - struct iovec iov[VIRTQUEUE_MAX_SIZE]; + hwaddr QEMU_UNINITIALIZED addr[VIRTQUEUE_MAX_SIZE]; + struct iovec QEMU_UNINITIALIZED iov[VIRTQUEUE_MAX_SIZE]; VRingDesc desc; int rc; @@ -1836,8 +1836,8 @@ static void *virtqueue_packed_pop(VirtQueue *vq, size_t sz) VirtIODevice *vdev = vq->vdev; VirtQueueElement *elem = NULL; unsigned out_num, in_num, elem_entries; - hwaddr addr[VIRTQUEUE_MAX_SIZE]; - struct iovec iov[VIRTQUEUE_MAX_SIZE]; + hwaddr QEMU_UNINITIALIZED addr[VIRTQUEUE_MAX_SIZE]; + struct iovec QEMU_UNINITIALIZED iov[VIRTQUEUE_MAX_SIZE]; VRingPackedDesc desc; uint16_t id; int rc; From 83750c1da807c973b0b11d977d61df7e41122d03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:41 +0100 Subject: [PATCH 1549/2760] block: skip automatic zero-init of large array in ioq_submit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'ioq_submit' method has a struct array that is 8k in size. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'iocbs' array will selectively initialized when processing the I/O data. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-4-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- block/linux-aio.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block/linux-aio.c b/block/linux-aio.c index 407369f5c9..c200e7ad20 100644 --- a/block/linux-aio.c +++ b/block/linux-aio.c @@ -291,7 +291,7 @@ static void ioq_submit(LinuxAioState *s) { int ret, len; struct qemu_laiocb *aiocb; - struct iocb *iocbs[MAX_EVENTS]; + QEMU_UNINITIALIZED struct iocb *iocbs[MAX_EVENTS]; QSIMPLEQ_HEAD(, qemu_laiocb) completed; do { From a503bdc22b91869e3bf45522e36b122889465306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:42 +0100 Subject: [PATCH 1550/2760] chardev/char-fd: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'fd_chr_read' method has a 4k byte array used for copying data between the socket and device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'buf' array will be fully initialized when reading data off the network socket. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-5-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- chardev/char-fd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chardev/char-fd.c b/chardev/char-fd.c index 23bfe3c0b1..6f03adf872 100644 --- a/chardev/char-fd.c +++ b/chardev/char-fd.c @@ -50,7 +50,7 @@ static gboolean fd_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) Chardev *chr = CHARDEV(opaque); FDChardev *s = FD_CHARDEV(opaque); int len; - uint8_t buf[CHR_READ_BUF_LEN]; + QEMU_UNINITIALIZED uint8_t buf[CHR_READ_BUF_LEN]; ssize_t ret; len = sizeof(buf); From 45bb7fb21c8d18294a9f92da99d01ab3c67c7df2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:43 +0100 Subject: [PATCH 1551/2760] chardev/char-pty: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'pty_chr_read' method has a 4k byte array used for copying data between the PTY and device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'buf' array will be fully initialized when reading data off the PTY. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-6-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- chardev/char-pty.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chardev/char-pty.c b/chardev/char-pty.c index c28554e6e0..674e9b3f14 100644 --- a/chardev/char-pty.c +++ b/chardev/char-pty.c @@ -154,7 +154,7 @@ static gboolean pty_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) Chardev *chr = CHARDEV(opaque); PtyChardev *s = PTY_CHARDEV(opaque); gsize len; - uint8_t buf[CHR_READ_BUF_LEN]; + QEMU_UNINITIALIZED uint8_t buf[CHR_READ_BUF_LEN]; ssize_t ret; len = sizeof(buf); From 9a23075cef1ac6e73a95a489ac72f41c573ceb9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:44 +0100 Subject: [PATCH 1552/2760] chardev/char-socket: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'tcp_chr_read' method has a 4k byte array used for copying data between the socket and device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'buf' array will be fully initialized when reading data off the network socket. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-7-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- chardev/char-socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/chardev/char-socket.c b/chardev/char-socket.c index e8dd2931dc..1e8313915b 100644 --- a/chardev/char-socket.c +++ b/chardev/char-socket.c @@ -497,7 +497,7 @@ static gboolean tcp_chr_read(QIOChannel *chan, GIOCondition cond, void *opaque) { Chardev *chr = CHARDEV(opaque); SocketChardev *s = SOCKET_CHARDEV(opaque); - uint8_t buf[CHR_READ_BUF_LEN]; + QEMU_UNINITIALIZED uint8_t buf[CHR_READ_BUF_LEN]; int len, size; if ((s->state != TCP_CHARDEV_STATE_CONNECTED) || From 2553d2d26a9d0f46386bf8c37d184567e5cede6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:45 +0100 Subject: [PATCH 1553/2760] hw/audio/ac97: skip automatic zero-init of large arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'read_audio' & 'write_audio' methods have a 4k byte array used for copying data between the audio backend and device. Skip the automatic zero-init of these arrays to eliminate the performance overhead in the I/O hot path. The 'tmpbuf' array will be fully initialized when reading data from the audio backend and/or device memory. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-8-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/audio/ac97.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/audio/ac97.c b/hw/audio/ac97.c index 669a0463cc..eb7a847080 100644 --- a/hw/audio/ac97.c +++ b/hw/audio/ac97.c @@ -886,7 +886,7 @@ static void nabm_writel(void *opaque, uint32_t addr, uint32_t val) static int write_audio(AC97LinkState *s, AC97BusMasterRegs *r, int max, int *stop) { - uint8_t tmpbuf[4096]; + QEMU_UNINITIALIZED uint8_t tmpbuf[4096]; uint32_t addr = r->bd.addr; uint32_t temp = r->picb << 1; uint32_t written = 0; @@ -959,7 +959,7 @@ static void write_bup(AC97LinkState *s, int elapsed) static int read_audio(AC97LinkState *s, AC97BusMasterRegs *r, int max, int *stop) { - uint8_t tmpbuf[4096]; + QEMU_UNINITIALIZED uint8_t tmpbuf[4096]; uint32_t addr = r->bd.addr; uint32_t temp = r->picb << 1; uint32_t nread = 0; From ca2cc0385d97cea66cd54ee42553f385c403d4a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:46 +0100 Subject: [PATCH 1554/2760] hw/audio/cs4231a: skip automatic zero-init of large arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'cs_write_audio' method has a pair of byte arrays, one 4k in size and one 8k, which are used in converting audio samples. Skip the automatic zero-init of these arrays to eliminate the performance overhead in the I/O hot path. The 'tmpbuf' array will be fully initialized when reading a block of data from the guest. The 'linbuf' array will be fully initialized when converting the audio samples. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-9-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/audio/cs4231a.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/audio/cs4231a.c b/hw/audio/cs4231a.c index eb9a45805b..6dfff202ff 100644 --- a/hw/audio/cs4231a.c +++ b/hw/audio/cs4231a.c @@ -528,7 +528,7 @@ static int cs_write_audio (CSState *s, int nchan, int dma_pos, int dma_len, int len) { int temp, net; - uint8_t tmpbuf[4096]; + QEMU_UNINITIALIZED uint8_t tmpbuf[4096]; IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); temp = len; @@ -547,7 +547,7 @@ static int cs_write_audio (CSState *s, int nchan, int dma_pos, copied = k->read_memory(s->isa_dma, nchan, tmpbuf, dma_pos, to_copy); if (s->tab) { int i; - int16_t linbuf[4096]; + QEMU_UNINITIALIZED int16_t linbuf[4096]; for (i = 0; i < copied; ++i) linbuf[i] = s->tab[tmpbuf[i]]; From 8236e206084b832d1d7ec947a4798b818f4cdf1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:47 +0100 Subject: [PATCH 1555/2760] hw/audio/es1370: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'es1370_transfer_audio' method has a 4k byte array used for copying data between the audio backend and device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'tmpbuf' array will be fully initialized when reading data from the audio backend and/or device memory. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-10-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/audio/es1370.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/es1370.c b/hw/audio/es1370.c index 8efb969212..a6a32a6348 100644 --- a/hw/audio/es1370.c +++ b/hw/audio/es1370.c @@ -604,7 +604,7 @@ static uint64_t es1370_read(void *opaque, hwaddr addr, unsigned size) static void es1370_transfer_audio (ES1370State *s, struct chan *d, int loop_sel, int max, bool *irq) { - uint8_t tmpbuf[4096]; + QEMU_UNINITIALIZED uint8_t tmpbuf[4096]; size_t to_transfer; uint32_t addr = d->frame_addr; int sc = d->scount & 0xffff; From 2e438da4929018c62609381e1156aac0b2fe3de3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:48 +0100 Subject: [PATCH 1556/2760] hw/audio/gus: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'GUS_read_DMA' method has a 4k byte array used for copying data between the audio backend and device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'tmpbuf' array will be fully initialized when reading data from device memory. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-11-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/audio/gus.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/gus.c b/hw/audio/gus.c index 87e8634893..c36df0240f 100644 --- a/hw/audio/gus.c +++ b/hw/audio/gus.c @@ -183,7 +183,7 @@ static int GUS_read_DMA (void *opaque, int nchan, int dma_pos, int dma_len) { GUSState *s = opaque; IsaDmaClass *k = ISADMA_GET_CLASS(s->isa_dma); - char tmpbuf[4096]; + QEMU_UNINITIALIZED char tmpbuf[4096]; int pos = dma_pos, mode, left = dma_len - dma_pos; ldebug ("read DMA %#x %d\n", dma_pos, dma_len); From 5b6cd5c5df4229972d8a0fd9dd9a089a1644d6ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:49 +0100 Subject: [PATCH 1557/2760] hw/audio/marvell_88w8618: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'mv88w8618_audio_callback' method has a 4k byte array used for copying data between the audio backend and device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'buf' array will be fully initialized when reading data from device memory. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-12-berrange@redhat.com [Fixed hw/audio/gus in commit message --Stefan] Signed-off-by: Stefan Hajnoczi --- hw/audio/marvell_88w8618.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/marvell_88w8618.c b/hw/audio/marvell_88w8618.c index 6d3ebbb0c8..c5c79d083a 100644 --- a/hw/audio/marvell_88w8618.c +++ b/hw/audio/marvell_88w8618.c @@ -66,7 +66,7 @@ static void mv88w8618_audio_callback(void *opaque, int free_out, int free_in) { mv88w8618_audio_state *s = opaque; int16_t *codec_buffer; - int8_t buf[4096]; + QEMU_UNINITIALIZED int8_t buf[4096]; int8_t *mem_buffer; int pos, block_size; From 30c82f6657c1ee9fbb5473924b4d3273f214bd6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:50 +0100 Subject: [PATCH 1558/2760] hw/audio/sb16: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'write_audio' method has a 4k byte array used for copying data between the audio backend and device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'tmpbuf' array will be fully initialized when reading data from device memory. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-13-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/audio/sb16.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/sb16.c b/hw/audio/sb16.c index 19fd3b9020..bac64118fe 100644 --- a/hw/audio/sb16.c +++ b/hw/audio/sb16.c @@ -1181,7 +1181,7 @@ static int write_audio (SB16State *s, int nchan, int dma_pos, IsaDma *isa_dma = nchan == s->dma ? s->isa_dma : s->isa_hdma; IsaDmaClass *k = ISADMA_GET_CLASS(isa_dma); int temp, net; - uint8_t tmpbuf[4096]; + QEMU_UNINITIALIZED uint8_t tmpbuf[4096]; temp = len; net = 0; From bb71d9fe1419f44529c91d1b09464718d157e647 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:51 +0100 Subject: [PATCH 1559/2760] hw/audio/via-ac97: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'out_cb' method has a 4k byte array used for copying data between the audio backend and device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'tmpbuf' array will be fully initialized when reading data from device memory. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-14-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/audio/via-ac97.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/audio/via-ac97.c b/hw/audio/via-ac97.c index 1e0a5c7398..d5231e1cf2 100644 --- a/hw/audio/via-ac97.c +++ b/hw/audio/via-ac97.c @@ -175,7 +175,7 @@ static void out_cb(void *opaque, int avail) ViaAC97SGDChannel *c = &s->aur; int temp, to_copy, copied; bool stop = false; - uint8_t tmpbuf[4096]; + QEMU_UNINITIALIZED uint8_t tmpbuf[4096]; if (c->stat & STAT_PAUSED) { return; From 8b1dac1ad57082611419b0e2f347acd96115d25f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:52 +0100 Subject: [PATCH 1560/2760] hw/char/sclpconsole-lm: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'process_mdb' method has a 4k byte array used for copying data between the guest and the chardev backend. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'buffer' array will be selectively initialized when data is converted between EBCDIC and ASCII. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-15-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/char/sclpconsole-lm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/char/sclpconsole-lm.c b/hw/char/sclpconsole-lm.c index e9580aacba..3e40d5e434 100644 --- a/hw/char/sclpconsole-lm.c +++ b/hw/char/sclpconsole-lm.c @@ -214,7 +214,7 @@ static int process_mdb(SCLPEvent *event, MDBO *mdbo) { int rc; int len; - uint8_t buffer[SIZE_BUFFER]; + QEMU_UNINITIALIZED uint8_t buffer[SIZE_BUFFER]; len = be16_to_cpu(mdbo->length); len -= sizeof(mdbo->length) + sizeof(mdbo->type) From ce14f24611aa0469b464a9512e192b4fd51dca2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:53 +0100 Subject: [PATCH 1561/2760] hw/dma/xlnx_csu_dma: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'xlnx_csu_dma_src_notify' method has a 4k byte array used for copying DMA data. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'buf' array will be fully initialized when data is copied. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-16-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/dma/xlnx_csu_dma.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/dma/xlnx_csu_dma.c b/hw/dma/xlnx_csu_dma.c index 3db3904d83..d8c7da1a50 100644 --- a/hw/dma/xlnx_csu_dma.c +++ b/hw/dma/xlnx_csu_dma.c @@ -287,7 +287,7 @@ static uint32_t xlnx_csu_dma_advance(XlnxCSUDMA *s, uint32_t len) static void xlnx_csu_dma_src_notify(void *opaque) { XlnxCSUDMA *s = XLNX_CSU_DMA(opaque); - unsigned char buf[4 * 1024]; + QEMU_UNINITIALIZED unsigned char buf[4 * 1024]; size_t rlen = 0; ptimer_transaction_begin(s->src_timer); From 7048e70f391df76d009eecca25f8027858f9f304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:54 +0100 Subject: [PATCH 1562/2760] hw/display/vmware_vga: skip automatic zero-init of large struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'vmsvga_fifo_run' method has a struct which is a little over 20k in size, used for holding image data for cursor changes. Skip the automatic zero-init of this struct to eliminate the performance overhead in the I/O hot path. The cursor variable will be fully initialized only when processing a cursor definition message from the guest. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-17-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/display/vmware_vga.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/display/vmware_vga.c b/hw/display/vmware_vga.c index 544bb65320..bc1a8ed466 100644 --- a/hw/display/vmware_vga.c +++ b/hw/display/vmware_vga.c @@ -618,7 +618,7 @@ static void vmsvga_fifo_run(struct vmsvga_state_s *s) uint32_t cmd, colour; int args, len, maxloop = 1024; int x, y, dx, dy, width, height; - struct vmsvga_cursor_definition_s cursor; + QEMU_UNINITIALIZED struct vmsvga_cursor_definition_s cursor; uint32_t cmd_start; len = vmsvga_fifo_length(s); From 5a1f614d0cd0bcc8e84e0b7ab6af63d56bd348a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:55 +0100 Subject: [PATCH 1563/2760] hw/hyperv/syndbg: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'handle_recv_msg' method has a 4k byte array used for copying data between the network socket and guest memory. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'data_buf' array will be fully initialized when data is read off the network socket. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-18-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/hyperv/syndbg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c index 8b8a14750d..ac7e15f6f1 100644 --- a/hw/hyperv/syndbg.c +++ b/hw/hyperv/syndbg.c @@ -192,7 +192,7 @@ static uint16_t handle_recv_msg(HvSynDbg *syndbg, uint64_t outgpa, { uint16_t ret; g_assert(MSG_BUFSZ >= qemu_target_page_size()); - uint8_t data_buf[MSG_BUFSZ]; + QEMU_UNINITIALIZED uint8_t data_buf[MSG_BUFSZ]; hwaddr out_len; void *out_data; ssize_t recv_byte_count; From 6992c886838282f36b20deee44b666bbfc573a8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:56 +0100 Subject: [PATCH 1564/2760] hw/misc/aspeed_hace: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'do_hash_operation' method has a 256 element iovec array used for holding pointers to data that is to be hashed. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'iovec' array will be selectively initialized based on data that needs to be hashed. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-19-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/misc/aspeed_hace.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/misc/aspeed_hace.c b/hw/misc/aspeed_hace.c index 8924a30eff..726368fbbc 100644 --- a/hw/misc/aspeed_hace.c +++ b/hw/misc/aspeed_hace.c @@ -419,7 +419,7 @@ static void hash_execute_acc_mode(AspeedHACEState *s, int algo, static void do_hash_operation(AspeedHACEState *s, int algo, bool sg_mode, bool acc_mode) { - struct iovec iov[ASPEED_HACE_MAX_SG]; + QEMU_UNINITIALIZED struct iovec iov[ASPEED_HACE_MAX_SG]; bool acc_final_request = false; int iov_idx = -1; From 3ccc6489dd4925ddd1f3066bd3751389169cd7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:57 +0100 Subject: [PATCH 1565/2760] hw/net/rtl8139: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'rtl8139_transmit_one' method has a 8k byte array used for copying data between guest and host. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'txbuffer' will be fully initialized when reading PCI DMA buffers. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-20-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/net/rtl8139.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 15b8f7501a..654a087d80 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -1816,7 +1816,7 @@ static int rtl8139_transmit_one(RTL8139State *s, int descriptor) PCIDevice *d = PCI_DEVICE(s); int txsize = s->TxStatus[descriptor] & 0x1fff; - uint8_t txbuffer[0x2000]; + QEMU_UNINITIALIZED uint8_t txbuffer[0x2000]; DPRINTF("+++ transmit reading %d bytes from host memory at 0x%08x\n", txsize, s->TxAddr[descriptor]); From e1afd5ee6eb2954f4baf3c97820e4aaf7de97d2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:58 +0100 Subject: [PATCH 1566/2760] hw/net/tulip: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'tulip_setup_frame' method has a 4k byte array used for copynig DMA data from the device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'buf' array will be fully initialized when reading data from the device. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-21-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/net/tulip.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/tulip.c b/hw/net/tulip.c index 63fe513458..319af906c8 100644 --- a/hw/net/tulip.c +++ b/hw/net/tulip.c @@ -629,7 +629,7 @@ static void tulip_setup_filter_addr(TULIPState *s, uint8_t *buf, int n) static void tulip_setup_frame(TULIPState *s, struct tulip_descriptor *desc) { - uint8_t buf[4096]; + QEMU_UNINITIALIZED uint8_t buf[4096]; int len = (desc->control >> TDES1_BUF1_SIZE_SHIFT) & TDES1_BUF1_SIZE_MASK; int i; From 21cf31c51a7aeff4270c9b30b37e019c536d54b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:36:59 +0100 Subject: [PATCH 1567/2760] hw/net/virtio-net: skip automatic zero-init of large arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'virtio_net_receive_rcu' method has three arrays with VIRTQUEUE_MAX_SIZE elements, which are apprixmately 32k in size used for copying data between guest and host. Skip the automatic zero-init of these arrays to eliminate the performance overhead in the I/O hot path. The three arrays will be selectively initialized as required when processing network buffers. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-22-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/net/virtio-net.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 221252e00a..eb93607b8c 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -1911,9 +1911,9 @@ static ssize_t virtio_net_receive_rcu(NetClientState *nc, const uint8_t *buf, VirtIONet *n = qemu_get_nic_opaque(nc); VirtIONetQueue *q; VirtIODevice *vdev = VIRTIO_DEVICE(n); - VirtQueueElement *elems[VIRTQUEUE_MAX_SIZE]; - size_t lens[VIRTQUEUE_MAX_SIZE]; - struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; + QEMU_UNINITIALIZED VirtQueueElement *elems[VIRTQUEUE_MAX_SIZE]; + QEMU_UNINITIALIZED size_t lens[VIRTQUEUE_MAX_SIZE]; + QEMU_UNINITIALIZED struct iovec mhdr_sg[VIRTQUEUE_MAX_SIZE]; struct virtio_net_hdr_v1_hash extra_hdr; unsigned mhdr_cnt = 0; size_t offset, i, guest_offset, j; From 8b723287b84a62bb5d1a7799ef0959ca8e6c293a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:37:00 +0100 Subject: [PATCH 1568/2760] hw/net/xgamc: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'xgmac_enet_send' method has a 8k byte array used for copying data between guest and host. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'frame' buffer will be fully initialized when reading guest memory to fetch the data to send. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Message-id: 20250610123709.835102-23-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/net/xgmac.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/xgmac.c b/hw/net/xgmac.c index 9c87c4e70f..d45f872467 100644 --- a/hw/net/xgmac.c +++ b/hw/net/xgmac.c @@ -207,7 +207,7 @@ static void xgmac_enet_send(XgmacState *s) struct desc bd; int frame_size; int len; - uint8_t frame[8192]; + QEMU_UNINITIALIZED uint8_t frame[8192]; uint8_t *ptr; ptr = frame; From 7eeb1d3acc175813ad3d5e824f26123e0992093a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:37:01 +0100 Subject: [PATCH 1569/2760] hw/nvme/ctrl: skip automatic zero-init of large arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'nvme_map_sgl' method has a 256 element array used for copying data from the device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'segment' array will be fully initialized when reading data from the device. The 'nme_changed_nslist' method has a 4k byte array that is manually initialized with memset(). The compiler ought to be intelligent enough to turn the memset() into a static initialization operation, and thus not duplicate the automatic zero-init. Replacing memset() with '{}' makes it unambiguous that the array is statically initialized. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Reviewed-by: Klaus Jensen Message-id: 20250610123709.835102-24-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/nvme/ctrl.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index fd935507bc..220002830d 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -1057,7 +1057,8 @@ static uint16_t nvme_map_sgl(NvmeCtrl *n, NvmeSg *sg, NvmeSglDescriptor sgl, */ #define SEG_CHUNK_SIZE 256 - NvmeSglDescriptor segment[SEG_CHUNK_SIZE], *sgld, *last_sgld; + QEMU_UNINITIALIZED NvmeSglDescriptor segment[SEG_CHUNK_SIZE]; + NvmeSglDescriptor *sgld, *last_sgld; uint64_t nsgld; uint32_t seg_len; uint16_t status; @@ -5128,7 +5129,7 @@ static uint16_t nvme_error_info(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, static uint16_t nvme_changed_nslist(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, uint64_t off, NvmeRequest *req) { - uint32_t nslist[1024]; + uint32_t nslist[1024] = {}; uint32_t trans_len; int i = 0; uint32_t nsid; @@ -5138,7 +5139,6 @@ static uint16_t nvme_changed_nslist(NvmeCtrl *n, uint8_t rae, uint32_t buf_len, return NVME_INVALID_FIELD | NVME_DNR; } - memset(nslist, 0x0, sizeof(nslist)); trans_len = MIN(sizeof(nslist) - off, buf_len); while ((nsid = find_first_bit(n->changed_nsids, NVME_CHANGED_NSID_SIZE)) != From 3438eabaf4f8ae58b6c47f1727938d1d7dac4823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:37:02 +0100 Subject: [PATCH 1570/2760] hw/ppc/pnv_occ: skip automatic zero-init of large struct MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'occ_model_tick' method has a 12k struct used for copying data between guest and host. Skip the automatic zero-init of this struct to eliminate the performance overhead in the I/O hot path. The 'dynamic_data' buffer will be fully initialized when reading data from the guest. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Reviewed-by: Klaus Jensen Reviewed-by: Harsh Prateek Bora Message-id: 20250610123709.835102-25-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/ppc/pnv_occ.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ppc/pnv_occ.c b/hw/ppc/pnv_occ.c index fa6f31cb8d..24b789c191 100644 --- a/hw/ppc/pnv_occ.c +++ b/hw/ppc/pnv_occ.c @@ -789,7 +789,7 @@ static bool occ_opal_process_command(PnvOCC *occ, static bool occ_model_tick(PnvOCC *occ) { - struct occ_dynamic_data dynamic_data; + QEMU_UNINITIALIZED struct occ_dynamic_data dynamic_data; if (!occ_read_dynamic_data(occ, &dynamic_data, NULL)) { /* Can't move OCC state field to safe because we can't map it! */ From 5dd9087fff74b5672526cad254e76f790fb35c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:37:03 +0100 Subject: [PATCH 1571/2760] hw/ppc/spapr_tpm_proxy: skip automatic zero-init of large arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'tpm_execute' method has a pair of 4k arrays used for copying data between guest and host. Skip the automatic zero-init of these arrays to eliminate the performance overhead in the I/O hot path. The two arrays will be fully initialized when reading data from guest memory or reading data from the proxy FD. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Reviewed-by: Klaus Jensen Reviewed-by: Harsh Prateek Bora Message-id: 20250610123709.835102-26-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/ppc/spapr_tpm_proxy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/ppc/spapr_tpm_proxy.c b/hw/ppc/spapr_tpm_proxy.c index 862eeaa50a..1297b3ad56 100644 --- a/hw/ppc/spapr_tpm_proxy.c +++ b/hw/ppc/spapr_tpm_proxy.c @@ -41,8 +41,8 @@ static ssize_t tpm_execute(SpaprTpmProxy *tpm_proxy, target_ulong *args) target_ulong data_in_size = args[2]; uint64_t data_out = ppc64_phys_to_real(args[3]); target_ulong data_out_size = args[4]; - uint8_t buf_in[TPM_SPAPR_BUFSIZE]; - uint8_t buf_out[TPM_SPAPR_BUFSIZE]; + QEMU_UNINITIALIZED uint8_t buf_in[TPM_SPAPR_BUFSIZE]; + QEMU_UNINITIALIZED uint8_t buf_out[TPM_SPAPR_BUFSIZE]; ssize_t ret; trace_spapr_tpm_execute(data_in, data_in_size, data_out, data_out_size); From 14997d521d1cd0bb36c902ef1032f0d3f2a3c912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:37:04 +0100 Subject: [PATCH 1572/2760] hw/usb/hcd-ohci: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'ohci_service_iso_td' method has a 8k byte array used for copying data between guest and host. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'buf' array will be fully initialized when reading data from guest memory. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Reviewed-by: Klaus Jensen Reviewed-by: Harsh Prateek Bora Message-id: 20250610123709.835102-27-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/usb/hcd-ohci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/usb/hcd-ohci.c b/hw/usb/hcd-ohci.c index 71b54914d3..72a9f9f474 100644 --- a/hw/usb/hcd-ohci.c +++ b/hw/usb/hcd-ohci.c @@ -577,7 +577,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed) USBDevice *dev; USBEndpoint *ep; USBPacket *pkt; - uint8_t buf[8192]; + QEMU_UNINITIALIZED uint8_t buf[8192]; bool int_req; struct ohci_iso_td iso_td; uint32_t addr; From 55243edf42ee87bce9f36ca251f3ab9cda1563e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:37:05 +0100 Subject: [PATCH 1573/2760] hw/scsi/lsi53c895a: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'lsi_memcpy' method has a 4k byte array used for copying data to/from the device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'buf' array will be fully initialized when data is copied. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Reviewed-by: Klaus Jensen Reviewed-by: Harsh Prateek Bora Message-id: 20250610123709.835102-28-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/scsi/lsi53c895a.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/scsi/lsi53c895a.c b/hw/scsi/lsi53c895a.c index f4f2ef321e..9ea4aa0a85 100644 --- a/hw/scsi/lsi53c895a.c +++ b/hw/scsi/lsi53c895a.c @@ -1112,7 +1112,7 @@ static void lsi_do_msgout(LSIState *s) static void lsi_memcpy(LSIState *s, uint32_t dest, uint32_t src, int count) { int n; - uint8_t buf[LSI_BUF_SIZE]; + QEMU_UNINITIALIZED uint8_t buf[LSI_BUF_SIZE]; trace_lsi_memcpy(dest, src, count); while (count) { From ca0559e2350c618048f7caf80cb79c1259e7cfd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:37:06 +0100 Subject: [PATCH 1574/2760] hw/scsi/megasas: skip automatic zero-init of large arrays MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'megasas_dcmd_pd_get_list' and 'megasas_dcmd_get_properties' methods have 4k structs used for copying data from the device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'info' structs are manually initialized with memset(). The compiler ought to be intelligent enough to turn the memset() into a static initialization operation, and thus not duplicate the automatic zero-init. Replacing memset() with '{}' makes it unambiguous that the arrays are statically initialized. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Reviewed-by: Klaus Jensen Reviewed-by: Harsh Prateek Bora Message-id: 20250610123709.835102-29-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/scsi/megasas.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/hw/scsi/megasas.c b/hw/scsi/megasas.c index 55cd188bd5..844643d916 100644 --- a/hw/scsi/megasas.c +++ b/hw/scsi/megasas.c @@ -981,13 +981,11 @@ static int megasas_event_wait(MegasasState *s, MegasasCmd *cmd) static int megasas_dcmd_pd_get_list(MegasasState *s, MegasasCmd *cmd) { - struct mfi_pd_list info; - size_t dcmd_size = sizeof(info); + struct mfi_pd_list info = {}; BusChild *kid; uint32_t offset, dcmd_limit, num_pd_disks = 0, max_pd_disks; dma_addr_t residual; - memset(&info, 0, dcmd_size); offset = 8; dcmd_limit = offset + sizeof(struct mfi_pd_address); if (cmd->iov_size < dcmd_limit) { @@ -1429,11 +1427,10 @@ static int megasas_dcmd_cfg_read(MegasasState *s, MegasasCmd *cmd) static int megasas_dcmd_get_properties(MegasasState *s, MegasasCmd *cmd) { - struct mfi_ctrl_props info; + struct mfi_ctrl_props info = {}; size_t dcmd_size = sizeof(info); dma_addr_t residual; - memset(&info, 0x0, dcmd_size); if (cmd->iov_size < dcmd_size) { trace_megasas_dcmd_invalid_xfer_len(cmd->index, cmd->iov_size, dcmd_size); From 7708e298180550eac262c1fd742e6e80c711a5d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:37:07 +0100 Subject: [PATCH 1575/2760] hw/ufs/lu: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'ufs_emulate_scsi_cmd' method has a 4k byte array used for copying data from the device. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'outbuf' array will be fully initialized when data is copied from the guest. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Reviewed-by: Klaus Jensen Reviewed-by: Harsh Prateek Bora Message-id: 20250610123709.835102-30-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- hw/ufs/lu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/ufs/lu.c b/hw/ufs/lu.c index 57b307ea56..2d8ffd72c5 100644 --- a/hw/ufs/lu.c +++ b/hw/ufs/lu.c @@ -194,7 +194,7 @@ static int ufs_emulate_wlun_inquiry(UfsRequest *req, uint8_t *outbuf, static UfsReqResult ufs_emulate_scsi_cmd(UfsLu *lu, UfsRequest *req) { uint8_t lun = lu->lun; - uint8_t outbuf[4096]; + QEMU_UNINITIALIZED uint8_t outbuf[4096]; uint8_t sense_buf[UFS_SENSE_SIZE]; uint8_t scsi_status; int len = 0; From 751b0e79f1e0e7f88fad2fe2f22595ad03d78859 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:37:08 +0100 Subject: [PATCH 1576/2760] net/socket: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'net_socket_send' method has a 68k byte array used for copying data between guest and host. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'buf1' array will be fully initialized when reading data off the network socket. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Reviewed-by: Klaus Jensen Reviewed-by: Harsh Prateek Bora Message-id: 20250610123709.835102-31-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- net/socket.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/socket.c b/net/socket.c index 8e3702e1f3..784dda686f 100644 --- a/net/socket.c +++ b/net/socket.c @@ -157,7 +157,7 @@ static void net_socket_send(void *opaque) NetSocketState *s = opaque; int size; int ret; - uint8_t buf1[NET_BUFSIZE]; + QEMU_UNINITIALIZED uint8_t buf1[NET_BUFSIZE]; const uint8_t *buf; size = recv(s->fd, buf1, sizeof(buf1), 0); From 837b87c4c5ba9ac7a255133c6642b8d578272a70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 10 Jun 2025 13:37:09 +0100 Subject: [PATCH 1577/2760] net/stream: skip automatic zero-init of large array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'net_stream_send' method has a 68k byte array used for copying data between guest and host. Skip the automatic zero-init of this array to eliminate the performance overhead in the I/O hot path. The 'buf1' array will be fully initialized when reading data off the network socket. Signed-off-by: Daniel P. Berrangé Reviewed-by: Stefan Hajnoczi Reviewed-by: Klaus Jensen Reviewed-by: Harsh Prateek Bora Message-id: 20250610123709.835102-32-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- net/stream.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/stream.c b/net/stream.c index 4de5613844..6152d2a05e 100644 --- a/net/stream.c +++ b/net/stream.c @@ -148,7 +148,7 @@ static gboolean net_stream_send(QIOChannel *ioc, NetStreamState *s = data; int size; int ret; - char buf1[NET_BUFSIZE]; + QEMU_UNINITIALIZED char buf1[NET_BUFSIZE]; const char *buf; size = qio_channel_read(s->ioc, buf1, sizeof(buf1), NULL); From e372214e663a4370fe064f7867f402eade37357e Mon Sep 17 00:00:00 2001 From: Ethan Chen Date: Fri, 6 Jun 2025 17:57:28 +0800 Subject: [PATCH 1578/2760] qemu-options.hx: Fix reversed description of icount sleep behavior The documentation for the -icount option incorrectly describes the behavior of the sleep suboption. Based on the actual implementation and system behavior, the effects of sleep=on and sleep=off were inadvertently reversed. This commit updates the description to reflect their intended functionality. Cc: qemu-stable@nongnu.org Fixes: fa647905e6ba ("qemu-options.hx: Fix minor issues in icount documentation") Signed-off-by: Ethan Chen Message-id: 20250606095728.3672832-1-ethan84@andestech.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- qemu-options.hx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qemu-options.hx b/qemu-options.hx index 7eb8e02b4b..1f862b19a6 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -4936,13 +4936,13 @@ SRST with actual performance. When the virtual cpu is sleeping, the virtual time will advance at - default speed unless ``sleep=on`` is specified. With - ``sleep=on``, the virtual time will jump to the next timer + default speed unless ``sleep=off`` is specified. With + ``sleep=off``, the virtual time will jump to the next timer deadline instantly whenever the virtual cpu goes to sleep mode and will not advance if no timer is enabled. This behavior gives deterministic execution times from the guest point of view. - The default if icount is enabled is ``sleep=off``. - ``sleep=on`` cannot be used together with either ``shift=auto`` + The default if icount is enabled is ``sleep=on``. + ``sleep=off`` cannot be used together with either ``shift=auto`` or ``align=on``. ``align=on`` will activate the delay algorithm which will try to From bedcc7465db867841a4c76e5a3aab8a7d51f22aa Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sat, 31 May 2025 21:11:06 +0900 Subject: [PATCH 1579/2760] target/arm: Define raw write for PMU CLR registers Raw writes to PMCNTENCLR and PMCNTENCLR_EL0 incorrectly used their default write function, which clears written bits instead of writes the raw value. PMINTENCLR and PMINTENCLR_EL1 are similar registers, but they instead had ARM_CP_NO_RAW. Commit 7a0e58fa6487 ("target-arm: Split NO_MIGRATE into ALIAS and NO_RAW") sugguests ARM_CP_ALIAS should be used instead of ARM_CP_NO_RAW in such a case: > We currently mark ARM coprocessor/system register definitions with > the flag ARM_CP_NO_MIGRATE for two different reasons: > 1) register is an alias on to state that's also visible via > some other register, and that other register is the one > responsible for migrating the state > 2) register is not actually state at all (for instance the TLB > or cache maintenance operation "registers") and it makes no > sense to attempt to migrate it or otherwise access the raw state > > This works fine for identifying which registers should be ignored > when performing migration, but we also use the same functions for > synchronizing system register state between QEMU and the kernel > when using KVM. In this case we don't want to try to sync state > into registers in category 2, but we do want to sync into registers > in category 1, because the kernel might have picked a different > one of the aliases as its choice for which one to expose for > migration. These registers fall in category 1 (ARM_CP_ALIAS), not category 2 (ARM_CP_NO_RAW). ARM_CP_NO_RAW also has another undesired side effect that hides registers from GDB. Properly set raw write functions and drop the ARM_CP_NO_RAW flag from PMINTENCLR and PMINTENCLR_EL1; this fixes GDB/KVM state synchronization of PMCNTENCLR and PMCNTENCLR_EL0, and exposes all these four registers to GDB. It is not necessary to add ARM_CP_ALIAS to these registers because the flag is already set. Signed-off-by: Akihiko Odaki Message-id: 20250531-clr-v3-1-377f9bf1746d@rsg.ci.i.u-tokyo.ac.jp Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/helper.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 7631210287..889d308807 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -1904,7 +1904,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), .accessfn = pmreg_access, .fgt = FGT_PMCNTEN, - .writefn = pmcntenclr_write, + .writefn = pmcntenclr_write, .raw_writefn = raw_write, .type = ARM_CP_ALIAS | ARM_CP_IO }, { .name = "PMCNTENCLR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 2, @@ -1912,7 +1912,7 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .fgt = FGT_PMCNTEN, .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), - .writefn = pmcntenclr_write }, + .writefn = pmcntenclr_write, .raw_writefn = raw_write }, { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, .access = PL0_RW, .type = ARM_CP_IO, .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), @@ -2029,16 +2029,16 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, .access = PL1_RW, .accessfn = access_tpm, .fgt = FGT_PMINTEN, - .type = ARM_CP_ALIAS | ARM_CP_IO | ARM_CP_NO_RAW, + .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), - .writefn = pmintenclr_write, }, + .writefn = pmintenclr_write, .raw_writefn = raw_write }, { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2, .access = PL1_RW, .accessfn = access_tpm, .fgt = FGT_PMINTEN, - .type = ARM_CP_ALIAS | ARM_CP_IO | ARM_CP_NO_RAW, + .type = ARM_CP_ALIAS | ARM_CP_IO, .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), - .writefn = pmintenclr_write }, + .writefn = pmintenclr_write, .raw_writefn = raw_write }, { .name = "CCSIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, .access = PL1_R, From 73cdd846adb80060fbdb2f2557aeafb41cb39064 Mon Sep 17 00:00:00 2001 From: Souleymane Conte Date: Mon, 9 Jun 2025 13:51:24 +0000 Subject: [PATCH 1580/2760] docs/interop: convert qed_spec.txt to reStructuredText format Convert the qed_spec.txt file to reStructuredText and include it in the manual. buglink: https://gitlab.com/qemu-project/qemu/-/issues/527 Signed-off-by: Souleymane Conte Message-id: 20250609135124.45078-1-conte.souleymane@gmail.com Reviewed-by: Peter Maydell [PMM: adjusted position of doc in the table of contents; bulked up commit message; added file to MAINTAINERS section for QED; made 'Consistency checking' a higher level section; fixed one preexisting grammar nit (s/by from/from/)] Signed-off-by: Peter Maydell --- MAINTAINERS | 1 + docs/interop/index.rst | 1 + docs/interop/qed_spec.rst | 219 ++++++++++++++++++++++++++++++++++++++ docs/interop/qed_spec.txt | 138 ------------------------ 4 files changed, 221 insertions(+), 138 deletions(-) create mode 100644 docs/interop/qed_spec.rst delete mode 100644 docs/interop/qed_spec.txt diff --git a/MAINTAINERS b/MAINTAINERS index 84cfef835e..94c4076127 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4117,6 +4117,7 @@ M: Stefan Hajnoczi L: qemu-block@nongnu.org S: Supported F: block/qed.c +F: docs/interop/qed_spec.rst raw M: Kevin Wolf diff --git a/docs/interop/index.rst b/docs/interop/index.rst index 4b951ae416..972f3e49ce 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -18,6 +18,7 @@ are useful for making QEMU interoperate with other software. parallels prl-xml qcow2 + qed_spec pr-helper qmp-spec qemu-ga diff --git a/docs/interop/qed_spec.rst b/docs/interop/qed_spec.rst new file mode 100644 index 0000000000..cd6c7d9005 --- /dev/null +++ b/docs/interop/qed_spec.rst @@ -0,0 +1,219 @@ +=================================== +QED Image File Format Specification +=================================== + +The file format looks like this:: + + +----------+----------+----------+-----+ + | cluster0 | cluster1 | cluster2 | ... | + +----------+----------+----------+-----+ + +The first cluster begins with the ``header``. The header contains information +about where regular clusters start; this allows the header to be extensible and +store extra information about the image file. A regular cluster may be +a ``data cluster``, an ``L2``, or an ``L1 table``. L1 and L2 tables are composed +of one or more contiguous clusters. + +Normally the file size will be a multiple of the cluster size. If the file size +is not a multiple, extra information after the last cluster may not be preserved +if data is written. Legitimate extra information should use space between the header +and the first regular cluster. + +All fields are little-endian. + +Header +------ + +:: + + Header { + uint32_t magic; /* QED\0 */ + + uint32_t cluster_size; /* in bytes */ + uint32_t table_size; /* for L1 and L2 tables, in clusters */ + uint32_t header_size; /* in clusters */ + + uint64_t features; /* format feature bits */ + uint64_t compat_features; /* compat feature bits */ + uint64_t autoclear_features; /* self-resetting feature bits */ + + uint64_t l1_table_offset; /* in bytes */ + uint64_t image_size; /* total logical image size, in bytes */ + + /* if (features & QED_F_BACKING_FILE) */ + uint32_t backing_filename_offset; /* in bytes from start of header */ + uint32_t backing_filename_size; /* in bytes */ + } + +Field descriptions: +~~~~~~~~~~~~~~~~~~~ + +- ``cluster_size`` must be a power of 2 in range [2^12, 2^26]. +- ``table_size`` must be a power of 2 in range [1, 16]. +- ``header_size`` is the number of clusters used by the header and any additional + information stored before regular clusters. +- ``features``, ``compat_features``, and ``autoclear_features`` are file format + extension bitmaps. They work as follows: + + - An image with unknown ``features`` bits enabled must not be opened. File format + changes that are not backwards-compatible must use ``features`` bits. + - An image with unknown ``compat_features`` bits enabled can be opened safely. + The unknown features are simply ignored and represent backwards-compatible + changes to the file format. + - An image with unknown ``autoclear_features`` bits enable can be opened safely + after clearing the unknown bits. This allows for backwards-compatible changes + to the file format which degrade gracefully and can be re-enabled again by a + new program later. +- ``l1_table_offset`` is the offset of the first byte of the L1 table in the image + file and must be a multiple of ``cluster_size``. +- ``image_size`` is the block device size seen by the guest and must be a multiple + of 512 bytes. +- ``backing_filename_offset`` and ``backing_filename_size`` describe a string in + (byte offset, byte size) form. It is not NUL-terminated and has no alignment constraints. + The string must be stored within the first ``header_size`` clusters. The backing filename + may be an absolute path or relative to the image file. + +Feature bits: +~~~~~~~~~~~~~ + +- ``QED_F_BACKING_FILE = 0x01``. The image uses a backing file. +- ``QED_F_NEED_CHECK = 0x02``. The image needs a consistency check before use. +- ``QED_F_BACKING_FORMAT_NO_PROBE = 0x04``. The backing file is a raw disk image + and no file format autodetection should be attempted. This should be used to + ensure that raw backing files are never detected as an image format if they happen + to contain magic constants. + +There are currently no defined ``compat_features`` or ``autoclear_features`` bits. + +Fields predicated on a feature bit are only used when that feature is set. +The fields always take up header space, regardless of whether or not the feature +bit is set. + +Tables +------ + +Tables provide the translation from logical offsets in the block device to cluster +offsets in the file. + +:: + + #define TABLE_NOFFSETS (table_size * cluster_size / sizeof(uint64_t)) + + Table { + uint64_t offsets[TABLE_NOFFSETS]; + } + +The tables are organized as follows:: + + +----------+ + | L1 table | + +----------+ + ,------' | '------. + +----------+ | +----------+ + | L2 table | ... | L2 table | + +----------+ +----------+ + ,------' | '------. + +----------+ | +----------+ + | Data | ... | Data | + +----------+ +----------+ + +A table is made up of one or more contiguous clusters. The ``table_size`` header +field determines table size for an image file. For example, ``cluster_size=64 KB`` +and ``table_size=4`` results in 256 KB tables. + +The logical image size must be less than or equal to the maximum possible size of +clusters rooted by the L1 table: + +.. code:: + + header.image_size <= TABLE_NOFFSETS * TABLE_NOFFSETS * header.cluster_size + +L1, L2, and data cluster offsets must be aligned to ``header.cluster_size``. +The following offsets have special meanings: + +L2 table offsets +~~~~~~~~~~~~~~~~ + +- 0 - unallocated. The L2 table is not yet allocated. + +Data cluster offsets +~~~~~~~~~~~~~~~~~~~~ + +- 0 - unallocated. The data cluster is not yet allocated. +- 1 - zero. The data cluster contents are all zeroes and no cluster is allocated. + +Future format extensions may wish to store per-offset information. The least +significant 12 bits of an offset are reserved for this purpose and must be set +to zero. Image files with ``cluster_size`` > 2^12 will have more unused bits +which should also be zeroed. + +Unallocated L2 tables and data clusters +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Reads to an unallocated area of the image file access the backing file. If there +is no backing file, then zeroes are produced. The backing file may be smaller +than the image file and reads of unallocated areas beyond the end of the backing +file produce zeroes. + +Writes to an unallocated area cause a new data clusters to be allocated, and a new +L2 table if that is also unallocated. The new data cluster is populated with data +from the backing file (or zeroes if no backing file) and the data being written. + +Zero data clusters +~~~~~~~~~~~~~~~~~~ + +Zero data clusters are a space-efficient way of storing zeroed regions of the image. + +Reads to a zero data cluster produce zeroes. + +.. note:: + The difference between an unallocated and a zero data cluster is that zero data + clusters stop the reading of contents from the backing file. + +Writes to a zero data cluster cause a new data cluster to be allocated. The new +data cluster is populated with zeroes and the data being written. + +Logical offset translation +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Logical offsets are translated into cluster offsets as follows:: + + table_bits table_bits cluster_bits + <--------> <--------> <---------------> + +----------+----------+-----------------+ + | L1 index | L2 index | byte offset | + +----------+----------+-----------------+ + + Structure of a logical offset + + offset_mask = ~(cluster_size - 1) # mask for the image file byte offset + + def logical_to_cluster_offset(l1_index, l2_index, byte_offset): + l2_offset = l1_table[l1_index] + l2_table = load_table(l2_offset) + cluster_offset = l2_table[l2_index] & offset_mask + return cluster_offset + byte_offset + +Consistency checking +-------------------- + +This section is informational and included to provide background on the use +of the ``QED_F_NEED_CHECK features`` bit. + +The ``QED_F_NEED_CHECK`` bit is used to mark an image as dirty before starting +an operation that could leave the image in an inconsistent state if interrupted +by a crash or power failure. A dirty image must be checked on open because its +metadata may not be consistent. + +Consistency check includes the following invariants: + +- Each cluster is referenced once and only once. It is an inconsistency to have + a cluster referenced more than once by L1 or L2 tables. A cluster has been leaked + if it has no references. +- Offsets must be within the image file size and must be ``cluster_size`` aligned. +- Table offsets must at least ``table_size`` * ``cluster_size`` bytes from the end + of the image file so that there is space for the entire table. + +The consistency check process starts from ``l1_table_offset`` and scans all L2 tables. +After the check completes with no other errors besides leaks, the ``QED_F_NEED_CHECK`` +bit can be cleared and the image can be accessed. diff --git a/docs/interop/qed_spec.txt b/docs/interop/qed_spec.txt deleted file mode 100644 index 7982e058b2..0000000000 --- a/docs/interop/qed_spec.txt +++ /dev/null @@ -1,138 +0,0 @@ -=Specification= - -The file format looks like this: - - +----------+----------+----------+-----+ - | cluster0 | cluster1 | cluster2 | ... | - +----------+----------+----------+-----+ - -The first cluster begins with the '''header'''. The header contains information about where regular clusters start; this allows the header to be extensible and store extra information about the image file. A regular cluster may be a '''data cluster''', an '''L2''', or an '''L1 table'''. L1 and L2 tables are composed of one or more contiguous clusters. - -Normally the file size will be a multiple of the cluster size. If the file size is not a multiple, extra information after the last cluster may not be preserved if data is written. Legitimate extra information should use space between the header and the first regular cluster. - -All fields are little-endian. - -==Header== - Header { - uint32_t magic; /* QED\0 */ - - uint32_t cluster_size; /* in bytes */ - uint32_t table_size; /* for L1 and L2 tables, in clusters */ - uint32_t header_size; /* in clusters */ - - uint64_t features; /* format feature bits */ - uint64_t compat_features; /* compat feature bits */ - uint64_t autoclear_features; /* self-resetting feature bits */ - - uint64_t l1_table_offset; /* in bytes */ - uint64_t image_size; /* total logical image size, in bytes */ - - /* if (features & QED_F_BACKING_FILE) */ - uint32_t backing_filename_offset; /* in bytes from start of header */ - uint32_t backing_filename_size; /* in bytes */ - } - -Field descriptions: -* ''cluster_size'' must be a power of 2 in range [2^12, 2^26]. -* ''table_size'' must be a power of 2 in range [1, 16]. -* ''header_size'' is the number of clusters used by the header and any additional information stored before regular clusters. -* ''features'', ''compat_features'', and ''autoclear_features'' are file format extension bitmaps. They work as follows: -** An image with unknown ''features'' bits enabled must not be opened. File format changes that are not backwards-compatible must use ''features'' bits. -** An image with unknown ''compat_features'' bits enabled can be opened safely. The unknown features are simply ignored and represent backwards-compatible changes to the file format. -** An image with unknown ''autoclear_features'' bits enable can be opened safely after clearing the unknown bits. This allows for backwards-compatible changes to the file format which degrade gracefully and can be re-enabled again by a new program later. -* ''l1_table_offset'' is the offset of the first byte of the L1 table in the image file and must be a multiple of ''cluster_size''. -* ''image_size'' is the block device size seen by the guest and must be a multiple of 512 bytes. -* ''backing_filename_offset'' and ''backing_filename_size'' describe a string in (byte offset, byte size) form. It is not NUL-terminated and has no alignment constraints. The string must be stored within the first ''header_size'' clusters. The backing filename may be an absolute path or relative to the image file. - -Feature bits: -* QED_F_BACKING_FILE = 0x01. The image uses a backing file. -* QED_F_NEED_CHECK = 0x02. The image needs a consistency check before use. -* QED_F_BACKING_FORMAT_NO_PROBE = 0x04. The backing file is a raw disk image and no file format autodetection should be attempted. This should be used to ensure that raw backing files are never detected as an image format if they happen to contain magic constants. - -There are currently no defined ''compat_features'' or ''autoclear_features'' bits. - -Fields predicated on a feature bit are only used when that feature is set. The fields always take up header space, regardless of whether or not the feature bit is set. - -==Tables== - -Tables provide the translation from logical offsets in the block device to cluster offsets in the file. - - #define TABLE_NOFFSETS (table_size * cluster_size / sizeof(uint64_t)) - - Table { - uint64_t offsets[TABLE_NOFFSETS]; - } - -The tables are organized as follows: - - +----------+ - | L1 table | - +----------+ - ,------' | '------. - +----------+ | +----------+ - | L2 table | ... | L2 table | - +----------+ +----------+ - ,------' | '------. - +----------+ | +----------+ - | Data | ... | Data | - +----------+ +----------+ - -A table is made up of one or more contiguous clusters. The table_size header field determines table size for an image file. For example, cluster_size=64 KB and table_size=4 results in 256 KB tables. - -The logical image size must be less than or equal to the maximum possible size of clusters rooted by the L1 table: - header.image_size <= TABLE_NOFFSETS * TABLE_NOFFSETS * header.cluster_size - -L1, L2, and data cluster offsets must be aligned to header.cluster_size. The following offsets have special meanings: - -===L2 table offsets=== -* 0 - unallocated. The L2 table is not yet allocated. - -===Data cluster offsets=== -* 0 - unallocated. The data cluster is not yet allocated. -* 1 - zero. The data cluster contents are all zeroes and no cluster is allocated. - -Future format extensions may wish to store per-offset information. The least significant 12 bits of an offset are reserved for this purpose and must be set to zero. Image files with cluster_size > 2^12 will have more unused bits which should also be zeroed. - -===Unallocated L2 tables and data clusters=== -Reads to an unallocated area of the image file access the backing file. If there is no backing file, then zeroes are produced. The backing file may be smaller than the image file and reads of unallocated areas beyond the end of the backing file produce zeroes. - -Writes to an unallocated area cause a new data clusters to be allocated, and a new L2 table if that is also unallocated. The new data cluster is populated with data from the backing file (or zeroes if no backing file) and the data being written. - -===Zero data clusters=== -Zero data clusters are a space-efficient way of storing zeroed regions of the image. - -Reads to a zero data cluster produce zeroes. Note that the difference between an unallocated and a zero data cluster is that zero data clusters stop the reading of contents from the backing file. - -Writes to a zero data cluster cause a new data cluster to be allocated. The new data cluster is populated with zeroes and the data being written. - -===Logical offset translation=== -Logical offsets are translated into cluster offsets as follows: - - table_bits table_bits cluster_bits - <--------> <--------> <---------------> - +----------+----------+-----------------+ - | L1 index | L2 index | byte offset | - +----------+----------+-----------------+ - - Structure of a logical offset - - offset_mask = ~(cluster_size - 1) # mask for the image file byte offset - - def logical_to_cluster_offset(l1_index, l2_index, byte_offset): - l2_offset = l1_table[l1_index] - l2_table = load_table(l2_offset) - cluster_offset = l2_table[l2_index] & offset_mask - return cluster_offset + byte_offset - -==Consistency checking== - -This section is informational and included to provide background on the use of the QED_F_NEED_CHECK ''features'' bit. - -The QED_F_NEED_CHECK bit is used to mark an image as dirty before starting an operation that could leave the image in an inconsistent state if interrupted by a crash or power failure. A dirty image must be checked on open because its metadata may not be consistent. - -Consistency check includes the following invariants: -# Each cluster is referenced once and only once. It is an inconsistency to have a cluster referenced more than once by L1 or L2 tables. A cluster has been leaked if it has no references. -# Offsets must be within the image file size and must be ''cluster_size'' aligned. -# Table offsets must at least ''table_size'' * ''cluster_size'' bytes from the end of the image file so that there is space for the entire table. - -The consistency check process starts by from ''l1_table_offset'' and scans all L2 tables. After the check completes with no other errors besides leaks, the QED_F_NEED_CHECK bit can be cleared and the image can be accessed. From 5dc8e4e892ba10e040d12afece0d36b8b6a269d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?= Date: Mon, 26 May 2025 10:55:20 +0200 Subject: [PATCH 1581/2760] hw/arm: make cpu targeted by arm_load_kernel the primary CPU. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, arm booting processus assumes that the first_cpu is the CPU that will boot: `arm_load_kernel` is powering off all but the `first_cpu`; `do_cpu_reset` is setting the loader address only for this `first_cpu`. For most of the boards, this isn't an issue as the kernel is loaded and booted on the first CPU anyway. However, for zynqmp, the option "boot-cpu" allows to choose any CPUs. Create a new arm_boot_info entry `primary_cpu` recording which CPU will be boot first. This one is set when `arm_boot_kernel` is called. Signed-off-by: Clément Chigot Reviewed-by: Peter Maydell Message-id: 20250526085523.809003-2-chigot@adacore.com Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- hw/arm/boot.c | 15 +++++++-------- include/hw/arm/boot.h | 3 +++ 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 79afb51b8a..3c93d87985 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -744,7 +744,7 @@ static void do_cpu_reset(void *opaque) } else { if (arm_feature(env, ARM_FEATURE_EL3) && (info->secure_boot || - (info->secure_board_setup && cs == first_cpu))) { + (info->secure_board_setup && cpu == info->primary_cpu))) { /* Start this CPU in Secure SVC */ target_el = 3; } @@ -752,7 +752,7 @@ static void do_cpu_reset(void *opaque) arm_emulate_firmware_reset(cs, target_el); - if (cs == first_cpu) { + if (cpu == info->primary_cpu) { AddressSpace *as = arm_boot_address_space(cpu, info); cpu_set_pc(cs, info->loader_start); @@ -1239,6 +1239,9 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) info->dtb_filename = ms->dtb; info->dtb_limit = 0; + /* We assume the CPU passed as argument is the primary CPU. */ + info->primary_cpu = cpu; + /* Load the kernel. */ if (!info->kernel_filename || info->firmware_loaded) { arm_setup_firmware_boot(cpu, info); @@ -1288,12 +1291,8 @@ void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info) object_property_set_int(cpuobj, "psci-conduit", info->psci_conduit, &error_abort); - /* - * Secondary CPUs start in PSCI powered-down state. Like the - * code in do_cpu_reset(), we assume first_cpu is the primary - * CPU. - */ - if (cs != first_cpu) { + /* Secondary CPUs start in PSCI powered-down state. */ + if (ARM_CPU(cs) != info->primary_cpu) { object_property_set_bool(cpuobj, "start-powered-off", true, &error_abort); } diff --git a/include/hw/arm/boot.h b/include/hw/arm/boot.h index b12bf61ca8..a2e22bda8a 100644 --- a/include/hw/arm/boot.h +++ b/include/hw/arm/boot.h @@ -132,6 +132,9 @@ struct arm_boot_info { bool secure_board_setup; arm_endianness endianness; + + /* CPU having load the kernel and that should be the first to boot. */ + ARMCPU *primary_cpu; }; /** From 6559e7ad8e535b70e34c79076e6cb6c09d626d0d Mon Sep 17 00:00:00 2001 From: Frederic Konrad Date: Mon, 26 May 2025 10:55:21 +0200 Subject: [PATCH 1582/2760] hw/intc/arm_gic: introduce a first-cpu-index property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This introduces a first-cpu-index property to the arm-gic, as some SOCs could have two separate GIC (ie: the zynqmp). Signed-off-by: Clément Chigot Message-id: 20250526085523.809003-3-chigot@adacore.com Reviewed-by: Peter Maydell [PMM: slightly expanded comment documenting GIC property] Signed-off-by: Peter Maydell --- hw/intc/arm_gic.c | 2 +- hw/intc/arm_gic_common.c | 1 + include/hw/intc/arm_gic.h | 3 +++ include/hw/intc/arm_gic_common.h | 2 ++ 4 files changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/intc/arm_gic.c b/hw/intc/arm_gic.c index d18bef40fc..899f133363 100644 --- a/hw/intc/arm_gic.c +++ b/hw/intc/arm_gic.c @@ -59,7 +59,7 @@ static const uint8_t gic_id_gicv2[] = { static inline int gic_get_current_cpu(GICState *s) { if (!qtest_enabled() && s->num_cpu > 1) { - return current_cpu->cpu_index; + return current_cpu->cpu_index - s->first_cpu_index; } return 0; } diff --git a/hw/intc/arm_gic_common.c b/hw/intc/arm_gic_common.c index 0f0c48d89a..ed5be05645 100644 --- a/hw/intc/arm_gic_common.c +++ b/hw/intc/arm_gic_common.c @@ -350,6 +350,7 @@ static void arm_gic_common_linux_init(ARMLinuxBootIf *obj, static const Property arm_gic_common_properties[] = { DEFINE_PROP_UINT32("num-cpu", GICState, num_cpu, 1), + DEFINE_PROP_UINT32("first-cpu-index", GICState, first_cpu_index, 0), DEFINE_PROP_UINT32("num-irq", GICState, num_irq, 32), /* Revision can be 1 or 2 for GIC architecture specification * versions 1 or 2, or 0 to indicate the legacy 11MPCore GIC. diff --git a/include/hw/intc/arm_gic.h b/include/hw/intc/arm_gic.h index 48f6a51a70..be923f7ed8 100644 --- a/include/hw/intc/arm_gic.h +++ b/include/hw/intc/arm_gic.h @@ -27,6 +27,9 @@ * implement the security extensions * + QOM property "has-virtualization-extensions": set true if the GIC should * implement the virtualization extensions + * + QOM property "first-cpu-index": index of the first cpu attached to the + * GIC (default 0). The CPUs connected to the GIC are assumed to be + * first-cpu-index, first-cpu-index + 1, ... first-cpu-index + num-cpu - 1. * + unnamed GPIO inputs: (where P is number of SPIs, i.e. num-irq - 32) * [0..P-1] SPIs * [P..P+31] PPIs for CPU 0 diff --git a/include/hw/intc/arm_gic_common.h b/include/hw/intc/arm_gic_common.h index 97fea4102d..93a3cc2bf8 100644 --- a/include/hw/intc/arm_gic_common.h +++ b/include/hw/intc/arm_gic_common.h @@ -129,6 +129,8 @@ struct GICState { uint32_t num_lrs; uint32_t num_cpu; + /* cpu_index of the first CPU, attached to this GIC. */ + uint32_t first_cpu_index; MemoryRegion iomem; /* Distributor */ /* This is just so we can have an opaque pointer which identifies From cd38e638c43e4d5d3fd65dd4529c2e6153c9c408 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 5 Jun 2025 15:18:01 +0100 Subject: [PATCH 1583/2760] hw/arm/mps2: Configure the AN500 CPU with 16 MPU regions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AN500 application note documents that it configures the Cortex-M7 CPU to have 16 MPU regions. We weren't doing this in our emulation, so the CPU had only the default 8 MPU regions. Set the mpu-ns-regions property to 16 for this board. This bug doesn't affect any of the other board types we model in this source file, because they all use either the Cortex-M3 or Cortex-M4. Those CPUs do not have an RTL configurable number of MPU regions, and always provide 8 regions if the MPU is built in. Cc: qemu-stable@nongnu.org Reported-by: Corentin GENDRE Signed-off-by: Peter Maydell Reviewed-by: Alex Bennée Message-id: 20250605141801.1083266-1-peter.maydell@linaro.org --- hw/arm/mps2.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/arm/mps2.c b/hw/arm/mps2.c index 58efb41e6d..bd378e360b 100644 --- a/hw/arm/mps2.c +++ b/hw/arm/mps2.c @@ -224,7 +224,11 @@ static void mps2_common_init(MachineState *machine) switch (mmc->fpga_type) { case FPGA_AN385: case FPGA_AN386: + qdev_prop_set_uint32(armv7m, "num-irq", 32); + break; case FPGA_AN500: + /* The AN500 configures its Cortex-M7 with 16 MPU regions */ + qdev_prop_set_uint32(armv7m, "mpu-ns-regions", 16); qdev_prop_set_uint32(armv7m, "num-irq", 32); break; case FPGA_AN511: From 5ad2b1f443a96444cf3e7a2fbe17aae696201012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20Neusch=C3=A4fer?= Date: Fri, 13 Jun 2025 17:59:32 +0200 Subject: [PATCH 1584/2760] linux-user/arm: Fix return value of SYS_cacheflush MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although the emulated cacheflush syscall does nothing, it still needs to return zero to indicate success. Cc: qemu-stable@nongnu.org Signed-off-by: J. Neuschäfer Message-id: 20250613-cache-v1-1-ee9f4a9ba81b@gmx.net Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- linux-user/arm/cpu_loop.c | 1 + 1 file changed, 1 insertion(+) diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index e8417d0406..33f63951a9 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -363,6 +363,7 @@ void cpu_loop(CPUARMState *env) switch (n) { case ARM_NR_cacheflush: /* nop */ + env->regs[0] = 0; break; case ARM_NR_set_tls: cpu_set_tls(env, env->regs[0]); From 9761ad5f65d23f080b5a3479e52196fbce2e1506 Mon Sep 17 00:00:00 2001 From: oltolm Date: Fri, 13 Jun 2025 00:15:22 +0200 Subject: [PATCH 1585/2760] meson: fix Windows build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The build fails on Windows. Replace calls to Unix programs like ´cat´, ´sed´ and ´true´ with calls to ´python´ and wrap calls to ´os.path.relpath´ in try-except because it can fail when the two paths are on different drives. Make sure to convert the Windows paths to Unix paths to prevent warnings in generated files. Signed-off-by: oltolm Message-id: 20250612221521.1109-2-oleg.tolmatcev@gmail.com Signed-off-by: Stefan Hajnoczi --- contrib/plugins/meson.build | 2 +- plugins/meson.build | 2 +- scripts/tracetool/__init__.py | 15 ++++++++++++--- scripts/tracetool/backend/ftrace.py | 4 +--- scripts/tracetool/backend/log.py | 4 +--- scripts/tracetool/backend/syslog.py | 4 +--- tests/functional/meson.build | 2 +- tests/include/meson.build | 2 +- tests/tcg/plugins/meson.build | 2 +- trace/meson.build | 5 +++-- 10 files changed, 23 insertions(+), 19 deletions(-) diff --git a/contrib/plugins/meson.build b/contrib/plugins/meson.build index fa8a426c8b..1876bc7843 100644 --- a/contrib/plugins/meson.build +++ b/contrib/plugins/meson.build @@ -24,7 +24,7 @@ endif if t.length() > 0 alias_target('contrib-plugins', t) else - run_target('contrib-plugins', command: find_program('true')) + run_target('contrib-plugins', command: [python, '-c', '']) endif plugin_modules += t diff --git a/plugins/meson.build b/plugins/meson.build index b20edfbabc..62c991d87f 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -33,7 +33,7 @@ if host_os == 'windows' input: qemu_plugin_symbols, output: 'qemu_plugin_api.def', capture: true, - command: ['sed', '-e', '0,/^/s//EXPORTS/; s/[{};]//g', '@INPUT@']) + command: [python, '-c', 'import fileinput, re; print("EXPORTS", end=""); [print(re.sub(r"[{};]", "", line), end="") for line in fileinput.input()]', '@INPUT@']) # then use dlltool to assemble a delaylib. # The delaylib will have an "imaginary" name (qemu.exe), that is used by the diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index bc03238c0f..6dfcbf71e1 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -12,12 +12,14 @@ __email__ = "stefanha@redhat.com" +import os import re import sys import weakref +from pathlib import PurePath -import tracetool.format import tracetool.backend +import tracetool.format def error_write(*lines): @@ -36,7 +38,7 @@ def error(*lines): def out_open(filename): global out_filename, out_fobj - out_filename = filename + out_filename = posix_relpath(filename) out_fobj = open(filename, 'wt') def out(*lines, **kwargs): @@ -308,7 +310,7 @@ def build(line_str, lineno, filename): fmt = [fmt_trans, fmt] args = Arguments.build(groups["args"]) - return Event(name, props, fmt, args, lineno, filename) + return Event(name, props, fmt, args, lineno, posix_relpath(filename)) def __repr__(self): """Evaluable string representation for this object.""" @@ -447,3 +449,10 @@ def generate(events, group, format, backends, tracetool.backend.dtrace.PROBEPREFIX = probe_prefix tracetool.format.generate(events, format, backend, group) + +def posix_relpath(path, start=None): + try: + path = os.path.relpath(path, start) + except ValueError: + pass + return PurePath(path).as_posix() diff --git a/scripts/tracetool/backend/ftrace.py b/scripts/tracetool/backend/ftrace.py index baed2ae61c..5fa30ccc08 100644 --- a/scripts/tracetool/backend/ftrace.py +++ b/scripts/tracetool/backend/ftrace.py @@ -12,8 +12,6 @@ __email__ = "stefanha@redhat.com" -import os.path - from tracetool import out @@ -47,7 +45,7 @@ def generate_h(event, group): args=event.args, event_id="TRACE_" + event.name.upper(), event_lineno=event.lineno, - event_filename=os.path.relpath(event.filename), + event_filename=event.filename, fmt=event.fmt.rstrip("\n"), argnames=argnames) diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index de27b7e62e..17ba1cd90e 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -12,8 +12,6 @@ __email__ = "stefanha@redhat.com" -import os.path - from tracetool import out @@ -55,7 +53,7 @@ def generate_h(event, group): ' }', cond=cond, event_lineno=event.lineno, - event_filename=os.path.relpath(event.filename), + event_filename=event.filename, name=event.name, fmt=event.fmt.rstrip("\n"), argnames=argnames) diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py index 012970f6cc..5a3a00fe31 100644 --- a/scripts/tracetool/backend/syslog.py +++ b/scripts/tracetool/backend/syslog.py @@ -12,8 +12,6 @@ __email__ = "stefanha@redhat.com" -import os.path - from tracetool import out @@ -43,7 +41,7 @@ def generate_h(event, group): ' }', cond=cond, event_lineno=event.lineno, - event_filename=os.path.relpath(event.filename), + event_filename=event.filename, name=event.name, fmt=event.fmt.rstrip("\n"), argnames=argnames) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 7faa2b6e3c..158a473c15 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -416,4 +416,4 @@ endforeach run_target('precache-functional', depends: precache_all, - command: ['true']) + command: [python, '-c', '']) diff --git a/tests/include/meson.build b/tests/include/meson.build index 9abba308fa..8e8d1ec4e6 100644 --- a/tests/include/meson.build +++ b/tests/include/meson.build @@ -13,4 +13,4 @@ test_qapi_outputs_extra = [ test_qapi_files_extra = custom_target('QAPI test (include)', output: test_qapi_outputs_extra, input: test_qapi_files, - command: 'true') + command: [python, '-c', '']) diff --git a/tests/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build index 41f02f2c7f..029342282a 100644 --- a/tests/tcg/plugins/meson.build +++ b/tests/tcg/plugins/meson.build @@ -17,7 +17,7 @@ endif if t.length() > 0 alias_target('test-plugins', t) else - run_target('test-plugins', command: find_program('true')) + run_target('test-plugins', command: [python, '-c', '']) endif plugin_modules += t diff --git a/trace/meson.build b/trace/meson.build index 3df4549355..9c42a57a05 100644 --- a/trace/meson.build +++ b/trace/meson.build @@ -4,7 +4,7 @@ trace_events_files = [] foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events if item in qapi_trace_events trace_events_file = item - group_name = item.full_path().split('/')[-1].underscorify() + group_name = fs.name(item).underscorify() else trace_events_file = meson.project_source_root() / item / 'trace-events' group_name = item == '.' ? 'root' : item.underscorify() @@ -57,10 +57,11 @@ foreach item : [ '.' ] + trace_events_subdirs + qapi_trace_events endif endforeach +cat = [ python, '-c', 'import fileinput; [print(line, end="") for line in fileinput.input()]', '@INPUT@' ] trace_events_all = custom_target('trace-events-all', output: 'trace-events-all', input: trace_events_files, - command: [ 'cat', '@INPUT@' ], + command: cat, capture: true, install: get_option('trace_backends') != [ 'nop' ], install_dir: qemu_datadir) From 9a02932b059613f7c4bea273677811561a9237ff Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 9 Jun 2025 12:58:54 +0200 Subject: [PATCH 1586/2760] meson: cleanup win32 library detection As pointed out by Akihiko Odaki, all Win32 libraries in MinGW have lowercase names. This means that on (case-insensitive) Windows you can use the mixed-case names suggested by Microsoft or all-lowercase names, while on Linux you need to make them lowercase. QEMU was already using lowercase names, so there is no need to test the mixed-case name version of libSynchronization. Remove the unnecessary test and while at it make all the tests use "required: true". Signed-off-by: Paolo Bonzini --- meson.build | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/meson.build b/meson.build index 34729c2a3d..ed60be2a2d 100644 --- a/meson.build +++ b/meson.build @@ -843,15 +843,12 @@ host_dsosuf = '.so' if host_os == 'windows' midl = find_program('midl', required: false) widl = find_program('widl', required: false) - pathcch = cc.find_library('pathcch') - synchronization = cc.find_library('Synchronization', required: false) - if not synchronization.found() - # The library name is lowercase on mingw - synchronization = cc.find_library('synchronization', required: true) - endif - socket = cc.find_library('ws2_32') - winmm = cc.find_library('winmm') + # MinGW uses lowercase for library names + pathcch = cc.find_library('pathcch', required: true) + synchronization = cc.find_library('synchronization', required: true) + socket = cc.find_library('ws2_32', required: true) + winmm = cc.find_library('winmm', required: true) win = import('windows') version_res = win.compile_resources('version.rc', From 0f1d6606c28d0ae81a1b311972c5c54e5e867bf0 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 11 Jun 2025 14:03:15 +0100 Subject: [PATCH 1587/2760] target/i386: fix TB exit logic in gen_movl_seg() when writing to SS Before commit e54ef98c8a ("target/i386: do not trigger IRQ shadow for LSS"), any write to SS in gen_movl_seg() would cause a TB exit. The changes introduced by this commit were intended to restrict the DISAS_EOB_INHIBIT_IRQ exit to the case where inhibit_irq is true, but missed that a DISAS_EOB_NEXT exit can still be required when writing to SS and inhibit_irq is false. Comparing the PE(s) && !VM86(s) section with the logic in x86_update_hflags(), we can see that the DISAS_EOB_NEXT exit is still required for the !CODE32 case when writing to SS in gen_movl_seg() because any change to the SS flags can affect hflags. Similarly we can see that the existing CODE32 case is still correct since a change to any of DS, ES and SS can affect hflags. Finally for the gen_op_movl_seg_real() case an explicit TB exit is not needed because the segment register selector does not affect hflags. Update the logic in gen_movl_seg() so that a write to SS with inhibit_irq set to false where PE(s) && !VM86(s) will generate a DISAS_EOB_NEXT exit along with the inline comment. This has the effect of allowing Win98SE to boot in QEMU once again. Signed-off-by: Mark Cave-Ayland Fixes: e54ef98c8a ("target/i386: do not trigger IRQ shadow for LSS") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2987 Link: https://lore.kernel.org/r/20250611130315.383151-1-mark.cave-ayland@ilande.co.uk Reviewed-by: Peter Maydell Signed-off-by: Paolo Bonzini --- target/i386/tcg/translate.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/translate.c b/target/i386/tcg/translate.c index 0fcddc2ec0..0cb87d0201 100644 --- a/target/i386/tcg/translate.c +++ b/target/i386/tcg/translate.c @@ -2033,8 +2033,11 @@ static void gen_movl_seg(DisasContext *s, X86Seg seg_reg, TCGv src, bool inhibit tcg_gen_trunc_tl_i32(sel, src); gen_helper_load_seg(tcg_env, tcg_constant_i32(seg_reg), sel); - /* For move to DS/ES/SS, the addseg or ss32 flags may change. */ - if (CODE32(s) && seg_reg < R_FS) { + /* + * For moves to SS, the SS32 flag may change. For CODE32 only, changes + * to SS, DS and ES may change the ADDSEG flags. + */ + if (seg_reg == R_SS || (CODE32(s) && seg_reg < R_FS)) { s->base.is_jmp = DISAS_EOB_NEXT; } } else { From 5d353cce65142440251c014331c2f25f3af5e1b2 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Tue, 10 Jun 2025 22:41:27 +0200 Subject: [PATCH 1588/2760] hw: Fix type constant for DTB files Commit fcb1ad456c58 ("system/datadir: Add new type constant for DTB files") introduced a new type constant for DTB files and converted the boards with bundled device trees to use it. Convert the other boards for consistency. Fixes: fcb1ad456c58 ("system/datadir: Add new type constant for DTB files") Signed-off-by: Bernhard Beschow Link: https://lore.kernel.org/r/20250610204131.2862-2-shentey@gmail.com Signed-off-by: Paolo Bonzini --- hw/arm/boot.c | 2 +- hw/ppc/e500.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/arm/boot.c b/hw/arm/boot.c index 79afb51b8a..64040504a1 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -527,7 +527,7 @@ int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo, if (binfo->dtb_filename) { char *filename; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, binfo->dtb_filename); if (!filename) { fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename); goto fail; diff --git a/hw/ppc/e500.c b/hw/ppc/e500.c index 6899802bed..723c97fad2 100644 --- a/hw/ppc/e500.c +++ b/hw/ppc/e500.c @@ -408,7 +408,7 @@ static int ppce500_load_device_tree(PPCE500MachineState *pms, if (dtb_file) { char *filename; - filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, dtb_file); + filename = qemu_find_file(QEMU_FILE_TYPE_DTB, dtb_file); if (!filename) { goto out; } From 6c2888dd0f9b7129943d53cacd6a7b7143a65cfb Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Tue, 10 Jun 2025 22:41:28 +0200 Subject: [PATCH 1589/2760] pc-bios/dtb/meson: Prefer target name to be outfile, not infile Makes this custom_target() usage consistent with other ones in QEMU. Fixes: 6e0dc9d2a88a ("meson: compile bundled device trees") Signed-off-by: Bernhard Beschow Link: https://lore.kernel.org/r/20250610204131.2862-3-shentey@gmail.com Signed-off-by: Paolo Bonzini --- pc-bios/dtb/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/dtb/meson.build b/pc-bios/dtb/meson.build index 7a71835bca..993032949f 100644 --- a/pc-bios/dtb/meson.build +++ b/pc-bios/dtb/meson.build @@ -9,7 +9,7 @@ dtc = find_program('dtc', required: false) if dtc.found() foreach out : dtbs f = fs.replace_suffix(out, '.dts') - custom_target(f, + custom_target(out, build_by_default: have_system, input: files(f), output: out, From abf18324240a3c8f3feafbe5a96d4b83cd044615 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Feb 2025 09:41:42 +0100 Subject: [PATCH 1590/2760] rust: qemu_api: introduce MaybeUninit field projection Add a macro that makes it possible to convert a MaybeUninit<> into another MaybeUninit<> for a single field within it. Furthermore, it is possible to use the resulting MaybeUninitField<> in APIs that take the parent object, such as memory_region_init_io(). This allows removing some of the undefined behavior from instance_init() functions, though this may not be the definitive implementation. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/meson.build | 1 + rust/qemu-api/src/lib.rs | 1 + rust/qemu-api/src/uninit.rs | 85 +++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+) create mode 100644 rust/qemu-api/src/uninit.rs diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index cac8595a14..33653b4a28 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -28,6 +28,7 @@ _qemu_api_rs = static_library( 'src/qom.rs', 'src/sysbus.rs', 'src/timer.rs', + 'src/uninit.rs', 'src/vmstate.rs', 'src/zeroable.rs', ], diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index 93902fc94b..c78198f0f4 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -27,6 +27,7 @@ pub mod qdev; pub mod qom; pub mod sysbus; pub mod timer; +pub mod uninit; pub mod vmstate; pub mod zeroable; diff --git a/rust/qemu-api/src/uninit.rs b/rust/qemu-api/src/uninit.rs new file mode 100644 index 0000000000..04123b4ae9 --- /dev/null +++ b/rust/qemu-api/src/uninit.rs @@ -0,0 +1,85 @@ +//! Access fields of a [`MaybeUninit`] + +use std::{ + mem::MaybeUninit, + ops::{Deref, DerefMut}, +}; + +pub struct MaybeUninitField<'a, T, U> { + parent: &'a mut MaybeUninit, + child: *mut U, +} + +impl<'a, T, U> MaybeUninitField<'a, T, U> { + #[doc(hidden)] + pub fn new(parent: &'a mut MaybeUninit, child: *mut U) -> Self { + MaybeUninitField { parent, child } + } + + /// Return a constant pointer to the containing object of the field. + /// + /// Because the `MaybeUninitField` remembers the containing object, + /// it is possible to use it in foreign APIs that initialize the + /// child. + pub fn parent(f: &Self) -> *const T { + f.parent.as_ptr() + } + + /// Return a mutable pointer to the containing object. + /// + /// Because the `MaybeUninitField` remembers the containing object, + /// it is possible to use it in foreign APIs that initialize the + /// child. + pub fn parent_mut(f: &mut Self) -> *mut T { + f.parent.as_mut_ptr() + } +} + +impl<'a, T, U> Deref for MaybeUninitField<'a, T, U> { + type Target = MaybeUninit; + + fn deref(&self) -> &MaybeUninit { + // SAFETY: self.child was obtained by dereferencing a valid mutable + // reference; the content of the memory may be invalid or uninitialized + // but MaybeUninit<_> makes no assumption on it + unsafe { &*(self.child.cast()) } + } +} + +impl<'a, T, U> DerefMut for MaybeUninitField<'a, T, U> { + fn deref_mut(&mut self) -> &mut MaybeUninit { + // SAFETY: self.child was obtained by dereferencing a valid mutable + // reference; the content of the memory may be invalid or uninitialized + // but MaybeUninit<_> makes no assumption on it + unsafe { &mut *(self.child.cast()) } + } +} + +/// ``` +/// #[derive(Debug)] +/// struct S { +/// x: u32, +/// y: u32, +/// } +/// +/// # use std::mem::MaybeUninit; +/// # use qemu_api::{assert_match, uninit_field_mut}; +/// +/// let mut s: MaybeUninit = MaybeUninit::zeroed(); +/// uninit_field_mut!(s, x).write(5); +/// let s = unsafe { s.assume_init() }; +/// assert_match!(s, S { x: 5, y: 0 }); +/// ``` +#[macro_export] +macro_rules! uninit_field_mut { + ($container:expr, $($field:tt)+) => {{ + let container__: &mut ::std::mem::MaybeUninit<_> = &mut $container; + let container_ptr__ = container__.as_mut_ptr(); + + // SAFETY: the container is not used directly, only through a MaybeUninit<>, + // so the safety is delegated to the caller and to final invocation of + // assume_init() + let target__ = unsafe { std::ptr::addr_of_mut!((*container_ptr__).$($field)+) }; + $crate::uninit::MaybeUninitField::new(container__, target__) + }}; +} From eb64a0c6ae492f8ef37f31c9d4994bfc69f02e3d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 15 Apr 2025 13:13:19 +0200 Subject: [PATCH 1591/2760] rust: hpet: fully initialize object during instance_init The array of BqlRefCell is not initialized yet at the end of instance_init. In particular, the "state" field is NonNull and therefore it is invalid to have it as zero bytes. Note that MaybeUninit is necessary because assigning to self.timers[index] would trigger Drop of the old value. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/device.rs | 42 +++++++++++++++++++------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 735b2fbef7..340ca1d355 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -4,6 +4,7 @@ use std::{ ffi::{c_int, c_void, CStr}, + mem::MaybeUninit, pin::Pin, ptr::{addr_of_mut, null_mut, NonNull}, slice::from_ref, @@ -25,6 +26,7 @@ use qemu_api::{ qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, + uninit_field_mut, vmstate::VMStateDescription, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_validate, zeroable::Zeroable, @@ -212,13 +214,13 @@ pub struct HPETTimer { } impl HPETTimer { - fn init(&mut self, index: u8, state: &HPETState) { - *self = HPETTimer { + fn new(index: u8, state: *const HPETState) -> HPETTimer { + HPETTimer { index, // SAFETY: the HPETTimer will only be used after the timer // is initialized below. qemu_timer: unsafe { Timer::new() }, - state: NonNull::new((state as *const HPETState).cast_mut()).unwrap(), + state: NonNull::new(state.cast_mut()).unwrap(), config: 0, cmp: 0, fsb: 0, @@ -226,19 +228,15 @@ impl HPETTimer { period: 0, wrap_flag: 0, last: 0, - }; + } + } + fn init_timer_with_cell(cell: &BqlRefCell) { + let mut timer = cell.borrow_mut(); // SAFETY: HPETTimer is only used as part of HPETState, which is // always pinned. - let qemu_timer = unsafe { Pin::new_unchecked(&mut self.qemu_timer) }; - qemu_timer.init_full( - None, - CLOCK_VIRTUAL, - Timer::NS, - 0, - timer_handler, - &state.timers[self.index as usize], - ) + let qemu_timer = unsafe { Pin::new_unchecked(&mut timer.qemu_timer) }; + qemu_timer.init_full(None, CLOCK_VIRTUAL, Timer::NS, 0, timer_handler, cell); } fn get_state(&self) -> &HPETState { @@ -607,9 +605,18 @@ impl HPETState { } } - fn init_timer(&self) { - for (index, timer) in self.timers.iter().enumerate() { - timer.borrow_mut().init(index.try_into().unwrap(), self); + fn init_timers(this: &mut MaybeUninit) { + let state = this.as_ptr(); + for index in 0..HPET_MAX_TIMERS { + let mut timer = uninit_field_mut!(*this, timers[index]); + + // Initialize in two steps, to avoid calling Timer::init_full on a + // temporary that can be moved. + let timer = timer.write(BqlRefCell::new(HPETTimer::new( + index.try_into().unwrap(), + state, + ))); + HPETTimer::init_timer_with_cell(timer); } } @@ -710,6 +717,8 @@ impl HPETState { "hpet", HPET_REG_SPACE_LEN, ); + + Self::init_timers(unsafe { &mut *((self as *mut Self).cast::>()) }); } fn post_init(&self) { @@ -731,7 +740,6 @@ impl HPETState { self.hpet_id.set(HPETFwConfig::assign_hpet_id()?); - self.init_timer(); // 64-bit General Capabilities and ID Register; LegacyReplacementRoute. self.capability.set( HPET_CAP_REV_ID_VALUE << HPET_CAP_REV_ID_SHIFT | From a44122258328a33aaa776c21277e6d4f98ab3d1f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Feb 2025 09:40:30 +0100 Subject: [PATCH 1592/2760] rust: qom: introduce ParentInit This is a smart pointer for MaybeUninit; it can be upcasted to the already-initialized parent classes, or dereferenced to a MaybeUninit for the class that is being initialized. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qom.rs | 96 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 14f98fee60..ef966e570c 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -95,7 +95,7 @@ use std::{ ffi::{c_void, CStr}, fmt, - mem::ManuallyDrop, + mem::{ManuallyDrop, MaybeUninit}, ops::{Deref, DerefMut}, ptr::NonNull, }; @@ -206,6 +206,100 @@ impl fmt::Display for ParentField { } } +/// This struct knows that the superclasses of the object have already been +/// initialized. +pub struct ParentInit<'a, T>(&'a mut MaybeUninit); + +impl<'a, T> ParentInit<'a, T> { + #[inline] + pub fn with(obj: &'a mut MaybeUninit, f: impl FnOnce(ParentInit<'a, T>)) { + let parent_init = ParentInit(obj); + f(parent_init) + } +} + +impl ParentInit<'_, T> { + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// Fields beyond `Object` could be uninitialized and it's your + /// responsibility to avoid that they're used when the pointer is + /// dereferenced, either directly or through a cast. + pub fn as_object_mut_ptr(&self) -> *mut bindings::Object { + self.as_object_ptr().cast_mut() + } + + /// Return the receiver as a mutable raw pointer to Object. + /// + /// # Safety + /// + /// Fields beyond `Object` could be uninitialized and it's your + /// responsibility to avoid that they're used when the pointer is + /// dereferenced, either directly or through a cast. + pub fn as_object_ptr(&self) -> *const bindings::Object { + self.0.as_ptr().cast() + } +} + +impl<'a, T: ObjectImpl> ParentInit<'a, T> { + /// Convert from a derived type to one of its parent types, which + /// have already been initialized. + /// + /// # Safety + /// + /// Structurally this is always a safe operation; the [`IsA`] trait + /// provides static verification trait that `Self` dereferences to `U` or + /// a child of `U`, and only parent types of `T` are allowed. + /// + /// However, while the fields of the resulting reference are initialized, + /// calls might use uninitialized fields of the subclass. It is your + /// responsibility to avoid this. + pub unsafe fn upcast(&self) -> &'a U + where + T::ParentType: IsA, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait; + // the parent has been initialized before `instance_init `is called + unsafe { &*(self.0.as_ptr().cast::()) } + } + + /// Convert from a derived type to one of its parent types, which + /// have already been initialized. + /// + /// # Safety + /// + /// Structurally this is always a safe operation; the [`IsA`] trait + /// provides static verification trait that `Self` dereferences to `U` or + /// a child of `U`, and only parent types of `T` are allowed. + /// + /// However, while the fields of the resulting reference are initialized, + /// calls might use uninitialized fields of the subclass. It is your + /// responsibility to avoid this. + pub unsafe fn upcast_mut(&mut self) -> &'a mut U + where + T::ParentType: IsA, + { + // SAFETY: soundness is declared via IsA, which is an unsafe trait; + // the parent has been initialized before `instance_init `is called + unsafe { &mut *(self.0.as_mut_ptr().cast::()) } + } +} + +impl Deref for ParentInit<'_, T> { + type Target = MaybeUninit; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl DerefMut for ParentInit<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + unsafe extern "C" fn rust_instance_init(obj: *mut bindings::Object) { let mut state = NonNull::new(obj).unwrap().cast::(); // SAFETY: obj is an instance of T, since rust_instance_init From 8d394f6cf0b50a82758b651e81a18dac13e70e7d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 28 Feb 2025 10:20:48 +0100 Subject: [PATCH 1593/2760] rust: qom: make ParentInit lifetime-invariant This is the trick that allows the parent-field initializer to be used only for the object that it's meant to be initialized. This way, the owner of a MemoryRegion must be the object that embeds it. More information is in the comments; it's best explained with a simplified example. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/qom.rs | 89 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 4 deletions(-) diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index ef966e570c..04d102591d 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -95,6 +95,7 @@ use std::{ ffi::{c_void, CStr}, fmt, + marker::PhantomData, mem::{ManuallyDrop, MaybeUninit}, ops::{Deref, DerefMut}, ptr::NonNull, @@ -208,12 +209,92 @@ impl fmt::Display for ParentField { /// This struct knows that the superclasses of the object have already been /// initialized. -pub struct ParentInit<'a, T>(&'a mut MaybeUninit); +/// +/// The declaration of `ParentInit` is.. *"a kind of magic"*. It uses a +/// technique that is found in several crates, the main ones probably being +/// `ghost-cell` (in fact it was introduced by the [`GhostCell` paper](https://plv.mpi-sws.org/rustbelt/ghostcell/)) +/// and `generativity`. +/// +/// The `PhantomData` makes the `ParentInit` type *invariant* with respect to +/// the lifetime argument `'init`. This, together with the `for<'...>` in +/// `[ParentInit::with]`, block any attempt of the compiler to be creative when +/// operating on types of type `ParentInit` and to extend their lifetimes. In +/// particular, it ensures that the `ParentInit` cannot be made to outlive the +/// `rust_instance_init()` function that creates it, and therefore that the +/// `&'init T` reference is valid. +/// +/// This implementation of the same concept, without the QOM baggage, can help +/// understanding the effect: +/// +/// ``` +/// use std::marker::PhantomData; +/// +/// #[derive(PartialEq, Eq)] +/// pub struct Jail<'closure, T: Copy>(&'closure T, PhantomData &'closure ()>); +/// +/// impl<'closure, T: Copy> Jail<'closure, T> { +/// fn get(&self) -> T { +/// *self.0 +/// } +/// +/// #[inline] +/// fn with(v: T, f: impl for<'id> FnOnce(Jail<'id, T>) -> U) -> U { +/// let parent_init = Jail(&v, PhantomData); +/// f(parent_init) +/// } +/// } +/// ``` +/// +/// It's impossible to escape the `Jail`; `token1` cannot be moved out of the +/// closure: +/// +/// ```ignore +/// let x = 42; +/// let escape = Jail::with(&x, |token1| { +/// println!("{}", token1.get()); +/// // fails to compile... +/// token1 +/// }); +/// // ... so you cannot do this: +/// println!("{}", escape.get()); +/// ``` +/// +/// Likewise, in the QOM case the `ParentInit` cannot be moved out of +/// `instance_init()`. Without this trick it would be possible to stash a +/// `ParentInit` and use it later to access uninitialized memory. +/// +/// Here is another example, showing how separately-created "identities" stay +/// isolated: +/// +/// ```ignore +/// impl<'closure, T: Copy> Clone for Jail<'closure, T> { +/// fn clone(&self) -> Jail<'closure, T> { +/// Jail(self.0, PhantomData) +/// } +/// } +/// +/// fn main() { +/// Jail::with(42, |token1| { +/// // this works and returns true: the clone has the same "identity" +/// println!("{}", token1 == token1.clone()); +/// Jail::with(42, |token2| { +/// // here the outer token remains accessible... +/// println!("{}", token1.get()); +/// // ... but the two are separate: this fails to compile: +/// println!("{}", token1 == token2); +/// }); +/// }); +/// } +/// ``` +pub struct ParentInit<'init, T>( + &'init mut MaybeUninit, + PhantomData &'init ()>, +); -impl<'a, T> ParentInit<'a, T> { +impl<'init, T> ParentInit<'init, T> { #[inline] - pub fn with(obj: &'a mut MaybeUninit, f: impl FnOnce(ParentInit<'a, T>)) { - let parent_init = ParentInit(obj); + pub fn with(obj: &'init mut MaybeUninit, f: impl for<'id> FnOnce(ParentInit<'id, T>)) { + let parent_init = ParentInit(obj, PhantomData); f(parent_init) } } From 345bef46a1b6765185bfe1450cc147f5feb5d0e7 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 4 Mar 2025 20:48:05 +0100 Subject: [PATCH 1594/2760] rust: qom: change instance_init to take a ParentInit<> This removes undefined behavior associated to writing to uninitialized fields, and makes it possible to remove "unsafe" from the instance_init implementation. However, the init function itself is still unsafe, because it must promise (as a sort as MaybeUninit::assume_init) that all fields have been initialized. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 34 ++++++++++------------ rust/hw/timer/hpet/src/device.rs | 16 ++++------- rust/qemu-api/src/memory.rs | 12 ++++---- rust/qemu-api/src/qdev.rs | 49 +++++++++++++++++++------------- rust/qemu-api/src/qom.rs | 9 ++++-- 5 files changed, 63 insertions(+), 57 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index be8387f6f2..2d416cd9a3 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,7 +2,7 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ffi::CStr, mem::size_of, ptr::addr_of_mut}; +use std::{ffi::CStr, mem::size_of}; use qemu_api::{ chardev::{CharBackend, Chardev, Event}, @@ -11,9 +11,10 @@ use qemu_api::{ memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, - qom::{ObjectImpl, Owned, ParentField}, + qom::{ObjectImpl, Owned, ParentField, ParentInit}, static_assert, sysbus::{SysBusDevice, SysBusDeviceImpl}, + uninit_field_mut, vmstate::VMStateDescription, }; @@ -163,7 +164,7 @@ impl PL011Impl for PL011State { impl ObjectImpl for PL011State { type ParentType = SysBusDevice; - const INSTANCE_INIT: Option = Some(Self::init); + const INSTANCE_INIT: Option)> = Some(Self::init); const INSTANCE_POST_INIT: Option = Some(Self::post_init); const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::; } @@ -488,7 +489,7 @@ impl PL011State { /// `PL011State` type. It must not be called more than once on the same /// location/instance. All its fields are expected to hold uninitialized /// values with the sole exception of `parent_obj`. - unsafe fn init(&mut self) { + unsafe fn init(mut this: ParentInit) { static PL011_OPS: MemoryRegionOps = MemoryRegionOpsBuilder::::new() .read(&PL011State::read) .write(&PL011State::write) @@ -496,28 +497,23 @@ impl PL011State { .impl_sizes(4, 4) .build(); - // SAFETY: - // - // self and self.iomem are guaranteed to be valid at this point since callers - // must make sure the `self` reference is valid. + // SAFETY: this and this.iomem are guaranteed to be valid at this point MemoryRegion::init_io( - unsafe { &mut *addr_of_mut!(self.iomem) }, - addr_of_mut!(*self), + &mut uninit_field_mut!(*this, iomem), &PL011_OPS, "pl011", 0x1000, ); - self.regs = Default::default(); + uninit_field_mut!(*this, regs).write(Default::default()); - // SAFETY: - // - // self.clock is not initialized at this point; but since `Owned<_>` is - // not Drop, we can overwrite the undefined value without side effects; - // it's not sound but, because for all PL011State instances are created - // by QOM code which calls this function to initialize the fields, at - // leastno code is able to access an invalid self.clock value. - self.clock = self.init_clock_in("clk", &Self::clock_update, ClockEvent::ClockUpdate); + let clock = DeviceState::init_clock_in( + &mut this, + "clk", + &Self::clock_update, + ClockEvent::ClockUpdate, + ); + uninit_field_mut!(*this, clock).write(clock); } const fn clock_update(&self, _event: ClockEvent) { diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index 340ca1d355..a281927781 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -21,8 +21,8 @@ use qemu_api::{ hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder, MEMTXATTRS_UNSPECIFIED, }, prelude::*, - qdev::{DeviceImpl, DeviceMethods, DeviceState, Property, ResetType, ResettablePhasesImpl}, - qom::{ObjectImpl, ObjectType, ParentField}, + qdev::{DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, + qom::{ObjectImpl, ObjectType, ParentField, ParentInit}, qom_isa, sysbus::{SysBusDevice, SysBusDeviceImpl}, timer::{Timer, CLOCK_VIRTUAL, NANOSECONDS_PER_SECOND}, @@ -697,7 +697,7 @@ impl HPETState { .set(self.counter.get().deposit(shift, len, val)); } - unsafe fn init(&mut self) { + unsafe fn init(mut this: ParentInit) { static HPET_RAM_OPS: MemoryRegionOps = MemoryRegionOpsBuilder::::new() .read(&HPETState::read) @@ -707,18 +707,14 @@ impl HPETState { .impl_sizes(4, 8) .build(); - // SAFETY: - // self and self.iomem are guaranteed to be valid at this point since callers - // must make sure the `self` reference is valid. MemoryRegion::init_io( - unsafe { &mut *addr_of_mut!(self.iomem) }, - addr_of_mut!(*self), + &mut uninit_field_mut!(*this, iomem), &HPET_RAM_OPS, "hpet", HPET_REG_SPACE_LEN, ); - Self::init_timers(unsafe { &mut *((self as *mut Self).cast::>()) }); + Self::init_timers(&mut this); } fn post_init(&self) { @@ -900,7 +896,7 @@ unsafe impl ObjectType for HPETState { impl ObjectImpl for HPETState { type ParentType = SysBusDevice; - const INSTANCE_INIT: Option = Some(Self::init); + const INSTANCE_INIT: Option)> = Some(Self::init); const INSTANCE_POST_INIT: Option = Some(Self::post_init); const CLASS_INIT: fn(&mut Self::Class) = Self::Class::class_init::; } diff --git a/rust/qemu-api/src/memory.rs b/rust/qemu-api/src/memory.rs index 9ef2694bd6..e40fad6cf1 100644 --- a/rust/qemu-api/src/memory.rs +++ b/rust/qemu-api/src/memory.rs @@ -16,6 +16,7 @@ use crate::{ callbacks::FnCall, cell::Opaque, prelude::*, + uninit::MaybeUninitField, zeroable::Zeroable, }; @@ -147,7 +148,7 @@ impl MemoryRegion { #[inline(always)] unsafe fn do_init_io( slot: *mut bindings::MemoryRegion, - owner: *mut Object, + owner: *mut bindings::Object, ops: &'static bindings::MemoryRegionOps, name: &'static str, size: u64, @@ -156,7 +157,7 @@ impl MemoryRegion { let cstr = CString::new(name).unwrap(); memory_region_init_io( slot, - owner.cast::(), + owner, ops, owner.cast::(), cstr.as_ptr(), @@ -166,16 +167,15 @@ impl MemoryRegion { } pub fn init_io>( - &mut self, - owner: *mut T, + this: &mut MaybeUninitField<'_, T, Self>, ops: &'static MemoryRegionOps, name: &'static str, size: u64, ) { unsafe { Self::do_init_io( - self.0.as_mut_ptr(), - owner.cast::(), + this.as_mut_ptr().cast(), + MaybeUninitField::parent_mut(this).cast(), &ops.0, name, size, diff --git a/rust/qemu-api/src/qdev.rs b/rust/qemu-api/src/qdev.rs index 0610959f46..36f02fb57d 100644 --- a/rust/qemu-api/src/qdev.rs +++ b/rust/qemu-api/src/qdev.rs @@ -19,7 +19,7 @@ use crate::{ error::{Error, Result}, irq::InterruptSource, prelude::*, - qom::{ObjectClass, ObjectImpl, Owned}, + qom::{ObjectClass, ObjectImpl, Owned, ParentInit}, vmstate::VMStateDescription, }; @@ -247,15 +247,9 @@ unsafe impl ObjectType for DeviceState { } qom_isa!(DeviceState: Object); -/// Trait for methods exposed by the [`DeviceState`] class. The methods can be -/// called on all objects that have the trait `IsA`. -/// -/// The trait should only be used through the blanket implementation, -/// which guarantees safety via `IsA`. -pub trait DeviceMethods: ObjectDeref -where - Self::Target: IsA, -{ +/// Initialization methods take a [`ParentInit`] and can be called as +/// associated functions. +impl DeviceState { /// Add an input clock named `name`. Invoke the callback with /// `self` as the first parameter for the events that are requested. /// @@ -266,12 +260,15 @@ where /// which Rust code has a reference to a child object) it would be /// possible for this function to return a `&Clock` too. #[inline] - fn init_clock_in FnCall<(&'a Self::Target, ClockEvent)>>( - &self, + pub fn init_clock_in FnCall<(&'a T, ClockEvent)>>( + this: &mut ParentInit, name: &str, _cb: &F, events: ClockEvent, - ) -> Owned { + ) -> Owned + where + T::ParentType: IsA, + { fn do_init_clock_in( dev: &DeviceState, name: &str, @@ -287,10 +284,10 @@ where unsafe { let cstr = CString::new(name).unwrap(); let clk = bindings::qdev_init_clock_in( - dev.as_mut_ptr(), + dev.0.as_mut_ptr(), cstr.as_ptr(), cb, - dev.as_void_ptr(), + dev.0.as_void_ptr(), events.0, ); @@ -307,12 +304,12 @@ where // SAFETY: the opaque is "this", which is indeed a pointer to T F::call((unsafe { &*(opaque.cast::()) }, event)) } - Some(rust_clock_cb::) + Some(rust_clock_cb::) } else { None }; - do_init_clock_in(self.upcast(), name, cb, events) + do_init_clock_in(unsafe { this.upcast_mut() }, name, cb, events) } /// Add an output clock named `name`. @@ -324,16 +321,30 @@ where /// which Rust code has a reference to a child object) it would be /// possible for this function to return a `&Clock` too. #[inline] - fn init_clock_out(&self, name: &str) -> Owned { + pub fn init_clock_out(this: &mut ParentInit, name: &str) -> Owned + where + T::ParentType: IsA, + { unsafe { let cstr = CString::new(name).unwrap(); - let clk = bindings::qdev_init_clock_out(self.upcast().as_mut_ptr(), cstr.as_ptr()); + let dev: &mut DeviceState = this.upcast_mut(); + let clk = bindings::qdev_init_clock_out(dev.0.as_mut_ptr(), cstr.as_ptr()); let clk: &Clock = Clock::from_raw(clk); Owned::from(clk) } } +} +/// Trait for methods exposed by the [`DeviceState`] class. The methods can be +/// called on all objects that have the trait `IsA`. +/// +/// The trait should only be used through the blanket implementation, +/// which guarantees safety via `IsA`. +pub trait DeviceMethods: ObjectDeref +where + Self::Target: IsA, +{ fn prop_set_chr(&self, propname: &str, chr: &Owned) { assert!(bql_locked()); let c_propname = CString::new(propname).unwrap(); diff --git a/rust/qemu-api/src/qom.rs b/rust/qemu-api/src/qom.rs index 04d102591d..e20ee014cb 100644 --- a/rust/qemu-api/src/qom.rs +++ b/rust/qemu-api/src/qom.rs @@ -382,12 +382,15 @@ impl DerefMut for ParentInit<'_, T> { } unsafe extern "C" fn rust_instance_init(obj: *mut bindings::Object) { - let mut state = NonNull::new(obj).unwrap().cast::(); + let mut state = NonNull::new(obj).unwrap().cast::>(); + // SAFETY: obj is an instance of T, since rust_instance_init // is called from QOM core as the instance_init function // for class T unsafe { - T::INSTANCE_INIT.unwrap()(state.as_mut()); + ParentInit::with(state.as_mut(), |parent_init| { + T::INSTANCE_INIT.unwrap()(parent_init); + }); } } @@ -654,7 +657,7 @@ pub trait ObjectImpl: ObjectType + IsA { /// /// FIXME: The argument is not really a valid reference. `&mut /// MaybeUninit` would be a better description. - const INSTANCE_INIT: Option = None; + const INSTANCE_INIT: Option)> = None; /// Function that is called to finish initialization of an object, once /// `INSTANCE_INIT` functions have been called. From 8bf8814ab1aba0243127bcada19414dddbfe9e51 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:30:21 +0800 Subject: [PATCH 1595/2760] hw/intc/loongarch_extioi: Add kernel irqchip realize function Function kvm_extioi_realize() is added if kvm_irqchip_in_kernel is set. It is to create and initialize ExtIOI device in kernel mode. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063033.2557365-2-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_extioi.c | 31 ++++++++++++-------- hw/intc/loongarch_extioi_kvm.c | 46 ++++++++++++++++++++++++++++++ hw/intc/meson.build | 2 ++ include/hw/intc/loongarch_extioi.h | 3 ++ 4 files changed, 70 insertions(+), 12 deletions(-) create mode 100644 hw/intc/loongarch_extioi_kvm.c diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index 7c38c4c9b7..837f649d6c 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -12,6 +12,7 @@ #include "hw/irq.h" #include "hw/loongarch/virt.h" #include "system/address-spaces.h" +#include "system/kvm.h" #include "hw/intc/loongarch_extioi.h" #include "trace.h" @@ -351,23 +352,29 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp) return; } - for (i = 0; i < EXTIOI_IRQS; i++) { - sysbus_init_irq(sbd, &s->irq[i]); - } - - qdev_init_gpio_in(dev, extioi_setirq, EXTIOI_IRQS); - memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops, - s, "extioi_system_mem", 0x900); - sysbus_init_mmio(sbd, &s->extioi_system_mem); - if (s->features & BIT(EXTIOI_HAS_VIRT_EXTENSION)) { - memory_region_init_io(&s->virt_extend, OBJECT(s), &extioi_virt_ops, - s, "extioi_virt", EXTIOI_VIRT_SIZE); - sysbus_init_mmio(sbd, &s->virt_extend); s->features |= EXTIOI_VIRT_HAS_FEATURES; } else { s->status |= BIT(EXTIOI_ENABLE); } + + if (kvm_irqchip_in_kernel()) { + kvm_extioi_realize(dev, errp); + } else { + for (i = 0; i < EXTIOI_IRQS; i++) { + sysbus_init_irq(sbd, &s->irq[i]); + } + + qdev_init_gpio_in(dev, extioi_setirq, EXTIOI_IRQS); + memory_region_init_io(&s->extioi_system_mem, OBJECT(s), &extioi_ops, + s, "extioi_system_mem", 0x900); + sysbus_init_mmio(sbd, &s->extioi_system_mem); + if (s->features & BIT(EXTIOI_HAS_VIRT_EXTENSION)) { + memory_region_init_io(&s->virt_extend, OBJECT(s), &extioi_virt_ops, + s, "extioi_virt", EXTIOI_VIRT_SIZE); + sysbus_init_mmio(sbd, &s->virt_extend); + } + } } static void loongarch_extioi_unrealize(DeviceState *dev) diff --git a/hw/intc/loongarch_extioi_kvm.c b/hw/intc/loongarch_extioi_kvm.c new file mode 100644 index 0000000000..e6d5dd379a --- /dev/null +++ b/hw/intc/loongarch_extioi_kvm.c @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch EXTIOI interrupt kvm support + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qemu/typedefs.h" +#include "hw/intc/loongarch_extioi.h" +#include "linux/kvm.h" +#include "qapi/error.h" +#include "system/kvm.h" + +void kvm_extioi_realize(DeviceState *dev, Error **errp) +{ + LoongArchExtIOICommonState *lecs = LOONGARCH_EXTIOI_COMMON(dev); + LoongArchExtIOIState *les = LOONGARCH_EXTIOI(dev); + int ret; + + ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_LOONGARCH_EIOINTC, false); + if (ret < 0) { + fprintf(stderr, "create KVM_LOONGARCH_EIOINTC failed: %s\n", + strerror(-ret)); + abort(); + } + + les->dev_fd = ret; + ret = kvm_device_access(les->dev_fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, + KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_NUM_CPU, + &lecs->num_cpu, true, NULL); + if (ret < 0) { + fprintf(stderr, "KVM_LOONGARCH_EXTIOI_INIT_NUM_CPU failed: %s\n", + strerror(-ret)); + abort(); + } + + ret = kvm_device_access(les->dev_fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, + KVM_DEV_LOONGARCH_EXTIOI_CTRL_INIT_FEATURE, + &lecs->features, true, NULL); + if (ret < 0) { + fprintf(stderr, "KVM_LOONGARCH_EXTIOI_INIT_FEATURE failed: %s\n", + strerror(-ret)); + abort(); + } +} diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 602da304b0..70e7548c52 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -74,3 +74,5 @@ specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c', 'loongarch_pic_common.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c', 'loongarch_extioi_common.c')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_LOONGARCH_EXTIOI'], + if_true: files('loongarch_extioi_kvm.c')) diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index 4a6ae903e9..69565e14ab 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -15,6 +15,7 @@ OBJECT_DECLARE_TYPE(LoongArchExtIOIState, LoongArchExtIOIClass, LOONGARCH_EXTIOI struct LoongArchExtIOIState { LoongArchExtIOICommonState parent_obj; + int dev_fd; }; struct LoongArchExtIOIClass { @@ -25,4 +26,6 @@ struct LoongArchExtIOIClass { ResettablePhases parent_phases; }; +void kvm_extioi_realize(DeviceState *dev, Error **errp); + #endif /* LOONGARCH_EXTIOI_H */ From 228c5413fb50ab43644689f4959c3ef1ef7571ea Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:30:22 +0800 Subject: [PATCH 1596/2760] hw/intc/loongarch_extioi: Add kernel irqchip save and restore function Add save and store funtction if kvm_irqchip_in_kernel() return true, it is to get and set ExtIOI irqchip state from KVM kernel. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063033.2557365-3-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_extioi.c | 14 +++++ hw/intc/loongarch_extioi_kvm.c | 90 ++++++++++++++++++++++++++++++ include/hw/intc/loongarch_extioi.h | 2 + 3 files changed, 106 insertions(+) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index 837f649d6c..7be0685f36 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -393,11 +393,24 @@ static void loongarch_extioi_reset_hold(Object *obj, ResetType type) } } +static int vmstate_extioi_pre_save(void *opaque) +{ + if (kvm_irqchip_in_kernel()) { + return kvm_extioi_get(opaque); + } + + return 0; +} + static int vmstate_extioi_post_load(void *opaque, int version_id) { LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(opaque); int i, start_irq; + if (kvm_irqchip_in_kernel()) { + return kvm_extioi_put(opaque, version_id); + } + for (i = 0; i < (EXTIOI_IRQS / 4); i++) { start_irq = i * 4; extioi_update_sw_coremap(s, start_irq, s->coremap[i], false); @@ -423,6 +436,7 @@ static void loongarch_extioi_class_init(ObjectClass *klass, const void *data) &lec->parent_unrealize); resettable_class_set_parent_phases(rc, NULL, loongarch_extioi_reset_hold, NULL, &lec->parent_phases); + lecc->pre_save = vmstate_extioi_pre_save; lecc->post_load = vmstate_extioi_post_load; } diff --git a/hw/intc/loongarch_extioi_kvm.c b/hw/intc/loongarch_extioi_kvm.c index e6d5dd379a..f4c618ca4c 100644 --- a/hw/intc/loongarch_extioi_kvm.c +++ b/hw/intc/loongarch_extioi_kvm.c @@ -12,6 +12,96 @@ #include "qapi/error.h" #include "system/kvm.h" +static void kvm_extioi_access_reg(int fd, uint64_t addr, void *val, bool write) +{ + kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_REGS, + addr, val, write, &error_abort); +} + +static void kvm_extioi_access_sw_state(int fd, uint64_t addr, + void *val, bool write) +{ + kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_SW_STATUS, + addr, val, write, &error_abort); +} + +static void kvm_extioi_access_sw_status(void *opaque, bool write) +{ + LoongArchExtIOICommonState *lecs = LOONGARCH_EXTIOI_COMMON(opaque); + LoongArchExtIOIState *les = LOONGARCH_EXTIOI(opaque); + int addr; + + addr = KVM_DEV_LOONGARCH_EXTIOI_SW_STATUS_STATE; + kvm_extioi_access_sw_state(les->dev_fd, addr, &lecs->status, write); +} + +static void kvm_extioi_access_regs(void *opaque, bool write) +{ + LoongArchExtIOICommonState *lecs = LOONGARCH_EXTIOI_COMMON(opaque); + LoongArchExtIOIState *les = LOONGARCH_EXTIOI(opaque); + int fd = les->dev_fd; + int addr, offset, cpu; + + for (addr = EXTIOI_NODETYPE_START; addr < EXTIOI_NODETYPE_END; addr += 4) { + offset = (addr - EXTIOI_NODETYPE_START) / 4; + kvm_extioi_access_reg(fd, addr, &lecs->nodetype[offset], write); + } + + for (addr = EXTIOI_IPMAP_START; addr < EXTIOI_IPMAP_END; addr += 4) { + offset = (addr - EXTIOI_IPMAP_START) / 4; + kvm_extioi_access_reg(fd, addr, &lecs->ipmap[offset], write); + } + + for (addr = EXTIOI_ENABLE_START; addr < EXTIOI_ENABLE_END; addr += 4) { + offset = (addr - EXTIOI_ENABLE_START) / 4; + kvm_extioi_access_reg(fd, addr, &lecs->enable[offset], write); + } + + for (addr = EXTIOI_BOUNCE_START; addr < EXTIOI_BOUNCE_END; addr += 4) { + offset = (addr - EXTIOI_BOUNCE_START) / 4; + kvm_extioi_access_reg(fd, addr, &lecs->bounce[offset], write); + } + + for (addr = EXTIOI_ISR_START; addr < EXTIOI_ISR_END; addr += 4) { + offset = (addr - EXTIOI_ISR_START) / 4; + kvm_extioi_access_reg(fd, addr, &lecs->isr[offset], write); + } + + for (addr = EXTIOI_COREMAP_START; addr < EXTIOI_COREMAP_END; addr += 4) { + offset = (addr - EXTIOI_COREMAP_START) / 4; + kvm_extioi_access_reg(fd, addr, &lecs->coremap[offset], write); + } + + for (cpu = 0; cpu < lecs->num_cpu; cpu++) { + for (addr = EXTIOI_COREISR_START; + addr < EXTIOI_COREISR_END; addr += 4) { + offset = (addr - EXTIOI_COREISR_START) / 4; + kvm_extioi_access_reg(fd, (cpu << 16) | addr, + &lecs->cpu[cpu].coreisr[offset], write); + } + } +} + +int kvm_extioi_get(void *opaque) +{ + kvm_extioi_access_regs(opaque, false); + kvm_extioi_access_sw_status(opaque, false); + return 0; +} + +int kvm_extioi_put(void *opaque, int version_id) +{ + LoongArchExtIOIState *les = LOONGARCH_EXTIOI(opaque); + int fd = les->dev_fd; + + kvm_extioi_access_regs(opaque, true); + kvm_extioi_access_sw_status(opaque, true); + kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, + KVM_DEV_LOONGARCH_EXTIOI_CTRL_LOAD_FINISHED, + NULL, true, &error_abort); + return 0; +} + void kvm_extioi_realize(DeviceState *dev, Error **errp) { LoongArchExtIOICommonState *lecs = LOONGARCH_EXTIOI_COMMON(dev); diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index 69565e14ab..9be1d736ea 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -27,5 +27,7 @@ struct LoongArchExtIOIClass { }; void kvm_extioi_realize(DeviceState *dev, Error **errp); +int kvm_extioi_get(void *opaque); +int kvm_extioi_put(void *opaque, int version_id); #endif /* LOONGARCH_EXTIOI_H */ From 412f655566bfadfe85d6f52a7e4420b418f261c3 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:30:23 +0800 Subject: [PATCH 1597/2760] hw/intc/loongarch_ipi: Add kernel irqchip realize function Function kvm_ipi_realize() is added if kvm_irqchip_in_kernel() return true. It is to create and initialize IPI device in kernel mode. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063033.2557365-4-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_ipi.c | 5 +++++ hw/intc/loongarch_ipi_kvm.c | 27 +++++++++++++++++++++++++++ hw/intc/loongson_ipi_common.c | 5 +++++ hw/intc/meson.build | 2 ++ include/hw/intc/loongarch_ipi.h | 3 +++ 5 files changed, 42 insertions(+) create mode 100644 hw/intc/loongarch_ipi_kvm.c diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 74372a2039..159ba91fb6 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -11,6 +11,7 @@ #include "qapi/error.h" #include "hw/intc/loongarch_ipi.h" #include "hw/qdev-properties.h" +#include "system/kvm.h" #include "target/loongarch/cpu.h" static AddressSpace *get_iocsr_as(CPUState *cpu) @@ -91,6 +92,10 @@ static void loongarch_ipi_realize(DeviceState *dev, Error **errp) lics->cpu[i].ipi = lics; qdev_init_gpio_out(dev, &lics->cpu[i].irq, 1); } + + if (kvm_irqchip_in_kernel()) { + kvm_ipi_realize(dev, errp); + } } static void loongarch_ipi_reset_hold(Object *obj, ResetType type) diff --git a/hw/intc/loongarch_ipi_kvm.c b/hw/intc/loongarch_ipi_kvm.c new file mode 100644 index 0000000000..51e9c7ed1e --- /dev/null +++ b/hw/intc/loongarch_ipi_kvm.c @@ -0,0 +1,27 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch IPI interrupt KVM support + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/intc/loongarch_ipi.h" +#include "system/kvm.h" +#include "target/loongarch/cpu.h" + +void kvm_ipi_realize(DeviceState *dev, Error **errp) +{ + LoongarchIPIState *lis = LOONGARCH_IPI(dev); + int ret; + + ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_LOONGARCH_IPI, false); + if (ret < 0) { + fprintf(stderr, "IPI KVM_CREATE_DEVICE failed: %s\n", + strerror(-ret)); + abort(); + } + + lis->dev_fd = ret; +} diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c index f32661c40f..ff2cc8bc91 100644 --- a/hw/intc/loongson_ipi_common.c +++ b/hw/intc/loongson_ipi_common.c @@ -11,6 +11,7 @@ #include "hw/irq.h" #include "qemu/log.h" #include "migration/vmstate.h" +#include "system/kvm.h" #include "trace.h" MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data, @@ -255,6 +256,10 @@ static void loongson_ipi_common_realize(DeviceState *dev, Error **errp) LoongsonIPICommonState *s = LOONGSON_IPI_COMMON(dev); SysBusDevice *sbd = SYS_BUS_DEVICE(dev); + if (kvm_irqchip_in_kernel()) { + return; + } + memory_region_init_io(&s->ipi_iocsr_mem, OBJECT(dev), &loongson_ipi_iocsr_ops, s, "loongson_ipi_iocsr", 0x48); diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 70e7548c52..1cc999771d 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -71,6 +71,8 @@ specific_ss.add(when: 'CONFIG_M68K_IRQC', if_true: files('m68k_irqc.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI_COMMON', if_true: files('loongson_ipi_common.c')) specific_ss.add(when: 'CONFIG_LOONGSON_IPI', if_true: files('loongson_ipi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_LOONGARCH_IPI'], + if_true: files('loongarch_ipi_kvm.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c', 'loongarch_pic_common.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c', 'loongarch_extioi_common.c')) diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h index a7c6bf85d3..608cd09a78 100644 --- a/include/hw/intc/loongarch_ipi.h +++ b/include/hw/intc/loongarch_ipi.h @@ -16,6 +16,7 @@ OBJECT_DECLARE_TYPE(LoongarchIPIState, LoongarchIPIClass, LOONGARCH_IPI) struct LoongarchIPIState { LoongsonIPICommonState parent_obj; + int dev_fd; }; struct LoongarchIPIClass { @@ -24,4 +25,6 @@ struct LoongarchIPIClass { ResettablePhases parent_phases; }; +void kvm_ipi_realize(DeviceState *dev, Error **errp); + #endif From 14be318c952ce2c29f2a69204a23c0008f779a3f Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:30:24 +0800 Subject: [PATCH 1598/2760] hw/intc/loongson_ipi: Add load and save interface with ipi_common class Add pre_save and post_load interfaces with ipi_common class, here only framework ipi_common adds these interfaces. The defailed implementation is LoongArchIPI child device in later. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063033.2557365-5-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongson_ipi_common.c | 28 +++++++++++++++++++++++++++ include/hw/intc/loongson_ipi_common.h | 2 ++ 2 files changed, 30 insertions(+) diff --git a/hw/intc/loongson_ipi_common.c b/hw/intc/loongson_ipi_common.c index ff2cc8bc91..8cd78d4858 100644 --- a/hw/intc/loongson_ipi_common.c +++ b/hw/intc/loongson_ipi_common.c @@ -282,10 +282,38 @@ static void loongson_ipi_common_unrealize(DeviceState *dev) g_free(s->cpu); } +static int loongson_ipi_common_pre_save(void *opaque) +{ + IPICore *ipicore = (IPICore *)opaque; + LoongsonIPICommonState *s = ipicore->ipi; + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(s); + + if (licc->pre_save) { + return licc->pre_save(s); + } + + return 0; +} + +static int loongson_ipi_common_post_load(void *opaque, int version_id) +{ + IPICore *ipicore = (IPICore *)opaque; + LoongsonIPICommonState *s = ipicore->ipi; + LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_GET_CLASS(s); + + if (licc->post_load) { + return licc->post_load(s, version_id); + } + + return 0; +} + static const VMStateDescription vmstate_ipi_core = { .name = "ipi-single", .version_id = 2, .minimum_version_id = 2, + .pre_save = loongson_ipi_common_pre_save, + .post_load = loongson_ipi_common_post_load, .fields = (const VMStateField[]) { VMSTATE_UINT32(status, IPICore), VMSTATE_UINT32(en, IPICore), diff --git a/include/hw/intc/loongson_ipi_common.h b/include/hw/intc/loongson_ipi_common.h index b587f9c571..e58ce2aa1c 100644 --- a/include/hw/intc/loongson_ipi_common.h +++ b/include/hw/intc/loongson_ipi_common.h @@ -48,6 +48,8 @@ struct LoongsonIPICommonClass { AddressSpace *(*get_iocsr_as)(CPUState *cpu); int (*cpu_by_arch_id)(LoongsonIPICommonState *lics, int64_t id, int *index, CPUState **pcs); + int (*pre_save)(void *opaque); + int (*post_load)(void *opaque, int version_id); }; MemTxResult loongson_ipi_core_readl(void *opaque, hwaddr addr, uint64_t *data, From f936caa315750406f50feb1ac9c93770b47ebe96 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:30:25 +0800 Subject: [PATCH 1599/2760] hw/intc/loongarch_ipi: Add kernel irqchip save and restore function Add save and store funtction if kvm_irqchip_in_kernel() return true, it is to get and set IPI irqchip state from KVM kernel. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063033.2557365-6-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_ipi.c | 20 ++++++++++++ hw/intc/loongarch_ipi_kvm.c | 54 +++++++++++++++++++++++++++++++++ include/hw/intc/loongarch_ipi.h | 2 ++ 3 files changed, 76 insertions(+) diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 159ba91fb6..0ea91ea054 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -171,6 +171,24 @@ static void loongarch_ipi_cpu_unplug(HotplugHandler *hotplug_dev, core->cpu = NULL; } +static int loongarch_ipi_pre_save(void *opaque) +{ + if (kvm_irqchip_in_kernel()) { + return kvm_ipi_get(opaque); + } + + return 0; +} + +static int loongarch_ipi_post_load(void *opaque, int version_id) +{ + if (kvm_irqchip_in_kernel()) { + return kvm_ipi_put(opaque, version_id); + } + + return 0; +} + static void loongarch_ipi_class_init(ObjectClass *klass, const void *data) { LoongsonIPICommonClass *licc = LOONGSON_IPI_COMMON_CLASS(klass); @@ -187,6 +205,8 @@ static void loongarch_ipi_class_init(ObjectClass *klass, const void *data) licc->cpu_by_arch_id = loongarch_cpu_by_arch_id; hc->plug = loongarch_ipi_cpu_plug; hc->unplug = loongarch_ipi_cpu_unplug; + licc->pre_save = loongarch_ipi_pre_save; + licc->post_load = loongarch_ipi_post_load; } static const TypeInfo loongarch_ipi_types[] = { diff --git a/hw/intc/loongarch_ipi_kvm.c b/hw/intc/loongarch_ipi_kvm.c index 51e9c7ed1e..b615060d83 100644 --- a/hw/intc/loongarch_ipi_kvm.c +++ b/hw/intc/loongarch_ipi_kvm.c @@ -11,6 +11,60 @@ #include "system/kvm.h" #include "target/loongarch/cpu.h" +static void kvm_ipi_access_reg(int fd, uint64_t addr, uint32_t *val, bool write) +{ + kvm_device_access(fd, KVM_DEV_LOONGARCH_IPI_GRP_REGS, + addr, val, write, &error_abort); +} + +static void kvm_ipi_access_regs(void *opaque, bool write) +{ + LoongsonIPICommonState *ipi = (LoongsonIPICommonState *)opaque; + LoongarchIPIState *lis = LOONGARCH_IPI(opaque); + IPICore *core; + uint64_t attr; + int cpu, fd = lis->dev_fd; + + for (cpu = 0; cpu < ipi->num_cpu; cpu++) { + core = &ipi->cpu[cpu]; + attr = (cpu << 16) | CORE_STATUS_OFF; + kvm_ipi_access_reg(fd, attr, &core->status, write); + + attr = (cpu << 16) | CORE_EN_OFF; + kvm_ipi_access_reg(fd, attr, &core->en, write); + + attr = (cpu << 16) | CORE_SET_OFF; + kvm_ipi_access_reg(fd, attr, &core->set, write); + + attr = (cpu << 16) | CORE_CLEAR_OFF; + kvm_ipi_access_reg(fd, attr, &core->clear, write); + + attr = (cpu << 16) | CORE_BUF_20; + kvm_ipi_access_reg(fd, attr, &core->buf[0], write); + + attr = (cpu << 16) | CORE_BUF_28; + kvm_ipi_access_reg(fd, attr, &core->buf[2], write); + + attr = (cpu << 16) | CORE_BUF_30; + kvm_ipi_access_reg(fd, attr, &core->buf[4], write); + + attr = (cpu << 16) | CORE_BUF_38; + kvm_ipi_access_reg(fd, attr, &core->buf[6], write); + } +} + +int kvm_ipi_get(void *opaque) +{ + kvm_ipi_access_regs(opaque, false); + return 0; +} + +int kvm_ipi_put(void *opaque, int version_id) +{ + kvm_ipi_access_regs(opaque, true); + return 0; +} + void kvm_ipi_realize(DeviceState *dev, Error **errp) { LoongarchIPIState *lis = LOONGARCH_IPI(dev); diff --git a/include/hw/intc/loongarch_ipi.h b/include/hw/intc/loongarch_ipi.h index 608cd09a78..5175a6b004 100644 --- a/include/hw/intc/loongarch_ipi.h +++ b/include/hw/intc/loongarch_ipi.h @@ -26,5 +26,7 @@ struct LoongarchIPIClass { }; void kvm_ipi_realize(DeviceState *dev, Error **errp); +int kvm_ipi_get(void *opaque); +int kvm_ipi_put(void *opaque, int version_id); #endif From 5b0e05b9023793197b82fd6feb275459adc9e10c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:30:26 +0800 Subject: [PATCH 1600/2760] hw/intc/loongarch_pch_msi: Inject MSI interrupt to kernel If kvm_irqchip_in_kernel() return true, MSI interrupt can be injected with API kvm_irqchip_send_msi() to KVM. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063033.2557365-7-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_pch_msi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/intc/loongarch_pch_msi.c b/hw/intc/loongarch_pch_msi.c index 06eb944da0..f6d163158d 100644 --- a/hw/intc/loongarch_pch_msi.c +++ b/hw/intc/loongarch_pch_msi.c @@ -13,6 +13,7 @@ #include "hw/pci/msi.h" #include "hw/misc/unimp.h" #include "migration/vmstate.h" +#include "system/kvm.h" #include "trace.h" static uint64_t loongarch_msi_mem_read(void *opaque, hwaddr addr, unsigned size) @@ -26,6 +27,15 @@ static void loongarch_msi_mem_write(void *opaque, hwaddr addr, LoongArchPCHMSI *s = (LoongArchPCHMSI *)opaque; int irq_num; + if (kvm_irqchip_in_kernel()) { + MSIMessage msg; + + msg.address = addr; + msg.data = val; + kvm_irqchip_send_msi(kvm_state, msg); + return; + } + /* * vector number is irq number from upper extioi intc * need subtract irq base to get msi vector offset From b758e28974614a15e70fbc983e28fe77852564ee Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:30:27 +0800 Subject: [PATCH 1601/2760] hw/intc/loongarch_pch: Add kernel irqchip realize function Function kvm_pic_realize() is added if kvm_irqchip_in_kernel() return true. It is to notify KVM kernel to create and initialize PCH PCI device in kernel mode. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063033.2557365-8-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 14 ++++++++--- hw/intc/loongarch_pic_kvm.c | 38 +++++++++++++++++++++++++++++ hw/intc/meson.build | 2 ++ include/hw/intc/loongarch_pch_pic.h | 3 +++ 4 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 hw/intc/loongarch_pic_kvm.c diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index ebb33ed0b0..6ac3a72c31 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -10,6 +10,7 @@ #include "qemu/log.h" #include "hw/irq.h" #include "hw/intc/loongarch_pch_pic.h" +#include "system/kvm.h" #include "trace.h" #include "qapi/error.h" @@ -275,10 +276,15 @@ static void loongarch_pic_realize(DeviceState *dev, Error **errp) qdev_init_gpio_out(dev, s->parent_irq, s->irq_num); qdev_init_gpio_in(dev, pch_pic_irq_handler, s->irq_num); - memory_region_init_io(&s->iomem, OBJECT(dev), - &loongarch_pch_pic_ops, - s, TYPE_LOONGARCH_PIC, VIRT_PCH_REG_SIZE); - sysbus_init_mmio(sbd, &s->iomem); + + if (kvm_irqchip_in_kernel()) { + kvm_pic_realize(dev, errp); + } else { + memory_region_init_io(&s->iomem, OBJECT(dev), + &loongarch_pch_pic_ops, + s, TYPE_LOONGARCH_PIC, VIRT_PCH_REG_SIZE); + sysbus_init_mmio(sbd, &s->iomem); + } } static void loongarch_pic_class_init(ObjectClass *klass, const void *data) diff --git a/hw/intc/loongarch_pic_kvm.c b/hw/intc/loongarch_pic_kvm.c new file mode 100644 index 0000000000..ee77f04a13 --- /dev/null +++ b/hw/intc/loongarch_pic_kvm.c @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * LoongArch kvm pch pic interrupt support + * + * Copyright (C) 2025 Loongson Technology Corporation Limited + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/intc/loongarch_pch_pic.h" +#include "hw/loongarch/virt.h" +#include "hw/pci-host/ls7a.h" +#include "system/kvm.h" + +void kvm_pic_realize(DeviceState *dev, Error **errp) +{ + LoongarchPICState *lps = LOONGARCH_PIC(dev); + uint64_t pch_pic_base = VIRT_PCH_REG_BASE; + int ret; + + ret = kvm_create_device(kvm_state, KVM_DEV_TYPE_LOONGARCH_PCHPIC, false); + if (ret < 0) { + fprintf(stderr, "Create KVM_LOONGARCH_PCHPIC failed: %s\n", + strerror(-ret)); + abort(); + } + + lps->dev_fd = ret; + ret = kvm_device_access(lps->dev_fd, KVM_DEV_LOONGARCH_PCH_PIC_GRP_CTRL, + KVM_DEV_LOONGARCH_PCH_PIC_CTRL_INIT, + &pch_pic_base, true, NULL); + if (ret < 0) { + fprintf(stderr, "KVM_LOONGARCH_PCH_PIC_INIT failed: %s\n", + strerror(-ret)); + abort(); + } +} diff --git a/hw/intc/meson.build b/hw/intc/meson.build index 1cc999771d..3137521a4a 100644 --- a/hw/intc/meson.build +++ b/hw/intc/meson.build @@ -74,6 +74,8 @@ specific_ss.add(when: 'CONFIG_LOONGARCH_IPI', if_true: files('loongarch_ipi.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_LOONGARCH_IPI'], if_true: files('loongarch_ipi_kvm.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_PIC', if_true: files('loongarch_pch_pic.c', 'loongarch_pic_common.c')) +specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_LOONGARCH_PCH_PIC'], + if_true: files('loongarch_pic_kvm.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_PCH_MSI', if_true: files('loongarch_pch_msi.c')) specific_ss.add(when: 'CONFIG_LOONGARCH_EXTIOI', if_true: files('loongarch_extioi.c', 'loongarch_extioi_common.c')) specific_ss.add(when: ['CONFIG_KVM', 'CONFIG_LOONGARCH_EXTIOI'], diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h index 839a59a43b..4b52f1165f 100644 --- a/include/hw/intc/loongarch_pch_pic.h +++ b/include/hw/intc/loongarch_pch_pic.h @@ -16,6 +16,7 @@ OBJECT_DECLARE_TYPE(LoongarchPICState, LoongarchPICClass, LOONGARCH_PIC) struct LoongarchPICState { LoongArchPICCommonState parent_obj; + int dev_fd; }; struct LoongarchPICClass { @@ -25,4 +26,6 @@ struct LoongarchPICClass { ResettablePhases parent_phases; }; +void kvm_pic_realize(DeviceState *dev, Error **errp); + #endif /* HW_LOONGARCH_PCH_PIC_H */ From 11a04c9f40ac479f68139f6801da314591e67ae1 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:30:28 +0800 Subject: [PATCH 1602/2760] hw/intc/loongarch_pch: Add kernel irqchip save and restore function Add save and store funtction if kvm_irqchip_in_kernel() return true, it is to get and set PCH PCI irqchip state from KVM kernel. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063033.2557365-9-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 22 ++++++++++++ hw/intc/loongarch_pic_kvm.c | 47 ++++++++++++++++++++++++++ include/hw/intc/loongarch_pch_pic.h | 2 ++ include/hw/intc/loongarch_pic_common.h | 1 + 4 files changed, 72 insertions(+) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 6ac3a72c31..13b5766444 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -287,16 +287,38 @@ static void loongarch_pic_realize(DeviceState *dev, Error **errp) } } +static int loongarch_pic_pre_save(LoongArchPICCommonState *opaque) +{ + if (kvm_irqchip_in_kernel()) { + return kvm_pic_get(opaque); + } + + return 0; +} + +static int loongarch_pic_post_load(LoongArchPICCommonState *opaque, + int version_id) +{ + if (kvm_irqchip_in_kernel()) { + return kvm_pic_put(opaque, version_id); + } + + return 0; +} + static void loongarch_pic_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); LoongarchPICClass *lpc = LOONGARCH_PIC_CLASS(klass); + LoongArchPICCommonClass *lpcc = LOONGARCH_PIC_COMMON_CLASS(klass); ResettableClass *rc = RESETTABLE_CLASS(klass); resettable_class_set_parent_phases(rc, NULL, loongarch_pic_reset_hold, NULL, &lpc->parent_phases); device_class_set_parent_realize(dc, loongarch_pic_realize, &lpc->parent_realize); + lpcc->pre_save = loongarch_pic_pre_save; + lpcc->post_load = loongarch_pic_post_load; } static const TypeInfo loongarch_pic_types[] = { diff --git a/hw/intc/loongarch_pic_kvm.c b/hw/intc/loongarch_pic_kvm.c index ee77f04a13..3eef81a9bb 100644 --- a/hw/intc/loongarch_pic_kvm.c +++ b/hw/intc/loongarch_pic_kvm.c @@ -13,6 +13,53 @@ #include "hw/pci-host/ls7a.h" #include "system/kvm.h" +static void kvm_pch_pic_access_reg(int fd, uint64_t addr, void *val, bool write) +{ + kvm_device_access(fd, KVM_DEV_LOONGARCH_PCH_PIC_GRP_REGS, + addr, val, write, &error_abort); +} + +static void kvm_pch_pic_access(void *opaque, bool write) +{ + LoongArchPICCommonState *s = LOONGARCH_PIC_COMMON(opaque); + LoongarchPICState *lps = LOONGARCH_PIC(opaque); + int fd = lps->dev_fd; + int addr, offset; + + kvm_pch_pic_access_reg(fd, PCH_PIC_INT_MASK, &s->int_mask, write); + kvm_pch_pic_access_reg(fd, PCH_PIC_HTMSI_EN, &s->htmsi_en, write); + kvm_pch_pic_access_reg(fd, PCH_PIC_INT_EDGE, &s->intedge, write); + kvm_pch_pic_access_reg(fd, PCH_PIC_AUTO_CTRL0, &s->auto_crtl0, write); + kvm_pch_pic_access_reg(fd, PCH_PIC_AUTO_CTRL1, &s->auto_crtl1, write); + + for (addr = PCH_PIC_ROUTE_ENTRY; + addr < PCH_PIC_ROUTE_ENTRY_END; addr++) { + offset = addr - PCH_PIC_ROUTE_ENTRY; + kvm_pch_pic_access_reg(fd, addr, &s->route_entry[offset], write); + } + + for (addr = PCH_PIC_HTMSI_VEC; addr < PCH_PIC_HTMSI_VEC_END; addr++) { + offset = addr - PCH_PIC_HTMSI_VEC; + kvm_pch_pic_access_reg(fd, addr, &s->htmsi_vector[offset], write); + } + + kvm_pch_pic_access_reg(fd, PCH_PIC_INT_REQUEST, &s->intirr, write); + kvm_pch_pic_access_reg(fd, PCH_PIC_INT_STATUS, &s->intisr, write); + kvm_pch_pic_access_reg(fd, PCH_PIC_INT_POL, &s->int_polarity, write); +} + +int kvm_pic_get(void *opaque) +{ + kvm_pch_pic_access(opaque, false); + return 0; +} + +int kvm_pic_put(void *opaque, int version_id) +{ + kvm_pch_pic_access(opaque, true); + return 0; +} + void kvm_pic_realize(DeviceState *dev, Error **errp) { LoongarchPICState *lps = LOONGARCH_PIC(dev); diff --git a/include/hw/intc/loongarch_pch_pic.h b/include/hw/intc/loongarch_pch_pic.h index 4b52f1165f..a46b6f8985 100644 --- a/include/hw/intc/loongarch_pch_pic.h +++ b/include/hw/intc/loongarch_pch_pic.h @@ -27,5 +27,7 @@ struct LoongarchPICClass { }; void kvm_pic_realize(DeviceState *dev, Error **errp); +int kvm_pic_get(void *opaque); +int kvm_pic_put(void *opaque, int version_id); #endif /* HW_LOONGARCH_PCH_PIC_H */ diff --git a/include/hw/intc/loongarch_pic_common.h b/include/hw/intc/loongarch_pic_common.h index 9349a055d0..f774c975d4 100644 --- a/include/hw/intc/loongarch_pic_common.h +++ b/include/hw/intc/loongarch_pic_common.h @@ -23,6 +23,7 @@ #define PCH_PIC_ROUTE_ENTRY_END 0x13f #define PCH_PIC_HTMSI_VEC 0x200 #define PCH_PIC_HTMSI_VEC_END 0x23f +#define PCH_PIC_INT_REQUEST 0x380 #define PCH_PIC_INT_STATUS 0x3a0 #define PCH_PIC_INT_POL 0x3e0 From 0dd6798a1adda03dcfa6304437faa8e62a193d9c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:30:29 +0800 Subject: [PATCH 1603/2760] hw/intc/loongarch_pch: Inject irq line interrupt to kernel If kvm_irqchip_in_kernel() return true, irq line interrupt can be injected with API kvm_set_irq() to KVM. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063033.2557365-10-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_pch_pic.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 13b5766444..1adef980d4 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -49,6 +49,11 @@ static void pch_pic_irq_handler(void *opaque, int irq, int level) assert(irq < s->irq_num); trace_loongarch_pch_pic_irq_handler(irq, level); + if (kvm_irqchip_in_kernel()) { + kvm_set_irq(kvm_state, irq, !!level); + return; + } + if (s->intedge & mask) { /* Edge triggered */ if (level) { From c642ddf19b248bb668e40a8d15089b877e4057fa Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:30:30 +0800 Subject: [PATCH 1604/2760] hw/loongarch/virt: Add reset support for kernel irqchip When system reboot, interrupt controller is restored to initial state. However if interrupt controller extioi/ipi/pch_pic is emulated in kernel, it should notify kvm to do so. Here suspend and restore API is used for reset, set initial state in qemu user space and restore API is used to notify kvm to reload register state. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063033.2557365-11-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/intc/loongarch_extioi.c | 4 ++++ hw/intc/loongarch_extioi_kvm.c | 4 ++++ hw/intc/loongarch_ipi.c | 4 ++++ hw/intc/loongarch_ipi_kvm.c | 4 ++++ hw/intc/loongarch_pch_pic.c | 4 ++++ hw/intc/loongarch_pic_kvm.c | 4 ++++ 6 files changed, 24 insertions(+) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index 7be0685f36..8b8ac6b187 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -391,6 +391,10 @@ static void loongarch_extioi_reset_hold(Object *obj, ResetType type) if (lec->parent_phases.hold) { lec->parent_phases.hold(obj, type); } + + if (kvm_irqchip_in_kernel()) { + kvm_extioi_put(obj, 0); + } } static int vmstate_extioi_pre_save(void *opaque) diff --git a/hw/intc/loongarch_extioi_kvm.c b/hw/intc/loongarch_extioi_kvm.c index f4c618ca4c..0133540c45 100644 --- a/hw/intc/loongarch_extioi_kvm.c +++ b/hw/intc/loongarch_extioi_kvm.c @@ -94,6 +94,10 @@ int kvm_extioi_put(void *opaque, int version_id) LoongArchExtIOIState *les = LOONGARCH_EXTIOI(opaque); int fd = les->dev_fd; + if (fd == 0) { + return 0; + } + kvm_extioi_access_regs(opaque, true); kvm_extioi_access_sw_status(opaque, true); kvm_device_access(fd, KVM_DEV_LOONGARCH_EXTIOI_GRP_CTRL, diff --git a/hw/intc/loongarch_ipi.c b/hw/intc/loongarch_ipi.c index 0ea91ea054..fc8005c944 100644 --- a/hw/intc/loongarch_ipi.c +++ b/hw/intc/loongarch_ipi.c @@ -122,6 +122,10 @@ static void loongarch_ipi_reset_hold(Object *obj, ResetType type) core->clear = 0; memset(core->buf, 0, sizeof(core->buf)); } + + if (kvm_irqchip_in_kernel()) { + kvm_ipi_put(obj, 0); + } } static void loongarch_ipi_cpu_plug(HotplugHandler *hotplug_dev, diff --git a/hw/intc/loongarch_ipi_kvm.c b/hw/intc/loongarch_ipi_kvm.c index b615060d83..4cb3acc921 100644 --- a/hw/intc/loongarch_ipi_kvm.c +++ b/hw/intc/loongarch_ipi_kvm.c @@ -25,6 +25,10 @@ static void kvm_ipi_access_regs(void *opaque, bool write) uint64_t attr; int cpu, fd = lis->dev_fd; + if (fd == 0) { + return; + } + for (cpu = 0; cpu < ipi->num_cpu; cpu++) { core = &ipi->cpu[cpu]; attr = (cpu << 16) | CORE_STATUS_OFF; diff --git a/hw/intc/loongarch_pch_pic.c b/hw/intc/loongarch_pch_pic.c index 1adef980d4..c4b242dbf4 100644 --- a/hw/intc/loongarch_pch_pic.c +++ b/hw/intc/loongarch_pch_pic.c @@ -264,6 +264,10 @@ static void loongarch_pic_reset_hold(Object *obj, ResetType type) if (lpc->parent_phases.hold) { lpc->parent_phases.hold(obj, type); } + + if (kvm_irqchip_in_kernel()) { + kvm_pic_put(obj, 0); + } } static void loongarch_pic_realize(DeviceState *dev, Error **errp) diff --git a/hw/intc/loongarch_pic_kvm.c b/hw/intc/loongarch_pic_kvm.c index 3eef81a9bb..dd504ec6a6 100644 --- a/hw/intc/loongarch_pic_kvm.c +++ b/hw/intc/loongarch_pic_kvm.c @@ -26,6 +26,10 @@ static void kvm_pch_pic_access(void *opaque, bool write) int fd = lps->dev_fd; int addr, offset; + if (fd == 0) { + return; + } + kvm_pch_pic_access_reg(fd, PCH_PIC_INT_MASK, &s->int_mask, write); kvm_pch_pic_access_reg(fd, PCH_PIC_HTMSI_EN, &s->htmsi_en, write); kvm_pch_pic_access_reg(fd, PCH_PIC_INT_EDGE, &s->intedge, write); From 74586a2482575776fff5f5f75e0657524c60719d Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:34:31 +0800 Subject: [PATCH 1605/2760] target/loongarch: Report error with split kernel_irqchip option Option kernel_irqchip=split is not supported on LoongArch virt machine, report error and exit if detect split kernel_irqchip option. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063431.2557468-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/kvm/kvm.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index c66bdd5302..c5d488aa42 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -1253,7 +1253,12 @@ int kvm_arch_init(MachineState *ms, KVMState *s) int kvm_arch_irqchip_create(KVMState *s) { - return 0; + if (kvm_kernel_irqchip_split()) { + error_report("kernel_irqchip=split is not supported on LoongArch"); + exit(1); + } + + return kvm_check_extension(s, KVM_CAP_DEVICE_CTRL); } void kvm_arch_pre_run(CPUState *cs, struct kvm_run *run) From 17fb88a0fa0a13b0e2976a2fe04756337d95fe1f Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:35:23 +0800 Subject: [PATCH 1606/2760] hw/loongarch/virt: Disable emulation with IOCSR misc register Register IOCSR MISC_FUNC_REG is to enable features about EXTIOI irqchip. If EXTIOI is emulated in kernel, MISC_FUNC_REG register should be emulated in kernel also. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063523.2557513-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index 34dfbd13e5..e5468b6af9 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -522,6 +522,10 @@ static MemTxResult virt_iocsr_misc_write(void *opaque, hwaddr addr, switch (addr) { case MISC_FUNC_REG: + if (kvm_irqchip_in_kernel()) { + return MEMTX_OK; + } + if (!virt_is_veiointc_enabled(lvms)) { return MEMTX_OK; } @@ -572,6 +576,10 @@ static MemTxResult virt_iocsr_misc_read(void *opaque, hwaddr addr, ret = 0x303030354133ULL; /* "3A5000" */ break; case MISC_FUNC_REG: + if (kvm_irqchip_in_kernel()) { + return MEMTX_OK; + } + if (!virt_is_veiointc_enabled(lvms)) { ret |= BIT_ULL(IOCSRM_EXTIOI_EN); break; From 27f5d500c2259650076e3abd72f092b566fd81ac Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Fri, 6 Jun 2025 14:36:07 +0800 Subject: [PATCH 1607/2760] hw/loongarch/virt: Add kernel irqchip support If kvm_irqchip_in_kernel() return true, interrupt controller ExtIOI, IPI, PCH_PCI and PCH_MSI should be emlated in kernel. And it is not necessary to create memory region for these devices in user space. Reviewed-by: Song Gao Signed-off-by: Bibo Mao Message-ID: <20250606063607.2557540-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- hw/loongarch/virt.c | 57 ++++++++++++++++++++++---------------- target/loongarch/cpu.h | 1 + target/loongarch/kvm/kvm.c | 16 +++++++++++ 3 files changed, 50 insertions(+), 24 deletions(-) diff --git a/hw/loongarch/virt.c b/hw/loongarch/virt.c index e5468b6af9..b15ada2078 100644 --- a/hw/loongarch/virt.c +++ b/hw/loongarch/virt.c @@ -414,12 +414,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) lvms->ipi = ipi; sysbus_realize_and_unref(SYS_BUS_DEVICE(ipi), &error_fatal); - /* IPI iocsr memory region */ - memory_region_add_subregion(&lvms->system_iocsr, SMP_IPI_MAILBOX, - sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); - memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR, - sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); - /* Create EXTIOI device */ extioi = qdev_new(TYPE_LOONGARCH_EXTIOI); lvms->extioi = extioi; @@ -427,12 +421,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) qdev_prop_set_bit(extioi, "has-virtualization-extension", true); } sysbus_realize_and_unref(SYS_BUS_DEVICE(extioi), &error_fatal); - memory_region_add_subregion(&lvms->system_iocsr, APIC_BASE, - sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); - if (virt_is_veiointc_enabled(lvms)) { - memory_region_add_subregion(&lvms->system_iocsr, EXTIOI_VIRT_BASE, - sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1)); - } virt_cpu_irq_init(lvms); pch_pic = qdev_new(TYPE_LOONGARCH_PIC); @@ -440,13 +428,6 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) qdev_prop_set_uint32(pch_pic, "pch_pic_irq_num", num); d = SYS_BUS_DEVICE(pch_pic); sysbus_realize_and_unref(d, &error_fatal); - memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE, - sysbus_mmio_get_region(d, 0)); - - /* Connect pch_pic irqs to extioi */ - for (i = 0; i < num; i++) { - qdev_connect_gpio_out(DEVICE(d), i, qdev_get_gpio_in(extioi, i)); - } pch_msi = qdev_new(TYPE_LOONGARCH_PCH_MSI); start = num; @@ -456,12 +437,40 @@ static void virt_irq_init(LoongArchVirtMachineState *lvms) d = SYS_BUS_DEVICE(pch_msi); sysbus_realize_and_unref(d, &error_fatal); sysbus_mmio_map(d, 0, VIRT_PCH_MSI_ADDR_LOW); - for (i = 0; i < num; i++) { - /* Connect pch_msi irqs to extioi */ - qdev_connect_gpio_out(DEVICE(d), i, - qdev_get_gpio_in(extioi, i + start)); - } + if (kvm_irqchip_in_kernel()) { + kvm_loongarch_init_irq_routing(); + } else { + /* IPI iocsr memory region */ + memory_region_add_subregion(&lvms->system_iocsr, SMP_IPI_MAILBOX, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 0)); + memory_region_add_subregion(&lvms->system_iocsr, MAIL_SEND_ADDR, + sysbus_mmio_get_region(SYS_BUS_DEVICE(ipi), 1)); + + /* EXTIOI iocsr memory region */ + memory_region_add_subregion(&lvms->system_iocsr, APIC_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 0)); + if (virt_is_veiointc_enabled(lvms)) { + memory_region_add_subregion(&lvms->system_iocsr, EXTIOI_VIRT_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(extioi), 1)); + } + + /* PCH_PIC memory region */ + memory_region_add_subregion(get_system_memory(), VIRT_IOAPIC_REG_BASE, + sysbus_mmio_get_region(SYS_BUS_DEVICE(pch_pic), 0)); + + /* Connect pch_pic irqs to extioi */ + for (i = 0; i < VIRT_PCH_PIC_IRQ_NUM; i++) { + qdev_connect_gpio_out(DEVICE(pch_pic), i, + qdev_get_gpio_in(extioi, i)); + } + + for (i = VIRT_PCH_PIC_IRQ_NUM; i < EXTIOI_IRQS; i++) { + /* Connect pch_msi irqs to extioi */ + qdev_connect_gpio_out(DEVICE(pch_msi), i - VIRT_PCH_PIC_IRQ_NUM, + qdev_get_gpio_in(extioi, i)); + } + } virt_devices_init(pch_pic, lvms); } diff --git a/target/loongarch/cpu.h b/target/loongarch/cpu.h index 262bf87f7b..9538e8d61d 100644 --- a/target/loongarch/cpu.h +++ b/target/loongarch/cpu.h @@ -503,5 +503,6 @@ static inline void kvm_loongarch_cpu_post_init(LoongArchCPU *cpu) { } #endif +void kvm_loongarch_init_irq_routing(void); #endif /* LOONGARCH_CPU_H */ diff --git a/target/loongarch/kvm/kvm.c b/target/loongarch/kvm/kvm.c index c5d488aa42..e5ea2dba9d 100644 --- a/target/loongarch/kvm/kvm.c +++ b/target/loongarch/kvm/kvm.c @@ -1240,6 +1240,22 @@ void kvm_arch_init_irq_routing(KVMState *s) { } +void kvm_loongarch_init_irq_routing(void) +{ + int i; + + kvm_async_interrupts_allowed = true; + kvm_msi_via_irqfd_allowed = kvm_irqfds_enabled(); + if (kvm_has_gsi_routing()) { + for (i = 0; i < KVM_IRQCHIP_NUM_PINS; ++i) { + kvm_irqchip_add_irq_route(kvm_state, i, 0, i); + } + + kvm_gsi_routing_allowed = true; + kvm_irqchip_commit_routes(kvm_state); + } +} + int kvm_arch_get_default_type(MachineState *ms) { return 0; From c2a2e1ad2a749caa864281b1d4dc3f16c3f344f6 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Wed, 4 Jun 2025 16:40:05 +0800 Subject: [PATCH 1608/2760] target/loongarch: fix vldi/xvldi raise wrong error on qemu we got an aborted error ** ERROR:../target/loongarch/tcg/insn_trans/trans_vec.c.inc:3574:vldi_get_value: code should not be reached Bail out! ERROR:../target/loongarch/tcg/insn_trans/trans_vec.c.inc:3574:vldi_get_value: code should not be reached Aborted (core dumped) but on 3A600/3A5000 we got a "Illegal instruction" error. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2971 Fixes: 29bb5d727ff ("target/loongarch: Implement vldi") Cc: qemu-stable@nongnu.org Reviewed-by: Bibo Mao Reviewed-by: Richard Henderson Signed-off-by: Song Gao --- target/loongarch/tcg/insn_trans/trans_vec.c.inc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/target/loongarch/tcg/insn_trans/trans_vec.c.inc b/target/loongarch/tcg/insn_trans/trans_vec.c.inc index d6f0560349..78730029cb 100644 --- a/target/loongarch/tcg/insn_trans/trans_vec.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_vec.c.inc @@ -3465,7 +3465,7 @@ TRANS(xvmsknz_b, LASX, gen_xx, gen_helper_vmsknz_b) static uint64_t vldi_get_value(DisasContext *ctx, uint32_t imm) { int mode; - uint64_t data, t; + uint64_t data = 0, t; /* * imm bit [11:8] is mode, mode value is 0-12. @@ -3570,17 +3570,26 @@ static uint64_t vldi_get_value(DisasContext *ctx, uint32_t imm) } break; default: - generate_exception(ctx, EXCCODE_INE); g_assert_not_reached(); } return data; } +static bool check_valid_vldi_mode(arg_vldi *a) +{ + return extract32(a->imm, 8, 4) <= 12; +} + static bool gen_vldi(DisasContext *ctx, arg_vldi *a, uint32_t oprsz) { int sel, vece; uint64_t value; + if (!check_valid_vldi_mode(a)) { + generate_exception(ctx, EXCCODE_INE); + return true; + } + if (!check_vec(ctx, oprsz)) { return true; } From 1548c5cdf010f6c89d577402c56eca7169936f48 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 13 Jun 2025 14:51:54 +0200 Subject: [PATCH 1609/2760] rust: prepare variable definitions for multiple bindgen invocations When splitting the QEMU Rust bindings into multiple crates, the bindgen-generated structs also have to be split so that it's possible to add "impl" blocks (e.g. for Sync/Send or Default, or even for utility methods in cases such as VMStateFlags). Tweak various variable definitions in meson.build, to avoid naming conflicts once there will be multiple bindgen invocations. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- meson.build | 21 ++++++++++++--------- rust/meson.build | 2 +- rust/qemu-api/meson.build | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/meson.build b/meson.build index ed60be2a2d..19ffa9cb34 100644 --- a/meson.build +++ b/meson.build @@ -4200,10 +4200,11 @@ foreach target_base_arch, config_base_arch : config_base_arch_mak endforeach if have_rust + bindings_incdir = include_directories('.', 'include') # We would like to use --generate-cstr, but it is only available # starting with bindgen 0.66.0. The oldest supported versions # is 0.60.x (Debian 12 has 0.60.1) which introduces --allowlist-file. - bindgen_args = [ + bindgen_args_common = [ '--disable-header-comment', '--raw-line', '// @generated', '--ctypes-prefix', 'std::os::raw', @@ -4219,20 +4220,22 @@ if have_rust ] if not rustfmt.found() if bindgen.version().version_compare('<0.65.0') - bindgen_args += ['--no-rustfmt-bindings'] + bindgen_args_common += ['--no-rustfmt-bindings'] else - bindgen_args += ['--formatter', 'none'] + bindgen_args_common += ['--formatter', 'none'] endif endif if bindgen.version().version_compare('>=0.66.0') - bindgen_args += ['--rust-target', '1.59'] + bindgen_args_common += ['--rust-target', '1.59'] endif if bindgen.version().version_compare('<0.61.0') # default in 0.61+ - bindgen_args += ['--size_t-is-usize'] + bindgen_args_common += ['--size_t-is-usize'] else - bindgen_args += ['--merge-extern-blocks'] + bindgen_args_common += ['--merge-extern-blocks'] endif + + bindgen_args = [] c_enums = [ 'DeviceCategory', 'GpioPolarity', @@ -4264,13 +4267,13 @@ if have_rust # this case you must pass the path to `clang` and `libclang` to your build # command invocation using the environment variables CLANG_PATH and # LIBCLANG_PATH - bindings_rs = rust.bindgen( + _qemu_api_bindings_inc_rs = rust.bindgen( input: 'rust/wrapper.h', dependencies: common_ss.all_dependencies(), output: 'bindings.inc.rs', - include_directories: include_directories('.', 'include'), + include_directories: bindings_incdir, bindgen_version: ['>=0.60.0'], - args: bindgen_args, + args: bindgen_args_common + bindgen_args, ) subdir('rust') endif diff --git a/rust/meson.build b/rust/meson.build index 99ae7956cd..e9f0879e29 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -33,5 +33,5 @@ if cargo.found() command: [config_host['MESON'], 'devenv', '--workdir', '@CURRENT_SOURCE_DIR@', cargo, 'fmt'], - depends: bindings_rs) + depends: _qemu_api_bindings_inc_rs) endif diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 33653b4a28..64c04dfd74 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -32,7 +32,7 @@ _qemu_api_rs = static_library( 'src/vmstate.rs', 'src/zeroable.rs', ], - {'.' : bindings_rs}, + {'.' : _qemu_api_bindings_inc_rs}, ), override_options: ['rust_std=2021', 'build.rust_std=2021'], rust_abi: 'rust', From 1ae4ca0463d737ead1162c35b267ef5882d42883 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 13 Jun 2025 14:49:27 +0200 Subject: [PATCH 1610/2760] rust: move rust.bindgen to qemu-api crate Once qemu-api is split in multiple crates, each of them will have its own invocation of bindgen. There cannot be only one, because there are occasional "impl" blocks for the bindgen-generated structs (e.g. VMStateFlags or QOM classes) that have to reside in the same crate as the bindgen-generated code. For now, prepare for this new organization by invoking bindgen within the qemu-api crate's build definitions; it's also a much better place to list enums that need specific treatment from bindgen. Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- meson.build | 41 ----------------------------------- rust/meson.build | 4 +++- rust/qemu-api/build.rs | 2 +- rust/qemu-api/meson.build | 41 +++++++++++++++++++++++++++++++++++ rust/{ => qemu-api}/wrapper.h | 0 5 files changed, 45 insertions(+), 43 deletions(-) rename rust/{ => qemu-api}/wrapper.h (100%) diff --git a/meson.build b/meson.build index 19ffa9cb34..4676908dbb 100644 --- a/meson.build +++ b/meson.build @@ -4234,47 +4234,6 @@ if have_rust else bindgen_args_common += ['--merge-extern-blocks'] endif - - bindgen_args = [] - c_enums = [ - 'DeviceCategory', - 'GpioPolarity', - 'MachineInitPhase', - 'MemoryDeviceInfoKind', - 'MigrationPolicy', - 'MigrationPriority', - 'QEMUChrEvent', - 'QEMUClockType', - 'ResetType', - 'device_endian', - 'module_init_type', - ] - foreach enum : c_enums - bindgen_args += ['--rustified-enum', enum] - endforeach - c_bitfields = [ - 'ClockEvent', - 'VMStateFlags', - ] - foreach enum : c_bitfields - bindgen_args += ['--bitfield-enum', enum] - endforeach - - # TODO: Remove this comment when the clang/libclang mismatch issue is solved. - # - # Rust bindings generation with `bindgen` might fail in some cases where the - # detected `libclang` does not match the expected `clang` version/target. In - # this case you must pass the path to `clang` and `libclang` to your build - # command invocation using the environment variables CLANG_PATH and - # LIBCLANG_PATH - _qemu_api_bindings_inc_rs = rust.bindgen( - input: 'rust/wrapper.h', - dependencies: common_ss.all_dependencies(), - output: 'bindings.inc.rs', - include_directories: bindings_incdir, - bindgen_version: ['>=0.60.0'], - args: bindgen_args_common + bindgen_args, - ) subdir('rust') endif diff --git a/rust/meson.build b/rust/meson.build index e9f0879e29..331f11b7e7 100644 --- a/rust/meson.build +++ b/rust/meson.build @@ -20,6 +20,8 @@ proc_macro2_rs_native = dependency('proc-macro2-1-rs', native: true) qemuutil_rs = qemuutil.partial_dependency(link_args: true, links: true) +genrs = [] + subdir('qemu-api-macros') subdir('bits') subdir('qemu-api') @@ -33,5 +35,5 @@ if cargo.found() command: [config_host['MESON'], 'devenv', '--workdir', '@CURRENT_SOURCE_DIR@', cargo, 'fmt'], - depends: _qemu_api_bindings_inc_rs) + depends: genrs) endif diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs index 1e720641d2..7849486c1b 100644 --- a/rust/qemu-api/build.rs +++ b/rust/qemu-api/build.rs @@ -14,7 +14,7 @@ fn main() -> Result<()> { let path = env::var("MESON_BUILD_ROOT") .unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR"))); - let file = format!("{path}/bindings.inc.rs"); + let file = format!("{path}/rust/qemu-api/bindings.inc.rs"); let file = Path::new(&file); if !Path::new(&file).exists() { panic!(concat!( diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 64c04dfd74..5b8c7e5e8d 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -7,6 +7,47 @@ if get_option('debug_mutex') _qemu_api_cfg += ['--cfg', 'feature="debug_cell"'] endif +c_enums = [ + 'DeviceCategory', + 'GpioPolarity', + 'MachineInitPhase', + 'MemoryDeviceInfoKind', + 'MigrationPolicy', + 'MigrationPriority', + 'QEMUChrEvent', + 'QEMUClockType', + 'ResetType', + 'device_endian', + 'module_init_type', +] +_qemu_api_bindgen_args = [] +foreach enum : c_enums + _qemu_api_bindgen_args += ['--rustified-enum', enum] +endforeach +c_bitfields = [ + 'ClockEvent', + 'VMStateFlags', +] +foreach enum : c_bitfields + _qemu_api_bindgen_args += ['--bitfield-enum', enum] +endforeach + +# TODO: Remove this comment when the clang/libclang mismatch issue is solved. +# +# Rust bindings generation with `bindgen` might fail in some cases where the +# detected `libclang` does not match the expected `clang` version/target. In +# this case you must pass the path to `clang` and `libclang` to your build +# command invocation using the environment variables CLANG_PATH and +# LIBCLANG_PATH +_qemu_api_bindings_inc_rs = rust.bindgen( + input: 'wrapper.h', + dependencies: common_ss.all_dependencies(), + output: 'bindings.inc.rs', + include_directories: bindings_incdir, + bindgen_version: ['>=0.60.0'], + args: bindgen_args_common + _qemu_api_bindgen_args, + ) + _qemu_api_rs = static_library( 'qemu_api', structured_sources( diff --git a/rust/wrapper.h b/rust/qemu-api/wrapper.h similarity index 100% rename from rust/wrapper.h rename to rust/qemu-api/wrapper.h From ab81002252af101068fca94bc82b31ba59ff154b Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 15 Jun 2025 13:20:34 +0200 Subject: [PATCH 1611/2760] rust/qemu-api: Add initial logging support based on C API A log_mask_ln!() macro is provided which expects similar arguments as the C version. However, the formatting works as one would expect from Rust. To maximize code reuse the macro is just a thin wrapper around qemu_log(). Also, just the bare minimum of logging masks is provided which should suffice for the current use case of Rust in QEMU. Signed-off-by: Bernhard Beschow Link: https://lore.kernel.org/r/20250615112037.11992-2-shentey@gmail.com Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 1 + rust/qemu-api/meson.build | 1 + rust/qemu-api/src/lib.rs | 1 + rust/qemu-api/src/log.rs | 73 ++++++++++++++++++++++++++++++++++++ rust/qemu-api/src/prelude.rs | 2 + rust/qemu-api/wrapper.h | 2 + 6 files changed, 80 insertions(+) create mode 100644 rust/qemu-api/src/log.rs diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index 47e9677fcb..dc8c44109e 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -162,6 +162,7 @@ module status ``errno`` complete ``error`` stable ``irq`` complete +``log`` proof of concept ``memory`` stable ``module`` complete ``qdev`` stable diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build index 5b8c7e5e8d..a090297c45 100644 --- a/rust/qemu-api/meson.build +++ b/rust/qemu-api/meson.build @@ -62,6 +62,7 @@ _qemu_api_rs = static_library( 'src/errno.rs', 'src/error.rs', 'src/irq.rs', + 'src/log.rs', 'src/memory.rs', 'src/module.rs', 'src/prelude.rs', diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs index c78198f0f4..86dcd8ef17 100644 --- a/rust/qemu-api/src/lib.rs +++ b/rust/qemu-api/src/lib.rs @@ -21,6 +21,7 @@ pub mod chardev; pub mod errno; pub mod error; pub mod irq; +pub mod log; pub mod memory; pub mod module; pub mod qdev; diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs new file mode 100644 index 0000000000..d6c3d6c1b6 --- /dev/null +++ b/rust/qemu-api/src/log.rs @@ -0,0 +1,73 @@ +// Copyright 2025 Bernhard Beschow +// SPDX-License-Identifier: GPL-2.0-or-later + +//! Bindings for QEMU's logging infrastructure + +#[repr(u32)] +/// Represents specific error categories within QEMU's logging system. +/// +/// The `Log` enum provides a Rust abstraction for logging errors, corresponding +/// to a subset of the error categories defined in the C implementation. +pub enum Log { + /// Log invalid access caused by the guest. + /// Corresponds to `LOG_GUEST_ERROR` in the C implementation. + GuestError = crate::bindings::LOG_GUEST_ERROR, + + /// Log guest access of unimplemented functionality. + /// Corresponds to `LOG_UNIMP` in the C implementation. + Unimp = crate::bindings::LOG_UNIMP, +} + +/// A macro to log messages conditionally based on a provided mask. +/// +/// The `log_mask_ln` macro checks whether the given mask matches the current +/// log level and, if so, formats and logs the message. It is the Rust +/// counterpart of the `qemu_log_mask()` macro in the C implementation. +/// +/// # Parameters +/// +/// - `$mask`: A log level mask. This should be a variant of the `Log` enum. +/// - `$fmt`: A format string following the syntax and rules of the `format!` +/// macro. It specifies the structure of the log message. +/// - `$args`: Optional arguments to be interpolated into the format string. +/// +/// # Example +/// +/// ``` +/// use qemu_api::{log::Log, log_mask_ln}; +/// +/// let error_address = 0xbad; +/// log_mask_ln!(Log::GuestError, "Address 0x{error_address:x} out of range"); +/// ``` +/// +/// It is also possible to use printf-style formatting, as well as having a +/// trailing `,`: +/// +/// ``` +/// use qemu_api::{log::Log, log_mask_ln}; +/// +/// let error_address = 0xbad; +/// log_mask_ln!( +/// Log::GuestError, +/// "Address 0x{:x} out of range", +/// error_address, +/// ); +/// ``` +#[macro_export] +macro_rules! log_mask_ln { + ($mask:expr, $fmt:tt $($args:tt)*) => {{ + // Type assertion to enforce type `Log` for $mask + let _: Log = $mask; + + if unsafe { + (::qemu_api::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0 + } { + let formatted_string = format!("{}\n", format_args!($fmt $($args)*)); + let c_string = std::ffi::CString::new(formatted_string).unwrap(); + + unsafe { + ::qemu_api::bindings::qemu_log(c_string.as_ptr()); + } + } + }}; +} diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs index 43bfcd5fca..8f9e23ee2c 100644 --- a/rust/qemu-api/src/prelude.rs +++ b/rust/qemu-api/src/prelude.rs @@ -11,6 +11,8 @@ pub use crate::cell::BqlRefCell; pub use crate::errno; +pub use crate::log_mask_ln; + pub use crate::qdev::DeviceMethods; pub use crate::qom::InterfaceType; diff --git a/rust/qemu-api/wrapper.h b/rust/qemu-api/wrapper.h index 6060d3ba1a..15a1b19847 100644 --- a/rust/qemu-api/wrapper.h +++ b/rust/qemu-api/wrapper.h @@ -48,6 +48,8 @@ typedef enum memory_order { #endif /* __CLANG_STDATOMIC_H */ #include "qemu/osdep.h" +#include "qemu/log.h" +#include "qemu/log-for-trace.h" #include "qemu/module.h" #include "qemu-io.h" #include "system/system.h" From 1563f287dc9c4bc6a50d380095e966ac039ac24a Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 15 Jun 2025 13:20:35 +0200 Subject: [PATCH 1612/2760] rust: pl011: Implement logging Now that there is logging support in Rust for QEMU, use it in the pl011 device. Signed-off-by: Bernhard Beschow Link: https://lore.kernel.org/r/20250615112037.11992-3-shentey@gmail.com Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 2d416cd9a3..92dc295540 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -8,6 +8,8 @@ use qemu_api::{ chardev::{CharBackend, Chardev, Event}, impl_vmstate_forward, irq::{IRQState, InterruptSource}, + log::Log, + log_mask_ln, memory::{hwaddr, MemoryRegion, MemoryRegionOps, MemoryRegionOpsBuilder}, prelude::*, qdev::{Clock, ClockEvent, DeviceImpl, DeviceState, Property, ResetType, ResettablePhasesImpl}, @@ -276,8 +278,7 @@ impl PL011Registers { DMACR => { self.dmacr = value; if value & 3 > 0 { - // qemu_log_mask(LOG_UNIMP, "pl011: DMA not implemented\n"); - eprintln!("pl011: DMA not implemented"); + log_mask_ln!(Log::Unimp, "pl011: DMA not implemented"); } } } @@ -534,7 +535,7 @@ impl PL011State { u64::from(device_id[(offset - 0xfe0) >> 2]) } Err(_) => { - // qemu_log_mask(LOG_GUEST_ERROR, "pl011_read: Bad offset 0x%x\n", (int)offset); + log_mask_ln!(Log::GuestError, "PL011State::read: Bad offset {offset}"); 0 } Ok(field) => { @@ -566,7 +567,10 @@ impl PL011State { .borrow_mut() .write(field, value as u32, &self.char_backend); } else { - eprintln!("write bad offset {offset} value {value}"); + log_mask_ln!( + Log::GuestError, + "PL011State::write: Bad offset {offset} value {value}" + ); } if update_irq { self.update(); From b783601d1a3b3fd468035baf0ce2ead166e0abdc Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 15 Jun 2025 13:20:36 +0200 Subject: [PATCH 1613/2760] rust: pl011: Add missing logging to match C version Co-developed-by: Paolo Bonzini Signed-off-by: Bernhard Beschow Link: https://lore.kernel.org/r/20250615112037.11992-4-shentey@gmail.com Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 92dc295540..5b53f2649f 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -305,6 +305,12 @@ impl PL011Registers { } fn write_data_register(&mut self, value: u32) -> bool { + if !self.control.enable_uart() { + log_mask_ln!(Log::GuestError, "PL011 data written to disabled UART"); + } + if !self.control.enable_transmit() { + log_mask_ln!(Log::GuestError, "PL011 data written to disabled TX UART"); + } // interrupts always checked let _ = self.loopback_tx(value.into()); self.int_level |= Interrupt::TX; From 6b3fad084fc4e13901e252fe6c2a2c46ecea999b Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 16 Jun 2025 18:56:49 +0200 Subject: [PATCH 1614/2760] rust: hpet: fix new warning Nightly rustc complains that HPETAddrDecode has a lifetime but it is not clearly noted that it comes from &self. Apply the compiler's suggestion to shut it up. Reviewed-by: Zhao Liu Reviewed-by: Stefan Hajnoczi Signed-off-by: Paolo Bonzini --- rust/hw/timer/hpet/src/device.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/hw/timer/hpet/src/device.rs b/rust/hw/timer/hpet/src/device.rs index a281927781..acf7251029 100644 --- a/rust/hw/timer/hpet/src/device.rs +++ b/rust/hw/timer/hpet/src/device.rs @@ -771,7 +771,7 @@ impl HPETState { self.rtc_irq_level.set(0); } - fn decode(&self, mut addr: hwaddr, size: u32) -> HPETAddrDecode { + fn decode(&self, mut addr: hwaddr, size: u32) -> HPETAddrDecode<'_> { let shift = ((addr & 4) * 8) as u32; let len = std::cmp::min(size * 8, 64 - shift); From e68ec2980901c8e7f948f3305770962806c53f0b Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Tue, 4 Mar 2025 00:24:49 -0500 Subject: [PATCH 1615/2760] i386/cpu: Move adjustment of CPUID_EXT_PDCM before feature_dependencies[] check There is one entry relates to CPUID_EXT_PDCM in feature_dependencies[]. So it needs to get correct value of CPUID_EXT_PDCM before using feature_dependencies[] to apply dependencies. Besides, it also ensures CPUID_EXT_PDCM value is tracked in env->features[FEAT_1_ECX]. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250304052450.465445-2-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 40aefb38f6..29bce67c3a 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7349,9 +7349,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (threads_per_pkg > 1) { *ebx |= threads_per_pkg << 16; } - if (!cpu->enable_pmu) { - *ecx &= ~CPUID_EXT_PDCM; - } break; case 2: /* cache info: needed for Pentium Pro compatibility */ @@ -8341,6 +8338,10 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) } } + if (!cpu->enable_pmu) { + env->features[FEAT_1_ECX] &= ~CPUID_EXT_PDCM; + } + for (i = 0; i < ARRAY_SIZE(feature_dependencies); i++) { FeatureDep *d = &feature_dependencies[i]; if (!(env->features[d->from.index] & d->from.mask)) { From 00268e00027459abede448662f8794d78eb4b0a4 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Tue, 4 Mar 2025 00:24:50 -0500 Subject: [PATCH 1616/2760] i386/cpu: Warn about why CPUID_EXT_PDCM is not available When user requests PDCM explicitly via "+pdcm" without PMU enabled, emit a warning to inform the user. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250304052450.465445-3-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 29bce67c3a..0d35e95430 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8339,6 +8339,9 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) } if (!cpu->enable_pmu) { + mark_unavailable_features(cpu, FEAT_1_ECX, + env->user_features[FEAT_1_ECX] & CPUID_EXT_PDCM, + "This feature is not available due to PMU being disabled"); env->features[FEAT_1_ECX] &= ~CPUID_EXT_PDCM; } From 750560f8a832361cf5cc4cd7bc4f56e1e76206f6 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 12 Jun 2025 09:38:01 -0400 Subject: [PATCH 1617/2760] i386/tdx: Error and exit when named cpu model is requested Currently, it gets below error when requesting any named cpu model with "-cpu" to boot a TDX VM: qemu-system-x86_64: KVM_TDX_INIT_VM failed: Invalid argument It misleads people to think it's the bug of KVM or QEMU. It is just that current QEMU doesn't support named cpu model for TDX. To support named cpu models for TDX guest, there are opens to be finalized and needs a mount of additional work. For now, explicitly check the case when named cpu model is requested. Error report a hint and exit. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250612133801.2238342-1-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 820ca3614e..2b52de9d71 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -739,8 +739,14 @@ static int tdx_kvm_type(X86ConfidentialGuest *cg) static void tdx_cpu_instance_init(X86ConfidentialGuest *cg, CPUState *cpu) { + X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); X86CPU *x86cpu = X86_CPU(cpu); + if (xcc->model) { + error_report("Named cpu model is not supported for TDX yet!"); + exit(1); + } + object_property_set_bool(OBJECT(cpu), "pmu", false, &error_abort); /* invtsc is fixed1 for TD guest */ From 90d2bbd1f6edfa22a056070ee62ded55099cd56d Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Tue, 3 Jun 2025 01:03:03 -0400 Subject: [PATCH 1618/2760] i386/cpu: Rename enable_cpuid_0x1f to force_cpuid_0x1f MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The name of "enable_cpuid_0x1f" isn't right to its behavior because the leaf 0x1f can be enabled even when "enable_cpuid_0x1f" is false. Rename it to "force_cpuid_0x1f" to better reflect its behavior. Suggested-by: Igor Mammedov Signed-off-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Reviewed-by: Igor Mammedov Link: https://lore.kernel.org/r/20250603050305.1704586-2-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 4 ++-- target/i386/kvm/tdx.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 5910dcf74d..51e10139df 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2269,7 +2269,7 @@ struct ArchCPU { bool enable_cpuid_0xb; /* Force to enable cpuid 0x1f */ - bool enable_cpuid_0x1f; + bool force_cpuid_0x1f; /* Enable auto level-increase for all CPUID leaves */ bool full_cpuid_auto_level; @@ -2539,7 +2539,7 @@ void mark_forced_on_features(X86CPU *cpu, FeatureWord w, uint64_t mask, static inline bool x86_has_cpuid_0x1f(X86CPU *cpu) { - return cpu->enable_cpuid_0x1f || + return cpu->force_cpuid_0x1f || x86_has_extended_topo(cpu->env.avail_cpu_topo); } diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 2b52de9d71..acbe749754 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -752,7 +752,7 @@ static void tdx_cpu_instance_init(X86ConfidentialGuest *cg, CPUState *cpu) /* invtsc is fixed1 for TD guest */ object_property_set_bool(OBJECT(cpu), "invtsc", true, &error_abort); - x86cpu->enable_cpuid_0x1f = true; + x86cpu->force_cpuid_0x1f = true; } static uint32_t tdx_adjust_cpuid_features(X86ConfidentialGuest *cg, From a38da9f4876bb17d7ed9c6e24964b12b61877d38 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Tue, 3 Jun 2025 01:03:04 -0400 Subject: [PATCH 1619/2760] i386/tdx: Fix the typo of the comment of struct TdxGuest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change sha348 to sha384. Signed-off-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Reviewed-by: Igor Mammedov Link: https://lore.kernel.org/r/20250603050305.1704586-3-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index 04b5afe199..8dd66e9014 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -40,9 +40,9 @@ typedef struct TdxGuest { bool initialized; uint64_t attributes; /* TD attributes */ uint64_t xfam; - char *mrconfigid; /* base64 encoded sha348 digest */ - char *mrowner; /* base64 encoded sha348 digest */ - char *mrownerconfig; /* base64 encoded sha348 digest */ + char *mrconfigid; /* base64 encoded sha384 digest */ + char *mrowner; /* base64 encoded sha384 digest */ + char *mrownerconfig; /* base64 encoded sha384 digest */ MemoryRegion *tdvf_mr; TdxFirmware tdvf; From 41cd354d350d3c64915be9c5decbf20abd84e486 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Tue, 3 Jun 2025 01:03:05 -0400 Subject: [PATCH 1620/2760] i386/tdx: Clarify the error message of mrconfigid/mrowner/mrownerconfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The error message is misleading - we successfully decoded the data, the decoded data was simply with the wrong length. Change the error message to show it is an length check failure with both the received and expected values. Suggested-by: Daniel P. Berrangé Signed-off-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Reviewed-by: Igor Mammedov Link: https://lore.kernel.org/r/20250603050305.1704586-4-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index acbe749754..2284167141 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -1032,7 +1032,9 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) return -1; } if (data_len != QCRYPTO_HASH_DIGEST_LEN_SHA384) { - error_setg(errp, "TDX: failed to decode mrconfigid"); + error_setg(errp, "TDX 'mrconfigid' sha384 digest was %ld bytes, " + "expected %d bytes", data_len, + QCRYPTO_HASH_DIGEST_LEN_SHA384); return -1; } memcpy(init_vm->mrconfigid, data, data_len); @@ -1045,7 +1047,9 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) return -1; } if (data_len != QCRYPTO_HASH_DIGEST_LEN_SHA384) { - error_setg(errp, "TDX: failed to decode mrowner"); + error_setg(errp, "TDX 'mrowner' sha384 digest was %ld bytes, " + "expected %d bytes", data_len, + QCRYPTO_HASH_DIGEST_LEN_SHA384); return -1; } memcpy(init_vm->mrowner, data, data_len); @@ -1058,7 +1062,9 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp) return -1; } if (data_len != QCRYPTO_HASH_DIGEST_LEN_SHA384) { - error_setg(errp, "TDX: failed to decode mrownerconfig"); + error_setg(errp, "TDX 'mrownerconfig' sha384 digest was %ld bytes, " + "expected %d bytes", data_len, + QCRYPTO_HASH_DIGEST_LEN_SHA384); return -1; } memcpy(init_vm->mrownerconfig, data, data_len); From 688b0756ad2fdbe8effdb66f724a1129f62be7a2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 19 Jun 2025 20:09:19 +0200 Subject: [PATCH 1621/2760] update Linux headers to v6.16-rc3 Signed-off-by: Paolo Bonzini --- include/standard-headers/asm-x86/setup_data.h | 13 +- include/standard-headers/drm/drm_fourcc.h | 45 ++++++ include/standard-headers/linux/ethtool.h | 134 +++++++++--------- include/standard-headers/linux/fuse.h | 6 +- .../linux/input-event-codes.h | 3 +- include/standard-headers/linux/pci_regs.h | 12 +- include/standard-headers/linux/virtio_gpu.h | 3 +- include/standard-headers/linux/virtio_pci.h | 1 + linux-headers/asm-arm64/kvm.h | 9 +- linux-headers/asm-x86/kvm.h | 1 + linux-headers/linux/bits.h | 4 +- linux-headers/linux/kvm.h | 25 ++++ linux-headers/linux/vhost.h | 4 +- 13 files changed, 182 insertions(+), 78 deletions(-) diff --git a/include/standard-headers/asm-x86/setup_data.h b/include/standard-headers/asm-x86/setup_data.h index a483d72f42..2e446c1d85 100644 --- a/include/standard-headers/asm-x86/setup_data.h +++ b/include/standard-headers/asm-x86/setup_data.h @@ -13,7 +13,8 @@ #define SETUP_CC_BLOB 7 #define SETUP_IMA 8 #define SETUP_RNG_SEED 9 -#define SETUP_ENUM_MAX SETUP_RNG_SEED +#define SETUP_KEXEC_KHO 10 +#define SETUP_ENUM_MAX SETUP_KEXEC_KHO #define SETUP_INDIRECT (1<<31) #define SETUP_TYPE_MAX (SETUP_ENUM_MAX | SETUP_INDIRECT) @@ -78,6 +79,16 @@ struct ima_setup_data { uint64_t size; } QEMU_PACKED; +/* + * Locations of kexec handover metadata + */ +struct kho_data { + uint64_t fdt_addr; + uint64_t fdt_size; + uint64_t scratch_addr; + uint64_t scratch_size; +} QEMU_PACKED; + #endif /* __ASSEMBLER__ */ #endif /* _ASM_X86_SETUP_DATA_H */ diff --git a/include/standard-headers/drm/drm_fourcc.h b/include/standard-headers/drm/drm_fourcc.h index a8b759dcbc..c8309d378b 100644 --- a/include/standard-headers/drm/drm_fourcc.h +++ b/include/standard-headers/drm/drm_fourcc.h @@ -421,6 +421,7 @@ extern "C" { #define DRM_FORMAT_MOD_VENDOR_ALLWINNER 0x09 #define DRM_FORMAT_MOD_VENDOR_AMLOGIC 0x0a #define DRM_FORMAT_MOD_VENDOR_MTK 0x0b +#define DRM_FORMAT_MOD_VENDOR_APPLE 0x0c /* add more to the end as needed */ @@ -1493,6 +1494,50 @@ drm_fourcc_canonicalize_nvidia_format_mod(uint64_t modifier) /* alias for the most common tiling format */ #define DRM_FORMAT_MOD_MTK_16L_32S_TILE DRM_FORMAT_MOD_MTK(MTK_FMT_MOD_TILE_16L32S) +/* + * Apple GPU-tiled layouts. + * + * Apple GPUs support nonlinear tilings with optional lossless compression. + * + * GPU-tiled images are divided into 16KiB tiles: + * + * Bytes per pixel Tile size + * --------------- --------- + * 1 128x128 + * 2 128x64 + * 4 64x64 + * 8 64x32 + * 16 32x32 + * + * Tiles are raster-order. Pixels within a tile are interleaved (Morton order). + * + * Compressed images pad the body to 128-bytes and are immediately followed by a + * metadata section. The metadata section rounds the image dimensions to + * powers-of-two and contains 8 bytes for each 16x16 compression subtile. + * Subtiles are interleaved (Morton order). + * + * All images are 128-byte aligned. + * + * These layouts fundamentally do not have meaningful strides. No matter how we + * specify strides for these layouts, userspace unaware of Apple image layouts + * will be unable to use correctly the specified stride for any purpose. + * Userspace aware of the image layouts do not use strides. The most "correct" + * convention would be setting the image stride to 0. Unfortunately, some + * software assumes the stride is at least (width * bytes per pixel). We + * therefore require that stride equals (width * bytes per pixel). Since the + * stride is arbitrary here, we pick the simplest convention. + * + * Although containing two sections, compressed image layouts are treated in + * software as a single plane. This is modelled after AFBC, a similar + * scheme. Attempting to separate the sections to be "explicit" in DRM would + * only generate more confusion, as software does not treat the image this way. + * + * For detailed information on the hardware image layouts, see + * https://docs.mesa3d.org/drivers/asahi.html#image-layouts + */ +#define DRM_FORMAT_MOD_APPLE_GPU_TILED fourcc_mod_code(APPLE, 1) +#define DRM_FORMAT_MOD_APPLE_GPU_TILED_COMPRESSED fourcc_mod_code(APPLE, 2) + /* * AMD modifiers * diff --git a/include/standard-headers/linux/ethtool.h b/include/standard-headers/linux/ethtool.h index 5d1ad5fdea..cef0d207a6 100644 --- a/include/standard-headers/linux/ethtool.h +++ b/include/standard-headers/linux/ethtool.h @@ -2295,71 +2295,75 @@ static inline int ethtool_validate_duplex(uint8_t duplex) #define RXH_XFRM_SYM_OR_XOR (1 << 1) #define RXH_XFRM_NO_CHANGE 0xff -/* L2-L4 network traffic flow types */ -#define TCP_V4_FLOW 0x01 /* hash or spec (tcp_ip4_spec) */ -#define UDP_V4_FLOW 0x02 /* hash or spec (udp_ip4_spec) */ -#define SCTP_V4_FLOW 0x03 /* hash or spec (sctp_ip4_spec) */ -#define AH_ESP_V4_FLOW 0x04 /* hash only */ -#define TCP_V6_FLOW 0x05 /* hash or spec (tcp_ip6_spec; nfc only) */ -#define UDP_V6_FLOW 0x06 /* hash or spec (udp_ip6_spec; nfc only) */ -#define SCTP_V6_FLOW 0x07 /* hash or spec (sctp_ip6_spec; nfc only) */ -#define AH_ESP_V6_FLOW 0x08 /* hash only */ -#define AH_V4_FLOW 0x09 /* hash or spec (ah_ip4_spec) */ -#define ESP_V4_FLOW 0x0a /* hash or spec (esp_ip4_spec) */ -#define AH_V6_FLOW 0x0b /* hash or spec (ah_ip6_spec; nfc only) */ -#define ESP_V6_FLOW 0x0c /* hash or spec (esp_ip6_spec; nfc only) */ -#define IPV4_USER_FLOW 0x0d /* spec only (usr_ip4_spec) */ -#define IP_USER_FLOW IPV4_USER_FLOW -#define IPV6_USER_FLOW 0x0e /* spec only (usr_ip6_spec; nfc only) */ -#define IPV4_FLOW 0x10 /* hash only */ -#define IPV6_FLOW 0x11 /* hash only */ -#define ETHER_FLOW 0x12 /* spec only (ether_spec) */ - -/* Used for GTP-U IPv4 and IPv6. - * The format of GTP packets only includes - * elements such as TEID and GTP version. - * It is primarily intended for data communication of the UE. - */ -#define GTPU_V4_FLOW 0x13 /* hash only */ -#define GTPU_V6_FLOW 0x14 /* hash only */ - -/* Use for GTP-C IPv4 and v6. - * The format of these GTP packets does not include TEID. - * Primarily expected to be used for communication - * to create sessions for UE data communication, - * commonly referred to as CSR (Create Session Request). - */ -#define GTPC_V4_FLOW 0x15 /* hash only */ -#define GTPC_V6_FLOW 0x16 /* hash only */ - -/* Use for GTP-C IPv4 and v6. - * Unlike GTPC_V4_FLOW, the format of these GTP packets includes TEID. - * After session creation, it becomes this packet. - * This is mainly used for requests to realize UE handover. - */ -#define GTPC_TEID_V4_FLOW 0x17 /* hash only */ -#define GTPC_TEID_V6_FLOW 0x18 /* hash only */ - -/* Use for GTP-U and extended headers for the PSC (PDU Session Container). - * The format of these GTP packets includes TEID and QFI. - * In 5G communication using UPF (User Plane Function), - * data communication with this extended header is performed. - */ -#define GTPU_EH_V4_FLOW 0x19 /* hash only */ -#define GTPU_EH_V6_FLOW 0x1a /* hash only */ - -/* Use for GTP-U IPv4 and v6 PSC (PDU Session Container) extended headers. - * This differs from GTPU_EH_V(4|6)_FLOW in that it is distinguished by - * UL/DL included in the PSC. - * There are differences in the data included based on Downlink/Uplink, - * and can be used to distinguish packets. - * The functions described so far are useful when you want to - * handle communication from the mobile network in UPF, PGW, etc. - */ -#define GTPU_UL_V4_FLOW 0x1b /* hash only */ -#define GTPU_UL_V6_FLOW 0x1c /* hash only */ -#define GTPU_DL_V4_FLOW 0x1d /* hash only */ -#define GTPU_DL_V6_FLOW 0x1e /* hash only */ +enum { + /* L2-L4 network traffic flow types */ + TCP_V4_FLOW = 0x01, /* hash or spec (tcp_ip4_spec) */ + UDP_V4_FLOW = 0x02, /* hash or spec (udp_ip4_spec) */ + SCTP_V4_FLOW = 0x03, /* hash or spec (sctp_ip4_spec) */ + AH_ESP_V4_FLOW = 0x04, /* hash only */ + TCP_V6_FLOW = 0x05, /* hash or spec (tcp_ip6_spec; nfc only) */ + UDP_V6_FLOW = 0x06, /* hash or spec (udp_ip6_spec; nfc only) */ + SCTP_V6_FLOW = 0x07, /* hash or spec (sctp_ip6_spec; nfc only) */ + AH_ESP_V6_FLOW = 0x08, /* hash only */ + AH_V4_FLOW = 0x09, /* hash or spec (ah_ip4_spec) */ + ESP_V4_FLOW = 0x0a, /* hash or spec (esp_ip4_spec) */ + AH_V6_FLOW = 0x0b, /* hash or spec (ah_ip6_spec; nfc only) */ + ESP_V6_FLOW = 0x0c, /* hash or spec (esp_ip6_spec; nfc only) */ + IPV4_USER_FLOW = 0x0d, /* spec only (usr_ip4_spec) */ + IP_USER_FLOW = IPV4_USER_FLOW, + IPV6_USER_FLOW = 0x0e, /* spec only (usr_ip6_spec; nfc only) */ + IPV4_FLOW = 0x10, /* hash only */ + IPV6_FLOW = 0x11, /* hash only */ + ETHER_FLOW = 0x12, /* spec only (ether_spec) */ + + /* Used for GTP-U IPv4 and IPv6. + * The format of GTP packets only includes + * elements such as TEID and GTP version. + * It is primarily intended for data communication of the UE. + */ + GTPU_V4_FLOW = 0x13, /* hash only */ + GTPU_V6_FLOW = 0x14, /* hash only */ + + /* Use for GTP-C IPv4 and v6. + * The format of these GTP packets does not include TEID. + * Primarily expected to be used for communication + * to create sessions for UE data communication, + * commonly referred to as CSR (Create Session Request). + */ + GTPC_V4_FLOW = 0x15, /* hash only */ + GTPC_V6_FLOW = 0x16, /* hash only */ + + /* Use for GTP-C IPv4 and v6. + * Unlike GTPC_V4_FLOW, the format of these GTP packets includes TEID. + * After session creation, it becomes this packet. + * This is mainly used for requests to realize UE handover. + */ + GTPC_TEID_V4_FLOW = 0x17, /* hash only */ + GTPC_TEID_V6_FLOW = 0x18, /* hash only */ + + /* Use for GTP-U and extended headers for the PSC (PDU Session Container). + * The format of these GTP packets includes TEID and QFI. + * In 5G communication using UPF (User Plane Function), + * data communication with this extended header is performed. + */ + GTPU_EH_V4_FLOW = 0x19, /* hash only */ + GTPU_EH_V6_FLOW = 0x1a, /* hash only */ + + /* Use for GTP-U IPv4 and v6 PSC (PDU Session Container) extended headers. + * This differs from GTPU_EH_V(4|6)_FLOW in that it is distinguished by + * UL/DL included in the PSC. + * There are differences in the data included based on Downlink/Uplink, + * and can be used to distinguish packets. + * The functions described so far are useful when you want to + * handle communication from the mobile network in UPF, PGW, etc. + */ + GTPU_UL_V4_FLOW = 0x1b, /* hash only */ + GTPU_UL_V6_FLOW = 0x1c, /* hash only */ + GTPU_DL_V4_FLOW = 0x1d, /* hash only */ + GTPU_DL_V6_FLOW = 0x1e, /* hash only */ + + __FLOW_TYPE_COUNT, +}; /* Flag to enable additional fields in struct ethtool_rx_flow_spec */ #define FLOW_EXT 0x80000000 diff --git a/include/standard-headers/linux/fuse.h b/include/standard-headers/linux/fuse.h index a2b5815d89..d8b2fd67e1 100644 --- a/include/standard-headers/linux/fuse.h +++ b/include/standard-headers/linux/fuse.h @@ -232,6 +232,9 @@ * * 7.43 * - add FUSE_REQUEST_TIMEOUT + * + * 7.44 + * - add FUSE_NOTIFY_INC_EPOCH */ #ifndef _LINUX_FUSE_H @@ -263,7 +266,7 @@ #define FUSE_KERNEL_VERSION 7 /** Minor version number of this interface */ -#define FUSE_KERNEL_MINOR_VERSION 43 +#define FUSE_KERNEL_MINOR_VERSION 44 /** The node ID of the root inode */ #define FUSE_ROOT_ID 1 @@ -667,6 +670,7 @@ enum fuse_notify_code { FUSE_NOTIFY_RETRIEVE = 5, FUSE_NOTIFY_DELETE = 6, FUSE_NOTIFY_RESEND = 7, + FUSE_NOTIFY_INC_EPOCH = 8, FUSE_NOTIFY_CODE_MAX, }; diff --git a/include/standard-headers/linux/input-event-codes.h b/include/standard-headers/linux/input-event-codes.h index 09ba0ad878..a82ff795e0 100644 --- a/include/standard-headers/linux/input-event-codes.h +++ b/include/standard-headers/linux/input-event-codes.h @@ -925,7 +925,8 @@ #define SW_MUTE_DEVICE 0x0e /* set = device disabled */ #define SW_PEN_INSERTED 0x0f /* set = pen inserted */ #define SW_MACHINE_COVER 0x10 /* set = cover closed */ -#define SW_MAX_ 0x10 +#define SW_USB_INSERT 0x11 /* set = USB audio device connected */ +#define SW_MAX_ 0x11 #define SW_CNT (SW_MAX_+1) /* diff --git a/include/standard-headers/linux/pci_regs.h b/include/standard-headers/linux/pci_regs.h index ba326710f9..a3a3e942de 100644 --- a/include/standard-headers/linux/pci_regs.h +++ b/include/standard-headers/linux/pci_regs.h @@ -750,7 +750,8 @@ #define PCI_EXT_CAP_ID_NPEM 0x29 /* Native PCIe Enclosure Management */ #define PCI_EXT_CAP_ID_PL_32GT 0x2A /* Physical Layer 32.0 GT/s */ #define PCI_EXT_CAP_ID_DOE 0x2E /* Data Object Exchange */ -#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_DOE +#define PCI_EXT_CAP_ID_PL_64GT 0x31 /* Physical Layer 64.0 GT/s */ +#define PCI_EXT_CAP_ID_MAX PCI_EXT_CAP_ID_PL_64GT #define PCI_EXT_CAP_DSN_SIZEOF 12 #define PCI_EXT_CAP_MCAST_ENDPOINT_SIZEOF 40 @@ -1144,12 +1145,21 @@ #define PCI_DLF_CAP 0x04 /* Capabilities Register */ #define PCI_DLF_EXCHANGE_ENABLE 0x80000000 /* Data Link Feature Exchange Enable */ +/* Secondary PCIe Capability 8.0 GT/s */ +#define PCI_SECPCI_LE_CTRL 0x0c /* Lane Equalization Control Register */ + /* Physical Layer 16.0 GT/s */ #define PCI_PL_16GT_LE_CTRL 0x20 /* Lane Equalization Control Register */ #define PCI_PL_16GT_LE_CTRL_DSP_TX_PRESET_MASK 0x0000000F #define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_MASK 0x000000F0 #define PCI_PL_16GT_LE_CTRL_USP_TX_PRESET_SHIFT 4 +/* Physical Layer 32.0 GT/s */ +#define PCI_PL_32GT_LE_CTRL 0x20 /* Lane Equalization Control Register */ + +/* Physical Layer 64.0 GT/s */ +#define PCI_PL_64GT_LE_CTRL 0x20 /* Lane Equalization Control Register */ + /* Native PCIe Enclosure Management */ #define PCI_NPEM_CAP 0x04 /* NPEM capability register */ #define PCI_NPEM_CAP_CAPABLE 0x00000001 /* NPEM Capable */ diff --git a/include/standard-headers/linux/virtio_gpu.h b/include/standard-headers/linux/virtio_gpu.h index 6459fdb9fb..00cd3f04af 100644 --- a/include/standard-headers/linux/virtio_gpu.h +++ b/include/standard-headers/linux/virtio_gpu.h @@ -309,8 +309,9 @@ struct virtio_gpu_cmd_submit { #define VIRTIO_GPU_CAPSET_VIRGL 1 #define VIRTIO_GPU_CAPSET_VIRGL2 2 -/* 3 is reserved for gfxstream */ +#define VIRTIO_GPU_CAPSET_GFXSTREAM_VULKAN 3 #define VIRTIO_GPU_CAPSET_VENUS 4 +#define VIRTIO_GPU_CAPSET_CROSS_DOMAIN 5 #define VIRTIO_GPU_CAPSET_DRM 6 /* VIRTIO_GPU_CMD_GET_CAPSET_INFO */ diff --git a/include/standard-headers/linux/virtio_pci.h b/include/standard-headers/linux/virtio_pci.h index 91fec6f502..09e964e6ee 100644 --- a/include/standard-headers/linux/virtio_pci.h +++ b/include/standard-headers/linux/virtio_pci.h @@ -246,6 +246,7 @@ struct virtio_pci_cfg_cap { #define VIRTIO_ADMIN_CMD_LIST_USE 0x1 /* Admin command group type. */ +#define VIRTIO_ADMIN_GROUP_TYPE_SELF 0x0 #define VIRTIO_ADMIN_GROUP_TYPE_SRIOV 0x1 /* Transitional device admin command. */ diff --git a/linux-headers/asm-arm64/kvm.h b/linux-headers/asm-arm64/kvm.h index 4e6aff08df..f4d9baafa1 100644 --- a/linux-headers/asm-arm64/kvm.h +++ b/linux-headers/asm-arm64/kvm.h @@ -419,10 +419,11 @@ enum { /* Device Control API on vcpu fd */ #define KVM_ARM_VCPU_PMU_V3_CTRL 0 -#define KVM_ARM_VCPU_PMU_V3_IRQ 0 -#define KVM_ARM_VCPU_PMU_V3_INIT 1 -#define KVM_ARM_VCPU_PMU_V3_FILTER 2 -#define KVM_ARM_VCPU_PMU_V3_SET_PMU 3 +#define KVM_ARM_VCPU_PMU_V3_IRQ 0 +#define KVM_ARM_VCPU_PMU_V3_INIT 1 +#define KVM_ARM_VCPU_PMU_V3_FILTER 2 +#define KVM_ARM_VCPU_PMU_V3_SET_PMU 3 +#define KVM_ARM_VCPU_PMU_V3_SET_NR_COUNTERS 4 #define KVM_ARM_VCPU_TIMER_CTRL 1 #define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0 #define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index 7fb57ccb2a..cd275ae76d 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -843,6 +843,7 @@ struct kvm_sev_snp_launch_start { }; /* Kept in sync with firmware values for simplicity. */ +#define KVM_SEV_PAGE_TYPE_INVALID 0x0 #define KVM_SEV_SNP_PAGE_TYPE_NORMAL 0x1 #define KVM_SEV_SNP_PAGE_TYPE_ZERO 0x3 #define KVM_SEV_SNP_PAGE_TYPE_UNMEASURED 0x4 diff --git a/linux-headers/linux/bits.h b/linux-headers/linux/bits.h index 58596d18f4..9243f38975 100644 --- a/linux-headers/linux/bits.h +++ b/linux-headers/linux/bits.h @@ -4,9 +4,9 @@ #ifndef _LINUX_BITS_H #define _LINUX_BITS_H -#define __GENMASK(h, l) (((~_UL(0)) << (l)) & (~_UL(0) >> (BITS_PER_LONG - 1 - (h)))) +#define __GENMASK(h, l) (((~_UL(0)) << (l)) & (~_UL(0) >> (__BITS_PER_LONG - 1 - (h)))) -#define __GENMASK_ULL(h, l) (((~_ULL(0)) << (l)) & (~_ULL(0) >> (BITS_PER_LONG_LONG - 1 - (h)))) +#define __GENMASK_ULL(h, l) (((~_ULL(0)) << (l)) & (~_ULL(0) >> (__BITS_PER_LONG_LONG - 1 - (h)))) #define __GENMASK_U128(h, l) \ ((_BIT128((h)) << 1) - (_BIT128(l))) diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 99cc82a275..0690743944 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -178,6 +178,7 @@ struct kvm_xen_exit { #define KVM_EXIT_NOTIFY 37 #define KVM_EXIT_LOONGARCH_IOCSR 38 #define KVM_EXIT_MEMORY_FAULT 39 +#define KVM_EXIT_TDX 40 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -439,6 +440,27 @@ struct kvm_run { __u64 gpa; __u64 size; } memory_fault; + /* KVM_EXIT_TDX */ + struct { + __u64 flags; + __u64 nr; + union { + struct { + __u64 ret; + __u64 data[5]; + } unknown; + struct { + __u64 ret; + __u64 gpa; + __u64 size; + } get_quote; + struct { + __u64 ret; + __u64 leaf; + __u64 r11, r12, r13, r14; + } get_tdvmcall_info; + }; + } tdx; /* Fix the size of the union. */ char padding[256]; }; @@ -923,6 +945,9 @@ struct kvm_enable_cap { #define KVM_CAP_X86_APIC_BUS_CYCLES_NS 237 #define KVM_CAP_X86_GUEST_MODE 238 #define KVM_CAP_ARM_WRITABLE_IMP_ID_REGS 239 +#define KVM_CAP_ARM_EL2 240 +#define KVM_CAP_ARM_EL2_E2H0 241 +#define KVM_CAP_RISCV_MP_STATE_RESET 242 struct kvm_irq_routing_irqchip { __u32 irqchip; diff --git a/linux-headers/linux/vhost.h b/linux-headers/linux/vhost.h index b95dd84eef..d4b3e2ae13 100644 --- a/linux-headers/linux/vhost.h +++ b/linux-headers/linux/vhost.h @@ -28,10 +28,10 @@ /* Set current process as the (exclusive) owner of this file descriptor. This * must be called before any other vhost command. Further calls to - * VHOST_OWNER_SET fail until VHOST_OWNER_RESET is called. */ + * VHOST_SET_OWNER fail until VHOST_RESET_OWNER is called. */ #define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01) /* Give up ownership, and reset the device to default values. - * Allows subsequent call to VHOST_OWNER_SET to succeed. */ + * Allows subsequent call to VHOST_SET_OWNER to succeed. */ #define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02) /* Set up/modify memory layout */ From 427b8cf47a6959cd8b0db12bcf66e9009afa2c07 Mon Sep 17 00:00:00 2001 From: Binbin Wu Date: Wed, 30 Apr 2025 08:53:14 +0800 Subject: [PATCH 1622/2760] i386/tdx: handle TDG.VP.VMCALL Signed-off-by: Binbin Wu Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 12 ++++++++++++ target/i386/kvm/tdx-stub.c | 4 ++++ target/i386/kvm/tdx.c | 12 ++++++++++++ target/i386/kvm/tdx.h | 9 +++++++++ 4 files changed, 37 insertions(+) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 56a6b9b638..8ef29fc1fb 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -6170,6 +6170,18 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) break; } break; + case KVM_EXIT_TDX: + /* + * run->tdx is already set up for the case where userspace + * does not handle the TDVMCALL. + */ + switch (run->tdx.nr) { + case TDVMCALL_GET_TD_VM_CALL_INFO: + tdx_handle_get_tdvmcall_info(cpu, run); + break; + } + ret = 0; + break; default: fprintf(stderr, "KVM: unknown exit reason %d\n", run->exit_reason); ret = -1; diff --git a/target/i386/kvm/tdx-stub.c b/target/i386/kvm/tdx-stub.c index 720a4ff046..62a12a0677 100644 --- a/target/i386/kvm/tdx-stub.c +++ b/target/i386/kvm/tdx-stub.c @@ -18,3 +18,7 @@ int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) { return -EINVAL; } + +void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) +{ +} diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 2284167141..ef10a19347 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -1120,6 +1120,18 @@ int tdx_parse_tdvf(void *flash_ptr, int size) return tdvf_parse_metadata(&tdx_guest->tdvf, flash_ptr, size); } +void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) +{ + if (run->tdx.get_tdvmcall_info.leaf != 1) { + return; + } + + run->tdx.get_tdvmcall_info.r11 = 0; + run->tdx.get_tdvmcall_info.r12 = 0; + run->tdx.get_tdvmcall_info.r13 = 0; + run->tdx.get_tdvmcall_info.r14 = 0; +} + static void tdx_panicked_on_fatal_error(X86CPU *cpu, uint64_t error_code, char *message, uint64_t gpa) { diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index 8dd66e9014..0dd41d5811 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -21,6 +21,14 @@ typedef struct TdxGuestClass { /* TDX requires bus frequency 25MHz */ #define TDX_APIC_BUS_CYCLES_NS 40 +#define TDVMCALL_GET_TD_VM_CALL_INFO 0x10000 + +#define TDG_VP_VMCALL_SUCCESS 0x0000000000000000ULL +#define TDG_VP_VMCALL_RETRY 0x0000000000000001ULL +#define TDG_VP_VMCALL_INVALID_OPERAND 0x8000000000000000ULL +#define TDG_VP_VMCALL_GPA_INUSE 0x8000000000000001ULL +#define TDG_VP_VMCALL_ALIGN_ERROR 0x8000000000000002ULL + enum TdxRamType { TDX_RAM_UNACCEPTED, TDX_RAM_ADDED, @@ -61,5 +69,6 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp); void tdx_set_tdvf_region(MemoryRegion *tdvf_mr); int tdx_parse_tdvf(void *flash_ptr, int size); int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run); +void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run); #endif /* QEMU_I386_TDX_H */ From 40da501d8989913935660dc24953ece02c9e98b8 Mon Sep 17 00:00:00 2001 From: Isaku Yamahata Date: Mon, 28 Nov 2022 17:43:52 +0800 Subject: [PATCH 1623/2760] i386/tdx: handle TDG.VP.VMCALL Add property "quote-generation-socket" to tdx-guest, which is a property of type SocketAddress to specify Quote Generation Service(QGS). On request of GetQuote, it connects to the QGS socket, read request data from shared guest memory, send the request data to the QGS, and store the response into shared guest memory, at last notify TD guest by interrupt. command line example: qemu-system-x86_64 \ -object '{"qom-type":"tdx-guest","id":"tdx0","quote-generation-socket":{"type":"unix", "path":"/var/run/tdx-qgs/qgs.socket"}}' \ -machine confidential-guest-support=tdx0 Note, above example uses the unix socket. It can be other types, like vsock, which depends on the implementation of QGS. To avoid no response from QGS server, setup a timer for the transaction. If timeout, make it an error and interrupt guest. Define the threshold of time to 30s at present, maybe change to other value if not appropriate. Signed-off-by: Isaku Yamahata Co-developed-by: Chenyi Qiang Signed-off-by: Chenyi Qiang Co-developed-by: Xiaoyao Li Signed-off-by: Xiaoyao Li Tested-by: Xiaoyao Li Signed-off-by: Paolo Bonzini --- qapi/qom.json | 8 +- target/i386/kvm/kvm.c | 3 + target/i386/kvm/meson.build | 2 +- target/i386/kvm/tdx-quote-generator.c | 300 ++++++++++++++++++++++++++ target/i386/kvm/tdx-quote-generator.h | 82 +++++++ target/i386/kvm/tdx-stub.c | 4 + target/i386/kvm/tdx.c | 176 ++++++++++++++- target/i386/kvm/tdx.h | 10 + 8 files changed, 582 insertions(+), 3 deletions(-) create mode 100644 target/i386/kvm/tdx-quote-generator.c create mode 100644 target/i386/kvm/tdx-quote-generator.h diff --git a/qapi/qom.json b/qapi/qom.json index 3e8debf78c..b133b06447 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -1071,6 +1071,11 @@ # e.g., specific to the workload rather than the run-time or OS # (base64 encoded SHA384 digest). Defaults to all zeros. # +# @quote-generation-socket: socket address for Quote Generation +# Service (QGS). QGS is a daemon running on the host. Without +# it, the guest will not be able to get a TD quote for +# attestation. +# # Since: 10.1 ## { 'struct': 'TdxGuestProperties', @@ -1078,7 +1083,8 @@ '*sept-ve-disable': 'bool', '*mrconfigid': 'str', '*mrowner': 'str', - '*mrownerconfig': 'str' } } + '*mrownerconfig': 'str', + '*quote-generation-socket': 'SocketAddress' } } ## # @ThreadContextProperties: diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 8ef29fc1fb..234878c613 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -6176,6 +6176,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) * does not handle the TDVMCALL. */ switch (run->tdx.nr) { + case TDVMCALL_GET_QUOTE: + tdx_handle_get_quote(cpu, run); + break; case TDVMCALL_GET_TD_VM_CALL_INFO: tdx_handle_get_tdvmcall_info(cpu, run); break; diff --git a/target/i386/kvm/meson.build b/target/i386/kvm/meson.build index 3f44cdedb7..2675bf8902 100644 --- a/target/i386/kvm/meson.build +++ b/target/i386/kvm/meson.build @@ -8,7 +8,7 @@ i386_kvm_ss.add(files( i386_kvm_ss.add(when: 'CONFIG_XEN_EMU', if_true: files('xen-emu.c')) -i386_kvm_ss.add(when: 'CONFIG_TDX', if_true: files('tdx.c'), if_false: files('tdx-stub.c')) +i386_kvm_ss.add(when: 'CONFIG_TDX', if_true: files('tdx.c', 'tdx-quote-generator.c'), if_false: files('tdx-stub.c')) i386_system_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c'), if_false: files('hyperv-stub.c')) diff --git a/target/i386/kvm/tdx-quote-generator.c b/target/i386/kvm/tdx-quote-generator.c new file mode 100644 index 0000000000..f59715f617 --- /dev/null +++ b/target/i386/kvm/tdx-quote-generator.c @@ -0,0 +1,300 @@ +/* + * QEMU TDX Quote Generation Support + * + * Copyright (c) 2025 Intel Corporation + * + * Author: + * Xiaoyao Li + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qapi/error.h" +#include "qapi/qapi-visit-sockets.h" + +#include "tdx-quote-generator.h" + +#define QGS_MSG_LIB_MAJOR_VER 1 +#define QGS_MSG_LIB_MINOR_VER 1 + +typedef enum _qgs_msg_type_t { + GET_QUOTE_REQ = 0, + GET_QUOTE_RESP = 1, + GET_COLLATERAL_REQ = 2, + GET_COLLATERAL_RESP = 3, + GET_PLATFORM_INFO_REQ = 4, + GET_PLATFORM_INFO_RESP = 5, + QGS_MSG_TYPE_MAX +} qgs_msg_type_t; + +typedef struct _qgs_msg_header_t { + uint16_t major_version; + uint16_t minor_version; + uint32_t type; + uint32_t size; // size of the whole message, include this header, in byte + uint32_t error_code; // used in response only +} qgs_msg_header_t; + +typedef struct _qgs_msg_get_quote_req_t { + qgs_msg_header_t header; // header.type = GET_QUOTE_REQ + uint32_t report_size; // cannot be 0 + uint32_t id_list_size; // length of id_list, in byte, can be 0 +} qgs_msg_get_quote_req_t; + +typedef struct _qgs_msg_get_quote_resp_s { + qgs_msg_header_t header; // header.type = GET_QUOTE_RESP + uint32_t selected_id_size; // can be 0 in case only one id is sent in request + uint32_t quote_size; // length of quote_data, in byte + uint8_t id_quote[]; // selected id followed by quote +} qgs_msg_get_quote_resp_t; + +#define HEADER_SIZE 4 + +static uint32_t decode_header(const char *buf, size_t len) { + if (len < HEADER_SIZE) { + return 0; + } + uint32_t msg_size = 0; + for (uint32_t i = 0; i < HEADER_SIZE; ++i) { + msg_size = msg_size * 256 + (buf[i] & 0xFF); + } + return msg_size; +} + +static void encode_header(char *buf, size_t len, uint32_t size) { + assert(len >= HEADER_SIZE); + buf[0] = ((size >> 24) & 0xFF); + buf[1] = ((size >> 16) & 0xFF); + buf[2] = ((size >> 8) & 0xFF); + buf[3] = (size & 0xFF); +} + +static void tdx_generate_quote_cleanup(TdxGenerateQuoteTask *task) +{ + timer_del(&task->timer); + + g_source_remove(task->watch); + qio_channel_close(QIO_CHANNEL(task->sioc), NULL); + object_unref(OBJECT(task->sioc)); + + task->completion(task); +} + +static gboolean tdx_get_quote_read(QIOChannel *ioc, GIOCondition condition, + gpointer opaque) +{ + TdxGenerateQuoteTask *task = opaque; + Error *err = NULL; + int ret; + + ret = qio_channel_read(ioc, task->receive_buf + task->receive_buf_received, + task->payload_len - task->receive_buf_received, &err); + if (ret < 0) { + if (ret == QIO_CHANNEL_ERR_BLOCK) { + return G_SOURCE_CONTINUE; + } else { + error_report_err(err); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + } + + if (ret == 0) { + error_report("End of file before reply received"); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + + task->receive_buf_received += ret; + if (task->receive_buf_received >= HEADER_SIZE) { + uint32_t len = decode_header(task->receive_buf, + task->receive_buf_received); + if (len == 0 || + len > (task->payload_len - HEADER_SIZE)) { + error_report("Message len %u must be non-zero & less than %zu", + len, (task->payload_len - HEADER_SIZE)); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + + /* Now we know the size, shrink to fit */ + task->payload_len = HEADER_SIZE + len; + task->receive_buf = g_renew(char, + task->receive_buf, + task->payload_len); + } + + if (task->receive_buf_received >= (sizeof(qgs_msg_header_t) + HEADER_SIZE)) { + qgs_msg_header_t *hdr = (qgs_msg_header_t *)(task->receive_buf + HEADER_SIZE); + if (hdr->major_version != QGS_MSG_LIB_MAJOR_VER || + hdr->minor_version != QGS_MSG_LIB_MINOR_VER) { + error_report("Invalid QGS message header version %d.%d", + hdr->major_version, + hdr->minor_version); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + if (hdr->type != GET_QUOTE_RESP) { + error_report("Invalid QGS message type %d", + hdr->type); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + if (hdr->size > (task->payload_len - HEADER_SIZE)) { + error_report("QGS message size %d exceeds payload capacity %zu", + hdr->size, task->payload_len); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + if (hdr->error_code != 0) { + error_report("QGS message error code %d", + hdr->error_code); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + } + if (task->receive_buf_received >= (sizeof(qgs_msg_get_quote_resp_t) + HEADER_SIZE)) { + qgs_msg_get_quote_resp_t *msg = (qgs_msg_get_quote_resp_t *)(task->receive_buf + HEADER_SIZE); + if (msg->selected_id_size != 0) { + error_report("QGS message selected ID was %d not 0", + msg->selected_id_size); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + + if ((task->payload_len - HEADER_SIZE - sizeof(qgs_msg_get_quote_resp_t)) != + msg->quote_size) { + error_report("QGS quote size %d should be %zu", + msg->quote_size, + (task->payload_len - sizeof(qgs_msg_get_quote_resp_t))); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + goto end; + } + } + + if (task->receive_buf_received == task->payload_len) { + size_t strip = HEADER_SIZE + sizeof(qgs_msg_get_quote_resp_t); + memmove(task->receive_buf, + task->receive_buf + strip, + task->receive_buf_received - strip); + task->receive_buf_received -= strip; + task->status_code = TDX_VP_GET_QUOTE_SUCCESS; + goto end; + } + + return G_SOURCE_CONTINUE; + +end: + tdx_generate_quote_cleanup(task); + return G_SOURCE_REMOVE; +} + +static gboolean tdx_send_report(QIOChannel *ioc, GIOCondition condition, + gpointer opaque) +{ + TdxGenerateQuoteTask *task = opaque; + Error *err = NULL; + int ret; + + ret = qio_channel_write(ioc, task->send_data + task->send_data_sent, + task->send_data_size - task->send_data_sent, &err); + if (ret < 0) { + if (ret == QIO_CHANNEL_ERR_BLOCK) { + ret = 0; + } else { + error_report_err(err); + task->status_code = TDX_VP_GET_QUOTE_ERROR; + tdx_generate_quote_cleanup(task); + goto end; + } + } + task->send_data_sent += ret; + + if (task->send_data_sent == task->send_data_size) { + task->watch = qio_channel_add_watch(QIO_CHANNEL(task->sioc), G_IO_IN, + tdx_get_quote_read, task, NULL); + goto end; + } + + return G_SOURCE_CONTINUE; + +end: + return G_SOURCE_REMOVE; +} + +static void tdx_quote_generator_connected(QIOTask *qio_task, gpointer opaque) +{ + TdxGenerateQuoteTask *task = opaque; + Error *err = NULL; + int ret; + + ret = qio_task_propagate_error(qio_task, &err); + if (ret) { + error_report_err(err); + task->status_code = TDX_VP_GET_QUOTE_QGS_UNAVAILABLE; + tdx_generate_quote_cleanup(task); + return; + } + + task->watch = qio_channel_add_watch(QIO_CHANNEL(task->sioc), G_IO_OUT, + tdx_send_report, task, NULL); +} + +#define TRANSACTION_TIMEOUT 30000 + +static void getquote_expired(void *opaque) +{ + TdxGenerateQuoteTask *task = opaque; + + task->status_code = TDX_VP_GET_QUOTE_ERROR; + tdx_generate_quote_cleanup(task); +} + +static void setup_get_quote_timer(TdxGenerateQuoteTask *task) +{ + int64_t time; + + timer_init_ms(&task->timer, QEMU_CLOCK_VIRTUAL, getquote_expired, task); + time = qemu_clock_get_ms(QEMU_CLOCK_VIRTUAL); + timer_mod(&task->timer, time + TRANSACTION_TIMEOUT); +} + +void tdx_generate_quote(TdxGenerateQuoteTask *task, + SocketAddress *qg_sock_addr) +{ + QIOChannelSocket *sioc; + qgs_msg_get_quote_req_t msg; + + /* Prepare a QGS message prelude */ + msg.header.major_version = QGS_MSG_LIB_MAJOR_VER; + msg.header.minor_version = QGS_MSG_LIB_MINOR_VER; + msg.header.type = GET_QUOTE_REQ; + msg.header.size = sizeof(msg) + task->send_data_size; + msg.header.error_code = 0; + msg.report_size = task->send_data_size; + msg.id_list_size = 0; + + /* Make room to add the QGS message prelude */ + task->send_data = g_renew(char, + task->send_data, + task->send_data_size + sizeof(msg) + HEADER_SIZE); + memmove(task->send_data + sizeof(msg) + HEADER_SIZE, + task->send_data, + task->send_data_size); + memcpy(task->send_data + HEADER_SIZE, + &msg, + sizeof(msg)); + encode_header(task->send_data, HEADER_SIZE, task->send_data_size + sizeof(msg)); + task->send_data_size += sizeof(msg) + HEADER_SIZE; + + sioc = qio_channel_socket_new(); + task->sioc = sioc; + + setup_get_quote_timer(task); + + qio_channel_socket_connect_async(sioc, qg_sock_addr, + tdx_quote_generator_connected, task, + NULL, NULL); +} diff --git a/target/i386/kvm/tdx-quote-generator.h b/target/i386/kvm/tdx-quote-generator.h new file mode 100644 index 0000000000..3bd9b8ef33 --- /dev/null +++ b/target/i386/kvm/tdx-quote-generator.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef QEMU_I386_TDX_QUOTE_GENERATOR_H +#define QEMU_I386_TDX_QUOTE_GENERATOR_H + +#include "qom/object_interfaces.h" +#include "io/channel-socket.h" +#include "exec/hwaddr.h" + +#define TDX_GET_QUOTE_STRUCTURE_VERSION 1ULL + +#define TDX_VP_GET_QUOTE_SUCCESS 0ULL +#define TDX_VP_GET_QUOTE_IN_FLIGHT (-1ULL) +#define TDX_VP_GET_QUOTE_ERROR 0x8000000000000000ULL +#define TDX_VP_GET_QUOTE_QGS_UNAVAILABLE 0x8000000000000001ULL + +/* Limit to avoid resource starvation. */ +#define TDX_GET_QUOTE_MAX_BUF_LEN (128 * 1024) +#define TDX_MAX_GET_QUOTE_REQUEST 16 + +#define TDX_GET_QUOTE_HDR_SIZE 24 + +/* Format of pages shared with guest. */ +struct tdx_get_quote_header { + /* Format version: must be 1 in little endian. */ + uint64_t structure_version; + + /* + * GetQuote status code in little endian: + * Guest must set error_code to 0 to avoid information leak. + * Qemu sets this before interrupting guest. + */ + uint64_t error_code; + + /* + * in-message size in little endian: The message will follow this header. + * The in-message will be send to QGS. + */ + uint32_t in_len; + + /* + * out-message size in little endian: + * On request, out_len must be zero to avoid information leak. + * On return, message size from QGS. Qemu overwrites this field. + * The message will follows this header. The in-message is overwritten. + */ + uint32_t out_len; + + /* + * Message buffer follows. + * Guest sets message that will be send to QGS. If out_len > in_len, guest + * should zero remaining buffer to avoid information leak. + * Qemu overwrites this buffer with a message returned from QGS. + */ +}; + +typedef struct TdxGenerateQuoteTask { + hwaddr buf_gpa; + hwaddr payload_gpa; + uint64_t payload_len; + + char *send_data; + uint64_t send_data_size; + uint64_t send_data_sent; + + char *receive_buf; + uint64_t receive_buf_received; + + uint64_t status_code; + struct tdx_get_quote_header hdr; + + QIOChannelSocket *sioc; + guint watch; + QEMUTimer timer; + + void (*completion)(struct TdxGenerateQuoteTask *task); + void *opaque; +} TdxGenerateQuoteTask; + +void tdx_generate_quote(TdxGenerateQuoteTask *task, SocketAddress *qg_sock_addr); + +#endif /* QEMU_I386_TDX_QUOTE_GENERATOR_H */ diff --git a/target/i386/kvm/tdx-stub.c b/target/i386/kvm/tdx-stub.c index 62a12a0677..76fee49eff 100644 --- a/target/i386/kvm/tdx-stub.c +++ b/target/i386/kvm/tdx-stub.c @@ -19,6 +19,10 @@ int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) return -EINVAL; } +void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run) +{ +} + void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) { } diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index ef10a19347..e809e4b2df 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -14,12 +14,14 @@ #include "qemu/base64.h" #include "qemu/mmap-alloc.h" #include "qapi/error.h" +#include "qapi/qapi-visit-sockets.h" #include "qom/object_interfaces.h" #include "crypto/hash.h" #include "system/kvm_int.h" #include "system/runstate.h" #include "system/system.h" #include "system/ramblock.h" +#include "system/address-spaces.h" #include @@ -32,6 +34,7 @@ #include "hw/i386/tdvf-hob.h" #include "kvm_i386.h" #include "tdx.h" +#include "tdx-quote-generator.h" #include "standard-headers/asm-x86/kvm_para.h" @@ -1120,13 +1123,146 @@ int tdx_parse_tdvf(void *flash_ptr, int size) return tdvf_parse_metadata(&tdx_guest->tdvf, flash_ptr, size); } +static void tdx_get_quote_completion(TdxGenerateQuoteTask *task) +{ + TdxGuest *tdx = task->opaque; + int ret; + + /* Maintain the number of in-flight requests. */ + qemu_mutex_lock(&tdx->lock); + tdx->num--; + qemu_mutex_unlock(&tdx->lock); + + if (task->status_code == TDX_VP_GET_QUOTE_SUCCESS) { + ret = address_space_write(&address_space_memory, task->payload_gpa, + MEMTXATTRS_UNSPECIFIED, task->receive_buf, + task->receive_buf_received); + if (ret != MEMTX_OK) { + error_report("TDX: get-quote: failed to write quote data."); + } else { + task->hdr.out_len = cpu_to_le64(task->receive_buf_received); + } + } + task->hdr.error_code = cpu_to_le64(task->status_code); + + /* Publish the response contents before marking this request completed. */ + smp_wmb(); + ret = address_space_write(&address_space_memory, task->buf_gpa, + MEMTXATTRS_UNSPECIFIED, &task->hdr, + TDX_GET_QUOTE_HDR_SIZE); + if (ret != MEMTX_OK) { + error_report("TDX: get-quote: failed to update GetQuote header."); + } + + g_free(task->send_data); + g_free(task->receive_buf); + g_free(task); + object_unref(tdx); +} + +void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run) +{ + TdxGenerateQuoteTask *task; + struct tdx_get_quote_header hdr; + hwaddr buf_gpa = run->tdx.get_quote.gpa; + uint64_t buf_len = run->tdx.get_quote.size; + + QEMU_BUILD_BUG_ON(sizeof(struct tdx_get_quote_header) != TDX_GET_QUOTE_HDR_SIZE); + + run->tdx.get_quote.ret = TDG_VP_VMCALL_INVALID_OPERAND; + + if (buf_len == 0) { + return; + } + + if (!QEMU_IS_ALIGNED(buf_gpa, 4096) || !QEMU_IS_ALIGNED(buf_len, 4096)) { + run->tdx.get_quote.ret = TDG_VP_VMCALL_ALIGN_ERROR; + return; + } + + if (address_space_read(&address_space_memory, buf_gpa, MEMTXATTRS_UNSPECIFIED, + &hdr, TDX_GET_QUOTE_HDR_SIZE) != MEMTX_OK) { + error_report("TDX: get-quote: failed to read GetQuote header."); + return; + } + + if (le64_to_cpu(hdr.structure_version) != TDX_GET_QUOTE_STRUCTURE_VERSION) { + return; + } + + /* Only safe-guard check to avoid too large buffer size. */ + if (buf_len > TDX_GET_QUOTE_MAX_BUF_LEN || + le32_to_cpu(hdr.in_len) > buf_len - TDX_GET_QUOTE_HDR_SIZE) { + return; + } + + if (!tdx_guest->qg_sock_addr) { + hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_QGS_UNAVAILABLE); + if (address_space_write(&address_space_memory, buf_gpa, + MEMTXATTRS_UNSPECIFIED, + &hdr, TDX_GET_QUOTE_HDR_SIZE) != MEMTX_OK) { + error_report("TDX: failed to update GetQuote header."); + return; + } + run->tdx.get_quote.ret = TDG_VP_VMCALL_SUCCESS; + return; + } + + qemu_mutex_lock(&tdx_guest->lock); + if (tdx_guest->num >= TDX_MAX_GET_QUOTE_REQUEST) { + qemu_mutex_unlock(&tdx_guest->lock); + run->tdx.get_quote.ret = TDG_VP_VMCALL_RETRY; + return; + } + tdx_guest->num++; + qemu_mutex_unlock(&tdx_guest->lock); + + task = g_new(TdxGenerateQuoteTask, 1); + task->buf_gpa = buf_gpa; + task->payload_gpa = buf_gpa + TDX_GET_QUOTE_HDR_SIZE; + task->payload_len = buf_len - TDX_GET_QUOTE_HDR_SIZE; + task->hdr = hdr; + task->completion = tdx_get_quote_completion; + + task->send_data_size = le32_to_cpu(hdr.in_len); + task->send_data = g_malloc(task->send_data_size); + task->send_data_sent = 0; + + if (address_space_read(&address_space_memory, task->payload_gpa, + MEMTXATTRS_UNSPECIFIED, task->send_data, + task->send_data_size) != MEMTX_OK) { + goto out_free; + } + + /* Mark the buffer in-flight. */ + hdr.error_code = cpu_to_le64(TDX_VP_GET_QUOTE_IN_FLIGHT); + if (address_space_write(&address_space_memory, buf_gpa, + MEMTXATTRS_UNSPECIFIED, + &hdr, TDX_GET_QUOTE_HDR_SIZE) != MEMTX_OK) { + goto out_free; + } + + task->receive_buf = g_malloc0(task->payload_len); + task->receive_buf_received = 0; + task->opaque = tdx_guest; + + object_ref(tdx_guest); + tdx_generate_quote(task, tdx_guest->qg_sock_addr); + run->tdx.get_quote.ret = TDG_VP_VMCALL_SUCCESS; + return; + +out_free: + g_free(task->send_data); + g_free(task); +} + void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) { if (run->tdx.get_tdvmcall_info.leaf != 1) { return; } - run->tdx.get_tdvmcall_info.r11 = 0; + run->tdx.get_tdvmcall_info.r11 = TDG_VP_VMCALL_SUBFUNC_GET_QUOTE; run->tdx.get_tdvmcall_info.r12 = 0; run->tdx.get_tdvmcall_info.r13 = 0; run->tdx.get_tdvmcall_info.r14 = 0; @@ -1263,6 +1399,37 @@ static void tdx_guest_set_mrownerconfig(Object *obj, const char *value, Error ** tdx->mrownerconfig = g_strdup(value); } +static void tdx_guest_get_qgs(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + + if (!tdx->qg_sock_addr) { + error_setg(errp, "quote-generation-socket is not set"); + return; + } + visit_type_SocketAddress(v, name, &tdx->qg_sock_addr, errp); +} + +static void tdx_guest_set_qgs(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + TdxGuest *tdx = TDX_GUEST(obj); + SocketAddress *sock = NULL; + + if (!visit_type_SocketAddress(v, name, &sock, errp)) { + return; + } + + if (tdx->qg_sock_addr) { + qapi_free_SocketAddress(tdx->qg_sock_addr); + } + + tdx->qg_sock_addr = sock; +} + /* tdx guest */ OBJECT_DEFINE_TYPE_WITH_INTERFACES(TdxGuest, tdx_guest, @@ -1294,6 +1461,13 @@ static void tdx_guest_init(Object *obj) object_property_add_str(obj, "mrownerconfig", tdx_guest_get_mrownerconfig, tdx_guest_set_mrownerconfig); + + object_property_add(obj, "quote-generation-socket", "SocketAddress", + tdx_guest_get_qgs, + tdx_guest_set_qgs, + NULL, NULL); + + qemu_mutex_init(&tdx->lock); } static void tdx_guest_finalize(Object *obj) diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index 0dd41d5811..35a09c19c5 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -11,6 +11,8 @@ #include "cpu.h" #include "hw/i386/tdvf.h" +#include "tdx-quote-generator.h" + #define TYPE_TDX_GUEST "tdx-guest" #define TDX_GUEST(obj) OBJECT_CHECK(TdxGuest, (obj), TYPE_TDX_GUEST) @@ -22,6 +24,7 @@ typedef struct TdxGuestClass { #define TDX_APIC_BUS_CYCLES_NS 40 #define TDVMCALL_GET_TD_VM_CALL_INFO 0x10000 +#define TDVMCALL_GET_QUOTE 0x10002 #define TDG_VP_VMCALL_SUCCESS 0x0000000000000000ULL #define TDG_VP_VMCALL_RETRY 0x0000000000000001ULL @@ -29,6 +32,8 @@ typedef struct TdxGuestClass { #define TDG_VP_VMCALL_GPA_INUSE 0x8000000000000001ULL #define TDG_VP_VMCALL_ALIGN_ERROR 0x8000000000000002ULL +#define TDG_VP_VMCALL_SUBFUNC_GET_QUOTE 0x0000000000000001ULL + enum TdxRamType { TDX_RAM_UNACCEPTED, TDX_RAM_ADDED, @@ -57,6 +62,10 @@ typedef struct TdxGuest { uint32_t nr_ram_entries; TdxRamEntry *ram_entries; + + /* GetQuote */ + SocketAddress *qg_sock_addr; + int num; } TdxGuest; #ifdef CONFIG_TDX @@ -69,6 +78,7 @@ int tdx_pre_create_vcpu(CPUState *cpu, Error **errp); void tdx_set_tdvf_region(MemoryRegion *tdvf_mr); int tdx_parse_tdvf(void *flash_ptr, int size); int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run); +void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run); void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run); #endif /* QEMU_I386_TDX_H */ From e14236b30bc5c65ccf37daa63b8258ebb9a839f9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 6 Mar 2024 11:44:59 -1000 Subject: [PATCH 1624/2760] tcg: Add dbase argument to do_dup_store Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/tcg-op-gvec.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index d32a4f146d..1aad7b0864 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -483,8 +483,8 @@ static TCGType choose_vector_type(const TCGOpcode *list, unsigned vece, return 0; } -static void do_dup_store(TCGType type, uint32_t dofs, uint32_t oprsz, - uint32_t maxsz, TCGv_vec t_vec) +static void do_dup_store(TCGType type, TCGv_ptr dbase, uint32_t dofs, + uint32_t oprsz, uint32_t maxsz, TCGv_vec t_vec) { uint32_t i = 0; @@ -496,7 +496,7 @@ static void do_dup_store(TCGType type, uint32_t dofs, uint32_t oprsz, * are misaligned wrt the maximum vector size, so do that first. */ if (dofs & 8) { - tcg_gen_stl_vec(t_vec, tcg_env, dofs + i, TCG_TYPE_V64); + tcg_gen_stl_vec(t_vec, dbase, dofs + i, TCG_TYPE_V64); i += 8; } @@ -508,17 +508,17 @@ static void do_dup_store(TCGType type, uint32_t dofs, uint32_t oprsz, * that e.g. size == 80 would be expanded with 2x32 + 1x16. */ for (; i + 32 <= oprsz; i += 32) { - tcg_gen_stl_vec(t_vec, tcg_env, dofs + i, TCG_TYPE_V256); + tcg_gen_stl_vec(t_vec, dbase, dofs + i, TCG_TYPE_V256); } /* fallthru */ case TCG_TYPE_V128: for (; i + 16 <= oprsz; i += 16) { - tcg_gen_stl_vec(t_vec, tcg_env, dofs + i, TCG_TYPE_V128); + tcg_gen_stl_vec(t_vec, dbase, dofs + i, TCG_TYPE_V128); } break; case TCG_TYPE_V64: for (; i < oprsz; i += 8) { - tcg_gen_stl_vec(t_vec, tcg_env, dofs + i, TCG_TYPE_V64); + tcg_gen_stl_vec(t_vec, dbase, dofs + i, TCG_TYPE_V64); } break; default: @@ -574,7 +574,7 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, } else { tcg_gen_dupi_vec(vece, t_vec, in_c); } - do_dup_store(type, dofs, oprsz, maxsz, t_vec); + do_dup_store(type, tcg_env, dofs, oprsz, maxsz, t_vec); return; } @@ -1731,7 +1731,7 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, if (type != 0) { TCGv_vec t_vec = tcg_temp_new_vec(type); tcg_gen_dup_mem_vec(vece, t_vec, tcg_env, aofs); - do_dup_store(type, dofs, oprsz, maxsz, t_vec); + do_dup_store(type, tcg_env, dofs, oprsz, maxsz, t_vec); } else if (vece <= MO_32) { TCGv_i32 in = tcg_temp_ebb_new_i32(); switch (vece) { From 731422ebbdaebd4a88c9df4072e19dee2ead70fe Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 6 Mar 2024 11:50:41 -1000 Subject: [PATCH 1625/2760] tcg: Add dbase argument to do_dup Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/tcg-op-gvec.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 1aad7b0864..b100dd66ab 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -530,13 +530,14 @@ static void do_dup_store(TCGType type, TCGv_ptr dbase, uint32_t dofs, } } -/* Set OPRSZ bytes at DOFS to replications of IN_32, IN_64 or IN_C. +/* + * Set OPRSZ bytes at DBASE + DOFS to replications of IN_32, IN_64 or IN_C. * Only one of IN_32 or IN_64 may be set; * IN_C is used if IN_32 and IN_64 are unset. */ -static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, - uint32_t maxsz, TCGv_i32 in_32, TCGv_i64 in_64, - uint64_t in_c) +static void do_dup(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + uint32_t oprsz, uint32_t maxsz, + TCGv_i32 in_32, TCGv_i64 in_64, uint64_t in_c) { TCGType type; TCGv_i64 t_64; @@ -574,7 +575,7 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, } else { tcg_gen_dupi_vec(vece, t_vec, in_c); } - do_dup_store(type, tcg_env, dofs, oprsz, maxsz, t_vec); + do_dup_store(type, dbase, dofs, oprsz, maxsz, t_vec); return; } @@ -618,14 +619,14 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, /* Implement inline if we picked an implementation size above. */ if (t_32) { for (i = 0; i < oprsz; i += 4) { - tcg_gen_st_i32(t_32, tcg_env, dofs + i); + tcg_gen_st_i32(t_32, dbase, dofs + i); } tcg_temp_free_i32(t_32); goto done; } if (t_64) { for (i = 0; i < oprsz; i += 8) { - tcg_gen_st_i64(t_64, tcg_env, dofs + i); + tcg_gen_st_i64(t_64, dbase, dofs + i); } tcg_temp_free_i64(t_64); goto done; @@ -634,7 +635,7 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, /* Otherwise implement out of line. */ t_ptr = tcg_temp_ebb_new_ptr(); - tcg_gen_addi_ptr(t_ptr, tcg_env, dofs); + tcg_gen_addi_ptr(t_ptr, dbase, dofs); /* * This may be expand_clr for the tail of an operation, e.g. @@ -710,7 +711,7 @@ static void do_dup(unsigned vece, uint32_t dofs, uint32_t oprsz, /* Likewise, but with zero. */ static void expand_clr(uint32_t dofs, uint32_t maxsz) { - do_dup(MO_8, dofs, maxsz, maxsz, NULL, NULL, 0); + do_dup(MO_8, tcg_env, dofs, maxsz, maxsz, NULL, NULL, 0); } /* Expand OPSZ bytes worth of two-operand operations using i32 elements. */ @@ -1711,7 +1712,7 @@ void tcg_gen_gvec_dup_i32(unsigned vece, uint32_t dofs, uint32_t oprsz, { check_size_align(oprsz, maxsz, dofs); tcg_debug_assert(vece <= MO_32); - do_dup(vece, dofs, oprsz, maxsz, in, NULL, 0); + do_dup(vece, tcg_env, dofs, oprsz, maxsz, in, NULL, 0); } void tcg_gen_gvec_dup_i64(unsigned vece, uint32_t dofs, uint32_t oprsz, @@ -1719,7 +1720,7 @@ void tcg_gen_gvec_dup_i64(unsigned vece, uint32_t dofs, uint32_t oprsz, { check_size_align(oprsz, maxsz, dofs); tcg_debug_assert(vece <= MO_64); - do_dup(vece, dofs, oprsz, maxsz, NULL, in, 0); + do_dup(vece, tcg_env, dofs, oprsz, maxsz, NULL, in, 0); } void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -1745,12 +1746,12 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, tcg_gen_ld_i32(in, tcg_env, aofs); break; } - do_dup(vece, dofs, oprsz, maxsz, in, NULL, 0); + do_dup(vece, tcg_env, dofs, oprsz, maxsz, in, NULL, 0); tcg_temp_free_i32(in); } else { TCGv_i64 in = tcg_temp_ebb_new_i64(); tcg_gen_ld_i64(in, tcg_env, aofs); - do_dup(vece, dofs, oprsz, maxsz, NULL, in, 0); + do_dup(vece, tcg_env, dofs, oprsz, maxsz, NULL, in, 0); tcg_temp_free_i64(in); } } else if (vece == 4) { @@ -1833,7 +1834,7 @@ void tcg_gen_gvec_dup_imm(unsigned vece, uint32_t dofs, uint32_t oprsz, uint32_t maxsz, uint64_t x) { check_size_align(oprsz, maxsz, dofs); - do_dup(vece, dofs, oprsz, maxsz, NULL, NULL, x); + do_dup(vece, tcg_env, dofs, oprsz, maxsz, NULL, NULL, x); } void tcg_gen_gvec_not(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -3772,7 +3773,7 @@ void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, check_overlap_3(dofs, aofs, bofs, maxsz); if (cond == TCG_COND_NEVER || cond == TCG_COND_ALWAYS) { - do_dup(MO_8, dofs, oprsz, maxsz, + do_dup(MO_8, tcg_env, dofs, oprsz, maxsz, NULL, NULL, -(cond == TCG_COND_ALWAYS)); return; } @@ -3892,7 +3893,7 @@ void tcg_gen_gvec_cmps(TCGCond cond, unsigned vece, uint32_t dofs, check_overlap_2(dofs, aofs, maxsz); if (cond == TCG_COND_NEVER || cond == TCG_COND_ALWAYS) { - do_dup(MO_8, dofs, oprsz, maxsz, + do_dup(MO_8, tcg_env, dofs, oprsz, maxsz, NULL, NULL, -(cond == TCG_COND_ALWAYS)); return; } From ca09b6b5e556f343b8a7bfd8913c51786844830e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 6 Mar 2024 11:56:27 -1000 Subject: [PATCH 1626/2760] tcg: Add dbase argument to expand_clr Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/tcg-op-gvec.c | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index b100dd66ab..f5edadb992 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -380,7 +380,7 @@ static inline bool check_size_impl(uint32_t oprsz, uint32_t lnsz) return q <= MAX_UNROLL; } -static void expand_clr(uint32_t dofs, uint32_t maxsz); +static void expand_clr(TCGv_ptr dbase, uint32_t dofs, uint32_t maxsz); /* Duplicate C as per VECE. */ uint64_t (dup_const)(unsigned vece, uint64_t c) @@ -526,7 +526,7 @@ static void do_dup_store(TCGType type, TCGv_ptr dbase, uint32_t dofs, } if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(dbase, dofs + oprsz, maxsz - oprsz); } } @@ -704,14 +704,14 @@ static void do_dup(unsigned vece, TCGv_ptr dbase, uint32_t dofs, done: if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(dbase, dofs + oprsz, maxsz - oprsz); } } /* Likewise, but with zero. */ -static void expand_clr(uint32_t dofs, uint32_t maxsz) +static void expand_clr(TCGv_ptr dbase, uint32_t dofs, uint32_t maxsz) { - do_dup(MO_8, tcg_env, dofs, maxsz, maxsz, NULL, NULL, 0); + do_dup(MO_8, dbase, dofs, maxsz, maxsz, NULL, NULL, 0); } /* Expand OPSZ bytes worth of two-operand operations using i32 elements. */ @@ -1256,7 +1256,7 @@ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -1325,7 +1325,7 @@ void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -1402,7 +1402,7 @@ void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, } if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -1468,7 +1468,7 @@ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -1537,7 +1537,7 @@ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -1606,7 +1606,7 @@ void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -1675,7 +1675,7 @@ void tcg_gen_gvec_4i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -1702,7 +1702,7 @@ void tcg_gen_gvec_mov(unsigned vece, uint32_t dofs, uint32_t aofs, } else { check_size_align(oprsz, maxsz, dofs); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } } @@ -1780,7 +1780,7 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, tcg_temp_free_i64(in1); } if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } else if (vece == 5) { /* 256-bit duplicate. */ @@ -1823,7 +1823,7 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, } } if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } else { g_assert_not_reached(); @@ -3256,7 +3256,7 @@ do_gvec_shifts(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_i32 shift, clear_tail: if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -3835,7 +3835,7 @@ void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } @@ -3976,7 +3976,7 @@ void tcg_gen_gvec_cmps(TCGCond cond, unsigned vece, uint32_t dofs, } if (oprsz < maxsz) { - expand_clr(dofs + oprsz, maxsz - oprsz); + expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); } } From 872dab5b7ec4722aaa7791820425e460466ddf71 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 6 Mar 2024 13:41:03 -1000 Subject: [PATCH 1627/2760] tcg: Add base arguments to check_overlap_[234] Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- tcg/tcg-op-gvec.c | 61 ++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 25 deletions(-) diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index f5edadb992..0e6b42476e 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -57,30 +57,39 @@ static void check_size_align(uint32_t oprsz, uint32_t maxsz, uint32_t ofs) tcg_debug_assert((ofs & max_align) == 0); } -/* Verify vector overlap rules for two operands. */ -static void check_overlap_2(uint32_t d, uint32_t a, uint32_t s) +/* + * Verify vector overlap rules for two operands. + * When dbase and abase are not the same pointer, we cannot check for + * overlap at compile-time, but the runtime restrictions remain. + */ +static void check_overlap_2(TCGv_ptr dbase, uint32_t d, + TCGv_ptr abase, uint32_t a, uint32_t s) { - tcg_debug_assert(d == a || d + s <= a || a + s <= d); + tcg_debug_assert(dbase != abase || d == a || d + s <= a || a + s <= d); } /* Verify vector overlap rules for three operands. */ -static void check_overlap_3(uint32_t d, uint32_t a, uint32_t b, uint32_t s) +static void check_overlap_3(TCGv_ptr dbase, uint32_t d, + TCGv_ptr abase, uint32_t a, + TCGv_ptr bbase, uint32_t b, uint32_t s) { - check_overlap_2(d, a, s); - check_overlap_2(d, b, s); - check_overlap_2(a, b, s); + check_overlap_2(dbase, d, abase, a, s); + check_overlap_2(dbase, d, bbase, b, s); + check_overlap_2(abase, a, bbase, b, s); } /* Verify vector overlap rules for four operands. */ -static void check_overlap_4(uint32_t d, uint32_t a, uint32_t b, - uint32_t c, uint32_t s) +static void check_overlap_4(TCGv_ptr dbase, uint32_t d, + TCGv_ptr abase, uint32_t a, + TCGv_ptr bbase, uint32_t b, + TCGv_ptr cbase, uint32_t c, uint32_t s) { - check_overlap_2(d, a, s); - check_overlap_2(d, b, s); - check_overlap_2(d, c, s); - check_overlap_2(a, b, s); - check_overlap_2(a, c, s); - check_overlap_2(b, c, s); + check_overlap_2(dbase, d, abase, a, s); + check_overlap_2(dbase, d, bbase, b, s); + check_overlap_2(dbase, d, cbase, c, s); + check_overlap_2(abase, a, bbase, b, s); + check_overlap_2(abase, a, cbase, c, s); + check_overlap_2(bbase, b, cbase, c, s); } /* Create a descriptor from components. */ @@ -1206,7 +1215,7 @@ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs); - check_overlap_2(dofs, aofs, maxsz); + check_overlap_2(tcg_env, dofs, tcg_env, aofs, maxsz); type = 0; if (g->fniv) { @@ -1270,7 +1279,7 @@ void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs); - check_overlap_2(dofs, aofs, maxsz); + check_overlap_2(tcg_env, dofs, tcg_env, aofs, maxsz); type = 0; if (g->fniv) { @@ -1336,7 +1345,7 @@ void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, TCGType type; check_size_align(oprsz, maxsz, dofs | aofs); - check_overlap_2(dofs, aofs, maxsz); + check_overlap_2(tcg_env, dofs, tcg_env, aofs, maxsz); type = 0; if (g->fniv) { @@ -1416,7 +1425,7 @@ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs | bofs); - check_overlap_3(dofs, aofs, bofs, maxsz); + check_overlap_3(tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, maxsz); type = 0; if (g->fniv) { @@ -1483,7 +1492,7 @@ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs | bofs); - check_overlap_3(dofs, aofs, bofs, maxsz); + check_overlap_3(tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, maxsz); type = 0; if (g->fniv) { @@ -1551,7 +1560,8 @@ void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs | bofs | cofs); - check_overlap_4(dofs, aofs, bofs, cofs, maxsz); + check_overlap_4(tcg_env, dofs, tcg_env, aofs, + tcg_env, bofs, tcg_env, cofs, maxsz); type = 0; if (g->fniv) { @@ -1621,7 +1631,8 @@ void tcg_gen_gvec_4i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs | bofs | cofs); - check_overlap_4(dofs, aofs, bofs, cofs, maxsz); + check_overlap_4(tcg_env, dofs, tcg_env, aofs, + tcg_env, bofs, tcg_env, cofs, maxsz); type = 0; if (g->fniv) { @@ -3150,7 +3161,7 @@ do_gvec_shifts(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_i32 shift, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs); - check_overlap_2(dofs, aofs, maxsz); + check_overlap_2(tcg_env, dofs, tcg_env, aofs, maxsz); /* If the backend has a scalar expansion, great. */ type = choose_vector_type(g->s_list, vece, oprsz, vece == MO_64); @@ -3770,7 +3781,7 @@ void tcg_gen_gvec_cmp(TCGCond cond, unsigned vece, uint32_t dofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs | bofs); - check_overlap_3(dofs, aofs, bofs, maxsz); + check_overlap_3(tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, maxsz); if (cond == TCG_COND_NEVER || cond == TCG_COND_ALWAYS) { do_dup(MO_8, tcg_env, dofs, oprsz, maxsz, @@ -3890,7 +3901,7 @@ void tcg_gen_gvec_cmps(TCGCond cond, unsigned vece, uint32_t dofs, TCGType type; check_size_align(oprsz, maxsz, dofs | aofs); - check_overlap_2(dofs, aofs, maxsz); + check_overlap_2(tcg_env, dofs, tcg_env, aofs, maxsz); if (cond == TCG_COND_NEVER || cond == TCG_COND_ALWAYS) { do_dup(MO_8, tcg_env, dofs, oprsz, maxsz, From 7a74c13468e71a56147b75c2881ce3edaf7fbc19 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 6 Mar 2024 12:10:15 -1000 Subject: [PATCH 1628/2760] tcg: Split out tcg_gen_gvec_2_var Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/tcg/tcg-op-gvec-common.h | 14 ++++-- tcg/tcg-op-gvec.c | 85 ++++++++++++++++++++------------ 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/include/tcg/tcg-op-gvec-common.h b/include/tcg/tcg-op-gvec-common.h index 65553f5f97..f00af047c4 100644 --- a/include/tcg/tcg-op-gvec-common.h +++ b/include/tcg/tcg-op-gvec-common.h @@ -227,12 +227,20 @@ typedef struct { bool prefer_i64; } GVecGen4i; +/* Expand (dbase+dofs) = op(abase+aofs), length @oprsz, clearing to @maxsz. */ +void tcg_gen_gvec_2_var(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen2 *op); +/* Similarly, expand (env+dofs) = op(env+aofs). */ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen2 *); + uint32_t oprsz, uint32_t maxsz, const GVecGen2 *op); +/* Similarly, expand (env+dofs) = op(env+aofs, c). */ void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, - uint32_t maxsz, int64_t c, const GVecGen2i *); + uint32_t maxsz, int64_t c, const GVecGen2i *op); +/* Similarly, expand (env+dofs) = op(env+aofs, s). */ void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, - uint32_t maxsz, TCGv_i64 c, const GVecGen2s *); + uint32_t maxsz, TCGv_i64 c, const GVecGen2s *op); + void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz, const GVecGen3 *); void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 0e6b42476e..ec926bdcd7 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -133,9 +133,10 @@ uint32_t simd_desc(uint32_t oprsz, uint32_t maxsz, int32_t data) } /* Generate a call to a gvec-style helper with two vector operands. */ -void tcg_gen_gvec_2_ool(uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz, int32_t data, - gen_helper_gvec_2 *fn) +static void expand_2_ool(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, + int32_t data, gen_helper_gvec_2 *fn) { TCGv_ptr a0, a1; TCGv_i32 desc = tcg_constant_i32(simd_desc(oprsz, maxsz, data)); @@ -143,8 +144,8 @@ void tcg_gen_gvec_2_ool(uint32_t dofs, uint32_t aofs, a0 = tcg_temp_ebb_new_ptr(); a1 = tcg_temp_ebb_new_ptr(); - tcg_gen_addi_ptr(a0, tcg_env, dofs); - tcg_gen_addi_ptr(a1, tcg_env, aofs); + tcg_gen_addi_ptr(a0, dbase, dofs); + tcg_gen_addi_ptr(a1, abase, aofs); fn(a0, a1, desc); @@ -152,6 +153,13 @@ void tcg_gen_gvec_2_ool(uint32_t dofs, uint32_t aofs, tcg_temp_free_ptr(a1); } +void tcg_gen_gvec_2_ool(uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, int32_t data, + gen_helper_gvec_2 *fn) +{ + expand_2_ool(tcg_env, dofs, tcg_env, aofs, oprsz, maxsz, data, fn); +} + /* Generate a call to a gvec-style helper with two vector operands and one scalar operand. */ void tcg_gen_gvec_2i_ool(uint32_t dofs, uint32_t aofs, TCGv_i64 c, @@ -724,20 +732,21 @@ static void expand_clr(TCGv_ptr dbase, uint32_t dofs, uint32_t maxsz) } /* Expand OPSZ bytes worth of two-operand operations using i32 elements. */ -static void expand_2_i32(uint32_t dofs, uint32_t aofs, uint32_t oprsz, - bool load_dest, void (*fni)(TCGv_i32, TCGv_i32)) +static void expand_2_i32(TCGv_ptr dbase, uint32_t dofs, TCGv_ptr abase, + uint32_t aofs, uint32_t oprsz, bool load_dest, + void (*fni)(TCGv_i32, TCGv_i32)) { TCGv_i32 t0 = tcg_temp_new_i32(); TCGv_i32 t1 = tcg_temp_new_i32(); uint32_t i; for (i = 0; i < oprsz; i += 4) { - tcg_gen_ld_i32(t0, tcg_env, aofs + i); + tcg_gen_ld_i32(t0, abase, aofs + i); if (load_dest) { - tcg_gen_ld_i32(t1, tcg_env, dofs + i); + tcg_gen_ld_i32(t1, dbase, dofs + i); } fni(t1, t0); - tcg_gen_st_i32(t1, tcg_env, dofs + i); + tcg_gen_st_i32(t1, dbase, dofs + i); } tcg_temp_free_i32(t0); tcg_temp_free_i32(t1); @@ -887,20 +896,21 @@ static void expand_4i_i32(uint32_t dofs, uint32_t aofs, uint32_t bofs, } /* Expand OPSZ bytes worth of two-operand operations using i64 elements. */ -static void expand_2_i64(uint32_t dofs, uint32_t aofs, uint32_t oprsz, - bool load_dest, void (*fni)(TCGv_i64, TCGv_i64)) +static void expand_2_i64(TCGv_ptr dbase, uint32_t dofs, TCGv_ptr abase, + uint32_t aofs, uint32_t oprsz, bool load_dest, + void (*fni)(TCGv_i64, TCGv_i64)) { TCGv_i64 t0 = tcg_temp_new_i64(); TCGv_i64 t1 = tcg_temp_new_i64(); uint32_t i; for (i = 0; i < oprsz; i += 8) { - tcg_gen_ld_i64(t0, tcg_env, aofs + i); + tcg_gen_ld_i64(t0, abase, aofs + i); if (load_dest) { - tcg_gen_ld_i64(t1, tcg_env, dofs + i); + tcg_gen_ld_i64(t1, dbase, dofs + i); } fni(t1, t0); - tcg_gen_st_i64(t1, tcg_env, dofs + i); + tcg_gen_st_i64(t1, dbase, dofs + i); } tcg_temp_free_i64(t0); tcg_temp_free_i64(t1); @@ -1050,7 +1060,8 @@ static void expand_4i_i64(uint32_t dofs, uint32_t aofs, uint32_t bofs, } /* Expand OPSZ bytes worth of two-operand operations using host vectors. */ -static void expand_2_vec(unsigned vece, uint32_t dofs, uint32_t aofs, +static void expand_2_vec(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, uint32_t oprsz, uint32_t tysz, TCGType type, bool load_dest, void (*fni)(unsigned, TCGv_vec, TCGv_vec)) @@ -1059,12 +1070,12 @@ static void expand_2_vec(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_vec t0 = tcg_temp_new_vec(type); TCGv_vec t1 = tcg_temp_new_vec(type); - tcg_gen_ld_vec(t0, tcg_env, aofs + i); + tcg_gen_ld_vec(t0, abase, aofs + i); if (load_dest) { - tcg_gen_ld_vec(t1, tcg_env, dofs + i); + tcg_gen_ld_vec(t1, dbase, dofs + i); } fni(vece, t1, t0); - tcg_gen_st_vec(t1, tcg_env, dofs + i); + tcg_gen_st_vec(t1, dbase, dofs + i); } } @@ -1206,8 +1217,9 @@ static void expand_4i_vec(unsigned vece, uint32_t dofs, uint32_t aofs, } /* Expand a vector two-operand operation. */ -void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen2 *g) +void tcg_gen_gvec_2_var(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen2 *g) { const TCGOpcode *this_list = g->opt_opc ? : vecop_list_empty; const TCGOpcode *hold_list = tcg_swap_vecop_list(this_list); @@ -1215,7 +1227,7 @@ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs); - check_overlap_2(tcg_env, dofs, tcg_env, aofs, maxsz); + check_overlap_2(dbase, dofs, abase, aofs, maxsz); type = 0; if (g->fniv) { @@ -1228,8 +1240,8 @@ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, * that e.g. size == 80 would be expanded with 2x32 + 1x16. */ some = QEMU_ALIGN_DOWN(oprsz, 32); - expand_2_vec(g->vece, dofs, aofs, some, 32, TCG_TYPE_V256, - g->load_dest, g->fniv); + expand_2_vec(g->vece, dbase, dofs, abase, aofs, some, 32, + TCG_TYPE_V256, g->load_dest, g->fniv); if (some == oprsz) { break; } @@ -1239,22 +1251,25 @@ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, maxsz -= some; /* fallthru */ case TCG_TYPE_V128: - expand_2_vec(g->vece, dofs, aofs, oprsz, 16, TCG_TYPE_V128, - g->load_dest, g->fniv); + expand_2_vec(g->vece, dbase, dofs, abase, aofs, oprsz, 16, + TCG_TYPE_V128, g->load_dest, g->fniv); break; case TCG_TYPE_V64: - expand_2_vec(g->vece, dofs, aofs, oprsz, 8, TCG_TYPE_V64, - g->load_dest, g->fniv); + expand_2_vec(g->vece, dbase, dofs, abase, aofs, oprsz, 8, + TCG_TYPE_V64, g->load_dest, g->fniv); break; case 0: if (g->fni8 && check_size_impl(oprsz, 8)) { - expand_2_i64(dofs, aofs, oprsz, g->load_dest, g->fni8); + expand_2_i64(dbase, dofs, abase, aofs, + oprsz, g->load_dest, g->fni8); } else if (g->fni4 && check_size_impl(oprsz, 4)) { - expand_2_i32(dofs, aofs, oprsz, g->load_dest, g->fni4); + expand_2_i32(dbase, dofs, abase, aofs, + oprsz, g->load_dest, g->fni4); } else { assert(g->fno != NULL); - tcg_gen_gvec_2_ool(dofs, aofs, oprsz, maxsz, g->data, g->fno); + expand_2_ool(dbase, dofs, abase, aofs, + oprsz, maxsz, g->data, g->fno); oprsz = maxsz; } break; @@ -1265,10 +1280,16 @@ void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); + expand_clr(dbase, dofs + oprsz, maxsz - oprsz); } } +void tcg_gen_gvec_2(uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen2 *g) +{ + tcg_gen_gvec_2_var(tcg_env, dofs, tcg_env, aofs, oprsz, maxsz, g); +} + /* Expand a vector operation with two vectors and an immediate. */ void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t maxsz, int64_t c, const GVecGen2i *g) From 465b21ffbef3de1888d535b37a2ad0b27438580e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 6 Mar 2024 13:24:31 -1000 Subject: [PATCH 1629/2760] tcg: Split out tcg_gen_gvec_3_var Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/tcg/tcg-op-gvec-common.h | 33 +++++++++- tcg/tcg-op-gvec.c | 102 +++++++++++++++++++------------ 2 files changed, 95 insertions(+), 40 deletions(-) diff --git a/include/tcg/tcg-op-gvec-common.h b/include/tcg/tcg-op-gvec-common.h index f00af047c4..29bcedb737 100644 --- a/include/tcg/tcg-op-gvec-common.h +++ b/include/tcg/tcg-op-gvec-common.h @@ -241,13 +241,42 @@ void tcg_gen_gvec_2i(uint32_t dofs, uint32_t aofs, uint32_t oprsz, void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t maxsz, TCGv_i64 c, const GVecGen2s *op); +/* + * Expand (dbase+dofs) = op(abase+aofs, bbase+bofs), + * length @oprsz, clearing to @maxsz. + */ +void tcg_gen_gvec_3_var(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen3 *op); +/* Similarly, expand (env+dofs) = op(env+aofs, env+bofs). */ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen3 *); + uint32_t oprsz, uint32_t maxsz, const GVecGen3 *op); + +/* + * Depending on op->load_dest and op->write_aofs, expand + * (env+dofs) = op(env+aofs, env+bofs, c) + * or + * (env+dofs) = op(env+dofs, env+aofs, env+bofs, c) + * or + * (env+dofs), (env+aofs) = op(env+aofs, env+bofs, c) + * or + * (env+dofs), (env+aofs) = op(env+dofs, env+aofs, env+bofs, c) + */ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz, int64_t c, - const GVecGen3i *); + const GVecGen3i *op); + +/* + * Depending on op->write_aofs, expand + * (env+dofs) = op(env+aofs, env+bofs, env+cofs) + * or + * (env+dofs), (env+aofs) = op(env+aofs, env+bofs, env+cofs) + */ void tcg_gen_gvec_4(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, uint32_t oprsz, uint32_t maxsz, const GVecGen4 *); + +/* Expand (env+dofs) = op(env+aofs, env+bofs, env+cofs, c). */ void tcg_gen_gvec_4i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, uint32_t oprsz, uint32_t maxsz, int64_t c, const GVecGen4i *); diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index ec926bdcd7..862cecf1bf 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -182,9 +182,11 @@ void tcg_gen_gvec_2i_ool(uint32_t dofs, uint32_t aofs, TCGv_i64 c, } /* Generate a call to a gvec-style helper with three vector operands. */ -void tcg_gen_gvec_3_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz, int32_t data, - gen_helper_gvec_3 *fn) +static void expand_3_ool(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, + int32_t data, gen_helper_gvec_3 *fn) { TCGv_ptr a0, a1, a2; TCGv_i32 desc = tcg_constant_i32(simd_desc(oprsz, maxsz, data)); @@ -193,9 +195,9 @@ void tcg_gen_gvec_3_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, a1 = tcg_temp_ebb_new_ptr(); a2 = tcg_temp_ebb_new_ptr(); - tcg_gen_addi_ptr(a0, tcg_env, dofs); - tcg_gen_addi_ptr(a1, tcg_env, aofs); - tcg_gen_addi_ptr(a2, tcg_env, bofs); + tcg_gen_addi_ptr(a0, dbase, dofs); + tcg_gen_addi_ptr(a1, abase, aofs); + tcg_gen_addi_ptr(a2, bbase, bofs); fn(a0, a1, a2, desc); @@ -204,6 +206,14 @@ void tcg_gen_gvec_3_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, tcg_temp_free_ptr(a2); } +void tcg_gen_gvec_3_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, int32_t data, + gen_helper_gvec_3 *fn) +{ + expand_3_ool(tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, + oprsz, maxsz, data, fn); +} + /* Generate a call to a gvec-style helper with four vector operands. */ void tcg_gen_gvec_4_ool(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, uint32_t oprsz, uint32_t maxsz, @@ -794,8 +804,10 @@ static void expand_2s_i32(uint32_t dofs, uint32_t aofs, uint32_t oprsz, } /* Expand OPSZ bytes worth of three-operand operations using i32 elements. */ -static void expand_3_i32(uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, bool load_dest, +static void expand_3_i32(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, bool load_dest, void (*fni)(TCGv_i32, TCGv_i32, TCGv_i32)) { TCGv_i32 t0 = tcg_temp_new_i32(); @@ -804,13 +816,13 @@ static void expand_3_i32(uint32_t dofs, uint32_t aofs, uint32_t i; for (i = 0; i < oprsz; i += 4) { - tcg_gen_ld_i32(t0, tcg_env, aofs + i); - tcg_gen_ld_i32(t1, tcg_env, bofs + i); + tcg_gen_ld_i32(t0, abase, aofs + i); + tcg_gen_ld_i32(t1, bbase, bofs + i); if (load_dest) { - tcg_gen_ld_i32(t2, tcg_env, dofs + i); + tcg_gen_ld_i32(t2, dbase, dofs + i); } fni(t2, t0, t1); - tcg_gen_st_i32(t2, tcg_env, dofs + i); + tcg_gen_st_i32(t2, dbase, dofs + i); } tcg_temp_free_i32(t2); tcg_temp_free_i32(t1); @@ -958,8 +970,10 @@ static void expand_2s_i64(uint32_t dofs, uint32_t aofs, uint32_t oprsz, } /* Expand OPSZ bytes worth of three-operand operations using i64 elements. */ -static void expand_3_i64(uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, bool load_dest, +static void expand_3_i64(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, bool load_dest, void (*fni)(TCGv_i64, TCGv_i64, TCGv_i64)) { TCGv_i64 t0 = tcg_temp_new_i64(); @@ -968,13 +982,13 @@ static void expand_3_i64(uint32_t dofs, uint32_t aofs, uint32_t i; for (i = 0; i < oprsz; i += 8) { - tcg_gen_ld_i64(t0, tcg_env, aofs + i); - tcg_gen_ld_i64(t1, tcg_env, bofs + i); + tcg_gen_ld_i64(t0, abase, aofs + i); + tcg_gen_ld_i64(t1, bbase, bofs + i); if (load_dest) { - tcg_gen_ld_i64(t2, tcg_env, dofs + i); + tcg_gen_ld_i64(t2, dbase, dofs + i); } fni(t2, t0, t1); - tcg_gen_st_i64(t2, tcg_env, dofs + i); + tcg_gen_st_i64(t2, dbase, dofs + i); } tcg_temp_free_i64(t2); tcg_temp_free_i64(t1); @@ -1119,8 +1133,9 @@ static void expand_2s_vec(unsigned vece, uint32_t dofs, uint32_t aofs, } /* Expand OPSZ bytes worth of three-operand operations using host vectors. */ -static void expand_3_vec(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, +static void expand_3_vec(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, uint32_t oprsz, uint32_t tysz, TCGType type, bool load_dest, void (*fni)(unsigned, TCGv_vec, TCGv_vec, TCGv_vec)) { @@ -1129,13 +1144,13 @@ static void expand_3_vec(unsigned vece, uint32_t dofs, uint32_t aofs, TCGv_vec t1 = tcg_temp_new_vec(type); TCGv_vec t2 = tcg_temp_new_vec(type); - tcg_gen_ld_vec(t0, tcg_env, aofs + i); - tcg_gen_ld_vec(t1, tcg_env, bofs + i); + tcg_gen_ld_vec(t0, abase, aofs + i); + tcg_gen_ld_vec(t1, bbase, bofs + i); if (load_dest) { - tcg_gen_ld_vec(t2, tcg_env, dofs + i); + tcg_gen_ld_vec(t2, dbase, dofs + i); } fni(vece, t2, t0, t1); - tcg_gen_st_vec(t2, tcg_env, dofs + i); + tcg_gen_st_vec(t2, dbase, dofs + i); } } @@ -1437,8 +1452,10 @@ void tcg_gen_gvec_2s(uint32_t dofs, uint32_t aofs, uint32_t oprsz, } /* Expand a vector three-operand operation. */ -void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, - uint32_t oprsz, uint32_t maxsz, const GVecGen3 *g) +void tcg_gen_gvec_3_var(TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen3 *g) { const TCGOpcode *this_list = g->opt_opc ? : vecop_list_empty; const TCGOpcode *hold_list = tcg_swap_vecop_list(this_list); @@ -1446,7 +1463,7 @@ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t some; check_size_align(oprsz, maxsz, dofs | aofs | bofs); - check_overlap_3(tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, maxsz); + check_overlap_3(dbase, dofs, abase, aofs, bbase, bofs, maxsz); type = 0; if (g->fniv) { @@ -1459,8 +1476,8 @@ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, * that e.g. size == 80 would be expanded with 2x32 + 1x16. */ some = QEMU_ALIGN_DOWN(oprsz, 32); - expand_3_vec(g->vece, dofs, aofs, bofs, some, 32, TCG_TYPE_V256, - g->load_dest, g->fniv); + expand_3_vec(g->vece, dbase, dofs, abase, aofs, bbase, bofs, + some, 32, TCG_TYPE_V256, g->load_dest, g->fniv); if (some == oprsz) { break; } @@ -1471,23 +1488,25 @@ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, maxsz -= some; /* fallthru */ case TCG_TYPE_V128: - expand_3_vec(g->vece, dofs, aofs, bofs, oprsz, 16, TCG_TYPE_V128, - g->load_dest, g->fniv); + expand_3_vec(g->vece, dbase, dofs, abase, aofs, bbase, bofs, + oprsz, 16, TCG_TYPE_V128, g->load_dest, g->fniv); break; case TCG_TYPE_V64: - expand_3_vec(g->vece, dofs, aofs, bofs, oprsz, 8, TCG_TYPE_V64, - g->load_dest, g->fniv); + expand_3_vec(g->vece, dbase, dofs, abase, aofs, bbase, bofs, + oprsz, 8, TCG_TYPE_V64, g->load_dest, g->fniv); break; case 0: if (g->fni8 && check_size_impl(oprsz, 8)) { - expand_3_i64(dofs, aofs, bofs, oprsz, g->load_dest, g->fni8); + expand_3_i64(dbase, dofs, abase, aofs, bbase, bofs, + oprsz, g->load_dest, g->fni8); } else if (g->fni4 && check_size_impl(oprsz, 4)) { - expand_3_i32(dofs, aofs, bofs, oprsz, g->load_dest, g->fni4); + expand_3_i32(dbase, dofs, abase, aofs, bbase, bofs, + oprsz, g->load_dest, g->fni4); } else { assert(g->fno != NULL); - tcg_gen_gvec_3_ool(dofs, aofs, bofs, oprsz, - maxsz, g->data, g->fno); + expand_3_ool(dbase, dofs, abase, aofs, bbase, bofs, + oprsz, maxsz, g->data, g->fno); oprsz = maxsz; } break; @@ -1498,10 +1517,17 @@ void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, tcg_swap_vecop_list(hold_list); if (oprsz < maxsz) { - expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); + expand_clr(dbase, dofs + oprsz, maxsz - oprsz); } } +void tcg_gen_gvec_3(uint32_t dofs, uint32_t aofs, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz, const GVecGen3 *g) +{ + tcg_gen_gvec_3_var(tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, + oprsz, maxsz, g); +} + /* Expand a vector operation with three vectors and an immediate. */ void tcg_gen_gvec_3i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz, int64_t c, From ebba58c44db127d676b4ab2d00e7ad1aba9f6bc4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 6 Mar 2024 12:24:04 -1000 Subject: [PATCH 1630/2760] tcg: Split out tcg_gen_gvec_mov_var Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/tcg/tcg-op-gvec-common.h | 4 ++++ tcg/tcg-op-gvec.c | 21 +++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/tcg/tcg-op-gvec-common.h b/include/tcg/tcg-op-gvec-common.h index 29bcedb737..b2e797780d 100644 --- a/include/tcg/tcg-op-gvec-common.h +++ b/include/tcg/tcg-op-gvec-common.h @@ -283,6 +283,10 @@ void tcg_gen_gvec_4i(uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t cofs, /* Expand a specific vector operation. */ +void tcg_gen_gvec_mov_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz); + void tcg_gen_gvec_mov(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t maxsz); void tcg_gen_gvec_not(unsigned vece, uint32_t dofs, uint32_t aofs, diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 862cecf1bf..85e1807580 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -1746,8 +1746,9 @@ static void vec_mov2(unsigned vece, TCGv_vec a, TCGv_vec b) tcg_gen_mov_vec(a, b); } -void tcg_gen_gvec_mov(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t oprsz, uint32_t maxsz) +void tcg_gen_gvec_mov_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz) { static const GVecGen2 g = { .fni8 = tcg_gen_mov_i64, @@ -1755,14 +1756,22 @@ void tcg_gen_gvec_mov(unsigned vece, uint32_t dofs, uint32_t aofs, .fno = gen_helper_gvec_mov, .prefer_i64 = TCG_TARGET_REG_BITS == 64, }; - if (dofs != aofs) { - tcg_gen_gvec_2(dofs, aofs, oprsz, maxsz, &g); - } else { + + if (dofs == aofs && dbase == abase) { check_size_align(oprsz, maxsz, dofs); if (oprsz < maxsz) { - expand_clr(tcg_env, dofs + oprsz, maxsz - oprsz); + expand_clr(dbase, dofs + oprsz, maxsz - oprsz); } + return; } + + tcg_gen_gvec_2_var(dbase, dofs, abase, aofs, oprsz, maxsz, &g); +} + +void tcg_gen_gvec_mov(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t oprsz, uint32_t maxsz) +{ + tcg_gen_gvec_mov_var(vece, tcg_env, dofs, tcg_env, aofs, oprsz, maxsz); } void tcg_gen_gvec_dup_i32(unsigned vece, uint32_t dofs, uint32_t oprsz, From 4474051821c994ca905b56ae1276bcc3ba14ccda Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 6 Mar 2024 14:12:11 -1000 Subject: [PATCH 1631/2760] tcg: Split out tcg_gen_gvec_{add,sub}_var Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/tcg/tcg-op-gvec-common.h | 9 +++++++++ tcg/tcg-op-gvec.c | 32 ++++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/include/tcg/tcg-op-gvec-common.h b/include/tcg/tcg-op-gvec-common.h index b2e797780d..c93d8b2356 100644 --- a/include/tcg/tcg-op-gvec-common.h +++ b/include/tcg/tcg-op-gvec-common.h @@ -296,6 +296,15 @@ void tcg_gen_gvec_neg(unsigned vece, uint32_t dofs, uint32_t aofs, void tcg_gen_gvec_abs(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_add_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz); +void tcg_gen_gvec_sub_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz); + void tcg_gen_gvec_add(unsigned vece, uint32_t dofs, uint32_t aofs, uint32_t bofs, uint32_t oprsz, uint32_t maxsz); void tcg_gen_gvec_sub(unsigned vece, uint32_t dofs, uint32_t aofs, diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 85e1807580..9714eb902b 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -1999,8 +1999,10 @@ void tcg_gen_vec_add32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) static const TCGOpcode vecop_list_add[] = { INDEX_op_add_vec, 0 }; -void tcg_gen_gvec_add(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +void tcg_gen_gvec_add_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz) { static const GVecGen3 g[4] = { { .fni8 = tcg_gen_vec_add8_i64, @@ -2027,7 +2029,15 @@ void tcg_gen_gvec_add(unsigned vece, uint32_t dofs, uint32_t aofs, }; tcg_debug_assert(vece <= MO_64); - tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); + tcg_gen_gvec_3_var(dbase, dofs, abase, aofs, bbase, bofs, + oprsz, maxsz, &g[vece]); +} + +void tcg_gen_gvec_add(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + tcg_gen_gvec_add_var(vece, tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, + oprsz, maxsz); } void tcg_gen_gvec_adds(unsigned vece, uint32_t dofs, uint32_t aofs, @@ -2180,8 +2190,10 @@ void tcg_gen_vec_sub32_i64(TCGv_i64 d, TCGv_i64 a, TCGv_i64 b) tcg_temp_free_i64(t2); } -void tcg_gen_gvec_sub(unsigned vece, uint32_t dofs, uint32_t aofs, - uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +void tcg_gen_gvec_sub_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + TCGv_ptr abase, uint32_t aofs, + TCGv_ptr bbase, uint32_t bofs, + uint32_t oprsz, uint32_t maxsz) { static const GVecGen3 g[4] = { { .fni8 = tcg_gen_vec_sub8_i64, @@ -2208,7 +2220,15 @@ void tcg_gen_gvec_sub(unsigned vece, uint32_t dofs, uint32_t aofs, }; tcg_debug_assert(vece <= MO_64); - tcg_gen_gvec_3(dofs, aofs, bofs, oprsz, maxsz, &g[vece]); + tcg_gen_gvec_3_var(dbase, dofs, abase, aofs, bbase, bofs, + oprsz, maxsz, &g[vece]); +} + +void tcg_gen_gvec_sub(unsigned vece, uint32_t dofs, uint32_t aofs, + uint32_t bofs, uint32_t oprsz, uint32_t maxsz) +{ + tcg_gen_gvec_sub_var(vece, tcg_env, dofs, tcg_env, aofs, tcg_env, bofs, + oprsz, maxsz); } static const TCGOpcode vecop_list_mul[] = { INDEX_op_mul_vec, 0 }; From aa1cc0d74dc48da3b90476fbcc7fa339c61c8045 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 6 Feb 2025 10:53:43 -0800 Subject: [PATCH 1632/2760] tcg: Split out tcg_gen_gvec_dup_imm_var Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- include/tcg/tcg-op-gvec-common.h | 3 +++ tcg/tcg-op-gvec.c | 10 ++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/include/tcg/tcg-op-gvec-common.h b/include/tcg/tcg-op-gvec-common.h index c93d8b2356..ea0c87f4db 100644 --- a/include/tcg/tcg-op-gvec-common.h +++ b/include/tcg/tcg-op-gvec-common.h @@ -386,6 +386,9 @@ void tcg_gen_gvec_dup_i32(unsigned vece, uint32_t dofs, uint32_t s, void tcg_gen_gvec_dup_i64(unsigned vece, uint32_t dofs, uint32_t s, uint32_t m, TCGv_i64); +void tcg_gen_gvec_dup_imm_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + uint32_t oprsz, uint32_t maxsz, uint64_t imm); + void tcg_gen_gvec_shli(unsigned vece, uint32_t dofs, uint32_t aofs, int64_t shift, uint32_t oprsz, uint32_t maxsz); void tcg_gen_gvec_shri(unsigned vece, uint32_t dofs, uint32_t aofs, diff --git a/tcg/tcg-op-gvec.c b/tcg/tcg-op-gvec.c index 9714eb902b..2d184547ba 100644 --- a/tcg/tcg-op-gvec.c +++ b/tcg/tcg-op-gvec.c @@ -1897,11 +1897,17 @@ void tcg_gen_gvec_dup_mem(unsigned vece, uint32_t dofs, uint32_t aofs, } } +void tcg_gen_gvec_dup_imm_var(unsigned vece, TCGv_ptr dbase, uint32_t dofs, + uint32_t oprsz, uint32_t maxsz, uint64_t x) +{ + check_size_align(oprsz, maxsz, dofs); + do_dup(vece, dbase, dofs, oprsz, maxsz, NULL, NULL, x); +} + void tcg_gen_gvec_dup_imm(unsigned vece, uint32_t dofs, uint32_t oprsz, uint32_t maxsz, uint64_t x) { - check_size_align(oprsz, maxsz, dofs); - do_dup(vece, tcg_env, dofs, oprsz, maxsz, NULL, NULL, x); + tcg_gen_gvec_dup_imm_var(vece, tcg_env, dofs, oprsz, maxsz, x); } void tcg_gen_gvec_not(unsigned vece, uint32_t dofs, uint32_t aofs, From a9cd024c5822d08656127fde133975fde40f206c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 18 Jun 2025 01:11:27 +0000 Subject: [PATCH 1633/2760] linux-user/aarch64: Update hwcap bits from 6.14 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson --- linux-user/elfload.c | 75 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 3 deletions(-) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 82ebf6a212..2add1665c7 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -751,7 +751,23 @@ enum { ARM_HWCAP_A64_SSBS = 1 << 28, ARM_HWCAP_A64_SB = 1 << 29, ARM_HWCAP_A64_PACA = 1 << 30, - ARM_HWCAP_A64_PACG = 1UL << 31, + ARM_HWCAP_A64_PACG = 1ULL << 31, + ARM_HWCAP_A64_GCS = 1ULL << 32, + ARM_HWCAP_A64_CMPBR = 1ULL << 33, + ARM_HWCAP_A64_FPRCVT = 1ULL << 34, + ARM_HWCAP_A64_F8MM8 = 1ULL << 35, + ARM_HWCAP_A64_F8MM4 = 1ULL << 36, + ARM_HWCAP_A64_SVE_F16MM = 1ULL << 37, + ARM_HWCAP_A64_SVE_ELTPERM = 1ULL << 38, + ARM_HWCAP_A64_SVE_AES2 = 1ULL << 39, + ARM_HWCAP_A64_SVE_BFSCALE = 1ULL << 40, + ARM_HWCAP_A64_SVE2P2 = 1ULL << 41, + ARM_HWCAP_A64_SME2P2 = 1ULL << 42, + ARM_HWCAP_A64_SME_SBITPERM = 1ULL << 43, + ARM_HWCAP_A64_SME_AES = 1ULL << 44, + ARM_HWCAP_A64_SME_SFEXPA = 1ULL << 45, + ARM_HWCAP_A64_SME_STMOP = 1ULL << 46, + ARM_HWCAP_A64_SME_SMOP4 = 1ULL << 47, ARM_HWCAP2_A64_DCPODP = 1 << 0, ARM_HWCAP2_A64_SVE2 = 1 << 1, @@ -798,6 +814,25 @@ enum { ARM_HWCAP2_A64_SME_F16F16 = 1ULL << 42, ARM_HWCAP2_A64_MOPS = 1ULL << 43, ARM_HWCAP2_A64_HBC = 1ULL << 44, + ARM_HWCAP2_A64_SVE_B16B16 = 1ULL << 45, + ARM_HWCAP2_A64_LRCPC3 = 1ULL << 46, + ARM_HWCAP2_A64_LSE128 = 1ULL << 47, + ARM_HWCAP2_A64_FPMR = 1ULL << 48, + ARM_HWCAP2_A64_LUT = 1ULL << 49, + ARM_HWCAP2_A64_FAMINMAX = 1ULL << 50, + ARM_HWCAP2_A64_F8CVT = 1ULL << 51, + ARM_HWCAP2_A64_F8FMA = 1ULL << 52, + ARM_HWCAP2_A64_F8DP4 = 1ULL << 53, + ARM_HWCAP2_A64_F8DP2 = 1ULL << 54, + ARM_HWCAP2_A64_F8E4M3 = 1ULL << 55, + ARM_HWCAP2_A64_F8E5M2 = 1ULL << 56, + ARM_HWCAP2_A64_SME_LUTV2 = 1ULL << 57, + ARM_HWCAP2_A64_SME_F8F16 = 1ULL << 58, + ARM_HWCAP2_A64_SME_F8F32 = 1ULL << 59, + ARM_HWCAP2_A64_SME_SF8FMA = 1ULL << 60, + ARM_HWCAP2_A64_SME_SF8DP4 = 1ULL << 61, + ARM_HWCAP2_A64_SME_SF8DP2 = 1ULL << 62, + ARM_HWCAP2_A64_POE = 1ULL << 63, }; #define ELF_HWCAP get_elf_hwcap() @@ -886,7 +921,7 @@ uint64_t get_elf_hwcap2(void) const char *elf_hwcap_str(uint32_t bit) { - static const char *hwcap_str[] = { + static const char * const hwcap_str[] = { [__builtin_ctz(ARM_HWCAP_A64_FP )] = "fp", [__builtin_ctz(ARM_HWCAP_A64_ASIMD )] = "asimd", [__builtin_ctz(ARM_HWCAP_A64_EVTSTRM )] = "evtstrm", @@ -919,6 +954,22 @@ const char *elf_hwcap_str(uint32_t bit) [__builtin_ctz(ARM_HWCAP_A64_SB )] = "sb", [__builtin_ctz(ARM_HWCAP_A64_PACA )] = "paca", [__builtin_ctz(ARM_HWCAP_A64_PACG )] = "pacg", + [__builtin_ctzll(ARM_HWCAP_A64_GCS )] = "gcs", + [__builtin_ctzll(ARM_HWCAP_A64_CMPBR )] = "cmpbr", + [__builtin_ctzll(ARM_HWCAP_A64_FPRCVT)] = "fprcvt", + [__builtin_ctzll(ARM_HWCAP_A64_F8MM8 )] = "f8mm8", + [__builtin_ctzll(ARM_HWCAP_A64_F8MM4 )] = "f8mm4", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_F16MM)] = "svef16mm", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_ELTPERM)] = "sveeltperm", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_AES2)] = "sveaes2", + [__builtin_ctzll(ARM_HWCAP_A64_SVE_BFSCALE)] = "svebfscale", + [__builtin_ctzll(ARM_HWCAP_A64_SVE2P2)] = "sve2p2", + [__builtin_ctzll(ARM_HWCAP_A64_SME2P2)] = "sme2p2", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SBITPERM)] = "smesbitperm", + [__builtin_ctzll(ARM_HWCAP_A64_SME_AES)] = "smeaes", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SFEXPA)] = "smesfexpa", + [__builtin_ctzll(ARM_HWCAP_A64_SME_STMOP)] = "smestmop", + [__builtin_ctzll(ARM_HWCAP_A64_SME_SMOP4)] = "smesmop4", }; return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; @@ -926,7 +977,7 @@ const char *elf_hwcap_str(uint32_t bit) const char *elf_hwcap2_str(uint32_t bit) { - static const char *hwcap_str[] = { + static const char * const hwcap_str[] = { [__builtin_ctz(ARM_HWCAP2_A64_DCPODP )] = "dcpodp", [__builtin_ctz(ARM_HWCAP2_A64_SVE2 )] = "sve2", [__builtin_ctz(ARM_HWCAP2_A64_SVEAES )] = "sveaes", @@ -972,6 +1023,24 @@ const char *elf_hwcap2_str(uint32_t bit) [__builtin_ctzll(ARM_HWCAP2_A64_SME_F16F16 )] = "smef16f16", [__builtin_ctzll(ARM_HWCAP2_A64_MOPS )] = "mops", [__builtin_ctzll(ARM_HWCAP2_A64_HBC )] = "hbc", + [__builtin_ctzll(ARM_HWCAP2_A64_SVE_B16B16 )] = "sveb16b16", + [__builtin_ctzll(ARM_HWCAP2_A64_LRCPC3 )] = "lrcpc3", + [__builtin_ctzll(ARM_HWCAP2_A64_LSE128 )] = "lse128", + [__builtin_ctzll(ARM_HWCAP2_A64_FPMR )] = "fpmr", + [__builtin_ctzll(ARM_HWCAP2_A64_LUT )] = "lut", + [__builtin_ctzll(ARM_HWCAP2_A64_FAMINMAX )] = "faminmax", + [__builtin_ctzll(ARM_HWCAP2_A64_F8CVT )] = "f8cvt", + [__builtin_ctzll(ARM_HWCAP2_A64_F8FMA )] = "f8fma", + [__builtin_ctzll(ARM_HWCAP2_A64_F8DP4 )] = "f8dp4", + [__builtin_ctzll(ARM_HWCAP2_A64_F8DP2 )] = "f8dp2", + [__builtin_ctzll(ARM_HWCAP2_A64_F8E4M3 )] = "f8e4m3", + [__builtin_ctzll(ARM_HWCAP2_A64_F8E5M2 )] = "f8e5m2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_LUTV2 )] = "smelutv2", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F16 )] = "smef8f16", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_F8F32 )] = "smef8f32", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP4 )] = "smesf8dp4", + [__builtin_ctzll(ARM_HWCAP2_A64_SME_SF8DP2 )] = "smesf8dp2", + [__builtin_ctzll(ARM_HWCAP2_A64_POE )] = "poe", }; return bit < ARRAY_SIZE(hwcap_str) ? hwcap_str[bit] : NULL; From 5171b794d4ac47efd81ef4da54137131e4ea550d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 13 May 2025 16:03:46 +0100 Subject: [PATCH 1634/2760] linux-user: fix resource leaks in gen-vdso MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are a number of resource leaks in gen-vdso. In theory they are harmless because this is a short lived process, but when building QEMU with --extra-cflags="-fsanitize=address" problems ensure. The gen-vdso program is run as part of the build, and that aborts due to the sanitizer identifying memory leaks, leaving QEMU unbuildable. FAILED: libqemu-x86_64-linux-user.a.p/vdso.c.inc /var/home/berrange/src/virt/qemu/build/linux-user/gen-vdso -o libqemu-x86_64-linux-user.a.p/vdso.c.inc ../linux-user/x86_64/vdso.so ================================================================= ==1696332==ERROR: LeakSanitizer: detected memory leaks Direct leak of 2968 byte(s) in 1 object(s) allocated from: #0 0x56495873f1f3 (/var/home/berrange/src/virt/qemu/build/linux-user/gen-vdso+0xa11f3) (BuildId: b69e241ad44719b6f3934f3c71dfc6727e8bdb12) #1 0x564958780b90 (/var/home/berrange/src/virt/qemu/build/linux-user/gen-vdso+0xe2b90) (BuildId: b69e241ad44719b6f3934f3c71dfc6727e8bdb12) This complaint is about the 'buf' variable, however, the FILE objects are also leaked in some error scenarios, so this fix refactors the cleanup paths to fix all leaks. For completeness it also reports an error if fclose() fails on 'inf'. Signed-off-by: Daniel P. Berrangé Tested-by: Arusekk Signed-off-by: Richard Henderson Message-ID: <20250513150346.1328217-1-berrange@redhat.com> --- linux-user/gen-vdso.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/linux-user/gen-vdso.c b/linux-user/gen-vdso.c index 721f38d5a3..fce9d5cbc3 100644 --- a/linux-user/gen-vdso.c +++ b/linux-user/gen-vdso.c @@ -56,13 +56,14 @@ static unsigned rt_sigreturn_addr; int main(int argc, char **argv) { - FILE *inf, *outf; + FILE *inf = NULL, *outf = NULL; long total_len; const char *prefix = "vdso"; const char *inf_name; const char *outf_name = NULL; - unsigned char *buf; + unsigned char *buf = NULL; bool need_bswap; + int ret = EXIT_FAILURE; while (1) { int opt = getopt(argc, argv, "o:p:r:s:"); @@ -129,7 +130,6 @@ int main(int argc, char **argv) fprintf(stderr, "%s: incomplete read\n", inf_name); return EXIT_FAILURE; } - fclose(inf); /* * Identify which elf flavor we're processing. @@ -205,19 +205,24 @@ int main(int argc, char **argv) fprintf(outf, " .rt_sigreturn_ofs = 0x%x,\n", rt_sigreturn_addr); fprintf(outf, "};\n"); - /* - * Everything should have gone well. - */ - if (fclose(outf)) { - goto perror_outf; + ret = EXIT_SUCCESS; + + cleanup: + free(buf); + + if (outf && fclose(outf) != 0) { + ret = EXIT_FAILURE; + } + if (inf && fclose(inf) != 0) { + ret = EXIT_FAILURE; } - return EXIT_SUCCESS; + return ret; perror_inf: perror(inf_name); - return EXIT_FAILURE; + goto cleanup; perror_outf: perror(outf_name); - return EXIT_FAILURE; + goto cleanup; } From fd0377150d5dd4b02b2e7fbe9e8c1ab4dcbd16bb Mon Sep 17 00:00:00 2001 From: Yanfei Xu Date: Wed, 14 May 2025 19:58:27 +0800 Subject: [PATCH 1635/2760] migration/ram: avoid to do log clear in the last round There won't be any ram sync after the stage of save_complete, therefore it's unnecessary to do manually protect for dirty pages being sent. Skip to do this in last round can reduce noticeable downtime. Signed-off-by: Yanfei Xu Tested-by: Fabiano Rosas Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250514115827.3216082-1-yanfei.xu@bytedance.com [peterx: add comments] Signed-off-by: Peter Xu --- migration/ram.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index d26dbd37c4..fd8d83b63c 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -831,14 +831,22 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, bool ret; /* - * Clear dirty bitmap if needed. This _must_ be called before we - * send any of the page in the chunk because we need to make sure - * we can capture further page content changes when we sync dirty - * log the next time. So as long as we are going to send any of - * the page in the chunk we clear the remote dirty bitmap for all. - * Clearing it earlier won't be a problem, but too late will. + * During the last stage (after source VM stopped), resetting the write + * protections isn't needed as we know there will be either (1) no + * further writes if migration will complete, or (2) migration fails + * at last then tracking isn't needed either. */ - migration_clear_memory_region_dirty_bitmap(rb, page); + if (!rs->last_stage) { + /* + * Clear dirty bitmap if needed. This _must_ be called before we + * send any of the page in the chunk because we need to make sure + * we can capture further page content changes when we sync dirty + * log the next time. So as long as we are going to send any of + * the page in the chunk we clear the remote dirty bitmap for all. + * Clearing it earlier won't be a problem, but too late will. + */ + migration_clear_memory_region_dirty_bitmap(rb, page); + } ret = test_and_clear_bit(page, rb->bmap); if (ret) { From 8120decfb593c386e053a1ac9723e75bd181dbff Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Fri, 23 May 2025 09:30:23 -0300 Subject: [PATCH 1636/2760] tests/qtest: Remove migration-helpers.c Commit 407bc4bf90 ("qapi: Move include/qapi/qmp/ to include/qobject/") brought the migration-helpers.c back by mistake. This file has been replaced with migration/migration-qmp.c and migration/migration-util.c. Fixes: 407bc4bf90 ("qapi: Move include/qapi/qmp/ to include/qobject/") Signed-off-by: Fabiano Rosas Message-id: 20200310152141.13959-1-peter.maydell@linaro.org Reviewed-by: Markus Armbruster Link: https://lore.kernel.org/r/20250523123023.19284-1-farosas@suse.de Signed-off-by: Peter Xu --- tests/qtest/migration-helpers.c | 530 -------------------------------- 1 file changed, 530 deletions(-) delete mode 100644 tests/qtest/migration-helpers.c diff --git a/tests/qtest/migration-helpers.c b/tests/qtest/migration-helpers.c deleted file mode 100644 index b08b49bd43..0000000000 --- a/tests/qtest/migration-helpers.c +++ /dev/null @@ -1,530 +0,0 @@ -/* - * QTest migration helpers - * - * Copyright (c) 2016-2018 Red Hat, Inc. and/or its affiliates - * based on the vhost-user-test.c that is: - * Copyright (c) 2014 Virtual Open Systems Sarl. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#include "qemu/osdep.h" -#include "qemu/ctype.h" -#include "qobject/qjson.h" -#include "qapi/qapi-visit-sockets.h" -#include "qapi/qobject-input-visitor.h" -#include "qapi/error.h" -#include "qobject/qlist.h" -#include "qemu/cutils.h" -#include "qemu/memalign.h" - -#include "migration-helpers.h" - -/* - * Number of seconds we wait when looking for migration - * status changes, to avoid test suite hanging forever - * when things go wrong. Needs to be higher enough to - * avoid false positives on loaded hosts. - */ -#define MIGRATION_STATUS_WAIT_TIMEOUT 120 - -static char *SocketAddress_to_str(SocketAddress *addr) -{ - switch (addr->type) { - case SOCKET_ADDRESS_TYPE_INET: - return g_strdup_printf("tcp:%s:%s", - addr->u.inet.host, - addr->u.inet.port); - case SOCKET_ADDRESS_TYPE_UNIX: - return g_strdup_printf("unix:%s", - addr->u.q_unix.path); - case SOCKET_ADDRESS_TYPE_FD: - return g_strdup_printf("fd:%s", addr->u.fd.str); - case SOCKET_ADDRESS_TYPE_VSOCK: - return g_strdup_printf("vsock:%s:%s", - addr->u.vsock.cid, - addr->u.vsock.port); - default: - return g_strdup("unknown address type"); - } -} - -static QDict *SocketAddress_to_qdict(SocketAddress *addr) -{ - QDict *dict = qdict_new(); - - switch (addr->type) { - case SOCKET_ADDRESS_TYPE_INET: - qdict_put_str(dict, "type", "inet"); - qdict_put_str(dict, "host", addr->u.inet.host); - qdict_put_str(dict, "port", addr->u.inet.port); - break; - case SOCKET_ADDRESS_TYPE_UNIX: - qdict_put_str(dict, "type", "unix"); - qdict_put_str(dict, "path", addr->u.q_unix.path); - break; - case SOCKET_ADDRESS_TYPE_FD: - qdict_put_str(dict, "type", "fd"); - qdict_put_str(dict, "str", addr->u.fd.str); - break; - case SOCKET_ADDRESS_TYPE_VSOCK: - qdict_put_str(dict, "type", "vsock"); - qdict_put_str(dict, "cid", addr->u.vsock.cid); - qdict_put_str(dict, "port", addr->u.vsock.port); - break; - default: - g_assert_not_reached(); - } - - return dict; -} - -static SocketAddressList *migrate_get_socket_address(QTestState *who) -{ - QDict *rsp; - SocketAddressList *addrs; - Visitor *iv = NULL; - QObject *object; - - rsp = migrate_query(who); - object = qdict_get(rsp, "socket-address"); - - iv = qobject_input_visitor_new(object); - visit_type_SocketAddressList(iv, NULL, &addrs, &error_abort); - visit_free(iv); - - qobject_unref(rsp); - return addrs; -} - -static char * -migrate_get_connect_uri(QTestState *who) -{ - SocketAddressList *addrs; - char *connect_uri; - - addrs = migrate_get_socket_address(who); - connect_uri = SocketAddress_to_str(addrs->value); - - qapi_free_SocketAddressList(addrs); - return connect_uri; -} - -static QDict * -migrate_get_connect_qdict(QTestState *who) -{ - SocketAddressList *addrs; - QDict *connect_qdict; - - addrs = migrate_get_socket_address(who); - connect_qdict = SocketAddress_to_qdict(addrs->value); - - qapi_free_SocketAddressList(addrs); - return connect_qdict; -} - -static void migrate_set_ports(QTestState *to, QList *channel_list) -{ - QDict *addr; - QListEntry *entry; - const char *addr_port = NULL; - - addr = migrate_get_connect_qdict(to); - - QLIST_FOREACH_ENTRY(channel_list, entry) { - QDict *channel = qobject_to(QDict, qlist_entry_obj(entry)); - QDict *addrdict = qdict_get_qdict(channel, "addr"); - - if (qdict_haskey(addrdict, "port") && - qdict_haskey(addr, "port") && - (strcmp(qdict_get_str(addrdict, "port"), "0") == 0)) { - addr_port = qdict_get_str(addr, "port"); - qdict_put_str(addrdict, "port", addr_port); - } - } - - qobject_unref(addr); -} - -bool migrate_watch_for_events(QTestState *who, const char *name, - QDict *event, void *opaque) -{ - QTestMigrationState *state = opaque; - - if (g_str_equal(name, "STOP")) { - state->stop_seen = true; - return true; - } else if (g_str_equal(name, "SUSPEND")) { - state->suspend_seen = true; - return true; - } else if (g_str_equal(name, "RESUME")) { - state->resume_seen = true; - return true; - } - - return false; -} - -void migrate_qmp_fail(QTestState *who, const char *uri, - const char *channels, const char *fmt, ...) -{ - va_list ap; - QDict *args, *err; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - if (uri) { - qdict_put_str(args, "uri", uri); - } - - g_assert(!qdict_haskey(args, "channels")); - if (channels) { - QObject *channels_obj = qobject_from_json(channels, &error_abort); - qdict_put_obj(args, "channels", channels_obj); - } - - err = qtest_qmp_assert_failure_ref( - who, "{ 'execute': 'migrate', 'arguments': %p}", args); - - g_assert(qdict_haskey(err, "desc")); - - qobject_unref(err); -} - -/* - * Send QMP command "migrate". - * Arguments are built from @fmt... (formatted like - * qobject_from_jsonf_nofail()) with "uri": @uri spliced in. - */ -void migrate_qmp(QTestState *who, QTestState *to, const char *uri, - const char *channels, const char *fmt, ...) -{ - va_list ap; - QDict *args; - g_autofree char *connect_uri = NULL; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - if (uri) { - qdict_put_str(args, "uri", uri); - } else if (!channels) { - connect_uri = migrate_get_connect_uri(to); - qdict_put_str(args, "uri", connect_uri); - } - - g_assert(!qdict_haskey(args, "channels")); - if (channels) { - QObject *channels_obj = qobject_from_json(channels, &error_abort); - QList *channel_list = qobject_to(QList, channels_obj); - migrate_set_ports(to, channel_list); - qdict_put_obj(args, "channels", channels_obj); - } - - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate', 'arguments': %p}", args); -} - -void migrate_set_capability(QTestState *who, const char *capability, - bool value) -{ - qtest_qmp_assert_success(who, - "{ 'execute': 'migrate-set-capabilities'," - "'arguments': { " - "'capabilities': [ { " - "'capability': %s, 'state': %i } ] } }", - capability, value); -} - -void migrate_incoming_qmp(QTestState *to, const char *uri, const char *fmt, ...) -{ - va_list ap; - QDict *args, *rsp; - - va_start(ap, fmt); - args = qdict_from_vjsonf_nofail(fmt, ap); - va_end(ap); - - g_assert(!qdict_haskey(args, "uri")); - qdict_put_str(args, "uri", uri); - - /* This function relies on the event to work, make sure it's enabled */ - migrate_set_capability(to, "events", true); - - rsp = qtest_qmp(to, "{ 'execute': 'migrate-incoming', 'arguments': %p}", - args); - - if (!qdict_haskey(rsp, "return")) { - g_autoptr(GString) s = qobject_to_json_pretty(QOBJECT(rsp), true); - g_test_message("%s", s->str); - } - - g_assert(qdict_haskey(rsp, "return")); - qobject_unref(rsp); - - migration_event_wait(to, "setup"); -} - -/* - * Note: caller is responsible to free the returned object via - * qobject_unref() after use - */ -QDict *migrate_query(QTestState *who) -{ - return qtest_qmp_assert_success_ref(who, "{ 'execute': 'query-migrate' }"); -} - -QDict *migrate_query_not_failed(QTestState *who) -{ - const char *status; - QDict *rsp = migrate_query(who); - status = qdict_get_str(rsp, "status"); - if (g_str_equal(status, "failed")) { - g_printerr("query-migrate shows failed migration: %s\n", - qdict_get_str(rsp, "error-desc")); - } - g_assert(!g_str_equal(status, "failed")); - return rsp; -} - -/* - * Note: caller is responsible to free the returned object via - * g_free() after use - */ -static gchar *migrate_query_status(QTestState *who) -{ - QDict *rsp_return = migrate_query(who); - gchar *status = g_strdup(qdict_get_str(rsp_return, "status")); - - g_assert(status); - qobject_unref(rsp_return); - - return status; -} - -static bool check_migration_status(QTestState *who, const char *goal, - const char **ungoals) -{ - bool ready; - char *current_status; - const char **ungoal; - - current_status = migrate_query_status(who); - ready = strcmp(current_status, goal) == 0; - if (!ungoals) { - g_assert_cmpstr(current_status, !=, "failed"); - /* - * If looking for a state other than completed, - * completion of migration would cause the test to - * hang. - */ - if (strcmp(goal, "completed") != 0) { - g_assert_cmpstr(current_status, !=, "completed"); - } - } else { - for (ungoal = ungoals; *ungoal; ungoal++) { - g_assert_cmpstr(current_status, !=, *ungoal); - } - } - g_free(current_status); - return ready; -} - -void wait_for_migration_status(QTestState *who, - const char *goal, const char **ungoals) -{ - g_test_timer_start(); - while (!check_migration_status(who, goal, ungoals)) { - usleep(1000); - - g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); - } -} - -void wait_for_migration_complete(QTestState *who) -{ - wait_for_migration_status(who, "completed", NULL); -} - -void wait_for_migration_fail(QTestState *from, bool allow_active) -{ - g_test_timer_start(); - QDict *rsp_return; - char *status; - bool failed; - - do { - status = migrate_query_status(from); - bool result = !strcmp(status, "setup") || !strcmp(status, "failed") || - (allow_active && !strcmp(status, "active")); - if (!result) { - fprintf(stderr, "%s: unexpected status status=%s allow_active=%d\n", - __func__, status, allow_active); - } - g_assert(result); - failed = !strcmp(status, "failed"); - g_free(status); - - g_assert(g_test_timer_elapsed() < MIGRATION_STATUS_WAIT_TIMEOUT); - } while (!failed); - - /* Is the machine currently running? */ - rsp_return = qtest_qmp_assert_success_ref(from, - "{ 'execute': 'query-status' }"); - g_assert(qdict_haskey(rsp_return, "running")); - g_assert(qdict_get_bool(rsp_return, "running")); - qobject_unref(rsp_return); -} - -char *find_common_machine_version(const char *mtype, const char *var1, - const char *var2) -{ - g_autofree char *type1 = qtest_resolve_machine_alias(var1, mtype); - g_autofree char *type2 = qtest_resolve_machine_alias(var2, mtype); - - g_assert(type1 && type2); - - if (g_str_equal(type1, type2)) { - /* either can be used */ - return g_strdup(type1); - } - - if (qtest_has_machine_with_env(var2, type1)) { - return g_strdup(type1); - } - - if (qtest_has_machine_with_env(var1, type2)) { - return g_strdup(type2); - } - - g_test_message("No common machine version for machine type '%s' between " - "binaries %s and %s", mtype, getenv(var1), getenv(var2)); - g_assert_not_reached(); -} - -char *resolve_machine_version(const char *alias, const char *var1, - const char *var2) -{ - const char *mname = g_getenv("QTEST_QEMU_MACHINE_TYPE"); - g_autofree char *machine_name = NULL; - - if (mname) { - const char *dash = strrchr(mname, '-'); - const char *dot = strrchr(mname, '.'); - - machine_name = g_strdup(mname); - - if (dash && dot) { - assert(qtest_has_machine(machine_name)); - return g_steal_pointer(&machine_name); - } - /* else: probably an alias, let it be resolved below */ - } else { - /* use the hardcoded alias */ - machine_name = g_strdup(alias); - } - - return find_common_machine_version(machine_name, var1, var2); -} - -typedef struct { - char *name; - void (*func)(void); -} MigrationTest; - -static void migration_test_destroy(gpointer data) -{ - MigrationTest *test = (MigrationTest *)data; - - g_free(test->name); - g_free(test); -} - -static void migration_test_wrapper(const void *data) -{ - MigrationTest *test = (MigrationTest *)data; - - g_test_message("Running /%s%s", qtest_get_arch(), test->name); - test->func(); -} - -void migration_test_add(const char *path, void (*fn)(void)) -{ - MigrationTest *test = g_new0(MigrationTest, 1); - - test->func = fn; - test->name = g_strdup(path); - - qtest_add_data_func_full(path, test, migration_test_wrapper, - migration_test_destroy); -} - -#ifdef O_DIRECT -/* - * Probe for O_DIRECT support on the filesystem. Since this is used - * for tests, be conservative, if anything fails, assume it's - * unsupported. - */ -bool probe_o_direct_support(const char *tmpfs) -{ - g_autofree char *filename = g_strdup_printf("%s/probe-o-direct", tmpfs); - int fd, flags = O_CREAT | O_RDWR | O_TRUNC | O_DIRECT; - void *buf; - ssize_t ret, len; - uint64_t offset; - - fd = open(filename, flags, 0660); - if (fd < 0) { - unlink(filename); - return false; - } - - /* - * Using 1MB alignment as conservative choice to satisfy any - * plausible architecture default page size, and/or filesystem - * alignment restrictions. - */ - len = 0x100000; - offset = 0x100000; - - buf = qemu_try_memalign(len, len); - g_assert(buf); - - ret = pwrite(fd, buf, len, offset); - unlink(filename); - g_free(buf); - - if (ret < 0) { - return false; - } - - return true; -} -#endif - -/* - * Wait for a "MIGRATION" event. This is what Libvirt uses to track - * migration status changes. - */ -void migration_event_wait(QTestState *s, const char *target) -{ - QDict *response, *data; - const char *status; - bool found; - - do { - response = qtest_qmp_eventwait_ref(s, "MIGRATION"); - data = qdict_get_qdict(response, "data"); - g_assert(data); - status = qdict_get_str(data, "status"); - found = (strcmp(status, target) == 0); - qobject_unref(response); - } while (!found); -} From 0310d594d98b39f9dde79b87fd8b0ad16e7c5459 Mon Sep 17 00:00:00 2001 From: Juraj Marcin Date: Wed, 21 May 2025 17:16:13 +0200 Subject: [PATCH 1637/2760] ui/vnc: Update display update interval when VM state changes to RUNNING MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If a virtual machine is paused for an extended period time, for example, due to an incoming migration, there are also no changes on the screen. VNC in such case increases the display update interval by VNC_REFRESH_INTERVAL_INC (50 ms). The update interval can then grow up to VNC_REFRESH_INTERVAL_MAX (3000 ms). When the machine resumes, it can then take up to 3 seconds for the first display update. Furthermore, the update interval is then halved with each display update with changes on the screen. If there are moving elements on the screen, such as a video, this can be perceived as freezing and stuttering for few seconds before the movement is smooth again. This patch resolves this issue, by adding a listener to VM state changes and changing the update interval when the VM state changes to RUNNING. The update_displaychangelistener() function updates the internal timer, and the display is refreshed immediately if the timer is expired. Signed-off-by: Juraj Marcin Reviewed-by: Marc-André Lureau Reviewed-by: Peter Xu Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250521151616.3951178-1-jmarcin@redhat.com Signed-off-by: Peter Xu --- ui/vnc.c | 12 ++++++++++++ ui/vnc.h | 2 ++ 2 files changed, 14 insertions(+) diff --git a/ui/vnc.c b/ui/vnc.c index d095cd7da3..e9c30aad62 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -3385,6 +3385,16 @@ static const DisplayChangeListenerOps dcl_ops = { .dpy_cursor_define = vnc_dpy_cursor_define, }; +static void vmstate_change_handler(void *opaque, bool running, RunState state) +{ + VncDisplay *vd = opaque; + + if (state != RUN_STATE_RUNNING) { + return; + } + update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); +} + void vnc_display_init(const char *id, Error **errp) { VncDisplay *vd; @@ -3421,6 +3431,8 @@ void vnc_display_init(const char *id, Error **errp) vd->dcl.ops = &dcl_ops; register_displaychangelistener(&vd->dcl); vd->kbd = qkbd_state_init(vd->dcl.con); + vd->vmstate_handler_entry = qemu_add_vm_change_state_handler( + &vmstate_change_handler, vd); } diff --git a/ui/vnc.h b/ui/vnc.h index 02613aa63a..b3e07269bb 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -185,6 +185,8 @@ struct VncDisplay #endif AudioState *audio_state; + + VMChangeStateEntry *vmstate_handler_entry; }; typedef struct VncTight { From 8f87c87eca4bc62258251eade7016f8a084b0988 Mon Sep 17 00:00:00 2001 From: Jaehoon Kim Date: Wed, 11 Jun 2025 15:56:10 -0500 Subject: [PATCH 1638/2760] migration: Support fd-based socket address in cpr_transfer_input Extend cpr_transfer_input to handle SOCKET_ADDRESS_TYPE_FD alongside SOCKET_ADDRESS_TYPE_UNIX. This change supports the use of pre-listened socket file descriptors for cpr migration channels. This change is particularly useful in qtest environments, where the socket may be created externally and passed via fd. Reviewed-by: Jason J. Herne Reviewed-by: Steve Sistare Signed-off-by: Jaehoon Kim Link: https://lore.kernel.org/r/20250611205610.147008-3-jhkim@linux.ibm.com Signed-off-by: Peter Xu --- migration/cpr-transfer.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/migration/cpr-transfer.c b/migration/cpr-transfer.c index e1f140359c..00371d17c3 100644 --- a/migration/cpr-transfer.c +++ b/migration/cpr-transfer.c @@ -46,7 +46,8 @@ QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp) MigrationAddress *addr = channel->addr; if (addr->transport == MIGRATION_ADDRESS_TYPE_SOCKET && - addr->u.socket.type == SOCKET_ADDRESS_TYPE_UNIX) { + (addr->u.socket.type == SOCKET_ADDRESS_TYPE_UNIX || + addr->u.socket.type == SOCKET_ADDRESS_TYPE_FD)) { g_autoptr(QIOChannelSocket) sioc = NULL; SocketAddress *saddr = &addr->u.socket; @@ -60,7 +61,9 @@ QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp) sioc = qio_net_listener_wait_client(listener); ioc = QIO_CHANNEL(sioc); - trace_cpr_transfer_input(addr->u.socket.u.q_unix.path); + trace_cpr_transfer_input( + addr->u.socket.type == SOCKET_ADDRESS_TYPE_UNIX ? + addr->u.socket.u.q_unix.path : addr->u.socket.u.fd.str); qio_channel_set_name(ioc, "cpr-in"); return qemu_file_new_input(ioc); From 430671f52669e84c176c4d3b9091b88b51f542fb Mon Sep 17 00:00:00 2001 From: Jaehoon Kim Date: Wed, 11 Jun 2025 15:56:09 -0500 Subject: [PATCH 1639/2760] tests/migration: Setup pre-listened cpr.sock to remove race-condition. When the source VM attempts to connect to the destination VM's Unix domain socket (cpr.sock) during a cpr-transfer test, race conditions can occur if the socket file isn't ready. This can lead to connection failures when running tests. This patch creates and listens on the socket in advance, and passes the pre-listened FD directly. This avoids timing issues and improves the reliability of CPR tests. Reviewed-by: Jason J. Herne Signed-off-by: Jaehoon Kim Reviewed-by: Steve Sistare Link: https://lore.kernel.org/r/20250611205610.147008-2-jhkim@linux.ibm.com [peterx: null-initialize opts_target, per Steve] Signed-off-by: Peter Xu --- tests/qtest/migration/cpr-tests.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/qtest/migration/cpr-tests.c b/tests/qtest/migration/cpr-tests.c index 5536e14610..5e764a6787 100644 --- a/tests/qtest/migration/cpr-tests.c +++ b/tests/qtest/migration/cpr-tests.c @@ -60,13 +60,12 @@ static void test_mode_transfer_common(bool incoming_defer) g_autofree char *cpr_path = g_strdup_printf("%s/cpr.sock", tmpfs); g_autofree char *mig_path = g_strdup_printf("%s/migsocket", tmpfs); g_autofree char *uri = g_strdup_printf("unix:%s", mig_path); + g_autofree char *opts_target = NULL; const char *opts = "-machine aux-ram-share=on -nodefaults"; g_autofree const char *cpr_channel = g_strdup_printf( "cpr,addr.transport=socket,addr.type=unix,addr.path=%s", cpr_path); - g_autofree char *opts_target = g_strdup_printf("-incoming %s %s", - cpr_channel, opts); g_autofree char *connect_channels = g_strdup_printf( "[ { 'channel-type': 'main'," @@ -75,6 +74,17 @@ static void test_mode_transfer_common(bool incoming_defer) " 'path': '%s' } } ]", mig_path); + /* + * Set up a UNIX domain socket for the CPR channel before + * launching the destination VM, to avoid timing issues + * during connection setup. + */ + int cpr_sockfd = qtest_socket_server(cpr_path); + g_assert(cpr_sockfd >= 0); + + opts_target = g_strdup_printf("-incoming cpr,addr.transport=socket," + "addr.type=fd,addr.str=%d %s", + cpr_sockfd, opts); MigrateCommon args = { .start.opts_source = opts, .start.opts_target = opts_target, From 983899eab4939dc4dff67fa4d822c5b4df7eae21 Mon Sep 17 00:00:00 2001 From: "Chaney, Ben" Date: Mon, 16 Jun 2025 20:56:50 +0000 Subject: [PATCH 1640/2760] migration: Don't sync volatile memory after migration completes Syncing volatile memory provides no benefit, instead it can cause performance issues in some cases. Only sync memory that is marked as non-volatile after migration completes on destination. Signed-off-by: Ben Chaney Fixes: bd108a44bc29 (migration: ram: Switch to ram block writeback) Link: https://lore.kernel.org/r/1CC43F59-336F-4A12-84AD-DB89E0A17A95@akamai.com Signed-off-by: Peter Xu --- migration/ram.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index fd8d83b63c..024d8b3d03 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3680,7 +3680,9 @@ static int ram_load_cleanup(void *opaque) RAMBlock *rb; RAMBLOCK_FOREACH_NOT_IGNORED(rb) { - qemu_ram_block_writeback(rb); + if (memory_region_is_nonvolatile(rb->mr)) { + qemu_ram_block_writeback(rb); + } } xbzrle_load_cleanup(); From f47a672a72acd6e2712031f0bc4d4f3ae4b6302c Mon Sep 17 00:00:00 2001 From: Chenyi Qiang Date: Thu, 12 Jun 2025 16:27:42 +0800 Subject: [PATCH 1641/2760] memory: Export a helper to get intersection of a MemoryRegionSection with a given range Rename the helper to memory_region_section_intersect_range() to make it more generic. Meanwhile, define the @end as Int128 and replace the related operations with Int128_* format since the helper is exported as a wider API. Suggested-by: Alexey Kardashevskiy Reviewed-by: Alexey Kardashevskiy Reviewed-by: Pankaj Gupta Reviewed-by: David Hildenbrand Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Signed-off-by: Chenyi Qiang Link: https://lore.kernel.org/r/20250612082747.51539-2-chenyi.qiang@intel.com Signed-off-by: Peter Xu --- hw/virtio/virtio-mem.c | 32 +++++--------------------------- include/system/memory.h | 30 ++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index a3d1a676e7..b3c126ea1e 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -244,28 +244,6 @@ static int virtio_mem_for_each_plugged_range(VirtIOMEM *vmem, void *arg, return ret; } -/* - * Adjust the memory section to cover the intersection with the given range. - * - * Returns false if the intersection is empty, otherwise returns true. - */ -static bool virtio_mem_intersect_memory_section(MemoryRegionSection *s, - uint64_t offset, uint64_t size) -{ - uint64_t start = MAX(s->offset_within_region, offset); - uint64_t end = MIN(s->offset_within_region + int128_get64(s->size), - offset + size); - - if (end <= start) { - return false; - } - - s->offset_within_address_space += start - s->offset_within_region; - s->offset_within_region = start; - s->size = int128_make64(end - start); - return true; -} - typedef int (*virtio_mem_section_cb)(MemoryRegionSection *s, void *arg); static int virtio_mem_for_each_plugged_section(const VirtIOMEM *vmem, @@ -287,7 +265,7 @@ static int virtio_mem_for_each_plugged_section(const VirtIOMEM *vmem, first_bit + 1) - 1; size = (last_bit - first_bit + 1) * vmem->block_size; - if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { + if (!memory_region_section_intersect_range(&tmp, offset, size)) { break; } ret = cb(&tmp, arg); @@ -319,7 +297,7 @@ static int virtio_mem_for_each_unplugged_section(const VirtIOMEM *vmem, first_bit + 1) - 1; size = (last_bit - first_bit + 1) * vmem->block_size; - if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { + if (!memory_region_section_intersect_range(&tmp, offset, size)) { break; } ret = cb(&tmp, arg); @@ -355,7 +333,7 @@ static void virtio_mem_notify_unplug(VirtIOMEM *vmem, uint64_t offset, QLIST_FOREACH(rdl, &vmem->rdl_list, next) { MemoryRegionSection tmp = *rdl->section; - if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { + if (!memory_region_section_intersect_range(&tmp, offset, size)) { continue; } rdl->notify_discard(rdl, &tmp); @@ -371,7 +349,7 @@ static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t offset, QLIST_FOREACH(rdl, &vmem->rdl_list, next) { MemoryRegionSection tmp = *rdl->section; - if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { + if (!memory_region_section_intersect_range(&tmp, offset, size)) { continue; } ret = rdl->notify_populate(rdl, &tmp); @@ -388,7 +366,7 @@ static int virtio_mem_notify_plug(VirtIOMEM *vmem, uint64_t offset, if (rdl2 == rdl) { break; } - if (!virtio_mem_intersect_memory_section(&tmp, offset, size)) { + if (!memory_region_section_intersect_range(&tmp, offset, size)) { continue; } rdl2->notify_discard(rdl2, &tmp); diff --git a/include/system/memory.h b/include/system/memory.h index 0848690ea4..da97753e28 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -1211,6 +1211,36 @@ MemoryRegionSection *memory_region_section_new_copy(MemoryRegionSection *s); */ void memory_region_section_free_copy(MemoryRegionSection *s); +/** + * memory_region_section_intersect_range: Adjust the memory section to cover + * the intersection with the given range. + * + * @s: the #MemoryRegionSection to be adjusted + * @offset: the offset of the given range in the memory region + * @size: the size of the given range + * + * Returns false if the intersection is empty, otherwise returns true. + */ +static inline bool memory_region_section_intersect_range(MemoryRegionSection *s, + uint64_t offset, + uint64_t size) +{ + uint64_t start = MAX(s->offset_within_region, offset); + Int128 end = int128_min(int128_add(int128_make64(s->offset_within_region), + s->size), + int128_add(int128_make64(offset), + int128_make64(size))); + + if (int128_le(end, int128_make64(start))) { + return false; + } + + s->offset_within_address_space += start - s->offset_within_region; + s->offset_within_region = start; + s->size = int128_sub(end, int128_make64(start)); + return true; +} + /** * memory_region_init: Initialize a memory region * From ff1211154c45c9f7f82116ae9a8c72a848e4a8b5 Mon Sep 17 00:00:00 2001 From: Chenyi Qiang Date: Thu, 12 Jun 2025 16:27:43 +0800 Subject: [PATCH 1642/2760] memory: Change memory_region_set_ram_discard_manager() to return the result Modify memory_region_set_ram_discard_manager() to return -EBUSY if a RamDiscardManager is already set in the MemoryRegion. The caller must handle this failure, such as having virtio-mem undo its actions and fail the realize() process. Opportunistically move the call earlier to avoid complex error handling. This change is beneficial when introducing a new RamDiscardManager instance besides virtio-mem. After ram_block_coordinated_discard_require(true) unlocks all RamDiscardManager instances, only one instance is allowed to be set for one MemoryRegion at present. Suggested-by: David Hildenbrand Reviewed-by: David Hildenbrand Reviewed-by: Pankaj Gupta Tested-by: Alexey Kardashevskiy Reviewed-by: Alexey Kardashevskiy Reviewed-by: Xiaoyao Li Signed-off-by: Chenyi Qiang Link: https://lore.kernel.org/r/20250612082747.51539-3-chenyi.qiang@intel.com Signed-off-by: Peter Xu --- hw/virtio/virtio-mem.c | 30 +++++++++++++++++------------- include/system/memory.h | 6 +++--- system/memory.c | 10 +++++++--- 3 files changed, 27 insertions(+), 19 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index b3c126ea1e..2e491e8c44 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -1047,6 +1047,17 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) return; } + /* + * Set ourselves as RamDiscardManager before the plug handler maps the + * memory region and exposes it via an address space. + */ + if (memory_region_set_ram_discard_manager(&vmem->memdev->mr, + RAM_DISCARD_MANAGER(vmem))) { + error_setg(errp, "Failed to set RamDiscardManager"); + ram_block_coordinated_discard_require(false); + return; + } + /* * We don't know at this point whether shared RAM is migrated using * QEMU or migrated using the file content. "x-ignore-shared" will be @@ -1061,6 +1072,7 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) ret = ram_block_discard_range(rb, 0, qemu_ram_get_used_length(rb)); if (ret) { error_setg_errno(errp, -ret, "Unexpected error discarding RAM"); + memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL); ram_block_coordinated_discard_require(false); return; } @@ -1122,13 +1134,6 @@ static void virtio_mem_device_realize(DeviceState *dev, Error **errp) vmem->system_reset = VIRTIO_MEM_SYSTEM_RESET(obj); vmem->system_reset->vmem = vmem; qemu_register_resettable(obj); - - /* - * Set ourselves as RamDiscardManager before the plug handler maps the - * memory region and exposes it via an address space. - */ - memory_region_set_ram_discard_manager(&vmem->memdev->mr, - RAM_DISCARD_MANAGER(vmem)); } static void virtio_mem_device_unrealize(DeviceState *dev) @@ -1136,12 +1141,6 @@ static void virtio_mem_device_unrealize(DeviceState *dev) VirtIODevice *vdev = VIRTIO_DEVICE(dev); VirtIOMEM *vmem = VIRTIO_MEM(dev); - /* - * The unplug handler unmapped the memory region, it cannot be - * found via an address space anymore. Unset ourselves. - */ - memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL); - qemu_unregister_resettable(OBJECT(vmem->system_reset)); object_unref(OBJECT(vmem->system_reset)); @@ -1154,6 +1153,11 @@ static void virtio_mem_device_unrealize(DeviceState *dev) virtio_del_queue(vdev, 0); virtio_cleanup(vdev); g_free(vmem->bitmap); + /* + * The unplug handler unmapped the memory region, it cannot be + * found via an address space anymore. Unset ourselves. + */ + memory_region_set_ram_discard_manager(&vmem->memdev->mr, NULL); ram_block_coordinated_discard_require(false); } diff --git a/include/system/memory.h b/include/system/memory.h index da97753e28..60983d4977 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -2499,13 +2499,13 @@ static inline bool memory_region_has_ram_discard_manager(MemoryRegion *mr) * * This function must not be called for a mapped #MemoryRegion, a #MemoryRegion * that does not cover RAM, or a #MemoryRegion that already has a - * #RamDiscardManager assigned. + * #RamDiscardManager assigned. Return 0 if the rdm is set successfully. * * @mr: the #MemoryRegion * @rdm: #RamDiscardManager to set */ -void memory_region_set_ram_discard_manager(MemoryRegion *mr, - RamDiscardManager *rdm); +int memory_region_set_ram_discard_manager(MemoryRegion *mr, + RamDiscardManager *rdm); /** * memory_region_find: translate an address/size relative to a diff --git a/system/memory.c b/system/memory.c index 306e9ff9eb..d0c186e9f6 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2106,12 +2106,16 @@ RamDiscardManager *memory_region_get_ram_discard_manager(MemoryRegion *mr) return mr->rdm; } -void memory_region_set_ram_discard_manager(MemoryRegion *mr, - RamDiscardManager *rdm) +int memory_region_set_ram_discard_manager(MemoryRegion *mr, + RamDiscardManager *rdm) { g_assert(memory_region_is_ram(mr)); - g_assert(!rdm || !mr->rdm); + if (mr->rdm && rdm) { + return -EBUSY; + } + mr->rdm = rdm; + return 0; } uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *rdm, From 2205b8466733f8c6e3306c964f31c5a7cac69dfa Mon Sep 17 00:00:00 2001 From: Chenyi Qiang Date: Thu, 12 Jun 2025 16:27:44 +0800 Subject: [PATCH 1643/2760] memory: Unify the definiton of ReplayRamPopulate() and ReplayRamDiscard() Update ReplayRamDiscard() function to return the result and unify the ReplayRamPopulate() and ReplayRamDiscard() to ReplayRamDiscardState() at the same time due to their identical definitions. This unification simplifies related structures, such as VirtIOMEMReplayData, which makes it cleaner. Reviewed-by: David Hildenbrand Reviewed-by: Pankaj Gupta Reviewed-by: Xiaoyao Li Signed-off-by: Chenyi Qiang Link: https://lore.kernel.org/r/20250612082747.51539-4-chenyi.qiang@intel.com Signed-off-by: Peter Xu --- hw/virtio/virtio-mem.c | 21 ++++++------ include/system/memory.h | 74 +++++++++++++++++++++++++++++++---------- migration/ram.c | 5 +-- system/memory.c | 12 +++---- 4 files changed, 76 insertions(+), 36 deletions(-) diff --git a/hw/virtio/virtio-mem.c b/hw/virtio/virtio-mem.c index 2e491e8c44..c46f6f9c3e 100644 --- a/hw/virtio/virtio-mem.c +++ b/hw/virtio/virtio-mem.c @@ -1732,7 +1732,7 @@ static bool virtio_mem_rdm_is_populated(const RamDiscardManager *rdm, } struct VirtIOMEMReplayData { - void *fn; + ReplayRamDiscardState fn; void *opaque; }; @@ -1740,12 +1740,12 @@ static int virtio_mem_rdm_replay_populated_cb(MemoryRegionSection *s, void *arg) { struct VirtIOMEMReplayData *data = arg; - return ((ReplayRamPopulate)data->fn)(s, data->opaque); + return data->fn(s, data->opaque); } static int virtio_mem_rdm_replay_populated(const RamDiscardManager *rdm, MemoryRegionSection *s, - ReplayRamPopulate replay_fn, + ReplayRamDiscardState replay_fn, void *opaque) { const VirtIOMEM *vmem = VIRTIO_MEM(rdm); @@ -1764,14 +1764,13 @@ static int virtio_mem_rdm_replay_discarded_cb(MemoryRegionSection *s, { struct VirtIOMEMReplayData *data = arg; - ((ReplayRamDiscard)data->fn)(s, data->opaque); - return 0; + return data->fn(s, data->opaque); } -static void virtio_mem_rdm_replay_discarded(const RamDiscardManager *rdm, - MemoryRegionSection *s, - ReplayRamDiscard replay_fn, - void *opaque) +static int virtio_mem_rdm_replay_discarded(const RamDiscardManager *rdm, + MemoryRegionSection *s, + ReplayRamDiscardState replay_fn, + void *opaque) { const VirtIOMEM *vmem = VIRTIO_MEM(rdm); struct VirtIOMEMReplayData data = { @@ -1780,8 +1779,8 @@ static void virtio_mem_rdm_replay_discarded(const RamDiscardManager *rdm, }; g_assert(s->mr == &vmem->memdev->mr); - virtio_mem_for_each_unplugged_section(vmem, s, &data, - virtio_mem_rdm_replay_discarded_cb); + return virtio_mem_for_each_unplugged_section(vmem, s, &data, + virtio_mem_rdm_replay_discarded_cb); } static void virtio_mem_rdm_register_listener(RamDiscardManager *rdm, diff --git a/include/system/memory.h b/include/system/memory.h index 60983d4977..46248d4a52 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -576,8 +576,20 @@ static inline void ram_discard_listener_init(RamDiscardListener *rdl, rdl->double_discard_supported = double_discard_supported; } -typedef int (*ReplayRamPopulate)(MemoryRegionSection *section, void *opaque); -typedef void (*ReplayRamDiscard)(MemoryRegionSection *section, void *opaque); +/** + * typedef ReplayRamDiscardState: + * + * The callback handler for #RamDiscardManagerClass.replay_populated/ + * #RamDiscardManagerClass.replay_discarded to invoke on populated/discarded + * parts. + * + * @section: the #MemoryRegionSection of populated/discarded part + * @opaque: pointer to forward to the callback + * + * Returns 0 on success, or a negative error if failed. + */ +typedef int (*ReplayRamDiscardState)(MemoryRegionSection *section, + void *opaque); /* * RamDiscardManagerClass: @@ -651,36 +663,38 @@ struct RamDiscardManagerClass { /** * @replay_populated: * - * Call the #ReplayRamPopulate callback for all populated parts within the - * #MemoryRegionSection via the #RamDiscardManager. + * Call the #ReplayRamDiscardState callback for all populated parts within + * the #MemoryRegionSection via the #RamDiscardManager. * * In case any call fails, no further calls are made. * * @rdm: the #RamDiscardManager * @section: the #MemoryRegionSection - * @replay_fn: the #ReplayRamPopulate callback + * @replay_fn: the #ReplayRamDiscardState callback * @opaque: pointer to forward to the callback * * Returns 0 on success, or a negative error if any notification failed. */ int (*replay_populated)(const RamDiscardManager *rdm, MemoryRegionSection *section, - ReplayRamPopulate replay_fn, void *opaque); + ReplayRamDiscardState replay_fn, void *opaque); /** * @replay_discarded: * - * Call the #ReplayRamDiscard callback for all discarded parts within the - * #MemoryRegionSection via the #RamDiscardManager. + * Call the #ReplayRamDiscardState callback for all discarded parts within + * the #MemoryRegionSection via the #RamDiscardManager. * * @rdm: the #RamDiscardManager * @section: the #MemoryRegionSection - * @replay_fn: the #ReplayRamDiscard callback + * @replay_fn: the #ReplayRamDiscardState callback * @opaque: pointer to forward to the callback + * + * Returns 0 on success, or a negative error if any notification failed. */ - void (*replay_discarded)(const RamDiscardManager *rdm, - MemoryRegionSection *section, - ReplayRamDiscard replay_fn, void *opaque); + int (*replay_discarded)(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscardState replay_fn, void *opaque); /** * @register_listener: @@ -721,15 +735,41 @@ uint64_t ram_discard_manager_get_min_granularity(const RamDiscardManager *rdm, bool ram_discard_manager_is_populated(const RamDiscardManager *rdm, const MemoryRegionSection *section); +/** + * ram_discard_manager_replay_populated: + * + * A wrapper to call the #RamDiscardManagerClass.replay_populated callback + * of the #RamDiscardManager. + * + * @rdm: the #RamDiscardManager + * @section: the #MemoryRegionSection + * @replay_fn: the #ReplayRamDiscardState callback + * @opaque: pointer to forward to the callback + * + * Returns 0 on success, or a negative error if any notification failed. + */ int ram_discard_manager_replay_populated(const RamDiscardManager *rdm, MemoryRegionSection *section, - ReplayRamPopulate replay_fn, + ReplayRamDiscardState replay_fn, void *opaque); -void ram_discard_manager_replay_discarded(const RamDiscardManager *rdm, - MemoryRegionSection *section, - ReplayRamDiscard replay_fn, - void *opaque); +/** + * ram_discard_manager_replay_discarded: + * + * A wrapper to call the #RamDiscardManagerClass.replay_discarded callback + * of the #RamDiscardManager. + * + * @rdm: the #RamDiscardManager + * @section: the #MemoryRegionSection + * @replay_fn: the #ReplayRamDiscardState callback + * @opaque: pointer to forward to the callback + * + * Returns 0 on success, or a negative error if any notification failed. + */ +int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscardState replay_fn, + void *opaque); void ram_discard_manager_register_listener(RamDiscardManager *rdm, RamDiscardListener *rdl, diff --git a/migration/ram.c b/migration/ram.c index 024d8b3d03..2140785a05 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -856,8 +856,8 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, return ret; } -static void dirty_bitmap_clear_section(MemoryRegionSection *section, - void *opaque) +static int dirty_bitmap_clear_section(MemoryRegionSection *section, + void *opaque) { const hwaddr offset = section->offset_within_region; const hwaddr size = int128_get64(section->size); @@ -876,6 +876,7 @@ static void dirty_bitmap_clear_section(MemoryRegionSection *section, } *cleared_bits += bitmap_count_one_with_offset(rb->bmap, start, npages); bitmap_clear(rb->bmap, start, npages); + return 0; } /* diff --git a/system/memory.c b/system/memory.c index d0c186e9f6..76b44b8220 100644 --- a/system/memory.c +++ b/system/memory.c @@ -2138,7 +2138,7 @@ bool ram_discard_manager_is_populated(const RamDiscardManager *rdm, int ram_discard_manager_replay_populated(const RamDiscardManager *rdm, MemoryRegionSection *section, - ReplayRamPopulate replay_fn, + ReplayRamDiscardState replay_fn, void *opaque) { RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm); @@ -2147,15 +2147,15 @@ int ram_discard_manager_replay_populated(const RamDiscardManager *rdm, return rdmc->replay_populated(rdm, section, replay_fn, opaque); } -void ram_discard_manager_replay_discarded(const RamDiscardManager *rdm, - MemoryRegionSection *section, - ReplayRamDiscard replay_fn, - void *opaque) +int ram_discard_manager_replay_discarded(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscardState replay_fn, + void *opaque) { RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_GET_CLASS(rdm); g_assert(rdmc->replay_discarded); - rdmc->replay_discarded(rdm, section, replay_fn, opaque); + return rdmc->replay_discarded(rdm, section, replay_fn, opaque); } void ram_discard_manager_register_listener(RamDiscardManager *rdm, From 5d6483edaa9232d8f3709f68c8eab4bc2033fb70 Mon Sep 17 00:00:00 2001 From: Chenyi Qiang Date: Thu, 12 Jun 2025 16:27:45 +0800 Subject: [PATCH 1644/2760] ram-block-attributes: Introduce RamBlockAttributes to manage RAMBlock with guest_memfd Commit 852f0048f3 ("RAMBlock: make guest_memfd require uncoordinated discard") highlighted that subsystems like VFIO may disable RAM block discard. However, guest_memfd relies on discard operations for page conversion between private and shared memory, potentially leading to the stale IOMMU mapping issue when assigning hardware devices to confidential VMs via shared memory. To address this and allow shared device assignement, it is crucial to ensure the VFIO system refreshes its IOMMU mappings. RamDiscardManager is an existing interface (used by virtio-mem) to adjust VFIO mappings in relation to VM page assignment. Effectively page conversion is similar to hot-removing a page in one mode and adding it back in the other. Therefore, similar actions are required for page conversion events. Introduce the RamDiscardManager to guest_memfd to facilitate this process. Since guest_memfd is not an object, it cannot directly implement the RamDiscardManager interface. Implementing it in HostMemoryBackend is not appropriate because guest_memfd is per RAMBlock, and some RAMBlocks have a memory backend while others do not. Notably, virtual BIOS RAMBlocks using memory_region_init_ram_guest_memfd() do not have a backend. To manage RAMBlocks with guest_memfd, define a new object named RamBlockAttributes to implement the RamDiscardManager interface. This object can store the guest_memfd information such as the bitmap for shared memory and the registered listeners for event notifications. A new state_change() helper function is provided to notify listeners, such as VFIO, allowing VFIO to do dynamically DMA map and unmap for the shared memory according to conversion events. Note that in the current context of RamDiscardManager for guest_memfd, the shared state is analogous to being populated, while the private state can be considered discarded for simplicity. In the future, it would be more complicated if considering more states like private/shared/discarded at the same time. In current implementation, memory state tracking is performed at the host page size granularity, as the minimum conversion size can be one page per request. Additionally, VFIO expected the DMA mapping for a specific IOVA to be mapped and unmapped with the same granularity. Confidential VMs may perform partial conversions, such as conversions on small regions within a larger one. To prevent such invalid cases and until support for DMA mapping cut operations is available, all operations are performed with 4K granularity. In addition, memory conversion failures cause QEMU to quit rather than resuming the guest or retrying the operation at present. It would be future work to add more error handling or rollback mechanisms once conversion failures are allowed. For example, in-place conversion of guest_memfd could retry the unmap operation during the conversion from shared to private. For now, keep the complex error handling out of the picture as it is not required. Tested-by: Alexey Kardashevskiy Reviewed-by: Alexey Kardashevskiy Reviewed-by: Pankaj Gupta Signed-off-by: Chenyi Qiang Link: https://lore.kernel.org/r/20250612082747.51539-5-chenyi.qiang@intel.com [peterx: squash fixup from Chenyi to fix builds] Signed-off-by: Peter Xu --- MAINTAINERS | 1 + include/system/ramblock.h | 21 ++ system/meson.build | 1 + system/ram-block-attributes.c | 444 ++++++++++++++++++++++++++++++++++ system/trace-events | 3 + 5 files changed, 470 insertions(+) create mode 100644 system/ram-block-attributes.c diff --git a/MAINTAINERS b/MAINTAINERS index 94c4076127..27f4fe3f25 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3175,6 +3175,7 @@ F: system/memory.c F: system/memory_mapping.c F: system/physmem.c F: system/memory-internal.h +F: system/ram-block-attributes.c F: scripts/coccinelle/memory-region-housekeeping.cocci Memory devices diff --git a/include/system/ramblock.h b/include/system/ramblock.h index d8a116ba99..1bab9e2dac 100644 --- a/include/system/ramblock.h +++ b/include/system/ramblock.h @@ -22,6 +22,10 @@ #include "exec/cpu-common.h" #include "qemu/rcu.h" #include "exec/ramlist.h" +#include "system/hostmem.h" + +#define TYPE_RAM_BLOCK_ATTRIBUTES "ram-block-attributes" +OBJECT_DECLARE_SIMPLE_TYPE(RamBlockAttributes, RAM_BLOCK_ATTRIBUTES) struct RAMBlock { struct rcu_head rcu; @@ -91,4 +95,21 @@ struct RAMBlock { ram_addr_t postcopy_length; }; +struct RamBlockAttributes { + Object parent; + + RAMBlock *ram_block; + + /* 1-setting of the bitmap represents ram is populated (shared) */ + unsigned bitmap_size; + unsigned long *bitmap; + + QLIST_HEAD(, RamDiscardListener) rdl_list; +}; + +RamBlockAttributes *ram_block_attributes_create(RAMBlock *ram_block); +void ram_block_attributes_destroy(RamBlockAttributes *attr); +int ram_block_attributes_state_change(RamBlockAttributes *attr, uint64_t offset, + uint64_t size, bool to_discard); + #endif diff --git a/system/meson.build b/system/meson.build index 7514bf3455..6d21ff9faa 100644 --- a/system/meson.build +++ b/system/meson.build @@ -17,6 +17,7 @@ system_ss.add(files( 'dma-helpers.c', 'globals.c', 'ioport.c', + 'ram-block-attributes.c', 'memory_mapping.c', 'memory.c', 'physmem.c', diff --git a/system/ram-block-attributes.c b/system/ram-block-attributes.c new file mode 100644 index 0000000000..68e8a02703 --- /dev/null +++ b/system/ram-block-attributes.c @@ -0,0 +1,444 @@ +/* + * QEMU ram block attributes + * + * Copyright Intel + * + * Author: + * Chenyi Qiang + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "system/ramblock.h" +#include "trace.h" + +OBJECT_DEFINE_SIMPLE_TYPE_WITH_INTERFACES(RamBlockAttributes, + ram_block_attributes, + RAM_BLOCK_ATTRIBUTES, + OBJECT, + { TYPE_RAM_DISCARD_MANAGER }, + { }) + +static size_t +ram_block_attributes_get_block_size(const RamBlockAttributes *attr) +{ + /* + * Because page conversion could be manipulated in the size of at least 4K + * or 4K aligned, Use the host page size as the granularity to track the + * memory attribute. + */ + g_assert(attr && attr->ram_block); + g_assert(attr->ram_block->page_size == qemu_real_host_page_size()); + return attr->ram_block->page_size; +} + + +static bool +ram_block_attributes_rdm_is_populated(const RamDiscardManager *rdm, + const MemoryRegionSection *section) +{ + const RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm); + const size_t block_size = ram_block_attributes_get_block_size(attr); + const uint64_t first_bit = section->offset_within_region / block_size; + const uint64_t last_bit = + first_bit + int128_get64(section->size) / block_size - 1; + unsigned long first_discarded_bit; + + first_discarded_bit = find_next_zero_bit(attr->bitmap, last_bit + 1, + first_bit); + return first_discarded_bit > last_bit; +} + +typedef int (*ram_block_attributes_section_cb)(MemoryRegionSection *s, + void *arg); + +static int +ram_block_attributes_notify_populate_cb(MemoryRegionSection *section, + void *arg) +{ + RamDiscardListener *rdl = arg; + + return rdl->notify_populate(rdl, section); +} + +static int +ram_block_attributes_notify_discard_cb(MemoryRegionSection *section, + void *arg) +{ + RamDiscardListener *rdl = arg; + + rdl->notify_discard(rdl, section); + return 0; +} + +static int +ram_block_attributes_for_each_populated_section(const RamBlockAttributes *attr, + MemoryRegionSection *section, + void *arg, + ram_block_attributes_section_cb cb) +{ + unsigned long first_bit, last_bit; + uint64_t offset, size; + const size_t block_size = ram_block_attributes_get_block_size(attr); + int ret = 0; + + first_bit = section->offset_within_region / block_size; + first_bit = find_next_bit(attr->bitmap, attr->bitmap_size, + first_bit); + + while (first_bit < attr->bitmap_size) { + MemoryRegionSection tmp = *section; + + offset = first_bit * block_size; + last_bit = find_next_zero_bit(attr->bitmap, attr->bitmap_size, + first_bit + 1) - 1; + size = (last_bit - first_bit + 1) * block_size; + + if (!memory_region_section_intersect_range(&tmp, offset, size)) { + break; + } + + ret = cb(&tmp, arg); + if (ret) { + error_report("%s: Failed to notify RAM discard listener: %s", + __func__, strerror(-ret)); + break; + } + + first_bit = find_next_bit(attr->bitmap, attr->bitmap_size, + last_bit + 2); + } + + return ret; +} + +static int +ram_block_attributes_for_each_discarded_section(const RamBlockAttributes *attr, + MemoryRegionSection *section, + void *arg, + ram_block_attributes_section_cb cb) +{ + unsigned long first_bit, last_bit; + uint64_t offset, size; + const size_t block_size = ram_block_attributes_get_block_size(attr); + int ret = 0; + + first_bit = section->offset_within_region / block_size; + first_bit = find_next_zero_bit(attr->bitmap, attr->bitmap_size, + first_bit); + + while (first_bit < attr->bitmap_size) { + MemoryRegionSection tmp = *section; + + offset = first_bit * block_size; + last_bit = find_next_bit(attr->bitmap, attr->bitmap_size, + first_bit + 1) - 1; + size = (last_bit - first_bit + 1) * block_size; + + if (!memory_region_section_intersect_range(&tmp, offset, size)) { + break; + } + + ret = cb(&tmp, arg); + if (ret) { + error_report("%s: Failed to notify RAM discard listener: %s", + __func__, strerror(-ret)); + break; + } + + first_bit = find_next_zero_bit(attr->bitmap, + attr->bitmap_size, + last_bit + 2); + } + + return ret; +} + +static uint64_t +ram_block_attributes_rdm_get_min_granularity(const RamDiscardManager *rdm, + const MemoryRegion *mr) +{ + const RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm); + + g_assert(mr == attr->ram_block->mr); + return ram_block_attributes_get_block_size(attr); +} + +static void +ram_block_attributes_rdm_register_listener(RamDiscardManager *rdm, + RamDiscardListener *rdl, + MemoryRegionSection *section) +{ + RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm); + int ret; + + g_assert(section->mr == attr->ram_block->mr); + rdl->section = memory_region_section_new_copy(section); + + QLIST_INSERT_HEAD(&attr->rdl_list, rdl, next); + + ret = ram_block_attributes_for_each_populated_section(attr, section, rdl, + ram_block_attributes_notify_populate_cb); + if (ret) { + error_report("%s: Failed to register RAM discard listener: %s", + __func__, strerror(-ret)); + exit(1); + } +} + +static void +ram_block_attributes_rdm_unregister_listener(RamDiscardManager *rdm, + RamDiscardListener *rdl) +{ + RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm); + int ret; + + g_assert(rdl->section); + g_assert(rdl->section->mr == attr->ram_block->mr); + + if (rdl->double_discard_supported) { + rdl->notify_discard(rdl, rdl->section); + } else { + ret = ram_block_attributes_for_each_populated_section(attr, + rdl->section, rdl, ram_block_attributes_notify_discard_cb); + if (ret) { + error_report("%s: Failed to unregister RAM discard listener: %s", + __func__, strerror(-ret)); + exit(1); + } + } + + memory_region_section_free_copy(rdl->section); + rdl->section = NULL; + QLIST_REMOVE(rdl, next); +} + +typedef struct RamBlockAttributesReplayData { + ReplayRamDiscardState fn; + void *opaque; +} RamBlockAttributesReplayData; + +static int ram_block_attributes_rdm_replay_cb(MemoryRegionSection *section, + void *arg) +{ + RamBlockAttributesReplayData *data = arg; + + return data->fn(section, data->opaque); +} + +static int +ram_block_attributes_rdm_replay_populated(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscardState replay_fn, + void *opaque) +{ + RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm); + RamBlockAttributesReplayData data = { .fn = replay_fn, .opaque = opaque }; + + g_assert(section->mr == attr->ram_block->mr); + return ram_block_attributes_for_each_populated_section(attr, section, &data, + ram_block_attributes_rdm_replay_cb); +} + +static int +ram_block_attributes_rdm_replay_discarded(const RamDiscardManager *rdm, + MemoryRegionSection *section, + ReplayRamDiscardState replay_fn, + void *opaque) +{ + RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(rdm); + RamBlockAttributesReplayData data = { .fn = replay_fn, .opaque = opaque }; + + g_assert(section->mr == attr->ram_block->mr); + return ram_block_attributes_for_each_discarded_section(attr, section, &data, + ram_block_attributes_rdm_replay_cb); +} + +static bool +ram_block_attributes_is_valid_range(RamBlockAttributes *attr, uint64_t offset, + uint64_t size) +{ + MemoryRegion *mr = attr->ram_block->mr; + + g_assert(mr); + + uint64_t region_size = memory_region_size(mr); + const size_t block_size = ram_block_attributes_get_block_size(attr); + + if (!QEMU_IS_ALIGNED(offset, block_size) || + !QEMU_IS_ALIGNED(size, block_size)) { + return false; + } + if (offset + size <= offset) { + return false; + } + if (offset + size > region_size) { + return false; + } + return true; +} + +static void ram_block_attributes_notify_discard(RamBlockAttributes *attr, + uint64_t offset, + uint64_t size) +{ + RamDiscardListener *rdl; + + QLIST_FOREACH(rdl, &attr->rdl_list, next) { + MemoryRegionSection tmp = *rdl->section; + + if (!memory_region_section_intersect_range(&tmp, offset, size)) { + continue; + } + rdl->notify_discard(rdl, &tmp); + } +} + +static int +ram_block_attributes_notify_populate(RamBlockAttributes *attr, + uint64_t offset, uint64_t size) +{ + RamDiscardListener *rdl; + int ret = 0; + + QLIST_FOREACH(rdl, &attr->rdl_list, next) { + MemoryRegionSection tmp = *rdl->section; + + if (!memory_region_section_intersect_range(&tmp, offset, size)) { + continue; + } + ret = rdl->notify_populate(rdl, &tmp); + if (ret) { + break; + } + } + + return ret; +} + +int ram_block_attributes_state_change(RamBlockAttributes *attr, + uint64_t offset, uint64_t size, + bool to_discard) +{ + const size_t block_size = ram_block_attributes_get_block_size(attr); + const unsigned long first_bit = offset / block_size; + const unsigned long nbits = size / block_size; + const unsigned long last_bit = first_bit + nbits - 1; + const bool is_discarded = find_next_bit(attr->bitmap, attr->bitmap_size, + first_bit) > last_bit; + const bool is_populated = find_next_zero_bit(attr->bitmap, + attr->bitmap_size, first_bit) > last_bit; + unsigned long bit; + int ret = 0; + + if (!ram_block_attributes_is_valid_range(attr, offset, size)) { + error_report("%s, invalid range: offset 0x%" PRIx64 ", size " + "0x%" PRIx64, __func__, offset, size); + return -EINVAL; + } + + trace_ram_block_attributes_state_change(offset, size, + is_discarded ? "discarded" : + is_populated ? "populated" : + "mixture", + to_discard ? "discarded" : + "populated"); + if (to_discard) { + if (is_discarded) { + /* Already private */ + } else if (is_populated) { + /* Completely shared */ + bitmap_clear(attr->bitmap, first_bit, nbits); + ram_block_attributes_notify_discard(attr, offset, size); + } else { + /* Unexpected mixture: process individual blocks */ + for (bit = first_bit; bit < first_bit + nbits; bit++) { + if (!test_bit(bit, attr->bitmap)) { + continue; + } + clear_bit(bit, attr->bitmap); + ram_block_attributes_notify_discard(attr, bit * block_size, + block_size); + } + } + } else { + if (is_populated) { + /* Already shared */ + } else if (is_discarded) { + /* Completely private */ + bitmap_set(attr->bitmap, first_bit, nbits); + ret = ram_block_attributes_notify_populate(attr, offset, size); + } else { + /* Unexpected mixture: process individual blocks */ + for (bit = first_bit; bit < first_bit + nbits; bit++) { + if (test_bit(bit, attr->bitmap)) { + continue; + } + set_bit(bit, attr->bitmap); + ret = ram_block_attributes_notify_populate(attr, + bit * block_size, + block_size); + if (ret) { + break; + } + } + } + } + + return ret; +} + +RamBlockAttributes *ram_block_attributes_create(RAMBlock *ram_block) +{ + const int block_size = qemu_real_host_page_size(); + RamBlockAttributes *attr; + MemoryRegion *mr = ram_block->mr; + + attr = RAM_BLOCK_ATTRIBUTES(object_new(TYPE_RAM_BLOCK_ATTRIBUTES)); + + attr->ram_block = ram_block; + if (memory_region_set_ram_discard_manager(mr, RAM_DISCARD_MANAGER(attr))) { + object_unref(OBJECT(attr)); + return NULL; + } + attr->bitmap_size = + ROUND_UP(int128_get64(mr->size), block_size) / block_size; + attr->bitmap = bitmap_new(attr->bitmap_size); + + return attr; +} + +void ram_block_attributes_destroy(RamBlockAttributes *attr) +{ + g_assert(attr); + + g_free(attr->bitmap); + memory_region_set_ram_discard_manager(attr->ram_block->mr, NULL); + object_unref(OBJECT(attr)); +} + +static void ram_block_attributes_init(Object *obj) +{ + RamBlockAttributes *attr = RAM_BLOCK_ATTRIBUTES(obj); + + QLIST_INIT(&attr->rdl_list); +} + +static void ram_block_attributes_finalize(Object *obj) +{ +} + +static void ram_block_attributes_class_init(ObjectClass *klass, + const void *data) +{ + RamDiscardManagerClass *rdmc = RAM_DISCARD_MANAGER_CLASS(klass); + + rdmc->get_min_granularity = ram_block_attributes_rdm_get_min_granularity; + rdmc->register_listener = ram_block_attributes_rdm_register_listener; + rdmc->unregister_listener = ram_block_attributes_rdm_unregister_listener; + rdmc->is_populated = ram_block_attributes_rdm_is_populated; + rdmc->replay_populated = ram_block_attributes_rdm_replay_populated; + rdmc->replay_discarded = ram_block_attributes_rdm_replay_discarded; +} diff --git a/system/trace-events b/system/trace-events index be12ebfb41..82856e44f2 100644 --- a/system/trace-events +++ b/system/trace-events @@ -52,3 +52,6 @@ dirtylimit_state_finalize(void) dirtylimit_throttle_pct(int cpu_index, uint64_t pct, int64_t time_us) "CPU[%d] throttle percent: %" PRIu64 ", throttle adjust time %"PRIi64 " us" dirtylimit_set_vcpu(int cpu_index, uint64_t quota) "CPU[%d] set dirty page rate limit %"PRIu64 dirtylimit_vcpu_execute(int cpu_index, int64_t sleep_time_us) "CPU[%d] sleep %"PRIi64 " us" + +# ram-block-attributes.c +ram_block_attributes_state_change(uint64_t offset, uint64_t size, const char *from, const char *to) "offset 0x%"PRIx64" size 0x%"PRIx64" from '%s' to '%s'" From 2fde3fb916079ee0ff0fc26d9446c813b1d5cc28 Mon Sep 17 00:00:00 2001 From: Chenyi Qiang Date: Thu, 12 Jun 2025 16:27:46 +0800 Subject: [PATCH 1645/2760] physmem: Support coordinated discarding of RAM with guest_memfd A new field, attributes, was introduced in RAMBlock to link to a RamBlockAttributes object, which centralizes all guest_memfd related information (such as fd and status bitmap) within a RAMBlock. Create and initialize the RamBlockAttributes object upon ram_block_add(). Meanwhile, register the object in the target RAMBlock's MemoryRegion. After that, guest_memfd-backed RAMBlock is associated with the RamDiscardManager interface, and the users can execute RamDiscardManager specific handling. For example, VFIO will register the RamDiscardListener and get notifications when the state_change() helper invokes. As coordinate discarding of RAM with guest_memfd is now supported, only block uncoordinated discard. Tested-by: Alexey Kardashevskiy Reviewed-by: Alexey Kardashevskiy Acked-by: David Hildenbrand Signed-off-by: Chenyi Qiang Link: https://lore.kernel.org/r/20250612082747.51539-6-chenyi.qiang@intel.com Signed-off-by: Peter Xu --- accel/kvm/kvm-all.c | 9 +++++++++ include/system/ramblock.h | 1 + system/physmem.c | 23 +++++++++++++++++++++-- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index a31778341c..d095d1b98f 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3091,6 +3091,15 @@ int kvm_convert_memory(hwaddr start, hwaddr size, bool to_private) addr = memory_region_get_ram_ptr(mr) + section.offset_within_region; rb = qemu_ram_block_from_host(addr, false, &offset); + ret = ram_block_attributes_state_change(RAM_BLOCK_ATTRIBUTES(mr->rdm), + offset, size, to_private); + if (ret) { + error_report("Failed to notify the listener the state change of " + "(0x%"HWADDR_PRIx" + 0x%"HWADDR_PRIx") to %s", + start, size, to_private ? "private" : "shared"); + goto out_unref; + } + if (to_private) { if (rb->page_size != qemu_real_host_page_size()) { /* diff --git a/include/system/ramblock.h b/include/system/ramblock.h index 1bab9e2dac..87e847e184 100644 --- a/include/system/ramblock.h +++ b/include/system/ramblock.h @@ -46,6 +46,7 @@ struct RAMBlock { int fd; uint64_t fd_offset; int guest_memfd; + RamBlockAttributes *attributes; size_t page_size; /* dirty bitmap used during migration */ unsigned long *bmap; diff --git a/system/physmem.c b/system/physmem.c index a8a9ca309e..ff0ca40222 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1916,7 +1916,7 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) } assert(new_block->guest_memfd < 0); - ret = ram_block_discard_require(true); + ret = ram_block_coordinated_discard_require(true); if (ret < 0) { error_setg_errno(errp, -ret, "cannot set up private guest memory: discard currently blocked"); @@ -1931,6 +1931,24 @@ static void ram_block_add(RAMBlock *new_block, Error **errp) goto out_free; } + /* + * The attribute bitmap of the RamBlockAttributes is default to + * discarded, which mimics the behavior of kvm_set_phys_mem() when it + * calls kvm_set_memory_attributes_private(). This leads to a brief + * period of inconsistency between the creation of the RAMBlock and its + * mapping into the physical address space. However, this is not + * problematic, as no users rely on the attribute status to perform + * any actions during this interval. + */ + new_block->attributes = ram_block_attributes_create(new_block); + if (!new_block->attributes) { + error_setg(errp, "Failed to create ram block attribute"); + close(new_block->guest_memfd); + ram_block_coordinated_discard_require(false); + qemu_mutex_unlock_ramlist(); + goto out_free; + } + /* * Add a specific guest_memfd blocker if a generic one would not be * added by ram_block_add_cpr_blocker. @@ -2287,8 +2305,9 @@ static void reclaim_ramblock(RAMBlock *block) } if (block->guest_memfd >= 0) { + ram_block_attributes_destroy(block->attributes); close(block->guest_memfd); - ram_block_discard_require(false); + ram_block_coordinated_discard_require(false); } g_free(block); From 2b0e4ecd9466d493664317ffcc95275406862390 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 16 Jun 2025 11:22:39 +0200 Subject: [PATCH 1646/2760] docs: introduce dedicated page about code provenance / sign-off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently we have a short paragraph saying that patches must include a Signed-off-by line, and merely link to the kernel documentation. The linked kernel docs have a lot of content beyond the part about sign-off an thus are misleading/distracting to QEMU contributors. This introduces a dedicated 'code-provenance' page in QEMU talking about why we require sign-off, explaining the other tags we commonly use, and what to do in some edge cases. Signed-off-by: Daniel P. Berrangé Reviewed-by: Peter Maydell Reviewed-by: Stefan Hajnoczi Reviewed-by: Alex Bennée Signed-off-by: Markus Armbruster Signed-off-by: Stefan Hajnoczi --- docs/devel/code-provenance.rst | 230 ++++++++++++++++++++++++++++++ docs/devel/index-process.rst | 1 + docs/devel/submitting-a-patch.rst | 25 +--- 3 files changed, 234 insertions(+), 22 deletions(-) create mode 100644 docs/devel/code-provenance.rst diff --git a/docs/devel/code-provenance.rst b/docs/devel/code-provenance.rst new file mode 100644 index 0000000000..95b2dd34e2 --- /dev/null +++ b/docs/devel/code-provenance.rst @@ -0,0 +1,230 @@ +.. _code-provenance: + +Code provenance +=============== + +Certifying patch submissions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The QEMU community **mandates** all contributors to certify provenance of +patch submissions they make to the project. To put it another way, +contributors must indicate that they are legally permitted to contribute to +the project. + +Certification is achieved with a low overhead by adding a single line to the +bottom of every git commit:: + + Signed-off-by: YOUR NAME + +The addition of this line asserts that the author of the patch is contributing +in accordance with the clauses specified in the +`Developer's Certificate of Origin `__: + +.. _dco: + + Developer's Certificate of Origin 1.1 + + By making a contribution to this project, I certify that: + + (a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + + (b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + + (c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + + (d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. + +The name used with "Signed-off-by" does not need to be your legal name, nor +birth name, nor appear on any government ID. It is the identity you choose to +be known by in the community, but should not be anonymous, nor misrepresent +whom you are. + +It is generally expected that the name and email addresses used in one of the +``Signed-off-by`` lines, matches that of the git commit ``Author`` field. +It's okay if you subscribe or contribute to the list via more than one +address, but using multiple addresses in one commit just confuses +things. + +If the person sending the mail is not one of the patch authors, they are +nonetheless expected to add their own ``Signed-off-by`` to comply with the +DCO clause (c). + +Multiple authorship +~~~~~~~~~~~~~~~~~~~ + +It is not uncommon for a patch to have contributions from multiple authors. In +this scenario, git commits will usually be expected to have a ``Signed-off-by`` +line for each contributor involved in creation of the patch. Some edge cases: + + * The non-primary author's contributions were so trivial that they can be + considered not subject to copyright. In this case the secondary authors + need not include a ``Signed-off-by``. + + This case most commonly applies where QEMU reviewers give short snippets + of code as suggested fixes to a patch. The reviewers don't need to have + their own ``Signed-off-by`` added unless their code suggestion was + unusually large, but it is common to add ``Suggested-by`` as a credit + for non-trivial code. + + * Both contributors work for the same employer and the employer requires + copyright assignment. + + It can be said that in this case a ``Signed-off-by`` is indicating that + the person has permission to contribute from their employer who is the + copyright holder. It is nonetheless still preferable to include a + ``Signed-off-by`` for each contributor, as in some countries employees are + not able to assign copyright to their employer, and it also covers any + time invested outside working hours. + +When multiple ``Signed-off-by`` tags are present, they should be strictly kept +in order of authorship, from oldest to newest. + +Other commit tags +~~~~~~~~~~~~~~~~~ + +While the ``Signed-off-by`` tag is mandatory, there are a number of other tags +that are commonly used during QEMU development: + + * **``Reviewed-by``**: when a QEMU community member reviews a patch on the + mailing list, if they consider the patch acceptable, they should send an + email reply containing a ``Reviewed-by`` tag. Subsystem maintainers who + review a patch should add this even if they are also adding their + ``Signed-off-by`` to the same commit. + + * **``Acked-by``**: when a QEMU subsystem maintainer approves a patch that + touches their subsystem, but intends to allow a different maintainer to + queue it and send a pull request, they would send a mail containing a + ``Acked-by`` tag. Where a patch touches multiple subsystems, ``Acked-by`` + only implies review of the maintainers' own areas of responsibility. If a + maintainer wants to indicate they have done a full review they should use + a ``Reviewed-by`` tag. + + * **``Tested-by``**: when a QEMU community member has functionally tested the + behaviour of the patch in some manner, they should send an email reply + containing a ``Tested-by`` tag. + + * **``Reported-by``**: when a QEMU community member reports a problem via the + mailing list, or some other informal channel that is not the issue tracker, + it is good practice to credit them by including a ``Reported-by`` tag on + any patch fixing the issue. When the problem is reported via the GitLab + issue tracker, however, it is sufficient to just include a link to the + issue. + + * **``Suggested-by``**: when a reviewer or other 3rd party makes non-trivial + suggestions for how to change a patch, it is good practice to credit them + by including a ``Suggested-by`` tag. + +Subsystem maintainer requirements +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When a subsystem maintainer accepts a patch from a contributor, in addition to +the normal code review points, they are expected to validate the presence of +suitable ``Signed-off-by`` tags. + +At the time they queue the patch in their subsystem tree, the maintainer +**must** also then add their own ``Signed-off-by`` to indicate that they have +done the aforementioned validation. This is in addition to any of their own +``Reviewed-by`` tags the subsystem maintainer may wish to include. + +When the maintainer modifies the patch after pulling into their tree, they +should record their contribution. This is typically done via a note in the +commit message, just prior to the maintainer's ``Signed-off-by``:: + + Signed-off-by: Cory Contributor + [Comment rephrased for clarity] + Signed-off-by: Mary Maintainer + + +Tools for adding ``Signed-off-by`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +There are a variety of ways tools can support adding ``Signed-off-by`` tags +for patches, avoiding the need for contributors to manually type in this +repetitive text each time. + +git commands +^^^^^^^^^^^^ + +When creating, or amending, a commit the ``-s`` flag to ``git commit`` will +append a suitable line matching the configured git author details. + +If preparing patches using the ``git format-patch`` tool, the ``-s`` flag can +be used to append a suitable line in the emails it creates, without modifying +the local commits. Alternatively to modify all the local commits on a branch:: + + git rebase master -x 'git commit --amend --no-edit -s' + +emacs +^^^^^ + +In the file ``$HOME/.emacs.d/abbrev_defs`` add: + +.. code:: elisp + + (define-abbrev-table 'global-abbrev-table + '( + ("8rev" "Reviewed-by: YOUR NAME " nil 1) + ("8ack" "Acked-by: YOUR NAME " nil 1) + ("8test" "Tested-by: YOUR NAME " nil 1) + ("8sob" "Signed-off-by: YOUR NAME " nil 1) + )) + +with this change, if you type (for example) ``8rev`` followed by ```` +or ```` it will expand to the whole phrase. + +vim +^^^ + +In the file ``$HOME/.vimrc`` add:: + + iabbrev 8rev Reviewed-by: YOUR NAME + iabbrev 8ack Acked-by: YOUR NAME + iabbrev 8test Tested-by: YOUR NAME + iabbrev 8sob Signed-off-by: YOUR NAME + +with this change, if you type (for example) ``8rev`` followed by ```` +or ```` it will expand to the whole phrase. + +Re-starting abandoned work +~~~~~~~~~~~~~~~~~~~~~~~~~~ + +For a variety of reasons there are some patches that get submitted to QEMU but +never merged. An unrelated contributor may decide (months or years later) to +continue working from the abandoned patch and re-submit it with extra changes. + +The general principles when picking up abandoned work are: + + * Continue to credit the original author for their work, by maintaining their + original ``Signed-off-by`` + * Indicate where the original patch was obtained from (mailing list, bug + tracker, author's git repo, etc) when sending it for review + * Acknowledge the extra work of the new contributor by including their + ``Signed-off-by`` in the patch in addition to the orignal author's + * Indicate who is responsible for what parts of the patch. This is typically + done via a note in the commit message, just prior to the new contributor's + ``Signed-off-by``:: + + Signed-off-by: Some Person + [Rebased and added support for 'foo'] + Signed-off-by: New Person + +In complicated cases, or if otherwise unsure, ask for advice on the project +mailing list. + +It is also recommended to attempt to contact the original author to let them +know you are interested in taking over their work, in case they still intended +to return to the work, or had any suggestions about the best way to continue. diff --git a/docs/devel/index-process.rst b/docs/devel/index-process.rst index cb7c6640fd..5807752d70 100644 --- a/docs/devel/index-process.rst +++ b/docs/devel/index-process.rst @@ -13,6 +13,7 @@ Notes about how to interact with the community and how and where to submit patch maintainers style submitting-a-patch + code-provenance trivial-patches stable-process submitting-a-pull-request diff --git a/docs/devel/submitting-a-patch.rst b/docs/devel/submitting-a-patch.rst index 65c64078cb..f7917b899f 100644 --- a/docs/devel/submitting-a-patch.rst +++ b/docs/devel/submitting-a-patch.rst @@ -344,28 +344,9 @@ Patch emails must include a ``Signed-off-by:`` line Your patches **must** include a Signed-off-by: line. This is a hard requirement because it's how you say "I'm legally okay to contribute -this and happy for it to go into QEMU". The process is modelled after -the `Linux kernel -`__ -policy. - -If you wrote the patch, make sure your "From:" and "Signed-off-by:" -lines use the same spelling. It's okay if you subscribe or contribute to -the list via more than one address, but using multiple addresses in one -commit just confuses things. If someone else wrote the patch, git will -include a "From:" line in the body of the email (different from your -envelope From:) that will give credit to the correct author; but again, -that author's Signed-off-by: line is mandatory, with the same spelling. - -The name used with "Signed-off-by" does not need to be your legal name, -nor birth name, nor appear on any government ID. It is the identity you -choose to be known by in the community, but should not be anonymous, -nor misrepresent whom you are. - -There are various tooling options for automatically adding these tags -include using ``git commit -s`` or ``git format-patch -s``. For more -information see `SubmittingPatches 1.12 -`__. +this and happy for it to go into QEMU". For full guidance, read the +:ref:`code-provenance` documentation. + .. _include_a_meaningful_cover_letter: From b5d00557722d46cb845c05e546474284870e440b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 16 Jun 2025 11:22:40 +0200 Subject: [PATCH 1647/2760] docs: define policy limiting the inclusion of generated files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Files contributed to QEMU are generally expected to be provided in the preferred format for manipulation. IOW, we generally don't expect to have generated / compiled code included in the tree, rather, we expect to run the code generator / compiler as part of the build process. There are some obvious exceptions to this seen in our existing tree, the biggest one being the inclusion of many binary firmware ROMs. A more niche example is the inclusion of a generated eBPF program. Or the CI dockerfiles which are mostly auto-generated. In these cases, however, the preferred format source code is still required to be included, alongside the generated output. Tools which perform user defined algorithmic transformations on code are not considered to be "code generators". ie, we permit use of coccinelle, spell checkers, and sed/awk/etc to manipulate code. Such use of automated manipulation should still be declared in the commit message. One off generators which create a boilerplate file which the author then fills in, are acceptable if their output has clear copyright and license status. This could be where a contributor writes a throwaway python script to automate creation of some mundane piece of code for example. Signed-off-by: Daniel P. Berrangé Reviewed-by: Alex Bennée Reviewed-by: Stefan Hajnoczi Signed-off-by: Markus Armbruster Signed-off-by: Stefan Hajnoczi --- docs/devel/code-provenance.rst | 55 ++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/docs/devel/code-provenance.rst b/docs/devel/code-provenance.rst index 95b2dd34e2..c25afed98d 100644 --- a/docs/devel/code-provenance.rst +++ b/docs/devel/code-provenance.rst @@ -228,3 +228,58 @@ mailing list. It is also recommended to attempt to contact the original author to let them know you are interested in taking over their work, in case they still intended to return to the work, or had any suggestions about the best way to continue. + +Inclusion of generated files +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Files in patches contributed to QEMU are generally expected to be provided +only in the preferred format for making modifications. The implication of +this is that the output of code generators or compilers is usually not +appropriate to contribute to QEMU. + +For reasons of practicality there are some exceptions to this rule, where +generated code is permitted, provided it is also accompanied by the +corresponding preferred source format. This is done where it is impractical +to expect those building QEMU to run the code generation or compilation +process. A non-exhaustive list of examples is: + + * Images: where an bitmap image is created from a vector file it is common + to include the rendered bitmaps at desired resolution(s), since subtle + changes in the rasterization process / tools may affect quality. The + original vector file is expected to accompany any generated bitmaps. + + * Firmware: QEMU includes pre-compiled binary ROMs for a variety of guest + firmwares. When such binary ROMs are contributed, the corresponding source + must also be provided, either directly, or through a git submodule link. + + * Dockerfiles: the majority of the dockerfiles are automatically generated + from a canonical list of build dependencies maintained in tree, together + with the libvirt-ci git submodule link. The generated dockerfiles are + included in tree because it is desirable to be able to directly build + container images from a clean git checkout. + + * eBPF: QEMU includes some generated eBPF machine code, since the required + eBPF compilation tools are not broadly available on all targetted OS + distributions. The corresponding eBPF C code for the binary is also + provided. This is a time-limited exception until the eBPF toolchain is + sufficiently broadly available in distros. + +In all cases above, the existence of generated files must be acknowledged +and justified in the commit that introduces them. + +Tools which perform changes to existing code with deterministic algorithmic +manipulation, driven by user specified inputs, are not generally considered +to be "generators". + +For instance, using Coccinelle to convert code from one pattern to another +pattern, or fixing documentation typos with a spell checker, or transforming +code using sed / awk / etc, are not considered to be acts of code +generation. Where an automated manipulation is performed on code, however, +this should be declared in the commit message. + +At times contributors may use or create scripts/tools to generate an initial +boilerplate code template which is then filled in to produce the final patch. +The output of such a tool would still be considered the "preferred format", +since it is intended to be a foundation for further human authored changes. +Such tools are acceptable to use, provided there is clearly defined copyright +and licensing for their output. From 3d40db0efc22520fa6c399cf73960dced423b048 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 16 Jun 2025 11:22:41 +0200 Subject: [PATCH 1648/2760] docs: define policy forbidding use of AI code generators MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There has been an explosion of interest in so called AI code generators. Thus far though, this is has not been matched by a broadly accepted legal interpretation of the licensing implications for code generator outputs. While the vendors may claim there is no problem and a free choice of license is possible, they have an inherent conflict of interest in promoting this interpretation. More broadly there is, as yet, no broad consensus on the licensing implications of code generators trained on inputs under a wide variety of licenses The DCO requires contributors to assert they have the right to contribute under the designated project license. Given the lack of consensus on the licensing of AI code generator output, it is not considered credible to assert compliance with the DCO clause (b) or (c) where a patch includes such generated code. This patch thus defines a policy that the QEMU project will currently not accept contributions where use of AI code generators is either known, or suspected. These are early days of AI-assisted software development. The legal questions will be resolved eventually. The tools will mature, and we can expect some to become safely usable in free software projects. The policy we set now must be for today, and be open to revision. It's best to start strict and safe, then relax. Meanwhile requests for exceptions can also be considered on a case by case basis. Signed-off-by: Daniel P. Berrangé Reviewed-by: Kevin Wolf Reviewed-by: Stefan Hajnoczi Reviewed-by: Alex Bennée Signed-off-by: Markus Armbruster Signed-off-by: Stefan Hajnoczi --- docs/devel/code-provenance.rst | 55 +++++++++++++++++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/docs/devel/code-provenance.rst b/docs/devel/code-provenance.rst index c25afed98d..b5aae2e253 100644 --- a/docs/devel/code-provenance.rst +++ b/docs/devel/code-provenance.rst @@ -282,4 +282,57 @@ boilerplate code template which is then filled in to produce the final patch. The output of such a tool would still be considered the "preferred format", since it is intended to be a foundation for further human authored changes. Such tools are acceptable to use, provided there is clearly defined copyright -and licensing for their output. +and licensing for their output. Note in particular the caveats applying to AI +content generators below. + +Use of AI content generators +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +TL;DR: + + **Current QEMU project policy is to DECLINE any contributions which are + believed to include or derive from AI generated content. This includes + ChatGPT, Claude, Copilot, Llama and similar tools.** + +The increasing prevalence of AI-assisted software development results in a +number of difficult legal questions and risks for software projects, including +QEMU. Of particular concern is content generated by `Large Language Models +`__ (LLMs). + +The QEMU community requires that contributors certify their patch submissions +are made in accordance with the rules of the `Developer's Certificate of +Origin (DCO) `. + +To satisfy the DCO, the patch contributor has to fully understand the +copyright and license status of content they are contributing to QEMU. With AI +content generators, the copyright and license status of the output is +ill-defined with no generally accepted, settled legal foundation. + +Where the training material is known, it is common for it to include large +volumes of material under restrictive licensing/copyright terms. Even where +the training material is all known to be under open source licenses, it is +likely to be under a variety of terms, not all of which will be compatible +with QEMU's licensing requirements. + +How contributors could comply with DCO terms (b) or (c) for the output of AI +content generators commonly available today is unclear. The QEMU project is +not willing or able to accept the legal risks of non-compliance. + +The QEMU project thus requires that contributors refrain from using AI content +generators on patches intended to be submitted to the project, and will +decline any contribution if use of AI is either known or suspected. + +This policy does not apply to other uses of AI, such as researching APIs or +algorithms, static analysis, or debugging, provided their output is not to be +included in contributions. + +Examples of tools impacted by this policy includes GitHub's CoPilot, OpenAI's +ChatGPT, Anthropic's Claude, and Meta's Code Llama, and code/content +generation agents which are built on top of such tools. + +This policy may evolve as AI tools mature and the legal situation is +clarifed. In the meanwhile, requests for exceptions to this policy will be +evaluated by the QEMU project on a case by case basis. To be granted an +exception, a contributor will need to demonstrate clarity of the license and +copyright status for the tool's output in relation to its training model and +code, to the satisfaction of the project maintainers. From 3dc9951906af4d47dddd56afd4c27ac8b3ed3c24 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 6 Jun 2025 11:20:33 +0200 Subject: [PATCH 1649/2760] tests/functional/test_pc_cpu_hotplug_props: Set 'pc' machine type explicitly In case the default machine has not been compiled into the QEMU binary, the cpu_hotplug_props test is currently failing. Add a set_machine('pc') here to make sure that the tests are correctly skipped in case the machine is not available. Signed-off-by: Thomas Huth Message-ID: <20250606092033.506736-1-thuth@redhat.com> --- tests/functional/test_pc_cpu_hotplug_props.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/test_pc_cpu_hotplug_props.py b/tests/functional/test_pc_cpu_hotplug_props.py index 9d5a37cb17..2bed8ada02 100755 --- a/tests/functional/test_pc_cpu_hotplug_props.py +++ b/tests/functional/test_pc_cpu_hotplug_props.py @@ -26,6 +26,7 @@ class OmittedCPUProps(QemuSystemTest): def test_no_die_id(self): + self.set_machine('pc') self.vm.add_args('-nodefaults', '-S') self.vm.add_args('-smp', '1,sockets=2,cores=2,threads=2,maxcpus=8') self.vm.add_args('-device', 'qemu64-x86_64-cpu,socket-id=1,core-id=0,thread-id=0') From 920f067347782e4737d0e4a89692b669b888fe0a Mon Sep 17 00:00:00 2001 From: Yoshinori Sato Date: Thu, 12 Jun 2025 22:16:32 +0900 Subject: [PATCH 1650/2760] MAINTAINERS: Yoshinori Sato email address has been updated The email address has been suspended. I have prepared a new email address. Signed-off-by: Yoshinori Sato Message-ID: <20250612131632.137155-1-yoshinori.sato@nifty.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 27f4fe3f25..5b8b314c8c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -352,7 +352,7 @@ F: target/riscv/insn_trans/trans_xventanacondops.c.inc F: disas/riscv-xventana* RENESAS RX CPUs -R: Yoshinori Sato +R: Yoshinori Sato S: Orphan F: target/rx/ @@ -368,7 +368,7 @@ F: tests/tcg/s390x/ L: qemu-s390x@nongnu.org SH4 TCG CPUs -R: Yoshinori Sato +R: Yoshinori Sato S: Orphan F: target/sh4/ F: hw/sh4/ @@ -1696,7 +1696,7 @@ F: docs/system/riscv/microblaze-v-generic.rst RX Machines ----------- rx-gdbsim -R: Yoshinori Sato +R: Yoshinori Sato S: Orphan F: docs/system/target-rx.rst F: hw/rx/rx-gdbsim.c @@ -1705,7 +1705,7 @@ F: tests/functional/test_rx_gdbsim.py SH4 Machines ------------ R2D -R: Yoshinori Sato +R: Yoshinori Sato R: Magnus Damm S: Odd Fixes F: hw/char/sh_serial.c @@ -2756,7 +2756,7 @@ F: tests/qtest/*xive* F: docs/*/*xive* Renesas peripherals -R: Yoshinori Sato +R: Yoshinori Sato R: Magnus Damm S: Odd Fixes F: hw/char/renesas_sci.c @@ -2768,7 +2768,7 @@ F: include/hw/sh4/sh.h F: include/hw/timer/renesas_*.h Renesas RX peripherals -R: Yoshinori Sato +R: Yoshinori Sato S: Orphan F: hw/intc/rx_icu.c F: hw/rx/ From 435c758da161448447dc47e678a97b8df434d94c Mon Sep 17 00:00:00 2001 From: Shalini Chellathurai Saroja Date: Mon, 16 Jun 2025 16:01:05 +0200 Subject: [PATCH 1651/2760] hw/s390x: add SCLP event type CPI Implement the Service-Call Logical Processor (SCLP) event type Control-Program Identification (CPI) in QEMU. This event is used to send CPI identifiers from the guest to the host. The CPI identifiers are: system type, system name, system level and sysplex name. System type: operating system of the guest (e.g. "LINUX "). System name: user configurable name of the guest (e.g. "TESTVM "). System level: distribution and kernel version, if the system type is Linux (e.g. 74872343805430528). Sysplex name: name of the cluster which the guest belongs to (if any) (e.g. "PLEX"). The SCLP event CPI is supported only from "s390-ccw-virtio-10.1" machine and higher. Signed-off-by: Shalini Chellathurai Saroja Reviewed-by: Nina Schoetterl-Glausch Reviewed-by: Thomas Huth Message-ID: <20250616140107.990538-2-shalini@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/s390x/event-facility.c | 2 + hw/s390x/meson.build | 1 + hw/s390x/s390-virtio-ccw.c | 22 ++++++ hw/s390x/sclpcpi.c | 123 +++++++++++++++++++++++++++++ include/hw/s390x/event-facility.h | 12 +++ include/hw/s390x/s390-virtio-ccw.h | 1 + 6 files changed, 161 insertions(+) create mode 100644 hw/s390x/sclpcpi.c diff --git a/hw/s390x/event-facility.c b/hw/s390x/event-facility.c index 7b7bf237fe..fee286ea63 100644 --- a/hw/s390x/event-facility.c +++ b/hw/s390x/event-facility.c @@ -4,6 +4,7 @@ * handles SCLP event types * - Signal Quiesce - system power down * - ASCII Console Data - VT220 read and write + * - Control-Program Identification - Send OS data from guest to host * * Copyright IBM, Corp. 2012 * @@ -40,6 +41,7 @@ struct SCLPEventFacility { SysBusDevice parent_obj; SCLPEventsBus sbus; SCLPEvent quiesce, cpu_hotplug; + SCLPEventCPI cpi; /* guest's receive mask */ union { uint32_t receive_mask_pieces[2]; diff --git a/hw/s390x/meson.build b/hw/s390x/meson.build index 99cbcbd7d6..1bc8583799 100644 --- a/hw/s390x/meson.build +++ b/hw/s390x/meson.build @@ -13,6 +13,7 @@ s390x_ss.add(files( 's390-skeys.c', 's390-stattrib.c', 'sclp.c', + 'sclpcpi.c', 'sclpcpu.c', 'sclpquiesce.c', 'tod.c', diff --git a/hw/s390x/s390-virtio-ccw.c b/hw/s390x/s390-virtio-ccw.c index ce3c13defb..a79bd13275 100644 --- a/hw/s390x/s390-virtio-ccw.c +++ b/hw/s390x/s390-virtio-ccw.c @@ -260,9 +260,21 @@ static void s390_create_sclpconsole(SCLPDevice *sclp, qdev_realize_and_unref(dev, ev_fac_bus, &error_fatal); } +static void s390_create_sclpcpi(SCLPDevice *sclp) +{ + SCLPEventFacility *ef = sclp->event_facility; + BusState *ev_fac_bus = sclp_get_event_facility_bus(ef); + DeviceState *dev; + + dev = qdev_new(TYPE_SCLP_EVENT_CPI); + object_property_add_child(OBJECT(ef), "sclpcpi", OBJECT(dev)); + qdev_realize_and_unref(dev, ev_fac_bus, &error_fatal); +} + static void ccw_init(MachineState *machine) { MachineClass *mc = MACHINE_GET_CLASS(machine); + S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); S390CcwMachineState *ms = S390_CCW_MACHINE(machine); int ret; VirtualCssBus *css_bus; @@ -323,6 +335,12 @@ static void ccw_init(MachineState *machine) /* init the TOD clock */ s390_init_tod(); + + /* init SCLP event Control-Program Identification */ + if (s390mc->use_cpi) { + s390_create_sclpcpi(ms->sclp); + } + } static void s390_cpu_plug(HotplugHandler *hotplug_dev, @@ -783,6 +801,7 @@ static void ccw_machine_class_init(ObjectClass *oc, const void *data) DumpSKeysInterface *dsi = DUMP_SKEYS_INTERFACE_CLASS(oc); s390mc->max_threads = 1; + s390mc->use_cpi = true; mc->reset = s390_machine_reset; mc->block_default_type = IF_VIRTIO; mc->no_cdrom = 1; @@ -908,6 +927,9 @@ static void ccw_machine_10_0_instance_options(MachineState *machine) static void ccw_machine_10_0_class_options(MachineClass *mc) { + S390CcwMachineClass *s390mc = S390_CCW_MACHINE_CLASS(mc); + s390mc->use_cpi = false; + ccw_machine_10_1_class_options(mc); compat_props_add(mc->compat_props, hw_compat_10_0, hw_compat_10_0_len); } diff --git a/hw/s390x/sclpcpi.c b/hw/s390x/sclpcpi.c new file mode 100644 index 0000000000..6e2090b27f --- /dev/null +++ b/hw/s390x/sclpcpi.c @@ -0,0 +1,123 @@ + /* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * SCLP event type 11 - Control-Program Identification (CPI): + * CPI is used to send program identifiers from the guest to the + * Service-Call Logical Processor (SCLP). It is not sent by the SCLP. + * + * Control-program identifiers provide data about the guest operating + * system. The control-program identifiers are: system type, system name, + * system level and sysplex name. + * + * In Linux, all the control-program identifiers are user configurable. The + * system type, system name, and sysplex name use EBCDIC characters from + * this set: capital A-Z, 0-9, $, @, #, and blank. In Linux, the system + * type, system name and sysplex name are arbitrary free-form texts. + * + * In Linux, the 8-byte hexadecimal system-level has the format + * 0x
    , where: + * : is a 4-bit digit, its most significant bit indicates hypervisor use + * : is one digit that represents Linux distributions as follows + * 0: generic Linux + * 1: Red Hat Enterprise Linux + * 2: SUSE Linux Enterprise Server + * 3: Canonical Ubuntu + * 4: Fedora + * 5: openSUSE Leap + * 6: Debian GNU/Linux + * 7: Red Hat Enterprise Linux CoreOS + * : are two digits for a distribution-specific encoding of the major + * version of the distribution + *
    : are two digits for a distribution-specific encoding of the minor + * version of the distribution + * : are four digits for the patch level of the distribution + * : are two digits for the major version of the kernel + * : are two digits for the minor version of the kernel + * : are two digits for the stable version of the kernel + * (e.g. 74872343805430528, when converted to hex is 0x010a000000060b00). On + * machines prior to z16, some of the values are not available to display. + * + * Sysplex refers to a cluster of logical partitions that communicates and + * co-operates with each other. + * + * The CPI feature is supported since 10.1. + * + * Copyright IBM, Corp. 2024 + * + * Authors: + * Shalini Chellathurai Saroja + * + */ + +#include "qemu/osdep.h" +#include "hw/s390x/event-facility.h" + +typedef struct Data { + uint8_t id_format; + uint8_t reserved0; + uint8_t system_type[8]; + uint64_t reserved1; + uint8_t system_name[8]; + uint64_t reserved2; + uint64_t system_level; + uint64_t reserved3; + uint8_t sysplex_name[8]; + uint8_t reserved4[16]; +} QEMU_PACKED Data; + +typedef struct ControlProgramIdMsg { + EventBufferHeader ebh; + Data data; +} QEMU_PACKED ControlProgramIdMsg; + +static bool can_handle_event(uint8_t type) +{ + return type == SCLP_EVENT_CTRL_PGM_ID; +} + +static sccb_mask_t send_mask(void) +{ + return 0; +} + +/* Enable SCLP to accept buffers of event type CPI from the control-program. */ +static sccb_mask_t receive_mask(void) +{ + return SCLP_EVENT_MASK_CTRL_PGM_ID; +} + +static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr) +{ + ControlProgramIdMsg *cpim = container_of(evt_buf_hdr, ControlProgramIdMsg, + ebh); + + cpim->ebh.flags = SCLP_EVENT_BUFFER_ACCEPTED; + return SCLP_RC_NORMAL_COMPLETION; +} + +static void cpi_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + SCLPEventClass *k = SCLP_EVENT_CLASS(klass); + + dc->user_creatable = false; + + k->can_handle_event = can_handle_event; + k->get_send_mask = send_mask; + k->get_receive_mask = receive_mask; + k->write_event_data = write_event_data; +} + +static const TypeInfo sclp_cpi_info = { + .name = TYPE_SCLP_EVENT_CPI, + .parent = TYPE_SCLP_EVENT, + .instance_size = sizeof(SCLPEventCPI), + .class_init = cpi_class_init, +}; + +static void sclp_cpi_register_types(void) +{ + type_register_static(&sclp_cpi_info); +} + +type_init(sclp_cpi_register_types) diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index ff874e792d..e81bc80498 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -25,6 +25,7 @@ #define SCLP_EVENT_MESSAGE 0x02 #define SCLP_EVENT_CONFIG_MGT_DATA 0x04 #define SCLP_EVENT_PMSGCMD 0x09 +#define SCLP_EVENT_CTRL_PGM_ID 0x0b #define SCLP_EVENT_ASCII_CONSOLE_DATA 0x1a #define SCLP_EVENT_SIGNAL_QUIESCE 0x1d @@ -35,6 +36,7 @@ #define SCLP_EVENT_MASK_MSG SCLP_EVMASK(SCLP_EVENT_MESSAGE) #define SCLP_EVENT_MASK_CONFIG_MGT_DATA SCLP_EVMASK(SCLP_EVENT_CONFIG_MGT_DATA) #define SCLP_EVENT_MASK_PMSGCMD SCLP_EVMASK(SCLP_EVENT_PMSGCMD) +#define SCLP_EVENT_MASK_CTRL_PGM_ID SCLP_EVMASK(SCLP_EVENT_CTRL_PGM_ID) #define SCLP_EVENT_MASK_MSG_ASCII SCLP_EVMASK(SCLP_EVENT_ASCII_CONSOLE_DATA) #define SCLP_EVENT_MASK_SIGNAL_QUIESCE SCLP_EVMASK(SCLP_EVENT_SIGNAL_QUIESCE) @@ -191,6 +193,16 @@ struct SCLPEventClass { bool (*can_handle_event)(uint8_t type); }; +#define TYPE_SCLP_EVENT_CPI "sclpcpi" +typedef struct SCLPEventCPIClass SCLPEventCPIClass; +typedef struct SCLPEventCPI SCLPEventCPI; +OBJECT_DECLARE_TYPE(SCLPEventCPI, SCLPEventCPIClass, + SCLP_EVENT_CPI) + +struct SCLPEventCPI { + SCLPEvent event; +}; + #define TYPE_SCLP_EVENT_FACILITY "s390-sclp-event-facility" typedef struct SCLPEventFacility SCLPEventFacility; typedef struct SCLPEventFacilityClass SCLPEventFacilityClass; diff --git a/include/hw/s390x/s390-virtio-ccw.h b/include/hw/s390x/s390-virtio-ccw.h index 321b26df30..526078a4e2 100644 --- a/include/hw/s390x/s390-virtio-ccw.h +++ b/include/hw/s390x/s390-virtio-ccw.h @@ -54,6 +54,7 @@ struct S390CcwMachineClass { /*< public >*/ int max_threads; + bool use_cpi; }; #endif From f345978f24becfab4445fbf1ed2519ee9101d1dd Mon Sep 17 00:00:00 2001 From: Shalini Chellathurai Saroja Date: Mon, 16 Jun 2025 16:01:06 +0200 Subject: [PATCH 1652/2760] hw/s390x: add Control-Program Identification to QOM Add Control-Program Identification (CPI) data to the QEMU Object Model (QOM), along with the timestamp in which the data was received as shown below. virsh # qemu-monitor-command vm --pretty '{"execute":"qom-list", "arguments":{"path":"/machine/sclp/s390-sclp-event-facility/sclpcpi"}}' { "return": [ [...] { "name": "system_level", "type": "uint64" }, { "name": "system_name", "type": "string" }, { "name": "system_type", "type": "string" }, { "name": "timestamp", "type": "uint64" }, { "name": "sysplex_name", "type": "string" } ], "id": "libvirt-14" } Example CPI data: virsh # qemu-monitor-command vm --pretty '{"execute":"qom-get", "arguments":{"path":"/machine/sclp/s390-sclp-event-facility/sclpcpi", "property":"system_type"}}' { "return": "LINUX ", "id": "libvirt-18" } virsh # qemu-monitor-command vm --pretty '{"execute":"qom-get", "arguments":{"path":"/machine/sclp/s390-sclp-event-facility/sclpcpi", "property":"system_name"}}' { "return": "TESTVM ", "id": "libvirt-19" } virsh # qemu-monitor-command vm --pretty '{"execute":"qom-get", "arguments":{"path":"/machine/sclp/s390-sclp-event-facility/sclpcpi", "property":"sysplex_name"}}' { "return": "PLEX ", "id": "libvirt-20" } virsh # qemu-monitor-command vm --pretty '{"execute":"qom-get", "arguments":{"path":"/machine/sclp/s390-sclp-event-facility/sclpcpi", "property":"system_level"}}' { "return": 74872343805430528, "id": "libvirt-21" } virsh # qemu-monitor-command vm --pretty '{"execute":"qom-get", "arguments":{"path":"/machine/sclp/s390-sclp-event-facility/sclpcpi", "property":"timestamp"}}' { "return": 1748866753433923000, "id": "libvirt-22" } Signed-off-by: Shalini Chellathurai Saroja Reviewed-by: Nina Schoetterl-Glausch Message-ID: <20250616140107.990538-3-shalini@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/s390x/sclpcpi.c | 74 +++++++++++++++++++++++++++++++ include/hw/s390x/event-facility.h | 5 +++ 2 files changed, 79 insertions(+) diff --git a/hw/s390x/sclpcpi.c b/hw/s390x/sclpcpi.c index 6e2090b27f..440a5ff1eb 100644 --- a/hw/s390x/sclpcpi.c +++ b/hw/s390x/sclpcpi.c @@ -50,7 +50,10 @@ */ #include "qemu/osdep.h" +#include "qemu/timer.h" #include "hw/s390x/event-facility.h" +#include "hw/s390x/ebcdic.h" +#include "qapi/qapi-visit-machine.h" typedef struct Data { uint8_t id_format; @@ -90,11 +93,58 @@ static int write_event_data(SCLPEvent *event, EventBufferHeader *evt_buf_hdr) { ControlProgramIdMsg *cpim = container_of(evt_buf_hdr, ControlProgramIdMsg, ebh); + SCLPEventCPI *e = SCLP_EVENT_CPI(event); + + ascii_put(e->system_type, (char *)cpim->data.system_type, + sizeof(cpim->data.system_type)); + ascii_put(e->system_name, (char *)cpim->data.system_name, + sizeof(cpim->data.system_name)); + ascii_put(e->sysplex_name, (char *)cpim->data.sysplex_name, + sizeof(cpim->data.sysplex_name)); + e->system_level = ldq_be_p(&cpim->data.system_level); + e->timestamp = qemu_clock_get_ns(QEMU_CLOCK_HOST); cpim->ebh.flags = SCLP_EVENT_BUFFER_ACCEPTED; return SCLP_RC_NORMAL_COMPLETION; } +static char *get_system_type(Object *obj, Error **errp) +{ + SCLPEventCPI *e = SCLP_EVENT_CPI(obj); + + return g_strndup((char *) e->system_type, sizeof(e->system_type)); +} + +static char *get_system_name(Object *obj, Error **errp) +{ + SCLPEventCPI *e = SCLP_EVENT_CPI(obj); + + return g_strndup((char *) e->system_name, sizeof(e->system_name)); +} + +static char *get_sysplex_name(Object *obj, Error **errp) +{ + SCLPEventCPI *e = SCLP_EVENT_CPI(obj); + + return g_strndup((char *) e->sysplex_name, sizeof(e->sysplex_name)); +} + +static void get_system_level(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + SCLPEventCPI *e = SCLP_EVENT_CPI(obj); + + visit_type_uint64(v, name, &e->system_level, errp); +} + +static void get_timestamp(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + SCLPEventCPI *e = SCLP_EVENT_CPI(obj); + + visit_type_uint64(v, name, &e->timestamp, errp); +} + static void cpi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); @@ -106,6 +156,30 @@ static void cpi_class_init(ObjectClass *klass, const void *data) k->get_send_mask = send_mask; k->get_receive_mask = receive_mask; k->write_event_data = write_event_data; + + object_class_property_add_str(klass, "system_type", get_system_type, NULL); + object_class_property_set_description(klass, "system_type", + "operating system e.g. \"LINUX \""); + + object_class_property_add_str(klass, "system_name", get_system_name, NULL); + object_class_property_set_description(klass, "system_name", + "user configurable name of the VM e.g. \"TESTVM \""); + + object_class_property_add_str(klass, "sysplex_name", get_sysplex_name, + NULL); + object_class_property_set_description(klass, "sysplex_name", + "name of the cluster which the VM belongs to, if any" + " e.g. \"PLEX \""); + + object_class_property_add(klass, "system_level", "uint64", get_system_level, + NULL, NULL, NULL); + object_class_property_set_description(klass, "system_level", + "distribution and kernel version in Linux e.g. 74872343805430528"); + + object_class_property_add(klass, "timestamp", "uint64", get_timestamp, + NULL, NULL, NULL); + object_class_property_set_description(klass, "timestamp", + "latest update of CPI data in nanoseconds since the UNIX EPOCH"); } static const TypeInfo sclp_cpi_info = { diff --git a/include/hw/s390x/event-facility.h b/include/hw/s390x/event-facility.h index e81bc80498..eac7a51100 100644 --- a/include/hw/s390x/event-facility.h +++ b/include/hw/s390x/event-facility.h @@ -201,6 +201,11 @@ OBJECT_DECLARE_TYPE(SCLPEventCPI, SCLPEventCPIClass, struct SCLPEventCPI { SCLPEvent event; + uint8_t system_type[8]; + uint8_t system_name[8]; + uint64_t system_level; + uint8_t sysplex_name[8]; + uint64_t timestamp; }; #define TYPE_SCLP_EVENT_FACILITY "s390-sclp-event-facility" From c61927ba8e4375fcb80aef9d9bc9e32b8130af01 Mon Sep 17 00:00:00 2001 From: Shalini Chellathurai Saroja Date: Mon, 16 Jun 2025 16:01:07 +0200 Subject: [PATCH 1653/2760] hw/s390x: support migration of CPI data Register Control-Program Identification data with the live migration infrastructure. Signed-off-by: Shalini Chellathurai Saroja Reviewed-by: Nina Schoetterl-Glausch Reviewed-by: Thomas Huth Message-ID: <20250616140107.990538-4-shalini@linux.ibm.com> Signed-off-by: Thomas Huth --- hw/s390x/sclpcpi.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/hw/s390x/sclpcpi.c b/hw/s390x/sclpcpi.c index 440a5ff1eb..7aa039d510 100644 --- a/hw/s390x/sclpcpi.c +++ b/hw/s390x/sclpcpi.c @@ -54,6 +54,7 @@ #include "hw/s390x/event-facility.h" #include "hw/s390x/ebcdic.h" #include "qapi/qapi-visit-machine.h" +#include "migration/vmstate.h" typedef struct Data { uint8_t id_format; @@ -145,12 +146,26 @@ static void get_timestamp(Object *obj, Visitor *v, const char *name, visit_type_uint64(v, name, &e->timestamp, errp); } +static const VMStateDescription vmstate_sclpcpi = { + .name = "s390_control_program_id", + .version_id = 0, + .fields = (const VMStateField[]) { + VMSTATE_UINT8_ARRAY(system_type, SCLPEventCPI, 8), + VMSTATE_UINT8_ARRAY(system_name, SCLPEventCPI, 8), + VMSTATE_UINT64(system_level, SCLPEventCPI), + VMSTATE_UINT8_ARRAY(sysplex_name, SCLPEventCPI, 8), + VMSTATE_UINT64(timestamp, SCLPEventCPI), + VMSTATE_END_OF_LIST() + } +}; + static void cpi_class_init(ObjectClass *klass, const void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SCLPEventClass *k = SCLP_EVENT_CLASS(klass); dc->user_creatable = false; + dc->vmsd = &vmstate_sclpcpi; k->can_handle_event = can_handle_event; k->get_send_mask = send_mask; From a96011fefc2d9073569d51f481b656b65f552df7 Mon Sep 17 00:00:00 2001 From: Matthew Rosato Date: Mon, 23 Jun 2025 12:00:30 -0400 Subject: [PATCH 1654/2760] MAINTAINERS: add reviewers for some s390 areas To improve review coverage, assign additional people as reviewers for multiple s390 sections. Signed-off-by: Matthew Rosato Acked-by: Jason J. Herne Acked-by: Collin Walling Acked-by: Jared Rossi Acked-by: Halil Pasic Acked-by: Farhan Ali Acked-by: Zhuoying Cai Acked-by: Eric Farman Message-ID: <20250623160030.98281-1-mjrosato@linux.ibm.com> Signed-off-by: Thomas Huth --- MAINTAINERS | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 5b8b314c8c..ebb8af7d5b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -459,6 +459,8 @@ F: target/ppc/kvm.c S390 KVM CPUs M: Halil Pasic M: Christian Borntraeger +R: Eric Farman +R: Matthew Rosato S: Supported F: target/s390x/kvm/ F: target/s390x/machine.c @@ -1771,6 +1773,7 @@ S390 Virtio-ccw M: Halil Pasic M: Christian Borntraeger M: Eric Farman +R: Matthew Rosato S: Supported F: hw/s390x/ F: include/hw/s390x/ @@ -1782,6 +1785,8 @@ L: qemu-s390x@nongnu.org S390-ccw boot M: Christian Borntraeger M: Thomas Huth +R: Jared Rossi +R: Zhuoying Cai S: Supported F: hw/s390x/ipl.* F: pc-bios/s390-ccw/ @@ -1802,6 +1807,7 @@ S390 channel subsystem M: Halil Pasic M: Christian Borntraeger M: Eric Farman +R: Farhan Ali S: Supported F: hw/s390x/ccw-device.[ch] F: hw/s390x/css.c @@ -1822,6 +1828,7 @@ L: qemu-s390x@nongnu.org S390 SCLP-backed devices M: Halil Pasic M: Christian Borntraeger +R: Jason Herne S: Supported F: include/hw/s390x/event-facility.h F: include/hw/s390x/sclp.h @@ -2809,6 +2816,7 @@ F: include/hw/timer/mips_gictimer.h S390 3270 device M: Halil Pasic M: Christian Borntraeger +R: Collin Walling S: Odd fixes F: include/hw/s390x/3270-ccw.h F: hw/char/terminal3270.c @@ -2818,6 +2826,7 @@ L: qemu-s390x@nongnu.org S390 diag 288 watchdog M: Halil Pasic M: Christian Borntraeger +R: Collin Walling S: Supported F: hw/watchdog/wdt_diag288.c F: include/hw/watchdog/wdt_diag288.h @@ -2826,6 +2835,7 @@ L: qemu-s390x@nongnu.org S390 storage key device M: Halil Pasic M: Christian Borntraeger +R: Jason Herne S: Supported F: hw/s390x/storage-keys.h F: hw/s390x/s390-skeys*.c @@ -2834,6 +2844,7 @@ L: qemu-s390x@nongnu.org S390 storage attribute device M: Halil Pasic M: Christian Borntraeger +R: Jason Herne S: Supported F: hw/s390x/storage-attributes.h F: hw/s390x/s390-stattrib*.c @@ -2843,6 +2854,7 @@ S390 floating interrupt controller M: Halil Pasic M: Christian Borntraeger M: David Hildenbrand +R: Jason Herne S: Supported F: hw/intc/s390_flic*.c F: include/hw/s390x/s390_flic.h From 78e3781541209b3dcd6f4bb66adf3a3e504b88a4 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 25 Jun 2025 10:27:51 +0200 Subject: [PATCH 1655/2760] hw/s390x/ccw-device: Fix memory leak in loadparm setter Commit bdf12f2a fixed the setter for the "loadparm" machine property, which gets a string from a visitor, passes it to s390_ipl_fmt_loadparm() and then forgot to free it. It left another instance of the same problem unfixed in the "loadparm" device property. Fix it. Signed-off-by: Kevin Wolf Message-ID: <20250625082751.24896-1-kwolf@redhat.com> Reviewed-by: Eric Farman Reviewed-by: Halil Pasic Tested-by: Thomas Huth Signed-off-by: Thomas Huth --- hw/s390x/ccw-device.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/s390x/ccw-device.c b/hw/s390x/ccw-device.c index 19c2238f76..8be1813b9e 100644 --- a/hw/s390x/ccw-device.c +++ b/hw/s390x/ccw-device.c @@ -57,7 +57,7 @@ static void ccw_device_set_loadparm(Object *obj, Visitor *v, Error **errp) { CcwDevice *dev = CCW_DEVICE(obj); - char *val; + g_autofree char *val = NULL; int index; index = object_property_get_int(obj, "bootindex", NULL); From 6e7cb5ff2071827ffac118bb3370b81397fe08d8 Mon Sep 17 00:00:00 2001 From: Haseung Bong Date: Sun, 15 Jun 2025 09:32:49 +0900 Subject: [PATCH 1656/2760] tests/vm: update openbsd image to 7.7 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update tests/vm/openbsd to release 7.7 Signed-off-by: Haseung Bong Reviewed-by: Daniel P. Berrangé Message-ID: <20250615003249.310160-1-hasueng@gmail.com> Signed-off-by: Thomas Huth --- tests/vm/openbsd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/vm/openbsd b/tests/vm/openbsd index 5e4f76f398..2ea86a01ba 100755 --- a/tests/vm/openbsd +++ b/tests/vm/openbsd @@ -22,8 +22,8 @@ class OpenBSDVM(basevm.BaseVM): name = "openbsd" arch = "x86_64" - link = "https://cdn.openbsd.org/pub/OpenBSD/7.6/amd64/install76.iso" - csum = "60cba8cb391b50bba8fa10fc768bd0529636f5345d82133c93e22c798d8e5269" + link = "https://cdn.openbsd.org/pub/OpenBSD/7.7/amd64/install77.iso" + csum = "da0106e39463f015524dca806f407c37a9bdd17e6dfffe533b06a2dd2edd8a27" size = "20G" pkgs = [ # tools From 4db50be038a839d8332790db4d2d63ac247efad3 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 12:33:10 -0400 Subject: [PATCH 1657/2760] COPYING: replace FSF postal address with licenses URL The license text in COPYING (GPLv2), COPYING.LIB (LGPLv2.1), and the linux-headers/LICENSES/preferred/GPL-2.0 file are referenced to the obsolete FSF postal address. Replace it with the canonical GNU licenses URL recommended by the FSF: https://www.gnu.org/licenses/ Signed-off-by: Sean Wei Message-ID: <20250613.qemu.patch.01@sean.taipei> [thuth: dropped the changes to the linux-headers folder] Signed-off-by: Thomas Huth --- COPYING | 5 ++--- COPYING.LIB | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/COPYING b/COPYING index 00ccfbb628..8095135d50 100644 --- a/COPYING +++ b/COPYING @@ -2,7 +2,7 @@ Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -304,8 +304,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License along - with this program; if not, write to the Free Software Foundation, Inc., - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + with this program; if not, see . Also add information on how to contact you by electronic and paper mail. diff --git a/COPYING.LIB b/COPYING.LIB index 4362b49151..99f47575b5 100644 --- a/COPYING.LIB +++ b/COPYING.LIB @@ -2,7 +2,7 @@ Version 2.1, February 1999 Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -484,8 +484,7 @@ convey the exclusion of warranty; and each file should have at least the Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public - License along with this library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + License along with this library; if not, see . Also add information on how to contact you by electronic and paper mail. From cf81c1c6b9761d319b37939b8608c2b7dc75ce13 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 12:39:50 -0400 Subject: [PATCH 1658/2760] libdecnumber: replace FSF postal address with licenses URL Some of the GPLv2 boiler-plate still contained the obsolete "51 Franklin Street" postal address. Replace it with the canonical GNU licenses URL recommended by the FSF: https://www.gnu.org/licenses/ Signed-off-by: Sean Wei Reviewed-by: Thomas Huth Message-ID: <20250613.qemu.patch.03@sean.taipei> Signed-off-by: Thomas Huth --- libdecnumber/decContext.c | 5 ++--- libdecnumber/decNumber.c | 5 ++--- libdecnumber/dpd/decimal128.c | 5 ++--- libdecnumber/dpd/decimal32.c | 5 ++--- libdecnumber/dpd/decimal64.c | 5 ++--- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/libdecnumber/decContext.c b/libdecnumber/decContext.c index 1956edf0a7..d99b08026c 100644 --- a/libdecnumber/decContext.c +++ b/libdecnumber/decContext.c @@ -24,9 +24,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ /* ------------------------------------------------------------------ */ /* Decimal Context module */ diff --git a/libdecnumber/decNumber.c b/libdecnumber/decNumber.c index 31282adafd..4b57d8a6fe 100644 --- a/libdecnumber/decNumber.c +++ b/libdecnumber/decNumber.c @@ -24,9 +24,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ /* ------------------------------------------------------------------ */ /* Decimal Number arithmetic module */ diff --git a/libdecnumber/dpd/decimal128.c b/libdecnumber/dpd/decimal128.c index ca4764e547..1064fb25e0 100644 --- a/libdecnumber/dpd/decimal128.c +++ b/libdecnumber/dpd/decimal128.c @@ -24,9 +24,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ /* ------------------------------------------------------------------ */ /* Decimal 128-bit format module */ diff --git a/libdecnumber/dpd/decimal32.c b/libdecnumber/dpd/decimal32.c index 53f29789d7..34ff0fe959 100644 --- a/libdecnumber/dpd/decimal32.c +++ b/libdecnumber/dpd/decimal32.c @@ -24,9 +24,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ /* ------------------------------------------------------------------ */ /* Decimal 32-bit format module */ diff --git a/libdecnumber/dpd/decimal64.c b/libdecnumber/dpd/decimal64.c index 290dbe8177..11e0674fa7 100644 --- a/libdecnumber/dpd/decimal64.c +++ b/libdecnumber/dpd/decimal64.c @@ -24,9 +24,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ /* ------------------------------------------------------------------ */ /* Decimal 64-bit format module */ From 36d7484b0cbd6a6c9e6945d90e4298bec0ff661b Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 12:43:40 -0400 Subject: [PATCH 1659/2760] include/libdecnumber: replace FSF postal address with licenses URL Some of the GPLv2 boiler-plate still contained the obsolete "51 Franklin Street" postal address. Replace it with the canonical GNU licenses URL recommended by the FSF: https://www.gnu.org/licenses/ Signed-off-by: Sean Wei Reviewed-by: Thomas Huth Message-ID: <20250613.qemu.patch.04@sean.taipei> Signed-off-by: Thomas Huth --- include/libdecnumber/dconfig.h | 5 ++--- include/libdecnumber/decContext.h | 5 ++--- include/libdecnumber/decDPD.h | 5 ++--- include/libdecnumber/decNumber.h | 5 ++--- include/libdecnumber/decNumberLocal.h | 5 ++--- include/libdecnumber/dpd/decimal128.h | 5 ++--- include/libdecnumber/dpd/decimal128Local.h | 5 ++--- include/libdecnumber/dpd/decimal32.h | 5 ++--- include/libdecnumber/dpd/decimal64.h | 5 ++--- 9 files changed, 18 insertions(+), 27 deletions(-) diff --git a/include/libdecnumber/dconfig.h b/include/libdecnumber/dconfig.h index 2bc0ba7f14..e67ecc1b5f 100644 --- a/include/libdecnumber/dconfig.h +++ b/include/libdecnumber/dconfig.h @@ -23,9 +23,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ #if HOST_BIG_ENDIAN diff --git a/include/libdecnumber/decContext.h b/include/libdecnumber/decContext.h index cea6e4279e..5bb64e1332 100644 --- a/include/libdecnumber/decContext.h +++ b/include/libdecnumber/decContext.h @@ -24,9 +24,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ /* ------------------------------------------------------------------ */ /* Decimal Context module header */ diff --git a/include/libdecnumber/decDPD.h b/include/libdecnumber/decDPD.h index 26a21ec8ed..8eb455277b 100644 --- a/include/libdecnumber/decDPD.h +++ b/include/libdecnumber/decDPD.h @@ -24,9 +24,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ /* ------------------------------------------------------------------------ */ /* Binary Coded Decimal and Densely Packed Decimal conversion lookup tables */ diff --git a/include/libdecnumber/decNumber.h b/include/libdecnumber/decNumber.h index 41bc2a0d36..bf37af83e6 100644 --- a/include/libdecnumber/decNumber.h +++ b/include/libdecnumber/decNumber.h @@ -24,9 +24,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ /* ------------------------------------------------------------------ */ /* Decimal Number arithmetic module header */ diff --git a/include/libdecnumber/decNumberLocal.h b/include/libdecnumber/decNumberLocal.h index 6198ca8593..0959f6606b 100644 --- a/include/libdecnumber/decNumberLocal.h +++ b/include/libdecnumber/decNumberLocal.h @@ -24,9 +24,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ /* ------------------------------------------------------------------ */ /* decNumber package local type, tuning, and macro definitions */ diff --git a/include/libdecnumber/dpd/decimal128.h b/include/libdecnumber/dpd/decimal128.h index aff261e556..c57180baf8 100644 --- a/include/libdecnumber/dpd/decimal128.h +++ b/include/libdecnumber/dpd/decimal128.h @@ -24,9 +24,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ /* ------------------------------------------------------------------ */ /* Decimal 128-bit format module header */ diff --git a/include/libdecnumber/dpd/decimal128Local.h b/include/libdecnumber/dpd/decimal128Local.h index 9765427719..2948ab2534 100644 --- a/include/libdecnumber/dpd/decimal128Local.h +++ b/include/libdecnumber/dpd/decimal128Local.h @@ -23,9 +23,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ #if !defined(DECIMAL128LOCAL) diff --git a/include/libdecnumber/dpd/decimal32.h b/include/libdecnumber/dpd/decimal32.h index 6cb9e43620..9a17933497 100644 --- a/include/libdecnumber/dpd/decimal32.h +++ b/include/libdecnumber/dpd/decimal32.h @@ -24,9 +24,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ /* ------------------------------------------------------------------ */ /* Decimal 32-bit format module header */ diff --git a/include/libdecnumber/dpd/decimal64.h b/include/libdecnumber/dpd/decimal64.h index f29e57064d..5c3d0bb43c 100644 --- a/include/libdecnumber/dpd/decimal64.h +++ b/include/libdecnumber/dpd/decimal64.h @@ -24,9 +24,8 @@ for more details. You should have received a copy of the GNU General Public License - along with GCC; see the file COPYING. If not, write to the Free - Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with GCC; see the file COPYING. If not, see + . */ /* ------------------------------------------------------------------ */ /* Decimal 64-bit format module header */ From e5308bc52d25da207fbb1cd7b557201b6612fd61 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 12:44:10 -0400 Subject: [PATCH 1660/2760] include/hw: replace FSF postal address with licenses URL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the GPLv2 boiler-plate still contained the obsolete "51 Franklin Street" postal address. Replace it with the canonical GNU licenses URL recommended by the FSF: https://www.gnu.org/licenses/ Signed-off-by: Sean Wei Reviewed-by: Cédric Le Goater Message-ID: <20250613.qemu.patch.05@sean.taipei> Signed-off-by: Thomas Huth --- include/hw/i2c/aspeed_i2c.h | 3 +-- include/hw/pci/pci_bridge.h | 4 ++-- include/hw/timer/aspeed_timer.h | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/include/hw/i2c/aspeed_i2c.h b/include/hw/i2c/aspeed_i2c.h index 2c4c81bd20..2daacc10ce 100644 --- a/include/hw/i2c/aspeed_i2c.h +++ b/include/hw/i2c/aspeed_i2c.h @@ -14,8 +14,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * with this program; if not, see . */ #ifndef ASPEED_I2C_H diff --git a/include/hw/pci/pci_bridge.h b/include/hw/pci/pci_bridge.h index b0f5204d80..8cdacbc4e1 100644 --- a/include/hw/pci/pci_bridge.h +++ b/include/hw/pci/pci_bridge.h @@ -14,8 +14,8 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * along with this program; if not, see + * . * * split out pci bus specific stuff from pci.[hc] to pci_bridge.[hc] * Copyright (c) 2009 Isaku Yamahata diff --git a/include/hw/timer/aspeed_timer.h b/include/hw/timer/aspeed_timer.h index 767cae4b05..a850625a05 100644 --- a/include/hw/timer/aspeed_timer.h +++ b/include/hw/timer/aspeed_timer.h @@ -16,8 +16,7 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * with this program; if not, see . */ #ifndef ASPEED_TIMER_H #define ASPEED_TIMER_H From c61b807e625846871e3d142a9f101d58f7ee2522 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 12:46:15 -0400 Subject: [PATCH 1661/2760] include/qemu: replace FSF postal address with licenses URL The LGPLv2.1 boiler-plate in rcu.h and rcu_queue.h still contained the obsolete "51 Franklin Street" postal address. Replace it with the canonical GNU licenses URL recommended by the FSF: https://www.gnu.org/licenses/ Signed-off-by: Sean Wei Reviewed-by: Thomas Huth Message-ID: <20250613.qemu.patch.06@sean.taipei> Signed-off-by: Thomas Huth --- include/qemu/rcu.h | 4 ++-- include/qemu/rcu_queue.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/qemu/rcu.h b/include/qemu/rcu.h index fea058aa9f..020dbe4d8b 100644 --- a/include/qemu/rcu.h +++ b/include/qemu/rcu.h @@ -17,8 +17,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * License along with this library; if not, see + * . * * IBM's contributions to this file may be relicensed under LGPLv2 or later. */ diff --git a/include/qemu/rcu_queue.h b/include/qemu/rcu_queue.h index 4e6298d473..bfd5900fda 100644 --- a/include/qemu/rcu_queue.h +++ b/include/qemu/rcu_queue.h @@ -17,8 +17,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * License along with this library; if not, see + * . * * Copyright (c) 2013 Mike D. Day, IBM Corporation. * From 145c3e54be7fa5e4f0a2ac527db28e27940dab91 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 12:46:50 -0400 Subject: [PATCH 1662/2760] util/rcu.c: replace FSF postal address with licenses URL The LGPLv2.1 boiler-plate in util/rcu.c still contained the obsolete "51 Franklin Street" postal address. Replace it with the canonical GNU licenses URL recommended by the FSF: https://www.gnu.org/licenses/ Signed-off-by: Sean Wei Reviewed-by: Thomas Huth Message-ID: <20250613.qemu.patch.07@sean.taipei> Signed-off-by: Thomas Huth --- util/rcu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/util/rcu.c b/util/rcu.c index fa32c942e4..b703c86f15 100644 --- a/util/rcu.c +++ b/util/rcu.c @@ -20,8 +20,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * License along with this library; if not, see + * . * * IBM's contributions to this file may be relicensed under LGPLv2 or later. */ From 49b91833cc671c30fd2a074a92c50c858d6ec055 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 12:50:20 -0400 Subject: [PATCH 1663/2760] hw: replace FSF postal address with licenses URL The GPLv2 boiler-plate in vmxnet3.h and vmw_pvscsi.h still contained the obsolete "51 Franklin Street" postal address. Replace it with the canonical GNU licenses URL recommended by the FSF: https://www.gnu.org/licenses/ Signed-off-by: Sean Wei Reviewed-by: Thomas Huth Message-ID: <20250613.qemu.patch.08@sean.taipei> Signed-off-by: Thomas Huth --- hw/net/vmxnet3.h | 4 ++-- hw/scsi/vmw_pvscsi.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/net/vmxnet3.h b/hw/net/vmxnet3.h index f9283f9e7b..dbc69d5fb6 100644 --- a/hw/net/vmxnet3.h +++ b/hw/net/vmxnet3.h @@ -63,8 +63,8 @@ * details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * along with this program; if not, see + * . * * The full GNU General Public License is included in this distribution in * the file called "COPYING". diff --git a/hw/scsi/vmw_pvscsi.h b/hw/scsi/vmw_pvscsi.h index 17fcf66273..a3ae517e19 100644 --- a/hw/scsi/vmw_pvscsi.h +++ b/hw/scsi/vmw_pvscsi.h @@ -14,8 +14,8 @@ * details. * * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * along with this program; if not, see + * . * * Maintained by: Arvind Kumar * From 19f5891f2ee6bdebc4c522ea5744f4d4ea7365f1 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 12:51:45 -0400 Subject: [PATCH 1664/2760] scripts: replace FSF postal address with licenses URL The GPLv2 boiler-plate in scripts/device-crash-test still contained the obsolete "51 Franklin Street" postal address. Replace it with the canonical GNU licenses URL recommended by the FSF: https://www.gnu.org/licenses/ Signed-off-by: Sean Wei Reviewed-by: Thomas Huth Message-ID: <20250613.qemu.patch.09@sean.taipei> Signed-off-by: Thomas Huth --- scripts/device-crash-test | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/scripts/device-crash-test b/scripts/device-crash-test index da8b56edd9..1ecb9663ae 100755 --- a/scripts/device-crash-test +++ b/scripts/device-crash-test @@ -16,8 +16,7 @@ # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along -# with this program; if not, write to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# with this program; if not, see . """ Run QEMU with all combinations of -machine and -device types, From e656492766ce947cf0699e0e3d602a2587ac38d3 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 12:53:00 -0400 Subject: [PATCH 1665/2760] contrib: replace FSF postal address with licenses URL The LGPLv2.1 boiler-plate in pdb.c file still contained the obsolete "51 Franklin Street" postal address. Replace it with the canonical GNU licenses URL recommended by the FSF: https://www.gnu.org/licenses/ Signed-off-by: Sean Wei Reviewed-by: Akihiko Odaki Message-ID: <20250613.qemu.patch.10@sean.taipei> Signed-off-by: Thomas Huth --- contrib/elf2dmp/pdb.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/elf2dmp/pdb.c b/contrib/elf2dmp/pdb.c index 492aca4434..47c5126fb8 100644 --- a/contrib/elf2dmp/pdb.c +++ b/contrib/elf2dmp/pdb.c @@ -14,8 +14,8 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + * License along with this library; if not, see + * . */ #include "qemu/osdep.h" From e38bdca0cab79a652ad4d619044857a2fe2a447b Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 12:53:32 -0400 Subject: [PATCH 1666/2760] target/xtensa: replace FSF postal address with licenses URL Some of the GPLv2 boiler-plate still contained the obsolete "51 Franklin Street" postal address. Replace it with the canonical GNU licenses URL recommended by the FSF: https://www.gnu.org/licenses/ Signed-off-by: Sean Wei Acked-by: Max Filippov Message-ID: <20250613.qemu.patch.11@sean.taipei> Signed-off-by: Thomas Huth --- target/xtensa/core-dc232b/gdb-config.c.inc | 5 ++--- target/xtensa/core-dc232b/xtensa-modules.c.inc | 5 ++--- target/xtensa/core-fsf/xtensa-modules.c.inc | 5 ++--- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/target/xtensa/core-dc232b/gdb-config.c.inc b/target/xtensa/core-dc232b/gdb-config.c.inc index d87168628b..8c88caef59 100644 --- a/target/xtensa/core-dc232b/gdb-config.c.inc +++ b/target/xtensa/core-dc232b/gdb-config.c.inc @@ -15,9 +15,8 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, USA. */ + along with this program; if not, see + . */ XTREG(0, 0, 32, 4, 4, 0x0020, 0x0006, -2, 9, 0x0100, pc, 0, 0, 0, 0, 0, 0) diff --git a/target/xtensa/core-dc232b/xtensa-modules.c.inc b/target/xtensa/core-dc232b/xtensa-modules.c.inc index 164df3b1a4..bb9ebd24b8 100644 --- a/target/xtensa/core-dc232b/xtensa-modules.c.inc +++ b/target/xtensa/core-dc232b/xtensa-modules.c.inc @@ -14,9 +14,8 @@ General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program; if not, see + . */ #include "qemu/osdep.h" #include "xtensa-isa.h" diff --git a/target/xtensa/core-fsf/xtensa-modules.c.inc b/target/xtensa/core-fsf/xtensa-modules.c.inc index c32683ff77..531f5e2b7e 100644 --- a/target/xtensa/core-fsf/xtensa-modules.c.inc +++ b/target/xtensa/core-fsf/xtensa-modules.c.inc @@ -14,9 +14,8 @@ General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program; if not, see + . */ #include "qemu/osdep.h" #include "xtensa-isa.h" From fd68168e0af14546117705176ace84d2c6c52416 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 12:54:20 -0400 Subject: [PATCH 1667/2760] target/i386/emulate: replace FSF postal address with licenses URL Some of the LGPLv2.1 boiler-plate still contained the obsolete "51 Franklin Street" postal address. Replace it with the canonical GNU licenses URL recommended by the FSF: https://www.gnu.org/licenses/ Signed-off-by: Sean Wei Reviewed-by: Wei Liu Message-ID: <20250613.qemu.patch.12@sean.taipei> Signed-off-by: Thomas Huth --- target/i386/emulate/x86_emu.c | 4 ++-- target/i386/emulate/x86_flags.c | 4 ++-- target/i386/emulate/x86_flags.h | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/i386/emulate/x86_emu.c b/target/i386/emulate/x86_emu.c index 4890e0a4e5..db7a7f7437 100644 --- a/target/i386/emulate/x86_emu.c +++ b/target/i386/emulate/x86_emu.c @@ -31,8 +31,8 @@ // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA +// License along with this library; if not, see +// . ///////////////////////////////////////////////////////////////////////// #include "qemu/osdep.h" diff --git a/target/i386/emulate/x86_flags.c b/target/i386/emulate/x86_flags.c index cc138c7749..6592193b5e 100644 --- a/target/i386/emulate/x86_flags.c +++ b/target/i386/emulate/x86_flags.c @@ -14,8 +14,8 @@ // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA +// License along with this library; if not, see +// . ///////////////////////////////////////////////////////////////////////// /* * flags functions diff --git a/target/i386/emulate/x86_flags.h b/target/i386/emulate/x86_flags.h index 28b008e577..a395c837a0 100644 --- a/target/i386/emulate/x86_flags.h +++ b/target/i386/emulate/x86_flags.h @@ -14,8 +14,8 @@ // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA B 02110-1301 USA +// License along with this library; if not, see +// . ///////////////////////////////////////////////////////////////////////// /* * x86 eflags functions From 639ff87a1a823715f16edec34caacd8ef824fcfd Mon Sep 17 00:00:00 2001 From: Rorie Reyes Date: Wed, 11 Jun 2025 17:12:52 -0400 Subject: [PATCH 1668/2760] hw/vfio/ap: attribute constructor for cfg_chg_events_lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created an attribute constructor for cfg_chg_events_lock for locking mechanism when storing event information for an AP configuration change event Fixes: fd03360215 ("Storing event information for an AP configuration change event") Signed-off-by: Rorie Reyes Reviewed-by: Thomas Huth Link: https://lore.kernel.org/qemu-devel/20250611211252.82107-1-rreyes@linux.ibm.com Signed-off-by: Cédric Le Goater --- hw/vfio/ap.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 874e0d1eaf..1df4438149 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -52,6 +52,11 @@ static QTAILQ_HEAD(, APConfigChgEvent) cfg_chg_events = static QemuMutex cfg_chg_events_lock; +static void __attribute__((constructor)) vfio_ap_global_init(void) +{ + qemu_mutex_init(&cfg_chg_events_lock); +} + OBJECT_DECLARE_SIMPLE_TYPE(VFIOAPDevice, VFIO_AP_DEVICE) static void vfio_ap_compute_needs_reset(VFIODevice *vdev) @@ -230,13 +235,6 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) VFIOAPDevice *vapdev = VFIO_AP_DEVICE(dev); VFIODevice *vbasedev = &vapdev->vdev; - static bool lock_initialized; - - if (!lock_initialized) { - qemu_mutex_init(&cfg_chg_events_lock); - lock_initialized = true; - } - if (!vfio_device_get_name(vbasedev, errp)) { return; } From b1f521de8b2f356f4ae7683c51aa8c3ba6c7931e Mon Sep 17 00:00:00 2001 From: John Levon Date: Mon, 16 Jun 2025 03:13:36 -0700 Subject: [PATCH 1669/2760] vfio: add vfio_device_get_region_fd() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This keeps the existence of ->region_fds private to hw/vfio/device.c. Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250616101337.3190027-1-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/device.c | 7 +++++++ hw/vfio/region.c | 5 +---- include/hw/vfio/vfio-device.h | 12 ++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/hw/vfio/device.c b/hw/vfio/device.c index d32600eda1..d91c695b69 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -243,6 +243,13 @@ int vfio_device_get_region_info(VFIODevice *vbasedev, int index, return 0; } +int vfio_device_get_region_fd(VFIODevice *vbasedev, int index) +{ + return vbasedev->region_fds ? + vbasedev->region_fds[index] : + vbasedev->fd; +} + int vfio_device_get_region_info_type(VFIODevice *vbasedev, uint32_t type, uint32_t subtype, struct vfio_region_info **info) { diff --git a/hw/vfio/region.c b/hw/vfio/region.c index f5b8e3cbf1..d04c57db63 100644 --- a/hw/vfio/region.c +++ b/hw/vfio/region.c @@ -273,10 +273,7 @@ int vfio_region_mmap(VFIORegion *region) goto no_mmap; } - /* Use the per-region fd if set, or the shared fd. */ - fd = region->vbasedev->region_fds ? - region->vbasedev->region_fds[region->nr] : - region->vbasedev->fd, + fd = vfio_device_get_region_fd(region->vbasedev, region->nr); map_align = (void *)ROUND_UP((uintptr_t)map_base, (uintptr_t)align); munmap(map_base, map_align - map_base); diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index d45e5a68a2..1926adb0a3 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -256,6 +256,18 @@ int vfio_device_get_region_info(VFIODevice *vbasedev, int index, struct vfio_region_info **info); int vfio_device_get_region_info_type(VFIODevice *vbasedev, uint32_t type, uint32_t subtype, struct vfio_region_info **info); + +/** + * Return the fd for mapping this region. This is either the device's fd (for + * e.g. kernel vfio), or a per-region fd (for vfio-user). + * + * @vbasedev: #VFIODevice to use + * @index: region index + * + * Returns the fd. + */ +int vfio_device_get_region_fd(VFIODevice *vbasedev, int index); + bool vfio_device_has_region_cap(VFIODevice *vbasedev, int region, uint16_t cap_type); int vfio_device_get_irq_info(VFIODevice *vbasedev, int index, From 8d60d069d7027bc3121763d73b0c973a1e3f19f3 Mon Sep 17 00:00:00 2001 From: John Levon Date: Mon, 16 Jun 2025 03:13:14 -0700 Subject: [PATCH 1670/2760] vfio: add documentation for posted write argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250616101314.3189793-1-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- include/hw/vfio/vfio-device.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 1926adb0a3..959e458d7f 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -240,6 +240,7 @@ struct VFIODeviceIOOps { * @off: offset within the region * @size: size in bytes to write * @data: buffer to write from + * @post: true if this is a posted write * * Returns number of bytes write on success or -errno. */ From eafb408629d980dac7db24abe5b8fab2c7a67525 Mon Sep 17 00:00:00 2001 From: John Levon Date: Mon, 23 Jun 2025 10:30:53 +0100 Subject: [PATCH 1671/2760] vfio: add license tag to some files MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add SPDX-License-Identifier to some files missing it in hw/vfio/. Signed-off-by: John Levon Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/qemu-devel/20250623093053.1495509-1-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/Kconfig | 2 ++ hw/vfio/meson.build | 2 ++ hw/vfio/trace-events | 2 ++ hw/vfio/trace.h | 3 +++ 4 files changed, 9 insertions(+) diff --git a/hw/vfio/Kconfig b/hw/vfio/Kconfig index 7cdba0560a..91d9023b79 100644 --- a/hw/vfio/Kconfig +++ b/hw/vfio/Kconfig @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + config VFIO bool depends on LINUX diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 73d29f925f..63ea393076 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + vfio_ss = ss.source_set() vfio_ss.add(files( 'listener.c', diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index f06236f37b..e1728c4ef6 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -1,4 +1,6 @@ # See docs/devel/tracing.rst for syntax documentation. +# +# SPDX-License-Identifier: GPL-2.0-or-later # pci.c vfio_intx_interrupt(const char *name, char line) " (%s) Pin %c" diff --git a/hw/vfio/trace.h b/hw/vfio/trace.h index 5a343aa59c..b34b61ddb2 100644 --- a/hw/vfio/trace.h +++ b/hw/vfio/trace.h @@ -1 +1,4 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + */ #include "trace/trace-hw_vfio.h" From abc6249d42f9681d5b659716434ef374c6e0edef Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Mon, 23 Jun 2025 18:22:32 +0800 Subject: [PATCH 1672/2760] vfio/container: Fix SIGSEGV when open container file fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When open /dev/vfio/vfio fails, SIGSEGV triggers because vfio_listener_unregister() doesn't support a NULL bcontainer pointer. Fixes: a1f267a7d4d9 ("vfio/container: reform vfio_container_connect cleanup") Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250623102235.94877-2-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 3e8d645ebb..2853f6f08b 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -710,7 +710,9 @@ static bool vfio_container_connect(VFIOGroup *group, AddressSpace *as, return true; fail: - vfio_listener_unregister(bcontainer); + if (new_container) { + vfio_listener_unregister(bcontainer); + } if (group_was_added) { vfio_container_group_del(container, group); From c72d6ebaadcaec1daa995d47716348e150757a51 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Mon, 23 Jun 2025 18:22:33 +0800 Subject: [PATCH 1673/2760] vfio/container: fails mdev hotplug if add migration blocker failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It's aggressive to abort a running QEMU process when hotplug a mdev and it fails migration blocker adding. Fix by just failing mdev hotplug itself. Signed-off-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250623102235.94877-3-zhenzhong.duan@intel.com [ clg: Changed test on value returned by migrate_add_blocker_modes() ] Signed-off-by: Cédric Le Goater --- hw/vfio/container.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/vfio/container.c b/hw/vfio/container.c index 2853f6f08b..3e13feaa74 100644 --- a/hw/vfio/container.c +++ b/hw/vfio/container.c @@ -992,12 +992,16 @@ static bool vfio_legacy_attach_device(const char *name, VFIODevice *vbasedev, if (vbasedev->mdev) { error_setg(&vbasedev->cpr.mdev_blocker, "CPR does not support vfio mdev %s", vbasedev->name); - migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker, &error_fatal, - MIG_MODE_CPR_TRANSFER, -1); + if (migrate_add_blocker_modes(&vbasedev->cpr.mdev_blocker, errp, + MIG_MODE_CPR_TRANSFER, -1) < 0) { + goto hiod_unref_exit; + } } return true; +hiod_unref_exit: + object_unref(vbasedev->hiod); device_put_exit: vfio_device_put(vbasedev); group_put_exit: From 9fca2b7d702f7ba216c571504922b2f8994537cc Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:29:53 +0100 Subject: [PATCH 1674/2760] vfio-user: add vfio-user class and container MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce basic plumbing for vfio-user with CONFIG_VFIO_USER. We introduce VFIOUserContainer in hw/vfio-user/container.c, which is a container type for the "IOMMU" type "vfio-iommu-user", and share some common container code from hw/vfio/container.c. Add hw/vfio-user/pci.c for instantiating VFIOUserPCIDevice objects, sharing some common code from hw/vfio/pci.c. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-2-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 8 + hw/Kconfig | 1 + hw/meson.build | 1 + hw/vfio-user/Kconfig | 7 + hw/vfio-user/container.c | 208 ++++++++++++++++++++++++++ hw/vfio-user/container.h | 21 +++ hw/vfio-user/meson.build | 9 ++ hw/vfio-user/pci.c | 185 +++++++++++++++++++++++ include/hw/vfio/vfio-container-base.h | 1 + 9 files changed, 441 insertions(+) create mode 100644 hw/vfio-user/Kconfig create mode 100644 hw/vfio-user/container.c create mode 100644 hw/vfio-user/container.h create mode 100644 hw/vfio-user/meson.build create mode 100644 hw/vfio-user/pci.c diff --git a/MAINTAINERS b/MAINTAINERS index 27f4fe3f25..2369391004 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4253,6 +4253,14 @@ F: hw/remote/iommu.c F: include/hw/remote/iommu.h F: tests/functional/test_multiprocess.py +VFIO-USER: +M: John Levon +M: Thanos Makatos +S: Supported +F: hw/vfio-user/* +F: include/hw/vfio-user/* +F: subprojects/libvfio-user + EBPF: M: Jason Wang R: Andrew Melnychenko diff --git a/hw/Kconfig b/hw/Kconfig index 9a86a6a28a..9e6c789ae7 100644 --- a/hw/Kconfig +++ b/hw/Kconfig @@ -42,6 +42,7 @@ source ufs/Kconfig source usb/Kconfig source virtio/Kconfig source vfio/Kconfig +source vfio-user/Kconfig source vmapple/Kconfig source xen/Kconfig source watchdog/Kconfig diff --git a/hw/meson.build b/hw/meson.build index b91f761fe0..791ce21ab4 100644 --- a/hw/meson.build +++ b/hw/meson.build @@ -39,6 +39,7 @@ subdir('uefi') subdir('ufs') subdir('usb') subdir('vfio') +subdir('vfio-user') subdir('virtio') subdir('vmapple') subdir('watchdog') diff --git a/hw/vfio-user/Kconfig b/hw/vfio-user/Kconfig new file mode 100644 index 0000000000..24bdf7af90 --- /dev/null +++ b/hw/vfio-user/Kconfig @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +config VFIO_USER + bool + default y + depends on VFIO_PCI + diff --git a/hw/vfio-user/container.c b/hw/vfio-user/container.c new file mode 100644 index 0000000000..2367332177 --- /dev/null +++ b/hw/vfio-user/container.c @@ -0,0 +1,208 @@ +/* + * Container for vfio-user IOMMU type: rather than communicating with the kernel + * vfio driver, we communicate over a socket to a server using the vfio-user + * protocol. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include +#include "qemu/osdep.h" + +#include "hw/vfio-user/container.h" +#include "hw/vfio/vfio-cpr.h" +#include "hw/vfio/vfio-device.h" +#include "hw/vfio/vfio-listener.h" +#include "qapi/error.h" + +static int vfio_user_dma_unmap(const VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + IOMMUTLBEntry *iotlb, bool unmap_all) +{ + return -ENOTSUP; +} + +static int vfio_user_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, + ram_addr_t size, void *vaddr, bool readonly, + MemoryRegion *mrp) +{ + return -ENOTSUP; +} + +static int +vfio_user_set_dirty_page_tracking(const VFIOContainerBase *bcontainer, + bool start, Error **errp) +{ + error_setg_errno(errp, ENOTSUP, "Not supported"); + return -ENOTSUP; +} + +static int vfio_user_query_dirty_bitmap(const VFIOContainerBase *bcontainer, + VFIOBitmap *vbmap, hwaddr iova, + hwaddr size, Error **errp) +{ + error_setg_errno(errp, ENOTSUP, "Not supported"); + return -ENOTSUP; +} + +static bool vfio_user_setup(VFIOContainerBase *bcontainer, Error **errp) +{ + error_setg_errno(errp, ENOTSUP, "Not supported"); + return -ENOTSUP; +} + +static VFIOUserContainer *vfio_user_create_container(Error **errp) +{ + VFIOUserContainer *container; + + container = VFIO_IOMMU_USER(object_new(TYPE_VFIO_IOMMU_USER)); + return container; +} + +/* + * Try to mirror vfio_container_connect() as much as possible. + */ +static VFIOUserContainer * +vfio_user_container_connect(AddressSpace *as, Error **errp) +{ + VFIOContainerBase *bcontainer; + VFIOUserContainer *container; + VFIOAddressSpace *space; + VFIOIOMMUClass *vioc; + + space = vfio_address_space_get(as); + + container = vfio_user_create_container(errp); + if (!container) { + goto put_space_exit; + } + + bcontainer = &container->bcontainer; + + if (!vfio_cpr_register_container(bcontainer, errp)) { + goto free_container_exit; + } + + vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + assert(vioc->setup); + + if (!vioc->setup(bcontainer, errp)) { + goto unregister_container_exit; + } + + vfio_address_space_insert(space, bcontainer); + + if (!vfio_listener_register(bcontainer, errp)) { + goto listener_release_exit; + } + + bcontainer->initialized = true; + + return container; + +listener_release_exit: + vfio_listener_unregister(bcontainer); + if (vioc->release) { + vioc->release(bcontainer); + } + +unregister_container_exit: + vfio_cpr_unregister_container(bcontainer); + +free_container_exit: + object_unref(container); + +put_space_exit: + vfio_address_space_put(space); + + return NULL; +} + +static void vfio_user_container_disconnect(VFIOUserContainer *container) +{ + VFIOContainerBase *bcontainer = &container->bcontainer; + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + + vfio_listener_unregister(bcontainer); + if (vioc->release) { + vioc->release(bcontainer); + } + + VFIOAddressSpace *space = bcontainer->space; + + vfio_cpr_unregister_container(bcontainer); + object_unref(container); + + vfio_address_space_put(space); +} + +static bool vfio_user_device_get(VFIOUserContainer *container, + VFIODevice *vbasedev, Error **errp) +{ + struct vfio_device_info info = { 0 }; + + vbasedev->fd = -1; + + vfio_device_prepare(vbasedev, &container->bcontainer, &info); + + return true; +} + +/* + * vfio_user_device_attach: attach a device to a new container. + */ +static bool vfio_user_device_attach(const char *name, VFIODevice *vbasedev, + AddressSpace *as, Error **errp) +{ + VFIOUserContainer *container; + + container = vfio_user_container_connect(as, errp); + if (container == NULL) { + error_prepend(errp, "failed to connect proxy"); + return false; + } + + return vfio_user_device_get(container, vbasedev, errp); +} + +static void vfio_user_device_detach(VFIODevice *vbasedev) +{ + VFIOUserContainer *container = container_of(vbasedev->bcontainer, + VFIOUserContainer, bcontainer); + + vfio_device_unprepare(vbasedev); + + vfio_user_container_disconnect(container); +} + +static int vfio_user_pci_hot_reset(VFIODevice *vbasedev, bool single) +{ + /* ->needs_reset is always false for vfio-user. */ + return 0; +} + +static void vfio_iommu_user_class_init(ObjectClass *klass, const void *data) +{ + VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); + + vioc->setup = vfio_user_setup; + vioc->dma_map = vfio_user_dma_map; + vioc->dma_unmap = vfio_user_dma_unmap; + vioc->attach_device = vfio_user_device_attach; + vioc->detach_device = vfio_user_device_detach; + vioc->set_dirty_page_tracking = vfio_user_set_dirty_page_tracking; + vioc->query_dirty_bitmap = vfio_user_query_dirty_bitmap; + vioc->pci_hot_reset = vfio_user_pci_hot_reset; +}; + +static const TypeInfo types[] = { + { + .name = TYPE_VFIO_IOMMU_USER, + .parent = TYPE_VFIO_IOMMU, + .instance_size = sizeof(VFIOUserContainer), + .class_init = vfio_iommu_user_class_init, + }, +}; + +DEFINE_TYPES(types) diff --git a/hw/vfio-user/container.h b/hw/vfio-user/container.h new file mode 100644 index 0000000000..e4a46d2c1b --- /dev/null +++ b/hw/vfio-user/container.h @@ -0,0 +1,21 @@ +/* + * vfio-user specific definitions. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_VFIO_USER_CONTAINER_H +#define HW_VFIO_USER_CONTAINER_H + +#include "qemu/osdep.h" + +#include "hw/vfio/vfio-container-base.h" + +/* MMU container sub-class for vfio-user. */ +typedef struct VFIOUserContainer { + VFIOContainerBase bcontainer; +} VFIOUserContainer; + +OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserContainer, VFIO_IOMMU_USER); + +#endif /* HW_VFIO_USER_CONTAINER_H */ diff --git a/hw/vfio-user/meson.build b/hw/vfio-user/meson.build new file mode 100644 index 0000000000..b82c558252 --- /dev/null +++ b/hw/vfio-user/meson.build @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-or-later + +vfio_user_ss = ss.source_set() +vfio_user_ss.add(files( + 'container.c', + 'pci.c', +)) + +system_ss.add_all(when: 'CONFIG_VFIO_USER', if_true: vfio_user_ss) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c new file mode 100644 index 0000000000..86d7055747 --- /dev/null +++ b/hw/vfio-user/pci.c @@ -0,0 +1,185 @@ +/* + * vfio PCI device over a UNIX socket. + * + * Copyright © 2018, 2021 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include "qemu/osdep.h" +#include "qapi-visit-sockets.h" + +#include "hw/qdev-properties.h" +#include "hw/vfio/pci.h" + +#define TYPE_VFIO_USER_PCI "vfio-user-pci" +OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserPCIDevice, VFIO_USER_PCI) + +struct VFIOUserPCIDevice { + VFIOPCIDevice device; + SocketAddress *socket; +}; + +/* + * Emulated devices don't use host hot reset + */ +static void vfio_user_compute_needs_reset(VFIODevice *vbasedev) +{ + vbasedev->needs_reset = false; +} + +static Object *vfio_user_pci_get_object(VFIODevice *vbasedev) +{ + VFIOUserPCIDevice *vdev = container_of(vbasedev, VFIOUserPCIDevice, + device.vbasedev); + + return OBJECT(vdev); +} + +static VFIODeviceOps vfio_user_pci_ops = { + .vfio_compute_needs_reset = vfio_user_compute_needs_reset, + .vfio_eoi = vfio_pci_intx_eoi, + .vfio_get_object = vfio_user_pci_get_object, + /* No live migration support yet. */ + .vfio_save_config = NULL, + .vfio_load_config = NULL, +}; + +static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) +{ + ERRP_GUARD(); + VFIOUserPCIDevice *udev = VFIO_USER_PCI(pdev); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(pdev); + VFIODevice *vbasedev = &vdev->vbasedev; + const char *sock_name; + AddressSpace *as; + + if (!udev->socket) { + error_setg(errp, "No socket specified"); + error_append_hint(errp, "e.g. -device '{" + "\"driver\":\"vfio-user-pci\", " + "\"socket\": {\"path\": \"/tmp/vfio-user.sock\", " + "\"type\": \"unix\"}'" + "}'\n"); + return; + } + + sock_name = udev->socket->u.q_unix.path; + + vbasedev->name = g_strdup_printf("vfio-user:%s", sock_name); + + /* + * vfio-user devices are effectively mdevs (don't use a host iommu). + */ + vbasedev->mdev = true; + + as = pci_device_iommu_address_space(pdev); + if (!vfio_device_attach_by_iommu_type(TYPE_VFIO_IOMMU_USER, + vbasedev->name, vbasedev, + as, errp)) { + error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->name); + return; + } +} + +static void vfio_user_instance_init(Object *obj) +{ + PCIDevice *pci_dev = PCI_DEVICE(obj); + VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); + VFIODevice *vbasedev = &vdev->vbasedev; + + device_add_bootindex_property(obj, &vdev->bootindex, + "bootindex", NULL, + &pci_dev->qdev); + vdev->host.domain = ~0U; + vdev->host.bus = ~0U; + vdev->host.slot = ~0U; + vdev->host.function = ~0U; + + vfio_device_init(vbasedev, VFIO_DEVICE_TYPE_PCI, &vfio_user_pci_ops, + DEVICE(vdev), false); + + vdev->nv_gpudirect_clique = 0xFF; + + /* + * QEMU_PCI_CAP_EXPRESS initialization does not depend on QEMU command + * line, therefore, no need to wait to realize like other devices. + */ + pci_dev->cap_present |= QEMU_PCI_CAP_EXPRESS; +} + +static void vfio_user_instance_finalize(Object *obj) +{ + VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); + + vfio_pci_put_device(vdev); +} + +static const Property vfio_user_pci_dev_properties[] = { + DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice, + vendor_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-pci-device-id", VFIOPCIDevice, + device_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-pci-sub-vendor-id", VFIOPCIDevice, + sub_vendor_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice, + sub_device_id, PCI_ANY_ID), +}; + +static void vfio_user_pci_set_socket(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + VFIOUserPCIDevice *udev = VFIO_USER_PCI(obj); + bool success; + + qapi_free_SocketAddress(udev->socket); + + udev->socket = NULL; + + success = visit_type_SocketAddress(v, name, &udev->socket, errp); + + if (!success) { + return; + } + + if (udev->socket->type != SOCKET_ADDRESS_TYPE_UNIX) { + error_setg(errp, "Unsupported socket type %s", + SocketAddressType_str(udev->socket->type)); + qapi_free_SocketAddress(udev->socket); + udev->socket = NULL; + return; + } +} + +static void vfio_user_pci_dev_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); + + device_class_set_props(dc, vfio_user_pci_dev_properties); + + object_class_property_add(klass, "socket", "SocketAddress", NULL, + vfio_user_pci_set_socket, NULL, NULL); + object_class_property_set_description(klass, "socket", + "SocketAddress (UNIX sockets only)"); + + dc->desc = "VFIO over socket PCI device assignment"; + pdc->realize = vfio_user_pci_realize; +} + +static const TypeInfo vfio_user_pci_dev_info = { + .name = TYPE_VFIO_USER_PCI, + .parent = TYPE_VFIO_PCI_BASE, + .instance_size = sizeof(VFIOUserPCIDevice), + .class_init = vfio_user_pci_dev_class_init, + .instance_init = vfio_user_instance_init, + .instance_finalize = vfio_user_instance_finalize, +}; + +static void register_vfio_user_dev_type(void) +{ + type_register_static(&vfio_user_pci_dev_info); +} + + type_init(register_vfio_user_dev_type) diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index f0232654ee..3cd86ec59e 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -109,6 +109,7 @@ vfio_container_get_page_size_mask(const VFIOContainerBase *bcontainer) #define TYPE_VFIO_IOMMU_LEGACY TYPE_VFIO_IOMMU "-legacy" #define TYPE_VFIO_IOMMU_SPAPR TYPE_VFIO_IOMMU "-spapr" #define TYPE_VFIO_IOMMU_IOMMUFD TYPE_VFIO_IOMMU "-iommufd" +#define TYPE_VFIO_IOMMU_USER TYPE_VFIO_IOMMU "-user" OBJECT_DECLARE_TYPE(VFIOContainerBase, VFIOIOMMUClass, VFIO_IOMMU) From 438d863f1f40fbc2b57bf94cc6c998a6445c0932 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:29:54 +0100 Subject: [PATCH 1675/2760] vfio-user: connect vfio proxy to remote server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce the vfio-user "proxy": this is the client code responsible for sending and receiving vfio-user messages across the control socket. The new files hw/vfio-user/proxy.[ch] contain some basic plumbing for managing the proxy; initialize the proxy during realization of the VFIOUserPCIDevice instance. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-3-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/meson.build | 1 + hw/vfio-user/pci.c | 22 +++++ hw/vfio-user/proxy.c | 162 ++++++++++++++++++++++++++++++++++ hw/vfio-user/proxy.h | 79 +++++++++++++++++ include/hw/vfio/vfio-device.h | 2 + 5 files changed, 266 insertions(+) create mode 100644 hw/vfio-user/proxy.c create mode 100644 hw/vfio-user/proxy.h diff --git a/hw/vfio-user/meson.build b/hw/vfio-user/meson.build index b82c558252..9e85a8ea51 100644 --- a/hw/vfio-user/meson.build +++ b/hw/vfio-user/meson.build @@ -4,6 +4,7 @@ vfio_user_ss = ss.source_set() vfio_user_ss.add(files( 'container.c', 'pci.c', + 'proxy.c', )) system_ss.add_all(when: 'CONFIG_VFIO_USER', if_true: vfio_user_ss) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index 86d7055747..642421e791 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -12,6 +12,7 @@ #include "hw/qdev-properties.h" #include "hw/vfio/pci.h" +#include "hw/vfio-user/proxy.h" #define TYPE_VFIO_USER_PCI "vfio-user-pci" OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserPCIDevice, VFIO_USER_PCI) @@ -54,6 +55,8 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) VFIODevice *vbasedev = &vdev->vbasedev; const char *sock_name; AddressSpace *as; + SocketAddress addr; + VFIOUserProxy *proxy; if (!udev->socket) { error_setg(errp, "No socket specified"); @@ -69,6 +72,15 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) vbasedev->name = g_strdup_printf("vfio-user:%s", sock_name); + memset(&addr, 0, sizeof(addr)); + addr.type = SOCKET_ADDRESS_TYPE_UNIX; + addr.u.q_unix.path = (char *)sock_name; + proxy = vfio_user_connect_dev(&addr, errp); + if (!proxy) { + return; + } + vbasedev->proxy = proxy; + /* * vfio-user devices are effectively mdevs (don't use a host iommu). */ @@ -112,8 +124,13 @@ static void vfio_user_instance_init(Object *obj) static void vfio_user_instance_finalize(Object *obj) { VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); + VFIODevice *vbasedev = &vdev->vbasedev; vfio_pci_put_device(vdev); + + if (vbasedev->proxy != NULL) { + vfio_user_disconnect(vbasedev->proxy); + } } static const Property vfio_user_pci_dev_properties[] = { @@ -133,6 +150,11 @@ static void vfio_user_pci_set_socket(Object *obj, Visitor *v, const char *name, VFIOUserPCIDevice *udev = VFIO_USER_PCI(obj); bool success; + if (udev->device.vbasedev.proxy) { + error_setg(errp, "Proxy is connected"); + return; + } + qapi_free_SocketAddress(udev->socket); udev->socket = NULL; diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c new file mode 100644 index 0000000000..bb436c9db9 --- /dev/null +++ b/hw/vfio-user/proxy.c @@ -0,0 +1,162 @@ +/* + * vfio protocol over a UNIX socket. + * + * Copyright © 2018, 2021 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include + +#include "hw/vfio/vfio-device.h" +#include "hw/vfio-user/proxy.h" +#include "qapi/error.h" +#include "qemu/error-report.h" +#include "qemu/lockable.h" +#include "system/iothread.h" + +static IOThread *vfio_user_iothread; + +static void vfio_user_shutdown(VFIOUserProxy *proxy); + + +/* + * Functions called by main, CPU, or iothread threads + */ + +static void vfio_user_shutdown(VFIOUserProxy *proxy) +{ + qio_channel_shutdown(proxy->ioc, QIO_CHANNEL_SHUTDOWN_READ, NULL); + qio_channel_set_aio_fd_handler(proxy->ioc, proxy->ctx, NULL, + proxy->ctx, NULL, NULL); +} + +/* + * Functions only called by iothread + */ + +static void vfio_user_cb(void *opaque) +{ + VFIOUserProxy *proxy = opaque; + + QEMU_LOCK_GUARD(&proxy->lock); + + proxy->state = VFIO_PROXY_CLOSED; + qemu_cond_signal(&proxy->close_cv); +} + + +/* + * Functions called by main or CPU threads + */ + +static QLIST_HEAD(, VFIOUserProxy) vfio_user_sockets = + QLIST_HEAD_INITIALIZER(vfio_user_sockets); + +VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp) +{ + VFIOUserProxy *proxy; + QIOChannelSocket *sioc; + QIOChannel *ioc; + char *sockname; + + if (addr->type != SOCKET_ADDRESS_TYPE_UNIX) { + error_setg(errp, "vfio_user_connect - bad address family"); + return NULL; + } + sockname = addr->u.q_unix.path; + + sioc = qio_channel_socket_new(); + ioc = QIO_CHANNEL(sioc); + if (qio_channel_socket_connect_sync(sioc, addr, errp)) { + object_unref(OBJECT(ioc)); + return NULL; + } + qio_channel_set_blocking(ioc, false, NULL); + + proxy = g_malloc0(sizeof(VFIOUserProxy)); + proxy->sockname = g_strdup_printf("unix:%s", sockname); + proxy->ioc = ioc; + proxy->flags = VFIO_PROXY_CLIENT; + proxy->state = VFIO_PROXY_CONNECTED; + + qemu_mutex_init(&proxy->lock); + qemu_cond_init(&proxy->close_cv); + + if (vfio_user_iothread == NULL) { + vfio_user_iothread = iothread_create("VFIO user", errp); + } + + proxy->ctx = iothread_get_aio_context(vfio_user_iothread); + + QTAILQ_INIT(&proxy->outgoing); + QTAILQ_INIT(&proxy->incoming); + QTAILQ_INIT(&proxy->free); + QTAILQ_INIT(&proxy->pending); + QLIST_INSERT_HEAD(&vfio_user_sockets, proxy, next); + + return proxy; +} + +void vfio_user_disconnect(VFIOUserProxy *proxy) +{ + VFIOUserMsg *r1, *r2; + + qemu_mutex_lock(&proxy->lock); + + /* our side is quitting */ + if (proxy->state == VFIO_PROXY_CONNECTED) { + vfio_user_shutdown(proxy); + if (!QTAILQ_EMPTY(&proxy->pending)) { + error_printf("vfio_user_disconnect: outstanding requests\n"); + } + } + object_unref(OBJECT(proxy->ioc)); + proxy->ioc = NULL; + + proxy->state = VFIO_PROXY_CLOSING; + QTAILQ_FOREACH_SAFE(r1, &proxy->outgoing, next, r2) { + qemu_cond_destroy(&r1->cv); + QTAILQ_REMOVE(&proxy->outgoing, r1, next); + g_free(r1); + } + QTAILQ_FOREACH_SAFE(r1, &proxy->incoming, next, r2) { + qemu_cond_destroy(&r1->cv); + QTAILQ_REMOVE(&proxy->incoming, r1, next); + g_free(r1); + } + QTAILQ_FOREACH_SAFE(r1, &proxy->pending, next, r2) { + qemu_cond_destroy(&r1->cv); + QTAILQ_REMOVE(&proxy->pending, r1, next); + g_free(r1); + } + QTAILQ_FOREACH_SAFE(r1, &proxy->free, next, r2) { + qemu_cond_destroy(&r1->cv); + QTAILQ_REMOVE(&proxy->free, r1, next); + g_free(r1); + } + + /* + * Make sure the iothread isn't blocking anywhere + * with a ref to this proxy by waiting for a BH + * handler to run after the proxy fd handlers were + * deleted above. + */ + aio_bh_schedule_oneshot(proxy->ctx, vfio_user_cb, proxy); + qemu_cond_wait(&proxy->close_cv, &proxy->lock); + + /* we now hold the only ref to proxy */ + qemu_mutex_unlock(&proxy->lock); + qemu_cond_destroy(&proxy->close_cv); + qemu_mutex_destroy(&proxy->lock); + + QLIST_REMOVE(proxy, next); + if (QLIST_EMPTY(&vfio_user_sockets)) { + iothread_destroy(vfio_user_iothread); + vfio_user_iothread = NULL; + } + + g_free(proxy->sockname); + g_free(proxy); +} diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h new file mode 100644 index 0000000000..a9bce82239 --- /dev/null +++ b/hw/vfio-user/proxy.h @@ -0,0 +1,79 @@ +#ifndef VFIO_USER_PROXY_H +#define VFIO_USER_PROXY_H + +/* + * vfio protocol over a UNIX socket. + * + * Copyright © 2018, 2021 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "io/channel.h" +#include "io/channel-socket.h" + +typedef struct { + int send_fds; + int recv_fds; + int *fds; +} VFIOUserFDs; + +enum msg_type { + VFIO_MSG_NONE, + VFIO_MSG_ASYNC, + VFIO_MSG_WAIT, + VFIO_MSG_NOWAIT, + VFIO_MSG_REQ, +}; + +typedef struct VFIOUserMsg { + QTAILQ_ENTRY(VFIOUserMsg) next; + VFIOUserFDs *fds; + uint32_t rsize; + uint32_t id; + QemuCond cv; + bool complete; + enum msg_type type; +} VFIOUserMsg; + + +enum proxy_state { + VFIO_PROXY_CONNECTED = 1, + VFIO_PROXY_ERROR = 2, + VFIO_PROXY_CLOSING = 3, + VFIO_PROXY_CLOSED = 4, +}; + +typedef QTAILQ_HEAD(VFIOUserMsgQ, VFIOUserMsg) VFIOUserMsgQ; + +typedef struct VFIOUserProxy { + QLIST_ENTRY(VFIOUserProxy) next; + char *sockname; + struct QIOChannel *ioc; + void (*request)(void *opaque, VFIOUserMsg *msg); + void *req_arg; + int flags; + QemuCond close_cv; + AioContext *ctx; + QEMUBH *req_bh; + + /* + * above only changed when BQL is held + * below are protected by per-proxy lock + */ + QemuMutex lock; + VFIOUserMsgQ free; + VFIOUserMsgQ pending; + VFIOUserMsgQ incoming; + VFIOUserMsgQ outgoing; + VFIOUserMsg *last_nowait; + enum proxy_state state; +} VFIOUserProxy; + +/* VFIOProxy flags */ +#define VFIO_PROXY_CLIENT 0x1 + +VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp); +void vfio_user_disconnect(VFIOUserProxy *proxy); + +#endif /* VFIO_USER_PROXY_H */ diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 959e458d7f..c616652ee7 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -47,6 +47,7 @@ typedef struct VFIOMigration VFIOMigration; typedef struct IOMMUFDBackend IOMMUFDBackend; typedef struct VFIOIOASHwpt VFIOIOASHwpt; +typedef struct VFIOUserProxy VFIOUserProxy; typedef struct VFIODevice { QLIST_ENTRY(VFIODevice) next; @@ -88,6 +89,7 @@ typedef struct VFIODevice { struct vfio_region_info **reginfo; int *region_fds; VFIODeviceCPR cpr; + VFIOUserProxy *proxy; } VFIODevice; struct VFIODeviceOps { From 0b3d881a061b284a3db00d7fe9d33581fb424287 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:29:55 +0100 Subject: [PATCH 1676/2760] vfio-user: implement message receive infrastructure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the basic implementation for receiving vfio-user messages from the control socket. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-4-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 11 + hw/vfio-user/protocol.h | 53 +++++ hw/vfio-user/proxy.c | 411 ++++++++++++++++++++++++++++++++++++++ hw/vfio-user/proxy.h | 11 + hw/vfio-user/trace-events | 8 + hw/vfio-user/trace.h | 4 + meson.build | 1 + 7 files changed, 499 insertions(+) create mode 100644 hw/vfio-user/protocol.h create mode 100644 hw/vfio-user/trace-events create mode 100644 hw/vfio-user/trace.h diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index 642421e791..bad2829f5c 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -22,6 +22,16 @@ struct VFIOUserPCIDevice { SocketAddress *socket; }; +/* + * Incoming request message callback. + * + * Runs off main loop, so BQL held. + */ +static void vfio_user_pci_process_req(void *opaque, VFIOUserMsg *msg) +{ + +} + /* * Emulated devices don't use host hot reset */ @@ -80,6 +90,7 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) return; } vbasedev->proxy = proxy; + vfio_user_set_handler(vbasedev, vfio_user_pci_process_req, vdev); /* * vfio-user devices are effectively mdevs (don't use a host iommu). diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h new file mode 100644 index 0000000000..4ddfb5f222 --- /dev/null +++ b/hw/vfio-user/protocol.h @@ -0,0 +1,53 @@ +#ifndef VFIO_USER_PROTOCOL_H +#define VFIO_USER_PROTOCOL_H + +/* + * vfio protocol over a UNIX socket. + * + * Copyright © 2018, 2021 Oracle and/or its affiliates. + * + * Each message has a standard header that describes the command + * being sent, which is almost always a VFIO ioctl(). + * + * The header may be followed by command-specific data, such as the + * region and offset info for read and write commands. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +typedef struct { + uint16_t id; + uint16_t command; + uint32_t size; + uint32_t flags; + uint32_t error_reply; +} VFIOUserHdr; + +/* VFIOUserHdr commands */ +enum vfio_user_command { + VFIO_USER_VERSION = 1, + VFIO_USER_DMA_MAP = 2, + VFIO_USER_DMA_UNMAP = 3, + VFIO_USER_DEVICE_GET_INFO = 4, + VFIO_USER_DEVICE_GET_REGION_INFO = 5, + VFIO_USER_DEVICE_GET_REGION_IO_FDS = 6, + VFIO_USER_DEVICE_GET_IRQ_INFO = 7, + VFIO_USER_DEVICE_SET_IRQS = 8, + VFIO_USER_REGION_READ = 9, + VFIO_USER_REGION_WRITE = 10, + VFIO_USER_DMA_READ = 11, + VFIO_USER_DMA_WRITE = 12, + VFIO_USER_DEVICE_RESET = 13, + VFIO_USER_DIRTY_PAGES = 14, + VFIO_USER_MAX, +}; + +/* VFIOUserHdr flags */ +#define VFIO_USER_REQUEST 0x0 +#define VFIO_USER_REPLY 0x1 +#define VFIO_USER_TYPE 0xF + +#define VFIO_USER_NO_REPLY 0x10 +#define VFIO_USER_ERROR 0x20 + +#endif /* VFIO_USER_PROTOCOL_H */ diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index bb436c9db9..349ea2b27c 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -11,15 +11,31 @@ #include "hw/vfio/vfio-device.h" #include "hw/vfio-user/proxy.h" +#include "hw/vfio-user/trace.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/lockable.h" +#include "qemu/main-loop.h" #include "system/iothread.h" static IOThread *vfio_user_iothread; static void vfio_user_shutdown(VFIOUserProxy *proxy); +static VFIOUserMsg *vfio_user_getmsg(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds); +static VFIOUserFDs *vfio_user_getfds(int numfds); +static void vfio_user_recycle(VFIOUserProxy *proxy, VFIOUserMsg *msg); +static void vfio_user_recv(void *opaque); +static void vfio_user_cb(void *opaque); + +static void vfio_user_request(void *opaque); + +static inline void vfio_user_set_error(VFIOUserHdr *hdr, uint32_t err) +{ + hdr->flags |= VFIO_USER_ERROR; + hdr->error_reply = err; +} /* * Functions called by main, CPU, or iothread threads @@ -32,10 +48,343 @@ static void vfio_user_shutdown(VFIOUserProxy *proxy) proxy->ctx, NULL, NULL); } +static VFIOUserMsg *vfio_user_getmsg(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds) +{ + VFIOUserMsg *msg; + + msg = QTAILQ_FIRST(&proxy->free); + if (msg != NULL) { + QTAILQ_REMOVE(&proxy->free, msg, next); + } else { + msg = g_malloc0(sizeof(*msg)); + qemu_cond_init(&msg->cv); + } + + msg->hdr = hdr; + msg->fds = fds; + return msg; +} + +/* + * Recycle a message list entry to the free list. + */ +static void vfio_user_recycle(VFIOUserProxy *proxy, VFIOUserMsg *msg) +{ + if (msg->type == VFIO_MSG_NONE) { + error_printf("vfio_user_recycle - freeing free msg\n"); + return; + } + + /* free msg buffer if no one is waiting to consume the reply */ + if (msg->type == VFIO_MSG_NOWAIT || msg->type == VFIO_MSG_ASYNC) { + g_free(msg->hdr); + if (msg->fds != NULL) { + g_free(msg->fds); + } + } + + msg->type = VFIO_MSG_NONE; + msg->hdr = NULL; + msg->fds = NULL; + msg->complete = false; + QTAILQ_INSERT_HEAD(&proxy->free, msg, next); +} + +static VFIOUserFDs *vfio_user_getfds(int numfds) +{ + VFIOUserFDs *fds = g_malloc0(sizeof(*fds) + (numfds * sizeof(int))); + + fds->fds = (int *)((char *)fds + sizeof(*fds)); + + return fds; +} + /* * Functions only called by iothread */ +/* + * Process a received message. + */ +static void vfio_user_process(VFIOUserProxy *proxy, VFIOUserMsg *msg, + bool isreply) +{ + + /* + * Replies signal a waiter, if none just check for errors + * and free the message buffer. + * + * Requests get queued for the BH. + */ + if (isreply) { + msg->complete = true; + if (msg->type == VFIO_MSG_WAIT) { + qemu_cond_signal(&msg->cv); + } else { + if (msg->hdr->flags & VFIO_USER_ERROR) { + error_printf("vfio_user_process: error reply on async "); + error_printf("request command %x error %s\n", + msg->hdr->command, + strerror(msg->hdr->error_reply)); + } + /* youngest nowait msg has been ack'd */ + if (proxy->last_nowait == msg) { + proxy->last_nowait = NULL; + } + vfio_user_recycle(proxy, msg); + } + } else { + QTAILQ_INSERT_TAIL(&proxy->incoming, msg, next); + qemu_bh_schedule(proxy->req_bh); + } +} + +/* + * Complete a partial message read + */ +static int vfio_user_complete(VFIOUserProxy *proxy, Error **errp) +{ + VFIOUserMsg *msg = proxy->part_recv; + size_t msgleft = proxy->recv_left; + bool isreply; + char *data; + int ret; + + data = (char *)msg->hdr + (msg->hdr->size - msgleft); + while (msgleft > 0) { + ret = qio_channel_read(proxy->ioc, data, msgleft, errp); + + /* error or would block */ + if (ret <= 0) { + /* try for rest on next iternation */ + if (ret == QIO_CHANNEL_ERR_BLOCK) { + proxy->recv_left = msgleft; + } + return ret; + } + trace_vfio_user_recv_read(msg->hdr->id, ret); + + msgleft -= ret; + data += ret; + } + + /* + * Read complete message, process it. + */ + proxy->part_recv = NULL; + proxy->recv_left = 0; + isreply = (msg->hdr->flags & VFIO_USER_TYPE) == VFIO_USER_REPLY; + vfio_user_process(proxy, msg, isreply); + + /* return positive value */ + return 1; +} + +/* + * Receive and process one incoming message. + * + * For replies, find matching outgoing request and wake any waiters. + * For requests, queue in incoming list and run request BH. + */ +static int vfio_user_recv_one(VFIOUserProxy *proxy, Error **errp) +{ + VFIOUserMsg *msg = NULL; + g_autofree int *fdp = NULL; + VFIOUserFDs *reqfds; + VFIOUserHdr hdr; + struct iovec iov = { + .iov_base = &hdr, + .iov_len = sizeof(hdr), + }; + bool isreply = false; + int i, ret; + size_t msgleft, numfds = 0; + char *data = NULL; + char *buf = NULL; + + /* + * Complete any partial reads + */ + if (proxy->part_recv != NULL) { + ret = vfio_user_complete(proxy, errp); + + /* still not complete, try later */ + if (ret == QIO_CHANNEL_ERR_BLOCK) { + return ret; + } + + if (ret <= 0) { + goto fatal; + } + /* else fall into reading another msg */ + } + + /* + * Read header + */ + ret = qio_channel_readv_full(proxy->ioc, &iov, 1, &fdp, &numfds, 0, + errp); + if (ret == QIO_CHANNEL_ERR_BLOCK) { + return ret; + } + + /* read error or other side closed connection */ + if (ret <= 0) { + goto fatal; + } + + if (ret < sizeof(hdr)) { + error_setg(errp, "short read of header"); + goto fatal; + } + + /* + * Validate header + */ + if (hdr.size < sizeof(VFIOUserHdr)) { + error_setg(errp, "bad header size"); + goto fatal; + } + switch (hdr.flags & VFIO_USER_TYPE) { + case VFIO_USER_REQUEST: + isreply = false; + break; + case VFIO_USER_REPLY: + isreply = true; + break; + default: + error_setg(errp, "unknown message type"); + goto fatal; + } + trace_vfio_user_recv_hdr(proxy->sockname, hdr.id, hdr.command, hdr.size, + hdr.flags); + + /* + * For replies, find the matching pending request. + * For requests, reap incoming FDs. + */ + if (isreply) { + QTAILQ_FOREACH(msg, &proxy->pending, next) { + if (hdr.id == msg->id) { + break; + } + } + if (msg == NULL) { + error_setg(errp, "unexpected reply"); + goto err; + } + QTAILQ_REMOVE(&proxy->pending, msg, next); + + /* + * Process any received FDs + */ + if (numfds != 0) { + if (msg->fds == NULL || msg->fds->recv_fds < numfds) { + error_setg(errp, "unexpected FDs"); + goto err; + } + msg->fds->recv_fds = numfds; + memcpy(msg->fds->fds, fdp, numfds * sizeof(int)); + } + } else { + if (numfds != 0) { + reqfds = vfio_user_getfds(numfds); + memcpy(reqfds->fds, fdp, numfds * sizeof(int)); + } else { + reqfds = NULL; + } + } + + /* + * Put the whole message into a single buffer. + */ + if (isreply) { + if (hdr.size > msg->rsize) { + error_setg(errp, "reply larger than recv buffer"); + goto err; + } + *msg->hdr = hdr; + data = (char *)msg->hdr + sizeof(hdr); + } else { + buf = g_malloc0(hdr.size); + memcpy(buf, &hdr, sizeof(hdr)); + data = buf + sizeof(hdr); + msg = vfio_user_getmsg(proxy, (VFIOUserHdr *)buf, reqfds); + msg->type = VFIO_MSG_REQ; + } + + /* + * Read rest of message. + */ + msgleft = hdr.size - sizeof(hdr); + while (msgleft > 0) { + ret = qio_channel_read(proxy->ioc, data, msgleft, errp); + + /* prepare to complete read on next iternation */ + if (ret == QIO_CHANNEL_ERR_BLOCK) { + proxy->part_recv = msg; + proxy->recv_left = msgleft; + return ret; + } + + if (ret <= 0) { + goto fatal; + } + trace_vfio_user_recv_read(hdr.id, ret); + + msgleft -= ret; + data += ret; + } + + vfio_user_process(proxy, msg, isreply); + return 0; + + /* + * fatal means the other side closed or we don't trust the stream + * err means this message is corrupt + */ +fatal: + vfio_user_shutdown(proxy); + proxy->state = VFIO_PROXY_ERROR; + + /* set error if server side closed */ + if (ret == 0) { + error_setg(errp, "server closed socket"); + } + +err: + for (i = 0; i < numfds; i++) { + close(fdp[i]); + } + if (isreply && msg != NULL) { + /* force an error to keep sending thread from hanging */ + vfio_user_set_error(msg->hdr, EINVAL); + msg->complete = true; + qemu_cond_signal(&msg->cv); + } + return -1; +} + +static void vfio_user_recv(void *opaque) +{ + VFIOUserProxy *proxy = opaque; + + QEMU_LOCK_GUARD(&proxy->lock); + + if (proxy->state == VFIO_PROXY_CONNECTED) { + Error *local_err = NULL; + + while (vfio_user_recv_one(proxy, &local_err) == 0) { + ; + } + + if (local_err != NULL) { + error_report_err(local_err); + } + } +} + static void vfio_user_cb(void *opaque) { VFIOUserProxy *proxy = opaque; @@ -51,6 +400,53 @@ static void vfio_user_cb(void *opaque) * Functions called by main or CPU threads */ +/* + * Process incoming requests. + * + * The bus-specific callback has the form: + * request(opaque, msg) + * where 'opaque' was specified in vfio_user_set_handler + * and 'msg' is the inbound message. + * + * The callback is responsible for disposing of the message buffer, + * usually by re-using it when calling vfio_send_reply or vfio_send_error, + * both of which free their message buffer when the reply is sent. + * + * If the callback uses a new buffer, it needs to free the old one. + */ +static void vfio_user_request(void *opaque) +{ + VFIOUserProxy *proxy = opaque; + VFIOUserMsgQ new, free; + VFIOUserMsg *msg, *m1; + + /* reap all incoming */ + QTAILQ_INIT(&new); + WITH_QEMU_LOCK_GUARD(&proxy->lock) { + QTAILQ_FOREACH_SAFE(msg, &proxy->incoming, next, m1) { + QTAILQ_REMOVE(&proxy->incoming, msg, next); + QTAILQ_INSERT_TAIL(&new, msg, next); + } + } + + /* process list */ + QTAILQ_INIT(&free); + QTAILQ_FOREACH_SAFE(msg, &new, next, m1) { + QTAILQ_REMOVE(&new, msg, next); + trace_vfio_user_recv_request(msg->hdr->command); + proxy->request(proxy->req_arg, msg); + QTAILQ_INSERT_HEAD(&free, msg, next); + } + + /* free list */ + WITH_QEMU_LOCK_GUARD(&proxy->lock) { + QTAILQ_FOREACH_SAFE(msg, &free, next, m1) { + vfio_user_recycle(proxy, msg); + } + } +} + + static QLIST_HEAD(, VFIOUserProxy) vfio_user_sockets = QLIST_HEAD_INITIALIZER(vfio_user_sockets); @@ -89,6 +485,7 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp) } proxy->ctx = iothread_get_aio_context(vfio_user_iothread); + proxy->req_bh = qemu_bh_new(vfio_user_request, proxy); QTAILQ_INIT(&proxy->outgoing); QTAILQ_INIT(&proxy->incoming); @@ -99,6 +496,18 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp) return proxy; } +void vfio_user_set_handler(VFIODevice *vbasedev, + void (*handler)(void *opaque, VFIOUserMsg *msg), + void *req_arg) +{ + VFIOUserProxy *proxy = vbasedev->proxy; + + proxy->request = handler; + proxy->req_arg = req_arg; + qio_channel_set_aio_fd_handler(proxy->ioc, proxy->ctx, + vfio_user_recv, NULL, NULL, proxy); +} + void vfio_user_disconnect(VFIOUserProxy *proxy) { VFIOUserMsg *r1, *r2; @@ -114,6 +523,8 @@ void vfio_user_disconnect(VFIOUserProxy *proxy) } object_unref(OBJECT(proxy->ioc)); proxy->ioc = NULL; + qemu_bh_delete(proxy->req_bh); + proxy->req_bh = NULL; proxy->state = VFIO_PROXY_CLOSING; QTAILQ_FOREACH_SAFE(r1, &proxy->outgoing, next, r2) { diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h index a9bce82239..ff553cad9d 100644 --- a/hw/vfio-user/proxy.h +++ b/hw/vfio-user/proxy.h @@ -12,6 +12,9 @@ #include "io/channel.h" #include "io/channel-socket.h" +#include "qemu/sockets.h" +#include "hw/vfio-user/protocol.h" + typedef struct { int send_fds; int recv_fds; @@ -28,6 +31,7 @@ enum msg_type { typedef struct VFIOUserMsg { QTAILQ_ENTRY(VFIOUserMsg) next; + VFIOUserHdr *hdr; VFIOUserFDs *fds; uint32_t rsize; uint32_t id; @@ -67,13 +71,20 @@ typedef struct VFIOUserProxy { VFIOUserMsgQ incoming; VFIOUserMsgQ outgoing; VFIOUserMsg *last_nowait; + VFIOUserMsg *part_recv; + size_t recv_left; enum proxy_state state; } VFIOUserProxy; /* VFIOProxy flags */ #define VFIO_PROXY_CLIENT 0x1 +typedef struct VFIODevice VFIODevice; + VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp); void vfio_user_disconnect(VFIOUserProxy *proxy); +void vfio_user_set_handler(VFIODevice *vbasedev, + void (*handler)(void *opaque, VFIOUserMsg *msg), + void *reqarg); #endif /* VFIO_USER_PROXY_H */ diff --git a/hw/vfio-user/trace-events b/hw/vfio-user/trace-events new file mode 100644 index 0000000000..ddeb9f4b2f --- /dev/null +++ b/hw/vfio-user/trace-events @@ -0,0 +1,8 @@ +# See docs/devel/tracing.rst for syntax documentation. +# +# SPDX-License-Identifier: GPL-2.0-or-later + +# common.c +vfio_user_recv_hdr(const char *name, uint16_t id, uint16_t cmd, uint32_t size, uint32_t flags) " (%s) id 0x%x cmd 0x%x size 0x%x flags 0x%x" +vfio_user_recv_read(uint16_t id, int read) " id 0x%x read 0x%x" +vfio_user_recv_request(uint16_t cmd) " command 0x%x" diff --git a/hw/vfio-user/trace.h b/hw/vfio-user/trace.h new file mode 100644 index 0000000000..9cf02d9506 --- /dev/null +++ b/hw/vfio-user/trace.h @@ -0,0 +1,4 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "trace/trace-hw_vfio_user.h" diff --git a/meson.build b/meson.build index 4676908dbb..dbc97bfdf7 100644 --- a/meson.build +++ b/meson.build @@ -3683,6 +3683,7 @@ if have_system 'hw/ufs', 'hw/usb', 'hw/vfio', + 'hw/vfio-user', 'hw/virtio', 'hw/vmapple', 'hw/watchdog', From 36227628d824f563fda95f9344176ca7263c7eaf Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:29:56 +0100 Subject: [PATCH 1677/2760] vfio-user: implement message send infrastructure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add plumbing for sending vfio-user messages on the control socket. Add initial version negotation on connection. Originally-by: John Johnson Signed-off-by: Jagannathan Raman Signed-off-by: Elena Ufimtseva Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-5-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 20 +- hw/vfio-user/protocol.h | 62 +++++ hw/vfio-user/proxy.c | 515 ++++++++++++++++++++++++++++++++++++++ hw/vfio-user/proxy.h | 9 + hw/vfio-user/trace-events | 2 + 5 files changed, 606 insertions(+), 2 deletions(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index bad2829f5c..61f525cf4a 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -20,6 +20,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserPCIDevice, VFIO_USER_PCI) struct VFIOUserPCIDevice { VFIOPCIDevice device; SocketAddress *socket; + bool send_queued; /* all sends are queued */ }; /* @@ -92,6 +93,16 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) vbasedev->proxy = proxy; vfio_user_set_handler(vbasedev, vfio_user_pci_process_req, vdev); + vbasedev->name = g_strdup_printf("vfio-user:%s", sock_name); + + if (udev->send_queued) { + proxy->flags |= VFIO_PROXY_FORCE_QUEUED; + } + + if (!vfio_user_validate_version(proxy, errp)) { + goto error; + } + /* * vfio-user devices are effectively mdevs (don't use a host iommu). */ @@ -101,9 +112,13 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) if (!vfio_device_attach_by_iommu_type(TYPE_VFIO_IOMMU_USER, vbasedev->name, vbasedev, as, errp)) { - error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->name); - return; + goto error; } + + return; + +error: + error_prepend(errp, VFIO_MSG_PREFIX, vdev->vbasedev.name); } static void vfio_user_instance_init(Object *obj) @@ -153,6 +168,7 @@ static const Property vfio_user_pci_dev_properties[] = { sub_vendor_id, PCI_ANY_ID), DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice, sub_device_id, PCI_ANY_ID), + DEFINE_PROP_BOOL("x-send-queued", VFIOUserPCIDevice, send_queued, false), }; static void vfio_user_pci_set_socket(Object *obj, Visitor *v, const char *name, diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h index 4ddfb5f222..2d52d0fb10 100644 --- a/hw/vfio-user/protocol.h +++ b/hw/vfio-user/protocol.h @@ -50,4 +50,66 @@ enum vfio_user_command { #define VFIO_USER_NO_REPLY 0x10 #define VFIO_USER_ERROR 0x20 + +/* + * VFIO_USER_VERSION + */ +typedef struct { + VFIOUserHdr hdr; + uint16_t major; + uint16_t minor; + char capabilities[]; +} VFIOUserVersion; + +#define VFIO_USER_MAJOR_VER 0 +#define VFIO_USER_MINOR_VER 0 + +#define VFIO_USER_CAP "capabilities" + +/* "capabilities" members */ +#define VFIO_USER_CAP_MAX_FDS "max_msg_fds" +#define VFIO_USER_CAP_MAX_XFER "max_data_xfer_size" +#define VFIO_USER_CAP_PGSIZES "pgsizes" +#define VFIO_USER_CAP_MAP_MAX "max_dma_maps" +#define VFIO_USER_CAP_MIGR "migration" + +/* "migration" members */ +#define VFIO_USER_CAP_PGSIZE "pgsize" +#define VFIO_USER_CAP_MAX_BITMAP "max_bitmap_size" + +/* + * Max FDs mainly comes into play when a device supports multiple interrupts + * where each ones uses an eventfd to inject it into the guest. + * It is clamped by the the number of FDs the qio channel supports in a + * single message. + */ +#define VFIO_USER_DEF_MAX_FDS 8 +#define VFIO_USER_MAX_MAX_FDS 16 + +/* + * Max transfer limits the amount of data in region and DMA messages. + * Region R/W will be very small (limited by how much a single instruction + * can process) so just use a reasonable limit here. + */ +#define VFIO_USER_DEF_MAX_XFER (1024 * 1024) +#define VFIO_USER_MAX_MAX_XFER (64 * 1024 * 1024) + +/* + * Default pagesizes supported is 4k. + */ +#define VFIO_USER_DEF_PGSIZE 4096 + +/* + * Default max number of DMA mappings is stolen from the + * linux kernel "dma_entry_limit" + */ +#define VFIO_USER_DEF_MAP_MAX 65535 + +/* + * Default max bitmap size is also take from the linux kernel, + * where usage of signed ints limits the VA range to 2^31 bytes. + * Dividing that by the number of bits per byte yields 256MB + */ +#define VFIO_USER_DEF_MAX_BITMAP (256 * 1024 * 1024) + #endif /* VFIO_USER_PROTOCOL_H */ diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index 349ea2b27c..874142e9e5 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -13,11 +13,15 @@ #include "hw/vfio-user/proxy.h" #include "hw/vfio-user/trace.h" #include "qapi/error.h" +#include "qobject/qdict.h" +#include "qobject/qjson.h" +#include "qobject/qnum.h" #include "qemu/error-report.h" #include "qemu/lockable.h" #include "qemu/main-loop.h" #include "system/iothread.h" +static int wait_time = 5000; /* wait up to 5 sec for busy servers */ static IOThread *vfio_user_iothread; static void vfio_user_shutdown(VFIOUserProxy *proxy); @@ -27,9 +31,12 @@ static VFIOUserFDs *vfio_user_getfds(int numfds); static void vfio_user_recycle(VFIOUserProxy *proxy, VFIOUserMsg *msg); static void vfio_user_recv(void *opaque); +static void vfio_user_send(void *opaque); static void vfio_user_cb(void *opaque); static void vfio_user_request(void *opaque); +static void vfio_user_request_msg(VFIOUserHdr *hdr, uint16_t cmd, + uint32_t size, uint32_t flags); static inline void vfio_user_set_error(VFIOUserHdr *hdr, uint32_t err) { @@ -48,6 +55,41 @@ static void vfio_user_shutdown(VFIOUserProxy *proxy) proxy->ctx, NULL, NULL); } +/* + * Same return values as qio_channel_writev_full(): + * + * QIO_CHANNEL_ERR_BLOCK: *errp not set + * -1: *errp will be populated + * otherwise: bytes written + */ +static ssize_t vfio_user_send_qio(VFIOUserProxy *proxy, VFIOUserMsg *msg, + Error **errp) +{ + VFIOUserFDs *fds = msg->fds; + struct iovec iov = { + .iov_base = msg->hdr, + .iov_len = msg->hdr->size, + }; + size_t numfds = 0; + int *fdp = NULL; + ssize_t ret; + + if (fds != NULL && fds->send_fds != 0) { + numfds = fds->send_fds; + fdp = fds->fds; + } + + ret = qio_channel_writev_full(proxy->ioc, &iov, 1, fdp, numfds, 0, errp); + + if (ret == -1) { + vfio_user_set_error(msg->hdr, EIO); + vfio_user_shutdown(proxy); + } + trace_vfio_user_send_write(msg->hdr->id, ret); + + return ret; +} + static VFIOUserMsg *vfio_user_getmsg(VFIOUserProxy *proxy, VFIOUserHdr *hdr, VFIOUserFDs *fds) { @@ -88,6 +130,7 @@ static void vfio_user_recycle(VFIOUserProxy *proxy, VFIOUserMsg *msg) msg->hdr = NULL; msg->fds = NULL; msg->complete = false; + msg->pending = false; QTAILQ_INSERT_HEAD(&proxy->free, msg, next); } @@ -385,6 +428,62 @@ static void vfio_user_recv(void *opaque) } } +/* + * Send a single message, same return semantics as vfio_user_send_qio(). + * + * Sent async messages are freed, others are moved to pending queue. + */ +static ssize_t vfio_user_send_one(VFIOUserProxy *proxy, Error **errp) +{ + VFIOUserMsg *msg; + ssize_t ret; + + msg = QTAILQ_FIRST(&proxy->outgoing); + ret = vfio_user_send_qio(proxy, msg, errp); + if (ret < 0) { + return ret; + } + + QTAILQ_REMOVE(&proxy->outgoing, msg, next); + if (msg->type == VFIO_MSG_ASYNC) { + vfio_user_recycle(proxy, msg); + } else { + QTAILQ_INSERT_TAIL(&proxy->pending, msg, next); + msg->pending = true; + } + + return ret; +} + +/* + * Send messages from outgoing queue when the socket buffer has space. + * If we deplete 'outgoing', remove ourselves from the poll list. + */ +static void vfio_user_send(void *opaque) +{ + VFIOUserProxy *proxy = opaque; + + QEMU_LOCK_GUARD(&proxy->lock); + + if (proxy->state == VFIO_PROXY_CONNECTED) { + while (!QTAILQ_EMPTY(&proxy->outgoing)) { + Error *local_err = NULL; + int ret; + + ret = vfio_user_send_one(proxy, &local_err); + + if (ret == QIO_CHANNEL_ERR_BLOCK) { + return; + } else if (ret == -1) { + error_report_err(local_err); + return; + } + } + qio_channel_set_aio_fd_handler(proxy->ioc, proxy->ctx, + vfio_user_recv, NULL, NULL, proxy); + } +} + static void vfio_user_cb(void *opaque) { VFIOUserProxy *proxy = opaque; @@ -446,6 +545,128 @@ static void vfio_user_request(void *opaque) } } +/* + * Messages are queued onto the proxy's outgoing list. + * + * It handles 3 types of messages: + * + * async messages - replies and posted writes + * + * There will be no reply from the server, so message + * buffers are freed after they're sent. + * + * nowait messages - map/unmap during address space transactions + * + * These are also sent async, but a reply is expected so that + * vfio_wait_reqs() can wait for the youngest nowait request. + * They transition from the outgoing list to the pending list + * when sent, and are freed when the reply is received. + * + * wait messages - all other requests + * + * The reply to these messages is waited for by their caller. + * They also transition from outgoing to pending when sent, but + * the message buffer is returned to the caller with the reply + * contents. The caller is responsible for freeing these messages. + * + * As an optimization, if the outgoing list and the socket send + * buffer are empty, the message is sent inline instead of being + * added to the outgoing list. The rest of the transitions are + * unchanged. + */ +static bool vfio_user_send_queued(VFIOUserProxy *proxy, VFIOUserMsg *msg, + Error **errp) +{ + int ret; + + /* + * Unsent outgoing msgs - add to tail + */ + if (!QTAILQ_EMPTY(&proxy->outgoing)) { + QTAILQ_INSERT_TAIL(&proxy->outgoing, msg, next); + return true; + } + + /* + * Try inline - if blocked, queue it and kick send poller + */ + if (proxy->flags & VFIO_PROXY_FORCE_QUEUED) { + ret = QIO_CHANNEL_ERR_BLOCK; + } else { + ret = vfio_user_send_qio(proxy, msg, errp); + } + + if (ret == QIO_CHANNEL_ERR_BLOCK) { + QTAILQ_INSERT_HEAD(&proxy->outgoing, msg, next); + qio_channel_set_aio_fd_handler(proxy->ioc, proxy->ctx, + vfio_user_recv, proxy->ctx, + vfio_user_send, proxy); + return true; + } + if (ret == -1) { + return false; + } + + /* + * Sent - free async, add others to pending + */ + if (msg->type == VFIO_MSG_ASYNC) { + vfio_user_recycle(proxy, msg); + } else { + QTAILQ_INSERT_TAIL(&proxy->pending, msg, next); + msg->pending = true; + } + + return true; +} + +/* + * Returns false if we did not successfully receive a reply message, in which + * case @errp will be populated. + * + * In either case, the caller must free @hdr and @fds if needed. + */ +static bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, int rsize, Error **errp) +{ + VFIOUserMsg *msg; + bool ok = false; + + if (hdr->flags & VFIO_USER_NO_REPLY) { + error_setg_errno(errp, EINVAL, "%s on NO_REPLY message", __func__); + return false; + } + + qemu_mutex_lock(&proxy->lock); + + msg = vfio_user_getmsg(proxy, hdr, fds); + msg->id = hdr->id; + msg->rsize = rsize ? rsize : hdr->size; + msg->type = VFIO_MSG_WAIT; + + ok = vfio_user_send_queued(proxy, msg, errp); + + if (ok) { + while (!msg->complete) { + if (!qemu_cond_timedwait(&msg->cv, &proxy->lock, wait_time)) { + VFIOUserMsgQ *list; + + list = msg->pending ? &proxy->pending : &proxy->outgoing; + QTAILQ_REMOVE(list, msg, next); + error_setg_errno(errp, ETIMEDOUT, + "timed out waiting for reply"); + ok = false; + break; + } + } + } + + vfio_user_recycle(proxy, msg); + + qemu_mutex_unlock(&proxy->lock); + + return ok; +} static QLIST_HEAD(, VFIOUserProxy) vfio_user_sockets = QLIST_HEAD_INITIALIZER(vfio_user_sockets); @@ -474,6 +695,15 @@ VFIOUserProxy *vfio_user_connect_dev(SocketAddress *addr, Error **errp) proxy = g_malloc0(sizeof(VFIOUserProxy)); proxy->sockname = g_strdup_printf("unix:%s", sockname); proxy->ioc = ioc; + + /* init defaults */ + proxy->max_xfer_size = VFIO_USER_DEF_MAX_XFER; + proxy->max_send_fds = VFIO_USER_DEF_MAX_FDS; + proxy->max_dma = VFIO_USER_DEF_MAP_MAX; + proxy->dma_pgsizes = VFIO_USER_DEF_PGSIZE; + proxy->max_bitmap = VFIO_USER_DEF_MAX_BITMAP; + proxy->migr_pgsize = VFIO_USER_DEF_PGSIZE; + proxy->flags = VFIO_PROXY_CLIENT; proxy->state = VFIO_PROXY_CONNECTED; @@ -571,3 +801,288 @@ void vfio_user_disconnect(VFIOUserProxy *proxy) g_free(proxy->sockname); g_free(proxy); } + +static void vfio_user_request_msg(VFIOUserHdr *hdr, uint16_t cmd, + uint32_t size, uint32_t flags) +{ + static uint16_t next_id; + + hdr->id = qatomic_fetch_inc(&next_id); + hdr->command = cmd; + hdr->size = size; + hdr->flags = (flags & ~VFIO_USER_TYPE) | VFIO_USER_REQUEST; + hdr->error_reply = 0; +} + +struct cap_entry { + const char *name; + bool (*check)(VFIOUserProxy *proxy, QObject *qobj, Error **errp); +}; + +static bool caps_parse(VFIOUserProxy *proxy, QDict *qdict, + struct cap_entry caps[], Error **errp) +{ + QObject *qobj; + struct cap_entry *p; + + for (p = caps; p->name != NULL; p++) { + qobj = qdict_get(qdict, p->name); + if (qobj != NULL) { + if (!p->check(proxy, qobj, errp)) { + return false; + } + qdict_del(qdict, p->name); + } + } + + /* warning, for now */ + if (qdict_size(qdict) != 0) { + warn_report("spurious capabilities"); + } + return true; +} + +static bool check_migr_pgsize(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QNum *qn = qobject_to(QNum, qobj); + uint64_t pgsize; + + if (qn == NULL || !qnum_get_try_uint(qn, &pgsize)) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_PGSIZE); + return false; + } + + /* must be larger than default */ + if (pgsize & (VFIO_USER_DEF_PGSIZE - 1)) { + error_setg(errp, "pgsize 0x%"PRIx64" too small", pgsize); + return false; + } + + proxy->migr_pgsize = pgsize; + return true; +} + +static bool check_bitmap(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QNum *qn = qobject_to(QNum, qobj); + uint64_t bitmap_size; + + if (qn == NULL || !qnum_get_try_uint(qn, &bitmap_size)) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_MAX_BITMAP); + return false; + } + + /* can only lower it */ + if (bitmap_size > VFIO_USER_DEF_MAX_BITMAP) { + error_setg(errp, "%s too large", VFIO_USER_CAP_MAX_BITMAP); + return false; + } + + proxy->max_bitmap = bitmap_size; + return true; +} + +static struct cap_entry caps_migr[] = { + { VFIO_USER_CAP_PGSIZE, check_migr_pgsize }, + { VFIO_USER_CAP_MAX_BITMAP, check_bitmap }, + { NULL } +}; + +static bool check_max_fds(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QNum *qn = qobject_to(QNum, qobj); + uint64_t max_send_fds; + + if (qn == NULL || !qnum_get_try_uint(qn, &max_send_fds) || + max_send_fds > VFIO_USER_MAX_MAX_FDS) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_MAX_FDS); + return false; + } + proxy->max_send_fds = max_send_fds; + return true; +} + +static bool check_max_xfer(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QNum *qn = qobject_to(QNum, qobj); + uint64_t max_xfer_size; + + if (qn == NULL || !qnum_get_try_uint(qn, &max_xfer_size) || + max_xfer_size > VFIO_USER_MAX_MAX_XFER) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_MAX_XFER); + return false; + } + proxy->max_xfer_size = max_xfer_size; + return true; +} + +static bool check_pgsizes(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QNum *qn = qobject_to(QNum, qobj); + uint64_t pgsizes; + + if (qn == NULL || !qnum_get_try_uint(qn, &pgsizes)) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_PGSIZES); + return false; + } + + /* must be larger than default */ + if (pgsizes & (VFIO_USER_DEF_PGSIZE - 1)) { + error_setg(errp, "pgsize 0x%"PRIx64" too small", pgsizes); + return false; + } + + proxy->dma_pgsizes = pgsizes; + return true; +} + +static bool check_max_dma(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QNum *qn = qobject_to(QNum, qobj); + uint64_t max_dma; + + if (qn == NULL || !qnum_get_try_uint(qn, &max_dma)) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_MAP_MAX); + return false; + } + + /* can only lower it */ + if (max_dma > VFIO_USER_DEF_MAP_MAX) { + error_setg(errp, "%s too large", VFIO_USER_CAP_MAP_MAX); + return false; + } + + proxy->max_dma = max_dma; + return true; +} + +static bool check_migr(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QDict *qdict = qobject_to(QDict, qobj); + + if (qdict == NULL) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_MAX_FDS); + return true; + } + return caps_parse(proxy, qdict, caps_migr, errp); +} + +static struct cap_entry caps_cap[] = { + { VFIO_USER_CAP_MAX_FDS, check_max_fds }, + { VFIO_USER_CAP_MAX_XFER, check_max_xfer }, + { VFIO_USER_CAP_PGSIZES, check_pgsizes }, + { VFIO_USER_CAP_MAP_MAX, check_max_dma }, + { VFIO_USER_CAP_MIGR, check_migr }, + { NULL } +}; + +static bool check_cap(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QDict *qdict = qobject_to(QDict, qobj); + + if (qdict == NULL) { + error_setg(errp, "malformed %s", VFIO_USER_CAP); + return false; + } + return caps_parse(proxy, qdict, caps_cap, errp); +} + +static struct cap_entry ver_0_0[] = { + { VFIO_USER_CAP, check_cap }, + { NULL } +}; + +static bool caps_check(VFIOUserProxy *proxy, int minor, const char *caps, + Error **errp) +{ + QObject *qobj; + QDict *qdict; + bool ret; + + qobj = qobject_from_json(caps, NULL); + if (qobj == NULL) { + error_setg(errp, "malformed capabilities %s", caps); + return false; + } + qdict = qobject_to(QDict, qobj); + if (qdict == NULL) { + error_setg(errp, "capabilities %s not an object", caps); + qobject_unref(qobj); + return false; + } + ret = caps_parse(proxy, qdict, ver_0_0, errp); + + qobject_unref(qobj); + return ret; +} + +static GString *caps_json(void) +{ + QDict *dict = qdict_new(); + QDict *capdict = qdict_new(); + QDict *migdict = qdict_new(); + GString *str; + + qdict_put_int(migdict, VFIO_USER_CAP_PGSIZE, VFIO_USER_DEF_PGSIZE); + qdict_put_int(migdict, VFIO_USER_CAP_MAX_BITMAP, VFIO_USER_DEF_MAX_BITMAP); + qdict_put_obj(capdict, VFIO_USER_CAP_MIGR, QOBJECT(migdict)); + + qdict_put_int(capdict, VFIO_USER_CAP_MAX_FDS, VFIO_USER_MAX_MAX_FDS); + qdict_put_int(capdict, VFIO_USER_CAP_MAX_XFER, VFIO_USER_DEF_MAX_XFER); + qdict_put_int(capdict, VFIO_USER_CAP_PGSIZES, VFIO_USER_DEF_PGSIZE); + qdict_put_int(capdict, VFIO_USER_CAP_MAP_MAX, VFIO_USER_DEF_MAP_MAX); + + qdict_put_obj(dict, VFIO_USER_CAP, QOBJECT(capdict)); + + str = qobject_to_json(QOBJECT(dict)); + qobject_unref(dict); + return str; +} + +bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp) +{ + g_autofree VFIOUserVersion *msgp = NULL; + GString *caps; + char *reply; + int size, caplen; + + caps = caps_json(); + caplen = caps->len + 1; + size = sizeof(*msgp) + caplen; + msgp = g_malloc0(size); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_VERSION, size, 0); + msgp->major = VFIO_USER_MAJOR_VER; + msgp->minor = VFIO_USER_MINOR_VER; + memcpy(&msgp->capabilities, caps->str, caplen); + g_string_free(caps, true); + trace_vfio_user_version(msgp->major, msgp->minor, msgp->capabilities); + + if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, errp)) { + return false; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + error_setg_errno(errp, msgp->hdr.error_reply, "version reply"); + return false; + } + + if (msgp->major != VFIO_USER_MAJOR_VER || + msgp->minor > VFIO_USER_MINOR_VER) { + error_setg(errp, "incompatible server version"); + return false; + } + + reply = msgp->capabilities; + if (reply[msgp->hdr.size - sizeof(*msgp) - 1] != '\0') { + error_setg(errp, "corrupt version reply"); + return false; + } + + if (!caps_check(proxy, msgp->minor, reply, errp)) { + return false; + } + + trace_vfio_user_version(msgp->major, msgp->minor, msgp->capabilities); + return true; +} diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h index ff553cad9d..5bc890a0f5 100644 --- a/hw/vfio-user/proxy.h +++ b/hw/vfio-user/proxy.h @@ -37,6 +37,7 @@ typedef struct VFIOUserMsg { uint32_t id; QemuCond cv; bool complete; + bool pending; enum msg_type type; } VFIOUserMsg; @@ -56,6 +57,12 @@ typedef struct VFIOUserProxy { struct QIOChannel *ioc; void (*request)(void *opaque, VFIOUserMsg *msg); void *req_arg; + uint64_t max_xfer_size; + uint64_t max_send_fds; + uint64_t max_dma; + uint64_t dma_pgsizes; + uint64_t max_bitmap; + uint64_t migr_pgsize; int flags; QemuCond close_cv; AioContext *ctx; @@ -78,6 +85,7 @@ typedef struct VFIOUserProxy { /* VFIOProxy flags */ #define VFIO_PROXY_CLIENT 0x1 +#define VFIO_PROXY_FORCE_QUEUED 0x4 typedef struct VFIODevice VFIODevice; @@ -86,5 +94,6 @@ void vfio_user_disconnect(VFIOUserProxy *proxy); void vfio_user_set_handler(VFIODevice *vbasedev, void (*handler)(void *opaque, VFIOUserMsg *msg), void *reqarg); +bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp); #endif /* VFIO_USER_PROXY_H */ diff --git a/hw/vfio-user/trace-events b/hw/vfio-user/trace-events index ddeb9f4b2f..a965c7b1f2 100644 --- a/hw/vfio-user/trace-events +++ b/hw/vfio-user/trace-events @@ -6,3 +6,5 @@ vfio_user_recv_hdr(const char *name, uint16_t id, uint16_t cmd, uint32_t size, uint32_t flags) " (%s) id 0x%x cmd 0x%x size 0x%x flags 0x%x" vfio_user_recv_read(uint16_t id, int read) " id 0x%x read 0x%x" vfio_user_recv_request(uint16_t cmd) " command 0x%x" +vfio_user_send_write(uint16_t id, int wrote) " id 0x%x wrote 0x%x" +vfio_user_version(uint16_t major, uint16_t minor, const char *caps) " major %d minor %d caps: %s" From 3bdb738b734c77f93f93f8119c8f6ba8a9c5947c Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:29:57 +0100 Subject: [PATCH 1678/2760] vfio-user: implement VFIO_USER_DEVICE_GET_INFO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for getting basic device information. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-6-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.c | 8 +++++- hw/vfio-user/device.c | 55 +++++++++++++++++++++++++++++++++++++++ hw/vfio-user/device.h | 20 ++++++++++++++ hw/vfio-user/meson.build | 1 + hw/vfio-user/protocol.h | 12 +++++++++ hw/vfio-user/proxy.c | 10 +++---- hw/vfio-user/proxy.h | 7 +++++ hw/vfio-user/trace-events | 1 + 8 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 hw/vfio-user/device.c create mode 100644 hw/vfio-user/device.h diff --git a/hw/vfio-user/container.c b/hw/vfio-user/container.c index 2367332177..f5bfd54316 100644 --- a/hw/vfio-user/container.c +++ b/hw/vfio-user/container.c @@ -11,6 +11,7 @@ #include "qemu/osdep.h" #include "hw/vfio-user/container.h" +#include "hw/vfio-user/device.h" #include "hw/vfio/vfio-cpr.h" #include "hw/vfio/vfio-device.h" #include "hw/vfio/vfio-listener.h" @@ -140,7 +141,12 @@ static void vfio_user_container_disconnect(VFIOUserContainer *container) static bool vfio_user_device_get(VFIOUserContainer *container, VFIODevice *vbasedev, Error **errp) { - struct vfio_device_info info = { 0 }; + struct vfio_device_info info = { .argsz = sizeof(info) }; + + + if (!vfio_user_get_device_info(vbasedev->proxy, &info, errp)) { + return false; + } vbasedev->fd = -1; diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c new file mode 100644 index 0000000000..4212fefd44 --- /dev/null +++ b/hw/vfio-user/device.c @@ -0,0 +1,55 @@ +/* + * vfio protocol over a UNIX socket device handling. + * + * Copyright © 2018, 2021 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "qemu/error-report.h" + +#include "hw/vfio-user/device.h" +#include "hw/vfio-user/trace.h" + +/* + * These are to defend against a malign server trying + * to force us to run out of memory. + */ +#define VFIO_USER_MAX_REGIONS 100 +#define VFIO_USER_MAX_IRQS 50 + +bool vfio_user_get_device_info(VFIOUserProxy *proxy, + struct vfio_device_info *info, Error **errp) +{ + VFIOUserDeviceInfo msg; + uint32_t argsz = sizeof(msg) - sizeof(msg.hdr); + + memset(&msg, 0, sizeof(msg)); + vfio_user_request_msg(&msg.hdr, VFIO_USER_DEVICE_GET_INFO, sizeof(msg), 0); + msg.argsz = argsz; + + if (!vfio_user_send_wait(proxy, &msg.hdr, NULL, 0, errp)) { + return false; + } + + if (msg.hdr.flags & VFIO_USER_ERROR) { + error_setg_errno(errp, -msg.hdr.error_reply, + "VFIO_USER_DEVICE_GET_INFO failed"); + return false; + } + + trace_vfio_user_get_info(msg.num_regions, msg.num_irqs); + + memcpy(info, &msg.argsz, argsz); + + /* defend against a malicious server */ + if (info->num_regions > VFIO_USER_MAX_REGIONS || + info->num_irqs > VFIO_USER_MAX_IRQS) { + error_setg_errno(errp, EINVAL, "invalid reply"); + return false; + } + + return true; +} diff --git a/hw/vfio-user/device.h b/hw/vfio-user/device.h new file mode 100644 index 0000000000..ef3f71ee69 --- /dev/null +++ b/hw/vfio-user/device.h @@ -0,0 +1,20 @@ +#ifndef VFIO_USER_DEVICE_H +#define VFIO_USER_DEVICE_H + +/* + * vfio protocol over a UNIX socket device handling. + * + * Copyright © 2018, 2021 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "linux/vfio.h" + +#include "hw/vfio-user/proxy.h" + +bool vfio_user_get_device_info(VFIOUserProxy *proxy, + struct vfio_device_info *info, Error **errp); + +#endif /* VFIO_USER_DEVICE_H */ diff --git a/hw/vfio-user/meson.build b/hw/vfio-user/meson.build index 9e85a8ea51..2ed0ae5b1d 100644 --- a/hw/vfio-user/meson.build +++ b/hw/vfio-user/meson.build @@ -3,6 +3,7 @@ vfio_user_ss = ss.source_set() vfio_user_ss.add(files( 'container.c', + 'device.c', 'pci.c', 'proxy.c', )) diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h index 2d52d0fb10..e0bba68739 100644 --- a/hw/vfio-user/protocol.h +++ b/hw/vfio-user/protocol.h @@ -112,4 +112,16 @@ typedef struct { */ #define VFIO_USER_DEF_MAX_BITMAP (256 * 1024 * 1024) +/* + * VFIO_USER_DEVICE_GET_INFO + * imported from struct vfio_device_info + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + uint32_t num_regions; + uint32_t num_irqs; +} VFIOUserDeviceInfo; + #endif /* VFIO_USER_PROTOCOL_H */ diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index 874142e9e5..aed7b22e2a 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -35,8 +35,6 @@ static void vfio_user_send(void *opaque); static void vfio_user_cb(void *opaque); static void vfio_user_request(void *opaque); -static void vfio_user_request_msg(VFIOUserHdr *hdr, uint16_t cmd, - uint32_t size, uint32_t flags); static inline void vfio_user_set_error(VFIOUserHdr *hdr, uint32_t err) { @@ -626,8 +624,8 @@ static bool vfio_user_send_queued(VFIOUserProxy *proxy, VFIOUserMsg *msg, * * In either case, the caller must free @hdr and @fds if needed. */ -static bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, - VFIOUserFDs *fds, int rsize, Error **errp) +bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, int rsize, Error **errp) { VFIOUserMsg *msg; bool ok = false; @@ -802,8 +800,8 @@ void vfio_user_disconnect(VFIOUserProxy *proxy) g_free(proxy); } -static void vfio_user_request_msg(VFIOUserHdr *hdr, uint16_t cmd, - uint32_t size, uint32_t flags) +void vfio_user_request_msg(VFIOUserHdr *hdr, uint16_t cmd, + uint32_t size, uint32_t flags) { static uint16_t next_id; diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h index 5bc890a0f5..837b02a8c4 100644 --- a/hw/vfio-user/proxy.h +++ b/hw/vfio-user/proxy.h @@ -12,7 +12,9 @@ #include "io/channel.h" #include "io/channel-socket.h" +#include "qemu/queue.h" #include "qemu/sockets.h" +#include "qemu/thread.h" #include "hw/vfio-user/protocol.h" typedef struct { @@ -96,4 +98,9 @@ void vfio_user_set_handler(VFIODevice *vbasedev, void *reqarg); bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp); +void vfio_user_request_msg(VFIOUserHdr *hdr, uint16_t cmd, + uint32_t size, uint32_t flags); +bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, int rsize, Error **errp); + #endif /* VFIO_USER_PROXY_H */ diff --git a/hw/vfio-user/trace-events b/hw/vfio-user/trace-events index a965c7b1f2..b7312d6d59 100644 --- a/hw/vfio-user/trace-events +++ b/hw/vfio-user/trace-events @@ -8,3 +8,4 @@ vfio_user_recv_read(uint16_t id, int read) " id 0x%x read 0x%x" vfio_user_recv_request(uint16_t cmd) " command 0x%x" vfio_user_send_write(uint16_t id, int wrote) " id 0x%x wrote 0x%x" vfio_user_version(uint16_t major, uint16_t minor, const char *caps) " major %d minor %d caps: %s" +vfio_user_get_info(uint32_t nregions, uint32_t nirqs) " #regions %d #irqs %d" From 667866d66620e9f545eb3bd4f065d9ab81bda727 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:29:58 +0100 Subject: [PATCH 1679/2760] vfio-user: implement VFIO_USER_DEVICE_GET_REGION_INFO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for getting region info for vfio-user. As vfio-user has one fd per region, enable ->use_region_fds. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-7-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 74 +++++++++++++++++++++++++++++++++++++++ hw/vfio-user/device.h | 2 ++ hw/vfio-user/pci.c | 11 ++++++ hw/vfio-user/protocol.h | 14 ++++++++ hw/vfio-user/proxy.h | 1 + hw/vfio-user/trace-events | 1 + 6 files changed, 103 insertions(+) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 4212fefd44..d90232a08f 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -53,3 +53,77 @@ bool vfio_user_get_device_info(VFIOUserProxy *proxy, return true; } + +static int vfio_user_get_region_info(VFIOUserProxy *proxy, + struct vfio_region_info *info, + VFIOUserFDs *fds) +{ + g_autofree VFIOUserRegionInfo *msgp = NULL; + Error *local_err = NULL; + uint32_t size; + + /* data returned can be larger than vfio_region_info */ + if (info->argsz < sizeof(*info)) { + error_printf("vfio_user_get_region_info argsz too small\n"); + return -E2BIG; + } + if (fds != NULL && fds->send_fds != 0) { + error_printf("vfio_user_get_region_info can't send FDs\n"); + return -EINVAL; + } + + size = info->argsz + sizeof(VFIOUserHdr); + msgp = g_malloc0(size); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_GET_REGION_INFO, + sizeof(*msgp), 0); + msgp->argsz = info->argsz; + msgp->index = info->index; + + if (!vfio_user_send_wait(proxy, &msgp->hdr, fds, size, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + return -msgp->hdr.error_reply; + } + trace_vfio_user_get_region_info(msgp->index, msgp->flags, msgp->size); + + memcpy(info, &msgp->argsz, info->argsz); + return 0; +} + + +static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev, + struct vfio_region_info *info, + int *fd) +{ + VFIOUserFDs fds = { 0, 1, fd}; + int ret; + + if (info->index > vbasedev->num_regions) { + return -EINVAL; + } + + ret = vfio_user_get_region_info(vbasedev->proxy, info, &fds); + if (ret) { + return ret; + } + + /* cap_offset in valid area */ + if ((info->flags & VFIO_REGION_INFO_FLAG_CAPS) && + (info->cap_offset < sizeof(*info) || info->cap_offset > info->argsz)) { + return -EINVAL; + } + + return 0; +} + +/* + * Socket-based io_ops + */ +VFIODeviceIOOps vfio_user_device_io_ops_sock = { + .get_region_info = vfio_user_device_io_get_region_info, +}; diff --git a/hw/vfio-user/device.h b/hw/vfio-user/device.h index ef3f71ee69..619c0f3140 100644 --- a/hw/vfio-user/device.h +++ b/hw/vfio-user/device.h @@ -17,4 +17,6 @@ bool vfio_user_get_device_info(VFIOUserProxy *proxy, struct vfio_device_info *info, Error **errp); +extern VFIODeviceIOOps vfio_user_device_io_ops_sock; + #endif /* VFIO_USER_DEVICE_H */ diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index 61f525cf4a..d704e3d390 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -12,6 +12,7 @@ #include "hw/qdev-properties.h" #include "hw/vfio/pci.h" +#include "hw/vfio-user/device.h" #include "hw/vfio-user/proxy.h" #define TYPE_VFIO_USER_PCI "vfio-user-pci" @@ -103,11 +104,21 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) goto error; } + /* + * Use socket-based device I/O instead of vfio kernel driver. + */ + vbasedev->io_ops = &vfio_user_device_io_ops_sock; + /* * vfio-user devices are effectively mdevs (don't use a host iommu). */ vbasedev->mdev = true; + /* + * Enable per-region fds. + */ + vbasedev->use_region_fds = true; + as = pci_device_iommu_address_space(pdev); if (!vfio_device_attach_by_iommu_type(TYPE_VFIO_IOMMU_USER, vbasedev->name, vbasedev, diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h index e0bba68739..db88f5fcb1 100644 --- a/hw/vfio-user/protocol.h +++ b/hw/vfio-user/protocol.h @@ -124,4 +124,18 @@ typedef struct { uint32_t num_irqs; } VFIOUserDeviceInfo; +/* + * VFIO_USER_DEVICE_GET_REGION_INFO + * imported from struct vfio_region_info + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + uint32_t index; + uint32_t cap_offset; + uint64_t size; + uint64_t offset; +} VFIOUserRegionInfo; + #endif /* VFIO_USER_PROTOCOL_H */ diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h index 837b02a8c4..ba1c33aba8 100644 --- a/hw/vfio-user/proxy.h +++ b/hw/vfio-user/proxy.h @@ -15,6 +15,7 @@ #include "qemu/queue.h" #include "qemu/sockets.h" #include "qemu/thread.h" +#include "hw/vfio/vfio-device.h" #include "hw/vfio-user/protocol.h" typedef struct { diff --git a/hw/vfio-user/trace-events b/hw/vfio-user/trace-events index b7312d6d59..ef3f14c74d 100644 --- a/hw/vfio-user/trace-events +++ b/hw/vfio-user/trace-events @@ -9,3 +9,4 @@ vfio_user_recv_request(uint16_t cmd) " command 0x%x" vfio_user_send_write(uint16_t id, int wrote) " id 0x%x wrote 0x%x" vfio_user_version(uint16_t major, uint16_t minor, const char *caps) " major %d minor %d caps: %s" vfio_user_get_info(uint32_t nregions, uint32_t nirqs) " #regions %d #irqs %d" +vfio_user_get_region_info(uint32_t index, uint32_t flags, uint64_t size) " index %d flags 0x%x size 0x%"PRIx64 From 1ed50fcb6a8b99312458b9653de2d1d5acfd7506 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:29:59 +0100 Subject: [PATCH 1680/2760] vfio-user: implement VFIO_USER_REGION_READ/WRITE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-8-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 79 +++++++++++++++++++++++++++++++++++++++ hw/vfio-user/protocol.h | 12 ++++++ hw/vfio-user/trace-events | 1 + 3 files changed, 92 insertions(+) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index d90232a08f..4ef6a2d850 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -121,9 +121,88 @@ static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev, return 0; } +static int vfio_user_device_io_region_read(VFIODevice *vbasedev, uint8_t index, + off_t off, uint32_t count, + void *data) +{ + g_autofree VFIOUserRegionRW *msgp = NULL; + VFIOUserProxy *proxy = vbasedev->proxy; + int size = sizeof(*msgp) + count; + Error *local_err = NULL; + + if (count > proxy->max_xfer_size) { + return -EINVAL; + } + + msgp = g_malloc0(size); + vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_READ, sizeof(*msgp), 0); + msgp->offset = off; + msgp->region = index; + msgp->count = count; + trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count); + + if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, size, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + return -msgp->hdr.error_reply; + } else if (msgp->count > count) { + return -E2BIG; + } else { + memcpy(data, &msgp->data, msgp->count); + } + + return msgp->count; +} + +static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index, + off_t off, unsigned count, + void *data, bool post) +{ + g_autofree VFIOUserRegionRW *msgp = NULL; + VFIOUserProxy *proxy = vbasedev->proxy; + int size = sizeof(*msgp) + count; + Error *local_err = NULL; + int ret; + + if (count > proxy->max_xfer_size) { + return -EINVAL; + } + + msgp = g_malloc0(size); + vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_WRITE, size, 0); + msgp->offset = off; + msgp->region = index; + msgp->count = count; + memcpy(&msgp->data, data, count); + trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count); + + /* Ignore post: all writes are synchronous/non-posted. */ + + if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + ret = -msgp->hdr.error_reply; + } else { + ret = count; + } + + return ret; +} + /* * Socket-based io_ops */ VFIODeviceIOOps vfio_user_device_io_ops_sock = { .get_region_info = vfio_user_device_io_get_region_info, + .region_read = vfio_user_device_io_region_read, + .region_write = vfio_user_device_io_region_write, + }; diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h index db88f5fcb1..0cd32ad71a 100644 --- a/hw/vfio-user/protocol.h +++ b/hw/vfio-user/protocol.h @@ -138,4 +138,16 @@ typedef struct { uint64_t offset; } VFIOUserRegionInfo; +/* + * VFIO_USER_REGION_READ + * VFIO_USER_REGION_WRITE + */ +typedef struct { + VFIOUserHdr hdr; + uint64_t offset; + uint32_t region; + uint32_t count; + char data[]; +} VFIOUserRegionRW; + #endif /* VFIO_USER_PROTOCOL_H */ diff --git a/hw/vfio-user/trace-events b/hw/vfio-user/trace-events index ef3f14c74d..733313cd1f 100644 --- a/hw/vfio-user/trace-events +++ b/hw/vfio-user/trace-events @@ -10,3 +10,4 @@ vfio_user_send_write(uint16_t id, int wrote) " id 0x%x wrote 0x%x" vfio_user_version(uint16_t major, uint16_t minor, const char *caps) " major %d minor %d caps: %s" vfio_user_get_info(uint32_t nregions, uint32_t nirqs) " #regions %d #irqs %d" vfio_user_get_region_info(uint32_t index, uint32_t flags, uint64_t size) " index %d flags 0x%x size 0x%"PRIx64 +vfio_user_region_rw(uint32_t region, uint64_t off, uint32_t count) " region %d offset 0x%"PRIx64" count %d" From 692e0ec50c2f38a7952f7e7c4c375c77b141f6b2 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:30:00 +0100 Subject: [PATCH 1681/2760] vfio-user: set up PCI in vfio_user_pci_realize() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-use PCI setup functions from hw/vfio/pci.c to realize the vfio-user PCI device. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-9-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index d704e3d390..b49f42b980 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -126,10 +126,39 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) goto error; } + if (!vfio_pci_populate_device(vdev, errp)) { + goto error; + } + + if (!vfio_pci_config_setup(vdev, errp)) { + goto error; + } + + /* + * vfio_pci_config_setup will have registered the device's BARs + * and setup any MSIX BARs, so errors after it succeeds must + * use out_teardown + */ + + if (!vfio_pci_add_capabilities(vdev, errp)) { + goto out_teardown; + } + + if (!vfio_pci_interrupt_setup(vdev, errp)) { + goto out_teardown; + } + + vfio_pci_register_err_notifier(vdev); + vfio_pci_register_req_notifier(vdev); + return; +out_teardown: + vfio_pci_teardown_msi(vdev); + vfio_pci_bars_exit(vdev); error: error_prepend(errp, VFIO_MSG_PREFIX, vdev->vbasedev.name); + vfio_pci_put_device(vdev); } static void vfio_user_instance_init(Object *obj) From ca1add1696dd53f905c2a17f36159d54e9b6f527 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:30:01 +0100 Subject: [PATCH 1682/2760] vfio-user: implement VFIO_USER_DEVICE_GET/SET_IRQ* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IRQ setup uses the same semantics as the traditional vfio path, but we need to share the corresponding file descriptors with the server as necessary. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-10-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 138 ++++++++++++++++++++++++++++++++++++++ hw/vfio-user/protocol.h | 25 +++++++ hw/vfio-user/trace-events | 2 + 3 files changed, 165 insertions(+) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 4ef6a2d850..f01b3925c5 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -121,6 +121,142 @@ static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev, return 0; } +static int vfio_user_device_io_get_irq_info(VFIODevice *vbasedev, + struct vfio_irq_info *info) +{ + VFIOUserProxy *proxy = vbasedev->proxy; + Error *local_err = NULL; + VFIOUserIRQInfo msg; + + memset(&msg, 0, sizeof(msg)); + vfio_user_request_msg(&msg.hdr, VFIO_USER_DEVICE_GET_IRQ_INFO, + sizeof(msg), 0); + msg.argsz = info->argsz; + msg.index = info->index; + + if (!vfio_user_send_wait(proxy, &msg.hdr, NULL, 0, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + if (msg.hdr.flags & VFIO_USER_ERROR) { + return -msg.hdr.error_reply; + } + trace_vfio_user_get_irq_info(msg.index, msg.flags, msg.count); + + memcpy(info, &msg.argsz, sizeof(*info)); + return 0; +} + +static int irq_howmany(int *fdp, uint32_t cur, uint32_t max) +{ + int n = 0; + + if (fdp[cur] != -1) { + do { + n++; + } while (n < max && fdp[cur + n] != -1); + } else { + do { + n++; + } while (n < max && fdp[cur + n] == -1); + } + + return n; +} + +static int vfio_user_device_io_set_irqs(VFIODevice *vbasedev, + struct vfio_irq_set *irq) +{ + VFIOUserProxy *proxy = vbasedev->proxy; + g_autofree VFIOUserIRQSet *msgp = NULL; + uint32_t size, nfds, send_fds, sent_fds, max; + Error *local_err = NULL; + + if (irq->argsz < sizeof(*irq)) { + error_printf("vfio_user_set_irqs argsz too small\n"); + return -EINVAL; + } + + /* + * Handle simple case + */ + if ((irq->flags & VFIO_IRQ_SET_DATA_EVENTFD) == 0) { + size = sizeof(VFIOUserHdr) + irq->argsz; + msgp = g_malloc0(size); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS, size, 0); + msgp->argsz = irq->argsz; + msgp->flags = irq->flags; + msgp->index = irq->index; + msgp->start = irq->start; + msgp->count = irq->count; + trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count, + msgp->flags); + + if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + return -msgp->hdr.error_reply; + } + + return 0; + } + + /* + * Calculate the number of FDs to send + * and adjust argsz + */ + nfds = (irq->argsz - sizeof(*irq)) / sizeof(int); + irq->argsz = sizeof(*irq); + msgp = g_malloc0(sizeof(*msgp)); + /* + * Send in chunks if over max_send_fds + */ + for (sent_fds = 0; nfds > sent_fds; sent_fds += send_fds) { + VFIOUserFDs *arg_fds, loop_fds; + + /* must send all valid FDs or all invalid FDs in single msg */ + max = nfds - sent_fds; + if (max > proxy->max_send_fds) { + max = proxy->max_send_fds; + } + send_fds = irq_howmany((int *)irq->data, sent_fds, max); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_DEVICE_SET_IRQS, + sizeof(*msgp), 0); + msgp->argsz = irq->argsz; + msgp->flags = irq->flags; + msgp->index = irq->index; + msgp->start = irq->start + sent_fds; + msgp->count = send_fds; + trace_vfio_user_set_irqs(msgp->index, msgp->start, msgp->count, + msgp->flags); + + loop_fds.send_fds = send_fds; + loop_fds.recv_fds = 0; + loop_fds.fds = (int *)irq->data + sent_fds; + arg_fds = loop_fds.fds[0] != -1 ? &loop_fds : NULL; + + if (!vfio_user_send_wait(proxy, &msgp->hdr, arg_fds, 0, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + return -msgp->hdr.error_reply; + } + } + + return 0; +} + static int vfio_user_device_io_region_read(VFIODevice *vbasedev, uint8_t index, off_t off, uint32_t count, void *data) @@ -202,6 +338,8 @@ static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index, */ VFIODeviceIOOps vfio_user_device_io_ops_sock = { .get_region_info = vfio_user_device_io_get_region_info, + .get_irq_info = vfio_user_device_io_get_irq_info, + .set_irqs = vfio_user_device_io_set_irqs, .region_read = vfio_user_device_io_region_read, .region_write = vfio_user_device_io_region_write, diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h index 0cd32ad71a..48144b2c33 100644 --- a/hw/vfio-user/protocol.h +++ b/hw/vfio-user/protocol.h @@ -138,6 +138,31 @@ typedef struct { uint64_t offset; } VFIOUserRegionInfo; +/* + * VFIO_USER_DEVICE_GET_IRQ_INFO + * imported from struct vfio_irq_info + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + uint32_t index; + uint32_t count; +} VFIOUserIRQInfo; + +/* + * VFIO_USER_DEVICE_SET_IRQS + * imported from struct vfio_irq_set + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + uint32_t index; + uint32_t start; + uint32_t count; +} VFIOUserIRQSet; + /* * VFIO_USER_REGION_READ * VFIO_USER_REGION_WRITE diff --git a/hw/vfio-user/trace-events b/hw/vfio-user/trace-events index 733313cd1f..aa8f3c3d4d 100644 --- a/hw/vfio-user/trace-events +++ b/hw/vfio-user/trace-events @@ -11,3 +11,5 @@ vfio_user_version(uint16_t major, uint16_t minor, const char *caps) " major %d m vfio_user_get_info(uint32_t nregions, uint32_t nirqs) " #regions %d #irqs %d" vfio_user_get_region_info(uint32_t index, uint32_t flags, uint64_t size) " index %d flags 0x%x size 0x%"PRIx64 vfio_user_region_rw(uint32_t region, uint64_t off, uint32_t count) " region %d offset 0x%"PRIx64" count %d" +vfio_user_get_irq_info(uint32_t index, uint32_t flags, uint32_t count) " index %d flags 0x%x count %d" +vfio_user_set_irqs(uint32_t index, uint32_t start, uint32_t count, uint32_t flags) " index %d start %d count %d flags 0x%x" From 777e45c7b914583ca83ace7601d9cf90ddf1f3fd Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:30:02 +0100 Subject: [PATCH 1683/2760] vfio-user: forward MSI-X PBA BAR accesses to server MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For vfio-user, the server holds the pending IRQ state; set up an I/O region for the MSI-X PBA so we can ask the server for this state on a PBA read. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-11-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++ hw/vfio/pci.h | 1 + 2 files changed, 65 insertions(+) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index b49f42b980..c0f00f15b1 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -24,6 +24,62 @@ struct VFIOUserPCIDevice { bool send_queued; /* all sends are queued */ }; +/* + * The server maintains the device's pending interrupts, + * via its MSIX table and PBA, so we treat these accesses + * like PCI config space and forward them. + */ +static uint64_t vfio_user_pba_read(void *opaque, hwaddr addr, + unsigned size) +{ + VFIOPCIDevice *vdev = opaque; + VFIORegion *region = &vdev->bars[vdev->msix->pba_bar].region; + uint64_t data; + + /* server copy is what matters */ + data = vfio_region_read(region, addr + vdev->msix->pba_offset, size); + return data; +} + +static void vfio_user_pba_write(void *opaque, hwaddr addr, + uint64_t data, unsigned size) +{ + /* dropped */ +} + +static const MemoryRegionOps vfio_user_pba_ops = { + .read = vfio_user_pba_read, + .write = vfio_user_pba_write, + .endianness = DEVICE_LITTLE_ENDIAN, +}; + +static void vfio_user_msix_setup(VFIOPCIDevice *vdev) +{ + MemoryRegion *vfio_reg, *msix_reg, *pba_reg; + + pba_reg = g_new0(MemoryRegion, 1); + vdev->msix->pba_region = pba_reg; + + vfio_reg = vdev->bars[vdev->msix->pba_bar].mr; + msix_reg = &vdev->pdev.msix_pba_mmio; + memory_region_init_io(pba_reg, OBJECT(vdev), &vfio_user_pba_ops, vdev, + "VFIO MSIX PBA", int128_get64(msix_reg->size)); + memory_region_add_subregion_overlap(vfio_reg, vdev->msix->pba_offset, + pba_reg, 1); +} + +static void vfio_user_msix_teardown(VFIOPCIDevice *vdev) +{ + MemoryRegion *mr, *sub; + + mr = vdev->bars[vdev->msix->pba_bar].mr; + sub = vdev->msix->pba_region; + memory_region_del_subregion(mr, sub); + + g_free(vdev->msix->pba_region); + vdev->msix->pba_region = NULL; +} + /* * Incoming request message callback. * @@ -144,6 +200,10 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) goto out_teardown; } + if (vdev->msix != NULL) { + vfio_user_msix_setup(vdev); + } + if (!vfio_pci_interrupt_setup(vdev, errp)) { goto out_teardown; } @@ -192,6 +252,10 @@ static void vfio_user_instance_finalize(Object *obj) VFIOPCIDevice *vdev = VFIO_PCI_BASE(obj); VFIODevice *vbasedev = &vdev->vbasedev; + if (vdev->msix != NULL) { + vfio_user_msix_teardown(vdev); + } + vfio_pci_put_device(vdev); if (vbasedev->proxy != NULL) { diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index d3dc2274a9..5ba7330b27 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -116,6 +116,7 @@ typedef struct VFIOMSIXInfo { uint32_t pba_offset; unsigned long *pending; bool noresize; + MemoryRegion *pba_region; } VFIOMSIXInfo; /* From 52ce9c35f8e364e5823fc13f23929eb597bb69ac Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:30:03 +0100 Subject: [PATCH 1684/2760] vfio-user: set up container access to the proxy MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The user container will shortly need access to the underlying vfio-user proxy; set this up. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-12-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.c | 43 +++++++++++++++++++++++++++++++--------- hw/vfio-user/container.h | 2 ++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/hw/vfio-user/container.c b/hw/vfio-user/container.c index f5bfd54316..b4a5a840b0 100644 --- a/hw/vfio-user/container.c +++ b/hw/vfio-user/container.c @@ -49,15 +49,28 @@ static int vfio_user_query_dirty_bitmap(const VFIOContainerBase *bcontainer, static bool vfio_user_setup(VFIOContainerBase *bcontainer, Error **errp) { - error_setg_errno(errp, ENOTSUP, "Not supported"); - return -ENOTSUP; + VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, + bcontainer); + + assert(container->proxy->dma_pgsizes != 0); + bcontainer->pgsizes = container->proxy->dma_pgsizes; + bcontainer->dma_max_mappings = container->proxy->max_dma; + + /* No live migration support yet. */ + bcontainer->dirty_pages_supported = false; + bcontainer->max_dirty_bitmap_size = container->proxy->max_bitmap; + bcontainer->dirty_pgsizes = container->proxy->migr_pgsize; + + return true; } -static VFIOUserContainer *vfio_user_create_container(Error **errp) +static VFIOUserContainer *vfio_user_create_container(VFIODevice *vbasedev, + Error **errp) { VFIOUserContainer *container; container = VFIO_IOMMU_USER(object_new(TYPE_VFIO_IOMMU_USER)); + container->proxy = vbasedev->proxy; return container; } @@ -65,16 +78,18 @@ static VFIOUserContainer *vfio_user_create_container(Error **errp) * Try to mirror vfio_container_connect() as much as possible. */ static VFIOUserContainer * -vfio_user_container_connect(AddressSpace *as, Error **errp) +vfio_user_container_connect(AddressSpace *as, VFIODevice *vbasedev, + Error **errp) { VFIOContainerBase *bcontainer; VFIOUserContainer *container; VFIOAddressSpace *space; VFIOIOMMUClass *vioc; + int ret; space = vfio_address_space_get(as); - container = vfio_user_create_container(errp); + container = vfio_user_create_container(vbasedev, errp); if (!container) { goto put_space_exit; } @@ -85,11 +100,17 @@ vfio_user_container_connect(AddressSpace *as, Error **errp) goto free_container_exit; } + ret = ram_block_uncoordinated_discard_disable(true); + if (ret) { + error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken"); + goto unregister_container_exit; + } + vioc = VFIO_IOMMU_GET_CLASS(bcontainer); assert(vioc->setup); if (!vioc->setup(bcontainer, errp)) { - goto unregister_container_exit; + goto enable_discards_exit; } vfio_address_space_insert(space, bcontainer); @@ -108,6 +129,9 @@ vfio_user_container_connect(AddressSpace *as, Error **errp) vioc->release(bcontainer); } +enable_discards_exit: + ram_block_uncoordinated_discard_disable(false); + unregister_container_exit: vfio_cpr_unregister_container(bcontainer); @@ -124,14 +148,15 @@ static void vfio_user_container_disconnect(VFIOUserContainer *container) { VFIOContainerBase *bcontainer = &container->bcontainer; VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + VFIOAddressSpace *space = bcontainer->space; + + ram_block_uncoordinated_discard_disable(false); vfio_listener_unregister(bcontainer); if (vioc->release) { vioc->release(bcontainer); } - VFIOAddressSpace *space = bcontainer->space; - vfio_cpr_unregister_container(bcontainer); object_unref(container); @@ -163,7 +188,7 @@ static bool vfio_user_device_attach(const char *name, VFIODevice *vbasedev, { VFIOUserContainer *container; - container = vfio_user_container_connect(as, errp); + container = vfio_user_container_connect(as, vbasedev, errp); if (container == NULL) { error_prepend(errp, "failed to connect proxy"); return false; diff --git a/hw/vfio-user/container.h b/hw/vfio-user/container.h index e4a46d2c1b..2bb1fa1343 100644 --- a/hw/vfio-user/container.h +++ b/hw/vfio-user/container.h @@ -10,10 +10,12 @@ #include "qemu/osdep.h" #include "hw/vfio/vfio-container-base.h" +#include "hw/vfio-user/proxy.h" /* MMU container sub-class for vfio-user. */ typedef struct VFIOUserContainer { VFIOContainerBase bcontainer; + VFIOUserProxy *proxy; } VFIOUserContainer; OBJECT_DECLARE_SIMPLE_TYPE(VFIOUserContainer, VFIO_IOMMU_USER); From 019232358124e4f4b929e40fa23253de60eec73e Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:30:04 +0100 Subject: [PATCH 1685/2760] vfio-user: implement VFIO_USER_DEVICE_RESET MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hook this call up to the legacy reset handler for vfio-user-pci. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-13-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 18 ++++++++++++++++++ hw/vfio-user/device.h | 2 ++ hw/vfio-user/pci.c | 15 +++++++++++++++ 3 files changed, 35 insertions(+) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index f01b3925c5..3a118e7361 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -54,6 +54,24 @@ bool vfio_user_get_device_info(VFIOUserProxy *proxy, return true; } +void vfio_user_device_reset(VFIOUserProxy *proxy) +{ + Error *local_err = NULL; + VFIOUserHdr hdr; + + vfio_user_request_msg(&hdr, VFIO_USER_DEVICE_RESET, sizeof(hdr), 0); + + if (!vfio_user_send_wait(proxy, &hdr, NULL, 0, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return; + } + + if (hdr.flags & VFIO_USER_ERROR) { + error_printf("reset reply error %d\n", hdr.error_reply); + } +} + static int vfio_user_get_region_info(VFIOUserProxy *proxy, struct vfio_region_info *info, VFIOUserFDs *fds) diff --git a/hw/vfio-user/device.h b/hw/vfio-user/device.h index 619c0f3140..d183a3950e 100644 --- a/hw/vfio-user/device.h +++ b/hw/vfio-user/device.h @@ -17,6 +17,8 @@ bool vfio_user_get_device_info(VFIOUserProxy *proxy, struct vfio_device_info *info, Error **errp); +void vfio_user_device_reset(VFIOUserProxy *proxy); + extern VFIODeviceIOOps vfio_user_device_io_ops_sock; #endif /* VFIO_USER_DEVICE_H */ diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index c0f00f15b1..49d12763ab 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -263,6 +263,20 @@ static void vfio_user_instance_finalize(Object *obj) } } +static void vfio_user_pci_reset(DeviceState *dev) +{ + VFIOPCIDevice *vdev = VFIO_PCI_BASE(dev); + VFIODevice *vbasedev = &vdev->vbasedev; + + vfio_pci_pre_reset(vdev); + + if (vbasedev->reset_works) { + vfio_user_device_reset(vbasedev->proxy); + } + + vfio_pci_post_reset(vdev); +} + static const Property vfio_user_pci_dev_properties[] = { DEFINE_PROP_UINT32("x-pci-vendor-id", VFIOPCIDevice, vendor_id, PCI_ANY_ID), @@ -310,6 +324,7 @@ static void vfio_user_pci_dev_class_init(ObjectClass *klass, const void *data) DeviceClass *dc = DEVICE_CLASS(klass); PCIDeviceClass *pdc = PCI_DEVICE_CLASS(klass); + device_class_set_legacy_reset(dc, vfio_user_pci_reset); device_class_set_props(dc, vfio_user_pci_dev_properties); object_class_property_add(klass, "socket", "SocketAddress", NULL, From 18e899e63dd9d05e567c081023ad7fc5b2c5dc9a Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:30:05 +0100 Subject: [PATCH 1686/2760] vfio-user: implement VFIO_USER_DMA_MAP/UNMAP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the vfio-user container gets mapping updates, share them with the vfio-user by sending a message; this can include the region fd, allowing the server to directly mmap() the region as needed. For performance, we only wait for the message responses when we're doing with a series of updates via the listener_commit() callback. Originally-by: John Johnson Signed-off-by: Jagannathan Raman Signed-off-by: Elena Ufimtseva Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-14-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.c | 135 +++++++++++++++++++++++++++++++++++++- hw/vfio-user/protocol.h | 32 +++++++++ hw/vfio-user/proxy.c | 84 +++++++++++++++++++++++- hw/vfio-user/proxy.h | 6 ++ hw/vfio-user/trace-events | 4 ++ 5 files changed, 257 insertions(+), 4 deletions(-) diff --git a/hw/vfio-user/container.c b/hw/vfio-user/container.c index b4a5a840b0..3133fef177 100644 --- a/hw/vfio-user/container.c +++ b/hw/vfio-user/container.c @@ -12,23 +12,152 @@ #include "hw/vfio-user/container.h" #include "hw/vfio-user/device.h" +#include "hw/vfio-user/trace.h" #include "hw/vfio/vfio-cpr.h" #include "hw/vfio/vfio-device.h" #include "hw/vfio/vfio-listener.h" #include "qapi/error.h" +/* + * When DMA space is the physical address space, the region add/del listeners + * will fire during memory update transactions. These depend on BQL being held, + * so do any resulting map/demap ops async while keeping BQL. + */ +static void vfio_user_listener_begin(VFIOContainerBase *bcontainer) +{ + VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, + bcontainer); + + container->proxy->async_ops = true; +} + +static void vfio_user_listener_commit(VFIOContainerBase *bcontainer) +{ + VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, + bcontainer); + + /* wait here for any async requests sent during the transaction */ + container->proxy->async_ops = false; + vfio_user_wait_reqs(container->proxy); +} + static int vfio_user_dma_unmap(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all) { - return -ENOTSUP; + VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, + bcontainer); + Error *local_err = NULL; + int ret = 0; + + VFIOUserDMAUnmap *msgp = g_malloc(sizeof(*msgp)); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_DMA_UNMAP, sizeof(*msgp), 0); + msgp->argsz = sizeof(struct vfio_iommu_type1_dma_unmap); + msgp->flags = unmap_all ? VFIO_DMA_UNMAP_FLAG_ALL : 0; + msgp->iova = iova; + msgp->size = size; + trace_vfio_user_dma_unmap(msgp->iova, msgp->size, msgp->flags, + container->proxy->async_ops); + + if (container->proxy->async_ops) { + if (!vfio_user_send_nowait(container->proxy, &msgp->hdr, NULL, + 0, &local_err)) { + error_report_err(local_err); + ret = -EFAULT; + } else { + ret = 0; + } + } else { + if (!vfio_user_send_wait(container->proxy, &msgp->hdr, NULL, + 0, &local_err)) { + error_report_err(local_err); + ret = -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + ret = -msgp->hdr.error_reply; + } + + g_free(msgp); + } + + return ret; } static int vfio_user_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mrp) { - return -ENOTSUP; + VFIOUserContainer *container = container_of(bcontainer, VFIOUserContainer, + bcontainer); + int fd = memory_region_get_fd(mrp); + Error *local_err = NULL; + int ret; + + VFIOUserFDs *fds = NULL; + VFIOUserDMAMap *msgp = g_malloc0(sizeof(*msgp)); + + vfio_user_request_msg(&msgp->hdr, VFIO_USER_DMA_MAP, sizeof(*msgp), 0); + msgp->argsz = sizeof(struct vfio_iommu_type1_dma_map); + msgp->flags = VFIO_DMA_MAP_FLAG_READ; + msgp->offset = 0; + msgp->iova = iova; + msgp->size = size; + + /* + * vaddr enters as a QEMU process address; make it either a file offset + * for mapped areas or leave as 0. + */ + if (fd != -1) { + msgp->offset = qemu_ram_block_host_offset(mrp->ram_block, vaddr); + } + + if (!readonly) { + msgp->flags |= VFIO_DMA_MAP_FLAG_WRITE; + } + + trace_vfio_user_dma_map(msgp->iova, msgp->size, msgp->offset, msgp->flags, + container->proxy->async_ops); + + /* + * The async_ops case sends without blocking. They're later waited for in + * vfio_send_wait_reqs. + */ + if (container->proxy->async_ops) { + /* can't use auto variable since we don't block */ + if (fd != -1) { + fds = vfio_user_getfds(1); + fds->send_fds = 1; + fds->fds[0] = fd; + } + + if (!vfio_user_send_nowait(container->proxy, &msgp->hdr, fds, + 0, &local_err)) { + error_report_err(local_err); + ret = -EFAULT; + } else { + ret = 0; + } + } else { + VFIOUserFDs local_fds = { 1, 0, &fd }; + + fds = fd != -1 ? &local_fds : NULL; + + if (!vfio_user_send_wait(container->proxy, &msgp->hdr, fds, + 0, &local_err)) { + error_report_err(local_err); + ret = -EFAULT; + } + + if (msgp->hdr.flags & VFIO_USER_ERROR) { + ret = -msgp->hdr.error_reply; + } + + g_free(msgp); + } + + return ret; } static int @@ -218,6 +347,8 @@ static void vfio_iommu_user_class_init(ObjectClass *klass, const void *data) VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); vioc->setup = vfio_user_setup; + vioc->listener_begin = vfio_user_listener_begin, + vioc->listener_commit = vfio_user_listener_commit, vioc->dma_map = vfio_user_dma_map; vioc->dma_unmap = vfio_user_dma_unmap; vioc->attach_device = vfio_user_device_attach; diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h index 48144b2c33..524f3d633a 100644 --- a/hw/vfio-user/protocol.h +++ b/hw/vfio-user/protocol.h @@ -112,6 +112,31 @@ typedef struct { */ #define VFIO_USER_DEF_MAX_BITMAP (256 * 1024 * 1024) +/* + * VFIO_USER_DMA_MAP + * imported from struct vfio_iommu_type1_dma_map + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + uint64_t offset; /* FD offset */ + uint64_t iova; + uint64_t size; +} VFIOUserDMAMap; + +/* + * VFIO_USER_DMA_UNMAP + * imported from struct vfio_iommu_type1_dma_unmap + */ +typedef struct { + VFIOUserHdr hdr; + uint32_t argsz; + uint32_t flags; + uint64_t iova; + uint64_t size; +} VFIOUserDMAUnmap; + /* * VFIO_USER_DEVICE_GET_INFO * imported from struct vfio_device_info @@ -175,4 +200,11 @@ typedef struct { char data[]; } VFIOUserRegionRW; +/*imported from struct vfio_bitmap */ +typedef struct { + uint64_t pgsize; + uint64_t size; + char data[]; +} VFIOUserBitmap; + #endif /* VFIO_USER_PROTOCOL_H */ diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index aed7b22e2a..c8ae8a59b4 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -27,7 +27,6 @@ static IOThread *vfio_user_iothread; static void vfio_user_shutdown(VFIOUserProxy *proxy); static VFIOUserMsg *vfio_user_getmsg(VFIOUserProxy *proxy, VFIOUserHdr *hdr, VFIOUserFDs *fds); -static VFIOUserFDs *vfio_user_getfds(int numfds); static void vfio_user_recycle(VFIOUserProxy *proxy, VFIOUserMsg *msg); static void vfio_user_recv(void *opaque); @@ -132,7 +131,7 @@ static void vfio_user_recycle(VFIOUserProxy *proxy, VFIOUserMsg *msg) QTAILQ_INSERT_HEAD(&proxy->free, msg, next); } -static VFIOUserFDs *vfio_user_getfds(int numfds) +VFIOUserFDs *vfio_user_getfds(int numfds) { VFIOUserFDs *fds = g_malloc0(sizeof(*fds) + (numfds * sizeof(int))); @@ -618,6 +617,43 @@ static bool vfio_user_send_queued(VFIOUserProxy *proxy, VFIOUserMsg *msg, return true; } +/* + * nowait send - vfio_wait_reqs() can wait for it later + * + * Returns false if we did not successfully receive a reply message, in which + * case @errp will be populated. + * + * In either case, ownership of @hdr and @fds is taken, and the caller must + * *not* free them itself. + */ +bool vfio_user_send_nowait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, int rsize, Error **errp) +{ + VFIOUserMsg *msg; + + QEMU_LOCK_GUARD(&proxy->lock); + + msg = vfio_user_getmsg(proxy, hdr, fds); + msg->id = hdr->id; + msg->rsize = rsize ? rsize : hdr->size; + msg->type = VFIO_MSG_NOWAIT; + + if (hdr->flags & VFIO_USER_NO_REPLY) { + error_setg_errno(errp, EINVAL, "%s on NO_REPLY message", __func__); + vfio_user_recycle(proxy, msg); + return false; + } + + if (!vfio_user_send_queued(proxy, msg, errp)) { + vfio_user_recycle(proxy, msg); + return false; + } + + proxy->last_nowait = msg; + + return true; +} + /* * Returns false if we did not successfully receive a reply message, in which * case @errp will be populated. @@ -666,6 +702,50 @@ bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, return ok; } +void vfio_user_wait_reqs(VFIOUserProxy *proxy) +{ + VFIOUserMsg *msg; + + /* + * Any DMA map/unmap requests sent in the middle + * of a memory region transaction were sent nowait. + * Wait for them here. + */ + qemu_mutex_lock(&proxy->lock); + if (proxy->last_nowait != NULL) { + /* + * Change type to WAIT to wait for reply + */ + msg = proxy->last_nowait; + msg->type = VFIO_MSG_WAIT; + proxy->last_nowait = NULL; + while (!msg->complete) { + if (!qemu_cond_timedwait(&msg->cv, &proxy->lock, wait_time)) { + VFIOUserMsgQ *list; + + list = msg->pending ? &proxy->pending : &proxy->outgoing; + QTAILQ_REMOVE(list, msg, next); + error_printf("vfio_wait_reqs - timed out\n"); + break; + } + } + + if (msg->hdr->flags & VFIO_USER_ERROR) { + error_printf("vfio_user_wait_reqs - error reply on async "); + error_printf("request: command %x error %s\n", msg->hdr->command, + strerror(msg->hdr->error_reply)); + } + + /* + * Change type back to NOWAIT to free + */ + msg->type = VFIO_MSG_NOWAIT; + vfio_user_recycle(proxy, msg); + } + + qemu_mutex_unlock(&proxy->lock); +} + static QLIST_HEAD(, VFIOUserProxy) vfio_user_sockets = QLIST_HEAD_INITIALIZER(vfio_user_sockets); diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h index ba1c33aba8..e2fc83ca3b 100644 --- a/hw/vfio-user/proxy.h +++ b/hw/vfio-user/proxy.h @@ -70,6 +70,7 @@ typedef struct VFIOUserProxy { QemuCond close_cv; AioContext *ctx; QEMUBH *req_bh; + bool async_ops; /* * above only changed when BQL is held @@ -99,9 +100,14 @@ void vfio_user_set_handler(VFIODevice *vbasedev, void *reqarg); bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp); +VFIOUserFDs *vfio_user_getfds(int numfds); + void vfio_user_request_msg(VFIOUserHdr *hdr, uint16_t cmd, uint32_t size, uint32_t flags); +void vfio_user_wait_reqs(VFIOUserProxy *proxy); bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, VFIOUserFDs *fds, int rsize, Error **errp); +bool vfio_user_send_nowait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, int rsize, Error **errp); #endif /* VFIO_USER_PROXY_H */ diff --git a/hw/vfio-user/trace-events b/hw/vfio-user/trace-events index aa8f3c3d4d..44dde020b3 100644 --- a/hw/vfio-user/trace-events +++ b/hw/vfio-user/trace-events @@ -13,3 +13,7 @@ vfio_user_get_region_info(uint32_t index, uint32_t flags, uint64_t size) " index vfio_user_region_rw(uint32_t region, uint64_t off, uint32_t count) " region %d offset 0x%"PRIx64" count %d" vfio_user_get_irq_info(uint32_t index, uint32_t flags, uint32_t count) " index %d flags 0x%x count %d" vfio_user_set_irqs(uint32_t index, uint32_t start, uint32_t count, uint32_t flags) " index %d start %d count %d flags 0x%x" + +# container.c +vfio_user_dma_map(uint64_t iova, uint64_t size, uint64_t off, uint32_t flags, bool async_ops) " iova 0x%"PRIx64" size 0x%"PRIx64" off 0x%"PRIx64" flags 0x%x async_ops %d" +vfio_user_dma_unmap(uint64_t iova, uint64_t size, uint32_t flags, bool async_ops) " iova 0x%"PRIx64" size 0x%"PRIx64" flags 0x%x async_ops %d" From c6ac52a4d8f7a7c03452454d36b60ac309f0b9ce Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:30:06 +0100 Subject: [PATCH 1687/2760] vfio-user: implement VFIO_USER_DMA_READ/WRITE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unlike most other messages, this is a server->client message, for when a server wants to do "DMA"; this is slow, so normally the server has memory directly mapped instead. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-15-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 111 ++++++++++++++++++++++++++++++++++++++++ hw/vfio-user/protocol.h | 13 ++++- hw/vfio-user/proxy.c | 97 +++++++++++++++++++++++++++++++++++ hw/vfio-user/proxy.h | 3 ++ 4 files changed, 223 insertions(+), 1 deletion(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index 49d12763ab..040660d197 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -9,6 +9,7 @@ #include #include "qemu/osdep.h" #include "qapi-visit-sockets.h" +#include "qemu/error-report.h" #include "hw/qdev-properties.h" #include "hw/vfio/pci.h" @@ -80,6 +81,95 @@ static void vfio_user_msix_teardown(VFIOPCIDevice *vdev) vdev->msix->pba_region = NULL; } +static void vfio_user_dma_read(VFIOPCIDevice *vdev, VFIOUserDMARW *msg) +{ + PCIDevice *pdev = &vdev->pdev; + VFIOUserProxy *proxy = vdev->vbasedev.proxy; + VFIOUserDMARW *res; + MemTxResult r; + size_t size; + + if (msg->hdr.size < sizeof(*msg)) { + vfio_user_send_error(proxy, &msg->hdr, EINVAL); + return; + } + if (msg->count > proxy->max_xfer_size) { + vfio_user_send_error(proxy, &msg->hdr, E2BIG); + return; + } + + /* switch to our own message buffer */ + size = msg->count + sizeof(VFIOUserDMARW); + res = g_malloc0(size); + memcpy(res, msg, sizeof(*res)); + g_free(msg); + + r = pci_dma_read(pdev, res->offset, &res->data, res->count); + + switch (r) { + case MEMTX_OK: + if (res->hdr.flags & VFIO_USER_NO_REPLY) { + g_free(res); + return; + } + vfio_user_send_reply(proxy, &res->hdr, size); + break; + case MEMTX_ERROR: + vfio_user_send_error(proxy, &res->hdr, EFAULT); + break; + case MEMTX_DECODE_ERROR: + vfio_user_send_error(proxy, &res->hdr, ENODEV); + break; + case MEMTX_ACCESS_ERROR: + vfio_user_send_error(proxy, &res->hdr, EPERM); + break; + default: + error_printf("vfio_user_dma_read unknown error %d\n", r); + vfio_user_send_error(vdev->vbasedev.proxy, &res->hdr, EINVAL); + } +} + +static void vfio_user_dma_write(VFIOPCIDevice *vdev, VFIOUserDMARW *msg) +{ + PCIDevice *pdev = &vdev->pdev; + VFIOUserProxy *proxy = vdev->vbasedev.proxy; + MemTxResult r; + + if (msg->hdr.size < sizeof(*msg)) { + vfio_user_send_error(proxy, &msg->hdr, EINVAL); + return; + } + /* make sure transfer count isn't larger than the message data */ + if (msg->count > msg->hdr.size - sizeof(*msg)) { + vfio_user_send_error(proxy, &msg->hdr, E2BIG); + return; + } + + r = pci_dma_write(pdev, msg->offset, &msg->data, msg->count); + + switch (r) { + case MEMTX_OK: + if ((msg->hdr.flags & VFIO_USER_NO_REPLY) == 0) { + vfio_user_send_reply(proxy, &msg->hdr, sizeof(msg->hdr)); + } else { + g_free(msg); + } + break; + case MEMTX_ERROR: + vfio_user_send_error(proxy, &msg->hdr, EFAULT); + break; + case MEMTX_DECODE_ERROR: + vfio_user_send_error(proxy, &msg->hdr, ENODEV); + break; + case MEMTX_ACCESS_ERROR: + vfio_user_send_error(proxy, &msg->hdr, EPERM); + break; + default: + error_printf("vfio_user_dma_write unknown error %d\n", r); + vfio_user_send_error(vdev->vbasedev.proxy, &msg->hdr, EINVAL); + } +} + /* * Incoming request message callback. * @@ -87,7 +177,28 @@ static void vfio_user_msix_teardown(VFIOPCIDevice *vdev) */ static void vfio_user_pci_process_req(void *opaque, VFIOUserMsg *msg) { + VFIOPCIDevice *vdev = opaque; + VFIOUserHdr *hdr = msg->hdr; + + /* no incoming PCI requests pass FDs */ + if (msg->fds != NULL) { + vfio_user_send_error(vdev->vbasedev.proxy, hdr, EINVAL); + vfio_user_putfds(msg); + return; + } + switch (hdr->command) { + case VFIO_USER_DMA_READ: + vfio_user_dma_read(vdev, (VFIOUserDMARW *)hdr); + break; + case VFIO_USER_DMA_WRITE: + vfio_user_dma_write(vdev, (VFIOUserDMARW *)hdr); + break; + default: + error_printf("vfio_user_pci_process_req unknown cmd %d\n", + hdr->command); + vfio_user_send_error(vdev->vbasedev.proxy, hdr, ENOSYS); + } } /* diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h index 524f3d633a..3e9d8e576b 100644 --- a/hw/vfio-user/protocol.h +++ b/hw/vfio-user/protocol.h @@ -200,7 +200,18 @@ typedef struct { char data[]; } VFIOUserRegionRW; -/*imported from struct vfio_bitmap */ +/* + * VFIO_USER_DMA_READ + * VFIO_USER_DMA_WRITE + */ +typedef struct { + VFIOUserHdr hdr; + uint64_t offset; + uint32_t count; + char data[]; +} VFIOUserDMARW; + +/* imported from struct vfio_bitmap */ typedef struct { uint64_t pgsize; uint64_t size; diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index c8ae8a59b4..cb93d9a660 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -347,6 +347,10 @@ static int vfio_user_recv_one(VFIOUserProxy *proxy, Error **errp) *msg->hdr = hdr; data = (char *)msg->hdr + sizeof(hdr); } else { + if (hdr.size > proxy->max_xfer_size + sizeof(VFIOUserDMARW)) { + error_setg(errp, "vfio_user_recv request larger than max"); + goto err; + } buf = g_malloc0(hdr.size); memcpy(buf, &hdr, sizeof(hdr)); data = buf + sizeof(hdr); @@ -702,6 +706,40 @@ bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, return ok; } +/* + * async send - msg can be queued, but will be freed when sent + * + * Returns false on failure, in which case @errp will be populated. + * + * In either case, ownership of @hdr and @fds is taken, and the caller must + * *not* free them itself. + */ +static bool vfio_user_send_async(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, Error **errp) +{ + VFIOUserMsg *msg; + + QEMU_LOCK_GUARD(&proxy->lock); + + msg = vfio_user_getmsg(proxy, hdr, fds); + msg->id = hdr->id; + msg->rsize = 0; + msg->type = VFIO_MSG_ASYNC; + + if (!(hdr->flags & (VFIO_USER_NO_REPLY | VFIO_USER_REPLY))) { + error_setg_errno(errp, EINVAL, "%s on sync message", __func__); + vfio_user_recycle(proxy, msg); + return false; + } + + if (!vfio_user_send_queued(proxy, msg, errp)) { + vfio_user_recycle(proxy, msg); + return false; + } + + return true; +} + void vfio_user_wait_reqs(VFIOUserProxy *proxy) { VFIOUserMsg *msg; @@ -746,6 +784,65 @@ void vfio_user_wait_reqs(VFIOUserProxy *proxy) qemu_mutex_unlock(&proxy->lock); } +/* + * Reply to an incoming request. + */ +void vfio_user_send_reply(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int size) +{ + Error *local_err = NULL; + + if (size < sizeof(VFIOUserHdr)) { + error_printf("%s: size too small", __func__); + g_free(hdr); + return; + } + + /* + * convert header to associated reply + */ + hdr->flags = VFIO_USER_REPLY; + hdr->size = size; + + if (!vfio_user_send_async(proxy, hdr, NULL, &local_err)) { + error_report_err(local_err); + } +} + +/* + * Send an error reply to an incoming request. + */ +void vfio_user_send_error(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int error) +{ + Error *local_err = NULL; + + /* + * convert header to associated reply + */ + hdr->flags = VFIO_USER_REPLY; + hdr->flags |= VFIO_USER_ERROR; + hdr->error_reply = error; + hdr->size = sizeof(*hdr); + + if (!vfio_user_send_async(proxy, hdr, NULL, &local_err)) { + error_report_err(local_err); + } +} + +/* + * Close FDs erroneously received in an incoming request. + */ +void vfio_user_putfds(VFIOUserMsg *msg) +{ + VFIOUserFDs *fds = msg->fds; + int i; + + for (i = 0; i < fds->recv_fds; i++) { + close(fds->fds[i]); + } + g_free(fds); + msg->fds = NULL; +} + static QLIST_HEAD(, VFIOUserProxy) vfio_user_sockets = QLIST_HEAD_INITIALIZER(vfio_user_sockets); diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h index e2fc83ca3b..39092c08c8 100644 --- a/hw/vfio-user/proxy.h +++ b/hw/vfio-user/proxy.h @@ -101,6 +101,7 @@ void vfio_user_set_handler(VFIODevice *vbasedev, bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp); VFIOUserFDs *vfio_user_getfds(int numfds); +void vfio_user_putfds(VFIOUserMsg *msg); void vfio_user_request_msg(VFIOUserHdr *hdr, uint16_t cmd, uint32_t size, uint32_t flags); @@ -109,5 +110,7 @@ bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, VFIOUserFDs *fds, int rsize, Error **errp); bool vfio_user_send_nowait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, VFIOUserFDs *fds, int rsize, Error **errp); +void vfio_user_send_reply(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int size); +void vfio_user_send_error(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int error); #endif /* VFIO_USER_PROXY_H */ From 3358d926addda99e9f29f57b40d6fd22d2c29472 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:30:07 +0100 Subject: [PATCH 1688/2760] vfio-user: add 'x-msg-timeout' option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By default, the vfio-user subsystem will wait 5 seconds for a message reply from the server. Add an option to allow this to be configurable. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-16-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/pci.c | 5 +++++ hw/vfio-user/proxy.c | 7 ++++--- hw/vfio-user/proxy.h | 1 + 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index 040660d197..f260bea490 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -23,6 +23,7 @@ struct VFIOUserPCIDevice { VFIOPCIDevice device; SocketAddress *socket; bool send_queued; /* all sends are queued */ + uint32_t wait_time; /* timeout for message replies */ }; /* @@ -267,6 +268,9 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) proxy->flags |= VFIO_PROXY_FORCE_QUEUED; } + /* user specified or 5 sec default */ + proxy->wait_time = udev->wait_time; + if (!vfio_user_validate_version(proxy, errp)) { goto error; } @@ -398,6 +402,7 @@ static const Property vfio_user_pci_dev_properties[] = { DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice, sub_device_id, PCI_ANY_ID), DEFINE_PROP_BOOL("x-send-queued", VFIOUserPCIDevice, send_queued, false), + DEFINE_PROP_UINT32("x-msg-timeout", VFIOUserPCIDevice, wait_time, 5000), }; static void vfio_user_pci_set_socket(Object *obj, Visitor *v, const char *name, diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index cb93d9a660..c3724ba212 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -21,7 +21,6 @@ #include "qemu/main-loop.h" #include "system/iothread.h" -static int wait_time = 5000; /* wait up to 5 sec for busy servers */ static IOThread *vfio_user_iothread; static void vfio_user_shutdown(VFIOUserProxy *proxy); @@ -686,7 +685,8 @@ bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, if (ok) { while (!msg->complete) { - if (!qemu_cond_timedwait(&msg->cv, &proxy->lock, wait_time)) { + if (!qemu_cond_timedwait(&msg->cv, &proxy->lock, + proxy->wait_time)) { VFIOUserMsgQ *list; list = msg->pending ? &proxy->pending : &proxy->outgoing; @@ -758,7 +758,8 @@ void vfio_user_wait_reqs(VFIOUserProxy *proxy) msg->type = VFIO_MSG_WAIT; proxy->last_nowait = NULL; while (!msg->complete) { - if (!qemu_cond_timedwait(&msg->cv, &proxy->lock, wait_time)) { + if (!qemu_cond_timedwait(&msg->cv, &proxy->lock, + proxy->wait_time)) { VFIOUserMsgQ *list; list = msg->pending ? &proxy->pending : &proxy->outgoing; diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h index 39092c08c8..6b29cd7cd3 100644 --- a/hw/vfio-user/proxy.h +++ b/hw/vfio-user/proxy.h @@ -67,6 +67,7 @@ typedef struct VFIOUserProxy { uint64_t max_bitmap; uint64_t migr_pgsize; int flags; + uint32_t wait_time; QemuCond close_cv; AioContext *ctx; QEMUBH *req_bh; From 98a906d9e5827b18c51d7d7485be2f21f8900cc7 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:30:08 +0100 Subject: [PATCH 1689/2760] vfio-user: support posted writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Support an asynchronous send of a vfio-user socket message (no wait for a reply) when the write is posted. This is only safe when no regions are mappable by the VM. Add an option to explicitly disable this as well. Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-17-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 45 +++++++++++++++++++++++++++++++++++++++---- hw/vfio-user/pci.c | 6 ++++++ hw/vfio-user/proxy.c | 12 ++++++++++-- hw/vfio-user/proxy.h | 6 ++++++ 4 files changed, 63 insertions(+), 6 deletions(-) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index 3a118e7361..aa07eac330 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -110,10 +110,21 @@ static int vfio_user_get_region_info(VFIOUserProxy *proxy, trace_vfio_user_get_region_info(msgp->index, msgp->flags, msgp->size); memcpy(info, &msgp->argsz, info->argsz); + + /* + * If at least one region is directly mapped into the VM, then we can no + * longer rely on the sequential nature of vfio-user request handling to + * ensure that posted writes are completed before a subsequent read. In this + * case, disable posted write support. This is a per-device property, not + * per-region. + */ + if (info->flags & VFIO_REGION_INFO_FLAG_MMAP) { + vfio_user_disable_posted_writes(proxy); + } + return 0; } - static int vfio_user_device_io_get_region_info(VFIODevice *vbasedev, struct vfio_region_info *info, int *fd) @@ -312,33 +323,58 @@ static int vfio_user_device_io_region_read(VFIODevice *vbasedev, uint8_t index, return msgp->count; } +/* + * If this is a posted write, and VFIO_PROXY_NO_POST is not set, then we are OK + * to send the write to the socket without waiting for the server's reply: + * a subsequent read (of any region) will not pass the posted write, as all + * messages are handled sequentially. + */ static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index, off_t off, unsigned count, void *data, bool post) { - g_autofree VFIOUserRegionRW *msgp = NULL; + VFIOUserRegionRW *msgp = NULL; VFIOUserProxy *proxy = vbasedev->proxy; int size = sizeof(*msgp) + count; Error *local_err = NULL; + int flags = 0; int ret; if (count > proxy->max_xfer_size) { return -EINVAL; } + if (proxy->flags & VFIO_PROXY_NO_POST) { + post = false; + } + + if (post) { + flags |= VFIO_USER_NO_REPLY; + } + msgp = g_malloc0(size); - vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_WRITE, size, 0); + vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_WRITE, size, flags); msgp->offset = off; msgp->region = index; msgp->count = count; memcpy(&msgp->data, data, count); trace_vfio_user_region_rw(msgp->region, msgp->offset, msgp->count); - /* Ignore post: all writes are synchronous/non-posted. */ + /* async send will free msg after it's sent */ + if (post) { + if (!vfio_user_send_async(proxy, &msgp->hdr, NULL, &local_err)) { + error_prepend(&local_err, "%s: ", __func__); + error_report_err(local_err); + return -EFAULT; + } + + return count; + } if (!vfio_user_send_wait(proxy, &msgp->hdr, NULL, 0, &local_err)) { error_prepend(&local_err, "%s: ", __func__); error_report_err(local_err); + g_free(msgp); return -EFAULT; } @@ -348,6 +384,7 @@ static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index, ret = count; } + g_free(msgp); return ret; } diff --git a/hw/vfio-user/pci.c b/hw/vfio-user/pci.c index f260bea490..be71c77729 100644 --- a/hw/vfio-user/pci.c +++ b/hw/vfio-user/pci.c @@ -24,6 +24,7 @@ struct VFIOUserPCIDevice { SocketAddress *socket; bool send_queued; /* all sends are queued */ uint32_t wait_time; /* timeout for message replies */ + bool no_post; /* all region writes are sync */ }; /* @@ -268,6 +269,10 @@ static void vfio_user_pci_realize(PCIDevice *pdev, Error **errp) proxy->flags |= VFIO_PROXY_FORCE_QUEUED; } + if (udev->no_post) { + proxy->flags |= VFIO_PROXY_NO_POST; + } + /* user specified or 5 sec default */ proxy->wait_time = udev->wait_time; @@ -403,6 +408,7 @@ static const Property vfio_user_pci_dev_properties[] = { sub_device_id, PCI_ANY_ID), DEFINE_PROP_BOOL("x-send-queued", VFIOUserPCIDevice, send_queued, false), DEFINE_PROP_UINT32("x-msg-timeout", VFIOUserPCIDevice, wait_time, 5000), + DEFINE_PROP_BOOL("x-no-posted-writes", VFIOUserPCIDevice, no_post, false), }; static void vfio_user_pci_set_socket(Object *obj, Visitor *v, const char *name, diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index c3724ba212..7ce8573abb 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -714,8 +714,8 @@ bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, * In either case, ownership of @hdr and @fds is taken, and the caller must * *not* free them itself. */ -static bool vfio_user_send_async(VFIOUserProxy *proxy, VFIOUserHdr *hdr, - VFIOUserFDs *fds, Error **errp) +bool vfio_user_send_async(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, Error **errp) { VFIOUserMsg *msg; @@ -844,6 +844,14 @@ void vfio_user_putfds(VFIOUserMsg *msg) msg->fds = NULL; } +void +vfio_user_disable_posted_writes(VFIOUserProxy *proxy) +{ + WITH_QEMU_LOCK_GUARD(&proxy->lock) { + proxy->flags |= VFIO_PROXY_NO_POST; + } +} + static QLIST_HEAD(, VFIOUserProxy) vfio_user_sockets = QLIST_HEAD_INITIALIZER(vfio_user_sockets); diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h index 6b29cd7cd3..0418f58bf1 100644 --- a/hw/vfio-user/proxy.h +++ b/hw/vfio-user/proxy.h @@ -91,6 +91,7 @@ typedef struct VFIOUserProxy { /* VFIOProxy flags */ #define VFIO_PROXY_CLIENT 0x1 #define VFIO_PROXY_FORCE_QUEUED 0x4 +#define VFIO_PROXY_NO_POST 0x8 typedef struct VFIODevice VFIODevice; @@ -104,6 +105,8 @@ bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp); VFIOUserFDs *vfio_user_getfds(int numfds); void vfio_user_putfds(VFIOUserMsg *msg); +void vfio_user_disable_posted_writes(VFIOUserProxy *proxy); + void vfio_user_request_msg(VFIOUserHdr *hdr, uint16_t cmd, uint32_t size, uint32_t flags); void vfio_user_wait_reqs(VFIOUserProxy *proxy); @@ -111,6 +114,9 @@ bool vfio_user_send_wait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, VFIOUserFDs *fds, int rsize, Error **errp); bool vfio_user_send_nowait(VFIOUserProxy *proxy, VFIOUserHdr *hdr, VFIOUserFDs *fds, int rsize, Error **errp); +bool vfio_user_send_async(VFIOUserProxy *proxy, VFIOUserHdr *hdr, + VFIOUserFDs *fds, Error **errp); + void vfio_user_send_reply(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int size); void vfio_user_send_error(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int error); From 1a0c32a9daa39738fffc7de086845b2e54ae7034 Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:30:09 +0100 Subject: [PATCH 1690/2760] vfio-user: add coalesced posted writes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new message to send multiple writes to server in a single message. Prevents the outgoing queue from overflowing when a long latency operation is followed by a series of posted writes. Originally-by: John Johnson Signed-off-by: Elena Ufimtseva Signed-off-by: Jagannathan Raman Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-18-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/device.c | 40 +++++++++++++++++++ hw/vfio-user/protocol.h | 21 ++++++++++ hw/vfio-user/proxy.c | 84 +++++++++++++++++++++++++++++++++++++++ hw/vfio-user/proxy.h | 12 ++++++ hw/vfio-user/trace-events | 1 + 5 files changed, 158 insertions(+) diff --git a/hw/vfio-user/device.c b/hw/vfio-user/device.c index aa07eac330..0609a7dc25 100644 --- a/hw/vfio-user/device.c +++ b/hw/vfio-user/device.c @@ -9,6 +9,8 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/error-report.h" +#include "qemu/lockable.h" +#include "qemu/thread.h" #include "hw/vfio-user/device.h" #include "hw/vfio-user/trace.h" @@ -337,6 +339,7 @@ static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index, VFIOUserProxy *proxy = vbasedev->proxy; int size = sizeof(*msgp) + count; Error *local_err = NULL; + bool can_multi; int flags = 0; int ret; @@ -352,6 +355,43 @@ static int vfio_user_device_io_region_write(VFIODevice *vbasedev, uint8_t index, flags |= VFIO_USER_NO_REPLY; } + /* write eligible to be in a WRITE_MULTI msg ? */ + can_multi = (proxy->flags & VFIO_PROXY_USE_MULTI) && post && + count <= VFIO_USER_MULTI_DATA; + + /* + * This should be a rare case, so first check without the lock, + * if we're wrong, vfio_send_queued() will flush any posted writes + * we missed here + */ + if (proxy->wr_multi != NULL || + (proxy->num_outgoing > VFIO_USER_OUT_HIGH && can_multi)) { + + /* + * re-check with lock + * + * if already building a WRITE_MULTI msg, + * add this one if possible else flush pending before + * sending the current one + * + * else if outgoing queue is over the highwater, + * start a new WRITE_MULTI message + */ + WITH_QEMU_LOCK_GUARD(&proxy->lock) { + if (proxy->wr_multi != NULL) { + if (can_multi) { + vfio_user_add_multi(proxy, index, off, count, data); + return count; + } + vfio_user_flush_multi(proxy); + } else if (proxy->num_outgoing > VFIO_USER_OUT_HIGH && can_multi) { + vfio_user_create_multi(proxy); + vfio_user_add_multi(proxy, index, off, count, data); + return count; + } + } + } + msgp = g_malloc0(size); vfio_user_request_msg(&msgp->hdr, VFIO_USER_REGION_WRITE, size, flags); msgp->offset = off; diff --git a/hw/vfio-user/protocol.h b/hw/vfio-user/protocol.h index 3e9d8e576b..3249a4a6b6 100644 --- a/hw/vfio-user/protocol.h +++ b/hw/vfio-user/protocol.h @@ -39,6 +39,7 @@ enum vfio_user_command { VFIO_USER_DMA_WRITE = 12, VFIO_USER_DEVICE_RESET = 13, VFIO_USER_DIRTY_PAGES = 14, + VFIO_USER_REGION_WRITE_MULTI = 15, VFIO_USER_MAX, }; @@ -72,6 +73,7 @@ typedef struct { #define VFIO_USER_CAP_PGSIZES "pgsizes" #define VFIO_USER_CAP_MAP_MAX "max_dma_maps" #define VFIO_USER_CAP_MIGR "migration" +#define VFIO_USER_CAP_MULTI "write_multiple" /* "migration" members */ #define VFIO_USER_CAP_PGSIZE "pgsize" @@ -218,4 +220,23 @@ typedef struct { char data[]; } VFIOUserBitmap; +/* + * VFIO_USER_REGION_WRITE_MULTI + */ +#define VFIO_USER_MULTI_DATA 8 +#define VFIO_USER_MULTI_MAX 200 + +typedef struct { + uint64_t offset; + uint32_t region; + uint32_t count; + char data[VFIO_USER_MULTI_DATA]; +} VFIOUserWROne; + +typedef struct { + VFIOUserHdr hdr; + uint64_t wr_cnt; + VFIOUserWROne wrs[VFIO_USER_MULTI_MAX]; +} VFIOUserWRMulti; + #endif /* VFIO_USER_PROTOCOL_H */ diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index 7ce8573abb..c418954440 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -13,12 +13,14 @@ #include "hw/vfio-user/proxy.h" #include "hw/vfio-user/trace.h" #include "qapi/error.h" +#include "qobject/qbool.h" #include "qobject/qdict.h" #include "qobject/qjson.h" #include "qobject/qnum.h" #include "qemu/error-report.h" #include "qemu/lockable.h" #include "qemu/main-loop.h" +#include "qemu/thread.h" #include "system/iothread.h" static IOThread *vfio_user_iothread; @@ -445,6 +447,7 @@ static ssize_t vfio_user_send_one(VFIOUserProxy *proxy, Error **errp) } QTAILQ_REMOVE(&proxy->outgoing, msg, next); + proxy->num_outgoing--; if (msg->type == VFIO_MSG_ASYNC) { vfio_user_recycle(proxy, msg); } else { @@ -481,6 +484,11 @@ static void vfio_user_send(void *opaque) } qio_channel_set_aio_fd_handler(proxy->ioc, proxy->ctx, vfio_user_recv, NULL, NULL, proxy); + + /* queue empty - send any pending multi write msgs */ + if (proxy->wr_multi != NULL) { + vfio_user_flush_multi(proxy); + } } } @@ -579,11 +587,18 @@ static bool vfio_user_send_queued(VFIOUserProxy *proxy, VFIOUserMsg *msg, { int ret; + /* older coalesced writes go first */ + if (proxy->wr_multi != NULL && + ((msg->hdr->flags & VFIO_USER_TYPE) == VFIO_USER_REQUEST)) { + vfio_user_flush_multi(proxy); + } + /* * Unsent outgoing msgs - add to tail */ if (!QTAILQ_EMPTY(&proxy->outgoing)) { QTAILQ_INSERT_TAIL(&proxy->outgoing, msg, next); + proxy->num_outgoing++; return true; } @@ -598,6 +613,7 @@ static bool vfio_user_send_queued(VFIOUserProxy *proxy, VFIOUserMsg *msg, if (ret == QIO_CHANNEL_ERR_BLOCK) { QTAILQ_INSERT_HEAD(&proxy->outgoing, msg, next); + proxy->num_outgoing = 1; qio_channel_set_aio_fd_handler(proxy->ioc, proxy->ctx, vfio_user_recv, proxy->ctx, vfio_user_send, proxy); @@ -1151,12 +1167,27 @@ static bool check_migr(VFIOUserProxy *proxy, QObject *qobj, Error **errp) return caps_parse(proxy, qdict, caps_migr, errp); } +static bool check_multi(VFIOUserProxy *proxy, QObject *qobj, Error **errp) +{ + QBool *qb = qobject_to(QBool, qobj); + + if (qb == NULL) { + error_setg(errp, "malformed %s", VFIO_USER_CAP_MULTI); + return false; + } + if (qbool_get_bool(qb)) { + proxy->flags |= VFIO_PROXY_USE_MULTI; + } + return true; +} + static struct cap_entry caps_cap[] = { { VFIO_USER_CAP_MAX_FDS, check_max_fds }, { VFIO_USER_CAP_MAX_XFER, check_max_xfer }, { VFIO_USER_CAP_PGSIZES, check_pgsizes }, { VFIO_USER_CAP_MAP_MAX, check_max_dma }, { VFIO_USER_CAP_MIGR, check_migr }, + { VFIO_USER_CAP_MULTI, check_multi }, { NULL } }; @@ -1215,6 +1246,7 @@ static GString *caps_json(void) qdict_put_int(capdict, VFIO_USER_CAP_MAX_XFER, VFIO_USER_DEF_MAX_XFER); qdict_put_int(capdict, VFIO_USER_CAP_PGSIZES, VFIO_USER_DEF_PGSIZE); qdict_put_int(capdict, VFIO_USER_CAP_MAP_MAX, VFIO_USER_DEF_MAP_MAX); + qdict_put_bool(capdict, VFIO_USER_CAP_MULTI, true); qdict_put_obj(dict, VFIO_USER_CAP, QOBJECT(capdict)); @@ -1270,3 +1302,55 @@ bool vfio_user_validate_version(VFIOUserProxy *proxy, Error **errp) trace_vfio_user_version(msgp->major, msgp->minor, msgp->capabilities); return true; } + +void vfio_user_flush_multi(VFIOUserProxy *proxy) +{ + VFIOUserMsg *msg; + VFIOUserWRMulti *wm = proxy->wr_multi; + Error *local_err = NULL; + + proxy->wr_multi = NULL; + + /* adjust size for actual # of writes */ + wm->hdr.size -= (VFIO_USER_MULTI_MAX - wm->wr_cnt) * sizeof(VFIOUserWROne); + + msg = vfio_user_getmsg(proxy, &wm->hdr, NULL); + msg->id = wm->hdr.id; + msg->rsize = 0; + msg->type = VFIO_MSG_ASYNC; + trace_vfio_user_wrmulti("flush", wm->wr_cnt); + + if (!vfio_user_send_queued(proxy, msg, &local_err)) { + error_report_err(local_err); + vfio_user_recycle(proxy, msg); + } +} + +void vfio_user_create_multi(VFIOUserProxy *proxy) +{ + VFIOUserWRMulti *wm; + + wm = g_malloc0(sizeof(*wm)); + vfio_user_request_msg(&wm->hdr, VFIO_USER_REGION_WRITE_MULTI, + sizeof(*wm), VFIO_USER_NO_REPLY); + proxy->wr_multi = wm; +} + +void vfio_user_add_multi(VFIOUserProxy *proxy, uint8_t index, + off_t offset, uint32_t count, void *data) +{ + VFIOUserWRMulti *wm = proxy->wr_multi; + VFIOUserWROne *w1 = &wm->wrs[wm->wr_cnt]; + + w1->offset = offset; + w1->region = index; + w1->count = count; + memcpy(&w1->data, data, count); + + wm->wr_cnt++; + trace_vfio_user_wrmulti("add", wm->wr_cnt); + if (wm->wr_cnt == VFIO_USER_MULTI_MAX || + proxy->num_outgoing < VFIO_USER_OUT_LOW) { + vfio_user_flush_multi(proxy); + } +} diff --git a/hw/vfio-user/proxy.h b/hw/vfio-user/proxy.h index 0418f58bf1..61e64a0020 100644 --- a/hw/vfio-user/proxy.h +++ b/hw/vfio-user/proxy.h @@ -85,6 +85,8 @@ typedef struct VFIOUserProxy { VFIOUserMsg *last_nowait; VFIOUserMsg *part_recv; size_t recv_left; + VFIOUserWRMulti *wr_multi; + int num_outgoing; enum proxy_state state; } VFIOUserProxy; @@ -92,6 +94,11 @@ typedef struct VFIOUserProxy { #define VFIO_PROXY_CLIENT 0x1 #define VFIO_PROXY_FORCE_QUEUED 0x4 #define VFIO_PROXY_NO_POST 0x8 +#define VFIO_PROXY_USE_MULTI 0x16 + +/* coalescing high and low water marks for VFIOProxy num_outgoing */ +#define VFIO_USER_OUT_HIGH 1024 +#define VFIO_USER_OUT_LOW 128 typedef struct VFIODevice VFIODevice; @@ -120,4 +127,9 @@ bool vfio_user_send_async(VFIOUserProxy *proxy, VFIOUserHdr *hdr, void vfio_user_send_reply(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int size); void vfio_user_send_error(VFIOUserProxy *proxy, VFIOUserHdr *hdr, int error); +void vfio_user_flush_multi(VFIOUserProxy *proxy); +void vfio_user_create_multi(VFIOUserProxy *proxy); +void vfio_user_add_multi(VFIOUserProxy *proxy, uint8_t index, + off_t offset, uint32_t count, void *data); + #endif /* VFIO_USER_PROXY_H */ diff --git a/hw/vfio-user/trace-events b/hw/vfio-user/trace-events index 44dde020b3..abb67f4c11 100644 --- a/hw/vfio-user/trace-events +++ b/hw/vfio-user/trace-events @@ -13,6 +13,7 @@ vfio_user_get_region_info(uint32_t index, uint32_t flags, uint64_t size) " index vfio_user_region_rw(uint32_t region, uint64_t off, uint32_t count) " region %d offset 0x%"PRIx64" count %d" vfio_user_get_irq_info(uint32_t index, uint32_t flags, uint32_t count) " index %d flags 0x%x count %d" vfio_user_set_irqs(uint32_t index, uint32_t start, uint32_t count, uint32_t flags) " index %d start %d count %d flags 0x%x" +vfio_user_wrmulti(const char *s, uint64_t wr_cnt) " %s count 0x%"PRIx64 # container.c vfio_user_dma_map(uint64_t iova, uint64_t size, uint64_t off, uint32_t flags, bool async_ops) " iova 0x%"PRIx64" size 0x%"PRIx64" off 0x%"PRIx64" flags 0x%x async_ops %d" From c688cc165b32c924a01a69d90ac9aac6fac8c64c Mon Sep 17 00:00:00 2001 From: John Levon Date: Wed, 25 Jun 2025 20:30:10 +0100 Subject: [PATCH 1691/2760] docs: add vfio-user documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some basic documentation on vfio-user usage. Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-19-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- docs/system/device-emulation.rst | 1 + docs/system/devices/vfio-user.rst | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 docs/system/devices/vfio-user.rst diff --git a/docs/system/device-emulation.rst b/docs/system/device-emulation.rst index a1b0d7997e..911381643f 100644 --- a/docs/system/device-emulation.rst +++ b/docs/system/device-emulation.rst @@ -85,6 +85,7 @@ Emulated Devices devices/can.rst devices/ccid.rst devices/cxl.rst + devices/vfio-user.rst devices/ivshmem.rst devices/ivshmem-flat.rst devices/keyboard.rst diff --git a/docs/system/devices/vfio-user.rst b/docs/system/devices/vfio-user.rst new file mode 100644 index 0000000000..b6dcaa5615 --- /dev/null +++ b/docs/system/devices/vfio-user.rst @@ -0,0 +1,26 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +========= +vfio-user +========= + +QEMU includes a ``vfio-user`` client. The ``vfio-user`` specification allows for +implementing (PCI) devices in userspace outside of QEMU; it is similar to +``vhost-user`` in this respect (see :doc:`vhost-user`), but can emulate arbitrary +PCI devices, not just ``virtio``. Whereas ``vfio`` is handled by the host +kernel, ``vfio-user``, while similar in implementation, is handled entirely in +userspace. + +For example, SPDK includes a virtual PCI NVMe controller implementation; by +setting up a ``vfio-user`` UNIX socket between QEMU and SPDK, a VM can send NVMe +I/O to the SPDK process. + +Presuming a suitable ``vfio-user`` server has opened a socket at +``/tmp/vfio-user.sock``, a device can be configured with for example: + +.. code-block:: console + +-device '{"driver": "vfio-user-pci","socket": {"path": "/tmp/vfio-user.sock", "type": "unix"}}' + +See `libvfio-user `_ for further +information. From da198e8f0f99cd8539f3072ad2071f9dc01680d6 Mon Sep 17 00:00:00 2001 From: Thanos Makatos Date: Wed, 25 Jun 2025 20:30:11 +0100 Subject: [PATCH 1692/2760] vfio-user: introduce vfio-user protocol specification MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch introduces the vfio-user protocol specification (formerly known as VFIO-over-socket), which is designed to allow devices to be emulated outside QEMU, in a separate process. vfio-user reuses the existing VFIO defines, structs and concepts. It has been earlier discussed as an RFC in: "RFC: use VFIO over a UNIX domain socket to implement device offloading" Signed-off-by: Thanos Makatos Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625193012.2316242-20-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 3 +- docs/interop/index.rst | 1 + docs/interop/vfio-user.rst | 1520 ++++++++++++++++++++++++++++++++++++ 3 files changed, 1523 insertions(+), 1 deletion(-) create mode 100644 docs/interop/vfio-user.rst diff --git a/MAINTAINERS b/MAINTAINERS index 2369391004..1b73b8b394 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4246,7 +4246,6 @@ F: hw/remote/proxy-memory-listener.c F: include/hw/remote/proxy-memory-listener.h F: hw/remote/iohub.c F: include/hw/remote/iohub.h -F: subprojects/libvfio-user F: hw/remote/vfio-user-obj.c F: include/hw/remote/vfio-user-obj.h F: hw/remote/iommu.c @@ -4257,6 +4256,8 @@ VFIO-USER: M: John Levon M: Thanos Makatos S: Supported +F: docs/interop/vfio-user.rst +F: docs/system/devices/vfio-user.rst F: hw/vfio-user/* F: include/hw/vfio-user/* F: subprojects/libvfio-user diff --git a/docs/interop/index.rst b/docs/interop/index.rst index 972f3e49ce..d830c5c410 100644 --- a/docs/interop/index.rst +++ b/docs/interop/index.rst @@ -25,6 +25,7 @@ are useful for making QEMU interoperate with other software. qemu-ga-ref qemu-qmp-ref qemu-storage-daemon-qmp-ref + vfio-user vhost-user vhost-user-gpu vhost-vdpa diff --git a/docs/interop/vfio-user.rst b/docs/interop/vfio-user.rst new file mode 100644 index 0000000000..0b06f026b0 --- /dev/null +++ b/docs/interop/vfio-user.rst @@ -0,0 +1,1520 @@ +.. include:: +.. SPDX-License-Identifier: GPL-2.0-or-later + +================================ +vfio-user Protocol Specification +================================ + +.. contents:: Table of Contents + +Introduction +============ +vfio-user is a protocol that allows a device to be emulated in a separate +process outside of a Virtual Machine Monitor (VMM). vfio-user devices consist +of a generic VFIO device type, living inside the VMM, which we call the client, +and the core device implementation, living outside the VMM, which we call the +server. + +The vfio-user specification is partly based on the +`Linux VFIO ioctl interface `_. + +VFIO is a mature and stable API, backed by an extensively used framework. The +existing VFIO client implementation in QEMU (``qemu/hw/vfio/``) can be largely +re-used, though there is nothing in this specification that requires that +particular implementation. None of the VFIO kernel modules are required for +supporting the protocol, on either the client or server side. Some source +definitions in VFIO are re-used for vfio-user. + +The main idea is to allow a virtual device to function in a separate process in +the same host over a UNIX domain socket. A UNIX domain socket (``AF_UNIX``) is +chosen because file descriptors can be trivially sent over it, which in turn +allows: + +* Sharing of client memory for DMA with the server. +* Sharing of server memory with the client for fast MMIO. +* Efficient sharing of eventfd's for triggering interrupts. + +Other socket types could be used which allow the server to run in a separate +guest in the same host (``AF_VSOCK``) or remotely (``AF_INET``). Theoretically +the underlying transport does not necessarily have to be a socket, however we do +not examine such alternatives. In this protocol version we focus on using a UNIX +domain socket and introduce basic support for the other two types of sockets +without considering performance implications. + +While passing of file descriptors is desirable for performance reasons, support +is not necessary for either the client or the server in order to implement the +protocol. There is always an in-band, message-passing fall back mechanism. + +Overview +======== + +VFIO is a framework that allows a physical device to be securely passed through +to a user space process; the device-specific kernel driver does not drive the +device at all. Typically, the user space process is a VMM and the device is +passed through to it in order to achieve high performance. VFIO provides an API +and the required functionality in the kernel. QEMU has adopted VFIO to allow a +guest to directly access physical devices, instead of emulating them in +software. + +vfio-user reuses the core VFIO concepts defined in its API, but implements them +as messages to be sent over a socket. It does not change the kernel-based VFIO +in any way, in fact none of the VFIO kernel modules need to be loaded to use +vfio-user. It is also possible for the client to concurrently use the current +kernel-based VFIO for one device, and vfio-user for another device. + +VFIO Device Model +----------------- + +A device under VFIO presents a standard interface to the user process. Many of +the VFIO operations in the existing interface use the ``ioctl()`` system call, and +references to the existing interface are called the ``ioctl()`` implementation in +this document. + +The following sections describe the set of messages that implement the vfio-user +interface over a socket. In many cases, the messages are analogous to data +structures used in the ``ioctl()`` implementation. Messages derived from the +``ioctl()`` will have a name derived from the ``ioctl()`` command name. E.g., the +``VFIO_DEVICE_GET_INFO`` ``ioctl()`` command becomes a +``VFIO_USER_DEVICE_GET_INFO`` message. The purpose of this reuse is to share as +much code as feasible with the ``ioctl()`` implementation``. + +Connection Initiation +^^^^^^^^^^^^^^^^^^^^^ + +After the client connects to the server, the initial client message is +``VFIO_USER_VERSION`` to propose a protocol version and set of capabilities to +apply to the session. The server replies with a compatible version and set of +capabilities it supports, or closes the connection if it cannot support the +advertised version. + +Device Information +^^^^^^^^^^^^^^^^^^ + +The client uses a ``VFIO_USER_DEVICE_GET_INFO`` message to query the server for +information about the device. This information includes: + +* The device type and whether it supports reset (``VFIO_DEVICE_FLAGS_``), +* the number of device regions, and +* the device presents to the client the number of interrupt types the device + supports. + +Region Information +^^^^^^^^^^^^^^^^^^ + +The client uses ``VFIO_USER_DEVICE_GET_REGION_INFO`` messages to query the +server for information about the device's regions. This information describes: + +* Read and write permissions, whether it can be memory mapped, and whether it + supports additional capabilities (``VFIO_REGION_INFO_CAP_``). +* Region index, size, and offset. + +When a device region can be mapped by the client, the server provides a file +descriptor which the client can ``mmap()``. The server is responsible for +polling for client updates to memory mapped regions. + +Region Capabilities +""""""""""""""""""" + +Some regions have additional capabilities that cannot be described adequately +by the region info data structure. These capabilities are returned in the +region info reply in a list similar to PCI capabilities in a PCI device's +configuration space. + +Sparse Regions +"""""""""""""" +A region can be memory-mappable in whole or in part. When only a subset of a +region can be mapped by the client, a ``VFIO_REGION_INFO_CAP_SPARSE_MMAP`` +capability is included in the region info reply. This capability describes +which portions can be mapped by the client. + +.. Note:: + For example, in a virtual NVMe controller, sparse regions can be used so + that accesses to the NVMe registers (found in the beginning of BAR0) are + trapped (an infrequent event), while allowing direct access to the doorbells + (an extremely frequent event as every I/O submission requires a write to + BAR0), found in the next page after the NVMe registers in BAR0. + +Device-Specific Regions +""""""""""""""""""""""" + +A device can define regions additional to the standard ones (e.g. PCI indexes +0-8). This is achieved by including a ``VFIO_REGION_INFO_CAP_TYPE`` capability +in the region info reply of a device-specific region. Such regions are reflected +in ``struct vfio_user_device_info.num_regions``. Thus, for PCI devices this +value can be equal to, or higher than, ``VFIO_PCI_NUM_REGIONS``. + +Region I/O via file descriptors +------------------------------- + +For unmapped regions, region I/O from the client is done via +``VFIO_USER_REGION_READ/WRITE``. As an optimization, ioeventfds or ioregionfds +may be configured for sub-regions of some regions. A client may request +information on these sub-regions via ``VFIO_USER_DEVICE_GET_REGION_IO_FDS``; by +configuring the returned file descriptors as ioeventfds or ioregionfds, the +server can be directly notified of I/O (for example, by KVM) without taking a +trip through the client. + +Interrupts +^^^^^^^^^^ + +The client uses ``VFIO_USER_DEVICE_GET_IRQ_INFO`` messages to query the server +for the device's interrupt types. The interrupt types are specific to the bus +the device is attached to, and the client is expected to know the capabilities +of each interrupt type. The server can signal an interrupt by directly injecting +interrupts into the guest via an event file descriptor. The client configures +how the server signals an interrupt with ``VFIO_USER_SET_IRQS`` messages. + +Device Read and Write +^^^^^^^^^^^^^^^^^^^^^ + +When the guest executes load or store operations to an unmapped device region, +the client forwards these operations to the server with +``VFIO_USER_REGION_READ`` or ``VFIO_USER_REGION_WRITE`` messages. The server +will reply with data from the device on read operations or an acknowledgement on +write operations. See `Read and Write Operations`_. + +Client memory access +-------------------- + +The client uses ``VFIO_USER_DMA_MAP`` and ``VFIO_USER_DMA_UNMAP`` messages to +inform the server of the valid DMA ranges that the server can access on behalf +of a device (typically, VM guest memory). DMA memory may be accessed by the +server via ``VFIO_USER_DMA_READ`` and ``VFIO_USER_DMA_WRITE`` messages over the +socket. In this case, the "DMA" part of the naming is a misnomer. + +Actual direct memory access of client memory from the server is possible if the +client provides file descriptors the server can ``mmap()``. Note that ``mmap()`` +privileges cannot be revoked by the client, therefore file descriptors should +only be exported in environments where the client trusts the server not to +corrupt guest memory. + +See `Read and Write Operations`_. + +Client/server interactions +========================== + +Socket +------ + +A server can serve: + +1) one or more clients, and/or +2) one or more virtual devices, belonging to one or more clients. + +The current protocol specification requires a dedicated socket per +client/server connection. It is a server-side implementation detail whether a +single server handles multiple virtual devices from the same or multiple +clients. The location of the socket is implementation-specific. Multiplexing +clients, devices, and servers over the same socket is not supported in this +version of the protocol. + +Authentication +-------------- + +For ``AF_UNIX``, we rely on OS mandatory access controls on the socket files, +therefore it is up to the management layer to set up the socket as required. +Socket types that span guests or hosts will require a proper authentication +mechanism. Defining that mechanism is deferred to a future version of the +protocol. + +Command Concurrency +------------------- + +A client may pipeline multiple commands without waiting for previous command +replies. The server will process commands in the order they are received. A +consequence of this is if a client issues a command with the *No_reply* bit, +then subsequently issues a command without *No_reply*, the older command will +have been processed before the reply to the younger command is sent by the +server. The client must be aware of the device's capability to process +concurrent commands if pipelining is used. For example, pipelining allows +multiple client threads to concurrently access device regions; the client must +ensure these accesses obey device semantics. + +An example is a frame buffer device, where the device may allow concurrent +access to different areas of video memory, but may have indeterminate behavior +if concurrent accesses are performed to command or status registers. + +Note that unrelated messages sent from the server to the client can appear in +between a client to server request/reply and vice versa. + +Implementers should be prepared for certain commands to exhibit potentially +unbounded latencies. For example, ``VFIO_USER_DEVICE_RESET`` may take an +arbitrarily long time to complete; clients should take care not to block +unnecessarily. + +Socket Disconnection Behavior +----------------------------- +The server and the client can disconnect from each other, either intentionally +or unexpectedly. Both the client and the server need to know how to handle such +events. + +Server Disconnection +^^^^^^^^^^^^^^^^^^^^ +A server disconnecting from the client may indicate that: + +1) A virtual device has been restarted, either intentionally (e.g. because of a + device update) or unintentionally (e.g. because of a crash). +2) A virtual device has been shut down with no intention to be restarted. + +It is impossible for the client to know whether or not a failure is +intermittent or innocuous and should be retried, therefore the client should +reset the VFIO device when it detects the socket has been disconnected. +Error recovery will be driven by the guest's device error handling +behavior. + +Client Disconnection +^^^^^^^^^^^^^^^^^^^^ +The client disconnecting from the server primarily means that the client +has exited. Currently, this means that the guest is shut down so the device is +no longer needed therefore the server can automatically exit. However, there +can be cases where a client disconnection should not result in a server exit: + +1) A single server serving multiple clients. +2) A multi-process QEMU upgrading itself step by step, which is not yet + implemented. + +Therefore in order for the protocol to be forward compatible, the server should +respond to a client disconnection as follows: + + - all client memory regions are unmapped and cleaned up (including closing any + passed file descriptors) + - all IRQ file descriptors passed from the old client are closed + - the device state should otherwise be retained + +The expectation is that when a client reconnects, it will re-establish IRQ and +client memory mappings. + +If anything happens to the client (such as qemu really did exit), the control +stack will know about it and can clean up resources accordingly. + +Security Considerations +----------------------- + +Speaking generally, vfio-user clients should not trust servers, and vice versa. +Standard tools and mechanisms should be used on both sides to validate input and +prevent against denial of service scenarios, buffer overflow, etc. + +Request Retry and Response Timeout +---------------------------------- +A failed command is a command that has been successfully sent and has been +responded to with an error code. Failure to send the command in the first place +(e.g. because the socket is disconnected) is a different type of error examined +earlier in the disconnect section. + +.. Note:: + QEMU's VFIO retries certain operations if they fail. While this makes sense + for real HW, we don't know for sure whether it makes sense for virtual + devices. + +Defining a retry and timeout scheme is deferred to a future version of the +protocol. + +Message sizes +------------- + +Some requests have an ``argsz`` field. In a request, it defines the maximum +expected reply payload size, which should be at least the size of the fixed +reply payload headers defined here. The *request* payload size is defined by the +usual ``msg_size`` field in the header, not the ``argsz`` field. + +In a reply, the server sets ``argsz`` field to the size needed for a full +payload size. This may be less than the requested maximum size. This may be +larger than the requested maximum size: in that case, the full payload is not +included in the reply, but the ``argsz`` field in the reply indicates the needed +size, allowing a client to allocate a larger buffer for holding the reply before +trying again. + +In addition, during negotiation (see `Version`_), the client and server may +each specify a ``max_data_xfer_size`` value; this defines the maximum data that +may be read or written via one of the ``VFIO_USER_DMA/REGION_READ/WRITE`` +messages; see `Read and Write Operations`_. + +Protocol Specification +====================== + +To distinguish from the base VFIO symbols, all vfio-user symbols are prefixed +with ``vfio_user`` or ``VFIO_USER``. In this revision, all data is in the +endianness of the host system, although this may be relaxed in future +revisions in cases where the client and server run on different hosts +with different endianness. + +Unless otherwise specified, all sizes should be presumed to be in bytes. + +.. _Commands: + +Commands +-------- +The following table lists the VFIO message command IDs, and whether the +message command is sent from the client or the server. + +====================================== ========= ================= +Name Command Request Direction +====================================== ========= ================= +``VFIO_USER_VERSION`` 1 client -> server +``VFIO_USER_DMA_MAP`` 2 client -> server +``VFIO_USER_DMA_UNMAP`` 3 client -> server +``VFIO_USER_DEVICE_GET_INFO`` 4 client -> server +``VFIO_USER_DEVICE_GET_REGION_INFO`` 5 client -> server +``VFIO_USER_DEVICE_GET_REGION_IO_FDS`` 6 client -> server +``VFIO_USER_DEVICE_GET_IRQ_INFO`` 7 client -> server +``VFIO_USER_DEVICE_SET_IRQS`` 8 client -> server +``VFIO_USER_REGION_READ`` 9 client -> server +``VFIO_USER_REGION_WRITE`` 10 client -> server +``VFIO_USER_DMA_READ`` 11 server -> client +``VFIO_USER_DMA_WRITE`` 12 server -> client +``VFIO_USER_DEVICE_RESET`` 13 client -> server +``VFIO_USER_REGION_WRITE_MULTI`` 15 client -> server +====================================== ========= ================= + +Header +------ + +All messages, both command messages and reply messages, are preceded by a +16-byte header that contains basic information about the message. The header is +followed by message-specific data described in the sections below. + ++----------------+--------+-------------+ +| Name | Offset | Size | ++================+========+=============+ +| Message ID | 0 | 2 | ++----------------+--------+-------------+ +| Command | 2 | 2 | ++----------------+--------+-------------+ +| Message size | 4 | 4 | ++----------------+--------+-------------+ +| Flags | 8 | 4 | ++----------------+--------+-------------+ +| | +-----+------------+ | +| | | Bit | Definition | | +| | +=====+============+ | +| | | 0-3 | Type | | +| | +-----+------------+ | +| | | 4 | No_reply | | +| | +-----+------------+ | +| | | 5 | Error | | +| | +-----+------------+ | ++----------------+--------+-------------+ +| Error | 12 | 4 | ++----------------+--------+-------------+ +| | 16 | variable | ++----------------+--------+-------------+ + +* *Message ID* identifies the message, and is echoed in the command's reply + message. Message IDs belong entirely to the sender, can be re-used (even + concurrently) and the receiver must not make any assumptions about their + uniqueness. +* *Command* specifies the command to be executed, listed in Commands_. It is + also set in the reply header. +* *Message size* contains the size of the entire message, including the header. +* *Flags* contains attributes of the message: + + * The *Type* bits indicate the message type. + + * *Command* (value 0x0) indicates a command message. + * *Reply* (value 0x1) indicates a reply message acknowledging a previous + command with the same message ID. + * *No_reply* in a command message indicates that no reply is needed for this + command. This is commonly used when multiple commands are sent, and only + the last needs acknowledgement. + * *Error* in a reply message indicates the command being acknowledged had + an error. In this case, the *Error* field will be valid. + +* *Error* in a reply message is an optional UNIX errno value. It may be zero + even if the Error bit is set in Flags. It is reserved in a command message. + +Each command message in Commands_ must be replied to with a reply message, +unless the message sets the *No_Reply* bit. The reply consists of the header +with the *Reply* bit set, plus any additional data. + +If an error occurs, the reply message must only include the reply header. + +As the header is standard in both requests and replies, it is not included in +the command-specific specifications below; each message definition should be +appended to the standard header, and the offsets are given from the end of the +standard header. + +``VFIO_USER_VERSION`` +--------------------- + +.. _Version: + +This is the initial message sent by the client after the socket connection is +established; the same format is used for the server's reply. + +Upon establishing a connection, the client must send a ``VFIO_USER_VERSION`` +message proposing a protocol version and a set of capabilities. The server +compares these with the versions and capabilities it supports and sends a +``VFIO_USER_VERSION`` reply according to the following rules. + +* The major version in the reply must be the same as proposed. If the client + does not support the proposed major, it closes the connection. +* The minor version in the reply must be equal to or less than the minor + version proposed. +* The capability list must be a subset of those proposed. If the server + requires a capability the client did not include, it closes the connection. + +The protocol major version will only change when incompatible protocol changes +are made, such as changing the message format. The minor version may change +when compatible changes are made, such as adding new messages or capabilities, +Both the client and server must support all minor versions less than the +maximum minor version it supports. E.g., an implementation that supports +version 1.3 must also support 1.0 through 1.2. + +When making a change to this specification, the protocol version number must +be included in the form "added in version X.Y" + +Request +^^^^^^^ + +============== ====== ==== +Name Offset Size +============== ====== ==== +version major 0 2 +version minor 2 2 +version data 4 variable (including terminating NUL). Optional. +============== ====== ==== + +The version data is an optional UTF-8 encoded JSON byte array with the following +format: + ++--------------+--------+-----------------------------------+ +| Name | Type | Description | ++==============+========+===================================+ +| capabilities | object | Contains common capabilities that | +| | | the sender supports. Optional. | ++--------------+--------+-----------------------------------+ + +Capabilities: + ++--------------------+---------+------------------------------------------------+ +| Name | Type | Description | ++====================+=========+================================================+ +| max_msg_fds | number | Maximum number of file descriptors that can be | +| | | received by the sender in one message. | +| | | Optional. If not specified then the receiver | +| | | must assume a value of ``1``. | ++--------------------+---------+------------------------------------------------+ +| max_data_xfer_size | number | Maximum ``count`` for data transfer messages; | +| | | see `Read and Write Operations`_. Optional, | +| | | with a default value of 1048576 bytes. | ++--------------------+---------+------------------------------------------------+ +| pgsizes | number | Page sizes supported in DMA map operations | +| | | or'ed together. Optional, with a default value | +| | | of supporting only 4k pages. | ++--------------------+---------+------------------------------------------------+ +| max_dma_maps | number | Maximum number DMA map windows that can be | +| | | valid simultaneously. Optional, with a | +| | | value of 65535 (64k-1). | ++--------------------+---------+------------------------------------------------+ +| migration | object | Migration capability parameters. If missing | +| | | then migration is not supported by the sender. | ++--------------------+---------+------------------------------------------------+ +| write_multiple | boolean | ``VFIO_USER_REGION_WRITE_MULTI`` messages | +| | | are supported if the value is ``true``. | ++--------------------+---------+------------------------------------------------+ + +The migration capability contains the following name/value pairs: + ++-----------------+--------+--------------------------------------------------+ +| Name | Type | Description | ++=================+========+==================================================+ +| pgsize | number | Page size of dirty pages bitmap. The smallest | +| | | between the client and the server is used. | ++-----------------+--------+--------------------------------------------------+ +| max_bitmap_size | number | Maximum bitmap size in ``VFIO_USER_DIRTY_PAGES`` | +| | | and ``VFIO_DMA_UNMAP`` messages. Optional, | +| | | with a default value of 256MB. | ++-----------------+--------+--------------------------------------------------+ + +Reply +^^^^^ + +The same message format is used in the server's reply with the semantics +described above. + +``VFIO_USER_DMA_MAP`` +--------------------- + +This command message is sent by the client to the server to inform it of the +memory regions the server can access. It must be sent before the server can +perform any DMA to the client. It is normally sent directly after the version +handshake is completed, but may also occur when memory is added to the client, +or if the client uses a vIOMMU. + +Request +^^^^^^^ + +The request payload for this message is a structure of the following format: + ++-------------+--------+-------------+ +| Name | Offset | Size | ++=============+========+=============+ +| argsz | 0 | 4 | ++-------------+--------+-------------+ +| flags | 4 | 4 | ++-------------+--------+-------------+ +| | +-----+------------+ | +| | | Bit | Definition | | +| | +=====+============+ | +| | | 0 | readable | | +| | +-----+------------+ | +| | | 1 | writeable | | +| | +-----+------------+ | ++-------------+--------+-------------+ +| offset | 8 | 8 | ++-------------+--------+-------------+ +| address | 16 | 8 | ++-------------+--------+-------------+ +| size | 24 | 8 | ++-------------+--------+-------------+ + +* *argsz* is the size of the above structure. Note there is no reply payload, + so this field differs from other message types. +* *flags* contains the following region attributes: + + * *readable* indicates that the region can be read from. + + * *writeable* indicates that the region can be written to. + +* *offset* is the file offset of the region with respect to the associated file + descriptor, or zero if the region is not mappable +* *address* is the base DMA address of the region. +* *size* is the size of the region. + +This structure is 32 bytes in size, so the message size is 16 + 32 bytes. + +If the DMA region being added can be directly mapped by the server, a file +descriptor must be sent as part of the message meta-data. The region can be +mapped via the mmap() system call. On ``AF_UNIX`` sockets, the file descriptor +must be passed as ``SCM_RIGHTS`` type ancillary data. Otherwise, if the DMA +region cannot be directly mapped by the server, no file descriptor must be sent +as part of the message meta-data and the DMA region can be accessed by the +server using ``VFIO_USER_DMA_READ`` and ``VFIO_USER_DMA_WRITE`` messages, +explained in `Read and Write Operations`_. A command to map over an existing +region must be failed by the server with ``EEXIST`` set in error field in the +reply. + +Reply +^^^^^ + +There is no payload in the reply message. + +``VFIO_USER_DMA_UNMAP`` +----------------------- + +This command message is sent by the client to the server to inform it that a +DMA region, previously made available via a ``VFIO_USER_DMA_MAP`` command +message, is no longer available for DMA. It typically occurs when memory is +subtracted from the client or if the client uses a vIOMMU. The DMA region is +described by the following structure: + +Request +^^^^^^^ + +The request payload for this message is a structure of the following format: + ++--------------+--------+------------------------+ +| Name | Offset | Size | ++==============+========+========================+ +| argsz | 0 | 4 | ++--------------+--------+------------------------+ +| flags | 4 | 4 | ++--------------+--------+------------------------+ +| address | 8 | 8 | ++--------------+--------+------------------------+ +| size | 16 | 8 | ++--------------+--------+------------------------+ + +* *argsz* is the maximum size of the reply payload. +* *flags* is unused in this version. +* *address* is the base DMA address of the DMA region. +* *size* is the size of the DMA region. + +The address and size of the DMA region being unmapped must match exactly a +previous mapping. + +Reply +^^^^^ + +Upon receiving a ``VFIO_USER_DMA_UNMAP`` command, if the file descriptor is +mapped then the server must release all references to that DMA region before +replying, which potentially includes in-flight DMA transactions. + +The server responds with the original DMA entry in the request. + + +``VFIO_USER_DEVICE_GET_INFO`` +----------------------------- + +This command message is sent by the client to the server to query for basic +information about the device. + +Request +^^^^^^^ + ++-------------+--------+--------------------------+ +| Name | Offset | Size | ++=============+========+==========================+ +| argsz | 0 | 4 | ++-------------+--------+--------------------------+ +| flags | 4 | 4 | ++-------------+--------+--------------------------+ +| | +-----+-------------------------+ | +| | | Bit | Definition | | +| | +=====+=========================+ | +| | | 0 | VFIO_DEVICE_FLAGS_RESET | | +| | +-----+-------------------------+ | +| | | 1 | VFIO_DEVICE_FLAGS_PCI | | +| | +-----+-------------------------+ | ++-------------+--------+--------------------------+ +| num_regions | 8 | 4 | ++-------------+--------+--------------------------+ +| num_irqs | 12 | 4 | ++-------------+--------+--------------------------+ + +* *argsz* is the maximum size of the reply payload +* all other fields must be zero. + +Reply +^^^^^ + ++-------------+--------+--------------------------+ +| Name | Offset | Size | ++=============+========+==========================+ +| argsz | 0 | 4 | ++-------------+--------+--------------------------+ +| flags | 4 | 4 | ++-------------+--------+--------------------------+ +| | +-----+-------------------------+ | +| | | Bit | Definition | | +| | +=====+=========================+ | +| | | 0 | VFIO_DEVICE_FLAGS_RESET | | +| | +-----+-------------------------+ | +| | | 1 | VFIO_DEVICE_FLAGS_PCI | | +| | +-----+-------------------------+ | ++-------------+--------+--------------------------+ +| num_regions | 8 | 4 | ++-------------+--------+--------------------------+ +| num_irqs | 12 | 4 | ++-------------+--------+--------------------------+ + +* *argsz* is the size required for the full reply payload (16 bytes today) +* *flags* contains the following device attributes. + + * ``VFIO_DEVICE_FLAGS_RESET`` indicates that the device supports the + ``VFIO_USER_DEVICE_RESET`` message. + * ``VFIO_DEVICE_FLAGS_PCI`` indicates that the device is a PCI device. + +* *num_regions* is the number of memory regions that the device exposes. +* *num_irqs* is the number of distinct interrupt types that the device supports. + +This version of the protocol only supports PCI devices. Additional devices may +be supported in future versions. + +``VFIO_USER_DEVICE_GET_REGION_INFO`` +------------------------------------ + +This command message is sent by the client to the server to query for +information about device regions. The VFIO region info structure is defined in +```` (``struct vfio_region_info``). + +Request +^^^^^^^ + ++------------+--------+------------------------------+ +| Name | Offset | Size | ++============+========+==============================+ +| argsz | 0 | 4 | ++------------+--------+------------------------------+ +| flags | 4 | 4 | ++------------+--------+------------------------------+ +| index | 8 | 4 | ++------------+--------+------------------------------+ +| cap_offset | 12 | 4 | ++------------+--------+------------------------------+ +| size | 16 | 8 | ++------------+--------+------------------------------+ +| offset | 24 | 8 | ++------------+--------+------------------------------+ + +* *argsz* the maximum size of the reply payload +* *index* is the index of memory region being queried, it is the only field + that is required to be set in the command message. +* all other fields must be zero. + +Reply +^^^^^ + ++------------+--------+------------------------------+ +| Name | Offset | Size | ++============+========+==============================+ +| argsz | 0 | 4 | ++------------+--------+------------------------------+ +| flags | 4 | 4 | ++------------+--------+------------------------------+ +| | +-----+-----------------------------+ | +| | | Bit | Definition | | +| | +=====+=============================+ | +| | | 0 | VFIO_REGION_INFO_FLAG_READ | | +| | +-----+-----------------------------+ | +| | | 1 | VFIO_REGION_INFO_FLAG_WRITE | | +| | +-----+-----------------------------+ | +| | | 2 | VFIO_REGION_INFO_FLAG_MMAP | | +| | +-----+-----------------------------+ | +| | | 3 | VFIO_REGION_INFO_FLAG_CAPS | | +| | +-----+-----------------------------+ | ++------------+--------+------------------------------+ ++------------+--------+------------------------------+ +| index | 8 | 4 | ++------------+--------+------------------------------+ +| cap_offset | 12 | 4 | ++------------+--------+------------------------------+ +| size | 16 | 8 | ++------------+--------+------------------------------+ +| offset | 24 | 8 | ++------------+--------+------------------------------+ + +* *argsz* is the size required for the full reply payload (region info structure + plus the size of any region capabilities) +* *flags* are attributes of the region: + + * ``VFIO_REGION_INFO_FLAG_READ`` allows client read access to the region. + * ``VFIO_REGION_INFO_FLAG_WRITE`` allows client write access to the region. + * ``VFIO_REGION_INFO_FLAG_MMAP`` specifies the client can mmap() the region. + When this flag is set, the reply will include a file descriptor in its + meta-data. On ``AF_UNIX`` sockets, the file descriptors will be passed as + ``SCM_RIGHTS`` type ancillary data. + * ``VFIO_REGION_INFO_FLAG_CAPS`` indicates additional capabilities found in the + reply. + +* *index* is the index of memory region being queried, it is the only field + that is required to be set in the command message. +* *cap_offset* describes where additional region capabilities can be found. + cap_offset is relative to the beginning of the VFIO region info structure. + The data structure it points is a VFIO cap header defined in + ````. +* *size* is the size of the region. +* *offset* is the offset that should be given to the mmap() system call for + regions with the MMAP attribute. It is also used as the base offset when + mapping a VFIO sparse mmap area, described below. + +VFIO region capabilities +"""""""""""""""""""""""" + +The VFIO region information can also include a capabilities list. This list is +similar to a PCI capability list - each entry has a common header that +identifies a capability and where the next capability in the list can be found. +The VFIO capability header format is defined in ```` (``struct +vfio_info_cap_header``). + +VFIO cap header format +"""""""""""""""""""""" + ++---------+--------+------+ +| Name | Offset | Size | ++=========+========+======+ +| id | 0 | 2 | ++---------+--------+------+ +| version | 2 | 2 | ++---------+--------+------+ +| next | 4 | 4 | ++---------+--------+------+ + +* *id* is the capability identity. +* *version* is a capability-specific version number. +* *next* specifies the offset of the next capability in the capability list. It + is relative to the beginning of the VFIO region info structure. + +VFIO sparse mmap cap header +""""""""""""""""""""""""""" + ++------------------+----------------------------------+ +| Name | Value | ++==================+==================================+ +| id | VFIO_REGION_INFO_CAP_SPARSE_MMAP | ++------------------+----------------------------------+ +| version | 0x1 | ++------------------+----------------------------------+ +| next | | ++------------------+----------------------------------+ +| sparse mmap info | VFIO region info sparse mmap | ++------------------+----------------------------------+ + +This capability is defined when only a subrange of the region supports +direct access by the client via mmap(). The VFIO sparse mmap area is defined in +```` (``struct vfio_region_sparse_mmap_area`` and ``struct +vfio_region_info_cap_sparse_mmap``). + +VFIO region info cap sparse mmap +"""""""""""""""""""""""""""""""" + ++----------+--------+------+ +| Name | Offset | Size | ++==========+========+======+ +| nr_areas | 0 | 4 | ++----------+--------+------+ +| reserved | 4 | 4 | ++----------+--------+------+ +| offset | 8 | 8 | ++----------+--------+------+ +| size | 16 | 8 | ++----------+--------+------+ +| ... | | | ++----------+--------+------+ + +* *nr_areas* is the number of sparse mmap areas in the region. +* *offset* and size describe a single area that can be mapped by the client. + There will be *nr_areas* pairs of offset and size. The offset will be added to + the base offset given in the ``VFIO_USER_DEVICE_GET_REGION_INFO`` to form the + offset argument of the subsequent mmap() call. + +The VFIO sparse mmap area is defined in ```` (``struct +vfio_region_info_cap_sparse_mmap``). + + +``VFIO_USER_DEVICE_GET_REGION_IO_FDS`` +-------------------------------------- + +Clients can access regions via ``VFIO_USER_REGION_READ/WRITE`` or, if provided, by +``mmap()`` of a file descriptor provided by the server. + +``VFIO_USER_DEVICE_GET_REGION_IO_FDS`` provides an alternative access mechanism via +file descriptors. This is an optional feature intended for performance +improvements where an underlying sub-system (such as KVM) supports communication +across such file descriptors to the vfio-user server, without needing to +round-trip through the client. + +The server returns an array of sub-regions for the requested region. Each +sub-region describes a span (offset and size) of a region, along with the +requested file descriptor notification mechanism to use. Each sub-region in the +response message may choose to use a different method, as defined below. The +two mechanisms supported in this specification are ioeventfds and ioregionfds. + +The server in addition returns a file descriptor in the ancillary data; clients +are expected to configure each sub-region's file descriptor with the requested +notification method. For example, a client could configure KVM with the +requested ioeventfd via a ``KVM_IOEVENTFD`` ``ioctl()``. + +Request +^^^^^^^ + ++-------------+--------+------+ +| Name | Offset | Size | ++=============+========+======+ +| argsz | 0 | 4 | ++-------------+--------+------+ +| flags | 4 | 4 | ++-------------+--------+------+ +| index | 8 | 4 | ++-------------+--------+------+ +| count | 12 | 4 | ++-------------+--------+------+ + +* *argsz* the maximum size of the reply payload +* *index* is the index of memory region being queried +* all other fields must be zero + +The client must set ``flags`` to zero and specify the region being queried in +the ``index``. + +Reply +^^^^^ + ++-------------+--------+------+ +| Name | Offset | Size | ++=============+========+======+ +| argsz | 0 | 4 | ++-------------+--------+------+ +| flags | 4 | 4 | ++-------------+--------+------+ +| index | 8 | 4 | ++-------------+--------+------+ +| count | 12 | 4 | ++-------------+--------+------+ +| sub-regions | 16 | ... | ++-------------+--------+------+ + +* *argsz* is the size of the region IO FD info structure plus the + total size of the sub-region array. Thus, each array entry "i" is at offset + i * ((argsz - 32) / count). Note that currently this is 40 bytes for both IO + FD types, but this is not to be relied on. As elsewhere, this indicates the + full reply payload size needed. +* *flags* must be zero +* *index* is the index of memory region being queried +* *count* is the number of sub-regions in the array +* *sub-regions* is the array of Sub-Region IO FD info structures + +The reply message will additionally include at least one file descriptor in the +ancillary data. Note that more than one sub-region may share the same file +descriptor. + +Note that it is the client's responsibility to verify the requested values (for +example, that the requested offset does not exceed the region's bounds). + +Each sub-region given in the response has one of two possible structures, +depending whether *type* is ``VFIO_USER_IO_FD_TYPE_IOEVENTFD`` or +``VFIO_USER_IO_FD_TYPE_IOREGIONFD``: + +Sub-Region IO FD info format (ioeventfd) +"""""""""""""""""""""""""""""""""""""""" + ++-----------+--------+------+ +| Name | Offset | Size | ++===========+========+======+ +| offset | 0 | 8 | ++-----------+--------+------+ +| size | 8 | 8 | ++-----------+--------+------+ +| fd_index | 16 | 4 | ++-----------+--------+------+ +| type | 20 | 4 | ++-----------+--------+------+ +| flags | 24 | 4 | ++-----------+--------+------+ +| padding | 28 | 4 | ++-----------+--------+------+ +| datamatch | 32 | 8 | ++-----------+--------+------+ + +* *offset* is the offset of the start of the sub-region within the region + requested ("physical address offset" for the region) +* *size* is the length of the sub-region. This may be zero if the access size is + not relevant, which may allow for optimizations +* *fd_index* is the index in the ancillary data of the FD to use for ioeventfd + notification; it may be shared. +* *type* is ``VFIO_USER_IO_FD_TYPE_IOEVENTFD`` +* *flags* is any of: + + * ``KVM_IOEVENTFD_FLAG_DATAMATCH`` + * ``KVM_IOEVENTFD_FLAG_PIO`` + * ``KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY`` (FIXME: makes sense?) + +* *datamatch* is the datamatch value if needed + +See https://www.kernel.org/doc/Documentation/virtual/kvm/api.txt, *4.59 +KVM_IOEVENTFD* for further context on the ioeventfd-specific fields. + +Sub-Region IO FD info format (ioregionfd) +""""""""""""""""""""""""""""""""""""""""" + ++-----------+--------+------+ +| Name | Offset | Size | ++===========+========+======+ +| offset | 0 | 8 | ++-----------+--------+------+ +| size | 8 | 8 | ++-----------+--------+------+ +| fd_index | 16 | 4 | ++-----------+--------+------+ +| type | 20 | 4 | ++-----------+--------+------+ +| flags | 24 | 4 | ++-----------+--------+------+ +| padding | 28 | 4 | ++-----------+--------+------+ +| user_data | 32 | 8 | ++-----------+--------+------+ + +* *offset* is the offset of the start of the sub-region within the region + requested ("physical address offset" for the region) +* *size* is the length of the sub-region. This may be zero if the access size is + not relevant, which may allow for optimizations; ``KVM_IOREGION_POSTED_WRITES`` + must be set in *flags* in this case +* *fd_index* is the index in the ancillary data of the FD to use for ioregionfd + messages; it may be shared +* *type* is ``VFIO_USER_IO_FD_TYPE_IOREGIONFD`` +* *flags* is any of: + + * ``KVM_IOREGION_PIO`` + * ``KVM_IOREGION_POSTED_WRITES`` + +* *user_data* is an opaque value passed back to the server via a message on the + file descriptor + +For further information on the ioregionfd-specific fields, see: +https://lore.kernel.org/kvm/cover.1613828726.git.eafanasova@gmail.com/ + +(FIXME: update with final API docs.) + +``VFIO_USER_DEVICE_GET_IRQ_INFO`` +--------------------------------- + +This command message is sent by the client to the server to query for +information about device interrupt types. The VFIO IRQ info structure is +defined in ```` (``struct vfio_irq_info``). + +Request +^^^^^^^ + ++-------+--------+---------------------------+ +| Name | Offset | Size | ++=======+========+===========================+ +| argsz | 0 | 4 | ++-------+--------+---------------------------+ +| flags | 4 | 4 | ++-------+--------+---------------------------+ +| | +-----+--------------------------+ | +| | | Bit | Definition | | +| | +=====+==========================+ | +| | | 0 | VFIO_IRQ_INFO_EVENTFD | | +| | +-----+--------------------------+ | +| | | 1 | VFIO_IRQ_INFO_MASKABLE | | +| | +-----+--------------------------+ | +| | | 2 | VFIO_IRQ_INFO_AUTOMASKED | | +| | +-----+--------------------------+ | +| | | 3 | VFIO_IRQ_INFO_NORESIZE | | +| | +-----+--------------------------+ | ++-------+--------+---------------------------+ +| index | 8 | 4 | ++-------+--------+---------------------------+ +| count | 12 | 4 | ++-------+--------+---------------------------+ + +* *argsz* is the maximum size of the reply payload (16 bytes today) +* index is the index of IRQ type being queried (e.g. ``VFIO_PCI_MSIX_IRQ_INDEX``) +* all other fields must be zero + +Reply +^^^^^ + ++-------+--------+---------------------------+ +| Name | Offset | Size | ++=======+========+===========================+ +| argsz | 0 | 4 | ++-------+--------+---------------------------+ +| flags | 4 | 4 | ++-------+--------+---------------------------+ +| | +-----+--------------------------+ | +| | | Bit | Definition | | +| | +=====+==========================+ | +| | | 0 | VFIO_IRQ_INFO_EVENTFD | | +| | +-----+--------------------------+ | +| | | 1 | VFIO_IRQ_INFO_MASKABLE | | +| | +-----+--------------------------+ | +| | | 2 | VFIO_IRQ_INFO_AUTOMASKED | | +| | +-----+--------------------------+ | +| | | 3 | VFIO_IRQ_INFO_NORESIZE | | +| | +-----+--------------------------+ | ++-------+--------+---------------------------+ +| index | 8 | 4 | ++-------+--------+---------------------------+ +| count | 12 | 4 | ++-------+--------+---------------------------+ + +* *argsz* is the size required for the full reply payload (16 bytes today) +* *flags* defines IRQ attributes: + + * ``VFIO_IRQ_INFO_EVENTFD`` indicates the IRQ type can support server eventfd + signalling. + * ``VFIO_IRQ_INFO_MASKABLE`` indicates that the IRQ type supports the ``MASK`` + and ``UNMASK`` actions in a ``VFIO_USER_DEVICE_SET_IRQS`` message. + * ``VFIO_IRQ_INFO_AUTOMASKED`` indicates the IRQ type masks itself after being + triggered, and the client must send an ``UNMASK`` action to receive new + interrupts. + * ``VFIO_IRQ_INFO_NORESIZE`` indicates ``VFIO_USER_SET_IRQS`` operations setup + interrupts as a set, and new sub-indexes cannot be enabled without disabling + the entire type. +* index is the index of IRQ type being queried +* count describes the number of interrupts of the queried type. + +``VFIO_USER_DEVICE_SET_IRQS`` +----------------------------- + +This command message is sent by the client to the server to set actions for +device interrupt types. The VFIO IRQ set structure is defined in +```` (``struct vfio_irq_set``). + +Request +^^^^^^^ + ++-------+--------+------------------------------+ +| Name | Offset | Size | ++=======+========+==============================+ +| argsz | 0 | 4 | ++-------+--------+------------------------------+ +| flags | 4 | 4 | ++-------+--------+------------------------------+ +| | +-----+-----------------------------+ | +| | | Bit | Definition | | +| | +=====+=============================+ | +| | | 0 | VFIO_IRQ_SET_DATA_NONE | | +| | +-----+-----------------------------+ | +| | | 1 | VFIO_IRQ_SET_DATA_BOOL | | +| | +-----+-----------------------------+ | +| | | 2 | VFIO_IRQ_SET_DATA_EVENTFD | | +| | +-----+-----------------------------+ | +| | | 3 | VFIO_IRQ_SET_ACTION_MASK | | +| | +-----+-----------------------------+ | +| | | 4 | VFIO_IRQ_SET_ACTION_UNMASK | | +| | +-----+-----------------------------+ | +| | | 5 | VFIO_IRQ_SET_ACTION_TRIGGER | | +| | +-----+-----------------------------+ | ++-------+--------+------------------------------+ +| index | 8 | 4 | ++-------+--------+------------------------------+ +| start | 12 | 4 | ++-------+--------+------------------------------+ +| count | 16 | 4 | ++-------+--------+------------------------------+ +| data | 20 | variable | ++-------+--------+------------------------------+ + +* *argsz* is the size of the VFIO IRQ set request payload, including any *data* + field. Note there is no reply payload, so this field differs from other + message types. +* *flags* defines the action performed on the interrupt range. The ``DATA`` + flags describe the data field sent in the message; the ``ACTION`` flags + describe the action to be performed. The flags are mutually exclusive for + both sets. + + * ``VFIO_IRQ_SET_DATA_NONE`` indicates there is no data field in the command. + The action is performed unconditionally. + * ``VFIO_IRQ_SET_DATA_BOOL`` indicates the data field is an array of boolean + bytes. The action is performed if the corresponding boolean is true. + * ``VFIO_IRQ_SET_DATA_EVENTFD`` indicates an array of event file descriptors + was sent in the message meta-data. These descriptors will be signalled when + the action defined by the action flags occurs. In ``AF_UNIX`` sockets, the + descriptors are sent as ``SCM_RIGHTS`` type ancillary data. + If no file descriptors are provided, this de-assigns the specified + previously configured interrupts. + * ``VFIO_IRQ_SET_ACTION_MASK`` indicates a masking event. It can be used with + ``VFIO_IRQ_SET_DATA_BOOL`` or ``VFIO_IRQ_SET_DATA_NONE`` to mask an interrupt, + or with ``VFIO_IRQ_SET_DATA_EVENTFD`` to generate an event when the guest masks + the interrupt. + * ``VFIO_IRQ_SET_ACTION_UNMASK`` indicates an unmasking event. It can be used + with ``VFIO_IRQ_SET_DATA_BOOL`` or ``VFIO_IRQ_SET_DATA_NONE`` to unmask an + interrupt, or with ``VFIO_IRQ_SET_DATA_EVENTFD`` to generate an event when the + guest unmasks the interrupt. + * ``VFIO_IRQ_SET_ACTION_TRIGGER`` indicates a triggering event. It can be used + with ``VFIO_IRQ_SET_DATA_BOOL`` or ``VFIO_IRQ_SET_DATA_NONE`` to trigger an + interrupt, or with ``VFIO_IRQ_SET_DATA_EVENTFD`` to generate an event when the + server triggers the interrupt. + +* *index* is the index of IRQ type being setup. +* *start* is the start of the sub-index being set. +* *count* describes the number of sub-indexes being set. As a special case, a + count (and start) of 0, with data flags of ``VFIO_IRQ_SET_DATA_NONE`` disables + all interrupts of the index. +* *data* is an optional field included when the + ``VFIO_IRQ_SET_DATA_BOOL`` flag is present. It contains an array of booleans + that specify whether the action is to be performed on the corresponding + index. It's used when the action is only performed on a subset of the range + specified. + +Not all interrupt types support every combination of data and action flags. +The client must know the capabilities of the device and IRQ index before it +sends a ``VFIO_USER_DEVICE_SET_IRQ`` message. + +In typical operation, a specific IRQ may operate as follows: + +1. The client sends a ``VFIO_USER_DEVICE_SET_IRQ`` message with + ``flags=(VFIO_IRQ_SET_DATA_EVENTFD|VFIO_IRQ_SET_ACTION_TRIGGER)`` along + with an eventfd. This associates the IRQ with a particular eventfd on the + server side. + +#. The client may send a ``VFIO_USER_DEVICE_SET_IRQ`` message with + ``flags=(VFIO_IRQ_SET_DATA_EVENTFD|VFIO_IRQ_SET_ACTION_MASK/UNMASK)`` along + with another eventfd. This associates the given eventfd with the + mask/unmask state on the server side. + +#. The server may trigger the IRQ by writing 1 to the eventfd. + +#. The server may mask/unmask an IRQ which will write 1 to the corresponding + mask/unmask eventfd, if there is one. + +5. A client may trigger a device IRQ itself, by sending a + ``VFIO_USER_DEVICE_SET_IRQ`` message with + ``flags=(VFIO_IRQ_SET_DATA_NONE/BOOL|VFIO_IRQ_SET_ACTION_TRIGGER)``. + +6. A client may mask or unmask the IRQ, by sending a + ``VFIO_USER_DEVICE_SET_IRQ`` message with + ``flags=(VFIO_IRQ_SET_DATA_NONE/BOOL|VFIO_IRQ_SET_ACTION_MASK/UNMASK)``. + +Reply +^^^^^ + +There is no payload in the reply. + +.. _Read and Write Operations: + +Note that all of these operations must be supported by the client and/or server, +even if the corresponding memory or device region has been shared as mappable. + +The ``count`` field must not exceed the value of ``max_data_xfer_size`` of the +peer, for both reads and writes. + +``VFIO_USER_REGION_READ`` +------------------------- + +If a device region is not mappable, it's not directly accessible by the client +via ``mmap()`` of the underlying file descriptor. In this case, a client can +read from a device region with this message. + +Request +^^^^^^^ + ++--------+--------+----------+ +| Name | Offset | Size | ++========+========+==========+ +| offset | 0 | 8 | ++--------+--------+----------+ +| region | 8 | 4 | ++--------+--------+----------+ +| count | 12 | 4 | ++--------+--------+----------+ + +* *offset* into the region being accessed. +* *region* is the index of the region being accessed. +* *count* is the size of the data to be transferred. + +Reply +^^^^^ + ++--------+--------+----------+ +| Name | Offset | Size | ++========+========+==========+ +| offset | 0 | 8 | ++--------+--------+----------+ +| region | 8 | 4 | ++--------+--------+----------+ +| count | 12 | 4 | ++--------+--------+----------+ +| data | 16 | variable | ++--------+--------+----------+ + +* *offset* into the region accessed. +* *region* is the index of the region accessed. +* *count* is the size of the data transferred. +* *data* is the data that was read from the device region. + +``VFIO_USER_REGION_WRITE`` +-------------------------- + +If a device region is not mappable, it's not directly accessible by the client +via mmap() of the underlying fd. In this case, a client can write to a device +region with this message. + +Request +^^^^^^^ + ++--------+--------+----------+ +| Name | Offset | Size | ++========+========+==========+ +| offset | 0 | 8 | ++--------+--------+----------+ +| region | 8 | 4 | ++--------+--------+----------+ +| count | 12 | 4 | ++--------+--------+----------+ +| data | 16 | variable | ++--------+--------+----------+ + +* *offset* into the region being accessed. +* *region* is the index of the region being accessed. +* *count* is the size of the data to be transferred. +* *data* is the data to write + +Reply +^^^^^ + ++--------+--------+----------+ +| Name | Offset | Size | ++========+========+==========+ +| offset | 0 | 8 | ++--------+--------+----------+ +| region | 8 | 4 | ++--------+--------+----------+ +| count | 12 | 4 | ++--------+--------+----------+ + +* *offset* into the region accessed. +* *region* is the index of the region accessed. +* *count* is the size of the data transferred. + +``VFIO_USER_DMA_READ`` +----------------------- + +If the client has not shared mappable memory, the server can use this message to +read from guest memory. + +Request +^^^^^^^ + ++---------+--------+----------+ +| Name | Offset | Size | ++=========+========+==========+ +| address | 0 | 8 | ++---------+--------+----------+ +| count | 8 | 8 | ++---------+--------+----------+ + +* *address* is the client DMA memory address being accessed. This address must have + been previously exported to the server with a ``VFIO_USER_DMA_MAP`` message. +* *count* is the size of the data to be transferred. + +Reply +^^^^^ + ++---------+--------+----------+ +| Name | Offset | Size | ++=========+========+==========+ +| address | 0 | 8 | ++---------+--------+----------+ +| count | 8 | 8 | ++---------+--------+----------+ +| data | 16 | variable | ++---------+--------+----------+ + +* *address* is the client DMA memory address being accessed. +* *count* is the size of the data transferred. +* *data* is the data read. + +``VFIO_USER_DMA_WRITE`` +----------------------- + +If the client has not shared mappable memory, the server can use this message to +write to guest memory. + +Request +^^^^^^^ + ++---------+--------+----------+ +| Name | Offset | Size | ++=========+========+==========+ +| address | 0 | 8 | ++---------+--------+----------+ +| count | 8 | 8 | ++---------+--------+----------+ +| data | 16 | variable | ++---------+--------+----------+ + +* *address* is the client DMA memory address being accessed. This address must have + been previously exported to the server with a ``VFIO_USER_DMA_MAP`` message. +* *count* is the size of the data to be transferred. +* *data* is the data to write + +Reply +^^^^^ + ++---------+--------+----------+ +| Name | Offset | Size | ++=========+========+==========+ +| address | 0 | 8 | ++---------+--------+----------+ +| count | 8 | 4 | ++---------+--------+----------+ + +* *address* is the client DMA memory address being accessed. +* *count* is the size of the data transferred. + +``VFIO_USER_DEVICE_RESET`` +-------------------------- + +This command message is sent from the client to the server to reset the device. +Neither the request or reply have a payload. + +``VFIO_USER_REGION_WRITE_MULTI`` +-------------------------------- + +This message can be used to coalesce multiple device write operations +into a single messgage. It is only used as an optimization when the +outgoing message queue is relatively full. + +Request +^^^^^^^ + ++---------+--------+----------+ +| Name | Offset | Size | ++=========+========+==========+ +| wr_cnt | 0 | 8 | ++---------+--------+----------+ +| wrs | 8 | variable | ++---------+--------+----------+ + +* *wr_cnt* is the number of device writes coalesced in the message +* *wrs* is an array of device writes defined below + +Single Device Write Format +"""""""""""""""""""""""""" + ++--------+--------+----------+ +| Name | Offset | Size | ++========+========+==========+ +| offset | 0 | 8 | ++--------+--------+----------+ +| region | 8 | 4 | ++--------+--------+----------+ +| count | 12 | 4 | ++--------+--------+----------+ +| data | 16 | 8 | ++--------+--------+----------+ + +* *offset* into the region being accessed. +* *region* is the index of the region being accessed. +* *count* is the size of the data to be transferred. This format can + only describe writes of 8 bytes or less. +* *data* is the data to write. + +Reply +^^^^^ + ++---------+--------+----------+ +| Name | Offset | Size | ++=========+========+==========+ +| wr_cnt | 0 | 8 | ++---------+--------+----------+ + +* *wr_cnt* is the number of device writes completed. + + +Appendices +========== + +Unused VFIO ``ioctl()`` commands +-------------------------------- + +The following VFIO commands do not have an equivalent vfio-user command: + +* ``VFIO_GET_API_VERSION`` +* ``VFIO_CHECK_EXTENSION`` +* ``VFIO_SET_IOMMU`` +* ``VFIO_GROUP_GET_STATUS`` +* ``VFIO_GROUP_SET_CONTAINER`` +* ``VFIO_GROUP_UNSET_CONTAINER`` +* ``VFIO_GROUP_GET_DEVICE_FD`` +* ``VFIO_IOMMU_GET_INFO`` + +However, once support for live migration for VFIO devices is finalized some +of the above commands may have to be handled by the client in their +corresponding vfio-user form. This will be addressed in a future protocol +version. + +VFIO groups and containers +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The current VFIO implementation includes group and container idioms that +describe how a device relates to the host IOMMU. In the vfio-user +implementation, the IOMMU is implemented in SW by the client, and is not +visible to the server. The simplest idea would be that the client put each +device into its own group and container. + +Backend Program Conventions +--------------------------- + +vfio-user backend program conventions are based on the vhost-user ones. + +* The backend program must not daemonize itself. +* No assumptions must be made as to what access the backend program has on the + system. +* File descriptors 0, 1 and 2 must exist, must have regular + stdin/stdout/stderr semantics, and can be redirected. +* The backend program must honor the SIGTERM signal. +* The backend program must accept the following commands line options: + + * ``--socket-path=PATH``: path to UNIX domain socket, + * ``--fd=FDNUM``: file descriptor for UNIX domain socket, incompatible with + ``--socket-path`` +* The backend program must be accompanied with a JSON file stored under + ``/usr/share/vfio-user``. + +TODO add schema similar to docs/interop/vhost-user.json. From 776bd01809fe0f524a578257ff82787d067dbe93 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Fri, 20 Jun 2025 13:21:40 +0300 Subject: [PATCH 1693/2760] MAINTAINERS: Update Kostiantyn Kostiuk transliteration Reviewed-by: Yan Vugenfirer Message-ID: <20250620102140.38556-1-kkostiuk@redhat.com> Signed-off-by: Kostiantyn Kostiuk --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index d1672fda8d..4cfb587a0d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3383,7 +3383,7 @@ T: git https://repo.or.cz/qemu/armbru.git qapi-next QEMU Guest Agent M: Michael Roth -M: Konstantin Kostiuk +M: Kostiantyn Kostiuk S: Maintained F: qga/ F: contrib/systemd/qemu-guest-agent.service @@ -3394,7 +3394,7 @@ F: tests/*/test-qga* T: git https://github.com/mdroth/qemu.git qga QEMU Guest Agent Win32 -M: Konstantin Kostiuk +M: Kostiantyn Kostiuk S: Maintained F: qga/*win32* F: qga/vss-win32/ From 012bb70cd16512dbcc1ba423c3f7f260e177afe7 Mon Sep 17 00:00:00 2001 From: Kostiantyn Kostiuk Date: Fri, 20 Jun 2025 11:31:32 +0300 Subject: [PATCH 1694/2760] qga-vss: Exit with non-zero code when register fail QGA installer uses rundll32 to run the DLLCOMRegister function from qga-vss.dll and perform VSS provider registration. rundll32 ignores the return value of the function and always exits with a zero exit code. This causes a situation where the installer does not know the status of VSS provider registration. This commit forces to change exit code when the VSS provider registration fails. https://learn.microsoft.com/en-us/windows-server/administration/windows-commands/rundll32 Reviewed-by: Yan Vugenfirer Tested-by: Dehan Meng Message-ID: <20250620083132.28347-1-kkostiuk@redhat.com> Signed-off-by: Kostiantyn Kostiuk --- qga/vss-win32/install.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp index 5cea5bcf74..6ee2f44a10 100644 --- a/qga/vss-win32/install.cpp +++ b/qga/vss-win32/install.cpp @@ -385,7 +385,10 @@ STDAPI COMRegister(void) STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int); STDAPI_(void) CALLBACK DLLCOMRegister(HWND, HINSTANCE, LPSTR, int) { - COMRegister(); + HRESULT hr = COMRegister(); + if (FAILED(hr)) { + exit(hr); + } } STDAPI_(void) CALLBACK DLLCOMUnregister(HWND, HINSTANCE, LPSTR, int); From 1c90e89e64beb2bd72f8e437c56274c885df7b3f Mon Sep 17 00:00:00 2001 From: Elizabeth Ashurov Date: Wed, 18 Jun 2025 12:18:06 +0300 Subject: [PATCH 1695/2760] qga/vss-win32: Add VSS provider unregistration retry This commit improves the QGA VSS provider installation flow by attempting to unregister the VSS provider if it's already found during installation. This allows for a retry of installation even if a previous unregistration failed or was not performed. This will prevent inconsistencies between QGA and QGA-VSS versions. Before this commit, QGA can use QGA-VSS from the previous installation. Signed-off-by: Elizabeth Ashurov Reviewed-by: Kostiantyn Kostiuk Message-ID: <20250618091806.170110-1-eashurov@redhat.com> Signed-off-by: Kostiantyn Kostiuk --- qga/vss-win32/install.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/qga/vss-win32/install.cpp b/qga/vss-win32/install.cpp index 6ee2f44a10..7b25d9098b 100644 --- a/qga/vss-win32/install.cpp +++ b/qga/vss-win32/install.cpp @@ -287,9 +287,13 @@ STDAPI COMRegister(void) chk(QGAProviderFind(QGAProviderCount, (void *)&count)); if (count) { - errmsg(E_ABORT, "QGA VSS Provider is already installed"); - qga_debug_end; - return E_ABORT; + qga_debug("QGA VSS Provider is already installed. Attempting to unregister first."); + hr = COMUnregister(); + if (FAILED(hr)) { + errmsg(hr, "Failed to unregister existing QGA VSS Provider. Aborting installation."); + qga_debug_end; + return E_ABORT; + } } chk(CoCreateInstance(CLSID_COMAdminCatalog, NULL, CLSCTX_INPROC_SERVER, From c1fa1b30ec5da0659cf071481f19ed64c964ea6b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 17 Feb 2025 15:17:47 -0800 Subject: [PATCH 1696/2760] tcg/optimize: Introduce arg_const_val Use arg_const_val instead of direct access to the TempOptInfo val member. Rename both val and is_const to catch all direct accesses. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 78 ++++++++++++++++++++++++++------------------------ 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 10a76c5461..73a272eeb3 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -39,11 +39,11 @@ typedef struct MemCopyInfo { } MemCopyInfo; typedef struct TempOptInfo { - bool is_const; + bool is_const_; TCGTemp *prev_copy; TCGTemp *next_copy; QSIMPLEQ_HEAD(, MemCopyInfo) mem_copy; - uint64_t val; + uint64_t val_; uint64_t z_mask; /* mask bit is 0 if and only if value bit is 0 */ uint64_t s_mask; /* mask bit is 1 if value bit matches msb */ } TempOptInfo; @@ -73,12 +73,12 @@ static inline TempOptInfo *arg_info(TCGArg arg) static inline bool ti_is_const(TempOptInfo *ti) { - return ti->is_const; + return ti->is_const_; } static inline uint64_t ti_const_val(TempOptInfo *ti) { - return ti->val; + return ti->val_; } static inline bool ti_is_const_val(TempOptInfo *ti, uint64_t val) @@ -101,6 +101,11 @@ static inline bool arg_is_const(TCGArg arg) return ts_is_const(arg_temp(arg)); } +static inline uint64_t arg_const_val(TCGArg arg) +{ + return ti_const_val(arg_info(arg)); +} + static inline bool arg_is_const_val(TCGArg arg, uint64_t val) { return ts_is_const_val(arg_temp(arg), val); @@ -137,12 +142,12 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts) ti->prev_copy = ts; QSIMPLEQ_INIT(&ti->mem_copy); if (ts->kind == TEMP_CONST) { - ti->is_const = true; - ti->val = ts->val; + ti->is_const_ = true; + ti->val_ = ts->val; ti->z_mask = ts->val; ti->s_mask = INT64_MIN >> clrsb64(ts->val); } else { - ti->is_const = false; + ti->is_const_ = false; ti->z_mask = -1; ti->s_mask = 0; } @@ -229,7 +234,7 @@ static void reset_ts(OptContext *ctx, TCGTemp *ts) pi->next_copy = ti->next_copy; ti->next_copy = ts; ti->prev_copy = ts; - ti->is_const = false; + ti->is_const_ = false; ti->z_mask = -1; ti->s_mask = 0; @@ -394,8 +399,8 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) di->prev_copy = src_ts; ni->prev_copy = dst_ts; si->next_copy = dst_ts; - di->is_const = si->is_const; - di->val = si->val; + di->is_const_ = si->is_const_; + di->val_ = si->val_; if (!QSIMPLEQ_EMPTY(&si->mem_copy) && cmp_better_copy(src_ts, dst_ts) == dst_ts) { @@ -687,8 +692,8 @@ static int do_constant_folding_cond(TCGType type, TCGArg x, TCGArg y, TCGCond c) { if (arg_is_const(x) && arg_is_const(y)) { - uint64_t xv = arg_info(x)->val; - uint64_t yv = arg_info(y)->val; + uint64_t xv = arg_const_val(x); + uint64_t yv = arg_const_val(y); switch (type) { case TCG_TYPE_I32: @@ -801,14 +806,14 @@ static int do_constant_folding_cond1(OptContext *ctx, TCGOp *op, TCGArg dest, * TSTNE x,i -> NE x,0 if i includes all nonzero bits of x */ if (args_are_copies(*p1, *p2) || - (arg_is_const(*p2) && (i1->z_mask & ~arg_info(*p2)->val) == 0)) { + (arg_is_const(*p2) && (i1->z_mask & ~arg_const_val(*p2)) == 0)) { *p2 = arg_new_constant(ctx, 0); *pcond = tcg_tst_eqne_cond(cond); return -1; } /* TSTNE x,i -> LT x,0 if i only includes sign bit copies */ - if (arg_is_const(*p2) && (arg_info(*p2)->val & ~i1->s_mask) == 0) { + if (arg_is_const(*p2) && (arg_const_val(*p2) & ~i1->s_mask) == 0) { *p2 = arg_new_constant(ctx, 0); *pcond = tcg_tst_ltge_cond(cond); return -1; @@ -849,13 +854,13 @@ static int do_constant_folding_cond2(OptContext *ctx, TCGOp *op, TCGArg *args) bh = args[3]; if (arg_is_const(bl) && arg_is_const(bh)) { - tcg_target_ulong blv = arg_info(bl)->val; - tcg_target_ulong bhv = arg_info(bh)->val; + tcg_target_ulong blv = arg_const_val(bl); + tcg_target_ulong bhv = arg_const_val(bh); uint64_t b = deposit64(blv, 32, 32, bhv); if (arg_is_const(al) && arg_is_const(ah)) { - tcg_target_ulong alv = arg_info(al)->val; - tcg_target_ulong ahv = arg_info(ah)->val; + tcg_target_ulong alv = arg_const_val(al); + tcg_target_ulong ahv = arg_const_val(ah); uint64_t a = deposit64(alv, 32, 32, ahv); r = do_constant_folding_cond_64(a, b, c); @@ -989,9 +994,8 @@ static bool finish_folding(OptContext *ctx, TCGOp *op) static bool fold_const1(OptContext *ctx, TCGOp *op) { if (arg_is_const(op->args[1])) { - uint64_t t; + uint64_t t = arg_const_val(op->args[1]); - t = arg_info(op->args[1])->val; t = do_constant_folding(op->opc, ctx->type, t, 0); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } @@ -1001,8 +1005,8 @@ static bool fold_const1(OptContext *ctx, TCGOp *op) static bool fold_const2(OptContext *ctx, TCGOp *op) { if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { - uint64_t t1 = arg_info(op->args[1])->val; - uint64_t t2 = arg_info(op->args[2])->val; + uint64_t t1 = arg_const_val(op->args[1]); + uint64_t t2 = arg_const_val(op->args[2]); t1 = do_constant_folding(op->opc, ctx->type, t1, t2); return tcg_opt_gen_movi(ctx, op, op->args[0], t1); @@ -1486,8 +1490,8 @@ static bool fold_bitsel_vec(OptContext *ctx, TCGOp *op) } if (arg_is_const(op->args[2]) && arg_is_const(op->args[3])) { - uint64_t tv = arg_info(op->args[2])->val; - uint64_t fv = arg_info(op->args[3])->val; + uint64_t tv = arg_const_val(op->args[2]); + uint64_t fv = arg_const_val(op->args[3]); if (tv == -1 && fv == 0) { return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); @@ -1504,7 +1508,7 @@ static bool fold_bitsel_vec(OptContext *ctx, TCGOp *op) } } if (arg_is_const(op->args[2])) { - uint64_t tv = arg_info(op->args[2])->val; + uint64_t tv = arg_const_val(op->args[2]); if (tv == -1) { op->opc = INDEX_op_or_vec; op->args[2] = op->args[3]; @@ -1518,7 +1522,7 @@ static bool fold_bitsel_vec(OptContext *ctx, TCGOp *op) } } if (arg_is_const(op->args[3])) { - uint64_t fv = arg_info(op->args[3])->val; + uint64_t fv = arg_const_val(op->args[3]); if (fv == 0) { op->opc = INDEX_op_and_vec; return fold_and(ctx, op); @@ -1876,7 +1880,7 @@ static bool fold_divide(OptContext *ctx, TCGOp *op) static bool fold_dup(OptContext *ctx, TCGOp *op) { if (arg_is_const(op->args[1])) { - uint64_t t = arg_info(op->args[1])->val; + uint64_t t = arg_const_val(op->args[1]); t = dup_const(TCGOP_VECE(op), t); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } @@ -1886,8 +1890,8 @@ static bool fold_dup(OptContext *ctx, TCGOp *op) static bool fold_dup2(OptContext *ctx, TCGOp *op) { if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { - uint64_t t = deposit64(arg_info(op->args[1])->val, 32, 32, - arg_info(op->args[2])->val); + uint64_t t = deposit64(arg_const_val(op->args[1]), 32, 32, + arg_const_val(op->args[2])); return tcg_opt_gen_movi(ctx, op, op->args[0], t); } @@ -1958,8 +1962,8 @@ static bool fold_extract(OptContext *ctx, TCGOp *op) static bool fold_extract2(OptContext *ctx, TCGOp *op) { if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { - uint64_t v1 = arg_info(op->args[1])->val; - uint64_t v2 = arg_info(op->args[2])->val; + uint64_t v1 = arg_const_val(op->args[1]); + uint64_t v2 = arg_const_val(op->args[2]); int shr = op->args[3]; if (ctx->type == TCG_TYPE_I32) { @@ -2127,8 +2131,8 @@ static bool fold_multiply2(OptContext *ctx, TCGOp *op) swap_commutative(op->args[0], &op->args[2], &op->args[3]); if (arg_is_const(op->args[2]) && arg_is_const(op->args[3])) { - uint64_t a = arg_info(op->args[2])->val; - uint64_t b = arg_info(op->args[3])->val; + uint64_t a = arg_const_val(op->args[2]); + uint64_t b = arg_const_val(op->args[3]); uint64_t h, l; TCGArg rl, rh; TCGOp *op2; @@ -2330,7 +2334,7 @@ static int fold_setcond_zmask(OptContext *ctx, TCGOp *op, bool neg) } a_zmask = arg_info(op->args[1])->z_mask; - b_val = arg_info(op->args[2])->val; + b_val = arg_const_val(op->args[2]); cond = op->args[3]; if (ctx->type == TCG_TYPE_I32) { @@ -2418,7 +2422,7 @@ static void fold_setcond_tst_pow2(OptContext *ctx, TCGOp *op, bool neg) } src2 = op->args[2]; - val = arg_info(src2)->val; + val = arg_const_val(src2); if (!is_power_of_2(val)) { return; } @@ -2669,7 +2673,7 @@ static bool fold_sub_to_neg(OptContext *ctx, TCGOp *op) TCGOpcode neg_op; bool have_neg; - if (!arg_is_const(op->args[1]) || arg_info(op->args[1])->val != 0) { + if (!arg_is_const_val(op->args[1], 0)) { return false; } @@ -2719,7 +2723,7 @@ static bool fold_sub(OptContext *ctx, TCGOp *op) /* Fold sub r,x,i to add r,x,-i */ if (arg_is_const(op->args[2])) { - uint64_t val = arg_info(op->args[2])->val; + uint64_t val = arg_const_val(op->args[2]); op->opc = INDEX_op_add; op->args[2] = arg_new_constant(ctx, -val); From 56f15f67ea116e96ea986a7cca9214f76af71433 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 22 Dec 2024 15:07:31 -0800 Subject: [PATCH 1697/2760] tcg/optimize: Add one's mask to TempOptInfo Add o_mask mirroring z_mask, but for 1's instead of 0's. Drop is_const and val fields, which now logically overlap. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 51 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 73a272eeb3..ce3cb4d7bc 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -39,12 +39,11 @@ typedef struct MemCopyInfo { } MemCopyInfo; typedef struct TempOptInfo { - bool is_const_; TCGTemp *prev_copy; TCGTemp *next_copy; QSIMPLEQ_HEAD(, MemCopyInfo) mem_copy; - uint64_t val_; uint64_t z_mask; /* mask bit is 0 if and only if value bit is 0 */ + uint64_t o_mask; /* mask bit is 1 if and only if value bit is 1 */ uint64_t s_mask; /* mask bit is 1 if value bit matches msb */ } TempOptInfo; @@ -73,12 +72,14 @@ static inline TempOptInfo *arg_info(TCGArg arg) static inline bool ti_is_const(TempOptInfo *ti) { - return ti->is_const_; + /* If all bits that are not known zeros are known ones, it's constant. */ + return ti->z_mask == ti->o_mask; } static inline uint64_t ti_const_val(TempOptInfo *ti) { - return ti->val_; + /* If constant, both z_mask and o_mask contain the value. */ + return ti->z_mask; } static inline bool ti_is_const_val(TempOptInfo *ti, uint64_t val) @@ -142,13 +143,12 @@ static void init_ts_info(OptContext *ctx, TCGTemp *ts) ti->prev_copy = ts; QSIMPLEQ_INIT(&ti->mem_copy); if (ts->kind == TEMP_CONST) { - ti->is_const_ = true; - ti->val_ = ts->val; ti->z_mask = ts->val; + ti->o_mask = ts->val; ti->s_mask = INT64_MIN >> clrsb64(ts->val); } else { - ti->is_const_ = false; ti->z_mask = -1; + ti->o_mask = 0; ti->s_mask = 0; } } @@ -234,8 +234,8 @@ static void reset_ts(OptContext *ctx, TCGTemp *ts) pi->next_copy = ti->next_copy; ti->next_copy = ts; ti->prev_copy = ts; - ti->is_const_ = false; ti->z_mask = -1; + ti->o_mask = 0; ti->s_mask = 0; if (!QSIMPLEQ_EMPTY(&ti->mem_copy)) { @@ -390,6 +390,7 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) op->args[1] = src; di->z_mask = si->z_mask; + di->o_mask = si->o_mask; di->s_mask = si->s_mask; if (src_ts->type == dst_ts->type) { @@ -399,13 +400,19 @@ static bool tcg_opt_gen_mov(OptContext *ctx, TCGOp *op, TCGArg dst, TCGArg src) di->prev_copy = src_ts; ni->prev_copy = dst_ts; si->next_copy = dst_ts; - di->is_const_ = si->is_const_; - di->val_ = si->val_; if (!QSIMPLEQ_EMPTY(&si->mem_copy) && cmp_better_copy(src_ts, dst_ts) == dst_ts) { move_mem_copies(dst_ts, src_ts); } + } else if (dst_ts->type == TCG_TYPE_I32) { + di->z_mask = (int32_t)di->z_mask; + di->o_mask = (int32_t)di->o_mask; + di->s_mask |= INT32_MIN; + } else { + di->z_mask |= MAKE_64BIT_MASK(32, 32); + di->o_mask = (uint32_t)di->o_mask; + di->s_mask = INT64_MIN; } return true; } @@ -1032,8 +1039,8 @@ static bool fold_const2_commutative(OptContext *ctx, TCGOp *op) * If z_mask allows, fold the output to constant zero. * The passed s_mask may be augmented by z_mask. */ -static bool fold_masks_zs(OptContext *ctx, TCGOp *op, - uint64_t z_mask, int64_t s_mask) +static bool fold_masks_zos(OptContext *ctx, TCGOp *op, uint64_t z_mask, + uint64_t o_mask, int64_t s_mask) { const TCGOpDef *def = &tcg_op_defs[op->opc]; TCGTemp *ts; @@ -1052,11 +1059,16 @@ static bool fold_masks_zs(OptContext *ctx, TCGOp *op, */ if (ctx->type == TCG_TYPE_I32) { z_mask = (int32_t)z_mask; + o_mask = (int32_t)o_mask; s_mask |= INT32_MIN; } - if (z_mask == 0) { - return tcg_opt_gen_movi(ctx, op, op->args[0], 0); + /* Bits that are known 1 and bits that are known 0 must not overlap. */ + tcg_debug_assert((o_mask & ~z_mask) == 0); + + /* All bits that are not known zero are known one is a constant. */ + if (z_mask == o_mask) { + return tcg_opt_gen_movi(ctx, op, op->args[0], o_mask); } ts = arg_temp(op->args[0]); @@ -1068,20 +1080,27 @@ static bool fold_masks_zs(OptContext *ctx, TCGOp *op, /* Canonicalize s_mask and incorporate data from z_mask. */ rep = clz64(~s_mask); rep = MAX(rep, clz64(z_mask)); + rep = MAX(rep, clz64(~o_mask)); rep = MAX(rep - 1, 0); ti->s_mask = INT64_MIN >> rep; return true; } +static bool fold_masks_zs(OptContext *ctx, TCGOp *op, + uint64_t z_mask, uint64_t s_mask) +{ + return fold_masks_zos(ctx, op, z_mask, 0, s_mask); +} + static bool fold_masks_z(OptContext *ctx, TCGOp *op, uint64_t z_mask) { - return fold_masks_zs(ctx, op, z_mask, 0); + return fold_masks_zos(ctx, op, z_mask, 0, 0); } static bool fold_masks_s(OptContext *ctx, TCGOp *op, uint64_t s_mask) { - return fold_masks_zs(ctx, op, -1, s_mask); + return fold_masks_zos(ctx, op, -1, 0, s_mask); } /* From 9e397cc0df9c642381e60f97ce37eafe5a2582b3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 2 Jun 2025 14:11:51 +0100 Subject: [PATCH 1698/2760] tcg/optimize: Introduce fold_masks_zosa Add a new function with an affected mask. This will allow folding to a constant to happen before folding to a copy, without having to mind the ordering in all users. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index ce3cb4d7bc..49ef039932 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1039,8 +1039,8 @@ static bool fold_const2_commutative(OptContext *ctx, TCGOp *op) * If z_mask allows, fold the output to constant zero. * The passed s_mask may be augmented by z_mask. */ -static bool fold_masks_zos(OptContext *ctx, TCGOp *op, uint64_t z_mask, - uint64_t o_mask, int64_t s_mask) +static bool fold_masks_zosa(OptContext *ctx, TCGOp *op, uint64_t z_mask, + uint64_t o_mask, int64_t s_mask, uint64_t a_mask) { const TCGOpDef *def = &tcg_op_defs[op->opc]; TCGTemp *ts; @@ -1061,6 +1061,7 @@ static bool fold_masks_zos(OptContext *ctx, TCGOp *op, uint64_t z_mask, z_mask = (int32_t)z_mask; o_mask = (int32_t)o_mask; s_mask |= INT32_MIN; + a_mask = (uint32_t)a_mask; } /* Bits that are known 1 and bits that are known 0 must not overlap. */ @@ -1071,6 +1072,11 @@ static bool fold_masks_zos(OptContext *ctx, TCGOp *op, uint64_t z_mask, return tcg_opt_gen_movi(ctx, op, op->args[0], o_mask); } + /* If no bits are affected, the operation devolves to a copy. */ + if (a_mask == 0) { + return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); + } + ts = arg_temp(op->args[0]); reset_ts(ctx, ts); @@ -1090,17 +1096,17 @@ static bool fold_masks_zos(OptContext *ctx, TCGOp *op, uint64_t z_mask, static bool fold_masks_zs(OptContext *ctx, TCGOp *op, uint64_t z_mask, uint64_t s_mask) { - return fold_masks_zos(ctx, op, z_mask, 0, s_mask); + return fold_masks_zosa(ctx, op, z_mask, 0, s_mask, -1); } static bool fold_masks_z(OptContext *ctx, TCGOp *op, uint64_t z_mask) { - return fold_masks_zos(ctx, op, z_mask, 0, 0); + return fold_masks_zosa(ctx, op, z_mask, 0, 0, -1); } static bool fold_masks_s(OptContext *ctx, TCGOp *op, uint64_t s_mask) { - return fold_masks_zos(ctx, op, -1, 0, s_mask); + return fold_masks_zosa(ctx, op, -1, 0, s_mask, -1); } /* From 1e2edf85cc4755c844f9c98cbe52ac63d7d1fb18 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 16:48:36 -0600 Subject: [PATCH 1699/2760] tcg/optimize: Build and use o_bits in fold_and Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 20 +++++++------------- 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 49ef039932..d9ccbb36e2 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1422,7 +1422,7 @@ static bool fold_addco(OptContext *ctx, TCGOp *op) static bool fold_and(OptContext *ctx, TCGOp *op) { - uint64_t z1, z2, z_mask, s_mask; + uint64_t z_mask, o_mask, s_mask, a_mask; TempOptInfo *t1, *t2; if (fold_const2_commutative(ctx, op) || @@ -1434,18 +1434,9 @@ static bool fold_and(OptContext *ctx, TCGOp *op) t1 = arg_info(op->args[1]); t2 = arg_info(op->args[2]); - z1 = t1->z_mask; - z2 = t2->z_mask; - /* - * Known-zeros does not imply known-ones. Therefore unless - * arg2 is constant, we can't infer affected bits from it. - */ - if (ti_is_const(t2) && fold_affected_mask(ctx, op, z1 & ~z2)) { - return true; - } - - z_mask = z1 & z2; + z_mask = t1->z_mask & t2->z_mask; + o_mask = t1->o_mask & t2->o_mask; /* * Sign repetitions are perforce all identical, whether they are 1 or 0. @@ -1453,7 +1444,10 @@ static bool fold_and(OptContext *ctx, TCGOp *op) */ s_mask = t1->s_mask & t2->s_mask; - return fold_masks_zs(ctx, op, z_mask, s_mask); + /* Affected bits are those not known zero, masked by those known one. */ + a_mask = t1->z_mask & ~t2->o_mask; + + return fold_masks_zosa(ctx, op, z_mask, o_mask, s_mask, a_mask); } static bool fold_andc(OptContext *ctx, TCGOp *op) From d4d441e3a1c9f485a27662890ec7c009a056fda8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sun, 22 Dec 2024 16:08:42 -0800 Subject: [PATCH 1700/2760] tcg/optimize: Build and use o_bits in fold_andc Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index d9ccbb36e2..123734b167 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1452,7 +1452,7 @@ static bool fold_and(OptContext *ctx, TCGOp *op) static bool fold_andc(OptContext *ctx, TCGOp *op) { - uint64_t z_mask, s_mask; + uint64_t z_mask, o_mask, s_mask, a_mask; TempOptInfo *t1, *t2; if (fold_const2(ctx, op) || @@ -1464,7 +1464,6 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) t1 = arg_info(op->args[1]); t2 = arg_info(op->args[2]); - z_mask = t1->z_mask; if (ti_is_const(t2)) { /* Fold andc r,x,i to and r,x,~i. */ @@ -1485,20 +1484,14 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) return fold_and(ctx, op); } - /* - * Known-zeros does not imply known-ones. Therefore unless - * arg2 is constant, we can't infer anything from it. - */ - if (ti_is_const(t2)) { - uint64_t v2 = ti_const_val(t2); - if (fold_affected_mask(ctx, op, z_mask & v2)) { - return true; - } - z_mask &= ~v2; - } - + z_mask = t1->z_mask & ~t2->o_mask; + o_mask = t1->o_mask & ~t2->z_mask; s_mask = t1->s_mask & t2->s_mask; - return fold_masks_zs(ctx, op, z_mask, s_mask); + + /* Affected bits are those not known zero, masked by those known zero. */ + a_mask = t1->z_mask & t2->z_mask; + + return fold_masks_zosa(ctx, op, z_mask, o_mask, s_mask, a_mask); } static bool fold_bitsel_vec(OptContext *ctx, TCGOp *op) From 33fcebadd04ab420a485f864bda651b8b360472d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 08:26:56 -0600 Subject: [PATCH 1701/2760] tcg/optimize: Build and use z_bits and o_bits in fold_eqv Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 123734b167..6d35a2e58b 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1093,6 +1093,12 @@ static bool fold_masks_zosa(OptContext *ctx, TCGOp *op, uint64_t z_mask, return true; } +static bool fold_masks_zos(OptContext *ctx, TCGOp *op, + uint64_t z_mask, uint64_t o_mask, uint64_t s_mask) +{ + return fold_masks_zosa(ctx, op, z_mask, o_mask, s_mask, -1); +} + static bool fold_masks_zs(OptContext *ctx, TCGOp *op, uint64_t z_mask, uint64_t s_mask) { @@ -1916,7 +1922,7 @@ static bool fold_dup2(OptContext *ctx, TCGOp *op) static bool fold_eqv(OptContext *ctx, TCGOp *op) { - uint64_t s_mask; + uint64_t z_mask, o_mask, s_mask; TempOptInfo *t1, *t2; if (fold_const2_commutative(ctx, op) || @@ -1946,8 +1952,12 @@ static bool fold_eqv(OptContext *ctx, TCGOp *op) } t1 = arg_info(op->args[1]); + + z_mask = (t1->z_mask | ~t2->o_mask) & (t2->z_mask | ~t1->o_mask); + o_mask = ~(t1->z_mask | t2->z_mask) | (t1->o_mask & t2->o_mask); s_mask = t1->s_mask & t2->s_mask; - return fold_masks_s(ctx, op, s_mask); + + return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); } static bool fold_extract(OptContext *ctx, TCGOp *op) From 16559c3ecbacab16d471a4a6f5a35f14325c3fe4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 18:13:15 -0600 Subject: [PATCH 1702/2760] tcg/optimize: Build and use z_bits and o_bits in fold_nand Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 6d35a2e58b..758f7b142e 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2197,16 +2197,22 @@ static bool fold_multiply2(OptContext *ctx, TCGOp *op) static bool fold_nand(OptContext *ctx, TCGOp *op) { - uint64_t s_mask; + uint64_t z_mask, o_mask, s_mask; + TempOptInfo *t1, *t2; if (fold_const2_commutative(ctx, op) || fold_xi_to_not(ctx, op, -1)) { return true; } - s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return fold_masks_s(ctx, op, s_mask); + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + + z_mask = ~(t1->o_mask & t2->o_mask); + o_mask = ~(t1->z_mask & t2->z_mask); + s_mask = t1->s_mask & t2->s_mask; + + return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); } static bool fold_neg_no_const(OptContext *ctx, TCGOp *op) From 682d6d57baf43c8f7273c328efa3402943bd557f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 21:13:02 -0600 Subject: [PATCH 1703/2760] tcg/optimize: Build and use z_bits and o_bits in fold_nor Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 758f7b142e..29d1f29124 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2231,16 +2231,22 @@ static bool fold_neg(OptContext *ctx, TCGOp *op) static bool fold_nor(OptContext *ctx, TCGOp *op) { - uint64_t s_mask; + uint64_t z_mask, o_mask, s_mask; + TempOptInfo *t1, *t2; if (fold_const2_commutative(ctx, op) || fold_xi_to_not(ctx, op, 0)) { return true; } - s_mask = arg_info(op->args[1])->s_mask - & arg_info(op->args[2])->s_mask; - return fold_masks_s(ctx, op, s_mask); + t1 = arg_info(op->args[1]); + t2 = arg_info(op->args[2]); + + z_mask = ~(t1->o_mask | t2->o_mask); + o_mask = ~(t1->z_mask | t2->z_mask); + s_mask = t1->s_mask & t2->s_mask; + + return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); } static bool fold_not(OptContext *ctx, TCGOp *op) From d89504b0477df0ac6ff969b79065d9494cb0e6be Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 21:15:37 -0600 Subject: [PATCH 1704/2760] tcg/optimize: Build and use z_bits and o_bits in fold_not Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 29d1f29124..d22396f6d7 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2251,10 +2251,14 @@ static bool fold_nor(OptContext *ctx, TCGOp *op) static bool fold_not(OptContext *ctx, TCGOp *op) { + TempOptInfo *t1; + if (fold_const1(ctx, op)) { return true; } - return fold_masks_s(ctx, op, arg_info(op->args[1])->s_mask); + + t1 = arg_info(op->args[1]); + return fold_masks_zos(ctx, op, ~t1->o_mask, ~t1->z_mask, t1->s_mask); } static bool fold_or(OptContext *ctx, TCGOp *op) From 84b399df9abea985089d46fd94da559d83d0e085 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 21:35:53 -0600 Subject: [PATCH 1705/2760] tcg/optimize: Build and use one and affected bits in fold_or Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index d22396f6d7..ce065d0e22 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2263,7 +2263,7 @@ static bool fold_not(OptContext *ctx, TCGOp *op) static bool fold_or(OptContext *ctx, TCGOp *op) { - uint64_t z_mask, s_mask; + uint64_t z_mask, o_mask, s_mask, a_mask; TempOptInfo *t1, *t2; if (fold_const2_commutative(ctx, op) || @@ -2274,9 +2274,15 @@ static bool fold_or(OptContext *ctx, TCGOp *op) t1 = arg_info(op->args[1]); t2 = arg_info(op->args[2]); + z_mask = t1->z_mask | t2->z_mask; + o_mask = t1->o_mask | t2->o_mask; s_mask = t1->s_mask & t2->s_mask; - return fold_masks_zs(ctx, op, z_mask, s_mask); + + /* Affected bits are those not known one, masked by those known zero. */ + a_mask = ~t1->o_mask & t2->z_mask; + + return fold_masks_zosa(ctx, op, z_mask, o_mask, s_mask, a_mask); } static bool fold_orc(OptContext *ctx, TCGOp *op) From cc4033ee47c39ca5668ca0ab9b23046cf029fc21 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 22:22:27 -0600 Subject: [PATCH 1706/2760] tcg/optimize: Build and use zero, one and affected bits in fold_orc Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index ce065d0e22..795f1c900e 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2287,7 +2287,7 @@ static bool fold_or(OptContext *ctx, TCGOp *op) static bool fold_orc(OptContext *ctx, TCGOp *op) { - uint64_t s_mask; + uint64_t z_mask, o_mask, s_mask, a_mask; TempOptInfo *t1, *t2; if (fold_const2(ctx, op) || @@ -2318,8 +2318,15 @@ static bool fold_orc(OptContext *ctx, TCGOp *op) } t1 = arg_info(op->args[1]); + + z_mask = t1->z_mask | ~t2->o_mask; + o_mask = t1->o_mask | ~t2->z_mask; s_mask = t1->s_mask & t2->s_mask; - return fold_masks_s(ctx, op, s_mask); + + /* Affected bits are those not known one, masked by those known one. */ + a_mask = ~t1->o_mask & t2->o_mask; + + return fold_masks_zosa(ctx, op, z_mask, o_mask, s_mask, a_mask); } static bool fold_qemu_ld_1reg(OptContext *ctx, TCGOp *op) From 787190e3f433022d6fd2983f5e5efdb848439a41 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 08:39:56 -0600 Subject: [PATCH 1707/2760] tcg/optimize: Build and use o_bits in fold_xor Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 795f1c900e..572d314578 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -3039,7 +3039,7 @@ static bool fold_tcg_st_memcopy(OptContext *ctx, TCGOp *op) static bool fold_xor(OptContext *ctx, TCGOp *op) { - uint64_t z_mask, s_mask; + uint64_t z_mask, o_mask, s_mask; TempOptInfo *t1, *t2; if (fold_const2_commutative(ctx, op) || @@ -3051,9 +3051,12 @@ static bool fold_xor(OptContext *ctx, TCGOp *op) t1 = arg_info(op->args[1]); t2 = arg_info(op->args[2]); - z_mask = t1->z_mask | t2->z_mask; + + z_mask = (t1->z_mask | t2->z_mask) & ~(t1->o_mask & t2->o_mask); + o_mask = (t1->o_mask & ~t2->z_mask) | (t2->o_mask & ~t1->z_mask); s_mask = t1->s_mask & t2->s_mask; - return fold_masks_zs(ctx, op, z_mask, s_mask); + + return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); } /* Propagate constants and copies, fold constant expressions. */ From e6e3733bf19eacec7bcaf6343619237f84ff840a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 15:02:41 -0600 Subject: [PATCH 1708/2760] tcg/optimize: Build and use o_bits in fold_bswap Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 49 ++++++++++++++++++++++++------------------------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 572d314578..c9c53f796f 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1671,53 +1671,52 @@ static bool fold_brcond2(OptContext *ctx, TCGOp *op) static bool fold_bswap(OptContext *ctx, TCGOp *op) { - uint64_t z_mask, s_mask, sign; + uint64_t z_mask, o_mask, s_mask; TempOptInfo *t1 = arg_info(op->args[1]); + int flags = op->args[2]; if (ti_is_const(t1)) { return tcg_opt_gen_movi(ctx, op, op->args[0], do_constant_folding(op->opc, ctx->type, - ti_const_val(t1), - op->args[2])); + ti_const_val(t1), flags)); } z_mask = t1->z_mask; + o_mask = t1->o_mask; + s_mask = 0; + switch (op->opc) { case INDEX_op_bswap16: z_mask = bswap16(z_mask); - sign = INT16_MIN; + o_mask = bswap16(o_mask); + if (flags & TCG_BSWAP_OS) { + z_mask = (int16_t)z_mask; + o_mask = (int16_t)o_mask; + s_mask = INT16_MIN; + } else if (!(flags & TCG_BSWAP_OZ)) { + z_mask |= MAKE_64BIT_MASK(16, 48); + } break; case INDEX_op_bswap32: z_mask = bswap32(z_mask); - sign = INT32_MIN; + o_mask = bswap32(o_mask); + if (flags & TCG_BSWAP_OS) { + z_mask = (int32_t)z_mask; + o_mask = (int32_t)o_mask; + s_mask = INT32_MIN; + } else if (!(flags & TCG_BSWAP_OZ)) { + z_mask |= MAKE_64BIT_MASK(32, 32); + } break; case INDEX_op_bswap64: z_mask = bswap64(z_mask); - sign = INT64_MIN; + o_mask = bswap64(o_mask); break; default: g_assert_not_reached(); } - s_mask = 0; - switch (op->args[2] & (TCG_BSWAP_OZ | TCG_BSWAP_OS)) { - case TCG_BSWAP_OZ: - break; - case TCG_BSWAP_OS: - /* If the sign bit may be 1, force all the bits above to 1. */ - if (z_mask & sign) { - z_mask |= sign; - } - /* The value and therefore s_mask is explicitly sign-extended. */ - s_mask = sign; - break; - default: - /* The high bits are undefined: force all bits above the sign to 1. */ - z_mask |= sign << 1; - break; - } - - return fold_masks_zs(ctx, op, z_mask, s_mask); + return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); } static bool fold_call(OptContext *ctx, TCGOp *op) From 9d80b3c89022279865220c23a1246a4e12786f27 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 14:45:44 -0600 Subject: [PATCH 1709/2760] tcg/optimize: Build and use o_bits in fold_deposit Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index c9c53f796f..043568a10d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1847,7 +1847,7 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) int ofs = op->args[3]; int len = op->args[4]; int width = 8 * tcg_type_size(ctx->type); - uint64_t z_mask, s_mask; + uint64_t z_mask, o_mask, s_mask; if (ti_is_const(t1) && ti_is_const(t2)) { return tcg_opt_gen_movi(ctx, op, op->args[0], @@ -1882,7 +1882,9 @@ static bool fold_deposit(OptContext *ctx, TCGOp *op) } z_mask = deposit64(t1->z_mask, ofs, len, t2->z_mask); - return fold_masks_zs(ctx, op, z_mask, s_mask); + o_mask = deposit64(t1->o_mask, ofs, len, t2->o_mask); + + return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); } static bool fold_divide(OptContext *ctx, TCGOp *op) From fcde7363d33744d14dc7e844752b53c4aa1d69ba Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 15:34:12 -0600 Subject: [PATCH 1710/2760] tcg/optimize: Build and use o_bits in fold_extract Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 043568a10d..f5fc0cfff9 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1963,7 +1963,7 @@ static bool fold_eqv(OptContext *ctx, TCGOp *op) static bool fold_extract(OptContext *ctx, TCGOp *op) { - uint64_t z_mask_old, z_mask; + uint64_t z_mask, o_mask, a_mask; TempOptInfo *t1 = arg_info(op->args[1]); int pos = op->args[2]; int len = op->args[3]; @@ -1973,13 +1973,11 @@ static bool fold_extract(OptContext *ctx, TCGOp *op) extract64(ti_const_val(t1), pos, len)); } - z_mask_old = t1->z_mask; - z_mask = extract64(z_mask_old, pos, len); - if (pos == 0 && fold_affected_mask(ctx, op, z_mask_old ^ z_mask)) { - return true; - } + z_mask = extract64(t1->z_mask, pos, len); + o_mask = extract64(t1->o_mask, pos, len); + a_mask = pos ? -1 : t1->z_mask ^ z_mask; - return fold_masks_z(ctx, op, z_mask); + return fold_masks_zosa(ctx, op, z_mask, o_mask, 0, a_mask); } static bool fold_extract2(OptContext *ctx, TCGOp *op) From 83c47c30278b130cade31c5e0dbbc08ac6258bad Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 15:48:16 -0600 Subject: [PATCH 1711/2760] tcg/optimize: Build and use z_bits and o_bits in fold_extract2 Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 38 +++++++++++++++++++++++++------------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index f5fc0cfff9..86d958267a 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1099,6 +1099,12 @@ static bool fold_masks_zos(OptContext *ctx, TCGOp *op, return fold_masks_zosa(ctx, op, z_mask, o_mask, s_mask, -1); } +static bool fold_masks_zo(OptContext *ctx, TCGOp *op, + uint64_t z_mask, uint64_t o_mask) +{ + return fold_masks_zosa(ctx, op, z_mask, o_mask, 0, -1); +} + static bool fold_masks_zs(OptContext *ctx, TCGOp *op, uint64_t z_mask, uint64_t s_mask) { @@ -1982,21 +1988,27 @@ static bool fold_extract(OptContext *ctx, TCGOp *op) static bool fold_extract2(OptContext *ctx, TCGOp *op) { - if (arg_is_const(op->args[1]) && arg_is_const(op->args[2])) { - uint64_t v1 = arg_const_val(op->args[1]); - uint64_t v2 = arg_const_val(op->args[2]); - int shr = op->args[3]; + TempOptInfo *t1 = arg_info(op->args[1]); + TempOptInfo *t2 = arg_info(op->args[2]); + uint64_t z1 = t1->z_mask; + uint64_t z2 = t2->z_mask; + uint64_t o1 = t1->o_mask; + uint64_t o2 = t2->o_mask; + int shr = op->args[3]; - if (ctx->type == TCG_TYPE_I32) { - v1 = (uint32_t)v1 >> shr; - v2 = (uint64_t)((int32_t)v2 << (32 - shr)); - } else { - v1 >>= shr; - v2 <<= 64 - shr; - } - return tcg_opt_gen_movi(ctx, op, op->args[0], v1 | v2); + if (ctx->type == TCG_TYPE_I32) { + z1 = (uint32_t)z1 >> shr; + o1 = (uint32_t)o1 >> shr; + z2 = (uint64_t)((int32_t)z2 << (32 - shr)); + o2 = (uint64_t)((int32_t)o2 << (32 - shr)); + } else { + z1 >>= shr; + o1 >>= shr; + z2 <<= 64 - shr; + o2 <<= 64 - shr; } - return finish_folding(ctx, op); + + return fold_masks_zo(ctx, op, z1 | z2, o1 | o2); } static bool fold_exts(OptContext *ctx, TCGOp *op) From de85257f14a5b16edb754190e850de1e916ac37c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 15:55:33 -0600 Subject: [PATCH 1712/2760] tcg/optimize: Build and use o_bits in fold_exts Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 86d958267a..103c94b12f 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2013,7 +2013,7 @@ static bool fold_extract2(OptContext *ctx, TCGOp *op) static bool fold_exts(OptContext *ctx, TCGOp *op) { - uint64_t s_mask, z_mask; + uint64_t z_mask, o_mask, s_mask; TempOptInfo *t1; if (fold_const1(ctx, op)) { @@ -2022,17 +2022,19 @@ static bool fold_exts(OptContext *ctx, TCGOp *op) t1 = arg_info(op->args[1]); z_mask = t1->z_mask; + o_mask = t1->o_mask; s_mask = t1->s_mask; switch (op->opc) { case INDEX_op_ext_i32_i64: s_mask |= INT32_MIN; z_mask = (int32_t)z_mask; + o_mask = (int32_t)o_mask; break; default: g_assert_not_reached(); } - return fold_masks_zs(ctx, op, z_mask, s_mask); + return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); } static bool fold_extu(OptContext *ctx, TCGOp *op) From f78342472fb4de10aa78e5cb5e2f502700d5a728 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 15:58:04 -0600 Subject: [PATCH 1713/2760] tcg/optimize: Build and use o_bits in fold_extu Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 103c94b12f..42d5ee23c0 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2039,25 +2039,31 @@ static bool fold_exts(OptContext *ctx, TCGOp *op) static bool fold_extu(OptContext *ctx, TCGOp *op) { - uint64_t z_mask; + uint64_t z_mask, o_mask; + TempOptInfo *t1; if (fold_const1(ctx, op)) { return true; } - z_mask = arg_info(op->args[1])->z_mask; + t1 = arg_info(op->args[1]); + z_mask = t1->z_mask; + o_mask = t1->o_mask; + switch (op->opc) { case INDEX_op_extrl_i64_i32: case INDEX_op_extu_i32_i64: z_mask = (uint32_t)z_mask; + o_mask = (uint32_t)o_mask; break; case INDEX_op_extrh_i64_i32: z_mask >>= 32; + o_mask >>= 32; break; default: g_assert_not_reached(); } - return fold_masks_z(ctx, op, z_mask); + return fold_masks_zo(ctx, op, z_mask, o_mask); } static bool fold_mb(OptContext *ctx, TCGOp *op) From 08d676a46b55a717447babab304a475e62b233ac Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 16:30:12 -0600 Subject: [PATCH 1714/2760] tcg/optimize: Build and use o_bits in fold_movcond Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 42d5ee23c0..abcbee9111 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2097,7 +2097,7 @@ static bool fold_mov(OptContext *ctx, TCGOp *op) static bool fold_movcond(OptContext *ctx, TCGOp *op) { - uint64_t z_mask, s_mask; + uint64_t z_mask, o_mask, s_mask; TempOptInfo *tt, *ft; int i; @@ -2123,6 +2123,7 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) tt = arg_info(op->args[3]); ft = arg_info(op->args[4]); z_mask = tt->z_mask | ft->z_mask; + o_mask = tt->o_mask & ft->o_mask; s_mask = tt->s_mask & ft->s_mask; if (ti_is_const(tt) && ti_is_const(ft)) { @@ -2145,7 +2146,7 @@ static bool fold_movcond(OptContext *ctx, TCGOp *op) } } - return fold_masks_zs(ctx, op, z_mask, s_mask); + return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); } static bool fold_mul(OptContext *ctx, TCGOp *op) From f4a818a08d86ba1fddc21391d9f9115ebef794db Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 16:40:24 -0600 Subject: [PATCH 1715/2760] tcg/optimize: Build and use o_bits in fold_sextract This was the last use of fold_affected_mask, now fully replaced by fold_masks_zosa. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 30 ++++++------------------------ 1 file changed, 6 insertions(+), 24 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index abcbee9111..673849f07a 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1121,22 +1121,6 @@ static bool fold_masks_s(OptContext *ctx, TCGOp *op, uint64_t s_mask) return fold_masks_zosa(ctx, op, -1, 0, s_mask, -1); } -/* - * An "affected" mask bit is 0 if and only if the result is identical - * to the first input. Thus if the entire mask is 0, the operation - * is equivalent to a copy. - */ -static bool fold_affected_mask(OptContext *ctx, TCGOp *op, uint64_t a_mask) -{ - if (ctx->type == TCG_TYPE_I32) { - a_mask = (uint32_t)a_mask; - } - if (a_mask == 0) { - return tcg_opt_gen_mov(ctx, op, op->args[0], op->args[1]); - } - return false; -} - /* * Convert @op to NOT, if NOT is supported by the host. * Return true f the conversion is successful, which will still @@ -2669,7 +2653,7 @@ static bool fold_setcond2(OptContext *ctx, TCGOp *op) static bool fold_sextract(OptContext *ctx, TCGOp *op) { - uint64_t z_mask, s_mask, s_mask_old; + uint64_t z_mask, o_mask, s_mask, a_mask; TempOptInfo *t1 = arg_info(op->args[1]); int pos = op->args[2]; int len = op->args[3]; @@ -2679,16 +2663,14 @@ static bool fold_sextract(OptContext *ctx, TCGOp *op) sextract64(ti_const_val(t1), pos, len)); } - s_mask_old = t1->s_mask; - s_mask = s_mask_old >> pos; + s_mask = t1->s_mask >> pos; s_mask |= -1ull << (len - 1); - - if (pos == 0 && fold_affected_mask(ctx, op, s_mask & ~s_mask_old)) { - return true; - } + a_mask = pos ? -1 : s_mask & ~t1->s_mask; z_mask = sextract64(t1->z_mask, pos, len); - return fold_masks_zs(ctx, op, z_mask, s_mask); + o_mask = sextract64(t1->o_mask, pos, len); + + return fold_masks_zosa(ctx, op, z_mask, o_mask, s_mask, a_mask); } static bool fold_shift(OptContext *ctx, TCGOp *op) From 03329e3ce4609d9d63f8786294eb724289f9b596 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 16:49:02 -0600 Subject: [PATCH 1716/2760] tcg/optimize: Build and use o_bits in fold_shift Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 673849f07a..0b441bc611 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2675,7 +2675,7 @@ static bool fold_sextract(OptContext *ctx, TCGOp *op) static bool fold_shift(OptContext *ctx, TCGOp *op) { - uint64_t s_mask, z_mask; + uint64_t s_mask, z_mask, o_mask; TempOptInfo *t1, *t2; if (fold_const2(ctx, op) || @@ -2688,14 +2688,16 @@ static bool fold_shift(OptContext *ctx, TCGOp *op) t2 = arg_info(op->args[2]); s_mask = t1->s_mask; z_mask = t1->z_mask; + o_mask = t1->o_mask; if (ti_is_const(t2)) { int sh = ti_const_val(t2); z_mask = do_constant_folding(op->opc, ctx->type, z_mask, sh); + o_mask = do_constant_folding(op->opc, ctx->type, o_mask, sh); s_mask = do_constant_folding(op->opc, ctx->type, s_mask, sh); - return fold_masks_zs(ctx, op, z_mask, s_mask); + return fold_masks_zos(ctx, op, z_mask, o_mask, s_mask); } switch (op->opc) { From e532a39352878c506966a0e4e19ee938fca194ec Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 14:05:01 -0600 Subject: [PATCH 1717/2760] tcg/optimize: Use fold_and in do_constant_folding_cond[12] When lowering tst comparisons, completely fold the and opcode that we generate. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tcg/optimize.c b/tcg/optimize.c index 0b441bc611..aa64f15779 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -784,6 +784,7 @@ static bool swap_commutative2(TCGArg *p1, TCGArg *p2) * Return -1 if the condition can't be simplified, * and the result of the condition (0 or 1) if it can. */ +static bool fold_and(OptContext *ctx, TCGOp *op); static int do_constant_folding_cond1(OptContext *ctx, TCGOp *op, TCGArg dest, TCGArg *p1, TCGArg *p2, TCGArg *pcond) { @@ -834,6 +835,7 @@ static int do_constant_folding_cond1(OptContext *ctx, TCGOp *op, TCGArg dest, op2->args[0] = tmp; op2->args[1] = *p1; op2->args[2] = *p2; + fold_and(ctx, op2); *p1 = tmp; *p2 = arg_new_constant(ctx, 0); @@ -929,9 +931,12 @@ static int do_constant_folding_cond2(OptContext *ctx, TCGOp *op, TCGArg *args) op1->args[0] = t1; op1->args[1] = al; op1->args[2] = bl; + fold_and(ctx, op1); + op2->args[0] = t2; op2->args[1] = ah; op2->args[2] = bh; + fold_and(ctx, op1); args[0] = t1; args[1] = t2; From 932522a9ddc1f7c7866009cd73eaf79452951b81 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 23 Oct 2023 14:29:46 -0700 Subject: [PATCH 1718/2760] tcg/optimize: Fold and to extract during optimize Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index aa64f15779..06ccf39d64 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1044,8 +1044,9 @@ static bool fold_const2_commutative(OptContext *ctx, TCGOp *op) * If z_mask allows, fold the output to constant zero. * The passed s_mask may be augmented by z_mask. */ -static bool fold_masks_zosa(OptContext *ctx, TCGOp *op, uint64_t z_mask, - uint64_t o_mask, int64_t s_mask, uint64_t a_mask) +static bool fold_masks_zosa_int(OptContext *ctx, TCGOp *op, + uint64_t z_mask, uint64_t o_mask, + int64_t s_mask, uint64_t a_mask) { const TCGOpDef *def = &tcg_op_defs[op->opc]; TCGTemp *ts; @@ -1095,6 +1096,13 @@ static bool fold_masks_zosa(OptContext *ctx, TCGOp *op, uint64_t z_mask, rep = MAX(rep - 1, 0); ti->s_mask = INT64_MIN >> rep; + return false; +} + +static bool fold_masks_zosa(OptContext *ctx, TCGOp *op, uint64_t z_mask, + uint64_t o_mask, int64_t s_mask, uint64_t a_mask) +{ + fold_masks_zosa_int(ctx, op, z_mask, o_mask, s_mask, -1); return true; } @@ -1448,7 +1456,26 @@ static bool fold_and(OptContext *ctx, TCGOp *op) /* Affected bits are those not known zero, masked by those known one. */ a_mask = t1->z_mask & ~t2->o_mask; - return fold_masks_zosa(ctx, op, z_mask, o_mask, s_mask, a_mask); + if (!fold_masks_zosa_int(ctx, op, z_mask, o_mask, s_mask, a_mask)) { + if (ti_is_const(t2)) { + /* + * Canonicalize on extract, if valid. This aids x86 with its + * 2 operand MOVZBL and 2 operand AND, selecting the TCGOpcode + * which does not require matching operands. Other backends can + * trivially expand the extract to AND during code generation. + */ + uint64_t val = ti_const_val(t2); + if (!(val & (val + 1))) { + unsigned len = ctz64(~val); + if (TCG_TARGET_extract_valid(ctx->type, 0, len)) { + op->opc = INDEX_op_extract; + op->args[2] = 0; + op->args[3] = len; + } + } + } + } + return true; } static bool fold_andc(OptContext *ctx, TCGOp *op) From 9ffa5420e927c327412c9e4ef113ad2f1a564aec Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 17:48:02 -0600 Subject: [PATCH 1719/2760] tcg/optimize: Simplify fold_and constant checks If operand 2 is constant, then the computation of z_mask and a_mask will produce the same results as the explicit checks via fold_xi_to_i and fold_xi_to_x. Shift the call of fold_xx_to_x down below the ti_is_const(t2) check. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 06ccf39d64..f3a2328fe4 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1434,10 +1434,7 @@ static bool fold_and(OptContext *ctx, TCGOp *op) uint64_t z_mask, o_mask, s_mask, a_mask; TempOptInfo *t1, *t2; - if (fold_const2_commutative(ctx, op) || - fold_xi_to_i(ctx, op, 0) || - fold_xi_to_x(ctx, op, -1) || - fold_xx_to_x(ctx, op)) { + if (fold_const2_commutative(ctx, op)) { return true; } @@ -1473,6 +1470,8 @@ static bool fold_and(OptContext *ctx, TCGOp *op) op->args[3] = len; } } + } else { + fold_xx_to_x(ctx, op); } } return true; From 3c75cb4d64811cfba2c03d66328d838cc72f8355 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 9 Dec 2024 17:56:10 -0600 Subject: [PATCH 1720/2760] tcg/optimize: Simplify fold_andc constant checks If operand 2 is constant, then the computation of z_mask and a_mask will produce the same results as the explicit check via fold_xi_to_i. Shift the calls of fold_xx_to_i and fold_ix_to_not down below the i2->is_const check. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index f3a2328fe4..8d14a38f9d 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1482,10 +1482,7 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) uint64_t z_mask, o_mask, s_mask, a_mask; TempOptInfo *t1, *t2; - if (fold_const2(ctx, op) || - fold_xx_to_i(ctx, op, 0) || - fold_xi_to_x(ctx, op, 0) || - fold_ix_to_not(ctx, op, -1)) { + if (fold_const2(ctx, op)) { return true; } @@ -1510,6 +1507,10 @@ static bool fold_andc(OptContext *ctx, TCGOp *op) op->args[2] = arg_new_constant(ctx, ~ti_const_val(t2)); return fold_and(ctx, op); } + if (fold_xx_to_i(ctx, op, 0) || + fold_ix_to_not(ctx, op, -1)) { + return true; + } z_mask = t1->z_mask & ~t2->o_mask; o_mask = t1->o_mask & ~t2->z_mask; From 61617f715ec26877cdda9e027ac43945910f4fe9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 08:14:35 -0600 Subject: [PATCH 1721/2760] tcg/optimize: Simplify fold_orc constant checks If operand 2 is constant, then the computation of z_mask and a_mask will produce the same results as the explicit check via fold_xi_to_i. Shift the calls of fold_xx_to_i and fold_ix_to_not down below the i2->is_const check. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 8d14a38f9d..a48ddd9171 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -2326,10 +2326,7 @@ static bool fold_orc(OptContext *ctx, TCGOp *op) uint64_t z_mask, o_mask, s_mask, a_mask; TempOptInfo *t1, *t2; - if (fold_const2(ctx, op) || - fold_xx_to_i(ctx, op, -1) || - fold_xi_to_x(ctx, op, -1) || - fold_ix_to_not(ctx, op, 0)) { + if (fold_const2(ctx, op)) { return true; } @@ -2352,7 +2349,10 @@ static bool fold_orc(OptContext *ctx, TCGOp *op) op->args[2] = arg_new_constant(ctx, ~ti_const_val(t2)); return fold_or(ctx, op); } - + if (fold_xx_to_i(ctx, op, -1) || + fold_ix_to_not(ctx, op, 0)) { + return true; + } t1 = arg_info(op->args[1]); z_mask = t1->z_mask | ~t2->o_mask; From 7630de24bace80ec031c52e478605692f79ac08b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 10 Dec 2024 08:30:50 -0600 Subject: [PATCH 1722/2760] tcg/optimize: Simplify fold_eqv constant checks Both cases are handled by fold_xor after conversion. Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson --- tcg/optimize.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index a48ddd9171..62a128bc9b 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1948,9 +1948,7 @@ static bool fold_eqv(OptContext *ctx, TCGOp *op) uint64_t z_mask, o_mask, s_mask; TempOptInfo *t1, *t2; - if (fold_const2_commutative(ctx, op) || - fold_xi_to_x(ctx, op, -1) || - fold_xi_to_not(ctx, op, 0)) { + if (fold_const2_commutative(ctx, op)) { return true; } From 169d253e1f25cc4adff6571e249222e422a4b07e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 27 Jun 2025 06:22:27 -0700 Subject: [PATCH 1723/2760] tcg/riscv: Fix typo in tgen_extract MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the direction of the shift, introduced when converting the codebase to TCGOutOp* and small tgen_* helpers. Fixes: 5a4d034f3cb ("tcg: Convert extract to TCGOutOpExtract") Reported-by: Andrea Bolognani Signed-off-by: Richard Henderson Tested-by: Andrea Bolognani Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé --- tcg/riscv/tcg-target.c.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/riscv/tcg-target.c.inc b/tcg/riscv/tcg-target.c.inc index 1800fd5077..31b9f7d87a 100644 --- a/tcg/riscv/tcg-target.c.inc +++ b/tcg/riscv/tcg-target.c.inc @@ -2502,7 +2502,7 @@ static void tgen_extract(TCGContext *s, TCGType type, TCGReg a0, TCGReg a1, } } if (ofs + len == 32) { - tgen_shli(s, TCG_TYPE_I32, a0, a1, ofs); + tgen_shri(s, TCG_TYPE_I32, a0, a1, ofs); return; } if (len == 1) { From 0d0fc3f4658937fb81fcc16a89738e83bd8d4795 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Sat, 28 Jun 2025 09:57:53 -0600 Subject: [PATCH 1724/2760] tcg: Fix constant propagation in tcg_reg_alloc_dup The scalar constant must be replicated for dup. Cc: qemu-stable@nongnu.org Fixes: bab1671f0fa ("tcg: Manually expand INDEX_op_dup_vec") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3002 Signed-off-by: Richard Henderson --- tcg/tcg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/tcg.c b/tcg/tcg.c index d714ae2889..50d40b9cbe 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -5154,7 +5154,7 @@ static void tcg_reg_alloc_dup(TCGContext *s, const TCGOp *op) if (its->val_type == TEMP_VAL_CONST) { /* Propagate constant via movi -> dupi. */ - tcg_target_ulong val = its->val; + tcg_target_ulong val = dup_const(vece, its->val); if (IS_DEAD_ARG(1)) { temp_dead(s, its); } From a89d18919e4b9a3f8bc67c43e4577ae76ff9ee41 Mon Sep 17 00:00:00 2001 From: Mads Ynddal Date: Tue, 1 Jul 2025 15:08:25 +0100 Subject: [PATCH 1725/2760] MAINTAINERS: add myself as reviewer for Apple Silicon HVF I've both publicly and private been digging around the Apple Silicon HVF code, and use it daily as part of my job. I feel I have a solid understanding of it, so I thought I'd step up and assist. I've added myself as reviewer to the common "HVF" as well, to be informed of changes that might affect the Apple Silicon HVF code, which will be my primary focus. Signed-off-by: Mads Ynddal Message-id: 20250617093001.70080-1-mads@ynddal.dk Signed-off-by: Peter Maydell --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index d1672fda8d..b3b2a11207 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -511,6 +511,7 @@ F: system/cpus.c Apple Silicon HVF CPUs M: Alexander Graf +R: Mads Ynddal S: Maintained F: target/arm/hvf/ F: target/arm/hvf-stub.c @@ -527,6 +528,7 @@ HVF M: Cameron Esfahani M: Roman Bolshakov R: Phil Dennis-Jordan +R: Mads Ynddal W: https://wiki.qemu.org/Features/HVF S: Maintained F: accel/hvf/ From 9a3bf0e0ab628de7051b41a88c4628aa9e4d311b Mon Sep 17 00:00:00 2001 From: Solomon Tan Date: Tue, 1 Jul 2025 15:08:25 +0100 Subject: [PATCH 1726/2760] target/arm: Make RETA[AB] UNDEF when pauth is not implemented MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the Arm A-profile A64 Instruction Set Architecture, RETA[AB] should be decoded as UNDEF if the pauth feature is not implemented. We got this right in the initial implementation, but accidentally dropped the feature-check when we converted these insns to decodetree. Cc: qemu-stable@nongnu.org Fixes: 0ebbe9021254f ("target/arm: Convert BRA[AB]Z, BLR[AB]Z, RETA[AB] to decodetree") Signed-off-by: Solomon Tan Reviewed-by: Alex Bennée Reviewed-by: Richard Henderson Message-id: 20250616171549.59190-1-root@wjsota.com Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ac80f572a2..d0719b5665 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1816,6 +1816,10 @@ static bool trans_RETA(DisasContext *s, arg_reta *a) { TCGv_i64 dst; + if (!dc_isar_feature(aa64_pauth, s)) { + return false; + } + dst = auth_branch_target(s, cpu_reg(s, 30), cpu_X[31], !a->m); gen_a64_set_pc(s, dst); s->base.is_jmp = DISAS_JUMP; From a2e3508ac0f2aeb27f859362bf8e6dcac96c88e7 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 1 Jul 2025 15:08:26 +0100 Subject: [PATCH 1727/2760] arm/cpu: Add sysreg definitions in cpu-sysregs.h This new header contains macros that define aarch64 registers. In a subsequent patch, this will be replaced by a more exhaustive version that will be generated from linux arch/arm64/tools/sysreg file. Those macros are sufficient to migrate the storage of those ID regs from named fields in isar struct to an array cell. [CH: reworked to use different structures] [CH: moved accessors from the patches first using them to here, dropped interaction with writable registers, which will happen later] [CH: use DEF magic suggested by rth] Reviewed-by: Richard Henderson Reviewed-by: Sebastian Ott Signed-off-by: Eric Auger Signed-off-by: Cornelia Huck Message-id: 20250617153931.1330449-2-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/cpu-sysregs.h | 42 +++++++++++++++++++++++++++++++ target/arm/cpu-sysregs.h.inc | 36 ++++++++++++++++++++++++++ target/arm/cpu.h | 49 ++++++++++++++++++++++++++++++++++++ target/arm/cpu64.c | 22 ++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 target/arm/cpu-sysregs.h create mode 100644 target/arm/cpu-sysregs.h.inc diff --git a/target/arm/cpu-sysregs.h b/target/arm/cpu-sysregs.h new file mode 100644 index 0000000000..7877a3b06a --- /dev/null +++ b/target/arm/cpu-sysregs.h @@ -0,0 +1,42 @@ +/* + * Definitions for Arm ID system registers + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef ARM_CPU_SYSREGS_H +#define ARM_CPU_SYSREGS_H + +/* + * Following is similar to the coprocessor regs encodings, but with an argument + * ordering that matches the ARM ARM. We also reuse the various CP_REG_ defines + * that actually are the same as the equivalent KVM_REG_ values. + */ +#define ENCODE_ID_REG(op0, op1, crn, crm, op2) \ + (((op0) << CP_REG_ARM64_SYSREG_OP0_SHIFT) | \ + ((op1) << CP_REG_ARM64_SYSREG_OP1_SHIFT) | \ + ((crn) << CP_REG_ARM64_SYSREG_CRN_SHIFT) | \ + ((crm) << CP_REG_ARM64_SYSREG_CRM_SHIFT) | \ + ((op2) << CP_REG_ARM64_SYSREG_OP2_SHIFT)) + +#define DEF(NAME, OP0, OP1, CRN, CRM, OP2) NAME##_IDX, + +typedef enum ARMIDRegisterIdx { +#include "cpu-sysregs.h.inc" + NUM_ID_IDX, +} ARMIDRegisterIdx; + +#undef DEF +#define DEF(NAME, OP0, OP1, CRN, CRM, OP2) \ + SYS_##NAME = ENCODE_ID_REG(OP0, OP1, CRN, CRM, OP2), + +typedef enum ARMSysRegs { +#include "cpu-sysregs.h.inc" +} ARMSysRegs; + +#undef DEF + +extern const uint32_t id_register_sysreg[NUM_ID_IDX]; + +int get_sysreg_idx(ARMSysRegs sysreg); + +#endif /* ARM_CPU_SYSREGS_H */ diff --git a/target/arm/cpu-sysregs.h.inc b/target/arm/cpu-sysregs.h.inc new file mode 100644 index 0000000000..cb99286f70 --- /dev/null +++ b/target/arm/cpu-sysregs.h.inc @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +DEF(ID_AA64PFR0_EL1, 3, 0, 0, 4, 0) +DEF(ID_AA64PFR1_EL1, 3, 0, 0, 4, 1) +DEF(ID_AA64SMFR0_EL1, 3, 0, 0, 4, 5) +DEF(ID_AA64DFR0_EL1, 3, 0, 0, 5, 0) +DEF(ID_AA64DFR1_EL1, 3, 0, 0, 5, 1) +DEF(ID_AA64ISAR0_EL1, 3, 0, 0, 6, 0) +DEF(ID_AA64ISAR1_EL1, 3, 0, 0, 6, 1) +DEF(ID_AA64ISAR2_EL1, 3, 0, 0, 6, 2) +DEF(ID_AA64MMFR0_EL1, 3, 0, 0, 7, 0) +DEF(ID_AA64MMFR1_EL1, 3, 0, 0, 7, 1) +DEF(ID_AA64MMFR2_EL1, 3, 0, 0, 7, 2) +DEF(ID_AA64MMFR3_EL1, 3, 0, 0, 7, 3) +DEF(ID_PFR0_EL1, 3, 0, 0, 1, 0) +DEF(ID_PFR1_EL1, 3, 0, 0, 1, 1) +DEF(ID_DFR0_EL1, 3, 0, 0, 1, 2) +DEF(ID_MMFR0_EL1, 3, 0, 0, 1, 4) +DEF(ID_MMFR1_EL1, 3, 0, 0, 1, 5) +DEF(ID_MMFR2_EL1, 3, 0, 0, 1, 6) +DEF(ID_MMFR3_EL1, 3, 0, 0, 1, 7) +DEF(ID_ISAR0_EL1, 3, 0, 0, 2, 0) +DEF(ID_ISAR1_EL1, 3, 0, 0, 2, 1) +DEF(ID_ISAR2_EL1, 3, 0, 0, 2, 2) +DEF(ID_ISAR3_EL1, 3, 0, 0, 2, 3) +DEF(ID_ISAR4_EL1, 3, 0, 0, 2, 4) +DEF(ID_ISAR5_EL1, 3, 0, 0, 2, 5) +DEF(ID_MMFR4_EL1, 3, 0, 0, 2, 6) +DEF(ID_ISAR6_EL1, 3, 0, 0, 2, 7) +DEF(MVFR0_EL1, 3, 0, 0, 3, 0) +DEF(MVFR1_EL1, 3, 0, 0, 3, 1) +DEF(MVFR2_EL1, 3, 0, 0, 3, 2) +DEF(ID_PFR2_EL1, 3, 0, 0, 3, 4) +DEF(ID_DFR1_EL1, 3, 0, 0, 3, 5) +DEF(ID_MMFR5_EL1, 3, 0, 0, 3, 6) +DEF(ID_AA64ZFR0_EL1, 3, 0, 0, 4, 4) +DEF(CTR_EL0, 3, 3, 0, 0, 1) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 302c24e232..45409f84ef 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -32,6 +32,7 @@ #include "qapi/qapi-types-common.h" #include "target/arm/multiprocessing.h" #include "target/arm/gtimer.h" +#include "target/arm/cpu-sysregs.h" #define EXCP_UDEF 1 /* undefined instruction */ #define EXCP_SWI 2 /* software interrupt */ @@ -834,6 +835,53 @@ typedef struct { uint32_t map, init, supported; } ARMVQMap; +/* REG is ID_XXX */ +#define FIELD_DP64_IDREG(ISAR, REG, FIELD, VALUE) \ + ({ \ + ARMISARegisters *i_ = (ISAR); \ + uint64_t regval = i_->idregs[REG ## _EL1_IDX]; \ + regval = FIELD_DP64(regval, REG, FIELD, VALUE); \ + i_->idregs[REG ## _EL1_IDX] = regval; \ + }) + +#define FIELD_DP32_IDREG(ISAR, REG, FIELD, VALUE) \ + ({ \ + ARMISARegisters *i_ = (ISAR); \ + uint64_t regval = i_->idregs[REG ## _EL1_IDX]; \ + regval = FIELD_DP32(regval, REG, FIELD, VALUE); \ + i_->idregs[REG ## _EL1_IDX] = regval; \ + }) + +#define FIELD_EX64_IDREG(ISAR, REG, FIELD) \ + ({ \ + const ARMISARegisters *i_ = (ISAR); \ + FIELD_EX64(i_->idregs[REG ## _EL1_IDX], REG, FIELD); \ + }) + +#define FIELD_EX32_IDREG(ISAR, REG, FIELD) \ + ({ \ + const ARMISARegisters *i_ = (ISAR); \ + FIELD_EX32(i_->idregs[REG ## _EL1_IDX], REG, FIELD); \ + }) + +#define FIELD_SEX64_IDREG(ISAR, REG, FIELD) \ + ({ \ + const ARMISARegisters *i_ = (ISAR); \ + FIELD_SEX64(i_->idregs[REG ## _EL1_IDX], REG, FIELD); \ + }) + +#define SET_IDREG(ISAR, REG, VALUE) \ + ({ \ + ARMISARegisters *i_ = (ISAR); \ + i_->idregs[REG ## _EL1_IDX] = VALUE; \ + }) + +#define GET_IDREG(ISAR, REG) \ + ({ \ + const ARMISARegisters *i_ = (ISAR); \ + i_->idregs[REG ## _EL1_IDX]; \ + }) + /** * ARMCPU: * @env: #CPUARMState @@ -1040,6 +1088,7 @@ struct ArchCPU { uint64_t id_aa64zfr0; uint64_t id_aa64smfr0; uint64_t reset_pmcr_el0; + uint64_t idregs[NUM_ID_IDX]; } isar; uint64_t midr; uint32_t revidr; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 200da1c489..77054e0ec3 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -36,6 +36,28 @@ #include "cpu-features.h" #include "cpregs.h" +/* convert between _IDX and SYS_ */ +#define DEF(NAME, OP0, OP1, CRN, CRM, OP2) \ + [NAME##_IDX] = SYS_##NAME, + +const uint32_t id_register_sysreg[NUM_ID_IDX] = { +#include "cpu-sysregs.h.inc" +}; + +#undef DEF +#define DEF(NAME, OP0, OP1, CRN, CRM, OP2) \ + case SYS_##NAME: return NAME##_IDX; + +int get_sysreg_idx(ARMSysRegs sysreg) +{ + switch (sysreg) { +#include "cpu-sysregs.h.inc" + } + g_assert_not_reached(); +} + +#undef DEF + void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) { /* From 804cfc7eedb7457b137282a528d8c3465760c2e6 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 1 Jul 2025 15:08:26 +0100 Subject: [PATCH 1728/2760] arm/cpu: Store aa64isar0/aa64zfr0 into the idregs arrays Also add kvm accessors for storing host features into idregs. Reviewed-by: Richard Henderson Reviewed-by: Sebastian Ott Signed-off-by: Eric Auger Signed-off-by: Cornelia Huck Message-id: 20250617153931.1330449-3-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 57 ++++++++++++++++++++------------------- target/arm/cpu.c | 9 +++---- target/arm/cpu.h | 2 -- target/arm/cpu64.c | 8 +++--- target/arm/helper.c | 6 +++-- target/arm/hvf/hvf.c | 3 ++- target/arm/kvm.c | 30 ++++++++++++++++++--- target/arm/tcg/cpu64.c | 44 ++++++++++++++++++------------ 8 files changed, 97 insertions(+), 62 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 4452e7c21e..6a47f1a6d2 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -23,6 +23,7 @@ #include "hw/registerfields.h" #include "qemu/host-utils.h" #include "cpu.h" +#include "cpu-sysregs.h" /* * Naming convention for isar_feature functions: @@ -377,92 +378,92 @@ static inline bool isar_feature_aa32_doublelock(const ARMISARegisters *id) */ static inline bool isar_feature_aa64_aes(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, AES) != 0; } static inline bool isar_feature_aa64_pmull(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, AES) > 1; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, AES) > 1; } static inline bool isar_feature_aa64_sha1(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA1) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, SHA1) != 0; } static inline bool isar_feature_aa64_sha256(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, SHA2) != 0; } static inline bool isar_feature_aa64_sha512(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA2) > 1; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, SHA2) > 1; } static inline bool isar_feature_aa64_crc32(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, CRC32) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, CRC32) != 0; } static inline bool isar_feature_aa64_atomics(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, ATOMIC) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, ATOMIC) != 0; } static inline bool isar_feature_aa64_rdm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RDM) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, RDM) != 0; } static inline bool isar_feature_aa64_sha3(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SHA3) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, SHA3) != 0; } static inline bool isar_feature_aa64_sm3(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM3) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, SM3) != 0; } static inline bool isar_feature_aa64_sm4(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, SM4) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, SM4) != 0; } static inline bool isar_feature_aa64_dp(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, DP) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, DP) != 0; } static inline bool isar_feature_aa64_fhm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, FHM) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, FHM) != 0; } static inline bool isar_feature_aa64_condm_4(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, TS) != 0; } static inline bool isar_feature_aa64_condm_5(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TS) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, TS) >= 2; } static inline bool isar_feature_aa64_rndr(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, RNDR) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, RNDR) != 0; } static inline bool isar_feature_aa64_tlbirange(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) == 2; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, TLB) == 2; } static inline bool isar_feature_aa64_tlbios(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar0, ID_AA64ISAR0, TLB) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR0, TLB) != 0; } static inline bool isar_feature_aa64_jscvt(const ARMISARegisters *id) @@ -928,52 +929,52 @@ static inline bool isar_feature_aa64_doublelock(const ARMISARegisters *id) static inline bool isar_feature_aa64_sve2(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SVEVER) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, SVEVER) != 0; } static inline bool isar_feature_aa64_sve2_aes(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, AES) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, AES) != 0; } static inline bool isar_feature_aa64_sve2_pmull128(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, AES) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, AES) >= 2; } static inline bool isar_feature_aa64_sve2_bitperm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, BITPERM) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, BITPERM) != 0; } static inline bool isar_feature_aa64_sve_bf16(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, BFLOAT16) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, BFLOAT16) != 0; } static inline bool isar_feature_aa64_sve2_sha3(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SHA3) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, SHA3) != 0; } static inline bool isar_feature_aa64_sve2_sm4(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, SM4) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, SM4) != 0; } static inline bool isar_feature_aa64_sve_i8mm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, I8MM) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, I8MM) != 0; } static inline bool isar_feature_aa64_sve_f32mm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, F32MM) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, F32MM) != 0; } static inline bool isar_feature_aa64_sve_f64mm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64zfr0, ID_AA64ZFR0, F64MM) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, F64MM) != 0; } static inline bool isar_feature_aa64_sme_f64f64(const ARMISARegisters *id) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index e025e241ed..f033411b5d 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1962,6 +1962,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) { CPUState *cs = CPU(dev); ARMCPU *cpu = ARM_CPU(dev); + ARMISARegisters *isar = &cpu->isar; ARMCPUClass *acc = ARM_CPU_GET_CLASS(dev); CPUARMState *env = &cpu->env; Error *local_err = NULL; @@ -2167,7 +2168,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) unset_feature(env, ARM_FEATURE_NEON); - t = cpu->isar.id_aa64isar0; + t = GET_IDREG(isar, ID_AA64ISAR0); t = FIELD_DP64(t, ID_AA64ISAR0, AES, 0); t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 0); t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 0); @@ -2175,7 +2176,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) t = FIELD_DP64(t, ID_AA64ISAR0, SM3, 0); t = FIELD_DP64(t, ID_AA64ISAR0, SM4, 0); t = FIELD_DP64(t, ID_AA64ISAR0, DP, 0); - cpu->isar.id_aa64isar0 = t; + SET_IDREG(isar, ID_AA64ISAR0, t); t = cpu->isar.id_aa64isar1; t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 0); @@ -2220,9 +2221,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) uint64_t t; uint32_t u; - t = cpu->isar.id_aa64isar0; - t = FIELD_DP64(t, ID_AA64ISAR0, FHM, 0); - cpu->isar.id_aa64isar0 = t; + FIELD_DP64_IDREG(isar, ID_AA64ISAR0, FHM, 0); t = cpu->isar.id_aa64isar1; t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 0); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 45409f84ef..7b5c7a4abc 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1074,7 +1074,6 @@ struct ArchCPU { uint32_t dbgdidr; uint32_t dbgdevid; uint32_t dbgdevid1; - uint64_t id_aa64isar0; uint64_t id_aa64isar1; uint64_t id_aa64isar2; uint64_t id_aa64pfr0; @@ -1085,7 +1084,6 @@ struct ArchCPU { uint64_t id_aa64mmfr3; uint64_t id_aa64dfr0; uint64_t id_aa64dfr1; - uint64_t id_aa64zfr0; uint64_t id_aa64smfr0; uint64_t reset_pmcr_el0; uint64_t idregs[NUM_ID_IDX]; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 77054e0ec3..c105fcc4ea 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -136,7 +136,7 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) * SVE is disabled and so are all vector lengths. Good. * Disable all SVE extensions as well. */ - cpu->isar.id_aa64zfr0 = 0; + SET_IDREG(&cpu->isar, ID_AA64ZFR0, 0); return; } @@ -639,6 +639,7 @@ void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) static void aarch64_a57_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,cortex-a57"; set_feature(&cpu->env, ARM_FEATURE_V8); @@ -676,7 +677,7 @@ static void aarch64_a57_initfn(Object *obj) cpu->isar.id_isar6 = 0; cpu->isar.id_aa64pfr0 = 0x00002222; cpu->isar.id_aa64dfr0 = 0x10305106; - cpu->isar.id_aa64isar0 = 0x00011120; + SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); cpu->isar.id_aa64mmfr0 = 0x00001124; cpu->isar.dbgdidr = 0x3516d000; cpu->isar.dbgdevid = 0x01110f13; @@ -700,6 +701,7 @@ static void aarch64_a57_initfn(Object *obj) static void aarch64_a53_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,cortex-a53"; set_feature(&cpu->env, ARM_FEATURE_V8); @@ -737,7 +739,7 @@ static void aarch64_a53_initfn(Object *obj) cpu->isar.id_isar6 = 0; cpu->isar.id_aa64pfr0 = 0x00002222; cpu->isar.id_aa64dfr0 = 0x10305106; - cpu->isar.id_aa64isar0 = 0x00011120; + SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); cpu->isar.id_aa64mmfr0 = 0x00001122; /* 40 bit physical addr */ cpu->isar.dbgdidr = 0x3516d000; cpu->isar.dbgdevid = 0x00110f13; diff --git a/target/arm/helper.c b/target/arm/helper.c index 889d308807..638550e45a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7750,6 +7750,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) { /* Register all the coprocessor registers based on feature bits */ CPUARMState *env = &cpu->env; + ARMISARegisters *isar = &cpu->isar; + if (arm_feature(env, ARM_FEATURE_M)) { /* M profile has no coprocessor registers */ return; @@ -7941,7 +7943,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64zfr0 }, + .resetvalue = GET_IDREG(isar, ID_AA64ZFR0)}, { .name = "ID_AA64SMFR0_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, @@ -8001,7 +8003,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64isar0 }, + .resetvalue = GET_IDREG(isar, ID_AA64ISAR0)}, { .name = "ID_AA64ISAR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 42258cc2d8..5d25260c5c 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -19,6 +19,7 @@ #include "system/hw_accel.h" #include "hvf_arm.h" #include "cpregs.h" +#include "cpu-sysregs.h" #include @@ -866,7 +867,7 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { HV_SYS_REG_ID_AA64PFR1_EL1, &host_isar.id_aa64pfr1 }, { HV_SYS_REG_ID_AA64DFR0_EL1, &host_isar.id_aa64dfr0 }, { HV_SYS_REG_ID_AA64DFR1_EL1, &host_isar.id_aa64dfr1 }, - { HV_SYS_REG_ID_AA64ISAR0_EL1, &host_isar.id_aa64isar0 }, + { HV_SYS_REG_ID_AA64ISAR0_EL1, &host_isar.idregs[ID_AA64ISAR0_EL1_IDX] }, { HV_SYS_REG_ID_AA64ISAR1_EL1, &host_isar.id_aa64isar1 }, /* Add ID_AA64ISAR2_EL1 here when HVF supports it */ { HV_SYS_REG_ID_AA64MMFR0_EL1, &host_isar.id_aa64mmfr0 }, diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 74fda8b809..bd33b0f656 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -26,6 +26,7 @@ #include "system/kvm_int.h" #include "kvm_arm.h" #include "cpu.h" +#include "cpu-sysregs.h" #include "trace.h" #include "internals.h" #include "hw/pci/pci.h" @@ -218,6 +219,28 @@ static bool kvm_arm_pauth_supported(void) kvm_check_extension(kvm_state, KVM_CAP_ARM_PTRAUTH_GENERIC)); } + +static uint64_t idregs_sysreg_to_kvm_reg(ARMSysRegs sysreg) +{ + return ARM64_SYS_REG((sysreg & CP_REG_ARM64_SYSREG_OP0_MASK) >> CP_REG_ARM64_SYSREG_OP0_SHIFT, + (sysreg & CP_REG_ARM64_SYSREG_OP1_MASK) >> CP_REG_ARM64_SYSREG_OP1_SHIFT, + (sysreg & CP_REG_ARM64_SYSREG_CRN_MASK) >> CP_REG_ARM64_SYSREG_CRN_SHIFT, + (sysreg & CP_REG_ARM64_SYSREG_CRM_MASK) >> CP_REG_ARM64_SYSREG_CRM_SHIFT, + (sysreg & CP_REG_ARM64_SYSREG_OP2_MASK) >> CP_REG_ARM64_SYSREG_OP2_SHIFT); +} + +/* read a sysreg value and store it in the idregs */ +static int get_host_cpu_reg(int fd, ARMHostCPUFeatures *ahcf, ARMIDRegisterIdx index) +{ + uint64_t *reg; + int ret; + + reg = &ahcf->isar.idregs[index]; + ret = read_sys_reg64(fd, reg, + idregs_sysreg_to_kvm_reg(id_register_sysreg[index])); + return ret; +} + static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { /* Identify the feature bits corresponding to the host CPU, and @@ -267,6 +290,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ahcf->target = init.target; ahcf->dtb_compatible = "arm,arm-v8"; + int fd = fdarray[2]; err = read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64pfr0, ARM64_SYS_REG(3, 0, 0, 4, 0)); @@ -298,8 +322,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ARM64_SYS_REG(3, 0, 0, 5, 0)); err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64dfr1, ARM64_SYS_REG(3, 0, 0, 5, 1)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar0, - ARM64_SYS_REG(3, 0, 0, 6, 0)); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64ISAR0_EL1_IDX); err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar1, ARM64_SYS_REG(3, 0, 0, 6, 1)); err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar2, @@ -408,8 +431,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * enabled SVE support, which resulted in an error rather than RAZ. * So only read the register if we set KVM_ARM_VCPU_SVE above. */ - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64zfr0, - ARM64_SYS_REG(3, 0, 0, 4, 4)); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64ZFR0_EL1_IDX); } } diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 5d8ed2794d..ed681ee08b 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -32,6 +32,7 @@ static void aarch64_a35_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,cortex-a35"; set_feature(&cpu->env, ARM_FEATURE_V8); @@ -66,7 +67,7 @@ static void aarch64_a35_initfn(Object *obj) cpu->isar.id_aa64pfr1 = 0; cpu->isar.id_aa64dfr0 = 0x10305106; cpu->isar.id_aa64dfr1 = 0; - cpu->isar.id_aa64isar0 = 0x00011120; + SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); cpu->isar.id_aa64isar1 = 0; cpu->isar.id_aa64mmfr0 = 0x00101122; cpu->isar.id_aa64mmfr1 = 0; @@ -204,6 +205,7 @@ static const Property arm_cpu_lpa2_property = static void aarch64_a55_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,cortex-a55"; set_feature(&cpu->env, ARM_FEATURE_V8); @@ -221,7 +223,7 @@ static void aarch64_a55_initfn(Object *obj) cpu->ctr = 0x84448004; /* L1Ip = VIPT */ cpu->dcz_blocksize = 4; /* 64 bytes */ cpu->isar.id_aa64dfr0 = 0x0000000010305408ull; - cpu->isar.id_aa64isar0 = 0x0000100010211120ull; + SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); cpu->isar.id_aa64isar1 = 0x0000000000100001ull; cpu->isar.id_aa64mmfr0 = 0x0000000000101122ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; @@ -276,6 +278,7 @@ static void aarch64_a55_initfn(Object *obj) static void aarch64_a72_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,cortex-a72"; set_feature(&cpu->env, ARM_FEATURE_V8); @@ -311,7 +314,7 @@ static void aarch64_a72_initfn(Object *obj) cpu->isar.id_isar5 = 0x00011121; cpu->isar.id_aa64pfr0 = 0x00002222; cpu->isar.id_aa64dfr0 = 0x10305106; - cpu->isar.id_aa64isar0 = 0x00011120; + SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); cpu->isar.id_aa64mmfr0 = 0x00001124; cpu->isar.dbgdidr = 0x3516d000; cpu->isar.dbgdevid = 0x01110f13; @@ -335,6 +338,7 @@ static void aarch64_a72_initfn(Object *obj) static void aarch64_a76_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,cortex-a76"; set_feature(&cpu->env, ARM_FEATURE_V8); @@ -352,7 +356,7 @@ static void aarch64_a76_initfn(Object *obj) cpu->ctr = 0x8444C004; cpu->dcz_blocksize = 4; cpu->isar.id_aa64dfr0 = 0x0000000010305408ull; - cpu->isar.id_aa64isar0 = 0x0000100010211120ull; + SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); cpu->isar.id_aa64isar1 = 0x0000000000100001ull; cpu->isar.id_aa64mmfr0 = 0x0000000000101122ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; @@ -408,6 +412,7 @@ static void aarch64_a76_initfn(Object *obj) static void aarch64_a64fx_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,a64fx"; set_feature(&cpu->env, ARM_FEATURE_V8); @@ -431,9 +436,9 @@ static void aarch64_a64fx_initfn(Object *obj) cpu->isar.id_aa64mmfr0 = 0x0000000000001122; cpu->isar.id_aa64mmfr1 = 0x0000000011212100; cpu->isar.id_aa64mmfr2 = 0x0000000000001011; - cpu->isar.id_aa64isar0 = 0x0000000010211120; + SET_IDREG(isar, ID_AA64ISAR0, 0x0000000010211120); cpu->isar.id_aa64isar1 = 0x0000000000010001; - cpu->isar.id_aa64zfr0 = 0x0000000000000000; + SET_IDREG(isar, ID_AA64ZFR0, 0x0000000000000000); cpu->clidr = 0x0000000080000023; /* 64KB L1 dcache */ cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 256, 64 * KiB, 7); @@ -581,6 +586,7 @@ static void define_neoverse_v1_cp_reginfo(ARMCPU *cpu) static void aarch64_neoverse_n1_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,neoverse-n1"; set_feature(&cpu->env, ARM_FEATURE_V8); @@ -598,7 +604,7 @@ static void aarch64_neoverse_n1_initfn(Object *obj) cpu->ctr = 0x8444c004; cpu->dcz_blocksize = 4; cpu->isar.id_aa64dfr0 = 0x0000000110305408ull; - cpu->isar.id_aa64isar0 = 0x0000100010211120ull; + SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); cpu->isar.id_aa64isar1 = 0x0000000000100001ull; cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; @@ -656,6 +662,7 @@ static void aarch64_neoverse_n1_initfn(Object *obj) static void aarch64_neoverse_v1_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,neoverse-v1"; set_feature(&cpu->env, ARM_FEATURE_V8); @@ -676,7 +683,7 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->id_aa64afr1 = 0x00000000; cpu->isar.id_aa64dfr0 = 0x000001f210305519ull; cpu->isar.id_aa64dfr1 = 0x00000000; - cpu->isar.id_aa64isar0 = 0x1011111110212120ull; /* with FEAT_RNG */ + SET_IDREG(isar, ID_AA64ISAR0, 0x1011111110212120ull); /* with FEAT_RNG */ cpu->isar.id_aa64isar1 = 0x0011100001211032ull; cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; @@ -735,7 +742,7 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->isar.mvfr2 = 0x00000043; /* From 3.7.5 ID_AA64ZFR0_EL1 */ - cpu->isar.id_aa64zfr0 = 0x0000100000100000; + SET_IDREG(isar, ID_AA64ZFR0, 0x0000100000100000); cpu->sve_vq.supported = (1 << 0) /* 128bit */ | (1 << 1); /* 256bit */ @@ -882,6 +889,7 @@ static const ARMCPRegInfo cortex_a710_cp_reginfo[] = { static void aarch64_a710_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,cortex-a710"; set_feature(&cpu->env, ARM_FEATURE_V8); @@ -919,12 +927,12 @@ static void aarch64_a710_initfn(Object *obj) cpu->isar.id_pfr2 = 0x00000011; cpu->isar.id_aa64pfr0 = 0x1201111120111112ull; /* GIC filled in later */ cpu->isar.id_aa64pfr1 = 0x0000000000000221ull; - cpu->isar.id_aa64zfr0 = 0x0000110100110021ull; /* with Crypto */ + SET_IDREG(isar, ID_AA64ZFR0, 0x0000110100110021ull); /* with Crypto */ cpu->isar.id_aa64dfr0 = 0x000011f010305619ull; cpu->isar.id_aa64dfr1 = 0; cpu->id_aa64afr0 = 0; cpu->id_aa64afr1 = 0; - cpu->isar.id_aa64isar0 = 0x0221111110212120ull; /* with Crypto */ + SET_IDREG(isar, ID_AA64ISAR0, 0x0221111110212120ull); /* with Crypto */ cpu->isar.id_aa64isar1 = 0x0010111101211052ull; cpu->isar.id_aa64mmfr0 = 0x0000022200101122ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; @@ -983,6 +991,7 @@ static const ARMCPRegInfo neoverse_n2_cp_reginfo[] = { static void aarch64_neoverse_n2_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,neoverse-n2"; set_feature(&cpu->env, ARM_FEATURE_V8); @@ -1020,12 +1029,12 @@ static void aarch64_neoverse_n2_initfn(Object *obj) cpu->isar.id_pfr2 = 0x00000011; cpu->isar.id_aa64pfr0 = 0x1201111120111112ull; /* GIC filled in later */ cpu->isar.id_aa64pfr1 = 0x0000000000000221ull; - cpu->isar.id_aa64zfr0 = 0x0000110100110021ull; /* with Crypto */ + SET_IDREG(isar, ID_AA64ZFR0, 0x0000110100110021ull); /* with Crypto */ cpu->isar.id_aa64dfr0 = 0x000011f210305619ull; cpu->isar.id_aa64dfr1 = 0; cpu->id_aa64afr0 = 0; cpu->id_aa64afr1 = 0; - cpu->isar.id_aa64isar0 = 0x1221111110212120ull; /* with Crypto and FEAT_RNG */ + SET_IDREG(isar, ID_AA64ISAR0, 0x1221111110212120ull); /* with Crypto and FEAT_RNG */ cpu->isar.id_aa64isar1 = 0x0011111101211052ull; cpu->isar.id_aa64mmfr0 = 0x0000022200101125ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; @@ -1083,6 +1092,7 @@ static void aarch64_neoverse_n2_initfn(Object *obj) void aarch64_max_tcg_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; uint64_t t; uint32_t u; @@ -1133,7 +1143,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, CTR_EL0, DIC, 1); cpu->ctr = t; - t = cpu->isar.id_aa64isar0; + t = GET_IDREG(isar, ID_AA64ISAR0); t = FIELD_DP64(t, ID_AA64ISAR0, AES, 2); /* FEAT_PMULL */ t = FIELD_DP64(t, ID_AA64ISAR0, SHA1, 1); /* FEAT_SHA1 */ t = FIELD_DP64(t, ID_AA64ISAR0, SHA2, 2); /* FEAT_SHA512 */ @@ -1148,7 +1158,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR0, TS, 2); /* FEAT_FlagM2 */ t = FIELD_DP64(t, ID_AA64ISAR0, TLB, 2); /* FEAT_TLBIRANGE */ t = FIELD_DP64(t, ID_AA64ISAR0, RNDR, 1); /* FEAT_RNG */ - cpu->isar.id_aa64isar0 = t; + SET_IDREG(isar, ID_AA64ISAR0, t); t = cpu->isar.id_aa64isar1; t = FIELD_DP64(t, ID_AA64ISAR1, DPB, 2); /* FEAT_DPB2 */ @@ -1244,7 +1254,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ cpu->isar.id_aa64mmfr3 = t; - t = cpu->isar.id_aa64zfr0; + t = GET_IDREG(isar, ID_AA64ZFR0); t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 1); t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* FEAT_SVE_PMULL128 */ t = FIELD_DP64(t, ID_AA64ZFR0, BITPERM, 1); /* FEAT_SVE_BitPerm */ @@ -1254,7 +1264,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ZFR0, I8MM, 1); /* FEAT_I8MM */ t = FIELD_DP64(t, ID_AA64ZFR0, F32MM, 1); /* FEAT_F32MM */ t = FIELD_DP64(t, ID_AA64ZFR0, F64MM, 1); /* FEAT_F64MM */ - cpu->isar.id_aa64zfr0 = t; + SET_IDREG(isar, ID_AA64ZFR0, t); t = cpu->isar.id_aa64dfr0; t = FIELD_DP64(t, ID_AA64DFR0, DEBUGVER, 10); /* FEAT_Debugv8p8 */ From 03380dd993de1a31c596b417f6e8a9de7dd8c370 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 1 Jul 2025 15:08:26 +0100 Subject: [PATCH 1729/2760] arm/cpu: Store aa64isar1/2 into the idregs array Reviewed-by: Richard Henderson Reviewed-by: Sebastian Ott Signed-off-by: Eric Auger Signed-off-by: Cornelia Huck Message-id: 20250617153931.1330449-4-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 44 +++++++++++++++++++-------------------- target/arm/cpu.c | 13 ++++-------- target/arm/cpu.h | 2 -- target/arm/cpu64.c | 9 ++++---- target/arm/helper.c | 4 ++-- target/arm/hvf/hvf.c | 2 +- target/arm/kvm.c | 6 ++---- target/arm/tcg/cpu64.c | 24 ++++++++++----------- 8 files changed, 48 insertions(+), 56 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 6a47f1a6d2..43c9695be0 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -468,17 +468,17 @@ static inline bool isar_feature_aa64_tlbios(const ARMISARegisters *id) static inline bool isar_feature_aa64_jscvt(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, JSCVT) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, JSCVT) != 0; } static inline bool isar_feature_aa64_fcma(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FCMA) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, FCMA) != 0; } static inline bool isar_feature_aa64_xs(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, XS) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, XS) != 0; } /* @@ -502,9 +502,9 @@ isar_feature_pauth_feature(const ARMISARegisters *id) * Architecturally, only one of {APA,API,APA3} may be active (non-zero) * and the other two must be zero. Thus we may avoid conditionals. */ - return (FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, APA) | - FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, API) | - FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, APA3)); + return (FIELD_EX64_IDREG(id, ID_AA64ISAR1, APA) | + FIELD_EX64_IDREG(id, ID_AA64ISAR1, API) | + FIELD_EX64_IDREG(id, ID_AA64ISAR2, APA3)); } static inline bool isar_feature_aa64_pauth(const ARMISARegisters *id) @@ -522,7 +522,7 @@ static inline bool isar_feature_aa64_pauth_qarma5(const ARMISARegisters *id) * Return true if pauth is enabled with the architected QARMA5 algorithm. * QEMU will always enable or disable both APA and GPA. */ - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, APA) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, APA) != 0; } static inline bool isar_feature_aa64_pauth_qarma3(const ARMISARegisters *id) @@ -531,77 +531,77 @@ static inline bool isar_feature_aa64_pauth_qarma3(const ARMISARegisters *id) * Return true if pauth is enabled with the architected QARMA3 algorithm. * QEMU will always enable or disable both APA3 and GPA3. */ - return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, APA3) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR2, APA3) != 0; } static inline bool isar_feature_aa64_sb(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SB) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, SB) != 0; } static inline bool isar_feature_aa64_predinv(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, SPECRES) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, SPECRES) != 0; } static inline bool isar_feature_aa64_frint(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, FRINTTS) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, FRINTTS) != 0; } static inline bool isar_feature_aa64_dcpop(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, DPB) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, DPB) != 0; } static inline bool isar_feature_aa64_dcpodp(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, DPB) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, DPB) >= 2; } static inline bool isar_feature_aa64_bf16(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, BF16) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, BF16) != 0; } static inline bool isar_feature_aa64_ebf16(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, BF16) > 1; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, BF16) > 1; } static inline bool isar_feature_aa64_rcpc_8_3(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, LRCPC) != 0; } static inline bool isar_feature_aa64_rcpc_8_4(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, LRCPC) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, LRCPC) >= 2; } static inline bool isar_feature_aa64_i8mm(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar1, ID_AA64ISAR1, I8MM) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR1, I8MM) != 0; } static inline bool isar_feature_aa64_wfxt(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, WFXT) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64ISAR2, WFXT) >= 2; } static inline bool isar_feature_aa64_hbc(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, BC) != 0; + return FIELD_EX64_IDREG(id, ID_AA64ISAR2, BC) != 0; } static inline bool isar_feature_aa64_mops(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, MOPS); + return FIELD_EX64_IDREG(id, ID_AA64ISAR2, MOPS); } static inline bool isar_feature_aa64_rpres(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64isar2, ID_AA64ISAR2, RPRES); + return FIELD_EX64_IDREG(id, ID_AA64ISAR2, RPRES); } static inline bool isar_feature_aa64_fp_simd(const ARMISARegisters *id) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index f033411b5d..2777de7294 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2123,9 +2123,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) uint64_t t; uint32_t u; - t = cpu->isar.id_aa64isar1; - t = FIELD_DP64(t, ID_AA64ISAR1, JSCVT, 0); - cpu->isar.id_aa64isar1 = t; + FIELD_DP64_IDREG(isar, ID_AA64ISAR1, JSCVT, 0); t = cpu->isar.id_aa64pfr0; t = FIELD_DP64(t, ID_AA64PFR0, FP, 0xf); @@ -2178,11 +2176,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) t = FIELD_DP64(t, ID_AA64ISAR0, DP, 0); SET_IDREG(isar, ID_AA64ISAR0, t); - t = cpu->isar.id_aa64isar1; + t = GET_IDREG(isar, ID_AA64ISAR1); t = FIELD_DP64(t, ID_AA64ISAR1, FCMA, 0); t = FIELD_DP64(t, ID_AA64ISAR1, BF16, 0); t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 0); - cpu->isar.id_aa64isar1 = t; + SET_IDREG(isar, ID_AA64ISAR1, t); t = cpu->isar.id_aa64pfr0; t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 0xf); @@ -2218,14 +2216,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } if (!cpu->has_neon && !cpu->has_vfp) { - uint64_t t; uint32_t u; FIELD_DP64_IDREG(isar, ID_AA64ISAR0, FHM, 0); - t = cpu->isar.id_aa64isar1; - t = FIELD_DP64(t, ID_AA64ISAR1, FRINTTS, 0); - cpu->isar.id_aa64isar1 = t; + FIELD_DP64_IDREG(isar, ID_AA64ISAR1, FRINTTS, 0); u = cpu->isar.mvfr0; u = FIELD_DP32(u, MVFR0, SIMDREG, 0); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 7b5c7a4abc..b81bc46966 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1074,8 +1074,6 @@ struct ArchCPU { uint32_t dbgdidr; uint32_t dbgdevid; uint32_t dbgdevid1; - uint64_t id_aa64isar1; - uint64_t id_aa64isar2; uint64_t id_aa64pfr0; uint64_t id_aa64pfr1; uint64_t id_aa64mmfr0; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index c105fcc4ea..e2b25b0043 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -502,6 +502,7 @@ void aarch64_add_sme_properties(Object *obj) void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) { ARMPauthFeature features = cpu_isar_feature(pauth_feature, cpu); + ARMISARegisters *isar = &cpu->isar; uint64_t isar1, isar2; /* @@ -512,13 +513,13 @@ void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) * * Begin by disabling all fields. */ - isar1 = cpu->isar.id_aa64isar1; + isar1 = GET_IDREG(isar, ID_AA64ISAR1); isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, APA, 0); isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPA, 0); isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, API, 0); isar1 = FIELD_DP64(isar1, ID_AA64ISAR1, GPI, 0); - isar2 = cpu->isar.id_aa64isar2; + isar2 = GET_IDREG(isar, ID_AA64ISAR2); isar2 = FIELD_DP64(isar2, ID_AA64ISAR2, APA3, 0); isar2 = FIELD_DP64(isar2, ID_AA64ISAR2, GPA3, 0); @@ -580,8 +581,8 @@ void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp) } } - cpu->isar.id_aa64isar1 = isar1; - cpu->isar.id_aa64isar2 = isar2; + SET_IDREG(isar, ID_AA64ISAR1, isar1); + SET_IDREG(isar, ID_AA64ISAR2, isar2); } static const Property arm_cpu_pauth_property = diff --git a/target/arm/helper.c b/target/arm/helper.c index 638550e45a..fd2a86b6b0 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -8008,12 +8008,12 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64isar1 }, + .resetvalue = GET_IDREG(isar, ID_AA64ISAR1)}, { .name = "ID_AA64ISAR2_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64isar2 }, + .resetvalue = GET_IDREG(isar, ID_AA64ISAR2)}, { .name = "ID_AA64ISAR3_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 6, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 5d25260c5c..7554282410 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -868,7 +868,7 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { HV_SYS_REG_ID_AA64DFR0_EL1, &host_isar.id_aa64dfr0 }, { HV_SYS_REG_ID_AA64DFR1_EL1, &host_isar.id_aa64dfr1 }, { HV_SYS_REG_ID_AA64ISAR0_EL1, &host_isar.idregs[ID_AA64ISAR0_EL1_IDX] }, - { HV_SYS_REG_ID_AA64ISAR1_EL1, &host_isar.id_aa64isar1 }, + { HV_SYS_REG_ID_AA64ISAR1_EL1, &host_isar.idregs[ID_AA64ISAR1_EL1_IDX] }, /* Add ID_AA64ISAR2_EL1 here when HVF supports it */ { HV_SYS_REG_ID_AA64MMFR0_EL1, &host_isar.id_aa64mmfr0 }, { HV_SYS_REG_ID_AA64MMFR1_EL1, &host_isar.id_aa64mmfr1 }, diff --git a/target/arm/kvm.c b/target/arm/kvm.c index bd33b0f656..6fa5bdff42 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -323,10 +323,8 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64dfr1, ARM64_SYS_REG(3, 0, 0, 5, 1)); err |= get_host_cpu_reg(fd, ahcf, ID_AA64ISAR0_EL1_IDX); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar1, - ARM64_SYS_REG(3, 0, 0, 6, 1)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64isar2, - ARM64_SYS_REG(3, 0, 0, 6, 2)); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64ISAR1_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64ISAR2_EL1_IDX); err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr0, ARM64_SYS_REG(3, 0, 0, 7, 0)); err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr1, diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index ed681ee08b..9175026472 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -68,7 +68,7 @@ static void aarch64_a35_initfn(Object *obj) cpu->isar.id_aa64dfr0 = 0x10305106; cpu->isar.id_aa64dfr1 = 0; SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); - cpu->isar.id_aa64isar1 = 0; + SET_IDREG(isar, ID_AA64ISAR1, 0); cpu->isar.id_aa64mmfr0 = 0x00101122; cpu->isar.id_aa64mmfr1 = 0; cpu->clidr = 0x0a200023; @@ -224,7 +224,7 @@ static void aarch64_a55_initfn(Object *obj) cpu->dcz_blocksize = 4; /* 64 bytes */ cpu->isar.id_aa64dfr0 = 0x0000000010305408ull; SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); - cpu->isar.id_aa64isar1 = 0x0000000000100001ull; + SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000100001ull); cpu->isar.id_aa64mmfr0 = 0x0000000000101122ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; @@ -357,7 +357,7 @@ static void aarch64_a76_initfn(Object *obj) cpu->dcz_blocksize = 4; cpu->isar.id_aa64dfr0 = 0x0000000010305408ull; SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); - cpu->isar.id_aa64isar1 = 0x0000000000100001ull; + SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000100001ull); cpu->isar.id_aa64mmfr0 = 0x0000000000101122ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; @@ -437,7 +437,7 @@ static void aarch64_a64fx_initfn(Object *obj) cpu->isar.id_aa64mmfr1 = 0x0000000011212100; cpu->isar.id_aa64mmfr2 = 0x0000000000001011; SET_IDREG(isar, ID_AA64ISAR0, 0x0000000010211120); - cpu->isar.id_aa64isar1 = 0x0000000000010001; + SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000010001); SET_IDREG(isar, ID_AA64ZFR0, 0x0000000000000000); cpu->clidr = 0x0000000080000023; /* 64KB L1 dcache */ @@ -605,7 +605,7 @@ static void aarch64_neoverse_n1_initfn(Object *obj) cpu->dcz_blocksize = 4; cpu->isar.id_aa64dfr0 = 0x0000000110305408ull; SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); - cpu->isar.id_aa64isar1 = 0x0000000000100001ull; + SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000100001ull); cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; @@ -684,7 +684,7 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->isar.id_aa64dfr0 = 0x000001f210305519ull; cpu->isar.id_aa64dfr1 = 0x00000000; SET_IDREG(isar, ID_AA64ISAR0, 0x1011111110212120ull); /* with FEAT_RNG */ - cpu->isar.id_aa64isar1 = 0x0011100001211032ull; + SET_IDREG(isar, ID_AA64ISAR1, 0x0011000001211032ull); cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x0220011102101011ull; @@ -933,7 +933,7 @@ static void aarch64_a710_initfn(Object *obj) cpu->id_aa64afr0 = 0; cpu->id_aa64afr1 = 0; SET_IDREG(isar, ID_AA64ISAR0, 0x0221111110212120ull); /* with Crypto */ - cpu->isar.id_aa64isar1 = 0x0010111101211052ull; + SET_IDREG(isar, ID_AA64ISAR1, 0x0010111101211052ull); cpu->isar.id_aa64mmfr0 = 0x0000022200101122ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x1221011110101011ull; @@ -1035,7 +1035,7 @@ static void aarch64_neoverse_n2_initfn(Object *obj) cpu->id_aa64afr0 = 0; cpu->id_aa64afr1 = 0; SET_IDREG(isar, ID_AA64ISAR0, 0x1221111110212120ull); /* with Crypto and FEAT_RNG */ - cpu->isar.id_aa64isar1 = 0x0011111101211052ull; + SET_IDREG(isar, ID_AA64ISAR1, 0x0011111101211052ull); cpu->isar.id_aa64mmfr0 = 0x0000022200101125ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x1221011112101011ull; @@ -1160,7 +1160,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR0, RNDR, 1); /* FEAT_RNG */ SET_IDREG(isar, ID_AA64ISAR0, t); - t = cpu->isar.id_aa64isar1; + t = GET_IDREG(isar, ID_AA64ISAR1); t = FIELD_DP64(t, ID_AA64ISAR1, DPB, 2); /* FEAT_DPB2 */ t = FIELD_DP64(t, ID_AA64ISAR1, APA, PauthFeat_FPACCOMBINED); t = FIELD_DP64(t, ID_AA64ISAR1, API, 1); @@ -1174,14 +1174,14 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR1, DGH, 1); /* FEAT_DGH */ t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 1); /* FEAT_I8MM */ t = FIELD_DP64(t, ID_AA64ISAR1, XS, 1); /* FEAT_XS */ - cpu->isar.id_aa64isar1 = t; + SET_IDREG(isar, ID_AA64ISAR1, t); - t = cpu->isar.id_aa64isar2; + t = GET_IDREG(isar, ID_AA64ISAR2); t = FIELD_DP64(t, ID_AA64ISAR2, RPRES, 1); /* FEAT_RPRES */ t = FIELD_DP64(t, ID_AA64ISAR2, MOPS, 1); /* FEAT_MOPS */ t = FIELD_DP64(t, ID_AA64ISAR2, BC, 1); /* FEAT_HBC */ t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */ - cpu->isar.id_aa64isar2 = t; + SET_IDREG(isar, ID_AA64ISAR2, t); t = cpu->isar.id_aa64pfr0; t = FIELD_DP64(t, ID_AA64PFR0, FP, 1); /* FEAT_FP16 */ From d1a3cc9634548b8fe1398c6eb624aa2c7e9e265b Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 1 Jul 2025 15:08:26 +0100 Subject: [PATCH 1730/2760] arm/cpu: Store aa64pfr0/1 into the idregs array Reviewed-by: Richard Henderson Reviewed-by: Sebastian Ott Signed-off-by: Eric Auger Signed-off-by: Cornelia Huck Message-id: 20250617153931.1330449-5-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 40 ++++++++++++++++----------------- target/arm/cpu.c | 29 ++++++++---------------- target/arm/cpu.h | 2 -- target/arm/cpu64.c | 14 ++++-------- target/arm/helper.c | 6 ++--- target/arm/hvf/hvf.c | 9 ++++---- target/arm/kvm.c | 12 +++++----- target/arm/tcg/cpu64.c | 47 ++++++++++++++++++--------------------- 8 files changed, 68 insertions(+), 91 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 43c9695be0..3adea85b79 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -607,68 +607,68 @@ static inline bool isar_feature_aa64_rpres(const ARMISARegisters *id) static inline bool isar_feature_aa64_fp_simd(const ARMISARegisters *id) { /* We always set the AdvSIMD and FP fields identically. */ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) != 0xf; + return FIELD_EX64_IDREG(id, ID_AA64PFR0, FP) != 0xf; } static inline bool isar_feature_aa64_fp16(const ARMISARegisters *id) { /* We always set the AdvSIMD and FP fields identically wrt FP16. */ - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, FP) == 1; + return FIELD_EX64_IDREG(id, ID_AA64PFR0, FP) == 1; } static inline bool isar_feature_aa64_aa32(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL0) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64PFR0, EL0) >= 2; } static inline bool isar_feature_aa64_aa32_el1(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL1) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64PFR0, EL1) >= 2; } static inline bool isar_feature_aa64_aa32_el2(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, EL2) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64PFR0, EL2) >= 2; } static inline bool isar_feature_aa64_ras(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RAS) != 0; + return FIELD_EX64_IDREG(id, ID_AA64PFR0, RAS) != 0; } static inline bool isar_feature_aa64_doublefault(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RAS) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64PFR0, RAS) >= 2; } static inline bool isar_feature_aa64_sve(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SVE) != 0; + return FIELD_EX64_IDREG(id, ID_AA64PFR0, SVE) != 0; } static inline bool isar_feature_aa64_sel2(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, SEL2) != 0; + return FIELD_EX64_IDREG(id, ID_AA64PFR0, SEL2) != 0; } static inline bool isar_feature_aa64_rme(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, RME) != 0; + return FIELD_EX64_IDREG(id, ID_AA64PFR0, RME) != 0; } static inline bool isar_feature_aa64_dit(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, DIT) != 0; + return FIELD_EX64_IDREG(id, ID_AA64PFR0, DIT) != 0; } static inline bool isar_feature_aa64_scxtnum(const ARMISARegisters *id) { - int key = FIELD_EX64(id->id_aa64pfr0, ID_AA64PFR0, CSV2); + int key = FIELD_EX64_IDREG(id, ID_AA64PFR0, CSV2); if (key >= 2) { return true; /* FEAT_CSV2_2 */ } if (key == 1) { - key = FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, CSV2_FRAC); + key = FIELD_EX64_IDREG(id, ID_AA64PFR1, CSV2_FRAC); return key >= 2; /* FEAT_CSV2_1p2 */ } return false; @@ -676,37 +676,37 @@ static inline bool isar_feature_aa64_scxtnum(const ARMISARegisters *id) static inline bool isar_feature_aa64_ssbs(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SSBS) != 0; + return FIELD_EX64_IDREG(id, ID_AA64PFR1, SSBS) != 0; } static inline bool isar_feature_aa64_bti(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, BT) != 0; + return FIELD_EX64_IDREG(id, ID_AA64PFR1, BT) != 0; } static inline bool isar_feature_aa64_mte_insn_reg(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) != 0; + return FIELD_EX64_IDREG(id, ID_AA64PFR1, MTE) != 0; } static inline bool isar_feature_aa64_mte(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64PFR1, MTE) >= 2; } static inline bool isar_feature_aa64_mte3(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, MTE) >= 3; + return FIELD_EX64_IDREG(id, ID_AA64PFR1, MTE) >= 3; } static inline bool isar_feature_aa64_sme(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, SME) != 0; + return FIELD_EX64_IDREG(id, ID_AA64PFR1, SME) != 0; } static inline bool isar_feature_aa64_nmi(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64pfr1, ID_AA64PFR1, NMI) != 0; + return FIELD_EX64_IDREG(id, ID_AA64PFR1, NMI) != 0; } static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 2777de7294..d39e8dc956 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2120,14 +2120,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) } if (!cpu->has_vfp) { - uint64_t t; uint32_t u; FIELD_DP64_IDREG(isar, ID_AA64ISAR1, JSCVT, 0); - t = cpu->isar.id_aa64pfr0; - t = FIELD_DP64(t, ID_AA64PFR0, FP, 0xf); - cpu->isar.id_aa64pfr0 = t; + FIELD_DP64_IDREG(isar, ID_AA64PFR0, FP, 0xf); u = cpu->isar.id_isar6; u = FIELD_DP32(u, ID_ISAR6, JSCVT, 0); @@ -2182,9 +2179,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) t = FIELD_DP64(t, ID_AA64ISAR1, I8MM, 0); SET_IDREG(isar, ID_AA64ISAR1, t); - t = cpu->isar.id_aa64pfr0; - t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 0xf); - cpu->isar.id_aa64pfr0 = t; + FIELD_DP64_IDREG(isar, ID_AA64PFR0, ADVSIMD, 0xf); u = cpu->isar.id_isar5; u = FIELD_DP32(u, ID_ISAR5, AES, 0); @@ -2326,12 +2321,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) */ cpu->isar.id_pfr1 = FIELD_DP32(cpu->isar.id_pfr1, ID_PFR1, SECURITY, 0); cpu->isar.id_dfr0 = FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, COPSDBG, 0); - cpu->isar.id_aa64pfr0 = FIELD_DP64(cpu->isar.id_aa64pfr0, - ID_AA64PFR0, EL3, 0); + FIELD_DP64_IDREG(isar, ID_AA64PFR0, EL3, 0); /* Disable the realm management extension, which requires EL3. */ - cpu->isar.id_aa64pfr0 = FIELD_DP64(cpu->isar.id_aa64pfr0, - ID_AA64PFR0, RME, 0); + FIELD_DP64_IDREG(isar, ID_AA64PFR0, RME, 0); } if (!cpu->has_el2) { @@ -2366,8 +2359,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * Disable the hypervisor feature bits in the processor feature * registers if we don't have EL2. */ - cpu->isar.id_aa64pfr0 = FIELD_DP64(cpu->isar.id_aa64pfr0, - ID_AA64PFR0, EL2, 0); + FIELD_DP64_IDREG(isar, ID_AA64PFR0, EL2, 0); cpu->isar.id_pfr1 = FIELD_DP32(cpu->isar.id_pfr1, ID_PFR1, VIRTUALIZATION, 0); } @@ -2388,8 +2380,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * This matches Cortex-A710 BROADCASTMTE input being LOW. */ if (tcg_enabled() && cpu->tag_memory == NULL) { - cpu->isar.id_aa64pfr1 = - FIELD_DP64(cpu->isar.id_aa64pfr1, ID_AA64PFR1, MTE, 1); + FIELD_DP64_IDREG(isar, ID_AA64PFR1, MTE, 1); } /* @@ -2397,7 +2388,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * enabled on the guest (i.e mte=off), clear guest's MTE bits." */ if (kvm_enabled() && !cpu->kvm_mte) { - FIELD_DP64(cpu->isar.id_aa64pfr1, ID_AA64PFR1, MTE, 0); + FIELD_DP64_IDREG(isar, ID_AA64PFR1, MTE, 0); } #endif } @@ -2436,13 +2427,11 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu->isar.id_dfr0 = FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, MMAPTRC, 0); /* FEAT_AMU (Activity Monitors Extension) */ - cpu->isar.id_aa64pfr0 = - FIELD_DP64(cpu->isar.id_aa64pfr0, ID_AA64PFR0, AMU, 0); + FIELD_DP64_IDREG(isar, ID_AA64PFR0, AMU, 0); cpu->isar.id_pfr0 = FIELD_DP32(cpu->isar.id_pfr0, ID_PFR0, AMU, 0); /* FEAT_MPAM (Memory Partitioning and Monitoring Extension) */ - cpu->isar.id_aa64pfr0 = - FIELD_DP64(cpu->isar.id_aa64pfr0, ID_AA64PFR0, MPAM, 0); + FIELD_DP64_IDREG(isar, ID_AA64PFR0, MPAM, 0); } /* MPU can be configured out of a PMSA CPU either by setting has-mpu diff --git a/target/arm/cpu.h b/target/arm/cpu.h index b81bc46966..05157a49d7 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1074,8 +1074,6 @@ struct ArchCPU { uint32_t dbgdidr; uint32_t dbgdevid; uint32_t dbgdevid1; - uint64_t id_aa64pfr0; - uint64_t id_aa64pfr1; uint64_t id_aa64mmfr0; uint64_t id_aa64mmfr1; uint64_t id_aa64mmfr2; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index e2b25b0043..502aac9173 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -310,16 +310,13 @@ static bool cpu_arm_get_sve(Object *obj, Error **errp) static void cpu_arm_set_sve(Object *obj, bool value, Error **errp) { ARMCPU *cpu = ARM_CPU(obj); - uint64_t t; if (value && kvm_enabled() && !kvm_arm_sve_supported()) { error_setg(errp, "'sve' feature not supported by KVM on this host"); return; } - t = cpu->isar.id_aa64pfr0; - t = FIELD_DP64(t, ID_AA64PFR0, SVE, value); - cpu->isar.id_aa64pfr0 = t; + FIELD_DP64_IDREG(&cpu->isar, ID_AA64PFR0, SVE, value); } void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp) @@ -370,11 +367,8 @@ static bool cpu_arm_get_sme(Object *obj, Error **errp) static void cpu_arm_set_sme(Object *obj, bool value, Error **errp) { ARMCPU *cpu = ARM_CPU(obj); - uint64_t t; - t = cpu->isar.id_aa64pfr1; - t = FIELD_DP64(t, ID_AA64PFR1, SME, value); - cpu->isar.id_aa64pfr1 = t; + FIELD_DP64_IDREG(&cpu->isar, ID_AA64PFR1, SME, value); } static bool cpu_arm_get_sme_fa64(Object *obj, Error **errp) @@ -676,7 +670,7 @@ static void aarch64_a57_initfn(Object *obj) cpu->isar.id_isar4 = 0x00011142; cpu->isar.id_isar5 = 0x00011121; cpu->isar.id_isar6 = 0; - cpu->isar.id_aa64pfr0 = 0x00002222; + SET_IDREG(isar, ID_AA64PFR0, 0x00002222); cpu->isar.id_aa64dfr0 = 0x10305106; SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); cpu->isar.id_aa64mmfr0 = 0x00001124; @@ -738,7 +732,7 @@ static void aarch64_a53_initfn(Object *obj) cpu->isar.id_isar4 = 0x00011142; cpu->isar.id_isar5 = 0x00011121; cpu->isar.id_isar6 = 0; - cpu->isar.id_aa64pfr0 = 0x00002222; + SET_IDREG(isar, ID_AA64PFR0, 0x00002222); cpu->isar.id_aa64dfr0 = 0x10305106; SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); cpu->isar.id_aa64mmfr0 = 0x00001122; /* 40 bit physical addr */ diff --git a/target/arm/helper.c b/target/arm/helper.c index fd2a86b6b0..b221e8df6c 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6943,7 +6943,7 @@ static uint64_t id_pfr1_read(CPUARMState *env, const ARMCPRegInfo *ri) static uint64_t id_aa64pfr0_read(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = env_archcpu(env); - uint64_t pfr0 = cpu->isar.id_aa64pfr0; + uint64_t pfr0 = GET_IDREG(&cpu->isar, ID_AA64PFR0); if (env->gicv3state) { pfr0 |= 1 << 24; @@ -7916,7 +7916,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, #ifdef CONFIG_USER_ONLY .type = ARM_CP_CONST, - .resetvalue = cpu->isar.id_aa64pfr0 + .resetvalue = GET_IDREG(isar, ID_AA64PFR0) #else .type = ARM_CP_NO_RAW, .accessfn = access_aa64_tid3, @@ -7928,7 +7928,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64pfr1}, + .resetvalue = GET_IDREG(isar, ID_AA64PFR1)}, { .name = "ID_AA64PFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 7554282410..e1bfca5947 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -863,8 +863,8 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) int reg; uint64_t *val; } regs[] = { - { HV_SYS_REG_ID_AA64PFR0_EL1, &host_isar.id_aa64pfr0 }, - { HV_SYS_REG_ID_AA64PFR1_EL1, &host_isar.id_aa64pfr1 }, + { HV_SYS_REG_ID_AA64PFR0_EL1, &host_isar.idregs[ID_AA64PFR0_EL1_IDX] }, + { HV_SYS_REG_ID_AA64PFR1_EL1, &host_isar.idregs[ID_AA64PFR1_EL1_IDX] }, { HV_SYS_REG_ID_AA64DFR0_EL1, &host_isar.id_aa64dfr0 }, { HV_SYS_REG_ID_AA64DFR1_EL1, &host_isar.id_aa64dfr1 }, { HV_SYS_REG_ID_AA64ISAR0_EL1, &host_isar.idregs[ID_AA64ISAR0_EL1_IDX] }, @@ -911,7 +911,8 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * - fix any assumptions we made that SME implies SVE (since * on the M4 there is SME but not SVE) */ - host_isar.id_aa64pfr1 &= ~R_ID_AA64PFR1_SME_MASK; + SET_IDREG(&host_isar, ID_AA64PFR1, + GET_IDREG(&host_isar, ID_AA64PFR1) & ~R_ID_AA64PFR1_SME_MASK); ahcf->isar = host_isar; @@ -928,7 +929,7 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ahcf->reset_sctlr |= 0x00800000; /* Make sure we don't advertise AArch32 support for EL0/EL1 */ - if ((host_isar.id_aa64pfr0 & 0xff) != 0x11) { + if ((GET_IDREG(&host_isar, ID_AA64PFR0) & 0xff) != 0x11) { return false; } diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 6fa5bdff42..1e19dba4cb 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -292,8 +292,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ahcf->dtb_compatible = "arm,arm-v8"; int fd = fdarray[2]; - err = read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64pfr0, - ARM64_SYS_REG(3, 0, 0, 4, 0)); + err = get_host_cpu_reg(fd, ahcf, ID_AA64PFR0_EL1_IDX); if (unlikely(err < 0)) { /* * Before v4.15, the kernel only exposed a limited number of system @@ -311,11 +310,10 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * ??? Either of these sounds like too much effort just * to work around running a modern host kernel. */ - ahcf->isar.id_aa64pfr0 = 0x00000011; /* EL1&0, AArch64 only */ + SET_IDREG(&ahcf->isar, ID_AA64PFR0, 0x00000011); /* EL1&0, AArch64 only */ err = 0; } else { - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64pfr1, - ARM64_SYS_REG(3, 0, 0, 4, 1)); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64PFR1_EL1_IDX); err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64smfr0, ARM64_SYS_REG(3, 0, 0, 4, 5)); err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64dfr0, @@ -395,14 +393,14 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * arch/arm64/kvm/sys_regs.c:trap_dbgidr() does. * We only do this if the CPU supports AArch32 at EL1. */ - if (FIELD_EX32(ahcf->isar.id_aa64pfr0, ID_AA64PFR0, EL1) >= 2) { + if (FIELD_EX32_IDREG(&ahcf->isar, ID_AA64PFR0, EL1) >= 2) { int wrps = FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, WRPS); int brps = FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, BRPS); int ctx_cmps = FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, CTX_CMPS); int version = 6; /* ARMv8 debug architecture */ bool has_el3 = - !!FIELD_EX32(ahcf->isar.id_aa64pfr0, ID_AA64PFR0, EL3); + !!FIELD_EX32_IDREG(&ahcf->isar, ID_AA64PFR0, EL3); uint32_t dbgdidr = 0; dbgdidr = FIELD_DP32(dbgdidr, DBGDIDR, WRPS, wrps); diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 9175026472..7a730c7974 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -63,8 +63,8 @@ static void aarch64_a35_initfn(Object *obj) cpu->isar.id_isar3 = 0x01112131; cpu->isar.id_isar4 = 0x00011142; cpu->isar.id_isar5 = 0x00011121; - cpu->isar.id_aa64pfr0 = 0x00002222; - cpu->isar.id_aa64pfr1 = 0; + SET_IDREG(isar, ID_AA64PFR0, 0x00002222); + SET_IDREG(isar, ID_AA64PFR1, 0); cpu->isar.id_aa64dfr0 = 0x10305106; cpu->isar.id_aa64dfr1 = 0; SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); @@ -158,11 +158,8 @@ static bool cpu_arm_get_rme(Object *obj, Error **errp) static void cpu_arm_set_rme(Object *obj, bool value, Error **errp) { ARMCPU *cpu = ARM_CPU(obj); - uint64_t t; - t = cpu->isar.id_aa64pfr0; - t = FIELD_DP64(t, ID_AA64PFR0, RME, value); - cpu->isar.id_aa64pfr0 = t; + FIELD_DP64_IDREG(&cpu->isar, ID_AA64PFR0, RME, value); } static void cpu_max_set_l0gptsz(Object *obj, Visitor *v, const char *name, @@ -228,8 +225,8 @@ static void aarch64_a55_initfn(Object *obj) cpu->isar.id_aa64mmfr0 = 0x0000000000101122ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; - cpu->isar.id_aa64pfr0 = 0x0000000010112222ull; - cpu->isar.id_aa64pfr1 = 0x0000000000000010ull; + SET_IDREG(isar, ID_AA64PFR0, 0x0000000010112222ull); + SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000010ull); cpu->id_afr0 = 0x00000000; cpu->isar.id_dfr0 = 0x04010088; cpu->isar.id_isar0 = 0x02101110; @@ -312,7 +309,7 @@ static void aarch64_a72_initfn(Object *obj) cpu->isar.id_isar3 = 0x01112131; cpu->isar.id_isar4 = 0x00011142; cpu->isar.id_isar5 = 0x00011121; - cpu->isar.id_aa64pfr0 = 0x00002222; + SET_IDREG(isar, ID_AA64PFR0, 0x00002222); cpu->isar.id_aa64dfr0 = 0x10305106; SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); cpu->isar.id_aa64mmfr0 = 0x00001124; @@ -361,8 +358,8 @@ static void aarch64_a76_initfn(Object *obj) cpu->isar.id_aa64mmfr0 = 0x0000000000101122ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; - cpu->isar.id_aa64pfr0 = 0x1100000010111112ull; /* GIC filled in later */ - cpu->isar.id_aa64pfr1 = 0x0000000000000010ull; + SET_IDREG(isar, ID_AA64PFR0, 0x1100000010111112ull); /* GIC filled in later */ + SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000010ull); cpu->id_afr0 = 0x00000000; cpu->isar.id_dfr0 = 0x04010088; cpu->isar.id_isar0 = 0x02101110; @@ -427,8 +424,8 @@ static void aarch64_a64fx_initfn(Object *obj) cpu->revidr = 0x00000000; cpu->ctr = 0x86668006; cpu->reset_sctlr = 0x30000180; - cpu->isar.id_aa64pfr0 = 0x0000000101111111; /* No RAS Extensions */ - cpu->isar.id_aa64pfr1 = 0x0000000000000000; + SET_IDREG(isar, ID_AA64PFR0, 0x0000000101111111); /* No RAS Extensions */ + SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000000); cpu->isar.id_aa64dfr0 = 0x0000000010305408; cpu->isar.id_aa64dfr1 = 0x0000000000000000; cpu->id_aa64afr0 = 0x0000000000000000; @@ -609,8 +606,8 @@ static void aarch64_neoverse_n1_initfn(Object *obj) cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; - cpu->isar.id_aa64pfr0 = 0x1100000010111112ull; /* GIC filled in later */ - cpu->isar.id_aa64pfr1 = 0x0000000000000020ull; + SET_IDREG(isar, ID_AA64PFR0, 0x1100000010111112ull); /* GIC filled in later */ + SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000020ull); cpu->id_afr0 = 0x00000000; cpu->isar.id_dfr0 = 0x04010088; cpu->isar.id_isar0 = 0x02101110; @@ -688,8 +685,8 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; cpu->isar.id_aa64mmfr2 = 0x0220011102101011ull; - cpu->isar.id_aa64pfr0 = 0x1101110120111112ull; /* GIC filled in later */ - cpu->isar.id_aa64pfr1 = 0x0000000000000020ull; + SET_IDREG(isar, ID_AA64PFR0, 0x1101110120111112ull); /* GIC filled in later */ + SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000020ull); cpu->id_afr0 = 0x00000000; cpu->isar.id_dfr0 = 0x15011099; cpu->isar.id_isar0 = 0x02101110; @@ -925,8 +922,8 @@ static void aarch64_a710_initfn(Object *obj) cpu->isar.mvfr1 = 0x13211111; cpu->isar.mvfr2 = 0x00000043; cpu->isar.id_pfr2 = 0x00000011; - cpu->isar.id_aa64pfr0 = 0x1201111120111112ull; /* GIC filled in later */ - cpu->isar.id_aa64pfr1 = 0x0000000000000221ull; + SET_IDREG(isar, ID_AA64PFR0, 0x1201111120111112ull); /* GIC filled in later */ + SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000221ull); SET_IDREG(isar, ID_AA64ZFR0, 0x0000110100110021ull); /* with Crypto */ cpu->isar.id_aa64dfr0 = 0x000011f010305619ull; cpu->isar.id_aa64dfr1 = 0; @@ -1027,8 +1024,8 @@ static void aarch64_neoverse_n2_initfn(Object *obj) cpu->isar.mvfr1 = 0x13211111; cpu->isar.mvfr2 = 0x00000043; cpu->isar.id_pfr2 = 0x00000011; - cpu->isar.id_aa64pfr0 = 0x1201111120111112ull; /* GIC filled in later */ - cpu->isar.id_aa64pfr1 = 0x0000000000000221ull; + SET_IDREG(isar, ID_AA64PFR0, 0x1201111120111112ull); /* GIC filled in later */ + SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000221ull); SET_IDREG(isar, ID_AA64ZFR0, 0x0000110100110021ull); /* with Crypto */ cpu->isar.id_aa64dfr0 = 0x000011f210305619ull; cpu->isar.id_aa64dfr1 = 0; @@ -1183,7 +1180,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ISAR2, WFXT, 2); /* FEAT_WFxT */ SET_IDREG(isar, ID_AA64ISAR2, t); - t = cpu->isar.id_aa64pfr0; + t = GET_IDREG(isar, ID_AA64PFR0); t = FIELD_DP64(t, ID_AA64PFR0, FP, 1); /* FEAT_FP16 */ t = FIELD_DP64(t, ID_AA64PFR0, ADVSIMD, 1); /* FEAT_FP16 */ t = FIELD_DP64(t, ID_AA64PFR0, RAS, 2); /* FEAT_RASv1p1 + FEAT_DoubleFault */ @@ -1192,9 +1189,9 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64PFR0, DIT, 1); /* FEAT_DIT */ t = FIELD_DP64(t, ID_AA64PFR0, CSV2, 3); /* FEAT_CSV2_3 */ t = FIELD_DP64(t, ID_AA64PFR0, CSV3, 1); /* FEAT_CSV3 */ - cpu->isar.id_aa64pfr0 = t; + SET_IDREG(isar, ID_AA64PFR0, t); - t = cpu->isar.id_aa64pfr1; + t = GET_IDREG(isar, ID_AA64PFR1); t = FIELD_DP64(t, ID_AA64PFR1, BT, 1); /* FEAT_BTI */ t = FIELD_DP64(t, ID_AA64PFR1, SSBS, 2); /* FEAT_SSBS2 */ /* @@ -1207,7 +1204,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64PFR1, SME, 1); /* FEAT_SME */ t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* FEAT_CSV2_3 */ t = FIELD_DP64(t, ID_AA64PFR1, NMI, 1); /* FEAT_NMI */ - cpu->isar.id_aa64pfr1 = t; + SET_IDREG(isar, ID_AA64PFR1, t); t = cpu->isar.id_aa64mmfr0; t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 6); /* FEAT_LPA: 52 bits */ From f1fd81291c91b6d6954ad67729a3061ec84e643e Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 1 Jul 2025 15:08:27 +0100 Subject: [PATCH 1731/2760] arm/cpu: Store aa64mmfr0-3 into the idregs array Reviewed-by: Richard Henderson Reviewed-by: Sebastian Ott Signed-off-by: Eric Auger Signed-off-by: Cornelia Huck Message-id: 20250617153931.1330449-6-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 74 +++++++++++++++++++-------------------- target/arm/cpu.h | 4 --- target/arm/cpu64.c | 8 ++--- target/arm/helper.c | 8 ++--- target/arm/hvf/hvf.c | 21 ++++++----- target/arm/kvm.c | 12 +++---- target/arm/ptw.c | 6 ++-- target/arm/tcg/cpu64.c | 64 ++++++++++++++++----------------- 8 files changed, 95 insertions(+), 102 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 3adea85b79..89c9278639 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -711,192 +711,192 @@ static inline bool isar_feature_aa64_nmi(const ARMISARegisters *id) static inline bool isar_feature_aa64_tgran4_lpa2(const ARMISARegisters *id) { - return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 1; + return FIELD_SEX64_IDREG(id, ID_AA64MMFR0, TGRAN4) >= 1; } static inline bool isar_feature_aa64_tgran4_2_lpa2(const ARMISARegisters *id) { - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); + unsigned t = FIELD_EX64_IDREG(id, ID_AA64MMFR0, TGRAN4_2); return t >= 3 || (t == 0 && isar_feature_aa64_tgran4_lpa2(id)); } static inline bool isar_feature_aa64_tgran16_lpa2(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64MMFR0, TGRAN16) >= 2; } static inline bool isar_feature_aa64_tgran16_2_lpa2(const ARMISARegisters *id) { - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); + unsigned t = FIELD_EX64_IDREG(id, ID_AA64MMFR0, TGRAN16_2); return t >= 3 || (t == 0 && isar_feature_aa64_tgran16_lpa2(id)); } static inline bool isar_feature_aa64_tgran4(const ARMISARegisters *id) { - return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4) >= 0; + return FIELD_SEX64_IDREG(id, ID_AA64MMFR0, TGRAN4) >= 0; } static inline bool isar_feature_aa64_tgran16(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16) >= 1; + return FIELD_EX64_IDREG(id, ID_AA64MMFR0, TGRAN16) >= 1; } static inline bool isar_feature_aa64_tgran64(const ARMISARegisters *id) { - return FIELD_SEX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN64) >= 0; + return FIELD_SEX64_IDREG(id, ID_AA64MMFR0, TGRAN64) >= 0; } static inline bool isar_feature_aa64_tgran4_2(const ARMISARegisters *id) { - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN4_2); + unsigned t = FIELD_EX64_IDREG(id, ID_AA64MMFR0, TGRAN4_2); return t >= 2 || (t == 0 && isar_feature_aa64_tgran4(id)); } static inline bool isar_feature_aa64_tgran16_2(const ARMISARegisters *id) { - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN16_2); + unsigned t = FIELD_EX64_IDREG(id, ID_AA64MMFR0, TGRAN16_2); return t >= 2 || (t == 0 && isar_feature_aa64_tgran16(id)); } static inline bool isar_feature_aa64_tgran64_2(const ARMISARegisters *id) { - unsigned t = FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, TGRAN64_2); + unsigned t = FIELD_EX64_IDREG(id, ID_AA64MMFR0, TGRAN64_2); return t >= 2 || (t == 0 && isar_feature_aa64_tgran64(id)); } static inline bool isar_feature_aa64_fgt(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, FGT) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR0, FGT) != 0; } static inline bool isar_feature_aa64_ecv_traps(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, ECV) > 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR0, ECV) > 0; } static inline bool isar_feature_aa64_ecv(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr0, ID_AA64MMFR0, ECV) > 1; + return FIELD_EX64_IDREG(id, ID_AA64MMFR0, ECV) > 1; } static inline bool isar_feature_aa64_vh(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, VH) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR1, VH) != 0; } static inline bool isar_feature_aa64_lor(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, LO) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR1, LO) != 0; } static inline bool isar_feature_aa64_pan(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR1, PAN) != 0; } static inline bool isar_feature_aa64_ats1e1(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64MMFR1, PAN) >= 2; } static inline bool isar_feature_aa64_pan3(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, PAN) >= 3; + return FIELD_EX64_IDREG(id, ID_AA64MMFR1, PAN) >= 3; } static inline bool isar_feature_aa64_hcx(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HCX) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR1, HCX) != 0; } static inline bool isar_feature_aa64_afp(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, AFP) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR1, AFP) != 0; } static inline bool isar_feature_aa64_tidcp1(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, TIDCP1) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR1, TIDCP1) != 0; } static inline bool isar_feature_aa64_cmow(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, CMOW) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR1, CMOW) != 0; } static inline bool isar_feature_aa64_hafs(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HAFDBS) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR1, HAFDBS) != 0; } static inline bool isar_feature_aa64_hdbs(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, HAFDBS) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64MMFR1, HAFDBS) >= 2; } static inline bool isar_feature_aa64_tts2uxn(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr1, ID_AA64MMFR1, XNX) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR1, XNX) != 0; } static inline bool isar_feature_aa64_uao(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, UAO) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR2, UAO) != 0; } static inline bool isar_feature_aa64_st(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, ST) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR2, ST) != 0; } static inline bool isar_feature_aa64_lse2(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, AT) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR2, AT) != 0; } static inline bool isar_feature_aa64_fwb(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, FWB) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR2, FWB) != 0; } static inline bool isar_feature_aa64_ids(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, IDS) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR2, IDS) != 0; } static inline bool isar_feature_aa64_half_evt(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, EVT) >= 1; + return FIELD_EX64_IDREG(id, ID_AA64MMFR2, EVT) >= 1; } static inline bool isar_feature_aa64_evt(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, EVT) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64MMFR2, EVT) >= 2; } static inline bool isar_feature_aa64_ccidx(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, CCIDX) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR2, CCIDX) != 0; } static inline bool isar_feature_aa64_lva(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, VARANGE) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR2, VARANGE) != 0; } static inline bool isar_feature_aa64_e0pd(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, E0PD) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR2, E0PD) != 0; } static inline bool isar_feature_aa64_nv(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, NV) != 0; + return FIELD_EX64_IDREG(id, ID_AA64MMFR2, NV) != 0; } static inline bool isar_feature_aa64_nv2(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64mmfr2, ID_AA64MMFR2, NV) >= 2; + return FIELD_EX64_IDREG(id, ID_AA64MMFR2, NV) >= 2; } static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 05157a49d7..df9b7cc8c8 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1074,10 +1074,6 @@ struct ArchCPU { uint32_t dbgdidr; uint32_t dbgdevid; uint32_t dbgdevid1; - uint64_t id_aa64mmfr0; - uint64_t id_aa64mmfr1; - uint64_t id_aa64mmfr2; - uint64_t id_aa64mmfr3; uint64_t id_aa64dfr0; uint64_t id_aa64dfr1; uint64_t id_aa64smfr0; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 502aac9173..500f3646bf 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -623,12 +623,12 @@ void arm_cpu_lpa2_finalize(ARMCPU *cpu, Error **errp) return; } - t = cpu->isar.id_aa64mmfr0; + t = GET_IDREG(&cpu->isar, ID_AA64MMFR0); t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16, 2); /* 16k pages w/ LPA2 */ t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4, 1); /* 4k pages w/ LPA2 */ t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16_2, 3); /* 16k stage2 w/ LPA2 */ t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4_2, 3); /* 4k stage2 w/ LPA2 */ - cpu->isar.id_aa64mmfr0 = t; + SET_IDREG(&cpu->isar, ID_AA64MMFR0, t); } static void aarch64_a57_initfn(Object *obj) @@ -673,7 +673,7 @@ static void aarch64_a57_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR0, 0x00002222); cpu->isar.id_aa64dfr0 = 0x10305106; SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); - cpu->isar.id_aa64mmfr0 = 0x00001124; + SET_IDREG(isar, ID_AA64MMFR0, 0x00001124); cpu->isar.dbgdidr = 0x3516d000; cpu->isar.dbgdevid = 0x01110f13; cpu->isar.dbgdevid1 = 0x2; @@ -735,7 +735,7 @@ static void aarch64_a53_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR0, 0x00002222); cpu->isar.id_aa64dfr0 = 0x10305106; SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); - cpu->isar.id_aa64mmfr0 = 0x00001122; /* 40 bit physical addr */ + SET_IDREG(isar, ID_AA64MMFR0, 0x00001122); /* 40 bit physical addr */ cpu->isar.dbgdidr = 0x3516d000; cpu->isar.dbgdevid = 0x00110f13; cpu->isar.dbgdevid1 = 0x1; diff --git a/target/arm/helper.c b/target/arm/helper.c index b221e8df6c..fe0bda749f 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -8043,22 +8043,22 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64mmfr0 }, + .resetvalue = GET_IDREG(isar, ID_AA64MMFR0)}, { .name = "ID_AA64MMFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64mmfr1 }, + .resetvalue = GET_IDREG(isar, ID_AA64MMFR1) }, { .name = "ID_AA64MMFR2_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64mmfr2 }, + .resetvalue = GET_IDREG(isar, ID_AA64MMFR2) }, { .name = "ID_AA64MMFR3_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64mmfr3 }, + .resetvalue = GET_IDREG(isar, ID_AA64MMFR3) }, { .name = "ID_AA64MMFR4_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 7, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index e1bfca5947..37a6303ec2 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -846,14 +846,17 @@ static uint64_t hvf_get_reg(CPUState *cpu, int rt) return val; } -static void clamp_id_aa64mmfr0_parange_to_ipa_size(uint64_t *id_aa64mmfr0) +static void clamp_id_aa64mmfr0_parange_to_ipa_size(ARMISARegisters *isar) { uint32_t ipa_size = chosen_ipa_bit_size ? chosen_ipa_bit_size : hvf_arm_get_max_ipa_bit_size(); + uint64_t id_aa64mmfr0; /* Clamp down the PARange to the IPA size the kernel supports. */ uint8_t index = round_down_to_parange_index(ipa_size); - *id_aa64mmfr0 = (*id_aa64mmfr0 & ~R_ID_AA64MMFR0_PARANGE_MASK) | index; + id_aa64mmfr0 = GET_IDREG(isar, ID_AA64MMFR0); + id_aa64mmfr0 = (id_aa64mmfr0 & ~R_ID_AA64MMFR0_PARANGE_MASK) | index; + SET_IDREG(isar, ID_AA64MMFR0, id_aa64mmfr0); } static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) @@ -870,9 +873,9 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) { HV_SYS_REG_ID_AA64ISAR0_EL1, &host_isar.idregs[ID_AA64ISAR0_EL1_IDX] }, { HV_SYS_REG_ID_AA64ISAR1_EL1, &host_isar.idregs[ID_AA64ISAR1_EL1_IDX] }, /* Add ID_AA64ISAR2_EL1 here when HVF supports it */ - { HV_SYS_REG_ID_AA64MMFR0_EL1, &host_isar.id_aa64mmfr0 }, - { HV_SYS_REG_ID_AA64MMFR1_EL1, &host_isar.id_aa64mmfr1 }, - { HV_SYS_REG_ID_AA64MMFR2_EL1, &host_isar.id_aa64mmfr2 }, + { HV_SYS_REG_ID_AA64MMFR0_EL1, &host_isar.idregs[ID_AA64MMFR0_EL1_IDX] }, + { HV_SYS_REG_ID_AA64MMFR1_EL1, &host_isar.idregs[ID_AA64MMFR1_EL1_IDX] }, + { HV_SYS_REG_ID_AA64MMFR2_EL1, &host_isar.idregs[ID_AA64MMFR2_EL1_IDX] }, /* Add ID_AA64MMFR3_EL1 here when HVF supports it */ }; hv_vcpu_t fd; @@ -899,7 +902,7 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) r |= hv_vcpu_get_sys_reg(fd, HV_SYS_REG_MIDR_EL1, &ahcf->midr); r |= hv_vcpu_destroy(fd); - clamp_id_aa64mmfr0_parange_to_ipa_size(&host_isar.id_aa64mmfr0); + clamp_id_aa64mmfr0_parange_to_ipa_size(&host_isar); /* * Disable SME, which is not properly handled by QEMU hvf yet. @@ -1067,12 +1070,12 @@ int hvf_arch_init_vcpu(CPUState *cpu) /* We're limited to underlying hardware caps, override internal versions */ ret = hv_vcpu_get_sys_reg(cpu->accel->fd, HV_SYS_REG_ID_AA64MMFR0_EL1, - &arm_cpu->isar.id_aa64mmfr0); + &arm_cpu->isar.idregs[ID_AA64MMFR0_EL1_IDX]); assert_hvf_ok(ret); - clamp_id_aa64mmfr0_parange_to_ipa_size(&arm_cpu->isar.id_aa64mmfr0); + clamp_id_aa64mmfr0_parange_to_ipa_size(&arm_cpu->isar); ret = hv_vcpu_set_sys_reg(cpu->accel->fd, HV_SYS_REG_ID_AA64MMFR0_EL1, - arm_cpu->isar.id_aa64mmfr0); + arm_cpu->isar.idregs[ID_AA64MMFR0_EL1_IDX]); assert_hvf_ok(ret); return 0; diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 1e19dba4cb..1dde96fbbd 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -323,14 +323,10 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) err |= get_host_cpu_reg(fd, ahcf, ID_AA64ISAR0_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_AA64ISAR1_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_AA64ISAR2_EL1_IDX); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr0, - ARM64_SYS_REG(3, 0, 0, 7, 0)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr1, - ARM64_SYS_REG(3, 0, 0, 7, 1)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr2, - ARM64_SYS_REG(3, 0, 0, 7, 2)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64mmfr3, - ARM64_SYS_REG(3, 0, 0, 7, 3)); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64MMFR0_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64MMFR1_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64MMFR2_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64MMFR3_EL1_IDX); /* * Note that if AArch32 support is not present in the host, diff --git a/target/arm/ptw.c b/target/arm/ptw.c index 44170d831c..561bf2678e 100644 --- a/target/arm/ptw.c +++ b/target/arm/ptw.c @@ -122,7 +122,7 @@ unsigned int arm_pamax(ARMCPU *cpu) { if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { unsigned int parange = - FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); + FIELD_EX64_IDREG(&cpu->isar, ID_AA64MMFR0, PARANGE); /* * id_aa64mmfr0 is a read-only register so values outside of the @@ -332,7 +332,7 @@ static bool granule_protection_check(CPUARMState *env, uint64_t paddress, * physical address size is invalid. */ pps = FIELD_EX64(gpccr, GPCCR, PPS); - if (pps > FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE)) { + if (pps > FIELD_EX64_IDREG(&cpu->isar, ID_AA64MMFR0, PARANGE)) { goto fault_walk; } pps = pamax_map[pps]; @@ -1703,7 +1703,7 @@ static bool get_phys_addr_lpae(CPUARMState *env, S1Translate *ptw, * ID_AA64MMFR0 is a read-only register so values outside of the * supported mappings can be considered an implementation error. */ - ps = FIELD_EX64(cpu->isar.id_aa64mmfr0, ID_AA64MMFR0, PARANGE); + ps = FIELD_EX64_IDREG(&cpu->isar, ID_AA64MMFR0, PARANGE); ps = MIN(ps, param.ps); assert(ps < ARRAY_SIZE(pamax_map)); outputsize = pamax_map[ps]; diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 7a730c7974..9efb7f0ce8 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -69,8 +69,8 @@ static void aarch64_a35_initfn(Object *obj) cpu->isar.id_aa64dfr1 = 0; SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); SET_IDREG(isar, ID_AA64ISAR1, 0); - cpu->isar.id_aa64mmfr0 = 0x00101122; - cpu->isar.id_aa64mmfr1 = 0; + SET_IDREG(isar, ID_AA64MMFR0, 0x00101122); + SET_IDREG(isar, ID_AA64MMFR1, 0); cpu->clidr = 0x0a200023; cpu->dcz_blocksize = 4; @@ -222,9 +222,9 @@ static void aarch64_a55_initfn(Object *obj) cpu->isar.id_aa64dfr0 = 0x0000000010305408ull; SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000100001ull); - cpu->isar.id_aa64mmfr0 = 0x0000000000101122ull; - cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; - cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; + SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000101122ull); + SET_IDREG(isar, ID_AA64MMFR1, 0x0000000010212122ull); + SET_IDREG(isar, ID_AA64MMFR2, 0x0000000000001011ull); SET_IDREG(isar, ID_AA64PFR0, 0x0000000010112222ull); SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000010ull); cpu->id_afr0 = 0x00000000; @@ -312,7 +312,7 @@ static void aarch64_a72_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR0, 0x00002222); cpu->isar.id_aa64dfr0 = 0x10305106; SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); - cpu->isar.id_aa64mmfr0 = 0x00001124; + SET_IDREG(isar, ID_AA64MMFR0, 0x00001124); cpu->isar.dbgdidr = 0x3516d000; cpu->isar.dbgdevid = 0x01110f13; cpu->isar.dbgdevid1 = 0x2; @@ -355,9 +355,9 @@ static void aarch64_a76_initfn(Object *obj) cpu->isar.id_aa64dfr0 = 0x0000000010305408ull; SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000100001ull); - cpu->isar.id_aa64mmfr0 = 0x0000000000101122ull; - cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; - cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; + SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000101122ull); + SET_IDREG(isar, ID_AA64MMFR1, 0x0000000010212122ull); + SET_IDREG(isar, ID_AA64MMFR2, 0x0000000000001011ull); SET_IDREG(isar, ID_AA64PFR0, 0x1100000010111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000010ull); cpu->id_afr0 = 0x00000000; @@ -430,9 +430,9 @@ static void aarch64_a64fx_initfn(Object *obj) cpu->isar.id_aa64dfr1 = 0x0000000000000000; cpu->id_aa64afr0 = 0x0000000000000000; cpu->id_aa64afr1 = 0x0000000000000000; - cpu->isar.id_aa64mmfr0 = 0x0000000000001122; - cpu->isar.id_aa64mmfr1 = 0x0000000011212100; - cpu->isar.id_aa64mmfr2 = 0x0000000000001011; + SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000001122); + SET_IDREG(isar, ID_AA64MMFR1, 0x0000000011212100); + SET_IDREG(isar, ID_AA64MMFR2, 0x0000000000001011); SET_IDREG(isar, ID_AA64ISAR0, 0x0000000010211120); SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000010001); SET_IDREG(isar, ID_AA64ZFR0, 0x0000000000000000); @@ -603,9 +603,9 @@ static void aarch64_neoverse_n1_initfn(Object *obj) cpu->isar.id_aa64dfr0 = 0x0000000110305408ull; SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000100001ull); - cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; - cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; - cpu->isar.id_aa64mmfr2 = 0x0000000000001011ull; + SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000101125ull); + SET_IDREG(isar, ID_AA64MMFR1, 0x0000000010212122ull); + SET_IDREG(isar, ID_AA64MMFR2, 0x0000000000001011ull); SET_IDREG(isar, ID_AA64PFR0, 0x1100000010111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000020ull); cpu->id_afr0 = 0x00000000; @@ -682,9 +682,9 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->isar.id_aa64dfr1 = 0x00000000; SET_IDREG(isar, ID_AA64ISAR0, 0x1011111110212120ull); /* with FEAT_RNG */ SET_IDREG(isar, ID_AA64ISAR1, 0x0011000001211032ull); - cpu->isar.id_aa64mmfr0 = 0x0000000000101125ull; - cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; - cpu->isar.id_aa64mmfr2 = 0x0220011102101011ull; + SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000101125ull); + SET_IDREG(isar, ID_AA64MMFR1, 0x0000000010212122ull), + SET_IDREG(isar, ID_AA64MMFR2, 0x0220011102101011ull), SET_IDREG(isar, ID_AA64PFR0, 0x1101110120111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000020ull); cpu->id_afr0 = 0x00000000; @@ -931,9 +931,9 @@ static void aarch64_a710_initfn(Object *obj) cpu->id_aa64afr1 = 0; SET_IDREG(isar, ID_AA64ISAR0, 0x0221111110212120ull); /* with Crypto */ SET_IDREG(isar, ID_AA64ISAR1, 0x0010111101211052ull); - cpu->isar.id_aa64mmfr0 = 0x0000022200101122ull; - cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; - cpu->isar.id_aa64mmfr2 = 0x1221011110101011ull; + SET_IDREG(isar, ID_AA64MMFR0, 0x0000022200101122ull); + SET_IDREG(isar, ID_AA64MMFR1, 0x0000000010212122ull); + SET_IDREG(isar, ID_AA64MMFR2, 0x1221011110101011ull); cpu->clidr = 0x0000001482000023ull; cpu->gm_blocksize = 4; cpu->ctr = 0x000000049444c004ull; @@ -1033,9 +1033,9 @@ static void aarch64_neoverse_n2_initfn(Object *obj) cpu->id_aa64afr1 = 0; SET_IDREG(isar, ID_AA64ISAR0, 0x1221111110212120ull); /* with Crypto and FEAT_RNG */ SET_IDREG(isar, ID_AA64ISAR1, 0x0011111101211052ull); - cpu->isar.id_aa64mmfr0 = 0x0000022200101125ull; - cpu->isar.id_aa64mmfr1 = 0x0000000010212122ull; - cpu->isar.id_aa64mmfr2 = 0x1221011112101011ull; + SET_IDREG(isar, ID_AA64MMFR0, 0x0000022200101125ull); + SET_IDREG(isar, ID_AA64MMFR1, 0x0000000010212122ull); + SET_IDREG(isar, ID_AA64MMFR2, 0x1221011112101011ull); cpu->clidr = 0x0000001482000023ull; cpu->gm_blocksize = 4; cpu->ctr = 0x00000004b444c004ull; @@ -1206,7 +1206,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64PFR1, NMI, 1); /* FEAT_NMI */ SET_IDREG(isar, ID_AA64PFR1, t); - t = cpu->isar.id_aa64mmfr0; + t = GET_IDREG(isar, ID_AA64MMFR0); t = FIELD_DP64(t, ID_AA64MMFR0, PARANGE, 6); /* FEAT_LPA: 52 bits */ t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16, 1); /* 16k pages supported */ t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN16_2, 2); /* 16k stage2 supported */ @@ -1214,9 +1214,9 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR0, TGRAN4_2, 2); /* 4k stage2 supported */ t = FIELD_DP64(t, ID_AA64MMFR0, FGT, 1); /* FEAT_FGT */ t = FIELD_DP64(t, ID_AA64MMFR0, ECV, 2); /* FEAT_ECV */ - cpu->isar.id_aa64mmfr0 = t; + SET_IDREG(isar, ID_AA64MMFR0, t); - t = cpu->isar.id_aa64mmfr1; + t = GET_IDREG(isar, ID_AA64MMFR1); t = FIELD_DP64(t, ID_AA64MMFR1, HAFDBS, 2); /* FEAT_HAFDBS */ t = FIELD_DP64(t, ID_AA64MMFR1, VMIDBITS, 2); /* FEAT_VMID16 */ t = FIELD_DP64(t, ID_AA64MMFR1, VH, 1); /* FEAT_VHE */ @@ -1229,9 +1229,9 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR1, AFP, 1); /* FEAT_AFP */ t = FIELD_DP64(t, ID_AA64MMFR1, TIDCP1, 1); /* FEAT_TIDCP1 */ t = FIELD_DP64(t, ID_AA64MMFR1, CMOW, 1); /* FEAT_CMOW */ - cpu->isar.id_aa64mmfr1 = t; + SET_IDREG(isar, ID_AA64MMFR1, t); - t = cpu->isar.id_aa64mmfr2; + t = GET_IDREG(isar, ID_AA64MMFR2); t = FIELD_DP64(t, ID_AA64MMFR2, CNP, 1); /* FEAT_TTCNP */ t = FIELD_DP64(t, ID_AA64MMFR2, UAO, 1); /* FEAT_UAO */ t = FIELD_DP64(t, ID_AA64MMFR2, IESB, 1); /* FEAT_IESB */ @@ -1245,11 +1245,9 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64MMFR2, BBM, 2); /* FEAT_BBM at level 2 */ t = FIELD_DP64(t, ID_AA64MMFR2, EVT, 2); /* FEAT_EVT */ t = FIELD_DP64(t, ID_AA64MMFR2, E0PD, 1); /* FEAT_E0PD */ - cpu->isar.id_aa64mmfr2 = t; + SET_IDREG(isar, ID_AA64MMFR2, t); - t = cpu->isar.id_aa64mmfr3; - t = FIELD_DP64(t, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ - cpu->isar.id_aa64mmfr3 = t; + FIELD_DP64_IDREG(isar, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ t = GET_IDREG(isar, ID_AA64ZFR0); t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 1); From def3f1c1026af66d5672f10b3e6cbb87e4e20f73 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 1 Jul 2025 15:08:27 +0100 Subject: [PATCH 1732/2760] arm/cpu: Store aa64dfr0/1 into the idregs array Reviewed-by: Richard Henderson Reviewed-by: Sebastian Ott Signed-off-by: Eric Auger Signed-off-by: Cornelia Huck Message-id: 20250617153931.1330449-7-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 16 ++++++++-------- target/arm/cpu.c | 15 +++++---------- target/arm/cpu.h | 2 -- target/arm/cpu64.c | 4 ++-- target/arm/helper.c | 4 ++-- target/arm/hvf/hvf.c | 4 ++-- target/arm/internals.h | 6 +++--- target/arm/kvm.c | 12 +++++------- target/arm/tcg/cpu64.c | 32 ++++++++++++++++---------------- 9 files changed, 43 insertions(+), 52 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 89c9278639..9517e8a74c 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -901,30 +901,30 @@ static inline bool isar_feature_aa64_nv2(const ARMISARegisters *id) static inline bool isar_feature_aa64_pmuv3p1(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 4 && - FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; + return FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) >= 4 && + FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) != 0xf; } static inline bool isar_feature_aa64_pmuv3p4(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 5 && - FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; + return FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) >= 5 && + FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) != 0xf; } static inline bool isar_feature_aa64_pmuv3p5(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) >= 6 && - FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, PMUVER) != 0xf; + return FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) >= 6 && + FIELD_EX64_IDREG(id, ID_AA64DFR0, PMUVER) != 0xf; } static inline bool isar_feature_aa64_debugv8p2(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64dfr0, ID_AA64DFR0, DEBUGVER) >= 8; + return FIELD_EX64_IDREG(id, ID_AA64DFR0, DEBUGVER) >= 8; } static inline bool isar_feature_aa64_doublelock(const ARMISARegisters *id) { - return FIELD_SEX64(id->id_aa64dfr0, ID_AA64DFR0, DOUBLELOCK) >= 0; + return FIELD_SEX64_IDREG(id, ID_AA64DFR0, DOUBLELOCK) >= 0; } static inline bool isar_feature_aa64_sve2(const ARMISARegisters *id) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index d39e8dc956..400bee8494 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2347,8 +2347,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) cpu); #endif } else { - cpu->isar.id_aa64dfr0 = - FIELD_DP64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, PMUVER, 0); + FIELD_DP64_IDREG(isar, ID_AA64DFR0, PMUVER, 0); cpu->isar.id_dfr0 = FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, PERFMON, 0); cpu->pmceid0 = 0; cpu->pmceid1 = 0; @@ -2408,19 +2407,15 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * try to access the non-existent system registers for them. */ /* FEAT_SPE (Statistical Profiling Extension) */ - cpu->isar.id_aa64dfr0 = - FIELD_DP64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, PMSVER, 0); + FIELD_DP64_IDREG(isar, ID_AA64DFR0, PMSVER, 0); /* FEAT_TRBE (Trace Buffer Extension) */ - cpu->isar.id_aa64dfr0 = - FIELD_DP64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, TRACEBUFFER, 0); + FIELD_DP64_IDREG(isar, ID_AA64DFR0, TRACEBUFFER, 0); /* FEAT_TRF (Self-hosted Trace Extension) */ - cpu->isar.id_aa64dfr0 = - FIELD_DP64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, TRACEFILT, 0); + FIELD_DP64_IDREG(isar, ID_AA64DFR0, TRACEFILT, 0); cpu->isar.id_dfr0 = FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, TRACEFILT, 0); /* Trace Macrocell system register access */ - cpu->isar.id_aa64dfr0 = - FIELD_DP64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, TRACEVER, 0); + FIELD_DP64_IDREG(isar, ID_AA64DFR0, TRACEVER, 0); cpu->isar.id_dfr0 = FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, COPTRC, 0); /* Memory mapped trace */ diff --git a/target/arm/cpu.h b/target/arm/cpu.h index df9b7cc8c8..c7935377c6 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1074,8 +1074,6 @@ struct ArchCPU { uint32_t dbgdidr; uint32_t dbgdevid; uint32_t dbgdevid1; - uint64_t id_aa64dfr0; - uint64_t id_aa64dfr1; uint64_t id_aa64smfr0; uint64_t reset_pmcr_el0; uint64_t idregs[NUM_ID_IDX]; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 500f3646bf..a215ba8b47 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -671,7 +671,7 @@ static void aarch64_a57_initfn(Object *obj) cpu->isar.id_isar5 = 0x00011121; cpu->isar.id_isar6 = 0; SET_IDREG(isar, ID_AA64PFR0, 0x00002222); - cpu->isar.id_aa64dfr0 = 0x10305106; + SET_IDREG(isar, ID_AA64DFR0, 0x10305106); SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); SET_IDREG(isar, ID_AA64MMFR0, 0x00001124); cpu->isar.dbgdidr = 0x3516d000; @@ -733,7 +733,7 @@ static void aarch64_a53_initfn(Object *obj) cpu->isar.id_isar5 = 0x00011121; cpu->isar.id_isar6 = 0; SET_IDREG(isar, ID_AA64PFR0, 0x00002222); - cpu->isar.id_aa64dfr0 = 0x10305106; + SET_IDREG(isar, ID_AA64DFR0, 0x10305106); SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); SET_IDREG(isar, ID_AA64MMFR0, 0x00001122); /* 40 bit physical addr */ cpu->isar.dbgdidr = 0x3516d000; diff --git a/target/arm/helper.c b/target/arm/helper.c index fe0bda749f..2ac132c1db 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7963,12 +7963,12 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64dfr0 }, + .resetvalue = GET_IDREG(isar, ID_AA64DFR0) }, { .name = "ID_AA64DFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64dfr1 }, + .resetvalue = GET_IDREG(isar, ID_AA64DFR1) }, { .name = "ID_AA64DFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 37a6303ec2..5c95ccc5b8 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -868,8 +868,8 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) } regs[] = { { HV_SYS_REG_ID_AA64PFR0_EL1, &host_isar.idregs[ID_AA64PFR0_EL1_IDX] }, { HV_SYS_REG_ID_AA64PFR1_EL1, &host_isar.idregs[ID_AA64PFR1_EL1_IDX] }, - { HV_SYS_REG_ID_AA64DFR0_EL1, &host_isar.id_aa64dfr0 }, - { HV_SYS_REG_ID_AA64DFR1_EL1, &host_isar.id_aa64dfr1 }, + { HV_SYS_REG_ID_AA64DFR0_EL1, &host_isar.idregs[ID_AA64DFR0_EL1_IDX] }, + { HV_SYS_REG_ID_AA64DFR1_EL1, &host_isar.idregs[ID_AA64DFR1_EL1_IDX] }, { HV_SYS_REG_ID_AA64ISAR0_EL1, &host_isar.idregs[ID_AA64ISAR0_EL1_IDX] }, { HV_SYS_REG_ID_AA64ISAR1_EL1, &host_isar.idregs[ID_AA64ISAR1_EL1_IDX] }, /* Add ID_AA64ISAR2_EL1 here when HVF supports it */ diff --git a/target/arm/internals.h b/target/arm/internals.h index 3360de9150..6216f68c94 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1175,7 +1175,7 @@ static inline bool regime_using_lpae_format(CPUARMState *env, ARMMMUIdx mmu_idx) static inline int arm_num_brps(ARMCPU *cpu) { if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - return FIELD_EX64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, BRPS) + 1; + return FIELD_EX64_IDREG(&cpu->isar, ID_AA64DFR0, BRPS) + 1; } else { return FIELD_EX32(cpu->isar.dbgdidr, DBGDIDR, BRPS) + 1; } @@ -1189,7 +1189,7 @@ static inline int arm_num_brps(ARMCPU *cpu) static inline int arm_num_wrps(ARMCPU *cpu) { if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - return FIELD_EX64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, WRPS) + 1; + return FIELD_EX64_IDREG(&cpu->isar, ID_AA64DFR0, WRPS) + 1; } else { return FIELD_EX32(cpu->isar.dbgdidr, DBGDIDR, WRPS) + 1; } @@ -1203,7 +1203,7 @@ static inline int arm_num_wrps(ARMCPU *cpu) static inline int arm_num_ctx_cmps(ARMCPU *cpu) { if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) { - return FIELD_EX64(cpu->isar.id_aa64dfr0, ID_AA64DFR0, CTX_CMPS) + 1; + return FIELD_EX64_IDREG(&cpu->isar, ID_AA64DFR0, CTX_CMPS) + 1; } else { return FIELD_EX32(cpu->isar.dbgdidr, DBGDIDR, CTX_CMPS) + 1; } diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 1dde96fbbd..479e5860e0 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -316,10 +316,8 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) err |= get_host_cpu_reg(fd, ahcf, ID_AA64PFR1_EL1_IDX); err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64smfr0, ARM64_SYS_REG(3, 0, 0, 4, 5)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64dfr0, - ARM64_SYS_REG(3, 0, 0, 5, 0)); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64dfr1, - ARM64_SYS_REG(3, 0, 0, 5, 1)); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64DFR0_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64DFR1_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_AA64ISAR0_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_AA64ISAR1_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_AA64ISAR2_EL1_IDX); @@ -390,10 +388,10 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * We only do this if the CPU supports AArch32 at EL1. */ if (FIELD_EX32_IDREG(&ahcf->isar, ID_AA64PFR0, EL1) >= 2) { - int wrps = FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, WRPS); - int brps = FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, BRPS); + int wrps = FIELD_EX64_IDREG(&ahcf->isar, ID_AA64DFR0, WRPS); + int brps = FIELD_EX64_IDREG(&ahcf->isar, ID_AA64DFR0, BRPS); int ctx_cmps = - FIELD_EX64(ahcf->isar.id_aa64dfr0, ID_AA64DFR0, CTX_CMPS); + FIELD_EX64_IDREG(&ahcf->isar, ID_AA64DFR0, CTX_CMPS); int version = 6; /* ARMv8 debug architecture */ bool has_el3 = !!FIELD_EX32_IDREG(&ahcf->isar, ID_AA64PFR0, EL3); diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 9efb7f0ce8..7e18d31a25 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -65,8 +65,8 @@ static void aarch64_a35_initfn(Object *obj) cpu->isar.id_isar5 = 0x00011121; SET_IDREG(isar, ID_AA64PFR0, 0x00002222); SET_IDREG(isar, ID_AA64PFR1, 0); - cpu->isar.id_aa64dfr0 = 0x10305106; - cpu->isar.id_aa64dfr1 = 0; + SET_IDREG(isar, ID_AA64DFR0, 0x10305106); + SET_IDREG(isar, ID_AA64DFR1, 0); SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); SET_IDREG(isar, ID_AA64ISAR1, 0); SET_IDREG(isar, ID_AA64MMFR0, 0x00101122); @@ -219,7 +219,7 @@ static void aarch64_a55_initfn(Object *obj) cpu->clidr = 0x82000023; cpu->ctr = 0x84448004; /* L1Ip = VIPT */ cpu->dcz_blocksize = 4; /* 64 bytes */ - cpu->isar.id_aa64dfr0 = 0x0000000010305408ull; + SET_IDREG(isar, ID_AA64DFR0, 0x0000000010305408ull); SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000100001ull); SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000101122ull); @@ -310,7 +310,7 @@ static void aarch64_a72_initfn(Object *obj) cpu->isar.id_isar4 = 0x00011142; cpu->isar.id_isar5 = 0x00011121; SET_IDREG(isar, ID_AA64PFR0, 0x00002222); - cpu->isar.id_aa64dfr0 = 0x10305106; + SET_IDREG(isar, ID_AA64DFR0, 0x10305106); SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); SET_IDREG(isar, ID_AA64MMFR0, 0x00001124); cpu->isar.dbgdidr = 0x3516d000; @@ -352,7 +352,7 @@ static void aarch64_a76_initfn(Object *obj) cpu->clidr = 0x82000023; cpu->ctr = 0x8444C004; cpu->dcz_blocksize = 4; - cpu->isar.id_aa64dfr0 = 0x0000000010305408ull; + SET_IDREG(isar, ID_AA64DFR0, 0x0000000010305408ull), SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000100001ull); SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000101122ull); @@ -426,8 +426,8 @@ static void aarch64_a64fx_initfn(Object *obj) cpu->reset_sctlr = 0x30000180; SET_IDREG(isar, ID_AA64PFR0, 0x0000000101111111); /* No RAS Extensions */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000000); - cpu->isar.id_aa64dfr0 = 0x0000000010305408; - cpu->isar.id_aa64dfr1 = 0x0000000000000000; + SET_IDREG(isar, ID_AA64DFR0, 0x0000000010305408), + SET_IDREG(isar, ID_AA64DFR1, 0x0000000000000000), cpu->id_aa64afr0 = 0x0000000000000000; cpu->id_aa64afr1 = 0x0000000000000000; SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000001122); @@ -600,7 +600,7 @@ static void aarch64_neoverse_n1_initfn(Object *obj) cpu->clidr = 0x82000023; cpu->ctr = 0x8444c004; cpu->dcz_blocksize = 4; - cpu->isar.id_aa64dfr0 = 0x0000000110305408ull; + SET_IDREG(isar, ID_AA64DFR0, 0x0000000110305408ull); SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000100001ull); SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000101125ull); @@ -678,8 +678,8 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->dcz_blocksize = 4; cpu->id_aa64afr0 = 0x00000000; cpu->id_aa64afr1 = 0x00000000; - cpu->isar.id_aa64dfr0 = 0x000001f210305519ull; - cpu->isar.id_aa64dfr1 = 0x00000000; + SET_IDREG(isar, ID_AA64DFR0, 0x000001f210305519ull), + SET_IDREG(isar, ID_AA64DFR1, 0x00000000), SET_IDREG(isar, ID_AA64ISAR0, 0x1011111110212120ull); /* with FEAT_RNG */ SET_IDREG(isar, ID_AA64ISAR1, 0x0011000001211032ull); SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000101125ull); @@ -925,8 +925,8 @@ static void aarch64_a710_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR0, 0x1201111120111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000221ull); SET_IDREG(isar, ID_AA64ZFR0, 0x0000110100110021ull); /* with Crypto */ - cpu->isar.id_aa64dfr0 = 0x000011f010305619ull; - cpu->isar.id_aa64dfr1 = 0; + SET_IDREG(isar, ID_AA64DFR0, 0x000011f010305619ull); + SET_IDREG(isar, ID_AA64DFR1, 0); cpu->id_aa64afr0 = 0; cpu->id_aa64afr1 = 0; SET_IDREG(isar, ID_AA64ISAR0, 0x0221111110212120ull); /* with Crypto */ @@ -1027,8 +1027,8 @@ static void aarch64_neoverse_n2_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR0, 0x1201111120111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000221ull); SET_IDREG(isar, ID_AA64ZFR0, 0x0000110100110021ull); /* with Crypto */ - cpu->isar.id_aa64dfr0 = 0x000011f210305619ull; - cpu->isar.id_aa64dfr1 = 0; + SET_IDREG(isar, ID_AA64DFR0, 0x000011f210305619ull); + SET_IDREG(isar, ID_AA64DFR1, 0); cpu->id_aa64afr0 = 0; cpu->id_aa64afr1 = 0; SET_IDREG(isar, ID_AA64ISAR0, 0x1221111110212120ull); /* with Crypto and FEAT_RNG */ @@ -1261,11 +1261,11 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64ZFR0, F64MM, 1); /* FEAT_F64MM */ SET_IDREG(isar, ID_AA64ZFR0, t); - t = cpu->isar.id_aa64dfr0; + t = GET_IDREG(isar, ID_AA64DFR0); t = FIELD_DP64(t, ID_AA64DFR0, DEBUGVER, 10); /* FEAT_Debugv8p8 */ t = FIELD_DP64(t, ID_AA64DFR0, PMUVER, 6); /* FEAT_PMUv3p5 */ t = FIELD_DP64(t, ID_AA64DFR0, HPMN0, 1); /* FEAT_HPMN0 */ - cpu->isar.id_aa64dfr0 = t; + SET_IDREG(isar, ID_AA64DFR0, t); t = cpu->isar.id_aa64smfr0; t = FIELD_DP64(t, ID_AA64SMFR0, F32F32, 1); /* FEAT_SME */ From 29279773649a4757b2d7d9eddf98685068e11154 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 1 Jul 2025 15:08:27 +0100 Subject: [PATCH 1733/2760] arm/cpu: Store aa64smfr0 into the idregs array Reviewed-by: Richard Henderson Reviewed-by: Sebastian Ott Signed-off-by: Eric Auger Signed-off-by: Cornelia Huck Message-id: 20250617153931.1330449-8-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 6 +++--- target/arm/cpu.h | 1 - target/arm/cpu64.c | 7 ++----- target/arm/helper.c | 2 +- target/arm/kvm.c | 3 +-- target/arm/tcg/cpu64.c | 4 ++-- 6 files changed, 9 insertions(+), 14 deletions(-) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 9517e8a74c..051ed7b884 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -979,17 +979,17 @@ static inline bool isar_feature_aa64_sve_f64mm(const ARMISARegisters *id) static inline bool isar_feature_aa64_sme_f64f64(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, F64F64); + return FIELD_EX64_IDREG(id, ID_AA64SMFR0, F64F64); } static inline bool isar_feature_aa64_sme_i16i64(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, I16I64) == 0xf; + return FIELD_EX64_IDREG(id, ID_AA64SMFR0, I16I64) == 0xf; } static inline bool isar_feature_aa64_sme_fa64(const ARMISARegisters *id) { - return FIELD_EX64(id->id_aa64smfr0, ID_AA64SMFR0, FA64); + return FIELD_EX64_IDREG(id, ID_AA64SMFR0, FA64); } /* diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c7935377c6..1083ae7623 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1074,7 +1074,6 @@ struct ArchCPU { uint32_t dbgdidr; uint32_t dbgdevid; uint32_t dbgdevid1; - uint64_t id_aa64smfr0; uint64_t reset_pmcr_el0; uint64_t idregs[NUM_ID_IDX]; } isar; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index a215ba8b47..0f938155d2 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -328,7 +328,7 @@ void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp) if (vq_map == 0) { if (!cpu_isar_feature(aa64_sme, cpu)) { - cpu->isar.id_aa64smfr0 = 0; + SET_IDREG(&cpu->isar, ID_AA64SMFR0, 0); return; } @@ -381,11 +381,8 @@ static bool cpu_arm_get_sme_fa64(Object *obj, Error **errp) static void cpu_arm_set_sme_fa64(Object *obj, bool value, Error **errp) { ARMCPU *cpu = ARM_CPU(obj); - uint64_t t; - t = cpu->isar.id_aa64smfr0; - t = FIELD_DP64(t, ID_AA64SMFR0, FA64, value); - cpu->isar.id_aa64smfr0 = t; + FIELD_DP64_IDREG(&cpu->isar, ID_AA64SMFR0, FA64, value); } #ifdef CONFIG_USER_ONLY diff --git a/target/arm/helper.c b/target/arm/helper.c index 2ac132c1db..39729d3a8d 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7948,7 +7948,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_aa64smfr0 }, + .resetvalue = GET_IDREG(isar, ID_AA64SMFR0)}, { .name = "ID_AA64PFR6_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 4, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 479e5860e0..8736855861 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -314,8 +314,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) err = 0; } else { err |= get_host_cpu_reg(fd, ahcf, ID_AA64PFR1_EL1_IDX); - err |= read_sys_reg64(fdarray[2], &ahcf->isar.id_aa64smfr0, - ARM64_SYS_REG(3, 0, 0, 4, 5)); + err |= get_host_cpu_reg(fd, ahcf, ID_AA64SMFR0_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_AA64DFR0_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_AA64DFR1_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_AA64ISAR0_EL1_IDX); diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 7e18d31a25..80a99ab025 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1267,7 +1267,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64DFR0, HPMN0, 1); /* FEAT_HPMN0 */ SET_IDREG(isar, ID_AA64DFR0, t); - t = cpu->isar.id_aa64smfr0; + t = GET_IDREG(isar, ID_AA64SMFR0); t = FIELD_DP64(t, ID_AA64SMFR0, F32F32, 1); /* FEAT_SME */ t = FIELD_DP64(t, ID_AA64SMFR0, B16F32, 1); /* FEAT_SME */ t = FIELD_DP64(t, ID_AA64SMFR0, F16F32, 1); /* FEAT_SME */ @@ -1275,7 +1275,7 @@ void aarch64_max_tcg_initfn(Object *obj) t = FIELD_DP64(t, ID_AA64SMFR0, F64F64, 1); /* FEAT_SME_F64F64 */ t = FIELD_DP64(t, ID_AA64SMFR0, I16I64, 0xf); /* FEAT_SME_I16I64 */ t = FIELD_DP64(t, ID_AA64SMFR0, FA64, 1); /* FEAT_SME_FA64 */ - cpu->isar.id_aa64smfr0 = t; + SET_IDREG(isar, ID_AA64SMFR0, t); /* Replicate the same data to the 32-bit id registers. */ aa32_max_features(cpu); From c0c2344c4354180edf2476ae5764548a36fa9b67 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 1 Jul 2025 15:08:28 +0100 Subject: [PATCH 1734/2760] arm/cpu: Store id_isar0-7 into the idregs array Reviewed-by: Richard Henderson Reviewed-by: Sebastian Ott Signed-off-by: Eric Auger Signed-off-by: Cornelia Huck Message-id: 20250617153931.1330449-9-cohuck@redhat.com Signed-off-by: Peter Maydell --- hw/intc/armv7m_nvic.c | 12 ++-- target/arm/cpu-features.h | 36 +++++----- target/arm/cpu.c | 24 +++---- target/arm/cpu.h | 7 -- target/arm/cpu64.c | 28 ++++---- target/arm/helper.c | 14 ++-- target/arm/kvm.c | 21 ++---- target/arm/tcg/cpu-v7m.c | 90 +++++++++++++----------- target/arm/tcg/cpu32.c | 144 +++++++++++++++++++++----------------- target/arm/tcg/cpu64.c | 108 ++++++++++++++-------------- 10 files changed, 243 insertions(+), 241 deletions(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 83ff74f899..fdb7f58e36 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -1303,32 +1303,32 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_isar0; + return GET_IDREG(&cpu->isar, ID_ISAR0); case 0xd64: /* ISAR1. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_isar1; + return GET_IDREG(&cpu->isar, ID_ISAR1); case 0xd68: /* ISAR2. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_isar2; + return GET_IDREG(&cpu->isar, ID_ISAR2); case 0xd6c: /* ISAR3. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_isar3; + return GET_IDREG(&cpu->isar, ID_ISAR3); case 0xd70: /* ISAR4. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_isar4; + return GET_IDREG(&cpu->isar, ID_ISAR4); case 0xd74: /* ISAR5. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_isar5; + return GET_IDREG(&cpu->isar, ID_ISAR5); case 0xd78: /* CLIDR */ return cpu->clidr; case 0xd7c: /* CTR */ diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 051ed7b884..75a2cc4077 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -46,93 +46,93 @@ */ static inline bool isar_feature_aa32_thumb_div(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR0, DIVIDE) != 0; } static inline bool isar_feature_aa32_arm_div(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar0, ID_ISAR0, DIVIDE) > 1; + return FIELD_EX32_IDREG(id, ID_ISAR0, DIVIDE) > 1; } static inline bool isar_feature_aa32_lob(const ARMISARegisters *id) { /* (M-profile) low-overhead loops and branch future */ - return FIELD_EX32(id->id_isar0, ID_ISAR0, CMPBRANCH) >= 3; + return FIELD_EX32_IDREG(id, ID_ISAR0, CMPBRANCH) >= 3; } static inline bool isar_feature_aa32_jazelle(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar1, ID_ISAR1, JAZELLE) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR1, JAZELLE) != 0; } static inline bool isar_feature_aa32_aes(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR5, AES) != 0; } static inline bool isar_feature_aa32_pmull(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, AES) > 1; + return FIELD_EX32_IDREG(id, ID_ISAR5, AES) > 1; } static inline bool isar_feature_aa32_sha1(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA1) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR5, SHA1) != 0; } static inline bool isar_feature_aa32_sha2(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, SHA2) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR5, SHA2) != 0; } static inline bool isar_feature_aa32_crc32(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, CRC32) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR5, CRC32) != 0; } static inline bool isar_feature_aa32_rdm(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, RDM) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR5, RDM) != 0; } static inline bool isar_feature_aa32_vcma(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar5, ID_ISAR5, VCMA) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR5, VCMA) != 0; } static inline bool isar_feature_aa32_jscvt(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, JSCVT) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR6, JSCVT) != 0; } static inline bool isar_feature_aa32_dp(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, DP) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR6, DP) != 0; } static inline bool isar_feature_aa32_fhm(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, FHM) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR6, FHM) != 0; } static inline bool isar_feature_aa32_sb(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, SB) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR6, SB) != 0; } static inline bool isar_feature_aa32_predinv(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, SPECRES) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR6, SPECRES) != 0; } static inline bool isar_feature_aa32_bf16(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, BF16) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR6, BF16) != 0; } static inline bool isar_feature_aa32_i8mm(const ARMISARegisters *id) { - return FIELD_EX32(id->id_isar6, ID_ISAR6, I8MM) != 0; + return FIELD_EX32_IDREG(id, ID_ISAR6, I8MM) != 0; } static inline bool isar_feature_aa32_ras(const ARMISARegisters *id) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 400bee8494..cf811e47d9 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2126,10 +2126,10 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) FIELD_DP64_IDREG(isar, ID_AA64PFR0, FP, 0xf); - u = cpu->isar.id_isar6; + u = GET_IDREG(isar, ID_ISAR6); u = FIELD_DP32(u, ID_ISAR6, JSCVT, 0); u = FIELD_DP32(u, ID_ISAR6, BF16, 0); - cpu->isar.id_isar6 = u; + SET_IDREG(isar, ID_ISAR6, u); u = cpu->isar.mvfr0; u = FIELD_DP32(u, MVFR0, FPSP, 0); @@ -2181,20 +2181,20 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) FIELD_DP64_IDREG(isar, ID_AA64PFR0, ADVSIMD, 0xf); - u = cpu->isar.id_isar5; + u = GET_IDREG(isar, ID_ISAR5); u = FIELD_DP32(u, ID_ISAR5, AES, 0); u = FIELD_DP32(u, ID_ISAR5, SHA1, 0); u = FIELD_DP32(u, ID_ISAR5, SHA2, 0); u = FIELD_DP32(u, ID_ISAR5, RDM, 0); u = FIELD_DP32(u, ID_ISAR5, VCMA, 0); - cpu->isar.id_isar5 = u; + SET_IDREG(isar, ID_ISAR5, u); - u = cpu->isar.id_isar6; + u = GET_IDREG(isar, ID_ISAR6); u = FIELD_DP32(u, ID_ISAR6, DP, 0); u = FIELD_DP32(u, ID_ISAR6, FHM, 0); u = FIELD_DP32(u, ID_ISAR6, BF16, 0); u = FIELD_DP32(u, ID_ISAR6, I8MM, 0); - cpu->isar.id_isar6 = u; + SET_IDREG(isar, ID_ISAR6, u); if (!arm_feature(env, ARM_FEATURE_M)) { u = cpu->isar.mvfr1; @@ -2232,19 +2232,17 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) unset_feature(env, ARM_FEATURE_THUMB_DSP); - u = cpu->isar.id_isar1; - u = FIELD_DP32(u, ID_ISAR1, EXTEND, 1); - cpu->isar.id_isar1 = u; + FIELD_DP32_IDREG(isar, ID_ISAR1, EXTEND, 1); - u = cpu->isar.id_isar2; + u = GET_IDREG(isar, ID_ISAR2); u = FIELD_DP32(u, ID_ISAR2, MULTU, 1); u = FIELD_DP32(u, ID_ISAR2, MULTS, 1); - cpu->isar.id_isar2 = u; + SET_IDREG(isar, ID_ISAR2, u); - u = cpu->isar.id_isar3; + u = GET_IDREG(isar, ID_ISAR3); u = FIELD_DP32(u, ID_ISAR3, SIMD, 1); u = FIELD_DP32(u, ID_ISAR3, SATURATE, 0); - cpu->isar.id_isar3 = u; + SET_IDREG(isar, ID_ISAR3, u); } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 1083ae7623..353c18e679 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1050,13 +1050,6 @@ struct ArchCPU { * field by reading the value from the KVM vCPU. */ struct ARMISARegisters { - uint32_t id_isar0; - uint32_t id_isar1; - uint32_t id_isar2; - uint32_t id_isar3; - uint32_t id_isar4; - uint32_t id_isar5; - uint32_t id_isar6; uint32_t id_mmfr0; uint32_t id_mmfr1; uint32_t id_mmfr2; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 0f938155d2..6be62c0711 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -660,13 +660,13 @@ static void aarch64_a57_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x40000000; cpu->isar.id_mmfr2 = 0x01260000; cpu->isar.id_mmfr3 = 0x02102211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00011142; - cpu->isar.id_isar5 = 0x00011121; - cpu->isar.id_isar6 = 0; + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232042); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00011142); + SET_IDREG(isar, ID_ISAR5, 0x00011121); + SET_IDREG(isar, ID_ISAR6, 0); SET_IDREG(isar, ID_AA64PFR0, 0x00002222); SET_IDREG(isar, ID_AA64DFR0, 0x10305106); SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); @@ -722,13 +722,13 @@ static void aarch64_a53_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x40000000; cpu->isar.id_mmfr2 = 0x01260000; cpu->isar.id_mmfr3 = 0x02102211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00011142; - cpu->isar.id_isar5 = 0x00011121; - cpu->isar.id_isar6 = 0; + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232042); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00011142); + SET_IDREG(isar, ID_ISAR5, 0x00011121); + SET_IDREG(isar, ID_ISAR6, 0); SET_IDREG(isar, ID_AA64PFR0, 0x00002222); SET_IDREG(isar, ID_AA64DFR0, 0x10305106); SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); diff --git a/target/arm/helper.c b/target/arm/helper.c index 39729d3a8d..7e0b3f164e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7830,32 +7830,32 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar0 }, + .resetvalue = GET_IDREG(isar, ID_ISAR0)}, { .name = "ID_ISAR1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 1, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar1 }, + .resetvalue = GET_IDREG(isar, ID_ISAR1)}, { .name = "ID_ISAR2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar2 }, + .resetvalue = GET_IDREG(isar, ID_ISAR2)}, { .name = "ID_ISAR3", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar3 }, + .resetvalue = GET_IDREG(isar, ID_ISAR3) }, { .name = "ID_ISAR4", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar4 }, + .resetvalue = GET_IDREG(isar, ID_ISAR4) }, { .name = "ID_ISAR5", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar5 }, + .resetvalue = GET_IDREG(isar, ID_ISAR5) }, { .name = "ID_MMFR4", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, @@ -7865,7 +7865,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_isar6 }, + .resetvalue = GET_IDREG(isar, ID_ISAR6) }, }; define_arm_cp_regs(cpu, v6_idregs); define_arm_cp_regs(cpu, v6_cp_reginfo); diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 8736855861..eef9481737 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -346,22 +346,15 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ARM64_SYS_REG(3, 0, 0, 1, 6)); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr3, ARM64_SYS_REG(3, 0, 0, 1, 7)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar0, - ARM64_SYS_REG(3, 0, 0, 2, 0)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar1, - ARM64_SYS_REG(3, 0, 0, 2, 1)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar2, - ARM64_SYS_REG(3, 0, 0, 2, 2)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar3, - ARM64_SYS_REG(3, 0, 0, 2, 3)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar4, - ARM64_SYS_REG(3, 0, 0, 2, 4)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar5, - ARM64_SYS_REG(3, 0, 0, 2, 5)); + err |= get_host_cpu_reg(fd, ahcf, ID_ISAR0_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_ISAR1_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_ISAR2_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_ISAR3_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_ISAR4_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_ISAR5_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_ISAR6_EL1_IDX); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr4, ARM64_SYS_REG(3, 0, 0, 2, 6)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_isar6, - ARM64_SYS_REG(3, 0, 0, 2, 7)); err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0, ARM64_SYS_REG(3, 0, 0, 3, 0)); diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 8e1a083b91..198c9f3e98 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -45,6 +45,7 @@ static bool arm_v7m_cpu_exec_interrupt(CPUState *cs, int interrupt_request) static void cortex_m0_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; set_feature(&cpu->env, ARM_FEATURE_V6); set_feature(&cpu->env, ARM_FEATURE_M); @@ -66,18 +67,19 @@ static void cortex_m0_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x00000000; cpu->isar.id_mmfr2 = 0x00000000; cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01141110; - cpu->isar.id_isar1 = 0x02111000; - cpu->isar.id_isar2 = 0x21112231; - cpu->isar.id_isar3 = 0x01111110; - cpu->isar.id_isar4 = 0x01310102; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; + SET_IDREG(isar, ID_ISAR0, 0x01141110); + SET_IDREG(isar, ID_ISAR1, 0x02111000); + SET_IDREG(isar, ID_ISAR2, 0x21112231); + SET_IDREG(isar, ID_ISAR3, 0x01111110); + SET_IDREG(isar, ID_ISAR4, 0x01310102); + SET_IDREG(isar, ID_ISAR5, 0x00000000); + SET_IDREG(isar, ID_ISAR6, 0x00000000); } static void cortex_m3_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_M); set_feature(&cpu->env, ARM_FEATURE_M_MAIN); @@ -91,18 +93,19 @@ static void cortex_m3_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x00000000; cpu->isar.id_mmfr2 = 0x00000000; cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01141110; - cpu->isar.id_isar1 = 0x02111000; - cpu->isar.id_isar2 = 0x21112231; - cpu->isar.id_isar3 = 0x01111110; - cpu->isar.id_isar4 = 0x01310102; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; + SET_IDREG(isar, ID_ISAR0, 0x01141110); + SET_IDREG(isar, ID_ISAR1, 0x02111000); + SET_IDREG(isar, ID_ISAR2, 0x21112231); + SET_IDREG(isar, ID_ISAR3, 0x01111110); + SET_IDREG(isar, ID_ISAR4, 0x01310102); + SET_IDREG(isar, ID_ISAR5, 0x00000000); + SET_IDREG(isar, ID_ISAR6, 0x00000000); } static void cortex_m4_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_M); @@ -121,18 +124,19 @@ static void cortex_m4_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x00000000; cpu->isar.id_mmfr2 = 0x00000000; cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01141110; - cpu->isar.id_isar1 = 0x02111000; - cpu->isar.id_isar2 = 0x21112231; - cpu->isar.id_isar3 = 0x01111110; - cpu->isar.id_isar4 = 0x01310102; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; + SET_IDREG(isar, ID_ISAR0, 0x01141110); + SET_IDREG(isar, ID_ISAR1, 0x02111000); + SET_IDREG(isar, ID_ISAR2, 0x21112231); + SET_IDREG(isar, ID_ISAR3, 0x01111110); + SET_IDREG(isar, ID_ISAR4, 0x01310102); + SET_IDREG(isar, ID_ISAR5, 0x00000000); + SET_IDREG(isar, ID_ISAR6, 0x00000000); } static void cortex_m7_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_M); @@ -151,18 +155,19 @@ static void cortex_m7_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x00000000; cpu->isar.id_mmfr2 = 0x01000000; cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01101110; - cpu->isar.id_isar1 = 0x02112000; - cpu->isar.id_isar2 = 0x20232231; - cpu->isar.id_isar3 = 0x01111131; - cpu->isar.id_isar4 = 0x01310132; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; + SET_IDREG(isar, ID_ISAR0, 0x01101110); + SET_IDREG(isar, ID_ISAR1, 0x02112000); + SET_IDREG(isar, ID_ISAR2, 0x20232231); + SET_IDREG(isar, ID_ISAR3, 0x01111131); + SET_IDREG(isar, ID_ISAR4, 0x01310132); + SET_IDREG(isar, ID_ISAR5, 0x00000000); + SET_IDREG(isar, ID_ISAR6, 0x00000000); } static void cortex_m33_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_M); @@ -183,13 +188,13 @@ static void cortex_m33_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x00000000; cpu->isar.id_mmfr2 = 0x01000000; cpu->isar.id_mmfr3 = 0x00000000; - cpu->isar.id_isar0 = 0x01101110; - cpu->isar.id_isar1 = 0x02212000; - cpu->isar.id_isar2 = 0x20232232; - cpu->isar.id_isar3 = 0x01111131; - cpu->isar.id_isar4 = 0x01310132; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; + SET_IDREG(isar, ID_ISAR0, 0x01101110); + SET_IDREG(isar, ID_ISAR1, 0x02212000); + SET_IDREG(isar, ID_ISAR2, 0x20232232); + SET_IDREG(isar, ID_ISAR3, 0x01111131); + SET_IDREG(isar, ID_ISAR4, 0x01310132); + SET_IDREG(isar, ID_ISAR5, 0x00000000); + SET_IDREG(isar, ID_ISAR6, 0x00000000); cpu->clidr = 0x00000000; cpu->ctr = 0x8000c000; } @@ -197,6 +202,7 @@ static void cortex_m33_initfn(Object *obj) static void cortex_m55_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_V8_1M); @@ -220,13 +226,13 @@ static void cortex_m55_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x00000000; cpu->isar.id_mmfr2 = 0x01000000; cpu->isar.id_mmfr3 = 0x00000011; - cpu->isar.id_isar0 = 0x01103110; - cpu->isar.id_isar1 = 0x02212000; - cpu->isar.id_isar2 = 0x20232232; - cpu->isar.id_isar3 = 0x01111131; - cpu->isar.id_isar4 = 0x01310132; - cpu->isar.id_isar5 = 0x00000000; - cpu->isar.id_isar6 = 0x00000000; + SET_IDREG(isar, ID_ISAR0, 0x01103110); + SET_IDREG(isar, ID_ISAR1, 0x02212000); + SET_IDREG(isar, ID_ISAR2, 0x20232232); + SET_IDREG(isar, ID_ISAR3, 0x01111131); + SET_IDREG(isar, ID_ISAR4, 0x01310132); + SET_IDREG(isar, ID_ISAR5, 0x00000000); + SET_IDREG(isar, ID_ISAR6, 0x00000000); cpu->clidr = 0x00000000; /* caches not implemented */ cpu->ctr = 0x8303c003; } diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index 2c45b7eddd..937a72b12c 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -23,18 +23,19 @@ void aa32_max_features(ARMCPU *cpu) { uint32_t t; + ARMISARegisters *isar = &cpu->isar; /* Add additional features supported by QEMU */ - t = cpu->isar.id_isar5; + t = GET_IDREG(isar, ID_ISAR5); t = FIELD_DP32(t, ID_ISAR5, AES, 2); /* FEAT_PMULL */ t = FIELD_DP32(t, ID_ISAR5, SHA1, 1); /* FEAT_SHA1 */ t = FIELD_DP32(t, ID_ISAR5, SHA2, 1); /* FEAT_SHA256 */ t = FIELD_DP32(t, ID_ISAR5, CRC32, 1); t = FIELD_DP32(t, ID_ISAR5, RDM, 1); /* FEAT_RDM */ t = FIELD_DP32(t, ID_ISAR5, VCMA, 1); /* FEAT_FCMA */ - cpu->isar.id_isar5 = t; + SET_IDREG(isar, ID_ISAR5, t); - t = cpu->isar.id_isar6; + t = GET_IDREG(isar, ID_ISAR6); t = FIELD_DP32(t, ID_ISAR6, JSCVT, 1); /* FEAT_JSCVT */ t = FIELD_DP32(t, ID_ISAR6, DP, 1); /* Feat_DotProd */ t = FIELD_DP32(t, ID_ISAR6, FHM, 1); /* FEAT_FHM */ @@ -42,7 +43,7 @@ void aa32_max_features(ARMCPU *cpu) t = FIELD_DP32(t, ID_ISAR6, SPECRES, 1); /* FEAT_SPECRES */ t = FIELD_DP32(t, ID_ISAR6, BF16, 1); /* FEAT_AA32BF16 */ t = FIELD_DP32(t, ID_ISAR6, I8MM, 1); /* FEAT_AA32I8MM */ - cpu->isar.id_isar6 = t; + SET_IDREG(isar, ID_ISAR6, t); t = cpu->isar.mvfr1; t = FIELD_DP32(t, MVFR1, FPHP, 3); /* FEAT_FP16 */ @@ -140,7 +141,7 @@ static void arm926_initfn(Object *obj) * ARMv5 does not have the ID_ISAR registers, but we can still * set the field to indicate Jazelle support within QEMU. */ - cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1); + FIELD_DP32_IDREG(&cpu->isar, ID_ISAR1, JAZELLE, 1); /* * Similarly, we need to set MVFR0 fields to enable vfp and short vector * support even though ARMv5 doesn't have this register. @@ -182,7 +183,7 @@ static void arm1026_initfn(Object *obj) * ARMv5 does not have the ID_ISAR registers, but we can still * set the field to indicate Jazelle support within QEMU. */ - cpu->isar.id_isar1 = FIELD_DP32(cpu->isar.id_isar1, ID_ISAR1, JAZELLE, 1); + FIELD_DP32_IDREG(&cpu->isar, ID_ISAR1, JAZELLE, 1); /* * Similarly, we need to set MVFR0 fields to enable vfp and short vector * support even though ARMv5 doesn't have this register. @@ -206,6 +207,7 @@ static void arm1026_initfn(Object *obj) static void arm1136_r2_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; /* * What qemu calls "arm1136_r2" is actually the 1136 r0p2, ie an * older core than plain "arm1136". In particular this does not @@ -233,17 +235,18 @@ static void arm1136_r2_initfn(Object *obj) cpu->isar.id_mmfr0 = 0x01130003; cpu->isar.id_mmfr1 = 0x10030302; cpu->isar.id_mmfr2 = 0x01222110; - cpu->isar.id_isar0 = 0x00140011; - cpu->isar.id_isar1 = 0x12002111; - cpu->isar.id_isar2 = 0x11231111; - cpu->isar.id_isar3 = 0x01102131; - cpu->isar.id_isar4 = 0x141; + SET_IDREG(isar, ID_ISAR0, 0x00140011); + SET_IDREG(isar, ID_ISAR1, 0x12002111); + SET_IDREG(isar, ID_ISAR2, 0x11231111); + SET_IDREG(isar, ID_ISAR3, 0x01102131); + SET_IDREG(isar, ID_ISAR4, 0x141); cpu->reset_auxcr = 7; } static void arm1136_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,arm1136"; set_feature(&cpu->env, ARM_FEATURE_V6K); @@ -264,17 +267,18 @@ static void arm1136_initfn(Object *obj) cpu->isar.id_mmfr0 = 0x01130003; cpu->isar.id_mmfr1 = 0x10030302; cpu->isar.id_mmfr2 = 0x01222110; - cpu->isar.id_isar0 = 0x00140011; - cpu->isar.id_isar1 = 0x12002111; - cpu->isar.id_isar2 = 0x11231111; - cpu->isar.id_isar3 = 0x01102131; - cpu->isar.id_isar4 = 0x141; + SET_IDREG(isar, ID_ISAR0, 0x00140011); + SET_IDREG(isar, ID_ISAR1, 0x12002111); + SET_IDREG(isar, ID_ISAR2, 0x11231111); + SET_IDREG(isar, ID_ISAR3, 0x01102131); + SET_IDREG(isar, ID_ISAR4, 0x141); cpu->reset_auxcr = 7; } static void arm1176_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,arm1176"; set_feature(&cpu->env, ARM_FEATURE_V6K); @@ -296,17 +300,18 @@ static void arm1176_initfn(Object *obj) cpu->isar.id_mmfr0 = 0x01130003; cpu->isar.id_mmfr1 = 0x10030302; cpu->isar.id_mmfr2 = 0x01222100; - cpu->isar.id_isar0 = 0x0140011; - cpu->isar.id_isar1 = 0x12002111; - cpu->isar.id_isar2 = 0x11231121; - cpu->isar.id_isar3 = 0x01102131; - cpu->isar.id_isar4 = 0x01141; + SET_IDREG(isar, ID_ISAR0, 0x0140011); + SET_IDREG(isar, ID_ISAR1, 0x12002111); + SET_IDREG(isar, ID_ISAR2, 0x11231121); + SET_IDREG(isar, ID_ISAR3, 0x01102131); + SET_IDREG(isar, ID_ISAR4, 0x01141); cpu->reset_auxcr = 7; } static void arm11mpcore_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,arm11mpcore"; set_feature(&cpu->env, ARM_FEATURE_V6K); @@ -325,11 +330,11 @@ static void arm11mpcore_initfn(Object *obj) cpu->isar.id_mmfr0 = 0x01100103; cpu->isar.id_mmfr1 = 0x10020302; cpu->isar.id_mmfr2 = 0x01222000; - cpu->isar.id_isar0 = 0x00100011; - cpu->isar.id_isar1 = 0x12002111; - cpu->isar.id_isar2 = 0x11221011; - cpu->isar.id_isar3 = 0x01102131; - cpu->isar.id_isar4 = 0x141; + SET_IDREG(isar, ID_ISAR0, 0x00100011); + SET_IDREG(isar, ID_ISAR1, 0x12002111); + SET_IDREG(isar, ID_ISAR2, 0x11221011); + SET_IDREG(isar, ID_ISAR3, 0x01102131); + SET_IDREG(isar, ID_ISAR4, 0x141); cpu->reset_auxcr = 1; } @@ -343,6 +348,7 @@ static const ARMCPRegInfo cortexa8_cp_reginfo[] = { static void cortex_a8_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,cortex-a8"; set_feature(&cpu->env, ARM_FEATURE_V7); @@ -365,11 +371,11 @@ static void cortex_a8_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x20000000; cpu->isar.id_mmfr2 = 0x01202000; cpu->isar.id_mmfr3 = 0x11; - cpu->isar.id_isar0 = 0x00101111; - cpu->isar.id_isar1 = 0x12112111; - cpu->isar.id_isar2 = 0x21232031; - cpu->isar.id_isar3 = 0x11112131; - cpu->isar.id_isar4 = 0x00111142; + SET_IDREG(isar, ID_ISAR0, 0x00101111); + SET_IDREG(isar, ID_ISAR1, 0x12112111); + SET_IDREG(isar, ID_ISAR2, 0x21232031); + SET_IDREG(isar, ID_ISAR3, 0x11112131); + SET_IDREG(isar, ID_ISAR4, 0x00111142); cpu->isar.dbgdidr = 0x15141000; cpu->clidr = (1 << 27) | (2 << 24) | 3; cpu->ccsidr[0] = 0xe007e01a; /* 16k L1 dcache. */ @@ -412,6 +418,7 @@ static const ARMCPRegInfo cortexa9_cp_reginfo[] = { static void cortex_a9_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,cortex-a9"; set_feature(&cpu->env, ARM_FEATURE_V7); @@ -440,11 +447,11 @@ static void cortex_a9_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x20000000; cpu->isar.id_mmfr2 = 0x01230000; cpu->isar.id_mmfr3 = 0x00002111; - cpu->isar.id_isar0 = 0x00101111; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232041; - cpu->isar.id_isar3 = 0x11112131; - cpu->isar.id_isar4 = 0x00111142; + SET_IDREG(isar, ID_ISAR0, 0x00101111); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232041); + SET_IDREG(isar, ID_ISAR3, 0x11112131); + SET_IDREG(isar, ID_ISAR4, 0x00111142); cpu->isar.dbgdidr = 0x35141000; cpu->clidr = (1 << 27) | (1 << 24) | 3; cpu->ccsidr[0] = 0xe00fe019; /* 16k L1 dcache. */ @@ -479,6 +486,7 @@ static const ARMCPRegInfo cortexa15_cp_reginfo[] = { static void cortex_a7_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,cortex-a7"; set_feature(&cpu->env, ARM_FEATURE_V7VE); @@ -509,11 +517,11 @@ static void cortex_a7_initfn(Object *obj) * a7_mpcore_r0p5_trm, page 4-4 gives 0x01101110; but * table 4-41 gives 0x02101110, which includes the arm div insns. */ - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232041; - cpu->isar.id_isar3 = 0x11112131; - cpu->isar.id_isar4 = 0x10011142; + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232041); + SET_IDREG(isar, ID_ISAR3, 0x11112131); + SET_IDREG(isar, ID_ISAR4, 0x10011142); cpu->isar.dbgdidr = 0x3515f005; cpu->isar.dbgdevid = 0x01110f13; cpu->isar.dbgdevid1 = 0x1; @@ -528,6 +536,7 @@ static void cortex_a7_initfn(Object *obj) static void cortex_a15_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; cpu->dtb_compatible = "arm,cortex-a15"; set_feature(&cpu->env, ARM_FEATURE_V7VE); @@ -556,11 +565,11 @@ static void cortex_a15_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x20000000; cpu->isar.id_mmfr2 = 0x01240000; cpu->isar.id_mmfr3 = 0x02102211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232041; - cpu->isar.id_isar3 = 0x11112131; - cpu->isar.id_isar4 = 0x10011142; + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232041); + SET_IDREG(isar, ID_ISAR3, 0x11112131); + SET_IDREG(isar, ID_ISAR4, 0x10011142); cpu->isar.dbgdidr = 0x3515f021; cpu->isar.dbgdevid = 0x01110f13; cpu->isar.dbgdevid1 = 0x0; @@ -585,6 +594,7 @@ static const ARMCPRegInfo cortexr5_cp_reginfo[] = { static void cortex_r5_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; set_feature(&cpu->env, ARM_FEATURE_V7); set_feature(&cpu->env, ARM_FEATURE_V7MP); @@ -599,13 +609,13 @@ static void cortex_r5_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x00000000; cpu->isar.id_mmfr2 = 0x01200000; cpu->isar.id_mmfr3 = 0x0211; - cpu->isar.id_isar0 = 0x02101111; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232141; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x0010142; - cpu->isar.id_isar5 = 0x0; - cpu->isar.id_isar6 = 0x0; + SET_IDREG(isar, ID_ISAR0, 0x02101111); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232141); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x0010142); + SET_IDREG(isar, ID_ISAR5, 0x0); + SET_IDREG(isar, ID_ISAR6, 0x0); cpu->mp_is_up = true; cpu->pmsav7_dregion = 16; cpu->isar.reset_pmcr_el0 = 0x41151800; @@ -720,6 +730,7 @@ static const ARMCPRegInfo cortex_r52_cp_reginfo[] = { static void cortex_r52_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; set_feature(&cpu->env, ARM_FEATURE_V8); set_feature(&cpu->env, ARM_FEATURE_EL2); @@ -746,12 +757,12 @@ static void cortex_r52_initfn(Object *obj) cpu->isar.id_mmfr2 = 0x01200000; cpu->isar.id_mmfr3 = 0xf0102211; cpu->isar.id_mmfr4 = 0x00000010; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232142; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00010142; - cpu->isar.id_isar5 = 0x00010001; + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232142); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00010142); + SET_IDREG(isar, ID_ISAR5, 0x00010001); cpu->isar.dbgdidr = 0x77168000; cpu->clidr = (1 << 27) | (1 << 24) | 0x3; cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */ @@ -949,6 +960,7 @@ static void pxa270c5_initfn(Object *obj) static void arm_max_initfn(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); + ARMISARegisters *isar = &cpu->isar; /* aarch64_a57_initfn, advertising none of the aarch64 features */ cpu->dtb_compatible = "arm,cortex-a57"; @@ -976,13 +988,13 @@ static void arm_max_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x40000000; cpu->isar.id_mmfr2 = 0x01260000; cpu->isar.id_mmfr3 = 0x02102211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00011142; - cpu->isar.id_isar5 = 0x00011121; - cpu->isar.id_isar6 = 0; + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232042); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00011142); + SET_IDREG(isar, ID_ISAR5, 0x00011121); + SET_IDREG(isar, ID_ISAR6, 0); cpu->isar.reset_pmcr_el0 = 0x41013000; cpu->clidr = 0x0a200023; cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 80a99ab025..dd4dc8ada5 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -57,12 +57,12 @@ static void aarch64_a35_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x40000000; cpu->isar.id_mmfr2 = 0x01260000; cpu->isar.id_mmfr3 = 0x02102211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00011142; - cpu->isar.id_isar5 = 0x00011121; + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232042); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00011142); + SET_IDREG(isar, ID_ISAR5, 0x00011121); SET_IDREG(isar, ID_AA64PFR0, 0x00002222); SET_IDREG(isar, ID_AA64PFR1, 0); SET_IDREG(isar, ID_AA64DFR0, 0x10305106); @@ -229,13 +229,13 @@ static void aarch64_a55_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000010ull); cpu->id_afr0 = 0x00000000; cpu->isar.id_dfr0 = 0x04010088; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00011142; - cpu->isar.id_isar5 = 0x01011121; - cpu->isar.id_isar6 = 0x00000010; + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232042); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00011142); + SET_IDREG(isar, ID_ISAR5, 0x01011121); + SET_IDREG(isar, ID_ISAR6, 0x00000010); cpu->isar.id_mmfr0 = 0x10201105; cpu->isar.id_mmfr1 = 0x40000000; cpu->isar.id_mmfr2 = 0x01260000; @@ -303,12 +303,12 @@ static void aarch64_a72_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x40000000; cpu->isar.id_mmfr2 = 0x01260000; cpu->isar.id_mmfr3 = 0x02102211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00011142; - cpu->isar.id_isar5 = 0x00011121; + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232042); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00011142); + SET_IDREG(isar, ID_ISAR5, 0x00011121); SET_IDREG(isar, ID_AA64PFR0, 0x00002222); SET_IDREG(isar, ID_AA64DFR0, 0x10305106); SET_IDREG(isar, ID_AA64ISAR0, 0x00011120); @@ -362,13 +362,13 @@ static void aarch64_a76_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000010ull); cpu->id_afr0 = 0x00000000; cpu->isar.id_dfr0 = 0x04010088; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00010142; - cpu->isar.id_isar5 = 0x01011121; - cpu->isar.id_isar6 = 0x00000010; + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232042); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00010142); + SET_IDREG(isar, ID_ISAR5, 0x01011121); + SET_IDREG(isar, ID_ISAR6, 0x00000010); cpu->isar.id_mmfr0 = 0x10201105; cpu->isar.id_mmfr1 = 0x40000000; cpu->isar.id_mmfr2 = 0x01260000; @@ -610,13 +610,13 @@ static void aarch64_neoverse_n1_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000020ull); cpu->id_afr0 = 0x00000000; cpu->isar.id_dfr0 = 0x04010088; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00010142; - cpu->isar.id_isar5 = 0x01011121; - cpu->isar.id_isar6 = 0x00000010; + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232042); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00010142); + SET_IDREG(isar, ID_ISAR5, 0x01011121); + SET_IDREG(isar, ID_ISAR6, 0x00000010); cpu->isar.id_mmfr0 = 0x10201105; cpu->isar.id_mmfr1 = 0x40000000; cpu->isar.id_mmfr2 = 0x01260000; @@ -689,13 +689,13 @@ static void aarch64_neoverse_v1_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000020ull); cpu->id_afr0 = 0x00000000; cpu->isar.id_dfr0 = 0x15011099; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00010142; - cpu->isar.id_isar5 = 0x11011121; - cpu->isar.id_isar6 = 0x01100111; + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232042); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00010142); + SET_IDREG(isar, ID_ISAR5, 0x11011121); + SET_IDREG(isar, ID_ISAR6, 0x01100111); cpu->isar.id_mmfr0 = 0x10201105; cpu->isar.id_mmfr1 = 0x40000000; cpu->isar.id_mmfr2 = 0x01260000; @@ -910,14 +910,14 @@ static void aarch64_a710_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x40000000; cpu->isar.id_mmfr2 = 0x01260000; cpu->isar.id_mmfr3 = 0x02122211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00010142; - cpu->isar.id_isar5 = 0x11011121; /* with Crypto */ + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232042); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00010142); + SET_IDREG(isar, ID_ISAR5, 0x11011121); /* with Crypto */ cpu->isar.id_mmfr4 = 0x21021110; - cpu->isar.id_isar6 = 0x01111111; + SET_IDREG(isar, ID_ISAR6, 0x01111111); cpu->isar.mvfr0 = 0x10110222; cpu->isar.mvfr1 = 0x13211111; cpu->isar.mvfr2 = 0x00000043; @@ -1012,14 +1012,14 @@ static void aarch64_neoverse_n2_initfn(Object *obj) cpu->isar.id_mmfr1 = 0x40000000; cpu->isar.id_mmfr2 = 0x01260000; cpu->isar.id_mmfr3 = 0x02122211; - cpu->isar.id_isar0 = 0x02101110; - cpu->isar.id_isar1 = 0x13112111; - cpu->isar.id_isar2 = 0x21232042; - cpu->isar.id_isar3 = 0x01112131; - cpu->isar.id_isar4 = 0x00010142; - cpu->isar.id_isar5 = 0x11011121; /* with Crypto */ + SET_IDREG(isar, ID_ISAR0, 0x02101110); + SET_IDREG(isar, ID_ISAR1, 0x13112111); + SET_IDREG(isar, ID_ISAR2, 0x21232042); + SET_IDREG(isar, ID_ISAR3, 0x01112131); + SET_IDREG(isar, ID_ISAR4, 0x00010142); + SET_IDREG(isar, ID_ISAR5, 0x11011121); /* with Crypto */ cpu->isar.id_mmfr4 = 0x01021110; - cpu->isar.id_isar6 = 0x01111111; + SET_IDREG(isar, ID_ISAR6, 0x01111111); cpu->isar.mvfr0 = 0x10110222; cpu->isar.mvfr1 = 0x13211111; cpu->isar.mvfr2 = 0x00000043; From 30ca689900c8c47bc4ca46be00ec9a7f1a78ccda Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 1 Jul 2025 15:08:28 +0100 Subject: [PATCH 1735/2760] arm/cpu: Store id_pfr0/1/2 into the idregs array Reviewed-by: Richard Henderson Reviewed-by: Sebastian Ott Signed-off-by: Eric Auger Signed-off-by: Cornelia Huck Message-id: 20250617153931.1330449-10-cohuck@redhat.com Signed-off-by: Peter Maydell --- hw/intc/armv7m_nvic.c | 5 ++-- target/arm/cpu-features.h | 10 ++++---- target/arm/cpu.c | 8 +++--- target/arm/cpu.h | 3 --- target/arm/cpu64.c | 8 +++--- target/arm/helper.c | 8 +++--- target/arm/kvm.c | 9 +++---- target/arm/tcg/cpu-v7m.c | 24 +++++++++--------- target/arm/tcg/cpu32.c | 52 +++++++++++++++++++-------------------- target/arm/tcg/cpu64.c | 44 ++++++++++++++++----------------- 10 files changed, 82 insertions(+), 89 deletions(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index fdb7f58e36..330205fa34 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -988,6 +988,7 @@ static void nvic_nmi_trigger(void *opaque, int n, int level) static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) { ARMCPU *cpu = s->cpu; + ARMISARegisters *isar = &cpu->isar; uint32_t val; switch (offset) { @@ -1263,12 +1264,12 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_pfr0; + return GET_IDREG(isar, ID_PFR0); case 0xd44: /* PFR1. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_pfr1; + return GET_IDREG(isar, ID_PFR1); case 0xd48: /* DFR0. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 75a2cc4077..a34378577f 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -137,12 +137,12 @@ static inline bool isar_feature_aa32_i8mm(const ARMISARegisters *id) static inline bool isar_feature_aa32_ras(const ARMISARegisters *id) { - return FIELD_EX32(id->id_pfr0, ID_PFR0, RAS) != 0; + return FIELD_EX32_IDREG(id, ID_PFR0, RAS) != 0; } static inline bool isar_feature_aa32_mprofile(const ARMISARegisters *id) { - return FIELD_EX32(id->id_pfr1, ID_PFR1, MPROGMOD) != 0; + return FIELD_EX32_IDREG(id, ID_PFR1, MPROGMOD) != 0; } static inline bool isar_feature_aa32_m_sec_state(const ARMISARegisters *id) @@ -151,7 +151,7 @@ static inline bool isar_feature_aa32_m_sec_state(const ARMISARegisters *id) * Return true if M-profile state handling insns * (VSCCLRM, CLRM, FPCTX access insns) are implemented */ - return FIELD_EX32(id->id_pfr1, ID_PFR1, SECURITY) >= 3; + return FIELD_EX32_IDREG(id, ID_PFR1, SECURITY) >= 3; } static inline bool isar_feature_aa32_fp16_arith(const ARMISARegisters *id) @@ -350,12 +350,12 @@ static inline bool isar_feature_aa32_evt(const ARMISARegisters *id) static inline bool isar_feature_aa32_dit(const ARMISARegisters *id) { - return FIELD_EX32(id->id_pfr0, ID_PFR0, DIT) != 0; + return FIELD_EX32_IDREG(id, ID_PFR0, DIT) != 0; } static inline bool isar_feature_aa32_ssbs(const ARMISARegisters *id) { - return FIELD_EX32(id->id_pfr2, ID_PFR2, SSBS) != 0; + return FIELD_EX32_IDREG(id, ID_PFR2, SSBS) != 0; } static inline bool isar_feature_aa32_debugv7p1(const ARMISARegisters *id) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index cf811e47d9..62c06c7269 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2317,7 +2317,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * Disable the security extension feature bits in the processor * feature registers as well. */ - cpu->isar.id_pfr1 = FIELD_DP32(cpu->isar.id_pfr1, ID_PFR1, SECURITY, 0); + FIELD_DP32_IDREG(isar, ID_PFR1, SECURITY, 0); cpu->isar.id_dfr0 = FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, COPSDBG, 0); FIELD_DP64_IDREG(isar, ID_AA64PFR0, EL3, 0); @@ -2357,8 +2357,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * registers if we don't have EL2. */ FIELD_DP64_IDREG(isar, ID_AA64PFR0, EL2, 0); - cpu->isar.id_pfr1 = FIELD_DP32(cpu->isar.id_pfr1, - ID_PFR1, VIRTUALIZATION, 0); + FIELD_DP32_IDREG(isar, ID_PFR1, VIRTUALIZATION, 0); } if (cpu_isar_feature(aa64_mte, cpu)) { @@ -2421,8 +2420,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, MMAPTRC, 0); /* FEAT_AMU (Activity Monitors Extension) */ FIELD_DP64_IDREG(isar, ID_AA64PFR0, AMU, 0); - cpu->isar.id_pfr0 = - FIELD_DP32(cpu->isar.id_pfr0, ID_PFR0, AMU, 0); + FIELD_DP32_IDREG(isar, ID_PFR0, AMU, 0); /* FEAT_MPAM (Memory Partitioning and Monitoring Extension) */ FIELD_DP64_IDREG(isar, ID_AA64PFR0, MPAM, 0); } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 353c18e679..30401926e1 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1056,9 +1056,6 @@ struct ArchCPU { uint32_t id_mmfr3; uint32_t id_mmfr4; uint32_t id_mmfr5; - uint32_t id_pfr0; - uint32_t id_pfr1; - uint32_t id_pfr2; uint32_t mvfr0; uint32_t mvfr1; uint32_t mvfr2; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 6be62c0711..5b628aa7eb 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -652,8 +652,8 @@ static void aarch64_a57_initfn(Object *obj) cpu->isar.mvfr2 = 0x00000043; cpu->ctr = 0x8444c004; cpu->reset_sctlr = 0x00c50838; - cpu->isar.id_pfr0 = 0x00000131; - cpu->isar.id_pfr1 = 0x00011011; + SET_IDREG(isar, ID_PFR0, 0x00000131); + SET_IDREG(isar, ID_PFR1, 0x00011011); cpu->isar.id_dfr0 = 0x03010066; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x10101105; @@ -714,8 +714,8 @@ static void aarch64_a53_initfn(Object *obj) cpu->isar.mvfr2 = 0x00000043; cpu->ctr = 0x84448004; /* L1Ip = VIPT */ cpu->reset_sctlr = 0x00c50838; - cpu->isar.id_pfr0 = 0x00000131; - cpu->isar.id_pfr1 = 0x00011011; + SET_IDREG(isar, ID_PFR0, 0x00000131); + SET_IDREG(isar, ID_PFR1, 0x00011011); cpu->isar.id_dfr0 = 0x03010066; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x10101105; diff --git a/target/arm/helper.c b/target/arm/helper.c index 7e0b3f164e..0329923822 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6932,7 +6932,7 @@ static void define_pmu_regs(ARMCPU *cpu) static uint64_t id_pfr1_read(CPUARMState *env, const ARMCPRegInfo *ri) { ARMCPU *cpu = env_archcpu(env); - uint64_t pfr1 = cpu->isar.id_pfr1; + uint64_t pfr1 = GET_IDREG(&cpu->isar, ID_PFR1); if (env->gicv3state) { pfr1 |= 1 << 28; @@ -7777,7 +7777,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_pfr0 }, + .resetvalue = GET_IDREG(isar, ID_PFR0)}, /* * ID_PFR1 is not a plain ARM_CP_CONST because we don't know * the value of the GIC field until after we define these regs. @@ -7788,7 +7788,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .accessfn = access_aa32_tid3, #ifdef CONFIG_USER_ONLY .type = ARM_CP_CONST, - .resetvalue = cpu->isar.id_pfr1, + .resetvalue = GET_IDREG(isar, ID_PFR1), #else .type = ARM_CP_NO_RAW, .accessfn = access_aa32_tid3, @@ -8130,7 +8130,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_pfr2 }, + .resetvalue = GET_IDREG(isar, ID_PFR2)}, { .name = "ID_DFR1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, diff --git a/target/arm/kvm.c b/target/arm/kvm.c index eef9481737..d945e652b3 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -332,10 +332,8 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) * than skipping the reads and leaving 0, as we must avoid * considering the values in every case. */ - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr0, - ARM64_SYS_REG(3, 0, 0, 1, 0)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr1, - ARM64_SYS_REG(3, 0, 0, 1, 1)); + err |= get_host_cpu_reg(fd, ahcf, ID_PFR0_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_PFR1_EL1_IDX); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_dfr0, ARM64_SYS_REG(3, 0, 0, 1, 2)); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr0, @@ -362,8 +360,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ARM64_SYS_REG(3, 0, 0, 3, 1)); err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr2, ARM64_SYS_REG(3, 0, 0, 3, 2)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_pfr2, - ARM64_SYS_REG(3, 0, 0, 3, 4)); + err |= get_host_cpu_reg(fd, ahcf, ID_PFR2_EL1_IDX); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_dfr1, ARM64_SYS_REG(3, 0, 0, 3, 5)); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr5, diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 198c9f3e98..4a2c3bd01a 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -59,8 +59,8 @@ static void cortex_m0_initfn(Object *obj) * by looking at ID register fields. We use the same values as * for the M3. */ - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; + SET_IDREG(isar, ID_PFR0, 0x00000030); + SET_IDREG(isar, ID_PFR1, 0x00000200); cpu->isar.id_dfr0 = 0x00100000; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00000030; @@ -85,8 +85,8 @@ static void cortex_m3_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_M_MAIN); cpu->midr = 0x410fc231; cpu->pmsav7_dregion = 8; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; + SET_IDREG(isar, ID_PFR0, 0x00000030); + SET_IDREG(isar, ID_PFR1, 0x00000200); cpu->isar.id_dfr0 = 0x00100000; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00000030; @@ -116,8 +116,8 @@ static void cortex_m4_initfn(Object *obj) cpu->isar.mvfr0 = 0x10110021; cpu->isar.mvfr1 = 0x11000011; cpu->isar.mvfr2 = 0x00000000; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; + SET_IDREG(isar, ID_PFR0, 0x00000030); + SET_IDREG(isar, ID_PFR1, 0x00000200); cpu->isar.id_dfr0 = 0x00100000; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00000030; @@ -147,8 +147,8 @@ static void cortex_m7_initfn(Object *obj) cpu->isar.mvfr0 = 0x10110221; cpu->isar.mvfr1 = 0x12000011; cpu->isar.mvfr2 = 0x00000040; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000200; + SET_IDREG(isar, ID_PFR0, 0x00000030); + SET_IDREG(isar, ID_PFR1, 0x00000200); cpu->isar.id_dfr0 = 0x00100000; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00100030; @@ -180,8 +180,8 @@ static void cortex_m33_initfn(Object *obj) cpu->isar.mvfr0 = 0x10110021; cpu->isar.mvfr1 = 0x11000011; cpu->isar.mvfr2 = 0x00000040; - cpu->isar.id_pfr0 = 0x00000030; - cpu->isar.id_pfr1 = 0x00000210; + SET_IDREG(isar, ID_PFR0, 0x00000030); + SET_IDREG(isar, ID_PFR1, 0x00000210); cpu->isar.id_dfr0 = 0x00200000; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00101F40; @@ -218,8 +218,8 @@ static void cortex_m55_initfn(Object *obj) cpu->isar.mvfr0 = 0x10110221; cpu->isar.mvfr1 = 0x12100211; cpu->isar.mvfr2 = 0x00000040; - cpu->isar.id_pfr0 = 0x20000030; - cpu->isar.id_pfr1 = 0x00000230; + SET_IDREG(isar, ID_PFR0, 0x20000030); + SET_IDREG(isar, ID_PFR1, 0x00000230); cpu->isar.id_dfr0 = 0x10200000; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00111040; diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index 937a72b12c..56374db269 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -71,16 +71,16 @@ void aa32_max_features(ARMCPU *cpu) t = FIELD_DP32(t, ID_MMFR5, ETS, 2); /* FEAT_ETS2 */ cpu->isar.id_mmfr5 = t; - t = cpu->isar.id_pfr0; + t = GET_IDREG(isar, ID_PFR0); t = FIELD_DP32(t, ID_PFR0, CSV2, 2); /* FEAT_CSV2 */ t = FIELD_DP32(t, ID_PFR0, DIT, 1); /* FEAT_DIT */ t = FIELD_DP32(t, ID_PFR0, RAS, 1); /* FEAT_RAS */ - cpu->isar.id_pfr0 = t; + SET_IDREG(isar, ID_PFR0, t); - t = cpu->isar.id_pfr2; + t = GET_IDREG(isar, ID_PFR2); t = FIELD_DP32(t, ID_PFR2, CSV3, 1); /* FEAT_CSV3 */ t = FIELD_DP32(t, ID_PFR2, SSBS, 1); /* FEAT_SSBS */ - cpu->isar.id_pfr2 = t; + SET_IDREG(isar, ID_PFR2, t); t = cpu->isar.id_dfr0; t = FIELD_DP32(t, ID_DFR0, COPDBG, 10); /* FEAT_Debugv8p8 */ @@ -228,8 +228,8 @@ static void arm1136_r2_initfn(Object *obj) cpu->isar.mvfr1 = 0x00000000; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00050078; - cpu->isar.id_pfr0 = 0x111; - cpu->isar.id_pfr1 = 0x1; + SET_IDREG(isar, ID_PFR0, 0x111); + SET_IDREG(isar, ID_PFR1, 0x1); cpu->isar.id_dfr0 = 0x2; cpu->id_afr0 = 0x3; cpu->isar.id_mmfr0 = 0x01130003; @@ -260,8 +260,8 @@ static void arm1136_initfn(Object *obj) cpu->isar.mvfr1 = 0x00000000; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00050078; - cpu->isar.id_pfr0 = 0x111; - cpu->isar.id_pfr1 = 0x1; + SET_IDREG(isar, ID_PFR0, 0x111); + SET_IDREG(isar, ID_PFR1, 0x1); cpu->isar.id_dfr0 = 0x2; cpu->id_afr0 = 0x3; cpu->isar.id_mmfr0 = 0x01130003; @@ -293,8 +293,8 @@ static void arm1176_initfn(Object *obj) cpu->isar.mvfr1 = 0x00000000; cpu->ctr = 0x1dd20d2; cpu->reset_sctlr = 0x00050078; - cpu->isar.id_pfr0 = 0x111; - cpu->isar.id_pfr1 = 0x11; + SET_IDREG(isar, ID_PFR0, 0x111); + SET_IDREG(isar, ID_PFR1, 0x11); cpu->isar.id_dfr0 = 0x33; cpu->id_afr0 = 0; cpu->isar.id_mmfr0 = 0x01130003; @@ -323,8 +323,8 @@ static void arm11mpcore_initfn(Object *obj) cpu->isar.mvfr0 = 0x11111111; cpu->isar.mvfr1 = 0x00000000; cpu->ctr = 0x1d192992; /* 32K icache 32K dcache */ - cpu->isar.id_pfr0 = 0x111; - cpu->isar.id_pfr1 = 0x1; + SET_IDREG(isar, ID_PFR0, 0x111); + SET_IDREG(isar, ID_PFR1, 0x1); cpu->isar.id_dfr0 = 0; cpu->id_afr0 = 0x2; cpu->isar.id_mmfr0 = 0x01100103; @@ -363,8 +363,8 @@ static void cortex_a8_initfn(Object *obj) cpu->isar.mvfr1 = 0x00011111; cpu->ctr = 0x82048004; cpu->reset_sctlr = 0x00c50078; - cpu->isar.id_pfr0 = 0x1031; - cpu->isar.id_pfr1 = 0x11; + SET_IDREG(isar, ID_PFR0, 0x1031); + SET_IDREG(isar, ID_PFR1, 0x11); cpu->isar.id_dfr0 = 0x400; cpu->id_afr0 = 0; cpu->isar.id_mmfr0 = 0x31100003; @@ -439,8 +439,8 @@ static void cortex_a9_initfn(Object *obj) cpu->isar.mvfr1 = 0x01111111; cpu->ctr = 0x80038003; cpu->reset_sctlr = 0x00c50078; - cpu->isar.id_pfr0 = 0x1031; - cpu->isar.id_pfr1 = 0x11; + SET_IDREG(isar, ID_PFR0, 0x1031); + SET_IDREG(isar, ID_PFR1, 0x11); cpu->isar.id_dfr0 = 0x000; cpu->id_afr0 = 0; cpu->isar.id_mmfr0 = 0x00100103; @@ -505,8 +505,8 @@ static void cortex_a7_initfn(Object *obj) cpu->isar.mvfr1 = 0x11111111; cpu->ctr = 0x84448003; cpu->reset_sctlr = 0x00c50078; - cpu->isar.id_pfr0 = 0x00001131; - cpu->isar.id_pfr1 = 0x00011011; + SET_IDREG(isar, ID_PFR0, 0x00001131); + SET_IDREG(isar, ID_PFR1, 0x00011011); cpu->isar.id_dfr0 = 0x02010555; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x10101105; @@ -557,8 +557,8 @@ static void cortex_a15_initfn(Object *obj) cpu->isar.mvfr1 = 0x11111111; cpu->ctr = 0x8444c004; cpu->reset_sctlr = 0x00c50078; - cpu->isar.id_pfr0 = 0x00001131; - cpu->isar.id_pfr1 = 0x00011011; + SET_IDREG(isar, ID_PFR0, 0x00001131); + SET_IDREG(isar, ID_PFR1, 0x00011011); cpu->isar.id_dfr0 = 0x02010555; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x10201105; @@ -601,8 +601,8 @@ static void cortex_r5_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_PMSA); set_feature(&cpu->env, ARM_FEATURE_PMU); cpu->midr = 0x411fc153; /* r1p3 */ - cpu->isar.id_pfr0 = 0x0131; - cpu->isar.id_pfr1 = 0x001; + SET_IDREG(isar, ID_PFR0, 0x0131); + SET_IDREG(isar, ID_PFR1, 0x001); cpu->isar.id_dfr0 = 0x010400; cpu->id_afr0 = 0x0; cpu->isar.id_mmfr0 = 0x0210030; @@ -748,8 +748,8 @@ static void cortex_r52_initfn(Object *obj) cpu->isar.mvfr2 = 0x00000043; cpu->ctr = 0x8144c004; cpu->reset_sctlr = 0x30c50838; - cpu->isar.id_pfr0 = 0x00000131; - cpu->isar.id_pfr1 = 0x10111001; + SET_IDREG(isar, ID_PFR0, 0x00000131); + SET_IDREG(isar, ID_PFR1, 0x10111001); cpu->isar.id_dfr0 = 0x03010006; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00211040; @@ -980,8 +980,8 @@ static void arm_max_initfn(Object *obj) cpu->isar.mvfr2 = 0x00000043; cpu->ctr = 0x8444c004; cpu->reset_sctlr = 0x00c50838; - cpu->isar.id_pfr0 = 0x00000131; - cpu->isar.id_pfr1 = 0x00011011; + SET_IDREG(isar, ID_PFR0, 0x00000131); + SET_IDREG(isar, ID_PFR1, 0x00011011); cpu->isar.id_dfr0 = 0x03010066; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x10101105; diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index dd4dc8ada5..c3f90e9d13 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -49,8 +49,8 @@ static void aarch64_a35_initfn(Object *obj) cpu->midr = 0x411fd040; cpu->revidr = 0; cpu->ctr = 0x84448004; - cpu->isar.id_pfr0 = 0x00000131; - cpu->isar.id_pfr1 = 0x00011011; + SET_IDREG(isar, ID_PFR0, 0x00000131); + SET_IDREG(isar, ID_PFR1, 0x00011011); cpu->isar.id_dfr0 = 0x03010066; cpu->id_afr0 = 0; cpu->isar.id_mmfr0 = 0x10201105; @@ -241,9 +241,9 @@ static void aarch64_a55_initfn(Object *obj) cpu->isar.id_mmfr2 = 0x01260000; cpu->isar.id_mmfr3 = 0x02122211; cpu->isar.id_mmfr4 = 0x00021110; - cpu->isar.id_pfr0 = 0x10010131; - cpu->isar.id_pfr1 = 0x00011011; - cpu->isar.id_pfr2 = 0x00000011; + SET_IDREG(isar, ID_PFR0, 0x10010131); + SET_IDREG(isar, ID_PFR1, 0x00011011); + SET_IDREG(isar, ID_PFR2, 0x00000011); cpu->midr = 0x412FD050; /* r2p0 */ cpu->revidr = 0; @@ -295,8 +295,8 @@ static void aarch64_a72_initfn(Object *obj) cpu->isar.mvfr2 = 0x00000043; cpu->ctr = 0x8444c004; cpu->reset_sctlr = 0x00c50838; - cpu->isar.id_pfr0 = 0x00000131; - cpu->isar.id_pfr1 = 0x00011011; + SET_IDREG(isar, ID_PFR0, 0x00000131); + SET_IDREG(isar, ID_PFR1, 0x00011011); cpu->isar.id_dfr0 = 0x03010066; cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x10201105; @@ -374,9 +374,9 @@ static void aarch64_a76_initfn(Object *obj) cpu->isar.id_mmfr2 = 0x01260000; cpu->isar.id_mmfr3 = 0x02122211; cpu->isar.id_mmfr4 = 0x00021110; - cpu->isar.id_pfr0 = 0x10010131; - cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ - cpu->isar.id_pfr2 = 0x00000011; + SET_IDREG(isar, ID_PFR0, 0x10010131); + SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ + SET_IDREG(isar, ID_PFR2, 0x00000011); cpu->midr = 0x414fd0b1; /* r4p1 */ cpu->revidr = 0; @@ -622,9 +622,9 @@ static void aarch64_neoverse_n1_initfn(Object *obj) cpu->isar.id_mmfr2 = 0x01260000; cpu->isar.id_mmfr3 = 0x02122211; cpu->isar.id_mmfr4 = 0x00021110; - cpu->isar.id_pfr0 = 0x10010131; - cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ - cpu->isar.id_pfr2 = 0x00000011; + SET_IDREG(isar, ID_PFR0, 0x10010131); + SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ + SET_IDREG(isar, ID_PFR2, 0x00000011); cpu->midr = 0x414fd0c1; /* r4p1 */ cpu->revidr = 0; @@ -701,9 +701,9 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->isar.id_mmfr2 = 0x01260000; cpu->isar.id_mmfr3 = 0x02122211; cpu->isar.id_mmfr4 = 0x01021110; - cpu->isar.id_pfr0 = 0x21110131; - cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ - cpu->isar.id_pfr2 = 0x00000011; + SET_IDREG(isar, ID_PFR0, 0x21110131); + SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ + SET_IDREG(isar, ID_PFR2, 0x00000011); cpu->midr = 0x411FD402; /* r1p2 */ cpu->revidr = 0; @@ -902,8 +902,8 @@ static void aarch64_a710_initfn(Object *obj) /* Ordered by Section B.4: AArch64 registers */ cpu->midr = 0x412FD471; /* r2p1 */ cpu->revidr = 0; - cpu->isar.id_pfr0 = 0x21110131; - cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ + SET_IDREG(isar, ID_PFR0, 0x21110131); + SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ cpu->isar.id_dfr0 = 0x16011099; cpu->id_afr0 = 0; cpu->isar.id_mmfr0 = 0x10201105; @@ -921,7 +921,7 @@ static void aarch64_a710_initfn(Object *obj) cpu->isar.mvfr0 = 0x10110222; cpu->isar.mvfr1 = 0x13211111; cpu->isar.mvfr2 = 0x00000043; - cpu->isar.id_pfr2 = 0x00000011; + SET_IDREG(isar, ID_PFR2, 0x00000011); SET_IDREG(isar, ID_AA64PFR0, 0x1201111120111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000221ull); SET_IDREG(isar, ID_AA64ZFR0, 0x0000110100110021ull); /* with Crypto */ @@ -1004,8 +1004,8 @@ static void aarch64_neoverse_n2_initfn(Object *obj) /* Ordered by Section B.5: AArch64 ID registers */ cpu->midr = 0x410FD493; /* r0p3 */ cpu->revidr = 0; - cpu->isar.id_pfr0 = 0x21110131; - cpu->isar.id_pfr1 = 0x00010000; /* GIC filled in later */ + SET_IDREG(isar, ID_PFR0, 0x21110131); + SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ cpu->isar.id_dfr0 = 0x16011099; cpu->id_afr0 = 0; cpu->isar.id_mmfr0 = 0x10201105; @@ -1023,7 +1023,7 @@ static void aarch64_neoverse_n2_initfn(Object *obj) cpu->isar.mvfr0 = 0x10110222; cpu->isar.mvfr1 = 0x13211111; cpu->isar.mvfr2 = 0x00000043; - cpu->isar.id_pfr2 = 0x00000011; + SET_IDREG(isar, ID_PFR2, 0x00000011); SET_IDREG(isar, ID_AA64PFR0, 0x1201111120111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000221ull); SET_IDREG(isar, ID_AA64ZFR0, 0x0000110100110021ull); /* with Crypto */ From 33801d9bd0947042f223966bbb04f7528e1443f2 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 1 Jul 2025 15:08:28 +0100 Subject: [PATCH 1736/2760] arm/cpu: Store id_dfr0/1 into the idregs array Reviewed-by: Richard Henderson Reviewed-by: Sebastian Ott Signed-off-by: Eric Auger Signed-off-by: Cornelia Huck Message-id: 20250617153931.1330449-11-cohuck@redhat.com Signed-off-by: Peter Maydell --- hw/intc/armv7m_nvic.c | 2 +- target/arm/cpu-features.h | 16 ++++++++-------- target/arm/cpu.c | 13 +++++-------- target/arm/cpu.h | 2 -- target/arm/cpu64.c | 4 ++-- target/arm/helper.c | 4 ++-- target/arm/kvm.c | 6 ++---- target/arm/tcg/cpu-v7m.c | 12 ++++++------ target/arm/tcg/cpu32.c | 30 ++++++++++++++---------------- target/arm/tcg/cpu64.c | 16 ++++++++-------- 10 files changed, 48 insertions(+), 57 deletions(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 330205fa34..2566dd6343 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -1274,7 +1274,7 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_dfr0; + return GET_IDREG(isar, ID_DFR0); case 0xd4c: /* AFR0. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index a34378577f..0292a7cd6e 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -300,22 +300,22 @@ static inline bool isar_feature_aa32_ats1e1(const ARMISARegisters *id) static inline bool isar_feature_aa32_pmuv3p1(const ARMISARegisters *id) { /* 0xf means "non-standard IMPDEF PMU" */ - return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 4 && - FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; + return FIELD_EX32_IDREG(id, ID_DFR0, PERFMON) >= 4 && + FIELD_EX32_IDREG(id, ID_DFR0, PERFMON) != 0xf; } static inline bool isar_feature_aa32_pmuv3p4(const ARMISARegisters *id) { /* 0xf means "non-standard IMPDEF PMU" */ - return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 5 && - FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; + return FIELD_EX32_IDREG(id, ID_DFR0, PERFMON) >= 5 && + FIELD_EX32_IDREG(id, ID_DFR0, PERFMON) != 0xf; } static inline bool isar_feature_aa32_pmuv3p5(const ARMISARegisters *id) { /* 0xf means "non-standard IMPDEF PMU" */ - return FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) >= 6 && - FIELD_EX32(id->id_dfr0, ID_DFR0, PERFMON) != 0xf; + return FIELD_EX32_IDREG(id, ID_DFR0, PERFMON) >= 6 && + FIELD_EX32_IDREG(id, ID_DFR0, PERFMON) != 0xf; } static inline bool isar_feature_aa32_hpd(const ARMISARegisters *id) @@ -360,12 +360,12 @@ static inline bool isar_feature_aa32_ssbs(const ARMISARegisters *id) static inline bool isar_feature_aa32_debugv7p1(const ARMISARegisters *id) { - return FIELD_EX32(id->id_dfr0, ID_DFR0, COPDBG) >= 5; + return FIELD_EX32_IDREG(id, ID_DFR0, COPDBG) >= 5; } static inline bool isar_feature_aa32_debugv8p2(const ARMISARegisters *id) { - return FIELD_EX32(id->id_dfr0, ID_DFR0, COPDBG) >= 8; + return FIELD_EX32_IDREG(id, ID_DFR0, COPDBG) >= 8; } static inline bool isar_feature_aa32_doublelock(const ARMISARegisters *id) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 62c06c7269..8e77414c2b 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2318,7 +2318,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) * feature registers as well. */ FIELD_DP32_IDREG(isar, ID_PFR1, SECURITY, 0); - cpu->isar.id_dfr0 = FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, COPSDBG, 0); + FIELD_DP32_IDREG(isar, ID_DFR0, COPSDBG, 0); FIELD_DP64_IDREG(isar, ID_AA64PFR0, EL3, 0); /* Disable the realm management extension, which requires EL3. */ @@ -2346,7 +2346,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) #endif } else { FIELD_DP64_IDREG(isar, ID_AA64DFR0, PMUVER, 0); - cpu->isar.id_dfr0 = FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, PERFMON, 0); + FIELD_DP32_IDREG(isar, ID_DFR0, PERFMON, 0); cpu->pmceid0 = 0; cpu->pmceid1 = 0; } @@ -2409,15 +2409,12 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp) FIELD_DP64_IDREG(isar, ID_AA64DFR0, TRACEBUFFER, 0); /* FEAT_TRF (Self-hosted Trace Extension) */ FIELD_DP64_IDREG(isar, ID_AA64DFR0, TRACEFILT, 0); - cpu->isar.id_dfr0 = - FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, TRACEFILT, 0); + FIELD_DP32_IDREG(isar, ID_DFR0, TRACEFILT, 0); /* Trace Macrocell system register access */ FIELD_DP64_IDREG(isar, ID_AA64DFR0, TRACEVER, 0); - cpu->isar.id_dfr0 = - FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, COPTRC, 0); + FIELD_DP32_IDREG(isar, ID_DFR0, COPTRC, 0); /* Memory mapped trace */ - cpu->isar.id_dfr0 = - FIELD_DP32(cpu->isar.id_dfr0, ID_DFR0, MMAPTRC, 0); + FIELD_DP32_IDREG(isar, ID_DFR0, MMAPTRC, 0); /* FEAT_AMU (Activity Monitors Extension) */ FIELD_DP64_IDREG(isar, ID_AA64PFR0, AMU, 0); FIELD_DP32_IDREG(isar, ID_PFR0, AMU, 0); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 30401926e1..c799105eeb 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1059,8 +1059,6 @@ struct ArchCPU { uint32_t mvfr0; uint32_t mvfr1; uint32_t mvfr2; - uint32_t id_dfr0; - uint32_t id_dfr1; uint32_t dbgdidr; uint32_t dbgdevid; uint32_t dbgdevid1; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 5b628aa7eb..47c2eed3c9 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -654,7 +654,7 @@ static void aarch64_a57_initfn(Object *obj) cpu->reset_sctlr = 0x00c50838; SET_IDREG(isar, ID_PFR0, 0x00000131); SET_IDREG(isar, ID_PFR1, 0x00011011); - cpu->isar.id_dfr0 = 0x03010066; + SET_IDREG(isar, ID_DFR0, 0x03010066); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x10101105; cpu->isar.id_mmfr1 = 0x40000000; @@ -716,7 +716,7 @@ static void aarch64_a53_initfn(Object *obj) cpu->reset_sctlr = 0x00c50838; SET_IDREG(isar, ID_PFR0, 0x00000131); SET_IDREG(isar, ID_PFR1, 0x00011011); - cpu->isar.id_dfr0 = 0x03010066; + SET_IDREG(isar, ID_DFR0, 0x03010066); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x10101105; cpu->isar.id_mmfr1 = 0x40000000; diff --git a/target/arm/helper.c b/target/arm/helper.c index 0329923822..4d90ff7fd5 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7800,7 +7800,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 2, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_dfr0 }, + .resetvalue = GET_IDREG(isar, ID_DFR0)}, { .name = "ID_AFR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, @@ -8135,7 +8135,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_dfr1 }, + .resetvalue = GET_IDREG(isar, ID_DFR1)}, { .name = "ID_MMFR5", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, diff --git a/target/arm/kvm.c b/target/arm/kvm.c index d945e652b3..2a6a5329b4 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -334,8 +334,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) */ err |= get_host_cpu_reg(fd, ahcf, ID_PFR0_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_PFR1_EL1_IDX); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_dfr0, - ARM64_SYS_REG(3, 0, 0, 1, 2)); + err |= get_host_cpu_reg(fd, ahcf, ID_DFR0_EL1_IDX); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr0, ARM64_SYS_REG(3, 0, 0, 1, 4)); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr1, @@ -361,8 +360,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr2, ARM64_SYS_REG(3, 0, 0, 3, 2)); err |= get_host_cpu_reg(fd, ahcf, ID_PFR2_EL1_IDX); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_dfr1, - ARM64_SYS_REG(3, 0, 0, 3, 5)); + err |= get_host_cpu_reg(fd, ahcf, ID_DFR1_EL1_IDX); err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr5, ARM64_SYS_REG(3, 0, 0, 3, 6)); diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 4a2c3bd01a..9697c362c1 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -61,7 +61,7 @@ static void cortex_m0_initfn(Object *obj) */ SET_IDREG(isar, ID_PFR0, 0x00000030); SET_IDREG(isar, ID_PFR1, 0x00000200); - cpu->isar.id_dfr0 = 0x00100000; + SET_IDREG(isar, ID_DFR0, 0x00100000); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00000030; cpu->isar.id_mmfr1 = 0x00000000; @@ -87,7 +87,7 @@ static void cortex_m3_initfn(Object *obj) cpu->pmsav7_dregion = 8; SET_IDREG(isar, ID_PFR0, 0x00000030); SET_IDREG(isar, ID_PFR1, 0x00000200); - cpu->isar.id_dfr0 = 0x00100000; + SET_IDREG(isar, ID_DFR0, 0x00100000); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00000030; cpu->isar.id_mmfr1 = 0x00000000; @@ -118,7 +118,7 @@ static void cortex_m4_initfn(Object *obj) cpu->isar.mvfr2 = 0x00000000; SET_IDREG(isar, ID_PFR0, 0x00000030); SET_IDREG(isar, ID_PFR1, 0x00000200); - cpu->isar.id_dfr0 = 0x00100000; + SET_IDREG(isar, ID_DFR0, 0x00100000); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00000030; cpu->isar.id_mmfr1 = 0x00000000; @@ -149,7 +149,7 @@ static void cortex_m7_initfn(Object *obj) cpu->isar.mvfr2 = 0x00000040; SET_IDREG(isar, ID_PFR0, 0x00000030); SET_IDREG(isar, ID_PFR1, 0x00000200); - cpu->isar.id_dfr0 = 0x00100000; + SET_IDREG(isar, ID_DFR0, 0x00100000); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00100030; cpu->isar.id_mmfr1 = 0x00000000; @@ -182,7 +182,7 @@ static void cortex_m33_initfn(Object *obj) cpu->isar.mvfr2 = 0x00000040; SET_IDREG(isar, ID_PFR0, 0x00000030); SET_IDREG(isar, ID_PFR1, 0x00000210); - cpu->isar.id_dfr0 = 0x00200000; + SET_IDREG(isar, ID_DFR0, 0x00200000); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00101F40; cpu->isar.id_mmfr1 = 0x00000000; @@ -220,7 +220,7 @@ static void cortex_m55_initfn(Object *obj) cpu->isar.mvfr2 = 0x00000040; SET_IDREG(isar, ID_PFR0, 0x20000030); SET_IDREG(isar, ID_PFR1, 0x00000230); - cpu->isar.id_dfr0 = 0x10200000; + SET_IDREG(isar, ID_DFR0, 0x10200000); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00111040; cpu->isar.id_mmfr1 = 0x00000000; diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index 56374db269..bec69fe52e 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -82,11 +82,11 @@ void aa32_max_features(ARMCPU *cpu) t = FIELD_DP32(t, ID_PFR2, SSBS, 1); /* FEAT_SSBS */ SET_IDREG(isar, ID_PFR2, t); - t = cpu->isar.id_dfr0; + t = GET_IDREG(isar, ID_DFR0); t = FIELD_DP32(t, ID_DFR0, COPDBG, 10); /* FEAT_Debugv8p8 */ t = FIELD_DP32(t, ID_DFR0, COPSDBG, 10); /* FEAT_Debugv8p8 */ t = FIELD_DP32(t, ID_DFR0, PERFMON, 6); /* FEAT_PMUv3p5 */ - cpu->isar.id_dfr0 = t; + SET_IDREG(isar, ID_DFR0, t); /* Debug ID registers. */ @@ -116,9 +116,7 @@ void aa32_max_features(ARMCPU *cpu) t = FIELD_DP32(t, DBGDEVID1, PCSROFFSET, 2); cpu->isar.dbgdevid1 = t; - t = cpu->isar.id_dfr1; - t = FIELD_DP32(t, ID_DFR1, HPMN0, 1); /* FEAT_HPMN0 */ - cpu->isar.id_dfr1 = t; + FIELD_DP32_IDREG(isar, ID_DFR1, HPMN0, 1); /* FEAT_HPMN0 */ } /* CPU models. These are not needed for the AArch64 linux-user build. */ @@ -230,7 +228,7 @@ static void arm1136_r2_initfn(Object *obj) cpu->reset_sctlr = 0x00050078; SET_IDREG(isar, ID_PFR0, 0x111); SET_IDREG(isar, ID_PFR1, 0x1); - cpu->isar.id_dfr0 = 0x2; + SET_IDREG(isar, ID_DFR0, 0x2); cpu->id_afr0 = 0x3; cpu->isar.id_mmfr0 = 0x01130003; cpu->isar.id_mmfr1 = 0x10030302; @@ -262,7 +260,7 @@ static void arm1136_initfn(Object *obj) cpu->reset_sctlr = 0x00050078; SET_IDREG(isar, ID_PFR0, 0x111); SET_IDREG(isar, ID_PFR1, 0x1); - cpu->isar.id_dfr0 = 0x2; + SET_IDREG(isar, ID_DFR0, 0x2); cpu->id_afr0 = 0x3; cpu->isar.id_mmfr0 = 0x01130003; cpu->isar.id_mmfr1 = 0x10030302; @@ -295,7 +293,7 @@ static void arm1176_initfn(Object *obj) cpu->reset_sctlr = 0x00050078; SET_IDREG(isar, ID_PFR0, 0x111); SET_IDREG(isar, ID_PFR1, 0x11); - cpu->isar.id_dfr0 = 0x33; + SET_IDREG(isar, ID_DFR0, 0x33); cpu->id_afr0 = 0; cpu->isar.id_mmfr0 = 0x01130003; cpu->isar.id_mmfr1 = 0x10030302; @@ -325,7 +323,7 @@ static void arm11mpcore_initfn(Object *obj) cpu->ctr = 0x1d192992; /* 32K icache 32K dcache */ SET_IDREG(isar, ID_PFR0, 0x111); SET_IDREG(isar, ID_PFR1, 0x1); - cpu->isar.id_dfr0 = 0; + SET_IDREG(isar, ID_DFR0, 0); cpu->id_afr0 = 0x2; cpu->isar.id_mmfr0 = 0x01100103; cpu->isar.id_mmfr1 = 0x10020302; @@ -365,7 +363,7 @@ static void cortex_a8_initfn(Object *obj) cpu->reset_sctlr = 0x00c50078; SET_IDREG(isar, ID_PFR0, 0x1031); SET_IDREG(isar, ID_PFR1, 0x11); - cpu->isar.id_dfr0 = 0x400; + SET_IDREG(isar, ID_DFR0, 0x400); cpu->id_afr0 = 0; cpu->isar.id_mmfr0 = 0x31100003; cpu->isar.id_mmfr1 = 0x20000000; @@ -441,7 +439,7 @@ static void cortex_a9_initfn(Object *obj) cpu->reset_sctlr = 0x00c50078; SET_IDREG(isar, ID_PFR0, 0x1031); SET_IDREG(isar, ID_PFR1, 0x11); - cpu->isar.id_dfr0 = 0x000; + SET_IDREG(isar, ID_DFR0, 0x000); cpu->id_afr0 = 0; cpu->isar.id_mmfr0 = 0x00100103; cpu->isar.id_mmfr1 = 0x20000000; @@ -507,7 +505,7 @@ static void cortex_a7_initfn(Object *obj) cpu->reset_sctlr = 0x00c50078; SET_IDREG(isar, ID_PFR0, 0x00001131); SET_IDREG(isar, ID_PFR1, 0x00011011); - cpu->isar.id_dfr0 = 0x02010555; + SET_IDREG(isar, ID_DFR0, 0x02010555); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x10101105; cpu->isar.id_mmfr1 = 0x40000000; @@ -559,7 +557,7 @@ static void cortex_a15_initfn(Object *obj) cpu->reset_sctlr = 0x00c50078; SET_IDREG(isar, ID_PFR0, 0x00001131); SET_IDREG(isar, ID_PFR1, 0x00011011); - cpu->isar.id_dfr0 = 0x02010555; + SET_IDREG(isar, ID_DFR0, 0x02010555); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x10201105; cpu->isar.id_mmfr1 = 0x20000000; @@ -603,7 +601,7 @@ static void cortex_r5_initfn(Object *obj) cpu->midr = 0x411fc153; /* r1p3 */ SET_IDREG(isar, ID_PFR0, 0x0131); SET_IDREG(isar, ID_PFR1, 0x001); - cpu->isar.id_dfr0 = 0x010400; + SET_IDREG(isar, ID_DFR0, 0x010400); cpu->id_afr0 = 0x0; cpu->isar.id_mmfr0 = 0x0210030; cpu->isar.id_mmfr1 = 0x00000000; @@ -750,7 +748,7 @@ static void cortex_r52_initfn(Object *obj) cpu->reset_sctlr = 0x30c50838; SET_IDREG(isar, ID_PFR0, 0x00000131); SET_IDREG(isar, ID_PFR1, 0x10111001); - cpu->isar.id_dfr0 = 0x03010006; + SET_IDREG(isar, ID_DFR0, 0x03010006); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x00211040; cpu->isar.id_mmfr1 = 0x40000000; @@ -982,7 +980,7 @@ static void arm_max_initfn(Object *obj) cpu->reset_sctlr = 0x00c50838; SET_IDREG(isar, ID_PFR0, 0x00000131); SET_IDREG(isar, ID_PFR1, 0x00011011); - cpu->isar.id_dfr0 = 0x03010066; + SET_IDREG(isar, ID_DFR0, 0x03010066); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x10101105; cpu->isar.id_mmfr1 = 0x40000000; diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index c3f90e9d13..aeaade488f 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -51,7 +51,7 @@ static void aarch64_a35_initfn(Object *obj) cpu->ctr = 0x84448004; SET_IDREG(isar, ID_PFR0, 0x00000131); SET_IDREG(isar, ID_PFR1, 0x00011011); - cpu->isar.id_dfr0 = 0x03010066; + SET_IDREG(isar, ID_DFR0, 0x03010066); cpu->id_afr0 = 0; cpu->isar.id_mmfr0 = 0x10201105; cpu->isar.id_mmfr1 = 0x40000000; @@ -228,7 +228,7 @@ static void aarch64_a55_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR0, 0x0000000010112222ull); SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000010ull); cpu->id_afr0 = 0x00000000; - cpu->isar.id_dfr0 = 0x04010088; + SET_IDREG(isar, ID_DFR0, 0x04010088); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232042); @@ -297,7 +297,7 @@ static void aarch64_a72_initfn(Object *obj) cpu->reset_sctlr = 0x00c50838; SET_IDREG(isar, ID_PFR0, 0x00000131); SET_IDREG(isar, ID_PFR1, 0x00011011); - cpu->isar.id_dfr0 = 0x03010066; + SET_IDREG(isar, ID_DFR0, 0x03010066); cpu->id_afr0 = 0x00000000; cpu->isar.id_mmfr0 = 0x10201105; cpu->isar.id_mmfr1 = 0x40000000; @@ -361,7 +361,7 @@ static void aarch64_a76_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR0, 0x1100000010111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000010ull); cpu->id_afr0 = 0x00000000; - cpu->isar.id_dfr0 = 0x04010088; + SET_IDREG(isar, ID_DFR0, 0x04010088); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232042); @@ -609,7 +609,7 @@ static void aarch64_neoverse_n1_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR0, 0x1100000010111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000020ull); cpu->id_afr0 = 0x00000000; - cpu->isar.id_dfr0 = 0x04010088; + SET_IDREG(isar, ID_DFR0, 0x04010088); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232042); @@ -688,7 +688,7 @@ static void aarch64_neoverse_v1_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR0, 0x1101110120111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000020ull); cpu->id_afr0 = 0x00000000; - cpu->isar.id_dfr0 = 0x15011099; + SET_IDREG(isar, ID_DFR0, 0x15011099); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232042); @@ -904,7 +904,7 @@ static void aarch64_a710_initfn(Object *obj) cpu->revidr = 0; SET_IDREG(isar, ID_PFR0, 0x21110131); SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ - cpu->isar.id_dfr0 = 0x16011099; + SET_IDREG(isar, ID_DFR0, 0x16011099); cpu->id_afr0 = 0; cpu->isar.id_mmfr0 = 0x10201105; cpu->isar.id_mmfr1 = 0x40000000; @@ -1006,7 +1006,7 @@ static void aarch64_neoverse_n2_initfn(Object *obj) cpu->revidr = 0; SET_IDREG(isar, ID_PFR0, 0x21110131); SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ - cpu->isar.id_dfr0 = 0x16011099; + SET_IDREG(isar, ID_DFR0, 0x16011099); cpu->id_afr0 = 0; cpu->isar.id_mmfr0 = 0x10201105; cpu->isar.id_mmfr1 = 0x40000000; From 987fa88a11cefe0058326c49dd190ab329635fe0 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Tue, 1 Jul 2025 15:08:29 +0100 Subject: [PATCH 1737/2760] arm/cpu: Store id_mmfr0-5 into the idregs array Reviewed-by: Richard Henderson Reviewed-by: Sebastian Ott Signed-off-by: Eric Auger Signed-off-by: Cornelia Huck Message-id: 20250617153931.1330449-12-cohuck@redhat.com Signed-off-by: Peter Maydell --- hw/intc/armv7m_nvic.c | 8 ++-- target/arm/cpu-features.h | 18 ++++---- target/arm/cpu.h | 6 --- target/arm/cpu64.c | 16 +++---- target/arm/helper.c | 12 ++--- target/arm/kvm.c | 18 +++----- target/arm/tcg/cpu-v7m.c | 48 ++++++++++---------- target/arm/tcg/cpu32.c | 94 +++++++++++++++++++-------------------- target/arm/tcg/cpu64.c | 76 +++++++++++++++---------------- 9 files changed, 140 insertions(+), 156 deletions(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 2566dd6343..6d85720f1b 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -1284,22 +1284,22 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_mmfr0; + return GET_IDREG(isar, ID_MMFR0); case 0xd54: /* MMFR1. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_mmfr1; + return GET_IDREG(isar, ID_MMFR1); case 0xd58: /* MMFR2. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_mmfr2; + return GET_IDREG(isar, ID_MMFR2); case 0xd5c: /* MMFR3. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->isar.id_mmfr3; + return GET_IDREG(isar, ID_MMFR3); case 0xd60: /* ISAR0. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 0292a7cd6e..5d8adfb73b 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -284,17 +284,17 @@ static inline bool isar_feature_aa32_vminmaxnm(const ARMISARegisters *id) static inline bool isar_feature_aa32_pxn(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr0, ID_MMFR0, VMSA) >= 4; + return FIELD_EX32_IDREG(id, ID_MMFR0, VMSA) >= 4; } static inline bool isar_feature_aa32_pan(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr3, ID_MMFR3, PAN) != 0; + return FIELD_EX32_IDREG(id, ID_MMFR3, PAN) != 0; } static inline bool isar_feature_aa32_ats1e1(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr3, ID_MMFR3, PAN) >= 2; + return FIELD_EX32_IDREG(id, ID_MMFR3, PAN) >= 2; } static inline bool isar_feature_aa32_pmuv3p1(const ARMISARegisters *id) @@ -320,32 +320,32 @@ static inline bool isar_feature_aa32_pmuv3p5(const ARMISARegisters *id) static inline bool isar_feature_aa32_hpd(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, HPDS) != 0; + return FIELD_EX32_IDREG(id, ID_MMFR4, HPDS) != 0; } static inline bool isar_feature_aa32_ac2(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, AC2) != 0; + return FIELD_EX32_IDREG(id, ID_MMFR4, AC2) != 0; } static inline bool isar_feature_aa32_ccidx(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, CCIDX) != 0; + return FIELD_EX32_IDREG(id, ID_MMFR4, CCIDX) != 0; } static inline bool isar_feature_aa32_tts2uxn(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, XNX) != 0; + return FIELD_EX32_IDREG(id, ID_MMFR4, XNX) != 0; } static inline bool isar_feature_aa32_half_evt(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, EVT) >= 1; + return FIELD_EX32_IDREG(id, ID_MMFR4, EVT) >= 1; } static inline bool isar_feature_aa32_evt(const ARMISARegisters *id) { - return FIELD_EX32(id->id_mmfr4, ID_MMFR4, EVT) >= 2; + return FIELD_EX32_IDREG(id, ID_MMFR4, EVT) >= 2; } static inline bool isar_feature_aa32_dit(const ARMISARegisters *id) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c799105eeb..8744922330 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1050,12 +1050,6 @@ struct ArchCPU { * field by reading the value from the KVM vCPU. */ struct ARMISARegisters { - uint32_t id_mmfr0; - uint32_t id_mmfr1; - uint32_t id_mmfr2; - uint32_t id_mmfr3; - uint32_t id_mmfr4; - uint32_t id_mmfr5; uint32_t mvfr0; uint32_t mvfr1; uint32_t mvfr2; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 47c2eed3c9..1f3406708b 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -656,10 +656,10 @@ static void aarch64_a57_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x03010066); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10101105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02102211; + SET_IDREG(isar, ID_MMFR0, 0x10101105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01260000); + SET_IDREG(isar, ID_MMFR3, 0x02102211); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232042); @@ -718,10 +718,10 @@ static void aarch64_a53_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x03010066); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10101105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02102211; + SET_IDREG(isar, ID_MMFR0, 0x10101105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01260000); + SET_IDREG(isar, ID_MMFR3, 0x02102211); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232042); diff --git a/target/arm/helper.c b/target/arm/helper.c index 4d90ff7fd5..c311d2df21 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7810,22 +7810,22 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_mmfr0 }, + .resetvalue = GET_IDREG(isar, ID_MMFR0)}, { .name = "ID_MMFR1", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_mmfr1 }, + .resetvalue = GET_IDREG(isar, ID_MMFR1)}, { .name = "ID_MMFR2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_mmfr2 }, + .resetvalue = GET_IDREG(isar, ID_MMFR2)}, { .name = "ID_MMFR3", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_mmfr3 }, + .resetvalue = GET_IDREG(isar, ID_MMFR3)}, { .name = "ID_ISAR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 0, .access = PL1_R, .type = ARM_CP_CONST, @@ -7860,7 +7860,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->isar.id_mmfr4 }, + .resetvalue = GET_IDREG(isar, ID_MMFR4)}, { .name = "ID_ISAR6", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 2, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, @@ -8140,7 +8140,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->isar.id_mmfr5 }, + .resetvalue = GET_IDREG(isar, ID_MMFR5)}, { .name = "RES_0_C0_C3_7", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 7, .access = PL1_R, .type = ARM_CP_CONST, diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 2a6a5329b4..3df046b2b9 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -335,14 +335,10 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) err |= get_host_cpu_reg(fd, ahcf, ID_PFR0_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_PFR1_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_DFR0_EL1_IDX); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr0, - ARM64_SYS_REG(3, 0, 0, 1, 4)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr1, - ARM64_SYS_REG(3, 0, 0, 1, 5)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr2, - ARM64_SYS_REG(3, 0, 0, 1, 6)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr3, - ARM64_SYS_REG(3, 0, 0, 1, 7)); + err |= get_host_cpu_reg(fd, ahcf, ID_MMFR0_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_MMFR1_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_MMFR2_EL1_IDX); + err |= get_host_cpu_reg(fd, ahcf, ID_MMFR3_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_ISAR0_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_ISAR1_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_ISAR2_EL1_IDX); @@ -350,8 +346,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) err |= get_host_cpu_reg(fd, ahcf, ID_ISAR4_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_ISAR5_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_ISAR6_EL1_IDX); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr4, - ARM64_SYS_REG(3, 0, 0, 2, 6)); + err |= get_host_cpu_reg(fd, ahcf, ID_MMFR4_EL1_IDX); err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0, ARM64_SYS_REG(3, 0, 0, 3, 0)); @@ -361,8 +356,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) ARM64_SYS_REG(3, 0, 0, 3, 2)); err |= get_host_cpu_reg(fd, ahcf, ID_PFR2_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_DFR1_EL1_IDX); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.id_mmfr5, - ARM64_SYS_REG(3, 0, 0, 3, 6)); + err |= get_host_cpu_reg(fd, ahcf, ID_MMFR5_EL1_IDX); /* * DBGDIDR is a bit complicated because the kernel doesn't diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index 9697c362c1..eddd7117d5 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -63,10 +63,10 @@ static void cortex_m0_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00000200); SET_IDREG(isar, ID_DFR0, 0x00100000); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00000030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x00000000; - cpu->isar.id_mmfr3 = 0x00000000; + SET_IDREG(isar, ID_MMFR0, 0x00000030); + SET_IDREG(isar, ID_MMFR1, 0x00000000); + SET_IDREG(isar, ID_MMFR2, 0x00000000); + SET_IDREG(isar, ID_MMFR3, 0x00000000); SET_IDREG(isar, ID_ISAR0, 0x01141110); SET_IDREG(isar, ID_ISAR1, 0x02111000); SET_IDREG(isar, ID_ISAR2, 0x21112231); @@ -89,10 +89,10 @@ static void cortex_m3_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00000200); SET_IDREG(isar, ID_DFR0, 0x00100000); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00000030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x00000000; - cpu->isar.id_mmfr3 = 0x00000000; + SET_IDREG(isar, ID_MMFR0, 0x00000030); + SET_IDREG(isar, ID_MMFR1, 0x00000000); + SET_IDREG(isar, ID_MMFR2, 0x00000000); + SET_IDREG(isar, ID_MMFR3, 0x00000000); SET_IDREG(isar, ID_ISAR0, 0x01141110); SET_IDREG(isar, ID_ISAR1, 0x02111000); SET_IDREG(isar, ID_ISAR2, 0x21112231); @@ -120,10 +120,10 @@ static void cortex_m4_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00000200); SET_IDREG(isar, ID_DFR0, 0x00100000); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00000030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x00000000; - cpu->isar.id_mmfr3 = 0x00000000; + SET_IDREG(isar, ID_MMFR0, 0x00000030); + SET_IDREG(isar, ID_MMFR1, 0x00000000); + SET_IDREG(isar, ID_MMFR2, 0x00000000); + SET_IDREG(isar, ID_MMFR3, 0x00000000); SET_IDREG(isar, ID_ISAR0, 0x01141110); SET_IDREG(isar, ID_ISAR1, 0x02111000); SET_IDREG(isar, ID_ISAR2, 0x21112231); @@ -151,10 +151,10 @@ static void cortex_m7_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00000200); SET_IDREG(isar, ID_DFR0, 0x00100000); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00100030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01000000; - cpu->isar.id_mmfr3 = 0x00000000; + SET_IDREG(isar, ID_MMFR0, 0x00100030); + SET_IDREG(isar, ID_MMFR1, 0x00000000); + SET_IDREG(isar, ID_MMFR2, 0x01000000); + SET_IDREG(isar, ID_MMFR3, 0x00000000); SET_IDREG(isar, ID_ISAR0, 0x01101110); SET_IDREG(isar, ID_ISAR1, 0x02112000); SET_IDREG(isar, ID_ISAR2, 0x20232231); @@ -184,10 +184,10 @@ static void cortex_m33_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00000210); SET_IDREG(isar, ID_DFR0, 0x00200000); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00101F40; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01000000; - cpu->isar.id_mmfr3 = 0x00000000; + SET_IDREG(isar, ID_MMFR0, 0x00101F40); + SET_IDREG(isar, ID_MMFR1, 0x00000000); + SET_IDREG(isar, ID_MMFR2, 0x01000000); + SET_IDREG(isar, ID_MMFR3, 0x00000000); SET_IDREG(isar, ID_ISAR0, 0x01101110); SET_IDREG(isar, ID_ISAR1, 0x02212000); SET_IDREG(isar, ID_ISAR2, 0x20232232); @@ -222,10 +222,10 @@ static void cortex_m55_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00000230); SET_IDREG(isar, ID_DFR0, 0x10200000); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00111040; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01000000; - cpu->isar.id_mmfr3 = 0x00000011; + SET_IDREG(isar, ID_MMFR0, 0x00111040); + SET_IDREG(isar, ID_MMFR1, 0x00000000); + SET_IDREG(isar, ID_MMFR2, 0x01000000); + SET_IDREG(isar, ID_MMFR3, 0x00000011); SET_IDREG(isar, ID_ISAR0, 0x01103110); SET_IDREG(isar, ID_ISAR1, 0x02212000); SET_IDREG(isar, ID_ISAR2, 0x20232232); diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index bec69fe52e..942b636aa5 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -55,21 +55,17 @@ void aa32_max_features(ARMCPU *cpu) t = FIELD_DP32(t, MVFR2, FPMISC, 4); /* FP MaxNum */ cpu->isar.mvfr2 = t; - t = cpu->isar.id_mmfr3; - t = FIELD_DP32(t, ID_MMFR3, PAN, 2); /* FEAT_PAN2 */ - cpu->isar.id_mmfr3 = t; + FIELD_DP32_IDREG(isar, ID_MMFR3, PAN, 2); /* FEAT_PAN2 */ - t = cpu->isar.id_mmfr4; + t = GET_IDREG(isar, ID_MMFR4); t = FIELD_DP32(t, ID_MMFR4, HPDS, 2); /* FEAT_HPDS2 */ t = FIELD_DP32(t, ID_MMFR4, AC2, 1); /* ACTLR2, HACTLR2 */ t = FIELD_DP32(t, ID_MMFR4, CNP, 1); /* FEAT_TTCNP */ t = FIELD_DP32(t, ID_MMFR4, XNX, 1); /* FEAT_XNX */ t = FIELD_DP32(t, ID_MMFR4, EVT, 2); /* FEAT_EVT */ - cpu->isar.id_mmfr4 = t; + SET_IDREG(isar, ID_MMFR4, t); - t = cpu->isar.id_mmfr5; - t = FIELD_DP32(t, ID_MMFR5, ETS, 2); /* FEAT_ETS2 */ - cpu->isar.id_mmfr5 = t; + FIELD_DP32_IDREG(isar, ID_MMFR5, ETS, 2); /* FEAT_ETS2 */ t = GET_IDREG(isar, ID_PFR0); t = FIELD_DP32(t, ID_PFR0, CSV2, 2); /* FEAT_CSV2 */ @@ -230,9 +226,9 @@ static void arm1136_r2_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x1); SET_IDREG(isar, ID_DFR0, 0x2); cpu->id_afr0 = 0x3; - cpu->isar.id_mmfr0 = 0x01130003; - cpu->isar.id_mmfr1 = 0x10030302; - cpu->isar.id_mmfr2 = 0x01222110; + SET_IDREG(isar, ID_MMFR0, 0x01130003); + SET_IDREG(isar, ID_MMFR1, 0x10030302); + SET_IDREG(isar, ID_MMFR2, 0x01222110); SET_IDREG(isar, ID_ISAR0, 0x00140011); SET_IDREG(isar, ID_ISAR1, 0x12002111); SET_IDREG(isar, ID_ISAR2, 0x11231111); @@ -262,9 +258,9 @@ static void arm1136_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x1); SET_IDREG(isar, ID_DFR0, 0x2); cpu->id_afr0 = 0x3; - cpu->isar.id_mmfr0 = 0x01130003; - cpu->isar.id_mmfr1 = 0x10030302; - cpu->isar.id_mmfr2 = 0x01222110; + SET_IDREG(isar, ID_MMFR0, 0x01130003); + SET_IDREG(isar, ID_MMFR1, 0x10030302); + SET_IDREG(isar, ID_MMFR2, 0x01222110); SET_IDREG(isar, ID_ISAR0, 0x00140011); SET_IDREG(isar, ID_ISAR1, 0x12002111); SET_IDREG(isar, ID_ISAR2, 0x11231111); @@ -295,9 +291,9 @@ static void arm1176_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x11); SET_IDREG(isar, ID_DFR0, 0x33); cpu->id_afr0 = 0; - cpu->isar.id_mmfr0 = 0x01130003; - cpu->isar.id_mmfr1 = 0x10030302; - cpu->isar.id_mmfr2 = 0x01222100; + SET_IDREG(isar, ID_MMFR0, 0x01130003); + SET_IDREG(isar, ID_MMFR1, 0x10030302); + SET_IDREG(isar, ID_MMFR2, 0x01222100); SET_IDREG(isar, ID_ISAR0, 0x0140011); SET_IDREG(isar, ID_ISAR1, 0x12002111); SET_IDREG(isar, ID_ISAR2, 0x11231121); @@ -325,9 +321,9 @@ static void arm11mpcore_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x1); SET_IDREG(isar, ID_DFR0, 0); cpu->id_afr0 = 0x2; - cpu->isar.id_mmfr0 = 0x01100103; - cpu->isar.id_mmfr1 = 0x10020302; - cpu->isar.id_mmfr2 = 0x01222000; + SET_IDREG(isar, ID_MMFR0, 0x01100103); + SET_IDREG(isar, ID_MMFR1, 0x10020302); + SET_IDREG(isar, ID_MMFR2, 0x01222000); SET_IDREG(isar, ID_ISAR0, 0x00100011); SET_IDREG(isar, ID_ISAR1, 0x12002111); SET_IDREG(isar, ID_ISAR2, 0x11221011); @@ -365,10 +361,10 @@ static void cortex_a8_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x11); SET_IDREG(isar, ID_DFR0, 0x400); cpu->id_afr0 = 0; - cpu->isar.id_mmfr0 = 0x31100003; - cpu->isar.id_mmfr1 = 0x20000000; - cpu->isar.id_mmfr2 = 0x01202000; - cpu->isar.id_mmfr3 = 0x11; + SET_IDREG(isar, ID_MMFR0, 0x31100003); + SET_IDREG(isar, ID_MMFR1, 0x20000000); + SET_IDREG(isar, ID_MMFR2, 0x01202000); + SET_IDREG(isar, ID_MMFR3, 0x11); SET_IDREG(isar, ID_ISAR0, 0x00101111); SET_IDREG(isar, ID_ISAR1, 0x12112111); SET_IDREG(isar, ID_ISAR2, 0x21232031); @@ -441,10 +437,10 @@ static void cortex_a9_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x11); SET_IDREG(isar, ID_DFR0, 0x000); cpu->id_afr0 = 0; - cpu->isar.id_mmfr0 = 0x00100103; - cpu->isar.id_mmfr1 = 0x20000000; - cpu->isar.id_mmfr2 = 0x01230000; - cpu->isar.id_mmfr3 = 0x00002111; + SET_IDREG(isar, ID_MMFR0, 0x00100103); + SET_IDREG(isar, ID_MMFR1, 0x20000000); + SET_IDREG(isar, ID_MMFR2, 0x01230000); + SET_IDREG(isar, ID_MMFR3, 0x00002111); SET_IDREG(isar, ID_ISAR0, 0x00101111); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232041); @@ -507,10 +503,10 @@ static void cortex_a7_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x02010555); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10101105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01240000; - cpu->isar.id_mmfr3 = 0x02102211; + SET_IDREG(isar, ID_MMFR0, 0x10101105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01240000); + SET_IDREG(isar, ID_MMFR3, 0x02102211); /* * a7_mpcore_r0p5_trm, page 4-4 gives 0x01101110; but * table 4-41 gives 0x02101110, which includes the arm div insns. @@ -559,10 +555,10 @@ static void cortex_a15_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x02010555); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x20000000; - cpu->isar.id_mmfr2 = 0x01240000; - cpu->isar.id_mmfr3 = 0x02102211; + SET_IDREG(isar, ID_MMFR0, 0x10201105); + SET_IDREG(isar, ID_MMFR1, 0x20000000); + SET_IDREG(isar, ID_MMFR2, 0x01240000); + SET_IDREG(isar, ID_MMFR3, 0x02102211); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232041); @@ -603,10 +599,10 @@ static void cortex_r5_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x001); SET_IDREG(isar, ID_DFR0, 0x010400); cpu->id_afr0 = 0x0; - cpu->isar.id_mmfr0 = 0x0210030; - cpu->isar.id_mmfr1 = 0x00000000; - cpu->isar.id_mmfr2 = 0x01200000; - cpu->isar.id_mmfr3 = 0x0211; + SET_IDREG(isar, ID_MMFR0, 0x0210030); + SET_IDREG(isar, ID_MMFR1, 0x00000000); + SET_IDREG(isar, ID_MMFR2, 0x01200000); + SET_IDREG(isar, ID_MMFR3, 0x0211); SET_IDREG(isar, ID_ISAR0, 0x02101111); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232141); @@ -750,11 +746,11 @@ static void cortex_r52_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x10111001); SET_IDREG(isar, ID_DFR0, 0x03010006); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x00211040; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01200000; - cpu->isar.id_mmfr3 = 0xf0102211; - cpu->isar.id_mmfr4 = 0x00000010; + SET_IDREG(isar, ID_MMFR0, 0x00211040); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01200000); + SET_IDREG(isar, ID_MMFR3, 0xf0102211); + SET_IDREG(isar, ID_MMFR4, 0x00000010); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232142); @@ -982,10 +978,10 @@ static void arm_max_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x03010066); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10101105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02102211; + SET_IDREG(isar, ID_MMFR0, 0x10101105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01260000); + SET_IDREG(isar, ID_MMFR3, 0x02102211); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232042); diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index aeaade488f..937f29e253 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -53,10 +53,10 @@ static void aarch64_a35_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x03010066); cpu->id_afr0 = 0; - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02102211; + SET_IDREG(isar, ID_MMFR0, 0x10201105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01260000); + SET_IDREG(isar, ID_MMFR3, 0x02102211); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232042); @@ -236,11 +236,11 @@ static void aarch64_a55_initfn(Object *obj) SET_IDREG(isar, ID_ISAR4, 0x00011142); SET_IDREG(isar, ID_ISAR5, 0x01011121); SET_IDREG(isar, ID_ISAR6, 0x00000010); - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02122211; - cpu->isar.id_mmfr4 = 0x00021110; + SET_IDREG(isar, ID_MMFR0, 0x10201105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01260000); + SET_IDREG(isar, ID_MMFR3, 0x02122211); + SET_IDREG(isar, ID_MMFR4, 0x00021110); SET_IDREG(isar, ID_PFR0, 0x10010131); SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_PFR2, 0x00000011); @@ -299,10 +299,10 @@ static void aarch64_a72_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x03010066); cpu->id_afr0 = 0x00000000; - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02102211; + SET_IDREG(isar, ID_MMFR0, 0x10201105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01260000); + SET_IDREG(isar, ID_MMFR3, 0x02102211); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232042); @@ -369,11 +369,11 @@ static void aarch64_a76_initfn(Object *obj) SET_IDREG(isar, ID_ISAR4, 0x00010142); SET_IDREG(isar, ID_ISAR5, 0x01011121); SET_IDREG(isar, ID_ISAR6, 0x00000010); - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02122211; - cpu->isar.id_mmfr4 = 0x00021110; + SET_IDREG(isar, ID_MMFR0, 0x10201105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01260000); + SET_IDREG(isar, ID_MMFR3, 0x02122211); + SET_IDREG(isar, ID_MMFR4, 0x00021110); SET_IDREG(isar, ID_PFR0, 0x10010131); SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ SET_IDREG(isar, ID_PFR2, 0x00000011); @@ -617,11 +617,11 @@ static void aarch64_neoverse_n1_initfn(Object *obj) SET_IDREG(isar, ID_ISAR4, 0x00010142); SET_IDREG(isar, ID_ISAR5, 0x01011121); SET_IDREG(isar, ID_ISAR6, 0x00000010); - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02122211; - cpu->isar.id_mmfr4 = 0x00021110; + SET_IDREG(isar, ID_MMFR0, 0x10201105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01260000); + SET_IDREG(isar, ID_MMFR3, 0x02122211); + SET_IDREG(isar, ID_MMFR4, 0x00021110); SET_IDREG(isar, ID_PFR0, 0x10010131); SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ SET_IDREG(isar, ID_PFR2, 0x00000011); @@ -696,11 +696,11 @@ static void aarch64_neoverse_v1_initfn(Object *obj) SET_IDREG(isar, ID_ISAR4, 0x00010142); SET_IDREG(isar, ID_ISAR5, 0x11011121); SET_IDREG(isar, ID_ISAR6, 0x01100111); - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02122211; - cpu->isar.id_mmfr4 = 0x01021110; + SET_IDREG(isar, ID_MMFR0, 0x10201105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01260000); + SET_IDREG(isar, ID_MMFR3, 0x02122211); + SET_IDREG(isar, ID_MMFR4, 0x01021110); SET_IDREG(isar, ID_PFR0, 0x21110131); SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ SET_IDREG(isar, ID_PFR2, 0x00000011); @@ -906,17 +906,17 @@ static void aarch64_a710_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ SET_IDREG(isar, ID_DFR0, 0x16011099); cpu->id_afr0 = 0; - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02122211; + SET_IDREG(isar, ID_MMFR0, 0x10201105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01260000); + SET_IDREG(isar, ID_MMFR3, 0x02122211); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232042); SET_IDREG(isar, ID_ISAR3, 0x01112131); SET_IDREG(isar, ID_ISAR4, 0x00010142); SET_IDREG(isar, ID_ISAR5, 0x11011121); /* with Crypto */ - cpu->isar.id_mmfr4 = 0x21021110; + SET_IDREG(isar, ID_MMFR4, 0x21021110); SET_IDREG(isar, ID_ISAR6, 0x01111111); cpu->isar.mvfr0 = 0x10110222; cpu->isar.mvfr1 = 0x13211111; @@ -1008,17 +1008,17 @@ static void aarch64_neoverse_n2_initfn(Object *obj) SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ SET_IDREG(isar, ID_DFR0, 0x16011099); cpu->id_afr0 = 0; - cpu->isar.id_mmfr0 = 0x10201105; - cpu->isar.id_mmfr1 = 0x40000000; - cpu->isar.id_mmfr2 = 0x01260000; - cpu->isar.id_mmfr3 = 0x02122211; + SET_IDREG(isar, ID_MMFR0, 0x10201105); + SET_IDREG(isar, ID_MMFR1, 0x40000000); + SET_IDREG(isar, ID_MMFR2, 0x01260000); + SET_IDREG(isar, ID_MMFR3, 0x02122211); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); SET_IDREG(isar, ID_ISAR2, 0x21232042); SET_IDREG(isar, ID_ISAR3, 0x01112131); SET_IDREG(isar, ID_ISAR4, 0x00010142); SET_IDREG(isar, ID_ISAR5, 0x11011121); /* with Crypto */ - cpu->isar.id_mmfr4 = 0x01021110; + SET_IDREG(isar, ID_MMFR4, 0x01021110); SET_IDREG(isar, ID_ISAR6, 0x01111111); cpu->isar.mvfr0 = 0x10110222; cpu->isar.mvfr1 = 0x13211111; From 8776a0c289f86348ec11e1edb3150ef32d78100a Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Tue, 1 Jul 2025 15:08:29 +0100 Subject: [PATCH 1738/2760] arm/kvm: use fd instead of fdarray[2] We have fd, so might as well neaten things up. Suggested-by: Eric Auger Reviewed-by: Eric Auger Signed-off-by: Cornelia Huck Message-id: 20250617153931.1330449-15-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/kvm.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 3df046b2b9..70919aedd0 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -348,11 +348,11 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) err |= get_host_cpu_reg(fd, ahcf, ID_ISAR6_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_MMFR4_EL1_IDX); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr0, + err |= read_sys_reg32(fd, &ahcf->isar.mvfr0, ARM64_SYS_REG(3, 0, 0, 3, 0)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr1, + err |= read_sys_reg32(fd, &ahcf->isar.mvfr1, ARM64_SYS_REG(3, 0, 0, 3, 1)); - err |= read_sys_reg32(fdarray[2], &ahcf->isar.mvfr2, + err |= read_sys_reg32(fd, &ahcf->isar.mvfr2, ARM64_SYS_REG(3, 0, 0, 3, 2)); err |= get_host_cpu_reg(fd, ahcf, ID_PFR2_EL1_IDX); err |= get_host_cpu_reg(fd, ahcf, ID_DFR1_EL1_IDX); @@ -390,7 +390,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) if (pmu_supported) { /* PMCR_EL0 is only accessible if the vCPU has feature PMU_V3 */ - err |= read_sys_reg64(fdarray[2], &ahcf->isar.reset_pmcr_el0, + err |= read_sys_reg64(fd, &ahcf->isar.reset_pmcr_el0, ARM64_SYS_REG(3, 3, 9, 12, 0)); } From 374d766da75ab89749f59813f7ae55d913c37b58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:29 +0100 Subject: [PATCH 1739/2760] hw/intc/gicv3_its: Do not check its_class_name() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit cc5e719e2c8 ("kvm: require KVM_CAP_SIGNAL_MSI"), the single implementation of its_class_name() no longer returns NULL (it now always returns a valid char pointer). Hence, update the prototype docstring and remove the tautological checks that use the its_class_name() returned value. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Gustavo Romero Reviewed-by: Richard Henderson Reviewed-by: Eric Auger Message-id: 20250628195722.977078-2-gustavo.romero@linaro.org Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 32 +++++++++++--------------- include/hw/intc/arm_gicv3_its_common.h | 2 +- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 7e8e0f0298..9eee284c80 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -737,20 +737,18 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) memmap[VIRT_HIGH_GIC_REDIST2].size); } - if (its_class_name()) { - /* - * ACPI spec, Revision 6.0 Errata A - * (original 6.0 definition has invalid Length) - * 5.2.12.18 GIC ITS Structure - */ - build_append_int_noprefix(table_data, 0xF, 1); /* Type */ - build_append_int_noprefix(table_data, 20, 1); /* Length */ - build_append_int_noprefix(table_data, 0, 2); /* Reserved */ - build_append_int_noprefix(table_data, 0, 4); /* GIC ITS ID */ - /* Physical Base Address */ - build_append_int_noprefix(table_data, memmap[VIRT_GIC_ITS].base, 8); - build_append_int_noprefix(table_data, 0, 4); /* Reserved */ - } + /* + * ACPI spec, Revision 6.0 Errata A + * (original 6.0 definition has invalid Length) + * 5.2.12.18 GIC ITS Structure + */ + build_append_int_noprefix(table_data, 0xF, 1); /* Type */ + build_append_int_noprefix(table_data, 20, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); /* GIC ITS ID */ + /* Physical Base Address */ + build_append_int_noprefix(table_data, memmap[VIRT_GIC_ITS].base, 8); + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ } else { const uint16_t spi_base = vms->irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE; @@ -969,10 +967,8 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) vms->oem_table_id); } - if (its_class_name()) { - acpi_add_table(table_offsets, tables_blob); - build_iort(tables_blob, tables->linker, vms); - } + acpi_add_table(table_offsets, tables_blob); + build_iort(tables_blob, tables->linker, vms); #ifdef CONFIG_TPM if (tpm_get_version(tpm_find()) == TPM_VERSION_2_0) { diff --git a/include/hw/intc/arm_gicv3_its_common.h b/include/hw/intc/arm_gicv3_its_common.h index 7dc712b38d..3c7b543b01 100644 --- a/include/hw/intc/arm_gicv3_its_common.h +++ b/include/hw/intc/arm_gicv3_its_common.h @@ -128,7 +128,7 @@ struct GICv3ITSCommonClass { * Return the ITS class name to use depending on whether KVM acceleration * and KVM CAP_SIGNAL_MSI are supported * - * Returns: class name to use or NULL + * Returns: class name to use */ const char *its_class_name(void); From 43eb1805437e1c904fcb5d8ce93096301128a4d0 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 1 Jul 2025 15:08:29 +0100 Subject: [PATCH 1740/2760] hw/arm/virt: Simplify logic for setting instance's 'tcg_its' variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because 'tcg_its' in the machine instance is set based on the machine class’s negated variable 'no_tcg_its', 'tcg_its' is the opposite of 'no_tcg_its' and hence the code in question can be simplified as: tcg_its = !no_tcg_its. Signed-off-by: Gustavo Romero Reviewed-by: Eric Auger Message-id: 20250628195722.977078-3-gustavo.romero@linaro.org Signed-off-by: Peter Maydell --- hw/arm/virt.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 99fde5836c..6d9256a525 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -3342,12 +3342,8 @@ static void virt_instance_init(Object *obj) /* Default allows ITS instantiation */ vms->its = true; - - if (vmc->no_tcg_its) { - vms->tcg_its = false; - } else { - vms->tcg_its = true; - } + /* Allow ITS emulation if the machine version supports it */ + vms->tcg_its = !vmc->no_tcg_its; /* Default disallows iommu instantiation */ vms->iommu = VIRT_IOMMU_NONE; From 029cd5d6d1b6fea5d3280c7093e2adbd7d9e32f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:30 +0100 Subject: [PATCH 1741/2760] hw/arm/virt: Simplify create_its() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to strstr() check the class name when we can use kvm_irqchip_in_kernel() to check if the ITS from the host can be used. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Eric Auger Reviewed-by: Gustavo Romero Message-id: 20250628195722.977078-4-gustavo.romero@linaro.org Signed-off-by: Peter Maydell --- hw/arm/virt.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 6d9256a525..ae419e8671 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -705,21 +705,18 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) static void create_its(VirtMachineState *vms) { - const char *itsclass = its_class_name(); DeviceState *dev; - if (!strcmp(itsclass, "arm-gicv3-its")) { - if (!vms->tcg_its) { - itsclass = NULL; - } - } - - if (!itsclass) { - /* Do nothing if not supported */ + assert(vms->its); + if (!kvm_irqchip_in_kernel() && !vms->tcg_its) { + /* + * Do nothing if ITS is neither supported by the host nor emulated by + * the machine. + */ return; } - dev = qdev_new(itsclass); + dev = qdev_new(its_class_name()); object_property_set_link(OBJECT(dev), "parent-gicv3", OBJECT(vms->gic), &error_abort); From 67473d32f020a3c62e24790ab93eb6086a8c8283 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 1 Jul 2025 15:08:30 +0100 Subject: [PATCH 1742/2760] hw/arm/virt-acpi-build: Improve comment in build_iort When building the Root Complex table, the comment about the code that maps the RC node to SMMU node is misleading because it reads "RC -> SMMUv3 -> ITS", but the code is only mapping the RCs IDs to the SMMUv3 node. The step of mapping from the SMMUv3 IDs to the ITS Group node is actually defined in another table (in the SMMUv3 node). So change the comment to read "RC -> SMMUv3" instead. Signed-off-by Gustavo Romero Reviewed-by: Eric Auger Message-id: 20250628195722.977078-5-gustavo.romero@linaro.org Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 9eee284c80..e9cd3fb351 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -370,7 +370,7 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) /* DeviceID mapping index (ignored since interrupts are GSIV based) */ build_append_int_noprefix(table_data, 0, 4); - /* output IORT node is the ITS group node (the first node) */ + /* Output IORT node is the ITS Group node (the first node) */ build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET); } @@ -407,23 +407,36 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) if (vms->iommu == VIRT_IOMMU_SMMUV3) { AcpiIortIdMapping *range; - /* translated RIDs connect to SMMUv3 node: RC -> SMMUv3 -> ITS */ + /* + * Map RIDs (input) from RC to SMMUv3 nodes: RC -> SMMUv3. + * + * N.B.: The mapping from SMMUv3 to ITS Group node (SMMUv3 -> ITS) is + * defined in the SMMUv3 table, where all SMMUv3 IDs are mapped to the + * ITS Group node. + */ for (i = 0; i < smmu_idmaps->len; i++) { range = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i); - /* output IORT node is the smmuv3 node */ + /* Output IORT node is the SMMUv3 node. */ build_iort_id_mapping(table_data, range->input_base, range->id_count, smmu_offset); } - /* bypassed RIDs connect to ITS group node directly: RC -> ITS */ + /* + * Map bypassed (don't go throught the SMMU) RIDs (input) to ITS Group + * node directly: RC -> ITS. + */ for (i = 0; i < its_idmaps->len; i++) { range = &g_array_index(its_idmaps, AcpiIortIdMapping, i); - /* output IORT node is the ITS group node (the first node) */ + /* Output IORT node is the ITS Group node (the first node). */ build_iort_id_mapping(table_data, range->input_base, range->id_count, IORT_NODE_OFFSET); } } else { - /* output IORT node is the ITS group node (the first node) */ + /* + * Map all RIDs (input) to ITS Group node directly, since there is no + * SMMU: RC -> ITS. + * Output IORT node is the ITS Group node (the first node). + */ build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET); } From 1c41eaa5fe03cbe9f60edb8f0414c1ad6d7fd786 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 1 Jul 2025 15:08:30 +0100 Subject: [PATCH 1743/2760] hw/arm/virt-acpi-build: Factor out create_its_idmaps Factor out a new function, create_its_idmaps(), from the current build_iort code. Add proper comments to it clarifying how the ID ranges that go directly to the ITS Group node are computed based on the ones that are directed to the SMMU node. Suggested-by: Eric Auger Signed-off-by: Gustavo Romero Message-id: 20250628195722.977078-6-gustavo.romero@linaro.org [PMM: drop hardcoded tabs] Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 61 ++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 21 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index e9cd3fb351..5886192fe3 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -266,6 +266,43 @@ static int iort_idmap_compare(gconstpointer a, gconstpointer b) return idmap_a->input_base - idmap_b->input_base; } +/* Compute ID ranges (RIDs) from RC that are directed to the ITS Group node */ +static void create_its_idmaps(GArray *its_idmaps, GArray *smmu_idmaps) +{ + AcpiIortIdMapping *idmap; + AcpiIortIdMapping next_range = {0}; + + /* + * Based on the RID ranges that are directed to the SMMU, determine the + * bypassed RID ranges, i.e., the ones that are directed to the ITS Group + * node and do not pass through the SMMU, by subtracting the SMMU-bound + * ranges from the full RID range (0x0000–0xFFFF). + */ + for (int i = 0; i < smmu_idmaps->len; i++) { + idmap = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i); + + if (next_range.input_base < idmap->input_base) { + next_range.id_count = idmap->input_base - next_range.input_base; + g_array_append_val(its_idmaps, next_range); + } + + next_range.input_base = idmap->input_base + idmap->id_count; + } + + /* + * Append the last RC -> ITS ID mapping. + * + * RIDs are 16-bit, according to the PCI Express 2.0 Base Specification, rev + * 0.9, section 2.2.6.2, "Transaction Descriptor - Transaction ID Field", + * hence the end of the range is 0x10000. + */ + if (next_range.input_base < 0x10000) { + next_range.id_count = 0x10000 - next_range.input_base; + g_array_append_val(its_idmaps, next_range); + } +} + + /* * Input Output Remapping Table (IORT) * Conforms to "IO Remapping Table System Software on ARM Platforms", @@ -276,7 +313,6 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) { int i, nb_nodes, rc_mapping_count; size_t node_size, smmu_offset = 0; - AcpiIortIdMapping *idmap; uint32_t id = 0; GArray *smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); GArray *its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); @@ -287,8 +323,6 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) acpi_table_begin(&table, table_data); if (vms->iommu == VIRT_IOMMU_SMMUV3) { - AcpiIortIdMapping next_range = {0}; - object_child_foreach_recursive(object_get_root(), iort_host_bridges, smmu_idmaps); @@ -296,25 +330,10 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) g_array_sort(smmu_idmaps, iort_idmap_compare); /* - * Split the whole RIDs by mapping from RC to SMMU, - * build the ID mapping from RC to ITS directly. + * Knowing the ID ranges from the RC to the SMMU, it's possible to + * determine the ID ranges from RC that are directed to the ITS. */ - for (i = 0; i < smmu_idmaps->len; i++) { - idmap = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i); - - if (next_range.input_base < idmap->input_base) { - next_range.id_count = idmap->input_base - next_range.input_base; - g_array_append_val(its_idmaps, next_range); - } - - next_range.input_base = idmap->input_base + idmap->id_count; - } - - /* Append the last RC -> ITS ID mapping */ - if (next_range.input_base < 0x10000) { - next_range.id_count = 0x10000 - next_range.input_base; - g_array_append_val(its_idmaps, next_range); - } + create_its_idmaps(its_idmaps, smmu_idmaps); nb_nodes = 3; /* RC, ITS, SMMUv3 */ rc_mapping_count = smmu_idmaps->len + its_idmaps->len; From f63c6c23fba52b1496420e31299de42fd342a2c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:30 +0100 Subject: [PATCH 1744/2760] qtest/bios-tables-test: Add test for when ITS is off on aarch64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Arm64 GIC ITS (Interrupt Translation Service) is an optional piece of hardware introduced in GICv3 and, being optional, it can be disabled in QEMU aarch64 VMs that support it using machine option "its=off", like, for instance: "-M virt,its=off". In ACPI, the ITS is advertised, if present, in the MADT (aka APIC) table, while the ID mappings from the Root Complex (RC) and from the SMMU nodes to the ITS Group nodes are described in the IORT table. This new test verifies that when the "its=off" option is passed to the machine the ITS-related data is correctly pruned from the ACPI tables. The new blobs for this test will be added in a following commit. Signed-off-by: Philippe Mathieu-Daudé Signed-off-by: Gustavo Romero Reviewed-by: Eric Auger Message-id: 20250628195722.977078-7-gustavo.romero@linaro.org Signed-off-by: Peter Maydell --- tests/qtest/bios-tables-test-allowed-diff.h | 2 ++ tests/qtest/bios-tables-test.c | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..a88198d5c2 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,3 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/APIC.its_off", +"tests/data/acpi/aarch64/virt/IORT.its_off", diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 0b2bdf9d0d..4dbc07ec5e 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -2146,6 +2146,25 @@ static void test_acpi_aarch64_virt_tcg_topology(void) free_test_data(&data); } +static void test_acpi_aarch64_virt_tcg_its_off(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .variant = ".its_off", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * 1024 * 1024, + }; + + test_acpi_one("-cpu cortex-a57 " + "-M gic-version=3,iommu=smmuv3,its=off", &data); + free_test_data(&data); +} + static void test_acpi_q35_viot(void) { test_data data = { @@ -2577,6 +2596,8 @@ int main(int argc, char *argv[]) test_acpi_aarch64_virt_tcg_acpi_hmat); qtest_add_func("acpi/virt/topology", test_acpi_aarch64_virt_tcg_topology); + qtest_add_func("acpi/virt/its_off", + test_acpi_aarch64_virt_tcg_its_off); qtest_add_func("acpi/virt/numamem", test_acpi_aarch64_virt_tcg_numamem); qtest_add_func("acpi/virt/memhp", test_acpi_aarch64_virt_tcg_memhp); From 50b5fd232ebc1901b2e040a8394f50a63e3f24ec Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 1 Jul 2025 15:08:31 +0100 Subject: [PATCH 1745/2760] qtest/bios-tables-test: Add blobs for its=off test on aarch64 Add blobs for test_acpi_aarch64_virt_tcg_its_off(), which introduces a new variant, .its_off, that requires variations of the MADT and IORT tables. MADT (aka APIC) diff: +[000h 0000 4] Signature : "APIC" [Multiple APIC Description Table (MADT)] +[004h 0004 4] Table Length : 000000B8 +[008h 0008 1] Revision : 04 +[009h 0009 1] Checksum : C1 +[00Ah 0010 6] Oem ID : "BOCHS " +[010h 0016 8] Oem Table ID : "BXPC " +[018h 0024 4] Oem Revision : 00000001 +[01Ch 0028 4] Asl Compiler ID : "BXPC" +[020h 0032 4] Asl Compiler Revision : 00000001 + +[024h 0036 4] Local Apic Address : 00000000 +[028h 0040 4] Flags (decoded below) : 00000000 + PC-AT Compatibility : 0 + +[02Ch 0044 1] Subtable Type : 0C [Generic Interrupt Distributor] +[02Dh 0045 1] Length : 18 +[02Eh 0046 2] Reserved : 0000 +[030h 0048 4] Local GIC Hardware ID : 00000000 +[034h 0052 8] Base Address : 0000000008000000 +[03Ch 0060 4] Interrupt Base : 00000000 +[040h 0064 1] Version : 03 +[041h 0065 3] Reserved : 000000 + +[044h 0068 1] Subtable Type : 0B [Generic Interrupt Controller] +[045h 0069 1] Length : 50 +[046h 0070 2] Reserved : 0000 +[048h 0072 4] CPU Interface Number : 00000000 +[04Ch 0076 4] Processor UID : 00000000 +[050h 0080 4] Flags (decoded below) : 00000001 + Processor Enabled : 1 + Performance Interrupt Trigger Mode : 0 + Virtual GIC Interrupt Trigger Mode : 0 +[054h 0084 4] Parking Protocol Version : 00000000 +[058h 0088 4] Performance Interrupt : 00000017 +[05Ch 0092 8] Parked Address : 0000000000000000 +[064h 0100 8] Base Address : 0000000000000000 +[06Ch 0108 8] Virtual GIC Base Address : 0000000000000000 +[074h 0116 8] Hypervisor GIC Base Address : 0000000000000000 +[07Ch 0124 4] Virtual GIC Interrupt : 00000000 +[080h 0128 8] Redistributor Base Address : 0000000000000000 +[088h 0136 8] ARM MPIDR : 0000000000000000 +[090h 0144 1] Efficiency Class : 00 +[091h 0145 1] Reserved : 00 +[092h 0146 2] SPE Overflow Interrupt : 0000 + +[094h 0148 1] Subtable Type : 0E [Generic Interrupt Redistributor] +[095h 0149 1] Length : 10 +[096h 0150 2] Reserved : 0000 +[098h 0152 8] Base Address : 00000000080A0000 +[0A0h 0160 4] Length : 00F60000 + +[0A4h 0164 1] Subtable Type : 0F [Generic Interrupt Translator] +[0A5h 0165 1] Length : 14 +[0A6h 0166 2] Reserved : 0000 +[0A8h 0168 4] Translation ID : 00000000 +[0ACh 0172 8] Base Address : 0000000008080000 +[0B4h 0180 4] Reserved : 00000000 IORT diff: +[000h 0000 4] Signature : "IORT" [IO Remapping Table] +[004h 0004 4] Table Length : 000000EC +[008h 0008 1] Revision : 03 +[009h 0009 1] Checksum : 57 +[00Ah 0010 6] Oem ID : "BOCHS " +[010h 0016 8] Oem Table ID : "BXPC " +[018h 0024 4] Oem Revision : 00000001 +[01Ch 0028 4] Asl Compiler ID : "BXPC" +[020h 0032 4] Asl Compiler Revision : 00000001 + +[024h 0036 4] Node Count : 00000003 +[028h 0040 4] Node Offset : 00000030 +[02Ch 0044 4] Reserved : 00000000 + +[030h 0048 1] Type : 00 +[031h 0049 2] Length : 0018 +[033h 0051 1] Revision : 01 +[034h 0052 4] Reserved : 00000000 +[038h 0056 4] Mapping Count : 00000000 +[03Ch 0060 4] Mapping Offset : 00000000 + +[040h 0064 4] ItsCount : 00000001 +[044h 0068 4] Identifiers : 00000000 + +[048h 0072 1] Type : 04 +[049h 0073 2] Length : 0058 +[04Bh 0075 1] Revision : 04 +[04Ch 0076 4] Reserved : 00000001 +[050h 0080 4] Mapping Count : 00000001 +[054h 0084 4] Mapping Offset : 00000044 + +[058h 0088 8] Base Address : 0000000009050000 +[060h 0096 4] Flags (decoded below) : 00000001 + COHACC Override : 1 + HTTU Override : 0 + Proximity Domain Valid : 0 +[064h 0100 4] Reserved : 00000000 +[068h 0104 8] VATOS Address : 0000000000000000 +[070h 0112 4] Model : 00000000 +[074h 0116 4] Event GSIV : 0000006A +[078h 0120 4] PRI GSIV : 0000006B +[07Ch 0124 4] GERR GSIV : 0000006D +[080h 0128 4] Sync GSIV : 0000006C +[084h 0132 4] Proximity Domain : 00000000 +[088h 0136 4] Device ID Mapping Index : 00000000 + +[08Ch 0140 4] Input base : 00000000 +[090h 0144 4] ID Count : 0000FFFF +[094h 0148 4] Output Base : 00000000 +[098h 0152 4] Output Reference : 00000030 +[09Ch 0156 4] Flags (decoded below) : 00000000 + Single Mapping : 0 + +[0A0h 0160 1] Type : 02 +[0A1h 0161 2] Length : 004C +[0A3h 0163 1] Revision : 03 +[0A4h 0164 4] Reserved : 00000002 +[0A8h 0168 4] Mapping Count : 00000002 +[0ACh 0172 4] Mapping Offset : 00000024 + +[0B0h 0176 8] Memory Properties : [IORT Memory Access Properties] +[0B0h 0176 4] Cache Coherency : 00000001 +[0B4h 0180 1] Hints (decoded below) : 00 + Transient : 0 + Write Allocate : 0 + Read Allocate : 0 + Override : 0 +[0B5h 0181 2] Reserved : 0000 +[0B7h 0183 1] Memory Flags (decoded below) : 03 + Coherency : 1 + Device Attribute : 1 +[0B8h 0184 4] ATS Attribute : 00000000 +[0BCh 0188 4] PCI Segment Number : 00000000 +[0C0h 0192 1] Memory Size Limit : 40 +[0C1h 0193 3] Reserved : 000000 + +[0C4h 0196 4] Input base : 00000000 +[0C8h 0200 4] ID Count : 000000FF +[0CCh 0204 4] Output Base : 00000000 +[0D0h 0208 4] Output Reference : 00000048 +[0D4h 0212 4] Flags (decoded below) : 00000000 + Single Mapping : 0 + +[0D8h 0216 4] Input base : 00000100 +[0DCh 0220 4] ID Count : 0000FEFF +[0E0h 0224 4] Output Base : 00000100 +[0E4h 0228 4] Output Reference : 00000030 +[0E8h 0232 4] Flags (decoded below) : 00000000 + Single Mapping : 0 Signed-off-by: Gustavo Romero Reviewed-by: Eric Auger Message-id: 20250628195722.977078-8-gustavo.romero@linaro.org Signed-off-by: Peter Maydell --- tests/data/acpi/aarch64/virt/APIC.its_off | Bin 0 -> 184 bytes tests/data/acpi/aarch64/virt/IORT.its_off | Bin 0 -> 236 bytes tests/qtest/bios-tables-test-allowed-diff.h | 2 -- 3 files changed, 2 deletions(-) create mode 100644 tests/data/acpi/aarch64/virt/APIC.its_off create mode 100644 tests/data/acpi/aarch64/virt/IORT.its_off diff --git a/tests/data/acpi/aarch64/virt/APIC.its_off b/tests/data/acpi/aarch64/virt/APIC.its_off new file mode 100644 index 0000000000000000000000000000000000000000..37d82e970b1331cb5b259f0bd2d3654bacb2d623 GIT binary patch literal 184 zcmZ<^@O0k6z`($A(8=G~BUr&HBEVSz2pEB4AU24G0Uik$i-7~iVg@p}17JJ`2AFzr Zgb>LrJ^_#xE~p*f82CkCMsUFG1ppOZ2>}2A literal 0 HcmV?d00001 diff --git a/tests/data/acpi/aarch64/virt/IORT.its_off b/tests/data/acpi/aarch64/virt/IORT.its_off new file mode 100644 index 0000000000000000000000000000000000000000..0fceb820d509e852ca0849baf568a8e93e426738 GIT binary patch literal 236 zcmebD4+?q1z`(#9?&R<65v<@85#X!<1dKp25F11@1F-=RgMkDCNC*yK9F_TjFfcO#g+N#Zh@s|zoCF3AP#UU@ R!2`+%Dg6Hr$N|zYvjDIZ5CH%H literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index a88198d5c2..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,3 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/aarch64/virt/APIC.its_off", -"tests/data/acpi/aarch64/virt/IORT.its_off", From d6afe18b7242111a71916a962fbd1876a77dc755 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 1 Jul 2025 15:08:31 +0100 Subject: [PATCH 1746/2760] hw/arm/virt-acpi-build: Fix ACPI IORT and MADT tables when its=off MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the ITS Group nodes in the IORT table and the GIC ITS Struct in the MADT table are always generated, even if GIC ITS is not available on the machine. This commit fixes it by not generating the ITS Group nodes, not mapping any other node to them, and not advertising the GIC ITS in the MADT table, when GIC ITS is not available on the machine. Since the fix changes the MADT and IORT tables, add the blobs for the "its=off" test to the allow list and update them in the next commit. This commit also renames the smmu_idmaps and its_idmaps variables in build_iort() to rc_smmu_idmaps and rc_its_idmaps, respectively, to make it clearer which nodes are involved in the mappings associated with these variables. Reported-by: Udo Steinberg Signed-off-by: Gustavo Romero Message-id: 20250628195722.977078-9-gustavo.romero@linaro.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2886 Signed-off-by: Gustavo Romero Co-authored-by: Philippe Mathieu-Daudé [PMM: wrapped an overlong comment] Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 142 ++++++++++++-------- tests/qtest/bios-tables-test-allowed-diff.h | 2 + 2 files changed, 90 insertions(+), 54 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 5886192fe3..cd90c47976 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -267,7 +267,7 @@ static int iort_idmap_compare(gconstpointer a, gconstpointer b) } /* Compute ID ranges (RIDs) from RC that are directed to the ITS Group node */ -static void create_its_idmaps(GArray *its_idmaps, GArray *smmu_idmaps) +static void create_rc_its_idmaps(GArray *its_idmaps, GArray *smmu_idmaps) { AcpiIortIdMapping *idmap; AcpiIortIdMapping next_range = {0}; @@ -314,8 +314,8 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) int i, nb_nodes, rc_mapping_count; size_t node_size, smmu_offset = 0; uint32_t id = 0; - GArray *smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); - GArray *its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); + GArray *rc_smmu_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); + GArray *rc_its_idmaps = g_array_new(false, true, sizeof(AcpiIortIdMapping)); AcpiTable table = { .sig = "IORT", .rev = 3, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; @@ -324,22 +324,38 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) if (vms->iommu == VIRT_IOMMU_SMMUV3) { object_child_foreach_recursive(object_get_root(), - iort_host_bridges, smmu_idmaps); + iort_host_bridges, rc_smmu_idmaps); /* Sort the smmu idmap by input_base */ - g_array_sort(smmu_idmaps, iort_idmap_compare); + g_array_sort(rc_smmu_idmaps, iort_idmap_compare); /* * Knowing the ID ranges from the RC to the SMMU, it's possible to * determine the ID ranges from RC that are directed to the ITS. */ - create_its_idmaps(its_idmaps, smmu_idmaps); + create_rc_its_idmaps(rc_its_idmaps, rc_smmu_idmaps); - nb_nodes = 3; /* RC, ITS, SMMUv3 */ - rc_mapping_count = smmu_idmaps->len + its_idmaps->len; + nb_nodes = 2; /* RC and SMMUv3 */ + rc_mapping_count = rc_smmu_idmaps->len; + + if (vms->its) { + /* + * Knowing the ID ranges from the RC to the SMMU, it's possible to + * determine the ID ranges from RC that go directly to ITS. + */ + create_rc_its_idmaps(rc_its_idmaps, rc_smmu_idmaps); + + nb_nodes++; /* ITS */ + rc_mapping_count += rc_its_idmaps->len; + } } else { - nb_nodes = 2; /* RC, ITS */ - rc_mapping_count = 1; + if (vms->its) { + nb_nodes = 2; /* RC and ITS */ + rc_mapping_count = 1; /* Direct map to ITS */ + } else { + nb_nodes = 1; /* RC only */ + rc_mapping_count = 0; /* No output mapping */ + } } /* Number of IORT Nodes */ build_append_int_noprefix(table_data, nb_nodes, 4); @@ -348,31 +364,43 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, IORT_NODE_OFFSET, 4); build_append_int_noprefix(table_data, 0, 4); /* Reserved */ - /* Table 12 ITS Group Format */ - build_append_int_noprefix(table_data, 0 /* ITS Group */, 1); /* Type */ - node_size = 20 /* fixed header size */ + 4 /* 1 GIC ITS Identifier */; - build_append_int_noprefix(table_data, node_size, 2); /* Length */ - build_append_int_noprefix(table_data, 1, 1); /* Revision */ - build_append_int_noprefix(table_data, id++, 4); /* Identifier */ - build_append_int_noprefix(table_data, 0, 4); /* Number of ID mappings */ - build_append_int_noprefix(table_data, 0, 4); /* Reference to ID Array */ - build_append_int_noprefix(table_data, 1, 4); /* Number of ITSs */ - /* GIC ITS Identifier Array */ - build_append_int_noprefix(table_data, 0 /* MADT translation_id */, 4); + if (vms->its) { + /* Table 12 ITS Group Format */ + build_append_int_noprefix(table_data, 0 /* ITS Group */, 1); /* Type */ + node_size = 20 /* fixed header size */ + 4 /* 1 GIC ITS Identifier */; + build_append_int_noprefix(table_data, node_size, 2); /* Length */ + build_append_int_noprefix(table_data, 1, 1); /* Revision */ + build_append_int_noprefix(table_data, id++, 4); /* Identifier */ + build_append_int_noprefix(table_data, 0, 4); /* Number of ID mappings */ + build_append_int_noprefix(table_data, 0, 4); /* Reference to ID Array */ + build_append_int_noprefix(table_data, 1, 4); /* Number of ITSs */ + /* GIC ITS Identifier Array */ + build_append_int_noprefix(table_data, 0 /* MADT translation_id */, 4); + } if (vms->iommu == VIRT_IOMMU_SMMUV3) { int irq = vms->irqmap[VIRT_SMMU] + ARM_SPI_BASE; - + int smmu_mapping_count, offset_to_id_array; + + if (vms->its) { + smmu_mapping_count = 1; /* ITS Group node */ + offset_to_id_array = SMMU_V3_ENTRY_SIZE; /* Just after the header */ + } else { + smmu_mapping_count = 0; /* No ID mappings */ + offset_to_id_array = 0; /* No ID mappings array */ + } smmu_offset = table_data->len - table.table_offset; /* Table 9 SMMUv3 Format */ build_append_int_noprefix(table_data, 4 /* SMMUv3 */, 1); /* Type */ - node_size = SMMU_V3_ENTRY_SIZE + ID_MAPPING_ENTRY_SIZE; + node_size = SMMU_V3_ENTRY_SIZE + + (ID_MAPPING_ENTRY_SIZE * smmu_mapping_count); build_append_int_noprefix(table_data, node_size, 2); /* Length */ build_append_int_noprefix(table_data, 4, 1); /* Revision */ build_append_int_noprefix(table_data, id++, 4); /* Identifier */ - build_append_int_noprefix(table_data, 1, 4); /* Number of ID mappings */ + /* Number of ID mappings */ + build_append_int_noprefix(table_data, smmu_mapping_count, 4); /* Reference to ID Array */ - build_append_int_noprefix(table_data, SMMU_V3_ENTRY_SIZE, 4); + build_append_int_noprefix(table_data, offset_to_id_array, 4); /* Base address */ build_append_int_noprefix(table_data, vms->memmap[VIRT_SMMU].base, 8); /* Flags */ @@ -388,9 +416,11 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_int_noprefix(table_data, 0, 4); /* Proximity domain */ /* DeviceID mapping index (ignored since interrupts are GSIV based) */ build_append_int_noprefix(table_data, 0, 4); - - /* Output IORT node is the ITS Group node (the first node) */ - build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET); + /* Array of ID mappings */ + if (smmu_mapping_count) { + /* Output IORT node is the ITS Group node (the first node). */ + build_iort_id_mapping(table_data, 0, 0x10000, IORT_NODE_OFFSET); + } } /* Table 17 Root Complex Node */ @@ -431,24 +461,26 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) * * N.B.: The mapping from SMMUv3 to ITS Group node (SMMUv3 -> ITS) is * defined in the SMMUv3 table, where all SMMUv3 IDs are mapped to the - * ITS Group node. + * ITS Group node, if ITS is available. */ - for (i = 0; i < smmu_idmaps->len; i++) { - range = &g_array_index(smmu_idmaps, AcpiIortIdMapping, i); + for (i = 0; i < rc_smmu_idmaps->len; i++) { + range = &g_array_index(rc_smmu_idmaps, AcpiIortIdMapping, i); /* Output IORT node is the SMMUv3 node. */ build_iort_id_mapping(table_data, range->input_base, range->id_count, smmu_offset); } - /* - * Map bypassed (don't go throught the SMMU) RIDs (input) to ITS Group - * node directly: RC -> ITS. - */ - for (i = 0; i < its_idmaps->len; i++) { - range = &g_array_index(its_idmaps, AcpiIortIdMapping, i); - /* Output IORT node is the ITS Group node (the first node). */ - build_iort_id_mapping(table_data, range->input_base, - range->id_count, IORT_NODE_OFFSET); + if (vms->its) { + /* + * Map bypassed (don't go through the SMMU) RIDs (input) to + * ITS Group node directly: RC -> ITS. + */ + for (i = 0; i < rc_its_idmaps->len; i++) { + range = &g_array_index(rc_its_idmaps, AcpiIortIdMapping, i); + /* Output IORT node is the ITS Group node (the first node). */ + build_iort_id_mapping(table_data, range->input_base, + range->id_count, IORT_NODE_OFFSET); + } } } else { /* @@ -460,8 +492,8 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) } acpi_table_end(linker, &table); - g_array_free(smmu_idmaps, true); - g_array_free(its_idmaps, true); + g_array_free(rc_smmu_idmaps, true); + g_array_free(rc_its_idmaps, true); } /* @@ -769,18 +801,20 @@ build_madt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) memmap[VIRT_HIGH_GIC_REDIST2].size); } - /* - * ACPI spec, Revision 6.0 Errata A - * (original 6.0 definition has invalid Length) - * 5.2.12.18 GIC ITS Structure - */ - build_append_int_noprefix(table_data, 0xF, 1); /* Type */ - build_append_int_noprefix(table_data, 20, 1); /* Length */ - build_append_int_noprefix(table_data, 0, 2); /* Reserved */ - build_append_int_noprefix(table_data, 0, 4); /* GIC ITS ID */ - /* Physical Base Address */ - build_append_int_noprefix(table_data, memmap[VIRT_GIC_ITS].base, 8); - build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + if (vms->its) { + /* + * ACPI spec, Revision 6.0 Errata A + * (original 6.0 definition has invalid Length) + * 5.2.12.18 GIC ITS Structure + */ + build_append_int_noprefix(table_data, 0xF, 1); /* Type */ + build_append_int_noprefix(table_data, 20, 1); /* Length */ + build_append_int_noprefix(table_data, 0, 2); /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); /* GIC ITS ID */ + /* Physical Base Address */ + build_append_int_noprefix(table_data, memmap[VIRT_GIC_ITS].base, 8); + build_append_int_noprefix(table_data, 0, 4); /* Reserved */ + } } else { const uint16_t spi_base = vms->irqmap[VIRT_GIC_V2M] + ARM_SPI_BASE; diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..a88198d5c2 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,3 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/APIC.its_off", +"tests/data/acpi/aarch64/virt/IORT.its_off", From 96791e69e644669bbc969424f0f31f0b6f522403 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Tue, 1 Jul 2025 15:08:31 +0100 Subject: [PATCH 1747/2760] qtest/bios-tables-test: Update blobs for its=off test on aarch64 Update blobs for the its=off test on aarch64 after fix. Basically, all structs related to ITS are gone in MADT and IORT tables after the fix (previously ITS was not properly disabled when "its=off" option was passed to the machine). MADT diff: [000h 0000 4] Signature : "APIC" [Multiple APIC Description Table (MADT)] -[004h 0004 4] Table Length : 000000B8 +[004h 0004 4] Table Length : 000000A4 [008h 0008 1] Revision : 04 -[009h 0009 1] Checksum : C1 +[009h 0009 1] Checksum : 08 [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 [024h 0036 4] Local Apic Address : 00000000 [028h 0040 4] Flags (decoded below) : 00000000 PC-AT Compatibility : 0 [02Ch 0044 1] Subtable Type : 0C [Generic Interrupt Distributor] [02Dh 0045 1] Length : 18 [02Eh 0046 2] Reserved : 0000 [030h 0048 4] Local GIC Hardware ID : 00000000 [034h 0052 8] Base Address : 0000000008000000 [03Ch 0060 4] Interrupt Base : 00000000 @@ -48,37 +48,29 @@ [064h 0100 8] Base Address : 0000000000000000 [06Ch 0108 8] Virtual GIC Base Address : 0000000000000000 [074h 0116 8] Hypervisor GIC Base Address : 0000000000000000 [07Ch 0124 4] Virtual GIC Interrupt : 00000000 [080h 0128 8] Redistributor Base Address : 0000000000000000 [088h 0136 8] ARM MPIDR : 0000000000000000 [090h 0144 1] Efficiency Class : 00 [091h 0145 1] Reserved : 00 [092h 0146 2] SPE Overflow Interrupt : 0000 [094h 0148 1] Subtable Type : 0E [Generic Interrupt Redistributor] [095h 0149 1] Length : 10 [096h 0150 2] Reserved : 0000 [098h 0152 8] Base Address : 00000000080A0000 [0A0h 0160 4] Length : 00F60000 -[0A4h 0164 1] Subtable Type : 0F [Generic Interrupt Translator] -[0A5h 0165 1] Length : 14 -[0A6h 0166 2] Reserved : 0000 -[0A8h 0168 4] Translation ID : 00000000 -[0ACh 0172 8] Base Address : 0000000008080000 -[0B4h 0180 4] Reserved : 00000000 IORT diff: [000h 0000 4] Signature : "IORT" [IO Remapping Table] -[004h 0004 4] Table Length : 000000EC +[004h 0004 4] Table Length : 000000AC [008h 0008 1] Revision : 03 -[009h 0009 1] Checksum : 57 +[009h 0009 1] Checksum : 97 [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 -[024h 0036 4] Node Count : 00000003 +[024h 0036 4] Node Count : 00000002 [028h 0040 4] Node Offset : 00000030 [02Ch 0044 4] Reserved : 00000000 -[030h 0048 1] Type : 00 -[031h 0049 2] Length : 0018 -[033h 0051 1] Revision : 01 +[030h 0048 1] Type : 04 +[031h 0049 2] Length : 0044 +[033h 0051 1] Revision : 04 [034h 0052 4] Reserved : 00000000 [038h 0056 4] Mapping Count : 00000000 [03Ch 0060 4] Mapping Offset : 00000000 -[040h 0064 4] ItsCount : 00000001 -[044h 0068 4] Identifiers : 00000000 - -[048h 0072 1] Type : 04 -[049h 0073 2] Length : 0058 -[04Bh 0075 1] Revision : 04 -[04Ch 0076 4] Reserved : 00000001 -[050h 0080 4] Mapping Count : 00000001 -[054h 0084 4] Mapping Offset : 00000044 - -[058h 0088 8] Base Address : 0000000009050000 -[060h 0096 4] Flags (decoded below) : 00000001 +[040h 0064 8] Base Address : 0000000009050000 +[048h 0072 4] Flags (decoded below) : 00000001 COHACC Override : 1 HTTU Override : 0 Proximity Domain Valid : 0 -[064h 0100 4] Reserved : 00000000 -[068h 0104 8] VATOS Address : 0000000000000000 -[070h 0112 4] Model : 00000000 -[074h 0116 4] Event GSIV : 0000006A -[078h 0120 4] PRI GSIV : 0000006B -[07Ch 0124 4] GERR GSIV : 0000006D -[080h 0128 4] Sync GSIV : 0000006C -[084h 0132 4] Proximity Domain : 00000000 -[088h 0136 4] Device ID Mapping Index : 00000000 - -[08Ch 0140 4] Input base : 00000000 -[090h 0144 4] ID Count : 0000FFFF -[094h 0148 4] Output Base : 00000000 -[098h 0152 4] Output Reference : 00000030 -[09Ch 0156 4] Flags (decoded below) : 00000000 - Single Mapping : 0 - -[0A0h 0160 1] Type : 02 -[0A1h 0161 2] Length : 004C -[0A3h 0163 1] Revision : 03 -[0A4h 0164 4] Reserved : 00000002 -[0A8h 0168 4] Mapping Count : 00000002 -[0ACh 0172 4] Mapping Offset : 00000024 - -[0B0h 0176 8] Memory Properties : [IORT Memory Access Properties] -[0B0h 0176 4] Cache Coherency : 00000001 -[0B4h 0180 1] Hints (decoded below) : 00 +[04Ch 0076 4] Reserved : 00000000 +[050h 0080 8] VATOS Address : 0000000000000000 +[058h 0088 4] Model : 00000000 +[05Ch 0092 4] Event GSIV : 0000006A +[060h 0096 4] PRI GSIV : 0000006B +[064h 0100 4] GERR GSIV : 0000006D +[068h 0104 4] Sync GSIV : 0000006C +[06Ch 0108 4] Proximity Domain : 00000000 +[070h 0112 4] Device ID Mapping Index : 00000000 + +[074h 0116 1] Type : 02 +[075h 0117 2] Length : 0038 +[077h 0119 1] Revision : 03 +[078h 0120 4] Reserved : 00000001 +[07Ch 0124 4] Mapping Count : 00000001 +[080h 0128 4] Mapping Offset : 00000024 + +[084h 0132 8] Memory Properties : [IORT Memory Access Properties] +[084h 0132 4] Cache Coherency : 00000001 +[088h 0136 1] Hints (decoded below) : 00 Transient : 0 Write Allocate : 0 Read Allocate : 0 Override : 0 -[0B5h 0181 2] Reserved : 0000 -[0B7h 0183 1] Memory Flags (decoded below) : 03 +[089h 0137 2] Reserved : 0000 +[08Bh 0139 1] Memory Flags (decoded below) : 03 Coherency : 1 Device Attribute : 1 -[0B8h 0184 4] ATS Attribute : 00000000 -[0BCh 0188 4] PCI Segment Number : 00000000 -[0C0h 0192 1] Memory Size Limit : 40 -[0C1h 0193 3] Reserved : 000000 - -[0C4h 0196 4] Input base : 00000000 -[0C8h 0200 4] ID Count : 000000FF -[0CCh 0204 4] Output Base : 00000000 -[0D0h 0208 4] Output Reference : 00000048 -[0D4h 0212 4] Flags (decoded below) : 00000000 - Single Mapping : 0 - -[0D8h 0216 4] Input base : 00000100 -[0DCh 0220 4] ID Count : 0000FEFF -[0E0h 0224 4] Output Base : 00000100 -[0E4h 0228 4] Output Reference : 00000030 -[0E8h 0232 4] Flags (decoded below) : 00000000 +[08Ch 0140 4] ATS Attribute : 00000000 +[090h 0144 4] PCI Segment Number : 00000000 +[094h 0148 1] Memory Size Limit : 40 +[095h 0149 3] Reserved : 000000 + +[098h 0152 4] Input base : 00000000 +[09Ch 0156 4] ID Count : 000000FF +[0A0h 0160 4] Output Base : 00000000 +[0A4h 0164 4] Output Reference : 00000030 +[0A8h 0168 4] Flags (decoded below) : 00000000 Single Mapping : 0 Signed-off-by: Gustavo Romero Reviewed-by: Eric Auger Message-id: 20250628195722.977078-10-gustavo.romero@linaro.org Signed-off-by: Peter Maydell --- tests/data/acpi/aarch64/virt/APIC.its_off | Bin 184 -> 164 bytes tests/data/acpi/aarch64/virt/IORT.its_off | Bin 236 -> 172 bytes tests/qtest/bios-tables-test-allowed-diff.h | 2 -- 3 files changed, 2 deletions(-) diff --git a/tests/data/acpi/aarch64/virt/APIC.its_off b/tests/data/acpi/aarch64/virt/APIC.its_off index 37d82e970b1331cb5b259f0bd2d3654bacb2d623..6130cb7d07103b326feb4dcd7034f85808bebadf 100644 GIT binary patch delta 18 ZcmdnNxP+0*F~HM#2?GNI3&%vRSpY2+1Zw~Q delta 39 jcmZ3&xPy_)F~HM#2Ll5G%fX3UvqbnsfJ`vp;DE6JqX7kf diff --git a/tests/data/acpi/aarch64/virt/IORT.its_off b/tests/data/acpi/aarch64/virt/IORT.its_off index 0fceb820d509e852ca0849baf568a8e93e426738..c10da4e61dd00e7eb062558a2735d49ca0b20620 100644 GIT binary patch delta 69 zcmaFExQ3C-(?2L=4FdxM^Yn>aQj$zSmH`lh0E-I)3xowECx)7HGFdP%GXmL+6IZHp Hz*GSMclZc% literal 236 zcmebD4+?q1z`(#9?&R<65v<@85#X!<1dKp25F11@1F-=RgMkDCNC*yK9F_TjFfcO#g+N#Zh@s|zoCF3AP#UU@ R!2`+%Dg6Hr$N|zYvjDIZ5CH%H diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index a88198d5c2..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,3 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/aarch64/virt/APIC.its_off", -"tests/data/acpi/aarch64/virt/IORT.its_off", From a5b94f6c7b3a6ae583ba07098bff810049262413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:31 +0100 Subject: [PATCH 1748/2760] target/arm: Remove arm_handle_psci_call() stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 0c1aaa66c24 ("target/arm: wrap psci call with tcg_enabled") the arm_handle_psci_call() call is elided when TCG is disabled. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-2-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/internals.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index 6216f68c94..21a8d67edd 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -650,16 +650,12 @@ static inline bool arm_is_psci_call(ARMCPU *cpu, int excp_type) { return false; } -static inline void arm_handle_psci_call(ARMCPU *cpu) -{ - g_assert_not_reached(); -} #else /* Return true if the r0/x0 value indicates that this SMC/HVC is a PSCI call. */ bool arm_is_psci_call(ARMCPU *cpu, int excp_type); +#endif /* Actually handle a PSCI call */ void arm_handle_psci_call(ARMCPU *cpu); -#endif /** * arm_clear_exclusive: clear the exclusive monitor From 4a94d8047d9dca2d7e73bbc863fabbea2df1bab6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:32 +0100 Subject: [PATCH 1749/2760] target/arm: Reduce arm_cpu_post_init() declaration scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit arm_cpu_post_init() is only used within the same file unit. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-3-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 2 +- target/arm/cpu.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 8e77414c2b..7030540f91 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1713,7 +1713,7 @@ static void arm_cpu_propagate_feature_implications(ARMCPU *cpu) } } -void arm_cpu_post_init(Object *obj) +static void arm_cpu_post_init(Object *obj) { ARMCPU *cpu = ARM_CPU(obj); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 8744922330..0338153923 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1168,8 +1168,6 @@ void arm_gt_sel2vtimer_cb(void *opaque); unsigned int gt_cntfrq_period_ns(ARMCPU *cpu); void gt_rme_post_el_change(ARMCPU *cpu, void *opaque); -void arm_cpu_post_init(Object *obj); - #define ARM_AFF0_SHIFT 0 #define ARM_AFF0_MASK (0xFFULL << ARM_AFF0_SHIFT) #define ARM_AFF1_SHIFT 8 From a74b63b3a4451bec2f0d77fc101cf066d1e0fdb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:32 +0100 Subject: [PATCH 1750/2760] target/arm: Unify gen_exception_internal() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Same code, use the generic variant. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-4-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 6 ------ target/arm/tcg/translate.c | 2 +- target/arm/tcg/translate.h | 1 + 3 files changed, 2 insertions(+), 7 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d0719b5665..815225b130 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -433,12 +433,6 @@ static void gen_rebuild_hflags(DisasContext *s) gen_helper_rebuild_hflags_a64(tcg_env, tcg_constant_i32(s->current_el)); } -static void gen_exception_internal(int excp) -{ - assert(excp_is_internal(excp)); - gen_helper_exception_internal(tcg_env, tcg_constant_i32(excp)); -} - static void gen_exception_internal_insn(DisasContext *s, int excp) { gen_a64_update_pc(s, 0); diff --git a/target/arm/tcg/translate.c b/target/arm/tcg/translate.c index 9962f43b1d..f7d6d8ce19 100644 --- a/target/arm/tcg/translate.c +++ b/target/arm/tcg/translate.c @@ -372,7 +372,7 @@ static void gen_rebuild_hflags(DisasContext *s, bool new_el) } } -static void gen_exception_internal(int excp) +void gen_exception_internal(int excp) { assert(excp_is_internal(excp)); gen_helper_exception_internal(tcg_env, tcg_constant_i32(excp)); diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 1bfdb0fb9b..0004a97219 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -347,6 +347,7 @@ void arm_jump_cc(DisasCompare *cmp, TCGLabel *label); void arm_gen_test_cc(int cc, TCGLabel *label); MemOp pow2_align(unsigned i); void unallocated_encoding(DisasContext *s); +void gen_exception_internal(int excp); void gen_exception_insn_el(DisasContext *s, target_long pc_diff, int excp, uint32_t syn, uint32_t target_el); void gen_exception_insn(DisasContext *s, target_long pc_diff, From 30484a6635161e8bd62014542c5fd15bbf14834f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:32 +0100 Subject: [PATCH 1751/2760] target/arm/hvf: Directly re-lock BQL after hv_vcpu_run() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep bql_unlock() / bql_lock() close. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-id: 20250623121845.7214-6-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 5c95ccc5b8..3c234f7b11 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1915,7 +1915,9 @@ int hvf_vcpu_exec(CPUState *cpu) flush_cpu_state(cpu); bql_unlock(); - assert_hvf_ok(hv_vcpu_run(cpu->accel->fd)); + r = hv_vcpu_run(cpu->accel->fd); + bql_lock(); + assert_hvf_ok(r); /* handle VMEXIT */ uint64_t exit_reason = hvf_exit->reason; @@ -1923,7 +1925,6 @@ int hvf_vcpu_exec(CPUState *cpu) uint32_t ec = syn_get_ec(syndrome); ret = 0; - bql_lock(); switch (exit_reason) { case HV_EXIT_REASON_EXCEPTION: /* This is the main one, handle below. */ From 4dcba9072be2c1e32f3fb676e97b098d5a9b66ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:32 +0100 Subject: [PATCH 1752/2760] target/arm/hvf: Trace hv_vcpu_run() failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow distinguishing HV_ILLEGAL_GUEST_STATE in trace events. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-7-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 10 +++++++++- target/arm/hvf/trace-events | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 3c234f7b11..0943365a68 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1917,7 +1917,15 @@ int hvf_vcpu_exec(CPUState *cpu) bql_unlock(); r = hv_vcpu_run(cpu->accel->fd); bql_lock(); - assert_hvf_ok(r); + switch (r) { + case HV_SUCCESS: + break; + case HV_ILLEGAL_GUEST_STATE: + trace_hvf_illegal_guest_state(); + /* fall through */ + default: + g_assert_not_reached(); + } /* handle VMEXIT */ uint64_t exit_reason = hvf_exit->reason; diff --git a/target/arm/hvf/trace-events b/target/arm/hvf/trace-events index 4fbbe4b45e..a4870e0a5c 100644 --- a/target/arm/hvf/trace-events +++ b/target/arm/hvf/trace-events @@ -11,3 +11,4 @@ hvf_exit(uint64_t syndrome, uint32_t ec, uint64_t pc) "exit: 0x%"PRIx64" [ec=0x% hvf_psci_call(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t cpuid) "PSCI Call x0=0x%016"PRIx64" x1=0x%016"PRIx64" x2=0x%016"PRIx64" x3=0x%016"PRIx64" cpu=0x%x" hvf_vgic_write(const char *name, uint64_t val) "vgic write to %s [val=0x%016"PRIx64"]" hvf_vgic_read(const char *name, uint64_t val) "vgic read from %s [val=0x%016"PRIx64"]" +hvf_illegal_guest_state(void) "HV_ILLEGAL_GUEST_STATE" From d17d5cc0d68cc1dbce4a9086ce1cb8c3aebb085c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:33 +0100 Subject: [PATCH 1753/2760] accel/hvf: Trace VM memory mapping MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Trace memory mapped / unmapped in the guest. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-8-philmd@linaro.org Signed-off-by: Peter Maydell --- accel/hvf/hvf-accel-ops.c | 6 ++++++ accel/hvf/trace-events | 7 +++++++ accel/hvf/trace.h | 2 ++ meson.build | 1 + 4 files changed, 16 insertions(+) create mode 100644 accel/hvf/trace-events create mode 100644 accel/hvf/trace.h diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index d60446b85b..b38977207d 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -59,6 +59,7 @@ #include "system/hvf_int.h" #include "system/runstate.h" #include "qemu/guest-random.h" +#include "trace.h" HVFState *hvf_state; @@ -97,6 +98,7 @@ static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags) if (macslot->present) { if (macslot->size != slot->size) { macslot->present = 0; + trace_hvf_vm_unmap(macslot->gpa_start, macslot->size); ret = hv_vm_unmap(macslot->gpa_start, macslot->size); assert_hvf_ok(ret); } @@ -109,6 +111,10 @@ static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags) macslot->present = 1; macslot->gpa_start = slot->start; macslot->size = slot->size; + trace_hvf_vm_map(slot->start, slot->size, slot->mem, flags, + flags & HV_MEMORY_READ ? 'R' : '-', + flags & HV_MEMORY_WRITE ? 'W' : '-', + flags & HV_MEMORY_EXEC ? 'E' : '-'); ret = hv_vm_map(slot->mem, slot->start, slot->size, flags); assert_hvf_ok(ret); return 0; diff --git a/accel/hvf/trace-events b/accel/hvf/trace-events new file mode 100644 index 0000000000..2fd3e127c7 --- /dev/null +++ b/accel/hvf/trace-events @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-or-later +# +# See docs/devel/tracing.rst for syntax documentation. + +# hvf-accel-ops.c +hvf_vm_map(uint64_t paddr, uint64_t size, void *vaddr, uint8_t flags, const char r, const char w, const char e) "paddr:0x%016"PRIx64" size:0x%08"PRIx64" vaddr:%p flags:0x%02x/%c%c%c" +hvf_vm_unmap(uint64_t paddr, uint64_t size) "paddr:0x%016"PRIx64" size:0x%08"PRIx64 diff --git a/accel/hvf/trace.h b/accel/hvf/trace.h new file mode 100644 index 0000000000..83a1883343 --- /dev/null +++ b/accel/hvf/trace.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "trace/trace-accel_hvf.h" diff --git a/meson.build b/meson.build index dbc97bfdf7..b5f74aa37a 100644 --- a/meson.build +++ b/meson.build @@ -3630,6 +3630,7 @@ if have_block endif if have_system trace_events_subdirs += [ + 'accel/hvf', 'accel/kvm', 'audio', 'backends', From 9097c0cd611d0f1924041c85b9aeab41ecbd1a9a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:33 +0100 Subject: [PATCH 1754/2760] target/arm/hvf: Log $pc in hvf_unknown_hvc() trace event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tracing $PC for unknown HVC instructions to not have to look at the disassembled flow of instructions. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-9-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 4 ++-- target/arm/hvf/trace-events | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 0943365a68..f36973a32e 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -2072,12 +2072,12 @@ int hvf_vcpu_exec(CPUState *cpu) cpu_synchronize_state(cpu); if (arm_cpu->psci_conduit == QEMU_PSCI_CONDUIT_HVC) { if (!hvf_handle_psci_call(cpu)) { - trace_hvf_unknown_hvc(env->xregs[0]); + trace_hvf_unknown_hvc(env->pc, env->xregs[0]); /* SMCCC 1.3 section 5.2 says every unknown SMCCC call returns -1 */ env->xregs[0] = -1; } } else { - trace_hvf_unknown_hvc(env->xregs[0]); + trace_hvf_unknown_hvc(env->pc, env->xregs[0]); hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); } break; diff --git a/target/arm/hvf/trace-events b/target/arm/hvf/trace-events index a4870e0a5c..b49746f28d 100644 --- a/target/arm/hvf/trace-events +++ b/target/arm/hvf/trace-events @@ -5,10 +5,10 @@ hvf_inject_irq(void) "injecting IRQ" hvf_data_abort(uint64_t pc, uint64_t va, uint64_t pa, bool isv, bool iswrite, bool s1ptw, uint32_t len, uint32_t srt) "data abort: [pc=0x%"PRIx64" va=0x%016"PRIx64" pa=0x%016"PRIx64" isv=%d iswrite=%d s1ptw=%d len=%d srt=%d]" hvf_sysreg_read(uint32_t reg, uint32_t op0, uint32_t op1, uint32_t crn, uint32_t crm, uint32_t op2, uint64_t val) "sysreg read 0x%08x (op0=%d op1=%d crn=%d crm=%d op2=%d) = 0x%016"PRIx64 hvf_sysreg_write(uint32_t reg, uint32_t op0, uint32_t op1, uint32_t crn, uint32_t crm, uint32_t op2, uint64_t val) "sysreg write 0x%08x (op0=%d op1=%d crn=%d crm=%d op2=%d, val=0x%016"PRIx64")" -hvf_unknown_hvc(uint64_t x0) "unknown HVC! 0x%016"PRIx64 +hvf_unknown_hvc(uint64_t pc, uint64_t x0) "pc=0x%"PRIx64" unknown HVC! 0x%016"PRIx64 hvf_unknown_smc(uint64_t x0) "unknown SMC! 0x%016"PRIx64 hvf_exit(uint64_t syndrome, uint32_t ec, uint64_t pc) "exit: 0x%"PRIx64" [ec=0x%x pc=0x%"PRIx64"]" -hvf_psci_call(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t cpuid) "PSCI Call x0=0x%016"PRIx64" x1=0x%016"PRIx64" x2=0x%016"PRIx64" x3=0x%016"PRIx64" cpu=0x%x" +hvf_psci_call(uint64_t x0, uint64_t x1, uint64_t x2, uint64_t x3, uint32_t cpuid) "PSCI Call x0=0x%016"PRIx64" x1=0x%016"PRIx64" x2=0x%016"PRIx64" x3=0x%016"PRIx64" cpuid=0x%x" hvf_vgic_write(const char *name, uint64_t val) "vgic write to %s [val=0x%016"PRIx64"]" hvf_vgic_read(const char *name, uint64_t val) "vgic read from %s [val=0x%016"PRIx64"]" hvf_illegal_guest_state(void) "HV_ILLEGAL_GUEST_STATE" From a412575837b6a46584fba891e3706e87bd09a3e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:33 +0100 Subject: [PATCH 1755/2760] target/arm: Correct KVM & HVF dtb_compatible value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Linux kernel knows how to parse "arm,armv8", not "arm,arm-v8". See arch/arm64/boot/dts/foundation-v8.dts: https://github.com/torvalds/linux/commit/90556ca1ebdd Cc: qemu-stable@nongnu.org Fixes: 26861c7ce06 ("target-arm: Add minimal KVM AArch64 support") Fixes: 585df85efea ("hvf: arm: Implement -cpu host") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-10-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 2 +- target/arm/kvm.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index f36973a32e..ebde4c6f18 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -883,7 +883,7 @@ static bool hvf_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) hv_vcpu_exit_t *exit; int i; - ahcf->dtb_compatible = "arm,arm-v8"; + ahcf->dtb_compatible = "arm,armv8"; ahcf->features = (1ULL << ARM_FEATURE_V8) | (1ULL << ARM_FEATURE_NEON) | (1ULL << ARM_FEATURE_AARCH64) | diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 70919aedd0..426f8b159e 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -289,7 +289,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) } ahcf->target = init.target; - ahcf->dtb_compatible = "arm,arm-v8"; + ahcf->dtb_compatible = "arm,armv8"; int fd = fdarray[2]; err = get_host_cpu_reg(fd, ahcf, ID_AA64PFR0_EL1_IDX); From 38d4089c048a055f36d21ef009b09612e60ac226 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:34 +0100 Subject: [PATCH 1756/2760] target/arm/hvf: Pass @target_el argument to hvf_raise_exception() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation of raising exceptions at EL2, add the 'target_el' argument to hvf_raise_exception(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-12-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index ebde4c6f18..7b6d291e79 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1088,13 +1088,13 @@ void hvf_kick_vcpu_thread(CPUState *cpu) } static void hvf_raise_exception(CPUState *cpu, uint32_t excp, - uint32_t syndrome) + uint32_t syndrome, int target_el) { ARMCPU *arm_cpu = ARM_CPU(cpu); CPUARMState *env = &arm_cpu->env; cpu->exception_index = excp; - env->exception.target_el = 1; + env->exception.target_el = target_el; env->exception.syndrome = syndrome; arm_cpu_do_interrupt(cpu); @@ -1454,7 +1454,7 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val) SYSREG_CRN(reg), SYSREG_CRM(reg), SYSREG_OP2(reg)); - hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized(), 1); return 1; } @@ -1764,7 +1764,7 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) SYSREG_CRN(reg), SYSREG_CRM(reg), SYSREG_OP2(reg)); - hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized(), 1); return 1; } @@ -1967,7 +1967,7 @@ int hvf_vcpu_exec(CPUState *cpu) if (!hvf_find_sw_breakpoint(cpu, env->pc)) { /* Re-inject into the guest */ ret = 0; - hvf_raise_exception(cpu, EXCP_BKPT, syn_aa64_bkpt(0)); + hvf_raise_exception(cpu, EXCP_BKPT, syn_aa64_bkpt(0), 1); } break; } @@ -2078,7 +2078,7 @@ int hvf_vcpu_exec(CPUState *cpu) } } else { trace_hvf_unknown_hvc(env->pc, env->xregs[0]); - hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized(), 1); } break; case EC_AA64_SMC: @@ -2093,7 +2093,7 @@ int hvf_vcpu_exec(CPUState *cpu) } } else { trace_hvf_unknown_smc(env->xregs[0]); - hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized()); + hvf_raise_exception(cpu, EXCP_UDEF, syn_uncategorized(), 1); } break; default: From 532884658d606160ff3ad73c73c05fddf3504d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:34 +0100 Subject: [PATCH 1757/2760] target/arm: Restrict system register properties to system binary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Do not expose the following system-specific properties on user-mode binaries: - psci-conduit - cntfrq (ARM_FEATURE_GENERIC_TIMER) - rvbar (ARM_FEATURE_V8) - has-mpu (ARM_FEATURE_PMSA) - pmsav7-dregion (ARM_FEATURE_PMSA) - reset-cbar (ARM_FEATURE_CBAR) - reset-hivecs (ARM_FEATURE_M) - init-nsvtor (ARM_FEATURE_M) - init-svtor (ARM_FEATURE_M_SECURITY) - idau (ARM_FEATURE_M_SECURITY) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-13-philmd@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 7030540f91..a59a5b57af 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1500,6 +1500,7 @@ static void arm_cpu_initfn(Object *obj) * 0 means "unset, use the default value". That default might vary depending * on the CPU type, and is set in the realize fn. */ +#ifndef CONFIG_USER_ONLY static const Property arm_cpu_gt_cntfrq_property = DEFINE_PROP_UINT64("cntfrq", ARMCPU, gt_cntfrq_hz, 0); @@ -1509,7 +1510,6 @@ static const Property arm_cpu_reset_cbar_property = static const Property arm_cpu_reset_hivecs_property = DEFINE_PROP_BOOL("reset-hivecs", ARMCPU, reset_hivecs, false); -#ifndef CONFIG_USER_ONLY static const Property arm_cpu_has_el2_property = DEFINE_PROP_BOOL("has_el2", ARMCPU, has_el2, true); @@ -1532,6 +1532,7 @@ static const Property arm_cpu_has_neon_property = static const Property arm_cpu_has_dsp_property = DEFINE_PROP_BOOL("dsp", ARMCPU, has_dsp, true); +#ifndef CONFIG_USER_ONLY static const Property arm_cpu_has_mpu_property = DEFINE_PROP_BOOL("has-mpu", ARMCPU, has_mpu, true); @@ -1544,6 +1545,7 @@ static const Property arm_cpu_pmsav7_dregion_property = DEFINE_PROP_UNSIGNED_NODEFAULT("pmsav7-dregion", ARMCPU, pmsav7_dregion, qdev_prop_uint32, uint32_t); +#endif static bool arm_get_pmu(Object *obj, Error **errp) { @@ -1731,6 +1733,7 @@ static void arm_cpu_post_init(Object *obj) "Set on/off to enable/disable aarch64 " "execution state "); } +#ifndef CONFIG_USER_ONLY if (arm_feature(&cpu->env, ARM_FEATURE_CBAR) || arm_feature(&cpu->env, ARM_FEATURE_CBAR_RO)) { qdev_property_add_static(DEVICE(obj), &arm_cpu_reset_cbar_property); @@ -1746,7 +1749,6 @@ static void arm_cpu_post_init(Object *obj) OBJ_PROP_FLAG_READWRITE); } -#ifndef CONFIG_USER_ONLY if (arm_feature(&cpu->env, ARM_FEATURE_EL3)) { /* Add the has_el3 state CPU property only if EL3 is allowed. This will * prevent "has_el3" from existing on CPUs which cannot support EL3. @@ -1818,6 +1820,7 @@ static void arm_cpu_post_init(Object *obj) qdev_property_add_static(DEVICE(obj), &arm_cpu_has_dsp_property); } +#ifndef CONFIG_USER_ONLY if (arm_feature(&cpu->env, ARM_FEATURE_PMSA)) { qdev_property_add_static(DEVICE(obj), &arm_cpu_has_mpu_property); if (arm_feature(&cpu->env, ARM_FEATURE_V7)) { @@ -1854,8 +1857,6 @@ static void arm_cpu_post_init(Object *obj) &cpu->psci_conduit, OBJ_PROP_FLAG_READWRITE); - qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property); - if (arm_feature(&cpu->env, ARM_FEATURE_GENERIC_TIMER)) { qdev_property_add_static(DEVICE(cpu), &arm_cpu_gt_cntfrq_property); } @@ -1864,7 +1865,6 @@ static void arm_cpu_post_init(Object *obj) kvm_arm_add_vcpu_properties(cpu); } -#ifndef CONFIG_USER_ONLY if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && cpu_isar_feature(aa64_mte, cpu)) { object_property_add_link(obj, "tag-memory", @@ -1882,6 +1882,7 @@ static void arm_cpu_post_init(Object *obj) } } #endif + qdev_property_add_static(DEVICE(obj), &arm_cpu_cfgend_property); } static void arm_cpu_finalizefn(Object *obj) From 1370fd56ec771f16197f255c25c910e9386ac587 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:34 +0100 Subject: [PATCH 1758/2760] hw/arm/virt: Make EL3-guest accel check an accept-list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently only the TCG and qtest accelerators can handle an EL3 guest. Instead of making the condition check be "fail if KVM or HVF" (an exclude-list), make it a be "allow if TCG or qtest" (an accept-list). This is better for if/when we add new accelerators, as it makes the default be that we forbid an EL3 guest. This is the most likely to be correct and also "fails safe"; if the new accelerator really can support EL3 guests then the implementor will see that they need to add it to the accept-list. Reported-by: Alex Bennée Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-19-philmd@linaro.org [PMM: rewrote commit message] Signed-off-by: Peter Maydell --- hw/arm/virt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index ae419e8671..b3b1c6df7f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2200,7 +2200,7 @@ static void machvirt_init(MachineState *machine) exit(1); } - if (vms->secure && (kvm_enabled() || hvf_enabled())) { + if (vms->secure && !tcg_enabled() && !qtest_enabled()) { error_report("mach-virt: %s does not support providing " "Security extensions (TrustZone) to the guest CPU", current_accel_name()); From 164258da4a24464a502b6ece6fce9fc6ca1c0207 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:34 +0100 Subject: [PATCH 1759/2760] hw/arm/virt: Make EL2 accelerator check an accept-list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently only the TCG and qtest accelerators can handle an EL2 guest. Instead of making the condition check be "fail if KVM or HVF" (an exclude-list), make it a be "allow if TCG or qtest" (an accept-list). This is better for if/when we add new accelerators, as it makes the default be that we forbid an EL2 guest. This is the most likely to be correct and also "fails safe"; if the new accelerator really can support EL2 guests then the implementor will see that they need to add it to the accept-list. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Message-id: 20250623121845.7214-20-philmd@linaro.org [PMM: rewrote commit message] Signed-off-by: Peter Maydell --- hw/arm/virt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index b3b1c6df7f..30f91bcfb3 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2207,7 +2207,7 @@ static void machvirt_init(MachineState *machine) exit(1); } - if (vms->virt && (kvm_enabled() || hvf_enabled())) { + if (vms->virt && !tcg_enabled() && !qtest_enabled()) { error_report("mach-virt: %s does not support providing " "Virtualization extensions to the guest CPU", current_accel_name()); From c6a613094e48fc83ee5c65464dccd90fae637516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:35 +0100 Subject: [PATCH 1760/2760] hw/arm/virt: Rename cpu_post_init() -> post_cpus_gic_realized() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit QDev uses _post_init() during instance creation, before being realized. Since here both vCPUs and GIC are REALIZED, rename as virt_post_cpus_gic_realized() for clarity. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-21-philmd@linaro.org [PMM: also fixed up comment] Signed-off-by: Peter Maydell --- hw/arm/virt.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 30f91bcfb3..3bcdf92e2f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -2026,10 +2026,11 @@ static void finalize_gic_version(VirtMachineState *vms) } /* - * virt_cpu_post_init() must be called after the CPUs have - * been realized and the GIC has been created. + * virt_post_cpus_gic_realized() must be called after the CPUs and + * the GIC have both been realized. */ -static void virt_cpu_post_init(VirtMachineState *vms, MemoryRegion *sysmem) +static void virt_post_cpus_gic_realized(VirtMachineState *vms, + MemoryRegion *sysmem) { int max_cpus = MACHINE(vms)->smp.max_cpus; bool aarch64, pmu, steal_time; @@ -2346,7 +2347,7 @@ static void machvirt_init(MachineState *machine) create_gic(vms, sysmem); - virt_cpu_post_init(vms, sysmem); + virt_post_cpus_gic_realized(vms, sysmem); fdt_add_pmu_nodes(vms); From 4e24ea43a126822d0c3d28d57b500ac9bbce5735 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:35 +0100 Subject: [PATCH 1761/2760] hw/arm/sbsa-ref: Tidy up use of RAMLIMIT_GB definition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define RAMLIMIT_BYTES using the TiB definition and display the error parsed with size_to_str(): $ qemu-system-aarch64-unsigned -M sbsa-ref -m 9T qemu-system-aarch64-unsigned: sbsa-ref: cannot model more than 8 TiB of RAM Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-22-philmd@linaro.org Signed-off-by: Peter Maydell --- hw/arm/sbsa-ref.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hw/arm/sbsa-ref.c b/hw/arm/sbsa-ref.c index deae5cf986..15c1ff4b14 100644 --- a/hw/arm/sbsa-ref.c +++ b/hw/arm/sbsa-ref.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/cutils.h" #include "qemu/datadir.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -53,8 +54,7 @@ #include "target/arm/cpu-qom.h" #include "target/arm/gtimer.h" -#define RAMLIMIT_GB 8192 -#define RAMLIMIT_BYTES (RAMLIMIT_GB * GiB) +#define RAMLIMIT_BYTES (8 * TiB) #define NUM_IRQS 256 #define NUM_SMMU_IRQS 4 @@ -756,7 +756,9 @@ static void sbsa_ref_init(MachineState *machine) sms->smp_cpus = smp_cpus; if (machine->ram_size > sbsa_ref_memmap[SBSA_MEM].size) { - error_report("sbsa-ref: cannot model more than %dGB RAM", RAMLIMIT_GB); + char *size_str = size_to_str(RAMLIMIT_BYTES); + + error_report("sbsa-ref: cannot model more than %s of RAM", size_str); exit(1); } From 9bc4c2a6118ab1713155dedd14ccdae8dde71de1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:35 +0100 Subject: [PATCH 1762/2760] tests/functional: Set sbsa-ref machine type in each test function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fetch_firmware() is only about fetching firmware. Set the machine type and its default console in test_sbsaref_edk2_firmware(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Leif Lindholm Message-id: 20250623121845.7214-23-philmd@linaro.org Signed-off-by: Peter Maydell --- tests/functional/test_aarch64_sbsaref.py | 5 +++-- tests/functional/test_aarch64_sbsaref_alpine.py | 3 ++- tests/functional/test_aarch64_sbsaref_freebsd.py | 3 ++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_aarch64_sbsaref.py b/tests/functional/test_aarch64_sbsaref.py index e6a55aecfa..d3402f5080 100755 --- a/tests/functional/test_aarch64_sbsaref.py +++ b/tests/functional/test_aarch64_sbsaref.py @@ -40,8 +40,6 @@ def fetch_firmware(test): with open(path, "ab+") as fd: fd.truncate(256 << 20) # Expand volumes to 256MiB - test.set_machine('sbsa-ref') - test.vm.set_console() test.vm.add_args( "-drive", f"if=pflash,file={fs0_path},format=raw", "-drive", f"if=pflash,file={fs1_path},format=raw", @@ -68,8 +66,11 @@ class Aarch64SbsarefMachine(QemuSystemTest): def test_sbsaref_edk2_firmware(self): + self.set_machine('sbsa-ref') + fetch_firmware(self) + self.vm.set_console() self.vm.add_args('-cpu', 'cortex-a57') self.vm.launch() diff --git a/tests/functional/test_aarch64_sbsaref_alpine.py b/tests/functional/test_aarch64_sbsaref_alpine.py index 6108ec65a5..8776999383 100755 --- a/tests/functional/test_aarch64_sbsaref_alpine.py +++ b/tests/functional/test_aarch64_sbsaref_alpine.py @@ -26,8 +26,9 @@ class Aarch64SbsarefAlpine(QemuSystemTest): # We only boot a whole OS for the current top level CPU and GIC # Other test profiles should use more minimal boots def boot_alpine_linux(self, cpu=None): - fetch_firmware(self) + self.set_machine('sbsa-ref') + fetch_firmware(self) iso_path = self.ASSET_ALPINE_ISO.fetch() self.vm.set_console() diff --git a/tests/functional/test_aarch64_sbsaref_freebsd.py b/tests/functional/test_aarch64_sbsaref_freebsd.py index 26dfc5878b..3cddc082f3 100755 --- a/tests/functional/test_aarch64_sbsaref_freebsd.py +++ b/tests/functional/test_aarch64_sbsaref_freebsd.py @@ -26,8 +26,9 @@ class Aarch64SbsarefFreeBSD(QemuSystemTest): # We only boot a whole OS for the current top level CPU and GIC # Other test profiles should use more minimal boots def boot_freebsd14(self, cpu=None): - fetch_firmware(self) + self.set_machine('sbsa-ref') + fetch_firmware(self) img_path = self.ASSET_FREEBSD_ISO.fetch() self.vm.set_console() From 72846594b089f03bed1bb46958c910afcb859d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:35 +0100 Subject: [PATCH 1763/2760] tests/functional: Restrict nested Aarch64 Xen test to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently QEMU only support accelerating EL0 and EL1, so features requiring EL2 (like virtualization) or EL3 must be emulated with TCG. On macOS this test fails: qemu-system-aarch64: mach-virt: HVF does not support providing Virtualization extensions to the guest CPU Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-id: 20250623121845.7214-24-philmd@linaro.org Signed-off-by: Peter Maydell --- tests/functional/test_aarch64_xen.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/test_aarch64_xen.py b/tests/functional/test_aarch64_xen.py index 339904221b..261d796540 100755 --- a/tests/functional/test_aarch64_xen.py +++ b/tests/functional/test_aarch64_xen.py @@ -33,6 +33,7 @@ def launch_xen(self, xen_path): """ Launch Xen with a dom0 guest kernel """ + self.require_accelerator("tcg") # virtualization=on self.set_machine('virt') self.cpu = "cortex-a57" self.kernel_path = self.ASSET_KERNEL.fetch() From f4b5fbeff6be3797881e69cbf42cc689a4e316c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:36 +0100 Subject: [PATCH 1764/2760] tests/functional: Require TCG to run Aarch64 imx8mp-evk test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The imx8mp-evk machine can only run with the TCG accelerator. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-id: 20250623121845.7214-25-philmd@linaro.org Signed-off-by: Peter Maydell --- tests/functional/test_aarch64_imx8mp_evk.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/functional/test_aarch64_imx8mp_evk.py b/tests/functional/test_aarch64_imx8mp_evk.py index 638bf9e131..99ddcdef83 100755 --- a/tests/functional/test_aarch64_imx8mp_evk.py +++ b/tests/functional/test_aarch64_imx8mp_evk.py @@ -49,6 +49,7 @@ def setUp(self): self.DTB_OFFSET, self.DTB_SIZE) def test_aarch64_imx8mp_evk_usdhc(self): + self.require_accelerator("tcg") self.set_machine('imx8mp-evk') self.vm.set_console(console_index=1) self.vm.add_args('-m', '2G', From 767df742fbc9e6cc06e9309685407beb2565c272 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Tue, 1 Jul 2025 17:22:27 +0100 Subject: [PATCH 1765/2760] tests/functional: Add hvf_available() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Thomas Huth Message-id: 20250623121845.7214-26-philmd@linaro.org [PMM: tweaks to satisfy the python linter CI job] Signed-off-by: Peter Maydell --- python/qemu/utils/__init__.py | 8 +++++++- python/qemu/utils/accel.py | 9 +++++++++ tests/functional/qemu_test/testcase.py | 6 ++++-- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/python/qemu/utils/__init__.py b/python/qemu/utils/__init__.py index 017cfdcda7..be5daa8363 100644 --- a/python/qemu/utils/__init__.py +++ b/python/qemu/utils/__init__.py @@ -23,13 +23,19 @@ from typing import Optional # pylint: disable=import-error -from .accel import kvm_available, list_accel, tcg_available +from .accel import ( + hvf_available, + kvm_available, + list_accel, + tcg_available, +) __all__ = ( 'VerboseProcessError', 'add_visual_margin', 'get_info_usernet_hostfwd_port', + 'hvf_available', 'kvm_available', 'list_accel', 'tcg_available', diff --git a/python/qemu/utils/accel.py b/python/qemu/utils/accel.py index 386ff640ca..f915b64669 100644 --- a/python/qemu/utils/accel.py +++ b/python/qemu/utils/accel.py @@ -82,3 +82,12 @@ def tcg_available(qemu_bin: str) -> bool: @param qemu_bin (str): path to the QEMU binary """ return 'tcg' in list_accel(qemu_bin) + + +def hvf_available(qemu_bin: str) -> bool: + """ + Check if HVF is available. + + @param qemu_bin (str): path to the QEMU binary + """ + return 'hvf' in list_accel(qemu_bin) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 50c401b8c3..2082c6fce4 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -23,7 +23,7 @@ import uuid from qemu.machine import QEMUMachine -from qemu.utils import kvm_available, tcg_available +from qemu.utils import hvf_available, kvm_available, tcg_available from .archive import archive_extract from .asset import Asset @@ -317,7 +317,9 @@ def require_accelerator(self, accelerator): :type accelerator: str """ checker = {'tcg': tcg_available, - 'kvm': kvm_available}.get(accelerator) + 'kvm': kvm_available, + 'hvf': hvf_available, + }.get(accelerator) if checker is None: self.skipTest("Don't know how to check for the presence " "of accelerator %s" % accelerator) From 9b4c8dd505b1630e3fdaca071886d1cd41019156 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 15:08:36 +0100 Subject: [PATCH 1766/2760] tests/functional: Expand Aarch64 SMMU tests to run on HVF accelerator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Thomas Huth Reviewed-by: Alex Bennée Message-id: 20250623121845.7214-27-philmd@linaro.org Signed-off-by: Peter Maydell --- tests/functional/test_aarch64_smmu.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/functional/test_aarch64_smmu.py b/tests/functional/test_aarch64_smmu.py index c65d0f2817..e0f4a92217 100755 --- a/tests/functional/test_aarch64_smmu.py +++ b/tests/functional/test_aarch64_smmu.py @@ -17,7 +17,7 @@ from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern from qemu_test import BUILD_DIR -from qemu.utils import kvm_available +from qemu.utils import kvm_available, hvf_available class SMMU(LinuxKernelTest): @@ -45,11 +45,17 @@ def set_up_boot(self, path): self.vm.add_args('-device', 'virtio-net,netdev=n1' + self.IOMMU_ADDON) def common_vm_setup(self, kernel, initrd, disk): - self.require_accelerator("kvm") + if hvf_available(self.qemu_bin): + accel = "hvf" + elif kvm_available(self.qemu_bin): + accel = "kvm" + else: + self.skipTest("Neither HVF nor KVM accelerator is available") + self.require_accelerator(accel) self.require_netdev('user') self.set_machine("virt") self.vm.add_args('-m', '1G') - self.vm.add_args("-accel", "kvm") + self.vm.add_args("-accel", accel) self.vm.add_args("-cpu", "host") self.vm.add_args("-machine", "iommu=smmuv3") self.vm.add_args("-d", "guest_errors") From 7bc86ccbb59f2022014e132327a33b94a7ed00fe Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Fri, 27 Jun 2025 13:02:22 -0700 Subject: [PATCH 1767/2760] tests/functional: test device passthrough on aarch64 This test allows to document and exercise device passthrough, using a nested virtual machine setup. Two disks are generated and passed to the VM, and their content is compared to original images. Guest and nested guests commands are executed through two scripts, and init used in both system is configured to trigger a kernel panic in case any command fails. This is more reliable and readable than executing all commands through prompt injection and trying to guess what failed. Initially, this test was supposed to test smmuv3 nested emulation (combining both stages of translation), but I could not find any setup (kernel + vmm) able to do the passthrough correctly, despite several tries. Signed-off-by: Pierrick Bouvier Message-id: 20250627200222.5172-1-pierrick.bouvier@linaro.org Signed-off-by: Peter Maydell --- tests/functional/meson.build | 2 + .../test_aarch64_device_passthrough.py | 142 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100755 tests/functional/test_aarch64_device_passthrough.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index e9f19d54a2..85158562a2 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -13,6 +13,7 @@ endif test_timeouts = { 'aarch64_aspeed_ast2700' : 600, 'aarch64_aspeed_ast2700fc' : 600, + 'aarch64_device_passthrough' : 720, 'aarch64_imx8mp_evk' : 240, 'aarch64_raspi4' : 480, 'aarch64_reverse_debug' : 180, @@ -83,6 +84,7 @@ tests_aarch64_system_quick = [ tests_aarch64_system_thorough = [ 'aarch64_aspeed_ast2700', 'aarch64_aspeed_ast2700fc', + 'aarch64_device_passthrough', 'aarch64_imx8mp_evk', 'aarch64_raspi3', 'aarch64_raspi4', diff --git a/tests/functional/test_aarch64_device_passthrough.py b/tests/functional/test_aarch64_device_passthrough.py new file mode 100755 index 0000000000..1f3f158a9f --- /dev/null +++ b/tests/functional/test_aarch64_device_passthrough.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python3 +# +# Boots a nested guest and compare content of a device (passthrough) to a +# reference image. Both vfio group and iommufd passthrough methods are tested. +# +# Copyright (c) 2025 Linaro Ltd. +# +# Author: Pierrick Bouvier +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import QemuSystemTest, Asset +from qemu_test import exec_command, wait_for_console_pattern +from qemu_test import exec_command_and_wait_for_pattern +from random import randbytes + +guest_script = ''' +#!/usr/bin/env bash + +set -euo pipefail +set -x + +# find disks from nvme serial +dev_vfio=$(lsblk --nvme | grep vfio | cut -f 1 -d ' ') +dev_iommufd=$(lsblk --nvme | grep iommufd | cut -f 1 -d ' ') +pci_vfio=$(basename $(readlink -f /sys/block/$dev_vfio/../../../)) +pci_iommufd=$(basename $(readlink -f /sys/block/$dev_iommufd/../../../)) + +# bind disks to vfio +for p in "$pci_vfio" "$pci_iommufd"; do + if [ "$(cat /sys/bus/pci/devices/$p/driver_override)" == vfio-pci ]; then + continue + fi + echo $p > /sys/bus/pci/drivers/nvme/unbind + echo vfio-pci > /sys/bus/pci/devices/$p/driver_override + echo $p > /sys/bus/pci/drivers/vfio-pci/bind +done + +# boot nested guest and execute /host/nested_guest.sh +# one disk is passed through vfio group, the other, through iommufd +qemu-system-aarch64 \ +-M virt \ +-display none \ +-serial stdio \ +-cpu host \ +-enable-kvm \ +-m 1G \ +-kernel /host/Image.gz \ +-drive format=raw,file=/host/guest.ext4,if=virtio \ +-append "root=/dev/vda init=/init -- bash /host/nested_guest.sh" \ +-virtfs local,path=/host,mount_tag=host,security_model=mapped,readonly=off \ +-device vfio-pci,host=$pci_vfio \ +-object iommufd,id=iommufd0 \ +-device vfio-pci,host=$pci_iommufd,iommufd=iommufd0 +''' + +nested_guest_script = ''' +#!/usr/bin/env bash + +set -euo pipefail +set -x + +image_vfio=/host/disk_vfio +image_iommufd=/host/disk_iommufd + +dev_vfio=$(lsblk --nvme | grep vfio | cut -f 1 -d ' ') +dev_iommufd=$(lsblk --nvme | grep iommufd | cut -f 1 -d ' ') + +# compare if devices are identical to original images +diff $image_vfio /dev/$dev_vfio +diff $image_iommufd /dev/$dev_iommufd + +echo device_passthrough_test_ok +''' + +class Aarch64DevicePassthrough(QemuSystemTest): + + # https://github.com/pbo-linaro/qemu-linux-stack + # + # Linux kernel is compiled with defconfig + + # IOMMUFD + VFIO_DEVICE_CDEV + ARM_SMMU_V3_IOMMUFD + # https://docs.kernel.org/driver-api/vfio.html#vfio-device-cde + ASSET_DEVICE_PASSTHROUGH_STACK = Asset( + ('https://fileserver.linaro.org/s/fx5DXxBYme8dw2G/' + 'download/device_passthrough.tar.xz'), + '812750b664d61c2986f2b149939ae28cafbd60d53e9c7e4b16e97143845e196d') + + # This tests the device passthrough implementation, by booting a VM + # supporting it with two nvme disks attached, and launching a nested VM + # reading their content. + def test_aarch64_device_passthrough(self): + self.set_machine('virt') + self.require_accelerator('tcg') + + self.vm.set_console() + + stack_path_tar_gz = self.ASSET_DEVICE_PASSTHROUGH_STACK.fetch() + self.archive_extract(stack_path_tar_gz, format="tar") + + stack = self.scratch_file('out') + kernel = os.path.join(stack, 'Image.gz') + rootfs_host = os.path.join(stack, 'host.ext4') + disk_vfio = os.path.join(stack, 'disk_vfio') + disk_iommufd = os.path.join(stack, 'disk_iommufd') + guest_cmd = os.path.join(stack, 'guest.sh') + nested_guest_cmd = os.path.join(stack, 'nested_guest.sh') + # we generate two random disks + with open(disk_vfio, "wb") as d: d.write(randbytes(512)) + with open(disk_iommufd, "wb") as d: d.write(randbytes(1024)) + with open(guest_cmd, 'w') as s: s.write(guest_script) + with open(nested_guest_cmd, 'w') as s: s.write(nested_guest_script) + + self.vm.add_args('-cpu', 'max') + self.vm.add_args('-m', '2G') + self.vm.add_args('-M', 'virt,' + 'virtualization=on,' + 'gic-version=max,' + 'iommu=smmuv3') + self.vm.add_args('-kernel', kernel) + self.vm.add_args('-drive', f'format=raw,file={rootfs_host}') + self.vm.add_args('-drive', + f'file={disk_vfio},if=none,id=vfio,format=raw') + self.vm.add_args('-device', 'nvme,serial=vfio,drive=vfio') + self.vm.add_args('-drive', + f'file={disk_iommufd},if=none,id=iommufd,format=raw') + self.vm.add_args('-device', 'nvme,serial=iommufd,drive=iommufd') + self.vm.add_args('-virtfs', + f'local,path={stack}/,mount_tag=host,' + 'security_model=mapped,readonly=off') + # boot and execute guest script + # init will trigger a kernel panic if script fails + self.vm.add_args('-append', + 'root=/dev/vda init=/init -- bash /host/guest.sh') + + self.vm.launch() + wait_for_console_pattern(self, 'device_passthrough_test_ok', + failure_message='Kernel panic') + +if __name__ == '__main__': + QemuSystemTest.main() From 6af9f88fff5baea33469d52b94c7e64bd596713e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 27 Jun 2025 12:24:57 +0100 Subject: [PATCH 1768/2760] gitlab: mark s390x-system to allow failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The system tests (usually qos-test or migration-test) prove to be very susceptible on the s390x runners. Although we have boosted memory and virtual CPUs on the runners problems persist. For now mark test as allow_failure so the its clear on the CI UI when checking test results. Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-2-alex.bennee@linaro.org> --- .gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml index ca374acb8c..e62ff1763f 100644 --- a/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml +++ b/.gitlab-ci.d/custom-runners/ubuntu-22.04-s390x.yml @@ -31,7 +31,9 @@ ubuntu-22.04-s390x-all-system: timeout: 75m rules: - if: '$CI_PROJECT_NAMESPACE == "qemu-project" && $CI_COMMIT_BRANCH =~ /^staging/' + allow_failure: true - if: "$S390X_RUNNER_AVAILABLE" + allow_failure: true script: - mkdir build - cd build From 374a245573b8da2d7fd91272868e173027adc7db Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 27 Jun 2025 12:24:58 +0100 Subject: [PATCH 1769/2760] tests/functional: Add PCI hotplug test for aarch64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a functional test, aarch64_hotplug_pci, to exercise PCI hotplug and hot-unplug on arm64. Signed-off-by: Gustavo Romero Reviewed-by: Daniel P. Berrangé Acked-by: Thomas Huth Message-ID: <20250528203137.1654964-1-gustavo.romero@linaro.org> [AJB: trimmed boilerplate for checkpatch, simplified invocations] Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-3-alex.bennee@linaro.org> --- MAINTAINERS | 6 ++ tests/functional/meson.build | 1 + tests/functional/test_aarch64_hotplug_pci.py | 72 ++++++++++++++++++++ 3 files changed, 79 insertions(+) create mode 100755 tests/functional/test_aarch64_hotplug_pci.py diff --git a/MAINTAINERS b/MAINTAINERS index d1672fda8d..850588fb64 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2089,6 +2089,12 @@ S: Supported F: include/hw/pci/pcie_doe.h F: hw/pci/pcie_doe.c +ARM PCI Hotplug +M: Gustavo Romero +L: qemu-arm@nongnu.org +S: Supported +F: tests/functional/test_aarch64_hotplug_pci.py + ACPI/SMBIOS M: Michael S. Tsirkin M: Igor Mammedov diff --git a/tests/functional/meson.build b/tests/functional/meson.build index e9f19d54a2..53721c97ec 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -83,6 +83,7 @@ tests_aarch64_system_quick = [ tests_aarch64_system_thorough = [ 'aarch64_aspeed_ast2700', 'aarch64_aspeed_ast2700fc', + 'aarch64_hotplug_pci', 'aarch64_imx8mp_evk', 'aarch64_raspi3', 'aarch64_raspi4', diff --git a/tests/functional/test_aarch64_hotplug_pci.py b/tests/functional/test_aarch64_hotplug_pci.py new file mode 100755 index 0000000000..c9bb7f1d97 --- /dev/null +++ b/tests/functional/test_aarch64_hotplug_pci.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python3 +# +# The test hotplugs a PCI device and checks it on a Linux guest. +# +# Copyright (c) 2025 Linaro Ltd. +# +# Author: +# Gustavo Romero +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import LinuxKernelTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import BUILD_DIR + +class HotplugPCI(LinuxKernelTest): + + ASSET_KERNEL = Asset( + ('https://ftp.debian.org/debian/dists/stable/main/installer-arm64/' + '20230607+deb12u11/images/netboot/debian-installer/arm64/linux'), + 'd92a60392ce1e379ca198a1a820899f8f0d39a62d047c41ab79492f81541a9d9') + + ASSET_INITRD = Asset( + ('https://ftp.debian.org/debian/dists/stable/main/installer-arm64/' + '20230607+deb12u11/images/netboot/debian-installer/arm64/initrd.gz'), + '9f817f76951f3237bca8216bee35267bfb826815687f4b2fcdd5e6c2a917790c') + + def test_hotplug_pci(self): + + self.set_machine('virt') + + self.vm.add_args('-m', '512M', + '-cpu', 'cortex-a57', + '-append', + 'console=ttyAMA0,115200 init=/bin/sh', + '-device', + 'pcie-root-port,bus=pcie.0,chassis=1,slot=1,id=pcie.1', + '-bios', + self.build_file('pc-bios', 'edk2-aarch64-code.fd')) + + # BusyBox prompt + prompt = "~ #" + self.launch_kernel(self.ASSET_KERNEL.fetch(), + self.ASSET_INITRD.fetch(), + wait_for=prompt) + + # Check for initial state: 2 network adapters, lo and enp0s1. + exec_command_and_wait_for_pattern(self, + 'ls /sys/class/net | wc -l', + '2') + + # Hotplug one network adapter to the root port, i.e. pcie.1 bus. + self.vm.cmd('device_add', + driver='virtio-net-pci', + bus='pcie.1', + addr=0, + id='na') + # Wait for the kernel to recognize the new device. + self.wait_for_console_pattern('virtio-pci') + self.wait_for_console_pattern('virtio_net') + + # Check if there is a new network adapter. + exec_command_and_wait_for_pattern(self, + 'ls /sys/class/net | wc -l', + '3') + + self.vm.cmd('device_del', id='na') + exec_command_and_wait_for_pattern(self, + 'ls /sys/class/net | wc -l', + '2') + +if __name__ == '__main__': + LinuxKernelTest.main() From 328c1a0b861e6c581880cf5831a0952ed92bcbf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 27 Jun 2025 12:24:59 +0100 Subject: [PATCH 1770/2760] semihosting/uaccess: Remove uses of target_ulong type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace target_ulong by vaddr or size_t types to match cpu_memory_rw_debug() prototype in "exec/cpu-common.h": > int cpu_memory_rw_debug(CPUState *cpu, vaddr addr, > void *ptr, size_t len, > bool is_write); Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-ID: <20250526095213.14113-2-philmd@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-4-alex.bennee@linaro.org> --- include/semihosting/uaccess.h | 12 ++++++------ semihosting/uaccess.c | 10 +++++----- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/include/semihosting/uaccess.h b/include/semihosting/uaccess.h index 6bc90b12d6..2093a49827 100644 --- a/include/semihosting/uaccess.h +++ b/include/semihosting/uaccess.h @@ -15,9 +15,9 @@ #endif #include "exec/cpu-common.h" -#include "exec/cpu-defs.h" #include "exec/tswap.h" #include "exec/page-protection.h" +#include "exec/vaddr.h" /** * get_user_u64: @@ -89,8 +89,8 @@ * * The returned pointer should be freed using uaccess_unlock_user(). */ -void *uaccess_lock_user(CPUArchState *env, target_ulong addr, - target_ulong len, bool copy); +void *uaccess_lock_user(CPUArchState *env, vaddr addr, + size_t len, bool copy); /** * lock_user: * @@ -103,7 +103,7 @@ void *uaccess_lock_user(CPUArchState *env, target_ulong addr, * * The returned string should be freed using uaccess_unlock_user(). */ -char *uaccess_lock_user_string(CPUArchState *env, target_ulong addr); +char *uaccess_lock_user_string(CPUArchState *env, vaddr addr); /** * uaccess_lock_user_string: * @@ -112,10 +112,10 @@ char *uaccess_lock_user_string(CPUArchState *env, target_ulong addr); #define lock_user_string(p) uaccess_lock_user_string(env, p) void uaccess_unlock_user(CPUArchState *env, void *p, - target_ulong addr, target_ulong len); + vaddr addr, size_t len); #define unlock_user(s, args, len) uaccess_unlock_user(env, s, args, len) -ssize_t uaccess_strlen_user(CPUArchState *env, target_ulong addr); +ssize_t uaccess_strlen_user(CPUArchState *env, vaddr addr); #define target_strlen(p) uaccess_strlen_user(env, p) #endif /* SEMIHOSTING_SOFTMMU_UACCESS_H */ diff --git a/semihosting/uaccess.c b/semihosting/uaccess.c index 4554844e15..ff944d8c2f 100644 --- a/semihosting/uaccess.c +++ b/semihosting/uaccess.c @@ -14,8 +14,8 @@ #include "exec/tlb-flags.h" #include "semihosting/uaccess.h" -void *uaccess_lock_user(CPUArchState *env, target_ulong addr, - target_ulong len, bool copy) +void *uaccess_lock_user(CPUArchState *env, vaddr addr, + size_t len, bool copy) { void *p = malloc(len); if (p && copy) { @@ -27,7 +27,7 @@ void *uaccess_lock_user(CPUArchState *env, target_ulong addr, return p; } -ssize_t uaccess_strlen_user(CPUArchState *env, target_ulong addr) +ssize_t uaccess_strlen_user(CPUArchState *env, vaddr addr) { int mmu_idx = cpu_mmu_index(env_cpu(env), false); size_t len = 0; @@ -75,7 +75,7 @@ ssize_t uaccess_strlen_user(CPUArchState *env, target_ulong addr) } } -char *uaccess_lock_user_string(CPUArchState *env, target_ulong addr) +char *uaccess_lock_user_string(CPUArchState *env, vaddr addr) { ssize_t len = uaccess_strlen_user(env, addr); if (len < 0) { @@ -85,7 +85,7 @@ char *uaccess_lock_user_string(CPUArchState *env, target_ulong addr) } void uaccess_unlock_user(CPUArchState *env, void *p, - target_ulong addr, target_ulong len) + vaddr addr, size_t len) { if (len) { cpu_memory_rw_debug(env_cpu(env), addr, p, len, 1); From b8e6bfd6695331c24a6a8c49db1321ef24ddecaf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 27 Jun 2025 12:25:00 +0100 Subject: [PATCH 1771/2760] semihosting/uaccess: Compile once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20250526095213.14113-3-philmd@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-5-alex.bennee@linaro.org> --- semihosting/meson.build | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/semihosting/meson.build b/semihosting/meson.build index f3d38dda91..b1ab2506c6 100644 --- a/semihosting/meson.build +++ b/semihosting/meson.build @@ -3,15 +3,12 @@ specific_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( 'syscalls.c', )) -specific_ss.add(when: ['CONFIG_SEMIHOSTING', 'CONFIG_SYSTEM_ONLY'], if_true: files( - 'uaccess.c', -)) - common_ss.add(when: 'CONFIG_SEMIHOSTING', if_false: files('stubs-all.c')) user_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files('user.c')) system_ss.add(when: 'CONFIG_SEMIHOSTING', if_true: files( 'config.c', 'console.c', + 'uaccess.c', ), if_false: files( 'stubs-system.c', )) From 1bb6403a34b05a128fbaa27f392f3f78c98c59a9 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Fri, 27 Jun 2025 12:25:01 +0100 Subject: [PATCH 1772/2760] gdbstub: Expose gdb_write_register function to consumers of gdbstub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch exposes the gdb_write_register function from gdbstub/gdbstub.c via the exec/gdbstub.h header file to support use in plugins to write register contents. Reviewed-by: Alex Bennée Reviewed-by: Julian Ganz Reviewed-by: Pierrick Bouvier Signed-off-by: Rowan Hart Message-ID: <20250624175351.440780-2-rowanbhart@gmail.com> Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-6-alex.bennee@linaro.org> --- gdbstub/gdbstub.c | 2 +- include/exec/gdbstub.h | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c index def0b7e877..dd5fb5667c 100644 --- a/gdbstub/gdbstub.c +++ b/gdbstub/gdbstub.c @@ -535,7 +535,7 @@ int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) return 0; } -static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) +int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) { GDBRegisterState *r; diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h index 0675b0b646..a16c0051ce 100644 --- a/include/exec/gdbstub.h +++ b/include/exec/gdbstub.h @@ -124,6 +124,20 @@ const GDBFeature *gdb_find_static_feature(const char *xmlname); */ int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); +/** + * gdb_write_register() - Write a register associated with a CPU. + * @cpu: The CPU associated with the register. + * @buf: The buffer that the register contents will be set to. + * @reg: The register's number returned by gdb_find_feature_register(). + * + * The size of @buf must be at least the size of the register being + * written. + * + * Return: The number of written bytes, or 0 if an error occurred (for + * example, an unknown register was provided). + */ +int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg); + /** * typedef GDBRegDesc - a register description from gdbstub */ From 1a92b65859e2d58dbf4b8356940c42c5834e9710 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Fri, 27 Jun 2025 12:25:02 +0100 Subject: [PATCH 1773/2760] plugins: Add register write API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a function to the plugins API to allow plugins to write register contents. It also moves the qemu_plugin_read_register function so all the register-related functions are grouped together in the file. Reviewed-by: Pierrick Bouvier Signed-off-by: Rowan Hart Message-ID: <20250624175351.440780-3-rowanbhart@gmail.com> Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-7-alex.bennee@linaro.org> --- include/qemu/qemu-plugin.h | 54 ++++++++++++++++++++++++++------------ plugins/api.c | 26 +++++++++++++----- 2 files changed, 56 insertions(+), 24 deletions(-) diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 3a850aa216..cfe1692ecb 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -871,7 +871,8 @@ struct qemu_plugin_register; /** * typedef qemu_plugin_reg_descriptor - register descriptions * - * @handle: opaque handle for retrieving value with qemu_plugin_read_register + * @handle: opaque handle for retrieving value with qemu_plugin_read_register or + * writing value with qemu_plugin_write_register * @name: register name * @feature: optional feature descriptor, can be NULL */ @@ -893,6 +894,41 @@ typedef struct { QEMU_PLUGIN_API GArray *qemu_plugin_get_registers(void); +/** + * qemu_plugin_read_register() - read register for current vCPU + * + * @handle: a @qemu_plugin_reg_handle handle + * @buf: A GByteArray for the data owned by the plugin + * + * This function is only available in a context that register read access is + * explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag. + * + * Returns the size of the read register. The content of @buf is in target byte + * order. On failure returns -1. + */ +QEMU_PLUGIN_API +int qemu_plugin_read_register(struct qemu_plugin_register *handle, + GByteArray *buf); + +/** + * qemu_plugin_write_register() - write register for current vCPU + * + * @handle: a @qemu_plugin_reg_handle handle + * @buf: A GByteArray for the data owned by the plugin + * + * This function is only available in a context that register write access is + * explicitly requested via the QEMU_PLUGIN_CB_RW_REGS flag. + * + * The size of @buf must be at least the size of the requested register. + * Attempting to write a register with @buf smaller than the register size + * will result in a crash or other undesired behavior. + * + * Returns the number of bytes written. On failure returns 0. + */ +QEMU_PLUGIN_API +int qemu_plugin_write_register(struct qemu_plugin_register *handle, + GByteArray *buf); + /** * qemu_plugin_read_memory_vaddr() - read from memory using a virtual address * @@ -915,22 +951,6 @@ QEMU_PLUGIN_API bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *data, size_t len); -/** - * qemu_plugin_read_register() - read register for current vCPU - * - * @handle: a @qemu_plugin_reg_handle handle - * @buf: A GByteArray for the data owned by the plugin - * - * This function is only available in a context that register read access is - * explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag. - * - * Returns the size of the read register. The content of @buf is in target byte - * order. On failure returns -1. - */ -QEMU_PLUGIN_API -int qemu_plugin_read_register(struct qemu_plugin_register *handle, - GByteArray *buf); - /** * qemu_plugin_scoreboard_new() - alloc a new scoreboard * diff --git a/plugins/api.c b/plugins/api.c index 3c9d4832e9..6514f2c76a 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -433,6 +433,25 @@ GArray *qemu_plugin_get_registers(void) return create_register_handles(regs); } +int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) +{ + g_assert(current_cpu); + + return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg) - 1); +} + +int qemu_plugin_write_register(struct qemu_plugin_register *reg, + GByteArray *buf) +{ + g_assert(current_cpu); + + if (buf->len == 0 || qemu_plugin_get_cb_flags() != QEMU_PLUGIN_CB_RW_REGS) { + return -1; + } + + return gdb_write_register(current_cpu, buf->data, GPOINTER_TO_INT(reg) - 1); +} + bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *data, size_t len) { g_assert(current_cpu); @@ -453,13 +472,6 @@ bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *data, size_t len) return true; } -int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) -{ - g_assert(current_cpu); - - return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg) - 1); -} - struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size) { return plugin_scoreboard_new(element_size); From 766e00bd570f54c8d08023a46cd3386e96ecd39f Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Fri, 27 Jun 2025 12:25:03 +0100 Subject: [PATCH 1774/2760] plugins: Add enforcement of QEMU_PLUGIN_CB flags in register R/W callbacks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds functionality to enforce the requested QEMU_PLUGIN_CB_ flags level passed when registering a callback function using the plugins API. Each time a callback is about to be invoked, a thread-local variable will be updated with the level that callback requested. Then, called API functions (in particular, the register read and write API) will call qemu_plugin_get_cb_flags() to check the level is at least the level they require. Reviewed-by: Pierrick Bouvier Signed-off-by: Rowan Hart Message-ID: <20250624175351.440780-4-rowanbhart@gmail.com> Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-8-alex.bennee@linaro.org> --- accel/tcg/plugin-gen.c | 30 ++++++++++++++++++++++++++++++ include/hw/core/cpu.h | 1 + include/qemu/plugin.h | 15 +++++++++++++++ include/qemu/qemu-plugin.h | 19 +++++++++++++------ plugins/api.c | 4 ++++ plugins/core.c | 33 +++++++++++++++++++++++++++++++++ 6 files changed, 96 insertions(+), 6 deletions(-) diff --git a/accel/tcg/plugin-gen.c b/accel/tcg/plugin-gen.c index c1da753894..9920381a84 100644 --- a/accel/tcg/plugin-gen.c +++ b/accel/tcg/plugin-gen.c @@ -117,10 +117,20 @@ static TCGv_i32 gen_cpu_index(void) static void gen_udata_cb(struct qemu_plugin_regular_cb *cb) { TCGv_i32 cpu_index = gen_cpu_index(); + enum qemu_plugin_cb_flags cb_flags = + tcg_call_to_qemu_plugin_cb_flags(cb->info->flags); + TCGv_i32 flags = tcg_constant_i32(cb_flags); + TCGv_i32 clear_flags = tcg_constant_i32(QEMU_PLUGIN_CB_NO_REGS); + tcg_gen_st_i32(flags, tcg_env, + offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState)); tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL, tcgv_i32_temp(cpu_index), tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); + tcg_gen_st_i32(clear_flags, tcg_env, + offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState)); tcg_temp_free_i32(cpu_index); + tcg_temp_free_i32(flags); + tcg_temp_free_i32(clear_flags); } static TCGv_ptr gen_plugin_u64_ptr(qemu_plugin_u64 entry) @@ -173,10 +183,20 @@ static void gen_udata_cond_cb(struct qemu_plugin_conditional_cb *cb) tcg_gen_ld_i64(val, ptr, 0); tcg_gen_brcondi_i64(cond, val, cb->imm, after_cb); TCGv_i32 cpu_index = gen_cpu_index(); + enum qemu_plugin_cb_flags cb_flags = + tcg_call_to_qemu_plugin_cb_flags(cb->info->flags); + TCGv_i32 flags = tcg_constant_i32(cb_flags); + TCGv_i32 clear_flags = tcg_constant_i32(QEMU_PLUGIN_CB_NO_REGS); + tcg_gen_st_i32(flags, tcg_env, + offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState)); tcg_gen_call2(cb->f.vcpu_udata, cb->info, NULL, tcgv_i32_temp(cpu_index), tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); + tcg_gen_st_i32(clear_flags, tcg_env, + offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState)); tcg_temp_free_i32(cpu_index); + tcg_temp_free_i32(flags); + tcg_temp_free_i32(clear_flags); gen_set_label(after_cb); tcg_temp_free_i64(val); @@ -210,12 +230,22 @@ static void gen_mem_cb(struct qemu_plugin_regular_cb *cb, qemu_plugin_meminfo_t meminfo, TCGv_i64 addr) { TCGv_i32 cpu_index = gen_cpu_index(); + enum qemu_plugin_cb_flags cb_flags = + tcg_call_to_qemu_plugin_cb_flags(cb->info->flags); + TCGv_i32 flags = tcg_constant_i32(cb_flags); + TCGv_i32 clear_flags = tcg_constant_i32(QEMU_PLUGIN_CB_NO_REGS); + tcg_gen_st_i32(flags, tcg_env, + offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState)); tcg_gen_call4(cb->f.vcpu_mem, cb->info, NULL, tcgv_i32_temp(cpu_index), tcgv_i32_temp(tcg_constant_i32(meminfo)), tcgv_i64_temp(addr), tcgv_ptr_temp(tcg_constant_ptr(cb->userp))); + tcg_gen_st_i32(clear_flags, tcg_env, + offsetof(CPUState, neg.plugin_cb_flags) - sizeof(CPUState)); tcg_temp_free_i32(cpu_index); + tcg_temp_free_i32(flags); + tcg_temp_free_i32(clear_flags); } static void inject_cb(struct qemu_plugin_dyn_cb *cb) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 33296a1c08..162a56a5da 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -368,6 +368,7 @@ typedef struct CPUNegativeOffsetState { GArray *plugin_mem_cbs; uint64_t plugin_mem_value_low; uint64_t plugin_mem_value_high; + int32_t plugin_cb_flags; #endif IcountDecr icount_decr; bool can_do_io; diff --git a/include/qemu/plugin.h b/include/qemu/plugin.h index 9726a9ebf3..f355c7cb8a 100644 --- a/include/qemu/plugin.h +++ b/include/qemu/plugin.h @@ -209,6 +209,21 @@ void qemu_plugin_user_prefork_lock(void); */ void qemu_plugin_user_postfork(bool is_child); +enum qemu_plugin_cb_flags tcg_call_to_qemu_plugin_cb_flags(int flags); + +static inline void qemu_plugin_set_cb_flags(CPUState *cpu, + enum qemu_plugin_cb_flags flags) +{ + assert(cpu); + cpu->neg.plugin_cb_flags = flags; +} + +static inline enum qemu_plugin_cb_flags qemu_plugin_get_cb_flags(void) +{ + assert(current_cpu); + return current_cpu->neg.plugin_cb_flags; +} + #else /* !CONFIG_PLUGIN */ static inline void qemu_plugin_add_opts(void) diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index cfe1692ecb..9c9ebf6ce0 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -254,9 +254,6 @@ typedef struct { * @QEMU_PLUGIN_CB_NO_REGS: callback does not access the CPU's regs * @QEMU_PLUGIN_CB_R_REGS: callback reads the CPU's regs * @QEMU_PLUGIN_CB_RW_REGS: callback reads and writes the CPU's regs - * - * Note: currently QEMU_PLUGIN_CB_RW_REGS is unused, plugins cannot change - * system register state. */ enum qemu_plugin_cb_flags { QEMU_PLUGIN_CB_NO_REGS, @@ -901,7 +898,12 @@ GArray *qemu_plugin_get_registers(void); * @buf: A GByteArray for the data owned by the plugin * * This function is only available in a context that register read access is - * explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag. + * explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag, if called inside a + * callback that can be registered with a qemu_plugin_cb_flags argument. This + * function can also be used in any callback context that does not use a flags + * argument, such as in a callback registered with + * qemu_plugin_register_vcpu_init_cb(), except for callbacks registered with + * qemu_plugin_register_atexit_cb() and qemu_plugin_register_flush_cb(). * * Returns the size of the read register. The content of @buf is in target byte * order. On failure returns -1. @@ -916,8 +918,13 @@ int qemu_plugin_read_register(struct qemu_plugin_register *handle, * @handle: a @qemu_plugin_reg_handle handle * @buf: A GByteArray for the data owned by the plugin * - * This function is only available in a context that register write access is - * explicitly requested via the QEMU_PLUGIN_CB_RW_REGS flag. + * This function is only available in a context that register read access is + * explicitly requested via the QEMU_PLUGIN_CB_RW_REGS flag, if called inside a + * callback that can be registered with a qemu_plugin_cb_flags argument. This + * function can also be used in any callback context that does not use a flags + * argument, such as in a callback registered with + * qemu_plugin_register_vcpu_init_cb(), except for callbacks registered with + * qemu_plugin_register_atexit_cb() and qemu_plugin_register_flush_cb(). * * The size of @buf must be at least the size of the requested register. * Attempting to write a register with @buf smaller than the register size diff --git a/plugins/api.c b/plugins/api.c index 6514f2c76a..3f04399c26 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -437,6 +437,10 @@ int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) { g_assert(current_cpu); + if (qemu_plugin_get_cb_flags() == QEMU_PLUGIN_CB_NO_REGS) { + return -1; + } + return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg) - 1); } diff --git a/plugins/core.c b/plugins/core.c index eb9281fe54..c6e9ef1478 100644 --- a/plugins/core.c +++ b/plugins/core.c @@ -15,6 +15,7 @@ #include "qemu/lockable.h" #include "qemu/option.h" #include "qemu/plugin.h" +#include "qemu/qemu-plugin.h" #include "qemu/queue.h" #include "qemu/rcu_queue.h" #include "qemu/rcu.h" @@ -266,7 +267,9 @@ static void qemu_plugin_vcpu_init__async(CPUState *cpu, run_on_cpu_data unused) plugin_grow_scoreboards__locked(cpu); qemu_rec_mutex_unlock(&plugin.lock); + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS); plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_INIT); + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS); } void qemu_plugin_vcpu_init_hook(CPUState *cpu) @@ -279,7 +282,9 @@ void qemu_plugin_vcpu_exit_hook(CPUState *cpu) { bool success; + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS); plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_EXIT); + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS); assert(cpu->cpu_index != UNASSIGNED_CPU_INDEX); qemu_rec_mutex_lock(&plugin.lock); @@ -367,6 +372,7 @@ void plugin_register_dyn_cb__udata(GArray **arr, static TCGHelperInfo info[3] = { [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG, [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG, + [QEMU_PLUGIN_CB_RW_REGS].flags = 0, /* * Match qemu_plugin_vcpu_udata_cb_t: * void (*)(uint32_t, void *) @@ -396,6 +402,7 @@ void plugin_register_dyn_cond_cb__udata(GArray **arr, static TCGHelperInfo info[3] = { [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG, [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG, + [QEMU_PLUGIN_CB_RW_REGS].flags = 0, /* * Match qemu_plugin_vcpu_udata_cb_t: * void (*)(uint32_t, void *) @@ -434,6 +441,7 @@ void plugin_register_vcpu_mem_cb(GArray **arr, static TCGHelperInfo info[3] = { [QEMU_PLUGIN_CB_NO_REGS].flags = TCG_CALL_NO_RWG, [QEMU_PLUGIN_CB_R_REGS].flags = TCG_CALL_NO_WG, + [QEMU_PLUGIN_CB_RW_REGS].flags = 0, /* * Match qemu_plugin_vcpu_mem_cb_t: * void (*)(uint32_t, qemu_plugin_meminfo_t, uint64_t, void *) @@ -473,7 +481,9 @@ void qemu_plugin_tb_trans_cb(CPUState *cpu, struct qemu_plugin_tb *tb) QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { qemu_plugin_vcpu_tb_trans_cb_t func = cb->f.vcpu_tb_trans; + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS); func(cb->ctx->id, tb); + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS); } } @@ -498,7 +508,9 @@ qemu_plugin_vcpu_syscall(CPUState *cpu, int64_t num, uint64_t a1, uint64_t a2, QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { qemu_plugin_vcpu_syscall_cb_t func = cb->f.vcpu_syscall; + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS); func(cb->ctx->id, cpu->cpu_index, num, a1, a2, a3, a4, a5, a6, a7, a8); + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS); } } @@ -520,7 +532,9 @@ void qemu_plugin_vcpu_syscall_ret(CPUState *cpu, int64_t num, int64_t ret) QLIST_FOREACH_SAFE_RCU(cb, &plugin.cb_lists[ev], entry, next) { qemu_plugin_vcpu_syscall_ret_cb_t func = cb->f.vcpu_syscall_ret; + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS); func(cb->ctx->id, cpu->cpu_index, num, ret); + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS); } } @@ -528,14 +542,18 @@ void qemu_plugin_vcpu_idle_cb(CPUState *cpu) { /* idle and resume cb may be called before init, ignore in this case */ if (cpu->cpu_index < plugin.num_vcpus) { + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS); plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_IDLE); + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS); } } void qemu_plugin_vcpu_resume_cb(CPUState *cpu) { if (cpu->cpu_index < plugin.num_vcpus) { + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_RW_REGS); plugin_vcpu_cb__simple(cpu, QEMU_PLUGIN_EV_VCPU_RESUME); + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS); } } @@ -615,9 +633,13 @@ void qemu_plugin_vcpu_mem_cb(CPUState *cpu, uint64_t vaddr, switch (cb->type) { case PLUGIN_CB_MEM_REGULAR: if (rw & cb->regular.rw) { + qemu_plugin_set_cb_flags(cpu, + tcg_call_to_qemu_plugin_cb_flags(cb->regular.info->flags)); + cb->regular.f.vcpu_mem(cpu->cpu_index, make_plugin_meminfo(oi, rw), vaddr, cb->regular.userp); + qemu_plugin_set_cb_flags(cpu, QEMU_PLUGIN_CB_NO_REGS); } break; case PLUGIN_CB_INLINE_ADD_U64: @@ -760,3 +782,14 @@ void plugin_scoreboard_free(struct qemu_plugin_scoreboard *score) g_array_free(score->data, TRUE); g_free(score); } + +enum qemu_plugin_cb_flags tcg_call_to_qemu_plugin_cb_flags(int flags) +{ + if (flags & TCG_CALL_NO_RWG) { + return QEMU_PLUGIN_CB_NO_REGS; + } else if (flags & TCG_CALL_NO_WG) { + return QEMU_PLUGIN_CB_R_REGS; + } else { + return QEMU_PLUGIN_CB_RW_REGS; + } +} From f00373b895da830ef6d0ee9a518e336e8252a4a3 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Fri, 27 Jun 2025 12:25:04 +0100 Subject: [PATCH 1775/2760] plugins: Add memory virtual address write API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds functions to the plugins API to allow reading and writing memory via virtual addresses. These functions only permit doing so on the current CPU, because there is no way to ensure consistency if plugins are allowed to read or write to other CPUs that aren't currently in the context of the plugin. Reviewed-by: Pierrick Bouvier Signed-off-by: Rowan Hart Message-ID: <20250624175351.440780-5-rowanbhart@gmail.com> Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-9-alex.bennee@linaro.org> --- include/qemu/qemu-plugin.h | 21 +++++++++++++++++++++ plugins/api.c | 18 ++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 9c9ebf6ce0..4167c46c2a 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -958,6 +958,27 @@ QEMU_PLUGIN_API bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *data, size_t len); +/** + * qemu_plugin_write_memory_vaddr() - write to memory using a virtual address + * + * @addr: A virtual address to write to + * @data: A byte array containing the data to write + * + * The contents of @data will be written to memory starting at the virtual + * address @addr. + * + * This function does not guarantee consistency of writes, nor does it ensure + * that pending writes are flushed either before or after the write takes place, + * so callers should take care to only call this function in vCPU context (i.e. + * in callbacks) and avoid depending on the existence of data written using this + * function which may be overwritten afterward. + * + * Returns true on success and false on failure. + */ +QEMU_PLUGIN_API +bool qemu_plugin_write_memory_vaddr(uint64_t addr, + GByteArray *data); + /** * qemu_plugin_scoreboard_new() - alloc a new scoreboard * diff --git a/plugins/api.c b/plugins/api.c index 3f04399c26..1f64a9ea64 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -476,6 +476,24 @@ bool qemu_plugin_read_memory_vaddr(uint64_t addr, GByteArray *data, size_t len) return true; } +bool qemu_plugin_write_memory_vaddr(uint64_t addr, GByteArray *data) +{ + g_assert(current_cpu); + + if (data->len == 0) { + return false; + } + + int result = cpu_memory_rw_debug(current_cpu, addr, data->data, + data->len, true); + + if (result < 0) { + return false; + } + + return true; +} + struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size) { return plugin_scoreboard_new(element_size); From 30424b8d4299d7dc50f90a6909e41a3c7ce94ccb Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Fri, 27 Jun 2025 12:25:05 +0100 Subject: [PATCH 1776/2760] plugins: Add memory hardware address read/write API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds functions to the plugins API to allow plugins to read and write memory via hardware addresses. The functions use the current address space of the current CPU in order to avoid exposing address space information to users. A later patch may want to add a function to permit a specified address space, for example to facilitate architecture-specific plugins that want to operate on them, for example reading ARM secure memory. Reviewed-by: Pierrick Bouvier Signed-off-by: Rowan Hart Message-ID: <20250624175351.440780-6-rowanbhart@gmail.com> Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-10-alex.bennee@linaro.org> --- include/qemu/qemu-plugin.h | 93 ++++++++++++++++++++++++++++++++++++ plugins/api.c | 97 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 190 insertions(+) diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 4167c46c2a..5eecdccc67 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -979,6 +979,99 @@ QEMU_PLUGIN_API bool qemu_plugin_write_memory_vaddr(uint64_t addr, GByteArray *data); +/** + * enum qemu_plugin_hwaddr_operation_result - result of a memory operation + * + * @QEMU_PLUGIN_HWADDR_OPERATION_OK: hwaddr operation succeeded + * @QEMU_PLUGIN_HWADDR_OPERATION_ERROR: unexpected error occurred + * @QEMU_PLUGIN_HWADDR_OPERATION_DEVICE_ERROR: error in memory device + * @QEMU_PLUGIN_HWADDR_OPERATION_ACCESS_DENIED: permission error + * @QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS: address was invalid + * @QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS_SPACE: invalid address space + */ +enum qemu_plugin_hwaddr_operation_result { + QEMU_PLUGIN_HWADDR_OPERATION_OK, + QEMU_PLUGIN_HWADDR_OPERATION_ERROR, + QEMU_PLUGIN_HWADDR_OPERATION_DEVICE_ERROR, + QEMU_PLUGIN_HWADDR_OPERATION_ACCESS_DENIED, + QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS, + QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS_SPACE, +}; + +/** + * qemu_plugin_read_memory_hwaddr() - read from memory using a hardware address + * + * @addr: The physical address to read from + * @data: A byte array to store data into + * @len: The number of bytes to read, starting from @addr + * + * @len bytes of data is read from the current memory space for the current + * vCPU starting at @addr and stored into @data. If @data is not large enough to + * hold @len bytes, it will be expanded to the necessary size, reallocating if + * necessary. @len must be greater than 0. + * + * This function does not ensure writes are flushed prior to reading, so + * callers should take care when calling this function in plugin callbacks to + * avoid attempting to read data which may not yet be written and should use + * the memory callback API instead. + * + * This function is only valid for softmmu targets. + * + * Returns a qemu_plugin_hwaddr_operation_result indicating the result of the + * operation. + */ +QEMU_PLUGIN_API +enum qemu_plugin_hwaddr_operation_result +qemu_plugin_read_memory_hwaddr(uint64_t addr, GByteArray *data, size_t len); + +/** + * qemu_plugin_write_memory_hwaddr() - write to memory using a hardware address + * + * @addr: A physical address to write to + * @data: A byte array containing the data to write + * + * The contents of @data will be written to memory starting at the hardware + * address @addr in the current address space for the current vCPU. + * + * This function does not guarantee consistency of writes, nor does it ensure + * that pending writes are flushed either before or after the write takes place, + * so callers should take care when calling this function in plugin callbacks to + * avoid depending on the existence of data written using this function which + * may be overwritten afterward. In addition, this function requires that the + * pages containing the address are not locked. Practically, this means that you + * should not write instruction memory in a current translation block inside a + * callback registered with qemu_plugin_register_vcpu_tb_trans_cb. + * + * You can, for example, write instruction memory in a current translation block + * in a callback registered with qemu_plugin_register_vcpu_tb_exec_cb, although + * be aware that the write will not be flushed until after the translation block + * has finished executing. In general, this function should be used to write + * data memory or to patch code at a known address, not in a current translation + * block. + * + * This function is only valid for softmmu targets. + * + * Returns a qemu_plugin_hwaddr_operation_result indicating the result of the + * operation. + */ +QEMU_PLUGIN_API +enum qemu_plugin_hwaddr_operation_result +qemu_plugin_write_memory_hwaddr(uint64_t addr, GByteArray *data); + +/** + * qemu_plugin_translate_vaddr() - translate virtual address for current vCPU + * + * @vaddr: virtual address to translate + * @hwaddr: pointer to store the physical address + * + * This function is only valid in vCPU context (i.e. in callbacks) and is only + * valid for softmmu targets. + * + * Returns true on success and false on failure. + */ +QEMU_PLUGIN_API +bool qemu_plugin_translate_vaddr(uint64_t vaddr, uint64_t *hwaddr); + /** * qemu_plugin_scoreboard_new() - alloc a new scoreboard * diff --git a/plugins/api.c b/plugins/api.c index 1f64a9ea64..eac04cc1f6 100644 --- a/plugins/api.c +++ b/plugins/api.c @@ -39,6 +39,7 @@ #include "qemu/main-loop.h" #include "qemu/plugin.h" #include "qemu/log.h" +#include "system/memory.h" #include "tcg/tcg.h" #include "exec/gdbstub.h" #include "exec/target_page.h" @@ -494,6 +495,102 @@ bool qemu_plugin_write_memory_vaddr(uint64_t addr, GByteArray *data) return true; } +enum qemu_plugin_hwaddr_operation_result +qemu_plugin_read_memory_hwaddr(hwaddr addr, GByteArray *data, size_t len) +{ +#ifdef CONFIG_SOFTMMU + if (len == 0) { + return QEMU_PLUGIN_HWADDR_OPERATION_ERROR; + } + + g_assert(current_cpu); + + + int as_idx = cpu_asidx_from_attrs(current_cpu, MEMTXATTRS_UNSPECIFIED); + AddressSpace *as = cpu_get_address_space(current_cpu, as_idx); + + if (as == NULL) { + return QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS_SPACE; + } + + g_byte_array_set_size(data, len); + MemTxResult res = address_space_rw(as, addr, + MEMTXATTRS_UNSPECIFIED, data->data, + data->len, false); + + switch (res) { + case MEMTX_OK: + return QEMU_PLUGIN_HWADDR_OPERATION_OK; + case MEMTX_ERROR: + return QEMU_PLUGIN_HWADDR_OPERATION_DEVICE_ERROR; + case MEMTX_DECODE_ERROR: + return QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS; + case MEMTX_ACCESS_ERROR: + return QEMU_PLUGIN_HWADDR_OPERATION_ACCESS_DENIED; + default: + return QEMU_PLUGIN_HWADDR_OPERATION_ERROR; + } +#else + return QEMU_PLUGIN_HWADDR_OPERATION_ERROR; +#endif +} + +enum qemu_plugin_hwaddr_operation_result +qemu_plugin_write_memory_hwaddr(hwaddr addr, GByteArray *data) +{ +#ifdef CONFIG_SOFTMMU + if (data->len == 0) { + return QEMU_PLUGIN_HWADDR_OPERATION_ERROR; + } + + g_assert(current_cpu); + + int as_idx = cpu_asidx_from_attrs(current_cpu, MEMTXATTRS_UNSPECIFIED); + AddressSpace *as = cpu_get_address_space(current_cpu, as_idx); + + if (as == NULL) { + return QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS_SPACE; + } + + MemTxResult res = address_space_rw(as, addr, + MEMTXATTRS_UNSPECIFIED, data->data, + data->len, true); + switch (res) { + case MEMTX_OK: + return QEMU_PLUGIN_HWADDR_OPERATION_OK; + case MEMTX_ERROR: + return QEMU_PLUGIN_HWADDR_OPERATION_DEVICE_ERROR; + case MEMTX_DECODE_ERROR: + return QEMU_PLUGIN_HWADDR_OPERATION_INVALID_ADDRESS; + case MEMTX_ACCESS_ERROR: + return QEMU_PLUGIN_HWADDR_OPERATION_ACCESS_DENIED; + default: + return QEMU_PLUGIN_HWADDR_OPERATION_ERROR; + } +#else + return QEMU_PLUGIN_HWADDR_OPERATION_ERROR; +#endif +} + +bool qemu_plugin_translate_vaddr(uint64_t vaddr, uint64_t *hwaddr) +{ +#ifdef CONFIG_SOFTMMU + g_assert(current_cpu); + + uint64_t res = cpu_get_phys_page_debug(current_cpu, vaddr); + + if (res == (uint64_t)-1) { + return false; + } + + *hwaddr = res | (vaddr & ~TARGET_PAGE_MASK); + + return true; +#else + return false; +#endif +} + struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size) { return plugin_scoreboard_new(element_size); From 5ea2abf07ccd661c434249c4d08fce9c0f8529a1 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Fri, 27 Jun 2025 12:25:06 +0100 Subject: [PATCH 1777/2760] tests/tcg: Remove copy-pasted notes and from i386 and add x86_64 system tests to tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The x86_64-softmmu Makefile seems to have been copy-pasted from the i386 Makefile at some point in the past. Cleaning up a vestigial unused variable and removing some outdated comments. Signed-off-by: Rowan Hart Message-ID: <20250624175351.440780-7-rowanbhart@gmail.com> Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-11-alex.bennee@linaro.org> --- tests/tcg/x86_64/Makefile.softmmu-target | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/tests/tcg/x86_64/Makefile.softmmu-target b/tests/tcg/x86_64/Makefile.softmmu-target index ef6bcb4dc7..d3e09708a5 100644 --- a/tests/tcg/x86_64/Makefile.softmmu-target +++ b/tests/tcg/x86_64/Makefile.softmmu-target @@ -1,13 +1,11 @@ # -# x86 system tests -# -# This currently builds only for i386. The common C code is built -# with standard compiler flags however so we can support both by -# adding additional boot files for x86_64. +# x86_64 system tests # -I386_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/i386/system X64_SYSTEM_SRC=$(SRC_PATH)/tests/tcg/x86_64/system +X64_SYSTEM_TESTS=$(patsubst $(X64_SYSTEM_SRC)/%.c, %, $(wildcard $(X64_SYSTEM_SRC)/*.c)) + +VPATH+=$(X64_SYSTEM_SRC) # These objects provide the basic boot code and helper functions for all tests CRT_OBJS=boot.o @@ -18,7 +16,7 @@ LDFLAGS=-Wl,-T$(LINK_SCRIPT) -Wl,-melf_x86_64 CFLAGS+=-nostdlib -ggdb -O0 $(MINILIB_INC) LDFLAGS+=-static -nostdlib $(CRT_OBJS) $(MINILIB_OBJS) -lgcc -TESTS+=$(MULTIARCH_TESTS) +TESTS+=$(MULTIARCH_TESTS) $(X64_SYSTEM_TESTS) EXTRA_RUNS+=$(MULTIARCH_RUNS) # building head blobs From 71d337943870b30ed8a137fb14dbf4680ba9ccc7 Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Fri, 27 Jun 2025 12:25:07 +0100 Subject: [PATCH 1778/2760] plugins: Add patcher plugin and test MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds a plugin that exercises the virtual and hardware memory read-write API functions added in a previous patch. The plugin takes a target and patch byte sequence, and will overwrite any instruction matching the target byte sequence with the patch. Reviewed-by: Pierrick Bouvier Signed-off-by: Rowan Hart Message-ID: <20250624175351.440780-8-rowanbhart@gmail.com> [AJB: tweak Makefile, use uintptr_t for pointer stuffing] Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-12-alex.bennee@linaro.org> --- tests/tcg/Makefile.target | 7 +- tests/tcg/plugins/meson.build | 2 +- tests/tcg/plugins/patch.c | 251 ++++++++++++++++++++++ tests/tcg/x86_64/Makefile.softmmu-target | 9 + tests/tcg/x86_64/system/patch-target.c | 22 ++ tests/tcg/x86_64/system/validate-patch.py | 39 ++++ 6 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 tests/tcg/plugins/patch.c create mode 100644 tests/tcg/x86_64/system/patch-target.c create mode 100755 tests/tcg/x86_64/system/validate-patch.py diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 95ff76ea44..af68f11664 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -151,7 +151,12 @@ ifeq ($(CONFIG_PLUGIN),y) PLUGIN_SRC=$(SRC_PATH)/tests/tcg/plugins PLUGIN_LIB=../plugins VPATH+=$(PLUGIN_LIB) -PLUGINS=$(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c))) +# Some plugins need to be disabled for all tests to avoid exponential explosion. +# For example, libpatch.so only needs to run against the arch-specific patch +# target test, so we explicitly run it in the arch-specific Makefile. +DISABLE_PLUGINS=libpatch.so +PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ + $(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c)))) # We need to ensure expand the run-plugin-TEST-with-PLUGIN # pre-requistes manually here as we can't use stems to handle it. We diff --git a/tests/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build index 029342282a..61a007d9e7 100644 --- a/tests/tcg/plugins/meson.build +++ b/tests/tcg/plugins/meson.build @@ -1,6 +1,6 @@ t = [] if get_option('plugins') - foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'reset', 'syscall'] + foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'reset', 'syscall', 'patch'] if host_os == 'windows' t += shared_module(i, files(i + '.c') + '../../../contrib/plugins/win32_linker.c', include_directories: '../../../include/qemu', diff --git a/tests/tcg/plugins/patch.c b/tests/tcg/plugins/patch.c new file mode 100644 index 0000000000..111c5c1f16 --- /dev/null +++ b/tests/tcg/plugins/patch.c @@ -0,0 +1,251 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This plugin patches instructions matching a pattern to a different + * instruction as they execute + * + */ + +#include "glib.h" +#include "glibconfig.h" + +#include +#include +#include + +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; + +static bool use_hwaddr; +static GByteArray *target_data; +static GByteArray *patch_data; + +/** + * Parse a string of hexadecimal digits into a GByteArray. The string must be + * even length + */ +static GByteArray *str_to_bytes(const char *str) +{ + size_t len = strlen(str); + + if (len == 0 || len % 2 != 0) { + return NULL; + } + + GByteArray *bytes = g_byte_array_new(); + char byte[3] = {0}; + guint8 value = 0; + + for (size_t i = 0; i < len; i += 2) { + byte[0] = str[i]; + byte[1] = str[i + 1]; + value = (guint8)g_ascii_strtoull(byte, NULL, 16); + g_byte_array_append(bytes, &value, 1); + } + + return bytes; +} + +static void patch_hwaddr(unsigned int vcpu_index, void *userdata) +{ + uintptr_t addr = (uintptr_t) userdata; + g_autoptr(GString) str = g_string_new(NULL); + g_string_printf(str, "patching: @0x%" + PRIxPTR "\n", + addr); + qemu_plugin_outs(str->str); + + enum qemu_plugin_hwaddr_operation_result result = + qemu_plugin_write_memory_hwaddr(addr, patch_data); + + + if (result != QEMU_PLUGIN_HWADDR_OPERATION_OK) { + g_autoptr(GString) errmsg = g_string_new(NULL); + g_string_printf(errmsg, "Failed to write memory: %d\n", result); + qemu_plugin_outs(errmsg->str); + return; + } + + GByteArray *read_data = g_byte_array_new(); + + result = qemu_plugin_read_memory_hwaddr(addr, read_data, + patch_data->len); + + qemu_plugin_outs("Reading memory...\n"); + + if (result != QEMU_PLUGIN_HWADDR_OPERATION_OK) { + g_autoptr(GString) errmsg = g_string_new(NULL); + g_string_printf(errmsg, "Failed to read memory: %d\n", result); + qemu_plugin_outs(errmsg->str); + return; + } + + if (memcmp(patch_data->data, read_data->data, patch_data->len) != 0) { + qemu_plugin_outs("Failed to read back written data\n"); + } + + qemu_plugin_outs("Success!\n"); + + return; +} + +static void patch_vaddr(unsigned int vcpu_index, void *userdata) +{ + uintptr_t addr = (uintptr_t) userdata; + uint64_t hwaddr = 0; + if (!qemu_plugin_translate_vaddr(addr, &hwaddr)) { + qemu_plugin_outs("Failed to translate vaddr\n"); + return; + } + g_autoptr(GString) str = g_string_new(NULL); + g_string_printf(str, "patching: @0x%" + PRIxPTR " hw: @0x%" PRIx64 "\n", + addr, hwaddr); + qemu_plugin_outs(str->str); + + qemu_plugin_outs("Writing memory (vaddr)...\n"); + + if (!qemu_plugin_write_memory_vaddr(addr, patch_data)) { + qemu_plugin_outs("Failed to write memory\n"); + return; + } + + qemu_plugin_outs("Reading memory (vaddr)...\n"); + + g_autoptr(GByteArray) read_data = g_byte_array_new(); + + if (!qemu_plugin_read_memory_vaddr(addr, read_data, patch_data->len)) { + qemu_plugin_outs("Failed to read memory\n"); + return; + } + + if (memcmp(patch_data->data, read_data->data, patch_data->len) != 0) { + qemu_plugin_outs("Failed to read back written data\n"); + } + + qemu_plugin_outs("Success!\n"); + + return; +} + +/* + * Callback on translation of a translation block. + */ +static void vcpu_tb_trans_cb(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) +{ + g_autoptr(GByteArray) insn_data = g_byte_array_new(); + uintptr_t addr = 0; + + for (size_t i = 0; i < qemu_plugin_tb_n_insns(tb); i++) { + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); + uint64_t vaddr = qemu_plugin_insn_vaddr(insn); + + if (use_hwaddr) { + uint64_t hwaddr = 0; + if (!qemu_plugin_translate_vaddr(vaddr, &hwaddr)) { + qemu_plugin_outs("Failed to translate vaddr\n"); + continue; + } + /* + * As we cannot emulate 64 bit systems on 32 bit hosts we + * should never see the top bits set, hence we can safely + * cast to uintptr_t. + */ + g_assert(hwaddr <= UINTPTR_MAX); + addr = (uintptr_t) hwaddr; + } else { + g_assert(vaddr <= UINTPTR_MAX); + addr = (uintptr_t) vaddr; + } + + g_byte_array_set_size(insn_data, qemu_plugin_insn_size(insn)); + qemu_plugin_insn_data(insn, insn_data->data, insn_data->len); + + if (insn_data->len >= target_data->len && + !memcmp(insn_data->data, target_data->data, + MIN(target_data->len, insn_data->len))) { + if (use_hwaddr) { + qemu_plugin_register_vcpu_tb_exec_cb(tb, patch_hwaddr, + QEMU_PLUGIN_CB_NO_REGS, + (void *) addr); + } else { + qemu_plugin_register_vcpu_tb_exec_cb(tb, patch_vaddr, + QEMU_PLUGIN_CB_NO_REGS, + (void *) addr); + } + } + } +} + +static void usage(void) +{ + fprintf(stderr, "Usage: ,target=,patch=" + "[,use_hwaddr=true|false]"); +} + +/* + * Called when the plugin is installed + */ +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, + const qemu_info_t *info, int argc, + char **argv) +{ + + use_hwaddr = true; + target_data = NULL; + patch_data = NULL; + + if (argc > 4) { + usage(); + return -1; + } + + for (size_t i = 0; i < argc; i++) { + char *opt = argv[i]; + g_auto(GStrv) tokens = g_strsplit(opt, "=", 2); + if (g_strcmp0(tokens[0], "use_hwaddr") == 0) { + if (!qemu_plugin_bool_parse(tokens[0], tokens[1], &use_hwaddr)) { + fprintf(stderr, + "Failed to parse boolean argument use_hwaddr\n"); + return -1; + } + } else if (g_strcmp0(tokens[0], "target") == 0) { + target_data = str_to_bytes(tokens[1]); + if (!target_data) { + fprintf(stderr, + "Failed to parse target bytes.\n"); + return -1; + } + } else if (g_strcmp0(tokens[0], "patch") == 0) { + patch_data = str_to_bytes(tokens[1]); + if (!patch_data) { + fprintf(stderr, "Failed to parse patch bytes.\n"); + return -1; + } + } else { + fprintf(stderr, "Unknown argument: %s\n", tokens[0]); + usage(); + return -1; + } + } + + if (!target_data) { + fprintf(stderr, "target argument is required\n"); + usage(); + return -1; + } + + if (!patch_data) { + fprintf(stderr, "patch argument is required\n"); + usage(); + return -1; + } + + if (target_data->len != patch_data->len) { + fprintf(stderr, "Target and patch data must be the same length\n"); + return -1; + } + + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans_cb); + + return 0; +} diff --git a/tests/tcg/x86_64/Makefile.softmmu-target b/tests/tcg/x86_64/Makefile.softmmu-target index d3e09708a5..3e30ca9307 100644 --- a/tests/tcg/x86_64/Makefile.softmmu-target +++ b/tests/tcg/x86_64/Makefile.softmmu-target @@ -33,3 +33,12 @@ memory: CFLAGS+=-DCHECK_UNALIGNED=1 # Running QEMU_OPTS+=-device isa-debugcon,chardev=output -device isa-debug-exit,iobase=0xf4,iosize=0x4 -kernel + +ifeq ($(CONFIG_PLUGIN),y) +run-plugin-patch-target-with-libpatch.so: \ + PLUGIN_ARGS=$(COMMA)target=ffc0$(COMMA)patch=9090$(COMMA)use_hwaddr=true +run-plugin-patch-target-with-libpatch.so: \ + CHECK_PLUGIN_OUTPUT_COMMAND=$(X64_SYSTEM_SRC)/validate-patch.py $@.out +run-plugin-patch-target-with-libpatch.so: patch-target libpatch.so +EXTRA_RUNS+=run-plugin-patch-target-with-libpatch.so +endif diff --git a/tests/tcg/x86_64/system/patch-target.c b/tests/tcg/x86_64/system/patch-target.c new file mode 100644 index 0000000000..8c2b6f4ba7 --- /dev/null +++ b/tests/tcg/x86_64/system/patch-target.c @@ -0,0 +1,22 @@ +/* + * SPDX-License-Identifier: GPL-2.0-or-later + * + * This test target increments a value 100 times. The patcher converts the + * inc instruction to a nop, so it only increments the value once. + * + */ +#include + +int main(void) +{ + ml_printf("Running test...\n"); + unsigned int x = 0; + for (int i = 0; i < 100; i++) { + asm volatile ( + "inc %[x]" + : [x] "+a" (x) + ); + } + ml_printf("Value: %d\n", x); + return 0; +} diff --git a/tests/tcg/x86_64/system/validate-patch.py b/tests/tcg/x86_64/system/validate-patch.py new file mode 100755 index 0000000000..700950eae5 --- /dev/null +++ b/tests/tcg/x86_64/system/validate-patch.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# +# validate-patch.py: check the patch applies +# +# This program takes two inputs: +# - the plugin output +# - the binary output +# +# Copyright (C) 2024 +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import sys +from argparse import ArgumentParser + +def main() -> None: + """ + Process the arguments, injest the program and plugin out and + verify they match up and report if they do not. + """ + parser = ArgumentParser(description="Validate patch") + parser.add_argument('test_output', + help="The output from the test itself") + parser.add_argument('plugin_output', + help="The output from plugin") + args = parser.parse_args() + + with open(args.test_output, 'r') as f: + test_data = f.read() + with open(args.plugin_output, 'r') as f: + plugin_data = f.read() + if "Value: 1" in test_data: + sys.exit(0) + else: + sys.exit(1) + +if __name__ == "__main__": + main() + From 137d2f947f7ad5ca3190e481b2b0b210386f5b4e Mon Sep 17 00:00:00 2001 From: Rowan Hart Date: Fri, 27 Jun 2025 12:25:08 +0100 Subject: [PATCH 1779/2760] plugins: Update plugin version and add notes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch updates the plugin version to gate new APIs and adds notes describing what has been added. Reviewed-by: Pierrick Bouvier Signed-off-by: Rowan Hart Message-ID: <20250624175351.440780-9-rowanbhart@gmail.com> Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-13-alex.bennee@linaro.org> --- include/qemu/qemu-plugin.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h index 5eecdccc67..c450106af1 100644 --- a/include/qemu/qemu-plugin.h +++ b/include/qemu/qemu-plugin.h @@ -65,11 +65,18 @@ typedef uint64_t qemu_plugin_id_t; * * version 4: * - added qemu_plugin_read_memory_vaddr + * + * version 5: + * - added qemu_plugin_write_memory_vaddr + * - added qemu_plugin_read_memory_hwaddr + * - added qemu_plugin_write_memory_hwaddr + * - added qemu_plugin_write_register + * - added qemu_plugin_translate_vaddr */ extern QEMU_PLUGIN_EXPORT int qemu_plugin_version; -#define QEMU_PLUGIN_VERSION 4 +#define QEMU_PLUGIN_VERSION 5 /** * struct qemu_info_t - system information for plugins From ef10cb656c9c9349091cf7ba67bf120d230f0435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 27 Jun 2025 12:25:09 +0100 Subject: [PATCH 1780/2760] MAINTAINERS: add myself to virtio-gpu for Odd Fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seeing as I've taken a few patches to here now I might as well put myself forward to maintain virtio-gpu. I've marked it as Odd Fixes as it is not my core focus. If someone with more GPU experience comes forward we can always update again. Reviewed-by: Markus Armbruster Message-ID: <20250603110204.838117-8-alex.bennee@linaro.org> Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-14-alex.bennee@linaro.org> --- MAINTAINERS | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 850588fb64..52f0164edf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2684,7 +2684,8 @@ F: hw/display/ramfb*.c F: include/hw/display/ramfb.h virtio-gpu -S: Orphan +M: Alex Bennée +S: Odd Fixes F: hw/display/virtio-gpu* F: hw/display/virtio-vga.* F: include/hw/virtio/virtio-gpu.h From 2882dfa57faad19c9f622eea67614b1215c24d8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 27 Jun 2025 12:25:10 +0100 Subject: [PATCH 1781/2760] MAINTAINERS: add Akihiko and Dmitry as reviewers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks for volunteering to help. Cc: Akihiko Odaki Cc: Dmitry Osipenko Reviewed-by: Markus Armbruster Reviewed-by: Philippe Mathieu-Daudé Acked-by: Michael S. Tsirkin Message-ID: <20250603110204.838117-9-alex.bennee@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250627112512.1880708-15-alex.bennee@linaro.org> --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 52f0164edf..3932a6e56f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2685,6 +2685,8 @@ F: include/hw/display/ramfb.h virtio-gpu M: Alex Bennée +R: Akihiko Odaki +R: Dmitry Osipenko S: Odd Fixes F: hw/display/virtio-gpu* F: hw/display/virtio-vga.* From 1fa2ffdbec55d84326e22f046bc3e26322836f5a Mon Sep 17 00:00:00 2001 From: Yiwei Zhang Date: Fri, 27 Jun 2025 12:25:11 +0100 Subject: [PATCH 1782/2760] virtio-gpu: support context init multiple timeline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Venus and later native contexts have their own fence context along with multiple timelines within. Fences wtih VIRTIO_GPU_FLAG_INFO_RING_IDX in the flags must be dispatched to be created on the target context. Fence signaling also has to be handled on the specific timeline within that target context. Before this change, venus fencing is completely broken if the host driver doesn't support implicit fencing with external memory objects. Frames can go backwards along with random artifacts on screen if the host driver doesn't attach an implicit fence to the render target. The symptom could be hidden by certain guest wsi backend that waits on a venus native VkFence object for the actual payload with limited present modes or under special configs. e.g. x11 mailbox or xwayland. After this change, everything related to venus fencing starts making sense. Confirmed this via guest and host side perfetto tracing. Cc: qemu-stable@nongnu.org Fixes: 94d0ea1c1928 ("virtio-gpu: Support Venus context") Signed-off-by: Yiwei Zhang Reviewed-by: Dmitry Osipenko Message-Id: <20250518152651.334115-1-zzyiwei@gmail.com> [AJB: remove version history from commit message] Tested-by: Dmitry Osipenko Signed-off-by: Alex Bennée Reviewed-by: Akihiko Odaki Message-ID: <20250627112512.1880708-16-alex.bennee@linaro.org> --- hw/display/virtio-gpu-virgl.c | 44 +++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/hw/display/virtio-gpu-virgl.c b/hw/display/virtio-gpu-virgl.c index 145a0b3879..94ddc01f91 100644 --- a/hw/display/virtio-gpu-virgl.c +++ b/hw/display/virtio-gpu-virgl.c @@ -970,6 +970,15 @@ void virtio_gpu_virgl_process_cmd(VirtIOGPU *g, } trace_virtio_gpu_fence_ctrl(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type); +#if VIRGL_VERSION_MAJOR >= 1 + if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_INFO_RING_IDX) { + virgl_renderer_context_create_fence(cmd->cmd_hdr.ctx_id, + VIRGL_RENDERER_FENCE_FLAG_MERGEABLE, + cmd->cmd_hdr.ring_idx, + cmd->cmd_hdr.fence_id); + return; + } +#endif virgl_renderer_create_fence(cmd->cmd_hdr.fence_id, cmd->cmd_hdr.type); } @@ -983,6 +992,11 @@ static void virgl_write_fence(void *opaque, uint32_t fence) * the guest can end up emitting fences out of order * so we should check all fenced cmds not just the first one. */ +#if VIRGL_VERSION_MAJOR >= 1 + if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_INFO_RING_IDX) { + continue; + } +#endif if (cmd->cmd_hdr.fence_id > fence) { continue; } @@ -997,6 +1011,29 @@ static void virgl_write_fence(void *opaque, uint32_t fence) } } +#if VIRGL_VERSION_MAJOR >= 1 +static void virgl_write_context_fence(void *opaque, uint32_t ctx_id, + uint32_t ring_idx, uint64_t fence_id) { + VirtIOGPU *g = opaque; + struct virtio_gpu_ctrl_command *cmd, *tmp; + + QTAILQ_FOREACH_SAFE(cmd, &g->fenceq, next, tmp) { + if (cmd->cmd_hdr.flags & VIRTIO_GPU_FLAG_INFO_RING_IDX && + cmd->cmd_hdr.ctx_id == ctx_id && cmd->cmd_hdr.ring_idx == ring_idx && + cmd->cmd_hdr.fence_id <= fence_id) { + trace_virtio_gpu_fence_resp(cmd->cmd_hdr.fence_id); + virtio_gpu_ctrl_response_nodata(g, cmd, VIRTIO_GPU_RESP_OK_NODATA); + QTAILQ_REMOVE(&g->fenceq, cmd, next); + g_free(cmd); + g->inflight--; + if (virtio_gpu_stats_enabled(g->parent_obj.conf)) { + trace_virtio_gpu_dec_inflight_fences(g->inflight); + } + } + } +} +#endif + static virgl_renderer_gl_context virgl_create_context(void *opaque, int scanout_idx, struct virgl_renderer_gl_ctx_param *params) @@ -1031,11 +1068,18 @@ static int virgl_make_context_current(void *opaque, int scanout_idx, } static struct virgl_renderer_callbacks virtio_gpu_3d_cbs = { +#if VIRGL_VERSION_MAJOR >= 1 + .version = 3, +#else .version = 1, +#endif .write_fence = virgl_write_fence, .create_gl_context = virgl_create_context, .destroy_gl_context = virgl_destroy_context, .make_current = virgl_make_context_current, +#if VIRGL_VERSION_MAJOR >= 1 + .write_context_fence = virgl_write_context_fence, +#endif }; static void virtio_gpu_print_stats(void *opaque) From 1d6045148f90c7427648b73dd97d37c4a1ad7418 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Mon, 16 Jun 2025 11:47:30 -0400 Subject: [PATCH 1783/2760] MAINTAINERS: update docs file extensions (.txt -> .rst) The documentation tree has been converted to reStructuredText, but two entries in MAINTAINERS still point to the removed *.txt files. This prevents scripts/get_maintainer.pl from matching the documents. Update those entries to *.rst so the maintainer script works again. Related commits: 8472cc5dbe6 (Sep 2023): docs/specs/vmw_pvscsi-spec: Convert to rST 8e72ceee5cd (Jun 2022): Rename docs/specs/fw_cfg.txt to .rst Signed-off-by: Sean Wei Message-ID: <20250616.qemu.relocated.01@sean.taipei> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- MAINTAINERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 7128e0bc98..749799b0f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2541,7 +2541,7 @@ F: hw/net/net_tx_pkt* Vmware M: Dmitry Fleytman S: Maintained -F: docs/specs/vmw_pvscsi-spec.txt +F: docs/specs/vmw_pvscsi-spec.rst F: hw/display/vmware_vga.c F: hw/net/vmxnet* F: hw/scsi/vmw_pvscsi* @@ -2746,7 +2746,7 @@ Firmware configuration (fw_cfg) M: Philippe Mathieu-Daudé R: Gerd Hoffmann S: Supported -F: docs/specs/fw_cfg.txt +F: docs/specs/fw_cfg.rst F: hw/nvram/fw_cfg*.c F: stubs/fw_cfg.c F: include/hw/nvram/fw_cfg.h From 1ed84eb01f8f202191eb57a1bb4e1ce55f5164cf Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Mon, 16 Jun 2025 11:49:30 -0400 Subject: [PATCH 1784/2760] MAINTAINERS: fix vendor capitalization (Vmware -> VMware) "VMware" is the vendor's official spelling. Adjust the spelling in MAINTAINERS for consistency. Signed-off-by: Sean Wei Message-ID: <20250616.qemu.relocated.03@sean.taipei> Signed-off-by: Thomas Huth --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 749799b0f8..585f08e709 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2538,7 +2538,7 @@ F: net/eth.c F: hw/net/net_rx_pkt* F: hw/net/net_tx_pkt* -Vmware +VMware M: Dmitry Fleytman S: Maintained F: docs/specs/vmw_pvscsi-spec.rst From 8231feef53fb130b276797396749508d4fe1ec03 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Mon, 16 Jun 2025 11:50:10 -0400 Subject: [PATCH 1785/2760] MAINTAINERS: fix VMware filename typo (vwm -> vmw) The entry for the VMware PVSCSI spec uses "vwm" instead of "vmw", which does not match any file in the tree. Correct the path so scripts/get_maintainer.pl can match the file. Signed-off-by: Sean Wei Reviewed-by: Thomas Huth Message-ID: <20250616.qemu.relocated.04@sean.taipei> Signed-off-by: Thomas Huth --- MAINTAINERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MAINTAINERS b/MAINTAINERS index 585f08e709..8aa0fd33b7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2549,7 +2549,7 @@ F: pc-bios/efi-vmxnet3.rom F: pc-bios/vgabios-vmware.bin F: roms/config.vga-vmware F: tests/qtest/vmxnet3-test.c -F: docs/specs/vwm_pvscsi-spec.rst +F: docs/specs/vmw_pvscsi-spec.rst Rocker M: Jiri Pirko From e06cd791381383c6fa6041ad0758a86c5b1509e6 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Mon, 16 Jun 2025 11:50:50 -0400 Subject: [PATCH 1786/2760] treewide: update docs file extensions (.txt -> .rst) in comments Several source comments still refer to docs with the old .txt extension that were previously converted to reStructuredText. Update these references to use the correct .rst extensions to maintain accurate in-tree documentation pointers. No functional changes. Related commits: 50f8174c5c1 (Jul 2021): docs/specs/acpi_nvdimm: Convert to rST f054eb1c920 (Jul 2021): docs/specs/acpi_pci_hotplug: Convert to rST 912fb3678b8 (Sep 2023): docs/specs/vmgenid: Convert to rST bb1cff6ee04 (Sep 2023): docs/specs/ivshmem-spec: Convert to rST 55ff468f781 (Jan 2022): docs: Rename ppc-spapr-hotplug.txt to .rst Signed-off-by: Sean Wei Message-ID: <20250616.qemu.relocated.05@sean.taipei> Reviewed-by: Thomas Huth Reviewed-by: Harsh Prateek Bora Signed-off-by: Thomas Huth --- hw/acpi/nvdimm.c | 2 +- hw/acpi/pcihp.c | 2 +- hw/acpi/vmgenid.c | 6 +++--- hw/misc/ivshmem-flat.c | 2 +- hw/ppc/spapr.c | 2 +- include/hw/acpi/pcihp.h | 2 +- include/hw/misc/ivshmem-flat.h | 2 +- tests/qtest/vmgenid-test.c | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/acpi/nvdimm.c b/hw/acpi/nvdimm.c index 9ba90806f2..732d613ac0 100644 --- a/hw/acpi/nvdimm.c +++ b/hw/acpi/nvdimm.c @@ -535,7 +535,7 @@ nvdimm_dsm_no_payload(uint32_t func_ret_status, hwaddr dsm_mem_addr) #define NVDIMM_QEMU_RSVD_HANDLE_ROOT 0x10000 -/* Read FIT data, defined in docs/specs/acpi_nvdimm.txt. */ +/* Read FIT data, defined in docs/specs/acpi_nvdimm.rst. */ static void nvdimm_dsm_func_read_fit(NVDIMMState *state, NvdimmDsmIn *in, hwaddr dsm_mem_addr) { diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index aac90013d4..497281ae20 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -3,7 +3,7 @@ * * QEMU supports PCI hotplug via ACPI. This module * implements the interface between QEMU and the ACPI BIOS. - * Interface specification - see docs/specs/acpi_pci_hotplug.txt + * Interface specification - see docs/specs/acpi_pci_hotplug.rst * * Copyright (c) 2013, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com) * Copyright (c) 2006 Fabrice Bellard diff --git a/hw/acpi/vmgenid.c b/hw/acpi/vmgenid.c index fac3d6d97e..33c35c85dd 100644 --- a/hw/acpi/vmgenid.c +++ b/hw/acpi/vmgenid.c @@ -38,7 +38,7 @@ void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid, guid_le = qemu_uuid_bswap(vms->guid); /* The GUID is written at a fixed offset into the fw_cfg file * in order to implement the "OVMF SDT Header probe suppressor" - * see docs/specs/vmgenid.txt for more details + * see docs/specs/vmgenid.rst for more details */ g_array_insert_vals(guid, VMGENID_GUID_OFFSET, guid_le.data, ARRAY_SIZE(guid_le.data)); @@ -101,7 +101,7 @@ void vmgenid_build_acpi(VmGenIdState *vms, GArray *table_data, GArray *guid, * < 4GB, but write 64 bits anyway. * The address that is patched in is offset in order to implement * the "OVMF SDT Header probe suppressor" - * see docs/specs/vmgenid.txt for more details. + * see docs/specs/vmgenid.rst for more details. */ bios_linker_loader_write_pointer(linker, VMGENID_ADDR_FW_CFG_FILE, 0, sizeof(uint64_t), @@ -153,7 +153,7 @@ static void vmgenid_update_guest(VmGenIdState *vms) guid_le = qemu_uuid_bswap(vms->guid); /* The GUID is written at a fixed offset into the fw_cfg file * in order to implement the "OVMF SDT Header probe suppressor" - * see docs/specs/vmgenid.txt for more details. + * see docs/specs/vmgenid.rst for more details. */ cpu_physical_memory_write(vmgenid_addr, guid_le.data, sizeof(guid_le.data)); diff --git a/hw/misc/ivshmem-flat.c b/hw/misc/ivshmem-flat.c index be28c24d73..fe4be6be17 100644 --- a/hw/misc/ivshmem-flat.c +++ b/hw/misc/ivshmem-flat.c @@ -362,7 +362,7 @@ static bool ivshmem_flat_connect_server(DeviceState *dev, Error **errp) * * ivshmem_flat_recv_msg() calls return 'msg' and 'fd'. * - * See ./docs/specs/ivshmem-spec.txt for details on the protocol. + * See docs/specs/ivshmem-spec.rst for details on the protocol. */ /* Step 0 */ diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 702f774cda..08615f6c90 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -577,7 +577,7 @@ static int spapr_dt_dynamic_memory(SpaprMachineState *spapr, void *fdt, /* * Adds ibm,dynamic-reconfiguration-memory node. - * Refer to docs/specs/ppc-spapr-hotplug.txt for the documentation + * Refer to docs/specs/ppc-spapr-hotplug.rst for the documentation * of this device tree node. */ static int spapr_dt_dynamic_reconfiguration_memory(SpaprMachineState *spapr, diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index a97904bada..cdc0cb8e43 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -3,7 +3,7 @@ * * QEMU supports PCI hotplug via ACPI. This module * implements the interface between QEMU and the ACPI BIOS. - * Interface specification - see docs/specs/acpi_pci_hotplug.txt + * Interface specification - see docs/specs/acpi_pci_hotplug.rst * * Copyright (c) 2013, Red Hat Inc, Michael S. Tsirkin (mst@redhat.com) * Copyright (c) 2006 Fabrice Bellard diff --git a/include/hw/misc/ivshmem-flat.h b/include/hw/misc/ivshmem-flat.h index 09bc3abcad..3eca99004e 100644 --- a/include/hw/misc/ivshmem-flat.h +++ b/include/hw/misc/ivshmem-flat.h @@ -36,7 +36,7 @@ typedef struct IvshmemFTState IvshmemFTState; DECLARE_INSTANCE_CHECKER(IvshmemFTState, IVSHMEM_FLAT, TYPE_IVSHMEM_FLAT) -/* Ivshmem registers. See ./docs/specs/ivshmem-spec.txt for details. */ +/* Ivshmem registers. See docs/specs/ivshmem-spec.rst for details. */ enum ivshmem_registers { INTMASK = 0, INTSTATUS = 4, diff --git a/tests/qtest/vmgenid-test.c b/tests/qtest/vmgenid-test.c index e613374665..33e96b7c55 100644 --- a/tests/qtest/vmgenid-test.c +++ b/tests/qtest/vmgenid-test.c @@ -61,7 +61,7 @@ static uint32_t acpi_find_vgia(QTestState *qts) /* The GUID is written at a fixed offset into the fw_cfg file * in order to implement the "OVMF SDT Header probe suppressor" - * see docs/specs/vmgenid.txt for more details + * see docs/specs/vmgenid.rst for more details */ guid_offset = le32_to_cpu(vgia_val) + VMGENID_GUID_OFFSET; g_free(table_aml); From c95b08106b4b47d511a91a1c41e79681588bef59 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Mon, 16 Jun 2025 11:51:30 -0400 Subject: [PATCH 1787/2760] treewide: fix paths for relocated files in comments After the docs directory restructuring, several comments refer to paths that no longer exist. Replace these references to the current file locations so readers can find the correct files. Related commits --------------- 189c099f75f (Jul 2021) docs: collect the disparate device emulation docs into one section Rename docs/system/{ => devices}/nvme.rst 5f4c96b779f (Feb 2023) docs/system/loongarch: update loongson3.rst and rename it to virt.rst Rename docs/system/loongarch/{loongson3.rst => virt.rst} fe0007f3c1d (Sep 2023) exec: Rename cpu.c -> cpu-target.c Rename cpus-common.c => cpu-common.c 42fa9665e59 (Apr 2025) exec: Restrict 'cpu_ldst.h' to accel/tcg/ Rename include/{exec/cpu_ldst.h => accel/tcg/cpu-ldst.h} Signed-off-by: Sean Wei Message-ID: <20250616.qemu.relocated.06@sean.taipei> Reviewed-by: Thomas Huth Signed-off-by: Thomas Huth --- docs/spin/tcg-exclusive.promela | 4 ++-- hw/nvme/ctrl.c | 2 +- target/arm/cpu.c | 2 +- target/loongarch/README | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/spin/tcg-exclusive.promela b/docs/spin/tcg-exclusive.promela index c91cfca9f7..1d03af850b 100644 --- a/docs/spin/tcg-exclusive.promela +++ b/docs/spin/tcg-exclusive.promela @@ -1,6 +1,6 @@ /* * This model describes the implementation of exclusive sections in - * cpus-common.c (start_exclusive, end_exclusive, cpu_exec_start, + * cpu-common.c (start_exclusive, end_exclusive, cpu_exec_start, * cpu_exec_end). * * Author: Paolo Bonzini @@ -65,7 +65,7 @@ } #define COND_BROADCAST(c) c++ -// this is the logic from cpus-common.c +// this is the logic from cpu-common.c mutex_t mutex; cond_t exclusive_cond; diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 220002830d..e764ec7683 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -22,7 +22,7 @@ * * Usage * ----- - * See docs/system/nvme.rst for extensive documentation. + * See docs/system/devices/nvme.rst for extensive documentation. * * Add options: * -drive file=,if=none,id= diff --git a/target/arm/cpu.c b/target/arm/cpu.c index a59a5b57af..ebac86f70d 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -2644,7 +2644,7 @@ static const char *arm_gdb_get_core_xml_file(CPUState *cs) * linux syscall TIF_TAGGED_ADDR setting, not TBI in general. * * There should be a better place to put this, but we need this in - * include/exec/cpu_ldst.h, and not some place linux-user specific. + * include/accel/tcg/cpu-ldst.h, and not some place linux-user specific. * * Note that arm-*-user will never set tagged_addr_enable. */ diff --git a/target/loongarch/README b/target/loongarch/README index 0b9dc0d40a..1ffd3422d2 100644 --- a/target/loongarch/README +++ b/target/loongarch/README @@ -11,7 +11,7 @@ - System emulation - You can reference docs/system/loongarch/loongson3.rst to get the information about system emulation of LoongArch. + You can reference docs/system/loongarch/virt.rst to get the information about system emulation of LoongArch. - Linux-user emulation From c6198abbb1ed80036f42523d885e1682b0bd9987 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Tue, 1 Jul 2025 14:06:32 -0400 Subject: [PATCH 1788/2760] MAINTAINERS: fix paths for relocated files Several files were renamed in previous commits, causing their entries in MAINTAINERS to reference outdated paths. This prevents scripts/get_maintainer.pl from correctly matching these files to their maintainers. Update the filenames to reflect their current locations so that maintainer lookup works properly. Related commits --------------- c45460decbd (Oct 2023) hw/input/stellaris_input: Rename to stellaris_gamepad Rename include/hw/input/{gamepad.h => stellaris_gamepad.h} 4faf359accb (Nov 2020) docs: Move virtio-net-failover.rst into the system manual Rename docs/{ => system}/virtio-net-failover.rst 89857312f32 (Apr 2024) hw/usb: move stubs out of stubs/ Rename stubs/usb-dev-stub.c => hw/usb/bus-stub.c f2604d8508a (Apr 2024) hw/virtio: move stubs out of stubs/ Rename stubs/virtio-md-pci.c => hw/virtio/virtio-md-stubs.c 2c888febdfa (Apr 2024) memory-device: move stubs out of stubs/ Rename stubs/memory_device.c => hw/mem/memory-device-stubs.c d481cec7565 (Oct 2024) migration: Move cpu-throttle.c from system to migration Rename {system => migration}/cpu-throttle.c 864a3fa4392 (Jan 2023) monitor: Rename misc.c to hmp-target.c Rename monitor/{misc.c => hmp-target.c} Signed-off-by: Sean Wei Message-ID: <374597a7-94e4-45b2-9617-35183db3ea9d@sean.taipei> Signed-off-by: Thomas Huth --- MAINTAINERS | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 8aa0fd33b7..a36f97ed00 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1007,7 +1007,7 @@ L: qemu-arm@nongnu.org S: Odd Fixes F: hw/*/stellaris* F: hw/display/ssd03* -F: include/hw/input/gamepad.h +F: include/hw/input/stellaris_gamepad.h F: include/hw/timer/stellaris-gptm.h F: docs/system/arm/stellaris.rst F: tests/functional/test_arm_stellaris.py @@ -2173,7 +2173,7 @@ F: hw/net/ F: include/hw/net/ F: tests/qtest/virtio-net-test.c F: tests/functional/test_info_usernet.py -F: docs/virtio-net-failover.rst +F: docs/system/virtio-net-failover.rst T: git https://github.com/jasowang/qemu.git net Parallel NOR Flash devices @@ -2224,7 +2224,6 @@ F: tests/qtest/sdhci-test.c USB S: Orphan F: hw/usb/* -F: stubs/usb-dev-stub.c F: tests/qtest/usb-*-test.c F: docs/system/devices/usb.rst F: include/hw/usb.h @@ -2477,9 +2476,8 @@ S: Supported F: hw/s390x/virtio-ccw-md.c F: hw/s390x/virtio-ccw-md.h F: hw/s390x/virtio-ccw-md-stubs.c -F: hw/virtio/virtio-md-pci.c +F: hw/virtio/virtio-md-*.c F: include/hw/virtio/virtio-md-pci.h -F: stubs/virtio-md-pci.c virtio-mem M: David Hildenbrand @@ -3197,13 +3195,12 @@ M: David Hildenbrand M: Igor Mammedov R: Xiao Guangrong S: Supported -F: hw/mem/memory-device.c +F: hw/mem/memory-device*.c F: hw/mem/nvdimm.c F: hw/mem/pc-dimm.c F: include/hw/mem/memory-device.h F: include/hw/mem/nvdimm.h F: include/hw/mem/pc-dimm.h -F: stubs/memory_device.c F: docs/nvdimm.txt SPICE @@ -3244,9 +3241,9 @@ F: util/qemu-timer*.c F: system/vl.c F: system/main.c F: system/cpus.c -F: system/cpu-throttle.c F: system/cpu-timers.c F: system/runstate* +F: migration/cpu-throttle.c F: qapi/run-state.json Read, Copy, Update (RCU) @@ -3265,7 +3262,7 @@ Human Monitor (HMP) M: Dr. David Alan Gilbert S: Maintained F: monitor/monitor-internal.h -F: monitor/misc.c +F: monitor/hmp-target.c F: monitor/monitor.c F: monitor/hmp* F: hmp.h From 27aa790376c9b111f872445a17d660db5e881911 Mon Sep 17 00:00:00 2001 From: Collin Walling Date: Sun, 29 Jun 2025 22:44:04 -0400 Subject: [PATCH 1789/2760] target/s390x: set has_deprecated_props flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the deprecated_props is an optional field, the expansion method must now set the "has_deprecated_props" flag in order for the data to be output from the response. Fixes: 448553bb7c (qapi: Make CpuModelExpansionInfo::deprecated-props optional and generic) Signed-off-by: Collin Walling Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250630024404.940882-1-walling@linux.ibm.com> Signed-off-by: Thomas Huth --- target/s390x/cpu_models_system.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/target/s390x/cpu_models_system.c b/target/s390x/cpu_models_system.c index 9d84faa3c9..5b84604867 100644 --- a/target/s390x/cpu_models_system.c +++ b/target/s390x/cpu_models_system.c @@ -252,6 +252,9 @@ CpuModelExpansionInfo *qmp_query_cpu_model_expansion(CpuModelExpansionType type, s390_feat_bitmap_to_ascii(deprecated_feats, &expansion_info->deprecated_props, list_add_feat); + + expansion_info->has_deprecated_props = !!expansion_info->deprecated_props; + return expansion_info; } From 10d4365c9e161d363e21182b0613bdbe7cee538e Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 1 Jul 2025 21:42:41 +0200 Subject: [PATCH 1790/2760] target/s390x: A fix for the trouble with tribles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While Tribbles are cute, it should be "triple store" here, not "trible store". Reviewed-by: Daniel P. Berrangé Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250701194241.434183-1-thuth@redhat.com> --- target/s390x/cpu_features_def.h.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/s390x/cpu_features_def.h.inc b/target/s390x/cpu_features_def.h.inc index e23e603a79..c017bffcdc 100644 --- a/target/s390x/cpu_features_def.h.inc +++ b/target/s390x/cpu_features_def.h.inc @@ -186,7 +186,7 @@ DEF_FEAT(PLO_CSO, "plo-cso", PLO, 25, "PLO Compare and swap (256 bit in paramete DEF_FEAT(PLO_DCSO, "plo-dcso", PLO, 26, "PLO Double compare and swap (256 bit in parameter list)") DEF_FEAT(PLO_CSSTO, "plo-cssto", PLO, 27, "PLO Compare and swap and store (256 bit in parameter list)") DEF_FEAT(PLO_CSDSTO, "plo-csdsto", PLO, 28, "PLO Compare and swap and double store (256 bit in parameter list)") -DEF_FEAT(PLO_CSTSTO, "plo-cststo", PLO, 29, "PLO Compare and swap and trible store (256 bit in parameter list)") +DEF_FEAT(PLO_CSTSTO, "plo-cststo", PLO, 29, "PLO Compare and swap and triple store (256 bit in parameter list)") DEF_FEAT(PLO_TCS, "plo-tcs", PLO, 30, "Triple compare and swap (32 bit in parameter list)") DEF_FEAT(PLO_TCSG, "plo-tcsg", PLO, 31, "Triple compare and swap (64 bit in parameter list)") DEF_FEAT(PLO_TCSX, "plo-tcsx", PLO, 32, "Triple compare and swap (128 bit in parameter list)") From 01e2b1bc27bae874bfeb6978ce093deac5bb9639 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 1 Jul 2025 12:58:09 +0200 Subject: [PATCH 1791/2760] tests/functional/test_aarch64_sbsaref_freebsd: Fix the URL of the ISO image The original image has been removed from the server, so the test currently fails if it has to fetch the asset, but we can still download the ISO from the archive server. While we're at it, prefer the XZ compressed image, it's much smaller and thus the download should be faster. Message-ID: <20250701105809.366180-1-thuth@redhat.com> Reviewed-by: Stefan Hajnoczi Signed-off-by: Thomas Huth --- tests/functional/test_aarch64_sbsaref_freebsd.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/functional/test_aarch64_sbsaref_freebsd.py b/tests/functional/test_aarch64_sbsaref_freebsd.py index 3cddc082f3..7ef016fba6 100755 --- a/tests/functional/test_aarch64_sbsaref_freebsd.py +++ b/tests/functional/test_aarch64_sbsaref_freebsd.py @@ -18,9 +18,9 @@ class Aarch64SbsarefFreeBSD(QemuSystemTest): ASSET_FREEBSD_ISO = Asset( - ('https://download.freebsd.org/releases/arm64/aarch64/ISO-IMAGES/' - '14.1/FreeBSD-14.1-RELEASE-arm64-aarch64-bootonly.iso'), - '44cdbae275ef1bb6dab1d5fbb59473d4f741e1c8ea8a80fd9e906b531d6ad461') + ('http://ftp-archive.freebsd.org/pub/FreeBSD-Archive/old-releases/arm64' + '/aarch64/ISO-IMAGES/14.1/FreeBSD-14.1-RELEASE-arm64-aarch64-bootonly.iso.xz'), + '7313a4495ffd71ab77b49b1e83f571521c32756e1d75bf48bd890e0ab0f75827') # This tests the whole boot chain from EFI to Userspace # We only boot a whole OS for the current top level CPU and GIC @@ -29,7 +29,7 @@ def boot_freebsd14(self, cpu=None): self.set_machine('sbsa-ref') fetch_firmware(self) - img_path = self.ASSET_FREEBSD_ISO.fetch() + img_path = self.uncompress(self.ASSET_FREEBSD_ISO) self.vm.set_console() self.vm.add_args( From 51ac481bff88723ef4c101925082fab03bba200a Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 18 Jun 2025 16:00:04 +0800 Subject: [PATCH 1792/2760] hw/misc/aspeed_sdmc: Skipping dram_init in u-boot for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On AST2700 SoC, QEMU now sets BIT6 in VGA0 SCRATCH register to indicate that DDR training has completed, thus skipping the dram_init(). To align with the recent U-Boot changes, where the Main Control Register's BIT16 is checked to skip the dram_init() process, this patch sets BIT16 in the SDMC Main Control Register at reset time. This allows both the main U-Boot stage to correctly detect and bypass DRAM initialization when running under QEMU. Reference: - QEMU: https://github.com/qemu/qemu/commit/2d082fea485ee455a70ed3e963cdf9a70f34858a - U-Boot: https://github.com/AspeedTech-BMC/u-boot/commit/94e5435504fb0d8888f5c1bfd3fa284cdd6aaf9b Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250618080006.846355-2-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_sdmc.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/misc/aspeed_sdmc.c b/hw/misc/aspeed_sdmc.c index f04d9930dd..dff7cc362d 100644 --- a/hw/misc/aspeed_sdmc.c +++ b/hw/misc/aspeed_sdmc.c @@ -570,6 +570,9 @@ static void aspeed_2700_sdmc_reset(DeviceState *dev) /* Set ram size bit and defaults values */ s->regs[R_MAIN_CONF] = asc->compute_conf(s, 0); + /* Skipping dram init */ + s->regs[R_MAIN_CONTROL] = BIT(16); + if (s->unlocked) { s->regs[R_2700_PROT] = PROT_UNLOCKED; } From a6ca5dfccc2b584d2f7bf31fb488fc178e967972 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Wed, 18 Jun 2025 16:00:05 +0800 Subject: [PATCH 1793/2760] hw/misc/aspeed_scu: Support the Frequency Counter Control register for AST2700 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the datasheet: BIT[1] (SCU_FREQ_OSC_EN) enables the oscillator frequency measurement counter. BIT[6] (SCU_FREQ_DONE) indicates the measurement is finished. Firmware polls BIT[6] to determine when measurement is complete. The flag can be cleared by writing BIT[1] to 0. To simulate this hardware behavior in QEMU: If BIT[1] is set to 1, BIT[6] is immediately set to 1 to avoid firmware hanging during polling. If BIT[1] is cleared to 0, BIT[6] is also cleared to 0 to match hardware semantics. The initial value of this register is initialized to 0x80, reflecting the default value confirmed from an EVB register dump. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250618080006.846355-3-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_scu.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 4930e00fed..11d0739108 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -176,6 +176,7 @@ #define AST2700_SCUIO_UARTCLK_GEN TO_REG(0x330) #define AST2700_SCUIO_HUARTCLK_GEN TO_REG(0x334) #define AST2700_SCUIO_CLK_DUTY_MEAS_RST TO_REG(0x388) +#define AST2700_SCUIO_FREQ_CNT_CTL TO_REG(0x3A0) #define SCU_IO_REGION_SIZE 0x1000 @@ -1022,6 +1023,10 @@ static void aspeed_ast2700_scuio_write(void *opaque, hwaddr offset, s->regs[reg - 1] ^= data; updated = true; break; + case AST2700_SCUIO_FREQ_CNT_CTL: + s->regs[reg] = deposit32(s->regs[reg], 6, 1, !!(data & BIT(1))); + updated = true; + break; default: qemu_log_mask(LOG_GUEST_ERROR, "%s: Unhandled write at offset 0x%" HWADDR_PRIx "\n", @@ -1066,6 +1071,7 @@ static const uint32_t ast2700_a0_resets_io[ASPEED_AST2700_SCU_NR_REGS] = { [AST2700_SCUIO_UARTCLK_GEN] = 0x00014506, [AST2700_SCUIO_HUARTCLK_GEN] = 0x000145c0, [AST2700_SCUIO_CLK_DUTY_MEAS_RST] = 0x0c9100d2, + [AST2700_SCUIO_FREQ_CNT_CTL] = 0x00000080, }; static void aspeed_2700_scuio_class_init(ObjectClass *klass, const void *data) From 126c56b067d6a68840542f8dc7afdec9b69caa83 Mon Sep 17 00:00:00 2001 From: Patrick Williams Date: Wed, 18 Jun 2025 23:58:50 -0400 Subject: [PATCH 1794/2760] hw/arm/aspeed: bletchley: update hw strap values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update the Bletchley hardware strap register values per actual hardware: ``` root@bmc:~# devmem 0x1e6e2500 0x00002000 root@bmc:~# devmem 0x1e6e2510 0x00000801 ``` Signed-off-by: Patrick Williams Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250619035850.2682690-1-patrick@stwcx.xyz Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index d0b333646e..5662cb2c4a 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -197,9 +197,8 @@ struct AspeedMachineState { #define FUJI_BMC_HW_STRAP2 0x00000000 /* Bletchley hardware value */ -/* TODO: Leave same as EVB for now. */ -#define BLETCHLEY_BMC_HW_STRAP1 AST2600_EVB_HW_STRAP1 -#define BLETCHLEY_BMC_HW_STRAP2 AST2600_EVB_HW_STRAP2 +#define BLETCHLEY_BMC_HW_STRAP1 0x00002000 +#define BLETCHLEY_BMC_HW_STRAP2 0x00000801 /* Qualcomm DC-SCM hardware value */ #define QCOM_DC_SCM_V1_BMC_HW_STRAP1 0x00000000 From 8e076a3f1b1ed0fc009ee25796e45c44b4cf74f9 Mon Sep 17 00:00:00 2001 From: Patrick Williams Date: Thu, 19 Jun 2025 11:14:58 -0400 Subject: [PATCH 1795/2760] hw/arm/aspeed: add Catalina machine type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the 'catalina-bmc' machine type based on the kernel DTS[1] as of 6.16-rc2. The i2c model is as complete as the current QEMU models support, but in some cases I substituted devices that are close enough for present functionality. Strap registers are were verified with hardware. This has been tested with an openbmc image built from [2]. Add a functional test in line with Bletchley, pointing at an image obtained from the OpenBMC Jenkins server. [1]: https://github.com/torvalds/linux/blob/v6.16-rc2/arch/arm/boot/dts/aspeed/aspeed-bmc-facebook-catalina.dts [2]: https://github.com/openbmc/openbmc/commit/5bc73ec261f981d5e586bda5ac78eb0cbd5f92b0 Signed-off-by: Patrick Williams Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250619151458.2831859-1-patrick@stwcx.xyz Signed-off-by: Cédric Le Goater --- hw/arm/Kconfig | 1 + hw/arm/aspeed.c | 200 +++++++++++++++++++ tests/functional/meson.build | 2 + tests/functional/test_arm_aspeed_catalina.py | 25 +++ 4 files changed, 228 insertions(+) create mode 100755 tests/functional/test_arm_aspeed_catalina.py diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index f543d944c3..6ea86534d5 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -532,6 +532,7 @@ config ASPEED_SOC select I2C select DPS310 select PCA9552 + select PCA9554 select SERIAL_MM select SMBUS_EEPROM select PCA954X diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 5662cb2c4a..94897505f8 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -19,6 +19,7 @@ #include "hw/i2c/i2c_mux_pca954x.h" #include "hw/i2c/smbus_eeprom.h" #include "hw/gpio/pca9552.h" +#include "hw/gpio/pca9554.h" #include "hw/nvram/eeprom_at24c.h" #include "hw/sensor/tmp105.h" #include "hw/misc/led.h" @@ -1002,6 +1003,180 @@ static void fuji_bmc_i2c_init(AspeedMachineState *bmc) } #define TYPE_TMP421 "tmp421" +#define TYPE_DS1338 "ds1338" + +/* Catalina hardware value */ +#define CATALINA_BMC_HW_STRAP1 0x00002002 +#define CATALINA_BMC_HW_STRAP2 0x00000800 + +#define CATALINA_BMC_RAM_SIZE ASPEED_RAM_SIZE(2 * GiB) + +static void catalina_bmc_i2c_init(AspeedMachineState *bmc) +{ + /* Reference from v6.16-rc2 aspeed-bmc-facebook-catalina.dts */ + + AspeedSoCState *soc = bmc->soc; + I2CBus *i2c[16] = {}; + I2CSlave *i2c_mux; + + /* busses 0-15 are all used. */ + for (int i = 0; i < ARRAY_SIZE(i2c); i++) { + i2c[i] = aspeed_i2c_get_bus(&soc->i2c, i); + } + + /* &i2c0 */ + /* i2c-mux@71 (PCA9546) on i2c0 */ + i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x71); + + /* i2c-mux@72 (PCA9546) on i2c0 */ + i2c_mux = i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x72); + + /* i2c0mux1ch1 */ + /* io_expander7 - pca9535@20 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 1), + TYPE_PCA9552, 0x20); + /* eeprom@50 */ + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 1), 0x50, 8 * KiB); + + /* i2c-mux@73 (PCA9546) on i2c0 */ + i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x73); + + /* i2c-mux@75 (PCA9546) on i2c0 */ + i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x75); + + /* i2c-mux@76 (PCA9546) on i2c0 */ + i2c_mux = i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x76); + + /* i2c0mux4ch1 */ + /* io_expander8 - pca9535@21 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 1), + TYPE_PCA9552, 0x21); + /* eeprom@50 */ + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 1), 0x50, 8 * KiB); + + /* i2c-mux@77 (PCA9546) on i2c0 */ + i2c_slave_create_simple(i2c[0], TYPE_PCA9546, 0x77); + + + /* &i2c1 */ + /* i2c-mux@70 (PCA9548) on i2c1 */ + i2c_mux = i2c_slave_create_simple(i2c[1], TYPE_PCA9548, 0x70); + /* i2c1mux0ch0 */ + /* ina238@41 - no model */ + /* ina238@42 - no model */ + /* ina238@44 - no model */ + /* i2c1mux0ch1 */ + /* ina238@41 - no model */ + /* ina238@43 - no model */ + /* i2c1mux0ch4 */ + /* ltc4287@42 - no model */ + /* ltc4287@43 - no model */ + + /* i2c1mux0ch5 */ + /* eeprom@54 */ + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 5), 0x54, 8 * KiB); + /* tpm75@4f */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 5), TYPE_TMP75, 0x4f); + + /* i2c1mux0ch6 */ + /* io_expander5 - pca9554@27 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 6), + TYPE_PCA9554, 0x27); + /* io_expander6 - pca9555@25 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 6), + TYPE_PCA9552, 0x25); + /* eeprom@51 */ + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 6), 0x51, 8 * KiB); + + /* i2c1mux0ch7 */ + /* eeprom@53 */ + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 7), 0x53, 8 * KiB); + /* temperature-sensor@4b - tmp75 */ + i2c_slave_create_simple(pca954x_i2c_get_bus(i2c_mux, 7), TYPE_TMP75, 0x4b); + + /* &i2c2 */ + /* io_expander0 - pca9555@20 */ + i2c_slave_create_simple(i2c[2], TYPE_PCA9552, 0x20); + /* io_expander0 - pca9555@21 */ + i2c_slave_create_simple(i2c[2], TYPE_PCA9552, 0x21); + /* io_expander0 - pca9555@27 */ + i2c_slave_create_simple(i2c[2], TYPE_PCA9552, 0x27); + /* eeprom@50 */ + at24c_eeprom_init(i2c[2], 0x50, 8 * KiB); + /* eeprom@51 */ + at24c_eeprom_init(i2c[2], 0x51, 8 * KiB); + + /* &i2c5 */ + /* i2c-mux@70 (PCA9548) on i2c5 */ + i2c_mux = i2c_slave_create_simple(i2c[5], TYPE_PCA9548, 0x70); + /* i2c5mux0ch6 */ + /* eeprom@52 */ + at24c_eeprom_init(pca954x_i2c_get_bus(i2c_mux, 6), 0x52, 8 * KiB); + /* i2c5mux0ch7 */ + /* ina230@40 - no model */ + /* ina230@41 - no model */ + /* ina230@44 - no model */ + /* ina230@45 - no model */ + + /* &i2c6 */ + /* io_expander3 - pca9555@21 */ + i2c_slave_create_simple(i2c[6], TYPE_PCA9552, 0x21); + /* rtc@6f - nct3018y */ + i2c_slave_create_simple(i2c[6], TYPE_DS1338, 0x6f); + + /* &i2c9 */ + /* io_expander4 - pca9555@4f */ + i2c_slave_create_simple(i2c[9], TYPE_PCA9552, 0x4f); + /* temperature-sensor@4b - tpm75 */ + i2c_slave_create_simple(i2c[9], TYPE_TMP75, 0x4b); + /* eeprom@50 */ + at24c_eeprom_init(i2c[9], 0x50, 8 * KiB); + /* eeprom@56 */ + at24c_eeprom_init(i2c[9], 0x56, 8 * KiB); + + /* &i2c10 */ + /* temperature-sensor@1f - tpm421 */ + i2c_slave_create_simple(i2c[10], TYPE_TMP421, 0x1f); + /* eeprom@50 */ + at24c_eeprom_init(i2c[10], 0x50, 8 * KiB); + + /* &i2c11 */ + /* ssif-bmc@10 - no model */ + + /* &i2c12 */ + /* eeprom@50 */ + at24c_eeprom_init(i2c[12], 0x50, 8 * KiB); + + /* &i2c13 */ + /* eeprom@50 */ + at24c_eeprom_init(i2c[13], 0x50, 8 * KiB); + /* eeprom@54 */ + at24c_eeprom_init(i2c[13], 0x54, 256); + /* eeprom@55 */ + at24c_eeprom_init(i2c[13], 0x55, 256); + /* eeprom@57 */ + at24c_eeprom_init(i2c[13], 0x57, 256); + + /* &i2c14 */ + /* io_expander9 - pca9555@10 */ + i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x10); + /* io_expander10 - pca9555@11 */ + i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x11); + /* io_expander11 - pca9555@12 */ + i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x12); + /* io_expander12 - pca9555@13 */ + i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x13); + /* io_expander13 - pca9555@14 */ + i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x14); + /* io_expander14 - pca9555@15 */ + i2c_slave_create_simple(i2c[14], TYPE_PCA9552, 0x15); + + /* &i2c15 */ + /* temperature-sensor@1f - tmp421 */ + i2c_slave_create_simple(i2c[15], TYPE_TMP421, 0x1f); + /* eeprom@52 */ + at24c_eeprom_init(i2c[15], 0x52, 8 * KiB); +} static void bletchley_bmc_i2c_init(AspeedMachineState *bmc) { @@ -1584,6 +1759,27 @@ static void aspeed_machine_bletchley_class_init(ObjectClass *oc, aspeed_machine_class_init_cpus_defaults(mc); } +static void aspeed_machine_catalina_class_init(ObjectClass *oc, + const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Facebook Catalina BMC (Cortex-A7)"; + amc->soc_name = "ast2600-a3"; + amc->hw_strap1 = CATALINA_BMC_HW_STRAP1; + amc->hw_strap2 = CATALINA_BMC_HW_STRAP2; + amc->fmc_model = "w25q01jvq"; + amc->spi_model = NULL; + amc->num_cs = 2; + amc->macs_mask = ASPEED_MAC2_ON; + amc->i2c_init = catalina_bmc_i2c_init; + mc->auto_create_sdcard = true; + mc->default_ram_size = CATALINA_BMC_RAM_SIZE; + aspeed_machine_class_init_cpus_defaults(mc); + aspeed_machine_ast2600_class_emmc_init(oc); +} + static void fby35_reset(MachineState *state, ResetType type) { AspeedMachineState *bmc = ASPEED_MACHINE(state); @@ -1876,6 +2072,10 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("bletchley-bmc"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_bletchley_class_init, + }, { + .name = MACHINE_TYPE_NAME("catalina-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_catalina_class_init, }, { .name = MACHINE_TYPE_NAME("fby35-bmc"), .parent = MACHINE_TYPE_NAME("ast2600-evb"), diff --git a/tests/functional/meson.build b/tests/functional/meson.build index b542b3a3af..fb87b957aa 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -32,6 +32,7 @@ test_timeouts = { 'arm_aspeed_ast2500' : 720, 'arm_aspeed_ast2600' : 1200, 'arm_aspeed_bletchley' : 480, + 'arm_aspeed_catalina' : 480, 'arm_aspeed_rainier' : 480, 'arm_bpim2u' : 500, 'arm_collie' : 180, @@ -127,6 +128,7 @@ tests_arm_system_thorough = [ 'arm_aspeed_ast2500', 'arm_aspeed_ast2600', 'arm_aspeed_bletchley', + 'arm_aspeed_catalina', 'arm_aspeed_rainier', 'arm_bpim2u', 'arm_canona1100', diff --git a/tests/functional/test_arm_aspeed_catalina.py b/tests/functional/test_arm_aspeed_catalina.py new file mode 100755 index 0000000000..dc2f24e7b4 --- /dev/null +++ b/tests/functional/test_arm_aspeed_catalina.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + + +class CatalinaMachine(AspeedTest): + + ASSET_CATALINA_FLASH = Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/a866feb5ef81245b4827a214584bf6bcc72939f6/images/catalina-bmc/obmc-phosphor-image-catalina-20250619123021.static.mtd.xz', + '287402e1ba021991e06be1d098f509444a02a3d81a73a932f66528b159e864f9') + + def test_arm_ast2600_catalina_openbmc(self): + image_path = self.uncompress(self.ASSET_CATALINA_FLASH) + + self.do_test_arm_aspeed_openbmc('catalina-bmc', image=image_path, + uboot='2019.04', cpu_id='0xf00', + soc='AST2600 rev A3') + +if __name__ == '__main__': + AspeedTest.main() From 10d1b6231b7fdbeb9c601af35f73c6353cbfe6c8 Mon Sep 17 00:00:00 2001 From: Tan Siewert Date: Thu, 19 Jun 2025 10:53:27 +0200 Subject: [PATCH 1796/2760] hw/misc/aspeed_scu: Handle AST2600 protection key registers correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The AST2600 SCU has two protection key registers (0x00 and 0x10) that both need to be unlocked. (Un-)locking 0x00 modifies both protection key registers, while modifying 0x10 only modifies itself. This commit updates the SCU write logic to reject writes unless both protection key registers are unlocked, matching the behaviour of real hardware. Signed-off-by: Tan Siewert Reviewed-by: Jamin Lin Link: https://lore.kernel.org/qemu-devel/20250619085329.42125-1-tan@siewert.io Signed-off-by: Cédric Le Goater --- hw/misc/aspeed_scu.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/hw/misc/aspeed_scu.c b/hw/misc/aspeed_scu.c index 11d0739108..a0ab5eed8f 100644 --- a/hw/misc/aspeed_scu.c +++ b/hw/misc/aspeed_scu.c @@ -91,6 +91,7 @@ #define BMC_DEV_ID TO_REG(0x1A4) #define AST2600_PROT_KEY TO_REG(0x00) +#define AST2600_PROT_KEY2 TO_REG(0x10) #define AST2600_SILICON_REV TO_REG(0x04) #define AST2600_SILICON_REV2 TO_REG(0x14) #define AST2600_SYS_RST_CTRL TO_REG(0x40) @@ -723,6 +724,8 @@ static void aspeed_ast2600_scu_write(void *opaque, hwaddr offset, int reg = TO_REG(offset); /* Truncate here so bitwise operations below behave as expected */ uint32_t data = data64; + bool prot_data_state = data == ASPEED_SCU_PROT_KEY; + bool unlocked = s->regs[AST2600_PROT_KEY] && s->regs[AST2600_PROT_KEY2]; if (reg >= ASPEED_AST2600_SCU_NR_REGS) { qemu_log_mask(LOG_GUEST_ERROR, @@ -731,15 +734,24 @@ static void aspeed_ast2600_scu_write(void *opaque, hwaddr offset, return; } - if (reg > PROT_KEY && !s->regs[PROT_KEY]) { + if ((reg != AST2600_PROT_KEY && reg != AST2600_PROT_KEY2) && !unlocked) { qemu_log_mask(LOG_GUEST_ERROR, "%s: SCU is locked!\n", __func__); + return; } trace_aspeed_scu_write(offset, size, data); switch (reg) { case AST2600_PROT_KEY: - s->regs[reg] = (data == ASPEED_SCU_PROT_KEY) ? 1 : 0; + /* + * Writing a value to SCU000 will modify both protection + * registers to each protection register individually. + */ + s->regs[AST2600_PROT_KEY] = prot_data_state; + s->regs[AST2600_PROT_KEY2] = prot_data_state; + return; + case AST2600_PROT_KEY2: + s->regs[AST2600_PROT_KEY2] = prot_data_state; return; case AST2600_HW_STRAP1: case AST2600_HW_STRAP2: From 116bf243d9137a9101aa1a403b402b78313615bc Mon Sep 17 00:00:00 2001 From: Tan Siewert Date: Mon, 30 Jun 2025 13:26:46 +0200 Subject: [PATCH 1797/2760] tests/qtest: Add test for ASPEED SCU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds basic tests for the ASPEED System Control Unit (SCU) and its protection mechanism on the AST2500 and AST2600 platforms. The tests verify: - That SCU protection registers can be unlocked and locked again - That modifying the primary protection register on AST2600 also affects the secondary one - That writes to protected SCU registers are blocked unless protection registers are unlocked explicitly These tests ensure proper emulation of hardware locking behaviour and help catch regressions in SCU access logic. Signed-off-by: Tan Siewert Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250630112646.74944-1-tan@siewert.io [ clg: Reordered file list in meson.build ] Signed-off-by: Cédric Le Goater --- tests/qtest/aspeed_scu-test.c | 231 ++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 1 + 2 files changed, 232 insertions(+) create mode 100644 tests/qtest/aspeed_scu-test.c diff --git a/tests/qtest/aspeed_scu-test.c b/tests/qtest/aspeed_scu-test.c new file mode 100644 index 0000000000..ca09f9171f --- /dev/null +++ b/tests/qtest/aspeed_scu-test.c @@ -0,0 +1,231 @@ +/* + * QTest testcase for the ASPEED AST2500 and AST2600 SCU. + * + * SPDX-License-Identifier: GPL-2.0-or-later + * Copyright (C) 2025 Tan Siewert + */ + +#include "qemu/osdep.h" +#include "libqtest-single.h" + +/* + * SCU base, as well as protection key are + * the same on AST2500 and 2600. + */ +#define AST_SCU_BASE 0x1E6E2000 +#define AST_SCU_PROT_LOCK_STATE 0x0 +#define AST_SCU_PROT_LOCK_VALUE 0x2 +#define AST_SCU_PROT_UNLOCK_STATE 0x1 +#define AST_SCU_PROT_UNLOCK_VALUE 0x1688A8A8 + +#define AST2500_MACHINE "-machine ast2500-evb" +#define AST2500_SCU_PROT_REG 0x00 +#define AST2500_SCU_MISC_2_CONTROL_REG 0x4C + +#define AST2600_MACHINE "-machine ast2600-evb" +/* AST2600 has two protection registers */ +#define AST2600_SCU_PROT_REG 0x000 +#define AST2600_SCU_PROT_REG2 0x010 +#define AST2600_SCU_MISC_2_CONTROL_REG 0x0C4 + +#define TEST_LOCK_ARBITRARY_VALUE 0xABCDEFAB + +/** + * Assert that a given register matches an expected value. + * + * Reads the register and checks if its value equals the expected value. + * + * @param *s - QTest machine state + * @param reg - Address of the register to be checked + * @param expected - Expected register value + */ +static inline void assert_register_eq(QTestState *s, + uint32_t reg, + uint32_t expected) +{ + uint32_t value = qtest_readl(s, reg); + g_assert_cmphex(value, ==, expected); +} + +/** + * Assert that a given register does not match a specific value. + * + * Reads the register and checks that its value is not equal to the + * provided value. + * + * @param *s - QTest machine state + * @param reg - Address of the register to be checked + * @param not_expected - Value the register must not contain + */ +static inline void assert_register_neq(QTestState *s, + uint32_t reg, + uint32_t not_expected) +{ + uint32_t value = qtest_readl(s, reg); + g_assert_cmphex(value, !=, not_expected); +} + +/** + * Test whether the SCU can be locked and unlocked correctly. + * + * When testing multiple registers, this function assumes that writing + * to the first register also affects the others. However, writing to + * any other register only affects itself. + * + * @param *machine - input machine configuration, passed directly + * to QTest + * @param regs[] - List of registers to be checked + * @param regc - amount of arguments for registers to be checked + */ +static void test_protection_register(const char *machine, + const uint32_t regs[], + const int regc) +{ + QTestState *s = qtest_init(machine); + + for (int i = 0; i < regc; i++) { + uint32_t reg = regs[i]; + + qtest_writel(s, reg, AST_SCU_PROT_UNLOCK_VALUE); + assert_register_eq(s, reg, AST_SCU_PROT_UNLOCK_STATE); + + /** + * Check that other registers are unlocked too, if more + * than one is available. + */ + if (regc > 1 && i == 0) { + /* Initialise at 1 instead of 0 to skip first */ + for (int j = 1; j < regc; j++) { + uint32_t add_reg = regs[j]; + assert_register_eq(s, add_reg, AST_SCU_PROT_UNLOCK_STATE); + } + } + + /* Lock the register again */ + qtest_writel(s, reg, AST_SCU_PROT_LOCK_VALUE); + assert_register_eq(s, reg, AST_SCU_PROT_LOCK_STATE); + + /* And the same for locked state */ + if (regc > 1 && i == 0) { + /* Initialise at 1 instead of 0 to skip first */ + for (int j = 1; j < regc; j++) { + uint32_t add_reg = regs[j]; + assert_register_eq(s, add_reg, AST_SCU_PROT_LOCK_STATE); + } + } + } + + qtest_quit(s); +} + +static void test_2500_protection_register(void) +{ + uint32_t regs[] = { AST_SCU_BASE + AST2500_SCU_PROT_REG }; + + test_protection_register(AST2500_MACHINE, + regs, + ARRAY_SIZE(regs)); +} + +static void test_2600_protection_register(void) +{ + /** + * The AST2600 has two protection registers, both + * being required to be unlocked to do any operation. + * + * Modifying SCU000 also modifies SCU010, but modifying + * SCU010 only will keep SCU000 untouched. + */ + uint32_t regs[] = { AST_SCU_BASE + AST2600_SCU_PROT_REG, + AST_SCU_BASE + AST2600_SCU_PROT_REG2 }; + + test_protection_register(AST2600_MACHINE, + regs, + ARRAY_SIZE(regs)); +} + +/** + * Test if SCU register writes are correctly allowed or blocked + * depending on the protection register state. + * + * The test first locks the protection register and verifies that + * writes to the target SCU register are rejected. It then unlocks + * the protection register and confirms that the written value is + * retained when unlocked. + * + * @param *machine - input machine configuration, passed directly + * to QTest + * @param protection_register - first SCU protection key register + * (only one for keeping it simple) + * @param test_register - Register to be used for writing arbitrary + * values + */ +static void test_write_permission_lock_state(const char *machine, + const uint32_t protection_register, + const uint32_t test_register) +{ + QTestState *s = qtest_init(machine); + + /* Arbitrary value to lock provided SCU protection register */ + qtest_writel(s, protection_register, AST_SCU_PROT_LOCK_VALUE); + + /* Ensure that the SCU is really locked */ + assert_register_eq(s, protection_register, AST_SCU_PROT_LOCK_STATE); + + /* Write a known arbitrary value to test that the write is blocked */ + qtest_writel(s, test_register, TEST_LOCK_ARBITRARY_VALUE); + + /* We do not want to have the written value to be saved */ + assert_register_neq(s, test_register, TEST_LOCK_ARBITRARY_VALUE); + + /** + * Unlock the SCU and verify that it can be written to. + * Assumes that the first SCU protection register is sufficient to + * unlock all protection registers, if multiple are present. + */ + qtest_writel(s, protection_register, AST_SCU_PROT_UNLOCK_VALUE); + assert_register_eq(s, protection_register, AST_SCU_PROT_UNLOCK_STATE); + + /* Write a known arbitrary value to test that the write works */ + qtest_writel(s, test_register, TEST_LOCK_ARBITRARY_VALUE); + + /* Ensure that the written value is retained */ + assert_register_eq(s, test_register, TEST_LOCK_ARBITRARY_VALUE); + + qtest_quit(s); +} + +static void test_2500_write_permission_lock_state(void) +{ + test_write_permission_lock_state( + AST2500_MACHINE, + AST_SCU_BASE + AST2500_SCU_PROT_REG, + AST_SCU_BASE + AST2500_SCU_MISC_2_CONTROL_REG + ); +} + +static void test_2600_write_permission_lock_state(void) +{ + test_write_permission_lock_state( + AST2600_MACHINE, + AST_SCU_BASE + AST2600_SCU_PROT_REG, + AST_SCU_BASE + AST2600_SCU_MISC_2_CONTROL_REG + ); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + qtest_add_func("/ast2500/scu/protection_register", + test_2500_protection_register); + qtest_add_func("/ast2600/scu/protection_register", + test_2600_protection_register); + + qtest_add_func("/ast2500/scu/write_permission_lock_state", + test_2500_write_permission_lock_state); + qtest_add_func("/ast2600/scu/write_permission_lock_state", + test_2600_write_permission_lock_state); + + return g_test_run(); +} diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 8ad849054f..91b4a71a18 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -215,6 +215,7 @@ qtests_npcm8xx = \ qtests_aspeed = \ ['aspeed_gpio-test', 'aspeed_hace-test', + 'aspeed_scu-test', 'aspeed_smc-test'] qtests_aspeed64 = \ ['ast2700-gpio-test', From e8c1128bf982f2e5f7c9ca6c1de632feec3d9e94 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Fri, 27 Jun 2025 14:33:31 +0800 Subject: [PATCH 1798/2760] vfio/container: Fix potential SIGSEGV when recover from unmap-all-vaddr failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CPR overrides then restores dma_map in both outgoing and incoming QEMU, for different reasons. But it only sets saved_dma_map in the target. Fix it by always setting saved_dma_map. Fixes: eba1f657cbb1 ("vfio/container: recover from unmap-all-vaddr failure") Suggested-by: Steven Sistare Signed-off-by: Zhenzhong Duan Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250627063332.5173-2-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-legacy.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index a84c3247b7..0a5d1bd480 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -180,9 +180,9 @@ bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) vmstate_register(NULL, -1, &vfio_container_vmstate, container); /* During incoming CPR, divert calls to dma_map. */ + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + container->cpr.saved_dma_map = vioc->dma_map; if (cpr_is_incoming()) { - VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); - container->cpr.saved_dma_map = vioc->dma_map; vioc->dma_map = vfio_legacy_cpr_dma_map; } From 924c3ccb310e615bd350d4c77b269b19d95bf5e4 Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Fri, 27 Jun 2025 14:33:32 +0800 Subject: [PATCH 1799/2760] vfio/container: Fix vfio_container_post_load() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When there are multiple VFIO containers, vioc->dma_map is restored multiple times, this made only first container work and remaining containers using vioc->dma_map restored by first container. Fix it by save and restore vioc->dma_map locally. saved_dma_map in VFIOContainerCPR becomes useless and is removed. Fixes: 7e9f21411302 ("vfio/container: restore DMA vaddr") Signed-off-by: Zhenzhong Duan Reviewed-by: Steve Sistare Link: https://lore.kernel.org/qemu-devel/20250627063332.5173-3-zhenzhong.duan@intel.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-legacy.c | 23 +++++++++-------------- include/hw/vfio/vfio-cpr.h | 7 ++++--- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index 0a5d1bd480..1216717546 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -99,20 +99,21 @@ static int vfio_container_post_load(void *opaque, int version_id) { VFIOContainer *container = opaque; VFIOContainerBase *bcontainer = &container->bcontainer; - VFIOGroup *group; + VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + dma_map_fn saved_dma_map = vioc->dma_map; Error *local_err = NULL; + /* During incoming CPR, divert calls to dma_map. */ + vioc->dma_map = vfio_legacy_cpr_dma_map; + if (!vfio_listener_register(bcontainer, &local_err)) { error_report_err(local_err); return -1; } - QLIST_FOREACH(group, &container->group_list, container_next) { - VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + /* Restore original dma_map function */ + vioc->dma_map = saved_dma_map; - /* Restore original dma_map function */ - vioc->dma_map = container->cpr.saved_dma_map; - } return 0; } @@ -148,6 +149,7 @@ static int vfio_cpr_fail_notifier(NotifierWithReturn *notifier, */ VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + dma_map_fn saved_dma_map = vioc->dma_map; vioc->dma_map = vfio_legacy_cpr_dma_map; container->cpr.remap_listener = (MemoryListener) { @@ -158,7 +160,7 @@ static int vfio_cpr_fail_notifier(NotifierWithReturn *notifier, bcontainer->space->as); memory_listener_unregister(&container->cpr.remap_listener); container->cpr.vaddr_unmapped = false; - vioc->dma_map = container->cpr.saved_dma_map; + vioc->dma_map = saved_dma_map; } return 0; } @@ -179,13 +181,6 @@ bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) vmstate_register(NULL, -1, &vfio_container_vmstate, container); - /* During incoming CPR, divert calls to dma_map. */ - VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); - container->cpr.saved_dma_map = vioc->dma_map; - if (cpr_is_incoming()) { - vioc->dma_map = vfio_legacy_cpr_dma_map; - } - migration_add_notifier_mode(&container->cpr.transfer_notifier, vfio_cpr_fail_notifier, MIG_MODE_CPR_TRANSFER); diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 8bf85b9f4e..dbb2a16b7a 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -16,14 +16,15 @@ struct VFIOContainer; struct VFIOContainerBase; struct VFIOGroup; +typedef int (*dma_map_fn)(const struct VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, void *vaddr, + bool readonly, MemoryRegion *mr); + typedef struct VFIOContainerCPR { Error *blocker; bool vaddr_unmapped; NotifierWithReturn transfer_notifier; MemoryListener remap_listener; - int (*saved_dma_map)(const struct VFIOContainerBase *bcontainer, - hwaddr iova, ram_addr_t size, - void *vaddr, bool readonly, MemoryRegion *mr); } VFIOContainerCPR; typedef struct VFIODeviceCPR { From f7c5dff26e0128246f5c220b38da51e56a756dbb Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Wed, 2 Jul 2025 12:59:49 +0100 Subject: [PATCH 1800/2760] vfio-user: do not register vfio-user container with cpr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As the full cpr implementation is yet to be merged upstream, do not register the vfio-user container with cpr. Full vfio-user support for cpr can be merged later as a follow-up series. Signed-off-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Message-ID: <20250702120043.267634-1-mark.caveayland@nutanix.com> [ clg: Removed now useless "hw/vfio/vfio-cpr.h" include ] Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.c | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/hw/vfio-user/container.c b/hw/vfio-user/container.c index 3133fef177..d318e6a339 100644 --- a/hw/vfio-user/container.c +++ b/hw/vfio-user/container.c @@ -13,7 +13,6 @@ #include "hw/vfio-user/container.h" #include "hw/vfio-user/device.h" #include "hw/vfio-user/trace.h" -#include "hw/vfio/vfio-cpr.h" #include "hw/vfio/vfio-device.h" #include "hw/vfio/vfio-listener.h" #include "qapi/error.h" @@ -225,14 +224,10 @@ vfio_user_container_connect(AddressSpace *as, VFIODevice *vbasedev, bcontainer = &container->bcontainer; - if (!vfio_cpr_register_container(bcontainer, errp)) { - goto free_container_exit; - } - ret = ram_block_uncoordinated_discard_disable(true); if (ret) { error_setg_errno(errp, -ret, "Cannot set discarding of RAM broken"); - goto unregister_container_exit; + goto free_container_exit; } vioc = VFIO_IOMMU_GET_CLASS(bcontainer); @@ -261,9 +256,6 @@ vfio_user_container_connect(AddressSpace *as, VFIODevice *vbasedev, enable_discards_exit: ram_block_uncoordinated_discard_disable(false); -unregister_container_exit: - vfio_cpr_unregister_container(bcontainer); - free_container_exit: object_unref(container); @@ -286,7 +278,6 @@ static void vfio_user_container_disconnect(VFIOUserContainer *container) vioc->release(bcontainer); } - vfio_cpr_unregister_container(bcontainer); object_unref(container); vfio_address_space_put(space); From 34ea448263a8dad7443bfeaf5eca75e49abc5865 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Wed, 25 Jun 2025 15:33:10 +0800 Subject: [PATCH 1801/2760] i386/tdx: Build TDX only for 64-bit target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build errors related to TDX were reported when QEMU built on 32-bit host[1][2]. Since TDX cannot work on 32-bit host and it's also not worth supporting TDX with 32-bit QEMU, limit TDX to 64-bit target only. [1] https://lore.kernel.org/qemu-devel/20250602173101.1052983-1-clg@redhat.com/ [2] https://lore.kernel.org/qemu-devel/b8171c39-6a92-4078-a59a-a63d7452e1e9@kaod.org/ Suggested-by: Cédric Le Goater Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Tested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250625073310.2796298-1-xiaoyao.li@intel.com Signed-off-by: Cédric Le Goater --- hw/i386/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index eb65bda6e0..14d23e27b5 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -13,7 +13,7 @@ config SGX config TDX bool select X86_FW_OVMF - depends on KVM + depends on KVM && X86_64 config PC bool From 4ba5cdfdf70843d7a74a7cb9704688c4c6957dd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 30 Jun 2025 19:23:01 +0200 Subject: [PATCH 1802/2760] b4: Drop linktrailermask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When grabbing a patch series, the link trailer is replaced with a Message-ID, which is not useful compared to an URL. Fix that by dropping the linktrailermask config. Cc: Philippe Mathieu-Daudé Cc: Jiaxun Yang Fixes: 838cf72b5d2c ("Add a b4 configuration file") Link: https://lore.kernel.org/qemu-devel/20250630172301.519848-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- .b4-config | 1 - 1 file changed, 1 deletion(-) diff --git a/.b4-config b/.b4-config index 4b9b2fe290..126f503ded 100644 --- a/.b4-config +++ b/.b4-config @@ -11,4 +11,3 @@ prep-perpatch-check-cmd = scripts/checkpatch.pl -q --terse --no-summary --mailback - searchmask = https://lore.kernel.org/qemu-devel/?x=m&t=1&q=%s linkmask = https://lore.kernel.org/qemu-devel/%s - linktrailermask = Message-ID: <%s> From 3f2a36d0ef6339ce78e107959da81f76283582f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 30 Jun 2025 19:20:48 +0200 Subject: [PATCH 1803/2760] Makefile: prune quilt source files for cscope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both quilt, to apply patches, and cscope, to navigate in the code, are useful tools. Make sure source files that quilt saves when applying patches are not taken into account when building the cscope database. Link: https://lore.kernel.org/qemu-devel/20250630172048.519182-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index c92a3cf785..74c2da2037 100644 --- a/Makefile +++ b/Makefile @@ -227,6 +227,7 @@ distclean: clean recurse-distclean rm -Rf .sdk qemu-bundle find-src-path = find "$(SRC_PATH)" -path "$(SRC_PATH)/meson" -prune -o \ + -path "$(SRC_PATH)/.pc" -prune -o \ -type l -prune -o \( -name "*.[chsS]" -o -name "*.[ch].inc" \) .PHONY: ctags From 30edcb4d4e7a265c2912ca6978b150c7c75b654f Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:38 -0700 Subject: [PATCH 1804/2760] vfio-pci: preserve MSI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Save the MSI message area as part of vfio-pci vmstate, and preserve the interrupt and notifier eventfd's. migrate_incoming loads the MSI data, then the vfio-pci post_load handler finds the eventfds in CPR state, rebuilds vector data structures, and attaches the interrupts to the new KVM instance. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1751493538-202042-2-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 97 ++++++++++++++++++++++++++++++++++++++ hw/vfio/pci.c | 52 +++++++++++++++++++- hw/vfio/pci.h | 2 + include/hw/vfio/vfio-cpr.h | 8 ++++ 4 files changed, 157 insertions(+), 2 deletions(-) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index fdbb58e203..e467373e8d 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -9,6 +9,8 @@ #include "hw/vfio/vfio-device.h" #include "hw/vfio/vfio-cpr.h" #include "hw/vfio/pci.h" +#include "hw/pci/msix.h" +#include "hw/pci/msi.h" #include "migration/cpr.h" #include "qapi/error.h" #include "system/runstate.h" @@ -40,6 +42,69 @@ void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer) migration_remove_notifier(&bcontainer->cpr_reboot_notifier); } +#define STRDUP_VECTOR_FD_NAME(vdev, name) \ + g_strdup_printf("%s_%s", (vdev)->vbasedev.name, (name)) + +void vfio_cpr_save_vector_fd(VFIOPCIDevice *vdev, const char *name, int nr, + int fd) +{ + g_autofree char *fdname = STRDUP_VECTOR_FD_NAME(vdev, name); + cpr_save_fd(fdname, nr, fd); +} + +int vfio_cpr_load_vector_fd(VFIOPCIDevice *vdev, const char *name, int nr) +{ + g_autofree char *fdname = STRDUP_VECTOR_FD_NAME(vdev, name); + return cpr_find_fd(fdname, nr); +} + +void vfio_cpr_delete_vector_fd(VFIOPCIDevice *vdev, const char *name, int nr) +{ + g_autofree char *fdname = STRDUP_VECTOR_FD_NAME(vdev, name); + cpr_delete_fd(fdname, nr); +} + +static void vfio_cpr_claim_vectors(VFIOPCIDevice *vdev, int nr_vectors, + bool msix) +{ + int i, fd; + bool pending = false; + PCIDevice *pdev = &vdev->pdev; + + vdev->nr_vectors = nr_vectors; + vdev->msi_vectors = g_new0(VFIOMSIVector, nr_vectors); + vdev->interrupt = msix ? VFIO_INT_MSIX : VFIO_INT_MSI; + + vfio_pci_prepare_kvm_msi_virq_batch(vdev); + + for (i = 0; i < nr_vectors; i++) { + VFIOMSIVector *vector = &vdev->msi_vectors[i]; + + fd = vfio_cpr_load_vector_fd(vdev, "interrupt", i); + if (fd >= 0) { + vfio_pci_vector_init(vdev, i); + vfio_pci_msi_set_handler(vdev, i); + } + + if (vfio_cpr_load_vector_fd(vdev, "kvm_interrupt", i) >= 0) { + vfio_pci_add_kvm_msi_virq(vdev, vector, i, msix); + } else { + vdev->msi_vectors[i].virq = -1; + } + + if (msix && msix_is_pending(pdev, i) && msix_is_masked(pdev, i)) { + set_bit(i, vdev->msix->pending); + pending = true; + } + } + + vfio_pci_commit_kvm_msi_virq_batch(vdev); + + if (msix) { + memory_region_set_enabled(&pdev->msix_pba_mmio, pending); + } +} + /* * The kernel may change non-emulated config bits. Exclude them from the * changed-bits check in get_pci_config_device. @@ -58,13 +123,45 @@ static int vfio_cpr_pci_pre_load(void *opaque) return 0; } +static int vfio_cpr_pci_post_load(void *opaque, int version_id) +{ + VFIOPCIDevice *vdev = opaque; + PCIDevice *pdev = &vdev->pdev; + int nr_vectors; + + if (msix_enabled(pdev)) { + vfio_pci_msix_set_notifiers(vdev); + nr_vectors = vdev->msix->entries; + vfio_cpr_claim_vectors(vdev, nr_vectors, true); + + } else if (msi_enabled(pdev)) { + nr_vectors = msi_nr_vectors_allocated(pdev); + vfio_cpr_claim_vectors(vdev, nr_vectors, false); + + } else if (vfio_pci_read_config(pdev, PCI_INTERRUPT_PIN, 1)) { + g_assert_not_reached(); /* completed in a subsequent patch */ + } + + return 0; +} + +static bool pci_msix_present(void *opaque, int version_id) +{ + PCIDevice *pdev = opaque; + + return msix_present(pdev); +} + const VMStateDescription vfio_cpr_pci_vmstate = { .name = "vfio-cpr-pci", .version_id = 0, .minimum_version_id = 0, .pre_load = vfio_cpr_pci_pre_load, + .post_load = vfio_cpr_pci_post_load, .needed = cpr_incoming_needed, .fields = (VMStateField[]) { + VMSTATE_PCI_DEVICE(pdev, VFIOPCIDevice), + VMSTATE_MSIX_TEST(pdev, VFIOPCIDevice, pci_msix_present), VMSTATE_END_OF_LIST() } }; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index fa25bded25..5f9f2640e5 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -29,6 +29,7 @@ #include "hw/pci/pci_bridge.h" #include "hw/qdev-properties.h" #include "hw/qdev-properties-system.h" +#include "hw/vfio/vfio-cpr.h" #include "migration/vmstate.h" #include "migration/cpr.h" #include "qobject/qdict.h" @@ -57,20 +58,33 @@ static void vfio_disable_interrupts(VFIOPCIDevice *vdev); static void vfio_mmap_set_enabled(VFIOPCIDevice *vdev, bool enabled); static void vfio_msi_disable_common(VFIOPCIDevice *vdev); +/* Create new or reuse existing eventfd */ static bool vfio_notifier_init(VFIOPCIDevice *vdev, EventNotifier *e, const char *name, int nr, Error **errp) { - int ret = event_notifier_init(e, 0); + int fd, ret; + fd = vfio_cpr_load_vector_fd(vdev, name, nr); + if (fd >= 0) { + event_notifier_init_fd(e, fd); + return true; + } + + ret = event_notifier_init(e, 0); if (ret) { error_setg_errno(errp, -ret, "vfio_notifier_init %s failed", name); + return false; } - return !ret; + + fd = event_notifier_get_fd(e); + vfio_cpr_save_vector_fd(vdev, name, nr, fd); + return true; } static void vfio_notifier_cleanup(VFIOPCIDevice *vdev, EventNotifier *e, const char *name, int nr) { + vfio_cpr_delete_vector_fd(vdev, name, nr); event_notifier_cleanup(e); } @@ -394,6 +408,14 @@ static void vfio_msi_interrupt(void *opaque) notify(&vdev->pdev, nr); } +void vfio_pci_msi_set_handler(VFIOPCIDevice *vdev, int nr) +{ + VFIOMSIVector *vector = &vdev->msi_vectors[nr]; + int fd = event_notifier_get_fd(&vector->interrupt); + + qemu_set_fd_handler(fd, vfio_msi_interrupt, NULL, vector); +} + /* * Get MSI-X enabled, but no vector enabled, by setting vector 0 with an invalid * fd to kernel. @@ -656,6 +678,15 @@ static int vfio_msix_vector_do_use(PCIDevice *pdev, unsigned int nr, static int vfio_msix_vector_use(PCIDevice *pdev, unsigned int nr, MSIMessage msg) { + /* + * Ignore the callback from msix_set_vector_notifiers during resume. + * The necessary subset of these actions is called from + * vfio_cpr_claim_vectors during post load. + */ + if (cpr_is_incoming()) { + return 0; + } + return vfio_msix_vector_do_use(pdev, nr, &msg, vfio_msi_interrupt); } @@ -686,6 +717,12 @@ static void vfio_msix_vector_release(PCIDevice *pdev, unsigned int nr) } } +void vfio_pci_msix_set_notifiers(VFIOPCIDevice *vdev) +{ + msix_set_vector_notifiers(&vdev->pdev, vfio_msix_vector_use, + vfio_msix_vector_release, NULL); +} + void vfio_pci_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev) { assert(!vdev->defer_kvm_irq_routing); @@ -2965,6 +3002,11 @@ void vfio_pci_register_err_notifier(VFIOPCIDevice *vdev) fd = event_notifier_get_fd(&vdev->err_notifier); qemu_set_fd_handler(fd, vfio_err_notifier_handler, NULL, vdev); + /* Do not alter irq_signaling during vfio_realize for cpr */ + if (cpr_is_incoming()) { + return; + } + if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_ERR_IRQ_INDEX, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); @@ -3032,6 +3074,12 @@ void vfio_pci_register_req_notifier(VFIOPCIDevice *vdev) fd = event_notifier_get_fd(&vdev->req_notifier); qemu_set_fd_handler(fd, vfio_req_notifier_handler, NULL, vdev); + /* Do not alter irq_signaling during vfio_realize for cpr */ + if (cpr_is_incoming()) { + vdev->req_enabled = true; + return; + } + if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_REQ_IRQ_INDEX, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, &err)) { error_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 5ba7330b27..495fae737d 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -218,6 +218,8 @@ void vfio_pci_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, void vfio_pci_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev); void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev); bool vfio_pci_intx_enable(VFIOPCIDevice *vdev, Error **errp); +void vfio_pci_msix_set_notifiers(VFIOPCIDevice *vdev); +void vfio_pci_msi_set_handler(VFIOPCIDevice *vdev, int nr); uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); void vfio_pci_write_config(PCIDevice *pdev, diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index dbb2a16b7a..f21578da3c 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -15,6 +15,7 @@ struct VFIOContainer; struct VFIOContainerBase; struct VFIOGroup; +struct VFIOPCIDevice; typedef int (*dma_map_fn)(const struct VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, @@ -53,6 +54,13 @@ void vfio_cpr_giommu_remap(struct VFIOContainerBase *bcontainer, bool vfio_cpr_ram_discard_register_listener( struct VFIOContainerBase *bcontainer, MemoryRegionSection *section); +void vfio_cpr_save_vector_fd(struct VFIOPCIDevice *vdev, const char *name, + int nr, int fd); +int vfio_cpr_load_vector_fd(struct VFIOPCIDevice *vdev, const char *name, + int nr); +void vfio_cpr_delete_vector_fd(struct VFIOPCIDevice *vdev, const char *name, + int nr); + extern const VMStateDescription vfio_cpr_pci_vmstate; #endif /* HW_VFIO_VFIO_CPR_H */ From 87aeaead5c75448d6e36bf6f9114862c8d523871 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:39 -0700 Subject: [PATCH 1805/2760] vfio-pci: preserve INTx MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Preserve vfio INTx state across cpr-transfer. Preserve VFIOINTx fields as follows: pin : Recover this from the vfio config in kernel space interrupt : Preserve its eventfd descriptor across exec. unmask : Ditto route.irq : This could perhaps be recovered in vfio_pci_post_load by calling pci_device_route_intx_to_irq(pin), whose implementation reads config space for a bridge device such as ich9. However, there is no guarantee that the bridge vmstate is read before vfio vmstate. Rather than fiddling with MigrationPriority for vmstate handlers, explicitly save route.irq in vfio vmstate. pending : save in vfio vmstate. mmap_timeout, mmap_timer : Re-initialize bool kvm_accel : Re-initialize In vfio_realize, defer calling vfio_intx_enable until the vmstate is available, in vfio_pci_post_load. Modify vfio_intx_enable and vfio_intx_kvm_enable to skip vfio initialization, but still perform kvm initialization. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1751493538-202042-3-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 27 ++++++++++++++++++++++++- hw/vfio/pci.c | 55 +++++++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index e467373e8d..f5555cabe7 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -139,7 +139,11 @@ static int vfio_cpr_pci_post_load(void *opaque, int version_id) vfio_cpr_claim_vectors(vdev, nr_vectors, false); } else if (vfio_pci_read_config(pdev, PCI_INTERRUPT_PIN, 1)) { - g_assert_not_reached(); /* completed in a subsequent patch */ + Error *local_err = NULL; + if (!vfio_pci_intx_enable(vdev, &local_err)) { + error_report_err(local_err); + return -1; + } } return 0; @@ -152,6 +156,26 @@ static bool pci_msix_present(void *opaque, int version_id) return msix_present(pdev); } +static const VMStateDescription vfio_intx_vmstate = { + .name = "vfio-cpr-intx", + .version_id = 0, + .minimum_version_id = 0, + .fields = (VMStateField[]) { + VMSTATE_BOOL(pending, VFIOINTx), + VMSTATE_UINT32(route.mode, VFIOINTx), + VMSTATE_INT32(route.irq, VFIOINTx), + VMSTATE_END_OF_LIST() + } +}; + +#define VMSTATE_VFIO_INTX(_field, _state) { \ + .name = (stringify(_field)), \ + .size = sizeof(VFIOINTx), \ + .vmsd = &vfio_intx_vmstate, \ + .flags = VMS_STRUCT, \ + .offset = vmstate_offset_value(_state, _field, VFIOINTx), \ +} + const VMStateDescription vfio_cpr_pci_vmstate = { .name = "vfio-cpr-pci", .version_id = 0, @@ -162,6 +186,7 @@ const VMStateDescription vfio_cpr_pci_vmstate = { .fields = (VMStateField[]) { VMSTATE_PCI_DEVICE(pdev, VFIOPCIDevice), VMSTATE_MSIX_TEST(pdev, VFIOPCIDevice, pci_msix_present), + VMSTATE_VFIO_INTX(intx, VFIOPCIDevice), VMSTATE_END_OF_LIST() } }; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 5f9f2640e5..dd0b2a0b94 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -210,6 +210,36 @@ static bool vfio_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) #endif } +static bool vfio_cpr_intx_enable_kvm(VFIOPCIDevice *vdev, Error **errp) +{ +#ifdef CONFIG_KVM + if (vdev->no_kvm_intx || !kvm_irqfds_enabled() || + vdev->intx.route.mode != PCI_INTX_ENABLED || + !kvm_resamplefds_enabled()) { + return true; + } + + if (!vfio_notifier_init(vdev, &vdev->intx.unmask, "intx-unmask", 0, errp)) { + return false; + } + + if (kvm_irqchip_add_irqfd_notifier_gsi(kvm_state, + &vdev->intx.interrupt, + &vdev->intx.unmask, + vdev->intx.route.irq)) { + error_setg_errno(errp, errno, "failed to setup resample irqfd"); + vfio_notifier_cleanup(vdev, &vdev->intx.unmask, "intx-unmask", 0); + return false; + } + + vdev->intx.kvm_accel = true; + trace_vfio_intx_enable_kvm(vdev->vbasedev.name); + return true; +#else + return true; +#endif +} + static void vfio_intx_disable_kvm(VFIOPCIDevice *vdev) { #ifdef CONFIG_KVM @@ -305,7 +335,13 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) return true; } - vfio_disable_interrupts(vdev); + /* + * Do not alter interrupt state during vfio_realize and cpr load. + * The incoming state is cleared thereafter. + */ + if (!cpr_is_incoming()) { + vfio_disable_interrupts(vdev); + } vdev->intx.pin = pin - 1; /* Pin A (1) -> irq[0] */ pci_config_set_interrupt_pin(vdev->pdev.config, pin); @@ -328,6 +364,14 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) fd = event_notifier_get_fd(&vdev->intx.interrupt); qemu_set_fd_handler(fd, vfio_intx_interrupt, NULL, vdev); + + if (cpr_is_incoming()) { + if (!vfio_cpr_intx_enable_kvm(vdev, &err)) { + warn_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); + } + goto skip_signaling; + } + if (!vfio_device_irq_set_signaling(&vdev->vbasedev, VFIO_PCI_INTX_IRQ_INDEX, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd, errp)) { qemu_set_fd_handler(fd, NULL, NULL, vdev); @@ -339,6 +383,7 @@ static bool vfio_intx_enable(VFIOPCIDevice *vdev, Error **errp) warn_reportf_err(err, VFIO_MSG_PREFIX, vdev->vbasedev.name); } +skip_signaling: vdev->interrupt = VFIO_INT_INTx; trace_vfio_intx_enable(vdev->vbasedev.name); @@ -3237,7 +3282,13 @@ bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp) vfio_intx_routing_notifier); vdev->irqchip_change_notifier.notify = vfio_irqchip_change; kvm_irqchip_add_change_notifier(&vdev->irqchip_change_notifier); - if (!vfio_intx_enable(vdev, errp)) { + + /* + * During CPR, do not call vfio_intx_enable at this time. Instead, + * call it from vfio_pci_post_load after the intx routing data has + * been loaded from vmstate. + */ + if (!cpr_is_incoming() && !vfio_intx_enable(vdev, errp)) { timer_free(vdev->intx.mmap_timer); pci_device_set_intx_routing_notifier(&vdev->pdev, NULL); kvm_irqchip_remove_change_notifier(&vdev->irqchip_change_notifier); From 7ed0919119b0e7a6b7db1dcaca3a2cb30c771dd1 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:40 -0700 Subject: [PATCH 1806/2760] migration: close kvm after cpr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpr-transfer breaks vfio network connectivity to and from the guest, and the host system log shows: irq bypass consumer (token 00000000a03c32e5) registration fails: -16 which is EBUSY. This occurs because KVM descriptors are still open in the old QEMU process. Close them. Cc: Paolo Bonzini Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1751493538-202042-4-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- accel/kvm/kvm-all.c | 32 ++++++++++++++++++++++++++++++++ hw/vfio/cpr-legacy.c | 2 ++ hw/vfio/cpr.c | 21 +++++++++++++++++++++ hw/vfio/helpers.c | 11 +++++++++++ include/hw/vfio/vfio-cpr.h | 2 ++ include/hw/vfio/vfio-device.h | 2 ++ include/system/kvm.h | 1 + 7 files changed, 71 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index d095d1b98f..8141854617 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -515,16 +515,23 @@ static int do_kvm_destroy_vcpu(CPUState *cpu) goto err; } + /* If I am the CPU that created coalesced_mmio_ring, then discard it */ + if (s->coalesced_mmio_ring == (void *)cpu->kvm_run + PAGE_SIZE) { + s->coalesced_mmio_ring = NULL; + } + ret = munmap(cpu->kvm_run, mmap_size); if (ret < 0) { goto err; } + cpu->kvm_run = NULL; if (cpu->kvm_dirty_gfns) { ret = munmap(cpu->kvm_dirty_gfns, s->kvm_dirty_ring_bytes); if (ret < 0) { goto err; } + cpu->kvm_dirty_gfns = NULL; } kvm_park_vcpu(cpu); @@ -608,6 +615,31 @@ int kvm_init_vcpu(CPUState *cpu, Error **errp) return ret; } +void kvm_close(void) +{ + CPUState *cpu; + + if (!kvm_state || kvm_state->fd == -1) { + return; + } + + CPU_FOREACH(cpu) { + cpu_remove_sync(cpu); + close(cpu->kvm_fd); + cpu->kvm_fd = -1; + close(cpu->kvm_vcpu_stats_fd); + cpu->kvm_vcpu_stats_fd = -1; + } + + if (kvm_state && kvm_state->fd != -1) { + close(kvm_state->vmfd); + kvm_state->vmfd = -1; + close(kvm_state->fd); + kvm_state->fd = -1; + } + kvm_state = NULL; +} + /* * dirty pages logging control */ diff --git a/hw/vfio/cpr-legacy.c b/hw/vfio/cpr-legacy.c index 1216717546..553b203e9b 100644 --- a/hw/vfio/cpr-legacy.c +++ b/hw/vfio/cpr-legacy.c @@ -179,6 +179,8 @@ bool vfio_legacy_cpr_register_container(VFIOContainer *container, Error **errp) MIG_MODE_CPR_TRANSFER, -1) == 0; } + vfio_cpr_add_kvm_notifier(); + vmstate_register(NULL, -1, &vfio_container_vmstate, container); migration_add_notifier_mode(&container->cpr.transfer_notifier, diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index f5555cabe7..0e903cdd2f 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -190,3 +190,24 @@ const VMStateDescription vfio_cpr_pci_vmstate = { VMSTATE_END_OF_LIST() } }; + +static NotifierWithReturn kvm_close_notifier; + +static int vfio_cpr_kvm_close_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, + Error **errp) +{ + if (e->type == MIG_EVENT_PRECOPY_DONE) { + vfio_kvm_device_close(); + } + return 0; +} + +void vfio_cpr_add_kvm_notifier(void) +{ + if (!kvm_close_notifier.notify) { + migration_add_notifier_mode(&kvm_close_notifier, + vfio_cpr_kvm_close_notifier, + MIG_MODE_CPR_TRANSFER); + } +} diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index d0dbab1d17..9a5f621545 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -117,6 +117,17 @@ bool vfio_get_info_dma_avail(struct vfio_iommu_type1_info *info, int vfio_kvm_device_fd = -1; #endif +void vfio_kvm_device_close(void) +{ +#ifdef CONFIG_KVM + kvm_close(); + if (vfio_kvm_device_fd != -1) { + close(vfio_kvm_device_fd); + vfio_kvm_device_fd = -1; + } +#endif +} + int vfio_kvm_device_add_fd(int fd, Error **errp) { #ifdef CONFIG_KVM diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index f21578da3c..d37acc4a0a 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -63,4 +63,6 @@ void vfio_cpr_delete_vector_fd(struct VFIOPCIDevice *vdev, const char *name, extern const VMStateDescription vfio_cpr_pci_vmstate; +void vfio_cpr_add_kvm_notifier(void); + #endif /* HW_VFIO_VFIO_CPR_H */ diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index c616652ee7..f503837ccc 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -283,4 +283,6 @@ void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp); void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops, DeviceState *dev, bool ram_discard); int vfio_device_get_aw_bits(VFIODevice *vdev); + +void vfio_kvm_device_close(void); #endif /* HW_VFIO_VFIO_COMMON_H */ diff --git a/include/system/kvm.h b/include/system/kvm.h index 7cc60d26f2..4896a3c9c5 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h @@ -195,6 +195,7 @@ bool kvm_has_sync_mmu(void); int kvm_has_vcpu_events(void); int kvm_max_nested_state_length(void); int kvm_has_gsi_routing(void); +void kvm_close(void); /** * kvm_arm_supports_user_irq From ccfc6715cf54caafba4c2516af394b4ed979c615 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:41 -0700 Subject: [PATCH 1807/2760] migration: cpr_get_fd_param helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the helper function cpr_get_fd_param, to use when preserving a file descriptor that is opened externally and passed to QEMU. cpr_get_fd_param returns a descriptor number either from a QEMU command-line parameter, from a getfd command, or from CPR state. When a descriptor is passed to new QEMU via SCM_RIGHTS, its number changes. Hence, during CPR, the command-line parameter is ignored in new QEMU, and over-ridden by the value found in CPR state. Similarly, if the descriptor was originally specified by a getfd command in old QEMU, the fd number is not known outside of QEMU, and it changes when sent to new QEMU via SCM_RIGHTS. Hence the user cannot send getfd to new QEMU, but when the user sends a hotplug command that references the fd, cpr_get_fd_param finds its value in CPR state. Signed-off-by: Steve Sistare Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1751493538-202042-5-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- include/migration/cpr.h | 2 ++ migration/cpr.c | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/include/migration/cpr.h b/include/migration/cpr.h index 07858e93fa..eb27a93301 100644 --- a/include/migration/cpr.h +++ b/include/migration/cpr.h @@ -32,6 +32,8 @@ void cpr_state_close(void); struct QIOChannel *cpr_state_ioc(void); bool cpr_incoming_needed(void *opaque); +int cpr_get_fd_param(const char *name, const char *fdname, int index, + Error **errp); QEMUFile *cpr_transfer_output(MigrationChannel *channel, Error **errp); QEMUFile *cpr_transfer_input(MigrationChannel *channel, Error **errp); diff --git a/migration/cpr.c b/migration/cpr.c index a50a57edca..535d587aee 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -13,6 +13,7 @@ #include "migration/qemu-file.h" #include "migration/savevm.h" #include "migration/vmstate.h" +#include "monitor/monitor.h" #include "system/runstate.h" #include "trace.h" @@ -264,3 +265,39 @@ bool cpr_incoming_needed(void *opaque) MigMode mode = migrate_mode(); return mode == MIG_MODE_CPR_TRANSFER; } + +/* + * cpr_get_fd_param: find a descriptor and return its value. + * + * @name: CPR name for the descriptor + * @fdname: An integer-valued string, or a name passed to a getfd command + * @index: CPR index of the descriptor + * @errp: returned error message + * + * If CPR is not being performed, then use @fdname to find the fd. + * If CPR is being performed, then ignore @fdname, and look for @name + * and @index in CPR state. + * + * On success returns the fd value, else returns -1. + */ +int cpr_get_fd_param(const char *name, const char *fdname, int index, + Error **errp) +{ + ERRP_GUARD(); + int fd; + + if (cpr_is_incoming()) { + fd = cpr_find_fd(name, index); + if (fd < 0) { + error_setg(errp, "cannot find saved value for fd %s", fdname); + } + } else { + fd = monitor_fd_param(monitor_cur(), fdname, errp); + if (fd >= 0) { + cpr_save_fd(name, index, fd); + } else { + error_prepend(errp, "Could not parse object fd %s:", fdname); + } + } + return fd; +} From e563dc88c21915e111ecef0756cc291e9e473c35 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:42 -0700 Subject: [PATCH 1808/2760] backends/iommufd: iommufd_backend_map_file_dma MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define iommufd_backend_map_file_dma to implement IOMMU_IOAS_MAP_FILE. This will be called as a substitute for iommufd_backend_map_dma, so the error conditions for BARs are copied as-is from that function. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-6-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- backends/iommufd.c | 34 ++++++++++++++++++++++++++++++++++ backends/trace-events | 1 + include/system/iommufd.h | 3 +++ 3 files changed, 38 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index c2c47abf7e..3a2ecc7f5b 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -172,6 +172,40 @@ int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, return ret; } +int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id, + hwaddr iova, ram_addr_t size, + int mfd, unsigned long start, bool readonly) +{ + int ret, fd = be->fd; + struct iommu_ioas_map_file map = { + .size = sizeof(map), + .flags = IOMMU_IOAS_MAP_READABLE | + IOMMU_IOAS_MAP_FIXED_IOVA, + .ioas_id = ioas_id, + .fd = mfd, + .start = start, + .iova = iova, + .length = size, + }; + + if (!readonly) { + map.flags |= IOMMU_IOAS_MAP_WRITEABLE; + } + + ret = ioctl(fd, IOMMU_IOAS_MAP_FILE, &map); + trace_iommufd_backend_map_file_dma(fd, ioas_id, iova, size, mfd, start, + readonly, ret); + if (ret) { + ret = -errno; + + /* TODO: Not support mapping hardware PCI BAR region for now. */ + if (errno == EFAULT) { + warn_report("IOMMU_IOAS_MAP_FILE failed: %m, PCI BAR?"); + } + } + return ret; +} + int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, ram_addr_t size) { diff --git a/backends/trace-events b/backends/trace-events index 7278214ea5..e5f3e70cd1 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -11,6 +11,7 @@ iommufd_backend_connect(int fd, bool owned, uint32_t users) "fd=%d owned=%d user iommufd_backend_disconnect(int fd, uint32_t users) "fd=%d users=%d" iommu_backend_set_fd(int fd) "pre-opened /dev/iommu fd=%d" iommufd_backend_map_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, void *vaddr, bool readonly, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" addr=%p readonly=%d (%d)" +iommufd_backend_map_file_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int fd, unsigned long start, bool readonly, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" fd=%d start=%ld readonly=%d (%d)" iommufd_backend_unmap_dma_non_exist(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " Unmap nonexistent mapping: iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)" iommufd_backend_unmap_dma(int iommufd, uint32_t ioas, uint64_t iova, uint64_t size, int ret) " iommufd=%d ioas=%d iova=0x%"PRIx64" size=0x%"PRIx64" (%d)" iommufd_backend_alloc_ioas(int iommufd, uint32_t ioas) " iommufd=%d ioas=%d" diff --git a/include/system/iommufd.h b/include/system/iommufd.h index 283861b924..2d24d93d17 100644 --- a/include/system/iommufd.h +++ b/include/system/iommufd.h @@ -43,6 +43,9 @@ void iommufd_backend_disconnect(IOMMUFDBackend *be); bool iommufd_backend_alloc_ioas(IOMMUFDBackend *be, uint32_t *ioas_id, Error **errp); void iommufd_backend_free_id(IOMMUFDBackend *be, uint32_t id); +int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id, + hwaddr iova, ram_addr_t size, int fd, + unsigned long start, bool readonly); int iommufd_backend_map_dma(IOMMUFDBackend *be, uint32_t ioas_id, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly); int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, From ab48cedc648a60a3e51db73acf12148d90f19c4c Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:43 -0700 Subject: [PATCH 1809/2760] backends/iommufd: change process ioctl MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define the change process ioctl Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-7-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- backends/iommufd.c | 24 ++++++++++++++++++++++++ backends/trace-events | 1 + include/system/iommufd.h | 3 +++ 3 files changed, 28 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index 3a2ecc7f5b..87f81a05f6 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -73,6 +73,30 @@ static void iommufd_backend_class_init(ObjectClass *oc, const void *data) object_class_property_add_str(oc, "fd", NULL, iommufd_backend_set_fd); } +bool iommufd_change_process_capable(IOMMUFDBackend *be) +{ + struct iommu_ioas_change_process args = {.size = sizeof(args)}; + + /* + * Call IOMMU_IOAS_CHANGE_PROCESS to verify it is a recognized ioctl. + * This is a no-op if the process has not changed since DMA was mapped. + */ + return !ioctl(be->fd, IOMMU_IOAS_CHANGE_PROCESS, &args); +} + +bool iommufd_change_process(IOMMUFDBackend *be, Error **errp) +{ + struct iommu_ioas_change_process args = {.size = sizeof(args)}; + bool ret = !ioctl(be->fd, IOMMU_IOAS_CHANGE_PROCESS, &args); + + if (!ret) { + error_setg_errno(errp, errno, "IOMMU_IOAS_CHANGE_PROCESS fd %d failed", + be->fd); + } + trace_iommufd_change_process(be->fd, ret); + return ret; +} + bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp) { int fd; diff --git a/backends/trace-events b/backends/trace-events index e5f3e70cd1..56132d3fd2 100644 --- a/backends/trace-events +++ b/backends/trace-events @@ -7,6 +7,7 @@ dbus_vmstate_loading(const char *id) "id: %s" dbus_vmstate_saving(const char *id) "id: %s" # iommufd.c +iommufd_change_process(int fd, bool ret) "fd=%d (%d)" iommufd_backend_connect(int fd, bool owned, uint32_t users) "fd=%d owned=%d users=%d" iommufd_backend_disconnect(int fd, uint32_t users) "fd=%d users=%d" iommu_backend_set_fd(int fd) "pre-opened /dev/iommu fd=%d" diff --git a/include/system/iommufd.h b/include/system/iommufd.h index 2d24d93d17..db5f2c716c 100644 --- a/include/system/iommufd.h +++ b/include/system/iommufd.h @@ -69,6 +69,9 @@ bool iommufd_backend_invalidate_cache(IOMMUFDBackend *be, uint32_t id, uint32_t *entry_num, void *data, Error **errp); +bool iommufd_change_process_capable(IOMMUFDBackend *be); +bool iommufd_change_process(IOMMUFDBackend *be, Error **errp); + #define TYPE_HOST_IOMMU_DEVICE_IOMMUFD TYPE_HOST_IOMMU_DEVICE "-iommufd" OBJECT_DECLARE_TYPE(HostIOMMUDeviceIOMMUFD, HostIOMMUDeviceIOMMUFDClass, HOST_IOMMU_DEVICE_IOMMUFD) From d7ae4a740c8e49962ccfbbf41da0b3b8314518ce Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:44 -0700 Subject: [PATCH 1810/2760] physmem: qemu_ram_get_fd_offset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define qemu_ram_get_fd_offset, so CPR can map a memory region using IOMMU_IOAS_MAP_FILE in a subsequent patch. Signed-off-by: Steve Sistare Reviewed-by: Peter Xu Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-8-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- include/exec/cpu-common.h | 1 + system/physmem.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/exec/cpu-common.h b/include/exec/cpu-common.h index a68485547d..9b658a3f48 100644 --- a/include/exec/cpu-common.h +++ b/include/exec/cpu-common.h @@ -85,6 +85,7 @@ void qemu_ram_unset_idstr(RAMBlock *block); const char *qemu_ram_get_idstr(RAMBlock *rb); void *qemu_ram_get_host_addr(RAMBlock *rb); ram_addr_t qemu_ram_get_offset(RAMBlock *rb); +ram_addr_t qemu_ram_get_fd_offset(RAMBlock *rb); ram_addr_t qemu_ram_get_used_length(RAMBlock *rb); ram_addr_t qemu_ram_get_max_length(RAMBlock *rb); bool qemu_ram_is_shared(RAMBlock *rb); diff --git a/system/physmem.c b/system/physmem.c index ff0ca40222..130c148ffb 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -1593,6 +1593,11 @@ ram_addr_t qemu_ram_get_offset(RAMBlock *rb) return rb->offset; } +ram_addr_t qemu_ram_get_fd_offset(RAMBlock *rb) +{ + return rb->fd_offset; +} + ram_addr_t qemu_ram_get_used_length(RAMBlock *rb) { return rb->used_length; From fb32965b6dd8a001815593642a5146fbd2e85651 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:45 -0700 Subject: [PATCH 1811/2760] vfio/iommufd: use IOMMU_IOAS_MAP_FILE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use IOMMU_IOAS_MAP_FILE when the mapped region is backed by a file. Such a mapping can be preserved without modification during CPR, because it depends on the file's address space, which does not change, rather than on the process's address space, which does change. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-9-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/container-base.c | 9 +++++++++ hw/vfio/iommufd.c | 13 +++++++++++++ include/hw/vfio/vfio-container-base.h | 15 +++++++++++++++ 3 files changed, 37 insertions(+) diff --git a/hw/vfio/container-base.c b/hw/vfio/container-base.c index d834bd4822..56304978e1 100644 --- a/hw/vfio/container-base.c +++ b/hw/vfio/container-base.c @@ -78,7 +78,16 @@ int vfio_container_dma_map(VFIOContainerBase *bcontainer, void *vaddr, bool readonly, MemoryRegion *mr) { VFIOIOMMUClass *vioc = VFIO_IOMMU_GET_CLASS(bcontainer); + RAMBlock *rb = mr->ram_block; + int mfd = rb ? qemu_ram_get_fd(rb) : -1; + if (mfd >= 0 && vioc->dma_map_file) { + unsigned long start = vaddr - qemu_ram_get_host_addr(rb); + unsigned long offset = qemu_ram_get_fd_offset(rb); + + return vioc->dma_map_file(bcontainer, iova, size, mfd, start + offset, + readonly); + } g_assert(vioc->dma_map); return vioc->dma_map(bcontainer, iova, size, vaddr, readonly, mr); } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index d3efef71af..962a1e2b1f 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -45,6 +45,18 @@ static int iommufd_cdev_map(const VFIOContainerBase *bcontainer, hwaddr iova, iova, size, vaddr, readonly); } +static int iommufd_cdev_map_file(const VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + int fd, unsigned long start, bool readonly) +{ + const VFIOIOMMUFDContainer *container = + container_of(bcontainer, VFIOIOMMUFDContainer, bcontainer); + + return iommufd_backend_map_file_dma(container->be, + container->ioas_id, + iova, size, fd, start, readonly); +} + static int iommufd_cdev_unmap(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, IOMMUTLBEntry *iotlb, bool unmap_all) @@ -807,6 +819,7 @@ static void vfio_iommu_iommufd_class_init(ObjectClass *klass, const void *data) VFIOIOMMUClass *vioc = VFIO_IOMMU_CLASS(klass); vioc->dma_map = iommufd_cdev_map; + vioc->dma_map_file = iommufd_cdev_map_file; vioc->dma_unmap = iommufd_cdev_unmap; vioc->attach_device = iommufd_cdev_attach; vioc->detach_device = iommufd_cdev_detach; diff --git a/include/hw/vfio/vfio-container-base.h b/include/hw/vfio/vfio-container-base.h index 3cd86ec59e..bded6e993f 100644 --- a/include/hw/vfio/vfio-container-base.h +++ b/include/hw/vfio/vfio-container-base.h @@ -167,6 +167,21 @@ struct VFIOIOMMUClass { int (*dma_map)(const VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, bool readonly, MemoryRegion *mr); + /** + * @dma_map_file + * + * Map a file range for the container. + * + * @bcontainer: #VFIOContainerBase to use for map + * @iova: start address to map + * @size: size of the range to map + * @fd: descriptor of the file to map + * @start: starting file offset of the range to map + * @readonly: map read only if true + */ + int (*dma_map_file)(const VFIOContainerBase *bcontainer, + hwaddr iova, ram_addr_t size, + int fd, unsigned long start, bool readonly); /** * @dma_unmap * From b9b389b9e0f08b207786d501b790dab9cb2c09de Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:46 -0700 Subject: [PATCH 1812/2760] vfio/iommufd: invariant device name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpr-transfer will use the device name as a key to find the value of the device descriptor in new QEMU. However, if the descriptor number is specified by a command-line fd parameter, then vfio_device_get_name creates a name that includes the fd number. This causes a chicken-and-egg problem: new QEMU must know the fd number to construct a name to find the fd number. To fix, create an invariant name based on the id command-line parameter, if id is defined. The user will need to provide such an id to use CPR. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-10-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/device.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/hw/vfio/device.c b/hw/vfio/device.c index d91c695b69..3cd365fb8b 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -316,12 +316,17 @@ bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp) error_setg(errp, "Use FD passing only with iommufd backend"); return false; } - /* - * Give a name with fd so any function printing out vbasedev->name - * will not break. - */ if (!vbasedev->name) { - vbasedev->name = g_strdup_printf("VFIO_FD%d", vbasedev->fd); + + if (vbasedev->dev->id) { + vbasedev->name = g_strdup(vbasedev->dev->id); + return true; + } else { + /* + * Assign a name so any function printing it will not break. + */ + vbasedev->name = g_strdup_printf("VFIO_FD%d", vbasedev->fd); + } } } From 184053f04f6ad6b2950d4712063ffed43bb2720f Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:47 -0700 Subject: [PATCH 1813/2760] vfio/iommufd: add vfio_device_free_name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define vfio_device_free_name to free the name created by vfio_device_get_name. A subsequent patch will do more there. No functional change. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-11-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/ap.c | 4 ++-- hw/vfio/ccw.c | 4 ++-- hw/vfio/device.c | 5 +++++ hw/vfio/pci.c | 2 +- hw/vfio/platform.c | 2 +- include/hw/vfio/vfio-device.h | 1 + 6 files changed, 12 insertions(+), 6 deletions(-) diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c index 1df4438149..7719f24579 100644 --- a/hw/vfio/ap.c +++ b/hw/vfio/ap.c @@ -265,7 +265,7 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp) error: error_prepend(errp, VFIO_MSG_PREFIX, vbasedev->name); - g_free(vbasedev->name); + vfio_device_free_name(vbasedev); } static void vfio_ap_unrealize(DeviceState *dev) @@ -275,7 +275,7 @@ static void vfio_ap_unrealize(DeviceState *dev) vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX); vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_CFG_CHG_IRQ_INDEX); vfio_device_detach(&vapdev->vdev); - g_free(vapdev->vdev.name); + vfio_device_free_name(&vapdev->vdev); } static const Property vfio_ap_properties[] = { diff --git a/hw/vfio/ccw.c b/hw/vfio/ccw.c index cea9d6e005..9560b8d851 100644 --- a/hw/vfio/ccw.c +++ b/hw/vfio/ccw.c @@ -619,7 +619,7 @@ static void vfio_ccw_realize(DeviceState *dev, Error **errp) out_region_err: vfio_device_detach(vbasedev); out_attach_dev_err: - g_free(vbasedev->name); + vfio_device_free_name(vbasedev); out_unrealize: if (cdc->unrealize) { cdc->unrealize(cdev); @@ -637,7 +637,7 @@ static void vfio_ccw_unrealize(DeviceState *dev) vfio_ccw_unregister_irq_notifier(vcdev, VFIO_CCW_IO_IRQ_INDEX); vfio_ccw_put_region(vcdev); vfio_device_detach(&vcdev->vdev); - g_free(vcdev->vdev.name); + vfio_device_free_name(&vcdev->vdev); if (cdc->unrealize) { cdc->unrealize(cdev); diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 3cd365fb8b..97eddd04f5 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -333,6 +333,11 @@ bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp) return true; } +void vfio_device_free_name(VFIODevice *vbasedev) +{ + g_clear_pointer(&vbasedev->name, g_free); +} + void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp) { ERRP_GUARD(); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index dd0b2a0b94..1093b28df7 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2996,7 +2996,7 @@ void vfio_pci_put_device(VFIOPCIDevice *vdev) vfio_device_detach(&vdev->vbasedev); - g_free(vdev->vbasedev.name); + vfio_device_free_name(&vdev->vbasedev); g_free(vdev->msix); } diff --git a/hw/vfio/platform.c b/hw/vfio/platform.c index 9a21f2e50a..5c1795a26f 100644 --- a/hw/vfio/platform.c +++ b/hw/vfio/platform.c @@ -530,7 +530,7 @@ static bool vfio_base_device_init(VFIODevice *vbasedev, Error **errp) { /* @fd takes precedence over @sysfsdev which takes precedence over @host */ if (vbasedev->fd < 0 && vbasedev->sysfsdev) { - g_free(vbasedev->name); + vfio_device_free_name(vbasedev); vbasedev->name = g_path_get_basename(vbasedev->sysfsdev); } else if (vbasedev->fd < 0) { if (!vbasedev->name || strchr(vbasedev->name, '/')) { diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index f503837ccc..1901a35aa9 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -279,6 +279,7 @@ int vfio_device_get_irq_info(VFIODevice *vbasedev, int index, /* Returns 0 on success, or a negative errno. */ bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp); +void vfio_device_free_name(VFIODevice *vbasedev); void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp); void vfio_device_init(VFIODevice *vbasedev, int type, VFIODeviceOps *ops, DeviceState *dev, bool ram_discard); From a434fd8f6462c1541927d22e07c58425d6cbd84b Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:48 -0700 Subject: [PATCH 1814/2760] vfio/iommufd: device name blocker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If an invariant device name cannot be created, block CPR. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-12-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/device.c | 11 +++++++++++ include/hw/vfio/vfio-cpr.h | 1 + 2 files changed, 12 insertions(+) diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 97eddd04f5..0ae3f3c660 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -28,6 +28,8 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "qemu/units.h" +#include "migration/cpr.h" +#include "migration/blocker.h" #include "monitor/monitor.h" #include "vfio-helpers.h" @@ -324,8 +326,16 @@ bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp) } else { /* * Assign a name so any function printing it will not break. + * The fd number changes across processes, so this cannot be + * used as an invariant name for CPR. */ vbasedev->name = g_strdup_printf("VFIO_FD%d", vbasedev->fd); + error_setg(&vbasedev->cpr.id_blocker, + "vfio device with fd=%d needs an id property", + vbasedev->fd); + return migrate_add_blocker_modes(&vbasedev->cpr.id_blocker, + errp, MIG_MODE_CPR_TRANSFER, + -1) == 0; } } } @@ -336,6 +346,7 @@ bool vfio_device_get_name(VFIODevice *vbasedev, Error **errp) void vfio_device_free_name(VFIODevice *vbasedev) { g_clear_pointer(&vbasedev->name, g_free); + migrate_del_blocker(&vbasedev->cpr.id_blocker); } void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp) diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index d37acc4a0a..fa7d43ddd8 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -30,6 +30,7 @@ typedef struct VFIOContainerCPR { typedef struct VFIODeviceCPR { Error *mdev_blocker; + Error *id_blocker; } VFIODeviceCPR; bool vfio_legacy_cpr_register_container(struct VFIOContainer *container, From 06c6a65852af0b7648cdb6ff6cf2e66929a7b5f5 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:49 -0700 Subject: [PATCH 1815/2760] vfio/iommufd: register container for cpr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Register a vfio iommufd container and device for CPR, replacing the generic CPR register call with a more specific iommufd register call. Add a blocker if the kernel does not support IOMMU_IOAS_CHANGE_PROCESS. This is mostly boiler plate. The fields to to saved and restored are added in subsequent patches. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-13-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- backends/iommufd.c | 10 +++++ hw/vfio/cpr-iommufd.c | 86 ++++++++++++++++++++++++++++++++++++++ hw/vfio/iommufd.c | 6 ++- hw/vfio/meson.build | 1 + include/hw/vfio/vfio-cpr.h | 12 ++++++ include/system/iommufd.h | 1 + 6 files changed, 114 insertions(+), 2 deletions(-) create mode 100644 hw/vfio/cpr-iommufd.c diff --git a/backends/iommufd.c b/backends/iommufd.c index 87f81a05f6..c554ce5385 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -108,6 +108,13 @@ bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp) } be->fd = fd; } + if (!be->users && !vfio_iommufd_cpr_register_iommufd(be, errp)) { + if (be->owned) { + close(be->fd); + be->fd = -1; + } + return false; + } be->users++; trace_iommufd_backend_connect(be->fd, be->owned, be->users); @@ -125,6 +132,9 @@ void iommufd_backend_disconnect(IOMMUFDBackend *be) be->fd = -1; } out: + if (!be->users) { + vfio_iommufd_cpr_unregister_iommufd(be); + } trace_iommufd_backend_disconnect(be->fd, be->users); } diff --git a/hw/vfio/cpr-iommufd.c b/hw/vfio/cpr-iommufd.c new file mode 100644 index 0000000000..2f58b43793 --- /dev/null +++ b/hw/vfio/cpr-iommufd.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024-2025 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/vfio/vfio-cpr.h" +#include "migration/blocker.h" +#include "migration/cpr.h" +#include "migration/migration.h" +#include "migration/vmstate.h" +#include "system/iommufd.h" +#include "vfio-iommufd.h" + +static bool vfio_cpr_supported(IOMMUFDBackend *be, Error **errp) +{ + if (!iommufd_change_process_capable(be)) { + if (errp) { + error_setg(errp, "vfio iommufd backend does not support " + "IOMMU_IOAS_CHANGE_PROCESS"); + } + return false; + } + return true; +} + +static const VMStateDescription iommufd_cpr_vmstate = { + .name = "iommufd", + .version_id = 0, + .minimum_version_id = 0, + .needed = cpr_incoming_needed, + .fields = (VMStateField[]) { + VMSTATE_END_OF_LIST() + } +}; + +bool vfio_iommufd_cpr_register_iommufd(IOMMUFDBackend *be, Error **errp) +{ + Error **cpr_blocker = &be->cpr_blocker; + + if (!vfio_cpr_supported(be, cpr_blocker)) { + return migrate_add_blocker_modes(cpr_blocker, errp, + MIG_MODE_CPR_TRANSFER, -1) == 0; + } + + vmstate_register(NULL, -1, &iommufd_cpr_vmstate, be); + + return true; +} + +void vfio_iommufd_cpr_unregister_iommufd(IOMMUFDBackend *be) +{ + vmstate_unregister(NULL, &iommufd_cpr_vmstate, be); + migrate_del_blocker(&be->cpr_blocker); +} + +bool vfio_iommufd_cpr_register_container(VFIOIOMMUFDContainer *container, + Error **errp) +{ + VFIOContainerBase *bcontainer = &container->bcontainer; + + migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, + vfio_cpr_reboot_notifier, + MIG_MODE_CPR_REBOOT); + + vfio_cpr_add_kvm_notifier(); + + return true; +} + +void vfio_iommufd_cpr_unregister_container(VFIOIOMMUFDContainer *container) +{ + VFIOContainerBase *bcontainer = &container->bcontainer; + + migration_remove_notifier(&bcontainer->cpr_reboot_notifier); +} + +void vfio_iommufd_cpr_register_device(VFIODevice *vbasedev) +{ +} + +void vfio_iommufd_cpr_unregister_device(VFIODevice *vbasedev) +{ +} diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index 962a1e2b1f..ff291be235 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -446,7 +446,7 @@ static void iommufd_cdev_container_destroy(VFIOIOMMUFDContainer *container) if (!QLIST_EMPTY(&bcontainer->device_list)) { return; } - vfio_cpr_unregister_container(bcontainer); + vfio_iommufd_cpr_unregister_container(container); vfio_listener_unregister(bcontainer); iommufd_backend_free_id(container->be, container->ioas_id); object_unref(container); @@ -592,7 +592,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, goto err_listener_register; } - if (!vfio_cpr_register_container(bcontainer, errp)) { + if (!vfio_iommufd_cpr_register_container(container, errp)) { goto err_listener_register; } @@ -623,6 +623,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, } vfio_device_prepare(vbasedev, bcontainer, &dev_info); + vfio_iommufd_cpr_register_device(vbasedev); trace_iommufd_cdev_device_info(vbasedev->name, devfd, vbasedev->num_irqs, vbasedev->num_regions, vbasedev->flags); @@ -660,6 +661,7 @@ static void iommufd_cdev_detach(VFIODevice *vbasedev) iommufd_cdev_container_destroy(container); vfio_address_space_put(space); + vfio_iommufd_cpr_unregister_device(vbasedev); iommufd_cdev_unbind_and_disconnect(vbasedev); close(vbasedev->fd); } diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 63ea393076..7a881740a6 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -31,6 +31,7 @@ system_ss.add(when: 'CONFIG_VFIO', if_true: files( )) system_ss.add(when: ['CONFIG_VFIO', 'CONFIG_IOMMUFD'], if_true: files( 'iommufd.c', + 'cpr-iommufd.c', )) system_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( 'display.c', diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index fa7d43ddd8..87b4206d81 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -15,7 +15,10 @@ struct VFIOContainer; struct VFIOContainerBase; struct VFIOGroup; +struct VFIODevice; struct VFIOPCIDevice; +struct VFIOIOMMUFDContainer; +struct IOMMUFDBackend; typedef int (*dma_map_fn)(const struct VFIOContainerBase *bcontainer, hwaddr iova, ram_addr_t size, void *vaddr, @@ -44,6 +47,15 @@ bool vfio_cpr_register_container(struct VFIOContainerBase *bcontainer, Error **errp); void vfio_cpr_unregister_container(struct VFIOContainerBase *bcontainer); +bool vfio_iommufd_cpr_register_container(struct VFIOIOMMUFDContainer *container, + Error **errp); +void vfio_iommufd_cpr_unregister_container( + struct VFIOIOMMUFDContainer *container); +bool vfio_iommufd_cpr_register_iommufd(struct IOMMUFDBackend *be, Error **errp); +void vfio_iommufd_cpr_unregister_iommufd(struct IOMMUFDBackend *be); +void vfio_iommufd_cpr_register_device(struct VFIODevice *vbasedev); +void vfio_iommufd_cpr_unregister_device(struct VFIODevice *vbasedev); + int vfio_cpr_group_get_device_fd(int d, const char *name); bool vfio_cpr_container_match(struct VFIOContainer *container, diff --git a/include/system/iommufd.h b/include/system/iommufd.h index db5f2c716c..c9c72ffc45 100644 --- a/include/system/iommufd.h +++ b/include/system/iommufd.h @@ -32,6 +32,7 @@ struct IOMMUFDBackend { /*< protected >*/ int fd; /* /dev/iommu file descriptor */ bool owned; /* is the /dev/iommu opened internally */ + Error *cpr_blocker;/* set if be does not support CPR */ uint32_t users; /*< public >*/ From a6f2f9c42f3a5418fc7000b1fd331b086b6133d9 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:50 -0700 Subject: [PATCH 1816/2760] migration: vfio cpr state hook MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define a list of vfio devices in CPR state, in a subsection so that older QEMU can be live updated to this version. However, new QEMU will not be live updateable to old QEMU. This is acceptable because CPR is not yet commonly used, and updates to older versions are unusual. The contents of each device object will be defined by the vfio subsystem in a subsequent patch. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-14-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-iommufd.c | 2 ++ hw/vfio/iommufd-stubs.c | 18 ++++++++++++++++++ hw/vfio/meson.build | 1 + include/hw/vfio/vfio-cpr.h | 1 + include/migration/cpr.h | 12 ++++++++++++ migration/cpr.c | 15 ++++++--------- 6 files changed, 40 insertions(+), 9 deletions(-) create mode 100644 hw/vfio/iommufd-stubs.c diff --git a/hw/vfio/cpr-iommufd.c b/hw/vfio/cpr-iommufd.c index 2f58b43793..f95773b02c 100644 --- a/hw/vfio/cpr-iommufd.c +++ b/hw/vfio/cpr-iommufd.c @@ -14,6 +14,8 @@ #include "system/iommufd.h" #include "vfio-iommufd.h" +const VMStateDescription vmstate_cpr_vfio_devices; /* TBD in a later patch */ + static bool vfio_cpr_supported(IOMMUFDBackend *be, Error **errp) { if (!iommufd_change_process_capable(be)) { diff --git a/hw/vfio/iommufd-stubs.c b/hw/vfio/iommufd-stubs.c new file mode 100644 index 0000000000..0be5276175 --- /dev/null +++ b/hw/vfio/iommufd-stubs.c @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2025 Oracle and/or its affiliates. + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "migration/cpr.h" +#include "migration/vmstate.h" + +const VMStateDescription vmstate_cpr_vfio_devices = { + .name = CPR_STATE "/vfio devices", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]){ + VMSTATE_END_OF_LIST() + } +}; diff --git a/hw/vfio/meson.build b/hw/vfio/meson.build index 7a881740a6..bfaf6be805 100644 --- a/hw/vfio/meson.build +++ b/hw/vfio/meson.build @@ -33,6 +33,7 @@ system_ss.add(when: ['CONFIG_VFIO', 'CONFIG_IOMMUFD'], if_true: files( 'iommufd.c', 'cpr-iommufd.c', )) +system_ss.add(when: 'CONFIG_IOMMUFD', if_false: files('iommufd-stubs.c')) system_ss.add(when: 'CONFIG_VFIO_PCI', if_true: files( 'display.c', )) diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 87b4206d81..286e3d4e9a 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -75,6 +75,7 @@ void vfio_cpr_delete_vector_fd(struct VFIOPCIDevice *vdev, const char *name, int nr); extern const VMStateDescription vfio_cpr_pci_vmstate; +extern const VMStateDescription vmstate_cpr_vfio_devices; void vfio_cpr_add_kvm_notifier(void); diff --git a/include/migration/cpr.h b/include/migration/cpr.h index eb27a93301..3fc19a74ef 100644 --- a/include/migration/cpr.h +++ b/include/migration/cpr.h @@ -9,11 +9,23 @@ #define MIGRATION_CPR_H #include "qapi/qapi-types-migration.h" +#include "qemu/queue.h" #define MIG_MODE_NONE -1 #define QEMU_CPR_FILE_MAGIC 0x51435052 #define QEMU_CPR_FILE_VERSION 0x00000001 +#define CPR_STATE "CprState" + +typedef QLIST_HEAD(CprFdList, CprFd) CprFdList; +typedef QLIST_HEAD(CprVFIODeviceList, CprVFIODevice) CprVFIODeviceList; + +typedef struct CprState { + CprFdList fds; + CprVFIODeviceList vfio_devices; +} CprState; + +extern CprState cpr_state; void cpr_save_fd(const char *name, int id, int fd); void cpr_delete_fd(const char *name, int id); diff --git a/migration/cpr.c b/migration/cpr.c index 535d587aee..42ad0b0d50 100644 --- a/migration/cpr.c +++ b/migration/cpr.c @@ -7,6 +7,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "hw/vfio/vfio-device.h" #include "migration/cpr.h" #include "migration/misc.h" #include "migration/options.h" @@ -20,13 +21,7 @@ /*************************************************************************/ /* cpr state container for all information to be saved. */ -typedef QLIST_HEAD(CprFdList, CprFd) CprFdList; - -typedef struct CprState { - CprFdList fds; -} CprState; - -static CprState cpr_state; +CprState cpr_state; /****************************************************************************/ @@ -127,8 +122,6 @@ int cpr_open_fd(const char *path, int flags, const char *name, int id, } /*************************************************************************/ -#define CPR_STATE "CprState" - static const VMStateDescription vmstate_cpr_state = { .name = CPR_STATE, .version_id = 1, @@ -136,6 +129,10 @@ static const VMStateDescription vmstate_cpr_state = { .fields = (VMStateField[]) { VMSTATE_QLIST_V(fds, CprState, 1, vmstate_cpr_fd, CprFd, next), VMSTATE_END_OF_LIST() + }, + .subsections = (const VMStateDescription * const []) { + &vmstate_cpr_vfio_devices, + NULL } }; /*************************************************************************/ From f2f3e4667e4d6026f39ab17f355f79b2f8431e19 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:51 -0700 Subject: [PATCH 1817/2760] vfio/iommufd: cpr state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VFIO iommufd devices will need access to ioas_id, devid, and hwpt_id in new QEMU at realize time, so add them to CPR state. Define CprVFIODevice as the object which holds the state and is serialized to the vmstate file. Define accessors to copy state between VFIODevice and CprVFIODevice. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-15-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-iommufd.c | 98 +++++++++++++++++++++++++++++++++++++- hw/vfio/iommufd.c | 2 + hw/vfio/trace-events | 3 ++ include/hw/vfio/vfio-cpr.h | 3 ++ 4 files changed, 105 insertions(+), 1 deletion(-) diff --git a/hw/vfio/cpr-iommufd.c b/hw/vfio/cpr-iommufd.c index f95773b02c..4166201e3f 100644 --- a/hw/vfio/cpr-iommufd.c +++ b/hw/vfio/cpr-iommufd.c @@ -7,14 +7,98 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/vfio/vfio-cpr.h" +#include "hw/vfio/vfio-device.h" #include "migration/blocker.h" #include "migration/cpr.h" #include "migration/migration.h" #include "migration/vmstate.h" #include "system/iommufd.h" #include "vfio-iommufd.h" +#include "trace.h" + +typedef struct CprVFIODevice { + char *name; + unsigned int namelen; + uint32_t ioas_id; + int devid; + uint32_t hwpt_id; + QLIST_ENTRY(CprVFIODevice) next; +} CprVFIODevice; + +static const VMStateDescription vmstate_cpr_vfio_device = { + .name = "cpr vfio device", + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(namelen, CprVFIODevice), + VMSTATE_VBUFFER_ALLOC_UINT32(name, CprVFIODevice, 0, NULL, namelen), + VMSTATE_INT32(devid, CprVFIODevice), + VMSTATE_UINT32(ioas_id, CprVFIODevice), + VMSTATE_UINT32(hwpt_id, CprVFIODevice), + VMSTATE_END_OF_LIST() + } +}; + +const VMStateDescription vmstate_cpr_vfio_devices = { + .name = CPR_STATE "/vfio devices", + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]){ + VMSTATE_QLIST_V(vfio_devices, CprState, 1, vmstate_cpr_vfio_device, + CprVFIODevice, next), + VMSTATE_END_OF_LIST() + } +}; + +static void vfio_cpr_save_device(VFIODevice *vbasedev) +{ + CprVFIODevice *elem = g_new0(CprVFIODevice, 1); + + elem->name = g_strdup(vbasedev->name); + elem->namelen = strlen(vbasedev->name) + 1; + elem->ioas_id = vbasedev->cpr.ioas_id; + elem->devid = vbasedev->devid; + elem->hwpt_id = vbasedev->cpr.hwpt_id; + QLIST_INSERT_HEAD(&cpr_state.vfio_devices, elem, next); +} + +static CprVFIODevice *find_device(const char *name) +{ + CprVFIODeviceList *head = &cpr_state.vfio_devices; + CprVFIODevice *elem; + + QLIST_FOREACH(elem, head, next) { + if (!strcmp(elem->name, name)) { + return elem; + } + } + return NULL; +} + +static void vfio_cpr_delete_device(const char *name) +{ + CprVFIODevice *elem = find_device(name); -const VMStateDescription vmstate_cpr_vfio_devices; /* TBD in a later patch */ + if (elem) { + QLIST_REMOVE(elem, next); + g_free(elem->name); + g_free(elem); + } +} + +static bool vfio_cpr_find_device(VFIODevice *vbasedev) +{ + CprVFIODevice *elem = find_device(vbasedev->name); + + if (elem) { + vbasedev->cpr.ioas_id = elem->ioas_id; + vbasedev->devid = elem->devid; + vbasedev->cpr.hwpt_id = elem->hwpt_id; + trace_vfio_cpr_find_device(elem->ioas_id, elem->devid, elem->hwpt_id); + return true; + } + return false; +} static bool vfio_cpr_supported(IOMMUFDBackend *be, Error **errp) { @@ -81,8 +165,20 @@ void vfio_iommufd_cpr_unregister_container(VFIOIOMMUFDContainer *container) void vfio_iommufd_cpr_register_device(VFIODevice *vbasedev) { + if (!cpr_is_incoming()) { + vfio_cpr_save_device(vbasedev); + } } void vfio_iommufd_cpr_unregister_device(VFIODevice *vbasedev) { + vfio_cpr_delete_device(vbasedev->name); +} + +void vfio_cpr_load_device(VFIODevice *vbasedev) +{ + if (cpr_is_incoming()) { + bool ret = vfio_cpr_find_device(vbasedev); + g_assert(ret); + } } diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index ff291be235..f0d57ea65f 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -515,6 +515,8 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, const VFIOIOMMUClass *iommufd_vioc = VFIO_IOMMU_CLASS(object_class_by_name(TYPE_VFIO_IOMMU_IOMMUFD)); + vfio_cpr_load_device(vbasedev); + if (vbasedev->fd < 0) { devfd = iommufd_cdev_getfd(vbasedev->sysfsdev, errp); if (devfd < 0) { diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index e1728c4ef6..8ec0ad0cde 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -197,6 +197,9 @@ iommufd_cdev_alloc_ioas(int iommufd, int ioas_id) " [iommufd=%d] new IOMMUFD con iommufd_cdev_device_info(char *name, int devfd, int num_irqs, int num_regions, int flags) " %s (%d) num_irqs=%d num_regions=%d flags=%d" iommufd_cdev_pci_hot_reset_dep_devices(int domain, int bus, int slot, int function, int dev_id) "\t%04x:%02x:%02x.%x devid %d" +# cpr-iommufd.c +vfio_cpr_find_device(uint32_t ioas_id, int devid, uint32_t hwpt_id) "ioas_id %u, devid %d, hwpt_id %u" + # device.c vfio_device_get_region_info_type(const char *name, int index, uint32_t type, uint32_t subtype) "%s index %d, %08x/%08x" vfio_device_reset_handler(void) "" diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 286e3d4e9a..2878372495 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -34,6 +34,8 @@ typedef struct VFIOContainerCPR { typedef struct VFIODeviceCPR { Error *mdev_blocker; Error *id_blocker; + uint32_t hwpt_id; + uint32_t ioas_id; } VFIODeviceCPR; bool vfio_legacy_cpr_register_container(struct VFIOContainer *container, @@ -55,6 +57,7 @@ bool vfio_iommufd_cpr_register_iommufd(struct IOMMUFDBackend *be, Error **errp); void vfio_iommufd_cpr_unregister_iommufd(struct IOMMUFDBackend *be); void vfio_iommufd_cpr_register_device(struct VFIODevice *vbasedev); void vfio_iommufd_cpr_unregister_device(struct VFIODevice *vbasedev); +void vfio_cpr_load_device(struct VFIODevice *vbasedev); int vfio_cpr_group_get_device_fd(int d, const char *name); From 2a3f0a59bd6479f75fa5335f82b85b4f9cd7ed4e Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:52 -0700 Subject: [PATCH 1818/2760] vfio/iommufd: preserve descriptors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Save the iommu and vfio device fd in CPR state when it is created. After CPR, the fd number is found in CPR state and reused. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-16-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- backends/iommufd.c | 35 +++++++++++++++++++++++++++++------ hw/vfio/cpr-iommufd.c | 10 ++++++++++ hw/vfio/device.c | 9 +-------- 3 files changed, 40 insertions(+), 14 deletions(-) diff --git a/backends/iommufd.c b/backends/iommufd.c index c554ce5385..e0917923bf 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -16,12 +16,18 @@ #include "qemu/module.h" #include "qom/object_interfaces.h" #include "qemu/error-report.h" +#include "migration/cpr.h" #include "monitor/monitor.h" #include "trace.h" #include "hw/vfio/vfio-device.h" #include #include +static const char *iommufd_fd_name(IOMMUFDBackend *be) +{ + return object_get_canonical_path_component(OBJECT(be)); +} + static void iommufd_backend_init(Object *obj) { IOMMUFDBackend *be = IOMMUFD_BACKEND(obj); @@ -64,11 +70,27 @@ static bool iommufd_backend_can_be_deleted(UserCreatable *uc) return !be->users; } +static void iommufd_backend_complete(UserCreatable *uc, Error **errp) +{ + IOMMUFDBackend *be = IOMMUFD_BACKEND(uc); + const char *name = iommufd_fd_name(be); + + if (!be->owned) { + /* fd came from the command line. Fetch updated value from cpr state. */ + if (cpr_is_incoming()) { + be->fd = cpr_find_fd(name, 0); + } else { + cpr_save_fd(name, 0, be->fd); + } + } +} + static void iommufd_backend_class_init(ObjectClass *oc, const void *data) { UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); ucc->can_be_deleted = iommufd_backend_can_be_deleted; + ucc->complete = iommufd_backend_complete; object_class_property_add_str(oc, "fd", NULL, iommufd_backend_set_fd); } @@ -102,7 +124,7 @@ bool iommufd_backend_connect(IOMMUFDBackend *be, Error **errp) int fd; if (be->owned && !be->users) { - fd = qemu_open("/dev/iommu", O_RDWR, errp); + fd = cpr_open_fd("/dev/iommu", O_RDWR, iommufd_fd_name(be), 0, errp); if (fd < 0) { return false; } @@ -127,14 +149,15 @@ void iommufd_backend_disconnect(IOMMUFDBackend *be) goto out; } be->users--; - if (!be->users && be->owned) { - close(be->fd); - be->fd = -1; - } -out: if (!be->users) { vfio_iommufd_cpr_unregister_iommufd(be); + if (be->owned) { + cpr_delete_fd(iommufd_fd_name(be), 0); + close(be->fd); + be->fd = -1; + } } +out: trace_iommufd_backend_disconnect(be->fd, be->users); } diff --git a/hw/vfio/cpr-iommufd.c b/hw/vfio/cpr-iommufd.c index 4166201e3f..a72b68daa8 100644 --- a/hw/vfio/cpr-iommufd.c +++ b/hw/vfio/cpr-iommufd.c @@ -166,12 +166,18 @@ void vfio_iommufd_cpr_unregister_container(VFIOIOMMUFDContainer *container) void vfio_iommufd_cpr_register_device(VFIODevice *vbasedev) { if (!cpr_is_incoming()) { + /* + * Beware fd may have already been saved by vfio_device_set_fd, + * so call resave to avoid a duplicate entry. + */ + cpr_resave_fd(vbasedev->name, 0, vbasedev->fd); vfio_cpr_save_device(vbasedev); } } void vfio_iommufd_cpr_unregister_device(VFIODevice *vbasedev) { + cpr_delete_fd(vbasedev->name, 0); vfio_cpr_delete_device(vbasedev->name); } @@ -180,5 +186,9 @@ void vfio_cpr_load_device(VFIODevice *vbasedev) if (cpr_is_incoming()) { bool ret = vfio_cpr_find_device(vbasedev); g_assert(ret); + + if (vbasedev->fd < 0) { + vbasedev->fd = cpr_find_fd(vbasedev->name, 0); + } } } diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 0ae3f3c660..96cf21462c 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -351,14 +351,7 @@ void vfio_device_free_name(VFIODevice *vbasedev) void vfio_device_set_fd(VFIODevice *vbasedev, const char *str, Error **errp) { - ERRP_GUARD(); - int fd = monitor_fd_param(monitor_cur(), str, errp); - - if (fd < 0) { - error_prepend(errp, "Could not parse remote object fd %s:", str); - return; - } - vbasedev->fd = fd; + vbasedev->fd = cpr_get_fd_param(vbasedev->dev->id, str, 0, errp); } static VFIODeviceIOOps vfio_device_io_ops_ioctl; From 4296ee07455e48c169eb110fbca6ef724c119381 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:53 -0700 Subject: [PATCH 1819/2760] vfio/iommufd: reconstruct device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reconstruct userland device state after CPR. During vfio_realize, skip all ioctls that configure the device, as it was already configured in old QEMU. Skip bind, and use the devid from CPR state. Skip allocation of, and attachment to, ioas_id. Recover ioas_id from CPR state, and use it to find a matching container, if any, before creating a new one. This reconstruction is not complete. hwpt_id is handled in a subsequent patch. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-17-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index f0d57ea65f..a650517a1d 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -25,6 +25,7 @@ #include "system/reset.h" #include "qemu/cutils.h" #include "qemu/chardev_open.h" +#include "migration/cpr.h" #include "pci.h" #include "vfio-iommufd.h" #include "vfio-helpers.h" @@ -121,6 +122,10 @@ static bool iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp) goto err_kvm_device_add; } + if (cpr_is_incoming()) { + goto skip_bind; + } + /* Bind device to iommufd */ bind.iommufd = iommufd->fd; if (ioctl(vbasedev->fd, VFIO_DEVICE_BIND_IOMMUFD, &bind)) { @@ -132,6 +137,8 @@ static bool iommufd_cdev_connect_and_bind(VFIODevice *vbasedev, Error **errp) vbasedev->devid = bind.out_devid; trace_iommufd_cdev_connect_and_bind(bind.iommufd, vbasedev->name, vbasedev->fd, vbasedev->devid); + +skip_bind: return true; err_bind: iommufd_cdev_kvm_device_del(vbasedev); @@ -421,7 +428,9 @@ static bool iommufd_cdev_attach_container(VFIODevice *vbasedev, return iommufd_cdev_autodomains_get(vbasedev, container, errp); } - return !iommufd_cdev_attach_ioas_hwpt(vbasedev, container->ioas_id, errp); + /* If CPR, we are already attached to ioas_id. */ + return cpr_is_incoming() || + !iommufd_cdev_attach_ioas_hwpt(vbasedev, container->ioas_id, errp); } static void iommufd_cdev_detach_container(VFIODevice *vbasedev, @@ -510,6 +519,7 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, VFIOAddressSpace *space; struct vfio_device_info dev_info = { .argsz = sizeof(dev_info) }; int ret, devfd; + bool res; uint32_t ioas_id; Error *err = NULL; const VFIOIOMMUClass *iommufd_vioc = @@ -540,7 +550,16 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, vbasedev->iommufd != container->be) { continue; } - if (!iommufd_cdev_attach_container(vbasedev, container, &err)) { + + if (!cpr_is_incoming()) { + res = iommufd_cdev_attach_container(vbasedev, container, &err); + } else if (vbasedev->cpr.ioas_id == container->ioas_id) { + res = true; + } else { + continue; + } + + if (!res) { const char *msg = error_get_pretty(err); trace_iommufd_cdev_fail_attach_existing_container(msg); @@ -557,6 +576,11 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, } } + if (cpr_is_incoming()) { + ioas_id = vbasedev->cpr.ioas_id; + goto skip_ioas_alloc; + } + /* Need to allocate a new dedicated container */ if (!iommufd_backend_alloc_ioas(vbasedev->iommufd, &ioas_id, errp)) { goto err_alloc_ioas; @@ -564,10 +588,12 @@ static bool iommufd_cdev_attach(const char *name, VFIODevice *vbasedev, trace_iommufd_cdev_alloc_ioas(vbasedev->iommufd->fd, ioas_id); +skip_ioas_alloc: container = VFIO_IOMMU_IOMMUFD(object_new(TYPE_VFIO_IOMMU_IOMMUFD)); container->be = vbasedev->iommufd; container->ioas_id = ioas_id; QLIST_INIT(&container->hwpt_list); + vbasedev->cpr.ioas_id = ioas_id; bcontainer = &container->bcontainer; vfio_address_space_insert(space, bcontainer); From 010643eeb1521c0240c83ee8678544de0cd30b78 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:54 -0700 Subject: [PATCH 1820/2760] vfio/iommufd: reconstruct hwpt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skip allocation of, and attachment to, hwpt_id. Recover it from CPR state. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-18-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/iommufd.c | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/hw/vfio/iommufd.c b/hw/vfio/iommufd.c index a650517a1d..48c590b6a9 100644 --- a/hw/vfio/iommufd.c +++ b/hw/vfio/iommufd.c @@ -332,7 +332,14 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, /* Try to find a domain */ QLIST_FOREACH(hwpt, &container->hwpt_list, next) { - ret = iommufd_cdev_attach_ioas_hwpt(vbasedev, hwpt->hwpt_id, errp); + if (!cpr_is_incoming()) { + ret = iommufd_cdev_attach_ioas_hwpt(vbasedev, hwpt->hwpt_id, errp); + } else if (vbasedev->cpr.hwpt_id == hwpt->hwpt_id) { + ret = 0; + } else { + continue; + } + if (ret) { /* -EINVAL means the domain is incompatible with the device. */ if (ret == -EINVAL) { @@ -349,6 +356,7 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, return false; } else { vbasedev->hwpt = hwpt; + vbasedev->cpr.hwpt_id = hwpt->hwpt_id; QLIST_INSERT_HEAD(&hwpt->device_list, vbasedev, hwpt_next); vbasedev->iommu_dirty_tracking = iommufd_hwpt_dirty_tracking(hwpt); return true; @@ -371,6 +379,11 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, flags = IOMMU_HWPT_ALLOC_DIRTY_TRACKING; } + if (cpr_is_incoming()) { + hwpt_id = vbasedev->cpr.hwpt_id; + goto skip_alloc; + } + if (!iommufd_backend_alloc_hwpt(iommufd, vbasedev->devid, container->ioas_id, flags, IOMMU_HWPT_DATA_NONE, 0, NULL, @@ -378,19 +391,20 @@ static bool iommufd_cdev_autodomains_get(VFIODevice *vbasedev, return false; } + ret = iommufd_cdev_attach_ioas_hwpt(vbasedev, hwpt_id, errp); + if (ret) { + iommufd_backend_free_id(container->be, hwpt_id); + return false; + } + +skip_alloc: hwpt = g_malloc0(sizeof(*hwpt)); hwpt->hwpt_id = hwpt_id; hwpt->hwpt_flags = flags; QLIST_INIT(&hwpt->device_list); - ret = iommufd_cdev_attach_ioas_hwpt(vbasedev, hwpt->hwpt_id, errp); - if (ret) { - iommufd_backend_free_id(container->be, hwpt->hwpt_id); - g_free(hwpt); - return false; - } - vbasedev->hwpt = hwpt; + vbasedev->cpr.hwpt_id = hwpt->hwpt_id; vbasedev->iommu_dirty_tracking = iommufd_hwpt_dirty_tracking(hwpt); QLIST_INSERT_HEAD(&hwpt->device_list, vbasedev, hwpt_next); QLIST_INSERT_HEAD(&container->hwpt_list, hwpt, next); From 5c066c4be2328c46f03e9166ea720b13bf68ea5d Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:55 -0700 Subject: [PATCH 1821/2760] vfio/iommufd: change process MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finish CPR by change the owning process of the iommufd device in post load. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-19-git-send-email-steven.sistare@oracle.com [ clg: Fixed missing "qemu/error-report.h" include ] Signed-off-by: Cédric Le Goater --- hw/vfio/cpr-iommufd.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/hw/vfio/cpr-iommufd.c b/hw/vfio/cpr-iommufd.c index a72b68daa8..148a06d552 100644 --- a/hw/vfio/cpr-iommufd.c +++ b/hw/vfio/cpr-iommufd.c @@ -5,6 +5,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qapi/error.h" #include "hw/vfio/vfio-cpr.h" #include "hw/vfio/vfio-device.h" @@ -112,10 +113,40 @@ static bool vfio_cpr_supported(IOMMUFDBackend *be, Error **errp) return true; } +static int iommufd_cpr_pre_save(void *opaque) +{ + IOMMUFDBackend *be = opaque; + + /* + * The process has not changed yet, but proactively try the ioctl, + * and it will fail if any DMA mappings are not supported. + */ + if (!iommufd_change_process_capable(be)) { + error_report("some memory regions do not support " + "IOMMU_IOAS_CHANGE_PROCESS"); + return -1; + } + return 0; +} + +static int iommufd_cpr_post_load(void *opaque, int version_id) +{ + IOMMUFDBackend *be = opaque; + Error *local_err = NULL; + + if (!iommufd_change_process(be, &local_err)) { + error_report_err(local_err); + return -1; + } + return 0; +} + static const VMStateDescription iommufd_cpr_vmstate = { .name = "iommufd", .version_id = 0, .minimum_version_id = 0, + .pre_save = iommufd_cpr_pre_save, + .post_load = iommufd_cpr_post_load, .needed = cpr_incoming_needed, .fields = (VMStateField[]) { VMSTATE_END_OF_LIST() From 6ff4cccd13155e718e630fe16a72d3cc9decde3b Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:56 -0700 Subject: [PATCH 1822/2760] iommufd: preserve DMA mappings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During cpr-transfer load in new QEMU, the vfio_memory_listener causes spurious calls to map and unmap DMA regions, as devices are created and the address space is built. This memory was already already mapped by the device in old QEMU, so suppress the map and unmap callbacks during incoming CPR. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1751493538-202042-20-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- backends/iommufd.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/backends/iommufd.c b/backends/iommufd.c index e0917923bf..2a33c7ab0b 100644 --- a/backends/iommufd.c +++ b/backends/iommufd.c @@ -245,6 +245,10 @@ int iommufd_backend_map_file_dma(IOMMUFDBackend *be, uint32_t ioas_id, .length = size, }; + if (cpr_is_incoming()) { + return 0; + } + if (!readonly) { map.flags |= IOMMU_IOAS_MAP_WRITEABLE; } @@ -274,6 +278,10 @@ int iommufd_backend_unmap_dma(IOMMUFDBackend *be, uint32_t ioas_id, .length = size, }; + if (cpr_is_incoming()) { + return 0; + } + ret = ioctl(fd, IOMMU_IOAS_UNMAP, &unmap); /* * IOMMUFD takes mapping as some kind of object, unmapping From 99cedd5d552130b9b27743c40ca9012e1f4f0371 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:57 -0700 Subject: [PATCH 1823/2760] vfio/container: delete old cpr register MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vfio_cpr_[un]register_container is no longer used since they were subsumed by container type-specific registration. Delete them. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1751493538-202042-21-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 13 ------------- include/hw/vfio/vfio-cpr.h | 4 ---- 2 files changed, 17 deletions(-) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index 0e903cdd2f..af0f12a7ad 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -29,19 +29,6 @@ int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier, return 0; } -bool vfio_cpr_register_container(VFIOContainerBase *bcontainer, Error **errp) -{ - migration_add_notifier_mode(&bcontainer->cpr_reboot_notifier, - vfio_cpr_reboot_notifier, - MIG_MODE_CPR_REBOOT); - return true; -} - -void vfio_cpr_unregister_container(VFIOContainerBase *bcontainer) -{ - migration_remove_notifier(&bcontainer->cpr_reboot_notifier); -} - #define STRDUP_VECTOR_FD_NAME(vdev, name) \ g_strdup_printf("%s_%s", (vdev)->vbasedev.name, (name)) diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 2878372495..80ad20d216 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -45,10 +45,6 @@ void vfio_legacy_cpr_unregister_container(struct VFIOContainer *container); int vfio_cpr_reboot_notifier(NotifierWithReturn *notifier, MigrationEvent *e, Error **errp); -bool vfio_cpr_register_container(struct VFIOContainerBase *bcontainer, - Error **errp); -void vfio_cpr_unregister_container(struct VFIOContainerBase *bcontainer); - bool vfio_iommufd_cpr_register_container(struct VFIOIOMMUFDContainer *container, Error **errp); void vfio_iommufd_cpr_unregister_container( From 7437caad2052d920452ff7b9b7bc84f5e8e55c90 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 2 Jul 2025 14:58:58 -0700 Subject: [PATCH 1824/2760] vfio: doc changes for cpr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Update documentation to say that cpr-transfer supports vfio and iommufd. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/1751493538-202042-22-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- docs/devel/migration/CPR.rst | 5 ++--- qapi/migration.json | 6 ++++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/devel/migration/CPR.rst b/docs/devel/migration/CPR.rst index 7897873c86..0a0fd4f6dc 100644 --- a/docs/devel/migration/CPR.rst +++ b/docs/devel/migration/CPR.rst @@ -152,8 +152,7 @@ cpr-transfer mode This mode allows the user to transfer a guest to a new QEMU instance on the same host with minimal guest pause time, by preserving guest RAM in place, albeit with new virtual addresses in new QEMU. Devices -and their pinned memory pages will also be preserved in a future QEMU -release. +and their pinned memory pages are also preserved for VFIO and IOMMUFD. The user starts new QEMU on the same host as old QEMU, with command- line arguments to create the same machine, plus the ``-incoming`` @@ -322,6 +321,6 @@ Futures cpr-transfer mode is based on a capability to transfer open file descriptors from old to new QEMU. In the future, descriptors for -vfio, iommufd, vhost, and char devices could be transferred, +vhost, and char devices could be transferred, preserving those devices and their kernel state without interruption, even if they do not explicitly support live migration. diff --git a/qapi/migration.json b/qapi/migration.json index 4963f6ca12..e8a7d3b2a9 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -620,8 +620,10 @@ # # @cpr-transfer: This mode allows the user to transfer a guest to a # new QEMU instance on the same host with minimal guest pause -# time by preserving guest RAM in place. Devices and their pinned -# pages will also be preserved in a future QEMU release. +# time by preserving guest RAM in place. +# +# Devices and their pinned pages are also preserved for VFIO and +# IOMMUFD. (since 10.1) # # The user starts new QEMU on the same host as old QEMU, with # command-line arguments to create the same machine, plus the From 6888a4a9c8601005a2329fee6487c3c0df1348c0 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Thu, 3 Jul 2025 13:24:00 +0800 Subject: [PATCH 1825/2760] aspeed: Deprecate the ast2700a0-evb machine MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ast2700a0-evb machine represents the first revision of the AST2700 and serves as the initial engineering sample rather than a production version. A newer revision, A1, is now supported, and the ast2700a1-evb should replace the older A0 version. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250703052400.2927831-1-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- docs/about/deprecated.rst | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 42037131de..5a3ed71a64 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -315,6 +315,14 @@ deprecated; use the new name ``dtb-randomness`` instead. The new name better reflects the way this property affects all random data within the device tree blob, not just the ``kaslr-seed`` node. +Arm ``ast2700a0-evb`` machine (since 10.1) +'''''''''''''''''''''''''''''''''''''''''' + +The ``ast2700a0-evb`` machine represents the first revision of the AST2700 +and serves as the initial engineering sample rather than a production version. +A newer revision, A1, is now supported, and the ``ast2700a1-evb`` should +replace the older A0 version. + Mips ``mipssim`` machine (since 10.0) ''''''''''''''''''''''''''''''''''''' From 92096685a00414a813aa4735db1706e4e5c6917d Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Thu, 3 Jul 2025 07:42:46 -0700 Subject: [PATCH 1826/2760] hw/arm/aspeed: Add second SPI chip to Aspeed model MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aspeed2600 has two spi lanes; Add a new struct that can mount the second SPI. Signed-off-by: Ed Tanous Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250703144249.3348879-2-etanous@nvidia.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 2 ++ include/hw/arm/aspeed.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 94897505f8..8d7757e11f 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -465,6 +465,8 @@ static void aspeed_machine_init(MachineState *machine) aspeed_board_init_flashes(&bmc->soc->spi[0], bmc->spi_model ? bmc->spi_model : amc->spi_model, 1, amc->num_cs); + aspeed_board_init_flashes(&bmc->soc->spi[1], + amc->spi2_model, 1, amc->num_cs2); } if (machine->kernel_filename && sc->num_cpus > 1) { diff --git a/include/hw/arm/aspeed.h b/include/hw/arm/aspeed.h index 973277bea6..6c36455656 100644 --- a/include/hw/arm/aspeed.h +++ b/include/hw/arm/aspeed.h @@ -35,7 +35,9 @@ struct AspeedMachineClass { uint32_t hw_strap2; const char *fmc_model; const char *spi_model; + const char *spi2_model; uint32_t num_cs; + uint32_t num_cs2; uint32_t macs_mask; void (*i2c_init)(AspeedMachineState *bmc); uint32_t uart_default; From ad8e0e8a0088b8e30582c65b739a7ee8bb0d71ee Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Thu, 3 Jul 2025 07:42:47 -0700 Subject: [PATCH 1827/2760] docs: add support for gb200-bmc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch updates the docs for support of gb200-bmc. Signed-off-by: Ed Tanous Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250703144249.3348879-3-etanous@nvidia.com Signed-off-by: Cédric Le Goater --- docs/system/arm/aspeed.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index 43d27d83cb..bec0a1dfa8 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -1,5 +1,4 @@ -Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``, ``bletchley-bmc``, ``fuji-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) -================================================================================================================================================================================================================================================================================================================================================================================================================================= +Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``, ``bletchley-bmc``, ``fuji-bmc``, ``gb200nvl-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) The QEMU Aspeed machines model BMCs of various OpenPOWER systems and Aspeed evaluation boards. They are based on different releases of the @@ -35,6 +34,7 @@ AST2600 SoC based machines : - ``fuji-bmc`` Facebook Fuji BMC - ``bletchley-bmc`` Facebook Bletchley BMC - ``fby35-bmc`` Facebook fby35 BMC +- ``gb200nvl-bmc`` Nvidia GB200nvl BMC - ``qcom-dc-scm-v1-bmc`` Qualcomm DC-SCM V1 BMC - ``qcom-firework-bmc`` Qualcomm Firework BMC From becfaa10a2b554ac619fdd9ae0aa9cacdba73d67 Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Thu, 3 Jul 2025 07:42:48 -0700 Subject: [PATCH 1828/2760] hw/arm/aspeed: Add GB200 BMC target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GB200nvl72 is a system for for accelerated compute. This is a model for the BMC target within the system. This is based on the device tree aspeed-bmc-nvidia-gb200nvl-bmc.dts from: [1] https://github.com/openbmc/linux/blob/dev-6.6/arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-gb200nvl-bmc.dts Signed-off-by: Ed Tanous Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250703144249.3348879-4-etanous@nvidia.com Signed-off-by: Cédric Le Goater --- hw/arm/aspeed.c | 78 ++++++++++++++++++++++++++++++++++++++++++ hw/arm/aspeed_eeprom.c | 21 ++++++++++++ hw/arm/aspeed_eeprom.h | 3 ++ 3 files changed, 102 insertions(+) diff --git a/hw/arm/aspeed.c b/hw/arm/aspeed.c index 8d7757e11f..c31bbe7701 100644 --- a/hw/arm/aspeed.c +++ b/hw/arm/aspeed.c @@ -201,6 +201,10 @@ struct AspeedMachineState { #define BLETCHLEY_BMC_HW_STRAP1 0x00002000 #define BLETCHLEY_BMC_HW_STRAP2 0x00000801 +/* GB200NVL hardware value */ +#define GB200NVL_BMC_HW_STRAP1 AST2600_EVB_HW_STRAP1 +#define GB200NVL_BMC_HW_STRAP2 AST2600_EVB_HW_STRAP2 + /* Qualcomm DC-SCM hardware value */ #define QCOM_DC_SCM_V1_BMC_HW_STRAP1 0x00000000 #define QCOM_DC_SCM_V1_BMC_HW_STRAP2 0x00000041 @@ -647,6 +651,12 @@ static void create_pca9552(AspeedSoCState *soc, int bus_id, int addr) TYPE_PCA9552, addr); } +static I2CSlave *create_pca9554(AspeedSoCState *soc, int bus_id, int addr) +{ + return i2c_slave_create_simple(aspeed_i2c_get_bus(&soc->i2c, bus_id), + TYPE_PCA9554, addr); +} + static void sonorapass_bmc_i2c_init(AspeedMachineState *bmc) { AspeedSoCState *soc = bmc->soc; @@ -1226,6 +1236,45 @@ static void bletchley_bmc_i2c_init(AspeedMachineState *bmc) i2c_slave_create_simple(i2c[12], TYPE_PCA9552, 0x67); } + +static void gb200nvl_bmc_i2c_init(AspeedMachineState *bmc) +{ + AspeedSoCState *soc = bmc->soc; + I2CBus *i2c[15] = {}; + DeviceState *dev; + for (int i = 0; i < sizeof(i2c) / sizeof(i2c[0]); i++) { + if ((i == 11) || (i == 12) || (i == 13)) { + continue; + } + i2c[i] = aspeed_i2c_get_bus(&soc->i2c, i); + } + + /* Bus 5 Expander */ + create_pca9554(soc, 4, 0x21); + + /* Mux I2c Expanders */ + i2c_slave_create_simple(i2c[5], "pca9546", 0x71); + i2c_slave_create_simple(i2c[5], "pca9546", 0x72); + i2c_slave_create_simple(i2c[5], "pca9546", 0x73); + i2c_slave_create_simple(i2c[5], "pca9546", 0x75); + i2c_slave_create_simple(i2c[5], "pca9546", 0x76); + i2c_slave_create_simple(i2c[5], "pca9546", 0x77); + + /* Bus 10 */ + dev = DEVICE(create_pca9554(soc, 9, 0x20)); + + /* Set FPGA_READY */ + object_property_set_str(OBJECT(dev), "pin1", "high", &error_fatal); + + create_pca9554(soc, 9, 0x21); + at24c_eeprom_init(i2c[9], 0x50, 64 * KiB); + at24c_eeprom_init(i2c[9], 0x51, 64 * KiB); + + /* Bus 11 */ + at24c_eeprom_init_rom(i2c[10], 0x50, 256, gb200nvl_bmc_fruid, + gb200nvl_bmc_fruid_len); +} + static void fby35_i2c_init(AspeedMachineState *bmc) { AspeedSoCState *soc = bmc->soc; @@ -1782,6 +1831,31 @@ static void aspeed_machine_catalina_class_init(ObjectClass *oc, aspeed_machine_ast2600_class_emmc_init(oc); } +#define GB200NVL_BMC_RAM_SIZE ASPEED_RAM_SIZE(1 * GiB) + +static void aspeed_machine_gb200nvl_class_init(ObjectClass *oc, + const void *data) +{ + MachineClass *mc = MACHINE_CLASS(oc); + AspeedMachineClass *amc = ASPEED_MACHINE_CLASS(oc); + + mc->desc = "Nvidia GB200NVL BMC (Cortex-A7)"; + amc->soc_name = "ast2600-a3"; + amc->hw_strap1 = GB200NVL_BMC_HW_STRAP1; + amc->hw_strap2 = GB200NVL_BMC_HW_STRAP2; + amc->fmc_model = "mx66u51235f"; + amc->spi_model = "mx66u51235f"; + amc->num_cs = 2; + + amc->spi2_model = "mx66u51235f"; + amc->num_cs2 = 1; + amc->macs_mask = ASPEED_MAC0_ON | ASPEED_MAC1_ON; + amc->i2c_init = gb200nvl_bmc_i2c_init; + mc->default_ram_size = GB200NVL_BMC_RAM_SIZE; + aspeed_machine_class_init_cpus_defaults(mc); + aspeed_machine_ast2600_class_emmc_init(oc); +} + static void fby35_reset(MachineState *state, ResetType type) { AspeedMachineState *bmc = ASPEED_MACHINE(state); @@ -2074,6 +2148,10 @@ static const TypeInfo aspeed_machine_types[] = { .name = MACHINE_TYPE_NAME("bletchley-bmc"), .parent = TYPE_ASPEED_MACHINE, .class_init = aspeed_machine_bletchley_class_init, + }, { + .name = MACHINE_TYPE_NAME("gb200nvl-bmc"), + .parent = TYPE_ASPEED_MACHINE, + .class_init = aspeed_machine_gb200nvl_class_init, }, { .name = MACHINE_TYPE_NAME("catalina-bmc"), .parent = TYPE_ASPEED_MACHINE, diff --git a/hw/arm/aspeed_eeprom.c b/hw/arm/aspeed_eeprom.c index daa3d329d1..8bbbdec834 100644 --- a/hw/arm/aspeed_eeprom.c +++ b/hw/arm/aspeed_eeprom.c @@ -162,6 +162,25 @@ const uint8_t rainier_bmc_fruid[] = { 0x31, 0x50, 0x46, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, }; +const uint8_t gb200nvl_bmc_fruid[] = { + 0x01, 0x00, 0x00, 0x01, 0x0b, 0x00, 0x00, 0xf3, 0x01, 0x0a, 0x19, 0x1f, + 0x0f, 0xe6, 0xc6, 0x4e, 0x56, 0x49, 0x44, 0x49, 0x41, 0xc5, 0x50, 0x33, + 0x38, 0x30, 0x39, 0xcd, 0x31, 0x35, 0x38, 0x33, 0x33, 0x32, 0x34, 0x38, + 0x30, 0x30, 0x31, 0x35, 0x30, 0xd2, 0x36, 0x39, 0x39, 0x2d, 0x31, 0x33, + 0x38, 0x30, 0x39, 0x2d, 0x30, 0x34, 0x30, 0x34, 0x2d, 0x36, 0x30, 0x30, + 0xc0, 0x01, 0x01, 0xd6, 0x4d, 0x41, 0x43, 0x3a, 0x20, 0x33, 0x43, 0x3a, + 0x36, 0x44, 0x3a, 0x36, 0x36, 0x3a, 0x31, 0x34, 0x3a, 0x43, 0x38, 0x3a, + 0x37, 0x41, 0xc1, 0x3b, 0x01, 0x09, 0x19, 0xc6, 0x4e, 0x56, 0x49, 0x44, + 0x49, 0x41, 0xc9, 0x50, 0x33, 0x38, 0x30, 0x39, 0x2d, 0x42, 0x4d, 0x43, + 0xd2, 0x36, 0x39, 0x39, 0x2d, 0x31, 0x33, 0x38, 0x30, 0x39, 0x2d, 0x30, + 0x34, 0x30, 0x34, 0x2d, 0x36, 0x30, 0x30, 0xc4, 0x41, 0x45, 0x2e, 0x31, + 0xcd, 0x31, 0x35, 0x38, 0x33, 0x33, 0x32, 0x34, 0x38, 0x30, 0x30, 0x31, + 0x35, 0x30, 0xc0, 0xc4, 0x76, 0x30, 0x2e, 0x31, 0xc1, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0xb4, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + +}; + const size_t tiogapass_bmc_fruid_len = sizeof(tiogapass_bmc_fruid); const size_t fby35_nic_fruid_len = sizeof(fby35_nic_fruid); const size_t fby35_bb_fruid_len = sizeof(fby35_bb_fruid); @@ -169,3 +188,5 @@ const size_t fby35_bmc_fruid_len = sizeof(fby35_bmc_fruid); const size_t yosemitev2_bmc_fruid_len = sizeof(yosemitev2_bmc_fruid); const size_t rainier_bb_fruid_len = sizeof(rainier_bb_fruid); const size_t rainier_bmc_fruid_len = sizeof(rainier_bmc_fruid); +const size_t gb200nvl_bmc_fruid_len = sizeof(gb200nvl_bmc_fruid); + diff --git a/hw/arm/aspeed_eeprom.h b/hw/arm/aspeed_eeprom.h index f08c16ef50..3ed9bc1d9a 100644 --- a/hw/arm/aspeed_eeprom.h +++ b/hw/arm/aspeed_eeprom.h @@ -26,4 +26,7 @@ extern const size_t rainier_bb_fruid_len; extern const uint8_t rainier_bmc_fruid[]; extern const size_t rainier_bmc_fruid_len; +extern const uint8_t gb200nvl_bmc_fruid[]; +extern const size_t gb200nvl_bmc_fruid_len; + #endif From 3a34dad2c0d25cebafed40696bbbdeb7ff4b9c7d Mon Sep 17 00:00:00 2001 From: Ed Tanous Date: Thu, 3 Jul 2025 07:42:49 -0700 Subject: [PATCH 1829/2760] tests/functional: Add gb200 tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To support the newly added gb200 machine, add appropriate tests and extend do_test_arm_aspeed_openbmc() to support the hostname of this new system: "gb200nvl-obmc". Signed-off-by: Ed Tanous Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250703144249.3348879-5-etanous@nvidia.com [ clg: Adjust commit log to document do_test_arm_aspeed_openbmc() change ] Signed-off-by: Cédric Le Goater --- tests/functional/aspeed.py | 9 +++++-- tests/functional/meson.build | 2 ++ .../test_arm_aspeed_gb200nvl_bmc.py | 26 +++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 tests/functional/test_arm_aspeed_gb200nvl_bmc.py diff --git a/tests/functional/aspeed.py b/tests/functional/aspeed.py index 7a40d5dda7..b131703c52 100644 --- a/tests/functional/aspeed.py +++ b/tests/functional/aspeed.py @@ -8,8 +8,13 @@ class AspeedTest(LinuxKernelTest): def do_test_arm_aspeed_openbmc(self, machine, image, uboot='2019.04', - cpu_id='0x0', soc='AST2500 rev A1'): - hostname = machine.removesuffix('-bmc') + cpu_id='0x0', soc='AST2500 rev A1', + image_hostname=None): + # Allow for the image hostname to not end in "-bmc" + if image_hostname is not None: + hostname = image_hostname + else: + hostname = machine.removesuffix('-bmc') self.set_machine(machine) self.vm.set_console() diff --git a/tests/functional/meson.build b/tests/functional/meson.build index fb87b957aa..050c9000b9 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -33,6 +33,7 @@ test_timeouts = { 'arm_aspeed_ast2600' : 1200, 'arm_aspeed_bletchley' : 480, 'arm_aspeed_catalina' : 480, + 'arm_aspeed_gb200nvl_bmc' : 480, 'arm_aspeed_rainier' : 480, 'arm_bpim2u' : 500, 'arm_collie' : 180, @@ -129,6 +130,7 @@ tests_arm_system_thorough = [ 'arm_aspeed_ast2600', 'arm_aspeed_bletchley', 'arm_aspeed_catalina', + 'arm_aspeed_gb200nvl_bmc', 'arm_aspeed_rainier', 'arm_bpim2u', 'arm_canona1100', diff --git a/tests/functional/test_arm_aspeed_gb200nvl_bmc.py b/tests/functional/test_arm_aspeed_gb200nvl_bmc.py new file mode 100644 index 0000000000..8e8e3f05c1 --- /dev/null +++ b/tests/functional/test_arm_aspeed_gb200nvl_bmc.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python3 +# +# Functional test that boots the ASPEED machines +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from aspeed import AspeedTest + + +class GB200Machine(AspeedTest): + + ASSET_GB200_FLASH = Asset( + 'https://github.com/legoater/qemu-aspeed-boot/raw/refs/heads/master/images/gb200nvl-obmc/obmc-phosphor-image-gb200nvl-obmc-20250702182348.static.mtd.xz', + 'b84819317cb3dc762895ad507705978ef000bfc77c50c33a63bdd37921db0dbc') + + def test_arm_aspeed_gb200_openbmc(self): + image_path = self.uncompress(self.ASSET_GB200_FLASH) + + self.do_test_arm_aspeed_openbmc('gb200nvl-bmc', image=image_path, + uboot='2019.04', cpu_id='0xf00', + soc='AST2600 rev A3', + image_hostname='gb200nvl-obmc') + +if __name__ == '__main__': + AspeedTest.main() From d30245d3f54187d5ef4fbc9013386b98b22da2b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Jun 2025 07:56:13 +0200 Subject: [PATCH 1830/2760] system/cpus: Assert interrupt handling is done with BQL locked MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-5-philmd@linaro.org> --- accel/tcg/tcg-accel-ops.c | 2 -- system/cpus.c | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index b24d6a7562..6116644d1c 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -93,8 +93,6 @@ static void tcg_cpu_reset_hold(CPUState *cpu) /* mask must never be zero, except for A20 change call */ void tcg_handle_interrupt(CPUState *cpu, int mask) { - g_assert(bql_locked()); - cpu->interrupt_request |= mask; /* diff --git a/system/cpus.c b/system/cpus.c index d16b0dff98..a43e0e4e79 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -265,6 +265,8 @@ static void generic_handle_interrupt(CPUState *cpu, int mask) void cpu_interrupt(CPUState *cpu, int mask) { + g_assert(bql_locked()); + if (cpus_accel->handle_interrupt) { cpus_accel->handle_interrupt(cpu, mask); } else { From f3fc87a14b601e7af727f556cf5a50df44d69839 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Jun 2025 14:15:52 +0200 Subject: [PATCH 1831/2760] accel/kvm: Remove kvm_init_cpu_signals() stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 57038a92bb0 ("cpus: extract out kvm-specific code to accel/kvm") the kvm_init_cpu_signals() stub is not necessary. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Message-Id: <20250703173248.44995-6-philmd@linaro.org> --- accel/stubs/kvm-stub.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index ecfd7636f5..b9b4427c91 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -105,11 +105,6 @@ unsigned int kvm_get_free_memslots(void) return 0; } -void kvm_init_cpu_signals(CPUState *cpu) -{ - abort(); -} - bool kvm_arm_supports_user_irq(void) { return false; From 06810394fdc013037811e317501902ed6ab9c276 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 07:02:17 +0200 Subject: [PATCH 1832/2760] accel/kvm: Reduce kvm_create_vcpu() declaration scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kvm_create_vcpu() is only used within the same file unit. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-7-philmd@linaro.org> --- accel/kvm/kvm-all.c | 8 +++++++- include/system/kvm.h | 8 -------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index d095d1b98f..17235f2646 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -453,7 +453,13 @@ static void kvm_reset_parked_vcpus(KVMState *s) } } -int kvm_create_vcpu(CPUState *cpu) +/** + * kvm_create_vcpu - Gets a parked KVM vCPU or creates a KVM vCPU + * @cpu: QOM CPUState object for which KVM vCPU has to be fetched/created. + * + * @returns: 0 when success, errno (<0) when failed. + */ +static int kvm_create_vcpu(CPUState *cpu) { unsigned long vcpu_id = kvm_arch_vcpu_id(cpu); KVMState *s = kvm_state; diff --git a/include/system/kvm.h b/include/system/kvm.h index 7cc60d26f2..e943df2c09 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h @@ -316,14 +316,6 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test); */ bool kvm_device_supported(int vmfd, uint64_t type); -/** - * kvm_create_vcpu - Gets a parked KVM vCPU or creates a KVM vCPU - * @cpu: QOM CPUState object for which KVM vCPU has to be fetched/created. - * - * @returns: 0 when success, errno (<0) when failed. - */ -int kvm_create_vcpu(CPUState *cpu); - /** * kvm_park_vcpu - Park QEMU KVM vCPU context * @cpu: QOM CPUState object for which QEMU KVM vCPU context has to be parked. From a8e49597d486123a62375eda6dbc939fffbfd31d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Jul 2025 11:42:43 +0200 Subject: [PATCH 1833/2760] accel/tcg: Remove 'info opcount' and @x-query-opcount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 1b65b4f54c7 ("accel/tcg: remove CONFIG_PROFILER", released with QEMU v8.1.0) we get pointless output: (qemu) info opcount [TCG profiler not compiled] Remove that unstable and unuseful command. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Dr. David Alan Gilbert Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Markus Armbruster Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-8-philmd@linaro.org> --- accel/tcg/monitor.c | 21 --------------------- hmp-commands-info.hx | 14 -------------- qapi/machine.json | 18 ------------------ tests/qtest/qmp-cmd-test.c | 1 - 4 files changed, 54 deletions(-) diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index 1c182b6bfb..7c686226b2 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -215,30 +215,9 @@ HumanReadableText *qmp_x_query_jit(Error **errp) return human_readable_text_from_str(buf); } -static void tcg_dump_op_count(GString *buf) -{ - g_string_append_printf(buf, "[TCG profiler not compiled]\n"); -} - -HumanReadableText *qmp_x_query_opcount(Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - - if (!tcg_enabled()) { - error_setg(errp, - "Opcode count information is only available with accel=tcg"); - return NULL; - } - - tcg_dump_op_count(buf); - - return human_readable_text_from_str(buf); -} - static void hmp_tcg_register(void) { monitor_register_hmp_info_hrt("jit", qmp_x_query_jit); - monitor_register_hmp_info_hrt("opcount", qmp_x_query_opcount); } type_init(hmp_tcg_register); diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 639a450ee5..d797922275 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -256,20 +256,6 @@ SRST Show dynamic compiler info. ERST -#if defined(CONFIG_TCG) - { - .name = "opcount", - .args_type = "", - .params = "", - .help = "show dynamic compiler opcode counters", - }, -#endif - -SRST - ``info opcount`` - Show dynamic compiler opcode counters -ERST - { .name = "sync-profile", .args_type = "mean:-m,no_coalesce:-n,max:i?", diff --git a/qapi/machine.json b/qapi/machine.json index 0650b8de71..f712e7da6d 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1761,24 +1761,6 @@ 'returns': 'HumanReadableText', 'features': [ 'unstable' ] } -## -# @x-query-opcount: -# -# Query TCG opcode counters -# -# Features: -# -# @unstable: This command is meant for debugging. -# -# Returns: TCG opcode counters -# -# Since: 6.2 -## -{ 'command': 'x-query-opcount', - 'returns': 'HumanReadableText', - 'if': 'CONFIG_TCG', - 'features': [ 'unstable' ] } - ## # @x-query-ramblock: # diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 040d042810..cf71876186 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -51,7 +51,6 @@ static int query_error_class(const char *cmd) { "x-query-usb", ERROR_CLASS_GENERIC_ERROR }, /* Only valid with accel=tcg */ { "x-query-jit", ERROR_CLASS_GENERIC_ERROR }, - { "x-query-opcount", ERROR_CLASS_GENERIC_ERROR }, { "xen-event-list", ERROR_CLASS_GENERIC_ERROR }, { NULL, -1 } }; From f1e59f012de35310314a2a12ea9cb5f72019c976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Jul 2025 11:42:17 +0200 Subject: [PATCH 1834/2760] accel/tcg: Remove profiler leftover MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TCG profiler was removed in commit 1b65b4f54c7. Fixes: 1b65b4f54c7 ("accel/tcg: remove CONFIG_PROFILER") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-9-philmd@linaro.org> --- accel/tcg/monitor.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index 7c686226b2..344ec50047 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -141,11 +141,6 @@ static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) *pelide = elide; } -static void tcg_dump_info(GString *buf) -{ - g_string_append_printf(buf, "[TCG profiler not compiled]\n"); -} - static void dump_exec_info(GString *buf) { struct tb_tree_stats tst = {}; @@ -196,7 +191,6 @@ static void dump_exec_info(GString *buf) g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); - tcg_dump_info(buf); } HumanReadableText *qmp_x_query_jit(Error **errp) From 04fbbeb7655372f500a7e1d610202a08a50cc364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 11:48:44 +0200 Subject: [PATCH 1835/2760] accel/tcg: Factor tcg_dump_flush_info() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-10-philmd@linaro.org> --- accel/tcg/monitor.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index 344ec50047..6d9cc11d94 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -141,11 +141,26 @@ static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) *pelide = elide; } +static void tcg_dump_flush_info(GString *buf) +{ + size_t flush_full, flush_part, flush_elide; + + g_string_append_printf(buf, "TB flush count %u\n", + qatomic_read(&tb_ctx.tb_flush_count)); + g_string_append_printf(buf, "TB invalidate count %u\n", + qatomic_read(&tb_ctx.tb_phys_invalidate_count)); + + tlb_flush_counts(&flush_full, &flush_part, &flush_elide); + g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); + g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); + g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); +} + static void dump_exec_info(GString *buf) { struct tb_tree_stats tst = {}; struct qht_stats hst; - size_t nb_tbs, flush_full, flush_part, flush_elide; + size_t nb_tbs; tcg_tb_foreach(tb_tree_stats_iter, &tst); nb_tbs = tst.nb_tbs; @@ -182,15 +197,7 @@ static void dump_exec_info(GString *buf) qht_statistics_destroy(&hst); g_string_append_printf(buf, "\nStatistics:\n"); - g_string_append_printf(buf, "TB flush count %u\n", - qatomic_read(&tb_ctx.tb_flush_count)); - g_string_append_printf(buf, "TB invalidate count %u\n", - qatomic_read(&tb_ctx.tb_phys_invalidate_count)); - - tlb_flush_counts(&flush_full, &flush_part, &flush_elide); - g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); - g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); - g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); + tcg_dump_flush_info(buf); } HumanReadableText *qmp_x_query_jit(Error **errp) From a472390e800825f8544b7aa7a3503b6a4ae4bec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Jul 2025 12:16:27 +0200 Subject: [PATCH 1836/2760] accel/tcg: Factor tcg_dump_stats() out for re-use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-11-philmd@linaro.org> --- accel/tcg/internal-common.h | 2 ++ accel/tcg/monitor.c | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index 1dbc45dd95..77a3a0684a 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -139,4 +139,6 @@ G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); +void tcg_dump_stats(GString *buf); + #endif diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index 6d9cc11d94..e7ed7281a4 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -200,6 +200,13 @@ static void dump_exec_info(GString *buf) tcg_dump_flush_info(buf); } +void tcg_dump_stats(GString *buf) +{ + dump_accel_info(buf); + dump_exec_info(buf); + dump_drift_info(buf); +} + HumanReadableText *qmp_x_query_jit(Error **errp) { g_autoptr(GString) buf = g_string_new(""); @@ -209,9 +216,7 @@ HumanReadableText *qmp_x_query_jit(Error **errp) return NULL; } - dump_accel_info(buf); - dump_exec_info(buf); - dump_drift_info(buf); + tcg_dump_stats(buf); return human_readable_text_from_str(buf); } From 0175310c385628de25a09905cb4bb35c2cd47d5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Jun 2025 11:17:43 +0200 Subject: [PATCH 1837/2760] accel/hvf: Restrict internal declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Common code only needs to know whether HVF is enabled and the QOM type. Move the rest to "hvf_int.h", removing the need for COMPILING_PER_TARGET #ifdef'ry. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-15-philmd@linaro.org> --- include/system/hvf.h | 38 -------------------------------------- include/system/hvf_int.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/include/system/hvf.h b/include/system/hvf.h index a9a502f0c8..d3dcf088b3 100644 --- a/include/system/hvf.h +++ b/include/system/hvf.h @@ -14,10 +14,6 @@ #define HVF_H #include "qemu/accel.h" -#include "qemu/queue.h" -#include "exec/vaddr.h" -#include "qom/object.h" -#include "exec/vaddr.h" #ifdef COMPILING_PER_TARGET # ifdef CONFIG_HVF @@ -40,38 +36,4 @@ typedef struct HVFState HVFState; DECLARE_INSTANCE_CHECKER(HVFState, HVF_STATE, TYPE_HVF_ACCEL) -#ifdef COMPILING_PER_TARGET -struct hvf_sw_breakpoint { - vaddr pc; - vaddr saved_insn; - int use_count; - QTAILQ_ENTRY(hvf_sw_breakpoint) entry; -}; - -struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, - vaddr pc); -int hvf_sw_breakpoints_active(CPUState *cpu); - -int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp); -int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp); -int hvf_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type); -int hvf_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type); -void hvf_arch_remove_all_hw_breakpoints(void); - -/* - * hvf_update_guest_debug: - * @cs: CPUState for the CPU to update - * - * Update guest to enable or disable debugging. Per-arch specifics will be - * handled by calling down to hvf_arch_update_guest_debug. - */ -int hvf_update_guest_debug(CPUState *cpu); -void hvf_arch_update_guest_debug(CPUState *cpu); - -/* - * Return whether the guest supports debugging. - */ -bool hvf_arch_supports_guest_debug(void); -#endif /* COMPILING_PER_TARGET */ - #endif diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h index d774e58df9..4f6db40c34 100644 --- a/include/system/hvf_int.h +++ b/include/system/hvf_int.h @@ -12,6 +12,8 @@ #define HVF_INT_H #include "qemu/queue.h" +#include "exec/vaddr.h" +#include "qom/object.h" #ifdef __aarch64__ #include @@ -77,4 +79,36 @@ int hvf_put_registers(CPUState *); int hvf_get_registers(CPUState *); void hvf_kick_vcpu_thread(CPUState *cpu); +struct hvf_sw_breakpoint { + vaddr pc; + vaddr saved_insn; + int use_count; + QTAILQ_ENTRY(hvf_sw_breakpoint) entry; +}; + +struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, + vaddr pc); +int hvf_sw_breakpoints_active(CPUState *cpu); + +int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp); +int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp); +int hvf_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type); +int hvf_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type); +void hvf_arch_remove_all_hw_breakpoints(void); + +/* + * hvf_update_guest_debug: + * @cs: CPUState for the CPU to update + * + * Update guest to enable or disable debugging. Per-arch specifics will be + * handled by calling down to hvf_arch_update_guest_debug. + */ +int hvf_update_guest_debug(CPUState *cpu); +void hvf_arch_update_guest_debug(CPUState *cpu); + +/* + * Return whether the guest supports debugging. + */ +bool hvf_arch_supports_guest_debug(void); + #endif From c4b231cbd3198a05bcaaef5c0a6ee3ecebd12e21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Jun 2025 11:33:53 +0200 Subject: [PATCH 1838/2760] accel/hvf: Move per-cpu method declarations to hvf-accel-ops.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hvf-all.c aims to contain the generic accel methods (TYPE_ACCEL), while hvf-accel-ops.c the per-vcpu methods (TYPE_ACCEL_OPS). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-16-philmd@linaro.org> --- accel/hvf/hvf-accel-ops.c | 30 ++++++++++++++++++++++++++++++ accel/hvf/hvf-all.c | 28 ---------------------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index b38977207d..3752334688 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -50,9 +50,11 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "qemu/queue.h" #include "system/address-spaces.h" #include "gdbstub/enums.h" #include "hw/boards.h" +#include "hw/core/cpu.h" #include "system/accel-ops.h" #include "system/cpus.h" #include "system/hvf.h" @@ -482,6 +484,34 @@ static void hvf_start_vcpu_thread(CPUState *cpu) cpu, QEMU_THREAD_JOINABLE); } +struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, vaddr pc) +{ + struct hvf_sw_breakpoint *bp; + + QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) { + if (bp->pc == pc) { + return bp; + } + } + return NULL; +} + +int hvf_sw_breakpoints_active(CPUState *cpu) +{ + return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints); +} + +static void do_hvf_update_guest_debug(CPUState *cpu, run_on_cpu_data arg) +{ + hvf_arch_update_guest_debug(cpu); +} + +int hvf_update_guest_debug(CPUState *cpu) +{ + run_on_cpu(cpu, do_hvf_update_guest_debug, RUN_ON_CPU_NULL); + return 0; +} + static int hvf_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len) { struct hvf_sw_breakpoint *bp; diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 8c387fda24..1c72c43ddb 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -41,31 +41,3 @@ void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line, abort(); } - -struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, vaddr pc) -{ - struct hvf_sw_breakpoint *bp; - - QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) { - if (bp->pc == pc) { - return bp; - } - } - return NULL; -} - -int hvf_sw_breakpoints_active(CPUState *cpu) -{ - return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints); -} - -static void do_hvf_update_guest_debug(CPUState *cpu, run_on_cpu_data arg) -{ - hvf_arch_update_guest_debug(cpu); -} - -int hvf_update_guest_debug(CPUState *cpu) -{ - run_on_cpu(cpu, do_hvf_update_guest_debug, RUN_ON_CPU_NULL); - return 0; -} From 5da232017a46a14f86cdbe70396b8051aacfed99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 14:57:25 +0200 Subject: [PATCH 1839/2760] accel/hvf: Move generic method declarations to hvf-all.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hvf-all.c aims to contain the generic accel methods (TYPE_ACCEL), while hvf-accel-ops.c the per-vcpu methods (TYPE_ACCEL_OPS). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-17-philmd@linaro.org> --- accel/hvf/hvf-accel-ops.c | 273 +------------------------------------- accel/hvf/hvf-all.c | 267 +++++++++++++++++++++++++++++++++++++ 2 files changed, 271 insertions(+), 269 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 3752334688..2c0715a17f 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -48,20 +48,16 @@ */ #include "qemu/osdep.h" -#include "qemu/error-report.h" +#include "qemu/guest-random.h" #include "qemu/main-loop.h" #include "qemu/queue.h" -#include "system/address-spaces.h" #include "gdbstub/enums.h" -#include "hw/boards.h" +#include "exec/cpu-common.h" #include "hw/core/cpu.h" #include "system/accel-ops.h" #include "system/cpus.h" #include "system/hvf.h" #include "system/hvf_int.h" -#include "system/runstate.h" -#include "qemu/guest-random.h" -#include "trace.h" HVFState *hvf_state; @@ -81,132 +77,6 @@ hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size) return NULL; } -struct mac_slot { - int present; - uint64_t size; - uint64_t gpa_start; - uint64_t gva; -}; - -struct mac_slot mac_slots[32]; - -static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags) -{ - struct mac_slot *macslot; - hv_return_t ret; - - macslot = &mac_slots[slot->slot_id]; - - if (macslot->present) { - if (macslot->size != slot->size) { - macslot->present = 0; - trace_hvf_vm_unmap(macslot->gpa_start, macslot->size); - ret = hv_vm_unmap(macslot->gpa_start, macslot->size); - assert_hvf_ok(ret); - } - } - - if (!slot->size) { - return 0; - } - - macslot->present = 1; - macslot->gpa_start = slot->start; - macslot->size = slot->size; - trace_hvf_vm_map(slot->start, slot->size, slot->mem, flags, - flags & HV_MEMORY_READ ? 'R' : '-', - flags & HV_MEMORY_WRITE ? 'W' : '-', - flags & HV_MEMORY_EXEC ? 'E' : '-'); - ret = hv_vm_map(slot->mem, slot->start, slot->size, flags); - assert_hvf_ok(ret); - return 0; -} - -static void hvf_set_phys_mem(MemoryRegionSection *section, bool add) -{ - hvf_slot *mem; - MemoryRegion *area = section->mr; - bool writable = !area->readonly && !area->rom_device; - hv_memory_flags_t flags; - uint64_t page_size = qemu_real_host_page_size(); - - if (!memory_region_is_ram(area)) { - if (writable) { - return; - } else if (!memory_region_is_romd(area)) { - /* - * If the memory device is not in romd_mode, then we actually want - * to remove the hvf memory slot so all accesses will trap. - */ - add = false; - } - } - - if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) || - !QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) { - /* Not page aligned, so we can not map as RAM */ - add = false; - } - - mem = hvf_find_overlap_slot( - section->offset_within_address_space, - int128_get64(section->size)); - - if (mem && add) { - if (mem->size == int128_get64(section->size) && - mem->start == section->offset_within_address_space && - mem->mem == (memory_region_get_ram_ptr(area) + - section->offset_within_region)) { - return; /* Same region was attempted to register, go away. */ - } - } - - /* Region needs to be reset. set the size to 0 and remap it. */ - if (mem) { - mem->size = 0; - if (do_hvf_set_memory(mem, 0)) { - error_report("Failed to reset overlapping slot"); - abort(); - } - } - - if (!add) { - return; - } - - if (area->readonly || - (!memory_region_is_ram(area) && memory_region_is_romd(area))) { - flags = HV_MEMORY_READ | HV_MEMORY_EXEC; - } else { - flags = HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC; - } - - /* Now make a new slot. */ - int x; - - for (x = 0; x < hvf_state->num_slots; ++x) { - mem = &hvf_state->slots[x]; - if (!mem->size) { - break; - } - } - - if (x == hvf_state->num_slots) { - error_report("No free slots"); - abort(); - } - - mem->size = int128_get64(section->size); - mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region; - mem->start = section->offset_within_address_space; - mem->region = area; - - if (do_hvf_set_memory(mem, flags)) { - error_report("Error registering new memory slot"); - abort(); - } -} - static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { if (!cpu->accel->dirty) { @@ -244,147 +114,10 @@ static void hvf_cpu_synchronize_pre_loadvm(CPUState *cpu) run_on_cpu(cpu, do_hvf_cpu_synchronize_set_dirty, RUN_ON_CPU_NULL); } -static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on) -{ - hvf_slot *slot; - - slot = hvf_find_overlap_slot( - section->offset_within_address_space, - int128_get64(section->size)); - - /* protect region against writes; begin tracking it */ - if (on) { - slot->flags |= HVF_SLOT_LOG; - hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size, - HV_MEMORY_READ | HV_MEMORY_EXEC); - /* stop tracking region*/ - } else { - slot->flags &= ~HVF_SLOT_LOG; - hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size, - HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC); - } -} - -static void hvf_log_start(MemoryListener *listener, - MemoryRegionSection *section, int old, int new) -{ - if (old != 0) { - return; - } - - hvf_set_dirty_tracking(section, 1); -} - -static void hvf_log_stop(MemoryListener *listener, - MemoryRegionSection *section, int old, int new) -{ - if (new != 0) { - return; - } - - hvf_set_dirty_tracking(section, 0); -} - -static void hvf_log_sync(MemoryListener *listener, - MemoryRegionSection *section) -{ - /* - * sync of dirty pages is handled elsewhere; just make sure we keep - * tracking the region. - */ - hvf_set_dirty_tracking(section, 1); -} - -static void hvf_region_add(MemoryListener *listener, - MemoryRegionSection *section) -{ - hvf_set_phys_mem(section, true); -} - -static void hvf_region_del(MemoryListener *listener, - MemoryRegionSection *section) -{ - hvf_set_phys_mem(section, false); -} - -static MemoryListener hvf_memory_listener = { - .name = "hvf", - .priority = MEMORY_LISTENER_PRIORITY_ACCEL, - .region_add = hvf_region_add, - .region_del = hvf_region_del, - .log_start = hvf_log_start, - .log_stop = hvf_log_stop, - .log_sync = hvf_log_sync, -}; - static void dummy_signal(int sig) { } -bool hvf_allowed; - -static int hvf_accel_init(MachineState *ms) -{ - int x; - hv_return_t ret; - HVFState *s; - int pa_range = 36; - MachineClass *mc = MACHINE_GET_CLASS(ms); - - if (mc->hvf_get_physical_address_range) { - pa_range = mc->hvf_get_physical_address_range(ms); - if (pa_range < 0) { - return -EINVAL; - } - } - - ret = hvf_arch_vm_create(ms, (uint32_t)pa_range); - assert_hvf_ok(ret); - - s = g_new0(HVFState, 1); - - s->num_slots = ARRAY_SIZE(s->slots); - for (x = 0; x < s->num_slots; ++x) { - s->slots[x].size = 0; - s->slots[x].slot_id = x; - } - - QTAILQ_INIT(&s->hvf_sw_breakpoints); - - hvf_state = s; - memory_listener_register(&hvf_memory_listener, &address_space_memory); - - return hvf_arch_init(); -} - -static inline int hvf_gdbstub_sstep_flags(void) -{ - return SSTEP_ENABLE | SSTEP_NOIRQ; -} - -static void hvf_accel_class_init(ObjectClass *oc, const void *data) -{ - AccelClass *ac = ACCEL_CLASS(oc); - ac->name = "HVF"; - ac->init_machine = hvf_accel_init; - ac->allowed = &hvf_allowed; - ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags; -} - -static const TypeInfo hvf_accel_type = { - .name = TYPE_HVF_ACCEL, - .parent = TYPE_ACCEL, - .instance_size = sizeof(HVFState), - .class_init = hvf_accel_class_init, -}; - -static void hvf_type_init(void) -{ - type_register_static(&hvf_accel_type); -} - -type_init(hvf_type_init); - static void hvf_vcpu_destroy(CPUState *cpu) { hv_return_t ret = hv_vcpu_destroy(cpu->accel->fd); @@ -639,8 +372,10 @@ static const TypeInfo hvf_accel_ops_type = { .class_init = hvf_accel_ops_class_init, .abstract = true, }; + static void hvf_accel_ops_register_types(void) { type_register_static(&hvf_accel_ops_type); } + type_init(hvf_accel_ops_register_types); diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 1c72c43ddb..ddc77e629f 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -10,9 +10,24 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "system/address-spaces.h" +#include "system/memory.h" #include "system/hvf.h" #include "system/hvf_int.h" #include "hw/core/cpu.h" +#include "hw/boards.h" +#include "trace.h" + +bool hvf_allowed; + +struct mac_slot { + int present; + uint64_t size; + uint64_t gpa_start; + uint64_t gva; +}; + +struct mac_slot mac_slots[32]; const char *hvf_return_string(hv_return_t ret) { @@ -41,3 +56,255 @@ void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line, abort(); } + +static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags) +{ + struct mac_slot *macslot; + hv_return_t ret; + + macslot = &mac_slots[slot->slot_id]; + + if (macslot->present) { + if (macslot->size != slot->size) { + macslot->present = 0; + trace_hvf_vm_unmap(macslot->gpa_start, macslot->size); + ret = hv_vm_unmap(macslot->gpa_start, macslot->size); + assert_hvf_ok(ret); + } + } + + if (!slot->size) { + return 0; + } + + macslot->present = 1; + macslot->gpa_start = slot->start; + macslot->size = slot->size; + trace_hvf_vm_map(slot->start, slot->size, slot->mem, flags, + flags & HV_MEMORY_READ ? 'R' : '-', + flags & HV_MEMORY_WRITE ? 'W' : '-', + flags & HV_MEMORY_EXEC ? 'E' : '-'); + ret = hv_vm_map(slot->mem, slot->start, slot->size, flags); + assert_hvf_ok(ret); + return 0; +} + +static void hvf_set_phys_mem(MemoryRegionSection *section, bool add) +{ + hvf_slot *mem; + MemoryRegion *area = section->mr; + bool writable = !area->readonly && !area->rom_device; + hv_memory_flags_t flags; + uint64_t page_size = qemu_real_host_page_size(); + + if (!memory_region_is_ram(area)) { + if (writable) { + return; + } else if (!memory_region_is_romd(area)) { + /* + * If the memory device is not in romd_mode, then we actually want + * to remove the hvf memory slot so all accesses will trap. + */ + add = false; + } + } + + if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) || + !QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) { + /* Not page aligned, so we can not map as RAM */ + add = false; + } + + mem = hvf_find_overlap_slot( + section->offset_within_address_space, + int128_get64(section->size)); + + if (mem && add) { + if (mem->size == int128_get64(section->size) && + mem->start == section->offset_within_address_space && + mem->mem == (memory_region_get_ram_ptr(area) + + section->offset_within_region)) { + return; /* Same region was attempted to register, go away. */ + } + } + + /* Region needs to be reset. set the size to 0 and remap it. */ + if (mem) { + mem->size = 0; + if (do_hvf_set_memory(mem, 0)) { + error_report("Failed to reset overlapping slot"); + abort(); + } + } + + if (!add) { + return; + } + + if (area->readonly || + (!memory_region_is_ram(area) && memory_region_is_romd(area))) { + flags = HV_MEMORY_READ | HV_MEMORY_EXEC; + } else { + flags = HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC; + } + + /* Now make a new slot. */ + int x; + + for (x = 0; x < hvf_state->num_slots; ++x) { + mem = &hvf_state->slots[x]; + if (!mem->size) { + break; + } + } + + if (x == hvf_state->num_slots) { + error_report("No free slots"); + abort(); + } + + mem->size = int128_get64(section->size); + mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region; + mem->start = section->offset_within_address_space; + mem->region = area; + + if (do_hvf_set_memory(mem, flags)) { + error_report("Error registering new memory slot"); + abort(); + } +} + +static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on) +{ + hvf_slot *slot; + + slot = hvf_find_overlap_slot( + section->offset_within_address_space, + int128_get64(section->size)); + + /* protect region against writes; begin tracking it */ + if (on) { + slot->flags |= HVF_SLOT_LOG; + hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size, + HV_MEMORY_READ | HV_MEMORY_EXEC); + /* stop tracking region*/ + } else { + slot->flags &= ~HVF_SLOT_LOG; + hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size, + HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC); + } +} + +static void hvf_log_start(MemoryListener *listener, + MemoryRegionSection *section, int old, int new) +{ + if (old != 0) { + return; + } + + hvf_set_dirty_tracking(section, 1); +} + +static void hvf_log_stop(MemoryListener *listener, + MemoryRegionSection *section, int old, int new) +{ + if (new != 0) { + return; + } + + hvf_set_dirty_tracking(section, 0); +} + +static void hvf_log_sync(MemoryListener *listener, + MemoryRegionSection *section) +{ + /* + * sync of dirty pages is handled elsewhere; just make sure we keep + * tracking the region. + */ + hvf_set_dirty_tracking(section, 1); +} + +static void hvf_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + hvf_set_phys_mem(section, true); +} + +static void hvf_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + hvf_set_phys_mem(section, false); +} + +static MemoryListener hvf_memory_listener = { + .name = "hvf", + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, + .region_add = hvf_region_add, + .region_del = hvf_region_del, + .log_start = hvf_log_start, + .log_stop = hvf_log_stop, + .log_sync = hvf_log_sync, +}; + +static int hvf_accel_init(MachineState *ms) +{ + int x; + hv_return_t ret; + HVFState *s; + int pa_range = 36; + MachineClass *mc = MACHINE_GET_CLASS(ms); + + if (mc->hvf_get_physical_address_range) { + pa_range = mc->hvf_get_physical_address_range(ms); + if (pa_range < 0) { + return -EINVAL; + } + } + + ret = hvf_arch_vm_create(ms, (uint32_t)pa_range); + assert_hvf_ok(ret); + + s = g_new0(HVFState, 1); + + s->num_slots = ARRAY_SIZE(s->slots); + for (x = 0; x < s->num_slots; ++x) { + s->slots[x].size = 0; + s->slots[x].slot_id = x; + } + + QTAILQ_INIT(&s->hvf_sw_breakpoints); + + hvf_state = s; + memory_listener_register(&hvf_memory_listener, &address_space_memory); + + return hvf_arch_init(); +} + +static int hvf_gdbstub_sstep_flags(void) +{ + return SSTEP_ENABLE | SSTEP_NOIRQ; +} + +static void hvf_accel_class_init(ObjectClass *oc, const void *data) +{ + AccelClass *ac = ACCEL_CLASS(oc); + ac->name = "HVF"; + ac->init_machine = hvf_accel_init; + ac->allowed = &hvf_allowed; + ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags; +} + +static const TypeInfo hvf_accel_type = { + .name = TYPE_HVF_ACCEL, + .parent = TYPE_ACCEL, + .instance_size = sizeof(HVFState), + .class_init = hvf_accel_class_init, +}; + +static void hvf_type_init(void) +{ + type_register_static(&hvf_accel_type); +} + +type_init(hvf_type_init); From 332ad068a025056e2ffd0dc7605897254eb20e15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 06:47:28 +0200 Subject: [PATCH 1840/2760] cpus: Document CPUState::vcpu_dirty field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-Id: <20250703173248.44995-18-philmd@linaro.org> --- include/hw/core/cpu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 162a56a5da..5eaf41a566 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -442,6 +442,7 @@ struct qemu_work_item; * @opaque: User data. * @mem_io_pc: Host Program Counter at which the memory was accessed. * @accel: Pointer to accelerator specific state. + * @vcpu_dirty: Hardware accelerator is not synchronized with QEMU state * @kvm_fd: vCPU file descriptor for KVM. * @work_mutex: Lock to prevent multiple access to @work_list. * @work_list: List of pending asynchronous work. @@ -538,7 +539,6 @@ struct CPUState { uint32_t kvm_fetch_index; uint64_t dirty_pages; int kvm_vcpu_stats_fd; - bool vcpu_dirty; /* Use by accel-block: CPU is executing an ioctl() */ QemuLockCnt in_ioctl_lock; @@ -554,6 +554,7 @@ struct CPUState { uint32_t halted; int32_t exception_index; + bool vcpu_dirty; AccelCPUState *accel; /* Used to keep track of an outstanding cpu throttle thread for migration From 93bbbcb8d6eb9325530da8c3c313220d45f27252 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 06:54:32 +0200 Subject: [PATCH 1841/2760] accel/hvf: Replace @dirty field by generic CPUState::vcpu_dirty field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need for accel-specific @dirty field when we have a generic one in CPUState. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-19-philmd@linaro.org> --- accel/hvf/hvf-accel-ops.c | 10 +++++----- include/system/hvf_int.h | 1 - target/arm/hvf/hvf.c | 4 ++-- target/i386/hvf/hvf.c | 4 ++-- target/i386/hvf/x86hvf.c | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 2c0715a17f..395b5a8e1c 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -79,15 +79,15 @@ hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size) static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { - if (!cpu->accel->dirty) { + if (!cpu->vcpu_dirty) { hvf_get_registers(cpu); - cpu->accel->dirty = true; + cpu->vcpu_dirty = true; } } static void hvf_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->accel->dirty) { + if (!cpu->vcpu_dirty) { run_on_cpu(cpu, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -96,7 +96,7 @@ static void do_hvf_cpu_synchronize_set_dirty(CPUState *cpu, run_on_cpu_data arg) { /* QEMU state is the reference, push it to HVF now and on next entry */ - cpu->accel->dirty = true; + cpu->vcpu_dirty = true; } static void hvf_cpu_synchronize_post_reset(CPUState *cpu) @@ -150,8 +150,8 @@ static int hvf_init_vcpu(CPUState *cpu) #else r = hv_vcpu_create(&cpu->accel->fd, HV_VCPU_DEFAULT); #endif - cpu->accel->dirty = true; assert_hvf_ok(r); + cpu->vcpu_dirty = true; cpu->accel->guest_debug_enabled = false; diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h index 4f6db40c34..5150c7dd9c 100644 --- a/include/system/hvf_int.h +++ b/include/system/hvf_int.h @@ -62,7 +62,6 @@ struct AccelCPUState { bool vtimer_masked; sigset_t unblock_ipi_mask; bool guest_debug_enabled; - bool dirty; }; void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line, diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 7b6d291e79..c9cfcdc08b 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -813,9 +813,9 @@ int hvf_put_registers(CPUState *cpu) static void flush_cpu_state(CPUState *cpu) { - if (cpu->accel->dirty) { + if (cpu->vcpu_dirty) { hvf_put_registers(cpu); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } } diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 99e37a33e5..818b50419f 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -733,9 +733,9 @@ int hvf_vcpu_exec(CPUState *cpu) } do { - if (cpu->accel->dirty) { + if (cpu->vcpu_dirty) { hvf_put_registers(cpu); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } if (hvf_inject_interrupts(cpu)) { diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 2057314892..17fce1d3cd 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -427,7 +427,7 @@ int hvf_process_events(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - if (!cs->accel->dirty) { + if (!cs->vcpu_dirty) { /* light weight sync for CPU_INTERRUPT_HARD and IF_MASK */ env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); } From 2098164a6bebe97b736cb1d657c2d750daf8eb76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 06:59:03 +0200 Subject: [PATCH 1842/2760] accel/nvmm: Replace @dirty field by generic CPUState::vcpu_dirty field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need for accel-specific @dirty field when we have a generic one in CPUState. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-20-philmd@linaro.org> --- target/i386/nvmm/nvmm-all.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index f1c6120ccf..aea61a6fd2 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -30,7 +30,6 @@ struct AccelCPUState { struct nvmm_vcpu vcpu; uint8_t tpr; bool stop; - bool dirty; /* Window-exiting for INTs/NMIs. */ bool int_window_exit; @@ -508,7 +507,7 @@ nvmm_io_callback(struct nvmm_io *io) } /* Needed, otherwise infinite loop. */ - current_cpu->accel->dirty = false; + current_cpu->vcpu_dirty = false; } static void @@ -517,7 +516,7 @@ nvmm_mem_callback(struct nvmm_mem *mem) cpu_physical_memory_rw(mem->gpa, mem->data, mem->size, mem->write); /* Needed, otherwise infinite loop. */ - current_cpu->accel->dirty = false; + current_cpu->vcpu_dirty = false; } static struct nvmm_assist_callbacks nvmm_callbacks = { @@ -727,9 +726,9 @@ nvmm_vcpu_loop(CPUState *cpu) * Inner VCPU loop. */ do { - if (cpu->accel->dirty) { + if (cpu->vcpu_dirty) { nvmm_set_registers(cpu); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } if (qcpu->stop) { @@ -827,32 +826,32 @@ static void do_nvmm_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { nvmm_get_registers(cpu); - cpu->accel->dirty = true; + cpu->vcpu_dirty = true; } static void do_nvmm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { nvmm_set_registers(cpu); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } static void do_nvmm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { nvmm_set_registers(cpu); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } static void do_nvmm_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) { - cpu->accel->dirty = true; + cpu->vcpu_dirty = true; } void nvmm_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->accel->dirty) { + if (!cpu->vcpu_dirty) { run_on_cpu(cpu, do_nvmm_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -982,7 +981,7 @@ nvmm_init_vcpu(CPUState *cpu) } } - qcpu->dirty = true; + qcpu->vcpu_dirty = true; cpu->accel = qcpu; return 0; From 29741712054aa8bd333d54dd88121ea3609ab386 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 06:59:29 +0200 Subject: [PATCH 1843/2760] accel/whpx: Replace @dirty field by generic CPUState::vcpu_dirty field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need for accel-specific @dirty field when we have a generic one in CPUState. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-21-philmd@linaro.org> --- target/i386/whpx/whpx-all.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index cf6d3e4cdd..5001afad3a 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -237,7 +237,6 @@ struct AccelCPUState { uint64_t tpr; uint64_t apic_base; bool interruption_pending; - bool dirty; /* Must be the last field as it may have a tail */ WHV_RUN_VP_EXIT_CONTEXT exit_ctx; @@ -836,7 +835,7 @@ static HRESULT CALLBACK whpx_emu_setreg_callback( * The emulator just successfully wrote the register state. We clear the * dirty state so we avoid the double write on resume of the VP. */ - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; return hr; } @@ -1391,7 +1390,7 @@ static int whpx_last_vcpu_stopping(CPUState *cpu) /* Returns the address of the next instruction that is about to be executed. */ static vaddr whpx_vcpu_get_pc(CPUState *cpu, bool exit_context_valid) { - if (cpu->accel->dirty) { + if (cpu->vcpu_dirty) { /* The CPU registers have been modified by other parts of QEMU. */ return cpu_env(cpu)->eip; } else if (exit_context_valid) { @@ -1704,9 +1703,9 @@ static int whpx_vcpu_run(CPUState *cpu) } do { - if (cpu->accel->dirty) { + if (cpu->vcpu_dirty) { whpx_set_registers(cpu, WHPX_SET_RUNTIME_STATE); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } if (exclusive_step_mode == WHPX_STEP_NONE) { @@ -2054,9 +2053,9 @@ static int whpx_vcpu_run(CPUState *cpu) static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { - if (!cpu->accel->dirty) { + if (!cpu->vcpu_dirty) { whpx_get_registers(cpu); - cpu->accel->dirty = true; + cpu->vcpu_dirty = true; } } @@ -2064,20 +2063,20 @@ static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { whpx_set_registers(cpu, WHPX_SET_RESET_STATE); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } static void do_whpx_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { whpx_set_registers(cpu, WHPX_SET_FULL_STATE); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) { - cpu->accel->dirty = true; + cpu->vcpu_dirty = true; } /* @@ -2086,7 +2085,7 @@ static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu, void whpx_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->accel->dirty) { + if (!cpu->vcpu_dirty) { run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -2226,7 +2225,7 @@ int whpx_init_vcpu(CPUState *cpu) } vcpu->interruptable = true; - vcpu->dirty = true; + cpu->vcpu_dirty = true; cpu->accel = vcpu; max_vcpu_index = max(max_vcpu_index, cpu->cpu_index); qemu_add_vm_change_state_handler(whpx_cpu_update_state, env); From 476e7379652d357f1053d340968e7f8d5c5b5281 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Jun 2025 14:13:20 +0200 Subject: [PATCH 1844/2760] accel/kvm: Remove kvm_cpu_synchronize_state() stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 57038a92bb0 ("cpus: extract out kvm-specific code to accel/kvm") the kvm_cpu_synchronize_state() stub is not necessary. Fixes: e0715f6abce ("kvm: remove kvm specific functions from global includes") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-22-philmd@linaro.org> --- accel/stubs/kvm-stub.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index b9b4427c91..68cd33ba97 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -29,10 +29,6 @@ void kvm_flush_coalesced_mmio_buffer(void) { } -void kvm_cpu_synchronize_state(CPUState *cpu) -{ -} - bool kvm_has_sync_mmu(void) { return false; From 1f8b0b64736e978120753e63c51921573962a54c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Jun 2025 16:09:08 +0200 Subject: [PATCH 1845/2760] accel/system: Document cpu_synchronize_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-23-philmd@linaro.org> --- include/system/accel-ops.h | 8 ++++++++ include/system/hw_accel.h | 11 ++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/system/accel-ops.h b/include/system/accel-ops.h index 4c99d25aef..55f91cea25 100644 --- a/include/system/accel-ops.h +++ b/include/system/accel-ops.h @@ -42,6 +42,14 @@ struct AccelOpsClass { void (*synchronize_post_reset)(CPUState *cpu); void (*synchronize_post_init)(CPUState *cpu); + /** + * synchronize_state: + * synchronize_pre_loadvm: + * @cpu: The vCPU to synchronize. + * + * Request to synchronize QEMU vCPU registers from the hardware accelerator + * (the hardware accelerator is the reference). + */ void (*synchronize_state)(CPUState *cpu); void (*synchronize_pre_loadvm)(CPUState *cpu); void (*synchronize_pre_resume)(bool step_pending); diff --git a/include/system/hw_accel.h b/include/system/hw_accel.h index 380e9e640b..574c973840 100644 --- a/include/system/hw_accel.h +++ b/include/system/hw_accel.h @@ -17,9 +17,18 @@ #include "system/whpx.h" #include "system/nvmm.h" +/** + * cpu_synchronize_state: + * cpu_synchronize_pre_loadvm: + * @cpu: The vCPU to synchronize. + * + * Request to synchronize QEMU vCPU registers from the hardware accelerator + * (the hardware accelerator is the reference). + */ void cpu_synchronize_state(CPUState *cpu); +void cpu_synchronize_pre_loadvm(CPUState *cpu); + void cpu_synchronize_post_reset(CPUState *cpu); void cpu_synchronize_post_init(CPUState *cpu); -void cpu_synchronize_pre_loadvm(CPUState *cpu); #endif /* QEMU_HW_ACCEL_H */ From 60c9cec12c99e47fb2c82ad5dca36c9441b623dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Jun 2025 16:09:23 +0200 Subject: [PATCH 1846/2760] accel/system: Document cpu_synchronize_state_post_init/reset() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-24-philmd@linaro.org> --- include/system/accel-ops.h | 8 ++++++++ include/system/hw_accel.h | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/include/system/accel-ops.h b/include/system/accel-ops.h index 55f91cea25..a4e706b49c 100644 --- a/include/system/accel-ops.h +++ b/include/system/accel-ops.h @@ -40,6 +40,14 @@ struct AccelOpsClass { void (*kick_vcpu_thread)(CPUState *cpu); bool (*cpu_thread_is_idle)(CPUState *cpu); + /** + * synchronize_post_reset: + * synchronize_post_init: + * @cpu: The vCPU to synchronize. + * + * Request to synchronize QEMU vCPU registers to the hardware accelerator + * (QEMU is the reference). + */ void (*synchronize_post_reset)(CPUState *cpu); void (*synchronize_post_init)(CPUState *cpu); /** diff --git a/include/system/hw_accel.h b/include/system/hw_accel.h index 574c973840..fa9228d5d2 100644 --- a/include/system/hw_accel.h +++ b/include/system/hw_accel.h @@ -28,6 +28,14 @@ void cpu_synchronize_state(CPUState *cpu); void cpu_synchronize_pre_loadvm(CPUState *cpu); +/** + * cpu_synchronize_post_reset: + * cpu_synchronize_post_init: + * @cpu: The vCPU to synchronize. + * + * Request to synchronize QEMU vCPU registers to the hardware accelerator + * (QEMU is the reference). + */ void cpu_synchronize_post_reset(CPUState *cpu); void cpu_synchronize_post_init(CPUState *cpu); From b6637bd5561d1d03f3a3d4335102cbf57fad5ad0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Jun 2025 10:39:09 +0200 Subject: [PATCH 1847/2760] accel/nvmm: Expose nvmm_enabled() to common code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently nvmm_enabled() is restricted to target-specific code. By defining CONFIG_NVMM_IS_POSSIBLE we allow its use anywhere. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-25-philmd@linaro.org> --- MAINTAINERS | 1 + accel/stubs/meson.build | 1 + accel/stubs/nvmm-stub.c | 12 ++++++++++++ include/system/nvmm.h | 23 ++++++++++++----------- target/i386/nvmm/nvmm-all.c | 8 +------- 5 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 accel/stubs/nvmm-stub.c diff --git a/MAINTAINERS b/MAINTAINERS index b1cbfe115b..c73468b242 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -586,6 +586,7 @@ NetBSD Virtual Machine Monitor (NVMM) CPU support M: Reinoud Zandijk S: Maintained F: include/system/nvmm.h +F: accel/stubs/nvmm-stub.c F: target/i386/nvmm/ Hosts diff --git a/accel/stubs/meson.build b/accel/stubs/meson.build index 8ca1a4529e..4c34287215 100644 --- a/accel/stubs/meson.build +++ b/accel/stubs/meson.build @@ -3,5 +3,6 @@ system_stubs_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c')) system_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) system_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c')) system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c')) +system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c')) specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: system_stubs_ss) diff --git a/accel/stubs/nvmm-stub.c b/accel/stubs/nvmm-stub.c new file mode 100644 index 0000000000..ec14837501 --- /dev/null +++ b/accel/stubs/nvmm-stub.c @@ -0,0 +1,12 @@ +/* + * NVMM stubs for QEMU + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/nvmm.h" + +bool nvmm_allowed; diff --git a/include/system/nvmm.h b/include/system/nvmm.h index 6971ddb3a5..7390def9ad 100644 --- a/include/system/nvmm.h +++ b/include/system/nvmm.h @@ -13,17 +13,18 @@ #define QEMU_NVMM_H #ifdef COMPILING_PER_TARGET - -#ifdef CONFIG_NVMM - -int nvmm_enabled(void); - -#else /* CONFIG_NVMM */ - -#define nvmm_enabled() (0) - -#endif /* CONFIG_NVMM */ - +# ifdef CONFIG_NVMM +# define CONFIG_NVMM_IS_POSSIBLE +# endif /* !CONFIG_NVMM */ +#else +# define CONFIG_NVMM_IS_POSSIBLE #endif /* COMPILING_PER_TARGET */ +#ifdef CONFIG_NVMM_IS_POSSIBLE +extern bool nvmm_allowed; +#define nvmm_enabled() (nvmm_allowed) +#else /* !CONFIG_NVMM_IS_POSSIBLE */ +#define nvmm_enabled() 0 +#endif /* !CONFIG_NVMM_IS_POSSIBLE */ + #endif /* QEMU_NVMM_H */ diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index aea61a6fd2..2df49d7eeb 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -46,7 +46,7 @@ struct qemu_machine { /* -------------------------------------------------------------------------- */ -static bool nvmm_allowed; +bool nvmm_allowed; static struct qemu_machine qemu_mach; static struct nvmm_machine * @@ -1192,12 +1192,6 @@ nvmm_accel_init(MachineState *ms) return 0; } -int -nvmm_enabled(void) -{ - return nvmm_allowed; -} - static void nvmm_accel_class_init(ObjectClass *oc, const void *data) { From d5a407a5763c4f5182124bb08c9157c0c667662f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Jun 2025 10:40:00 +0200 Subject: [PATCH 1848/2760] accel/whpx: Expose whpx_enabled() to common code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently whpx_enabled() is restricted to target-specific code. By defining CONFIG_WHPX_IS_POSSIBLE we allow its use anywhere. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250703173248.44995-26-philmd@linaro.org> --- MAINTAINERS | 1 + accel/stubs/meson.build | 1 + accel/stubs/whpx-stub.c | 12 ++++++++++++ include/system/whpx.h | 23 ++++++++++++----------- target/i386/whpx/whpx-all.c | 7 +------ 5 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 accel/stubs/whpx-stub.c diff --git a/MAINTAINERS b/MAINTAINERS index c73468b242..0fd8b2a4e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -540,6 +540,7 @@ WHPX CPUs M: Sunil Muthuswamy S: Supported F: target/i386/whpx/ +F: accel/stubs/whpx-stub.c F: include/system/whpx.h X86 Instruction Emulator diff --git a/accel/stubs/meson.build b/accel/stubs/meson.build index 4c34287215..9dfc4f9dda 100644 --- a/accel/stubs/meson.build +++ b/accel/stubs/meson.build @@ -4,5 +4,6 @@ system_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) system_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c')) system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c')) system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c')) +system_stubs_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c')) specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: system_stubs_ss) diff --git a/accel/stubs/whpx-stub.c b/accel/stubs/whpx-stub.c new file mode 100644 index 0000000000..c564c89fd0 --- /dev/null +++ b/accel/stubs/whpx-stub.c @@ -0,0 +1,12 @@ +/* + * WHPX stubs for QEMU + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/whpx.h" + +bool whpx_allowed; diff --git a/include/system/whpx.h b/include/system/whpx.h index 00ff409b68..00f6a3e523 100644 --- a/include/system/whpx.h +++ b/include/system/whpx.h @@ -16,19 +16,20 @@ #define QEMU_WHPX_H #ifdef COMPILING_PER_TARGET +# ifdef CONFIG_WHPX +# define CONFIG_WHPX_IS_POSSIBLE +# endif /* !CONFIG_WHPX */ +#else +# define CONFIG_WHPX_IS_POSSIBLE +#endif /* COMPILING_PER_TARGET */ -#ifdef CONFIG_WHPX - -int whpx_enabled(void); +#ifdef CONFIG_WHPX_IS_POSSIBLE +extern bool whpx_allowed; +#define whpx_enabled() (whpx_allowed) bool whpx_apic_in_platform(void); - -#else /* CONFIG_WHPX */ - -#define whpx_enabled() (0) +#else /* !CONFIG_WHPX_IS_POSSIBLE */ +#define whpx_enabled() 0 #define whpx_apic_in_platform() (0) - -#endif /* CONFIG_WHPX */ - -#endif /* COMPILING_PER_TARGET */ +#endif /* !CONFIG_WHPX_IS_POSSIBLE */ #endif /* QEMU_WHPX_H */ diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 5001afad3a..94fd5fc784 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -242,7 +242,7 @@ struct AccelCPUState { WHV_RUN_VP_EXIT_CONTEXT exit_ctx; }; -static bool whpx_allowed; +bool whpx_allowed; static bool whp_dispatch_initialized; static HMODULE hWinHvPlatform, hWinHvEmulation; static uint32_t max_vcpu_index; @@ -2688,11 +2688,6 @@ static int whpx_accel_init(MachineState *ms) return ret; } -int whpx_enabled(void) -{ - return whpx_allowed; -} - bool whpx_apic_in_platform(void) { return whpx_global.apic_in_platform; } From e8388158e62184cb567b7b97ab9e6738dec45348 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Jun 2025 16:20:10 +0200 Subject: [PATCH 1849/2760] accel/dummy: Extract 'dummy-cpus.h' header from 'system/cpus.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'dummy' helpers are specific to accelerator implementations, no need to expose them via "system/cpus.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Message-Id: <20250703173248.44995-27-philmd@linaro.org> --- MAINTAINERS | 1 + accel/dummy-cpus.c | 1 + accel/dummy-cpus.h | 14 ++++++++++++++ accel/qtest/qtest.c | 1 + accel/xen/xen-all.c | 1 + include/system/cpus.h | 5 ----- 6 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 accel/dummy-cpus.h diff --git a/MAINTAINERS b/MAINTAINERS index 0fd8b2a4e6..fca98e1219 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -503,6 +503,7 @@ F: include/system/accel-*.h F: include/system/cpus.h F: include/accel/accel-cpu*.h F: accel/accel-*.? +F: accel/dummy-cpus.? F: accel/Makefile.objs F: accel/stubs/Makefile.objs F: cpu-common.c diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c index 867276144f..03cfc0fa01 100644 --- a/accel/dummy-cpus.c +++ b/accel/dummy-cpus.c @@ -17,6 +17,7 @@ #include "qemu/guest-random.h" #include "qemu/main-loop.h" #include "hw/core/cpu.h" +#include "accel/dummy-cpus.h" static void *dummy_cpu_thread_fn(void *arg) { diff --git a/accel/dummy-cpus.h b/accel/dummy-cpus.h new file mode 100644 index 0000000000..d18dd0fdc5 --- /dev/null +++ b/accel/dummy-cpus.h @@ -0,0 +1,14 @@ +/* + * Dummy cpu thread code + * + * Copyright IBM, Corp. 2011 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ACCEL_DUMMY_CPUS_H +#define ACCEL_DUMMY_CPUS_H + +void dummy_start_vcpu_thread(CPUState *cpu); + +#endif diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index 92bed9264c..612cede160 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -24,6 +24,7 @@ #include "qemu/guest-random.h" #include "qemu/main-loop.h" #include "hw/core/cpu.h" +#include "accel/dummy-cpus.h" static int64_t qtest_clock_counter; diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index de52a8f882..c150dd43ca 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -18,6 +18,7 @@ #include "hw/xen/xen_igd.h" #include "chardev/char.h" #include "qemu/accel.h" +#include "accel/dummy-cpus.h" #include "system/accel-ops.h" #include "system/cpus.h" #include "system/xen.h" diff --git a/include/system/cpus.h b/include/system/cpus.h index 3226c765d0..69be6a77a7 100644 --- a/include/system/cpus.h +++ b/include/system/cpus.h @@ -7,11 +7,6 @@ void cpus_register_accel(const AccelOpsClass *i); /* return registers ops */ const AccelOpsClass *cpus_get_accel(void); -/* accel/dummy-cpus.c */ - -/* Create a dummy vcpu for AccelOpsClass->create_vcpu_thread */ -void dummy_start_vcpu_thread(CPUState *); - /* interface available for cpus accelerator threads */ /* For temporary buffers for forming a name */ From b9b8ce038497a18f4525e9b229f9090b1cec3b05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Jun 2025 14:45:19 +0200 Subject: [PATCH 1850/2760] accel: Expose and register generic_handle_interrupt() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to dispatch over AccelOpsClass::handle_interrupt(), we need it always defined, not calling a hidden handler under the hood. Make AccelOpsClass::handle_interrupt() mandatory. Expose generic_handle_interrupt() prototype and register it for each accelerator. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Zhao Liu Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Message-Id: <20250703173248.44995-29-philmd@linaro.org> --- accel/hvf/hvf-accel-ops.c | 1 + accel/kvm/kvm-accel-ops.c | 1 + accel/qtest/qtest.c | 1 + accel/xen/xen-all.c | 1 + include/system/accel-ops.h | 3 +++ system/cpus.c | 10 ++++------ target/i386/nvmm/nvmm-accel-ops.c | 1 + target/i386/whpx/whpx-accel-ops.c | 1 + 8 files changed, 13 insertions(+), 6 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 395b5a8e1c..be8724ac89 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -353,6 +353,7 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, const void *data) ops->create_vcpu_thread = hvf_start_vcpu_thread; ops->kick_vcpu_thread = hvf_kick_vcpu_thread; + ops->handle_interrupt = generic_handle_interrupt; ops->synchronize_post_reset = hvf_cpu_synchronize_post_reset; ops->synchronize_post_init = hvf_cpu_synchronize_post_init; diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index e5c15449aa..0eafc902c3 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -101,6 +101,7 @@ static void kvm_accel_ops_class_init(ObjectClass *oc, const void *data) ops->synchronize_post_init = kvm_cpu_synchronize_post_init; ops->synchronize_state = kvm_cpu_synchronize_state; ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm; + ops->handle_interrupt = generic_handle_interrupt; #ifdef TARGET_KVM_HAVE_GUEST_DEBUG ops->update_guest_debug = kvm_update_guest_debug_ops; diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index 612cede160..5474ce7313 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -67,6 +67,7 @@ static void qtest_accel_ops_class_init(ObjectClass *oc, const void *data) ops->create_vcpu_thread = dummy_start_vcpu_thread; ops->get_virtual_clock = qtest_get_virtual_clock; ops->set_virtual_clock = qtest_set_virtual_clock; + ops->handle_interrupt = generic_handle_interrupt; }; static const TypeInfo qtest_accel_ops_type = { diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index c150dd43ca..c12c22de78 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -153,6 +153,7 @@ static void xen_accel_ops_class_init(ObjectClass *oc, const void *data) AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); ops->create_vcpu_thread = dummy_start_vcpu_thread; + ops->handle_interrupt = generic_handle_interrupt; } static const TypeInfo xen_accel_ops_type = { diff --git a/include/system/accel-ops.h b/include/system/accel-ops.h index a4e706b49c..e775ecc348 100644 --- a/include/system/accel-ops.h +++ b/include/system/accel-ops.h @@ -62,6 +62,7 @@ struct AccelOpsClass { void (*synchronize_pre_loadvm)(CPUState *cpu); void (*synchronize_pre_resume)(bool step_pending); + /* handle_interrupt is mandatory. */ void (*handle_interrupt)(CPUState *cpu, int mask); /** @@ -86,4 +87,6 @@ struct AccelOpsClass { void (*remove_all_breakpoints)(CPUState *cpu); }; +void generic_handle_interrupt(CPUState *cpu, int mask); + #endif /* ACCEL_OPS_H */ diff --git a/system/cpus.c b/system/cpus.c index a43e0e4e79..0d0eec82a2 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -254,7 +254,7 @@ int64_t cpus_get_elapsed_ticks(void) return cpu_get_ticks(); } -static void generic_handle_interrupt(CPUState *cpu, int mask) +void generic_handle_interrupt(CPUState *cpu, int mask) { cpu->interrupt_request |= mask; @@ -267,11 +267,7 @@ void cpu_interrupt(CPUState *cpu, int mask) { g_assert(bql_locked()); - if (cpus_accel->handle_interrupt) { - cpus_accel->handle_interrupt(cpu, mask); - } else { - generic_handle_interrupt(cpu, mask); - } + cpus_accel->handle_interrupt(cpu, mask); } /* @@ -680,6 +676,8 @@ void cpus_register_accel(const AccelOpsClass *ops) { assert(ops != NULL); assert(ops->create_vcpu_thread != NULL); /* mandatory */ + assert(ops->handle_interrupt); + cpus_accel = ops; } diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index 21443078b7..a5517b0abf 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -87,6 +87,7 @@ static void nvmm_accel_ops_class_init(ObjectClass *oc, const void *data) ops->create_vcpu_thread = nvmm_start_vcpu_thread; ops->kick_vcpu_thread = nvmm_kick_vcpu_thread; + ops->handle_interrupt = generic_handle_interrupt; ops->synchronize_post_reset = nvmm_cpu_synchronize_post_reset; ops->synchronize_post_init = nvmm_cpu_synchronize_post_init; diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index b8bebe403c..31cf15f004 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -90,6 +90,7 @@ static void whpx_accel_ops_class_init(ObjectClass *oc, const void *data) ops->create_vcpu_thread = whpx_start_vcpu_thread; ops->kick_vcpu_thread = whpx_kick_vcpu_thread; ops->cpu_thread_is_idle = whpx_vcpu_thread_is_idle; + ops->handle_interrupt = generic_handle_interrupt; ops->synchronize_post_reset = whpx_cpu_synchronize_post_reset; ops->synchronize_post_init = whpx_cpu_synchronize_post_init; From 38623a9f63395cecdd4138a305e1187e10964990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jun 2025 12:07:47 +0200 Subject: [PATCH 1851/2760] accel: Keep reference to AccelOpsClass in AccelClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow dereferencing AccelOpsClass outside of accel/accel-system.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Message-Id: <20250703173248.44995-30-philmd@linaro.org> --- accel/accel-system.c | 3 ++- accel/tcg/tcg-accel-ops.c | 4 +++- include/qemu/accel.h | 3 +++ include/system/accel-ops.h | 3 ++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/accel/accel-system.c b/accel/accel-system.c index a0f562ae9f..64bc991b1c 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -85,8 +85,9 @@ void accel_init_ops_interfaces(AccelClass *ac) * non-NULL create_vcpu_thread operation. */ ops = ACCEL_OPS_CLASS(oc); + ac->ops = ops; if (ops->ops_init) { - ops->ops_init(ops); + ops->ops_init(ac); } cpus_register_accel(ops); } diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 6116644d1c..37b4b21f88 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -196,8 +196,10 @@ static inline void tcg_remove_all_breakpoints(CPUState *cpu) cpu_watchpoint_remove_all(cpu, BP_GDB); } -static void tcg_accel_ops_init(AccelOpsClass *ops) +static void tcg_accel_ops_init(AccelClass *ac) { + AccelOpsClass *ops = ac->ops; + if (qemu_tcg_mttcg_enabled()) { ops->create_vcpu_thread = mttcg_start_vcpu_thread; ops->kick_vcpu_thread = mttcg_kick_vcpu_thread; diff --git a/include/qemu/accel.h b/include/qemu/accel.h index fbd3d897fe..9dea314542 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -37,6 +37,9 @@ typedef struct AccelClass { /*< public >*/ const char *name; + /* Cached by accel_init_ops_interfaces() when created */ + AccelOpsClass *ops; + int (*init_machine)(MachineState *ms); bool (*cpu_common_realize)(CPUState *cpu, Error **errp); void (*cpu_common_unrealize)(CPUState *cpu); diff --git a/include/system/accel-ops.h b/include/system/accel-ops.h index e775ecc348..a786c7d478 100644 --- a/include/system/accel-ops.h +++ b/include/system/accel-ops.h @@ -10,6 +10,7 @@ #ifndef ACCEL_OPS_H #define ACCEL_OPS_H +#include "qemu/accel.h" #include "exec/vaddr.h" #include "qom/object.h" @@ -31,7 +32,7 @@ struct AccelOpsClass { /*< public >*/ /* initialization function called when accel is chosen */ - void (*ops_init)(AccelOpsClass *ops); + void (*ops_init)(AccelClass *ac); bool (*cpus_are_resettable)(void); void (*cpu_reset_hold)(CPUState *cpu); From 51e189619992a08d529bf580f6bcf074f5a7af0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jun 2025 12:24:41 +0200 Subject: [PATCH 1852/2760] accel: Propagate AccelState to AccelClass::init_machine() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to avoid init_machine() to call current_accel(), pass AccelState along. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Message-Id: <20250703173248.44995-31-philmd@linaro.org> --- accel/accel-system.c | 2 +- accel/hvf/hvf-all.c | 2 +- accel/kvm/kvm-all.c | 2 +- accel/qtest/qtest.c | 2 +- accel/tcg/tcg-all.c | 2 +- accel/xen/xen-all.c | 2 +- bsd-user/main.c | 2 +- include/qemu/accel.h | 2 +- linux-user/main.c | 2 +- target/i386/nvmm/nvmm-all.c | 2 +- target/i386/whpx/whpx-all.c | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/accel/accel-system.c b/accel/accel-system.c index 64bc991b1c..913b7155d7 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -37,7 +37,7 @@ int accel_init_machine(AccelState *accel, MachineState *ms) int ret; ms->accelerator = accel; *(acc->allowed) = true; - ret = acc->init_machine(ms); + ret = acc->init_machine(accel, ms); if (ret < 0) { ms->accelerator = NULL; *(acc->allowed) = false; diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index ddc77e629f..68f1425fae 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -247,7 +247,7 @@ static MemoryListener hvf_memory_listener = { .log_sync = hvf_log_sync, }; -static int hvf_accel_init(MachineState *ms) +static int hvf_accel_init(AccelState *as, MachineState *ms) { int x; hv_return_t ret; diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 17235f2646..264f288dc6 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2573,7 +2573,7 @@ static int kvm_setup_dirty_ring(KVMState *s) return 0; } -static int kvm_init(MachineState *ms) +static int kvm_init(AccelState *as, MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); static const char upgrade_note[] = diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index 5474ce7313..2b83126020 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -38,7 +38,7 @@ static void qtest_set_virtual_clock(int64_t count) qatomic_set_i64(&qtest_clock_counter, count); } -static int qtest_init_accel(MachineState *ms) +static int qtest_init_accel(AccelState *as, MachineState *ms) { return 0; } diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 6e5dc333d5..d68fbb2377 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -80,7 +80,7 @@ static void tcg_accel_instance_init(Object *obj) bool one_insn_per_tb; -static int tcg_init_machine(MachineState *ms) +static int tcg_init_machine(AccelState *as, MachineState *ms) { TCGState *s = TCG_STATE(current_accel()); unsigned max_threads = 1; diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index c12c22de78..8279746f11 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -77,7 +77,7 @@ static void xen_setup_post(MachineState *ms, AccelState *accel) } } -static int xen_init(MachineState *ms) +static int xen_init(AccelState *as, MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); diff --git a/bsd-user/main.c b/bsd-user/main.c index 7c0a059c3b..d0cc8e0088 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -474,7 +474,7 @@ int main(int argc, char **argv) opt_one_insn_per_tb, &error_abort); object_property_set_int(OBJECT(accel), "tb-size", opt_tb_size, &error_abort); - ac->init_machine(NULL); + ac->init_machine(accel, NULL); } /* diff --git a/include/qemu/accel.h b/include/qemu/accel.h index 9dea314542..b9a9b3593d 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -40,7 +40,7 @@ typedef struct AccelClass { /* Cached by accel_init_ops_interfaces() when created */ AccelOpsClass *ops; - int (*init_machine)(MachineState *ms); + int (*init_machine)(AccelState *as, MachineState *ms); bool (*cpu_common_realize)(CPUState *cpu, Error **errp); void (*cpu_common_unrealize)(CPUState *cpu); diff --git a/linux-user/main.c b/linux-user/main.c index 5ac5b55dc6..a9142ee726 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -820,7 +820,7 @@ int main(int argc, char **argv, char **envp) opt_one_insn_per_tb, &error_abort); object_property_set_int(OBJECT(accel), "tb-size", opt_tb_size, &error_abort); - ac->init_machine(NULL); + ac->init_machine(accel, NULL); } /* diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 2df49d7eeb..b4a4d50e86 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -1152,7 +1152,7 @@ static struct RAMBlockNotifier nvmm_ram_notifier = { /* -------------------------------------------------------------------------- */ static int -nvmm_accel_init(MachineState *ms) +nvmm_accel_init(AccelState *as, MachineState *ms) { int ret, err; diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 94fd5fc784..721c4782b9 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -2504,7 +2504,7 @@ static void whpx_set_kernel_irqchip(Object *obj, Visitor *v, * Partition support */ -static int whpx_accel_init(MachineState *ms) +static int whpx_accel_init(AccelState *as, MachineState *ms) { struct whpx_state *whpx; int ret; From 0fdcfc3baf9af2f2e1903eebf23edc9dd8b0aec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jun 2025 12:29:17 +0200 Subject: [PATCH 1853/2760] accel/tcg: Prefer local AccelState over global current_accel() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250703173248.44995-33-philmd@linaro.org> --- accel/tcg/tcg-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index d68fbb2377..c674d5bcf7 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -82,7 +82,7 @@ bool one_insn_per_tb; static int tcg_init_machine(AccelState *as, MachineState *ms) { - TCGState *s = TCG_STATE(current_accel()); + TCGState *s = TCG_STATE(as); unsigned max_threads = 1; #ifndef CONFIG_USER_ONLY From f0db25adcfa930a97b5192a6f59fd08c369c32e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jun 2025 12:26:18 +0200 Subject: [PATCH 1854/2760] accel/kvm: Prefer local AccelState over global MachineState::accel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250703173248.44995-32-philmd@linaro.org> --- accel/kvm/kvm-all.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 264f288dc6..72fba12d9f 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2588,15 +2588,13 @@ static int kvm_init(AccelState *as, MachineState *ms) { /* end of list */ } }, *nc = num_cpus; int soft_vcpus_limit, hard_vcpus_limit; - KVMState *s; + KVMState *s = KVM_STATE(as); const KVMCapabilityInfo *missing_cap; int ret; int type; qemu_mutex_init(&kml_slots_lock); - s = KVM_STATE(ms->accelerator); - /* * On systems where the kernel can support different base page * sizes, host page size may be different from TARGET_PAGE_SIZE, From 583d1c8f1652777e7d60f09eb58c1f6378d897f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Jun 2025 15:30:24 +0200 Subject: [PATCH 1855/2760] accel/kvm: Directly pass KVMState argument to do_kvm_create_vm() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250703173248.44995-35-philmd@linaro.org> --- accel/kvm/kvm-all.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 72fba12d9f..007f82a50d 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2470,13 +2470,10 @@ uint32_t kvm_dirty_ring_size(void) return kvm_state->kvm_dirty_ring_size; } -static int do_kvm_create_vm(MachineState *ms, int type) +static int do_kvm_create_vm(KVMState *s, int type) { - KVMState *s; int ret; - s = KVM_STATE(ms->accelerator); - do { ret = kvm_ioctl(s, KVM_CREATE_VM, type); } while (ret == -EINTR); @@ -2646,7 +2643,7 @@ static int kvm_init(AccelState *as, MachineState *ms) goto err; } - ret = do_kvm_create_vm(ms, type); + ret = do_kvm_create_vm(s, type); if (ret < 0) { goto err; } From 8dd5e6befc86161598eace2bfeb090c2cfb179fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Jun 2025 15:28:08 +0200 Subject: [PATCH 1856/2760] accel: Directly pass AccelState argument to AccelClass::has_memory() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250703173248.44995-34-philmd@linaro.org> --- accel/kvm/kvm-all.c | 4 ++-- include/qemu/accel.h | 2 +- system/memory.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 007f82a50d..6f6f9ef69b 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3786,10 +3786,10 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target) return r; } -static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as, +static bool kvm_accel_has_memory(AccelState *accel, AddressSpace *as, hwaddr start_addr, hwaddr size) { - KVMState *kvm = KVM_STATE(ms->accelerator); + KVMState *kvm = KVM_STATE(accel); int i; for (i = 0; i < kvm->nr_as; ++i) { diff --git a/include/qemu/accel.h b/include/qemu/accel.h index b9a9b3593d..f327a71282 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -46,7 +46,7 @@ typedef struct AccelClass { /* system related hooks */ void (*setup_post)(MachineState *ms, AccelState *accel); - bool (*has_memory)(MachineState *ms, AddressSpace *as, + bool (*has_memory)(AccelState *accel, AddressSpace *as, hwaddr start_addr, hwaddr size); /* gdbstub related hooks */ diff --git a/system/memory.c b/system/memory.c index 76b44b8220..e8d9b15b28 100644 --- a/system/memory.c +++ b/system/memory.c @@ -3501,7 +3501,7 @@ static void mtree_print_flatview(gpointer key, gpointer value, if (fvi->ac) { for (i = 0; i < fv_address_spaces->len; ++i) { as = g_array_index(fv_address_spaces, AddressSpace*, i); - if (fvi->ac->has_memory(current_machine, as, + if (fvi->ac->has_memory(current_machine->accelerator, as, int128_get64(range->addr.start), MR_SIZE(range->addr.size) + 1)) { qemu_printf(" %s", fvi->ac->name); From 1e9fb43d30c0526e164bebaab387b615edfa79ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Jun 2025 15:33:25 +0200 Subject: [PATCH 1857/2760] accel: Remove unused MachineState argument of AccelClass::setup_post() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This method only accesses xen_domid/xen_domid_restrict, which are both related to the 'accelerator', not the machine. Besides, xen_domid aims to be in Xen AccelState and xen_domid_restrict a xen_domid_restrict QOM property. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250703173248.44995-36-philmd@linaro.org> --- accel/accel-system.c | 2 +- accel/xen/xen-all.c | 2 +- include/qemu/accel.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/accel/accel-system.c b/accel/accel-system.c index 913b7155d7..af713cc902 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -58,7 +58,7 @@ void accel_setup_post(MachineState *ms) AccelState *accel = ms->accelerator; AccelClass *acc = ACCEL_GET_CLASS(accel); if (acc->setup_post) { - acc->setup_post(ms, accel); + acc->setup_post(accel); } } diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index 8279746f11..bd0ff64bef 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -64,7 +64,7 @@ static void xen_set_igd_gfx_passthru(Object *obj, bool value, Error **errp) xen_igd_gfx_pt_set(value, errp); } -static void xen_setup_post(MachineState *ms, AccelState *accel) +static void xen_setup_post(AccelState *as) { int rc; diff --git a/include/qemu/accel.h b/include/qemu/accel.h index f327a71282..a6a95ff0bc 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -45,7 +45,7 @@ typedef struct AccelClass { void (*cpu_common_unrealize)(CPUState *cpu); /* system related hooks */ - void (*setup_post)(MachineState *ms, AccelState *accel); + void (*setup_post)(AccelState *as); bool (*has_memory)(AccelState *accel, AddressSpace *as, hwaddr start_addr, hwaddr size); From 842e7eecd4446957c5edf0c65e5e41fadea2f015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Jun 2025 10:59:21 +0200 Subject: [PATCH 1858/2760] accel: Pass AccelState argument to gdbstub_supported_sstep_flags() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to have AccelClass methods instrospect their state, we need to pass AccelState by argument. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250703173248.44995-37-philmd@linaro.org> --- accel/accel-common.c | 2 +- accel/hvf/hvf-all.c | 2 +- accel/kvm/kvm-all.c | 2 +- accel/tcg/tcg-all.c | 2 +- include/qemu/accel.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/accel/accel-common.c b/accel/accel-common.c index 4894b98d64..591ff4cbb6 100644 --- a/accel/accel-common.c +++ b/accel/accel-common.c @@ -124,7 +124,7 @@ int accel_supported_gdbstub_sstep_flags(void) AccelState *accel = current_accel(); AccelClass *acc = ACCEL_GET_CLASS(accel); if (acc->gdbstub_supported_sstep_flags) { - return acc->gdbstub_supported_sstep_flags(); + return acc->gdbstub_supported_sstep_flags(accel); } return 0; } diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 68f1425fae..b6075c036e 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -281,7 +281,7 @@ static int hvf_accel_init(AccelState *as, MachineState *ms) return hvf_arch_init(); } -static int hvf_gdbstub_sstep_flags(void) +static int hvf_gdbstub_sstep_flags(AccelState *as) { return SSTEP_ENABLE | SSTEP_NOIRQ; } diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 6f6f9ef69b..45579f80fa 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3980,7 +3980,7 @@ static void kvm_accel_instance_init(Object *obj) * Returns: SSTEP_* flags that KVM supports for guest debug. The * support is probed during kvm_init() */ -static int kvm_gdbstub_sstep_flags(void) +static int kvm_gdbstub_sstep_flags(AccelState *as) { return kvm_sstep_flags; } diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index c674d5bcf7..5904582a68 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -219,7 +219,7 @@ static void tcg_set_one_insn_per_tb(Object *obj, bool value, Error **errp) qatomic_set(&one_insn_per_tb, value); } -static int tcg_gdbstub_supported_sstep_flags(void) +static int tcg_gdbstub_supported_sstep_flags(AccelState *as) { /* * In replay mode all events will come from the log and can't be diff --git a/include/qemu/accel.h b/include/qemu/accel.h index a6a95ff0bc..1c097ac4df 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -50,7 +50,7 @@ typedef struct AccelClass { hwaddr start_addr, hwaddr size); /* gdbstub related hooks */ - int (*gdbstub_supported_sstep_flags)(void); + int (*gdbstub_supported_sstep_flags)(AccelState *as); bool *allowed; /* From 0fd1d74080215571f2c8e2b85bfefb3c65238cfe Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 23 Jun 2025 11:53:06 -0300 Subject: [PATCH 1859/2760] monitor/hmp-cmds-target: add CPU_DUMP_VPU in hmp_info_registers() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b84694defb added the CPU_DUMP_VPU to allow vector registers to be logged by log_cpu_exec() in TCG. This flag was then used in commit b227f6a8a7 to print RISC-V vector registers using this flag. Note that this change was done in riscv_cpu_dump_state(), the cpu_dump_state() callback for RISC-V, the same callback used in hmp_info_registers(). Back then we forgot to change hmp_info_registers(), and 'info registers' isn't showing RISC-V vector registers as a result. No other target is impacted since only RISC-V is using CPU_DUMP_VPU. There's no reason to not show VPU regs in info_registers(), so add CPU_DUMP_VPU to hmp_info_registers(). This will print vector registers for all RISC-V machines and, as said above, has no impact in other archs. Cc: Dr. David Alan Gilbert Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250623145306.991562-1-dbarboza@ventanamicro.com> Signed-off-by: Philippe Mathieu-Daudé --- monitor/hmp-cmds-target.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monitor/hmp-cmds-target.c b/monitor/hmp-cmds-target.c index 8eaf70d9c9..e982061146 100644 --- a/monitor/hmp-cmds-target.c +++ b/monitor/hmp-cmds-target.c @@ -102,7 +102,7 @@ void hmp_info_registers(Monitor *mon, const QDict *qdict) if (all_cpus) { CPU_FOREACH(cs) { monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); - cpu_dump_state(cs, NULL, CPU_DUMP_FPU); + cpu_dump_state(cs, NULL, CPU_DUMP_FPU | CPU_DUMP_VPU); } } else { cs = vcpu >= 0 ? qemu_get_cpu(vcpu) : mon_get_cpu(mon); @@ -117,7 +117,7 @@ void hmp_info_registers(Monitor *mon, const QDict *qdict) } monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); - cpu_dump_state(cs, NULL, CPU_DUMP_FPU); + cpu_dump_state(cs, NULL, CPU_DUMP_FPU | CPU_DUMP_VPU); } } From c8beb901be15c57d166574ecf660261f0f23209f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Jul 2025 19:26:19 +0200 Subject: [PATCH 1860/2760] MAINTAINERS: Add me as reviewer of overall accelerators section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'd like to be informed of overall changes of accelerators. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250703173248.44995-40-philmd@linaro.org> --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index fca98e1219..bfd59f6412 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -495,6 +495,7 @@ Guest CPU Cores (other accelerators) Overall M: Richard Henderson R: Paolo Bonzini +R: Philippe Mathieu-Daudé S: Maintained F: include/exec/cpu*.h F: include/exec/target_long.h From 6eba6fe967c3a2606ef5ec5ba9a283b21d09f85b Mon Sep 17 00:00:00 2001 From: Jim Shu Date: Mon, 19 May 2025 22:35:15 +0800 Subject: [PATCH 1861/2760] target/riscv: Add the checking into stimecmp write function. Preparation commit to let aclint timer to use stimecmp write function. Aclint timer doesn't call sstc() predicate so we need to check inside the stimecmp write function. Signed-off-by: Jim Shu Acked-by: Alistair Francis Message-ID: <20250519143518.11086-2-jim.shu@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/time_helper.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c index bc0d9a0c4c..aebf0798d0 100644 --- a/target/riscv/time_helper.c +++ b/target/riscv/time_helper.c @@ -46,8 +46,23 @@ void riscv_timer_write_timecmp(CPURISCVState *env, QEMUTimer *timer, { uint64_t diff, ns_diff, next; RISCVAclintMTimerState *mtimer = env->rdtime_fn_arg; - uint32_t timebase_freq = mtimer->timebase_freq; - uint64_t rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta; + uint32_t timebase_freq; + uint64_t rtc_r; + + if (!riscv_cpu_cfg(env)->ext_sstc || !env->rdtime_fn || + !env->rdtime_fn_arg || !get_field(env->menvcfg, MENVCFG_STCE)) { + /* S/VS Timer IRQ depends on sstc extension, rdtime_fn(), and STCE. */ + return; + } + + if (timer_irq == MIP_VSTIP && + (!riscv_has_ext(env, RVH) || !get_field(env->henvcfg, HENVCFG_STCE))) { + /* VS Timer IRQ also depends on RVH and henvcfg.STCE. */ + return; + } + + timebase_freq = mtimer->timebase_freq; + rtc_r = env->rdtime_fn(env->rdtime_fn_arg) + delta; if (timecmp <= rtc_r) { /* From af27fc569af58e10d9e77afd10079809f05827bc Mon Sep 17 00:00:00 2001 From: Jim Shu Date: Mon, 19 May 2025 22:35:16 +0800 Subject: [PATCH 1862/2760] hw/intc: riscv_aclint: Fix mtime write for sstc extension When changing the mtime value, the period of [s|vs]timecmp timers should also be updated, similar to the period of mtimecmp timer. The period of the stimecmp timer is the time until the next S-mode timer IRQ. The value is calculated as "stimecmp - time". [1] It is equal to "stimecmp - mtime" since the time CSR is a read-only shadow of the memory-mapped mtime register. Thus, changing mtime value will update the period of stimecmp timer. Similarly, the period of vstimecmp timer is calculated as "vstimecmp - (mtime + htimedelta)" [2], so changing mtime value will update the period of vstimecmp timer. [1] RISC-V Priv spec ch 9.1.1. Supervisor Timer (stimecmp) Register A supervisor timer interrupt becomes pending, as reflected in the STIP bit in the mip and sip registers whenever time contains a value greater than or equal to stimecmp. [2] RISC-V Priv spec ch19.2.1. Virtual Supervisor Timer (vstimecmp) Register A virtual supervisor timer interrupt becomes pending, as reflected in the VSTIP bit in the hip register, whenever (time + htimedelta), truncated to 64 bits, contains a value greater than or equal to vstimecmp Signed-off-by: Jim Shu Acked-by: Alistair Francis Message-ID: <20250519143518.11086-3-jim.shu@sifive.com> Signed-off-by: Alistair Francis --- hw/intc/riscv_aclint.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/intc/riscv_aclint.c b/hw/intc/riscv_aclint.c index b0139f03f5..4623cfa029 100644 --- a/hw/intc/riscv_aclint.c +++ b/hw/intc/riscv_aclint.c @@ -28,6 +28,7 @@ #include "qemu/module.h" #include "hw/sysbus.h" #include "target/riscv/cpu.h" +#include "target/riscv/time_helper.h" #include "hw/qdev-properties.h" #include "hw/intc/riscv_aclint.h" #include "qemu/timer.h" @@ -240,6 +241,10 @@ static void riscv_aclint_mtimer_write(void *opaque, hwaddr addr, riscv_aclint_mtimer_write_timecmp(mtimer, RISCV_CPU(cpu), mtimer->hartid_base + i, mtimer->timecmp[i]); + riscv_timer_write_timecmp(env, env->stimer, env->stimecmp, 0, MIP_STIP); + riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp, + env->htimedelta, MIP_VSTIP); + } return; } From 3cb2edae740121cf5da3a9adb8190051e866eb01 Mon Sep 17 00:00:00 2001 From: Jim Shu Date: Mon, 19 May 2025 22:35:17 +0800 Subject: [PATCH 1863/2760] target/riscv: Fix VSTIP bit in sstc extension. VSTIP is only writable when both [mh]envcfg.STCE is enabled, or it will revert it's defined behavior as if sstc extension is not implemented. Signed-off-by: Jim Shu Acked-by: Alistair Francis Message-ID: <20250519143518.11086-4-jim.shu@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index fb14972169..d8102943dd 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -3651,7 +3651,14 @@ static RISCVException rmw_mip64(CPURISCVState *env, int csrno, if (riscv_cpu_cfg(env)->ext_sstc && (env->priv == PRV_M) && get_field(env->menvcfg, MENVCFG_STCE)) { /* sstc extension forbids STIP & VSTIP to be writeable in mip */ - mask = mask & ~(MIP_STIP | MIP_VSTIP); + + /* STIP is not writable when menvcfg.STCE is enabled. */ + mask = mask & ~MIP_STIP; + + /* VSTIP is not writable when both [mh]envcfg.STCE are enabled. */ + if (get_field(env->henvcfg, HENVCFG_STCE)) { + mask = mask & ~MIP_VSTIP; + } } if (mask) { From dff5f515409f1c9c10df00160524b21381cbef26 Mon Sep 17 00:00:00 2001 From: Jim Shu Date: Mon, 19 May 2025 22:35:18 +0800 Subject: [PATCH 1864/2760] target/riscv: Enable/Disable S/VS-mode Timer when STCE bit is changed Updating STCE will enable/disable SSTC in S-mode or/and VS-mode, so we also need to update S/VS-mode Timer and S/VSTIP bits in $mip CSR. Signed-off-by: Jim Shu Acked-by: Alistair Francis Message-ID: <20250519143518.11086-5-jim.shu@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 46 ++++++++++++++++++++++++++++++++++++++ target/riscv/time_helper.c | 46 ++++++++++++++++++++++++++++++++++++++ target/riscv/time_helper.h | 1 + 3 files changed, 93 insertions(+) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index d8102943dd..1151ebb6ad 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -3181,6 +3181,7 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); uint64_t mask = MENVCFG_FIOM | MENVCFG_CBIE | MENVCFG_CBCFE | MENVCFG_CBZE | MENVCFG_CDE; + bool stce_changed = false; if (riscv_cpu_mxl(env) == MXL_RV64) { mask |= (cfg->ext_svpbmt ? MENVCFG_PBMTE : 0) | @@ -3206,8 +3207,18 @@ static RISCVException write_menvcfg(CPURISCVState *env, int csrno, if ((val & MENVCFG_DTE) == 0) { env->mstatus &= ~MSTATUS_SDT; } + + if (cfg->ext_sstc && + ((env->menvcfg & MENVCFG_STCE) != (val & MENVCFG_STCE))) { + stce_changed = true; + } } env->menvcfg = (env->menvcfg & ~mask) | (val & mask); + + if (stce_changed) { + riscv_timer_stce_changed(env, true, !!(val & MENVCFG_STCE)); + } + return write_henvcfg(env, CSR_HENVCFG, env->henvcfg, ra); } @@ -3230,12 +3241,23 @@ static RISCVException write_menvcfgh(CPURISCVState *env, int csrno, (cfg->ext_smcdeleg ? MENVCFG_CDE : 0) | (cfg->ext_ssdbltrp ? MENVCFG_DTE : 0); uint64_t valh = (uint64_t)val << 32; + bool stce_changed = false; + + if (cfg->ext_sstc && + ((env->menvcfg & MENVCFG_STCE) != (valh & MENVCFG_STCE))) { + stce_changed = true; + } if ((valh & MENVCFG_DTE) == 0) { env->mstatus &= ~MSTATUS_SDT; } env->menvcfg = (env->menvcfg & ~mask) | (valh & mask); + + if (stce_changed) { + riscv_timer_stce_changed(env, true, !!(valh & MENVCFG_STCE)); + } + return write_henvcfgh(env, CSR_HENVCFGH, env->henvcfg >> 32, ra); } @@ -3313,8 +3335,10 @@ static RISCVException read_henvcfg(CPURISCVState *env, int csrno, static RISCVException write_henvcfg(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); uint64_t mask = HENVCFG_FIOM | HENVCFG_CBIE | HENVCFG_CBCFE | HENVCFG_CBZE; RISCVException ret; + bool stce_changed = false; ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG); if (ret != RISCV_EXCP_NONE) { @@ -3340,6 +3364,11 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, get_field(val, HENVCFG_PMM) != PMM_FIELD_RESERVED) { mask |= HENVCFG_PMM; } + + if (cfg->ext_sstc && + ((env->henvcfg & HENVCFG_STCE) != (val & HENVCFG_STCE))) { + stce_changed = true; + } } env->henvcfg = val & mask; @@ -3347,6 +3376,10 @@ static RISCVException write_henvcfg(CPURISCVState *env, int csrno, env->vsstatus &= ~MSTATUS_SDT; } + if (stce_changed) { + riscv_timer_stce_changed(env, false, !!(val & HENVCFG_STCE)); + } + return RISCV_EXCP_NONE; } @@ -3368,19 +3401,32 @@ static RISCVException read_henvcfgh(CPURISCVState *env, int csrno, static RISCVException write_henvcfgh(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { + const RISCVCPUConfig *cfg = riscv_cpu_cfg(env); uint64_t mask = env->menvcfg & (HENVCFG_PBMTE | HENVCFG_STCE | HENVCFG_ADUE | HENVCFG_DTE); uint64_t valh = (uint64_t)val << 32; RISCVException ret; + bool stce_changed = false; ret = smstateen_acc_ok(env, 0, SMSTATEEN0_HSENVCFG); if (ret != RISCV_EXCP_NONE) { return ret; } + + if (cfg->ext_sstc && + ((env->henvcfg & HENVCFG_STCE) != (valh & HENVCFG_STCE))) { + stce_changed = true; + } + env->henvcfg = (env->henvcfg & 0xFFFFFFFF) | (valh & mask); if ((env->henvcfg & HENVCFG_DTE) == 0) { env->vsstatus &= ~MSTATUS_SDT; } + + if (stce_changed) { + riscv_timer_stce_changed(env, false, !!(val & HENVCFG_STCE)); + } + return RISCV_EXCP_NONE; } diff --git a/target/riscv/time_helper.c b/target/riscv/time_helper.c index aebf0798d0..400e917354 100644 --- a/target/riscv/time_helper.c +++ b/target/riscv/time_helper.c @@ -140,6 +140,52 @@ void riscv_timer_write_timecmp(CPURISCVState *env, QEMUTimer *timer, timer_mod(timer, next); } +/* + * When disabling xenvcfg.STCE, the S/VS Timer may be disabled at the same time. + * It is safe to call this function regardless of whether the timer has been + * deleted or not. timer_del() will do nothing if the timer has already + * been deleted. + */ +static void riscv_timer_disable_timecmp(CPURISCVState *env, QEMUTimer *timer, + uint32_t timer_irq) +{ + /* Disable S-mode Timer IRQ and HW-based STIP */ + if ((timer_irq == MIP_STIP) && !get_field(env->menvcfg, MENVCFG_STCE)) { + riscv_cpu_update_mip(env, timer_irq, BOOL_TO_MASK(0)); + timer_del(timer); + return; + } + + /* Disable VS-mode Timer IRQ and HW-based VSTIP */ + if ((timer_irq == MIP_VSTIP) && + (!get_field(env->menvcfg, MENVCFG_STCE) || + !get_field(env->henvcfg, HENVCFG_STCE))) { + env->vstime_irq = 0; + riscv_cpu_update_mip(env, 0, BOOL_TO_MASK(0)); + timer_del(timer); + return; + } +} + +/* Enable or disable S/VS-mode Timer when xenvcfg.STCE is changed */ +void riscv_timer_stce_changed(CPURISCVState *env, bool is_m_mode, bool enable) +{ + if (enable) { + riscv_timer_write_timecmp(env, env->vstimer, env->vstimecmp, + env->htimedelta, MIP_VSTIP); + } else { + riscv_timer_disable_timecmp(env, env->vstimer, MIP_VSTIP); + } + + if (is_m_mode) { + if (enable) { + riscv_timer_write_timecmp(env, env->stimer, env->stimecmp, 0, MIP_STIP); + } else { + riscv_timer_disable_timecmp(env, env->stimer, MIP_STIP); + } + } +} + void riscv_timer_init(RISCVCPU *cpu) { CPURISCVState *env; diff --git a/target/riscv/time_helper.h b/target/riscv/time_helper.h index cacd79b80c..af1f634f89 100644 --- a/target/riscv/time_helper.h +++ b/target/riscv/time_helper.h @@ -25,6 +25,7 @@ void riscv_timer_write_timecmp(CPURISCVState *env, QEMUTimer *timer, uint64_t timecmp, uint64_t delta, uint32_t timer_irq); +void riscv_timer_stce_changed(CPURISCVState *env, bool is_m_mode, bool enable); void riscv_timer_init(RISCVCPU *cpu); #endif From 148499b343a9aac62c5f5d55617a1c317127e2e3 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 22 May 2025 08:33:44 -0300 Subject: [PATCH 1865/2760] target/riscv/cpu.c: fix zama16b order in isa_edata_arr[] Put it after zalrsc and before zawrs. Cc: qemu-trivial@nongnu.org Fixes: a60ce58fd9 ("target/riscv: Support Zama16b extension") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250522113344.823294-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 629ac37501..fe21e0fb44 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -127,8 +127,8 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zaamo, PRIV_VERSION_1_12_0, ext_zaamo), ISA_EXT_DATA_ENTRY(zabha, PRIV_VERSION_1_13_0, ext_zabha), ISA_EXT_DATA_ENTRY(zacas, PRIV_VERSION_1_12_0, ext_zacas), - ISA_EXT_DATA_ENTRY(zama16b, PRIV_VERSION_1_13_0, ext_zama16b), ISA_EXT_DATA_ENTRY(zalrsc, PRIV_VERSION_1_12_0, ext_zalrsc), + ISA_EXT_DATA_ENTRY(zama16b, PRIV_VERSION_1_13_0, ext_zama16b), ISA_EXT_DATA_ENTRY(zawrs, PRIV_VERSION_1_12_0, ext_zawrs), ISA_EXT_DATA_ENTRY(zfa, PRIV_VERSION_1_12_0, ext_zfa), ISA_EXT_DATA_ENTRY(zfbfmin, PRIV_VERSION_1_12_0, ext_zfbfmin), From a429f9304d54e9a44773b753d0844204b4b3b22f Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 28 May 2025 15:44:05 -0300 Subject: [PATCH 1866/2760] target/riscv/tcg: restrict satp_mode changes in cpu_set_profile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We're changing 'mmu' to true regardless of whether the profile is being enabled or not, and at the same time we're changing satp_mode to profile->enabled. This will promote a situation where we'll set mmu=on without a virtual memory mode, which is a mistake. Only touch 'mmu' and satp_mode if the profile is being enabled. Suggested-by: Andrew Jones Fixes: 55398025e7 ("target/riscv: add satp_mode profile support") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Björn Töpel Tested-by: Björn Töpel Message-ID: <20250528184407.1451983-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 55fd9e5584..39de32cf76 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1365,16 +1365,16 @@ static void cpu_set_profile(Object *obj, Visitor *v, const char *name, if (profile->enabled) { cpu->env.priv_ver = profile->priv_spec; - } #ifndef CONFIG_USER_ONLY - if (profile->satp_mode != RISCV_PROFILE_ATTR_UNUSED) { - object_property_set_bool(obj, "mmu", true, NULL); - const char *satp_prop = satp_mode_str(profile->satp_mode, - riscv_cpu_is_32bit(cpu)); - object_property_set_bool(obj, satp_prop, profile->enabled, NULL); - } + if (profile->satp_mode != RISCV_PROFILE_ATTR_UNUSED) { + object_property_set_bool(obj, "mmu", true, NULL); + const char *satp_prop = satp_mode_str(profile->satp_mode, + riscv_cpu_is_32bit(cpu)); + object_property_set_bool(obj, satp_prop, true, NULL); + } #endif + } for (i = 0; misa_bits[i] != 0; i++) { uint32_t bit = misa_bits[i]; From f655704c3dfefd57172e3347c22aca18283ee2b7 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 28 May 2025 15:44:06 -0300 Subject: [PATCH 1867/2760] target/riscv/tcg: decouple profile enablement from user prop MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We have code in riscv_cpu_add_profiles() to enable a profile right away in case a CPU chose the profile during its cpu_init(). But we're using the user callback option to do so, setting profile->user_set. Create a new helper that does all the grunt work to enable/disable a given profile. Use this new helper in the cases where we want a CPU to be compatible to a certain profile, leaving the user callback to be used exclusively by users. Fixes: fba92a92e3 ("target/riscv: add 'rva22u64' CPU") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Björn Töpel Tested-by: Björn Töpel Message-ID: <20250528184407.1451983-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/tcg/tcg-cpu.c | 127 +++++++++++++++++++------------------ 1 file changed, 67 insertions(+), 60 deletions(-) diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 39de32cf76..e10e03a577 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1166,6 +1166,70 @@ static bool riscv_cpu_is_generic(Object *cpu_obj) return object_dynamic_cast(cpu_obj, TYPE_RISCV_DYNAMIC_CPU) != NULL; } +static void riscv_cpu_set_profile(RISCVCPU *cpu, + RISCVCPUProfile *profile, + bool enabled) +{ + int i, ext_offset; + + if (profile->u_parent != NULL) { + riscv_cpu_set_profile(cpu, profile->u_parent, enabled); + } + + if (profile->s_parent != NULL) { + riscv_cpu_set_profile(cpu, profile->s_parent, enabled); + } + + profile->enabled = enabled; + + if (profile->enabled) { + cpu->env.priv_ver = profile->priv_spec; + +#ifndef CONFIG_USER_ONLY + if (profile->satp_mode != RISCV_PROFILE_ATTR_UNUSED) { + object_property_set_bool(OBJECT(cpu), "mmu", true, NULL); + const char *satp_prop = satp_mode_str(profile->satp_mode, + riscv_cpu_is_32bit(cpu)); + object_property_set_bool(OBJECT(cpu), satp_prop, true, NULL); + } +#endif + } + + for (i = 0; misa_bits[i] != 0; i++) { + uint32_t bit = misa_bits[i]; + + if (!(profile->misa_ext & bit)) { + continue; + } + + if (bit == RVI && !profile->enabled) { + /* + * Disabling profiles will not disable the base + * ISA RV64I. + */ + continue; + } + + cpu_misa_ext_add_user_opt(bit, profile->enabled); + riscv_cpu_write_misa_bit(cpu, bit, profile->enabled); + } + + for (i = 0; profile->ext_offsets[i] != RISCV_PROFILE_EXT_LIST_END; i++) { + ext_offset = profile->ext_offsets[i]; + + if (profile->enabled) { + if (cpu_cfg_offset_is_named_feat(ext_offset)) { + riscv_cpu_enable_named_feat(cpu, ext_offset); + } + + cpu_bump_multi_ext_priv_ver(&cpu->env, ext_offset); + } + + cpu_cfg_ext_add_user_opt(ext_offset, profile->enabled); + isa_ext_update_enabled(cpu, ext_offset, profile->enabled); + } +} + /* * We'll get here via the following path: * @@ -1332,7 +1396,6 @@ static void cpu_set_profile(Object *obj, Visitor *v, const char *name, RISCVCPUProfile *profile = opaque; RISCVCPU *cpu = RISCV_CPU(obj); bool value; - int i, ext_offset; if (riscv_cpu_is_vendor(obj)) { error_setg(errp, "Profile %s is not available for vendor CPUs", @@ -1351,64 +1414,8 @@ static void cpu_set_profile(Object *obj, Visitor *v, const char *name, } profile->user_set = true; - profile->enabled = value; - - if (profile->u_parent != NULL) { - object_property_set_bool(obj, profile->u_parent->name, - profile->enabled, NULL); - } - - if (profile->s_parent != NULL) { - object_property_set_bool(obj, profile->s_parent->name, - profile->enabled, NULL); - } - - if (profile->enabled) { - cpu->env.priv_ver = profile->priv_spec; - -#ifndef CONFIG_USER_ONLY - if (profile->satp_mode != RISCV_PROFILE_ATTR_UNUSED) { - object_property_set_bool(obj, "mmu", true, NULL); - const char *satp_prop = satp_mode_str(profile->satp_mode, - riscv_cpu_is_32bit(cpu)); - object_property_set_bool(obj, satp_prop, true, NULL); - } -#endif - } - - for (i = 0; misa_bits[i] != 0; i++) { - uint32_t bit = misa_bits[i]; - - if (!(profile->misa_ext & bit)) { - continue; - } - if (bit == RVI && !profile->enabled) { - /* - * Disabling profiles will not disable the base - * ISA RV64I. - */ - continue; - } - - cpu_misa_ext_add_user_opt(bit, profile->enabled); - riscv_cpu_write_misa_bit(cpu, bit, profile->enabled); - } - - for (i = 0; profile->ext_offsets[i] != RISCV_PROFILE_EXT_LIST_END; i++) { - ext_offset = profile->ext_offsets[i]; - - if (profile->enabled) { - if (cpu_cfg_offset_is_named_feat(ext_offset)) { - riscv_cpu_enable_named_feat(cpu, ext_offset); - } - - cpu_bump_multi_ext_priv_ver(&cpu->env, ext_offset); - } - - cpu_cfg_ext_add_user_opt(ext_offset, profile->enabled); - isa_ext_update_enabled(cpu, ext_offset, profile->enabled); - } + riscv_cpu_set_profile(cpu, profile, value); } static void cpu_get_profile(Object *obj, Visitor *v, const char *name, @@ -1423,7 +1430,7 @@ static void cpu_get_profile(Object *obj, Visitor *v, const char *name, static void riscv_cpu_add_profiles(Object *cpu_obj) { for (int i = 0; riscv_profiles[i] != NULL; i++) { - const RISCVCPUProfile *profile = riscv_profiles[i]; + RISCVCPUProfile *profile = riscv_profiles[i]; object_property_add(cpu_obj, profile->name, "bool", cpu_get_profile, cpu_set_profile, @@ -1435,7 +1442,7 @@ static void riscv_cpu_add_profiles(Object *cpu_obj) * case. */ if (profile->enabled) { - object_property_set_bool(cpu_obj, profile->name, true, NULL); + riscv_cpu_set_profile(RISCV_CPU(cpu_obj), profile, true); } } } From cab6b5d8c351863b21d05eb8c9bb7920ad883b05 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 28 May 2025 15:44:07 -0300 Subject: [PATCH 1868/2760] target/riscv: add profile->present flag MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Björn reported in [1] a case where a rv64 CPU is going through the profile code path to enable satp mode. In this case,the amount of extensions on top of the rv64 CPU made it compliant with the RVA22S64 profile during the validation of CPU 0. When the subsequent CPUs were initialized the static profile object has the 'enable' flag set, enabling the profile code path for those CPUs. This happens because we are initializing and realizing each CPU before going to the next, i.e. init and realize CPU0, then init and realize CPU1 and so on. If we change any persistent state during the validation of CPU N it will interfere with the init/realization of CPU N+1. We're using the 'enabled' profile flag to do two distinct things: inform cpu_init() that we want profile extensions to be enabled, and telling QMP that a profile is currently enabled in the CPU. We want to be flexible enough to recognize profile support for all CPUs that has the extension prerequisites, but we do not want to force the profile code path if a profile wasn't set too. Add a new 'present' flag for profiles that will coexist with the 'enabled' flag. Enabling a profile means "we want to switch on all its mandatory extensions". A profile is 'present' if we asserted during validation that the CPU has the needed prerequisites. This means that the case reported by Björn now results in RVA22S64.enabled=false and RVA22S64.present=true. QMP will recognize it as a RVA22 compliant CPU and we won't force the CPU into the profile path. [1] https://lore.kernel.org/qemu-riscv/87y0usiz22.fsf@all.your.base.are.belong.to.us/ Reported-by: Björn Töpel Fixes: 2af005d610 ("target/riscv/tcg: validate profiles during finalize") Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Reviewed-by: Björn Töpel Tested-by: Björn Töpel Message-ID: <20250528184407.1451983-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.h | 15 +++++++++++++++ target/riscv/riscv-qmp-cmds.c | 2 +- target/riscv/tcg/tcg-cpu.c | 11 +++-------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 229ade9ed9..2a6793e022 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -82,7 +82,22 @@ typedef struct riscv_cpu_profile { struct riscv_cpu_profile *s_parent; const char *name; uint32_t misa_ext; + /* + * The profile is enabled/disabled via command line or + * via cpu_init(). Enabling a profile will add all its + * mandatory extensions in the CPU during init(). + */ bool enabled; + /* + * The profile is present in the CPU, i.e. the current set of + * CPU extensions complies with it. A profile can be enabled + * and not present (e.g. the user disabled a mandatory extension) + * and the other way around (e.g. all mandatory extensions are + * present in a non-profile CPU). + * + * QMP uses this flag. + */ + bool present; bool user_set; int priv_spec; int satp_mode; diff --git a/target/riscv/riscv-qmp-cmds.c b/target/riscv/riscv-qmp-cmds.c index 8ba8aa0d5f..8a1856c50e 100644 --- a/target/riscv/riscv-qmp-cmds.c +++ b/target/riscv/riscv-qmp-cmds.c @@ -121,7 +121,7 @@ static void riscv_obj_add_profiles_qdict(Object *obj, QDict *qdict_out) for (int i = 0; riscv_profiles[i] != NULL; i++) { profile = riscv_profiles[i]; - value = QOBJECT(qbool_from_bool(profile->enabled)); + value = QOBJECT(qbool_from_bool(profile->present)); qdict_put_obj(qdict_out, profile->name, value); } diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index e10e03a577..c5e260e360 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -867,16 +867,11 @@ static void riscv_cpu_check_parent_profile(RISCVCPU *cpu, RISCVCPUProfile *profile, RISCVCPUProfile *parent) { - const char *parent_name; - bool parent_enabled; - - if (!profile->enabled || !parent) { + if (!profile->present || !parent) { return; } - parent_name = parent->name; - parent_enabled = object_property_get_bool(OBJECT(cpu), parent_name, NULL); - profile->enabled = parent_enabled; + profile->present = parent->present; } static void riscv_cpu_validate_profile(RISCVCPU *cpu, @@ -937,7 +932,7 @@ static void riscv_cpu_validate_profile(RISCVCPU *cpu, } } - profile->enabled = profile_impl; + profile->present = profile_impl; riscv_cpu_check_parent_profile(cpu, profile, profile->u_parent); riscv_cpu_check_parent_profile(cpu, profile, profile->s_parent); From 444cffd37b69b06f170aeaedadf1b52be96d4acd Mon Sep 17 00:00:00 2001 From: Jay Chang Date: Thu, 22 May 2025 16:12:35 +0800 Subject: [PATCH 1869/2760] target/riscv: Extend PMP region up to 64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the RISC-V Privileged Specification (version >1.12), RV32 supports 16 CSRs (pmpcfg0–pmpcfg15) to configure 64 PMP regions (pmpaddr0–pmpaddr63). Signed-off-by: Jay Chang Reviewed-by: Frank Chang Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20250522081236.4050-2-jay.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu_bits.h | 60 +++++++++++++++++++ target/riscv/csr.c | 124 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 182 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index a30317c617..e6b3e28386 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -372,6 +372,18 @@ #define CSR_PMPCFG1 0x3a1 #define CSR_PMPCFG2 0x3a2 #define CSR_PMPCFG3 0x3a3 +#define CSR_PMPCFG4 0x3a4 +#define CSR_PMPCFG5 0x3a5 +#define CSR_PMPCFG6 0x3a6 +#define CSR_PMPCFG7 0x3a7 +#define CSR_PMPCFG8 0x3a8 +#define CSR_PMPCFG9 0x3a9 +#define CSR_PMPCFG10 0x3aa +#define CSR_PMPCFG11 0x3ab +#define CSR_PMPCFG12 0x3ac +#define CSR_PMPCFG13 0x3ad +#define CSR_PMPCFG14 0x3ae +#define CSR_PMPCFG15 0x3af #define CSR_PMPADDR0 0x3b0 #define CSR_PMPADDR1 0x3b1 #define CSR_PMPADDR2 0x3b2 @@ -388,6 +400,54 @@ #define CSR_PMPADDR13 0x3bd #define CSR_PMPADDR14 0x3be #define CSR_PMPADDR15 0x3bf +#define CSR_PMPADDR16 0x3c0 +#define CSR_PMPADDR17 0x3c1 +#define CSR_PMPADDR18 0x3c2 +#define CSR_PMPADDR19 0x3c3 +#define CSR_PMPADDR20 0x3c4 +#define CSR_PMPADDR21 0x3c5 +#define CSR_PMPADDR22 0x3c6 +#define CSR_PMPADDR23 0x3c7 +#define CSR_PMPADDR24 0x3c8 +#define CSR_PMPADDR25 0x3c9 +#define CSR_PMPADDR26 0x3ca +#define CSR_PMPADDR27 0x3cb +#define CSR_PMPADDR28 0x3cc +#define CSR_PMPADDR29 0x3cd +#define CSR_PMPADDR30 0x3ce +#define CSR_PMPADDR31 0x3cf +#define CSR_PMPADDR32 0x3d0 +#define CSR_PMPADDR33 0x3d1 +#define CSR_PMPADDR34 0x3d2 +#define CSR_PMPADDR35 0x3d3 +#define CSR_PMPADDR36 0x3d4 +#define CSR_PMPADDR37 0x3d5 +#define CSR_PMPADDR38 0x3d6 +#define CSR_PMPADDR39 0x3d7 +#define CSR_PMPADDR40 0x3d8 +#define CSR_PMPADDR41 0x3d9 +#define CSR_PMPADDR42 0x3da +#define CSR_PMPADDR43 0x3db +#define CSR_PMPADDR44 0x3dc +#define CSR_PMPADDR45 0x3dd +#define CSR_PMPADDR46 0x3de +#define CSR_PMPADDR47 0x3df +#define CSR_PMPADDR48 0x3e0 +#define CSR_PMPADDR49 0x3e1 +#define CSR_PMPADDR50 0x3e2 +#define CSR_PMPADDR51 0x3e3 +#define CSR_PMPADDR52 0x3e4 +#define CSR_PMPADDR53 0x3e5 +#define CSR_PMPADDR54 0x3e6 +#define CSR_PMPADDR55 0x3e7 +#define CSR_PMPADDR56 0x3e8 +#define CSR_PMPADDR57 0x3e9 +#define CSR_PMPADDR58 0x3ea +#define CSR_PMPADDR59 0x3eb +#define CSR_PMPADDR60 0x3ec +#define CSR_PMPADDR61 0x3ed +#define CSR_PMPADDR62 0x3ee +#define CSR_PMPADDR63 0x3ef /* RNMI */ #define CSR_MNSCRATCH 0x740 diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 1151ebb6ad..d6cd441133 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -6164,6 +6164,30 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_PMPCFG1] = { "pmpcfg1", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG2] = { "pmpcfg2", pmp, read_pmpcfg, write_pmpcfg }, [CSR_PMPCFG3] = { "pmpcfg3", pmp, read_pmpcfg, write_pmpcfg }, + [CSR_PMPCFG4] = { "pmpcfg4", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG5] = { "pmpcfg5", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG6] = { "pmpcfg6", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG7] = { "pmpcfg7", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG8] = { "pmpcfg8", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG9] = { "pmpcfg9", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG10] = { "pmpcfg10", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG11] = { "pmpcfg11", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG12] = { "pmpcfg12", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG13] = { "pmpcfg13", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG14] = { "pmpcfg14", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPCFG15] = { "pmpcfg15", pmp, read_pmpcfg, write_pmpcfg, + .min_priv_ver = PRIV_VERSION_1_12_0 }, [CSR_PMPADDR0] = { "pmpaddr0", pmp, read_pmpaddr, write_pmpaddr }, [CSR_PMPADDR1] = { "pmpaddr1", pmp, read_pmpaddr, write_pmpaddr }, [CSR_PMPADDR2] = { "pmpaddr2", pmp, read_pmpaddr, write_pmpaddr }, @@ -6178,8 +6202,104 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_PMPADDR11] = { "pmpaddr11", pmp, read_pmpaddr, write_pmpaddr }, [CSR_PMPADDR12] = { "pmpaddr12", pmp, read_pmpaddr, write_pmpaddr }, [CSR_PMPADDR13] = { "pmpaddr13", pmp, read_pmpaddr, write_pmpaddr }, - [CSR_PMPADDR14] = { "pmpaddr14", pmp, read_pmpaddr, write_pmpaddr }, - [CSR_PMPADDR15] = { "pmpaddr15", pmp, read_pmpaddr, write_pmpaddr }, + [CSR_PMPADDR14] = { "pmpaddr14", pmp, read_pmpaddr, write_pmpaddr }, + [CSR_PMPADDR15] = { "pmpaddr15", pmp, read_pmpaddr, write_pmpaddr }, + [CSR_PMPADDR16] = { "pmpaddr16", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR17] = { "pmpaddr17", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR18] = { "pmpaddr18", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR19] = { "pmpaddr19", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR20] = { "pmpaddr20", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR21] = { "pmpaddr21", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR22] = { "pmpaddr22", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR23] = { "pmpaddr23", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR24] = { "pmpaddr24", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR25] = { "pmpaddr25", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR26] = { "pmpaddr26", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR27] = { "pmpaddr27", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR28] = { "pmpaddr28", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR29] = { "pmpaddr29", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR30] = { "pmpaddr30", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR31] = { "pmpaddr31", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR32] = { "pmpaddr32", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR33] = { "pmpaddr33", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR34] = { "pmpaddr34", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR35] = { "pmpaddr35", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR36] = { "pmpaddr36", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR37] = { "pmpaddr37", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR38] = { "pmpaddr38", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR39] = { "pmpaddr39", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR40] = { "pmpaddr40", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR41] = { "pmpaddr41", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR42] = { "pmpaddr42", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR43] = { "pmpaddr43", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR44] = { "pmpaddr44", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR45] = { "pmpaddr45", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR46] = { "pmpaddr46", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR47] = { "pmpaddr47", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR48] = { "pmpaddr48", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR49] = { "pmpaddr49", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR50] = { "pmpaddr50", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR51] = { "pmpaddr51", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR52] = { "pmpaddr52", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR53] = { "pmpaddr53", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR54] = { "pmpaddr54", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR55] = { "pmpaddr55", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR56] = { "pmpaddr56", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR57] = { "pmpaddr57", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR58] = { "pmpaddr58", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR59] = { "pmpaddr59", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR60] = { "pmpaddr60", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR61] = { "pmpaddr61", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR62] = { "pmpaddr62", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, + [CSR_PMPADDR63] = { "pmpaddr63", pmp, read_pmpaddr, write_pmpaddr, + .min_priv_ver = PRIV_VERSION_1_12_0 }, /* Debug CSRs */ [CSR_TSELECT] = { "tselect", debug, read_tselect, write_tselect }, From 455c0fa9eef7f27e5b50ebd2b6fe3447c4f1ca51 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Fri, 30 May 2025 10:46:08 -0300 Subject: [PATCH 1870/2760] target/riscv: remove capital 'Z' CPU properties These properties were deprecated in QEMU 8.2, commit 8043effd9b. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250530134608.1806922-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 17 ----------------- target/riscv/cpu.h | 1 - target/riscv/tcg/tcg-cpu.c | 31 +------------------------------ 3 files changed, 1 insertion(+), 48 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index fe21e0fb44..7c6e0844d0 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1387,23 +1387,6 @@ const RISCVCPUMultiExtConfig riscv_cpu_named_features[] = { { }, }; -/* Deprecated entries marked for future removal */ -const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[] = { - MULTI_EXT_CFG_BOOL("Zifencei", ext_zifencei, true), - MULTI_EXT_CFG_BOOL("Zicsr", ext_zicsr, true), - MULTI_EXT_CFG_BOOL("Zihintntl", ext_zihintntl, true), - MULTI_EXT_CFG_BOOL("Zihintpause", ext_zihintpause, true), - MULTI_EXT_CFG_BOOL("Zawrs", ext_zawrs, true), - MULTI_EXT_CFG_BOOL("Zfa", ext_zfa, true), - MULTI_EXT_CFG_BOOL("Zfh", ext_zfh, false), - MULTI_EXT_CFG_BOOL("Zfhmin", ext_zfhmin, false), - MULTI_EXT_CFG_BOOL("Zve32f", ext_zve32f, false), - MULTI_EXT_CFG_BOOL("Zve64f", ext_zve64f, false), - MULTI_EXT_CFG_BOOL("Zve64d", ext_zve64d, false), - - { }, -}; - static void cpu_set_prop_err(RISCVCPU *cpu, const char *propname, Error **errp) { diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 2a6793e022..17bf4e7579 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -951,7 +951,6 @@ extern const RISCVCPUMultiExtConfig riscv_cpu_extensions[]; extern const RISCVCPUMultiExtConfig riscv_cpu_vendor_exts[]; extern const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[]; extern const RISCVCPUMultiExtConfig riscv_cpu_named_features[]; -extern const RISCVCPUMultiExtConfig riscv_cpu_deprecated_exts[]; typedef struct isa_ext_data { const char *name; diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index c5e260e360..81174de409 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -1442,25 +1442,6 @@ static void riscv_cpu_add_profiles(Object *cpu_obj) } } -static bool cpu_ext_is_deprecated(const char *ext_name) -{ - return isupper(ext_name[0]); -} - -/* - * String will be allocated in the heap. Caller is responsible - * for freeing it. - */ -static char *cpu_ext_to_lower(const char *ext_name) -{ - char *ret = g_malloc0(strlen(ext_name) + 1); - - strcpy(ret, ext_name); - ret[0] = tolower(ret[0]); - - return ret; -} - static void cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name, void *opaque, Error **errp) { @@ -1473,13 +1454,6 @@ static void cpu_set_multi_ext_cfg(Object *obj, Visitor *v, const char *name, return; } - if (cpu_ext_is_deprecated(multi_ext_cfg->name)) { - g_autofree char *lower = cpu_ext_to_lower(multi_ext_cfg->name); - - warn_report("CPU property '%s' is deprecated. Please use '%s' instead", - multi_ext_cfg->name, lower); - } - cpu_cfg_ext_add_user_opt(multi_ext_cfg->offset, value); prev_val = isa_ext_is_enabled(cpu, multi_ext_cfg->offset); @@ -1515,14 +1489,13 @@ static void cpu_add_multi_ext_prop(Object *cpu_obj, const RISCVCPUMultiExtConfig *multi_cfg) { bool generic_cpu = riscv_cpu_is_generic(cpu_obj); - bool deprecated_ext = cpu_ext_is_deprecated(multi_cfg->name); object_property_add(cpu_obj, multi_cfg->name, "bool", cpu_get_multi_ext_cfg, cpu_set_multi_ext_cfg, NULL, (void *)multi_cfg); - if (!generic_cpu || deprecated_ext) { + if (!generic_cpu) { return; } @@ -1565,8 +1538,6 @@ static void riscv_cpu_add_user_properties(Object *obj) riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_vendor_exts); riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_experimental_exts); - riscv_cpu_add_multiext_prop_array(obj, riscv_cpu_deprecated_exts); - riscv_cpu_add_profiles(obj); } From f31ba686a9387640a905ac70fe682c15c461a134 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 4 Jun 2025 14:43:27 -0300 Subject: [PATCH 1871/2760] target/riscv/cpu.c: add 'sdtrig' in riscv,isa We have support for sdtrig for awhile but we are not advertising it. It is enabled by default via the 'debug' flag. Use the same flag to also advertise sdtrig. Add an exception in disable_priv_spec_isa_exts() to avoid spamming warnings for 'sdtrig' for vendor CPUs like sifive_u. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Message-ID: <20250604174329.1147549-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + target/riscv/tcg/tcg-cpu.c | 9 +++++++++ tests/data/acpi/riscv64/virt/RHCT | Bin 400 -> 406 bytes 3 files changed, 10 insertions(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 7c6e0844d0..b4e7eff331 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -189,6 +189,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(zvkt, PRIV_VERSION_1_12_0, ext_zvkt), ISA_EXT_DATA_ENTRY(zhinx, PRIV_VERSION_1_12_0, ext_zhinx), ISA_EXT_DATA_ENTRY(zhinxmin, PRIV_VERSION_1_12_0, ext_zhinxmin), + ISA_EXT_DATA_ENTRY(sdtrig, PRIV_VERSION_1_12_0, debug), ISA_EXT_DATA_ENTRY(shcounterenw, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(sha, PRIV_VERSION_1_12_0, ext_sha), ISA_EXT_DATA_ENTRY(shgatpa, PRIV_VERSION_1_12_0, has_priv_1_12), diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 81174de409..163e7ce364 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -451,6 +451,15 @@ static void riscv_cpu_disable_priv_spec_isa_exts(RISCVCPU *cpu) continue; } + /* + * cpu.debug = true is marked as 'sdtrig', priv spec 1.12. + * Skip this warning since existing CPUs with older priv + * spec and debug = true will be impacted. + */ + if (!strcmp(edata->name, "sdtrig")) { + continue; + } + isa_ext_update_enabled(cpu, edata->ext_enable_offset, false); /* diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index 13c8025b868051485be5ba62974a22971a07bc6a..156607dec45b0e63e5b3ebed62e81076dacd80d0 100644 GIT binary patch delta 49 zcmbQhJdK$v$iq2g8Y2S( Date: Wed, 4 Jun 2025 14:43:28 -0300 Subject: [PATCH 1872/2760] target/riscv/cpu.c: add 'ssstrict' to riscv, isa 'ssstrict' is a RVA23 profile-defined extension defined as follows: "No non-conforming extensions are present. Attempts to execute unimplemented opcodes or access unimplemented CSRs in the standard or reserved encoding spaces raises an illegal instruction exception that results in a contained trap to the supervisor-mode trap handler." In short, we need to throw an exception when accessing unimplemented CSRs or opcodes. We do that, so let's advertise it. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Andrew Jones Message-ID: <20250529202315.1684198-3-dbarboza@ventanamicro.com> Message-ID: <20250604174329.1147549-3-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + tests/data/acpi/riscv64/virt/RHCT | Bin 406 -> 416 bytes 2 files changed, 1 insertion(+) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index b4e7eff331..626b0b8b26 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -217,6 +217,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(ssnpm, PRIV_VERSION_1_13_0, ext_ssnpm), ISA_EXT_DATA_ENTRY(sspm, PRIV_VERSION_1_13_0, ext_sspm), ISA_EXT_DATA_ENTRY(ssstateen, PRIV_VERSION_1_12_0, ext_ssstateen), + ISA_EXT_DATA_ENTRY(ssstrict, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(sstc, PRIV_VERSION_1_12_0, ext_sstc), ISA_EXT_DATA_ENTRY(sstvala, PRIV_VERSION_1_12_0, has_priv_1_12), ISA_EXT_DATA_ENTRY(sstvecd, PRIV_VERSION_1_12_0, has_priv_1_12), diff --git a/tests/data/acpi/riscv64/virt/RHCT b/tests/data/acpi/riscv64/virt/RHCT index 156607dec45b0e63e5b3ebed62e81076dacd80d0..52a4cc4b6380eee3299b965271a39e9e01f5a698 100644 GIT binary patch delta 52 zcmbQnynvZ2$iq2g0V4wgn@V11f0% FVgOSb3+?~_ delta 45 ycmZ3$JdK$v$iq2g8Y2S(U$N|cg0WkpaF9|>Z From 5ee4f21713d51fdf3d8756744ffe92365df8598b Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Wed, 4 Jun 2025 14:43:29 -0300 Subject: [PATCH 1873/2760] target/riscv/cpu.c: do better with 'named features' doc Most of the named features are added directly in isa_edata_arr[], some of them are also added in riscv_cpu_named_features(). There is a reason for that, and the existing docs can do better explaining it. Signed-off-by: Daniel Henrique Barboza Message-ID: <20250529202315.1684198-4-dbarboza@ventanamicro.com> Acked-by: Alistair Francis Message-ID: <20250604174329.1147549-4-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 626b0b8b26..bf14256a61 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1377,13 +1377,23 @@ const RISCVCPUMultiExtConfig riscv_cpu_experimental_exts[] = { * 'Named features' is the name we give to extensions that we * don't want to expose to users. They are either immutable * (always enabled/disable) or they'll vary depending on - * the resulting CPU state. They have riscv,isa strings - * and priv_ver like regular extensions. + * the resulting CPU state. + * + * Some of them are always enabled depending on priv version + * of the CPU and are declared directly in isa_edata_arr[]. + * The ones listed here have special checks during finalize() + * time and require their own flags like regular extensions. + * See riscv_cpu_update_named_features() for more info. */ const RISCVCPUMultiExtConfig riscv_cpu_named_features[] = { MULTI_EXT_CFG_BOOL("zic64b", ext_zic64b, true), MULTI_EXT_CFG_BOOL("ssstateen", ext_ssstateen, true), MULTI_EXT_CFG_BOOL("sha", ext_sha, true), + + /* + * 'ziccrse' has its own flag because the KVM driver + * wants to enable/disable it on its own accord. + */ MULTI_EXT_CFG_BOOL("ziccrse", ext_ziccrse, true), { }, From f9eaa1542be5f7273dec1111a1e17d8ff2a5bcba Mon Sep 17 00:00:00 2001 From: Jim Shu Date: Thu, 8 May 2025 17:48:38 +0800 Subject: [PATCH 1874/2760] target/riscv: support atomic instruction fetch (Ziccif) Support 4-byte atomic instruction fetch when instruction is natural aligned. Current implementation is not atomic because it loads instruction twice for first and last 2 bytes. We load 4 bytes at once to keep the atomicity. This instruction preload method only applys when instruction is 4-byte aligned. If instruction is unaligned, it could be across pages so that preload will trigger additional page fault. We encounter this issue when doing pressure test of enabling & disabling Linux kernel ftrace. Ftrace with kernel preemption requires concurrent modification and execution of instruction, so non-atomic instruction fetch will cause the race condition. We may fetch the wrong instruction which is the mixing of 2 instructions. Also, RISC-V Profile wants to provide this feature by HW. RVA20U64 Ziccif protects the atomicity of instruction fetch when it is natural aligned. This commit depends on the atomic read support of translator_ld in the commit 6a9dfe1984b0c593fb0ddb52d4e70832e6201dd6. Signed-off-by: Jim Shu Reviewed-by: Frank Chang Reviewed-by: Alistair Francis Message-ID: <20250508094838.19394-1-jim.shu@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/translate.c | 46 +++++++++++++++++++++++++++++----------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/target/riscv/translate.c b/target/riscv/translate.c index d7a6de02df..9ddef2d6e2 100644 --- a/target/riscv/translate.c +++ b/target/riscv/translate.c @@ -1217,13 +1217,35 @@ const RISCVDecoder decoder_table[] = { const size_t decoder_table_size = ARRAY_SIZE(decoder_table); -static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) +static void decode_opc(CPURISCVState *env, DisasContext *ctx) { + uint32_t opcode; + bool pc_is_4byte_align = ((ctx->base.pc_next % 4) == 0); + ctx->virt_inst_excp = false; - ctx->cur_insn_len = insn_len(opcode); + if (pc_is_4byte_align) { + /* + * Load 4 bytes at once to make instruction fetch atomically. + * + * Note: When pc is 4-byte aligned, 4-byte instruction wouldn't be + * across pages. We could preload 4 bytes instruction no matter + * real one is 2 or 4 bytes. Instruction preload wouldn't trigger + * additional page fault. + */ + opcode = translator_ldl(env, &ctx->base, ctx->base.pc_next); + } else { + /* + * For unaligned pc, instruction preload may trigger additional + * page fault so we only load 2 bytes here. + */ + opcode = (uint32_t) translator_lduw(env, &ctx->base, ctx->base.pc_next); + } + ctx->ol = ctx->xl; + + ctx->cur_insn_len = insn_len((uint16_t)opcode); /* Check for compressed insn */ if (ctx->cur_insn_len == 2) { - ctx->opcode = opcode; + ctx->opcode = (uint16_t)opcode; /* * The Zca extension is added as way to refer to instructions in the C * extension that do not include the floating-point loads and stores @@ -1233,15 +1255,17 @@ static void decode_opc(CPURISCVState *env, DisasContext *ctx, uint16_t opcode) return; } } else { - uint32_t opcode32 = opcode; - opcode32 = deposit32(opcode32, 16, 16, - translator_lduw(env, &ctx->base, - ctx->base.pc_next + 2)); - ctx->opcode = opcode32; + if (!pc_is_4byte_align) { + /* Load last 2 bytes of instruction here */ + opcode = deposit32(opcode, 16, 16, + translator_lduw(env, &ctx->base, + ctx->base.pc_next + 2)); + } + ctx->opcode = opcode; for (guint i = 0; i < ctx->decoders->len; ++i) { riscv_cpu_decode_fn func = g_ptr_array_index(ctx->decoders, i); - if (func(ctx, opcode32)) { + if (func(ctx, opcode)) { return; } } @@ -1319,10 +1343,8 @@ static void riscv_tr_translate_insn(DisasContextBase *dcbase, CPUState *cpu) { DisasContext *ctx = container_of(dcbase, DisasContext, base); CPURISCVState *env = cpu_env(cpu); - uint16_t opcode16 = translator_lduw(env, &ctx->base, ctx->base.pc_next); - ctx->ol = ctx->xl; - decode_opc(env, ctx, opcode16); + decode_opc(env, ctx); ctx->base.pc_next += ctx->cur_insn_len; /* From b0175841fa4f867b4b1219dba72c33392118dd43 Mon Sep 17 00:00:00 2001 From: Meng Zhuo Date: Fri, 6 Jun 2025 11:42:51 +0800 Subject: [PATCH 1875/2760] target/riscv/kvm: add max_satp_mode from host cpu This patch adds max_satp_mode from host kvm cpu setting. Tested on: Milkv Megrez (Eswin 7700x) Reviewed-by: Andrew Jones Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2931 Signed-off-by: Meng Zhuo Message-ID: <20250606034250.181707-1-mengzhuo@iscas.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index e1a04be20f..502d33f404 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -999,6 +999,19 @@ static void kvm_riscv_destroy_scratch_vcpu(KVMScratchCPU *scratch) close(scratch->kvmfd); } +static void kvm_riscv_init_max_satp_mode(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) +{ + struct kvm_one_reg reg; + int ret; + + reg.id = RISCV_CONFIG_REG(satp_mode); + reg.addr = (uint64_t)&cpu->cfg.max_satp_mode; + ret = ioctl(kvmcpu->cpufd, KVM_GET_ONE_REG, ®); + if (ret != 0) { + error_report("Unable to retrieve satp mode from host, error %d", ret); + } +} + static void kvm_riscv_init_machine_ids(RISCVCPU *cpu, KVMScratchCPU *kvmcpu) { struct kvm_one_reg reg; @@ -1302,6 +1315,7 @@ static void riscv_init_kvm_registers(Object *cpu_obj) kvm_riscv_init_machine_ids(cpu, &kvmcpu); kvm_riscv_init_misa_ext_mask(cpu, &kvmcpu); kvm_riscv_init_cfg(cpu, &kvmcpu); + kvm_riscv_init_max_satp_mode(cpu, &kvmcpu); kvm_riscv_destroy_scratch_vcpu(&kvmcpu); } @@ -1985,7 +1999,7 @@ static bool kvm_cpu_realize(CPUState *cs, Error **errp) } } - return true; + return true; } void riscv_kvm_cpu_finalize_features(RISCVCPU *cpu, Error **errp) From cd633bea8b0d30f3418b0dd372116bf3e028e42f Mon Sep 17 00:00:00 2001 From: Jay Chang Date: Fri, 6 Jun 2025 15:25:25 +0800 Subject: [PATCH 1876/2760] target/riscv: Make PMP region count configurable Previously, the number of PMP regions was hardcoded to 16 in QEMU. This patch replaces the fixed value with a new `pmp_regions` field, allowing platforms to configure the number of PMP regions. If no specific value is provided, the default number of PMP regions remains 16 to preserve the existing behavior. A new CPU parameter num-pmp-regions has been introduced to the QEMU command line. For example: -cpu rv64, g=true, c=true, pmp=true, num-pmp-regions=8 Signed-off-by: Jay Chang Reviewed-by: Frank Chang Reviewed-by: Alistair Francis Message-ID: <20250606072525.17313-3-jay.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 48 +++++++++++++++++++++++++++++-- target/riscv/cpu.h | 3 +- target/riscv/cpu_cfg_fields.h.inc | 1 + target/riscv/csr.c | 5 +++- target/riscv/machine.c | 3 +- target/riscv/pmp.c | 28 ++++++++++++------ 6 files changed, 74 insertions(+), 14 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index bf14256a61..758f254c15 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -1119,6 +1119,7 @@ static void riscv_cpu_init(Object *obj) cpu->cfg.cbom_blocksize = 64; cpu->cfg.cbop_blocksize = 64; cpu->cfg.cboz_blocksize = 64; + cpu->cfg.pmp_regions = 16; cpu->env.vext_ver = VEXT_VERSION_1_00_0; cpu->cfg.max_satp_mode = -1; @@ -1563,6 +1564,46 @@ static const PropertyInfo prop_pmp = { .set = prop_pmp_set, }; +static void prop_num_pmp_regions_set(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + RISCVCPU *cpu = RISCV_CPU(obj); + uint8_t value; + + visit_type_uint8(v, name, &value, errp); + + if (cpu->cfg.pmp_regions != value && riscv_cpu_is_vendor(obj)) { + cpu_set_prop_err(cpu, name, errp); + return; + } + + if (cpu->env.priv_ver < PRIV_VERSION_1_12_0 && value > OLD_MAX_RISCV_PMPS) { + error_setg(errp, "Number of PMP regions exceeds maximum available"); + return; + } else if (value > MAX_RISCV_PMPS) { + error_setg(errp, "Number of PMP regions exceeds maximum available"); + return; + } + + cpu_option_add_user_setting(name, value); + cpu->cfg.pmp_regions = value; +} + +static void prop_num_pmp_regions_get(Object *obj, Visitor *v, const char *name, + void *opaque, Error **errp) +{ + uint8_t value = RISCV_CPU(obj)->cfg.pmp_regions; + + visit_type_uint8(v, name, &value, errp); +} + +static const PropertyInfo prop_num_pmp_regions = { + .type = "uint8", + .description = "num-pmp-regions", + .get = prop_num_pmp_regions_get, + .set = prop_num_pmp_regions_set, +}; + static int priv_spec_from_str(const char *priv_spec_str) { int priv_version = -1; @@ -2562,6 +2603,7 @@ static const Property riscv_cpu_properties[] = { {.name = "mmu", .info = &prop_mmu}, {.name = "pmp", .info = &prop_pmp}, + {.name = "num-pmp-regions", .info = &prop_num_pmp_regions}, {.name = "priv_spec", .info = &prop_priv_spec}, {.name = "vext_spec", .info = &prop_vext_spec}, @@ -2932,7 +2974,8 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.max_satp_mode = VM_1_10_MBARE, .cfg.ext_zifencei = true, .cfg.ext_zicsr = true, - .cfg.pmp = true + .cfg.pmp = true, + .cfg.pmp_regions = 8 ), DEFINE_ABSTRACT_RISCV_CPU(TYPE_RISCV_CPU_SIFIVE_U, TYPE_RISCV_VENDOR_CPU, @@ -2943,7 +2986,8 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.ext_zifencei = true, .cfg.ext_zicsr = true, .cfg.mmu = true, - .cfg.pmp = true + .cfg.pmp = true, + .cfg.pmp_regions = 8 ), #if defined(TARGET_RISCV32) || \ diff --git a/target/riscv/cpu.h b/target/riscv/cpu.h index 17bf4e7579..4a862da615 100644 --- a/target/riscv/cpu.h +++ b/target/riscv/cpu.h @@ -174,7 +174,8 @@ extern RISCVCPUImpliedExtsRule *riscv_multi_ext_implied_rules[]; #define MMU_USER_IDX 3 -#define MAX_RISCV_PMPS (16) +#define MAX_RISCV_PMPS (64) +#define OLD_MAX_RISCV_PMPS (16) #if !defined(CONFIG_USER_ONLY) #include "pmp.h" diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index 59f134a419..33c4f9bac8 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -163,6 +163,7 @@ TYPED_FIELD(uint16_t, elen, 0) TYPED_FIELD(uint16_t, cbom_blocksize, 0) TYPED_FIELD(uint16_t, cbop_blocksize, 0) TYPED_FIELD(uint16_t, cboz_blocksize, 0) +TYPED_FIELD(uint8_t, pmp_regions, 0) TYPED_FIELD(int8_t, max_satp_mode, -1) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index d6cd441133..6296ecd1e1 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -738,7 +738,10 @@ static RISCVException dbltrp_hmode(CPURISCVState *env, int csrno) static RISCVException pmp(CPURISCVState *env, int csrno) { if (riscv_cpu_cfg(env)->pmp) { - if (csrno <= CSR_PMPCFG3) { + int max_pmpcfg = (env->priv_ver >= PRIV_VERSION_1_12_0) ? ++ CSR_PMPCFG15 : CSR_PMPCFG3; + + if (csrno <= max_pmpcfg) { uint32_t reg_index = csrno - CSR_PMPCFG0; /* TODO: RV128 restriction check */ diff --git a/target/riscv/machine.c b/target/riscv/machine.c index c97e9ce9df..1600ec44f0 100644 --- a/target/riscv/machine.c +++ b/target/riscv/machine.c @@ -36,8 +36,9 @@ static int pmp_post_load(void *opaque, int version_id) RISCVCPU *cpu = opaque; CPURISCVState *env = &cpu->env; int i; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; - for (i = 0; i < MAX_RISCV_PMPS; i++) { + for (i = 0; i < pmp_regions; i++) { pmp_update_rule_addr(env, i); } pmp_update_rule_nums(env); diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 5af295e410..3540327c9a 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -122,7 +122,9 @@ uint32_t pmp_get_num_rules(CPURISCVState *env) */ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) { - if (pmp_index < MAX_RISCV_PMPS) { + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; + + if (pmp_index < pmp_regions) { return env->pmp_state.pmp[pmp_index].cfg_reg; } @@ -136,7 +138,9 @@ static inline uint8_t pmp_read_cfg(CPURISCVState *env, uint32_t pmp_index) */ static bool pmp_write_cfg(CPURISCVState *env, uint32_t pmp_index, uint8_t val) { - if (pmp_index < MAX_RISCV_PMPS) { + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; + + if (pmp_index < pmp_regions) { if (env->pmp_state.pmp[pmp_index].cfg_reg == val) { /* no change */ return false; @@ -236,9 +240,10 @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) void pmp_update_rule_nums(CPURISCVState *env) { int i; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; env->pmp_state.num_rules = 0; - for (i = 0; i < MAX_RISCV_PMPS; i++) { + for (i = 0; i < pmp_regions; i++) { const uint8_t a_field = pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg); if (PMP_AMATCH_OFF != a_field) { @@ -332,6 +337,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr, int pmp_size = 0; hwaddr s = 0; hwaddr e = 0; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; /* Short cut if no rules */ if (0 == pmp_get_num_rules(env)) { @@ -356,7 +362,7 @@ bool pmp_hart_has_privs(CPURISCVState *env, hwaddr addr, * 1.10 draft priv spec states there is an implicit order * from low to high */ - for (i = 0; i < MAX_RISCV_PMPS; i++) { + for (i = 0; i < pmp_regions; i++) { s = pmp_is_in_range(env, i, addr); e = pmp_is_in_range(env, i, addr + pmp_size - 1); @@ -527,8 +533,9 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, { trace_pmpaddr_csr_write(env->mhartid, addr_index, val); bool is_next_cfg_tor = false; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; - if (addr_index < MAX_RISCV_PMPS) { + if (addr_index < pmp_regions) { if (env->pmp_state.pmp[addr_index].addr_reg == val) { /* no change */ return; @@ -538,7 +545,7 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, * In TOR mode, need to check the lock bit of the next pmp * (if there is a next). */ - if (addr_index + 1 < MAX_RISCV_PMPS) { + if (addr_index + 1 < pmp_regions) { uint8_t pmp_cfg = env->pmp_state.pmp[addr_index + 1].cfg_reg; is_next_cfg_tor = PMP_AMATCH_TOR == pmp_get_a_field(pmp_cfg); @@ -573,8 +580,9 @@ void pmpaddr_csr_write(CPURISCVState *env, uint32_t addr_index, target_ulong pmpaddr_csr_read(CPURISCVState *env, uint32_t addr_index) { target_ulong val = 0; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; - if (addr_index < MAX_RISCV_PMPS) { + if (addr_index < pmp_regions) { val = env->pmp_state.pmp[addr_index].addr_reg; trace_pmpaddr_csr_read(env->mhartid, addr_index, val); } else { @@ -592,6 +600,7 @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val) { int i; uint64_t mask = MSECCFG_MMWP | MSECCFG_MML; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; /* Update PMM field only if the value is valid according to Zjpm v1.0 */ if (riscv_cpu_cfg(env)->ext_smmpm && riscv_cpu_mxl(env) == MXL_RV64 && @@ -603,7 +612,7 @@ void mseccfg_csr_write(CPURISCVState *env, target_ulong val) /* RLB cannot be enabled if it's already 0 and if any regions are locked */ if (!MSECCFG_RLB_ISSET(env)) { - for (i = 0; i < MAX_RISCV_PMPS; i++) { + for (i = 0; i < pmp_regions; i++) { if (pmp_is_locked(env, i)) { val &= ~MSECCFG_RLB; break; @@ -659,6 +668,7 @@ target_ulong pmp_get_tlb_size(CPURISCVState *env, hwaddr addr) hwaddr tlb_sa = addr & ~(TARGET_PAGE_SIZE - 1); hwaddr tlb_ea = tlb_sa + TARGET_PAGE_SIZE - 1; int i; + uint8_t pmp_regions = riscv_cpu_cfg(env)->pmp_regions; /* * If PMP is not supported or there are no PMP rules, the TLB page will not @@ -669,7 +679,7 @@ target_ulong pmp_get_tlb_size(CPURISCVState *env, hwaddr addr) return TARGET_PAGE_SIZE; } - for (i = 0; i < MAX_RISCV_PMPS; i++) { + for (i = 0; i < pmp_regions; i++) { if (pmp_get_a_field(env->pmp_state.pmp[i].cfg_reg) == PMP_AMATCH_OFF) { continue; } From 5000ba0cb1c513283f0a2f2f9742cfe8053dab8d Mon Sep 17 00:00:00 2001 From: Nutty Liu Date: Thu, 5 Jun 2025 20:48:48 +0800 Subject: [PATCH 1877/2760] hw/riscv/riscv-iommu: Fix PPN field of Translation-reponse register The original implementation incorrectly performed a bitwise AND operation between the PPN of iova and PPN Mask, leading to an incorrect PPN field in Translation-reponse register. The PPN of iova should be set entirely in the PPN field of Translation-reponse register. Also remove the code that was used to clear S field since this field is already zero. Signed-off-by: Nutty Liu Reviewed-by: Tomasz Jeznach Message-ID: <20250605124848.1248-1-liujingqi@lanxincomputing.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index a877e5da84..d8b1cb03a8 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -1935,11 +1935,7 @@ static void riscv_iommu_process_dbg(RISCVIOMMUState *s) iova = RISCV_IOMMU_TR_RESPONSE_FAULT | (((uint64_t) fault) << 10); } else { iova = iotlb.translated_addr & ~iotlb.addr_mask; - iova >>= TARGET_PAGE_BITS; - iova &= RISCV_IOMMU_TR_RESPONSE_PPN; - - /* We do not support superpages (> 4kbs) for now */ - iova &= ~RISCV_IOMMU_TR_RESPONSE_S; + iova = set_field(0, RISCV_IOMMU_TR_RESPONSE_PPN, PPN_DOWN(iova)); } riscv_iommu_reg_set64(s, RISCV_IOMMU_REG_TR_RESPONSE, iova); } From 7ec39d0cc945dd81108e08ca37afcfb3c9e5e012 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 5 Jun 2025 06:44:56 -0300 Subject: [PATCH 1878/2760] target/riscv: use qemu_chr_fe_write_all() in DBCN_CONSOLE_WRITE_BYTE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SBI spec states, for console write byte: "This is a blocking SBI call and it will only return after writing the specified byte to the debug console. It will also return, with SBI_ERR_FAILED, if there are I/O errors." Being a blocker call will either succeed writing the byte or error out, it's feasible to use the blocking qemu_chr_fe_write_all() instead of qemu_chr_fe_write(). Last but not the least, we will duck possible changes in qemu_chr_fe_write() where ret = 0 will have a 'zero byte written' semantic [1] - something that we're not ready to deal in this current state. [1] https://lore.kernel.org/qemu-devel/CAFEAcA_kEndvNtw4EHySXWwQPoGs029yAzZGGBcV=zGHaj7KUQ@mail.gmail.com/ Signed-off-by: Daniel Henrique Barboza Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250605094456.1385105-2-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/kvm/kvm-cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/kvm/kvm-cpu.c b/target/riscv/kvm/kvm-cpu.c index 502d33f404..5c19062c19 100644 --- a/target/riscv/kvm/kvm-cpu.c +++ b/target/riscv/kvm/kvm-cpu.c @@ -1619,7 +1619,7 @@ static void kvm_riscv_handle_sbi_dbcn(CPUState *cs, struct kvm_run *run) break; case SBI_EXT_DBCN_CONSOLE_WRITE_BYTE: ch = run->riscv_sbi.args[0]; - ret = qemu_chr_fe_write(serial_hd(0)->be, &ch, sizeof(ch)); + ret = qemu_chr_fe_write_all(serial_hd(0)->be, &ch, sizeof(ch)); if (ret < 0) { error_report("SBI_EXT_DBCN_CONSOLE_WRITE_BYTE: error when " From 2b027e73eefab0f9d3a0048564d1b653ba1d7703 Mon Sep 17 00:00:00 2001 From: Anton Blanchard Date: Thu, 1 May 2025 11:42:53 +0000 Subject: [PATCH 1879/2760] target/riscv: Fix fcvt.s.bf16 NaN box checking fcvt.s.bf16 uses the FP16 check_nanbox_h() which returns an FP16 quiet NaN. Add check_nanbox_bf16() which returns a BF16 quiet NaN. Signed-off-by: Anton Blanchard Acked-by: Alistair Francis Message-ID: <20250501114253.594887-1-antonb@tenstorrent.com> Signed-off-by: Alistair Francis --- target/riscv/fpu_helper.c | 2 +- target/riscv/internals.h | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/target/riscv/fpu_helper.c b/target/riscv/fpu_helper.c index 706bdfa61d..af40561b31 100644 --- a/target/riscv/fpu_helper.c +++ b/target/riscv/fpu_helper.c @@ -755,6 +755,6 @@ uint64_t helper_fcvt_bf16_s(CPURISCVState *env, uint64_t rs1) uint64_t helper_fcvt_s_bf16(CPURISCVState *env, uint64_t rs1) { - float16 frs1 = check_nanbox_h(env, rs1); + float16 frs1 = check_nanbox_bf16(env, rs1); return nanbox_s(env, bfloat16_to_float32(frs1, &env->fp_status)); } diff --git a/target/riscv/internals.h b/target/riscv/internals.h index 4570bd50be..9686bb6208 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -142,6 +142,22 @@ static inline float16 check_nanbox_h(CPURISCVState *env, uint64_t f) } } +static inline float16 check_nanbox_bf16(CPURISCVState *env, uint64_t f) +{ + /* Disable nanbox check when enable zfinx */ + if (env_archcpu(env)->cfg.ext_zfinx) { + return (uint16_t)f; + } + + uint64_t mask = MAKE_64BIT_MASK(16, 48); + + if (likely((f & mask) == mask)) { + return (uint16_t)f; + } else { + return 0x7FC0u; /* default qnan */ + } +} + #ifndef CONFIG_USER_ONLY /* Our implementation of SysemuCPUOps::has_work */ bool riscv_cpu_has_work(CPUState *cs); From 61240e3a06dc622d249b530557d5ce03c5854592 Mon Sep 17 00:00:00 2001 From: Florian Lugou Date: Thu, 5 Jun 2025 12:12:54 +0200 Subject: [PATCH 1880/2760] hw/char: sifive_uart: Avoid infinite delay of async xmit function The current handler for TXFIFO writes schedules an async callback to pop characters from the queue. When software writes to TXFIFO faster than the async callback delay (100ns), the timer may be pushed back while the previous character has not be dequeued yet. This happens in particular when using -icount with small shift values. This is especially worrysome when software repetitively issues amoor.w instructions (as suggested by SiFive specification) and the FIFO is full, leading to the callback being infinitly pushed back. This commit fixes the issue by never pushing back the timer, only updating it if it is not already active. Signed-off-by: Florian Lugou Reviewed-by: Alistair Francis Message-ID: <20250605101255.797162-1-florian.lugou@provenrun.com> Signed-off-by: Alistair Francis --- hw/char/sifive_uart.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hw/char/sifive_uart.c b/hw/char/sifive_uart.c index 0fc89e76d1..9bc697a67b 100644 --- a/hw/char/sifive_uart.c +++ b/hw/char/sifive_uart.c @@ -128,8 +128,10 @@ static void sifive_uart_write_tx_fifo(SiFiveUARTState *s, const uint8_t *buf, s->txfifo |= SIFIVE_UART_TXFIFO_FULL; } - timer_mod(s->fifo_trigger_handle, current_time + - TX_INTERRUPT_TRIGGER_DELAY_NS); + if (!timer_pending(s->fifo_trigger_handle)) { + timer_mod(s->fifo_trigger_handle, current_time + + TX_INTERRUPT_TRIGGER_DELAY_NS); + } } static uint64_t From 81a245091fff2651f14004d1820f7ee77dd4f0fc Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 4 Jun 2025 12:24:37 +0930 Subject: [PATCH 1881/2760] hw/riscv/virt: Fix clint base address type The address is a hardware address, so use hwaddr for consistency with the rest of the machine. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Joel Stanley Message-ID: <20250604025450.85327-2-joel@jms.id.au> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index cf280a92e5..875eb7155a 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -324,7 +324,7 @@ static void create_fdt_socket_clint(RISCVVirtState *s, int cpu; g_autofree char *clint_name = NULL; g_autofree uint32_t *clint_cells = NULL; - unsigned long clint_addr; + hwaddr clint_addr; MachineState *ms = MACHINE(s); static const char * const clint_compat[2] = { "sifive,clint0", "riscv,clint0" @@ -340,8 +340,8 @@ static void create_fdt_socket_clint(RISCVVirtState *s, } clint_addr = s->memmap[VIRT_CLINT].base + - (s->memmap[VIRT_CLINT].size * socket); - clint_name = g_strdup_printf("/soc/clint@%lx", clint_addr); + s->memmap[VIRT_CLINT].size * socket; + clint_name = g_strdup_printf("/soc/clint@%"HWADDR_PRIx, clint_addr); qemu_fdt_add_subnode(ms->fdt, clint_name); qemu_fdt_setprop_string_array(ms->fdt, clint_name, "compatible", (char **)&clint_compat, From 16adb1f5d7ac548983f1b59d8e15f2eb5e51ae2c Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 4 Jun 2025 12:24:38 +0930 Subject: [PATCH 1882/2760] hw/riscv/virt: Use setprop_sized_cells for clint The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Joel Stanley Message-ID: <20250604025450.85327-3-joel@jms.id.au> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 875eb7155a..5143a46555 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -346,8 +346,8 @@ static void create_fdt_socket_clint(RISCVVirtState *s, qemu_fdt_setprop_string_array(ms->fdt, clint_name, "compatible", (char **)&clint_compat, ARRAY_SIZE(clint_compat)); - qemu_fdt_setprop_cells(ms->fdt, clint_name, "reg", - 0x0, clint_addr, 0x0, s->memmap[VIRT_CLINT].size); + qemu_fdt_setprop_sized_cells(ms->fdt, clint_name, "reg", + 2, clint_addr, 2, s->memmap[VIRT_CLINT].size); qemu_fdt_setprop(ms->fdt, clint_name, "interrupts-extended", clint_cells, s->soc[socket].num_harts * sizeof(uint32_t) * 4); riscv_socket_fdt_write_id(ms, clint_name, socket); From 349500bfb8523c94f88da4bfc41a22ccbba19055 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 4 Jun 2025 12:24:39 +0930 Subject: [PATCH 1883/2760] hw/riscv/virt: Use setprop_sized_cells for memory Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Joel Stanley Message-ID: <20250604025450.85327-4-joel@jms.id.au> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 5143a46555..e074a29675 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -311,8 +311,7 @@ static void create_fdt_socket_memory(RISCVVirtState *s, int socket) size = riscv_socket_mem_size(ms, socket); mem_name = g_strdup_printf("/memory@%"HWADDR_PRIx, addr); qemu_fdt_add_subnode(ms->fdt, mem_name); - qemu_fdt_setprop_cells(ms->fdt, mem_name, "reg", - addr >> 32, addr, size >> 32, size); + qemu_fdt_setprop_sized_cells(ms->fdt, mem_name, "reg", 2, addr, 2, size); qemu_fdt_setprop_string(ms->fdt, mem_name, "device_type", "memory"); riscv_socket_fdt_write_id(ms, mem_name, socket); } From 4b7b4f9cb4fd4b569661e690c593780332d7291a Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 4 Jun 2025 12:24:40 +0930 Subject: [PATCH 1884/2760] hw/riscv/virt: Use setprop_sized_cells for aplic The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Joel Stanley Message-ID: <20250604025450.85327-5-joel@jms.id.au> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index e074a29675..205fa6e44f 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -655,8 +655,8 @@ static void create_fdt_one_aplic(RISCVVirtState *s, int socket, qemu_fdt_setprop_cell(ms->fdt, aplic_name, "msi-parent", msi_phandle); } - qemu_fdt_setprop_cells(ms->fdt, aplic_name, "reg", - 0x0, aplic_addr, 0x0, aplic_size); + qemu_fdt_setprop_sized_cells(ms->fdt, aplic_name, "reg", + 2, aplic_addr, 2, aplic_size); qemu_fdt_setprop_cell(ms->fdt, aplic_name, "riscv,num-sources", VIRT_IRQCHIP_NUM_SOURCES); From dd3d4fd9923082d8d2ad6ed71b4be05dad36b601 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 4 Jun 2025 12:24:41 +0930 Subject: [PATCH 1885/2760] hw/riscv/virt: Use setprop_sized_cells for aclint The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Joel Stanley Message-ID: <20250604025450.85327-6-joel@jms.id.au> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 205fa6e44f..4fd966a342 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -387,8 +387,8 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-mswi"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, addr, 0x0, RISCV_ACLINT_SWI_SIZE); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", + 2, addr, 2, RISCV_ACLINT_SWI_SIZE); qemu_fdt_setprop(ms->fdt, name, "interrupts-extended", aclint_mswi_cells, aclint_cells_size); qemu_fdt_setprop(ms->fdt, name, "interrupt-controller", NULL, 0); @@ -410,11 +410,11 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-mtimer"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, addr + RISCV_ACLINT_DEFAULT_MTIME, - 0x0, size - RISCV_ACLINT_DEFAULT_MTIME, - 0x0, addr + RISCV_ACLINT_DEFAULT_MTIMECMP, - 0x0, RISCV_ACLINT_DEFAULT_MTIME); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", + 2, addr + RISCV_ACLINT_DEFAULT_MTIME, + 2, size - RISCV_ACLINT_DEFAULT_MTIME, + 2, addr + RISCV_ACLINT_DEFAULT_MTIMECMP, + 2, RISCV_ACLINT_DEFAULT_MTIME); qemu_fdt_setprop(ms->fdt, name, "interrupts-extended", aclint_mtimer_cells, aclint_cells_size); riscv_socket_fdt_write_id(ms, name, socket); @@ -428,8 +428,8 @@ static void create_fdt_socket_aclint(RISCVVirtState *s, qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "riscv,aclint-sswi"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, addr, 0x0, s->memmap[VIRT_ACLINT_SSWI].size); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", + 2, addr, 2, s->memmap[VIRT_ACLINT_SSWI].size); qemu_fdt_setprop(ms->fdt, name, "interrupts-extended", aclint_sswi_cells, aclint_cells_size); qemu_fdt_setprop(ms->fdt, name, "interrupt-controller", NULL, 0); From 507161b5f53ae37e2aeeb99e558485146546331e Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 4 Jun 2025 12:24:42 +0930 Subject: [PATCH 1886/2760] hw/riscv/virt: Use setprop_sized_cells for plic The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Joel Stanley Message-ID: <20250604025450.85327-7-joel@jms.id.au> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 4fd966a342..67e60eec1f 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -493,8 +493,8 @@ static void create_fdt_socket_plic(RISCVVirtState *s, s->soc[socket].num_harts * sizeof(uint32_t) * 4); } - qemu_fdt_setprop_cells(ms->fdt, plic_name, "reg", - 0x0, plic_addr, 0x0, s->memmap[VIRT_PLIC].size); + qemu_fdt_setprop_sized_cells(ms->fdt, plic_name, "reg", + 2, plic_addr, 2, s->memmap[VIRT_PLIC].size); qemu_fdt_setprop_cell(ms->fdt, plic_name, "riscv,ndev", VIRT_IRQCHIP_NUM_SOURCES - 1); riscv_socket_fdt_write_id(ms, plic_name, socket); From ad41a7022bd10a2db922cd4e314af2b2f1b6caa0 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 4 Jun 2025 12:24:43 +0930 Subject: [PATCH 1887/2760] hw/riscv/virt: Use setprop_sized_cells for virtio The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Joel Stanley Message-ID: <20250604025450.85327-8-joel@jms.id.au> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 67e60eec1f..851c7cc82a 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -856,9 +856,7 @@ static void create_fdt_virtio(RISCVVirtState *s, uint32_t irq_virtio_phandle) qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "virtio,mmio"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, addr, - 0x0, size); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", 2, addr, 2, size); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_virtio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { From 08454fc3f5e43338c2e13dc040a2ebe77834945f Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 4 Jun 2025 12:24:44 +0930 Subject: [PATCH 1888/2760] hw/riscv/virt: Use setprop_sized_cells for reset The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Joel Stanley Message-ID: <20250604025450.85327-9-joel@jms.id.au> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 851c7cc82a..b59f10dabe 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -932,8 +932,9 @@ static void create_fdt_reset(RISCVVirtState *s, uint32_t *phandle) qemu_fdt_setprop_string_array(ms->fdt, name, "compatible", (char **)&compat, ARRAY_SIZE(compat)); } - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, s->memmap[VIRT_TEST].base, 0x0, s->memmap[VIRT_TEST].size); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", + 2, s->memmap[VIRT_TEST].base, + 2, s->memmap[VIRT_TEST].size); qemu_fdt_setprop_cell(ms->fdt, name, "phandle", test_phandle); test_phandle = qemu_fdt_get_phandle(ms->fdt, name); g_free(name); From 4f1572d6f191f4618b30be3fda7344461910a03f Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 4 Jun 2025 12:24:45 +0930 Subject: [PATCH 1889/2760] hw/riscv/virt: Use setprop_sized_cells for uart The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Joel Stanley Message-ID: <20250604025450.85327-10-joel@jms.id.au> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index b59f10dabe..7c38a90480 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -966,9 +966,9 @@ static void create_fdt_uart(RISCVVirtState *s, s->memmap[VIRT_UART0].base); qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "ns16550a"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, s->memmap[VIRT_UART0].base, - 0x0, s->memmap[VIRT_UART0].size); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", + 2, s->memmap[VIRT_UART0].base, + 2, s->memmap[VIRT_UART0].size); qemu_fdt_setprop_cell(ms->fdt, name, "clock-frequency", 3686400); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_mmio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { From faa991f67888ae25744f535ad73043900729c024 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 4 Jun 2025 12:24:46 +0930 Subject: [PATCH 1890/2760] hw/riscv/virt: Use setprop_sized_cells for rtc The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Joel Stanley Message-ID: <20250604025450.85327-11-joel@jms.id.au> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 7c38a90480..4fa2bad248 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -992,8 +992,9 @@ static void create_fdt_rtc(RISCVVirtState *s, qemu_fdt_add_subnode(ms->fdt, name); qemu_fdt_setprop_string(ms->fdt, name, "compatible", "google,goldfish-rtc"); - qemu_fdt_setprop_cells(ms->fdt, name, "reg", - 0x0, s->memmap[VIRT_RTC].base, 0x0, s->memmap[VIRT_RTC].size); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", + 2, s->memmap[VIRT_RTC].base, + 2, s->memmap[VIRT_RTC].size); qemu_fdt_setprop_cell(ms->fdt, name, "interrupt-parent", irq_mmio_phandle); if (s->aia_type == VIRT_AIA_TYPE_NONE) { From 0e7e0ee63939a09b098279a475698d73243ffc76 Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 4 Jun 2025 12:24:47 +0930 Subject: [PATCH 1891/2760] hw/riscv/virt: Use setprop_sized_cells for iommu The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Joel Stanley Message-ID: <20250604025450.85327-12-joel@jms.id.au> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 4fa2bad248..67490c5c69 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -1088,8 +1088,7 @@ static void create_fdt_iommu_sys(RISCVVirtState *s, uint32_t irq_chip, qemu_fdt_setprop_cell(fdt, iommu_node, "#iommu-cells", 1); qemu_fdt_setprop_cell(fdt, iommu_node, "phandle", iommu_phandle); - qemu_fdt_setprop_cells(fdt, iommu_node, "reg", - addr >> 32, addr, size >> 32, size); + qemu_fdt_setprop_sized_cells(fdt, iommu_node, "reg", 2, addr, 2, size); qemu_fdt_setprop_cell(fdt, iommu_node, "interrupt-parent", irq_chip); qemu_fdt_setprop_cells(fdt, iommu_node, "interrupts", From 2454fc95ece3c73f649e21621775bcbe859d28ec Mon Sep 17 00:00:00 2001 From: Joel Stanley Date: Wed, 4 Jun 2025 12:24:48 +0930 Subject: [PATCH 1892/2760] hw/riscv/virt: Use setprop_sized_cells for pcie The current device tree property uses two cells for the address (and for the size), but assumes the they are less than 32 bits by hard coding the high cell to zero. Use qemu_fdt_setprop_sized_cells to do the job of splitting the upper and lower 32 bits across cells. Reviewed-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Signed-off-by: Joel Stanley Message-ID: <20250604025450.85327-13-joel@jms.id.au> Signed-off-by: Alistair Francis --- hw/riscv/virt.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c index 67490c5c69..47e573f85a 100644 --- a/hw/riscv/virt.c +++ b/hw/riscv/virt.c @@ -894,8 +894,8 @@ static void create_fdt_pcie(RISCVVirtState *s, if (s->aia_type == VIRT_AIA_TYPE_APLIC_IMSIC) { qemu_fdt_setprop_cell(ms->fdt, name, "msi-parent", msi_pcie_phandle); } - qemu_fdt_setprop_cells(ms->fdt, name, "reg", 0, - s->memmap[VIRT_PCIE_ECAM].base, 0, s->memmap[VIRT_PCIE_ECAM].size); + qemu_fdt_setprop_sized_cells(ms->fdt, name, "reg", 2, + s->memmap[VIRT_PCIE_ECAM].base, 2, s->memmap[VIRT_PCIE_ECAM].size); qemu_fdt_setprop_sized_cells(ms->fdt, name, "ranges", 1, FDT_PCI_RANGE_IOPORT, 2, 0, 2, s->memmap[VIRT_PCIE_PIO].base, 2, s->memmap[VIRT_PCIE_PIO].size, From 60aab7ad11e6cd8a82420ed6a18416853c0fb762 Mon Sep 17 00:00:00 2001 From: Huang Borong <3543977024@qq.com> Date: Fri, 25 Apr 2025 20:22:12 +0800 Subject: [PATCH 1893/2760] target/riscv: Add BOSC's Xiangshan Kunminghu CPU Add a CPU entry for the Xiangshan Kunminghu CPU, an open-source, high-performance RISC-V processor. More details can be found at: https://github.com/OpenXiangShan/XiangShan Note: The ISA extensions supported by the Xiangshan Kunminghu CPU are categorized based on four RISC-V specifications: Volume I: Unprivileged Architecture, Volume II: Privileged Architecture, AIA, and RVA23. The extensions within each category are organized according to the chapter order in the specifications. Signed-off-by: Yu Hu Signed-off-by: Ran Wang Signed-off-by: Borong Huang <3543977024@qq.com> Reviewed-by: Daniel Henrique Barboza Acked-by: Alistair Francis Message-ID: <20250425122212.364-1-wangran@bosc.ac.cn> Signed-off-by: Alistair Francis --- target/riscv/cpu-qom.h | 1 + target/riscv/cpu.c | 58 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/target/riscv/cpu-qom.h b/target/riscv/cpu-qom.h index 1ee05eb393..75f4e43408 100644 --- a/target/riscv/cpu-qom.h +++ b/target/riscv/cpu-qom.h @@ -55,6 +55,7 @@ #define TYPE_RISCV_CPU_VEYRON_V1 RISCV_CPU_TYPE_NAME("veyron-v1") #define TYPE_RISCV_CPU_TT_ASCALON RISCV_CPU_TYPE_NAME("tt-ascalon") #define TYPE_RISCV_CPU_XIANGSHAN_NANHU RISCV_CPU_TYPE_NAME("xiangshan-nanhu") +#define TYPE_RISCV_CPU_XIANGSHAN_KMH RISCV_CPU_TYPE_NAME("xiangshan-kunminghu") #define TYPE_RISCV_CPU_HOST RISCV_CPU_TYPE_NAME("host") OBJECT_DECLARE_CPU_TYPE(RISCVCPU, RISCVCPUClass, RISCV_CPU) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 758f254c15..e3f8ecef68 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -3206,6 +3206,64 @@ static const TypeInfo riscv_cpu_type_infos[] = { .cfg.max_satp_mode = VM_1_10_SV39, ), + DEFINE_RISCV_CPU(TYPE_RISCV_CPU_XIANGSHAN_KMH, TYPE_RISCV_VENDOR_CPU, + .misa_mxl_max = MXL_RV64, + .misa_ext = RVG | RVC | RVB | RVS | RVU | RVH | RVV, + .priv_spec = PRIV_VERSION_1_13_0, + /* + * The RISC-V Instruction Set Manual: Volume I + * Unprivileged Architecture + */ + .cfg.ext_zicntr = true, + .cfg.ext_zihpm = true, + .cfg.ext_zihintntl = true, + .cfg.ext_zihintpause = true, + .cfg.ext_zimop = true, + .cfg.ext_zcmop = true, + .cfg.ext_zicond = true, + .cfg.ext_zawrs = true, + .cfg.ext_zacas = true, + .cfg.ext_zfh = true, + .cfg.ext_zfa = true, + .cfg.ext_zcb = true, + .cfg.ext_zbc = true, + .cfg.ext_zvfh = true, + .cfg.ext_zkn = true, + .cfg.ext_zks = true, + .cfg.ext_zkt = true, + .cfg.ext_zvbb = true, + .cfg.ext_zvkt = true, + /* + * The RISC-V Instruction Set Manual: Volume II + * Privileged Architecture + */ + .cfg.ext_smstateen = true, + .cfg.ext_smcsrind = true, + .cfg.ext_sscsrind = true, + .cfg.ext_svnapot = true, + .cfg.ext_svpbmt = true, + .cfg.ext_svinval = true, + .cfg.ext_sstc = true, + .cfg.ext_sscofpmf = true, + .cfg.ext_ssdbltrp = true, + .cfg.ext_ssnpm = true, + .cfg.ext_smnpm = true, + .cfg.ext_smmpm = true, + .cfg.ext_sspm = true, + .cfg.ext_supm = true, + /* The RISC-V Advanced Interrupt Architecture */ + .cfg.ext_smaia = true, + .cfg.ext_ssaia = true, + /* RVA23 Profiles */ + .cfg.ext_zicbom = true, + .cfg.ext_zicbop = true, + .cfg.ext_zicboz = true, + .cfg.ext_svade = true, + .cfg.mmu = true, + .cfg.pmp = true, + .cfg.max_satp_mode = VM_1_10_SV48, + ), + #if defined(CONFIG_TCG) && !defined(CONFIG_USER_ONLY) DEFINE_RISCV_CPU(TYPE_RISCV_CPU_BASE128, TYPE_RISCV_DYNAMIC_CPU, .cfg.max_satp_mode = VM_1_10_SV57, From 29abd3d112c380b8019a77ebad65f61addfb812d Mon Sep 17 00:00:00 2001 From: Huang Borong <3543977024@qq.com> Date: Tue, 17 Jun 2025 15:42:22 +0800 Subject: [PATCH 1894/2760] hw/riscv: Initial support for BOSC's Xiangshan Kunminghu FPGA prototype This implementation provides emulation for the Xiangshan Kunminghu FPGA prototype platform, including support for UART, CLINT, IMSIC, and APLIC devices. More details can be found at https://github.com/OpenXiangShan/XiangShan Signed-off-by: qinshaoqing Signed-off-by: Yang Wang Signed-off-by: Yu Hu <819258943@qq.com> Signed-off-by: Ran Wang Signed-off-by: Borong Huang <3543977024@qq.com> Reviewed-by: Daniel Henrique Barboza Message-ID: <20250617074222.17618-1-wangran@bosc.ac.cn> Signed-off-by: Alistair Francis --- MAINTAINERS | 7 + configs/devices/riscv64-softmmu/default.mak | 1 + docs/system/riscv/xiangshan-kunminghu.rst | 39 ++++ docs/system/target-riscv.rst | 1 + hw/riscv/Kconfig | 9 + hw/riscv/meson.build | 1 + hw/riscv/xiangshan_kmh.c | 220 ++++++++++++++++++++ include/hw/riscv/xiangshan_kmh.h | 68 ++++++ 8 files changed, 346 insertions(+) create mode 100644 docs/system/riscv/xiangshan-kunminghu.rst create mode 100644 hw/riscv/xiangshan_kmh.c create mode 100644 include/hw/riscv/xiangshan_kmh.h diff --git a/MAINTAINERS b/MAINTAINERS index b1cbfe115b..12efc88aa7 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1697,6 +1697,13 @@ S: Maintained F: hw/riscv/microblaze-v-generic.c F: docs/system/riscv/microblaze-v-generic.rst +Xiangshan Kunminghu +M: Ran Wang +S: Maintained +F: docs/system/riscv/xiangshan-kunminghu.rst +F: hw/riscv/xiangshan_kmh.c +F: include/hw/riscv/xiangshan_kmh.h + RX Machines ----------- rx-gdbsim diff --git a/configs/devices/riscv64-softmmu/default.mak b/configs/devices/riscv64-softmmu/default.mak index 39ed3a0061..e485bbd1a3 100644 --- a/configs/devices/riscv64-softmmu/default.mak +++ b/configs/devices/riscv64-softmmu/default.mak @@ -11,3 +11,4 @@ # CONFIG_RISCV_VIRT=n # CONFIG_MICROCHIP_PFSOC=n # CONFIG_SHAKTI_C=n +# CONFIG_XIANGSHAN_KUNMINGHU=n diff --git a/docs/system/riscv/xiangshan-kunminghu.rst b/docs/system/riscv/xiangshan-kunminghu.rst new file mode 100644 index 0000000000..46e7ceeda0 --- /dev/null +++ b/docs/system/riscv/xiangshan-kunminghu.rst @@ -0,0 +1,39 @@ +BOSC Xiangshan Kunminghu FPGA prototype platform (``xiangshan-kunminghu``) +========================================================================== +The ``xiangshan-kunminghu`` machine is compatible with our FPGA prototype +platform. + +XiangShan is an open-source high-performance RISC-V processor project. +The third generation processor is called Kunminghu. Kunminghu is a 64-bit +RV64GCBSUHV processor core. More information can be found in our Github +repository: +https://github.com/OpenXiangShan/XiangShan + +Supported devices +----------------- +The ``xiangshan-kunminghu`` machine supports the following devices: + +* Up to 16 xiangshan-kunminghu cores +* Core Local Interruptor (CLINT) +* Incoming MSI Controller (IMSIC) +* Advanced Platform-Level Interrupt Controller (APLIC) +* 1 UART + +Boot options +------------ +The ``xiangshan-kunminghu`` machine can start using the standard ``-bios`` +functionality for loading the boot image. You need to compile and link +the firmware, kernel, and Device Tree (FDT) into a single binary file, +such as ``fw_payload.bin``. + +Running +------- +Below is an example command line for running the ``xiangshan-kunminghu`` +machine: + +.. code-block:: bash + + $ qemu-system-riscv64 -machine xiangshan-kunminghu \ + -smp 16 -m 16G \ + -bios path/to/opensbi/platform/generic/firmware/fw_payload.bin \ + -nographic diff --git a/docs/system/target-riscv.rst b/docs/system/target-riscv.rst index 95457af130..89b2cb732c 100644 --- a/docs/system/target-riscv.rst +++ b/docs/system/target-riscv.rst @@ -71,6 +71,7 @@ undocumented; you can get a complete list by running riscv/shakti-c riscv/sifive_u riscv/virt + riscv/xiangshan-kunminghu RISC-V CPU firmware ------------------- diff --git a/hw/riscv/Kconfig b/hw/riscv/Kconfig index e6a0ac1fa1..fc9c35bd98 100644 --- a/hw/riscv/Kconfig +++ b/hw/riscv/Kconfig @@ -119,3 +119,12 @@ config SPIKE select HTIF select RISCV_ACLINT select SIFIVE_PLIC + +config XIANGSHAN_KUNMINGHU + bool + default y + depends on RISCV64 + select RISCV_ACLINT + select RISCV_APLIC + select RISCV_IMSIC + select SERIAL_MM diff --git a/hw/riscv/meson.build b/hw/riscv/meson.build index c22f3a7216..2a8d5b136c 100644 --- a/hw/riscv/meson.build +++ b/hw/riscv/meson.build @@ -13,5 +13,6 @@ riscv_ss.add(when: 'CONFIG_ACPI', if_true: files('virt-acpi-build.c')) riscv_ss.add(when: 'CONFIG_RISCV_IOMMU', if_true: files( 'riscv-iommu.c', 'riscv-iommu-pci.c', 'riscv-iommu-sys.c', 'riscv-iommu-hpm.c')) riscv_ss.add(when: 'CONFIG_MICROBLAZE_V', if_true: files('microblaze-v-generic.c')) +riscv_ss.add(when: 'CONFIG_XIANGSHAN_KUNMINGHU', if_true: files('xiangshan_kmh.c')) hw_arch += {'riscv': riscv_ss} diff --git a/hw/riscv/xiangshan_kmh.c b/hw/riscv/xiangshan_kmh.c new file mode 100644 index 0000000000..a95fd6174f --- /dev/null +++ b/hw/riscv/xiangshan_kmh.c @@ -0,0 +1,220 @@ +/* + * QEMU RISC-V Board Compatible with the Xiangshan Kunminghu + * FPGA prototype platform + * + * Copyright (c) 2025 Beijing Institute of Open Source Chip (BOSC) + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Provides a board compatible with the Xiangshan Kunminghu + * FPGA prototype platform: + * + * 0) UART (16550A) + * 1) CLINT (Core-Local Interruptor) + * 2) IMSIC (Incoming MSI Controller) + * 3) APLIC (Advanced Platform-Level Interrupt Controller) + * + * More information can be found in our Github repository: + * https://github.com/OpenXiangShan/XiangShan + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2 or later, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program. If not, see . + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "system/address-spaces.h" +#include "hw/boards.h" +#include "hw/char/serial-mm.h" +#include "hw/intc/riscv_aclint.h" +#include "hw/intc/riscv_aplic.h" +#include "hw/intc/riscv_imsic.h" +#include "hw/qdev-properties.h" +#include "hw/riscv/boot.h" +#include "hw/riscv/xiangshan_kmh.h" +#include "hw/riscv/riscv_hart.h" +#include "system/system.h" + +static const MemMapEntry xiangshan_kmh_memmap[] = { + [XIANGSHAN_KMH_ROM] = { 0x1000, 0xF000 }, + [XIANGSHAN_KMH_UART0] = { 0x310B0000, 0x10000 }, + [XIANGSHAN_KMH_CLINT] = { 0x38000000, 0x10000 }, + [XIANGSHAN_KMH_APLIC_M] = { 0x31100000, 0x4000 }, + [XIANGSHAN_KMH_APLIC_S] = { 0x31120000, 0x4000 }, + [XIANGSHAN_KMH_IMSIC_M] = { 0x3A800000, 0x10000 }, + [XIANGSHAN_KMH_IMSIC_S] = { 0x3B000000, 0x80000 }, + [XIANGSHAN_KMH_DRAM] = { 0x80000000, 0x0 }, +}; + +static DeviceState *xiangshan_kmh_create_aia(uint32_t num_harts) +{ + int i; + const MemMapEntry *memmap = xiangshan_kmh_memmap; + hwaddr addr = 0; + DeviceState *aplic_m = NULL; + + /* M-level IMSICs */ + addr = memmap[XIANGSHAN_KMH_IMSIC_M].base; + for (i = 0; i < num_harts; i++) { + riscv_imsic_create(addr + i * IMSIC_HART_SIZE(0), i, true, + 1, XIANGSHAN_KMH_IMSIC_NUM_IDS); + } + + /* S-level IMSICs */ + addr = memmap[XIANGSHAN_KMH_IMSIC_S].base; + for (i = 0; i < num_harts; i++) { + riscv_imsic_create(addr + + i * IMSIC_HART_SIZE(XIANGSHAN_KMH_IMSIC_GUEST_BITS), + i, false, 1 + XIANGSHAN_KMH_IMSIC_GUEST_BITS, + XIANGSHAN_KMH_IMSIC_NUM_IDS); + } + + /* M-level APLIC */ + aplic_m = riscv_aplic_create(memmap[XIANGSHAN_KMH_APLIC_M].base, + memmap[XIANGSHAN_KMH_APLIC_M].size, + 0, 0, XIANGSHAN_KMH_APLIC_NUM_SOURCES, + 1, true, true, NULL); + + /* S-level APLIC */ + riscv_aplic_create(memmap[XIANGSHAN_KMH_APLIC_S].base, + memmap[XIANGSHAN_KMH_APLIC_S].size, + 0, 0, XIANGSHAN_KMH_APLIC_NUM_SOURCES, + 1, true, false, aplic_m); + + return aplic_m; +} + +static void xiangshan_kmh_soc_realize(DeviceState *dev, Error **errp) +{ + MachineState *ms = MACHINE(qdev_get_machine()); + XiangshanKmhSoCState *s = XIANGSHAN_KMH_SOC(dev); + const MemMapEntry *memmap = xiangshan_kmh_memmap; + MemoryRegion *system_memory = get_system_memory(); + uint32_t num_harts = ms->smp.cpus; + + qdev_prop_set_uint32(DEVICE(&s->cpus), "num-harts", num_harts); + qdev_prop_set_uint32(DEVICE(&s->cpus), "hartid-base", 0); + qdev_prop_set_string(DEVICE(&s->cpus), "cpu-type", + TYPE_RISCV_CPU_XIANGSHAN_KMH); + sysbus_realize(SYS_BUS_DEVICE(&s->cpus), &error_fatal); + + /* AIA */ + s->irqchip = xiangshan_kmh_create_aia(num_harts); + + /* UART */ + serial_mm_init(system_memory, memmap[XIANGSHAN_KMH_UART0].base, 2, + qdev_get_gpio_in(s->irqchip, XIANGSHAN_KMH_UART0_IRQ), + 115200, serial_hd(0), DEVICE_LITTLE_ENDIAN); + + /* CLINT */ + riscv_aclint_swi_create(memmap[XIANGSHAN_KMH_CLINT].base, + 0, num_harts, false); + riscv_aclint_mtimer_create(memmap[XIANGSHAN_KMH_CLINT].base + + RISCV_ACLINT_SWI_SIZE, + RISCV_ACLINT_DEFAULT_MTIMER_SIZE, + 0, num_harts, RISCV_ACLINT_DEFAULT_MTIMECMP, + RISCV_ACLINT_DEFAULT_MTIME, + XIANGSHAN_KMH_CLINT_TIMEBASE_FREQ, true); + + /* ROM */ + memory_region_init_rom(&s->rom, OBJECT(dev), "xiangshan.kunminghu.rom", + memmap[XIANGSHAN_KMH_ROM].size, &error_fatal); + memory_region_add_subregion(system_memory, + memmap[XIANGSHAN_KMH_ROM].base, &s->rom); +} + +static void xiangshan_kmh_soc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = xiangshan_kmh_soc_realize; + dc->user_creatable = false; +} + +static void xiangshan_kmh_soc_instance_init(Object *obj) +{ + XiangshanKmhSoCState *s = XIANGSHAN_KMH_SOC(obj); + + object_initialize_child(obj, "cpus", &s->cpus, TYPE_RISCV_HART_ARRAY); +} + +static const TypeInfo xiangshan_kmh_soc_info = { + .name = TYPE_XIANGSHAN_KMH_SOC, + .parent = TYPE_DEVICE, + .instance_size = sizeof(XiangshanKmhSoCState), + .instance_init = xiangshan_kmh_soc_instance_init, + .class_init = xiangshan_kmh_soc_class_init, +}; + +static void xiangshan_kmh_soc_register_types(void) +{ + type_register_static(&xiangshan_kmh_soc_info); +} +type_init(xiangshan_kmh_soc_register_types) + +static void xiangshan_kmh_machine_init(MachineState *machine) +{ + XiangshanKmhState *s = XIANGSHAN_KMH_MACHINE(machine); + const MemMapEntry *memmap = xiangshan_kmh_memmap; + MemoryRegion *system_memory = get_system_memory(); + hwaddr start_addr = memmap[XIANGSHAN_KMH_DRAM].base; + + /* Initialize SoC */ + object_initialize_child(OBJECT(machine), "soc", &s->soc, + TYPE_XIANGSHAN_KMH_SOC); + qdev_realize(DEVICE(&s->soc), NULL, &error_fatal); + + /* Register RAM */ + memory_region_add_subregion(system_memory, + memmap[XIANGSHAN_KMH_DRAM].base, + machine->ram); + + /* ROM reset vector */ + riscv_setup_rom_reset_vec(machine, &s->soc.cpus, + start_addr, + memmap[XIANGSHAN_KMH_ROM].base, + memmap[XIANGSHAN_KMH_ROM].size, 0, 0); + if (machine->firmware) { + riscv_load_firmware(machine->firmware, &start_addr, NULL); + } + + /* Note: dtb has been integrated into firmware(OpenSBI) when compiling */ +} + +static void xiangshan_kmh_machine_class_init(ObjectClass *klass, const void *data) +{ + MachineClass *mc = MACHINE_CLASS(klass); + static const char *const valid_cpu_types[] = { + TYPE_RISCV_CPU_XIANGSHAN_KMH, + NULL + }; + + mc->desc = "RISC-V Board compatible with the Xiangshan " \ + "Kunminghu FPGA prototype platform"; + mc->init = xiangshan_kmh_machine_init; + mc->max_cpus = XIANGSHAN_KMH_MAX_CPUS; + mc->default_cpu_type = TYPE_RISCV_CPU_XIANGSHAN_KMH; + mc->valid_cpu_types = valid_cpu_types; + mc->default_ram_id = "xiangshan.kunminghu.ram"; +} + +static const TypeInfo xiangshan_kmh_machine_info = { + .name = TYPE_XIANGSHAN_KMH_MACHINE, + .parent = TYPE_MACHINE, + .instance_size = sizeof(XiangshanKmhState), + .class_init = xiangshan_kmh_machine_class_init, +}; + +static void xiangshan_kmh_machine_register_types(void) +{ + type_register_static(&xiangshan_kmh_machine_info); +} +type_init(xiangshan_kmh_machine_register_types) diff --git a/include/hw/riscv/xiangshan_kmh.h b/include/hw/riscv/xiangshan_kmh.h new file mode 100644 index 0000000000..c5dc6b1a9a --- /dev/null +++ b/include/hw/riscv/xiangshan_kmh.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: BSD-2-Clause */ +/* + * QEMU RISC-V Board Compatible with the Xiangshan Kunminghu + * FPGA prototype platform + * + * Copyright (c) 2025 Beijing Institute of Open Source Chip (BOSC) + * + */ + +#ifndef HW_XIANGSHAN_KMH_H +#define HW_XIANGSHAN_KMH_H + +#include "hw/boards.h" +#include "hw/riscv/riscv_hart.h" + +#define XIANGSHAN_KMH_MAX_CPUS 16 + +typedef struct XiangshanKmhSoCState { + /*< private >*/ + DeviceState parent_obj; + + /*< public >*/ + RISCVHartArrayState cpus; + DeviceState *irqchip; + MemoryRegion rom; +} XiangshanKmhSoCState; + +#define TYPE_XIANGSHAN_KMH_SOC "xiangshan.kunminghu.soc" +DECLARE_INSTANCE_CHECKER(XiangshanKmhSoCState, XIANGSHAN_KMH_SOC, + TYPE_XIANGSHAN_KMH_SOC) + +typedef struct XiangshanKmhState { + /*< private >*/ + MachineState parent_obj; + + /*< public >*/ + XiangshanKmhSoCState soc; +} XiangshanKmhState; + +#define TYPE_XIANGSHAN_KMH_MACHINE MACHINE_TYPE_NAME("xiangshan-kunminghu") +DECLARE_INSTANCE_CHECKER(XiangshanKmhState, XIANGSHAN_KMH_MACHINE, + TYPE_XIANGSHAN_KMH_MACHINE) + +enum { + XIANGSHAN_KMH_ROM, + XIANGSHAN_KMH_UART0, + XIANGSHAN_KMH_CLINT, + XIANGSHAN_KMH_APLIC_M, + XIANGSHAN_KMH_APLIC_S, + XIANGSHAN_KMH_IMSIC_M, + XIANGSHAN_KMH_IMSIC_S, + XIANGSHAN_KMH_DRAM, +}; + +enum { + XIANGSHAN_KMH_UART0_IRQ = 10, +}; + +/* Indicating Timebase-freq (1MHZ) */ +#define XIANGSHAN_KMH_CLINT_TIMEBASE_FREQ 1000000 + +#define XIANGSHAN_KMH_IMSIC_NUM_IDS 255 +#define XIANGSHAN_KMH_IMSIC_NUM_GUESTS 7 +#define XIANGSHAN_KMH_IMSIC_GUEST_BITS 3 + +#define XIANGSHAN_KMH_APLIC_NUM_SOURCES 96 + +#endif From b5092b3db23391d6ee770123715b76b85169d977 Mon Sep 17 00:00:00 2001 From: Max Chou Date: Fri, 27 Jun 2025 21:30:13 +0800 Subject: [PATCH 1895/2760] target/riscv: rvv: Fix missing exit TB flow for ldff_trans According to the V spec, the vector fault-only-first load instructions may change the VL CSR. So the ldff_trans TCG translation function should generate the lookup_and_goto_ptr flow as the vsetvl/vsetvli translation function to make sure the vl_eq_vlmax TB flag is correct. Signed-off-by: Max Chou Reviewed-by: Richard Henderson Message-ID: <20250627133013.443997-1-max.chou@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 2b6077ac06..4cd030c7eb 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -1361,6 +1361,12 @@ static bool ldff_trans(uint32_t vd, uint32_t rs1, uint32_t data, fn(dest, mask, base, tcg_env, desc); finalize_rvv_inst(s); + + /* vector unit-stride fault-only-first load may modify vl CSR */ + gen_update_pc(s, s->cur_insn_len); + lookup_and_goto_ptr(s); + s->base.is_jmp = DISAS_NORETURN; + return true; } From bc2200134c1229a83bbcd8e75ab541ca110609f6 Mon Sep 17 00:00:00 2001 From: "liu.xuemei1@zte.com.cn" Date: Mon, 16 Jun 2025 15:00:34 +0800 Subject: [PATCH 1896/2760] migration: Fix migration failure when aia is configured as aplic-imsic Address an error in migration when aia is configured as 'aplic-imsic' in riscv kvm vm by adding riscv_aplic_state_needed() and riscv_imsic_state_needed() to determine whether the corresponding sates are needed. Previously, the fields in the vmsds of 'riscv_aplic' and 'riscv_imsic' can only be initialized under certain special conditions in commit 95a97b3fd2. However, the corresponding ses of these vmsds are inserted into the savevm_state.handlers unconditionally. This led to migration failure characterized by uninitialized fields when save vm state: qemu-system-riscv64: ../migration/vmstate.c:433: vmstate_save_state_v: Assertion 'first_elem || !n_elems || !size' failed. Fixes: 95a97b3fd2 ("target/riscv: update APLIC and IMSIC to support KVM AIA") Signed-off-by: Xuemei Liu Reviewed-by: Alistair Francis Message-ID: <20250616150034827wuHs_ffe3Qm8cqFXT7HeW@zte.com.cn> Signed-off-by: Alistair Francis --- hw/intc/riscv_aplic.c | 12 ++++++++++-- hw/intc/riscv_imsic.c | 10 ++++++++-- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 8bcd9f4697..4fa5f7597b 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -962,10 +962,18 @@ static const Property riscv_aplic_properties[] = { DEFINE_PROP_BOOL("mmode", RISCVAPLICState, mmode, 0), }; +static bool riscv_aplic_state_needed(void *opaque) +{ + RISCVAPLICState *aplic = opaque; + + return riscv_use_emulated_aplic(aplic->msimode); +} + static const VMStateDescription vmstate_riscv_aplic = { .name = "riscv_aplic", - .version_id = 2, - .minimum_version_id = 2, + .version_id = 3, + .minimum_version_id = 3, + .needed = riscv_aplic_state_needed, .fields = (const VMStateField[]) { VMSTATE_UINT32(domaincfg, RISCVAPLICState), VMSTATE_UINT32(mmsicfgaddr, RISCVAPLICState), diff --git a/hw/intc/riscv_imsic.c b/hw/intc/riscv_imsic.c index 2169988167..6174e1a05d 100644 --- a/hw/intc/riscv_imsic.c +++ b/hw/intc/riscv_imsic.c @@ -398,10 +398,16 @@ static const Property riscv_imsic_properties[] = { DEFINE_PROP_UINT32("num-irqs", RISCVIMSICState, num_irqs, 0), }; +static bool riscv_imsic_state_needed(void *opaque) +{ + return !kvm_irqchip_in_kernel(); +} + static const VMStateDescription vmstate_riscv_imsic = { .name = "riscv_imsic", - .version_id = 1, - .minimum_version_id = 1, + .version_id = 2, + .minimum_version_id = 2, + .needed = riscv_imsic_state_needed, .fields = (const VMStateField[]) { VMSTATE_VARRAY_UINT32(eidelivery, RISCVIMSICState, num_pages, 0, From b3452452e64be647fef98d2dce16c3f5c149235a Mon Sep 17 00:00:00 2001 From: Charalampos Mitrodimas Date: Thu, 3 Jul 2025 18:21:43 +0000 Subject: [PATCH 1897/2760] target/riscv: Fix MEPC/SEPC bit masking for IALIGN According to the RISC-V Privileged Architecture specification, the low bit of MEPC/SEPC must always be zero. When IALIGN=32, the two low bits must be zero. This commit fixes the behavior of MEPC/SEPC CSR reads and writes, and the implicit reads by MRET/SRET instructions to properly mask the lowest bit(s) based on whether the C extension is enabled: - When C extension is enabled (IALIGN=16): mask bit 0 - When C extension is disabled (IALIGN=32): mask bits [1:0] Previously, when vectored mode bits from STVEC (which sets bit 0 for vectored mode) were written to MEPC, the bits would not be cleared correctly, causing incorrect behavior on MRET. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2855 Signed-off-by: Charalampos Mitrodimas Reviewed-by: Alistair Francis Reviewed-by: Daniel Henrique Barboza Message-ID: <20250703182157.281320-2-charmitro@posteo.net> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 8 ++++---- target/riscv/internals.h | 11 +++++++++++ target/riscv/op_helper.c | 4 ++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 6296ecd1e1..8631be97c5 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -3129,14 +3129,14 @@ static RISCVException write_mscratch(CPURISCVState *env, int csrno, static RISCVException read_mepc(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->mepc; + *val = env->mepc & get_xepc_mask(env); return RISCV_EXCP_NONE; } static RISCVException write_mepc(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { - env->mepc = val; + env->mepc = val & get_xepc_mask(env); return RISCV_EXCP_NONE; } @@ -4169,14 +4169,14 @@ static RISCVException write_sscratch(CPURISCVState *env, int csrno, static RISCVException read_sepc(CPURISCVState *env, int csrno, target_ulong *val) { - *val = env->sepc; + *val = env->sepc & get_xepc_mask(env); return RISCV_EXCP_NONE; } static RISCVException write_sepc(CPURISCVState *env, int csrno, target_ulong val, uintptr_t ra) { - env->sepc = val; + env->sepc = val & get_xepc_mask(env); return RISCV_EXCP_NONE; } diff --git a/target/riscv/internals.h b/target/riscv/internals.h index 9686bb6208..172296f12e 100644 --- a/target/riscv/internals.h +++ b/target/riscv/internals.h @@ -158,6 +158,17 @@ static inline float16 check_nanbox_bf16(CPURISCVState *env, uint64_t f) } } +static inline target_ulong get_xepc_mask(CPURISCVState *env) +{ + /* When IALIGN=32, both low bits must be zero. + * When IALIGN=16 (has C extension), only bit 0 must be zero. */ + if (riscv_has_ext(env, RVC)) { + return ~(target_ulong)1; + } else { + return ~(target_ulong)3; + } +} + #ifndef CONFIG_USER_ONLY /* Our implementation of SysemuCPUOps::has_work */ bool riscv_cpu_has_work(CPUState *cs); diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 557807ba4b..15460bf84b 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -280,7 +280,7 @@ target_ulong helper_sret(CPURISCVState *env) riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); } - target_ulong retpc = env->sepc; + target_ulong retpc = env->sepc & get_xepc_mask(env); if (!riscv_cpu_allow_16bit_insn(&env_archcpu(env)->cfg, env->priv_ver, env->misa_ext) && (retpc & 0x3)) { @@ -391,7 +391,7 @@ static target_ulong ssdbltrp_mxret(CPURISCVState *env, target_ulong mstatus, target_ulong helper_mret(CPURISCVState *env) { - target_ulong retpc = env->mepc; + target_ulong retpc = env->mepc & get_xepc_mask(env); uint64_t mstatus = env->mstatus; target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); From a1f44e0c59081afceb5e2b389b6de96602f73977 Mon Sep 17 00:00:00 2001 From: Charalampos Mitrodimas Date: Thu, 3 Jul 2025 18:21:44 +0000 Subject: [PATCH 1898/2760] tests/tcg/riscv64: Add test for MEPC bit masking Add a regression test to verify that MEPC properly masks the lower bits when an address with mode bits is written to it, as required by the RISC-V Privileged Architecture specification. The test sets STVEC to an address with bit 0 set (vectored mode), triggers an illegal instruction exception, copies STVEC to MEPC in the trap handler, and verifies that MEPC masks bits [1:0] correctly for IALIGN=32. Without the fix, MEPC retains the mode bits (returns non-zero/FAIL). With the fix, MEPC clears bits [1:0] (returns 0/PASS). Signed-off-by: Charalampos Mitrodimas Reviewed-by: Daniel Henrique Barboza Message-ID: <20250703182157.281320-3-charmitro@posteo.net> Signed-off-by: Alistair Francis --- tests/tcg/riscv64/Makefile.softmmu-target | 4 ++ tests/tcg/riscv64/test-mepc-masking.S | 73 +++++++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 tests/tcg/riscv64/test-mepc-masking.S diff --git a/tests/tcg/riscv64/Makefile.softmmu-target b/tests/tcg/riscv64/Makefile.softmmu-target index 7c1d44d3f4..3ca595335d 100644 --- a/tests/tcg/riscv64/Makefile.softmmu-target +++ b/tests/tcg/riscv64/Makefile.softmmu-target @@ -20,5 +20,9 @@ EXTRA_RUNS += run-issue1060 run-issue1060: issue1060 $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<) +EXTRA_RUNS += run-test-mepc-masking +run-test-mepc-masking: test-mepc-masking + $(call run-test, $<, $(QEMU) $(QEMU_OPTS)$<) + # We don't currently support the multiarch system tests undefine MULTIARCH_TESTS diff --git a/tests/tcg/riscv64/test-mepc-masking.S b/tests/tcg/riscv64/test-mepc-masking.S new file mode 100644 index 0000000000..fccd2a7ac4 --- /dev/null +++ b/tests/tcg/riscv64/test-mepc-masking.S @@ -0,0 +1,73 @@ +/* + * Test for MEPC masking bug fix + * + * This test verifies that MEPC properly masks the lower bits according + * to the RISC-V specification when vectored mode bits from STVEC are + * written to MEPC. + */ + + .option norvc + + .text + .global _start +_start: + /* Set up machine trap vector */ + lla t0, machine_trap_handler + csrw mtvec, t0 + + /* Set STVEC with vectored mode (mode bits = 01) */ + li t0, 0x80004001 + csrw stvec, t0 + + /* Clear medeleg to handle exceptions in M-mode */ + csrw medeleg, zero + + /* Trigger illegal instruction exception */ + .word 0xffffffff + +test_completed: + /* Exit with result in a0 */ + /* a0 = 0: success (bits [1:0] were masked) */ + /* a0 != 0: failure (some bits were not masked) */ + j _exit + +machine_trap_handler: + /* Check if illegal instruction (mcause = 2) */ + csrr t0, mcause + li t1, 2 + bne t0, t1, skip_test + + /* Test: Copy STVEC (with mode bits) to MEPC */ + csrr t0, stvec /* t0 = 0x80004001 */ + csrw mepc, t0 /* Write to MEPC */ + csrr t1, mepc /* Read back MEPC */ + + /* Check if bits [1:0] are masked (IALIGN=32 without RVC) */ + andi a0, t1, 3 /* a0 = 0 if both bits masked correctly */ + + /* Set correct return address */ + lla t0, test_completed + csrw mepc, t0 + +skip_test: + mret + +/* Exit with semihosting */ +_exit: + lla a1, semiargs + li t0, 0x20026 /* ADP_Stopped_ApplicationExit */ + sd t0, 0(a1) + sd a0, 8(a1) + li a0, 0x20 /* TARGET_SYS_EXIT_EXTENDED */ + + /* Semihosting call sequence */ + .balign 16 + slli zero, zero, 0x1f + ebreak + srai zero, zero, 0x7 + j . + + .data + .balign 8 +semiargs: + .space 16 From 5625817e8b77715b18d0ce3bfcc59fb337e387d8 Mon Sep 17 00:00:00 2001 From: Vasilis Liaskovitis Date: Wed, 18 Jun 2025 23:35:42 +0200 Subject: [PATCH 1899/2760] target/riscv: Add a property to set vill bit on reserved usage of vsetvli instruction Usage of vsetvli instruction is reserved if VLMAX is changed when vsetvli rs1 and rd arguments are x0. In this case, if the new property is true, only the vill bit will be set. See https://github.com/riscv/riscv-isa-manual/blob/main/src/v-st-ext.adoc#avl-encoding According to the spec, the above use cases are reserved, and "Implementations may set vill in either case." Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2422 Signed-off-by: Vasilis Liaskovitis Reviewed-by: Daniel Henrique Barboza Message-ID: <20250618213542.22873-1-vliaskovitis@suse.com> Signed-off-by: Alistair Francis --- target/riscv/cpu.c | 1 + target/riscv/cpu_cfg_fields.h.inc | 1 + target/riscv/helper.h | 2 +- target/riscv/insn_trans/trans_rvv.c.inc | 4 ++-- target/riscv/vector_helper.c | 12 +++++++++++- 5 files changed, 16 insertions(+), 4 deletions(-) diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index e3f8ecef68..6f1d0618f1 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -2632,6 +2632,7 @@ static const Property riscv_cpu_properties[] = { DEFINE_PROP_BOOL("rvv_ta_all_1s", RISCVCPU, cfg.rvv_ta_all_1s, false), DEFINE_PROP_BOOL("rvv_ma_all_1s", RISCVCPU, cfg.rvv_ma_all_1s, false), DEFINE_PROP_BOOL("rvv_vl_half_avl", RISCVCPU, cfg.rvv_vl_half_avl, false), + DEFINE_PROP_BOOL("rvv_vsetvl_x0_vill", RISCVCPU, cfg.rvv_vsetvl_x0_vill, false), /* * write_misa() is marked as experimental for now so mark diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index 33c4f9bac8..98ceb7b340 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -114,6 +114,7 @@ BOOL_FIELD(ext_supm) BOOL_FIELD(rvv_ta_all_1s) BOOL_FIELD(rvv_ma_all_1s) BOOL_FIELD(rvv_vl_half_avl) +BOOL_FIELD(rvv_vsetvl_x0_vill) /* Named features */ BOOL_FIELD(ext_svade) BOOL_FIELD(ext_zic64b) diff --git a/target/riscv/helper.h b/target/riscv/helper.h index 85d73e492d..f712b1c368 100644 --- a/target/riscv/helper.h +++ b/target/riscv/helper.h @@ -159,7 +159,7 @@ DEF_HELPER_FLAGS_3(hyp_hsv_d, TCG_CALL_NO_WG, void, env, tl, tl) #endif /* Vector functions */ -DEF_HELPER_3(vsetvl, tl, env, tl, tl) +DEF_HELPER_4(vsetvl, tl, env, tl, tl, tl) DEF_HELPER_5(vle8_v, void, ptr, ptr, tl, env, i32) DEF_HELPER_5(vle16_v, void, ptr, ptr, tl, env, i32) DEF_HELPER_5(vle32_v, void, ptr, ptr, tl, env, i32) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 4cd030c7eb..610bf9ff30 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -202,7 +202,7 @@ static bool do_vsetvl(DisasContext *s, int rd, int rs1, TCGv s2) s1 = get_gpr(s, rs1, EXT_ZERO); } - gen_helper_vsetvl(dst, tcg_env, s1, s2); + gen_helper_vsetvl(dst, tcg_env, s1, s2, tcg_constant_tl((int) (rd == 0 && rs1 == 0))); gen_set_gpr(s, rd, dst); finalize_rvv_inst(s); @@ -222,7 +222,7 @@ static bool do_vsetivli(DisasContext *s, int rd, TCGv s1, TCGv s2) dst = dest_gpr(s, rd); - gen_helper_vsetvl(dst, tcg_env, s1, s2); + gen_helper_vsetvl(dst, tcg_env, s1, s2, tcg_constant_tl(0)); gen_set_gpr(s, rd, dst); finalize_rvv_inst(s); gen_update_pc(s, s->cur_insn_len); diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index 5dc1c10012..b41c29da0b 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -35,7 +35,7 @@ #include target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1, - target_ulong s2) + target_ulong s2, target_ulong x0) { int vlmax, vl; RISCVCPU *cpu = env_archcpu(env); @@ -83,6 +83,16 @@ target_ulong HELPER(vsetvl)(CPURISCVState *env, target_ulong s1, } else { vl = vlmax; } + + if (cpu->cfg.rvv_vsetvl_x0_vill && x0 && (env->vl != vl)) { + /* only set vill bit. */ + env->vill = 1; + env->vtype = 0; + env->vl = 0; + env->vstart = 0; + return 0; + } + env->vl = vl; env->vtype = s2; env->vstart = 0; From dc8bffc4eb0a93d3266cea1b17f8848dea5b915c Mon Sep 17 00:00:00 2001 From: Alexandre Ghiti Date: Wed, 2 Jul 2025 07:28:52 +0000 Subject: [PATCH 1900/2760] target: riscv: Add Svrsw60t59b extension support The Svrsw60t59b extension allows to free the PTE reserved bits 60 and 59 for software to use. Reviewed-by: Deepak Gupta Signed-off-by: Alexandre Ghiti Reviewed-by: Daniel Henrique Barboza Reviewed-by: Nutty Liu Message-ID: <20250702-dev-alex-svrsw60b59b_v2-v2-1-504ddf0f8530@rivosinc.com> Signed-off-by: Alistair Francis --- hw/riscv/riscv-iommu-bits.h | 1 + hw/riscv/riscv-iommu.c | 3 ++- target/riscv/cpu.c | 2 ++ target/riscv/cpu_bits.h | 3 ++- target/riscv/cpu_cfg_fields.h.inc | 1 + target/riscv/cpu_helper.c | 3 ++- target/riscv/tcg/tcg-cpu.c | 8 ++++++++ 7 files changed, 18 insertions(+), 3 deletions(-) diff --git a/hw/riscv/riscv-iommu-bits.h b/hw/riscv/riscv-iommu-bits.h index 1017d73fc6..47fe01bee5 100644 --- a/hw/riscv/riscv-iommu-bits.h +++ b/hw/riscv/riscv-iommu-bits.h @@ -79,6 +79,7 @@ struct riscv_iommu_pq_record { #define RISCV_IOMMU_CAP_SV39 BIT_ULL(9) #define RISCV_IOMMU_CAP_SV48 BIT_ULL(10) #define RISCV_IOMMU_CAP_SV57 BIT_ULL(11) +#define RISCV_IOMMU_CAP_SVRSW60T59B BIT_ULL(14) #define RISCV_IOMMU_CAP_SV32X4 BIT_ULL(16) #define RISCV_IOMMU_CAP_SV39X4 BIT_ULL(17) #define RISCV_IOMMU_CAP_SV48X4 BIT_ULL(18) diff --git a/hw/riscv/riscv-iommu.c b/hw/riscv/riscv-iommu.c index d8b1cb03a8..96a7fbdefc 100644 --- a/hw/riscv/riscv-iommu.c +++ b/hw/riscv/riscv-iommu.c @@ -2351,7 +2351,8 @@ static void riscv_iommu_realize(DeviceState *dev, Error **errp) } if (s->enable_g_stage) { s->cap |= RISCV_IOMMU_CAP_SV32X4 | RISCV_IOMMU_CAP_SV39X4 | - RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4; + RISCV_IOMMU_CAP_SV48X4 | RISCV_IOMMU_CAP_SV57X4 | + RISCV_IOMMU_CAP_SVRSW60T59B; } if (s->hpm_cntrs > 0) { diff --git a/target/riscv/cpu.c b/target/riscv/cpu.c index 6f1d0618f1..d055ddf462 100644 --- a/target/riscv/cpu.c +++ b/target/riscv/cpu.c @@ -230,6 +230,7 @@ const RISCVIsaExtData isa_edata_arr[] = { ISA_EXT_DATA_ENTRY(svinval, PRIV_VERSION_1_12_0, ext_svinval), ISA_EXT_DATA_ENTRY(svnapot, PRIV_VERSION_1_12_0, ext_svnapot), ISA_EXT_DATA_ENTRY(svpbmt, PRIV_VERSION_1_12_0, ext_svpbmt), + ISA_EXT_DATA_ENTRY(svrsw60t59b, PRIV_VERSION_1_13_0, ext_svrsw60t59b), ISA_EXT_DATA_ENTRY(svukte, PRIV_VERSION_1_13_0, ext_svukte), ISA_EXT_DATA_ENTRY(svvptc, PRIV_VERSION_1_13_0, ext_svvptc), ISA_EXT_DATA_ENTRY(xtheadba, PRIV_VERSION_1_11_0, ext_xtheadba), @@ -1285,6 +1286,7 @@ const RISCVCPUMultiExtConfig riscv_cpu_extensions[] = { MULTI_EXT_CFG_BOOL("svinval", ext_svinval, false), MULTI_EXT_CFG_BOOL("svnapot", ext_svnapot, false), MULTI_EXT_CFG_BOOL("svpbmt", ext_svpbmt, false), + MULTI_EXT_CFG_BOOL("svrsw60t59b", ext_svrsw60t59b, false), MULTI_EXT_CFG_BOOL("svvptc", ext_svvptc, true), MULTI_EXT_CFG_BOOL("zicntr", ext_zicntr, true), diff --git a/target/riscv/cpu_bits.h b/target/riscv/cpu_bits.h index e6b3e28386..b62dd82fe7 100644 --- a/target/riscv/cpu_bits.h +++ b/target/riscv/cpu_bits.h @@ -735,7 +735,8 @@ typedef enum { #define PTE_SOFT 0x300 /* Reserved for Software */ #define PTE_PBMT 0x6000000000000000ULL /* Page-based memory types */ #define PTE_N 0x8000000000000000ULL /* NAPOT translation */ -#define PTE_RESERVED 0x1FC0000000000000ULL /* Reserved bits */ +#define PTE_RESERVED(svrsw60t59b) \ + (svrsw60t59b ? 0x07C0000000000000ULL : 0x1FC0000000000000ULL) /* Reserved bits */ #define PTE_ATTR (PTE_N | PTE_PBMT) /* All attributes bits */ /* Page table PPN shift amount */ diff --git a/target/riscv/cpu_cfg_fields.h.inc b/target/riscv/cpu_cfg_fields.h.inc index 98ceb7b340..e2d116f0df 100644 --- a/target/riscv/cpu_cfg_fields.h.inc +++ b/target/riscv/cpu_cfg_fields.h.inc @@ -57,6 +57,7 @@ BOOL_FIELD(ext_svadu) BOOL_FIELD(ext_svinval) BOOL_FIELD(ext_svnapot) BOOL_FIELD(ext_svpbmt) +BOOL_FIELD(ext_svrsw60t59b) BOOL_FIELD(ext_svvptc) BOOL_FIELD(ext_svukte) BOOL_FIELD(ext_zdinx) diff --git a/target/riscv/cpu_helper.c b/target/riscv/cpu_helper.c index 2ed69d7c2d..3479a62cc7 100644 --- a/target/riscv/cpu_helper.c +++ b/target/riscv/cpu_helper.c @@ -1309,6 +1309,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, bool svade = riscv_cpu_cfg(env)->ext_svade; bool svadu = riscv_cpu_cfg(env)->ext_svadu; bool adue = svadu ? env->menvcfg & MENVCFG_ADUE : !svade; + bool svrsw60t59b = riscv_cpu_cfg(env)->ext_svrsw60t59b; if (first_stage && two_stage && env->virt_enabled) { pbmte = pbmte && (env->henvcfg & HENVCFG_PBMTE); @@ -1376,7 +1377,7 @@ static int get_physical_address(CPURISCVState *env, hwaddr *physical, if (riscv_cpu_sxl(env) == MXL_RV32) { ppn = pte >> PTE_PPN_SHIFT; } else { - if (pte & PTE_RESERVED) { + if (pte & PTE_RESERVED(svrsw60t59b)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: reserved bits set in PTE: " "addr: 0x%" HWADDR_PRIx " pte: 0x" TARGET_FMT_lx "\n", __func__, pte_addr, pte); diff --git a/target/riscv/tcg/tcg-cpu.c b/target/riscv/tcg/tcg-cpu.c index 163e7ce364..78fb279184 100644 --- a/target/riscv/tcg/tcg-cpu.c +++ b/target/riscv/tcg/tcg-cpu.c @@ -839,6 +839,12 @@ void riscv_cpu_validate_set_extensions(RISCVCPU *cpu, Error **errp) cpu->cfg.ext_ssctr = false; } + if (cpu->cfg.ext_svrsw60t59b && + (!cpu->cfg.mmu || mcc->def->misa_mxl_max == MXL_RV32)) { + error_setg(errp, "svrsw60t59b is not supported on RV32 and MMU-less platforms"); + return; + } + /* * Disable isa extensions based on priv spec after we * validated and set everything we need. @@ -1588,6 +1594,8 @@ static void riscv_init_max_cpu_extensions(Object *obj) if (env->misa_mxl != MXL_RV32) { isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_zcf), false); + } else { + isa_ext_update_enabled(cpu, CPU_CFG_OFFSET(ext_svrsw60t59b), false); } /* From 9526b9b620c78a1336bca1b55a562fad23208392 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 2 Jul 2025 13:30:51 +0200 Subject: [PATCH 1901/2760] hw/arm/highbank: Mark the "highbank" and the "midway" machine as deprecated We don't have any automatic regression tests for these machines and when asking the usual suspects on the mailing list we came to the conclusion that nobody tests these machines manually, too, so it seems like this is currently just completely unused code. Mark them as depre- cated to see whether anybody still speaks up during the deprecation period, otherwise we can likely remove these two machines in a couple of releases. Signed-off-by: Thomas Huth Acked-by: Rob Herring Acked-by: Guenter Roeck Message-id: 20250702113051.46483-1-thuth@redhat.com Reviewed-by: Peter Maydell [PMM: tweaked deprecation.rst text] Signed-off-by: Peter Maydell --- docs/about/deprecated.rst | 7 +++++++ hw/arm/highbank.c | 2 ++ 2 files changed, 9 insertions(+) diff --git a/docs/about/deprecated.rst b/docs/about/deprecated.rst index 42037131de..b24c278f70 100644 --- a/docs/about/deprecated.rst +++ b/docs/about/deprecated.rst @@ -344,6 +344,13 @@ they want to use and avoids confusion. Existing users of the ``spike`` machine must ensure that they're setting the ``spike`` machine in the command line (``-M spike``). +Arm ``highbank`` and ``midway`` machines (since 10.1) +''''''''''''''''''''''''''''''''''''''''''''''''''''' + +There are no known users left for these machines (if you still use it, +please write a mail to the qemu-devel mailing list). If you just want to +boot a Cortex-A15 or Cortex-A9 Linux, use the ``virt`` machine instead. + System emulator binaries ------------------------ diff --git a/hw/arm/highbank.c b/hw/arm/highbank.c index 3ae26ebebd..165c0b741a 100644 --- a/hw/arm/highbank.c +++ b/hw/arm/highbank.c @@ -357,6 +357,7 @@ static void highbank_class_init(ObjectClass *oc, const void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "highbank.dram"; + mc->deprecation_reason = "no known users left for this machine"; } static const TypeInfo highbank_type = { @@ -381,6 +382,7 @@ static void midway_class_init(ObjectClass *oc, const void *data) mc->max_cpus = 4; mc->ignore_memory_transaction_failures = true; mc->default_ram_id = "highbank.dram"; + mc->deprecation_reason = "no known users left for this machine"; } static const TypeInfo midway_type = { From 597ae563ba2a46174a414a3d223db4aac9a76cd3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Jun 2025 14:15:52 +0200 Subject: [PATCH 1902/2760] accel/kvm: Remove kvm_init_cpu_signals() stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 57038a92bb0 ("cpus: extract out kvm-specific code to accel/kvm") the kvm_init_cpu_signals() stub is not necessary. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-6-philmd@linaro.org> --- accel/stubs/kvm-stub.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index ecfd7636f5..b9b4427c91 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -105,11 +105,6 @@ unsigned int kvm_get_free_memslots(void) return 0; } -void kvm_init_cpu_signals(CPUState *cpu) -{ - abort(); -} - bool kvm_arm_supports_user_irq(void) { return false; From af065855ced71fdad6fc0fecec7dd65593b73413 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 07:02:17 +0200 Subject: [PATCH 1903/2760] accel/kvm: Reduce kvm_create_vcpu() declaration scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit kvm_create_vcpu() is only used within the same file unit. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-7-philmd@linaro.org> --- accel/kvm/kvm-all.c | 8 +++++++- include/system/kvm.h | 8 -------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index d095d1b98f..17235f2646 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -453,7 +453,13 @@ static void kvm_reset_parked_vcpus(KVMState *s) } } -int kvm_create_vcpu(CPUState *cpu) +/** + * kvm_create_vcpu - Gets a parked KVM vCPU or creates a KVM vCPU + * @cpu: QOM CPUState object for which KVM vCPU has to be fetched/created. + * + * @returns: 0 when success, errno (<0) when failed. + */ +static int kvm_create_vcpu(CPUState *cpu) { unsigned long vcpu_id = kvm_arch_vcpu_id(cpu); KVMState *s = kvm_state; diff --git a/include/system/kvm.h b/include/system/kvm.h index 7cc60d26f2..e943df2c09 100644 --- a/include/system/kvm.h +++ b/include/system/kvm.h @@ -316,14 +316,6 @@ int kvm_create_device(KVMState *s, uint64_t type, bool test); */ bool kvm_device_supported(int vmfd, uint64_t type); -/** - * kvm_create_vcpu - Gets a parked KVM vCPU or creates a KVM vCPU - * @cpu: QOM CPUState object for which KVM vCPU has to be fetched/created. - * - * @returns: 0 when success, errno (<0) when failed. - */ -int kvm_create_vcpu(CPUState *cpu); - /** * kvm_park_vcpu - Park QEMU KVM vCPU context * @cpu: QOM CPUState object for which QEMU KVM vCPU context has to be parked. From 7359f690095bea8466bef2b7b822bb4d2962dbb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Jul 2025 11:42:43 +0200 Subject: [PATCH 1904/2760] accel/tcg: Remove 'info opcount' and @x-query-opcount MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 1b65b4f54c7 ("accel/tcg: remove CONFIG_PROFILER", released with QEMU v8.1.0) we get pointless output: (qemu) info opcount [TCG profiler not compiled] Remove that unstable and unuseful command. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Dr. David Alan Gilbert Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Markus Armbruster Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-8-philmd@linaro.org> --- accel/tcg/monitor.c | 21 --------------------- hmp-commands-info.hx | 14 -------------- qapi/machine.json | 18 ------------------ tests/qtest/qmp-cmd-test.c | 1 - 4 files changed, 54 deletions(-) diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index 1c182b6bfb..7c686226b2 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -215,30 +215,9 @@ HumanReadableText *qmp_x_query_jit(Error **errp) return human_readable_text_from_str(buf); } -static void tcg_dump_op_count(GString *buf) -{ - g_string_append_printf(buf, "[TCG profiler not compiled]\n"); -} - -HumanReadableText *qmp_x_query_opcount(Error **errp) -{ - g_autoptr(GString) buf = g_string_new(""); - - if (!tcg_enabled()) { - error_setg(errp, - "Opcode count information is only available with accel=tcg"); - return NULL; - } - - tcg_dump_op_count(buf); - - return human_readable_text_from_str(buf); -} - static void hmp_tcg_register(void) { monitor_register_hmp_info_hrt("jit", qmp_x_query_jit); - monitor_register_hmp_info_hrt("opcount", qmp_x_query_opcount); } type_init(hmp_tcg_register); diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index 639a450ee5..d797922275 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -256,20 +256,6 @@ SRST Show dynamic compiler info. ERST -#if defined(CONFIG_TCG) - { - .name = "opcount", - .args_type = "", - .params = "", - .help = "show dynamic compiler opcode counters", - }, -#endif - -SRST - ``info opcount`` - Show dynamic compiler opcode counters -ERST - { .name = "sync-profile", .args_type = "mean:-m,no_coalesce:-n,max:i?", diff --git a/qapi/machine.json b/qapi/machine.json index 0650b8de71..f712e7da6d 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1761,24 +1761,6 @@ 'returns': 'HumanReadableText', 'features': [ 'unstable' ] } -## -# @x-query-opcount: -# -# Query TCG opcode counters -# -# Features: -# -# @unstable: This command is meant for debugging. -# -# Returns: TCG opcode counters -# -# Since: 6.2 -## -{ 'command': 'x-query-opcount', - 'returns': 'HumanReadableText', - 'if': 'CONFIG_TCG', - 'features': [ 'unstable' ] } - ## # @x-query-ramblock: # diff --git a/tests/qtest/qmp-cmd-test.c b/tests/qtest/qmp-cmd-test.c index 040d042810..cf71876186 100644 --- a/tests/qtest/qmp-cmd-test.c +++ b/tests/qtest/qmp-cmd-test.c @@ -51,7 +51,6 @@ static int query_error_class(const char *cmd) { "x-query-usb", ERROR_CLASS_GENERIC_ERROR }, /* Only valid with accel=tcg */ { "x-query-jit", ERROR_CLASS_GENERIC_ERROR }, - { "x-query-opcount", ERROR_CLASS_GENERIC_ERROR }, { "xen-event-list", ERROR_CLASS_GENERIC_ERROR }, { NULL, -1 } }; From 93d7064e594f442235d7d9442005f4404a7a5004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Jul 2025 11:42:17 +0200 Subject: [PATCH 1905/2760] accel/tcg: Remove profiler leftover MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TCG profiler was removed in commit 1b65b4f54c7. Fixes: 1b65b4f54c7 ("accel/tcg: remove CONFIG_PROFILER") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-9-philmd@linaro.org> --- accel/tcg/monitor.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index 7c686226b2..344ec50047 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -141,11 +141,6 @@ static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) *pelide = elide; } -static void tcg_dump_info(GString *buf) -{ - g_string_append_printf(buf, "[TCG profiler not compiled]\n"); -} - static void dump_exec_info(GString *buf) { struct tb_tree_stats tst = {}; @@ -196,7 +191,6 @@ static void dump_exec_info(GString *buf) g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); - tcg_dump_info(buf); } HumanReadableText *qmp_x_query_jit(Error **errp) From 8becf103741bd1b50b827bc3cd60872e9f36e981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 11:48:44 +0200 Subject: [PATCH 1906/2760] accel/tcg: Factor tcg_dump_flush_info() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-10-philmd@linaro.org> --- accel/tcg/monitor.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index 344ec50047..6d9cc11d94 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -141,11 +141,26 @@ static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) *pelide = elide; } +static void tcg_dump_flush_info(GString *buf) +{ + size_t flush_full, flush_part, flush_elide; + + g_string_append_printf(buf, "TB flush count %u\n", + qatomic_read(&tb_ctx.tb_flush_count)); + g_string_append_printf(buf, "TB invalidate count %u\n", + qatomic_read(&tb_ctx.tb_phys_invalidate_count)); + + tlb_flush_counts(&flush_full, &flush_part, &flush_elide); + g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); + g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); + g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); +} + static void dump_exec_info(GString *buf) { struct tb_tree_stats tst = {}; struct qht_stats hst; - size_t nb_tbs, flush_full, flush_part, flush_elide; + size_t nb_tbs; tcg_tb_foreach(tb_tree_stats_iter, &tst); nb_tbs = tst.nb_tbs; @@ -182,15 +197,7 @@ static void dump_exec_info(GString *buf) qht_statistics_destroy(&hst); g_string_append_printf(buf, "\nStatistics:\n"); - g_string_append_printf(buf, "TB flush count %u\n", - qatomic_read(&tb_ctx.tb_flush_count)); - g_string_append_printf(buf, "TB invalidate count %u\n", - qatomic_read(&tb_ctx.tb_phys_invalidate_count)); - - tlb_flush_counts(&flush_full, &flush_part, &flush_elide); - g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); - g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); - g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); + tcg_dump_flush_info(buf); } HumanReadableText *qmp_x_query_jit(Error **errp) From 3955a104bca098b820a0b57bb411281ae5cac498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Jul 2025 12:16:27 +0200 Subject: [PATCH 1907/2760] accel/tcg: Factor tcg_dump_stats() out for re-use MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-11-philmd@linaro.org> --- accel/tcg/internal-common.h | 2 ++ accel/tcg/monitor.c | 11 ++++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index 1dbc45dd95..77a3a0684a 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -139,4 +139,6 @@ G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); +void tcg_dump_stats(GString *buf); + #endif diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index 6d9cc11d94..e7ed7281a4 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -200,6 +200,13 @@ static void dump_exec_info(GString *buf) tcg_dump_flush_info(buf); } +void tcg_dump_stats(GString *buf) +{ + dump_accel_info(buf); + dump_exec_info(buf); + dump_drift_info(buf); +} + HumanReadableText *qmp_x_query_jit(Error **errp) { g_autoptr(GString) buf = g_string_new(""); @@ -209,9 +216,7 @@ HumanReadableText *qmp_x_query_jit(Error **errp) return NULL; } - dump_accel_info(buf); - dump_exec_info(buf); - dump_drift_info(buf); + tcg_dump_stats(buf); return human_readable_text_from_str(buf); } From 6bb8f2c51b28ce35c83ac4e53bbd85ef37d787c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 16:13:26 +0200 Subject: [PATCH 1908/2760] accel/tcg: Clear exit_request once in tcg_cpu_exec() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250701144017.43487-62-philmd@linaro.org> --- accel/tcg/tcg-accel-ops-mttcg.c | 1 - accel/tcg/tcg-accel-ops.c | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/accel/tcg/tcg-accel-ops-mttcg.c b/accel/tcg/tcg-accel-ops-mttcg.c index dfcee30947..337b993d3d 100644 --- a/accel/tcg/tcg-accel-ops-mttcg.c +++ b/accel/tcg/tcg-accel-ops-mttcg.c @@ -113,7 +113,6 @@ static void *mttcg_cpu_thread_fn(void *arg) } } - qatomic_set_mb(&cpu->exit_request, 0); qemu_wait_io_event(cpu); } while (!cpu->unplug || cpu_can_run(cpu)); diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 6116644d1c..71776bc29c 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -80,6 +80,9 @@ int tcg_cpu_exec(CPUState *cpu) cpu_exec_start(cpu); ret = cpu_exec(cpu); cpu_exec_end(cpu); + + qatomic_set_mb(&cpu->exit_request, 0); + return ret; } From dd0b228552e4b1d3b680ff4e8033a110536fdc4c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 16:04:35 +0200 Subject: [PATCH 1909/2760] accel/tcg: Unregister the RCU before exiting RR thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although unreachable, still unregister the RCU before exiting the thread, as documented in "qemu/rcu.h": /* * Important ! * * Each thread containing read-side critical sections must be registered * with rcu_register_thread() before calling rcu_read_lock(). * rcu_unregister_thread() should be called before the thread exits. */ Unregister the RCU to be on par with what is done for other accelerators. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Reviewed-by: Pierrick Bouvier Reviewed-by: Zhao Liu Message-Id: <20250702185332.43650-66-philmd@linaro.org> --- accel/tcg/tcg-accel-ops-rr.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index 6eec5c9eee..a578698d07 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -302,6 +302,8 @@ static void *rr_cpu_thread_fn(void *arg) rr_deal_with_unplugged_cpus(); } + rcu_unregister_thread(); + g_assert_not_reached(); } From 4dc480e7da9c389c069da250a6e9252ee9fa0659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Jun 2025 11:17:43 +0200 Subject: [PATCH 1910/2760] accel/hvf: Restrict internal declarations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Common code only needs to know whether HVF is enabled and the QOM type. Move the rest to "hvf_int.h", removing the need for COMPILING_PER_TARGET #ifdef'ry. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-15-philmd@linaro.org> --- include/system/hvf.h | 38 -------------------------------------- include/system/hvf_int.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 38 deletions(-) diff --git a/include/system/hvf.h b/include/system/hvf.h index a9a502f0c8..d3dcf088b3 100644 --- a/include/system/hvf.h +++ b/include/system/hvf.h @@ -14,10 +14,6 @@ #define HVF_H #include "qemu/accel.h" -#include "qemu/queue.h" -#include "exec/vaddr.h" -#include "qom/object.h" -#include "exec/vaddr.h" #ifdef COMPILING_PER_TARGET # ifdef CONFIG_HVF @@ -40,38 +36,4 @@ typedef struct HVFState HVFState; DECLARE_INSTANCE_CHECKER(HVFState, HVF_STATE, TYPE_HVF_ACCEL) -#ifdef COMPILING_PER_TARGET -struct hvf_sw_breakpoint { - vaddr pc; - vaddr saved_insn; - int use_count; - QTAILQ_ENTRY(hvf_sw_breakpoint) entry; -}; - -struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, - vaddr pc); -int hvf_sw_breakpoints_active(CPUState *cpu); - -int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp); -int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp); -int hvf_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type); -int hvf_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type); -void hvf_arch_remove_all_hw_breakpoints(void); - -/* - * hvf_update_guest_debug: - * @cs: CPUState for the CPU to update - * - * Update guest to enable or disable debugging. Per-arch specifics will be - * handled by calling down to hvf_arch_update_guest_debug. - */ -int hvf_update_guest_debug(CPUState *cpu); -void hvf_arch_update_guest_debug(CPUState *cpu); - -/* - * Return whether the guest supports debugging. - */ -bool hvf_arch_supports_guest_debug(void); -#endif /* COMPILING_PER_TARGET */ - #endif diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h index d774e58df9..4f6db40c34 100644 --- a/include/system/hvf_int.h +++ b/include/system/hvf_int.h @@ -12,6 +12,8 @@ #define HVF_INT_H #include "qemu/queue.h" +#include "exec/vaddr.h" +#include "qom/object.h" #ifdef __aarch64__ #include @@ -77,4 +79,36 @@ int hvf_put_registers(CPUState *); int hvf_get_registers(CPUState *); void hvf_kick_vcpu_thread(CPUState *cpu); +struct hvf_sw_breakpoint { + vaddr pc; + vaddr saved_insn; + int use_count; + QTAILQ_ENTRY(hvf_sw_breakpoint) entry; +}; + +struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, + vaddr pc); +int hvf_sw_breakpoints_active(CPUState *cpu); + +int hvf_arch_insert_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp); +int hvf_arch_remove_sw_breakpoint(CPUState *cpu, struct hvf_sw_breakpoint *bp); +int hvf_arch_insert_hw_breakpoint(vaddr addr, vaddr len, int type); +int hvf_arch_remove_hw_breakpoint(vaddr addr, vaddr len, int type); +void hvf_arch_remove_all_hw_breakpoints(void); + +/* + * hvf_update_guest_debug: + * @cs: CPUState for the CPU to update + * + * Update guest to enable or disable debugging. Per-arch specifics will be + * handled by calling down to hvf_arch_update_guest_debug. + */ +int hvf_update_guest_debug(CPUState *cpu); +void hvf_arch_update_guest_debug(CPUState *cpu); + +/* + * Return whether the guest supports debugging. + */ +bool hvf_arch_supports_guest_debug(void); + #endif From 3240b69f683865f0c098669608add5414e532da1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Jun 2025 11:33:53 +0200 Subject: [PATCH 1911/2760] accel/hvf: Move per-cpu method declarations to hvf-accel-ops.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hvf-all.c aims to contain the generic accel methods (TYPE_ACCEL), while hvf-accel-ops.c the per-vcpu methods (TYPE_ACCEL_OPS). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-16-philmd@linaro.org> --- accel/hvf/hvf-accel-ops.c | 30 ++++++++++++++++++++++++++++++ accel/hvf/hvf-all.c | 28 ---------------------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index b38977207d..3752334688 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -50,9 +50,11 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" +#include "qemu/queue.h" #include "system/address-spaces.h" #include "gdbstub/enums.h" #include "hw/boards.h" +#include "hw/core/cpu.h" #include "system/accel-ops.h" #include "system/cpus.h" #include "system/hvf.h" @@ -482,6 +484,34 @@ static void hvf_start_vcpu_thread(CPUState *cpu) cpu, QEMU_THREAD_JOINABLE); } +struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, vaddr pc) +{ + struct hvf_sw_breakpoint *bp; + + QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) { + if (bp->pc == pc) { + return bp; + } + } + return NULL; +} + +int hvf_sw_breakpoints_active(CPUState *cpu) +{ + return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints); +} + +static void do_hvf_update_guest_debug(CPUState *cpu, run_on_cpu_data arg) +{ + hvf_arch_update_guest_debug(cpu); +} + +int hvf_update_guest_debug(CPUState *cpu) +{ + run_on_cpu(cpu, do_hvf_update_guest_debug, RUN_ON_CPU_NULL); + return 0; +} + static int hvf_insert_breakpoint(CPUState *cpu, int type, vaddr addr, vaddr len) { struct hvf_sw_breakpoint *bp; diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 8c387fda24..1c72c43ddb 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -41,31 +41,3 @@ void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line, abort(); } - -struct hvf_sw_breakpoint *hvf_find_sw_breakpoint(CPUState *cpu, vaddr pc) -{ - struct hvf_sw_breakpoint *bp; - - QTAILQ_FOREACH(bp, &hvf_state->hvf_sw_breakpoints, entry) { - if (bp->pc == pc) { - return bp; - } - } - return NULL; -} - -int hvf_sw_breakpoints_active(CPUState *cpu) -{ - return !QTAILQ_EMPTY(&hvf_state->hvf_sw_breakpoints); -} - -static void do_hvf_update_guest_debug(CPUState *cpu, run_on_cpu_data arg) -{ - hvf_arch_update_guest_debug(cpu); -} - -int hvf_update_guest_debug(CPUState *cpu) -{ - run_on_cpu(cpu, do_hvf_update_guest_debug, RUN_ON_CPU_NULL); - return 0; -} From 81490432b667ada39d51247aa56f51110bcb595a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 1 Jul 2025 14:57:25 +0200 Subject: [PATCH 1912/2760] accel/hvf: Move generic method declarations to hvf-all.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit hvf-all.c aims to contain the generic accel methods (TYPE_ACCEL), while hvf-accel-ops.c the per-vcpu methods (TYPE_ACCEL_OPS). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-17-philmd@linaro.org> --- accel/hvf/hvf-accel-ops.c | 273 +------------------------------------- accel/hvf/hvf-all.c | 267 +++++++++++++++++++++++++++++++++++++ 2 files changed, 271 insertions(+), 269 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 3752334688..2c0715a17f 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -48,20 +48,16 @@ */ #include "qemu/osdep.h" -#include "qemu/error-report.h" +#include "qemu/guest-random.h" #include "qemu/main-loop.h" #include "qemu/queue.h" -#include "system/address-spaces.h" #include "gdbstub/enums.h" -#include "hw/boards.h" +#include "exec/cpu-common.h" #include "hw/core/cpu.h" #include "system/accel-ops.h" #include "system/cpus.h" #include "system/hvf.h" #include "system/hvf_int.h" -#include "system/runstate.h" -#include "qemu/guest-random.h" -#include "trace.h" HVFState *hvf_state; @@ -81,132 +77,6 @@ hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size) return NULL; } -struct mac_slot { - int present; - uint64_t size; - uint64_t gpa_start; - uint64_t gva; -}; - -struct mac_slot mac_slots[32]; - -static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags) -{ - struct mac_slot *macslot; - hv_return_t ret; - - macslot = &mac_slots[slot->slot_id]; - - if (macslot->present) { - if (macslot->size != slot->size) { - macslot->present = 0; - trace_hvf_vm_unmap(macslot->gpa_start, macslot->size); - ret = hv_vm_unmap(macslot->gpa_start, macslot->size); - assert_hvf_ok(ret); - } - } - - if (!slot->size) { - return 0; - } - - macslot->present = 1; - macslot->gpa_start = slot->start; - macslot->size = slot->size; - trace_hvf_vm_map(slot->start, slot->size, slot->mem, flags, - flags & HV_MEMORY_READ ? 'R' : '-', - flags & HV_MEMORY_WRITE ? 'W' : '-', - flags & HV_MEMORY_EXEC ? 'E' : '-'); - ret = hv_vm_map(slot->mem, slot->start, slot->size, flags); - assert_hvf_ok(ret); - return 0; -} - -static void hvf_set_phys_mem(MemoryRegionSection *section, bool add) -{ - hvf_slot *mem; - MemoryRegion *area = section->mr; - bool writable = !area->readonly && !area->rom_device; - hv_memory_flags_t flags; - uint64_t page_size = qemu_real_host_page_size(); - - if (!memory_region_is_ram(area)) { - if (writable) { - return; - } else if (!memory_region_is_romd(area)) { - /* - * If the memory device is not in romd_mode, then we actually want - * to remove the hvf memory slot so all accesses will trap. - */ - add = false; - } - } - - if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) || - !QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) { - /* Not page aligned, so we can not map as RAM */ - add = false; - } - - mem = hvf_find_overlap_slot( - section->offset_within_address_space, - int128_get64(section->size)); - - if (mem && add) { - if (mem->size == int128_get64(section->size) && - mem->start == section->offset_within_address_space && - mem->mem == (memory_region_get_ram_ptr(area) + - section->offset_within_region)) { - return; /* Same region was attempted to register, go away. */ - } - } - - /* Region needs to be reset. set the size to 0 and remap it. */ - if (mem) { - mem->size = 0; - if (do_hvf_set_memory(mem, 0)) { - error_report("Failed to reset overlapping slot"); - abort(); - } - } - - if (!add) { - return; - } - - if (area->readonly || - (!memory_region_is_ram(area) && memory_region_is_romd(area))) { - flags = HV_MEMORY_READ | HV_MEMORY_EXEC; - } else { - flags = HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC; - } - - /* Now make a new slot. */ - int x; - - for (x = 0; x < hvf_state->num_slots; ++x) { - mem = &hvf_state->slots[x]; - if (!mem->size) { - break; - } - } - - if (x == hvf_state->num_slots) { - error_report("No free slots"); - abort(); - } - - mem->size = int128_get64(section->size); - mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region; - mem->start = section->offset_within_address_space; - mem->region = area; - - if (do_hvf_set_memory(mem, flags)) { - error_report("Error registering new memory slot"); - abort(); - } -} - static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { if (!cpu->accel->dirty) { @@ -244,147 +114,10 @@ static void hvf_cpu_synchronize_pre_loadvm(CPUState *cpu) run_on_cpu(cpu, do_hvf_cpu_synchronize_set_dirty, RUN_ON_CPU_NULL); } -static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on) -{ - hvf_slot *slot; - - slot = hvf_find_overlap_slot( - section->offset_within_address_space, - int128_get64(section->size)); - - /* protect region against writes; begin tracking it */ - if (on) { - slot->flags |= HVF_SLOT_LOG; - hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size, - HV_MEMORY_READ | HV_MEMORY_EXEC); - /* stop tracking region*/ - } else { - slot->flags &= ~HVF_SLOT_LOG; - hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size, - HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC); - } -} - -static void hvf_log_start(MemoryListener *listener, - MemoryRegionSection *section, int old, int new) -{ - if (old != 0) { - return; - } - - hvf_set_dirty_tracking(section, 1); -} - -static void hvf_log_stop(MemoryListener *listener, - MemoryRegionSection *section, int old, int new) -{ - if (new != 0) { - return; - } - - hvf_set_dirty_tracking(section, 0); -} - -static void hvf_log_sync(MemoryListener *listener, - MemoryRegionSection *section) -{ - /* - * sync of dirty pages is handled elsewhere; just make sure we keep - * tracking the region. - */ - hvf_set_dirty_tracking(section, 1); -} - -static void hvf_region_add(MemoryListener *listener, - MemoryRegionSection *section) -{ - hvf_set_phys_mem(section, true); -} - -static void hvf_region_del(MemoryListener *listener, - MemoryRegionSection *section) -{ - hvf_set_phys_mem(section, false); -} - -static MemoryListener hvf_memory_listener = { - .name = "hvf", - .priority = MEMORY_LISTENER_PRIORITY_ACCEL, - .region_add = hvf_region_add, - .region_del = hvf_region_del, - .log_start = hvf_log_start, - .log_stop = hvf_log_stop, - .log_sync = hvf_log_sync, -}; - static void dummy_signal(int sig) { } -bool hvf_allowed; - -static int hvf_accel_init(MachineState *ms) -{ - int x; - hv_return_t ret; - HVFState *s; - int pa_range = 36; - MachineClass *mc = MACHINE_GET_CLASS(ms); - - if (mc->hvf_get_physical_address_range) { - pa_range = mc->hvf_get_physical_address_range(ms); - if (pa_range < 0) { - return -EINVAL; - } - } - - ret = hvf_arch_vm_create(ms, (uint32_t)pa_range); - assert_hvf_ok(ret); - - s = g_new0(HVFState, 1); - - s->num_slots = ARRAY_SIZE(s->slots); - for (x = 0; x < s->num_slots; ++x) { - s->slots[x].size = 0; - s->slots[x].slot_id = x; - } - - QTAILQ_INIT(&s->hvf_sw_breakpoints); - - hvf_state = s; - memory_listener_register(&hvf_memory_listener, &address_space_memory); - - return hvf_arch_init(); -} - -static inline int hvf_gdbstub_sstep_flags(void) -{ - return SSTEP_ENABLE | SSTEP_NOIRQ; -} - -static void hvf_accel_class_init(ObjectClass *oc, const void *data) -{ - AccelClass *ac = ACCEL_CLASS(oc); - ac->name = "HVF"; - ac->init_machine = hvf_accel_init; - ac->allowed = &hvf_allowed; - ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags; -} - -static const TypeInfo hvf_accel_type = { - .name = TYPE_HVF_ACCEL, - .parent = TYPE_ACCEL, - .instance_size = sizeof(HVFState), - .class_init = hvf_accel_class_init, -}; - -static void hvf_type_init(void) -{ - type_register_static(&hvf_accel_type); -} - -type_init(hvf_type_init); - static void hvf_vcpu_destroy(CPUState *cpu) { hv_return_t ret = hv_vcpu_destroy(cpu->accel->fd); @@ -639,8 +372,10 @@ static const TypeInfo hvf_accel_ops_type = { .class_init = hvf_accel_ops_class_init, .abstract = true, }; + static void hvf_accel_ops_register_types(void) { type_register_static(&hvf_accel_ops_type); } + type_init(hvf_accel_ops_register_types); diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 1c72c43ddb..ddc77e629f 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -10,9 +10,24 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "system/address-spaces.h" +#include "system/memory.h" #include "system/hvf.h" #include "system/hvf_int.h" #include "hw/core/cpu.h" +#include "hw/boards.h" +#include "trace.h" + +bool hvf_allowed; + +struct mac_slot { + int present; + uint64_t size; + uint64_t gpa_start; + uint64_t gva; +}; + +struct mac_slot mac_slots[32]; const char *hvf_return_string(hv_return_t ret) { @@ -41,3 +56,255 @@ void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line, abort(); } + +static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags) +{ + struct mac_slot *macslot; + hv_return_t ret; + + macslot = &mac_slots[slot->slot_id]; + + if (macslot->present) { + if (macslot->size != slot->size) { + macslot->present = 0; + trace_hvf_vm_unmap(macslot->gpa_start, macslot->size); + ret = hv_vm_unmap(macslot->gpa_start, macslot->size); + assert_hvf_ok(ret); + } + } + + if (!slot->size) { + return 0; + } + + macslot->present = 1; + macslot->gpa_start = slot->start; + macslot->size = slot->size; + trace_hvf_vm_map(slot->start, slot->size, slot->mem, flags, + flags & HV_MEMORY_READ ? 'R' : '-', + flags & HV_MEMORY_WRITE ? 'W' : '-', + flags & HV_MEMORY_EXEC ? 'E' : '-'); + ret = hv_vm_map(slot->mem, slot->start, slot->size, flags); + assert_hvf_ok(ret); + return 0; +} + +static void hvf_set_phys_mem(MemoryRegionSection *section, bool add) +{ + hvf_slot *mem; + MemoryRegion *area = section->mr; + bool writable = !area->readonly && !area->rom_device; + hv_memory_flags_t flags; + uint64_t page_size = qemu_real_host_page_size(); + + if (!memory_region_is_ram(area)) { + if (writable) { + return; + } else if (!memory_region_is_romd(area)) { + /* + * If the memory device is not in romd_mode, then we actually want + * to remove the hvf memory slot so all accesses will trap. + */ + add = false; + } + } + + if (!QEMU_IS_ALIGNED(int128_get64(section->size), page_size) || + !QEMU_IS_ALIGNED(section->offset_within_address_space, page_size)) { + /* Not page aligned, so we can not map as RAM */ + add = false; + } + + mem = hvf_find_overlap_slot( + section->offset_within_address_space, + int128_get64(section->size)); + + if (mem && add) { + if (mem->size == int128_get64(section->size) && + mem->start == section->offset_within_address_space && + mem->mem == (memory_region_get_ram_ptr(area) + + section->offset_within_region)) { + return; /* Same region was attempted to register, go away. */ + } + } + + /* Region needs to be reset. set the size to 0 and remap it. */ + if (mem) { + mem->size = 0; + if (do_hvf_set_memory(mem, 0)) { + error_report("Failed to reset overlapping slot"); + abort(); + } + } + + if (!add) { + return; + } + + if (area->readonly || + (!memory_region_is_ram(area) && memory_region_is_romd(area))) { + flags = HV_MEMORY_READ | HV_MEMORY_EXEC; + } else { + flags = HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC; + } + + /* Now make a new slot. */ + int x; + + for (x = 0; x < hvf_state->num_slots; ++x) { + mem = &hvf_state->slots[x]; + if (!mem->size) { + break; + } + } + + if (x == hvf_state->num_slots) { + error_report("No free slots"); + abort(); + } + + mem->size = int128_get64(section->size); + mem->mem = memory_region_get_ram_ptr(area) + section->offset_within_region; + mem->start = section->offset_within_address_space; + mem->region = area; + + if (do_hvf_set_memory(mem, flags)) { + error_report("Error registering new memory slot"); + abort(); + } +} + +static void hvf_set_dirty_tracking(MemoryRegionSection *section, bool on) +{ + hvf_slot *slot; + + slot = hvf_find_overlap_slot( + section->offset_within_address_space, + int128_get64(section->size)); + + /* protect region against writes; begin tracking it */ + if (on) { + slot->flags |= HVF_SLOT_LOG; + hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size, + HV_MEMORY_READ | HV_MEMORY_EXEC); + /* stop tracking region*/ + } else { + slot->flags &= ~HVF_SLOT_LOG; + hv_vm_protect((uintptr_t)slot->start, (size_t)slot->size, + HV_MEMORY_READ | HV_MEMORY_WRITE | HV_MEMORY_EXEC); + } +} + +static void hvf_log_start(MemoryListener *listener, + MemoryRegionSection *section, int old, int new) +{ + if (old != 0) { + return; + } + + hvf_set_dirty_tracking(section, 1); +} + +static void hvf_log_stop(MemoryListener *listener, + MemoryRegionSection *section, int old, int new) +{ + if (new != 0) { + return; + } + + hvf_set_dirty_tracking(section, 0); +} + +static void hvf_log_sync(MemoryListener *listener, + MemoryRegionSection *section) +{ + /* + * sync of dirty pages is handled elsewhere; just make sure we keep + * tracking the region. + */ + hvf_set_dirty_tracking(section, 1); +} + +static void hvf_region_add(MemoryListener *listener, + MemoryRegionSection *section) +{ + hvf_set_phys_mem(section, true); +} + +static void hvf_region_del(MemoryListener *listener, + MemoryRegionSection *section) +{ + hvf_set_phys_mem(section, false); +} + +static MemoryListener hvf_memory_listener = { + .name = "hvf", + .priority = MEMORY_LISTENER_PRIORITY_ACCEL, + .region_add = hvf_region_add, + .region_del = hvf_region_del, + .log_start = hvf_log_start, + .log_stop = hvf_log_stop, + .log_sync = hvf_log_sync, +}; + +static int hvf_accel_init(MachineState *ms) +{ + int x; + hv_return_t ret; + HVFState *s; + int pa_range = 36; + MachineClass *mc = MACHINE_GET_CLASS(ms); + + if (mc->hvf_get_physical_address_range) { + pa_range = mc->hvf_get_physical_address_range(ms); + if (pa_range < 0) { + return -EINVAL; + } + } + + ret = hvf_arch_vm_create(ms, (uint32_t)pa_range); + assert_hvf_ok(ret); + + s = g_new0(HVFState, 1); + + s->num_slots = ARRAY_SIZE(s->slots); + for (x = 0; x < s->num_slots; ++x) { + s->slots[x].size = 0; + s->slots[x].slot_id = x; + } + + QTAILQ_INIT(&s->hvf_sw_breakpoints); + + hvf_state = s; + memory_listener_register(&hvf_memory_listener, &address_space_memory); + + return hvf_arch_init(); +} + +static int hvf_gdbstub_sstep_flags(void) +{ + return SSTEP_ENABLE | SSTEP_NOIRQ; +} + +static void hvf_accel_class_init(ObjectClass *oc, const void *data) +{ + AccelClass *ac = ACCEL_CLASS(oc); + ac->name = "HVF"; + ac->init_machine = hvf_accel_init; + ac->allowed = &hvf_allowed; + ac->gdbstub_supported_sstep_flags = hvf_gdbstub_sstep_flags; +} + +static const TypeInfo hvf_accel_type = { + .name = TYPE_HVF_ACCEL, + .parent = TYPE_ACCEL, + .instance_size = sizeof(HVFState), + .class_init = hvf_accel_class_init, +}; + +static void hvf_type_init(void) +{ + type_register_static(&hvf_accel_type); +} + +type_init(hvf_type_init); From 5f3bfbd8e2453671176e9759b9dc14584cd11e79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Jul 2025 00:26:05 +0200 Subject: [PATCH 1913/2760] accel/hvf: Report missing com.apple.security.hypervisor entitlement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We need the QEMU binary signed to be able to use HVF. Improve the following: $ ./qemu-system-aarch64-unsigned -M virt -accel hvf qemu-system-aarch64-unsigned: -accel hvf: Error: ret = HV_DENIED (0xfae94007, at ../../accel/hvf/hvf-accel-ops.c:339) Abort trap: 6 to: $ ./qemu-system-aarch64-unsigned -M virt -accel hvf qemu-system-aarch64-unsigned: -accel hvf: Could not access HVF. Is the executable signed with com.apple.security.hypervisor entitlement? Suggested-by: Shatyuka Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2800 Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Reviewed-by: Pierrick Bouvier Reviewed-by: Mads Ynddal Message-Id: <20250702185332.43650-29-philmd@linaro.org> --- accel/hvf/hvf-all.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index ddc77e629f..09fe3f2415 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -263,6 +263,11 @@ static int hvf_accel_init(MachineState *ms) } ret = hvf_arch_vm_create(ms, (uint32_t)pa_range); + if (ret == HV_DENIED) { + error_report("Could not access HVF. Is the executable signed" + " with com.apple.security.hypervisor entitlement?"); + exit(1); + } assert_hvf_ok(ret); s = g_new0(HVFState, 1); From b6340f5866e19deadae8c19399907fed938d8d1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 06:47:28 +0200 Subject: [PATCH 1914/2760] cpus: Document CPUState::vcpu_dirty field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Reviewed-by: Xiaoyao Li Message-Id: <20250703173248.44995-18-philmd@linaro.org> --- include/hw/core/cpu.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/hw/core/cpu.h b/include/hw/core/cpu.h index 162a56a5da..5eaf41a566 100644 --- a/include/hw/core/cpu.h +++ b/include/hw/core/cpu.h @@ -442,6 +442,7 @@ struct qemu_work_item; * @opaque: User data. * @mem_io_pc: Host Program Counter at which the memory was accessed. * @accel: Pointer to accelerator specific state. + * @vcpu_dirty: Hardware accelerator is not synchronized with QEMU state * @kvm_fd: vCPU file descriptor for KVM. * @work_mutex: Lock to prevent multiple access to @work_list. * @work_list: List of pending asynchronous work. @@ -538,7 +539,6 @@ struct CPUState { uint32_t kvm_fetch_index; uint64_t dirty_pages; int kvm_vcpu_stats_fd; - bool vcpu_dirty; /* Use by accel-block: CPU is executing an ioctl() */ QemuLockCnt in_ioctl_lock; @@ -554,6 +554,7 @@ struct CPUState { uint32_t halted; int32_t exception_index; + bool vcpu_dirty; AccelCPUState *accel; /* Used to keep track of an outstanding cpu throttle thread for migration From 6f13a0ada0160d420f1e4945ee53a1abdae33a1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 06:54:32 +0200 Subject: [PATCH 1915/2760] accel/hvf: Replace @dirty field by generic CPUState::vcpu_dirty field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need for accel-specific @dirty field when we have a generic one in CPUState. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Reviewed-by: Mads Ynddal Message-Id: <20250703173248.44995-19-philmd@linaro.org> --- accel/hvf/hvf-accel-ops.c | 10 +++++----- include/system/hvf_int.h | 1 - target/arm/hvf/hvf.c | 4 ++-- target/i386/hvf/hvf.c | 4 ++-- target/i386/hvf/x86hvf.c | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 2c0715a17f..395b5a8e1c 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -79,15 +79,15 @@ hvf_slot *hvf_find_overlap_slot(uint64_t start, uint64_t size) static void do_hvf_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { - if (!cpu->accel->dirty) { + if (!cpu->vcpu_dirty) { hvf_get_registers(cpu); - cpu->accel->dirty = true; + cpu->vcpu_dirty = true; } } static void hvf_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->accel->dirty) { + if (!cpu->vcpu_dirty) { run_on_cpu(cpu, do_hvf_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -96,7 +96,7 @@ static void do_hvf_cpu_synchronize_set_dirty(CPUState *cpu, run_on_cpu_data arg) { /* QEMU state is the reference, push it to HVF now and on next entry */ - cpu->accel->dirty = true; + cpu->vcpu_dirty = true; } static void hvf_cpu_synchronize_post_reset(CPUState *cpu) @@ -150,8 +150,8 @@ static int hvf_init_vcpu(CPUState *cpu) #else r = hv_vcpu_create(&cpu->accel->fd, HV_VCPU_DEFAULT); #endif - cpu->accel->dirty = true; assert_hvf_ok(r); + cpu->vcpu_dirty = true; cpu->accel->guest_debug_enabled = false; diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h index 4f6db40c34..5150c7dd9c 100644 --- a/include/system/hvf_int.h +++ b/include/system/hvf_int.h @@ -62,7 +62,6 @@ struct AccelCPUState { bool vtimer_masked; sigset_t unblock_ipi_mask; bool guest_debug_enabled; - bool dirty; }; void assert_hvf_ok_impl(hv_return_t ret, const char *file, unsigned int line, diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 7b6d291e79..c9cfcdc08b 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -813,9 +813,9 @@ int hvf_put_registers(CPUState *cpu) static void flush_cpu_state(CPUState *cpu) { - if (cpu->accel->dirty) { + if (cpu->vcpu_dirty) { hvf_put_registers(cpu); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } } diff --git a/target/i386/hvf/hvf.c b/target/i386/hvf/hvf.c index 99e37a33e5..818b50419f 100644 --- a/target/i386/hvf/hvf.c +++ b/target/i386/hvf/hvf.c @@ -733,9 +733,9 @@ int hvf_vcpu_exec(CPUState *cpu) } do { - if (cpu->accel->dirty) { + if (cpu->vcpu_dirty) { hvf_put_registers(cpu); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } if (hvf_inject_interrupts(cpu)) { diff --git a/target/i386/hvf/x86hvf.c b/target/i386/hvf/x86hvf.c index 2057314892..17fce1d3cd 100644 --- a/target/i386/hvf/x86hvf.c +++ b/target/i386/hvf/x86hvf.c @@ -427,7 +427,7 @@ int hvf_process_events(CPUState *cs) X86CPU *cpu = X86_CPU(cs); CPUX86State *env = &cpu->env; - if (!cs->accel->dirty) { + if (!cs->vcpu_dirty) { /* light weight sync for CPU_INTERRUPT_HARD and IF_MASK */ env->eflags = rreg(cs->accel->fd, HV_X86_RFLAGS); } From ffd8ee9d7cbd220d15526d14d5b3402a63eab0ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 06:59:03 +0200 Subject: [PATCH 1916/2760] accel/nvmm: Replace @dirty field by generic CPUState::vcpu_dirty field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need for accel-specific @dirty field when we have a generic one in CPUState. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-20-philmd@linaro.org> --- target/i386/nvmm/nvmm-all.c | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index f1c6120ccf..aea61a6fd2 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -30,7 +30,6 @@ struct AccelCPUState { struct nvmm_vcpu vcpu; uint8_t tpr; bool stop; - bool dirty; /* Window-exiting for INTs/NMIs. */ bool int_window_exit; @@ -508,7 +507,7 @@ nvmm_io_callback(struct nvmm_io *io) } /* Needed, otherwise infinite loop. */ - current_cpu->accel->dirty = false; + current_cpu->vcpu_dirty = false; } static void @@ -517,7 +516,7 @@ nvmm_mem_callback(struct nvmm_mem *mem) cpu_physical_memory_rw(mem->gpa, mem->data, mem->size, mem->write); /* Needed, otherwise infinite loop. */ - current_cpu->accel->dirty = false; + current_cpu->vcpu_dirty = false; } static struct nvmm_assist_callbacks nvmm_callbacks = { @@ -727,9 +726,9 @@ nvmm_vcpu_loop(CPUState *cpu) * Inner VCPU loop. */ do { - if (cpu->accel->dirty) { + if (cpu->vcpu_dirty) { nvmm_set_registers(cpu); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } if (qcpu->stop) { @@ -827,32 +826,32 @@ static void do_nvmm_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { nvmm_get_registers(cpu); - cpu->accel->dirty = true; + cpu->vcpu_dirty = true; } static void do_nvmm_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { nvmm_set_registers(cpu); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } static void do_nvmm_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { nvmm_set_registers(cpu); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } static void do_nvmm_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) { - cpu->accel->dirty = true; + cpu->vcpu_dirty = true; } void nvmm_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->accel->dirty) { + if (!cpu->vcpu_dirty) { run_on_cpu(cpu, do_nvmm_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -982,7 +981,7 @@ nvmm_init_vcpu(CPUState *cpu) } } - qcpu->dirty = true; + qcpu->vcpu_dirty = true; cpu->accel = qcpu; return 0; From 36ab216b81deaca2a6a39be803e870dead2b13f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 06:59:29 +0200 Subject: [PATCH 1917/2760] accel/whpx: Replace @dirty field by generic CPUState::vcpu_dirty field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need for accel-specific @dirty field when we have a generic one in CPUState. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-21-philmd@linaro.org> --- target/i386/whpx/whpx-all.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index cf6d3e4cdd..5001afad3a 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -237,7 +237,6 @@ struct AccelCPUState { uint64_t tpr; uint64_t apic_base; bool interruption_pending; - bool dirty; /* Must be the last field as it may have a tail */ WHV_RUN_VP_EXIT_CONTEXT exit_ctx; @@ -836,7 +835,7 @@ static HRESULT CALLBACK whpx_emu_setreg_callback( * The emulator just successfully wrote the register state. We clear the * dirty state so we avoid the double write on resume of the VP. */ - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; return hr; } @@ -1391,7 +1390,7 @@ static int whpx_last_vcpu_stopping(CPUState *cpu) /* Returns the address of the next instruction that is about to be executed. */ static vaddr whpx_vcpu_get_pc(CPUState *cpu, bool exit_context_valid) { - if (cpu->accel->dirty) { + if (cpu->vcpu_dirty) { /* The CPU registers have been modified by other parts of QEMU. */ return cpu_env(cpu)->eip; } else if (exit_context_valid) { @@ -1704,9 +1703,9 @@ static int whpx_vcpu_run(CPUState *cpu) } do { - if (cpu->accel->dirty) { + if (cpu->vcpu_dirty) { whpx_set_registers(cpu, WHPX_SET_RUNTIME_STATE); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } if (exclusive_step_mode == WHPX_STEP_NONE) { @@ -2054,9 +2053,9 @@ static int whpx_vcpu_run(CPUState *cpu) static void do_whpx_cpu_synchronize_state(CPUState *cpu, run_on_cpu_data arg) { - if (!cpu->accel->dirty) { + if (!cpu->vcpu_dirty) { whpx_get_registers(cpu); - cpu->accel->dirty = true; + cpu->vcpu_dirty = true; } } @@ -2064,20 +2063,20 @@ static void do_whpx_cpu_synchronize_post_reset(CPUState *cpu, run_on_cpu_data arg) { whpx_set_registers(cpu, WHPX_SET_RESET_STATE); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } static void do_whpx_cpu_synchronize_post_init(CPUState *cpu, run_on_cpu_data arg) { whpx_set_registers(cpu, WHPX_SET_FULL_STATE); - cpu->accel->dirty = false; + cpu->vcpu_dirty = false; } static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu, run_on_cpu_data arg) { - cpu->accel->dirty = true; + cpu->vcpu_dirty = true; } /* @@ -2086,7 +2085,7 @@ static void do_whpx_cpu_synchronize_pre_loadvm(CPUState *cpu, void whpx_cpu_synchronize_state(CPUState *cpu) { - if (!cpu->accel->dirty) { + if (!cpu->vcpu_dirty) { run_on_cpu(cpu, do_whpx_cpu_synchronize_state, RUN_ON_CPU_NULL); } } @@ -2226,7 +2225,7 @@ int whpx_init_vcpu(CPUState *cpu) } vcpu->interruptable = true; - vcpu->dirty = true; + cpu->vcpu_dirty = true; cpu->accel = vcpu; max_vcpu_index = max(max_vcpu_index, cpu->cpu_index); qemu_add_vm_change_state_handler(whpx_cpu_update_state, env); From 888a6be77560992d17ab823f27a8297ecd7893b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Jun 2025 14:13:20 +0200 Subject: [PATCH 1918/2760] accel/kvm: Remove kvm_cpu_synchronize_state() stub MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 57038a92bb0 ("cpus: extract out kvm-specific code to accel/kvm") the kvm_cpu_synchronize_state() stub is not necessary. Fixes: e0715f6abce ("kvm: remove kvm specific functions from global includes") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-22-philmd@linaro.org> --- accel/stubs/kvm-stub.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/accel/stubs/kvm-stub.c b/accel/stubs/kvm-stub.c index b9b4427c91..68cd33ba97 100644 --- a/accel/stubs/kvm-stub.c +++ b/accel/stubs/kvm-stub.c @@ -29,10 +29,6 @@ void kvm_flush_coalesced_mmio_buffer(void) { } -void kvm_cpu_synchronize_state(CPUState *cpu) -{ -} - bool kvm_has_sync_mmu(void) { return false; From 8e825755c5ddcdfd91e585d850aecee434d2ff63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Jun 2025 16:09:08 +0200 Subject: [PATCH 1919/2760] accel/system: Document cpu_synchronize_state() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-23-philmd@linaro.org> --- include/system/accel-ops.h | 8 ++++++++ include/system/hw_accel.h | 11 ++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/include/system/accel-ops.h b/include/system/accel-ops.h index 4c99d25aef..55f91cea25 100644 --- a/include/system/accel-ops.h +++ b/include/system/accel-ops.h @@ -42,6 +42,14 @@ struct AccelOpsClass { void (*synchronize_post_reset)(CPUState *cpu); void (*synchronize_post_init)(CPUState *cpu); + /** + * synchronize_state: + * synchronize_pre_loadvm: + * @cpu: The vCPU to synchronize. + * + * Request to synchronize QEMU vCPU registers from the hardware accelerator + * (the hardware accelerator is the reference). + */ void (*synchronize_state)(CPUState *cpu); void (*synchronize_pre_loadvm)(CPUState *cpu); void (*synchronize_pre_resume)(bool step_pending); diff --git a/include/system/hw_accel.h b/include/system/hw_accel.h index 380e9e640b..574c973840 100644 --- a/include/system/hw_accel.h +++ b/include/system/hw_accel.h @@ -17,9 +17,18 @@ #include "system/whpx.h" #include "system/nvmm.h" +/** + * cpu_synchronize_state: + * cpu_synchronize_pre_loadvm: + * @cpu: The vCPU to synchronize. + * + * Request to synchronize QEMU vCPU registers from the hardware accelerator + * (the hardware accelerator is the reference). + */ void cpu_synchronize_state(CPUState *cpu); +void cpu_synchronize_pre_loadvm(CPUState *cpu); + void cpu_synchronize_post_reset(CPUState *cpu); void cpu_synchronize_post_init(CPUState *cpu); -void cpu_synchronize_pre_loadvm(CPUState *cpu); #endif /* QEMU_HW_ACCEL_H */ From 04bd6c3631e0184dcafb0403e0248a2052585914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Jun 2025 16:09:23 +0200 Subject: [PATCH 1920/2760] accel/system: Document cpu_synchronize_state_post_init/reset() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-24-philmd@linaro.org> --- include/system/accel-ops.h | 8 ++++++++ include/system/hw_accel.h | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/include/system/accel-ops.h b/include/system/accel-ops.h index 55f91cea25..a4e706b49c 100644 --- a/include/system/accel-ops.h +++ b/include/system/accel-ops.h @@ -40,6 +40,14 @@ struct AccelOpsClass { void (*kick_vcpu_thread)(CPUState *cpu); bool (*cpu_thread_is_idle)(CPUState *cpu); + /** + * synchronize_post_reset: + * synchronize_post_init: + * @cpu: The vCPU to synchronize. + * + * Request to synchronize QEMU vCPU registers to the hardware accelerator + * (QEMU is the reference). + */ void (*synchronize_post_reset)(CPUState *cpu); void (*synchronize_post_init)(CPUState *cpu); /** diff --git a/include/system/hw_accel.h b/include/system/hw_accel.h index 574c973840..fa9228d5d2 100644 --- a/include/system/hw_accel.h +++ b/include/system/hw_accel.h @@ -28,6 +28,14 @@ void cpu_synchronize_state(CPUState *cpu); void cpu_synchronize_pre_loadvm(CPUState *cpu); +/** + * cpu_synchronize_post_reset: + * cpu_synchronize_post_init: + * @cpu: The vCPU to synchronize. + * + * Request to synchronize QEMU vCPU registers to the hardware accelerator + * (QEMU is the reference). + */ void cpu_synchronize_post_reset(CPUState *cpu); void cpu_synchronize_post_init(CPUState *cpu); From 80a1efdedd3099cde51cabf91789e037a6af11df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Jun 2025 10:39:09 +0200 Subject: [PATCH 1921/2760] accel/nvmm: Expose nvmm_enabled() to common code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently nvmm_enabled() is restricted to target-specific code. By defining CONFIG_NVMM_IS_POSSIBLE we allow its use anywhere. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-25-philmd@linaro.org> --- MAINTAINERS | 1 + accel/stubs/meson.build | 1 + accel/stubs/nvmm-stub.c | 12 ++++++++++++ include/system/nvmm.h | 23 ++++++++++++----------- target/i386/nvmm/nvmm-all.c | 8 +------- 5 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 accel/stubs/nvmm-stub.c diff --git a/MAINTAINERS b/MAINTAINERS index b1cbfe115b..c73468b242 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -586,6 +586,7 @@ NetBSD Virtual Machine Monitor (NVMM) CPU support M: Reinoud Zandijk S: Maintained F: include/system/nvmm.h +F: accel/stubs/nvmm-stub.c F: target/i386/nvmm/ Hosts diff --git a/accel/stubs/meson.build b/accel/stubs/meson.build index 8ca1a4529e..4c34287215 100644 --- a/accel/stubs/meson.build +++ b/accel/stubs/meson.build @@ -3,5 +3,6 @@ system_stubs_ss.add(when: 'CONFIG_XEN', if_false: files('xen-stub.c')) system_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) system_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c')) system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c')) +system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c')) specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: system_stubs_ss) diff --git a/accel/stubs/nvmm-stub.c b/accel/stubs/nvmm-stub.c new file mode 100644 index 0000000000..ec14837501 --- /dev/null +++ b/accel/stubs/nvmm-stub.c @@ -0,0 +1,12 @@ +/* + * NVMM stubs for QEMU + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/nvmm.h" + +bool nvmm_allowed; diff --git a/include/system/nvmm.h b/include/system/nvmm.h index 6971ddb3a5..7390def9ad 100644 --- a/include/system/nvmm.h +++ b/include/system/nvmm.h @@ -13,17 +13,18 @@ #define QEMU_NVMM_H #ifdef COMPILING_PER_TARGET - -#ifdef CONFIG_NVMM - -int nvmm_enabled(void); - -#else /* CONFIG_NVMM */ - -#define nvmm_enabled() (0) - -#endif /* CONFIG_NVMM */ - +# ifdef CONFIG_NVMM +# define CONFIG_NVMM_IS_POSSIBLE +# endif /* !CONFIG_NVMM */ +#else +# define CONFIG_NVMM_IS_POSSIBLE #endif /* COMPILING_PER_TARGET */ +#ifdef CONFIG_NVMM_IS_POSSIBLE +extern bool nvmm_allowed; +#define nvmm_enabled() (nvmm_allowed) +#else /* !CONFIG_NVMM_IS_POSSIBLE */ +#define nvmm_enabled() 0 +#endif /* !CONFIG_NVMM_IS_POSSIBLE */ + #endif /* QEMU_NVMM_H */ diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index aea61a6fd2..2df49d7eeb 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -46,7 +46,7 @@ struct qemu_machine { /* -------------------------------------------------------------------------- */ -static bool nvmm_allowed; +bool nvmm_allowed; static struct qemu_machine qemu_mach; static struct nvmm_machine * @@ -1192,12 +1192,6 @@ nvmm_accel_init(MachineState *ms) return 0; } -int -nvmm_enabled(void) -{ - return nvmm_allowed; -} - static void nvmm_accel_class_init(ObjectClass *oc, const void *data) { From a9c2afd74b887b4775f425b72be1888220594bb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 16 Jun 2025 10:40:00 +0200 Subject: [PATCH 1922/2760] accel/whpx: Expose whpx_enabled() to common code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently whpx_enabled() is restricted to target-specific code. By defining CONFIG_WHPX_IS_POSSIBLE we allow its use anywhere. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250703173248.44995-26-philmd@linaro.org> --- MAINTAINERS | 1 + accel/stubs/meson.build | 1 + accel/stubs/whpx-stub.c | 12 ++++++++++++ include/system/whpx.h | 23 ++++++++++++----------- target/i386/whpx/whpx-all.c | 7 +------ 5 files changed, 27 insertions(+), 17 deletions(-) create mode 100644 accel/stubs/whpx-stub.c diff --git a/MAINTAINERS b/MAINTAINERS index c73468b242..0fd8b2a4e6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -540,6 +540,7 @@ WHPX CPUs M: Sunil Muthuswamy S: Supported F: target/i386/whpx/ +F: accel/stubs/whpx-stub.c F: include/system/whpx.h X86 Instruction Emulator diff --git a/accel/stubs/meson.build b/accel/stubs/meson.build index 4c34287215..9dfc4f9dda 100644 --- a/accel/stubs/meson.build +++ b/accel/stubs/meson.build @@ -4,5 +4,6 @@ system_stubs_ss.add(when: 'CONFIG_KVM', if_false: files('kvm-stub.c')) system_stubs_ss.add(when: 'CONFIG_TCG', if_false: files('tcg-stub.c')) system_stubs_ss.add(when: 'CONFIG_HVF', if_false: files('hvf-stub.c')) system_stubs_ss.add(when: 'CONFIG_NVMM', if_false: files('nvmm-stub.c')) +system_stubs_ss.add(when: 'CONFIG_WHPX', if_false: files('whpx-stub.c')) specific_ss.add_all(when: ['CONFIG_SYSTEM_ONLY'], if_true: system_stubs_ss) diff --git a/accel/stubs/whpx-stub.c b/accel/stubs/whpx-stub.c new file mode 100644 index 0000000000..c564c89fd0 --- /dev/null +++ b/accel/stubs/whpx-stub.c @@ -0,0 +1,12 @@ +/* + * WHPX stubs for QEMU + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "system/whpx.h" + +bool whpx_allowed; diff --git a/include/system/whpx.h b/include/system/whpx.h index 00ff409b68..00f6a3e523 100644 --- a/include/system/whpx.h +++ b/include/system/whpx.h @@ -16,19 +16,20 @@ #define QEMU_WHPX_H #ifdef COMPILING_PER_TARGET +# ifdef CONFIG_WHPX +# define CONFIG_WHPX_IS_POSSIBLE +# endif /* !CONFIG_WHPX */ +#else +# define CONFIG_WHPX_IS_POSSIBLE +#endif /* COMPILING_PER_TARGET */ -#ifdef CONFIG_WHPX - -int whpx_enabled(void); +#ifdef CONFIG_WHPX_IS_POSSIBLE +extern bool whpx_allowed; +#define whpx_enabled() (whpx_allowed) bool whpx_apic_in_platform(void); - -#else /* CONFIG_WHPX */ - -#define whpx_enabled() (0) +#else /* !CONFIG_WHPX_IS_POSSIBLE */ +#define whpx_enabled() 0 #define whpx_apic_in_platform() (0) - -#endif /* CONFIG_WHPX */ - -#endif /* COMPILING_PER_TARGET */ +#endif /* !CONFIG_WHPX_IS_POSSIBLE */ #endif /* QEMU_WHPX_H */ diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 5001afad3a..94fd5fc784 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -242,7 +242,7 @@ struct AccelCPUState { WHV_RUN_VP_EXIT_CONTEXT exit_ctx; }; -static bool whpx_allowed; +bool whpx_allowed; static bool whp_dispatch_initialized; static HMODULE hWinHvPlatform, hWinHvEmulation; static uint32_t max_vcpu_index; @@ -2688,11 +2688,6 @@ static int whpx_accel_init(MachineState *ms) return ret; } -int whpx_enabled(void) -{ - return whpx_allowed; -} - bool whpx_apic_in_platform(void) { return whpx_global.apic_in_platform; } From 20a0181600d61f5e58a087be14bd63c40aca2cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Jun 2025 16:20:10 +0200 Subject: [PATCH 1923/2760] accel/dummy: Extract 'dummy-cpus.h' header from 'system/cpus.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'dummy' helpers are specific to accelerator implementations, no need to expose them via "system/cpus.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-27-philmd@linaro.org> --- MAINTAINERS | 1 + accel/dummy-cpus.c | 1 + accel/dummy-cpus.h | 14 ++++++++++++++ accel/qtest/qtest.c | 1 + accel/xen/xen-all.c | 1 + include/system/cpus.h | 5 ----- 6 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 accel/dummy-cpus.h diff --git a/MAINTAINERS b/MAINTAINERS index 0fd8b2a4e6..fca98e1219 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -503,6 +503,7 @@ F: include/system/accel-*.h F: include/system/cpus.h F: include/accel/accel-cpu*.h F: accel/accel-*.? +F: accel/dummy-cpus.? F: accel/Makefile.objs F: accel/stubs/Makefile.objs F: cpu-common.c diff --git a/accel/dummy-cpus.c b/accel/dummy-cpus.c index 867276144f..03cfc0fa01 100644 --- a/accel/dummy-cpus.c +++ b/accel/dummy-cpus.c @@ -17,6 +17,7 @@ #include "qemu/guest-random.h" #include "qemu/main-loop.h" #include "hw/core/cpu.h" +#include "accel/dummy-cpus.h" static void *dummy_cpu_thread_fn(void *arg) { diff --git a/accel/dummy-cpus.h b/accel/dummy-cpus.h new file mode 100644 index 0000000000..d18dd0fdc5 --- /dev/null +++ b/accel/dummy-cpus.h @@ -0,0 +1,14 @@ +/* + * Dummy cpu thread code + * + * Copyright IBM, Corp. 2011 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ACCEL_DUMMY_CPUS_H +#define ACCEL_DUMMY_CPUS_H + +void dummy_start_vcpu_thread(CPUState *cpu); + +#endif diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index 92bed9264c..612cede160 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -24,6 +24,7 @@ #include "qemu/guest-random.h" #include "qemu/main-loop.h" #include "hw/core/cpu.h" +#include "accel/dummy-cpus.h" static int64_t qtest_clock_counter; diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index de52a8f882..c150dd43ca 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -18,6 +18,7 @@ #include "hw/xen/xen_igd.h" #include "chardev/char.h" #include "qemu/accel.h" +#include "accel/dummy-cpus.h" #include "system/accel-ops.h" #include "system/cpus.h" #include "system/xen.h" diff --git a/include/system/cpus.h b/include/system/cpus.h index 3226c765d0..69be6a77a7 100644 --- a/include/system/cpus.h +++ b/include/system/cpus.h @@ -7,11 +7,6 @@ void cpus_register_accel(const AccelOpsClass *i); /* return registers ops */ const AccelOpsClass *cpus_get_accel(void); -/* accel/dummy-cpus.c */ - -/* Create a dummy vcpu for AccelOpsClass->create_vcpu_thread */ -void dummy_start_vcpu_thread(CPUState *); - /* interface available for cpus accelerator threads */ /* For temporary buffers for forming a name */ From b64bb17d14a62ea04b605f81daec8a5a4fad3be4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 12 Jun 2025 14:45:19 +0200 Subject: [PATCH 1924/2760] accel: Expose and register generic_handle_interrupt() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to dispatch over AccelOpsClass::handle_interrupt(), we need it always defined, not calling a hidden handler under the hood. Make AccelOpsClass::handle_interrupt() mandatory. Expose generic_handle_interrupt() prototype and register it for each accelerator. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Zhao Liu Reviewed-by: Richard Henderson Reviewed-by: Xiaoyao Li Reviewed-by: Mads Ynddal Message-Id: <20250703173248.44995-29-philmd@linaro.org> --- accel/hvf/hvf-accel-ops.c | 1 + accel/kvm/kvm-accel-ops.c | 1 + accel/qtest/qtest.c | 1 + accel/xen/xen-all.c | 1 + include/system/accel-ops.h | 3 +++ system/cpus.c | 10 ++++------ target/i386/nvmm/nvmm-accel-ops.c | 1 + target/i386/whpx/whpx-accel-ops.c | 1 + 8 files changed, 13 insertions(+), 6 deletions(-) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 395b5a8e1c..be8724ac89 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -353,6 +353,7 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, const void *data) ops->create_vcpu_thread = hvf_start_vcpu_thread; ops->kick_vcpu_thread = hvf_kick_vcpu_thread; + ops->handle_interrupt = generic_handle_interrupt; ops->synchronize_post_reset = hvf_cpu_synchronize_post_reset; ops->synchronize_post_init = hvf_cpu_synchronize_post_init; diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index e5c15449aa..0eafc902c3 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -101,6 +101,7 @@ static void kvm_accel_ops_class_init(ObjectClass *oc, const void *data) ops->synchronize_post_init = kvm_cpu_synchronize_post_init; ops->synchronize_state = kvm_cpu_synchronize_state; ops->synchronize_pre_loadvm = kvm_cpu_synchronize_pre_loadvm; + ops->handle_interrupt = generic_handle_interrupt; #ifdef TARGET_KVM_HAVE_GUEST_DEBUG ops->update_guest_debug = kvm_update_guest_debug_ops; diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index 612cede160..5474ce7313 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -67,6 +67,7 @@ static void qtest_accel_ops_class_init(ObjectClass *oc, const void *data) ops->create_vcpu_thread = dummy_start_vcpu_thread; ops->get_virtual_clock = qtest_get_virtual_clock; ops->set_virtual_clock = qtest_set_virtual_clock; + ops->handle_interrupt = generic_handle_interrupt; }; static const TypeInfo qtest_accel_ops_type = { diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index c150dd43ca..c12c22de78 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -153,6 +153,7 @@ static void xen_accel_ops_class_init(ObjectClass *oc, const void *data) AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); ops->create_vcpu_thread = dummy_start_vcpu_thread; + ops->handle_interrupt = generic_handle_interrupt; } static const TypeInfo xen_accel_ops_type = { diff --git a/include/system/accel-ops.h b/include/system/accel-ops.h index a4e706b49c..e775ecc348 100644 --- a/include/system/accel-ops.h +++ b/include/system/accel-ops.h @@ -62,6 +62,7 @@ struct AccelOpsClass { void (*synchronize_pre_loadvm)(CPUState *cpu); void (*synchronize_pre_resume)(bool step_pending); + /* handle_interrupt is mandatory. */ void (*handle_interrupt)(CPUState *cpu, int mask); /** @@ -86,4 +87,6 @@ struct AccelOpsClass { void (*remove_all_breakpoints)(CPUState *cpu); }; +void generic_handle_interrupt(CPUState *cpu, int mask); + #endif /* ACCEL_OPS_H */ diff --git a/system/cpus.c b/system/cpus.c index a43e0e4e79..0d0eec82a2 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -254,7 +254,7 @@ int64_t cpus_get_elapsed_ticks(void) return cpu_get_ticks(); } -static void generic_handle_interrupt(CPUState *cpu, int mask) +void generic_handle_interrupt(CPUState *cpu, int mask) { cpu->interrupt_request |= mask; @@ -267,11 +267,7 @@ void cpu_interrupt(CPUState *cpu, int mask) { g_assert(bql_locked()); - if (cpus_accel->handle_interrupt) { - cpus_accel->handle_interrupt(cpu, mask); - } else { - generic_handle_interrupt(cpu, mask); - } + cpus_accel->handle_interrupt(cpu, mask); } /* @@ -680,6 +676,8 @@ void cpus_register_accel(const AccelOpsClass *ops) { assert(ops != NULL); assert(ops->create_vcpu_thread != NULL); /* mandatory */ + assert(ops->handle_interrupt); + cpus_accel = ops; } diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index 21443078b7..a5517b0abf 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -87,6 +87,7 @@ static void nvmm_accel_ops_class_init(ObjectClass *oc, const void *data) ops->create_vcpu_thread = nvmm_start_vcpu_thread; ops->kick_vcpu_thread = nvmm_kick_vcpu_thread; + ops->handle_interrupt = generic_handle_interrupt; ops->synchronize_post_reset = nvmm_cpu_synchronize_post_reset; ops->synchronize_post_init = nvmm_cpu_synchronize_post_init; diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index b8bebe403c..31cf15f004 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -90,6 +90,7 @@ static void whpx_accel_ops_class_init(ObjectClass *oc, const void *data) ops->create_vcpu_thread = whpx_start_vcpu_thread; ops->kick_vcpu_thread = whpx_kick_vcpu_thread; ops->cpu_thread_is_idle = whpx_vcpu_thread_is_idle; + ops->handle_interrupt = generic_handle_interrupt; ops->synchronize_post_reset = whpx_cpu_synchronize_post_reset; ops->synchronize_post_init = whpx_cpu_synchronize_post_init; From 487b25c9d93add2e0e58275d7c1ef89810fad763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jun 2025 12:07:47 +0200 Subject: [PATCH 1925/2760] accel: Keep reference to AccelOpsClass in AccelClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow dereferencing AccelOpsClass outside of accel/accel-system.c. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-30-philmd@linaro.org> --- accel/accel-system.c | 3 ++- accel/tcg/tcg-accel-ops.c | 4 +++- include/qemu/accel.h | 3 +++ include/system/accel-ops.h | 3 ++- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/accel/accel-system.c b/accel/accel-system.c index a0f562ae9f..64bc991b1c 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -85,8 +85,9 @@ void accel_init_ops_interfaces(AccelClass *ac) * non-NULL create_vcpu_thread operation. */ ops = ACCEL_OPS_CLASS(oc); + ac->ops = ops; if (ops->ops_init) { - ops->ops_init(ops); + ops->ops_init(ac); } cpus_register_accel(ops); } diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 71776bc29c..279dbfa1cf 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -199,8 +199,10 @@ static inline void tcg_remove_all_breakpoints(CPUState *cpu) cpu_watchpoint_remove_all(cpu, BP_GDB); } -static void tcg_accel_ops_init(AccelOpsClass *ops) +static void tcg_accel_ops_init(AccelClass *ac) { + AccelOpsClass *ops = ac->ops; + if (qemu_tcg_mttcg_enabled()) { ops->create_vcpu_thread = mttcg_start_vcpu_thread; ops->kick_vcpu_thread = mttcg_kick_vcpu_thread; diff --git a/include/qemu/accel.h b/include/qemu/accel.h index fbd3d897fe..9dea314542 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -37,6 +37,9 @@ typedef struct AccelClass { /*< public >*/ const char *name; + /* Cached by accel_init_ops_interfaces() when created */ + AccelOpsClass *ops; + int (*init_machine)(MachineState *ms); bool (*cpu_common_realize)(CPUState *cpu, Error **errp); void (*cpu_common_unrealize)(CPUState *cpu); diff --git a/include/system/accel-ops.h b/include/system/accel-ops.h index e775ecc348..a786c7d478 100644 --- a/include/system/accel-ops.h +++ b/include/system/accel-ops.h @@ -10,6 +10,7 @@ #ifndef ACCEL_OPS_H #define ACCEL_OPS_H +#include "qemu/accel.h" #include "exec/vaddr.h" #include "qom/object.h" @@ -31,7 +32,7 @@ struct AccelOpsClass { /*< public >*/ /* initialization function called when accel is chosen */ - void (*ops_init)(AccelOpsClass *ops); + void (*ops_init)(AccelClass *ac); bool (*cpus_are_resettable)(void); void (*cpu_reset_hold)(CPUState *cpu); From fa8f6f25ea8dc3a1123deaf53cf4907005880e52 Mon Sep 17 00:00:00 2001 From: William Kosasih Date: Thu, 3 Jul 2025 18:25:54 +0930 Subject: [PATCH 1926/2760] target/arm: Bring VLSTM/VLLDM helper store/load closer to the ARM pseudocode This patch brings the VLSTM and VLLDM helper functions closer to the ARM pseudocode by adding MO_ALIGN to the MemOpIdx of the associated store (`cpu_stl_mmu`) operations and load (`cpu_ldl_mmu`) operations. Note that this is not a bug fix: an 8-byte alignment check already exists and remains in place, enforcing stricter alignment than the 4 bytes requirement in the individual loads and stores. This change merely makes the helper implementations closer to the ARM pseudocode. That said, as a side effect, the MMU index is now resolved once instead of on every `cpu_*_data_ra` call, reducing redundant lookups Signed-off-by: William Kosasih Reviewed-by: Richard Henderson Message-id: 20250703085604.154449-2-kosasihwilliam4@gmail.com Signed-off-by: Peter Maydell --- target/arm/tcg/m_helper.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index 6614719832..251e12edf9 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -1048,6 +1048,9 @@ void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) bool s = env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_S_MASK; bool lspact = env->v7m.fpccr[s] & R_V7M_FPCCR_LSPACT_MASK; uintptr_t ra = GETPC(); + ARMMMUIdx mmu_idx = arm_mmu_idx(env); + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, + arm_to_core_mmu_idx(mmu_idx)); assert(env->v7m.secure); @@ -1073,7 +1076,7 @@ void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) * Note that we do not use v7m_stack_write() here, because the * accesses should not set the FSR bits for stacking errors if they * fail. (In pseudocode terms, they are AccType_NORMAL, not AccType_STACK - * or AccType_LAZYFP). Faults in cpu_stl_data_ra() will throw exceptions + * or AccType_LAZYFP). Faults in cpu_stl_mmu() will throw exceptions * and longjmp out. */ if (!(env->v7m.fpccr[M_REG_S] & R_V7M_FPCCR_LSPEN_MASK)) { @@ -1089,12 +1092,12 @@ void HELPER(v7m_vlstm)(CPUARMState *env, uint32_t fptr) if (i >= 16) { faddr += 8; /* skip the slot for the FPSCR */ } - cpu_stl_data_ra(env, faddr, slo, ra); - cpu_stl_data_ra(env, faddr + 4, shi, ra); + cpu_stl_mmu(env, faddr, slo, oi, ra); + cpu_stl_mmu(env, faddr + 4, shi, oi, ra); } - cpu_stl_data_ra(env, fptr + 0x40, vfp_get_fpscr(env), ra); + cpu_stl_mmu(env, fptr + 0x40, vfp_get_fpscr(env), oi, ra); if (cpu_isar_feature(aa32_mve, cpu)) { - cpu_stl_data_ra(env, fptr + 0x44, env->v7m.vpr, ra); + cpu_stl_mmu(env, fptr + 0x44, env->v7m.vpr, oi, ra); } /* @@ -1121,6 +1124,9 @@ void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) { ARMCPU *cpu = env_archcpu(env); uintptr_t ra = GETPC(); + ARMMMUIdx mmu_idx = arm_mmu_idx(env); + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, + arm_to_core_mmu_idx(mmu_idx)); /* fptr is the value of Rn, the frame pointer we load the FP regs from */ assert(env->v7m.secure); @@ -1155,16 +1161,16 @@ void HELPER(v7m_vlldm)(CPUARMState *env, uint32_t fptr) faddr += 8; /* skip the slot for the FPSCR and VPR */ } - slo = cpu_ldl_data_ra(env, faddr, ra); - shi = cpu_ldl_data_ra(env, faddr + 4, ra); + slo = cpu_ldl_mmu(env, faddr, oi, ra); + shi = cpu_ldl_mmu(env, faddr + 4, oi, ra); dn = (uint64_t) shi << 32 | slo; *aa32_vfp_dreg(env, i / 2) = dn; } - fpscr = cpu_ldl_data_ra(env, fptr + 0x40, ra); + fpscr = cpu_ldl_mmu(env, fptr + 0x40, oi, ra); vfp_set_fpscr(env, fpscr); if (cpu_isar_feature(aa32_mve, cpu)) { - env->v7m.vpr = cpu_ldl_data_ra(env, fptr + 0x44, ra); + env->v7m.vpr = cpu_ldl_mmu(env, fptr + 0x44, oi, ra); } } From 94b07a46d1dbf1c31373da14e14f88b65ff94181 Mon Sep 17 00:00:00 2001 From: William Kosasih Date: Thu, 3 Jul 2025 18:25:55 +0930 Subject: [PATCH 1927/2760] target/arm: Fix BLXNS helper store alignment checks This patch adds alignment checks in the store operations (when stacking the return pc and psr) in the BLXNS instruction. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1154 Signed-off-by: William Kosasih Reviewed-by: Richard Henderson Message-id: 20250703085604.154449-3-kosasihwilliam4@gmail.com Signed-off-by: Peter Maydell --- target/arm/tcg/m_helper.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index 251e12edf9..f342d93489 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -632,8 +632,11 @@ void HELPER(v7m_blxns)(CPUARMState *env, uint32_t dest) } /* Note that these stores can throw exceptions on MPU faults */ - cpu_stl_data_ra(env, sp, nextinst, GETPC()); - cpu_stl_data_ra(env, sp + 4, saved_psr, GETPC()); + ARMMMUIdx mmu_idx = arm_mmu_idx(env); + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, + arm_to_core_mmu_idx(mmu_idx)); + cpu_stl_mmu(env, sp, nextinst, oi, GETPC()); + cpu_stl_mmu(env, sp + 4, saved_psr, oi, GETPC()); env->regs[13] = sp; env->regs[14] = 0xfeffffff; From c4940752ae12b82938c95a9cfe0864b725ac85f1 Mon Sep 17 00:00:00 2001 From: William Kosasih Date: Thu, 3 Jul 2025 18:25:56 +0930 Subject: [PATCH 1928/2760] target/arm: Fix function_return helper load alignment checks This patch adds alignment checks in the load operations (when unstacking the return pc and psr) in the FunctionReturn pseudocode. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1154 Signed-off-by: William Kosasih Reviewed-by: Richard Henderson Message-id: 20250703085604.154449-4-kosasihwilliam4@gmail.com Signed-off-by: Peter Maydell --- target/arm/tcg/m_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/m_helper.c b/target/arm/tcg/m_helper.c index f342d93489..28307b5615 100644 --- a/target/arm/tcg/m_helper.c +++ b/target/arm/tcg/m_helper.c @@ -1946,7 +1946,7 @@ static bool do_v7m_function_return(ARMCPU *cpu) * do them as secure, so work out what MMU index that is. */ mmu_idx = arm_v7m_mmu_idx_for_secstate(env, true); - oi = make_memop_idx(MO_LEUL, arm_to_core_mmu_idx(mmu_idx)); + oi = make_memop_idx(MO_LEUL | MO_ALIGN, arm_to_core_mmu_idx(mmu_idx)); newpc = cpu_ldl_mmu(env, frameptr, oi, 0); newpsr = cpu_ldl_mmu(env, frameptr + 4, oi, 0); From 56249f3e6ceb4076b9d1b2500450420cf18346e2 Mon Sep 17 00:00:00 2001 From: William Kosasih Date: Thu, 3 Jul 2025 18:25:57 +0930 Subject: [PATCH 1929/2760] target/arm: Fix VLDR helper load alignment checks This patch adds alignment checks in the load operations in the VLDR instruction. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1154 Signed-off-by: William Kosasih Reviewed-by: Richard Henderson Message-id: 20250703085604.154449-5-kosasihwilliam4@gmail.com Signed-off-by: Peter Maydell --- target/arm/tcg/mve_helper.c | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 506d1c3475..8834523c23 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -148,13 +148,15 @@ static void mve_advance_vpt(CPUARMState *env) } /* For loads, predicated lanes are zeroed instead of keeping their old values */ -#define DO_VLDR(OP, MSIZE, LDTYPE, ESIZE, TYPE) \ +#define DO_VLDR(OP, MFLAG, MSIZE, MTYPE, LDTYPE, ESIZE, TYPE) \ void HELPER(mve_##OP)(CPUARMState *env, void *vd, uint32_t addr) \ { \ TYPE *d = vd; \ uint16_t mask = mve_element_mask(env); \ uint16_t eci_mask = mve_eci_mask(env); \ unsigned b, e; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MFLAG | MO_ALIGN, mmu_idx); \ /* \ * R_SXTM allows the dest reg to become UNKNOWN for abandoned \ * beats so we don't care if we update part of the dest and \ @@ -163,7 +165,7 @@ static void mve_advance_vpt(CPUARMState *env) for (b = 0, e = 0; b < 16; b += ESIZE, e++) { \ if (eci_mask & (1 << b)) { \ d[H##ESIZE(e)] = (mask & (1 << b)) ? \ - cpu_##LDTYPE##_data_ra(env, addr, GETPC()) : 0; \ + (MTYPE)cpu_##LDTYPE##_mmu(env, addr, oi, GETPC()) : 0;\ } \ addr += MSIZE; \ } \ @@ -185,20 +187,20 @@ static void mve_advance_vpt(CPUARMState *env) mve_advance_vpt(env); \ } -DO_VLDR(vldrb, 1, ldub, 1, uint8_t) -DO_VLDR(vldrh, 2, lduw, 2, uint16_t) -DO_VLDR(vldrw, 4, ldl, 4, uint32_t) +DO_VLDR(vldrb, MO_UB, 1, uint8_t, ldb, 1, uint8_t) +DO_VLDR(vldrh, MO_TEUW, 2, uint16_t, ldw, 2, uint16_t) +DO_VLDR(vldrw, MO_TEUL, 4, uint32_t, ldl, 4, uint32_t) DO_VSTR(vstrb, 1, stb, 1, uint8_t) DO_VSTR(vstrh, 2, stw, 2, uint16_t) DO_VSTR(vstrw, 4, stl, 4, uint32_t) -DO_VLDR(vldrb_sh, 1, ldsb, 2, int16_t) -DO_VLDR(vldrb_sw, 1, ldsb, 4, int32_t) -DO_VLDR(vldrb_uh, 1, ldub, 2, uint16_t) -DO_VLDR(vldrb_uw, 1, ldub, 4, uint32_t) -DO_VLDR(vldrh_sw, 2, ldsw, 4, int32_t) -DO_VLDR(vldrh_uw, 2, lduw, 4, uint32_t) +DO_VLDR(vldrb_sh, MO_SB, 1, int8_t, ldb, 2, int16_t) +DO_VLDR(vldrb_sw, MO_SB, 1, int8_t, ldb, 4, int32_t) +DO_VLDR(vldrb_uh, MO_UB, 1, uint8_t, ldb, 2, uint16_t) +DO_VLDR(vldrb_uw, MO_UB, 1, uint8_t, ldb, 4, uint32_t) +DO_VLDR(vldrh_sw, MO_TESW, 2, int16_t, ldw, 4, int32_t) +DO_VLDR(vldrh_uw, MO_TEUW, 2, uint16_t, ldw, 4, uint32_t) DO_VSTR(vstrb_h, 1, stb, 2, int16_t) DO_VSTR(vstrb_w, 1, stb, 4, int32_t) From c8bde49179be1196d8ccfcfdc6269ce3861053a5 Mon Sep 17 00:00:00 2001 From: William Kosasih Date: Thu, 3 Jul 2025 18:25:58 +0930 Subject: [PATCH 1930/2760] target/arm: Fix VSTR helper store alignment checks This patch adds alignment checks in the store operations in the VSTR instruction. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1154 Signed-off-by: William Kosasih Reviewed-by: Richard Henderson Message-id: 20250703085604.154449-6-kosasihwilliam4@gmail.com Signed-off-by: Peter Maydell --- target/arm/tcg/mve_helper.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 8834523c23..9587f2da7d 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -172,15 +172,17 @@ static void mve_advance_vpt(CPUARMState *env) mve_advance_vpt(env); \ } -#define DO_VSTR(OP, MSIZE, STTYPE, ESIZE, TYPE) \ +#define DO_VSTR(OP, MFLAG, MSIZE, STTYPE, ESIZE, TYPE) \ void HELPER(mve_##OP)(CPUARMState *env, void *vd, uint32_t addr) \ { \ TYPE *d = vd; \ uint16_t mask = mve_element_mask(env); \ unsigned b, e; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MFLAG | MO_ALIGN, mmu_idx); \ for (b = 0, e = 0; b < 16; b += ESIZE, e++) { \ if (mask & (1 << b)) { \ - cpu_##STTYPE##_data_ra(env, addr, d[H##ESIZE(e)], GETPC()); \ + cpu_##STTYPE##_mmu(env, addr, d[H##ESIZE(e)], oi, GETPC()); \ } \ addr += MSIZE; \ } \ @@ -191,9 +193,9 @@ DO_VLDR(vldrb, MO_UB, 1, uint8_t, ldb, 1, uint8_t) DO_VLDR(vldrh, MO_TEUW, 2, uint16_t, ldw, 2, uint16_t) DO_VLDR(vldrw, MO_TEUL, 4, uint32_t, ldl, 4, uint32_t) -DO_VSTR(vstrb, 1, stb, 1, uint8_t) -DO_VSTR(vstrh, 2, stw, 2, uint16_t) -DO_VSTR(vstrw, 4, stl, 4, uint32_t) +DO_VSTR(vstrb, MO_UB, 1, stb, 1, uint8_t) +DO_VSTR(vstrh, MO_TEUW, 2, stw, 2, uint16_t) +DO_VSTR(vstrw, MO_TEUL, 4, stl, 4, uint32_t) DO_VLDR(vldrb_sh, MO_SB, 1, int8_t, ldb, 2, int16_t) DO_VLDR(vldrb_sw, MO_SB, 1, int8_t, ldb, 4, int32_t) @@ -202,9 +204,9 @@ DO_VLDR(vldrb_uw, MO_UB, 1, uint8_t, ldb, 4, uint32_t) DO_VLDR(vldrh_sw, MO_TESW, 2, int16_t, ldw, 4, int32_t) DO_VLDR(vldrh_uw, MO_TEUW, 2, uint16_t, ldw, 4, uint32_t) -DO_VSTR(vstrb_h, 1, stb, 2, int16_t) -DO_VSTR(vstrb_w, 1, stb, 4, int32_t) -DO_VSTR(vstrh_w, 2, stw, 4, int32_t) +DO_VSTR(vstrb_h, MO_UB, 1, stb, 2, int16_t) +DO_VSTR(vstrb_w, MO_UB, 1, stb, 4, int32_t) +DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) #undef DO_VLDR #undef DO_VSTR From f8436889a0c8468b5cb7b881a5d62283a27c44b6 Mon Sep 17 00:00:00 2001 From: William Kosasih Date: Thu, 3 Jul 2025 18:25:59 +0930 Subject: [PATCH 1931/2760] target/arm: Fix VLDR_SG helper load alignment checks This patch adds alignment checks in the load operations in the VLDR_SG instructions. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1154 Signed-off-by: William Kosasih Reviewed-by: Richard Henderson Message-id: 20250703085604.154449-7-kosasihwilliam4@gmail.com Signed-off-by: Peter Maydell --- target/arm/tcg/mve_helper.c | 42 ++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 9587f2da7d..633f511a7e 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -218,7 +218,7 @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) * For loads, predicated lanes are zeroed instead of retaining * their previous values. */ -#define DO_VLDR_SG(OP, LDTYPE, ESIZE, TYPE, OFFTYPE, ADDRFN, WB) \ +#define DO_VLDR_SG(OP, MFLAG, MTYPE, LDTYPE, ESIZE, TYPE, OFFTYPE, ADDRFN, WB)\ void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm, \ uint32_t base) \ { \ @@ -228,13 +228,15 @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) uint16_t eci_mask = mve_eci_mask(env); \ unsigned e; \ uint32_t addr; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MFLAG | MO_ALIGN, mmu_idx); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE, eci_mask >>= ESIZE) { \ if (!(eci_mask & 1)) { \ continue; \ } \ addr = ADDRFN(base, m[H##ESIZE(e)]); \ d[H##ESIZE(e)] = (mask & 1) ? \ - cpu_##LDTYPE##_data_ra(env, addr, GETPC()) : 0; \ + (MTYPE)cpu_##LDTYPE##_mmu(env, addr, oi, GETPC()) : 0; \ if (WB) { \ m[H##ESIZE(e)] = addr; \ } \ @@ -286,13 +288,15 @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) uint16_t eci_mask = mve_eci_mask(env); \ unsigned e; \ uint32_t addr; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (e = 0; e < 16 / 4; e++, mask >>= 4, eci_mask >>= 4) { \ if (!(eci_mask & 1)) { \ continue; \ } \ addr = ADDRFN(base, m[H4(e & ~1)]); \ addr += 4 * (e & 1); \ - d[H4(e)] = (mask & 1) ? cpu_ldl_data_ra(env, addr, GETPC()) : 0; \ + d[H4(e)] = (mask & 1) ? cpu_ldl_mmu(env, addr, oi, GETPC()) : 0; \ if (WB && (e & 1)) { \ m[H4(e & ~1)] = addr - 4; \ } \ @@ -331,22 +335,26 @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) #define ADDR_ADD_OSW(BASE, OFFSET) ((BASE) + ((OFFSET) << 2)) #define ADDR_ADD_OSD(BASE, OFFSET) ((BASE) + ((OFFSET) << 3)) -DO_VLDR_SG(vldrb_sg_sh, ldsb, 2, int16_t, uint16_t, ADDR_ADD, false) -DO_VLDR_SG(vldrb_sg_sw, ldsb, 4, int32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_sw, ldsw, 4, int32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrb_sg_sh, MO_SB, int8_t, ldb, 2, int16_t, uint16_t, ADDR_ADD, false) +DO_VLDR_SG(vldrb_sg_sw, MO_SB, int8_t, ldb, 4, int32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_sw, MO_TESW, int16_t, ldw, 4, int32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrb_sg_ub, ldub, 1, uint8_t, uint8_t, ADDR_ADD, false) -DO_VLDR_SG(vldrb_sg_uh, ldub, 2, uint16_t, uint16_t, ADDR_ADD, false) -DO_VLDR_SG(vldrb_sg_uw, ldub, 4, uint32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_uh, lduw, 2, uint16_t, uint16_t, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_uw, lduw, 4, uint32_t, uint32_t, ADDR_ADD, false) -DO_VLDR_SG(vldrw_sg_uw, ldl, 4, uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrb_sg_ub, MO_UB, uint8_t, ldb, 1, uint8_t, uint8_t, ADDR_ADD, false) +DO_VLDR_SG(vldrb_sg_uh, MO_UB, uint8_t, ldb, 2, uint16_t, uint16_t, ADDR_ADD, false) +DO_VLDR_SG(vldrb_sg_uw, MO_UB, uint8_t, ldb, 4, uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_uh, MO_TEUW, uint16_t, ldw, 2, uint16_t, uint16_t, ADDR_ADD, false) +DO_VLDR_SG(vldrh_sg_uw, MO_TEUW, uint16_t, ldw, 4, uint32_t, uint32_t, ADDR_ADD, false) +DO_VLDR_SG(vldrw_sg_uw, MO_TEUL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD, false) DO_VLDR64_SG(vldrd_sg_ud, ADDR_ADD, false) -DO_VLDR_SG(vldrh_sg_os_sw, ldsw, 4, int32_t, uint32_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrh_sg_os_uh, lduw, 2, uint16_t, uint16_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrh_sg_os_uw, lduw, 4, uint32_t, uint32_t, ADDR_ADD_OSH, false) -DO_VLDR_SG(vldrw_sg_os_uw, ldl, 4, uint32_t, uint32_t, ADDR_ADD_OSW, false) +DO_VLDR_SG(vldrh_sg_os_sw, MO_TESW, int16_t, ldw, 4, + int32_t, uint32_t, ADDR_ADD_OSH, false) +DO_VLDR_SG(vldrh_sg_os_uh, MO_TEUW, uint16_t, ldw, 2, + uint16_t, uint16_t, ADDR_ADD_OSH, false) +DO_VLDR_SG(vldrh_sg_os_uw, MO_TEUW, uint16_t, ldw, 4, + uint32_t, uint32_t, ADDR_ADD_OSH, false) +DO_VLDR_SG(vldrw_sg_os_uw, MO_TEUL, uint32_t, ldl, 4, + uint32_t, uint32_t, ADDR_ADD_OSW, false) DO_VLDR64_SG(vldrd_sg_os_ud, ADDR_ADD_OSD, false) DO_VSTR_SG(vstrb_sg_ub, stb, 1, uint8_t, ADDR_ADD, false) @@ -362,7 +370,7 @@ DO_VSTR_SG(vstrh_sg_os_uw, stw, 4, uint32_t, ADDR_ADD_OSH, false) DO_VSTR_SG(vstrw_sg_os_uw, stl, 4, uint32_t, ADDR_ADD_OSW, false) DO_VSTR64_SG(vstrd_sg_os_ud, ADDR_ADD_OSD, false) -DO_VLDR_SG(vldrw_sg_wb_uw, ldl, 4, uint32_t, uint32_t, ADDR_ADD, true) +DO_VLDR_SG(vldrw_sg_wb_uw, MO_TEUL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD, true) DO_VLDR64_SG(vldrd_sg_wb_ud, ADDR_ADD, true) DO_VSTR_SG(vstrw_sg_wb_uw, stl, 4, uint32_t, ADDR_ADD, true) DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) From 13ab3764ea0c0e43e2258e1755c08551c8eb8e60 Mon Sep 17 00:00:00 2001 From: William Kosasih Date: Thu, 3 Jul 2025 18:26:00 +0930 Subject: [PATCH 1932/2760] target/arm: Fix VSTR_SG helper store alignment checks This patch adds alignment checks in the store operations in the VSTR_SG instructions. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1154 Signed-off-by: William Kosasih Reviewed-by: Richard Henderson Message-id: 20250703085604.154449-8-kosasihwilliam4@gmail.com Signed-off-by: Peter Maydell --- target/arm/tcg/mve_helper.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 633f511a7e..2d21625f24 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -245,7 +245,7 @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) } /* We know here TYPE is unsigned so always the same as the offset type */ -#define DO_VSTR_SG(OP, STTYPE, ESIZE, TYPE, ADDRFN, WB) \ +#define DO_VSTR_SG(OP, MFLAG, STTYPE, ESIZE, TYPE, ADDRFN, WB) \ void HELPER(mve_##OP)(CPUARMState *env, void *vd, void *vm, \ uint32_t base) \ { \ @@ -255,13 +255,15 @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) uint16_t eci_mask = mve_eci_mask(env); \ unsigned e; \ uint32_t addr; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MFLAG | MO_ALIGN, mmu_idx); \ for (e = 0; e < 16 / ESIZE; e++, mask >>= ESIZE, eci_mask >>= ESIZE) { \ if (!(eci_mask & 1)) { \ continue; \ } \ addr = ADDRFN(base, m[H##ESIZE(e)]); \ if (mask & 1) { \ - cpu_##STTYPE##_data_ra(env, addr, d[H##ESIZE(e)], GETPC()); \ + cpu_##STTYPE##_mmu(env, addr, d[H##ESIZE(e)], oi, GETPC()); \ } \ if (WB) { \ m[H##ESIZE(e)] = addr; \ @@ -314,6 +316,8 @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) uint16_t eci_mask = mve_eci_mask(env); \ unsigned e; \ uint32_t addr; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (e = 0; e < 16 / 4; e++, mask >>= 4, eci_mask >>= 4) { \ if (!(eci_mask & 1)) { \ continue; \ @@ -321,7 +325,7 @@ DO_VSTR(vstrh_w, MO_TEUW, 2, stw, 4, int32_t) addr = ADDRFN(base, m[H4(e & ~1)]); \ addr += 4 * (e & 1); \ if (mask & 1) { \ - cpu_stl_data_ra(env, addr, d[H4(e)], GETPC()); \ + cpu_stl_mmu(env, addr, d[H4(e)], oi, GETPC()); \ } \ if (WB && (e & 1)) { \ m[H4(e & ~1)] = addr - 4; \ @@ -357,22 +361,22 @@ DO_VLDR_SG(vldrw_sg_os_uw, MO_TEUL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD_OSW, false) DO_VLDR64_SG(vldrd_sg_os_ud, ADDR_ADD_OSD, false) -DO_VSTR_SG(vstrb_sg_ub, stb, 1, uint8_t, ADDR_ADD, false) -DO_VSTR_SG(vstrb_sg_uh, stb, 2, uint16_t, ADDR_ADD, false) -DO_VSTR_SG(vstrb_sg_uw, stb, 4, uint32_t, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_uh, stw, 2, uint16_t, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_uw, stw, 4, uint32_t, ADDR_ADD, false) -DO_VSTR_SG(vstrw_sg_uw, stl, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrb_sg_ub, MO_UB, stb, 1, uint8_t, ADDR_ADD, false) +DO_VSTR_SG(vstrb_sg_uh, MO_UB, stb, 2, uint16_t, ADDR_ADD, false) +DO_VSTR_SG(vstrb_sg_uw, MO_UB, stb, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrh_sg_uh, MO_TEUW, stw, 2, uint16_t, ADDR_ADD, false) +DO_VSTR_SG(vstrh_sg_uw, MO_TEUW, stw, 4, uint32_t, ADDR_ADD, false) +DO_VSTR_SG(vstrw_sg_uw, MO_TEUL, stl, 4, uint32_t, ADDR_ADD, false) DO_VSTR64_SG(vstrd_sg_ud, ADDR_ADD, false) -DO_VSTR_SG(vstrh_sg_os_uh, stw, 2, uint16_t, ADDR_ADD_OSH, false) -DO_VSTR_SG(vstrh_sg_os_uw, stw, 4, uint32_t, ADDR_ADD_OSH, false) -DO_VSTR_SG(vstrw_sg_os_uw, stl, 4, uint32_t, ADDR_ADD_OSW, false) +DO_VSTR_SG(vstrh_sg_os_uh, MO_TEUW, stw, 2, uint16_t, ADDR_ADD_OSH, false) +DO_VSTR_SG(vstrh_sg_os_uw, MO_TEUW, stw, 4, uint32_t, ADDR_ADD_OSH, false) +DO_VSTR_SG(vstrw_sg_os_uw, MO_TEUL, stl, 4, uint32_t, ADDR_ADD_OSW, false) DO_VSTR64_SG(vstrd_sg_os_ud, ADDR_ADD_OSD, false) DO_VLDR_SG(vldrw_sg_wb_uw, MO_TEUL, uint32_t, ldl, 4, uint32_t, uint32_t, ADDR_ADD, true) DO_VLDR64_SG(vldrd_sg_wb_ud, ADDR_ADD, true) -DO_VSTR_SG(vstrw_sg_wb_uw, stl, 4, uint32_t, ADDR_ADD, true) +DO_VSTR_SG(vstrw_sg_wb_uw, MO_TEUL, stl, 4, uint32_t, ADDR_ADD, true) DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) /* From a7498d625fb18d44a42b87af6051295259142437 Mon Sep 17 00:00:00 2001 From: William Kosasih Date: Thu, 3 Jul 2025 18:26:01 +0930 Subject: [PATCH 1933/2760] target/arm: Fix VLD4 helper load alignment checks This patch adds alignment checks in the load operations in the VLD4 instruction. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1154 Signed-off-by: William Kosasih Reviewed-by: Richard Henderson Message-id: 20250703085604.154449-9-kosasihwilliam4@gmail.com Signed-off-by: Peter Maydell --- target/arm/tcg/mve_helper.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 2d21625f24..7069910db4 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -403,13 +403,15 @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) uint16_t mask = mve_eci_mask(env); \ static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ continue; \ } \ addr = base + off[beat] * 4; \ - data = cpu_ldl_le_data_ra(env, addr, GETPC()); \ + data = cpu_ldl_mmu(env, addr, oi, GETPC()); \ for (e = 0; e < 4; e++, data >>= 8) { \ uint8_t *qd = (uint8_t *)aa32_vfp_qreg(env, qnidx + e); \ qd[H1(off[beat])] = data; \ @@ -427,13 +429,15 @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) uint32_t addr, data; \ int y; /* y counts 0 2 0 2 */ \ uint16_t *qd; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (beat = 0, y = 0; beat < 4; beat++, mask >>= 4, y ^= 2) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ continue; \ } \ addr = base + off[beat] * 8 + (beat & 1) * 4; \ - data = cpu_ldl_le_data_ra(env, addr, GETPC()); \ + data = cpu_ldl_mmu(env, addr, oi, GETPC()); \ qd = (uint16_t *)aa32_vfp_qreg(env, qnidx + y); \ qd[H2(off[beat])] = data; \ data >>= 16; \ @@ -452,13 +456,15 @@ DO_VSTR64_SG(vstrd_sg_wb_ud, ADDR_ADD, true) uint32_t addr, data; \ uint32_t *qd; \ int y; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ continue; \ } \ addr = base + off[beat] * 4; \ - data = cpu_ldl_le_data_ra(env, addr, GETPC()); \ + data = cpu_ldl_mmu(env, addr, oi, GETPC()); \ y = (beat + (O1 & 2)) & 3; \ qd = (uint32_t *)aa32_vfp_qreg(env, qnidx + y); \ qd[H4(off[beat] >> 2)] = data; \ From 26aa0e36bfdbbc0f4b07b4ea51e910c4dc43e890 Mon Sep 17 00:00:00 2001 From: William Kosasih Date: Thu, 3 Jul 2025 18:26:02 +0930 Subject: [PATCH 1934/2760] target/arm: Fix VLD2 helper load alignment checks This patch adds alignment checks in the load operations in the VLD2 instruction. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1154 Signed-off-by: William Kosasih Reviewed-by: Richard Henderson Message-id: 20250703085604.154449-10-kosasihwilliam4@gmail.com Signed-off-by: Peter Maydell --- target/arm/tcg/mve_helper.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 7069910db4..7473dfad2d 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -495,13 +495,15 @@ DO_VLD4W(vld43w, 6, 7, 8, 9) static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ uint8_t *qd; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ continue; \ } \ addr = base + off[beat] * 2; \ - data = cpu_ldl_le_data_ra(env, addr, GETPC()); \ + data = cpu_ldl_mmu(env, addr, oi, GETPC()); \ for (e = 0; e < 4; e++, data >>= 8) { \ qd = (uint8_t *)aa32_vfp_qreg(env, qnidx + (e & 1)); \ qd[H1(off[beat] + (e >> 1))] = data; \ @@ -519,13 +521,15 @@ DO_VLD4W(vld43w, 6, 7, 8, 9) uint32_t addr, data; \ int e; \ uint16_t *qd; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ continue; \ } \ addr = base + off[beat] * 4; \ - data = cpu_ldl_le_data_ra(env, addr, GETPC()); \ + data = cpu_ldl_mmu(env, addr, oi, GETPC()); \ for (e = 0; e < 2; e++, data >>= 16) { \ qd = (uint16_t *)aa32_vfp_qreg(env, qnidx + e); \ qd[H2(off[beat])] = data; \ @@ -542,13 +546,15 @@ DO_VLD4W(vld43w, 6, 7, 8, 9) static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ uint32_t *qd; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ continue; \ } \ addr = base + off[beat]; \ - data = cpu_ldl_le_data_ra(env, addr, GETPC()); \ + data = cpu_ldl_mmu(env, addr, oi, GETPC()); \ qd = (uint32_t *)aa32_vfp_qreg(env, qnidx + (beat & 1)); \ qd[H4(off[beat] >> 3)] = data; \ } \ From 28b3745991add8c3488c0c984bf17a235d70329b Mon Sep 17 00:00:00 2001 From: William Kosasih Date: Thu, 3 Jul 2025 18:26:03 +0930 Subject: [PATCH 1935/2760] target/arm: Fix VST4 helper store alignment checks This patch adds alignment checks in the store operations in the VST4 instruction. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1154 Signed-off-by: William Kosasih Reviewed-by: Richard Henderson Message-id: 20250703085604.154449-11-kosasihwilliam4@gmail.com Signed-off-by: Peter Maydell --- target/arm/tcg/mve_helper.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 7473dfad2d..5273411389 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -577,6 +577,8 @@ DO_VLD2W(vld21w, 8, 12, 16, 20) uint16_t mask = mve_eci_mask(env); \ static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -588,7 +590,7 @@ DO_VLD2W(vld21w, 8, 12, 16, 20) uint8_t *qd = (uint8_t *)aa32_vfp_qreg(env, qnidx + e); \ data = (data << 8) | qd[H1(off[beat])]; \ } \ - cpu_stl_le_data_ra(env, addr, data, GETPC()); \ + cpu_stl_mmu(env, addr, data, oi, GETPC()); \ } \ } @@ -602,6 +604,8 @@ DO_VLD2W(vld21w, 8, 12, 16, 20) uint32_t addr, data; \ int y; /* y counts 0 2 0 2 */ \ uint16_t *qd; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (beat = 0, y = 0; beat < 4; beat++, mask >>= 4, y ^= 2) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -612,7 +616,7 @@ DO_VLD2W(vld21w, 8, 12, 16, 20) data = qd[H2(off[beat])]; \ qd = (uint16_t *)aa32_vfp_qreg(env, qnidx + y + 1); \ data |= qd[H2(off[beat])] << 16; \ - cpu_stl_le_data_ra(env, addr, data, GETPC()); \ + cpu_stl_mmu(env, addr, data, oi, GETPC()); \ } \ } @@ -626,6 +630,8 @@ DO_VLD2W(vld21w, 8, 12, 16, 20) uint32_t addr, data; \ uint32_t *qd; \ int y; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -635,7 +641,7 @@ DO_VLD2W(vld21w, 8, 12, 16, 20) y = (beat + (O1 & 2)) & 3; \ qd = (uint32_t *)aa32_vfp_qreg(env, qnidx + y); \ data = qd[H4(off[beat] >> 2)]; \ - cpu_stl_le_data_ra(env, addr, data, GETPC()); \ + cpu_stl_mmu(env, addr, data, oi, GETPC()); \ } \ } From c9bc9f57ffbafea6bb9fe45610b9a1e9ff33e990 Mon Sep 17 00:00:00 2001 From: William Kosasih Date: Thu, 3 Jul 2025 18:26:04 +0930 Subject: [PATCH 1936/2760] target/arm: Fix VST2 helper store alignment checks This patch adds alignment checks in the store operations in the VST2 instruction. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1154 Signed-off-by: William Kosasih Reviewed-by: Richard Henderson Message-id: 20250703085604.154449-12-kosasihwilliam4@gmail.com Signed-off-by: Peter Maydell --- target/arm/tcg/mve_helper.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 5273411389..42bb3b979b 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -669,6 +669,8 @@ DO_VST4W(vst43w, 6, 7, 8, 9) static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ uint8_t *qd; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -680,7 +682,7 @@ DO_VST4W(vst43w, 6, 7, 8, 9) qd = (uint8_t *)aa32_vfp_qreg(env, qnidx + (e & 1)); \ data = (data << 8) | qd[H1(off[beat] + (e >> 1))]; \ } \ - cpu_stl_le_data_ra(env, addr, data, GETPC()); \ + cpu_stl_mmu(env, addr, data, oi, GETPC()); \ } \ } @@ -694,6 +696,8 @@ DO_VST4W(vst43w, 6, 7, 8, 9) uint32_t addr, data; \ int e; \ uint16_t *qd; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -705,7 +709,7 @@ DO_VST4W(vst43w, 6, 7, 8, 9) qd = (uint16_t *)aa32_vfp_qreg(env, qnidx + e); \ data = (data << 16) | qd[H2(off[beat])]; \ } \ - cpu_stl_le_data_ra(env, addr, data, GETPC()); \ + cpu_stl_mmu(env, addr, data, oi, GETPC()); \ } \ } @@ -718,6 +722,8 @@ DO_VST4W(vst43w, 6, 7, 8, 9) static const uint8_t off[4] = { O1, O2, O3, O4 }; \ uint32_t addr, data; \ uint32_t *qd; \ + int mmu_idx = arm_to_core_mmu_idx(arm_mmu_idx(env)); \ + MemOpIdx oi = make_memop_idx(MO_TEUL | MO_ALIGN, mmu_idx); \ for (beat = 0; beat < 4; beat++, mask >>= 4) { \ if ((mask & 1) == 0) { \ /* ECI says skip this beat */ \ @@ -726,7 +732,7 @@ DO_VST4W(vst43w, 6, 7, 8, 9) addr = base + off[beat]; \ qd = (uint32_t *)aa32_vfp_qreg(env, qnidx + (beat & 1)); \ data = qd[H4(off[beat] >> 3)]; \ - cpu_stl_le_data_ra(env, addr, data, GETPC()); \ + cpu_stl_mmu(env, addr, data, oi, GETPC()); \ } \ } From 9d01d2e86d450f12f275bd64aeb022e8423e220c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jun 2025 12:24:41 +0200 Subject: [PATCH 1937/2760] accel: Propagate AccelState to AccelClass::init_machine() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to avoid init_machine() to call current_accel(), pass AccelState along. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-31-philmd@linaro.org> --- accel/accel-system.c | 2 +- accel/hvf/hvf-all.c | 2 +- accel/kvm/kvm-all.c | 2 +- accel/qtest/qtest.c | 2 +- accel/tcg/tcg-all.c | 2 +- accel/xen/xen-all.c | 2 +- bsd-user/main.c | 2 +- include/qemu/accel.h | 2 +- linux-user/main.c | 2 +- target/i386/nvmm/nvmm-all.c | 2 +- target/i386/whpx/whpx-all.c | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/accel/accel-system.c b/accel/accel-system.c index 64bc991b1c..913b7155d7 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -37,7 +37,7 @@ int accel_init_machine(AccelState *accel, MachineState *ms) int ret; ms->accelerator = accel; *(acc->allowed) = true; - ret = acc->init_machine(ms); + ret = acc->init_machine(accel, ms); if (ret < 0) { ms->accelerator = NULL; *(acc->allowed) = false; diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 09fe3f2415..9e4012e2d1 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -247,7 +247,7 @@ static MemoryListener hvf_memory_listener = { .log_sync = hvf_log_sync, }; -static int hvf_accel_init(MachineState *ms) +static int hvf_accel_init(AccelState *as, MachineState *ms) { int x; hv_return_t ret; diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 17235f2646..264f288dc6 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2573,7 +2573,7 @@ static int kvm_setup_dirty_ring(KVMState *s) return 0; } -static int kvm_init(MachineState *ms) +static int kvm_init(AccelState *as, MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); static const char upgrade_note[] = diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index 5474ce7313..2b83126020 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -38,7 +38,7 @@ static void qtest_set_virtual_clock(int64_t count) qatomic_set_i64(&qtest_clock_counter, count); } -static int qtest_init_accel(MachineState *ms) +static int qtest_init_accel(AccelState *as, MachineState *ms) { return 0; } diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 6e5dc333d5..d68fbb2377 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -80,7 +80,7 @@ static void tcg_accel_instance_init(Object *obj) bool one_insn_per_tb; -static int tcg_init_machine(MachineState *ms) +static int tcg_init_machine(AccelState *as, MachineState *ms) { TCGState *s = TCG_STATE(current_accel()); unsigned max_threads = 1; diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index c12c22de78..8279746f11 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -77,7 +77,7 @@ static void xen_setup_post(MachineState *ms, AccelState *accel) } } -static int xen_init(MachineState *ms) +static int xen_init(AccelState *as, MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); diff --git a/bsd-user/main.c b/bsd-user/main.c index 7c0a059c3b..d0cc8e0088 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -474,7 +474,7 @@ int main(int argc, char **argv) opt_one_insn_per_tb, &error_abort); object_property_set_int(OBJECT(accel), "tb-size", opt_tb_size, &error_abort); - ac->init_machine(NULL); + ac->init_machine(accel, NULL); } /* diff --git a/include/qemu/accel.h b/include/qemu/accel.h index 9dea314542..b9a9b3593d 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -40,7 +40,7 @@ typedef struct AccelClass { /* Cached by accel_init_ops_interfaces() when created */ AccelOpsClass *ops; - int (*init_machine)(MachineState *ms); + int (*init_machine)(AccelState *as, MachineState *ms); bool (*cpu_common_realize)(CPUState *cpu, Error **errp); void (*cpu_common_unrealize)(CPUState *cpu); diff --git a/linux-user/main.c b/linux-user/main.c index 5ac5b55dc6..a9142ee726 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -820,7 +820,7 @@ int main(int argc, char **argv, char **envp) opt_one_insn_per_tb, &error_abort); object_property_set_int(OBJECT(accel), "tb-size", opt_tb_size, &error_abort); - ac->init_machine(NULL); + ac->init_machine(accel, NULL); } /* diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index 2df49d7eeb..b4a4d50e86 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -1152,7 +1152,7 @@ static struct RAMBlockNotifier nvmm_ram_notifier = { /* -------------------------------------------------------------------------- */ static int -nvmm_accel_init(MachineState *ms) +nvmm_accel_init(AccelState *as, MachineState *ms) { int ret, err; diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 94fd5fc784..721c4782b9 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -2504,7 +2504,7 @@ static void whpx_set_kernel_irqchip(Object *obj, Visitor *v, * Partition support */ -static int whpx_accel_init(MachineState *ms) +static int whpx_accel_init(AccelState *as, MachineState *ms) { struct whpx_state *whpx; int ret; From d8878e4fcaf3dfe591b18a06760831c041402d15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jun 2025 12:26:56 +0200 Subject: [PATCH 1938/2760] accel/hvf: Re-use QOM allocated state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250606164418.98655-8-philmd@linaro.org> --- accel/hvf/hvf-all.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 9e4012e2d1..df95ba74d6 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -251,7 +251,7 @@ static int hvf_accel_init(AccelState *as, MachineState *ms) { int x; hv_return_t ret; - HVFState *s; + HVFState *s = HVF_STATE(as); int pa_range = 36; MachineClass *mc = MACHINE_GET_CLASS(ms); @@ -270,8 +270,6 @@ static int hvf_accel_init(AccelState *as, MachineState *ms) } assert_hvf_ok(ret); - s = g_new0(HVFState, 1); - s->num_slots = ARRAY_SIZE(s->slots); for (x = 0; x < s->num_slots; ++x) { s->slots[x].size = 0; From 7bdeb984cd3a382ec2b22b8b8c4a5207c316482f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jun 2025 12:29:17 +0200 Subject: [PATCH 1939/2760] accel/tcg: Prefer local AccelState over global current_accel() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-33-philmd@linaro.org> --- accel/tcg/tcg-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index d68fbb2377..c674d5bcf7 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -82,7 +82,7 @@ bool one_insn_per_tb; static int tcg_init_machine(AccelState *as, MachineState *ms) { - TCGState *s = TCG_STATE(current_accel()); + TCGState *s = TCG_STATE(as); unsigned max_threads = 1; #ifndef CONFIG_USER_ONLY From 4bc5c9ab62f28f66f83648c82508cce16d805a0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 6 Jun 2025 12:26:18 +0200 Subject: [PATCH 1940/2760] accel/kvm: Prefer local AccelState over global MachineState::accel MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-32-philmd@linaro.org> --- accel/kvm/kvm-all.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 264f288dc6..72fba12d9f 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2588,15 +2588,13 @@ static int kvm_init(AccelState *as, MachineState *ms) { /* end of list */ } }, *nc = num_cpus; int soft_vcpus_limit, hard_vcpus_limit; - KVMState *s; + KVMState *s = KVM_STATE(as); const KVMCapabilityInfo *missing_cap; int ret; int type; qemu_mutex_init(&kml_slots_lock); - s = KVM_STATE(ms->accelerator); - /* * On systems where the kernel can support different base page * sizes, host page size may be different from TARGET_PAGE_SIZE, From 6b1ce32fee6879d1d09070dfde0d14cbbeaced5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Jun 2025 15:30:24 +0200 Subject: [PATCH 1941/2760] accel/kvm: Directly pass KVMState argument to do_kvm_create_vm() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-35-philmd@linaro.org> --- accel/kvm/kvm-all.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 72fba12d9f..007f82a50d 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2470,13 +2470,10 @@ uint32_t kvm_dirty_ring_size(void) return kvm_state->kvm_dirty_ring_size; } -static int do_kvm_create_vm(MachineState *ms, int type) +static int do_kvm_create_vm(KVMState *s, int type) { - KVMState *s; int ret; - s = KVM_STATE(ms->accelerator); - do { ret = kvm_ioctl(s, KVM_CREATE_VM, type); } while (ret == -EINTR); @@ -2646,7 +2643,7 @@ static int kvm_init(AccelState *as, MachineState *ms) goto err; } - ret = do_kvm_create_vm(ms, type); + ret = do_kvm_create_vm(s, type); if (ret < 0) { goto err; } From 14784d00ce6643077d3b9f24c19cb2882c173ff2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Jun 2025 15:28:08 +0200 Subject: [PATCH 1942/2760] accel: Directly pass AccelState argument to AccelClass::has_memory() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-34-philmd@linaro.org> --- accel/kvm/kvm-all.c | 4 ++-- include/qemu/accel.h | 2 +- system/memory.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 007f82a50d..6f6f9ef69b 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3786,10 +3786,10 @@ int kvm_get_one_reg(CPUState *cs, uint64_t id, void *target) return r; } -static bool kvm_accel_has_memory(MachineState *ms, AddressSpace *as, +static bool kvm_accel_has_memory(AccelState *accel, AddressSpace *as, hwaddr start_addr, hwaddr size) { - KVMState *kvm = KVM_STATE(ms->accelerator); + KVMState *kvm = KVM_STATE(accel); int i; for (i = 0; i < kvm->nr_as; ++i) { diff --git a/include/qemu/accel.h b/include/qemu/accel.h index b9a9b3593d..f327a71282 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -46,7 +46,7 @@ typedef struct AccelClass { /* system related hooks */ void (*setup_post)(MachineState *ms, AccelState *accel); - bool (*has_memory)(MachineState *ms, AddressSpace *as, + bool (*has_memory)(AccelState *accel, AddressSpace *as, hwaddr start_addr, hwaddr size); /* gdbstub related hooks */ diff --git a/system/memory.c b/system/memory.c index 76b44b8220..e8d9b15b28 100644 --- a/system/memory.c +++ b/system/memory.c @@ -3501,7 +3501,7 @@ static void mtree_print_flatview(gpointer key, gpointer value, if (fvi->ac) { for (i = 0; i < fv_address_spaces->len; ++i) { as = g_array_index(fv_address_spaces, AddressSpace*, i); - if (fvi->ac->has_memory(current_machine, as, + if (fvi->ac->has_memory(current_machine->accelerator, as, int128_get64(range->addr.start), MR_SIZE(range->addr.size) + 1)) { qemu_printf(" %s", fvi->ac->name); From c7212fd2ce9182ec205dd22ff5bc66864fb3cd10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 30 Jun 2025 15:33:25 +0200 Subject: [PATCH 1943/2760] accel: Remove unused MachineState argument of AccelClass::setup_post() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This method only accesses xen_domid/xen_domid_restrict, which are both related to the 'accelerator', not the machine. Besides, xen_domid aims to be in Xen AccelState and xen_domid_restrict a xen_domid_restrict QOM property. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-36-philmd@linaro.org> --- accel/accel-system.c | 2 +- accel/xen/xen-all.c | 2 +- include/qemu/accel.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/accel/accel-system.c b/accel/accel-system.c index 913b7155d7..af713cc902 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -58,7 +58,7 @@ void accel_setup_post(MachineState *ms) AccelState *accel = ms->accelerator; AccelClass *acc = ACCEL_GET_CLASS(accel); if (acc->setup_post) { - acc->setup_post(ms, accel); + acc->setup_post(accel); } } diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index 8279746f11..bd0ff64bef 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -64,7 +64,7 @@ static void xen_set_igd_gfx_passthru(Object *obj, bool value, Error **errp) xen_igd_gfx_pt_set(value, errp); } -static void xen_setup_post(MachineState *ms, AccelState *accel) +static void xen_setup_post(AccelState *as) { int rc; diff --git a/include/qemu/accel.h b/include/qemu/accel.h index f327a71282..a6a95ff0bc 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -45,7 +45,7 @@ typedef struct AccelClass { void (*cpu_common_unrealize)(CPUState *cpu); /* system related hooks */ - void (*setup_post)(MachineState *ms, AccelState *accel); + void (*setup_post)(AccelState *as); bool (*has_memory)(AccelState *accel, AddressSpace *as, hwaddr start_addr, hwaddr size); From 261573c7724ffca8795416eae8b435e175672491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 20 Jun 2025 10:59:21 +0200 Subject: [PATCH 1944/2760] accel: Pass AccelState argument to gdbstub_supported_sstep_flags() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to have AccelClass methods instrospect their state, we need to pass AccelState by argument. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-37-philmd@linaro.org> --- accel/accel-common.c | 2 +- accel/hvf/hvf-all.c | 2 +- accel/kvm/kvm-all.c | 2 +- accel/tcg/tcg-all.c | 2 +- include/qemu/accel.h | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/accel/accel-common.c b/accel/accel-common.c index 4894b98d64..591ff4cbb6 100644 --- a/accel/accel-common.c +++ b/accel/accel-common.c @@ -124,7 +124,7 @@ int accel_supported_gdbstub_sstep_flags(void) AccelState *accel = current_accel(); AccelClass *acc = ACCEL_GET_CLASS(accel); if (acc->gdbstub_supported_sstep_flags) { - return acc->gdbstub_supported_sstep_flags(); + return acc->gdbstub_supported_sstep_flags(accel); } return 0; } diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index df95ba74d6..1fa07c8b69 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -284,7 +284,7 @@ static int hvf_accel_init(AccelState *as, MachineState *ms) return hvf_arch_init(); } -static int hvf_gdbstub_sstep_flags(void) +static int hvf_gdbstub_sstep_flags(AccelState *as) { return SSTEP_ENABLE | SSTEP_NOIRQ; } diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 6f6f9ef69b..45579f80fa 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -3980,7 +3980,7 @@ static void kvm_accel_instance_init(Object *obj) * Returns: SSTEP_* flags that KVM supports for guest debug. The * support is probed during kvm_init() */ -static int kvm_gdbstub_sstep_flags(void) +static int kvm_gdbstub_sstep_flags(AccelState *as) { return kvm_sstep_flags; } diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index c674d5bcf7..5904582a68 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -219,7 +219,7 @@ static void tcg_set_one_insn_per_tb(Object *obj, bool value, Error **errp) qatomic_set(&one_insn_per_tb, value); } -static int tcg_gdbstub_supported_sstep_flags(void) +static int tcg_gdbstub_supported_sstep_flags(AccelState *as) { /* * In replay mode all events will come from the log and can't be diff --git a/include/qemu/accel.h b/include/qemu/accel.h index a6a95ff0bc..1c097ac4df 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -50,7 +50,7 @@ typedef struct AccelClass { hwaddr start_addr, hwaddr size); /* gdbstub related hooks */ - int (*gdbstub_supported_sstep_flags)(void); + int (*gdbstub_supported_sstep_flags)(AccelState *as); bool *allowed; /* From 9a8e6b9ca1a6cf5aa66f0f7f9620a0b90320b064 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 23 Jun 2025 17:58:39 +0200 Subject: [PATCH 1945/2760] accel/system: Convert pre_resume() from AccelOpsClass to AccelClass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Accelerators call pre_resume() once. Since it isn't a method to call for each vCPU, move it from AccelOpsClass to AccelClass. Adapt WHPX. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250702185332.43650-21-philmd@linaro.org> --- accel/accel-system.c | 9 +++++++++ include/qemu/accel.h | 3 +++ include/system/accel-ops.h | 1 - system/cpus.c | 4 +--- target/i386/whpx/whpx-accel-ops.c | 1 - target/i386/whpx/whpx-accel-ops.h | 1 - target/i386/whpx/whpx-all.c | 3 ++- 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/accel/accel-system.c b/accel/accel-system.c index af713cc902..c54c30f18b 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -62,6 +62,15 @@ void accel_setup_post(MachineState *ms) } } +void accel_pre_resume(MachineState *ms, bool step_pending) +{ + AccelState *accel = ms->accelerator; + AccelClass *acc = ACCEL_GET_CLASS(accel); + if (acc->pre_resume_vm) { + acc->pre_resume_vm(accel, step_pending); + } +} + /* initialize the arch-independent accel operation interfaces */ void accel_init_ops_interfaces(AccelClass *ac) { diff --git a/include/qemu/accel.h b/include/qemu/accel.h index 1c097ac4df..9e821d0fae 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -46,6 +46,7 @@ typedef struct AccelClass { /* system related hooks */ void (*setup_post)(AccelState *as); + void (*pre_resume_vm)(AccelState *as, bool step_pending); bool (*has_memory)(AccelState *accel, AddressSpace *as, hwaddr start_addr, hwaddr size); @@ -86,6 +87,8 @@ int accel_init_machine(AccelState *accel, MachineState *ms); /* Called just before os_setup_post (ie just before drop OS privs) */ void accel_setup_post(MachineState *ms); +void accel_pre_resume(MachineState *ms, bool step_pending); + /** * accel_cpu_instance_init: * @cpu: The CPU that needs to do accel-specific object initializations. diff --git a/include/system/accel-ops.h b/include/system/accel-ops.h index a786c7d478..bf7383511d 100644 --- a/include/system/accel-ops.h +++ b/include/system/accel-ops.h @@ -61,7 +61,6 @@ struct AccelOpsClass { */ void (*synchronize_state)(CPUState *cpu); void (*synchronize_pre_loadvm)(CPUState *cpu); - void (*synchronize_pre_resume)(bool step_pending); /* handle_interrupt is mandatory. */ void (*handle_interrupt)(CPUState *cpu, int mask); diff --git a/system/cpus.c b/system/cpus.c index 0d0eec82a2..8e6da2e060 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -768,9 +768,7 @@ int vm_prepare_start(bool step_pending) * WHPX accelerator needs to know whether we are going to step * any CPUs, before starting the first one. */ - if (cpus_accel->synchronize_pre_resume) { - cpus_accel->synchronize_pre_resume(step_pending); - } + accel_pre_resume(MACHINE(qdev_get_machine()), step_pending); /* We are sending this now, but the CPUs will be resumed shortly later */ qapi_event_send_resume(); diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index 31cf15f004..5f4841c9fa 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -96,7 +96,6 @@ static void whpx_accel_ops_class_init(ObjectClass *oc, const void *data) ops->synchronize_post_init = whpx_cpu_synchronize_post_init; ops->synchronize_state = whpx_cpu_synchronize_state; ops->synchronize_pre_loadvm = whpx_cpu_synchronize_pre_loadvm; - ops->synchronize_pre_resume = whpx_cpu_synchronize_pre_resume; } static const TypeInfo whpx_accel_ops_type = { diff --git a/target/i386/whpx/whpx-accel-ops.h b/target/i386/whpx/whpx-accel-ops.h index e6cf15511d..54cfc25a14 100644 --- a/target/i386/whpx/whpx-accel-ops.h +++ b/target/i386/whpx/whpx-accel-ops.h @@ -21,7 +21,6 @@ void whpx_cpu_synchronize_state(CPUState *cpu); void whpx_cpu_synchronize_post_reset(CPUState *cpu); void whpx_cpu_synchronize_post_init(CPUState *cpu); void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu); -void whpx_cpu_synchronize_pre_resume(bool step_pending); /* state subset only touched by the VCPU itself during runtime */ #define WHPX_SET_RUNTIME_STATE 1 diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index 721c4782b9..faf56e1972 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -2105,7 +2105,7 @@ void whpx_cpu_synchronize_pre_loadvm(CPUState *cpu) run_on_cpu(cpu, do_whpx_cpu_synchronize_pre_loadvm, RUN_ON_CPU_NULL); } -void whpx_cpu_synchronize_pre_resume(bool step_pending) +static void whpx_pre_resume_vm(AccelState *as, bool step_pending) { whpx_global.step_pending = step_pending; } @@ -2697,6 +2697,7 @@ static void whpx_accel_class_init(ObjectClass *oc, const void *data) AccelClass *ac = ACCEL_CLASS(oc); ac->name = "WHPX"; ac->init_machine = whpx_accel_init; + ac->pre_resume_vm = whpx_pre_resume_vm; ac->allowed = &whpx_allowed; object_class_property_add(oc, "kernel-irqchip", "on|off|split", From e4a4163ed21e8d88f6a0c2d17f9487ad83841653 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 23 Jun 2025 11:53:06 -0300 Subject: [PATCH 1946/2760] monitor/hmp-cmds-target: add CPU_DUMP_VPU in hmp_info_registers() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit b84694defb added the CPU_DUMP_VPU to allow vector registers to be logged by log_cpu_exec() in TCG. This flag was then used in commit b227f6a8a7 to print RISC-V vector registers using this flag. Note that this change was done in riscv_cpu_dump_state(), the cpu_dump_state() callback for RISC-V, the same callback used in hmp_info_registers(). Back then we forgot to change hmp_info_registers(), and 'info registers' isn't showing RISC-V vector registers as a result. No other target is impacted since only RISC-V is using CPU_DUMP_VPU. There's no reason to not show VPU regs in info_registers(), so add CPU_DUMP_VPU to hmp_info_registers(). This will print vector registers for all RISC-V machines and, as said above, has no impact in other archs. Cc: Dr. David Alan Gilbert Signed-off-by: Daniel Henrique Barboza Reviewed-by: Alistair Francis Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250623145306.991562-1-dbarboza@ventanamicro.com> Signed-off-by: Philippe Mathieu-Daudé --- monitor/hmp-cmds-target.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/monitor/hmp-cmds-target.c b/monitor/hmp-cmds-target.c index 8eaf70d9c9..e982061146 100644 --- a/monitor/hmp-cmds-target.c +++ b/monitor/hmp-cmds-target.c @@ -102,7 +102,7 @@ void hmp_info_registers(Monitor *mon, const QDict *qdict) if (all_cpus) { CPU_FOREACH(cs) { monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); - cpu_dump_state(cs, NULL, CPU_DUMP_FPU); + cpu_dump_state(cs, NULL, CPU_DUMP_FPU | CPU_DUMP_VPU); } } else { cs = vcpu >= 0 ? qemu_get_cpu(vcpu) : mon_get_cpu(mon); @@ -117,7 +117,7 @@ void hmp_info_registers(Monitor *mon, const QDict *qdict) } monitor_printf(mon, "\nCPU#%d\n", cs->cpu_index); - cpu_dump_state(cs, NULL, CPU_DUMP_FPU); + cpu_dump_state(cs, NULL, CPU_DUMP_FPU | CPU_DUMP_VPU); } } From 431ac3b5d2b60c8bd8b34d417154060b9184dec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Jul 2025 19:26:19 +0200 Subject: [PATCH 1947/2760] MAINTAINERS: Add me as reviewer of overall accelerators section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I'd like to be informed of overall changes of accelerators. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250703173248.44995-40-philmd@linaro.org> --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index fca98e1219..bfd59f6412 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -495,6 +495,7 @@ Guest CPU Cores (other accelerators) Overall M: Richard Henderson R: Paolo Bonzini +R: Philippe Mathieu-Daudé S: Maintained F: include/exec/cpu*.h F: include/exec/target_long.h From f9b0f69304071384b12912bf9dd78e9ffd261cec Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:24 -0600 Subject: [PATCH 1948/2760] target/arm: Fix SME vs AdvSIMD exception priority We failed to raise an exception when sme_excp_el == 0 and fp_excp_el == 1. Cc: qemu-stable@nongnu.org Fixes: 3d74825f4d6 ("target/arm: Add SME enablement checks") Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 815225b130..5c44a5dece 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1488,7 +1488,8 @@ bool sme_enabled_check(DisasContext *s) * to be zero when fp_excp_el has priority. This is because we need * sme_excp_el by itself for cpregs access checks. */ - if (!s->fp_excp_el || s->sme_excp_el < s->fp_excp_el) { + if (s->sme_excp_el + && (!s->fp_excp_el || s->sme_excp_el <= s->fp_excp_el)) { bool ret = sme_access_check(s); s->fp_access_checked = (ret ? 1 : -1); return ret; From b4b2e070f41dd8774a70c6186141678558d79a38 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:25 -0600 Subject: [PATCH 1949/2760] target/arm: Fix sve_access_check for SME Do not assume SME implies SVE. Ensure that the non-streaming check is present along the SME path, since it is not implied by sme_*_enabled_check. Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-3-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.c | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index 5c44a5dece..b0caccca46 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -1381,11 +1381,8 @@ static bool fp_access_check_only(DisasContext *s) return true; } -static bool fp_access_check(DisasContext *s) +static bool nonstreaming_check(DisasContext *s) { - if (!fp_access_check_only(s)) { - return false; - } if (s->sme_trap_nonstreaming && s->is_nonstreaming) { gen_exception_insn(s, 0, EXCP_UDEF, syn_smetrap(SME_ET_Streaming, false)); @@ -1394,6 +1391,11 @@ static bool fp_access_check(DisasContext *s) return true; } +static bool fp_access_check(DisasContext *s) +{ + return fp_access_check_only(s) && nonstreaming_check(s); +} + /* * Return <0 for non-supported element sizes, with MO_16 controlled by * FEAT_FP16; return 0 for fp disabled; otherwise return >0 for success. @@ -1444,14 +1446,24 @@ static int fp_access_check_vector_hsd(DisasContext *s, bool is_q, MemOp esz) */ bool sve_access_check(DisasContext *s) { - if (s->pstate_sm || !dc_isar_feature(aa64_sve, s)) { + if (dc_isar_feature(aa64_sme, s)) { bool ret; - assert(dc_isar_feature(aa64_sme, s)); - ret = sme_sm_enabled_check(s); + if (s->pstate_sm) { + ret = sme_enabled_check(s); + } else if (dc_isar_feature(aa64_sve, s)) { + goto continue_sve; + } else { + ret = sme_sm_enabled_check(s); + } + if (ret) { + ret = nonstreaming_check(s); + } s->sve_access_checked = (ret ? 1 : -1); return ret; } + + continue_sve: if (s->sve_excp_el) { /* Assert that we only raise one exception per instruction. */ assert(!s->sve_access_checked); From e6ffd009c7710a8cc98094897fa0af609c114683 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:26 -0600 Subject: [PATCH 1950/2760] target/arm: Fix 128-bit element ZIP, UZP, TRN We missed the instructions UDEF when the vector size is too small. We missed marking the instructions non-streaming with SME. Cc: qemu-stable@nongnu.org Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-4-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-sve.c | 43 ++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 13 deletions(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index f3cf028cb9..588a5b006b 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -2352,6 +2352,23 @@ TRANS_FEAT(PUNPKHI, aa64_sve, do_perm_pred2, a, 1, gen_helper_sve_punpk_p) *** SVE Permute - Interleaving Group */ +static bool do_interleave_q(DisasContext *s, gen_helper_gvec_3 *fn, + arg_rrr_esz *a, int data) +{ + if (sve_access_check(s)) { + unsigned vsz = vec_full_reg_size(s); + if (vsz < 32) { + unallocated_encoding(s); + } else { + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vec_full_reg_offset(s, a->rm), + vsz, vsz, data, fn); + } + } + return true; +} + static gen_helper_gvec_3 * const zip_fns[4] = { gen_helper_sve_zip_b, gen_helper_sve_zip_h, gen_helper_sve_zip_s, gen_helper_sve_zip_d, @@ -2361,11 +2378,11 @@ TRANS_FEAT(ZIP1_z, aa64_sve, gen_gvec_ool_arg_zzz, TRANS_FEAT(ZIP2_z, aa64_sve, gen_gvec_ool_arg_zzz, zip_fns[a->esz], a, vec_full_reg_size(s) / 2) -TRANS_FEAT(ZIP1_q, aa64_sve_f64mm, gen_gvec_ool_arg_zzz, - gen_helper_sve2_zip_q, a, 0) -TRANS_FEAT(ZIP2_q, aa64_sve_f64mm, gen_gvec_ool_arg_zzz, - gen_helper_sve2_zip_q, a, - QEMU_ALIGN_DOWN(vec_full_reg_size(s), 32) / 2) +TRANS_FEAT_NONSTREAMING(ZIP1_q, aa64_sve_f64mm, do_interleave_q, + gen_helper_sve2_zip_q, a, 0) +TRANS_FEAT_NONSTREAMING(ZIP2_q, aa64_sve_f64mm, do_interleave_q, + gen_helper_sve2_zip_q, a, + QEMU_ALIGN_DOWN(vec_full_reg_size(s), 32) / 2) static gen_helper_gvec_3 * const uzp_fns[4] = { gen_helper_sve_uzp_b, gen_helper_sve_uzp_h, @@ -2377,10 +2394,10 @@ TRANS_FEAT(UZP1_z, aa64_sve, gen_gvec_ool_arg_zzz, TRANS_FEAT(UZP2_z, aa64_sve, gen_gvec_ool_arg_zzz, uzp_fns[a->esz], a, 1 << a->esz) -TRANS_FEAT(UZP1_q, aa64_sve_f64mm, gen_gvec_ool_arg_zzz, - gen_helper_sve2_uzp_q, a, 0) -TRANS_FEAT(UZP2_q, aa64_sve_f64mm, gen_gvec_ool_arg_zzz, - gen_helper_sve2_uzp_q, a, 16) +TRANS_FEAT_NONSTREAMING(UZP1_q, aa64_sve_f64mm, do_interleave_q, + gen_helper_sve2_uzp_q, a, 0) +TRANS_FEAT_NONSTREAMING(UZP2_q, aa64_sve_f64mm, do_interleave_q, + gen_helper_sve2_uzp_q, a, 16) static gen_helper_gvec_3 * const trn_fns[4] = { gen_helper_sve_trn_b, gen_helper_sve_trn_h, @@ -2392,10 +2409,10 @@ TRANS_FEAT(TRN1_z, aa64_sve, gen_gvec_ool_arg_zzz, TRANS_FEAT(TRN2_z, aa64_sve, gen_gvec_ool_arg_zzz, trn_fns[a->esz], a, 1 << a->esz) -TRANS_FEAT(TRN1_q, aa64_sve_f64mm, gen_gvec_ool_arg_zzz, - gen_helper_sve2_trn_q, a, 0) -TRANS_FEAT(TRN2_q, aa64_sve_f64mm, gen_gvec_ool_arg_zzz, - gen_helper_sve2_trn_q, a, 16) +TRANS_FEAT_NONSTREAMING(TRN1_q, aa64_sve_f64mm, do_interleave_q, + gen_helper_sve2_trn_q, a, 0) +TRANS_FEAT_NONSTREAMING(TRN2_q, aa64_sve_f64mm, do_interleave_q, + gen_helper_sve2_trn_q, a, 16) /* *** SVE Permute Vector - Predicated Group From 4f06e6eaeb2fe65b957be546c61ab2f11a87202c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:27 -0600 Subject: [PATCH 1951/2760] target/arm: Replace @rda_rn_rm_e0 in sve.decode Replace @rda_rn_rm_e0 with @rda_rn_rm_ex, and require users to supply an explicit esz. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-5-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve.decode | 48 +++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 04b6fcc0cf..3a99eb7299 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -131,11 +131,11 @@ @rda_rn_rm ........ esz:2 . rm:5 ... ... rn:5 rd:5 \ &rrrr_esz ra=%reg_movprfx -# Four operand with unused vector element size -@rda_rn_rm_e0 ........ ... rm:5 ... ... rn:5 rd:5 \ - &rrrr_esz esz=0 ra=%reg_movprfx -@rdn_ra_rm_e0 ........ ... rm:5 ... ... ra:5 rd:5 \ - &rrrr_esz esz=0 rn=%reg_movprfx +# Four operand with explicit vector element size +@rda_rn_rm_ex ........ ... rm:5 ... ... rn:5 rd:5 \ + &rrrr_esz ra=%reg_movprfx +@rdn_ra_rm_ex ........ ... rm:5 ... ... ra:5 rd:5 \ + &rrrr_esz rn=%reg_movprfx # Three operand with "memory" size, aka immediate left shift @rd_rn_msz_rm ........ ... rm:5 .... imm:2 rn:5 rd:5 &rrri @@ -428,12 +428,12 @@ XAR 00000100 .. 1 ..... 001 101 rm:5 rd:5 &rrri_esz \ rn=%reg_movprfx esz=%tszimm16_esz imm=%tszimm16_shr # SVE2 bitwise ternary operations -EOR3 00000100 00 1 ..... 001 110 ..... ..... @rdn_ra_rm_e0 -BSL 00000100 00 1 ..... 001 111 ..... ..... @rdn_ra_rm_e0 -BCAX 00000100 01 1 ..... 001 110 ..... ..... @rdn_ra_rm_e0 -BSL1N 00000100 01 1 ..... 001 111 ..... ..... @rdn_ra_rm_e0 -BSL2N 00000100 10 1 ..... 001 111 ..... ..... @rdn_ra_rm_e0 -NBSL 00000100 11 1 ..... 001 111 ..... ..... @rdn_ra_rm_e0 +EOR3 00000100 00 1 ..... 001 110 ..... ..... @rdn_ra_rm_ex esz=0 +BSL 00000100 00 1 ..... 001 111 ..... ..... @rdn_ra_rm_ex esz=0 +BCAX 00000100 01 1 ..... 001 110 ..... ..... @rdn_ra_rm_ex esz=0 +BSL1N 00000100 01 1 ..... 001 111 ..... ..... @rdn_ra_rm_ex esz=0 +BSL2N 00000100 10 1 ..... 001 111 ..... ..... @rdn_ra_rm_ex esz=0 +NBSL 00000100 11 1 ..... 001 111 ..... ..... @rdn_ra_rm_ex esz=0 ### SVE Index Generation Group @@ -1450,9 +1450,9 @@ EORTB 01000101 .. 0 ..... 10010 1 ..... ..... @rd_rn_rm ## SVE integer matrix multiply accumulate -SMMLA 01000101 00 0 ..... 10011 0 ..... ..... @rda_rn_rm_e0 -USMMLA 01000101 10 0 ..... 10011 0 ..... ..... @rda_rn_rm_e0 -UMMLA 01000101 11 0 ..... 10011 0 ..... ..... @rda_rn_rm_e0 +SMMLA 01000101 00 0 ..... 10011 0 ..... ..... @rda_rn_rm_ex esz=2 +USMMLA 01000101 10 0 ..... 10011 0 ..... ..... @rda_rn_rm_ex esz=2 +UMMLA 01000101 11 0 ..... 10011 0 ..... ..... @rda_rn_rm_ex esz=2 ## SVE2 bitwise permute @@ -1602,9 +1602,9 @@ SQRDCMLAH_zzzz 01000100 esz:2 0 rm:5 0011 rot:2 rn:5 rd:5 ra=%reg_movprfx USDOT_zzzz 01000100 .. 0 ..... 011 110 ..... ..... @rda_rn_rm ### SVE2 floating point matrix multiply accumulate -BFMMLA 01100100 01 1 ..... 111 001 ..... ..... @rda_rn_rm_e0 -FMMLA_s 01100100 10 1 ..... 111 001 ..... ..... @rda_rn_rm_e0 -FMMLA_d 01100100 11 1 ..... 111 001 ..... ..... @rda_rn_rm_e0 +BFMMLA 01100100 01 1 ..... 111 001 ..... ..... @rda_rn_rm_ex esz=1 +FMMLA_s 01100100 10 1 ..... 111 001 ..... ..... @rda_rn_rm_ex esz=2 +FMMLA_d 01100100 11 1 ..... 111 001 ..... ..... @rda_rn_rm_ex esz=3 ### SVE2 Memory Gather Load Group @@ -1654,16 +1654,16 @@ FCVTLT_sd 01100100 11 0010 11 101 ... ..... ..... @rd_pg_rn_e0 FLOGB 01100101 00 011 esz:2 0101 pg:3 rn:5 rd:5 &rpr_esz ### SVE2 floating-point multiply-add long (vectors) -FMLALB_zzzw 01100100 10 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_e0 -FMLALT_zzzw 01100100 10 1 ..... 10 0 00 1 ..... ..... @rda_rn_rm_e0 -FMLSLB_zzzw 01100100 10 1 ..... 10 1 00 0 ..... ..... @rda_rn_rm_e0 -FMLSLT_zzzw 01100100 10 1 ..... 10 1 00 1 ..... ..... @rda_rn_rm_e0 +FMLALB_zzzw 01100100 10 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_ex esz=2 +FMLALT_zzzw 01100100 10 1 ..... 10 0 00 1 ..... ..... @rda_rn_rm_ex esz=2 +FMLSLB_zzzw 01100100 10 1 ..... 10 1 00 0 ..... ..... @rda_rn_rm_ex esz=2 +FMLSLT_zzzw 01100100 10 1 ..... 10 1 00 1 ..... ..... @rda_rn_rm_ex esz=2 -BFMLALB_zzzw 01100100 11 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_e0 -BFMLALT_zzzw 01100100 11 1 ..... 10 0 00 1 ..... ..... @rda_rn_rm_e0 +BFMLALB_zzzw 01100100 11 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_ex esz=2 +BFMLALT_zzzw 01100100 11 1 ..... 10 0 00 1 ..... ..... @rda_rn_rm_ex esz=2 ### SVE2 floating-point bfloat16 dot-product -BFDOT_zzzz 01100100 01 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_e0 +BFDOT_zzzz 01100100 01 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_ex esz=2 ### SVE2 floating-point multiply-add long (indexed) FMLALB_zzxw 01100100 10 1 ..... 0100.0 ..... ..... @rrxr_3a esz=2 From c60a52fc576c9e2f5b8c26ed7b49ebd173b5c1e3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:28 -0600 Subject: [PATCH 1952/2760] target/arm: Fix FMMLA (64-bit element) for 128-bit VL Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-6-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-sve.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 588a5b006b..a0de5b488d 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -7025,17 +7025,26 @@ DO_ZPZZ_FP(FMINNMP, aa64_sve2, sve2_fminnmp_zpzz) DO_ZPZZ_FP(FMAXP, aa64_sve2, sve2_fmaxp_zpzz) DO_ZPZZ_FP(FMINP, aa64_sve2, sve2_fminp_zpzz) +static bool do_fmmla(DisasContext *s, arg_rrrr_esz *a, + gen_helper_gvec_4_ptr *fn) +{ + if (sve_access_check(s)) { + if (vec_full_reg_size(s) < 4 * memop_size(a->esz)) { + unallocated_encoding(s); + } else { + gen_gvec_fpst_zzzz(s, fn, a->rd, a->rn, a->rm, a->ra, 0, FPST_A64); + } + } + return true; +} + +TRANS_FEAT_NONSTREAMING(FMMLA_s, aa64_sve_f32mm, do_fmmla, a, gen_helper_fmmla_s) +TRANS_FEAT_NONSTREAMING(FMMLA_d, aa64_sve_f64mm, do_fmmla, a, gen_helper_fmmla_d) + /* * SVE Integer Multiply-Add (unpredicated) */ -TRANS_FEAT_NONSTREAMING(FMMLA_s, aa64_sve_f32mm, gen_gvec_fpst_zzzz, - gen_helper_fmmla_s, a->rd, a->rn, a->rm, a->ra, - 0, FPST_A64) -TRANS_FEAT_NONSTREAMING(FMMLA_d, aa64_sve_f64mm, gen_gvec_fpst_zzzz, - gen_helper_fmmla_d, a->rd, a->rn, a->rm, a->ra, - 0, FPST_A64) - static gen_helper_gvec_4 * const sqdmlal_zzzw_fns[] = { NULL, gen_helper_sve2_sqdmlal_zzzw_h, gen_helper_sve2_sqdmlal_zzzw_s, gen_helper_sve2_sqdmlal_zzzw_d, From a827b94c67bab630499876e888f1ffe737d9f26b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:29 -0600 Subject: [PATCH 1953/2760] target/arm: Disable FEAT_F64MM if maximum SVE vector size too small All F64MM instructions operate on a 256-bit vector. If only 128-bit vectors is supported by the cpu, then the cpu cannot enable F64MM. Suggested-by: Peter Maydell Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-7-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu64.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 1f3406708b..77e7c4a6a5 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -259,6 +259,13 @@ void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp) /* From now on sve_max_vq is the actual maximum supported length. */ cpu->sve_max_vq = max_vq; cpu->sve_vq.map = vq_map; + + /* FEAT_F64MM requires the existence of a 256-bit vector size. */ + if (max_vq < 2) { + uint64_t t = GET_IDREG(&cpu->isar, ID_AA64ZFR0); + t = FIELD_DP64(t, ID_AA64ZFR0, F64MM, 0); + SET_IDREG(&cpu->isar, ID_AA64ZFR0, t); + } } /* From 3801c5b75ffc60957265513338e8fd5f8b6ce8a1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:30 -0600 Subject: [PATCH 1954/2760] target/arm: Fix PSEL size operands to tcg_gen_gvec_ands Gvec only operates on size 8 and multiples of 16. Predicates may be any multiple of 2. Round up the size using the appropriate function. Cc: qemu-stable@nongnu.org Fixes: 598ab0b24c0 ("target/arm: Implement PSEL") Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-8-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-sve.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index a0de5b488d..8403034a0e 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -7291,6 +7291,7 @@ static bool trans_PSEL(DisasContext *s, arg_psel *a) tcg_gen_neg_i64(tmp, tmp); /* Apply to either copy the source, or write zeros. */ + pl = size_for_gvec(pl); tcg_gen_gvec_ands(MO_64, pred_full_reg_offset(s, a->pd), pred_full_reg_offset(s, a->pn), tmp, pl, pl); return true; From cfc688c00ade84f6b32c7814b52c217f1d3b5eb1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:31 -0600 Subject: [PATCH 1955/2760] target/arm: Fix f16_dotadd vs nan selection Implement FPProcessNaNs4 within f16_dotadd, rather than simply letting NaNs propagate through the function. Cc: qemu-stable@nongnu.org Fixes: 3916841ac75 ("target/arm: Implement FMOPA, FMOPS (widening)") Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-9-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme_helper.c | 62 +++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index de0c6e54d4..8f33387e4b 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1005,25 +1005,55 @@ static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2, * - we have pre-set-up copy of s_std which is set to round-to-odd, * for the multiply (see below) */ - float64 e1r = float16_to_float64(e1 & 0xffff, true, s_f16); - float64 e1c = float16_to_float64(e1 >> 16, true, s_f16); - float64 e2r = float16_to_float64(e2 & 0xffff, true, s_f16); - float64 e2c = float16_to_float64(e2 >> 16, true, s_f16); - float64 t64; + float16 h1r = e1 & 0xffff; + float16 h1c = e1 >> 16; + float16 h2r = e2 & 0xffff; + float16 h2c = e2 >> 16; float32 t32; - /* - * The ARM pseudocode function FPDot performs both multiplies - * and the add with a single rounding operation. Emulate this - * by performing the first multiply in round-to-odd, then doing - * the second multiply as fused multiply-add, and rounding to - * float32 all in one step. - */ - t64 = float64_mul(e1r, e2r, s_odd); - t64 = float64r32_muladd(e1c, e2c, t64, 0, s_std); + /* C.f. FPProcessNaNs4 */ + if (float16_is_any_nan(h1r) || float16_is_any_nan(h1c) || + float16_is_any_nan(h2r) || float16_is_any_nan(h2c)) { + float16 t16; + + if (float16_is_signaling_nan(h1r, s_f16)) { + t16 = h1r; + } else if (float16_is_signaling_nan(h1c, s_f16)) { + t16 = h1c; + } else if (float16_is_signaling_nan(h2r, s_f16)) { + t16 = h2r; + } else if (float16_is_signaling_nan(h2c, s_f16)) { + t16 = h2c; + } else if (float16_is_any_nan(h1r)) { + t16 = h1r; + } else if (float16_is_any_nan(h1c)) { + t16 = h1c; + } else if (float16_is_any_nan(h2r)) { + t16 = h2r; + } else { + t16 = h2c; + } + t32 = float16_to_float32(t16, true, s_f16); + } else { + float64 e1r = float16_to_float64(h1r, true, s_f16); + float64 e1c = float16_to_float64(h1c, true, s_f16); + float64 e2r = float16_to_float64(h2r, true, s_f16); + float64 e2c = float16_to_float64(h2c, true, s_f16); + float64 t64; - /* This conversion is exact, because we've already rounded. */ - t32 = float64_to_float32(t64, s_std); + /* + * The ARM pseudocode function FPDot performs both multiplies + * and the add with a single rounding operation. Emulate this + * by performing the first multiply in round-to-odd, then doing + * the second multiply as fused multiply-add, and rounding to + * float32 all in one step. + */ + t64 = float64_mul(e1r, e2r, s_odd); + t64 = float64r32_muladd(e1c, e2c, t64, 0, s_std); + + /* This conversion is exact, because we've already rounded. */ + t32 = float64_to_float32(t64, s_std); + } /* The final accumulation step is not fused. */ return float32_add(sum, t32, s_std); From bf020eaa6741711902a425016e2c7585f222562d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:32 -0600 Subject: [PATCH 1956/2760] target/arm: Fix bfdotadd_ebf vs nan selection Implement FPProcessNaNs4 within bfdotadd_ebf, rather than simply letting NaNs propagate through the function. Cc: qemu-stable@nongnu.org Fixes: 0e1850182a1 ("target/arm: Implement FPCR.EBF=1 semantics for bfdotadd()") Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250704142112.1018902-10-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/vec_helper.c | 75 ++++++++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 22 deletions(-) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 986eaf8ffa..3b7f308803 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2989,31 +2989,62 @@ float32 bfdotadd(float32 sum, uint32_t e1, uint32_t e2, float_status *fpst) float32 bfdotadd_ebf(float32 sum, uint32_t e1, uint32_t e2, float_status *fpst, float_status *fpst_odd) { - /* - * Compare f16_dotadd() in sme_helper.c, but here we have - * bfloat16 inputs. In particular that means that we do not - * want the FPCR.FZ16 flush semantics, so we use the normal - * float_status for the input handling here. - */ - float64 e1r = float32_to_float64(e1 << 16, fpst); - float64 e1c = float32_to_float64(e1 & 0xffff0000u, fpst); - float64 e2r = float32_to_float64(e2 << 16, fpst); - float64 e2c = float32_to_float64(e2 & 0xffff0000u, fpst); - float64 t64; + float32 s1r = e1 << 16; + float32 s1c = e1 & 0xffff0000u; + float32 s2r = e2 << 16; + float32 s2c = e2 & 0xffff0000u; float32 t32; - /* - * The ARM pseudocode function FPDot performs both multiplies - * and the add with a single rounding operation. Emulate this - * by performing the first multiply in round-to-odd, then doing - * the second multiply as fused multiply-add, and rounding to - * float32 all in one step. - */ - t64 = float64_mul(e1r, e2r, fpst_odd); - t64 = float64r32_muladd(e1c, e2c, t64, 0, fpst); + /* C.f. FPProcessNaNs4 */ + if (float32_is_any_nan(s1r) || float32_is_any_nan(s1c) || + float32_is_any_nan(s2r) || float32_is_any_nan(s2c)) { + if (float32_is_signaling_nan(s1r, fpst)) { + t32 = s1r; + } else if (float32_is_signaling_nan(s1c, fpst)) { + t32 = s1c; + } else if (float32_is_signaling_nan(s2r, fpst)) { + t32 = s2r; + } else if (float32_is_signaling_nan(s2c, fpst)) { + t32 = s2c; + } else if (float32_is_any_nan(s1r)) { + t32 = s1r; + } else if (float32_is_any_nan(s1c)) { + t32 = s1c; + } else if (float32_is_any_nan(s2r)) { + t32 = s2r; + } else { + t32 = s2c; + } + /* + * FPConvertNaN(FPProcessNaN(t32)) will be done as part + * of the final addition below. + */ + } else { + /* + * Compare f16_dotadd() in sme_helper.c, but here we have + * bfloat16 inputs. In particular that means that we do not + * want the FPCR.FZ16 flush semantics, so we use the normal + * float_status for the input handling here. + */ + float64 e1r = float32_to_float64(s1r, fpst); + float64 e1c = float32_to_float64(s1c, fpst); + float64 e2r = float32_to_float64(s2r, fpst); + float64 e2c = float32_to_float64(s2c, fpst); + float64 t64; - /* This conversion is exact, because we've already rounded. */ - t32 = float64_to_float32(t64, fpst); + /* + * The ARM pseudocode function FPDot performs both multiplies + * and the add with a single rounding operation. Emulate this + * by performing the first multiply in round-to-odd, then doing + * the second multiply as fused multiply-add, and rounding to + * float32 all in one step. + */ + t64 = float64_mul(e1r, e2r, fpst_odd); + t64 = float64r32_muladd(e1c, e2c, t64, 0, fpst); + + /* This conversion is exact, because we've already rounded. */ + t32 = float64_to_float32(t64, fpst); + } /* The final accumulation step is not fused. */ return float32_add(sum, t32, fpst); From afb1bd20df88edb553dbb70857a0b7f15c72c672 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:33 -0600 Subject: [PATCH 1957/2760] target/arm: Remove CPUARMState.vfp.scratch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The last use of this field was removed in b2fc7be972b9. Reviewed-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-11-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 0338153923..96a4907266 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -669,9 +669,6 @@ typedef struct CPUArchState { uint32_t xregs[16]; - /* Scratch space for aa32 neon expansion. */ - uint32_t scratch[8]; - /* There are a number of distinct float control structures. */ float_status fp_status[FPST_COUNT]; From c994c84d384b87cd862d12f6b9db4bcfd42b5a7b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:34 -0600 Subject: [PATCH 1958/2760] target/arm: Introduce FPST_ZA, FPST_ZA_F16 Rather than repeatedly copying FPST_FPCR to locals and setting default nan mode, create dedicated float_status. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-12-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 4 ++++ target/arm/cpu.h | 12 +++++++++++- target/arm/tcg/vfp_helper.c | 12 +++++++++++- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index ebac86f70d..7b207f2620 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -554,11 +554,15 @@ static void arm_cpu_reset_hold(Object *obj, ResetType type) set_flush_inputs_to_zero(1, &env->vfp.fp_status[FPST_STD]); set_default_nan_mode(1, &env->vfp.fp_status[FPST_STD]); set_default_nan_mode(1, &env->vfp.fp_status[FPST_STD_F16]); + set_default_nan_mode(1, &env->vfp.fp_status[FPST_ZA]); + set_default_nan_mode(1, &env->vfp.fp_status[FPST_ZA_F16]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A32]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64]); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_ZA]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A32_F16]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_ZA_F16]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_STD_F16]); arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_AH]); set_flush_to_zero(1, &env->vfp.fp_status[FPST_AH]); diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 96a4907266..2530aaca22 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -207,6 +207,8 @@ typedef struct NVICState NVICState; * when FPCR.AH == 1 (bfloat16 conversions and multiplies, * and the reciprocal and square root estimate/step insns); * for half-precision + * ZA: the "streaming sve" fp status. + * ZA_F16: likewise for half-precision. * * Half-precision operations are governed by a separate * flush-to-zero control bit in FPSCR:FZ16. We pass a separate @@ -227,6 +229,12 @@ typedef struct NVICState NVICState; * they ignore FPCR.RMode. But they don't ignore FPCR.FZ16, * which means we need an FPST_AH_F16 as well. * + * The "ZA" float_status are for Streaming SVE operations which use + * default-NaN and do not generate fp exceptions, which means that they + * do not accumulate exception bits back into FPCR. + * See e.g. FPAdd vs FPAdd_ZA pseudocode functions, and the setting + * of fpcr.DN and fpexec parameters. + * * To avoid having to transfer exception bits around, we simply * say that the FPSCR cumulative exception flags are the logical * OR of the flags in the four fp statuses. This relies on the @@ -240,10 +248,12 @@ typedef enum ARMFPStatusFlavour { FPST_A64_F16, FPST_AH, FPST_AH_F16, + FPST_ZA, + FPST_ZA_F16, FPST_STD, FPST_STD_F16, } ARMFPStatusFlavour; -#define FPST_COUNT 8 +#define FPST_COUNT 10 typedef struct CPUArchState { /* Regs for current mode. */ diff --git a/target/arm/tcg/vfp_helper.c b/target/arm/tcg/vfp_helper.c index b1324c5c0a..e156e3774a 100644 --- a/target/arm/tcg/vfp_helper.c +++ b/target/arm/tcg/vfp_helper.c @@ -123,7 +123,7 @@ uint32_t vfp_get_fpsr_from_host(CPUARMState *env) a64_flags |= (get_float_exception_flags(&env->vfp.fp_status[FPST_A64_F16]) & ~(float_flag_input_denormal_flushed | float_flag_input_denormal_used)); /* - * We do not merge in flags from FPST_AH or FPST_AH_F16, because + * We do not merge in flags from FPST_{AH,ZA} or FPST_{AH,ZA}_F16, because * they are used for insns that must not set the cumulative exception bits. */ @@ -196,6 +196,8 @@ void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64]); set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A32_F16]); set_float_rounding_mode(i, &env->vfp.fp_status[FPST_A64_F16]); + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_ZA]); + set_float_rounding_mode(i, &env->vfp.fp_status[FPST_ZA_F16]); } if (changed & FPCR_FZ16) { bool ftz_enabled = val & FPCR_FZ16; @@ -203,15 +205,18 @@ void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_ZA_F16]); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32_F16]); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64_F16]); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_STD_F16]); set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_AH_F16]); + set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_ZA_F16]); } if (changed & FPCR_FZ) { bool ftz_enabled = val & FPCR_FZ; set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32]); set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A64]); + set_flush_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_ZA]); /* FIZ is A64 only so FZ always makes A32 code flush inputs to zero */ set_flush_inputs_to_zero(ftz_enabled, &env->vfp.fp_status[FPST_A32]); } @@ -223,6 +228,7 @@ void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) bool fitz_enabled = (val & FPCR_FIZ) || (val & (FPCR_FZ | FPCR_AH)) == FPCR_FZ; set_flush_inputs_to_zero(fitz_enabled, &env->vfp.fp_status[FPST_A64]); + set_flush_inputs_to_zero(fitz_enabled, &env->vfp.fp_status[FPST_ZA]); } if (changed & FPCR_DN) { bool dnan_enabled = val & FPCR_DN; @@ -240,9 +246,13 @@ void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask) /* Change behaviours for A64 FP operations */ arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64]); arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); + arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_ZA]); + arm_set_ah_fp_behaviours(&env->vfp.fp_status[FPST_ZA_F16]); } else { arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64]); arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_A64_F16]); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_ZA]); + arm_set_default_fp_behaviours(&env->vfp.fp_status[FPST_ZA_F16]); } } /* From d74f0ceae557c35ee80bcc0afa20e4f95b6e3262 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:35 -0600 Subject: [PATCH 1959/2760] target/arm: Use FPST_ZA for sme_fmopa_[hsd] Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-13-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme_helper.c | 37 ++++++++-------------------------- target/arm/tcg/translate-sme.c | 4 ++-- 2 files changed, 10 insertions(+), 31 deletions(-) diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 8f33387e4b..9942d717aa 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -904,20 +904,11 @@ void HELPER(sme_addva_d)(void *vzda, void *vzn, void *vpn, } void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, float_status *fpst_in, uint32_t desc) + void *vpm, float_status *fpst, uint32_t desc) { intptr_t row, col, oprsz = simd_maxsz(desc); uint32_t neg = simd_data(desc) << 31; uint16_t *pn = vpn, *pm = vpm; - float_status fpst; - - /* - * Make a copy of float_status because this operation does not - * update the cumulative fp exception status. It also produces - * default nans. - */ - fpst = *fpst_in; - set_default_nan_mode(true, &fpst); for (row = 0; row < oprsz; ) { uint16_t pa = pn[H2(row >> 4)]; @@ -932,7 +923,7 @@ void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, if (pb & 1) { uint32_t *a = vza_row + H1_4(col); uint32_t *m = vzm + H1_4(col); - *a = float32_muladd(n, *m, *a, 0, &fpst); + *a = float32_muladd(n, *m, *a, 0, fpst); } col += 4; pb >>= 4; @@ -946,15 +937,12 @@ void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, } void HELPER(sme_fmopa_d)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, float_status *fpst_in, uint32_t desc) + void *vpm, float_status *fpst, uint32_t desc) { intptr_t row, col, oprsz = simd_oprsz(desc) / 8; uint64_t neg = (uint64_t)simd_data(desc) << 63; uint64_t *za = vza, *zn = vzn, *zm = vzm; uint8_t *pn = vpn, *pm = vpm; - float_status fpst = *fpst_in; - - set_default_nan_mode(true, &fpst); for (row = 0; row < oprsz; ++row) { if (pn[H1(row)] & 1) { @@ -964,7 +952,7 @@ void HELPER(sme_fmopa_d)(void *vza, void *vzn, void *vzm, void *vpn, for (col = 0; col < oprsz; ++col) { if (pm[H1(col)] & 1) { uint64_t *a = &za_row[col]; - *a = float64_muladd(n, zm[col], *a, 0, &fpst); + *a = float64_muladd(n, zm[col], *a, 0, fpst); } } } @@ -1065,19 +1053,8 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, intptr_t row, col, oprsz = simd_maxsz(desc); uint32_t neg = simd_data(desc) * 0x80008000u; uint16_t *pn = vpn, *pm = vpm; - float_status fpst_odd, fpst_std, fpst_f16; + float_status fpst_odd = env->vfp.fp_status[FPST_ZA]; - /* - * Make copies of the fp status fields we use, because this operation - * does not update the cumulative fp exception status. It also - * produces default NaNs. We also need a second copy of fp_status with - * round-to-odd -- see above. - */ - fpst_f16 = env->vfp.fp_status[FPST_A64_F16]; - fpst_std = env->vfp.fp_status[FPST_A64]; - set_default_nan_mode(true, &fpst_std); - set_default_nan_mode(true, &fpst_f16); - fpst_odd = fpst_std; set_float_rounding_mode(float_round_to_odd, &fpst_odd); for (row = 0; row < oprsz; ) { @@ -1097,7 +1074,9 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, m = f16mop_adj_pair(m, pcol, 0); *a = f16_dotadd(*a, n, m, - &fpst_f16, &fpst_std, &fpst_odd); + &env->vfp.fp_status[FPST_ZA_F16], + &env->vfp.fp_status[FPST_ZA], + &fpst_odd); } col += 4; pcol >>= 4; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index fcbb350016..51175c923e 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -358,9 +358,9 @@ static bool do_outprod_env(DisasContext *s, arg_op *a, MemOp esz, TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_env, a, MO_32, gen_helper_sme_fmopa_h) TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, - MO_32, FPST_A64, gen_helper_sme_fmopa_s) + MO_32, FPST_ZA, gen_helper_sme_fmopa_s) TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, - MO_64, FPST_A64, gen_helper_sme_fmopa_d) + MO_64, FPST_ZA, gen_helper_sme_fmopa_d) TRANS_FEAT(BFMOPA, aa64_sme, do_outprod_env, a, MO_32, gen_helper_sme_bfmopa) From 81123324a56fecce275a9995ddc1593e880b43ef Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:36 -0600 Subject: [PATCH 1960/2760] target/arm: Rename zarray to za_state.za The whole ZA state will also contain ZT0. Make things easier in aarch64_set_svcr to zero both by wrapping them in a common structure. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-14-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/aarch64/signal.c | 4 +-- target/arm/cpu.c | 4 +-- target/arm/cpu.h | 48 +++++++++++++++++++--------------- target/arm/helper.c | 2 +- target/arm/machine.c | 2 +- target/arm/tcg/sme_helper.c | 6 ++--- target/arm/tcg/translate-sme.c | 4 +-- 7 files changed, 38 insertions(+), 32 deletions(-) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index bc7a13800d..d50cab78d8 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -248,7 +248,7 @@ static void target_setup_za_record(struct target_za_context *za, for (i = 0; i < vl; ++i) { uint64_t *z = (void *)za + TARGET_ZA_SIG_ZAV_OFFSET(vq, i); for (j = 0; j < vq * 2; ++j) { - __put_user_e(env->zarray[i].d[j], z + j, le); + __put_user_e(env->za_state.za[i].d[j], z + j, le); } } } @@ -397,7 +397,7 @@ static bool target_restore_za_record(CPUARMState *env, for (i = 0; i < vl; ++i) { uint64_t *z = (void *)za + TARGET_ZA_SIG_ZAV_OFFSET(vq, i); for (j = 0; j < vq * 2; ++j) { - __get_user_e(env->zarray[i].d[j], z + j, le); + __get_user_e(env->za_state.za[i].d[j], z + j, le); } } return true; diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 7b207f2620..6265627762 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -1335,8 +1335,8 @@ static void aarch64_cpu_dump_state(CPUState *cs, FILE *f, int flags) qemu_fprintf(f, "ZA[%0*d]=", svl_lg10, i); for (j = zcr_len; j >= 0; --j) { qemu_fprintf(f, "%016" PRIx64 ":%016" PRIx64 "%c", - env->zarray[i].d[2 * j + 1], - env->zarray[i].d[2 * j], + env->za_state.za[i].d[2 * j + 1], + env->za_state.za[i].d[2 * j], j ? ':' : '\n'); } } diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 2530aaca22..7db97e8038 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -715,27 +715,33 @@ typedef struct CPUArchState { uint64_t scxtnum_el[4]; - /* - * SME ZA storage -- 256 x 256 byte array, with bytes in host word order, - * as we do with vfp.zregs[]. This corresponds to the architectural ZA - * array, where ZA[N] is in the least-significant bytes of env->zarray[N]. - * When SVL is less than the architectural maximum, the accessible - * storage is restricted, such that if the SVL is X bytes the guest can - * see only the bottom X elements of zarray[], and only the least - * significant X bytes of each element of the array. (In other words, - * the observable part is always square.) - * - * The ZA storage can also be considered as a set of square tiles of - * elements of different sizes. The mapping from tiles to the ZA array - * is architecturally defined, such that for tiles of elements of esz - * bytes, the Nth row (or "horizontal slice") of tile T is in - * ZA[T + N * esz]. Note that this means that each tile is not contiguous - * in the ZA storage, because its rows are striped through the ZA array. - * - * Because this is so large, keep this toward the end of the reset area, - * to keep the offsets into the rest of the structure smaller. - */ - ARMVectorReg zarray[ARM_MAX_VQ * 16]; + struct { + /* + * SME ZA storage -- 256 x 256 byte array, with bytes in host + * word order, as we do with vfp.zregs[]. This corresponds to + * the architectural ZA array, where ZA[N] is in the least + * significant bytes of env->za_state.za[N]. + * + * When SVL is less than the architectural maximum, the accessible + * storage is restricted, such that if the SVL is X bytes the guest + * can see only the bottom X elements of zarray[], and only the least + * significant X bytes of each element of the array. (In other words, + * the observable part is always square.) + * + * The ZA storage can also be considered as a set of square tiles of + * elements of different sizes. The mapping from tiles to the ZA array + * is architecturally defined, such that for tiles of elements of esz + * bytes, the Nth row (or "horizontal slice") of tile T is in + * ZA[T + N * esz]. Note that this means that each tile is not + * contiguous in the ZA storage, because its rows are striped through + * the ZA array. + * + * Because this is so large, keep this toward the end of the + * reset area, to keep the offsets into the rest of the structure + * smaller. + */ + ARMVectorReg za[ARM_MAX_VQ * 16]; + } za_state; struct CPUBreakpoint *cpu_breakpoint[16]; struct CPUWatchpoint *cpu_watchpoint[16]; diff --git a/target/arm/helper.c b/target/arm/helper.c index c311d2df21..3879bce848 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6663,7 +6663,7 @@ void aarch64_set_svcr(CPUARMState *env, uint64_t new, uint64_t mask) * when disabled either. */ if (change & new & R_SVCR_ZA_MASK) { - memset(env->zarray, 0, sizeof(env->zarray)); + memset(&env->za_state, 0, sizeof(env->za_state)); } if (tcg_enabled()) { diff --git a/target/arm/machine.c b/target/arm/machine.c index e442d48524..6e73368ef9 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -315,7 +315,7 @@ static const VMStateDescription vmstate_za = { .minimum_version_id = 1, .needed = za_needed, .fields = (const VMStateField[]) { - VMSTATE_STRUCT_ARRAY(env.zarray, ARMCPU, ARM_MAX_VQ * 16, 0, + VMSTATE_STRUCT_ARRAY(env.za_state.za, ARMCPU, ARM_MAX_VQ * 16, 0, vmstate_vreg, ARMVectorReg), VMSTATE_END_OF_LIST() } diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 9942d717aa..7b0bc1b17c 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -39,12 +39,12 @@ void helper_sme_zero(CPUARMState *env, uint32_t imm, uint32_t svl) uint32_t i; /* - * Special case clearing the entire ZA space. + * Special case clearing the entire ZArray. * This falls into the CONSTRAINED UNPREDICTABLE zeroing of any * parts of the ZA storage outside of SVL. */ if (imm == 0xff) { - memset(env->zarray, 0, sizeof(env->zarray)); + memset(env->za_state.za, 0, sizeof(env->za_state.za)); return; } @@ -54,7 +54,7 @@ void helper_sme_zero(CPUARMState *env, uint32_t imm, uint32_t svl) */ for (i = 0; i < svl; i++) { if (imm & (1 << (i % 8))) { - memset(&env->zarray[i], 0, svl); + memset(&env->za_state.za[i], 0, svl); } } } diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 51175c923e..e8b3578174 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -92,7 +92,7 @@ static TCGv_ptr get_tile_rowcol(DisasContext *s, int esz, int rs, offset = tile * sizeof(ARMVectorReg); /* Include the byte offset of zarray to make this relative to env. */ - offset += offsetof(CPUARMState, zarray); + offset += offsetof(CPUARMState, za_state.za); tcg_gen_addi_i32(tmp, tmp, offset); /* Add the byte offset to env to produce the final pointer. */ @@ -112,7 +112,7 @@ static TCGv_ptr get_tile(DisasContext *s, int esz, int tile) TCGv_ptr addr = tcg_temp_new_ptr(); int offset; - offset = tile * sizeof(ARMVectorReg) + offsetof(CPUARMState, zarray); + offset = tile * sizeof(ARMVectorReg) + offsetof(CPUARMState, za_state.za); tcg_gen_addi_ptr(addr, tcg_env, offset); return addr; From d8ff459b4021a4fdbd3bf0c4311bb24868188f0d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:37 -0600 Subject: [PATCH 1961/2760] target/arm: Add isar feature tests for SME2p1, SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-15-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu-features.h | 63 +++++++++++++++++++++++++++++++++++++++ target/arm/cpu.h | 1 + 2 files changed, 64 insertions(+) diff --git a/target/arm/cpu-features.h b/target/arm/cpu-features.h index 5d8adfb73b..5876162428 100644 --- a/target/arm/cpu-features.h +++ b/target/arm/cpu-features.h @@ -604,6 +604,11 @@ static inline bool isar_feature_aa64_rpres(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ISAR2, RPRES); } +static inline bool isar_feature_aa64_lut(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64ISAR2, LUT); +} + static inline bool isar_feature_aa64_fp_simd(const ARMISARegisters *id) { /* We always set the AdvSIMD and FP fields identically. */ @@ -932,6 +937,11 @@ static inline bool isar_feature_aa64_sve2(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ZFR0, SVEVER) != 0; } +static inline bool isar_feature_aa64_sve2p1(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, SVEVER) >=2; +} + static inline bool isar_feature_aa64_sve2_aes(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64ZFR0, AES) != 0; @@ -977,6 +987,21 @@ static inline bool isar_feature_aa64_sve_f64mm(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64ZFR0, F64MM) != 0; } +static inline bool isar_feature_aa64_sve_b16b16(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64ZFR0, B16B16); +} + +static inline bool isar_feature_aa64_sme_b16b16(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64SMFR0, B16B16); +} + +static inline bool isar_feature_aa64_sme_f16f16(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64SMFR0, F16F16); +} + static inline bool isar_feature_aa64_sme_f64f64(const ARMISARegisters *id) { return FIELD_EX64_IDREG(id, ID_AA64SMFR0, F64F64); @@ -992,6 +1017,44 @@ static inline bool isar_feature_aa64_sme_fa64(const ARMISARegisters *id) return FIELD_EX64_IDREG(id, ID_AA64SMFR0, FA64); } +static inline bool isar_feature_aa64_sme2(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64SMFR0, SMEVER) != 0; +} + +static inline bool isar_feature_aa64_sme2p1(const ARMISARegisters *id) +{ + return FIELD_EX64_IDREG(id, ID_AA64SMFR0, SMEVER) >= 2; +} + +/* + * Combinations of feature tests, for ease of use with TRANS_FEAT. + */ +static inline bool isar_feature_aa64_sme_or_sve2p1(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme(id) || isar_feature_aa64_sve2p1(id); +} + +static inline bool isar_feature_aa64_sme2_or_sve2p1(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme2(id) || isar_feature_aa64_sve2p1(id); +} + +static inline bool isar_feature_aa64_sme2p1_or_sve2p1(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme2p1(id) || isar_feature_aa64_sve2p1(id); +} + +static inline bool isar_feature_aa64_sme2_i16i64(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme2(id) && isar_feature_aa64_sme_i16i64(id); +} + +static inline bool isar_feature_aa64_sme2_f64f64(const ARMISARegisters *id) +{ + return isar_feature_aa64_sme2(id) && isar_feature_aa64_sme_f64f64(id); +} + /* * Feature tests for "does this exist in either 32-bit or 64-bit?" */ diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 7db97e8038..c6041a9357 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -2211,6 +2211,7 @@ FIELD(ID_AA64ISAR2, SYSINSTR_128, 36, 4) FIELD(ID_AA64ISAR2, PRFMSLC, 40, 4) FIELD(ID_AA64ISAR2, RPRFM, 48, 4) FIELD(ID_AA64ISAR2, CSSC, 52, 4) +FIELD(ID_AA64ISAR2, LUT, 56, 4) FIELD(ID_AA64ISAR2, ATS1A, 60, 4) FIELD(ID_AA64PFR0, EL0, 0, 4) From 694b2625dd4ac1d6f4aa4dfc8ed7380ec843cd9b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:38 -0600 Subject: [PATCH 1962/2760] target/arm: Add ZT0 This is a 512-bit array introduced with SME2. Save it only when ZA is in use. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-16-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 3 +++ target/arm/machine.c | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c6041a9357..cbc2043483 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -716,6 +716,9 @@ typedef struct CPUArchState { uint64_t scxtnum_el[4]; struct { + /* SME2 ZT0 -- 512 bit array, with data ordered like ARMVectorReg. */ + uint64_t zt0[512 / 64] QEMU_ALIGNED(16); + /* * SME ZA storage -- 256 x 256 byte array, with bytes in host * word order, as we do with vfp.zregs[]. This corresponds to diff --git a/target/arm/machine.c b/target/arm/machine.c index 6e73368ef9..6986915bee 100644 --- a/target/arm/machine.c +++ b/target/arm/machine.c @@ -321,6 +321,25 @@ static const VMStateDescription vmstate_za = { } }; +static bool zt0_needed(void *opaque) +{ + ARMCPU *cpu = opaque; + + return za_needed(cpu) && cpu_isar_feature(aa64_sme2, cpu); +} + +static const VMStateDescription vmstate_zt0 = { + .name = "cpu/zt0", + .version_id = 1, + .minimum_version_id = 1, + .needed = zt0_needed, + .fields = (VMStateField[]) { + VMSTATE_UINT64_ARRAY(env.za_state.zt0, ARMCPU, + ARRAY_SIZE(((CPUARMState *)0)->za_state.zt0)), + VMSTATE_END_OF_LIST() + } +}; + static bool serror_needed(void *opaque) { ARMCPU *cpu = opaque; @@ -1096,6 +1115,7 @@ const VMStateDescription vmstate_arm_cpu = { &vmstate_m_security, &vmstate_sve, &vmstate_za, + &vmstate_zt0, &vmstate_serror, &vmstate_irq_line_state, &vmstate_wfxt_timer, From 3aad4bc8beb9a3a8c1ea3dc6ca9bfc85d3c7c8a4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:39 -0600 Subject: [PATCH 1963/2760] target/arm: Add zt0_excp_el to DisasContext Pipe the value through from SMCR_ELx through hflags and into the disassembly context. Enable EZT0 in smcr_write. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-17-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.c | 3 +++ target/arm/cpu.h | 2 ++ target/arm/helper.c | 6 +++++- target/arm/tcg/hflags.c | 34 +++++++++++++++++++++++++++++++++- target/arm/tcg/translate-a64.c | 1 + target/arm/tcg/translate.h | 1 + 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 6265627762..08c43f674a 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -635,6 +635,9 @@ void arm_emulate_firmware_reset(CPUState *cpustate, int target_el) env->cp15.cptr_el[3] |= R_CPTR_EL3_ESM_MASK; env->cp15.scr_el3 |= SCR_ENTP2; env->vfp.smcr_el[3] = 0xf; + if (cpu_isar_feature(aa64_sme2, cpu)) { + env->vfp.smcr_el[3] |= R_SMCR_EZT0_MASK; + } } if (cpu_isar_feature(aa64_hcx, cpu)) { env->cp15.scr_el3 |= SCR_HXEN; diff --git a/target/arm/cpu.h b/target/arm/cpu.h index cbc2043483..39a9234ff2 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1513,6 +1513,7 @@ FIELD(SVCR, ZA, 1, 1) /* Fields for SMCR_ELx. */ FIELD(SMCR, LEN, 0, 4) +FIELD(SMCR, EZT0, 30, 1) FIELD(SMCR, FA64, 31, 1) /* Write a new value to v7m.exception, thus transitioning into or out @@ -3084,6 +3085,7 @@ FIELD(TBFLAG_A64, NV2_MEM_E20, 35, 1) FIELD(TBFLAG_A64, NV2_MEM_BE, 36, 1) FIELD(TBFLAG_A64, AH, 37, 1) /* FPCR.AH */ FIELD(TBFLAG_A64, NEP, 38, 1) /* FPCR.NEP */ +FIELD(TBFLAG_A64, ZT0EXC_EL, 39, 2) /* * Helpers for using the above. Note that only the A64 accessors use diff --git a/target/arm/helper.c b/target/arm/helper.c index 3879bce848..b3f0d6f17a 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -6682,10 +6682,14 @@ static void smcr_write(CPUARMState *env, const ARMCPRegInfo *ri, { int cur_el = arm_current_el(env); int old_len = sve_vqm1_for_el(env, cur_el); + uint64_t valid_mask = R_SMCR_LEN_MASK | R_SMCR_FA64_MASK; int new_len; QEMU_BUILD_BUG_ON(ARM_MAX_VQ > R_SMCR_LEN_MASK + 1); - value &= R_SMCR_LEN_MASK | R_SMCR_FA64_MASK; + if (cpu_isar_feature(aa64_sme2, env_archcpu(env))) { + valid_mask |= R_SMCR_EZT0_MASK; + } + value &= valid_mask; raw_write(env, ri, value); /* diff --git a/target/arm/tcg/hflags.c b/target/arm/tcg/hflags.c index 1ccec63bbd..59ab526375 100644 --- a/target/arm/tcg/hflags.c +++ b/target/arm/tcg/hflags.c @@ -214,6 +214,31 @@ static CPUARMTBFlags rebuild_hflags_a32(CPUARMState *env, int fp_el, return rebuild_hflags_common_32(env, fp_el, mmu_idx, flags); } +/* + * Return the exception level to which exceptions should be taken for ZT0. + * C.f. the ARM pseudocode function CheckSMEZT0Enabled, after the ZA check. + */ +static int zt0_exception_el(CPUARMState *env, int el) +{ +#ifndef CONFIG_USER_ONLY + if (el <= 1 + && !el_is_in_host(env, el) + && !FIELD_EX64(env->vfp.smcr_el[1], SMCR, EZT0)) { + return 1; + } + if (el <= 2 + && arm_is_el2_enabled(env) + && !FIELD_EX64(env->vfp.smcr_el[2], SMCR, EZT0)) { + return 2; + } + if (arm_feature(env, ARM_FEATURE_EL3) + && !FIELD_EX64(env->vfp.smcr_el[3], SMCR, EZT0)) { + return 3; + } +#endif + return 0; +} + static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, ARMMMUIdx mmu_idx) { @@ -269,7 +294,14 @@ static CPUARMTBFlags rebuild_hflags_a64(CPUARMState *env, int el, int fp_el, DP_TBFLAG_A64(flags, PSTATE_SM, 1); DP_TBFLAG_A64(flags, SME_TRAP_NONSTREAMING, !sme_fa64(env, el)); } - DP_TBFLAG_A64(flags, PSTATE_ZA, FIELD_EX64(env->svcr, SVCR, ZA)); + + if (FIELD_EX64(env->svcr, SVCR, ZA)) { + DP_TBFLAG_A64(flags, PSTATE_ZA, 1); + if (cpu_isar_feature(aa64_sme2, env_archcpu(env))) { + int zt0_el = zt0_exception_el(env, el); + DP_TBFLAG_A64(flags, ZT0EXC_EL, zt0_el); + } + } } sctlr = regime_sctlr(env, stage1); diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index b0caccca46..ad293c0885 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10139,6 +10139,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->trap_eret = EX_TBFLAG_A64(tb_flags, TRAP_ERET); dc->sve_excp_el = EX_TBFLAG_A64(tb_flags, SVEEXC_EL); dc->sme_excp_el = EX_TBFLAG_A64(tb_flags, SMEEXC_EL); + dc->zt0_excp_el = EX_TBFLAG_A64(tb_flags, ZT0EXC_EL); dc->vl = (EX_TBFLAG_A64(tb_flags, VL) + 1) * 16; dc->svl = (EX_TBFLAG_A64(tb_flags, SVL) + 1) * 16; dc->pauth_active = EX_TBFLAG_A64(tb_flags, PAUTH_ACTIVE); diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 0004a97219..b03956a793 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -70,6 +70,7 @@ typedef struct DisasContext { int fp_excp_el; /* FP exception EL or 0 if enabled */ int sve_excp_el; /* SVE exception EL or 0 if enabled */ int sme_excp_el; /* SME exception EL or 0 if enabled */ + int zt0_excp_el; /* ZT0 exception EL or 0 if enabled */ int vl; /* current vector length in bytes */ int svl; /* current streaming vector length in bytes */ bool vfp_enabled; /* FP enabled via FPSCR.EN */ From 10f02d0ced4e15343fd234d5c6b0e6725a4f7f16 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:40 -0600 Subject: [PATCH 1964/2760] target/arm: Implement SME2 ZERO ZT0 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-18-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/syndrome.h | 1 + target/arm/tcg/sme.decode | 1 + target/arm/tcg/translate-sme.c | 26 ++++++++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/target/arm/syndrome.h b/target/arm/syndrome.h index 3244e0740d..c48d3b8587 100644 --- a/target/arm/syndrome.h +++ b/target/arm/syndrome.h @@ -80,6 +80,7 @@ typedef enum { SME_ET_Streaming, SME_ET_NotStreaming, SME_ET_InactiveZA, + SME_ET_InaccessibleZT0, } SMEExceptionType; #define ARM_EL_EC_LENGTH 6 diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 628804e37a..dd1f983941 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -22,6 +22,7 @@ ### SME Misc ZERO 11000000 00 001 00000000000 imm:8 +ZERO_zt0 11000000 01 001 00000000000 00000001 ### SME Move into/from Array diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index e8b3578174..246f191eca 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -27,6 +27,19 @@ #include "decode-sme.c.inc" +static bool sme2_zt0_enabled_check(DisasContext *s) +{ + if (!sme_za_enabled_check(s)) { + return false; + } + if (s->zt0_excp_el) { + gen_exception_insn_el(s, 0, EXCP_UDEF, + syn_smetrap(SME_ET_InaccessibleZT0, false), + s->zt0_excp_el); + return false; + } + return true; +} /* * Resolve tile.size[index] to a host pointer, where tile and index @@ -130,6 +143,19 @@ static bool trans_ZERO(DisasContext *s, arg_ZERO *a) return true; } +static bool trans_ZERO_zt0(DisasContext *s, arg_ZERO_zt0 *a) +{ + if (!dc_isar_feature(aa64_sme2, s)) { + return false; + } + if (sme_enabled_check(s) && sme2_zt0_enabled_check(s)) { + tcg_gen_gvec_dup_imm(MO_64, offsetof(CPUARMState, za_state.zt0), + sizeof_field(CPUARMState, za_state.zt0), + sizeof_field(CPUARMState, za_state.zt0), 0); + } + return true; +} + static bool trans_MOVA(DisasContext *s, arg_MOVA *a) { static gen_helper_gvec_4 * const h_fns[5] = { From 83eae543efac1b2119eed5d888423fe9b87daa30 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:41 -0600 Subject: [PATCH 1965/2760] target/arm: Add alignment argument to gen_sve_{ldr, str} Honor AlignmentEnforced() for LDR/STR (vector), (predicate), and (array vector). Within the expansion functions, clear @align when we're done emitting loads at the largest size. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-19-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-a64.h | 6 ++-- target/arm/tcg/translate-sme.c | 5 ++-- target/arm/tcg/translate-sve.c | 50 ++++++++++++++++++++++++---------- 3 files changed, 42 insertions(+), 19 deletions(-) diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h index b2420f59eb..8a9f4b2053 100644 --- a/target/arm/tcg/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -225,7 +225,9 @@ void gen_gvec_usqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); -void gen_sve_ldr(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm); -void gen_sve_str(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm); +void gen_sve_ldr(DisasContext *s, TCGv_ptr, int vofs, + int len, int rn, int imm, MemOp align); +void gen_sve_str(DisasContext *s, TCGv_ptr, int vofs, + int len, int rn, int imm, MemOp align); #endif /* TARGET_ARM_TRANSLATE_A64_H */ diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 246f191eca..1dbd0199af 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -269,7 +269,7 @@ static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) return true; } -typedef void GenLdStR(DisasContext *, TCGv_ptr, int, int, int, int); +typedef void GenLdStR(DisasContext *, TCGv_ptr, int, int, int, int, MemOp); static bool do_ldst_r(DisasContext *s, arg_ldstr *a, GenLdStR *fn) { @@ -284,7 +284,8 @@ static bool do_ldst_r(DisasContext *s, arg_ldstr *a, GenLdStR *fn) /* ZA[n] equates to ZA0H.B[n]. */ base = get_tile_rowcol(s, MO_8, a->rv, imm, false); - fn(s, base, 0, svl, a->rn, imm * svl); + fn(s, base, 0, svl, a->rn, imm * svl, + s->align_mem ? MO_ALIGN_16 : MO_UNALN); return true; } diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 8403034a0e..d2cd298b5b 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4160,7 +4160,7 @@ TRANS_FEAT(UCVTF_dd, aa64_sve, gen_gvec_fpst_arg_zpz, */ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, - int len, int rn, int imm) + int len, int rn, int imm, MemOp align) { int len_align = QEMU_ALIGN_DOWN(len, 16); int len_remain = len % 16; @@ -4189,12 +4189,15 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, for (i = 0; i < len_align; i += 16) { tcg_gen_qemu_ld_i128(t16, clean_addr, midx, - MO_LE | MO_128 | MO_ATOM_NONE); + MO_LE | MO_128 | MO_ATOM_NONE | align); tcg_gen_extr_i128_i64(t0, t1, t16); tcg_gen_st_i64(t0, base, vofs + i); tcg_gen_st_i64(t1, base, vofs + i + 8); tcg_gen_addi_i64(clean_addr, clean_addr, 16); } + if (len_align) { + align = MO_UNALN; + } } else { TCGLabel *loop = gen_new_label(); TCGv_ptr tp, i = tcg_temp_new_ptr(); @@ -4204,7 +4207,7 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, t16 = tcg_temp_new_i128(); tcg_gen_qemu_ld_i128(t16, clean_addr, midx, - MO_LE | MO_128 | MO_ATOM_NONE); + MO_LE | MO_128 | MO_ATOM_NONE | align); tcg_gen_addi_i64(clean_addr, clean_addr, 16); tp = tcg_temp_new_ptr(); @@ -4219,6 +4222,7 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, tcg_gen_st_i64(t1, tp, vofs + 8); tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); + align = MO_UNALN; } /* @@ -4227,7 +4231,9 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, */ if (len_remain >= 8) { t0 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUQ | MO_ATOM_NONE); + tcg_gen_qemu_ld_i64(t0, clean_addr, midx, + MO_LEUQ | MO_ATOM_NONE | align); + align = MO_UNALN; tcg_gen_st_i64(t0, base, vofs + len_align); len_remain -= 8; len_align += 8; @@ -4242,12 +4248,14 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, case 4: case 8: tcg_gen_qemu_ld_i64(t0, clean_addr, midx, - MO_LE | ctz32(len_remain) | MO_ATOM_NONE); + MO_LE | ctz32(len_remain) + | MO_ATOM_NONE | align); break; case 6: t1 = tcg_temp_new_i64(); - tcg_gen_qemu_ld_i64(t0, clean_addr, midx, MO_LEUL | MO_ATOM_NONE); + tcg_gen_qemu_ld_i64(t0, clean_addr, midx, + MO_LEUL | MO_ATOM_NONE | align); tcg_gen_addi_i64(clean_addr, clean_addr, 4); tcg_gen_qemu_ld_i64(t1, clean_addr, midx, MO_LEUW | MO_ATOM_NONE); tcg_gen_deposit_i64(t0, t0, t1, 32, 32); @@ -4262,7 +4270,7 @@ void gen_sve_ldr(DisasContext *s, TCGv_ptr base, int vofs, /* Similarly for stores. */ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, - int len, int rn, int imm) + int len, int rn, int imm, MemOp align) { int len_align = QEMU_ALIGN_DOWN(len, 16); int len_remain = len % 16; @@ -4294,9 +4302,12 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, tcg_gen_ld_i64(t1, base, vofs + i + 8); tcg_gen_concat_i64_i128(t16, t0, t1); tcg_gen_qemu_st_i128(t16, clean_addr, midx, - MO_LE | MO_128 | MO_ATOM_NONE); + MO_LE | MO_128 | MO_ATOM_NONE | align); tcg_gen_addi_i64(clean_addr, clean_addr, 16); } + if (len_align) { + align = MO_UNALN; + } } else { TCGLabel *loop = gen_new_label(); TCGv_ptr tp, i = tcg_temp_new_ptr(); @@ -4320,13 +4331,16 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, tcg_gen_addi_i64(clean_addr, clean_addr, 16); tcg_gen_brcondi_ptr(TCG_COND_LTU, i, len_align, loop); + align = MO_UNALN; } /* Predicate register stores can be any multiple of 2. */ if (len_remain >= 8) { t0 = tcg_temp_new_i64(); tcg_gen_ld_i64(t0, base, vofs + len_align); - tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUQ | MO_ATOM_NONE); + tcg_gen_qemu_st_i64(t0, clean_addr, midx, + MO_LEUQ | MO_ATOM_NONE | align); + align = MO_UNALN; len_remain -= 8; len_align += 8; if (len_remain) { @@ -4342,11 +4356,13 @@ void gen_sve_str(DisasContext *s, TCGv_ptr base, int vofs, case 4: case 8: tcg_gen_qemu_st_i64(t0, clean_addr, midx, - MO_LE | ctz32(len_remain) | MO_ATOM_NONE); + MO_LE | ctz32(len_remain) + | MO_ATOM_NONE | align); break; case 6: - tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUL | MO_ATOM_NONE); + tcg_gen_qemu_st_i64(t0, clean_addr, midx, + MO_LEUL | MO_ATOM_NONE | align); tcg_gen_addi_i64(clean_addr, clean_addr, 4); tcg_gen_shri_i64(t0, t0, 32); tcg_gen_qemu_st_i64(t0, clean_addr, midx, MO_LEUW | MO_ATOM_NONE); @@ -4366,7 +4382,8 @@ static bool trans_LDR_zri(DisasContext *s, arg_rri *a) if (sve_access_check(s)) { int size = vec_full_reg_size(s); int off = vec_full_reg_offset(s, a->rd); - gen_sve_ldr(s, tcg_env, off, size, a->rn, a->imm * size); + gen_sve_ldr(s, tcg_env, off, size, a->rn, a->imm * size, + s->align_mem ? MO_ALIGN_16 : MO_UNALN); } return true; } @@ -4379,7 +4396,8 @@ static bool trans_LDR_pri(DisasContext *s, arg_rri *a) if (sve_access_check(s)) { int size = pred_full_reg_size(s); int off = pred_full_reg_offset(s, a->rd); - gen_sve_ldr(s, tcg_env, off, size, a->rn, a->imm * size); + gen_sve_ldr(s, tcg_env, off, size, a->rn, a->imm * size, + s->align_mem ? MO_ALIGN_2 : MO_UNALN); } return true; } @@ -4392,7 +4410,8 @@ static bool trans_STR_zri(DisasContext *s, arg_rri *a) if (sve_access_check(s)) { int size = vec_full_reg_size(s); int off = vec_full_reg_offset(s, a->rd); - gen_sve_str(s, tcg_env, off, size, a->rn, a->imm * size); + gen_sve_str(s, tcg_env, off, size, a->rn, a->imm * size, + s->align_mem ? MO_ALIGN_16 : MO_UNALN); } return true; } @@ -4405,7 +4424,8 @@ static bool trans_STR_pri(DisasContext *s, arg_rri *a) if (sve_access_check(s)) { int size = pred_full_reg_size(s); int off = pred_full_reg_offset(s, a->rd); - gen_sve_str(s, tcg_env, off, size, a->rn, a->imm * size); + gen_sve_str(s, tcg_env, off, size, a->rn, a->imm * size, + s->align_mem ? MO_ALIGN_2 : MO_UNALN); } return true; } From 12511e2a7621d7e812ef76c4b6f0846ce494f5b1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:42 -0600 Subject: [PATCH 1966/2760] target/arm: Implement SME2 LDR/STR ZT0 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-20-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 6 ++++++ target/arm/tcg/translate-sme.c | 13 +++++++++++++ 2 files changed, 19 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index dd1f983941..cef49c3b29 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -55,6 +55,12 @@ LDST1 1110000 111 st:1 rm:5 v:1 .. pg:3 rn:5 0 za_imm:4 \ LDR 1110000 100 0 000000 .. 000 ..... 0 .... @ldstr STR 1110000 100 1 000000 .. 000 ..... 0 .... @ldstr +&ldstzt0 rn +@ldstzt0 ....... ... . ...... .. ... rn:5 ..... &ldstzt0 + +LDR_zt0 1110000 100 0 111111 00 000 ..... 00000 @ldstzt0 +STR_zt0 1110000 100 1 111111 00 000 ..... 00000 @ldstzt0 + ### SME Add Vector to Array &adda zad zn pm pn diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 1dbd0199af..9f25273992 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -292,6 +292,19 @@ static bool do_ldst_r(DisasContext *s, arg_ldstr *a, GenLdStR *fn) TRANS_FEAT(LDR, aa64_sme, do_ldst_r, a, gen_sve_ldr) TRANS_FEAT(STR, aa64_sme, do_ldst_r, a, gen_sve_str) +static bool do_ldst_zt0(DisasContext *s, arg_ldstzt0 *a, GenLdStR *fn) +{ + if (sme2_zt0_enabled_check(s)) { + fn(s, tcg_env, offsetof(CPUARMState, za_state.zt0), + sizeof_field(CPUARMState, za_state.zt0), a->rn, 0, + s->align_mem ? MO_ALIGN_16 : MO_UNALN); + } + return true; +} + +TRANS_FEAT(LDR_zt0, aa64_sme2, do_ldst_zt0, a, gen_sve_ldr) +TRANS_FEAT(STR_zt0, aa64_sme2, do_ldst_zt0, a, gen_sve_str) + static bool do_adda(DisasContext *s, arg_adda *a, MemOp esz, gen_helper_gvec_4 *fn) { From 4fc8b7f4bcb7d16b02afb9ca1ddae538bba03c17 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:43 -0600 Subject: [PATCH 1967/2760] target/arm: Implement SME2 MOVT Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-21-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 5 +++++ target/arm/tcg/translate-sme.c | 13 +++++++++++++ 2 files changed, 18 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index cef49c3b29..83ca6a9104 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -39,6 +39,11 @@ MOVA 11000000 esz:2 00001 0 v:1 .. pg:3 0 za_imm:4 zr:5 \ MOVA 11000000 11 00001 1 v:1 .. pg:3 0 za_imm:4 zr:5 \ &mova to_vec=1 rs=%mova_rs esz=4 +### SME Move into/from ZT0 + +MOVT_rzt 1100 0000 0100 1100 0 off:3 00 11111 rt:5 +MOVT_ztr 1100 0000 0100 1110 0 off:3 00 11111 rt:5 + ### SME Memory &ldst esz rs pg rn rm za_imm v:bool st:bool diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 9f25273992..797035e289 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -210,6 +210,19 @@ static bool trans_MOVA(DisasContext *s, arg_MOVA *a) return true; } +static bool do_movt(DisasContext *s, arg_MOVT_rzt *a, + void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) +{ + if (sme2_zt0_enabled_check(s)) { + func(cpu_reg(s, a->rt), tcg_env, + offsetof(CPUARMState, za_state.zt0) + a->off * 8); + } + return true; +} + +TRANS_FEAT(MOVT_rzt, aa64_sme2, do_movt, a, tcg_gen_ld_i64) +TRANS_FEAT(MOVT_ztr, aa64_sme2, do_movt, a, tcg_gen_st_i64) + static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) { typedef void GenLdSt1(TCGv_env, TCGv_ptr, TCGv_ptr, TCGv, TCGv_i32); From 51a64f1e772746ad0581a3b0d7637995fa608540 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:44 -0600 Subject: [PATCH 1968/2760] target/arm: Split get_tile_rowcol argument tile_index Decode tile number and index offset beforehand and separately. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-22-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 52 +++++++++++++++++++++++----------- target/arm/tcg/translate-sme.c | 17 ++++------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 83ca6a9104..efe369e079 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -27,17 +27,29 @@ ZERO_zt0 11000000 01 001 00000000000 00000001 ### SME Move into/from Array %mova_rs 13:2 !function=plus_12 -&mova esz rs pg zr za_imm v:bool to_vec:bool - -MOVA 11000000 esz:2 00000 0 v:1 .. pg:3 zr:5 0 za_imm:4 \ - &mova to_vec=0 rs=%mova_rs -MOVA 11000000 11 00000 1 v:1 .. pg:3 zr:5 0 za_imm:4 \ - &mova to_vec=0 rs=%mova_rs esz=4 - -MOVA 11000000 esz:2 00001 0 v:1 .. pg:3 0 za_imm:4 zr:5 \ - &mova to_vec=1 rs=%mova_rs -MOVA 11000000 11 00001 1 v:1 .. pg:3 0 za_imm:4 zr:5 \ - &mova to_vec=1 rs=%mova_rs esz=4 +&mova esz rs pg zr za off v:bool to_vec:bool + +MOVA 11000000 00 00000 0 v:1 .. pg:3 zr:5 0 off:4 \ + &mova to_vec=0 rs=%mova_rs esz=0 za=0 +MOVA 11000000 01 00000 0 v:1 .. pg:3 zr:5 0 za:1 off:3 \ + &mova to_vec=0 rs=%mova_rs esz=1 +MOVA 11000000 10 00000 0 v:1 .. pg:3 zr:5 0 za:2 off:2 \ + &mova to_vec=0 rs=%mova_rs esz=2 +MOVA 11000000 11 00000 0 v:1 .. pg:3 zr:5 0 za:3 off:1 \ + &mova to_vec=0 rs=%mova_rs esz=3 +MOVA 11000000 11 00000 1 v:1 .. pg:3 zr:5 0 za:4 \ + &mova to_vec=0 rs=%mova_rs esz=4 off=0 + +MOVA 11000000 00 00001 0 v:1 .. pg:3 0 off:4 zr:5 \ + &mova to_vec=1 rs=%mova_rs esz=0 za=0 +MOVA 11000000 01 00001 0 v:1 .. pg:3 0 za:1 off:3 zr:5 \ + &mova to_vec=1 rs=%mova_rs esz=1 +MOVA 11000000 10 00001 0 v:1 .. pg:3 0 za:2 off:2 zr:5 \ + &mova to_vec=1 rs=%mova_rs esz=2 +MOVA 11000000 11 00001 0 v:1 .. pg:3 0 za:3 off:1 zr:5 \ + &mova to_vec=1 rs=%mova_rs esz=3 +MOVA 11000000 11 00001 1 v:1 .. pg:3 0 za:4 zr:5 \ + &mova to_vec=1 rs=%mova_rs esz=4 off=0 ### SME Move into/from ZT0 @@ -46,12 +58,18 @@ MOVT_ztr 1100 0000 0100 1110 0 off:3 00 11111 rt:5 ### SME Memory -&ldst esz rs pg rn rm za_imm v:bool st:bool - -LDST1 1110000 0 esz:2 st:1 rm:5 v:1 .. pg:3 rn:5 0 za_imm:4 \ - &ldst rs=%mova_rs -LDST1 1110000 111 st:1 rm:5 v:1 .. pg:3 rn:5 0 za_imm:4 \ - &ldst esz=4 rs=%mova_rs +&ldst esz rs pg rn rm za off v:bool st:bool + +LDST1 1110000 0 00 st:1 rm:5 v:1 .. pg:3 rn:5 0 off:4 \ + &ldst rs=%mova_rs esz=0 za=0 +LDST1 1110000 0 01 st:1 rm:5 v:1 .. pg:3 rn:5 0 za:1 off:3 \ + &ldst rs=%mova_rs esz=1 +LDST1 1110000 0 10 st:1 rm:5 v:1 .. pg:3 rn:5 0 za:2 off:2 \ + &ldst rs=%mova_rs esz=2 +LDST1 1110000 0 11 st:1 rm:5 v:1 .. pg:3 rn:5 0 za:3 off:1 \ + &ldst rs=%mova_rs esz=3 +LDST1 1110000 1 11 st:1 rm:5 v:1 .. pg:3 rn:5 0 za:4 \ + &ldst rs=%mova_rs esz=4 off=0 &ldstr rv rn imm @ldstr ....... ... . ...... .. ... rn:5 . imm:4 \ diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 797035e289..e22ec393fd 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -41,15 +41,10 @@ static bool sme2_zt0_enabled_check(DisasContext *s) return true; } -/* - * Resolve tile.size[index] to a host pointer, where tile and index - * are always decoded together, dependent on the element size. - */ +/* Resolve tile.size[rs+imm] to a host pointer. */ static TCGv_ptr get_tile_rowcol(DisasContext *s, int esz, int rs, - int tile_index, bool vertical) + int tile, int imm, bool vertical) { - int tile = tile_index >> (4 - esz); - int index = esz == MO_128 ? 0 : extract32(tile_index, 0, 4 - esz); int pos, len, offset; TCGv_i32 tmp; TCGv_ptr addr; @@ -57,7 +52,7 @@ static TCGv_ptr get_tile_rowcol(DisasContext *s, int esz, int rs, /* Compute the final index, which is Rs+imm. */ tmp = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(tmp, cpu_reg(s, rs)); - tcg_gen_addi_i32(tmp, tmp, index); + tcg_gen_addi_i32(tmp, tmp, imm); /* Prepare a power-of-two modulo via extraction of @len bits. */ len = ctz32(streaming_vec_reg_size(s)) - esz; @@ -185,7 +180,7 @@ static bool trans_MOVA(DisasContext *s, arg_MOVA *a) return true; } - t_za = get_tile_rowcol(s, a->esz, a->rs, a->za_imm, a->v); + t_za = get_tile_rowcol(s, a->esz, a->rs, a->za, a->off, a->v); t_zr = vec_full_reg_ptr(s, a->zr); t_pg = pred_full_reg_ptr(s, a->pg); @@ -264,7 +259,7 @@ static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) return true; } - t_za = get_tile_rowcol(s, a->esz, a->rs, a->za_imm, a->v); + t_za = get_tile_rowcol(s, a->esz, a->rs, a->za, a->off, a->v); t_pg = pred_full_reg_ptr(s, a->pg); addr = tcg_temp_new_i64(); @@ -295,7 +290,7 @@ static bool do_ldst_r(DisasContext *s, arg_ldstr *a, GenLdStR *fn) } /* ZA[n] equates to ZA0H.B[n]. */ - base = get_tile_rowcol(s, MO_8, a->rv, imm, false); + base = get_tile_rowcol(s, MO_8, a->rv, 0, imm, false); fn(s, base, 0, svl, a->rn, imm * svl, s->align_mem ? MO_ALIGN_16 : MO_UNALN); From 2c43bee917bbb2c1844c30a21baaceee7874265c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:45 -0600 Subject: [PATCH 1969/2760] target/arm: Rename MOVA for translate Prepare for more kinds of MOVA from SME2 by renaming the existing SME1 MOVA to indicate tile to/from vector. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-23-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 46 +++++++++++++++++----------------- target/arm/tcg/translate-sme.c | 12 ++++----- 2 files changed, 29 insertions(+), 29 deletions(-) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index efe369e079..459b96805f 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -27,29 +27,29 @@ ZERO_zt0 11000000 01 001 00000000000 00000001 ### SME Move into/from Array %mova_rs 13:2 !function=plus_12 -&mova esz rs pg zr za off v:bool to_vec:bool - -MOVA 11000000 00 00000 0 v:1 .. pg:3 zr:5 0 off:4 \ - &mova to_vec=0 rs=%mova_rs esz=0 za=0 -MOVA 11000000 01 00000 0 v:1 .. pg:3 zr:5 0 za:1 off:3 \ - &mova to_vec=0 rs=%mova_rs esz=1 -MOVA 11000000 10 00000 0 v:1 .. pg:3 zr:5 0 za:2 off:2 \ - &mova to_vec=0 rs=%mova_rs esz=2 -MOVA 11000000 11 00000 0 v:1 .. pg:3 zr:5 0 za:3 off:1 \ - &mova to_vec=0 rs=%mova_rs esz=3 -MOVA 11000000 11 00000 1 v:1 .. pg:3 zr:5 0 za:4 \ - &mova to_vec=0 rs=%mova_rs esz=4 off=0 - -MOVA 11000000 00 00001 0 v:1 .. pg:3 0 off:4 zr:5 \ - &mova to_vec=1 rs=%mova_rs esz=0 za=0 -MOVA 11000000 01 00001 0 v:1 .. pg:3 0 za:1 off:3 zr:5 \ - &mova to_vec=1 rs=%mova_rs esz=1 -MOVA 11000000 10 00001 0 v:1 .. pg:3 0 za:2 off:2 zr:5 \ - &mova to_vec=1 rs=%mova_rs esz=2 -MOVA 11000000 11 00001 0 v:1 .. pg:3 0 za:3 off:1 zr:5 \ - &mova to_vec=1 rs=%mova_rs esz=3 -MOVA 11000000 11 00001 1 v:1 .. pg:3 0 za:4 zr:5 \ - &mova to_vec=1 rs=%mova_rs esz=4 off=0 +&mova_p esz rs pg zr za off v:bool + +MOVA_tz 11000000 00 00000 0 v:1 .. pg:3 zr:5 0 off:4 \ + &mova_p rs=%mova_rs esz=0 za=0 +MOVA_tz 11000000 01 00000 0 v:1 .. pg:3 zr:5 0 za:1 off:3 \ + &mova_p rs=%mova_rs esz=1 +MOVA_tz 11000000 10 00000 0 v:1 .. pg:3 zr:5 0 za:2 off:2 \ + &mova_p rs=%mova_rs esz=2 +MOVA_tz 11000000 11 00000 0 v:1 .. pg:3 zr:5 0 za:3 off:1 \ + &mova_p rs=%mova_rs esz=3 +MOVA_tz 11000000 11 00000 1 v:1 .. pg:3 zr:5 0 za:4 \ + &mova_p rs=%mova_rs esz=4 off=0 + +MOVA_zt 11000000 00 00001 0 v:1 .. pg:3 0 off:4 zr:5 \ + &mova_p rs=%mova_rs esz=0 za=0 +MOVA_zt 11000000 01 00001 0 v:1 .. pg:3 0 za:1 off:3 zr:5 \ + &mova_p rs=%mova_rs esz=1 +MOVA_zt 11000000 10 00001 0 v:1 .. pg:3 0 za:2 off:2 zr:5 \ + &mova_p rs=%mova_rs esz=2 +MOVA_zt 11000000 11 00001 0 v:1 .. pg:3 0 za:3 off:1 zr:5 \ + &mova_p rs=%mova_rs esz=3 +MOVA_zt 11000000 11 00001 1 v:1 .. pg:3 0 za:4 zr:5 \ + &mova_p rs=%mova_rs esz=4 off=0 ### SME Move into/from ZT0 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index e22ec393fd..2c8cb24b7c 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -151,7 +151,7 @@ static bool trans_ZERO_zt0(DisasContext *s, arg_ZERO_zt0 *a) return true; } -static bool trans_MOVA(DisasContext *s, arg_MOVA *a) +static bool do_mova_tile(DisasContext *s, arg_mova_p *a, bool to_vec) { static gen_helper_gvec_4 * const h_fns[5] = { gen_helper_sve_sel_zpzz_b, gen_helper_sve_sel_zpzz_h, @@ -173,9 +173,6 @@ static bool trans_MOVA(DisasContext *s, arg_MOVA *a) TCGv_i32 t_desc; int svl; - if (!dc_isar_feature(aa64_sme, s)) { - return false; - } if (!sme_smza_enabled_check(s)) { return true; } @@ -189,14 +186,14 @@ static bool trans_MOVA(DisasContext *s, arg_MOVA *a) if (a->v) { /* Vertical slice -- use sme mova helpers. */ - if (a->to_vec) { + if (to_vec) { zc_fns[a->esz](t_zr, t_za, t_pg, t_desc); } else { cz_fns[a->esz](t_za, t_zr, t_pg, t_desc); } } else { /* Horizontal slice -- reuse sve sel helpers. */ - if (a->to_vec) { + if (to_vec) { h_fns[a->esz](t_zr, t_za, t_zr, t_pg, t_desc); } else { h_fns[a->esz](t_za, t_zr, t_za, t_pg, t_desc); @@ -205,6 +202,9 @@ static bool trans_MOVA(DisasContext *s, arg_MOVA *a) return true; } +TRANS_FEAT(MOVA_tz, aa64_sme, do_mova_tile, a, false) +TRANS_FEAT(MOVA_zt, aa64_sme, do_mova_tile, a, true) + static bool do_movt(DisasContext *s, arg_MOVT_rzt *a, void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) { From 7afb9855fc81a0ac138b398aa645aff552d52ea5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:46 -0600 Subject: [PATCH 1970/2760] target/arm: Split out get_zarray Prepare for MOVA array to/from vector with multiple registers by adding a div_len parameter, herein always 1, and a vec_mod parameter, herein always 0. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-24-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-sme.c | 47 +++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 2c8cb24b7c..ea0e5a7cb5 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -43,7 +43,8 @@ static bool sme2_zt0_enabled_check(DisasContext *s) /* Resolve tile.size[rs+imm] to a host pointer. */ static TCGv_ptr get_tile_rowcol(DisasContext *s, int esz, int rs, - int tile, int imm, bool vertical) + int tile, int imm, int div_len, + int vec_mod, bool vertical) { int pos, len, offset; TCGv_i32 tmp; @@ -52,10 +53,23 @@ static TCGv_ptr get_tile_rowcol(DisasContext *s, int esz, int rs, /* Compute the final index, which is Rs+imm. */ tmp = tcg_temp_new_i32(); tcg_gen_trunc_tl_i32(tmp, cpu_reg(s, rs)); + /* + * Round the vector index down to a multiple of vec_mod if necessary. + * We do this before adding the offset, to handle cases like + * MOVA (tile to vector, 2 registers) where we want to call this + * several times in a loop with an increasing offset. We rely on + * the instruction encodings always forcing the initial offset in + * [rs + offset] to be a multiple of vec_mod. The pseudocode usually + * does the round-down after adding the offset rather than before, + * but MOVA is an exception. + */ + if (vec_mod > 1) { + tcg_gen_andc_i32(tmp, tmp, tcg_constant_i32(vec_mod - 1)); + } tcg_gen_addi_i32(tmp, tmp, imm); /* Prepare a power-of-two modulo via extraction of @len bits. */ - len = ctz32(streaming_vec_reg_size(s)) - esz; + len = ctz32(streaming_vec_reg_size(s) / div_len) - esz; if (!len) { /* @@ -111,6 +125,14 @@ static TCGv_ptr get_tile_rowcol(DisasContext *s, int esz, int rs, return addr; } +/* Resolve ZArray[rs+imm] to a host pointer. */ +static TCGv_ptr get_zarray(DisasContext *s, int rs, int imm, + int div_len, int vec_mod) +{ + /* ZA[n] equates to ZA0H.B[n]. */ + return get_tile_rowcol(s, MO_8, rs, 0, imm, div_len, vec_mod, false); +} + /* * Resolve tile.size[0] to a host pointer. * Used by e.g. outer product insns where we require the entire tile. @@ -177,7 +199,7 @@ static bool do_mova_tile(DisasContext *s, arg_mova_p *a, bool to_vec) return true; } - t_za = get_tile_rowcol(s, a->esz, a->rs, a->za, a->off, a->v); + t_za = get_tile_rowcol(s, a->esz, a->rs, a->za, a->off, 1, 0, a->v); t_zr = vec_full_reg_ptr(s, a->zr); t_pg = pred_full_reg_ptr(s, a->pg); @@ -259,7 +281,7 @@ static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) return true; } - t_za = get_tile_rowcol(s, a->esz, a->rs, a->za, a->off, a->v); + t_za = get_tile_rowcol(s, a->esz, a->rs, a->za, a->off, 1, 0, a->v); t_pg = pred_full_reg_ptr(s, a->pg); addr = tcg_temp_new_i64(); @@ -281,19 +303,14 @@ typedef void GenLdStR(DisasContext *, TCGv_ptr, int, int, int, int, MemOp); static bool do_ldst_r(DisasContext *s, arg_ldstr *a, GenLdStR *fn) { - int svl = streaming_vec_reg_size(s); - int imm = a->imm; - TCGv_ptr base; + if (sme_za_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + int imm = a->imm; + TCGv_ptr base = get_zarray(s, a->rv, imm, 1, 0); - if (!sme_za_enabled_check(s)) { - return true; + fn(s, base, 0, svl, a->rn, imm * svl, + s->align_mem ? MO_ALIGN_16 : MO_UNALN); } - - /* ZA[n] equates to ZA0H.B[n]. */ - base = get_tile_rowcol(s, MO_8, a->rv, 0, imm, false); - - fn(s, base, 0, svl, a->rn, imm * svl, - s->align_mem ? MO_ALIGN_16 : MO_UNALN); return true; } From c48d0471beb0cb197efda0b2be3fa75b4628a673 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:47 -0600 Subject: [PATCH 1971/2760] target/arm: Introduce ARMCPU.sme_max_vq Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-25-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/cpu.h | 1 + target/arm/cpu64.c | 1 + 2 files changed, 2 insertions(+) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 39a9234ff2..c8cf0ab417 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1136,6 +1136,7 @@ struct ArchCPU { /* Used to set the maximum vector length the cpu will support. */ uint32_t sve_max_vq; + uint32_t sme_max_vq; #ifdef CONFIG_USER_ONLY /* Used to set the default vector length at process start. */ diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index 77e7c4a6a5..bd33d6cc6e 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -363,6 +363,7 @@ void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp) } cpu->sme_vq.map = vq_map; + cpu->sme_max_vq = 32 - clz32(vq_map); } static bool cpu_arm_get_sme(Object *obj, Error **errp) From 72cd08eb7614af79c7ef9fd66ff4e51352ad103b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:48 -0600 Subject: [PATCH 1972/2760] target/arm: Implement SME2 MOVA to/from tile, multiple registers Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-26-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 9 +++++ target/arm/tcg/sme.decode | 37 ++++++++++++++++++ target/arm/tcg/sme_helper.c | 64 ++++++++++++++++++++++++++++++ target/arm/tcg/translate-a64.c | 1 + target/arm/tcg/translate-sme.c | 71 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate.h | 1 + 6 files changed, 183 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 858d69188f..8246ce774c 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -33,6 +33,15 @@ DEF_HELPER_FLAGS_4(sme_mova_zc_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme_mova_cz_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme_mova_zc_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_mova_cz_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_mova_zc_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_mova_cz_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_mova_zc_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_mova_cz_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_mova_zc_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_mova_cz_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_mova_zc_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sme_ld1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_5(sme_ld1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_5(sme_ld1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 459b96805f..5eca5f4acf 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -28,6 +28,7 @@ ZERO_zt0 11000000 01 001 00000000000 00000001 %mova_rs 13:2 !function=plus_12 &mova_p esz rs pg zr za off v:bool +&mova_t esz rs zr za off v:bool MOVA_tz 11000000 00 00000 0 v:1 .. pg:3 zr:5 0 off:4 \ &mova_p rs=%mova_rs esz=0 za=0 @@ -51,6 +52,42 @@ MOVA_zt 11000000 11 00001 0 v:1 .. pg:3 0 za:3 off:1 zr:5 \ MOVA_zt 11000000 11 00001 1 v:1 .. pg:3 0 za:4 zr:5 \ &mova_p rs=%mova_rs esz=4 off=0 +MOVA_tz2 11000000 00 00010 0 v:1 .. 000 zr:4 0 00 off:3 \ + &mova_t rs=%mova_rs esz=0 za=0 +MOVA_tz2 11000000 01 00010 0 v:1 .. 000 zr:4 0 00 za:1 off:2 \ + &mova_t rs=%mova_rs esz=1 +MOVA_tz2 11000000 10 00010 0 v:1 .. 000 zr:4 0 00 za:2 off:1 \ + &mova_t rs=%mova_rs esz=2 +MOVA_tz2 11000000 11 00010 0 v:1 .. 000 zr:4 0 00 za:3 \ + &mova_t rs=%mova_rs esz=3 off=0 + +MOVA_zt2 11000000 00 00011 0 v:1 .. 000 00 off:3 zr:4 0 \ + &mova_t rs=%mova_rs esz=0 za=0 +MOVA_zt2 11000000 01 00011 0 v:1 .. 000 00 za:1 off:2 zr:4 0 \ + &mova_t rs=%mova_rs esz=1 +MOVA_zt2 11000000 10 00011 0 v:1 .. 000 00 za:2 off:1 zr:4 0 \ + &mova_t rs=%mova_rs esz=2 +MOVA_zt2 11000000 11 00011 0 v:1 .. 000 00 za:3 zr:4 0 \ + &mova_t rs=%mova_rs esz=3 off=0 + +MOVA_tz4 11000000 00 00010 0 v:1 .. 001 zr:3 00 000 off:2 \ + &mova_t rs=%mova_rs esz=0 za=0 +MOVA_tz4 11000000 01 00010 0 v:1 .. 001 zr:3 00 000 za:1 off:1 \ + &mova_t rs=%mova_rs esz=1 +MOVA_tz4 11000000 10 00010 0 v:1 .. 001 zr:3 00 000 za:2 \ + &mova_t rs=%mova_rs esz=2 off=0 +MOVA_tz4 11000000 11 00010 0 v:1 .. 001 zr:3 00 00 za:3 \ + &mova_t rs=%mova_rs esz=3 off=0 + +MOVA_zt4 11000000 00 00011 0 v:1 .. 001 000 off:2 zr:3 00 \ + &mova_t rs=%mova_rs esz=0 za=0 +MOVA_zt4 11000000 01 00011 0 v:1 .. 001 000 za:1 off:1 zr:3 00 \ + &mova_t rs=%mova_rs esz=1 +MOVA_zt4 11000000 10 00011 0 v:1 .. 001 000 za:2 zr:3 00 \ + &mova_t rs=%mova_rs esz=2 off=0 +MOVA_zt4 11000000 11 00011 0 v:1 .. 001 00 za:3 zr:3 00 \ + &mova_t rs=%mova_rs esz=3 off=0 + ### SME Move into/from ZT0 MOVT_rzt 1100 0000 0100 1100 0 off:3 00 11111 rt:5 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 7b0bc1b17c..baeaa3e069 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -206,6 +206,50 @@ void HELPER(sme_mova_zc_q)(void *vd, void *za, void *vg, uint32_t desc) #undef DO_MOVA_Z +void HELPER(sme2_mova_zc_b)(void *vdst, void *vsrc, uint32_t desc) +{ + const uint8_t *src = vsrc; + uint8_t *dst = vdst; + size_t i, n = simd_oprsz(desc); + + for (i = 0; i < n; ++i) { + dst[i] = src[tile_vslice_index(i)]; + } +} + +void HELPER(sme2_mova_zc_h)(void *vdst, void *vsrc, uint32_t desc) +{ + const uint16_t *src = vsrc; + uint16_t *dst = vdst; + size_t i, n = simd_oprsz(desc) / 2; + + for (i = 0; i < n; ++i) { + dst[i] = src[tile_vslice_index(i)]; + } +} + +void HELPER(sme2_mova_zc_s)(void *vdst, void *vsrc, uint32_t desc) +{ + const uint32_t *src = vsrc; + uint32_t *dst = vdst; + size_t i, n = simd_oprsz(desc) / 4; + + for (i = 0; i < n; ++i) { + dst[i] = src[tile_vslice_index(i)]; + } +} + +void HELPER(sme2_mova_zc_d)(void *vdst, void *vsrc, uint32_t desc) +{ + const uint64_t *src = vsrc; + uint64_t *dst = vdst; + size_t i, n = simd_oprsz(desc) / 8; + + for (i = 0; i < n; ++i) { + dst[i] = src[tile_vslice_index(i)]; + } +} + /* * Clear elements in a tile slice comprising len bytes. */ @@ -314,6 +358,26 @@ static void copy_vertical_q(void *vdst, const void *vsrc, size_t len) } } +void HELPER(sme2_mova_cz_b)(void *vdst, void *vsrc, uint32_t desc) +{ + copy_vertical_b(vdst, vsrc, simd_oprsz(desc)); +} + +void HELPER(sme2_mova_cz_h)(void *vdst, void *vsrc, uint32_t desc) +{ + copy_vertical_h(vdst, vsrc, simd_oprsz(desc)); +} + +void HELPER(sme2_mova_cz_s)(void *vdst, void *vsrc, uint32_t desc) +{ + copy_vertical_s(vdst, vsrc, simd_oprsz(desc)); +} + +void HELPER(sme2_mova_cz_d)(void *vdst, void *vsrc, uint32_t desc) +{ + copy_vertical_d(vdst, vsrc, simd_oprsz(desc)); +} + /* * Host and TLB primitives for vertical tile slice addressing. */ diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index ad293c0885..d823036c96 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -10142,6 +10142,7 @@ static void aarch64_tr_init_disas_context(DisasContextBase *dcbase, dc->zt0_excp_el = EX_TBFLAG_A64(tb_flags, ZT0EXC_EL); dc->vl = (EX_TBFLAG_A64(tb_flags, VL) + 1) * 16; dc->svl = (EX_TBFLAG_A64(tb_flags, SVL) + 1) * 16; + dc->max_svl = arm_cpu->sme_max_vq * 16; dc->pauth_active = EX_TBFLAG_A64(tb_flags, PAUTH_ACTIVE); dc->bt = EX_TBFLAG_A64(tb_flags, BT); dc->btype = EX_TBFLAG_A64(tb_flags, BTYPE); diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index ea0e5a7cb5..7d4c7d7e85 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -227,6 +227,77 @@ static bool do_mova_tile(DisasContext *s, arg_mova_p *a, bool to_vec) TRANS_FEAT(MOVA_tz, aa64_sme, do_mova_tile, a, false) TRANS_FEAT(MOVA_zt, aa64_sme, do_mova_tile, a, true) +static bool do_mova_tile_n(DisasContext *s, arg_mova_t *a, int n, bool to_vec) +{ + static gen_helper_gvec_2 * const cz_fns[] = { + gen_helper_sme2_mova_cz_b, gen_helper_sme2_mova_cz_h, + gen_helper_sme2_mova_cz_s, gen_helper_sme2_mova_cz_d, + }; + static gen_helper_gvec_2 * const zc_fns[] = { + gen_helper_sme2_mova_zc_b, gen_helper_sme2_mova_zc_h, + gen_helper_sme2_mova_zc_s, gen_helper_sme2_mova_zc_d, + }; + TCGv_ptr t_za; + int svl, bytes_per_op = n << a->esz; + + /* + * The MaxImplementedSVL check happens in the decode pseudocode, + * before the SM+ZA enabled check in the operation pseudocode. + * This will (currently) only fail for NREG=4, ESZ=MO_64. + */ + if (s->max_svl < bytes_per_op) { + unallocated_encoding(s); + return true; + } + + if (!sme_smza_enabled_check(s)) { + return true; + } + + svl = streaming_vec_reg_size(s); + + /* + * The CurrentVL check happens in the operation pseudocode, + * after the SM+ZA enabled check. + */ + if (svl < bytes_per_op) { + unallocated_encoding(s); + return true; + } + + if (a->v) { + TCGv_i32 t_desc = tcg_constant_i32(simd_desc(svl, svl, 0)); + + for (int i = 0; i < n; ++i) { + TCGv_ptr t_zr = vec_full_reg_ptr(s, a->zr * n + i); + t_za = get_tile_rowcol(s, a->esz, a->rs, a->za, + a->off * n + i, 1, n, a->v); + if (to_vec) { + zc_fns[a->esz](t_zr, t_za, t_desc); + } else { + cz_fns[a->esz](t_za, t_zr, t_desc); + } + } + } else { + for (int i = 0; i < n; ++i) { + int o_zr = vec_full_reg_offset(s, a->zr * n + i); + t_za = get_tile_rowcol(s, a->esz, a->rs, a->za, + a->off * n + i, 1, n, a->v); + if (to_vec) { + tcg_gen_gvec_mov_var(MO_8, tcg_env, o_zr, t_za, 0, svl, svl); + } else { + tcg_gen_gvec_mov_var(MO_8, t_za, 0, tcg_env, o_zr, svl, svl); + } + } + } + return true; +} + +TRANS_FEAT(MOVA_tz2, aa64_sme2, do_mova_tile_n, a, 2, false) +TRANS_FEAT(MOVA_tz4, aa64_sme2, do_mova_tile_n, a, 4, false) +TRANS_FEAT(MOVA_zt2, aa64_sme2, do_mova_tile_n, a, 2, true) +TRANS_FEAT(MOVA_zt4, aa64_sme2, do_mova_tile_n, a, 4, true) + static bool do_movt(DisasContext *s, arg_MOVT_rzt *a, void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) { diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index b03956a793..7336b7db72 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -73,6 +73,7 @@ typedef struct DisasContext { int zt0_excp_el; /* ZT0 exception EL or 0 if enabled */ int vl; /* current vector length in bytes */ int svl; /* current streaming vector length in bytes */ + int max_svl; /* maximum implemented streaming vector length */ bool vfp_enabled; /* FP enabled via FPSCR.EN */ int vec_len; int vec_stride; From e9de40e53821d4c2adbb115a412991b15cd59a76 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:49 -0600 Subject: [PATCH 1973/2760] target/arm: Implement SME2 MOVA to/from array, multiple registers Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-27-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 12 ++++++++++++ target/arm/tcg/translate-sme.c | 30 ++++++++++++++++++++++++++++++ target/arm/tcg/translate.h | 5 +++++ 3 files changed, 47 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 5eca5f4acf..37bd0c6198 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -27,6 +27,8 @@ ZERO_zt0 11000000 01 001 00000000000 00000001 ### SME Move into/from Array %mova_rs 13:2 !function=plus_12 +%mova_rv 13:2 !function=plus_8 +&mova_a rv zr off &mova_p esz rs pg zr za off v:bool &mova_t esz rs zr za off v:bool @@ -88,6 +90,16 @@ MOVA_zt4 11000000 10 00011 0 v:1 .. 001 000 za:2 zr:3 00 \ MOVA_zt4 11000000 11 00011 0 v:1 .. 001 00 za:3 zr:3 00 \ &mova_t rs=%mova_rs esz=3 off=0 +MOVA_az2 11000000 00 00010 00 .. 010 zr:4 000 off:3 \ + &mova_a rv=%mova_rv +MOVA_az4 11000000 00 00010 00 .. 011 zr:3 0000 off:3 \ + &mova_a rv=%mova_rv + +MOVA_za2 11000000 00 00011 00 .. 010 00 off:3 zr:4 0 \ + &mova_a rv=%mova_rv +MOVA_za4 11000000 00 00011 00 .. 011 00 off:3 zr:3 00 \ + &mova_a rv=%mova_rv + ### SME Move into/from ZT0 MOVT_rzt 1100 0000 0100 1100 0 off:3 00 11111 rt:5 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 7d4c7d7e85..7b275dd2b8 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -298,6 +298,36 @@ TRANS_FEAT(MOVA_tz4, aa64_sme2, do_mova_tile_n, a, 4, false) TRANS_FEAT(MOVA_zt2, aa64_sme2, do_mova_tile_n, a, 2, true) TRANS_FEAT(MOVA_zt4, aa64_sme2, do_mova_tile_n, a, 4, true) +static bool do_mova_array_n(DisasContext *s, arg_mova_a *a, int n, bool to_vec) +{ + TCGv_ptr t_za; + int svl; + + if (!sme_smza_enabled_check(s)) { + return true; + } + + svl = streaming_vec_reg_size(s); + t_za = get_zarray(s, a->rv, a->off, n, 0); + + for (int i = 0; i < n; ++i) { + int o_za = (svl / n * sizeof(ARMVectorReg)) * i; + int o_zr = vec_full_reg_offset(s, a->zr * n + i); + + if (to_vec) { + tcg_gen_gvec_mov_var(MO_8, tcg_env, o_zr, t_za, o_za, svl, svl); + } else { + tcg_gen_gvec_mov_var(MO_8, t_za, o_za, tcg_env, o_zr, svl, svl); + } + } + return true; +} + +TRANS_FEAT(MOVA_az2, aa64_sme2, do_mova_array_n, a, 2, false) +TRANS_FEAT(MOVA_az4, aa64_sme2, do_mova_array_n, a, 4, false) +TRANS_FEAT(MOVA_za2, aa64_sme2, do_mova_array_n, a, 2, true) +TRANS_FEAT(MOVA_za4, aa64_sme2, do_mova_array_n, a, 4, true) + static bool do_movt(DisasContext *s, arg_MOVT_rzt *a, void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) { diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index 7336b7db72..a18d07540e 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -210,6 +210,11 @@ static inline int plus_2(DisasContext *s, int x) return x + 2; } +static inline int plus_8(DisasContext *s, int x) +{ + return x + 8; +} + static inline int plus_12(DisasContext *s, int x) { return x + 12; From 24b5220f4c9d168b1acbbba71044d7848c90c8b5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:50 -0600 Subject: [PATCH 1974/2760] target/arm: Implement SME2 BMOPA Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-28-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 3 +++ target/arm/tcg/sme.decode | 2 ++ target/arm/tcg/sme_helper.c | 34 ++++++++++++++++++++++++---------- target/arm/tcg/translate-sme.c | 2 ++ 4 files changed, 31 insertions(+), 10 deletions(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 8246ce774c..17d1a7c102 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -153,3 +153,6 @@ DEF_HELPER_FLAGS_6(sme_sumopa_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_6(sme_usmopa_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_6(sme2_bmopa_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 37bd0c6198..de8d04cb87 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -165,3 +165,5 @@ SMOPA_d 1010000 0 11 0 ..... ... ... ..... . 0 ... @op_64 SUMOPA_d 1010000 0 11 1 ..... ... ... ..... . 0 ... @op_64 USMOPA_d 1010000 1 11 0 ..... ... ... ..... . 0 ... @op_64 UMOPA_d 1010000 1 11 1 ..... ... ... ..... . 0 ... @op_64 + +BMOPA 1000000 0 10 0 ..... ... ... ..... . 10 .. @op_32 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index baeaa3e069..6122a5b5d9 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1297,17 +1297,31 @@ DEF_IMOP_64(umopa_d, uint16_t, uint16_t) DEF_IMOP_64(sumopa_d, int16_t, uint16_t) DEF_IMOP_64(usmopa_d, uint16_t, int16_t) -#define DEF_IMOPH(NAME, S) \ - void HELPER(sme_##NAME##_##S)(void *vza, void *vzn, void *vzm, \ +#define DEF_IMOPH(P, NAME, S) \ + void HELPER(P##_##NAME##_##S)(void *vza, void *vzn, void *vzm, \ void *vpn, void *vpm, uint32_t desc) \ { do_imopa_##S(vza, vzn, vzm, vpn, vpm, desc, NAME##_##S); } -DEF_IMOPH(smopa, s) -DEF_IMOPH(umopa, s) -DEF_IMOPH(sumopa, s) -DEF_IMOPH(usmopa, s) +DEF_IMOPH(sme, smopa, s) +DEF_IMOPH(sme, umopa, s) +DEF_IMOPH(sme, sumopa, s) +DEF_IMOPH(sme, usmopa, s) -DEF_IMOPH(smopa, d) -DEF_IMOPH(umopa, d) -DEF_IMOPH(sumopa, d) -DEF_IMOPH(usmopa, d) +DEF_IMOPH(sme, smopa, d) +DEF_IMOPH(sme, umopa, d) +DEF_IMOPH(sme, sumopa, d) +DEF_IMOPH(sme, usmopa, d) + +static uint32_t bmopa_s(uint32_t n, uint32_t m, uint32_t a, uint8_t p, bool neg) +{ + uint32_t sum = ctpop32(~(n ^ m)); + if (neg) { + sum = -sum; + } + if (!(p & 1)) { + sum = 0; + } + return a + sum; +} + +DEF_IMOPH(sme2, bmopa, s) diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 7b275dd2b8..c2615f06ae 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -539,3 +539,5 @@ TRANS_FEAT(SMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_smopa_ TRANS_FEAT(UMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_umopa_d) TRANS_FEAT(SUMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_sumopa_d) TRANS_FEAT(USMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_usmopa_d) + +TRANS_FEAT(BMOPA, aa64_sme2, do_outprod, a, MO_32, gen_helper_sme2_bmopa_s) From e129ceae820a1b4dc1d10cc55ba98d421afe824a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:51 -0600 Subject: [PATCH 1975/2760] target/arm: Implement SME2 SMOPS, UMOPS (2-way) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-29-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 4 ++++ target/arm/tcg/sme.decode | 2 ++ target/arm/tcg/sme_helper.c | 37 +++++++++++++++++++++++++--------- target/arm/tcg/translate-sme.c | 2 ++ 4 files changed, 35 insertions(+), 10 deletions(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 17d1a7c102..ecd06f2cd1 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -156,3 +156,7 @@ DEF_HELPER_FLAGS_6(sme_usmopa_d, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sme2_bmopa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme2_smopa2_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_6(sme2_umopa2_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index de8d04cb87..36f369d02a 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -167,3 +167,5 @@ USMOPA_d 1010000 1 11 0 ..... ... ... ..... . 0 ... @op_64 UMOPA_d 1010000 1 11 1 ..... ... ... ..... . 0 ... @op_64 BMOPA 1000000 0 10 0 ..... ... ... ..... . 10 .. @op_32 +SMOPA2_s 1010000 0 10 0 ..... ... ... ..... . 10 .. @op_32 +UMOPA2_s 1010000 1 10 0 ..... ... ... ..... . 10 .. @op_32 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 6122a5b5d9..8ce50653b4 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1261,7 +1261,7 @@ static inline void do_imopa_d(uint64_t *za, uint64_t *zn, uint64_t *zm, } } -#define DEF_IMOP_32(NAME, NTYPE, MTYPE) \ +#define DEF_IMOP_8x4_32(NAME, NTYPE, MTYPE) \ static uint32_t NAME(uint32_t n, uint32_t m, uint32_t a, uint8_t p, bool neg) \ { \ uint32_t sum = 0; \ @@ -1274,7 +1274,7 @@ static uint32_t NAME(uint32_t n, uint32_t m, uint32_t a, uint8_t p, bool neg) \ return neg ? a - sum : a + sum; \ } -#define DEF_IMOP_64(NAME, NTYPE, MTYPE) \ +#define DEF_IMOP_16x4_64(NAME, NTYPE, MTYPE) \ static uint64_t NAME(uint64_t n, uint64_t m, uint64_t a, uint8_t p, bool neg) \ { \ uint64_t sum = 0; \ @@ -1287,15 +1287,15 @@ static uint64_t NAME(uint64_t n, uint64_t m, uint64_t a, uint8_t p, bool neg) \ return neg ? a - sum : a + sum; \ } -DEF_IMOP_32(smopa_s, int8_t, int8_t) -DEF_IMOP_32(umopa_s, uint8_t, uint8_t) -DEF_IMOP_32(sumopa_s, int8_t, uint8_t) -DEF_IMOP_32(usmopa_s, uint8_t, int8_t) +DEF_IMOP_8x4_32(smopa_s, int8_t, int8_t) +DEF_IMOP_8x4_32(umopa_s, uint8_t, uint8_t) +DEF_IMOP_8x4_32(sumopa_s, int8_t, uint8_t) +DEF_IMOP_8x4_32(usmopa_s, uint8_t, int8_t) -DEF_IMOP_64(smopa_d, int16_t, int16_t) -DEF_IMOP_64(umopa_d, uint16_t, uint16_t) -DEF_IMOP_64(sumopa_d, int16_t, uint16_t) -DEF_IMOP_64(usmopa_d, uint16_t, int16_t) +DEF_IMOP_16x4_64(smopa_d, int16_t, int16_t) +DEF_IMOP_16x4_64(umopa_d, uint16_t, uint16_t) +DEF_IMOP_16x4_64(sumopa_d, int16_t, uint16_t) +DEF_IMOP_16x4_64(usmopa_d, uint16_t, int16_t) #define DEF_IMOPH(P, NAME, S) \ void HELPER(P##_##NAME##_##S)(void *vza, void *vzn, void *vzm, \ @@ -1325,3 +1325,20 @@ static uint32_t bmopa_s(uint32_t n, uint32_t m, uint32_t a, uint8_t p, bool neg) } DEF_IMOPH(sme2, bmopa, s) + +#define DEF_IMOP_16x2_32(NAME, NTYPE, MTYPE) \ +static uint32_t NAME(uint32_t n, uint32_t m, uint32_t a, uint8_t p, bool neg) \ +{ \ + uint32_t sum = 0; \ + /* Apply P to N as a mask, making the inactive elements 0. */ \ + n &= expand_pred_h(p); \ + sum += (NTYPE)(n >> 0) * (MTYPE)(m >> 0); \ + sum += (NTYPE)(n >> 16) * (MTYPE)(m >> 16); \ + return neg ? a - sum : a + sum; \ +} + +DEF_IMOP_16x2_32(smopa2_s, int16_t, int16_t) +DEF_IMOP_16x2_32(umopa2_s, uint16_t, uint16_t) + +DEF_IMOPH(sme2, smopa2, s) +DEF_IMOPH(sme2, umopa2, s) diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index c2615f06ae..0a863a50bf 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -541,3 +541,5 @@ TRANS_FEAT(SUMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_sumop TRANS_FEAT(USMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_usmopa_d) TRANS_FEAT(BMOPA, aa64_sme2, do_outprod, a, MO_32, gen_helper_sme2_bmopa_s) +TRANS_FEAT(SMOPA2_s, aa64_sme2, do_outprod, a, MO_32, gen_helper_sme2_smopa2_s) +TRANS_FEAT(UMOPA2_s, aa64_sme2, do_outprod, a, MO_32, gen_helper_sme2_umopa2_s) From baf450ef83b27394358e294d2bd7f64899e0ef45 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:52 -0600 Subject: [PATCH 1976/2760] target/arm: Introduce gen_gvec_sve2_sqdmulh To be used by both SVE2 and SME2. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-30-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/gengvec64.c | 11 +++++++++++ target/arm/tcg/translate-a64.h | 4 ++++ target/arm/tcg/translate-sve.c | 8 +------- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/target/arm/tcg/gengvec64.c b/target/arm/tcg/gengvec64.c index 2617cde0a5..2429cab1b8 100644 --- a/target/arm/tcg/gengvec64.c +++ b/target/arm/tcg/gengvec64.c @@ -369,3 +369,14 @@ void gen_gvec_usqadd_qc(unsigned vece, uint32_t rd_ofs, tcg_gen_gvec_4(rd_ofs, offsetof(CPUARMState, vfp.qc), rn_ofs, rm_ofs, opr_sz, max_sz, &ops[vece]); } + +void gen_gvec_sve2_sqdmulh(unsigned vece, uint32_t rd_ofs, + uint32_t rn_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[4] = { + gen_helper_sve2_sqdmulh_b, gen_helper_sve2_sqdmulh_h, + gen_helper_sve2_sqdmulh_s, gen_helper_sve2_sqdmulh_d, + }; + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h index 8a9f4b2053..993dde61a4 100644 --- a/target/arm/tcg/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -225,6 +225,10 @@ void gen_gvec_usqadd_qc(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz); +void gen_gvec_sve2_sqdmulh(unsigned vece, uint32_t rd_ofs, + uint32_t rn_ofs, uint32_t rm_ofs, + uint32_t opr_sz, uint32_t max_sz); + void gen_sve_ldr(DisasContext *s, TCGv_ptr, int vofs, int len, int rn, int imm, MemOp align); void gen_sve_str(DisasContext *s, TCGv_ptr, int vofs, diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index d2cd298b5b..7e304245c6 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -5948,6 +5948,7 @@ TRANS_FEAT(MOVPRFX_z, aa64_sve, do_movz_zpz, a->rd, a->rn, a->pg, a->esz, false) */ TRANS_FEAT(MUL_zzz, aa64_sve2, gen_gvec_fn_arg_zzz, tcg_gen_gvec_mul, a) +TRANS_FEAT(SQDMULH_zzz, aa64_sve2, gen_gvec_fn_arg_zzz, gen_gvec_sve2_sqdmulh, a) static gen_helper_gvec_3 * const smulh_zzz_fns[4] = { gen_helper_gvec_smulh_b, gen_helper_gvec_smulh_h, @@ -5966,13 +5967,6 @@ TRANS_FEAT(UMULH_zzz, aa64_sve2, gen_gvec_ool_arg_zzz, TRANS_FEAT(PMUL_zzz, aa64_sve2, gen_gvec_ool_arg_zzz, gen_helper_gvec_pmul_b, a, 0) -static gen_helper_gvec_3 * const sqdmulh_zzz_fns[4] = { - gen_helper_sve2_sqdmulh_b, gen_helper_sve2_sqdmulh_h, - gen_helper_sve2_sqdmulh_s, gen_helper_sve2_sqdmulh_d, -}; -TRANS_FEAT(SQDMULH_zzz, aa64_sve2, gen_gvec_ool_arg_zzz, - sqdmulh_zzz_fns[a->esz], a, 0) - static gen_helper_gvec_3 * const sqrdmulh_zzz_fns[4] = { gen_helper_sve2_sqrdmulh_b, gen_helper_sve2_sqrdmulh_h, gen_helper_sve2_sqrdmulh_s, gen_helper_sve2_sqrdmulh_d, From bc65d2bd1cbffff6e0616a1417acf35ad2e10f29 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:53 -0600 Subject: [PATCH 1977/2760] target/arm: Implement SME2 Multiple and Single SVE Destructive Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-31-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-a64.c | 2 + target/arm/tcg/helper-sme.h | 13 ++++ target/arm/tcg/helper.h | 8 ++ target/arm/tcg/neon_helper.c | 30 ++++++++ target/arm/tcg/sme.decode | 40 ++++++++++ target/arm/tcg/translate-sme.c | 137 +++++++++++++++++++++++++++++++++ target/arm/tcg/vec_helper.c | 7 ++ target/arm/tcg/vec_internal.h | 4 + 8 files changed, 241 insertions(+) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index 4f618ae390..c66d521278 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -402,6 +402,8 @@ AH_MINMAX_HELPER(vfp_ah_mind, float64, float64, min) AH_MINMAX_HELPER(vfp_ah_maxh, dh_ctype_f16, float16, max) AH_MINMAX_HELPER(vfp_ah_maxs, float32, float32, max) AH_MINMAX_HELPER(vfp_ah_maxd, float64, float64, max) +AH_MINMAX_HELPER(sme2_ah_fmax_b16, bfloat16, bfloat16, max) +AH_MINMAX_HELPER(sme2_ah_fmin_b16, bfloat16, bfloat16, min) /* 64-bit versions of the CRC helpers. Note that although the operation * (and the prototypes of crc32c() and crc32() mean that only the bottom diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index ecd06f2cd1..cdd7058aed 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -160,3 +160,16 @@ DEF_HELPER_FLAGS_6(sme2_smopa2_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_6(sme2_umopa2_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(gvec_fmax_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmin_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmax_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_fmin_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmaxnum_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fminnum_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index 80db7c2c37..8c4705fc53 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -353,6 +353,14 @@ DEF_HELPER_FLAGS_4(gvec_urshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_urshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(gvec_urshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_srshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_srshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_srshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sme2_urshl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_urshl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_urshl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_2(neon_add_u8, i32, i32, i32) DEF_HELPER_2(neon_add_u16, i32, i32, i32) DEF_HELPER_2(neon_sub_u8, i32, i32, i32) diff --git a/target/arm/tcg/neon_helper.c b/target/arm/tcg/neon_helper.c index 2cc8241f1e..8d288f3a70 100644 --- a/target/arm/tcg/neon_helper.c +++ b/target/arm/tcg/neon_helper.c @@ -228,16 +228,31 @@ NEON_VOP(rshl_s16, neon_s16, 2) NEON_GVEC_VOP2(gvec_srshl_h, int16_t) #undef NEON_FN +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, src2, 16, true, NULL)) +NEON_GVEC_VOP2(sme2_srshl_h, int16_t) +#undef NEON_FN + #define NEON_FN(dest, src1, src2) \ (dest = do_sqrshl_bhs(src1, (int8_t)src2, 32, true, NULL)) NEON_GVEC_VOP2(gvec_srshl_s, int32_t) #undef NEON_FN +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_bhs(src1, src2, 32, true, NULL)) +NEON_GVEC_VOP2(sme2_srshl_s, int32_t) +#undef NEON_FN + #define NEON_FN(dest, src1, src2) \ (dest = do_sqrshl_d(src1, (int8_t)src2, true, NULL)) NEON_GVEC_VOP2(gvec_srshl_d, int64_t) #undef NEON_FN +#define NEON_FN(dest, src1, src2) \ + (dest = do_sqrshl_d(src1, src2, true, NULL)) +NEON_GVEC_VOP2(sme2_srshl_d, int64_t) +#undef NEON_FN + uint32_t HELPER(neon_rshl_s32)(uint32_t val, uint32_t shift) { return do_sqrshl_bhs(val, (int8_t)shift, 32, true, NULL); @@ -260,16 +275,31 @@ NEON_VOP(rshl_u16, neon_u16, 2) NEON_GVEC_VOP2(gvec_urshl_h, uint16_t) #undef NEON_FN +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, (int16_t)src2, 16, true, NULL)) +NEON_GVEC_VOP2(sme2_urshl_h, uint16_t) +#undef NEON_FN + #define NEON_FN(dest, src1, src2) \ (dest = do_uqrshl_bhs(src1, (int8_t)src2, 32, true, NULL)) NEON_GVEC_VOP2(gvec_urshl_s, int32_t) #undef NEON_FN +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_bhs(src1, src2, 32, true, NULL)) +NEON_GVEC_VOP2(sme2_urshl_s, int32_t) +#undef NEON_FN + #define NEON_FN(dest, src1, src2) \ (dest = do_uqrshl_d(src1, (int8_t)src2, true, NULL)) NEON_GVEC_VOP2(gvec_urshl_d, int64_t) #undef NEON_FN +#define NEON_FN(dest, src1, src2) \ + (dest = do_uqrshl_d(src1, src2, true, NULL)) +NEON_GVEC_VOP2(sme2_urshl_d, int64_t) +#undef NEON_FN + uint32_t HELPER(neon_rshl_u32)(uint32_t val, uint32_t shift) { return do_uqrshl_bhs(val, (int8_t)shift, 32, true, NULL); diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 36f369d02a..005f87777b 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -169,3 +169,43 @@ UMOPA_d 1010000 1 11 1 ..... ... ... ..... . 0 ... @op_64 BMOPA 1000000 0 10 0 ..... ... ... ..... . 10 .. @op_32 SMOPA2_s 1010000 0 10 0 ..... ... ... ..... . 10 .. @op_32 UMOPA2_s 1010000 1 10 0 ..... ... ... ..... . 10 .. @op_32 + +### SME2 Multi-vector Multiple and Single SVE Destructive + +%zd_ax2 1:4 !function=times_2 +%zd_ax4 2:3 !function=times_4 + +&z2z_en zdn zm esz n +@z2z_2x1 ....... . esz:2 .. zm:4 ....0. ..... .... . \ + &z2z_en n=2 zdn=%zd_ax2 +@z2z_4x1 ....... . esz:2 .. zm:4 ....1. ..... ...0 . \ + &z2z_en n=4 zdn=%zd_ax4 + +SMAX_n1 1100000 1 .. 10 .... 1010.0 00000 .... 0 @z2z_2x1 +SMAX_n1 1100000 1 .. 10 .... 1010.0 00000 .... 0 @z2z_4x1 +UMAX_n1 1100000 1 .. 10 .... 1010.0 00000 .... 1 @z2z_2x1 +UMAX_n1 1100000 1 .. 10 .... 1010.0 00000 .... 1 @z2z_4x1 +SMIN_n1 1100000 1 .. 10 .... 1010.0 00001 .... 0 @z2z_2x1 +SMIN_n1 1100000 1 .. 10 .... 1010.0 00001 .... 0 @z2z_4x1 +UMIN_n1 1100000 1 .. 10 .... 1010.0 00001 .... 1 @z2z_2x1 +UMIN_n1 1100000 1 .. 10 .... 1010.0 00001 .... 1 @z2z_4x1 + +FMAX_n1 1100000 1 .. 10 .... 1010.0 01000 .... 0 @z2z_2x1 +FMAX_n1 1100000 1 .. 10 .... 1010.0 01000 .... 0 @z2z_4x1 +FMIN_n1 1100000 1 .. 10 .... 1010.0 01000 .... 1 @z2z_2x1 +FMIN_n1 1100000 1 .. 10 .... 1010.0 01000 .... 1 @z2z_4x1 +FMAXNM_n1 1100000 1 .. 10 .... 1010.0 01001 .... 0 @z2z_2x1 +FMAXNM_n1 1100000 1 .. 10 .... 1010.0 01001 .... 0 @z2z_4x1 +FMINNM_n1 1100000 1 .. 10 .... 1010.0 01001 .... 1 @z2z_2x1 +FMINNM_n1 1100000 1 .. 10 .... 1010.0 01001 .... 1 @z2z_4x1 + +SRSHL_n1 1100000 1 .. 10 .... 1010.0 10001 .... 0 @z2z_2x1 +SRSHL_n1 1100000 1 .. 10 .... 1010.0 10001 .... 0 @z2z_4x1 +URSHL_n1 1100000 1 .. 10 .... 1010.0 10001 .... 1 @z2z_2x1 +URSHL_n1 1100000 1 .. 10 .... 1010.0 10001 .... 1 @z2z_4x1 + +ADD_n1 1100000 1 .. 10 .... 1010.0 11000 .... 0 @z2z_2x1 +ADD_n1 1100000 1 .. 10 .... 1010.0 11000 .... 0 @z2z_4x1 + +SQDMULH_n1 1100000 1 .. 10 .... 1010.1 00000 .... 0 @z2z_2x1 +SQDMULH_n1 1100000 1 .. 10 .... 1010.1 00000 .... 0 @z2z_4x1 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 0a863a50bf..954da18ff0 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -543,3 +543,140 @@ TRANS_FEAT(USMOPA_d, aa64_sme_i16i64, do_outprod, a, MO_64, gen_helper_sme_usmop TRANS_FEAT(BMOPA, aa64_sme2, do_outprod, a, MO_32, gen_helper_sme2_bmopa_s) TRANS_FEAT(SMOPA2_s, aa64_sme2, do_outprod, a, MO_32, gen_helper_sme2_smopa2_s) TRANS_FEAT(UMOPA2_s, aa64_sme2, do_outprod, a, MO_32, gen_helper_sme2_umopa2_s) + +static bool do_z2z_n1(DisasContext *s, arg_z2z_en *a, GVecGen3Fn *fn) +{ + int esz, dn, vsz, mofs, n; + bool overlap = false; + + if (!sme_sm_enabled_check(s)) { + return true; + } + + esz = a->esz; + n = a->n; + dn = a->zdn; + mofs = vec_full_reg_offset(s, a->zm); + vsz = streaming_vec_reg_size(s); + + for (int i = 0; i < n; i++) { + int dofs = vec_full_reg_offset(s, dn + i); + if (dofs == mofs) { + overlap = true; + } else { + fn(esz, dofs, dofs, mofs, vsz, vsz); + } + } + if (overlap) { + fn(esz, mofs, mofs, mofs, vsz, vsz); + } + return true; +} + +static void gen_sme2_srshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[] = { + gen_helper_gvec_srshl_b, gen_helper_sme2_srshl_h, + gen_helper_sme2_srshl_s, gen_helper_sme2_srshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +static void gen_sme2_urshl(unsigned vece, uint32_t rd_ofs, uint32_t rn_ofs, + uint32_t rm_ofs, uint32_t opr_sz, uint32_t max_sz) +{ + static gen_helper_gvec_3 * const fns[] = { + gen_helper_gvec_urshl_b, gen_helper_sme2_urshl_h, + gen_helper_sme2_urshl_s, gen_helper_sme2_urshl_d, + }; + tcg_debug_assert(vece <= MO_64); + tcg_gen_gvec_3_ool(rd_ofs, rn_ofs, rm_ofs, opr_sz, max_sz, 0, fns[vece]); +} + +TRANS_FEAT(ADD_n1, aa64_sme2, do_z2z_n1, a, tcg_gen_gvec_add) +TRANS_FEAT(SMAX_n1, aa64_sme2, do_z2z_n1, a, tcg_gen_gvec_smax) +TRANS_FEAT(SMIN_n1, aa64_sme2, do_z2z_n1, a, tcg_gen_gvec_smin) +TRANS_FEAT(UMAX_n1, aa64_sme2, do_z2z_n1, a, tcg_gen_gvec_umax) +TRANS_FEAT(UMIN_n1, aa64_sme2, do_z2z_n1, a, tcg_gen_gvec_umin) +TRANS_FEAT(SRSHL_n1, aa64_sme2, do_z2z_n1, a, gen_sme2_srshl) +TRANS_FEAT(URSHL_n1, aa64_sme2, do_z2z_n1, a, gen_sme2_urshl) +TRANS_FEAT(SQDMULH_n1, aa64_sme2, do_z2z_n1, a, gen_gvec_sve2_sqdmulh) + +static bool do_z2z_n1_fpst(DisasContext *s, arg_z2z_en *a, + gen_helper_gvec_3_ptr * const fns[4]) +{ + int esz = a->esz, n, dn, vsz, mofs; + bool overlap = false; + gen_helper_gvec_3_ptr *fn; + TCGv_ptr fpst; + + /* These insns use MO_8 to encode BFloat16. */ + if (esz == MO_8 && !dc_isar_feature(aa64_sme_b16b16, s)) { + return false; + } + if (!sme_sm_enabled_check(s)) { + return true; + } + + fpst = fpstatus_ptr(esz == MO_16 ? FPST_A64_F16 : FPST_A64); + fn = fns[esz]; + n = a->n; + dn = a->zdn; + mofs = vec_full_reg_offset(s, a->zm); + vsz = streaming_vec_reg_size(s); + + for (int i = 0; i < n; i++) { + int dofs = vec_full_reg_offset(s, dn + i); + if (dofs == mofs) { + overlap = true; + } else { + tcg_gen_gvec_3_ptr(dofs, dofs, mofs, fpst, vsz, vsz, 0, fn); + } + } + if (overlap) { + tcg_gen_gvec_3_ptr(mofs, mofs, mofs, fpst, vsz, vsz, 0, fn); + } + return true; +} + +static gen_helper_gvec_3_ptr * const f_vector_fmax[2][4] = { + { gen_helper_gvec_fmax_b16, + gen_helper_gvec_fmax_h, + gen_helper_gvec_fmax_s, + gen_helper_gvec_fmax_d }, + { gen_helper_gvec_ah_fmax_b16, + gen_helper_gvec_ah_fmax_h, + gen_helper_gvec_ah_fmax_s, + gen_helper_gvec_ah_fmax_d }, +}; +TRANS_FEAT(FMAX_n1, aa64_sme2, do_z2z_n1_fpst, a, f_vector_fmax[s->fpcr_ah]) + +static gen_helper_gvec_3_ptr * const f_vector_fmin[2][4] = { + { gen_helper_gvec_fmin_b16, + gen_helper_gvec_fmin_h, + gen_helper_gvec_fmin_s, + gen_helper_gvec_fmin_d }, + { gen_helper_gvec_ah_fmin_b16, + gen_helper_gvec_ah_fmin_h, + gen_helper_gvec_ah_fmin_s, + gen_helper_gvec_ah_fmin_d }, +}; +TRANS_FEAT(FMIN_n1, aa64_sme2, do_z2z_n1_fpst, a, f_vector_fmin[s->fpcr_ah]) + +static gen_helper_gvec_3_ptr * const f_vector_fmaxnm[4] = { + gen_helper_gvec_fmaxnum_b16, + gen_helper_gvec_fmaxnum_h, + gen_helper_gvec_fmaxnum_s, + gen_helper_gvec_fmaxnum_d, +}; +TRANS_FEAT(FMAXNM_n1, aa64_sme2, do_z2z_n1_fpst, a, f_vector_fmaxnm) + +static gen_helper_gvec_3_ptr * const f_vector_fminnm[4] = { + gen_helper_gvec_fminnum_b16, + gen_helper_gvec_fminnum_h, + gen_helper_gvec_fminnum_s, + gen_helper_gvec_fminnum_d, +}; +TRANS_FEAT(FMINNM_n1, aa64_sme2, do_z2z_n1_fpst, a, f_vector_fminnm) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 3b7f308803..8b49577e2f 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1515,6 +1515,13 @@ DO_3OP(gvec_ah_fmin_h, helper_vfp_ah_minh, float16) DO_3OP(gvec_ah_fmin_s, helper_vfp_ah_mins, float32) DO_3OP(gvec_ah_fmin_d, helper_vfp_ah_mind, float64) +DO_3OP(gvec_fmax_b16, bfloat16_max, bfloat16) +DO_3OP(gvec_fmin_b16, bfloat16_min, bfloat16) +DO_3OP(gvec_fmaxnum_b16, bfloat16_maxnum, bfloat16) +DO_3OP(gvec_fminnum_b16, bfloat16_minnum, bfloat16) +DO_3OP(gvec_ah_fmax_b16, helper_sme2_ah_fmax_b16, bfloat16) +DO_3OP(gvec_ah_fmin_b16, helper_sme2_ah_fmin_b16, bfloat16) + #endif #undef DO_3OP diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index c02f9c37f8..64a7249d01 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -302,4 +302,8 @@ static inline float64 float64_maybe_ah_chs(float64 a, bool fpcr_ah) return fpcr_ah && float64_is_any_nan(a) ? a : float64_chs(a); } +/* Not actually called directly as a helper, but uses similar machinery. */ +bfloat16 helper_sme2_ah_fmax_b16(bfloat16 a, bfloat16 b, float_status *fpst); +bfloat16 helper_sme2_ah_fmin_b16(bfloat16 a, bfloat16 b, float_status *fpst); + #endif /* TARGET_ARM_VEC_INTERNAL_H */ From 930760eb753d3beead00920cbb0187f755c06dd9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:54 -0600 Subject: [PATCH 1978/2760] target/arm: Implement SME2 Multiple Vectors SVE Destructive Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-32-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 36 +++++++++++++++++++ target/arm/tcg/translate-sme.c | 65 ++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 005f87777b..470592f4c0 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -209,3 +209,39 @@ ADD_n1 1100000 1 .. 10 .... 1010.0 11000 .... 0 @z2z_4x1 SQDMULH_n1 1100000 1 .. 10 .... 1010.1 00000 .... 0 @z2z_2x1 SQDMULH_n1 1100000 1 .. 10 .... 1010.1 00000 .... 0 @z2z_4x1 + +### SME2 Multi-vector Multiple Vectors SVE Destructive + +%zm_ax2 17:4 !function=times_2 +%zm_ax4 18:3 !function=times_4 + +@z2z_2x2 ....... . esz:2 . ....0 ....0. ..... .... . \ + &z2z_en n=2 zdn=%zd_ax2 zm=%zm_ax2 +@z2z_4x4 ....... . esz:2 . ...00 ....1. ..... ...0 . \ + &z2z_en n=4 zdn=%zd_ax4 zm=%zm_ax4 + +SMAX_nn 1100000 1 .. 1 ..... 1011.0 00000 .... 0 @z2z_2x2 +SMAX_nn 1100000 1 .. 1 ..... 1011.0 00000 .... 0 @z2z_4x4 +UMAX_nn 1100000 1 .. 1 ..... 1011.0 00000 .... 1 @z2z_2x2 +UMAX_nn 1100000 1 .. 1 ..... 1011.0 00000 .... 1 @z2z_4x4 +SMIN_nn 1100000 1 .. 1 ..... 1011.0 00001 .... 0 @z2z_2x2 +SMIN_nn 1100000 1 .. 1 ..... 1011.0 00001 .... 0 @z2z_4x4 +UMIN_nn 1100000 1 .. 1 ..... 1011.0 00001 .... 1 @z2z_2x2 +UMIN_nn 1100000 1 .. 1 ..... 1011.0 00001 .... 1 @z2z_4x4 + +FMAX_nn 1100000 1 .. 1 ..... 1011.0 01000 .... 0 @z2z_2x2 +FMAX_nn 1100000 1 .. 1 ..... 1011.0 01000 .... 0 @z2z_4x4 +FMIN_nn 1100000 1 .. 1 ..... 1011.0 01000 .... 1 @z2z_2x2 +FMIN_nn 1100000 1 .. 1 ..... 1011.0 01000 .... 1 @z2z_4x4 +FMAXNM_nn 1100000 1 .. 1 ..... 1011.0 01001 .... 0 @z2z_2x2 +FMAXNM_nn 1100000 1 .. 1 ..... 1011.0 01001 .... 0 @z2z_4x4 +FMINNM_nn 1100000 1 .. 1 ..... 1011.0 01001 .... 1 @z2z_2x2 +FMINNM_nn 1100000 1 .. 1 ..... 1011.0 01001 .... 1 @z2z_4x4 + +SRSHL_nn 1100000 1 .. 1 ..... 1011.0 10001 .... 0 @z2z_2x2 +SRSHL_nn 1100000 1 .. 1 ..... 1011.0 10001 .... 0 @z2z_4x4 +URSHL_nn 1100000 1 .. 1 ..... 1011.0 10001 .... 1 @z2z_2x2 +URSHL_nn 1100000 1 .. 1 ..... 1011.0 10001 .... 1 @z2z_4x4 + +SQDMULH_nn 1100000 1 .. 1 ..... 1011.1 00000 .... 0 @z2z_2x2 +SQDMULH_nn 1100000 1 .. 1 ..... 1011.1 00000 .... 0 @z2z_4x4 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 954da18ff0..0a6a10b63d 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -604,6 +604,37 @@ TRANS_FEAT(SRSHL_n1, aa64_sme2, do_z2z_n1, a, gen_sme2_srshl) TRANS_FEAT(URSHL_n1, aa64_sme2, do_z2z_n1, a, gen_sme2_urshl) TRANS_FEAT(SQDMULH_n1, aa64_sme2, do_z2z_n1, a, gen_gvec_sve2_sqdmulh) +static bool do_z2z_nn(DisasContext *s, arg_z2z_en *a, GVecGen3Fn *fn) +{ + int esz, dn, dm, vsz, n; + + if (!sme_sm_enabled_check(s)) { + return true; + } + + esz = a->esz; + n = a->n; + dn = a->zdn; + dm = a->zm; + vsz = streaming_vec_reg_size(s); + + for (int i = 0; i < n; i++) { + int dofs = vec_full_reg_offset(s, dn + i); + int mofs = vec_full_reg_offset(s, dm + i); + + fn(esz, dofs, dofs, mofs, vsz, vsz); + } + return true; +} + +TRANS_FEAT(SMAX_nn, aa64_sme2, do_z2z_nn, a, tcg_gen_gvec_smax) +TRANS_FEAT(SMIN_nn, aa64_sme2, do_z2z_nn, a, tcg_gen_gvec_smin) +TRANS_FEAT(UMAX_nn, aa64_sme2, do_z2z_nn, a, tcg_gen_gvec_umax) +TRANS_FEAT(UMIN_nn, aa64_sme2, do_z2z_nn, a, tcg_gen_gvec_umin) +TRANS_FEAT(SRSHL_nn, aa64_sme2, do_z2z_nn, a, gen_sme2_srshl) +TRANS_FEAT(URSHL_nn, aa64_sme2, do_z2z_nn, a, gen_sme2_urshl) +TRANS_FEAT(SQDMULH_nn, aa64_sme2, do_z2z_nn, a, gen_gvec_sve2_sqdmulh) + static bool do_z2z_n1_fpst(DisasContext *s, arg_z2z_en *a, gen_helper_gvec_3_ptr * const fns[4]) { @@ -641,6 +672,36 @@ static bool do_z2z_n1_fpst(DisasContext *s, arg_z2z_en *a, return true; } +static bool do_z2z_nn_fpst(DisasContext *s, arg_z2z_en *a, + gen_helper_gvec_3_ptr * const fns[4]) +{ + int esz = a->esz, n, dn, dm, vsz; + gen_helper_gvec_3_ptr *fn; + TCGv_ptr fpst; + + if (esz == MO_8 && !dc_isar_feature(aa64_sme_b16b16, s)) { + return false; + } + if (!sme_sm_enabled_check(s)) { + return true; + } + + fpst = fpstatus_ptr(esz == MO_16 ? FPST_A64_F16 : FPST_A64); + fn = fns[esz]; + n = a->n; + dn = a->zdn; + dm = a->zm; + vsz = streaming_vec_reg_size(s); + + for (int i = 0; i < n; i++) { + int dofs = vec_full_reg_offset(s, dn + i); + int mofs = vec_full_reg_offset(s, dm + i); + + tcg_gen_gvec_3_ptr(dofs, dofs, mofs, fpst, vsz, vsz, 0, fn); + } + return true; +} + static gen_helper_gvec_3_ptr * const f_vector_fmax[2][4] = { { gen_helper_gvec_fmax_b16, gen_helper_gvec_fmax_h, @@ -652,6 +713,7 @@ static gen_helper_gvec_3_ptr * const f_vector_fmax[2][4] = { gen_helper_gvec_ah_fmax_d }, }; TRANS_FEAT(FMAX_n1, aa64_sme2, do_z2z_n1_fpst, a, f_vector_fmax[s->fpcr_ah]) +TRANS_FEAT(FMAX_nn, aa64_sme2, do_z2z_nn_fpst, a, f_vector_fmax[s->fpcr_ah]) static gen_helper_gvec_3_ptr * const f_vector_fmin[2][4] = { { gen_helper_gvec_fmin_b16, @@ -664,6 +726,7 @@ static gen_helper_gvec_3_ptr * const f_vector_fmin[2][4] = { gen_helper_gvec_ah_fmin_d }, }; TRANS_FEAT(FMIN_n1, aa64_sme2, do_z2z_n1_fpst, a, f_vector_fmin[s->fpcr_ah]) +TRANS_FEAT(FMIN_nn, aa64_sme2, do_z2z_nn_fpst, a, f_vector_fmin[s->fpcr_ah]) static gen_helper_gvec_3_ptr * const f_vector_fmaxnm[4] = { gen_helper_gvec_fmaxnum_b16, @@ -672,6 +735,7 @@ static gen_helper_gvec_3_ptr * const f_vector_fmaxnm[4] = { gen_helper_gvec_fmaxnum_d, }; TRANS_FEAT(FMAXNM_n1, aa64_sme2, do_z2z_n1_fpst, a, f_vector_fmaxnm) +TRANS_FEAT(FMAXNM_nn, aa64_sme2, do_z2z_nn_fpst, a, f_vector_fmaxnm) static gen_helper_gvec_3_ptr * const f_vector_fminnm[4] = { gen_helper_gvec_fminnum_b16, @@ -680,3 +744,4 @@ static gen_helper_gvec_3_ptr * const f_vector_fminnm[4] = { gen_helper_gvec_fminnum_d, }; TRANS_FEAT(FMINNM_n1, aa64_sme2, do_z2z_n1_fpst, a, f_vector_fminnm) +TRANS_FEAT(FMINNM_nn, aa64_sme2, do_z2z_nn_fpst, a, f_vector_fminnm) From d636130fbba61956d49a139c5690c3dc569dfb9a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:55 -0600 Subject: [PATCH 1979/2760] target/arm: Implement SME2 ADD/SUB (array results, multiple and single vector) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-33-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 15 +++++++++++++++ target/arm/tcg/translate-sme.c | 30 ++++++++++++++++++++++++++++++ target/arm/tcg/translate.h | 2 ++ 3 files changed, 47 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 470592f4c0..8b81c0a0ce 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -245,3 +245,18 @@ URSHL_nn 1100000 1 .. 1 ..... 1011.0 10001 .... 1 @z2z_4x4 SQDMULH_nn 1100000 1 .. 1 ..... 1011.1 00000 .... 0 @z2z_2x2 SQDMULH_nn 1100000 1 .. 1 ..... 1011.1 00000 .... 0 @z2z_4x4 + +### SME2 Multi-vector Multiple and Single Array Vectors + +&azz_n n off rv zn zm +@azz_nx1_o3 ........ .... zm:4 ...... zn:5 .. off:3 &azz_n rv=%mova_rv + +ADD_azz_n1_s 11000001 0010 .... 0 .. 110 ..... 10 ... @azz_nx1_o3 n=2 +ADD_azz_n1_s 11000001 0011 .... 0 .. 110 ..... 10 ... @azz_nx1_o3 n=4 +ADD_azz_n1_d 11000001 0110 .... 0 .. 110 ..... 10 ... @azz_nx1_o3 n=2 +ADD_azz_n1_d 11000001 0111 .... 0 .. 110 ..... 10 ... @azz_nx1_o3 n=4 + +SUB_azz_n1_s 11000001 0010 .... 0 .. 110 ..... 11 ... @azz_nx1_o3 n=2 +SUB_azz_n1_s 11000001 0011 .... 0 .. 110 ..... 11 ... @azz_nx1_o3 n=4 +SUB_azz_n1_d 11000001 0110 .... 0 .. 110 ..... 11 ... @azz_nx1_o3 n=2 +SUB_azz_n1_d 11000001 0111 .... 0 .. 110 ..... 11 ... @azz_nx1_o3 n=4 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 0a6a10b63d..4bb880d9ef 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -745,3 +745,33 @@ static gen_helper_gvec_3_ptr * const f_vector_fminnm[4] = { }; TRANS_FEAT(FMINNM_n1, aa64_sme2, do_z2z_n1_fpst, a, f_vector_fminnm) TRANS_FEAT(FMINNM_nn, aa64_sme2, do_z2z_nn_fpst, a, f_vector_fminnm) + +/* Add/Sub vector Z[m] to each Z[n*N] with result in ZA[d*N]. */ +static bool do_azz_n1(DisasContext *s, arg_azz_n *a, int esz, + GVecGen3FnVar *fn) +{ + TCGv_ptr t_za; + int svl, n, o_zm; + + if (!sme_smza_enabled_check(s)) { + return true; + } + + n = a->n; + t_za = get_zarray(s, a->rv, a->off, n, 0); + o_zm = vec_full_reg_offset(s, a->zm); + svl = streaming_vec_reg_size(s); + + for (int i = 0; i < n; ++i) { + int o_za = (svl / n * sizeof(ARMVectorReg)) * i; + int o_zn = vec_full_reg_offset(s, (a->zn + i) % 32); + + fn(esz, t_za, o_za, tcg_env, o_zn, tcg_env, o_zm, svl, svl); + } + return true; +} + +TRANS_FEAT(ADD_azz_n1_s, aa64_sme2, do_azz_n1, a, MO_32, tcg_gen_gvec_add_var) +TRANS_FEAT(SUB_azz_n1_s, aa64_sme2, do_azz_n1, a, MO_32, tcg_gen_gvec_sub_var) +TRANS_FEAT(ADD_azz_n1_d, aa64_sme2_i16i64, do_azz_n1, a, MO_64, tcg_gen_gvec_add_var) +TRANS_FEAT(SUB_azz_n1_d, aa64_sme2_i16i64, do_azz_n1, a, MO_64, tcg_gen_gvec_sub_var) diff --git a/target/arm/tcg/translate.h b/target/arm/tcg/translate.h index a18d07540e..f974996f3f 100644 --- a/target/arm/tcg/translate.h +++ b/target/arm/tcg/translate.h @@ -643,6 +643,8 @@ typedef void GVecGen3Fn(unsigned, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); typedef void GVecGen4Fn(unsigned, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t); +typedef void GVecGen3FnVar(unsigned, TCGv_ptr, uint32_t, TCGv_ptr, uint32_t, + TCGv_ptr, uint32_t, uint32_t, uint32_t); /* Function prototype for gen_ functions for calling Neon helpers */ typedef void NeonGenOneOpFn(TCGv_i32, TCGv_i32); From 9fbc9bd4861971b717181872ec9c578b42f5bcf8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:56 -0600 Subject: [PATCH 1980/2760] target/arm: Implement SME2 ADD/SUB (array results, multiple vectors) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-34-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 20 ++++++++++++++++++++ target/arm/tcg/translate-sme.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 8b81c0a0ce..a6dee08661 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -260,3 +260,23 @@ SUB_azz_n1_s 11000001 0010 .... 0 .. 110 ..... 11 ... @azz_nx1_o3 n=2 SUB_azz_n1_s 11000001 0011 .... 0 .. 110 ..... 11 ... @azz_nx1_o3 n=4 SUB_azz_n1_d 11000001 0110 .... 0 .. 110 ..... 11 ... @azz_nx1_o3 n=2 SUB_azz_n1_d 11000001 0111 .... 0 .. 110 ..... 11 ... @azz_nx1_o3 n=4 + +### SME2 Multi-vector Multiple Array Vectors + +%zn_ax2 6:4 !function=times_2 +%zn_ax4 7:3 !function=times_4 + +@azz_2x2_o3 ........ ... ..... . .. ... ..... .. off:3 \ + &azz_n n=2 rv=%mova_rv zn=%zn_ax2 zm=%zm_ax2 +@azz_4x4_o3 ........ ... ..... . .. ... ..... .. off:3 \ + &azz_n n=4 rv=%mova_rv zn=%zn_ax4 zm=%zm_ax4 + +ADD_azz_nn_s 11000001 101 ....0 0 .. 110 ....0 10 ... @azz_2x2_o3 +ADD_azz_nn_s 11000001 101 ...01 0 .. 110 ...00 10 ... @azz_4x4_o3 +ADD_azz_nn_d 11000001 111 ....0 0 .. 110 ....0 10 ... @azz_2x2_o3 +ADD_azz_nn_d 11000001 111 ...01 0 .. 110 ...00 10 ... @azz_4x4_o3 + +SUB_azz_nn_s 11000001 101 ....0 0 .. 110 ....0 11 ... @azz_2x2_o3 +SUB_azz_nn_s 11000001 101 ...01 0 .. 110 ...00 11 ... @azz_4x4_o3 +SUB_azz_nn_d 11000001 111 ....0 0 .. 110 ....0 11 ... @azz_2x2_o3 +SUB_azz_nn_d 11000001 111 ...01 0 .. 110 ...00 11 ... @azz_4x4_o3 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 4bb880d9ef..aabdb96675 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -775,3 +775,33 @@ TRANS_FEAT(ADD_azz_n1_s, aa64_sme2, do_azz_n1, a, MO_32, tcg_gen_gvec_add_var) TRANS_FEAT(SUB_azz_n1_s, aa64_sme2, do_azz_n1, a, MO_32, tcg_gen_gvec_sub_var) TRANS_FEAT(ADD_azz_n1_d, aa64_sme2_i16i64, do_azz_n1, a, MO_64, tcg_gen_gvec_add_var) TRANS_FEAT(SUB_azz_n1_d, aa64_sme2_i16i64, do_azz_n1, a, MO_64, tcg_gen_gvec_sub_var) + +/* Add/Sub each vector Z[m*N] to each Z[n*N] with result in ZA[d*N]. */ +static bool do_azz_nn(DisasContext *s, arg_azz_n *a, int esz, + GVecGen3FnVar *fn) +{ + TCGv_ptr t_za; + int svl, n; + + if (!sme_smza_enabled_check(s)) { + return true; + } + + n = a->n; + t_za = get_zarray(s, a->rv, a->off, n, 1); + svl = streaming_vec_reg_size(s); + + for (int i = 0; i < n; ++i) { + int o_za = (svl / n * sizeof(ARMVectorReg)) * i; + int o_zn = vec_full_reg_offset(s, a->zn + i); + int o_zm = vec_full_reg_offset(s, a->zm + i); + + fn(esz, t_za, o_za, tcg_env, o_zn, tcg_env, o_zm, svl, svl); + } + return true; +} + +TRANS_FEAT(ADD_azz_nn_s, aa64_sme2, do_azz_nn, a, MO_32, tcg_gen_gvec_add_var) +TRANS_FEAT(SUB_azz_nn_s, aa64_sme2, do_azz_nn, a, MO_32, tcg_gen_gvec_sub_var) +TRANS_FEAT(ADD_azz_nn_d, aa64_sme2_i16i64, do_azz_nn, a, MO_64, tcg_gen_gvec_add_var) +TRANS_FEAT(SUB_azz_nn_d, aa64_sme2_i16i64, do_azz_nn, a, MO_64, tcg_gen_gvec_sub_var) From a476ef9652793f1932605737adb8bce89ee069b2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:57 -0600 Subject: [PATCH 1981/2760] target/arm: Pass ZA to helper_sve2_fmlal_zz[zx]w_s Indicate whether to use FPST_FPCR or FPST_ZA via bit 2 of simd_data(desc). For SVE, this bit remains zero. For do_FMLAL_zzzw, this requires no change. For do_FMLAL_zzxw, move the index up one bit. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-35-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-sve.c | 2 +- target/arm/tcg/vec_helper.c | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 7e304245c6..b85bd885f9 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -7214,7 +7214,7 @@ static bool do_FMLAL_zzxw(DisasContext *s, arg_rrxr_esz *a, bool sub, bool sel) { return gen_gvec_ptr_zzzz(s, gen_helper_sve2_fmlal_zzxw_s, a->rd, a->rn, a->rm, a->ra, - (a->index << 2) | (sel << 1) | sub, tcg_env); + (a->index << 3) | (sel << 1) | sub, tcg_env); } TRANS_FEAT(FMLALB_zzxw, aa64_sve2, do_FMLAL_zzxw, a, false, false) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 8b49577e2f..e8467823f2 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -2191,7 +2191,8 @@ void HELPER(sve2_fmlal_zzzw_s)(void *vd, void *vn, void *vm, void *va, intptr_t i, oprsz = simd_oprsz(desc); bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); - float_status *status = &env->vfp.fp_status[FPST_A64]; + bool za = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + float_status *status = &env->vfp.fp_status[za ? FPST_ZA : FPST_A64]; bool fz16 = env->vfp.fpcr & FPCR_FZ16; int negx = 0, negf = 0; @@ -2274,8 +2275,9 @@ void HELPER(sve2_fmlal_zzxw_s)(void *vd, void *vn, void *vm, void *va, intptr_t i, j, oprsz = simd_oprsz(desc); bool is_s = extract32(desc, SIMD_DATA_SHIFT, 1); intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 1, 1) * sizeof(float16); - intptr_t idx = extract32(desc, SIMD_DATA_SHIFT + 2, 3) * sizeof(float16); - float_status *status = &env->vfp.fp_status[FPST_A64]; + bool za = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + intptr_t idx = extract32(desc, SIMD_DATA_SHIFT + 3, 3) * sizeof(float16); + float_status *status = &env->vfp.fp_status[za ? FPST_ZA : FPST_A64]; bool fz16 = env->vfp.fpcr & FPCR_FZ16; int negx = 0, negf = 0; From 04afb926711924b41a84058851f285460ccb16c8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:58 -0600 Subject: [PATCH 1982/2760] target/arm: Add helper_gvec{_ah}_bfmlsl{_nx} Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-36-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper.h | 8 +++++ target/arm/tcg/vec_helper.c | 58 ++++++++++++++++++++++++++++--------- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index 8c4705fc53..a05b63380c 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -1095,8 +1095,16 @@ DEF_HELPER_FLAGS_6(gvec_bfmmla, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(gvec_bfmlal, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_bfmlsl, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_ah_bfmlsl, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_bfmlal_idx, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_bfmlsl_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_ah_bfmlsl_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_sclamp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index e8467823f2..5dcd6f9282 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -3186,44 +3186,76 @@ void HELPER(gvec_bfmmla)(void *vd, void *vn, void *vm, void *va, clear_tail(d, opr_sz, simd_maxsz(desc)); } -void HELPER(gvec_bfmlal)(void *vd, void *vn, void *vm, void *va, - float_status *stat, uint32_t desc) +static void do_bfmlal(float32 *d, bfloat16 *n, bfloat16 *m, float32 *a, + float_status *stat, uint32_t desc, int negx, int negf) { intptr_t i, opr_sz = simd_oprsz(desc); - intptr_t sel = simd_data(desc); - float32 *d = vd, *a = va; - bfloat16 *n = vn, *m = vm; + intptr_t sel = extract32(desc, SIMD_DATA_SHIFT, 1); for (i = 0; i < opr_sz / 4; ++i) { - float32 nn = n[H2(i * 2 + sel)] << 16; + float32 nn = (negx ^ n[H2(i * 2 + sel)]) << 16; float32 mm = m[H2(i * 2 + sel)] << 16; - d[H4(i)] = float32_muladd(nn, mm, a[H4(i)], 0, stat); + d[H4(i)] = float32_muladd(nn, mm, a[H4(i)], negf, stat); } clear_tail(d, opr_sz, simd_maxsz(desc)); } -void HELPER(gvec_bfmlal_idx)(void *vd, void *vn, void *vm, - void *va, float_status *stat, uint32_t desc) +void HELPER(gvec_bfmlal)(void *vd, void *vn, void *vm, void *va, + float_status *stat, uint32_t desc) +{ + do_bfmlal(vd, vn, vm, va, stat, desc, 0, 0); +} + +void HELPER(gvec_bfmlsl)(void *vd, void *vn, void *vm, void *va, + float_status *stat, uint32_t desc) +{ + do_bfmlal(vd, vn, vm, va, stat, desc, 0x8000, 0); +} + +void HELPER(gvec_ah_bfmlsl)(void *vd, void *vn, void *vm, void *va, + float_status *stat, uint32_t desc) +{ + do_bfmlal(vd, vn, vm, va, stat, desc, 0, float_muladd_negate_product); +} + +static void do_bfmlal_idx(float32 *d, bfloat16 *n, bfloat16 *m, float32 *a, + float_status *stat, uint32_t desc, int negx, int negf) { intptr_t i, j, opr_sz = simd_oprsz(desc); intptr_t sel = extract32(desc, SIMD_DATA_SHIFT, 1); intptr_t index = extract32(desc, SIMD_DATA_SHIFT + 1, 3); intptr_t elements = opr_sz / 4; intptr_t eltspersegment = MIN(16 / 4, elements); - float32 *d = vd, *a = va; - bfloat16 *n = vn, *m = vm; for (i = 0; i < elements; i += eltspersegment) { float32 m_idx = m[H2(2 * i + index)] << 16; for (j = i; j < i + eltspersegment; j++) { - float32 n_j = n[H2(2 * j + sel)] << 16; - d[H4(j)] = float32_muladd(n_j, m_idx, a[H4(j)], 0, stat); + float32 n_j = (negx ^ n[H2(2 * j + sel)]) << 16; + d[H4(j)] = float32_muladd(n_j, m_idx, a[H4(j)], negf, stat); } } clear_tail(d, opr_sz, simd_maxsz(desc)); } +void HELPER(gvec_bfmlal_idx)(void *vd, void *vn, void *vm, void *va, + float_status *stat, uint32_t desc) +{ + do_bfmlal_idx(vd, vn, vm, va, stat, desc, 0, 0); +} + +void HELPER(gvec_bfmlsl_idx)(void *vd, void *vn, void *vm, void *va, + float_status *stat, uint32_t desc) +{ + do_bfmlal_idx(vd, vn, vm, va, stat, desc, 0x8000, 0); +} + +void HELPER(gvec_ah_bfmlsl_idx)(void *vd, void *vn, void *vm, void *va, + float_status *stat, uint32_t desc) +{ + do_bfmlal_idx(vd, vn, vm, va, stat, desc, 0, float_muladd_negate_product); +} + #define DO_CLAMP(NAME, TYPE) \ void HELPER(NAME)(void *d, void *n, void *m, void *a, uint32_t desc) \ { \ From 50548277bd1be465bb7e8e575d67d20836552e31 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:19:59 -0600 Subject: [PATCH 1983/2760] target/arm: Implement SME2 FMLAL, BFMLAL Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-37-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 71 ++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 98 ++++++++++++++++++++++++++++++++++ 2 files changed, 169 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index a6dee08661..9850c19d90 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -261,6 +261,30 @@ SUB_azz_n1_s 11000001 0011 .... 0 .. 110 ..... 11 ... @azz_nx1_o3 n=4 SUB_azz_n1_d 11000001 0110 .... 0 .. 110 ..... 11 ... @azz_nx1_o3 n=2 SUB_azz_n1_d 11000001 0111 .... 0 .. 110 ..... 11 ... @azz_nx1_o3 n=4 +%off3_x2 0:3 !function=times_2 +%off2_x2 0:2 !function=times_2 + +@azz_nx1_o3x2 ........ ... . zm:4 . .. ... zn:5 .. ... \ + &azz_n off=%off3_x2 rv=%mova_rv +@azz_nx1_o2x2 ........ ... . zm:4 . .. ... zn:5 ... .. \ + &azz_n off=%off2_x2 rv=%mova_rv + +FMLAL_n1 11000001 001 0 .... 0 .. 011 ..... 00 ... @azz_nx1_o3x2 n=1 +FMLAL_n1 11000001 001 0 .... 0 .. 010 ..... 000 .. @azz_nx1_o2x2 n=2 +FMLAL_n1 11000001 001 1 .... 0 .. 010 ..... 000 .. @azz_nx1_o2x2 n=4 + +FMLSL_n1 11000001 001 0 .... 0 .. 011 ..... 01 ... @azz_nx1_o3x2 n=1 +FMLSL_n1 11000001 001 0 .... 0 .. 010 ..... 010 .. @azz_nx1_o2x2 n=2 +FMLSL_n1 11000001 001 1 .... 0 .. 010 ..... 010 .. @azz_nx1_o2x2 n=4 + +BFMLAL_n1 11000001 001 0 .... 0 .. 011 ..... 10 ... @azz_nx1_o3x2 n=1 +BFMLAL_n1 11000001 001 0 .... 0 .. 010 ..... 100 .. @azz_nx1_o2x2 n=2 +BFMLAL_n1 11000001 001 1 .... 0 .. 010 ..... 100 .. @azz_nx1_o2x2 n=4 + +BFMLSL_n1 11000001 001 0 .... 0 .. 011 ..... 11 ... @azz_nx1_o3x2 n=1 +BFMLSL_n1 11000001 001 0 .... 0 .. 010 ..... 110 .. @azz_nx1_o2x2 n=2 +BFMLSL_n1 11000001 001 1 .... 0 .. 010 ..... 110 .. @azz_nx1_o2x2 n=4 + ### SME2 Multi-vector Multiple Array Vectors %zn_ax2 6:4 !function=times_2 @@ -280,3 +304,50 @@ SUB_azz_nn_s 11000001 101 ....0 0 .. 110 ....0 11 ... @azz_2x2_o3 SUB_azz_nn_s 11000001 101 ...01 0 .. 110 ...00 11 ... @azz_4x4_o3 SUB_azz_nn_d 11000001 111 ....0 0 .. 110 ....0 11 ... @azz_2x2_o3 SUB_azz_nn_d 11000001 111 ...01 0 .. 110 ...00 11 ... @azz_4x4_o3 + +@azz_2x2_o2x2 ........ ... ..... . .. ... ..... ... .. \ + &azz_n n=2 rv=%mova_rv zn=%zn_ax2 zm=%zm_ax2 off=%off2_x2 +@azz_4x4_o2x2 ........ ... ..... . .. ... ..... ... .. \ + &azz_n n=4 rv=%mova_rv zn=%zn_ax4 zm=%zm_ax4 off=%off2_x2 + +FMLAL_nn 11000001 101 ....0 0 .. 010 ....0 000 .. @azz_2x2_o2x2 +FMLAL_nn 11000001 101 ...01 0 .. 010 ...00 000 .. @azz_4x4_o2x2 + +FMLSL_nn 11000001 101 ....0 0 .. 010 ....0 010 .. @azz_2x2_o2x2 +FMLSL_nn 11000001 101 ...01 0 .. 010 ...00 010 .. @azz_4x4_o2x2 + +BFMLAL_nn 11000001 101 ....0 0 .. 010 ....0 100 .. @azz_2x2_o2x2 +BFMLAL_nn 11000001 101 ...01 0 .. 010 ...00 100 .. @azz_4x4_o2x2 + +BFMLSL_nn 11000001 101 ....0 0 .. 010 ....0 110 .. @azz_2x2_o2x2 +BFMLSL_nn 11000001 101 ...01 0 .. 010 ...00 110 .. @azz_4x4_o2x2 + +### SME2 Multi-vector Indexed + +&azx_n n off rv zn zm idx + +%idx3_15_10 15:1 10:2 +%idx2_10_2 10:2 2:1 + +@azx_1x1_o3x2 ........ .... zm:4 . .. . .. zn:5 .. ... \ + &azx_n n=1 rv=%mova_rv off=%off3_x2 idx=%idx3_15_10 +@azx_2x1_o2x2 ........ .... zm:4 . .. . .. ..... .. ... \ + &azx_n n=2 rv=%mova_rv off=%off2_x2 zn=%zn_ax2 idx=%idx2_10_2 +@azx_4x1_o2x2 ........ .... zm:4 . .. . .. ..... .. ... \ + &azx_n n=4 rv=%mova_rv off=%off2_x2 zn=%zn_ax4 idx=%idx2_10_2 + +FMLAL_nx 11000001 1000 .... . .. 1 .. ..... 00 ... @azx_1x1_o3x2 +FMLAL_nx 11000001 1001 .... 0 .. 1 .. ....0 00 ... @azx_2x1_o2x2 +FMLAL_nx 11000001 1001 .... 1 .. 1 .. ...00 00 ... @azx_4x1_o2x2 + +FMLSL_nx 11000001 1000 .... . .. 1 .. ..... 01 ... @azx_1x1_o3x2 +FMLSL_nx 11000001 1001 .... 0 .. 1 .. ....0 01 ... @azx_2x1_o2x2 +FMLSL_nx 11000001 1001 .... 1 .. 1 .. ...00 01 ... @azx_4x1_o2x2 + +BFMLAL_nx 11000001 1000 .... . .. 1 .. ..... 10 ... @azx_1x1_o3x2 +BFMLAL_nx 11000001 1001 .... 0 .. 1 .. ....0 10 ... @azx_2x1_o2x2 +BFMLAL_nx 11000001 1001 .... 1 .. 1 .. ...00 10 ... @azx_4x1_o2x2 + +BFMLSL_nx 11000001 1000 .... . .. 1 .. ..... 11 ... @azx_1x1_o3x2 +BFMLSL_nx 11000001 1001 .... 0 .. 1 .. ....0 11 ... @azx_2x1_o2x2 +BFMLSL_nx 11000001 1001 .... 1 .. 1 .. ...00 11 ... @azx_4x1_o2x2 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index aabdb96675..9ec02d960d 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -805,3 +805,101 @@ TRANS_FEAT(ADD_azz_nn_s, aa64_sme2, do_azz_nn, a, MO_32, tcg_gen_gvec_add_var) TRANS_FEAT(SUB_azz_nn_s, aa64_sme2, do_azz_nn, a, MO_32, tcg_gen_gvec_sub_var) TRANS_FEAT(ADD_azz_nn_d, aa64_sme2_i16i64, do_azz_nn, a, MO_64, tcg_gen_gvec_add_var) TRANS_FEAT(SUB_azz_nn_d, aa64_sme2_i16i64, do_azz_nn, a, MO_64, tcg_gen_gvec_sub_var) + +/* + * Expand array multi-vector single (n1), array multi-vector (nn), + * and array multi-vector indexed (nx), for floating-point accumulate. + * multi: true for nn, false for n1. + * fpst: >= 0 to set ptr argument for FPST_*, < 0 for ENV. + * data: stuff for simd_data, including any index. + */ +#define FPST_ENV -1 + +static bool do_azz_acc_fp(DisasContext *s, int nreg, int nsel, + int rv, int off, int zn, int zm, + int data, int shsel, bool multi, int fpst, + gen_helper_gvec_4_ptr *fn) +{ + if (sme_smza_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + int vstride = svl / nreg; + TCGv_ptr t_za = get_zarray(s, rv, off, nreg, nsel); + TCGv_ptr t, ptr; + + if (fpst >= 0) { + ptr = fpstatus_ptr(fpst); + } else { + ptr = tcg_env; + } + t = tcg_temp_new_ptr(); + + for (int r = 0; r < nreg; ++r) { + TCGv_ptr t_zn = vec_full_reg_ptr(s, zn); + TCGv_ptr t_zm = vec_full_reg_ptr(s, zm); + + for (int i = 0; i < nsel; ++i) { + int o_za = (r * vstride + i) * sizeof(ARMVectorReg); + int desc = simd_desc(svl, svl, data | (i << shsel)); + + tcg_gen_addi_ptr(t, t_za, o_za); + fn(t, t_zn, t_zm, t, ptr, tcg_constant_i32(desc)); + } + + /* + * For multiple-and-single vectors, Zn may wrap. + * For multiple vectors, both Zn and Zm are aligned. + */ + zn = (zn + 1) % 32; + zm += multi; + } + } + return true; +} + +static bool do_fmlal(DisasContext *s, arg_azz_n *a, bool sub, bool multi) +{ + return do_azz_acc_fp(s, a->n, 2, a->rv, a->off, a->zn, a->zm, + (1 << 2) | sub, 1, + multi, FPST_ENV, gen_helper_sve2_fmlal_zzzw_s); +} + +TRANS_FEAT(FMLAL_n1, aa64_sme2, do_fmlal, a, false, false) +TRANS_FEAT(FMLSL_n1, aa64_sme2, do_fmlal, a, true, false) +TRANS_FEAT(FMLAL_nn, aa64_sme2, do_fmlal, a, false, true) +TRANS_FEAT(FMLSL_nn, aa64_sme2, do_fmlal, a, true, true) + +static bool do_fmlal_nx(DisasContext *s, arg_azx_n *a, bool sub) +{ + return do_azz_acc_fp(s, a->n, 2, a->rv, a->off, a->zn, a->zm, + (a->idx << 3) | (1 << 2) | sub, 1, + false, FPST_ENV, gen_helper_sve2_fmlal_zzxw_s); +} + +TRANS_FEAT(FMLAL_nx, aa64_sme2, do_fmlal_nx, a, false) +TRANS_FEAT(FMLSL_nx, aa64_sme2, do_fmlal_nx, a, true) + +static bool do_bfmlal(DisasContext *s, arg_azz_n *a, bool sub, bool multi) +{ + return do_azz_acc_fp(s, a->n, 2, a->rv, a->off, a->zn, a->zm, + 0, 0, multi, FPST_ZA, + (!sub ? gen_helper_gvec_bfmlal + : s->fpcr_ah ? gen_helper_gvec_ah_bfmlsl + : gen_helper_gvec_bfmlsl)); +} + +TRANS_FEAT(BFMLAL_n1, aa64_sme2, do_bfmlal, a, false, false) +TRANS_FEAT(BFMLSL_n1, aa64_sme2, do_bfmlal, a, true, false) +TRANS_FEAT(BFMLAL_nn, aa64_sme2, do_bfmlal, a, false, true) +TRANS_FEAT(BFMLSL_nn, aa64_sme2, do_bfmlal, a, true, true) + +static bool do_bfmlal_nx(DisasContext *s, arg_azx_n *a, bool sub) +{ + return do_azz_acc_fp(s, a->n, 2, a->rv, a->off, a->zn, a->zm, + a->idx << 1, 0, false, FPST_ZA, + !sub ? gen_helper_gvec_bfmlal_idx + : s->fpcr_ah ? gen_helper_gvec_ah_bfmlsl_idx + : gen_helper_gvec_bfmlsl_idx); +} + +TRANS_FEAT(BFMLAL_nx, aa64_sme2, do_bfmlal_nx, a, false) +TRANS_FEAT(BFMLSL_nx, aa64_sme2, do_bfmlal_nx, a, true) From 05cad66f103f4ac0d937d31ada5dcd5a550e6304 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:00 -0600 Subject: [PATCH 1984/2760] target/arm: Implement SME2 FDOT Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-38-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 5 ++++ target/arm/tcg/sme.decode | 14 +++++++++++ target/arm/tcg/sme_helper.c | 44 ++++++++++++++++++++++++++++++++++ target/arm/tcg/sve.decode | 7 ++++-- target/arm/tcg/translate-sme.c | 18 ++++++++++++++ target/arm/tcg/translate-sve.c | 5 ++++ 6 files changed, 91 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index cdd7058aed..ec93ff57ff 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -173,3 +173,8 @@ DEF_HELPER_FLAGS_5(gvec_fmaxnum_b16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fminnum_b16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_6(sme2_fdot_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(sme2_fdot_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 9850c19d90..a2b93519c4 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -285,6 +285,9 @@ BFMLSL_n1 11000001 001 0 .... 0 .. 011 ..... 11 ... @azz_nx1_o3x2 n=1 BFMLSL_n1 11000001 001 0 .... 0 .. 010 ..... 110 .. @azz_nx1_o2x2 n=2 BFMLSL_n1 11000001 001 1 .... 0 .. 010 ..... 110 .. @azz_nx1_o2x2 n=4 +FDOT_n1 11000001 001 0 .... 0 .. 100 ..... 00 ... @azz_nx1_o3 n=2 +FDOT_n1 11000001 001 1 .... 0 .. 100 ..... 00 ... @azz_nx1_o3 n=4 + ### SME2 Multi-vector Multiple Array Vectors %zn_ax2 6:4 !function=times_2 @@ -322,6 +325,9 @@ BFMLAL_nn 11000001 101 ...01 0 .. 010 ...00 100 .. @azz_4x4_o2x2 BFMLSL_nn 11000001 101 ....0 0 .. 010 ....0 110 .. @azz_2x2_o2x2 BFMLSL_nn 11000001 101 ...01 0 .. 010 ...00 110 .. @azz_4x4_o2x2 +FDOT_nn 11000001 101 ....0 0 .. 100 ....0 00 ... @azz_2x2_o3 +FDOT_nn 11000001 101 ...01 0 .. 100 ...00 00 ... @azz_4x4_o3 + ### SME2 Multi-vector Indexed &azx_n n off rv zn zm idx @@ -351,3 +357,11 @@ BFMLAL_nx 11000001 1001 .... 1 .. 1 .. ...00 10 ... @azx_4x1_o2x2 BFMLSL_nx 11000001 1000 .... . .. 1 .. ..... 11 ... @azx_1x1_o3x2 BFMLSL_nx 11000001 1001 .... 0 .. 1 .. ....0 11 ... @azx_2x1_o2x2 BFMLSL_nx 11000001 1001 .... 1 .. 1 .. ...00 11 ... @azx_4x1_o2x2 + +@azx_2x1_i2_o3 ........ .... zm:4 . .. . idx:2 .... ... off:3 \ + &azx_n n=2 rv=%mova_rv zn=%zn_ax2 +@azx_4x1_i2_o3 ........ .... zm:4 . .. . idx:2 .... ... off:3 \ + &azx_n n=2 rv=%mova_rv zn=%zn_ax4 + +FDOT_nx 11000001 0101 .... 0 .. 1 .. ....0 01 ... @azx_2x1_i2_o3 +FDOT_nx 11000001 0101 .... 1 .. 1 .. ...00 01 ... @azx_4x1_i2_o3 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 8ce50653b4..60322be3d0 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1152,6 +1152,50 @@ void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, } } +void HELPER(sme2_fdot_h)(void *vd, void *vn, void *vm, void *va, + CPUARMState *env, uint32_t desc) +{ + intptr_t i, oprsz = simd_maxsz(desc); + bool za = extract32(desc, SIMD_DATA_SHIFT, 1); + float_status *fpst_std = &env->vfp.fp_status[za ? FPST_ZA : FPST_A64]; + float_status *fpst_f16 = &env->vfp.fp_status[za ? FPST_ZA_F16 : FPST_A64_F16]; + float_status fpst_odd = *fpst_std; + float32 *d = vd, *a = va; + uint32_t *n = vn, *m = vm; + + set_float_rounding_mode(float_round_to_odd, &fpst_odd); + + for (i = 0; i < oprsz / sizeof(float32); ++i) { + d[H4(i)] = f16_dotadd(a[H4(i)], n[H4(i)], m[H4(i)], + fpst_f16, fpst_std, &fpst_odd); + } +} + +void HELPER(sme2_fdot_idx_h)(void *vd, void *vn, void *vm, void *va, + CPUARMState *env, uint32_t desc) +{ + intptr_t i, j, oprsz = simd_maxsz(desc); + intptr_t elements = oprsz / sizeof(float32); + intptr_t eltspersegment = MIN(4, elements); + int idx = extract32(desc, SIMD_DATA_SHIFT, 2); + bool za = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + float_status *fpst_std = &env->vfp.fp_status[za ? FPST_ZA : FPST_A64]; + float_status *fpst_f16 = &env->vfp.fp_status[za ? FPST_ZA_F16 : FPST_A64_F16]; + float_status fpst_odd = *fpst_std; + float32 *d = vd, *a = va; + uint32_t *n = vn, *m = (uint32_t *)vm + H4(idx); + + set_float_rounding_mode(float_round_to_odd, &fpst_odd); + + for (i = 0; i < elements; i += eltspersegment) { + uint32_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + d[H4(i + j)] = f16_dotadd(a[H4(i + j)], n[H4(i + j)], mm, + fpst_f16, fpst_std, &fpst_odd); + } + } +} + void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, void *vpn, void *vpm, CPUARMState *env, uint32_t desc) { diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 3a99eb7299..5970ed9ac4 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1662,7 +1662,8 @@ FMLSLT_zzzw 01100100 10 1 ..... 10 1 00 1 ..... ..... @rda_rn_rm_ex esz=2 BFMLALB_zzzw 01100100 11 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_ex esz=2 BFMLALT_zzzw 01100100 11 1 ..... 10 0 00 1 ..... ..... @rda_rn_rm_ex esz=2 -### SVE2 floating-point bfloat16 dot-product +### SVE2 floating-point dot-product +FDOT_zzzz 01100100 00 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_ex esz=2 BFDOT_zzzz 01100100 01 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_ex esz=2 ### SVE2 floating-point multiply-add long (indexed) @@ -1673,7 +1674,9 @@ FMLSLT_zzxw 01100100 10 1 ..... 0110.1 ..... ..... @rrxr_3a esz=2 BFMLALB_zzxw 01100100 11 1 ..... 0100.0 ..... ..... @rrxr_3a esz=2 BFMLALT_zzxw 01100100 11 1 ..... 0100.1 ..... ..... @rrxr_3a esz=2 -### SVE2 floating-point bfloat16 dot-product (indexed) +### SVE2 floating-point dot-product (indexed) + +FDOT_zzxz 01100100 00 1 ..... 010000 ..... ..... @rrxr_2 esz=2 BFDOT_zzxz 01100100 01 1 ..... 010000 ..... ..... @rrxr_2 esz=2 ### SVE broadcast predicate element diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 9ec02d960d..761584c90b 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -903,3 +903,21 @@ static bool do_bfmlal_nx(DisasContext *s, arg_azx_n *a, bool sub) TRANS_FEAT(BFMLAL_nx, aa64_sme2, do_bfmlal_nx, a, false) TRANS_FEAT(BFMLSL_nx, aa64_sme2, do_bfmlal_nx, a, true) + +static bool do_fdot(DisasContext *s, arg_azz_n *a, bool multi) +{ + return do_azz_acc_fp(s, a->n, 1, a->rv, a->off, a->zn, a->zm, 1, 0, + multi, FPST_ENV, gen_helper_sme2_fdot_h); +} + +TRANS_FEAT(FDOT_n1, aa64_sme2, do_fdot, a, false) +TRANS_FEAT(FDOT_nn, aa64_sme2, do_fdot, a, true) + +static bool do_fdot_nx(DisasContext *s, arg_azx_n *a) +{ + return do_azz_acc_fp(s, a->n, 1, a->rv, a->off, a->zn, a->zm, + a->idx | (1 << 2), 0, false, FPST_ENV, + gen_helper_sme2_fdot_idx_h); +} + +TRANS_FEAT(FDOT_nx, aa64_sme2, do_fdot_nx, a) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index b85bd885f9..4acd22f55e 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -7229,6 +7229,11 @@ TRANS_FEAT_NONSTREAMING(USMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, TRANS_FEAT_NONSTREAMING(UMMLA, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, gen_helper_gvec_ummla_b, a, 0) +TRANS_FEAT(FDOT_zzzz, aa64_sme2_or_sve2p1, gen_gvec_env_arg_zzzz, + gen_helper_sme2_fdot_h, a, 0) +TRANS_FEAT(FDOT_zzxz, aa64_sme2_or_sve2p1, gen_gvec_env_arg_zzxz, + gen_helper_sme2_fdot_idx_h, a) + TRANS_FEAT(BFDOT_zzzz, aa64_sve_bf16, gen_gvec_env_arg_zzzz, gen_helper_gvec_bfdot, a, 0) TRANS_FEAT(BFDOT_zzxz, aa64_sve_bf16, gen_gvec_env_arg_zzxz, From be0b56851a56da8ef4410cd936a351bd4070caaa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:01 -0600 Subject: [PATCH 1985/2760] target/arm: Implement SME2 BFDOT Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-39-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 9 +++++++++ target/arm/tcg/translate-sme.c | 17 +++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index a2b93519c4..18e625605f 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -288,6 +288,9 @@ BFMLSL_n1 11000001 001 1 .... 0 .. 010 ..... 110 .. @azz_nx1_o2x2 n=4 FDOT_n1 11000001 001 0 .... 0 .. 100 ..... 00 ... @azz_nx1_o3 n=2 FDOT_n1 11000001 001 1 .... 0 .. 100 ..... 00 ... @azz_nx1_o3 n=4 +BFDOT_n1 11000001 001 0 .... 0 .. 100 ..... 10 ... @azz_nx1_o3 n=2 +BFDOT_n1 11000001 001 1 .... 0 .. 100 ..... 10 ... @azz_nx1_o3 n=4 + ### SME2 Multi-vector Multiple Array Vectors %zn_ax2 6:4 !function=times_2 @@ -328,6 +331,9 @@ BFMLSL_nn 11000001 101 ...01 0 .. 010 ...00 110 .. @azz_4x4_o2x2 FDOT_nn 11000001 101 ....0 0 .. 100 ....0 00 ... @azz_2x2_o3 FDOT_nn 11000001 101 ...01 0 .. 100 ...00 00 ... @azz_4x4_o3 +BFDOT_nn 11000001 101 ....0 0 .. 100 ....0 10 ... @azz_2x2_o3 +BFDOT_nn 11000001 101 ...01 0 .. 100 ...00 10 ... @azz_4x4_o3 + ### SME2 Multi-vector Indexed &azx_n n off rv zn zm idx @@ -365,3 +371,6 @@ BFMLSL_nx 11000001 1001 .... 1 .. 1 .. ...00 11 ... @azx_4x1_o2x2 FDOT_nx 11000001 0101 .... 0 .. 1 .. ....0 01 ... @azx_2x1_i2_o3 FDOT_nx 11000001 0101 .... 1 .. 1 .. ...00 01 ... @azx_4x1_i2_o3 + +BFDOT_nx 11000001 0101 .... 0 .. 1 .. ....0 11 ... @azx_2x1_i2_o3 +BFDOT_nx 11000001 0101 .... 1 .. 1 .. ...00 11 ... @azx_4x1_i2_o3 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 761584c90b..965a49465e 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -921,3 +921,20 @@ static bool do_fdot_nx(DisasContext *s, arg_azx_n *a) } TRANS_FEAT(FDOT_nx, aa64_sme2, do_fdot_nx, a) + +static bool do_bfdot(DisasContext *s, arg_azz_n *a, bool multi) +{ + return do_azz_acc_fp(s, a->n, 1, a->rv, a->off, a->zn, a->zm, 0, 0, + multi, FPST_ENV, gen_helper_gvec_bfdot); +} + +TRANS_FEAT(BFDOT_n1, aa64_sme2, do_bfdot, a, false) +TRANS_FEAT(BFDOT_nn, aa64_sme2, do_bfdot, a, true) + +static bool do_bfdot_nx(DisasContext *s, arg_azx_n *a) +{ + return do_azz_acc_fp(s, a->n, 1, a->rv, a->off, a->zn, a->zm, a->idx, 0, + false, FPST_ENV, gen_helper_gvec_bfdot_idx); +} + +TRANS_FEAT(BFDOT_nx, aa64_sme2, do_bfdot_nx, a) From 7bca70e01a3b2e89b265254a468ecc3c150eade9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:02 -0600 Subject: [PATCH 1986/2760] target/arm: Implement SME2 FVDOT, BFVDOT Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-40-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 2 ++ target/arm/tcg/helper.h | 2 ++ target/arm/tcg/sme.decode | 3 +++ target/arm/tcg/sme_helper.c | 30 ++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 24 +++++++++++++++++++++ target/arm/tcg/vec_helper.c | 39 ++++++++++++++++++++++++++++++++++ 6 files changed, 100 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index ec93ff57ff..8f5a1b3c90 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -178,3 +178,5 @@ DEF_HELPER_FLAGS_6(sme2_fdot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(sme2_fdot_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(sme2_fvdot_idx_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index a05b63380c..d8d389af2b 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -1089,6 +1089,8 @@ DEF_HELPER_FLAGS_6(gvec_bfdot, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(gvec_bfdot_idx, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_6(sme2_bfvdot_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(gvec_bfmmla, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 18e625605f..7c057bcad2 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -374,3 +374,6 @@ FDOT_nx 11000001 0101 .... 1 .. 1 .. ...00 01 ... @azx_4x1_i2_o3 BFDOT_nx 11000001 0101 .... 0 .. 1 .. ....0 11 ... @azx_2x1_i2_o3 BFDOT_nx 11000001 0101 .... 1 .. 1 .. ...00 11 ... @azx_4x1_i2_o3 + +FVDOT 11000001 0101 .... 0 .. 0 .. ....0 01 ... @azx_2x1_i2_o3 +BFVDOT 11000001 0101 .... 0 .. 0 .. ....0 11 ... @azx_2x1_i2_o3 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 60322be3d0..8b45865461 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1196,6 +1196,36 @@ void HELPER(sme2_fdot_idx_h)(void *vd, void *vn, void *vm, void *va, } } +void HELPER(sme2_fvdot_idx_h)(void *vd, void *vn, void *vm, void *va, + CPUARMState *env, uint32_t desc) +{ + intptr_t i, j, oprsz = simd_maxsz(desc); + intptr_t elements = oprsz / sizeof(float32); + intptr_t eltspersegment = MIN(4, elements); + int idx = extract32(desc, SIMD_DATA_SHIFT, 2); + int sel = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + float_status fpst_odd, *fpst_std, *fpst_f16; + float32 *d = vd, *a = va; + uint16_t *n0 = vn; + uint16_t *n1 = vn + sizeof(ARMVectorReg); + uint32_t *m = (uint32_t *)vm + H4(idx); + + fpst_std = &env->vfp.fp_status[FPST_ZA]; + fpst_f16 = &env->vfp.fp_status[FPST_ZA_F16]; + fpst_odd = *fpst_std; + set_float_rounding_mode(float_round_to_odd, &fpst_odd); + + for (i = 0; i < elements; i += eltspersegment) { + uint32_t mm = m[i]; + for (j = 0; j < eltspersegment; ++j) { + uint32_t nn = (n0[H2(2 * (i + j) + sel)]) + | (n1[H2(2 * (i + j) + sel)] << 16); + d[i + H4(j)] = f16_dotadd(a[i + H4(j)], nn, mm, + fpst_f16, fpst_std, &fpst_odd); + } + } +} + void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, void *vpn, void *vpm, CPUARMState *env, uint32_t desc) { diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 965a49465e..410a8d037c 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -938,3 +938,27 @@ static bool do_bfdot_nx(DisasContext *s, arg_azx_n *a) } TRANS_FEAT(BFDOT_nx, aa64_sme2, do_bfdot_nx, a) + +static bool do_vdot(DisasContext *s, arg_azx_n *a, gen_helper_gvec_4_ptr *fn) +{ + if (sme_smza_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + int vstride = svl / 2; + TCGv_ptr t_za = get_zarray(s, a->rv, a->off, 2, 1); + TCGv_ptr t_zn = vec_full_reg_ptr(s, a->zn); + TCGv_ptr t_zm = vec_full_reg_ptr(s, a->zm); + TCGv_ptr t = tcg_temp_new_ptr(); + + for (int i = 0; i < 2; ++i) { + int o_za = i * vstride * sizeof(ARMVectorReg); + int desc = simd_desc(svl, svl, a->idx | (i << 2)); + + tcg_gen_addi_ptr(t, t_za, o_za); + fn(t, t_zn, t_zm, t, tcg_env, tcg_constant_i32(desc)); + } + } + return true; +} + +TRANS_FEAT(FVDOT, aa64_sme, do_vdot, a, gen_helper_sme2_fvdot_idx_h) +TRANS_FEAT(BFVDOT, aa64_sme, do_vdot, a, gen_helper_sme2_bfvdot_idx) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 5dcd6f9282..616f4050e4 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -3110,6 +3110,45 @@ void HELPER(gvec_bfdot_idx)(void *vd, void *vn, void *vm, clear_tail(d, opr_sz, simd_maxsz(desc)); } +void HELPER(sme2_bfvdot_idx)(void *vd, void *vn, void *vm, + void *va, CPUARMState *env, uint32_t desc) +{ + intptr_t i, j, opr_sz = simd_oprsz(desc); + intptr_t idx = extract32(desc, SIMD_DATA_SHIFT, 2); + intptr_t sel = extract32(desc, SIMD_DATA_SHIFT + 2, 1); + intptr_t elements = opr_sz / 4; + intptr_t eltspersegment = MIN(16 / 4, elements); + float32 *d = vd, *a = va; + uint16_t *n0 = vn; + uint16_t *n1 = vn + sizeof(ARMVectorReg); + uint32_t *m = vm; + float_status fpst, fpst_odd; + + if (is_ebf(env, &fpst, &fpst_odd)) { + for (i = 0; i < elements; i += eltspersegment) { + uint32_t m_idx = m[i + H4(idx)]; + + for (j = 0; j < eltspersegment; j++) { + uint32_t nn = (n0[H2(2 * (i + j) + sel)]) + | (n1[H2(2 * (i + j) + sel)] << 16); + d[i + H4(j)] = bfdotadd_ebf(a[i + H4(j)], nn, m_idx, + &fpst, &fpst_odd); + } + } + } else { + for (i = 0; i < elements; i += eltspersegment) { + uint32_t m_idx = m[i + H4(idx)]; + + for (j = 0; j < eltspersegment; j++) { + uint32_t nn = (n0[H2(2 * (i + j) + sel)]) + | (n1[H2(2 * (i + j) + sel)] << 16); + d[i + H4(j)] = bfdotadd(a[i + H4(j)], nn, m_idx, &fpst); + } + } + } + clear_tail(d, opr_sz, simd_maxsz(desc)); +} + void HELPER(gvec_bfmmla)(void *vd, void *vn, void *vm, void *va, CPUARMState *env, uint32_t desc) { From f7186b4288d88c529c5cb3eb813146092a37cfce Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:03 -0600 Subject: [PATCH 1987/2760] target/arm: Rename helper_gvec_*dot_[bh] to *_4[bh] Emphasize that these are 4-way dot products. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-41-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper.h | 22 +++++++++++----------- target/arm/tcg/translate-a64.c | 14 +++++++------- target/arm/tcg/translate-neon.c | 14 +++++++------- target/arm/tcg/translate-sve.c | 18 +++++++++--------- target/arm/tcg/vec_helper.c | 22 +++++++++++----------- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index d8d389af2b..a19955b872 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -616,23 +616,23 @@ DEF_HELPER_FLAGS_5(sve2_sqrdmlah_d, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve2_sqrdmlsh_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sdot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_udot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sdot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_udot_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_usdot_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sdot_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_udot_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sdot_4h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_udot_4h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_usdot_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sdot_idx_b, TCG_CALL_NO_RWG, +DEF_HELPER_FLAGS_5(gvec_sdot_idx_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_udot_idx_b, TCG_CALL_NO_RWG, +DEF_HELPER_FLAGS_5(gvec_udot_idx_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sdot_idx_h, TCG_CALL_NO_RWG, +DEF_HELPER_FLAGS_5(gvec_sdot_idx_4h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_udot_idx_h, TCG_CALL_NO_RWG, +DEF_HELPER_FLAGS_5(gvec_udot_idx_4h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_sudot_idx_b, TCG_CALL_NO_RWG, +DEF_HELPER_FLAGS_5(gvec_sudot_idx_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(gvec_usdot_idx_b, TCG_CALL_NO_RWG, +DEF_HELPER_FLAGS_5(gvec_usdot_idx_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_fcaddh, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/translate-a64.c b/target/arm/tcg/translate-a64.c index d823036c96..dbf47595db 100644 --- a/target/arm/tcg/translate-a64.c +++ b/target/arm/tcg/translate-a64.c @@ -6114,9 +6114,9 @@ static bool do_dot_vector_env(DisasContext *s, arg_qrrr_e *a, return true; } -TRANS_FEAT(SDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_sdot_b) -TRANS_FEAT(UDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_udot_b) -TRANS_FEAT(USDOT_v, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_usdot_b) +TRANS_FEAT(SDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_sdot_4b) +TRANS_FEAT(UDOT_v, aa64_dp, do_dot_vector, a, gen_helper_gvec_udot_4b) +TRANS_FEAT(USDOT_v, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_usdot_4b) TRANS_FEAT(BFDOT_v, aa64_bf16, do_dot_vector_env, a, gen_helper_gvec_bfdot) TRANS_FEAT(BFMMLA, aa64_bf16, do_dot_vector_env, a, gen_helper_gvec_bfmmla) TRANS_FEAT(SMMLA, aa64_i8mm, do_dot_vector, a, gen_helper_gvec_smmla_b) @@ -6876,12 +6876,12 @@ static bool do_dot_vector_idx_env(DisasContext *s, arg_qrrx_e *a, return true; } -TRANS_FEAT(SDOT_vi, aa64_dp, do_dot_vector_idx, a, gen_helper_gvec_sdot_idx_b) -TRANS_FEAT(UDOT_vi, aa64_dp, do_dot_vector_idx, a, gen_helper_gvec_udot_idx_b) +TRANS_FEAT(SDOT_vi, aa64_dp, do_dot_vector_idx, a, gen_helper_gvec_sdot_idx_4b) +TRANS_FEAT(UDOT_vi, aa64_dp, do_dot_vector_idx, a, gen_helper_gvec_udot_idx_4b) TRANS_FEAT(SUDOT_vi, aa64_i8mm, do_dot_vector_idx, a, - gen_helper_gvec_sudot_idx_b) + gen_helper_gvec_sudot_idx_4b) TRANS_FEAT(USDOT_vi, aa64_i8mm, do_dot_vector_idx, a, - gen_helper_gvec_usdot_idx_b) + gen_helper_gvec_usdot_idx_4b) TRANS_FEAT(BFDOT_vi, aa64_bf16, do_dot_vector_idx_env, a, gen_helper_gvec_bfdot_idx) diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index c4fecb8fd6..ea04336797 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -271,7 +271,7 @@ static bool trans_VSDOT(DisasContext *s, arg_VSDOT *a) return false; } return do_neon_ddda(s, a->q * 7, a->vd, a->vn, a->vm, 0, - gen_helper_gvec_sdot_b); + gen_helper_gvec_sdot_4b); } static bool trans_VUDOT(DisasContext *s, arg_VUDOT *a) @@ -280,7 +280,7 @@ static bool trans_VUDOT(DisasContext *s, arg_VUDOT *a) return false; } return do_neon_ddda(s, a->q * 7, a->vd, a->vn, a->vm, 0, - gen_helper_gvec_udot_b); + gen_helper_gvec_udot_4b); } static bool trans_VUSDOT(DisasContext *s, arg_VUSDOT *a) @@ -289,7 +289,7 @@ static bool trans_VUSDOT(DisasContext *s, arg_VUSDOT *a) return false; } return do_neon_ddda(s, a->q * 7, a->vd, a->vn, a->vm, 0, - gen_helper_gvec_usdot_b); + gen_helper_gvec_usdot_4b); } static bool trans_VDOT_b16(DisasContext *s, arg_VDOT_b16 *a) @@ -356,7 +356,7 @@ static bool trans_VSDOT_scalar(DisasContext *s, arg_VSDOT_scalar *a) return false; } return do_neon_ddda(s, a->q * 6, a->vd, a->vn, a->vm, a->index, - gen_helper_gvec_sdot_idx_b); + gen_helper_gvec_sdot_idx_4b); } static bool trans_VUDOT_scalar(DisasContext *s, arg_VUDOT_scalar *a) @@ -365,7 +365,7 @@ static bool trans_VUDOT_scalar(DisasContext *s, arg_VUDOT_scalar *a) return false; } return do_neon_ddda(s, a->q * 6, a->vd, a->vn, a->vm, a->index, - gen_helper_gvec_udot_idx_b); + gen_helper_gvec_udot_idx_4b); } static bool trans_VUSDOT_scalar(DisasContext *s, arg_VUSDOT_scalar *a) @@ -374,7 +374,7 @@ static bool trans_VUSDOT_scalar(DisasContext *s, arg_VUSDOT_scalar *a) return false; } return do_neon_ddda(s, a->q * 6, a->vd, a->vn, a->vm, a->index, - gen_helper_gvec_usdot_idx_b); + gen_helper_gvec_usdot_idx_4b); } static bool trans_VSUDOT_scalar(DisasContext *s, arg_VSUDOT_scalar *a) @@ -383,7 +383,7 @@ static bool trans_VSUDOT_scalar(DisasContext *s, arg_VSUDOT_scalar *a) return false; } return do_neon_ddda(s, a->q * 6, a->vd, a->vn, a->vm, a->index, - gen_helper_gvec_sudot_idx_b); + gen_helper_gvec_sudot_idx_4b); } static bool trans_VDOT_b16_scal(DisasContext *s, arg_VDOT_b16_scal *a) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 4acd22f55e..1564ee2558 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3402,8 +3402,8 @@ DO_ZZI(UMIN, umin) #undef DO_ZZI static gen_helper_gvec_4 * const dot_fns[2][2] = { - { gen_helper_gvec_sdot_b, gen_helper_gvec_sdot_h }, - { gen_helper_gvec_udot_b, gen_helper_gvec_udot_h } + { gen_helper_gvec_sdot_4b, gen_helper_gvec_sdot_4h }, + { gen_helper_gvec_udot_4b, gen_helper_gvec_udot_4h } }; TRANS_FEAT(DOT_zzzz, aa64_sve, gen_gvec_ool_zzzz, dot_fns[a->u][a->sz], a->rd, a->rn, a->rm, a->ra, 0) @@ -3413,18 +3413,18 @@ TRANS_FEAT(DOT_zzzz, aa64_sve, gen_gvec_ool_zzzz, */ TRANS_FEAT(SDOT_zzxw_s, aa64_sve, gen_gvec_ool_arg_zzxz, - gen_helper_gvec_sdot_idx_b, a) + gen_helper_gvec_sdot_idx_4b, a) TRANS_FEAT(SDOT_zzxw_d, aa64_sve, gen_gvec_ool_arg_zzxz, - gen_helper_gvec_sdot_idx_h, a) + gen_helper_gvec_sdot_idx_4h, a) TRANS_FEAT(UDOT_zzxw_s, aa64_sve, gen_gvec_ool_arg_zzxz, - gen_helper_gvec_udot_idx_b, a) + gen_helper_gvec_udot_idx_4b, a) TRANS_FEAT(UDOT_zzxw_d, aa64_sve, gen_gvec_ool_arg_zzxz, - gen_helper_gvec_udot_idx_h, a) + gen_helper_gvec_udot_idx_4h, a) TRANS_FEAT(SUDOT_zzxw_s, aa64_sve_i8mm, gen_gvec_ool_arg_zzxz, - gen_helper_gvec_sudot_idx_b, a) + gen_helper_gvec_sudot_idx_4b, a) TRANS_FEAT(USDOT_zzxw_s, aa64_sve_i8mm, gen_gvec_ool_arg_zzxz, - gen_helper_gvec_usdot_idx_b, a) + gen_helper_gvec_usdot_idx_4b, a) #define DO_SVE2_RRX(NAME, FUNC) \ TRANS_FEAT(NAME, aa64_sve, gen_gvec_ool_zzz, FUNC, \ @@ -7152,7 +7152,7 @@ TRANS_FEAT(SQRDCMLAH_zzzz, aa64_sve2, gen_gvec_ool_zzzz, sqrdcmlah_fns[a->esz], a->rd, a->rn, a->rm, a->ra, a->rot) TRANS_FEAT(USDOT_zzzz, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, - a->esz == 2 ? gen_helper_gvec_usdot_b : NULL, a, 0) + a->esz == 2 ? gen_helper_gvec_usdot_4b : NULL, a, 0) TRANS_FEAT_NONSTREAMING(AESMC, aa64_sve2_aes, gen_gvec_ool_zz, gen_helper_crypto_aesmc, a->rd, a->rd, 0) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 616f4050e4..f18d1675b5 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -825,11 +825,11 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ clear_tail(d, opr_sz, simd_maxsz(desc)); \ } -DO_DOT(gvec_sdot_b, int32_t, int8_t, int8_t) -DO_DOT(gvec_udot_b, uint32_t, uint8_t, uint8_t) -DO_DOT(gvec_usdot_b, uint32_t, uint8_t, int8_t) -DO_DOT(gvec_sdot_h, int64_t, int16_t, int16_t) -DO_DOT(gvec_udot_h, uint64_t, uint16_t, uint16_t) +DO_DOT(gvec_sdot_4b, int32_t, int8_t, int8_t) +DO_DOT(gvec_udot_4b, uint32_t, uint8_t, uint8_t) +DO_DOT(gvec_usdot_4b, uint32_t, uint8_t, int8_t) +DO_DOT(gvec_sdot_4h, int64_t, int16_t, int16_t) +DO_DOT(gvec_udot_4h, uint64_t, uint16_t, uint16_t) #define DO_DOT_IDX(NAME, TYPED, TYPEN, TYPEM, HD) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ @@ -865,12 +865,12 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ clear_tail(d, opr_sz, simd_maxsz(desc)); \ } -DO_DOT_IDX(gvec_sdot_idx_b, int32_t, int8_t, int8_t, H4) -DO_DOT_IDX(gvec_udot_idx_b, uint32_t, uint8_t, uint8_t, H4) -DO_DOT_IDX(gvec_sudot_idx_b, int32_t, int8_t, uint8_t, H4) -DO_DOT_IDX(gvec_usdot_idx_b, int32_t, uint8_t, int8_t, H4) -DO_DOT_IDX(gvec_sdot_idx_h, int64_t, int16_t, int16_t, H8) -DO_DOT_IDX(gvec_udot_idx_h, uint64_t, uint16_t, uint16_t, H8) +DO_DOT_IDX(gvec_sdot_idx_4b, int32_t, int8_t, int8_t, H4) +DO_DOT_IDX(gvec_udot_idx_4b, uint32_t, uint8_t, uint8_t, H4) +DO_DOT_IDX(gvec_sudot_idx_4b, int32_t, int8_t, uint8_t, H4) +DO_DOT_IDX(gvec_usdot_idx_4b, int32_t, uint8_t, int8_t, H4) +DO_DOT_IDX(gvec_sdot_idx_4h, int64_t, int16_t, int16_t, H8) +DO_DOT_IDX(gvec_udot_idx_4h, uint64_t, uint16_t, uint16_t, H8) void HELPER(gvec_fcaddh)(void *vd, void *vn, void *vm, float_status *fpst, uint32_t desc) From 9fce675a8e3c216db3a9dd029d4e655295125110 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:04 -0600 Subject: [PATCH 1988/2760] target/arm: Implemement SME2 SDOT, UDOT, USDOT, SUDOT Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250704142112.1018902-42-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper.h | 8 ++++ target/arm/tcg/sme.decode | 63 ++++++++++++++++++++++++- target/arm/tcg/translate-sme.c | 85 ++++++++++++++++++++++++++++++++++ target/arm/tcg/vec_helper.c | 51 ++++++++++++++++++++ 4 files changed, 206 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index a19955b872..c4a208e3ba 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -622,6 +622,9 @@ DEF_HELPER_FLAGS_5(gvec_sdot_4h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_udot_4h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_usdot_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sdot_2h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_udot_2h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(gvec_sdot_idx_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(gvec_udot_idx_4b, TCG_CALL_NO_RWG, @@ -635,6 +638,11 @@ DEF_HELPER_FLAGS_5(gvec_sudot_idx_4b, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_usdot_idx_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_sdot_idx_2h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(gvec_udot_idx_2h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(gvec_fcaddh, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fcadds, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 7c057bcad2..338637decd 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -291,6 +291,26 @@ FDOT_n1 11000001 001 1 .... 0 .. 100 ..... 00 ... @azz_nx1_o3 n=4 BFDOT_n1 11000001 001 0 .... 0 .. 100 ..... 10 ... @azz_nx1_o3 n=2 BFDOT_n1 11000001 001 1 .... 0 .. 100 ..... 10 ... @azz_nx1_o3 n=4 +USDOT_n1 11000001 001 0 .... 0 .. 101 ..... 01 ... @azz_nx1_o3 n=2 +USDOT_n1 11000001 001 1 .... 0 .. 101 ..... 01 ... @azz_nx1_o3 n=4 + +SUDOT_n1 11000001 001 0 .... 0 .. 101 ..... 11 ... @azz_nx1_o3 n=2 +SUDOT_n1 11000001 001 1 .... 0 .. 101 ..... 11 ... @azz_nx1_o3 n=4 + +SDOT_n1_4b 11000001 001 0 .... 0 .. 101 ..... 00 ... @azz_nx1_o3 n=2 +SDOT_n1_4b 11000001 001 1 .... 0 .. 101 ..... 00 ... @azz_nx1_o3 n=4 +SDOT_n1_4h 11000001 011 0 .... 0 .. 101 ..... 00 ... @azz_nx1_o3 n=2 +SDOT_n1_4h 11000001 011 1 .... 0 .. 101 ..... 00 ... @azz_nx1_o3 n=4 +SDOT_n1_2h 11000001 011 0 .... 0 .. 101 ..... 01 ... @azz_nx1_o3 n=2 +SDOT_n1_2h 11000001 011 1 .... 0 .. 101 ..... 01 ... @azz_nx1_o3 n=4 + +UDOT_n1_4b 11000001 001 0 .... 0 .. 101 ..... 10 ... @azz_nx1_o3 n=2 +UDOT_n1_4b 11000001 001 1 .... 0 .. 101 ..... 10 ... @azz_nx1_o3 n=4 +UDOT_n1_4h 11000001 011 0 .... 0 .. 101 ..... 10 ... @azz_nx1_o3 n=2 +UDOT_n1_4h 11000001 011 1 .... 0 .. 101 ..... 10 ... @azz_nx1_o3 n=4 +UDOT_n1_2h 11000001 011 0 .... 0 .. 101 ..... 11 ... @azz_nx1_o3 n=2 +UDOT_n1_2h 11000001 011 1 .... 0 .. 101 ..... 11 ... @azz_nx1_o3 n=4 + ### SME2 Multi-vector Multiple Array Vectors %zn_ax2 6:4 !function=times_2 @@ -334,6 +354,23 @@ FDOT_nn 11000001 101 ...01 0 .. 100 ...00 00 ... @azz_4x4_o3 BFDOT_nn 11000001 101 ....0 0 .. 100 ....0 10 ... @azz_2x2_o3 BFDOT_nn 11000001 101 ...01 0 .. 100 ...00 10 ... @azz_4x4_o3 +USDOT_nn 11000001 101 ....0 0 .. 101 ....0 01 ... @azz_2x2_o3 +USDOT_nn 11000001 101 ...01 0 .. 101 ...00 01 ... @azz_4x4_o3 + +SDOT_nn_4b 11000001 101 ....0 0 .. 101 ....0 00 ... @azz_2x2_o3 +SDOT_nn_4b 11000001 101 ...01 0 .. 101 ...00 00 ... @azz_4x4_o3 +SDOT_nn_4h 11000001 111 ....0 0 .. 101 ....0 00 ... @azz_2x2_o3 +SDOT_nn_4h 11000001 111 ...01 0 .. 101 ...00 00 ... @azz_4x4_o3 +SDOT_nn_2h 11000001 111 ....0 0 .. 101 ....0 01 ... @azz_2x2_o3 +SDOT_nn_2h 11000001 111 ...01 0 .. 101 ...00 01 ... @azz_4x4_o3 + +UDOT_nn_4b 11000001 101 ....0 0 .. 101 ....0 10 ... @azz_2x2_o3 +UDOT_nn_4b 11000001 101 ...01 0 .. 101 ...00 10 ... @azz_4x4_o3 +UDOT_nn_4h 11000001 111 ....0 0 .. 101 ....0 10 ... @azz_2x2_o3 +UDOT_nn_4h 11000001 111 ...01 0 .. 101 ...00 10 ... @azz_4x4_o3 +UDOT_nn_2h 11000001 111 ....0 0 .. 101 ....0 11 ... @azz_2x2_o3 +UDOT_nn_2h 11000001 111 ...01 0 .. 101 ...00 11 ... @azz_4x4_o3 + ### SME2 Multi-vector Indexed &azx_n n off rv zn zm idx @@ -367,7 +404,11 @@ BFMLSL_nx 11000001 1001 .... 1 .. 1 .. ...00 11 ... @azx_4x1_o2x2 @azx_2x1_i2_o3 ........ .... zm:4 . .. . idx:2 .... ... off:3 \ &azx_n n=2 rv=%mova_rv zn=%zn_ax2 @azx_4x1_i2_o3 ........ .... zm:4 . .. . idx:2 .... ... off:3 \ - &azx_n n=2 rv=%mova_rv zn=%zn_ax4 + &azx_n n=4 rv=%mova_rv zn=%zn_ax4 +@azx_2x1_i1_o3 ........ .... zm:4 . .. .. idx:1 .... ... off:3 \ + &azx_n n=2 rv=%mova_rv zn=%zn_ax2 +@azx_4x1_i1_o3 ........ .... zm:4 . .. .. idx:1 .... ... off:3 \ + &azx_n n=4 rv=%mova_rv zn=%zn_ax4 FDOT_nx 11000001 0101 .... 0 .. 1 .. ....0 01 ... @azx_2x1_i2_o3 FDOT_nx 11000001 0101 .... 1 .. 1 .. ...00 01 ... @azx_4x1_i2_o3 @@ -377,3 +418,23 @@ BFDOT_nx 11000001 0101 .... 1 .. 1 .. ...00 11 ... @azx_4x1_i2_o3 FVDOT 11000001 0101 .... 0 .. 0 .. ....0 01 ... @azx_2x1_i2_o3 BFVDOT 11000001 0101 .... 0 .. 0 .. ....0 11 ... @azx_2x1_i2_o3 + +SDOT_nx_2h 11000001 0101 .... 0 .. 1 .. ....0 00 ... @azx_2x1_i2_o3 +SDOT_nx_2h 11000001 0101 .... 1 .. 1 .. ...00 00 ... @azx_4x1_i2_o3 +SDOT_nx_4b 11000001 0101 .... 0 .. 1 .. ....1 00 ... @azx_2x1_i2_o3 +SDOT_nx_4b 11000001 0101 .... 1 .. 1 .. ...01 00 ... @azx_4x1_i2_o3 +SDOT_nx_4h 11000001 1101 .... 0 .. 00 . ....0 01 ... @azx_2x1_i1_o3 +SDOT_nx_4h 11000001 1101 .... 1 .. 00 . ...00 01 ... @azx_4x1_i1_o3 + +UDOT_nx_2h 11000001 0101 .... 0 .. 1 .. ....0 10 ... @azx_2x1_i2_o3 +UDOT_nx_2h 11000001 0101 .... 1 .. 1 .. ...00 10 ... @azx_4x1_i2_o3 +UDOT_nx_4b 11000001 0101 .... 0 .. 1 .. ....1 10 ... @azx_2x1_i2_o3 +UDOT_nx_4b 11000001 0101 .... 1 .. 1 .. ...01 10 ... @azx_4x1_i2_o3 +UDOT_nx_4h 11000001 1101 .... 0 .. 00 . ....0 11 ... @azx_2x1_i1_o3 +UDOT_nx_4h 11000001 1101 .... 1 .. 00 . ...00 11 ... @azx_4x1_i1_o3 + +USDOT_nx 11000001 0101 .... 0 .. 1 .. ....1 01 ... @azx_2x1_i2_o3 +USDOT_nx 11000001 0101 .... 1 .. 1 .. ...01 01 ... @azx_4x1_i2_o3 + +SUDOT_nx 11000001 0101 .... 0 .. 1 .. ....1 11 ... @azx_2x1_i2_o3 +SUDOT_nx 11000001 0101 .... 1 .. 1 .. ...01 11 ... @azx_4x1_i2_o3 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 410a8d037c..341f4495e9 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -962,3 +962,88 @@ static bool do_vdot(DisasContext *s, arg_azx_n *a, gen_helper_gvec_4_ptr *fn) TRANS_FEAT(FVDOT, aa64_sme, do_vdot, a, gen_helper_sme2_fvdot_idx_h) TRANS_FEAT(BFVDOT, aa64_sme, do_vdot, a, gen_helper_sme2_bfvdot_idx) + +/* + * Expand array multi-vector single (n1), array multi-vector (nn), + * and array multi-vector indexed (nx), for integer accumulate. + * multi: true for nn, false for n1. + * data: stuff for simd_data, including any index. + */ +static bool do_azz_acc(DisasContext *s, int nreg, int nsel, + int rv, int off, int zn, int zm, + int data, int shsel, bool multi, + gen_helper_gvec_4 *fn) +{ + if (sme_smza_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + int vstride = svl / nreg; + TCGv_ptr t_za = get_zarray(s, rv, off, nreg, nsel); + TCGv_ptr t = tcg_temp_new_ptr(); + + for (int r = 0; r < nreg; ++r) { + TCGv_ptr t_zn = vec_full_reg_ptr(s, zn); + TCGv_ptr t_zm = vec_full_reg_ptr(s, zm); + + for (int i = 0; i < nsel; ++i) { + int o_za = (r * vstride + i) * sizeof(ARMVectorReg); + int desc = simd_desc(svl, svl, data | (i << shsel)); + + tcg_gen_addi_ptr(t, t_za, o_za); + fn(t, t_zn, t_zm, t, tcg_constant_i32(desc)); + } + + /* + * For multiple-and-single vectors, Zn may wrap. + * For multiple vectors, both Zn and Zm are aligned. + */ + zn = (zn + 1) % 32; + zm += multi; + } + } + return true; +} + +static bool do_dot(DisasContext *s, arg_azz_n *a, bool multi, + gen_helper_gvec_4 *fn) +{ + return do_azz_acc(s, a->n, 1, a->rv, a->off, a->zn, a->zm, + 0, 0, multi, fn); +} + +static void gen_helper_gvec_sudot_4b(TCGv_ptr d, TCGv_ptr n, TCGv_ptr m, + TCGv_ptr a, TCGv_i32 desc) +{ + gen_helper_gvec_usdot_4b(d, m, n, a, desc); +} + +TRANS_FEAT(USDOT_n1, aa64_sme2, do_dot, a, false, gen_helper_gvec_usdot_4b) +TRANS_FEAT(SUDOT_n1, aa64_sme2, do_dot, a, false, gen_helper_gvec_sudot_4b) +TRANS_FEAT(SDOT_n1_2h, aa64_sme2, do_dot, a, false, gen_helper_gvec_sdot_2h) +TRANS_FEAT(UDOT_n1_2h, aa64_sme2, do_dot, a, false, gen_helper_gvec_udot_2h) +TRANS_FEAT(SDOT_n1_4b, aa64_sme2, do_dot, a, false, gen_helper_gvec_sdot_4b) +TRANS_FEAT(UDOT_n1_4b, aa64_sme2, do_dot, a, false, gen_helper_gvec_udot_4b) +TRANS_FEAT(SDOT_n1_4h, aa64_sme2_i16i64, do_dot, a, false, gen_helper_gvec_sdot_4h) +TRANS_FEAT(UDOT_n1_4h, aa64_sme2_i16i64, do_dot, a, false, gen_helper_gvec_udot_4h) + +TRANS_FEAT(USDOT_nn, aa64_sme2, do_dot, a, true, gen_helper_gvec_usdot_4b) +TRANS_FEAT(SDOT_nn_2h, aa64_sme2, do_dot, a, true, gen_helper_gvec_sdot_2h) +TRANS_FEAT(UDOT_nn_2h, aa64_sme2, do_dot, a, true, gen_helper_gvec_udot_2h) +TRANS_FEAT(SDOT_nn_4b, aa64_sme2, do_dot, a, true, gen_helper_gvec_sdot_4b) +TRANS_FEAT(UDOT_nn_4b, aa64_sme2, do_dot, a, true, gen_helper_gvec_udot_4b) +TRANS_FEAT(SDOT_nn_4h, aa64_sme2_i16i64, do_dot, a, true, gen_helper_gvec_sdot_4h) +TRANS_FEAT(UDOT_nn_4h, aa64_sme2_i16i64, do_dot, a, true, gen_helper_gvec_udot_4h) + +static bool do_dot_nx(DisasContext *s, arg_azx_n *a, gen_helper_gvec_4 *fn) +{ + return do_azz_acc(s, a->n, 1, a->rv, a->off, a->zn, a->zm, + a->idx, 0, false, fn); +} + +TRANS_FEAT(USDOT_nx, aa64_sme2, do_dot_nx, a, gen_helper_gvec_usdot_idx_4b) +TRANS_FEAT(SUDOT_nx, aa64_sme2, do_dot_nx, a, gen_helper_gvec_sudot_idx_4b) +TRANS_FEAT(SDOT_nx_2h, aa64_sme2, do_dot_nx, a, gen_helper_gvec_sdot_idx_2h) +TRANS_FEAT(UDOT_nx_2h, aa64_sme2, do_dot_nx, a, gen_helper_gvec_udot_idx_2h) +TRANS_FEAT(SDOT_nx_4b, aa64_sme2, do_dot_nx, a, gen_helper_gvec_sdot_idx_4b) +TRANS_FEAT(UDOT_nx_4b, aa64_sme2, do_dot_nx, a, gen_helper_gvec_udot_idx_4b) +TRANS_FEAT(SDOT_nx_4h, aa64_sme2_i16i64, do_dot_nx, a, gen_helper_gvec_sdot_idx_4h) +TRANS_FEAT(UDOT_nx_4h, aa64_sme2_i16i64, do_dot_nx, a, gen_helper_gvec_udot_idx_4h) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index f18d1675b5..baea6d8b89 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -872,6 +872,57 @@ DO_DOT_IDX(gvec_usdot_idx_4b, int32_t, uint8_t, int8_t, H4) DO_DOT_IDX(gvec_sdot_idx_4h, int64_t, int16_t, int16_t, H8) DO_DOT_IDX(gvec_udot_idx_4h, uint64_t, uint16_t, uint16_t, H8) +#undef DO_DOT +#undef DO_DOT_IDX + +/* Similar for 2-way dot product */ +#define DO_DOT(NAME, TYPED, TYPEN, TYPEM) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ +{ \ + intptr_t i, opr_sz = simd_oprsz(desc); \ + TYPED *d = vd, *a = va; \ + TYPEN *n = vn; \ + TYPEM *m = vm; \ + for (i = 0; i < opr_sz / sizeof(TYPED); ++i) { \ + d[i] = (a[i] + \ + (TYPED)n[i * 2 + 0] * m[i * 2 + 0] + \ + (TYPED)n[i * 2 + 1] * m[i * 2 + 1]); \ + } \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + +#define DO_DOT_IDX(NAME, TYPED, TYPEN, TYPEM, HD) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ +{ \ + intptr_t i = 0, opr_sz = simd_oprsz(desc); \ + intptr_t opr_sz_n = opr_sz / sizeof(TYPED); \ + intptr_t segend = MIN(16 / sizeof(TYPED), opr_sz_n); \ + intptr_t index = simd_data(desc); \ + TYPED *d = vd, *a = va; \ + TYPEN *n = vn; \ + TYPEM *m_indexed = (TYPEM *)vm + HD(index) * 2; \ + do { \ + TYPED m0 = m_indexed[i * 2 + 0]; \ + TYPED m1 = m_indexed[i * 2 + 1]; \ + do { \ + d[i] = (a[i] + \ + n[i * 2 + 0] * m0 + \ + n[i * 2 + 1] * m1); \ + } while (++i < segend); \ + segend = i + (16 / sizeof(TYPED)); \ + } while (i < opr_sz_n); \ + clear_tail(d, opr_sz, simd_maxsz(desc)); \ +} + +DO_DOT(gvec_sdot_2h, int32_t, int16_t, int16_t) +DO_DOT(gvec_udot_2h, uint32_t, uint16_t, uint16_t) + +DO_DOT_IDX(gvec_sdot_idx_2h, int32_t, int16_t, int16_t, H4) +DO_DOT_IDX(gvec_udot_idx_2h, uint32_t, uint16_t, uint16_t, H4) + +#undef DO_DOT +#undef DO_DOT_IDX + void HELPER(gvec_fcaddh)(void *vd, void *vn, void *vm, float_status *fpst, uint32_t desc) { From a49089cd60667123cfdb949a8bd0b672575d0eb2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:05 -0600 Subject: [PATCH 1989/2760] target/arm: Rename SVE SDOT and UDOT patterns Emphasize the 4-way nature of these dot products. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-43-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve.decode | 12 ++++++------ target/arm/tcg/translate-sve.c | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 5970ed9ac4..51847ea56e 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -851,10 +851,10 @@ CDOT_zzzz 01000100 esz:2 0 rm:5 0001 rot:2 rn:5 rd:5 ra=%reg_movprfx #### SVE Multiply - Indexed # SVE integer dot product (indexed) -SDOT_zzxw_s 01000100 10 1 ..... 000000 ..... ..... @rrxr_2 esz=2 -SDOT_zzxw_d 01000100 11 1 ..... 000000 ..... ..... @rrxr_1 esz=3 -UDOT_zzxw_s 01000100 10 1 ..... 000001 ..... ..... @rrxr_2 esz=2 -UDOT_zzxw_d 01000100 11 1 ..... 000001 ..... ..... @rrxr_1 esz=3 +SDOT_zzxw_4s 01000100 10 1 ..... 000000 ..... ..... @rrxr_2 esz=2 +SDOT_zzxw_4d 01000100 11 1 ..... 000000 ..... ..... @rrxr_1 esz=3 +UDOT_zzxw_4s 01000100 10 1 ..... 000001 ..... ..... @rrxr_2 esz=2 +UDOT_zzxw_4d 01000100 11 1 ..... 000001 ..... ..... @rrxr_1 esz=3 # SVE2 integer multiply-add (indexed) MLA_zzxz_h 01000100 0. 1 ..... 000010 ..... ..... @rrxr_3 esz=1 @@ -873,8 +873,8 @@ SQRDMLSH_zzxz_s 01000100 10 1 ..... 000101 ..... ..... @rrxr_2 esz=2 SQRDMLSH_zzxz_d 01000100 11 1 ..... 000101 ..... ..... @rrxr_1 esz=3 # SVE mixed sign dot product (indexed) -USDOT_zzxw_s 01000100 10 1 ..... 000110 ..... ..... @rrxr_2 esz=2 -SUDOT_zzxw_s 01000100 10 1 ..... 000111 ..... ..... @rrxr_2 esz=2 +USDOT_zzxw_4s 01000100 10 1 ..... 000110 ..... ..... @rrxr_2 esz=2 +SUDOT_zzxw_4s 01000100 10 1 ..... 000111 ..... ..... @rrxr_2 esz=2 # SVE2 saturating multiply-add (indexed) SQDMLALB_zzxw_s 01000100 10 1 ..... 0010.0 ..... ..... @rrxr_3a esz=2 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 1564ee2558..56353b3bb4 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3412,18 +3412,18 @@ TRANS_FEAT(DOT_zzzz, aa64_sve, gen_gvec_ool_zzzz, * SVE Multiply - Indexed */ -TRANS_FEAT(SDOT_zzxw_s, aa64_sve, gen_gvec_ool_arg_zzxz, +TRANS_FEAT(SDOT_zzxw_4s, aa64_sve, gen_gvec_ool_arg_zzxz, gen_helper_gvec_sdot_idx_4b, a) -TRANS_FEAT(SDOT_zzxw_d, aa64_sve, gen_gvec_ool_arg_zzxz, +TRANS_FEAT(SDOT_zzxw_4d, aa64_sve, gen_gvec_ool_arg_zzxz, gen_helper_gvec_sdot_idx_4h, a) -TRANS_FEAT(UDOT_zzxw_s, aa64_sve, gen_gvec_ool_arg_zzxz, +TRANS_FEAT(UDOT_zzxw_4s, aa64_sve, gen_gvec_ool_arg_zzxz, gen_helper_gvec_udot_idx_4b, a) -TRANS_FEAT(UDOT_zzxw_d, aa64_sve, gen_gvec_ool_arg_zzxz, +TRANS_FEAT(UDOT_zzxw_4d, aa64_sve, gen_gvec_ool_arg_zzxz, gen_helper_gvec_udot_idx_4h, a) -TRANS_FEAT(SUDOT_zzxw_s, aa64_sve_i8mm, gen_gvec_ool_arg_zzxz, +TRANS_FEAT(SUDOT_zzxw_4s, aa64_sve_i8mm, gen_gvec_ool_arg_zzxz, gen_helper_gvec_sudot_idx_4b, a) -TRANS_FEAT(USDOT_zzxw_s, aa64_sve_i8mm, gen_gvec_ool_arg_zzxz, +TRANS_FEAT(USDOT_zzxw_4s, aa64_sve_i8mm, gen_gvec_ool_arg_zzxz, gen_helper_gvec_usdot_idx_4b, a) #define DO_SVE2_RRX(NAME, FUNC) \ From 050ce4fb6468d485cb906e0315eb8725266f5b0e Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:06 -0600 Subject: [PATCH 1990/2760] target/arm: Tighten USDOT (vectors) decode Rename to USDOT_zzzz_4s and force size=2 during decode. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-44-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve.decode | 2 +- target/arm/tcg/translate-sve.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 51847ea56e..401c025852 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1599,7 +1599,7 @@ SQRDCMLAH_zzzz 01000100 esz:2 0 rm:5 0011 rot:2 rn:5 rd:5 ra=%reg_movprfx ## SVE mixed sign dot product -USDOT_zzzz 01000100 .. 0 ..... 011 110 ..... ..... @rda_rn_rm +USDOT_zzzz_4s 01000100 10 0 ..... 011 110 ..... ..... @rda_rn_rm_ex esz=2 ### SVE2 floating point matrix multiply accumulate BFMMLA 01100100 01 1 ..... 111 001 ..... ..... @rda_rn_rm_ex esz=1 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 56353b3bb4..c82a0f771d 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -7151,8 +7151,8 @@ static gen_helper_gvec_4 * const sqrdcmlah_fns[] = { TRANS_FEAT(SQRDCMLAH_zzzz, aa64_sve2, gen_gvec_ool_zzzz, sqrdcmlah_fns[a->esz], a->rd, a->rn, a->rm, a->ra, a->rot) -TRANS_FEAT(USDOT_zzzz, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, - a->esz == 2 ? gen_helper_gvec_usdot_4b : NULL, a, 0) +TRANS_FEAT(USDOT_zzzz_4s, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, + gen_helper_gvec_usdot_4b, a, 0) TRANS_FEAT_NONSTREAMING(AESMC, aa64_sve2_aes, gen_gvec_ool_zz, gen_helper_crypto_aesmc, a->rd, a->rd, 0) From 26c3bafbf355f8aa8cb9197ccc4136c878c8bbfb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:07 -0600 Subject: [PATCH 1991/2760] target/arm: Implement SDOT, UDOT (2-way) for SME2/SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-45-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve.decode | 8 +++++++- target/arm/tcg/translate-sve.c | 10 ++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 401c025852..83c259def6 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -856,6 +856,9 @@ SDOT_zzxw_4d 01000100 11 1 ..... 000000 ..... ..... @rrxr_1 esz=3 UDOT_zzxw_4s 01000100 10 1 ..... 000001 ..... ..... @rrxr_2 esz=2 UDOT_zzxw_4d 01000100 11 1 ..... 000001 ..... ..... @rrxr_1 esz=3 +SDOT_zzxw_2s 01000100 10 0 ..... 110010 ..... ..... @rrxr_2 esz=2 +UDOT_zzxw_2s 01000100 10 0 ..... 110011 ..... ..... @rrxr_2 esz=2 + # SVE2 integer multiply-add (indexed) MLA_zzxz_h 01000100 0. 1 ..... 000010 ..... ..... @rrxr_3 esz=1 MLA_zzxz_s 01000100 10 1 ..... 000010 ..... ..... @rrxr_2 esz=2 @@ -1597,7 +1600,10 @@ UMLSLT_zzzw 01000100 .. 0 ..... 010 111 ..... ..... @rda_rn_rm CMLA_zzzz 01000100 esz:2 0 rm:5 0010 rot:2 rn:5 rd:5 ra=%reg_movprfx SQRDCMLAH_zzzz 01000100 esz:2 0 rm:5 0011 rot:2 rn:5 rd:5 ra=%reg_movprfx -## SVE mixed sign dot product +## SVE dot product + +SDOT_zzzz_2s 01000100 00 0 ..... 110 010 ..... ..... @rda_rn_rm_ex esz=2 +UDOT_zzzz_2s 01000100 00 0 ..... 110 011 ..... ..... @rda_rn_rm_ex esz=2 USDOT_zzzz_4s 01000100 10 0 ..... 011 110 ..... ..... @rda_rn_rm_ex esz=2 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index c82a0f771d..10261e25f9 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3426,6 +3426,11 @@ TRANS_FEAT(SUDOT_zzxw_4s, aa64_sve_i8mm, gen_gvec_ool_arg_zzxz, TRANS_FEAT(USDOT_zzxw_4s, aa64_sve_i8mm, gen_gvec_ool_arg_zzxz, gen_helper_gvec_usdot_idx_4b, a) +TRANS_FEAT(SDOT_zzxw_2s, aa64_sme2_or_sve2p1, gen_gvec_ool_arg_zzxz, + gen_helper_gvec_sdot_idx_2h, a) +TRANS_FEAT(UDOT_zzxw_2s, aa64_sme2_or_sve2p1, gen_gvec_ool_arg_zzxz, + gen_helper_gvec_udot_idx_2h, a) + #define DO_SVE2_RRX(NAME, FUNC) \ TRANS_FEAT(NAME, aa64_sve, gen_gvec_ool_zzz, FUNC, \ a->rd, a->rn, a->rm, a->index) @@ -7154,6 +7159,11 @@ TRANS_FEAT(SQRDCMLAH_zzzz, aa64_sve2, gen_gvec_ool_zzzz, TRANS_FEAT(USDOT_zzzz_4s, aa64_sve_i8mm, gen_gvec_ool_arg_zzzz, gen_helper_gvec_usdot_4b, a, 0) +TRANS_FEAT(SDOT_zzzz_2s, aa64_sme2_or_sve2p1, gen_gvec_ool_arg_zzzz, + gen_helper_gvec_sdot_2h, a, 0) +TRANS_FEAT(UDOT_zzzz_2s, aa64_sme2_or_sve2p1, gen_gvec_ool_arg_zzzz, + gen_helper_gvec_udot_2h, a, 0) + TRANS_FEAT_NONSTREAMING(AESMC, aa64_sve2_aes, gen_gvec_ool_zz, gen_helper_crypto_aesmc, a->rd, a->rd, 0) TRANS_FEAT_NONSTREAMING(AESIMC, aa64_sve2_aes, gen_gvec_ool_zz, From 279cd4d3123b52764c5e320b0e5a41b6cf7b4510 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:08 -0600 Subject: [PATCH 1992/2760] target/arm: Implement SME2 SVDOT, UVDOT, SUVDOT, USVDOT Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-46-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 11 +++++++++ target/arm/tcg/sme.decode | 11 +++++++++ target/arm/tcg/sme_helper.c | 42 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 23 +++++++++++++++++++ 4 files changed, 87 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 8f5a1b3c90..464877516b 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -180,3 +180,14 @@ DEF_HELPER_FLAGS_6(sme2_fdot_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(sme2_fvdot_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(sme2_svdot_idx_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_uvdot_idx_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_suvdot_idx_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_usvdot_idx_4b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sme2_svdot_idx_4h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_uvdot_idx_4h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sme2_svdot_idx_2h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_uvdot_idx_2h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 338637decd..4146744a46 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -438,3 +438,14 @@ USDOT_nx 11000001 0101 .... 1 .. 1 .. ...01 01 ... @azx_4x1_i2_o3 SUDOT_nx 11000001 0101 .... 0 .. 1 .. ....1 11 ... @azx_2x1_i2_o3 SUDOT_nx 11000001 0101 .... 1 .. 1 .. ...01 11 ... @azx_4x1_i2_o3 + +SVDOT_nx_2h 11000001 0101 .... 0 .. 0 .. ....1 00 ... @azx_2x1_i2_o3 +SVDOT_nx_4b 11000001 0101 .... 1 .. 0 .. ...01 00 ... @azx_4x1_i2_o3 +SVDOT_nx_4h 11000001 1101 .... 1 .. 01 . ...00 01 ... @azx_4x1_i1_o3 + +UVDOT_nx_2h 11000001 0101 .... 0 .. 0 .. ....1 10 ... @azx_2x1_i2_o3 +UVDOT_nx_4b 11000001 0101 .... 1 .. 0 .. ...01 10 ... @azx_4x1_i2_o3 +UVDOT_nx_4h 11000001 1101 .... 1 .. 01 . ...00 11 ... @azx_4x1_i1_o3 + +SUVDOT_nx_4b 11000001 0101 .... 1 .. 0 .. ...01 11 ... @azx_4x1_i2_o3 +USVDOT_nx_4b 11000001 0101 .... 1 .. 0 .. ...01 01 ... @azx_4x1_i2_o3 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 8b45865461..f5242d99be 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1416,3 +1416,45 @@ DEF_IMOP_16x2_32(umopa2_s, uint16_t, uint16_t) DEF_IMOPH(sme2, smopa2, s) DEF_IMOPH(sme2, umopa2, s) + +#define DO_VDOT_IDX(NAME, TYPED, TYPEN, TYPEM, HD, HN) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + intptr_t svl = simd_oprsz(desc); \ + intptr_t elements = svl / sizeof(TYPED); \ + intptr_t eltperseg = 16 / sizeof(TYPED); \ + intptr_t nreg = sizeof(TYPED) / sizeof(TYPEN); \ + intptr_t vstride = (svl / nreg) * sizeof(ARMVectorReg); \ + intptr_t zstride = sizeof(ARMVectorReg) / sizeof(TYPEN); \ + intptr_t idx = extract32(desc, SIMD_DATA_SHIFT, 2); \ + TYPEN *n = vn; \ + TYPEM *m = vm; \ + for (intptr_t r = 0; r < nreg; r++) { \ + TYPED *d = vd + r * vstride; \ + for (intptr_t seg = 0; seg < elements; seg += eltperseg) { \ + intptr_t s = seg + idx; \ + for (intptr_t e = seg; e < seg + eltperseg; e++) { \ + TYPED sum = d[HD(e)]; \ + for (intptr_t i = 0; i < nreg; i++) { \ + TYPED nn = n[i * zstride + HN(nreg * e + r)]; \ + TYPED mm = m[HN(nreg * s + i)]; \ + sum += nn * mm; \ + } \ + d[HD(e)] = sum; \ + } \ + } \ + } \ +} + +DO_VDOT_IDX(sme2_svdot_idx_4b, int32_t, int8_t, int8_t, H4, H1) +DO_VDOT_IDX(sme2_uvdot_idx_4b, uint32_t, uint8_t, uint8_t, H4, H1) +DO_VDOT_IDX(sme2_suvdot_idx_4b, int32_t, int8_t, uint8_t, H4, H1) +DO_VDOT_IDX(sme2_usvdot_idx_4b, int32_t, uint8_t, int8_t, H4, H1) + +DO_VDOT_IDX(sme2_svdot_idx_4h, int64_t, int16_t, int16_t, H8, H2) +DO_VDOT_IDX(sme2_uvdot_idx_4h, uint64_t, uint16_t, uint16_t, H8, H2) + +DO_VDOT_IDX(sme2_svdot_idx_2h, int32_t, int16_t, int16_t, H4, H2) +DO_VDOT_IDX(sme2_uvdot_idx_2h, uint32_t, uint16_t, uint16_t, H4, H2) + +#undef DO_VDOT_IDX diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 341f4495e9..b88f439ef9 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1047,3 +1047,26 @@ TRANS_FEAT(SDOT_nx_4b, aa64_sme2, do_dot_nx, a, gen_helper_gvec_sdot_idx_4b) TRANS_FEAT(UDOT_nx_4b, aa64_sme2, do_dot_nx, a, gen_helper_gvec_udot_idx_4b) TRANS_FEAT(SDOT_nx_4h, aa64_sme2_i16i64, do_dot_nx, a, gen_helper_gvec_sdot_idx_4h) TRANS_FEAT(UDOT_nx_4h, aa64_sme2_i16i64, do_dot_nx, a, gen_helper_gvec_udot_idx_4h) + +static bool do_vdot_nx(DisasContext *s, arg_azx_n *a, gen_helper_gvec_3 *fn) +{ + if (sme_smza_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + fn(get_zarray(s, a->rv, a->off, a->n, 0), + vec_full_reg_ptr(s, a->zn), + vec_full_reg_ptr(s, a->zm), + tcg_constant_i32(simd_desc(svl, svl, a->idx))); + } + return true; +} + +TRANS_FEAT(SVDOT_nx_2h, aa64_sme2, do_vdot_nx, a, gen_helper_sme2_svdot_idx_2h) +TRANS_FEAT(SVDOT_nx_4b, aa64_sme2, do_vdot_nx, a, gen_helper_sme2_svdot_idx_4b) +TRANS_FEAT(SVDOT_nx_4h, aa64_sme2, do_vdot_nx, a, gen_helper_sme2_svdot_idx_4h) + +TRANS_FEAT(UVDOT_nx_2h, aa64_sme2, do_vdot_nx, a, gen_helper_sme2_uvdot_idx_2h) +TRANS_FEAT(UVDOT_nx_4b, aa64_sme2, do_vdot_nx, a, gen_helper_sme2_uvdot_idx_4b) +TRANS_FEAT(UVDOT_nx_4h, aa64_sme2, do_vdot_nx, a, gen_helper_sme2_uvdot_idx_4h) + +TRANS_FEAT(SUVDOT_nx_4b, aa64_sme2, do_vdot_nx, a, gen_helper_sme2_suvdot_idx_4b) +TRANS_FEAT(USVDOT_nx_4b, aa64_sme2, do_vdot_nx, a, gen_helper_sme2_usvdot_idx_4b) From 0a151896ebba73b1fa75bbfdbf25a2f32e09fa19 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:09 -0600 Subject: [PATCH 1993/2760] target/arm: Implement SME2 SMLAL, SMLSL, UMLAL, UMLSL Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-47-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 21 +++++ target/arm/tcg/sme.decode | 168 +++++++++++++++++++++++++++++++++ target/arm/tcg/sme_helper.c | 59 ++++++++++++ target/arm/tcg/translate-sme.c | 84 +++++++++++++++++ 4 files changed, 332 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 464877516b..0bb8af194b 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -191,3 +191,24 @@ DEF_HELPER_FLAGS_4(sme2_uvdot_idx_4h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme2_svdot_idx_2h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sme2_uvdot_idx_2h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sme2_smlall_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_smlall_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_smlsll_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_smlsll_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_umlall_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_umlall_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_umlsll_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_umlsll_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_usmlall_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sme2_smlall_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_smlall_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_smlsll_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_smlsll_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_umlall_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_umlall_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_umlsll_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_umlsll_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_usmlall_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_5(sme2_sumlall_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 4146744a46..934e4a802e 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -311,6 +311,65 @@ UDOT_n1_4h 11000001 011 1 .... 0 .. 101 ..... 10 ... @azz_nx1_o3 n=4 UDOT_n1_2h 11000001 011 0 .... 0 .. 101 ..... 11 ... @azz_nx1_o3 n=2 UDOT_n1_2h 11000001 011 1 .... 0 .. 101 ..... 11 ... @azz_nx1_o3 n=4 +SMLAL_n1 11000001 011 0 .... 0 .. 011 ..... 00 ... @azz_nx1_o3x2 n=1 +SMLAL_n1 11000001 011 0 .... 0 .. 010 ..... 000 .. @azz_nx1_o2x2 n=2 +SMLAL_n1 11000001 011 1 .... 0 .. 010 ..... 000 .. @azz_nx1_o2x2 n=4 + +SMLSL_n1 11000001 011 0 .... 0 .. 011 ..... 01 ... @azz_nx1_o3x2 n=1 +SMLSL_n1 11000001 011 0 .... 0 .. 010 ..... 010 .. @azz_nx1_o2x2 n=2 +SMLSL_n1 11000001 011 1 .... 0 .. 010 ..... 010 .. @azz_nx1_o2x2 n=4 + +UMLAL_n1 11000001 011 0 .... 0 .. 011 ..... 10 ... @azz_nx1_o3x2 n=1 +UMLAL_n1 11000001 011 0 .... 0 .. 010 ..... 100 .. @azz_nx1_o2x2 n=2 +UMLAL_n1 11000001 011 1 .... 0 .. 010 ..... 100 .. @azz_nx1_o2x2 n=4 + +UMLSL_n1 11000001 011 0 .... 0 .. 011 ..... 11 ... @azz_nx1_o3x2 n=1 +UMLSL_n1 11000001 011 0 .... 0 .. 010 ..... 110 .. @azz_nx1_o2x2 n=2 +UMLSL_n1 11000001 011 1 .... 0 .. 010 ..... 110 .. @azz_nx1_o2x2 n=4 + +%off2_x4 0:2 !function=times_4 +%off1_x4 0:1 !function=times_4 + +@azz_nx1_o2x4 ........ ... . zm:4 . .. ... zn:5 ... .. \ + &azz_n off=%off2_x4 rv=%mova_rv +@azz_nx1_o1x4 ........ ... . zm:4 . .. ... zn:5 .... . \ + &azz_n off=%off1_x4 rv=%mova_rv + +SMLALL_n1_s 11000001 001 0 .... 0 .. 001 ..... 000 .. @azz_nx1_o2x4 n=1 +SMLALL_n1_d 11000001 011 0 .... 0 .. 001 ..... 000 .. @azz_nx1_o2x4 n=1 +SMLALL_n1_s 11000001 001 0 .... 0 .. 000 ..... 0000 . @azz_nx1_o1x4 n=2 +SMLALL_n1_d 11000001 011 0 .... 0 .. 000 ..... 0000 . @azz_nx1_o1x4 n=2 +SMLALL_n1_s 11000001 001 1 .... 0 .. 000 ..... 0000 . @azz_nx1_o1x4 n=4 +SMLALL_n1_d 11000001 011 1 .... 0 .. 000 ..... 0000 . @azz_nx1_o1x4 n=4 + +SMLSLL_n1_s 11000001 001 0 .... 0 .. 001 ..... 010 .. @azz_nx1_o2x4 n=1 +SMLSLL_n1_d 11000001 011 0 .... 0 .. 001 ..... 010 .. @azz_nx1_o2x4 n=1 +SMLSLL_n1_s 11000001 001 0 .... 0 .. 000 ..... 0100 . @azz_nx1_o1x4 n=2 +SMLSLL_n1_d 11000001 011 0 .... 0 .. 000 ..... 0100 . @azz_nx1_o1x4 n=2 +SMLSLL_n1_s 11000001 001 1 .... 0 .. 000 ..... 0100 . @azz_nx1_o1x4 n=4 +SMLSLL_n1_d 11000001 011 1 .... 0 .. 000 ..... 0100 . @azz_nx1_o1x4 n=4 + +UMLALL_n1_s 11000001 001 0 .... 0 .. 001 ..... 100 .. @azz_nx1_o2x4 n=1 +UMLALL_n1_d 11000001 011 0 .... 0 .. 001 ..... 100 .. @azz_nx1_o2x4 n=1 +UMLALL_n1_s 11000001 001 0 .... 0 .. 000 ..... 1000 . @azz_nx1_o1x4 n=2 +UMLALL_n1_d 11000001 011 0 .... 0 .. 000 ..... 1000 . @azz_nx1_o1x4 n=2 +UMLALL_n1_s 11000001 001 1 .... 0 .. 000 ..... 1000 . @azz_nx1_o1x4 n=4 +UMLALL_n1_d 11000001 011 1 .... 0 .. 000 ..... 1000 . @azz_nx1_o1x4 n=4 + +UMLSLL_n1_s 11000001 001 0 .... 0 .. 001 ..... 110 .. @azz_nx1_o2x4 n=1 +UMLSLL_n1_d 11000001 011 0 .... 0 .. 001 ..... 110 .. @azz_nx1_o2x4 n=1 +UMLSLL_n1_s 11000001 001 0 .... 0 .. 000 ..... 1100 . @azz_nx1_o1x4 n=2 +UMLSLL_n1_d 11000001 011 0 .... 0 .. 000 ..... 1100 . @azz_nx1_o1x4 n=2 +UMLSLL_n1_s 11000001 001 1 .... 0 .. 000 ..... 1100 . @azz_nx1_o1x4 n=4 +UMLSLL_n1_d 11000001 011 1 .... 0 .. 000 ..... 1100 . @azz_nx1_o1x4 n=4 + +USMLALL_n1_s 11000001 001 0 .... 0 .. 001 ..... 001 .. @azz_nx1_o2x4 n=1 +USMLALL_n1_s 11000001 001 0 .... 0 .. 000 ..... 0010 . @azz_nx1_o1x4 n=2 +USMLALL_n1_s 11000001 001 1 .... 0 .. 000 ..... 0010 . @azz_nx1_o1x4 n=4 + +SUMLALL_n1_s 11000001 001 0 .... 0 .. 000 ..... 1010 . @azz_nx1_o1x4 n=2 +SUMLALL_n1_s 11000001 001 1 .... 0 .. 000 ..... 1010 . @azz_nx1_o1x4 n=4 + ### SME2 Multi-vector Multiple Array Vectors %zn_ax2 6:4 !function=times_2 @@ -371,6 +430,46 @@ UDOT_nn_4h 11000001 111 ...01 0 .. 101 ...00 10 ... @azz_4x4_o3 UDOT_nn_2h 11000001 111 ....0 0 .. 101 ....0 11 ... @azz_2x2_o3 UDOT_nn_2h 11000001 111 ...01 0 .. 101 ...00 11 ... @azz_4x4_o3 +SMLAL_nn 11000001 111 ....0 0 .. 010 ....0 000 .. @azz_2x2_o2x2 +SMLAL_nn 11000001 111 ...01 0 .. 010 ...00 000 .. @azz_4x4_o2x2 + +SMLSL_nn 11000001 111 ....0 0 .. 010 ....0 010 .. @azz_2x2_o2x2 +SMLSL_nn 11000001 111 ...01 0 .. 010 ...00 010 .. @azz_4x4_o2x2 + +UMLAL_nn 11000001 111 ....0 0 .. 010 ....0 100 .. @azz_2x2_o2x2 +UMLAL_nn 11000001 111 ...01 0 .. 010 ...00 100 .. @azz_4x4_o2x2 + +UMLSL_nn 11000001 111 ....0 0 .. 010 ....0 110 .. @azz_2x2_o2x2 +UMLSL_nn 11000001 111 ...01 0 .. 010 ...00 110 .. @azz_4x4_o2x2 + +@azz_2x2_o1x4 ........ ... ..... . .. ... ..... ... .. \ + &azz_n n=2 rv=%mova_rv zn=%zn_ax2 zm=%zm_ax2 off=%off1_x4 +@azz_4x4_o1x4 ........ ... ..... . .. ... ..... ... .. \ + &azz_n n=4 rv=%mova_rv zn=%zn_ax4 zm=%zm_ax4 off=%off1_x4 + +SMLALL_nn_s 11000001 101 ....0 0 .. 000 ....0 0000 . @azz_2x2_o1x4 +SMLALL_nn_d 11000001 111 ....0 0 .. 000 ....0 0000 . @azz_2x2_o1x4 +SMLALL_nn_s 11000001 101 ...01 0 .. 000 ...00 0000 . @azz_4x4_o1x4 +SMLALL_nn_d 11000001 111 ...01 0 .. 000 ...00 0000 . @azz_4x4_o1x4 + +SMLSLL_nn_s 11000001 101 ....0 0 .. 000 ....0 0100 . @azz_2x2_o1x4 +SMLSLL_nn_d 11000001 111 ....0 0 .. 000 ....0 0100 . @azz_2x2_o1x4 +SMLSLL_nn_s 11000001 101 ...01 0 .. 000 ...00 0100 . @azz_4x4_o1x4 +SMLSLL_nn_d 11000001 111 ...01 0 .. 000 ...00 0100 . @azz_4x4_o1x4 + +UMLALL_nn_s 11000001 101 ....0 0 .. 000 ....0 1000 . @azz_2x2_o1x4 +UMLALL_nn_d 11000001 111 ....0 0 .. 000 ....0 1000 . @azz_2x2_o1x4 +UMLALL_nn_s 11000001 101 ...01 0 .. 000 ...00 1000 . @azz_4x4_o1x4 +UMLALL_nn_d 11000001 111 ...01 0 .. 000 ...00 1000 . @azz_4x4_o1x4 + +UMLSLL_nn_s 11000001 101 ....0 0 .. 000 ....0 1100 . @azz_2x2_o1x4 +UMLSLL_nn_d 11000001 111 ....0 0 .. 000 ....0 1100 . @azz_2x2_o1x4 +UMLSLL_nn_s 11000001 101 ...01 0 .. 000 ...00 1100 . @azz_4x4_o1x4 +UMLSLL_nn_d 11000001 111 ...01 0 .. 000 ...00 1100 . @azz_4x4_o1x4 + +USMLALL_nn_s 11000001 101 ....0 0 .. 000 ....0 0010 . @azz_2x2_o1x4 +USMLALL_nn_s 11000001 101 ...01 0 .. 000 ...00 0010 . @azz_4x4_o1x4 + ### SME2 Multi-vector Indexed &azx_n n off rv zn zm idx @@ -449,3 +548,72 @@ UVDOT_nx_4h 11000001 1101 .... 1 .. 01 . ...00 11 ... @azx_4x1_i1_o3 SUVDOT_nx_4b 11000001 0101 .... 1 .. 0 .. ...01 11 ... @azx_4x1_i2_o3 USVDOT_nx_4b 11000001 0101 .... 1 .. 0 .. ...01 01 ... @azx_4x1_i2_o3 + +SMLAL_nx 11000001 1100 .... . .. 1 .. ..... 00 ... @azx_1x1_o3x2 +SMLAL_nx 11000001 1101 .... 0 .. 1 .. ....0 00 ... @azx_2x1_o2x2 +SMLAL_nx 11000001 1101 .... 1 .. 1 .. ...00 00 ... @azx_4x1_o2x2 + +SMLSL_nx 11000001 1100 .... . .. 1 .. ..... 01 ... @azx_1x1_o3x2 +SMLSL_nx 11000001 1101 .... 0 .. 1 .. ....0 01 ... @azx_2x1_o2x2 +SMLSL_nx 11000001 1101 .... 1 .. 1 .. ...00 01 ... @azx_4x1_o2x2 + +UMLAL_nx 11000001 1100 .... . .. 1 .. ..... 10 ... @azx_1x1_o3x2 +UMLAL_nx 11000001 1101 .... 0 .. 1 .. ....0 10 ... @azx_2x1_o2x2 +UMLAL_nx 11000001 1101 .... 1 .. 1 .. ...00 10 ... @azx_4x1_o2x2 + +UMLSL_nx 11000001 1100 .... . .. 1 .. ..... 11 ... @azx_1x1_o3x2 +UMLSL_nx 11000001 1101 .... 0 .. 1 .. ....0 11 ... @azx_2x1_o2x2 +UMLSL_nx 11000001 1101 .... 1 .. 1 .. ...00 11 ... @azx_4x1_o2x2 + +%idx4_15_10 15:1 10:3 +%idx4_10_1 10:2 1:2 +%idx3_10_1 10:1 1:2 + +@azx_1x1_i4_o2 ........ .... zm:4 . .. ... zn:5 ... .. \ + &azx_n n=1 rv=%mova_rv off=%off2_x4 idx=%idx4_15_10 +@azx_1x1_i3_o2 ........ .... zm:4 . .. ... zn:5 ... .. \ + &azx_n n=1 rv=%mova_rv off=%off2_x4 idx=%idx3_15_10 +@azx_2x1_i4_o1 ........ .... zm:4 . .. ... ..... ... .. \ + &azx_n n=2 rv=%mova_rv off=%off1_x4 zn=%zn_ax2 idx=%idx4_10_1 +@azx_2x1_i3_o1 ........ .... zm:4 . .. ... ..... ... .. \ + &azx_n n=2 rv=%mova_rv off=%off1_x4 zn=%zn_ax2 idx=%idx3_10_1 +@azx_4x1_i4_o1 ........ .... zm:4 . .. ... ..... ... .. \ + &azx_n n=4 rv=%mova_rv off=%off1_x4 zn=%zn_ax4 idx=%idx4_10_1 +@azx_4x1_i3_o1 ........ .... zm:4 . .. ... ..... ... .. \ + &azx_n n=4 rv=%mova_rv off=%off1_x4 zn=%zn_ax4 idx=%idx3_10_1 + +SMLALL_nx_s 11000001 0000 .... . .. ... ..... 000 .. @azx_1x1_i4_o2 +SMLALL_nx_d 11000001 1000 .... . .. 0.. ..... 000 .. @azx_1x1_i3_o2 +SMLALL_nx_s 11000001 0001 .... 0 .. 0.. ....0 00 ... @azx_2x1_i4_o1 +SMLALL_nx_d 11000001 1001 .... 0 .. 00. ....0 00 ... @azx_2x1_i3_o1 +SMLALL_nx_s 11000001 0001 .... 1 .. 0.. ...00 00 ... @azx_4x1_i4_o1 +SMLALL_nx_d 11000001 1001 .... 1 .. 00. ...00 00 ... @azx_4x1_i3_o1 + +SMLSLL_nx_s 11000001 0000 .... . .. ... ..... 010 .. @azx_1x1_i4_o2 +SMLSLL_nx_d 11000001 1000 .... . .. 0.. ..... 010 .. @azx_1x1_i3_o2 +SMLSLL_nx_s 11000001 0001 .... 0 .. 0.. ....0 01 ... @azx_2x1_i4_o1 +SMLSLL_nx_d 11000001 1001 .... 0 .. 00. ....0 01 ... @azx_2x1_i3_o1 +SMLSLL_nx_s 11000001 0001 .... 1 .. 0.. ...00 01 ... @azx_4x1_i4_o1 +SMLSLL_nx_d 11000001 1001 .... 1 .. 00. ...00 01 ... @azx_4x1_i3_o1 + +UMLALL_nx_s 11000001 0000 .... . .. ... ..... 100 .. @azx_1x1_i4_o2 +UMLALL_nx_d 11000001 1000 .... . .. 0.. ..... 100 .. @azx_1x1_i3_o2 +UMLALL_nx_s 11000001 0001 .... 0 .. 0.. ....0 10 ... @azx_2x1_i4_o1 +UMLALL_nx_d 11000001 1001 .... 0 .. 00. ....0 10 ... @azx_2x1_i3_o1 +UMLALL_nx_s 11000001 0001 .... 1 .. 0.. ...00 10 ... @azx_4x1_i4_o1 +UMLALL_nx_d 11000001 1001 .... 1 .. 00. ...00 10 ... @azx_4x1_i3_o1 + +UMLSLL_nx_s 11000001 0000 .... . .. ... ..... 110 .. @azx_1x1_i4_o2 +UMLSLL_nx_d 11000001 1000 .... . .. 0.. ..... 110 .. @azx_1x1_i3_o2 +UMLSLL_nx_s 11000001 0001 .... 0 .. 0.. ....0 11 ... @azx_2x1_i4_o1 +UMLSLL_nx_d 11000001 1001 .... 0 .. 00. ....0 11 ... @azx_2x1_i3_o1 +UMLSLL_nx_s 11000001 0001 .... 1 .. 0.. ...00 11 ... @azx_4x1_i4_o1 +UMLSLL_nx_d 11000001 1001 .... 1 .. 00. ...00 11 ... @azx_4x1_i3_o1 + +USMLALL_nx_s 11000001 0000 .... . .. ... ..... 001 .. @azx_1x1_i4_o2 +USMLALL_nx_s 11000001 0001 .... 0 .. 0.. ....1 00 ... @azx_2x1_i4_o1 +USMLALL_nx_s 11000001 0001 .... 1 .. 0.. ...01 00 ... @azx_4x1_i4_o1 + +SUMLALL_nx_s 11000001 0000 .... . .. ... ..... 101 .. @azx_1x1_i4_o2 +SUMLALL_nx_s 11000001 0001 .... 0 .. 0.. ....1 10 ... @azx_2x1_i4_o1 +SUMLALL_nx_s 11000001 0001 .... 1 .. 0.. ...01 10 ... @azx_4x1_i4_o1 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index f5242d99be..0f79d7cb6e 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1458,3 +1458,62 @@ DO_VDOT_IDX(sme2_svdot_idx_2h, int32_t, int16_t, int16_t, H4, H2) DO_VDOT_IDX(sme2_uvdot_idx_2h, uint32_t, uint16_t, uint16_t, H4, H2) #undef DO_VDOT_IDX + +#define DO_MLALL(NAME, TYPEW, TYPEN, TYPEM, HW, HN, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ +{ \ + intptr_t elements = simd_oprsz(desc) / sizeof(TYPEW); \ + intptr_t sel = extract32(desc, SIMD_DATA_SHIFT, 2); \ + TYPEW *d = vd, *a = va; TYPEN *n = vn; TYPEM *m = vm; \ + for (intptr_t i = 0; i < elements; ++i) { \ + TYPEW nn = n[HN(i * 4 + sel)]; \ + TYPEM mm = m[HN(i * 4 + sel)]; \ + d[HW(i)] = a[HW(i)] OP (nn * mm); \ + } \ +} + +DO_MLALL(sme2_smlall_s, int32_t, int8_t, int8_t, H4, H1, +) +DO_MLALL(sme2_smlall_d, int64_t, int16_t, int16_t, H8, H2, +) +DO_MLALL(sme2_smlsll_s, int32_t, int8_t, int8_t, H4, H1, -) +DO_MLALL(sme2_smlsll_d, int64_t, int16_t, int16_t, H8, H2, -) + +DO_MLALL(sme2_umlall_s, uint32_t, uint8_t, uint8_t, H4, H1, +) +DO_MLALL(sme2_umlall_d, uint64_t, uint16_t, uint16_t, H8, H2, +) +DO_MLALL(sme2_umlsll_s, uint32_t, uint8_t, uint8_t, H4, H1, -) +DO_MLALL(sme2_umlsll_d, uint64_t, uint16_t, uint16_t, H8, H2, -) + +DO_MLALL(sme2_usmlall_s, uint32_t, uint8_t, int8_t, H4, H1, +) + +#undef DO_MLALL + +#define DO_MLALL_IDX(NAME, TYPEW, TYPEN, TYPEM, HW, HN, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, uint32_t desc) \ +{ \ + intptr_t elements = simd_oprsz(desc) / sizeof(TYPEW); \ + intptr_t eltspersegment = 16 / sizeof(TYPEW); \ + intptr_t sel = extract32(desc, SIMD_DATA_SHIFT, 2); \ + intptr_t idx = extract32(desc, SIMD_DATA_SHIFT + 2, 4); \ + TYPEW *d = vd, *a = va; TYPEN *n = vn; TYPEM *m = vm; \ + for (intptr_t i = 0; i < elements; i += eltspersegment) { \ + TYPEW mm = m[HN(i * 4 + idx)]; \ + for (intptr_t j = 0; j < eltspersegment; ++j) { \ + TYPEN nn = n[HN((i + j) * 4 + sel)]; \ + d[HW(i + j)] = a[HW(i + j)] OP (nn * mm); \ + } \ + } \ +} + +DO_MLALL_IDX(sme2_smlall_idx_s, int32_t, int8_t, int8_t, H4, H1, +) +DO_MLALL_IDX(sme2_smlall_idx_d, int64_t, int16_t, int16_t, H8, H2, +) +DO_MLALL_IDX(sme2_smlsll_idx_s, int32_t, int8_t, int8_t, H4, H1, -) +DO_MLALL_IDX(sme2_smlsll_idx_d, int64_t, int16_t, int16_t, H8, H2, -) + +DO_MLALL_IDX(sme2_umlall_idx_s, uint32_t, uint8_t, uint8_t, H4, H1, +) +DO_MLALL_IDX(sme2_umlall_idx_d, uint64_t, uint16_t, uint16_t, H8, H2, +) +DO_MLALL_IDX(sme2_umlsll_idx_s, uint32_t, uint8_t, uint8_t, H4, H1, -) +DO_MLALL_IDX(sme2_umlsll_idx_d, uint64_t, uint16_t, uint16_t, H8, H2, -) + +DO_MLALL_IDX(sme2_usmlall_idx_s, uint32_t, uint8_t, int8_t, H4, H1, +) +DO_MLALL_IDX(sme2_sumlall_idx_s, uint32_t, int8_t, uint8_t, H4, H1, +) + +#undef DO_MLALL_IDX diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index b88f439ef9..070cebc573 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1070,3 +1070,87 @@ TRANS_FEAT(UVDOT_nx_4h, aa64_sme2, do_vdot_nx, a, gen_helper_sme2_uvdot_idx_4h) TRANS_FEAT(SUVDOT_nx_4b, aa64_sme2, do_vdot_nx, a, gen_helper_sme2_suvdot_idx_4b) TRANS_FEAT(USVDOT_nx_4b, aa64_sme2, do_vdot_nx, a, gen_helper_sme2_usvdot_idx_4b) + +static bool do_smlal(DisasContext *s, arg_azz_n *a, bool multi, + gen_helper_gvec_4 *fn) +{ + return do_azz_acc(s, a->n, 2, a->rv, a->off, a->zn, a->zm, + 0, 0, multi, fn); +} + +TRANS_FEAT(SMLAL_n1, aa64_sme2, do_smlal, a, false, gen_helper_sve2_smlal_zzzw_s) +TRANS_FEAT(SMLSL_n1, aa64_sme2, do_smlal, a, false, gen_helper_sve2_smlsl_zzzw_s) +TRANS_FEAT(UMLAL_n1, aa64_sme2, do_smlal, a, false, gen_helper_sve2_umlal_zzzw_s) +TRANS_FEAT(UMLSL_n1, aa64_sme2, do_smlal, a, false, gen_helper_sve2_umlsl_zzzw_s) + +TRANS_FEAT(SMLAL_nn, aa64_sme2, do_smlal, a, true, gen_helper_sve2_smlal_zzzw_s) +TRANS_FEAT(SMLSL_nn, aa64_sme2, do_smlal, a, true, gen_helper_sve2_smlsl_zzzw_s) +TRANS_FEAT(UMLAL_nn, aa64_sme2, do_smlal, a, true, gen_helper_sve2_umlal_zzzw_s) +TRANS_FEAT(UMLSL_nn, aa64_sme2, do_smlal, a, true, gen_helper_sve2_umlsl_zzzw_s) + +static bool do_smlal_nx(DisasContext *s, arg_azx_n *a, + gen_helper_gvec_4 *fn) +{ + return do_azz_acc(s, a->n, 2, a->rv, a->off, a->zn, a->zm, + a->idx << 1, 0, false, fn); +} + +TRANS_FEAT(SMLAL_nx, aa64_sme2, do_smlal_nx, a, gen_helper_sve2_smlal_idx_s) +TRANS_FEAT(SMLSL_nx, aa64_sme2, do_smlal_nx, a, gen_helper_sve2_smlsl_idx_s) +TRANS_FEAT(UMLAL_nx, aa64_sme2, do_smlal_nx, a, gen_helper_sve2_umlal_idx_s) +TRANS_FEAT(UMLSL_nx, aa64_sme2, do_smlal_nx, a, gen_helper_sve2_umlsl_idx_s) + +static bool do_smlall(DisasContext *s, arg_azz_n *a, bool multi, + gen_helper_gvec_4 *fn) +{ + return do_azz_acc(s, a->n, 4, a->rv, a->off, a->zn, a->zm, + 0, 0, multi, fn); +} + +static void gen_helper_sme2_sumlall_s(TCGv_ptr d, TCGv_ptr n, TCGv_ptr m, + TCGv_ptr a, TCGv_i32 desc) +{ + gen_helper_sme2_usmlall_s(d, m, n, a, desc); +} + +TRANS_FEAT(SMLALL_n1_s, aa64_sme2, do_smlall, a, false, gen_helper_sme2_smlall_s) +TRANS_FEAT(SMLSLL_n1_s, aa64_sme2, do_smlall, a, false, gen_helper_sme2_smlsll_s) +TRANS_FEAT(UMLALL_n1_s, aa64_sme2, do_smlall, a, false, gen_helper_sme2_umlall_s) +TRANS_FEAT(UMLSLL_n1_s, aa64_sme2, do_smlall, a, false, gen_helper_sme2_umlsll_s) +TRANS_FEAT(USMLALL_n1_s, aa64_sme2, do_smlall, a, false, gen_helper_sme2_usmlall_s) +TRANS_FEAT(SUMLALL_n1_s, aa64_sme2, do_smlall, a, false, gen_helper_sme2_sumlall_s) + +TRANS_FEAT(SMLALL_n1_d, aa64_sme2_i16i64, do_smlall, a, false, gen_helper_sme2_smlall_d) +TRANS_FEAT(SMLSLL_n1_d, aa64_sme2_i16i64, do_smlall, a, false, gen_helper_sme2_smlsll_d) +TRANS_FEAT(UMLALL_n1_d, aa64_sme2_i16i64, do_smlall, a, false, gen_helper_sme2_umlall_d) +TRANS_FEAT(UMLSLL_n1_d, aa64_sme2_i16i64, do_smlall, a, false, gen_helper_sme2_umlsll_d) + +TRANS_FEAT(SMLALL_nn_s, aa64_sme2, do_smlall, a, true, gen_helper_sme2_smlall_s) +TRANS_FEAT(SMLSLL_nn_s, aa64_sme2, do_smlall, a, true, gen_helper_sme2_smlsll_s) +TRANS_FEAT(UMLALL_nn_s, aa64_sme2, do_smlall, a, true, gen_helper_sme2_umlall_s) +TRANS_FEAT(UMLSLL_nn_s, aa64_sme2, do_smlall, a, true, gen_helper_sme2_umlsll_s) +TRANS_FEAT(USMLALL_nn_s, aa64_sme2, do_smlall, a, true, gen_helper_sme2_usmlall_s) + +TRANS_FEAT(SMLALL_nn_d, aa64_sme2_i16i64, do_smlall, a, true, gen_helper_sme2_smlall_d) +TRANS_FEAT(SMLSLL_nn_d, aa64_sme2_i16i64, do_smlall, a, true, gen_helper_sme2_smlsll_d) +TRANS_FEAT(UMLALL_nn_d, aa64_sme2_i16i64, do_smlall, a, true, gen_helper_sme2_umlall_d) +TRANS_FEAT(UMLSLL_nn_d, aa64_sme2_i16i64, do_smlall, a, true, gen_helper_sme2_umlsll_d) + +static bool do_smlall_nx(DisasContext *s, arg_azx_n *a, + gen_helper_gvec_4 *fn) +{ + return do_azz_acc(s, a->n, 4, a->rv, a->off, a->zn, a->zm, + a->idx << 2, 0, false, fn); +} + +TRANS_FEAT(SMLALL_nx_s, aa64_sme2, do_smlall_nx, a, gen_helper_sme2_smlall_idx_s) +TRANS_FEAT(SMLSLL_nx_s, aa64_sme2, do_smlall_nx, a, gen_helper_sme2_smlsll_idx_s) +TRANS_FEAT(UMLALL_nx_s, aa64_sme2, do_smlall_nx, a, gen_helper_sme2_umlall_idx_s) +TRANS_FEAT(UMLSLL_nx_s, aa64_sme2, do_smlall_nx, a, gen_helper_sme2_umlsll_idx_s) +TRANS_FEAT(USMLALL_nx_s, aa64_sme2, do_smlall_nx, a, gen_helper_sme2_usmlall_idx_s) +TRANS_FEAT(SUMLALL_nx_s, aa64_sme2, do_smlall_nx, a, gen_helper_sme2_sumlall_idx_s) + +TRANS_FEAT(SMLALL_nx_d, aa64_sme2_i16i64, do_smlall_nx, a, gen_helper_sme2_smlall_idx_d) +TRANS_FEAT(SMLSLL_nx_d, aa64_sme2_i16i64, do_smlall_nx, a, gen_helper_sme2_smlsll_idx_d) +TRANS_FEAT(UMLALL_nx_d, aa64_sme2_i16i64, do_smlall_nx, a, gen_helper_sme2_umlall_idx_d) +TRANS_FEAT(UMLSLL_nx_d, aa64_sme2_i16i64, do_smlall_nx, a, gen_helper_sme2_umlsll_idx_d) From e0646fc93e40d32462ee2b87be187e088dba8593 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:10 -0600 Subject: [PATCH 1994/2760] target/arm: Rename gvec_fml[as]_[hs] with _nf_ infix Emphasize the non-fused nature of these multiply-add. Matches other helpers such as gvec_rsqrts_nf_[hs]. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-48-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper.h | 8 ++++---- target/arm/tcg/translate-neon.c | 4 ++-- target/arm/tcg/vec_helper.c | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index c4a208e3ba..b741470b51 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -790,11 +790,11 @@ DEF_HELPER_FLAGS_5(gvec_recps_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_rsqrts_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmla_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmla_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmla_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmla_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmls_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_5(gvec_fmls_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmls_nf_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmls_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_vfma_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_vfma_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/translate-neon.c b/target/arm/tcg/translate-neon.c index ea04336797..844d2e29e4 100644 --- a/target/arm/tcg/translate-neon.c +++ b/target/arm/tcg/translate-neon.c @@ -1010,8 +1010,8 @@ DO_3S_FP_GVEC(VACGE, gen_helper_gvec_facge_s, gen_helper_gvec_facge_h) DO_3S_FP_GVEC(VACGT, gen_helper_gvec_facgt_s, gen_helper_gvec_facgt_h) DO_3S_FP_GVEC(VMAX, gen_helper_gvec_fmax_s, gen_helper_gvec_fmax_h) DO_3S_FP_GVEC(VMIN, gen_helper_gvec_fmin_s, gen_helper_gvec_fmin_h) -DO_3S_FP_GVEC(VMLA, gen_helper_gvec_fmla_s, gen_helper_gvec_fmla_h) -DO_3S_FP_GVEC(VMLS, gen_helper_gvec_fmls_s, gen_helper_gvec_fmls_h) +DO_3S_FP_GVEC(VMLA, gen_helper_gvec_fmla_nf_s, gen_helper_gvec_fmla_nf_h) +DO_3S_FP_GVEC(VMLS, gen_helper_gvec_fmls_nf_s, gen_helper_gvec_fmls_nf_h) DO_3S_FP_GVEC(VFMA, gen_helper_gvec_vfma_s, gen_helper_gvec_vfma_h) DO_3S_FP_GVEC(VFMS, gen_helper_gvec_vfms_s, gen_helper_gvec_vfms_h) DO_3S_FP_GVEC(VRECPS, gen_helper_gvec_recps_nf_s, gen_helper_gvec_recps_nf_h) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index baea6d8b89..4b7de5016e 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1668,11 +1668,11 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, \ clear_tail(d, oprsz, simd_maxsz(desc)); \ } -DO_MULADD(gvec_fmla_h, float16_muladd_nf, float16) -DO_MULADD(gvec_fmla_s, float32_muladd_nf, float32) +DO_MULADD(gvec_fmla_nf_h, float16_muladd_nf, float16) +DO_MULADD(gvec_fmla_nf_s, float32_muladd_nf, float32) -DO_MULADD(gvec_fmls_h, float16_mulsub_nf, float16) -DO_MULADD(gvec_fmls_s, float32_mulsub_nf, float32) +DO_MULADD(gvec_fmls_nf_h, float16_mulsub_nf, float16) +DO_MULADD(gvec_fmls_nf_s, float32_mulsub_nf, float32) DO_MULADD(gvec_vfma_h, float16_muladd_f, float16) DO_MULADD(gvec_vfma_s, float32_muladd_f, float32) From 77a15fa72a0e9b0d5d78da0a482c57edcac67216 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:11 -0600 Subject: [PATCH 1995/2760] target/arm: Implement SME2 FMLA, FMLS Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-49-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 48 +++++++++++++++++ target/arm/tcg/translate-sme.c | 95 ++++++++++++++++++++++++++++++++++ 2 files changed, 143 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 934e4a802e..962ffae3d6 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -370,6 +370,20 @@ USMLALL_n1_s 11000001 001 1 .... 0 .. 000 ..... 0010 . @azz_nx1_o1x4 n=4 SUMLALL_n1_s 11000001 001 0 .... 0 .. 000 ..... 1010 . @azz_nx1_o1x4 n=2 SUMLALL_n1_s 11000001 001 1 .... 0 .. 000 ..... 1010 . @azz_nx1_o1x4 n=4 +FMLA_n1_h 11000001 001 0 .... 0 .. 111 ..... 00 ... @azz_nx1_o3 n=2 +FMLA_n1_s 11000001 001 0 .... 0 .. 110 ..... 00 ... @azz_nx1_o3 n=2 +FMLA_n1_d 11000001 011 0 .... 0 .. 110 ..... 00 ... @azz_nx1_o3 n=2 +FMLA_n1_h 11000001 001 1 .... 0 .. 111 ..... 00 ... @azz_nx1_o3 n=4 +FMLA_n1_s 11000001 001 1 .... 0 .. 110 ..... 00 ... @azz_nx1_o3 n=4 +FMLA_n1_d 11000001 011 1 .... 0 .. 110 ..... 00 ... @azz_nx1_o3 n=4 + +FMLS_n1_h 11000001 001 0 .... 0 .. 111 ..... 01 ... @azz_nx1_o3 n=2 +FMLS_n1_s 11000001 001 0 .... 0 .. 110 ..... 01 ... @azz_nx1_o3 n=2 +FMLS_n1_d 11000001 011 0 .... 0 .. 110 ..... 01 ... @azz_nx1_o3 n=2 +FMLS_n1_h 11000001 001 1 .... 0 .. 111 ..... 01 ... @azz_nx1_o3 n=4 +FMLS_n1_s 11000001 001 1 .... 0 .. 110 ..... 01 ... @azz_nx1_o3 n=4 +FMLS_n1_d 11000001 011 1 .... 0 .. 110 ..... 01 ... @azz_nx1_o3 n=4 + ### SME2 Multi-vector Multiple Array Vectors %zn_ax2 6:4 !function=times_2 @@ -470,6 +484,20 @@ UMLSLL_nn_d 11000001 111 ...01 0 .. 000 ...00 1100 . @azz_4x4_o1x4 USMLALL_nn_s 11000001 101 ....0 0 .. 000 ....0 0010 . @azz_2x2_o1x4 USMLALL_nn_s 11000001 101 ...01 0 .. 000 ...00 0010 . @azz_4x4_o1x4 +FMLA_nn_h 11000001 101 ....0 0 .. 100 ....0 01 ... @azz_2x2_o3 +FMLA_nn_s 11000001 101 ....0 0 .. 110 ....0 00 ... @azz_2x2_o3 +FMLA_nn_d 11000001 111 ....0 0 .. 110 ....0 00 ... @azz_2x2_o3 +FMLA_nn_h 11000001 101 ...01 0 .. 100 ...00 01 ... @azz_4x4_o3 +FMLA_nn_s 11000001 101 ...01 0 .. 110 ...00 00 ... @azz_4x4_o3 +FMLA_nn_d 11000001 111 ...01 0 .. 110 ...00 00 ... @azz_4x4_o3 + +FMLS_nn_h 11000001 101 ....0 0 .. 100 ....0 11 ... @azz_2x2_o3 +FMLS_nn_s 11000001 101 ....0 0 .. 110 ....0 01 ... @azz_2x2_o3 +FMLS_nn_d 11000001 111 ....0 0 .. 110 ....0 01 ... @azz_2x2_o3 +FMLS_nn_h 11000001 101 ...01 0 .. 100 ...00 11 ... @azz_4x4_o3 +FMLS_nn_s 11000001 101 ...01 0 .. 110 ...00 01 ... @azz_4x4_o3 +FMLS_nn_d 11000001 111 ...01 0 .. 110 ...00 01 ... @azz_4x4_o3 + ### SME2 Multi-vector Indexed &azx_n n off rv zn zm idx @@ -617,3 +645,23 @@ USMLALL_nx_s 11000001 0001 .... 1 .. 0.. ...01 00 ... @azx_4x1_i4_o1 SUMLALL_nx_s 11000001 0000 .... . .. ... ..... 101 .. @azx_1x1_i4_o2 SUMLALL_nx_s 11000001 0001 .... 0 .. 0.. ....1 10 ... @azx_2x1_i4_o1 SUMLALL_nx_s 11000001 0001 .... 1 .. 0.. ...01 10 ... @azx_4x1_i4_o1 + +%idx3_10_3 10:2 3:1 +@azx_2x1_i3_o3 ........ .... zm:4 . .. ... ..... .. off:3 \ + &azx_n n=2 rv=%mova_rv zn=%zn_ax2 idx=%idx3_10_3 +@azx_4x1_i3_o3 ........ .... zm:4 . .. ... ..... .. off:3 \ + &azx_n n=4 rv=%mova_rv zn=%zn_ax4 idx=%idx3_10_3 + +FMLA_nx_h 11000001 0001 .... 0 .. 1.. ....0 0 .... @azx_2x1_i3_o3 +FMLA_nx_s 11000001 0101 .... 0 .. 0.. ....0 00 ... @azx_2x1_i2_o3 +FMLA_nx_d 11000001 1101 .... 0 .. 00. ....0 00 ... @azx_2x1_i1_o3 +FMLA_nx_h 11000001 0001 .... 1 .. 1.. ...00 0 .... @azx_4x1_i3_o3 +FMLA_nx_s 11000001 0101 .... 1 .. 0.. ...00 00 ... @azx_4x1_i2_o3 +FMLA_nx_d 11000001 1101 .... 1 .. 00. ...00 00 ... @azx_4x1_i1_o3 + +FMLS_nx_h 11000001 0001 .... 0 .. 1.. ....0 1 .... @azx_2x1_i3_o3 +FMLS_nx_s 11000001 0101 .... 0 .. 0.. ....0 10 ... @azx_2x1_i2_o3 +FMLS_nx_d 11000001 1101 .... 0 .. 00. ....0 10 ... @azx_2x1_i1_o3 +FMLS_nx_h 11000001 0001 .... 1 .. 1.. ...00 1 .... @azx_4x1_i3_o3 +FMLS_nx_s 11000001 0101 .... 1 .. 0.. ...00 10 ... @azx_4x1_i2_o3 +FMLS_nx_d 11000001 1101 .... 1 .. 00. ...00 10 ... @azx_4x1_i1_o3 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 070cebc573..5f51369609 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -815,6 +815,47 @@ TRANS_FEAT(SUB_azz_nn_d, aa64_sme2_i16i64, do_azz_nn, a, MO_64, tcg_gen_gvec_sub */ #define FPST_ENV -1 +static bool do_azz_fp(DisasContext *s, int nreg, int nsel, + int rv, int off, int zn, int zm, + int data, int shsel, bool multi, int fpst, + gen_helper_gvec_3_ptr *fn) +{ + if (sme_smza_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + int vstride = svl / nreg; + TCGv_ptr t_za = get_zarray(s, rv, off, nreg, nsel); + TCGv_ptr t, ptr; + + if (fpst >= 0) { + ptr = fpstatus_ptr(fpst); + } else { + ptr = tcg_env; + } + t = tcg_temp_new_ptr(); + + for (int r = 0; r < nreg; ++r) { + TCGv_ptr t_zn = vec_full_reg_ptr(s, zn); + TCGv_ptr t_zm = vec_full_reg_ptr(s, zm); + + for (int i = 0; i < nsel; ++i) { + int o_za = (r * vstride + i) * sizeof(ARMVectorReg); + int desc = simd_desc(svl, svl, data | (i << shsel)); + + tcg_gen_addi_ptr(t, t_za, o_za); + fn(t, t_zn, t_zm, ptr, tcg_constant_i32(desc)); + } + + /* + * For multiple-and-single vectors, Zn may wrap. + * For multiple vectors, both Zn and Zm are aligned. + */ + zn = (zn + 1) % 32; + zm += multi; + } + } + return true; +} + static bool do_azz_acc_fp(DisasContext *s, int nreg, int nsel, int rv, int off, int zn, int zm, int data, int shsel, bool multi, int fpst, @@ -963,6 +1004,60 @@ static bool do_vdot(DisasContext *s, arg_azx_n *a, gen_helper_gvec_4_ptr *fn) TRANS_FEAT(FVDOT, aa64_sme, do_vdot, a, gen_helper_sme2_fvdot_idx_h) TRANS_FEAT(BFVDOT, aa64_sme, do_vdot, a, gen_helper_sme2_bfvdot_idx) +static bool do_fmla(DisasContext *s, arg_azz_n *a, bool multi, + ARMFPStatusFlavour fpst, gen_helper_gvec_3_ptr *fn) +{ + return do_azz_fp(s, a->n, 1, a->rv, a->off, a->zn, a->zm, + 0, 0, multi, fpst, fn); +} + +TRANS_FEAT(FMLA_n1_h, aa64_sme_f16f16, do_fmla, a, false, FPST_ZA_F16, + gen_helper_gvec_vfma_h) +TRANS_FEAT(FMLS_n1_h, aa64_sme_f16f16, do_fmla, a, false, FPST_ZA_F16, + s->fpcr_ah ? gen_helper_gvec_ah_vfms_h : gen_helper_gvec_vfms_h) +TRANS_FEAT(FMLA_nn_h, aa64_sme_f16f16, do_fmla, a, true, FPST_ZA_F16, + gen_helper_gvec_vfma_h) +TRANS_FEAT(FMLS_nn_h, aa64_sme_f16f16, do_fmla, a, true, FPST_ZA_F16, + s->fpcr_ah ? gen_helper_gvec_ah_vfms_h : gen_helper_gvec_vfms_h) + +TRANS_FEAT(FMLA_n1_s, aa64_sme2, do_fmla, a, false, FPST_ZA, + gen_helper_gvec_vfma_s) +TRANS_FEAT(FMLS_n1_s, aa64_sme2, do_fmla, a, false, FPST_ZA, + s->fpcr_ah ? gen_helper_gvec_ah_vfms_s : gen_helper_gvec_vfms_s) +TRANS_FEAT(FMLA_nn_s, aa64_sme2, do_fmla, a, true, FPST_ZA, + gen_helper_gvec_vfma_s) +TRANS_FEAT(FMLS_nn_s, aa64_sme2, do_fmla, a, true, FPST_ZA, + s->fpcr_ah ? gen_helper_gvec_ah_vfms_s : gen_helper_gvec_vfms_s) + +TRANS_FEAT(FMLA_n1_d, aa64_sme2_f64f64, do_fmla, a, false, FPST_ZA, + gen_helper_gvec_vfma_d) +TRANS_FEAT(FMLS_n1_d, aa64_sme2_f64f64, do_fmla, a, false, FPST_ZA, + s->fpcr_ah ? gen_helper_gvec_ah_vfms_d : gen_helper_gvec_vfms_d) +TRANS_FEAT(FMLA_nn_d, aa64_sme2_f64f64, do_fmla, a, true, FPST_ZA, + gen_helper_gvec_vfma_d) +TRANS_FEAT(FMLS_nn_d, aa64_sme2_f64f64, do_fmla, a, true, FPST_ZA, + s->fpcr_ah ? gen_helper_gvec_ah_vfms_d : gen_helper_gvec_vfms_d) + +static bool do_fmla_nx(DisasContext *s, arg_azx_n *a, + ARMFPStatusFlavour fpst, gen_helper_gvec_4_ptr *fn) +{ + return do_azz_acc_fp(s, a->n, 1, a->rv, a->off, a->zn, a->zm, + a->idx, 0, false, fpst, fn); +} + +TRANS_FEAT(FMLA_nx_h, aa64_sme_f16f16, do_fmla_nx, a, FPST_ZA_F16, + gen_helper_gvec_fmla_idx_h) +TRANS_FEAT(FMLS_nx_h, aa64_sme_f16f16, do_fmla_nx, a, FPST_ZA_F16, + s->fpcr_ah ? gen_helper_gvec_ah_fmls_idx_h : gen_helper_gvec_fmls_idx_h) +TRANS_FEAT(FMLA_nx_s, aa64_sme2, do_fmla_nx, a, FPST_ZA, + gen_helper_gvec_fmla_idx_s) +TRANS_FEAT(FMLS_nx_s, aa64_sme2, do_fmla_nx, a, FPST_ZA, + s->fpcr_ah ? gen_helper_gvec_ah_fmls_idx_s : gen_helper_gvec_fmls_idx_s) +TRANS_FEAT(FMLA_nx_d, aa64_sme2_f64f64, do_fmla_nx, a, FPST_ZA, + gen_helper_gvec_fmla_idx_d) +TRANS_FEAT(FMLS_nx_d, aa64_sme2_f64f64, do_fmla_nx, a, FPST_ZA, + s->fpcr_ah ? gen_helper_gvec_ah_fmls_idx_d : gen_helper_gvec_fmls_idx_d) + /* * Expand array multi-vector single (n1), array multi-vector (nn), * and array multi-vector indexed (nx), for integer accumulate. From 57ef00819ef46188f803e4f59382044c75073d34 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:12 -0600 Subject: [PATCH 1996/2760] target/arm: Implement SME2 BFMLA, BFMLS Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-50-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper.h | 9 +++++++++ target/arm/tcg/sme.decode | 18 ++++++++++++++++++ target/arm/tcg/translate-sme.c | 14 ++++++++++++++ target/arm/tcg/vec_helper.c | 26 ++++++++++++++++++++++++++ 4 files changed, 67 insertions(+) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index b741470b51..37dd384659 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -799,14 +799,17 @@ DEF_HELPER_FLAGS_5(gvec_fmls_nf_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i DEF_HELPER_FLAGS_5(gvec_vfma_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_vfma_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_vfma_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_bfmla, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_bfmls, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_ah_vfms_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_ah_vfms_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_ah_vfms_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_ah_bfmls, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_ftsmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) @@ -838,6 +841,8 @@ DEF_HELPER_FLAGS_6(gvec_fmla_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_fmla_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_bfmla_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_fmls_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) @@ -845,6 +850,8 @@ DEF_HELPER_FLAGS_6(gvec_fmls_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_fmls_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_bfmls_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) @@ -852,6 +859,8 @@ DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(gvec_ah_fmls_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(gvec_ah_bfmls_idx, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_uqadd_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 962ffae3d6..b3dd3bffc3 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -370,16 +370,22 @@ USMLALL_n1_s 11000001 001 1 .... 0 .. 000 ..... 0010 . @azz_nx1_o1x4 n=4 SUMLALL_n1_s 11000001 001 0 .... 0 .. 000 ..... 1010 . @azz_nx1_o1x4 n=2 SUMLALL_n1_s 11000001 001 1 .... 0 .. 000 ..... 1010 . @azz_nx1_o1x4 n=4 +BFMLA_n1 11000001 011 0 .... 0 .. 111 ..... 00 ... @azz_nx1_o3 n=2 FMLA_n1_h 11000001 001 0 .... 0 .. 111 ..... 00 ... @azz_nx1_o3 n=2 FMLA_n1_s 11000001 001 0 .... 0 .. 110 ..... 00 ... @azz_nx1_o3 n=2 FMLA_n1_d 11000001 011 0 .... 0 .. 110 ..... 00 ... @azz_nx1_o3 n=2 + +BFMLA_n1 11000001 011 1 .... 0 .. 111 ..... 00 ... @azz_nx1_o3 n=4 FMLA_n1_h 11000001 001 1 .... 0 .. 111 ..... 00 ... @azz_nx1_o3 n=4 FMLA_n1_s 11000001 001 1 .... 0 .. 110 ..... 00 ... @azz_nx1_o3 n=4 FMLA_n1_d 11000001 011 1 .... 0 .. 110 ..... 00 ... @azz_nx1_o3 n=4 +BFMLS_n1 11000001 011 0 .... 0 .. 111 ..... 01 ... @azz_nx1_o3 n=2 FMLS_n1_h 11000001 001 0 .... 0 .. 111 ..... 01 ... @azz_nx1_o3 n=2 FMLS_n1_s 11000001 001 0 .... 0 .. 110 ..... 01 ... @azz_nx1_o3 n=2 FMLS_n1_d 11000001 011 0 .... 0 .. 110 ..... 01 ... @azz_nx1_o3 n=2 + +BFMLS_n1 11000001 011 1 .... 0 .. 111 ..... 01 ... @azz_nx1_o3 n=4 FMLS_n1_h 11000001 001 1 .... 0 .. 111 ..... 01 ... @azz_nx1_o3 n=4 FMLS_n1_s 11000001 001 1 .... 0 .. 110 ..... 01 ... @azz_nx1_o3 n=4 FMLS_n1_d 11000001 011 1 .... 0 .. 110 ..... 01 ... @azz_nx1_o3 n=4 @@ -484,16 +490,22 @@ UMLSLL_nn_d 11000001 111 ...01 0 .. 000 ...00 1100 . @azz_4x4_o1x4 USMLALL_nn_s 11000001 101 ....0 0 .. 000 ....0 0010 . @azz_2x2_o1x4 USMLALL_nn_s 11000001 101 ...01 0 .. 000 ...00 0010 . @azz_4x4_o1x4 +BFMLA_nn 11000001 111 ....0 0 .. 100 ....0 01 ... @azz_2x2_o3 FMLA_nn_h 11000001 101 ....0 0 .. 100 ....0 01 ... @azz_2x2_o3 FMLA_nn_s 11000001 101 ....0 0 .. 110 ....0 00 ... @azz_2x2_o3 FMLA_nn_d 11000001 111 ....0 0 .. 110 ....0 00 ... @azz_2x2_o3 + +BFMLA_nn 11000001 111 ...01 0 .. 100 ...00 01 ... @azz_4x4_o3 FMLA_nn_h 11000001 101 ...01 0 .. 100 ...00 01 ... @azz_4x4_o3 FMLA_nn_s 11000001 101 ...01 0 .. 110 ...00 00 ... @azz_4x4_o3 FMLA_nn_d 11000001 111 ...01 0 .. 110 ...00 00 ... @azz_4x4_o3 +BFMLS_nn 11000001 111 ....0 0 .. 100 ....0 11 ... @azz_2x2_o3 FMLS_nn_h 11000001 101 ....0 0 .. 100 ....0 11 ... @azz_2x2_o3 FMLS_nn_s 11000001 101 ....0 0 .. 110 ....0 01 ... @azz_2x2_o3 FMLS_nn_d 11000001 111 ....0 0 .. 110 ....0 01 ... @azz_2x2_o3 + +BFMLS_nn 11000001 111 ...01 0 .. 100 ...00 11 ... @azz_4x4_o3 FMLS_nn_h 11000001 101 ...01 0 .. 100 ...00 11 ... @azz_4x4_o3 FMLS_nn_s 11000001 101 ...01 0 .. 110 ...00 01 ... @azz_4x4_o3 FMLS_nn_d 11000001 111 ...01 0 .. 110 ...00 01 ... @azz_4x4_o3 @@ -652,16 +664,22 @@ SUMLALL_nx_s 11000001 0001 .... 1 .. 0.. ...01 10 ... @azx_4x1_i4_o1 @azx_4x1_i3_o3 ........ .... zm:4 . .. ... ..... .. off:3 \ &azx_n n=4 rv=%mova_rv zn=%zn_ax4 idx=%idx3_10_3 +BFMLA_nx 11000001 0001 .... 0 .. 1.. ....1 0 .... @azx_2x1_i3_o3 FMLA_nx_h 11000001 0001 .... 0 .. 1.. ....0 0 .... @azx_2x1_i3_o3 FMLA_nx_s 11000001 0101 .... 0 .. 0.. ....0 00 ... @azx_2x1_i2_o3 FMLA_nx_d 11000001 1101 .... 0 .. 00. ....0 00 ... @azx_2x1_i1_o3 + +BFMLA_nx 11000001 0001 .... 1 .. 1.. ...01 0 .... @azx_4x1_i3_o3 FMLA_nx_h 11000001 0001 .... 1 .. 1.. ...00 0 .... @azx_4x1_i3_o3 FMLA_nx_s 11000001 0101 .... 1 .. 0.. ...00 00 ... @azx_4x1_i2_o3 FMLA_nx_d 11000001 1101 .... 1 .. 00. ...00 00 ... @azx_4x1_i1_o3 +BFMLS_nx 11000001 0001 .... 0 .. 1.. ....1 1 .... @azx_2x1_i3_o3 FMLS_nx_h 11000001 0001 .... 0 .. 1.. ....0 1 .... @azx_2x1_i3_o3 FMLS_nx_s 11000001 0101 .... 0 .. 0.. ....0 10 ... @azx_2x1_i2_o3 FMLS_nx_d 11000001 1101 .... 0 .. 00. ....0 10 ... @azx_2x1_i1_o3 + +BFMLS_nx 11000001 0001 .... 1 .. 1.. ...01 1 .... @azx_4x1_i3_o3 FMLS_nx_h 11000001 0001 .... 1 .. 1.. ...00 1 .... @azx_4x1_i3_o3 FMLS_nx_s 11000001 0101 .... 1 .. 0.. ...00 10 ... @azx_4x1_i2_o3 FMLS_nx_d 11000001 1101 .... 1 .. 00. ...00 10 ... @azx_4x1_i1_o3 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 5f51369609..3f9a80248a 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1038,6 +1038,15 @@ TRANS_FEAT(FMLA_nn_d, aa64_sme2_f64f64, do_fmla, a, true, FPST_ZA, TRANS_FEAT(FMLS_nn_d, aa64_sme2_f64f64, do_fmla, a, true, FPST_ZA, s->fpcr_ah ? gen_helper_gvec_ah_vfms_d : gen_helper_gvec_vfms_d) +TRANS_FEAT(BFMLA_n1, aa64_sme_b16b16, do_fmla, a, false, FPST_ZA, + gen_helper_gvec_bfmla) +TRANS_FEAT(BFMLS_n1, aa64_sme_b16b16, do_fmla, a, false, FPST_ZA, + s->fpcr_ah ? gen_helper_gvec_ah_bfmls : gen_helper_gvec_bfmls) +TRANS_FEAT(BFMLA_nn, aa64_sme_b16b16, do_fmla, a, true, FPST_ZA, + gen_helper_gvec_bfmla) +TRANS_FEAT(BFMLS_nn, aa64_sme_b16b16, do_fmla, a, true, FPST_ZA, + s->fpcr_ah ? gen_helper_gvec_ah_bfmls : gen_helper_gvec_bfmls) + static bool do_fmla_nx(DisasContext *s, arg_azx_n *a, ARMFPStatusFlavour fpst, gen_helper_gvec_4_ptr *fn) { @@ -1058,6 +1067,11 @@ TRANS_FEAT(FMLA_nx_d, aa64_sme2_f64f64, do_fmla_nx, a, FPST_ZA, TRANS_FEAT(FMLS_nx_d, aa64_sme2_f64f64, do_fmla_nx, a, FPST_ZA, s->fpcr_ah ? gen_helper_gvec_ah_fmls_idx_d : gen_helper_gvec_fmls_idx_d) +TRANS_FEAT(BFMLA_nx, aa64_sme_b16b16, do_fmla_nx, a, FPST_ZA, + gen_helper_gvec_bfmla_idx) +TRANS_FEAT(BFMLS_nx, aa64_sme_b16b16, do_fmla_nx, a, FPST_ZA, + s->fpcr_ah ? gen_helper_gvec_ah_bfmls_idx : gen_helper_gvec_bfmls_idx) + /* * Expand array multi-vector single (n1), array multi-vector (nn), * and array multi-vector indexed (nx), for integer accumulate. diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 4b7de5016e..51bfd767a3 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1608,6 +1608,12 @@ static float16 float16_muladd_f(float16 dest, float16 op1, float16 op2, return float16_muladd(op1, op2, dest, 0, stat); } +static bfloat16 bfloat16_muladd_f(bfloat16 dest, bfloat16 op1, bfloat16 op2, + float_status *stat) +{ + return bfloat16_muladd(op1, op2, dest, 0, stat); +} + static float32 float32_muladd_f(float32 dest, float32 op1, float32 op2, float_status *stat) { @@ -1626,6 +1632,12 @@ static float16 float16_mulsub_f(float16 dest, float16 op1, float16 op2, return float16_muladd(float16_chs(op1), op2, dest, 0, stat); } +static bfloat16 bfloat16_mulsub_f(bfloat16 dest, bfloat16 op1, bfloat16 op2, + float_status *stat) +{ + return bfloat16_muladd(bfloat16_chs(op1), op2, dest, 0, stat); +} + static float32 float32_mulsub_f(float32 dest, float32 op1, float32 op2, float_status *stat) { @@ -1644,6 +1656,12 @@ static float16 float16_ah_mulsub_f(float16 dest, float16 op1, float16 op2, return float16_muladd(op1, op2, dest, float_muladd_negate_product, stat); } +static bfloat16 bfloat16_ah_mulsub_f(bfloat16 dest, bfloat16 op1, bfloat16 op2, + float_status *stat) +{ + return bfloat16_muladd(op1, op2, dest, float_muladd_negate_product, stat); +} + static float32 float32_ah_mulsub_f(float32 dest, float32 op1, float32 op2, float_status *stat) { @@ -1677,14 +1695,19 @@ DO_MULADD(gvec_fmls_nf_s, float32_mulsub_nf, float32) DO_MULADD(gvec_vfma_h, float16_muladd_f, float16) DO_MULADD(gvec_vfma_s, float32_muladd_f, float32) DO_MULADD(gvec_vfma_d, float64_muladd_f, float64) +DO_MULADD(gvec_bfmla, bfloat16_muladd_f, bfloat16) DO_MULADD(gvec_vfms_h, float16_mulsub_f, float16) DO_MULADD(gvec_vfms_s, float32_mulsub_f, float32) DO_MULADD(gvec_vfms_d, float64_mulsub_f, float64) +DO_MULADD(gvec_bfmls, bfloat16_mulsub_f, bfloat16) DO_MULADD(gvec_ah_vfms_h, float16_ah_mulsub_f, float16) DO_MULADD(gvec_ah_vfms_s, float32_ah_mulsub_f, float32) DO_MULADD(gvec_ah_vfms_d, float64_ah_mulsub_f, float64) +DO_MULADD(gvec_ah_bfmls, bfloat16_ah_mulsub_f, bfloat16) + +#undef DO_MULADD /* For the indexed ops, SVE applies the index per 128-bit vector segment. * For AdvSIMD, there is of course only one such vector segment. @@ -1803,14 +1826,17 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *va, \ DO_FMLA_IDX(gvec_fmla_idx_h, float16, H2, 0, 0) DO_FMLA_IDX(gvec_fmla_idx_s, float32, H4, 0, 0) DO_FMLA_IDX(gvec_fmla_idx_d, float64, H8, 0, 0) +DO_FMLA_IDX(gvec_bfmla_idx, bfloat16, H2, 0, 0) DO_FMLA_IDX(gvec_fmls_idx_h, float16, H2, INT16_MIN, 0) DO_FMLA_IDX(gvec_fmls_idx_s, float32, H4, INT32_MIN, 0) DO_FMLA_IDX(gvec_fmls_idx_d, float64, H8, INT64_MIN, 0) +DO_FMLA_IDX(gvec_bfmls_idx, bfloat16, H2, INT16_MIN, 0) DO_FMLA_IDX(gvec_ah_fmls_idx_h, float16, H2, 0, float_muladd_negate_product) DO_FMLA_IDX(gvec_ah_fmls_idx_s, float32, H4, 0, float_muladd_negate_product) DO_FMLA_IDX(gvec_ah_fmls_idx_d, float64, H8, 0, float_muladd_negate_product) +DO_FMLA_IDX(gvec_ah_bfmls_idx, bfloat16, H2, 0, float_muladd_negate_product) #undef DO_FMLA_IDX From c4f514e2b005038d4bb9edc14b3878632f043458 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:13 -0600 Subject: [PATCH 1997/2760] target/arm: Implement SME2 FADD, FSUB, BFADD, BFSUB Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-51-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper.h | 2 ++ target/arm/tcg/sme.decode | 25 +++++++++++++++++++ target/arm/tcg/translate-sme.c | 44 ++++++++++++++++++++++++++++++++++ target/arm/tcg/vec_helper.c | 2 ++ 4 files changed, 73 insertions(+) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index 37dd384659..392bf7b9b5 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -731,10 +731,12 @@ DEF_HELPER_FLAGS_4(gvec_fclt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_bfadd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_bfsub, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index b3dd3bffc3..0badc8f9fa 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -510,6 +510,31 @@ FMLS_nn_h 11000001 101 ...01 0 .. 100 ...00 11 ... @azz_4x4_o3 FMLS_nn_s 11000001 101 ...01 0 .. 110 ...00 01 ... @azz_4x4_o3 FMLS_nn_d 11000001 111 ...01 0 .. 110 ...00 01 ... @azz_4x4_o3 +&az_n n off rv zm +@az_2x2_o3 ........ ... ..... . .. ... ..... .. off:3 \ + &az_n n=2 rv=%mova_rv zm=%zn_ax2 +@az_4x4_o3 ........ ... ..... . .. ... ..... .. off:3 \ + &az_n n=4 rv=%mova_rv zm=%zn_ax4 + +FADD_nn_h 11000001 101 00100 0 .. 111 ....0 00 ... @az_2x2_o3 +FADD_nn_s 11000001 101 00000 0 .. 111 ....0 00 ... @az_2x2_o3 +FADD_nn_d 11000001 111 00000 0 .. 111 ....0 00 ... @az_2x2_o3 +FADD_nn_h 11000001 101 00101 0 .. 111 ...00 00 ... @az_4x4_o3 +FADD_nn_s 11000001 101 00001 0 .. 111 ...00 00 ... @az_4x4_o3 +FADD_nn_d 11000001 111 00001 0 .. 111 ...00 00 ... @az_4x4_o3 + +FSUB_nn_h 11000001 101 00100 0 .. 111 ....0 01 ... @az_2x2_o3 +FSUB_nn_s 11000001 101 00000 0 .. 111 ....0 01 ... @az_2x2_o3 +FSUB_nn_d 11000001 111 00000 0 .. 111 ....0 01 ... @az_2x2_o3 +FSUB_nn_h 11000001 101 00101 0 .. 111 ...00 01 ... @az_4x4_o3 +FSUB_nn_s 11000001 101 00001 0 .. 111 ...00 01 ... @az_4x4_o3 +FSUB_nn_d 11000001 111 00001 0 .. 111 ...00 01 ... @az_4x4_o3 + +BFADD_nn 11000001 111 00100 0 .. 111 ....0 00 ... @az_2x2_o3 +BFADD_nn 11000001 111 00101 0 .. 111 ...00 00 ... @az_4x4_o3 +BFSUB_nn 11000001 111 00100 0 .. 111 ....0 01 ... @az_2x2_o3 +BFSUB_nn 11000001 111 00101 0 .. 111 ...00 01 ... @az_4x4_o3 + ### SME2 Multi-vector Indexed &azx_n n off rv zn zm idx diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 3f9a80248a..664fdd86aa 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1072,6 +1072,50 @@ TRANS_FEAT(BFMLA_nx, aa64_sme_b16b16, do_fmla_nx, a, FPST_ZA, TRANS_FEAT(BFMLS_nx, aa64_sme_b16b16, do_fmla_nx, a, FPST_ZA, s->fpcr_ah ? gen_helper_gvec_ah_bfmls_idx : gen_helper_gvec_bfmls_idx) +static bool do_faddsub(DisasContext *s, arg_az_n *a, ARMFPStatusFlavour fpst, + gen_helper_gvec_3_ptr *fn) +{ + if (sme_smza_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + int n = a->n; + int zm = a->zm; + int vstride = svl / n; + TCGv_ptr t_za = get_zarray(s, a->rv, a->off, n, 0); + TCGv_ptr ptr = fpstatus_ptr(fpst); + TCGv_ptr t = tcg_temp_new_ptr(); + + for (int r = 0; r < n; ++r) { + TCGv_ptr t_zm = vec_full_reg_ptr(s, zm + r); + int o_za = r * vstride * sizeof(ARMVectorReg); + int desc = simd_desc(svl, svl, 0); + + tcg_gen_addi_ptr(t, t_za, o_za); + fn(t, t, t_zm, ptr, tcg_constant_i32(desc)); + } + } + return true; +} + +TRANS_FEAT(FADD_nn_h, aa64_sme_f16f16, do_faddsub, a, + FPST_ZA_F16, gen_helper_gvec_fadd_h) +TRANS_FEAT(FSUB_nn_h, aa64_sme_f16f16, do_faddsub, a, + FPST_ZA_F16, gen_helper_gvec_fsub_h) + +TRANS_FEAT(FADD_nn_s, aa64_sme2, do_faddsub, a, + FPST_ZA, gen_helper_gvec_fadd_s) +TRANS_FEAT(FSUB_nn_s, aa64_sme2, do_faddsub, a, + FPST_ZA, gen_helper_gvec_fsub_s) + +TRANS_FEAT(FADD_nn_d, aa64_sme2_f64f64, do_faddsub, a, + FPST_ZA, gen_helper_gvec_fadd_d) +TRANS_FEAT(FSUB_nn_d, aa64_sme2_f64f64, do_faddsub, a, + FPST_ZA, gen_helper_gvec_fsub_d) + +TRANS_FEAT(BFADD_nn, aa64_sme_b16b16, do_faddsub, a, + FPST_ZA, gen_helper_gvec_bfadd) +TRANS_FEAT(BFSUB_nn, aa64_sme_b16b16, do_faddsub, a, + FPST_ZA, gen_helper_gvec_bfsub) + /* * Expand array multi-vector single (n1), array multi-vector (nn), * and array multi-vector indexed (nx), for integer accumulate. diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 51bfd767a3..d4ee6f4d29 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1470,10 +1470,12 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, \ DO_3OP(gvec_fadd_h, float16_add, float16) DO_3OP(gvec_fadd_s, float32_add, float32) DO_3OP(gvec_fadd_d, float64_add, float64) +DO_3OP(gvec_bfadd, bfloat16_add, bfloat16) DO_3OP(gvec_fsub_h, float16_sub, float16) DO_3OP(gvec_fsub_s, float32_sub, float32) DO_3OP(gvec_fsub_d, float64_sub, float64) +DO_3OP(gvec_bfsub, bfloat16_sub, bfloat16) DO_3OP(gvec_fmul_h, float16_mul, float16) DO_3OP(gvec_fmul_s, float32_mul, float32) From ccb512d5b504d21ab59c28e3e41757b88779c890 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:14 -0600 Subject: [PATCH 1998/2760] target/arm: Implement SME2 ADD/SUB (array accumulator) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-52-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 12 ++++++++++++ target/arm/tcg/translate-sme.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 0badc8f9fa..22e2a68b1b 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -708,3 +708,15 @@ BFMLS_nx 11000001 0001 .... 1 .. 1.. ...01 1 .... @azx_4x1_i3_o3 FMLS_nx_h 11000001 0001 .... 1 .. 1.. ...00 1 .... @azx_4x1_i3_o3 FMLS_nx_s 11000001 0101 .... 1 .. 0.. ...00 10 ... @azx_4x1_i2_o3 FMLS_nx_d 11000001 1101 .... 1 .. 00. ...00 10 ... @azx_4x1_i1_o3 + +### SME2 Add / Sub array accumulators + +ADD_aaz_s 11000001 101 000000 .. 111 ....0 10 ... @az_2x2_o3 +ADD_aaz_s 11000001 101 000010 .. 111 ...00 10 ... @az_4x4_o3 +ADD_aaz_d 11000001 111 000000 .. 111 ....0 10 ... @az_2x2_o3 +ADD_aaz_d 11000001 111 000010 .. 111 ...00 10 ... @az_4x4_o3 + +SUB_aaz_s 11000001 101 000000 .. 111 ....0 11 ... @az_2x2_o3 +SUB_aaz_s 11000001 101 000010 .. 111 ...00 11 ... @az_4x4_o3 +SUB_aaz_d 11000001 111 000000 .. 111 ....0 11 ... @az_2x2_o3 +SUB_aaz_d 11000001 111 000010 .. 111 ...00 11 ... @az_4x4_o3 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 664fdd86aa..4c3b9aa7d6 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -806,6 +806,34 @@ TRANS_FEAT(SUB_azz_nn_s, aa64_sme2, do_azz_nn, a, MO_32, tcg_gen_gvec_sub_var) TRANS_FEAT(ADD_azz_nn_d, aa64_sme2_i16i64, do_azz_nn, a, MO_64, tcg_gen_gvec_add_var) TRANS_FEAT(SUB_azz_nn_d, aa64_sme2_i16i64, do_azz_nn, a, MO_64, tcg_gen_gvec_sub_var) +/* Add/Sub each ZA[d*N] += Z[m*N] */ +static bool do_aaz(DisasContext *s, arg_az_n *a, int esz, GVecGen3FnVar *fn) +{ + TCGv_ptr t_za; + int svl, n; + + if (!sme_smza_enabled_check(s)) { + return true; + } + + n = a->n; + t_za = get_zarray(s, a->rv, a->off, n, 0); + svl = streaming_vec_reg_size(s); + + for (int i = 0; i < n; ++i) { + int o_za = (svl / n * sizeof(ARMVectorReg)) * i; + int o_zm = vec_full_reg_offset(s, a->zm + i); + + fn(esz, t_za, o_za, t_za, o_za, tcg_env, o_zm, svl, svl); + } + return true; +} + +TRANS_FEAT(ADD_aaz_s, aa64_sme2, do_aaz, a, MO_32, tcg_gen_gvec_add_var) +TRANS_FEAT(SUB_aaz_s, aa64_sme2, do_aaz, a, MO_32, tcg_gen_gvec_sub_var) +TRANS_FEAT(ADD_aaz_d, aa64_sme2_i16i64, do_aaz, a, MO_64, tcg_gen_gvec_add_var) +TRANS_FEAT(SUB_aaz_d, aa64_sme2_i16i64, do_aaz, a, MO_64, tcg_gen_gvec_sub_var) + /* * Expand array multi-vector single (n1), array multi-vector (nn), * and array multi-vector indexed (nx), for floating-point accumulate. From 465d36db0e12cf9eb8a08e3ba1c6b306f356ba52 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:15 -0600 Subject: [PATCH 1999/2760] target/arm: Implement SME2 BFCVT, BFCVTN, FCVT, FCVTN Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-53-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 5 +++ target/arm/tcg/sme.decode | 12 ++++++ target/arm/tcg/sme_helper.c | 74 ++++++++++++++++++++++++++++++++++ target/arm/tcg/sve_helper.c | 2 +- target/arm/tcg/translate-sme.c | 25 ++++++++++++ target/arm/tcg/vec_internal.h | 2 + 6 files changed, 119 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 0bb8af194b..97e70959e8 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -212,3 +212,8 @@ DEF_HELPER_FLAGS_5(sme2_umlsll_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, DEF_HELPER_FLAGS_5(sme2_umlsll_idx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sme2_usmlall_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sme2_sumlall_idx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sme2_bfcvt, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sme2_bfcvtn, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sme2_fcvt_n, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sme2_fcvtn, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 22e2a68b1b..831179c0ee 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -720,3 +720,15 @@ SUB_aaz_s 11000001 101 000000 .. 111 ....0 11 ... @az_2x2_o3 SUB_aaz_s 11000001 101 000010 .. 111 ...00 11 ... @az_4x4_o3 SUB_aaz_d 11000001 111 000000 .. 111 ....0 11 ... @az_2x2_o3 SUB_aaz_d 11000001 111 000010 .. 111 ...00 11 ... @az_4x4_o3 + +### SME2 Multi-vector SVE Constructive Unary + +&zz_n zd zn n +@zz_1x2 ........ ... ..... ...... ..... zd:5 \ + &zz_n n=1 zn=%zn_ax2 + +BFCVT 11000001 011 00000 111000 ....0 ..... @zz_1x2 +BFCVTN 11000001 011 00000 111000 ....1 ..... @zz_1x2 + +FCVT_n 11000001 001 00000 111000 ....0 ..... @zz_1x2 +FCVTN 11000001 001 00000 111000 ....1 ..... @zz_1x2 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 0f79d7cb6e..d97afdb69c 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1517,3 +1517,77 @@ DO_MLALL_IDX(sme2_usmlall_idx_s, uint32_t, uint8_t, int8_t, H4, H1, +) DO_MLALL_IDX(sme2_sumlall_idx_s, uint32_t, int8_t, uint8_t, H4, H1, +) #undef DO_MLALL_IDX + +/* Convert and compress */ +void HELPER(sme2_bfcvt)(void *vd, void *vs, float_status *fpst, uint32_t desc) +{ + ARMVectorReg scratch; + size_t oprsz = simd_oprsz(desc); + size_t i, n = oprsz / 4; + float32 *s0 = vs; + float32 *s1 = vs + sizeof(ARMVectorReg); + bfloat16 *d = vd; + + if (vd == s1) { + s1 = memcpy(&scratch, s1, oprsz); + } + + for (i = 0; i < n; ++i) { + d[H2(i)] = float32_to_bfloat16(s0[H4(i)], fpst); + } + for (i = 0; i < n; ++i) { + d[H2(i) + n] = float32_to_bfloat16(s1[H4(i)], fpst); + } +} + +void HELPER(sme2_fcvt_n)(void *vd, void *vs, float_status *fpst, uint32_t desc) +{ + ARMVectorReg scratch; + size_t oprsz = simd_oprsz(desc); + size_t i, n = oprsz / 4; + float32 *s0 = vs; + float32 *s1 = vs + sizeof(ARMVectorReg); + float16 *d = vd; + + if (vd == s1) { + s1 = memcpy(&scratch, s1, oprsz); + } + + for (i = 0; i < n; ++i) { + d[H2(i)] = sve_f32_to_f16(s0[H4(i)], fpst); + } + for (i = 0; i < n; ++i) { + d[H2(i) + n] = sve_f32_to_f16(s1[H4(i)], fpst); + } +} + +/* Convert and interleave */ +void HELPER(sme2_bfcvtn)(void *vd, void *vs, float_status *fpst, uint32_t desc) +{ + size_t i, n = simd_oprsz(desc) / 4; + float32 *s0 = vs; + float32 *s1 = vs + sizeof(ARMVectorReg); + bfloat16 *d = vd; + + for (i = 0; i < n; ++i) { + bfloat16 d0 = float32_to_bfloat16(s0[H4(i)], fpst); + bfloat16 d1 = float32_to_bfloat16(s1[H4(i)], fpst); + d[H2(i * 2 + 0)] = d0; + d[H2(i * 2 + 1)] = d1; + } +} + +void HELPER(sme2_fcvtn)(void *vd, void *vs, float_status *fpst, uint32_t desc) +{ + size_t i, n = simd_oprsz(desc) / 4; + float32 *s0 = vs; + float32 *s1 = vs + sizeof(ARMVectorReg); + bfloat16 *d = vd; + + for (i = 0; i < n; ++i) { + bfloat16 d0 = sve_f32_to_f16(s0[H4(i)], fpst); + bfloat16 d1 = sve_f32_to_f16(s1[H4(i)], fpst); + d[H2(i * 2 + 0)] = d0; + d[H2(i * 2 + 1)] = d1; + } +} diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index a2c363a4e1..b522ddaf7c 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4576,7 +4576,7 @@ static inline float64 sve_f16_to_f64(float16 f, float_status *fpst) return ret; } -static inline float16 sve_f32_to_f16(float32 f, float_status *fpst) +float16 sve_f32_to_f16(float32 f, float_status *fpst) { bool save = get_flush_to_zero(fpst); float16 ret; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 4c3b9aa7d6..10e5b77040 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1335,3 +1335,28 @@ TRANS_FEAT(SMLALL_nx_d, aa64_sme2_i16i64, do_smlall_nx, a, gen_helper_sme2_smlal TRANS_FEAT(SMLSLL_nx_d, aa64_sme2_i16i64, do_smlall_nx, a, gen_helper_sme2_smlsll_idx_d) TRANS_FEAT(UMLALL_nx_d, aa64_sme2_i16i64, do_smlall_nx, a, gen_helper_sme2_umlall_idx_d) TRANS_FEAT(UMLSLL_nx_d, aa64_sme2_i16i64, do_smlall_nx, a, gen_helper_sme2_umlsll_idx_d) + +static bool do_zz_fpst(DisasContext *s, arg_zz_n *a, int data, + ARMFPStatusFlavour type, gen_helper_gvec_2_ptr *fn) +{ + if (sme_sm_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + TCGv_ptr fpst = fpstatus_ptr(type); + + for (int i = 0, n = a->n; i < n; ++i) { + tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, a->zd + i), + vec_full_reg_offset(s, a->zn + i), + fpst, svl, svl, data, fn); + } + } + return true; +} + +TRANS_FEAT(BFCVT, aa64_sme2, do_zz_fpst, a, 0, + FPST_A64, gen_helper_sme2_bfcvt) +TRANS_FEAT(BFCVTN, aa64_sme2, do_zz_fpst, a, 0, + FPST_A64, gen_helper_sme2_bfcvtn) +TRANS_FEAT(FCVT_n, aa64_sme2, do_zz_fpst, a, 0, + FPST_A64, gen_helper_sme2_fcvt_n) +TRANS_FEAT(FCVTN, aa64_sme2, do_zz_fpst, a, 0, + FPST_A64, gen_helper_sme2_fcvtn) diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index 64a7249d01..c8199a3ef8 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -306,4 +306,6 @@ static inline float64 float64_maybe_ah_chs(float64 a, bool fpcr_ah) bfloat16 helper_sme2_ah_fmax_b16(bfloat16 a, bfloat16 b, float_status *fpst); bfloat16 helper_sme2_ah_fmin_b16(bfloat16 a, bfloat16 b, float_status *fpst); +float16 sve_f32_to_f16(float32 f, float_status *fpst); + #endif /* TARGET_ARM_VEC_INTERNAL_H */ From f73ef7f90f5c99111383f1005c0d7c72d9cb9247 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:16 -0600 Subject: [PATCH 2000/2760] target/arm: Implement SME2 FCVT (widening), FCVTL Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-54-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 2 ++ target/arm/tcg/sme.decode | 5 ++++ target/arm/tcg/sme_helper.c | 45 ++++++++++++++++++++++++++++++++++ target/arm/tcg/sve_helper.c | 2 +- target/arm/tcg/translate-sme.c | 5 ++++ target/arm/tcg/vec_internal.h | 1 + 6 files changed, 59 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 97e70959e8..be4621f2d9 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -217,3 +217,5 @@ DEF_HELPER_FLAGS_4(sme2_bfcvt, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sme2_bfcvtn, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sme2_fcvt_n, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sme2_fcvtn, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sme2_fcvt_w, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sme2_fcvtl, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 831179c0ee..5100ebaeba 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -726,9 +726,14 @@ SUB_aaz_d 11000001 111 000010 .. 111 ...00 11 ... @az_4x4_o3 &zz_n zd zn n @zz_1x2 ........ ... ..... ...... ..... zd:5 \ &zz_n n=1 zn=%zn_ax2 +@zz_2x1 ........ ... ..... ...... zn:5 ..... \ + &zz_n n=1 zd=%zd_ax2 BFCVT 11000001 011 00000 111000 ....0 ..... @zz_1x2 BFCVTN 11000001 011 00000 111000 ....1 ..... @zz_1x2 FCVT_n 11000001 001 00000 111000 ....0 ..... @zz_1x2 FCVTN 11000001 001 00000 111000 ....1 ..... @zz_1x2 + +FCVT_w 11000001 101 00000 111000 ..... ....0 @zz_2x1 +FCVTL 11000001 101 00000 111000 ..... ....1 @zz_2x1 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index d97afdb69c..c696246d15 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -29,6 +29,13 @@ #include "vec_internal.h" #include "sve_ldst_internal.h" + +static bool vectors_overlap(ARMVectorReg *x, unsigned nx, + ARMVectorReg *y, unsigned ny) +{ + return !(x + nx <= y || y + ny <= x); +} + void helper_set_svcr(CPUARMState *env, uint32_t val, uint32_t mask) { aarch64_set_svcr(env, val, mask); @@ -1591,3 +1598,41 @@ void HELPER(sme2_fcvtn)(void *vd, void *vs, float_status *fpst, uint32_t desc) d[H2(i * 2 + 1)] = d1; } } + +/* Expand and convert */ +void HELPER(sme2_fcvt_w)(void *vd, void *vs, float_status *fpst, uint32_t desc) +{ + ARMVectorReg scratch; + size_t oprsz = simd_oprsz(desc); + size_t i, n = oprsz / 4; + float16 *s = vs; + float32 *d0 = vd; + float32 *d1 = vd + sizeof(ARMVectorReg); + + if (vectors_overlap(vd, 1, vs, 2)) { + s = memcpy(&scratch, s, oprsz); + } + + for (i = 0; i < n; ++i) { + d0[H4(i)] = sve_f16_to_f32(s[H2(i)], fpst); + } + for (i = 0; i < n; ++i) { + d1[H4(i)] = sve_f16_to_f32(s[H2(n + i)], fpst); + } +} + +/* Deinterleave and convert. */ +void HELPER(sme2_fcvtl)(void *vd, void *vs, float_status *fpst, uint32_t desc) +{ + size_t i, n = simd_oprsz(desc) / 4; + float16 *s = vs; + float32 *d0 = vd; + float32 *d1 = vd + sizeof(ARMVectorReg); + + for (i = 0; i < n; ++i) { + float32 v0 = sve_f16_to_f32(s[H2(i * 2 + 0)], fpst); + float32 v1 = sve_f16_to_f32(s[H2(i * 2 + 1)], fpst); + d0[H4(i)] = v0; + d1[H4(i)] = v1; + } +} diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index b522ddaf7c..4f12723afe 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4554,7 +4554,7 @@ void HELPER(NAME)(void *vd, void *vn, void *vg, \ * FZ16. When converting from fp16, this affects flushing input denormals; * when converting to fp16, this affects flushing output denormals. */ -static inline float32 sve_f16_to_f32(float16 f, float_status *fpst) +float32 sve_f16_to_f32(float16 f, float_status *fpst) { bool save = get_flush_inputs_to_zero(fpst); float32 ret; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 10e5b77040..d6167eafe1 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1360,3 +1360,8 @@ TRANS_FEAT(FCVT_n, aa64_sme2, do_zz_fpst, a, 0, FPST_A64, gen_helper_sme2_fcvt_n) TRANS_FEAT(FCVTN, aa64_sme2, do_zz_fpst, a, 0, FPST_A64, gen_helper_sme2_fcvtn) + +TRANS_FEAT(FCVT_w, aa64_sme_f16f16, do_zz_fpst, a, 0, + FPST_A64_F16, gen_helper_sme2_fcvt_w) +TRANS_FEAT(FCVTL, aa64_sme_f16f16, do_zz_fpst, a, 0, + FPST_A64_F16, gen_helper_sme2_fcvtl) diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index c8199a3ef8..bbf76f63b1 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -306,6 +306,7 @@ static inline float64 float64_maybe_ah_chs(float64 a, bool fpcr_ah) bfloat16 helper_sme2_ah_fmax_b16(bfloat16 a, bfloat16 b, float_status *fpst); bfloat16 helper_sme2_ah_fmin_b16(bfloat16 a, bfloat16 b, float_status *fpst); +float32 sve_f16_to_f32(float16 f, float_status *fpst); float16 sve_f32_to_f16(float32 f, float_status *fpst); #endif /* TARGET_ARM_VEC_INTERNAL_H */ From 91a11f74683f7a873be5908601999239fb7ba363 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:17 -0600 Subject: [PATCH 2001/2760] target/arm: Implement SME2 FCVTZS, FCVTZU Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-55-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 9 +++++++++ target/arm/tcg/translate-sme.c | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 5100ebaeba..79df2dcd2b 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -728,6 +728,10 @@ SUB_aaz_d 11000001 111 000010 .. 111 ...00 11 ... @az_4x4_o3 &zz_n n=1 zn=%zn_ax2 @zz_2x1 ........ ... ..... ...... zn:5 ..... \ &zz_n n=1 zd=%zd_ax2 +@zz_2x2 ........ ... ..... ...... .... . ..... \ + &zz_n n=2 zd=%zd_ax2 zn=%zn_ax2 +@zz_4x4 ........ ... ..... ...... .... . ..... \ + &zz_n n=4 zd=%zd_ax4 zn=%zn_ax4 BFCVT 11000001 011 00000 111000 ....0 ..... @zz_1x2 BFCVTN 11000001 011 00000 111000 ....1 ..... @zz_1x2 @@ -737,3 +741,8 @@ FCVTN 11000001 001 00000 111000 ....1 ..... @zz_1x2 FCVT_w 11000001 101 00000 111000 ..... ....0 @zz_2x1 FCVTL 11000001 101 00000 111000 ..... ....1 @zz_2x1 + +FCVTZS 11000001 001 00001 111000 ....0 ....0 @zz_2x2 +FCVTZS 11000001 001 10001 111000 ...00 ...00 @zz_4x4 +FCVTZU 11000001 001 00001 111000 ....1 ....0 @zz_2x2 +FCVTZU 11000001 001 10001 111000 ...01 ...00 @zz_4x4 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index d6167eafe1..3bf2b6935f 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1365,3 +1365,8 @@ TRANS_FEAT(FCVT_w, aa64_sme_f16f16, do_zz_fpst, a, 0, FPST_A64_F16, gen_helper_sme2_fcvt_w) TRANS_FEAT(FCVTL, aa64_sme_f16f16, do_zz_fpst, a, 0, FPST_A64_F16, gen_helper_sme2_fcvtl) + +TRANS_FEAT(FCVTZS, aa64_sme2, do_zz_fpst, a, 0, + FPST_A64, gen_helper_gvec_vcvt_rz_fs) +TRANS_FEAT(FCVTZU, aa64_sme2, do_zz_fpst, a, 0, + FPST_A64, gen_helper_gvec_vcvt_rz_fu) From 41167be4290124052f28d719994572269d7b3ec1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:18 -0600 Subject: [PATCH 2002/2760] target/arm: Implement SME2 SCVTF, UCVTF Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-56-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 2 ++ target/arm/tcg/sme.decode | 5 +++++ target/arm/tcg/sme_helper.c | 22 ++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 5 +++++ 4 files changed, 34 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index be4621f2d9..6314ad7e01 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -219,3 +219,5 @@ DEF_HELPER_FLAGS_4(sme2_fcvt_n, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sme2_fcvtn, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sme2_fcvt_w, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sme2_fcvtl, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sme2_scvtf, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_4(sme2_ucvtf, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 79df2dcd2b..449d97bd28 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -746,3 +746,8 @@ FCVTZS 11000001 001 00001 111000 ....0 ....0 @zz_2x2 FCVTZS 11000001 001 10001 111000 ...00 ...00 @zz_4x4 FCVTZU 11000001 001 00001 111000 ....1 ....0 @zz_2x2 FCVTZU 11000001 001 10001 111000 ...01 ...00 @zz_4x4 + +SCVTF 11000001 001 00010 111000 ....0 ....0 @zz_2x2 +SCVTF 11000001 001 10010 111000 ...00 ...00 @zz_4x4 +UCVTF 11000001 001 00010 111000 ....1 ....0 @zz_2x2 +UCVTF 11000001 001 10010 111000 ...01 ...00 @zz_4x4 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index c696246d15..d3841400ee 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1636,3 +1636,25 @@ void HELPER(sme2_fcvtl)(void *vd, void *vs, float_status *fpst, uint32_t desc) d1[H4(i)] = v1; } } + +void HELPER(sme2_scvtf)(void *vd, void *vs, float_status *fpst, uint32_t desc) +{ + size_t i, n = simd_oprsz(desc) / 4; + int32_t *d = vd; + float32 *s = vs; + + for (i = 0; i < n; ++i) { + d[i] = int32_to_float32(s[i], fpst); + } +} + +void HELPER(sme2_ucvtf)(void *vd, void *vs, float_status *fpst, uint32_t desc) +{ + size_t i, n = simd_oprsz(desc) / 4; + uint32_t *d = vd; + float32 *s = vs; + + for (i = 0; i < n; ++i) { + d[i] = uint32_to_float32(s[i], fpst); + } +} diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 3bf2b6935f..dce3b56de2 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1370,3 +1370,8 @@ TRANS_FEAT(FCVTZS, aa64_sme2, do_zz_fpst, a, 0, FPST_A64, gen_helper_gvec_vcvt_rz_fs) TRANS_FEAT(FCVTZU, aa64_sme2, do_zz_fpst, a, 0, FPST_A64, gen_helper_gvec_vcvt_rz_fu) + +TRANS_FEAT(SCVTF, aa64_sme2, do_zz_fpst, a, 0, + FPST_A64, gen_helper_sme2_scvtf) +TRANS_FEAT(UCVTF, aa64_sme2, do_zz_fpst, a, 0, + FPST_A64, gen_helper_sme2_ucvtf) From 2065b1420d701b95dfed7e9d76bf7f011c3da9b3 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:19 -0600 Subject: [PATCH 2003/2760] target/arm: Implement SME2 FRINTN, FRINTP, FRINTM, FRINTA Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-57-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 9 +++++++++ target/arm/tcg/translate-sme.c | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 449d97bd28..9cc25622d4 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -751,3 +751,12 @@ SCVTF 11000001 001 00010 111000 ....0 ....0 @zz_2x2 SCVTF 11000001 001 10010 111000 ...00 ...00 @zz_4x4 UCVTF 11000001 001 00010 111000 ....1 ....0 @zz_2x2 UCVTF 11000001 001 10010 111000 ...01 ...00 @zz_4x4 + +FRINTN 11000001 101 01000 111000 ....0 ....0 @zz_2x2 +FRINTN 11000001 101 11000 111000 ...00 ...00 @zz_4x4 +FRINTP 11000001 101 01001 111000 ....0 ....0 @zz_2x2 +FRINTP 11000001 101 11001 111000 ...00 ...00 @zz_4x4 +FRINTM 11000001 101 01010 111000 ....0 ....0 @zz_2x2 +FRINTM 11000001 101 11010 111000 ...00 ...00 @zz_4x4 +FRINTA 11000001 101 01100 111000 ....0 ....0 @zz_2x2 +FRINTA 11000001 101 11100 111000 ...00 ...00 @zz_4x4 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index dce3b56de2..4fbc61ae27 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1375,3 +1375,12 @@ TRANS_FEAT(SCVTF, aa64_sme2, do_zz_fpst, a, 0, FPST_A64, gen_helper_sme2_scvtf) TRANS_FEAT(UCVTF, aa64_sme2, do_zz_fpst, a, 0, FPST_A64, gen_helper_sme2_ucvtf) + +TRANS_FEAT(FRINTN, aa64_sme2, do_zz_fpst, a, float_round_nearest_even, + FPST_A64, gen_helper_gvec_vrint_rm_s) +TRANS_FEAT(FRINTP, aa64_sme2, do_zz_fpst, a, float_round_up, + FPST_A64, gen_helper_gvec_vrint_rm_s) +TRANS_FEAT(FRINTM, aa64_sme2, do_zz_fpst, a, float_round_down, + FPST_A64, gen_helper_gvec_vrint_rm_s) +TRANS_FEAT(FRINTA, aa64_sme2, do_zz_fpst, a, float_round_ties_away, + FPST_A64, gen_helper_gvec_vrint_rm_s) From 88b73373110bbc60c9d5238fd387d213e91933af Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:20 -0600 Subject: [PATCH 2004/2760] target/arm: Introduce do_[us]sat_[bhs] macros Inputs are a wider type of indeterminate sign. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-58-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/vec_internal.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index bbf76f63b1..a1c10c60a1 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -223,6 +223,13 @@ int16_t do_sqrdmlah_h(int16_t, int16_t, int16_t, bool, bool, uint32_t *); int32_t do_sqrdmlah_s(int32_t, int32_t, int32_t, bool, bool, uint32_t *); int64_t do_sqrdmlah_d(int64_t, int64_t, int64_t, bool, bool); +#define do_ssat_b(val) MIN(MAX(val, INT8_MIN), INT8_MAX) +#define do_ssat_h(val) MIN(MAX(val, INT16_MIN), INT16_MAX) +#define do_ssat_s(val) MIN(MAX(val, INT32_MIN), INT32_MAX) +#define do_usat_b(val) MIN(MAX(val, 0), UINT8_MAX) +#define do_usat_h(val) MIN(MAX(val, 0), UINT16_MAX) +#define do_usat_s(val) MIN(MAX(val, 0), UINT32_MAX) + /** * bfdotadd: * @sum: addend From 63b2c8de713933f42f1999cd003f12f90bdc5249 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:21 -0600 Subject: [PATCH 2005/2760] target/arm: Use do_[us]sat_[bhs] in sve_helper.c Replace and remove do_sat_bhs. This avoids multiple repetitions of INT*_MIN/MAX. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-59-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve_helper.c | 116 +++++++++++++++--------------------- 1 file changed, 48 insertions(+), 68 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 4f12723afe..f9052f2164 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -527,14 +527,9 @@ DO_ZPZZ(sve2_uhsub_zpzz_h, uint16_t, H1_2, DO_HSUB_BHS) DO_ZPZZ(sve2_uhsub_zpzz_s, uint32_t, H1_4, DO_HSUB_BHS) DO_ZPZZ_D(sve2_uhsub_zpzz_d, uint64_t, DO_HSUB_D) -static inline int32_t do_sat_bhs(int64_t val, int64_t min, int64_t max) -{ - return val >= max ? max : val <= min ? min : val; -} - -#define DO_SQADD_B(n, m) do_sat_bhs((int64_t)n + m, INT8_MIN, INT8_MAX) -#define DO_SQADD_H(n, m) do_sat_bhs((int64_t)n + m, INT16_MIN, INT16_MAX) -#define DO_SQADD_S(n, m) do_sat_bhs((int64_t)n + m, INT32_MIN, INT32_MAX) +#define DO_SQADD_B(n, m) do_ssat_b((int64_t)n + m) +#define DO_SQADD_H(n, m) do_ssat_h((int64_t)n + m) +#define DO_SQADD_S(n, m) do_ssat_s((int64_t)n + m) static inline int64_t do_sqadd_d(int64_t n, int64_t m) { @@ -551,9 +546,9 @@ DO_ZPZZ(sve2_sqadd_zpzz_h, int16_t, H1_2, DO_SQADD_H) DO_ZPZZ(sve2_sqadd_zpzz_s, int32_t, H1_4, DO_SQADD_S) DO_ZPZZ_D(sve2_sqadd_zpzz_d, int64_t, do_sqadd_d) -#define DO_UQADD_B(n, m) do_sat_bhs((int64_t)n + m, 0, UINT8_MAX) -#define DO_UQADD_H(n, m) do_sat_bhs((int64_t)n + m, 0, UINT16_MAX) -#define DO_UQADD_S(n, m) do_sat_bhs((int64_t)n + m, 0, UINT32_MAX) +#define DO_UQADD_B(n, m) do_usat_b((int64_t)n + m) +#define DO_UQADD_H(n, m) do_usat_h((int64_t)n + m) +#define DO_UQADD_S(n, m) do_usat_s((int64_t)n + m) static inline uint64_t do_uqadd_d(uint64_t n, uint64_t m) { @@ -566,9 +561,9 @@ DO_ZPZZ(sve2_uqadd_zpzz_h, uint16_t, H1_2, DO_UQADD_H) DO_ZPZZ(sve2_uqadd_zpzz_s, uint32_t, H1_4, DO_UQADD_S) DO_ZPZZ_D(sve2_uqadd_zpzz_d, uint64_t, do_uqadd_d) -#define DO_SQSUB_B(n, m) do_sat_bhs((int64_t)n - m, INT8_MIN, INT8_MAX) -#define DO_SQSUB_H(n, m) do_sat_bhs((int64_t)n - m, INT16_MIN, INT16_MAX) -#define DO_SQSUB_S(n, m) do_sat_bhs((int64_t)n - m, INT32_MIN, INT32_MAX) +#define DO_SQSUB_B(n, m) do_ssat_b((int64_t)n - m) +#define DO_SQSUB_H(n, m) do_ssat_h((int64_t)n - m) +#define DO_SQSUB_S(n, m) do_ssat_s((int64_t)n - m) static inline int64_t do_sqsub_d(int64_t n, int64_t m) { @@ -585,9 +580,9 @@ DO_ZPZZ(sve2_sqsub_zpzz_h, int16_t, H1_2, DO_SQSUB_H) DO_ZPZZ(sve2_sqsub_zpzz_s, int32_t, H1_4, DO_SQSUB_S) DO_ZPZZ_D(sve2_sqsub_zpzz_d, int64_t, do_sqsub_d) -#define DO_UQSUB_B(n, m) do_sat_bhs((int64_t)n - m, 0, UINT8_MAX) -#define DO_UQSUB_H(n, m) do_sat_bhs((int64_t)n - m, 0, UINT16_MAX) -#define DO_UQSUB_S(n, m) do_sat_bhs((int64_t)n - m, 0, UINT32_MAX) +#define DO_UQSUB_B(n, m) do_usat_b((int64_t)n - m) +#define DO_UQSUB_H(n, m) do_usat_h((int64_t)n - m) +#define DO_UQSUB_S(n, m) do_usat_s((int64_t)n - m) static inline uint64_t do_uqsub_d(uint64_t n, uint64_t m) { @@ -599,12 +594,9 @@ DO_ZPZZ(sve2_uqsub_zpzz_h, uint16_t, H1_2, DO_UQSUB_H) DO_ZPZZ(sve2_uqsub_zpzz_s, uint32_t, H1_4, DO_UQSUB_S) DO_ZPZZ_D(sve2_uqsub_zpzz_d, uint64_t, do_uqsub_d) -#define DO_SUQADD_B(n, m) \ - do_sat_bhs((int64_t)(int8_t)n + m, INT8_MIN, INT8_MAX) -#define DO_SUQADD_H(n, m) \ - do_sat_bhs((int64_t)(int16_t)n + m, INT16_MIN, INT16_MAX) -#define DO_SUQADD_S(n, m) \ - do_sat_bhs((int64_t)(int32_t)n + m, INT32_MIN, INT32_MAX) +#define DO_SUQADD_B(n, m) do_ssat_b((int64_t)(int8_t)n + m) +#define DO_SUQADD_H(n, m) do_ssat_h((int64_t)(int16_t)n + m) +#define DO_SUQADD_S(n, m) do_ssat_s((int64_t)(int32_t)n + m) static inline int64_t do_suqadd_d(int64_t n, uint64_t m) { @@ -634,12 +626,9 @@ DO_ZPZZ(sve2_suqadd_zpzz_h, uint16_t, H1_2, DO_SUQADD_H) DO_ZPZZ(sve2_suqadd_zpzz_s, uint32_t, H1_4, DO_SUQADD_S) DO_ZPZZ_D(sve2_suqadd_zpzz_d, uint64_t, do_suqadd_d) -#define DO_USQADD_B(n, m) \ - do_sat_bhs((int64_t)n + (int8_t)m, 0, UINT8_MAX) -#define DO_USQADD_H(n, m) \ - do_sat_bhs((int64_t)n + (int16_t)m, 0, UINT16_MAX) -#define DO_USQADD_S(n, m) \ - do_sat_bhs((int64_t)n + (int32_t)m, 0, UINT32_MAX) +#define DO_USQADD_B(n, m) do_usat_b((int64_t)n + (int8_t)m) +#define DO_USQADD_H(n, m) do_usat_h((int64_t)n + (int16_t)m) +#define DO_USQADD_S(n, m) do_usat_s((int64_t)n + (int32_t)m) static inline uint64_t do_usqadd_d(uint64_t n, int64_t m) { @@ -1226,37 +1215,29 @@ void HELPER(NAME)(void *vd, void *vn, uint32_t desc) \ } \ } -#define DO_SQXTN_H(n) do_sat_bhs(n, INT8_MIN, INT8_MAX) -#define DO_SQXTN_S(n) do_sat_bhs(n, INT16_MIN, INT16_MAX) -#define DO_SQXTN_D(n) do_sat_bhs(n, INT32_MIN, INT32_MAX) - -DO_XTNB(sve2_sqxtnb_h, int16_t, DO_SQXTN_H) -DO_XTNB(sve2_sqxtnb_s, int32_t, DO_SQXTN_S) -DO_XTNB(sve2_sqxtnb_d, int64_t, DO_SQXTN_D) - -DO_XTNT(sve2_sqxtnt_h, int16_t, int8_t, H1, DO_SQXTN_H) -DO_XTNT(sve2_sqxtnt_s, int32_t, int16_t, H1_2, DO_SQXTN_S) -DO_XTNT(sve2_sqxtnt_d, int64_t, int32_t, H1_4, DO_SQXTN_D) +DO_XTNB(sve2_sqxtnb_h, int16_t, do_ssat_b) +DO_XTNB(sve2_sqxtnb_s, int32_t, do_ssat_h) +DO_XTNB(sve2_sqxtnb_d, int64_t, do_ssat_s) -#define DO_UQXTN_H(n) do_sat_bhs(n, 0, UINT8_MAX) -#define DO_UQXTN_S(n) do_sat_bhs(n, 0, UINT16_MAX) -#define DO_UQXTN_D(n) do_sat_bhs(n, 0, UINT32_MAX) +DO_XTNT(sve2_sqxtnt_h, int16_t, int8_t, H1, do_ssat_b) +DO_XTNT(sve2_sqxtnt_s, int32_t, int16_t, H1_2, do_ssat_h) +DO_XTNT(sve2_sqxtnt_d, int64_t, int32_t, H1_4, do_ssat_s) -DO_XTNB(sve2_uqxtnb_h, uint16_t, DO_UQXTN_H) -DO_XTNB(sve2_uqxtnb_s, uint32_t, DO_UQXTN_S) -DO_XTNB(sve2_uqxtnb_d, uint64_t, DO_UQXTN_D) +DO_XTNB(sve2_uqxtnb_h, uint16_t, do_usat_b) +DO_XTNB(sve2_uqxtnb_s, uint32_t, do_usat_h) +DO_XTNB(sve2_uqxtnb_d, uint64_t, do_usat_s) -DO_XTNT(sve2_uqxtnt_h, uint16_t, uint8_t, H1, DO_UQXTN_H) -DO_XTNT(sve2_uqxtnt_s, uint32_t, uint16_t, H1_2, DO_UQXTN_S) -DO_XTNT(sve2_uqxtnt_d, uint64_t, uint32_t, H1_4, DO_UQXTN_D) +DO_XTNT(sve2_uqxtnt_h, uint16_t, uint8_t, H1, do_usat_b) +DO_XTNT(sve2_uqxtnt_s, uint32_t, uint16_t, H1_2, do_usat_h) +DO_XTNT(sve2_uqxtnt_d, uint64_t, uint32_t, H1_4, do_usat_s) -DO_XTNB(sve2_sqxtunb_h, int16_t, DO_UQXTN_H) -DO_XTNB(sve2_sqxtunb_s, int32_t, DO_UQXTN_S) -DO_XTNB(sve2_sqxtunb_d, int64_t, DO_UQXTN_D) +DO_XTNB(sve2_sqxtunb_h, int16_t, do_usat_b) +DO_XTNB(sve2_sqxtunb_s, int32_t, do_usat_h) +DO_XTNB(sve2_sqxtunb_d, int64_t, do_usat_s) -DO_XTNT(sve2_sqxtunt_h, int16_t, int8_t, H1, DO_UQXTN_H) -DO_XTNT(sve2_sqxtunt_s, int32_t, int16_t, H1_2, DO_UQXTN_S) -DO_XTNT(sve2_sqxtunt_d, int64_t, int32_t, H1_4, DO_UQXTN_D) +DO_XTNT(sve2_sqxtunt_h, int16_t, int8_t, H1, do_usat_b) +DO_XTNT(sve2_sqxtunt_s, int32_t, int16_t, H1_2, do_usat_h) +DO_XTNT(sve2_sqxtunt_d, int64_t, int32_t, H1_4, do_usat_s) #undef DO_XTNB #undef DO_XTNT @@ -2187,10 +2168,9 @@ DO_SHRNT(sve2_rshrnt_h, uint16_t, uint8_t, H1_2, H1, do_urshr) DO_SHRNT(sve2_rshrnt_s, uint32_t, uint16_t, H1_4, H1_2, do_urshr) DO_SHRNT(sve2_rshrnt_d, uint64_t, uint32_t, H1_8, H1_4, do_urshr) -#define DO_SQSHRUN_H(x, sh) do_sat_bhs((int64_t)(x) >> sh, 0, UINT8_MAX) -#define DO_SQSHRUN_S(x, sh) do_sat_bhs((int64_t)(x) >> sh, 0, UINT16_MAX) -#define DO_SQSHRUN_D(x, sh) \ - do_sat_bhs((int64_t)(x) >> (sh < 64 ? sh : 63), 0, UINT32_MAX) +#define DO_SQSHRUN_H(x, sh) do_usat_b((int64_t)(x) >> sh) +#define DO_SQSHRUN_S(x, sh) do_usat_h((int64_t)(x) >> sh) +#define DO_SQSHRUN_D(x, sh) do_usat_s((int64_t)(x) >> (sh < 64 ? sh : 63)) DO_SHRNB(sve2_sqshrunb_h, int16_t, uint8_t, DO_SQSHRUN_H) DO_SHRNB(sve2_sqshrunb_s, int32_t, uint16_t, DO_SQSHRUN_S) @@ -2200,9 +2180,9 @@ DO_SHRNT(sve2_sqshrunt_h, int16_t, uint8_t, H1_2, H1, DO_SQSHRUN_H) DO_SHRNT(sve2_sqshrunt_s, int32_t, uint16_t, H1_4, H1_2, DO_SQSHRUN_S) DO_SHRNT(sve2_sqshrunt_d, int64_t, uint32_t, H1_8, H1_4, DO_SQSHRUN_D) -#define DO_SQRSHRUN_H(x, sh) do_sat_bhs(do_srshr(x, sh), 0, UINT8_MAX) -#define DO_SQRSHRUN_S(x, sh) do_sat_bhs(do_srshr(x, sh), 0, UINT16_MAX) -#define DO_SQRSHRUN_D(x, sh) do_sat_bhs(do_srshr(x, sh), 0, UINT32_MAX) +#define DO_SQRSHRUN_H(x, sh) do_usat_b(do_srshr(x, sh)) +#define DO_SQRSHRUN_S(x, sh) do_usat_h(do_srshr(x, sh)) +#define DO_SQRSHRUN_D(x, sh) do_usat_s(do_srshr(x, sh)) DO_SHRNB(sve2_sqrshrunb_h, int16_t, uint8_t, DO_SQRSHRUN_H) DO_SHRNB(sve2_sqrshrunb_s, int32_t, uint16_t, DO_SQRSHRUN_S) @@ -2212,9 +2192,9 @@ DO_SHRNT(sve2_sqrshrunt_h, int16_t, uint8_t, H1_2, H1, DO_SQRSHRUN_H) DO_SHRNT(sve2_sqrshrunt_s, int32_t, uint16_t, H1_4, H1_2, DO_SQRSHRUN_S) DO_SHRNT(sve2_sqrshrunt_d, int64_t, uint32_t, H1_8, H1_4, DO_SQRSHRUN_D) -#define DO_SQSHRN_H(x, sh) do_sat_bhs(x >> sh, INT8_MIN, INT8_MAX) -#define DO_SQSHRN_S(x, sh) do_sat_bhs(x >> sh, INT16_MIN, INT16_MAX) -#define DO_SQSHRN_D(x, sh) do_sat_bhs(x >> sh, INT32_MIN, INT32_MAX) +#define DO_SQSHRN_H(x, sh) do_ssat_b(x >> sh) +#define DO_SQSHRN_S(x, sh) do_ssat_h(x >> sh) +#define DO_SQSHRN_D(x, sh) do_ssat_s(x >> sh) DO_SHRNB(sve2_sqshrnb_h, int16_t, uint8_t, DO_SQSHRN_H) DO_SHRNB(sve2_sqshrnb_s, int32_t, uint16_t, DO_SQSHRN_S) @@ -2224,9 +2204,9 @@ DO_SHRNT(sve2_sqshrnt_h, int16_t, uint8_t, H1_2, H1, DO_SQSHRN_H) DO_SHRNT(sve2_sqshrnt_s, int32_t, uint16_t, H1_4, H1_2, DO_SQSHRN_S) DO_SHRNT(sve2_sqshrnt_d, int64_t, uint32_t, H1_8, H1_4, DO_SQSHRN_D) -#define DO_SQRSHRN_H(x, sh) do_sat_bhs(do_srshr(x, sh), INT8_MIN, INT8_MAX) -#define DO_SQRSHRN_S(x, sh) do_sat_bhs(do_srshr(x, sh), INT16_MIN, INT16_MAX) -#define DO_SQRSHRN_D(x, sh) do_sat_bhs(do_srshr(x, sh), INT32_MIN, INT32_MAX) +#define DO_SQRSHRN_H(x, sh) do_ssat_b(do_srshr(x, sh)) +#define DO_SQRSHRN_S(x, sh) do_ssat_h(do_srshr(x, sh)) +#define DO_SQRSHRN_D(x, sh) do_ssat_s(do_srshr(x, sh)) DO_SHRNB(sve2_sqrshrnb_h, int16_t, uint8_t, DO_SQRSHRN_H) DO_SHRNB(sve2_sqrshrnb_s, int32_t, uint16_t, DO_SQRSHRN_S) From bde57978ce3794b8ebaab99307a4899cf4bedd55 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:22 -0600 Subject: [PATCH 2006/2760] target/arm: Implement SME2 SQCVT, UQCVT, SQCVTU Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250704142112.1018902-60-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 20 ++++++ target/arm/tcg/sme.decode | 22 +++++++ target/arm/tcg/sme_helper.c | 116 +++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 35 ++++++++++ 4 files changed, 193 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 6314ad7e01..792b993695 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -221,3 +221,23 @@ DEF_HELPER_FLAGS_4(sme2_fcvt_w, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sme2_fcvtl, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sme2_scvtf, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(sme2_ucvtf, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_3(sme2_sqcvt_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uqcvt_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqcvtu_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqcvt_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uqcvt_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqcvtu_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqcvt_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uqcvt_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqcvtu_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sme2_sqcvtn_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uqcvtn_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqcvtun_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqcvtn_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uqcvtn_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqcvtun_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqcvtn_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uqcvtn_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqcvtun_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 9cc25622d4..e005f6e6ed 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -726,6 +726,8 @@ SUB_aaz_d 11000001 111 000010 .. 111 ...00 11 ... @az_4x4_o3 &zz_n zd zn n @zz_1x2 ........ ... ..... ...... ..... zd:5 \ &zz_n n=1 zn=%zn_ax2 +@zz_1x4 ........ ... ..... ...... ..... zd:5 \ + &zz_n n=1 zn=%zn_ax4 @zz_2x1 ........ ... ..... ...... zn:5 ..... \ &zz_n n=1 zd=%zd_ax2 @zz_2x2 ........ ... ..... ...... .... . ..... \ @@ -760,3 +762,23 @@ FRINTM 11000001 101 01010 111000 ....0 ....0 @zz_2x2 FRINTM 11000001 101 11010 111000 ...00 ...00 @zz_4x4 FRINTA 11000001 101 01100 111000 ....0 ....0 @zz_2x2 FRINTA 11000001 101 11100 111000 ...00 ...00 @zz_4x4 + +SQCVT_sh 11000001 001 00011 111000 ....0 ..... @zz_1x2 +UQCVT_sh 11000001 001 00011 111000 ....1 ..... @zz_1x2 +SQCVTU_sh 11000001 011 00011 111000 ....0 ..... @zz_1x2 + +SQCVT_sb 11000001 001 10011 111000 ...00 ..... @zz_1x4 +UQCVT_sb 11000001 001 10011 111000 ...01 ..... @zz_1x4 +SQCVTU_sb 11000001 011 10011 111000 ...00 ..... @zz_1x4 + +SQCVT_dh 11000001 101 10011 111000 ...00 ..... @zz_1x4 +UQCVT_dh 11000001 101 10011 111000 ...01 ..... @zz_1x4 +SQCVTU_dh 11000001 111 10011 111000 ...00 ..... @zz_1x4 + +SQCVTN_sb 11000001 001 10011 111000 ...10 ..... @zz_1x4 +UQCVTN_sb 11000001 001 10011 111000 ...11 ..... @zz_1x4 +SQCVTUN_sb 11000001 011 10011 111000 ...10 ..... @zz_1x4 + +SQCVTN_dh 11000001 101 10011 111000 ...10 ..... @zz_1x4 +UQCVTN_dh 11000001 101 10011 111000 ...11 ..... @zz_1x4 +SQCVTUN_dh 11000001 111 10011 111000 ...10 ..... @zz_1x4 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index d3841400ee..094a1e57f3 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1568,6 +1568,64 @@ void HELPER(sme2_fcvt_n)(void *vd, void *vs, float_status *fpst, uint32_t desc) } } +#define SQCVT2(NAME, TW, TN, HW, HN, SAT) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + ARMVectorReg scratch; \ + size_t oprsz = simd_oprsz(desc), n = oprsz / sizeof(TW); \ + TW *s0 = vs, *s1 = vs + sizeof(ARMVectorReg); \ + TN *d = vd; \ + if (vectors_overlap(vd, 1, vs, 2)) { \ + d = (TN *)&scratch; \ + } \ + for (size_t i = 0; i < n; ++i) { \ + d[HN(i)] = SAT(s0[HW(i)]); \ + d[HN(i + n)] = SAT(s1[HW(i)]); \ + } \ + if (d != vd) { \ + memcpy(vd, d, oprsz); \ + } \ +} + +SQCVT2(sme2_sqcvt_sh, int32_t, int16_t, H4, H2, do_ssat_h) +SQCVT2(sme2_uqcvt_sh, uint32_t, uint16_t, H4, H2, do_usat_h) +SQCVT2(sme2_sqcvtu_sh, int32_t, uint16_t, H4, H2, do_usat_h) + +#undef SQCVT2 + +#define SQCVT4(NAME, TW, TN, HW, HN, SAT) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + ARMVectorReg scratch; \ + size_t oprsz = simd_oprsz(desc), n = oprsz / sizeof(TW); \ + TW *s0 = vs, *s1 = vs + sizeof(ARMVectorReg); \ + TW *s2 = vs + 2 * sizeof(ARMVectorReg); \ + TW *s3 = vs + 3 * sizeof(ARMVectorReg); \ + TN *d = vd; \ + if (vectors_overlap(vd, 1, vs, 4)) { \ + d = (TN *)&scratch; \ + } \ + for (size_t i = 0; i < n; ++i) { \ + d[HN(i)] = SAT(s0[HW(i)]); \ + d[HN(i + n)] = SAT(s1[HW(i)]); \ + d[HN(i + 2 * n)] = SAT(s2[HW(i)]); \ + d[HN(i + 3 * n)] = SAT(s3[HW(i)]); \ + } \ + if (d != vd) { \ + memcpy(vd, d, oprsz); \ + } \ +} + +SQCVT4(sme2_sqcvt_sb, int32_t, int8_t, H4, H2, do_ssat_b) +SQCVT4(sme2_uqcvt_sb, uint32_t, uint8_t, H4, H2, do_usat_b) +SQCVT4(sme2_sqcvtu_sb, int32_t, uint8_t, H4, H2, do_usat_b) + +SQCVT4(sme2_sqcvt_dh, int64_t, int16_t, H8, H2, do_ssat_h) +SQCVT4(sme2_uqcvt_dh, uint64_t, uint16_t, H8, H2, do_usat_h) +SQCVT4(sme2_sqcvtu_dh, int64_t, uint16_t, H8, H2, do_usat_h) + +#undef SQCVT4 + /* Convert and interleave */ void HELPER(sme2_bfcvtn)(void *vd, void *vs, float_status *fpst, uint32_t desc) { @@ -1599,6 +1657,64 @@ void HELPER(sme2_fcvtn)(void *vd, void *vs, float_status *fpst, uint32_t desc) } } +#define SQCVTN2(NAME, TW, TN, HW, HN, SAT) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + ARMVectorReg scratch; \ + size_t oprsz = simd_oprsz(desc), n = oprsz / sizeof(TW); \ + TW *s0 = vs, *s1 = vs + sizeof(ARMVectorReg); \ + TN *d = vd; \ + if (vectors_overlap(vd, 1, vs, 2)) { \ + d = (TN *)&scratch; \ + } \ + for (size_t i = 0; i < n; ++i) { \ + d[HN(2 * i + 0)] = SAT(s0[HW(i)]); \ + d[HN(2 * i + 1)] = SAT(s1[HW(i)]); \ + } \ + if (d != vd) { \ + memcpy(vd, d, oprsz); \ + } \ +} + +SQCVTN2(sme2_sqcvtn_sh, int32_t, int16_t, H4, H2, do_ssat_h) +SQCVTN2(sme2_uqcvtn_sh, uint32_t, uint16_t, H4, H2, do_usat_h) +SQCVTN2(sme2_sqcvtun_sh, int32_t, uint16_t, H4, H2, do_usat_h) + +#undef SQCVTN2 + +#define SQCVTN4(NAME, TW, TN, HW, HN, SAT) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + ARMVectorReg scratch; \ + size_t oprsz = simd_oprsz(desc), n = oprsz / sizeof(TW); \ + TW *s0 = vs, *s1 = vs + sizeof(ARMVectorReg); \ + TW *s2 = vs + 2 * sizeof(ARMVectorReg); \ + TW *s3 = vs + 3 * sizeof(ARMVectorReg); \ + TN *d = vd; \ + if (vectors_overlap(vd, 1, vs, 4)) { \ + d = (TN *)&scratch; \ + } \ + for (size_t i = 0; i < n; ++i) { \ + d[HN(4 * i + 0)] = SAT(s0[HW(i)]); \ + d[HN(4 * i + 1)] = SAT(s1[HW(i)]); \ + d[HN(4 * i + 2)] = SAT(s2[HW(i)]); \ + d[HN(4 * i + 3)] = SAT(s3[HW(i)]); \ + } \ + if (d != vd) { \ + memcpy(vd, d, oprsz); \ + } \ +} + +SQCVTN4(sme2_sqcvtn_sb, int32_t, int8_t, H4, H1, do_ssat_b) +SQCVTN4(sme2_uqcvtn_sb, uint32_t, uint8_t, H4, H1, do_usat_b) +SQCVTN4(sme2_sqcvtun_sb, int32_t, uint8_t, H4, H1, do_usat_b) + +SQCVTN4(sme2_sqcvtn_dh, int64_t, int16_t, H8, H2, do_ssat_h) +SQCVTN4(sme2_uqcvtn_dh, uint64_t, uint16_t, H8, H2, do_usat_h) +SQCVTN4(sme2_sqcvtun_dh, int64_t, uint16_t, H8, H2, do_usat_h) + +#undef SQCVTN4 + /* Expand and convert */ void HELPER(sme2_fcvt_w)(void *vd, void *vs, float_status *fpst, uint32_t desc) { diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 4fbc61ae27..dd1a6668fb 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1384,3 +1384,38 @@ TRANS_FEAT(FRINTM, aa64_sme2, do_zz_fpst, a, float_round_down, FPST_A64, gen_helper_gvec_vrint_rm_s) TRANS_FEAT(FRINTA, aa64_sme2, do_zz_fpst, a, float_round_ties_away, FPST_A64, gen_helper_gvec_vrint_rm_s) + +static bool do_zz(DisasContext *s, arg_zz_n *a, int data, + gen_helper_gvec_2 *fn) +{ + if (sme_sm_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + + for (int i = 0, n = a->n; i < n; ++i) { + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, a->zd + i), + vec_full_reg_offset(s, a->zn + i), + svl, svl, data, fn); + } + } + return true; +} + +TRANS_FEAT(SQCVT_sh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvt_sh) +TRANS_FEAT(UQCVT_sh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uqcvt_sh) +TRANS_FEAT(SQCVTU_sh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvtu_sh) + +TRANS_FEAT(SQCVT_sb, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvt_sb) +TRANS_FEAT(UQCVT_sb, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uqcvt_sb) +TRANS_FEAT(SQCVTU_sb, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvtu_sb) + +TRANS_FEAT(SQCVT_dh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvt_dh) +TRANS_FEAT(UQCVT_dh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uqcvt_dh) +TRANS_FEAT(SQCVTU_dh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvtu_dh) + +TRANS_FEAT(SQCVTN_sb, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvtn_sb) +TRANS_FEAT(UQCVTN_sb, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uqcvtn_sb) +TRANS_FEAT(SQCVTUN_sb, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvtun_sb) + +TRANS_FEAT(SQCVTN_dh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvtn_dh) +TRANS_FEAT(UQCVTN_dh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uqcvtn_dh) +TRANS_FEAT(SQCVTUN_dh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvtun_dh) From 10168d3912d46d075724dd485aee5eabced7b99f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:23 -0600 Subject: [PATCH 2007/2760] target/arm: Implement SQCVTN, UQCVTN, SQCVTUN for SME2/SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-61-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve.decode | 20 ++++++++++++++++---- target/arm/tcg/translate-sve.c | 7 +++++++ 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 83c259def6..f808362f23 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -57,6 +57,8 @@ # as propagated via the MOVPRFX instruction. %reg_movprfx 0:5 +%rn_ax2 6:4 !function=times_2 + ########################################################################### # Named attribute sets. These are used to make nice(er) names # when creating helpers common to those for the individual @@ -102,6 +104,7 @@ # Two operand @pd_pn ........ esz:2 .. .... ....... rn:4 . rd:4 &rr_esz @rd_rn ........ esz:2 ...... ...... rn:5 rd:5 &rr_esz +@rd_rnx2 ........ ... ..... ...... ..... rd:5 &rr_esz rn=%rn_ax2 # Two operand with governing predicate, flags setting @pd_pg_pn_s ........ . s:1 ...... .. pg:4 . rn:4 . rd:4 &rpr_s @@ -1507,13 +1510,22 @@ UABA 01000101 .. 0 ..... 11111 1 ..... ..... @rd_rn_rm #### SVE2 Narrowing ## SVE2 saturating extract narrow - # Bits 23, 18-16 are zero, limited in the translator via esz < 3 & imm == 0. -SQXTNB 01000101 .. 1 ..... 010 000 ..... ..... @rd_rn_tszimm_shl + +{ + SQCVTN_sh 01000101 00 1 10001 010 000 ....0 ..... @rd_rnx2 esz=1 + SQXTNB 01000101 .. 1 ..... 010 000 ..... ..... @rd_rn_tszimm_shl +} SQXTNT 01000101 .. 1 ..... 010 001 ..... ..... @rd_rn_tszimm_shl -UQXTNB 01000101 .. 1 ..... 010 010 ..... ..... @rd_rn_tszimm_shl +{ + UQCVTN_sh 01000101 00 1 10001 010 010 ....0 ..... @rd_rnx2 esz=1 + UQXTNB 01000101 .. 1 ..... 010 010 ..... ..... @rd_rn_tszimm_shl +} UQXTNT 01000101 .. 1 ..... 010 011 ..... ..... @rd_rn_tszimm_shl -SQXTUNB 01000101 .. 1 ..... 010 100 ..... ..... @rd_rn_tszimm_shl +{ + SQCVTUN_sh 01000101 00 1 10001 010 100 ....0 ..... @rd_rnx2 esz=1 + SQXTUNB 01000101 .. 1 ..... 010 100 ..... ..... @rd_rn_tszimm_shl +} SQXTUNT 01000101 .. 1 ..... 010 101 ..... ..... @rd_rn_tszimm_shl ## SVE2 bitwise shift right narrow diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 10261e25f9..ac4dc7db46 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -7427,3 +7427,10 @@ static void gen_uclamp(unsigned vece, uint32_t d, uint32_t n, uint32_t m, } TRANS_FEAT(UCLAMP, aa64_sme, gen_gvec_fn_arg_zzzz, gen_uclamp, a) + +TRANS_FEAT(SQCVTN_sh, aa64_sme2_or_sve2p1, gen_gvec_ool_zz, + gen_helper_sme2_sqcvtn_sh, a->rd, a->rn, 0) +TRANS_FEAT(UQCVTN_sh, aa64_sme2_or_sve2p1, gen_gvec_ool_zz, + gen_helper_sme2_uqcvtn_sh, a->rd, a->rn, 0) +TRANS_FEAT(SQCVTUN_sh, aa64_sme2_or_sve2p1, gen_gvec_ool_zz, + gen_helper_sme2_sqcvtun_sh, a->rd, a->rn, 0) From 2ccea621573dd8c2aa23583947d598fe8382b93c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:24 -0600 Subject: [PATCH 2008/2760] target/arm: Implement SME2 SUNPK, UUNPK Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250704142112.1018902-62-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 13 ++++++++++++ target/arm/tcg/sme.decode | 18 ++++++++++++++++ target/arm/tcg/sme_helper.c | 38 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 16 ++++++++++++++ 4 files changed, 85 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 792b993695..893b23aa3b 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -241,3 +241,16 @@ DEF_HELPER_FLAGS_3(sme2_sqcvtun_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_sqcvtn_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_uqcvtn_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_sqcvtun_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sme2_sunpk2_bh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sunpk2_hs, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sunpk2_sd, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sunpk4_bh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sunpk4_hs, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sunpk4_sd, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uunpk2_bh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uunpk2_hs, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uunpk2_sd, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uunpk4_bh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uunpk4_hs, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uunpk4_sd, TCG_CALL_NO_RWG, void, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index e005f6e6ed..38c210cd4f 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -734,6 +734,8 @@ SUB_aaz_d 11000001 111 000010 .. 111 ...00 11 ... @az_4x4_o3 &zz_n n=2 zd=%zd_ax2 zn=%zn_ax2 @zz_4x4 ........ ... ..... ...... .... . ..... \ &zz_n n=4 zd=%zd_ax4 zn=%zn_ax4 +@zz_4x2_n1 ........ ... ..... ...... .... . ..... \ + &zz_n n=1 zd=%zd_ax4 zn=%zn_ax2 BFCVT 11000001 011 00000 111000 ....0 ..... @zz_1x2 BFCVTN 11000001 011 00000 111000 ....1 ..... @zz_1x2 @@ -782,3 +784,19 @@ SQCVTUN_sb 11000001 011 10011 111000 ...10 ..... @zz_1x4 SQCVTN_dh 11000001 101 10011 111000 ...10 ..... @zz_1x4 UQCVTN_dh 11000001 101 10011 111000 ...11 ..... @zz_1x4 SQCVTUN_dh 11000001 111 10011 111000 ...10 ..... @zz_1x4 + +SUNPK_2bh 11000001 011 00101 111000 ..... ....0 @zz_2x1 +SUNPK_2hs 11000001 101 00101 111000 ..... ....0 @zz_2x1 +SUNPK_2sd 11000001 111 00101 111000 ..... ....0 @zz_2x1 + +UUNPK_2bh 11000001 011 00101 111000 ..... ....1 @zz_2x1 +UUNPK_2hs 11000001 101 00101 111000 ..... ....1 @zz_2x1 +UUNPK_2sd 11000001 111 00101 111000 ..... ....1 @zz_2x1 + +SUNPK_4bh 11000001 011 10101 111000 ....0 ...00 @zz_4x2_n1 +SUNPK_4hs 11000001 101 10101 111000 ....0 ...00 @zz_4x2_n1 +SUNPK_4sd 11000001 111 10101 111000 ....0 ...00 @zz_4x2_n1 + +UUNPK_4bh 11000001 011 10101 111000 ....0 ...01 @zz_4x2_n1 +UUNPK_4hs 11000001 101 10101 111000 ....0 ...01 @zz_4x2_n1 +UUNPK_4sd 11000001 111 10101 111000 ....0 ...01 @zz_4x2_n1 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 094a1e57f3..ee4596e066 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1737,6 +1737,44 @@ void HELPER(sme2_fcvt_w)(void *vd, void *vs, float_status *fpst, uint32_t desc) } } +#define UNPK(NAME, SREG, TW, TN, HW, HN) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + ARMVectorReg scratch[SREG]; \ + size_t oprsz = simd_oprsz(desc); \ + size_t n = oprsz / sizeof(TW); \ + if (vectors_overlap(vd, 2 * SREG, vs, SREG)) { \ + vs = memcpy(scratch, vs, sizeof(scratch)); \ + } \ + for (size_t r = 0; r < SREG; ++r) { \ + TN *s = vs + r * sizeof(ARMVectorReg); \ + for (size_t i = 0; i < 2; ++i) { \ + TW *d = vd + (2 * r + i) * sizeof(ARMVectorReg); \ + for (size_t e = 0; e < n; ++e) { \ + d[HW(e)] = s[HN(i * n + e)]; \ + } \ + } \ + } \ +} + +UNPK(sme2_sunpk2_bh, 1, int16_t, int8_t, H2, H1) +UNPK(sme2_sunpk2_hs, 1, int32_t, int16_t, H4, H2) +UNPK(sme2_sunpk2_sd, 1, int64_t, int32_t, H8, H4) + +UNPK(sme2_sunpk4_bh, 2, int16_t, int8_t, H2, H1) +UNPK(sme2_sunpk4_hs, 2, int32_t, int16_t, H4, H2) +UNPK(sme2_sunpk4_sd, 2, int64_t, int32_t, H8, H4) + +UNPK(sme2_uunpk2_bh, 1, uint16_t, uint8_t, H2, H1) +UNPK(sme2_uunpk2_hs, 1, uint32_t, uint16_t, H4, H2) +UNPK(sme2_uunpk2_sd, 1, uint64_t, uint32_t, H8, H4) + +UNPK(sme2_uunpk4_bh, 2, uint16_t, uint8_t, H2, H1) +UNPK(sme2_uunpk4_hs, 2, uint32_t, uint16_t, H4, H2) +UNPK(sme2_uunpk4_sd, 2, uint64_t, uint32_t, H8, H4) + +#undef UNPK + /* Deinterleave and convert. */ void HELPER(sme2_fcvtl)(void *vd, void *vs, float_status *fpst, uint32_t desc) { diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index dd1a6668fb..b45e68750d 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1419,3 +1419,19 @@ TRANS_FEAT(SQCVTUN_sb, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvtun_sb) TRANS_FEAT(SQCVTN_dh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvtn_dh) TRANS_FEAT(UQCVTN_dh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uqcvtn_dh) TRANS_FEAT(SQCVTUN_dh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sqcvtun_dh) + +TRANS_FEAT(SUNPK_2bh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sunpk2_bh) +TRANS_FEAT(SUNPK_2hs, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sunpk2_hs) +TRANS_FEAT(SUNPK_2sd, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sunpk2_sd) + +TRANS_FEAT(SUNPK_4bh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sunpk4_bh) +TRANS_FEAT(SUNPK_4hs, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sunpk4_hs) +TRANS_FEAT(SUNPK_4sd, aa64_sme2, do_zz, a, 0, gen_helper_sme2_sunpk4_sd) + +TRANS_FEAT(UUNPK_2bh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uunpk2_bh) +TRANS_FEAT(UUNPK_2hs, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uunpk2_hs) +TRANS_FEAT(UUNPK_2sd, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uunpk2_sd) + +TRANS_FEAT(UUNPK_4bh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uunpk4_bh) +TRANS_FEAT(UUNPK_4hs, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uunpk4_hs) +TRANS_FEAT(UUNPK_4sd, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uunpk4_sd) From a3a019c0efc06e63c648c416484ed5083678c93d Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:25 -0600 Subject: [PATCH 2009/2760] target/arm: Implement SME2 ZIP, UZP (four registers) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-63-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 12 ++++++ target/arm/tcg/sme.decode | 11 ++++++ target/arm/tcg/sme_helper.c | 68 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 39 +++++++++++++++++++ 4 files changed, 130 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 893b23aa3b..7e21f1095a 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -254,3 +254,15 @@ DEF_HELPER_FLAGS_3(sme2_uunpk2_sd, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_uunpk4_bh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_uunpk4_hs, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_uunpk4_sd, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sme2_zip4_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_zip4_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_zip4_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_zip4_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_zip4_q, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sme2_uzp4_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uzp4_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uzp4_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uzp4_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uzp4_q, TCG_CALL_NO_RWG, void, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 38c210cd4f..81783b4705 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -723,6 +723,7 @@ SUB_aaz_d 11000001 111 000010 .. 111 ...00 11 ... @az_4x4_o3 ### SME2 Multi-vector SVE Constructive Unary +&zz_e zd zn esz &zz_n zd zn n @zz_1x2 ........ ... ..... ...... ..... zd:5 \ &zz_n n=1 zn=%zn_ax2 @@ -800,3 +801,13 @@ SUNPK_4sd 11000001 111 10101 111000 ....0 ...00 @zz_4x2_n1 UUNPK_4bh 11000001 011 10101 111000 ....0 ...01 @zz_4x2_n1 UUNPK_4hs 11000001 101 10101 111000 ....0 ...01 @zz_4x2_n1 UUNPK_4sd 11000001 111 10101 111000 ....0 ...01 @zz_4x2_n1 + +ZIP_4 11000001 esz:2 1 10110 111000 ...00 ... 00 \ + &zz_e zd=%zd_ax4 zn=%zn_ax4 +ZIP_4 11000001 001 10111 111000 ...00 ... 00 \ + &zz_e esz=4 zd=%zd_ax4 zn=%zn_ax4 + +UZP_4 11000001 esz:2 1 10110 111000 ...00 ... 10 \ + &zz_e zd=%zd_ax4 zn=%zn_ax4 +UZP_4 11000001 001 10111 111000 ...00 ... 10 \ + &zz_e esz=4 zd=%zd_ax4 zn=%zn_ax4 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index ee4596e066..4a05fff5fe 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1812,3 +1812,71 @@ void HELPER(sme2_ucvtf)(void *vd, void *vs, float_status *fpst, uint32_t desc) d[i] = uint32_to_float32(s[i], fpst); } } + +#define ZIP4(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + ARMVectorReg scratch[4]; \ + size_t oprsz = simd_oprsz(desc); \ + size_t quads = oprsz / (sizeof(TYPE) * 4); \ + TYPE *s0, *s1, *s2, *s3; \ + if (vs == vd) { \ + vs = memcpy(scratch, vs, sizeof(scratch)); \ + } \ + s0 = vs; \ + s1 = vs + sizeof(ARMVectorReg); \ + s2 = vs + 2 * sizeof(ARMVectorReg); \ + s3 = vs + 3 * sizeof(ARMVectorReg); \ + for (size_t r = 0; r < 4; ++r) { \ + TYPE *d = vd + r * sizeof(ARMVectorReg); \ + size_t base = r * quads; \ + for (size_t q = 0; q < quads; ++q) { \ + d[H(4 * q + 0)] = s0[base + H(q)]; \ + d[H(4 * q + 1)] = s1[base + H(q)]; \ + d[H(4 * q + 2)] = s2[base + H(q)]; \ + d[H(4 * q + 3)] = s3[base + H(q)]; \ + } \ + } \ +} + +ZIP4(sme2_zip4_b, uint8_t, H1) +ZIP4(sme2_zip4_h, uint16_t, H2) +ZIP4(sme2_zip4_s, uint32_t, H4) +ZIP4(sme2_zip4_d, uint64_t, ) +ZIP4(sme2_zip4_q, Int128, ) + +#undef ZIP4 + +#define UZP4(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + ARMVectorReg scratch[4]; \ + size_t oprsz = simd_oprsz(desc); \ + size_t quads = oprsz / (sizeof(TYPE) * 4); \ + TYPE *d0, *d1, *d2, *d3; \ + if (vs == vd) { \ + vs = memcpy(scratch, vs, sizeof(scratch)); \ + } \ + d0 = vd; \ + d1 = vd + sizeof(ARMVectorReg); \ + d2 = vd + 2 * sizeof(ARMVectorReg); \ + d3 = vd + 3 * sizeof(ARMVectorReg); \ + for (size_t r = 0; r < 4; ++r) { \ + TYPE *s = vs + r * sizeof(ARMVectorReg); \ + size_t base = r * quads; \ + for (size_t q = 0; q < quads; ++q) { \ + d0[base + H(q)] = s[H(4 * q + 0)]; \ + d1[base + H(q)] = s[H(4 * q + 1)]; \ + d2[base + H(q)] = s[H(4 * q + 2)]; \ + d3[base + H(q)] = s[H(4 * q + 3)]; \ + } \ + } \ +} + +UZP4(sme2_uzp4_b, uint8_t, H1) +UZP4(sme2_uzp4_h, uint16_t, H2) +UZP4(sme2_uzp4_s, uint32_t, H4) +UZP4(sme2_uzp4_d, uint64_t, ) +UZP4(sme2_uzp4_q, Int128, ) + +#undef UZP4 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index b45e68750d..de9545ee6d 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1435,3 +1435,42 @@ TRANS_FEAT(UUNPK_2sd, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uunpk2_sd) TRANS_FEAT(UUNPK_4bh, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uunpk4_bh) TRANS_FEAT(UUNPK_4hs, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uunpk4_hs) TRANS_FEAT(UUNPK_4sd, aa64_sme2, do_zz, a, 0, gen_helper_sme2_uunpk4_sd) + +static bool do_zipuzp_4(DisasContext *s, arg_zz_e *a, + gen_helper_gvec_2 * const fn[5]) +{ + int bytes_per_op = 4 << a->esz; + + /* Both MO_64 and MO_128 can fail the size test. */ + if (s->max_svl < bytes_per_op) { + unallocated_encoding(s); + } else if (sme_sm_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + if (svl < bytes_per_op) { + unallocated_encoding(s); + } else { + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, a->zd), + vec_full_reg_offset(s, a->zn), + svl, svl, 0, fn[a->esz]); + } + } + return true; +} + +static gen_helper_gvec_2 * const zip4_fns[] = { + gen_helper_sme2_zip4_b, + gen_helper_sme2_zip4_h, + gen_helper_sme2_zip4_s, + gen_helper_sme2_zip4_d, + gen_helper_sme2_zip4_q, +}; +TRANS_FEAT(ZIP_4, aa64_sme2, do_zipuzp_4, a, zip4_fns) + +static gen_helper_gvec_2 * const uzp4_fns[] = { + gen_helper_sme2_uzp4_b, + gen_helper_sme2_uzp4_h, + gen_helper_sme2_uzp4_s, + gen_helper_sme2_uzp4_d, + gen_helper_sme2_uzp4_q, +}; +TRANS_FEAT(UZP_4, aa64_sme2, do_zipuzp_4, a, uzp4_fns) From 9a7d445c09aa79a14bc4c5eede635b043622cbf0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:26 -0600 Subject: [PATCH 2010/2760] target/arm: Move do_urshr, do_srshr to vec_internal.h Unify two copies of these inline functions. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-64-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/mve_helper.c | 21 --------------------- target/arm/tcg/sve_helper.c | 21 --------------------- target/arm/tcg/vec_internal.h | 21 +++++++++++++++++++++ 3 files changed, 21 insertions(+), 42 deletions(-) diff --git a/target/arm/tcg/mve_helper.c b/target/arm/tcg/mve_helper.c index 42bb3b979b..63ddcf3fec 100644 --- a/target/arm/tcg/mve_helper.c +++ b/target/arm/tcg/mve_helper.c @@ -2204,27 +2204,6 @@ DO_VSHLL_ALL(vshllt, true) DO_VSHRN(OP##tb, true, 1, uint8_t, 2, uint16_t, FN) \ DO_VSHRN(OP##th, true, 2, uint16_t, 4, uint32_t, FN) -static inline uint64_t do_urshr(uint64_t x, unsigned sh) -{ - if (likely(sh < 64)) { - return (x >> sh) + ((x >> (sh - 1)) & 1); - } else if (sh == 64) { - return x >> 63; - } else { - return 0; - } -} - -static inline int64_t do_srshr(int64_t x, unsigned sh) -{ - if (likely(sh < 64)) { - return (x >> sh) + ((x >> (sh - 1)) & 1); - } else { - /* Rounding the sign bit always produces 0. */ - return 0; - } -} - DO_VSHRN_ALL(vshrn, DO_SHR) DO_VSHRN_ALL(vrshrn, do_urshr) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index f9052f2164..0e59ad2262 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -2050,27 +2050,6 @@ void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ when N is negative, add 2**M-1. */ #define DO_ASRD(N, M) ((N + (N < 0 ? ((__typeof(N))1 << M) - 1 : 0)) >> M) -static inline uint64_t do_urshr(uint64_t x, unsigned sh) -{ - if (likely(sh < 64)) { - return (x >> sh) + ((x >> (sh - 1)) & 1); - } else if (sh == 64) { - return x >> 63; - } else { - return 0; - } -} - -static inline int64_t do_srshr(int64_t x, unsigned sh) -{ - if (likely(sh < 64)) { - return (x >> sh) + ((x >> (sh - 1)) & 1); - } else { - /* Rounding the sign bit always produces 0. */ - return 0; - } -} - DO_ZPZI(sve_asr_zpzi_b, int8_t, H1, DO_SHR) DO_ZPZI(sve_asr_zpzi_h, int16_t, H1_2, DO_SHR) DO_ZPZI(sve_asr_zpzi_s, int32_t, H1_4, DO_SHR) diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index a1c10c60a1..5efd257c50 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -230,6 +230,27 @@ int64_t do_sqrdmlah_d(int64_t, int64_t, int64_t, bool, bool); #define do_usat_h(val) MIN(MAX(val, 0), UINT16_MAX) #define do_usat_s(val) MIN(MAX(val, 0), UINT32_MAX) +static inline uint64_t do_urshr(uint64_t x, unsigned sh) +{ + if (likely(sh < 64)) { + return (x >> sh) + ((x >> (sh - 1)) & 1); + } else if (sh == 64) { + return x >> 63; + } else { + return 0; + } +} + +static inline int64_t do_srshr(int64_t x, unsigned sh) +{ + if (likely(sh < 64)) { + return (x >> sh) + ((x >> (sh - 1)) & 1); + } else { + /* Rounding the sign bit always produces 0. */ + return 0; + } +} + /** * bfdotadd: * @sum: addend From 70ad5b9fb172709d1e8bb8718c4e180e46967b8b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:27 -0600 Subject: [PATCH 2011/2760] target/arm: Implement SME2 SQRSHR, UQRSHR, SQRSHRN Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250704142112.1018902-65-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 20 ++++++ target/arm/tcg/sme.decode | 37 ++++++++++ target/arm/tcg/sme_helper.c | 120 +++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 33 +++++++++ 4 files changed, 210 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 7e21f1095a..97428bcd6b 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -266,3 +266,23 @@ DEF_HELPER_FLAGS_3(sme2_uzp4_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_uzp4_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_uzp4_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_uzp4_q, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sme2_sqrshr_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uqrshr_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqrshru_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqrshr_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uqrshr_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqrshru_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqrshr_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uqrshr_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqrshru_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(sme2_sqrshrn_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uqrshrn_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqrshrun_sh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqrshrn_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uqrshrn_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqrshrun_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqrshrn_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_uqrshrn_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2_sqrshrun_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 81783b4705..05d513efba 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -811,3 +811,40 @@ UZP_4 11000001 esz:2 1 10110 111000 ...00 ... 10 \ &zz_e zd=%zd_ax4 zn=%zn_ax4 UZP_4 11000001 001 10111 111000 ...00 ... 10 \ &zz_e esz=4 zd=%zd_ax4 zn=%zn_ax4 + +### SME2 Multi-vector SVE Constructive Binary + +&rshr zd zn shift + +%rshr_sh_shift 16:4 !function=rsub_16 +%rshr_sb_shift 16:5 !function=rsub_32 +%rshr_dh_shift 22:1 16:5 !function=rsub_64 + +@rshr_sh ........ .... .... ...... ..... zd:5 \ + &rshr zn=%zn_ax2 shift=%rshr_sh_shift +@rshr_sb ........ ... ..... ...... ..... zd:5 \ + &rshr zn=%zn_ax4 shift=%rshr_sb_shift +@rshr_dh ........ ... ..... ...... ..... zd:5 \ + &rshr zn=%zn_ax4 shift=%rshr_dh_shift + +SQRSHR_sh 11000001 1110 .... 110101 ....0 ..... @rshr_sh +UQRSHR_sh 11000001 1110 .... 110101 ....1 ..... @rshr_sh +SQRSHRU_sh 11000001 1111 .... 110101 ....0 ..... @rshr_sh + +SQRSHR_sb 11000001 011 ..... 110110 ...00 ..... @rshr_sb +SQRSHR_dh 11000001 1.1 ..... 110110 ...00 ..... @rshr_dh +UQRSHR_sb 11000001 011 ..... 110110 ...01 ..... @rshr_sb +UQRSHR_dh 11000001 1.1 ..... 110110 ...01 ..... @rshr_dh +SQRSHRU_sb 11000001 011 ..... 110110 ...10 ..... @rshr_sb +SQRSHRU_dh 11000001 1.1 ..... 110110 ...10 ..... @rshr_dh + +SQRSHRN_sh 01000101 1011 .... 001010 ....0 ..... @rshr_sh +UQRSHRN_sh 01000101 1011 .... 001110 ....0 ..... @rshr_sh +SQRSHRUN_sh 01000101 1011 .... 000010 ....0 ..... @rshr_sh + +SQRSHRN_sb 11000001 011 ..... 110111 ...00 ..... @rshr_sb +SQRSHRN_dh 11000001 1.1 ..... 110111 ...00 ..... @rshr_dh +UQRSHRN_sb 11000001 011 ..... 110111 ...01 ..... @rshr_sb +UQRSHRN_dh 11000001 1.1 ..... 110111 ...01 ..... @rshr_dh +SQRSHRUN_sb 11000001 011 ..... 110111 ...10 ..... @rshr_sb +SQRSHRUN_dh 11000001 1.1 ..... 110111 ...10 ..... @rshr_dh diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 4a05fff5fe..2e95fe38cd 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1626,6 +1626,66 @@ SQCVT4(sme2_sqcvtu_dh, int64_t, uint16_t, H8, H2, do_usat_h) #undef SQCVT4 +#define SQRSHR2(NAME, TW, TN, HW, HN, RSHR, SAT) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + ARMVectorReg scratch; \ + size_t oprsz = simd_oprsz(desc), n = oprsz / sizeof(TW); \ + int shift = simd_data(desc); \ + TW *s0 = vs, *s1 = vs + sizeof(ARMVectorReg); \ + TN *d = vd; \ + if (vectors_overlap(vd, 1, vs, 2)) { \ + d = (TN *)&scratch; \ + } \ + for (size_t i = 0; i < n; ++i) { \ + d[HN(i)] = SAT(RSHR(s0[HW(i)], shift)); \ + d[HN(i + n)] = SAT(RSHR(s1[HW(i)], shift)); \ + } \ + if (d != vd) { \ + memcpy(vd, d, oprsz); \ + } \ +} + +SQRSHR2(sme2_sqrshr_sh, int32_t, int16_t, H4, H2, do_srshr, do_ssat_h) +SQRSHR2(sme2_uqrshr_sh, uint32_t, uint16_t, H4, H2, do_urshr, do_usat_h) +SQRSHR2(sme2_sqrshru_sh, int32_t, uint16_t, H4, H2, do_srshr, do_usat_h) + +#undef SQRSHR2 + +#define SQRSHR4(NAME, TW, TN, HW, HN, RSHR, SAT) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + ARMVectorReg scratch; \ + size_t oprsz = simd_oprsz(desc), n = oprsz / sizeof(TW); \ + int shift = simd_data(desc); \ + TW *s0 = vs, *s1 = vs + sizeof(ARMVectorReg); \ + TW *s2 = vs + 2 * sizeof(ARMVectorReg); \ + TW *s3 = vs + 3 * sizeof(ARMVectorReg); \ + TN *d = vd; \ + if (vectors_overlap(vd, 1, vs, 4)) { \ + d = (TN *)&scratch; \ + } \ + for (size_t i = 0; i < n; ++i) { \ + d[HN(i)] = SAT(RSHR(s0[HW(i)], shift)); \ + d[HN(i + n)] = SAT(RSHR(s1[HW(i)], shift)); \ + d[HN(i + 2 * n)] = SAT(RSHR(s2[HW(i)], shift)); \ + d[HN(i + 3 * n)] = SAT(RSHR(s3[HW(i)], shift)); \ + } \ + if (d != vd) { \ + memcpy(vd, d, oprsz); \ + } \ +} + +SQRSHR4(sme2_sqrshr_sb, int32_t, int8_t, H4, H2, do_srshr, do_ssat_b) +SQRSHR4(sme2_uqrshr_sb, uint32_t, uint8_t, H4, H2, do_urshr, do_usat_b) +SQRSHR4(sme2_sqrshru_sb, int32_t, uint8_t, H4, H2, do_srshr, do_usat_b) + +SQRSHR4(sme2_sqrshr_dh, int64_t, int16_t, H8, H2, do_srshr, do_ssat_h) +SQRSHR4(sme2_uqrshr_dh, uint64_t, uint16_t, H8, H2, do_urshr, do_usat_h) +SQRSHR4(sme2_sqrshru_dh, int64_t, uint16_t, H8, H2, do_srshr, do_usat_h) + +#undef SQRSHR4 + /* Convert and interleave */ void HELPER(sme2_bfcvtn)(void *vd, void *vs, float_status *fpst, uint32_t desc) { @@ -1715,6 +1775,66 @@ SQCVTN4(sme2_sqcvtun_dh, int64_t, uint16_t, H8, H2, do_usat_h) #undef SQCVTN4 +#define SQRSHRN2(NAME, TW, TN, HW, HN, RSHR, SAT) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + ARMVectorReg scratch; \ + size_t oprsz = simd_oprsz(desc), n = oprsz / sizeof(TW); \ + int shift = simd_data(desc); \ + TW *s0 = vs, *s1 = vs + sizeof(ARMVectorReg); \ + TN *d = vd; \ + if (vectors_overlap(vd, 1, vs, 2)) { \ + d = (TN *)&scratch; \ + } \ + for (size_t i = 0; i < n; ++i) { \ + d[HN(2 * i + 0)] = SAT(RSHR(s0[HW(i)], shift)); \ + d[HN(2 * i + 1)] = SAT(RSHR(s1[HW(i)], shift)); \ + } \ + if (d != vd) { \ + memcpy(vd, d, oprsz); \ + } \ +} + +SQRSHRN2(sme2_sqrshrn_sh, int32_t, int16_t, H4, H2, do_srshr, do_ssat_h) +SQRSHRN2(sme2_uqrshrn_sh, uint32_t, uint16_t, H4, H2, do_urshr, do_usat_h) +SQRSHRN2(sme2_sqrshrun_sh, int32_t, uint16_t, H4, H2, do_srshr, do_usat_h) + +#undef SQRSHRN2 + +#define SQRSHRN4(NAME, TW, TN, HW, HN, RSHR, SAT) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + ARMVectorReg scratch; \ + size_t oprsz = simd_oprsz(desc), n = oprsz / sizeof(TW); \ + int shift = simd_data(desc); \ + TW *s0 = vs, *s1 = vs + sizeof(ARMVectorReg); \ + TW *s2 = vs + 2 * sizeof(ARMVectorReg); \ + TW *s3 = vs + 3 * sizeof(ARMVectorReg); \ + TN *d = vd; \ + if (vectors_overlap(vd, 1, vs, 4)) { \ + d = (TN *)&scratch; \ + } \ + for (size_t i = 0; i < n; ++i) { \ + d[HN(4 * i + 0)] = SAT(RSHR(s0[HW(i)], shift)); \ + d[HN(4 * i + 1)] = SAT(RSHR(s1[HW(i)], shift)); \ + d[HN(4 * i + 2)] = SAT(RSHR(s2[HW(i)], shift)); \ + d[HN(4 * i + 3)] = SAT(RSHR(s3[HW(i)], shift)); \ + } \ + if (d != vd) { \ + memcpy(vd, d, oprsz); \ + } \ +} + +SQRSHRN4(sme2_sqrshrn_sb, int32_t, int8_t, H4, H1, do_srshr, do_ssat_b) +SQRSHRN4(sme2_uqrshrn_sb, uint32_t, uint8_t, H4, H1, do_urshr, do_usat_b) +SQRSHRN4(sme2_sqrshrun_sb, int32_t, uint8_t, H4, H1, do_srshr, do_usat_b) + +SQRSHRN4(sme2_sqrshrn_dh, int64_t, int16_t, H8, H2, do_srshr, do_ssat_h) +SQRSHRN4(sme2_uqrshrn_dh, uint64_t, uint16_t, H8, H2, do_urshr, do_usat_h) +SQRSHRN4(sme2_sqrshrun_dh, int64_t, uint16_t, H8, H2, do_srshr, do_usat_h) + +#undef SQRSHRN4 + /* Expand and convert */ void HELPER(sme2_fcvt_w)(void *vd, void *vs, float_status *fpst, uint32_t desc) { diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index de9545ee6d..d413efd20e 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1474,3 +1474,36 @@ static gen_helper_gvec_2 * const uzp4_fns[] = { gen_helper_sme2_uzp4_q, }; TRANS_FEAT(UZP_4, aa64_sme2, do_zipuzp_4, a, uzp4_fns) + +static bool do_zz_rshr(DisasContext *s, arg_rshr *a, gen_helper_gvec_2 *fn) +{ + if (sve_access_check(s)) { + int vl = vec_full_reg_size(s); + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, a->zd), + vec_full_reg_offset(s, a->zn), + vl, vl, a->shift, fn); + } + return true; +} + +TRANS_FEAT(SQRSHR_sh, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_sqrshr_sh) +TRANS_FEAT(UQRSHR_sh, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_uqrshr_sh) +TRANS_FEAT(SQRSHRU_sh, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_sqrshru_sh) + +TRANS_FEAT(SQRSHR_sb, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_sqrshr_sb) +TRANS_FEAT(SQRSHR_dh, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_sqrshr_dh) +TRANS_FEAT(UQRSHR_sb, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_uqrshr_sb) +TRANS_FEAT(UQRSHR_dh, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_uqrshr_dh) +TRANS_FEAT(SQRSHRU_sb, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_sqrshru_sb) +TRANS_FEAT(SQRSHRU_dh, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_sqrshru_dh) + +TRANS_FEAT(SQRSHRN_sh, aa64_sme2_or_sve2p1, do_zz_rshr, a, gen_helper_sme2_sqrshrn_sh) +TRANS_FEAT(UQRSHRN_sh, aa64_sme2_or_sve2p1, do_zz_rshr, a, gen_helper_sme2_uqrshrn_sh) +TRANS_FEAT(SQRSHRUN_sh, aa64_sme2_or_sve2p1, do_zz_rshr, a, gen_helper_sme2_sqrshrun_sh) + +TRANS_FEAT(SQRSHRN_sb, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_sqrshrn_sb) +TRANS_FEAT(SQRSHRN_dh, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_sqrshrn_dh) +TRANS_FEAT(UQRSHRN_sb, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_uqrshrn_sb) +TRANS_FEAT(UQRSHRN_dh, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_uqrshrn_dh) +TRANS_FEAT(SQRSHRUN_sb, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_sqrshrun_sb) +TRANS_FEAT(SQRSHRUN_dh, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_sqrshrun_dh) From 7e27088cfc3506c402ea50198cc317315258cadd Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:28 -0600 Subject: [PATCH 2012/2760] target/arm: Implement SME2 ZIP, UZP (two registers) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-66-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 12 +++++++ target/arm/tcg/sme.decode | 12 +++++++ target/arm/tcg/sme_helper.c | 62 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 40 ++++++++++++++++++++++ 4 files changed, 126 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 97428bcd6b..06b95da3c3 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -255,6 +255,18 @@ DEF_HELPER_FLAGS_3(sme2_uunpk4_bh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_uunpk4_hs, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_uunpk4_sd, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_zip2_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_zip2_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_zip2_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_zip2_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_zip2_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sme2_uzp2_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_uzp2_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_uzp2_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_uzp2_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_uzp2_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_3(sme2_zip4_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_zip4_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_zip4_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 05d513efba..c1f73d9f63 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -848,3 +848,15 @@ UQRSHRN_sb 11000001 011 ..... 110111 ...01 ..... @rshr_sb UQRSHRN_dh 11000001 1.1 ..... 110111 ...01 ..... @rshr_dh SQRSHRUN_sb 11000001 011 ..... 110111 ...10 ..... @rshr_sb SQRSHRUN_dh 11000001 1.1 ..... 110111 ...10 ..... @rshr_dh + +&zzz_e zd zn zm esz + +ZIP_2 11000001 esz:2 1 zm:5 110100 zn:5 .... 0 \ + &zzz_e zd=%zd_ax2 +ZIP_2 11000001 00 1 zm:5 110101 zn:5 .... 0 \ + &zzz_e zd=%zd_ax2 esz=4 + +UZP_2 11000001 esz:2 1 zm:5 110100 zn:5 .... 1 \ + &zzz_e zd=%zd_ax2 +UZP_2 11000001 00 1 zm:5 110101 zn:5 .... 1 \ + &zzz_e zd=%zd_ax2 esz=4 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 2e95fe38cd..cf0e655bdd 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1933,6 +1933,37 @@ void HELPER(sme2_ucvtf)(void *vd, void *vs, float_status *fpst, uint32_t desc) } } +#define ZIP2(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + ARMVectorReg scratch[2]; \ + size_t oprsz = simd_oprsz(desc); \ + size_t pairs = oprsz / (sizeof(TYPE) * 2); \ + TYPE *n = vn, *m = vm; \ + if (vectors_overlap(vd, 2, vn, 1)) { \ + n = memcpy(&scratch[0], vn, oprsz); \ + } \ + if (vectors_overlap(vd, 2, vm, 1)) { \ + m = memcpy(&scratch[1], vm, oprsz); \ + } \ + for (size_t r = 0; r < 2; ++r) { \ + TYPE *d = vd + r * sizeof(ARMVectorReg); \ + size_t base = r * pairs; \ + for (size_t p = 0; p < pairs; ++p) { \ + d[H(2 * p + 0)] = n[base + H(p)]; \ + d[H(2 * p + 1)] = m[base + H(p)]; \ + } \ + } \ +} + +ZIP2(sme2_zip2_b, uint8_t, H1) +ZIP2(sme2_zip2_h, uint16_t, H2) +ZIP2(sme2_zip2_s, uint32_t, H4) +ZIP2(sme2_zip2_d, uint64_t, ) +ZIP2(sme2_zip2_q, Int128, ) + +#undef ZIP2 + #define ZIP4(NAME, TYPE, H) \ void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ { \ @@ -1967,6 +1998,37 @@ ZIP4(sme2_zip4_q, Int128, ) #undef ZIP4 +#define UZP2(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + ARMVectorReg scratch[2]; \ + size_t oprsz = simd_oprsz(desc); \ + size_t pairs = oprsz / (sizeof(TYPE) * 2); \ + TYPE *d0 = vd, *d1 = vd + sizeof(ARMVectorReg); \ + if (vectors_overlap(vd, 2, vn, 1)) { \ + vn = memcpy(&scratch[0], vn, oprsz); \ + } \ + if (vectors_overlap(vd, 2, vm, 1)) { \ + vm = memcpy(&scratch[1], vm, oprsz); \ + } \ + for (size_t r = 0; r < 2; ++r) { \ + TYPE *s = r ? vm : vn; \ + size_t base = r * pairs; \ + for (size_t p = 0; p < pairs; ++p) { \ + d0[base + H(p)] = s[H(2 * p + 0)]; \ + d1[base + H(p)] = s[H(2 * p + 1)]; \ + } \ + } \ +} + +UZP2(sme2_uzp2_b, uint8_t, H1) +UZP2(sme2_uzp2_h, uint16_t, H2) +UZP2(sme2_uzp2_s, uint32_t, H4) +UZP2(sme2_uzp2_d, uint64_t, ) +UZP2(sme2_uzp2_q, Int128, ) + +#undef UZP2 + #define UZP4(NAME, TYPE, H) \ void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ { \ diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index d413efd20e..d52ccf2ac5 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1507,3 +1507,43 @@ TRANS_FEAT(UQRSHRN_sb, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_uqrshrn_sb) TRANS_FEAT(UQRSHRN_dh, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_uqrshrn_dh) TRANS_FEAT(SQRSHRUN_sb, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_sqrshrun_sb) TRANS_FEAT(SQRSHRUN_dh, aa64_sme2, do_zz_rshr, a, gen_helper_sme2_sqrshrun_dh) + +static bool do_zipuzp_2(DisasContext *s, arg_zzz_e *a, + gen_helper_gvec_3 * const fn[5]) +{ + int bytes_per_op = 2 << a->esz; + + /* MO_128 can fail the size test. */ + if (s->max_svl < bytes_per_op) { + unallocated_encoding(s); + } else if (sme_sm_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + if (svl < bytes_per_op) { + unallocated_encoding(s); + } else { + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->zd), + vec_full_reg_offset(s, a->zn), + vec_full_reg_offset(s, a->zm), + svl, svl, 0, fn[a->esz]); + } + } + return true; +} + +static gen_helper_gvec_3 * const zip2_fns[] = { + gen_helper_sme2_zip2_b, + gen_helper_sme2_zip2_h, + gen_helper_sme2_zip2_s, + gen_helper_sme2_zip2_d, + gen_helper_sme2_zip2_q, +}; +TRANS_FEAT(ZIP_2, aa64_sme2, do_zipuzp_2, a, zip2_fns) + +static gen_helper_gvec_3 * const uzp2_fns[] = { + gen_helper_sme2_uzp2_b, + gen_helper_sme2_uzp2_h, + gen_helper_sme2_uzp2_s, + gen_helper_sme2_uzp2_d, + gen_helper_sme2_uzp2_q, +}; +TRANS_FEAT(UZP_2, aa64_sme2, do_zipuzp_2, a, uzp2_fns) From 8b61eff8e72fee0d5b0cb937674d77fc99674e6c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:29 -0600 Subject: [PATCH 2013/2760] target/arm: Implement SME2 FCLAMP, SCLAMP, UCLAMP Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-67-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 15 +++++++ target/arm/tcg/sme.decode | 17 ++++++++ target/arm/tcg/sme_helper.c | 56 +++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 75 ++++++++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 06b95da3c3..78ba3d1f20 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -298,3 +298,18 @@ DEF_HELPER_FLAGS_3(sme2_sqrshrun_sb, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_sqrshrn_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_uqrshrn_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_sqrshrun_dh, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sme2_sclamp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_sclamp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_sclamp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_sclamp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sme2_uclamp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_uclamp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_uclamp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sme2_uclamp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sme2_fclamp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sme2_fclamp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sme2_fclamp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sme2_bfclamp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index c1f73d9f63..47adcb5a15 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -860,3 +860,20 @@ UZP_2 11000001 esz:2 1 zm:5 110100 zn:5 .... 1 \ &zzz_e zd=%zd_ax2 UZP_2 11000001 00 1 zm:5 110101 zn:5 .... 1 \ &zzz_e zd=%zd_ax2 esz=4 + +&zzz_en zd zn zm esz n + +FCLAMP 11000001 esz:2 1 zm:5 110000 zn:5 .... 0 \ + &zzz_en zd=%zd_ax2 n=2 +FCLAMP 11000001 esz:2 1 zm:5 110010 zn:5 ...0 0 \ + &zzz_en zd=%zd_ax4 n=4 + +SCLAMP 11000001 esz:2 1 zm:5 110001 zn:5 .... 0 \ + &zzz_en zd=%zd_ax2 n=2 +SCLAMP 11000001 esz:2 1 zm:5 110011 zn:5 ...0 0 \ + &zzz_en zd=%zd_ax4 n=4 + +UCLAMP 11000001 esz:2 1 zm:5 110001 zn:5 .... 1 \ + &zzz_en zd=%zd_ax2 n=2 +UCLAMP 11000001 esz:2 1 zm:5 110011 zn:5 ...0 1 \ + &zzz_en zd=%zd_ax4 n=4 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index cf0e655bdd..8a1f9fbf3d 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -2062,3 +2062,59 @@ UZP4(sme2_uzp4_d, uint64_t, ) UZP4(sme2_uzp4_q, Int128, ) #undef UZP4 + +#define ICLAMP(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ +{ \ + size_t stride = sizeof(ARMVectorReg) / sizeof(TYPE); \ + size_t elements = simd_oprsz(desc) / sizeof(TYPE); \ + size_t nreg = simd_data(desc); \ + TYPE *d = vd, *n = vn, *m = vm; \ + for (size_t e = 0; e < elements; e++) { \ + TYPE nn = n[H(e)], mm = m[H(e)]; \ + for (size_t r = 0; r < nreg; r++) { \ + TYPE *dd = &d[r * stride + H(e)]; \ + *dd = MIN(MAX(*dd, nn), mm); \ + } \ + } \ +} + +ICLAMP(sme2_sclamp_b, int8_t, H1) +ICLAMP(sme2_sclamp_h, int16_t, H2) +ICLAMP(sme2_sclamp_s, int32_t, H4) +ICLAMP(sme2_sclamp_d, int64_t, H8) + +ICLAMP(sme2_uclamp_b, uint8_t, H1) +ICLAMP(sme2_uclamp_h, uint16_t, H2) +ICLAMP(sme2_uclamp_s, uint32_t, H4) +ICLAMP(sme2_uclamp_d, uint64_t, H8) + +#undef ICLAMP + +/* + * Note the argument ordering to minnum and maxnum must match + * the ARM pseudocode so that NaNs are propagated properly. + */ +#define FCLAMP(NAME, TYPE, H) \ +void HELPER(NAME)(void *vd, void *vn, void *vm, \ + float_status *fpst, uint32_t desc) \ +{ \ + size_t stride = sizeof(ARMVectorReg) / sizeof(TYPE); \ + size_t elements = simd_oprsz(desc) / sizeof(TYPE); \ + size_t nreg = simd_data(desc); \ + TYPE *d = vd, *n = vn, *m = vm; \ + for (size_t e = 0; e < elements; e++) { \ + TYPE nn = n[H(e)], mm = m[H(e)]; \ + for (size_t r = 0; r < nreg; r++) { \ + TYPE *dd = &d[r * stride + H(e)]; \ + *dd = TYPE##_minnum(TYPE##_maxnum(nn, *dd, fpst), mm, fpst); \ + } \ + } \ +} + +FCLAMP(sme2_fclamp_h, float16, H2) +FCLAMP(sme2_fclamp_s, float32, H4) +FCLAMP(sme2_fclamp_d, float64, H8) +FCLAMP(sme2_bfclamp, bfloat16, H2) + +#undef FCLAMP diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index d52ccf2ac5..99e4056316 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1547,3 +1547,78 @@ static gen_helper_gvec_3 * const uzp2_fns[] = { gen_helper_sme2_uzp2_q, }; TRANS_FEAT(UZP_2, aa64_sme2, do_zipuzp_2, a, uzp2_fns) + +static bool trans_FCLAMP(DisasContext *s, arg_zzz_en *a) +{ + static gen_helper_gvec_3_ptr * const fn[] = { + gen_helper_sme2_bfclamp, + gen_helper_sme2_fclamp_h, + gen_helper_sme2_fclamp_s, + gen_helper_sme2_fclamp_d, + }; + TCGv_ptr fpst; + int vl; + + if (!dc_isar_feature(aa64_sme2, s)) { + return false; + } + /* This insn uses MO_8 to encode BFloat16. */ + if (a->esz == MO_8 && !dc_isar_feature(aa64_sme_b16b16, s)) { + return false; + } + if (!sme_sm_enabled_check(s)) { + return true; + } + + fpst = fpstatus_ptr(a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); + vl = vec_full_reg_size(s); + + tcg_gen_gvec_3_ptr(vec_full_reg_offset(s, a->zd), + vec_full_reg_offset(s, a->zn), + vec_full_reg_offset(s, a->zm), + fpst, vl, vl, a->n, fn[a->esz]); + return true; +} + +static bool do_clamp(DisasContext *s, arg_zzz_en *a, + gen_helper_gvec_3 * const fn[4]) +{ + int vl; + + if (!dc_isar_feature(aa64_sme2, s)) { + return false; + } + if (!sme_sm_enabled_check(s)) { + return true; + } + + /* + * Clamp is just a min+max, easily supported by most host + * vector operations -- we already have such an expansion in + * translate-sve.c for a single output. + * TODO: Add support in gvec for multiple simultaneous output, + * and/or copy to temporary upon overlap. + */ + vl = vec_full_reg_size(s); + tcg_gen_gvec_3_ool(vec_full_reg_offset(s, a->zd), + vec_full_reg_offset(s, a->zn), + vec_full_reg_offset(s, a->zm), + vl, vl, a->n, fn[a->esz]); + return true; +} + +static gen_helper_gvec_3 * const sclamp_fns[] = { + gen_helper_sme2_sclamp_b, + gen_helper_sme2_sclamp_h, + gen_helper_sme2_sclamp_s, + gen_helper_sme2_sclamp_d, +}; +TRANS(SCLAMP, do_clamp, a, sclamp_fns) + +static gen_helper_gvec_3 * const uclamp_fns[] = { + gen_helper_sme2_uclamp_b, + gen_helper_sme2_uclamp_h, + gen_helper_sme2_uclamp_s, + gen_helper_sme2_uclamp_d, +}; +TRANS(UCLAMP, do_clamp, a, uclamp_fns) From c1317025d83a298be82a2d05586a6a23684ad877 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:30 -0600 Subject: [PATCH 2014/2760] target/arm: Enable SCLAMP, UCLAMP for SVE2p1 These instructions are present in both SME(1) and SVE2.1 extensions. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-68-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/translate-sve.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index ac4dc7db46..ff70bf27b0 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -7375,7 +7375,7 @@ static void gen_sclamp(unsigned vece, uint32_t d, uint32_t n, uint32_t m, tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &ops[vece]); } -TRANS_FEAT(SCLAMP, aa64_sme, gen_gvec_fn_arg_zzzz, gen_sclamp, a) +TRANS_FEAT(SCLAMP, aa64_sme_or_sve2p1, gen_gvec_fn_arg_zzzz, gen_sclamp, a) static void gen_uclamp_i32(TCGv_i32 d, TCGv_i32 n, TCGv_i32 m, TCGv_i32 a) { @@ -7426,7 +7426,7 @@ static void gen_uclamp(unsigned vece, uint32_t d, uint32_t n, uint32_t m, tcg_gen_gvec_4(d, n, m, a, oprsz, maxsz, &ops[vece]); } -TRANS_FEAT(UCLAMP, aa64_sme, gen_gvec_fn_arg_zzzz, gen_uclamp, a) +TRANS_FEAT(UCLAMP, aa64_sme_or_sve2p1, gen_gvec_fn_arg_zzzz, gen_uclamp, a) TRANS_FEAT(SQCVTN_sh, aa64_sme2_or_sve2p1, gen_gvec_ool_zz, gen_helper_sme2_sqcvtn_sh, a->rd, a->rn, 0) From e9b743947b154b51dad778e31e323c8ef3133a52 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:31 -0600 Subject: [PATCH 2015/2760] target/arm: Implement FCLAMP for SME2, SVE2p1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is the single vector version within SVE decode space. Tested-by: Alex Bennée Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-69-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve.decode | 2 ++ target/arm/tcg/translate-sve.c | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index f808362f23..dcfc40ef44 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1721,3 +1721,5 @@ PSEL 00100101 .1 1 000 .. 01 .... 0 .... 0 .... \ SCLAMP 01000100 .. 0 ..... 110000 ..... ..... @rda_rn_rm UCLAMP 01000100 .. 0 ..... 110001 ..... ..... @rda_rn_rm + +FCLAMP 01100100 .. 1 ..... 001001 ..... ..... @rda_rn_rm diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index ff70bf27b0..210a029ab8 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -7428,6 +7428,28 @@ static void gen_uclamp(unsigned vece, uint32_t d, uint32_t n, uint32_t m, TRANS_FEAT(UCLAMP, aa64_sme_or_sve2p1, gen_gvec_fn_arg_zzzz, gen_uclamp, a) +static bool trans_FCLAMP(DisasContext *s, arg_FCLAMP *a) +{ + static gen_helper_gvec_3_ptr * const fn[] = { + gen_helper_sme2_bfclamp, + gen_helper_sme2_fclamp_h, + gen_helper_sme2_fclamp_s, + gen_helper_sme2_fclamp_d, + }; + + /* This insn uses MO_8 to encode BFloat16. */ + if (a->esz == MO_8 + ? !dc_isar_feature(aa64_sve_b16b16, s) + : !dc_isar_feature(aa64_sme2_or_sve2p1, s)) { + return false; + } + + /* So far we never optimize rda with MOVPRFX */ + assert(a->rd == a->ra); + return gen_gvec_fpst_zzz(s, fn[a->esz], a->rd, a->rn, a->rm, 1, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); +} + TRANS_FEAT(SQCVTN_sh, aa64_sme2_or_sve2p1, gen_gvec_ool_zz, gen_helper_sme2_sqcvtn_sh, a->rd, a->rn, 0) TRANS_FEAT(UQCVTN_sh, aa64_sme2_or_sve2p1, gen_gvec_ool_zz, From f8f65ebc0ab747cd233ace17318b268593f56910 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:32 -0600 Subject: [PATCH 2016/2760] target/arm: Implement SME2p1 Multiple Zero Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-70-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme.decode | 23 +++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 20 ++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 47adcb5a15..c4b85a36d0 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -877,3 +877,26 @@ UCLAMP 11000001 esz:2 1 zm:5 110001 zn:5 .... 1 \ &zzz_en zd=%zd_ax2 n=2 UCLAMP 11000001 esz:2 1 zm:5 110011 zn:5 ...0 1 \ &zzz_en zd=%zd_ax4 n=4 + +### SME Multiple Zero + +&zero_za rv off ngrp nvec + +ZERO_za 11000000 000011 000 .. 0000000000 off:3 \ + &zero_za ngrp=2 nvec=1 rv=%mova_rv +ZERO_za 11000000 000011 100 .. 0000000000 off:3 \ + &zero_za ngrp=4 nvec=1 rv=%mova_rv + +ZERO_za 11000000 000011 001 .. 0000000000 ... \ + &zero_za ngrp=1 nvec=2 rv=%mova_rv off=%off3_x2 +ZERO_za 11000000 000011 010 .. 0000000000 0.. \ + &zero_za ngrp=2 nvec=2 rv=%mova_rv off=%off2_x2 +ZERO_za 11000000 000011 011 .. 0000000000 0.. \ + &zero_za ngrp=4 nvec=2 rv=%mova_rv off=%off2_x2 + +ZERO_za 11000000 000011 101 .. 0000000000 0.. \ + &zero_za ngrp=1 nvec=4 rv=%mova_rv off=%off2_x4 +ZERO_za 11000000 000011 110 .. 0000000000 00. \ + &zero_za ngrp=2 nvec=4 rv=%mova_rv off=%off1_x4 +ZERO_za 11000000 000011 111 .. 0000000000 00. \ + &zero_za ngrp=4 nvec=4 rv=%mova_rv off=%off1_x4 diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 99e4056316..b6316ac716 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -173,6 +173,26 @@ static bool trans_ZERO_zt0(DisasContext *s, arg_ZERO_zt0 *a) return true; } +static bool trans_ZERO_za(DisasContext *s, arg_ZERO_za *a) +{ + if (!dc_isar_feature(aa64_sme2p1, s)) { + return false; + } + if (sme_smza_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + int vstride = svl / a->ngrp; + TCGv_ptr t_za = get_zarray(s, a->rv, a->off, a->ngrp, a->nvec); + + for (int r = 0; r < a->ngrp; ++r) { + for (int i = 0; i < a->nvec; ++i) { + int o_za = (r * vstride + i) * sizeof(ARMVectorReg); + tcg_gen_gvec_dup_imm_var(MO_64, t_za, o_za, svl, svl, 0); + } + } + } + return true; +} + static bool do_mova_tile(DisasContext *s, arg_mova_p *a, bool to_vec) { static gen_helper_gvec_4 * const h_fns[5] = { From bcbe82a29396e9f007e1575da0bb4e7e57451c71 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:33 -0600 Subject: [PATCH 2017/2760] target/arm: Introduce pred_count_test For WHILE, we have the count of enabled predicates, so we don't need to search to compute the PredTest result. Reuse the logic that will shortly be required for counted predicates. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250704142112.1018902-71-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve_helper.c | 79 +++++++++++++++++++++---------------- 1 file changed, 45 insertions(+), 34 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 0e59ad2262..5b5871ba13 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4102,30 +4102,46 @@ uint64_t HELPER(sve_cntp)(void *vn, void *vg, uint32_t pred_desc) return sum; } +/* C.f. Arm pseudocode PredCountTest */ +static uint32_t pred_count_test(uint32_t elements, uint32_t count, bool invert) +{ + uint32_t flags; + + if (count == 0) { + flags = 1; /* !N, Z, C */ + } else if (!invert) { + flags = (1u << 31) | 2; /* N, !Z */ + flags |= count != elements; /* C */ + } else { + flags = 2; /* !Z, !C */ + flags |= (count == elements) << 31; /* N */ + } + return flags; +} + uint32_t HELPER(sve_whilel)(void *vd, uint32_t count, uint32_t pred_desc) { intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); intptr_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); uint64_t esz_mask = pred_esz_masks[esz]; ARMPredicateReg *d = vd; - uint32_t flags; - intptr_t i; + intptr_t i, oprbits = oprsz * 8; - /* Begin with a zero predicate register. */ - flags = do_zero(d, oprsz); - if (count == 0) { - return flags; - } + tcg_debug_assert(count <= oprbits); - /* Set all of the requested bits. */ - for (i = 0; i < count / 64; ++i) { - d->p[i] = esz_mask; - } - if (count & 63) { - d->p[i] = MAKE_64BIT_MASK(0, count & 63) & esz_mask; + /* Begin with a zero predicate register. */ + do_zero(d, oprsz); + if (count) { + /* Set all of the requested bits. */ + for (i = 0; i < count / 64; ++i) { + d->p[i] = esz_mask; + } + if (count & 63) { + d->p[i] = MAKE_64BIT_MASK(0, count & 63) & esz_mask; + } } - return predtest_ones(d, oprsz, esz_mask); + return pred_count_test(oprbits, count, false); } uint32_t HELPER(sve_whileg)(void *vd, uint32_t count, uint32_t pred_desc) @@ -4134,34 +4150,29 @@ uint32_t HELPER(sve_whileg)(void *vd, uint32_t count, uint32_t pred_desc) intptr_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); uint64_t esz_mask = pred_esz_masks[esz]; ARMPredicateReg *d = vd; - intptr_t i, invcount, oprbits; + intptr_t i, invcount, oprbits = oprsz * 8; uint64_t bits; - if (count == 0) { - return do_zero(d, oprsz); - } - - oprbits = oprsz * 8; tcg_debug_assert(count <= oprbits); - bits = esz_mask; - if (oprbits & 63) { - bits &= MAKE_64BIT_MASK(0, oprbits & 63); - } - - invcount = oprbits - count; - for (i = (oprsz - 1) / 8; i > invcount / 64; --i) { - d->p[i] = bits; + /* Begin with a zero predicate register. */ + do_zero(d, oprsz); + if (count) { + /* Set all of the requested bits. */ bits = esz_mask; - } - - d->p[i] = bits & MAKE_64BIT_MASK(invcount & 63, 64); + if (oprbits & 63) { + bits &= MAKE_64BIT_MASK(0, oprbits & 63); + } - while (--i >= 0) { - d->p[i] = 0; + invcount = oprbits - count; + for (i = (oprsz - 1) / 8; i > invcount / 64; --i) { + d->p[i] = bits; + bits = esz_mask; + } + d->p[i] = bits & MAKE_64BIT_MASK(invcount & 63, 64); } - return predtest_ones(d, oprsz, esz_mask); + return pred_count_test(oprbits, count, true); } /* Recursive reduction on a function; From 18914dfba78e64148219ba9611b0019c0a81ff52 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:34 -0600 Subject: [PATCH 2018/2760] target/arm: Fold predtest_ones into helper_sve_brkns Merge predtest_ones into its only caller. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-72-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve_helper.c | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 5b5871ba13..492e42acc8 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4061,31 +4061,25 @@ void HELPER(sve_brkn)(void *vd, void *vn, void *vg, uint32_t pred_desc) } } -/* As if PredTest(Ones(PL), D, esz). */ -static uint32_t predtest_ones(ARMPredicateReg *d, intptr_t oprsz, - uint64_t esz_mask) -{ - uint32_t flags = PREDTEST_INIT; - intptr_t i; - - for (i = 0; i < oprsz / 8; i++) { - flags = iter_predtest_fwd(d->p[i], esz_mask, flags); - } - if (oprsz & 7) { - uint64_t mask = ~(-1ULL << (8 * (oprsz & 7))); - flags = iter_predtest_fwd(d->p[i], esz_mask & mask, flags); - } - return flags; -} - uint32_t HELPER(sve_brkns)(void *vd, void *vn, void *vg, uint32_t pred_desc) { intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); if (last_active_pred(vn, vg, oprsz)) { - return predtest_ones(vd, oprsz, -1); - } else { - return do_zero(vd, oprsz); + ARMPredicateReg *d = vd; + uint32_t flags = PREDTEST_INIT; + intptr_t i; + + /* As if PredTest(Ones(PL), D, MO_8). */ + for (i = 0; i < oprsz / 8; i++) { + flags = iter_predtest_fwd(d->p[i], -1, flags); + } + if (oprsz & 7) { + uint64_t mask = ~(-1ULL << (8 * (oprsz & 7))); + flags = iter_predtest_fwd(d->p[i], mask, flags); + } + return flags; } + return do_zero(vd, oprsz); } uint64_t HELPER(sve_cntp)(void *vn, void *vg, uint32_t pred_desc) From 91afd009bab55b0b479e6fb0aadc9366e9bd6209 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:35 -0600 Subject: [PATCH 2019/2760] target/arm: Expand do_zero inline Expand to memset plus the return value, when used. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250704142112.1018902-73-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve_helper.c | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 492e42acc8..a62a647f3a 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -3952,15 +3952,6 @@ static uint32_t compute_brks_m(uint64_t *d, uint64_t *n, uint64_t *g, return flags; } -static uint32_t do_zero(ARMPredicateReg *d, intptr_t oprsz) -{ - /* It is quicker to zero the whole predicate than loop on OPRSZ. - * The compiler should turn this into 4 64-bit integer stores. - */ - memset(d, 0, sizeof(ARMPredicateReg)); - return PREDTEST_INIT; -} - void HELPER(sve_brkpa)(void *vd, void *vn, void *vm, void *vg, uint32_t pred_desc) { @@ -3968,7 +3959,7 @@ void HELPER(sve_brkpa)(void *vd, void *vn, void *vm, void *vg, if (last_active_pred(vn, vg, oprsz)) { compute_brk_z(vd, vm, vg, oprsz, true); } else { - do_zero(vd, oprsz); + memset(vd, 0, sizeof(ARMPredicateReg)); } } @@ -3979,7 +3970,8 @@ uint32_t HELPER(sve_brkpas)(void *vd, void *vn, void *vm, void *vg, if (last_active_pred(vn, vg, oprsz)) { return compute_brks_z(vd, vm, vg, oprsz, true); } else { - return do_zero(vd, oprsz); + memset(vd, 0, sizeof(ARMPredicateReg)); + return PREDTEST_INIT; } } @@ -3990,7 +3982,7 @@ void HELPER(sve_brkpb)(void *vd, void *vn, void *vm, void *vg, if (last_active_pred(vn, vg, oprsz)) { compute_brk_z(vd, vm, vg, oprsz, false); } else { - do_zero(vd, oprsz); + memset(vd, 0, sizeof(ARMPredicateReg)); } } @@ -4001,7 +3993,8 @@ uint32_t HELPER(sve_brkpbs)(void *vd, void *vn, void *vm, void *vg, if (last_active_pred(vn, vg, oprsz)) { return compute_brks_z(vd, vm, vg, oprsz, false); } else { - return do_zero(vd, oprsz); + memset(vd, 0, sizeof(ARMPredicateReg)); + return PREDTEST_INIT; } } @@ -4057,7 +4050,7 @@ void HELPER(sve_brkn)(void *vd, void *vn, void *vg, uint32_t pred_desc) { intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); if (!last_active_pred(vn, vg, oprsz)) { - do_zero(vd, oprsz); + memset(vd, 0, sizeof(ARMPredicateReg)); } } @@ -4079,7 +4072,8 @@ uint32_t HELPER(sve_brkns)(void *vd, void *vn, void *vg, uint32_t pred_desc) } return flags; } - return do_zero(vd, oprsz); + memset(vd, 0, sizeof(ARMPredicateReg)); + return PREDTEST_INIT; } uint64_t HELPER(sve_cntp)(void *vn, void *vg, uint32_t pred_desc) @@ -4124,7 +4118,7 @@ uint32_t HELPER(sve_whilel)(void *vd, uint32_t count, uint32_t pred_desc) tcg_debug_assert(count <= oprbits); /* Begin with a zero predicate register. */ - do_zero(d, oprsz); + memset(d, 0, sizeof(*d)); if (count) { /* Set all of the requested bits. */ for (i = 0; i < count / 64; ++i) { @@ -4150,7 +4144,7 @@ uint32_t HELPER(sve_whileg)(void *vd, uint32_t count, uint32_t pred_desc) tcg_debug_assert(count <= oprbits); /* Begin with a zero predicate register. */ - do_zero(d, oprsz); + memset(d, 0, sizeof(*d)); if (count) { /* Set all of the requested bits. */ bits = esz_mask; From 56ddef77944b4441f199b5e15c8fc1a058fd3335 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:36 -0600 Subject: [PATCH 2020/2760] target/arm: Split out do_whilel from helper_sve_whilel Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-74-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve_helper.c | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index a62a647f3a..741d04309c 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4107,19 +4107,14 @@ static uint32_t pred_count_test(uint32_t elements, uint32_t count, bool invert) return flags; } -uint32_t HELPER(sve_whilel)(void *vd, uint32_t count, uint32_t pred_desc) +/* D must be cleared on entry. */ +static void do_whilel(ARMPredicateReg *d, uint64_t esz_mask, + uint32_t count, uint32_t oprbits) { - intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); - intptr_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); - uint64_t esz_mask = pred_esz_masks[esz]; - ARMPredicateReg *d = vd; - intptr_t i, oprbits = oprsz * 8; - tcg_debug_assert(count <= oprbits); - - /* Begin with a zero predicate register. */ - memset(d, 0, sizeof(*d)); if (count) { + uint32_t i; + /* Set all of the requested bits. */ for (i = 0; i < count / 64; ++i) { d->p[i] = esz_mask; @@ -4128,7 +4123,18 @@ uint32_t HELPER(sve_whilel)(void *vd, uint32_t count, uint32_t pred_desc) d->p[i] = MAKE_64BIT_MASK(0, count & 63) & esz_mask; } } +} + +uint32_t HELPER(sve_whilel)(void *vd, uint32_t count, uint32_t pred_desc) +{ + uint32_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); + uint32_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); + uint32_t oprbits = oprsz * 8; + uint64_t esz_mask = pred_esz_masks[esz]; + ARMPredicateReg *d = vd; + memset(d, 0, sizeof(*d)); + do_whilel(d, esz_mask, count, oprbits); return pred_count_test(oprbits, count, false); } From 3fd8ce429447db5d1c1302887c68bc23a73a8a81 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:37 -0600 Subject: [PATCH 2021/2760] target/arm: Split out do_whileg from helper_sve_whileg Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Message-id: 20250704142112.1018902-75-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve_helper.c | 39 +++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 741d04309c..5e11e86e5e 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4138,34 +4138,35 @@ uint32_t HELPER(sve_whilel)(void *vd, uint32_t count, uint32_t pred_desc) return pred_count_test(oprbits, count, false); } -uint32_t HELPER(sve_whileg)(void *vd, uint32_t count, uint32_t pred_desc) +/* D must be cleared on entry. */ +static void do_whileg(ARMPredicateReg *d, uint64_t esz_mask, + uint32_t count, uint32_t oprbits) { - intptr_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); - intptr_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); - uint64_t esz_mask = pred_esz_masks[esz]; - ARMPredicateReg *d = vd; - intptr_t i, invcount, oprbits = oprsz * 8; - uint64_t bits; - tcg_debug_assert(count <= oprbits); - - /* Begin with a zero predicate register. */ - memset(d, 0, sizeof(*d)); if (count) { - /* Set all of the requested bits. */ - bits = esz_mask; - if (oprbits & 63) { - bits &= MAKE_64BIT_MASK(0, oprbits & 63); - } + uint32_t i, invcount = oprbits - count; + uint64_t bits = esz_mask & MAKE_64BIT_MASK(invcount & 63, 64); - invcount = oprbits - count; - for (i = (oprsz - 1) / 8; i > invcount / 64; --i) { + for (i = invcount / 64; i < oprbits / 64; ++i) { d->p[i] = bits; bits = esz_mask; } - d->p[i] = bits & MAKE_64BIT_MASK(invcount & 63, 64); + if (oprbits & 63) { + d->p[i] = bits & MAKE_64BIT_MASK(0, oprbits & 63); + } } +} + +uint32_t HELPER(sve_whileg)(void *vd, uint32_t count, uint32_t pred_desc) +{ + uint32_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); + uint32_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); + uint32_t oprbits = oprsz * 8; + uint64_t esz_mask = pred_esz_masks[esz]; + ARMPredicateReg *d = vd; + memset(d, 0, sizeof(*d)); + do_whileg(d, esz_mask, count, oprbits); return pred_count_test(oprbits, count, true); } From f96fd13c6ea030871e114c0d274e61d0cd5ff3ca Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:38 -0600 Subject: [PATCH 2022/2760] target/arm: Move scale by esz into helper_sve_while* Change the API to pass element count rather than bit count. This will be helpful later for predicate as counter. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-76-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve_helper.c | 2 ++ target/arm/tcg/translate-sve.c | 13 +++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 5e11e86e5e..b8f3fbb816 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4133,6 +4133,7 @@ uint32_t HELPER(sve_whilel)(void *vd, uint32_t count, uint32_t pred_desc) uint64_t esz_mask = pred_esz_masks[esz]; ARMPredicateReg *d = vd; + count <<= esz; memset(d, 0, sizeof(*d)); do_whilel(d, esz_mask, count, oprbits); return pred_count_test(oprbits, count, false); @@ -4165,6 +4166,7 @@ uint32_t HELPER(sve_whileg)(void *vd, uint32_t count, uint32_t pred_desc) uint64_t esz_mask = pred_esz_masks[esz]; ARMPredicateReg *d = vd; + count <<= esz; memset(d, 0, sizeof(*d)); do_whileg(d, esz_mask, count, oprbits); return pred_count_test(oprbits, count, true); diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 210a029ab8..f74f2bb1b3 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3198,9 +3198,6 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a) t2 = tcg_temp_new_i32(); tcg_gen_extrl_i64_i32(t2, t0); - /* Scale elements to bits. */ - tcg_gen_shli_i32(t2, t2, a->esz); - desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz / 8); desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); @@ -3234,7 +3231,7 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) op0 = read_cpu_reg(s, a->rn, 1); op1 = read_cpu_reg(s, a->rm, 1); - tmax = tcg_constant_i64(vsz); + tmax = tcg_constant_i64(vsz >> a->esz); diff = tcg_temp_new_i64(); if (a->rw) { @@ -3244,15 +3241,15 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) tcg_gen_sub_i64(diff, op0, op1); tcg_gen_sub_i64(t1, op1, op0); tcg_gen_movcond_i64(TCG_COND_GEU, diff, op0, op1, diff, t1); - /* Round down to a multiple of ESIZE. */ - tcg_gen_andi_i64(diff, diff, -1 << a->esz); + /* Divide, rounding down, by ESIZE. */ + tcg_gen_shri_i64(diff, diff, a->esz); /* If op1 == op0, diff == 0, and the condition is always true. */ tcg_gen_movcond_i64(TCG_COND_EQ, diff, op0, op1, tmax, diff); } else { /* WHILEWR */ tcg_gen_sub_i64(diff, op1, op0); - /* Round down to a multiple of ESIZE. */ - tcg_gen_andi_i64(diff, diff, -1 << a->esz); + /* Divide, rounding down, by ESIZE. */ + tcg_gen_shri_i64(diff, diff, a->esz); /* If op0 >= op1, diff <= 0, the condition is always true. */ tcg_gen_movcond_i64(TCG_COND_GEU, diff, op0, op1, tmax, diff); } From 54e260bbfc3fb4c6817d9a2457bdc9ef4d142f3a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:39 -0600 Subject: [PATCH 2023/2760] target/arm: Split trans_WHILE to lt and gt Use TRANS_FEAT to select the correct predicate. Pass the helper and a boolean to do_WHILE. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-77-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve.decode | 4 +++- target/arm/tcg/translate-sve.c | 23 +++++++++-------------- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index dcfc40ef44..c90d975ebf 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -789,7 +789,9 @@ SINCDECP_z 00100101 .. 1010 d:1 u:1 10000 00 .... ..... @incdec2_pred CTERM 00100101 1 sf:1 1 rm:5 001000 rn:5 ne:1 0000 # SVE integer compare scalar count and limit -WHILE 00100101 esz:2 1 rm:5 000 sf:1 u:1 lt:1 rn:5 eq:1 rd:4 +&while esz rd rn rm sf u eq +WHILE_lt 00100101 esz:2 1 rm:5 000 sf:1 u:1 1 rn:5 eq:1 rd:4 &while +WHILE_gt 00100101 esz:2 1 rm:5 000 sf:1 u:1 0 rn:5 eq:1 rd:4 &while # SVE2 pointer conflict compare WHILE_ptr 00100101 esz:2 1 rm:5 001 100 rn:5 rw:1 rd:4 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index f74f2bb1b3..62d3e2efd6 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3108,7 +3108,8 @@ static bool trans_CTERM(DisasContext *s, arg_CTERM *a) return true; } -static bool trans_WHILE(DisasContext *s, arg_WHILE *a) +typedef void gen_while_fn(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32); +static bool do_WHILE(DisasContext *s, arg_while *a, bool lt, gen_while_fn *fn) { TCGv_i64 op0, op1, t0, t1, tmax; TCGv_i32 t2; @@ -3118,14 +3119,8 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a) TCGCond cond; uint64_t maxval; /* Note that GE/HS has a->eq == 0 and GT/HI has a->eq == 1. */ - bool eq = a->eq == a->lt; + bool eq = a->eq == lt; - /* The greater-than conditions are all SVE2. */ - if (a->lt - ? !dc_isar_feature(aa64_sve, s) - : !dc_isar_feature(aa64_sve2, s)) { - return false; - } if (!sve_access_check(s)) { return true; } @@ -3149,7 +3144,7 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a) t0 = tcg_temp_new_i64(); t1 = tcg_temp_new_i64(); - if (a->lt) { + if (lt) { tcg_gen_sub_i64(t0, op1, op0); if (a->u) { maxval = a->sf ? UINT64_MAX : UINT32_MAX; @@ -3204,15 +3199,15 @@ static bool trans_WHILE(DisasContext *s, arg_WHILE *a) ptr = tcg_temp_new_ptr(); tcg_gen_addi_ptr(ptr, tcg_env, pred_full_reg_offset(s, a->rd)); - if (a->lt) { - gen_helper_sve_whilel(t2, ptr, t2, tcg_constant_i32(desc)); - } else { - gen_helper_sve_whileg(t2, ptr, t2, tcg_constant_i32(desc)); - } + fn(t2, ptr, t2, tcg_constant_i32(desc)); + do_pred_flags(t2); return true; } +TRANS_FEAT(WHILE_lt, aa64_sve, do_WHILE, a, true, gen_helper_sve_whilel) +TRANS_FEAT(WHILE_gt, aa64_sve2, do_WHILE, a, false, gen_helper_sve_whileg) + static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) { TCGv_i64 op0, op1, diff, t1, tmax; From b61f4530535a483688e76a925f6203fa22e332e8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:40 -0600 Subject: [PATCH 2024/2760] target/arm: Enable PSEL for SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-78-richard.henderson@linaro.org This instruction is present in both SME(1) and SVE2.1 extensions. Signed-off-by: Richard Henderson Signed-off-by: Peter Maydell --- target/arm/tcg/translate-sve.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 62d3e2efd6..cb60b533e9 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -7273,7 +7273,7 @@ static bool trans_PSEL(DisasContext *s, arg_psel *a) TCGv_i64 tmp, didx, dbit; TCGv_ptr ptr; - if (!dc_isar_feature(aa64_sme, s)) { + if (!dc_isar_feature(aa64_sme_or_sve2p1, s)) { return false; } if (!sve_access_check(s)) { From a3dde8e382cd90e5b48c363255bd9d8ef31d4232 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:41 -0600 Subject: [PATCH 2025/2760] target/arm: Implement SVE2p1 WHILE (predicate pair) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-79-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 3 +++ target/arm/tcg/sve.decode | 8 +++++++ target/arm/tcg/sve_helper.c | 40 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sve.c | 14 ++++++++---- 4 files changed, 61 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 0b1b588783..eac23e75b9 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -941,6 +941,9 @@ DEF_HELPER_FLAGS_3(sve_cntp, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_whilel, TCG_CALL_NO_RWG, i32, ptr, i32, i32) DEF_HELPER_FLAGS_3(sve_whileg, TCG_CALL_NO_RWG, i32, ptr, i32, i32) +DEF_HELPER_FLAGS_3(sve_while2l, TCG_CALL_NO_RWG, i32, ptr, i32, i32) +DEF_HELPER_FLAGS_3(sve_while2g, TCG_CALL_NO_RWG, i32, ptr, i32, i32) + DEF_HELPER_FLAGS_4(sve_subri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_subri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_subri_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index c90d975ebf..968b2a57e9 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -796,6 +796,14 @@ WHILE_gt 00100101 esz:2 1 rm:5 000 sf:1 u:1 0 rn:5 eq:1 rd:4 &while # SVE2 pointer conflict compare WHILE_ptr 00100101 esz:2 1 rm:5 001 100 rn:5 rw:1 rd:4 +# SVE2.1 predicate pair +%pd_pair 1:3 !function=times_2 +@while_pair ........ esz:2 . rm:5 .... u:1 . rn:5 . ... eq:1 \ + &while rd=%pd_pair sf=1 + +WHILE_lt_pair 00100101 .. 1 ..... 0101 . 1 ..... 1 ... . @while_pair +WHILE_gt_pair 00100101 .. 1 ..... 0101 . 0 ..... 1 ... . @while_pair + ### SVE Integer Wide Immediate - Unpredicated Group # SVE broadcast floating-point immediate (unpredicated) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index b8f3fbb816..30394f4880 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4139,6 +4139,26 @@ uint32_t HELPER(sve_whilel)(void *vd, uint32_t count, uint32_t pred_desc) return pred_count_test(oprbits, count, false); } +uint32_t HELPER(sve_while2l)(void *vd, uint32_t count, uint32_t pred_desc) +{ + uint32_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); + uint32_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); + uint32_t oprbits = oprsz * 8; + uint64_t esz_mask = pred_esz_masks[esz]; + ARMPredicateReg *d = vd; + + count <<= esz; + memset(d, 0, 2 * sizeof(*d)); + if (count <= oprbits) { + do_whilel(&d[0], esz_mask, count, oprbits); + } else { + do_whilel(&d[0], esz_mask, oprbits, oprbits); + do_whilel(&d[1], esz_mask, count - oprbits, oprbits); + } + + return pred_count_test(2 * oprbits, count, false); +} + /* D must be cleared on entry. */ static void do_whileg(ARMPredicateReg *d, uint64_t esz_mask, uint32_t count, uint32_t oprbits) @@ -4172,6 +4192,26 @@ uint32_t HELPER(sve_whileg)(void *vd, uint32_t count, uint32_t pred_desc) return pred_count_test(oprbits, count, true); } +uint32_t HELPER(sve_while2g)(void *vd, uint32_t count, uint32_t pred_desc) +{ + uint32_t oprsz = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); + uint32_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); + uint32_t oprbits = oprsz * 8; + uint64_t esz_mask = pred_esz_masks[esz]; + ARMPredicateReg *d = vd; + + count <<= esz; + memset(d, 0, 2 * sizeof(*d)); + if (count <= oprbits) { + do_whileg(&d[1], esz_mask, count, oprbits); + } else { + do_whilel(&d[1], esz_mask, oprbits, oprbits); + do_whileg(&d[0], esz_mask, count - oprbits, oprbits); + } + + return pred_count_test(2 * oprbits, count, true); +} + /* Recursive reduction on a function; * C.f. the ARM ARM function ReducePredicated. * diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index cb60b533e9..de6ffe7511 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3109,7 +3109,8 @@ static bool trans_CTERM(DisasContext *s, arg_CTERM *a) } typedef void gen_while_fn(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32); -static bool do_WHILE(DisasContext *s, arg_while *a, bool lt, gen_while_fn *fn) +static bool do_WHILE(DisasContext *s, arg_while *a, + bool lt, int scale, gen_while_fn *fn) { TCGv_i64 op0, op1, t0, t1, tmax; TCGv_i32 t2; @@ -3164,7 +3165,7 @@ static bool do_WHILE(DisasContext *s, arg_while *a, bool lt, gen_while_fn *fn) } } - tmax = tcg_constant_i64(vsz >> a->esz); + tmax = tcg_constant_i64((vsz << scale) >> a->esz); if (eq) { /* Equality means one more iteration. */ tcg_gen_addi_i64(t0, t0, 1); @@ -3205,8 +3206,13 @@ static bool do_WHILE(DisasContext *s, arg_while *a, bool lt, gen_while_fn *fn) return true; } -TRANS_FEAT(WHILE_lt, aa64_sve, do_WHILE, a, true, gen_helper_sve_whilel) -TRANS_FEAT(WHILE_gt, aa64_sve2, do_WHILE, a, false, gen_helper_sve_whileg) +TRANS_FEAT(WHILE_lt, aa64_sve, do_WHILE, a, true, 0, gen_helper_sve_whilel) +TRANS_FEAT(WHILE_gt, aa64_sve2, do_WHILE, a, false, 0, gen_helper_sve_whileg) + +TRANS_FEAT(WHILE_lt_pair, aa64_sme2_or_sve2p1, do_WHILE, + a, true, 1, gen_helper_sve_while2l) +TRANS_FEAT(WHILE_gt_pair, aa64_sme2_or_sve2p1, do_WHILE, + a, false, 1, gen_helper_sve_while2g) static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) { From f9cb0ac8ba387f7da6153d39d77b24271eb7d49c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:42 -0600 Subject: [PATCH 2026/2760] target/arm: Implement SVE2p1 WHILE (predicate as counter) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-80-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 3 ++ target/arm/tcg/sve.decode | 11 +++++++ target/arm/tcg/sve_helper.c | 53 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sve.c | 22 ++++++++++---- 4 files changed, 84 insertions(+), 5 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index eac23e75b9..74029c641b 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -944,6 +944,9 @@ DEF_HELPER_FLAGS_3(sve_whileg, TCG_CALL_NO_RWG, i32, ptr, i32, i32) DEF_HELPER_FLAGS_3(sve_while2l, TCG_CALL_NO_RWG, i32, ptr, i32, i32) DEF_HELPER_FLAGS_3(sve_while2g, TCG_CALL_NO_RWG, i32, ptr, i32, i32) +DEF_HELPER_FLAGS_3(sve_whilecl, TCG_CALL_NO_RWG, i32, ptr, i32, i32) +DEF_HELPER_FLAGS_3(sve_whilecg, TCG_CALL_NO_RWG, i32, ptr, i32, i32) + DEF_HELPER_FLAGS_4(sve_subri_b, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_subri_h, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) DEF_HELPER_FLAGS_4(sve_subri_s, TCG_CALL_NO_RWG, void, ptr, ptr, i64, i32) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 968b2a57e9..389a72d824 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -59,6 +59,8 @@ %rn_ax2 6:4 !function=times_2 +%pnd 0:3 !function=plus_8 + ########################################################################### # Named attribute sets. These are used to make nice(er) names # when creating helpers common to those for the individual @@ -804,6 +806,15 @@ WHILE_ptr 00100101 esz:2 1 rm:5 001 100 rn:5 rw:1 rd:4 WHILE_lt_pair 00100101 .. 1 ..... 0101 . 1 ..... 1 ... . @while_pair WHILE_gt_pair 00100101 .. 1 ..... 0101 . 0 ..... 1 ... . @while_pair +# SVE2.1 predicate as count +@while_cnt ........ esz:2 . rm:5 .... u:1 . rn:5 . eq:1 ... \ + &while rd=%pnd sf=1 + +WHILE_lt_cnt2 00100101 .. 1 ..... 0100 . 1 ..... 1 . ... @while_cnt +WHILE_lt_cnt4 00100101 .. 1 ..... 0110 . 1 ..... 1 . ... @while_cnt +WHILE_gt_cnt2 00100101 .. 1 ..... 0100 . 0 ..... 1 . ... @while_cnt +WHILE_gt_cnt4 00100101 .. 1 ..... 0110 . 0 ..... 1 . ... @while_cnt + ### SVE Integer Wide Immediate - Unpredicated Group # SVE broadcast floating-point immediate (unpredicated) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 30394f4880..713642d4c8 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4090,6 +4090,29 @@ uint64_t HELPER(sve_cntp)(void *vn, void *vg, uint32_t pred_desc) return sum; } +/* C.f. Arm pseudocode EncodePredCount */ +static uint64_t encode_pred_count(uint32_t elements, uint32_t count, + uint32_t esz, bool invert) +{ + uint32_t pred; + + if (count == 0) { + return 0; + } + if (invert) { + count = elements - count; + } else if (count == elements) { + count = 0; + invert = true; + } + + pred = (count << 1) | 1; + pred <<= esz; + pred |= invert << 15; + + return pred; +} + /* C.f. Arm pseudocode PredCountTest */ static uint32_t pred_count_test(uint32_t elements, uint32_t count, bool invert) { @@ -4159,6 +4182,21 @@ uint32_t HELPER(sve_while2l)(void *vd, uint32_t count, uint32_t pred_desc) return pred_count_test(2 * oprbits, count, false); } +uint32_t HELPER(sve_whilecl)(void *vd, uint32_t count, uint32_t pred_desc) +{ + uint32_t pl = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); + uint32_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); + uint32_t scale = FIELD_EX32(pred_desc, PREDDESC, DATA); + uint32_t vl = pl * 8; + uint32_t elements = (vl >> esz) << scale; + ARMPredicateReg *d = vd; + + *d = (ARMPredicateReg) { + .p[0] = encode_pred_count(elements, count, esz, false) + }; + return pred_count_test(elements, count, false); +} + /* D must be cleared on entry. */ static void do_whileg(ARMPredicateReg *d, uint64_t esz_mask, uint32_t count, uint32_t oprbits) @@ -4212,6 +4250,21 @@ uint32_t HELPER(sve_while2g)(void *vd, uint32_t count, uint32_t pred_desc) return pred_count_test(2 * oprbits, count, true); } +uint32_t HELPER(sve_whilecg)(void *vd, uint32_t count, uint32_t pred_desc) +{ + uint32_t pl = FIELD_EX32(pred_desc, PREDDESC, OPRSZ); + uint32_t esz = FIELD_EX32(pred_desc, PREDDESC, ESZ); + uint32_t scale = FIELD_EX32(pred_desc, PREDDESC, DATA); + uint32_t vl = pl * 8; + uint32_t elements = (vl >> esz) << scale; + ARMPredicateReg *d = vd; + + *d = (ARMPredicateReg) { + .p[0] = encode_pred_count(elements, count, esz, true) + }; + return pred_count_test(elements, count, true); +} + /* Recursive reduction on a function; * C.f. the ARM ARM function ReducePredicated. * diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index de6ffe7511..c93dca224c 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3110,7 +3110,7 @@ static bool trans_CTERM(DisasContext *s, arg_CTERM *a) typedef void gen_while_fn(TCGv_i32, TCGv_ptr, TCGv_i32, TCGv_i32); static bool do_WHILE(DisasContext *s, arg_while *a, - bool lt, int scale, gen_while_fn *fn) + bool lt, int scale, int data, gen_while_fn *fn) { TCGv_i64 op0, op1, t0, t1, tmax; TCGv_i32 t2; @@ -3196,6 +3196,7 @@ static bool do_WHILE(DisasContext *s, arg_while *a, desc = FIELD_DP32(desc, PREDDESC, OPRSZ, vsz / 8); desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); + desc = FIELD_DP32(desc, PREDDESC, DATA, data); ptr = tcg_temp_new_ptr(); tcg_gen_addi_ptr(ptr, tcg_env, pred_full_reg_offset(s, a->rd)); @@ -3206,13 +3207,24 @@ static bool do_WHILE(DisasContext *s, arg_while *a, return true; } -TRANS_FEAT(WHILE_lt, aa64_sve, do_WHILE, a, true, 0, gen_helper_sve_whilel) -TRANS_FEAT(WHILE_gt, aa64_sve2, do_WHILE, a, false, 0, gen_helper_sve_whileg) +TRANS_FEAT(WHILE_lt, aa64_sve, do_WHILE, + a, true, 0, 0, gen_helper_sve_whilel) +TRANS_FEAT(WHILE_gt, aa64_sve2, do_WHILE, + a, false, 0, 0, gen_helper_sve_whileg) TRANS_FEAT(WHILE_lt_pair, aa64_sme2_or_sve2p1, do_WHILE, - a, true, 1, gen_helper_sve_while2l) + a, true, 1, 0, gen_helper_sve_while2l) TRANS_FEAT(WHILE_gt_pair, aa64_sme2_or_sve2p1, do_WHILE, - a, false, 1, gen_helper_sve_while2g) + a, false, 1, 0, gen_helper_sve_while2g) + +TRANS_FEAT(WHILE_lt_cnt2, aa64_sme2_or_sve2p1, do_WHILE, + a, true, 1, 1, gen_helper_sve_whilecl) +TRANS_FEAT(WHILE_lt_cnt4, aa64_sme2_or_sve2p1, do_WHILE, + a, true, 2, 2, gen_helper_sve_whilecl) +TRANS_FEAT(WHILE_gt_cnt2, aa64_sme2_or_sve2p1, do_WHILE, + a, false, 1, 1, gen_helper_sve_whilecg) +TRANS_FEAT(WHILE_gt_cnt4, aa64_sme2_or_sve2p1, do_WHILE, + a, false, 2, 2, gen_helper_sve_whilecg) static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) { From b7094bc2528ee310ccabc05a494fe68ce9c06ce7 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:43 -0600 Subject: [PATCH 2027/2760] target/arm: Implement SVE2p1 PTRUE (predicate as counter) Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-81-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve.decode | 1 + target/arm/tcg/translate-sve.c | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 389a72d824..3517efb31b 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -730,6 +730,7 @@ PTEST 00100101 01 010000 11 pg:4 0 rn:4 0 0000 # SVE predicate initialize PTRUE 00100101 esz:2 01100 s:1 111000 pat:5 0 rd:4 +PTRUE_cnt 00100101 esz:2 1000000111100000010 ... rd=%pnd # SVE initialize FFR SETFFR 00100101 0010 1100 1001 0000 0000 0000 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index c93dca224c..651b4aa378 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -1679,6 +1679,22 @@ static bool do_predset(DisasContext *s, int esz, int rd, int pat, bool setflag) TRANS_FEAT(PTRUE, aa64_sve, do_predset, a->esz, a->rd, a->pat, a->s) +static bool trans_PTRUE_cnt(DisasContext *s, arg_PTRUE_cnt *a) +{ + if (!dc_isar_feature(aa64_sme2_or_sve2p1, s)) { + return false; + } + if (sve_access_check(s)) { + /* Canonical TRUE is 0 count, invert bit, plus element size. */ + int val = (1 << 15) | (1 << a->esz); + + /* Write val to the first uint64_t; clear all of the rest. */ + tcg_gen_gvec_dup_imm(MO_64, pred_full_reg_offset(s, a->rd), + 8, size_for_gvec(pred_full_reg_size(s)), val); + } + return true; +} + /* Note pat == 31 is #all, to set all elements. */ TRANS_FEAT_NONSTREAMING(SETFFR, aa64_sve, do_predset, 0, FFR_PRED_NUM, 31, false) From 8f7e127b66d0e51b3940c779695bc698138060d4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:44 -0600 Subject: [PATCH 2028/2760] target/arm: Implement {ADD, SMIN, SMAX, UMIN, UMAX}QV for SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-82-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 25 ++++++++++++++++++ target/arm/tcg/sve.decode | 7 ++++++ target/arm/tcg/sve_helper.c | 46 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sve.c | 35 ++++++++++++++++++++++++++ 4 files changed, 113 insertions(+) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 74029c641b..5f5ecc2e0d 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -2928,3 +2928,28 @@ DEF_HELPER_FLAGS_4(sve2_sqshlu_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_sqshlu_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_sqshlu_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_sqshlu_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2p1_addqv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_addqv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_addqv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_addqv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2p1_smaxqv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_smaxqv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_smaxqv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_smaxqv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2p1_sminqv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_sminqv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_sminqv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_sminqv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2p1_umaxqv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_umaxqv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_umaxqv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_umaxqv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2p1_uminqv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_uminqv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_uminqv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_uminqv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 3517efb31b..a3221308ad 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -340,6 +340,13 @@ UMAXV 00000100 .. 001 001 001 ... ..... ..... @rd_pg_rn SMINV 00000100 .. 001 010 001 ... ..... ..... @rd_pg_rn UMINV 00000100 .. 001 011 001 ... ..... ..... @rd_pg_rn +# SVE2.1 segment reduction +ADDQV 00000100 .. 000 101 001 ... ..... ..... @rd_pg_rn +SMAXQV 00000100 .. 001 100 001 ... ..... ..... @rd_pg_rn +SMINQV 00000100 .. 001 110 001 ... ..... ..... @rd_pg_rn +UMAXQV 00000100 .. 001 101 001 ... ..... ..... @rd_pg_rn +UMINQV 00000100 .. 001 111 001 ... ..... ..... @rd_pg_rn + ### SVE Shift by Immediate - Predicated Group # SVE bitwise shift by immediate (predicated) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 713642d4c8..625734fff9 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -1814,6 +1814,52 @@ DO_VPZ_D(sve_uminv_d, uint64_t, uint64_t, -1, DO_MIN) #undef DO_VPZ #undef DO_VPZ_D +#define DO_VPQ(NAME, TYPE, H, INIT, OP) \ +void HELPER(NAME)(void *vd, void *vn, void *vg, uint32_t desc) \ +{ \ + TYPE tmp[16 / sizeof(TYPE)] = { [0 ... 16 / sizeof(TYPE) - 1] = INIT }; \ + TYPE *n = vn; uint16_t *g = vg; \ + uintptr_t oprsz = simd_oprsz(desc); \ + uintptr_t nseg = oprsz / 16, nsegelt = 16 / sizeof(TYPE); \ + for (uintptr_t s = 0; s < nseg; s++) { \ + uint16_t pg = g[H2(s)]; \ + for (uintptr_t e = 0; e < nsegelt; e++, pg >>= sizeof(TYPE)) { \ + if (pg & 1) { \ + tmp[e] = OP(tmp[H(e)], n[s * nsegelt + H(e)]); \ + } \ + } \ + } \ + memcpy(vd, tmp, 16); \ + clear_tail(vd, 16, simd_maxsz(desc)); \ +} + +DO_VPQ(sve2p1_addqv_b, uint8_t, H1, 0, DO_ADD) +DO_VPQ(sve2p1_addqv_h, uint16_t, H2, 0, DO_ADD) +DO_VPQ(sve2p1_addqv_s, uint32_t, H4, 0, DO_ADD) +DO_VPQ(sve2p1_addqv_d, uint64_t, H8, 0, DO_ADD) + +DO_VPQ(sve2p1_smaxqv_b, int8_t, H1, INT8_MIN, DO_MAX) +DO_VPQ(sve2p1_smaxqv_h, int16_t, H2, INT16_MIN, DO_MAX) +DO_VPQ(sve2p1_smaxqv_s, int32_t, H4, INT32_MIN, DO_MAX) +DO_VPQ(sve2p1_smaxqv_d, int64_t, H8, INT64_MIN, DO_MAX) + +DO_VPQ(sve2p1_sminqv_b, int8_t, H1, INT8_MAX, DO_MIN) +DO_VPQ(sve2p1_sminqv_h, int16_t, H2, INT16_MAX, DO_MIN) +DO_VPQ(sve2p1_sminqv_s, int32_t, H4, INT32_MAX, DO_MIN) +DO_VPQ(sve2p1_sminqv_d, int64_t, H8, INT64_MAX, DO_MIN) + +DO_VPQ(sve2p1_umaxqv_b, uint8_t, H1, 0, DO_MAX) +DO_VPQ(sve2p1_umaxqv_h, uint16_t, H2, 0, DO_MAX) +DO_VPQ(sve2p1_umaxqv_s, uint32_t, H4, 0, DO_MAX) +DO_VPQ(sve2p1_umaxqv_d, uint64_t, H8, 0, DO_MAX) + +DO_VPQ(sve2p1_uminqv_b, uint8_t, H1, -1, DO_MIN) +DO_VPQ(sve2p1_uminqv_h, uint16_t, H2, -1, DO_MIN) +DO_VPQ(sve2p1_uminqv_s, uint32_t, H4, -1, DO_MIN) +DO_VPQ(sve2p1_uminqv_d, uint64_t, H8, -1, DO_MIN) + +#undef DO_VPQ + /* Two vector operand, one scalar operand, unpredicated. */ #define DO_ZZI(NAME, TYPE, OP) \ void HELPER(NAME)(void *vd, void *vn, uint64_t s64, uint32_t desc) \ diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 651b4aa378..2e29dff989 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -828,6 +828,41 @@ TRANS_FEAT(SXTW, aa64_sve, gen_gvec_ool_arg_zpz, TRANS_FEAT(UXTW, aa64_sve, gen_gvec_ool_arg_zpz, a->esz == 3 ? gen_helper_sve_uxtw_d : NULL, a, 0) +static gen_helper_gvec_3 * const addqv_fns[4] = { + gen_helper_sve2p1_addqv_b, gen_helper_sve2p1_addqv_h, + gen_helper_sve2p1_addqv_s, gen_helper_sve2p1_addqv_d, +}; +TRANS_FEAT(ADDQV, aa64_sme2p1_or_sve2p1, + gen_gvec_ool_arg_zpz, addqv_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const smaxqv_fns[4] = { + gen_helper_sve2p1_smaxqv_b, gen_helper_sve2p1_smaxqv_h, + gen_helper_sve2p1_smaxqv_s, gen_helper_sve2p1_smaxqv_d, +}; +TRANS_FEAT(SMAXQV, aa64_sme2p1_or_sve2p1, + gen_gvec_ool_arg_zpz, smaxqv_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const sminqv_fns[4] = { + gen_helper_sve2p1_sminqv_b, gen_helper_sve2p1_sminqv_h, + gen_helper_sve2p1_sminqv_s, gen_helper_sve2p1_sminqv_d, +}; +TRANS_FEAT(SMINQV, aa64_sme2p1_or_sve2p1, + gen_gvec_ool_arg_zpz, sminqv_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const umaxqv_fns[4] = { + gen_helper_sve2p1_umaxqv_b, gen_helper_sve2p1_umaxqv_h, + gen_helper_sve2p1_umaxqv_s, gen_helper_sve2p1_umaxqv_d, +}; +TRANS_FEAT(UMAXQV, aa64_sme2p1_or_sve2p1, + gen_gvec_ool_arg_zpz, umaxqv_fns[a->esz], a, 0) + +static gen_helper_gvec_3 * const uminqv_fns[4] = { + gen_helper_sve2p1_uminqv_b, gen_helper_sve2p1_uminqv_h, + gen_helper_sve2p1_uminqv_s, gen_helper_sve2p1_uminqv_d, +}; +TRANS_FEAT(UMINQV, aa64_sme2p1_or_sve2p1, + gen_gvec_ool_arg_zpz, uminqv_fns[a->esz], a, 0) + /* *** SVE Integer Reduction Group */ From 16fe3bb942e80ae0a2cd0690629bb73cc131092b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:45 -0600 Subject: [PATCH 2029/2760] target/arm: Implement SVE2p1 PEXT Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-83-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 2 + target/arm/tcg/sve.decode | 6 +++ target/arm/tcg/sve_helper.c | 28 +++++++++++++ target/arm/tcg/translate-sve.c | 36 +++++++++++++++++ target/arm/tcg/vec_internal.h | 74 ++++++++++++++++++++++++++++++++++ 5 files changed, 146 insertions(+) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 5f5ecc2e0d..ec82d0a4e7 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -2953,3 +2953,5 @@ DEF_HELPER_FLAGS_4(sve2p1_uminqv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2p1_uminqv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2p1_uminqv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2p1_uminqv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(pext, TCG_CALL_NO_RWG, void, ptr, i32, i32) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index a3221308ad..b762257759 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -60,6 +60,7 @@ %rn_ax2 6:4 !function=times_2 %pnd 0:3 !function=plus_8 +%pnn 5:3 !function=plus_8 ########################################################################### # Named attribute sets. These are used to make nice(er) names @@ -823,6 +824,11 @@ WHILE_lt_cnt4 00100101 .. 1 ..... 0110 . 1 ..... 1 . ... @while_cnt WHILE_gt_cnt2 00100101 .. 1 ..... 0100 . 0 ..... 1 . ... @while_cnt WHILE_gt_cnt4 00100101 .. 1 ..... 0110 . 0 ..... 1 . ... @while_cnt +# SVE2.1 extract mask predicate from predicate-as-counter +&pext rd rn esz imm +PEXT_1 00100101 esz:2 1 00000 0111 00 imm:2 ... 1 rd:4 &pext rn=%pnn +PEXT_2 00100101 esz:2 1 00000 0111 010 imm:1 ... 1 rd:4 &pext rn=%pnn + ### SVE Integer Wide Immediate - Unpredicated Group # SVE broadcast floating-point immediate (unpredicated) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 625734fff9..866f85ba30 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -7821,3 +7821,31 @@ DO_FCVTLT(sve2_fcvtlt_sd, uint64_t, uint32_t, H1_8, H1_4, float32_to_float64) #undef DO_FCVTLT #undef DO_FCVTNT + +void HELPER(pext)(void *vd, uint32_t png, uint32_t desc) +{ + int pl = FIELD_EX32(desc, PREDDESC, OPRSZ); + int vl = pl * 8; + unsigned v_esz = FIELD_EX32(desc, PREDDESC, ESZ); + int part = FIELD_EX32(desc, PREDDESC, DATA); + DecodeCounter p = decode_counter(png, vl, v_esz); + uint64_t mask = pred_esz_masks[v_esz + p.lg2_stride]; + ARMPredicateReg *d = vd; + + /* + * Convert from element count to byte count and adjust + * for the portion of the 4*VL counter to be extracted. + */ + int b_count = (p.count << v_esz) - vl * part; + + memset(d, 0, sizeof(*d)); + if (p.invert) { + if (b_count <= 0) { + do_whilel(vd, mask, vl, vl); + } else if (b_count < vl) { + do_whileg(vd, mask, vl - b_count, vl); + } + } else if (b_count > 0) { + do_whilel(vd, mask, MIN(b_count, vl), vl); + } +} diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 2e29dff989..6ad4d1e289 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3336,6 +3336,42 @@ static bool trans_WHILE_ptr(DisasContext *s, arg_WHILE_ptr *a) return true; } +static bool do_pext(DisasContext *s, arg_pext *a, int n) +{ + TCGv_i32 t_png; + TCGv_ptr t_pd; + int pl; + + if (!sve_access_check(s)) { + return true; + } + + t_png = tcg_temp_new_i32(); + tcg_gen_ld16u_i32(t_png, tcg_env, + pred_full_reg_offset(s, a->rn) ^ + (HOST_BIG_ENDIAN ? 6 : 0)); + + t_pd = tcg_temp_new_ptr(); + pl = pred_full_reg_size(s); + + for (int i = 0; i < n; ++i) { + int rd = (a->rd + i) % 16; + int part = a->imm * n + i; + unsigned desc = 0; + + desc = FIELD_DP32(desc, PREDDESC, OPRSZ, pl); + desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); + desc = FIELD_DP32(desc, PREDDESC, DATA, part); + + tcg_gen_addi_ptr(t_pd, tcg_env, pred_full_reg_offset(s, rd)); + gen_helper_pext(t_pd, t_png, tcg_constant_i32(desc)); + } + return true; +} + +TRANS_FEAT(PEXT_1, aa64_sme2_or_sve2p1, do_pext, a, 1) +TRANS_FEAT(PEXT_2, aa64_sme2_or_sve2p1, do_pext, a, 2) + /* *** SVE Integer Wide Immediate - Unpredicated Group */ diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index 5efd257c50..61c67bb35e 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -337,4 +337,78 @@ bfloat16 helper_sme2_ah_fmin_b16(bfloat16 a, bfloat16 b, float_status *fpst); float32 sve_f16_to_f32(float16 f, float_status *fpst); float16 sve_f32_to_f16(float32 f, float_status *fpst); +/* + * Decode helper functions for predicate as counter. + */ + +typedef struct { + unsigned count; + unsigned lg2_stride; + bool invert; +} DecodeCounter; + +static inline DecodeCounter +decode_counter(unsigned png, unsigned vl, unsigned v_esz) +{ + DecodeCounter ret = { }; + + /* C.f. Arm pseudocode CounterToPredicate. */ + if (likely(png & 0xf)) { + unsigned p_esz = ctz32(png); + + /* + * maxbit = log2(pl(bits) * 4) + * = log2(vl(bytes) * 4) + * = log2(vl) + 2 + * maxbit_mask = ones + * = (1 << (maxbit + 1)) - 1 + * = (1 << (log2(vl) + 2 + 1)) - 1 + * = (1 << (log2(vl) + 3)) - 1 + * = (pow2ceil(vl) << 3) - 1 + */ + ret.count = png & (((unsigned)pow2ceil(vl) << 3) - 1); + ret.count >>= p_esz + 1; + + ret.invert = (png >> 15) & 1; + + /* + * The Arm pseudocode for CounterToPredicate expands the count to + * a set of bits, and then the operation proceeds as for the original + * interpretation of predicates as a set of bits. + * + * We can avoid the expansion by adjusting the count and supplying + * an element stride. + */ + if (unlikely(p_esz != v_esz)) { + if (p_esz < v_esz) { + /* + * For predicate esz < vector esz, the expanded predicate + * will have more bits set than will be consumed. + * Adjust the count down, rounding up. + * Consider p_esz = MO_8, v_esz = MO_64, count 14: + * The expanded predicate would be + * 0011 1111 1111 1111 + * The significant bits are + * ...1 ...1 ...1 ...1 + */ + unsigned shift = v_esz - p_esz; + unsigned trunc = ret.count >> shift; + ret.count = trunc + (ret.count != (trunc << shift)); + } else { + /* + * For predicate esz > vector esz, the expanded predicate + * will have bits set only at power-of-two multiples of + * the vector esz. Bits at other multiples will all be + * false. Adjust the count up, and supply the caller + * with a stride of elements to skip. + */ + unsigned shift = p_esz - v_esz; + ret.count <<= shift; + ret.lg2_stride = shift; + } + } + } + return ret; +} + #endif /* TARGET_ARM_VEC_INTERNAL_H */ From ab6bf3d93d3296b46c106c5867db09e9d3bd8880 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:46 -0600 Subject: [PATCH 2030/2760] target/arm: Implement SME2 SEL Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-84-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 5 + target/arm/tcg/sme.decode | 9 + target/arm/tcg/sme_helper.c | 317 +++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 31 ++++ 4 files changed, 362 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 78ba3d1f20..467073ea25 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -313,3 +313,8 @@ DEF_HELPER_FLAGS_5(sme2_fclamp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i3 DEF_HELPER_FLAGS_5(sme2_fclamp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sme2_fclamp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sme2_bfclamp, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(sme2_sel_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32, i32) +DEF_HELPER_FLAGS_5(sme2_sel_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32, i32) +DEF_HELPER_FLAGS_5(sme2_sel_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32, i32) +DEF_HELPER_FLAGS_5(sme2_sel_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index c4b85a36d0..0a2ceea8be 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -878,6 +878,15 @@ UCLAMP 11000001 esz:2 1 zm:5 110001 zn:5 .... 1 \ UCLAMP 11000001 esz:2 1 zm:5 110011 zn:5 ...0 1 \ &zzz_en zd=%zd_ax4 n=4 +### SME2 Multi-vector SVE Select + +%sel_pg 10:3 !function=plus_8 + +SEL 11000001 esz:2 1 ....0 100 ... ....0 ....0 \ + n=2 zd=%zd_ax2 zn=%zn_ax2 zm=%zm_ax2 pg=%sel_pg +SEL 11000001 esz:2 1 ...01 100 ... ...00 ...00 \ + n=4 zd=%zd_ax4 zn=%zn_ax4 zm=%zm_ax4 pg=%sel_pg + ### SME Multiple Zero &zero_za rv off ngrp nvec diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 8a1f9fbf3d..c1166e4ffa 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -2118,3 +2118,320 @@ FCLAMP(sme2_fclamp_d, float64, H8) FCLAMP(sme2_bfclamp, bfloat16, H2) #undef FCLAMP + +void HELPER(sme2_sel_b)(void *vd, void *vn, void *vm, + uint32_t png, uint32_t desc) +{ + int vl = simd_oprsz(desc); + int nreg = simd_data(desc); + int elements = vl / sizeof(uint8_t); + DecodeCounter p = decode_counter(png, vl, MO_8); + + if (p.lg2_stride == 0) { + if (p.invert) { + for (int r = 0; r < nreg; r++) { + uint8_t *d = vd + r * sizeof(ARMVectorReg); + uint8_t *n = vn + r * sizeof(ARMVectorReg); + uint8_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + + if (split <= 0) { + memcpy(d, n, vl); /* all true */ + } else if (elements <= split) { + memcpy(d, m, vl); /* all false */ + } else { + for (int e = 0; e < split; e++) { + d[H1(e)] = m[H1(e)]; + } + for (int e = split; e < elements; e++) { + d[H1(e)] = n[H1(e)]; + } + } + } + } else { + for (int r = 0; r < nreg; r++) { + uint8_t *d = vd + r * sizeof(ARMVectorReg); + uint8_t *n = vn + r * sizeof(ARMVectorReg); + uint8_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + + if (split <= 0) { + memcpy(d, m, vl); /* all false */ + } else if (elements <= split) { + memcpy(d, n, vl); /* all true */ + } else { + for (int e = 0; e < split; e++) { + d[H1(e)] = n[H1(e)]; + } + for (int e = split; e < elements; e++) { + d[H1(e)] = m[H1(e)]; + } + } + } + } + } else { + int estride = 1 << p.lg2_stride; + if (p.invert) { + for (int r = 0; r < nreg; r++) { + uint8_t *d = vd + r * sizeof(ARMVectorReg); + uint8_t *n = vn + r * sizeof(ARMVectorReg); + uint8_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + int e = 0; + + for (; e < MIN(split, elements); e++) { + d[H1(e)] = m[H1(e)]; + } + for (; e < elements; e += estride) { + d[H1(e)] = n[H1(e)]; + for (int i = 1; i < estride; i++) { + d[H1(e + i)] = m[H1(e + i)]; + } + } + } + } else { + for (int r = 0; r < nreg; r++) { + uint8_t *d = vd + r * sizeof(ARMVectorReg); + uint8_t *n = vn + r * sizeof(ARMVectorReg); + uint8_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + int e = 0; + + for (; e < MIN(split, elements); e += estride) { + d[H1(e)] = n[H1(e)]; + for (int i = 1; i < estride; i++) { + d[H1(e + i)] = m[H1(e + i)]; + } + } + for (; e < elements; e++) { + d[H1(e)] = m[H1(e)]; + } + } + } + } +} + +void HELPER(sme2_sel_h)(void *vd, void *vn, void *vm, + uint32_t png, uint32_t desc) +{ + int vl = simd_oprsz(desc); + int nreg = simd_data(desc); + int elements = vl / sizeof(uint16_t); + DecodeCounter p = decode_counter(png, vl, MO_16); + + if (p.lg2_stride == 0) { + if (p.invert) { + for (int r = 0; r < nreg; r++) { + uint16_t *d = vd + r * sizeof(ARMVectorReg); + uint16_t *n = vn + r * sizeof(ARMVectorReg); + uint16_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + + if (split <= 0) { + memcpy(d, n, vl); /* all true */ + } else if (elements <= split) { + memcpy(d, m, vl); /* all false */ + } else { + for (int e = 0; e < split; e++) { + d[H2(e)] = m[H2(e)]; + } + for (int e = split; e < elements; e++) { + d[H2(e)] = n[H2(e)]; + } + } + } + } else { + for (int r = 0; r < nreg; r++) { + uint16_t *d = vd + r * sizeof(ARMVectorReg); + uint16_t *n = vn + r * sizeof(ARMVectorReg); + uint16_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + + if (split <= 0) { + memcpy(d, m, vl); /* all false */ + } else if (elements <= split) { + memcpy(d, n, vl); /* all true */ + } else { + for (int e = 0; e < split; e++) { + d[H2(e)] = n[H2(e)]; + } + for (int e = split; e < elements; e++) { + d[H2(e)] = m[H2(e)]; + } + } + } + } + } else { + int estride = 1 << p.lg2_stride; + if (p.invert) { + for (int r = 0; r < nreg; r++) { + uint16_t *d = vd + r * sizeof(ARMVectorReg); + uint16_t *n = vn + r * sizeof(ARMVectorReg); + uint16_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + int e = 0; + + for (; e < MIN(split, elements); e++) { + d[H2(e)] = m[H2(e)]; + } + for (; e < elements; e += estride) { + d[H2(e)] = n[H2(e)]; + for (int i = 1; i < estride; i++) { + d[H2(e + i)] = m[H2(e + i)]; + } + } + } + } else { + for (int r = 0; r < nreg; r++) { + uint16_t *d = vd + r * sizeof(ARMVectorReg); + uint16_t *n = vn + r * sizeof(ARMVectorReg); + uint16_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + int e = 0; + + for (; e < MIN(split, elements); e += estride) { + d[H2(e)] = n[H2(e)]; + for (int i = 1; i < estride; i++) { + d[H2(e + i)] = m[H2(e + i)]; + } + } + for (; e < elements; e++) { + d[H2(e)] = m[H2(e)]; + } + } + } + } +} + +void HELPER(sme2_sel_s)(void *vd, void *vn, void *vm, + uint32_t png, uint32_t desc) +{ + int vl = simd_oprsz(desc); + int nreg = simd_data(desc); + int elements = vl / sizeof(uint32_t); + DecodeCounter p = decode_counter(png, vl, MO_32); + + if (p.lg2_stride == 0) { + if (p.invert) { + for (int r = 0; r < nreg; r++) { + uint32_t *d = vd + r * sizeof(ARMVectorReg); + uint32_t *n = vn + r * sizeof(ARMVectorReg); + uint32_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + + if (split <= 0) { + memcpy(d, n, vl); /* all true */ + } else if (elements <= split) { + memcpy(d, m, vl); /* all false */ + } else { + for (int e = 0; e < split; e++) { + d[H4(e)] = m[H4(e)]; + } + for (int e = split; e < elements; e++) { + d[H4(e)] = n[H4(e)]; + } + } + } + } else { + for (int r = 0; r < nreg; r++) { + uint32_t *d = vd + r * sizeof(ARMVectorReg); + uint32_t *n = vn + r * sizeof(ARMVectorReg); + uint32_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + + if (split <= 0) { + memcpy(d, m, vl); /* all false */ + } else if (elements <= split) { + memcpy(d, n, vl); /* all true */ + } else { + for (int e = 0; e < split; e++) { + d[H4(e)] = n[H4(e)]; + } + for (int e = split; e < elements; e++) { + d[H4(e)] = m[H4(e)]; + } + } + } + } + } else { + /* p.esz must be MO_64, so stride must be 2. */ + if (p.invert) { + for (int r = 0; r < nreg; r++) { + uint32_t *d = vd + r * sizeof(ARMVectorReg); + uint32_t *n = vn + r * sizeof(ARMVectorReg); + uint32_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + int e = 0; + + for (; e < MIN(split, elements); e++) { + d[H4(e)] = m[H4(e)]; + } + for (; e < elements; e += 2) { + d[H4(e)] = n[H4(e)]; + d[H4(e + 1)] = m[H4(e + 1)]; + } + } + } else { + for (int r = 0; r < nreg; r++) { + uint32_t *d = vd + r * sizeof(ARMVectorReg); + uint32_t *n = vn + r * sizeof(ARMVectorReg); + uint32_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + int e = 0; + + for (; e < MIN(split, elements); e += 2) { + d[H4(e)] = n[H4(e)]; + d[H4(e + 1)] = m[H4(e + 1)]; + } + for (; e < elements; e++) { + d[H4(e)] = m[H4(e)]; + } + } + } + } +} + +void HELPER(sme2_sel_d)(void *vd, void *vn, void *vm, + uint32_t png, uint32_t desc) +{ + int vl = simd_oprsz(desc); + int nreg = simd_data(desc); + int elements = vl / sizeof(uint64_t); + DecodeCounter p = decode_counter(png, vl, MO_64); + + if (p.invert) { + for (int r = 0; r < nreg; r++) { + uint64_t *d = vd + r * sizeof(ARMVectorReg); + uint64_t *n = vn + r * sizeof(ARMVectorReg); + uint64_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + + if (split <= 0) { + memcpy(d, n, vl); /* all true */ + } else if (elements <= split) { + memcpy(d, m, vl); /* all false */ + } else { + memcpy(d, m, split * sizeof(uint64_t)); + memcpy(d + split, n + split, + (elements - split) * sizeof(uint64_t)); + } + } + } else { + for (int r = 0; r < nreg; r++) { + uint64_t *d = vd + r * sizeof(ARMVectorReg); + uint64_t *n = vn + r * sizeof(ARMVectorReg); + uint64_t *m = vm + r * sizeof(ARMVectorReg); + int split = p.count - r * elements; + + if (split <= 0) { + memcpy(d, m, vl); /* all false */ + } else if (elements <= split) { + memcpy(d, n, vl); /* all true */ + } else { + memcpy(d, n, split * sizeof(uint64_t)); + memcpy(d + split, m + split, + (elements - split) * sizeof(uint64_t)); + } + } + } +} diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index b6316ac716..7407597177 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1642,3 +1642,34 @@ static gen_helper_gvec_3 * const uclamp_fns[] = { gen_helper_sme2_uclamp_d, }; TRANS(UCLAMP, do_clamp, a, uclamp_fns) + +static bool trans_SEL(DisasContext *s, arg_SEL *a) +{ + typedef void sme_sel_fn(TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32, TCGv_i32); + static sme_sel_fn * const fns[4] = { + gen_helper_sme2_sel_b, gen_helper_sme2_sel_h, + gen_helper_sme2_sel_s, gen_helper_sme2_sel_d + }; + + if (!dc_isar_feature(aa64_sme2, s)) { + return false; + } + if (sme_sm_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + uint32_t desc = simd_desc(svl, svl, a->n); + TCGv_ptr t_d = tcg_temp_new_ptr(); + TCGv_ptr t_n = tcg_temp_new_ptr(); + TCGv_ptr t_m = tcg_temp_new_ptr(); + TCGv_i32 png = tcg_temp_new_i32(); + + tcg_gen_addi_ptr(t_d, tcg_env, vec_full_reg_offset(s, a->zd)); + tcg_gen_addi_ptr(t_n, tcg_env, vec_full_reg_offset(s, a->zn)); + tcg_gen_addi_ptr(t_m, tcg_env, vec_full_reg_offset(s, a->zm)); + + tcg_gen_ld16u_i32(png, tcg_env, pred_full_reg_offset(s, a->pg) + ^ (HOST_BIG_ENDIAN ? 6 : 0)); + + fns[a->esz](t_d, t_n, t_m, png, tcg_constant_i32(desc)); + } + return true; +} From 5b334d17e639799904b2aa873b9cbd7c08b7ab8f Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:47 -0600 Subject: [PATCH 2031/2760] target/arm: Implement ANDQV, ORQV, EORQV for SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-85-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 15 ++++++++++++ target/arm/tcg/sve.decode | 5 ++++ target/arm/tcg/sve_helper.c | 42 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sve.c | 3 +++ 4 files changed, 65 insertions(+) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index ec82d0a4e7..9758613b2d 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -2955,3 +2955,18 @@ DEF_HELPER_FLAGS_4(sve2p1_uminqv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2p1_uminqv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_3(pext, TCG_CALL_NO_RWG, void, ptr, i32, i32) + +DEF_HELPER_FLAGS_4(sve2p1_orqv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_orqv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_orqv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_orqv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2p1_eorqv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_eorqv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_eorqv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_eorqv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sve2p1_andqv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_andqv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_andqv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_andqv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index b762257759..ff740f7b40 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -326,6 +326,11 @@ ORV 00000100 .. 011 000 001 ... ..... ..... @rd_pg_rn EORV 00000100 .. 011 001 001 ... ..... ..... @rd_pg_rn ANDV 00000100 .. 011 010 001 ... ..... ..... @rd_pg_rn +# SVE2.1 bitwise logical reduction (quadwords) +ORQV 00000100 .. 011 100 001 ... ..... ..... @rd_pg_rn +EORQV 00000100 .. 011 101 001 ... ..... ..... @rd_pg_rn +ANDQV 00000100 .. 011 110 001 ... ..... ..... @rd_pg_rn + # SVE constructive prefix (predicated) MOVPRFX_z 00000100 .. 010 000 001 ... ..... ..... @rd_pg_rn MOVPRFX_m 00000100 .. 010 001 001 ... ..... ..... @rd_pg_rn diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 866f85ba30..f47e719fbe 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -123,6 +123,11 @@ static inline uint64_t expand_pred_s(uint8_t byte) return word[byte & 0x11]; } +static inline uint64_t expand_pred_d(uint8_t byte) +{ + return -(uint64_t)(byte & 1); +} + #define LOGICAL_PPPP(NAME, FUNC) \ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ { \ @@ -206,6 +211,7 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, uint32_t desc) \ #define DO_EOR(N, M) (N ^ M) #define DO_ORR(N, M) (N | M) #define DO_BIC(N, M) (N & ~M) +#define DO_ORC(N, M) (N | ~M) #define DO_ADD(N, M) (N + M) #define DO_SUB(N, M) (N - M) #define DO_MAX(N, M) ((N) >= (M) ? (N) : (M)) @@ -1900,10 +1906,46 @@ DO_ZZI(sve_umini_d, uint64_t, DO_MIN) #undef DO_ZZI +#define DO_LOGIC_QV(NAME, SUFF, INIT, VOP, POP) \ +void HELPER(NAME ## _ ## SUFF)(void *vd, void *vn, void *vg, uint32_t desc) \ +{ \ + unsigned seg = simd_oprsz(desc) / 16; \ + uint64_t r0 = INIT, r1 = INIT; \ + for (unsigned s = 0; s < seg; s++) { \ + uint64_t p0 = expand_pred_##SUFF(*(uint8_t *)(vg + H1(s * 2))); \ + uint64_t p1 = expand_pred_##SUFF(*(uint8_t *)(vg + H1(s * 2 + 1))); \ + uint64_t v0 = *(uint64_t *)(vn + s * 16); \ + uint64_t v1 = *(uint64_t *)(vn + s * 16 + 8); \ + v0 = POP(v0, p0), v1 = POP(v1, p1); \ + r0 = VOP(r0, v0), r1 = VOP(r1, v1); \ + } \ + *(uint64_t *)(vd + 0) = r0; \ + *(uint64_t *)(vd + 8) = r1; \ + clear_tail(vd, 16, simd_maxsz(desc)); \ +} + +DO_LOGIC_QV(sve2p1_orqv, b, 0, DO_ORR, DO_AND) +DO_LOGIC_QV(sve2p1_orqv, h, 0, DO_ORR, DO_AND) +DO_LOGIC_QV(sve2p1_orqv, s, 0, DO_ORR, DO_AND) +DO_LOGIC_QV(sve2p1_orqv, d, 0, DO_ORR, DO_AND) + +DO_LOGIC_QV(sve2p1_eorqv, b, 0, DO_EOR, DO_AND) +DO_LOGIC_QV(sve2p1_eorqv, h, 0, DO_EOR, DO_AND) +DO_LOGIC_QV(sve2p1_eorqv, s, 0, DO_EOR, DO_AND) +DO_LOGIC_QV(sve2p1_eorqv, d, 0, DO_EOR, DO_AND) + +DO_LOGIC_QV(sve2p1_andqv, b, -1, DO_AND, DO_ORC) +DO_LOGIC_QV(sve2p1_andqv, h, -1, DO_AND, DO_ORC) +DO_LOGIC_QV(sve2p1_andqv, s, -1, DO_AND, DO_ORC) +DO_LOGIC_QV(sve2p1_andqv, d, -1, DO_AND, DO_ORC) + +#undef DO_LOGIC_QV + #undef DO_AND #undef DO_ORR #undef DO_EOR #undef DO_BIC +#undef DO_ORC #undef DO_ADD #undef DO_SUB #undef DO_MAX diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 6ad4d1e289..2114b2ecca 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -778,6 +778,9 @@ DO_ZPZ(NOT_zpz, aa64_sve, sve_not_zpz) DO_ZPZ(ABS, aa64_sve, sve_abs) DO_ZPZ(NEG, aa64_sve, sve_neg) DO_ZPZ(RBIT, aa64_sve, sve_rbit) +DO_ZPZ(ORQV, aa64_sme2p1_or_sve2p1, sve2p1_orqv) +DO_ZPZ(EORQV, aa64_sme2p1_or_sve2p1, sve2p1_eorqv) +DO_ZPZ(ANDQV, aa64_sme2p1_or_sve2p1, sve2p1_andqv) static gen_helper_gvec_3 * const fabs_fns[4] = { NULL, gen_helper_sve_fabs_h, From 1de7ecfc12d05d5c7929c22ccdaf7c834f6f2305 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:48 -0600 Subject: [PATCH 2032/2760] target/arm: Implement FADDQV, F{MIN, MAX}{NM}QV for SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-86-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 49 ++++++++++++++++++++++++ target/arm/tcg/sve.decode | 8 ++++ target/arm/tcg/sve_helper.c | 70 +++++++++++++++++++++------------- target/arm/tcg/translate-sve.c | 48 +++++++++++++++++++++++ 4 files changed, 148 insertions(+), 27 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 9758613b2d..906da384dc 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1077,6 +1077,55 @@ DEF_HELPER_FLAGS_4(sve_ah_fminv_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_4(sve_ah_fminv_d, TCG_CALL_NO_RWG, i64, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_faddqv_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_faddqv_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_faddqv_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(sve2p1_fmaxnmqv_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_fmaxnmqv_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_fmaxnmqv_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(sve2p1_fminnmqv_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_fminnmqv_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_fminnmqv_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(sve2p1_fmaxqv_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_fmaxqv_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_fmaxqv_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(sve2p1_fminqv_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_fminqv_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_fminqv_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(sve2p1_ah_fmaxqv_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_ah_fmaxqv_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_ah_fmaxqv_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + +DEF_HELPER_FLAGS_5(sve2p1_ah_fminqv_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_ah_fminqv_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(sve2p1_ah_fminqv_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) + DEF_HELPER_FLAGS_5(sve_fadda_h, TCG_CALL_NO_RWG, i64, i64, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(sve_fadda_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index ff740f7b40..10cac2de22 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1036,6 +1036,14 @@ FMINNMV 01100101 .. 000 101 001 ... ..... ..... @rd_pg_rn FMAXV 01100101 .. 000 110 001 ... ..... ..... @rd_pg_rn FMINV 01100101 .. 000 111 001 ... ..... ..... @rd_pg_rn +### SVE FP recursive reduction (quadwords) + +FADDQV 01100100 .. 010 000 101 ... ..... ..... @rd_pg_rn +FMAXNMQV 01100100 .. 010 100 101 ... ..... ..... @rd_pg_rn +FMINNMQV 01100100 .. 010 101 101 ... ..... ..... @rd_pg_rn +FMAXQV 01100100 .. 010 110 101 ... ..... ..... @rd_pg_rn +FMINQV 01100100 .. 010 111 101 ... ..... ..... @rd_pg_rn + ## SVE Floating Point Unary Operations - Unpredicated Group FRECPE 01100101 .. 001 110 001100 ..... ..... @rd_rn diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index f47e719fbe..6512df54e6 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4361,19 +4361,20 @@ uint32_t HELPER(sve_whilecg)(void *vd, uint32_t count, uint32_t pred_desc) * The recursion is bounded to depth 7 (128 fp16 elements), so there's * little to gain with a more complex non-recursive form. */ -#define DO_REDUCE(NAME, TYPE, H, FUNC, IDENT) \ -static TYPE NAME##_reduce(TYPE *data, float_status *status, uintptr_t n) \ +#define DO_REDUCE(NAME, SUF, TYPE, H, FUNC, IDENT) \ +static TYPE FUNC##_reduce(TYPE *data, float_status *status, uintptr_t n) \ { \ if (n == 1) { \ return *data; \ } else { \ uintptr_t half = n / 2; \ - TYPE lo = NAME##_reduce(data, status, half); \ - TYPE hi = NAME##_reduce(data + half, status, half); \ + TYPE lo = FUNC##_reduce(data, status, half); \ + TYPE hi = FUNC##_reduce(data + half, status, half); \ return FUNC(lo, hi, status); \ } \ } \ -uint64_t HELPER(NAME)(void *vn, void *vg, float_status *s, uint32_t desc) \ +uint64_t helper_sve_##NAME##v_##SUF(void *vn, void *vg, \ + float_status *s, uint32_t desc) \ { \ uintptr_t i, oprsz = simd_oprsz(desc), maxsz = simd_data(desc); \ TYPE data[sizeof(ARMVectorReg) / sizeof(TYPE)]; \ @@ -4388,39 +4389,54 @@ uint64_t HELPER(NAME)(void *vn, void *vg, float_status *s, uint32_t desc) \ for (; i < maxsz; i += sizeof(TYPE)) { \ *(TYPE *)((void *)data + i) = IDENT; \ } \ - return NAME##_reduce(data, s, maxsz / sizeof(TYPE)); \ + return FUNC##_reduce(data, s, maxsz / sizeof(TYPE)); \ +} \ +void helper_sve2p1_##NAME##qv_##SUF(void *vd, void *vn, void *vg, \ + float_status *status, uint32_t desc) \ +{ \ + unsigned oprsz = simd_oprsz(desc), segments = oprsz / 16; \ + for (unsigned e = 0; e < 16; e += sizeof(TYPE)) { \ + TYPE data[ARM_MAX_VQ]; \ + for (unsigned s = 0; s < segments; s++) { \ + uint16_t pg = *(uint16_t *)(vg + H1_2(s * 2)); \ + TYPE nn = *(TYPE *)(vn + H(s * 16 + H(e))); \ + data[s] = (pg >> e) & 1 ? nn : IDENT; \ + } \ + *(TYPE *)(vd + H(e)) = FUNC##_reduce(data, status, segments); \ + } \ + clear_tail(vd, 16, simd_maxsz(desc)); \ } -DO_REDUCE(sve_faddv_h, float16, H1_2, float16_add, float16_zero) -DO_REDUCE(sve_faddv_s, float32, H1_4, float32_add, float32_zero) -DO_REDUCE(sve_faddv_d, float64, H1_8, float64_add, float64_zero) +DO_REDUCE(fadd,h, float16, H1_2, float16_add, float16_zero) +DO_REDUCE(fadd,s, float32, H1_4, float32_add, float32_zero) +DO_REDUCE(fadd,d, float64, H1_8, float64_add, float64_zero) /* Identity is floatN_default_nan, without the function call. */ -DO_REDUCE(sve_fminnmv_h, float16, H1_2, float16_minnum, 0x7E00) -DO_REDUCE(sve_fminnmv_s, float32, H1_4, float32_minnum, 0x7FC00000) -DO_REDUCE(sve_fminnmv_d, float64, H1_8, float64_minnum, 0x7FF8000000000000ULL) +DO_REDUCE(fminnm,h, float16, H1_2, float16_minnum, 0x7E00) +DO_REDUCE(fminnm,s, float32, H1_4, float32_minnum, 0x7FC00000) +DO_REDUCE(fminnm,d, float64, H1_8, float64_minnum, 0x7FF8000000000000ULL) -DO_REDUCE(sve_fmaxnmv_h, float16, H1_2, float16_maxnum, 0x7E00) -DO_REDUCE(sve_fmaxnmv_s, float32, H1_4, float32_maxnum, 0x7FC00000) -DO_REDUCE(sve_fmaxnmv_d, float64, H1_8, float64_maxnum, 0x7FF8000000000000ULL) +DO_REDUCE(fmaxnm,h, float16, H1_2, float16_maxnum, 0x7E00) +DO_REDUCE(fmaxnm,s, float32, H1_4, float32_maxnum, 0x7FC00000) +DO_REDUCE(fmaxnm,d, float64, H1_8, float64_maxnum, 0x7FF8000000000000ULL) -DO_REDUCE(sve_fminv_h, float16, H1_2, float16_min, float16_infinity) -DO_REDUCE(sve_fminv_s, float32, H1_4, float32_min, float32_infinity) -DO_REDUCE(sve_fminv_d, float64, H1_8, float64_min, float64_infinity) +DO_REDUCE(fmin,h, float16, H1_2, float16_min, float16_infinity) +DO_REDUCE(fmin,s, float32, H1_4, float32_min, float32_infinity) +DO_REDUCE(fmin,d, float64, H1_8, float64_min, float64_infinity) -DO_REDUCE(sve_fmaxv_h, float16, H1_2, float16_max, float16_chs(float16_infinity)) -DO_REDUCE(sve_fmaxv_s, float32, H1_4, float32_max, float32_chs(float32_infinity)) -DO_REDUCE(sve_fmaxv_d, float64, H1_8, float64_max, float64_chs(float64_infinity)) +DO_REDUCE(fmax,h, float16, H1_2, float16_max, float16_chs(float16_infinity)) +DO_REDUCE(fmax,s, float32, H1_4, float32_max, float32_chs(float32_infinity)) +DO_REDUCE(fmax,d, float64, H1_8, float64_max, float64_chs(float64_infinity)) -DO_REDUCE(sve_ah_fminv_h, float16, H1_2, helper_vfp_ah_minh, float16_infinity) -DO_REDUCE(sve_ah_fminv_s, float32, H1_4, helper_vfp_ah_mins, float32_infinity) -DO_REDUCE(sve_ah_fminv_d, float64, H1_8, helper_vfp_ah_mind, float64_infinity) +DO_REDUCE(ah_fmin,h, float16, H1_2, helper_vfp_ah_minh, float16_infinity) +DO_REDUCE(ah_fmin,s, float32, H1_4, helper_vfp_ah_mins, float32_infinity) +DO_REDUCE(ah_fmin,d, float64, H1_8, helper_vfp_ah_mind, float64_infinity) -DO_REDUCE(sve_ah_fmaxv_h, float16, H1_2, helper_vfp_ah_maxh, +DO_REDUCE(ah_fmax,h, float16, H1_2, helper_vfp_ah_maxh, float16_chs(float16_infinity)) -DO_REDUCE(sve_ah_fmaxv_s, float32, H1_4, helper_vfp_ah_maxs, +DO_REDUCE(ah_fmax,s, float32, H1_4, helper_vfp_ah_maxs, float32_chs(float32_infinity)) -DO_REDUCE(sve_ah_fmaxv_d, float64, H1_8, helper_vfp_ah_maxd, +DO_REDUCE(ah_fmax,d, float64, H1_8, helper_vfp_ah_maxd, float64_chs(float64_infinity)) #undef DO_REDUCE diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 2114b2ecca..05c0fc948a 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3743,6 +3743,54 @@ DO_VPZ_AH(FMAXV, fmaxv) #undef DO_VPZ +static gen_helper_gvec_3_ptr * const faddqv_fns[4] = { + NULL, gen_helper_sve2p1_faddqv_h, + gen_helper_sve2p1_faddqv_s, gen_helper_sve2p1_faddqv_d, +}; +TRANS_FEAT(FADDQV, aa64_sme2p1_or_sve2p1, gen_gvec_fpst_arg_zpz, + faddqv_fns[a->esz], a, 0, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) + +static gen_helper_gvec_3_ptr * const fmaxnmqv_fns[4] = { + NULL, gen_helper_sve2p1_fmaxnmqv_h, + gen_helper_sve2p1_fmaxnmqv_s, gen_helper_sve2p1_fmaxnmqv_d, +}; +TRANS_FEAT(FMAXNMQV, aa64_sme2p1_or_sve2p1, gen_gvec_fpst_arg_zpz, + fmaxnmqv_fns[a->esz], a, 0, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) + +static gen_helper_gvec_3_ptr * const fminnmqv_fns[4] = { + NULL, gen_helper_sve2p1_fminnmqv_h, + gen_helper_sve2p1_fminnmqv_s, gen_helper_sve2p1_fminnmqv_d, +}; +TRANS_FEAT(FMINNMQV, aa64_sme2p1_or_sve2p1, gen_gvec_fpst_arg_zpz, + fminnmqv_fns[a->esz], a, 0, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) + +static gen_helper_gvec_3_ptr * const fmaxqv_fns[4] = { + NULL, gen_helper_sve2p1_fmaxqv_h, + gen_helper_sve2p1_fmaxqv_s, gen_helper_sve2p1_fmaxqv_d, +}; +static gen_helper_gvec_3_ptr * const fmaxqv_ah_fns[4] = { + NULL, gen_helper_sve2p1_ah_fmaxqv_h, + gen_helper_sve2p1_ah_fmaxqv_s, gen_helper_sve2p1_ah_fmaxqv_d, +}; +TRANS_FEAT(FMAXQV, aa64_sme2p1_or_sve2p1, gen_gvec_fpst_arg_zpz, + (s->fpcr_ah ? fmaxqv_fns : fmaxqv_ah_fns)[a->esz], a, 0, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) + +static gen_helper_gvec_3_ptr * const fminqv_fns[4] = { + NULL, gen_helper_sve2p1_fminqv_h, + gen_helper_sve2p1_fminqv_s, gen_helper_sve2p1_fminqv_d, +}; +static gen_helper_gvec_3_ptr * const fminqv_ah_fns[4] = { + NULL, gen_helper_sve2p1_ah_fminqv_h, + gen_helper_sve2p1_ah_fminqv_s, gen_helper_sve2p1_ah_fminqv_d, +}; +TRANS_FEAT(FMINQV, aa64_sme2p1_or_sve2p1, gen_gvec_fpst_arg_zpz, + (s->fpcr_ah ? fminqv_fns : fminqv_ah_fns)[a->esz], a, 0, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) + /* *** SVE Floating Point Unary Operations - Unpredicated Group */ From 4fddbdf934937d0043d0601262809df14bd14abb Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:49 -0600 Subject: [PATCH 2033/2760] target/arm: Implement BFMLSLB{L, T} for SME2/SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-87-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve.decode | 6 ++++++ target/arm/tcg/translate-sve.c | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 10cac2de22..0df3312739 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1727,18 +1727,24 @@ FMLSLT_zzzw 01100100 10 1 ..... 10 1 00 1 ..... ..... @rda_rn_rm_ex esz=2 BFMLALB_zzzw 01100100 11 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_ex esz=2 BFMLALT_zzzw 01100100 11 1 ..... 10 0 00 1 ..... ..... @rda_rn_rm_ex esz=2 +BFMLSLB_zzzw 01100100 11 1 ..... 10 1 00 0 ..... ..... @rda_rn_rm_ex esz=2 +BFMLSLT_zzzw 01100100 11 1 ..... 10 1 00 1 ..... ..... @rda_rn_rm_ex esz=2 ### SVE2 floating-point dot-product FDOT_zzzz 01100100 00 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_ex esz=2 BFDOT_zzzz 01100100 01 1 ..... 10 0 00 0 ..... ..... @rda_rn_rm_ex esz=2 ### SVE2 floating-point multiply-add long (indexed) + FMLALB_zzxw 01100100 10 1 ..... 0100.0 ..... ..... @rrxr_3a esz=2 FMLALT_zzxw 01100100 10 1 ..... 0100.1 ..... ..... @rrxr_3a esz=2 FMLSLB_zzxw 01100100 10 1 ..... 0110.0 ..... ..... @rrxr_3a esz=2 FMLSLT_zzxw 01100100 10 1 ..... 0110.1 ..... ..... @rrxr_3a esz=2 + BFMLALB_zzxw 01100100 11 1 ..... 0100.0 ..... ..... @rrxr_3a esz=2 BFMLALT_zzxw 01100100 11 1 ..... 0100.1 ..... ..... @rrxr_3a esz=2 +BFMLSLB_zzxw 01100100 11 1 ..... 0110.0 ..... ..... @rrxr_3a esz=2 +BFMLSLT_zzxw 01100100 11 1 ..... 0110.1 ..... ..... @rrxr_3a esz=2 ### SVE2 floating-point dot-product (indexed) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 05c0fc948a..079d310960 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -7421,6 +7421,36 @@ static bool do_BFMLAL_zzxw(DisasContext *s, arg_rrxr_esz *a, bool sel) TRANS_FEAT(BFMLALB_zzxw, aa64_sve_bf16, do_BFMLAL_zzxw, a, false) TRANS_FEAT(BFMLALT_zzxw, aa64_sve_bf16, do_BFMLAL_zzxw, a, true) +static bool do_BFMLSL_zzzw(DisasContext *s, arg_rrrr_esz *a, bool sel) +{ + if (s->fpcr_ah) { + return gen_gvec_fpst_zzzz(s, gen_helper_gvec_ah_bfmlsl, + a->rd, a->rn, a->rm, a->ra, sel, FPST_AH); + } else { + return gen_gvec_fpst_zzzz(s, gen_helper_gvec_bfmlsl, + a->rd, a->rn, a->rm, a->ra, sel, FPST_A64); + } +} + +TRANS_FEAT(BFMLSLB_zzzw, aa64_sme2_or_sve2p1, do_BFMLSL_zzzw, a, false) +TRANS_FEAT(BFMLSLT_zzzw, aa64_sme2_or_sve2p1, do_BFMLSL_zzzw, a, true) + +static bool do_BFMLSL_zzxw(DisasContext *s, arg_rrxr_esz *a, bool sel) +{ + if (s->fpcr_ah) { + return gen_gvec_fpst_zzzz(s, gen_helper_gvec_ah_bfmlsl_idx, + a->rd, a->rn, a->rm, a->ra, + (a->index << 1) | sel, FPST_AH); + } else { + return gen_gvec_fpst_zzzz(s, gen_helper_gvec_bfmlsl_idx, + a->rd, a->rn, a->rm, a->ra, + (a->index << 1) | sel, FPST_A64); + } +} + +TRANS_FEAT(BFMLSLB_zzxw, aa64_sme2_or_sve2p1, do_BFMLSL_zzxw, a, false) +TRANS_FEAT(BFMLSLT_zzxw, aa64_sme2_or_sve2p1, do_BFMLSL_zzxw, a, true) + static bool trans_PSEL(DisasContext *s, arg_psel *a) { int vl = vec_full_reg_size(s); From 47810f99e846501c817fb77ebd03b6914342f7e5 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:50 -0600 Subject: [PATCH 2034/2760] target/arm: Implement CNTP (predicate as counter) for SME2/SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-88-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 1 + target/arm/tcg/sve.decode | 3 ++- target/arm/tcg/sve_helper.c | 21 +++++++++++++++++++++ target/arm/tcg/translate-sve.c | 30 ++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 906da384dc..733828a880 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -937,6 +937,7 @@ DEF_HELPER_FLAGS_4(sve_brkn, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_brkns, TCG_CALL_NO_RWG, i32, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_cntp, TCG_CALL_NO_RWG, i64, ptr, ptr, i32) +DEF_HELPER_FLAGS_2(sve2p1_cntp_c, TCG_CALL_NO_RWG_SE, i64, i32, i32) DEF_HELPER_FLAGS_3(sve_whilel, TCG_CALL_NO_RWG, i32, ptr, i32, i32) DEF_HELPER_FLAGS_3(sve_whileg, TCG_CALL_NO_RWG, i32, ptr, i32, i32) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 0df3312739..db16849731 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -784,7 +784,8 @@ BRKN 00100101 0. 01100001 .... 0 .... 0 .... @pd_pg_pn_s ### SVE Predicate Count Group # SVE predicate count -CNTP 00100101 .. 100 000 10 .... 0 .... ..... @rd_pg4_pn +CNTP 00100101 .. 100 000 10 .... 0 .... ..... @rd_pg4_pn +CNTP_c 00100101 esz:2 100 000 10 000 vl:1 1 rn:4 rd:5 # SVE inc/dec register by predicate count INCDECP_r 00100101 .. 10110 d:1 10001 00 .... ..... @incdec_pred u=1 diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 6512df54e6..def841fd52 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4178,6 +4178,27 @@ uint64_t HELPER(sve_cntp)(void *vn, void *vg, uint32_t pred_desc) return sum; } +uint64_t HELPER(sve2p1_cntp_c)(uint32_t png, uint32_t desc) +{ + int pl = FIELD_EX32(desc, PREDDESC, OPRSZ); + int vl = pl * 8; + unsigned v_esz = FIELD_EX32(desc, PREDDESC, ESZ); + int lg2_width = FIELD_EX32(desc, PREDDESC, DATA) + 1; + DecodeCounter p = decode_counter(png, vl, v_esz); + unsigned maxelem = (vl << lg2_width) >> v_esz; + unsigned count = p.count; + + if (p.invert) { + if (count >= maxelem) { + return 0; + } + count = maxelem - count; + } else { + count = MIN(count, maxelem); + } + return count >> p.lg2_stride; +} + /* C.f. Arm pseudocode EncodePredCount */ static uint64_t encode_pred_count(uint32_t elements, uint32_t count, uint32_t esz, bool invert) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 079d310960..53db8851bf 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3052,6 +3052,36 @@ static bool trans_CNTP(DisasContext *s, arg_CNTP *a) return true; } +static bool trans_CNTP_c(DisasContext *s, arg_CNTP_c *a) +{ + TCGv_i32 t_png; + uint32_t desc = 0; + + if (dc_isar_feature(aa64_sve2p1, s)) { + if (!sve_access_check(s)) { + return true; + } + } else if (dc_isar_feature(aa64_sme2, s)) { + if (!sme_sm_enabled_check(s)) { + return true; + } + } else { + return false; + } + + t_png = tcg_temp_new_i32(); + tcg_gen_ld16u_i32(t_png, tcg_env, + pred_full_reg_offset(s, a->rn) ^ + (HOST_BIG_ENDIAN ? 6 : 0)); + + desc = FIELD_DP32(desc, PREDDESC, OPRSZ, pred_full_reg_size(s)); + desc = FIELD_DP32(desc, PREDDESC, ESZ, a->esz); + desc = FIELD_DP32(desc, PREDDESC, DATA, a->vl); + + gen_helper_sve2p1_cntp_c(cpu_reg(s, a->rd), t_png, tcg_constant_i32(desc)); + return true; +} + static bool trans_INCDECP_r(DisasContext *s, arg_incdec_pred *a) { if (!dc_isar_feature(aa64_sve, s)) { From a4a49a31f499adb8ba2fca6b048d4f51f41e178a Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:51 -0600 Subject: [PATCH 2035/2760] target/arm: Implement DUPQ for SME2p1/SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-89-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve.decode | 6 ++++++ target/arm/tcg/translate-sve.c | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index db16849731..2650e00f80 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -577,6 +577,12 @@ DUP_s 00000101 .. 1 00000 001110 ..... ..... @rd_rn DUP_x 00000101 .. 1 ..... 001000 rn:5 rd:5 \ &rri imm=%imm7_22_16 +# SVE Permute Vector - one source quadwords +DUPQ 00000101 001 imm:4 1 001001 rn:5 rd:5 &rri_esz esz=0 +DUPQ 00000101 001 imm:3 10 001001 rn:5 rd:5 &rri_esz esz=1 +DUPQ 00000101 001 imm:2 100 001001 rn:5 rd:5 &rri_esz esz=2 +DUPQ 00000101 001 imm:1 1000 001001 rn:5 rd:5 &rri_esz esz=3 + # SVE insert SIMD&FP scalar register INSR_f 00000101 .. 1 10100 001110 ..... ..... @rdn_rm diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 53db8851bf..e33b2eb2a4 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -2249,6 +2249,27 @@ static bool trans_DUP_x(DisasContext *s, arg_DUP_x *a) return true; } +static bool trans_DUPQ(DisasContext *s, arg_DUPQ *a) +{ + unsigned vl, dofs, nofs; + + if (!dc_isar_feature(aa64_sme2p1_or_sve2p1, s)) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + vl = vec_full_reg_size(s); + dofs = vec_full_reg_offset(s, a->rd); + nofs = vec_reg_offset(s, a->rn, a->imm, a->esz); + + for (unsigned i = 0; i < vl; i += 16) { + tcg_gen_gvec_dup_mem(a->esz, dofs + i, nofs + i, 16, 16); + } + return true; +} + static void do_insr_i64(DisasContext *s, arg_rrr_esz *a, TCGv_i64 val) { typedef void gen_insr(TCGv_ptr, TCGv_ptr, TCGv_i64, TCGv_i32); From 3b5257c8602b3002540c4dbdd3929251bd3acc85 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:52 -0600 Subject: [PATCH 2036/2760] target/arm: Implement EXTQ for SME2p1/SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-90-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve.decode | 2 ++ target/arm/tcg/translate-sve.c | 49 ++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 2650e00f80..af4fb966bf 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -583,6 +583,8 @@ DUPQ 00000101 001 imm:3 10 001001 rn:5 rd:5 &rri_esz esz=1 DUPQ 00000101 001 imm:2 100 001001 rn:5 rd:5 &rri_esz esz=2 DUPQ 00000101 001 imm:1 1000 001001 rn:5 rd:5 &rri_esz esz=3 +EXTQ 00000101 0110 imm:4 001001 rn:5 rd:5 &rri + # SVE insert SIMD&FP scalar register INSR_f 00000101 .. 1 10100 001110 ..... ..... @rdn_rm diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index e33b2eb2a4..a918da31fe 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -2202,6 +2202,55 @@ static bool do_EXT(DisasContext *s, int rd, int rn, int rm, int imm) TRANS_FEAT(EXT, aa64_sve, do_EXT, a->rd, a->rn, a->rm, a->imm) TRANS_FEAT(EXT_sve2, aa64_sve2, do_EXT, a->rd, a->rn, (a->rn + 1) % 32, a->imm) +static bool trans_EXTQ(DisasContext *s, arg_EXTQ *a) +{ + unsigned vl, dofs, sofs0, sofs1, sofs2, imm; + + if (!dc_isar_feature(aa64_sme2p1_or_sve2p1, s)) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + imm = a->imm; + if (imm == 0) { + /* So far we never optimize Zdn with MOVPRFX, so zd = zn is a nop. */ + return true; + } + + vl = vec_full_reg_size(s); + dofs = vec_full_reg_offset(s, a->rd); + sofs2 = vec_full_reg_offset(s, a->rn); + + if (imm & 8) { + sofs0 = dofs + 8; + sofs1 = sofs2; + sofs2 += 8; + } else { + sofs0 = dofs; + sofs1 = dofs + 8; + } + imm = (imm & 7) << 3; + + for (unsigned i = 0; i < vl; i += 16) { + TCGv_i64 s0 = tcg_temp_new_i64(); + TCGv_i64 s1 = tcg_temp_new_i64(); + TCGv_i64 s2 = tcg_temp_new_i64(); + + tcg_gen_ld_i64(s0, tcg_env, sofs0 + i); + tcg_gen_ld_i64(s1, tcg_env, sofs1 + i); + tcg_gen_ld_i64(s2, tcg_env, sofs2 + i); + + tcg_gen_extract2_i64(s0, s0, s1, imm); + tcg_gen_extract2_i64(s1, s1, s2, imm); + + tcg_gen_st_i64(s0, tcg_env, dofs + i); + tcg_gen_st_i64(s1, tcg_env, dofs + i + 8); + } + return true; +} + /* *** SVE Permute - Unpredicated Group */ From e421e4e9727c2b835a09b9d43777f3a56b252899 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:53 -0600 Subject: [PATCH 2037/2760] target/arm: Implement PMOV for SME2p1/SVE2p1 Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-91-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 8 +++ target/arm/tcg/sve.decode | 17 ++++++ target/arm/tcg/sve_helper.c | 50 +++++++++++++++++ target/arm/tcg/translate-sve.c | 98 ++++++++++++++++++++++++++++++++++ target/arm/tcg/vec_internal.h | 34 ++++++++++++ 5 files changed, 207 insertions(+) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 733828a880..04b9545c11 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -3020,3 +3020,11 @@ DEF_HELPER_FLAGS_4(sve2p1_andqv_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2p1_andqv_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2p1_andqv_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2p1_andqv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(pmov_pv_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(pmov_pv_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(pmov_pv_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_3(pmov_vp_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(pmov_vp_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(pmov_vp_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index af4fb966bf..3271c9cf78 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -30,6 +30,7 @@ %size_23 23:2 %dtype_23_13 23:2 13:2 %index3_22_19 22:1 19:2 +%index3_22_17 22:1 17:2 %index3_19_11 19:2 11:1 %index2_20_11 20:1 11:1 @@ -594,6 +595,22 @@ INSR_r 00000101 .. 1 00100 001110 ..... ..... @rdn_rm # SVE reverse vector elements REV_v 00000101 .. 1 11000 001110 ..... ..... @rd_rn +# SVE move predicate to/from vector + +PMOV_pv 00000101 00 101 01 0001110 rn:5 0 rd:4 \ + &rri_esz esz=0 imm=0 +PMOV_pv 00000101 00 101 1 imm:1 0001110 rn:5 0 rd:4 &rri_esz esz=1 +PMOV_pv 00000101 01 101 imm:2 0001110 rn:5 0 rd:4 &rri_esz esz=2 +PMOV_pv 00000101 1. 101 .. 0001110 rn:5 0 rd:4 \ + &rri_esz esz=3 imm=%index3_22_17 + +PMOV_vp 00000101 00 101 01 1001110 0 rn:4 rd:5 \ + &rri_esz esz=0 imm=0 +PMOV_vp 00000101 00 101 1 imm:1 1001110 0 rn:4 rd:5 &rri_esz esz=1 +PMOV_vp 00000101 01 101 imm:2 1001110 0 rn:4 rd:5 &rri_esz esz=2 +PMOV_vp 00000101 1. 101 .. 1001110 0 rn:4 rd:5 \ + &rri_esz esz=3 imm=%index3_22_17 + # SVE vector table lookup TBL 00000101 .. 1 ..... 001100 ..... ..... @rd_rn_rm diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index def841fd52..a9dc3c44b2 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -3035,6 +3035,56 @@ void HELPER(sve_rev_d)(void *vd, void *vn, uint32_t desc) } } +/* + * TODO: This could use half_shuffle64 and similar bit tricks to + * expand blocks of bits at once. + */ +#define DO_PMOV_PV(NAME, ESIZE) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + unsigned vl = simd_oprsz(desc); \ + unsigned idx = simd_data(desc); \ + unsigned elements = vl / ESIZE; \ + ARMPredicateReg *d = vd; \ + ARMVectorReg *s = vs; \ + memset(d, 0, sizeof(*d)); \ + for (unsigned e = 0; e < elements; ++e) { \ + depositn(d->p, e * ESIZE, 1, extractn(s->d, elements * idx + e, 1)); \ + } \ +} + +DO_PMOV_PV(pmov_pv_h, 2) +DO_PMOV_PV(pmov_pv_s, 4) +DO_PMOV_PV(pmov_pv_d, 8) + +#undef DO_PMOV_PV + +/* + * TODO: This could use half_unshuffle64 and similar bit tricks to + * compress blocks of bits at once. + */ +#define DO_PMOV_VP(NAME, ESIZE) \ +void HELPER(NAME)(void *vd, void *vs, uint32_t desc) \ +{ \ + unsigned vl = simd_oprsz(desc); \ + unsigned idx = simd_data(desc); \ + unsigned elements = vl / ESIZE; \ + ARMVectorReg *d = vd; \ + ARMPredicateReg *s = vs; \ + if (idx == 0) { \ + memset(d, 0, vl); \ + } \ + for (unsigned e = 0; e < elements; ++e) { \ + depositn(d->d, elements * idx + e, 1, extractn(s->p, e * ESIZE, 1)); \ + } \ +} + +DO_PMOV_VP(pmov_vp_h, 2) +DO_PMOV_VP(pmov_vp_s, 4) +DO_PMOV_VP(pmov_vp_d, 8) + +#undef DO_PMOV_VP + typedef void tb_impl_fn(void *, void *, void *, void *, uintptr_t, bool); static inline void do_tbl1(void *vd, void *vn, void *vm, uint32_t desc, diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index a918da31fe..01c4eaa585 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -2386,6 +2386,104 @@ static gen_helper_gvec_3 * const tbx_fns[4] = { }; TRANS_FEAT(TBX, aa64_sve2, gen_gvec_ool_arg_zzz, tbx_fns[a->esz], a, 0) +static bool trans_PMOV_pv(DisasContext *s, arg_PMOV_pv *a) +{ + static gen_helper_gvec_2 * const fns[4] = { + NULL, gen_helper_pmov_pv_h, + gen_helper_pmov_pv_s, gen_helper_pmov_pv_d + }; + unsigned vl, pl, vofs, pofs; + TCGv_i64 tmp; + + if (!dc_isar_feature(aa64_sme2p1_or_sve2p1, s)) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + vl = vec_full_reg_size(s); + if (a->esz != MO_8) { + tcg_gen_gvec_2_ool(pred_full_reg_offset(s, a->rd), + vec_full_reg_offset(s, a->rn), + vl, vl, a->imm, fns[a->esz]); + return true; + } + + /* + * Copy the low PL bytes from vector Zn, zero-extending to a + * multiple of 8 bytes, so that Pd is properly cleared. + */ + + pl = vl / 8; + pofs = pred_full_reg_offset(s, a->rd); + vofs = vec_full_reg_offset(s, a->rn); + + QEMU_BUILD_BUG_ON(sizeof(ARMPredicateReg) != 32); + for (unsigned i = 32; i >= 8; i >>= 1) { + if (pl & i) { + tcg_gen_gvec_mov(MO_64, pofs, vofs, i, i); + pofs += i; + vofs += i; + } + } + switch (pl & 7) { + case 0: + return true; + case 2: + tmp = tcg_temp_new_i64(); + tcg_gen_ld16u_i64(tmp, tcg_env, vofs + (HOST_BIG_ENDIAN ? 6 : 0)); + break; + case 4: + tmp = tcg_temp_new_i64(); + tcg_gen_ld32u_i64(tmp, tcg_env, vofs + (HOST_BIG_ENDIAN ? 4 : 0)); + break; + case 6: + tmp = tcg_temp_new_i64(); + tcg_gen_ld_i64(tmp, tcg_env, vofs); + tcg_gen_extract_i64(tmp, tmp, 0, 48); + break; + default: + g_assert_not_reached(); + } + tcg_gen_st_i64(tmp, tcg_env, pofs); + return true; +} + +static bool trans_PMOV_vp(DisasContext *s, arg_PMOV_pv *a) +{ + static gen_helper_gvec_2 * const fns[4] = { + NULL, gen_helper_pmov_vp_h, + gen_helper_pmov_vp_s, gen_helper_pmov_vp_d + }; + unsigned vl; + + if (!dc_isar_feature(aa64_sme2p1_or_sve2p1, s)) { + return false; + } + if (!sve_access_check(s)) { + return true; + } + + vl = vec_full_reg_size(s); + + if (a->esz == MO_8) { + /* + * The low PL bytes are copied from Pn to Zd unchanged. + * We know that the unused portion of Pn is zero, and + * that imm == 0, so the balance of Zd must be zeroed. + */ + tcg_gen_gvec_mov(MO_64, vec_full_reg_offset(s, a->rd), + pred_full_reg_offset(s, a->rn), + size_for_gvec(vl / 8), vl); + } else { + tcg_gen_gvec_2_ool(vec_full_reg_offset(s, a->rd), + pred_full_reg_offset(s, a->rn), + vl, vl, a->imm, fns[a->esz]); + } + return true; +} + static bool trans_UNPK(DisasContext *s, arg_UNPK *a) { static gen_helper_gvec_2 * const fns[4][2] = { diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index 61c67bb35e..957bf6d9fc 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -411,4 +411,38 @@ decode_counter(unsigned png, unsigned vl, unsigned v_esz) return ret; } +/* Extract @len bits from an array of uint64_t at offset @pos bits. */ +static inline uint64_t extractn(uint64_t *p, unsigned pos, unsigned len) +{ + uint64_t x; + + p += pos / 64; + pos = pos % 64; + + x = p[0]; + if (pos + len > 64) { + x = (x >> pos) | (p[1] << (-pos & 63)); + pos = 0; + } + return extract64(x, pos, len); +} + +/* Deposit @len bits into an array of uint64_t at offset @pos bits. */ +static inline void depositn(uint64_t *p, unsigned pos, + unsigned len, uint64_t val) +{ + p += pos / 64; + pos = pos % 64; + + if (pos + len <= 64) { + p[0] = deposit64(p[0], pos, len, val); + } else { + unsigned len0 = 64 - pos; + unsigned len1 = len - len0; + + p[0] = deposit64(p[0], pos, len0, val); + p[1] = deposit64(p[1], 0, len1, val >> len0); + } +} + #endif /* TARGET_ARM_VEC_INTERNAL_H */ From 945a379438736571d9bb6086f1e264452c5427f1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:54 -0600 Subject: [PATCH 2038/2760] target/arm: Implement ZIPQ, UZPQ for SME2p1/SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-92-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 10 ++++++++++ target/arm/tcg/sve.decode | 6 ++++++ target/arm/tcg/sve_helper.c | 29 +++++++++++++++++++++++++++++ target/arm/tcg/translate-sve.c | 19 ++++++++++++++++++- 4 files changed, 63 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 04b9545c11..0f510ea610 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -701,12 +701,22 @@ DEF_HELPER_FLAGS_4(sve_zip_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_zip_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_zip_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_zipq_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_zipq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_zipq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_zipq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve_uzp_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_uzp_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_uzp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_uzp_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_uzp_q, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_uzpq_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_uzpq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_uzpq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_uzpq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve_trn_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_trn_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve_trn_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 3271c9cf78..e98275eec1 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -657,6 +657,12 @@ UZP2_q 00000101 10 1 ..... 000 011 ..... ..... @rd_rn_rm_e0 TRN1_q 00000101 10 1 ..... 000 110 ..... ..... @rd_rn_rm_e0 TRN2_q 00000101 10 1 ..... 000 111 ..... ..... @rd_rn_rm_e0 +# SVE2.1 permute vector elements (quadwords) +ZIPQ1 01000100 .. 0 ..... 111 000 ..... ..... @rd_rn_rm +ZIPQ2 01000100 .. 0 ..... 111 001 ..... ..... @rd_rn_rm +UZPQ1 01000100 .. 0 ..... 111 010 ..... ..... @rd_rn_rm +UZPQ2 01000100 .. 0 ..... 111 011 ..... ..... @rd_rn_rm + ### SVE Permute - Predicated Group # SVE compress active elements diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index a9dc3c44b2..dbbfbc12b9 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -3550,6 +3550,35 @@ DO_UZP(sve_uzp_s, uint32_t, H1_4) DO_UZP(sve_uzp_d, uint64_t, H1_8) DO_UZP(sve2_uzp_q, Int128, ) +typedef void perseg_zzz_fn(void *vd, void *vn, void *vm, uint32_t desc); + +static void do_perseg_zzz(void *vd, void *vn, void *vm, + uint32_t desc, perseg_zzz_fn *fn) +{ + intptr_t oprsz = simd_oprsz(desc); + + desc = simd_desc(16, 16, simd_data(desc)); + for (intptr_t i = 0; i < oprsz; i += 16) { + fn(vd + i, vn + i, vm + i, desc); + } +} + +#define DO_PERSEG_ZZZ(NAME, FUNC) \ + void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ + { do_perseg_zzz(vd, vn, vm, desc, FUNC); } + +DO_PERSEG_ZZZ(sve2p1_uzpq_b, helper_sve_uzp_b) +DO_PERSEG_ZZZ(sve2p1_uzpq_h, helper_sve_uzp_h) +DO_PERSEG_ZZZ(sve2p1_uzpq_s, helper_sve_uzp_s) +DO_PERSEG_ZZZ(sve2p1_uzpq_d, helper_sve_uzp_d) + +DO_PERSEG_ZZZ(sve2p1_zipq_b, helper_sve_zip_b) +DO_PERSEG_ZZZ(sve2p1_zipq_h, helper_sve_zip_h) +DO_PERSEG_ZZZ(sve2p1_zipq_s, helper_sve_zip_s) +DO_PERSEG_ZZZ(sve2p1_zipq_d, helper_sve_zip_d) + +#undef DO_PERSEG_ZZZ + #define DO_TRN(NAME, TYPE, H) \ void HELPER(NAME)(void *vd, void *vn, void *vm, uint32_t desc) \ { \ diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 01c4eaa585..28cfcb9cb4 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -2606,11 +2606,19 @@ TRANS_FEAT_NONSTREAMING(ZIP2_q, aa64_sve_f64mm, do_interleave_q, gen_helper_sve2_zip_q, a, QEMU_ALIGN_DOWN(vec_full_reg_size(s), 32) / 2) +static gen_helper_gvec_3 * const zipq_fns[4] = { + gen_helper_sve2p1_zipq_b, gen_helper_sve2p1_zipq_h, + gen_helper_sve2p1_zipq_s, gen_helper_sve2p1_zipq_d, +}; +TRANS_FEAT(ZIPQ1, aa64_sme2p1_or_sve2p1, gen_gvec_ool_arg_zzz, + zipq_fns[a->esz], a, 0) +TRANS_FEAT(ZIPQ2, aa64_sme2p1_or_sve2p1, gen_gvec_ool_arg_zzz, + zipq_fns[a->esz], a, 16 / 2) + static gen_helper_gvec_3 * const uzp_fns[4] = { gen_helper_sve_uzp_b, gen_helper_sve_uzp_h, gen_helper_sve_uzp_s, gen_helper_sve_uzp_d, }; - TRANS_FEAT(UZP1_z, aa64_sve, gen_gvec_ool_arg_zzz, uzp_fns[a->esz], a, 0) TRANS_FEAT(UZP2_z, aa64_sve, gen_gvec_ool_arg_zzz, @@ -2621,6 +2629,15 @@ TRANS_FEAT_NONSTREAMING(UZP1_q, aa64_sve_f64mm, do_interleave_q, TRANS_FEAT_NONSTREAMING(UZP2_q, aa64_sve_f64mm, do_interleave_q, gen_helper_sve2_uzp_q, a, 16) +static gen_helper_gvec_3 * const uzpq_fns[4] = { + gen_helper_sve2p1_uzpq_b, gen_helper_sve2p1_uzpq_h, + gen_helper_sve2p1_uzpq_s, gen_helper_sve2p1_uzpq_d, +}; +TRANS_FEAT(UZPQ1, aa64_sme2p1_or_sve2p1, gen_gvec_ool_arg_zzz, + uzpq_fns[a->esz], a, 0) +TRANS_FEAT(UZPQ2, aa64_sme2p1_or_sve2p1, gen_gvec_ool_arg_zzz, + uzpq_fns[a->esz], a, 1 << a->esz) + static gen_helper_gvec_3 * const trn_fns[4] = { gen_helper_sve_trn_b, gen_helper_sve_trn_h, gen_helper_sve_trn_s, gen_helper_sve_trn_d, From 47f4cdd6c28755c7f1415ac38bc3350afaaee330 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:55 -0600 Subject: [PATCH 2039/2760] target/arm: Implement TBLQ, TBXQ for SME2p1/SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-93-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 10 ++++++++++ target/arm/tcg/sve.decode | 3 +++ target/arm/tcg/sve_helper.c | 10 ++++++++++ target/arm/tcg/translate-sve.c | 14 ++++++++++++++ 4 files changed, 37 insertions(+) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 0f510ea610..5f4b4aa036 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -676,11 +676,21 @@ DEF_HELPER_FLAGS_5(sve2_tbl_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve2_tbl_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sve2_tbl_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_tblq_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_tblq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_tblq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_tblq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_4(sve2_tbx_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_tbx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_tbx_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_tbx_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_tbxq_b, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_tbxq_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_tbxq_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) +DEF_HELPER_FLAGS_4(sve2p1_tbxq_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) + DEF_HELPER_FLAGS_3(sve_sunpk_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_sunpk_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sve_sunpk_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index e98275eec1..52a56d3341 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -663,6 +663,9 @@ ZIPQ2 01000100 .. 0 ..... 111 001 ..... ..... @rd_rn_rm UZPQ1 01000100 .. 0 ..... 111 010 ..... ..... @rd_rn_rm UZPQ2 01000100 .. 0 ..... 111 011 ..... ..... @rd_rn_rm +TBLQ 01000100 .. 0 ..... 111 110 ..... ..... @rd_rn_rm +TBXQ 00000101 .. 1 ..... 001 101 ..... ..... @rd_rn_rm + ### SVE Permute - Predicated Group # SVE compress active elements diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index dbbfbc12b9..42b05756a9 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -3577,6 +3577,16 @@ DO_PERSEG_ZZZ(sve2p1_zipq_h, helper_sve_zip_h) DO_PERSEG_ZZZ(sve2p1_zipq_s, helper_sve_zip_s) DO_PERSEG_ZZZ(sve2p1_zipq_d, helper_sve_zip_d) +DO_PERSEG_ZZZ(sve2p1_tblq_b, helper_sve_tbl_b) +DO_PERSEG_ZZZ(sve2p1_tblq_h, helper_sve_tbl_h) +DO_PERSEG_ZZZ(sve2p1_tblq_s, helper_sve_tbl_s) +DO_PERSEG_ZZZ(sve2p1_tblq_d, helper_sve_tbl_d) + +DO_PERSEG_ZZZ(sve2p1_tbxq_b, helper_sve2_tbx_b) +DO_PERSEG_ZZZ(sve2p1_tbxq_h, helper_sve2_tbx_h) +DO_PERSEG_ZZZ(sve2p1_tbxq_s, helper_sve2_tbx_s) +DO_PERSEG_ZZZ(sve2p1_tbxq_d, helper_sve2_tbx_d) + #undef DO_PERSEG_ZZZ #define DO_TRN(NAME, TYPE, H) \ diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 28cfcb9cb4..02f885dd48 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -2380,12 +2380,26 @@ static gen_helper_gvec_4 * const sve2_tbl_fns[4] = { TRANS_FEAT(TBL_sve2, aa64_sve2, gen_gvec_ool_zzzz, sve2_tbl_fns[a->esz], a->rd, a->rn, (a->rn + 1) % 32, a->rm, 0) +static gen_helper_gvec_3 * const tblq_fns[4] = { + gen_helper_sve2p1_tblq_b, gen_helper_sve2p1_tblq_h, + gen_helper_sve2p1_tblq_s, gen_helper_sve2p1_tblq_d +}; +TRANS_FEAT(TBLQ, aa64_sme2p1_or_sve2p1, gen_gvec_ool_arg_zzz, + tblq_fns[a->esz], a, 0) + static gen_helper_gvec_3 * const tbx_fns[4] = { gen_helper_sve2_tbx_b, gen_helper_sve2_tbx_h, gen_helper_sve2_tbx_s, gen_helper_sve2_tbx_d }; TRANS_FEAT(TBX, aa64_sve2, gen_gvec_ool_arg_zzz, tbx_fns[a->esz], a, 0) +static gen_helper_gvec_3 * const tbxq_fns[4] = { + gen_helper_sve2p1_tbxq_b, gen_helper_sve2p1_tbxq_h, + gen_helper_sve2p1_tbxq_s, gen_helper_sve2p1_tbxq_d +}; +TRANS_FEAT(TBXQ, aa64_sme2p1_or_sve2p1, gen_gvec_ool_arg_zzz, + tbxq_fns[a->esz], a, 0) + static bool trans_PMOV_pv(DisasContext *s, arg_PMOV_pv *a) { static gen_helper_gvec_2 * const fns[4] = { From b0b0818a4a41ab10f1bd5fb5e2c0cd815ae3fee9 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:56 -0600 Subject: [PATCH 2040/2760] target/arm: Implement SME2 counted predicate register load/store Implement the SVE2p1 consecutive register LD1/ST1, and the SME2 strided register LD1/ST1. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-94-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 16 ++ target/arm/tcg/sve.decode | 50 ++++ target/arm/tcg/sve_helper.c | 493 +++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sve.c | 103 +++++++ 4 files changed, 662 insertions(+) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 5f4b4aa036..c4736d7510 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -3048,3 +3048,19 @@ DEF_HELPER_FLAGS_3(pmov_pv_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(pmov_vp_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(pmov_vp_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(pmov_vp_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_5(sve2p1_ld1bb_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_ld1hh_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_ld1hh_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_ld1ss_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_ld1ss_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_ld1dd_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_ld1dd_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) + +DEF_HELPER_FLAGS_5(sve2p1_st1bb_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_st1hh_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_st1hh_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_st1ss_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_st1ss_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_st1dd_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_st1dd_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 52a56d3341..bf3d4f4853 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1812,3 +1812,53 @@ SCLAMP 01000100 .. 0 ..... 110000 ..... ..... @rda_rn_rm UCLAMP 01000100 .. 0 ..... 110001 ..... ..... @rda_rn_rm FCLAMP 01100100 .. 1 ..... 001001 ..... ..... @rda_rn_rm + +### SVE2p1 multi-vec contiguous load + +&zcrr_ldst rd png rn rm esz nreg +&zcri_ldst rd png rn imm esz nreg +%png 10:3 !function=plus_8 +%zd_ax2 1:4 !function=times_2 +%zd_ax4 2:3 !function=times_4 + +LD1_zcrr 10100000000 rm:5 0 esz:2 ... rn:5 .... - \ + &zcrr_ldst %png rd=%zd_ax2 nreg=2 +LD1_zcrr 10100000000 rm:5 1 esz:2 ... rn:5 ... 0- \ + &zcrr_ldst %png rd=%zd_ax4 nreg=4 + +ST1_zcrr 10100000001 rm:5 0 esz:2 ... rn:5 .... - \ + &zcrr_ldst %png rd=%zd_ax2 nreg=2 +ST1_zcrr 10100000001 rm:5 1 esz:2 ... rn:5 ... 0- \ + &zcrr_ldst %png rd=%zd_ax4 nreg=4 + +LD1_zcri 101000000100 imm:s4 0 esz:2 ... rn:5 .... - \ + &zcri_ldst %png rd=%zd_ax2 nreg=2 +LD1_zcri 101000000100 imm:s4 1 esz:2 ... rn:5 ... 0- \ + &zcri_ldst %png rd=%zd_ax4 nreg=4 + +ST1_zcri 101000000110 imm:s4 0 esz:2 ... rn:5 .... - \ + &zcri_ldst %png rd=%zd_ax2 nreg=2 +ST1_zcri 101000000110 imm:s4 1 esz:2 ... rn:5 ... 0- \ + &zcri_ldst %png rd=%zd_ax4 nreg=4 + +# Note: N bit and 0 bit (for nreg4) still mashed in rd. +# This is handled within gen_ldst_c(). +LD1_zcrr_stride 10100001000 rm:5 0 esz:2 ... rn:5 rd:5 \ + &zcrr_ldst %png nreg=2 +LD1_zcrr_stride 10100001000 rm:5 1 esz:2 ... rn:5 rd:5 \ + &zcrr_ldst %png nreg=4 + +ST1_zcrr_stride 10100001001 rm:5 0 esz:2 ... rn:5 rd:5 \ + &zcrr_ldst %png nreg=2 +ST1_zcrr_stride 10100001001 rm:5 1 esz:2 ... rn:5 rd:5 \ + &zcrr_ldst %png nreg=4 + +LD1_zcri_stride 101000010100 imm:s4 0 esz:2 ... rn:5 rd:5 \ + &zcri_ldst %png nreg=2 +LD1_zcri_stride 101000010100 imm:s4 1 esz:2 ... rn:5 rd:5 \ + &zcri_ldst %png nreg=4 + +ST1_zcri_stride 101000010110 imm:s4 0 esz:2 ... rn:5 rd:5 \ + &zcri_ldst %png nreg=2 +ST1_zcri_stride 101000010110 imm:s4 1 esz:2 ... rn:5 rd:5 \ + &zcri_ldst %png nreg=4 diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 42b05756a9..e6342990fa 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -7586,6 +7586,499 @@ DO_ST1_ZPZ_D(dd_be, zd, MO_64) #undef DO_ST1_ZPZ_S #undef DO_ST1_ZPZ_D +/* + * SVE2.1 consecutive register load/store + */ + +static unsigned sve2p1_cont_ldst_elements(SVEContLdSt *info, vaddr addr, + uint32_t png, intptr_t reg_max, + int N, int v_esz) +{ + const int esize = 1 << v_esz; + intptr_t reg_off_first = -1, reg_off_last = -1, reg_off_split; + DecodeCounter p = decode_counter(png, reg_max, v_esz); + unsigned b_count = p.count << v_esz; + unsigned b_stride = 1 << (v_esz + p.lg2_stride); + intptr_t page_split; + + /* Set all of the element indices to -1, and the TLB data to 0. */ + memset(info, -1, offsetof(SVEContLdSt, page)); + memset(info->page, 0, sizeof(info->page)); + + if (p.invert) { + if (b_count >= reg_max * N) { + return 0; + } + reg_off_first = b_count; + reg_off_last = reg_max * N - b_stride; + } else { + if (b_count == 0) { + return 0; + } + reg_off_first = 0; + reg_off_last = MIN(b_count - esize, reg_max * N - b_stride); + } + + info->reg_off_first[0] = reg_off_first; + info->mem_off_first[0] = reg_off_first; + + page_split = -(addr | TARGET_PAGE_MASK); + if (reg_off_last + esize <= page_split || reg_off_first >= page_split) { + /* The entire operation fits within a single page. */ + info->reg_off_last[0] = reg_off_last; + return b_stride; + } + + info->page_split = page_split; + reg_off_split = ROUND_DOWN(page_split, esize); + + /* + * This is the last full element on the first page, but it is not + * necessarily active. If there is no full element, i.e. the first + * active element is the one that's split, this value remains -1. + * It is useful as iteration bounds. + */ + if (reg_off_split != 0) { + info->reg_off_last[0] = ROUND_DOWN(reg_off_split - esize, b_stride); + } + + /* Determine if an unaligned element spans the pages. */ + if (page_split & (esize - 1)) { + /* It is helpful to know if the split element is active. */ + if ((reg_off_split & (b_stride - 1)) == 0) { + info->reg_off_split = reg_off_split; + info->mem_off_split = reg_off_split; + } + reg_off_split += esize; + } + + /* + * We do want the first active element on the second page, because + * this may affect the address reported in an exception. + */ + reg_off_split = ROUND_UP(reg_off_split, b_stride); + if (reg_off_split <= reg_off_last) { + info->reg_off_first[1] = reg_off_split; + info->mem_off_first[1] = reg_off_split; + info->reg_off_last[1] = reg_off_last; + } + return b_stride; +} + +static void sve2p1_cont_ldst_watchpoints(SVEContLdSt *info, CPUARMState *env, + target_ulong addr, unsigned estride, + int esize, int wp_access, uintptr_t ra) +{ +#ifndef CONFIG_USER_ONLY + intptr_t count_off, count_last; + int flags0 = info->page[0].flags; + int flags1 = info->page[1].flags; + + if (likely(!((flags0 | flags1) & TLB_WATCHPOINT))) { + return; + } + + /* Indicate that watchpoints are handled. */ + info->page[0].flags = flags0 & ~TLB_WATCHPOINT; + info->page[1].flags = flags1 & ~TLB_WATCHPOINT; + + if (flags0 & TLB_WATCHPOINT) { + count_off = info->reg_off_first[0]; + count_last = info->reg_off_split; + if (count_last < 0) { + count_last = info->reg_off_last[0]; + } + do { + cpu_check_watchpoint(env_cpu(env), addr + count_off, + esize, info->page[0].attrs, wp_access, ra); + count_off += estride; + } while (count_off <= count_last); + } + + count_off = info->reg_off_first[1]; + if ((flags1 & TLB_WATCHPOINT) && count_off >= 0) { + count_last = info->reg_off_last[1]; + do { + cpu_check_watchpoint(env_cpu(env), addr + count_off, + esize, info->page[1].attrs, + wp_access, ra); + count_off += estride; + } while (count_off <= count_last); + } +#endif +} + +static void sve2p1_cont_ldst_mte_check(SVEContLdSt *info, CPUARMState *env, + target_ulong addr, unsigned estride, + int esize, uint32_t mtedesc, + uintptr_t ra) +{ + intptr_t count_off, count_last; + + /* + * TODO: estride is always a small power of two, <= 8. + * Manipulate the stride within the loops such that + * - first iteration hits addr + off, as required, + * - second iteration hits ALIGN_UP(addr, 16), + * - other iterations advance addr by 16. + * This will minimize the probing to once per MTE granule. + */ + + /* Process the page only if MemAttr == Tagged. */ + if (info->page[0].tagged) { + count_off = info->reg_off_first[0]; + count_last = info->reg_off_split; + if (count_last < 0) { + count_last = info->reg_off_last[0]; + } + + do { + mte_check(env, mtedesc, addr + count_off, ra); + count_off += estride; + } while (count_off <= count_last); + } + + count_off = info->reg_off_first[1]; + if (count_off >= 0 && info->page[1].tagged) { + count_last = info->reg_off_last[1]; + do { + mte_check(env, mtedesc, addr + count_off, ra); + count_off += estride; + } while (count_off <= count_last); + } +} + +static inline QEMU_ALWAYS_INLINE +void sve2p1_ld1_c(CPUARMState *env, ARMVectorReg *zd, const vaddr addr, + uint32_t png, uint32_t desc, + const uintptr_t ra, const MemOp esz, + sve_ldst1_host_fn *host_fn, + sve_ldst1_tlb_fn *tlb_fn) +{ + const unsigned N = (desc >> SIMD_DATA_SHIFT) & 1 ? 4 : 2; + const unsigned rstride = 1 << ((desc >> (SIMD_DATA_SHIFT + 1)) % 4); + uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + const intptr_t reg_max = simd_oprsz(desc); + const unsigned esize = 1 << esz; + intptr_t count_off, count_last; + intptr_t reg_off, reg_last, reg_n; + SVEContLdSt info; + unsigned estride, flags; + void *host; + + estride = sve2p1_cont_ldst_elements(&info, addr, png, reg_max, N, esz); + if (estride == 0) { + /* The entire predicate was false; no load occurs. */ + for (unsigned n = 0; n < N; n++) { + memset(zd + n * rstride, 0, reg_max); + } + return; + } + + /* Probe the page(s). Exit with exception for any invalid page. */ + sve_cont_ldst_pages(&info, FAULT_ALL, env, addr, MMU_DATA_LOAD, ra); + + /* Handle watchpoints for all active elements. */ + sve2p1_cont_ldst_watchpoints(&info, env, addr, estride, + esize, BP_MEM_READ, ra); + + /* + * Handle mte checks for all active elements. + * Since TBI must be set for MTE, !mtedesc => !mte_active. + */ + if (mtedesc) { + sve2p1_cont_ldst_mte_check(&info, env, estride, addr, + esize, mtedesc, ra); + } + + flags = info.page[0].flags | info.page[1].flags; + if (unlikely(flags != 0)) { + /* + * At least one page includes MMIO. + * Any bus operation can fail with cpu_transaction_failed, + * which for ARM will raise SyncExternal. Perform the load + * into scratch memory to preserve register state until the end. + */ + ARMVectorReg scratch[4] = { }; + + count_off = info.reg_off_first[0]; + count_last = info.reg_off_last[1]; + if (count_last < 0) { + count_last = info.reg_off_split; + if (count_last < 0) { + count_last = info.reg_off_last[0]; + } + } + reg_off = count_off % reg_max; + reg_n = count_off / reg_max; + + do { + reg_last = MIN(count_last - count_off, reg_max - esize); + do { + tlb_fn(env, &scratch[reg_n], reg_off, addr + count_off, ra); + reg_off += estride; + count_off += estride; + } while (reg_off <= reg_last); + reg_off = 0; + reg_n++; + } while (count_off <= count_last); + + for (unsigned n = 0; n < N; ++n) { + memcpy(&zd[n * rstride], &scratch[n], reg_max); + } + return; + } + + /* The entire operation is in RAM, on valid pages. */ + + for (unsigned n = 0; n < N; ++n) { + memset(&zd[n * rstride], 0, reg_max); + } + + count_off = info.reg_off_first[0]; + count_last = info.reg_off_last[0]; + reg_off = count_off % reg_max; + reg_n = count_off / reg_max; + host = info.page[0].host; + + set_helper_retaddr(ra); + + do { + reg_last = MIN(count_last - reg_n * reg_max, reg_max - esize); + do { + host_fn(&zd[reg_n * rstride], reg_off, host + count_off); + reg_off += estride; + count_off += estride; + } while (reg_off <= reg_last); + reg_off = 0; + reg_n++; + } while (count_off <= count_last); + + clear_helper_retaddr(); + + /* + * Use the slow path to manage the cross-page misalignment. + * But we know this is RAM and cannot trap. + */ + count_off = info.reg_off_split; + if (unlikely(count_off >= 0)) { + reg_off = count_off % reg_max; + reg_n = count_off / reg_max; + tlb_fn(env, &zd[reg_n * rstride], reg_off, addr + count_off, ra); + } + + count_off = info.reg_off_first[1]; + if (unlikely(count_off >= 0)) { + count_last = info.reg_off_last[1]; + reg_off = count_off % reg_max; + reg_n = count_off / reg_max; + host = info.page[1].host; + + set_helper_retaddr(ra); + + do { + reg_last = MIN(count_last - reg_n * reg_max, reg_max - esize); + do { + host_fn(&zd[reg_n * rstride], reg_off, host + count_off); + reg_off += estride; + count_off += estride; + } while (reg_off <= reg_last); + reg_off = 0; + reg_n++; + } while (count_off <= count_last); + + clear_helper_retaddr(); + } +} + +void HELPER(sve2p1_ld1bb_c)(CPUARMState *env, void *vd, target_ulong addr, + uint32_t png, uint32_t desc) +{ + sve2p1_ld1_c(env, vd, addr, png, desc, GETPC(), MO_8, + sve_ld1bb_host, sve_ld1bb_tlb); +} + +#define DO_LD1_2(NAME, ESZ) \ +void HELPER(sve2p1_##NAME##_le_c)(CPUARMState *env, void *vd, \ + target_ulong addr, uint32_t png, \ + uint32_t desc) \ +{ \ + sve2p1_ld1_c(env, vd, addr, png, desc, GETPC(), ESZ, \ + sve_##NAME##_le_host, sve_##NAME##_le_tlb); \ +} \ +void HELPER(sve2p1_##NAME##_be_c)(CPUARMState *env, void *vd, \ + target_ulong addr, uint32_t png, \ + uint32_t desc) \ +{ \ + sve2p1_ld1_c(env, vd, addr, png, desc, GETPC(), ESZ, \ + sve_##NAME##_be_host, sve_##NAME##_be_tlb); \ +} + +DO_LD1_2(ld1hh, MO_16) +DO_LD1_2(ld1ss, MO_32) +DO_LD1_2(ld1dd, MO_64) + +#undef DO_LD1_2 + +static inline QEMU_ALWAYS_INLINE +void sve2p1_st1_c(CPUARMState *env, ARMVectorReg *zd, const vaddr addr, + uint32_t png, uint32_t desc, + const uintptr_t ra, const int esz, + sve_ldst1_host_fn *host_fn, + sve_ldst1_tlb_fn *tlb_fn) +{ + const unsigned N = (desc >> SIMD_DATA_SHIFT) & 1 ? 4 : 2; + const unsigned rstride = 1 << ((desc >> (SIMD_DATA_SHIFT + 1)) % 4); + uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + const intptr_t reg_max = simd_oprsz(desc); + const unsigned esize = 1 << esz; + intptr_t count_off, count_last; + intptr_t reg_off, reg_last, reg_n; + SVEContLdSt info; + unsigned estride, flags; + void *host; + + estride = sve2p1_cont_ldst_elements(&info, addr, png, reg_max, N, esz); + if (estride == 0) { + /* The entire predicate was false; no store occurs. */ + return; + } + + /* Probe the page(s). Exit with exception for any invalid page. */ + sve_cont_ldst_pages(&info, FAULT_ALL, env, addr, MMU_DATA_STORE, ra); + + /* Handle watchpoints for all active elements. */ + sve2p1_cont_ldst_watchpoints(&info, env, addr, estride, + esize, BP_MEM_WRITE, ra); + + /* + * Handle mte checks for all active elements. + * Since TBI must be set for MTE, !mtedesc => !mte_active. + */ + if (mtedesc) { + sve2p1_cont_ldst_mte_check(&info, env, estride, addr, + esize, mtedesc, ra); + } + + flags = info.page[0].flags | info.page[1].flags; + if (unlikely(flags != 0)) { + /* + * At least one page includes MMIO. + * Any bus operation can fail with cpu_transaction_failed, + * which for ARM will raise SyncExternal. Perform the load + * into scratch memory to preserve register state until the end. + */ + count_off = info.reg_off_first[0]; + count_last = info.reg_off_last[1]; + if (count_last < 0) { + count_last = info.reg_off_split; + if (count_last < 0) { + count_last = info.reg_off_last[0]; + } + } + reg_off = count_off % reg_max; + reg_n = count_off / reg_max; + + do { + reg_last = MIN(count_last - count_off, reg_max - esize); + do { + tlb_fn(env, &zd[reg_n * rstride], reg_off, addr + count_off, ra); + reg_off += estride; + count_off += estride; + } while (reg_off <= reg_last); + reg_off = 0; + reg_n++; + } while (count_off <= count_last); + return; + } + + /* The entire operation is in RAM, on valid pages. */ + + count_off = info.reg_off_first[0]; + count_last = info.reg_off_last[0]; + reg_off = count_off % reg_max; + reg_n = count_off / reg_max; + host = info.page[0].host; + + set_helper_retaddr(ra); + + do { + reg_last = MIN(count_last - reg_n * reg_max, reg_max - esize); + do { + host_fn(&zd[reg_n * rstride], reg_off, host + count_off); + reg_off += estride; + count_off += estride; + } while (reg_off <= reg_last); + reg_off = 0; + reg_n++; + } while (count_off <= count_last); + + clear_helper_retaddr(); + + /* + * Use the slow path to manage the cross-page misalignment. + * But we know this is RAM and cannot trap. + */ + count_off = info.reg_off_split; + if (unlikely(count_off >= 0)) { + reg_off = count_off % reg_max; + reg_n = count_off / reg_max; + tlb_fn(env, &zd[reg_n * rstride], reg_off, addr + count_off, ra); + } + + count_off = info.reg_off_first[1]; + if (unlikely(count_off >= 0)) { + count_last = info.reg_off_last[1]; + reg_off = count_off % reg_max; + reg_n = count_off / reg_max; + host = info.page[1].host; + + set_helper_retaddr(ra); + + do { + reg_last = MIN(count_last - reg_n * reg_max, reg_max - esize); + do { + host_fn(&zd[reg_n * rstride], reg_off, host + count_off); + reg_off += estride; + count_off += estride; + } while (reg_off <= reg_last); + reg_off = 0; + reg_n++; + } while (count_off <= count_last); + + clear_helper_retaddr(); + } +} + +void HELPER(sve2p1_st1bb_c)(CPUARMState *env, void *vd, target_ulong addr, + uint32_t png, uint32_t desc) +{ + sve2p1_st1_c(env, vd, addr, png, desc, GETPC(), MO_8, + sve_st1bb_host, sve_st1bb_tlb); +} + +#define DO_ST1_2(NAME, ESZ) \ +void HELPER(sve2p1_##NAME##_le_c)(CPUARMState *env, void *vd, \ + target_ulong addr, uint32_t png, \ + uint32_t desc) \ +{ \ + sve2p1_st1_c(env, vd, addr, png, desc, GETPC(), ESZ, \ + sve_##NAME##_le_host, sve_##NAME##_le_tlb); \ +} \ +void HELPER(sve2p1_##NAME##_be_c)(CPUARMState *env, void *vd, \ + target_ulong addr, uint32_t png, \ + uint32_t desc) \ +{ \ + sve2p1_st1_c(env, vd, addr, png, desc, GETPC(), ESZ, \ + sve_##NAME##_be_host, sve_##NAME##_be_tlb); \ +} + +DO_ST1_2(st1hh, MO_16) +DO_ST1_2(st1ss, MO_32) +DO_ST1_2(st1dd, MO_64) + +#undef DO_ST1_2 + void HELPER(sve2_eor3)(void *vd, void *vn, void *vm, void *vk, uint32_t desc) { intptr_t i, opr_sz = simd_oprsz(desc) / 8; diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 02f885dd48..dfb53e4bf4 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -7863,3 +7863,106 @@ TRANS_FEAT(UQCVTN_sh, aa64_sme2_or_sve2p1, gen_gvec_ool_zz, gen_helper_sme2_uqcvtn_sh, a->rd, a->rn, 0) TRANS_FEAT(SQCVTUN_sh, aa64_sme2_or_sve2p1, gen_gvec_ool_zz, gen_helper_sme2_sqcvtun_sh, a->rd, a->rn, 0) + +static bool gen_ldst_c(DisasContext *s, TCGv_i64 addr, int zd, int png, + MemOp esz, bool is_write, int n, bool strided) +{ + typedef void ldst_c_fn(TCGv_env, TCGv_ptr, TCGv_i64, + TCGv_i32, TCGv_i32); + static ldst_c_fn * const f_ldst[2][2][4] = { + { { gen_helper_sve2p1_ld1bb_c, + gen_helper_sve2p1_ld1hh_le_c, + gen_helper_sve2p1_ld1ss_le_c, + gen_helper_sve2p1_ld1dd_le_c, }, + { gen_helper_sve2p1_ld1bb_c, + gen_helper_sve2p1_ld1hh_be_c, + gen_helper_sve2p1_ld1ss_be_c, + gen_helper_sve2p1_ld1dd_be_c, } }, + + { { gen_helper_sve2p1_st1bb_c, + gen_helper_sve2p1_st1hh_le_c, + gen_helper_sve2p1_st1ss_le_c, + gen_helper_sve2p1_st1dd_le_c, }, + { gen_helper_sve2p1_st1bb_c, + gen_helper_sve2p1_st1hh_be_c, + gen_helper_sve2p1_st1ss_be_c, + gen_helper_sve2p1_st1dd_be_c, } } + }; + + TCGv_i32 t_png, t_desc; + TCGv_ptr t_zd; + uint32_t desc, lg2_rstride = 0; + bool be = s->be_data == MO_BE; + + assert(n == 2 || n == 4); + if (strided) { + lg2_rstride = 3; + if (n == 4) { + /* Validate ZD alignment. */ + if (zd & 4) { + return false; + } + lg2_rstride = 2; + } + /* Ignore non-temporal bit */ + zd &= ~8; + } + + if (strided || !dc_isar_feature(aa64_sve2p1, s) + ? !sme_sm_enabled_check(s) + : !sve_access_check(s)) { + return true; + } + + if (!s->mte_active[0]) { + addr = clean_data_tbi(s, addr); + } + + desc = n == 2 ? 0 : 1; + desc = desc | (lg2_rstride << 1); + desc = make_svemte_desc(s, vec_full_reg_size(s), 1, esz, is_write, desc); + t_desc = tcg_constant_i32(desc); + + t_png = tcg_temp_new_i32(); + tcg_gen_ld16u_i32(t_png, tcg_env, + pred_full_reg_offset(s, png) ^ + (HOST_BIG_ENDIAN ? 6 : 0)); + + t_zd = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(t_zd, tcg_env, vec_full_reg_offset(s, zd)); + + f_ldst[is_write][be][esz](tcg_env, t_zd, addr, t_png, t_desc); + return true; +} + +static bool gen_ldst_zcrr_c(DisasContext *s, arg_zcrr_ldst *a, + bool is_write, bool strided) +{ + TCGv_i64 addr = tcg_temp_new_i64(); + + tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), a->esz); + tcg_gen_add_i64(addr, addr, cpu_reg_sp(s, a->rn)); + return gen_ldst_c(s, addr, a->rd, a->png, a->esz, is_write, + a->nreg, strided); +} + +static bool gen_ldst_zcri_c(DisasContext *s, arg_zcri_ldst *a, + bool is_write, bool strided) +{ + TCGv_i64 addr = tcg_temp_new_i64(); + + tcg_gen_addi_i64(addr, cpu_reg_sp(s, a->rn), + a->imm * a->nreg * vec_full_reg_size(s)); + return gen_ldst_c(s, addr, a->rd, a->png, a->esz, is_write, + a->nreg, strided); +} + +TRANS_FEAT(LD1_zcrr, aa64_sme2_or_sve2p1, gen_ldst_zcrr_c, a, false, false) +TRANS_FEAT(LD1_zcri, aa64_sme2_or_sve2p1, gen_ldst_zcri_c, a, false, false) +TRANS_FEAT(ST1_zcrr, aa64_sme2_or_sve2p1, gen_ldst_zcrr_c, a, true, false) +TRANS_FEAT(ST1_zcri, aa64_sme2_or_sve2p1, gen_ldst_zcri_c, a, true, false) + +TRANS_FEAT(LD1_zcrr_stride, aa64_sme2, gen_ldst_zcrr_c, a, false, true) +TRANS_FEAT(LD1_zcri_stride, aa64_sme2, gen_ldst_zcri_c, a, false, true) +TRANS_FEAT(ST1_zcrr_stride, aa64_sme2, gen_ldst_zcrr_c, a, true, true) +TRANS_FEAT(ST1_zcri_stride, aa64_sme2, gen_ldst_zcri_c, a, true, true) From 0af0c9bbb94b5049354cd7a102929b1fbf097c20 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:57 -0600 Subject: [PATCH 2041/2760] target/arm: Split the ST_zpri and ST_zprr patterns The msz > esz encodings are reserved, and some of them are about to be reused. Split these patterns so that the new insns do not overlap. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-95-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sve.decode | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index bf3d4f4853..7020771898 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -252,7 +252,7 @@ # Stores; user must fill in ESZ, MSZ, NREG as needed. @rprr_store ....... .. .. rm:5 ... pg:3 rn:5 rd:5 &rprr_store -@rpri_store_msz ....... msz:2 .. . imm:s4 ... pg:3 rn:5 rd:5 &rpri_store +@rpri_store ....... .. .. . imm:s4 ... pg:3 rn:5 rd:5 &rpri_store @rprr_store_esz_n0 ....... .. esz:2 rm:5 ... pg:3 rn:5 rd:5 \ &rprr_store nreg=0 @rprr_scatter_store ....... msz:2 .. rm:5 ... pg:3 rn:5 rd:5 \ @@ -1334,8 +1334,16 @@ STR_zri 1110010 11 0. ..... 010 ... ..... ..... @rd_rn_i9 # SVE contiguous store (scalar plus immediate) # ST1B, ST1H, ST1W, ST1D; require msz <= esz -ST_zpri 1110010 .. esz:2 0.... 111 ... ..... ..... \ - @rpri_store_msz nreg=0 +ST_zpri 1110010 00 esz:2 0.... 111 ... ..... ..... \ + @rpri_store msz=0 nreg=0 +ST_zpri 1110010 01 esz:2 0.... 111 ... ..... ..... \ + @rpri_store msz=1 nreg=0 +ST_zpri 1110010 10 10 0.... 111 ... ..... ..... \ + @rpri_store msz=2 esz=2 nreg=0 +ST_zpri 1110010 10 11 0.... 111 ... ..... ..... \ + @rpri_store msz=2 esz=3 nreg=0 +ST_zpri 1110010 11 11 0.... 111 ... ..... ..... \ + @rpri_store msz=3 esz=3 nreg=0 # SVE contiguous store (scalar plus scalar) # ST1B, ST1H, ST1W, ST1D; require msz <= esz @@ -1344,20 +1352,22 @@ ST_zprr 1110010 00 .. ..... 010 ... ..... ..... \ @rprr_store_esz_n0 msz=0 ST_zprr 1110010 01 .. ..... 010 ... ..... ..... \ @rprr_store_esz_n0 msz=1 -ST_zprr 1110010 10 .. ..... 010 ... ..... ..... \ - @rprr_store_esz_n0 msz=2 +ST_zprr 1110010 10 10 ..... 010 ... ..... ..... \ + @rprr_store msz=2 esz=2 nreg=0 +ST_zprr 1110010 10 11 ..... 010 ... ..... ..... \ + @rprr_store msz=2 esz=3 nreg=0 ST_zprr 1110010 11 11 ..... 010 ... ..... ..... \ @rprr_store msz=3 esz=3 nreg=0 # SVE contiguous non-temporal store (scalar plus immediate) (nreg == 0) # SVE store multiple structures (scalar plus immediate) (nreg != 0) ST_zpri 1110010 .. nreg:2 1.... 111 ... ..... ..... \ - @rpri_store_msz esz=%size_23 + @rpri_store msz=%size_23 esz=%size_23 # SVE contiguous non-temporal store (scalar plus scalar) (nreg == 0) # SVE store multiple structures (scalar plus scalar) (nreg != 0) -ST_zprr 1110010 msz:2 nreg:2 ..... 011 ... ..... ..... \ - @rprr_store esz=%size_23 +ST_zprr 1110010 .. nreg:2 ..... 011 ... ..... ..... \ + @rprr_store msz=%size_23 esz=%size_23 # SVE 32-bit scatter store (scalar plus 32-bit scaled offsets) # Require msz > 0 && msz <= esz. From fc5f060bcb7b45d8e330e294947cac7dffa0ea82 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:58 -0600 Subject: [PATCH 2042/2760] target/arm: Implement {LD1, ST1}{W, D} (128-bit element) for SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-96-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 22 +++++ target/arm/tcg/sve.decode | 20 +++++ target/arm/tcg/sve_helper.c | 6 ++ target/arm/tcg/sve_ldst_internal.h | 26 ++++++ target/arm/tcg/translate-sve.c | 136 +++++++++++++++++++++++------ 5 files changed, 183 insertions(+), 27 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index c4736d7510..1999c4bb1f 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1678,9 +1678,15 @@ DEF_HELPER_FLAGS_4(sve_ld1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1squ_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1dqu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + DEF_HELPER_FLAGS_4(sve_ld1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1squ_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1dqu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + DEF_HELPER_FLAGS_4(sve_ld1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld2bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld3bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) @@ -1736,9 +1742,15 @@ DEF_HELPER_FLAGS_4(sve_ld1hds_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1sdu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1sds_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1squ_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1dqu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + DEF_HELPER_FLAGS_4(sve_ld1sdu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1sds_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1squ_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1dqu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + DEF_HELPER_FLAGS_4(sve_ldff1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ldff1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ldff1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) @@ -1946,6 +1958,11 @@ DEF_HELPER_FLAGS_4(sve_st1hd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1sd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1sd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1sq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1sq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1dq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1dq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + DEF_HELPER_FLAGS_4(sve_st1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st2bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st3bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) @@ -1993,6 +2010,11 @@ DEF_HELPER_FLAGS_4(sve_st1hd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1sd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1sd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1sq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1sq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1dq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st1dq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + DEF_HELPER_FLAGS_6(sve_ldbsu_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldhsu_le_zsu, TCG_CALL_NO_WG, diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 7020771898..bf33bc305f 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1240,12 +1240,24 @@ LD1_zpiz 1000010 .. 01 ..... 1.. ... ..... ..... \ # SVE contiguous load (scalar plus scalar) LD_zprr 1010010 .... ..... 010 ... ..... ..... @rprr_load_dt nreg=0 +# LD1W (128-bit element) +LD_zprr 1010010 1000 rm:5 100 pg:3 rn:5 rd:5 \ + &rprr_load dtype=16 nreg=0 +# LD1D (128-bit element) +LD_zprr 1010010 1100 rm:5 100 pg:3 rn:5 rd:5 \ + &rprr_load dtype=17 nreg=0 # SVE contiguous first-fault load (scalar plus scalar) LDFF1_zprr 1010010 .... ..... 011 ... ..... ..... @rprr_load_dt nreg=0 # SVE contiguous load (scalar plus immediate) LD_zpri 1010010 .... 0.... 101 ... ..... ..... @rpri_load_dt nreg=0 +# LD1W (128-bit element) +LD_zpri 1010010 1000 1 imm:s4 001 pg:3 rn:5 rd:5 \ + &rpri_load dtype=16 nreg=0 +# LD1D (128-bit element) +LD_zpri 1010010 1100 1 imm:s4 001 pg:3 rn:5 rd:5 \ + &rpri_load dtype=17 nreg=0 # SVE contiguous non-fault load (scalar plus immediate) LDNF1_zpri 1010010 .... 1.... 101 ... ..... ..... @rpri_load_dt nreg=0 @@ -1344,6 +1356,10 @@ ST_zpri 1110010 10 11 0.... 111 ... ..... ..... \ @rpri_store msz=2 esz=3 nreg=0 ST_zpri 1110010 11 11 0.... 111 ... ..... ..... \ @rpri_store msz=3 esz=3 nreg=0 +ST_zpri 1110010 10 00 0.... 111 ... ..... ..... \ + @rpri_store msz=2 esz=4 nreg=0 +ST_zpri 1110010 11 10 0.... 111 ... ..... ..... \ + @rpri_store msz=3 esz=4 nreg=0 # SVE contiguous store (scalar plus scalar) # ST1B, ST1H, ST1W, ST1D; require msz <= esz @@ -1358,6 +1374,10 @@ ST_zprr 1110010 10 11 ..... 010 ... ..... ..... \ @rprr_store msz=2 esz=3 nreg=0 ST_zprr 1110010 11 11 ..... 010 ... ..... ..... \ @rprr_store msz=3 esz=3 nreg=0 +ST_zprr 1110010 10 00 ..... 010 ... ..... ..... \ + @rprr_store msz=2 esz=4 nreg=0 +ST_zprr 1110010 11 10 ..... 010 ... ..... ..... \ + @rprr_store msz=3 esz=4 nreg=0 # SVE contiguous non-temporal store (scalar plus immediate) (nreg == 0) # SVE store multiple structures (scalar plus immediate) (nreg != 0) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index e6342990fa..91cd5970ee 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -6359,6 +6359,9 @@ DO_LD1_2(ld1sds, MO_64, MO_32) DO_LD1_2(ld1dd, MO_64, MO_64) +DO_LD1_2(ld1squ, MO_32, MO_128) +DO_LD1_2(ld1dqu, MO_64, MO_128) + #undef DO_LD1_1 #undef DO_LD1_2 @@ -6981,6 +6984,9 @@ DO_STN_2(2, dd, MO_64, MO_64) DO_STN_2(3, dd, MO_64, MO_64) DO_STN_2(4, dd, MO_64, MO_64) +DO_STN_2(1, sq, MO_128, MO_32) +DO_STN_2(1, dq, MO_128, MO_64) + #undef DO_STN_1 #undef DO_STN_2 diff --git a/target/arm/tcg/sve_ldst_internal.h b/target/arm/tcg/sve_ldst_internal.h index f2243daf37..e87beba435 100644 --- a/target/arm/tcg/sve_ldst_internal.h +++ b/target/arm/tcg/sve_ldst_internal.h @@ -116,6 +116,31 @@ DO_ST_PRIM_2(sd, H1_8, uint64_t, uint32_t, stl) DO_LD_PRIM_2(dd, H1_8, uint64_t, uint64_t, ldq) DO_ST_PRIM_2(dd, H1_8, uint64_t, uint64_t, stq) +#define DO_LD_PRIM_3(NAME, FUNC) \ + static inline void sve_##NAME##_host(void *vd, \ + intptr_t reg_off, void *host) \ + { sve_##FUNC##_host(vd, reg_off, host); \ + *(uint64_t *)(vd + reg_off + 8) = 0; } \ + static inline void sve_##NAME##_tlb(CPUARMState *env, void *vd, \ + intptr_t reg_off, target_ulong addr, uintptr_t ra) \ + { sve_##FUNC##_tlb(env, vd, reg_off, addr, ra); \ + *(uint64_t *)(vd + reg_off + 8) = 0; } + +DO_LD_PRIM_3(ld1squ_be, ld1sdu_be) +DO_LD_PRIM_3(ld1squ_le, ld1sdu_le) +DO_LD_PRIM_3(ld1dqu_be, ld1dd_be) +DO_LD_PRIM_3(ld1dqu_le, ld1dd_le) + +#define sve_st1sq_be_host sve_st1sd_be_host +#define sve_st1sq_le_host sve_st1sd_le_host +#define sve_st1sq_be_tlb sve_st1sd_be_tlb +#define sve_st1sq_le_tlb sve_st1sd_le_tlb + +#define sve_st1dq_be_host sve_st1dd_be_host +#define sve_st1dq_le_host sve_st1dd_le_host +#define sve_st1dq_be_tlb sve_st1dd_be_tlb +#define sve_st1dq_le_tlb sve_st1dd_le_tlb + #undef DO_LD_TLB #undef DO_ST_TLB #undef DO_LD_HOST @@ -123,6 +148,7 @@ DO_ST_PRIM_2(dd, H1_8, uint64_t, uint64_t, stq) #undef DO_ST_PRIM_1 #undef DO_LD_PRIM_2 #undef DO_ST_PRIM_2 +#undef DO_LD_PRIM_3 /* * Resolve the guest virtual address to info->host and info->flags. diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index dfb53e4bf4..8e945c5d2d 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4817,21 +4817,25 @@ static bool trans_STR_pri(DisasContext *s, arg_rri *a) */ /* The memory mode of the dtype. */ -static const MemOp dtype_mop[16] = { +static const MemOp dtype_mop[19] = { MO_UB, MO_UB, MO_UB, MO_UB, MO_SL, MO_UW, MO_UW, MO_UW, MO_SW, MO_SW, MO_UL, MO_UL, - MO_SB, MO_SB, MO_SB, MO_UQ + MO_SB, MO_SB, MO_SB, MO_UQ, + /* Artificial values used by decode */ + MO_UL, MO_UQ, MO_128, }; #define dtype_msz(x) (dtype_mop[x] & MO_SIZE) /* The vector element size of dtype. */ -static const uint8_t dtype_esz[16] = { +static const uint8_t dtype_esz[19] = { 0, 1, 2, 3, 3, 1, 2, 3, 3, 2, 2, 3, - 3, 2, 1, 3 + 3, 2, 1, 3, + /* Artificial values used by decode */ + 4, 4, 4, }; uint32_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, @@ -4882,7 +4886,7 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, } /* Indexed by [mte][be][dtype][nreg] */ -static gen_helper_gvec_mem * const ldr_fns[2][2][16][4] = { +static gen_helper_gvec_mem * const ldr_fns[2][2][18][4] = { { /* mte inactive, little-endian */ { { gen_helper_sve_ld1bb_r, gen_helper_sve_ld2bb_r, gen_helper_sve_ld3bb_r, gen_helper_sve_ld4bb_r }, @@ -4906,7 +4910,11 @@ static gen_helper_gvec_mem * const ldr_fns[2][2][16][4] = { { gen_helper_sve_ld1bss_r, NULL, NULL, NULL }, { gen_helper_sve_ld1bhs_r, NULL, NULL, NULL }, { gen_helper_sve_ld1dd_le_r, gen_helper_sve_ld2dd_le_r, - gen_helper_sve_ld3dd_le_r, gen_helper_sve_ld4dd_le_r } }, + gen_helper_sve_ld3dd_le_r, gen_helper_sve_ld4dd_le_r }, + + { gen_helper_sve_ld1squ_le_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1dqu_le_r, NULL, NULL, NULL }, + }, /* mte inactive, big-endian */ { { gen_helper_sve_ld1bb_r, gen_helper_sve_ld2bb_r, @@ -4931,7 +4939,12 @@ static gen_helper_gvec_mem * const ldr_fns[2][2][16][4] = { { gen_helper_sve_ld1bss_r, NULL, NULL, NULL }, { gen_helper_sve_ld1bhs_r, NULL, NULL, NULL }, { gen_helper_sve_ld1dd_be_r, gen_helper_sve_ld2dd_be_r, - gen_helper_sve_ld3dd_be_r, gen_helper_sve_ld4dd_be_r } } }, + gen_helper_sve_ld3dd_be_r, gen_helper_sve_ld4dd_be_r }, + + { gen_helper_sve_ld1squ_be_r, NULL, NULL, NULL }, + { gen_helper_sve_ld1dqu_be_r, NULL, NULL, NULL }, + }, + }, { /* mte active, little-endian */ { { gen_helper_sve_ld1bb_r_mte, @@ -4964,7 +4977,11 @@ static gen_helper_gvec_mem * const ldr_fns[2][2][16][4] = { { gen_helper_sve_ld1dd_le_r_mte, gen_helper_sve_ld2dd_le_r_mte, gen_helper_sve_ld3dd_le_r_mte, - gen_helper_sve_ld4dd_le_r_mte } }, + gen_helper_sve_ld4dd_le_r_mte }, + + { gen_helper_sve_ld1squ_le_r_mte, NULL, NULL, NULL }, + { gen_helper_sve_ld1dqu_le_r_mte, NULL, NULL, NULL }, + }, /* mte active, big-endian */ { { gen_helper_sve_ld1bb_r_mte, @@ -4997,7 +5014,12 @@ static gen_helper_gvec_mem * const ldr_fns[2][2][16][4] = { { gen_helper_sve_ld1dd_be_r_mte, gen_helper_sve_ld2dd_be_r_mte, gen_helper_sve_ld3dd_be_r_mte, - gen_helper_sve_ld4dd_be_r_mte } } }, + gen_helper_sve_ld4dd_be_r_mte }, + + { gen_helper_sve_ld1squ_be_r_mte, NULL, NULL, NULL }, + { gen_helper_sve_ld1dqu_be_r_mte, NULL, NULL, NULL }, + }, + }, }; static void do_ld_zpa(DisasContext *s, int zt, int pg, @@ -5016,9 +5038,22 @@ static void do_ld_zpa(DisasContext *s, int zt, int pg, static bool trans_LD_zprr(DisasContext *s, arg_rprr_load *a) { - if (a->rm == 31 || !dc_isar_feature(aa64_sve, s)) { + if (a->rm == 31) { return false; } + + /* dtypes 16 and 17 are artificial, representing 128-bit element */ + if (a->dtype < 16) { + if (!dc_isar_feature(aa64_sve, s)) { + return false; + } + } else { + if (!dc_isar_feature(aa64_sve2p1, s)) { + return false; + } + s->is_nonstreaming = true; + } + if (sve_access_check(s)) { TCGv_i64 addr = tcg_temp_new_i64(); tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), dtype_msz(a->dtype)); @@ -5030,9 +5065,18 @@ static bool trans_LD_zprr(DisasContext *s, arg_rprr_load *a) static bool trans_LD_zpri(DisasContext *s, arg_rpri_load *a) { - if (!dc_isar_feature(aa64_sve, s)) { - return false; + /* dtypes 16 and 17 are artificial, representing 128-bit element */ + if (a->dtype < 16) { + if (!dc_isar_feature(aa64_sve, s)) { + return false; + } + } else { + if (!dc_isar_feature(aa64_sve2p1, s)) { + return false; + } + s->is_nonstreaming = true; } + if (sve_access_check(s)) { int vsz = vec_full_reg_size(s); int elements = vsz >> dtype_esz[a->dtype]; @@ -5479,7 +5523,7 @@ static bool trans_LD1R_zpri(DisasContext *s, arg_rpri_load *a) static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, int msz, int esz, int nreg) { - static gen_helper_gvec_mem * const fn_single[2][2][4][4] = { + static gen_helper_gvec_mem * const fn_single[2][2][4][5] = { { { { gen_helper_sve_st1bb_r, gen_helper_sve_st1bh_r, gen_helper_sve_st1bs_r, @@ -5490,9 +5534,11 @@ static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, gen_helper_sve_st1hd_le_r }, { NULL, NULL, gen_helper_sve_st1ss_le_r, - gen_helper_sve_st1sd_le_r }, + gen_helper_sve_st1sd_le_r, + gen_helper_sve_st1sq_le_r, }, { NULL, NULL, NULL, - gen_helper_sve_st1dd_le_r } }, + gen_helper_sve_st1dd_le_r, + gen_helper_sve_st1dq_le_r, } }, { { gen_helper_sve_st1bb_r, gen_helper_sve_st1bh_r, gen_helper_sve_st1bs_r, @@ -5503,9 +5549,11 @@ static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, gen_helper_sve_st1hd_be_r }, { NULL, NULL, gen_helper_sve_st1ss_be_r, - gen_helper_sve_st1sd_be_r }, + gen_helper_sve_st1sd_be_r, + gen_helper_sve_st1sq_be_r }, { NULL, NULL, NULL, - gen_helper_sve_st1dd_be_r } } }, + gen_helper_sve_st1dd_be_r, + gen_helper_sve_st1dq_be_r } } }, { { { gen_helper_sve_st1bb_r_mte, gen_helper_sve_st1bh_r_mte, @@ -5517,9 +5565,11 @@ static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, gen_helper_sve_st1hd_le_r_mte }, { NULL, NULL, gen_helper_sve_st1ss_le_r_mte, - gen_helper_sve_st1sd_le_r_mte }, + gen_helper_sve_st1sd_le_r_mte, + gen_helper_sve_st1sq_le_r_mte }, { NULL, NULL, NULL, - gen_helper_sve_st1dd_le_r_mte } }, + gen_helper_sve_st1dd_le_r_mte, + gen_helper_sve_st1dq_le_r_mte } }, { { gen_helper_sve_st1bb_r_mte, gen_helper_sve_st1bh_r_mte, gen_helper_sve_st1bs_r_mte, @@ -5530,9 +5580,11 @@ static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, gen_helper_sve_st1hd_be_r_mte }, { NULL, NULL, gen_helper_sve_st1ss_be_r_mte, - gen_helper_sve_st1sd_be_r_mte }, + gen_helper_sve_st1sd_be_r_mte, + gen_helper_sve_st1sq_be_r_mte }, { NULL, NULL, NULL, - gen_helper_sve_st1dd_be_r_mte } } }, + gen_helper_sve_st1dd_be_r_mte, + gen_helper_sve_st1dq_be_r_mte } } }, }; static gen_helper_gvec_mem * const fn_multiple[2][2][3][4] = { { { { gen_helper_sve_st2bb_r, @@ -5601,12 +5653,27 @@ static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, static bool trans_ST_zprr(DisasContext *s, arg_rprr_store *a) { - if (!dc_isar_feature(aa64_sve, s)) { - return false; - } if (a->rm == 31 || a->msz > a->esz) { return false; } + switch (a->esz) { + case MO_8 ... MO_64: + if (!dc_isar_feature(aa64_sve, s)) { + return false; + } + break; + case MO_128: + assert(a->msz < a->esz); + assert(a->nreg == 0); + if (!dc_isar_feature(aa64_sve2p1, s)) { + return false; + } + s->is_nonstreaming = true; + break; + default: + g_assert_not_reached(); + } + if (sve_access_check(s)) { TCGv_i64 addr = tcg_temp_new_i64(); tcg_gen_shli_i64(addr, cpu_reg(s, a->rm), a->msz); @@ -5618,12 +5685,27 @@ static bool trans_ST_zprr(DisasContext *s, arg_rprr_store *a) static bool trans_ST_zpri(DisasContext *s, arg_rpri_store *a) { - if (!dc_isar_feature(aa64_sve, s)) { - return false; - } if (a->msz > a->esz) { return false; } + switch (a->esz) { + case MO_8 ... MO_64: + if (!dc_isar_feature(aa64_sve, s)) { + return false; + } + break; + case MO_128: + assert(a->msz < a->esz); + assert(a->nreg == 0); + if (!dc_isar_feature(aa64_sve2p1, s)) { + return false; + } + s->is_nonstreaming = true; + break; + default: + g_assert_not_reached(); + } + if (sve_access_check(s)) { int vsz = vec_full_reg_size(s); int elements = vsz >> a->esz; From 697fe75202f56135b74940ec41deecd3357bda23 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:20:59 -0600 Subject: [PATCH 2043/2760] target/arm: Move ld1qq and st1qq primitives to sve_ldst_internal.h Move from sme_helper.c to the shared header. Add a comment noting the lack of atomicity. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-97-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/sme_helper.c | 44 +++------------------ target/arm/tcg/sve_ldst_internal.h | 63 ++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 38 deletions(-) diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index c1166e4ffa..df16bb2f9c 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -415,54 +415,22 @@ static inline void sme_##NAME##_v_tlb(CPUARMState *env, void *za, \ TLB(env, useronly_clean_ptr(addr), val, ra); \ } -/* - * The ARMVectorReg elements are stored in host-endian 64-bit units. - * For 128-bit quantities, the sequence defined by the Elem[] pseudocode - * corresponds to storing the two 64-bit pieces in little-endian order. - */ -#define DO_LDQ(HNAME, VNAME, BE, HOST, TLB) \ -static inline void HNAME##_host(void *za, intptr_t off, void *host) \ -{ \ - uint64_t val0 = HOST(host), val1 = HOST(host + 8); \ - uint64_t *ptr = za + off; \ - ptr[0] = BE ? val1 : val0, ptr[1] = BE ? val0 : val1; \ -} \ +#define DO_LDQ(HNAME, VNAME) \ static inline void VNAME##_v_host(void *za, intptr_t off, void *host) \ { \ HNAME##_host(za, tile_vslice_offset(off), host); \ } \ -static inline void HNAME##_tlb(CPUARMState *env, void *za, intptr_t off, \ - target_ulong addr, uintptr_t ra) \ -{ \ - uint64_t val0 = TLB(env, useronly_clean_ptr(addr), ra); \ - uint64_t val1 = TLB(env, useronly_clean_ptr(addr + 8), ra); \ - uint64_t *ptr = za + off; \ - ptr[0] = BE ? val1 : val0, ptr[1] = BE ? val0 : val1; \ -} \ static inline void VNAME##_v_tlb(CPUARMState *env, void *za, intptr_t off, \ target_ulong addr, uintptr_t ra) \ { \ HNAME##_tlb(env, za, tile_vslice_offset(off), addr, ra); \ } -#define DO_STQ(HNAME, VNAME, BE, HOST, TLB) \ -static inline void HNAME##_host(void *za, intptr_t off, void *host) \ -{ \ - uint64_t *ptr = za + off; \ - HOST(host, ptr[BE]); \ - HOST(host + 8, ptr[!BE]); \ -} \ +#define DO_STQ(HNAME, VNAME) \ static inline void VNAME##_v_host(void *za, intptr_t off, void *host) \ { \ HNAME##_host(za, tile_vslice_offset(off), host); \ } \ -static inline void HNAME##_tlb(CPUARMState *env, void *za, intptr_t off, \ - target_ulong addr, uintptr_t ra) \ -{ \ - uint64_t *ptr = za + off; \ - TLB(env, useronly_clean_ptr(addr), ptr[BE], ra); \ - TLB(env, useronly_clean_ptr(addr + 8), ptr[!BE], ra); \ -} \ static inline void VNAME##_v_tlb(CPUARMState *env, void *za, intptr_t off, \ target_ulong addr, uintptr_t ra) \ { \ @@ -477,8 +445,8 @@ DO_LD(ld1s_le, uint32_t, ldl_le_p, cpu_ldl_le_data_ra) DO_LD(ld1d_be, uint64_t, ldq_be_p, cpu_ldq_be_data_ra) DO_LD(ld1d_le, uint64_t, ldq_le_p, cpu_ldq_le_data_ra) -DO_LDQ(sve_ld1qq_be, sme_ld1q_be, 1, ldq_be_p, cpu_ldq_be_data_ra) -DO_LDQ(sve_ld1qq_le, sme_ld1q_le, 0, ldq_le_p, cpu_ldq_le_data_ra) +DO_LDQ(sve_ld1qq_be, sme_ld1q_be) +DO_LDQ(sve_ld1qq_le, sme_ld1q_le) DO_ST(st1b, uint8_t, stb_p, cpu_stb_data_ra) DO_ST(st1h_be, uint16_t, stw_be_p, cpu_stw_be_data_ra) @@ -488,8 +456,8 @@ DO_ST(st1s_le, uint32_t, stl_le_p, cpu_stl_le_data_ra) DO_ST(st1d_be, uint64_t, stq_be_p, cpu_stq_be_data_ra) DO_ST(st1d_le, uint64_t, stq_le_p, cpu_stq_le_data_ra) -DO_STQ(sve_st1qq_be, sme_st1q_be, 1, stq_be_p, cpu_stq_be_data_ra) -DO_STQ(sve_st1qq_le, sme_st1q_le, 0, stq_le_p, cpu_stq_le_data_ra) +DO_STQ(sve_st1qq_be, sme_st1q_be) +DO_STQ(sve_st1qq_le, sme_st1q_le) #undef DO_LD #undef DO_ST diff --git a/target/arm/tcg/sve_ldst_internal.h b/target/arm/tcg/sve_ldst_internal.h index e87beba435..c67cda9d3b 100644 --- a/target/arm/tcg/sve_ldst_internal.h +++ b/target/arm/tcg/sve_ldst_internal.h @@ -141,6 +141,69 @@ DO_LD_PRIM_3(ld1dqu_le, ld1dd_le) #define sve_st1dq_be_tlb sve_st1dd_be_tlb #define sve_st1dq_le_tlb sve_st1dd_le_tlb +/* + * The ARMVectorReg elements are stored in host-endian 64-bit units. + * For 128-bit quantities, the sequence defined by the Elem[] pseudocode + * corresponds to storing the two 64-bit pieces in little-endian order. + */ +/* FIXME: Nothing in this file makes any effort at atomicity. */ + +static inline void sve_ld1qq_be_host(void *vd, intptr_t reg_off, void *host) +{ + sve_ld1dd_be_host(vd, reg_off + 8, host); + sve_ld1dd_be_host(vd, reg_off, host + 8); +} + +static inline void sve_ld1qq_le_host(void *vd, intptr_t reg_off, void *host) +{ + sve_ld1dd_le_host(vd, reg_off, host); + sve_ld1dd_le_host(vd, reg_off + 8, host + 8); +} + +static inline void +sve_ld1qq_be_tlb(CPUARMState *env, void *vd, intptr_t reg_off, + target_ulong addr, uintptr_t ra) +{ + sve_ld1dd_be_tlb(env, vd, reg_off + 8, addr, ra); + sve_ld1dd_be_tlb(env, vd, reg_off, addr + 8, ra); +} + +static inline void +sve_ld1qq_le_tlb(CPUARMState *env, void *vd, intptr_t reg_off, + target_ulong addr, uintptr_t ra) +{ + sve_ld1dd_le_tlb(env, vd, reg_off, addr, ra); + sve_ld1dd_le_tlb(env, vd, reg_off + 8, addr + 8, ra); +} + +static inline void sve_st1qq_be_host(void *vd, intptr_t reg_off, void *host) +{ + sve_st1dd_be_host(vd, reg_off + 8, host); + sve_st1dd_be_host(vd, reg_off, host + 8); +} + +static inline void sve_st1qq_le_host(void *vd, intptr_t reg_off, void *host) +{ + sve_st1dd_le_host(vd, reg_off, host); + sve_st1dd_le_host(vd, reg_off + 8, host + 8); +} + +static inline void +sve_st1qq_be_tlb(CPUARMState *env, void *vd, intptr_t reg_off, + target_ulong addr, uintptr_t ra) +{ + sve_st1dd_be_tlb(env, vd, reg_off + 8, addr, ra); + sve_st1dd_be_tlb(env, vd, reg_off, addr + 8, ra); +} + +static inline void +sve_st1qq_le_tlb(CPUARMState *env, void *vd, intptr_t reg_off, + target_ulong addr, uintptr_t ra) +{ + sve_st1dd_le_tlb(env, vd, reg_off, addr, ra); + sve_st1dd_le_tlb(env, vd, reg_off + 8, addr + 8, ra); +} + #undef DO_LD_TLB #undef DO_ST_TLB #undef DO_LD_HOST From 0e4ba0607b74c82525dcfc6ed1c4b89bb4cbb39b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:21:00 -0600 Subject: [PATCH 2044/2760] target/arm: Implement {LD, ST}[234]Q for SME2p1/SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-98-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 32 +++++++++ target/arm/tcg/sve.decode | 31 +++++++++ target/arm/tcg/sve_helper.c | 8 +++ target/arm/tcg/translate-sve.c | 116 ++++++++++++++++++++++++--------- 4 files changed, 156 insertions(+), 31 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 1999c4bb1f..ade76ff664 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1658,6 +1658,14 @@ DEF_HELPER_FLAGS_4(sve_ld2dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld3dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld4dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld2qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + DEF_HELPER_FLAGS_4(sve_ld1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) @@ -1722,6 +1730,14 @@ DEF_HELPER_FLAGS_4(sve_ld2dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld3dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld4dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld2qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_ld2qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld3qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld4qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + DEF_HELPER_FLAGS_4(sve_ld1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_ld1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) @@ -1946,6 +1962,14 @@ DEF_HELPER_FLAGS_4(sve_st2dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st3dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st4dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st2qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + DEF_HELPER_FLAGS_4(sve_st1bh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1bs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1bd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) @@ -1998,6 +2022,14 @@ DEF_HELPER_FLAGS_4(sve_st2dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st3dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st4dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st2qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + +DEF_HELPER_FLAGS_4(sve_st2qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st3qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_st4qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + DEF_HELPER_FLAGS_4(sve_st1bh_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1bs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve_st1bd_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index bf33bc305f..3eda029146 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -229,6 +229,9 @@ @rprr_load_dt ....... dtype:4 rm:5 ... pg:3 rn:5 rd:5 &rprr_load @rpri_load_dt ....... dtype:4 . imm:s4 ... pg:3 rn:5 rd:5 &rpri_load +@rprr_load ....... .... rm:5 ... pg:3 rn:5 rd:5 &rprr_load +@rpri_load ....... .... . imm:s4 ... pg:3 rn:5 rd:5 &rpri_load + @rprr_load_msz ....... .... rm:5 ... pg:3 rn:5 rd:5 \ &rprr_load dtype=%msz_dtype @rpri_load_msz ....... .... . imm:s4 ... pg:3 rn:5 rd:5 \ @@ -1267,12 +1270,26 @@ LDNF1_zpri 1010010 .... 1.... 101 ... ..... ..... @rpri_load_dt nreg=0 # SVE load multiple structures (scalar plus scalar) # LD2B, LD2H, LD2W, LD2D; etc. LD_zprr 1010010 .. nreg:2 ..... 110 ... ..... ..... @rprr_load_msz +# LD[234]Q +LD_zprr 1010010 01 01 ..... 100 ... ..... ..... \ + @rprr_load dtype=18 nreg=1 +LD_zprr 1010010 10 01 ..... 100 ... ..... ..... \ + @rprr_load dtype=18 nreg=2 +LD_zprr 1010010 11 01 ..... 100 ... ..... ..... \ + @rprr_load dtype=18 nreg=3 # SVE contiguous non-temporal load (scalar plus immediate) # LDNT1B, LDNT1H, LDNT1W, LDNT1D # SVE load multiple structures (scalar plus immediate) # LD2B, LD2H, LD2W, LD2D; etc. LD_zpri 1010010 .. nreg:2 0.... 111 ... ..... ..... @rpri_load_msz +# LD[234]Q +LD_zpri 1010010 01 001 .... 111 ... ..... ..... \ + @rpri_load dtype=18 nreg=1 +LD_zpri 1010010 10 001 .... 111 ... ..... ..... \ + @rpri_load dtype=18 nreg=2 +LD_zpri 1010010 11 001 .... 111 ... ..... ..... \ + @rpri_load dtype=18 nreg=3 # SVE load and broadcast quadword (scalar plus scalar) LD1RQ_zprr 1010010 .. 00 ..... 000 ... ..... ..... \ @@ -1383,11 +1400,25 @@ ST_zprr 1110010 11 10 ..... 010 ... ..... ..... \ # SVE store multiple structures (scalar plus immediate) (nreg != 0) ST_zpri 1110010 .. nreg:2 1.... 111 ... ..... ..... \ @rpri_store msz=%size_23 esz=%size_23 +# ST[234]Q +ST_zpri 11100100 01 00 .... 000 ... ..... ..... \ + @rpri_store msz=4 esz=4 nreg=1 +ST_zpri 11100100 10 00 .... 000 ... ..... ..... \ + @rpri_store msz=4 esz=4 nreg=2 +ST_zpri 11100100 11 00 .... 000 ... ..... ..... \ + @rpri_store msz=4 esz=4 nreg=3 # SVE contiguous non-temporal store (scalar plus scalar) (nreg == 0) # SVE store multiple structures (scalar plus scalar) (nreg != 0) ST_zprr 1110010 .. nreg:2 ..... 011 ... ..... ..... \ @rprr_store msz=%size_23 esz=%size_23 +# ST[234]Q +ST_zprr 11100100 01 1 ..... 000 ... ..... ..... \ + @rprr_store msz=4 esz=4 nreg=1 +ST_zprr 11100100 10 1 ..... 000 ... ..... ..... \ + @rprr_store msz=4 esz=4 nreg=2 +ST_zprr 11100100 11 1 ..... 000 ... ..... ..... \ + @rprr_store msz=4 esz=4 nreg=3 # SVE 32-bit scatter store (scalar plus 32-bit scaled offsets) # Require msz > 0 && msz <= esz. diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 91cd5970ee..360114ece2 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -6421,6 +6421,10 @@ DO_LDN_2(2, dd, MO_64) DO_LDN_2(3, dd, MO_64) DO_LDN_2(4, dd, MO_64) +DO_LDN_2(2, qq, MO_128) +DO_LDN_2(3, qq, MO_128) +DO_LDN_2(4, qq, MO_128) + #undef DO_LDN_1 #undef DO_LDN_2 @@ -6987,6 +6991,10 @@ DO_STN_2(4, dd, MO_64, MO_64) DO_STN_2(1, sq, MO_128, MO_32) DO_STN_2(1, dq, MO_128, MO_64) +DO_STN_2(2, qq, MO_128, MO_128) +DO_STN_2(3, qq, MO_128, MO_128) +DO_STN_2(4, qq, MO_128, MO_128) + #undef DO_STN_1 #undef DO_STN_2 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 8e945c5d2d..7dff028569 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -89,7 +89,7 @@ static inline int expand_imm_sh8u(DisasContext *s, int x) */ static inline int msz_dtype(DisasContext *s, int msz) { - static const uint8_t dtype[4] = { 0, 5, 10, 15 }; + static const uint8_t dtype[5] = { 0, 5, 10, 15, 18 }; return dtype[msz]; } @@ -4886,7 +4886,7 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, } /* Indexed by [mte][be][dtype][nreg] */ -static gen_helper_gvec_mem * const ldr_fns[2][2][18][4] = { +static gen_helper_gvec_mem * const ldr_fns[2][2][19][4] = { { /* mte inactive, little-endian */ { { gen_helper_sve_ld1bb_r, gen_helper_sve_ld2bb_r, gen_helper_sve_ld3bb_r, gen_helper_sve_ld4bb_r }, @@ -4914,6 +4914,8 @@ static gen_helper_gvec_mem * const ldr_fns[2][2][18][4] = { { gen_helper_sve_ld1squ_le_r, NULL, NULL, NULL }, { gen_helper_sve_ld1dqu_le_r, NULL, NULL, NULL }, + { NULL, gen_helper_sve_ld2qq_le_r, + gen_helper_sve_ld3qq_le_r, gen_helper_sve_ld4qq_le_r }, }, /* mte inactive, big-endian */ @@ -4943,6 +4945,8 @@ static gen_helper_gvec_mem * const ldr_fns[2][2][18][4] = { { gen_helper_sve_ld1squ_be_r, NULL, NULL, NULL }, { gen_helper_sve_ld1dqu_be_r, NULL, NULL, NULL }, + { NULL, gen_helper_sve_ld2qq_be_r, + gen_helper_sve_ld3qq_be_r, gen_helper_sve_ld4qq_be_r }, }, }, @@ -4981,6 +4985,10 @@ static gen_helper_gvec_mem * const ldr_fns[2][2][18][4] = { { gen_helper_sve_ld1squ_le_r_mte, NULL, NULL, NULL }, { gen_helper_sve_ld1dqu_le_r_mte, NULL, NULL, NULL }, + { NULL, + gen_helper_sve_ld2qq_le_r_mte, + gen_helper_sve_ld3qq_le_r_mte, + gen_helper_sve_ld4qq_le_r_mte }, }, /* mte active, big-endian */ @@ -5018,6 +5026,10 @@ static gen_helper_gvec_mem * const ldr_fns[2][2][18][4] = { { gen_helper_sve_ld1squ_be_r_mte, NULL, NULL, NULL }, { gen_helper_sve_ld1dqu_be_r_mte, NULL, NULL, NULL }, + { NULL, + gen_helper_sve_ld2qq_be_r_mte, + gen_helper_sve_ld3qq_be_r_mte, + gen_helper_sve_ld4qq_be_r_mte }, }, }, }; @@ -5042,16 +5054,26 @@ static bool trans_LD_zprr(DisasContext *s, arg_rprr_load *a) return false; } - /* dtypes 16 and 17 are artificial, representing 128-bit element */ - if (a->dtype < 16) { + /* dtypes 16-18 are artificial, representing 128-bit element */ + switch (a->dtype) { + case 0 ... 15: if (!dc_isar_feature(aa64_sve, s)) { return false; } - } else { + break; + case 16: case 17: if (!dc_isar_feature(aa64_sve2p1, s)) { return false; } s->is_nonstreaming = true; + break; + case 18: + if (!dc_isar_feature(aa64_sme2p1_or_sve2p1, s)) { + return false; + } + break; + default: + g_assert_not_reached(); } if (sve_access_check(s)) { @@ -5065,16 +5087,26 @@ static bool trans_LD_zprr(DisasContext *s, arg_rprr_load *a) static bool trans_LD_zpri(DisasContext *s, arg_rpri_load *a) { - /* dtypes 16 and 17 are artificial, representing 128-bit element */ - if (a->dtype < 16) { + /* dtypes 16-18 are artificial, representing 128-bit element */ + switch (a->dtype) { + case 0 ... 15: if (!dc_isar_feature(aa64_sve, s)) { return false; } - } else { + break; + case 16: case 17: if (!dc_isar_feature(aa64_sve2p1, s)) { return false; } s->is_nonstreaming = true; + break; + case 18: + if (!dc_isar_feature(aa64_sme2p1_or_sve2p1, s)) { + return false; + } + break; + default: + g_assert_not_reached(); } if (sve_access_check(s)) { @@ -5586,55 +5618,67 @@ static void do_st_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, gen_helper_sve_st1dd_be_r_mte, gen_helper_sve_st1dq_be_r_mte } } }, }; - static gen_helper_gvec_mem * const fn_multiple[2][2][3][4] = { + static gen_helper_gvec_mem * const fn_multiple[2][2][3][5] = { { { { gen_helper_sve_st2bb_r, gen_helper_sve_st2hh_le_r, gen_helper_sve_st2ss_le_r, - gen_helper_sve_st2dd_le_r }, + gen_helper_sve_st2dd_le_r, + gen_helper_sve_st2qq_le_r }, { gen_helper_sve_st3bb_r, gen_helper_sve_st3hh_le_r, gen_helper_sve_st3ss_le_r, - gen_helper_sve_st3dd_le_r }, + gen_helper_sve_st3dd_le_r, + gen_helper_sve_st3qq_le_r }, { gen_helper_sve_st4bb_r, gen_helper_sve_st4hh_le_r, gen_helper_sve_st4ss_le_r, - gen_helper_sve_st4dd_le_r } }, + gen_helper_sve_st4dd_le_r, + gen_helper_sve_st4qq_le_r } }, { { gen_helper_sve_st2bb_r, gen_helper_sve_st2hh_be_r, gen_helper_sve_st2ss_be_r, - gen_helper_sve_st2dd_be_r }, + gen_helper_sve_st2dd_be_r, + gen_helper_sve_st2qq_be_r }, { gen_helper_sve_st3bb_r, gen_helper_sve_st3hh_be_r, gen_helper_sve_st3ss_be_r, - gen_helper_sve_st3dd_be_r }, + gen_helper_sve_st3dd_be_r, + gen_helper_sve_st3qq_be_r }, { gen_helper_sve_st4bb_r, gen_helper_sve_st4hh_be_r, gen_helper_sve_st4ss_be_r, - gen_helper_sve_st4dd_be_r } } }, + gen_helper_sve_st4dd_be_r, + gen_helper_sve_st4qq_be_r } } }, { { { gen_helper_sve_st2bb_r_mte, gen_helper_sve_st2hh_le_r_mte, gen_helper_sve_st2ss_le_r_mte, - gen_helper_sve_st2dd_le_r_mte }, + gen_helper_sve_st2dd_le_r_mte, + gen_helper_sve_st2qq_le_r_mte }, { gen_helper_sve_st3bb_r_mte, gen_helper_sve_st3hh_le_r_mte, gen_helper_sve_st3ss_le_r_mte, - gen_helper_sve_st3dd_le_r_mte }, + gen_helper_sve_st3dd_le_r_mte, + gen_helper_sve_st3qq_le_r_mte }, { gen_helper_sve_st4bb_r_mte, gen_helper_sve_st4hh_le_r_mte, gen_helper_sve_st4ss_le_r_mte, - gen_helper_sve_st4dd_le_r_mte } }, + gen_helper_sve_st4dd_le_r_mte, + gen_helper_sve_st4qq_le_r_mte } }, { { gen_helper_sve_st2bb_r_mte, gen_helper_sve_st2hh_be_r_mte, gen_helper_sve_st2ss_be_r_mte, - gen_helper_sve_st2dd_be_r_mte }, + gen_helper_sve_st2dd_be_r_mte, + gen_helper_sve_st2qq_be_r_mte }, { gen_helper_sve_st3bb_r_mte, gen_helper_sve_st3hh_be_r_mte, gen_helper_sve_st3ss_be_r_mte, - gen_helper_sve_st3dd_be_r_mte }, + gen_helper_sve_st3dd_be_r_mte, + gen_helper_sve_st3qq_be_r_mte }, { gen_helper_sve_st4bb_r_mte, gen_helper_sve_st4hh_be_r_mte, gen_helper_sve_st4ss_be_r_mte, - gen_helper_sve_st4dd_be_r_mte } } }, + gen_helper_sve_st4dd_be_r_mte, + gen_helper_sve_st4qq_be_r_mte } } }, }; gen_helper_gvec_mem *fn; int be = s->be_data == MO_BE; @@ -5663,12 +5707,17 @@ static bool trans_ST_zprr(DisasContext *s, arg_rprr_store *a) } break; case MO_128: - assert(a->msz < a->esz); - assert(a->nreg == 0); - if (!dc_isar_feature(aa64_sve2p1, s)) { - return false; + if (a->nreg == 0) { + assert(a->msz < a->esz); + if (!dc_isar_feature(aa64_sve2p1, s)) { + return false; + } + s->is_nonstreaming = true; + } else { + if (!dc_isar_feature(aa64_sme2p1_or_sve2p1, s)) { + return false; + } } - s->is_nonstreaming = true; break; default: g_assert_not_reached(); @@ -5695,12 +5744,17 @@ static bool trans_ST_zpri(DisasContext *s, arg_rpri_store *a) } break; case MO_128: - assert(a->msz < a->esz); - assert(a->nreg == 0); - if (!dc_isar_feature(aa64_sve2p1, s)) { - return false; + if (a->nreg == 0) { + assert(a->msz < a->esz); + if (!dc_isar_feature(aa64_sve2p1, s)) { + return false; + } + s->is_nonstreaming = true; + } else { + if (!dc_isar_feature(aa64_sme2p1_or_sve2p1, s)) { + return false; + } } - s->is_nonstreaming = true; break; default: g_assert_not_reached(); From d2aa9a804ee678f0327bc5c6018a814caa424b77 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:21:01 -0600 Subject: [PATCH 2045/2760] target/arm: Implement LD1Q, ST1Q for SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-99-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sve.h | 16 ++++++++++++++++ target/arm/tcg/sve.decode | 8 ++++++++ target/arm/tcg/sve_helper.c | 6 ++++++ target/arm/tcg/translate-sve.c | 34 ++++++++++++++++++++++++++++++++-- 4 files changed, 62 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index ade76ff664..c36090d13d 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -2155,6 +2155,10 @@ DEF_HELPER_FLAGS_6(sve_ldsds_le_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldsds_be_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldqq_le_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldqq_be_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldbsu_zsu_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) @@ -2264,6 +2268,10 @@ DEF_HELPER_FLAGS_6(sve_ldsds_le_zd_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldsds_be_zd_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldqq_le_zd_mte, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_ldqq_be_zd_mte, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_ldffbsu_zsu, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) @@ -2549,6 +2557,10 @@ DEF_HELPER_FLAGS_6(sve_stdd_le_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_stdd_be_zd, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stqq_le_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stqq_be_zd, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_stbs_zsu_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) @@ -2616,6 +2628,10 @@ DEF_HELPER_FLAGS_6(sve_stdd_le_zd_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_6(sve_stdd_be_zd_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stqq_le_zd_mte, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_6(sve_stqq_be_zd_mte, TCG_CALL_NO_WG, + void, env, ptr, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_4(sve2_sqdmull_zzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 3eda029146..2efd5f57e4 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1340,6 +1340,10 @@ LD1_zprz 1100010 10 1. ..... 1.. ... ..... ..... \ LD1_zprz 1100010 11 1. ..... 11. ... ..... ..... \ @rprr_g_load_sc esz=3 msz=3 u=1 +# LD1Q +LD1_zprz 1100 0100 000 rm:5 101 pg:3 rn:5 rd:5 \ + &rprr_gather_load u=0 ff=0 xs=2 esz=4 msz=4 scale=0 + # SVE 64-bit gather load (vector plus immediate) LD1_zpiz 1100010 .. 01 ..... 1.. ... ..... ..... \ @rpri_g_load esz=3 @@ -1443,6 +1447,10 @@ ST1_zprz 1110010 .. 01 ..... 101 ... ..... ..... \ ST1_zprz 1110010 .. 00 ..... 101 ... ..... ..... \ @rprr_scatter_store xs=2 esz=3 scale=0 +# ST1Q +ST1_zprz 1110 0100 001 rm:5 001 pg:3 rn:5 rd:5 \ + &rprr_scatter_store xs=2 msz=4 esz=4 scale=0 + # SVE 64-bit scatter store (vector plus immediate) ST1_zpiz 1110010 .. 10 ..... 101 ... ..... ..... \ @rpri_scatter_store esz=3 diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 360114ece2..43b872c7fd 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -7211,6 +7211,9 @@ DO_LD1_ZPZ_D(dd_be, zsu, MO_64) DO_LD1_ZPZ_D(dd_be, zss, MO_64) DO_LD1_ZPZ_D(dd_be, zd, MO_64) +DO_LD1_ZPZ_D(qq_le, zd, MO_128) +DO_LD1_ZPZ_D(qq_be, zd, MO_128) + #undef DO_LD1_ZPZ_S #undef DO_LD1_ZPZ_D @@ -7597,6 +7600,9 @@ DO_ST1_ZPZ_D(sd_be, zd, MO_32) DO_ST1_ZPZ_D(dd_le, zd, MO_64) DO_ST1_ZPZ_D(dd_be, zd, MO_64) +DO_ST1_ZPZ_D(qq_le, zd, MO_128) +DO_ST1_ZPZ_D(qq_be, zd, MO_128) + #undef DO_ST1_ZPZ_S #undef DO_ST1_ZPZ_D diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 7dff028569..7b575734fd 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -6121,13 +6121,23 @@ gather_load_fn64[2][2][2][3][2][4] = { gen_helper_sve_ldffdd_be_zd_mte, } } } } }, }; +static gen_helper_gvec_mem_scatter * const +gather_load_fn128[2][2] = { + { gen_helper_sve_ldqq_le_zd, + gen_helper_sve_ldqq_be_zd }, + { gen_helper_sve_ldqq_le_zd_mte, + gen_helper_sve_ldqq_be_zd_mte } +}; + static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a) { gen_helper_gvec_mem_scatter *fn = NULL; bool be = s->be_data == MO_BE; bool mte = s->mte_active[0]; - if (!dc_isar_feature(aa64_sve, s)) { + if (a->esz < MO_128 + ? !dc_isar_feature(aa64_sve, s) + : !dc_isar_feature(aa64_sve2p1, s)) { return false; } s->is_nonstreaming = true; @@ -6142,6 +6152,12 @@ static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a) case MO_64: fn = gather_load_fn64[mte][be][a->ff][a->xs][a->u][a->msz]; break; + case MO_128: + assert(!a->ff && a->u && a->xs == 2 && a->msz == MO_128); + fn = gather_load_fn128[mte][be]; + break; + default: + g_assert_not_reached(); } assert(fn != NULL); @@ -6309,6 +6325,14 @@ static gen_helper_gvec_mem_scatter * const scatter_store_fn64[2][2][3][4] = { gen_helper_sve_stdd_be_zd_mte, } } }, }; +static gen_helper_gvec_mem_scatter * const +scatter_store_fn128[2][2] = { + { gen_helper_sve_stqq_le_zd, + gen_helper_sve_stqq_be_zd }, + { gen_helper_sve_stqq_le_zd_mte, + gen_helper_sve_stqq_be_zd_mte } +}; + static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a) { gen_helper_gvec_mem_scatter *fn; @@ -6318,7 +6342,9 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a) if (a->esz < a->msz || (a->msz == 0 && a->scale)) { return false; } - if (!dc_isar_feature(aa64_sve, s)) { + if (a->esz < MO_128 + ? !dc_isar_feature(aa64_sve, s) + : !dc_isar_feature(aa64_sve2p1, s)) { return false; } s->is_nonstreaming = true; @@ -6332,6 +6358,10 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a) case MO_64: fn = scatter_store_fn64[mte][be][a->xs][a->msz]; break; + case MO_128: + assert(a->xs == 2 && a->msz == MO_128); + fn = scatter_store_fn128[mte][be]; + break; default: g_assert_not_reached(); } From ec2d9709653f5c0c5503f177e5cdf6ba3e8f2627 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:21:02 -0600 Subject: [PATCH 2046/2760] target/arm: Implement MOVAZ for SME2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-100-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 6 ++++ target/arm/tcg/sme.decode | 36 ++++++++++++++++++++ target/arm/tcg/sme_helper.c | 60 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 46 +++++++++++++++++++------- 4 files changed, 137 insertions(+), 11 deletions(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 467073ea25..c9961d6b7f 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -42,6 +42,12 @@ DEF_HELPER_FLAGS_3(sme2_mova_zc_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_mova_cz_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2_mova_zc_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2p1_movaz_zc_b, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2p1_movaz_zc_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2p1_movaz_zc_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2p1_movaz_zc_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) +DEF_HELPER_FLAGS_3(sme2p1_movaz_zc_q, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + DEF_HELPER_FLAGS_5(sme_ld1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_5(sme_ld1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) DEF_HELPER_FLAGS_5(sme_ld1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 0a2ceea8be..f7e4143b7d 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -100,6 +100,42 @@ MOVA_za2 11000000 00 00011 00 .. 010 00 off:3 zr:4 0 \ MOVA_za4 11000000 00 00011 00 .. 011 00 off:3 zr:3 00 \ &mova_a rv=%mova_rv +### SME Move and Zero + +MOVAZ_za2 11000000 00000110 0 .. 01010 off:3 zr:4 0 \ + &mova_a rv=%mova_rv +MOVAZ_za4 11000000 00000110 0 .. 01110 off:3 zr:3 00 \ + &mova_a rv=%mova_rv + +MOVAZ_zt 11000000 00 00001 0 v:1 .. 0001 off:4 zr:5 \ + &mova_t rs=%mova_rs esz=0 za=0 +MOVAZ_zt 11000000 01 00001 0 v:1 .. 0001 za:1 off:3 zr:5 \ + &mova_t rs=%mova_rs esz=1 +MOVAZ_zt 11000000 10 00001 0 v:1 .. 0001 za:2 off:2 zr:5 \ + &mova_t rs=%mova_rs esz=2 +MOVAZ_zt 11000000 11 00001 0 v:1 .. 0001 za:3 off:1 zr:5 \ + &mova_t rs=%mova_rs esz=3 +MOVAZ_zt 11000000 11 00001 1 v:1 .. 0001 za:4 zr:5 \ + &mova_t rs=%mova_rs esz=4 off=0 + +MOVAZ_zt2 11000000 00 00011 0 v:1 .. 00010 off:3 zr:4 0 \ + &mova_t rs=%mova_rs esz=0 za=0 +MOVAZ_zt2 11000000 01 00011 0 v:1 .. 00010 za:1 off:2 zr:4 0 \ + &mova_t rs=%mova_rs esz=1 +MOVAZ_zt2 11000000 10 00011 0 v:1 .. 00010 za:2 off:1 zr:4 0 \ + &mova_t rs=%mova_rs esz=2 +MOVAZ_zt2 11000000 11 00011 0 v:1 .. 00010 za:3 zr:4 0 \ + &mova_t rs=%mova_rs esz=3 off=0 + +MOVAZ_zt4 11000000 00 00011 0 v:1 .. 001100 off:2 zr:3 00 \ + &mova_t rs=%mova_rs esz=0 za=0 +MOVAZ_zt4 11000000 01 00011 0 v:1 .. 001100 za:1 off:1 zr:3 00 \ + &mova_t rs=%mova_rs esz=1 +MOVAZ_zt4 11000000 10 00011 0 v:1 .. 001100 za:2 zr:3 00 \ + &mova_t rs=%mova_rs esz=2 off=0 +MOVAZ_zt4 11000000 11 00011 0 v:1 .. 00110 za:3 zr:3 00 \ + &mova_t rs=%mova_rs esz=3 off=0 + ### SME Move into/from ZT0 MOVT_rzt 1100 0000 0100 1100 0 off:3 00 11111 rt:5 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index df16bb2f9c..424fc006be 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -257,6 +257,66 @@ void HELPER(sme2_mova_zc_d)(void *vdst, void *vsrc, uint32_t desc) } } +void HELPER(sme2p1_movaz_zc_b)(void *vdst, void *vsrc, uint32_t desc) +{ + uint8_t *src = vsrc; + uint8_t *dst = vdst; + size_t i, n = simd_oprsz(desc); + + for (i = 0; i < n; ++i) { + dst[i] = src[tile_vslice_index(i)]; + src[tile_vslice_index(i)] = 0; + } +} + +void HELPER(sme2p1_movaz_zc_h)(void *vdst, void *vsrc, uint32_t desc) +{ + uint16_t *src = vsrc; + uint16_t *dst = vdst; + size_t i, n = simd_oprsz(desc) / 2; + + for (i = 0; i < n; ++i) { + dst[i] = src[tile_vslice_index(i)]; + src[tile_vslice_index(i)] = 0; + } +} + +void HELPER(sme2p1_movaz_zc_s)(void *vdst, void *vsrc, uint32_t desc) +{ + uint32_t *src = vsrc; + uint32_t *dst = vdst; + size_t i, n = simd_oprsz(desc) / 4; + + for (i = 0; i < n; ++i) { + dst[i] = src[tile_vslice_index(i)]; + src[tile_vslice_index(i)] = 0; + } +} + +void HELPER(sme2p1_movaz_zc_d)(void *vdst, void *vsrc, uint32_t desc) +{ + uint64_t *src = vsrc; + uint64_t *dst = vdst; + size_t i, n = simd_oprsz(desc) / 8; + + for (i = 0; i < n; ++i) { + dst[i] = src[tile_vslice_index(i)]; + src[tile_vslice_index(i)] = 0; + } +} + +void HELPER(sme2p1_movaz_zc_q)(void *vdst, void *vsrc, uint32_t desc) +{ + Int128 *src = vsrc; + Int128 *dst = vdst; + size_t i, n = simd_oprsz(desc) / 16; + + for (i = 0; i < n; ++i) { + dst[i] = src[tile_vslice_index(i)]; + memset(&src[tile_vslice_index(i)], 0, 16); + } +} + /* * Clear elements in a tile slice comprising len bytes. */ diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 7407597177..9592f6a996 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -247,7 +247,8 @@ static bool do_mova_tile(DisasContext *s, arg_mova_p *a, bool to_vec) TRANS_FEAT(MOVA_tz, aa64_sme, do_mova_tile, a, false) TRANS_FEAT(MOVA_zt, aa64_sme, do_mova_tile, a, true) -static bool do_mova_tile_n(DisasContext *s, arg_mova_t *a, int n, bool to_vec) +static bool do_mova_tile_n(DisasContext *s, arg_mova_t *a, int n, + bool to_vec, bool zero) { static gen_helper_gvec_2 * const cz_fns[] = { gen_helper_sme2_mova_cz_b, gen_helper_sme2_mova_cz_h, @@ -257,6 +258,11 @@ static bool do_mova_tile_n(DisasContext *s, arg_mova_t *a, int n, bool to_vec) gen_helper_sme2_mova_zc_b, gen_helper_sme2_mova_zc_h, gen_helper_sme2_mova_zc_s, gen_helper_sme2_mova_zc_d, }; + static gen_helper_gvec_2 * const zc_z_fns[] = { + gen_helper_sme2p1_movaz_zc_b, gen_helper_sme2p1_movaz_zc_h, + gen_helper_sme2p1_movaz_zc_s, gen_helper_sme2p1_movaz_zc_d, + gen_helper_sme2p1_movaz_zc_q, + }; TCGv_ptr t_za; int svl, bytes_per_op = n << a->esz; @@ -270,6 +276,8 @@ static bool do_mova_tile_n(DisasContext *s, arg_mova_t *a, int n, bool to_vec) return true; } + assert(a->esz <= MO_64 + zero); + if (!sme_smza_enabled_check(s)) { return true; } @@ -292,7 +300,9 @@ static bool do_mova_tile_n(DisasContext *s, arg_mova_t *a, int n, bool to_vec) TCGv_ptr t_zr = vec_full_reg_ptr(s, a->zr * n + i); t_za = get_tile_rowcol(s, a->esz, a->rs, a->za, a->off * n + i, 1, n, a->v); - if (to_vec) { + if (zero) { + zc_z_fns[a->esz](t_zr, t_za, t_desc); + } else if (to_vec) { zc_fns[a->esz](t_zr, t_za, t_desc); } else { cz_fns[a->esz](t_za, t_zr, t_desc); @@ -305,6 +315,9 @@ static bool do_mova_tile_n(DisasContext *s, arg_mova_t *a, int n, bool to_vec) a->off * n + i, 1, n, a->v); if (to_vec) { tcg_gen_gvec_mov_var(MO_8, tcg_env, o_zr, t_za, 0, svl, svl); + if (zero) { + tcg_gen_gvec_dup_imm_var(MO_8, t_za, 0, svl, svl, 0); + } } else { tcg_gen_gvec_mov_var(MO_8, t_za, 0, tcg_env, o_zr, svl, svl); } @@ -313,12 +326,17 @@ static bool do_mova_tile_n(DisasContext *s, arg_mova_t *a, int n, bool to_vec) return true; } -TRANS_FEAT(MOVA_tz2, aa64_sme2, do_mova_tile_n, a, 2, false) -TRANS_FEAT(MOVA_tz4, aa64_sme2, do_mova_tile_n, a, 4, false) -TRANS_FEAT(MOVA_zt2, aa64_sme2, do_mova_tile_n, a, 2, true) -TRANS_FEAT(MOVA_zt4, aa64_sme2, do_mova_tile_n, a, 4, true) +TRANS_FEAT(MOVA_tz2, aa64_sme2, do_mova_tile_n, a, 2, false, false) +TRANS_FEAT(MOVA_tz4, aa64_sme2, do_mova_tile_n, a, 4, false, false) +TRANS_FEAT(MOVA_zt2, aa64_sme2, do_mova_tile_n, a, 2, true, false) +TRANS_FEAT(MOVA_zt4, aa64_sme2, do_mova_tile_n, a, 4, true, false) -static bool do_mova_array_n(DisasContext *s, arg_mova_a *a, int n, bool to_vec) +TRANS_FEAT(MOVAZ_zt, aa64_sme2p1, do_mova_tile_n, a, 1, true, true) +TRANS_FEAT(MOVAZ_zt2, aa64_sme2p1, do_mova_tile_n, a, 2, true, true) +TRANS_FEAT(MOVAZ_zt4, aa64_sme2p1, do_mova_tile_n, a, 4, true, true) + +static bool do_mova_array_n(DisasContext *s, arg_mova_a *a, int n, + bool to_vec, bool zero) { TCGv_ptr t_za; int svl; @@ -336,6 +354,9 @@ static bool do_mova_array_n(DisasContext *s, arg_mova_a *a, int n, bool to_vec) if (to_vec) { tcg_gen_gvec_mov_var(MO_8, tcg_env, o_zr, t_za, o_za, svl, svl); + if (zero) { + tcg_gen_gvec_dup_imm_var(MO_8, t_za, o_za, svl, svl, 0); + } } else { tcg_gen_gvec_mov_var(MO_8, t_za, o_za, tcg_env, o_zr, svl, svl); } @@ -343,10 +364,13 @@ static bool do_mova_array_n(DisasContext *s, arg_mova_a *a, int n, bool to_vec) return true; } -TRANS_FEAT(MOVA_az2, aa64_sme2, do_mova_array_n, a, 2, false) -TRANS_FEAT(MOVA_az4, aa64_sme2, do_mova_array_n, a, 4, false) -TRANS_FEAT(MOVA_za2, aa64_sme2, do_mova_array_n, a, 2, true) -TRANS_FEAT(MOVA_za4, aa64_sme2, do_mova_array_n, a, 4, true) +TRANS_FEAT(MOVA_az2, aa64_sme2, do_mova_array_n, a, 2, false, false) +TRANS_FEAT(MOVA_az4, aa64_sme2, do_mova_array_n, a, 4, false, false) +TRANS_FEAT(MOVA_za2, aa64_sme2, do_mova_array_n, a, 2, true, false) +TRANS_FEAT(MOVA_za4, aa64_sme2, do_mova_array_n, a, 4, true, false) + +TRANS_FEAT(MOVAZ_za2, aa64_sme2p1, do_mova_array_n, a, 2, true, true) +TRANS_FEAT(MOVAZ_za4, aa64_sme2p1, do_mova_array_n, a, 4, true, true) static bool do_movt(DisasContext *s, arg_MOVT_rzt *a, void (*func)(TCGv_i64, TCGv_ptr, tcg_target_long)) From a19b104f849a0ab6f781755f76e567960629a69c Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:21:03 -0600 Subject: [PATCH 2047/2760] target/arm: Implement LUTI2, LUTI4 for SME2/SME2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-101-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper.h | 24 ++++++++++ target/arm/tcg/sme.decode | 42 ++++++++++++++++ target/arm/tcg/translate-sme.c | 56 ++++++++++++++++++++++ target/arm/tcg/vec_helper.c | 88 ++++++++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index 392bf7b9b5..d9565c8069 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -1188,3 +1188,27 @@ DEF_HELPER_FLAGS_4(gvec_uminp_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_3(gvec_urecpe_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(gvec_ursqrte_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) + +DEF_HELPER_FLAGS_4(sme2_luti2_1b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(sme2_luti2_1h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(sme2_luti2_1s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(sme2_luti2_2b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(sme2_luti2_2h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(sme2_luti2_2s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(sme2_luti2_4b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(sme2_luti2_4h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(sme2_luti2_4s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(sme2_luti4_1b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(sme2_luti4_1h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(sme2_luti4_1s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(sme2_luti4_2b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(sme2_luti4_2h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(sme2_luti4_2s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_4(sme2_luti4_4b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(sme2_luti4_4h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_4(sme2_luti4_4s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index f7e4143b7d..5a5b8ff8a5 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -945,3 +945,45 @@ ZERO_za 11000000 000011 110 .. 0000000000 00. \ &zero_za ngrp=2 nvec=4 rv=%mova_rv off=%off1_x4 ZERO_za 11000000 000011 111 .. 0000000000 00. \ &zero_za ngrp=4 nvec=4 rv=%mova_rv off=%off1_x4 + +### SME Lookup Table Read + +&lut zd zn idx + +# LUTI2, consecutive +LUTI2_c_1b 1100 0000 1100 11 idx:4 00 00 zn:5 zd:5 &lut +LUTI2_c_1h 1100 0000 1100 11 idx:4 01 00 zn:5 zd:5 &lut +LUTI2_c_1s 1100 0000 1100 11 idx:4 10 00 zn:5 zd:5 &lut + +LUTI2_c_2b 1100 0000 1000 11 idx:3 1 00 00 zn:5 .... 0 &lut zd=%zd_ax2 +LUTI2_c_2h 1100 0000 1000 11 idx:3 1 01 00 zn:5 .... 0 &lut zd=%zd_ax2 +LUTI2_c_2s 1100 0000 1000 11 idx:3 1 10 00 zn:5 .... 0 &lut zd=%zd_ax2 + +LUTI2_c_4b 1100 0000 1000 11 idx:2 10 00 00 zn:5 ... 00 &lut zd=%zd_ax4 +LUTI2_c_4h 1100 0000 1000 11 idx:2 10 01 00 zn:5 ... 00 &lut zd=%zd_ax4 +LUTI2_c_4s 1100 0000 1000 11 idx:2 10 10 00 zn:5 ... 00 &lut zd=%zd_ax4 + +# LUTI2, strided (must check zd alignment) +LUTI2_s_2b 1100 0000 1001 11 idx:3 1 00 00 zn:5 zd:5 &lut +LUTI2_s_2h 1100 0000 1001 11 idx:3 1 01 00 zn:5 zd:5 &lut + +LUTI2_s_4b 1100 0000 1001 11 idx:2 10 00 00 zn:5 zd:5 &lut +LUTI2_s_4h 1100 0000 1001 11 idx:2 10 01 00 zn:5 zd:5 &lut + +# LUTI4, consecutive +LUTI4_c_1b 1100 0000 1100 101 idx:3 00 00 zn:5 zd:5 &lut +LUTI4_c_1h 1100 0000 1100 101 idx:3 01 00 zn:5 zd:5 &lut +LUTI4_c_1s 1100 0000 1100 101 idx:3 10 00 zn:5 zd:5 &lut + +LUTI4_c_2b 1100 0000 1000 101 idx:2 1 00 00 zn:5 .... 0 &lut zd=%zd_ax2 +LUTI4_c_2h 1100 0000 1000 101 idx:2 1 01 00 zn:5 .... 0 &lut zd=%zd_ax2 +LUTI4_c_2s 1100 0000 1000 101 idx:2 1 10 00 zn:5 .... 0 &lut zd=%zd_ax2 + +LUTI4_c_4h 1100 0000 1000 101 idx:1 10 01 00 zn:5 ... 00 &lut zd=%zd_ax4 +LUTI4_c_4s 1100 0000 1000 101 idx:1 10 10 00 zn:5 ... 00 &lut zd=%zd_ax4 + +# LUTI4, strided (must check zd alignment) +LUTI4_s_2b 1100 0000 1001 101 idx:2 1 00 00 zn:5 zd:5 &lut +LUTI4_s_2h 1100 0000 1001 101 idx:2 1 01 00 zn:5 zd:5 &lut + +LUTI4_s_4h 1100 0000 1001 101 idx:1 10 01 00 zn:5 zd:5 &lut diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 9592f6a996..d38b8a5ca2 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -1697,3 +1697,59 @@ static bool trans_SEL(DisasContext *s, arg_SEL *a) } return true; } + +static bool do_lut(DisasContext *s, arg_lut *a, + gen_helper_gvec_2_ptr *fn, bool strided) +{ + if (sme_sm_enabled_check(s) && sme2_zt0_enabled_check(s)) { + int svl = streaming_vec_reg_size(s); + tcg_gen_gvec_2_ptr(vec_full_reg_offset(s, a->zd), + vec_full_reg_offset(s, a->zn), + tcg_env, svl, svl, strided | (a->idx << 1), fn); + } + return true; +} + +TRANS_FEAT(LUTI2_c_1b, aa64_sme2, do_lut, a, gen_helper_sme2_luti2_1b, false) +TRANS_FEAT(LUTI2_c_1h, aa64_sme2, do_lut, a, gen_helper_sme2_luti2_1h, false) +TRANS_FEAT(LUTI2_c_1s, aa64_sme2, do_lut, a, gen_helper_sme2_luti2_1s, false) + +TRANS_FEAT(LUTI2_c_2b, aa64_sme2, do_lut, a, gen_helper_sme2_luti2_2b, false) +TRANS_FEAT(LUTI2_c_2h, aa64_sme2, do_lut, a, gen_helper_sme2_luti2_2h, false) +TRANS_FEAT(LUTI2_c_2s, aa64_sme2, do_lut, a, gen_helper_sme2_luti2_2s, false) + +TRANS_FEAT(LUTI2_c_4b, aa64_sme2, do_lut, a, gen_helper_sme2_luti2_4b, false) +TRANS_FEAT(LUTI2_c_4h, aa64_sme2, do_lut, a, gen_helper_sme2_luti2_4h, false) +TRANS_FEAT(LUTI2_c_4s, aa64_sme2, do_lut, a, gen_helper_sme2_luti2_4s, false) + +TRANS_FEAT(LUTI4_c_1b, aa64_sme2, do_lut, a, gen_helper_sme2_luti4_1b, false) +TRANS_FEAT(LUTI4_c_1h, aa64_sme2, do_lut, a, gen_helper_sme2_luti4_1h, false) +TRANS_FEAT(LUTI4_c_1s, aa64_sme2, do_lut, a, gen_helper_sme2_luti4_1s, false) + +TRANS_FEAT(LUTI4_c_2b, aa64_sme2, do_lut, a, gen_helper_sme2_luti4_2b, false) +TRANS_FEAT(LUTI4_c_2h, aa64_sme2, do_lut, a, gen_helper_sme2_luti4_2h, false) +TRANS_FEAT(LUTI4_c_2s, aa64_sme2, do_lut, a, gen_helper_sme2_luti4_2s, false) + +TRANS_FEAT(LUTI4_c_4h, aa64_sme2, do_lut, a, gen_helper_sme2_luti4_4h, false) +TRANS_FEAT(LUTI4_c_4s, aa64_sme2, do_lut, a, gen_helper_sme2_luti4_4s, false) + +static bool do_lut_s4(DisasContext *s, arg_lut *a, gen_helper_gvec_2_ptr *fn) +{ + return !(a->zd & 0b01100) && do_lut(s, a, fn, true); +} + +static bool do_lut_s8(DisasContext *s, arg_lut *a, gen_helper_gvec_2_ptr *fn) +{ + return !(a->zd & 0b01000) && do_lut(s, a, fn, true); +} + +TRANS_FEAT(LUTI2_s_2b, aa64_sme2p1, do_lut_s8, a, gen_helper_sme2_luti2_2b) +TRANS_FEAT(LUTI2_s_2h, aa64_sme2p1, do_lut_s8, a, gen_helper_sme2_luti2_2h) + +TRANS_FEAT(LUTI2_s_4b, aa64_sme2p1, do_lut_s4, a, gen_helper_sme2_luti2_4b) +TRANS_FEAT(LUTI2_s_4h, aa64_sme2p1, do_lut_s4, a, gen_helper_sme2_luti2_4h) + +TRANS_FEAT(LUTI4_s_2b, aa64_sme2p1, do_lut_s8, a, gen_helper_sme2_luti4_2b) +TRANS_FEAT(LUTI4_s_2h, aa64_sme2p1, do_lut_s8, a, gen_helper_sme2_luti4_2h) + +TRANS_FEAT(LUTI4_s_4h, aa64_sme2p1, do_lut_s4, a, gen_helper_sme2_luti4_4h) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index d4ee6f4d29..0603db0909 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -3443,3 +3443,91 @@ void HELPER(gvec_ursqrte_s)(void *vd, void *vn, uint32_t desc) } clear_tail(d, opr_sz, simd_maxsz(desc)); } + +static inline void do_lut_b(void *zd, uint64_t *indexes, uint64_t *table, + unsigned elements, unsigned segbase, + unsigned dstride, unsigned isize, + unsigned tsize, unsigned nreg) +{ + for (unsigned r = 0; r < nreg; ++r) { + uint8_t *dst = zd + dstride * r; + unsigned base = segbase + r * elements; + + for (unsigned e = 0; e < elements; ++e) { + unsigned index = extractn(indexes, (base + e) * isize, isize); + dst[H1(e)] = extractn(table, index * tsize, 8); + } + } +} + +static inline void do_lut_h(void *zd, uint64_t *indexes, uint64_t *table, + unsigned elements, unsigned segbase, + unsigned dstride, unsigned isize, + unsigned tsize, unsigned nreg) +{ + for (unsigned r = 0; r < nreg; ++r) { + uint16_t *dst = zd + dstride * r; + unsigned base = segbase + r * elements; + + for (unsigned e = 0; e < elements; ++e) { + unsigned index = extractn(indexes, (base + e) * isize, isize); + dst[H2(e)] = extractn(table, index * tsize, 16); + } + } +} + +static inline void do_lut_s(void *zd, uint64_t *indexes, uint32_t *table, + unsigned elements, unsigned segbase, + unsigned dstride, unsigned isize, + unsigned tsize, unsigned nreg) +{ + for (unsigned r = 0; r < nreg; ++r) { + uint32_t *dst = zd + dstride * r; + unsigned base = segbase + r * elements; + + for (unsigned e = 0; e < elements; ++e) { + unsigned index = extractn(indexes, (base + e) * isize, isize); + dst[H4(e)] = table[H4(index)]; + } + } +} + +#define DO_SME2_LUT(ISIZE, NREG, SUFF, ESIZE) \ +void helper_sme2_luti##ISIZE##_##NREG##SUFF \ + (void *zd, void *zn, CPUARMState *env, uint32_t desc) \ +{ \ + unsigned vl = simd_oprsz(desc); \ + unsigned strided = extract32(desc, SIMD_DATA_SHIFT, 1); \ + unsigned idx = extract32(desc, SIMD_DATA_SHIFT + 1, 4); \ + unsigned elements = vl / ESIZE; \ + unsigned dstride = (!strided ? 1 : NREG == 4 ? 4 : 8); \ + unsigned segments = (ESIZE * 8) / (ISIZE * NREG); \ + unsigned segment = idx & (segments - 1); \ + ARMVectorReg indexes; \ + memcpy(&indexes, zn, vl); \ + do_lut_##SUFF(zd, indexes.d, (void *)env->za_state.zt0, elements, \ + segment * NREG * elements, \ + dstride * sizeof(ARMVectorReg), ISIZE, 32, NREG); \ +} + +DO_SME2_LUT(2,1,b, 1) +DO_SME2_LUT(2,1,h, 2) +DO_SME2_LUT(2,1,s, 4) +DO_SME2_LUT(2,2,b, 1) +DO_SME2_LUT(2,2,h, 2) +DO_SME2_LUT(2,2,s, 4) +DO_SME2_LUT(2,4,b, 1) +DO_SME2_LUT(2,4,h, 2) +DO_SME2_LUT(2,4,s, 4) + +DO_SME2_LUT(4,1,b, 1) +DO_SME2_LUT(4,1,h, 2) +DO_SME2_LUT(4,1,s, 4) +DO_SME2_LUT(4,2,b, 1) +DO_SME2_LUT(4,2,h, 2) +DO_SME2_LUT(4,2,s, 4) +DO_SME2_LUT(4,4,b, 1) +DO_SME2_LUT(4,4,h, 2) +DO_SME2_LUT(4,4,s, 4) + +#undef DO_SME2_LUT From 960bf1a032d1edbff6b1f2ea716e6e8d539e3468 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Jul 2025 08:21:04 -0600 Subject: [PATCH 2048/2760] target/arm: Rename FMOPA_h to FMOPA_w_h The pattern we currently have as FMOPA_h is the "widening" insn that takes fp16 inputs and produces single-precision outputs. This is unlike FMOPA_s and FMOPA_d, which are non-widening produce outputs the same size as their inputs. SME2 introduces a non-widening fp16 FMOPA operation; rename FMOPA_h to FMOPA_w_h (for 'widening'), so we can use FMOPA_h for the non-widening version, giving it a name in line with the other non-widening ops FMOPA_s and FMOPA_d. Signed-off-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-102-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 2 +- target/arm/tcg/sme.decode | 2 +- target/arm/tcg/sme_helper.c | 4 ++-- target/arm/tcg/translate-sme.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index c9961d6b7f..67d620e456 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -135,7 +135,7 @@ DEF_HELPER_FLAGS_5(sme_addva_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sme_addha_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sme_addva_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_7(sme_fmopa_h, TCG_CALL_NO_RWG, +DEF_HELPER_FLAGS_7(sme_fmopa_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_7(sme_fmopa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 5a5b8ff8a5..77744eef4f 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -190,7 +190,7 @@ FMOPA_s 10000000 100 ..... ... ... ..... . 00 .. @op_32 FMOPA_d 10000000 110 ..... ... ... ..... . 0 ... @op_64 BFMOPA 10000001 100 ..... ... ... ..... . 00 .. @op_32 -FMOPA_h 10000001 101 ..... ... ... ..... . 00 .. @op_32 +FMOPA_w_h 10000001 101 ..... ... ... ..... . 00 .. @op_32 SMOPA_s 1010000 0 10 0 ..... ... ... ..... . 00 .. @op_32 SUMOPA_s 1010000 0 10 1 ..... ... ... ..... . 00 .. @op_32 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 424fc006be..1575a2f20c 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1146,8 +1146,8 @@ static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2, return float32_add(sum, t32, s_std); } -void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, CPUARMState *env, uint32_t desc) +void HELPER(sme_fmopa_w_h)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, CPUARMState *env, uint32_t desc) { intptr_t row, col, oprsz = simd_maxsz(desc); uint32_t neg = simd_data(desc) * 0x80008000u; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index d38b8a5ca2..1167aa4964 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -565,8 +565,8 @@ static bool do_outprod_env(DisasContext *s, arg_op *a, MemOp esz, return true; } -TRANS_FEAT(FMOPA_h, aa64_sme, do_outprod_env, a, - MO_32, gen_helper_sme_fmopa_h) +TRANS_FEAT(FMOPA_w_h, aa64_sme, do_outprod_env, a, + MO_32, gen_helper_sme_fmopa_w_h) TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, MO_32, FPST_ZA, gen_helper_sme_fmopa_s) TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, From 99548ad2475435bd73dc6dfb008475c1b670fe2a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Jul 2025 08:21:05 -0600 Subject: [PATCH 2049/2760] target/arm: Rename BFMOPA to BFMOPA_w Our current BFMOPA opcode pattern is the widening version of the insn. Rename it to BFMOPA_w, to make way for the non-widening version added in SME2. Signed-off-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-103-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 2 +- target/arm/tcg/sme.decode | 2 +- target/arm/tcg/sme_helper.c | 4 ++-- target/arm/tcg/translate-sme.c | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 67d620e456..16083660e2 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -141,7 +141,7 @@ DEF_HELPER_FLAGS_7(sme_fmopa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_fmopa_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) -DEF_HELPER_FLAGS_7(sme_bfmopa, TCG_CALL_NO_RWG, +DEF_HELPER_FLAGS_7(sme_bfmopa_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) DEF_HELPER_FLAGS_6(sme_smopa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 77744eef4f..8ad86f707e 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -189,7 +189,7 @@ ADDVA_d 11000000 11 01000 1 ... ... ..... 00 ... @adda_64 FMOPA_s 10000000 100 ..... ... ... ..... . 00 .. @op_32 FMOPA_d 10000000 110 ..... ... ... ..... . 0 ... @op_64 -BFMOPA 10000001 100 ..... ... ... ..... . 00 .. @op_32 +BFMOPA_w 10000001 100 ..... ... ... ..... . 00 .. @op_32 FMOPA_w_h 10000001 101 ..... ... ... ..... . 00 .. @op_32 SMOPA_s 1010000 0 10 0 ..... ... ... ..... . 00 .. @op_32 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 1575a2f20c..4772c97deb 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1261,8 +1261,8 @@ void HELPER(sme2_fvdot_idx_h)(void *vd, void *vn, void *vm, void *va, } } -void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, - void *vpn, void *vpm, CPUARMState *env, uint32_t desc) +void HELPER(sme_bfmopa_w)(void *vza, void *vzn, void *vzm, + void *vpn, void *vpm, CPUARMState *env, uint32_t desc) { intptr_t row, col, oprsz = simd_maxsz(desc); uint32_t neg = simd_data(desc) * 0x80008000u; diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 1167aa4964..38d0231b0a 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -572,7 +572,7 @@ TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, MO_64, FPST_ZA, gen_helper_sme_fmopa_d) -TRANS_FEAT(BFMOPA, aa64_sme, do_outprod_env, a, MO_32, gen_helper_sme_bfmopa) +TRANS_FEAT(BFMOPA_w, aa64_sme, do_outprod_env, a, MO_32, gen_helper_sme_bfmopa_w) TRANS_FEAT(SMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_smopa_s) TRANS_FEAT(UMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_umopa_s) From eba6a6f88279f00525bc9f35d758ba0f163fc9f8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:21:06 -0600 Subject: [PATCH 2050/2760] target/arm: Support FPCR.AH in SME FMOPS, BFMOPS For non-widening, we can use float_muladd_negate_product, For widening, which uses dot-product, we need to handle the negation explicitly. Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-104-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 19 +++++ target/arm/tcg/sme_helper.c | 141 +++++++++++++++++++++++++++------ target/arm/tcg/translate-sme.c | 29 ++++--- target/arm/tcg/vec_internal.h | 5 ++ 4 files changed, 161 insertions(+), 33 deletions(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 16083660e2..2b22c6aee5 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -143,6 +143,25 @@ DEF_HELPER_FLAGS_7(sme_fmopa_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_bfmopa_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_7(sme_fmops_w_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_7(sme_fmops_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sme_fmops_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sme_bfmops_w, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, env, i32) + +DEF_HELPER_FLAGS_7(sme_ah_fmops_w_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_7(sme_ah_fmops_s, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sme_ah_fmops_d, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sme_ah_bfmops_w, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, env, i32) + DEF_HELPER_FLAGS_6(sme_smopa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_6(sme_umopa_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 4772c97deb..eff0ce7480 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1002,19 +1002,18 @@ void HELPER(sme_addva_d)(void *vzda, void *vzn, void *vpn, } } -void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, float_status *fpst, uint32_t desc) +static void do_fmopa_s(void *vza, void *vzn, void *vzm, uint16_t *pn, + uint16_t *pm, float_status *fpst, uint32_t desc, + uint32_t negx, int negf) { intptr_t row, col, oprsz = simd_maxsz(desc); - uint32_t neg = simd_data(desc) << 31; - uint16_t *pn = vpn, *pm = vpm; for (row = 0; row < oprsz; ) { uint16_t pa = pn[H2(row >> 4)]; do { if (pa & 1) { void *vza_row = vza + tile_vslice_offset(row); - uint32_t n = *(uint32_t *)(vzn + H1_4(row)) ^ neg; + uint32_t n = *(uint32_t *)(vzn + H1_4(row)) ^ negx; for (col = 0; col < oprsz; ) { uint16_t pb = pm[H2(col >> 4)]; @@ -1022,7 +1021,7 @@ void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, if (pb & 1) { uint32_t *a = vza_row + H1_4(col); uint32_t *m = vzm + H1_4(col); - *a = float32_muladd(n, *m, *a, 0, fpst); + *a = float32_muladd(n, *m, *a, negf, fpst); } col += 4; pb >>= 4; @@ -1035,29 +1034,65 @@ void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, } } -void HELPER(sme_fmopa_d)(void *vza, void *vzn, void *vzm, void *vpn, +void HELPER(sme_fmopa_s)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, float_status *fpst, uint32_t desc) +{ + do_fmopa_s(vza, vzn, vzm, vpn, vpm, fpst, desc, 0, 0); +} + +void HELPER(sme_fmops_s)(void *vza, void *vzn, void *vzm, void *vpn, void *vpm, float_status *fpst, uint32_t desc) +{ + do_fmopa_s(vza, vzn, vzm, vpn, vpm, fpst, desc, 1u << 31, 0); +} + +void HELPER(sme_ah_fmops_s)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, float_status *fpst, uint32_t desc) +{ + do_fmopa_s(vza, vzn, vzm, vpn, vpm, fpst, desc, 0, + float_muladd_negate_product); +} + +static void do_fmopa_d(uint64_t *za, uint64_t *zn, uint64_t *zm, uint8_t *pn, + uint8_t *pm, float_status *fpst, uint32_t desc, + uint64_t negx, int negf) { intptr_t row, col, oprsz = simd_oprsz(desc) / 8; - uint64_t neg = (uint64_t)simd_data(desc) << 63; - uint64_t *za = vza, *zn = vzn, *zm = vzm; - uint8_t *pn = vpn, *pm = vpm; for (row = 0; row < oprsz; ++row) { if (pn[H1(row)] & 1) { uint64_t *za_row = &za[tile_vslice_index(row)]; - uint64_t n = zn[row] ^ neg; + uint64_t n = zn[row] ^ negx; for (col = 0; col < oprsz; ++col) { if (pm[H1(col)] & 1) { uint64_t *a = &za_row[col]; - *a = float64_muladd(n, zm[col], *a, 0, fpst); + *a = float64_muladd(n, zm[col], *a, negf, fpst); } } } } } +void HELPER(sme_fmopa_d)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, float_status *fpst, uint32_t desc) +{ + do_fmopa_d(vza, vzn, vzm, vpn, vpm, fpst, desc, 0, 0); +} + +void HELPER(sme_fmops_d)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, float_status *fpst, uint32_t desc) +{ + do_fmopa_d(vza, vzn, vzm, vpn, vpm, fpst, desc, 1ull << 63, 0); +} + +void HELPER(sme_ah_fmops_d)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, float_status *fpst, uint32_t desc) +{ + do_fmopa_d(vza, vzn, vzm, vpn, vpm, fpst, desc, 0, + float_muladd_negate_product); +} + /* * Alter PAIR as needed for controlling predicates being false, * and for NEG on an enabled row element. @@ -1078,6 +1113,20 @@ static inline uint32_t f16mop_adj_pair(uint32_t pair, uint32_t pg, uint32_t neg) return pair; } +static inline uint32_t f16mop_ah_neg_adj_pair(uint32_t pair, uint32_t pg) +{ + uint32_t l = pg & 1 ? float16_ah_chs(pair) : 0; + uint32_t h = pg & 4 ? float16_ah_chs(pair >> 16) : 0; + return l | (h << 16); +} + +static inline uint32_t bf16mop_ah_neg_adj_pair(uint32_t pair, uint32_t pg) +{ + uint32_t l = pg & 1 ? bfloat16_ah_chs(pair) : 0; + uint32_t h = pg & 4 ? bfloat16_ah_chs(pair >> 16) : 0; + return l | (h << 16); +} + static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2, float_status *s_f16, float_status *s_std, float_status *s_odd) @@ -1146,12 +1195,11 @@ static float32 f16_dotadd(float32 sum, uint32_t e1, uint32_t e2, return float32_add(sum, t32, s_std); } -void HELPER(sme_fmopa_w_h)(void *vza, void *vzn, void *vzm, void *vpn, - void *vpm, CPUARMState *env, uint32_t desc) +static void do_fmopa_w_h(void *vza, void *vzn, void *vzm, uint16_t *pn, + uint16_t *pm, CPUARMState *env, uint32_t desc, + uint32_t negx, bool ah_neg) { intptr_t row, col, oprsz = simd_maxsz(desc); - uint32_t neg = simd_data(desc) * 0x80008000u; - uint16_t *pn = vpn, *pm = vpm; float_status fpst_odd = env->vfp.fp_status[FPST_ZA]; set_float_rounding_mode(float_round_to_odd, &fpst_odd); @@ -1162,7 +1210,11 @@ void HELPER(sme_fmopa_w_h)(void *vza, void *vzn, void *vzm, void *vpn, void *vza_row = vza + tile_vslice_offset(row); uint32_t n = *(uint32_t *)(vzn + H1_4(row)); - n = f16mop_adj_pair(n, prow, neg); + if (ah_neg) { + n = f16mop_ah_neg_adj_pair(n, prow); + } else { + n = f16mop_adj_pair(n, prow, negx); + } for (col = 0; col < oprsz; ) { uint16_t pcol = pm[H2(col >> 4)]; @@ -1187,6 +1239,24 @@ void HELPER(sme_fmopa_w_h)(void *vza, void *vzn, void *vzm, void *vpn, } } +void HELPER(sme_fmopa_w_h)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, CPUARMState *env, uint32_t desc) +{ + do_fmopa_w_h(vza, vzn, vzm, vpn, vpm, env, desc, 0, false); +} + +void HELPER(sme_fmops_w_h)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, CPUARMState *env, uint32_t desc) +{ + do_fmopa_w_h(vza, vzn, vzm, vpn, vpm, env, desc, 0x80008000u, false); +} + +void HELPER(sme_ah_fmops_w_h)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, CPUARMState *env, uint32_t desc) +{ + do_fmopa_w_h(vza, vzn, vzm, vpn, vpm, env, desc, 0, true); +} + void HELPER(sme2_fdot_h)(void *vd, void *vn, void *vm, void *va, CPUARMState *env, uint32_t desc) { @@ -1261,12 +1331,11 @@ void HELPER(sme2_fvdot_idx_h)(void *vd, void *vn, void *vm, void *va, } } -void HELPER(sme_bfmopa_w)(void *vza, void *vzn, void *vzm, - void *vpn, void *vpm, CPUARMState *env, uint32_t desc) +static void do_bfmopa_w(void *vza, void *vzn, void *vzm, + uint16_t *pn, uint16_t *pm, CPUARMState *env, + uint32_t desc, uint32_t negx, bool ah_neg) { intptr_t row, col, oprsz = simd_maxsz(desc); - uint32_t neg = simd_data(desc) * 0x80008000u; - uint16_t *pn = vpn, *pm = vpm; float_status fpst, fpst_odd; if (is_ebf(env, &fpst, &fpst_odd)) { @@ -1276,7 +1345,11 @@ void HELPER(sme_bfmopa_w)(void *vza, void *vzn, void *vzm, void *vza_row = vza + tile_vslice_offset(row); uint32_t n = *(uint32_t *)(vzn + H1_4(row)); - n = f16mop_adj_pair(n, prow, neg); + if (ah_neg) { + n = bf16mop_ah_neg_adj_pair(n, prow); + } else { + n = f16mop_adj_pair(n, prow, negx); + } for (col = 0; col < oprsz; ) { uint16_t pcol = pm[H2(col >> 4)]; @@ -1303,7 +1376,11 @@ void HELPER(sme_bfmopa_w)(void *vza, void *vzn, void *vzm, void *vza_row = vza + tile_vslice_offset(row); uint32_t n = *(uint32_t *)(vzn + H1_4(row)); - n = f16mop_adj_pair(n, prow, neg); + if (ah_neg) { + n = bf16mop_ah_neg_adj_pair(n, prow); + } else { + n = f16mop_adj_pair(n, prow, negx); + } for (col = 0; col < oprsz; ) { uint16_t pcol = pm[H2(col >> 4)]; @@ -1326,6 +1403,24 @@ void HELPER(sme_bfmopa_w)(void *vza, void *vzn, void *vzm, } } +void HELPER(sme_bfmopa_w)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, CPUARMState *env, uint32_t desc) +{ + do_bfmopa_w(vza, vzn, vzm, vpn, vpm, env, desc, 0, false); +} + +void HELPER(sme_bfmops_w)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, CPUARMState *env, uint32_t desc) +{ + do_bfmopa_w(vza, vzn, vzm, vpn, vpm, env, desc, 0x80008000u, false); +} + +void HELPER(sme_ah_bfmops_w)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, CPUARMState *env, uint32_t desc) +{ + do_bfmopa_w(vza, vzn, vzm, vpn, vpm, env, desc, 0, true); +} + typedef uint32_t IMOPFn32(uint32_t, uint32_t, uint32_t, uint8_t, bool); static inline void do_imopa_s(uint32_t *za, uint32_t *zn, uint32_t *zm, uint8_t *pn, uint8_t *pm, diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 38d0231b0a..782f408061 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -526,7 +526,7 @@ static bool do_outprod_fpst(DisasContext *s, arg_op *a, MemOp esz, gen_helper_gvec_5_ptr *fn) { int svl = streaming_vec_reg_size(s); - uint32_t desc = simd_desc(svl, svl, a->sub); + uint32_t desc = simd_desc(svl, svl, 0); TCGv_ptr za, zn, zm, pn, pm, fpst; if (!sme_smza_enabled_check(s)) { @@ -548,7 +548,7 @@ static bool do_outprod_env(DisasContext *s, arg_op *a, MemOp esz, gen_helper_gvec_5_ptr *fn) { int svl = streaming_vec_reg_size(s); - uint32_t desc = simd_desc(svl, svl, a->sub); + uint32_t desc = simd_desc(svl, svl, 0); TCGv_ptr za, zn, zm, pn, pm; if (!sme_smza_enabled_check(s)) { @@ -565,14 +565,23 @@ static bool do_outprod_env(DisasContext *s, arg_op *a, MemOp esz, return true; } -TRANS_FEAT(FMOPA_w_h, aa64_sme, do_outprod_env, a, - MO_32, gen_helper_sme_fmopa_w_h) -TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, - MO_32, FPST_ZA, gen_helper_sme_fmopa_s) -TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, - MO_64, FPST_ZA, gen_helper_sme_fmopa_d) - -TRANS_FEAT(BFMOPA_w, aa64_sme, do_outprod_env, a, MO_32, gen_helper_sme_bfmopa_w) +TRANS_FEAT(FMOPA_w_h, aa64_sme, do_outprod_env, a, MO_32, + !a->sub ? gen_helper_sme_fmopa_w_h + : !s->fpcr_ah ? gen_helper_sme_fmops_w_h + : gen_helper_sme_ah_fmops_w_h) +TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, MO_32, FPST_ZA, + !a->sub ? gen_helper_sme_fmopa_s + : !s->fpcr_ah ? gen_helper_sme_fmops_s + : gen_helper_sme_ah_fmops_s) +TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, MO_64, FPST_ZA, + !a->sub ? gen_helper_sme_fmopa_d + : !s->fpcr_ah ? gen_helper_sme_fmops_d + : gen_helper_sme_ah_fmops_d) + +TRANS_FEAT(BFMOPA_w, aa64_sme, do_outprod_env, a, MO_32, + !a->sub ? gen_helper_sme_bfmopa_w + : !s->fpcr_ah ? gen_helper_sme_bfmops_w + : gen_helper_sme_ah_bfmops_w) TRANS_FEAT(SMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_smopa_s) TRANS_FEAT(UMOPA_s, aa64_sme, do_outprod, a, MO_32, gen_helper_sme_umopa_s) diff --git a/target/arm/tcg/vec_internal.h b/target/arm/tcg/vec_internal.h index 957bf6d9fc..cf41b03dbc 100644 --- a/target/arm/tcg/vec_internal.h +++ b/target/arm/tcg/vec_internal.h @@ -300,6 +300,11 @@ bool is_ebf(CPUARMState *env, float_status *statusp, float_status *oddstatusp); /* * Negate as for FPCR.AH=1 -- do not negate NaNs. */ +static inline float16 bfloat16_ah_chs(float16 a) +{ + return bfloat16_is_any_nan(a) ? a : bfloat16_chs(a); +} + static inline float16 float16_ah_chs(float16 a) { return float16_is_any_nan(a) ? a : float16_chs(a); From 1a039f94d355b615237221c72b81fe6bd2018210 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Jul 2025 08:21:07 -0600 Subject: [PATCH 2051/2760] target/arm: Implement FMOPA (non-widening) for fp16 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Alex Bennée Signed-off-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-105-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 6 ++++ target/arm/tcg/sme.decode | 2 ++ target/arm/tcg/sme_helper.c | 51 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 4 +++ 4 files changed, 63 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 2b22c6aee5..fac70a82b9 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -137,6 +137,8 @@ DEF_HELPER_FLAGS_5(sme_addva_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_7(sme_fmopa_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_7(sme_fmopa_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_fmopa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_fmopa_d, TCG_CALL_NO_RWG, @@ -146,6 +148,8 @@ DEF_HELPER_FLAGS_7(sme_bfmopa_w, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sme_fmops_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_7(sme_fmops_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_fmops_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_fmops_d, TCG_CALL_NO_RWG, @@ -155,6 +159,8 @@ DEF_HELPER_FLAGS_7(sme_bfmops_w, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sme_ah_fmops_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_7(sme_ah_fmops_h, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_ah_fmops_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_ah_fmops_d, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 8ad86f707e..3c58fdd2a0 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -183,9 +183,11 @@ ADDVA_d 11000000 11 01000 1 ... ... ..... 00 ... @adda_64 ### SME Outer Product &op zad zn zm pm pn sub:bool +@op_16 ........ ... zm:5 pm:3 pn:3 zn:5 sub:1 ... zad:1 &op @op_32 ........ ... zm:5 pm:3 pn:3 zn:5 sub:1 .. zad:2 &op @op_64 ........ ... zm:5 pm:3 pn:3 zn:5 sub:1 . zad:3 &op +FMOPA_h 10000001 100 ..... ... ... ..... . 100 . @op_16 FMOPA_s 10000000 100 ..... ... ... ..... . 00 .. @op_32 FMOPA_d 10000000 110 ..... ... ... ..... . 0 ... @op_64 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index eff0ce7480..d23d9f08d2 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1002,6 +1002,57 @@ void HELPER(sme_addva_d)(void *vzda, void *vzn, void *vpn, } } +static void do_fmopa_h(void *vza, void *vzn, void *vzm, uint16_t *pn, + uint16_t *pm, float_status *fpst, uint32_t desc, + uint16_t negx, int negf) +{ + intptr_t row, col, oprsz = simd_maxsz(desc); + + for (row = 0; row < oprsz; ) { + uint16_t pa = pn[H2(row >> 4)]; + do { + if (pa & 1) { + void *vza_row = vza + tile_vslice_offset(row); + uint16_t n = *(uint32_t *)(vzn + H1_2(row)) ^ negx; + + for (col = 0; col < oprsz; ) { + uint16_t pb = pm[H2(col >> 4)]; + do { + if (pb & 1) { + uint16_t *a = vza_row + H1_2(col); + uint16_t *m = vzm + H1_2(col); + *a = float16_muladd(n, *m, *a, negf, fpst); + } + col += 2; + pb >>= 2; + } while (col & 15); + } + } + row += 2; + pa >>= 2; + } while (row & 15); + } +} + +void HELPER(sme_fmopa_h)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, float_status *fpst, uint32_t desc) +{ + do_fmopa_h(vza, vzn, vzm, vpn, vpm, fpst, desc, 0, 0); +} + +void HELPER(sme_fmops_h)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, float_status *fpst, uint32_t desc) +{ + do_fmopa_h(vza, vzn, vzm, vpn, vpm, fpst, desc, 1u << 15, 0); +} + +void HELPER(sme_ah_fmops_h)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, float_status *fpst, uint32_t desc) +{ + do_fmopa_h(vza, vzn, vzm, vpn, vpm, fpst, desc, 0, + float_muladd_negate_product); +} + static void do_fmopa_s(void *vza, void *vzn, void *vzm, uint16_t *pn, uint16_t *pm, float_status *fpst, uint32_t desc, uint32_t negx, int negf) diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 782f408061..119a5665e6 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -569,6 +569,10 @@ TRANS_FEAT(FMOPA_w_h, aa64_sme, do_outprod_env, a, MO_32, !a->sub ? gen_helper_sme_fmopa_w_h : !s->fpcr_ah ? gen_helper_sme_fmops_w_h : gen_helper_sme_ah_fmops_w_h) +TRANS_FEAT(FMOPA_h, aa64_sme_f16f16, do_outprod_fpst, a, MO_16, FPST_ZA_F16, + !a->sub ? gen_helper_sme_fmopa_h + : !s->fpcr_ah ? gen_helper_sme_fmops_h + : gen_helper_sme_ah_fmops_h) TRANS_FEAT(FMOPA_s, aa64_sme, do_outprod_fpst, a, MO_32, FPST_ZA, !a->sub ? gen_helper_sme_fmopa_s : !s->fpcr_ah ? gen_helper_sme_fmops_s From 4805911a925ffbf798b29dce50cbb9585fd8e87e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Jul 2025 08:21:08 -0600 Subject: [PATCH 2052/2760] target/arm: Implement SME2 BFMOPA (non-widening) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Alex Bennée Signed-off-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-106-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/tcg/helper-sme.h | 6 ++++ target/arm/tcg/sme.decode | 2 ++ target/arm/tcg/sme_helper.c | 51 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sme.c | 5 ++++ 4 files changed, 64 insertions(+) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index fac70a82b9..1fc756bec6 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -145,6 +145,8 @@ DEF_HELPER_FLAGS_7(sme_fmopa_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_bfmopa_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_7(sme_bfmopa, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_fmops_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) @@ -156,6 +158,8 @@ DEF_HELPER_FLAGS_7(sme_fmops_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_bfmops_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_7(sme_bfmops, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_ah_fmops_w_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) @@ -167,6 +171,8 @@ DEF_HELPER_FLAGS_7(sme_ah_fmops_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sme_ah_bfmops_w, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, env, i32) +DEF_HELPER_FLAGS_7(sme_ah_bfmops, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sme_smopa_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/sme.decode b/target/arm/tcg/sme.decode index 3c58fdd2a0..6bb9aa2a90 100644 --- a/target/arm/tcg/sme.decode +++ b/target/arm/tcg/sme.decode @@ -191,6 +191,8 @@ FMOPA_h 10000001 100 ..... ... ... ..... . 100 . @op_16 FMOPA_s 10000000 100 ..... ... ... ..... . 00 .. @op_32 FMOPA_d 10000000 110 ..... ... ... ..... . 0 ... @op_64 +BFMOPA 10000001 101 ..... ... ... ..... . 100 . @op_16 + BFMOPA_w 10000001 100 ..... ... ... ..... . 00 .. @op_32 FMOPA_w_h 10000001 101 ..... ... ... ..... . 00 .. @op_32 diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index d23d9f08d2..bb8ed1ed0e 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -1144,6 +1144,57 @@ void HELPER(sme_ah_fmops_d)(void *vza, void *vzn, void *vzm, void *vpn, float_muladd_negate_product); } +static void do_bfmopa(void *vza, void *vzn, void *vzm, uint16_t *pn, + uint16_t *pm, float_status *fpst, uint32_t desc, + uint16_t negx, int negf) +{ + intptr_t row, col, oprsz = simd_maxsz(desc); + + for (row = 0; row < oprsz; ) { + uint16_t pa = pn[H2(row >> 4)]; + do { + if (pa & 1) { + void *vza_row = vza + tile_vslice_offset(row); + uint16_t n = *(uint32_t *)(vzn + H1_2(row)) ^ negx; + + for (col = 0; col < oprsz; ) { + uint16_t pb = pm[H2(col >> 4)]; + do { + if (pb & 1) { + uint16_t *a = vza_row + H1_2(col); + uint16_t *m = vzm + H1_2(col); + *a = bfloat16_muladd(n, *m, *a, negf, fpst); + } + col += 2; + pb >>= 2; + } while (col & 15); + } + } + row += 2; + pa >>= 2; + } while (row & 15); + } +} + +void HELPER(sme_bfmopa)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, float_status *fpst, uint32_t desc) +{ + do_bfmopa(vza, vzn, vzm, vpn, vpm, fpst, desc, 0, 0); +} + +void HELPER(sme_bfmops)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, float_status *fpst, uint32_t desc) +{ + do_bfmopa(vza, vzn, vzm, vpn, vpm, fpst, desc, 1u << 15, 0); +} + +void HELPER(sme_ah_bfmops)(void *vza, void *vzn, void *vzm, void *vpn, + void *vpm, float_status *fpst, uint32_t desc) +{ + do_bfmopa(vza, vzn, vzm, vpn, vpm, fpst, desc, 0, + float_muladd_negate_product); +} + /* * Alter PAIR as needed for controlling predicates being false, * and for NEG on an enabled row element. diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 119a5665e6..65fc8bc9b2 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -582,6 +582,11 @@ TRANS_FEAT(FMOPA_d, aa64_sme_f64f64, do_outprod_fpst, a, MO_64, FPST_ZA, : !s->fpcr_ah ? gen_helper_sme_fmops_d : gen_helper_sme_ah_fmops_d) +TRANS_FEAT(BFMOPA, aa64_sme_b16b16, do_outprod_fpst, a, MO_16, FPST_ZA, + !a->sub ? gen_helper_sme_bfmopa + : !s->fpcr_ah ? gen_helper_sme_bfmops + : gen_helper_sme_ah_bfmops) + TRANS_FEAT(BFMOPA_w, aa64_sme, do_outprod_env, a, MO_32, !a->sub ? gen_helper_sme_bfmopa_w : !s->fpcr_ah ? gen_helper_sme_bfmops_w From 7b1613a1020d294219867a2cad6b41c46acec8f2 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:21:09 -0600 Subject: [PATCH 2053/2760] target/arm: Enable FEAT_SME2p1 on -cpu max Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-107-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- docs/system/arm/emulation.rst | 6 ++++++ target/arm/tcg/cpu64.c | 10 ++++++++-- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/system/arm/emulation.rst b/docs/system/arm/emulation.rst index 78c2fd2113..890dc6fee2 100644 --- a/docs/system/arm/emulation.rst +++ b/docs/system/arm/emulation.rst @@ -129,16 +129,22 @@ the following architecture extensions: - FEAT_SM3 (Advanced SIMD SM3 instructions) - FEAT_SM4 (Advanced SIMD SM4 instructions) - FEAT_SME (Scalable Matrix Extension) +- FEAT_SME2 (Scalable Matrix Extension version 2) +- FEAT_SME2p1 (Scalable Matrix Extension version 2.1) +- FEAT_SME_B16B16 (Non-widening BFloat16 arithmetic for SME2) - FEAT_SME_FA64 (Full A64 instruction set in Streaming SVE mode) +- FEAT_SME_F16F16 (Non-widening half-precision FP16 arithmetic for SME2) - FEAT_SME_F64F64 (Double-precision floating-point outer product instructions) - FEAT_SME_I16I64 (16-bit to 64-bit integer widening outer product instructions) - FEAT_SVE (Scalable Vector Extension) - FEAT_SVE_AES (Scalable Vector AES instructions) +- FEAT_SVE_B16B16 (Non-widening BFloat16 arithmetic for SVE2) - FEAT_SVE_BitPerm (Scalable Vector Bit Permutes instructions) - FEAT_SVE_PMULL128 (Scalable Vector PMULL instructions) - FEAT_SVE_SHA3 (Scalable Vector SHA3 instructions) - FEAT_SVE_SM4 (Scalable Vector SM4 instructions) - FEAT_SVE2 (Scalable Vector Extension version 2) +- FEAT_SVE2p1 (Scalable Vector Extension version 2.1) - FEAT_SPECRES (Speculation restriction instructions) - FEAT_SSBS (Speculative Store Bypass Safe) - FEAT_SSBS2 (MRS and MSR instructions for SSBS version 2) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 937f29e253..d0df50a2f3 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -1201,7 +1201,7 @@ void aarch64_max_tcg_initfn(Object *obj) */ t = FIELD_DP64(t, ID_AA64PFR1, MTE, 3); /* FEAT_MTE3 */ t = FIELD_DP64(t, ID_AA64PFR1, RAS_FRAC, 0); /* FEAT_RASv1p1 + FEAT_DoubleFault */ - t = FIELD_DP64(t, ID_AA64PFR1, SME, 1); /* FEAT_SME */ + t = FIELD_DP64(t, ID_AA64PFR1, SME, 2); /* FEAT_SME2 */ t = FIELD_DP64(t, ID_AA64PFR1, CSV2_FRAC, 0); /* FEAT_CSV2_3 */ t = FIELD_DP64(t, ID_AA64PFR1, NMI, 1); /* FEAT_NMI */ SET_IDREG(isar, ID_AA64PFR1, t); @@ -1250,10 +1250,11 @@ void aarch64_max_tcg_initfn(Object *obj) FIELD_DP64_IDREG(isar, ID_AA64MMFR3, SPEC_FPACC, 1); /* FEAT_FPACC_SPEC */ t = GET_IDREG(isar, ID_AA64ZFR0); - t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 1); + t = FIELD_DP64(t, ID_AA64ZFR0, SVEVER, 2); /* FEAT_SVE2p1 */ t = FIELD_DP64(t, ID_AA64ZFR0, AES, 2); /* FEAT_SVE_PMULL128 */ t = FIELD_DP64(t, ID_AA64ZFR0, BITPERM, 1); /* FEAT_SVE_BitPerm */ t = FIELD_DP64(t, ID_AA64ZFR0, BFLOAT16, 2); /* FEAT_BF16, FEAT_EBF16 */ + t = FIELD_DP64(t, ID_AA64ZFR0, B16B16, 1); /* FEAT_SVE_B16B16 */ t = FIELD_DP64(t, ID_AA64ZFR0, SHA3, 1); /* FEAT_SVE_SHA3 */ t = FIELD_DP64(t, ID_AA64ZFR0, SM4, 1); /* FEAT_SVE_SM4 */ t = FIELD_DP64(t, ID_AA64ZFR0, I8MM, 1); /* FEAT_I8MM */ @@ -1269,11 +1270,16 @@ void aarch64_max_tcg_initfn(Object *obj) t = GET_IDREG(isar, ID_AA64SMFR0); t = FIELD_DP64(t, ID_AA64SMFR0, F32F32, 1); /* FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, BI32I32, 1); /* FEAT_SME2 */ t = FIELD_DP64(t, ID_AA64SMFR0, B16F32, 1); /* FEAT_SME */ t = FIELD_DP64(t, ID_AA64SMFR0, F16F32, 1); /* FEAT_SME */ t = FIELD_DP64(t, ID_AA64SMFR0, I8I32, 0xf); /* FEAT_SME */ + t = FIELD_DP64(t, ID_AA64SMFR0, F16F16, 1); /* FEAT_SME_F16F16 */ + t = FIELD_DP64(t, ID_AA64SMFR0, B16B16, 1); /* FEAT_SME_B16B16 */ + t = FIELD_DP64(t, ID_AA64SMFR0, I16I32, 5); /* FEAT_SME2 */ t = FIELD_DP64(t, ID_AA64SMFR0, F64F64, 1); /* FEAT_SME_F64F64 */ t = FIELD_DP64(t, ID_AA64SMFR0, I16I64, 0xf); /* FEAT_SME_I16I64 */ + t = FIELD_DP64(t, ID_AA64SMFR0, SMEVER, 2); /* FEAT_SME2p1 */ t = FIELD_DP64(t, ID_AA64SMFR0, FA64, 1); /* FEAT_SME_FA64 */ SET_IDREG(isar, ID_AA64SMFR0, t); From 083fef73585dfa03f72055ace6de8dec4912d0b0 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 4 Jul 2025 08:21:10 -0600 Subject: [PATCH 2054/2760] linux-user/aarch64: Set hwcap bits for SME2p1/SVE2p1 Reviewed-by: Peter Maydell Signed-off-by: Richard Henderson Message-id: 20250704142112.1018902-108-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- linux-user/elfload.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/linux-user/elfload.c b/linux-user/elfload.c index 2add1665c7..ea214105ff 100644 --- a/linux-user/elfload.c +++ b/linux-user/elfload.c @@ -915,6 +915,14 @@ uint64_t get_elf_hwcap2(void) GET_FEATURE_ID(aa64_sme_fa64, ARM_HWCAP2_A64_SME_FA64); GET_FEATURE_ID(aa64_hbc, ARM_HWCAP2_A64_HBC); GET_FEATURE_ID(aa64_mops, ARM_HWCAP2_A64_MOPS); + GET_FEATURE_ID(aa64_sve2p1, ARM_HWCAP2_A64_SVE2P1); + GET_FEATURE_ID(aa64_sme2, (ARM_HWCAP2_A64_SME2 | + ARM_HWCAP2_A64_SME_I16I32 | + ARM_HWCAP2_A64_SME_BI32I32)); + GET_FEATURE_ID(aa64_sme2p1, ARM_HWCAP2_A64_SME2P1); + GET_FEATURE_ID(aa64_sme_b16b16, ARM_HWCAP2_A64_SME_B16B16); + GET_FEATURE_ID(aa64_sme_f16f16, ARM_HWCAP2_A64_SME_F16F16); + GET_FEATURE_ID(aa64_sve_b16b16, ARM_HWCAP2_A64_SVE_B16B16); return hwcaps; } From 51eb283dd0e29f29adf1943c063614156ca7cbd7 Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 4 Jul 2025 18:32:29 -0400 Subject: [PATCH 2055/2760] MAX78000: Add MAX78000FTHR Machine This patch adds support for the MAX78000FTHR machine. The MAX78000FTHR contains a MAX78000 and a RISC-V core. This patch implements only the MAX78000, which is Cortex-M4 based. Details can be found at: https://www.analog.com/media/en/technical-documentation/user-guides/max78000-user-guide.pdf Signed-off-by: Jackson Donaldson Reviewed-by: Peter Maydell Message-id: 20250704223239.248781-2-jcksn@duck.com Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 10 ++ hw/arm/max78000_soc.c | 172 ++++++++++++++++++++++++++++++++++ hw/arm/max78000fthr.c | 50 ++++++++++ hw/arm/meson.build | 2 + include/hw/arm/max78000_soc.h | 35 +++++++ 5 files changed, 269 insertions(+) create mode 100644 hw/arm/max78000_soc.c create mode 100644 hw/arm/max78000fthr.c create mode 100644 include/hw/arm/max78000_soc.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 6ea86534d5..44815af41f 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -95,6 +95,12 @@ config INTEGRATOR select PL181 # display select SMC91C111 +config MAX78000FTHR + bool + default y + depends on TCG && ARM + select MAX78000_SOC + config MPS3R bool default y @@ -357,6 +363,10 @@ config ALLWINNER_R40 select USB_EHCI_SYSBUS select SD +config MAX78000_SOC + bool + select ARM_V7M + config RASPI bool default y diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c new file mode 100644 index 0000000000..9676ada6a2 --- /dev/null +++ b/hw/arm/max78000_soc.c @@ -0,0 +1,172 @@ +/* + * MAX78000 SOC + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + * + * Implementation based on stm32f205 and Max78000 user guide at + * https://www.analog.com/media/en/technical-documentation/user-guides/max78000-user-guide.pdf + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "system/address-spaces.h" +#include "system/system.h" +#include "hw/arm/max78000_soc.h" +#include "hw/qdev-clock.h" +#include "hw/misc/unimp.h" + +static void max78000_soc_initfn(Object *obj) +{ + MAX78000State *s = MAX78000_SOC(obj); + + object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M); + + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); +} + +static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) +{ + MAX78000State *s = MAX78000_SOC(dev_soc); + MemoryRegion *system_memory = get_system_memory(); + DeviceState *armv7m; + Error *err = NULL; + + if (!clock_has_source(s->sysclk)) { + error_setg(errp, "sysclk clock must be wired up by the board code"); + return; + } + + memory_region_init_rom(&s->flash, OBJECT(dev_soc), "MAX78000.flash", + FLASH_SIZE, &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + + memory_region_add_subregion(system_memory, FLASH_BASE_ADDRESS, &s->flash); + + memory_region_init_ram(&s->sram, NULL, "MAX78000.sram", SRAM_SIZE, + &err); + if (err != NULL) { + error_propagate(errp, err); + return; + } + memory_region_add_subregion(system_memory, SRAM_BASE_ADDRESS, &s->sram); + + armv7m = DEVICE(&s->armv7m); + + /* + * The MAX78000 user guide's Interrupt Vector Table section + * suggests that there are 120 IRQs in the text, while only listing + * 104 in table 5-1. Implement the more generous of the two. + * This has not been tested in hardware. + */ + qdev_prop_set_uint32(armv7m, "num-irq", 120); + qdev_prop_set_uint8(armv7m, "num-prio-bits", 3); + qdev_prop_set_string(armv7m, "cpu-type", ARM_CPU_TYPE_NAME("cortex-m4")); + qdev_prop_set_bit(armv7m, "enable-bitband", true); + qdev_connect_clock_in(armv7m, "cpuclk", s->sysclk); + object_property_set_link(OBJECT(&s->armv7m), "memory", + OBJECT(system_memory), &error_abort); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->armv7m), errp)) { + return; + } + + create_unimplemented_device("globalControl", 0x40000000, 0x400); + create_unimplemented_device("systemInterface", 0x40000400, 0x400); + create_unimplemented_device("functionControl", 0x40000800, 0x400); + create_unimplemented_device("watchdogTimer0", 0x40003000, 0x400); + create_unimplemented_device("dynamicVoltScale", 0x40003c00, 0x40); + create_unimplemented_device("SIMO", 0x40004400, 0x400); + create_unimplemented_device("trimSystemInit", 0x40005400, 0x400); + create_unimplemented_device("generalCtrlFunc", 0x40005800, 0x400); + create_unimplemented_device("wakeupTimer", 0x40006400, 0x400); + create_unimplemented_device("powerSequencer", 0x40006800, 0x400); + create_unimplemented_device("miscControl", 0x40006c00, 0x400); + + create_unimplemented_device("aes", 0x40007400, 0x400); + create_unimplemented_device("aesKey", 0x40007800, 0x400); + + create_unimplemented_device("gpio0", 0x40008000, 0x1000); + create_unimplemented_device("gpio1", 0x40009000, 0x1000); + + create_unimplemented_device("parallelCamInterface", 0x4000e000, 0x1000); + create_unimplemented_device("CRC", 0x4000f000, 0x1000); + + create_unimplemented_device("timer0", 0x40010000, 0x1000); + create_unimplemented_device("timer1", 0x40011000, 0x1000); + create_unimplemented_device("timer2", 0x40012000, 0x1000); + create_unimplemented_device("timer3", 0x40013000, 0x1000); + + create_unimplemented_device("i2c0", 0x4001d000, 0x1000); + create_unimplemented_device("i2c1", 0x4001e000, 0x1000); + create_unimplemented_device("i2c2", 0x4001f000, 0x1000); + + create_unimplemented_device("standardDMA", 0x40028000, 0x1000); + create_unimplemented_device("flashController0", 0x40029000, 0x400); + + create_unimplemented_device("icc0", 0x4002a000, 0x800); + create_unimplemented_device("icc1", 0x4002a800, 0x800); + + create_unimplemented_device("adc", 0x40034000, 0x1000); + create_unimplemented_device("pulseTrainEngine", 0x4003c000, 0xa0); + create_unimplemented_device("oneWireMaster", 0x4003d000, 0x1000); + create_unimplemented_device("semaphore", 0x4003e000, 0x1000); + + create_unimplemented_device("uart0", 0x40042000, 0x1000); + create_unimplemented_device("uart1", 0x40043000, 0x1000); + create_unimplemented_device("uart2", 0x40044000, 0x1000); + + create_unimplemented_device("spi1", 0x40046000, 0x2000); + create_unimplemented_device("trng", 0x4004d000, 0x1000); + create_unimplemented_device("i2s", 0x40060000, 0x1000); + create_unimplemented_device("lowPowerControl", 0x40080000, 0x400); + create_unimplemented_device("gpio2", 0x40080400, 0x200); + create_unimplemented_device("lowPowerWatchdogTimer", 0x40080800, 0x400); + create_unimplemented_device("lowPowerTimer4", 0x40080c00, 0x400); + + create_unimplemented_device("lowPowerTimer5", 0x40081000, 0x400); + create_unimplemented_device("lowPowerUART0", 0x40081400, 0x400); + create_unimplemented_device("lowPowerComparator", 0x40088000, 0x400); + + create_unimplemented_device("spi0", 0x400be000, 0x400); + + /* + * The MAX78000 user guide's base address map lists the CNN TX FIFO as + * beginning at 0x400c0400 and ending at 0x400c0400. Given that CNN_FIFO + * is listed as having data accessible up to offset 0x1000, the user + * guide is likely incorrect. + */ + create_unimplemented_device("cnnTxFIFO", 0x400c0400, 0x2000); + + create_unimplemented_device("cnnGlobalControl", 0x50000000, 0x10000); + create_unimplemented_device("cnnx16quad0", 0x50100000, 0x40000); + create_unimplemented_device("cnnx16quad1", 0x50500000, 0x40000); + create_unimplemented_device("cnnx16quad2", 0x50900000, 0x40000); + create_unimplemented_device("cnnx16quad3", 0x50d00000, 0x40000); + +} + +static void max78000_soc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->realize = max78000_soc_realize; +} + +static const TypeInfo max78000_soc_info = { + .name = TYPE_MAX78000_SOC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(MAX78000State), + .instance_init = max78000_soc_initfn, + .class_init = max78000_soc_class_init, +}; + +static void max78000_soc_types(void) +{ + type_register_static(&max78000_soc_info); +} + +type_init(max78000_soc_types) diff --git a/hw/arm/max78000fthr.c b/hw/arm/max78000fthr.c new file mode 100644 index 0000000000..c4f6b5b1b0 --- /dev/null +++ b/hw/arm/max78000fthr.c @@ -0,0 +1,50 @@ +/* + * MAX78000FTHR Evaluation Board + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "hw/boards.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-clock.h" +#include "qemu/error-report.h" +#include "hw/arm/max78000_soc.h" +#include "hw/arm/boot.h" + +/* 60MHz is the default, but other clocks can be selected. */ +#define SYSCLK_FRQ 60000000ULL +static void max78000_init(MachineState *machine) +{ + DeviceState *dev; + Clock *sysclk; + + sysclk = clock_new(OBJECT(machine), "SYSCLK"); + clock_set_hz(sysclk, SYSCLK_FRQ); + + dev = qdev_new(TYPE_MAX78000_SOC); + object_property_add_child(OBJECT(machine), "soc", OBJECT(dev)); + qdev_connect_clock_in(dev, "sysclk", sysclk); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + + armv7m_load_kernel(ARM_CPU(first_cpu), + machine->kernel_filename, + 0x00000000, FLASH_SIZE); +} + +static void max78000_machine_init(MachineClass *mc) +{ + static const char * const valid_cpu_types[] = { + ARM_CPU_TYPE_NAME("cortex-m4"), + NULL + }; + + mc->desc = "MAX78000FTHR Board (Cortex-M4 / (Unimplemented) RISC-V)"; + mc->init = max78000_init; + mc->valid_cpu_types = valid_cpu_types; +} + +DEFINE_MACHINE("max78000fthr", max78000_machine_init) diff --git a/hw/arm/meson.build b/hw/arm/meson.build index d90be8f4c9..dc68391305 100644 --- a/hw/arm/meson.build +++ b/hw/arm/meson.build @@ -27,6 +27,7 @@ arm_common_ss.add(when: 'CONFIG_OMAP', if_true: files('omap1.c')) arm_common_ss.add(when: 'CONFIG_ALLWINNER_A10', if_true: files('allwinner-a10.c', 'cubieboard.c')) arm_common_ss.add(when: 'CONFIG_ALLWINNER_H3', if_true: files('allwinner-h3.c', 'orangepi.c')) arm_common_ss.add(when: 'CONFIG_ALLWINNER_R40', if_true: files('allwinner-r40.c', 'bananapi_m2u.c')) +arm_common_ss.add(when: 'CONFIG_MAX78000_SOC', if_true: files('max78000_soc.c')) arm_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2836.c', 'raspi.c')) arm_common_ss.add(when: ['CONFIG_RASPI', 'TARGET_AARCH64'], if_true: files('bcm2838.c', 'raspi4b.c')) arm_common_ss.add(when: 'CONFIG_STM32F100_SOC', if_true: files('stm32f100_soc.c')) @@ -71,6 +72,7 @@ arm_ss.add(when: 'CONFIG_XEN', if_true: files( arm_common_ss.add(when: 'CONFIG_ARM_SMMUV3', if_true: files('smmu-common.c')) arm_common_ss.add(when: 'CONFIG_COLLIE', if_true: files('collie.c')) arm_common_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4_boards.c')) +arm_common_ss.add(when: 'CONFIG_MAX78000FTHR', if_true: files('max78000fthr.c')) arm_common_ss.add(when: 'CONFIG_NETDUINO2', if_true: files('netduino2.c')) arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_peripherals.c')) arm_common_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2838_peripherals.c')) diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h new file mode 100644 index 0000000000..97bf4099c9 --- /dev/null +++ b/include/hw/arm/max78000_soc.h @@ -0,0 +1,35 @@ +/* + * MAX78000 SOC + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_ARM_MAX78000_SOC_H +#define HW_ARM_MAX78000_SOC_H + +#include "hw/or-irq.h" +#include "hw/arm/armv7m.h" +#include "qom/object.h" + +#define TYPE_MAX78000_SOC "max78000-soc" +OBJECT_DECLARE_SIMPLE_TYPE(MAX78000State, MAX78000_SOC) + +#define FLASH_BASE_ADDRESS 0x10000000 +#define FLASH_SIZE (512 * 1024) +#define SRAM_BASE_ADDRESS 0x20000000 +#define SRAM_SIZE (128 * 1024) + +struct MAX78000State { + SysBusDevice parent_obj; + + ARMv7MState armv7m; + + MemoryRegion sram; + MemoryRegion flash; + + Clock *sysclk; +}; + +#endif From 3ec680e64c6d0686c518f25fdadf8866d7cd12a1 Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 4 Jul 2025 18:32:30 -0400 Subject: [PATCH 2056/2760] MAX78000: ICC Implementation This commit implements the Instruction Cache Controller for the MAX78000 Signed-off-by: Jackson Donaldson Reviewed-by: Peter Maydell Message-id: 20250704223239.248781-3-jcksn@duck.com Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/misc/Kconfig | 3 + hw/misc/max78000_icc.c | 120 +++++++++++++++++++++++++++++++++ hw/misc/meson.build | 1 + include/hw/misc/max78000_icc.h | 33 +++++++++ 5 files changed, 158 insertions(+) create mode 100644 hw/misc/max78000_icc.c create mode 100644 include/hw/misc/max78000_icc.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 44815af41f..035568a085 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -366,6 +366,7 @@ config ALLWINNER_R40 config MAX78000_SOC bool select ARM_V7M + select MAX78000_ICC config RASPI bool diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index ec0fa5aa9f..781bcf74cc 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -47,6 +47,9 @@ config A9SCU config ARM11SCU bool +config MAX78000_ICC + bool + config MOS6522 bool diff --git a/hw/misc/max78000_icc.c b/hw/misc/max78000_icc.c new file mode 100644 index 0000000000..6f7d2b20bf --- /dev/null +++ b/hw/misc/max78000_icc.c @@ -0,0 +1,120 @@ +/* + * MAX78000 Instruction Cache + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "hw/misc/max78000_icc.h" + + +static uint64_t max78000_icc_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Max78000IccState *s = opaque; + switch (addr) { + case ICC_INFO: + return s->info; + + case ICC_SZ: + return s->sz; + + case ICC_CTRL: + return s->ctrl; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + return 0; + + } +} + +static void max78000_icc_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Max78000IccState *s = opaque; + + switch (addr) { + case ICC_CTRL: + s->ctrl = 0x10000 | (val64 & 1); + break; + + case ICC_INVALIDATE: + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + break; + } +} + +static const MemoryRegionOps max78000_icc_ops = { + .read = max78000_icc_read, + .write = max78000_icc_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription max78000_icc_vmstate = { + .name = TYPE_MAX78000_ICC, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(info, Max78000IccState), + VMSTATE_UINT32(sz, Max78000IccState), + VMSTATE_UINT32(ctrl, Max78000IccState), + VMSTATE_END_OF_LIST() + } +}; + +static void max78000_icc_reset_hold(Object *obj, ResetType type) +{ + Max78000IccState *s = MAX78000_ICC(obj); + s->info = 0; + s->sz = 0x10000010; + s->ctrl = 0x10000; +} + +static void max78000_icc_init(Object *obj) +{ + Max78000IccState *s = MAX78000_ICC(obj); + + memory_region_init_io(&s->mmio, obj, &max78000_icc_ops, s, + TYPE_MAX78000_ICC, 0x800); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void max78000_icc_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = max78000_icc_reset_hold; + dc->vmsd = &max78000_icc_vmstate; +} + +static const TypeInfo max78000_icc_info = { + .name = TYPE_MAX78000_ICC, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Max78000IccState), + .instance_init = max78000_icc_init, + .class_init = max78000_icc_class_init, +}; + +static void max78000_icc_register_types(void) +{ + type_register_static(&max78000_icc_info); +} + +type_init(max78000_icc_register_types) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 6d47de482c..a21a994ff8 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -70,6 +70,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files( 'imx_ccm.c', 'imx_rngc.c', )) +system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c')) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files( 'npcm_clk.c', 'npcm_gcr.c', diff --git a/include/hw/misc/max78000_icc.h b/include/hw/misc/max78000_icc.h new file mode 100644 index 0000000000..6fe2bb7a15 --- /dev/null +++ b/include/hw/misc/max78000_icc.h @@ -0,0 +1,33 @@ +/* + * MAX78000 Instruction Cache + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MAX78000_ICC_H +#define HW_MAX78000_ICC_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_MAX78000_ICC "max78000-icc" +OBJECT_DECLARE_SIMPLE_TYPE(Max78000IccState, MAX78000_ICC) + +#define ICC_INFO 0x0 +#define ICC_SZ 0x4 +#define ICC_CTRL 0x100 +#define ICC_INVALIDATE 0x700 + +struct Max78000IccState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t info; + uint32_t sz; + uint32_t ctrl; +}; + +#endif From 65714d3e6c384956537c43ee9a58f2e4ebfdd883 Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 4 Jul 2025 18:32:31 -0400 Subject: [PATCH 2057/2760] MAX78000: Add ICC to SOC This commit adds the instruction cache controller to max78000_soc Signed-off-by: Jackson Donaldson Reviewed-by: Peter Maydell Message-id: 20250704223239.248781-4-jcksn@duck.com Signed-off-by: Peter Maydell --- hw/arm/max78000_soc.c | 20 ++++++++++++++++---- include/hw/arm/max78000_soc.h | 6 ++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c index 9676ada6a2..0c83b08eca 100644 --- a/hw/arm/max78000_soc.c +++ b/hw/arm/max78000_soc.c @@ -17,12 +17,20 @@ #include "hw/qdev-clock.h" #include "hw/misc/unimp.h" +static const uint32_t max78000_icc_addr[] = {0x4002a000, 0x4002a800}; + static void max78000_soc_initfn(Object *obj) { MAX78000State *s = MAX78000_SOC(obj); + int i; object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M); + for (i = 0; i < MAX78000_NUM_ICC; i++) { + g_autofree char *name = g_strdup_printf("icc%d", i); + object_initialize_child(obj, name, &s->icc[i], TYPE_MAX78000_ICC); + } + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); } @@ -30,8 +38,9 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) { MAX78000State *s = MAX78000_SOC(dev_soc); MemoryRegion *system_memory = get_system_memory(); - DeviceState *armv7m; + DeviceState *dev, *armv7m; Error *err = NULL; + int i; if (!clock_has_source(s->sysclk)) { error_setg(errp, "sysclk clock must be wired up by the board code"); @@ -74,6 +83,12 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) return; } + for (i = 0; i < MAX78000_NUM_ICC; i++) { + dev = DEVICE(&(s->icc[i])); + sysbus_realize(SYS_BUS_DEVICE(dev), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, max78000_icc_addr[i]); + } + create_unimplemented_device("globalControl", 0x40000000, 0x400); create_unimplemented_device("systemInterface", 0x40000400, 0x400); create_unimplemented_device("functionControl", 0x40000800, 0x400); @@ -107,9 +122,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("standardDMA", 0x40028000, 0x1000); create_unimplemented_device("flashController0", 0x40029000, 0x400); - create_unimplemented_device("icc0", 0x4002a000, 0x800); - create_unimplemented_device("icc1", 0x4002a800, 0x800); - create_unimplemented_device("adc", 0x40034000, 0x1000); create_unimplemented_device("pulseTrainEngine", 0x4003c000, 0xa0); create_unimplemented_device("oneWireMaster", 0x4003d000, 0x1000); diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h index 97bf4099c9..27b506d6ee 100644 --- a/include/hw/arm/max78000_soc.h +++ b/include/hw/arm/max78000_soc.h @@ -11,6 +11,7 @@ #include "hw/or-irq.h" #include "hw/arm/armv7m.h" +#include "hw/misc/max78000_icc.h" #include "qom/object.h" #define TYPE_MAX78000_SOC "max78000-soc" @@ -21,6 +22,9 @@ OBJECT_DECLARE_SIMPLE_TYPE(MAX78000State, MAX78000_SOC) #define SRAM_BASE_ADDRESS 0x20000000 #define SRAM_SIZE (128 * 1024) +/* The MAX78k has 2 instruction caches; only icc0 matters, icc1 is for RISC */ +#define MAX78000_NUM_ICC 2 + struct MAX78000State { SysBusDevice parent_obj; @@ -29,6 +33,8 @@ struct MAX78000State { MemoryRegion sram; MemoryRegion flash; + Max78000IccState icc[MAX78000_NUM_ICC]; + Clock *sysclk; }; From d447e4b70295bb7a11715230f56ccea7d8b3b797 Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 4 Jul 2025 18:32:32 -0400 Subject: [PATCH 2058/2760] MAX78000: UART Implementation This commit implements UART support for the MAX78000 Signed-off-by: Jackson Donaldson Reviewed-by: Peter Maydell Message-id: 20250704223239.248781-5-jcksn@duck.com Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/char/Kconfig | 3 + hw/char/max78000_uart.c | 285 ++++++++++++++++++++++++++++++++ hw/char/meson.build | 1 + include/hw/char/max78000_uart.h | 78 +++++++++ 5 files changed, 368 insertions(+) create mode 100644 hw/char/max78000_uart.c create mode 100644 include/hw/char/max78000_uart.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 035568a085..7e3610dca8 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -367,6 +367,7 @@ config MAX78000_SOC bool select ARM_V7M select MAX78000_ICC + select MAX78000_UART config RASPI bool diff --git a/hw/char/Kconfig b/hw/char/Kconfig index 9d517f3e28..020c0a84bb 100644 --- a/hw/char/Kconfig +++ b/hw/char/Kconfig @@ -48,6 +48,9 @@ config VIRTIO_SERIAL default y depends on VIRTIO +config MAX78000_UART + bool + config STM32F2XX_USART bool diff --git a/hw/char/max78000_uart.c b/hw/char/max78000_uart.c new file mode 100644 index 0000000000..19506d52ef --- /dev/null +++ b/hw/char/max78000_uart.c @@ -0,0 +1,285 @@ +/* + * MAX78000 UART + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "hw/char/max78000_uart.h" +#include "hw/irq.h" +#include "hw/qdev-properties.h" +#include "hw/qdev-properties-system.h" +#include "qemu/log.h" +#include "qemu/module.h" +#include "migration/vmstate.h" +#include "trace.h" + + +static int max78000_uart_can_receive(void *opaque) +{ + Max78000UartState *s = opaque; + if (!(s->ctrl & UART_BCLKEN)) { + return 0; + } + return fifo8_num_free(&s->rx_fifo); +} + +static void max78000_update_irq(Max78000UartState *s) +{ + int interrupt_level; + + interrupt_level = s->int_fl & s->int_en; + qemu_set_irq(s->irq, interrupt_level); +} + +static void max78000_uart_receive(void *opaque, const uint8_t *buf, int size) +{ + Max78000UartState *s = opaque; + + assert(size <= fifo8_num_free(&s->rx_fifo)); + + fifo8_push_all(&s->rx_fifo, buf, size); + + uint32_t rx_threshold = s->ctrl & 0xf; + + if (fifo8_num_used(&s->rx_fifo) >= rx_threshold) { + s->int_fl |= UART_RX_THD; + } + + max78000_update_irq(s); +} + +static void max78000_uart_reset_hold(Object *obj, ResetType type) +{ + Max78000UartState *s = MAX78000_UART(obj); + + s->ctrl = 0; + s->status = UART_TX_EM | UART_RX_EM; + s->int_en = 0; + s->int_fl = 0; + s->osr = 0; + s->txpeek = 0; + s->pnr = UART_RTS; + s->fifo = 0; + s->dma = 0; + s->wken = 0; + s->wkfl = 0; + fifo8_reset(&s->rx_fifo); +} + +static uint64_t max78000_uart_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Max78000UartState *s = opaque; + uint64_t retvalue = 0; + switch (addr) { + case UART_CTRL: + retvalue = s->ctrl; + break; + case UART_STATUS: + retvalue = (fifo8_num_used(&s->rx_fifo) << UART_RX_LVL) | + UART_TX_EM | + (fifo8_is_empty(&s->rx_fifo) ? UART_RX_EM : 0); + break; + case UART_INT_EN: + retvalue = s->int_en; + break; + case UART_INT_FL: + retvalue = s->int_fl; + break; + case UART_CLKDIV: + retvalue = s->clkdiv; + break; + case UART_OSR: + retvalue = s->osr; + break; + case UART_TXPEEK: + if (!fifo8_is_empty(&s->rx_fifo)) { + retvalue = fifo8_peek(&s->rx_fifo); + } + break; + case UART_PNR: + retvalue = s->pnr; + break; + case UART_FIFO: + if (!fifo8_is_empty(&s->rx_fifo)) { + retvalue = fifo8_pop(&s->rx_fifo); + max78000_update_irq(s); + } + break; + case UART_DMA: + /* DMA not implemented */ + retvalue = s->dma; + break; + case UART_WKEN: + retvalue = s->wken; + break; + case UART_WKFL: + retvalue = s->wkfl; + break; + default: + qemu_log_mask(LOG_GUEST_ERROR, + "%s: Bad offset 0x%"HWADDR_PRIx"\n", __func__, addr); + break; + } + + return retvalue; +} + +static void max78000_uart_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Max78000UartState *s = opaque; + + uint32_t value = val64; + uint8_t data; + + switch (addr) { + case UART_CTRL: + if (value & UART_FLUSH_RX) { + fifo8_reset(&s->rx_fifo); + } + if (value & UART_BCLKEN) { + value = value | UART_BCLKRDY; + } + s->ctrl = value & ~(UART_FLUSH_RX | UART_FLUSH_TX); + + /* + * Software can manage UART flow control manually by setting hfc_en + * in UART_CTRL. This would require emulating uart at a lower level, + * and is currently unimplemented. + */ + + return; + case UART_STATUS: + /* UART_STATUS is read only */ + return; + case UART_INT_EN: + s->int_en = value; + return; + case UART_INT_FL: + s->int_fl = s->int_fl & ~(value); + max78000_update_irq(s); + return; + case UART_CLKDIV: + s->clkdiv = value; + return; + case UART_OSR: + s->osr = value; + return; + case UART_PNR: + s->pnr = value; + return; + case UART_FIFO: + data = value & 0xff; + /* + * XXX this blocks entire thread. Rewrite to use + * qemu_chr_fe_write and background I/O callbacks + */ + qemu_chr_fe_write_all(&s->chr, &data, 1); + + /* TX is always empty */ + s->int_fl |= UART_TX_HE; + max78000_update_irq(s); + + return; + case UART_DMA: + /* DMA not implemented */ + s->dma = value; + return; + case UART_WKEN: + s->wken = value; + return; + case UART_WKFL: + s->wkfl = value; + return; + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + } +} + +static const MemoryRegionOps max78000_uart_ops = { + .read = max78000_uart_read, + .write = max78000_uart_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const Property max78000_uart_properties[] = { + DEFINE_PROP_CHR("chardev", Max78000UartState, chr), +}; + +static const VMStateDescription max78000_uart_vmstate = { + .name = TYPE_MAX78000_UART, + .version_id = 1, + .minimum_version_id = 1, + .fields = (VMStateField[]) { + VMSTATE_UINT32(ctrl, Max78000UartState), + VMSTATE_UINT32(status, Max78000UartState), + VMSTATE_UINT32(int_en, Max78000UartState), + VMSTATE_UINT32(int_fl, Max78000UartState), + VMSTATE_UINT32(clkdiv, Max78000UartState), + VMSTATE_UINT32(osr, Max78000UartState), + VMSTATE_UINT32(txpeek, Max78000UartState), + VMSTATE_UINT32(pnr, Max78000UartState), + VMSTATE_UINT32(fifo, Max78000UartState), + VMSTATE_UINT32(dma, Max78000UartState), + VMSTATE_UINT32(wken, Max78000UartState), + VMSTATE_UINT32(wkfl, Max78000UartState), + VMSTATE_FIFO8(rx_fifo, Max78000UartState), + VMSTATE_END_OF_LIST() + } +}; + +static void max78000_uart_init(Object *obj) +{ + Max78000UartState *s = MAX78000_UART(obj); + fifo8_create(&s->rx_fifo, 8); + + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &max78000_uart_ops, s, + TYPE_MAX78000_UART, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); +} + +static void max78000_uart_realize(DeviceState *dev, Error **errp) +{ + Max78000UartState *s = MAX78000_UART(dev); + + qemu_chr_fe_set_handlers(&s->chr, max78000_uart_can_receive, + max78000_uart_receive, NULL, NULL, + s, NULL, true); +} + +static void max78000_uart_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + rc->phases.hold = max78000_uart_reset_hold; + + device_class_set_props(dc, max78000_uart_properties); + dc->realize = max78000_uart_realize; + + dc->vmsd = &max78000_uart_vmstate; +} + +static const TypeInfo max78000_uart_info = { + .name = TYPE_MAX78000_UART, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Max78000UartState), + .instance_init = max78000_uart_init, + .class_init = max78000_uart_class_init, +}; + +static void max78000_uart_register_types(void) +{ + type_register_static(&max78000_uart_info); +} + +type_init(max78000_uart_register_types) diff --git a/hw/char/meson.build b/hw/char/meson.build index 4e439da8b9..a9e1dc26c0 100644 --- a/hw/char/meson.build +++ b/hw/char/meson.build @@ -26,6 +26,7 @@ system_ss.add(when: 'CONFIG_AVR_USART', if_true: files('avr_usart.c')) system_ss.add(when: 'CONFIG_COLDFIRE', if_true: files('mcf_uart.c')) system_ss.add(when: 'CONFIG_DIGIC', if_true: files('digic-uart.c')) system_ss.add(when: 'CONFIG_EXYNOS4', if_true: files('exynos4210_uart.c')) +system_ss.add(when: 'CONFIG_MAX78000_UART', if_true: files('max78000_uart.c')) system_ss.add(when: 'CONFIG_OMAP', if_true: files('omap_uart.c')) system_ss.add(when: 'CONFIG_RASPI', if_true: files('bcm2835_aux.c')) system_ss.add(when: 'CONFIG_RENESAS_SCI', if_true: files('renesas_sci.c')) diff --git a/include/hw/char/max78000_uart.h b/include/hw/char/max78000_uart.h new file mode 100644 index 0000000000..cf90d51dbf --- /dev/null +++ b/include/hw/char/max78000_uart.h @@ -0,0 +1,78 @@ +/* + * MAX78000 UART + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef HW_MAX78000_UART_H +#define HW_MAX78000_UART_H + +#include "hw/sysbus.h" +#include "chardev/char-fe.h" +#include "qemu/fifo8.h" +#include "qom/object.h" + +#define UART_CTRL 0x0 +#define UART_STATUS 0x4 +#define UART_INT_EN 0x8 +#define UART_INT_FL 0xc +#define UART_CLKDIV 0x10 +#define UART_OSR 0x14 +#define UART_TXPEEK 0x18 +#define UART_PNR 0x1c +#define UART_FIFO 0x20 +#define UART_DMA 0x30 +#define UART_WKEN 0x34 +#define UART_WKFL 0x38 + +/* CTRL */ +#define UART_CTF_DIS (1 << 7) +#define UART_FLUSH_TX (1 << 8) +#define UART_FLUSH_RX (1 << 9) +#define UART_BCLKEN (1 << 15) +#define UART_BCLKRDY (1 << 19) + +/* STATUS */ +#define UART_RX_LVL 8 +#define UART_TX_EM (1 << 6) +#define UART_RX_FULL (1 << 5) +#define UART_RX_EM (1 << 4) + +/* PNR (Pin Control Register) */ +#define UART_CTS 1 +#define UART_RTS (1 << 1) + +/* INT_EN / INT_FL */ +#define UART_RX_THD (1 << 4) +#define UART_TX_HE (1 << 6) + +#define UART_RXBUFLEN 0x100 +#define TYPE_MAX78000_UART "max78000-uart" +OBJECT_DECLARE_SIMPLE_TYPE(Max78000UartState, MAX78000_UART) + +struct Max78000UartState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t ctrl; + uint32_t status; + uint32_t int_en; + uint32_t int_fl; + uint32_t clkdiv; + uint32_t osr; + uint32_t txpeek; + uint32_t pnr; + uint32_t fifo; + uint32_t dma; + uint32_t wken; + uint32_t wkfl; + + Fifo8 rx_fifo; + + CharBackend chr; + qemu_irq irq; +}; +#endif /* HW_STM32F2XX_USART_H */ From a670bb8a729945117fc0e8b357e02a311945cb74 Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 4 Jul 2025 18:32:33 -0400 Subject: [PATCH 2059/2760] MAX78000: Add UART to SOC This commit adds UART to max78000_soc Signed-off-by: Jackson Donaldson Reviewed-by: Peter Maydell Message-id: 20250704223239.248781-6-jcksn@duck.com Signed-off-by: Peter Maydell --- hw/arm/max78000_soc.c | 28 ++++++++++++++++++++++++---- include/hw/arm/max78000_soc.h | 3 +++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c index 0c83b08eca..2f93ab882d 100644 --- a/hw/arm/max78000_soc.c +++ b/hw/arm/max78000_soc.c @@ -18,6 +18,10 @@ #include "hw/misc/unimp.h" static const uint32_t max78000_icc_addr[] = {0x4002a000, 0x4002a800}; +static const uint32_t max78000_uart_addr[] = {0x40042000, 0x40043000, + 0x40044000}; + +static const int max78000_uart_irq[] = {14, 15, 34}; static void max78000_soc_initfn(Object *obj) { @@ -31,6 +35,12 @@ static void max78000_soc_initfn(Object *obj) object_initialize_child(obj, name, &s->icc[i], TYPE_MAX78000_ICC); } + for (i = 0; i < MAX78000_NUM_UART; i++) { + g_autofree char *name = g_strdup_printf("uart%d", i); + object_initialize_child(obj, name, &s->uart[i], + TYPE_MAX78000_UART); + } + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); } @@ -39,6 +49,7 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) MAX78000State *s = MAX78000_SOC(dev_soc); MemoryRegion *system_memory = get_system_memory(); DeviceState *dev, *armv7m; + SysBusDevice *busdev; Error *err = NULL; int i; @@ -89,6 +100,19 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, max78000_icc_addr[i]); } + for (i = 0; i < MAX78000_NUM_UART; i++) { + dev = DEVICE(&(s->uart[i])); + qdev_prop_set_chr(dev, "chardev", serial_hd(i)); + if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) { + return; + } + + busdev = SYS_BUS_DEVICE(dev); + sysbus_mmio_map(busdev, 0, max78000_uart_addr[i]); + sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, + max78000_uart_irq[i])); + } + create_unimplemented_device("globalControl", 0x40000000, 0x400); create_unimplemented_device("systemInterface", 0x40000400, 0x400); create_unimplemented_device("functionControl", 0x40000800, 0x400); @@ -127,10 +151,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("oneWireMaster", 0x4003d000, 0x1000); create_unimplemented_device("semaphore", 0x4003e000, 0x1000); - create_unimplemented_device("uart0", 0x40042000, 0x1000); - create_unimplemented_device("uart1", 0x40043000, 0x1000); - create_unimplemented_device("uart2", 0x40044000, 0x1000); - create_unimplemented_device("spi1", 0x40046000, 0x2000); create_unimplemented_device("trng", 0x4004d000, 0x1000); create_unimplemented_device("i2s", 0x40060000, 0x1000); diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h index 27b506d6ee..57894f0035 100644 --- a/include/hw/arm/max78000_soc.h +++ b/include/hw/arm/max78000_soc.h @@ -12,6 +12,7 @@ #include "hw/or-irq.h" #include "hw/arm/armv7m.h" #include "hw/misc/max78000_icc.h" +#include "hw/char/max78000_uart.h" #include "qom/object.h" #define TYPE_MAX78000_SOC "max78000-soc" @@ -24,6 +25,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(MAX78000State, MAX78000_SOC) /* The MAX78k has 2 instruction caches; only icc0 matters, icc1 is for RISC */ #define MAX78000_NUM_ICC 2 +#define MAX78000_NUM_UART 3 struct MAX78000State { SysBusDevice parent_obj; @@ -34,6 +36,7 @@ struct MAX78000State { MemoryRegion flash; Max78000IccState icc[MAX78000_NUM_ICC]; + Max78000UartState uart[MAX78000_NUM_UART]; Clock *sysclk; }; From a017f53e093a9018e33fb33bbdaa322c2de3dbe7 Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 4 Jul 2025 18:32:34 -0400 Subject: [PATCH 2060/2760] MAX78000: GCR Implementation This commit implements the Global Control Register for the MAX78000 Signed-off-by: Jackson Donaldson Reviewed-by: Peter Maydell Message-id: 20250704223239.248781-7-jcksn@duck.com Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/misc/Kconfig | 3 + hw/misc/max78000_gcr.c | 339 +++++++++++++++++++++++++++++++++ hw/misc/meson.build | 1 + include/hw/misc/max78000_gcr.h | 129 +++++++++++++ 5 files changed, 473 insertions(+) create mode 100644 hw/misc/max78000_gcr.c create mode 100644 include/hw/misc/max78000_gcr.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 7e3610dca8..a96349ee11 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -368,6 +368,7 @@ config MAX78000_SOC select ARM_V7M select MAX78000_ICC select MAX78000_UART + select MAX78000_GCR config RASPI bool diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index 781bcf74cc..fde2266b8f 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -47,6 +47,9 @@ config A9SCU config ARM11SCU bool +config MAX78000_GCR + bool + config MAX78000_ICC bool diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c new file mode 100644 index 0000000000..8c282f3916 --- /dev/null +++ b/hw/misc/max78000_gcr.c @@ -0,0 +1,339 @@ +/* + * MAX78000 Global Control Registers + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/irq.h" +#include "system/runstate.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/char/max78000_uart.h" +#include "hw/misc/max78000_gcr.h" + + +static void max78000_gcr_reset_hold(Object *obj, ResetType type) +{ + DeviceState *dev = DEVICE(obj); + Max78000GcrState *s = MAX78000_GCR(dev); + s->sysctrl = 0x21002; + s->rst0 = 0; + /* All clocks are always ready */ + s->clkctrl = 0x3e140008; + s->pm = 0x3f000; + s->pclkdiv = 0; + s->pclkdis0 = 0xffffffff; + s->memctrl = 0x5; + s->memz = 0; + s->sysst = 0; + s->rst1 = 0; + s->pckdis1 = 0xffffffff; + s->eventen = 0; + s->revision = 0xa1; + s->sysie = 0; + s->eccerr = 0; + s->ecced = 0; + s->eccie = 0; + s->eccaddr = 0; +} + +static uint64_t max78000_gcr_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Max78000GcrState *s = opaque; + + switch (addr) { + case SYSCTRL: + return s->sysctrl; + + case RST0: + return s->rst0; + + case CLKCTRL: + return s->clkctrl; + + case PM: + return s->pm; + + case PCLKDIV: + return s->pclkdiv; + + case PCLKDIS0: + return s->pclkdis0; + + case MEMCTRL: + return s->memctrl; + + case MEMZ: + return s->memz; + + case SYSST: + return s->sysst; + + case RST1: + return s->rst1; + + case PCKDIS1: + return s->pckdis1; + + case EVENTEN: + return s->eventen; + + case REVISION: + return s->revision; + + case SYSIE: + return s->sysie; + + case ECCERR: + return s->eccerr; + + case ECCED: + return s->ecced; + + case ECCIE: + return s->eccie; + + case ECCADDR: + return s->eccaddr; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + return 0; + + } +} + +static void max78000_gcr_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Max78000GcrState *s = opaque; + uint32_t val = val64; + uint8_t zero[0xc000] = {0}; + switch (addr) { + case SYSCTRL: + /* Checksum calculations always pass immediately */ + s->sysctrl = (val & 0x30000) | 0x1002; + break; + + case RST0: + if (val & SYSTEM_RESET) { + qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); + } + if (val & PERIPHERAL_RESET) { + /* + * Peripheral reset resets all peripherals. The CPU + * retains its state. The GPIO, watchdog timers, AoD, + * RAM retention, and general control registers (GCR), + * including the clock configuration, are unaffected. + */ + val = UART2_RESET | UART1_RESET | UART0_RESET | + ADC_RESET | CNN_RESET | TRNG_RESET | + RTC_RESET | I2C0_RESET | SPI1_RESET | + TMR3_RESET | TMR2_RESET | TMR1_RESET | + TMR0_RESET | WDT0_RESET | DMA_RESET; + } + if (val & SOFT_RESET) { + /* Soft reset also resets GPIO */ + val = UART2_RESET | UART1_RESET | UART0_RESET | + ADC_RESET | CNN_RESET | TRNG_RESET | + RTC_RESET | I2C0_RESET | SPI1_RESET | + TMR3_RESET | TMR2_RESET | TMR1_RESET | + TMR0_RESET | GPIO1_RESET | GPIO0_RESET | + DMA_RESET; + } + if (val & UART2_RESET) { + device_cold_reset(s->uart2); + } + if (val & UART1_RESET) { + device_cold_reset(s->uart1); + } + if (val & UART0_RESET) { + device_cold_reset(s->uart0); + } + /* TODO: As other devices are implemented, add them here */ + break; + + case CLKCTRL: + s->clkctrl = val | SYSCLK_RDY; + break; + + case PM: + s->pm = val; + break; + + case PCLKDIV: + s->pclkdiv = val; + break; + + case PCLKDIS0: + s->pclkdis0 = val; + break; + + case MEMCTRL: + s->memctrl = val; + break; + + case MEMZ: + if (val & ram0) { + address_space_write(&s->sram_as, SYSRAM0_START, + MEMTXATTRS_UNSPECIFIED, zero, 0x8000); + } + if (val & ram1) { + address_space_write(&s->sram_as, SYSRAM1_START, + MEMTXATTRS_UNSPECIFIED, zero, 0x8000); + } + if (val & ram2) { + address_space_write(&s->sram_as, SYSRAM2_START, + MEMTXATTRS_UNSPECIFIED, zero, 0xC000); + } + if (val & ram3) { + address_space_write(&s->sram_as, SYSRAM3_START, + MEMTXATTRS_UNSPECIFIED, zero, 0x4000); + } + break; + + case SYSST: + s->sysst = val; + break; + + case RST1: + /* TODO: As other devices are implemented, add them here */ + s->rst1 = val; + break; + + case PCKDIS1: + s->pckdis1 = val; + break; + + case EVENTEN: + s->eventen = val; + break; + + case REVISION: + s->revision = val; + break; + + case SYSIE: + s->sysie = val; + break; + + case ECCERR: + s->eccerr = val; + break; + + case ECCED: + s->ecced = val; + break; + + case ECCIE: + s->eccie = val; + break; + + case ECCADDR: + s->eccaddr = val; + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" HWADDR_PRIx "\n", + __func__, addr); + break; + + } +} + +static const Property max78000_gcr_properties[] = { + DEFINE_PROP_LINK("sram", Max78000GcrState, sram, + TYPE_MEMORY_REGION, MemoryRegion*), + DEFINE_PROP_LINK("uart0", Max78000GcrState, uart0, + TYPE_MAX78000_UART, DeviceState*), + DEFINE_PROP_LINK("uart1", Max78000GcrState, uart1, + TYPE_MAX78000_UART, DeviceState*), + DEFINE_PROP_LINK("uart2", Max78000GcrState, uart2, + TYPE_MAX78000_UART, DeviceState*), +}; + +static const MemoryRegionOps max78000_gcr_ops = { + .read = max78000_gcr_read, + .write = max78000_gcr_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_max78000_gcr = { + .name = TYPE_MAX78000_GCR, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(sysctrl, Max78000GcrState), + VMSTATE_UINT32(rst0, Max78000GcrState), + VMSTATE_UINT32(clkctrl, Max78000GcrState), + VMSTATE_UINT32(pm, Max78000GcrState), + VMSTATE_UINT32(pclkdiv, Max78000GcrState), + VMSTATE_UINT32(pclkdis0, Max78000GcrState), + VMSTATE_UINT32(memctrl, Max78000GcrState), + VMSTATE_UINT32(memz, Max78000GcrState), + VMSTATE_UINT32(sysst, Max78000GcrState), + VMSTATE_UINT32(rst1, Max78000GcrState), + VMSTATE_UINT32(pckdis1, Max78000GcrState), + VMSTATE_UINT32(eventen, Max78000GcrState), + VMSTATE_UINT32(revision, Max78000GcrState), + VMSTATE_UINT32(sysie, Max78000GcrState), + VMSTATE_UINT32(eccerr, Max78000GcrState), + VMSTATE_UINT32(ecced, Max78000GcrState), + VMSTATE_UINT32(eccie, Max78000GcrState), + VMSTATE_UINT32(eccaddr, Max78000GcrState), + VMSTATE_END_OF_LIST() + } +}; + +static void max78000_gcr_init(Object *obj) +{ + Max78000GcrState *s = MAX78000_GCR(obj); + + memory_region_init_io(&s->mmio, obj, &max78000_gcr_ops, s, + TYPE_MAX78000_GCR, 0x400); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + +} + +static void max78000_gcr_realize(DeviceState *dev, Error **errp) +{ + Max78000GcrState *s = MAX78000_GCR(dev); + + address_space_init(&s->sram_as, s->sram, "sram"); +} + +static void max78000_gcr_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + ResettableClass *rc = RESETTABLE_CLASS(klass); + + device_class_set_props(dc, max78000_gcr_properties); + + dc->realize = max78000_gcr_realize; + dc->vmsd = &vmstate_max78000_gcr; + rc->phases.hold = max78000_gcr_reset_hold; +} + +static const TypeInfo max78000_gcr_info = { + .name = TYPE_MAX78000_GCR, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Max78000GcrState), + .instance_init = max78000_gcr_init, + .class_init = max78000_gcr_class_init, +}; + +static void max78000_gcr_register_types(void) +{ + type_register_static(&max78000_gcr_info); +} + +type_init(max78000_gcr_register_types) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index a21a994ff8..283d06dad4 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -70,6 +70,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files( 'imx_ccm.c', 'imx_rngc.c', )) +system_ss.add(when: 'CONFIG_MAX78000_GCR', if_true: files('max78000_gcr.c')) system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c')) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files( 'npcm_clk.c', diff --git a/include/hw/misc/max78000_gcr.h b/include/hw/misc/max78000_gcr.h new file mode 100644 index 0000000000..f04c8a3ee7 --- /dev/null +++ b/include/hw/misc/max78000_gcr.h @@ -0,0 +1,129 @@ +/* + * MAX78000 Global Control Register + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_MAX78000_GCR_H +#define HW_MAX78000_GCR_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_MAX78000_GCR "max78000-gcr" +OBJECT_DECLARE_SIMPLE_TYPE(Max78000GcrState, MAX78000_GCR) + +#define SYSCTRL 0x0 +#define RST0 0x4 +#define CLKCTRL 0x8 +#define PM 0xc +#define PCLKDIV 0x18 +#define PCLKDIS0 0x24 +#define MEMCTRL 0x28 +#define MEMZ 0x2c +#define SYSST 0x40 +#define RST1 0x44 +#define PCKDIS1 0x48 +#define EVENTEN 0x4c +#define REVISION 0x50 +#define SYSIE 0x54 +#define ECCERR 0x64 +#define ECCED 0x68 +#define ECCIE 0x6c +#define ECCADDR 0x70 + +/* RST0 */ +#define SYSTEM_RESET (1 << 31) +#define PERIPHERAL_RESET (1 << 30) +#define SOFT_RESET (1 << 29) +#define UART2_RESET (1 << 28) + +#define ADC_RESET (1 << 26) +#define CNN_RESET (1 << 25) +#define TRNG_RESET (1 << 24) + +#define RTC_RESET (1 << 17) +#define I2C0_RESET (1 << 16) + +#define SPI1_RESET (1 << 13) +#define UART1_RESET (1 << 12) +#define UART0_RESET (1 << 11) + +#define TMR3_RESET (1 << 8) +#define TMR2_RESET (1 << 7) +#define TMR1_RESET (1 << 6) +#define TMR0_RESET (1 << 5) + +#define GPIO1_RESET (1 << 3) +#define GPIO0_RESET (1 << 2) +#define WDT0_RESET (1 << 1) +#define DMA_RESET (1 << 0) + +/* CLKCTRL */ +#define SYSCLK_RDY (1 << 13) + +/* MEMZ */ +#define ram0 (1 << 0) +#define ram1 (1 << 1) +#define ram2 (1 << 2) +#define ram3 (1 << 3) + +/* RST1 */ +#define CPU1_RESET (1 << 31) + +#define SIMO_RESET (1 << 25) +#define DVS_RESET (1 << 24) + +#define I2C2_RESET (1 << 20) +#define I2S_RESET (1 << 19) + +#define SMPHR_RESET (1 << 16) + +#define SPI0_RESET (1 << 11) +#define AES_RESET (1 << 10) +#define CRC_RESET (1 << 9) + +#define PT_RESET (1 << 1) +#define I2C1_RESET (1 << 0) + + +#define SYSRAM0_START 0x20000000 +#define SYSRAM1_START 0x20008000 +#define SYSRAM2_START 0x20010000 +#define SYSRAM3_START 0x2001C000 + +struct Max78000GcrState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t sysctrl; + uint32_t rst0; + uint32_t clkctrl; + uint32_t pm; + uint32_t pclkdiv; + uint32_t pclkdis0; + uint32_t memctrl; + uint32_t memz; + uint32_t sysst; + uint32_t rst1; + uint32_t pckdis1; + uint32_t eventen; + uint32_t revision; + uint32_t sysie; + uint32_t eccerr; + uint32_t ecced; + uint32_t eccie; + uint32_t eccaddr; + + MemoryRegion *sram; + AddressSpace sram_as; + + DeviceState *uart0; + DeviceState *uart1; + DeviceState *uart2; + +}; + +#endif From 035a38fa97d07b80ff5b9fa6c3da43528770899d Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 4 Jul 2025 18:32:35 -0400 Subject: [PATCH 2061/2760] MAX78000: Add GCR to SOC This commit adds the Global Control Register to max78000_soc Signed-off-by: Jackson Donaldson Reviewed-by: Peter Maydell Message-id: 20250704223239.248781-8-jcksn@duck.com Signed-off-by: Peter Maydell --- hw/arm/max78000_soc.c | 18 ++++++++++++++++-- include/hw/arm/max78000_soc.h | 2 ++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c index 2f93ab882d..45c6088312 100644 --- a/hw/arm/max78000_soc.c +++ b/hw/arm/max78000_soc.c @@ -30,6 +30,8 @@ static void max78000_soc_initfn(Object *obj) object_initialize_child(obj, "armv7m", &s->armv7m, TYPE_ARMV7M); + object_initialize_child(obj, "gcr", &s->gcr, TYPE_MAX78000_GCR); + for (i = 0; i < MAX78000_NUM_ICC; i++) { g_autofree char *name = g_strdup_printf("icc%d", i); object_initialize_child(obj, name, &s->icc[i], TYPE_MAX78000_ICC); @@ -48,7 +50,7 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) { MAX78000State *s = MAX78000_SOC(dev_soc); MemoryRegion *system_memory = get_system_memory(); - DeviceState *dev, *armv7m; + DeviceState *dev, *gcrdev, *armv7m; SysBusDevice *busdev; Error *err = NULL; int i; @@ -69,6 +71,11 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) memory_region_init_ram(&s->sram, NULL, "MAX78000.sram", SRAM_SIZE, &err); + + gcrdev = DEVICE(&s->gcr); + object_property_set_link(OBJECT(gcrdev), "sram", OBJECT(&s->sram), + &err); + if (err != NULL) { error_propagate(errp, err); return; @@ -101,19 +108,26 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) } for (i = 0; i < MAX78000_NUM_UART; i++) { + g_autofree char *link = g_strdup_printf("uart%d", i); dev = DEVICE(&(s->uart[i])); qdev_prop_set_chr(dev, "chardev", serial_hd(i)); if (!sysbus_realize(SYS_BUS_DEVICE(&s->uart[i]), errp)) { return; } + object_property_set_link(OBJECT(gcrdev), link, OBJECT(dev), + &err); + busdev = SYS_BUS_DEVICE(dev); sysbus_mmio_map(busdev, 0, max78000_uart_addr[i]); sysbus_connect_irq(busdev, 0, qdev_get_gpio_in(armv7m, max78000_uart_irq[i])); } - create_unimplemented_device("globalControl", 0x40000000, 0x400); + dev = DEVICE(&s->gcr); + sysbus_realize(SYS_BUS_DEVICE(dev), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40000000); + create_unimplemented_device("systemInterface", 0x40000400, 0x400); create_unimplemented_device("functionControl", 0x40000800, 0x400); create_unimplemented_device("watchdogTimer0", 0x40003000, 0x400); diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h index 57894f0035..919aca0855 100644 --- a/include/hw/arm/max78000_soc.h +++ b/include/hw/arm/max78000_soc.h @@ -11,6 +11,7 @@ #include "hw/or-irq.h" #include "hw/arm/armv7m.h" +#include "hw/misc/max78000_gcr.h" #include "hw/misc/max78000_icc.h" #include "hw/char/max78000_uart.h" #include "qom/object.h" @@ -35,6 +36,7 @@ struct MAX78000State { MemoryRegion sram; MemoryRegion flash; + Max78000GcrState gcr; Max78000IccState icc[MAX78000_NUM_ICC]; Max78000UartState uart[MAX78000_NUM_UART]; From 069852d159a18219eb19281b146d612849a84e03 Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 4 Jul 2025 18:32:36 -0400 Subject: [PATCH 2062/2760] MAX78000: TRNG Implementation This commit implements the True Random Number Generator for the MAX78000 Signed-off-by: Jackson Donaldson Reviewed-by: Peter Maydell Message-id: 20250704223239.248781-9-jcksn@duck.com Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/misc/Kconfig | 3 + hw/misc/max78000_gcr.c | 6 ++ hw/misc/max78000_trng.c | 139 ++++++++++++++++++++++++++++++++ hw/misc/meson.build | 1 + include/hw/misc/max78000_gcr.h | 1 + include/hw/misc/max78000_trng.h | 35 ++++++++ 7 files changed, 186 insertions(+) create mode 100644 hw/misc/max78000_trng.c create mode 100644 include/hw/misc/max78000_trng.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index a96349ee11..c7aae4c9e7 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -369,6 +369,7 @@ config MAX78000_SOC select MAX78000_ICC select MAX78000_UART select MAX78000_GCR + select MAX78000_TRNG config RASPI bool diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index fde2266b8f..dd6a6e54da 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -53,6 +53,9 @@ config MAX78000_GCR config MAX78000_ICC bool +config MAX78000_TRNG + bool + config MOS6522 bool diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c index 8c282f3916..5916ee615a 100644 --- a/hw/misc/max78000_gcr.c +++ b/hw/misc/max78000_gcr.c @@ -14,6 +14,7 @@ #include "migration/vmstate.h" #include "hw/qdev-properties.h" #include "hw/char/max78000_uart.h" +#include "hw/misc/max78000_trng.h" #include "hw/misc/max78000_gcr.h" @@ -157,6 +158,9 @@ static void max78000_gcr_write(void *opaque, hwaddr addr, if (val & UART0_RESET) { device_cold_reset(s->uart0); } + if (val & TRNG_RESET) { + device_cold_reset(s->trng); + } /* TODO: As other devices are implemented, add them here */ break; @@ -257,6 +261,8 @@ static const Property max78000_gcr_properties[] = { TYPE_MAX78000_UART, DeviceState*), DEFINE_PROP_LINK("uart2", Max78000GcrState, uart2, TYPE_MAX78000_UART, DeviceState*), + DEFINE_PROP_LINK("trng", Max78000GcrState, trng, + TYPE_MAX78000_TRNG, DeviceState*), }; static const MemoryRegionOps max78000_gcr_ops = { diff --git a/hw/misc/max78000_trng.c b/hw/misc/max78000_trng.c new file mode 100644 index 0000000000..ecdaef53b6 --- /dev/null +++ b/hw/misc/max78000_trng.c @@ -0,0 +1,139 @@ +/* + * MAX78000 True Random Number Generator + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "hw/misc/max78000_trng.h" +#include "qemu/guest-random.h" + +static uint64_t max78000_trng_read(void *opaque, hwaddr addr, + unsigned int size) +{ + uint32_t data; + + Max78000TrngState *s = opaque; + switch (addr) { + case CTRL: + return s->ctrl; + + case STATUS: + return 1; + + case DATA: + /* + * When interrupts are enabled, reading random data should cause a + * new interrupt to be generated; since there's always a random number + * available, we could qemu_set_irq(s->irq, s->ctrl & RND_IE). Because + * of how trng_write is set up, this is always a noop, so don't + */ + qemu_guest_getrandom_nofail(&data, sizeof(data)); + return data; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + } + return 0; +} + +static void max78000_trng_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Max78000TrngState *s = opaque; + uint32_t val = val64; + switch (addr) { + case CTRL: + /* TODO: implement AES keygen */ + s->ctrl = val; + + /* + * This device models random number generation as taking 0 time. + * A new random number is always available, so the condition for the + * RND interrupt is always fulfilled; we can just set irq to 1. + */ + if (val & RND_IE) { + qemu_set_irq(s->irq, 1); + } else{ + qemu_set_irq(s->irq, 0); + } + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + } +} + +static void max78000_trng_reset_hold(Object *obj, ResetType type) +{ + Max78000TrngState *s = MAX78000_TRNG(obj); + s->ctrl = 0; + s->status = 0; + s->data = 0; +} + +static const MemoryRegionOps max78000_trng_ops = { + .read = max78000_trng_read, + .write = max78000_trng_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription max78000_trng_vmstate = { + .name = TYPE_MAX78000_TRNG, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(ctrl, Max78000TrngState), + VMSTATE_UINT32(status, Max78000TrngState), + VMSTATE_UINT32(data, Max78000TrngState), + VMSTATE_END_OF_LIST() + } +}; + +static void max78000_trng_init(Object *obj) +{ + Max78000TrngState *s = MAX78000_TRNG(obj); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &max78000_trng_ops, s, + TYPE_MAX78000_TRNG, 0x1000); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + +} + +static void max78000_trng_class_init(ObjectClass *klass, const void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + rc->phases.hold = max78000_trng_reset_hold; + dc->vmsd = &max78000_trng_vmstate; + +} + +static const TypeInfo max78000_trng_info = { + .name = TYPE_MAX78000_TRNG, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Max78000TrngState), + .instance_init = max78000_trng_init, + .class_init = max78000_trng_class_init, +}; + +static void max78000_trng_register_types(void) +{ + type_register_static(&max78000_trng_info); +} + +type_init(max78000_trng_register_types) diff --git a/hw/misc/meson.build b/hw/misc/meson.build index 283d06dad4..c7c57d924b 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -72,6 +72,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files( )) system_ss.add(when: 'CONFIG_MAX78000_GCR', if_true: files('max78000_gcr.c')) system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c')) +system_ss.add(when: 'CONFIG_MAX78000_TRNG', if_true: files('max78000_trng.c')) system_ss.add(when: 'CONFIG_NPCM7XX', if_true: files( 'npcm_clk.c', 'npcm_gcr.c', diff --git a/include/hw/misc/max78000_gcr.h b/include/hw/misc/max78000_gcr.h index f04c8a3ee7..23ddf0885b 100644 --- a/include/hw/misc/max78000_gcr.h +++ b/include/hw/misc/max78000_gcr.h @@ -123,6 +123,7 @@ struct Max78000GcrState { DeviceState *uart0; DeviceState *uart1; DeviceState *uart2; + DeviceState *trng; }; diff --git a/include/hw/misc/max78000_trng.h b/include/hw/misc/max78000_trng.h new file mode 100644 index 0000000000..c5a8129b6a --- /dev/null +++ b/include/hw/misc/max78000_trng.h @@ -0,0 +1,35 @@ +/* + * MAX78000 True Random Number Generator + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_MAX78000_TRNG_H +#define HW_MAX78000_TRNG_H + +#include "hw/sysbus.h" +#include "qom/object.h" + +#define TYPE_MAX78000_TRNG "max78000-trng" +OBJECT_DECLARE_SIMPLE_TYPE(Max78000TrngState, MAX78000_TRNG) + +#define CTRL 0 +#define STATUS 4 +#define DATA 8 + +#define RND_IE (1 << 1) + +struct Max78000TrngState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t ctrl; + uint32_t status; + uint32_t data; + + qemu_irq irq; +}; + +#endif From 5adeb160322ff827f3e81f38e9481fb4896670e8 Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 4 Jul 2025 18:32:37 -0400 Subject: [PATCH 2063/2760] MAX78000: Add TRNG to SOC This commit adds TRNG to max78000_soc Signed-off-by: Jackson Donaldson Message-id: 20250704223239.248781-10-jcksn@duck.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/max78000_soc.c | 10 +++++++++- include/hw/arm/max78000_soc.h | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c index 45c6088312..3f2069fb03 100644 --- a/hw/arm/max78000_soc.c +++ b/hw/arm/max78000_soc.c @@ -43,6 +43,8 @@ static void max78000_soc_initfn(Object *obj) TYPE_MAX78000_UART); } + object_initialize_child(obj, "trng", &s->trng, TYPE_MAX78000_TRNG); + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); } @@ -124,6 +126,13 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) max78000_uart_irq[i])); } + dev = DEVICE(&s->trng); + sysbus_realize(SYS_BUS_DEVICE(dev), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x4004d000); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(armv7m, 4)); + + object_property_set_link(OBJECT(gcrdev), "trng", OBJECT(dev), &err); + dev = DEVICE(&s->gcr); sysbus_realize(SYS_BUS_DEVICE(dev), errp); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40000000); @@ -166,7 +175,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("semaphore", 0x4003e000, 0x1000); create_unimplemented_device("spi1", 0x40046000, 0x2000); - create_unimplemented_device("trng", 0x4004d000, 0x1000); create_unimplemented_device("i2s", 0x40060000, 0x1000); create_unimplemented_device("lowPowerControl", 0x40080000, 0x400); create_unimplemented_device("gpio2", 0x40080400, 0x200); diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h index 919aca0855..528598cfcb 100644 --- a/include/hw/arm/max78000_soc.h +++ b/include/hw/arm/max78000_soc.h @@ -14,6 +14,7 @@ #include "hw/misc/max78000_gcr.h" #include "hw/misc/max78000_icc.h" #include "hw/char/max78000_uart.h" +#include "hw/misc/max78000_trng.h" #include "qom/object.h" #define TYPE_MAX78000_SOC "max78000-soc" @@ -39,6 +40,7 @@ struct MAX78000State { Max78000GcrState gcr; Max78000IccState icc[MAX78000_NUM_ICC]; Max78000UartState uart[MAX78000_NUM_UART]; + Max78000TrngState trng; Clock *sysclk; }; From 33dfff7e3405e9c7e877556d5f7050da4af0304f Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 4 Jul 2025 18:32:38 -0400 Subject: [PATCH 2064/2760] MAX78000: AES implementation This commit implements AES for the MAX78000 Signed-off-by: Jackson Donaldson Reviewed-by: Peter Maydell Message-id: 20250704223239.248781-11-jcksn@duck.com Signed-off-by: Peter Maydell --- hw/arm/Kconfig | 1 + hw/misc/Kconfig | 3 + hw/misc/max78000_aes.c | 223 +++++++++++++++++++++++++++++++++ hw/misc/max78000_gcr.c | 6 + hw/misc/meson.build | 1 + include/hw/misc/max78000_aes.h | 68 ++++++++++ include/hw/misc/max78000_gcr.h | 1 + 7 files changed, 303 insertions(+) create mode 100644 hw/misc/max78000_aes.c create mode 100644 include/hw/misc/max78000_aes.h diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index c7aae4c9e7..1634e26fcc 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -370,6 +370,7 @@ config MAX78000_SOC select MAX78000_UART select MAX78000_GCR select MAX78000_TRNG + select MAX78000_AES config RASPI bool diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index dd6a6e54da..c27285b47a 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -47,6 +47,9 @@ config A9SCU config ARM11SCU bool +config MAX78000_AES + bool + config MAX78000_GCR bool diff --git a/hw/misc/max78000_aes.c b/hw/misc/max78000_aes.c new file mode 100644 index 0000000000..0bfb2f02b5 --- /dev/null +++ b/hw/misc/max78000_aes.c @@ -0,0 +1,223 @@ +/* + * MAX78000 AES + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/log.h" +#include "trace.h" +#include "hw/irq.h" +#include "migration/vmstate.h" +#include "hw/misc/max78000_aes.h" +#include "crypto/aes.h" + +static void max78000_aes_set_status(Max78000AesState *s) +{ + s->status = 0; + if (s->result_index >= 16) { + s->status |= OUTPUT_FULL; + } + if (s->result_index == 0) { + s->status |= OUTPUT_EMPTY; + } + if (s->data_index >= 16) { + s->status |= INPUT_FULL; + } + if (s->data_index == 0) { + s->status |= INPUT_EMPTY; + } +} + +static uint64_t max78000_aes_read(void *opaque, hwaddr addr, + unsigned int size) +{ + Max78000AesState *s = opaque; + switch (addr) { + case CTRL: + return s->ctrl; + + case STATUS: + return s->status; + + case INTFL: + return s->intfl; + + case INTEN: + return s->inten; + + case FIFO: + if (s->result_index >= 4) { + s->intfl &= ~DONE; + s->result_index -= 4; + max78000_aes_set_status(s); + return ldl_be_p(&s->result[s->result_index]); + } else{ + return 0; + } + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + + } + return 0; +} + +static void max78000_aes_do_crypto(Max78000AesState *s) +{ + int keylen = 256; + uint8_t *keydata = s->key; + if ((s->ctrl & KEY_SIZE) == 0) { + keylen = 128; + keydata += 16; + } else if ((s->ctrl & KEY_SIZE) == 1 << 6) { + keylen = 192; + keydata += 8; + } + + AES_KEY key; + if ((s->ctrl & TYPE) == 0) { + AES_set_encrypt_key(keydata, keylen, &key); + AES_set_decrypt_key(keydata, keylen, &s->internal_key); + AES_encrypt(s->data, s->result, &key); + s->result_index = 16; + } else if ((s->ctrl & TYPE) == 1 << 8) { + AES_set_decrypt_key(keydata, keylen, &key); + AES_set_decrypt_key(keydata, keylen, &s->internal_key); + AES_decrypt(s->data, s->result, &key); + s->result_index = 16; + } else{ + AES_decrypt(s->data, s->result, &s->internal_key); + s->result_index = 16; + } + s->intfl |= DONE; +} + +static void max78000_aes_write(void *opaque, hwaddr addr, + uint64_t val64, unsigned int size) +{ + Max78000AesState *s = opaque; + uint32_t val = val64; + switch (addr) { + case CTRL: + if (val & OUTPUT_FLUSH) { + s->result_index = 0; + val &= ~OUTPUT_FLUSH; + } + if (val & INPUT_FLUSH) { + s->data_index = 0; + val &= ~INPUT_FLUSH; + } + if (val & START) { + max78000_aes_do_crypto(s); + } + + /* Hardware appears to stay enabled even if 0 written */ + s->ctrl = val | (s->ctrl & AES_EN); + break; + + case FIFO: + assert(s->data_index <= 12); + stl_be_p(&s->data[12 - s->data_index], val); + s->data_index += 4; + if (s->data_index >= 16) { + s->data_index = 0; + max78000_aes_do_crypto(s); + } + break; + + case KEY_BASE ... KEY_END - 4: + stl_be_p(&s->key[(KEY_END - KEY_BASE - 4) - (addr - KEY_BASE)], val); + break; + + default: + qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad offset 0x%" + HWADDR_PRIx "\n", __func__, addr); + break; + + } + max78000_aes_set_status(s); +} + +static void max78000_aes_reset_hold(Object *obj, ResetType type) +{ + Max78000AesState *s = MAX78000_AES(obj); + s->ctrl = 0; + s->status = 0; + s->intfl = 0; + s->inten = 0; + + s->data_index = 0; + s->result_index = 0; + + memset(s->data, 0, sizeof(s->data)); + memset(s->key, 0, sizeof(s->key)); + memset(s->result, 0, sizeof(s->result)); + memset(&s->internal_key, 0, sizeof(s->internal_key)); +} + +static const MemoryRegionOps max78000_aes_ops = { + .read = max78000_aes_read, + .write = max78000_aes_write, + .endianness = DEVICE_LITTLE_ENDIAN, + .valid.min_access_size = 4, + .valid.max_access_size = 4, +}; + +static const VMStateDescription vmstate_max78000_aes = { + .name = TYPE_MAX78000_AES, + .version_id = 1, + .minimum_version_id = 1, + .fields = (const VMStateField[]) { + VMSTATE_UINT32(ctrl, Max78000AesState), + VMSTATE_UINT32(status, Max78000AesState), + VMSTATE_UINT32(intfl, Max78000AesState), + VMSTATE_UINT32(inten, Max78000AesState), + VMSTATE_UINT8_ARRAY(data, Max78000AesState, 16), + VMSTATE_UINT8_ARRAY(key, Max78000AesState, 32), + VMSTATE_UINT8_ARRAY(result, Max78000AesState, 16), + VMSTATE_UINT32_ARRAY(internal_key.rd_key, Max78000AesState, 60), + VMSTATE_INT32(internal_key.rounds, Max78000AesState), + VMSTATE_END_OF_LIST() + } +}; + +static void max78000_aes_init(Object *obj) +{ + Max78000AesState *s = MAX78000_AES(obj); + sysbus_init_irq(SYS_BUS_DEVICE(obj), &s->irq); + + memory_region_init_io(&s->mmio, obj, &max78000_aes_ops, s, + TYPE_MAX78000_AES, 0xc00); + sysbus_init_mmio(SYS_BUS_DEVICE(obj), &s->mmio); + +} + +static void max78000_aes_class_init(ObjectClass *klass, const void *data) +{ + ResettableClass *rc = RESETTABLE_CLASS(klass); + DeviceClass *dc = DEVICE_CLASS(klass); + + rc->phases.hold = max78000_aes_reset_hold; + dc->vmsd = &vmstate_max78000_aes; + +} + +static const TypeInfo max78000_aes_info = { + .name = TYPE_MAX78000_AES, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(Max78000AesState), + .instance_init = max78000_aes_init, + .class_init = max78000_aes_class_init, +}; + +static void max78000_aes_register_types(void) +{ + type_register_static(&max78000_aes_info); +} + +type_init(max78000_aes_register_types) diff --git a/hw/misc/max78000_gcr.c b/hw/misc/max78000_gcr.c index 5916ee615a..fbbc92cca3 100644 --- a/hw/misc/max78000_gcr.c +++ b/hw/misc/max78000_gcr.c @@ -15,6 +15,7 @@ #include "hw/qdev-properties.h" #include "hw/char/max78000_uart.h" #include "hw/misc/max78000_trng.h" +#include "hw/misc/max78000_aes.h" #include "hw/misc/max78000_gcr.h" @@ -161,6 +162,9 @@ static void max78000_gcr_write(void *opaque, hwaddr addr, if (val & TRNG_RESET) { device_cold_reset(s->trng); } + if (val & AES_RESET) { + device_cold_reset(s->aes); + } /* TODO: As other devices are implemented, add them here */ break; @@ -263,6 +267,8 @@ static const Property max78000_gcr_properties[] = { TYPE_MAX78000_UART, DeviceState*), DEFINE_PROP_LINK("trng", Max78000GcrState, trng, TYPE_MAX78000_TRNG, DeviceState*), + DEFINE_PROP_LINK("aes", Max78000GcrState, aes, + TYPE_MAX78000_AES, DeviceState*), }; static const MemoryRegionOps max78000_gcr_ops = { diff --git a/hw/misc/meson.build b/hw/misc/meson.build index c7c57d924b..b1d8d8e5d2 100644 --- a/hw/misc/meson.build +++ b/hw/misc/meson.build @@ -70,6 +70,7 @@ system_ss.add(when: 'CONFIG_IMX', if_true: files( 'imx_ccm.c', 'imx_rngc.c', )) +system_ss.add(when: 'CONFIG_MAX78000_AES', if_true: files('max78000_aes.c')) system_ss.add(when: 'CONFIG_MAX78000_GCR', if_true: files('max78000_gcr.c')) system_ss.add(when: 'CONFIG_MAX78000_ICC', if_true: files('max78000_icc.c')) system_ss.add(when: 'CONFIG_MAX78000_TRNG', if_true: files('max78000_trng.c')) diff --git a/include/hw/misc/max78000_aes.h b/include/hw/misc/max78000_aes.h new file mode 100644 index 0000000000..407c45ef61 --- /dev/null +++ b/include/hw/misc/max78000_aes.h @@ -0,0 +1,68 @@ +/* + * MAX78000 AES + * + * Copyright (c) 2025 Jackson Donaldson + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_MAX78000_AES_H +#define HW_MAX78000_AES_H + +#include "hw/sysbus.h" +#include "crypto/aes.h" +#include "qom/object.h" + +#define TYPE_MAX78000_AES "max78000-aes" +OBJECT_DECLARE_SIMPLE_TYPE(Max78000AesState, MAX78000_AES) + +#define CTRL 0 +#define STATUS 4 +#define INTFL 8 +#define INTEN 0xc +#define FIFO 0x10 + +#define KEY_BASE 0x400 +#define KEY_END 0x420 + +/* CTRL */ +#define TYPE (1 << 9 | 1 << 8) +#define KEY_SIZE (1 << 7 | 1 << 6) +#define OUTPUT_FLUSH (1 << 5) +#define INPUT_FLUSH (1 << 4) +#define START (1 << 3) + +#define AES_EN (1 << 0) + +/* STATUS */ +#define OUTPUT_FULL (1 << 4) +#define OUTPUT_EMPTY (1 << 3) +#define INPUT_FULL (1 << 2) +#define INPUT_EMPTY (1 << 1) +#define BUSY (1 << 0) + +/* INTFL*/ +#define DONE (1 << 0) + +struct Max78000AesState { + SysBusDevice parent_obj; + + MemoryRegion mmio; + + uint32_t ctrl; + uint32_t status; + uint32_t intfl; + uint32_t inten; + uint32_t data_index; + uint8_t data[16]; + + uint8_t key[32]; + AES_KEY internal_key; + + uint32_t result_index; + uint8_t result[16]; + + + qemu_irq irq; +}; + +#endif diff --git a/include/hw/misc/max78000_gcr.h b/include/hw/misc/max78000_gcr.h index 23ddf0885b..d5858a40f3 100644 --- a/include/hw/misc/max78000_gcr.h +++ b/include/hw/misc/max78000_gcr.h @@ -124,6 +124,7 @@ struct Max78000GcrState { DeviceState *uart1; DeviceState *uart2; DeviceState *trng; + DeviceState *aes; }; From 4b3a1eb0664db2df02ff4f8affe6e7bda1edaa85 Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 4 Jul 2025 18:32:39 -0400 Subject: [PATCH 2065/2760] MAX78000: Add AES to SOC This commit adds AES to max78000_soc Signed-off-by: Jackson Donaldson Reviewed-by: Peter Maydell Message-id: 20250704223239.248781-12-jcksn@duck.com Signed-off-by: Peter Maydell --- hw/arm/max78000_soc.c | 12 +++++++++--- include/hw/arm/max78000_soc.h | 2 ++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/hw/arm/max78000_soc.c b/hw/arm/max78000_soc.c index 3f2069fb03..7f1856f5ba 100644 --- a/hw/arm/max78000_soc.c +++ b/hw/arm/max78000_soc.c @@ -45,6 +45,8 @@ static void max78000_soc_initfn(Object *obj) object_initialize_child(obj, "trng", &s->trng, TYPE_MAX78000_TRNG); + object_initialize_child(obj, "aes", &s->aes, TYPE_MAX78000_AES); + s->sysclk = qdev_init_clock_in(DEVICE(s), "sysclk", NULL, NULL, 0); } @@ -133,6 +135,13 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) object_property_set_link(OBJECT(gcrdev), "trng", OBJECT(dev), &err); + dev = DEVICE(&s->aes); + sysbus_realize(SYS_BUS_DEVICE(dev), errp); + sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40007400); + sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(armv7m, 5)); + + object_property_set_link(OBJECT(gcrdev), "aes", OBJECT(dev), &err); + dev = DEVICE(&s->gcr); sysbus_realize(SYS_BUS_DEVICE(dev), errp); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, 0x40000000); @@ -148,9 +157,6 @@ static void max78000_soc_realize(DeviceState *dev_soc, Error **errp) create_unimplemented_device("powerSequencer", 0x40006800, 0x400); create_unimplemented_device("miscControl", 0x40006c00, 0x400); - create_unimplemented_device("aes", 0x40007400, 0x400); - create_unimplemented_device("aesKey", 0x40007800, 0x400); - create_unimplemented_device("gpio0", 0x40008000, 0x1000); create_unimplemented_device("gpio1", 0x40009000, 0x1000); diff --git a/include/hw/arm/max78000_soc.h b/include/hw/arm/max78000_soc.h index 528598cfcb..a203079ee9 100644 --- a/include/hw/arm/max78000_soc.h +++ b/include/hw/arm/max78000_soc.h @@ -11,6 +11,7 @@ #include "hw/or-irq.h" #include "hw/arm/armv7m.h" +#include "hw/misc/max78000_aes.h" #include "hw/misc/max78000_gcr.h" #include "hw/misc/max78000_icc.h" #include "hw/char/max78000_uart.h" @@ -41,6 +42,7 @@ struct MAX78000State { Max78000IccState icc[MAX78000_NUM_ICC]; Max78000UartState uart[MAX78000_NUM_UART]; Max78000TrngState trng; + Max78000AesState aes; Clock *sysclk; }; From 35566583d86e167b617f0b55e485fc4ef1ae5dc3 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Thu, 3 Jul 2025 11:41:06 +0100 Subject: [PATCH 2066/2760] hw/cxl-host: Add an index field to CXLFixedMemoryWindow To enable these to be found in a fixed order, that order needs to be known. This will later be used to sort a list of these structures so that address map and ACPI table entries are predictable. Tested-by: Li Zhijian Reviewed-by: Li Zhijian Reviewed-by: Fan Ni Reviewed-by: Eric Auger Signed-off-by: Jonathan Cameron Tested-by: Itaru Kitayama Message-id: 20250703104110.992379-2-Jonathan.Cameron@huawei.com Signed-off-by: Peter Maydell --- hw/cxl/cxl-host.c | 9 ++++++--- include/hw/cxl/cxl.h | 1 + 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index e010163174..b7aa429ddf 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -24,13 +24,15 @@ static void cxl_fixed_memory_window_config(CXLState *cxl_state, CXLFixedMemoryWindowOptions *object, - Error **errp) + int index, Error **errp) { ERRP_GUARD(); g_autofree CXLFixedWindow *fw = g_malloc0(sizeof(*fw)); strList *target; int i; + fw->index = index; + for (target = object->targets; target; target = target->next) { fw->num_targets++; } @@ -325,14 +327,15 @@ static void machine_set_cfmw(Object *obj, Visitor *v, const char *name, CXLState *state = opaque; CXLFixedMemoryWindowOptionsList *cfmw_list = NULL; CXLFixedMemoryWindowOptionsList *it; + int index; visit_type_CXLFixedMemoryWindowOptionsList(v, name, &cfmw_list, errp); if (!cfmw_list) { return; } - for (it = cfmw_list; it; it = it->next) { - cxl_fixed_memory_window_config(state, it->value, errp); + for (it = cfmw_list, index = 0; it; it = it->next, index++) { + cxl_fixed_memory_window_config(state, it->value, index, errp); } state->cfmw_list = cfmw_list; } diff --git a/include/hw/cxl/cxl.h b/include/hw/cxl/cxl.h index 75e47b6864..b2bcce7ed6 100644 --- a/include/hw/cxl/cxl.h +++ b/include/hw/cxl/cxl.h @@ -27,6 +27,7 @@ typedef struct PXBCXLDev PXBCXLDev; typedef struct CXLFixedWindow { + int index; uint64_t size; char **targets; PXBCXLDev *target_hbs[16]; From 584f722eb3ab4896ce9e3913c49f4f22e8b51f2b Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Thu, 3 Jul 2025 11:41:07 +0100 Subject: [PATCH 2067/2760] hw/cxl: Make the CXL fixed memory windows devices. Previously these somewhat device like structures were tracked using a list in the CXLState in each machine. This is proving restrictive in a few cases where we need to iterate through these without being aware of the machine type. Just make them sysbus devices. Restrict them to not user created as they need to be visible to early stages of machine init given effects on the memory map. This change both simplifies state tracking and enables features needed for performance optimization and hotness tracking by making it possible to retrieve the fixed memory window on actions elsewhere in the topology. In some cases the ordering of the Fixed Memory Windows matters. For those utility functions provide a GSList sorted by the window index. This ensures that we get consistency across: - ordering in the command line - ordering of the host PA ranges - ordering of ACPI CEDT structures describing the CFMWS. Other aspects don't have this constraint. For those direct iteration of the underlying hash structures is fine. In the setup path for the memory map in pc_memory_init() split the operations into two calls. The first, cxl_fmws_set_mmemap(), loops over fixed memory windows in order and assigns their addresses. The second, cxl_fmws_update_mmio() actually sets up the mmio for each window. This is obviously less efficient than a single loop but this split design is needed to put the logic in two different places in the arm64 support and it is not a hot enough path to justify an x86 only implementation. Reviewed-by: Li Zhijian Tested-by: Li Zhijian Signed-off-by: Jonathan Cameron Tested-by: Itaru Kitayama Message-id: 20250703104110.992379-3-Jonathan.Cameron@huawei.com Signed-off-by: Peter Maydell --- hw/acpi/cxl.c | 76 +++++++++-------- hw/cxl/cxl-host-stubs.c | 7 +- hw/cxl/cxl-host.c | 167 +++++++++++++++++++++++++++++++------- hw/i386/pc.c | 50 +++++------- include/hw/cxl/cxl.h | 4 +- include/hw/cxl/cxl_host.h | 5 +- 6 files changed, 214 insertions(+), 95 deletions(-) diff --git a/hw/acpi/cxl.c b/hw/acpi/cxl.c index 9cd7905ea2..75d5b30bb8 100644 --- a/hw/acpi/cxl.c +++ b/hw/acpi/cxl.c @@ -22,6 +22,7 @@ #include "hw/pci/pci_bridge.h" #include "hw/pci/pci_host.h" #include "hw/cxl/cxl.h" +#include "hw/cxl/cxl_host.h" #include "hw/mem/memory-device.h" #include "hw/acpi/acpi.h" #include "hw/acpi/aml-build.h" @@ -135,55 +136,52 @@ static void cedt_build_chbs(GArray *table_data, PXBCXLDev *cxl) * Interleave ways encoding in CXL 2.0 ECN: 3, 6, 12 and 16-way memory * interleaving. */ -static void cedt_build_cfmws(GArray *table_data, CXLState *cxls) +static void cedt_build_cfmws(CXLFixedWindow *fw, Aml *cedt) { - GList *it; + GArray *table_data = cedt->buf; + int i; - for (it = cxls->fixed_windows; it; it = it->next) { - CXLFixedWindow *fw = it->data; - int i; - - /* Type */ - build_append_int_noprefix(table_data, 1, 1); + /* Type */ + build_append_int_noprefix(table_data, 1, 1); - /* Reserved */ - build_append_int_noprefix(table_data, 0, 1); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 1); - /* Record Length */ - build_append_int_noprefix(table_data, 36 + 4 * fw->num_targets, 2); + /* Record Length */ + build_append_int_noprefix(table_data, 36 + 4 * fw->num_targets, 2); - /* Reserved */ - build_append_int_noprefix(table_data, 0, 4); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 4); - /* Base HPA */ - build_append_int_noprefix(table_data, fw->mr.addr, 8); + /* Base HPA */ + build_append_int_noprefix(table_data, fw->mr.addr, 8); - /* Window Size */ - build_append_int_noprefix(table_data, fw->size, 8); + /* Window Size */ + build_append_int_noprefix(table_data, fw->size, 8); - /* Host Bridge Interleave Ways */ - build_append_int_noprefix(table_data, fw->enc_int_ways, 1); + /* Host Bridge Interleave Ways */ + build_append_int_noprefix(table_data, fw->enc_int_ways, 1); - /* Host Bridge Interleave Arithmetic */ - build_append_int_noprefix(table_data, 0, 1); + /* Host Bridge Interleave Arithmetic */ + build_append_int_noprefix(table_data, 0, 1); - /* Reserved */ - build_append_int_noprefix(table_data, 0, 2); + /* Reserved */ + build_append_int_noprefix(table_data, 0, 2); - /* Host Bridge Interleave Granularity */ - build_append_int_noprefix(table_data, fw->enc_int_gran, 4); + /* Host Bridge Interleave Granularity */ + build_append_int_noprefix(table_data, fw->enc_int_gran, 4); - /* Window Restrictions */ - build_append_int_noprefix(table_data, 0x0f, 2); /* No restrictions */ + /* Window Restrictions */ + build_append_int_noprefix(table_data, 0x0f, 2); - /* QTG ID */ - build_append_int_noprefix(table_data, 0, 2); + /* QTG ID */ + build_append_int_noprefix(table_data, 0, 2); - /* Host Bridge List (list of UIDs - currently bus_nr) */ - for (i = 0; i < fw->num_targets; i++) { - g_assert(fw->target_hbs[i]); - build_append_int_noprefix(table_data, PXB_DEV(fw->target_hbs[i])->bus_nr, 4); - } + /* Host Bridge List (list of UIDs - currently bus_nr) */ + for (i = 0; i < fw->num_targets; i++) { + g_assert(fw->target_hbs[i]); + build_append_int_noprefix(table_data, + PXB_DEV(fw->target_hbs[i])->bus_nr, 4); } } @@ -202,6 +200,7 @@ void cxl_build_cedt(GArray *table_offsets, GArray *table_data, BIOSLinker *linker, const char *oem_id, const char *oem_table_id, CXLState *cxl_state) { + GSList *cfmws_list, *iter; Aml *cedt; AcpiTable table = { .sig = "CEDT", .rev = 1, .oem_id = oem_id, .oem_table_id = oem_table_id }; @@ -213,7 +212,12 @@ void cxl_build_cedt(GArray *table_offsets, GArray *table_data, /* reserve space for CEDT header */ object_child_foreach_recursive(object_get_root(), cxl_foreach_pxb_hb, cedt); - cedt_build_cfmws(cedt->buf, cxl_state); + + cfmws_list = cxl_fmws_get_all_sorted(); + for (iter = cfmws_list; iter; iter = iter->next) { + cedt_build_cfmws(CXL_FMW(iter->data), cedt); + } + g_slist_free(cfmws_list); /* copy AML table into ACPI tables blob and patch header there */ g_array_append_vals(table_data, cedt->buf->data, cedt->buf->len); diff --git a/hw/cxl/cxl-host-stubs.c b/hw/cxl/cxl-host-stubs.c index cae4afcdde..c015baac81 100644 --- a/hw/cxl/cxl-host-stubs.c +++ b/hw/cxl/cxl-host-stubs.c @@ -8,8 +8,13 @@ #include "hw/cxl/cxl.h" #include "hw/cxl/cxl_host.h" -void cxl_fmws_link_targets(CXLState *stat, Error **errp) {}; +void cxl_fmws_link_targets(Error **errp) {}; void cxl_machine_init(Object *obj, CXLState *state) {}; void cxl_hook_up_pxb_registers(PCIBus *bus, CXLState *state, Error **errp) {}; +hwaddr cxl_fmws_set_memmap(hwaddr base, hwaddr max_addr) +{ + return base; +}; +void cxl_fmws_update_mmio(void) {}; const MemoryRegionOps cfmws_ops; diff --git a/hw/cxl/cxl-host.c b/hw/cxl/cxl-host.c index b7aa429ddf..5c2ce25a19 100644 --- a/hw/cxl/cxl-host.c +++ b/hw/cxl/cxl-host.c @@ -22,12 +22,12 @@ #include "hw/pci/pcie_port.h" #include "hw/pci-bridge/pci_expander_bridge.h" -static void cxl_fixed_memory_window_config(CXLState *cxl_state, - CXLFixedMemoryWindowOptions *object, +static void cxl_fixed_memory_window_config(CXLFixedMemoryWindowOptions *object, int index, Error **errp) { ERRP_GUARD(); - g_autofree CXLFixedWindow *fw = g_malloc0(sizeof(*fw)); + DeviceState *dev = qdev_new(TYPE_CXL_FMW); + CXLFixedWindow *fw = CXL_FMW(dev); strList *target; int i; @@ -67,35 +67,39 @@ static void cxl_fixed_memory_window_config(CXLState *cxl_state, fw->targets[i] = g_strdup(target->value); } - cxl_state->fixed_windows = g_list_append(cxl_state->fixed_windows, - g_steal_pointer(&fw)); + sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), errp); } -void cxl_fmws_link_targets(CXLState *cxl_state, Error **errp) +static int cxl_fmws_link(Object *obj, void *opaque) { - if (cxl_state && cxl_state->fixed_windows) { - GList *it; - - for (it = cxl_state->fixed_windows; it; it = it->next) { - CXLFixedWindow *fw = it->data; - int i; - - for (i = 0; i < fw->num_targets; i++) { - Object *o; - bool ambig; - - o = object_resolve_path_type(fw->targets[i], - TYPE_PXB_CXL_DEV, - &ambig); - if (!o) { - error_setg(errp, "Could not resolve CXLFM target %s", - fw->targets[i]); - return; - } - fw->target_hbs[i] = PXB_CXL_DEV(o); - } + struct CXLFixedWindow *fw; + int i; + + if (!object_dynamic_cast(obj, TYPE_CXL_FMW)) { + return 0; + } + fw = CXL_FMW(obj); + + for (i = 0; i < fw->num_targets; i++) { + Object *o; + bool ambig; + + o = object_resolve_path_type(fw->targets[i], TYPE_PXB_CXL_DEV, + &ambig); + if (!o) { + error_setg(&error_fatal, "Could not resolve CXLFM target %s", + fw->targets[i]); + return 1; } + fw->target_hbs[i] = PXB_CXL_DEV(o); } + return 0; +} + +void cxl_fmws_link_targets(Error **errp) +{ + /* Order doesn't matter for this, so no need to build list */ + object_child_foreach_recursive(object_get_root(), cxl_fmws_link, NULL); } static bool cxl_hdm_find_target(uint32_t *cache_mem, hwaddr addr, @@ -335,7 +339,7 @@ static void machine_set_cfmw(Object *obj, Visitor *v, const char *name, } for (it = cfmw_list, index = 0; it; it = it->next, index++) { - cxl_fixed_memory_window_config(state, it->value, index, errp); + cxl_fixed_memory_window_config(it->value, index, errp); } state->cfmw_list = cfmw_list; } @@ -373,3 +377,110 @@ void cxl_hook_up_pxb_registers(PCIBus *bus, CXLState *state, Error **errp) } } } + +static int cxl_fmws_find(Object *obj, void *opaque) +{ + GSList **list = opaque; + + if (!object_dynamic_cast(obj, TYPE_CXL_FMW)) { + return 0; + } + *list = g_slist_prepend(*list, obj); + + return 0; +} + +static GSList *cxl_fmws_get_all(void) +{ + GSList *list = NULL; + + object_child_foreach_recursive(object_get_root(), cxl_fmws_find, &list); + + return list; +} + +static gint cfmws_cmp(gconstpointer a, gconstpointer b, gpointer d) +{ + const struct CXLFixedWindow *ap = a; + const struct CXLFixedWindow *bp = b; + + return ap->index > bp->index; +} + +GSList *cxl_fmws_get_all_sorted(void) +{ + return g_slist_sort_with_data(cxl_fmws_get_all(), cfmws_cmp, NULL); +} + +static int cxl_fmws_mmio_map(Object *obj, void *opaque) +{ + struct CXLFixedWindow *fw; + + if (!object_dynamic_cast(obj, TYPE_CXL_FMW)) { + return 0; + } + fw = CXL_FMW(obj); + sysbus_mmio_map(SYS_BUS_DEVICE(fw), 0, fw->base); + + return 0; +} + +void cxl_fmws_update_mmio(void) +{ + /* Ordering is not required for this */ + object_child_foreach_recursive(object_get_root(), cxl_fmws_mmio_map, NULL); +} + +hwaddr cxl_fmws_set_memmap(hwaddr base, hwaddr max_addr) +{ + GSList *cfmws_list, *iter; + CXLFixedWindow *fw; + + cfmws_list = cxl_fmws_get_all_sorted(); + for (iter = cfmws_list; iter; iter = iter->next) { + fw = CXL_FMW(iter->data); + if (base + fw->size <= max_addr) { + fw->base = base; + base += fw->size; + } + } + g_slist_free(cfmws_list); + + return base; +} + +static void cxl_fmw_realize(DeviceState *dev, Error **errp) +{ + CXLFixedWindow *fw = CXL_FMW(dev); + + memory_region_init_io(&fw->mr, OBJECT(dev), &cfmws_ops, fw, + "cxl-fixed-memory-region", fw->size); + sysbus_init_mmio(SYS_BUS_DEVICE(dev), &fw->mr); +} + +/* + * Note: Fixed memory windows represent fixed address decoders on the host and + * as such have no dynamic state to reset or migrate + */ +static void cxl_fmw_class_init(ObjectClass *klass, const void *data) +{ + DeviceClass *dc = DEVICE_CLASS(klass); + + dc->desc = "CXL Fixed Memory Window"; + dc->realize = cxl_fmw_realize; + /* Reason - created by machines as tightly coupled to machine memory map */ + dc->user_creatable = false; +} + +static const TypeInfo cxl_fmw_info = { + .name = TYPE_CXL_FMW, + .parent = TYPE_SYS_BUS_DEVICE, + .instance_size = sizeof(CXLFixedWindow), + .class_init = cxl_fmw_class_init, +}; + +static void cxl_host_register_types(void) +{ + type_register_static(&cxl_fmw_info); +} +type_init(cxl_host_register_types) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index b211633575..860346d6b7 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -609,7 +609,7 @@ void pc_machine_done(Notifier *notifier, void *data) &error_fatal); if (pcms->cxl_devices_state.is_enabled) { - cxl_fmws_link_targets(&pcms->cxl_devices_state, &error_fatal); + cxl_fmws_link_targets(&error_fatal); } /* set the number of CPUs */ @@ -718,20 +718,28 @@ static uint64_t pc_get_cxl_range_start(PCMachineState *pcms) return cxl_base; } -static uint64_t pc_get_cxl_range_end(PCMachineState *pcms) +static int cxl_get_fmw_end(Object *obj, void *opaque) { - uint64_t start = pc_get_cxl_range_start(pcms) + MiB; + struct CXLFixedWindow *fw; + uint64_t *start = opaque; - if (pcms->cxl_devices_state.fixed_windows) { - GList *it; - - start = ROUND_UP(start, 256 * MiB); - for (it = pcms->cxl_devices_state.fixed_windows; it; it = it->next) { - CXLFixedWindow *fw = it->data; - start += fw->size; - } + if (!object_dynamic_cast(obj, TYPE_CXL_FMW)) { + return 0; } + fw = CXL_FMW(obj); + + *start += fw->size; + return 0; +} + +static uint64_t pc_get_cxl_range_end(PCMachineState *pcms) +{ + uint64_t start = pc_get_cxl_range_start(pcms) + MiB; + + /* Ordering doesn't matter so no need to build a sorted list */ + object_child_foreach_recursive(object_get_root(), cxl_get_fmw_end, + &start); return start; } @@ -933,23 +941,9 @@ void pc_memory_init(PCMachineState *pcms, cxl_base = pc_get_cxl_range_start(pcms); memory_region_init(mr, OBJECT(machine), "cxl_host_reg", cxl_size); memory_region_add_subregion(system_memory, cxl_base, mr); - cxl_resv_end = cxl_base + cxl_size; - if (pcms->cxl_devices_state.fixed_windows) { - hwaddr cxl_fmw_base; - GList *it; - - cxl_fmw_base = ROUND_UP(cxl_base + cxl_size, 256 * MiB); - for (it = pcms->cxl_devices_state.fixed_windows; it; it = it->next) { - CXLFixedWindow *fw = it->data; - - fw->base = cxl_fmw_base; - memory_region_init_io(&fw->mr, OBJECT(machine), &cfmws_ops, fw, - "cxl-fixed-memory-region", fw->size); - memory_region_add_subregion(system_memory, fw->base, &fw->mr); - cxl_fmw_base += fw->size; - cxl_resv_end = cxl_fmw_base; - } - } + cxl_base = ROUND_UP(cxl_base + cxl_size, 256 * MiB); + cxl_resv_end = cxl_fmws_set_memmap(cxl_base, maxphysaddr); + cxl_fmws_update_mmio(); } /* Initialize PC system firmware */ diff --git a/include/hw/cxl/cxl.h b/include/hw/cxl/cxl.h index b2bcce7ed6..de66ab8c35 100644 --- a/include/hw/cxl/cxl.h +++ b/include/hw/cxl/cxl.h @@ -27,6 +27,7 @@ typedef struct PXBCXLDev PXBCXLDev; typedef struct CXLFixedWindow { + SysBusDevice parent_obj; int index; uint64_t size; char **targets; @@ -38,12 +39,13 @@ typedef struct CXLFixedWindow { MemoryRegion mr; hwaddr base; } CXLFixedWindow; +#define TYPE_CXL_FMW "cxl-fmw" +OBJECT_DECLARE_SIMPLE_TYPE(CXLFixedWindow, CXL_FMW) typedef struct CXLState { bool is_enabled; MemoryRegion host_mr; unsigned int next_mr_idx; - GList *fixed_windows; CXLFixedMemoryWindowOptionsList *cfmw_list; } CXLState; diff --git a/include/hw/cxl/cxl_host.h b/include/hw/cxl/cxl_host.h index c9bc9c7c50..cd3c368c86 100644 --- a/include/hw/cxl/cxl_host.h +++ b/include/hw/cxl/cxl_host.h @@ -14,8 +14,11 @@ #define CXL_HOST_H void cxl_machine_init(Object *obj, CXLState *state); -void cxl_fmws_link_targets(CXLState *stat, Error **errp); +void cxl_fmws_link_targets(Error **errp); void cxl_hook_up_pxb_registers(PCIBus *bus, CXLState *state, Error **errp); +hwaddr cxl_fmws_set_memmap(hwaddr base, hwaddr max_addr); +void cxl_fmws_update_mmio(void); +GSList *cxl_fmws_get_all_sorted(void); extern const MemoryRegionOps cfmws_ops; From 9d8ade51a20d15f3be70c821c274b081ba65cea8 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Thu, 3 Jul 2025 11:41:08 +0100 Subject: [PATCH 2068/2760] hw/arm/virt: Basic CXL enablement on pci_expander_bridge instances pxb-cxl Code based on i386/pc enablement. The memory layout places space for 16 host bridge register regions after the GIC_REDIST2 in the extended memmap. This is a hole in the current map so adding them here has no impact on placement of other memory regions (tested with enough CPUs for GIC_REDIST2 to be in use.) The high memory map is GiB aligned so the hole is there whatever the size of memory or device_memory below this point. The CFMWs are placed above the extended memmap. Note the existing variable highest_gpa is the highest GPA that has been allocated at a particular point in setting up the memory map. Whilst this caused some confusion in review there are existing comments explaining this so nothing is added. The cxl_devices_state.host_mr provides a small space in which to place the individual host bridge register regions for whatever host bridges are allocated via -device pxb-cxl on the command line. The existing dynamic sysbus infrastructure is not reused because pxb-cxl is a PCI device not a sysbus one but these registers are directly in the main memory map, not the PCI address space. Only create the CEDT table if cxl=on set for the machine. Default to off. Signed-off-by: Jonathan Cameron Tested-by: Itaru Kitayama Tested-by: Li Zhijian Message-id: 20250703104110.992379-4-Jonathan.Cameron@huawei.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- docs/system/arm/virt.rst | 9 +++++++++ hw/arm/virt-acpi-build.c | 34 ++++++++++++++++++++++++++++++++++ hw/arm/virt.c | 30 ++++++++++++++++++++++++++++++ include/hw/arm/virt.h | 4 ++++ 4 files changed, 77 insertions(+) diff --git a/docs/system/arm/virt.rst b/docs/system/arm/virt.rst index 6a719b9586..10cbffc8a7 100644 --- a/docs/system/arm/virt.rst +++ b/docs/system/arm/virt.rst @@ -31,6 +31,7 @@ Supported devices The virt board supports: - PCI/PCIe devices +- CXL Fixed memory windows, root bridges and devices. - Flash memory - Either one or two PL011 UARTs for the NonSecure World - An RTC @@ -189,6 +190,14 @@ ras acpi Set ``on``/``off``/``auto`` to enable/disable ACPI. +cxl + Set ``on``/``off`` to enable/disable CXL. More details in + :doc:`../devices/cxl`. The default is off. + +cxl-fmw + Array of CXL fixed memory windows describing fixed address routing to + target CXL host bridges. See :doc:`../devices/cxl`. + dtb-randomness Set ``on``/``off`` to pass random seeds via the guest DTB rng-seed and kaslr-seed nodes (in both "/chosen" and diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index cd90c47976..c3b9b3f6ea 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -39,10 +39,12 @@ #include "hw/acpi/aml-build.h" #include "hw/acpi/utils.h" #include "hw/acpi/pci.h" +#include "hw/acpi/cxl.h" #include "hw/acpi/memory_hotplug.h" #include "hw/acpi/generic_event_device.h" #include "hw/acpi/tpm.h" #include "hw/acpi/hmat.h" +#include "hw/cxl/cxl.h" #include "hw/pci/pcie_host.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bus.h" @@ -119,10 +121,29 @@ static void acpi_dsdt_add_flash(Aml *scope, const MemMapEntry *flash_memmap) aml_append(scope, dev); } +static void build_acpi0017(Aml *table) +{ + Aml *dev, *scope, *method; + + scope = aml_scope("_SB"); + dev = aml_device("CXLM"); + aml_append(dev, aml_name_decl("_HID", aml_string("ACPI0017"))); + + method = aml_method("_STA", 0, AML_NOTSERIALIZED); + aml_append(method, aml_return(aml_int(0x0B))); + aml_append(dev, method); + build_cxl_dsm_method(dev); + + aml_append(scope, dev); + aml_append(table, scope); +} + static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, uint32_t irq, VirtMachineState *vms) { int ecam_id = VIRT_ECAM_ID(vms->highmem_ecam); + bool cxl_present = false; + PCIBus *bus = vms->bus; struct GPEXConfig cfg = { .mmio32 = memmap[VIRT_PCIE_MMIO], .pio = memmap[VIRT_PCIE_PIO], @@ -136,6 +157,14 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, } acpi_dsdt_add_gpex(scope, &cfg); + QLIST_FOREACH(bus, &vms->bus->child, sibling) { + if (pci_bus_is_cxl(bus)) { + cxl_present = true; + } + } + if (cxl_present) { + build_acpi0017(scope); + } } static void acpi_dsdt_add_gpio(Aml *scope, const MemMapEntry *gpio_memmap, @@ -1027,6 +1056,11 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) } } + if (vms->cxl_devices_state.is_enabled) { + cxl_build_cedt(table_offsets, tables_blob, tables->linker, + vms->oem_id, vms->oem_table_id, &vms->cxl_devices_state); + } + if (ms->nvdimms_state->is_enabled) { nvdimm_build_acpi(table_offsets, tables_blob, tables->linker, ms->nvdimms_state, ms->ram_slots, vms->oem_id, diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 3bcdf92e2f..394e8b5301 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -57,6 +57,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "hw/pci-host/gpex.h" +#include "hw/pci-bridge/pci_expander_bridge.h" #include "hw/virtio/virtio-pci.h" #include "hw/core/sysbus-fdt.h" #include "hw/platform-bus.h" @@ -86,6 +87,8 @@ #include "hw/virtio/virtio-md-pci.h" #include "hw/virtio/virtio-iommu.h" #include "hw/char/pl011.h" +#include "hw/cxl/cxl.h" +#include "hw/cxl/cxl_host.h" #include "qemu/guest-random.h" static GlobalProperty arm_virt_compat[] = { @@ -220,9 +223,11 @@ static const MemMapEntry base_memmap[] = { static MemMapEntry extended_memmap[] = { /* Additional 64 MB redist region (can contain up to 512 redistributors) */ [VIRT_HIGH_GIC_REDIST2] = { 0x0, 64 * MiB }, + [VIRT_CXL_HOST] = { 0x0, 64 * KiB * 16 }, /* 16 UID */ [VIRT_HIGH_PCIE_ECAM] = { 0x0, 256 * MiB }, /* Second PCIe window */ [VIRT_HIGH_PCIE_MMIO] = { 0x0, DEFAULT_HIGH_PCIE_MMIO_SIZE }, + /* Any CXL Fixed memory windows come here */ }; static const int a15irqmap[] = { @@ -1623,6 +1628,17 @@ static void create_pcie(VirtMachineState *vms) } } +static void create_cxl_host_reg_region(VirtMachineState *vms) +{ + MemoryRegion *sysmem = get_system_memory(); + MemoryRegion *mr = &vms->cxl_devices_state.host_mr; + + memory_region_init(mr, OBJECT(vms), "cxl_host_reg", + vms->memmap[VIRT_CXL_HOST].size); + memory_region_add_subregion(sysmem, vms->memmap[VIRT_CXL_HOST].base, mr); + vms->highmem_cxl = true; +} + static void create_platform_bus(VirtMachineState *vms) { DeviceState *dev; @@ -1739,6 +1755,12 @@ void virt_machine_done(Notifier *notifier, void *data) struct arm_boot_info *info = &vms->bootinfo; AddressSpace *as = arm_boot_address_space(cpu, info); + cxl_hook_up_pxb_registers(vms->bus, &vms->cxl_devices_state, + &error_fatal); + + if (vms->cxl_devices_state.is_enabled) { + cxl_fmws_link_targets(&error_fatal); + } /* * If the user provided a dtb, we assume the dynamic sysbus nodes * already are integrated there. This corresponds to a use case where @@ -1785,6 +1807,7 @@ static inline bool *virt_get_high_memmap_enabled(VirtMachineState *vms, { bool *enabled_array[] = { &vms->highmem_redists, + &vms->highmem_cxl, &vms->highmem_ecam, &vms->highmem_mmio, }; @@ -1892,6 +1915,9 @@ static void virt_set_memmap(VirtMachineState *vms, int pa_bits) if (device_memory_size > 0) { machine_memory_devices_init(ms, device_memory_base, device_memory_size); } + vms->highest_gpa = cxl_fmws_set_memmap(ROUND_UP(vms->highest_gpa + 1, + 256 * MiB), + BIT_ULL(pa_bits)) - 1; } static VirtGICType finalize_gic_version_do(const char *accel_name, @@ -2343,6 +2369,8 @@ static void machvirt_init(MachineState *machine) memory_region_add_subregion(sysmem, vms->memmap[VIRT_MEM].base, machine->ram); + cxl_fmws_update_mmio(); + virt_flash_fdt(vms, sysmem, secure_sysmem ?: sysmem); create_gic(vms, sysmem); @@ -2398,6 +2426,7 @@ static void machvirt_init(MachineState *machine) create_rtc(vms); create_pcie(vms); + create_cxl_host_reg_region(vms); if (has_ged && aarch64 && firmware_loaded && virt_is_acpi_enabled(vms)) { vms->acpi_dev = create_acpi_ged(vms); @@ -3364,6 +3393,7 @@ static void virt_instance_init(Object *obj) vms->oem_id = g_strndup(ACPI_BUILD_APPNAME6, 6); vms->oem_table_id = g_strndup(ACPI_BUILD_APPNAME8, 8); + cxl_machine_init(obj, &vms->cxl_devices_state); } static const TypeInfo virt_machine_info = { diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 9a1b0f53d2..4375819ea0 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -36,6 +36,7 @@ #include "hw/arm/boot.h" #include "hw/arm/bsa.h" #include "hw/block/flash.h" +#include "hw/cxl/cxl.h" #include "system/kvm.h" #include "hw/intc/arm_gicv3_common.h" #include "qom/object.h" @@ -85,6 +86,7 @@ enum { /* indices of IO regions located after the RAM */ enum { VIRT_HIGH_GIC_REDIST2 = VIRT_LOWMEMMAP_LAST, + VIRT_CXL_HOST, VIRT_HIGH_PCIE_ECAM, VIRT_HIGH_PCIE_MMIO, }; @@ -140,6 +142,7 @@ struct VirtMachineState { bool secure; bool highmem; bool highmem_compact; + bool highmem_cxl; bool highmem_ecam; bool highmem_mmio; bool highmem_redists; @@ -174,6 +177,7 @@ struct VirtMachineState { char *oem_id; char *oem_table_id; bool ns_el2_virt_timer_irq; + CXLState cxl_devices_state; }; #define VIRT_ECAM_ID(high) (high ? VIRT_HIGH_PCIE_ECAM : VIRT_PCIE_ECAM) From 3fd8426aefa946ac6ab86103f68c9b526a0de237 Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Thu, 3 Jul 2025 11:41:09 +0100 Subject: [PATCH 2069/2760] docs/cxl: Add an arm/virt example. Only add one very simple example as all the i386/pc examples will work for arm/virt with a change to appropriate executable and appropriate standard launch line for arm/virt. Note that max cpu is used to ensure we have plenty of physical address space. Suggested-by: Peter Maydell Reviewed-by: Eric Auger Signed-off-by: Jonathan Cameron Tested-by: Itaru Kitayama Tested-by: Li Zhijian Message-id: 20250703104110.992379-5-Jonathan.Cameron@huawei.com Signed-off-by: Peter Maydell --- docs/system/devices/cxl.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/system/devices/cxl.rst b/docs/system/devices/cxl.rst index e307caf3f8..ca15a0da1c 100644 --- a/docs/system/devices/cxl.rst +++ b/docs/system/devices/cxl.rst @@ -384,6 +384,17 @@ An example of 4 devices below a switch suitable for 1, 2 or 4 way interleave:: -device cxl-type3,bus=swport3,persistent-memdev=cxl-mem3,lsa=cxl-lsa3,id=cxl-pmem3,sn=0x4 \ -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G,cxl-fmw.0.interleave-granularity=4k +A simple arm/virt example featuring a single direct connected CXL Type 3 +Volatile Memory device:: + + qemu-system-aarch64 -M virt,gic-version=3,cxl=on -m 4g,maxmem=8g,slots=4 -cpu max -smp 4 \ + ... + -object memory-backend-ram,id=vmem0,share=on,size=256M \ + -device pxb-cxl,bus_nr=12,bus=pcie.0,id=cxl.1 \ + -device cxl-rp,port=0,bus=cxl.1,id=root_port13,chassis=0,slot=2 \ + -device cxl-type3,bus=root_port13,volatile-memdev=vmem0,id=cxl-vmem0 \ + -M cxl-fmw.0.targets.0=cxl.1,cxl-fmw.0.size=4G + Deprecations ------------ From 67fe3c8a73876ff727f301a306a03eb3230b58ee Mon Sep 17 00:00:00 2001 From: Jonathan Cameron Date: Thu, 3 Jul 2025 11:41:10 +0100 Subject: [PATCH 2070/2760] qtest/cxl: Add aarch64 virt test for CXL Add a single complex case for aarch64 virt machine. Given existing much more comprehensive tests for x86 cover the common functionality, a single test should be enough to verify that the aarch64 part continues to work. Tested-by: Itaru Kitayama Reviewed-by: Eric Auger Signed-off-by: Jonathan Cameron Tested-by: Li Zhijian Message-id: 20250703104110.992379-6-Jonathan.Cameron@huawei.com Signed-off-by: Peter Maydell --- tests/qtest/cxl-test.c | 58 ++++++++++++++++++++++++++++++++--------- tests/qtest/meson.build | 1 + 2 files changed, 46 insertions(+), 13 deletions(-) diff --git a/tests/qtest/cxl-test.c b/tests/qtest/cxl-test.c index a600331843..8fb7e58d4f 100644 --- a/tests/qtest/cxl-test.c +++ b/tests/qtest/cxl-test.c @@ -19,6 +19,12 @@ "-device pxb-cxl,id=cxl.1,bus=pcie.0,bus_nr=53 " \ "-M cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.targets.1=cxl.1,cxl-fmw.0.size=4G " +#define QEMU_VIRT_2PXB_CMD \ + "-machine virt,cxl=on -cpu max " \ + "-device pxb-cxl,id=cxl.0,bus=pcie.0,bus_nr=52 " \ + "-device pxb-cxl,id=cxl.1,bus=pcie.0,bus_nr=53 " \ + "-M cxl-fmw.0.targets.0=cxl.0,cxl-fmw.0.targets.1=cxl.1,cxl-fmw.0.size=4G " + #define QEMU_RP \ "-device cxl-rp,id=rp0,bus=cxl.0,chassis=0,slot=0 " @@ -197,25 +203,51 @@ static void cxl_2pxb_4rp_4t3d(void) qtest_end(); rmdir(tmpfs); } + +static void cxl_virt_2pxb_4rp_4t3d(void) +{ + g_autoptr(GString) cmdline = g_string_new(NULL); + g_autofree const char *tmpfs = NULL; + + tmpfs = g_dir_make_tmp("cxl-test-XXXXXX", NULL); + + g_string_printf(cmdline, QEMU_VIRT_2PXB_CMD QEMU_4RP QEMU_4T3D, + tmpfs, tmpfs, tmpfs, tmpfs, tmpfs, tmpfs, + tmpfs, tmpfs); + + qtest_start(cmdline->str); + qtest_end(); + rmdir(tmpfs); +} #endif /* CONFIG_POSIX */ int main(int argc, char **argv) { - g_test_init(&argc, &argv, NULL); + const char *arch = qtest_get_arch(); - qtest_add_func("/pci/cxl/basic_hostbridge", cxl_basic_hb); - qtest_add_func("/pci/cxl/basic_pxb", cxl_basic_pxb); - qtest_add_func("/pci/cxl/pxb_with_window", cxl_pxb_with_window); - qtest_add_func("/pci/cxl/pxb_x2_with_window", cxl_2pxb_with_window); - qtest_add_func("/pci/cxl/rp", cxl_root_port); - qtest_add_func("/pci/cxl/rp_x2", cxl_2root_port); + g_test_init(&argc, &argv, NULL); + if (strcmp(arch, "i386") == 0 || strcmp(arch, "x86_64") == 0) { + qtest_add_func("/pci/cxl/basic_hostbridge", cxl_basic_hb); + qtest_add_func("/pci/cxl/basic_pxb", cxl_basic_pxb); + qtest_add_func("/pci/cxl/pxb_with_window", cxl_pxb_with_window); + qtest_add_func("/pci/cxl/pxb_x2_with_window", cxl_2pxb_with_window); + qtest_add_func("/pci/cxl/rp", cxl_root_port); + qtest_add_func("/pci/cxl/rp_x2", cxl_2root_port); #ifdef CONFIG_POSIX - qtest_add_func("/pci/cxl/type3_device", cxl_t3d_deprecated); - qtest_add_func("/pci/cxl/type3_device_pmem", cxl_t3d_persistent); - qtest_add_func("/pci/cxl/type3_device_vmem", cxl_t3d_volatile); - qtest_add_func("/pci/cxl/type3_device_vmem_lsa", cxl_t3d_volatile_lsa); - qtest_add_func("/pci/cxl/rp_x2_type3_x2", cxl_1pxb_2rp_2t3d); - qtest_add_func("/pci/cxl/pxb_x2_root_port_x4_type3_x4", cxl_2pxb_4rp_4t3d); + qtest_add_func("/pci/cxl/type3_device", cxl_t3d_deprecated); + qtest_add_func("/pci/cxl/type3_device_pmem", cxl_t3d_persistent); + qtest_add_func("/pci/cxl/type3_device_vmem", cxl_t3d_volatile); + qtest_add_func("/pci/cxl/type3_device_vmem_lsa", cxl_t3d_volatile_lsa); + qtest_add_func("/pci/cxl/rp_x2_type3_x2", cxl_1pxb_2rp_2t3d); + qtest_add_func("/pci/cxl/pxb_x2_root_port_x4_type3_x4", + cxl_2pxb_4rp_4t3d); #endif + } else if (strcmp(arch, "aarch64") == 0) { +#ifdef CONFIG_POSIX + qtest_add_func("/pci/cxl/virt/pxb_x2_root_port_x4_type3_x4", + cxl_virt_2pxb_4rp_4t3d); +#endif + } + return g_test_run(); } diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 91b4a71a18..5ad969f616 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -262,6 +262,7 @@ qtests_aarch64 = \ config_all_devices.has_key('CONFIG_TPM_TIS_I2C') ? ['tpm-tis-i2c-test'] : []) + \ (config_all_devices.has_key('CONFIG_ASPEED_SOC') ? qtests_aspeed64 : []) + \ (config_all_devices.has_key('CONFIG_NPCM8XX') ? qtests_npcm8xx : []) + \ + qtests_cxl + \ ['arm-cpu-features', 'numa-test', 'boot-serial-test', From 1fea334eeed2b747d1c91ee0099401595ba697f8 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 4 Jul 2025 16:19:23 +0200 Subject: [PATCH 2071/2760] arm/cpu: store id_afr0 into the idregs array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cornelia Huck Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eric Auger Message-id: 20250704141927.38963-2-cohuck@redhat.com Signed-off-by: Peter Maydell --- hw/intc/armv7m_nvic.c | 2 +- target/arm/cpu-sysregs.h.inc | 1 + target/arm/cpu.h | 1 - target/arm/cpu64.c | 4 ++-- target/arm/helper.c | 2 +- target/arm/tcg/cpu-v7m.c | 12 ++++++------ target/arm/tcg/cpu32.c | 22 +++++++++++----------- target/arm/tcg/cpu64.c | 16 ++++++++-------- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index 6d85720f1b..d93e593fcb 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -1279,7 +1279,7 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; } - return cpu->id_afr0; + return GET_IDREG(isar, ID_AFR0); case 0xd50: /* MMFR0. */ if (!arm_feature(&cpu->env, ARM_FEATURE_M_MAIN)) { goto bad_offset; diff --git a/target/arm/cpu-sysregs.h.inc b/target/arm/cpu-sysregs.h.inc index cb99286f70..b96a358804 100644 --- a/target/arm/cpu-sysregs.h.inc +++ b/target/arm/cpu-sysregs.h.inc @@ -14,6 +14,7 @@ DEF(ID_AA64MMFR3_EL1, 3, 0, 0, 7, 3) DEF(ID_PFR0_EL1, 3, 0, 0, 1, 0) DEF(ID_PFR1_EL1, 3, 0, 0, 1, 1) DEF(ID_DFR0_EL1, 3, 0, 0, 1, 2) +DEF(ID_AFR0_EL1, 3, 0, 0, 1, 3) DEF(ID_MMFR0_EL1, 3, 0, 0, 1, 4) DEF(ID_MMFR1_EL1, 3, 0, 0, 1, 5) DEF(ID_MMFR2_EL1, 3, 0, 0, 1, 6) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index c8cf0ab417..835700cfab 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1082,7 +1082,6 @@ struct ArchCPU { uint32_t reset_sctlr; uint64_t pmceid0; uint64_t pmceid1; - uint32_t id_afr0; uint64_t id_aa64afr0; uint64_t id_aa64afr1; uint64_t clidr; diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index bd33d6cc6e..d648ea066c 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -663,7 +663,7 @@ static void aarch64_a57_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00000131); SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x03010066); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x10101105); SET_IDREG(isar, ID_MMFR1, 0x40000000); SET_IDREG(isar, ID_MMFR2, 0x01260000); @@ -725,7 +725,7 @@ static void aarch64_a53_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00000131); SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x03010066); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x10101105); SET_IDREG(isar, ID_MMFR1, 0x40000000); SET_IDREG(isar, ID_MMFR2, 0x01260000); diff --git a/target/arm/helper.c b/target/arm/helper.c index b3f0d6f17a..ae6231803e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7809,7 +7809,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 3, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa32_tid3, - .resetvalue = cpu->id_afr0 }, + .resetvalue = GET_IDREG(isar, ID_AFR0)}, { .name = "ID_MMFR0", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 1, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index eddd7117d5..a65b83fe99 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -62,7 +62,7 @@ static void cortex_m0_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00000030); SET_IDREG(isar, ID_PFR1, 0x00000200); SET_IDREG(isar, ID_DFR0, 0x00100000); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x00000030); SET_IDREG(isar, ID_MMFR1, 0x00000000); SET_IDREG(isar, ID_MMFR2, 0x00000000); @@ -88,7 +88,7 @@ static void cortex_m3_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00000030); SET_IDREG(isar, ID_PFR1, 0x00000200); SET_IDREG(isar, ID_DFR0, 0x00100000); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x00000030); SET_IDREG(isar, ID_MMFR1, 0x00000000); SET_IDREG(isar, ID_MMFR2, 0x00000000); @@ -119,7 +119,7 @@ static void cortex_m4_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00000030); SET_IDREG(isar, ID_PFR1, 0x00000200); SET_IDREG(isar, ID_DFR0, 0x00100000); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x00000030); SET_IDREG(isar, ID_MMFR1, 0x00000000); SET_IDREG(isar, ID_MMFR2, 0x00000000); @@ -150,7 +150,7 @@ static void cortex_m7_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00000030); SET_IDREG(isar, ID_PFR1, 0x00000200); SET_IDREG(isar, ID_DFR0, 0x00100000); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x00100030); SET_IDREG(isar, ID_MMFR1, 0x00000000); SET_IDREG(isar, ID_MMFR2, 0x01000000); @@ -183,7 +183,7 @@ static void cortex_m33_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00000030); SET_IDREG(isar, ID_PFR1, 0x00000210); SET_IDREG(isar, ID_DFR0, 0x00200000); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x00101F40); SET_IDREG(isar, ID_MMFR1, 0x00000000); SET_IDREG(isar, ID_MMFR2, 0x01000000); @@ -221,7 +221,7 @@ static void cortex_m55_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x20000030); SET_IDREG(isar, ID_PFR1, 0x00000230); SET_IDREG(isar, ID_DFR0, 0x10200000); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x00111040); SET_IDREG(isar, ID_MMFR1, 0x00000000); SET_IDREG(isar, ID_MMFR2, 0x01000000); diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index 942b636aa5..03cbe42f22 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -225,7 +225,7 @@ static void arm1136_r2_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x111); SET_IDREG(isar, ID_PFR1, 0x1); SET_IDREG(isar, ID_DFR0, 0x2); - cpu->id_afr0 = 0x3; + SET_IDREG(isar, ID_AFR0, 0x3); SET_IDREG(isar, ID_MMFR0, 0x01130003); SET_IDREG(isar, ID_MMFR1, 0x10030302); SET_IDREG(isar, ID_MMFR2, 0x01222110); @@ -257,7 +257,7 @@ static void arm1136_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x111); SET_IDREG(isar, ID_PFR1, 0x1); SET_IDREG(isar, ID_DFR0, 0x2); - cpu->id_afr0 = 0x3; + SET_IDREG(isar, ID_AFR0, 0x3); SET_IDREG(isar, ID_MMFR0, 0x01130003); SET_IDREG(isar, ID_MMFR1, 0x10030302); SET_IDREG(isar, ID_MMFR2, 0x01222110); @@ -290,7 +290,7 @@ static void arm1176_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x111); SET_IDREG(isar, ID_PFR1, 0x11); SET_IDREG(isar, ID_DFR0, 0x33); - cpu->id_afr0 = 0; + SET_IDREG(isar, ID_AFR0, 0); SET_IDREG(isar, ID_MMFR0, 0x01130003); SET_IDREG(isar, ID_MMFR1, 0x10030302); SET_IDREG(isar, ID_MMFR2, 0x01222100); @@ -320,7 +320,7 @@ static void arm11mpcore_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x111); SET_IDREG(isar, ID_PFR1, 0x1); SET_IDREG(isar, ID_DFR0, 0); - cpu->id_afr0 = 0x2; + SET_IDREG(isar, ID_AFR0, 0x2); SET_IDREG(isar, ID_MMFR0, 0x01100103); SET_IDREG(isar, ID_MMFR1, 0x10020302); SET_IDREG(isar, ID_MMFR2, 0x01222000); @@ -360,7 +360,7 @@ static void cortex_a8_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x1031); SET_IDREG(isar, ID_PFR1, 0x11); SET_IDREG(isar, ID_DFR0, 0x400); - cpu->id_afr0 = 0; + SET_IDREG(isar, ID_AFR0, 0); SET_IDREG(isar, ID_MMFR0, 0x31100003); SET_IDREG(isar, ID_MMFR1, 0x20000000); SET_IDREG(isar, ID_MMFR2, 0x01202000); @@ -436,7 +436,7 @@ static void cortex_a9_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x1031); SET_IDREG(isar, ID_PFR1, 0x11); SET_IDREG(isar, ID_DFR0, 0x000); - cpu->id_afr0 = 0; + SET_IDREG(isar, ID_AFR0, 0); SET_IDREG(isar, ID_MMFR0, 0x00100103); SET_IDREG(isar, ID_MMFR1, 0x20000000); SET_IDREG(isar, ID_MMFR2, 0x01230000); @@ -502,7 +502,7 @@ static void cortex_a7_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00001131); SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x02010555); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x10101105); SET_IDREG(isar, ID_MMFR1, 0x40000000); SET_IDREG(isar, ID_MMFR2, 0x01240000); @@ -554,7 +554,7 @@ static void cortex_a15_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00001131); SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x02010555); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x10201105); SET_IDREG(isar, ID_MMFR1, 0x20000000); SET_IDREG(isar, ID_MMFR2, 0x01240000); @@ -598,7 +598,7 @@ static void cortex_r5_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x0131); SET_IDREG(isar, ID_PFR1, 0x001); SET_IDREG(isar, ID_DFR0, 0x010400); - cpu->id_afr0 = 0x0; + SET_IDREG(isar, ID_AFR0, 0x0); SET_IDREG(isar, ID_MMFR0, 0x0210030); SET_IDREG(isar, ID_MMFR1, 0x00000000); SET_IDREG(isar, ID_MMFR2, 0x01200000); @@ -745,7 +745,7 @@ static void cortex_r52_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00000131); SET_IDREG(isar, ID_PFR1, 0x10111001); SET_IDREG(isar, ID_DFR0, 0x03010006); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x00211040); SET_IDREG(isar, ID_MMFR1, 0x40000000); SET_IDREG(isar, ID_MMFR2, 0x01200000); @@ -977,7 +977,7 @@ static void arm_max_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00000131); SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x03010066); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x10101105); SET_IDREG(isar, ID_MMFR1, 0x40000000); SET_IDREG(isar, ID_MMFR2, 0x01260000); diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index d0df50a2f3..e3183c53bb 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -52,7 +52,7 @@ static void aarch64_a35_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00000131); SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x03010066); - cpu->id_afr0 = 0; + SET_IDREG(isar, ID_AFR0, 0); SET_IDREG(isar, ID_MMFR0, 0x10201105); SET_IDREG(isar, ID_MMFR1, 0x40000000); SET_IDREG(isar, ID_MMFR2, 0x01260000); @@ -227,7 +227,7 @@ static void aarch64_a55_initfn(Object *obj) SET_IDREG(isar, ID_AA64MMFR2, 0x0000000000001011ull); SET_IDREG(isar, ID_AA64PFR0, 0x0000000010112222ull); SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000010ull); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_DFR0, 0x04010088); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); @@ -298,7 +298,7 @@ static void aarch64_a72_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x00000131); SET_IDREG(isar, ID_PFR1, 0x00011011); SET_IDREG(isar, ID_DFR0, 0x03010066); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_MMFR0, 0x10201105); SET_IDREG(isar, ID_MMFR1, 0x40000000); SET_IDREG(isar, ID_MMFR2, 0x01260000); @@ -360,7 +360,7 @@ static void aarch64_a76_initfn(Object *obj) SET_IDREG(isar, ID_AA64MMFR2, 0x0000000000001011ull); SET_IDREG(isar, ID_AA64PFR0, 0x1100000010111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000010ull); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_DFR0, 0x04010088); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); @@ -608,7 +608,7 @@ static void aarch64_neoverse_n1_initfn(Object *obj) SET_IDREG(isar, ID_AA64MMFR2, 0x0000000000001011ull); SET_IDREG(isar, ID_AA64PFR0, 0x1100000010111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000020ull); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_DFR0, 0x04010088); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); @@ -687,7 +687,7 @@ static void aarch64_neoverse_v1_initfn(Object *obj) SET_IDREG(isar, ID_AA64MMFR2, 0x0220011102101011ull), SET_IDREG(isar, ID_AA64PFR0, 0x1101110120111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000020ull); - cpu->id_afr0 = 0x00000000; + SET_IDREG(isar, ID_AFR0, 0x00000000); SET_IDREG(isar, ID_DFR0, 0x15011099); SET_IDREG(isar, ID_ISAR0, 0x02101110); SET_IDREG(isar, ID_ISAR1, 0x13112111); @@ -905,7 +905,7 @@ static void aarch64_a710_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x21110131); SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ SET_IDREG(isar, ID_DFR0, 0x16011099); - cpu->id_afr0 = 0; + SET_IDREG(isar, ID_AFR0, 0); SET_IDREG(isar, ID_MMFR0, 0x10201105); SET_IDREG(isar, ID_MMFR1, 0x40000000); SET_IDREG(isar, ID_MMFR2, 0x01260000); @@ -1007,7 +1007,7 @@ static void aarch64_neoverse_n2_initfn(Object *obj) SET_IDREG(isar, ID_PFR0, 0x21110131); SET_IDREG(isar, ID_PFR1, 0x00010000); /* GIC filled in later */ SET_IDREG(isar, ID_DFR0, 0x16011099); - cpu->id_afr0 = 0; + SET_IDREG(isar, ID_AFR0, 0); SET_IDREG(isar, ID_MMFR0, 0x10201105); SET_IDREG(isar, ID_MMFR1, 0x40000000); SET_IDREG(isar, ID_MMFR2, 0x01260000); From a7e1c62d075d48f0d05e5758471d9caf97e330df Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 4 Jul 2025 16:19:24 +0200 Subject: [PATCH 2072/2760] arm/cpu: store id_aa64afr{0,1} into the idregs array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cornelia Huck Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eric Auger Message-id: 20250704141927.38963-3-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/cpu-sysregs.h.inc | 2 ++ target/arm/cpu.h | 2 -- target/arm/helper.c | 4 ++-- target/arm/tcg/cpu64.c | 16 ++++++++-------- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/target/arm/cpu-sysregs.h.inc b/target/arm/cpu-sysregs.h.inc index b96a358804..44c877245e 100644 --- a/target/arm/cpu-sysregs.h.inc +++ b/target/arm/cpu-sysregs.h.inc @@ -4,6 +4,8 @@ DEF(ID_AA64PFR1_EL1, 3, 0, 0, 4, 1) DEF(ID_AA64SMFR0_EL1, 3, 0, 0, 4, 5) DEF(ID_AA64DFR0_EL1, 3, 0, 0, 5, 0) DEF(ID_AA64DFR1_EL1, 3, 0, 0, 5, 1) +DEF(ID_AA64AFR0_EL1, 3, 0, 0, 5, 4) +DEF(ID_AA64AFR1_EL1, 3, 0, 0, 5, 5) DEF(ID_AA64ISAR0_EL1, 3, 0, 0, 6, 0) DEF(ID_AA64ISAR1_EL1, 3, 0, 0, 6, 1) DEF(ID_AA64ISAR2_EL1, 3, 0, 0, 6, 2) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 835700cfab..008e530578 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1082,8 +1082,6 @@ struct ArchCPU { uint32_t reset_sctlr; uint64_t pmceid0; uint64_t pmceid1; - uint64_t id_aa64afr0; - uint64_t id_aa64afr1; uint64_t clidr; uint64_t mp_affinity; /* MP ID without feature bits */ /* The elements of this array are the CCSIDR values for each cache, diff --git a/target/arm/helper.c b/target/arm/helper.c index ae6231803e..93da8f170e 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7987,12 +7987,12 @@ void register_cp_regs_for_features(ARMCPU *cpu) .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 4, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->id_aa64afr0 }, + .resetvalue = GET_IDREG(isar, ID_AA64AFR0) }, { .name = "ID_AA64AFR1_EL1", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 5, .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, - .resetvalue = cpu->id_aa64afr1 }, + .resetvalue = GET_IDREG(isar, ID_AA64AFR1) }, { .name = "ID_AA64AFR2_EL1_RESERVED", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 6, .access = PL1_R, .type = ARM_CP_CONST, diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index e3183c53bb..3a65d3903b 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -428,8 +428,8 @@ static void aarch64_a64fx_initfn(Object *obj) SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000000); SET_IDREG(isar, ID_AA64DFR0, 0x0000000010305408), SET_IDREG(isar, ID_AA64DFR1, 0x0000000000000000), - cpu->id_aa64afr0 = 0x0000000000000000; - cpu->id_aa64afr1 = 0x0000000000000000; + SET_IDREG(isar, ID_AA64AFR0, 0x0000000000000000); + SET_IDREG(isar, ID_AA64AFR1, 0x0000000000000000); SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000001122); SET_IDREG(isar, ID_AA64MMFR1, 0x0000000011212100); SET_IDREG(isar, ID_AA64MMFR2, 0x0000000000001011); @@ -676,8 +676,8 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->clidr = 0x82000023; cpu->ctr = 0xb444c004; /* With DIC and IDC set */ cpu->dcz_blocksize = 4; - cpu->id_aa64afr0 = 0x00000000; - cpu->id_aa64afr1 = 0x00000000; + SET_IDREG(isar, ID_AA64AFR0, 0x00000000); + SET_IDREG(isar, ID_AA64AFR1, 0x00000000); SET_IDREG(isar, ID_AA64DFR0, 0x000001f210305519ull), SET_IDREG(isar, ID_AA64DFR1, 0x00000000), SET_IDREG(isar, ID_AA64ISAR0, 0x1011111110212120ull); /* with FEAT_RNG */ @@ -927,8 +927,8 @@ static void aarch64_a710_initfn(Object *obj) SET_IDREG(isar, ID_AA64ZFR0, 0x0000110100110021ull); /* with Crypto */ SET_IDREG(isar, ID_AA64DFR0, 0x000011f010305619ull); SET_IDREG(isar, ID_AA64DFR1, 0); - cpu->id_aa64afr0 = 0; - cpu->id_aa64afr1 = 0; + SET_IDREG(isar, ID_AA64AFR0, 0); + SET_IDREG(isar, ID_AA64AFR1, 0); SET_IDREG(isar, ID_AA64ISAR0, 0x0221111110212120ull); /* with Crypto */ SET_IDREG(isar, ID_AA64ISAR1, 0x0010111101211052ull); SET_IDREG(isar, ID_AA64MMFR0, 0x0000022200101122ull); @@ -1029,8 +1029,8 @@ static void aarch64_neoverse_n2_initfn(Object *obj) SET_IDREG(isar, ID_AA64ZFR0, 0x0000110100110021ull); /* with Crypto */ SET_IDREG(isar, ID_AA64DFR0, 0x000011f210305619ull); SET_IDREG(isar, ID_AA64DFR1, 0); - cpu->id_aa64afr0 = 0; - cpu->id_aa64afr1 = 0; + SET_IDREG(isar, ID_AA64AFR0, 0); + SET_IDREG(isar, ID_AA64AFR1, 0); SET_IDREG(isar, ID_AA64ISAR0, 0x1221111110212120ull); /* with Crypto and FEAT_RNG */ SET_IDREG(isar, ID_AA64ISAR1, 0x0011111101211052ull); SET_IDREG(isar, ID_AA64MMFR0, 0x0000022200101125ull); From e61aa4a5ffb5e849a25454c3f5fa1ae1b036bb29 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 4 Jul 2025 16:19:25 +0200 Subject: [PATCH 2073/2760] arm/cpu: fix trailing ',' for SET_IDREG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While a trailing comma is not broken for SET_IDREG invocations, it does look odd; use a semicolon instead. Fixes: f1fd81291c91 ("arm/cpu: Store aa64mmfr0-3 into the idregs array") Fixes: def3f1c1026a ("arm/cpu: Store aa64dfr0/1 into the idregs array") Signed-off-by: Cornelia Huck Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eric Auger Message-id: 20250704141927.38963-4-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/tcg/cpu64.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index 3a65d3903b..bcc8e2dfaf 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -352,7 +352,7 @@ static void aarch64_a76_initfn(Object *obj) cpu->clidr = 0x82000023; cpu->ctr = 0x8444C004; cpu->dcz_blocksize = 4; - SET_IDREG(isar, ID_AA64DFR0, 0x0000000010305408ull), + SET_IDREG(isar, ID_AA64DFR0, 0x0000000010305408ull); SET_IDREG(isar, ID_AA64ISAR0, 0x0000100010211120ull); SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000100001ull); SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000101122ull); @@ -426,8 +426,8 @@ static void aarch64_a64fx_initfn(Object *obj) cpu->reset_sctlr = 0x30000180; SET_IDREG(isar, ID_AA64PFR0, 0x0000000101111111); /* No RAS Extensions */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000000); - SET_IDREG(isar, ID_AA64DFR0, 0x0000000010305408), - SET_IDREG(isar, ID_AA64DFR1, 0x0000000000000000), + SET_IDREG(isar, ID_AA64DFR0, 0x0000000010305408); + SET_IDREG(isar, ID_AA64DFR1, 0x0000000000000000); SET_IDREG(isar, ID_AA64AFR0, 0x0000000000000000); SET_IDREG(isar, ID_AA64AFR1, 0x0000000000000000); SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000001122); @@ -678,13 +678,13 @@ static void aarch64_neoverse_v1_initfn(Object *obj) cpu->dcz_blocksize = 4; SET_IDREG(isar, ID_AA64AFR0, 0x00000000); SET_IDREG(isar, ID_AA64AFR1, 0x00000000); - SET_IDREG(isar, ID_AA64DFR0, 0x000001f210305519ull), - SET_IDREG(isar, ID_AA64DFR1, 0x00000000), + SET_IDREG(isar, ID_AA64DFR0, 0x000001f210305519ull); + SET_IDREG(isar, ID_AA64DFR1, 0x00000000); SET_IDREG(isar, ID_AA64ISAR0, 0x1011111110212120ull); /* with FEAT_RNG */ SET_IDREG(isar, ID_AA64ISAR1, 0x0011000001211032ull); SET_IDREG(isar, ID_AA64MMFR0, 0x0000000000101125ull); - SET_IDREG(isar, ID_AA64MMFR1, 0x0000000010212122ull), - SET_IDREG(isar, ID_AA64MMFR2, 0x0220011102101011ull), + SET_IDREG(isar, ID_AA64MMFR1, 0x0000000010212122ull); + SET_IDREG(isar, ID_AA64MMFR2, 0x0220011102101011ull); SET_IDREG(isar, ID_AA64PFR0, 0x1101110120111112ull); /* GIC filled in later */ SET_IDREG(isar, ID_AA64PFR1, 0x0000000000000020ull); SET_IDREG(isar, ID_AFR0, 0x00000000); From f73632932bf19788dfd0ff406c2a8fe98e827c14 Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 4 Jul 2025 16:19:26 +0200 Subject: [PATCH 2074/2760] arm/cpu: store clidr into the idregs array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Cornelia Huck Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eric Auger Message-id: 20250704141927.38963-5-cohuck@redhat.com Signed-off-by: Peter Maydell --- hw/intc/armv7m_nvic.c | 2 +- target/arm/cpu-sysregs.h.inc | 1 + target/arm/cpu.h | 3 +-- target/arm/cpu64.c | 4 ++-- target/arm/helper.c | 2 +- target/arm/tcg/cpu-v7m.c | 4 ++-- target/arm/tcg/cpu32.c | 12 ++++++------ target/arm/tcg/cpu64.c | 22 +++++++++++----------- 8 files changed, 25 insertions(+), 25 deletions(-) diff --git a/hw/intc/armv7m_nvic.c b/hw/intc/armv7m_nvic.c index d93e593fcb..7c78961040 100644 --- a/hw/intc/armv7m_nvic.c +++ b/hw/intc/armv7m_nvic.c @@ -1331,7 +1331,7 @@ static uint32_t nvic_readl(NVICState *s, uint32_t offset, MemTxAttrs attrs) } return GET_IDREG(&cpu->isar, ID_ISAR5); case 0xd78: /* CLIDR */ - return cpu->clidr; + return GET_IDREG(&cpu->isar, CLIDR); case 0xd7c: /* CTR */ return cpu->ctr; case 0xd80: /* CSSIDR */ diff --git a/target/arm/cpu-sysregs.h.inc b/target/arm/cpu-sysregs.h.inc index 44c877245e..f48a9daa7c 100644 --- a/target/arm/cpu-sysregs.h.inc +++ b/target/arm/cpu-sysregs.h.inc @@ -35,5 +35,6 @@ DEF(MVFR2_EL1, 3, 0, 0, 3, 2) DEF(ID_PFR2_EL1, 3, 0, 0, 3, 4) DEF(ID_DFR1_EL1, 3, 0, 0, 3, 5) DEF(ID_MMFR5_EL1, 3, 0, 0, 3, 6) +DEF(CLIDR_EL1, 3, 1, 0, 0, 1) DEF(ID_AA64ZFR0_EL1, 3, 0, 0, 4, 4) DEF(CTR_EL0, 3, 3, 0, 0, 1) diff --git a/target/arm/cpu.h b/target/arm/cpu.h index 008e530578..dc9b6dce4c 100644 --- a/target/arm/cpu.h +++ b/target/arm/cpu.h @@ -1082,7 +1082,6 @@ struct ArchCPU { uint32_t reset_sctlr; uint64_t pmceid0; uint64_t pmceid1; - uint64_t clidr; uint64_t mp_affinity; /* MP ID without feature bits */ /* The elements of this array are the CCSIDR values for each cache, * in the order L1DCache, L1ICache, L2DCache, L2ICache, etc. @@ -2945,7 +2944,7 @@ static inline bool arm_v7m_csselr_razwi(ARMCPU *cpu) /* If all the CLIDR.Ctypem bits are 0 there are no caches, and * CSSELR is RAZ/WI. */ - return (cpu->clidr & R_V7M_CLIDR_CTYPE_ALL_MASK) != 0; + return (GET_IDREG(&cpu->isar, CLIDR) & R_V7M_CLIDR_CTYPE_ALL_MASK) != 0; } static inline bool arm_sctlr_b(CPUARMState *env) diff --git a/target/arm/cpu64.c b/target/arm/cpu64.c index d648ea066c..26cf7e6dfa 100644 --- a/target/arm/cpu64.c +++ b/target/arm/cpu64.c @@ -683,7 +683,7 @@ static void aarch64_a57_initfn(Object *obj) cpu->isar.dbgdevid = 0x01110f13; cpu->isar.dbgdevid1 = 0x2; cpu->isar.reset_pmcr_el0 = 0x41013000; - cpu->clidr = 0x0a200023; + SET_IDREG(isar, CLIDR, 0x0a200023); /* 32KB L1 dcache */ cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 32 * KiB, 7); /* 48KB L1 icache */ @@ -745,7 +745,7 @@ static void aarch64_a53_initfn(Object *obj) cpu->isar.dbgdevid = 0x00110f13; cpu->isar.dbgdevid1 = 0x1; cpu->isar.reset_pmcr_el0 = 0x41033000; - cpu->clidr = 0x0a200023; + SET_IDREG(isar, CLIDR, 0x0a200023); /* 32KB L1 dcache */ cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 32 * KiB, 7); /* 32KB L1 icache */ diff --git a/target/arm/helper.c b/target/arm/helper.c index 93da8f170e..3ea9958ea7 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7889,7 +7889,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_tid4, .fgt = FGT_CLIDR_EL1, - .resetvalue = cpu->clidr + .resetvalue = GET_IDREG(isar, CLIDR) }; define_one_arm_cp_reg(cpu, &clidr); define_arm_cp_regs(cpu, v7_cp_reginfo); diff --git a/target/arm/tcg/cpu-v7m.c b/target/arm/tcg/cpu-v7m.c index a65b83fe99..dc249ce1f1 100644 --- a/target/arm/tcg/cpu-v7m.c +++ b/target/arm/tcg/cpu-v7m.c @@ -195,7 +195,7 @@ static void cortex_m33_initfn(Object *obj) SET_IDREG(isar, ID_ISAR4, 0x01310132); SET_IDREG(isar, ID_ISAR5, 0x00000000); SET_IDREG(isar, ID_ISAR6, 0x00000000); - cpu->clidr = 0x00000000; + SET_IDREG(isar, CLIDR, 0x00000000); cpu->ctr = 0x8000c000; } @@ -233,7 +233,7 @@ static void cortex_m55_initfn(Object *obj) SET_IDREG(isar, ID_ISAR4, 0x01310132); SET_IDREG(isar, ID_ISAR5, 0x00000000); SET_IDREG(isar, ID_ISAR6, 0x00000000); - cpu->clidr = 0x00000000; /* caches not implemented */ + SET_IDREG(isar, CLIDR, 0x00000000); /* caches not implemented */ cpu->ctr = 0x8303c003; } diff --git a/target/arm/tcg/cpu32.c b/target/arm/tcg/cpu32.c index 03cbe42f22..a2a23eae0d 100644 --- a/target/arm/tcg/cpu32.c +++ b/target/arm/tcg/cpu32.c @@ -371,7 +371,7 @@ static void cortex_a8_initfn(Object *obj) SET_IDREG(isar, ID_ISAR3, 0x11112131); SET_IDREG(isar, ID_ISAR4, 0x00111142); cpu->isar.dbgdidr = 0x15141000; - cpu->clidr = (1 << 27) | (2 << 24) | 3; + SET_IDREG(isar, CLIDR, (1 << 27) | (2 << 24) | 3); cpu->ccsidr[0] = 0xe007e01a; /* 16k L1 dcache. */ cpu->ccsidr[1] = 0x2007e01a; /* 16k L1 icache. */ cpu->ccsidr[2] = 0xf0000000; /* No L2 icache. */ @@ -447,7 +447,7 @@ static void cortex_a9_initfn(Object *obj) SET_IDREG(isar, ID_ISAR3, 0x11112131); SET_IDREG(isar, ID_ISAR4, 0x00111142); cpu->isar.dbgdidr = 0x35141000; - cpu->clidr = (1 << 27) | (1 << 24) | 3; + SET_IDREG(isar, CLIDR, (1 << 27) | (1 << 24) | 3); cpu->ccsidr[0] = 0xe00fe019; /* 16k L1 dcache. */ cpu->ccsidr[1] = 0x200fe019; /* 16k L1 icache. */ cpu->isar.reset_pmcr_el0 = 0x41093000; @@ -519,7 +519,7 @@ static void cortex_a7_initfn(Object *obj) cpu->isar.dbgdidr = 0x3515f005; cpu->isar.dbgdevid = 0x01110f13; cpu->isar.dbgdevid1 = 0x1; - cpu->clidr = 0x0a200023; + SET_IDREG(isar, CLIDR, 0x0a200023); cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */ cpu->ccsidr[2] = 0x711fe07a; /* 4096K L2 unified cache */ @@ -567,7 +567,7 @@ static void cortex_a15_initfn(Object *obj) cpu->isar.dbgdidr = 0x3515f021; cpu->isar.dbgdevid = 0x01110f13; cpu->isar.dbgdevid1 = 0x0; - cpu->clidr = 0x0a200023; + SET_IDREG(isar, CLIDR, 0x0a200023); cpu->ccsidr[0] = 0x701fe00a; /* 32K L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32K L1 icache */ cpu->ccsidr[2] = 0x711fe07a; /* 4096K L2 unified cache */ @@ -758,7 +758,7 @@ static void cortex_r52_initfn(Object *obj) SET_IDREG(isar, ID_ISAR4, 0x00010142); SET_IDREG(isar, ID_ISAR5, 0x00010001); cpu->isar.dbgdidr = 0x77168000; - cpu->clidr = (1 << 27) | (1 << 24) | 0x3; + SET_IDREG(isar, CLIDR, (1 << 27) | (1 << 24) | 0x3); cpu->ccsidr[0] = 0x700fe01a; /* 32KB L1 dcache */ cpu->ccsidr[1] = 0x201fe00a; /* 32KB L1 icache */ @@ -990,7 +990,7 @@ static void arm_max_initfn(Object *obj) SET_IDREG(isar, ID_ISAR5, 0x00011121); SET_IDREG(isar, ID_ISAR6, 0); cpu->isar.reset_pmcr_el0 = 0x41013000; - cpu->clidr = 0x0a200023; + SET_IDREG(isar, CLIDR, 0x0a200023); cpu->ccsidr[0] = 0x701fe00a; /* 32KB L1 dcache */ cpu->ccsidr[1] = 0x201fe012; /* 48KB L1 icache */ cpu->ccsidr[2] = 0x70ffe07a; /* 2048KB L2 cache */ diff --git a/target/arm/tcg/cpu64.c b/target/arm/tcg/cpu64.c index bcc8e2dfaf..35cddbafa4 100644 --- a/target/arm/tcg/cpu64.c +++ b/target/arm/tcg/cpu64.c @@ -71,7 +71,7 @@ static void aarch64_a35_initfn(Object *obj) SET_IDREG(isar, ID_AA64ISAR1, 0); SET_IDREG(isar, ID_AA64MMFR0, 0x00101122); SET_IDREG(isar, ID_AA64MMFR1, 0); - cpu->clidr = 0x0a200023; + SET_IDREG(isar, CLIDR, 0x0a200023); cpu->dcz_blocksize = 4; /* From B2.4 AArch64 Virtual Memory control registers */ @@ -216,7 +216,7 @@ static void aarch64_a55_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_PMU); /* Ordered by B2.4 AArch64 registers by functional group */ - cpu->clidr = 0x82000023; + SET_IDREG(isar, CLIDR, 0x82000023); cpu->ctr = 0x84448004; /* L1Ip = VIPT */ cpu->dcz_blocksize = 4; /* 64 bytes */ SET_IDREG(isar, ID_AA64DFR0, 0x0000000010305408ull); @@ -317,7 +317,7 @@ static void aarch64_a72_initfn(Object *obj) cpu->isar.dbgdevid = 0x01110f13; cpu->isar.dbgdevid1 = 0x2; cpu->isar.reset_pmcr_el0 = 0x41023000; - cpu->clidr = 0x0a200023; + SET_IDREG(isar, CLIDR, 0x0a200023); /* 32KB L1 dcache */ cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 64, 32 * KiB, 7); /* 48KB L1 dcache */ @@ -349,7 +349,7 @@ static void aarch64_a76_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_PMU); /* Ordered by B2.4 AArch64 registers by functional group */ - cpu->clidr = 0x82000023; + SET_IDREG(isar, CLIDR, 0x82000023); cpu->ctr = 0x8444C004; cpu->dcz_blocksize = 4; SET_IDREG(isar, ID_AA64DFR0, 0x0000000010305408ull); @@ -436,7 +436,7 @@ static void aarch64_a64fx_initfn(Object *obj) SET_IDREG(isar, ID_AA64ISAR0, 0x0000000010211120); SET_IDREG(isar, ID_AA64ISAR1, 0x0000000000010001); SET_IDREG(isar, ID_AA64ZFR0, 0x0000000000000000); - cpu->clidr = 0x0000000080000023; + SET_IDREG(isar, CLIDR, 0x0000000080000023); /* 64KB L1 dcache */ cpu->ccsidr[0] = make_ccsidr(CCSIDR_FORMAT_LEGACY, 4, 256, 64 * KiB, 7); /* 64KB L1 icache */ @@ -597,7 +597,7 @@ static void aarch64_neoverse_n1_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_PMU); /* Ordered by B2.4 AArch64 registers by functional group */ - cpu->clidr = 0x82000023; + SET_IDREG(isar, CLIDR, 0x82000023); cpu->ctr = 0x8444c004; cpu->dcz_blocksize = 4; SET_IDREG(isar, ID_AA64DFR0, 0x0000000110305408ull); @@ -673,7 +673,7 @@ static void aarch64_neoverse_v1_initfn(Object *obj) set_feature(&cpu->env, ARM_FEATURE_PMU); /* Ordered by 3.2.4 AArch64 registers by functional group */ - cpu->clidr = 0x82000023; + SET_IDREG(isar, CLIDR, 0x82000023); cpu->ctr = 0xb444c004; /* With DIC and IDC set */ cpu->dcz_blocksize = 4; SET_IDREG(isar, ID_AA64AFR0, 0x00000000); @@ -934,7 +934,7 @@ static void aarch64_a710_initfn(Object *obj) SET_IDREG(isar, ID_AA64MMFR0, 0x0000022200101122ull); SET_IDREG(isar, ID_AA64MMFR1, 0x0000000010212122ull); SET_IDREG(isar, ID_AA64MMFR2, 0x1221011110101011ull); - cpu->clidr = 0x0000001482000023ull; + SET_IDREG(isar, CLIDR, 0x0000001482000023ull); cpu->gm_blocksize = 4; cpu->ctr = 0x000000049444c004ull; cpu->dcz_blocksize = 4; @@ -1036,7 +1036,7 @@ static void aarch64_neoverse_n2_initfn(Object *obj) SET_IDREG(isar, ID_AA64MMFR0, 0x0000022200101125ull); SET_IDREG(isar, ID_AA64MMFR1, 0x0000000010212122ull); SET_IDREG(isar, ID_AA64MMFR2, 0x1221011112101011ull); - cpu->clidr = 0x0000001482000023ull; + SET_IDREG(isar, CLIDR, 0x0000001482000023ull); cpu->gm_blocksize = 4; cpu->ctr = 0x00000004b444c004ull; cpu->dcz_blocksize = 4; @@ -1125,10 +1125,10 @@ void aarch64_max_tcg_initfn(Object *obj) * We're going to set FEAT_S2FWB, which mandates that CLIDR_EL1.{LoUU,LoUIS} * are zero. */ - u = cpu->clidr; + u = GET_IDREG(isar, CLIDR); u = FIELD_DP32(u, CLIDR_EL1, LOUIS, 0); u = FIELD_DP32(u, CLIDR_EL1, LOUU, 0); - cpu->clidr = u; + SET_IDREG(isar, CLIDR, u); /* * Set CTR_EL0.DIC and IDC to tell the guest it doesnt' need to From 3f1772cbdc152125d84d2f6e75ec06799a498b4e Mon Sep 17 00:00:00 2001 From: Cornelia Huck Date: Fri, 4 Jul 2025 16:19:27 +0200 Subject: [PATCH 2075/2760] arm/kvm: shorten one overly long line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: 804cfc7eedb7 ("arm/cpu: Store aa64isar0/aa64zfr0 into the idregs arrays") Signed-off-by: Cornelia Huck Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Eric Auger Tested-by: Philippe Mathieu-Daudé Message-id: 20250704141927.38963-6-cohuck@redhat.com Signed-off-by: Peter Maydell --- target/arm/kvm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 426f8b159e..8ab0d692d3 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -230,7 +230,8 @@ static uint64_t idregs_sysreg_to_kvm_reg(ARMSysRegs sysreg) } /* read a sysreg value and store it in the idregs */ -static int get_host_cpu_reg(int fd, ARMHostCPUFeatures *ahcf, ARMIDRegisterIdx index) +static int get_host_cpu_reg(int fd, ARMHostCPUFeatures *ahcf, + ARMIDRegisterIdx index) { uint64_t *reg; int ret; From dcef48a56556235578b9e4ea3b4a13e01d9d95e8 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 7 Jul 2025 09:15:45 -0600 Subject: [PATCH 2076/2760] target/arm: Drop stub for define_tlb_insn_regs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow the call to be compiled out by protecting it with tcg_enabled. Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250707151547.196393-2-richard.henderson@linaro.org Signed-off-by: Peter Maydell --- target/arm/helper.c | 4 +++- target/arm/tcg-stubs.c | 5 ----- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/target/arm/helper.c b/target/arm/helper.c index 3ea9958ea7..c1b684e3d1 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -7771,7 +7771,9 @@ void register_cp_regs_for_features(ARMCPU *cpu) } #ifndef CONFIG_USER_ONLY - define_tlb_insn_regs(cpu); + if (tcg_enabled()) { + define_tlb_insn_regs(cpu); + } #endif if (arm_feature(env, ARM_FEATURE_V6)) { diff --git a/target/arm/tcg-stubs.c b/target/arm/tcg-stubs.c index 5e5166c049..aac99b2672 100644 --- a/target/arm/tcg-stubs.c +++ b/target/arm/tcg-stubs.c @@ -22,11 +22,6 @@ void raise_exception_ra(CPUARMState *env, uint32_t excp, uint32_t syndrome, g_assert_not_reached(); } -/* TLBI insns are only used by TCG, so we don't need to do anything for KVM */ -void define_tlb_insn_regs(ARMCPU *cpu) -{ -} - /* With KVM, we never use float_status, so these can be no-ops */ void arm_set_default_fp_behaviours(float_status *s) { From a62fa0c7a00fab51459c68986e9259c389f1e5d6 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 7 Jul 2025 09:15:46 -0600 Subject: [PATCH 2077/2760] target/arm: Split out AT insns to tcg/cpregs-at.c Split out all "system instructions for address translation". While mapped into "cpregs", these are instructions, and thus are handled in hardware by virtualization. They are all priviledged, and thus not reachable for user-only. Signed-off-by: Richard Henderson Message-id: 20250707151547.196393-3-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/helper.c | 514 +----------------------------------- target/arm/internals.h | 3 + target/arm/tcg/cpregs-at.c | 519 +++++++++++++++++++++++++++++++++++++ target/arm/tcg/meson.build | 1 + 4 files changed, 525 insertions(+), 512 deletions(-) create mode 100644 target/arm/tcg/cpregs-at.c diff --git a/target/arm/helper.c b/target/arm/helper.c index c1b684e3d1..0883246905 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -270,7 +270,7 @@ void init_cpreg_list(ARMCPU *cpu) g_list_free(keys); } -static bool arm_pan_enabled(CPUARMState *env) +bool arm_pan_enabled(CPUARMState *env) { if (is_a64(env)) { if ((arm_hcr_el2_eff(env) & (HCR_NV | HCR_NV1)) == (HCR_NV | HCR_NV1)) { @@ -3448,402 +3448,6 @@ static void par_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) } } -#ifndef CONFIG_USER_ONLY -/* get_phys_addr() isn't present for user-mode-only targets */ - -static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - if (ri->opc2 & 4) { - /* - * The ATS12NSO* operations must trap to EL3 or EL2 if executed in - * Secure EL1 (which can only happen if EL3 is AArch64). - * They are simply UNDEF if executed from NS EL1. - * They function normally from EL2 or EL3. - */ - if (arm_current_el(env) == 1) { - if (arm_is_secure_below_el3(env)) { - if (env->cp15.scr_el3 & SCR_EEL2) { - return CP_ACCESS_TRAP_EL2; - } - return CP_ACCESS_TRAP_EL3; - } - return CP_ACCESS_UNDEFINED; - } - } - return CP_ACCESS_OK; -} - -#ifdef CONFIG_TCG -static int par_el1_shareability(GetPhysAddrResult *res) -{ - /* - * The PAR_EL1.SH field must be 0b10 for Device or Normal-NC - * memory -- see pseudocode PAREncodeShareability(). - */ - if (((res->cacheattrs.attrs & 0xf0) == 0) || - res->cacheattrs.attrs == 0x44 || res->cacheattrs.attrs == 0x40) { - return 2; - } - return res->cacheattrs.shareability; -} - -static uint64_t do_ats_write(CPUARMState *env, uint64_t value, - MMUAccessType access_type, ARMMMUIdx mmu_idx, - ARMSecuritySpace ss) -{ - bool ret; - uint64_t par64; - bool format64 = false; - ARMMMUFaultInfo fi = {}; - GetPhysAddrResult res = {}; - - /* - * I_MXTJT: Granule protection checks are not performed on the final - * address of a successful translation. This is a translation not a - * memory reference, so "memop = none = 0". - */ - ret = get_phys_addr_with_space_nogpc(env, value, access_type, 0, - mmu_idx, ss, &res, &fi); - - /* - * ATS operations only do S1 or S1+S2 translations, so we never - * have to deal with the ARMCacheAttrs format for S2 only. - */ - assert(!res.cacheattrs.is_s2_format); - - if (ret) { - /* - * Some kinds of translation fault must cause exceptions rather - * than being reported in the PAR. - */ - int current_el = arm_current_el(env); - int target_el; - uint32_t syn, fsr, fsc; - bool take_exc = false; - - if (fi.s1ptw && current_el == 1 - && arm_mmu_idx_is_stage1_of_2(mmu_idx)) { - /* - * Synchronous stage 2 fault on an access made as part of the - * translation table walk for AT S1E0* or AT S1E1* insn - * executed from NS EL1. If this is a synchronous external abort - * and SCR_EL3.EA == 1, then we take a synchronous external abort - * to EL3. Otherwise the fault is taken as an exception to EL2, - * and HPFAR_EL2 holds the faulting IPA. - */ - if (fi.type == ARMFault_SyncExternalOnWalk && - (env->cp15.scr_el3 & SCR_EA)) { - target_el = 3; - } else { - env->cp15.hpfar_el2 = extract64(fi.s2addr, 12, 47) << 4; - if (arm_is_secure_below_el3(env) && fi.s1ns) { - env->cp15.hpfar_el2 |= HPFAR_NS; - } - target_el = 2; - } - take_exc = true; - } else if (fi.type == ARMFault_SyncExternalOnWalk) { - /* - * Synchronous external aborts during a translation table walk - * are taken as Data Abort exceptions. - */ - if (fi.stage2) { - if (current_el == 3) { - target_el = 3; - } else { - target_el = 2; - } - } else { - target_el = exception_target_el(env); - } - take_exc = true; - } - - if (take_exc) { - /* Construct FSR and FSC using same logic as arm_deliver_fault() */ - if (target_el == 2 || arm_el_is_aa64(env, target_el) || - arm_s1_regime_using_lpae_format(env, mmu_idx)) { - fsr = arm_fi_to_lfsc(&fi); - fsc = extract32(fsr, 0, 6); - } else { - fsr = arm_fi_to_sfsc(&fi); - fsc = 0x3f; - } - /* - * Report exception with ESR indicating a fault due to a - * translation table walk for a cache maintenance instruction. - */ - syn = syn_data_abort_no_iss(current_el == target_el, 0, - fi.ea, 1, fi.s1ptw, 1, fsc); - env->exception.vaddress = value; - env->exception.fsr = fsr; - raise_exception(env, EXCP_DATA_ABORT, syn, target_el); - } - } - - if (is_a64(env)) { - format64 = true; - } else if (arm_feature(env, ARM_FEATURE_LPAE)) { - /* - * ATS1Cxx: - * * TTBCR.EAE determines whether the result is returned using the - * 32-bit or the 64-bit PAR format - * * Instructions executed in Hyp mode always use the 64bit format - * - * ATS1S2NSOxx uses the 64bit format if any of the following is true: - * * The Non-secure TTBCR.EAE bit is set to 1 - * * The implementation includes EL2, and the value of HCR.VM is 1 - * - * (Note that HCR.DC makes HCR.VM behave as if it is 1.) - * - * ATS1Hx always uses the 64bit format. - */ - format64 = arm_s1_regime_using_lpae_format(env, mmu_idx); - - if (arm_feature(env, ARM_FEATURE_EL2)) { - if (mmu_idx == ARMMMUIdx_E10_0 || - mmu_idx == ARMMMUIdx_E10_1 || - mmu_idx == ARMMMUIdx_E10_1_PAN) { - format64 |= env->cp15.hcr_el2 & (HCR_VM | HCR_DC); - } else { - format64 |= arm_current_el(env) == 2; - } - } - } - - if (format64) { - /* Create a 64-bit PAR */ - par64 = (1 << 11); /* LPAE bit always set */ - if (!ret) { - par64 |= res.f.phys_addr & ~0xfffULL; - if (!res.f.attrs.secure) { - par64 |= (1 << 9); /* NS */ - } - par64 |= (uint64_t)res.cacheattrs.attrs << 56; /* ATTR */ - par64 |= par_el1_shareability(&res) << 7; /* SH */ - } else { - uint32_t fsr = arm_fi_to_lfsc(&fi); - - par64 |= 1; /* F */ - par64 |= (fsr & 0x3f) << 1; /* FS */ - if (fi.stage2) { - par64 |= (1 << 9); /* S */ - } - if (fi.s1ptw) { - par64 |= (1 << 8); /* PTW */ - } - } - } else { - /* - * fsr is a DFSR/IFSR value for the short descriptor - * translation table format (with WnR always clear). - * Convert it to a 32-bit PAR. - */ - if (!ret) { - /* We do not set any attribute bits in the PAR */ - if (res.f.lg_page_size == 24 - && arm_feature(env, ARM_FEATURE_V7)) { - par64 = (res.f.phys_addr & 0xff000000) | (1 << 1); - } else { - par64 = res.f.phys_addr & 0xfffff000; - } - if (!res.f.attrs.secure) { - par64 |= (1 << 9); /* NS */ - } - } else { - uint32_t fsr = arm_fi_to_sfsc(&fi); - - par64 = ((fsr & (1 << 10)) >> 5) | ((fsr & (1 << 12)) >> 6) | - ((fsr & 0xf) << 1) | 1; - } - } - return par64; -} -#endif /* CONFIG_TCG */ - -static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) -{ -#ifdef CONFIG_TCG - MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; - uint64_t par64; - ARMMMUIdx mmu_idx; - int el = arm_current_el(env); - ARMSecuritySpace ss = arm_security_space(env); - - switch (ri->opc2 & 6) { - case 0: - /* stage 1 current state PL1: ATS1CPR, ATS1CPW, ATS1CPRP, ATS1CPWP */ - switch (el) { - case 3: - if (ri->crm == 9 && arm_pan_enabled(env)) { - mmu_idx = ARMMMUIdx_E30_3_PAN; - } else { - mmu_idx = ARMMMUIdx_E3; - } - break; - case 2: - g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */ - /* fall through */ - case 1: - if (ri->crm == 9 && arm_pan_enabled(env)) { - mmu_idx = ARMMMUIdx_Stage1_E1_PAN; - } else { - mmu_idx = ARMMMUIdx_Stage1_E1; - } - break; - default: - g_assert_not_reached(); - } - break; - case 2: - /* stage 1 current state PL0: ATS1CUR, ATS1CUW */ - switch (el) { - case 3: - mmu_idx = ARMMMUIdx_E30_0; - break; - case 2: - g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */ - mmu_idx = ARMMMUIdx_Stage1_E0; - break; - case 1: - mmu_idx = ARMMMUIdx_Stage1_E0; - break; - default: - g_assert_not_reached(); - } - break; - case 4: - /* stage 1+2 NonSecure PL1: ATS12NSOPR, ATS12NSOPW */ - mmu_idx = ARMMMUIdx_E10_1; - ss = ARMSS_NonSecure; - break; - case 6: - /* stage 1+2 NonSecure PL0: ATS12NSOUR, ATS12NSOUW */ - mmu_idx = ARMMMUIdx_E10_0; - ss = ARMSS_NonSecure; - break; - default: - g_assert_not_reached(); - } - - par64 = do_ats_write(env, value, access_type, mmu_idx, ss); - - A32_BANKED_CURRENT_REG_SET(env, par, par64); -#else - /* Handled by hardware accelerator. */ - g_assert_not_reached(); -#endif /* CONFIG_TCG */ -} - -static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ -#ifdef CONFIG_TCG - MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; - uint64_t par64; - - /* There is no SecureEL2 for AArch32. */ - par64 = do_ats_write(env, value, access_type, ARMMMUIdx_E2, - ARMSS_NonSecure); - - A32_BANKED_CURRENT_REG_SET(env, par, par64); -#else - /* Handled by hardware accelerator. */ - g_assert_not_reached(); -#endif /* CONFIG_TCG */ -} - -static CPAccessResult at_e012_access(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - /* - * R_NYXTL: instruction is UNDEFINED if it applies to an Exception level - * lower than EL3 and the combination SCR_EL3.{NSE,NS} is reserved. This can - * only happen when executing at EL3 because that combination also causes an - * illegal exception return. We don't need to check FEAT_RME either, because - * scr_write() ensures that the NSE bit is not set otherwise. - */ - if ((env->cp15.scr_el3 & (SCR_NSE | SCR_NS)) == SCR_NSE) { - return CP_ACCESS_UNDEFINED; - } - return CP_ACCESS_OK; -} - -static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - if (arm_current_el(env) == 3 && - !(env->cp15.scr_el3 & (SCR_NS | SCR_EEL2))) { - return CP_ACCESS_UNDEFINED; - } - return at_e012_access(env, ri, isread); -} - -static CPAccessResult at_s1e01_access(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_AT)) { - return CP_ACCESS_TRAP_EL2; - } - return at_e012_access(env, ri, isread); -} - -static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ -#ifdef CONFIG_TCG - MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; - ARMMMUIdx mmu_idx; - uint64_t hcr_el2 = arm_hcr_el2_eff(env); - bool regime_e20 = (hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE); - bool for_el3 = false; - ARMSecuritySpace ss; - - switch (ri->opc2 & 6) { - case 0: - switch (ri->opc1) { - case 0: /* AT S1E1R, AT S1E1W, AT S1E1RP, AT S1E1WP */ - if (ri->crm == 9 && arm_pan_enabled(env)) { - mmu_idx = regime_e20 ? - ARMMMUIdx_E20_2_PAN : ARMMMUIdx_Stage1_E1_PAN; - } else { - mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_Stage1_E1; - } - break; - case 4: /* AT S1E2R, AT S1E2W */ - mmu_idx = hcr_el2 & HCR_E2H ? ARMMMUIdx_E20_2 : ARMMMUIdx_E2; - break; - case 6: /* AT S1E3R, AT S1E3W */ - mmu_idx = ARMMMUIdx_E3; - for_el3 = true; - break; - default: - g_assert_not_reached(); - } - break; - case 2: /* AT S1E0R, AT S1E0W */ - mmu_idx = regime_e20 ? ARMMMUIdx_E20_0 : ARMMMUIdx_Stage1_E0; - break; - case 4: /* AT S12E1R, AT S12E1W */ - mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_E10_1; - break; - case 6: /* AT S12E0R, AT S12E0W */ - mmu_idx = regime_e20 ? ARMMMUIdx_E20_0 : ARMMMUIdx_E10_0; - break; - default: - g_assert_not_reached(); - } - - ss = for_el3 ? arm_security_space(env) : arm_security_space_below_el3(env); - env->cp15.par_el[1] = do_ats_write(env, value, access_type, mmu_idx, ss); -#else - /* Handled by hardware accelerator. */ - g_assert_not_reached(); -#endif /* CONFIG_TCG */ -} -#endif - /* Return basic MPU access permission bits. */ static uint32_t simple_mpu_ap_bits(uint32_t val) { @@ -5094,53 +4698,6 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 14, .opc2 = 2, .fgt = FGT_DCCISW, .access = PL1_W, .accessfn = access_tsw, .type = ARM_CP_NOP }, -#ifndef CONFIG_USER_ONLY - /* 64 bit address translation operations */ - { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .fgt = FGT_ATS1E1R, - .accessfn = at_s1e01_access, .writefn = ats_write64 }, - { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .fgt = FGT_ATS1E1W, - .accessfn = at_s1e01_access, .writefn = ats_write64 }, - { .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2, - .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .fgt = FGT_ATS1E0R, - .accessfn = at_s1e01_access, .writefn = ats_write64 }, - { .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3, - .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .fgt = FGT_ATS1E0W, - .accessfn = at_s1e01_access, .writefn = ats_write64 }, - { .name = "AT_S12E1R", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 4, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .accessfn = at_e012_access, .writefn = ats_write64 }, - { .name = "AT_S12E1W", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 5, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .accessfn = at_e012_access, .writefn = ats_write64 }, - { .name = "AT_S12E0R", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 6, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .accessfn = at_e012_access, .writefn = ats_write64 }, - { .name = "AT_S12E0W", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 7, - .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .accessfn = at_e012_access, .writefn = ats_write64 }, - /* AT S1E2* are elsewhere as they UNDEF from EL3 if EL2 is not present */ - { .name = "AT_S1E3R", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 0, - .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write64 }, - { .name = "AT_S1E3W", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 1, - .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write64 }, { .name = "PAR_EL1", .state = ARM_CP_STATE_AA64, .type = ARM_CP_ALIAS, .opc0 = 3, .opc1 = 0, .crn = 7, .crm = 4, .opc2 = 0, @@ -5148,7 +4705,6 @@ static const ARMCPRegInfo v8_cp_reginfo[] = { .fgt = FGT_PAR_EL1, .fieldoffset = offsetof(CPUARMState, cp15.par_el[1]), .writefn = par_write }, -#endif /* 32 bit cache operations */ { .name = "ICIALLUIS", .cp = 15, .opc1 = 0, .crn = 7, .crm = 1, .opc2 = 0, .type = ARM_CP_NOP, .access = PL1_W, .accessfn = access_ticab }, @@ -5751,33 +5307,6 @@ static const ARMCPRegInfo el2_cp_reginfo[] = { .access = PL2_RW, .type = ARM_CP_64BIT | ARM_CP_ALIAS, .fieldoffset = offsetof(CPUARMState, cp15.ttbr0_el[2]) }, #ifndef CONFIG_USER_ONLY - /* - * Unlike the other EL2-related AT operations, these must - * UNDEF from EL3 if EL2 is not implemented, which is why we - * define them here rather than with the rest of the AT ops. - */ - { .name = "AT_S1E2R", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0, - .access = PL2_W, .accessfn = at_s1e2_access, - .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = ats_write64 }, - { .name = "AT_S1E2W", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 1, - .access = PL2_W, .accessfn = at_s1e2_access, - .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC | ARM_CP_EL3_NO_EL2_UNDEF, - .writefn = ats_write64 }, - /* - * The AArch32 ATS1H* operations are CONSTRAINED UNPREDICTABLE - * if EL2 is not implemented; we choose to UNDEF. Behaviour at EL3 - * with SCR.NS == 0 outside Monitor mode is UNPREDICTABLE; we choose - * to behave as if SCR.NS was 1. - */ - { .name = "ATS1HR", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0, - .access = PL2_W, - .writefn = ats1h_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, - { .name = "ATS1HW", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 1, - .access = PL2_W, - .writefn = ats1h_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, { .name = "CNTHCTL_EL2", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .opc1 = 4, .crn = 14, .crm = 1, .opc2 = 0, /* @@ -7704,32 +7233,6 @@ static const ARMCPRegInfo vhe_reginfo[] = { #endif }; -#ifndef CONFIG_USER_ONLY -static const ARMCPRegInfo ats1e1_reginfo[] = { - { .name = "AT_S1E1RP", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .fgt = FGT_ATS1E1RP, - .accessfn = at_s1e01_access, .writefn = ats_write64 }, - { .name = "AT_S1E1WP", .state = ARM_CP_STATE_AA64, - .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .fgt = FGT_ATS1E1WP, - .accessfn = at_s1e01_access, .writefn = ats_write64 }, -}; - -static const ARMCPRegInfo ats1cp_reginfo[] = { - { .name = "ATS1CPRP", - .cp = 15, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, - .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write }, - { .name = "ATS1CPWP", - .cp = 15, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, - .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, - .writefn = ats_write }, -}; -#endif - /* * ACTLR2 and HACTLR2 map to ACTLR_EL1[63:32] and * ACTLR_EL2[63:32]. They exist only if the ID_MMFR4.AC2 field @@ -7773,6 +7276,7 @@ void register_cp_regs_for_features(ARMCPU *cpu) #ifndef CONFIG_USER_ONLY if (tcg_enabled()) { define_tlb_insn_regs(cpu); + define_at_insn_regs(cpu); } #endif @@ -8506,12 +8010,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) .bank_fieldoffsets = { offsetoflow32(CPUARMState, cp15.par_s), offsetoflow32(CPUARMState, cp15.par_ns) }, .writefn = par_write}, -#ifndef CONFIG_USER_ONLY - /* This underdecoding is safe because the reginfo is NO_RAW. */ - { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, - .access = PL1_W, .accessfn = ats_access, - .writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, -#endif }; /* @@ -8917,14 +8415,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (cpu_isar_feature(aa64_pan, cpu)) { define_one_arm_cp_reg(cpu, &pan_reginfo); } -#ifndef CONFIG_USER_ONLY - if (cpu_isar_feature(aa64_ats1e1, cpu)) { - define_arm_cp_regs(cpu, ats1e1_reginfo); - } - if (cpu_isar_feature(aa32_ats1e1, cpu)) { - define_arm_cp_regs(cpu, ats1cp_reginfo); - } -#endif if (cpu_isar_feature(aa64_uao, cpu)) { define_one_arm_cp_reg(cpu, &uao_reginfo); } diff --git a/target/arm/internals.h b/target/arm/internals.h index 21a8d67edd..bcaf8965fc 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1871,6 +1871,8 @@ void define_debug_regs(ARMCPU *cpu); /* Add the cpreg definitions for TLBI instructions */ void define_tlb_insn_regs(ARMCPU *cpu); +/* Add the cpreg definitions for AT instructions */ +void define_at_insn_regs(ARMCPU *cpu); /* Effective value of MDCR_EL2 */ static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env) @@ -1981,5 +1983,6 @@ void vfp_clear_float_status_exc_flags(CPUARMState *env); * specified by mask changing to the values in val. */ void vfp_set_fpcr_to_host(CPUARMState *env, uint32_t val, uint32_t mask); +bool arm_pan_enabled(CPUARMState *env); #endif diff --git a/target/arm/tcg/cpregs-at.c b/target/arm/tcg/cpregs-at.c new file mode 100644 index 0000000000..398a61d398 --- /dev/null +++ b/target/arm/tcg/cpregs-at.c @@ -0,0 +1,519 @@ +/* + * System instructions for address translation + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "cpu-features.h" +#include "internals.h" +#include "cpregs.h" + + +static int par_el1_shareability(GetPhysAddrResult *res) +{ + /* + * The PAR_EL1.SH field must be 0b10 for Device or Normal-NC + * memory -- see pseudocode PAREncodeShareability(). + */ + if (((res->cacheattrs.attrs & 0xf0) == 0) || + res->cacheattrs.attrs == 0x44 || res->cacheattrs.attrs == 0x40) { + return 2; + } + return res->cacheattrs.shareability; +} + +static uint64_t do_ats_write(CPUARMState *env, uint64_t value, + MMUAccessType access_type, ARMMMUIdx mmu_idx, + ARMSecuritySpace ss) +{ + bool ret; + uint64_t par64; + bool format64 = false; + ARMMMUFaultInfo fi = {}; + GetPhysAddrResult res = {}; + + /* + * I_MXTJT: Granule protection checks are not performed on the final + * address of a successful translation. This is a translation not a + * memory reference, so "memop = none = 0". + */ + ret = get_phys_addr_with_space_nogpc(env, value, access_type, 0, + mmu_idx, ss, &res, &fi); + + /* + * ATS operations only do S1 or S1+S2 translations, so we never + * have to deal with the ARMCacheAttrs format for S2 only. + */ + assert(!res.cacheattrs.is_s2_format); + + if (ret) { + /* + * Some kinds of translation fault must cause exceptions rather + * than being reported in the PAR. + */ + int current_el = arm_current_el(env); + int target_el; + uint32_t syn, fsr, fsc; + bool take_exc = false; + + if (fi.s1ptw && current_el == 1 + && arm_mmu_idx_is_stage1_of_2(mmu_idx)) { + /* + * Synchronous stage 2 fault on an access made as part of the + * translation table walk for AT S1E0* or AT S1E1* insn + * executed from NS EL1. If this is a synchronous external abort + * and SCR_EL3.EA == 1, then we take a synchronous external abort + * to EL3. Otherwise the fault is taken as an exception to EL2, + * and HPFAR_EL2 holds the faulting IPA. + */ + if (fi.type == ARMFault_SyncExternalOnWalk && + (env->cp15.scr_el3 & SCR_EA)) { + target_el = 3; + } else { + env->cp15.hpfar_el2 = extract64(fi.s2addr, 12, 47) << 4; + if (arm_is_secure_below_el3(env) && fi.s1ns) { + env->cp15.hpfar_el2 |= HPFAR_NS; + } + target_el = 2; + } + take_exc = true; + } else if (fi.type == ARMFault_SyncExternalOnWalk) { + /* + * Synchronous external aborts during a translation table walk + * are taken as Data Abort exceptions. + */ + if (fi.stage2) { + if (current_el == 3) { + target_el = 3; + } else { + target_el = 2; + } + } else { + target_el = exception_target_el(env); + } + take_exc = true; + } + + if (take_exc) { + /* Construct FSR and FSC using same logic as arm_deliver_fault() */ + if (target_el == 2 || arm_el_is_aa64(env, target_el) || + arm_s1_regime_using_lpae_format(env, mmu_idx)) { + fsr = arm_fi_to_lfsc(&fi); + fsc = extract32(fsr, 0, 6); + } else { + fsr = arm_fi_to_sfsc(&fi); + fsc = 0x3f; + } + /* + * Report exception with ESR indicating a fault due to a + * translation table walk for a cache maintenance instruction. + */ + syn = syn_data_abort_no_iss(current_el == target_el, 0, + fi.ea, 1, fi.s1ptw, 1, fsc); + env->exception.vaddress = value; + env->exception.fsr = fsr; + raise_exception(env, EXCP_DATA_ABORT, syn, target_el); + } + } + + if (is_a64(env)) { + format64 = true; + } else if (arm_feature(env, ARM_FEATURE_LPAE)) { + /* + * ATS1Cxx: + * * TTBCR.EAE determines whether the result is returned using the + * 32-bit or the 64-bit PAR format + * * Instructions executed in Hyp mode always use the 64bit format + * + * ATS1S2NSOxx uses the 64bit format if any of the following is true: + * * The Non-secure TTBCR.EAE bit is set to 1 + * * The implementation includes EL2, and the value of HCR.VM is 1 + * + * (Note that HCR.DC makes HCR.VM behave as if it is 1.) + * + * ATS1Hx always uses the 64bit format. + */ + format64 = arm_s1_regime_using_lpae_format(env, mmu_idx); + + if (arm_feature(env, ARM_FEATURE_EL2)) { + if (mmu_idx == ARMMMUIdx_E10_0 || + mmu_idx == ARMMMUIdx_E10_1 || + mmu_idx == ARMMMUIdx_E10_1_PAN) { + format64 |= env->cp15.hcr_el2 & (HCR_VM | HCR_DC); + } else { + format64 |= arm_current_el(env) == 2; + } + } + } + + if (format64) { + /* Create a 64-bit PAR */ + par64 = (1 << 11); /* LPAE bit always set */ + if (!ret) { + par64 |= res.f.phys_addr & ~0xfffULL; + if (!res.f.attrs.secure) { + par64 |= (1 << 9); /* NS */ + } + par64 |= (uint64_t)res.cacheattrs.attrs << 56; /* ATTR */ + par64 |= par_el1_shareability(&res) << 7; /* SH */ + } else { + uint32_t fsr = arm_fi_to_lfsc(&fi); + + par64 |= 1; /* F */ + par64 |= (fsr & 0x3f) << 1; /* FS */ + if (fi.stage2) { + par64 |= (1 << 9); /* S */ + } + if (fi.s1ptw) { + par64 |= (1 << 8); /* PTW */ + } + } + } else { + /* + * fsr is a DFSR/IFSR value for the short descriptor + * translation table format (with WnR always clear). + * Convert it to a 32-bit PAR. + */ + if (!ret) { + /* We do not set any attribute bits in the PAR */ + if (res.f.lg_page_size == 24 + && arm_feature(env, ARM_FEATURE_V7)) { + par64 = (res.f.phys_addr & 0xff000000) | (1 << 1); + } else { + par64 = res.f.phys_addr & 0xfffff000; + } + if (!res.f.attrs.secure) { + par64 |= (1 << 9); /* NS */ + } + } else { + uint32_t fsr = arm_fi_to_sfsc(&fi); + + par64 = ((fsr & (1 << 10)) >> 5) | ((fsr & (1 << 12)) >> 6) | + ((fsr & 0xf) << 1) | 1; + } + } + return par64; +} + +static void ats_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) +{ + MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; + uint64_t par64; + ARMMMUIdx mmu_idx; + int el = arm_current_el(env); + ARMSecuritySpace ss = arm_security_space(env); + + switch (ri->opc2 & 6) { + case 0: + /* stage 1 current state PL1: ATS1CPR, ATS1CPW, ATS1CPRP, ATS1CPWP */ + switch (el) { + case 3: + if (ri->crm == 9 && arm_pan_enabled(env)) { + mmu_idx = ARMMMUIdx_E30_3_PAN; + } else { + mmu_idx = ARMMMUIdx_E3; + } + break; + case 2: + g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */ + /* fall through */ + case 1: + if (ri->crm == 9 && arm_pan_enabled(env)) { + mmu_idx = ARMMMUIdx_Stage1_E1_PAN; + } else { + mmu_idx = ARMMMUIdx_Stage1_E1; + } + break; + default: + g_assert_not_reached(); + } + break; + case 2: + /* stage 1 current state PL0: ATS1CUR, ATS1CUW */ + switch (el) { + case 3: + mmu_idx = ARMMMUIdx_E30_0; + break; + case 2: + g_assert(ss != ARMSS_Secure); /* ARMv8.4-SecEL2 is 64-bit only */ + mmu_idx = ARMMMUIdx_Stage1_E0; + break; + case 1: + mmu_idx = ARMMMUIdx_Stage1_E0; + break; + default: + g_assert_not_reached(); + } + break; + case 4: + /* stage 1+2 NonSecure PL1: ATS12NSOPR, ATS12NSOPW */ + mmu_idx = ARMMMUIdx_E10_1; + ss = ARMSS_NonSecure; + break; + case 6: + /* stage 1+2 NonSecure PL0: ATS12NSOUR, ATS12NSOUW */ + mmu_idx = ARMMMUIdx_E10_0; + ss = ARMSS_NonSecure; + break; + default: + g_assert_not_reached(); + } + + par64 = do_ats_write(env, value, access_type, mmu_idx, ss); + + A32_BANKED_CURRENT_REG_SET(env, par, par64); +} + +static void ats1h_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; + uint64_t par64; + + /* There is no SecureEL2 for AArch32. */ + par64 = do_ats_write(env, value, access_type, ARMMMUIdx_E2, + ARMSS_NonSecure); + + A32_BANKED_CURRENT_REG_SET(env, par, par64); +} + +static CPAccessResult at_e012_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* + * R_NYXTL: instruction is UNDEFINED if it applies to an Exception level + * lower than EL3 and the combination SCR_EL3.{NSE,NS} is reserved. This can + * only happen when executing at EL3 because that combination also causes an + * illegal exception return. We don't need to check FEAT_RME either, because + * scr_write() ensures that the NSE bit is not set otherwise. + */ + if ((env->cp15.scr_el3 & (SCR_NSE | SCR_NS)) == SCR_NSE) { + return CP_ACCESS_UNDEFINED; + } + return CP_ACCESS_OK; +} + +static CPAccessResult at_s1e2_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 3 && + !(env->cp15.scr_el3 & (SCR_NS | SCR_EEL2))) { + return CP_ACCESS_UNDEFINED; + } + return at_e012_access(env, ri, isread); +} + +static CPAccessResult at_s1e01_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (arm_current_el(env) == 1 && (arm_hcr_el2_eff(env) & HCR_AT)) { + return CP_ACCESS_TRAP_EL2; + } + return at_e012_access(env, ri, isread); +} + +static void ats_write64(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + MMUAccessType access_type = ri->opc2 & 1 ? MMU_DATA_STORE : MMU_DATA_LOAD; + ARMMMUIdx mmu_idx; + uint64_t hcr_el2 = arm_hcr_el2_eff(env); + bool regime_e20 = (hcr_el2 & (HCR_E2H | HCR_TGE)) == (HCR_E2H | HCR_TGE); + bool for_el3 = false; + ARMSecuritySpace ss; + + switch (ri->opc2 & 6) { + case 0: + switch (ri->opc1) { + case 0: /* AT S1E1R, AT S1E1W, AT S1E1RP, AT S1E1WP */ + if (ri->crm == 9 && arm_pan_enabled(env)) { + mmu_idx = regime_e20 ? + ARMMMUIdx_E20_2_PAN : ARMMMUIdx_Stage1_E1_PAN; + } else { + mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_Stage1_E1; + } + break; + case 4: /* AT S1E2R, AT S1E2W */ + mmu_idx = hcr_el2 & HCR_E2H ? ARMMMUIdx_E20_2 : ARMMMUIdx_E2; + break; + case 6: /* AT S1E3R, AT S1E3W */ + mmu_idx = ARMMMUIdx_E3; + for_el3 = true; + break; + default: + g_assert_not_reached(); + } + break; + case 2: /* AT S1E0R, AT S1E0W */ + mmu_idx = regime_e20 ? ARMMMUIdx_E20_0 : ARMMMUIdx_Stage1_E0; + break; + case 4: /* AT S12E1R, AT S12E1W */ + mmu_idx = regime_e20 ? ARMMMUIdx_E20_2 : ARMMMUIdx_E10_1; + break; + case 6: /* AT S12E0R, AT S12E0W */ + mmu_idx = regime_e20 ? ARMMMUIdx_E20_0 : ARMMMUIdx_E10_0; + break; + default: + g_assert_not_reached(); + } + + ss = for_el3 ? arm_security_space(env) : arm_security_space_below_el3(env); + env->cp15.par_el[1] = do_ats_write(env, value, access_type, mmu_idx, ss); +} + +static CPAccessResult ats_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + if (ri->opc2 & 4) { + /* + * The ATS12NSO* operations must trap to EL3 or EL2 if executed in + * Secure EL1 (which can only happen if EL3 is AArch64). + * They are simply UNDEF if executed from NS EL1. + * They function normally from EL2 or EL3. + */ + if (arm_current_el(env) == 1) { + if (arm_is_secure_below_el3(env)) { + if (env->cp15.scr_el3 & SCR_EEL2) { + return CP_ACCESS_TRAP_EL2; + } + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_UNDEFINED; + } + } + return CP_ACCESS_OK; +} + +static const ARMCPRegInfo vapa_ats_reginfo[] = { + /* This underdecoding is safe because the reginfo is NO_RAW. */ + { .name = "ATS", .cp = 15, .crn = 7, .crm = 8, .opc1 = 0, .opc2 = CP_ANY, + .access = PL1_W, .accessfn = ats_access, + .writefn = ats_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, +}; + +static const ARMCPRegInfo v8_ats_reginfo[] = { + /* 64 bit address translation operations */ + { .name = "AT_S1E1R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E1R, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, + { .name = "AT_S1E1W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E1W, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, + { .name = "AT_S1E0R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 2, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E0R, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, + { .name = "AT_S1E0W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 8, .opc2 = 3, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E0W, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, + { .name = "AT_S12E1R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 4, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .accessfn = at_e012_access, .writefn = ats_write64 }, + { .name = "AT_S12E1W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 5, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .accessfn = at_e012_access, .writefn = ats_write64 }, + { .name = "AT_S12E0R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 6, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .accessfn = at_e012_access, .writefn = ats_write64 }, + { .name = "AT_S12E0W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 7, + .access = PL2_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .accessfn = at_e012_access, .writefn = ats_write64 }, + /* AT S1E2* are elsewhere as they UNDEF from EL3 if EL2 is not present */ + { .name = "AT_S1E3R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 0, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .writefn = ats_write64 }, + { .name = "AT_S1E3W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 6, .crn = 7, .crm = 8, .opc2 = 1, + .access = PL3_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .writefn = ats_write64 }, +}; + +static const ARMCPRegInfo el2_ats_reginfo[] = { + /* + * Unlike the other EL2-related AT operations, these must + * UNDEF from EL3 if EL2 is not implemented, which is why we + * define them here rather than with the rest of the AT ops. + */ + { .name = "AT_S1E2R", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0, + .access = PL2_W, .accessfn = at_s1e2_access, + .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = ats_write64 }, + { .name = "AT_S1E2W", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 1, + .access = PL2_W, .accessfn = at_s1e2_access, + .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC | ARM_CP_EL3_NO_EL2_UNDEF, + .writefn = ats_write64 }, + /* + * The AArch32 ATS1H* operations are CONSTRAINED UNPREDICTABLE + * if EL2 is not implemented; we choose to UNDEF. Behaviour at EL3 + * with SCR.NS == 0 outside Monitor mode is UNPREDICTABLE; we choose + * to behave as if SCR.NS was 1. + */ + { .name = "ATS1HR", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 0, + .access = PL2_W, + .writefn = ats1h_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, + { .name = "ATS1HW", .cp = 15, .opc1 = 4, .crn = 7, .crm = 8, .opc2 = 1, + .access = PL2_W, + .writefn = ats1h_write, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC }, +}; + +static const ARMCPRegInfo ats1e1_reginfo[] = { + { .name = "AT_S1E1RP", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E1RP, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, + { .name = "AT_S1E1WP", .state = ARM_CP_STATE_AA64, + .opc0 = 1, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .fgt = FGT_ATS1E1WP, + .accessfn = at_s1e01_access, .writefn = ats_write64 }, +}; + +static const ARMCPRegInfo ats1cp_reginfo[] = { + { .name = "ATS1CPRP", + .cp = 15, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 0, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .writefn = ats_write }, + { .name = "ATS1CPWP", + .cp = 15, .opc1 = 0, .crn = 7, .crm = 9, .opc2 = 1, + .access = PL1_W, .type = ARM_CP_NO_RAW | ARM_CP_RAISES_EXC, + .writefn = ats_write }, +}; + +void define_at_insn_regs(ARMCPU *cpu) +{ + CPUARMState *env = &cpu->env; + + if (arm_feature(env, ARM_FEATURE_VAPA)) { + define_arm_cp_regs(cpu, vapa_ats_reginfo); + } + if (arm_feature(env, ARM_FEATURE_V8)) { + define_arm_cp_regs(cpu, v8_ats_reginfo); + } + if (arm_feature(env, ARM_FEATURE_EL2) + || (arm_feature(env, ARM_FEATURE_EL3) + && arm_feature(env, ARM_FEATURE_V8))) { + define_arm_cp_regs(cpu, el2_ats_reginfo); + } + if (cpu_isar_feature(aa64_ats1e1, cpu)) { + define_arm_cp_regs(cpu, ats1e1_reginfo); + } + if (cpu_isar_feature(aa32_ats1e1, cpu)) { + define_arm_cp_regs(cpu, ats1cp_reginfo); + } +} diff --git a/target/arm/tcg/meson.build b/target/arm/tcg/meson.build index c59f0f03a1..895facdc30 100644 --- a/target/arm/tcg/meson.build +++ b/target/arm/tcg/meson.build @@ -64,6 +64,7 @@ arm_common_ss.add(files( )) arm_common_system_ss.add(files( + 'cpregs-at.c', 'hflags.c', 'iwmmxt_helper.c', 'neon_helper.c', From ae2086426d3784cf66e5b0b7ac823c08e87b4c57 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Mon, 7 Jul 2025 09:15:47 -0600 Subject: [PATCH 2078/2760] target/arm: Split out performance monitor regs to cpregs-pmu.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250707151547.196393-4-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpregs-pmu.c | 1309 +++++++++++++++++++++++++++++++++++++++ target/arm/cpregs.h | 3 + target/arm/helper.c | 1287 +------------------------------------- target/arm/internals.h | 2 + target/arm/meson.build | 2 + 5 files changed, 1319 insertions(+), 1284 deletions(-) create mode 100644 target/arm/cpregs-pmu.c diff --git a/target/arm/cpregs-pmu.c b/target/arm/cpregs-pmu.c new file mode 100644 index 0000000000..0f295b1376 --- /dev/null +++ b/target/arm/cpregs-pmu.c @@ -0,0 +1,1309 @@ +/* + * QEMU ARM CP Register PMU insns + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/timer.h" +#include "exec/icount.h" +#include "hw/irq.h" +#include "cpu.h" +#include "cpu-features.h" +#include "cpregs.h" +#include "internals.h" + + +#define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ + +/* + * Check for traps to performance monitor registers, which are controlled + * by MDCR_EL2.TPM for EL2 and MDCR_EL3.TPM for EL3. + */ +static CPAccessResult access_tpm(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + int el = arm_current_el(env); + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + + if (el < 2 && (mdcr_el2 & MDCR_TPM)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) { + return CP_ACCESS_TRAP_EL3; + } + return CP_ACCESS_OK; +} + +typedef struct pm_event { + uint16_t number; /* PMEVTYPER.evtCount is 16 bits wide */ + /* If the event is supported on this CPU (used to generate PMCEID[01]) */ + bool (*supported)(CPUARMState *); + /* + * Retrieve the current count of the underlying event. The programmed + * counters hold a difference from the return value from this function + */ + uint64_t (*get_count)(CPUARMState *); + /* + * Return how many nanoseconds it will take (at a minimum) for count events + * to occur. A negative value indicates the counter will never overflow, or + * that the counter has otherwise arranged for the overflow bit to be set + * and the PMU interrupt to be raised on overflow. + */ + int64_t (*ns_per_count)(uint64_t); +} pm_event; + +static bool event_always_supported(CPUARMState *env) +{ + return true; +} + +static uint64_t swinc_get_count(CPUARMState *env) +{ + /* + * SW_INCR events are written directly to the pmevcntr's by writes to + * PMSWINC, so there is no underlying count maintained by the PMU itself + */ + return 0; +} + +static int64_t swinc_ns_per(uint64_t ignored) +{ + return -1; +} + +/* + * Return the underlying cycle count for the PMU cycle counters. If we're in + * usermode, simply return 0. + */ +static uint64_t cycles_get_count(CPUARMState *env) +{ +#ifndef CONFIG_USER_ONLY + return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + ARM_CPU_FREQ, NANOSECONDS_PER_SECOND); +#else + return cpu_get_host_ticks(); +#endif +} + +#ifndef CONFIG_USER_ONLY +static int64_t cycles_ns_per(uint64_t cycles) +{ + return (ARM_CPU_FREQ / NANOSECONDS_PER_SECOND) * cycles; +} + +static bool instructions_supported(CPUARMState *env) +{ + /* Precise instruction counting */ + return icount_enabled() == ICOUNT_PRECISE; +} + +static uint64_t instructions_get_count(CPUARMState *env) +{ + assert(icount_enabled() == ICOUNT_PRECISE); + return (uint64_t)icount_get_raw(); +} + +static int64_t instructions_ns_per(uint64_t icount) +{ + assert(icount_enabled() == ICOUNT_PRECISE); + return icount_to_ns((int64_t)icount); +} +#endif + +static bool pmuv3p1_events_supported(CPUARMState *env) +{ + /* For events which are supported in any v8.1 PMU */ + return cpu_isar_feature(any_pmuv3p1, env_archcpu(env)); +} + +static bool pmuv3p4_events_supported(CPUARMState *env) +{ + /* For events which are supported in any v8.1 PMU */ + return cpu_isar_feature(any_pmuv3p4, env_archcpu(env)); +} + +static uint64_t zero_event_get_count(CPUARMState *env) +{ + /* For events which on QEMU never fire, so their count is always zero */ + return 0; +} + +static int64_t zero_event_ns_per(uint64_t cycles) +{ + /* An event which never fires can never overflow */ + return -1; +} + +static const pm_event pm_events[] = { + { .number = 0x000, /* SW_INCR */ + .supported = event_always_supported, + .get_count = swinc_get_count, + .ns_per_count = swinc_ns_per, + }, +#ifndef CONFIG_USER_ONLY + { .number = 0x008, /* INST_RETIRED, Instruction architecturally executed */ + .supported = instructions_supported, + .get_count = instructions_get_count, + .ns_per_count = instructions_ns_per, + }, + { .number = 0x011, /* CPU_CYCLES, Cycle */ + .supported = event_always_supported, + .get_count = cycles_get_count, + .ns_per_count = cycles_ns_per, + }, +#endif + { .number = 0x023, /* STALL_FRONTEND */ + .supported = pmuv3p1_events_supported, + .get_count = zero_event_get_count, + .ns_per_count = zero_event_ns_per, + }, + { .number = 0x024, /* STALL_BACKEND */ + .supported = pmuv3p1_events_supported, + .get_count = zero_event_get_count, + .ns_per_count = zero_event_ns_per, + }, + { .number = 0x03c, /* STALL */ + .supported = pmuv3p4_events_supported, + .get_count = zero_event_get_count, + .ns_per_count = zero_event_ns_per, + }, +}; + +/* + * Note: Before increasing MAX_EVENT_ID beyond 0x3f into the 0x40xx range of + * events (i.e. the statistical profiling extension), this implementation + * should first be updated to something sparse instead of the current + * supported_event_map[] array. + */ +#define MAX_EVENT_ID 0x3c +#define UNSUPPORTED_EVENT UINT16_MAX +static uint16_t supported_event_map[MAX_EVENT_ID + 1]; + +/* + * Called upon CPU initialization to initialize PMCEID[01]_EL0 and build a map + * of ARM event numbers to indices in our pm_events array. + * + * Note: Events in the 0x40XX range are not currently supported. + */ +void pmu_init(ARMCPU *cpu) +{ + unsigned int i; + + /* + * Empty supported_event_map and cpu->pmceid[01] before adding supported + * events to them + */ + for (i = 0; i < ARRAY_SIZE(supported_event_map); i++) { + supported_event_map[i] = UNSUPPORTED_EVENT; + } + cpu->pmceid0 = 0; + cpu->pmceid1 = 0; + + for (i = 0; i < ARRAY_SIZE(pm_events); i++) { + const pm_event *cnt = &pm_events[i]; + assert(cnt->number <= MAX_EVENT_ID); + /* We do not currently support events in the 0x40xx range */ + assert(cnt->number <= 0x3f); + + if (cnt->supported(&cpu->env)) { + supported_event_map[cnt->number] = i; + uint64_t event_mask = 1ULL << (cnt->number & 0x1f); + if (cnt->number & 0x20) { + cpu->pmceid1 |= event_mask; + } else { + cpu->pmceid0 |= event_mask; + } + } + } +} + +/* + * Check at runtime whether a PMU event is supported for the current machine + */ +static bool event_supported(uint16_t number) +{ + if (number > MAX_EVENT_ID) { + return false; + } + return supported_event_map[number] != UNSUPPORTED_EVENT; +} + +static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* + * Performance monitor registers user accessibility is controlled + * by PMUSERENR. MDCR_EL2.TPM and MDCR_EL3.TPM allow configurable + * trapping to EL2 or EL3 for other accesses. + */ + int el = arm_current_el(env); + uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); + + if (el == 0 && !(env->cp15.c9_pmuserenr & 1)) { + return CP_ACCESS_TRAP_EL1; + } + if (el < 2 && (mdcr_el2 & MDCR_TPM)) { + return CP_ACCESS_TRAP_EL2; + } + if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) { + return CP_ACCESS_TRAP_EL3; + } + + return CP_ACCESS_OK; +} + +static CPAccessResult pmreg_access_xevcntr(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + /* ER: event counter read trap control */ + if (arm_feature(env, ARM_FEATURE_V8) + && arm_current_el(env) == 0 + && (env->cp15.c9_pmuserenr & (1 << 3)) != 0 + && isread) { + return CP_ACCESS_OK; + } + + return pmreg_access(env, ri, isread); +} + +static CPAccessResult pmreg_access_swinc(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + /* SW: software increment write trap control */ + if (arm_feature(env, ARM_FEATURE_V8) + && arm_current_el(env) == 0 + && (env->cp15.c9_pmuserenr & (1 << 1)) != 0 + && !isread) { + return CP_ACCESS_OK; + } + + return pmreg_access(env, ri, isread); +} + +static CPAccessResult pmreg_access_selr(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + /* ER: event counter read trap control */ + if (arm_feature(env, ARM_FEATURE_V8) + && arm_current_el(env) == 0 + && (env->cp15.c9_pmuserenr & (1 << 3)) != 0) { + return CP_ACCESS_OK; + } + + return pmreg_access(env, ri, isread); +} + +static CPAccessResult pmreg_access_ccntr(CPUARMState *env, + const ARMCPRegInfo *ri, + bool isread) +{ + /* CR: cycle counter read trap control */ + if (arm_feature(env, ARM_FEATURE_V8) + && arm_current_el(env) == 0 + && (env->cp15.c9_pmuserenr & (1 << 2)) != 0 + && isread) { + return CP_ACCESS_OK; + } + + return pmreg_access(env, ri, isread); +} + +/* + * Returns true if the counter (pass 31 for PMCCNTR) should count events using + * the current EL, security state, and register configuration. + */ +static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter) +{ + uint64_t filter; + bool e, p, u, nsk, nsu, nsh, m; + bool enabled, prohibited = false, filtered; + bool secure = arm_is_secure(env); + int el = arm_current_el(env); + uint64_t mdcr_el2; + uint8_t hpmn; + + /* + * We might be called for M-profile cores where MDCR_EL2 doesn't + * exist and arm_mdcr_el2_eff() will assert, so this early-exit check + * must be before we read that value. + */ + if (!arm_feature(env, ARM_FEATURE_PMU)) { + return false; + } + + mdcr_el2 = arm_mdcr_el2_eff(env); + hpmn = mdcr_el2 & MDCR_HPMN; + + if (!arm_feature(env, ARM_FEATURE_EL2) || + (counter < hpmn || counter == 31)) { + e = env->cp15.c9_pmcr & PMCRE; + } else { + e = mdcr_el2 & MDCR_HPME; + } + enabled = e && (env->cp15.c9_pmcnten & (1 << counter)); + + /* Is event counting prohibited? */ + if (el == 2 && (counter < hpmn || counter == 31)) { + prohibited = mdcr_el2 & MDCR_HPMD; + } + if (secure) { + prohibited = prohibited || !(env->cp15.mdcr_el3 & MDCR_SPME); + } + + if (counter == 31) { + /* + * The cycle counter defaults to running. PMCR.DP says "disable + * the cycle counter when event counting is prohibited". + * Some MDCR bits disable the cycle counter specifically. + */ + prohibited = prohibited && env->cp15.c9_pmcr & PMCRDP; + if (cpu_isar_feature(any_pmuv3p5, env_archcpu(env))) { + if (secure) { + prohibited = prohibited || (env->cp15.mdcr_el3 & MDCR_SCCD); + } + if (el == 2) { + prohibited = prohibited || (mdcr_el2 & MDCR_HCCD); + } + } + } + + if (counter == 31) { + filter = env->cp15.pmccfiltr_el0; + } else { + filter = env->cp15.c14_pmevtyper[counter]; + } + + p = filter & PMXEVTYPER_P; + u = filter & PMXEVTYPER_U; + nsk = arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_NSK); + nsu = arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_NSU); + nsh = arm_feature(env, ARM_FEATURE_EL2) && (filter & PMXEVTYPER_NSH); + m = arm_el_is_aa64(env, 1) && + arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_M); + + if (el == 0) { + filtered = secure ? u : u != nsu; + } else if (el == 1) { + filtered = secure ? p : p != nsk; + } else if (el == 2) { + filtered = !nsh; + } else { /* EL3 */ + filtered = m != p; + } + + if (counter != 31) { + /* + * If not checking PMCCNTR, ensure the counter is setup to an event we + * support + */ + uint16_t event = filter & PMXEVTYPER_EVTCOUNT; + if (!event_supported(event)) { + return false; + } + } + + return enabled && !prohibited && !filtered; +} + +static void pmu_update_irq(CPUARMState *env) +{ + ARMCPU *cpu = env_archcpu(env); + qemu_set_irq(cpu->pmu_interrupt, (env->cp15.c9_pmcr & PMCRE) && + (env->cp15.c9_pminten & env->cp15.c9_pmovsr)); +} + +static bool pmccntr_clockdiv_enabled(CPUARMState *env) +{ + /* + * Return true if the clock divider is enabled and the cycle counter + * is supposed to tick only once every 64 clock cycles. This is + * controlled by PMCR.D, but if PMCR.LC is set to enable the long + * (64-bit) cycle counter PMCR.D has no effect. + */ + return (env->cp15.c9_pmcr & (PMCRD | PMCRLC)) == PMCRD; +} + +static bool pmevcntr_is_64_bit(CPUARMState *env, int counter) +{ + /* Return true if the specified event counter is configured to be 64 bit */ + + /* This isn't intended to be used with the cycle counter */ + assert(counter < 31); + + if (!cpu_isar_feature(any_pmuv3p5, env_archcpu(env))) { + return false; + } + + if (arm_feature(env, ARM_FEATURE_EL2)) { + /* + * MDCR_EL2.HLP still applies even when EL2 is disabled in the + * current security state, so we don't use arm_mdcr_el2_eff() here. + */ + bool hlp = env->cp15.mdcr_el2 & MDCR_HLP; + int hpmn = env->cp15.mdcr_el2 & MDCR_HPMN; + + if (counter >= hpmn) { + return hlp; + } + } + return env->cp15.c9_pmcr & PMCRLP; +} + +/* + * Ensure c15_ccnt is the guest-visible count so that operations such as + * enabling/disabling the counter or filtering, modifying the count itself, + * etc. can be done logically. This is essentially a no-op if the counter is + * not enabled at the time of the call. + */ +static void pmccntr_op_start(CPUARMState *env) +{ + uint64_t cycles = cycles_get_count(env); + + if (pmu_counter_enabled(env, 31)) { + uint64_t eff_cycles = cycles; + if (pmccntr_clockdiv_enabled(env)) { + eff_cycles /= 64; + } + + uint64_t new_pmccntr = eff_cycles - env->cp15.c15_ccnt_delta; + + uint64_t overflow_mask = env->cp15.c9_pmcr & PMCRLC ? \ + 1ull << 63 : 1ull << 31; + if (env->cp15.c15_ccnt & ~new_pmccntr & overflow_mask) { + env->cp15.c9_pmovsr |= (1ULL << 31); + pmu_update_irq(env); + } + + env->cp15.c15_ccnt = new_pmccntr; + } + env->cp15.c15_ccnt_delta = cycles; +} + +/* + * If PMCCNTR is enabled, recalculate the delta between the clock and the + * guest-visible count. A call to pmccntr_op_finish should follow every call to + * pmccntr_op_start. + */ +static void pmccntr_op_finish(CPUARMState *env) +{ + if (pmu_counter_enabled(env, 31)) { +#ifndef CONFIG_USER_ONLY + /* Calculate when the counter will next overflow */ + uint64_t remaining_cycles = -env->cp15.c15_ccnt; + if (!(env->cp15.c9_pmcr & PMCRLC)) { + remaining_cycles = (uint32_t)remaining_cycles; + } + int64_t overflow_in = cycles_ns_per(remaining_cycles); + + if (overflow_in > 0) { + int64_t overflow_at; + + if (!sadd64_overflow(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + overflow_in, &overflow_at)) { + ARMCPU *cpu = env_archcpu(env); + timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); + } + } +#endif + + uint64_t prev_cycles = env->cp15.c15_ccnt_delta; + if (pmccntr_clockdiv_enabled(env)) { + prev_cycles /= 64; + } + env->cp15.c15_ccnt_delta = prev_cycles - env->cp15.c15_ccnt; + } +} + +static void pmevcntr_op_start(CPUARMState *env, uint8_t counter) +{ + + uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT; + uint64_t count = 0; + if (event_supported(event)) { + uint16_t event_idx = supported_event_map[event]; + count = pm_events[event_idx].get_count(env); + } + + if (pmu_counter_enabled(env, counter)) { + uint64_t new_pmevcntr = count - env->cp15.c14_pmevcntr_delta[counter]; + uint64_t overflow_mask = pmevcntr_is_64_bit(env, counter) ? + 1ULL << 63 : 1ULL << 31; + + if (env->cp15.c14_pmevcntr[counter] & ~new_pmevcntr & overflow_mask) { + env->cp15.c9_pmovsr |= (1 << counter); + pmu_update_irq(env); + } + env->cp15.c14_pmevcntr[counter] = new_pmevcntr; + } + env->cp15.c14_pmevcntr_delta[counter] = count; +} + +static void pmevcntr_op_finish(CPUARMState *env, uint8_t counter) +{ + if (pmu_counter_enabled(env, counter)) { +#ifndef CONFIG_USER_ONLY + uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT; + uint16_t event_idx = supported_event_map[event]; + uint64_t delta = -(env->cp15.c14_pmevcntr[counter] + 1); + int64_t overflow_in; + + if (!pmevcntr_is_64_bit(env, counter)) { + delta = (uint32_t)delta; + } + overflow_in = pm_events[event_idx].ns_per_count(delta); + + if (overflow_in > 0) { + int64_t overflow_at; + + if (!sadd64_overflow(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), + overflow_in, &overflow_at)) { + ARMCPU *cpu = env_archcpu(env); + timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); + } + } +#endif + + env->cp15.c14_pmevcntr_delta[counter] -= + env->cp15.c14_pmevcntr[counter]; + } +} + +void pmu_op_start(CPUARMState *env) +{ + unsigned int i; + pmccntr_op_start(env); + for (i = 0; i < pmu_num_counters(env); i++) { + pmevcntr_op_start(env, i); + } +} + +void pmu_op_finish(CPUARMState *env) +{ + unsigned int i; + pmccntr_op_finish(env); + for (i = 0; i < pmu_num_counters(env); i++) { + pmevcntr_op_finish(env, i); + } +} + +void pmu_pre_el_change(ARMCPU *cpu, void *ignored) +{ + pmu_op_start(&cpu->env); +} + +void pmu_post_el_change(ARMCPU *cpu, void *ignored) +{ + pmu_op_finish(&cpu->env); +} + +void arm_pmu_timer_cb(void *opaque) +{ + ARMCPU *cpu = opaque; + + /* + * Update all the counter values based on the current underlying counts, + * triggering interrupts to be raised, if necessary. pmu_op_finish() also + * has the effect of setting the cpu->pmu_timer to the next earliest time a + * counter may expire. + */ + pmu_op_start(&cpu->env); + pmu_op_finish(&cpu->env); +} + +static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + pmu_op_start(env); + + if (value & PMCRC) { + /* The counter has been reset */ + env->cp15.c15_ccnt = 0; + } + + if (value & PMCRP) { + unsigned int i; + for (i = 0; i < pmu_num_counters(env); i++) { + env->cp15.c14_pmevcntr[i] = 0; + } + } + + env->cp15.c9_pmcr &= ~PMCR_WRITABLE_MASK; + env->cp15.c9_pmcr |= (value & PMCR_WRITABLE_MASK); + + pmu_op_finish(env); +} + +static uint64_t pmcr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + uint64_t pmcr = env->cp15.c9_pmcr; + + /* + * If EL2 is implemented and enabled for the current security state, reads + * of PMCR.N from EL1 or EL0 return the value of MDCR_EL2.HPMN or HDCR.HPMN. + */ + if (arm_current_el(env) <= 1 && arm_is_el2_enabled(env)) { + pmcr &= ~PMCRN_MASK; + pmcr |= (env->cp15.mdcr_el2 & MDCR_HPMN) << PMCRN_SHIFT; + } + + return pmcr; +} + +static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + unsigned int i; + uint64_t overflow_mask, new_pmswinc; + + for (i = 0; i < pmu_num_counters(env); i++) { + /* Increment a counter's count iff: */ + if ((value & (1 << i)) && /* counter's bit is set */ + /* counter is enabled and not filtered */ + pmu_counter_enabled(env, i) && + /* counter is SW_INCR */ + (env->cp15.c14_pmevtyper[i] & PMXEVTYPER_EVTCOUNT) == 0x0) { + pmevcntr_op_start(env, i); + + /* + * Detect if this write causes an overflow since we can't predict + * PMSWINC overflows like we can for other events + */ + new_pmswinc = env->cp15.c14_pmevcntr[i] + 1; + + overflow_mask = pmevcntr_is_64_bit(env, i) ? + 1ULL << 63 : 1ULL << 31; + + if (env->cp15.c14_pmevcntr[i] & ~new_pmswinc & overflow_mask) { + env->cp15.c9_pmovsr |= (1 << i); + pmu_update_irq(env); + } + + env->cp15.c14_pmevcntr[i] = new_pmswinc; + + pmevcntr_op_finish(env, i); + } + } +} + +static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + uint64_t ret; + pmccntr_op_start(env); + ret = env->cp15.c15_ccnt; + pmccntr_op_finish(env); + return ret; +} + +static void pmselr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* + * The value of PMSELR.SEL affects the behavior of PMXEVTYPER and + * PMXEVCNTR. We allow [0..31] to be written to PMSELR here; in the + * meanwhile, we check PMSELR.SEL when PMXEVTYPER and PMXEVCNTR are + * accessed. + */ + env->cp15.c9_pmselr = value & 0x1f; +} + +static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + pmccntr_op_start(env); + env->cp15.c15_ccnt = value; + pmccntr_op_finish(env); +} + +static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint64_t cur_val = pmccntr_read(env, NULL); + + pmccntr_write(env, ri, deposit64(cur_val, 0, 32, value)); +} + +static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + pmccntr_op_start(env); + env->cp15.pmccfiltr_el0 = value & PMCCFILTR_EL0; + pmccntr_op_finish(env); +} + +static void pmccfiltr_write_a32(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + pmccntr_op_start(env); + /* M is not accessible from AArch32 */ + env->cp15.pmccfiltr_el0 = (env->cp15.pmccfiltr_el0 & PMCCFILTR_M) | + (value & PMCCFILTR); + pmccntr_op_finish(env); +} + +static uint64_t pmccfiltr_read_a32(CPUARMState *env, const ARMCPRegInfo *ri) +{ + /* M is not visible in AArch32 */ + return env->cp15.pmccfiltr_el0 & PMCCFILTR; +} + +static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + pmu_op_start(env); + value &= pmu_counter_mask(env); + env->cp15.c9_pmcnten |= value; + pmu_op_finish(env); +} + +static void pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + pmu_op_start(env); + value &= pmu_counter_mask(env); + env->cp15.c9_pmcnten &= ~value; + pmu_op_finish(env); +} + +static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + value &= pmu_counter_mask(env); + env->cp15.c9_pmovsr &= ~value; + pmu_update_irq(env); +} + +static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + value &= pmu_counter_mask(env); + env->cp15.c9_pmovsr |= value; + pmu_update_irq(env); +} + +static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value, const uint8_t counter) +{ + if (counter == 31) { + pmccfiltr_write(env, ri, value); + } else if (counter < pmu_num_counters(env)) { + pmevcntr_op_start(env, counter); + + /* + * If this counter's event type is changing, store the current + * underlying count for the new type in c14_pmevcntr_delta[counter] so + * pmevcntr_op_finish has the correct baseline when it converts back to + * a delta. + */ + uint16_t old_event = env->cp15.c14_pmevtyper[counter] & + PMXEVTYPER_EVTCOUNT; + uint16_t new_event = value & PMXEVTYPER_EVTCOUNT; + if (old_event != new_event) { + uint64_t count = 0; + if (event_supported(new_event)) { + uint16_t event_idx = supported_event_map[new_event]; + count = pm_events[event_idx].get_count(env); + } + env->cp15.c14_pmevcntr_delta[counter] = count; + } + + env->cp15.c14_pmevtyper[counter] = value & PMXEVTYPER_MASK; + pmevcntr_op_finish(env, counter); + } + /* + * Attempts to access PMXEVTYPER are CONSTRAINED UNPREDICTABLE when + * PMSELR value is equal to or greater than the number of implemented + * counters, but not equal to 0x1f. We opt to behave as a RAZ/WI. + */ +} + +static uint64_t pmevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri, + const uint8_t counter) +{ + if (counter == 31) { + return env->cp15.pmccfiltr_el0; + } else if (counter < pmu_num_counters(env)) { + return env->cp15.c14_pmevtyper[counter]; + } else { + /* + * We opt to behave as a RAZ/WI when attempts to access PMXEVTYPER + * are CONSTRAINED UNPREDICTABLE. See comments in pmevtyper_write(). + */ + return 0; + } +} + +static void pmevtyper_writefn(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + pmevtyper_write(env, ri, value, counter); +} + +static void pmevtyper_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + env->cp15.c14_pmevtyper[counter] = value; + + /* + * pmevtyper_rawwrite is called between a pair of pmu_op_start and + * pmu_op_finish calls when loading saved state for a migration. Because + * we're potentially updating the type of event here, the value written to + * c14_pmevcntr_delta by the preceding pmu_op_start call may be for a + * different counter type. Therefore, we need to set this value to the + * current count for the counter type we're writing so that pmu_op_finish + * has the correct count for its calculation. + */ + uint16_t event = value & PMXEVTYPER_EVTCOUNT; + if (event_supported(event)) { + uint16_t event_idx = supported_event_map[event]; + env->cp15.c14_pmevcntr_delta[counter] = + pm_events[event_idx].get_count(env); + } +} + +static uint64_t pmevtyper_readfn(CPUARMState *env, const ARMCPRegInfo *ri) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + return pmevtyper_read(env, ri, counter); +} + +static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + pmevtyper_write(env, ri, value, env->cp15.c9_pmselr & 31); +} + +static uint64_t pmxevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return pmevtyper_read(env, ri, env->cp15.c9_pmselr & 31); +} + +static void pmevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value, uint8_t counter) +{ + if (!cpu_isar_feature(any_pmuv3p5, env_archcpu(env))) { + /* Before FEAT_PMUv3p5, top 32 bits of event counters are RES0 */ + value &= MAKE_64BIT_MASK(0, 32); + } + if (counter < pmu_num_counters(env)) { + pmevcntr_op_start(env, counter); + env->cp15.c14_pmevcntr[counter] = value; + pmevcntr_op_finish(env, counter); + } + /* + * We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR + * are CONSTRAINED UNPREDICTABLE. + */ +} + +static uint64_t pmevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri, + uint8_t counter) +{ + if (counter < pmu_num_counters(env)) { + uint64_t ret; + pmevcntr_op_start(env, counter); + ret = env->cp15.c14_pmevcntr[counter]; + pmevcntr_op_finish(env, counter); + if (!cpu_isar_feature(any_pmuv3p5, env_archcpu(env))) { + /* Before FEAT_PMUv3p5, top 32 bits of event counters are RES0 */ + ret &= MAKE_64BIT_MASK(0, 32); + } + return ret; + } else { + /* + * We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR + * are CONSTRAINED UNPREDICTABLE. + */ + return 0; + } +} + +static void pmevcntr_writefn(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + pmevcntr_write(env, ri, value, counter); +} + +static uint64_t pmevcntr_readfn(CPUARMState *env, const ARMCPRegInfo *ri) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + return pmevcntr_read(env, ri, counter); +} + +static void pmevcntr_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + assert(counter < pmu_num_counters(env)); + env->cp15.c14_pmevcntr[counter] = value; + pmevcntr_write(env, ri, value, counter); +} + +static uint64_t pmevcntr_rawread(CPUARMState *env, const ARMCPRegInfo *ri) +{ + uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); + assert(counter < pmu_num_counters(env)); + return env->cp15.c14_pmevcntr[counter]; +} + +static void pmxevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + pmevcntr_write(env, ri, value, env->cp15.c9_pmselr & 31); +} + +static uint64_t pmxevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri) +{ + return pmevcntr_read(env, ri, env->cp15.c9_pmselr & 31); +} + +static void pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + if (arm_feature(env, ARM_FEATURE_V8)) { + env->cp15.c9_pmuserenr = value & 0xf; + } else { + env->cp15.c9_pmuserenr = value & 1; + } +} + +static void pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + /* We have no event counters so only the C bit can be changed */ + value &= pmu_counter_mask(env); + env->cp15.c9_pminten |= value; + pmu_update_irq(env); +} + +static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, + uint64_t value) +{ + value &= pmu_counter_mask(env); + env->cp15.c9_pminten &= ~value; + pmu_update_irq(env); +} + +static const ARMCPRegInfo v7_pm_reginfo[] = { + /* + * Performance monitors are implementation defined in v7, + * but with an ARM recommended set of registers, which we + * follow. + * + * Performance registers fall into three categories: + * (a) always UNDEF in PL0, RW in PL1 (PMINTENSET, PMINTENCLR) + * (b) RO in PL0 (ie UNDEF on write), RW in PL1 (PMUSERENR) + * (c) UNDEF in PL0 if PMUSERENR.EN==0, otherwise accessible (all others) + * For the cases controlled by PMUSERENR we must set .access to PL0_RW + * or PL0_RO as appropriate and then check PMUSERENR in the helper fn. + */ + { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1, + .access = PL0_RW, .type = ARM_CP_ALIAS | ARM_CP_IO, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), + .writefn = pmcntenset_write, + .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, + .raw_writefn = raw_write }, + { .name = "PMCNTENSET_EL0", .state = ARM_CP_STATE_AA64, .type = ARM_CP_IO, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 1, + .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .resetvalue = 0, + .writefn = pmcntenset_write, .raw_writefn = raw_write }, + { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2, + .access = PL0_RW, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), + .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, + .writefn = pmcntenclr_write, .raw_writefn = raw_write, + .type = ARM_CP_ALIAS | ARM_CP_IO }, + { .name = "PMCNTENCLR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 2, + .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCNTEN, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), + .writefn = pmcntenclr_write, .raw_writefn = raw_write }, + { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, + .access = PL0_RW, .type = ARM_CP_IO, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), + .accessfn = pmreg_access, + .fgt = FGT_PMOVS, + .writefn = pmovsr_write, + .raw_writefn = raw_write }, + { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3, + .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMOVS, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), + .writefn = pmovsr_write, + .raw_writefn = raw_write }, + { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4, + .access = PL0_W, .accessfn = pmreg_access_swinc, + .fgt = FGT_PMSWINC_EL0, + .type = ARM_CP_NO_RAW | ARM_CP_IO, + .writefn = pmswinc_write }, + { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4, + .access = PL0_W, .accessfn = pmreg_access_swinc, + .fgt = FGT_PMSWINC_EL0, + .type = ARM_CP_NO_RAW | ARM_CP_IO, + .writefn = pmswinc_write }, + { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5, + .access = PL0_RW, .type = ARM_CP_ALIAS, + .fgt = FGT_PMSELR_EL0, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmselr), + .accessfn = pmreg_access_selr, .writefn = pmselr_write, + .raw_writefn = raw_write}, + { .name = "PMSELR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 5, + .access = PL0_RW, .accessfn = pmreg_access_selr, + .fgt = FGT_PMSELR_EL0, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr), + .writefn = pmselr_write, .raw_writefn = raw_write, }, + { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, + .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS | ARM_CP_IO, + .fgt = FGT_PMCCNTR_EL0, + .readfn = pmccntr_read, .writefn = pmccntr_write32, + .accessfn = pmreg_access_ccntr }, + { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0, + .access = PL0_RW, .accessfn = pmreg_access_ccntr, + .fgt = FGT_PMCCNTR_EL0, + .type = ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c15_ccnt), + .readfn = pmccntr_read, .writefn = pmccntr_write, + .raw_readfn = raw_read, .raw_writefn = raw_write, }, + { .name = "PMCCFILTR", .cp = 15, .opc1 = 0, .crn = 14, .crm = 15, .opc2 = 7, + .writefn = pmccfiltr_write_a32, .readfn = pmccfiltr_read_a32, + .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCCFILTR_EL0, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .resetvalue = 0, }, + { .name = "PMCCFILTR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 15, .opc2 = 7, + .writefn = pmccfiltr_write, .raw_writefn = raw_write, + .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCCFILTR_EL0, + .type = ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.pmccfiltr_el0), + .resetvalue = 0, }, + { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1, + .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .accessfn = pmreg_access, + .fgt = FGT_PMEVTYPERN_EL0, + .writefn = pmxevtyper_write, .readfn = pmxevtyper_read }, + { .name = "PMXEVTYPER_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 1, + .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .accessfn = pmreg_access, + .fgt = FGT_PMEVTYPERN_EL0, + .writefn = pmxevtyper_write, .readfn = pmxevtyper_read }, + { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2, + .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .accessfn = pmreg_access_xevcntr, + .fgt = FGT_PMEVCNTRN_EL0, + .writefn = pmxevcntr_write, .readfn = pmxevcntr_read }, + { .name = "PMXEVCNTR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 2, + .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, + .accessfn = pmreg_access_xevcntr, + .fgt = FGT_PMEVCNTRN_EL0, + .writefn = pmxevcntr_write, .readfn = pmxevcntr_read }, + { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0, + .access = PL0_R | PL1_RW, .accessfn = access_tpm, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmuserenr), + .resetvalue = 0, + .writefn = pmuserenr_write, .raw_writefn = raw_write }, + { .name = "PMUSERENR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 0, + .access = PL0_R | PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr), + .resetvalue = 0, + .writefn = pmuserenr_write, .raw_writefn = raw_write }, + { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1, + .access = PL1_RW, .accessfn = access_tpm, + .fgt = FGT_PMINTEN, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pminten), + .resetvalue = 0, + .writefn = pmintenset_write, .raw_writefn = raw_write }, + { .name = "PMINTENSET_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 1, + .access = PL1_RW, .accessfn = access_tpm, + .fgt = FGT_PMINTEN, + .type = ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), + .writefn = pmintenset_write, .raw_writefn = raw_write, + .resetvalue = 0x0 }, + { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, + .access = PL1_RW, .accessfn = access_tpm, + .fgt = FGT_PMINTEN, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), + .writefn = pmintenclr_write, .raw_writefn = raw_write }, + { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2, + .access = PL1_RW, .accessfn = access_tpm, + .fgt = FGT_PMINTEN, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), + .writefn = pmintenclr_write, .raw_writefn = raw_write }, +}; + +static const ARMCPRegInfo pmovsset_cp_reginfo[] = { + /* PMOVSSET is not implemented in v7 before v7ve */ + { .name = "PMOVSSET", .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 3, + .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMOVS, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), + .writefn = pmovsset_write, + .raw_writefn = raw_write }, + { .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3, + .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMOVS, + .type = ARM_CP_ALIAS | ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), + .writefn = pmovsset_write, + .raw_writefn = raw_write }, +}; + +void define_pm_cpregs(ARMCPU *cpu) +{ + CPUARMState *env = &cpu->env; + + if (arm_feature(env, ARM_FEATURE_V7)) { + /* + * v7 performance monitor control register: same implementor + * field as main ID register, and we implement four counters in + * addition to the cycle count register. + */ + static const ARMCPRegInfo pmcr = { + .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0, + .access = PL0_RW, + .fgt = FGT_PMCR_EL0, + .type = ARM_CP_IO | ARM_CP_ALIAS, + .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr), + .accessfn = pmreg_access, + .readfn = pmcr_read, .raw_readfn = raw_read, + .writefn = pmcr_write, .raw_writefn = raw_write, + }; + const ARMCPRegInfo pmcr64 = { + .name = "PMCR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0, + .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMCR_EL0, + .type = ARM_CP_IO, + .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), + .resetvalue = cpu->isar.reset_pmcr_el0, + .readfn = pmcr_read, .raw_readfn = raw_read, + .writefn = pmcr_write, .raw_writefn = raw_write, + }; + + define_one_arm_cp_reg(cpu, &pmcr); + define_one_arm_cp_reg(cpu, &pmcr64); + define_arm_cp_regs(cpu, v7_pm_reginfo); + + for (unsigned i = 0, pmcrn = pmu_num_counters(env); i < pmcrn; i++) { + g_autofree char *pmevcntr_name = g_strdup_printf("PMEVCNTR%d", i); + g_autofree char *pmevcntr_el0_name = g_strdup_printf("PMEVCNTR%d_EL0", i); + g_autofree char *pmevtyper_name = g_strdup_printf("PMEVTYPER%d", i); + g_autofree char *pmevtyper_el0_name = g_strdup_printf("PMEVTYPER%d_EL0", i); + + ARMCPRegInfo pmev_regs[] = { + { .name = pmevcntr_name, .cp = 15, .crn = 14, + .crm = 8 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, + .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, + .fgt = FGT_PMEVCNTRN_EL0, + .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, + .accessfn = pmreg_access_xevcntr }, + { .name = pmevcntr_el0_name, .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 8 | (3 & (i >> 3)), + .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access_xevcntr, + .type = ARM_CP_IO, + .fgt = FGT_PMEVCNTRN_EL0, + .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, + .raw_readfn = pmevcntr_rawread, + .raw_writefn = pmevcntr_rawwrite }, + { .name = pmevtyper_name, .cp = 15, .crn = 14, + .crm = 12 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, + .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, + .fgt = FGT_PMEVTYPERN_EL0, + .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, + .accessfn = pmreg_access }, + { .name = pmevtyper_el0_name, .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 12 | (3 & (i >> 3)), + .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access, + .fgt = FGT_PMEVTYPERN_EL0, + .type = ARM_CP_IO, + .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, + .raw_writefn = pmevtyper_rawwrite }, + }; + define_arm_cp_regs(cpu, pmev_regs); + } + } + if (arm_feature(env, ARM_FEATURE_V7VE)) { + define_arm_cp_regs(cpu, pmovsset_cp_reginfo); + } + + if (arm_feature(env, ARM_FEATURE_V8)) { + const ARMCPRegInfo v8_pm_reginfo[] = { + { .name = "PMCEID0", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 6, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, + .resetvalue = extract64(cpu->pmceid0, 0, 32) }, + { .name = "PMCEID0_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 6, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, + .resetvalue = cpu->pmceid0 }, + { .name = "PMCEID1", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 7, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, + .resetvalue = extract64(cpu->pmceid1, 0, 32) }, + { .name = "PMCEID1_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 7, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, + .resetvalue = cpu->pmceid1 }, + }; + define_arm_cp_regs(cpu, v8_pm_reginfo); + } + + if (cpu_isar_feature(aa32_pmuv3p1, cpu)) { + ARMCPRegInfo v81_pmu_regs[] = { + { .name = "PMCEID2", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 4, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, + .resetvalue = extract64(cpu->pmceid0, 32, 32) }, + { .name = "PMCEID3", .state = ARM_CP_STATE_AA32, + .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 5, + .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMCEIDN_EL0, + .resetvalue = extract64(cpu->pmceid1, 32, 32) }, + }; + define_arm_cp_regs(cpu, v81_pmu_regs); + } + + if (cpu_isar_feature(any_pmuv3p4, cpu)) { + static const ARMCPRegInfo v84_pmmir = { + .name = "PMMIR_EL1", .state = ARM_CP_STATE_BOTH, + .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 6, + .access = PL1_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, + .fgt = FGT_PMMIR_EL1, + .resetvalue = 0 + }; + define_one_arm_cp_reg(cpu, &v84_pmmir); + } +} diff --git a/target/arm/cpregs.h b/target/arm/cpregs.h index c1a7ae3735..c9506aa6d5 100644 --- a/target/arm/cpregs.h +++ b/target/arm/cpregs.h @@ -1065,6 +1065,9 @@ void arm_cp_write_ignore(CPUARMState *env, const ARMCPRegInfo *ri, /* CPReadFn that can be used for read-as-zero behaviour */ uint64_t arm_cp_read_zero(CPUARMState *env, const ARMCPRegInfo *ri); +/* CPReadFn that just reads the value from ri->fieldoffset */ +uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri); + /* CPWriteFn that just writes the value to ri->fieldoffset */ void raw_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value); diff --git a/target/arm/helper.c b/target/arm/helper.c index 0883246905..0c1299ff84 100644 --- a/target/arm/helper.c +++ b/target/arm/helper.c @@ -38,11 +38,9 @@ #define HELPER_H "tcg/helper.h" #include "exec/helper-proto.h.inc" -#define ARM_CPU_FREQ 1000000000 /* FIXME: 1 GHz, should be configurable */ - static void switch_mode(CPUARMState *env, int mode); -static uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) +uint64_t raw_read(CPUARMState *env, const ARMCPRegInfo *ri) { assert(ri->fieldoffset); if (cpreg_field_is_64bit(ri)) { @@ -319,25 +317,6 @@ static CPAccessResult access_trap_aa32s_el1(CPUARMState *env, return CP_ACCESS_UNDEFINED; } -/* - * Check for traps to performance monitor registers, which are controlled - * by MDCR_EL2.TPM for EL2 and MDCR_EL3.TPM for EL3. - */ -static CPAccessResult access_tpm(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - int el = arm_current_el(env); - uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); - - if (el < 2 && (mdcr_el2 & MDCR_TPM)) { - return CP_ACCESS_TRAP_EL2; - } - if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) { - return CP_ACCESS_TRAP_EL3; - } - return CP_ACCESS_OK; -} - /* Check for traps from EL1 due to HCR_EL2.TVM and HCR_EL2.TRVM. */ CPAccessResult access_tvm_trvm(CPUARMState *env, const ARMCPRegInfo *ri, bool isread) @@ -681,283 +660,6 @@ static const ARMCPRegInfo v6_cp_reginfo[] = { .resetfn = cpacr_reset, .writefn = cpacr_write, .readfn = cpacr_read }, }; -typedef struct pm_event { - uint16_t number; /* PMEVTYPER.evtCount is 16 bits wide */ - /* If the event is supported on this CPU (used to generate PMCEID[01]) */ - bool (*supported)(CPUARMState *); - /* - * Retrieve the current count of the underlying event. The programmed - * counters hold a difference from the return value from this function - */ - uint64_t (*get_count)(CPUARMState *); - /* - * Return how many nanoseconds it will take (at a minimum) for count events - * to occur. A negative value indicates the counter will never overflow, or - * that the counter has otherwise arranged for the overflow bit to be set - * and the PMU interrupt to be raised on overflow. - */ - int64_t (*ns_per_count)(uint64_t); -} pm_event; - -static bool event_always_supported(CPUARMState *env) -{ - return true; -} - -static uint64_t swinc_get_count(CPUARMState *env) -{ - /* - * SW_INCR events are written directly to the pmevcntr's by writes to - * PMSWINC, so there is no underlying count maintained by the PMU itself - */ - return 0; -} - -static int64_t swinc_ns_per(uint64_t ignored) -{ - return -1; -} - -/* - * Return the underlying cycle count for the PMU cycle counters. If we're in - * usermode, simply return 0. - */ -static uint64_t cycles_get_count(CPUARMState *env) -{ -#ifndef CONFIG_USER_ONLY - return muldiv64(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - ARM_CPU_FREQ, NANOSECONDS_PER_SECOND); -#else - return cpu_get_host_ticks(); -#endif -} - -#ifndef CONFIG_USER_ONLY -static int64_t cycles_ns_per(uint64_t cycles) -{ - return (ARM_CPU_FREQ / NANOSECONDS_PER_SECOND) * cycles; -} - -static bool instructions_supported(CPUARMState *env) -{ - /* Precise instruction counting */ - return icount_enabled() == ICOUNT_PRECISE; -} - -static uint64_t instructions_get_count(CPUARMState *env) -{ - assert(icount_enabled() == ICOUNT_PRECISE); - return (uint64_t)icount_get_raw(); -} - -static int64_t instructions_ns_per(uint64_t icount) -{ - assert(icount_enabled() == ICOUNT_PRECISE); - return icount_to_ns((int64_t)icount); -} -#endif - -static bool pmuv3p1_events_supported(CPUARMState *env) -{ - /* For events which are supported in any v8.1 PMU */ - return cpu_isar_feature(any_pmuv3p1, env_archcpu(env)); -} - -static bool pmuv3p4_events_supported(CPUARMState *env) -{ - /* For events which are supported in any v8.1 PMU */ - return cpu_isar_feature(any_pmuv3p4, env_archcpu(env)); -} - -static uint64_t zero_event_get_count(CPUARMState *env) -{ - /* For events which on QEMU never fire, so their count is always zero */ - return 0; -} - -static int64_t zero_event_ns_per(uint64_t cycles) -{ - /* An event which never fires can never overflow */ - return -1; -} - -static const pm_event pm_events[] = { - { .number = 0x000, /* SW_INCR */ - .supported = event_always_supported, - .get_count = swinc_get_count, - .ns_per_count = swinc_ns_per, - }, -#ifndef CONFIG_USER_ONLY - { .number = 0x008, /* INST_RETIRED, Instruction architecturally executed */ - .supported = instructions_supported, - .get_count = instructions_get_count, - .ns_per_count = instructions_ns_per, - }, - { .number = 0x011, /* CPU_CYCLES, Cycle */ - .supported = event_always_supported, - .get_count = cycles_get_count, - .ns_per_count = cycles_ns_per, - }, -#endif - { .number = 0x023, /* STALL_FRONTEND */ - .supported = pmuv3p1_events_supported, - .get_count = zero_event_get_count, - .ns_per_count = zero_event_ns_per, - }, - { .number = 0x024, /* STALL_BACKEND */ - .supported = pmuv3p1_events_supported, - .get_count = zero_event_get_count, - .ns_per_count = zero_event_ns_per, - }, - { .number = 0x03c, /* STALL */ - .supported = pmuv3p4_events_supported, - .get_count = zero_event_get_count, - .ns_per_count = zero_event_ns_per, - }, -}; - -/* - * Note: Before increasing MAX_EVENT_ID beyond 0x3f into the 0x40xx range of - * events (i.e. the statistical profiling extension), this implementation - * should first be updated to something sparse instead of the current - * supported_event_map[] array. - */ -#define MAX_EVENT_ID 0x3c -#define UNSUPPORTED_EVENT UINT16_MAX -static uint16_t supported_event_map[MAX_EVENT_ID + 1]; - -/* - * Called upon CPU initialization to initialize PMCEID[01]_EL0 and build a map - * of ARM event numbers to indices in our pm_events array. - * - * Note: Events in the 0x40XX range are not currently supported. - */ -void pmu_init(ARMCPU *cpu) -{ - unsigned int i; - - /* - * Empty supported_event_map and cpu->pmceid[01] before adding supported - * events to them - */ - for (i = 0; i < ARRAY_SIZE(supported_event_map); i++) { - supported_event_map[i] = UNSUPPORTED_EVENT; - } - cpu->pmceid0 = 0; - cpu->pmceid1 = 0; - - for (i = 0; i < ARRAY_SIZE(pm_events); i++) { - const pm_event *cnt = &pm_events[i]; - assert(cnt->number <= MAX_EVENT_ID); - /* We do not currently support events in the 0x40xx range */ - assert(cnt->number <= 0x3f); - - if (cnt->supported(&cpu->env)) { - supported_event_map[cnt->number] = i; - uint64_t event_mask = 1ULL << (cnt->number & 0x1f); - if (cnt->number & 0x20) { - cpu->pmceid1 |= event_mask; - } else { - cpu->pmceid0 |= event_mask; - } - } - } -} - -/* - * Check at runtime whether a PMU event is supported for the current machine - */ -static bool event_supported(uint16_t number) -{ - if (number > MAX_EVENT_ID) { - return false; - } - return supported_event_map[number] != UNSUPPORTED_EVENT; -} - -static CPAccessResult pmreg_access(CPUARMState *env, const ARMCPRegInfo *ri, - bool isread) -{ - /* - * Performance monitor registers user accessibility is controlled - * by PMUSERENR. MDCR_EL2.TPM and MDCR_EL3.TPM allow configurable - * trapping to EL2 or EL3 for other accesses. - */ - int el = arm_current_el(env); - uint64_t mdcr_el2 = arm_mdcr_el2_eff(env); - - if (el == 0 && !(env->cp15.c9_pmuserenr & 1)) { - return CP_ACCESS_TRAP_EL1; - } - if (el < 2 && (mdcr_el2 & MDCR_TPM)) { - return CP_ACCESS_TRAP_EL2; - } - if (el < 3 && (env->cp15.mdcr_el3 & MDCR_TPM)) { - return CP_ACCESS_TRAP_EL3; - } - - return CP_ACCESS_OK; -} - -static CPAccessResult pmreg_access_xevcntr(CPUARMState *env, - const ARMCPRegInfo *ri, - bool isread) -{ - /* ER: event counter read trap control */ - if (arm_feature(env, ARM_FEATURE_V8) - && arm_current_el(env) == 0 - && (env->cp15.c9_pmuserenr & (1 << 3)) != 0 - && isread) { - return CP_ACCESS_OK; - } - - return pmreg_access(env, ri, isread); -} - -static CPAccessResult pmreg_access_swinc(CPUARMState *env, - const ARMCPRegInfo *ri, - bool isread) -{ - /* SW: software increment write trap control */ - if (arm_feature(env, ARM_FEATURE_V8) - && arm_current_el(env) == 0 - && (env->cp15.c9_pmuserenr & (1 << 1)) != 0 - && !isread) { - return CP_ACCESS_OK; - } - - return pmreg_access(env, ri, isread); -} - -static CPAccessResult pmreg_access_selr(CPUARMState *env, - const ARMCPRegInfo *ri, - bool isread) -{ - /* ER: event counter read trap control */ - if (arm_feature(env, ARM_FEATURE_V8) - && arm_current_el(env) == 0 - && (env->cp15.c9_pmuserenr & (1 << 3)) != 0) { - return CP_ACCESS_OK; - } - - return pmreg_access(env, ri, isread); -} - -static CPAccessResult pmreg_access_ccntr(CPUARMState *env, - const ARMCPRegInfo *ri, - bool isread) -{ - /* CR: cycle counter read trap control */ - if (arm_feature(env, ARM_FEATURE_V8) - && arm_current_el(env) == 0 - && (env->cp15.c9_pmuserenr & (1 << 2)) != 0 - && isread) { - return CP_ACCESS_OK; - } - - return pmreg_access(env, ri, isread); -} - /* * Bits in MDCR_EL2 and MDCR_EL3 which pmu_counter_enabled() looks at. * We use these to decide whether we need to wrap a write to MDCR_EL2 @@ -967,684 +669,6 @@ static CPAccessResult pmreg_access_ccntr(CPUARMState *env, (MDCR_HPME | MDCR_HPMD | MDCR_HPMN | MDCR_HCCD | MDCR_HLP) #define MDCR_EL3_PMU_ENABLE_BITS (MDCR_SPME | MDCR_SCCD) -/* - * Returns true if the counter (pass 31 for PMCCNTR) should count events using - * the current EL, security state, and register configuration. - */ -static bool pmu_counter_enabled(CPUARMState *env, uint8_t counter) -{ - uint64_t filter; - bool e, p, u, nsk, nsu, nsh, m; - bool enabled, prohibited = false, filtered; - bool secure = arm_is_secure(env); - int el = arm_current_el(env); - uint64_t mdcr_el2; - uint8_t hpmn; - - /* - * We might be called for M-profile cores where MDCR_EL2 doesn't - * exist and arm_mdcr_el2_eff() will assert, so this early-exit check - * must be before we read that value. - */ - if (!arm_feature(env, ARM_FEATURE_PMU)) { - return false; - } - - mdcr_el2 = arm_mdcr_el2_eff(env); - hpmn = mdcr_el2 & MDCR_HPMN; - - if (!arm_feature(env, ARM_FEATURE_EL2) || - (counter < hpmn || counter == 31)) { - e = env->cp15.c9_pmcr & PMCRE; - } else { - e = mdcr_el2 & MDCR_HPME; - } - enabled = e && (env->cp15.c9_pmcnten & (1 << counter)); - - /* Is event counting prohibited? */ - if (el == 2 && (counter < hpmn || counter == 31)) { - prohibited = mdcr_el2 & MDCR_HPMD; - } - if (secure) { - prohibited = prohibited || !(env->cp15.mdcr_el3 & MDCR_SPME); - } - - if (counter == 31) { - /* - * The cycle counter defaults to running. PMCR.DP says "disable - * the cycle counter when event counting is prohibited". - * Some MDCR bits disable the cycle counter specifically. - */ - prohibited = prohibited && env->cp15.c9_pmcr & PMCRDP; - if (cpu_isar_feature(any_pmuv3p5, env_archcpu(env))) { - if (secure) { - prohibited = prohibited || (env->cp15.mdcr_el3 & MDCR_SCCD); - } - if (el == 2) { - prohibited = prohibited || (mdcr_el2 & MDCR_HCCD); - } - } - } - - if (counter == 31) { - filter = env->cp15.pmccfiltr_el0; - } else { - filter = env->cp15.c14_pmevtyper[counter]; - } - - p = filter & PMXEVTYPER_P; - u = filter & PMXEVTYPER_U; - nsk = arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_NSK); - nsu = arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_NSU); - nsh = arm_feature(env, ARM_FEATURE_EL2) && (filter & PMXEVTYPER_NSH); - m = arm_el_is_aa64(env, 1) && - arm_feature(env, ARM_FEATURE_EL3) && (filter & PMXEVTYPER_M); - - if (el == 0) { - filtered = secure ? u : u != nsu; - } else if (el == 1) { - filtered = secure ? p : p != nsk; - } else if (el == 2) { - filtered = !nsh; - } else { /* EL3 */ - filtered = m != p; - } - - if (counter != 31) { - /* - * If not checking PMCCNTR, ensure the counter is setup to an event we - * support - */ - uint16_t event = filter & PMXEVTYPER_EVTCOUNT; - if (!event_supported(event)) { - return false; - } - } - - return enabled && !prohibited && !filtered; -} - -static void pmu_update_irq(CPUARMState *env) -{ - ARMCPU *cpu = env_archcpu(env); - qemu_set_irq(cpu->pmu_interrupt, (env->cp15.c9_pmcr & PMCRE) && - (env->cp15.c9_pminten & env->cp15.c9_pmovsr)); -} - -static bool pmccntr_clockdiv_enabled(CPUARMState *env) -{ - /* - * Return true if the clock divider is enabled and the cycle counter - * is supposed to tick only once every 64 clock cycles. This is - * controlled by PMCR.D, but if PMCR.LC is set to enable the long - * (64-bit) cycle counter PMCR.D has no effect. - */ - return (env->cp15.c9_pmcr & (PMCRD | PMCRLC)) == PMCRD; -} - -static bool pmevcntr_is_64_bit(CPUARMState *env, int counter) -{ - /* Return true if the specified event counter is configured to be 64 bit */ - - /* This isn't intended to be used with the cycle counter */ - assert(counter < 31); - - if (!cpu_isar_feature(any_pmuv3p5, env_archcpu(env))) { - return false; - } - - if (arm_feature(env, ARM_FEATURE_EL2)) { - /* - * MDCR_EL2.HLP still applies even when EL2 is disabled in the - * current security state, so we don't use arm_mdcr_el2_eff() here. - */ - bool hlp = env->cp15.mdcr_el2 & MDCR_HLP; - int hpmn = env->cp15.mdcr_el2 & MDCR_HPMN; - - if (counter >= hpmn) { - return hlp; - } - } - return env->cp15.c9_pmcr & PMCRLP; -} - -/* - * Ensure c15_ccnt is the guest-visible count so that operations such as - * enabling/disabling the counter or filtering, modifying the count itself, - * etc. can be done logically. This is essentially a no-op if the counter is - * not enabled at the time of the call. - */ -static void pmccntr_op_start(CPUARMState *env) -{ - uint64_t cycles = cycles_get_count(env); - - if (pmu_counter_enabled(env, 31)) { - uint64_t eff_cycles = cycles; - if (pmccntr_clockdiv_enabled(env)) { - eff_cycles /= 64; - } - - uint64_t new_pmccntr = eff_cycles - env->cp15.c15_ccnt_delta; - - uint64_t overflow_mask = env->cp15.c9_pmcr & PMCRLC ? \ - 1ull << 63 : 1ull << 31; - if (env->cp15.c15_ccnt & ~new_pmccntr & overflow_mask) { - env->cp15.c9_pmovsr |= (1ULL << 31); - pmu_update_irq(env); - } - - env->cp15.c15_ccnt = new_pmccntr; - } - env->cp15.c15_ccnt_delta = cycles; -} - -/* - * If PMCCNTR is enabled, recalculate the delta between the clock and the - * guest-visible count. A call to pmccntr_op_finish should follow every call to - * pmccntr_op_start. - */ -static void pmccntr_op_finish(CPUARMState *env) -{ - if (pmu_counter_enabled(env, 31)) { -#ifndef CONFIG_USER_ONLY - /* Calculate when the counter will next overflow */ - uint64_t remaining_cycles = -env->cp15.c15_ccnt; - if (!(env->cp15.c9_pmcr & PMCRLC)) { - remaining_cycles = (uint32_t)remaining_cycles; - } - int64_t overflow_in = cycles_ns_per(remaining_cycles); - - if (overflow_in > 0) { - int64_t overflow_at; - - if (!sadd64_overflow(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - overflow_in, &overflow_at)) { - ARMCPU *cpu = env_archcpu(env); - timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); - } - } -#endif - - uint64_t prev_cycles = env->cp15.c15_ccnt_delta; - if (pmccntr_clockdiv_enabled(env)) { - prev_cycles /= 64; - } - env->cp15.c15_ccnt_delta = prev_cycles - env->cp15.c15_ccnt; - } -} - -static void pmevcntr_op_start(CPUARMState *env, uint8_t counter) -{ - - uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT; - uint64_t count = 0; - if (event_supported(event)) { - uint16_t event_idx = supported_event_map[event]; - count = pm_events[event_idx].get_count(env); - } - - if (pmu_counter_enabled(env, counter)) { - uint64_t new_pmevcntr = count - env->cp15.c14_pmevcntr_delta[counter]; - uint64_t overflow_mask = pmevcntr_is_64_bit(env, counter) ? - 1ULL << 63 : 1ULL << 31; - - if (env->cp15.c14_pmevcntr[counter] & ~new_pmevcntr & overflow_mask) { - env->cp15.c9_pmovsr |= (1 << counter); - pmu_update_irq(env); - } - env->cp15.c14_pmevcntr[counter] = new_pmevcntr; - } - env->cp15.c14_pmevcntr_delta[counter] = count; -} - -static void pmevcntr_op_finish(CPUARMState *env, uint8_t counter) -{ - if (pmu_counter_enabled(env, counter)) { -#ifndef CONFIG_USER_ONLY - uint16_t event = env->cp15.c14_pmevtyper[counter] & PMXEVTYPER_EVTCOUNT; - uint16_t event_idx = supported_event_map[event]; - uint64_t delta = -(env->cp15.c14_pmevcntr[counter] + 1); - int64_t overflow_in; - - if (!pmevcntr_is_64_bit(env, counter)) { - delta = (uint32_t)delta; - } - overflow_in = pm_events[event_idx].ns_per_count(delta); - - if (overflow_in > 0) { - int64_t overflow_at; - - if (!sadd64_overflow(qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL), - overflow_in, &overflow_at)) { - ARMCPU *cpu = env_archcpu(env); - timer_mod_anticipate_ns(cpu->pmu_timer, overflow_at); - } - } -#endif - - env->cp15.c14_pmevcntr_delta[counter] -= - env->cp15.c14_pmevcntr[counter]; - } -} - -void pmu_op_start(CPUARMState *env) -{ - unsigned int i; - pmccntr_op_start(env); - for (i = 0; i < pmu_num_counters(env); i++) { - pmevcntr_op_start(env, i); - } -} - -void pmu_op_finish(CPUARMState *env) -{ - unsigned int i; - pmccntr_op_finish(env); - for (i = 0; i < pmu_num_counters(env); i++) { - pmevcntr_op_finish(env, i); - } -} - -void pmu_pre_el_change(ARMCPU *cpu, void *ignored) -{ - pmu_op_start(&cpu->env); -} - -void pmu_post_el_change(ARMCPU *cpu, void *ignored) -{ - pmu_op_finish(&cpu->env); -} - -void arm_pmu_timer_cb(void *opaque) -{ - ARMCPU *cpu = opaque; - - /* - * Update all the counter values based on the current underlying counts, - * triggering interrupts to be raised, if necessary. pmu_op_finish() also - * has the effect of setting the cpu->pmu_timer to the next earliest time a - * counter may expire. - */ - pmu_op_start(&cpu->env); - pmu_op_finish(&cpu->env); -} - -static void pmcr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - pmu_op_start(env); - - if (value & PMCRC) { - /* The counter has been reset */ - env->cp15.c15_ccnt = 0; - } - - if (value & PMCRP) { - unsigned int i; - for (i = 0; i < pmu_num_counters(env); i++) { - env->cp15.c14_pmevcntr[i] = 0; - } - } - - env->cp15.c9_pmcr &= ~PMCR_WRITABLE_MASK; - env->cp15.c9_pmcr |= (value & PMCR_WRITABLE_MASK); - - pmu_op_finish(env); -} - -static uint64_t pmcr_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - uint64_t pmcr = env->cp15.c9_pmcr; - - /* - * If EL2 is implemented and enabled for the current security state, reads - * of PMCR.N from EL1 or EL0 return the value of MDCR_EL2.HPMN or HDCR.HPMN. - */ - if (arm_current_el(env) <= 1 && arm_is_el2_enabled(env)) { - pmcr &= ~PMCRN_MASK; - pmcr |= (env->cp15.mdcr_el2 & MDCR_HPMN) << PMCRN_SHIFT; - } - - return pmcr; -} - -static void pmswinc_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - unsigned int i; - uint64_t overflow_mask, new_pmswinc; - - for (i = 0; i < pmu_num_counters(env); i++) { - /* Increment a counter's count iff: */ - if ((value & (1 << i)) && /* counter's bit is set */ - /* counter is enabled and not filtered */ - pmu_counter_enabled(env, i) && - /* counter is SW_INCR */ - (env->cp15.c14_pmevtyper[i] & PMXEVTYPER_EVTCOUNT) == 0x0) { - pmevcntr_op_start(env, i); - - /* - * Detect if this write causes an overflow since we can't predict - * PMSWINC overflows like we can for other events - */ - new_pmswinc = env->cp15.c14_pmevcntr[i] + 1; - - overflow_mask = pmevcntr_is_64_bit(env, i) ? - 1ULL << 63 : 1ULL << 31; - - if (env->cp15.c14_pmevcntr[i] & ~new_pmswinc & overflow_mask) { - env->cp15.c9_pmovsr |= (1 << i); - pmu_update_irq(env); - } - - env->cp15.c14_pmevcntr[i] = new_pmswinc; - - pmevcntr_op_finish(env, i); - } - } -} - -static uint64_t pmccntr_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - uint64_t ret; - pmccntr_op_start(env); - ret = env->cp15.c15_ccnt; - pmccntr_op_finish(env); - return ret; -} - -static void pmselr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* - * The value of PMSELR.SEL affects the behavior of PMXEVTYPER and - * PMXEVCNTR. We allow [0..31] to be written to PMSELR here; in the - * meanwhile, we check PMSELR.SEL when PMXEVTYPER and PMXEVCNTR are - * accessed. - */ - env->cp15.c9_pmselr = value & 0x1f; -} - -static void pmccntr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - pmccntr_op_start(env); - env->cp15.c15_ccnt = value; - pmccntr_op_finish(env); -} - -static void pmccntr_write32(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - uint64_t cur_val = pmccntr_read(env, NULL); - - pmccntr_write(env, ri, deposit64(cur_val, 0, 32, value)); -} - -static void pmccfiltr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - pmccntr_op_start(env); - env->cp15.pmccfiltr_el0 = value & PMCCFILTR_EL0; - pmccntr_op_finish(env); -} - -static void pmccfiltr_write_a32(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - pmccntr_op_start(env); - /* M is not accessible from AArch32 */ - env->cp15.pmccfiltr_el0 = (env->cp15.pmccfiltr_el0 & PMCCFILTR_M) | - (value & PMCCFILTR); - pmccntr_op_finish(env); -} - -static uint64_t pmccfiltr_read_a32(CPUARMState *env, const ARMCPRegInfo *ri) -{ - /* M is not visible in AArch32 */ - return env->cp15.pmccfiltr_el0 & PMCCFILTR; -} - -static void pmcntenset_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - pmu_op_start(env); - value &= pmu_counter_mask(env); - env->cp15.c9_pmcnten |= value; - pmu_op_finish(env); -} - -static void pmcntenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - pmu_op_start(env); - value &= pmu_counter_mask(env); - env->cp15.c9_pmcnten &= ~value; - pmu_op_finish(env); -} - -static void pmovsr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - value &= pmu_counter_mask(env); - env->cp15.c9_pmovsr &= ~value; - pmu_update_irq(env); -} - -static void pmovsset_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - value &= pmu_counter_mask(env); - env->cp15.c9_pmovsr |= value; - pmu_update_irq(env); -} - -static void pmevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value, const uint8_t counter) -{ - if (counter == 31) { - pmccfiltr_write(env, ri, value); - } else if (counter < pmu_num_counters(env)) { - pmevcntr_op_start(env, counter); - - /* - * If this counter's event type is changing, store the current - * underlying count for the new type in c14_pmevcntr_delta[counter] so - * pmevcntr_op_finish has the correct baseline when it converts back to - * a delta. - */ - uint16_t old_event = env->cp15.c14_pmevtyper[counter] & - PMXEVTYPER_EVTCOUNT; - uint16_t new_event = value & PMXEVTYPER_EVTCOUNT; - if (old_event != new_event) { - uint64_t count = 0; - if (event_supported(new_event)) { - uint16_t event_idx = supported_event_map[new_event]; - count = pm_events[event_idx].get_count(env); - } - env->cp15.c14_pmevcntr_delta[counter] = count; - } - - env->cp15.c14_pmevtyper[counter] = value & PMXEVTYPER_MASK; - pmevcntr_op_finish(env, counter); - } - /* - * Attempts to access PMXEVTYPER are CONSTRAINED UNPREDICTABLE when - * PMSELR value is equal to or greater than the number of implemented - * counters, but not equal to 0x1f. We opt to behave as a RAZ/WI. - */ -} - -static uint64_t pmevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri, - const uint8_t counter) -{ - if (counter == 31) { - return env->cp15.pmccfiltr_el0; - } else if (counter < pmu_num_counters(env)) { - return env->cp15.c14_pmevtyper[counter]; - } else { - /* - * We opt to behave as a RAZ/WI when attempts to access PMXEVTYPER - * are CONSTRAINED UNPREDICTABLE. See comments in pmevtyper_write(). - */ - return 0; - } -} - -static void pmevtyper_writefn(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); - pmevtyper_write(env, ri, value, counter); -} - -static void pmevtyper_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); - env->cp15.c14_pmevtyper[counter] = value; - - /* - * pmevtyper_rawwrite is called between a pair of pmu_op_start and - * pmu_op_finish calls when loading saved state for a migration. Because - * we're potentially updating the type of event here, the value written to - * c14_pmevcntr_delta by the preceding pmu_op_start call may be for a - * different counter type. Therefore, we need to set this value to the - * current count for the counter type we're writing so that pmu_op_finish - * has the correct count for its calculation. - */ - uint16_t event = value & PMXEVTYPER_EVTCOUNT; - if (event_supported(event)) { - uint16_t event_idx = supported_event_map[event]; - env->cp15.c14_pmevcntr_delta[counter] = - pm_events[event_idx].get_count(env); - } -} - -static uint64_t pmevtyper_readfn(CPUARMState *env, const ARMCPRegInfo *ri) -{ - uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); - return pmevtyper_read(env, ri, counter); -} - -static void pmxevtyper_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - pmevtyper_write(env, ri, value, env->cp15.c9_pmselr & 31); -} - -static uint64_t pmxevtyper_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - return pmevtyper_read(env, ri, env->cp15.c9_pmselr & 31); -} - -static void pmevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value, uint8_t counter) -{ - if (!cpu_isar_feature(any_pmuv3p5, env_archcpu(env))) { - /* Before FEAT_PMUv3p5, top 32 bits of event counters are RES0 */ - value &= MAKE_64BIT_MASK(0, 32); - } - if (counter < pmu_num_counters(env)) { - pmevcntr_op_start(env, counter); - env->cp15.c14_pmevcntr[counter] = value; - pmevcntr_op_finish(env, counter); - } - /* - * We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR - * are CONSTRAINED UNPREDICTABLE. - */ -} - -static uint64_t pmevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri, - uint8_t counter) -{ - if (counter < pmu_num_counters(env)) { - uint64_t ret; - pmevcntr_op_start(env, counter); - ret = env->cp15.c14_pmevcntr[counter]; - pmevcntr_op_finish(env, counter); - if (!cpu_isar_feature(any_pmuv3p5, env_archcpu(env))) { - /* Before FEAT_PMUv3p5, top 32 bits of event counters are RES0 */ - ret &= MAKE_64BIT_MASK(0, 32); - } - return ret; - } else { - /* - * We opt to behave as a RAZ/WI when attempts to access PM[X]EVCNTR - * are CONSTRAINED UNPREDICTABLE. - */ - return 0; - } -} - -static void pmevcntr_writefn(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); - pmevcntr_write(env, ri, value, counter); -} - -static uint64_t pmevcntr_readfn(CPUARMState *env, const ARMCPRegInfo *ri) -{ - uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); - return pmevcntr_read(env, ri, counter); -} - -static void pmevcntr_rawwrite(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); - assert(counter < pmu_num_counters(env)); - env->cp15.c14_pmevcntr[counter] = value; - pmevcntr_write(env, ri, value, counter); -} - -static uint64_t pmevcntr_rawread(CPUARMState *env, const ARMCPRegInfo *ri) -{ - uint8_t counter = ((ri->crm & 3) << 3) | (ri->opc2 & 7); - assert(counter < pmu_num_counters(env)); - return env->cp15.c14_pmevcntr[counter]; -} - -static void pmxevcntr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - pmevcntr_write(env, ri, value, env->cp15.c9_pmselr & 31); -} - -static uint64_t pmxevcntr_read(CPUARMState *env, const ARMCPRegInfo *ri) -{ - return pmevcntr_read(env, ri, env->cp15.c9_pmselr & 31); -} - -static void pmuserenr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - if (arm_feature(env, ARM_FEATURE_V8)) { - env->cp15.c9_pmuserenr = value & 0xf; - } else { - env->cp15.c9_pmuserenr = value & 1; - } -} - -static void pmintenset_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - /* We have no event counters so only the C bit can be changed */ - value &= pmu_counter_mask(env); - env->cp15.c9_pminten |= value; - pmu_update_irq(env); -} - -static void pmintenclr_write(CPUARMState *env, const ARMCPRegInfo *ri, - uint64_t value) -{ - value &= pmu_counter_mask(env); - env->cp15.c9_pminten &= ~value; - pmu_update_irq(env); -} - static void vbar_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -1874,171 +898,6 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { /* the old v6 WFI, UNPREDICTABLE in v7 but we choose to NOP */ { .name = "NOP", .cp = 15, .crn = 7, .crm = 0, .opc1 = 0, .opc2 = 4, .access = PL1_W, .type = ARM_CP_NOP }, - /* - * Performance monitors are implementation defined in v7, - * but with an ARM recommended set of registers, which we - * follow. - * - * Performance registers fall into three categories: - * (a) always UNDEF in PL0, RW in PL1 (PMINTENSET, PMINTENCLR) - * (b) RO in PL0 (ie UNDEF on write), RW in PL1 (PMUSERENR) - * (c) UNDEF in PL0 if PMUSERENR.EN==0, otherwise accessible (all others) - * For the cases controlled by PMUSERENR we must set .access to PL0_RW - * or PL0_RO as appropriate and then check PMUSERENR in the helper fn. - */ - { .name = "PMCNTENSET", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 1, - .access = PL0_RW, .type = ARM_CP_ALIAS | ARM_CP_IO, - .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), - .writefn = pmcntenset_write, - .accessfn = pmreg_access, - .fgt = FGT_PMCNTEN, - .raw_writefn = raw_write }, - { .name = "PMCNTENSET_EL0", .state = ARM_CP_STATE_AA64, .type = ARM_CP_IO, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 1, - .access = PL0_RW, .accessfn = pmreg_access, - .fgt = FGT_PMCNTEN, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), .resetvalue = 0, - .writefn = pmcntenset_write, .raw_writefn = raw_write }, - { .name = "PMCNTENCLR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 2, - .access = PL0_RW, - .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcnten), - .accessfn = pmreg_access, - .fgt = FGT_PMCNTEN, - .writefn = pmcntenclr_write, .raw_writefn = raw_write, - .type = ARM_CP_ALIAS | ARM_CP_IO }, - { .name = "PMCNTENCLR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 2, - .access = PL0_RW, .accessfn = pmreg_access, - .fgt = FGT_PMCNTEN, - .type = ARM_CP_ALIAS | ARM_CP_IO, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcnten), - .writefn = pmcntenclr_write, .raw_writefn = raw_write }, - { .name = "PMOVSR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 3, - .access = PL0_RW, .type = ARM_CP_IO, - .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), - .accessfn = pmreg_access, - .fgt = FGT_PMOVS, - .writefn = pmovsr_write, - .raw_writefn = raw_write }, - { .name = "PMOVSCLR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 3, - .access = PL0_RW, .accessfn = pmreg_access, - .fgt = FGT_PMOVS, - .type = ARM_CP_ALIAS | ARM_CP_IO, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), - .writefn = pmovsr_write, - .raw_writefn = raw_write }, - { .name = "PMSWINC", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 4, - .access = PL0_W, .accessfn = pmreg_access_swinc, - .fgt = FGT_PMSWINC_EL0, - .type = ARM_CP_NO_RAW | ARM_CP_IO, - .writefn = pmswinc_write }, - { .name = "PMSWINC_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 4, - .access = PL0_W, .accessfn = pmreg_access_swinc, - .fgt = FGT_PMSWINC_EL0, - .type = ARM_CP_NO_RAW | ARM_CP_IO, - .writefn = pmswinc_write }, - { .name = "PMSELR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 5, - .access = PL0_RW, .type = ARM_CP_ALIAS, - .fgt = FGT_PMSELR_EL0, - .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmselr), - .accessfn = pmreg_access_selr, .writefn = pmselr_write, - .raw_writefn = raw_write}, - { .name = "PMSELR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 5, - .access = PL0_RW, .accessfn = pmreg_access_selr, - .fgt = FGT_PMSELR_EL0, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr), - .writefn = pmselr_write, .raw_writefn = raw_write, }, - { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, - .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS | ARM_CP_IO, - .fgt = FGT_PMCCNTR_EL0, - .readfn = pmccntr_read, .writefn = pmccntr_write32, - .accessfn = pmreg_access_ccntr }, - { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0, - .access = PL0_RW, .accessfn = pmreg_access_ccntr, - .fgt = FGT_PMCCNTR_EL0, - .type = ARM_CP_IO, - .fieldoffset = offsetof(CPUARMState, cp15.c15_ccnt), - .readfn = pmccntr_read, .writefn = pmccntr_write, - .raw_readfn = raw_read, .raw_writefn = raw_write, }, - { .name = "PMCCFILTR", .cp = 15, .opc1 = 0, .crn = 14, .crm = 15, .opc2 = 7, - .writefn = pmccfiltr_write_a32, .readfn = pmccfiltr_read_a32, - .access = PL0_RW, .accessfn = pmreg_access, - .fgt = FGT_PMCCFILTR_EL0, - .type = ARM_CP_ALIAS | ARM_CP_IO, - .resetvalue = 0, }, - { .name = "PMCCFILTR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 15, .opc2 = 7, - .writefn = pmccfiltr_write, .raw_writefn = raw_write, - .access = PL0_RW, .accessfn = pmreg_access, - .fgt = FGT_PMCCFILTR_EL0, - .type = ARM_CP_IO, - .fieldoffset = offsetof(CPUARMState, cp15.pmccfiltr_el0), - .resetvalue = 0, }, - { .name = "PMXEVTYPER", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 1, - .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, - .accessfn = pmreg_access, - .fgt = FGT_PMEVTYPERN_EL0, - .writefn = pmxevtyper_write, .readfn = pmxevtyper_read }, - { .name = "PMXEVTYPER_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 1, - .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, - .accessfn = pmreg_access, - .fgt = FGT_PMEVTYPERN_EL0, - .writefn = pmxevtyper_write, .readfn = pmxevtyper_read }, - { .name = "PMXEVCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 2, - .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, - .accessfn = pmreg_access_xevcntr, - .fgt = FGT_PMEVCNTRN_EL0, - .writefn = pmxevcntr_write, .readfn = pmxevcntr_read }, - { .name = "PMXEVCNTR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 2, - .access = PL0_RW, .type = ARM_CP_NO_RAW | ARM_CP_IO, - .accessfn = pmreg_access_xevcntr, - .fgt = FGT_PMEVCNTRN_EL0, - .writefn = pmxevcntr_write, .readfn = pmxevcntr_read }, - { .name = "PMUSERENR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 0, - .access = PL0_R | PL1_RW, .accessfn = access_tpm, - .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmuserenr), - .resetvalue = 0, - .writefn = pmuserenr_write, .raw_writefn = raw_write }, - { .name = "PMUSERENR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 0, - .access = PL0_R | PL1_RW, .accessfn = access_tpm, .type = ARM_CP_ALIAS, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pmuserenr), - .resetvalue = 0, - .writefn = pmuserenr_write, .raw_writefn = raw_write }, - { .name = "PMINTENSET", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 1, - .access = PL1_RW, .accessfn = access_tpm, - .fgt = FGT_PMINTEN, - .type = ARM_CP_ALIAS | ARM_CP_IO, - .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pminten), - .resetvalue = 0, - .writefn = pmintenset_write, .raw_writefn = raw_write }, - { .name = "PMINTENSET_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 1, - .access = PL1_RW, .accessfn = access_tpm, - .fgt = FGT_PMINTEN, - .type = ARM_CP_IO, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), - .writefn = pmintenset_write, .raw_writefn = raw_write, - .resetvalue = 0x0 }, - { .name = "PMINTENCLR", .cp = 15, .crn = 9, .crm = 14, .opc1 = 0, .opc2 = 2, - .access = PL1_RW, .accessfn = access_tpm, - .fgt = FGT_PMINTEN, - .type = ARM_CP_ALIAS | ARM_CP_IO, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), - .writefn = pmintenclr_write, .raw_writefn = raw_write }, - { .name = "PMINTENCLR_EL1", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 2, - .access = PL1_RW, .accessfn = access_tpm, - .fgt = FGT_PMINTEN, - .type = ARM_CP_ALIAS | ARM_CP_IO, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pminten), - .writefn = pmintenclr_write, .raw_writefn = raw_write }, { .name = "CCSIDR", .state = ARM_CP_STATE_BOTH, .opc0 = 3, .crn = 0, .crm = 0, .opc1 = 1, .opc2 = 0, .access = PL1_R, @@ -2121,25 +980,6 @@ static const ARMCPRegInfo v7_cp_reginfo[] = { .type = ARM_CP_NO_RAW, .access = PL1_R, .readfn = isr_read }, }; -static const ARMCPRegInfo pmovsset_cp_reginfo[] = { - /* PMOVSSET is not implemented in v7 before v7ve */ - { .name = "PMOVSSET", .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 3, - .access = PL0_RW, .accessfn = pmreg_access, - .fgt = FGT_PMOVS, - .type = ARM_CP_ALIAS | ARM_CP_IO, - .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmovsr), - .writefn = pmovsset_write, - .raw_writefn = raw_write }, - { .name = "PMOVSSET_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 14, .opc2 = 3, - .access = PL0_RW, .accessfn = pmreg_access, - .fgt = FGT_PMOVS, - .type = ARM_CP_ALIAS | ARM_CP_IO, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pmovsr), - .writefn = pmovsset_write, - .raw_writefn = raw_write }, -}; - static void teecr_write(CPUARMState *env, const ARMCPRegInfo *ri, uint64_t value) { @@ -6356,105 +5196,6 @@ static const ARMCPRegInfo nmi_reginfo[] = { .resetfn = arm_cp_reset_ignore }, }; -static void define_pmu_regs(ARMCPU *cpu) -{ - /* - * v7 performance monitor control register: same implementor - * field as main ID register, and we implement four counters in - * addition to the cycle count register. - */ - unsigned int i, pmcrn = pmu_num_counters(&cpu->env); - ARMCPRegInfo pmcr = { - .name = "PMCR", .cp = 15, .crn = 9, .crm = 12, .opc1 = 0, .opc2 = 0, - .access = PL0_RW, - .fgt = FGT_PMCR_EL0, - .type = ARM_CP_IO | ARM_CP_ALIAS, - .fieldoffset = offsetoflow32(CPUARMState, cp15.c9_pmcr), - .accessfn = pmreg_access, - .readfn = pmcr_read, .raw_readfn = raw_read, - .writefn = pmcr_write, .raw_writefn = raw_write, - }; - ARMCPRegInfo pmcr64 = { - .name = "PMCR_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 0, - .access = PL0_RW, .accessfn = pmreg_access, - .fgt = FGT_PMCR_EL0, - .type = ARM_CP_IO, - .fieldoffset = offsetof(CPUARMState, cp15.c9_pmcr), - .resetvalue = cpu->isar.reset_pmcr_el0, - .readfn = pmcr_read, .raw_readfn = raw_read, - .writefn = pmcr_write, .raw_writefn = raw_write, - }; - - define_one_arm_cp_reg(cpu, &pmcr); - define_one_arm_cp_reg(cpu, &pmcr64); - for (i = 0; i < pmcrn; i++) { - char *pmevcntr_name = g_strdup_printf("PMEVCNTR%d", i); - char *pmevcntr_el0_name = g_strdup_printf("PMEVCNTR%d_EL0", i); - char *pmevtyper_name = g_strdup_printf("PMEVTYPER%d", i); - char *pmevtyper_el0_name = g_strdup_printf("PMEVTYPER%d_EL0", i); - ARMCPRegInfo pmev_regs[] = { - { .name = pmevcntr_name, .cp = 15, .crn = 14, - .crm = 8 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, - .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, - .fgt = FGT_PMEVCNTRN_EL0, - .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, - .accessfn = pmreg_access_xevcntr }, - { .name = pmevcntr_el0_name, .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 8 | (3 & (i >> 3)), - .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access_xevcntr, - .type = ARM_CP_IO, - .fgt = FGT_PMEVCNTRN_EL0, - .readfn = pmevcntr_readfn, .writefn = pmevcntr_writefn, - .raw_readfn = pmevcntr_rawread, - .raw_writefn = pmevcntr_rawwrite }, - { .name = pmevtyper_name, .cp = 15, .crn = 14, - .crm = 12 | (3 & (i >> 3)), .opc1 = 0, .opc2 = i & 7, - .access = PL0_RW, .type = ARM_CP_IO | ARM_CP_ALIAS, - .fgt = FGT_PMEVTYPERN_EL0, - .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, - .accessfn = pmreg_access }, - { .name = pmevtyper_el0_name, .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 14, .crm = 12 | (3 & (i >> 3)), - .opc2 = i & 7, .access = PL0_RW, .accessfn = pmreg_access, - .fgt = FGT_PMEVTYPERN_EL0, - .type = ARM_CP_IO, - .readfn = pmevtyper_readfn, .writefn = pmevtyper_writefn, - .raw_writefn = pmevtyper_rawwrite }, - }; - define_arm_cp_regs(cpu, pmev_regs); - g_free(pmevcntr_name); - g_free(pmevcntr_el0_name); - g_free(pmevtyper_name); - g_free(pmevtyper_el0_name); - } - if (cpu_isar_feature(aa32_pmuv3p1, cpu)) { - ARMCPRegInfo v81_pmu_regs[] = { - { .name = "PMCEID2", .state = ARM_CP_STATE_AA32, - .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 4, - .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, - .fgt = FGT_PMCEIDN_EL0, - .resetvalue = extract64(cpu->pmceid0, 32, 32) }, - { .name = "PMCEID3", .state = ARM_CP_STATE_AA32, - .cp = 15, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 5, - .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, - .fgt = FGT_PMCEIDN_EL0, - .resetvalue = extract64(cpu->pmceid1, 32, 32) }, - }; - define_arm_cp_regs(cpu, v81_pmu_regs); - } - if (cpu_isar_feature(any_pmuv3p4, cpu)) { - static const ARMCPRegInfo v84_pmmir = { - .name = "PMMIR_EL1", .state = ARM_CP_STATE_BOTH, - .opc0 = 3, .opc1 = 0, .crn = 9, .crm = 14, .opc2 = 6, - .access = PL1_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, - .fgt = FGT_PMMIR_EL1, - .resetvalue = 0 - }; - define_one_arm_cp_reg(cpu, &v84_pmmir); - } -} - #ifndef CONFIG_USER_ONLY /* * We don't know until after realize whether there's a GICv3 @@ -7385,9 +6126,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) if (arm_feature(env, ARM_FEATURE_V6K)) { define_arm_cp_regs(cpu, v6k_cp_reginfo); } - if (arm_feature(env, ARM_FEATURE_V7VE)) { - define_arm_cp_regs(cpu, pmovsset_cp_reginfo); - } if (arm_feature(env, ARM_FEATURE_V7)) { ARMCPRegInfo clidr = { .name = "CLIDR", .state = ARM_CP_STATE_BOTH, @@ -7400,7 +6138,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_one_arm_cp_reg(cpu, &clidr); define_arm_cp_regs(cpu, v7_cp_reginfo); define_debug_regs(cpu); - define_pmu_regs(cpu); } else { define_arm_cp_regs(cpu, not_v7_cp_reginfo); } @@ -7656,26 +6393,6 @@ void register_cp_regs_for_features(ARMCPU *cpu) .access = PL1_R, .type = ARM_CP_CONST, .accessfn = access_aa64_tid3, .resetvalue = 0 }, - { .name = "PMCEID0", .state = ARM_CP_STATE_AA32, - .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 6, - .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, - .fgt = FGT_PMCEIDN_EL0, - .resetvalue = extract64(cpu->pmceid0, 0, 32) }, - { .name = "PMCEID0_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 6, - .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, - .fgt = FGT_PMCEIDN_EL0, - .resetvalue = cpu->pmceid0 }, - { .name = "PMCEID1", .state = ARM_CP_STATE_AA32, - .cp = 15, .opc1 = 0, .crn = 9, .crm = 12, .opc2 = 7, - .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, - .fgt = FGT_PMCEIDN_EL0, - .resetvalue = extract64(cpu->pmceid1, 0, 32) }, - { .name = "PMCEID1_EL0", .state = ARM_CP_STATE_AA64, - .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 12, .opc2 = 7, - .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, - .fgt = FGT_PMCEIDN_EL0, - .resetvalue = cpu->pmceid1 }, }; #ifdef CONFIG_USER_ONLY static const ARMCPRegUserSpaceInfo v8_user_idregs[] = { @@ -8514,6 +7231,8 @@ void register_cp_regs_for_features(ARMCPU *cpu) define_arm_cp_regs(cpu, ccsidr2_reginfo); } + define_pm_cpregs(cpu); + #ifndef CONFIG_USER_ONLY /* * Register redirections and aliases must be done last, diff --git a/target/arm/internals.h b/target/arm/internals.h index bcaf8965fc..c4765e4489 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1873,6 +1873,8 @@ void define_debug_regs(ARMCPU *cpu); void define_tlb_insn_regs(ARMCPU *cpu); /* Add the cpreg definitions for AT instructions */ void define_at_insn_regs(ARMCPU *cpu); +/* Add the cpreg definitions for PM cpregs */ +void define_pm_cpregs(ARMCPU *cpu); /* Effective value of MDCR_EL2 */ static inline uint64_t arm_mdcr_el2_eff(CPUARMState *env) diff --git a/target/arm/meson.build b/target/arm/meson.build index 7aa81e30ab..07d9271aa4 100644 --- a/target/arm/meson.build +++ b/target/arm/meson.build @@ -22,6 +22,7 @@ arm_user_ss.add(when: 'TARGET_AARCH64', if_false: files( 'cpu32-stubs.c', )) arm_user_ss.add(files( + 'cpregs-pmu.c', 'debug_helper.c', 'helper.c', 'vfp_fpscr.c', @@ -36,6 +37,7 @@ arm_common_system_ss.add(files( 'arch_dump.c', 'arm-powerctl.c', 'cortex-regs.c', + 'cpregs-pmu.c', 'debug_helper.c', 'helper.c', 'machine.c', From c563cd7e61d074f58eef413322144461dd243716 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 4 Jul 2025 17:56:36 +0100 Subject: [PATCH 2079/2760] target/arm: Don't enforce NSE,NS check for EL3->EL3 returns In the Arm ARM, rule R_TYTWB that defines illegal exception return cases includes the case: If FEAT_RME is implemented, then if SCR_EL3.{NSE, NS} is {1, 0}, an exception return from EL3 to a lower Exception level Our implementation of this check fails to check that the return is to a lower exception level, so it will incorrectly fire on EL3->EL3 exception returns. Fix the check condition. This requires us to move it further down in the function to a point where we know the new_el value. Fixes: 35aa6715ddcd9 ("target/arm: Catch illegal-exception-return from EL3 with bad NSE/NS") Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3016 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250704165636.261888-1-peter.maydell@linaro.org --- target/arm/tcg/helper-a64.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/target/arm/tcg/helper-a64.c b/target/arm/tcg/helper-a64.c index c66d521278..71c6c44ee8 100644 --- a/target/arm/tcg/helper-a64.c +++ b/target/arm/tcg/helper-a64.c @@ -658,15 +658,6 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) spsr &= ~PSTATE_SS; } - /* - * FEAT_RME forbids return from EL3 with an invalid security state. - * We don't need an explicit check for FEAT_RME here because we enforce - * in scr_write() that you can't set the NSE bit without it. - */ - if (cur_el == 3 && (env->cp15.scr_el3 & (SCR_NS | SCR_NSE)) == SCR_NSE) { - goto illegal_return; - } - new_el = el_from_spsr(spsr); if (new_el == -1) { goto illegal_return; @@ -678,6 +669,17 @@ void HELPER(exception_return)(CPUARMState *env, uint64_t new_pc) goto illegal_return; } + /* + * FEAT_RME forbids return from EL3 to a lower exception level + * with an invalid security state. + * We don't need an explicit check for FEAT_RME here because we enforce + * in scr_write() that you can't set the NSE bit without it. + */ + if (cur_el == 3 && new_el < 3 && + (env->cp15.scr_el3 & (SCR_NS | SCR_NSE)) == SCR_NSE) { + goto illegal_return; + } + if (new_el != 0 && arm_el_is_aa64(env, new_el) != return_to_aa64) { /* Return to an EL which is configured for a different register width */ goto illegal_return; From 930180f3b9a292639eb894f1ca846683834ed4b7 Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Sun, 29 Jun 2025 22:48:50 +0200 Subject: [PATCH 2080/2760] hw/arm/fsl-imx8mp: Wire VIRQ and VFIQ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allows to run KVM guests inside the imx8mp-evk machine. Fixes: a4eefc69b237 ("hw/arm: Add i.MX 8M Plus EVK board") CC: qemu-stable Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- hw/arm/fsl-imx8mp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/arm/fsl-imx8mp.c b/hw/arm/fsl-imx8mp.c index 23e662c16c..866f4d1d74 100644 --- a/hw/arm/fsl-imx8mp.c +++ b/hw/arm/fsl-imx8mp.c @@ -356,6 +356,10 @@ static void fsl_imx8mp_realize(DeviceState *dev, Error **errp) qdev_get_gpio_in(cpudev, ARM_CPU_IRQ)); sysbus_connect_irq(gicsbd, i + ms->smp.cpus, qdev_get_gpio_in(cpudev, ARM_CPU_FIQ)); + sysbus_connect_irq(gicsbd, i + 2 * ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VIRQ)); + sysbus_connect_irq(gicsbd, i + 3 * ms->smp.cpus, + qdev_get_gpio_in(cpudev, ARM_CPU_VFIQ)); } } From 66ba6d1367d7e81d705430ff611af01280953992 Mon Sep 17 00:00:00 2001 From: Haibo Xu Date: Mon, 7 Jul 2025 18:40:27 +0200 Subject: [PATCH 2081/2760] hw/arm: Allow setting KVM vGIC maintenance IRQ Allow virt arm machine to set the interrupt ID for the KVM GIC maintenance interrupt. This setting must be done before the KVM_DEV_ARM_VGIC_CTRL_INIT hence the choice to perform the setting in the GICv3 realize instead of proceeding the same way as kvm_arm_pmu_set_irq(). Signed-off-by: Haibo Xu Signed-off-by: Miguel Luis Signed-off-by: Eric Auger Message-id: 20250707164129.1167837-2-eric.auger@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/virt.c | 3 +++ hw/intc/arm_gicv3_common.c | 1 + hw/intc/arm_gicv3_kvm.c | 21 +++++++++++++++++++++ include/hw/intc/arm_gicv3_common.h | 1 + 4 files changed, 26 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 394e8b5301..c9f3991937 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -833,6 +833,9 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) OBJECT(mem), &error_fatal); qdev_prop_set_bit(vms->gic, "has-lpi", true); } + } else if (vms->virt) { + qdev_prop_set_uint32(vms->gic, "maintenance-interrupt-id", + ARCH_GIC_MAINT_IRQ); } } else { if (!kvm_irqchip_in_kernel()) { diff --git a/hw/intc/arm_gicv3_common.c b/hw/intc/arm_gicv3_common.c index 1cee68193c..e438d8c042 100644 --- a/hw/intc/arm_gicv3_common.c +++ b/hw/intc/arm_gicv3_common.c @@ -612,6 +612,7 @@ static const Property arm_gicv3_common_properties[] = { DEFINE_PROP_BOOL("has-lpi", GICv3State, lpi_enable, 0), DEFINE_PROP_BOOL("has-nmi", GICv3State, nmi_support, 0), DEFINE_PROP_BOOL("has-security-extensions", GICv3State, security_extn, 0), + DEFINE_PROP_UINT32("maintenance-interrupt-id", GICv3State, maint_irq, 0), /* * Compatibility property: force 8 bits of physical priority, even * if the CPU being emulated should have fewer. diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 3be3bf6c28..b30aac7aee 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -22,6 +22,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/intc/arm_gicv3_common.h" +#include "hw/arm/virt.h" #include "qemu/error-report.h" #include "qemu/module.h" #include "system/kvm.h" @@ -825,6 +826,26 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) return; } + if (s->maint_irq) { + int ret; + + ret = kvm_device_check_attr(s->dev_fd, + KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ, 0); + if (!ret) { + error_setg_errno(errp, errno, + "VGICv3 setting maintenance IRQ is not " + "supported by this host kernel"); + return; + } + + ret = kvm_device_access(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ, 0, + &s->maint_irq, true, errp); + if (ret) { + error_setg_errno(errp, errno, "Failed to set VGIC maintenance IRQ"); + return; + } + } + multiple_redist_region_allowed = kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION); diff --git a/include/hw/intc/arm_gicv3_common.h b/include/hw/intc/arm_gicv3_common.h index a3d6a0e507..c18503869f 100644 --- a/include/hw/intc/arm_gicv3_common.h +++ b/include/hw/intc/arm_gicv3_common.h @@ -231,6 +231,7 @@ struct GICv3State { uint32_t num_cpu; uint32_t num_irq; uint32_t revision; + uint32_t maint_irq; bool lpi_enable; bool nmi_support; bool security_extn; From 776aac5653cd9b072c735fcd65a3e58bd59370f4 Mon Sep 17 00:00:00 2001 From: Haibo Xu Date: Mon, 7 Jul 2025 18:40:28 +0200 Subject: [PATCH 2082/2760] target/arm/kvm: Add helper to detect EL2 when using KVM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce query support for KVM_CAP_ARM_EL2. Signed-off-by: Haibo Xu Signed-off-by: Miguel Luis Signed-off-by: Eric Auger Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250707164129.1167837-3-eric.auger@redhat.com Signed-off-by: Peter Maydell --- target/arm/kvm-stub.c | 5 +++++ target/arm/kvm.c | 5 +++++ target/arm/kvm_arm.h | 7 +++++++ 3 files changed, 17 insertions(+) diff --git a/target/arm/kvm-stub.c b/target/arm/kvm-stub.c index 34e57fab01..c93462c5b9 100644 --- a/target/arm/kvm-stub.c +++ b/target/arm/kvm-stub.c @@ -47,6 +47,11 @@ bool kvm_arm_mte_supported(void) return false; } +bool kvm_arm_el2_supported(void) +{ + return false; +} + /* * These functions should never actually be called without KVM support. */ diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 8ab0d692d3..9fdf354f3b 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -1763,6 +1763,11 @@ bool kvm_arm_aarch32_supported(void) return kvm_check_extension(kvm_state, KVM_CAP_ARM_EL1_32BIT); } +bool kvm_arm_el2_supported(void) +{ + return kvm_check_extension(kvm_state, KVM_CAP_ARM_EL2); +} + bool kvm_arm_sve_supported(void) { return kvm_check_extension(kvm_state, KVM_CAP_ARM_SVE); diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index 7dc83caed5..b4cad05155 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -191,6 +191,13 @@ bool kvm_arm_sve_supported(void); */ bool kvm_arm_mte_supported(void); +/** + * kvm_arm_el2_supported: + * + * Returns true if KVM can enable EL2 and false otherwise. + */ +bool kvm_arm_el2_supported(void); + /** * kvm_arm_get_max_vm_ipa_size: * @ms: Machine state handle From 4ee1efc9871936e2561212e409ad094c2ae83cad Mon Sep 17 00:00:00 2001 From: Haibo Xu Date: Mon, 7 Jul 2025 18:40:29 +0200 Subject: [PATCH 2083/2760] target/arm: Enable feature ARM_FEATURE_EL2 if EL2 is supported KVM_CAP_ARM_EL2 must be supported by the cpu to enable ARM_FEATURE_EL2. In case the host does support NV, expose the feature. Signed-off-by: Haibo Xu Signed-off-by: Miguel Luis Signed-off-by: Eric Auger Reviewed-by: Richard Henderson Message-id: 20250707164129.1167837-4-eric.auger@redhat.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/kvm.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/target/arm/kvm.c b/target/arm/kvm.c index 9fdf354f3b..6672344855 100644 --- a/target/arm/kvm.c +++ b/target/arm/kvm.c @@ -251,6 +251,7 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) */ int fdarray[3]; bool sve_supported; + bool el2_supported; bool pmu_supported = false; uint64_t features = 0; int err; @@ -270,6 +271,14 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) init.features[0] |= 1 << KVM_ARM_VCPU_SVE; } + /* + * Ask for EL2 if supported. + */ + el2_supported = kvm_arm_el2_supported(); + if (el2_supported) { + init.features[0] |= 1 << KVM_ARM_VCPU_HAS_EL2; + } + /* * Ask for Pointer Authentication if supported, so that we get * the unsanitized field values for AA64ISAR1_EL1. @@ -423,6 +432,10 @@ static bool kvm_arm_get_host_cpu_features(ARMHostCPUFeatures *ahcf) features |= 1ULL << ARM_FEATURE_AARCH64; features |= 1ULL << ARM_FEATURE_GENERIC_TIMER; + if (el2_supported) { + features |= 1ULL << ARM_FEATURE_EL2; + } + ahcf->features = features; return true; @@ -1888,6 +1901,9 @@ int kvm_arch_init_vcpu(CPUState *cs) cpu->kvm_init_features[0] |= (1 << KVM_ARM_VCPU_PTRAUTH_ADDRESS | 1 << KVM_ARM_VCPU_PTRAUTH_GENERIC); } + if (cpu->has_el2 && kvm_arm_el2_supported()) { + cpu->kvm_init_features[0] |= 1 << KVM_ARM_VCPU_HAS_EL2; + } /* Do KVM_ARM_VCPU_INIT ioctl */ ret = kvm_arm_vcpu_init(cpu); From f36032440f5dd7ceb90833dd44600363b1472757 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 7 Jul 2025 18:40:30 +0200 Subject: [PATCH 2084/2760] hw/arm/arm_gicv3_kvm: Add a migration blocker with kvm nested virt We may be miss some NV related GIC register save/restore. Until we complete the study, let's add a migration blocker when the maintenance IRQ is set. Signed-off-by: Eric Auger Message-id: 20250707164129.1167837-5-eric.auger@redhat.com Suggested-by: Peter Maydell Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index b30aac7aee..8ed88e7429 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -827,8 +827,16 @@ static void kvm_arm_gicv3_realize(DeviceState *dev, Error **errp) } if (s->maint_irq) { + Error *kvm_nv_migration_blocker = NULL; int ret; + error_setg(&kvm_nv_migration_blocker, + "Live migration disabled because KVM nested virt is enabled"); + if (migrate_add_blocker(&kvm_nv_migration_blocker, errp)) { + error_free(kvm_nv_migration_blocker); + return; + } + ret = kvm_device_check_attr(s->dev_fd, KVM_DEV_ARM_VGIC_GRP_MAINT_IRQ, 0); if (!ret) { From 851dcb8355cb3c37fbbf8cc99ae5fac4871d1a44 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 Jul 2025 11:39:33 +0100 Subject: [PATCH 2085/2760] hw/arm/virt: Allow virt extensions with KVM Up to now virt support on guest has been only supported with TCG. Now it becomes feasible to use it with KVM acceleration. Check neither in-kernel GICv3 nor aarch64=off is used along with KVM EL2. Signed-off-by: Haibo Xu Signed-off-by: Miguel Luis Signed-off-by: Eric Auger Reviewed-by: Richard Henderson Message-id: 20250707164129.1167837-6-eric.auger@redhat.com [PMM: make "kernel doesn't have EL2 support" error message distinct from the old "QEMU doesn't have KVM EL2 support" one] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/arm/virt.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index c9f3991937..8070ff7b11 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -797,6 +797,13 @@ static void create_gic(VirtMachineState *vms, MemoryRegion *mem) default: g_assert_not_reached(); } + + if (kvm_enabled() && vms->virt && + (revision != 3 || !kvm_irqchip_in_kernel())) { + error_report("KVM EL2 is only supported with in-kernel GICv3"); + exit(1); + } + vms->gic = qdev_new(gictype); qdev_prop_set_uint32(vms->gic, "revision", revision); qdev_prop_set_uint32(vms->gic, "num-cpu", smp_cpus); @@ -2092,6 +2099,10 @@ static void virt_post_cpus_gic_realized(VirtMachineState *vms, memory_region_init_ram(pvtime, NULL, "pvtime", pvtime_size, NULL); memory_region_add_subregion(sysmem, pvtime_reg_base, pvtime); } + if (!aarch64 && vms->virt) { + error_report("KVM does not support EL2 on an AArch32 vCPU"); + exit(1); + } CPU_FOREACH(cpu) { if (pmu) { @@ -2237,7 +2248,13 @@ static void machvirt_init(MachineState *machine) exit(1); } - if (vms->virt && !tcg_enabled() && !qtest_enabled()) { + if (vms->virt && kvm_enabled() && !kvm_arm_el2_supported()) { + error_report("mach-virt: host kernel KVM does not support providing " + "Virtualization extensions to the guest CPU"); + exit(1); + } + + if (vms->virt && !kvm_enabled() && !tcg_enabled() && !qtest_enabled()) { error_report("mach-virt: %s does not support providing " "Virtualization extensions to the guest CPU", current_accel_name()); From 677bb509bfbe5f94bb15a62d8490cbad89aa9b94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 10 Feb 2025 13:10:45 +0100 Subject: [PATCH 2086/2760] system/qdev: Remove pointless NULL check in qdev_device_add_from_qdict MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity reported a unnecessary NULL check: qemu/system/qdev-monitor.c: 720 in qdev_device_add_from_qdict() 683 /* create device */ 684 dev = qdev_new(driver); ... 719 err_del_dev: >>> CID 1590192: Null pointer dereferences (REVERSE_INULL) >>> Null-checking "dev" suggests that it may be null, but it has already been dereferenced on all paths leading to the check. 720 if (dev) { 721 object_unparent(OBJECT(dev)); 722 object_unref(OBJECT(dev)); 723 } 724 return NULL; 725 } Indeed, unlike qdev_try_new() which can return NULL, qdev_new() always returns a heap pointer (or aborts). Remove the unnecessary assignment and check. Fixes: f3a85056569 ("qdev/qbus: add hidden device support") Resolves: Coverity CID 1590192 (Null pointer dereferences) Suggested-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Peter Maydell --- system/qdev-monitor.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/system/qdev-monitor.c b/system/qdev-monitor.c index 5588ed2047..2ac92d0a07 100644 --- a/system/qdev-monitor.c +++ b/system/qdev-monitor.c @@ -628,7 +628,7 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, DeviceClass *dc; const char *driver, *path; char *id; - DeviceState *dev = NULL; + DeviceState *dev; BusState *bus = NULL; QDict *properties; @@ -717,10 +717,9 @@ DeviceState *qdev_device_add_from_qdict(const QDict *opts, return dev; err_del_dev: - if (dev) { - object_unparent(OBJECT(dev)); - object_unref(OBJECT(dev)); - } + object_unparent(OBJECT(dev)); + object_unref(OBJECT(dev)); + return NULL; } From 47a4f6a900e1d0764cb973b7140f471859de4128 Mon Sep 17 00:00:00 2001 From: Shameer Kolothum Date: Tue, 8 Jul 2025 16:40:44 +0100 Subject: [PATCH 2087/2760] hw/arm/virt-acpi-build: Don't create ITS id mappings by default Commit d6afe18b7242 ("hw/arm/virt-acpi-build: Fix ACPI IORT and MADT tables when its=off") moved ITS group node generation under the its=on condition. However, it still creates rc_its_idmaps unconditionally, which results in duplicate ID mappings in the IORT table. Fixes:d6afe18b7242 ("hw/arm/virt-acpi-build: Fix ACPI IORT and MADT tables when its=off") Reviewed-by: Jonathan Cameron Reviewed-by: Eric Auger Reviewed-by: Donald Dutile Tested-by: Eric Auger Signed-off-by: Shameer Kolothum Signed-off-by: Peter Maydell --- hw/arm/virt-acpi-build.c | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index c3b9b3f6ea..0dfb8ec2c3 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -358,12 +358,6 @@ build_iort(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) /* Sort the smmu idmap by input_base */ g_array_sort(rc_smmu_idmaps, iort_idmap_compare); - /* - * Knowing the ID ranges from the RC to the SMMU, it's possible to - * determine the ID ranges from RC that are directed to the ITS. - */ - create_rc_its_idmaps(rc_its_idmaps, rc_smmu_idmaps); - nb_nodes = 2; /* RC and SMMUv3 */ rc_mapping_count = rc_smmu_idmaps->len; From bffbb430ee045c616dfe13ad4fd44823f18e6b21 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 23 Jun 2025 15:34:36 +0800 Subject: [PATCH 2088/2760] rust/qemu-api: Fix binding path in source directory The build.rs had supported placing bindings.inc.rs in rust/qemu-api/src, but this "not encouraged" feature is broken. Considering that manually copying bindings.inc.rs to the development directory is also useful, fix the bindings.inc.rs path generation to give this feature another chance. Fixes: commit 1ae4ca0463d7 ("rust: move rust.bindgen to qemu-api crate") Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250623073436.1833357-1-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- rust/qemu-api/build.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs index 7849486c1b..29d0945625 100644 --- a/rust/qemu-api/build.rs +++ b/rust/qemu-api/build.rs @@ -9,12 +9,14 @@ use std::os::windows::fs::symlink_file; use std::{env, fs::remove_file, io::Result, path::Path}; fn main() -> Result<()> { - // Placing bindings.inc.rs in the source directory is supported - // but not documented or encouraged. - let path = env::var("MESON_BUILD_ROOT") - .unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR"))); + let file = if let Ok(root) = env::var("MESON_BUILD_ROOT") { + format!("{root}/rust/qemu-api/bindings.inc.rs") + } else { + // Placing bindings.inc.rs in the source directory is supported + // but not documented or encouraged. + format!("{}/src/bindings.inc.rs", env!("CARGO_MANIFEST_DIR")) + }; - let file = format!("{path}/rust/qemu-api/bindings.inc.rs"); let file = Path::new(&file); if !Path::new(&file).exists() { panic!(concat!( From caa08d30f240c07f2b6fd08c6ffb9ae28d187f09 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 3 Jul 2025 16:33:43 +0300 Subject: [PATCH 2089/2760] rust/qemu-api-macros: use syn::Error directly Our MacroError type wraps syn::Error as a variant, and uses another variant for custom errors. Fortunately syn::Error can be used directly, avoiding extra code on our side, so change the proc macro crate to use it. Signed-off-by: Manos Pitsidianakis Link: https://lore.kernel.org/r/20250703-rust_macros-v1-1-b99f82febbbf@linaro.org Signed-off-by: Paolo Bonzini --- docs/devel/rust.rst | 11 ++-- rust/qemu-api-macros/src/bits.rs | 58 ++++++++------------- rust/qemu-api-macros/src/lib.rs | 86 +++++++++++++++---------------- rust/qemu-api-macros/src/utils.rs | 26 ---------- 4 files changed, 70 insertions(+), 111 deletions(-) delete mode 100644 rust/qemu-api-macros/src/utils.rs diff --git a/docs/devel/rust.rst b/docs/devel/rust.rst index dc8c44109e..b6737536c6 100644 --- a/docs/devel/rust.rst +++ b/docs/devel/rust.rst @@ -351,7 +351,7 @@ Writing procedural macros ''''''''''''''''''''''''' By conventions, procedural macros are split in two functions, one -returning ``Result`` with the body of +returning ``Result`` with the body of the procedural macro, and the second returning ``proc_macro::TokenStream`` which is the actual procedural macro. The former's name is the same as the latter with the ``_or_error`` suffix. The code for the latter is more @@ -361,18 +361,19 @@ from the type after ``as`` in the invocation of ``parse_macro_input!``:: #[proc_macro_derive(Object)] pub fn derive_object(input: TokenStream) -> TokenStream { let input = parse_macro_input!(input as DeriveInput); - let expanded = derive_object_or_error(input).unwrap_or_else(Into::into); - TokenStream::from(expanded) + derive_object_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() } The ``qemu_api_macros`` crate has utility functions to examine a ``DeriveInput`` and perform common checks (e.g. looking for a struct -with named fields). These functions return ``Result<..., MacroError>`` +with named fields). These functions return ``Result<..., syn::Error>`` and can be used easily in the procedural macro function:: fn derive_object_or_error(input: DeriveInput) -> - Result + Result { is_c_repr(&input, "#[derive(Object)]")?; diff --git a/rust/qemu-api-macros/src/bits.rs b/rust/qemu-api-macros/src/bits.rs index 5ba84757ee..a80a3b9fee 100644 --- a/rust/qemu-api-macros/src/bits.rs +++ b/rust/qemu-api-macros/src/bits.rs @@ -6,8 +6,7 @@ use proc_macro2::{ Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream, TokenTree, TokenTree as TT, }; - -use crate::utils::MacroError; +use syn::Error; pub struct BitsConstInternal { typ: TokenTree, @@ -36,27 +35,21 @@ impl BitsConstInternal { tok: TokenTree, it: &mut dyn Iterator, out: &mut TokenStream, - ) -> Result, MacroError> { + ) -> Result, Error> { let next = match tok { TT::Group(ref g) => { if g.delimiter() != Delimiter::Parenthesis && g.delimiter() != Delimiter::None { - return Err(MacroError::Message("expected parenthesis".into(), g.span())); + return Err(Error::new(g.span(), "expected parenthesis")); } let mut stream = g.stream().into_iter(); let Some(first_tok) = stream.next() else { - return Err(MacroError::Message( - "expected operand, found ')'".into(), - g.span(), - )); + return Err(Error::new(g.span(), "expected operand, found ')'")); }; let mut output = TokenStream::new(); // start from the lowest precedence let next = self.parse_or(first_tok, &mut stream, &mut output)?; if let Some(tok) = next { - return Err(MacroError::Message( - format!("unexpected token {tok}"), - tok.span(), - )); + return Err(Error::new(tok.span(), format!("unexpected token {tok}"))); } out.extend(Some(paren(output))); it.next() @@ -74,20 +67,17 @@ impl BitsConstInternal { } TT::Punct(ref p) => { if p.as_char() != '!' { - return Err(MacroError::Message("expected operand".into(), p.span())); + return Err(Error::new(p.span(), "expected operand")); } let Some(rhs_tok) = it.next() else { - return Err(MacroError::Message( - "expected operand at end of input".into(), - p.span(), - )); + return Err(Error::new(p.span(), "expected operand at end of input")); }; let next = self.parse_primary(rhs_tok, it, out)?; out.extend([punct('.'), ident("invert"), paren(TokenStream::new())]); next } _ => { - return Err(MacroError::Message("unexpected literal".into(), tok.span())); + return Err(Error::new(tok.span(), "unexpected literal")); } }; Ok(next) @@ -99,7 +89,7 @@ impl BitsConstInternal { TokenTree, &mut dyn Iterator, &mut TokenStream, - ) -> Result, MacroError>, + ) -> Result, Error>, >( &self, tok: TokenTree, @@ -108,7 +98,7 @@ impl BitsConstInternal { ch: char, f: F, method: &'static str, - ) -> Result, MacroError> { + ) -> Result, Error> { let mut next = f(self, tok, it, out)?; while next.is_some() { let op = next.as_ref().unwrap(); @@ -118,10 +108,7 @@ impl BitsConstInternal { } let Some(rhs_tok) = it.next() else { - return Err(MacroError::Message( - "expected operand at end of input".into(), - p.span(), - )); + return Err(Error::new(p.span(), "expected operand at end of input")); }; let mut rhs = TokenStream::new(); next = f(self, rhs_tok, it, &mut rhs)?; @@ -136,7 +123,7 @@ impl BitsConstInternal { tok: TokenTree, it: &mut dyn Iterator, out: &mut TokenStream, - ) -> Result, MacroError> { + ) -> Result, Error> { self.parse_binop(tok, it, out, '-', Self::parse_primary, "difference") } @@ -146,7 +133,7 @@ impl BitsConstInternal { tok: TokenTree, it: &mut dyn Iterator, out: &mut TokenStream, - ) -> Result, MacroError> { + ) -> Result, Error> { self.parse_binop(tok, it, out, '&', Self::parse_sub, "intersection") } @@ -156,7 +143,7 @@ impl BitsConstInternal { tok: TokenTree, it: &mut dyn Iterator, out: &mut TokenStream, - ) -> Result, MacroError> { + ) -> Result, Error> { self.parse_binop(tok, it, out, '^', Self::parse_and, "symmetric_difference") } @@ -166,13 +153,13 @@ impl BitsConstInternal { tok: TokenTree, it: &mut dyn Iterator, out: &mut TokenStream, - ) -> Result, MacroError> { + ) -> Result, Error> { self.parse_binop(tok, it, out, '|', Self::parse_xor, "union") } pub fn parse( it: &mut dyn Iterator, - ) -> Result { + ) -> Result { let mut pos = Span::call_site(); let mut typ = proc_macro2::TokenStream::new(); @@ -198,15 +185,15 @@ impl BitsConstInternal { }; let Some(tok) = next else { - return Err(MacroError::Message( - "expected expression, do not call this macro directly".into(), + return Err(Error::new( pos, + "expected expression, do not call this macro directly", )); }; let TT::Group(ref _group) = tok else { - return Err(MacroError::Message( - "expected parenthesis, do not call this macro directly".into(), + return Err(Error::new( tok.span(), + "expected parenthesis, do not call this macro directly", )); }; let mut out = TokenStream::new(); @@ -219,10 +206,7 @@ impl BitsConstInternal { // A parenthesized expression is a single production of the grammar, // so the input must have reached the last token. if let Some(tok) = next { - return Err(MacroError::Message( - format!("unexpected token {tok}"), - tok.span(), - )); + return Err(Error::new(tok.span(), format!("unexpected token {tok}"))); } Ok(out) } diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index c18bb4e036..2cb79c799a 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -6,83 +6,79 @@ use proc_macro::TokenStream; use quote::quote; use syn::{ parse_macro_input, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data, - DeriveInput, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Variant, + DeriveInput, Error, Field, Fields, FieldsUnnamed, Ident, Meta, Path, Token, Variant, }; - -mod utils; -use utils::MacroError; - mod bits; use bits::BitsConstInternal; fn get_fields<'a>( input: &'a DeriveInput, msg: &str, -) -> Result<&'a Punctuated, MacroError> { +) -> Result<&'a Punctuated, Error> { let Data::Struct(ref s) = &input.data else { - return Err(MacroError::Message( - format!("Struct required for {msg}"), + return Err(Error::new( input.ident.span(), + format!("Struct required for {msg}"), )); }; let Fields::Named(ref fs) = &s.fields else { - return Err(MacroError::Message( - format!("Named fields required for {msg}"), + return Err(Error::new( input.ident.span(), + format!("Named fields required for {msg}"), )); }; Ok(&fs.named) } -fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, MacroError> { +fn get_unnamed_field<'a>(input: &'a DeriveInput, msg: &str) -> Result<&'a Field, Error> { let Data::Struct(ref s) = &input.data else { - return Err(MacroError::Message( - format!("Struct required for {msg}"), + return Err(Error::new( input.ident.span(), + format!("Struct required for {msg}"), )); }; let Fields::Unnamed(FieldsUnnamed { ref unnamed, .. }) = &s.fields else { - return Err(MacroError::Message( - format!("Tuple struct required for {msg}"), + return Err(Error::new( s.fields.span(), + format!("Tuple struct required for {msg}"), )); }; if unnamed.len() != 1 { - return Err(MacroError::Message( - format!("A single field is required for {msg}"), + return Err(Error::new( s.fields.span(), + format!("A single field is required for {msg}"), )); } Ok(&unnamed[0]) } -fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> { +fn is_c_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> { let expected = parse_quote! { #[repr(C)] }; if input.attrs.iter().any(|attr| attr == &expected) { Ok(()) } else { - Err(MacroError::Message( - format!("#[repr(C)] required for {msg}"), + Err(Error::new( input.ident.span(), + format!("#[repr(C)] required for {msg}"), )) } } -fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), MacroError> { +fn is_transparent_repr(input: &DeriveInput, msg: &str) -> Result<(), Error> { let expected = parse_quote! { #[repr(transparent)] }; if input.attrs.iter().any(|attr| attr == &expected) { Ok(()) } else { - Err(MacroError::Message( - format!("#[repr(transparent)] required for {msg}"), + Err(Error::new( input.ident.span(), + format!("#[repr(transparent)] required for {msg}"), )) } } -fn derive_object_or_error(input: DeriveInput) -> Result { +fn derive_object_or_error(input: DeriveInput) -> Result { is_c_repr(&input, "#[derive(Object)]")?; let name = &input.ident; @@ -103,12 +99,13 @@ fn derive_object_or_error(input: DeriveInput) -> Result TokenStream { let input = parse_macro_input!(input as DeriveInput); - let expanded = derive_object_or_error(input).unwrap_or_else(Into::into); - TokenStream::from(expanded) + derive_object_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() } -fn derive_opaque_or_error(input: DeriveInput) -> Result { +fn derive_opaque_or_error(input: DeriveInput) -> Result { is_transparent_repr(&input, "#[derive(Wrapper)]")?; let name = &input.ident; @@ -149,13 +146,14 @@ fn derive_opaque_or_error(input: DeriveInput) -> Result TokenStream { let input = parse_macro_input!(input as DeriveInput); - let expanded = derive_opaque_or_error(input).unwrap_or_else(Into::into); - TokenStream::from(expanded) + derive_opaque_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() } #[allow(non_snake_case)] -fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result { +fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result { let repr = input.attrs.iter().find(|attr| attr.path().is_ident("repr")); if let Some(repr) = repr { let nested = repr.parse_args_with(Punctuated::::parse_terminated)?; @@ -170,23 +168,23 @@ fn get_repr_uN(input: &DeriveInput, msg: &str) -> Result { } } - Err(MacroError::Message( - format!("#[repr(u8/u16/u32/u64) required for {msg}"), + Err(Error::new( input.ident.span(), + format!("#[repr(u8/u16/u32/u64) required for {msg}"), )) } -fn get_variants(input: &DeriveInput) -> Result<&Punctuated, MacroError> { +fn get_variants(input: &DeriveInput) -> Result<&Punctuated, Error> { let Data::Enum(ref e) = &input.data else { - return Err(MacroError::Message( - "Cannot derive TryInto for union or struct.".to_string(), + return Err(Error::new( input.ident.span(), + "Cannot derive TryInto for union or struct.", )); }; if let Some(v) = e.variants.iter().find(|v| v.fields != Fields::Unit) { - return Err(MacroError::Message( - "Cannot derive TryInto for enum with non-unit variants.".to_string(), + return Err(Error::new( v.fields.span(), + "Cannot derive TryInto for enum with non-unit variants.", )); } Ok(&e.variants) @@ -197,7 +195,7 @@ fn derive_tryinto_body( name: &Ident, variants: &Punctuated, repr: &Path, -) -> Result { +) -> Result { let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect(); Ok(quote! { @@ -210,7 +208,7 @@ fn derive_tryinto_body( } #[rustfmt::skip::macros(quote)] -fn derive_tryinto_or_error(input: DeriveInput) -> Result { +fn derive_tryinto_or_error(input: DeriveInput) -> Result { let repr = get_repr_uN(&input, "#[derive(TryInto)]")?; let name = &input.ident; let body = derive_tryinto_body(name, get_variants(&input)?, &repr)?; @@ -247,9 +245,10 @@ fn derive_tryinto_or_error(input: DeriveInput) -> Result TokenStream { let input = parse_macro_input!(input as DeriveInput); - let expanded = derive_tryinto_or_error(input).unwrap_or_else(Into::into); - TokenStream::from(expanded) + derive_tryinto_or_error(input) + .unwrap_or_else(syn::Error::into_compile_error) + .into() } #[proc_macro] @@ -257,6 +256,7 @@ pub fn bits_const_internal(ts: TokenStream) -> TokenStream { let ts = proc_macro2::TokenStream::from(ts); let mut it = ts.into_iter(); - let expanded = BitsConstInternal::parse(&mut it).unwrap_or_else(Into::into); - TokenStream::from(expanded) + BitsConstInternal::parse(&mut it) + .unwrap_or_else(syn::Error::into_compile_error) + .into() } diff --git a/rust/qemu-api-macros/src/utils.rs b/rust/qemu-api-macros/src/utils.rs deleted file mode 100644 index 02c91aed7f..0000000000 --- a/rust/qemu-api-macros/src/utils.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Procedural macro utilities. -// Author(s): Paolo Bonzini -// SPDX-License-Identifier: GPL-2.0-or-later - -use proc_macro2::Span; -use quote::quote_spanned; - -pub enum MacroError { - Message(String, Span), - ParseError(syn::Error), -} - -impl From for MacroError { - fn from(err: syn::Error) -> Self { - MacroError::ParseError(err) - } -} - -impl From for proc_macro2::TokenStream { - fn from(err: MacroError) -> Self { - match err { - MacroError::Message(msg, span) => quote_spanned! { span => compile_error!(#msg); }, - MacroError::ParseError(err) => err.into_compile_error(), - } - } -} From dab63b8a710c2c489276d60d51e21e82b91a92f5 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 3 Jul 2025 17:20:22 +0300 Subject: [PATCH 2090/2760] rust/bindings: allow unnecessary_transmutes (1.88) This is a new lint introduced in Rust 1.88. It does not affect compilation when using a previous version or our MSRV, 1.77. But with 1.88 compilation fails because we deny all warnings: error: unnecessary transmute --> rust/qemu-api/libqemu_api.rlib.p/structured/bindings.inc.rs:729:18 | 729 | unsafe { ::std::mem::transmute(self._bitfield_1.get(0usize, 24u8) as u32) } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace this with: `u32::cast_signed(self._bitfield_1.get(0usize, 24u8) as u32)` | = note: `-D unnecessary-transmutes` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(unnecessary_transmutes)]` Allow this lint, which even though it does not exist in previous versions, it works because we allow for `unknown_lints` in rust/Cargo.toml. Signed-off-by: Manos Pitsidianakis Link: https://lore.kernel.org/r/20250703-rust_bindings_allow_unnecessary_transmutes-v1-1-692ca210d331@linaro.org Signed-off-by: Paolo Bonzini --- rust/qemu-api/src/bindings.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 057de4b646..3cdad0f0ec 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -6,6 +6,7 @@ non_camel_case_types, non_snake_case, non_upper_case_globals, + unnecessary_transmutes, unsafe_op_in_unsafe_fn, clippy::pedantic, clippy::restriction, From c3a08c8dcbe568d9e7f8a66d300a668bcb8673c0 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Fri, 4 Jul 2025 13:26:57 +0300 Subject: [PATCH 2091/2760] rust/qemu-api-macros: normalize TryInto output Remove extraneous `;` and add missing trailing comma to TryInto derive macro to match rustfmt style. We will add a test in the followup commit and we would like the inlined output in the test body to be properly formatted as well. No functional changes intended. Signed-off-by: Manos Pitsidianakis Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250704-rust_add_derive_macro_unit_tests-v1-1-ebd47fa7f78f@linaro.org Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 2cb79c799a..5bbf8c6127 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -199,7 +199,7 @@ fn derive_tryinto_body( let discriminants: Vec<&Ident> = variants.iter().map(|f| &f.ident).collect(); Ok(quote! { - #(const #discriminants: #repr = #name::#discriminants as #repr;)*; + #(const #discriminants: #repr = #name::#discriminants as #repr;)* match value { #(#discriminants => core::result::Result::Ok(#name::#discriminants),)* _ => core::result::Result::Err(value), @@ -227,7 +227,7 @@ fn derive_tryinto_or_error(input: DeriveInput) -> Result x, - Err(_) => panic!(#errmsg) + Err(_) => panic!(#errmsg), } } } From a721d9a9f3dd7bb9d6ed81ea1a11a1157755741c Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Fri, 4 Jul 2025 13:26:58 +0300 Subject: [PATCH 2092/2760] rust/qemu-api-macros: add unit tests Add unit tests to check Derive macro output for expected error messages, or for expected correct codegen output. Signed-off-by: Manos Pitsidianakis Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250704-rust_add_derive_macro_unit_tests-v1-2-ebd47fa7f78f@linaro.org [Remove usage of MacroError. - Paolo] Signed-off-by: Paolo Bonzini --- rust/qemu-api-macros/meson.build | 3 + rust/qemu-api-macros/src/lib.rs | 3 + rust/qemu-api-macros/src/tests.rs | 137 ++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 rust/qemu-api-macros/src/tests.rs diff --git a/rust/qemu-api-macros/meson.build b/rust/qemu-api-macros/meson.build index 8610ce1c84..2152bcb99b 100644 --- a/rust/qemu-api-macros/meson.build +++ b/rust/qemu-api-macros/meson.build @@ -17,3 +17,6 @@ _qemu_api_macros_rs = rust.proc_macro( qemu_api_macros = declare_dependency( link_with: _qemu_api_macros_rs, ) + +rust.test('rust-qemu-api-macros-tests', _qemu_api_macros_rs, + suite: ['unit', 'rust']) diff --git a/rust/qemu-api-macros/src/lib.rs b/rust/qemu-api-macros/src/lib.rs index 5bbf8c6127..b525d89c09 100644 --- a/rust/qemu-api-macros/src/lib.rs +++ b/rust/qemu-api-macros/src/lib.rs @@ -11,6 +11,9 @@ use syn::{ mod bits; use bits::BitsConstInternal; +#[cfg(test)] +mod tests; + fn get_fields<'a>( input: &'a DeriveInput, msg: &str, diff --git a/rust/qemu-api-macros/src/tests.rs b/rust/qemu-api-macros/src/tests.rs new file mode 100644 index 0000000000..d6dcd62fcf --- /dev/null +++ b/rust/qemu-api-macros/src/tests.rs @@ -0,0 +1,137 @@ +// Copyright 2025, Linaro Limited +// Author(s): Manos Pitsidianakis +// SPDX-License-Identifier: GPL-2.0-or-later + +use quote::quote; + +use super::*; + +macro_rules! derive_compile_fail { + ($derive_fn:ident, $input:expr, $error_msg:expr) => {{ + let input: proc_macro2::TokenStream = $input; + let error_msg: &str = $error_msg; + let derive_fn: fn(input: syn::DeriveInput) -> Result = + $derive_fn; + + let input: syn::DeriveInput = syn::parse2(input).unwrap(); + let result = derive_fn(input); + let err = result.unwrap_err().into_compile_error(); + assert_eq!( + err.to_string(), + quote! { ::core::compile_error! { #error_msg } }.to_string() + ); + }}; +} + +macro_rules! derive_compile { + ($derive_fn:ident, $input:expr, $($expected:tt)*) => {{ + let input: proc_macro2::TokenStream = $input; + let expected: proc_macro2::TokenStream = $($expected)*; + let derive_fn: fn(input: syn::DeriveInput) -> Result = + $derive_fn; + + let input: syn::DeriveInput = syn::parse2(input).unwrap(); + let result = derive_fn(input).unwrap(); + assert_eq!(result.to_string(), expected.to_string()); + }}; +} + +#[test] +fn test_derive_object() { + derive_compile_fail!( + derive_object_or_error, + quote! { + #[derive(Object)] + struct Foo { + _unused: [u8; 0], + } + }, + "#[repr(C)] required for #[derive(Object)]" + ); + derive_compile!( + derive_object_or_error, + quote! { + #[derive(Object)] + #[repr(C)] + struct Foo { + _unused: [u8; 0], + } + }, + quote! { + ::qemu_api::assert_field_type!( + Foo, + _unused, + ::qemu_api::qom::ParentField<::ParentType> + ); + ::qemu_api::module_init! { + MODULE_INIT_QOM => unsafe { + ::qemu_api::bindings::type_register_static(&::TYPE_INFO); + } + } + } + ); +} + +#[test] +fn test_derive_tryinto() { + derive_compile_fail!( + derive_tryinto_or_error, + quote! { + #[derive(TryInto)] + struct Foo { + _unused: [u8; 0], + } + }, + "#[repr(u8/u16/u32/u64) required for #[derive(TryInto)]" + ); + derive_compile!( + derive_tryinto_or_error, + quote! { + #[derive(TryInto)] + #[repr(u8)] + enum Foo { + First = 0, + Second, + } + }, + quote! { + impl Foo { + #[allow(dead_code)] + pub const fn into_bits(self) -> u8 { + self as u8 + } + + #[allow(dead_code)] + pub const fn from_bits(value: u8) -> Self { + match ({ + const First: u8 = Foo::First as u8; + const Second: u8 = Foo::Second as u8; + match value { + First => core::result::Result::Ok(Foo::First), + Second => core::result::Result::Ok(Foo::Second), + _ => core::result::Result::Err(value), + } + }) { + Ok(x) => x, + Err(_) => panic!("invalid value for Foo"), + } + } + } + + impl core::convert::TryFrom for Foo { + type Error = u8; + + #[allow(ambiguous_associated_items)] + fn try_from(value: u8) -> Result { + const First: u8 = Foo::First as u8; + const Second: u8 = Foo::Second as u8; + match value { + First => core::result::Result::Ok(Foo::First), + Second => core::result::Result::Ok(Foo::Second), + _ => core::result::Result::Err(value), + } + } + } + } + ); +} From ed7f6da282e263403badb507b2532f51f8ed31cf Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 11 Jun 2025 10:16:10 +0200 Subject: [PATCH 2093/2760] rust/qemu-api: log: implement io::Write This makes it possible to lock the log file; it also makes log_mask_ln! not allocate memory when logging a constant string. Reviewed-by: Zhao Liu Reviewed-by: Manos Pitsidianakis Signed-off-by: Paolo Bonzini --- include/qemu/log.h | 2 + rust/qemu-api/src/log.rs | 92 ++++++++++++++++++++++++++++++++++++---- util/log.c | 12 ++++++ 3 files changed, 98 insertions(+), 8 deletions(-) diff --git a/include/qemu/log.h b/include/qemu/log.h index 60da703e67..aae72985f0 100644 --- a/include/qemu/log.h +++ b/include/qemu/log.h @@ -84,6 +84,8 @@ typedef struct QEMULogItem { extern const QEMULogItem qemu_log_items[]; +ssize_t rust_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); + bool qemu_set_log(int log_flags, Error **errp); bool qemu_set_log_filename(const char *filename, Error **errp); bool qemu_set_log_filename_flags(const char *name, int flags, Error **errp); diff --git a/rust/qemu-api/src/log.rs b/rust/qemu-api/src/log.rs index d6c3d6c1b6..a441b8c1f2 100644 --- a/rust/qemu-api/src/log.rs +++ b/rust/qemu-api/src/log.rs @@ -3,6 +3,13 @@ //! Bindings for QEMU's logging infrastructure +use std::{ + io::{self, Write}, + ptr::NonNull, +}; + +use crate::{bindings, errno}; + #[repr(u32)] /// Represents specific error categories within QEMU's logging system. /// @@ -11,11 +18,82 @@ pub enum Log { /// Log invalid access caused by the guest. /// Corresponds to `LOG_GUEST_ERROR` in the C implementation. - GuestError = crate::bindings::LOG_GUEST_ERROR, + GuestError = bindings::LOG_GUEST_ERROR, /// Log guest access of unimplemented functionality. /// Corresponds to `LOG_UNIMP` in the C implementation. - Unimp = crate::bindings::LOG_UNIMP, + Unimp = bindings::LOG_UNIMP, +} + +/// A RAII guard for QEMU's logging infrastructure. Creating the guard +/// locks the log file, and dropping it (letting it go out of scope) unlocks +/// the file. +/// +/// As long as the guard lives, it can be written to using [`std::io::Write`]. +/// +/// The locking is recursive, therefore owning a guard does not prevent +/// using [`log_mask_ln!()`](crate::log_mask_ln). +pub struct LogGuard(NonNull); + +impl LogGuard { + /// Return a RAII guard that writes to QEMU's logging infrastructure. + /// The log file is locked while the guard exists, ensuring that there + /// is no tearing of the messages. + /// + /// Return `None` if the log file is closed and could not be opened. + /// Do *not* use `unwrap()` on the result; failure can be handled simply + /// by not logging anything. + /// + /// # Examples + /// + /// ``` + /// # use qemu_api::log::LogGuard; + /// # use std::io::Write; + /// if let Some(mut log) = LogGuard::new() { + /// writeln!(log, "test"); + /// } + /// ``` + pub fn new() -> Option { + let f = unsafe { bindings::qemu_log_trylock() }.cast(); + NonNull::new(f).map(Self) + } + + /// Writes a formatted string into the log, returning any error encountered. + /// + /// This method is primarily used by the + /// [`log_mask_ln!()`](crate::log_mask_ln) macro, and it is rare for it + /// to be called explicitly. It is public because it is the only way to + /// examine the error, which `log_mask_ln!()` ignores + /// + /// Unlike `log_mask_ln!()`, it does *not* append a newline at the end. + pub fn log_fmt(args: std::fmt::Arguments) -> io::Result<()> { + if let Some(mut log) = Self::new() { + log.write_fmt(args)?; + } + Ok(()) + } +} + +impl Write for LogGuard { + fn write(&mut self, bytes: &[u8]) -> io::Result { + let ret = unsafe { + bindings::rust_fwrite(bytes.as_ptr().cast(), 1, bytes.len(), self.0.as_ptr()) + }; + errno::into_io_result(ret) + } + + fn flush(&mut self) -> io::Result<()> { + // Do nothing, dropping the guard takes care of flushing + Ok(()) + } +} + +impl Drop for LogGuard { + fn drop(&mut self) { + unsafe { + bindings::qemu_log_unlock(self.0.as_ptr()); + } + } } /// A macro to log messages conditionally based on a provided mask. @@ -24,6 +102,8 @@ pub enum Log { /// log level and, if so, formats and logs the message. It is the Rust /// counterpart of the `qemu_log_mask()` macro in the C implementation. /// +/// Errors from writing to the log are ignored. +/// /// # Parameters /// /// - `$mask`: A log level mask. This should be a variant of the `Log` enum. @@ -62,12 +142,8 @@ macro_rules! log_mask_ln { if unsafe { (::qemu_api::bindings::qemu_loglevel & ($mask as std::os::raw::c_int)) != 0 } { - let formatted_string = format!("{}\n", format_args!($fmt $($args)*)); - let c_string = std::ffi::CString::new(formatted_string).unwrap(); - - unsafe { - ::qemu_api::bindings::qemu_log(c_string.as_ptr()); - } + _ = ::qemu_api::log::LogGuard::log_fmt( + format_args!("{}\n", format_args!($fmt $($args)*))); } }}; } diff --git a/util/log.c b/util/log.c index b87d399e4c..58d24de48a 100644 --- a/util/log.c +++ b/util/log.c @@ -558,3 +558,15 @@ void qemu_print_log_usage(FILE *f) fprintf(f, "\nUse \"-d trace:help\" to get a list of trace events.\n\n"); #endif } + +#ifdef CONFIG_HAVE_RUST +ssize_t rust_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) +{ + /* + * Same as fwrite, but return -errno because Rust libc does not provide + * portable access to errno. :( + */ + int ret = fwrite(ptr, size, nmemb, stream); + return ret < 0 ? -errno : 0; +} +#endif From abbeb42c1762a529cfbae52845c279b648a7acb4 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Tue, 6 May 2025 19:25:54 +0000 Subject: [PATCH 2094/2760] fpu: Process float_muladd_negate_result after rounding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changing the sign before rounding affects the correctness of the asymmetric rouding modes: float_round_up and float_round_down. Reported-by: WANG Rui Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- fpu/softfloat-parts.c.inc | 4 --- fpu/softfloat.c | 54 +++++++++++++++++++++++------ tests/tcg/multiarch/Makefile.target | 1 + tests/tcg/multiarch/fnmsub.c | 37 ++++++++++++++++++++ 4 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 tests/tcg/multiarch/fnmsub.c diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index 171bfd06e3..5e0438fc0b 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -708,10 +708,6 @@ static FloatPartsN *partsN(muladd_scalbn)(FloatPartsN *a, FloatPartsN *b, return_normal: a->exp += scale; finish_sign: - if (flags & float_muladd_negate_result) { - a->sign ^= 1; - } - /* * All result types except for "return the default NaN * because this is an Invalid Operation" go through here; diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 34c962d6bd..8094358c2e 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -1731,11 +1731,8 @@ static float64 float64_round_pack_canonical(FloatParts64 *p, return float64_pack_raw(p); } -static float64 float64r32_round_pack_canonical(FloatParts64 *p, - float_status *s) +static float64 float64r32_pack_raw(FloatParts64 *p) { - parts_uncanon(p, s, &float32_params); - /* * In parts_uncanon, we placed the fraction for float32 at the lsb. * We need to adjust the fraction higher so that the least N bits are @@ -1776,6 +1773,13 @@ static float64 float64r32_round_pack_canonical(FloatParts64 *p, return float64_pack_raw(p); } +static float64 float64r32_round_pack_canonical(FloatParts64 *p, + float_status *s) +{ + parts_uncanon(p, s, &float32_params); + return float64r32_pack_raw(p); +} + static void float128_unpack_canonical(FloatParts128 *p, float128 f, float_status *s) { @@ -2240,7 +2244,12 @@ float16_muladd_scalbn(float16 a, float16 b, float16 c, float16_unpack_canonical(&pc, c, status); pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status); - return float16_round_pack_canonical(pr, status); + /* Round before applying negate result. */ + parts_uncanon(pr, status, &float16_params); + if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) { + pr->sign ^= 1; + } + return float16_pack_raw(pr); } float16 float16_muladd(float16 a, float16 b, float16 c, @@ -2260,7 +2269,12 @@ float32_muladd_scalbn(float32 a, float32 b, float32 c, float32_unpack_canonical(&pc, c, status); pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status); - return float32_round_pack_canonical(pr, status); + /* Round before applying negate result. */ + parts_uncanon(pr, status, &float32_params); + if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) { + pr->sign ^= 1; + } + return float32_pack_raw(pr); } float64 QEMU_SOFTFLOAT_ATTR @@ -2274,7 +2288,12 @@ float64_muladd_scalbn(float64 a, float64 b, float64 c, float64_unpack_canonical(&pc, c, status); pr = parts_muladd_scalbn(&pa, &pb, &pc, scale, flags, status); - return float64_round_pack_canonical(pr, status); + /* Round before applying negate result. */ + parts_uncanon(pr, status, &float64_params); + if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) { + pr->sign ^= 1; + } + return float64_pack_raw(pr); } static bool force_soft_fma; @@ -2428,7 +2447,12 @@ float64 float64r32_muladd(float64 a, float64 b, float64 c, float64_unpack_canonical(&pc, c, status); pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status); - return float64r32_round_pack_canonical(pr, status); + /* Round before applying negate result. */ + parts_uncanon(pr, status, &float32_params); + if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) { + pr->sign ^= 1; + } + return float64r32_pack_raw(pr); } bfloat16 QEMU_FLATTEN bfloat16_muladd(bfloat16 a, bfloat16 b, bfloat16 c, @@ -2441,7 +2465,12 @@ bfloat16 QEMU_FLATTEN bfloat16_muladd(bfloat16 a, bfloat16 b, bfloat16 c, bfloat16_unpack_canonical(&pc, c, status); pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status); - return bfloat16_round_pack_canonical(pr, status); + /* Round before applying negate result. */ + parts_uncanon(pr, status, &bfloat16_params); + if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) { + pr->sign ^= 1; + } + return bfloat16_pack_raw(pr); } float128 QEMU_FLATTEN float128_muladd(float128 a, float128 b, float128 c, @@ -2454,7 +2483,12 @@ float128 QEMU_FLATTEN float128_muladd(float128 a, float128 b, float128 c, float128_unpack_canonical(&pc, c, status); pr = parts_muladd_scalbn(&pa, &pb, &pc, 0, flags, status); - return float128_round_pack_canonical(pr, status); + /* Round before applying negate result. */ + parts_uncanon(pr, status, &float128_params); + if ((flags & float_muladd_negate_result) && !is_nan(pr->cls)) { + pr->sign ^= 1; + } + return float128_pack_raw(pr); } /* diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 45c9cfe18c..bfdf7197a7 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -29,6 +29,7 @@ run-float_%: float_% $(call run-test,$<, $(QEMU) $(QEMU_OPTS) $<) $(call conditional-diff-out,$<,$(SRC_PATH)/tests/tcg/$(TARGET_NAME)/$<.ref) +fnmsub: LDFLAGS+=-lm testthread: LDFLAGS+=-lpthread diff --git a/tests/tcg/multiarch/fnmsub.c b/tests/tcg/multiarch/fnmsub.c new file mode 100644 index 0000000000..15dd41d3bd --- /dev/null +++ b/tests/tcg/multiarch/fnmsub.c @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include +#include + +union U { + double d; + unsigned long long l; +}; + +union U x = { .l = 0x4ff0000000000000ULL }; +union U y = { .l = 0x2ff0000000000000ULL }; +union U r; + +int main() +{ +#ifdef FE_DOWNWARD + fesetround(FE_DOWNWARD); + +#if defined(__loongarch__) + asm("fnmsub.d %0, %1, %1, %2" : "=f"(r.d) : "f"(x.d), "f"(y.d)); +#elif defined(__powerpc64__) + asm("fnmsub %0,%1,%1,%2" : "=f"(r.d) : "f"(x.d), "f"(y.d)); +#elif defined(__s390x__) && 0 /* need -march=z14 */ + asm("vfnms %0,%1,%1,%2,0,3" : "=f"(r.d) : "f"(x.d), "f"(y.d)); +#else + r.d = -fma(x.d, x.d, -y.d); +#endif + + if (r.l != 0xdfefffffffffffffULL) { + printf("r = %.18a (%016llx)\n", r.d, r.l); + return 1; + } +#endif + return 0; +} From 6a3e132a1be8c9e649967a4eb341d00731be7f51 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 Jul 2025 12:31:23 +0100 Subject: [PATCH 2095/2760] linux-user: Implement fchmodat2 syscall MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The fchmodat2 syscall is new from Linux 6.6; it is like the existing fchmodat syscall except that it takes a flags parameter. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3019 Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250710113123.1109461-1-peter.maydell@linaro.org> --- linux-user/syscall.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index fc37028597..e1b1476936 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -790,6 +790,10 @@ safe_syscall6(ssize_t, copy_file_range, int, infd, loff_t *, pinoff, int, outfd, loff_t *, poutoff, size_t, length, unsigned int, flags) #endif +#if defined(TARGET_NR_fchmodat2) && defined(__NR_fchmodat2) +safe_syscall4(int, fchmodat2, int, dfd, const char *, filename, + unsigned short, mode, unsigned int, flags) +#endif /* We do ioctl like this rather than via safe_syscall3 to preserve the * "third argument might be integer or pointer or not present" behaviour of @@ -10713,6 +10717,15 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, ret = get_errno(fchmodat(arg1, p, arg3, 0)); unlock_user(p, arg2, 0); return ret; +#endif +#if defined(TARGET_NR_fchmodat2) && defined(__NR_fchmodat2) + case TARGET_NR_fchmodat2: + if (!(p = lock_user_string(arg2))) { + return -TARGET_EFAULT; + } + ret = get_errno(safe_fchmodat2(arg1, p, arg3, arg4)); + unlock_user(p, arg2, 0); + return ret; #endif case TARGET_NR_getpriority: /* Note that negative values are valid for getpriority, so we must From c4828cb8502d0b2adc39b9cde93df7d2886df897 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 Jul 2025 17:43:54 +0100 Subject: [PATCH 2096/2760] linux-user: Check for EFAULT failure in nanosleep target_to_host_timespec() returns an error if the memory the guest passed us isn't actually readable. We check for this everywhere except the callsite in the TARGET_NR_nanosleep case, so this mistake was caught by a Coverity heuristic. Add the missing error checks to the calls that convert between the host and target timespec structs. Coverity: CID 1507104 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250710164355.1296648-1-peter.maydell@linaro.org> --- linux-user/syscall.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index e1b1476936..38dd563166 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -11643,10 +11643,14 @@ static abi_long do_syscall1(CPUArchState *cpu_env, int num, abi_long arg1, case TARGET_NR_nanosleep: { struct timespec req, rem; - target_to_host_timespec(&req, arg1); + if (target_to_host_timespec(&req, arg1)) { + return -TARGET_EFAULT; + } ret = get_errno(safe_nanosleep(&req, &rem)); if (is_error(ret) && arg2) { - host_to_target_timespec(arg2, &rem); + if (host_to_target_timespec(arg2, &rem)) { + return -TARGET_EFAULT; + } } } return ret; From ff3b0e8d326155e45574dd14de5db6702a32d06e Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 Jul 2025 18:07:06 +0100 Subject: [PATCH 2097/2760] linux-user/gen-vdso: Handle fseek() failure Coverity points out that we don't check for fseek() failure in gen-vdso.c, and so we might pass -1 to malloc(). Add the error checking. (This is a standalone executable that doesn't link against glib, so we can't do the easy thing and use g_file_get_contents().) Coverity: CID 1523742 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250710170707.1299926-2-peter.maydell@linaro.org> --- linux-user/gen-vdso.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/linux-user/gen-vdso.c b/linux-user/gen-vdso.c index fce9d5cbc3..1c406d1b10 100644 --- a/linux-user/gen-vdso.c +++ b/linux-user/gen-vdso.c @@ -113,9 +113,16 @@ int main(int argc, char **argv) * We expect the vdso to be small, on the order of one page, * therefore we do not expect a partial read. */ - fseek(inf, 0, SEEK_END); + if (fseek(inf, 0, SEEK_END) < 0) { + goto perror_inf; + } total_len = ftell(inf); - fseek(inf, 0, SEEK_SET); + if (total_len < 0) { + goto perror_inf; + } + if (fseek(inf, 0, SEEK_SET) < 0) { + goto perror_inf; + } buf = malloc(total_len); if (buf == NULL) { From cb8607b89ffbba905eac3af595d1db974d2ffc5d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 Jul 2025 18:07:07 +0100 Subject: [PATCH 2098/2760] linux-user/gen-vdso: Don't read off the end of buf[] In gen-vdso we load in a file and assume it's a valid ELF file. In particular we assume it's big enough to be able to read the ELF information in e_ident in the ELF header. Add a check that the total file length is at least big enough for all the e_ident bytes, which is good enough for the code in gen-vdso.c. This will catch the most obvious possible bad input file (truncated) and allow us to run the sanity checks like "not actually an ELF file" without potentially crashing. The code in elf32_process() and elf64_process() still makes assumptions about the file being well-formed, but this is OK because we only run it on the vdso binaries that we create ourselves in the build process by running the compiler. Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250710170707.1299926-3-peter.maydell@linaro.org> --- linux-user/gen-vdso.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/linux-user/gen-vdso.c b/linux-user/gen-vdso.c index 1c406d1b10..aeaa927db8 100644 --- a/linux-user/gen-vdso.c +++ b/linux-user/gen-vdso.c @@ -124,6 +124,11 @@ int main(int argc, char **argv) goto perror_inf; } + if (total_len < EI_NIDENT) { + fprintf(stderr, "%s: file too small (truncated?)\n", inf_name); + return EXIT_FAILURE; + } + buf = malloc(total_len); if (buf == NULL) { goto perror_inf; From 91748d50c7ef4addcc9302160a4b8b3c63d5d024 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Wed, 9 Jul 2025 22:57:16 +0200 Subject: [PATCH 2099/2760] linux-user/mips/o32: Drop sa_restorer functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Linux kernel dropped support for sa_restorer on O32 MIPS in the release 2.5.48 because it was unused. See the comment in arch/mips/include/uapi/asm/signal.h. Applications using the kernels UAPI headers will not reserve enough space for qemu-user to copy the sigaction.sa_restorer field to. Unrelated data may be overwritten. Align qemu-user with the kernel by also dropping sa_restorer support. Signed-off-by: Thomas Weißschuh Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson Message-ID: <20250709-mips-sa-restorer-v1-1-fc17120e4afe@t-8ch.de> --- linux-user/mips/target_signal.h | 1 - linux-user/syscall_defs.h | 4 ---- 2 files changed, 5 deletions(-) diff --git a/linux-user/mips/target_signal.h b/linux-user/mips/target_signal.h index fa542c1f4e..4481426b99 100644 --- a/linux-user/mips/target_signal.h +++ b/linux-user/mips/target_signal.h @@ -64,7 +64,6 @@ typedef struct target_sigaltstack { #define TARGET_SA_NODEFER 0x40000000 #define TARGET_SA_RESTART 0x10000000 #define TARGET_SA_RESETHAND 0x80000000 -#define TARGET_SA_RESTORER 0x04000000 /* Only for O32 */ #define TARGET_MINSIGSTKSZ 2048 diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h index 5d22759992..df26a2d28f 100644 --- a/linux-user/syscall_defs.h +++ b/linux-user/syscall_defs.h @@ -515,10 +515,6 @@ struct target_sigaction { abi_ulong _sa_handler; #endif target_sigset_t sa_mask; -#ifdef TARGET_ARCH_HAS_SA_RESTORER - /* ??? This is always present, but ignored unless O32. */ - abi_ulong sa_restorer; -#endif }; #else struct target_old_sigaction { From e4e839b2eeea5745c48ce47144c7842eb7cd455f Mon Sep 17 00:00:00 2001 From: Geoffrey Thomas Date: Fri, 14 Mar 2025 08:47:42 -0400 Subject: [PATCH 2100/2760] linux-user: Hold the fd-trans lock across fork If another thread is holding target_fd_trans_lock during a fork, then the lock becomes permanently locked in the child and the emulator deadlocks at the next interaction with the fd-trans table. As with other locks, acquire the lock in fork_start() and release it in fork_end(). Cc: qemu-stable@nongnu.org Signed-off-by: Geoffrey Thomas Fixes: c093364f4d91 "fd-trans: Fix race condition on reallocation of the translation table." Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2846 Buglink: https://github.com/astral-sh/uv/issues/6105 Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250314124742.4965-1-geofft@ldpreload.com> --- linux-user/fd-trans.h | 10 ++++++++++ linux-user/main.c | 2 ++ 2 files changed, 12 insertions(+) diff --git a/linux-user/fd-trans.h b/linux-user/fd-trans.h index 910faaf237..e14f96059c 100644 --- a/linux-user/fd-trans.h +++ b/linux-user/fd-trans.h @@ -36,6 +36,16 @@ static inline void fd_trans_init(void) qemu_mutex_init(&target_fd_trans_lock); } +static inline void fd_trans_prefork(void) +{ + qemu_mutex_lock(&target_fd_trans_lock); +} + +static inline void fd_trans_postfork(void) +{ + qemu_mutex_unlock(&target_fd_trans_lock); +} + static inline TargetFdDataFunc fd_trans_target_to_host_data(int fd) { if (fd < 0) { diff --git a/linux-user/main.c b/linux-user/main.c index a9142ee726..f4f2007439 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -149,12 +149,14 @@ void fork_start(void) cpu_list_lock(); qemu_plugin_user_prefork_lock(); gdbserver_fork_start(); + fd_trans_prefork(); } void fork_end(pid_t pid) { bool child = pid == 0; + fd_trans_postfork(); qemu_plugin_user_postfork(child); mmap_fork_end(child); if (child) { From 90cff30d72d4f63fbfa637140b9e06e9894220c2 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 10 Jul 2025 16:21:31 +0800 Subject: [PATCH 2101/2760] hw/intc/loongarch_extioi: Move unrealize function to common code Memory about LoongArchExtIOICommonState::cpu is allocated in common code, it had better be freed in common code also. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- hw/intc/loongarch_extioi.c | 9 --------- hw/intc/loongarch_extioi_common.c | 9 +++++++++ include/hw/intc/loongarch_extioi.h | 1 - include/hw/intc/loongarch_extioi_common.h | 1 + 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hw/intc/loongarch_extioi.c b/hw/intc/loongarch_extioi.c index 8b8ac6b187..3e9c88d1d9 100644 --- a/hw/intc/loongarch_extioi.c +++ b/hw/intc/loongarch_extioi.c @@ -377,13 +377,6 @@ static void loongarch_extioi_realize(DeviceState *dev, Error **errp) } } -static void loongarch_extioi_unrealize(DeviceState *dev) -{ - LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(dev); - - g_free(s->cpu); -} - static void loongarch_extioi_reset_hold(Object *obj, ResetType type) { LoongArchExtIOIClass *lec = LOONGARCH_EXTIOI_GET_CLASS(obj); @@ -436,8 +429,6 @@ static void loongarch_extioi_class_init(ObjectClass *klass, const void *data) device_class_set_parent_realize(dc, loongarch_extioi_realize, &lec->parent_realize); - device_class_set_parent_unrealize(dc, loongarch_extioi_unrealize, - &lec->parent_unrealize); resettable_class_set_parent_phases(rc, NULL, loongarch_extioi_reset_hold, NULL, &lec->parent_phases); lecc->pre_save = vmstate_extioi_pre_save; diff --git a/hw/intc/loongarch_extioi_common.c b/hw/intc/loongarch_extioi_common.c index 4a904b3bc1..ba03383ed1 100644 --- a/hw/intc/loongarch_extioi_common.c +++ b/hw/intc/loongarch_extioi_common.c @@ -108,6 +108,13 @@ static void loongarch_extioi_common_realize(DeviceState *dev, Error **errp) } } +static void loongarch_extioi_common_unrealize(DeviceState *dev) +{ + LoongArchExtIOICommonState *s = LOONGARCH_EXTIOI_COMMON(dev); + + g_free(s->cpu); +} + static void loongarch_extioi_common_reset_hold(Object *obj, ResetType type) { LoongArchExtIOICommonClass *lecc = LOONGARCH_EXTIOI_COMMON_GET_CLASS(obj); @@ -221,6 +228,8 @@ static void loongarch_extioi_common_class_init(ObjectClass *klass, device_class_set_parent_realize(dc, loongarch_extioi_common_realize, &lecc->parent_realize); + device_class_set_parent_unrealize(dc, loongarch_extioi_common_unrealize, + &lecc->parent_unrealize); resettable_class_set_parent_phases(rc, NULL, loongarch_extioi_common_reset_hold, NULL, &lecc->parent_phases); diff --git a/include/hw/intc/loongarch_extioi.h b/include/hw/intc/loongarch_extioi.h index 9be1d736ea..4795bdc15f 100644 --- a/include/hw/intc/loongarch_extioi.h +++ b/include/hw/intc/loongarch_extioi.h @@ -22,7 +22,6 @@ struct LoongArchExtIOIClass { LoongArchExtIOICommonClass parent_class; DeviceRealize parent_realize; - DeviceUnrealize parent_unrealize; ResettablePhases parent_phases; }; diff --git a/include/hw/intc/loongarch_extioi_common.h b/include/hw/intc/loongarch_extioi_common.h index dca25ff893..c021ccee0f 100644 --- a/include/hw/intc/loongarch_extioi_common.h +++ b/include/hw/intc/loongarch_extioi_common.h @@ -94,6 +94,7 @@ struct LoongArchExtIOICommonClass { SysBusDeviceClass parent_class; DeviceRealize parent_realize; + DeviceUnrealize parent_unrealize; ResettablePhases parent_phases; int (*pre_save)(void *s); int (*post_load)(void *s, int version_id); From e5de64ae0233a13f5a623a62aec0b95d66ab7ce6 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 18 Jun 2025 10:30:55 +0800 Subject: [PATCH 2102/2760] target/loongarch: Correct spelling in helper_csrwr_pwcl() There is small typo issue in function helper_csrwr_pwcl(), this patch corrects this issue. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- target/loongarch/tcg/csr_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 2942d7feb8..46d331ce8a 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -131,8 +131,8 @@ target_ulong helper_csrwr_pwcl(CPULoongArchState *env, target_ulong val) } if (!check_ps(env, ptbase)) { qemu_log_mask(LOG_GUEST_ERROR, - "Attrmpted set ptbase 2^%d\n", ptbase); + "Attempted set ptbase 2^%d\n", ptbase); } - env->CSR_PWCL =val; + env->CSR_PWCL = val; return old_v; } From 5a2e76fc8786760a8fbb42af5cd8a61ecb6aba87 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 18 Jun 2025 10:51:34 +0800 Subject: [PATCH 2103/2760] target/loongarch: Fix CSR STLBPS register write emulation Function helper_csrwr_stlbps() is emulation with CSR STLBPS register write operation. However there is only parameter checking action, and no register updating action. Here update value of CSR_STLBPS when parameter passes to check. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- target/loongarch/tcg/csr_helper.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/loongarch/tcg/csr_helper.c b/target/loongarch/tcg/csr_helper.c index 46d331ce8a..28b1bb86bd 100644 --- a/target/loongarch/tcg/csr_helper.c +++ b/target/loongarch/tcg/csr_helper.c @@ -29,7 +29,11 @@ target_ulong helper_csrwr_stlbps(CPULoongArchState *env, target_ulong val) if (!check_ps(env, tlb_ps)) { qemu_log_mask(LOG_GUEST_ERROR, "Attempted set ps %d\n", tlb_ps); + } else { + /* Only update PS field, reserved bit keeps zero */ + env->CSR_STLBPS = FIELD_DP64(old_v, CSR_STLBPS, PS, tlb_ps); } + return old_v; } From 94c874f0f2bdc048bacf9873d8d9ee9a68d44ea5 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Wed, 18 Jun 2025 11:53:28 +0800 Subject: [PATCH 2104/2760] target/loongarch: Remove unnecessary page size validity checking Page size of TLB entry comes from CSR STLBPS and pwcl register. With huge page, it is dir_base + dir_width from pwcl register. With normal page, it is field of PTBASE from pwcl register. So it is ok to check validity in function helper_ldpte() and function helper_csrwr_stlbps(). And it is unnecessary in tlb entry fill path. Signed-off-by: Bibo Mao Reviewed-by: Song Gao --- target/loongarch/tcg/tlb_helper.c | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/target/loongarch/tcg/tlb_helper.c b/target/loongarch/tcg/tlb_helper.c index dc48b0f4d2..8872593ff0 100644 --- a/target/loongarch/tcg/tlb_helper.c +++ b/target/loongarch/tcg/tlb_helper.c @@ -173,12 +173,6 @@ static void fill_tlb_entry(CPULoongArchState *env, int index) lo1 = env->CSR_TLBELO1; } - /*check csr_ps */ - if (!check_ps(env, csr_ps)) { - qemu_log_mask(LOG_GUEST_ERROR, "csr_ps %d is illegal\n", csr_ps); - return; - } - /* Only MTLB has the ps fields */ if (index >= LOONGARCH_STLB) { tlb->tlb_misc = FIELD_DP64(tlb->tlb_misc, TLB_MISC, PS, csr_ps); @@ -340,23 +334,16 @@ void helper_tlbfill(CPULoongArchState *env) if (FIELD_EX64(env->CSR_TLBRERA, CSR_TLBRERA, ISTLBR)) { entryhi = env->CSR_TLBREHI; + /* Validity of pagesize is checked in helper_ldpte() */ pagesize = FIELD_EX64(env->CSR_TLBREHI, CSR_TLBREHI, PS); } else { entryhi = env->CSR_TLBEHI; + /* Validity of pagesize is checked in helper_tlbrd() */ pagesize = FIELD_EX64(env->CSR_TLBIDX, CSR_TLBIDX, PS); } - if (!check_ps(env, pagesize)) { - qemu_log_mask(LOG_GUEST_ERROR, "pagesize %d is illegal\n", pagesize); - return; - } - + /* Validity of stlb_ps is checked in helper_csrwr_stlbps() */ stlb_ps = FIELD_EX64(env->CSR_STLBPS, CSR_STLBPS, PS); - if (!check_ps(env, stlb_ps)) { - qemu_log_mask(LOG_GUEST_ERROR, "stlb_ps %d is illegal\n", stlb_ps); - return; - } - if (pagesize == stlb_ps) { /* Only write into STLB bits [47:13] */ address = entryhi & ~MAKE_64BIT_MASK(0, R_CSR_TLBEHI_64_VPPN_SHIFT); @@ -611,10 +598,11 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, uint32_t mem_idx) { CPUState *cs = env_cpu(env); - target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, ps, badv; + target_ulong phys, tmp0, ptindex, ptoffset0, ptoffset1, badv; uint64_t ptbase = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTBASE); uint64_t ptwidth = FIELD_EX64(env->CSR_PWCL, CSR_PWCL, PTWIDTH); uint64_t dir_base, dir_width; + uint8_t ps; /* * The parameter "base" has only two types, @@ -651,6 +639,11 @@ void helper_ldpte(CPULoongArchState *env, target_ulong base, target_ulong odd, if (odd) { tmp0 += MAKE_64BIT_MASK(ps, 1); } + + if (!check_ps(env, ps)) { + qemu_log_mask(LOG_GUEST_ERROR, "Illegal huge pagesize %d\n", ps); + return; + } } else { badv = env->CSR_TLBRBADV; From cb2273edf5df57cb270bc19d7e92d8be5870c17a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 5 Jul 2025 14:05:42 +0200 Subject: [PATCH 2105/2760] target/i386: move max_features to class max_features is always set to true for instances created by -cpu max or -cpu host; it's always false for other classes. Therefore it can be turned into a field in the X86CPUClass. Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 7 ++++--- target/i386/cpu.h | 2 +- target/i386/hvf/hvf-cpu.c | 3 ++- target/i386/kvm/kvm-cpu.c | 5 +++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 0d35e95430..4d4e523424 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6187,6 +6187,7 @@ static void max_x86_cpu_class_init(ObjectClass *oc, const void *data) xcc->ordering = 9; + xcc->max_features = true; xcc->model_description = "Enables all features supported by the accelerator in the current host"; @@ -6201,7 +6202,6 @@ static void max_x86_cpu_initfn(Object *obj) /* We can't fill the features array here because we don't know yet if * "migratable" is true or false. */ - cpu->max_features = true; object_property_set_bool(OBJECT(cpu), "pmu", true, &error_abort); /* @@ -8282,6 +8282,7 @@ static void x86_cpu_enable_xsave_components(X86CPU *cpu) */ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) { + X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); CPUX86State *env = &cpu->env; FeatureWord w; int i; @@ -8301,12 +8302,12 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) } } - /*TODO: Now cpu->max_features doesn't overwrite features + /* TODO: Now xcc->max_features doesn't overwrite features * set using QOM properties, and we can convert * plus_features & minus_features to global properties * inside x86_cpu_parse_featurestr() too. */ - if (cpu->max_features) { + if (xcc->max_features) { for (w = 0; w < FEATURE_WORDS; w++) { /* Override only features that weren't set explicitly * by the user. diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 51e10139df..be3ae6d546 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2196,7 +2196,6 @@ struct ArchCPU { bool expose_tcg; bool migratable; bool migrate_smi_count; - bool max_features; /* Enable all supported features automatically */ uint32_t apic_id; /* Enables publishing of TSC increment and Local APIC bus frequencies to @@ -2349,6 +2348,7 @@ struct X86CPUClass { */ const X86CPUModel *model; + bool max_features; /* Enable all supported features automatically */ bool host_cpuid_required; int ordering; bool migration_safe; diff --git a/target/i386/hvf/hvf-cpu.c b/target/i386/hvf/hvf-cpu.c index dfdda70126..2b991f2fc8 100644 --- a/target/i386/hvf/hvf-cpu.c +++ b/target/i386/hvf/hvf-cpu.c @@ -61,13 +61,14 @@ static void hvf_cpu_xsave_init(void) static void hvf_cpu_instance_init(CPUState *cs) { X86CPU *cpu = X86_CPU(cs); + X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); host_cpu_instance_init(cpu); /* Special cases not set in the X86CPUDefinition structs: */ /* TODO: in-kernel irqchip for hvf */ - if (cpu->max_features) { + if (xcc->max_features) { hvf_cpu_max_instance_init(cpu); } diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index 16bde4de01..0fb88a239d 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -41,6 +41,7 @@ static void kvm_set_guest_phys_bits(CPUState *cs) static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) { X86CPU *cpu = X86_CPU(cs); + X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); CPUX86State *env = &cpu->env; bool ret; @@ -63,7 +64,7 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) * check/update ucode_rev, phys_bits, guest_phys_bits, mwait * cpu_common_realizefn() (via xcc->parent_realize) */ - if (cpu->max_features) { + if (xcc->max_features) { if (enable_cpu_pm) { if (kvm_has_waitpkg()) { env->features[FEAT_7_0_ECX] |= CPUID_7_0_ECX_WAITPKG; @@ -216,7 +217,7 @@ static void kvm_cpu_instance_init(CPUState *cs) x86_cpu_apply_props(cpu, kvm_default_props); } - if (cpu->max_features) { + if (xcc->max_features) { kvm_cpu_max_instance_init(cpu); } From 0492c8929f4cb58372273b956e2f8b3d93bd7e33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 7 Jul 2025 19:10:58 +0200 Subject: [PATCH 2106/2760] target/s390x/kvm: Use vaddr in find/insert_hw_breakpoint() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit b8a6eb1862a both kvm_arch_insert_hw_breakpoint() and kvm_arch_remove_hw_breakpoint() use a vaddr type. Use the same type for the callees. Fixes: b8a6eb1862a ("sysemu/kvm: Use vaddr for kvm_arch_[insert|remove]_hw_breakpoint") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20250707171059.3064-2-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/kvm/kvm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/s390x/kvm/kvm.c b/target/s390x/kvm/kvm.c index 67d9a1977c..491cc5f975 100644 --- a/target/s390x/kvm/kvm.c +++ b/target/s390x/kvm/kvm.c @@ -889,7 +889,7 @@ int kvm_arch_remove_sw_breakpoint(CPUState *cs, struct kvm_sw_breakpoint *bp) return 0; } -static struct kvm_hw_breakpoint *find_hw_breakpoint(target_ulong addr, +static struct kvm_hw_breakpoint *find_hw_breakpoint(vaddr addr, int len, int type) { int n; @@ -904,7 +904,7 @@ static struct kvm_hw_breakpoint *find_hw_breakpoint(target_ulong addr, return NULL; } -static int insert_hw_breakpoint(target_ulong addr, int len, int type) +static int insert_hw_breakpoint(vaddr addr, int len, int type) { int size; From 79d7e60c326bcb9d165e5d52a29b238937bedd8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 7 Jul 2025 19:10:59 +0200 Subject: [PATCH 2107/2760] target/s390x/tcg: Use vaddr in s390_probe_access() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 70ebd9ce1cb ("s390x/tcg: Fault-safe memset") passed vaddr type to access_prepare(), and commit b6c636f2cd6 ("s390x/tcg: Fault-safe memmove") to do_access_get_byte(), but declared S390Access::vaddr[1,2] as target_ulong. Directly declare these as vaddr type, and have s390_probe_access() use that type as argument. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20250707171059.3064-3-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/tcg/mem_helper.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/target/s390x/tcg/mem_helper.c b/target/s390x/tcg/mem_helper.c index a03609a140..f1acb1618f 100644 --- a/target/s390x/tcg/mem_helper.c +++ b/target/s390x/tcg/mem_helper.c @@ -126,8 +126,8 @@ static inline void cpu_stsize_data_ra(CPUS390XState *env, uint64_t addr, /* An access covers at most 4096 bytes and therefore at most two pages. */ typedef struct S390Access { - target_ulong vaddr1; - target_ulong vaddr2; + vaddr vaddr1; + vaddr vaddr2; void *haddr1; void *haddr2; uint16_t size1; @@ -148,7 +148,7 @@ typedef struct S390Access { * For !CONFIG_USER_ONLY, the TEC is stored stored to env->tlb_fill_tec. * For CONFIG_USER_ONLY, the faulting address is stored to env->__excp_addr. */ -static inline int s390_probe_access(CPUArchState *env, target_ulong addr, +static inline int s390_probe_access(CPUArchState *env, vaddr addr, int size, MMUAccessType access_type, int mmu_idx, bool nonfault, void **phost, uintptr_t ra) @@ -258,7 +258,7 @@ static void access_memset(CPUS390XState *env, S390Access *desta, static uint8_t access_get_byte(CPUS390XState *env, S390Access *access, int offset, uintptr_t ra) { - target_ulong vaddr = access->vaddr1; + vaddr vaddr = access->vaddr1; void *haddr = access->haddr1; if (unlikely(offset >= access->size1)) { @@ -278,7 +278,7 @@ static uint8_t access_get_byte(CPUS390XState *env, S390Access *access, static void access_set_byte(CPUS390XState *env, S390Access *access, int offset, uint8_t byte, uintptr_t ra) { - target_ulong vaddr = access->vaddr1; + vaddr vaddr = access->vaddr1; void *haddr = access->haddr1; if (unlikely(offset >= access->size1)) { From 3ffa21d293bf5e4f997ba117ee3e943344b71044 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 Jul 2025 17:15:52 +0100 Subject: [PATCH 2108/2760] hw/s390x/s390-pci-bus.c: Use g_assert_not_reached() in functions taking an ett The s390-pci-bus.c code, Coverity complains about a possible overflow because get_table_index() can return -1 if the ett value passed in is not one of the three permitted ZPCI_ETT_PT, ZPCI_ETT_ST, ZPCI_ETT_RT, but the caller in table_translate() doesn't check this and instead uses the return value directly in a calculation of the guest address to read from. In fact this case cannot happen, because: * get_table_index() is called only from table_translate() * the only caller of table_translate() loops through the ett values in the order RT, ST, PT until table_translate() returns 0 * table_translate() will return 0 for the error cases and when translate_iscomplete() returns true * translate_iscomplete() is always true for ZPCI_ETT_PT So table_translate() is always called with a valid ett value. Instead of having the various functions called from table_translate() return a default or dummy value when the ett argument is out of range, use g_assert_not_reached() to indicate that this is impossible. Coverity: CID 1547609 Signed-off-by: Peter Maydell Reviewed-by: Matthew Rosato Reviewed-by: Halil Pasic Message-ID: <20250710161552.1287399-1-peter.maydell@linaro.org> Signed-off-by: Thomas Huth --- hw/s390x/s390-pci-bus.c | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/hw/s390x/s390-pci-bus.c b/hw/s390x/s390-pci-bus.c index e6aa44531f..f87d2748b6 100644 --- a/hw/s390x/s390-pci-bus.c +++ b/hw/s390x/s390-pci-bus.c @@ -384,9 +384,9 @@ static uint64_t get_table_index(uint64_t iova, int8_t ett) return calc_sx(iova); case ZPCI_ETT_RT: return calc_rtx(iova); + default: + g_assert_not_reached(); } - - return -1; } static bool entry_isvalid(uint64_t entry, int8_t ett) @@ -397,22 +397,24 @@ static bool entry_isvalid(uint64_t entry, int8_t ett) case ZPCI_ETT_ST: case ZPCI_ETT_RT: return rt_entry_isvalid(entry); + default: + g_assert_not_reached(); } - - return false; } /* Return true if address translation is done */ static bool translate_iscomplete(uint64_t entry, int8_t ett) { switch (ett) { - case 0: + case ZPCI_ETT_ST: return (entry & ZPCI_TABLE_FC) ? true : false; - case 1: + case ZPCI_ETT_RT: return false; + case ZPCI_ETT_PT: + return true; + default: + g_assert_not_reached(); } - - return true; } static uint64_t get_frame_size(int8_t ett) @@ -424,9 +426,9 @@ static uint64_t get_frame_size(int8_t ett) return 1ULL << 20; case ZPCI_ETT_RT: return 1ULL << 31; + default: + g_assert_not_reached(); } - - return 0; } static uint64_t get_next_table_origin(uint64_t entry, int8_t ett) @@ -438,9 +440,9 @@ static uint64_t get_next_table_origin(uint64_t entry, int8_t ett) return get_st_pto(entry); case ZPCI_ETT_RT: return get_rt_sto(entry); + default: + g_assert_not_reached(); } - - return 0; } /** From a4adf071dc7ee615c2ed4517ad77b125e2e70066 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 9 Jul 2025 10:34:39 +0200 Subject: [PATCH 2109/2760] pc-bios/s390-ccw: Allow to select a different pxelinux.cfg entry via loadparm Since we're linking the network booting code into the main firmware binary nowadays, we can support the "loadparm" parameter now quite easily for pxelinux.cfg config files that contain multiple entries. Reviewed-by: Jared Rossi Signed-off-by: Thomas Huth Message-ID: <20250709083443.41574-2-thuth@redhat.com> --- pc-bios/s390-ccw/netmain.c | 32 ++++++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index 719a547ada..c0aafca22d 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -332,6 +332,27 @@ static int load_kernel_with_initrd(filename_ip_t *fn_ip, return rc; } +static int net_select_and_load_kernel(filename_ip_t *fn_ip, + int num_ent, int selected, + struct pl_cfg_entry *entries) +{ + unsigned int loadparm = get_loadparm_index(); + + if (num_ent <= 0) { + return -1; + } + + IPL_assert(loadparm <= num_ent, + "loadparm is set to an entry that is not available in the " + "pxelinux.cfg file!"); + + if (loadparm > 0) { + selected = loadparm - 1; + } + + return load_kernel_with_initrd(fn_ip, &entries[selected]); +} + #define MAX_PXELINUX_ENTRIES 16 static int net_try_pxelinux_cfg(filename_ip_t *fn_ip) @@ -343,11 +364,8 @@ static int net_try_pxelinux_cfg(filename_ip_t *fn_ip) DEFAULT_TFTP_RETRIES, cfgbuf, sizeof(cfgbuf), entries, MAX_PXELINUX_ENTRIES, &def_ent); - if (num_ent > 0) { - return load_kernel_with_initrd(fn_ip, &entries[def_ent]); - } - return -1; + return net_select_and_load_kernel(fn_ip, num_ent, def_ent, entries); } /** @@ -433,10 +451,8 @@ static int net_try_direct_tftp_load(filename_ip_t *fn_ip) num_ent = pxelinux_parse_cfg(cfgbuf, sizeof(cfgbuf), entries, MAX_PXELINUX_ENTRIES, &def_ent); - if (num_ent <= 0) { - return -1; - } - return load_kernel_with_initrd(fn_ip, &entries[def_ent]); + return net_select_and_load_kernel(fn_ip, num_ent, def_ent, + entries); } } From 108977796fbb765c7bcf040500ae9711cb2fa596 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 9 Jul 2025 10:34:40 +0200 Subject: [PATCH 2110/2760] pc-bios/s390-ccw: Allow up to 31 entries for pxelinux.cfg We're going to support a menu for the pxelinux.cfg code, and to be able to reuse some functionality from menu.c, we should align the maximum amount of possible entries with the MAX_BOOT_ENTRIES constant that is used there. Thus replace MAX_PXELINUX_ENTRIES with MAX_BOOT_ENTRIES. Reviewed-by: Jared Rossi Signed-off-by: Thomas Huth Message-ID: <20250709083443.41574-3-thuth@redhat.com> --- pc-bios/s390-ccw/netmain.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index c0aafca22d..6f64323cd8 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -353,17 +353,15 @@ static int net_select_and_load_kernel(filename_ip_t *fn_ip, return load_kernel_with_initrd(fn_ip, &entries[selected]); } -#define MAX_PXELINUX_ENTRIES 16 - static int net_try_pxelinux_cfg(filename_ip_t *fn_ip) { - struct pl_cfg_entry entries[MAX_PXELINUX_ENTRIES]; + struct pl_cfg_entry entries[MAX_BOOT_ENTRIES]; int num_ent, def_ent = 0; num_ent = pxelinux_load_parse_cfg(fn_ip, mac, get_uuid(), DEFAULT_TFTP_RETRIES, cfgbuf, sizeof(cfgbuf), - entries, MAX_PXELINUX_ENTRIES, &def_ent); + entries, MAX_BOOT_ENTRIES, &def_ent); return net_select_and_load_kernel(fn_ip, num_ent, def_ent, entries); } @@ -446,11 +444,11 @@ static int net_try_direct_tftp_load(filename_ip_t *fn_ip) * a magic comment string. */ if (!strncasecmp("# pxelinux", cfgbuf, 10)) { - struct pl_cfg_entry entries[MAX_PXELINUX_ENTRIES]; + struct pl_cfg_entry entries[MAX_BOOT_ENTRIES]; int num_ent, def_ent = 0; num_ent = pxelinux_parse_cfg(cfgbuf, sizeof(cfgbuf), entries, - MAX_PXELINUX_ENTRIES, &def_ent); + MAX_BOOT_ENTRIES, &def_ent); return net_select_and_load_kernel(fn_ip, num_ent, def_ent, entries); } From 47d68f7475feb86c9347f7013066b3b05b545cfc Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 9 Jul 2025 10:34:41 +0200 Subject: [PATCH 2111/2760] pc-bios/s390-ccw: Make get_boot_index() from menu.c global We are going to reuse this function for selecting an entry from the pxelinux.cfg menu, so rename this function with a "menu_" prefix and make it available globally. Reviewed-by: Jared Rossi Signed-off-by: Thomas Huth Message-ID: <20250709083443.41574-4-thuth@redhat.com> --- pc-bios/s390-ccw/menu.c | 6 +++--- pc-bios/s390-ccw/s390-ccw.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pc-bios/s390-ccw/menu.c b/pc-bios/s390-ccw/menu.c index 84062e94af..eeaff78f87 100644 --- a/pc-bios/s390-ccw/menu.c +++ b/pc-bios/s390-ccw/menu.c @@ -159,7 +159,7 @@ static void boot_menu_prompt(bool retry) } } -static int get_boot_index(bool *valid_entries) +int menu_get_boot_index(bool *valid_entries) { int boot_index; bool retry = false; @@ -224,7 +224,7 @@ int menu_get_zipl_boot_index(const char *menu_data) } printf("\n"); - return get_boot_index(valid_entries); + return menu_get_boot_index(valid_entries); } int menu_get_enum_boot_index(bool *valid_entries) @@ -247,7 +247,7 @@ int menu_get_enum_boot_index(bool *valid_entries) } printf("\n"); - return get_boot_index(valid_entries); + return menu_get_boot_index(valid_entries); } void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout) diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 6cdce3e5e5..b1dc35cded 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -87,6 +87,7 @@ int menu_get_zipl_boot_index(const char *menu_data); bool menu_is_enabled_zipl(void); int menu_get_enum_boot_index(bool *valid_entries); bool menu_is_enabled_enum(void); +int menu_get_boot_index(bool *valid_entries); #define MAX_BOOT_ENTRIES 31 From fc24fd9342ec5ddaeecdf2a28f7fc4c2cdf6b014 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 9 Jul 2025 10:34:42 +0200 Subject: [PATCH 2112/2760] pc-bios/s390-ccw: Add a boot menu for booting via pxelinux.cfg Show a simple boot menu for pxelinux.cfg, too, if the user requested it. Reviewed-by: Jared Rossi Signed-off-by: Thomas Huth Message-ID: <20250709083443.41574-5-thuth@redhat.com> --- pc-bios/s390-ccw/netmain.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/pc-bios/s390-ccw/netmain.c b/pc-bios/s390-ccw/netmain.c index 6f64323cd8..a9521dff41 100644 --- a/pc-bios/s390-ccw/netmain.c +++ b/pc-bios/s390-ccw/netmain.c @@ -332,6 +332,28 @@ static int load_kernel_with_initrd(filename_ip_t *fn_ip, return rc; } +static int net_boot_menu(int num_ent, int def_ent, + struct pl_cfg_entry *entries) +{ + bool valid_entries[MAX_BOOT_ENTRIES] = { false }; + int idx; + + puts("\ns390-ccw pxelinux.cfg boot menu:\n"); + printf(" [0] default (%d)\n", def_ent + 1); + valid_entries[0] = true; + + for (idx = 1; idx <= num_ent; idx++) { + printf(" [%d] %s\n", idx, entries[idx - 1].label); + valid_entries[idx] = true; + } + putchar('\n'); + + idx = menu_get_boot_index(valid_entries); + putchar('\n'); + + return idx; +} + static int net_select_and_load_kernel(filename_ip_t *fn_ip, int num_ent, int selected, struct pl_cfg_entry *entries) @@ -342,6 +364,10 @@ static int net_select_and_load_kernel(filename_ip_t *fn_ip, return -1; } + if (menu_is_enabled_enum() && num_ent > 1) { + loadparm = net_boot_menu(num_ent, selected, entries); + } + IPL_assert(loadparm <= num_ent, "loadparm is set to an entry that is not available in the " "pxelinux.cfg file!"); From c784de966b154810a4c97eb0e4a658ec6457dd4c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 9 Jul 2025 10:34:43 +0200 Subject: [PATCH 2113/2760] tests/functional: Add a test for s390x pxelinux.cfg network booting Check the various ways of booting a kernel via pxelinux.cfg file, e.g. by specifying the config file name via the MAC address or the UUID of the guest. Also check whether we can successfully load an alternate kernel via the "loadparm" parameter here and whether the boot menu shows up with "-boot menu=on". Reviewed-by: Jared Rossi Signed-off-by: Thomas Huth Message-ID: <20250709083443.41574-6-thuth@redhat.com> --- MAINTAINERS | 1 + tests/functional/meson.build | 1 + tests/functional/test_s390x_pxelinux.py | 119 ++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100755 tests/functional/test_s390x_pxelinux.py diff --git a/MAINTAINERS b/MAINTAINERS index 1842c3dd83..e88ed2c0a9 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1805,6 +1805,7 @@ F: hw/s390x/ipl.* F: pc-bios/s390-ccw/ F: pc-bios/s390-ccw.img F: docs/devel/s390-dasd-ipl.rst +F: tests/functional/test_s390x_pxelinux.py T: git https://github.com/borntraeger/qemu.git s390-next L: qemu-s390x@nongnu.org diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 050c9000b9..1ae5f02fb3 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -281,6 +281,7 @@ tests_rx_system_thorough = [ tests_s390x_system_thorough = [ 's390x_ccw_virtio', + 's390x_pxelinux', 's390x_replay', 's390x_topology', 's390x_tuxrun', diff --git a/tests/functional/test_s390x_pxelinux.py b/tests/functional/test_s390x_pxelinux.py new file mode 100755 index 0000000000..4fc33b8c46 --- /dev/null +++ b/tests/functional/test_s390x_pxelinux.py @@ -0,0 +1,119 @@ +#!/usr/bin/env python3 +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Functional test that checks the pxelinux.cfg network booting of a s390x VM +# (TFTP booting without config file is already tested by the pxe qtest, so +# we don't repeat that here). + +import os +import shutil + +from qemu_test import QemuSystemTest, Asset, wait_for_console_pattern + + +pxelinux_cfg_contents='''# pxelinux.cfg style config file +default Debian +label Nonexisting +kernel kernel.notavailable +initrd initrd.notavailable +label Debian +kernel kernel.debian +initrd initrd.debian +append testoption=teststring +label Fedora +kernel kernel.fedora +''' + +class S390PxeLinux(QemuSystemTest): + + ASSET_DEBIAN_KERNEL = Asset( + ('https://snapshot.debian.org/archive/debian/' + '20201126T092837Z/dists/buster/main/installer-s390x/' + '20190702+deb10u6/images/generic/kernel.debian'), + 'd411d17c39ae7ad38d27534376cbe88b68b403c325739364122c2e6f1537e818') + + ASSET_DEBIAN_INITRD = Asset( + ('https://snapshot.debian.org/archive/debian/' + '20201126T092837Z/dists/buster/main/installer-s390x/' + '20190702+deb10u6/images/generic/initrd.debian'), + '836bbd0fe6a5ca81274c28c2b063ea315ce1868660866e9b60180c575fef9fd5') + + ASSET_FEDORA_KERNEL = Asset( + ('https://archives.fedoraproject.org/pub/archive' + '/fedora-secondary/releases/31/Server/s390x/os' + '/images/kernel.img'), + '480859574f3f44caa6cd35c62d70e1ac0609134e22ce2a954bbed9b110c06e0b') + + def pxelinux_launch(self, pl_name='default', extra_opts=None): + self.require_netdev('user') + self.set_machine('s390-ccw-virtio') + + debian_kernel = self.ASSET_DEBIAN_KERNEL.fetch() + debian_initrd = self.ASSET_DEBIAN_INITRD.fetch() + fedora_kernel = self.ASSET_FEDORA_KERNEL.fetch() + + # Prepare a folder for the TFTP "server": + tftpdir = self.scratch_file('tftp') + shutil.rmtree(tftpdir, ignore_errors=True) # Remove stale stuff + os.mkdir(tftpdir) + shutil.copy(debian_kernel, os.path.join(tftpdir, 'kernel.debian')) + shutil.copy(debian_initrd, os.path.join(tftpdir, 'initrd.debian')) + shutil.copy(fedora_kernel, os.path.join(tftpdir, 'kernel.fedora')) + + pxelinuxdir = self.scratch_file('tftp', 'pxelinux.cfg') + os.mkdir(pxelinuxdir) + + cfg_fname = self.scratch_file('tftp', 'pxelinux.cfg', pl_name) + with open(cfg_fname, 'w', encoding='utf-8') as f: + f.write(pxelinux_cfg_contents) + + virtio_net_dev = 'virtio-net-ccw,netdev=n1,bootindex=1' + if extra_opts: + virtio_net_dev += ',' + extra_opts + + self.vm.add_args('-m', '384', + '-netdev', f'user,id=n1,tftp={tftpdir}', + '-device', virtio_net_dev) + self.vm.set_console() + self.vm.launch() + + + def test_default(self): + self.pxelinux_launch() + # The kernel prints its arguments to the console, so we can use + # this to check whether the kernel parameters are correctly handled: + wait_for_console_pattern(self, 'testoption=teststring') + # Now also check that we've successfully loaded the initrd: + wait_for_console_pattern(self, 'Unpacking initramfs...') + wait_for_console_pattern(self, 'Run /init as init process') + + def test_mac(self): + self.pxelinux_launch(pl_name='01-02-ca-fe-ba-be-42', + extra_opts='mac=02:ca:fe:ba:be:42,loadparm=3') + wait_for_console_pattern(self, 'Linux version 5.3.7-301.fc31.s390x') + + def test_uuid(self): + # Also add a non-bootable disk to check the fallback to network boot: + self.vm.add_args('-blockdev', 'null-co,size=65536,node-name=d1', + '-device', 'virtio-blk,drive=d1,bootindex=0,loadparm=1', + '-uuid', '550e8400-e29b-11d4-a716-446655441234') + self.pxelinux_launch(pl_name='550e8400-e29b-11d4-a716-446655441234') + wait_for_console_pattern(self, 'Debian 4.19.146-1 (2020-09-17)') + + def test_ip(self): + self.vm.add_args('-M', 'loadparm=3') + self.pxelinux_launch(pl_name='0A00020F') + wait_for_console_pattern(self, 'Linux version 5.3.7-301.fc31.s390x') + + def test_menu(self): + self.vm.add_args('-boot', 'menu=on,splash-time=10') + self.pxelinux_launch(pl_name='0A00') + wait_for_console_pattern(self, '[1] Nonexisting') + wait_for_console_pattern(self, '[2] Debian') + wait_for_console_pattern(self, '[3] Fedora') + wait_for_console_pattern(self, 'Debian 4.19.146-1 (2020-09-17)') + + +if __name__ == '__main__': + QemuSystemTest.main() From acb00a7aae47e9f6e9a955f4c83e2295c7271837 Mon Sep 17 00:00:00 2001 From: Sertonix Date: Tue, 10 Jun 2025 17:58:06 +0000 Subject: [PATCH 2114/2760] pc-bios/s390-ccw: link statically Adding -pie to LDFLAGS caused s390-ccw.img to become dynamically linked. By using -static-pie it will be linked statically like other bios. This ensures that the build output doesn't change depending on the default dynamic loader path of the toolchain. Fixes: d884c86dcd3b ("s390/bios: Make the s390-ccw.img relocatable") Signed-off-by: Sertonix Message-ID: Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index dc69dd484f..a0f24c94a8 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -47,7 +47,7 @@ EXTRA_CFLAGS += -fwrapv -fno-strict-aliasing -fno-asynchronous-unwind-tables EXTRA_CFLAGS += -msoft-float EXTRA_CFLAGS += -std=gnu99 EXTRA_CFLAGS += $(LIBC_INC) $(LIBNET_INC) -EXTRA_LDFLAGS += -Wl,-pie -nostdlib -z noexecstack -z text +EXTRA_LDFLAGS += -static-pie -nostdlib -z noexecstack -z text cc-test = $(CC) -Werror $1 -c -o /dev/null -xc /dev/null >/dev/null 2>/dev/null cc-option = if $(call cc-test, $1); then \ From 21820b4b479842c7fa67ec65f9b0b62c286b86f4 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Fri, 11 Jul 2025 10:18:23 +0200 Subject: [PATCH 2115/2760] pc-bios: Update the s390 bios images with the pxelinux.cfg loadparm changes This new s390-ccw.img binary contains the addition of the loadparm feature to the pxelinux.cfg network booting code. Signed-off-by: Thomas Huth --- pc-bios/s390-ccw.img | Bin 96000 -> 87824 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img index 47240f0a74eec17b0da3ad334157d1b0e62632b4..ff60978d28c59a3d9ee706e48ff268aad6476ddc 100644 GIT binary patch delta 39259 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 bNH0+UL|xw+p2|b(z0ohE+@7U()n54>tz%p6 delta 42894 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 I&`7WR1IT5~=Kufz From 06fcf87f56ac47b89932b3698e86224ea7bc5a93 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Tue, 1 Jul 2025 12:48:27 +0200 Subject: [PATCH 2116/2760] tests/functional: Add dependency to the keymap_targets When doing a "configure" in a an empty build directory, followed by a "make check" without a normal build in between, the vnc functional test currently fails since the keymaps have not been built yet. Thus add a dependency to the keymap_targets here to make sure that the keymaps are built before running the functional tests. Signed-off-by: Thomas Huth Message-ID: <20250701104827.363904-1-thuth@redhat.com> --- tests/functional/meson.build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 1ae5f02fb3..ae5c52d79f 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -374,7 +374,7 @@ foreach speed : ['quick', 'thorough'] target_tests = get_variable('tests_' + target_base + '_' + sysmode + '_' + speed, []) endif - test_deps = roms + test_deps = [roms, keymap_targets] test_env = environment() if have_tools test_env.set('QEMU_TEST_QEMU_IMG', meson.global_build_root() / 'qemu-img') From 3b1cf40dd665a0c4c38bc339fea6eacf1742b46c Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Mon, 7 Jul 2025 20:47:36 +0200 Subject: [PATCH 2117/2760] tests/functional/test_ppc_bamboo: Replace broken link with working assets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old image that we used for testing the bamboo machine has disappeared from the internet. Fortunately there is another kernel + initrd provided by Cédric that can be used for testing this machine, too. Reported-by: Stefan Hajnoczi Suggested-by: Cédric Le Goater Reviewed-by: Cédric Le Goater Signed-off-by: Thomas Huth Message-ID: <20250707184736.88660-1-thuth@redhat.com> --- tests/functional/test_ppc_bamboo.py | 34 ++++++++++++++++------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/tests/functional/test_ppc_bamboo.py b/tests/functional/test_ppc_bamboo.py index fddcc24d0d..c634ae7b4a 100755 --- a/tests/functional/test_ppc_bamboo.py +++ b/tests/functional/test_ppc_bamboo.py @@ -16,28 +16,32 @@ class BambooMachine(QemuSystemTest): timeout = 90 - ASSET_IMAGE = Asset( - ('http://landley.net/aboriginal/downloads/binaries/' - 'system-image-powerpc-440fp.tar.gz'), - 'c12b58f841c775a0e6df4832a55afe6b74814d1565d08ddeafc1fb949a075c5e') + ASSET_KERNEL = Asset( + ('https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/' + 'buildroot/qemu_ppc_bamboo-2023.11-8-gdcd9f0f6eb-20240105/vmlinux'), + 'a2e12eb45b73491ac62fc0bbeb68dead0dc5c0f22cf83146558389209b420ad1') + ASSET_INITRD = Asset( + ('https://github.com/legoater/qemu-ppc-boot/raw/refs/heads/main/' + 'buildroot/qemu_ppc_bamboo-2023.11-8-gdcd9f0f6eb-20240105/rootfs.cpio'), + 'd2a36bdb8763b389765dc8c29d4904cec2bd001c587f92e85ab9eb10d5ddda54') def test_ppc_bamboo(self): self.set_machine('bamboo') self.require_accelerator("tcg") self.require_netdev('user') - self.archive_extract(self.ASSET_IMAGE) + + kernel = self.ASSET_KERNEL.fetch() + initrd = self.ASSET_INITRD.fetch() + self.vm.set_console() - self.vm.add_args('-kernel', - self.scratch_file('system-image-powerpc-440fp', - 'linux'), - '-initrd', - self.scratch_file('system-image-powerpc-440fp', - 'rootfs.cpio.gz'), - '-nic', 'user,model=rtl8139,restrict=on') + self.vm.add_args('-kernel', kernel, + '-initrd', initrd, + '-nic', 'user,model=virtio-net-pci,restrict=on') self.vm.launch() - wait_for_console_pattern(self, 'Type exit when done') - exec_command_and_wait_for_pattern(self, 'ping 10.0.2.2', - '10.0.2.2 is alive!') + wait_for_console_pattern(self, 'buildroot login:') + exec_command_and_wait_for_pattern(self, 'root', '#') + exec_command_and_wait_for_pattern(self, 'ping -c1 10.0.2.2', + '1 packets transmitted, 1 packets received, 0% packet loss') exec_command_and_wait_for_pattern(self, 'halt', 'System Halted') if __name__ == '__main__': From 40c94731c4495e78f0f7402890eb01a0f43a64ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 11:57:43 +0200 Subject: [PATCH 2118/2760] target/s390x: Remove unused s390_cpu_[un]halt() user stubs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit da944885469 ("target/s390x: make helper.c sysemu-only") target/s390x/helper.c is only built for system mode, so s390_cpu_halt() and s390_cpu_unhalt() are never called from user mode. Fixes: da944885469 ("target/s390x: make helper.c sysemu-only") Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250708095746.12697-2-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/s390x-internal.h | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index a4ba6227ab..6894f0a256 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -246,16 +246,6 @@ void s390_cpu_finalize(Object *obj); void s390_cpu_system_class_init(CPUClass *cc); void s390_cpu_machine_reset_cb(void *opaque); bool s390_cpu_has_work(CPUState *cs); - -#else -static inline unsigned int s390_cpu_halt(S390CPU *cpu) -{ - return 0; -} - -static inline void s390_cpu_unhalt(S390CPU *cpu) -{ -} #endif /* CONFIG_USER_ONLY */ From b1180352f1c758ba4270cdaa3c41ceead3a43aad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 11:57:44 +0200 Subject: [PATCH 2119/2760] target/s390x: Expose s390_count_running_cpus() method MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to simplify the next commit where s390_count_running_cpus() is split out of s390_cpu_halt(), make its prototype public as a preliminary step. Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250708095746.12697-3-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/cpu-system.c | 2 +- target/s390x/s390x-internal.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c index 9b380e343c..2fa8c4d75d 100644 --- a/target/s390x/cpu-system.c +++ b/target/s390x/cpu-system.c @@ -196,7 +196,7 @@ static bool disabled_wait(CPUState *cpu) (PSW_MASK_IO | PSW_MASK_EXT | PSW_MASK_MCHECK)); } -static unsigned s390_count_running_cpus(void) +unsigned s390_count_running_cpus(void) { CPUState *cpu; int nr_running = 0; diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 6894f0a256..145e472edf 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -238,6 +238,7 @@ uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst, /* cpu.c */ #ifndef CONFIG_USER_ONLY +unsigned int s390_count_running_cpus(void); unsigned int s390_cpu_halt(S390CPU *cpu); void s390_cpu_unhalt(S390CPU *cpu); void s390_cpu_system_init(Object *obj); From 693b3039d77195953e70f008991c80bf9c5b9691 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 11:57:45 +0200 Subject: [PATCH 2120/2760] target/s390x: Have s390_cpu_halt() not return anything MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since halting a vCPU and how many left running do not need to be tied together, split the s390_count_running_cpus() call out of s390_cpu_halt() to the single caller using it: s390_handle_wait(). Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250708095746.12697-4-philmd@linaro.org> Signed-off-by: Thomas Huth --- target/s390x/cpu-system.c | 4 +--- target/s390x/helper.c | 4 +++- target/s390x/s390x-internal.h | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/target/s390x/cpu-system.c b/target/s390x/cpu-system.c index 2fa8c4d75d..709ccd5299 100644 --- a/target/s390x/cpu-system.c +++ b/target/s390x/cpu-system.c @@ -214,7 +214,7 @@ unsigned s390_count_running_cpus(void) return nr_running; } -unsigned int s390_cpu_halt(S390CPU *cpu) +void s390_cpu_halt(S390CPU *cpu) { CPUState *cs = CPU(cpu); trace_cpu_halt(cs->cpu_index); @@ -223,8 +223,6 @@ unsigned int s390_cpu_halt(S390CPU *cpu) cs->halted = 1; cs->exception_index = EXCP_HLT; } - - return s390_count_running_cpus(); } void s390_cpu_unhalt(S390CPU *cpu) diff --git a/target/s390x/helper.c b/target/s390x/helper.c index 3c57c32e47..5c127da1a6 100644 --- a/target/s390x/helper.c +++ b/target/s390x/helper.c @@ -91,7 +91,9 @@ void s390_handle_wait(S390CPU *cpu) { CPUState *cs = CPU(cpu); - if (s390_cpu_halt(cpu) == 0) { + s390_cpu_halt(cpu); + + if (s390_count_running_cpus() == 0) { if (is_special_wait_psw(cpu->env.psw.addr)) { qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); } else { diff --git a/target/s390x/s390x-internal.h b/target/s390x/s390x-internal.h index 145e472edf..56cce2e7f5 100644 --- a/target/s390x/s390x-internal.h +++ b/target/s390x/s390x-internal.h @@ -239,7 +239,7 @@ uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst, /* cpu.c */ #ifndef CONFIG_USER_ONLY unsigned int s390_count_running_cpus(void); -unsigned int s390_cpu_halt(S390CPU *cpu); +void s390_cpu_halt(S390CPU *cpu); void s390_cpu_unhalt(S390CPU *cpu); void s390_cpu_system_init(Object *obj); bool s390_cpu_system_realize(DeviceState *dev, Error **errp); From 34fc927b9138b7c4b91e58d29e26d27e04510a4b Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 Jul 2025 11:39:45 -0600 Subject: [PATCH 2121/2760] target/arm: Remove helper_sme2_luti4_4b This function isn't used. Resolves: Coverity CID 1612139 Signed-off-by: Richard Henderson Message-id: 20250710173945.115428-1-richard.henderson@linaro.org Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/tcg/helper.h | 1 - target/arm/tcg/vec_helper.c | 1 - 2 files changed, 2 deletions(-) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index d9565c8069..0a006d9514 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -1209,6 +1209,5 @@ DEF_HELPER_FLAGS_4(sme2_luti4_2b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) DEF_HELPER_FLAGS_4(sme2_luti4_2h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) DEF_HELPER_FLAGS_4(sme2_luti4_2s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) -DEF_HELPER_FLAGS_4(sme2_luti4_4b, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) DEF_HELPER_FLAGS_4(sme2_luti4_4h, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) DEF_HELPER_FLAGS_4(sme2_luti4_4s, TCG_CALL_NO_RWG, void, ptr, ptr, env, i32) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 0603db0909..bae6165b50 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -3526,7 +3526,6 @@ DO_SME2_LUT(4,1,s, 4) DO_SME2_LUT(4,2,b, 1) DO_SME2_LUT(4,2,h, 2) DO_SME2_LUT(4,2,s, 4) -DO_SME2_LUT(4,4,b, 1) DO_SME2_LUT(4,4,h, 2) DO_SME2_LUT(4,4,s, 4) From 26bab49db56acf8ef767a60fa1feb27a3556d3ec Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 11 Jul 2025 07:06:25 -0400 Subject: [PATCH 2122/2760] docs/system: arm: Add max78000 board description This adds the target guide for the max78000FTHR Signed-off-by: Jackson Donaldson Message-id: 20250711110626.624534-2-jcksn@duck.com [PMM: Moved doc to correct place in index; made underlines correct length; added missing trailing newline; added SPDX] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- docs/system/arm/max78000.rst | 37 ++++++++++++++++++++++++++++++++++++ docs/system/target-arm.rst | 1 + 2 files changed, 38 insertions(+) create mode 100644 docs/system/arm/max78000.rst diff --git a/docs/system/arm/max78000.rst b/docs/system/arm/max78000.rst new file mode 100644 index 0000000000..3d95011fef --- /dev/null +++ b/docs/system/arm/max78000.rst @@ -0,0 +1,37 @@ +.. SPDX-License-Identifier: GPL-2.0-or-later + +Analog Devices max78000 board (``max78000fthr``) +================================================ + +The max78000 is a Cortex-M4 based SOC with a RISC-V coprocessor. The RISC-V coprocessor is not supported. + +Supported devices +----------------- + + * Instruction Cache Controller + * UART + * Global Control Register + * True Random Number Generator + * AES + +Notable unsupported devices +--------------------------- + + * I2C + * CNN + * CRC + * SPI + +Boot options +------------ + +The max78000 can be started using the ``-kernel`` option to load a +firmware at address 0 as the ROM. As the ROM normally jumps to software loaded +from the internal flash at address 0x10000000, loading your program there is +generally advisable. If you don't have a copy of the ROM, the interrupt +vector table from user firmware will do. +Example: + +.. code-block:: bash + + $ qemu-system-arm -machine max78000fthr -kernel max78000.bin -device loader,file=max78000.bin,addr=0x10000000 diff --git a/docs/system/target-arm.rst b/docs/system/target-arm.rst index b96a05a920..a96d1867df 100644 --- a/docs/system/target-arm.rst +++ b/docs/system/target-arm.rst @@ -71,6 +71,7 @@ Board-specific documentation .. toctree:: :maxdepth: 1 + arm/max78000 arm/integratorcp arm/mps2 arm/musca From 3a323a813fd42fc7c37ef09bc7a714d8e31691ce Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Fri, 11 Jul 2025 07:06:26 -0400 Subject: [PATCH 2123/2760] tests/functional: Add a test for the MAX78000 arm machine Runs a binary from the max78000test repo used in developing the qemu implementation of the max78000 to verify that the machine and implemented devices generally still work. Signed-off-by: Jackson Donaldson Reviewed-by: Thomas Huth Message-id: 20250711110626.624534-3-jcksn@duck.com Signed-off-by: Peter Maydell --- tests/functional/meson.build | 1 + tests/functional/test_arm_max78000fthr.py | 48 +++++++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100755 tests/functional/test_arm_max78000fthr.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 050c9000b9..cd67e6d734 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -138,6 +138,7 @@ tests_arm_system_thorough = [ 'arm_cubieboard', 'arm_emcraft_sf2', 'arm_integratorcp', + 'arm_max78000fthr', 'arm_microbit', 'arm_orangepi', 'arm_quanta_gsj', diff --git a/tests/functional/test_arm_max78000fthr.py b/tests/functional/test_arm_max78000fthr.py new file mode 100755 index 0000000000..a82980b0f7 --- /dev/null +++ b/tests/functional/test_arm_max78000fthr.py @@ -0,0 +1,48 @@ +#!/usr/bin/env python3 +# +# Functional test that checks the max78000fthr machine. +# Tests ICC, GCR, TRNG, AES, and UART +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import QemuSystemTest, Asset, exec_command_and_wait_for_pattern +from qemu_test import wait_for_console_pattern + + +class Max78000Machine(QemuSystemTest): + + ASSET_FW = Asset( + 'https://github.com/JacksonDonaldson/max78000Test/raw/main/build/max78000.bin', + '86940b4bf60931bc6a8aa5db4b9f7f3cf8f64dbbd7ac534647980e536cf3adf7') + + def test_fthr(self): + self.set_machine('max78000fthr') + fw_path = self.ASSET_FW.fetch() + self.vm.set_console() + self.vm.add_args('-kernel', fw_path) + self.vm.add_args('-device', "loader,file=" + fw_path + ",addr=0x10000000") + self.vm.launch() + + wait_for_console_pattern(self, 'started') + + # i -> prints instruction cache values + exec_command_and_wait_for_pattern(self, 'i', 'CTRL: 00010001') + + # r -> gcr resets the machine + exec_command_and_wait_for_pattern(self, 'r', 'started') + + # z -> sets some memory, then has gcr zero it + exec_command_and_wait_for_pattern(self, 'z', 'initial value: 12345678') + wait_for_console_pattern(self, "after memz: 00000000") + + # t -> runs trng + exec_command_and_wait_for_pattern(self, 't', 'random data:') + + # a -> runs aes + exec_command_and_wait_for_pattern(self, 'a', + 'encrypted to : a47ca9dd e0df4c86 a070af6e 91710dec') + wait_for_console_pattern(self, + 'encrypted to : cab7a28e bf456751 9049fcea 8960494b') + +if __name__ == '__main__': + QemuSystemTest.main() From 31a90360fd8209fb479964a37c4329d5dbc3fb07 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:07:51 -0400 Subject: [PATCH 2124/2760] migration/hmp: Reorg "info migrate" once more Dave suggested the HMP output for "info migrate" can not only leverage the lines but also better grouping: https://lore.kernel.org/r/aC4_-nMc7FwsMf9p@gallifrey I followed Dave's suggestion, and some more modifications on top: - Added all elements into the picture - Use size_to_str() and drop most of the units: benefit is more friendly to most human eyes, bad side effect is lose of details, but that should be corner case per my uses, and one can still leverage the QMP interface when necessary. - Sub-grouping for "Transfers" ("Channels" and "Page Types"). - Better indentations Sample output: (qemu) info migrate Status: postcopy-active Time (ms): total=47317, setup=5, down=8 RAM info: Throughput (Mbps): 1342.83 Sizes: pagesize=4 KiB, total=4.02 GiB Transfers: transferred=1.41 GiB, remain=2.46 GiB Channels: precopy=15.2 MiB, multifd=0 B, postcopy=1.39 GiB Page Types: normal=367713, zero=41195 Page Rates (pps): transfer=40900, dirty=4 Others: dirty_syncs=2, postcopy_req=57503 Suggested-by: Dr. David Alan Gilbert Tested-by: Li Zhijian Reviewed-by: Li Zhijian Acked-by: Dr. David Alan Gilbert Reviewed-by: Juraj Marcin Tested-by: Mario Casquero Link: https://lore.kernel.org/r/20250613140801.474264-2-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration-hmp-cmds.c | 59 ++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index e8a563c7d8..367ff6037f 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -69,7 +69,7 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) } if (info->has_status) { - monitor_printf(mon, "Status: %s", + monitor_printf(mon, "Status: \t\t%s", MigrationStatus_str(info->status)); if (info->status == MIGRATION_STATUS_FAILED && info->error_desc) { monitor_printf(mon, " (%s)\n", info->error_desc); @@ -78,7 +78,7 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) } if (info->total_time) { - monitor_printf(mon, "Time (ms): total=%" PRIu64, + monitor_printf(mon, "Time (ms): \t\ttotal=%" PRIu64, info->total_time); if (info->has_setup_time) { monitor_printf(mon, ", setup=%" PRIu64, @@ -110,48 +110,51 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) } if (info->ram) { + g_autofree char *str_psize = size_to_str(info->ram->page_size); + g_autofree char *str_total = size_to_str(info->ram->total); + g_autofree char *str_transferred = size_to_str(info->ram->transferred); + g_autofree char *str_remaining = size_to_str(info->ram->remaining); + g_autofree char *str_precopy = size_to_str(info->ram->precopy_bytes); + g_autofree char *str_multifd = size_to_str(info->ram->multifd_bytes); + g_autofree char *str_postcopy = size_to_str(info->ram->postcopy_bytes); + monitor_printf(mon, "RAM info:\n"); - monitor_printf(mon, " Throughput (Mbps): %0.2f\n", + monitor_printf(mon, " Throughput (Mbps): \t%0.2f\n", info->ram->mbps); - monitor_printf(mon, " Sizes (KiB): pagesize=%" PRIu64 - ", total=%" PRIu64 ",\n", - info->ram->page_size >> 10, - info->ram->total >> 10); - monitor_printf(mon, " transferred=%" PRIu64 - ", remain=%" PRIu64 ",\n", - info->ram->transferred >> 10, - info->ram->remaining >> 10); - monitor_printf(mon, " precopy=%" PRIu64 - ", multifd=%" PRIu64 - ", postcopy=%" PRIu64, - info->ram->precopy_bytes >> 10, - info->ram->multifd_bytes >> 10, - info->ram->postcopy_bytes >> 10); + monitor_printf(mon, " Sizes: \t\tpagesize=%s, total=%s\n", + str_psize, str_total); + monitor_printf(mon, " Transfers: \t\ttransferred=%s, remain=%s\n", + str_transferred, str_remaining); + monitor_printf(mon, " Channels: \t\tprecopy=%s, " + "multifd=%s, postcopy=%s", + str_precopy, str_multifd, str_postcopy); if (info->vfio) { - monitor_printf(mon, ", vfio=%" PRIu64, - info->vfio->transferred >> 10); + g_autofree char *str_vfio = size_to_str(info->vfio->transferred); + + monitor_printf(mon, ", vfio=%s", str_vfio); } monitor_printf(mon, "\n"); - monitor_printf(mon, " Pages: normal=%" PRIu64 ", zero=%" PRIu64 - ", rate_per_sec=%" PRIu64 "\n", - info->ram->normal, - info->ram->duplicate, + monitor_printf(mon, " Page Types: \tnormal=%" PRIu64 + ", zero=%" PRIu64 "\n", + info->ram->normal, info->ram->duplicate); + monitor_printf(mon, " Page Rates (pps): \ttransfer=%" PRIu64, info->ram->pages_per_second); - monitor_printf(mon, " Others: dirty_syncs=%" PRIu64, - info->ram->dirty_sync_count); - if (info->ram->dirty_pages_rate) { - monitor_printf(mon, ", dirty_pages_rate=%" PRIu64, + monitor_printf(mon, ", dirty=%" PRIu64, info->ram->dirty_pages_rate); } + monitor_printf(mon, "\n"); + + monitor_printf(mon, " Others: \t\tdirty_syncs=%" PRIu64, + info->ram->dirty_sync_count); if (info->ram->postcopy_requests) { monitor_printf(mon, ", postcopy_req=%" PRIu64, info->ram->postcopy_requests); } if (info->ram->downtime_bytes) { - monitor_printf(mon, ", downtime_ram=%" PRIu64, + monitor_printf(mon, ", downtime_bytes=%" PRIu64, info->ram->downtime_bytes); } if (info->ram->dirty_sync_missed_zero_copy) { From 2862d6d4fb09f57cabc4389fed34ae767ab2da94 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:07:52 -0400 Subject: [PATCH 2125/2760] migration/hmp: Fix postcopy-blocktime per-vCPU results Unfortunately, it was never correctly shown.. This is only found when I started to look into making the blocktime feature more useful (so as to avoid using bpftrace, even though I'm not sure which one will be harder to use..). So the old dump would look like this: Postcopy vCPU Blocktime: 0-1,4,10,21,33,46,48,59 Even though there're actually 40 vcpus, and the string will merge same elements and also sort them. To fix it, simply loop over the uint32List manually. Now it looks like: Postcopy vCPU Blocktime (ms): [15, 0, 0, 43, 29, 34, 36, 29, 37, 41, 33, 37, 45, 52, 50, 38, 40, 37, 40, 49, 40, 35, 35, 35, 81, 19, 18, 19, 18, 30, 22, 3, 0, 0, 0, 0, 0, 0, 0, 0] Cc: Dr. David Alan Gilbert Cc: Alexey Perevalov Cc: Markus Armbruster Tested-by: Mario Casquero Reviewed-by: Juraj Marcin Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613140801.474264-3-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration-hmp-cmds.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 367ff6037f..867e017b32 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -208,15 +208,19 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) } if (info->has_postcopy_vcpu_blocktime) { - Visitor *v; - char *str; - v = string_output_visitor_new(false, &str); - visit_type_uint32List(v, NULL, &info->postcopy_vcpu_blocktime, - &error_abort); - visit_complete(v, &str); - monitor_printf(mon, "Postcopy vCPU Blocktime: %s\n", str); - g_free(str); - visit_free(v); + uint32List *item = info->postcopy_vcpu_blocktime; + const char *sep = ""; + int count = 0; + + monitor_printf(mon, "Postcopy vCPU Blocktime (ms):\n ["); + + while (item) { + monitor_printf(mon, "%s%"PRIu32, sep, item->value); + item = item->next; + /* Each line 10 vcpu results, newline if there's more */ + sep = ((++count % 10 == 0) && item) ? ",\n " : ", "; + } + monitor_printf(mon, "]\n"); } out: From 35290df01b064134a57339b2dbfee8713f9e6d85 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:07:53 -0400 Subject: [PATCH 2126/2760] migration/docs: Move docs for postcopy blocktime feature Move it out of vanilla postcopy session, but instead a standalone feature. When at it, removing the NOTE because it's incorrect now after introduction of max-postcopy-bandwidth, which can control the throughput even for postcopy phase. Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250613140801.474264-4-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- docs/devel/migration/postcopy.rst | 36 +++++++++++++++---------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/docs/devel/migration/postcopy.rst b/docs/devel/migration/postcopy.rst index 82e7a848c6..e319388d8f 100644 --- a/docs/devel/migration/postcopy.rst +++ b/docs/devel/migration/postcopy.rst @@ -33,25 +33,6 @@ will now cause the transition from precopy to postcopy. It can be issued immediately after migration is started or any time later on. Issuing it after the end of a migration is harmless. -Blocktime is a postcopy live migration metric, intended to show how -long the vCPU was in state of interruptible sleep due to pagefault. -That metric is calculated both for all vCPUs as overlapped value, and -separately for each vCPU. These values are calculated on destination -side. To enable postcopy blocktime calculation, enter following -command on destination monitor: - -``migrate_set_capability postcopy-blocktime on`` - -Postcopy blocktime can be retrieved by query-migrate qmp command. -postcopy-blocktime value of qmp command will show overlapped blocking -time for all vCPU, postcopy-vcpu-blocktime will show list of blocking -time per vCPU. - -.. note:: - During the postcopy phase, the bandwidth limits set using - ``migrate_set_parameter`` is ignored (to avoid delaying requested pages that - the destination is waiting for). - Postcopy internals ================== @@ -312,3 +293,20 @@ explicitly) to be sent in a separate preempt channel, rather than queued in the background migration channel. Anyone who cares about latencies of page faults during a postcopy migration should enable this feature. By default, it's not enabled. + +Postcopy blocktime statistics +----------------------------- + +Blocktime is a postcopy live migration metric, intended to show how +long the vCPU was in state of interruptible sleep due to pagefault. +That metric is calculated both for all vCPUs as overlapped value, and +separately for each vCPU. These values are calculated on destination +side. To enable postcopy blocktime calculation, enter following +command on destination monitor: + +``migrate_set_capability postcopy-blocktime on`` + +Postcopy blocktime can be retrieved by query-migrate qmp command. +postcopy-blocktime value of qmp command will show overlapped blocking +time for all vCPU, postcopy-vcpu-blocktime will show list of blocking +time per vCPU. From 2145f38c31e940abca19bb8a9dc0d2549a40df14 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:07:54 -0400 Subject: [PATCH 2127/2760] migration/bg-snapshot: Do not check for SKIP in iterator It's not possible to happen in bg-snapshot case. Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250613140801.474264-5-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 4098870bce..e33e39ac74 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3887,9 +3887,8 @@ static void *bg_migration_thread(void *opaque) while (migration_is_active()) { MigIterateState iter_state = bg_migration_iteration_run(s); - if (iter_state == MIG_ITERATE_SKIP) { - continue; - } else if (iter_state == MIG_ITERATE_BREAK) { + + if (iter_state == MIG_ITERATE_BREAK) { break; } From d7530a9682b7cdac1859dcf1e28573415d2afd56 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:07:55 -0400 Subject: [PATCH 2128/2760] migration: Drop save_live_complete_postcopy hook The hook is only defined in two vmstate users ("ram" and "block dirty bitmap"), meanwhile both of them define the hook exactly the same as the precopy version. Hence, this postcopy version isn't needed. No functional change intended. Reviewed-by: Juraj Marcin Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613140801.474264-6-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- include/migration/register.h | 24 ++++++++---------------- migration/block-dirty-bitmap.c | 1 - migration/ram.c | 1 - migration/savevm.c | 9 ++++----- 4 files changed, 12 insertions(+), 23 deletions(-) diff --git a/include/migration/register.h b/include/migration/register.h index b79dc81b8d..e022195785 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -77,26 +77,18 @@ typedef struct SaveVMHandlers { */ void (*save_cleanup)(void *opaque); - /** - * @save_live_complete_postcopy - * - * Called at the end of postcopy for all postcopyable devices. - * - * @f: QEMUFile where to send the data - * @opaque: data pointer passed to register_savevm_live() - * - * Returns zero to indicate success and negative for error - */ - int (*save_live_complete_postcopy)(QEMUFile *f, void *opaque); - /** * @save_live_complete_precopy * * Transmits the last section for the device containing any - * remaining data at the end of a precopy phase. When postcopy is - * enabled, devices that support postcopy will skip this step, - * where the final data will be flushed at the end of postcopy via - * @save_live_complete_postcopy instead. + * remaining data at the end phase of migration. + * + * For precopy, this will be invoked _during_ the switchover phase + * after source VM is stopped. + * + * For postcopy, this will be invoked _after_ the switchover phase + * (except some very unusual cases, like PMEM ramblocks), while + * destination VM can be running. * * @f: QEMUFile where to send the data * @opaque: data pointer passed to register_savevm_live() diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index f2c352d4a7..6ee3c32a76 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -1248,7 +1248,6 @@ static bool dirty_bitmap_has_postcopy(void *opaque) static SaveVMHandlers savevm_dirty_bitmap_handlers = { .save_setup = dirty_bitmap_save_setup, - .save_live_complete_postcopy = dirty_bitmap_save_complete, .save_live_complete_precopy = dirty_bitmap_save_complete, .has_postcopy = dirty_bitmap_has_postcopy, .state_pending_exact = dirty_bitmap_state_pending, diff --git a/migration/ram.c b/migration/ram.c index 2140785a05..6bb4f13031 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -4548,7 +4548,6 @@ void postcopy_preempt_shutdown_file(MigrationState *s) static SaveVMHandlers savevm_ram_handlers = { .save_setup = ram_save_setup, .save_live_iterate = ram_save_iterate, - .save_live_complete_postcopy = ram_save_complete, .save_live_complete_precopy = ram_save_complete, .has_postcopy = ram_has_postcopy, .state_pending_exact = ram_state_pending_exact, diff --git a/migration/savevm.c b/migration/savevm.c index bb04a4520d..3e20f8608a 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1485,9 +1485,8 @@ bool should_send_vmdesc(void) } /* - * Calls the save_live_complete_postcopy methods - * causing the last few pages to be sent immediately and doing any associated - * cleanup. + * Complete saving any postcopy-able devices. + * * Note postcopy also calls qemu_savevm_state_complete_precopy to complete * all the other devices, but that happens at the point we switch to postcopy. */ @@ -1497,7 +1496,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) int ret; QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (!se->ops || !se->ops->save_live_complete_postcopy) { + if (!se->ops || !se->ops->save_live_complete_precopy) { continue; } if (se->ops->is_active) { @@ -1510,7 +1509,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) qemu_put_byte(f, QEMU_VM_SECTION_END); qemu_put_be32(f, se->section_id); - ret = se->ops->save_live_complete_postcopy(f, se->opaque); + ret = se->ops->save_live_complete_precopy(f, se->opaque); trace_savevm_section_end(se->idstr, se->section_id, ret); save_section_footer(f, se); if (ret < 0) { From 57c43e52bdf7f97c7555f408bddbc0b95e081844 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:07:56 -0400 Subject: [PATCH 2129/2760] migration: Rename save_live_complete_precopy to save_complete Now after merging the precopy and postcopy version of complete() hook, rename the precopy version from save_live_complete_precopy() to save_complete(). Dropping the "live" when at it, because it's in most cases not live when happening (in precopy). No functional change intended. Reviewed-by: Juraj Marcin Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613140801.474264-7-peterx@redhat.com [peterx: squash the fixup that covers a few more doc spots, per Juraj] Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- docs/devel/migration/main.rst | 4 ++-- docs/devel/migration/vfio.rst | 12 ++++++------ hw/ppc/spapr.c | 2 +- hw/s390x/s390-stattrib.c | 2 +- hw/vfio/migration.c | 2 +- include/migration/register.h | 6 +++--- migration/block-dirty-bitmap.c | 2 +- migration/ram.c | 2 +- migration/savevm.c | 8 ++++---- 9 files changed, 20 insertions(+), 20 deletions(-) diff --git a/docs/devel/migration/main.rst b/docs/devel/migration/main.rst index cdd4f4a6d7..6493c1d2bc 100644 --- a/docs/devel/migration/main.rst +++ b/docs/devel/migration/main.rst @@ -508,8 +508,8 @@ An iterative device must provide: the point that stream bandwidth limits tell it to stop. Each call generates one section. - - A ``save_live_complete_precopy`` function that must transmit the - last section for the device containing any remaining data. + - A ``save_complete`` function that must transmit the last section for + the device containing any remaining data. - A ``load_state`` function used to load sections generated by any of the save functions that generate sections. diff --git a/docs/devel/migration/vfio.rst b/docs/devel/migration/vfio.rst index 673e354754..8ff5ab0c74 100644 --- a/docs/devel/migration/vfio.rst +++ b/docs/devel/migration/vfio.rst @@ -75,10 +75,10 @@ VFIO implements the device hooks for the iterative approach as follows: in the non-multifd mode. In the multifd mode it just emits either a dummy EOS marker. -* A ``save_live_complete_precopy`` function that sets the VFIO device in - _STOP_COPY state and iteratively copies the data for the VFIO device until - the vendor driver indicates that no data remains. - In the multifd mode it just emits a dummy EOS marker. +* A ``save_complete`` function that sets the VFIO device in _STOP_COPY + state and iteratively copies the data for the VFIO device until the + vendor driver indicates that no data remains. In the multifd mode it + just emits a dummy EOS marker. * A ``save_live_complete_precopy_thread`` function that in the multifd mode provides thread handler performing multifd device state transfer. @@ -195,9 +195,9 @@ Live migration save path | Then the VFIO device is put in _STOP_COPY state (FINISH_MIGRATE, _ACTIVE, _STOP_COPY) - .save_live_complete_precopy() is called for each active device + .save_complete() is called for each active device For the VFIO device: in the non-multifd mode iterate in - .save_live_complete_precopy() until + .save_complete() until pending data is 0 In the multifd mode this iteration is done in .save_live_complete_precopy_thread() instead. diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 08615f6c90..40f53ad7b3 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -2518,7 +2518,7 @@ static void htab_save_cleanup(void *opaque) static SaveVMHandlers savevm_htab_handlers = { .save_setup = htab_save_setup, .save_live_iterate = htab_save_iterate, - .save_live_complete_precopy = htab_save_complete, + .save_complete = htab_save_complete, .save_cleanup = htab_save_cleanup, .load_state = htab_load, }; diff --git a/hw/s390x/s390-stattrib.c b/hw/s390x/s390-stattrib.c index f74cf32636..13a678a803 100644 --- a/hw/s390x/s390-stattrib.c +++ b/hw/s390x/s390-stattrib.c @@ -338,7 +338,7 @@ static const TypeInfo qemu_s390_stattrib_info = { static SaveVMHandlers savevm_s390_stattrib_handlers = { .save_setup = cmma_save_setup, .save_live_iterate = cmma_save_iterate, - .save_live_complete_precopy = cmma_save_complete, + .save_complete = cmma_save_complete, .state_pending_exact = cmma_state_pending, .state_pending_estimate = cmma_state_pending, .save_cleanup = cmma_save_cleanup, diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index b76697bd1a..33a71f8999 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -824,7 +824,7 @@ static const SaveVMHandlers savevm_vfio_handlers = { .state_pending_exact = vfio_state_pending_exact, .is_active_iterate = vfio_is_active_iterate, .save_live_iterate = vfio_save_iterate, - .save_live_complete_precopy = vfio_save_complete_precopy, + .save_complete = vfio_save_complete_precopy, .save_state = vfio_save_state, .load_setup = vfio_load_setup, .load_cleanup = vfio_load_cleanup, diff --git a/include/migration/register.h b/include/migration/register.h index e022195785..2a26e76a68 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -78,7 +78,7 @@ typedef struct SaveVMHandlers { void (*save_cleanup)(void *opaque); /** - * @save_live_complete_precopy + * @save_complete * * Transmits the last section for the device containing any * remaining data at the end phase of migration. @@ -95,7 +95,7 @@ typedef struct SaveVMHandlers { * * Returns zero to indicate success and negative for error */ - int (*save_live_complete_precopy)(QEMUFile *f, void *opaque); + int (*save_complete)(QEMUFile *f, void *opaque); /** * @save_live_complete_precopy_thread (invoked in a separate thread) @@ -103,7 +103,7 @@ typedef struct SaveVMHandlers { * Called at the end of a precopy phase from a separate worker thread * in configurations where multifd device state transfer is supported * in order to perform asynchronous transmission of the remaining data in - * parallel with @save_live_complete_precopy handlers. + * parallel with @save_complete handlers. * When postcopy is enabled, devices that support postcopy will skip this * step. * diff --git a/migration/block-dirty-bitmap.c b/migration/block-dirty-bitmap.c index 6ee3c32a76..a061aad817 100644 --- a/migration/block-dirty-bitmap.c +++ b/migration/block-dirty-bitmap.c @@ -1248,7 +1248,7 @@ static bool dirty_bitmap_has_postcopy(void *opaque) static SaveVMHandlers savevm_dirty_bitmap_handlers = { .save_setup = dirty_bitmap_save_setup, - .save_live_complete_precopy = dirty_bitmap_save_complete, + .save_complete = dirty_bitmap_save_complete, .has_postcopy = dirty_bitmap_has_postcopy, .state_pending_exact = dirty_bitmap_state_pending, .state_pending_estimate = dirty_bitmap_state_pending, diff --git a/migration/ram.c b/migration/ram.c index 6bb4f13031..ffd839f57c 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -4548,7 +4548,7 @@ void postcopy_preempt_shutdown_file(MigrationState *s) static SaveVMHandlers savevm_ram_handlers = { .save_setup = ram_save_setup, .save_live_iterate = ram_save_iterate, - .save_live_complete_precopy = ram_save_complete, + .save_complete = ram_save_complete, .has_postcopy = ram_has_postcopy, .state_pending_exact = ram_state_pending_exact, .state_pending_estimate = ram_state_pending_estimate, diff --git a/migration/savevm.c b/migration/savevm.c index 3e20f8608a..454e914b56 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1496,7 +1496,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) int ret; QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (!se->ops || !se->ops->save_live_complete_precopy) { + if (!se->ops || !se->ops->save_complete) { continue; } if (se->ops->is_active) { @@ -1509,7 +1509,7 @@ void qemu_savevm_state_complete_postcopy(QEMUFile *f) qemu_put_byte(f, QEMU_VM_SECTION_END); qemu_put_be32(f, se->section_id); - ret = se->ops->save_live_complete_precopy(f, se->opaque); + ret = se->ops->save_complete(f, se->opaque); trace_savevm_section_end(se->idstr, se->section_id, ret); save_section_footer(f, se); if (ret < 0) { @@ -1583,7 +1583,7 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) if (!se->ops || (in_postcopy && se->ops->has_postcopy && se->ops->has_postcopy(se->opaque)) || - !se->ops->save_live_complete_precopy) { + !se->ops->save_complete) { continue; } @@ -1598,7 +1598,7 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) save_section_header(f, se, QEMU_VM_SECTION_END); - ret = se->ops->save_live_complete_precopy(f, se->opaque); + ret = se->ops->save_complete(f, se->opaque); trace_savevm_section_end(se->idstr, se->section_id, ret); save_section_footer(f, se); if (ret < 0) { From 7dd95a963072e0e33addbf2c87b548a624ce66b6 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:07:57 -0400 Subject: [PATCH 2130/2760] migration: qemu_savevm_complete*() helpers Since we use the same save_complete() hook for both precopy and postcopy, add a set of helpers to invoke the hook() to dedup the code. Reviewed-by: Juraj Marcin Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613140801.474264-8-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/savevm.c | 78 ++++++++++++++++++++++++++-------------------- 1 file changed, 44 insertions(+), 34 deletions(-) diff --git a/migration/savevm.c b/migration/savevm.c index 454e914b56..c4fd5f5a5b 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1484,6 +1484,38 @@ bool should_send_vmdesc(void) return !machine->suppress_vmdesc; } +static bool qemu_savevm_complete_exists(SaveStateEntry *se) +{ + return se->ops && se->ops->save_complete; +} + +/* + * Invoke the ->save_complete() if necessary. + * Returns: 0 if skip the current SE or succeeded, <0 if error happened. + */ +static int qemu_savevm_complete(SaveStateEntry *se, QEMUFile *f) +{ + int ret; + + if (se->ops->is_active) { + if (!se->ops->is_active(se->opaque)) { + return 0; + } + } + + trace_savevm_section_start(se->idstr, se->section_id); + save_section_header(f, se, QEMU_VM_SECTION_END); + ret = se->ops->save_complete(f, se->opaque); + trace_savevm_section_end(se->idstr, se->section_id, ret); + save_section_footer(f, se); + + if (ret < 0) { + qemu_file_set_error(f, ret); + } + + return ret; +} + /* * Complete saving any postcopy-able devices. * @@ -1493,27 +1525,13 @@ bool should_send_vmdesc(void) void qemu_savevm_state_complete_postcopy(QEMUFile *f) { SaveStateEntry *se; - int ret; QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (!se->ops || !se->ops->save_complete) { + if (!qemu_savevm_complete_exists(se)) { continue; } - if (se->ops->is_active) { - if (!se->ops->is_active(se->opaque)) { - continue; - } - } - trace_savevm_section_start(se->idstr, se->section_id); - /* Section type */ - qemu_put_byte(f, QEMU_VM_SECTION_END); - qemu_put_be32(f, se->section_id); - ret = se->ops->save_complete(f, se->opaque); - trace_savevm_section_end(se->idstr, se->section_id, ret); - save_section_footer(f, se); - if (ret < 0) { - qemu_file_set_error(f, ret); + if (qemu_savevm_complete(se, f) < 0) { return; } } @@ -1559,7 +1577,6 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) { int64_t start_ts_each, end_ts_each; SaveStateEntry *se; - int ret; bool multifd_device_state = multifd_device_state_supported(); if (multifd_device_state) { @@ -1580,32 +1597,25 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) } QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - if (!se->ops || - (in_postcopy && se->ops->has_postcopy && - se->ops->has_postcopy(se->opaque)) || - !se->ops->save_complete) { + if (!qemu_savevm_complete_exists(se)) { continue; } - if (se->ops->is_active) { - if (!se->ops->is_active(se->opaque)) { - continue; - } + if (in_postcopy && se->ops->has_postcopy && + se->ops->has_postcopy(se->opaque)) { + /* + * If postcopy will start soon, and if the SE supports + * postcopy, then we can skip the SE for the postcopy phase. + */ + continue; } start_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); - trace_savevm_section_start(se->idstr, se->section_id); - - save_section_header(f, se, QEMU_VM_SECTION_END); - - ret = se->ops->save_complete(f, se->opaque); - trace_savevm_section_end(se->idstr, se->section_id, ret); - save_section_footer(f, se); - if (ret < 0) { - qemu_file_set_error(f, ret); + if (qemu_savevm_complete(se, f) < 0) { goto ret_fail_abort_threads; } end_ts_each = qemu_clock_get_us(QEMU_CLOCK_REALTIME); + trace_vmstate_downtime_save("iterable", se->idstr, se->instance_id, end_ts_each - start_ts_each); } From ff9dfc41d9e74651d565a3cad80dbaac3cde1eb9 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:07:58 -0400 Subject: [PATCH 2131/2760] migration/ram: One less indent for ram_find_and_save_block() The check over PAGE_DIRTY_FOUND isn't necessary. We could indent one less and assert that instead. Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250613140801.474264-9-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/ram.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/migration/ram.c b/migration/ram.c index ffd839f57c..aa76f0a53f 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -2286,16 +2286,18 @@ static int ram_find_and_save_block(RAMState *rs) if (!get_queued_page(rs, pss)) { /* priority queue empty, so just search for something dirty */ int res = find_dirty_block(rs, pss); - if (res != PAGE_DIRTY_FOUND) { - if (res == PAGE_ALL_CLEAN) { - break; - } else if (res == PAGE_TRY_AGAIN) { - continue; - } else if (res < 0) { - pages = res; - break; - } + + if (res == PAGE_ALL_CLEAN) { + break; + } else if (res == PAGE_TRY_AGAIN) { + continue; + } else if (res < 0) { + pages = res; + break; } + + /* Otherwise we must have a dirty page to move */ + assert(res == PAGE_DIRTY_FOUND); } pages = ram_save_host_page(rs, pss); if (pages) { From f1549da610dc3d37da4de0e3ba7374c5316af716 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:07:59 -0400 Subject: [PATCH 2132/2760] migration/ram: Add tracepoints for ram_save_complete() Take notes on start/end state of dirty pages for the whole system. Reviewed-by: Juraj Marcin Link: https://lore.kernel.org/r/20250613140801.474264-10-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/ram.c | 5 +++++ migration/trace-events | 1 + 2 files changed, 6 insertions(+) diff --git a/migration/ram.c b/migration/ram.c index aa76f0a53f..e4871b4c17 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -3290,6 +3290,8 @@ static int ram_save_complete(QEMUFile *f, void *opaque) RAMState *rs = *temp; int ret = 0; + trace_ram_save_complete(rs->migration_dirty_pages, 0); + rs->last_stage = !migration_in_colo_state(); WITH_RCU_READ_LOCK_GUARD() { @@ -3353,6 +3355,9 @@ static int ram_save_complete(QEMUFile *f, void *opaque) } qemu_put_be64(f, RAM_SAVE_FLAG_EOS); + + trace_ram_save_complete(rs->migration_dirty_pages, 1); + return qemu_fflush(f); } diff --git a/migration/trace-events b/migration/trace-events index c506e11a2e..dcd8fe9a0c 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -105,6 +105,7 @@ ram_load_postcopy_loop(int channel, uint64_t addr, int flags) "chan=%d addr=0x%" ram_postcopy_send_discard_bitmap(void) "" ram_save_page(const char *rbname, uint64_t offset, void *host) "%s: offset: 0x%" PRIx64 " host: %p" ram_save_queue_pages(const char *rbname, size_t start, size_t len) "%s: start: 0x%zx len: 0x%zx" +ram_save_complete(uint64_t dirty_pages, int done) "dirty=%" PRIu64 ", done=%d" ram_dirty_bitmap_request(char *str) "%s" ram_dirty_bitmap_reload_begin(char *str) "%s" ram_dirty_bitmap_reload_complete(char *str) "%s" From 7aaa1fc072ca7b9abf08c62504faa96126b583ce Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:08:00 -0400 Subject: [PATCH 2133/2760] migration: Rewrite the migration complete detect logic There're a few things off here in that logic, rewrite it. When at it, add rich comment to explain each of the decisions. Since this is very sensitive path for migration, below are the list of things changed with their reasonings. (1) Exact pending size is only needed for precopy not postcopy Fundamentally it's because "exact" version only does one more deep sync to fetch the pending results, while in postcopy's case it's never going to sync anything more than estimate as the VM on source is stopped. (2) Do _not_ rely on threshold_size anymore to decide whether postcopy should complete threshold_size was calculated from the expected downtime and bandwidth only during precopy as an efficient way to decide when to switchover. It's not sensible to rely on threshold_size in postcopy. For precopy, if switchover is decided, the migration will complete soon. It's not true for postcopy. Logically speaking, postcopy should only complete the migration if all pending data is flushed. Here it used to work because save_complete() used to implicitly contain save_live_iterate() when there's pending size. Even if that looks benign, having RAMs to be migrated in postcopy's save_complete() has other bad side effects: (a) Since save_complete() needs to be run once at a time, it means when moving RAM there's no way moving other things (rather than round-robin iterating the vmstate handlers like what we do with ITERABLE phase). Not an immediate concern, but it may stop working in the future when there're more than one iterables (e.g. vfio postcopy). (b) postcopy recovery, unfortunately, only works during ITERABLE phase. IOW, if src QEMU moves RAM during postcopy's save_complete() and network failed, then it'll crash both QEMUs... OTOH if it failed during iteration it'll still be recoverable. IOW, this change should further reduce the window QEMU split brain and crash in extreme cases. If we enable the ram_save_complete() tracepoints, we'll see this before this patch: 1267959@1748381938.294066:ram_save_complete dirty=9627, done=0 1267959@1748381938.308884:ram_save_complete dirty=0, done=1 It means in this migration there're 9627 pages migrated at complete() of postcopy phase. After this change, all the postcopy RAM should be migrated in iterable phase, rather than save_complete(): 1267959@1748381938.294066:ram_save_complete dirty=0, done=0 1267959@1748381938.308884:ram_save_complete dirty=0, done=1 (3) Adjust when to decide to switch to postcopy This shouldn't be super important, the movement makes sure there's only one in_postcopy check, then we are clear on what we do with the two completely differnt use cases (precopy v.s. postcopy). (4) Trivial touch up on threshold_size comparision Which changes: "(!pending_size || pending_size < s->threshold_size)" into: "(pending_size <= s->threshold_size)" Reviewed-by: Juraj Marcin Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613140801.474264-11-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration.c | 57 +++++++++++++++++++++++++++++++------------ 1 file changed, 42 insertions(+), 15 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index e33e39ac74..923400f801 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -3436,33 +3436,60 @@ static MigIterateState migration_iteration_run(MigrationState *s) Error *local_err = NULL; bool in_postcopy = s->state == MIGRATION_STATUS_POSTCOPY_ACTIVE; bool can_switchover = migration_can_switchover(s); + bool complete_ready; + /* Fast path - get the estimated amount of pending data */ qemu_savevm_state_pending_estimate(&must_precopy, &can_postcopy); pending_size = must_precopy + can_postcopy; trace_migrate_pending_estimate(pending_size, must_precopy, can_postcopy); - if (pending_size < s->threshold_size) { - qemu_savevm_state_pending_exact(&must_precopy, &can_postcopy); - pending_size = must_precopy + can_postcopy; - trace_migrate_pending_exact(pending_size, must_precopy, can_postcopy); + if (in_postcopy) { + /* + * Iterate in postcopy until all pending data flushed. Note that + * postcopy completion doesn't rely on can_switchover, because when + * POSTCOPY_ACTIVE it means switchover already happened. + */ + complete_ready = !pending_size; + } else { + /* + * Exact pending reporting is only needed for precopy. Taking RAM + * as example, there'll be no extra dirty information after + * postcopy started, so ESTIMATE should always match with EXACT + * during postcopy phase. + */ + if (pending_size < s->threshold_size) { + qemu_savevm_state_pending_exact(&must_precopy, &can_postcopy); + pending_size = must_precopy + can_postcopy; + trace_migrate_pending_exact(pending_size, must_precopy, + can_postcopy); + } + + /* Should we switch to postcopy now? */ + if (must_precopy <= s->threshold_size && + can_switchover && qatomic_read(&s->start_postcopy)) { + if (postcopy_start(s, &local_err)) { + migrate_set_error(s, local_err); + error_report_err(local_err); + } + return MIG_ITERATE_SKIP; + } + + /* + * For precopy, migration can complete only if: + * + * (1) Switchover is acknowledged by destination + * (2) Pending size is no more than the threshold specified + * (which was calculated from expected downtime) + */ + complete_ready = can_switchover && (pending_size <= s->threshold_size); } - if ((!pending_size || pending_size < s->threshold_size) && can_switchover) { + if (complete_ready) { trace_migration_thread_low_pending(pending_size); migration_completion(s); return MIG_ITERATE_BREAK; } - /* Still a significant amount to transfer */ - if (!in_postcopy && must_precopy <= s->threshold_size && can_switchover && - qatomic_read(&s->start_postcopy)) { - if (postcopy_start(s, &local_err)) { - migrate_set_error(s, local_err); - error_report_err(local_err); - } - return MIG_ITERATE_SKIP; - } - /* Just another iteration step */ qemu_savevm_state_iterate(s->to_dst_file, in_postcopy); return MIG_ITERATE_RESUME; From adb13d6e42d6e0084e19a41303138d5d022e5904 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:08:01 -0400 Subject: [PATCH 2134/2760] migration/postcopy: Avoid clearing dirty bitmap for postcopy too This is a follow up on the other commit "migration/ram: avoid to do log clear in the last round" but for postcopy. https://lore.kernel.org/r/20250514115827.3216082-1-yanfei.xu@bytedance.com I can observe more than 10% reduction of average page fault latency during postcopy phase with this optimization: Before: 268.00us (+-1.87%) After: 232.67us (+-2.01%) The test was done with a 16GB VM with 80 vCPUs, running a workload that busy random writes to 13GB memory. Cc: Yanfei Xu Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613140801.474264-12-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/ram.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/migration/ram.c b/migration/ram.c index e4871b4c17..7208bc114f 100644 --- a/migration/ram.c +++ b/migration/ram.c @@ -835,8 +835,10 @@ static inline bool migration_bitmap_clear_dirty(RAMState *rs, * protections isn't needed as we know there will be either (1) no * further writes if migration will complete, or (2) migration fails * at last then tracking isn't needed either. + * + * Do the same for postcopy due to the same reason. */ - if (!rs->last_stage) { + if (!rs->last_stage && !migration_in_postcopy()) { /* * Clear dirty bitmap if needed. This _must_ be called before we * send any of the page in the chunk because we need to make sure From f62b7a0a29192891f0139f2ad8009f55a41c8641 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:04 -0400 Subject: [PATCH 2135/2760] migration: Add option to set postcopy-blocktime Add a global property to allow enabling postcopy-blocktime feature. Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613141217.474825-2-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/options.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/migration/options.c b/migration/options.c index 162c72cda4..4e923a2e07 100644 --- a/migration/options.c +++ b/migration/options.c @@ -187,6 +187,8 @@ const Property migration_properties[] = { DEFINE_PROP_MIG_CAP("x-postcopy-ram", MIGRATION_CAPABILITY_POSTCOPY_RAM), DEFINE_PROP_MIG_CAP("x-postcopy-preempt", MIGRATION_CAPABILITY_POSTCOPY_PREEMPT), + DEFINE_PROP_MIG_CAP("postcopy-blocktime", + MIGRATION_CAPABILITY_POSTCOPY_BLOCKTIME), DEFINE_PROP_MIG_CAP("x-colo", MIGRATION_CAPABILITY_X_COLO), DEFINE_PROP_MIG_CAP("x-release-ram", MIGRATION_CAPABILITY_RELEASE_RAM), DEFINE_PROP_MIG_CAP("x-return-path", MIGRATION_CAPABILITY_RETURN_PATH), From d2a81ca8c6fbe6ed691889d953d0b5fe2c7e4671 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:05 -0400 Subject: [PATCH 2136/2760] migration/postcopy: Push blocktime start/end into page req mutex The postcopy blocktime feature was tricky that it used quite some atomic operations over quite a few arrays and vars, without explaining how that would be thread safe. The thread safety here is about concurrency between the fault thread and the fault resolution threads, possible to access the same chunk of data. All these atomic ops can be expensive too before knowing clearly how it works. OTOH, postcopy has one page_request_mutex used to serialize the received bitmap updates. So far it's ok - we don't yet have a lot of threads contending the lock. It might change after multifd will be supported, but that's a separate story. What is important is, with that mutex, it's pretty lightweight to move all the blocktime maintenance into the mutex critical section. It's because the blocktime layer is lightweighted: almost "remember which vcpu faulted on which address", and "ok we get some fault resolved, calculate how long it takes". It's also an optional feature for now (but I have thought of changing that, maybe in the future). Let's push the blocktime layer into the mutex, so that it's always thread-safe even without any atomic ops. To achieve that, I'll need to add a tid parameter on fault path so that it'll start to pass the faulted thread ID into deeper the stack, but not too deep. When at it, add a comment for the shared fault handler (for example, vhost-user devices running with postcopy), to mention a TODO. One reason it might not be trivial is that vhost-user's userfaultfds should be opened by vhost-user process, so it's pretty hard to control making sure the TID feature will be around. It wasn't supported before, so keep it like that for now. Now we should be as ease when everything is protected by a mutex that we always take anyway. One side effect: we can finally remove one ramblock_recv_bitmap_test() in mark_postcopy_blocktime_begin(), which was pretty weird and which also includes a weird (but maybe necessary.. but maybe not?) operation to inject a blocktime entry then quickly erase it.. When we're with the mutex, and when we make sure it's invoked after checking the receive bitmap, it's not needed anymore. Instead, we assert. As another side effect, this paves way for removing all atomic ops in all the mem accesses in blocktime layer. Note that we need a stub for mark_postcopy_blocktime_begin() for Windows builds. Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613141217.474825-3-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration.c | 25 +++++++++++------- migration/migration.h | 2 +- migration/postcopy-ram.c | 56 +++++++++++++++++++++------------------- migration/postcopy-ram.h | 2 ++ migration/trace-events | 2 +- 5 files changed, 48 insertions(+), 39 deletions(-) diff --git a/migration/migration.c b/migration/migration.c index 923400f801..10c216d25d 100644 --- a/migration/migration.c +++ b/migration/migration.c @@ -576,22 +576,27 @@ int migrate_send_rp_message_req_pages(MigrationIncomingState *mis, } int migrate_send_rp_req_pages(MigrationIncomingState *mis, - RAMBlock *rb, ram_addr_t start, uint64_t haddr) + RAMBlock *rb, ram_addr_t start, uint64_t haddr, + uint32_t tid) { void *aligned = (void *)(uintptr_t)ROUND_DOWN(haddr, qemu_ram_pagesize(rb)); bool received = false; WITH_QEMU_LOCK_GUARD(&mis->page_request_mutex) { received = ramblock_recv_bitmap_test_byte_offset(rb, start); - if (!received && !g_tree_lookup(mis->page_requested, aligned)) { - /* - * The page has not been received, and it's not yet in the page - * request list. Queue it. Set the value of element to 1, so that - * things like g_tree_lookup() will return TRUE (1) when found. - */ - g_tree_insert(mis->page_requested, aligned, (gpointer)1); - qatomic_inc(&mis->page_requested_count); - trace_postcopy_page_req_add(aligned, mis->page_requested_count); + if (!received) { + if (!g_tree_lookup(mis->page_requested, aligned)) { + /* + * The page has not been received, and it's not yet in the + * page request list. Queue it. Set the value of element + * to 1, so that things like g_tree_lookup() will return + * TRUE (1) when found. + */ + g_tree_insert(mis->page_requested, aligned, (gpointer)1); + qatomic_inc(&mis->page_requested_count); + trace_postcopy_page_req_add(aligned, mis->page_requested_count); + } + mark_postcopy_blocktime_begin(haddr, tid, rb); } } diff --git a/migration/migration.h b/migration/migration.h index 739289de93..01329bf824 100644 --- a/migration/migration.h +++ b/migration/migration.h @@ -546,7 +546,7 @@ void migrate_send_rp_shut(MigrationIncomingState *mis, void migrate_send_rp_pong(MigrationIncomingState *mis, uint32_t value); int migrate_send_rp_req_pages(MigrationIncomingState *mis, RAMBlock *rb, - ram_addr_t start, uint64_t haddr); + ram_addr_t start, uint64_t haddr, uint32_t tid); int migrate_send_rp_message_req_pages(MigrationIncomingState *mis, RAMBlock *rb, ram_addr_t start); void migrate_send_rp_recv_bitmap(MigrationIncomingState *mis, diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 75fd310fb2..32fa06dabd 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -752,8 +752,12 @@ int postcopy_wake_shared(struct PostCopyFD *pcfd, pagesize); } +/* + * NOTE: @tid is only used when postcopy-blocktime feature is enabled, and + * also optional: when zero is provided, the fault accounting will be ignored. + */ static int postcopy_request_page(MigrationIncomingState *mis, RAMBlock *rb, - ram_addr_t start, uint64_t haddr) + ram_addr_t start, uint64_t haddr, uint32_t tid) { void *aligned = (void *)(uintptr_t)ROUND_DOWN(haddr, qemu_ram_pagesize(rb)); @@ -772,7 +776,7 @@ static int postcopy_request_page(MigrationIncomingState *mis, RAMBlock *rb, return received ? 0 : postcopy_place_page_zero(mis, aligned, rb); } - return migrate_send_rp_req_pages(mis, rb, start, haddr); + return migrate_send_rp_req_pages(mis, rb, start, haddr, tid); } /* @@ -793,7 +797,8 @@ int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb, qemu_ram_get_idstr(rb), rb_offset); return postcopy_wake_shared(pcfd, client_addr, rb); } - postcopy_request_page(mis, rb, aligned_rbo, client_addr); + /* TODO: support blocktime tracking */ + postcopy_request_page(mis, rb, aligned_rbo, client_addr, 0); return 0; } @@ -819,17 +824,17 @@ static uint32_t get_low_time_offset(PostcopyBlocktimeContext *dc) } /* - * This function is being called when pagefault occurs. It - * tracks down vCPU blocking time. + * This function is being called when pagefault occurs. It tracks down vCPU + * blocking time. It's protected by @page_request_mutex. * * @addr: faulted host virtual address * @ptid: faulted process thread id * @rb: ramblock appropriate to addr */ -static void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, - RAMBlock *rb) +void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, + RAMBlock *rb) { - int cpu, already_received; + int cpu; MigrationIncomingState *mis = migration_incoming_get_current(); PostcopyBlocktimeContext *dc = mis->blocktime_ctx; uint32_t low_time_offset; @@ -852,24 +857,19 @@ static void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, qatomic_xchg(&dc->vcpu_addr[cpu], addr); /* - * check it here, not at the beginning of the function, - * due to, check could occur early than bitmap_set in - * qemu_ufd_copy_ioctl + * The caller should only inject a blocktime entry when the page is + * yet missing. */ - already_received = ramblock_recv_bitmap_test(rb, (void *)addr); - if (already_received) { - qatomic_xchg(&dc->vcpu_addr[cpu], 0); - qatomic_xchg(&dc->page_fault_vcpu_time[cpu], 0); - qatomic_dec(&dc->smp_cpus_down); - } + assert(!ramblock_recv_bitmap_test(rb, (void *)addr)); + trace_mark_postcopy_blocktime_begin(addr, dc, dc->page_fault_vcpu_time[cpu], - cpu, already_received); + cpu); } /* - * This function just provide calculated blocktime per cpu and trace it. - * Total blocktime is calculated in mark_postcopy_blocktime_end. - * + * This function just provide calculated blocktime per cpu and trace it. + * Total blocktime is calculated in mark_postcopy_blocktime_end. It's + * protected by @page_request_mutex. * * Assume we have 3 CPU * @@ -1068,17 +1068,14 @@ static void *postcopy_ram_fault_thread(void *opaque) qemu_ram_get_idstr(rb), rb_offset, msg.arg.pagefault.feat.ptid); - mark_postcopy_blocktime_begin( - (uintptr_t)(msg.arg.pagefault.address), - msg.arg.pagefault.feat.ptid, rb); - retry: /* * Send the request to the source - we want to request one * of our host page sizes (which is >= TPS) */ ret = postcopy_request_page(mis, rb, rb_offset, - msg.arg.pagefault.address); + msg.arg.pagefault.address, + msg.arg.pagefault.feat.ptid); if (ret) { /* May be network failure, try to wait for recovery */ postcopy_pause_fault_thread(mis); @@ -1299,8 +1296,8 @@ static int qemu_ufd_copy_ioctl(MigrationIncomingState *mis, void *host_addr, qemu_cond_signal(&mis->page_request_cond); } } - qemu_mutex_unlock(&mis->page_request_mutex); mark_postcopy_blocktime_end((uintptr_t)host_addr); + qemu_mutex_unlock(&mis->page_request_mutex); } return ret; } @@ -1430,6 +1427,11 @@ int postcopy_wake_shared(struct PostCopyFD *pcfd, { g_assert_not_reached(); } + +void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, + RAMBlock *rb) +{ +} #endif /* ------------------------------------------------------------------------- */ diff --git a/migration/postcopy-ram.h b/migration/postcopy-ram.h index a6df1b2811..3852141d7e 100644 --- a/migration/postcopy-ram.h +++ b/migration/postcopy-ram.h @@ -196,5 +196,7 @@ void postcopy_preempt_new_channel(MigrationIncomingState *mis, QEMUFile *file); void postcopy_preempt_setup(MigrationState *s); int postcopy_preempt_establish_channel(MigrationState *s); bool postcopy_is_paused(MigrationStatus status); +void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, + RAMBlock *rb); #endif diff --git a/migration/trace-events b/migration/trace-events index dcd8fe9a0c..917f521e88 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -285,7 +285,7 @@ postcopy_nhp_range(const char *ramblock, void *host_addr, size_t offset, size_t postcopy_place_page(void *host_addr) "host=%p" postcopy_place_page_zero(void *host_addr) "host=%p" postcopy_ram_enable_notify(void) "" -mark_postcopy_blocktime_begin(uint64_t addr, void *dd, uint32_t time, int cpu, int received) "addr: 0x%" PRIx64 ", dd: %p, time: %u, cpu: %d, already_received: %d" +mark_postcopy_blocktime_begin(uint64_t addr, void *dd, uint32_t time, int cpu) "addr: 0x%" PRIx64 ", dd: %p, time: %u, cpu: %d" mark_postcopy_blocktime_end(uint64_t addr, void *dd, uint32_t time, int affected_cpu) "addr: 0x%" PRIx64 ", dd: %p, time: %u, affected_cpu: %d" postcopy_pause_fault_thread(void) "" postcopy_pause_fault_thread_continued(void) "" From c0f47dfb5b06c40ef41641a5f03ebafa8125c557 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:06 -0400 Subject: [PATCH 2137/2760] migration/postcopy: Drop all atomic ops in blocktime feature Now with the mutex protection it's not needed anymore. Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613141217.474825-4-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/postcopy-ram.c | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 32fa06dabd..81925532de 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -849,12 +849,12 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, low_time_offset = get_low_time_offset(dc); if (dc->vcpu_addr[cpu] == 0) { - qatomic_inc(&dc->smp_cpus_down); + dc->smp_cpus_down++; } - qatomic_xchg(&dc->last_begin, low_time_offset); - qatomic_xchg(&dc->page_fault_vcpu_time[cpu], low_time_offset); - qatomic_xchg(&dc->vcpu_addr[cpu], addr); + dc->last_begin = low_time_offset; + dc->page_fault_vcpu_time[cpu] = low_time_offset; + dc->vcpu_addr[cpu] = addr; /* * The caller should only inject a blocktime entry when the page is @@ -915,29 +915,26 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) for (i = 0; i < smp_cpus; i++) { uint32_t vcpu_blocktime = 0; - read_vcpu_time = qatomic_fetch_add(&dc->page_fault_vcpu_time[i], 0); - if (qatomic_fetch_add(&dc->vcpu_addr[i], 0) != addr || - read_vcpu_time == 0) { + read_vcpu_time = dc->page_fault_vcpu_time[i]; + if (dc->vcpu_addr[i] != addr || read_vcpu_time == 0) { continue; } - qatomic_xchg(&dc->vcpu_addr[i], 0); + dc->vcpu_addr[i] = 0; vcpu_blocktime = low_time_offset - read_vcpu_time; affected_cpu += 1; /* we need to know is that mark_postcopy_end was due to * faulted page, another possible case it's prefetched * page and in that case we shouldn't be here */ - if (!vcpu_total_blocktime && - qatomic_fetch_add(&dc->smp_cpus_down, 0) == smp_cpus) { + if (!vcpu_total_blocktime && dc->smp_cpus_down == smp_cpus) { vcpu_total_blocktime = true; } /* continue cycle, due to one page could affect several vCPUs */ dc->vcpu_blocktime[i] += vcpu_blocktime; } - qatomic_sub(&dc->smp_cpus_down, affected_cpu); + dc->smp_cpus_down -= affected_cpu; if (vcpu_total_blocktime) { - dc->total_blocktime += low_time_offset - qatomic_fetch_add( - &dc->last_begin, 0); + dc->total_blocktime += low_time_offset - dc->last_begin; } trace_mark_postcopy_blocktime_end(addr, dc, dc->total_blocktime, affected_cpu); From b2819530e3134fb47c92c1bf0f3def8ea5b1c8ee Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:07 -0400 Subject: [PATCH 2138/2760] migration/postcopy: Make all blocktime vars 64bits I am guessing it was used to be 32bits because of the atomic ops. Now all the atomic ops are gone and we're protected by a mutex instead, it's ok we can switch to 64 bits. Reasons to move over: - Allow further patches to change the unit from ms to us: with postcopy preempt mode, we're really into hundreds of microseconds level on blocktime. We'd better be able to trap those. - This also paves way for some other tricks that the original version used to avoid overflows, e.g., start_time was almost only useful before to make sure the sampled timestamp won't overflow a 32-bit field. - This prepares further reports on top of existing data collected, e.g. average page fault latencies. When average operation is taken into account, milliseconds are simply too coarse grained. When at it: - Rename page_fault_vcpu_time to vcpu_blocktime_start. - Rename vcpu_blocktime to vcpu_blocktime_total. - Touch up the trace-events to not dump blocktime ctx pointer Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613141217.474825-5-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/postcopy-ram.c | 50 ++++++++++++++++++++-------------------- migration/trace-events | 4 ++-- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 81925532de..ec91821b85 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -112,14 +112,15 @@ void postcopy_thread_create(MigrationIncomingState *mis, typedef struct PostcopyBlocktimeContext { /* time when page fault initiated per vCPU */ - uint32_t *page_fault_vcpu_time; + uint64_t *vcpu_blocktime_start; + /* blocktime per vCPU */ + uint64_t *vcpu_blocktime_total; /* page address per vCPU */ uintptr_t *vcpu_addr; - uint32_t total_blocktime; - /* blocktime per vCPU */ - uint32_t *vcpu_blocktime; + /* total blocktime when all vCPUs are stopped */ + uint64_t total_blocktime; /* point in time when last page fault was initiated */ - uint32_t last_begin; + uint64_t last_begin; /* number of vCPU are suspended */ int smp_cpus_down; uint64_t start_time; @@ -133,9 +134,9 @@ typedef struct PostcopyBlocktimeContext { static void destroy_blocktime_context(struct PostcopyBlocktimeContext *ctx) { - g_free(ctx->page_fault_vcpu_time); + g_free(ctx->vcpu_blocktime_start); + g_free(ctx->vcpu_blocktime_total); g_free(ctx->vcpu_addr); - g_free(ctx->vcpu_blocktime); g_free(ctx); } @@ -151,13 +152,14 @@ static struct PostcopyBlocktimeContext *blocktime_context_new(void) MachineState *ms = MACHINE(qdev_get_machine()); unsigned int smp_cpus = ms->smp.cpus; PostcopyBlocktimeContext *ctx = g_new0(PostcopyBlocktimeContext, 1); - ctx->page_fault_vcpu_time = g_new0(uint32_t, smp_cpus); - ctx->vcpu_addr = g_new0(uintptr_t, smp_cpus); - ctx->vcpu_blocktime = g_new0(uint32_t, smp_cpus); + ctx->vcpu_blocktime_start = g_new0(uint64_t, smp_cpus); + ctx->vcpu_blocktime_total = g_new0(uint64_t, smp_cpus); + ctx->vcpu_addr = g_new0(uintptr_t, smp_cpus); ctx->exit_notifier.notify = migration_exit_cb; ctx->start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); qemu_add_exit_notifier(&ctx->exit_notifier); + return ctx; } @@ -168,7 +170,7 @@ static uint32List *get_vcpu_blocktime_list(PostcopyBlocktimeContext *ctx) int i; for (i = ms->smp.cpus - 1; i >= 0; i--) { - QAPI_LIST_PREPEND(list, ctx->vcpu_blocktime[i]); + QAPI_LIST_PREPEND(list, (uint32_t)ctx->vcpu_blocktime_total[i]); } return list; @@ -191,12 +193,12 @@ void fill_destination_postcopy_migration_info(MigrationInfo *info) } info->has_postcopy_blocktime = true; - info->postcopy_blocktime = bc->total_blocktime; + info->postcopy_blocktime = (uint32_t)bc->total_blocktime; info->has_postcopy_vcpu_blocktime = true; info->postcopy_vcpu_blocktime = get_vcpu_blocktime_list(bc); } -static uint32_t get_postcopy_total_blocktime(void) +static uint64_t get_postcopy_total_blocktime(void) { MigrationIncomingState *mis = migration_incoming_get_current(); PostcopyBlocktimeContext *bc = mis->blocktime_ctx; @@ -816,11 +818,9 @@ static int get_mem_fault_cpu_index(uint32_t pid) return -1; } -static uint32_t get_low_time_offset(PostcopyBlocktimeContext *dc) +static uint64_t get_low_time_offset(PostcopyBlocktimeContext *dc) { - int64_t start_time_offset = qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - - dc->start_time; - return start_time_offset < 1 ? 1 : start_time_offset & UINT32_MAX; + return (uint64_t)qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - dc->start_time; } /* @@ -837,7 +837,7 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, int cpu; MigrationIncomingState *mis = migration_incoming_get_current(); PostcopyBlocktimeContext *dc = mis->blocktime_ctx; - uint32_t low_time_offset; + uint64_t low_time_offset; if (!dc || ptid == 0) { return; @@ -853,7 +853,7 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, } dc->last_begin = low_time_offset; - dc->page_fault_vcpu_time[cpu] = low_time_offset; + dc->vcpu_blocktime_start[cpu] = low_time_offset; dc->vcpu_addr[cpu] = addr; /* @@ -862,7 +862,7 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, */ assert(!ramblock_recv_bitmap_test(rb, (void *)addr)); - trace_mark_postcopy_blocktime_begin(addr, dc, dc->page_fault_vcpu_time[cpu], + trace_mark_postcopy_blocktime_begin(addr, dc->vcpu_blocktime_start[cpu], cpu); } @@ -901,7 +901,7 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) unsigned int smp_cpus = ms->smp.cpus; int i, affected_cpu = 0; bool vcpu_total_blocktime = false; - uint32_t read_vcpu_time, low_time_offset; + uint64_t read_vcpu_time, low_time_offset; if (!dc) { return; @@ -913,9 +913,9 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) * optimal, more optimal algorithm is keeping tree or hash * where key is address value is a list of */ for (i = 0; i < smp_cpus; i++) { - uint32_t vcpu_blocktime = 0; + uint64_t vcpu_blocktime = 0; - read_vcpu_time = dc->page_fault_vcpu_time[i]; + read_vcpu_time = dc->vcpu_blocktime_start[i]; if (dc->vcpu_addr[i] != addr || read_vcpu_time == 0) { continue; } @@ -929,14 +929,14 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) vcpu_total_blocktime = true; } /* continue cycle, due to one page could affect several vCPUs */ - dc->vcpu_blocktime[i] += vcpu_blocktime; + dc->vcpu_blocktime_total[i] += vcpu_blocktime; } dc->smp_cpus_down -= affected_cpu; if (vcpu_total_blocktime) { dc->total_blocktime += low_time_offset - dc->last_begin; } - trace_mark_postcopy_blocktime_end(addr, dc, dc->total_blocktime, + trace_mark_postcopy_blocktime_end(addr, dc->total_blocktime, affected_cpu); } diff --git a/migration/trace-events b/migration/trace-events index 917f521e88..02cdb6e7cc 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -285,8 +285,8 @@ postcopy_nhp_range(const char *ramblock, void *host_addr, size_t offset, size_t postcopy_place_page(void *host_addr) "host=%p" postcopy_place_page_zero(void *host_addr) "host=%p" postcopy_ram_enable_notify(void) "" -mark_postcopy_blocktime_begin(uint64_t addr, void *dd, uint32_t time, int cpu) "addr: 0x%" PRIx64 ", dd: %p, time: %u, cpu: %d" -mark_postcopy_blocktime_end(uint64_t addr, void *dd, uint32_t time, int affected_cpu) "addr: 0x%" PRIx64 ", dd: %p, time: %u, affected_cpu: %d" +mark_postcopy_blocktime_begin(uint64_t addr, uint64_t time, int cpu) "addr: 0x%" PRIx64 ", time: %" PRIu64 ", cpu: %d" +mark_postcopy_blocktime_end(uint64_t addr, uint64_t time, int affected_cpu) "addr: 0x%" PRIx64 ", time: %" PRIu64 ", affected_cpus: %d" postcopy_pause_fault_thread(void) "" postcopy_pause_fault_thread_continued(void) "" postcopy_pause_fast_load(void) "" From 08fb2a933586183be788aac43c62b2993e0a99ce Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:08 -0400 Subject: [PATCH 2139/2760] migration/postcopy: Drop PostcopyBlocktimeContext.start_time Now with 64bits, the offseting using start_time is not needed anymore, because the array can always remember the whole timestamp. Then drop the unused parameter in get_low_time_offset() altogether. Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613141217.474825-6-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/postcopy-ram.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index ec91821b85..e9acb4ef6e 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -123,7 +123,6 @@ typedef struct PostcopyBlocktimeContext { uint64_t last_begin; /* number of vCPU are suspended */ int smp_cpus_down; - uint64_t start_time; /* * Handler for exit event, necessary for @@ -157,7 +156,6 @@ static struct PostcopyBlocktimeContext *blocktime_context_new(void) ctx->vcpu_blocktime_total = g_new0(uint64_t, smp_cpus); ctx->vcpu_addr = g_new0(uintptr_t, smp_cpus); ctx->exit_notifier.notify = migration_exit_cb; - ctx->start_time = qemu_clock_get_ms(QEMU_CLOCK_REALTIME); qemu_add_exit_notifier(&ctx->exit_notifier); return ctx; @@ -818,9 +816,9 @@ static int get_mem_fault_cpu_index(uint32_t pid) return -1; } -static uint64_t get_low_time_offset(PostcopyBlocktimeContext *dc) +static uint64_t get_low_time_offset(void) { - return (uint64_t)qemu_clock_get_ms(QEMU_CLOCK_REALTIME) - dc->start_time; + return (uint64_t)qemu_clock_get_ms(QEMU_CLOCK_REALTIME); } /* @@ -847,7 +845,7 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, return; } - low_time_offset = get_low_time_offset(dc); + low_time_offset = get_low_time_offset(); if (dc->vcpu_addr[cpu] == 0) { dc->smp_cpus_down++; } @@ -907,7 +905,7 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) return; } - low_time_offset = get_low_time_offset(dc); + low_time_offset = get_low_time_offset(); /* lookup cpu, to clear it, * that algorithm looks straightforward, but it's not * optimal, more optimal algorithm is keeping tree or hash From a098761f63e019b75a23575bb8d5a520c0dbce64 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:09 -0400 Subject: [PATCH 2140/2760] migration/postcopy: Bring blocktime layer to ns level With 64-bit fields, it is trivial. The caution is when exposing any values in QMP, it was still declared with milliseconds (ms). Hence it's needed to do the convertion when exporting the values to existing QMP queries. Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613141217.474825-7-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/postcopy-ram.c | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index e9acb4ef6e..9dfa92a62d 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -110,6 +110,7 @@ void postcopy_thread_create(MigrationIncomingState *mis, #include #include +/* All the time records are in unit of nanoseconds */ typedef struct PostcopyBlocktimeContext { /* time when page fault initiated per vCPU */ uint64_t *vcpu_blocktime_start; @@ -168,7 +169,9 @@ static uint32List *get_vcpu_blocktime_list(PostcopyBlocktimeContext *ctx) int i; for (i = ms->smp.cpus - 1; i >= 0; i--) { - QAPI_LIST_PREPEND(list, (uint32_t)ctx->vcpu_blocktime_total[i]); + /* Convert ns -> ms */ + QAPI_LIST_PREPEND( + list, (uint32_t)(ctx->vcpu_blocktime_total[i] / SCALE_MS)); } return list; @@ -191,7 +194,8 @@ void fill_destination_postcopy_migration_info(MigrationInfo *info) } info->has_postcopy_blocktime = true; - info->postcopy_blocktime = (uint32_t)bc->total_blocktime; + /* Convert ns -> ms */ + info->postcopy_blocktime = (uint32_t)(bc->total_blocktime / SCALE_MS); info->has_postcopy_vcpu_blocktime = true; info->postcopy_vcpu_blocktime = get_vcpu_blocktime_list(bc); } @@ -816,9 +820,9 @@ static int get_mem_fault_cpu_index(uint32_t pid) return -1; } -static uint64_t get_low_time_offset(void) +static uint64_t get_current_ns(void) { - return (uint64_t)qemu_clock_get_ms(QEMU_CLOCK_REALTIME); + return (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_REALTIME); } /* @@ -835,7 +839,7 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, int cpu; MigrationIncomingState *mis = migration_incoming_get_current(); PostcopyBlocktimeContext *dc = mis->blocktime_ctx; - uint64_t low_time_offset; + uint64_t current; if (!dc || ptid == 0) { return; @@ -845,13 +849,13 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, return; } - low_time_offset = get_low_time_offset(); + current = get_current_ns(); if (dc->vcpu_addr[cpu] == 0) { dc->smp_cpus_down++; } - dc->last_begin = low_time_offset; - dc->vcpu_blocktime_start[cpu] = low_time_offset; + dc->last_begin = current; + dc->vcpu_blocktime_start[cpu] = current; dc->vcpu_addr[cpu] = addr; /* @@ -899,13 +903,13 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) unsigned int smp_cpus = ms->smp.cpus; int i, affected_cpu = 0; bool vcpu_total_blocktime = false; - uint64_t read_vcpu_time, low_time_offset; + uint64_t read_vcpu_time, current; if (!dc) { return; } - low_time_offset = get_low_time_offset(); + current = get_current_ns(); /* lookup cpu, to clear it, * that algorithm looks straightforward, but it's not * optimal, more optimal algorithm is keeping tree or hash @@ -918,7 +922,7 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) continue; } dc->vcpu_addr[i] = 0; - vcpu_blocktime = low_time_offset - read_vcpu_time; + vcpu_blocktime = current - read_vcpu_time; affected_cpu += 1; /* we need to know is that mark_postcopy_end was due to * faulted page, another possible case it's prefetched @@ -932,7 +936,7 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) dc->smp_cpus_down -= affected_cpu; if (vcpu_total_blocktime) { - dc->total_blocktime += low_time_offset - dc->last_begin; + dc->total_blocktime += current - dc->last_begin; } trace_mark_postcopy_blocktime_end(addr, dc->total_blocktime, affected_cpu); From 271a1940e91a32fab6165841279f250204f53ae4 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:10 -0400 Subject: [PATCH 2141/2760] migration/postcopy: Add blocktime fault counts per-vcpu Add a field to count how many remote faults one vCPU has taken. So far it's still not used, but will be soon. Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613141217.474825-8-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/postcopy-ram.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 9dfa92a62d..15ea106910 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -116,6 +116,8 @@ typedef struct PostcopyBlocktimeContext { uint64_t *vcpu_blocktime_start; /* blocktime per vCPU */ uint64_t *vcpu_blocktime_total; + /* count of faults per vCPU */ + uint64_t *vcpu_faults_count; /* page address per vCPU */ uintptr_t *vcpu_addr; /* total blocktime when all vCPUs are stopped */ @@ -136,6 +138,7 @@ static void destroy_blocktime_context(struct PostcopyBlocktimeContext *ctx) { g_free(ctx->vcpu_blocktime_start); g_free(ctx->vcpu_blocktime_total); + g_free(ctx->vcpu_faults_count); g_free(ctx->vcpu_addr); g_free(ctx); } @@ -155,6 +158,7 @@ static struct PostcopyBlocktimeContext *blocktime_context_new(void) ctx->vcpu_blocktime_start = g_new0(uint64_t, smp_cpus); ctx->vcpu_blocktime_total = g_new0(uint64_t, smp_cpus); + ctx->vcpu_faults_count = g_new0(uint64_t, smp_cpus); ctx->vcpu_addr = g_new0(uintptr_t, smp_cpus); ctx->exit_notifier.notify = migration_exit_cb; qemu_add_exit_notifier(&ctx->exit_notifier); @@ -857,6 +861,7 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, dc->last_begin = current; dc->vcpu_blocktime_start[cpu] = current; dc->vcpu_addr[cpu] = addr; + dc->vcpu_faults_count[cpu]++; /* * The caller should only inject a blocktime entry when the page is From b4c82b428828c0ffff273a49f24a22cb4e18d485 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:11 -0400 Subject: [PATCH 2142/2760] migration/postcopy: Report fault latencies in blocktime Blocktime so far only cares about the time one vcpu (or the whole system) got blocked. It would be also be helpful if it can also report the latency of page requests, which could be very sensitive during postcopy. Blocktime itself is sometimes not very important, especially when one thinks about KVM async PF support, which means vCPUs are literally almost not blocked at all because the guest OS is smart enough to switch to another task when a remote fault is needed. However, latency is still sensitive and important because even if the guest vCPU is running on threads that do not need a remote fault, the workload that accesses some missing page is still affected. Add two entries to the report, showing how long it takes to resolve a remote fault. Mention in the QAPI doc that this is not the real average fault latency, but only the ones that was requested for a remote fault. Unwrap get_vcpu_blocktime_list() so we don't need to walk the list twice, meanwhile add the entry checks in qtests for all postcopy tests. Cc: Markus Armbruster Cc: Dr. David Alan Gilbert Reviewed-by: Fabiano Rosas Tested-by: Mario Casquero Link: https://lore.kernel.org/r/20250613141217.474825-9-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration-hmp-cmds.c | 67 ++++++++++++++++++--------- migration/postcopy-ram.c | 49 +++++++++++++------- qapi/migration.json | 20 ++++++++ tests/qtest/migration/migration-qmp.c | 3 ++ 4 files changed, 102 insertions(+), 37 deletions(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 867e017b32..8b3846dab5 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -52,6 +52,51 @@ static void migration_global_dump(Monitor *mon) ms->clear_bitmap_shift); } +static void migration_dump_blocktime(Monitor *mon, MigrationInfo *info) +{ + if (info->has_postcopy_blocktime) { + monitor_printf(mon, "Postcopy Blocktime (ms): %" PRIu32 "\n", + info->postcopy_blocktime); + } + + if (info->has_postcopy_vcpu_blocktime) { + uint32List *item = info->postcopy_vcpu_blocktime; + const char *sep = ""; + int count = 0; + + monitor_printf(mon, "Postcopy vCPU Blocktime (ms):\n ["); + + while (item) { + monitor_printf(mon, "%s%"PRIu32, sep, item->value); + item = item->next; + /* Each line 10 vcpu results, newline if there's more */ + sep = ((++count % 10 == 0) && item) ? ",\n " : ", "; + } + monitor_printf(mon, "]\n"); + } + + if (info->has_postcopy_latency) { + monitor_printf(mon, "Postcopy Latency (ns): %" PRIu64 "\n", + info->postcopy_latency); + } + + if (info->has_postcopy_vcpu_latency) { + uint64List *item = info->postcopy_vcpu_latency; + const char *sep = ""; + int count = 0; + + monitor_printf(mon, "Postcopy vCPU Latencies (ns):\n ["); + + while (item) { + monitor_printf(mon, "%s%"PRIu64, sep, item->value); + item = item->next; + /* Each line 10 vcpu results, newline if there's more */ + sep = ((++count % 10 == 0) && item) ? ",\n " : ", "; + } + monitor_printf(mon, "]\n"); + } +} + void hmp_info_migrate(Monitor *mon, const QDict *qdict) { bool show_all = qdict_get_try_bool(qdict, "all", false); @@ -202,27 +247,7 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) info->dirty_limit_ring_full_time); } - if (info->has_postcopy_blocktime) { - monitor_printf(mon, "Postcopy Blocktime (ms): %" PRIu32 "\n", - info->postcopy_blocktime); - } - - if (info->has_postcopy_vcpu_blocktime) { - uint32List *item = info->postcopy_vcpu_blocktime; - const char *sep = ""; - int count = 0; - - monitor_printf(mon, "Postcopy vCPU Blocktime (ms):\n ["); - - while (item) { - monitor_printf(mon, "%s%"PRIu32, sep, item->value); - item = item->next; - /* Each line 10 vcpu results, newline if there's more */ - sep = ((++count % 10 == 0) && item) ? ",\n " : ", "; - } - monitor_printf(mon, "]\n"); - } - + migration_dump_blocktime(mon, info); out: qapi_free_MigrationInfo(info); } diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 15ea106910..fe940f89b9 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -166,21 +166,6 @@ static struct PostcopyBlocktimeContext *blocktime_context_new(void) return ctx; } -static uint32List *get_vcpu_blocktime_list(PostcopyBlocktimeContext *ctx) -{ - MachineState *ms = MACHINE(qdev_get_machine()); - uint32List *list = NULL; - int i; - - for (i = ms->smp.cpus - 1; i >= 0; i--) { - /* Convert ns -> ms */ - QAPI_LIST_PREPEND( - list, (uint32_t)(ctx->vcpu_blocktime_total[i] / SCALE_MS)); - } - - return list; -} - /* * This function just populates MigrationInfo from postcopy's * blocktime context. It will not populate MigrationInfo, @@ -192,16 +177,48 @@ void fill_destination_postcopy_migration_info(MigrationInfo *info) { MigrationIncomingState *mis = migration_incoming_get_current(); PostcopyBlocktimeContext *bc = mis->blocktime_ctx; + MachineState *ms = MACHINE(qdev_get_machine()); + uint64_t latency_total = 0, faults = 0; + uint32List *list_blocktime = NULL; + uint64List *list_latency = NULL; + int i; if (!bc) { return; } + for (i = ms->smp.cpus - 1; i >= 0; i--) { + uint64_t latency, total, count; + + /* Convert ns -> ms */ + QAPI_LIST_PREPEND(list_blocktime, + (uint32_t)(bc->vcpu_blocktime_total[i] / SCALE_MS)); + + /* The rest in nanoseconds */ + total = bc->vcpu_blocktime_total[i]; + latency_total += total; + count = bc->vcpu_faults_count[i]; + faults += count; + + if (count) { + latency = total / count; + } else { + /* No fault detected */ + latency = 0; + } + + QAPI_LIST_PREPEND(list_latency, latency); + } + info->has_postcopy_blocktime = true; /* Convert ns -> ms */ info->postcopy_blocktime = (uint32_t)(bc->total_blocktime / SCALE_MS); info->has_postcopy_vcpu_blocktime = true; - info->postcopy_vcpu_blocktime = get_vcpu_blocktime_list(bc); + info->postcopy_vcpu_blocktime = list_blocktime; + info->has_postcopy_latency = true; + info->postcopy_latency = faults ? (latency_total / faults) : 0; + info->has_postcopy_vcpu_latency = true; + info->postcopy_vcpu_latency = list_latency; } static uint64_t get_postcopy_total_blocktime(void) diff --git a/qapi/migration.json b/qapi/migration.json index e8a7d3b2a9..bb41dc0795 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -236,6 +236,17 @@ # This is only present when the postcopy-blocktime migration # capability is enabled. (Since 3.0) # +# @postcopy-latency: average remote page fault latency (in ns). Note that +# this doesn't include all faults, but only the ones that require a +# remote page request. So it should be always bigger than the real +# average page fault latency. This is only present when the +# postcopy-blocktime migration capability is enabled. (Since 10.1) +# +# @postcopy-vcpu-latency: average remote page fault latency per vCPU (in +# ns). It has the same definition of @postcopy-latency, but instead +# this is the per-vCPU statistics. This is only present when the +# postcopy-blocktime migration capability is enabled. (Since 10.1) +# # @socket-address: Only used for tcp, to know what the real port is # (Since 4.0) # @@ -260,6 +271,11 @@ # average memory load of the virtual CPU indirectly. Note that # zero means guest doesn't dirty memory. (Since 8.1) # +# Features: +# +# @unstable: Members @postcopy-latency, @postcopy-vcpu-latency are +# experimental. +# # Since: 0.14 ## { 'struct': 'MigrationInfo', @@ -275,6 +291,10 @@ '*blocked-reasons': ['str'], '*postcopy-blocktime': 'uint32', '*postcopy-vcpu-blocktime': ['uint32'], + '*postcopy-latency': { + 'type': 'uint64', 'features': [ 'unstable' ] }, + '*postcopy-vcpu-latency': { + 'type': ['uint64'], 'features': [ 'unstable' ] }, '*socket-address': ['SocketAddress'], '*dirty-limit-throttle-time-per-round': 'uint64', '*dirty-limit-ring-full-time': 'uint64'} } diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c index fb59741b2c..1a5ab2d229 100644 --- a/tests/qtest/migration/migration-qmp.c +++ b/tests/qtest/migration/migration-qmp.c @@ -358,6 +358,9 @@ void read_blocktime(QTestState *who) rsp_return = migrate_query_not_failed(who); g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); + g_assert(qdict_haskey(rsp_return, "postcopy-vcpu-blocktime")); + g_assert(qdict_haskey(rsp_return, "postcopy-latency")); + g_assert(qdict_haskey(rsp_return, "postcopy-vcpu-latency")); qobject_unref(rsp_return); } From f07f2a3092b70d407a009dae28b44ecc8fbcffb7 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:12 -0400 Subject: [PATCH 2143/2760] migration/postcopy: Initialize blocktime context only until listen Before this patch, the blocktime context can be created very early, because postcopy_ram_supported_by_host() <- migrate_caps_check() can happen during migration object init. The trick here is the blocktime context needs system vCPU information, which seems to be possible to change after that point. I didn't verify it, but it doesn't sound right. Now move it out and initialize the context only when postcopy listen starts. That is already during a migration so it should be guaranteed the vCPU topology can never change on both sides. While at it, assert that the ctx isn't created instead this time; the old "if" trick isn't needed when we're sure it will only happen once now. Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613141217.474825-10-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/postcopy-ram.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index fe940f89b9..dd3615663f 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -325,13 +325,13 @@ static bool ufd_check_and_apply(int ufd, MigrationIncomingState *mis, } #ifdef UFFD_FEATURE_THREAD_ID + /* + * Postcopy blocktime conditionally needs THREAD_ID feature (introduced + * to Linux in 2017). Always try to enable it when QEMU is compiled + * with such environment. + */ if (UFFD_FEATURE_THREAD_ID & supported_features) { asked_features |= UFFD_FEATURE_THREAD_ID; - if (migrate_postcopy_blocktime()) { - if (!mis->blocktime_ctx) { - mis->blocktime_ctx = blocktime_context_new(); - } - } } #endif @@ -1239,6 +1239,11 @@ int postcopy_ram_incoming_setup(MigrationIncomingState *mis) return -1; } + if (migrate_postcopy_blocktime()) { + assert(mis->blocktime_ctx == NULL); + mis->blocktime_ctx = blocktime_context_new(); + } + /* Now an eventfd we use to tell the fault-thread to quit */ mis->userfault_event_fd = eventfd(0, EFD_CLOEXEC); if (mis->userfault_event_fd == -1) { From 28a185204ee9a4dd1b0da38c92f2d9326ca590d5 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:13 -0400 Subject: [PATCH 2144/2760] migration/postcopy: Cache the tid->vcpu mapping for blocktime Looking up the vCPU index for each fault can be expensive when there're hundreds of vCPUs. Provide a cache for tid->vcpu instead with a hash table, then lookup from there. When at it, add another counter to record how many non-vCPU faults it gets. For example, the main thread can also access a guest page that was missing. These kind of faults are not accounted by blocktime so far. Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613141217.474825-11-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/postcopy-ram.c | 68 ++++++++++++++++++++++++++++++++++------ migration/trace-events | 3 +- 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index dd3615663f..bf65d6035c 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -127,6 +127,17 @@ typedef struct PostcopyBlocktimeContext { /* number of vCPU are suspended */ int smp_cpus_down; + /* + * Fast path for looking up vcpu_index from tid. NOTE: this result + * only reflects the vcpu setup when postcopy is running. It may not + * always match with the current vcpu setup because vcpus can be hot + * attached/detached after migration completes. However this should be + * stable when blocktime is using the structure. + */ + GHashTable *tid_to_vcpu_hash; + /* Count of non-vCPU faults. This is only for debugging purpose. */ + uint64_t non_vcpu_faults; + /* * Handler for exit event, necessary for * releasing whole blocktime_ctx @@ -136,6 +147,7 @@ typedef struct PostcopyBlocktimeContext { static void destroy_blocktime_context(struct PostcopyBlocktimeContext *ctx) { + g_hash_table_destroy(ctx->tid_to_vcpu_hash); g_free(ctx->vcpu_blocktime_start); g_free(ctx->vcpu_blocktime_total); g_free(ctx->vcpu_faults_count); @@ -150,6 +162,36 @@ static void migration_exit_cb(Notifier *n, void *data) destroy_blocktime_context(ctx); } +static GHashTable *blocktime_init_tid_to_vcpu_hash(void) +{ + /* + * TID as an unsigned int can be directly used as the key. However, + * CPU index can NOT be directly used as value, because CPU index can + * be 0, which means NULL. Then when lookup we can never know whether + * it's 0 or "not found". Hence use an indirection for CPU index. + */ + GHashTable *table = g_hash_table_new_full(g_direct_hash, g_direct_equal, + NULL, g_free); + CPUState *cpu; + + /* + * Initialize the tid->cpu_id mapping for lookups. The caller needs to + * make sure when reaching here the CPU topology is frozen and will be + * stable for the whole blocktime trapping period. + */ + CPU_FOREACH(cpu) { + int *value = g_new(int, 1); + + *value = cpu->cpu_index; + g_hash_table_insert(table, + GUINT_TO_POINTER((uint32_t)cpu->thread_id), + value); + trace_postcopy_blocktime_tid_cpu_map(cpu->cpu_index, cpu->thread_id); + } + + return table; +} + static struct PostcopyBlocktimeContext *blocktime_context_new(void) { MachineState *ms = MACHINE(qdev_get_machine()); @@ -160,6 +202,8 @@ static struct PostcopyBlocktimeContext *blocktime_context_new(void) ctx->vcpu_blocktime_total = g_new0(uint64_t, smp_cpus); ctx->vcpu_faults_count = g_new0(uint64_t, smp_cpus); ctx->vcpu_addr = g_new0(uintptr_t, smp_cpus); + ctx->tid_to_vcpu_hash = blocktime_init_tid_to_vcpu_hash(); + ctx->exit_notifier.notify = migration_exit_cb; qemu_add_exit_notifier(&ctx->exit_notifier); @@ -827,18 +871,21 @@ int postcopy_request_shared_page(struct PostCopyFD *pcfd, RAMBlock *rb, return 0; } -static int get_mem_fault_cpu_index(uint32_t pid) +static int blocktime_get_vcpu(PostcopyBlocktimeContext *ctx, uint32_t tid) { - CPUState *cpu_iter; + int *found; - CPU_FOREACH(cpu_iter) { - if (cpu_iter->thread_id == pid) { - trace_get_mem_fault_cpu_index(cpu_iter->cpu_index, pid); - return cpu_iter->cpu_index; - } + found = g_hash_table_lookup(ctx->tid_to_vcpu_hash, GUINT_TO_POINTER(tid)); + if (!found) { + /* + * NOTE: this is possible, because QEMU's non-vCPU threads can + * also access a missing page. Or, when KVM async pf is enabled, a + * fault can even happen from a kworker.. + */ + return -1; } - trace_get_mem_fault_cpu_index(-1, pid); - return -1; + + return *found; } static uint64_t get_current_ns(void) @@ -865,8 +912,9 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, if (!dc || ptid == 0) { return; } - cpu = get_mem_fault_cpu_index(ptid); + cpu = blocktime_get_vcpu(dc, ptid); if (cpu < 0) { + dc->non_vcpu_faults++; return; } diff --git a/migration/trace-events b/migration/trace-events index 02cdb6e7cc..9c1f3b7044 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -310,8 +310,7 @@ postcopy_preempt_tls_handshake(void) "" postcopy_preempt_new_channel(void) "" postcopy_preempt_thread_entry(void) "" postcopy_preempt_thread_exit(void) "" - -get_mem_fault_cpu_index(int cpu, uint32_t pid) "cpu: %d, pid: %u" +postcopy_blocktime_tid_cpu_map(int cpu, uint32_t tid) "cpu: %d, tid: %u" # exec.c migration_exec_outgoing(const char *cmd) "cmd=%s" From 4c8a1194852844a1fc07af804579c7cf997e5c4a Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:14 -0400 Subject: [PATCH 2145/2760] migration/postcopy: Cleanup the total blocktime accounting The variable vcpu_total_blocktime isn't easy to follow. In reality, it wants to capture the case where all vCPUs are stopped, and now there will be some vCPUs starts running. The name now starts to conflict with vcpu_blocktime_total[], meanwhile it's actually not necessary to have the variable at all: since nobody is touching smp_cpus_down except ourselves, we can safely do the calculation at the end before decrementing smp_cpus_down. Hopefully this makes the logic easier to read, side benefit is we drop one temp var. Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613141217.474825-12-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/postcopy-ram.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index bf65d6035c..fd6c0bdb1e 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -972,7 +972,6 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) MachineState *ms = MACHINE(qdev_get_machine()); unsigned int smp_cpus = ms->smp.cpus; int i, affected_cpu = 0; - bool vcpu_total_blocktime = false; uint64_t read_vcpu_time, current; if (!dc) { @@ -994,20 +993,19 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) dc->vcpu_addr[i] = 0; vcpu_blocktime = current - read_vcpu_time; affected_cpu += 1; - /* we need to know is that mark_postcopy_end was due to - * faulted page, another possible case it's prefetched - * page and in that case we shouldn't be here */ - if (!vcpu_total_blocktime && dc->smp_cpus_down == smp_cpus) { - vcpu_total_blocktime = true; - } /* continue cycle, due to one page could affect several vCPUs */ dc->vcpu_blocktime_total[i] += vcpu_blocktime; } - dc->smp_cpus_down -= affected_cpu; - if (vcpu_total_blocktime) { + /* + * If all vCPUs used to be down, and copying this page would free some + * vCPUs, then the system-level blocktime ends here. + */ + if (dc->smp_cpus_down == smp_cpus && affected_cpu) { dc->total_blocktime += current - dc->last_begin; } + dc->smp_cpus_down -= affected_cpu; + trace_mark_postcopy_blocktime_end(addr, dc->total_blocktime, affected_cpu); } From b63a2e9e4b6dd779f7a699162ffdafc95e905c80 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:15 -0400 Subject: [PATCH 2146/2760] migration/postcopy: Optimize blocktime fault tracking with hashtable Currently, the postcopy blocktime feature maintains vCPU fault information using an array (vcpu_addr[]). It has two issues. Issue 1: Performance Concern ============================ The old algorithm was almost OK and fast on inserts, except that the lookup is slow and won't scale if there are a lot of vCPUs: when a page is copied during postcopy, mark_postcopy_blocktime_end() will walk the whole array trying to find which vCPUs are blocked by the address. So it needs constant O(N) walk for each page resolution. Alexey (the author of postcopy blocktime) mentioned the perf issue and how to optimize it in a piece of comment in the page resolution path. The comment was (interestingly..) not complete, but it's relatively clear what he wanted to say about this perf issue. Issue 2: Wrong Accounting on re-entrancies ========================================== People might think that each vCPU should only and always get one fault at a time, so that when the blocktime layer captured one fault on one vCPU, we should never see another fault message on this vCPU. It's almost correct, except in some extreme rare cases. Case 1: it's possible the fault thread processes the userfaultfd messages too fast so it can see >1 messages on one vCPU before the previous one was resolved. Case 2: it's theoretically also possible one vCPU can get even more than one message on the same fault address if a fault is retried by the kernel (e.g., handle_userfault() got interrupted before page resolution). As this info might be important, instead of using commit message, I put more details into the code as comment, when introducing an array maintaining concurrent faults on one vCPU. Please refer to the comments for details on both cases, especially case 1 which can be tricky. Case 1 sounds rare, but it can be easily reproduced locally for me when we run blocktime together with the migration-test on the vanilla postcopy. New Design ========== This patch should do almost what Alexey mentioned, but slightly differently: instead of having an array to maintain vCPU fault addresses, for each of the fault message we push a message into a hash, indexed by the fault address. With the hash, it can replace the old two structs: both the vcpu_addr[] array, and also the array to store the start time of the fault. However due to above we need one more counter array to account concurrent faults on the same vCPU - that should even be needed in the old code, it's just that the old code was buggy and it will blindly overwrite an existing entry.. now we'll start to really track everything. The hash structure might be more efficient than tree to maintain such addr->(cpu, fault_time) information, so that the insert() and lookup() paths should ideally both be ~O(1). After all, we do not need to sort. Here we need to do one remove() though after the lookup(). It could be slow but only if many vCPUs faulted exactly on the same address (so when the list of cpu entries is long), which should be unlikely. Even with that, it's still a worst case O(N) (consider 400 vCPUs faulted on the same address and how likely is it..) rather than a constant O(N) complexity. When at it, touch up the tracepoints to make them slightly more useful. One tracepoint is added when walking all the fault entries. Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613141217.474825-13-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/postcopy-ram.c | 259 ++++++++++++++++++++++++++++++++------- migration/trace-events | 5 +- 2 files changed, 216 insertions(+), 48 deletions(-) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index fd6c0bdb1e..91c23b446e 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -112,14 +112,69 @@ void postcopy_thread_create(MigrationIncomingState *mis, /* All the time records are in unit of nanoseconds */ typedef struct PostcopyBlocktimeContext { - /* time when page fault initiated per vCPU */ - uint64_t *vcpu_blocktime_start; /* blocktime per vCPU */ uint64_t *vcpu_blocktime_total; /* count of faults per vCPU */ uint64_t *vcpu_faults_count; - /* page address per vCPU */ - uintptr_t *vcpu_addr; + /* + * count of currently blocked faults per vCPU. + * + * NOTE: Normally there should only be one fault in-progress per vCPU + * thread, so logically it _seems_ vcpu_faults_count[] for any vCPU + * should be either zero or one. However, there can be reasons we see + * >1 faults on the same vCPU thread. + * + * CASE (1): since the process to resolve faults (ioctl(UFFDIO_COPY), + * for example) is done before taking the mutex that protects the + * blocktime context, it can happen that we read more than one faulted + * addresses per vCPU. + * + * One example when we can see >1 faulted addresses for one vCPU: + * + * vcpu1 thread fault thread resolve thread + * ============ ============ ============== + * + * faulted on addr1 + * read uffd msg (addr1) + * MUTEX_LOCK + * add entry (cpu1, addr1) + * MUTEX_UNLOCK + * request remote fault (addr1) + * resolve fault (addr1) + * addr1 resolved, continue.. + * faulted on addr2 + * read uffd msg (addr2) + * MUTEX_LOCK + * add entry (cpu1, addr2) <--------------- [A] + * MUTEX_UNLOCK + * MUTEX_LOCK + * remove entry (cpu1, addr1) + * MUTEX_UNLOCK + * + * In above case, we may see (cpu1, addr1) and (cpu1, addr2) entries to + * appear together at [A], when it gets the lock before the resolve + * thread. Use this counter to maintain such case, and only when it + * reaches zero we know the vCPU is not blocked anymore. + * + * CASE (2): theoretically (the author admit to not have verified + * this..), one vCPU thread can also generate more than one userfaultfd + * message on the same address. It can happen e.g. for whatever reason + * the fault got retried before a resolution arrives. In that extremely + * rare case, we could also see two (cpu1, addr1) entries. + * + * In all cases, be prepared with such re-entrancies with this array. + * + * Using uint8_t should be far enough for now. For example, when + * there're only one resolve thread (postcopy ram listening thread), + * the max (concurrent fault entries) should be two. + */ + uint8_t *vcpu_faults_current; + /* + * The hash that contains addr1->[(cpu1,ts1),(cpu2,ts2) ...] mappings. + * Each of the entry is a tuple of (CPU index, fault timestamp) showing + * that a fault was requested. + */ + GHashTable *vcpu_addr_hash; /* total blocktime when all vCPUs are stopped */ uint64_t total_blocktime; /* point in time when last page fault was initiated */ @@ -145,13 +200,38 @@ typedef struct PostcopyBlocktimeContext { Notifier exit_notifier; } PostcopyBlocktimeContext; +typedef struct { + /* The time the fault was triggered */ + uint64_t fault_time; + /* The vCPU index that was blocked */ + int cpu; +} BlocktimeVCPUEntry; + +/* Alloc an entry to record a vCPU fault */ +static BlocktimeVCPUEntry * +blocktime_vcpu_entry_alloc(int cpu, uint64_t fault_time) +{ + BlocktimeVCPUEntry *entry = g_new(BlocktimeVCPUEntry, 1); + + entry->fault_time = fault_time; + entry->cpu = cpu; + + return entry; +} + +/* Free a @GList of @BlocktimeVCPUEntry */ +static void blocktime_vcpu_list_free(gpointer data) +{ + g_list_free_full(data, g_free); +} + static void destroy_blocktime_context(struct PostcopyBlocktimeContext *ctx) { g_hash_table_destroy(ctx->tid_to_vcpu_hash); - g_free(ctx->vcpu_blocktime_start); + g_hash_table_destroy(ctx->vcpu_addr_hash); g_free(ctx->vcpu_blocktime_total); g_free(ctx->vcpu_faults_count); - g_free(ctx->vcpu_addr); + g_free(ctx->vcpu_faults_current); g_free(ctx); } @@ -198,12 +278,22 @@ static struct PostcopyBlocktimeContext *blocktime_context_new(void) unsigned int smp_cpus = ms->smp.cpus; PostcopyBlocktimeContext *ctx = g_new0(PostcopyBlocktimeContext, 1); - ctx->vcpu_blocktime_start = g_new0(uint64_t, smp_cpus); ctx->vcpu_blocktime_total = g_new0(uint64_t, smp_cpus); ctx->vcpu_faults_count = g_new0(uint64_t, smp_cpus); - ctx->vcpu_addr = g_new0(uintptr_t, smp_cpus); + ctx->vcpu_faults_current = g_new0(uint8_t, smp_cpus); ctx->tid_to_vcpu_hash = blocktime_init_tid_to_vcpu_hash(); + /* + * The key (host virtual addresses) will always be gpointer-sized on + * either 32bits or 64bits systems, so it'll fit as a direct key. + * + * The value will be a list of BlocktimeVCPUEntry entries. + */ + ctx->vcpu_addr_hash = g_hash_table_new_full(g_direct_hash, + g_direct_equal, + NULL, + blocktime_vcpu_list_free); + ctx->exit_notifier.notify = migration_exit_cb; qemu_add_exit_notifier(&ctx->exit_notifier); @@ -893,6 +983,39 @@ static uint64_t get_current_ns(void) return (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_REALTIME); } +/* Inject an (cpu, fault_time) entry into the database, using addr as key */ +static void blocktime_fault_inject(PostcopyBlocktimeContext *ctx, + uintptr_t addr, int cpu, uint64_t time) +{ + BlocktimeVCPUEntry *entry = blocktime_vcpu_entry_alloc(cpu, time); + GHashTable *table = ctx->vcpu_addr_hash; + gpointer key = (gpointer)addr; + GList *head, *list; + gboolean result; + + head = g_hash_table_lookup(table, key); + if (head) { + /* + * If existed, steal the @head for list operation rather than + * freeing it, making sure steal succeeded. + */ + result = g_hash_table_steal(table, key); + assert(result == TRUE); + } + + /* + * Now the key is guaranteed to be absent. Two cases: + * + * (1) There's no existing entry, list contains the only one. Insert. + * (2) There're existing entries, after stealing we own it, prepend the + * result and re-insert. + */ + list = g_list_prepend(head, entry); + g_hash_table_insert(table, key, list); + + trace_postcopy_blocktime_begin(addr, time, cpu, !!head); +} + /* * This function is being called when pagefault occurs. It tracks down vCPU * blocking time. It's protected by @page_request_mutex. @@ -912,30 +1035,74 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, if (!dc || ptid == 0) { return; } + + /* + * The caller should only inject a blocktime entry when the page is + * yet missing. + */ + assert(!ramblock_recv_bitmap_test(rb, (void *)addr)); + + current = get_current_ns(); cpu = blocktime_get_vcpu(dc, ptid); - if (cpu < 0) { + + if (cpu >= 0) { + /* How many faults on this vCPU in total? */ + dc->vcpu_faults_count[cpu]++; + + /* + * Account how many concurrent faults on this vCPU we trapped. See + * comments above vcpu_faults_current[] on why it can be more than one. + */ + if (dc->vcpu_faults_current[cpu]++ == 0) { + dc->smp_cpus_down++; + /* + * We use last_begin to cover (1) the 1st fault on this specific + * vCPU, but meanwhile (2) the last vCPU that got blocked. It's + * only used to calculate system-wide blocktime. + */ + dc->last_begin = current; + } + + /* Making sure it won't overflow - it really should never! */ + assert(dc->vcpu_faults_current[cpu] <= 255); + } else { + /* We do not support non-vCPU thread tracking yet */ dc->non_vcpu_faults++; return; } - current = get_current_ns(); - if (dc->vcpu_addr[cpu] == 0) { - dc->smp_cpus_down++; - } + blocktime_fault_inject(dc, addr, cpu, current); +} - dc->last_begin = current; - dc->vcpu_blocktime_start[cpu] = current; - dc->vcpu_addr[cpu] = addr; - dc->vcpu_faults_count[cpu]++; +typedef struct { + PostcopyBlocktimeContext *ctx; + uint64_t current; + int affected_cpus; +} BlockTimeVCPUIter; + +static void blocktime_cpu_list_iter_fn(gpointer data, gpointer user_data) +{ + BlockTimeVCPUIter *iter = user_data; + PostcopyBlocktimeContext *ctx = iter->ctx; + BlocktimeVCPUEntry *entry = data; + int cpu = entry->cpu; /* - * The caller should only inject a blocktime entry when the page is - * yet missing. + * Time should never go back.. so when the fault is resolved it must be + * later than when it was faulted. */ - assert(!ramblock_recv_bitmap_test(rb, (void *)addr)); + assert(iter->current >= entry->fault_time); + + /* + * If we resolved all pending faults on one vCPU due to this page + * resolution, take a note. + */ + if (--ctx->vcpu_faults_current[cpu] == 0) { + ctx->vcpu_blocktime_total[cpu] += iter->current - entry->fault_time; + iter->affected_cpus += 1; + } - trace_mark_postcopy_blocktime_begin(addr, dc->vcpu_blocktime_start[cpu], - cpu); + trace_postcopy_blocktime_end_one(cpu, ctx->vcpu_faults_current[cpu]); } /* @@ -971,43 +1138,43 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) PostcopyBlocktimeContext *dc = mis->blocktime_ctx; MachineState *ms = MACHINE(qdev_get_machine()); unsigned int smp_cpus = ms->smp.cpus; - int i, affected_cpu = 0; - uint64_t read_vcpu_time, current; + BlockTimeVCPUIter iter = { + .current = get_current_ns(), + .affected_cpus = 0, + .ctx = dc, + }; + gpointer key = (gpointer)addr; + GHashTable *table; + GList *list; if (!dc) { return; } - current = get_current_ns(); - /* lookup cpu, to clear it, - * that algorithm looks straightforward, but it's not - * optimal, more optimal algorithm is keeping tree or hash - * where key is address value is a list of */ - for (i = 0; i < smp_cpus; i++) { - uint64_t vcpu_blocktime = 0; - - read_vcpu_time = dc->vcpu_blocktime_start[i]; - if (dc->vcpu_addr[i] != addr || read_vcpu_time == 0) { - continue; - } - dc->vcpu_addr[i] = 0; - vcpu_blocktime = current - read_vcpu_time; - affected_cpu += 1; - /* continue cycle, due to one page could affect several vCPUs */ - dc->vcpu_blocktime_total[i] += vcpu_blocktime; + table = dc->vcpu_addr_hash; + /* the address wasn't tracked at all? */ + list = g_hash_table_lookup(table, key); + if (!list) { + return; } + /* + * Loop over the set of vCPUs that got blocked on this addr, do the + * blocktime accounting. After that, remove the whole list. + */ + g_list_foreach(list, blocktime_cpu_list_iter_fn, &iter); + g_hash_table_remove(table, key); + /* * If all vCPUs used to be down, and copying this page would free some * vCPUs, then the system-level blocktime ends here. */ - if (dc->smp_cpus_down == smp_cpus && affected_cpu) { - dc->total_blocktime += current - dc->last_begin; + if (dc->smp_cpus_down == smp_cpus && iter.affected_cpus) { + dc->total_blocktime += iter.current - dc->last_begin; } - dc->smp_cpus_down -= affected_cpu; + dc->smp_cpus_down -= iter.affected_cpus; - trace_mark_postcopy_blocktime_end(addr, dc->total_blocktime, - affected_cpu); + trace_postcopy_blocktime_end(addr, iter.current, iter.affected_cpus); } static void postcopy_pause_fault_thread(MigrationIncomingState *mis) diff --git a/migration/trace-events b/migration/trace-events index 9c1f3b7044..a36a78f01a 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -285,8 +285,6 @@ postcopy_nhp_range(const char *ramblock, void *host_addr, size_t offset, size_t postcopy_place_page(void *host_addr) "host=%p" postcopy_place_page_zero(void *host_addr) "host=%p" postcopy_ram_enable_notify(void) "" -mark_postcopy_blocktime_begin(uint64_t addr, uint64_t time, int cpu) "addr: 0x%" PRIx64 ", time: %" PRIu64 ", cpu: %d" -mark_postcopy_blocktime_end(uint64_t addr, uint64_t time, int affected_cpu) "addr: 0x%" PRIx64 ", time: %" PRIu64 ", affected_cpus: %d" postcopy_pause_fault_thread(void) "" postcopy_pause_fault_thread_continued(void) "" postcopy_pause_fast_load(void) "" @@ -311,6 +309,9 @@ postcopy_preempt_new_channel(void) "" postcopy_preempt_thread_entry(void) "" postcopy_preempt_thread_exit(void) "" postcopy_blocktime_tid_cpu_map(int cpu, uint32_t tid) "cpu: %d, tid: %u" +postcopy_blocktime_begin(uint64_t addr, uint64_t time, int cpu, bool exists) "addr: 0x%" PRIx64 ", time: %" PRIu64 ", cpu: %d, exist: %d" +postcopy_blocktime_end(uint64_t addr, uint64_t time, int affected_cpu) "addr: 0x%" PRIx64 ", time: %" PRIu64 ", affected_cpus: %d" +postcopy_blocktime_end_one(int cpu, uint8_t left_faults) "cpu: %d, left_faults: %" PRIu8 # exec.c migration_exec_outgoing(const char *cmd) "cmd=%s" From ed23a159763293e84d3562dedd731192b093b808 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:16 -0400 Subject: [PATCH 2147/2760] migration/postcopy: blocktime allows track / report non-vCPU faults When used to report page fault latencies, the blocktime feature can be almost useless when KVM async page fault is enabled, because in most cases such remote fault will kickoff async page faults, then it's not trackable from blocktime layer. After all these recent rewrites to blocktime layer, it's finally so easy to also support tracking non-vCPU faults. It'll be even faster if we could always index fault records with TIDs, unfortunately we need to maintain the blocktime API which report things in vCPU indexes. Of course this can work not only for kworkers, but also any guest accesses that may reach a missing page, for example, very likely when in the QEMU main thread too (and all other threads whenever applicable). In this case, we don't care about "how long the threads are blocked", but we only care about "how long the fault will be resolved". Cc: Markus Armbruster Cc: Dr. David Alan Gilbert Reviewed-by: Fabiano Rosas Tested-by: Mario Casquero Link: https://lore.kernel.org/r/20250613141217.474825-14-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration-hmp-cmds.c | 5 +++ migration/postcopy-ram.c | 64 +++++++++++++++++++++------ migration/trace-events | 2 +- qapi/migration.json | 12 ++++- tests/qtest/migration/migration-qmp.c | 1 + 5 files changed, 67 insertions(+), 17 deletions(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index 8b3846dab5..e1f9530520 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -80,6 +80,11 @@ static void migration_dump_blocktime(Monitor *mon, MigrationInfo *info) info->postcopy_latency); } + if (info->has_postcopy_non_vcpu_latency) { + monitor_printf(mon, "Postcopy non-vCPU Latencies (ns): %" PRIu64 "\n", + info->postcopy_non_vcpu_latency); + } + if (info->has_postcopy_vcpu_latency) { uint64List *item = info->postcopy_vcpu_latency; const char *sep = ""; diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index 91c23b446e..f4cb23b3e0 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -192,6 +192,8 @@ typedef struct PostcopyBlocktimeContext { GHashTable *tid_to_vcpu_hash; /* Count of non-vCPU faults. This is only for debugging purpose. */ uint64_t non_vcpu_faults; + /* total blocktime when a non-vCPU thread is stopped */ + uint64_t non_vcpu_blocktime_total; /* * Handler for exit event, necessary for @@ -203,7 +205,10 @@ typedef struct PostcopyBlocktimeContext { typedef struct { /* The time the fault was triggered */ uint64_t fault_time; - /* The vCPU index that was blocked */ + /* + * The vCPU index that was blocked, when cpu==-1, it means it's a + * fault from non-vCPU threads. + */ int cpu; } BlocktimeVCPUEntry; @@ -344,6 +349,12 @@ void fill_destination_postcopy_migration_info(MigrationInfo *info) QAPI_LIST_PREPEND(list_latency, latency); } + latency_total += bc->non_vcpu_blocktime_total; + faults += bc->non_vcpu_faults; + + info->has_postcopy_non_vcpu_latency = true; + info->postcopy_non_vcpu_latency = bc->non_vcpu_faults ? + (bc->non_vcpu_blocktime_total / bc->non_vcpu_faults) : 0; info->has_postcopy_blocktime = true; /* Convert ns -> ms */ info->postcopy_blocktime = (uint32_t)(bc->total_blocktime / SCALE_MS); @@ -983,7 +994,10 @@ static uint64_t get_current_ns(void) return (uint64_t)qemu_clock_get_ns(QEMU_CLOCK_REALTIME); } -/* Inject an (cpu, fault_time) entry into the database, using addr as key */ +/* + * Inject an (cpu, fault_time) entry into the database, using addr as key. + * When cpu==-1, it means it's a non-vCPU fault. + */ static void blocktime_fault_inject(PostcopyBlocktimeContext *ctx, uintptr_t addr, int cpu, uint64_t time) { @@ -1066,9 +1080,17 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, /* Making sure it won't overflow - it really should never! */ assert(dc->vcpu_faults_current[cpu] <= 255); } else { - /* We do not support non-vCPU thread tracking yet */ + /* + * For non-vCPU thread faults, we don't care about tid or cpu index + * or time the thread is blocked (e.g., a kworker trying to help + * KVM when async_pf=on is OK to be blocked and not affect guest + * responsiveness), but we care about latency. Track it with + * cpu=-1. + * + * Note that this will NOT affect blocktime reports on vCPU being + * blocked, but only about system-wide latency reports. + */ dc->non_vcpu_faults++; - return; } blocktime_fault_inject(dc, addr, cpu, current); @@ -1078,6 +1100,7 @@ typedef struct { PostcopyBlocktimeContext *ctx; uint64_t current; int affected_cpus; + int affected_non_cpus; } BlockTimeVCPUIter; static void blocktime_cpu_list_iter_fn(gpointer data, gpointer user_data) @@ -1085,6 +1108,7 @@ static void blocktime_cpu_list_iter_fn(gpointer data, gpointer user_data) BlockTimeVCPUIter *iter = user_data; PostcopyBlocktimeContext *ctx = iter->ctx; BlocktimeVCPUEntry *entry = data; + uint64_t time_passed; int cpu = entry->cpu; /* @@ -1092,17 +1116,27 @@ static void blocktime_cpu_list_iter_fn(gpointer data, gpointer user_data) * later than when it was faulted. */ assert(iter->current >= entry->fault_time); + time_passed = iter->current - entry->fault_time; - /* - * If we resolved all pending faults on one vCPU due to this page - * resolution, take a note. - */ - if (--ctx->vcpu_faults_current[cpu] == 0) { - ctx->vcpu_blocktime_total[cpu] += iter->current - entry->fault_time; - iter->affected_cpus += 1; + if (cpu >= 0) { + /* + * If we resolved all pending faults on one vCPU due to this page + * resolution, take a note. + */ + if (--ctx->vcpu_faults_current[cpu] == 0) { + ctx->vcpu_blocktime_total[cpu] += time_passed; + iter->affected_cpus += 1; + } + trace_postcopy_blocktime_end_one(cpu, ctx->vcpu_faults_current[cpu]); + } else { + iter->affected_non_cpus++; + ctx->non_vcpu_blocktime_total += time_passed; + /* + * We do not maintain how many pending non-vCPU faults because we + * do not care about blocktime, only latency. + */ + trace_postcopy_blocktime_end_one(-1, 0); } - - trace_postcopy_blocktime_end_one(cpu, ctx->vcpu_faults_current[cpu]); } /* @@ -1141,6 +1175,7 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) BlockTimeVCPUIter iter = { .current = get_current_ns(), .affected_cpus = 0, + .affected_non_cpus = 0, .ctx = dc, }; gpointer key = (gpointer)addr; @@ -1174,7 +1209,8 @@ static void mark_postcopy_blocktime_end(uintptr_t addr) } dc->smp_cpus_down -= iter.affected_cpus; - trace_postcopy_blocktime_end(addr, iter.current, iter.affected_cpus); + trace_postcopy_blocktime_end(addr, iter.current, iter.affected_cpus, + iter.affected_non_cpus); } static void postcopy_pause_fault_thread(MigrationIncomingState *mis) diff --git a/migration/trace-events b/migration/trace-events index a36a78f01a..706db97def 100644 --- a/migration/trace-events +++ b/migration/trace-events @@ -310,7 +310,7 @@ postcopy_preempt_thread_entry(void) "" postcopy_preempt_thread_exit(void) "" postcopy_blocktime_tid_cpu_map(int cpu, uint32_t tid) "cpu: %d, tid: %u" postcopy_blocktime_begin(uint64_t addr, uint64_t time, int cpu, bool exists) "addr: 0x%" PRIx64 ", time: %" PRIu64 ", cpu: %d, exist: %d" -postcopy_blocktime_end(uint64_t addr, uint64_t time, int affected_cpu) "addr: 0x%" PRIx64 ", time: %" PRIu64 ", affected_cpus: %d" +postcopy_blocktime_end(uint64_t addr, uint64_t time, int affected_cpu, int affected_non_cpus) "addr: 0x%" PRIx64 ", time: %" PRIu64 ", affected_cpus: %d, affected_non_cpus: %d" postcopy_blocktime_end_one(int cpu, uint8_t left_faults) "cpu: %d, left_faults: %" PRIu8 # exec.c diff --git a/qapi/migration.json b/qapi/migration.json index bb41dc0795..66fb8ac74d 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -247,6 +247,12 @@ # this is the per-vCPU statistics. This is only present when the # postcopy-blocktime migration capability is enabled. (Since 10.1) # +# @postcopy-non-vcpu-latency: average remote page fault latency for all +# faults happend in non-vCPU threads (in ns). It has the same +# definition of @postcopy-latency but this only provides statistics to +# non-vCPU faults. This is only present when the postcopy-blocktime +# migration capability is enabled. (Since 10.1) +# # @socket-address: Only used for tcp, to know what the real port is # (Since 4.0) # @@ -273,8 +279,8 @@ # # Features: # -# @unstable: Members @postcopy-latency, @postcopy-vcpu-latency are -# experimental. +# @unstable: Members @postcopy-latency, @postcopy-vcpu-latency, +# @postcopy-non-vcpu-latency are experimental. # # Since: 0.14 ## @@ -295,6 +301,8 @@ 'type': 'uint64', 'features': [ 'unstable' ] }, '*postcopy-vcpu-latency': { 'type': ['uint64'], 'features': [ 'unstable' ] }, + '*postcopy-non-vcpu-latency': { + 'type': 'uint64', 'features': [ 'unstable' ] }, '*socket-address': ['SocketAddress'], '*dirty-limit-throttle-time-per-round': 'uint64', '*dirty-limit-ring-full-time': 'uint64'} } diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c index 1a5ab2d229..67a67d4bd6 100644 --- a/tests/qtest/migration/migration-qmp.c +++ b/tests/qtest/migration/migration-qmp.c @@ -361,6 +361,7 @@ void read_blocktime(QTestState *who) g_assert(qdict_haskey(rsp_return, "postcopy-vcpu-blocktime")); g_assert(qdict_haskey(rsp_return, "postcopy-latency")); g_assert(qdict_haskey(rsp_return, "postcopy-vcpu-latency")); + g_assert(qdict_haskey(rsp_return, "postcopy-non-vcpu-latency")); qobject_unref(rsp_return); } From 3345fb3b6d7f511c948bfb153b85b3cabb996231 Mon Sep 17 00:00:00 2001 From: Peter Xu Date: Fri, 13 Jun 2025 10:12:17 -0400 Subject: [PATCH 2148/2760] migration/postcopy: Add latency distribution report for blocktime Add the latency distribution too for blocktime, using order-of-two buckets. It accounts for all the faults, from either vCPU or non-vCPU threads. With prior rework, it's very easy to achieve by adding an array to account for faults in each buckets. Sample output for HMP (while for QMP it's simply an array): Postcopy Latency Distribution: [ 1 us - 2 us ]: 0 [ 2 us - 4 us ]: 0 [ 4 us - 8 us ]: 1 [ 8 us - 16 us ]: 2 [ 16 us - 32 us ]: 2 [ 32 us - 64 us ]: 3 [ 64 us - 128 us ]: 10169 [ 128 us - 256 us ]: 50151 [ 256 us - 512 us ]: 12876 [ 512 us - 1 ms ]: 97 [ 1 ms - 2 ms ]: 42 [ 2 ms - 4 ms ]: 44 [ 4 ms - 8 ms ]: 93 [ 8 ms - 16 ms ]: 138 [ 16 ms - 32 ms ]: 0 [ 32 ms - 65 ms ]: 0 [ 65 ms - 131 ms ]: 0 [ 131 ms - 262 ms ]: 0 [ 262 ms - 524 ms ]: 0 [ 524 ms - 1 sec ]: 0 [ 1 sec - 2 sec ]: 0 [ 2 sec - 4 sec ]: 0 [ 4 sec - 8 sec ]: 0 [ 8 sec - 16 sec ]: 0 Cc: Markus Armbruster Acked-by: Dr. David Alan Gilbert Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/r/20250613141217.474825-15-peterx@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- migration/migration-hmp-cmds.c | 32 +++++++++++++++++++ migration/postcopy-ram.c | 46 +++++++++++++++++++++++++++ qapi/migration.json | 12 ++++++- tests/qtest/migration/migration-qmp.c | 1 + 4 files changed, 90 insertions(+), 1 deletion(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index e1f9530520..cef5608210 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -52,6 +52,21 @@ static void migration_global_dump(Monitor *mon) ms->clear_bitmap_shift); } +static const gchar *format_time_str(uint64_t us) +{ + const char *units[] = {"us", "ms", "sec"}; + int index = 0; + + while (us > 1000) { + us /= 1000; + if (++index >= (sizeof(units) - 1)) { + break; + } + } + + return g_strdup_printf("%"PRIu64" %s", us, units[index]); +} + static void migration_dump_blocktime(Monitor *mon, MigrationInfo *info) { if (info->has_postcopy_blocktime) { @@ -100,6 +115,23 @@ static void migration_dump_blocktime(Monitor *mon, MigrationInfo *info) } monitor_printf(mon, "]\n"); } + + if (info->has_postcopy_latency_dist) { + uint64List *item = info->postcopy_latency_dist; + int count = 0; + + monitor_printf(mon, "Postcopy Latency Distribution:\n"); + + while (item) { + g_autofree const gchar *from = format_time_str(1UL << count); + g_autofree const gchar *to = format_time_str(1UL << (count + 1)); + + monitor_printf(mon, " [ %8s - %8s ]: %10"PRIu64"\n", + from, to, item->value); + item = item->next; + count++; + } + } } void hmp_info_migrate(Monitor *mon, const QDict *qdict) diff --git a/migration/postcopy-ram.c b/migration/postcopy-ram.c index f4cb23b3e0..45af9a361e 100644 --- a/migration/postcopy-ram.c +++ b/migration/postcopy-ram.c @@ -110,6 +110,15 @@ void postcopy_thread_create(MigrationIncomingState *mis, #include #include +/* + * Here we use 24 buckets, which means the last bucket will cover [2^24 us, + * 2^25 us) ~= [16, 32) seconds. It should be far enough to record even + * extreme (perf-wise broken) 1G pages moving over, which can sometimes + * take a few seconds due to various reasons. Anything more than that + * might be unsensible to account anymore. + */ +#define BLOCKTIME_LATENCY_BUCKET_N (24) + /* All the time records are in unit of nanoseconds */ typedef struct PostcopyBlocktimeContext { /* blocktime per vCPU */ @@ -175,6 +184,11 @@ typedef struct PostcopyBlocktimeContext { * that a fault was requested. */ GHashTable *vcpu_addr_hash; + /* + * Each bucket stores the count of faults that were resolved within the + * bucket window [2^N us, 2^(N+1) us). + */ + uint64_t latency_buckets[BLOCKTIME_LATENCY_BUCKET_N]; /* total blocktime when all vCPUs are stopped */ uint64_t total_blocktime; /* point in time when last page fault was initiated */ @@ -283,6 +297,9 @@ static struct PostcopyBlocktimeContext *blocktime_context_new(void) unsigned int smp_cpus = ms->smp.cpus; PostcopyBlocktimeContext *ctx = g_new0(PostcopyBlocktimeContext, 1); + /* Initialize all counters to be zeros */ + memset(ctx->latency_buckets, 0, sizeof(ctx->latency_buckets)); + ctx->vcpu_blocktime_total = g_new0(uint64_t, smp_cpus); ctx->vcpu_faults_count = g_new0(uint64_t, smp_cpus); ctx->vcpu_faults_current = g_new0(uint8_t, smp_cpus); @@ -320,6 +337,7 @@ void fill_destination_postcopy_migration_info(MigrationInfo *info) uint64_t latency_total = 0, faults = 0; uint32List *list_blocktime = NULL; uint64List *list_latency = NULL; + uint64List *latency_buckets = NULL; int i; if (!bc) { @@ -349,6 +367,10 @@ void fill_destination_postcopy_migration_info(MigrationInfo *info) QAPI_LIST_PREPEND(list_latency, latency); } + for (i = BLOCKTIME_LATENCY_BUCKET_N - 1; i >= 0; i--) { + QAPI_LIST_PREPEND(latency_buckets, bc->latency_buckets[i]); + } + latency_total += bc->non_vcpu_blocktime_total; faults += bc->non_vcpu_faults; @@ -364,6 +386,8 @@ void fill_destination_postcopy_migration_info(MigrationInfo *info) info->postcopy_latency = faults ? (latency_total / faults) : 0; info->has_postcopy_vcpu_latency = true; info->postcopy_vcpu_latency = list_latency; + info->has_postcopy_latency_dist = true; + info->postcopy_latency_dist = latency_buckets; } static uint64_t get_postcopy_total_blocktime(void) @@ -1096,6 +1120,25 @@ void mark_postcopy_blocktime_begin(uintptr_t addr, uint32_t ptid, blocktime_fault_inject(dc, addr, cpu, current); } +static void blocktime_latency_account(PostcopyBlocktimeContext *ctx, + uint64_t time_us) +{ + /* + * Convert time (in us) to bucket index it belongs. Take extra caution + * of time_us==0 even if normally rare - when happens put into bucket 0. + */ + int index = time_us ? (63 - clz64(time_us)) : 0; + + assert(index >= 0); + + /* If it's too large, put into top bucket */ + if (index >= BLOCKTIME_LATENCY_BUCKET_N) { + index = BLOCKTIME_LATENCY_BUCKET_N - 1; + } + + ctx->latency_buckets[index]++; +} + typedef struct { PostcopyBlocktimeContext *ctx; uint64_t current; @@ -1118,6 +1161,9 @@ static void blocktime_cpu_list_iter_fn(gpointer data, gpointer user_data) assert(iter->current >= entry->fault_time); time_passed = iter->current - entry->fault_time; + /* Latency buckets are in microseconds */ + blocktime_latency_account(ctx, time_passed / SCALE_US); + if (cpu >= 0) { /* * If we resolved all pending faults on one vCPU due to this page diff --git a/qapi/migration.json b/qapi/migration.json index 66fb8ac74d..2d39a8f748 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -242,6 +242,14 @@ # average page fault latency. This is only present when the # postcopy-blocktime migration capability is enabled. (Since 10.1) # +# @postcopy-latency-dist: remote page fault latency distributions. Each +# element of the array is the number of faults that fall into the +# bucket period. For the N-th bucket (N>=0), the latency window is +# [2^Nus, 2^(N+1)us). For example, the 8th element stores how many +# remote faults got resolved within [256us, 512us) window. This is only +# present when the postcopy-blocktime migration capability is enabled. +# (Since 10.1) +# # @postcopy-vcpu-latency: average remote page fault latency per vCPU (in # ns). It has the same definition of @postcopy-latency, but instead # this is the per-vCPU statistics. This is only present when the @@ -280,7 +288,7 @@ # Features: # # @unstable: Members @postcopy-latency, @postcopy-vcpu-latency, -# @postcopy-non-vcpu-latency are experimental. +# @postcopy-latency-dist, @postcopy-non-vcpu-latency are experimental. # # Since: 0.14 ## @@ -299,6 +307,8 @@ '*postcopy-vcpu-blocktime': ['uint32'], '*postcopy-latency': { 'type': 'uint64', 'features': [ 'unstable' ] }, + '*postcopy-latency-dist': { + 'type': ['uint64'], 'features': [ 'unstable' ] }, '*postcopy-vcpu-latency': { 'type': ['uint64'], 'features': [ 'unstable' ] }, '*postcopy-non-vcpu-latency': { diff --git a/tests/qtest/migration/migration-qmp.c b/tests/qtest/migration/migration-qmp.c index 67a67d4bd6..66dd369ba7 100644 --- a/tests/qtest/migration/migration-qmp.c +++ b/tests/qtest/migration/migration-qmp.c @@ -360,6 +360,7 @@ void read_blocktime(QTestState *who) g_assert(qdict_haskey(rsp_return, "postcopy-blocktime")); g_assert(qdict_haskey(rsp_return, "postcopy-vcpu-blocktime")); g_assert(qdict_haskey(rsp_return, "postcopy-latency")); + g_assert(qdict_haskey(rsp_return, "postcopy-latency-dist")); g_assert(qdict_haskey(rsp_return, "postcopy-vcpu-latency")); g_assert(qdict_haskey(rsp_return, "postcopy-non-vcpu-latency")); qobject_unref(rsp_return); From beeac2df5ff0850299e58f4ad27f83dae64c54df Mon Sep 17 00:00:00 2001 From: Juraj Marcin Date: Thu, 26 Jun 2025 10:52:32 +0200 Subject: [PATCH 2149/2760] migration: Rename save_live_complete_precopy_thread to save_complete_precopy_thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent patch [1] renames the save_live_complete_precopy handler to save_complete, as the machine is not live in most cases when this handler is executed. The same is true also for save_live_complete_precopy_thread, therefore this patch removes the "live" keyword from the handler itself and related types to keep the naming unified. In contrast to save_complete, this handler is only executed at the end of precopy, therefore the "precopy" keyword is retained. [1]: https://lore.kernel.org/all/20250613140801.474264-7-peterx@redhat.com/ Cc: Alex Williamson Cc: Cédric Le Goater Signed-off-by: Juraj Marcin Link: https://lore.kernel.org/r/20250626085235.294690-1-jmarcin@redhat.com Signed-off-by: Peter Xu Signed-off-by: Fabiano Rosas --- docs/devel/migration/vfio.rst | 4 ++-- hw/vfio/migration-multifd.c | 4 ++-- hw/vfio/migration-multifd.h | 2 +- hw/vfio/migration.c | 2 +- include/migration/misc.h | 8 ++++---- include/migration/register.h | 6 +++--- include/qemu/typedefs.h | 6 +++--- migration/multifd-device-state.c | 10 +++++----- migration/savevm.c | 6 +++--- 9 files changed, 24 insertions(+), 24 deletions(-) diff --git a/docs/devel/migration/vfio.rst b/docs/devel/migration/vfio.rst index 8ff5ab0c74..2d8e5ca9dd 100644 --- a/docs/devel/migration/vfio.rst +++ b/docs/devel/migration/vfio.rst @@ -80,7 +80,7 @@ VFIO implements the device hooks for the iterative approach as follows: vendor driver indicates that no data remains. In the multifd mode it just emits a dummy EOS marker. -* A ``save_live_complete_precopy_thread`` function that in the multifd mode +* A ``save_complete_precopy_thread`` function that in the multifd mode provides thread handler performing multifd device state transfer. It sets the VFIO device to _STOP_COPY state, iteratively reads the data from the VFIO device and queues it for multifd transmission until the vendor @@ -200,7 +200,7 @@ Live migration save path .save_complete() until pending data is 0 In the multifd mode this iteration is done in - .save_live_complete_precopy_thread() instead. + .save_complete_precopy_thread() instead. | (POSTMIGRATE, _COMPLETED, _STOP_COPY) Migraton thread schedules cleanup bottom half and exits diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index 850a319488..55635486c8 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -583,7 +583,7 @@ vfio_save_complete_precopy_thread_config_state(VFIODevice *vbasedev, /* * This thread is spawned by the migration core directly via - * .save_live_complete_precopy_thread SaveVMHandler. + * .save_complete_precopy_thread SaveVMHandler. * * It exits after either: * * completing saving the remaining device state and device config, OR: @@ -592,7 +592,7 @@ vfio_save_complete_precopy_thread_config_state(VFIODevice *vbasedev, * multifd_device_state_save_thread_should_exit() returning true. */ bool -vfio_multifd_save_complete_precopy_thread(SaveLiveCompletePrecopyThreadData *d, +vfio_multifd_save_complete_precopy_thread(SaveCompletePrecopyThreadData *d, Error **errp) { VFIODevice *vbasedev = d->handler_opaque; diff --git a/hw/vfio/migration-multifd.h b/hw/vfio/migration-multifd.h index 0bab63211d..ebf22a7997 100644 --- a/hw/vfio/migration-multifd.h +++ b/hw/vfio/migration-multifd.h @@ -26,7 +26,7 @@ bool vfio_multifd_load_state_buffer(void *opaque, char *data, size_t data_size, void vfio_multifd_emit_dummy_eos(VFIODevice *vbasedev, QEMUFile *f); bool -vfio_multifd_save_complete_precopy_thread(SaveLiveCompletePrecopyThreadData *d, +vfio_multifd_save_complete_precopy_thread(SaveCompletePrecopyThreadData *d, Error **errp); int vfio_multifd_switchover_start(VFIODevice *vbasedev); diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index 33a71f8999..c329578eec 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -835,7 +835,7 @@ static const SaveVMHandlers savevm_vfio_handlers = { */ .load_state_buffer = vfio_multifd_load_state_buffer, .switchover_start = vfio_switchover_start, - .save_live_complete_precopy_thread = vfio_multifd_save_complete_precopy_thread, + .save_complete_precopy_thread = vfio_multifd_save_complete_precopy_thread, }; /* ---------------------------------------------------------------------- */ diff --git a/include/migration/misc.h b/include/migration/misc.h index 8fd36eba1d..a261f99d89 100644 --- a/include/migration/misc.h +++ b/include/migration/misc.h @@ -119,19 +119,19 @@ bool migrate_uri_parse(const char *uri, MigrationChannel **channel, Error **errp); /* migration/multifd-device-state.c */ -typedef struct SaveLiveCompletePrecopyThreadData { - SaveLiveCompletePrecopyThreadHandler hdlr; +typedef struct SaveCompletePrecopyThreadData { + SaveCompletePrecopyThreadHandler hdlr; char *idstr; uint32_t instance_id; void *handler_opaque; -} SaveLiveCompletePrecopyThreadData; +} SaveCompletePrecopyThreadData; bool multifd_queue_device_state(char *idstr, uint32_t instance_id, char *data, size_t len); bool multifd_device_state_supported(void); void -multifd_spawn_device_state_save_thread(SaveLiveCompletePrecopyThreadHandler hdlr, +multifd_spawn_device_state_save_thread(SaveCompletePrecopyThreadHandler hdlr, char *idstr, uint32_t instance_id, void *opaque); diff --git a/include/migration/register.h b/include/migration/register.h index 2a26e76a68..ae79794cdd 100644 --- a/include/migration/register.h +++ b/include/migration/register.h @@ -98,7 +98,7 @@ typedef struct SaveVMHandlers { int (*save_complete)(QEMUFile *f, void *opaque); /** - * @save_live_complete_precopy_thread (invoked in a separate thread) + * @save_complete_precopy_thread (invoked in a separate thread) * * Called at the end of a precopy phase from a separate worker thread * in configurations where multifd device state transfer is supported @@ -107,14 +107,14 @@ typedef struct SaveVMHandlers { * When postcopy is enabled, devices that support postcopy will skip this * step. * - * @d: a #SaveLiveCompletePrecopyThreadData containing parameters that the + * @d: a #SaveCompletePrecopyThreadData containing parameters that the * handler may need, including this device section idstr and instance_id, * and opaque data pointer passed to register_savevm_live(). * @errp: pointer to Error*, to store an error if it happens. * * Returns true to indicate success and false for errors. */ - SaveLiveCompletePrecopyThreadHandler save_live_complete_precopy_thread; + SaveCompletePrecopyThreadHandler save_complete_precopy_thread; /* This runs both outside and inside the BQL. */ diff --git a/include/qemu/typedefs.h b/include/qemu/typedefs.h index 507f0814d5..4a94af9665 100644 --- a/include/qemu/typedefs.h +++ b/include/qemu/typedefs.h @@ -109,7 +109,7 @@ typedef struct QString QString; typedef struct RAMBlock RAMBlock; typedef struct Range Range; typedef struct ReservedRegion ReservedRegion; -typedef struct SaveLiveCompletePrecopyThreadData SaveLiveCompletePrecopyThreadData; +typedef struct SaveCompletePrecopyThreadData SaveCompletePrecopyThreadData; typedef struct SHPCDevice SHPCDevice; typedef struct SSIBus SSIBus; typedef struct TCGCPUOps TCGCPUOps; @@ -135,7 +135,7 @@ typedef struct IRQState *qemu_irq; typedef void (*qemu_irq_handler)(void *opaque, int n, int level); typedef bool (*MigrationLoadThread)(void *opaque, bool *should_quit, Error **errp); -typedef bool (*SaveLiveCompletePrecopyThreadHandler)(SaveLiveCompletePrecopyThreadData *d, - Error **errp); +typedef bool (*SaveCompletePrecopyThreadHandler)(SaveCompletePrecopyThreadData *d, + Error **errp); #endif /* QEMU_TYPEDEFS_H */ diff --git a/migration/multifd-device-state.c b/migration/multifd-device-state.c index 94222d0eb0..fce64f00b0 100644 --- a/migration/multifd-device-state.c +++ b/migration/multifd-device-state.c @@ -131,7 +131,7 @@ bool multifd_device_state_supported(void) static void multifd_device_state_save_thread_data_free(void *opaque) { - SaveLiveCompletePrecopyThreadData *data = opaque; + SaveCompletePrecopyThreadData *data = opaque; g_clear_pointer(&data->idstr, g_free); g_free(data); @@ -139,7 +139,7 @@ static void multifd_device_state_save_thread_data_free(void *opaque) static int multifd_device_state_save_thread(void *opaque) { - SaveLiveCompletePrecopyThreadData *data = opaque; + SaveCompletePrecopyThreadData *data = opaque; g_autoptr(Error) local_err = NULL; if (!data->hdlr(data, &local_err)) { @@ -170,18 +170,18 @@ bool multifd_device_state_save_thread_should_exit(void) } void -multifd_spawn_device_state_save_thread(SaveLiveCompletePrecopyThreadHandler hdlr, +multifd_spawn_device_state_save_thread(SaveCompletePrecopyThreadHandler hdlr, char *idstr, uint32_t instance_id, void *opaque) { - SaveLiveCompletePrecopyThreadData *data; + SaveCompletePrecopyThreadData *data; assert(multifd_device_state_supported()); assert(multifd_send_device_state); assert(!qatomic_read(&multifd_send_device_state->threads_abort)); - data = g_new(SaveLiveCompletePrecopyThreadData, 1); + data = g_new(SaveCompletePrecopyThreadData, 1); data->hdlr = hdlr; data->idstr = g_strdup(idstr); data->instance_id = instance_id; diff --git a/migration/savevm.c b/migration/savevm.c index c4fd5f5a5b..fabbeb296a 100644 --- a/migration/savevm.c +++ b/migration/savevm.c @@ -1581,15 +1581,15 @@ int qemu_savevm_state_complete_precopy_iterable(QEMUFile *f, bool in_postcopy) if (multifd_device_state) { QTAILQ_FOREACH(se, &savevm_state.handlers, entry) { - SaveLiveCompletePrecopyThreadHandler hdlr; + SaveCompletePrecopyThreadHandler hdlr; if (!se->ops || (in_postcopy && se->ops->has_postcopy && se->ops->has_postcopy(se->opaque)) || - !se->ops->save_live_complete_precopy_thread) { + !se->ops->save_complete_precopy_thread) { continue; } - hdlr = se->ops->save_live_complete_precopy_thread; + hdlr = se->ops->save_complete_precopy_thread; multifd_spawn_device_state_save_thread(hdlr, se->idstr, se->instance_id, se->opaque); From c86da2b1dd7589d414b5a2d1e5361d6c3b4ca885 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Thu, 10 Jul 2025 16:42:47 -0600 Subject: [PATCH 2150/2760] tcg: Use uintptr_t in tcg_malloc implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid ubsan failure with clang-20, tcg.h:715:19: runtime error: applying non-zero offset 64 to null pointer by not using pointers. Acked-by: Ilya Leoshkevich Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Richard Henderson --- include/tcg/tcg.h | 6 +++--- tcg/tcg.c | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 125323f153..0c2a319c11 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -357,7 +357,7 @@ static inline TCGRegSet output_pref(const TCGOp *op, unsigned i) } struct TCGContext { - uint8_t *pool_cur, *pool_end; + uintptr_t pool_cur, pool_end; TCGPool *pool_first, *pool_current, *pool_first_large; int nb_labels; int nb_globals; @@ -706,7 +706,7 @@ size_t tcg_nb_tbs(void); static inline void *tcg_malloc(int size) { TCGContext *s = tcg_ctx; - uint8_t *ptr, *ptr_end; + uintptr_t ptr, ptr_end; /* ??? This is a weak placeholder for minimum malloc alignment. */ size = QEMU_ALIGN_UP(size, 8); @@ -717,7 +717,7 @@ static inline void *tcg_malloc(int size) return tcg_malloc_internal(tcg_ctx, size); } else { s->pool_cur = ptr_end; - return ptr; + return (void *)ptr; } } diff --git a/tcg/tcg.c b/tcg/tcg.c index 50d40b9cbe..afac55a203 100644 --- a/tcg/tcg.c +++ b/tcg/tcg.c @@ -1331,8 +1331,9 @@ void *tcg_malloc_internal(TCGContext *s, int size) p = s->pool_current; if (!p) { p = s->pool_first; - if (!p) + if (!p) { goto new_pool; + } } else { if (!p->next) { new_pool: @@ -1351,8 +1352,8 @@ void *tcg_malloc_internal(TCGContext *s, int size) } } s->pool_current = p; - s->pool_cur = p->data + size; - s->pool_end = p->data + p->size; + s->pool_cur = (uintptr_t)p->data + size; + s->pool_end = (uintptr_t)p->data + p->size; return p->data; } @@ -1364,7 +1365,7 @@ void tcg_pool_reset(TCGContext *s) g_free(p); } s->pool_first_large = NULL; - s->pool_cur = s->pool_end = NULL; + s->pool_cur = s->pool_end = 0; s->pool_current = NULL; } From d6390204c61e148488f034d1f79be35cd3318d93 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 11 Jul 2025 15:12:17 +0100 Subject: [PATCH 2151/2760] linux-user: Use qemu_set_cloexec() to mark pidfd as FD_CLOEXEC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the linux-user do_fork() function we try to set the FD_CLOEXEC flag on a pidfd like this: fcntl(pid_fd, F_SETFD, fcntl(pid_fd, F_GETFL) | FD_CLOEXEC); This has two problems: (1) it doesn't check errors, which Coverity complains about (2) we use F_GETFL when we mean F_GETFD Deal with both of these problems by using qemu_set_cloexec() instead. That function will assert() if the fcntls fail, which is fine (we are inside fork_start()/fork_end() so we know nothing can mess around with our file descriptors here, and we just got this one from pidfd_open()). (As we are touching the if() statement here, we correct the indentation.) Coverity: CID 1508111 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Daniel P. Berrangé Signed-off-by: Richard Henderson Message-ID: <20250711141217.1429412-1-peter.maydell@linaro.org> --- linux-user/syscall.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/linux-user/syscall.c b/linux-user/syscall.c index 38dd563166..91360a072c 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -6747,10 +6747,9 @@ static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp, int pid_child = ret; pid_fd = pidfd_open(pid_child, 0); if (pid_fd >= 0) { - fcntl(pid_fd, F_SETFD, fcntl(pid_fd, F_GETFL) - | FD_CLOEXEC); + qemu_set_cloexec(pid_fd); } else { - pid_fd = 0; + pid_fd = 0; } #endif put_user_u32(pid_fd, parent_tidptr); From d93972d88b0984ed0a2090493f8d62cc188976d2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 5 Jul 2025 14:17:30 +0200 Subject: [PATCH 2152/2760] target/i386: nvmm, whpx: add accel/CPU class that sets host vendor NVMM and WHPX are virtualizers, and therefore they need to use (at least by default) the host vendor for the guest CPUID. Add a cpu_instance_init implementation to these accelerators. Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 3 ++- target/i386/meson.build | 2 ++ target/i386/nvmm/nvmm-all.c | 25 +++++++++++++++++++++++++ target/i386/whpx/whpx-all.c | 25 +++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4d4e523424..8fb74b56dd 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -44,6 +44,7 @@ #include "hw/boards.h" #include "hw/i386/sgx-epc.h" #endif +#include "system/qtest.h" #include "tcg/tcg-cpu.h" #include "disas/capstone.h" @@ -1943,7 +1944,7 @@ uint32_t xsave_area_size(uint64_t mask, bool compacted) static inline bool accel_uses_host_cpuid(void) { - return kvm_enabled() || hvf_enabled(); + return !tcg_enabled() && !qtest_enabled(); } static inline uint64_t x86_cpu_xsave_xcr0_components(X86CPU *cpu) diff --git a/target/i386/meson.build b/target/i386/meson.build index c1aacea613..092af34e2d 100644 --- a/target/i386/meson.build +++ b/target/i386/meson.build @@ -11,6 +11,8 @@ i386_ss.add(when: 'CONFIG_SEV', if_true: files('host-cpu.c', 'confidential-guest # x86 cpu type i386_ss.add(when: 'CONFIG_KVM', if_true: files('host-cpu.c')) i386_ss.add(when: 'CONFIG_HVF', if_true: files('host-cpu.c')) +i386_ss.add(when: 'CONFIG_WHPX', if_true: files('host-cpu.c')) +i386_ss.add(when: 'CONFIG_NVMM', if_true: files('host-cpu.c')) i386_system_ss = ss.source_set() i386_system_ss.add(files( diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index b4a4d50e86..11c263004d 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -19,6 +19,8 @@ #include "qemu/error-report.h" #include "qapi/error.h" #include "qemu/queue.h" +#include "accel/accel-cpu-target.h" +#include "host-cpu.h" #include "migration/blocker.h" #include "strings.h" @@ -1207,10 +1209,33 @@ static const TypeInfo nvmm_accel_type = { .class_init = nvmm_accel_class_init, }; +static void nvmm_cpu_instance_init(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + + host_cpu_instance_init(cpu); +} + +static void nvmm_cpu_accel_class_init(ObjectClass *oc, const void *data) +{ + AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); + + acc->cpu_instance_init = nvmm_cpu_instance_init; +} + +static const TypeInfo nvmm_cpu_accel_type = { + .name = ACCEL_CPU_NAME("nvmm"), + + .parent = TYPE_ACCEL_CPU, + .class_init = nvmm_cpu_accel_class_init, + .abstract = true, +}; + static void nvmm_type_init(void) { type_register_static(&nvmm_accel_type); + type_register_static(&nvmm_cpu_accel_type); } type_init(nvmm_type_init); diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index faf56e1972..22ac609070 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -26,6 +26,8 @@ #include "qapi/qapi-types-common.h" #include "qapi/qapi-visit-common.h" #include "migration/blocker.h" +#include "host-cpu.h" +#include "accel/accel-cpu-target.h" #include #include "whpx-internal.h" @@ -2500,6 +2502,28 @@ static void whpx_set_kernel_irqchip(Object *obj, Visitor *v, } } +static void whpx_cpu_instance_init(CPUState *cs) +{ + X86CPU *cpu = X86_CPU(cs); + + host_cpu_instance_init(cpu); +} + +static void whpx_cpu_accel_class_init(ObjectClass *oc, const void *data) +{ + AccelCPUClass *acc = ACCEL_CPU_CLASS(oc); + + acc->cpu_instance_init = whpx_cpu_instance_init; +} + +static const TypeInfo whpx_cpu_accel_type = { + .name = ACCEL_CPU_NAME("whpx"), + + .parent = TYPE_ACCEL_CPU, + .class_init = whpx_cpu_accel_class_init, + .abstract = true, +}; + /* * Partition support */ @@ -2726,6 +2750,7 @@ static const TypeInfo whpx_accel_type = { static void whpx_type_init(void) { type_register_static(&whpx_accel_type); + type_register_static(&whpx_cpu_accel_type); } bool init_whp_dispatch(void) From 810fcc41fc572d90b3c05af3f06f451626ee6b10 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 10 Jul 2025 18:49:17 +0200 Subject: [PATCH 2153/2760] target/i386: allow reordering max_x86_cpu_initfn vs accel CPU init The PMU feature is only supported by KVM, so move it there. And since all accelerators other than TCG overwrite the vendor, set it in max_x86_cpu_initfn only if it has not been initialized by the superclass. This makes it possible to run max_x86_cpu_initfn after accelerator init. Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 24 ++++++++++++------------ target/i386/kvm/kvm-cpu.c | 2 ++ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 8fb74b56dd..9c5cef2c7c 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6199,21 +6199,21 @@ static void max_x86_cpu_class_init(ObjectClass *oc, const void *data) static void max_x86_cpu_initfn(Object *obj) { X86CPU *cpu = X86_CPU(obj); - - /* We can't fill the features array here because we don't know yet if - * "migratable" is true or false. - */ - object_property_set_bool(OBJECT(cpu), "pmu", true, &error_abort); + CPUX86State *env = &cpu->env; /* - * these defaults are used for TCG and all other accelerators - * besides KVM and HVF, which overwrite these values + * these defaults are used for TCG, other accelerators overwrite these + * values */ - object_property_set_str(OBJECT(cpu), "vendor", CPUID_VENDOR_AMD, - &error_abort); - object_property_set_str(OBJECT(cpu), "model-id", - "QEMU TCG CPU version " QEMU_HW_VERSION, - &error_abort); + if (!env->cpuid_vendor1) { + object_property_set_str(OBJECT(cpu), "vendor", CPUID_VENDOR_AMD, + &error_abort); + } + if (!env->cpuid_model[0]) { + object_property_set_str(OBJECT(cpu), "model-id", + "QEMU TCG CPU version " QEMU_HW_VERSION, + &error_abort); + } } static const TypeInfo max_x86_cpu_type_info = { diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index 0fb88a239d..6fed353548 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -111,6 +111,8 @@ static void kvm_cpu_max_instance_init(X86CPU *cpu) host_cpu_max_instance_init(cpu); + object_property_set_bool(OBJECT(cpu), "pmu", true, &error_abort); + if (lmce_supported()) { object_property_set_bool(OBJECT(cpu), "lmce", true, &error_abort); } From 5f158abef44c7e0945fc5f76715ef135a9bf9bd2 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 5 Jul 2025 14:30:52 +0200 Subject: [PATCH 2154/2760] target/i386: move accel_cpu_instance_init to .instance_init With the reordering of instance_post_init callbacks that is new in 10.1 accel_cpu_instance_init must execute in .instance_init as is already the case for RISC-V. Otherwise, for example, setting the vendor property is broken when using KVM or Hypervisor.framework, because KVM sets it *after* the user's value is set by DeviceState's intance_post_init callback. Reported-by: Like Xu Reported-by: Dongli Zhang Reviewed-by: Xiaoyao Li Reviewed-by: Zhao Liu Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 9c5cef2c7c..44178bc523 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6202,8 +6202,8 @@ static void max_x86_cpu_initfn(Object *obj) CPUX86State *env = &cpu->env; /* - * these defaults are used for TCG, other accelerators overwrite these - * values + * these defaults are used for TCG, other accelerators have overwritten + * these values */ if (!env->cpuid_vendor1) { object_property_set_str(OBJECT(cpu), "vendor", CPUID_VENDOR_AMD, @@ -9038,8 +9038,6 @@ static void x86_cpu_post_initfn(Object *obj) } } - accel_cpu_instance_init(CPU(obj)); - #ifndef CONFIG_USER_ONLY if (current_machine && current_machine->cgs) { x86_confidential_guest_cpu_instance_init( @@ -9114,6 +9112,8 @@ static void x86_cpu_initfn(Object *obj) if (xcc->model) { x86_cpu_load_model(cpu, xcc->model); } + + accel_cpu_instance_init(CPU(obj)); } static int64_t x86_cpu_get_arch_id(CPUState *cs) From 29f1ba338baf60a9e455b6fdc37489ca1efe25aa Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 11 Jul 2025 09:43:52 +0200 Subject: [PATCH 2155/2760] target/i386: merge host_cpu_instance_init() and host_cpu_max_instance_init() Simplify the accelerators' cpu_instance_init callbacks by doing all host-cpu setup in a single function. Based-on: <20250711000603.438312-1-pbonzini@redhat.com> Cc: Xiaoyao Li Signed-off-by: Paolo Bonzini --- target/i386/host-cpu.c | 28 ++++++++++++++-------------- target/i386/hvf/hvf-cpu.c | 2 -- target/i386/kvm/kvm-cpu.c | 2 -- 3 files changed, 14 insertions(+), 18 deletions(-) diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index 7512567298..3399edc1ad 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -132,27 +132,27 @@ void host_cpu_instance_init(X86CPU *cpu) { X86CPUClass *xcc = X86_CPU_GET_CLASS(cpu); - if (xcc->model) { - char vendor[CPUID_VENDOR_SZ + 1]; - - host_cpu_vendor_fms(vendor, NULL, NULL, NULL); - object_property_set_str(OBJECT(cpu), "vendor", vendor, &error_abort); - } -} - -void host_cpu_max_instance_init(X86CPU *cpu) -{ char vendor[CPUID_VENDOR_SZ + 1] = { 0 }; char model_id[CPUID_MODEL_ID_SZ + 1] = { 0 }; int family, model, stepping; - /* Use max host physical address bits if -cpu max option is applied */ - object_property_set_bool(OBJECT(cpu), "host-phys-bits", true, &error_abort); - + /* + * setting vendor applies to both max/host and builtin_x86_defs CPU. + * FIXME: this probably should warn or should be skipped if vendors do + * not match, because family numbers are incompatible between Intel and AMD. + */ host_cpu_vendor_fms(vendor, &family, &model, &stepping); + object_property_set_str(OBJECT(cpu), "vendor", vendor, &error_abort); + + if (!xcc->max_features) { + return; + } + host_cpu_fill_model_id(model_id); - object_property_set_str(OBJECT(cpu), "vendor", vendor, &error_abort); + /* Use max host physical address bits if -cpu max option is applied */ + object_property_set_bool(OBJECT(cpu), "host-phys-bits", true, &error_abort); + object_property_set_int(OBJECT(cpu), "family", family, &error_abort); object_property_set_int(OBJECT(cpu), "model", model, &error_abort); object_property_set_int(OBJECT(cpu), "stepping", stepping, diff --git a/target/i386/hvf/hvf-cpu.c b/target/i386/hvf/hvf-cpu.c index 2b991f2fc8..94ee096ecf 100644 --- a/target/i386/hvf/hvf-cpu.c +++ b/target/i386/hvf/hvf-cpu.c @@ -21,8 +21,6 @@ static void hvf_cpu_max_instance_init(X86CPU *cpu) { CPUX86State *env = &cpu->env; - host_cpu_max_instance_init(cpu); - env->cpuid_min_level = hvf_get_supported_cpuid(0x0, 0, R_EAX); env->cpuid_min_xlevel = diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index 6fed353548..a99b876464 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -109,8 +109,6 @@ static void kvm_cpu_max_instance_init(X86CPU *cpu) CPUX86State *env = &cpu->env; KVMState *s = kvm_state; - host_cpu_max_instance_init(cpu); - object_property_set_bool(OBJECT(cpu), "pmu", true, &error_abort); if (lmce_supported()) { From b57999bb258349fabe497c8ff70277b5e5b281e2 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 3 Jul 2025 10:40:17 +0800 Subject: [PATCH 2156/2760] i386/tdx: Remove enumeration of GetQuote in tdx_handle_get_tdvmcall_info() GHCI is finalized with the being one of the base VMCALLs, and not enuemrated via . Adjust tdx_handle_get_tdvmcall_info() to match with GHCI. Opportunistically fix the wrong indentation and explicitly set the ret to TDG_VP_VMCALL_SUCCESS (in case KVM leaves unexpected value). Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250703024021.3559286-2-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 6 ++++-- target/i386/kvm/tdx.h | 2 -- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index e809e4b2df..8c661c3ecf 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -1259,13 +1259,15 @@ void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run) void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) { if (run->tdx.get_tdvmcall_info.leaf != 1) { - return; + return; } - run->tdx.get_tdvmcall_info.r11 = TDG_VP_VMCALL_SUBFUNC_GET_QUOTE; + run->tdx.get_tdvmcall_info.r11 = 0; run->tdx.get_tdvmcall_info.r12 = 0; run->tdx.get_tdvmcall_info.r13 = 0; run->tdx.get_tdvmcall_info.r14 = 0; + + run->tdx.get_tdvmcall_info.ret = TDG_VP_VMCALL_SUCCESS; } static void tdx_panicked_on_fatal_error(X86CPU *cpu, uint64_t error_code, diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index 35a09c19c5..d439078a87 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -32,8 +32,6 @@ typedef struct TdxGuestClass { #define TDG_VP_VMCALL_GPA_INUSE 0x8000000000000001ULL #define TDG_VP_VMCALL_ALIGN_ERROR 0x8000000000000002ULL -#define TDG_VP_VMCALL_SUBFUNC_GET_QUOTE 0x0000000000000001ULL - enum TdxRamType { TDX_RAM_UNACCEPTED, TDX_RAM_ADDED, From 25c98a135001559be905a0399669e5cdb3b0a613 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 3 Jul 2025 10:40:18 +0800 Subject: [PATCH 2157/2760] update Linux headers to KVM tree master To fetch the update of TDX Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250703024021.3559286-3-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- linux-headers/asm-x86/kvm.h | 8 +++++++- linux-headers/linux/kvm.h | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/linux-headers/asm-x86/kvm.h b/linux-headers/asm-x86/kvm.h index cd275ae76d..f0c1a730d9 100644 --- a/linux-headers/asm-x86/kvm.h +++ b/linux-headers/asm-x86/kvm.h @@ -963,7 +963,13 @@ struct kvm_tdx_cmd { struct kvm_tdx_capabilities { __u64 supported_attrs; __u64 supported_xfam; - __u64 reserved[254]; + + __u64 kernel_tdvmcallinfo_1_r11; + __u64 user_tdvmcallinfo_1_r11; + __u64 kernel_tdvmcallinfo_1_r12; + __u64 user_tdvmcallinfo_1_r12; + + __u64 reserved[250]; /* Configurable CPUID bits for userspace */ struct kvm_cpuid2 cpuid; diff --git a/linux-headers/linux/kvm.h b/linux-headers/linux/kvm.h index 0690743944..32c5885a3c 100644 --- a/linux-headers/linux/kvm.h +++ b/linux-headers/linux/kvm.h @@ -459,6 +459,10 @@ struct kvm_run { __u64 leaf; __u64 r11, r12, r13, r14; } get_tdvmcall_info; + struct { + __u64 ret; + __u64 vector; + } setup_event_notify; }; } tdx; /* Fix the size of the union. */ From 55be385b10658a2372f944fa41aaba016e1e8433 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 3 Jul 2025 10:40:19 +0800 Subject: [PATCH 2158/2760] i386/tdx: Set value of based on capabilities of both KVM and QEMU KVM reports the supported TDVMCALL sub leafs in TDX capabilities. one for kernel-supported TDVMCALLs (userspace can set those blindly) and one for user-supported TDVMCALLs (userspace can set those if it knows how to handle them) Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250703024021.3559286-4-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 8c661c3ecf..10dfb80d22 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -1256,14 +1256,21 @@ void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run) g_free(task); } +#define SUPPORTED_TDVMCALLINFO_1_R11 (0) +#define SUPPORTED_TDVMCALLINFO_1_R12 (0) + void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) { if (run->tdx.get_tdvmcall_info.leaf != 1) { return; } - run->tdx.get_tdvmcall_info.r11 = 0; - run->tdx.get_tdvmcall_info.r12 = 0; + run->tdx.get_tdvmcall_info.r11 = (tdx_caps->user_tdvmcallinfo_1_r11 & + SUPPORTED_TDVMCALLINFO_1_R11) | + tdx_caps->kernel_tdvmcallinfo_1_r11; + run->tdx.get_tdvmcall_info.r12 = (tdx_caps->user_tdvmcallinfo_1_r12 & + SUPPORTED_TDVMCALLINFO_1_R12) | + tdx_caps->kernel_tdvmcallinfo_1_r12; run->tdx.get_tdvmcall_info.r13 = 0; run->tdx.get_tdvmcall_info.r14 = 0; From efa742b23eff2a799c196d756bd506fe74e96fdc Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 3 Jul 2025 10:40:20 +0800 Subject: [PATCH 2159/2760] i386/tdx: handle TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT Record the interrupt vector and the apic id of the vcpu that calls TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT. Inject the interrupt to TD guest to notify the completion of when notify interrupt vector is valid. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250703024021.3559286-5-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 3 +++ target/i386/kvm/tdx-stub.c | 4 ++++ target/i386/kvm/tdx.c | 48 +++++++++++++++++++++++++++++++++++++- target/i386/kvm/tdx.h | 7 ++++++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index 234878c613..fc58a23b30 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -6182,6 +6182,9 @@ int kvm_arch_handle_exit(CPUState *cs, struct kvm_run *run) case TDVMCALL_GET_TD_VM_CALL_INFO: tdx_handle_get_tdvmcall_info(cpu, run); break; + case TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT: + tdx_handle_setup_event_notify_interrupt(cpu, run); + break; } ret = 0; break; diff --git a/target/i386/kvm/tdx-stub.c b/target/i386/kvm/tdx-stub.c index 76fee49eff..1f0e108a69 100644 --- a/target/i386/kvm/tdx-stub.c +++ b/target/i386/kvm/tdx-stub.c @@ -26,3 +26,7 @@ void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run) void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) { } + +void tdx_handle_setup_event_notify_interrupt(X86CPU *cpu, struct kvm_run *run) +{ +} diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 10dfb80d22..fb31071dd8 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -28,10 +28,13 @@ #include "cpu.h" #include "cpu-internal.h" #include "host-cpu.h" +#include "hw/i386/apic_internal.h" +#include "hw/i386/apic-msidef.h" #include "hw/i386/e820_memory_layout.h" #include "hw/i386/tdvf.h" #include "hw/i386/x86.h" #include "hw/i386/tdvf-hob.h" +#include "hw/pci/msi.h" #include "kvm_i386.h" #include "tdx.h" #include "tdx-quote-generator.h" @@ -1123,6 +1126,28 @@ int tdx_parse_tdvf(void *flash_ptr, int size) return tdvf_parse_metadata(&tdx_guest->tdvf, flash_ptr, size); } +static void tdx_inject_interrupt(uint32_t apicid, uint32_t vector) +{ + int ret; + + if (vector < 32 || vector > 255) { + return; + } + + MSIMessage msg = { + .address = ((apicid & 0xff) << MSI_ADDR_DEST_ID_SHIFT) | + (((uint64_t)apicid & 0xffffff00) << 32), + .data = vector | (APIC_DM_FIXED << MSI_DATA_DELIVERY_MODE_SHIFT), + }; + + ret = kvm_irqchip_send_msi(kvm_state, msg); + if (ret < 0) { + /* In this case, no better way to tell it to guest. Log it. */ + error_report("TDX: injection interrupt %d failed, interrupt lost (%s).", + vector, strerror(-ret)); + } +} + static void tdx_get_quote_completion(TdxGenerateQuoteTask *task) { TdxGuest *tdx = task->opaque; @@ -1154,6 +1179,9 @@ static void tdx_get_quote_completion(TdxGenerateQuoteTask *task) error_report("TDX: get-quote: failed to update GetQuote header."); } + tdx_inject_interrupt(tdx_guest->event_notify_apicid, + tdx_guest->event_notify_vector); + g_free(task->send_data); g_free(task->receive_buf); g_free(task); @@ -1256,7 +1284,7 @@ void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run) g_free(task); } -#define SUPPORTED_TDVMCALLINFO_1_R11 (0) +#define SUPPORTED_TDVMCALLINFO_1_R11 (TDG_VP_VMCALL_SUBFUNC_SET_EVENT_NOTIFY_INTERRUPT) #define SUPPORTED_TDVMCALLINFO_1_R12 (0) void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) @@ -1277,6 +1305,21 @@ void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run) run->tdx.get_tdvmcall_info.ret = TDG_VP_VMCALL_SUCCESS; } +void tdx_handle_setup_event_notify_interrupt(X86CPU *cpu, struct kvm_run *run) +{ + uint64_t vector = run->tdx.setup_event_notify.vector; + + if (vector >= 32 && vector < 256) { + qemu_mutex_lock(&tdx_guest->lock); + tdx_guest->event_notify_vector = vector; + tdx_guest->event_notify_apicid = cpu->apic_id; + qemu_mutex_unlock(&tdx_guest->lock); + run->tdx.setup_event_notify.ret = TDG_VP_VMCALL_SUCCESS; + } else { + run->tdx.setup_event_notify.ret = TDG_VP_VMCALL_INVALID_OPERAND; + } +} + static void tdx_panicked_on_fatal_error(X86CPU *cpu, uint64_t error_code, char *message, uint64_t gpa) { @@ -1477,6 +1520,9 @@ static void tdx_guest_init(Object *obj) NULL, NULL); qemu_mutex_init(&tdx->lock); + + tdx->event_notify_vector = -1; + tdx->event_notify_apicid = -1; } static void tdx_guest_finalize(Object *obj) diff --git a/target/i386/kvm/tdx.h b/target/i386/kvm/tdx.h index d439078a87..1c38faf983 100644 --- a/target/i386/kvm/tdx.h +++ b/target/i386/kvm/tdx.h @@ -25,6 +25,7 @@ typedef struct TdxGuestClass { #define TDVMCALL_GET_TD_VM_CALL_INFO 0x10000 #define TDVMCALL_GET_QUOTE 0x10002 +#define TDVMCALL_SETUP_EVENT_NOTIFY_INTERRUPT 0x10004 #define TDG_VP_VMCALL_SUCCESS 0x0000000000000000ULL #define TDG_VP_VMCALL_RETRY 0x0000000000000001ULL @@ -32,6 +33,8 @@ typedef struct TdxGuestClass { #define TDG_VP_VMCALL_GPA_INUSE 0x8000000000000001ULL #define TDG_VP_VMCALL_ALIGN_ERROR 0x8000000000000002ULL +#define TDG_VP_VMCALL_SUBFUNC_SET_EVENT_NOTIFY_INTERRUPT BIT_ULL(1) + enum TdxRamType { TDX_RAM_UNACCEPTED, TDX_RAM_ADDED, @@ -64,6 +67,9 @@ typedef struct TdxGuest { /* GetQuote */ SocketAddress *qg_sock_addr; int num; + + uint32_t event_notify_vector; + uint32_t event_notify_apicid; } TdxGuest; #ifdef CONFIG_TDX @@ -78,5 +84,6 @@ int tdx_parse_tdvf(void *flash_ptr, int size); int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run); void tdx_handle_get_quote(X86CPU *cpu, struct kvm_run *run); void tdx_handle_get_tdvmcall_info(X86CPU *cpu, struct kvm_run *run); +void tdx_handle_setup_event_notify_interrupt(X86CPU *cpu, struct kvm_run *run); #endif /* QEMU_I386_TDX_H */ From b28f6d5c16f19f8c56926c10929db29f913895ad Mon Sep 17 00:00:00 2001 From: Zhenzhong Duan Date: Wed, 9 Jul 2025 23:55:38 -0400 Subject: [PATCH 2160/2760] i386/tdx: Fix the report of gpa in QAPI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Gpa is defined in QAPI but never reported to monitor because has_gpa is never set to ture. Fix it by setting has_gpa to ture when TDX_REPORT_FATAL_ERROR_GPA_VALID is set in error_code. Fixes: 6e250463b08b ("i386/tdx: Wire TDX_REPORT_FATAL_ERROR with GuestPanic facility") Signed-off-by: Zhenzhong Duan Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250710035538.303136-1-zhenzhong.duan@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index fb31071dd8..7d69d6d7b0 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -1321,7 +1321,8 @@ void tdx_handle_setup_event_notify_interrupt(X86CPU *cpu, struct kvm_run *run) } static void tdx_panicked_on_fatal_error(X86CPU *cpu, uint64_t error_code, - char *message, uint64_t gpa) + char *message, bool has_gpa, + uint64_t gpa) { GuestPanicInformation *panic_info; @@ -1330,6 +1331,7 @@ static void tdx_panicked_on_fatal_error(X86CPU *cpu, uint64_t error_code, panic_info->u.tdx.error_code = (uint32_t) error_code; panic_info->u.tdx.message = message; panic_info->u.tdx.gpa = gpa; + panic_info->u.tdx.has_gpa = has_gpa; qemu_system_guest_panicked(panic_info); } @@ -1349,6 +1351,7 @@ int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) char *message = NULL; uint64_t *tmp; uint64_t gpa = -1ull; + bool has_gpa = false; if (error_code & 0xffff) { error_report("TDX: REPORT_FATAL_ERROR: invalid error code: 0x%"PRIx64, @@ -1381,9 +1384,10 @@ int tdx_handle_report_fatal_error(X86CPU *cpu, struct kvm_run *run) if (error_code & TDX_REPORT_FATAL_ERROR_GPA_VALID) { gpa = run->system_event.data[R_R13]; + has_gpa = true; } - tdx_panicked_on_fatal_error(cpu, error_code, message, gpa); + tdx_panicked_on_fatal_error(cpu, error_code, message, has_gpa, gpa); return -1; } From 84fe49d94ac72d7fd226a65d2250c6294885561d Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 16:00:36 +0100 Subject: [PATCH 2161/2760] meson: Add optional dependency on IGVM library MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The IGVM library allows Independent Guest Virtual Machine files to be parsed and processed. IGVM files are used to configure guest memory layout, initial processor state and other configuration pertaining to secure virtual machines. This adds the --enable-igvm configure option, enabled by default, which attempts to locate and link against the IGVM library via pkgconfig and sets CONFIG_IGVM if found. The library is added to the system_ss target in backends/meson.build where the IGVM parsing will be performed by the ConfidentialGuestSupport object. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Daniel P. Berrangé Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/45945a83a638c3f08e68c025f378e7b7f4f6d593.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- backends/meson.build | 3 +++ meson.build | 8 ++++++++ meson_options.txt | 2 ++ scripts/meson-buildoptions.sh | 3 +++ 4 files changed, 16 insertions(+) diff --git a/backends/meson.build b/backends/meson.build index 9b88d22685..ac0fac7845 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -34,6 +34,9 @@ if have_vhost_user_crypto endif system_ss.add(when: gio, if_true: files('dbus-vmstate.c')) system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c')) +if igvm.found() + system_ss.add(igvm) +endif system_ss.add(when: 'CONFIG_SPDM_SOCKET', if_true: files('spdm-socket.c')) diff --git a/meson.build b/meson.build index b5f74aa37a..200352c244 100644 --- a/meson.build +++ b/meson.build @@ -1424,6 +1424,12 @@ if host_os == 'linux' and (have_system or have_tools) method: 'pkg-config', required: get_option('libudev')) endif +igvm = not_found +if not get_option('igvm').auto() or have_system + igvm = dependency('igvm', version: '>= 0.3.0', + method: 'pkg-config', + required: get_option('igvm')) +endif mpathlibs = [libudev] mpathpersist = not_found @@ -2601,6 +2607,7 @@ config_host_data.set('CONFIG_CFI', get_option('cfi')) config_host_data.set('CONFIG_SELINUX', selinux.found()) config_host_data.set('CONFIG_XEN_BACKEND', xen.found()) config_host_data.set('CONFIG_LIBDW', libdw.found()) +config_host_data.set('CONFIG_IGVM', igvm.found()) if xen.found() # protect from xen.version() having less than three components xen_version = xen.version().split('.') + ['0', '0'] @@ -4965,6 +4972,7 @@ summary_info += {'seccomp support': seccomp} summary_info += {'GlusterFS support': glusterfs} summary_info += {'hv-balloon support': hv_balloon} summary_info += {'TPM support': have_tpm} +summary_info += {'IGVM support': igvm} summary_info += {'libssh support': libssh} summary_info += {'lzo support': lzo} summary_info += {'snappy support': snappy} diff --git a/meson_options.txt b/meson_options.txt index a442be2995..1e429311a2 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -117,6 +117,8 @@ option('tpm', type : 'feature', value : 'auto', description: 'TPM support') option('valgrind', type : 'feature', value: 'auto', description: 'valgrind debug support for coroutine stacks') +option('igvm', type: 'feature', value: 'auto', + description: 'Independent Guest Virtual Machine (IGVM) file support') # Do not enable it by default even for Mingw32, because it doesn't # work on Wine. diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 73e0770f42..7851540445 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -130,6 +130,7 @@ meson_options_help() { printf "%s\n" ' hv-balloon hv-balloon driver (requires Glib 2.68+ GTree API)' printf "%s\n" ' hvf HVF acceleration support' printf "%s\n" ' iconv Font glyph conversion support' + printf "%s\n" ' igvm IGVM file support' printf "%s\n" ' jack JACK sound support' printf "%s\n" ' keyring Linux keyring support' printf "%s\n" ' kvm KVM acceleration support' @@ -346,6 +347,8 @@ _meson_option_parse() { --iasl=*) quote_sh "-Diasl=$2" ;; --enable-iconv) printf "%s" -Diconv=enabled ;; --disable-iconv) printf "%s" -Diconv=disabled ;; + --enable-igvm) printf "%s" -Digvm=enabled ;; + --disable-igvm) printf "%s" -Digvm=disabled ;; --includedir=*) quote_sh "-Dincludedir=$2" ;; --enable-install-blobs) printf "%s" -Dinstall_blobs=true ;; --disable-install-blobs) printf "%s" -Dinstall_blobs=false ;; From e7ed19507bc4f63162c578037db7d98178fbf54d Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 16:03:10 +0100 Subject: [PATCH 2162/2760] backends/confidential-guest-support: Add functions to support IGVM In preparation for supporting the processing of IGVM files to configure guests, this adds a set of functions to ConfidentialGuestSupport allowing configuration of secure virtual machines that can be implemented for each supported isolation platform type such as Intel TDX or AMD SEV-SNP. These functions will be called by IGVM processing code in subsequent patches. This commit provides a default implementation of the functions that either perform no action or generate an error when they are called. Targets that support ConfidentalGuestSupport should override these implementations. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/23e34a106da87427899f93178102e4a6ef50c966.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- backends/confidential-guest-support.c | 31 ++++++++++ include/system/confidential-guest-support.h | 67 +++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/backends/confidential-guest-support.c b/backends/confidential-guest-support.c index 8ff7bfa857..c5bef1fbfa 100644 --- a/backends/confidential-guest-support.c +++ b/backends/confidential-guest-support.c @@ -14,15 +14,46 @@ #include "qemu/osdep.h" #include "system/confidential-guest-support.h" +#include "qapi/error.h" OBJECT_DEFINE_ABSTRACT_TYPE(ConfidentialGuestSupport, confidential_guest_support, CONFIDENTIAL_GUEST_SUPPORT, OBJECT) +static bool check_support(ConfidentialGuestPlatformType platform, + uint16_t platform_version, uint8_t highest_vtl, + uint64_t shared_gpa_boundary) +{ + /* Default: no support. */ + return false; +} + +static int set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, + ConfidentialGuestPageType memory_type, + uint16_t cpu_index, Error **errp) +{ + error_setg(errp, + "Setting confidential guest state is not supported for this platform"); + return -1; +} + +static int get_mem_map_entry(int index, ConfidentialGuestMemoryMapEntry *entry, + Error **errp) +{ + error_setg( + errp, + "Obtaining the confidential guest memory map is not supported for this platform"); + return -1; +} + static void confidential_guest_support_class_init(ObjectClass *oc, const void *data) { + ConfidentialGuestSupportClass *cgsc = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); + cgsc->check_support = check_support; + cgsc->set_guest_state = set_guest_state; + cgsc->get_mem_map_entry = get_mem_map_entry; } static void confidential_guest_support_init(Object *obj) diff --git a/include/system/confidential-guest-support.h b/include/system/confidential-guest-support.h index ea46b50c56..79ecd21f42 100644 --- a/include/system/confidential-guest-support.h +++ b/include/system/confidential-guest-support.h @@ -19,6 +19,7 @@ #define QEMU_CONFIDENTIAL_GUEST_SUPPORT_H #include "qom/object.h" +#include "exec/hwaddr.h" #define TYPE_CONFIDENTIAL_GUEST_SUPPORT "confidential-guest-support" OBJECT_DECLARE_TYPE(ConfidentialGuestSupport, @@ -26,6 +27,36 @@ OBJECT_DECLARE_TYPE(ConfidentialGuestSupport, CONFIDENTIAL_GUEST_SUPPORT) +typedef enum ConfidentialGuestPlatformType { + CGS_PLATFORM_SEV, + CGS_PLATFORM_SEV_ES, + CGS_PLATFORM_SEV_SNP, +} ConfidentialGuestPlatformType; + +typedef enum ConfidentialGuestMemoryType { + CGS_MEM_RAM, + CGS_MEM_RESERVED, + CGS_MEM_ACPI, + CGS_MEM_NVS, + CGS_MEM_UNUSABLE, +} ConfidentialGuestMemoryType; + +typedef struct ConfidentialGuestMemoryMapEntry { + uint64_t gpa; + uint64_t size; + ConfidentialGuestMemoryType type; +} ConfidentialGuestMemoryMapEntry; + +typedef enum ConfidentialGuestPageType { + CGS_PAGE_TYPE_NORMAL, + CGS_PAGE_TYPE_VMSA, + CGS_PAGE_TYPE_ZERO, + CGS_PAGE_TYPE_UNMEASURED, + CGS_PAGE_TYPE_SECRETS, + CGS_PAGE_TYPE_CPUID, + CGS_PAGE_TYPE_REQUIRED_MEMORY, +} ConfidentialGuestPageType; + struct ConfidentialGuestSupport { Object parent; @@ -64,6 +95,42 @@ typedef struct ConfidentialGuestSupportClass { int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp); int (*kvm_reset)(ConfidentialGuestSupport *cgs, Error **errp); + + /* + * Check to see if this confidential guest supports a particular + * platform or configuration. + * + * Return true if supported or false if not supported. + */ + bool (*check_support)(ConfidentialGuestPlatformType platform, + uint16_t platform_version, uint8_t highest_vtl, + uint64_t shared_gpa_boundary); + + /* + * Configure part of the state of a guest for a particular set of data, page + * type and gpa. This can be used for example to pre-populate and measure + * guest memory contents, define private ranges or set the initial CPU state + * for one or more CPUs. + * + * If memory_type is CGS_PAGE_TYPE_VMSA then ptr points to the initial CPU + * context for a virtual CPU. The format of the data depends on the type of + * confidential virtual machine. For example, for SEV-ES ptr will point to a + * vmcb_save_area structure that should be copied into guest memory at the + * address specified in gpa. The cpu_index parameter contains the index of + * the CPU the VMSA applies to. + */ + int (*set_guest_state)(hwaddr gpa, uint8_t *ptr, uint64_t len, + ConfidentialGuestPageType memory_type, + uint16_t cpu_index, Error **errp); + + /* + * Iterate the system memory map, getting the entry with the given index + * that can be populated into guest memory. + * + * Returns 0 for ok, 1 if the index is out of range and -1 on error. + */ + int (*get_mem_map_entry)(int index, ConfidentialGuestMemoryMapEntry *entry, + Error **errp); } ConfidentialGuestSupportClass; static inline int confidential_guest_kvm_init(ConfidentialGuestSupport *cgs, From c1d466d267cf40093c9489075906b385459a195f Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 16:10:22 +0100 Subject: [PATCH 2163/2760] backends/igvm: Add IGVM loader and configuration Adds an IGVM loader to QEMU which processes a given IGVM file and applies the directives within the file to the current guest configuration. The IGVM loader can be used to configure both confidential and non-confidential guests. For confidential guests, the ConfidentialGuestSupport object for the system is used to encrypt memory, apply the initial CPU state and perform other confidential guest operations. The loader is configured via a new IgvmCfg QOM object which allows the user to provide a path to the IGVM file to process. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Stefano Garzarella Link: https://lore.kernel.org/r/ae3a07d8f514d93845a9c16bb155c847cb567b0d.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- backends/igvm-cfg.c | 51 +++ backends/igvm.c | 807 ++++++++++++++++++++++++++++++++++++++ backends/igvm.h | 22 ++ backends/meson.build | 2 + include/system/igvm-cfg.h | 46 +++ qapi/qom.json | 17 + 6 files changed, 945 insertions(+) create mode 100644 backends/igvm-cfg.c create mode 100644 backends/igvm.c create mode 100644 backends/igvm.h create mode 100644 include/system/igvm-cfg.h diff --git a/backends/igvm-cfg.c b/backends/igvm-cfg.c new file mode 100644 index 0000000000..45df63e06c --- /dev/null +++ b/backends/igvm-cfg.c @@ -0,0 +1,51 @@ +/* + * QEMU IGVM interface + * + * Copyright (C) 2023-2024 SUSE + * + * Authors: + * Roy Hopkins + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "system/igvm-cfg.h" +#include "igvm.h" +#include "qom/object_interfaces.h" + +static char *get_igvm(Object *obj, Error **errp) +{ + IgvmCfg *igvm = IGVM_CFG(obj); + return g_strdup(igvm->filename); +} + +static void set_igvm(Object *obj, const char *value, Error **errp) +{ + IgvmCfg *igvm = IGVM_CFG(obj); + g_free(igvm->filename); + igvm->filename = g_strdup(value); +} + +OBJECT_DEFINE_TYPE_WITH_INTERFACES(IgvmCfg, igvm_cfg, IGVM_CFG, OBJECT, + { TYPE_USER_CREATABLE }, { NULL }) + +static void igvm_cfg_class_init(ObjectClass *oc, const void *data) +{ + IgvmCfgClass *igvmc = IGVM_CFG_CLASS(oc); + + object_class_property_add_str(oc, "file", get_igvm, set_igvm); + object_class_property_set_description(oc, "file", + "Set the IGVM filename to use"); + + igvmc->process = qigvm_process_file; +} + +static void igvm_cfg_init(Object *obj) +{ +} + +static void igvm_cfg_finalize(Object *obj) +{ +} diff --git a/backends/igvm.c b/backends/igvm.c new file mode 100644 index 0000000000..2a31021d44 --- /dev/null +++ b/backends/igvm.c @@ -0,0 +1,807 @@ +/* + * QEMU IGVM configuration backend for guests + * + * Copyright (C) 2023-2024 SUSE + * + * Authors: + * Roy Hopkins + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" + +#include "igvm.h" +#include "qapi/error.h" +#include "system/memory.h" +#include "system/address-spaces.h" +#include "hw/core/cpu.h" + +#include +#include + +typedef struct QIgvmParameterData { + QTAILQ_ENTRY(QIgvmParameterData) next; + uint8_t *data; + uint32_t size; + uint32_t index; +} QIgvmParameterData; + +/* + * QIgvm contains the information required during processing + * of a single IGVM file. + */ +typedef struct QIgvm { + IgvmHandle file; + ConfidentialGuestSupport *cgs; + ConfidentialGuestSupportClass *cgsc; + uint32_t compatibility_mask; + unsigned current_header_index; + QTAILQ_HEAD(, QIgvmParameterData) parameter_data; + + /* These variables keep track of contiguous page regions */ + IGVM_VHS_PAGE_DATA region_prev_page_data; + uint64_t region_start; + unsigned region_start_index; + unsigned region_last_index; + unsigned region_page_count; +} QIgvm; + +static int qigvm_directive_page_data(QIgvm *ctx, const uint8_t *header_data, + Error **errp); +static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data, + Error **errp); +static int qigvm_directive_parameter_area(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); +static int qigvm_directive_parameter_insert(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); +static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data, + Error **errp); +static int qigvm_directive_vp_count(QIgvm *ctx, const uint8_t *header_data, + Error **errp); +static int qigvm_directive_environment_info(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); +static int qigvm_directive_required_memory(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); + +struct QIGVMHandler { + uint32_t type; + uint32_t section; + int (*handler)(QIgvm *ctx, const uint8_t *header_data, Error **errp); +}; + +static struct QIGVMHandler handlers[] = { + { IGVM_VHT_PAGE_DATA, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_page_data }, + { IGVM_VHT_VP_CONTEXT, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_vp_context }, + { IGVM_VHT_PARAMETER_AREA, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_parameter_area }, + { IGVM_VHT_PARAMETER_INSERT, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_parameter_insert }, + { IGVM_VHT_MEMORY_MAP, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_memory_map }, + { IGVM_VHT_VP_COUNT_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_vp_count }, + { IGVM_VHT_ENVIRONMENT_INFO_PARAMETER, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_environment_info }, + { IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_required_memory }, +}; + +static int qigvm_handler(QIgvm *ctx, uint32_t type, Error **errp) +{ + size_t handler; + IgvmHandle header_handle; + const uint8_t *header_data; + int result; + + for (handler = 0; handler < G_N_ELEMENTS(handlers); handler++) { + if (handlers[handler].type != type) { + continue; + } + header_handle = igvm_get_header(ctx->file, handlers[handler].section, + ctx->current_header_index); + if (header_handle < 0) { + error_setg( + errp, + "IGVM file is invalid: Failed to read directive header (code: %d)", + (int)header_handle); + return -1; + } + header_data = igvm_get_buffer(ctx->file, header_handle) + + sizeof(IGVM_VHS_VARIABLE_HEADER); + result = handlers[handler].handler(ctx, header_data, errp); + igvm_free_buffer(ctx->file, header_handle); + return result; + } + error_setg(errp, + "IGVM: Unknown header type encountered when processing file: " + "(type 0x%X)", + type); + return -1; +} + +static void *qigvm_prepare_memory(QIgvm *ctx, uint64_t addr, uint64_t size, + int region_identifier, Error **errp) +{ + ERRP_GUARD(); + MemoryRegion *igvm_pages = NULL; + Int128 gpa_region_size; + MemoryRegionSection mrs = + memory_region_find(get_system_memory(), addr, size); + if (mrs.mr) { + if (!memory_region_is_ram(mrs.mr)) { + memory_region_unref(mrs.mr); + error_setg( + errp, + "Processing of IGVM file failed: Could not prepare memory " + "at address 0x%lX due to existing non-RAM region", + addr); + return NULL; + } + + gpa_region_size = int128_make64(size); + if (int128_lt(mrs.size, gpa_region_size)) { + memory_region_unref(mrs.mr); + error_setg( + errp, + "Processing of IGVM file failed: Could not prepare memory " + "at address 0x%lX: region size exceeded", + addr); + return NULL; + } + return qemu_map_ram_ptr(mrs.mr->ram_block, mrs.offset_within_region); + } else { + /* + * The region_identifier is the is the index of the IGVM directive that + * contains the page with the lowest GPA in the region. This will + * generate a unique region name. + */ + g_autofree char *region_name = + g_strdup_printf("igvm.%X", region_identifier); + igvm_pages = g_new0(MemoryRegion, 1); + if (ctx->cgs && ctx->cgs->require_guest_memfd) { + if (!memory_region_init_ram_guest_memfd(igvm_pages, NULL, + region_name, size, errp)) { + return NULL; + } + } else { + if (!memory_region_init_ram(igvm_pages, NULL, region_name, size, + errp)) { + return NULL; + } + } + memory_region_add_subregion(get_system_memory(), addr, igvm_pages); + return memory_region_get_ram_ptr(igvm_pages); + } +} + +static int qigvm_type_to_cgs_type(IgvmPageDataType memory_type, bool unmeasured, + bool zero) +{ + switch (memory_type) { + case IGVM_PAGE_DATA_TYPE_NORMAL: { + if (unmeasured) { + return CGS_PAGE_TYPE_UNMEASURED; + } else { + return zero ? CGS_PAGE_TYPE_ZERO : CGS_PAGE_TYPE_NORMAL; + } + } + case IGVM_PAGE_DATA_TYPE_SECRETS: + return CGS_PAGE_TYPE_SECRETS; + case IGVM_PAGE_DATA_TYPE_CPUID_DATA: + return CGS_PAGE_TYPE_CPUID; + case IGVM_PAGE_DATA_TYPE_CPUID_XF: + return CGS_PAGE_TYPE_CPUID; + default: + return -1; + } +} + +static bool qigvm_page_attrs_equal(IgvmHandle igvm, unsigned header_index, + const IGVM_VHS_PAGE_DATA *page_1, + const IGVM_VHS_PAGE_DATA *page_2) +{ + IgvmHandle data_handle1, data_handle2; + + /* + * If one page has data and the other doesn't then this results in different + * page types: NORMAL vs ZERO. + */ + data_handle1 = igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE, + header_index - 1); + data_handle2 = + igvm_get_header_data(igvm, IGVM_HEADER_SECTION_DIRECTIVE, header_index); + if ((data_handle1 == IGVMAPI_NO_DATA || + data_handle2 == IGVMAPI_NO_DATA) && + data_handle1 != data_handle2) { + return false; + } + return ((*(const uint32_t *)&page_1->flags == + *(const uint32_t *)&page_2->flags) && + (page_1->data_type == page_2->data_type) && + (page_1->compatibility_mask == page_2->compatibility_mask)); +} + +static int qigvm_process_mem_region(QIgvm *ctx, unsigned start_index, + uint64_t gpa_start, unsigned page_count, + const IgvmPageDataFlags *flags, + const IgvmPageDataType page_type, + Error **errp) +{ + uint8_t *region; + IgvmHandle data_handle; + const void *data; + uint32_t data_size; + unsigned page_index; + bool zero = true; + const uint64_t page_size = flags->is_2mb_page ? 0x200000 : 0x1000; + int result; + int cgs_page_type; + + region = qigvm_prepare_memory(ctx, gpa_start, page_count * page_size, + start_index, errp); + if (!region) { + return -1; + } + + for (page_index = 0; page_index < page_count; page_index++) { + data_handle = igvm_get_header_data( + ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, page_index + start_index); + if (data_handle == IGVMAPI_NO_DATA) { + /* No data indicates a zero page */ + memset(®ion[page_index * page_size], 0, page_size); + } else if (data_handle < 0) { + error_setg( + errp, + "IGVM file contains invalid page data for directive with " + "index %d", + page_index + start_index); + return -1; + } else { + zero = false; + data_size = igvm_get_buffer_size(ctx->file, data_handle); + if (data_size < page_size) { + memset(®ion[page_index * page_size], 0, page_size); + } else if (data_size > page_size) { + error_setg(errp, + "IGVM file contains page data with invalid size for " + "directive with index %d", + page_index + start_index); + return -1; + } + data = igvm_get_buffer(ctx->file, data_handle); + memcpy(®ion[page_index * page_size], data, data_size); + igvm_free_buffer(ctx->file, data_handle); + } + } + + /* + * If a confidential guest support object is provided then use it to set the + * guest state. + */ + if (ctx->cgs) { + cgs_page_type = + qigvm_type_to_cgs_type(page_type, flags->unmeasured, zero); + if (cgs_page_type < 0) { + error_setg(errp, + "Invalid page type in IGVM file. Directives: %d to %d, " + "page type: %d", + start_index, start_index + page_count, page_type); + return -1; + } + + result = ctx->cgsc->set_guest_state( + gpa_start, region, page_size * page_count, cgs_page_type, 0, errp); + if (result < 0) { + return result; + } + } + return 0; +} + +static int qigvm_process_mem_page(QIgvm *ctx, + const IGVM_VHS_PAGE_DATA *page_data, + Error **errp) +{ + if (page_data) { + if (ctx->region_page_count == 0) { + ctx->region_start = page_data->gpa; + ctx->region_start_index = ctx->current_header_index; + } else { + if (!qigvm_page_attrs_equal(ctx->file, ctx->current_header_index, + page_data, + &ctx->region_prev_page_data) || + ((ctx->region_prev_page_data.gpa + + (ctx->region_prev_page_data.flags.is_2mb_page ? 0x200000 : + 0x1000)) != + page_data->gpa) || + (ctx->region_last_index != (ctx->current_header_index - 1))) { + /* End of current region */ + if (qigvm_process_mem_region( + ctx, ctx->region_start_index, ctx->region_start, + ctx->region_page_count, + &ctx->region_prev_page_data.flags, + ctx->region_prev_page_data.data_type, errp) < 0) { + return -1; + } + ctx->region_page_count = 0; + ctx->region_start = page_data->gpa; + ctx->region_start_index = ctx->current_header_index; + } + } + memcpy(&ctx->region_prev_page_data, page_data, + sizeof(ctx->region_prev_page_data)); + ctx->region_last_index = ctx->current_header_index; + ctx->region_page_count++; + } else { + if (ctx->region_page_count > 0) { + if (qigvm_process_mem_region( + ctx, ctx->region_start_index, ctx->region_start, + ctx->region_page_count, &ctx->region_prev_page_data.flags, + ctx->region_prev_page_data.data_type, errp) < 0) { + return -1; + } + ctx->region_page_count = 0; + } + } + return 0; +} + +static int qigvm_directive_page_data(QIgvm *ctx, const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PAGE_DATA *page_data = + (const IGVM_VHS_PAGE_DATA *)header_data; + if (page_data->compatibility_mask & ctx->compatibility_mask) { + return qigvm_process_mem_page(ctx, page_data, errp); + } + return 0; +} + +static int qigvm_directive_vp_context(QIgvm *ctx, const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_VP_CONTEXT *vp_context = + (const IGVM_VHS_VP_CONTEXT *)header_data; + IgvmHandle data_handle; + uint8_t *data; + int result; + + if (!(vp_context->compatibility_mask & ctx->compatibility_mask)) { + return 0; + } + + /* + * A confidential guest support object must be provided for setting + * a VP context. + */ + if (!ctx->cgs) { + error_setg( + errp, + "A VP context is present in the IGVM file but is not supported " + "by the current system."); + return -1; + } + + data_handle = igvm_get_header_data(ctx->file, IGVM_HEADER_SECTION_DIRECTIVE, + ctx->current_header_index); + if (data_handle < 0) { + error_setg(errp, "Invalid VP context in IGVM file. Error code: %X", + data_handle); + return -1; + } + + data = (uint8_t *)igvm_get_buffer(ctx->file, data_handle); + result = ctx->cgsc->set_guest_state( + vp_context->gpa, data, igvm_get_buffer_size(ctx->file, data_handle), + CGS_PAGE_TYPE_VMSA, vp_context->vp_index, errp); + igvm_free_buffer(ctx->file, data_handle); + if (result < 0) { + return result; + } + return 0; +} + +static int qigvm_directive_parameter_area(QIgvm *ctx, + const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER_AREA *param_area = + (const IGVM_VHS_PARAMETER_AREA *)header_data; + QIgvmParameterData *param_entry; + + param_entry = g_new0(QIgvmParameterData, 1); + param_entry->size = param_area->number_of_bytes; + param_entry->index = param_area->parameter_area_index; + param_entry->data = g_malloc0(param_entry->size); + + QTAILQ_INSERT_TAIL(&ctx->parameter_data, param_entry, next); + return 0; +} + +static int qigvm_directive_parameter_insert(QIgvm *ctx, + const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER_INSERT *param = + (const IGVM_VHS_PARAMETER_INSERT *)header_data; + QIgvmParameterData *param_entry; + int result; + void *region; + + if (!(param->compatibility_mask & ctx->compatibility_mask)) { + return 0; + } + + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) + { + if (param_entry->index == param->parameter_area_index) { + region = qigvm_prepare_memory(ctx, param->gpa, param_entry->size, + ctx->current_header_index, errp); + if (!region) { + return -1; + } + memcpy(region, param_entry->data, param_entry->size); + g_free(param_entry->data); + param_entry->data = NULL; + + /* + * If a confidential guest support object is provided then use it to + * set the guest state. + */ + if (ctx->cgs) { + result = ctx->cgsc->set_guest_state(param->gpa, region, + param_entry->size, + CGS_PAGE_TYPE_UNMEASURED, 0, + errp); + if (result < 0) { + return -1; + } + } + } + } + return 0; +} + +static int qigvm_cmp_mm_entry(const void *a, const void *b) +{ + const IGVM_VHS_MEMORY_MAP_ENTRY *entry_a = + (const IGVM_VHS_MEMORY_MAP_ENTRY *)a; + const IGVM_VHS_MEMORY_MAP_ENTRY *entry_b = + (const IGVM_VHS_MEMORY_MAP_ENTRY *)b; + if (entry_a->starting_gpa_page_number < entry_b->starting_gpa_page_number) { + return -1; + } else if (entry_a->starting_gpa_page_number > + entry_b->starting_gpa_page_number) { + return 1; + } else { + return 0; + } +} + +static int qigvm_directive_memory_map(QIgvm *ctx, const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data; + QIgvmParameterData *param_entry; + int max_entry_count; + int entry = 0; + IGVM_VHS_MEMORY_MAP_ENTRY *mm_entry; + ConfidentialGuestMemoryMapEntry cgmm_entry; + int retval = 0; + + if (!ctx->cgs) { + error_setg(errp, + "IGVM file contains a memory map but this is not supported " + "by the current system."); + return -1; + } + + /* Find the parameter area that should hold the memory map */ + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) + { + if (param_entry->index == param->parameter_area_index) { + max_entry_count = + param_entry->size / sizeof(IGVM_VHS_MEMORY_MAP_ENTRY); + mm_entry = (IGVM_VHS_MEMORY_MAP_ENTRY *)param_entry->data; + + retval = ctx->cgsc->get_mem_map_entry(entry, &cgmm_entry, errp); + while (retval == 0) { + if (entry > max_entry_count) { + error_setg( + errp, + "IGVM: guest memory map size exceeds parameter area defined in IGVM file"); + return -1; + } + mm_entry[entry].starting_gpa_page_number = cgmm_entry.gpa >> 12; + mm_entry[entry].number_of_pages = cgmm_entry.size >> 12; + + switch (cgmm_entry.type) { + case CGS_MEM_RAM: + mm_entry[entry].entry_type = + IGVM_MEMORY_MAP_ENTRY_TYPE_MEMORY; + break; + case CGS_MEM_RESERVED: + mm_entry[entry].entry_type = + IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED; + break; + case CGS_MEM_ACPI: + mm_entry[entry].entry_type = + IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED; + break; + case CGS_MEM_NVS: + mm_entry[entry].entry_type = + IGVM_MEMORY_MAP_ENTRY_TYPE_PERSISTENT; + break; + case CGS_MEM_UNUSABLE: + mm_entry[entry].entry_type = + IGVM_MEMORY_MAP_ENTRY_TYPE_PLATFORM_RESERVED; + break; + } + retval = + ctx->cgsc->get_mem_map_entry(++entry, &cgmm_entry, errp); + } + if (retval < 0) { + return retval; + } + /* The entries need to be sorted */ + qsort(mm_entry, entry, sizeof(IGVM_VHS_MEMORY_MAP_ENTRY), + qigvm_cmp_mm_entry); + + break; + } + } + return 0; +} + +static int qigvm_directive_vp_count(QIgvm *ctx, const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data; + QIgvmParameterData *param_entry; + uint32_t *vp_count; + CPUState *cpu; + + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) + { + if (param_entry->index == param->parameter_area_index) { + vp_count = (uint32_t *)(param_entry->data + param->byte_offset); + *vp_count = 0; + CPU_FOREACH(cpu) + { + (*vp_count)++; + } + break; + } + } + return 0; +} + +static int qigvm_directive_environment_info(QIgvm *ctx, + const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_PARAMETER *param = (const IGVM_VHS_PARAMETER *)header_data; + QIgvmParameterData *param_entry; + IgvmEnvironmentInfo *environmental_state; + + QTAILQ_FOREACH(param_entry, &ctx->parameter_data, next) + { + if (param_entry->index == param->parameter_area_index) { + environmental_state = + (IgvmEnvironmentInfo *)(param_entry->data + param->byte_offset); + environmental_state->memory_is_shared = 1; + break; + } + } + return 0; +} + +static int qigvm_directive_required_memory(QIgvm *ctx, + const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_REQUIRED_MEMORY *mem = + (const IGVM_VHS_REQUIRED_MEMORY *)header_data; + uint8_t *region; + int result; + + if (!(mem->compatibility_mask & ctx->compatibility_mask)) { + return 0; + } + + region = qigvm_prepare_memory(ctx, mem->gpa, mem->number_of_bytes, + ctx->current_header_index, errp); + if (!region) { + return -1; + } + if (ctx->cgs) { + result = + ctx->cgsc->set_guest_state(mem->gpa, region, mem->number_of_bytes, + CGS_PAGE_TYPE_REQUIRED_MEMORY, 0, errp); + if (result < 0) { + return result; + } + } + return 0; +} + +static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) +{ + int32_t header_count; + unsigned header_index; + IgvmHandle header_handle; + IGVM_VHS_SUPPORTED_PLATFORM *platform; + uint32_t compatibility_mask_sev = 0; + uint32_t compatibility_mask_sev_es = 0; + uint32_t compatibility_mask_sev_snp = 0; + uint32_t compatibility_mask = 0; + + header_count = igvm_header_count(ctx->file, IGVM_HEADER_SECTION_PLATFORM); + if (header_count < 0) { + error_setg(errp, + "Invalid platform header count in IGVM file. Error code: %X", + header_count); + return -1; + } + + for (header_index = 0; header_index < (unsigned)header_count; + header_index++) { + IgvmVariableHeaderType typ = igvm_get_header_type( + ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index); + if (typ == IGVM_VHT_SUPPORTED_PLATFORM) { + header_handle = igvm_get_header( + ctx->file, IGVM_HEADER_SECTION_PLATFORM, header_index); + if (header_handle < 0) { + error_setg(errp, + "Invalid platform header in IGVM file. " + "Index: %d, Error code: %X", + header_index, header_handle); + return -1; + } + platform = + (IGVM_VHS_SUPPORTED_PLATFORM *)(igvm_get_buffer(ctx->file, + header_handle) + + sizeof( + IGVM_VHS_VARIABLE_HEADER)); + if ((platform->platform_type == IGVM_PLATFORM_TYPE_SEV_ES) && + ctx->cgs) { + if (ctx->cgsc->check_support( + CGS_PLATFORM_SEV_ES, platform->platform_version, + platform->highest_vtl, platform->shared_gpa_boundary)) { + compatibility_mask_sev_es = platform->compatibility_mask; + } + } else if ((platform->platform_type == IGVM_PLATFORM_TYPE_SEV) && + ctx->cgs) { + if (ctx->cgsc->check_support( + CGS_PLATFORM_SEV, platform->platform_version, + platform->highest_vtl, platform->shared_gpa_boundary)) { + compatibility_mask_sev = platform->compatibility_mask; + } + } else if ((platform->platform_type == + IGVM_PLATFORM_TYPE_SEV_SNP) && + ctx->cgs) { + if (ctx->cgsc->check_support( + CGS_PLATFORM_SEV_SNP, platform->platform_version, + platform->highest_vtl, platform->shared_gpa_boundary)) { + compatibility_mask_sev_snp = platform->compatibility_mask; + } + } else if (platform->platform_type == IGVM_PLATFORM_TYPE_NATIVE) { + compatibility_mask = platform->compatibility_mask; + } + igvm_free_buffer(ctx->file, header_handle); + } + } + /* Choose the strongest supported isolation technology */ + if (compatibility_mask_sev_snp != 0) { + ctx->compatibility_mask = compatibility_mask_sev_snp; + } else if (compatibility_mask_sev_es != 0) { + ctx->compatibility_mask = compatibility_mask_sev_es; + } else if (compatibility_mask_sev != 0) { + ctx->compatibility_mask = compatibility_mask_sev; + } else if (compatibility_mask != 0) { + ctx->compatibility_mask = compatibility_mask; + } else { + error_setg( + errp, + "IGVM file does not describe a compatible supported platform"); + return -1; + } + return 0; +} + +static IgvmHandle qigvm_file_init(char *filename, Error **errp) +{ + IgvmHandle igvm; + g_autofree uint8_t *buf = NULL; + unsigned long len; + g_autoptr(GError) gerr = NULL; + + if (!g_file_get_contents(filename, (gchar **)&buf, &len, &gerr)) { + error_setg(errp, "Unable to load %s: %s", filename, gerr->message); + return -1; + } + + igvm = igvm_new_from_binary(buf, len); + if (igvm < 0) { + error_setg(errp, "Unable to parse IGVM file %s: %d", filename, igvm); + return -1; + } + return igvm; +} + +int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, + Error **errp) +{ + int32_t header_count; + QIgvmParameterData *parameter; + int retval = -1; + QIgvm ctx; + + memset(&ctx, 0, sizeof(ctx)); + ctx.file = qigvm_file_init(cfg->filename, errp); + if (ctx.file < 0) { + return -1; + } + + /* + * The ConfidentialGuestSupport object is optional and allows a confidential + * guest platform to perform extra processing, such as page measurement, on + * IGVM directives. + */ + ctx.cgs = cgs; + ctx.cgsc = cgs ? CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(cgs) : NULL; + + /* + * Check that the IGVM file provides configuration for the current + * platform + */ + if (qigvm_supported_platform_compat_mask(&ctx, errp) < 0) { + goto cleanup; + } + + header_count = igvm_header_count(ctx.file, IGVM_HEADER_SECTION_DIRECTIVE); + if (header_count <= 0) { + error_setg( + errp, "Invalid directive header count in IGVM file. Error code: %X", + header_count); + goto cleanup; + } + + QTAILQ_INIT(&ctx.parameter_data); + + for (ctx.current_header_index = 0; + ctx.current_header_index < (unsigned)header_count; + ctx.current_header_index++) { + IgvmVariableHeaderType type = igvm_get_header_type( + ctx.file, IGVM_HEADER_SECTION_DIRECTIVE, ctx.current_header_index); + if (qigvm_handler(&ctx, type, errp) < 0) { + goto cleanup_parameters; + } + } + + /* + * Contiguous pages of data with compatible flags are grouped together in + * order to reduce the number of memory regions we create. Make sure the + * last group is processed with this call. + */ + retval = qigvm_process_mem_page(&ctx, NULL, errp); + +cleanup_parameters: + QTAILQ_FOREACH(parameter, &ctx.parameter_data, next) + { + g_free(parameter->data); + parameter->data = NULL; + } + +cleanup: + igvm_free(ctx.file); + + return retval; +} diff --git a/backends/igvm.h b/backends/igvm.h new file mode 100644 index 0000000000..db02ea9165 --- /dev/null +++ b/backends/igvm.h @@ -0,0 +1,22 @@ +/* + * QEMU IGVM configuration backend for Confidential Guests + * + * Copyright (C) 2023-2024 SUSE + * + * Authors: + * Roy Hopkins + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef BACKENDS_IGVM_H +#define BACKENDS_IGVM_H + +#include "system/confidential-guest-support.h" +#include "system/igvm-cfg.h" +#include "qapi/error.h" + +int qigvm_process_file(IgvmCfg *igvm, ConfidentialGuestSupport *cgs, + Error **errp); + +#endif diff --git a/backends/meson.build b/backends/meson.build index ac0fac7845..60021f45d1 100644 --- a/backends/meson.build +++ b/backends/meson.build @@ -36,6 +36,8 @@ system_ss.add(when: gio, if_true: files('dbus-vmstate.c')) system_ss.add(when: 'CONFIG_SGX', if_true: files('hostmem-epc.c')) if igvm.found() system_ss.add(igvm) + system_ss.add(files('igvm-cfg.c'), igvm) + system_ss.add(files('igvm.c'), igvm) endif system_ss.add(when: 'CONFIG_SPDM_SOCKET', if_true: files('spdm-socket.c')) diff --git a/include/system/igvm-cfg.h b/include/system/igvm-cfg.h new file mode 100644 index 0000000000..321b3196f0 --- /dev/null +++ b/include/system/igvm-cfg.h @@ -0,0 +1,46 @@ +/* + * QEMU IGVM interface + * + * Copyright (C) 2024 SUSE + * + * Authors: + * Roy Hopkins + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_IGVM_CFG_H +#define QEMU_IGVM_CFG_H + +#include "qom/object.h" + +typedef struct IgvmCfg { + ObjectClass parent_class; + + /* + * filename: Filename that specifies a file that contains the configuration + * of the guest in Independent Guest Virtual Machine (IGVM) + * format. + */ + char *filename; +} IgvmCfg; + +typedef struct IgvmCfgClass { + ObjectClass parent_class; + + /* + * If an IGVM filename has been specified then process the IGVM file. + * Performs a no-op if no filename has been specified. + * + * Returns 0 for ok and -1 on error. + */ + int (*process)(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, + Error **errp); + +} IgvmCfgClass; + +#define TYPE_IGVM_CFG "igvm-cfg" + +OBJECT_DECLARE_TYPE(IgvmCfg, IgvmCfgClass, IGVM_CFG) + +#endif diff --git a/qapi/qom.json b/qapi/qom.json index b133b06447..bbdb56dced 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -932,6 +932,19 @@ 'data': { '*filename': 'str' }, 'if': 'CONFIG_POSIX' } +## +# @IgvmCfgProperties: +# +# Properties common to objects that handle IGVM files. +# +# @file: IGVM file to use to configure guest +# +# Since: 10.1 +## +{ 'struct': 'IgvmCfgProperties', + 'if': 'CONFIG_IGVM', + 'data': { 'file': 'str' } } + ## # @SevCommonProperties: # @@ -1142,6 +1155,8 @@ 'filter-redirector', 'filter-replay', 'filter-rewriter', + { 'name': 'igvm-cfg', + 'if': 'CONFIG_IGVM' }, 'input-barrier', { 'name': 'input-linux', 'if': 'CONFIG_LINUX' }, @@ -1218,6 +1233,8 @@ 'filter-redirector': 'FilterRedirectorProperties', 'filter-replay': 'NetfilterProperties', 'filter-rewriter': 'FilterRewriterProperties', + 'igvm-cfg': { 'type': 'IgvmCfgProperties', + 'if': 'CONFIG_IGVM' }, 'input-barrier': 'InputBarrierProperties', 'input-linux': { 'type': 'InputLinuxProperties', 'if': 'CONFIG_LINUX' }, From 28e5ef4a6574cbaf6a5706d582b5b463b7f8d162 Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 16:11:02 +0100 Subject: [PATCH 2164/2760] hw/i386: Add igvm-cfg object and processing for IGVM files An IGVM file contains configuration of guest state that should be applied during configuration of the guest, before the guest is started. This patch allows the user to add an igvm-cfg object to an X86 machine configuration that allows an IGVM file to be configured that will be applied to the guest before it is started. If an IGVM configuration is provided then the IGVM file is processed at the end of the board initialization, before the state transition to PHASE_MACHINE_INITIALIZED. Signed-off-by: Roy Hopkins Acked-by: Gerd Hoffman Reviewed-by: Michael S. Tsirkin Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/23bc66ae4504ba5cf2134826e055b25df3fc9cd9.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 12 ++++++++++++ hw/i386/pc_piix.c | 10 ++++++++++ hw/i386/pc_q35.c | 10 ++++++++++ include/hw/i386/x86.h | 3 +++ qemu-options.hx | 28 ++++++++++++++++++++++++++++ 5 files changed, 63 insertions(+) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index b211633575..432ab288a8 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -1833,6 +1833,18 @@ static void pc_machine_class_init(ObjectClass *oc, const void *data) object_class_property_add_bool(oc, "fd-bootchk", pc_machine_get_fd_bootchk, pc_machine_set_fd_bootchk); + +#if defined(CONFIG_IGVM) + object_class_property_add_link(oc, "igvm-cfg", + TYPE_IGVM_CFG, + offsetof(X86MachineState, igvm), + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG); + object_class_property_set_description(oc, "igvm-cfg", + "Set IGVM configuration"); +#endif + + } static const TypeInfo pc_machine_info = { diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index ea7572e783..3184ea1b37 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -366,6 +366,16 @@ static void pc_init1(MachineState *machine, const char *pci_type) x86_nvdimm_acpi_dsmio, x86ms->fw_cfg, OBJECT(pcms)); } + +#if defined(CONFIG_IGVM) + /* Apply guest state from IGVM if supplied */ + if (x86ms->igvm) { + if (IGVM_CFG_GET_CLASS(x86ms->igvm) + ->process(x86ms->igvm, machine->cgs, &error_fatal) < 0) { + g_assert_not_reached(); + } + } +#endif } typedef enum PCSouthBridgeOption { diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 33211b1876..6990e1c669 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -325,6 +325,16 @@ static void pc_q35_init(MachineState *machine) x86_nvdimm_acpi_dsmio, x86ms->fw_cfg, OBJECT(pcms)); } + +#if defined(CONFIG_IGVM) + /* Apply guest state from IGVM if supplied */ + if (x86ms->igvm) { + if (IGVM_CFG_GET_CLASS(x86ms->igvm) + ->process(x86ms->igvm, machine->cgs, &error_fatal) < 0) { + g_assert_not_reached(); + } + } +#endif } #define DEFINE_Q35_MACHINE(major, minor) \ diff --git a/include/hw/i386/x86.h b/include/hw/i386/x86.h index fc460b82f8..8755cad50a 100644 --- a/include/hw/i386/x86.h +++ b/include/hw/i386/x86.h @@ -25,6 +25,7 @@ #include "hw/intc/ioapic.h" #include "hw/isa/isa.h" #include "qom/object.h" +#include "system/igvm-cfg.h" struct X86MachineClass { MachineClass parent; @@ -92,6 +93,8 @@ struct X86MachineState { * which means no limitation on the guest's bus locks. */ uint64_t bus_lock_ratelimit; + + IgvmCfg *igvm; }; #define X86_MACHINE_SMM "smm" diff --git a/qemu-options.hx b/qemu-options.hx index 1f862b19a6..f4c05b388b 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -5992,6 +5992,34 @@ SRST -machine ...,memory-encryption=sev0 \\ ..... + ``-object igvm-cfg,file=file`` + Create an IGVM configuration object that defines the initial state + of the guest using a file in that conforms to the Independent Guest + Virtual Machine (IGVM) file format. + + This is currently only supported by ``-machine q35`` and + ``-machine pc``. + + The ``file`` parameter is used to specify the IGVM file to load. + When provided, the IGVM file is used to populate the initial + memory of the virtual machine and, depending on the platform, can + define the initial processor state, memory map and parameters. + + The IGVM file is expected to contain the firmware for the virtual + machine, therefore an ``igvm-cfg`` object cannot be provided along + with other ways of specifying firmware, such as the ``-bios`` + parameter on x86 machines. + + e.g to launch a machine providing the firmware in an IGVM file + + .. parsed-literal:: + + # |qemu_system_x86| \\ + ...... \\ + -object igvm-cfg,id=igvm0,file=bios.igvm \\ + -machine ...,igvm-cfg=igvm0 \\ + ..... + ``-object authz-simple,id=id,identity=string`` Create an authorization object that will control access to network services. From 170beda3cf4381e0b824ac96e06367391a669dca Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 16:15:34 +0100 Subject: [PATCH 2165/2760] i386/pc_sysfw: Ensure sysfw flash configuration does not conflict with IGVM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When using an IGVM file the configuration of the system firmware is defined by IGVM directives contained in the file. In this case the user should not configure any pflash devices. This commit skips initialization of the ROM mode when pflash0 is not set then checks to ensure no pflash devices have been configured when using IGVM, exiting with an error message if this is not the case. Signed-off-by: Roy Hopkins Acked-by: Gerd Hoffman Reviewed-by: Daniel P. Berrangé Reviewed-by: Michael S. Tsirkin Reviewed-by: Stefano Garzarella Reviewed-by: Pankaj Gupta Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/c6166cfe128933b04003a9288566b7affe170dfe.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- hw/i386/pc_sysfw.c | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/hw/i386/pc_sysfw.c b/hw/i386/pc_sysfw.c index 821396c16e..1a12b635ad 100644 --- a/hw/i386/pc_sysfw.c +++ b/hw/i386/pc_sysfw.c @@ -220,7 +220,13 @@ void pc_system_firmware_init(PCMachineState *pcms, BlockBackend *pflash_blk[ARRAY_SIZE(pcms->flash)]; if (!pcmc->pci_enabled) { - x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, true); + /* + * If an IGVM file is specified then the firmware must be provided + * in the IGVM file. + */ + if (!X86_MACHINE(pcms)->igvm) { + x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, true); + } return; } @@ -240,8 +246,13 @@ void pc_system_firmware_init(PCMachineState *pcms, } if (!pflash_blk[0]) { - /* Machine property pflash0 not set, use ROM mode */ - x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, false); + /* + * Machine property pflash0 not set, use ROM mode unless using IGVM, + * in which case the firmware must be provided by the IGVM file. + */ + if (!X86_MACHINE(pcms)->igvm) { + x86_bios_rom_init(X86_MACHINE(pcms), "bios.bin", rom_memory, false); + } } else { if (kvm_enabled() && !kvm_readonly_mem_enabled()) { /* @@ -257,6 +268,20 @@ void pc_system_firmware_init(PCMachineState *pcms, } pc_system_flash_cleanup_unused(pcms); + + /* + * The user should not have specified any pflash devices when using IGVM + * to configure the guest. + */ + if (X86_MACHINE(pcms)->igvm) { + for (i = 0; i < ARRAY_SIZE(pcms->flash); i++) { + if (pcms->flash[i]) { + error_report("pflash devices cannot be configured when " + "using IGVM"); + exit(1); + } + } + } } void x86_firmware_configure(hwaddr gpa, void *ptr, int size) From 224e807f90a008dac1e4ba52e1bebc93b00472dd Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 16:21:32 +0100 Subject: [PATCH 2166/2760] sev: Update launch_update_data functions to use Error handling The class function and implementations for updating launch data return a code in case of error. In some cases an error message is generated and in other cases, just the error return value is used. This small refactor adds an 'Error **errp' parameter to all functions which consistently set an error condition if a non-zero value is returned. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Pankaj Gupta Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/d59721f7b99cfc87aab71f8f551937e98e983615.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 68 +++++++++++++++++++++++------------------------ 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index 1a12f0671c..a84f5f5d28 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -122,7 +122,8 @@ struct SevCommonStateClass { Error **errp); int (*launch_start)(SevCommonState *sev_common); void (*launch_finish)(SevCommonState *sev_common); - int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, uint8_t *ptr, size_t len); + int (*launch_update_data)(SevCommonState *sev_common, hwaddr gpa, + uint8_t *ptr, size_t len, Error **errp); int (*kvm_init)(ConfidentialGuestSupport *cgs, Error **errp); }; @@ -970,9 +971,8 @@ sev_snp_adjust_cpuid_features(X86ConfidentialGuest *cg, uint32_t feature, uint32 return value; } -static int -sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, - uint8_t *addr, size_t len) +static int sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, + uint8_t *addr, size_t len, Error **errp) { int ret, fw_error; struct kvm_sev_launch_update_data update; @@ -987,8 +987,8 @@ sev_launch_update_data(SevCommonState *sev_common, hwaddr gpa, ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_LAUNCH_UPDATE_DATA, &update, &fw_error); if (ret) { - error_report("%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", - __func__, ret, fw_error, fw_error_to_str(fw_error)); + error_setg(errp, "%s: LAUNCH_UPDATE ret=%d fw_error=%d '%s'", __func__, + ret, fw_error, fw_error_to_str(fw_error)); } return ret; @@ -1116,8 +1116,8 @@ sev_launch_finish(SevCommonState *sev_common) migrate_add_blocker(&sev_mig_blocker, &error_fatal); } -static int -snp_launch_update_data(uint64_t gpa, void *hva, size_t len, int type) +static int snp_launch_update_data(uint64_t gpa, void *hva, size_t len, + int type, Error **errp) { SevLaunchUpdateData *data; @@ -1132,23 +1132,21 @@ snp_launch_update_data(uint64_t gpa, void *hva, size_t len, int type) return 0; } -static int -sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa, - uint8_t *ptr, size_t len) +static int sev_snp_launch_update_data(SevCommonState *sev_common, hwaddr gpa, + uint8_t *ptr, size_t len, Error **errp) { - int ret = snp_launch_update_data(gpa, ptr, len, - KVM_SEV_SNP_PAGE_TYPE_NORMAL); - return ret; + return snp_launch_update_data(gpa, ptr, len, + KVM_SEV_SNP_PAGE_TYPE_NORMAL, errp); } static int sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info, - const KvmCpuidInfo *kvm_cpuid_info) + const KvmCpuidInfo *kvm_cpuid_info, Error **errp) { size_t i; if (kvm_cpuid_info->cpuid.nent > SNP_CPUID_FUNCTION_MAXCOUNT) { - error_report("SEV-SNP: CPUID entry count (%d) exceeds max (%d)", + error_setg(errp, "SEV-SNP: CPUID entry count (%d) exceeds max (%d)", kvm_cpuid_info->cpuid.nent, SNP_CPUID_FUNCTION_MAXCOUNT); return -1; } @@ -1190,8 +1188,8 @@ sev_snp_cpuid_info_fill(SnpCpuidInfo *snp_cpuid_info, return 0; } -static int -snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, size_t cpuid_len) +static int snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, + size_t cpuid_len, Error **errp) { KvmCpuidInfo kvm_cpuid_info = {0}; SnpCpuidInfo snp_cpuid_info; @@ -1208,26 +1206,25 @@ snp_launch_update_cpuid(uint32_t cpuid_addr, void *hva, size_t cpuid_len) } while (ret == -E2BIG); if (ret) { - error_report("SEV-SNP: unable to query CPUID values for CPU: '%s'", - strerror(-ret)); - return 1; + error_setg(errp, "SEV-SNP: unable to query CPUID values for CPU: '%s'", + strerror(-ret)); + return -1; } - ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info); - if (ret) { - error_report("SEV-SNP: failed to generate CPUID table information"); - return 1; + ret = sev_snp_cpuid_info_fill(&snp_cpuid_info, &kvm_cpuid_info, errp); + if (ret < 0) { + return -1; } memcpy(hva, &snp_cpuid_info, sizeof(snp_cpuid_info)); return snp_launch_update_data(cpuid_addr, hva, cpuid_len, - KVM_SEV_SNP_PAGE_TYPE_CPUID); + KVM_SEV_SNP_PAGE_TYPE_CPUID, errp); } -static int -snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, uint32_t addr, - void *hva, uint32_t len) +static int snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, + uint32_t addr, void *hva, + uint32_t len, Error **errp) { int type = KVM_SEV_SNP_PAGE_TYPE_ZERO; if (sev_snp->parent_obj.kernel_hashes) { @@ -1239,7 +1236,7 @@ snp_launch_update_kernel_hashes(SevSnpGuestState *sev_snp, uint32_t addr, sizeof(*sev_snp->kernel_hashes_data)); type = KVM_SEV_SNP_PAGE_TYPE_NORMAL; } - return snp_launch_update_data(addr, hva, len, type); + return snp_launch_update_data(addr, hva, len, type, errp); } static int @@ -1277,12 +1274,14 @@ snp_populate_metadata_pages(SevSnpGuestState *sev_snp, } if (type == KVM_SEV_SNP_PAGE_TYPE_CPUID) { - ret = snp_launch_update_cpuid(desc->base, hva, desc->len); + ret = snp_launch_update_cpuid(desc->base, hva, desc->len, + &error_fatal); } else if (desc->type == SEV_DESC_TYPE_SNP_KERNEL_HASHES) { ret = snp_launch_update_kernel_hashes(sev_snp, desc->base, hva, - desc->len); + desc->len, &error_fatal); } else { - ret = snp_launch_update_data(desc->base, hva, desc->len, type); + ret = snp_launch_update_data(desc->base, hva, desc->len, type, + &error_fatal); } if (ret) { @@ -1615,9 +1614,8 @@ sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp) if (sev_check_state(sev_common, SEV_STATE_LAUNCH_UPDATE)) { int ret; - ret = klass->launch_update_data(sev_common, gpa, ptr, len); + ret = klass->launch_update_data(sev_common, gpa, ptr, len, errp); if (ret < 0) { - error_setg(errp, "SEV: Failed to encrypt pflash rom"); return ret; } } From b0e8986668426dbd2bb3eee4c6e14fe6262ca34e Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 16:31:58 +0100 Subject: [PATCH 2167/2760] target/i386: Allow setting of R_LDTR and R_TR with cpu_x86_load_seg_cache() The x86 segment registers are identified by the X86Seg enumeration which includes LDTR and TR as well as the normal segment registers. The function 'cpu_x86_load_seg_cache()' uses the enum to determine which segment to set. However, specifying R_LDTR or R_TR results in an out-of-bounds access of the segment array. Possibly by coincidence, the function does correctly set LDTR or TR in this case as the structures for these registers immediately follow the array which is accessed out of bounds. This patch adds correct handling for R_LDTR and R_TR in the function. Signed-off-by: Roy Hopkins Acked-by: Gerd Hoffman Reviewed-by: Michael S. Tsirkin Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/95c69253ea4f91107625872d5e3f0c586376771d.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- target/i386/cpu.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.h b/target/i386/cpu.h index be3ae6d546..9829824ac8 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2417,7 +2417,14 @@ static inline void cpu_x86_load_seg_cache(CPUX86State *env, SegmentCache *sc; unsigned int new_hflags; - sc = &env->segs[seg_reg]; + if (seg_reg == R_LDTR) { + sc = &env->ldt; + } else if (seg_reg == R_TR) { + sc = &env->tr; + } else { + sc = &env->segs[seg_reg]; + } + sc->selector = selector; sc->base = base; sc->limit = limit; From 4c7f0976b0d2afc892c10f6cc503e8b4607115e2 Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 16:31:59 +0100 Subject: [PATCH 2168/2760] i386/sev: Refactor setting of reset vector and initial CPU state When an SEV guest is started, the reset vector and state are extracted from metadata that is contained in the firmware volume. In preparation for using IGVM to setup the initial CPU state, the code has been refactored to populate vmcb_save_area for each CPU which is then applied during guest startup and CPU reset. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Stefano Garzarella Acked-by: Gerd Hoffman Reviewed-by: Pankaj Gupta Link: https://lore.kernel.org/r/d3c2debca496c4366a278b135f951908f3b9c341.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 322 +++++++++++++++++++++++++++++++++++++++++----- target/i386/sev.h | 110 ++++++++++++++++ 2 files changed, 399 insertions(+), 33 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index a84f5f5d28..a13f91e615 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -50,6 +50,12 @@ OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, SEV_SNP_GUEST) /* hard code sha256 digest size */ #define HASH_SIZE 32 +/* Convert between SEV-ES VMSA and SegmentCache flags/attributes */ +#define FLAGS_VMSA_TO_SEGCACHE(flags) \ + ((((flags) & 0xff00) << 12) | (((flags) & 0xff) << 8)) +#define FLAGS_SEGCACHE_TO_VMSA(flags) \ + ((((flags) & 0xff00) >> 8) | (((flags) & 0xf00000) >> 12)) + typedef struct QEMU_PACKED SevHashTableEntry { QemuUUID guid; uint16_t len; @@ -89,6 +95,14 @@ typedef struct QEMU_PACKED SevHashTableDescriptor { uint32_t size; } SevHashTableDescriptor; +typedef struct SevLaunchVmsa { + QTAILQ_ENTRY(SevLaunchVmsa) next; + + uint16_t cpu_index; + uint64_t gpa; + struct sev_es_save_area vmsa; +} SevLaunchVmsa; + struct SevCommonState { X86ConfidentialGuest parent_obj; @@ -107,9 +121,7 @@ struct SevCommonState { int sev_fd; SevState state; - uint32_t reset_cs; - uint32_t reset_ip; - bool reset_data_valid; + QTAILQ_HEAD(, SevLaunchVmsa) launch_vmsa; }; struct SevCommonStateClass { @@ -364,6 +376,172 @@ static struct RAMBlockNotifier sev_ram_notifier = { .ram_block_removed = sev_ram_block_removed, }; +static void sev_apply_cpu_context(CPUState *cpu) +{ + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + X86CPU *x86; + CPUX86State *env; + struct SevLaunchVmsa *launch_vmsa; + + /* See if an initial VMSA has been provided for this CPU */ + QTAILQ_FOREACH(launch_vmsa, &sev_common->launch_vmsa, next) + { + if (cpu->cpu_index == launch_vmsa->cpu_index) { + x86 = X86_CPU(cpu); + env = &x86->env; + + /* + * Ideally we would provide the VMSA directly to kvm which would + * ensure that the resulting initial VMSA measurement which is + * calculated during KVM_SEV_LAUNCH_UPDATE_VMSA is calculated from + * exactly what we provide here. Currently this is not possible so + * we need to copy the parts of the VMSA structure that we currently + * support into the CPU state. + */ + cpu_load_efer(env, launch_vmsa->vmsa.efer); + cpu_x86_update_cr4(env, launch_vmsa->vmsa.cr4); + cpu_x86_update_cr0(env, launch_vmsa->vmsa.cr0); + cpu_x86_update_cr3(env, launch_vmsa->vmsa.cr3); + env->xcr0 = launch_vmsa->vmsa.xcr0; + env->pat = launch_vmsa->vmsa.g_pat; + + cpu_x86_load_seg_cache( + env, R_CS, launch_vmsa->vmsa.cs.selector, + launch_vmsa->vmsa.cs.base, launch_vmsa->vmsa.cs.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.cs.attrib)); + cpu_x86_load_seg_cache( + env, R_DS, launch_vmsa->vmsa.ds.selector, + launch_vmsa->vmsa.ds.base, launch_vmsa->vmsa.ds.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ds.attrib)); + cpu_x86_load_seg_cache( + env, R_ES, launch_vmsa->vmsa.es.selector, + launch_vmsa->vmsa.es.base, launch_vmsa->vmsa.es.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.es.attrib)); + cpu_x86_load_seg_cache( + env, R_FS, launch_vmsa->vmsa.fs.selector, + launch_vmsa->vmsa.fs.base, launch_vmsa->vmsa.fs.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.fs.attrib)); + cpu_x86_load_seg_cache( + env, R_GS, launch_vmsa->vmsa.gs.selector, + launch_vmsa->vmsa.gs.base, launch_vmsa->vmsa.gs.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.gs.attrib)); + cpu_x86_load_seg_cache( + env, R_SS, launch_vmsa->vmsa.ss.selector, + launch_vmsa->vmsa.ss.base, launch_vmsa->vmsa.ss.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ss.attrib)); + + env->gdt.base = launch_vmsa->vmsa.gdtr.base; + env->gdt.limit = launch_vmsa->vmsa.gdtr.limit; + env->gdt.flags = + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.gdtr.attrib); + env->idt.base = launch_vmsa->vmsa.idtr.base; + env->idt.limit = launch_vmsa->vmsa.idtr.limit; + env->idt.flags = + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.idtr.attrib); + + cpu_x86_load_seg_cache( + env, R_LDTR, launch_vmsa->vmsa.ldtr.selector, + launch_vmsa->vmsa.ldtr.base, launch_vmsa->vmsa.ldtr.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.ldtr.attrib)); + cpu_x86_load_seg_cache( + env, R_TR, launch_vmsa->vmsa.tr.selector, + launch_vmsa->vmsa.ldtr.base, launch_vmsa->vmsa.tr.limit, + FLAGS_VMSA_TO_SEGCACHE(launch_vmsa->vmsa.tr.attrib)); + + env->dr[6] = launch_vmsa->vmsa.dr6; + env->dr[7] = launch_vmsa->vmsa.dr7; + + env->regs[R_EAX] = launch_vmsa->vmsa.rax; + env->regs[R_ECX] = launch_vmsa->vmsa.rcx; + env->regs[R_EDX] = launch_vmsa->vmsa.rdx; + env->regs[R_EBX] = launch_vmsa->vmsa.rbx; + env->regs[R_ESP] = launch_vmsa->vmsa.rsp; + env->regs[R_EBP] = launch_vmsa->vmsa.rbp; + env->regs[R_ESI] = launch_vmsa->vmsa.rsi; + env->regs[R_EDI] = launch_vmsa->vmsa.rdi; +#ifdef TARGET_X86_64 + env->regs[R_R8] = launch_vmsa->vmsa.r8; + env->regs[R_R9] = launch_vmsa->vmsa.r9; + env->regs[R_R10] = launch_vmsa->vmsa.r10; + env->regs[R_R11] = launch_vmsa->vmsa.r11; + env->regs[R_R12] = launch_vmsa->vmsa.r12; + env->regs[R_R13] = launch_vmsa->vmsa.r13; + env->regs[R_R14] = launch_vmsa->vmsa.r14; + env->regs[R_R15] = launch_vmsa->vmsa.r15; +#endif + env->eip = launch_vmsa->vmsa.rip; + env->eflags = launch_vmsa->vmsa.rflags; + + cpu_set_fpuc(env, launch_vmsa->vmsa.x87_fcw); + env->mxcsr = launch_vmsa->vmsa.mxcsr; + + break; + } + } +} + +static int sev_set_cpu_context(uint16_t cpu_index, const void *ctx, + uint32_t ctx_len, hwaddr gpa, Error **errp) +{ + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + SevLaunchVmsa *launch_vmsa; + CPUState *cpu; + bool exists = false; + + /* + * Setting the CPU context is only supported for SEV-ES and SEV-SNP. The + * context buffer will contain a sev_es_save_area from the Linux kernel + * which is defined by "Table B-4. VMSA Layout, State Save Area for SEV-ES" + * in the AMD64 APM, Volume 2. + */ + + if (!sev_es_enabled()) { + error_setg(errp, "SEV: unable to set CPU context: Not supported"); + return -1; + } + + if (ctx_len < sizeof(struct sev_es_save_area)) { + error_setg(errp, "SEV: unable to set CPU context: " + "Invalid context provided"); + return -1; + } + + cpu = qemu_get_cpu(cpu_index); + if (!cpu) { + error_setg(errp, "SEV: unable to set CPU context for out of bounds " + "CPU index %d", cpu_index); + return -1; + } + + /* + * If the context of this VP has already been set then replace it with the + * new context. + */ + QTAILQ_FOREACH(launch_vmsa, &sev_common->launch_vmsa, next) + { + if (cpu_index == launch_vmsa->cpu_index) { + launch_vmsa->gpa = gpa; + memcpy(&launch_vmsa->vmsa, ctx, sizeof(launch_vmsa->vmsa)); + exists = true; + break; + } + } + + if (!exists) { + /* New VP context */ + launch_vmsa = g_new0(SevLaunchVmsa, 1); + memcpy(&launch_vmsa->vmsa, ctx, sizeof(launch_vmsa->vmsa)); + launch_vmsa->cpu_index = cpu_index; + launch_vmsa->gpa = gpa; + QTAILQ_INSERT_TAIL(&sev_common->launch_vmsa, launch_vmsa, next); + } + + /* Synchronise the VMSA with the current CPU state */ + sev_apply_cpu_context(cpu); + + return 0; +} + bool sev_enabled(void) { @@ -998,6 +1176,16 @@ static int sev_launch_update_vmsa(SevGuestState *sev_guest) { int ret, fw_error; + CPUState *cpu; + + /* + * The initial CPU state is measured as part of KVM_SEV_LAUNCH_UPDATE_VMSA. + * Synchronise the CPU state to any provided launch VMSA structures. + */ + CPU_FOREACH(cpu) { + sev_apply_cpu_context(cpu); + } + ret = sev_ioctl(SEV_COMMON(sev_guest)->sev_fd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL, &fw_error); @@ -1780,40 +1968,109 @@ sev_es_find_reset_vector(void *flash_ptr, uint64_t flash_size, return sev_es_parse_reset_block(info, addr); } -void sev_es_set_reset_vector(CPUState *cpu) + +static void seg_to_vmsa(const SegmentCache *cpu_seg, struct vmcb_seg *vmsa_seg) { - X86CPU *x86; - CPUX86State *env; - ConfidentialGuestSupport *cgs = MACHINE(qdev_get_machine())->cgs; - SevCommonState *sev_common = SEV_COMMON( - object_dynamic_cast(OBJECT(cgs), TYPE_SEV_COMMON)); + vmsa_seg->selector = cpu_seg->selector; + vmsa_seg->base = cpu_seg->base; + vmsa_seg->limit = cpu_seg->limit; + vmsa_seg->attrib = FLAGS_SEGCACHE_TO_VMSA(cpu_seg->flags); +} - /* Only update if we have valid reset information */ - if (!sev_common || !sev_common->reset_data_valid) { - return; - } +static void initialize_vmsa(const CPUState *cpu, struct sev_es_save_area *vmsa) +{ + const X86CPU *x86 = X86_CPU(cpu); + const CPUX86State *env = &x86->env; - /* Do not update the BSP reset state */ - if (cpu->cpu_index == 0) { - return; + /* + * Initialize the SEV-ES save area from the current state of + * the CPU. The entire state does not need to be copied, only the state + * that is copied back to the CPUState in sev_apply_cpu_context. + */ + memset(vmsa, 0, sizeof(struct sev_es_save_area)); + vmsa->efer = env->efer; + vmsa->cr0 = env->cr[0]; + vmsa->cr3 = env->cr[3]; + vmsa->cr4 = env->cr[4]; + vmsa->xcr0 = env->xcr0; + vmsa->g_pat = env->pat; + + seg_to_vmsa(&env->segs[R_CS], &vmsa->cs); + seg_to_vmsa(&env->segs[R_DS], &vmsa->ds); + seg_to_vmsa(&env->segs[R_ES], &vmsa->es); + seg_to_vmsa(&env->segs[R_FS], &vmsa->fs); + seg_to_vmsa(&env->segs[R_GS], &vmsa->gs); + seg_to_vmsa(&env->segs[R_SS], &vmsa->ss); + + seg_to_vmsa(&env->gdt, &vmsa->gdtr); + seg_to_vmsa(&env->idt, &vmsa->idtr); + seg_to_vmsa(&env->ldt, &vmsa->ldtr); + seg_to_vmsa(&env->tr, &vmsa->tr); + + vmsa->dr6 = env->dr[6]; + vmsa->dr7 = env->dr[7]; + + vmsa->rax = env->regs[R_EAX]; + vmsa->rcx = env->regs[R_ECX]; + vmsa->rdx = env->regs[R_EDX]; + vmsa->rbx = env->regs[R_EBX]; + vmsa->rsp = env->regs[R_ESP]; + vmsa->rbp = env->regs[R_EBP]; + vmsa->rsi = env->regs[R_ESI]; + vmsa->rdi = env->regs[R_EDI]; + +#ifdef TARGET_X86_64 + vmsa->r8 = env->regs[R_R8]; + vmsa->r9 = env->regs[R_R9]; + vmsa->r10 = env->regs[R_R10]; + vmsa->r11 = env->regs[R_R11]; + vmsa->r12 = env->regs[R_R12]; + vmsa->r13 = env->regs[R_R13]; + vmsa->r14 = env->regs[R_R14]; + vmsa->r15 = env->regs[R_R15]; +#endif + + vmsa->rip = env->eip; + vmsa->rflags = env->eflags; +} + +static void sev_es_set_ap_context(uint32_t reset_addr) +{ + CPUState *cpu; + struct sev_es_save_area vmsa; + SegmentCache cs; + + cs.selector = 0xf000; + cs.base = reset_addr & 0xffff0000; + cs.limit = 0xffff; + cs.flags = DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK | DESC_R_MASK | + DESC_A_MASK; + + CPU_FOREACH(cpu) { + if (cpu->cpu_index == 0) { + /* Do not update the BSP reset state */ + continue; + } + initialize_vmsa(cpu, &vmsa); + seg_to_vmsa(&cs, &vmsa.cs); + vmsa.rip = reset_addr & 0x0000ffff; + sev_set_cpu_context(cpu->cpu_index, &vmsa, + sizeof(struct sev_es_save_area), + 0, &error_fatal); } +} - x86 = X86_CPU(cpu); - env = &x86->env; - - cpu_x86_load_seg_cache(env, R_CS, 0xf000, sev_common->reset_cs, 0xffff, - DESC_P_MASK | DESC_S_MASK | DESC_CS_MASK | - DESC_R_MASK | DESC_A_MASK); - - env->eip = sev_common->reset_ip; +void sev_es_set_reset_vector(CPUState *cpu) +{ + if (sev_enabled()) { + sev_apply_cpu_context(cpu); + } } int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) { - CPUState *cpu; uint32_t addr; int ret; - SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); if (!sev_es_enabled()) { return 0; @@ -1826,14 +2083,12 @@ int sev_es_save_reset_vector(void *flash_ptr, uint64_t flash_size) return ret; } + /* + * The reset vector is saved into a CPU context for each AP but not for + * the BSP. This is applied during guest startup or when the CPU is reset. + */ if (addr) { - sev_common->reset_cs = addr & 0xffff0000; - sev_common->reset_ip = addr & 0x0000ffff; - sev_common->reset_data_valid = true; - - CPU_FOREACH(cpu) { - sev_es_set_reset_vector(cpu); - } + sev_es_set_ap_context(addr); } return 0; @@ -2068,6 +2323,7 @@ sev_common_instance_init(Object *obj) object_property_add_uint32_ptr(obj, "reduced-phys-bits", &sev_common->reduced_phys_bits, OBJ_PROP_FLAG_READWRITE); + QTAILQ_INIT(&sev_common->launch_vmsa); } /* sev guest info common to sev/sev-es/sev-snp */ diff --git a/target/i386/sev.h b/target/i386/sev.h index 373669eaac..38caa849f5 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -55,6 +55,116 @@ typedef struct SevKernelLoaderContext { size_t cmdline_size; } SevKernelLoaderContext; +/* Save area definition for SEV-ES and SEV-SNP guests */ +struct QEMU_PACKED sev_es_save_area { + struct vmcb_seg es; + struct vmcb_seg cs; + struct vmcb_seg ss; + struct vmcb_seg ds; + struct vmcb_seg fs; + struct vmcb_seg gs; + struct vmcb_seg gdtr; + struct vmcb_seg ldtr; + struct vmcb_seg idtr; + struct vmcb_seg tr; + uint64_t vmpl0_ssp; + uint64_t vmpl1_ssp; + uint64_t vmpl2_ssp; + uint64_t vmpl3_ssp; + uint64_t u_cet; + uint8_t reserved_0xc8[2]; + uint8_t vmpl; + uint8_t cpl; + uint8_t reserved_0xcc[4]; + uint64_t efer; + uint8_t reserved_0xd8[104]; + uint64_t xss; + uint64_t cr4; + uint64_t cr3; + uint64_t cr0; + uint64_t dr7; + uint64_t dr6; + uint64_t rflags; + uint64_t rip; + uint64_t dr0; + uint64_t dr1; + uint64_t dr2; + uint64_t dr3; + uint64_t dr0_addr_mask; + uint64_t dr1_addr_mask; + uint64_t dr2_addr_mask; + uint64_t dr3_addr_mask; + uint8_t reserved_0x1c0[24]; + uint64_t rsp; + uint64_t s_cet; + uint64_t ssp; + uint64_t isst_addr; + uint64_t rax; + uint64_t star; + uint64_t lstar; + uint64_t cstar; + uint64_t sfmask; + uint64_t kernel_gs_base; + uint64_t sysenter_cs; + uint64_t sysenter_esp; + uint64_t sysenter_eip; + uint64_t cr2; + uint8_t reserved_0x248[32]; + uint64_t g_pat; + uint64_t dbgctl; + uint64_t br_from; + uint64_t br_to; + uint64_t last_excp_from; + uint64_t last_excp_to; + uint8_t reserved_0x298[80]; + uint32_t pkru; + uint32_t tsc_aux; + uint8_t reserved_0x2f0[24]; + uint64_t rcx; + uint64_t rdx; + uint64_t rbx; + uint64_t reserved_0x320; /* rsp already available at 0x01d8 */ + uint64_t rbp; + uint64_t rsi; + uint64_t rdi; + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint8_t reserved_0x380[16]; + uint64_t guest_exit_info_1; + uint64_t guest_exit_info_2; + uint64_t guest_exit_int_info; + uint64_t guest_nrip; + uint64_t sev_features; + uint64_t vintr_ctrl; + uint64_t guest_exit_code; + uint64_t virtual_tom; + uint64_t tlb_id; + uint64_t pcpu_id; + uint64_t event_inj; + uint64_t xcr0; + uint8_t reserved_0x3f0[16]; + + /* Floating point area */ + uint64_t x87_dp; + uint32_t mxcsr; + uint16_t x87_ftw; + uint16_t x87_fsw; + uint16_t x87_fcw; + uint16_t x87_fop; + uint16_t x87_ds; + uint16_t x87_cs; + uint64_t x87_rip; + uint8_t fpreg_x87[80]; + uint8_t fpreg_xmm[256]; + uint8_t fpreg_ymm[256]; +}; + bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp); From 86e85eb1d41d1f2b328be812d3390d47055b620f Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 16:34:41 +0100 Subject: [PATCH 2169/2760] i386/sev: Implement ConfidentialGuestSupport functions for SEV The ConfidentialGuestSupport object defines a number of virtual functions that are called during processing of IGVM directives to query or configure initial guest state. In order to support processing of IGVM files, these functions need to be implemented by relevant isolation hardware support code such as SEV. This commit implements the required functions for SEV-ES and adds support for processing IGVM files for configuring the guest. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Stefano Garzarella Acked-by: Gerd Hoffman Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/7145835f729e6195f2fbda308aa90e089a96ae6e.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 254 ++++++++++++++++++++++++++++++++++++++++++++-- target/i386/sev.h | 2 + 2 files changed, 246 insertions(+), 10 deletions(-) diff --git a/target/i386/sev.c b/target/i386/sev.c index a13f91e615..1296f4feb6 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -41,7 +41,9 @@ #include "confidential-guest.h" #include "hw/i386/pc.h" #include "system/address-spaces.h" +#include "hw/i386/e820_memory_layout.h" #include "qemu/queue.h" +#include "qemu/cutils.h" OBJECT_DECLARE_TYPE(SevCommonState, SevCommonStateClass, SEV_COMMON) OBJECT_DECLARE_TYPE(SevGuestState, SevCommonStateClass, SEV_GUEST) @@ -50,6 +52,9 @@ OBJECT_DECLARE_TYPE(SevSnpGuestState, SevCommonStateClass, SEV_SNP_GUEST) /* hard code sha256 digest size */ #define HASH_SIZE 32 +/* Hard coded GPA that KVM uses for the VMSA */ +#define KVM_VMSA_GPA 0xFFFFFFFFF000 + /* Convert between SEV-ES VMSA and SegmentCache flags/attributes */ #define FLAGS_VMSA_TO_SEGCACHE(flags) \ ((((flags) & 0xff00) << 12) | (((flags) & 0xff) << 8)) @@ -480,6 +485,103 @@ static void sev_apply_cpu_context(CPUState *cpu) } } +static int check_vmsa_supported(hwaddr gpa, const struct sev_es_save_area *vmsa, + Error **errp) +{ + struct sev_es_save_area vmsa_check; + + /* + * KVM always populates the VMSA at a fixed GPA which cannot be modified + * from userspace. Specifying a different GPA will not prevent the guest + * from starting but will cause the launch measurement to be different + * from expected. Therefore check that the provided GPA matches the KVM + * hardcoded value. + */ + if (gpa != KVM_VMSA_GPA) { + error_setg(errp, + "%s: The VMSA GPA must be %lX but is specified as %lX", + __func__, KVM_VMSA_GPA, gpa); + return -1; + } + + /* + * Clear all supported fields so we can then check the entire structure + * is zero. + */ + memcpy(&vmsa_check, vmsa, sizeof(struct sev_es_save_area)); + memset(&vmsa_check.es, 0, sizeof(vmsa_check.es)); + memset(&vmsa_check.cs, 0, sizeof(vmsa_check.cs)); + memset(&vmsa_check.ss, 0, sizeof(vmsa_check.ss)); + memset(&vmsa_check.ds, 0, sizeof(vmsa_check.ds)); + memset(&vmsa_check.fs, 0, sizeof(vmsa_check.fs)); + memset(&vmsa_check.gs, 0, sizeof(vmsa_check.gs)); + memset(&vmsa_check.gdtr, 0, sizeof(vmsa_check.gdtr)); + memset(&vmsa_check.idtr, 0, sizeof(vmsa_check.idtr)); + memset(&vmsa_check.ldtr, 0, sizeof(vmsa_check.ldtr)); + memset(&vmsa_check.tr, 0, sizeof(vmsa_check.tr)); + vmsa_check.efer = 0; + vmsa_check.cr0 = 0; + vmsa_check.cr3 = 0; + vmsa_check.cr4 = 0; + vmsa_check.xcr0 = 0; + vmsa_check.dr6 = 0; + vmsa_check.dr7 = 0; + vmsa_check.rax = 0; + vmsa_check.rcx = 0; + vmsa_check.rdx = 0; + vmsa_check.rbx = 0; + vmsa_check.rsp = 0; + vmsa_check.rbp = 0; + vmsa_check.rsi = 0; + vmsa_check.rdi = 0; + vmsa_check.r8 = 0; + vmsa_check.r9 = 0; + vmsa_check.r10 = 0; + vmsa_check.r11 = 0; + vmsa_check.r12 = 0; + vmsa_check.r13 = 0; + vmsa_check.r14 = 0; + vmsa_check.r15 = 0; + vmsa_check.rip = 0; + vmsa_check.rflags = 0; + + vmsa_check.g_pat = 0; + vmsa_check.xcr0 = 0; + + vmsa_check.x87_fcw = 0; + vmsa_check.mxcsr = 0; + + if (sev_snp_enabled()) { + if (vmsa_check.sev_features != SVM_SEV_FEAT_SNP_ACTIVE) { + error_setg(errp, + "%s: sev_features in the VMSA contains an unsupported " + "value. For SEV-SNP, sev_features must be set to %x.", + __func__, SVM_SEV_FEAT_SNP_ACTIVE); + return -1; + } + vmsa_check.sev_features = 0; + } else { + if (vmsa_check.sev_features != 0) { + error_setg(errp, + "%s: sev_features in the VMSA contains an unsupported " + "value. For SEV-ES and SEV, sev_features must be " + "set to 0.", __func__); + return -1; + } + } + + if (!buffer_is_zero(&vmsa_check, sizeof(vmsa_check))) { + error_setg(errp, + "%s: The VMSA contains fields that are not " + "synchronized with KVM. Continuing would result in " + "either unpredictable guest behavior, or a " + "mismatched launch measurement.", + __func__); + return -1; + } + return 0; +} + static int sev_set_cpu_context(uint16_t cpu_index, const void *ctx, uint32_t ctx_len, hwaddr gpa, Error **errp) { @@ -1491,18 +1593,26 @@ sev_snp_launch_finish(SevCommonState *sev_common) struct kvm_sev_snp_launch_finish *finish = &sev_snp->kvm_finish_conf; /* - * To boot the SNP guest, the hypervisor is required to populate the CPUID - * and Secrets page before finalizing the launch flow. The location of - * the secrets and CPUID page is available through the OVMF metadata GUID. + * Populate all the metadata pages if not using an IGVM file. In the case + * where an IGVM file is provided it will be used to configure the metadata + * pages directly. */ - metadata = pc_system_get_ovmf_sev_metadata_ptr(); - if (metadata == NULL) { - error_report("%s: Failed to locate SEV metadata header", __func__); - exit(1); - } + if (!X86_MACHINE(qdev_get_machine())->igvm) { + /* + * To boot the SNP guest, the hypervisor is required to populate the + * CPUID and Secrets page before finalizing the launch flow. The + * location of the secrets and CPUID page is available through the + * OVMF metadata GUID. + */ + metadata = pc_system_get_ovmf_sev_metadata_ptr(); + if (metadata == NULL) { + error_report("%s: Failed to locate SEV metadata header", __func__); + exit(1); + } - /* Populate all the metadata pages */ - snp_populate_metadata_pages(sev_snp, metadata); + /* Populate all the metadata pages */ + snp_populate_metadata_pages(sev_snp, metadata); + } QTAILQ_FOREACH(data, &launch_update, next) { ret = sev_snp_launch_update(sev_snp, data); @@ -2290,6 +2400,124 @@ static void sev_common_set_kernel_hashes(Object *obj, bool value, Error **errp) SEV_COMMON(obj)->kernel_hashes = value; } +static bool cgs_check_support(ConfidentialGuestPlatformType platform, + uint16_t platform_version, uint8_t highest_vtl, + uint64_t shared_gpa_boundary) +{ + return (((platform == CGS_PLATFORM_SEV_SNP) && sev_snp_enabled()) || + ((platform == CGS_PLATFORM_SEV_ES) && sev_es_enabled()) || + ((platform == CGS_PLATFORM_SEV) && sev_enabled())); +} + +static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, + ConfidentialGuestPageType memory_type, + uint16_t cpu_index, Error **errp) +{ + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(sev_common); + + if (!sev_enabled()) { + error_setg(errp, "%s: attempt to configure guest memory, but SEV " + "is not enabled", __func__); + return -1; + } + + switch (memory_type) { + case CGS_PAGE_TYPE_NORMAL: + case CGS_PAGE_TYPE_ZERO: + return klass->launch_update_data(sev_common, gpa, ptr, len, errp); + + case CGS_PAGE_TYPE_VMSA: + if (!sev_es_enabled()) { + error_setg(errp, + "%s: attempt to configure initial VMSA, but SEV-ES " + "is not supported", + __func__); + return -1; + } + if (check_vmsa_supported(gpa, (const struct sev_es_save_area *)ptr, + errp) < 0) { + return -1; + } + return sev_set_cpu_context(cpu_index, ptr, len, gpa, errp); + + case CGS_PAGE_TYPE_UNMEASURED: + if (sev_snp_enabled()) { + return snp_launch_update_data( + gpa, ptr, len, KVM_SEV_SNP_PAGE_TYPE_UNMEASURED, errp); + } + /* No action required if not SEV-SNP */ + return 0; + + case CGS_PAGE_TYPE_SECRETS: + if (!sev_snp_enabled()) { + error_setg(errp, + "%s: attempt to configure secrets page, but SEV-SNP " + "is not supported", + __func__); + return -1; + } + return snp_launch_update_data(gpa, ptr, len, + KVM_SEV_SNP_PAGE_TYPE_SECRETS, errp); + + case CGS_PAGE_TYPE_REQUIRED_MEMORY: + if (kvm_convert_memory(gpa, len, true) < 0) { + error_setg( + errp, + "%s: failed to configure required memory. gpa: %lX, type: %d", + __func__, gpa, memory_type); + return -1; + } + return 0; + + case CGS_PAGE_TYPE_CPUID: + if (!sev_snp_enabled()) { + error_setg(errp, + "%s: attempt to configure CPUID page, but SEV-SNP " + "is not supported", + __func__); + return -1; + } + return snp_launch_update_cpuid(gpa, ptr, len, errp); + } + error_setg(errp, "%s: failed to update guest. gpa: %lX, type: %d", __func__, + gpa, memory_type); + return -1; +} + +static int cgs_get_mem_map_entry(int index, + ConfidentialGuestMemoryMapEntry *entry, + Error **errp) +{ + struct e820_entry *table; + int num_entries; + + num_entries = e820_get_table(&table); + if ((index < 0) || (index >= num_entries)) { + return 1; + } + entry->gpa = table[index].address; + entry->size = table[index].length; + switch (table[index].type) { + case E820_RAM: + entry->type = CGS_MEM_RAM; + break; + case E820_RESERVED: + entry->type = CGS_MEM_RESERVED; + break; + case E820_ACPI: + entry->type = CGS_MEM_ACPI; + break; + case E820_NVS: + entry->type = CGS_MEM_NVS; + break; + case E820_UNUSABLE: + entry->type = CGS_MEM_UNUSABLE; + break; + } + return 0; +} + static void sev_common_class_init(ObjectClass *oc, const void *data) { @@ -2313,6 +2541,8 @@ static void sev_common_instance_init(Object *obj) { SevCommonState *sev_common = SEV_COMMON(obj); + ConfidentialGuestSupportClass *cgs = + CONFIDENTIAL_GUEST_SUPPORT_GET_CLASS(obj); sev_common->kvm_type = -1; @@ -2323,6 +2553,10 @@ sev_common_instance_init(Object *obj) object_property_add_uint32_ptr(obj, "reduced-phys-bits", &sev_common->reduced_phys_bits, OBJ_PROP_FLAG_READWRITE); + cgs->check_support = cgs_check_support; + cgs->set_guest_state = cgs_set_guest_state; + cgs->get_mem_map_entry = cgs_get_mem_map_entry; + QTAILQ_INIT(&sev_common->launch_vmsa); } diff --git a/target/i386/sev.h b/target/i386/sev.h index 38caa849f5..d2eb06db32 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -44,6 +44,8 @@ bool sev_snp_enabled(void); #define SEV_SNP_POLICY_SMT 0x10000 #define SEV_SNP_POLICY_DBG 0x80000 +#define SVM_SEV_FEAT_SNP_ACTIVE 1 + typedef struct SevKernelLoaderContext { char *setup_data; size_t setup_size; From 565d591f719d05763544a5d929de3a40c903b3ea Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 16:41:51 +0100 Subject: [PATCH 2170/2760] docs/system: Add documentation on support for IGVM MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit IGVM support has been implemented for Confidential Guests that support AMD SEV and AMD SEV-ES. Add some documentation that gives some background on the IGVM format and how to use it to configure a confidential guest. Signed-off-by: Roy Hopkins Reviewed-by: Daniel P. Berrangé Reviewed-by: Stefano Garzarella Reviewed-by: Pankaj Gupta Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/b4dc920a30717e19cd79bbbe2cc769f3b9ff3d37.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- docs/system/i386/amd-memory-encryption.rst | 2 + docs/system/igvm.rst | 173 +++++++++++++++++++++ docs/system/index.rst | 1 + 3 files changed, 176 insertions(+) create mode 100644 docs/system/igvm.rst diff --git a/docs/system/i386/amd-memory-encryption.rst b/docs/system/i386/amd-memory-encryption.rst index 748f5094ba..6c23f3535f 100644 --- a/docs/system/i386/amd-memory-encryption.rst +++ b/docs/system/i386/amd-memory-encryption.rst @@ -1,3 +1,5 @@ +.. _amd-sev: + AMD Secure Encrypted Virtualization (SEV) ========================================= diff --git a/docs/system/igvm.rst b/docs/system/igvm.rst new file mode 100644 index 0000000000..79508d9588 --- /dev/null +++ b/docs/system/igvm.rst @@ -0,0 +1,173 @@ +Independent Guest Virtual Machine (IGVM) support +================================================ + +IGVM files are designed to encapsulate all the information required to launch a +virtual machine on any given virtualization stack in a deterministic way. This +allows the cryptographic measurement of initial guest state for Confidential +Guests to be calculated when the IGVM file is built, allowing a relying party to +verify the initial state of a guest via a remote attestation. + +Although IGVM files are designed with Confidential Computing in mind, they can +also be used to configure non-confidential guests. Multiple platforms can be +defined by a single IGVM file, allowing a single IGVM file to configure a +virtual machine that can run on, for example, TDX, SEV and non-confidential +hosts. + +QEMU supports IGVM files through the user-creatable ``igvm-cfg`` object. This +object is used to define the filename of the IGVM file to process. A reference +to the object is added to the ``-machine`` to configure the virtual machine +to use the IGVM file for configuration. + +Confidential platform support is provided through the use of +the ``ConfidentialGuestSupport`` object. If the virtual machine provides an +instance of this object then this is used by the IGVM loader to configure the +isolation properties of the directives within the file. + +Further Information on IGVM +--------------------------- + +Information about the IGVM format, including links to the format specification +and documentation for the Rust and C libraries can be found at the project +repository: + +https://github.com/microsoft/igvm + + +Supported Platforms +------------------- + +Currently, IGVM files can be provided for Confidential Guests on host systems +that support AMD SEV, SEV-ES and SEV-SNP with KVM. IGVM files can also be +provided for non-confidential guests. + + +Limitations when using IGVM with AMD SEV, SEV-ES and SEV-SNP +------------------------------------------------------------ + +IGVM files configure the initial state of the guest using a set of directives. +Not every directive is supported by every Confidential Guest type. For example, +AMD SEV does not support encrypted save state regions, therefore setting the +initial CPU state using IGVM for SEV is not possible. When an IGVM file contains +directives that are not supported for the active platform, an error is generated +and the guest launch is aborted. + +The table below describes the list of directives that are supported for SEV, +SEV-ES, SEV-SNP and non-confidential platforms. + +.. list-table:: SEV, SEV-ES, SEV-SNP & non-confidential Supported Directives + :widths: 35 65 + :header-rows: 1 + + * - IGVM directive + - Notes + * - IGVM_VHT_PAGE_DATA + - ``NORMAL`` zero, measured and unmeasured page types are supported. Other + page types result in an error. + * - IGVM_VHT_PARAMETER_AREA + - + * - IGVM_VHT_PARAMETER_INSERT + - + * - IGVM_VHT_VP_COUNT_PARAMETER + - The guest parameter page is populated with the CPU count. + * - IGVM_VHT_ENVIRONMENT_INFO_PARAMETER + - The ``memory_is_shared`` parameter is set to 1 in the guest parameter + page. + +.. list-table:: Additional SEV, SEV-ES & SEV_SNP Supported Directives + :widths: 25 75 + :header-rows: 1 + + * - IGVM directive + - Notes + * - IGVM_VHT_MEMORY_MAP + - The memory map page is populated using entries from the E820 table. + * - IGVM_VHT_REQUIRED_MEMORY + - Ensures memory is available in the guest at the specified range. + +.. list-table:: Additional SEV-ES & SEV-SNP Supported Directives + :widths: 25 75 + :header-rows: 1 + + * - IGVM directive + - Notes + * - IGVM_VHT_VP_CONTEXT + - Setting of the initial CPU state for the boot CPU and additional CPUs is + supported with limitations on the fields that can be provided in the + VMSA. See below for details on which fields are supported. + +Initial CPU state with VMSA +--------------------------- + +The initial state of guest CPUs can be defined in the IGVM file for AMD SEV-ES +and SEV-SNP. The state data is provided as a VMSA structure as defined in Table +B-4 in the AMD64 Architecture Programmer's Manual, Volume 2 [1]. + +The IGVM VMSA is translated to CPU state in QEMU which is then synchronized +by KVM to the guest VMSA during the launch process where it contributes to the +launch measurement. See :ref:`amd-sev` for details on the launch process and +guest launch measurement. + +It is important that no information is lost or changed when translating the +VMSA provided by the IGVM file into the VSMA that is used to launch the guest. +Therefore, QEMU restricts the VMSA fields that can be provided in the IGVM +VMSA structure to the following registers: + +RAX, RCX, RDX, RBX, RBP, RSI, RDI, R8-R15, RSP, RIP, CS, DS, ES, FS, GS, SS, +CR0, CR3, CR4, XCR0, EFER, PAT, GDT, IDT, LDTR, TR, DR6, DR7, RFLAGS, X87_FCW, +MXCSR. + +When processing the IGVM file, QEMU will check if any fields other than the +above are non-zero and generate an error if this is the case. + +KVM uses a hardcoded GPA of 0xFFFFFFFFF000 for the VMSA. When an IGVM file +defines initial CPU state, the GPA for each VMSA must match this hardcoded +value. + +Firmware Images with IGVM +------------------------- + +When an IGVM filename is specified for a Confidential Guest Support object it +overrides the default handling of system firmware: the firmware image, such as +an OVMF binary should be contained as a payload of the IGVM file and not +provided as a flash drive or via the ``-bios`` parameter. The default QEMU +firmware is not automatically populated into the guest memory space. + +If an IGVM file is provided along with either the ``-bios`` parameter or pflash +devices then an error is displayed and the guest startup is aborted. + +Running a guest configured using IGVM +------------------------------------- + +To run a guest configured with IGVM you firstly need to generate an IGVM file +that contains a guest configuration compatible with the platform you are +targeting. + +The ``buildigvm`` tool [2] is an example of a tool that can be used to generate +IGVM files for non-confidential X86 platforms as well as for SEV, SEV-ES and +SEV-SNP confidential platforms. + +Example using this tool to generate an IGVM file for AMD SEV-SNP:: + + buildigvm --firmware /path/to/OVMF.fd --output sev-snp.igvm \ + --cpucount 4 sev-snp + +To run a guest configured with the generated IGVM you need to add an +``igvm-cfg`` object and refer to it from the ``-machine`` parameter: + +Example (for AMD SEV):: + + qemu-system-x86_64 \ + \ + -machine ...,confidential-guest-support=sev0,igvm-cfg=igvm0 \ + -object sev-guest,id=sev0,cbitpos=47,reduced-phys-bits=1 \ + -object igvm-cfg,id=igvm0,file=/path/to/sev-snp.igvm + +References +---------- + +[1] AMD64 Architecture Programmer's Manual, Volume 2: System Programming + Rev 3.41 + https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/programmer-references/24593.pdf + +[2] ``buildigvm`` - A tool to build example IGVM files containing OVMF firmware + https://github.com/roy-hopkins/buildigvm \ No newline at end of file diff --git a/docs/system/index.rst b/docs/system/index.rst index 718e9d3c56..427b020483 100644 --- a/docs/system/index.rst +++ b/docs/system/index.rst @@ -38,5 +38,6 @@ or Hypervisor.Framework. security multi-process confidential-guest-support + igvm vm-templating sriov From 596c330b19cf00384ec14d0bff25758ed204b49d Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 17:02:19 +0100 Subject: [PATCH 2171/2760] docs/interop/firmware.json: Add igvm to FirmwareDevice Create an enum entry within FirmwareDevice for 'igvm' to describe that an IGVM file can be used to map firmware into memory as an alternative to pre-existing firmware devices. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/2eca2611d372facbffa65ee8244cf2d321eb9d17.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- docs/interop/firmware.json | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index 745d21d822..0711b6f323 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -57,10 +57,17 @@ # # @memory: The firmware is to be mapped into memory. # +# @igvm: The firmware is defined by a file conforming to the IGVM +# specification and mapped into memory according to directives +# defined in the file. This is similar to @memory but may +# include additional processing defined by the IGVM file +# including initial CPU state or population of metadata into +# the guest address space. Since: 10.1 +# # Since: 3.0 ## { 'enum' : 'FirmwareDevice', - 'data' : [ 'flash', 'kernel', 'memory' ] } + 'data' : [ 'flash', 'kernel', 'memory', 'igvm' ] } ## # @FirmwareArchitecture: @@ -377,6 +384,24 @@ { 'struct' : 'FirmwareMappingMemory', 'data' : { 'filename' : 'str' } } +## +# @FirmwareMappingIgvm: +# +# Describes loading and mapping properties for the firmware executable, +# when @FirmwareDevice is @igvm. +# +# @filename: Identifies the IGVM file containing the firmware executable +# along with other information used to configure the initial +# state of the guest. The IGVM file may be shared by multiple +# virtual machine definitions. This corresponds to creating +# an object on the command line with "-object igvm-cfg, +# file=@filename". +# +# Since: 10.1 +## +{ 'struct' : 'FirmwareMappingIgvm', + 'data' : { 'filename' : 'str' } } + ## # @FirmwareMapping: # @@ -393,7 +418,8 @@ 'discriminator' : 'device', 'data' : { 'flash' : 'FirmwareMappingFlash', 'kernel' : 'FirmwareMappingKernel', - 'memory' : 'FirmwareMappingMemory' } } + 'memory' : 'FirmwareMappingMemory', + 'igvm' : 'FirmwareMappingIgvm' } } ## # @Firmware: From 96a3088f5ebe6854dc8fb5a547c6b1d1db60f0fa Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 17:02:20 +0100 Subject: [PATCH 2172/2760] backends/confidential-guest-support: Add set_guest_policy() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For confidential guests a policy can be provided that defines the security level, debug status, expected launch measurement and other parameters that define the configuration of the confidential platform. This commit adds a new function named set_guest_policy() that can be implemented by each confidential platform, such as AMD SEV to set the policy. This will allow configuration of the policy from a multi-platform resource such as an IGVM file without the IGVM processor requiring specific implementation details for each platform. Signed-off-by: Roy Hopkins Reviewed-by: Daniel P. Berrangé Reviewed-by: Stefano Garzarella Reviewed-by: Ani Sinha Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Link: https://lore.kernel.org/r/d3888a2eb170c8d8c85a1c4b7e99accf3a15589c.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- backends/confidential-guest-support.c | 12 ++++++++++++ include/system/confidential-guest-support.h | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/backends/confidential-guest-support.c b/backends/confidential-guest-support.c index c5bef1fbfa..156dd15e66 100644 --- a/backends/confidential-guest-support.c +++ b/backends/confidential-guest-support.c @@ -38,6 +38,17 @@ static int set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, return -1; } +static int set_guest_policy(ConfidentialGuestPolicyType policy_type, + uint64_t policy, + void *policy_data1, uint32_t policy_data1_size, + void *policy_data2, uint32_t policy_data2_size, + Error **errp) +{ + error_setg(errp, + "Setting confidential guest policy is not supported for this platform"); + return -1; +} + static int get_mem_map_entry(int index, ConfidentialGuestMemoryMapEntry *entry, Error **errp) { @@ -53,6 +64,7 @@ static void confidential_guest_support_class_init(ObjectClass *oc, ConfidentialGuestSupportClass *cgsc = CONFIDENTIAL_GUEST_SUPPORT_CLASS(oc); cgsc->check_support = check_support; cgsc->set_guest_state = set_guest_state; + cgsc->set_guest_policy = set_guest_policy; cgsc->get_mem_map_entry = get_mem_map_entry; } diff --git a/include/system/confidential-guest-support.h b/include/system/confidential-guest-support.h index 79ecd21f42..0cc8b26e64 100644 --- a/include/system/confidential-guest-support.h +++ b/include/system/confidential-guest-support.h @@ -57,6 +57,10 @@ typedef enum ConfidentialGuestPageType { CGS_PAGE_TYPE_REQUIRED_MEMORY, } ConfidentialGuestPageType; +typedef enum ConfidentialGuestPolicyType { + GUEST_POLICY_SEV, +} ConfidentialGuestPolicyType; + struct ConfidentialGuestSupport { Object parent; @@ -123,6 +127,23 @@ typedef struct ConfidentialGuestSupportClass { ConfidentialGuestPageType memory_type, uint16_t cpu_index, Error **errp); + /* + * Set the guest policy. The policy can be used to configure the + * confidential platform, such as if debug is enabled or not and can contain + * information about expected launch measurements, signed verification of + * guest configuration and other platform data. + * + * The format of the policy data is specific to each platform. For example, + * SEV-SNP uses a policy bitfield in the 'policy' argument and provides an + * ID block and ID authentication in the 'policy_data' parameters. The type + * of policy data is identified by the 'policy_type' argument. + */ + int (*set_guest_policy)(ConfidentialGuestPolicyType policy_type, + uint64_t policy, + void *policy_data1, uint32_t policy_data1_size, + void *policy_data2, uint32_t policy_data2_size, + Error **errp); + /* * Iterate the system memory map, getting the entry with the given index * that can be populated into guest memory. From 9de40d7df3266625bf6e6c2712604ab306d12e65 Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 17:18:59 +0100 Subject: [PATCH 2173/2760] backends/igvm: Process initialization sections in IGVM file The initialization sections in IGVM files contain configuration that should be applied to the guest platform before it is started. This includes guest policy and other information that can affect the security level and the startup measurement of a guest. This commit introduces handling of the initialization sections during processing of the IGVM file. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffman Reviewed-by: Stefano Garzarella Link: https://lore.kernel.org/r/9de24fb5df402024b40cbe02de0b13faa7cb4d84.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- backends/igvm.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/backends/igvm.c b/backends/igvm.c index 2a31021d44..ebdb4594d1 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -786,6 +786,27 @@ int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, } } + header_count = + igvm_header_count(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION); + if (header_count < 0) { + error_setg( + errp, + "Invalid initialization header count in IGVM file. Error code: %X", + header_count); + goto cleanup_parameters; + } + + for (ctx.current_header_index = 0; + ctx.current_header_index < (unsigned)header_count; + ctx.current_header_index++) { + IgvmVariableHeaderType type = + igvm_get_header_type(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION, + ctx.current_header_index); + if (qigvm_handler(&ctx, type, errp) < 0) { + goto cleanup_parameters; + } + } + /* * Contiguous pages of data with compatible flags are grouped together in * order to reduce the number of memory regions we create. Make sure the From 915b47078d6b4cffde209aab81ab56f73e4a2632 Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 17:21:57 +0100 Subject: [PATCH 2174/2760] backends/igvm: Handle policy for SEV guests Adds a handler for the guest policy initialization IGVM section and builds an SEV policy based on this information and the ID block directive if present. The policy is applied using by calling 'set_guest_policy()' on the ConfidentialGuestSupport object. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Stefano Garzarella Acked-by: Gerd Hoffman Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/57707230bef331b53e9366ce6a23ed25cd6f1293.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- backends/igvm.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) diff --git a/backends/igvm.c b/backends/igvm.c index ebdb4594d1..b568f06c76 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -27,6 +27,40 @@ typedef struct QIgvmParameterData { uint32_t index; } QIgvmParameterData; +/* + * Some directives are specific to particular confidential computing platforms. + * Define required types for each of those platforms here. + */ + +/* SEV/SEV-ES/SEV-SNP */ + +/* + * These structures are defined in "SEV Secure Nested Paging Firmware ABI + * Specification" Rev 1.58, section 8.18. + */ +struct QEMU_PACKED sev_id_block { + uint8_t ld[48]; + uint8_t family_id[16]; + uint8_t image_id[16]; + uint32_t version; + uint32_t guest_svn; + uint64_t policy; +}; + +struct QEMU_PACKED sev_id_authentication { + uint32_t id_key_alg; + uint32_t auth_key_algo; + uint8_t reserved[56]; + uint8_t id_block_sig[512]; + uint8_t id_key[1028]; + uint8_t reserved2[60]; + uint8_t id_key_sig[512]; + uint8_t author_key[1028]; + uint8_t reserved3[892]; +}; + +#define IGVM_SEV_ID_BLOCK_VERSION 1 + /* * QIgvm contains the information required during processing * of a single IGVM file. @@ -38,6 +72,17 @@ typedef struct QIgvm { uint32_t compatibility_mask; unsigned current_header_index; QTAILQ_HEAD(, QIgvmParameterData) parameter_data; + IgvmPlatformType platform_type; + + /* + * SEV-SNP platforms can contain an ID block and authentication + * that should be verified by the guest. + */ + struct sev_id_block *id_block; + struct sev_id_authentication *id_auth; + + /* Define the guest policy for SEV guests */ + uint64_t sev_policy; /* These variables keep track of contiguous page regions */ IGVM_VHS_PAGE_DATA region_prev_page_data; @@ -67,6 +112,11 @@ static int qigvm_directive_environment_info(QIgvm *ctx, static int qigvm_directive_required_memory(QIgvm *ctx, const uint8_t *header_data, Error **errp); +static int qigvm_directive_snp_id_block(QIgvm *ctx, const uint8_t *header_data, + Error **errp); +static int qigvm_initialization_guest_policy(QIgvm *ctx, + const uint8_t *header_data, + Error **errp); struct QIGVMHandler { uint32_t type; @@ -91,6 +141,10 @@ static struct QIGVMHandler handlers[] = { qigvm_directive_environment_info }, { IGVM_VHT_REQUIRED_MEMORY, IGVM_HEADER_SECTION_DIRECTIVE, qigvm_directive_required_memory }, + { IGVM_VHT_SNP_ID_BLOCK, IGVM_HEADER_SECTION_DIRECTIVE, + qigvm_directive_snp_id_block }, + { IGVM_VHT_GUEST_POLICY, IGVM_HEADER_SECTION_INITIALIZATION, + qigvm_initialization_guest_policy }, }; static int qigvm_handler(QIgvm *ctx, uint32_t type, Error **errp) @@ -632,6 +686,74 @@ static int qigvm_directive_required_memory(QIgvm *ctx, return 0; } +static int qigvm_directive_snp_id_block(QIgvm *ctx, const uint8_t *header_data, + Error **errp) +{ + const IGVM_VHS_SNP_ID_BLOCK *igvm_id = + (const IGVM_VHS_SNP_ID_BLOCK *)header_data; + + if (!(igvm_id->compatibility_mask & ctx->compatibility_mask)) { + return 0; + } + + if (ctx->id_block) { + error_setg(errp, "IGVM: Multiple ID blocks encountered " + "in IGVM file."); + return -1; + } + ctx->id_block = g_new0(struct sev_id_block, 1); + ctx->id_auth = g_new0(struct sev_id_authentication, 1); + + memcpy(ctx->id_block->family_id, igvm_id->family_id, + sizeof(ctx->id_block->family_id)); + memcpy(ctx->id_block->image_id, igvm_id->image_id, + sizeof(ctx->id_block->image_id)); + ctx->id_block->guest_svn = igvm_id->guest_svn; + ctx->id_block->version = IGVM_SEV_ID_BLOCK_VERSION; + memcpy(ctx->id_block->ld, igvm_id->ld, sizeof(ctx->id_block->ld)); + + ctx->id_auth->id_key_alg = igvm_id->id_key_algorithm; + assert(sizeof(igvm_id->id_key_signature) <= + sizeof(ctx->id_auth->id_block_sig)); + memcpy(ctx->id_auth->id_block_sig, &igvm_id->id_key_signature, + sizeof(igvm_id->id_key_signature)); + + ctx->id_auth->auth_key_algo = igvm_id->author_key_algorithm; + assert(sizeof(igvm_id->author_key_signature) <= + sizeof(ctx->id_auth->id_key_sig)); + memcpy(ctx->id_auth->id_key_sig, &igvm_id->author_key_signature, + sizeof(igvm_id->author_key_signature)); + + /* + * SEV and IGVM public key structure population are slightly different. + * See SEV Secure Nested Paging Firmware ABI Specification, Chapter 10. + */ + *((uint32_t *)ctx->id_auth->id_key) = igvm_id->id_public_key.curve; + memcpy(&ctx->id_auth->id_key[4], &igvm_id->id_public_key.qx, 72); + memcpy(&ctx->id_auth->id_key[76], &igvm_id->id_public_key.qy, 72); + + *((uint32_t *)ctx->id_auth->author_key) = + igvm_id->author_public_key.curve; + memcpy(&ctx->id_auth->author_key[4], &igvm_id->author_public_key.qx, + 72); + memcpy(&ctx->id_auth->author_key[76], &igvm_id->author_public_key.qy, + 72); + + return 0; +} + +static int qigvm_initialization_guest_policy(QIgvm *ctx, + const uint8_t *header_data, Error **errp) +{ + const IGVM_VHS_GUEST_POLICY *guest = + (const IGVM_VHS_GUEST_POLICY *)header_data; + + if (guest->compatibility_mask & ctx->compatibility_mask) { + ctx->sev_policy = guest->policy; + } + return 0; +} + static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) { int32_t header_count; @@ -701,12 +823,16 @@ static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) /* Choose the strongest supported isolation technology */ if (compatibility_mask_sev_snp != 0) { ctx->compatibility_mask = compatibility_mask_sev_snp; + ctx->platform_type = IGVM_PLATFORM_TYPE_SEV_SNP; } else if (compatibility_mask_sev_es != 0) { ctx->compatibility_mask = compatibility_mask_sev_es; + ctx->platform_type = IGVM_PLATFORM_TYPE_SEV_ES; } else if (compatibility_mask_sev != 0) { ctx->compatibility_mask = compatibility_mask_sev; + ctx->platform_type = IGVM_PLATFORM_TYPE_SEV; } else if (compatibility_mask != 0) { ctx->compatibility_mask = compatibility_mask; + ctx->platform_type = IGVM_PLATFORM_TYPE_NATIVE; } else { error_setg( errp, @@ -716,6 +842,23 @@ static int qigvm_supported_platform_compat_mask(QIgvm *ctx, Error **errp) return 0; } +static int qigvm_handle_policy(QIgvm *ctx, Error **errp) +{ + if (ctx->platform_type == IGVM_PLATFORM_TYPE_SEV_SNP) { + int id_block_len = 0; + int id_auth_len = 0; + if (ctx->id_block) { + ctx->id_block->policy = ctx->sev_policy; + id_block_len = sizeof(struct sev_id_block); + id_auth_len = sizeof(struct sev_id_authentication); + } + return ctx->cgsc->set_guest_policy(GUEST_POLICY_SEV, ctx->sev_policy, + ctx->id_block, id_block_len, + ctx->id_auth, id_auth_len, errp); + } + return 0; +} + static IgvmHandle qigvm_file_init(char *filename, Error **errp) { IgvmHandle igvm; @@ -814,12 +957,18 @@ int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, */ retval = qigvm_process_mem_page(&ctx, NULL, errp); + if (retval == 0) { + retval = qigvm_handle_policy(&ctx, errp); + } + cleanup_parameters: QTAILQ_FOREACH(parameter, &ctx.parameter_data, next) { g_free(parameter->data); parameter->data = NULL; } + g_free(ctx.id_block); + g_free(ctx.id_auth); cleanup: igvm_free(ctx.file); From 2ff75825cc5a5d56ea90d79cd15578f6b1893561 Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 17:21:58 +0100 Subject: [PATCH 2175/2760] i386/sev: Add implementation of CGS set_guest_policy() The new cgs_set_guest_policy() function is provided to receive the guest policy flags, SNP ID block and SNP ID authentication from guest configuration such as an IGVM file and apply it to the platform prior to launching the guest. The policy is used to populate values for the existing 'policy', 'id_block' and 'id_auth' parameters. When provided, the guest policy is applied and the ID block configuration is used to verify the launch measurement and signatures. The guest is only successfully started if the expected launch measurements match the actual measurements and the signatures are valid. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Stefano Garzarella Acked-by: Gerd Hoffman Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/99e82ddec4ad2970c790db8bea16ea3f57eb0e53.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- target/i386/sev.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++ target/i386/sev.h | 12 +++++++ 2 files changed, 95 insertions(+) diff --git a/target/i386/sev.c b/target/i386/sev.c index 1296f4feb6..3e5722ba65 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -2518,6 +2518,88 @@ static int cgs_get_mem_map_entry(int index, return 0; } +static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, + uint64_t policy, void *policy_data1, + uint32_t policy_data1_size, void *policy_data2, + uint32_t policy_data2_size, Error **errp) +{ + if (policy_type != GUEST_POLICY_SEV) { + error_setg(errp, "%s: Invalid guest policy type provided for SEV: %d", + __func__, policy_type); + return -1; + } + /* + * SEV-SNP handles policy differently. The policy flags are defined in + * kvm_start_conf.policy and an ID block and ID auth can be provided. + */ + if (sev_snp_enabled()) { + SevSnpGuestState *sev_snp_guest = + SEV_SNP_GUEST(MACHINE(qdev_get_machine())->cgs); + struct kvm_sev_snp_launch_finish *finish = + &sev_snp_guest->kvm_finish_conf; + + /* + * The policy consists of flags in 'policy' and optionally an ID block + * and ID auth in policy_data1 and policy_data2 respectively. The ID + * block and auth are optional so clear any previous ID block and auth + * and set them if provided, but always set the policy flags. + */ + g_free(sev_snp_guest->id_block); + g_free((guchar *)finish->id_block_uaddr); + g_free(sev_snp_guest->id_auth); + g_free((guchar *)finish->id_auth_uaddr); + sev_snp_guest->id_block = NULL; + finish->id_block_uaddr = 0; + sev_snp_guest->id_auth = NULL; + finish->id_auth_uaddr = 0; + + if (policy_data1_size > 0) { + struct sev_snp_id_authentication *id_auth = + (struct sev_snp_id_authentication *)policy_data2; + + if (policy_data1_size != KVM_SEV_SNP_ID_BLOCK_SIZE) { + error_setg(errp, "%s: Invalid SEV-SNP ID block: incorrect size", + __func__); + return -1; + } + if (policy_data2_size != KVM_SEV_SNP_ID_AUTH_SIZE) { + error_setg(errp, + "%s: Invalid SEV-SNP ID auth block: incorrect size", + __func__); + return -1; + } + assert(policy_data1 != NULL); + assert(policy_data2 != NULL); + + finish->id_block_uaddr = + (__u64)g_memdup2(policy_data1, KVM_SEV_SNP_ID_BLOCK_SIZE); + finish->id_auth_uaddr = + (__u64)g_memdup2(policy_data2, KVM_SEV_SNP_ID_AUTH_SIZE); + + /* + * Check if an author key has been provided and use that to flag + * whether the author key is enabled. The first of the author key + * must be non-zero to indicate the key type, which will currently + * always be 2. + */ + sev_snp_guest->kvm_finish_conf.auth_key_en = + id_auth->author_key[0] ? 1 : 0; + finish->id_block_en = 1; + } + sev_snp_guest->kvm_start_conf.policy = policy; + } else { + SevGuestState *sev_guest = SEV_GUEST(MACHINE(qdev_get_machine())->cgs); + /* Only the policy flags are supported for SEV and SEV-ES */ + if ((policy_data1_size > 0) || (policy_data2_size > 0) || !sev_guest) { + error_setg(errp, "%s: An ID block/ID auth block has been provided " + "but SEV-SNP is not enabled", __func__); + return -1; + } + sev_guest->policy = policy; + } + return 0; +} + static void sev_common_class_init(ObjectClass *oc, const void *data) { @@ -2556,6 +2638,7 @@ sev_common_instance_init(Object *obj) cgs->check_support = cgs_check_support; cgs->set_guest_state = cgs_set_guest_state; cgs->get_mem_map_entry = cgs_get_mem_map_entry; + cgs->set_guest_policy = cgs_set_guest_policy; QTAILQ_INIT(&sev_common->launch_vmsa); } diff --git a/target/i386/sev.h b/target/i386/sev.h index d2eb06db32..9db1a802f6 100644 --- a/target/i386/sev.h +++ b/target/i386/sev.h @@ -167,6 +167,18 @@ struct QEMU_PACKED sev_es_save_area { uint8_t fpreg_ymm[256]; }; +struct QEMU_PACKED sev_snp_id_authentication { + uint32_t id_key_alg; + uint32_t auth_key_algo; + uint8_t reserved[56]; + uint8_t id_block_sig[512]; + uint8_t id_key[1028]; + uint8_t reserved2[60]; + uint8_t id_key_sig[512]; + uint8_t author_key[1028]; + uint8_t reserved3[892]; +}; + bool sev_add_kernel_loader_hashes(SevKernelLoaderContext *ctx, Error **errp); int sev_encrypt_flash(hwaddr gpa, uint8_t *ptr, uint64_t len, Error **errp); From d60238b4c1e81235d5efb4a978a6f6b1adabccab Mon Sep 17 00:00:00 2001 From: Roy Hopkins Date: Thu, 3 Jul 2025 17:21:59 +0100 Subject: [PATCH 2176/2760] sev: Provide sev_features flags from IGVM VMSA to KVM_SEV_INIT2 IGVM files can contain an initial VMSA that should be applied to each vcpu as part of the initial guest state. The sev_features flags are provided as part of the VMSA structure. However, KVM only allows sev_features to be set during initialization and not as the guest is being prepared for launch. This patch queries KVM for the supported set of sev_features flags and processes the VP context entries in the IGVM file during kvm_init to determine any sev_features flags set in the IGVM file. These are then provided in the call to KVM_SEV_INIT2 to ensure the guest state matches that specified in the IGVM file. The igvm process() function is modified to allow a partial processing of the file during initialization, with only the IGVM_VHT_VP_CONTEXT fields being processed. This means the function is called twice, firstly to extract the sev_features then secondly to actually configure the guest. Signed-off-by: Roy Hopkins Acked-by: Michael S. Tsirkin Acked-by: Stefano Garzarella Acked-by: Gerd Hoffman Tested-by: Stefano Garzarella Reviewed-by: Liam Merwick Reviewed-by: Ani Sinha Link: https://lore.kernel.org/r/b2f986aae04e1da2aee530c9be22a54c0c59a560.1751554099.git.roy.hopkins@randomman.co.uk Signed-off-by: Paolo Bonzini --- backends/igvm.c | 17 +++- backends/igvm.h | 2 +- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 2 +- include/system/igvm-cfg.h | 5 +- target/i386/sev.c | 161 +++++++++++++++++++++++++++++++++----- 6 files changed, 163 insertions(+), 26 deletions(-) diff --git a/backends/igvm.c b/backends/igvm.c index b568f06c76..9ad41582ee 100644 --- a/backends/igvm.c +++ b/backends/igvm.c @@ -880,7 +880,7 @@ static IgvmHandle qigvm_file_init(char *filename, Error **errp) } int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, - Error **errp) + bool onlyVpContext, Error **errp) { int32_t header_count; QIgvmParameterData *parameter; @@ -924,11 +924,22 @@ int qigvm_process_file(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, ctx.current_header_index++) { IgvmVariableHeaderType type = igvm_get_header_type( ctx.file, IGVM_HEADER_SECTION_DIRECTIVE, ctx.current_header_index); - if (qigvm_handler(&ctx, type, errp) < 0) { - goto cleanup_parameters; + if (!onlyVpContext || (type == IGVM_VHT_VP_CONTEXT)) { + if (qigvm_handler(&ctx, type, errp) < 0) { + goto cleanup_parameters; + } } } + /* + * If only processing the VP context then we don't need to process + * any more of the file. + */ + if (onlyVpContext) { + retval = 0; + goto cleanup_parameters; + } + header_count = igvm_header_count(ctx.file, IGVM_HEADER_SECTION_INITIALIZATION); if (header_count < 0) { diff --git a/backends/igvm.h b/backends/igvm.h index db02ea9165..a4abab043a 100644 --- a/backends/igvm.h +++ b/backends/igvm.h @@ -17,6 +17,6 @@ #include "qapi/error.h" int qigvm_process_file(IgvmCfg *igvm, ConfidentialGuestSupport *cgs, - Error **errp); + bool onlyVpContext, Error **errp); #endif diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 3184ea1b37..a3285fbc64 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -371,7 +371,7 @@ static void pc_init1(MachineState *machine, const char *pci_type) /* Apply guest state from IGVM if supplied */ if (x86ms->igvm) { if (IGVM_CFG_GET_CLASS(x86ms->igvm) - ->process(x86ms->igvm, machine->cgs, &error_fatal) < 0) { + ->process(x86ms->igvm, machine->cgs, false, &error_fatal) < 0) { g_assert_not_reached(); } } diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 6990e1c669..cf871cfdad 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -330,7 +330,7 @@ static void pc_q35_init(MachineState *machine) /* Apply guest state from IGVM if supplied */ if (x86ms->igvm) { if (IGVM_CFG_GET_CLASS(x86ms->igvm) - ->process(x86ms->igvm, machine->cgs, &error_fatal) < 0) { + ->process(x86ms->igvm, machine->cgs, false, &error_fatal) < 0) { g_assert_not_reached(); } } diff --git a/include/system/igvm-cfg.h b/include/system/igvm-cfg.h index 321b3196f0..944f23a814 100644 --- a/include/system/igvm-cfg.h +++ b/include/system/igvm-cfg.h @@ -31,11 +31,14 @@ typedef struct IgvmCfgClass { /* * If an IGVM filename has been specified then process the IGVM file. * Performs a no-op if no filename has been specified. + * If onlyVpContext is true then only the IGVM_VHT_VP_CONTEXT entries + * in the IGVM file will be processed, allowing information about the + * CPU state to be determined before processing the entire file. * * Returns 0 for ok and -1 on error. */ int (*process)(IgvmCfg *cfg, ConfidentialGuestSupport *cgs, - Error **errp); + bool onlyVpContext, Error **errp); } IgvmCfgClass; diff --git a/target/i386/sev.c b/target/i386/sev.c index 3e5722ba65..1057b8ab2c 100644 --- a/target/i386/sev.c +++ b/target/i386/sev.c @@ -118,6 +118,8 @@ struct SevCommonState { uint32_t cbitpos; uint32_t reduced_phys_bits; bool kernel_hashes; + uint64_t sev_features; + uint64_t supported_sev_features; /* runtime state */ uint8_t api_major; @@ -485,7 +487,40 @@ static void sev_apply_cpu_context(CPUState *cpu) } } -static int check_vmsa_supported(hwaddr gpa, const struct sev_es_save_area *vmsa, +static int check_sev_features(SevCommonState *sev_common, uint64_t sev_features, + Error **errp) +{ + /* + * Ensure SEV_FEATURES is configured for correct SEV hardware and that + * the requested features are supported. If SEV-SNP is enabled then + * that feature must be enabled, otherwise it must be cleared. + */ + if (sev_snp_enabled() && !(sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { + error_setg( + errp, + "%s: SEV_SNP is enabled but is not enabled in VMSA sev_features", + __func__); + return -1; + } else if (!sev_snp_enabled() && + (sev_features & SVM_SEV_FEAT_SNP_ACTIVE)) { + error_setg( + errp, + "%s: SEV_SNP is not enabled but is enabled in VMSA sev_features", + __func__); + return -1; + } + if (sev_features & ~sev_common->supported_sev_features) { + error_setg(errp, + "%s: VMSA contains unsupported sev_features: %lX, " + "supported features: %lX", + __func__, sev_features, sev_common->supported_sev_features); + return -1; + } + return 0; +} + +static int check_vmsa_supported(SevCommonState *sev_common, hwaddr gpa, + const struct sev_es_save_area *vmsa, Error **errp) { struct sev_es_save_area vmsa_check; @@ -551,24 +586,10 @@ static int check_vmsa_supported(hwaddr gpa, const struct sev_es_save_area *vmsa, vmsa_check.x87_fcw = 0; vmsa_check.mxcsr = 0; - if (sev_snp_enabled()) { - if (vmsa_check.sev_features != SVM_SEV_FEAT_SNP_ACTIVE) { - error_setg(errp, - "%s: sev_features in the VMSA contains an unsupported " - "value. For SEV-SNP, sev_features must be set to %x.", - __func__, SVM_SEV_FEAT_SNP_ACTIVE); - return -1; - } - vmsa_check.sev_features = 0; - } else { - if (vmsa_check.sev_features != 0) { - error_setg(errp, - "%s: sev_features in the VMSA contains an unsupported " - "value. For SEV-ES and SEV, sev_features must be " - "set to 0.", __func__); - return -1; - } + if (check_sev_features(sev_common, vmsa_check.sev_features, errp) < 0) { + return -1; } + vmsa_check.sev_features = 0; if (!buffer_is_zero(&vmsa_check, sizeof(vmsa_check))) { error_setg(errp, @@ -1722,6 +1743,39 @@ static int sev_snp_kvm_type(X86ConfidentialGuest *cg) return KVM_X86_SNP_VM; } +static int sev_init_supported_features(ConfidentialGuestSupport *cgs, + SevCommonState *sev_common, Error **errp) +{ + X86ConfidentialGuestClass *x86_klass = + X86_CONFIDENTIAL_GUEST_GET_CLASS(cgs); + /* + * Older kernels do not support query or setting of sev_features. In this + * case the set of supported features must be zero to match the settings + * in the kernel. + */ + if (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common)) == + KVM_X86_DEFAULT_VM) { + sev_common->supported_sev_features = 0; + return 0; + } + + /* Query KVM for the supported set of sev_features */ + struct kvm_device_attr attr = { + .group = KVM_X86_GRP_SEV, + .attr = KVM_X86_SEV_VMSA_FEATURES, + .addr = (unsigned long)&sev_common->supported_sev_features, + }; + if (kvm_ioctl(kvm_state, KVM_GET_DEVICE_ATTR, &attr) < 0) { + error_setg(errp, "%s: failed to query supported sev_features", + __func__); + return -1; + } + if (sev_snp_enabled()) { + sev_common->supported_sev_features |= SVM_SEV_FEAT_SNP_ACTIVE; + } + return 0; +} + static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) { char *devname; @@ -1802,6 +1856,10 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) } } + if (sev_init_supported_features(cgs, sev_common, errp) < 0) { + return -1; + } + trace_kvm_sev_init(); switch (x86_klass->kvm_type(X86_CONFIDENTIAL_GUEST(sev_common))) { case KVM_X86_DEFAULT_VM: @@ -1813,6 +1871,40 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp) case KVM_X86_SEV_ES_VM: case KVM_X86_SNP_VM: { struct kvm_sev_init args = { 0 }; + MachineState *machine = MACHINE(qdev_get_machine()); + X86MachineState *x86machine = X86_MACHINE(qdev_get_machine()); + + /* + * If configuration is provided via an IGVM file then the IGVM file + * might contain configuration of the initial vcpu context. For SEV + * the vcpu context includes the sev_features which should be applied + * to the vcpu. + * + * KVM does not synchronize sev_features from CPU state. Instead it + * requires sev_features to be provided as part of this initialization + * call which is subsequently automatically applied to the VMSA of + * each vcpu. + * + * The IGVM file is normally processed after initialization. Therefore + * we need to pre-process it here to extract sev_features in order to + * provide it to KVM_SEV_INIT2. Each cgs_* function that is called by + * the IGVM processor detects this pre-process by observing the state + * as SEV_STATE_UNINIT. + */ + if (x86machine->igvm) { + if (IGVM_CFG_GET_CLASS(x86machine->igvm) + ->process(x86machine->igvm, machine->cgs, true, errp) == + -1) { + return -1; + } + /* + * KVM maintains a bitmask of allowed sev_features. This does not + * include SVM_SEV_FEAT_SNP_ACTIVE which is set accordingly by KVM + * itself. Therefore we need to clear this flag. + */ + args.vmsa_features = sev_common->sev_features & + ~SVM_SEV_FEAT_SNP_ACTIVE; + } ret = sev_ioctl(sev_common->sev_fd, KVM_SEV_INIT2, &args, &fw_error); break; @@ -2416,6 +2508,24 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); SevCommonStateClass *klass = SEV_COMMON_GET_CLASS(sev_common); + if (sev_common->state == SEV_STATE_UNINIT) { + /* Pre-processing of IGVM file called from sev_common_kvm_init() */ + if ((cpu_index == 0) && (memory_type == CGS_PAGE_TYPE_VMSA)) { + const struct sev_es_save_area *sa = + (const struct sev_es_save_area *)ptr; + if (len < sizeof(*sa)) { + error_setg(errp, "%s: invalid VMSA length encountered", + __func__); + return -1; + } + if (check_sev_features(sev_common, sa->sev_features, errp) < 0) { + return -1; + } + sev_common->sev_features = sa->sev_features; + } + return 0; + } + if (!sev_enabled()) { error_setg(errp, "%s: attempt to configure guest memory, but SEV " "is not enabled", __func__); @@ -2435,7 +2545,8 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len, __func__); return -1; } - if (check_vmsa_supported(gpa, (const struct sev_es_save_area *)ptr, + if (check_vmsa_supported(sev_common, gpa, + (const struct sev_es_save_area *)ptr, errp) < 0) { return -1; } @@ -2492,6 +2603,12 @@ static int cgs_get_mem_map_entry(int index, struct e820_entry *table; int num_entries; + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + if (sev_common->state == SEV_STATE_UNINIT) { + /* Pre-processing of IGVM file called from sev_common_kvm_init() */ + return 1; + } + num_entries = e820_get_table(&table); if ((index < 0) || (index >= num_entries)) { return 1; @@ -2523,6 +2640,12 @@ static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type, uint32_t policy_data1_size, void *policy_data2, uint32_t policy_data2_size, Error **errp) { + SevCommonState *sev_common = SEV_COMMON(MACHINE(qdev_get_machine())->cgs); + if (sev_common->state == SEV_STATE_UNINIT) { + /* Pre-processing of IGVM file called from sev_common_kvm_init() */ + return 0; + } + if (policy_type != GUEST_POLICY_SEV) { error_setg(errp, "%s: Invalid guest policy type provided for SEV: %d", __func__, policy_type); From 2393fb93ed45109ecd2a07cf6e6444b1b4c8d7cb Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Tue, 1 Jul 2025 15:57:38 +0800 Subject: [PATCH 2177/2760] i386/cpu: Move the implementation of is_host_cpu_intel() host-cpu.c It's more proper to put is_host_cpu_intel() in host-cpu.c instead of vmsr_energy.c. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250701075738.3451873-3-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/host-cpu.c | 9 +++++++++ target/i386/host-cpu.h | 1 + target/i386/kvm/vmsr_energy.c | 9 --------- target/i386/kvm/vmsr_energy.h | 1 - 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index 3399edc1ad..e9a49e628f 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -161,6 +161,15 @@ void host_cpu_instance_init(X86CPU *cpu) &error_abort); } +bool is_host_cpu_intel(void) +{ + char vendor[CPUID_VENDOR_SZ + 1]; + + host_cpu_vendor_fms(vendor, NULL, NULL, NULL); + + return g_str_equal(vendor, CPUID_VENDOR_INTEL); +} + static void host_cpu_class_init(ObjectClass *oc, const void *data) { X86CPUClass *xcc = X86_CPU_CLASS(oc); diff --git a/target/i386/host-cpu.h b/target/i386/host-cpu.h index b97ec01c9b..10df4b3a3a 100644 --- a/target/i386/host-cpu.h +++ b/target/i386/host-cpu.h @@ -17,4 +17,5 @@ bool host_cpu_realizefn(CPUState *cs, Error **errp); void host_cpu_vendor_fms(char *vendor, int *family, int *model, int *stepping); +bool is_host_cpu_intel(void); #endif /* HOST_CPU_H */ diff --git a/target/i386/kvm/vmsr_energy.c b/target/i386/kvm/vmsr_energy.c index d6aad5246b..58ce3df53a 100644 --- a/target/i386/kvm/vmsr_energy.c +++ b/target/i386/kvm/vmsr_energy.c @@ -27,15 +27,6 @@ char *vmsr_compute_default_paths(void) return g_build_filename(state, "run", "qemu-vmsr-helper.sock", NULL); } -bool is_host_cpu_intel(void) -{ - char vendor[CPUID_VENDOR_SZ + 1]; - - host_cpu_vendor_fms(vendor, NULL, NULL, NULL); - - return g_str_equal(vendor, CPUID_VENDOR_INTEL); -} - int is_rapl_enabled(void) { const char *path = "/sys/class/powercap/intel-rapl/enabled"; diff --git a/target/i386/kvm/vmsr_energy.h b/target/i386/kvm/vmsr_energy.h index 16cc1f4814..151bcbd642 100644 --- a/target/i386/kvm/vmsr_energy.h +++ b/target/i386/kvm/vmsr_energy.h @@ -94,6 +94,5 @@ double vmsr_get_ratio(uint64_t e_delta, unsigned long long delta_ticks, unsigned int maxticks); void vmsr_init_topo_info(X86CPUTopoInfo *topo_info, const MachineState *ms); -bool is_host_cpu_intel(void); int is_rapl_enabled(void); #endif /* VMSR_ENERGY_H */ From 7e862c3526450fe7cb14e25b5200036c237baa03 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Mon, 30 Jun 2025 16:06:07 +0800 Subject: [PATCH 2178/2760] i386/cpu: Use CPUID_MODEL_ID_SZ instead of hardcoded 48 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is already the MACRO CPUID_MODEL_ID_SZ defined in QEMU. Use it to replace all the hardcoded 48. Opportunistically fix the indentation of CPUID_VENDOR_SZ. Signed-off-by: Xiaoyao Li Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/r/20250630080610.3151956-2-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 8 ++++---- target/i386/cpu.h | 3 ++- target/i386/host-cpu.c | 1 - 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 44178bc523..70b742fcde 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6469,11 +6469,11 @@ static char *x86_cpuid_get_model_id(Object *obj, Error **errp) char *value; int i; - value = g_malloc(48 + 1); - for (i = 0; i < 48; i++) { + value = g_malloc(CPUID_MODEL_ID_SZ + 1); + for (i = 0; i < CPUID_MODEL_ID_SZ; i++) { value[i] = env->cpuid_model[i >> 2] >> (8 * (i & 3)); } - value[48] = '\0'; + value[CPUID_MODEL_ID_SZ] = '\0'; return value; } @@ -6488,7 +6488,7 @@ static void x86_cpuid_set_model_id(Object *obj, const char *model_id, model_id = ""; } len = strlen(model_id); - memset(env->cpuid_model, 0, 48); + memset(env->cpuid_model, 0, CPUID_MODEL_ID_SZ); for (i = 0; i < 48; i++) { if (i >= len) { c = '\0'; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 9829824ac8..b3bb988857 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -1159,7 +1159,8 @@ uint64_t x86_cpu_get_supported_feature_word(X86CPU *cpu, FeatureWord w); /* PMM enabled */ #define CPUID_C000_0001_EDX_PMM_EN (1U << 13) -#define CPUID_VENDOR_SZ 12 +#define CPUID_VENDOR_SZ 12 +#define CPUID_MODEL_ID_SZ 48 #define CPUID_VENDOR_INTEL_1 0x756e6547 /* "Genu" */ #define CPUID_VENDOR_INTEL_2 0x49656e69 /* "ineI" */ diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index e9a49e628f..b1fb6d6816 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -80,7 +80,6 @@ bool host_cpu_realizefn(CPUState *cs, Error **errp) return true; } -#define CPUID_MODEL_ID_SZ 48 /** * cpu_x86_fill_model_id: * Get CPUID model ID string from host CPU. From 58f17c4a4fe9fc78bcadcdceada4c094c4b1d2c8 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Mon, 30 Jun 2025 16:06:08 +0800 Subject: [PATCH 2179/2760] i386: Cleanup the usage of CPUID_VENDOR_INTEL_1 There are code using "env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1" to check if it is Intel vcpu. Cleanup them to just use IS_INTEL_CPU() Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250630080610.3151956-3-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 2 +- target/i386/tcg/decode-new.c.inc | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 70b742fcde..d3d13b1472 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7761,7 +7761,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = env->features[FEAT_8000_0001_ECX]; *edx = env->features[FEAT_8000_0001_EDX]; - if (tcg_enabled() && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1 && + if (tcg_enabled() && IS_INTEL_CPU(env) && !(env->hflags & HF_LMA_MASK)) { *edx &= ~CPUID_EXT2_SYSCALL; } diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 55216e0d24..853b1c8bf9 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -2722,14 +2722,14 @@ static void disas_insn(DisasContext *s, CPUState *cpu) if (decode.e.check & X86_CHECK_i64) { goto illegal_op; } - if ((decode.e.check & X86_CHECK_i64_amd) && env->cpuid_vendor1 != CPUID_VENDOR_INTEL_1) { + if ((decode.e.check & X86_CHECK_i64_amd) && !IS_INTEL_CPU(env)) { goto illegal_op; } } else { if (decode.e.check & X86_CHECK_o64) { goto illegal_op; } - if ((decode.e.check & X86_CHECK_o64_intel) && env->cpuid_vendor1 == CPUID_VENDOR_INTEL_1) { + if ((decode.e.check & X86_CHECK_o64_intel) && IS_INTEL_CPU(env)) { goto illegal_op; } } From 3359b588f1f51c1ceaa72eb95cdc7e842d00a5b0 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Mon, 30 Jun 2025 16:06:09 +0800 Subject: [PATCH 2180/2760] i386/kvm-cpu: Fix the indentation inside kvm_cpu_realizefn() The indentation of one of the } inside kvm_cpu_realizefn() isn'f correct. fix it. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250630080610.3151956-4-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm-cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/i386/kvm/kvm-cpu.c b/target/i386/kvm/kvm-cpu.c index a99b876464..89a7953659 100644 --- a/target/i386/kvm/kvm-cpu.c +++ b/target/i386/kvm/kvm-cpu.c @@ -73,7 +73,7 @@ static bool kvm_cpu_realizefn(CPUState *cs, Error **errp) if (env->features[FEAT_1_ECX] & CPUID_EXT_MONITOR) { host_cpuid(5, 0, &cpu->mwait.eax, &cpu->mwait.ebx, &cpu->mwait.ecx, &cpu->mwait.edx); - } + } } if (cpu->ucode_rev == 0) { cpu->ucode_rev = From b6c5b41ba28198030ce2f2b19cf333a9129d07dc Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Mon, 30 Jun 2025 16:06:10 +0800 Subject: [PATCH 2181/2760] i386/cpu: Unify family, model and stepping calculation for x86 CPU There are multiple places where CPUID family/model/stepping info are retrieved from env->cpuid_version. Besides, the calculation of family and model inside host_cpu_vendor_fms() doesn't comply to what Intel and AMD define. For family, both Intel and AMD define that Extended Family ID needs to be counted only when (base) Family is 0xF. For model, Intel counts Extended Model when (base) Family is 0x6 or 0xF, while AMD counts EXtended MOdel when (base) Family is 0xF. Introduce generic helper functions to get family, model and stepping from the EAX value of CPUID leaf 1, with the correct calculation formula. Signed-off-by: Xiaoyao Li Link: https://lore.kernel.org/r/20250630080610.3151956-5-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 12 ++++-------- target/i386/cpu.h | 30 ++++++++++++++++++++++++++++++ target/i386/host-cpu.c | 6 +++--- target/i386/kvm/kvm.c | 2 +- 4 files changed, 38 insertions(+), 12 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index d3d13b1472..b768838b10 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6325,10 +6325,7 @@ static void x86_cpuid_version_get_family(Object *obj, Visitor *v, CPUX86State *env = &cpu->env; uint64_t value; - value = (env->cpuid_version >> 8) & 0xf; - if (value == 0xf) { - value += (env->cpuid_version >> 20) & 0xff; - } + value = x86_cpu_family(env->cpuid_version); visit_type_uint64(v, name, &value, errp); } @@ -6366,8 +6363,7 @@ static void x86_cpuid_version_get_model(Object *obj, Visitor *v, CPUX86State *env = &cpu->env; uint64_t value; - value = (env->cpuid_version >> 4) & 0xf; - value |= ((env->cpuid_version >> 16) & 0xf) << 4; + value = x86_cpu_model(env->cpuid_version); visit_type_uint64(v, name, &value, errp); } @@ -6401,7 +6397,7 @@ static void x86_cpuid_version_get_stepping(Object *obj, Visitor *v, CPUX86State *env = &cpu->env; uint64_t value; - value = env->cpuid_version & 0xf; + value = x86_cpu_stepping(env->cpuid_version); visit_type_uint64(v, name, &value, errp); } @@ -8155,7 +8151,7 @@ static void mce_init(X86CPU *cpu) CPUX86State *cenv = &cpu->env; unsigned int bank; - if (((cenv->cpuid_version >> 8) & 0xf) >= 6 + if (x86_cpu_family(cenv->cpuid_version) >= 6 && (cenv->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) == (CPUID_MCE | CPUID_MCA)) { cenv->mcg_cap = MCE_CAP_DEF | MCE_BANKS_DEF | diff --git a/target/i386/cpu.h b/target/i386/cpu.h index b3bb988857..a580562b3d 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2678,6 +2678,36 @@ static inline int32_t x86_get_a20_mask(CPUX86State *env) } } +static inline uint32_t x86_cpu_family(uint32_t eax) +{ + uint32_t family = (eax >> 8) & 0xf; + + if (family == 0xf) { + family += (eax >> 20) & 0xff; + } + + return family; +} + +static inline uint32_t x86_cpu_model(uint32_t eax) +{ + uint32_t family, model; + + family = x86_cpu_family(eax); + model = (eax >> 4) & 0xf; + + if (family >= 0x6) { + model += ((eax >> 16) & 0xf) << 4; + } + + return model; +} + +static inline uint32_t x86_cpu_stepping(uint32_t eax) +{ + return eax & 0xf; +} + static inline bool cpu_has_vmx(CPUX86State *env) { return env->features[FEAT_1_ECX] & CPUID_EXT_VMX; diff --git a/target/i386/host-cpu.c b/target/i386/host-cpu.c index b1fb6d6816..d5e2bb5e18 100644 --- a/target/i386/host-cpu.c +++ b/target/i386/host-cpu.c @@ -117,13 +117,13 @@ void host_cpu_vendor_fms(char *vendor, int *family, int *model, int *stepping) host_cpuid(0x1, 0, &eax, &ebx, &ecx, &edx); if (family) { - *family = ((eax >> 8) & 0x0F) + ((eax >> 20) & 0xFF); + *family = x86_cpu_family(eax); } if (model) { - *model = ((eax >> 4) & 0x0F) | ((eax & 0xF0000) >> 12); + *model = x86_cpu_model(eax); } if (stepping) { - *stepping = eax & 0x0F; + *stepping = x86_cpu_stepping(eax); } } diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index fc58a23b30..e8c8be09ba 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -2259,7 +2259,7 @@ int kvm_arch_init_vcpu(CPUState *cs) cpuid_i = kvm_x86_build_cpuid(env, cpuid_data.entries, cpuid_i); cpuid_data.cpuid.nent = cpuid_i; - if (((env->cpuid_version >> 8)&0xF) >= 6 + if (x86_cpu_family(env->cpuid_version) >= 6 && (env->features[FEAT_1_EDX] & (CPUID_MCE | CPUID_MCA)) == (CPUID_MCE | CPUID_MCA)) { uint64_t mcg_cap, unsupported_caps; From 50fd57418c3f08f13eb964dcb49f065246f2ecbf Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Wed, 25 Jun 2025 11:55:05 +0800 Subject: [PATCH 2182/2760] i386/tdx: Remove task->watch only when it's valid In some case (e.g., failed to connect to QGS socket), tdx_generate_quote_cleanup() is called with task->watch invalid. It triggers assertion of qemu-system-x86_64: GLib: g_source_remove: assertion 'tag > 0' failed Fix it by checking task->watch. Fixes: 40da501d8989 ("i386/tdx: handle TDG.VP.VMCALL") Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250625035505.2770580-1-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx-quote-generator.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/i386/kvm/tdx-quote-generator.c b/target/i386/kvm/tdx-quote-generator.c index f59715f617..dee8334b27 100644 --- a/target/i386/kvm/tdx-quote-generator.c +++ b/target/i386/kvm/tdx-quote-generator.c @@ -75,7 +75,9 @@ static void tdx_generate_quote_cleanup(TdxGenerateQuoteTask *task) { timer_del(&task->timer); - g_source_remove(task->watch); + if (task->watch) { + g_source_remove(task->watch); + } qio_channel_close(QIO_CHANNEL(task->sioc), NULL); object_unref(OBJECT(task->sioc)); From 7ff24fb657d35c014f735f69aef03810fde607ab Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Wed, 25 Jun 2025 11:57:10 +0800 Subject: [PATCH 2183/2760] i386/tdx: Don't mask off CPUID_EXT_PDCM It gets below warning when booting TDX VMs: warning: TDX forcibly sets the feature: CPUID[eax=01h].ECX.pdcm [bit 15] Because CPUID_EXT_PDCM is fixed1 for TDX, and MSR_IA32_PERF_CAPABILITIES is supported for TDX guest unconditioanlly. Don't mask off CPUID_EXT_PDCM for TDX. Signed-off-by: Xiaoyao Li Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250625035710.2770679-1-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index b768838b10..f9e6bc8d0e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -28,6 +28,7 @@ #include "system/hvf.h" #include "hvf/hvf-i386.h" #include "kvm/kvm_i386.h" +#include "kvm/tdx.h" #include "sev.h" #include "qapi/error.h" #include "qemu/error-report.h" @@ -8336,7 +8337,8 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) } } - if (!cpu->enable_pmu) { + /* PDCM is fixed1 bit for TDX */ + if (!cpu->enable_pmu && !is_tdx_vm()) { mark_unavailable_features(cpu, FEAT_1_ECX, env->user_features[FEAT_1_ECX] & CPUID_EXT_PDCM, "This feature is not available due to PMU being disabled"); From 1034b94fe98da35857f261d4db89ce2ebed8c5c3 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:26 +0800 Subject: [PATCH 2184/2760] i386/cpu: Refine comment of CPUID2CacheDescriptorInfo Refer to SDM vol.3 table 1-21, add the notes about the missing descriptor, and fix the typo and comment format. Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index f9e6bc8d0e..f107e58673 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -68,6 +68,7 @@ struct CPUID2CacheDescriptorInfo { /* * Known CPUID 2 cache descriptors. + * TLB, prefetch and sectored cache related descriptors are not included. * From Intel SDM Volume 2A, CPUID instruction */ struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { @@ -89,18 +90,29 @@ struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { .associativity = 2, .line_size = 64, }, [0x21] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, .associativity = 8, .line_size = 64, }, - /* lines per sector is not supported cpuid2_cache_descriptor(), - * so descriptors 0x22, 0x23 are not included - */ + /* + * lines per sector is not supported cpuid2_cache_descriptor(), + * so descriptors 0x22, 0x23 are not included + */ [0x24] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, .associativity = 16, .line_size = 64, }, - /* lines per sector is not supported cpuid2_cache_descriptor(), - * so descriptors 0x25, 0x20 are not included - */ + /* + * lines per sector is not supported cpuid2_cache_descriptor(), + * so descriptors 0x25, 0x29 are not included + */ [0x2C] = { .level = 1, .type = DATA_CACHE, .size = 32 * KiB, .associativity = 8, .line_size = 64, }, [0x30] = { .level = 1, .type = INSTRUCTION_CACHE, .size = 32 * KiB, .associativity = 8, .line_size = 64, }, + /* + * Newer Intel CPUs (having the cores without L3, e.g., Intel MTL, ARL) + * use CPUID 0x4 leaf to describe cache topology, by encoding CPUID 0x2 + * leaf with 0xFF. For older CPUs (without 0x4 leaf), it's also valid + * to just ignore L3's code if there's no L3. + * + * This already covers all the cases in QEMU, so code 0x40 is not + * included. + */ [0x41] = { .level = 2, .type = UNIFIED_CACHE, .size = 128 * KiB, .associativity = 4, .line_size = 32, }, [0x42] = { .level = 2, .type = UNIFIED_CACHE, .size = 256 * KiB, @@ -138,9 +150,10 @@ struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { .associativity = 4, .line_size = 64, }, [0x78] = { .level = 2, .type = UNIFIED_CACHE, .size = 1 * MiB, .associativity = 4, .line_size = 64, }, - /* lines per sector is not supported cpuid2_cache_descriptor(), - * so descriptors 0x79, 0x7A, 0x7B, 0x7C are not included. - */ + /* + * lines per sector is not supported cpuid2_cache_descriptor(), + * so descriptors 0x79, 0x7A, 0x7B, 0x7C are not included. + */ [0x7D] = { .level = 2, .type = UNIFIED_CACHE, .size = 2 * MiB, .associativity = 8, .line_size = 64, }, [0x7F] = { .level = 2, .type = UNIFIED_CACHE, .size = 512 * KiB, From 67d54014afcfdd316a0e9c979a0ff8145d4cf963 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:27 +0800 Subject: [PATCH 2185/2760] i386/cpu: Add descriptor 0x49 for CPUID 0x2 encoding The legacy_l2_cache (2nd-level cache: 4 MByte, 16-way set associative, 64 byte line size) corresponds to descriptor 0x49, but at present cpuid2_cache_descriptors doesn't support descriptor 0x49 because it has multiple meanings. The 0x49 is necessary when CPUID 0x2 and 0x4 leaves have the consistent cache model, and use legacy_l2_cache as the default L2 cache. Therefore, add descriptor 0x49 to represent general L2 cache. Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index f107e58673..4386b60ff6 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -129,7 +129,18 @@ struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { .associativity = 8, .line_size = 64, }, [0x48] = { .level = 2, .type = UNIFIED_CACHE, .size = 3 * MiB, .associativity = 12, .line_size = 64, }, - /* Descriptor 0x49 depends on CPU family/model, so it is not included */ + /* + * Descriptor 0x49 has 2 cases: + * - 2nd-level cache: 4 MByte, 16-way set associative, 64 byte line size. + * - 3rd-level cache: 4MB, 16-way set associative, 64-byte line size + * (Intel Xeon processor MP, Family 0FH, Model 06H). + * + * When it represents L3, then it depends on CPU family/model. Fortunately, + * the legacy cache/CPU models don't have such special L3. So, just add it + * to represent the general L2 case. + */ + [0x49] = { .level = 2, .type = UNIFIED_CACHE, .size = 4 * MiB, + .associativity = 16, .line_size = 64, }, [0x4A] = { .level = 3, .type = UNIFIED_CACHE, .size = 6 * MiB, .associativity = 12, .line_size = 64, }, [0x4B] = { .level = 3, .type = UNIFIED_CACHE, .size = 8 * MiB, From 2c70a50c8c3d8620f34884b340778358b6faf5d4 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:28 +0800 Subject: [PATCH 2186/2760] i386/cpu: Add default cache model for Intel CPUs with level < 4 Old Intel CPUs with CPUID level < 4, use CPUID 0x2 leaf (if available) to encode cache information. Introduce a cache model "legacy_intel_cpuid2_cache_info" for the CPUs with CPUID level < 4, based on legacy_l1d_cache, legacy_l1i_cache, legacy_l2_cache_cpuid2 and legacy_l3_cache. But for L2 cache, this cache model completes self_init, sets, partitions, no_invd_sharing and share_level fields, referring legacy_l2_cache, to avoid someone increases CPUID level manually and meets assert() error. But the cache information present in CPUID 0x2 leaf doesn't change. This new cache model makes it possible to remove legacy_l2_cache_cpuid2 in X86CPUState and help to clarify historical cache inconsistency issue. Furthermore, apply this legacy cache model to all Intel CPUs with CPUID level < 4. This includes not only "pentium2" and "pentium3" (which have 0x2 leaf), but also "486" and "pentium" (which only have 0x1 leaf, and cache model won't be presented, just for simplicity). A legacy_intel_cpuid2_cache_info cache model doesn't change the cache information of the above CPUs, because they just depend on 0x2 leaf. Only when someone adjusts the min-level to >=4 will the cache information in CPUID leaf 4 differ from before: previously, the L2 cache information in CPUID leaf 0x2 and 0x4 was different, but now with legacy_intel_cpuid2_cache_info, the information they present will be consistent. This case almost never happens, emulating a CPUID that is not supported by the "ancient" hardware is itself meaningless behavior. Therefore, even though there's the above difference (for really rare case) and considering these old CPUs ("486", "pentium", "pentium2" and "pentium3") won't be used for migration, there's no need to add new versioned CPU models Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-4-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4386b60ff6..3278d5de5a 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -712,6 +712,67 @@ static CPUCacheInfo legacy_l3_cache = { .share_level = CPU_TOPOLOGY_LEVEL_DIE, }; +/* + * Only used for the CPU models with CPUID level < 4. + * These CPUs (CPUID level < 4) only use CPUID leaf 2 to present + * cache information. + * + * Note: This cache model is just a default one, and is not + * guaranteed to match real hardwares. + */ +static const CPUCaches legacy_intel_cpuid2_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 64, + .partitions = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 32 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 64, + .partitions = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 2 * MiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 4096, + .partitions = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 16 * MiB, + .line_size = 64, + .associativity = 16, + .sets = 16384, + .partitions = 1, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + /* TLB definitions: */ #define L1_DTLB_2M_ASSOC 1 @@ -3045,6 +3106,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { I486_FEATURES, .xlevel = 0, .model_id = "", + .cache_info = &legacy_intel_cpuid2_cache_info, }, { .name = "pentium", @@ -3057,6 +3119,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { PENTIUM_FEATURES, .xlevel = 0, .model_id = "", + .cache_info = &legacy_intel_cpuid2_cache_info, }, { .name = "pentium2", @@ -3069,6 +3132,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { PENTIUM2_FEATURES, .xlevel = 0, .model_id = "", + .cache_info = &legacy_intel_cpuid2_cache_info, }, { .name = "pentium3", @@ -3081,6 +3145,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { PENTIUM3_FEATURES, .xlevel = 0, .model_id = "", + .cache_info = &legacy_intel_cpuid2_cache_info, }, { .name = "athlon", From 6699e5dc864b0793e4fd90ef8038f6c71fcd0d47 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:29 +0800 Subject: [PATCH 2187/2760] i386/cpu: Present same cache model in CPUID 0x2 & 0x4 For a long time, the default cache models used in CPUID 0x2 and 0x4 were inconsistent and had a FIXME note from Eduardo at commit 5e891bf8fd50 ("target-i386: Use #defines instead of magic numbers for CPUID cache info"): "/*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */". This difference is wrong, in principle, both 0x2 and 0x4 are used for Intel's cache description. 0x2 leaf is used for ancient machines while 0x4 leaf is a subsequent addition, and both should be based on the same cache model. Furthermore, on real hardware, 0x4 leaf should be used in preference to 0x2 when it is available. Revisiting the git history, that difference occurred much earlier. Current legacy_l2_cache_cpuid2 (hardcode: "0x2c307d"), which is used for CPUID 0x2 leaf, is introduced in commit d8134d91d9b7 ("Intel cache info, by Filip Navara."). Its commit message didn't said anything, but its patch [1] mentioned the cache model chosen is "closest to the ones reported in the AMD registers". Now it is not possible to check which AMD generation this cache model is based on (unfortunately, AMD does not use 0x2 leaf), but at least it is close to the Pentium 4. In fact, the patch description of commit d8134d91d9b7 is also a bit wrong, the original cache model in leaf 2 is from Pentium Pro, and its cache descriptor had specified the cache line size ad 32 byte by default, while the updated cache model in commit d8134d91d9b7 has 64 byte line size. But after so many years, such judgments are no longer meaningful. On the other hand, for legacy_l2_cache, which is used in CPUID 0x4 leaf, is based on Intel Core Duo (patch [2]) and Core2 Duo (commit e737b32a3688 ("Core 2 Duo specification (Alexander Graf).") The patches of Core Duo and Core 2 Duo add the cache model for CPUID 0x4, but did not update CPUID 0x2 encoding. This is the reason that Intel Guests use two cache models in 0x2 and 0x4 all the time. Of course, while no Core Duo or Core 2 Duo machines have been found for double checking, this still makes no sense to encode different cache models on a single machine. Referring to the SDM and the real hardware available, 0x2 leaf can be directly encoded 0xFF to instruct software to go to 0x4 leaf to get the cache information, when 0x4 is available. Therefore, it's time to clean up Intel's default cache models. As the first step, add "x-consistent-cache" compat option to allow newer machines (v10.1 and newer) to have the consistent cache model in CPUID 0x2 and 0x4 leaves. This doesn't affect the CPU models with CPUID level < 4 ("486", "pentium", "pentium2" and "pentium3"), because they have already had the special default cache model - legacy_intel_cpuid2_cache_info. [1]: https://lore.kernel.org/qemu-devel/5b31733c0709081227w3e5f1036odbc649edfdc8c79b@mail.gmail.com/ [2]: https://lore.kernel.org/qemu-devel/478B65C8.2080602@csgraf.de/ Cc: Alexander Graf Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-5-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 4 +++- target/i386/cpu.c | 7 ++++++- target/i386/cpu.h | 7 +++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index 432ab288a8..a6fa792368 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -81,7 +81,9 @@ { "qemu64-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, },\ { "athlon-" TYPE_X86_CPU, "model-id", "QEMU Virtual CPU version " v, }, -GlobalProperty pc_compat_10_0[] = {}; +GlobalProperty pc_compat_10_0[] = { + { TYPE_X86_CPU, "x-consistent-cache", "false" }, +}; const size_t pc_compat_10_0_len = G_N_ELEMENTS(pc_compat_10_0); GlobalProperty pc_compat_9_2[] = {}; diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 3278d5de5a..1f27fcf3ee 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8935,7 +8935,11 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) /* Build legacy cache information */ env->cache_info_cpuid2.l1d_cache = &legacy_l1d_cache; env->cache_info_cpuid2.l1i_cache = &legacy_l1i_cache; - env->cache_info_cpuid2.l2_cache = &legacy_l2_cache_cpuid2; + if (!cpu->consistent_cache) { + env->cache_info_cpuid2.l2_cache = &legacy_l2_cache_cpuid2; + } else { + env->cache_info_cpuid2.l2_cache = &legacy_l2_cache; + } env->cache_info_cpuid2.l3_cache = &legacy_l3_cache; env->cache_info_cpuid4.l1d_cache = &legacy_l1d_cache; @@ -9461,6 +9465,7 @@ static const Property x86_cpu_properties[] = { * own cache information (see x86_cpu_load_def()). */ DEFINE_PROP_BOOL("legacy-cache", X86CPU, legacy_cache, true), + DEFINE_PROP_BOOL("x-consistent-cache", X86CPU, consistent_cache, true), DEFINE_PROP_BOOL("legacy-multi-node", X86CPU, legacy_multi_node, false), DEFINE_PROP_BOOL("xen-vapic", X86CPU, xen_vapic, false), diff --git a/target/i386/cpu.h b/target/i386/cpu.h index a580562b3d..a3ebd3e08c 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2259,6 +2259,13 @@ struct ArchCPU { */ bool legacy_cache; + /* + * Compatibility bits for old machine types. + * If true, use the same cache model in CPUID leaf 0x2 + * and 0x4. + */ + bool consistent_cache; + /* Compatibility bits for old machine types. * If true decode the CPUID Function 0x8000001E_ECX to support multiple * nodes per processor From fe77a78149359485459db26814af436cfc873afe Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:30 +0800 Subject: [PATCH 2188/2760] i386/cpu: Consolidate CPUID 0x4 leaf Modern Intel CPUs use CPUID 0x4 leaf to describe cache information and leave space in 0x2 for prefetch and TLBs (even TLB has its own leaf CPUID 0x18). And 0x2 leaf provides a descriptor 0xFF to instruct software to check cache information in 0x4 leaf instead. Therefore, follow this behavior to encode 0xFF when Intel CPU has 0x4 leaf with "x-consistent-cache=true" for compatibility. In addition, for older CPUs without 0x4 leaf, still enumerate the cache descriptor in 0x2 leaf, except the case that there's no descriptor matching the cache model, then directly encode 0xFF in 0x2 leaf. This makes sense, as in the 0x2 leaf era, all supported caches should have the corresponding descriptor. Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-6-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 48 ++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 1f27fcf3ee..812e85b952 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -225,7 +225,7 @@ struct CPUID2CacheDescriptorInfo cpuid2_cache_descriptors[] = { * Return a CPUID 2 cache descriptor for a given cache. * If no known descriptor is found, return CACHE_DESCRIPTOR_UNAVAILABLE */ -static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache) +static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache, bool *unmacthed) { int i; @@ -242,9 +242,44 @@ static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache) } } + *unmacthed |= true; return CACHE_DESCRIPTOR_UNAVAILABLE; } +/* Encode cache info for CPUID[2] */ +static void encode_cache_cpuid2(X86CPU *cpu, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + CPUX86State *env = &cpu->env; + CPUCaches *caches = &env->cache_info_cpuid2; + int l1d, l1i, l2, l3; + bool unmatched = false; + + *eax = 1; /* Number of CPUID[EAX=2] calls required */ + *ebx = *ecx = *edx = 0; + + l1d = cpuid2_cache_descriptor(caches->l1d_cache, &unmatched); + l1i = cpuid2_cache_descriptor(caches->l1i_cache, &unmatched); + l2 = cpuid2_cache_descriptor(caches->l2_cache, &unmatched); + l3 = cpuid2_cache_descriptor(caches->l3_cache, &unmatched); + + if (!cpu->consistent_cache || + (env->cpuid_min_level < 0x4 && !unmatched)) { + /* + * Though SDM defines code 0x40 for cases with no L2 or L3. It's + * also valid to just ignore l3's code if there's no l2. + */ + if (cpu->enable_l3_cache) { + *ecx = l3; + } + *edx = (l1d << 16) | (l1i << 8) | l2; + } else { + *ecx = 0; + *edx = CACHE_DESCRIPTOR_UNAVAILABLE; + } +} + /* CPUID Leaf 4 constants: */ /* EAX: */ @@ -7446,16 +7481,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *eax = *ebx = *ecx = *edx = 0; break; } - *eax = 1; /* Number of CPUID[EAX=2] calls required */ - *ebx = 0; - if (!cpu->enable_l3_cache) { - *ecx = 0; - } else { - *ecx = cpuid2_cache_descriptor(env->cache_info_cpuid2.l3_cache); - } - *edx = (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1d_cache) << 16) | - (cpuid2_cache_descriptor(env->cache_info_cpuid2.l1i_cache) << 8) | - (cpuid2_cache_descriptor(env->cache_info_cpuid2.l2_cache)); + encode_cache_cpuid2(cpu, eax, ebx, ecx, edx); break; case 4: /* cache info: needed for Core compatibility */ From c416411c28a2b21e590943f300ce0336f109abb5 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:31 +0800 Subject: [PATCH 2189/2760] i386/cpu: Drop CPUID 0x2 specific cache info in X86CPUState With the pre-defined cache model legacy_intel_cpuid2_cache_info, for X86CPUState there's no need to cache special cache information for CPUID 0x2 leaf. Drop the cache_info_cpuid2 field of X86CPUState and use the legacy_intel_cpuid2_cache_info directly. Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-7-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 31 +++++++++++-------------------- target/i386/cpu.h | 3 ++- 2 files changed, 13 insertions(+), 21 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 812e85b952..ac22548f47 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -246,19 +246,27 @@ static uint8_t cpuid2_cache_descriptor(CPUCacheInfo *cache, bool *unmacthed) return CACHE_DESCRIPTOR_UNAVAILABLE; } +static const CPUCaches legacy_intel_cpuid2_cache_info; + /* Encode cache info for CPUID[2] */ static void encode_cache_cpuid2(X86CPU *cpu, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { CPUX86State *env = &cpu->env; - CPUCaches *caches = &env->cache_info_cpuid2; + const CPUCaches *caches; int l1d, l1i, l2, l3; bool unmatched = false; *eax = 1; /* Number of CPUID[EAX=2] calls required */ *ebx = *ecx = *edx = 0; + if (env->enable_legacy_cpuid2_cache) { + caches = &legacy_intel_cpuid2_cache_info; + } else { + caches = &env->cache_info_cpuid4; + } + l1d = cpuid2_cache_descriptor(caches->l1d_cache, &unmatched); l1i = cpuid2_cache_descriptor(caches->l1i_cache, &unmatched); l2 = cpuid2_cache_descriptor(caches->l2_cache, &unmatched); @@ -707,17 +715,6 @@ static CPUCacheInfo legacy_l2_cache = { .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; -/*FIXME: CPUID leaf 2 descriptor is inconsistent with CPUID leaf 4 */ -static CPUCacheInfo legacy_l2_cache_cpuid2 = { - .type = UNIFIED_CACHE, - .level = 2, - .size = 2 * MiB, - .line_size = 64, - .associativity = 8, - .share_level = CPU_TOPOLOGY_LEVEL_INVALID, -}; - - /*FIXME: CPUID leaf 0x80000006 is inconsistent with leaves 2 & 4 */ static CPUCacheInfo legacy_l2_cache_amd = { .type = UNIFIED_CACHE, @@ -8955,18 +8952,12 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) "CPU model '%s' doesn't support legacy-cache=off", name); return; } - env->cache_info_cpuid2 = env->cache_info_cpuid4 = env->cache_info_amd = - *cache_info; + env->cache_info_cpuid4 = env->cache_info_amd = *cache_info; } else { /* Build legacy cache information */ - env->cache_info_cpuid2.l1d_cache = &legacy_l1d_cache; - env->cache_info_cpuid2.l1i_cache = &legacy_l1i_cache; if (!cpu->consistent_cache) { - env->cache_info_cpuid2.l2_cache = &legacy_l2_cache_cpuid2; - } else { - env->cache_info_cpuid2.l2_cache = &legacy_l2_cache; + env->enable_legacy_cpuid2_cache = true; } - env->cache_info_cpuid2.l3_cache = &legacy_l3_cache; env->cache_info_cpuid4.l1d_cache = &legacy_l1d_cache; env->cache_info_cpuid4.l1i_cache = &legacy_l1i_cache; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index a3ebd3e08c..d3f7c53e30 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2077,7 +2077,8 @@ typedef struct CPUArchState { * on each CPUID leaf will be different, because we keep compatibility * with old QEMU versions. */ - CPUCaches cache_info_cpuid2, cache_info_cpuid4, cache_info_amd; + CPUCaches cache_info_cpuid4, cache_info_amd; + bool enable_legacy_cpuid2_cache; /* MTRRs */ uint64_t mtrr_fixed[11]; From 216d9bb6d77162a93a0f09d72fdabfd252d941ce Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:32 +0800 Subject: [PATCH 2190/2760] i386/cpu: Add x-vendor-cpuid-only-v2 option for compatibility Add a compat property "x-vendor-cpuid-only-v2" (for PC machine v10.0 and older) to keep the original behavior. This property will be used to adjust vendor specific CPUID fields. Make x-vendor-cpuid-only-v2 depend on x-vendor-cpuid-only. Although x-vendor-cpuid-only and v2 should be initernal only, QEMU doesn't support "internal" property. To avoid any other unexpected issues, check the dependency. Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-8-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- hw/i386/pc.c | 1 + target/i386/cpu.c | 10 ++++++++++ target/i386/cpu.h | 11 ++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/hw/i386/pc.c b/hw/i386/pc.c index a6fa792368..7cfa61c9ee 100644 --- a/hw/i386/pc.c +++ b/hw/i386/pc.c @@ -83,6 +83,7 @@ GlobalProperty pc_compat_10_0[] = { { TYPE_X86_CPU, "x-consistent-cache", "false" }, + { TYPE_X86_CPU, "x-vendor-cpuid-only-v2", "false" }, }; const size_t pc_compat_10_0_len = G_N_ELEMENTS(pc_compat_10_0); diff --git a/target/i386/cpu.c b/target/i386/cpu.c index ac22548f47..630a40d72b 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8749,6 +8749,16 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) tcg_cflags_set(cs, CF_PCREL); #endif + /* + * x-vendor-cpuid-only and v2 should be initernal only. But + * QEMU doesn't support "internal" property. + */ + if (!cpu->vendor_cpuid_only && cpu->vendor_cpuid_only_v2) { + error_setg(errp, "x-vendor-cpuid-only-v2 property " + "depends on x-vendor-cpuid-only"); + return; + } + if (cpu->apic_id == UNASSIGNED_APIC_ID) { error_setg(errp, "apic-id property was not initialized properly"); return; diff --git a/target/i386/cpu.h b/target/i386/cpu.h index d3f7c53e30..d88481ba8e 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2282,9 +2282,18 @@ struct ArchCPU { /* Enable auto level-increase for all CPUID leaves */ bool full_cpuid_auto_level; - /* Only advertise CPUID leaves defined by the vendor */ + /* + * Compatibility bits for old machine types (PC machine v6.0 and older). + * Only advertise CPUID leaves defined by the vendor. + */ bool vendor_cpuid_only; + /* + * Compatibility bits for old machine types (PC machine v10.0 and older). + * Only advertise CPUID leaves defined by the vendor. + */ + bool vendor_cpuid_only_v2; + /* Only advertise TOPOEXT features that AMD defines */ bool amd_topoext_features_only; From 225aad5a7b78b3dc017996ab5b5e1b9249c036a0 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:33 +0800 Subject: [PATCH 2191/2760] i386/cpu: Mark CPUID[0x80000005] as reserved for Intel Per SDM, 0x80000005 leaf is reserved for Intel CPU, and its current "assert" check blocks adding new cache model for non-AMD CPUs. And please note, although Zhaoxin mostly follows Intel behavior, this leaf is an exception [1]. So, with the compat property "x-vendor-cpuid-only-v2", for the machine since v10.1, check the vendor and encode this leaf as all-0 only for Intel CPU. In addition, drop lines_per_tag assertion in encode_cache_cpuid80000005(), since Zhaoxin will use legacy Intel cache model in this leaf - which doesn't have this field. This fix also resolves 2 FIXMEs of legacy_l1d_cache_amd and legacy_l1i_cache_amd: /*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ In addition, per AMD's APM, update the comment of CPUID[0x80000005]. [1]: https://lore.kernel.org/qemu-devel/fa16f7a8-4917-4731-9d9f-7d4c10977168@zhaoxin.com/ Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-9-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 630a40d72b..2abcb5acff 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -499,7 +499,6 @@ static void encode_topo_cpuid1f(CPUX86State *env, uint32_t count, static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache) { assert(cache->size % 1024 == 0); - assert(cache->lines_per_tag > 0); assert(cache->associativity > 0); assert(cache->line_size > 0); return ((cache->size / 1024) << 24) | (cache->associativity << 16) | @@ -657,7 +656,6 @@ static CPUCacheInfo legacy_l1d_cache = { .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; -/*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ static CPUCacheInfo legacy_l1d_cache_amd = { .type = DATA_CACHE, .level = 1, @@ -686,7 +684,6 @@ static CPUCacheInfo legacy_l1i_cache = { .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; -/*FIXME: CPUID leaf 0x80000005 is inconsistent with leaves 2 & 4 */ static CPUCacheInfo legacy_l1i_cache_amd = { .type = INSTRUCTION_CACHE, .level = 1, @@ -7884,11 +7881,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = env->cpuid_model[(index - 0x80000002) * 4 + 3]; break; case 0x80000005: - /* cache info (L1 cache) */ + /* cache info (L1 cache/TLB Associativity Field) */ if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, 0, eax, ebx, ecx, edx); break; } + + if (cpu->vendor_cpuid_only_v2 && IS_INTEL_CPU(env)) { + *eax = *ebx = *ecx = *edx = 0; + break; + } + *eax = (L1_DTLB_2M_ASSOC << 24) | (L1_DTLB_2M_ENTRIES << 16) | (L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES); *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | @@ -9478,6 +9481,7 @@ static const Property x86_cpu_properties[] = { DEFINE_PROP_STRING("hv-vendor-id", X86CPU, hyperv_vendor), DEFINE_PROP_BOOL("cpuid-0xb", X86CPU, enable_cpuid_0xb, true), DEFINE_PROP_BOOL("x-vendor-cpuid-only", X86CPU, vendor_cpuid_only, true), + DEFINE_PROP_BOOL("x-vendor-cpuid-only-v2", X86CPU, vendor_cpuid_only_v2, true), DEFINE_PROP_BOOL("x-amd-topoext-features-only", X86CPU, amd_topoext_features_only, true), DEFINE_PROP_BOOL("lmce", X86CPU, enable_lmce, false), DEFINE_PROP_BOOL("l3-cache", X86CPU, enable_l3_cache, true), From 3275a379c8ff0862550910a97e8c0c9b056643cd Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:34 +0800 Subject: [PATCH 2192/2760] i386/cpu: Rename AMD_ENC_ASSOC to X86_ENC_ASSOC Rename AMD_ENC_ASSOC to X86_ENC_ASSOC since Intel also uses the same rules. While there are some slight differences between the rules in AMD APM v4.07 no.40332 and Intel. But considerring the needs of current QEMU, generally they are consistent and current AMD_ENC_ASSOC can be applied for Intel CPUs.. Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-10-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 2abcb5acff..8a97272b4a 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -507,8 +507,8 @@ static uint32_t encode_cache_cpuid80000005(CPUCacheInfo *cache) #define ASSOC_FULL 0xFF -/* AMD associativity encoding used on CPUID Leaf 0x80000006: */ -#define AMD_ENC_ASSOC(a) (a <= 1 ? a : \ +/* x86 associativity encoding used on CPUID Leaf 0x80000006: */ +#define X86_ENC_ASSOC(a) (a <= 1 ? a : \ a == 2 ? 0x2 : \ a == 4 ? 0x4 : \ a == 8 ? 0x6 : \ @@ -534,7 +534,7 @@ static void encode_cache_cpuid80000006(CPUCacheInfo *l2, assert(l2->lines_per_tag > 0); assert(l2->line_size > 0); *ecx = ((l2->size / 1024) << 16) | - (AMD_ENC_ASSOC(l2->associativity) << 12) | + (X86_ENC_ASSOC(l2->associativity) << 12) | (l2->lines_per_tag << 8) | (l2->line_size); if (l3) { @@ -543,7 +543,7 @@ static void encode_cache_cpuid80000006(CPUCacheInfo *l2, assert(l3->lines_per_tag > 0); assert(l3->line_size > 0); *edx = ((l3->size / (512 * 1024)) << 18) | - (AMD_ENC_ASSOC(l3->associativity) << 12) | + (X86_ENC_ASSOC(l3->associativity) << 12) | (l3->lines_per_tag << 8) | (l3->line_size); } else { *edx = 0; @@ -7905,13 +7905,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, x86_cpu_get_cache_cpuid(index, 0, eax, ebx, ecx, edx); break; } - *eax = (AMD_ENC_ASSOC(L2_DTLB_2M_ASSOC) << 28) | + *eax = (X86_ENC_ASSOC(L2_DTLB_2M_ASSOC) << 28) | (L2_DTLB_2M_ENTRIES << 16) | - (AMD_ENC_ASSOC(L2_ITLB_2M_ASSOC) << 12) | + (X86_ENC_ASSOC(L2_ITLB_2M_ASSOC) << 12) | (L2_ITLB_2M_ENTRIES); - *ebx = (AMD_ENC_ASSOC(L2_DTLB_4K_ASSOC) << 28) | + *ebx = (X86_ENC_ASSOC(L2_DTLB_4K_ASSOC) << 28) | (L2_DTLB_4K_ENTRIES << 16) | - (AMD_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | + (X86_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | (L2_ITLB_4K_ENTRIES); encode_cache_cpuid80000006(env->cache_info_amd.l2_cache, cpu->enable_l3_cache ? From 63dc9f52f63475f61404a7f5a010aa352ead8763 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:35 +0800 Subject: [PATCH 2193/2760] i386/cpu: Fix CPUID[0x80000006] for Intel CPU Per SDM, Intel supports CPUID[0x80000006]. But only L2 information is encoded in ECX (note that L2 associativity field encodings rules consistent with AMD are used), all other fields are reserved. Therefore, make the following changes to CPUID[0x80000006]: * Check the vendor in CPUID[0x80000006] and just encode L2 to ECX for Intel. * Drop the lines_per_tag assertion, since AMD supports this field but Intel doesn't. And this field can be easily checked via cpuid tool in Guest. * Apply the encoding change of Intel for Zhaoxin as well [1]. This fix also resolves the FIXME of legacy_l2_cache_amd: /*FIXME: CPUID leaf 0x80000006 is inconsistent with leaves 2 & 4 */ In addition, per AMD's APM, update the comment of CPUID[0x80000006]. [1]: https://lore.kernel.org/qemu-devel/c522ebb5-04d5-49c6-9ad8-d755b8998988@zhaoxin.com/ Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-11-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 8a97272b4a..6a0e8592bb 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -531,16 +531,15 @@ static void encode_cache_cpuid80000006(CPUCacheInfo *l2, { assert(l2->size % 1024 == 0); assert(l2->associativity > 0); - assert(l2->lines_per_tag > 0); assert(l2->line_size > 0); *ecx = ((l2->size / 1024) << 16) | (X86_ENC_ASSOC(l2->associativity) << 12) | (l2->lines_per_tag << 8) | (l2->line_size); + /* For Intel, EDX is reserved. */ if (l3) { assert(l3->size % (512 * 1024) == 0); assert(l3->associativity > 0); - assert(l3->lines_per_tag > 0); assert(l3->line_size > 0); *edx = ((l3->size / (512 * 1024)) << 18) | (X86_ENC_ASSOC(l3->associativity) << 12) | @@ -712,7 +711,6 @@ static CPUCacheInfo legacy_l2_cache = { .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; -/*FIXME: CPUID leaf 0x80000006 is inconsistent with leaves 2 & 4 */ static CPUCacheInfo legacy_l2_cache_amd = { .type = UNIFIED_CACHE, .level = 2, @@ -7900,11 +7898,20 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = encode_cache_cpuid80000005(env->cache_info_amd.l1i_cache); break; case 0x80000006: - /* cache info (L2 cache) */ + /* cache info (L2 cache/TLB/L3 cache) */ if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, 0, eax, ebx, ecx, edx); break; } + + if (cpu->vendor_cpuid_only_v2 && + (IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { + *eax = *ebx = 0; + encode_cache_cpuid80000006(env->cache_info_cpuid4.l2_cache, + NULL, ecx, edx); + break; + } + *eax = (X86_ENC_ASSOC(L2_DTLB_2M_ASSOC) << 28) | (L2_DTLB_2M_ENTRIES << 16) | (X86_ENC_ASSOC(L2_ITLB_2M_ASSOC) << 12) | @@ -7913,6 +7920,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (L2_DTLB_4K_ENTRIES << 16) | (X86_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | (L2_ITLB_4K_ENTRIES); + encode_cache_cpuid80000006(env->cache_info_amd.l2_cache, cpu->enable_l3_cache ? env->cache_info_amd.l3_cache : NULL, From a9b6cca2df62ab3b0ba03edb3229a0eda385f65b Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:36 +0800 Subject: [PATCH 2194/2760] i386/cpu: Add legacy_intel_cache_info cache model Based on legacy_l1d_cache, legacy_l1i_cache, legacy_l2_cache and legacy_l3_cache, build a complete legacy intel cache model, which can clarify the purpose of these trivial legacy cache models, simplify the initialization of cache info in X86CPUState, and make it easier to handle compatibility later. Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-12-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 101 +++++++++++++++++++++++++--------------------- 1 file changed, 54 insertions(+), 47 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 6a0e8592bb..d265dc8156 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -640,21 +640,6 @@ static void encode_topo_cpuid8000001e(X86CPU *cpu, X86CPUTopoInfo *topo_info, * These are legacy cache values. If there is a need to change any * of these values please use builtin_x86_defs */ - -/* L1 data cache: */ -static CPUCacheInfo legacy_l1d_cache = { - .type = DATA_CACHE, - .level = 1, - .size = 32 * KiB, - .self_init = 1, - .line_size = 64, - .associativity = 8, - .sets = 64, - .partitions = 1, - .no_invd_sharing = true, - .share_level = CPU_TOPOLOGY_LEVEL_CORE, -}; - static CPUCacheInfo legacy_l1d_cache_amd = { .type = DATA_CACHE, .level = 1, @@ -669,20 +654,6 @@ static CPUCacheInfo legacy_l1d_cache_amd = { .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; -/* L1 instruction cache: */ -static CPUCacheInfo legacy_l1i_cache = { - .type = INSTRUCTION_CACHE, - .level = 1, - .size = 32 * KiB, - .self_init = 1, - .line_size = 64, - .associativity = 8, - .sets = 64, - .partitions = 1, - .no_invd_sharing = true, - .share_level = CPU_TOPOLOGY_LEVEL_CORE, -}; - static CPUCacheInfo legacy_l1i_cache_amd = { .type = INSTRUCTION_CACHE, .level = 1, @@ -697,20 +668,6 @@ static CPUCacheInfo legacy_l1i_cache_amd = { .share_level = CPU_TOPOLOGY_LEVEL_CORE, }; -/* Level 2 unified cache: */ -static CPUCacheInfo legacy_l2_cache = { - .type = UNIFIED_CACHE, - .level = 2, - .size = 4 * MiB, - .self_init = 1, - .line_size = 64, - .associativity = 16, - .sets = 4096, - .partitions = 1, - .no_invd_sharing = true, - .share_level = CPU_TOPOLOGY_LEVEL_CORE, -}; - static CPUCacheInfo legacy_l2_cache_amd = { .type = UNIFIED_CACHE, .level = 2, @@ -800,6 +757,59 @@ static const CPUCaches legacy_intel_cpuid2_cache_info = { }, }; +static const CPUCaches legacy_intel_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 32 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 64, + .partitions = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 32 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 8, + .sets = 64, + .partitions = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 4 * MiB, + .self_init = 1, + .line_size = 64, + .associativity = 16, + .sets = 4096, + .partitions = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 16 * MiB, + .line_size = 64, + .associativity = 16, + .sets = 16384, + .partitions = 1, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + /* TLB definitions: */ #define L1_DTLB_2M_ASSOC 1 @@ -8980,10 +8990,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) env->enable_legacy_cpuid2_cache = true; } - env->cache_info_cpuid4.l1d_cache = &legacy_l1d_cache; - env->cache_info_cpuid4.l1i_cache = &legacy_l1i_cache; - env->cache_info_cpuid4.l2_cache = &legacy_l2_cache; - env->cache_info_cpuid4.l3_cache = &legacy_l3_cache; + env->cache_info_cpuid4 = legacy_intel_cache_info; env->cache_info_amd.l1d_cache = &legacy_l1d_cache_amd; env->cache_info_amd.l1i_cache = &legacy_l1i_cache_amd; From dcec3c18f9daed23bfb38786edfbd2c0c6ee71c6 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:37 +0800 Subject: [PATCH 2195/2760] i386/cpu: Add legacy_amd_cache_info cache model Based on legacy_l1d_cachei_amd, legacy_l1i_cache_amd, legacy_l2_cache_amd and legacy_l3_cache, build a complete legacy AMD cache model, which can clarify the purpose of these trivial legacy cache models, simplify the initialization of cache info in X86CPUState, and make it easier to handle compatibility later. Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-13-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 112 ++++++++++++++++++++++------------------------ 1 file changed, 53 insertions(+), 59 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index d265dc8156..92d21ce64c 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -640,60 +640,58 @@ static void encode_topo_cpuid8000001e(X86CPU *cpu, X86CPUTopoInfo *topo_info, * These are legacy cache values. If there is a need to change any * of these values please use builtin_x86_defs */ -static CPUCacheInfo legacy_l1d_cache_amd = { - .type = DATA_CACHE, - .level = 1, - .size = 64 * KiB, - .self_init = 1, - .line_size = 64, - .associativity = 2, - .sets = 512, - .partitions = 1, - .lines_per_tag = 1, - .no_invd_sharing = true, - .share_level = CPU_TOPOLOGY_LEVEL_CORE, -}; - -static CPUCacheInfo legacy_l1i_cache_amd = { - .type = INSTRUCTION_CACHE, - .level = 1, - .size = 64 * KiB, - .self_init = 1, - .line_size = 64, - .associativity = 2, - .sets = 512, - .partitions = 1, - .lines_per_tag = 1, - .no_invd_sharing = true, - .share_level = CPU_TOPOLOGY_LEVEL_CORE, -}; - -static CPUCacheInfo legacy_l2_cache_amd = { - .type = UNIFIED_CACHE, - .level = 2, - .size = 512 * KiB, - .line_size = 64, - .lines_per_tag = 1, - .associativity = 16, - .sets = 512, - .partitions = 1, - .share_level = CPU_TOPOLOGY_LEVEL_CORE, -}; - -/* Level 3 unified cache: */ -static CPUCacheInfo legacy_l3_cache = { - .type = UNIFIED_CACHE, - .level = 3, - .size = 16 * MiB, - .line_size = 64, - .associativity = 16, - .sets = 16384, - .partitions = 1, - .lines_per_tag = 1, - .self_init = true, - .inclusive = true, - .complex_indexing = true, - .share_level = CPU_TOPOLOGY_LEVEL_DIE, +static const CPUCaches legacy_amd_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + .type = DATA_CACHE, + .level = 1, + .size = 64 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 2, + .sets = 512, + .partitions = 1, + .lines_per_tag = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + .type = INSTRUCTION_CACHE, + .level = 1, + .size = 64 * KiB, + .self_init = 1, + .line_size = 64, + .associativity = 2, + .sets = 512, + .partitions = 1, + .lines_per_tag = 1, + .no_invd_sharing = true, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 2, + .size = 512 * KiB, + .line_size = 64, + .lines_per_tag = 1, + .associativity = 16, + .sets = 512, + .partitions = 1, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + .type = UNIFIED_CACHE, + .level = 3, + .size = 16 * MiB, + .line_size = 64, + .associativity = 16, + .sets = 16384, + .partitions = 1, + .lines_per_tag = 1, + .self_init = true, + .inclusive = true, + .complex_indexing = true, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, }; /* @@ -8991,11 +8989,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) } env->cache_info_cpuid4 = legacy_intel_cache_info; - - env->cache_info_amd.l1d_cache = &legacy_l1d_cache_amd; - env->cache_info_amd.l1i_cache = &legacy_l1i_cache_amd; - env->cache_info_amd.l2_cache = &legacy_l2_cache_amd; - env->cache_info_amd.l3_cache = &legacy_l3_cache; + env->cache_info_amd = legacy_amd_cache_info; } #ifndef CONFIG_USER_ONLY From 2f5b76bce5c9aa71b35bbe5fb666343eed7478f1 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:38 +0800 Subject: [PATCH 2196/2760] i386/cpu: Select legacy cache model based on vendor in CPUID 0x2 As preparation for merging cache_info_cpuid4 and cache_info_amd in X86CPUState, set legacy cache model based on vendor in the CPUID 0x2 leaf. For AMD CPU, select legacy AMD cache model (in cache_info_amd) as the default cache model, otherwise, select legacy Intel cache model (in cache_info_cpuid4) as before. To ensure compatibility is not broken, add an enable_legacy_vendor_cache flag based on x-vendor-only-v2 to indicate cases where the legacy cache model should be used regardless of the vendor. For CPUID 0x2 leaf, enable_legacy_vendor_cache flag indicates to pick legacy Intel cache model, which is for compatibility with the behavior of PC machine v10.0 and older. The following explains how current vendor-based default legacy cache model ensures correctness without breaking compatibility. * For the PC machine v6.0 and older, vendor_cpuid_only=false, and vendor_cpuid_only_v2=false. - If the named CPU model has its own cache model, and doesn't use legacy cache model (legacy_cache=false), then cache_info_cpuid4 and cache_info_amd are same, so 0x2 leaf uses its own cache model regardless of the vendor. - For max/host/named CPU (without its own cache model), then the flag enable_legacy_vendor_cache is true, they will use legacy Intel cache model just like their previous behavior. * For the PC machine v10.0 and older (to v6.1), vendor_cpuid_only=true, and vendor_cpuid_only_v2=false. - If the named CPU model has its own cache model (legacy_cache=false), then cache_info_cpuid4 & cache_info_amd both equal to its own cache model, so it uses its own cache model in 0x2 leaf regardless of the vendor. Only AMD CPUs have all-0 leaf due to vendor_cpuid_only=true, and this is exactly the behavior of these old machines. - For max/host/named CPU (without its own cache model), then the flag enable_legacy_vendor_cache is true, they will use legacy Intel cache model. Similarly, only AMD CPUs have all-0 leaf, and this is exactly the behavior of these old machines. * For the PC machine v10.1 and newer, vendor_cpuid_only=true, and vendor_cpuid_only_v2=true. - If the named CPU model has its own cache model (legacy_cache=false), then cache_info_cpuid4 & cache_info_amd both equal to its own cache model, so it uses its own cache model in 0x2 leaf regardless of the vendor. And AMD CPUs have all-0 leaf. Nothing will change. - For max/host/named CPU (without its own cache model), then the flag enable_legacy_vendor_cache is false, the legacy cache model is selected based on vendor. For AMD CPU, it will use legacy AMD cache but still get all-0 leaf due to vendor_cpuid_only=true. For non-AMD (Intel/Zhaoxin) CPU, it will use legacy Intel cache as expected. Here, selecting the legacy cache model based on the vendor does not change the previous (before the change) behavior. Therefore, the above analysis proves that, with the help of the flag enable_legacy_vendor_cache, it is acceptable to select the default legacy cache model based on the vendor. For the CPUID 0x2 leaf, in X86CPUState, a unified cache_info is enough. It only needs to be initialized and configured with the corresponding legacy cache model based on the vendor. Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-14-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 47 +++++++++++++++++++++++++++++++++++++---------- target/i386/cpu.h | 1 + 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 92d21ce64c..ca85603077 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -250,23 +250,17 @@ static const CPUCaches legacy_intel_cpuid2_cache_info; /* Encode cache info for CPUID[2] */ static void encode_cache_cpuid2(X86CPU *cpu, + const CPUCaches *caches, uint32_t *eax, uint32_t *ebx, uint32_t *ecx, uint32_t *edx) { CPUX86State *env = &cpu->env; - const CPUCaches *caches; int l1d, l1i, l2, l3; bool unmatched = false; *eax = 1; /* Number of CPUID[EAX=2] calls required */ *ebx = *ecx = *edx = 0; - if (env->enable_legacy_cpuid2_cache) { - caches = &legacy_intel_cpuid2_cache_info; - } else { - caches = &env->cache_info_cpuid4; - } - l1d = cpuid2_cache_descriptor(caches->l1d_cache, &unmatched); l1i = cpuid2_cache_descriptor(caches->l1i_cache, &unmatched); l2 = cpuid2_cache_descriptor(caches->l2_cache, &unmatched); @@ -7472,8 +7466,37 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ebx |= threads_per_pkg << 16; } break; - case 2: - /* cache info: needed for Pentium Pro compatibility */ + case 2: { /* cache info: needed for Pentium Pro compatibility */ + const CPUCaches *caches; + + if (env->enable_legacy_cpuid2_cache) { + caches = &legacy_intel_cpuid2_cache_info; + } else if (env->enable_legacy_vendor_cache) { + caches = &legacy_intel_cache_info; + } else { + /* + * FIXME: Temporarily select cache info model here based on + * vendor, and merge these 2 cache info models later. + * + * This condition covers the following cases (with + * enable_legacy_vendor_cache=false): + * - When CPU model has its own cache model and doesn't use legacy + * cache model (legacy_model=off). Then cache_info_amd and + * cache_info_cpuid4 are the same. + * + * - For v10.1 and newer machines, when CPU model uses legacy cache + * model. Non-AMD CPUs use cache_info_cpuid4 like before and AMD + * CPU will use cache_info_amd. But this doesn't matter for AMD + * CPU, because this leaf encodes all-0 for AMD whatever its cache + * model is. + */ + if (IS_AMD_CPU(env)) { + caches = &env->cache_info_amd; + } else { + caches = &env->cache_info_cpuid4; + } + } + if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, 0, eax, ebx, ecx, edx); break; @@ -7481,8 +7504,9 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *eax = *ebx = *ecx = *edx = 0; break; } - encode_cache_cpuid2(cpu, eax, ebx, ecx, edx); + encode_cache_cpuid2(cpu, caches, eax, ebx, ecx, edx); break; + } case 4: /* cache info: needed for Core compatibility */ if (cpu->cache_info_passthrough) { @@ -8988,6 +9012,9 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) env->enable_legacy_cpuid2_cache = true; } + if (!cpu->vendor_cpuid_only_v2) { + env->enable_legacy_vendor_cache = true; + } env->cache_info_cpuid4 = legacy_intel_cache_info; env->cache_info_amd = legacy_amd_cache_info; } diff --git a/target/i386/cpu.h b/target/i386/cpu.h index d88481ba8e..20499a82a5 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2079,6 +2079,7 @@ typedef struct CPUArchState { */ CPUCaches cache_info_cpuid4, cache_info_amd; bool enable_legacy_cpuid2_cache; + bool enable_legacy_vendor_cache; /* MTRRs */ uint64_t mtrr_fixed[11]; From 69fe18ff9ced6e9cac3e4f3f3b94452603f2006e Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:39 +0800 Subject: [PATCH 2197/2760] i386/cpu: Select legacy cache model based on vendor in CPUID 0x4 As preparation for merging cache_info_cpuid4 and cache_info_amd in X86CPUState, set legacy cache model based on vendor in the CPUID 0x4 leaf. For AMD CPU, select legacy AMD cache model (in cache_info_amd) as the default cache model, otherwise, select legacy Intel cache model (in cache_info_cpuid4) as before. To ensure compatibility is not broken, add an enable_legacy_vendor_cache flag based on x-vendor-only-v2 to indicate cases where the legacy cache model should be used regardless of the vendor. For CPUID 0x4 leaf, enable_legacy_vendor_cache flag indicates to pick legacy Intel cache model, which is for compatibility with the behavior of PC machine v10.0 and older. The following explains how current vendor-based default legacy cache model ensures correctness without breaking compatibility. * For the PC machine v6.0 and older, vendor_cpuid_only=false, and vendor_cpuid_only_v2=false. - If the named CPU model has its own cache model, and doesn't use legacy cache model (legacy_cache=false), then cache_info_cpuid4 and cache_info_amd are same, so 0x4 leaf uses its own cache model regardless of the vendor. - For max/host/named CPU (without its own cache model), then the flag enable_legacy_vendor_cache is true, they will use legacy Intel cache model just like their previous behavior. * For the PC machine v10.0 and older (to v6.1), vendor_cpuid_only=true, and vendor_cpuid_only_v2=false. - If the named CPU model has its own cache model (legacy_cache=false), then cache_info_cpuid4 & cache_info_amd both equal to its own cache model, so it uses its own cache model in 0x4 leaf regardless of the vendor. Only AMD CPUs have all-0 leaf due to vendor_cpuid_only=true, and this is exactly the behavior of these old machines. - For max/host/named CPU (without its own cache model), then the flag enable_legacy_vendor_cache is true, they will use legacy Intel cache model. Similarly, only AMD CPUs have all-0 leaf, and this is exactly the behavior of these old machines. * For the PC machine v10.1 and newer, vendor_cpuid_only=true, and vendor_cpuid_only_v2=true. - If the named CPU model has its own cache model (legacy_cache=false), then cache_info_cpuid4 & cache_info_amd both equal to its own cache model, so it uses its own cache model in 0x4 leaf regardless of the vendor. And AMD CPUs have all-0 leaf. Nothing will change. - For max/host/named CPU (without its own cache model), then the flag enable_legacy_vendor_cache is false, the legacy cache model is selected based on vendor. For AMD CPU, it will use legacy AMD cache but still get all-0 leaf due to vendor_cpuid_only=true. For non-AMD (Intel/Zhaoxin) CPU, it will use legacy Intel cache as expected. Here, selecting the legacy cache model based on the vendor does not change the previous (before the change) behavior. Therefore, the above analysis proves that, with the help of the flag enable_legacy_vendor_cache, it is acceptable to select the default legacy cache model based on the vendor. For the CPUID 0x4 leaf, in X86CPUState, a unified cache_info is enough. It only needs to be initialized and configured with the corresponding legacy cache model based on the vendor. Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-15-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 43 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index ca85603077..565eaf0071 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7507,7 +7507,35 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, encode_cache_cpuid2(cpu, caches, eax, ebx, ecx, edx); break; } - case 4: + case 4: { + const CPUCaches *caches; + + if (env->enable_legacy_vendor_cache) { + caches = &legacy_intel_cache_info; + } else { + /* + * FIXME: Temporarily select cache info model here based on + * vendor, and merge these 2 cache info models later. + * + * This condition covers the following cases (with + * enable_legacy_vendor_cache=false): + * - When CPU model has its own cache model and doesn't use legacy + * cache model (legacy_model=off). Then cache_info_amd and + * cache_info_cpuid4 are the same. + * + * - For v10.1 and newer machines, when CPU model uses legacy cache + * model. Non-AMD CPUs use cache_info_cpuid4 like before and AMD + * CPU will use cache_info_amd. But this doesn't matter for AMD + * CPU, because this leaf encodes all-0 for AMD whatever its cache + * model is. + */ + if (IS_AMD_CPU(env)) { + caches = &env->cache_info_amd; + } else { + caches = &env->cache_info_cpuid4; + } + } + /* cache info: needed for Core compatibility */ if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, count, eax, ebx, ecx, edx); @@ -7535,30 +7563,26 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, switch (count) { case 0: /* L1 dcache info */ - encode_cache_cpuid4(env->cache_info_cpuid4.l1d_cache, - topo_info, + encode_cache_cpuid4(caches->l1d_cache, topo_info, eax, ebx, ecx, edx); if (!cpu->l1_cache_per_core) { *eax &= ~MAKE_64BIT_MASK(14, 12); } break; case 1: /* L1 icache info */ - encode_cache_cpuid4(env->cache_info_cpuid4.l1i_cache, - topo_info, + encode_cache_cpuid4(caches->l1i_cache, topo_info, eax, ebx, ecx, edx); if (!cpu->l1_cache_per_core) { *eax &= ~MAKE_64BIT_MASK(14, 12); } break; case 2: /* L2 cache info */ - encode_cache_cpuid4(env->cache_info_cpuid4.l2_cache, - topo_info, + encode_cache_cpuid4(caches->l2_cache, topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ if (cpu->enable_l3_cache) { - encode_cache_cpuid4(env->cache_info_cpuid4.l3_cache, - topo_info, + encode_cache_cpuid4(caches->l3_cache, topo_info, eax, ebx, ecx, edx); break; } @@ -7569,6 +7593,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } } break; + } case 5: /* MONITOR/MWAIT Leaf */ *eax = cpu->mwait.eax; /* Smallest monitor-line size in bytes */ From d4a8c66de8cb1f90ef9c23db8fb0ccf668c3518b Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:40 +0800 Subject: [PATCH 2198/2760] i386/cpu: Select legacy cache model based on vendor in CPUID 0x80000005 As preparation for merging cache_info_cpuid4 and cache_info_amd in X86CPUState, set legacy cache model based on vendor in the CPUID 0x80000005 leaf. For AMD CPU, select legacy AMD cache model (in cache_info_amd) as the default cache model like before, otherwise, select legacy Intel cache model (in cache_info_cpuid4). To ensure compatibility is not broken, add an enable_legacy_vendor_cache flag based on x-vendor-only-v2 to indicate cases where the legacy cache model should be used regardless of the vendor. For CPUID 0x80000005 leaf, enable_legacy_vendor_cache flag indicates to pick legacy AMD cache model, which is for compatibility with the behavior of PC machine v10.0 and older. The following explains how current vendor-based default legacy cache model ensures correctness without breaking compatibility. * For the PC machine v6.0 and older, vendor_cpuid_only=false, and vendor_cpuid_only_v2=false. - If the named CPU model has its own cache model, and doesn't use legacy cache model (legacy_cache=false), then cache_info_cpuid4 and cache_info_amd are same, so 0x80000005 leaf uses its own cache model regardless of the vendor. - For max/host/named CPU (without its own cache model), then the flag enable_legacy_vendor_cache is true, they will use legacy AMD cache model just like their previous behavior. * For the PC machine v10.0 and older (to v6.1), vendor_cpuid_only=true, and vendor_cpuid_only_v2=false. - No change, since this leaf doesn't aware vendor_cpuid_only. * For the PC machine v10.1 and newer, vendor_cpuid_only=true, and vendor_cpuid_only_v2=true. - If the named CPU model has its own cache model (legacy_cache=false), then cache_info_cpuid4 & cache_info_amd both equal to its own cache model, so it uses its own cache model in 0x80000005 leaf regardless of the vendor. Only Intel CPUs have all-0 leaf due to vendor_cpuid_only_2=true, and this is exactly the expected behavior. - For max/host/named CPU (without its own cache model), then the flag enable_legacy_vendor_cache is false, the legacy cache model is selected based on vendor. For AMD CPU, it will use legacy AMD cache as expected. For Intel CPU, it will use legacy Intel cache but still get all-0 leaf due to vendor_cpuid_only_2=true as expected. (Note) And for Zhaoxin CPU, it will use legacy Intel cache model instead of AMD's. This is the difference brought by this change! But it's correct since then Zhaoxin could have the consistent cache info in CPUID 0x2, 0x4 and 0x80000005 leaves. Here, except Zhaoxin, selecting the legacy cache model based on the vendor does not change the previous (before the change) behavior. And the change for Zhaoxin is also a good improvement. Therefore, the above analysis proves that, with the help of the flag enable_legacy_vendor_cache, it is acceptable to select the default legacy cache model based on the vendor. For the CPUID 0x80000005 leaf, in X86CPUState, a unified cache_info is enough. It only needs to be initialized and configured with the corresponding legacy cache model based on the vendor. Cc: EwanHai Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-16-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 35 ++++++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 565eaf0071..e98ffb11c3 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7935,8 +7935,36 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = env->cpuid_model[(index - 0x80000002) * 4 + 2]; *edx = env->cpuid_model[(index - 0x80000002) * 4 + 3]; break; - case 0x80000005: + case 0x80000005: { /* cache info (L1 cache/TLB Associativity Field) */ + const CPUCaches *caches; + + if (env->enable_legacy_vendor_cache) { + caches = &legacy_amd_cache_info; + } else { + /* + * FIXME: Temporarily select cache info model here based on + * vendor, and merge these 2 cache info models later. + * + * This condition covers the following cases (with + * enable_legacy_vendor_cache=false): + * - When CPU model has its own cache model and doesn't uses legacy + * cache model (legacy_model=off). Then cache_info_amd and + * cache_info_cpuid4 are the same. + * + * - For v10.1 and newer machines, when CPU model uses legacy cache + * model. AMD CPUs use cache_info_amd like before and non-AMD + * CPU will use cache_info_cpuid4. But this doesn't matter, + * because for Intel CPU, it will get all-0 leaf, and Zhaoxin CPU + * will get correct cache info. Both are expected. + */ + if (IS_AMD_CPU(env)) { + caches = &env->cache_info_amd; + } else { + caches = &env->cache_info_cpuid4; + } + } + if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, 0, eax, ebx, ecx, edx); break; @@ -7951,9 +7979,10 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (L1_ITLB_2M_ASSOC << 8) | (L1_ITLB_2M_ENTRIES); *ebx = (L1_DTLB_4K_ASSOC << 24) | (L1_DTLB_4K_ENTRIES << 16) | (L1_ITLB_4K_ASSOC << 8) | (L1_ITLB_4K_ENTRIES); - *ecx = encode_cache_cpuid80000005(env->cache_info_amd.l1d_cache); - *edx = encode_cache_cpuid80000005(env->cache_info_amd.l1i_cache); + *ecx = encode_cache_cpuid80000005(caches->l1d_cache); + *edx = encode_cache_cpuid80000005(caches->l1i_cache); break; + } case 0x80000006: /* cache info (L2 cache/TLB/L3 cache) */ if (cpu->cache_info_passthrough) { From 00fa96c96a864e133c22a5f3793b468bb8a121a5 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:41 +0800 Subject: [PATCH 2199/2760] i386/cpu: Select legacy cache model based on vendor in CPUID 0x80000006 As preparation for merging cache_info_cpuid4 and cache_info_amd in X86CPUState, set legacy cache model based on vendor in the CPUID 0x80000006 leaf. For AMD CPU, select legacy AMD cache model (in cache_info_amd) as the default cache model like before, otherwise, select legacy Intel cache model (in cache_info_cpuid4). To ensure compatibility is not broken, add an enable_legacy_vendor_cache flag based on x-vendor-only-v2 to indicate cases where the legacy cache model should be used regardless of the vendor. For CPUID 0x80000006 leaf, enable_legacy_vendor_cache flag indicates to pick legacy Intel cache model, which is for compatibility with the behavior of PC machine v10.0 and older. The following explains how current vendor-based default legacy cache model ensures correctness without breaking compatibility. * For the PC machine v6.0 and older, vendor_cpuid_only=false, and vendor_cpuid_only_v2=false. - If the named CPU model has its own cache model, and doesn't use legacy cache model (legacy_cache=false), then cache_info_cpuid4 and cache_info_amd are same, so 0x80000006 leaf uses its own cache model regardless of the vendor. - For max/host/named CPU (without its own cache model), then the flag enable_legacy_vendor_cache is true, they will use legacy AMD cache model just like their previous behavior. * For the PC machine v10.0 and older (to v6.1), vendor_cpuid_only=true, and vendor_cpuid_only_v2=false. - No change, since this leaf doesn't aware vendor_cpuid_only. * For the PC machine v10.1 and newer, vendor_cpuid_only=true, and vendor_cpuid_only_v2=true. - If the named CPU model has its own cache model (legacy_cache=false), then cache_info_cpuid4 & cache_info_amd both equal to its own cache model, so it uses its own cache model in 0x80000006 leaf regardless of the vendor. Intel and Zhaoxin CPUs have their special encoding based on SDM, which is the expected behavior and no different from before. - For max/host/named CPU (without its own cache model), then the flag enable_legacy_vendor_cache is false, the legacy cache model is selected based on vendor. For AMD CPU, it will use legacy AMD cache as before. For non-AMD (Intel/Zhaoxin) CPU, it will use legacy Intel cache and be encoded based on SDM as expected. Here, selecting the legacy cache model based on the vendor does not change the previous (before the change) behavior. Therefore, the above analysis proves that, with the help of the flag enable_legacy_vendor_cache, it is acceptable to select the default legacy cache model based on the vendor. For the CPUID 0x80000006 leaf, in X86CPUState, a unified cache_info is enough. It only needs to be initialized and configured with the corresponding legacy cache model based on the vendor. Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-17-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index e98ffb11c3..b557fd01c0 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7983,8 +7983,33 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = encode_cache_cpuid80000005(caches->l1i_cache); break; } - case 0x80000006: - /* cache info (L2 cache/TLB/L3 cache) */ + case 0x80000006: { /* cache info (L2 cache/TLB/L3 cache) */ + const CPUCaches *caches; + + if (env->enable_legacy_vendor_cache) { + caches = &legacy_amd_cache_info; + } else { + /* + * FIXME: Temporarily select cache info model here based on + * vendor, and merge these 2 cache info models later. + * + * This condition covers the following cases (with + * enable_legacy_vendor_cache=false): + * - When CPU model has its own cache model and doesn't uses legacy + * cache model (legacy_model=off). Then cache_info_amd and + * cache_info_cpuid4 are the same. + * + * - For v10.1 and newer machines, when CPU model uses legacy cache + * model. AMD CPUs use cache_info_amd like before and non-AMD + * CPU (Intel & Zhaoxin) will use cache_info_cpuid4 as expected. + */ + if (IS_AMD_CPU(env)) { + caches = &env->cache_info_amd; + } else { + caches = &env->cache_info_cpuid4; + } + } + if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, 0, eax, ebx, ecx, edx); break; @@ -7993,7 +8018,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (cpu->vendor_cpuid_only_v2 && (IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { *eax = *ebx = 0; - encode_cache_cpuid80000006(env->cache_info_cpuid4.l2_cache, + encode_cache_cpuid80000006(caches->l2_cache, NULL, ecx, edx); break; } @@ -8007,11 +8032,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, (X86_ENC_ASSOC(L2_ITLB_4K_ASSOC) << 12) | (L2_ITLB_4K_ENTRIES); - encode_cache_cpuid80000006(env->cache_info_amd.l2_cache, + encode_cache_cpuid80000006(caches->l2_cache, cpu->enable_l3_cache ? - env->cache_info_amd.l3_cache : NULL, + caches->l3_cache : NULL, ecx, edx); break; + } case 0x80000007: *eax = 0; *ebx = env->features[FEAT_8000_0007_EBX]; From 25acae4c6e7635ff0f0d36945548de4c0dc70608 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:42 +0800 Subject: [PATCH 2200/2760] i386/cpu: Select legacy cache model based on vendor in CPUID 0x8000001D As preparation for merging cache_info_cpuid4 and cache_info_amd in X86CPUState, set legacy cache model based on vendor in the CPUID 0x8000001D leaf. For AMD CPU, select legacy AMD cache model (in cache_info_amd) as the default cache model like before, otherwise, select legacy Intel cache model (in cache_info_cpuid4). In fact, for Intel (and Zhaoxin) CPU, this change is safe because the extended CPUID level supported by Intel is up to 0x80000008. So Intel Guest doesn't have this 0x8000001D leaf. Although someone could bump "xlevel" up to 0x8000001D for Intel Guest, it's meaningless and this is undefined behavior. This leaf should be considered reserved, but the SDM does not explicitly state this. So, there's no need to specifically use vendor_cpuid_only_v2 to fix anything, as it doesn't even qualify as a fix since nothing is currently broken. Therefore, it is acceptable to select the default legacy cache model based on the vendor. For the CPUID 0x8000001D leaf, in X86CPUState, a unified cache_info is enough. It only needs to be initialized and configured with the corresponding legacy cache model based on the vendor. Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-18-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index b557fd01c0..5b969743bc 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8080,7 +8080,22 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = 0; } break; - case 0x8000001D: + case 0x8000001D: { + const CPUCaches *caches; + + /* + * FIXME: Temporarily select cache info model here based on + * vendor, and merge these 2 cache info models later. + * + * Intel doesn't support this leaf so that Intel Guests don't + * have this leaf. This change is harmless to Intel CPUs. + */ + if (IS_AMD_CPU(env)) { + caches = &env->cache_info_amd; + } else { + caches = &env->cache_info_cpuid4; + } + *eax = 0; if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, count, eax, ebx, ecx, edx); @@ -8088,19 +8103,19 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } switch (count) { case 0: /* L1 dcache info */ - encode_cache_cpuid8000001d(env->cache_info_amd.l1d_cache, + encode_cache_cpuid8000001d(caches->l1d_cache, topo_info, eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ - encode_cache_cpuid8000001d(env->cache_info_amd.l1i_cache, + encode_cache_cpuid8000001d(caches->l1i_cache, topo_info, eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ - encode_cache_cpuid8000001d(env->cache_info_amd.l2_cache, + encode_cache_cpuid8000001d(caches->l2_cache, topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ - encode_cache_cpuid8000001d(env->cache_info_amd.l3_cache, + encode_cache_cpuid8000001d(caches->l3_cache, topo_info, eax, ebx, ecx, edx); break; default: /* end of info */ @@ -8111,6 +8126,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx &= CACHE_NO_INVD_SHARING | CACHE_INCLUSIVE; } break; + } case 0x8000001E: if (cpu->core_id <= 255) { encode_topo_cpuid8000001e(cpu, topo_info, eax, ebx, ecx, edx); From 2141898bb9b7f3d426871eaa6e98b4c77a1f81e8 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:21:43 +0800 Subject: [PATCH 2201/2760] i386/cpu: Use a unified cache_info in X86CPUState At present, all cases using the cache model (CPUID 0x2, 0x4, 0x80000005, 0x80000006 and 0x8000001D leaves) have been verified to be able to select either cache_info_intel or cache_info_amd based on the vendor. Therefore, further merge cache_info_intel and cache_info_amd into a unified cache_info in X86CPUState, and during its initialization, set different legacy cache models based on the vendor. Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711102143.1622339-19-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 150 ++++++++-------------------------------------- target/i386/cpu.h | 5 +- 2 files changed, 27 insertions(+), 128 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 5b969743bc..ca6e412024 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7474,27 +7474,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } else if (env->enable_legacy_vendor_cache) { caches = &legacy_intel_cache_info; } else { - /* - * FIXME: Temporarily select cache info model here based on - * vendor, and merge these 2 cache info models later. - * - * This condition covers the following cases (with - * enable_legacy_vendor_cache=false): - * - When CPU model has its own cache model and doesn't use legacy - * cache model (legacy_model=off). Then cache_info_amd and - * cache_info_cpuid4 are the same. - * - * - For v10.1 and newer machines, when CPU model uses legacy cache - * model. Non-AMD CPUs use cache_info_cpuid4 like before and AMD - * CPU will use cache_info_amd. But this doesn't matter for AMD - * CPU, because this leaf encodes all-0 for AMD whatever its cache - * model is. - */ - if (IS_AMD_CPU(env)) { - caches = &env->cache_info_amd; - } else { - caches = &env->cache_info_cpuid4; - } + caches = &env->cache_info; } if (cpu->cache_info_passthrough) { @@ -7513,27 +7493,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (env->enable_legacy_vendor_cache) { caches = &legacy_intel_cache_info; } else { - /* - * FIXME: Temporarily select cache info model here based on - * vendor, and merge these 2 cache info models later. - * - * This condition covers the following cases (with - * enable_legacy_vendor_cache=false): - * - When CPU model has its own cache model and doesn't use legacy - * cache model (legacy_model=off). Then cache_info_amd and - * cache_info_cpuid4 are the same. - * - * - For v10.1 and newer machines, when CPU model uses legacy cache - * model. Non-AMD CPUs use cache_info_cpuid4 like before and AMD - * CPU will use cache_info_amd. But this doesn't matter for AMD - * CPU, because this leaf encodes all-0 for AMD whatever its cache - * model is. - */ - if (IS_AMD_CPU(env)) { - caches = &env->cache_info_amd; - } else { - caches = &env->cache_info_cpuid4; - } + caches = &env->cache_info; } /* cache info: needed for Core compatibility */ @@ -7942,27 +7902,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (env->enable_legacy_vendor_cache) { caches = &legacy_amd_cache_info; } else { - /* - * FIXME: Temporarily select cache info model here based on - * vendor, and merge these 2 cache info models later. - * - * This condition covers the following cases (with - * enable_legacy_vendor_cache=false): - * - When CPU model has its own cache model and doesn't uses legacy - * cache model (legacy_model=off). Then cache_info_amd and - * cache_info_cpuid4 are the same. - * - * - For v10.1 and newer machines, when CPU model uses legacy cache - * model. AMD CPUs use cache_info_amd like before and non-AMD - * CPU will use cache_info_cpuid4. But this doesn't matter, - * because for Intel CPU, it will get all-0 leaf, and Zhaoxin CPU - * will get correct cache info. Both are expected. - */ - if (IS_AMD_CPU(env)) { - caches = &env->cache_info_amd; - } else { - caches = &env->cache_info_cpuid4; - } + caches = &env->cache_info; } if (cpu->cache_info_passthrough) { @@ -7989,25 +7929,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, if (env->enable_legacy_vendor_cache) { caches = &legacy_amd_cache_info; } else { - /* - * FIXME: Temporarily select cache info model here based on - * vendor, and merge these 2 cache info models later. - * - * This condition covers the following cases (with - * enable_legacy_vendor_cache=false): - * - When CPU model has its own cache model and doesn't uses legacy - * cache model (legacy_model=off). Then cache_info_amd and - * cache_info_cpuid4 are the same. - * - * - For v10.1 and newer machines, when CPU model uses legacy cache - * model. AMD CPUs use cache_info_amd like before and non-AMD - * CPU (Intel & Zhaoxin) will use cache_info_cpuid4 as expected. - */ - if (IS_AMD_CPU(env)) { - caches = &env->cache_info_amd; - } else { - caches = &env->cache_info_cpuid4; - } + caches = &env->cache_info; } if (cpu->cache_info_passthrough) { @@ -8080,22 +8002,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = 0; } break; - case 0x8000001D: { - const CPUCaches *caches; - - /* - * FIXME: Temporarily select cache info model here based on - * vendor, and merge these 2 cache info models later. - * - * Intel doesn't support this leaf so that Intel Guests don't - * have this leaf. This change is harmless to Intel CPUs. - */ - if (IS_AMD_CPU(env)) { - caches = &env->cache_info_amd; - } else { - caches = &env->cache_info_cpuid4; - } - + case 0x8000001D: *eax = 0; if (cpu->cache_info_passthrough) { x86_cpu_get_cache_cpuid(index, count, eax, ebx, ecx, edx); @@ -8103,19 +8010,19 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } switch (count) { case 0: /* L1 dcache info */ - encode_cache_cpuid8000001d(caches->l1d_cache, + encode_cache_cpuid8000001d(env->cache_info.l1d_cache, topo_info, eax, ebx, ecx, edx); break; case 1: /* L1 icache info */ - encode_cache_cpuid8000001d(caches->l1i_cache, + encode_cache_cpuid8000001d(env->cache_info.l1i_cache, topo_info, eax, ebx, ecx, edx); break; case 2: /* L2 cache info */ - encode_cache_cpuid8000001d(caches->l2_cache, + encode_cache_cpuid8000001d(env->cache_info.l2_cache, topo_info, eax, ebx, ecx, edx); break; case 3: /* L3 cache info */ - encode_cache_cpuid8000001d(caches->l3_cache, + encode_cache_cpuid8000001d(env->cache_info.l3_cache, topo_info, eax, ebx, ecx, edx); break; default: /* end of info */ @@ -8126,7 +8033,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx &= CACHE_NO_INVD_SHARING | CACHE_INCLUSIVE; } break; - } case 0x8000001E: if (cpu->core_id <= 255) { encode_topo_cpuid8000001e(cpu, topo_info, eax, ebx, ecx, edx); @@ -8825,46 +8731,34 @@ static bool x86_cpu_update_smp_cache_topo(MachineState *ms, X86CPU *cpu, level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D); if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { - env->cache_info_cpuid4.l1d_cache->share_level = level; - env->cache_info_amd.l1d_cache->share_level = level; + env->cache_info.l1d_cache->share_level = level; } else { machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D, - env->cache_info_cpuid4.l1d_cache->share_level); - machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1D, - env->cache_info_amd.l1d_cache->share_level); + env->cache_info.l1d_cache->share_level); } level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I); if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { - env->cache_info_cpuid4.l1i_cache->share_level = level; - env->cache_info_amd.l1i_cache->share_level = level; + env->cache_info.l1i_cache->share_level = level; } else { machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I, - env->cache_info_cpuid4.l1i_cache->share_level); - machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L1I, - env->cache_info_amd.l1i_cache->share_level); + env->cache_info.l1i_cache->share_level); } level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2); if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { - env->cache_info_cpuid4.l2_cache->share_level = level; - env->cache_info_amd.l2_cache->share_level = level; + env->cache_info.l2_cache->share_level = level; } else { machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2, - env->cache_info_cpuid4.l2_cache->share_level); - machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L2, - env->cache_info_amd.l2_cache->share_level); + env->cache_info.l2_cache->share_level); } level = machine_get_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3); if (level != CPU_TOPOLOGY_LEVEL_DEFAULT) { - env->cache_info_cpuid4.l3_cache->share_level = level; - env->cache_info_amd.l3_cache->share_level = level; + env->cache_info.l3_cache->share_level = level; } else { machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3, - env->cache_info_cpuid4.l3_cache->share_level); - machine_set_cache_topo_level(ms, CACHE_LEVEL_AND_TYPE_L3, - env->cache_info_amd.l3_cache->share_level); + env->cache_info.l3_cache->share_level); } if (!machine_check_smp_cache(ms, errp)) { @@ -9101,7 +8995,7 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) "CPU model '%s' doesn't support legacy-cache=off", name); return; } - env->cache_info_cpuid4 = env->cache_info_amd = *cache_info; + env->cache_info = *cache_info; } else { /* Build legacy cache information */ if (!cpu->consistent_cache) { @@ -9111,8 +9005,12 @@ static void x86_cpu_realizefn(DeviceState *dev, Error **errp) if (!cpu->vendor_cpuid_only_v2) { env->enable_legacy_vendor_cache = true; } - env->cache_info_cpuid4 = legacy_intel_cache_info; - env->cache_info_amd = legacy_amd_cache_info; + + if (IS_AMD_CPU(env)) { + env->cache_info = legacy_amd_cache_info; + } else { + env->cache_info = legacy_intel_cache_info; + } } #ifndef CONFIG_USER_ONLY diff --git a/target/i386/cpu.h b/target/i386/cpu.h index 20499a82a5..f977fc49a7 100644 --- a/target/i386/cpu.h +++ b/target/i386/cpu.h @@ -2073,11 +2073,12 @@ typedef struct CPUArchState { /* Features that were explicitly enabled/disabled */ FeatureWordArray user_features; uint32_t cpuid_model[12]; - /* Cache information for CPUID. When legacy-cache=on, the cache data + /* + * Cache information for CPUID. When legacy-cache=on, the cache data * on each CPUID leaf will be different, because we keep compatibility * with old QEMU versions. */ - CPUCaches cache_info_cpuid4, cache_info_amd; + CPUCaches cache_info; bool enable_legacy_cpuid2_cache; bool enable_legacy_vendor_cache; From 1f0a9ce6c0c934507eed076b1b9a2774b7c81de4 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:45:55 +0800 Subject: [PATCH 2202/2760] i386/cpu: Introduce cache model for SierraForest MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the cache model to SierraForest (v3) to better emulate its environment. The cache model is based on SierraForest-SP (Scalable Performance): --- cache 0 --- cache type = data cache (1) cache level = 0x1 (1) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x0 (0) maximum IDs for cores in pkg = 0x3f (63) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0x8 (8) number of sets = 0x40 (64) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = false number of sets (s) = 64 (size synth) = 32768 (32 KB) --- cache 1 --- cache type = instruction cache (2) cache level = 0x1 (1) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x0 (0) maximum IDs for cores in pkg = 0x3f (63) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0x8 (8) number of sets = 0x80 (128) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = false number of sets (s) = 128 (size synth) = 65536 (64 KB) --- cache 2 --- cache type = unified cache (3) cache level = 0x2 (2) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x7 (7) maximum IDs for cores in pkg = 0x3f (63) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0x10 (16) number of sets = 0x1000 (4096) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = false number of sets (s) = 4096 (size synth) = 4194304 (4 MB) --- cache 3 --- cache type = unified cache (3) cache level = 0x3 (3) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x1ff (511) maximum IDs for cores in pkg = 0x3f (63) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0xc (12) number of sets = 0x24000 (147456) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = true number of sets (s) = 147456 (size synth) = 113246208 (108 MB) --- cache 4 --- cache type = no more caches (0) Suggested-by: Tejus GK Suggested-by: Jason Zeng Suggested-by: "Daniel P . Berrangé" Reviewed-by: Dapeng Mi Reviewed-by: Tao Su Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711104603.1634832-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index ca6e412024..3c28e9588a 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -2880,6 +2880,97 @@ static const CPUCaches epyc_turin_cache_info = { .no_invd_sharing = true, .complex_indexing = false, .share_level = CPU_TOPOLOGY_LEVEL_DIE, + } +}; + +static const CPUCaches xeon_srf_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x0.EAX */ + .type = DATA_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x0.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 8, + + /* CPUID 0x4.0x0.ECX */ + .sets = 64, + + /* CPUID 0x4.0x0.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 32 * KiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x1.EAX */ + .type = INSTRUCTION_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x1.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 8, + + /* CPUID 0x4.0x1.ECX */ + .sets = 128, + + /* CPUID 0x4.0x1.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 64 * KiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x2.EAX */ + .type = UNIFIED_CACHE, + .level = 2, + .self_init = true, + + /* CPUID 0x4.0x2.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x2.ECX */ + .sets = 4096, + + /* CPUID 0x4.0x2.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 4 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_MODULE, + }, + .l3_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x3.EAX */ + .type = UNIFIED_CACHE, + .level = 3, + .self_init = true, + + /* CPUID 0x4.0x3.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 12, + + /* CPUID 0x4.0x3.ECX */ + .sets = 147456, + + /* CPUID 0x4.0x3.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = true, + + .size = 108 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_SOCKET, }, }; @@ -5005,6 +5096,11 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 3, + .note = "with srf-sp cache model", + .cache_info = &xeon_srf_cache_info, + }, { /* end of list */ }, }, }, From 23fb57838ddfa9271f8ebf8b7a74ed7ba3359cd3 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:45:56 +0800 Subject: [PATCH 2203/2760] i386/cpu: Introduce cache model for GraniteRapids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the cache model to GraniteRapids (v3) to better emulate its environment. The cache model is based on GraniteRapids-SP (Scalable Performance): --- cache 0 --- cache type = data cache (1) cache level = 0x1 (1) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x1 (1) maximum IDs for cores in pkg = 0x3f (63) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0xc (12) number of sets = 0x40 (64) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = false number of sets (s) = 64 (size synth) = 49152 (48 KB) --- cache 1 --- cache type = instruction cache (2) cache level = 0x1 (1) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x1 (1) maximum IDs for cores in pkg = 0x3f (63) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0x10 (16) number of sets = 0x40 (64) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = false number of sets (s) = 64 (size synth) = 65536 (64 KB) --- cache 2 --- cache type = unified cache (3) cache level = 0x2 (2) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x1 (1) maximum IDs for cores in pkg = 0x3f (63) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0x10 (16) number of sets = 0x800 (2048) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = false number of sets (s) = 2048 (size synth) = 2097152 (2 MB) --- cache 3 --- cache type = unified cache (3) cache level = 0x3 (3) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0xff (255) maximum IDs for cores in pkg = 0x3f (63) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0x10 (16) number of sets = 0x48000 (294912) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = true number of sets (s) = 294912 (size synth) = 301989888 (288 MB) --- cache 4 --- cache type = no more caches (0) Suggested-by: Tejus GK Suggested-by: Jason Zeng Suggested-by: "Daniel P . Berrangé" Reviewed-by: Dapeng Mi Reviewed-by: Tao Su Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711104603.1634832-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 3c28e9588a..d1fc74eb0e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -2883,6 +2883,97 @@ static const CPUCaches epyc_turin_cache_info = { } }; +static const CPUCaches xeon_gnr_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x0.EAX */ + .type = DATA_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x0.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 12, + + /* CPUID 0x4.0x0.ECX */ + .sets = 64, + + /* CPUID 0x4.0x0.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 48 * KiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x1.EAX */ + .type = INSTRUCTION_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x1.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x1.ECX */ + .sets = 64, + + /* CPUID 0x4.0x1.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 64 * KiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x2.EAX */ + .type = UNIFIED_CACHE, + .level = 2, + .self_init = true, + + /* CPUID 0x4.0x2.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x2.ECX */ + .sets = 2048, + + /* CPUID 0x4.0x2.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 2 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x3.EAX */ + .type = UNIFIED_CACHE, + .level = 3, + .self_init = true, + + /* CPUID 0x4.0x3.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x3.ECX */ + .sets = 294912, + + /* CPUID 0x4.0x3.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = true, + + .size = 288 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_SOCKET, + }, +}; + static const CPUCaches xeon_srf_cache_info = { .l1d_cache = &(CPUCacheInfo) { /* CPUID 0x4.0x0.EAX */ @@ -4951,6 +5042,11 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 3, + .note = "with gnr-sp cache model", + .cache_info = &xeon_gnr_cache_info, + }, { /* end of list */ }, }, }, From 8d69fc2158edc83517622bd862beb29ffc6bbf02 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:45:57 +0800 Subject: [PATCH 2204/2760] i386/cpu: Introduce cache model for SapphireRapids MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add the cache model to SapphireRapids (v4) to better emulate its environment. The cache model is based on SapphireRapids-SP (Scalable Performance): --- cache 0 --- cache type = data cache (1) cache level = 0x1 (1) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x1 (1) maximum IDs for cores in pkg = 0x3f (63) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0xc (12) number of sets = 0x40 (64) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = false number of sets (s) = 64 (size synth) = 49152 (48 KB) --- cache 1 --- cache type = instruction cache (2) cache level = 0x1 (1) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x1 (1) maximum IDs for cores in pkg = 0x3f (63) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0x8 (8) number of sets = 0x40 (64) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = false number of sets (s) = 64 (size synth) = 32768 (32 KB) --- cache 2 --- cache type = unified cache (3) cache level = 0x2 (2) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x1 (1) maximum IDs for cores in pkg = 0x3f (63) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0x10 (16) number of sets = 0x800 (2048) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = false number of sets (s) = 2048 (size synth) = 2097152 (2 MB) --- cache 3 --- cache type = unified cache (3) cache level = 0x3 (3) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x7f (127) maximum IDs for cores in pkg = 0x3f (63) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0xf (15) number of sets = 0x10000 (65536) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = true number of sets (s) = 65536 (size synth) = 62914560 (60 MB) --- cache 4 --- cache type = no more caches (0) Suggested-by: Tejus GK Suggested-by: Jason Zeng Suggested-by: "Daniel P . Berrangé" Reviewed-by: Dapeng Mi Reviewed-by: Tao Su Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711104603.1634832-4-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 96 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 96 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index d1fc74eb0e..b3b29f6966 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -2883,6 +2883,97 @@ static const CPUCaches epyc_turin_cache_info = { } }; +static const CPUCaches xeon_spr_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x0.EAX */ + .type = DATA_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x0.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 12, + + /* CPUID 0x4.0x0.ECX */ + .sets = 64, + + /* CPUID 0x4.0x0.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 48 * KiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x1.EAX */ + .type = INSTRUCTION_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x1.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 8, + + /* CPUID 0x4.0x1.ECX */ + .sets = 64, + + /* CPUID 0x4.0x1.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 32 * KiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x2.EAX */ + .type = UNIFIED_CACHE, + .level = 2, + .self_init = true, + + /* CPUID 0x4.0x2.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x2.ECX */ + .sets = 2048, + + /* CPUID 0x4.0x2.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + .size = 2 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x3.EAX */ + .type = UNIFIED_CACHE, + .level = 3, + .self_init = true, + + /* CPUID 0x4.0x3.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 15, + + /* CPUID 0x4.0x3.ECX */ + .sets = 65536, + + /* CPUID 0x4.0x3.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = true, + + .size = 60 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_SOCKET, + }, +}; + static const CPUCaches xeon_gnr_cache_info = { .l1d_cache = &(CPUCacheInfo) { /* CPUID 0x4.0x0.EAX */ @@ -4889,6 +4980,11 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 4, + .note = "with spr-sp cache model", + .cache_info = &xeon_spr_cache_info, + }, { /* end of list */ } } }, From b1a3a090b21a505d7a9da097c2046824f9e38f84 Mon Sep 17 00:00:00 2001 From: Ewan Hai Date: Fri, 11 Jul 2025 18:45:58 +0800 Subject: [PATCH 2205/2760] i386/cpu: Introduce cache model for YongFeng Add the cache model to YongFeng (v3) to better emulate its environment. Note, although YongFeng v2 was added after v10.0, it was also back ported to v10.0.2. Therefore, the new version (v3) is needed to avoid conflict. The cache model is as follows: --- cache 0 --- cache type = data cache (1) cache level = 0x1 (1) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x0 (0) maximum IDs for cores in pkg = 0x0 (0) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0x8 (8) number of sets = 0x40 (64) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = false number of sets (s) = 64 (size synth) = 32768 (32 KB) --- cache 1 --- cache type = instruction cache (2) cache level = 0x1 (1) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x0 (0) maximum IDs for cores in pkg = 0x0 (0) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0x10 (16) number of sets = 0x40 (64) WBINVD/INVD acts on lower caches = false inclusive to lower caches = false complex cache indexing = false number of sets (s) = 64 (size synth) = 65536 (64 KB) --- cache 2 --- cache type = unified cache (3) cache level = 0x2 (2) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x0 (0) maximum IDs for cores in pkg = 0x0 (0) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0x8 (8) number of sets = 0x200 (512) WBINVD/INVD acts on lower caches = false inclusive to lower caches = true complex cache indexing = false number of sets (s) = 512 (size synth) = 262144 (256 KB) --- cache 3 --- cache type = unified cache (3) cache level = 0x3 (3) self-initializing cache level = true fully associative cache = false maximum IDs for CPUs sharing cache = 0x0 (0) maximum IDs for cores in pkg = 0x0 (0) system coherency line size = 0x40 (64) physical line partitions = 0x1 (1) ways of associativity = 0x10 (16) number of sets = 0x2000 (8192) WBINVD/INVD acts on lower caches = true inclusive to lower caches = true complex cache indexing = false number of sets (s) = 8192 (size synth) = 8388608 (8 MB) --- cache 4 --- cache type = no more caches (0) Tested-by: Yi Lai Signed-off-by: Ewan Hai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711104603.1634832-5-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index b3b29f6966..40f3b5eac8 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -3156,6 +3156,105 @@ static const CPUCaches xeon_srf_cache_info = { }, }; +static const CPUCaches yongfeng_cache_info = { + .l1d_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x0.EAX */ + .type = DATA_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x0.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 8, + + /* CPUID 0x4.0x0.ECX */ + .sets = 64, + + /* CPUID 0x4.0x0.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + /* CPUID 0x80000005.ECX */ + .lines_per_tag = 1, + .size = 32 * KiB, + + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l1i_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x1.EAX */ + .type = INSTRUCTION_CACHE, + .level = 1, + .self_init = true, + + /* CPUID 0x4.0x1.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x1.ECX */ + .sets = 64, + + /* CPUID 0x4.0x1.EDX */ + .no_invd_sharing = false, + .inclusive = false, + .complex_indexing = false, + + /* CPUID 0x80000005.EDX */ + .lines_per_tag = 1, + .size = 64 * KiB, + + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l2_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x2.EAX */ + .type = UNIFIED_CACHE, + .level = 2, + .self_init = true, + + /* CPUID 0x4.0x2.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 8, + + /* CPUID 0x4.0x2.ECX */ + .sets = 512, + + /* CPUID 0x4.0x2.EDX */ + .no_invd_sharing = false, + .inclusive = true, + .complex_indexing = false, + + /* CPUID 0x80000006.ECX */ + .size = 256 * KiB, + + .share_level = CPU_TOPOLOGY_LEVEL_CORE, + }, + .l3_cache = &(CPUCacheInfo) { + /* CPUID 0x4.0x3.EAX */ + .type = UNIFIED_CACHE, + .level = 3, + .self_init = true, + + /* CPUID 0x4.0x3.EBX */ + .line_size = 64, + .partitions = 1, + .associativity = 16, + + /* CPUID 0x4.0x3.ECX */ + .sets = 8192, + + /* CPUID 0x4.0x3.EDX */ + .no_invd_sharing = true, + .inclusive = true, + .complex_indexing = false, + + .size = 8 * MiB, + .share_level = CPU_TOPOLOGY_LEVEL_DIE, + }, +}; + /* The following VMX features are not supported by KVM and are left out in the * CPU definitions: * @@ -6435,6 +6534,11 @@ static const X86CPUDefinition builtin_x86_defs[] = { { /* end of list */ } } }, + { + .version = 3, + .note = "with the cache model", + .cache_info = &yongfeng_cache_info, + }, { /* end of list */ } } }, From bf4032561447a518e9cc4af308ef1a15023bc4c6 Mon Sep 17 00:00:00 2001 From: Manish Mishra Date: Fri, 11 Jul 2025 18:45:59 +0800 Subject: [PATCH 2206/2760] i386/cpu: Add a "x-force-cpuid-0x1f" property Add a "x-force-cpuid-0x1f" property so that CPU models can enable it and have 0x1f CPUID leaf natually as the Host CPU. The advantage is that when the CPU model's cache model is already consistent with the Host CPU, for example, SRF defaults to l2 per module & l3 per package, 0x1f can better help users identify the topology in the VM. Adding 0x1f for specific CPU models should not cause any trouble in principle. This property is only enabled for CPU models that already have 0x1f leaf on the Host, so software that originally runs normally on the Host won't encounter issues in the Guest with corresponding CPU model. Conversely, some software that relies on checking 0x1f might have problems in the Guest due to the lack of 0x1f [*]. In summary, adding 0x1f is also intended to further emulate the Host CPU environment. [*]: https://lore.kernel.org/qemu-devel/PH0PR02MB738410511BF51B12DB09BE6CF6AC2@PH0PR02MB7384.namprd02.prod.outlook.com/ Signed-off-by: Manish Mishra Co-authored-by: Xiaoyao Li Signed-off-by: Xiaoyao Li [Integrated and rebased 2 previous patches (ordered by post time)] Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711104603.1634832-6-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 40f3b5eac8..482979a443 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -9940,6 +9940,7 @@ static const Property x86_cpu_properties[] = { 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), }; #ifndef CONFIG_USER_ONLY From e468c5e44435f5cbe1dd61099e6991a66c25988f Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:46:00 +0800 Subject: [PATCH 2207/2760] i386/cpu: Enable 0x1f leaf for SierraForest by default Host SierraForest CPU has 0x1f leaf by default, so that enable it for Guest CPU by default as well. Suggested-by: Igor Mammedov Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711104603.1634832-7-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 482979a443..668f3e63b7 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5389,8 +5389,11 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, { .version = 3, - .note = "with srf-sp cache model", + .note = "with srf-sp cache model and 0x1f leaf", .cache_info = &xeon_srf_cache_info, + .props = (PropValue[]) { + { "x-force-cpuid-0x1f", "on" }, + } }, { /* end of list */ }, }, From af62bd3db0fde8aa132dd2a0d7dc620827ba1361 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:46:00 +0800 Subject: [PATCH 2208/2760] i386/cpu: Enable 0x1f leaf for SierraForest by default Host SierraForest CPU has 0x1f leaf by default, so that enable it for Guest CPU by default as well. Suggested-by: Igor Mammedov Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711104603.1634832-7-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 1 + 1 file changed, 1 insertion(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 668f3e63b7..c15082e8af 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5393,6 +5393,7 @@ static const X86CPUDefinition builtin_x86_defs[] = { .cache_info = &xeon_srf_cache_info, .props = (PropValue[]) { { "x-force-cpuid-0x1f", "on" }, + { /* end of list */ }, } }, { /* end of list */ }, From 5dbdcdce06d9421598e84fa77692810ccc5c3a81 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:46:01 +0800 Subject: [PATCH 2209/2760] i386/cpu: Enable 0x1f leaf for GraniteRapids by default Host GraniteRapids CPU has 0x1f leaf by default, so that enable it for Guest CPU by default as well. Suggested-by: Igor Mammedov Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711104603.1634832-8-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index c15082e8af..a11e9bb111 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5239,8 +5239,12 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, { .version = 3, - .note = "with gnr-sp cache model", + .note = "with gnr-sp cache model and 0x1f leaf", .cache_info = &xeon_gnr_cache_info, + .props = (PropValue[]) { + { "x-force-cpuid-0x1f", "on" }, + { /* end of list */ }, + } }, { /* end of list */ }, }, From 1bf465f3e0a786bf3267931461c0c15fe6232bc8 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:46:02 +0800 Subject: [PATCH 2210/2760] i386/cpu: Enable 0x1f leaf for SapphireRapids by default Host SapphireRapids CPU has 0x1f leaf by default, so that enable it for Guest CPU by default as well. Suggested-by: Igor Mammedov Reviewed-by: Dapeng Mi Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711104603.1634832-9-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index a11e9bb111..216e0232df 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -5081,8 +5081,12 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, { .version = 4, - .note = "with spr-sp cache model", + .note = "with spr-sp cache model and 0x1f leaf", .cache_info = &xeon_spr_cache_info, + .props = (PropValue[]) { + { "x-force-cpuid-0x1f", "on" }, + { /* end of list */ }, + } }, { /* end of list */ } } From 8113b7f0e662c138a4d0a739807cc3ad33bca8b3 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 11 Jul 2025 18:46:03 +0800 Subject: [PATCH 2211/2760] i386/cpu: Enable 0x1f leaf for YongFeng by default Host YongFeng CPU has 0x1f leaf by default, so that enable it for Guest CPU by default as well. Suggested-by: Ewan Hai Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250711104603.1634832-10-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 216e0232df..4f0c973446 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -6548,8 +6548,12 @@ static const X86CPUDefinition builtin_x86_defs[] = { }, { .version = 3, - .note = "with the cache model", + .note = "with the cache model and 0x1f leaf", .cache_info = &yongfeng_cache_info, + .props = (PropValue[]) { + { "x-force-cpuid-0x1f", "on" }, + { /* end of list */ }, + } }, { /* end of list */ } } From ae1a6c6163bf2ad5889067a7ef121644249fade3 Mon Sep 17 00:00:00 2001 From: Thomas Lambertz Date: Mon, 12 May 2025 23:50:10 +0200 Subject: [PATCH 2212/2760] hw/usb/dev-hid: Support side and extra mouse buttons for usb-tablet The necessary plumbing for side- and extra mouse buttons to reach usb-tablet is already done. But the descriptor advertises three buttons max. Increase this to 5. Buttons are now identical to usb-mouse. Signed-off-by: Thomas Lambertz Signed-off-by: Michael Tokarev --- hw/usb/dev-hid.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/usb/dev-hid.c b/hw/usb/dev-hid.c index 54d064e54e..96623aa322 100644 --- a/hw/usb/dev-hid.c +++ b/hw/usb/dev-hid.c @@ -491,14 +491,14 @@ static const uint8_t qemu_tablet_hid_report_descriptor[] = { 0xa1, 0x00, /* Collection (Physical) */ 0x05, 0x09, /* Usage Page (Button) */ 0x19, 0x01, /* Usage Minimum (1) */ - 0x29, 0x03, /* Usage Maximum (3) */ + 0x29, 0x05, /* Usage Maximum (5) */ 0x15, 0x00, /* Logical Minimum (0) */ 0x25, 0x01, /* Logical Maximum (1) */ - 0x95, 0x03, /* Report Count (3) */ + 0x95, 0x05, /* Report Count (5) */ 0x75, 0x01, /* Report Size (1) */ 0x81, 0x02, /* Input (Data, Variable, Absolute) */ 0x95, 0x01, /* Report Count (1) */ - 0x75, 0x05, /* Report Size (5) */ + 0x75, 0x03, /* Report Size (3) */ 0x81, 0x01, /* Input (Constant) */ 0x05, 0x01, /* Usage Page (Generic Desktop) */ 0x09, 0x30, /* Usage (X) */ From 7d6a775f17f1756f22a55cda847e9d7df93746ba Mon Sep 17 00:00:00 2001 From: Andrew Kreimer Date: Sun, 15 Jun 2025 17:33:03 +0300 Subject: [PATCH 2213/2760] docs: remove repeated word The word 'find' appears twice, remove the extra one. Signed-off-by: Andrew Kreimer Reviewed-by: Markus Armbruster Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/system/introduction.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/system/introduction.rst b/docs/system/introduction.rst index 338d3745c3..4cd46b5b8f 100644 --- a/docs/system/introduction.rst +++ b/docs/system/introduction.rst @@ -81,7 +81,7 @@ may not be optimal for modern systems. For a non-x86 system where we emulate a broad range of machine types, the command lines are generally more explicit in defining the machine -and boot behaviour. You will find often find example command lines in +and boot behaviour. You will often find example command lines in the :ref:`system-targets-ref` section of the manual. While the project doesn't want to discourage users from using the From f180e367fce44b336105a11a62edf9610b6b2a06 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 2 Jul 2025 08:03:19 +0200 Subject: [PATCH 2214/2760] accel/kvm: Adjust the note about the minimum required kernel version MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 126e7f78036 ("kvm: require KVM_CAP_IOEVENTFD and KVM_CAP_IOEVENTFD_ANY_LENGTH") we require at least kernel 4.5 to be able to use KVM. Adjust the upgrade_note accordingly. While we're at it, remove the text about kvm-kmod and the SourceForge URL since this is not actively maintained anymore. Fixes: 126e7f78036 ("kvm: require KVM_CAP_IOEVENTFD and KVM_CAP_IOEVENTFD_ANY_LENGTH") Signed-off-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- accel/kvm/kvm-all.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index a106d1ba0f..78fc2d26fc 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2606,8 +2606,7 @@ static int kvm_init(AccelState *as, MachineState *ms) { MachineClass *mc = MACHINE_GET_CLASS(ms); static const char upgrade_note[] = - "Please upgrade to at least kernel 2.6.29 or recent kvm-kmod\n" - "(see http://sourceforge.net/projects/kvm).\n"; + "Please upgrade to at least kernel 4.5.\n"; const struct { const char *name; int num; From 3773ba281a3b8c3c75b9d9b614148b2356c328ac Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Wed, 2 Jul 2025 10:21:38 +0200 Subject: [PATCH 2215/2760] docs/system/target-i386: Remove the sentence about RHEL 7 being supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to our "Supported build platforms" policy, RHEL 7 is not supported anymore, so let's remove the related sentence from the x86 documentation. Signed-off-by: Thomas Huth Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/system/target-i386.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/system/target-i386.rst b/docs/system/target-i386.rst index 43b09c79d6..2374391397 100644 --- a/docs/system/target-i386.rst +++ b/docs/system/target-i386.rst @@ -37,6 +37,4 @@ OS requirements ~~~~~~~~~~~~~~~ On x86_64 hosts, the default set of CPU features enabled by the KVM -accelerator require the host to be running Linux v4.5 or newer. Red Hat -Enterprise Linux 7 is also supported, since the required -functionality was backported. +accelerator require the host to be running Linux v4.5 or newer. From 21f4c5a700394878a6028708be480e73d8bea890 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 7 Jul 2025 15:24:12 +0100 Subject: [PATCH 2216/2760] hw/uefi: Create and use trace.h wrapper header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation of the trace subsystem (docs/devel/tracing.rst) says that each subdirectory which uses trace events should create a wrapper trace.h file which includes the trace/trace-foo.h generated header, and that .c files then #include "trace.h". We didn't follow this pattern in hw/uefi/. Correct this by creating and using the trace.h wrapper header. Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- hw/uefi/trace.h | 2 ++ hw/uefi/var-service-core.c | 2 +- hw/uefi/var-service-policy.c | 2 +- hw/uefi/var-service-utils.c | 2 +- hw/uefi/var-service-vars.c | 2 +- 5 files changed, 6 insertions(+), 4 deletions(-) create mode 100644 hw/uefi/trace.h diff --git a/hw/uefi/trace.h b/hw/uefi/trace.h new file mode 100644 index 0000000000..6aa1c93896 --- /dev/null +++ b/hw/uefi/trace.h @@ -0,0 +1,2 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#include "trace/trace-hw_uefi.h" diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c index 4836a0cb81..feec5a5958 100644 --- a/hw/uefi/var-service-core.c +++ b/hw/uefi/var-service-core.c @@ -12,7 +12,7 @@ #include "hw/uefi/var-service-api.h" #include "hw/uefi/var-service-edk2.h" -#include "trace/trace-hw_uefi.h" +#include "trace.h" static int uefi_vars_pre_load(void *opaque) { diff --git a/hw/uefi/var-service-policy.c b/hw/uefi/var-service-policy.c index 3b1155fe4e..58da4adbeb 100644 --- a/hw/uefi/var-service-policy.c +++ b/hw/uefi/var-service-policy.c @@ -14,7 +14,7 @@ #include "hw/uefi/var-service-api.h" #include "hw/uefi/var-service-edk2.h" -#include "trace/trace-hw_uefi.h" +#include "trace.h" static void calc_policy(uefi_var_policy *pol); diff --git a/hw/uefi/var-service-utils.c b/hw/uefi/var-service-utils.c index c9ef46570f..258013f436 100644 --- a/hw/uefi/var-service-utils.c +++ b/hw/uefi/var-service-utils.c @@ -8,7 +8,7 @@ #include "hw/uefi/var-service.h" -#include "trace/trace-hw_uefi.h" +#include "trace.h" /* ------------------------------------------------------------------ */ diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c index 7f98d77a38..37d05b71cf 100644 --- a/hw/uefi/var-service-vars.c +++ b/hw/uefi/var-service-vars.c @@ -12,7 +12,7 @@ #include "hw/uefi/var-service-api.h" #include "hw/uefi/var-service-edk2.h" -#include "trace/trace-hw_uefi.h" +#include "trace.h" #define EFI_VARIABLE_ATTRIBUTE_SUPPORTED \ (EFI_VARIABLE_NON_VOLATILE | \ From 0f55ca43cd90eb89193bae45d0907c1b17a1dadd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 7 Jul 2025 15:49:17 +0100 Subject: [PATCH 2217/2760] docs/devel/tracing: Update trace.h creation rune to include SPDX checkpatch now checks that new files have an SPDX line. If you use the shell rune in tracing.rst to create a trace.h wrapper header, this triggers checkpatch to complain. Although these files are tiny, it's worth having the SPDX line to avoid having to add extra exception cases to checkpatch. Update the rune to include creating an SPDX line. Signed-off-by: Peter Maydell Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- docs/devel/tracing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devel/tracing.rst b/docs/devel/tracing.rst index 043bed7fd0..f4557ee20e 100644 --- a/docs/devel/tracing.rst +++ b/docs/devel/tracing.rst @@ -76,7 +76,7 @@ The "io/trace.h" file must be created manually with an #include of the corresponding "trace/trace-.h" file that will be generated in the builddir:: - $ echo '#include "trace/trace-io.h"' >io/trace.h + $ (echo '/* SPDX-License-Identifier: GPL-2.0-or-later */' ; echo '#include "trace/trace-io.h"') >io/trace.h While it is possible to include a trace.h file from outside a source file's own sub-directory, this is discouraged in general. It is strongly preferred that From 110d0fa2d4d1f754242f6775baec43776a9adb35 Mon Sep 17 00:00:00 2001 From: Anastasia Belova Date: Mon, 2 Jun 2025 11:57:17 +0300 Subject: [PATCH 2218/2760] net: fix buffer overflow in af_xdp_umem_create() s->pool has n_descs elements so maximum i should be n_descs - 1. Fix the upper bound. Found by Linux Verification Center (linuxtesting.org) with SVACE. Fixes: cb039ef3d9 ("net: add initial support for AF_XDP network backend") Cc: qemu-stable@nongnu.org Reviewed-by: Ilya Maximets Signed-off-by: Anastasia Belova Signed-off-by: Jason Wang --- net/af-xdp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/af-xdp.c b/net/af-xdp.c index 01c5fb914e..d022534d76 100644 --- a/net/af-xdp.c +++ b/net/af-xdp.c @@ -323,7 +323,7 @@ static int af_xdp_umem_create(AFXDPState *s, int sock_fd, Error **errp) s->pool = g_new(uint64_t, n_descs); /* Fill the pool in the opposite order, because it's a LIFO queue. */ - for (i = n_descs; i >= 0; i--) { + for (i = n_descs - 1; i >= 0; i--) { s->pool[i] = i * XSK_UMEM__DEFAULT_FRAME_SIZE; } s->n_pool = n_descs; From adda0ad56bd28d5a809051cbd190fda5798ec4e4 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 30 May 2025 14:18:53 +0900 Subject: [PATCH 2219/2760] virtio-net: Add queues for RSS during migration virtio_net_pre_load_queues() inspects vdev->guest_features to tell if VIRTIO_NET_F_RSS or VIRTIO_NET_F_MQ is enabled to infer the required number of queues. This works for VIRTIO_NET_F_MQ but it doesn't for VIRTIO_NET_F_RSS because only the lowest 32 bits of vdev->guest_features is set at the point and VIRTIO_NET_F_RSS uses bit 60 while VIRTIO_NET_F_MQ uses bit 22. Instead of inferring the required number of queues from vdev->guest_features, use the number loaded from the vm state. This change also has a nice side effect to remove a duplicate peer queue pair change by circumventing virtio_net_set_multiqueue(). Also update the comment in include/hw/virtio/virtio.h to prevent an implementation of pre_load_queues() from refering to any fields being loaded during migration by accident in the future. Fixes: 8c49756825da ("virtio-net: Add only one queue pair when realizing") Tested-by: Lei Yang Cc: qemu-stable@nongnu.org Signed-off-by: Akihiko Odaki Signed-off-by: Jason Wang --- hw/net/virtio-net.c | 11 ++++------- hw/virtio/virtio.c | 14 +++++++------- include/hw/virtio/virtio.h | 10 ++++++++-- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index eb93607b8c..351377c025 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3022,11 +3022,10 @@ static void virtio_net_del_queue(VirtIONet *n, int index) virtio_del_queue(vdev, index * 2 + 1); } -static void virtio_net_change_num_queue_pairs(VirtIONet *n, int new_max_queue_pairs) +static void virtio_net_change_num_queues(VirtIONet *n, int new_num_queues) { VirtIODevice *vdev = VIRTIO_DEVICE(n); int old_num_queues = virtio_get_num_queues(vdev); - int new_num_queues = new_max_queue_pairs * 2 + 1; int i; assert(old_num_queues >= 3); @@ -3062,16 +3061,14 @@ static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue) int max = multiqueue ? n->max_queue_pairs : 1; n->multiqueue = multiqueue; - virtio_net_change_num_queue_pairs(n, max); + virtio_net_change_num_queues(n, max * 2 + 1); virtio_net_set_queue_pairs(n); } -static int virtio_net_pre_load_queues(VirtIODevice *vdev) +static int virtio_net_pre_load_queues(VirtIODevice *vdev, uint32_t n) { - virtio_net_set_multiqueue(VIRTIO_NET(vdev), - virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_RSS) || - virtio_has_feature(vdev->guest_features, VIRTIO_NET_F_MQ)); + virtio_net_change_num_queues(VIRTIO_NET(vdev), n); return 0; } diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 82a285a31d..7e38b1ca97 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -3270,13 +3270,6 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) config_len--; } - if (vdc->pre_load_queues) { - ret = vdc->pre_load_queues(vdev); - if (ret) { - return ret; - } - } - num = qemu_get_be32(f); if (num > VIRTIO_QUEUE_MAX) { @@ -3284,6 +3277,13 @@ virtio_load(VirtIODevice *vdev, QEMUFile *f, int version_id) return -1; } + if (vdc->pre_load_queues) { + ret = vdc->pre_load_queues(vdev, num); + if (ret) { + return ret; + } + } + for (i = 0; i < num; i++) { vdev->vq[i].vring.num = qemu_get_be32(f); if (k->has_variable_vring_alignment) { diff --git a/include/hw/virtio/virtio.h b/include/hw/virtio/virtio.h index 214d4a77e9..c594764f23 100644 --- a/include/hw/virtio/virtio.h +++ b/include/hw/virtio/virtio.h @@ -210,8 +210,14 @@ struct VirtioDeviceClass { void (*guest_notifier_mask)(VirtIODevice *vdev, int n, bool mask); int (*start_ioeventfd)(VirtIODevice *vdev); void (*stop_ioeventfd)(VirtIODevice *vdev); - /* Called before loading queues. Useful to add queues before loading. */ - int (*pre_load_queues)(VirtIODevice *vdev); + /* + * Called before loading queues. + * If the number of queues change at runtime, use @n to know the + * number and add or remove queues accordingly. + * Note that this function is called in the middle of loading vmsd; + * no assumption should be made on states being loaded from vmsd. + */ + int (*pre_load_queues)(VirtIODevice *vdev, uint32_t n); /* Saving and loading of a device; trying to deprecate save/load * use vmsd for new devices. */ From adf684ce6908e2d2883946ffe1790c4ddfcf3ce0 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:15 +0200 Subject: [PATCH 2220/2760] net: Refactor stream logic for reuse in '-net passt' To prepare for the implementation of '-net passt', this patch moves the generic stream handling functions from net/stream.c into new net/stream_data.c and net/stream_data.h files. This refactoring introduces a NetStreamData struct that encapsulates the generic fields and logic previously in NetStreamState. The NetStreamState now embeds NetStreamData and delegates the core stream operations to the new generic functions. To maintain flexibility for different users of this generic code, callbacks for send and listen operations are now passed via function pointers within the NetStreamData struct. This allows callers to provide their own specific implementations while reusing the common connection and data transfer logic. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- net/meson.build | 3 +- net/stream.c | 282 +++++++++++----------------------------------- net/stream_data.c | 193 +++++++++++++++++++++++++++++++ net/stream_data.h | 31 +++++ 4 files changed, 290 insertions(+), 219 deletions(-) create mode 100644 net/stream_data.c create mode 100644 net/stream_data.h diff --git a/net/meson.build b/net/meson.build index bb97b4dcbe..bb3c011e5a 100644 --- a/net/meson.build +++ b/net/meson.build @@ -1,6 +1,7 @@ system_ss.add(files( 'announce.c', 'checksum.c', + 'dgram.c', 'dump.c', 'eth.c', 'filter-buffer.c', @@ -12,7 +13,7 @@ system_ss.add(files( 'queue.c', 'socket.c', 'stream.c', - 'dgram.c', + 'stream_data.c', 'util.c', )) diff --git a/net/stream.c b/net/stream.c index 6152d2a05e..d893f02cab 100644 --- a/net/stream.c +++ b/net/stream.c @@ -27,173 +27,50 @@ #include "net/net.h" #include "clients.h" -#include "monitor/monitor.h" #include "qapi/error.h" -#include "qemu/error-report.h" -#include "qemu/option.h" -#include "qemu/sockets.h" -#include "qemu/iov.h" -#include "qemu/main-loop.h" -#include "qemu/cutils.h" -#include "io/channel.h" -#include "io/channel-socket.h" #include "io/net-listener.h" #include "qapi/qapi-events-net.h" #include "qapi/qapi-visit-sockets.h" #include "qapi/clone-visitor.h" +#include "stream_data.h" + typedef struct NetStreamState { - NetClientState nc; - QIOChannel *listen_ioc; - QIONetListener *listener; - QIOChannel *ioc; - guint ioc_read_tag; - guint ioc_write_tag; - SocketReadState rs; - unsigned int send_index; /* number of bytes sent*/ + NetStreamData data; uint32_t reconnect_ms; guint timer_tag; SocketAddress *addr; } NetStreamState; -static void net_stream_listen(QIONetListener *listener, - QIOChannelSocket *cioc, - void *opaque); static void net_stream_arm_reconnect(NetStreamState *s); -static gboolean net_stream_writable(QIOChannel *ioc, - GIOCondition condition, - gpointer data) -{ - NetStreamState *s = data; - - s->ioc_write_tag = 0; - - qemu_flush_queued_packets(&s->nc); - - return G_SOURCE_REMOVE; -} - static ssize_t net_stream_receive(NetClientState *nc, const uint8_t *buf, size_t size) { - NetStreamState *s = DO_UPCAST(NetStreamState, nc, nc); - uint32_t len = htonl(size); - struct iovec iov[] = { - { - .iov_base = &len, - .iov_len = sizeof(len), - }, { - .iov_base = (void *)buf, - .iov_len = size, - }, - }; - struct iovec local_iov[2]; - unsigned int nlocal_iov; - size_t remaining; - ssize_t ret; - - remaining = iov_size(iov, 2) - s->send_index; - nlocal_iov = iov_copy(local_iov, 2, iov, 2, s->send_index, remaining); - ret = qio_channel_writev(s->ioc, local_iov, nlocal_iov, NULL); - if (ret == QIO_CHANNEL_ERR_BLOCK) { - ret = 0; /* handled further down */ - } - if (ret == -1) { - s->send_index = 0; - return -errno; - } - if (ret < (ssize_t)remaining) { - s->send_index += ret; - s->ioc_write_tag = qio_channel_add_watch(s->ioc, G_IO_OUT, - net_stream_writable, s, NULL); - return 0; - } - s->send_index = 0; - return size; -} - -static gboolean net_stream_send(QIOChannel *ioc, - GIOCondition condition, - gpointer data); - -static void net_stream_send_completed(NetClientState *nc, ssize_t len) -{ - NetStreamState *s = DO_UPCAST(NetStreamState, nc, nc); - - if (!s->ioc_read_tag) { - s->ioc_read_tag = qio_channel_add_watch(s->ioc, G_IO_IN, - net_stream_send, s, NULL); - } -} + NetStreamData *d = DO_UPCAST(NetStreamData, nc, nc); -static void net_stream_rs_finalize(SocketReadState *rs) -{ - NetStreamState *s = container_of(rs, NetStreamState, rs); - - if (qemu_send_packet_async(&s->nc, rs->buf, - rs->packet_len, - net_stream_send_completed) == 0) { - if (s->ioc_read_tag) { - g_source_remove(s->ioc_read_tag); - s->ioc_read_tag = 0; - } - } + return net_stream_data_receive(d, buf, size); } static gboolean net_stream_send(QIOChannel *ioc, GIOCondition condition, gpointer data) { - NetStreamState *s = data; - int size; - int ret; - QEMU_UNINITIALIZED char buf1[NET_BUFSIZE]; - const char *buf; - - size = qio_channel_read(s->ioc, buf1, sizeof(buf1), NULL); - if (size < 0) { - if (errno != EWOULDBLOCK) { - goto eoc; - } - } else if (size == 0) { - /* end of connection */ - eoc: - s->ioc_read_tag = 0; - if (s->ioc_write_tag) { - g_source_remove(s->ioc_write_tag); - s->ioc_write_tag = 0; - } - if (s->listener) { - qemu_set_info_str(&s->nc, "listening"); - qio_net_listener_set_client_func(s->listener, net_stream_listen, - s, NULL); - } - object_unref(OBJECT(s->ioc)); - s->ioc = NULL; - - net_socket_rs_init(&s->rs, net_stream_rs_finalize, false); - s->nc.link_down = true; + if (net_stream_data_send(ioc, condition, data) == G_SOURCE_REMOVE) { + NetStreamState *s = DO_UPCAST(NetStreamState, data, data); - qapi_event_send_netdev_stream_disconnected(s->nc.name); + qapi_event_send_netdev_stream_disconnected(s->data.nc.name); net_stream_arm_reconnect(s); return G_SOURCE_REMOVE; } - buf = buf1; - - ret = net_fill_rstate(&s->rs, (const uint8_t *)buf, size); - - if (ret == -1) { - goto eoc; - } return G_SOURCE_CONTINUE; } static void net_stream_cleanup(NetClientState *nc) { - NetStreamState *s = DO_UPCAST(NetStreamState, nc, nc); + NetStreamState *s = DO_UPCAST(NetStreamState, data.nc, nc); if (s->timer_tag) { g_source_remove(s->timer_tag); s->timer_tag = 0; @@ -202,28 +79,28 @@ static void net_stream_cleanup(NetClientState *nc) qapi_free_SocketAddress(s->addr); s->addr = NULL; } - if (s->ioc) { - if (QIO_CHANNEL_SOCKET(s->ioc)->fd != -1) { - if (s->ioc_read_tag) { - g_source_remove(s->ioc_read_tag); - s->ioc_read_tag = 0; + if (s->data.ioc) { + if (QIO_CHANNEL_SOCKET(s->data.ioc)->fd != -1) { + if (s->data.ioc_read_tag) { + g_source_remove(s->data.ioc_read_tag); + s->data.ioc_read_tag = 0; } - if (s->ioc_write_tag) { - g_source_remove(s->ioc_write_tag); - s->ioc_write_tag = 0; + if (s->data.ioc_write_tag) { + g_source_remove(s->data.ioc_write_tag); + s->data.ioc_write_tag = 0; } } - object_unref(OBJECT(s->ioc)); - s->ioc = NULL; + object_unref(OBJECT(s->data.ioc)); + s->data.ioc = NULL; } - if (s->listen_ioc) { - if (s->listener) { - qio_net_listener_disconnect(s->listener); - object_unref(OBJECT(s->listener)); - s->listener = NULL; + if (s->data.listen_ioc) { + if (s->data.listener) { + qio_net_listener_disconnect(s->data.listener); + object_unref(OBJECT(s->data.listener)); + s->data.listener = NULL; } - object_unref(OBJECT(s->listen_ioc)); - s->listen_ioc = NULL; + object_unref(OBJECT(s->data.listen_ioc)); + s->data.listen_ioc = NULL; } } @@ -235,23 +112,13 @@ static NetClientInfo net_stream_info = { }; static void net_stream_listen(QIONetListener *listener, - QIOChannelSocket *cioc, - void *opaque) + QIOChannelSocket *cioc, gpointer data) { - NetStreamState *s = opaque; + NetStreamData *d = data; SocketAddress *addr; char *uri; - object_ref(OBJECT(cioc)); - - qio_net_listener_set_client_func(s->listener, NULL, s, NULL); - - s->ioc = QIO_CHANNEL(cioc); - qio_channel_set_name(s->ioc, "stream-server"); - s->nc.link_down = false; - - s->ioc_read_tag = qio_channel_add_watch(s->ioc, G_IO_IN, net_stream_send, - s, NULL); + net_stream_data_listen(listener, cioc, data); if (cioc->localAddr.ss_family == AF_UNIX) { addr = qio_channel_socket_get_local_address(cioc, NULL); @@ -260,22 +127,22 @@ static void net_stream_listen(QIONetListener *listener, } g_assert(addr != NULL); uri = socket_uri(addr); - qemu_set_info_str(&s->nc, "%s", uri); + qemu_set_info_str(&d->nc, "%s", uri); g_free(uri); - qapi_event_send_netdev_stream_connected(s->nc.name, addr); + qapi_event_send_netdev_stream_connected(d->nc.name, addr); qapi_free_SocketAddress(addr); } static void net_stream_server_listening(QIOTask *task, gpointer opaque) { - NetStreamState *s = opaque; - QIOChannelSocket *listen_sioc = QIO_CHANNEL_SOCKET(s->listen_ioc); + NetStreamData *d = opaque; + QIOChannelSocket *listen_sioc = QIO_CHANNEL_SOCKET(d->listen_ioc); SocketAddress *addr; int ret; Error *err = NULL; if (qio_task_propagate_error(task, &err)) { - qemu_set_info_str(&s->nc, "error: %s", error_get_pretty(err)); + qemu_set_info_str(&d->nc, "error: %s", error_get_pretty(err)); error_free(err); return; } @@ -284,20 +151,21 @@ static void net_stream_server_listening(QIOTask *task, gpointer opaque) g_assert(addr != NULL); ret = qemu_socket_try_set_nonblock(listen_sioc->fd); if (addr->type == SOCKET_ADDRESS_TYPE_FD && ret < 0) { - qemu_set_info_str(&s->nc, "can't use file descriptor %s (errno %d)", + qemu_set_info_str(&d->nc, "can't use file descriptor %s (errno %d)", addr->u.fd.str, -ret); return; } g_assert(ret == 0); qapi_free_SocketAddress(addr); - s->nc.link_down = true; - s->listener = qio_net_listener_new(); + d->nc.link_down = true; + d->listener = qio_net_listener_new(); - qemu_set_info_str(&s->nc, "listening"); - net_socket_rs_init(&s->rs, net_stream_rs_finalize, false); - qio_net_listener_set_client_func(s->listener, net_stream_listen, s, NULL); - qio_net_listener_add(s->listener, listen_sioc); + qemu_set_info_str(&d->nc, "listening"); + net_socket_rs_init(&d->rs, net_stream_data_rs_finalize, false); + qio_net_listener_set_client_func(d->listener, d->listen, d, + NULL); + qio_net_listener_add(d->listener, listen_sioc); } static int net_stream_server_init(NetClientState *peer, @@ -307,16 +175,18 @@ static int net_stream_server_init(NetClientState *peer, Error **errp) { NetClientState *nc; - NetStreamState *s; + NetStreamData *d; QIOChannelSocket *listen_sioc = qio_channel_socket_new(); nc = qemu_new_net_client(&net_stream_info, peer, model, name); - s = DO_UPCAST(NetStreamState, nc, nc); - qemu_set_info_str(&s->nc, "initializing"); + d = DO_UPCAST(NetStreamData, nc, nc); + d->send = net_stream_send; + d->listen = net_stream_listen; + qemu_set_info_str(&d->nc, "initializing"); - s->listen_ioc = QIO_CHANNEL(listen_sioc); + d->listen_ioc = QIO_CHANNEL(listen_sioc); qio_channel_socket_listen_async(listen_sioc, addr, 0, - net_stream_server_listening, s, + net_stream_server_listening, d, NULL, NULL); return 0; @@ -325,49 +195,23 @@ static int net_stream_server_init(NetClientState *peer, static void net_stream_client_connected(QIOTask *task, gpointer opaque) { NetStreamState *s = opaque; - QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(s->ioc); + NetStreamData *d = &s->data; + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(d->ioc); SocketAddress *addr; gchar *uri; - int ret; - Error *err = NULL; - if (qio_task_propagate_error(task, &err)) { - qemu_set_info_str(&s->nc, "error: %s", error_get_pretty(err)); - error_free(err); - goto error; + if (net_stream_data_client_connected(task, d) == -1) { + net_stream_arm_reconnect(s); + return; } addr = qio_channel_socket_get_remote_address(sioc, NULL); g_assert(addr != NULL); uri = socket_uri(addr); - qemu_set_info_str(&s->nc, "%s", uri); + qemu_set_info_str(&d->nc, "%s", uri); g_free(uri); - - ret = qemu_socket_try_set_nonblock(sioc->fd); - if (addr->type == SOCKET_ADDRESS_TYPE_FD && ret < 0) { - qemu_set_info_str(&s->nc, "can't use file descriptor %s (errno %d)", - addr->u.fd.str, -ret); - qapi_free_SocketAddress(addr); - goto error; - } - g_assert(ret == 0); - - net_socket_rs_init(&s->rs, net_stream_rs_finalize, false); - - /* Disable Nagle algorithm on TCP sockets to reduce latency */ - qio_channel_set_delay(s->ioc, false); - - s->ioc_read_tag = qio_channel_add_watch(s->ioc, G_IO_IN, net_stream_send, - s, NULL); - s->nc.link_down = false; - qapi_event_send_netdev_stream_connected(s->nc.name, addr); + qapi_event_send_netdev_stream_connected(d->nc.name, addr); qapi_free_SocketAddress(addr); - - return; -error: - object_unref(OBJECT(s->ioc)); - s->ioc = NULL; - net_stream_arm_reconnect(s); } static gboolean net_stream_reconnect(gpointer data) @@ -378,7 +222,7 @@ static gboolean net_stream_reconnect(gpointer data) s->timer_tag = 0; sioc = qio_channel_socket_new(); - s->ioc = QIO_CHANNEL(sioc); + s->data.ioc = QIO_CHANNEL(sioc); qio_channel_socket_connect_async(sioc, s->addr, net_stream_client_connected, s, NULL, NULL); @@ -388,7 +232,7 @@ static gboolean net_stream_reconnect(gpointer data) static void net_stream_arm_reconnect(NetStreamState *s) { if (s->reconnect_ms && s->timer_tag == 0) { - qemu_set_info_str(&s->nc, "connecting"); + qemu_set_info_str(&s->data.nc, "connecting"); s->timer_tag = g_timeout_add(s->reconnect_ms, net_stream_reconnect, s); } } @@ -405,11 +249,13 @@ static int net_stream_client_init(NetClientState *peer, QIOChannelSocket *sioc = qio_channel_socket_new(); nc = qemu_new_net_client(&net_stream_info, peer, model, name); - s = DO_UPCAST(NetStreamState, nc, nc); - qemu_set_info_str(&s->nc, "connecting"); + s = DO_UPCAST(NetStreamState, data.nc, nc); + qemu_set_info_str(&s->data.nc, "connecting"); - s->ioc = QIO_CHANNEL(sioc); - s->nc.link_down = true; + s->data.ioc = QIO_CHANNEL(sioc); + s->data.nc.link_down = true; + s->data.send = net_stream_send; + s->data.listen = net_stream_listen; s->reconnect_ms = reconnect_ms; if (reconnect_ms) { diff --git a/net/stream_data.c b/net/stream_data.c new file mode 100644 index 0000000000..5af27e0d1d --- /dev/null +++ b/net/stream_data.c @@ -0,0 +1,193 @@ +/* + * net stream generic functions + * + * Copyright Red Hat + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/iov.h" +#include "qapi/error.h" +#include "net/net.h" +#include "io/channel.h" +#include "io/net-listener.h" + +#include "stream_data.h" + +static gboolean net_stream_data_writable(QIOChannel *ioc, + GIOCondition condition, gpointer data) +{ + NetStreamData *d = data; + + d->ioc_write_tag = 0; + + qemu_flush_queued_packets(&d->nc); + + return G_SOURCE_REMOVE; +} + +ssize_t net_stream_data_receive(NetStreamData *d, const uint8_t *buf, + size_t size) +{ + uint32_t len = htonl(size); + struct iovec iov[] = { + { + .iov_base = &len, + .iov_len = sizeof(len), + }, { + .iov_base = (void *)buf, + .iov_len = size, + }, + }; + struct iovec local_iov[2]; + unsigned int nlocal_iov; + size_t remaining; + ssize_t ret; + + remaining = iov_size(iov, 2) - d->send_index; + nlocal_iov = iov_copy(local_iov, 2, iov, 2, d->send_index, remaining); + ret = qio_channel_writev(d->ioc, local_iov, nlocal_iov, NULL); + if (ret == QIO_CHANNEL_ERR_BLOCK) { + ret = 0; /* handled further down */ + } + if (ret == -1) { + d->send_index = 0; + return -errno; + } + if (ret < (ssize_t)remaining) { + d->send_index += ret; + d->ioc_write_tag = qio_channel_add_watch(d->ioc, G_IO_OUT, + net_stream_data_writable, d, + NULL); + return 0; + } + d->send_index = 0; + return size; +} + +static void net_stream_data_send_completed(NetClientState *nc, ssize_t len) +{ + NetStreamData *d = DO_UPCAST(NetStreamData, nc, nc); + + if (!d->ioc_read_tag) { + d->ioc_read_tag = qio_channel_add_watch(d->ioc, G_IO_IN, d->send, d, + NULL); + } +} + +void net_stream_data_rs_finalize(SocketReadState *rs) +{ + NetStreamData *d = container_of(rs, NetStreamData, rs); + + if (qemu_send_packet_async(&d->nc, rs->buf, + rs->packet_len, + net_stream_data_send_completed) == 0) { + if (d->ioc_read_tag) { + g_source_remove(d->ioc_read_tag); + d->ioc_read_tag = 0; + } + } +} + +gboolean net_stream_data_send(QIOChannel *ioc, GIOCondition condition, + NetStreamData *d) +{ + int size; + int ret; + QEMU_UNINITIALIZED char buf1[NET_BUFSIZE]; + const char *buf; + + size = qio_channel_read(d->ioc, buf1, sizeof(buf1), NULL); + if (size < 0) { + if (errno != EWOULDBLOCK) { + goto eoc; + } + } else if (size == 0) { + /* end of connection */ + eoc: + d->ioc_read_tag = 0; + if (d->ioc_write_tag) { + g_source_remove(d->ioc_write_tag); + d->ioc_write_tag = 0; + } + if (d->listener) { + qemu_set_info_str(&d->nc, "listening"); + qio_net_listener_set_client_func(d->listener, + d->listen, d, NULL); + } + object_unref(OBJECT(d->ioc)); + d->ioc = NULL; + + net_socket_rs_init(&d->rs, net_stream_data_rs_finalize, false); + d->nc.link_down = true; + + return G_SOURCE_REMOVE; + } + buf = buf1; + + ret = net_fill_rstate(&d->rs, (const uint8_t *)buf, size); + + if (ret == -1) { + goto eoc; + } + + return G_SOURCE_CONTINUE; +} + +void net_stream_data_listen(QIONetListener *listener, + QIOChannelSocket *cioc, + NetStreamData *d) +{ + object_ref(OBJECT(cioc)); + + qio_net_listener_set_client_func(d->listener, NULL, d, NULL); + + d->ioc = QIO_CHANNEL(cioc); + qio_channel_set_name(d->ioc, "stream-server"); + d->nc.link_down = false; + + d->ioc_read_tag = qio_channel_add_watch(d->ioc, G_IO_IN, d->send, d, NULL); +} + +int net_stream_data_client_connected(QIOTask *task, NetStreamData *d) +{ + QIOChannelSocket *sioc = QIO_CHANNEL_SOCKET(d->ioc); + SocketAddress *addr; + int ret; + Error *err = NULL; + + if (qio_task_propagate_error(task, &err)) { + qemu_set_info_str(&d->nc, "error: %s", error_get_pretty(err)); + error_free(err); + goto error; + } + + addr = qio_channel_socket_get_remote_address(sioc, NULL); + g_assert(addr != NULL); + + ret = qemu_socket_try_set_nonblock(sioc->fd); + if (addr->type == SOCKET_ADDRESS_TYPE_FD && ret < 0) { + qemu_set_info_str(&d->nc, "can't use file descriptor %s (errno %d)", + addr->u.fd.str, -ret); + qapi_free_SocketAddress(addr); + goto error; + } + g_assert(ret == 0); + qapi_free_SocketAddress(addr); + + net_socket_rs_init(&d->rs, net_stream_data_rs_finalize, false); + + /* Disable Nagle algorithm on TCP sockets to reduce latency */ + qio_channel_set_delay(d->ioc, false); + + d->ioc_read_tag = qio_channel_add_watch(d->ioc, G_IO_IN, d->send, d, NULL); + d->nc.link_down = false; + + return 0; +error: + object_unref(OBJECT(d->ioc)); + d->ioc = NULL; + + return -1; +} diff --git a/net/stream_data.h b/net/stream_data.h new file mode 100644 index 0000000000..b868625665 --- /dev/null +++ b/net/stream_data.h @@ -0,0 +1,31 @@ +/* + * net stream generic functions + * + * Copyright Red Hat + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +typedef struct NetStreamData { + NetClientState nc; + QIOChannel *ioc; + guint ioc_read_tag; + guint ioc_write_tag; + SocketReadState rs; + unsigned int send_index; /* number of bytes sent*/ + QIOChannelFunc send; + /* server data */ + QIOChannel *listen_ioc; + QIONetListener *listener; + QIONetListenerClientFunc listen; +} NetStreamData; + +ssize_t net_stream_data_receive(NetStreamData *d, const uint8_t *buf, + size_t size); +void net_stream_data_rs_finalize(SocketReadState *rs); +gboolean net_stream_data_send(QIOChannel *ioc, GIOCondition condition, + NetStreamData *d); +int net_stream_data_client_connected(QIOTask *task, NetStreamData *d); +void net_stream_data_listen(QIONetListener *listener, + QIOChannelSocket *cioc, + NetStreamData *d); From 9b5b45c7173b0447ec19aed4b901fd22db83d7eb Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:16 +0200 Subject: [PATCH 2221/2760] net: Define net_client_set_link() The code to set the link status is currently located in qmp_set_link(). This function identifies the device by name, searches for the corresponding NetClientState, and then updates the link status. In some parts of the code, such as vhost-user.c, the NetClientState are already available. Calling qmp_set_link() from these locations leads to a redundant search for the clients. This patch refactors the logic by introducing a new function, net_client_set_link(), which accepts a NetClientState array directly. qmp_set_link() is simplified to be a wrapper that performs the client search and then calls the new function. The vhost-user implementation is updated to use net_client_set_link() directly, thereby eliminating the unnecessary client lookup. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- include/net/net.h | 1 + net/net.c | 32 ++++++++++++++++++++------------ net/vhost-user.c | 4 ++-- 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/include/net/net.h b/include/net/net.h index cdd5b109b0..ac59b593ba 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -298,6 +298,7 @@ void net_client_parse(QemuOptsList *opts_list, const char *optstr); void show_netdevs(void); void net_init_clients(void); void net_check_clients(void); +void net_client_set_link(NetClientState **ncs, int queues, bool up); void net_cleanup(void); void hmp_host_net_add(Monitor *mon, const QDict *qdict); void hmp_host_net_remove(Monitor *mon, const QDict *qdict); diff --git a/net/net.c b/net/net.c index 39d6f28158..cfa2d8e958 100644 --- a/net/net.c +++ b/net/net.c @@ -1601,21 +1601,11 @@ void colo_notify_filters_event(int event, Error **errp) } } -void qmp_set_link(const char *name, bool up, Error **errp) +void net_client_set_link(NetClientState **ncs, int queues, bool up) { - NetClientState *ncs[MAX_QUEUE_NUM]; NetClientState *nc; - int queues, i; - - queues = qemu_find_net_clients_except(name, ncs, - NET_CLIENT_DRIVER__MAX, - MAX_QUEUE_NUM); + int i; - if (queues == 0) { - error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, - "Device '%s' not found", name); - return; - } nc = ncs[0]; for (i = 0; i < queues; i++) { @@ -1646,6 +1636,24 @@ void qmp_set_link(const char *name, bool up, Error **errp) } } +void qmp_set_link(const char *name, bool up, Error **errp) +{ + NetClientState *ncs[MAX_QUEUE_NUM]; + int queues; + + queues = qemu_find_net_clients_except(name, ncs, + NET_CLIENT_DRIVER__MAX, + MAX_QUEUE_NUM); + + if (queues == 0) { + error_set(errp, ERROR_CLASS_DEVICE_NOT_FOUND, + "Device '%s' not found", name); + return; + } + + net_client_set_link(ncs, queues, up); +} + static void net_vm_change_state_handler(void *opaque, bool running, RunState state) { diff --git a/net/vhost-user.c b/net/vhost-user.c index 0b235e50c6..10ac8dc0b3 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -264,7 +264,7 @@ static void chr_closed_bh(void *opaque) vhost_user_save_acked_features(ncs[i]); } - qmp_set_link(name, false, &err); + net_client_set_link(ncs, queues, false); qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event, NULL, opaque, NULL, true); @@ -300,7 +300,7 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event) } s->watch = qemu_chr_fe_add_watch(&s->chr, G_IO_HUP, net_vhost_user_watch, s); - qmp_set_link(name, true, &err); + net_client_set_link(ncs, queues, true); s->started = true; qapi_event_send_netdev_vhost_user_connected(name, chr->label); break; From 7136352b40631b058dd0fe731a0d404e761e799f Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:17 +0200 Subject: [PATCH 2222/2760] vhost_net: Rename vhost_set_vring_enable() for clarity This is a cosmetic change with no functional impact. The function vhost_set_vring_enable() is specific to vhost_net and is used outside of vhost_net.c (specifically, in hw/net/virtio-net.c). To prevent confusion with other similarly named vhost functions, such as the one found in cryptodev-vhost.c, it has been renamed to vhost_net_set_vring_enable(). This clarifies that the function belongs to the vhost_net module. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- hw/net/vhost_net-stub.c | 2 +- hw/net/vhost_net.c | 4 ++-- hw/net/virtio-net.c | 4 ++-- include/net/vhost_net.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c index 72df6d757e..7bed0bf92b 100644 --- a/hw/net/vhost_net-stub.c +++ b/hw/net/vhost_net-stub.c @@ -101,7 +101,7 @@ VHostNetState *get_vhost_net(NetClientState *nc) return 0; } -int vhost_set_vring_enable(NetClientState *nc, int enable) +int vhost_net_set_vring_enable(NetClientState *nc, int enable) { return 0; } diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 891f235a0a..cb87056397 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -551,7 +551,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, if (peer->vring_enable) { /* restore vring enable state */ - r = vhost_set_vring_enable(peer, peer->vring_enable); + r = vhost_net_set_vring_enable(peer, peer->vring_enable); if (r < 0) { goto err_guest_notifiers; @@ -686,7 +686,7 @@ VHostNetState *get_vhost_net(NetClientState *nc) return vhost_net; } -int vhost_set_vring_enable(NetClientState *nc, int enable) +int vhost_net_set_vring_enable(NetClientState *nc, int enable) { VHostNetState *net = get_vhost_net(nc); const VhostOps *vhost_ops = net->dev.vhost_ops; diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 351377c025..e3400f18c8 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -697,7 +697,7 @@ static int peer_attach(VirtIONet *n, int index) } if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { - vhost_set_vring_enable(nc->peer, 1); + vhost_net_set_vring_enable(nc->peer, 1); } if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) { @@ -720,7 +720,7 @@ static int peer_detach(VirtIONet *n, int index) } if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { - vhost_set_vring_enable(nc->peer, 0); + vhost_net_set_vring_enable(nc->peer, 0); } if (nc->peer->info->type != NET_CLIENT_DRIVER_TAP) { diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index c6a5361a2a..0f40049f34 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -41,7 +41,7 @@ void vhost_net_config_mask(VHostNetState *net, VirtIODevice *dev, bool mask); int vhost_net_notify_migration_done(VHostNetState *net, char* mac_addr); VHostNetState *get_vhost_net(NetClientState *nc); -int vhost_set_vring_enable(NetClientState * nc, int enable); +int vhost_net_set_vring_enable(NetClientState *nc, int enable); uint64_t vhost_net_get_acked_features(VHostNetState *net); From 8f6e5c620a5b21c070eed93721236cad48b6f9d7 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:18 +0200 Subject: [PATCH 2223/2760] net: Add get_vhost_net callback to NetClientInfo The get_vhost_net() function previously contained a large switch statement to find the VHostNetState pointer based on the net client's type. This created a tight coupling, requiring the generic vhost layer to be aware of every specific backend that supported vhost, such as tap, vhost-user, and vhost-vdpa. This approach is not scalable and requires modifying a central function for any new backend. It also forced each backend to expose its internal getter function in a public header file. This patch refactors the logic by introducing a new get_vhost_net function pointer to the NetClientInfo struct. The central get_vhost_net() function is now a simple, generic dispatcher that invokes the callback provided by the net client. Each backend now implements its own private getter and registers it in its NetClientInfo. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- hw/net/vhost_net.c | 31 ++++--------------------------- include/net/net.h | 2 ++ include/net/tap.h | 3 --- include/net/vhost-user.h | 1 - include/net/vhost-vdpa.h | 2 -- net/tap-win32.c | 5 ----- net/tap.c | 20 +++++++++++++------- net/vhost-user.c | 3 ++- net/vhost-vdpa.c | 4 +++- 9 files changed, 24 insertions(+), 47 deletions(-) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index cb87056397..db8b97b753 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -649,41 +649,18 @@ void vhost_net_config_mask(VHostNetState *net, VirtIODevice *dev, bool mask) { vhost_config_mask(&net->dev, dev, mask); } + VHostNetState *get_vhost_net(NetClientState *nc) { - VHostNetState *vhost_net = 0; - if (!nc) { return 0; } - switch (nc->info->type) { - case NET_CLIENT_DRIVER_TAP: - vhost_net = tap_get_vhost_net(nc); - /* - * tap_get_vhost_net() can return NULL if a tap net-device backend is - * created with 'vhost=off' option, 'vhostforce=off' or no vhost or - * vhostforce or vhostfd options at all. Please see net_init_tap_one(). - * Hence, we omit the assertion here. - */ - break; -#ifdef CONFIG_VHOST_NET_USER - case NET_CLIENT_DRIVER_VHOST_USER: - vhost_net = vhost_user_get_vhost_net(nc); - assert(vhost_net); - break; -#endif -#ifdef CONFIG_VHOST_NET_VDPA - case NET_CLIENT_DRIVER_VHOST_VDPA: - vhost_net = vhost_vdpa_get_vhost_net(nc); - assert(vhost_net); - break; -#endif - default: - break; + if (nc->info->get_vhost_net) { + return nc->info->get_vhost_net(nc); } - return vhost_net; + return NULL; } int vhost_net_set_vring_enable(NetClientState *nc, int enable) diff --git a/include/net/net.h b/include/net/net.h index ac59b593ba..e67b375626 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -67,6 +67,7 @@ typedef void (SocketReadStateFinalize)(SocketReadState *rs); typedef void (NetAnnounce)(NetClientState *); typedef bool (SetSteeringEBPF)(NetClientState *, int); typedef bool (NetCheckPeerType)(NetClientState *, ObjectClass *, Error **); +typedef struct vhost_net *(GetVHostNet)(NetClientState *nc); typedef struct NetClientInfo { NetClientDriver type; @@ -92,6 +93,7 @@ typedef struct NetClientInfo { NetAnnounce *announce; SetSteeringEBPF *set_steering_ebpf; NetCheckPeerType *check_peer_type; + GetVHostNet *get_vhost_net; } NetClientInfo; struct NetClientState { diff --git a/include/net/tap.h b/include/net/tap.h index 5d585515f9..6f34f13eae 100644 --- a/include/net/tap.h +++ b/include/net/tap.h @@ -33,7 +33,4 @@ int tap_disable(NetClientState *nc); int tap_get_fd(NetClientState *nc); -struct vhost_net; -struct vhost_net *tap_get_vhost_net(NetClientState *nc); - #endif /* QEMU_NET_TAP_H */ diff --git a/include/net/vhost-user.h b/include/net/vhost-user.h index 35bf619709..0b233a2673 100644 --- a/include/net/vhost-user.h +++ b/include/net/vhost-user.h @@ -12,7 +12,6 @@ #define VHOST_USER_H struct vhost_net; -struct vhost_net *vhost_user_get_vhost_net(NetClientState *nc); uint64_t vhost_user_get_acked_features(NetClientState *nc); void vhost_user_save_acked_features(NetClientState *nc); diff --git a/include/net/vhost-vdpa.h b/include/net/vhost-vdpa.h index b81f9a6f2a..916ead3793 100644 --- a/include/net/vhost-vdpa.h +++ b/include/net/vhost-vdpa.h @@ -14,8 +14,6 @@ #define TYPE_VHOST_VDPA "vhost-vdpa" -struct vhost_net *vhost_vdpa_get_vhost_net(NetClientState *nc); - extern const int vdpa_feature_bits[]; #endif /* VHOST_VDPA_H */ diff --git a/net/tap-win32.c b/net/tap-win32.c index 671dee970f..38baf90e0b 100644 --- a/net/tap-win32.c +++ b/net/tap-win32.c @@ -704,11 +704,6 @@ static void tap_win32_send(void *opaque) } } -struct vhost_net *tap_get_vhost_net(NetClientState *nc) -{ - return NULL; -} - static NetClientInfo net_tap_win32_info = { .type = NET_CLIENT_DRIVER_TAP, .size = sizeof(TAPState), diff --git a/net/tap.c b/net/tap.c index ae1c7e3983..4beba6d7a7 100644 --- a/net/tap.c +++ b/net/tap.c @@ -329,6 +329,18 @@ int tap_get_fd(NetClientState *nc) return s->fd; } +/* + * tap_get_vhost_net() can return NULL if a tap net-device backend is + * created with 'vhost=off' option, 'vhostforce=off' or no vhost or + * vhostforce or vhostfd options at all. Please see net_init_tap_one(). + */ +static VHostNetState *tap_get_vhost_net(NetClientState *nc) +{ + TAPState *s = DO_UPCAST(TAPState, nc, nc); + assert(nc->info->type == NET_CLIENT_DRIVER_TAP); + return s->vhost_net; +} + /* fd support */ static NetClientInfo net_tap_info = { @@ -347,6 +359,7 @@ static NetClientInfo net_tap_info = { .set_vnet_le = tap_set_vnet_le, .set_vnet_be = tap_set_vnet_be, .set_steering_ebpf = tap_set_steering_ebpf, + .get_vhost_net = tap_get_vhost_net, }; static TAPState *net_tap_fd_init(NetClientState *peer, @@ -980,13 +993,6 @@ int net_init_tap(const Netdev *netdev, const char *name, return 0; } -VHostNetState *tap_get_vhost_net(NetClientState *nc) -{ - TAPState *s = DO_UPCAST(TAPState, nc, nc); - assert(nc->info->type == NET_CLIENT_DRIVER_TAP); - return s->vhost_net; -} - int tap_enable(NetClientState *nc) { TAPState *s = DO_UPCAST(TAPState, nc, nc); diff --git a/net/vhost-user.c b/net/vhost-user.c index 10ac8dc0b3..b7bf0d2042 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -32,7 +32,7 @@ typedef struct NetVhostUserState { bool started; } NetVhostUserState; -VHostNetState *vhost_user_get_vhost_net(NetClientState *nc) +static struct vhost_net *vhost_user_get_vhost_net(NetClientState *nc) { NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); @@ -231,6 +231,7 @@ static NetClientInfo net_vhost_user_info = { .set_vnet_be = vhost_user_set_vnet_endianness, .set_vnet_le = vhost_user_set_vnet_endianness, .check_peer_type = vhost_user_check_peer_type, + .get_vhost_net = vhost_user_get_vhost_net, }; static gboolean net_vhost_user_watch(void *do_not_use, GIOCondition cond, diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 58d738945d..0b86c917ed 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -132,7 +132,7 @@ static const uint64_t vdpa_svq_device_features = #define VHOST_VDPA_NET_CVQ_ASID 1 -VHostNetState *vhost_vdpa_get_vhost_net(NetClientState *nc) +static struct vhost_net *vhost_vdpa_get_vhost_net(NetClientState *nc) { VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); @@ -432,6 +432,7 @@ static NetClientInfo net_vhost_vdpa_info = { .set_vnet_le = vhost_vdpa_set_vnet_le, .check_peer_type = vhost_vdpa_check_peer_type, .set_steering_ebpf = vhost_vdpa_set_steering_ebpf, + .get_vhost_net = vhost_vdpa_get_vhost_net, }; static int64_t vhost_vdpa_get_vring_group(int device_fd, unsigned vq_index, @@ -1287,6 +1288,7 @@ static NetClientInfo net_vhost_vdpa_cvq_info = { .has_ufo = vhost_vdpa_has_ufo, .check_peer_type = vhost_vdpa_check_peer_type, .set_steering_ebpf = vhost_vdpa_set_steering_ebpf, + .get_vhost_net = vhost_vdpa_get_vhost_net, }; /* From effdacbf28d76ca0eec9086539649e547e510bbc Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:19 +0200 Subject: [PATCH 2224/2760] net: Consolidate vhost feature bits into vhost_net structure Previously, the vhost_net_get_feature_bits() function in hw/net/vhost_net.c used a large switch statement to determine the appropriate feature bits based on the NetClientDriver type. This created unnecessary coupling between the generic vhost layer and specific network backends (like TAP, vhost-user, and vhost-vdpa). This patch moves the definition of vhost feature bits directly into the vhost_net structure for each relevant network client. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- hw/net/vhost_net.c | 90 ++------------------------------------- include/hw/virtio/vhost.h | 1 + include/net/vhost-vdpa.h | 2 - include/net/vhost_net.h | 1 + net/tap.c | 19 +++++++++ net/vhost-user.c | 43 +++++++++++++++++++ net/vhost-vdpa.c | 3 +- 7 files changed, 69 insertions(+), 90 deletions(-) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index db8b97b753..787c769ccc 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -36,94 +36,9 @@ #include "hw/virtio/virtio-bus.h" #include "linux-headers/linux/vhost.h" - -/* Features supported by host kernel. */ -static const int kernel_feature_bits[] = { - VIRTIO_F_NOTIFY_ON_EMPTY, - VIRTIO_RING_F_INDIRECT_DESC, - VIRTIO_RING_F_EVENT_IDX, - VIRTIO_NET_F_MRG_RXBUF, - VIRTIO_F_VERSION_1, - VIRTIO_NET_F_MTU, - VIRTIO_F_IOMMU_PLATFORM, - VIRTIO_F_RING_PACKED, - VIRTIO_F_RING_RESET, - VIRTIO_F_IN_ORDER, - VIRTIO_F_NOTIFICATION_DATA, - VIRTIO_NET_F_RSC_EXT, - VIRTIO_NET_F_HASH_REPORT, - VHOST_INVALID_FEATURE_BIT -}; - -/* Features supported by others. */ -static const int user_feature_bits[] = { - VIRTIO_F_NOTIFY_ON_EMPTY, - VIRTIO_F_NOTIFICATION_DATA, - VIRTIO_RING_F_INDIRECT_DESC, - VIRTIO_RING_F_EVENT_IDX, - - VIRTIO_F_ANY_LAYOUT, - VIRTIO_F_VERSION_1, - VIRTIO_NET_F_CSUM, - VIRTIO_NET_F_GUEST_CSUM, - VIRTIO_NET_F_GSO, - VIRTIO_NET_F_GUEST_TSO4, - VIRTIO_NET_F_GUEST_TSO6, - VIRTIO_NET_F_GUEST_ECN, - VIRTIO_NET_F_GUEST_UFO, - VIRTIO_NET_F_HOST_TSO4, - VIRTIO_NET_F_HOST_TSO6, - VIRTIO_NET_F_HOST_ECN, - VIRTIO_NET_F_HOST_UFO, - VIRTIO_NET_F_MRG_RXBUF, - VIRTIO_NET_F_MTU, - VIRTIO_F_IOMMU_PLATFORM, - VIRTIO_F_RING_PACKED, - VIRTIO_F_RING_RESET, - VIRTIO_F_IN_ORDER, - VIRTIO_NET_F_RSS, - VIRTIO_NET_F_RSC_EXT, - VIRTIO_NET_F_HASH_REPORT, - VIRTIO_NET_F_GUEST_USO4, - VIRTIO_NET_F_GUEST_USO6, - VIRTIO_NET_F_HOST_USO, - - /* This bit implies RARP isn't sent by QEMU out of band */ - VIRTIO_NET_F_GUEST_ANNOUNCE, - - VIRTIO_NET_F_MQ, - - VHOST_INVALID_FEATURE_BIT -}; - -static const int *vhost_net_get_feature_bits(struct vhost_net *net) -{ - const int *feature_bits = 0; - - switch (net->nc->info->type) { - case NET_CLIENT_DRIVER_TAP: - feature_bits = kernel_feature_bits; - break; - case NET_CLIENT_DRIVER_VHOST_USER: - feature_bits = user_feature_bits; - break; -#ifdef CONFIG_VHOST_NET_VDPA - case NET_CLIENT_DRIVER_VHOST_VDPA: - feature_bits = vdpa_feature_bits; - break; -#endif - default: - error_report("Feature bits not defined for this type: %d", - net->nc->info->type); - break; - } - - return feature_bits; -} - uint64_t vhost_net_get_features(struct vhost_net *net, uint64_t features) { - return vhost_get_features(&net->dev, vhost_net_get_feature_bits(net), + return vhost_get_features(&net->dev, net->feature_bits, features); } int vhost_net_get_config(struct vhost_net *net, uint8_t *config, @@ -140,7 +55,7 @@ int vhost_net_set_config(struct vhost_net *net, const uint8_t *data, void vhost_net_ack_features(struct vhost_net *net, uint64_t features) { net->dev.acked_features = net->dev.backend_features; - vhost_ack_features(&net->dev, vhost_net_get_feature_bits(net), features); + vhost_ack_features(&net->dev, net->feature_bits, features); } uint64_t vhost_net_get_max_queues(VHostNetState *net) @@ -329,6 +244,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) } net->nc = options->net_backend; net->dev.nvqs = options->nvqs; + net->feature_bits = options->feature_bits; net->dev.max_queues = 1; net->dev.vqs = net->vqs; diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 38800a7156..6a75fdc021 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -143,6 +143,7 @@ struct vhost_net { struct vhost_dev dev; struct vhost_virtqueue vqs[2]; int backend; + const int *feature_bits; NetClientState *nc; }; diff --git a/include/net/vhost-vdpa.h b/include/net/vhost-vdpa.h index 916ead3793..f8d7d6c904 100644 --- a/include/net/vhost-vdpa.h +++ b/include/net/vhost-vdpa.h @@ -14,6 +14,4 @@ #define TYPE_VHOST_VDPA "vhost-vdpa" -extern const int vdpa_feature_bits[]; - #endif /* VHOST_VDPA_H */ diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index 0f40049f34..fbed37385a 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -12,6 +12,7 @@ typedef struct VhostNetOptions { NetClientState *net_backend; uint32_t busyloop_timeout; unsigned int nvqs; + const int *feature_bits; void *opaque; } VhostNetOptions; diff --git a/net/tap.c b/net/tap.c index 4beba6d7a7..a33eb23212 100644 --- a/net/tap.c +++ b/net/tap.c @@ -42,11 +42,29 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/sockets.h" +#include "hw/virtio/vhost.h" #include "net/tap.h" #include "net/vhost_net.h" +static const int kernel_feature_bits[] = { + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_F_VERSION_1, + VIRTIO_NET_F_MTU, + VIRTIO_F_IOMMU_PLATFORM, + VIRTIO_F_RING_PACKED, + VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, + VIRTIO_F_NOTIFICATION_DATA, + VIRTIO_NET_F_RSC_EXT, + VIRTIO_NET_F_HASH_REPORT, + VHOST_INVALID_FEATURE_BIT +}; + typedef struct TAPState { NetClientState nc; int fd; @@ -725,6 +743,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, } options.opaque = (void *)(uintptr_t)vhostfd; options.nvqs = 2; + options.feature_bits = kernel_feature_bits; s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { diff --git a/net/vhost-user.c b/net/vhost-user.c index b7bf0d2042..bc8e82a092 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -12,7 +12,9 @@ #include "clients.h" #include "net/vhost_net.h" #include "net/vhost-user.h" +#include "hw/virtio/vhost.h" #include "hw/virtio/vhost-user.h" +#include "standard-headers/linux/virtio_net.h" #include "chardev/char-fe.h" #include "qapi/error.h" #include "qapi/qapi-commands-net.h" @@ -22,6 +24,46 @@ #include "qemu/option.h" #include "trace.h" +static const int user_feature_bits[] = { + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_F_NOTIFICATION_DATA, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + + VIRTIO_F_ANY_LAYOUT, + VIRTIO_F_VERSION_1, + VIRTIO_NET_F_CSUM, + VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GSO, + VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_ECN, + VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, + VIRTIO_NET_F_HOST_TSO6, + VIRTIO_NET_F_HOST_ECN, + VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_NET_F_MTU, + VIRTIO_F_IOMMU_PLATFORM, + VIRTIO_F_RING_PACKED, + VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, + VIRTIO_NET_F_RSS, + VIRTIO_NET_F_RSC_EXT, + VIRTIO_NET_F_HASH_REPORT, + VIRTIO_NET_F_GUEST_USO4, + VIRTIO_NET_F_GUEST_USO6, + VIRTIO_NET_F_HOST_USO, + + /* This bit implies RARP isn't sent by QEMU out of band */ + VIRTIO_NET_F_GUEST_ANNOUNCE, + + VIRTIO_NET_F_MQ, + + VHOST_INVALID_FEATURE_BIT +}; + typedef struct NetVhostUserState { NetClientState nc; CharBackend chr; /* only queue index 0 */ @@ -96,6 +138,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], options.opaque = be; options.busyloop_timeout = 0; options.nvqs = 2; + options.feature_bits = user_feature_bits; net = vhost_net_init(&options); if (!net) { error_report("failed to init vhost_net for queue %d", i); diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 0b86c917ed..cbbea0eb71 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -55,7 +55,7 @@ typedef struct VhostVDPAState { * with the exception of VHOST_INVALID_FEATURE_BIT, * which should always be the last entry. */ -const int vdpa_feature_bits[] = { +static const int vdpa_feature_bits[] = { VIRTIO_F_ANY_LAYOUT, VIRTIO_F_IOMMU_PLATFORM, VIRTIO_F_NOTIFY_ON_EMPTY, @@ -201,6 +201,7 @@ static int vhost_vdpa_add(NetClientState *ncs, void *be, options.opaque = be; options.busyloop_timeout = 0; options.nvqs = nvqs; + options.feature_bits = vdpa_feature_bits; net = vhost_net_init(&options); if (!net) { From bd38794a1119ec8e3f0a7473458ce4cdd229bc42 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:20 +0200 Subject: [PATCH 2225/2760] net: Add get_acked_features callback to VhostNetOptions This patch continues the effort to decouple the generic vhost layer from specific network backend implementations. Previously, the vhost_net initialization code contained a hardcoded check for the vhost-user client type to retrieve its acked features by calling vhost_user_get_acked_features(). This exposed an internal vhost-user function in a public header and coupled the two modules. The vhost-user backend is updated to provide a callback, and its getter function is now static. The call site in vhost_net.c is simplified to use the new generic helper, removing the type check and the direct dependency. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- hw/net/vhost_net.c | 6 ++---- include/net/vhost-user.h | 2 -- include/net/vhost_net.h | 3 +++ net/tap.c | 1 + net/vhost-user.c | 4 +++- net/vhost-vdpa.c | 1 + 6 files changed, 10 insertions(+), 7 deletions(-) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 787c769ccc..fb169af0e8 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -288,9 +288,8 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) } /* Set sane init value. Override when guest acks. */ -#ifdef CONFIG_VHOST_NET_USER - if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) { - features = vhost_user_get_acked_features(net->nc); + if (options->get_acked_features) { + features = options->get_acked_features(net->nc); if (~net->dev.features & features) { fprintf(stderr, "vhost lacks feature mask 0x%" PRIx64 " for backend\n", @@ -298,7 +297,6 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) goto fail; } } -#endif vhost_net_ack_features(net, features); diff --git a/include/net/vhost-user.h b/include/net/vhost-user.h index 0b233a2673..a4d0ce4b8d 100644 --- a/include/net/vhost-user.h +++ b/include/net/vhost-user.h @@ -11,8 +11,6 @@ #ifndef VHOST_USER_H #define VHOST_USER_H -struct vhost_net; -uint64_t vhost_user_get_acked_features(NetClientState *nc); void vhost_user_save_acked_features(NetClientState *nc); #endif /* VHOST_USER_H */ diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index fbed37385a..a8d281c8f7 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -7,12 +7,15 @@ struct vhost_net; typedef struct vhost_net VHostNetState; +typedef uint64_t (GetAckedFeatures)(NetClientState *nc); + typedef struct VhostNetOptions { VhostBackendType backend_type; NetClientState *net_backend; uint32_t busyloop_timeout; unsigned int nvqs; const int *feature_bits; + GetAckedFeatures *get_acked_features; void *opaque; } VhostNetOptions; diff --git a/net/tap.c b/net/tap.c index a33eb23212..acd77f816f 100644 --- a/net/tap.c +++ b/net/tap.c @@ -744,6 +744,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, options.opaque = (void *)(uintptr_t)vhostfd; options.nvqs = 2; options.feature_bits = kernel_feature_bits; + options.get_acked_features = NULL; s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { diff --git a/net/vhost-user.c b/net/vhost-user.c index bc8e82a092..93b413b49f 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -81,7 +81,7 @@ static struct vhost_net *vhost_user_get_vhost_net(NetClientState *nc) return s->vhost_net; } -uint64_t vhost_user_get_acked_features(NetClientState *nc) +static uint64_t vhost_user_get_acked_features(NetClientState *nc) { NetVhostUserState *s = DO_UPCAST(NetVhostUserState, nc, nc); assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_USER); @@ -139,6 +139,8 @@ static int vhost_user_start(int queues, NetClientState *ncs[], options.busyloop_timeout = 0; options.nvqs = 2; options.feature_bits = user_feature_bits; + options.get_acked_features = vhost_user_get_acked_features; + net = vhost_net_init(&options); if (!net) { error_report("failed to init vhost_net for queue %d", i); diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index cbbea0eb71..a3980d1fb5 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -202,6 +202,7 @@ static int vhost_vdpa_add(NetClientState *ncs, void *be, options.busyloop_timeout = 0; options.nvqs = nvqs; options.feature_bits = vdpa_feature_bits; + options.get_acked_features = NULL; net = vhost_net_init(&options); if (!net) { From 1652f1b335fb5bec921c64ff7f378e6732510ca4 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:21 +0200 Subject: [PATCH 2226/2760] net: Add save_acked_features callback to vhost_net This commit introduces a save_acked_features function pointer to vhost_net and converts the vhost_net function into a generic dispatcher. The vhost-user backend provides the callback, making its function static. With this change, no other module has a direct dependency on the vhost-user implementation. This cleanup allows for the complete removal of the net/vhost-user.h header file. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- hw/net/vhost_net-stub.c | 1 - hw/net/vhost_net.c | 10 +++++----- include/hw/virtio/vhost.h | 2 ++ include/net/vhost-user.h | 16 ---------------- include/net/vhost_net.h | 2 ++ net/tap.c | 1 + net/vhost-user-stub.c | 1 - net/vhost-user.c | 4 ++-- net/vhost-vdpa.c | 1 + 9 files changed, 13 insertions(+), 25 deletions(-) delete mode 100644 include/net/vhost-user.h diff --git a/hw/net/vhost_net-stub.c b/hw/net/vhost_net-stub.c index 7bed0bf92b..7d49f82906 100644 --- a/hw/net/vhost_net-stub.c +++ b/hw/net/vhost_net-stub.c @@ -13,7 +13,6 @@ #include "qemu/osdep.h" #include "net/net.h" #include "net/tap.h" -#include "net/vhost-user.h" #include "hw/virtio/virtio-net.h" #include "net/vhost_net.h" diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index fb169af0e8..976d2b315a 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -16,7 +16,6 @@ #include "qemu/osdep.h" #include "net/net.h" #include "net/tap.h" -#include "net/vhost-user.h" #include "net/vhost-vdpa.h" #include "standard-headers/linux/vhost_types.h" @@ -70,11 +69,11 @@ uint64_t vhost_net_get_acked_features(VHostNetState *net) void vhost_net_save_acked_features(NetClientState *nc) { -#ifdef CONFIG_VHOST_NET_USER - if (nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) { - vhost_user_save_acked_features(nc); + struct vhost_net *net = get_vhost_net(nc); + + if (net && net->save_acked_features) { + net->save_acked_features(nc); } -#endif } static void vhost_net_disable_notifiers_nvhosts(VirtIODevice *dev, @@ -245,6 +244,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->nc = options->net_backend; net->dev.nvqs = options->nvqs; net->feature_bits = options->feature_bits; + net->save_acked_features = options->save_acked_features; net->dev.max_queues = 1; net->dev.vqs = net->vqs; diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 6a75fdc021..b0830bac79 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -1,6 +1,7 @@ #ifndef VHOST_H #define VHOST_H +#include "net/vhost_net.h" #include "hw/virtio/vhost-backend.h" #include "hw/virtio/virtio.h" #include "system/memory.h" @@ -144,6 +145,7 @@ struct vhost_net { struct vhost_virtqueue vqs[2]; int backend; const int *feature_bits; + SaveAcketFeatures *save_acked_features; NetClientState *nc; }; diff --git a/include/net/vhost-user.h b/include/net/vhost-user.h deleted file mode 100644 index a4d0ce4b8d..0000000000 --- a/include/net/vhost-user.h +++ /dev/null @@ -1,16 +0,0 @@ -/* - * vhost-user.h - * - * Copyright (c) 2013 Virtual Open Systems Sarl. - * - * This work is licensed under the terms of the GNU GPL, version 2 or later. - * See the COPYING file in the top-level directory. - * - */ - -#ifndef VHOST_USER_H -#define VHOST_USER_H - -void vhost_user_save_acked_features(NetClientState *nc); - -#endif /* VHOST_USER_H */ diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index a8d281c8f7..eb26ed9bdc 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -8,6 +8,7 @@ struct vhost_net; typedef struct vhost_net VHostNetState; typedef uint64_t (GetAckedFeatures)(NetClientState *nc); +typedef void (SaveAcketFeatures)(NetClientState *nc); typedef struct VhostNetOptions { VhostBackendType backend_type; @@ -16,6 +17,7 @@ typedef struct VhostNetOptions { unsigned int nvqs; const int *feature_bits; GetAckedFeatures *get_acked_features; + SaveAcketFeatures *save_acked_features; void *opaque; } VhostNetOptions; diff --git a/net/tap.c b/net/tap.c index acd77f816f..79fa02a65c 100644 --- a/net/tap.c +++ b/net/tap.c @@ -745,6 +745,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, options.nvqs = 2; options.feature_bits = kernel_feature_bits; options.get_acked_features = NULL; + options.save_acked_features = NULL; s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { diff --git a/net/vhost-user-stub.c b/net/vhost-user-stub.c index 52ab4e13f1..283dee87db 100644 --- a/net/vhost-user-stub.c +++ b/net/vhost-user-stub.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "clients.h" #include "net/vhost_net.h" -#include "net/vhost-user.h" #include "qemu/error-report.h" #include "qapi/error.h" diff --git a/net/vhost-user.c b/net/vhost-user.c index 93b413b49f..8a3df27b02 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "clients.h" #include "net/vhost_net.h" -#include "net/vhost-user.h" #include "hw/virtio/vhost.h" #include "hw/virtio/vhost-user.h" #include "standard-headers/linux/virtio_net.h" @@ -88,7 +87,7 @@ static uint64_t vhost_user_get_acked_features(NetClientState *nc) return s->acked_features; } -void vhost_user_save_acked_features(NetClientState *nc) +static void vhost_user_save_acked_features(NetClientState *nc) { NetVhostUserState *s; @@ -140,6 +139,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], options.nvqs = 2; options.feature_bits = user_feature_bits; options.get_acked_features = vhost_user_get_acked_features; + options.save_acked_features = vhost_user_save_acked_features; net = vhost_net_init(&options); if (!net) { diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index a3980d1fb5..c63225d3d2 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -203,6 +203,7 @@ static int vhost_vdpa_add(NetClientState *ncs, void *be, options.nvqs = nvqs; options.feature_bits = vdpa_feature_bits; options.get_acked_features = NULL; + options.save_acked_features = NULL; net = vhost_net_init(&options); if (!net) { From 33b78a30a3e8e1cf16ef423bf2e78caf3d560985 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:22 +0200 Subject: [PATCH 2227/2760] net: Allow network backends to advertise max TX queue size This commit refactors how the maximum transmit queue size for virtio-net devices is determined, making the mechanism more generic and extensible. Previously, virtio_net_max_tx_queue_size() contained hardcoded checks for specific network backend types (vhost-user and vhost-vdpa) to determine their supported maximum queue size. This created direct dependencies and would require modifications for every new backend that supports variable queue sizes. To improve flexibility, a new max_tx_queue_size field is added to the vhost_net structure. This allows each network backend to advertise its supported maximum transmit queue size directly. The virtio_net_max_tx_queue_size() function now retrieves the max TX queue size from the vhost_net struct, if available and set. Otherwise, it defaults to VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- hw/net/vhost_net.c | 1 + hw/net/virtio-net.c | 24 ++++++++++++------------ include/hw/virtio/vhost.h | 1 + include/net/vhost_net.h | 1 + net/tap.c | 1 + net/vhost-user.c | 1 + net/vhost-vdpa.c | 1 + 7 files changed, 18 insertions(+), 12 deletions(-) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 976d2b315a..74d2e3ed90 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -245,6 +245,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->dev.nvqs = options->nvqs; net->feature_bits = options->feature_bits; net->save_acked_features = options->save_acked_features; + net->max_tx_queue_size = options->max_tx_queue_size; net->dev.max_queues = 1; net->dev.vqs = net->vqs; diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index e3400f18c8..39fc280839 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -670,22 +670,22 @@ static void virtio_net_set_mrg_rx_bufs(VirtIONet *n, int mergeable_rx_bufs, static int virtio_net_max_tx_queue_size(VirtIONet *n) { NetClientState *peer = n->nic_conf.peers.ncs[0]; + struct vhost_net *net; - /* - * Backends other than vhost-user or vhost-vdpa don't support max queue - * size. - */ if (!peer) { - return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; + goto default_value; } - switch(peer->info->type) { - case NET_CLIENT_DRIVER_VHOST_USER: - case NET_CLIENT_DRIVER_VHOST_VDPA: - return VIRTQUEUE_MAX_SIZE; - default: - return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; - }; + net = get_vhost_net(peer); + + if (!net || !net->max_tx_queue_size) { + goto default_value; + } + + return net->max_tx_queue_size; + +default_value: + return VIRTIO_NET_TX_QUEUE_DEFAULT_SIZE; } static int peer_attach(VirtIONet *n, int index) diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index b0830bac79..a62992c819 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -145,6 +145,7 @@ struct vhost_net { struct vhost_virtqueue vqs[2]; int backend; const int *feature_bits; + int max_tx_queue_size; SaveAcketFeatures *save_acked_features; NetClientState *nc; }; diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index eb26ed9bdc..8f4fddfb69 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -16,6 +16,7 @@ typedef struct VhostNetOptions { uint32_t busyloop_timeout; unsigned int nvqs; const int *feature_bits; + int max_tx_queue_size; GetAckedFeatures *get_acked_features; SaveAcketFeatures *save_acked_features; void *opaque; diff --git a/net/tap.c b/net/tap.c index 79fa02a65c..2f0cb55c9a 100644 --- a/net/tap.c +++ b/net/tap.c @@ -746,6 +746,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, options.feature_bits = kernel_feature_bits; options.get_acked_features = NULL; options.save_acked_features = NULL; + options.max_tx_queue_size = 0; s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { diff --git a/net/vhost-user.c b/net/vhost-user.c index 8a3df27b02..bf892915de 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -138,6 +138,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], options.busyloop_timeout = 0; options.nvqs = 2; options.feature_bits = user_feature_bits; + options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; options.get_acked_features = vhost_user_get_acked_features; options.save_acked_features = vhost_user_save_acked_features; diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index c63225d3d2..353392b3d7 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -204,6 +204,7 @@ static int vhost_vdpa_add(NetClientState *ncs, void *be, options.feature_bits = vdpa_feature_bits; options.get_acked_features = NULL; options.save_acked_features = NULL; + options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; net = vhost_net_init(&options); if (!net) { From ba5acc5d6e0bc9ab86619c92cb76493aa197d7a2 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:23 +0200 Subject: [PATCH 2228/2760] net: Add is_vhost_user flag to vhost_net struct Introduce a boolean is_vhost_user field to the vhost_net structure. This flag is initialized during vhost_net_init based on whether the backend is vhost-user. This refactoring simplifies checks for vhost-user specific behavior, replacing direct comparisons of 'net->nc->info->type' with the new flag. It improves readability and encapsulates the backend type information directly within the vhost_net instance. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- hw/net/vhost_net.c | 3 ++- hw/net/virtio-net.c | 8 ++++++-- include/hw/virtio/vhost.h | 1 + include/net/vhost_net.h | 1 + net/tap.c | 1 + net/vhost-user.c | 1 + net/vhost-vdpa.c | 1 + 7 files changed, 13 insertions(+), 3 deletions(-) diff --git a/hw/net/vhost_net.c b/hw/net/vhost_net.c index 74d2e3ed90..540492b37d 100644 --- a/hw/net/vhost_net.c +++ b/hw/net/vhost_net.c @@ -246,6 +246,7 @@ struct vhost_net *vhost_net_init(VhostNetOptions *options) net->feature_bits = options->feature_bits; net->save_acked_features = options->save_acked_features; net->max_tx_queue_size = options->max_tx_queue_size; + net->is_vhost_user = options->is_vhost_user; net->dev.max_queues = 1; net->dev.vqs = net->vqs; @@ -440,7 +441,7 @@ int vhost_net_start(VirtIODevice *dev, NetClientState *ncs, * because vhost user doesn't interrupt masking/unmasking * properly. */ - if (net->nc->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + if (net->is_vhost_user) { dev->use_guest_notifier_mask = false; } } diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 39fc280839..00df5fd6cd 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -691,12 +691,14 @@ static int virtio_net_max_tx_queue_size(VirtIONet *n) static int peer_attach(VirtIONet *n, int index) { NetClientState *nc = qemu_get_subqueue(n->nic, index); + struct vhost_net *net; if (!nc->peer) { return 0; } - if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + net = get_vhost_net(nc->peer); + if (net && net->is_vhost_user) { vhost_net_set_vring_enable(nc->peer, 1); } @@ -714,12 +716,14 @@ static int peer_attach(VirtIONet *n, int index) static int peer_detach(VirtIONet *n, int index) { NetClientState *nc = qemu_get_subqueue(n->nic, index); + struct vhost_net *net; if (!nc->peer) { return 0; } - if (nc->peer->info->type == NET_CLIENT_DRIVER_VHOST_USER) { + net = get_vhost_net(nc->peer); + if (net && net->is_vhost_user) { vhost_net_set_vring_enable(nc->peer, 0); } diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index a62992c819..f178cf9e1d 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -147,6 +147,7 @@ struct vhost_net { const int *feature_bits; int max_tx_queue_size; SaveAcketFeatures *save_acked_features; + bool is_vhost_user; NetClientState *nc; }; diff --git a/include/net/vhost_net.h b/include/net/vhost_net.h index 8f4fddfb69..879781dad7 100644 --- a/include/net/vhost_net.h +++ b/include/net/vhost_net.h @@ -17,6 +17,7 @@ typedef struct VhostNetOptions { unsigned int nvqs; const int *feature_bits; int max_tx_queue_size; + bool is_vhost_user; GetAckedFeatures *get_acked_features; SaveAcketFeatures *save_acked_features; void *opaque; diff --git a/net/tap.c b/net/tap.c index 2f0cb55c9a..23536c09b4 100644 --- a/net/tap.c +++ b/net/tap.c @@ -747,6 +747,7 @@ static void net_init_tap_one(const NetdevTapOptions *tap, NetClientState *peer, options.get_acked_features = NULL; options.save_acked_features = NULL; options.max_tx_queue_size = 0; + options.is_vhost_user = false; s->vhost_net = vhost_net_init(&options); if (!s->vhost_net) { diff --git a/net/vhost-user.c b/net/vhost-user.c index bf892915de..1c3b8b36f3 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -141,6 +141,7 @@ static int vhost_user_start(int queues, NetClientState *ncs[], options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; options.get_acked_features = vhost_user_get_acked_features; options.save_acked_features = vhost_user_save_acked_features; + options.is_vhost_user = true; net = vhost_net_init(&options); if (!net) { diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 353392b3d7..943e9c585c 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -205,6 +205,7 @@ static int vhost_vdpa_add(NetClientState *ncs, void *be, options.get_acked_features = NULL; options.save_acked_features = NULL; options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; + options.is_vhost_user = false; net = vhost_net_init(&options); if (!net) { From 854ee02b22220377f3fa3806adf7e0718c3a5c5a Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:24 +0200 Subject: [PATCH 2229/2760] net: Add passt network backend This commit introduces support for passt as a new network backend. passt is an unprivileged, user-mode networking solution that provides connectivity for virtual machines by launching an external helper process. The implementation reuses the generic stream data handling logic. It launches the passt binary using GSubprocess, passing it a file descriptor from a socketpair() for communication. QEMU connects to the other end of the socket pair to establish the network data stream. The PID of the passt daemon is tracked via a temporary file to ensure it is terminated when QEMU exits. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- docs/system/devices/net.rst | 40 +++- hmp-commands.hx | 3 + meson.build | 6 + meson_options.txt | 2 + net/clients.h | 4 + net/hub.c | 3 + net/meson.build | 3 + net/net.c | 4 + net/passt.c | 407 ++++++++++++++++++++++++++++++++++ qapi/net.json | 115 ++++++++++ qemu-options.hx | 145 +++++++++++- scripts/meson-buildoptions.sh | 3 + 12 files changed, 731 insertions(+), 4 deletions(-) create mode 100644 net/passt.c diff --git a/docs/system/devices/net.rst b/docs/system/devices/net.rst index a3efbdcabd..c586ee0f40 100644 --- a/docs/system/devices/net.rst +++ b/docs/system/devices/net.rst @@ -85,13 +85,49 @@ passt doesn't require any capability or privilege. passt has better performance than ``-net user``, full IPv6 support and better security as it's a daemon that is not executed in QEMU context. -passt can be connected to QEMU either by using a socket -(``-netdev stream``) or using the vhost-user interface (``-netdev vhost-user``). +passt_ can be used in the same way as the user backend (using ``-net passt``, +``-netdev passt`` or ``-nic passt``) or it can be launched manually and +connected to QEMU either by using a socket (``-netdev stream``) or by using +the vhost-user interface (``-netdev vhost-user``). + +Using ``-netdev stream`` or ``-netdev vhost-user`` will allow the user to +enable functionalities not available through the passt backend interface +(like migration). + See `passt(1)`_ for more details on passt. .. _passt: https://passt.top/ .. _passt(1): https://passt.top/builds/latest/web/passt.1.html +To use the passt backend interface +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +There is no need to start the daemon as QEMU will do it for you. + +passt is started in the socket-based mode. + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -nic passt + + (qemu) info network + e1000e.0: index=0,type=nic,model=e1000e,macaddr=52:54:00:12:34:56 + \ #net071: index=0,type=passt,stream,connected to pid 24846 + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -net nic -net passt,tcp-ports=10001,udp-ports=10001 + + (qemu) info network + hub 0 + \ hub0port1: #net136: index=0,type=passt,stream,connected to pid 25204 + \ hub0port0: e1000e.0: index=0,type=nic,model=e1000e,macaddr=52:54:00:12:34:56 + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -netdev passt,id=netdev0 -device virtio-net,mac=9a:2b:2c:2d:2e:2f,id=virtio0,netdev=netdev0 + + (qemu) info network + virtio0: index=0,type=nic,model=virtio-net-pci,macaddr=9a:2b:2c:2d:2e:2f + \ netdev0: index=0,type=passt,stream,connected to pid 25428 + To use socket based passt interface: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/hmp-commands.hx b/hmp-commands.hx index 06746f0afc..d0e4f35a30 100644 --- a/hmp-commands.hx +++ b/hmp-commands.hx @@ -1287,6 +1287,9 @@ ERST .name = "netdev_add", .args_type = "netdev:O", .params = "[user|tap|socket|stream|dgram|vde|bridge|hubport|netmap|vhost-user" +#ifdef CONFIG_PASST + "|passt" +#endif #ifdef CONFIG_AF_XDP "|af-xdp" #endif diff --git a/meson.build b/meson.build index b5f74aa37a..2adb22f198 100644 --- a/meson.build +++ b/meson.build @@ -1285,6 +1285,10 @@ if not get_option('slirp').auto() or have_system endif endif +enable_passt = get_option('passt') \ + .require(host_os == 'linux', error_message: 'passt is supported only on Linux') \ + .allowed() + vde = not_found if not get_option('vde').auto() or have_system or have_tools vde = cc.find_library('vdeplug', has_headers: ['libvdeplug.h'], @@ -2538,6 +2542,7 @@ if seccomp.found() config_host_data.set('CONFIG_SECCOMP_SYSRAWRC', seccomp_has_sysrawrc) endif config_host_data.set('CONFIG_PIXMAN', pixman.found()) +config_host_data.set('CONFIG_PASST', enable_passt) config_host_data.set('CONFIG_SLIRP', slirp.found()) config_host_data.set('CONFIG_SNAPPY', snappy.found()) config_host_data.set('CONFIG_SOLARIS', host_os == 'sunos') @@ -4926,6 +4931,7 @@ if host_os == 'darwin' summary_info += {'vmnet.framework support': vmnet} endif summary_info += {'AF_XDP support': libxdp} +summary_info += {'passt support': enable_passt} summary_info += {'slirp support': slirp} summary_info += {'vde support': vde} summary_info += {'netmap support': have_netmap} diff --git a/meson_options.txt b/meson_options.txt index a442be2995..3146eec194 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -234,6 +234,8 @@ option('pixman', type : 'feature', value : 'auto', description: 'pixman support') option('slirp', type: 'feature', value: 'auto', description: 'libslirp user mode network backend support') +option('passt', type: 'feature', value: 'auto', + description: 'passt network backend support') option('vde', type : 'feature', value : 'auto', description: 'vde network backend support') option('vmnet', type : 'feature', value : 'auto', diff --git a/net/clients.h b/net/clients.h index be53794582..e786ab4203 100644 --- a/net/clients.h +++ b/net/clients.h @@ -29,6 +29,10 @@ int net_init_dump(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp); +#ifdef CONFIG_PASST +int net_init_passt(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp); +#endif #ifdef CONFIG_SLIRP int net_init_slirp(const Netdev *netdev, const char *name, NetClientState *peer, Error **errp); diff --git a/net/hub.c b/net/hub.c index cba20ebd87..e3b58b1c4f 100644 --- a/net/hub.c +++ b/net/hub.c @@ -285,6 +285,9 @@ void net_hub_check_clients(void) case NET_CLIENT_DRIVER_NIC: has_nic = 1; break; +#ifdef CONFIG_PASST + case NET_CLIENT_DRIVER_PASST: +#endif case NET_CLIENT_DRIVER_USER: case NET_CLIENT_DRIVER_TAP: case NET_CLIENT_DRIVER_SOCKET: diff --git a/net/meson.build b/net/meson.build index bb3c011e5a..da6ea635e9 100644 --- a/net/meson.build +++ b/net/meson.build @@ -34,6 +34,9 @@ system_ss.add(when: 'CONFIG_TCG', if_true: files('filter-replay.c')) if have_l2tpv3 system_ss.add(files('l2tpv3.c')) endif +if enable_passt + system_ss.add(files('passt.c')) +endif system_ss.add(when: slirp, if_true: files('slirp.c')) system_ss.add(when: vde, if_true: files('vde.c')) if have_netmap diff --git a/net/net.c b/net/net.c index cfa2d8e958..90f69fdf39 100644 --- a/net/net.c +++ b/net/net.c @@ -1248,6 +1248,9 @@ static int (* const net_client_init_fun[NET_CLIENT_DRIVER__MAX])( const char *name, NetClientState *peer, Error **errp) = { [NET_CLIENT_DRIVER_NIC] = net_init_nic, +#ifdef CONFIG_PASST + [NET_CLIENT_DRIVER_PASST] = net_init_passt, +#endif #ifdef CONFIG_SLIRP [NET_CLIENT_DRIVER_USER] = net_init_slirp, #endif @@ -1353,6 +1356,7 @@ void show_netdevs(void) "dgram", "hubport", "tap", + "passt", #ifdef CONFIG_SLIRP "user", #endif diff --git a/net/passt.c b/net/passt.c new file mode 100644 index 0000000000..0a4a1ba6aa --- /dev/null +++ b/net/passt.c @@ -0,0 +1,407 @@ +/* + * passt network backend + * + * Copyright Red Hat + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#include "qemu/osdep.h" +#include +#include +#include "net/net.h" +#include "clients.h" +#include "qapi/error.h" +#include "io/net-listener.h" +#include "stream_data.h" + +typedef struct NetPasstState { + NetStreamData data; + GPtrArray *args; + gchar *pidfile; + pid_t pid; +} NetPasstState; + +static int net_passt_stream_start(NetPasstState *s, Error **errp); + +static void net_passt_cleanup(NetClientState *nc) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + + kill(s->pid, SIGTERM); + g_remove(s->pidfile); + g_free(s->pidfile); + g_ptr_array_free(s->args, TRUE); +} + +static ssize_t net_passt_receive(NetClientState *nc, const uint8_t *buf, + size_t size) +{ + NetStreamData *d = DO_UPCAST(NetStreamData, nc, nc); + + return net_stream_data_receive(d, buf, size); +} + +static gboolean net_passt_send(QIOChannel *ioc, GIOCondition condition, + gpointer data) +{ + if (net_stream_data_send(ioc, condition, data) == G_SOURCE_REMOVE) { + NetPasstState *s = DO_UPCAST(NetPasstState, data, data); + Error *error; + + /* we need to restart passt */ + kill(s->pid, SIGTERM); + if (net_passt_stream_start(s, &error) == -1) { + error_report_err(error); + } + + return G_SOURCE_REMOVE; + } + + return G_SOURCE_CONTINUE; +} + +static NetClientInfo net_passt_info = { + .type = NET_CLIENT_DRIVER_PASST, + .size = sizeof(NetPasstState), + .receive = net_passt_receive, + .cleanup = net_passt_cleanup, +}; + +static void net_passt_client_connected(QIOTask *task, gpointer opaque) +{ + NetPasstState *s = opaque; + + if (net_stream_data_client_connected(task, &s->data) == 0) { + qemu_set_info_str(&s->data.nc, "stream,connected to pid %d", s->pid); + } +} + +static int net_passt_start_daemon(NetPasstState *s, int sock, Error **errp) +{ + g_autoptr(GSubprocess) daemon = NULL; + g_autofree gchar *contents = NULL; + g_autoptr(GError) error = NULL; + GSubprocessLauncher *launcher; + + qemu_set_info_str(&s->data.nc, "launching passt"); + + launcher = g_subprocess_launcher_new(G_SUBPROCESS_FLAGS_NONE); + g_subprocess_launcher_take_fd(launcher, sock, 3); + + daemon = g_subprocess_launcher_spawnv(launcher, + (const gchar *const *)s->args->pdata, + &error); + g_object_unref(launcher); + + if (!daemon) { + error_setg(errp, "Error creating daemon: %s", error->message); + return -1; + } + + if (!g_subprocess_wait(daemon, NULL, &error)) { + error_setg(errp, "Error waiting for daemon: %s", error->message); + return -1; + } + + if (g_subprocess_get_if_exited(daemon) && + g_subprocess_get_exit_status(daemon)) { + return -1; + } + + if (!g_file_get_contents(s->pidfile, &contents, NULL, &error)) { + error_setg(errp, "Cannot read passt pid: %s", error->message); + return -1; + } + + s->pid = (pid_t)g_ascii_strtoll(contents, NULL, 10); + if (s->pid <= 0) { + error_setg(errp, "File '%s' did not contain a valid PID.", s->pidfile); + return -1; + } + + return 0; +} + +static int net_passt_stream_start(NetPasstState *s, Error **errp) +{ + QIOChannelSocket *sioc; + SocketAddress *addr; + int sv[2]; + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { + error_setg_errno(errp, errno, "socketpair() failed"); + return -1; + } + + /* connect to passt */ + qemu_set_info_str(&s->data.nc, "connecting to passt"); + + /* create socket channel */ + sioc = qio_channel_socket_new(); + s->data.ioc = QIO_CHANNEL(sioc); + s->data.nc.link_down = true; + s->data.send = net_passt_send; + + addr = g_new0(SocketAddress, 1); + addr->type = SOCKET_ADDRESS_TYPE_FD; + addr->u.fd.str = g_strdup_printf("%d", sv[0]); + + qio_channel_socket_connect_async(sioc, addr, + net_passt_client_connected, s, + NULL, NULL); + + qapi_free_SocketAddress(addr); + + /* start passt */ + if (net_passt_start_daemon(s, sv[1], errp) == -1) { + close(sv[0]); + close(sv[1]); + return -1; + } + close(sv[1]); + + return 0; +} + +static GPtrArray *net_passt_decode_args(const NetDevPasstOptions *passt, + gchar *pidfile, Error **errp) +{ + GPtrArray *args = g_ptr_array_new_with_free_func(g_free); + + if (passt->path) { + g_ptr_array_add(args, g_strdup(passt->path)); + } else { + g_ptr_array_add(args, g_strdup("passt")); + } + + /* by default, be quiet */ + if (!passt->has_quiet || passt->quiet) { + g_ptr_array_add(args, g_strdup("--quiet")); + } + + if (passt->has_mtu) { + g_ptr_array_add(args, g_strdup("--mtu")); + g_ptr_array_add(args, g_strdup_printf("%"PRId64, passt->mtu)); + } + + if (passt->address) { + g_ptr_array_add(args, g_strdup("--address")); + g_ptr_array_add(args, g_strdup(passt->address)); + } + + if (passt->netmask) { + g_ptr_array_add(args, g_strdup("--netmask")); + g_ptr_array_add(args, g_strdup(passt->netmask)); + } + + if (passt->mac) { + g_ptr_array_add(args, g_strdup("--mac-addr")); + g_ptr_array_add(args, g_strdup(passt->mac)); + } + + if (passt->gateway) { + g_ptr_array_add(args, g_strdup("--gateway")); + g_ptr_array_add(args, g_strdup(passt->gateway)); + } + + if (passt->interface) { + g_ptr_array_add(args, g_strdup("--interface")); + g_ptr_array_add(args, g_strdup(passt->interface)); + } + + if (passt->outbound) { + g_ptr_array_add(args, g_strdup("--outbound")); + g_ptr_array_add(args, g_strdup(passt->outbound)); + } + + if (passt->outbound_if4) { + g_ptr_array_add(args, g_strdup("--outbound-if4")); + g_ptr_array_add(args, g_strdup(passt->outbound_if4)); + } + + if (passt->outbound_if6) { + g_ptr_array_add(args, g_strdup("--outbound-if6")); + g_ptr_array_add(args, g_strdup(passt->outbound_if6)); + } + + if (passt->dns) { + g_ptr_array_add(args, g_strdup("--dns")); + g_ptr_array_add(args, g_strdup(passt->dns)); + } + if (passt->fqdn) { + g_ptr_array_add(args, g_strdup("--fqdn")); + g_ptr_array_add(args, g_strdup(passt->fqdn)); + } + + if (passt->has_dhcp_dns && !passt->dhcp_dns) { + g_ptr_array_add(args, g_strdup("--no-dhcp-dns")); + } + + if (passt->has_dhcp_search && !passt->dhcp_search) { + g_ptr_array_add(args, g_strdup("--no-dhcp-search")); + } + + if (passt->map_host_loopback) { + g_ptr_array_add(args, g_strdup("--map-host-loopback")); + g_ptr_array_add(args, g_strdup(passt->map_host_loopback)); + } + + if (passt->map_guest_addr) { + g_ptr_array_add(args, g_strdup("--map-guest-addr")); + g_ptr_array_add(args, g_strdup(passt->map_guest_addr)); + } + + if (passt->dns_forward) { + g_ptr_array_add(args, g_strdup("--dns-forward")); + g_ptr_array_add(args, g_strdup(passt->dns_forward)); + } + + if (passt->dns_host) { + g_ptr_array_add(args, g_strdup("--dns-host")); + g_ptr_array_add(args, g_strdup(passt->dns_host)); + } + + if (passt->has_tcp && !passt->tcp) { + g_ptr_array_add(args, g_strdup("--no-tcp")); + } + + if (passt->has_udp && !passt->udp) { + g_ptr_array_add(args, g_strdup("--no-udp")); + } + + if (passt->has_icmp && !passt->icmp) { + g_ptr_array_add(args, g_strdup("--no-icmp")); + } + + if (passt->has_dhcp && !passt->dhcp) { + g_ptr_array_add(args, g_strdup("--no-dhcp")); + } + + if (passt->has_ndp && !passt->ndp) { + g_ptr_array_add(args, g_strdup("--no-ndp")); + } + if (passt->has_dhcpv6 && !passt->dhcpv6) { + g_ptr_array_add(args, g_strdup("--no-dhcpv6")); + } + + if (passt->has_ra && !passt->ra) { + g_ptr_array_add(args, g_strdup("--no-ra")); + } + + if (passt->has_freebind && passt->freebind) { + g_ptr_array_add(args, g_strdup("--freebind")); + } + + if (passt->has_ipv4 && !passt->ipv4) { + g_ptr_array_add(args, g_strdup("--ipv6-only")); + } + + if (passt->has_ipv6 && !passt->ipv6) { + g_ptr_array_add(args, g_strdup("--ipv4-only")); + } + + if (passt->has_search && passt->search) { + const StringList *list = passt->search; + GString *domains = g_string_new(list->value->str); + + list = list->next; + while (list) { + g_string_append(domains, " "); + g_string_append(domains, list->value->str); + list = list->next; + } + + g_ptr_array_add(args, g_strdup("--search")); + g_ptr_array_add(args, g_string_free(domains, FALSE)); + } + + if (passt->has_tcp_ports && passt->tcp_ports) { + const StringList *list = passt->tcp_ports; + GString *tcp_ports = g_string_new(list->value->str); + + list = list->next; + while (list) { + g_string_append(tcp_ports, ","); + g_string_append(tcp_ports, list->value->str); + list = list->next; + } + + g_ptr_array_add(args, g_strdup("--tcp-ports")); + g_ptr_array_add(args, g_string_free(tcp_ports, FALSE)); + } + + if (passt->has_udp_ports && passt->udp_ports) { + const StringList *list = passt->udp_ports; + GString *udp_ports = g_string_new(list->value->str); + + list = list->next; + while (list) { + g_string_append(udp_ports, ","); + g_string_append(udp_ports, list->value->str); + list = list->next; + } + + g_ptr_array_add(args, g_strdup("--udp-ports")); + g_ptr_array_add(args, g_string_free(udp_ports, FALSE)); + } + + if (passt->has_param && passt->param) { + const StringList *list = passt->param; + + while (list) { + g_ptr_array_add(args, g_strdup(list->value->str)); + list = list->next; + } + } + + /* provide a pid file to be able to kil passt on exit */ + g_ptr_array_add(args, g_strdup("--pid")); + g_ptr_array_add(args, g_strdup(pidfile)); + + /* g_subprocess_launcher_take_fd() will set the socket on fd 3 */ + g_ptr_array_add(args, g_strdup("--fd")); + g_ptr_array_add(args, g_strdup("3")); + + g_ptr_array_add(args, NULL); + + return args; +} + +int net_init_passt(const Netdev *netdev, const char *name, + NetClientState *peer, Error **errp) +{ + g_autoptr(GError) error = NULL; + NetClientState *nc; + NetPasstState *s; + GPtrArray *args; + gchar *pidfile; + int pidfd; + + assert(netdev->type == NET_CLIENT_DRIVER_PASST); + + pidfd = g_file_open_tmp("passt-XXXXXX.pid", &pidfile, &error); + if (pidfd == -1) { + error_setg(errp, "Failed to create temporary file: %s", error->message); + return -1; + } + close(pidfd); + + args = net_passt_decode_args(&netdev->u.passt, pidfile, errp); + if (args == NULL) { + g_free(pidfile); + return -1; + } + + nc = qemu_new_net_client(&net_passt_info, peer, "passt", name); + s = DO_UPCAST(NetPasstState, data.nc, nc); + + s->args = args; + s->pidfile = pidfile; + + if (net_passt_stream_start(s, errp) == -1) { + qemu_del_net_client(nc); + return -1; + } + + return 0; +} diff --git a/qapi/net.json b/qapi/net.json index 97ea183981..24999f6752 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -112,6 +112,116 @@ 'data': { 'str': 'str' } } +## +# @NetDevPasstOptions: +# +# Unprivileged user-mode network connectivity using passt +# +# @path: Filename of the passt program to run (by default 'passt', and use PATH) +# +# @quiet: don't print informational messages (default, passed as '--quiet') +# +# @mtu: assign MTU via DHCP/NDP +# +# @address: IPv4 or IPv6 address +# +# @netmask: IPv4 mask +# +# @mac: source MAC address +# +# @gateway: IPv4 or IPv6 address as gateway +# +# @interface: interface for addresses and routes +# +# @outbound: bind to address as outbound source +# +# @outbound-if4: bind to outbound interface for IPv4 +# +# @outbound-if6: bind to outbound interface for IPv6 +# +# @dns: IPv4 or IPv6 address as DNS +# +# @search: search domains +# +# @fqdn: FQDN to configure client with +# +# @dhcp-dns: enable/disable DNS list in DHCP/DHCPv6/NDP +# +# @dhcp-search: enable/disable list in DHCP/DHCPv6/NDP +# +# @map-host-loopback: addresse to refer to host +# +# @map-guest-addr: addr to translate to guest's address +# +# @dns-forward: forward DNS queries sent to +# +# @dns-host: host nameserver to direct queries to +# +# @tcp: enable/disable TCP +# +# @udp: enable/disable UDP +# +# @icmp: enable/disable ICMP +# +# @dhcp: enable/disable DHCP +# +# @ndp: enable/disable NDP +# +# @dhcpv6: enable/disable DHCPv6 +# +# @ra: enable/disable route advertisements +# +# @freebind: bind to any address for forwarding +# +# @ipv4: enable/disable IPv4 +# +# @ipv6: enable/disable IPv6 +# +# @tcp-ports: TCP ports to forward +# +# @udp-ports: UDP ports to forward +# +# @param: parameter to pass to passt command +# +# Since: 10.1 +## +{ 'struct': 'NetDevPasstOptions', + 'data': { + '*path': 'str', + '*quiet': 'bool', + '*mtu': 'int', + '*address': 'str', + '*netmask': 'str', + '*mac': 'str', + '*gateway': 'str', + '*interface': 'str', + '*outbound': 'str', + '*outbound-if4': 'str', + '*outbound-if6': 'str', + '*dns': 'str', + '*search': ['String'], + '*fqdn': 'str', + '*dhcp-dns': 'bool', + '*dhcp-search': 'bool', + '*map-host-loopback': 'str', + '*map-guest-addr': 'str', + '*dns-forward': 'str', + '*dns-host': 'str', + '*tcp': 'bool', + '*udp': 'bool', + '*icmp': 'bool', + '*dhcp': 'bool', + '*ndp': 'bool', + '*dhcpv6': 'bool', + '*ra': 'bool', + '*freebind': 'bool', + '*ipv4': 'bool', + '*ipv6': 'bool', + '*tcp-ports': ['String'], + '*udp-ports': ['String'], + '*param': ['String'] }, + 'if': 'CONFIG_PASST' } + ## # @NetdevUserOptions: # @@ -729,12 +839,15 @@ # # @af-xdp: since 8.2 # +# @passt: since 10.1 +# # Since: 2.7 ## { 'enum': 'NetClientDriver', 'data': [ 'none', 'nic', 'user', 'tap', 'l2tpv3', 'socket', 'stream', 'dgram', 'vde', 'bridge', 'hubport', 'netmap', 'vhost-user', 'vhost-vdpa', + { 'name': 'passt', 'if': 'CONFIG_PASST' }, { 'name': 'af-xdp', 'if': 'CONFIG_AF_XDP' }, { 'name': 'vmnet-host', 'if': 'CONFIG_VMNET' }, { 'name': 'vmnet-shared', 'if': 'CONFIG_VMNET' }, @@ -756,6 +869,8 @@ 'discriminator': 'type', 'data': { 'nic': 'NetLegacyNicOptions', + 'passt': { 'type': 'NetDevPasstOptions', + 'if': 'CONFIG_PASST' }, 'user': 'NetdevUserOptions', 'tap': 'NetdevTapOptions', 'l2tpv3': 'NetdevL2TPv3Options', diff --git a/qemu-options.hx b/qemu-options.hx index 1f862b19a6..e8252cd5e8 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2796,6 +2796,24 @@ DEFHEADING() DEFHEADING(Network options:) DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, +#ifdef CONFIG_PASST + "-netdev passt,id=str[,path=file][,quiet=on|off]\n" + "[,mtu=mtu][,address=addr][,netmask=mask][,mac=addr][,gateway=addr]\n" + " [,interface=name][,outbound=address][,outbound-if4=name]\n" + " [,outbound-if6=name][,dns=addr][,search=list][,fqdn=name]\n" + " [,dhcp-dns=on|off][,dhcp-search=on|off][,map-host-loopback=addr]\n" + " [,map-guest-addr=addr][,dns-forward=addr][,dns-host=addr]\n" + " [,tcp=on|off][,udp=on|off][,icmp=on|off][,dhcp=on|off]\n" + " [,ndp=on|off][,dhcpv6=on|off][,ra=on|off][,freebind=on|off]\n" + " [,ipv4=on|off][,ipv6=on|off][,tcp-ports=spec][,udp-ports=spec]\n" + " [,param=list]\n" + " configure a passt network backend with ID 'str'\n" + " if 'path' is not provided 'passt' will be started according to PATH\n" + " by default, informational message of passt are not displayed (quiet=on)\n" + " to display this message, use 'quiet=off'\n" + " for details on other options, refer to passt(1)\n" + " 'param' allows to pass any option defined by passt(1)\n" +#endif #ifdef CONFIG_SLIRP "-netdev user,id=str[,ipv4=on|off][,net=addr[/mask]][,host=addr]\n" " [,ipv6=on|off][,ipv6-net=addr[/int]][,ipv6-host=addr]\n" @@ -2952,6 +2970,9 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " configure a hub port on the hub with ID 'n'\n", QEMU_ARCH_ALL) DEF("nic", HAS_ARG, QEMU_OPTION_nic, "-nic [tap|bridge|" +#ifdef CONFIG_PASST + "passt|" +#endif #ifdef CONFIG_SLIRP "user|" #endif @@ -2984,6 +3005,9 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, " configure or create an on-board (or machine default) NIC and\n" " connect it to hub 0 (please use -nic unless you need a hub)\n" "-net [" +#ifdef CONFIG_PASST + "passt|" +#endif #ifdef CONFIG_SLIRP "user|" #endif @@ -3005,7 +3029,7 @@ DEF("net", HAS_ARG, QEMU_OPTION_net, " old way to initialize a host network interface\n" " (use the -netdev option if possible instead)\n", QEMU_ARCH_ALL) SRST -``-nic [tap|bridge|user|l2tpv3|vde|netmap|af-xdp|vhost-user|socket][,...][,mac=macaddr][,model=mn]`` +``-nic [tap|passt|bridge|user|l2tpv3|vde|netmap|af-xdp|vhost-user|socket][,...][,mac=macaddr][,model=mn]`` This option is a shortcut for configuring both the on-board (default) guest NIC hardware and the host network backend in one go. The host backend options are the same as with the corresponding @@ -3027,6 +3051,123 @@ SRST network backend) which is activated if no other networking options are provided. +``-netdev passt,id=str[,option][,...]`` + Configure a passt network backend which requires no administrator + privilege to run. Valid options are: + + ``id=id`` + Assign symbolic name for use in monitor commands. + + ``path=file`` + Filename of the passt program to run. If it is not provided, + passt command will be started with the help of the PATH environment + variable. + + ``quiet=on|off`` + By default, ``quiet=on`` to disable informational message from + passt. ``quiet=on`` is passed as ``--quiet`` to passt. + + ``@mtu`` + Assign MTU via DHCP/NDP + + ``address`` + IPv4 or IPv6 address + + ``netmask`` + IPv4 mask + + ``mac`` + source MAC address + + ``gateway`` + IPv4 or IPv6 address as gateway + + ``interface`` + Interface for addresses and routes + + ``outbound`` + Bind to address as outbound source + + ``outbound-if4`` + Bind to outbound interface for IPv4 + + ``outbound-if6`` + Bind to outbound interface for IPv6 + + ``dns`` + IPv4 or IPv6 address as DNS + + ``search`` + Search domains + + ``fqdn`` + FQDN to configure client with + + ``dhcp-dns`` + Enable/disable DNS list in DHCP/DHCPv6/NDP + + ``dhcp-search`` + Enable/disable list in DHCP/DHCPv6/NDP + + ``map-host-loopback`` + Addresse to refer to host + + ``map-guest-addr`` + Addr to translate to guest's address + + ``dns-forward`` + Forward DNS queries sent to + + ``dns-host`` + Host nameserver to direct queries to + + ``tcp`` + Enable/disable TCP + + ``udp`` + Enable/disable UDP + + ``icmp`` + Enable/disable ICMP + + ``dhcp`` + Enable/disable DHCP + + ``ndp`` + Enable/disable NDP + + ``dhcpv6`` + Enable/disable DHCPv6 + + ``ra`` + Enable/disable route advertisements + + ``freebind`` + Bind to any address for forwarding + + ``ipv4`` + Enable/disable IPv4 + + ``ipv6`` + Enable/disable IPv6 + + ``tcp-ports`` + TCP ports to forward + + ``udp-ports`` + UDP ports to forward + + ``param=string`` + ``string`` will be passed to passt has a command line parameter, + we can have multiple occurences of the ``param`` parameter to + pass multiple parameters to passt. + + For instance, to pass ``--trace --log=trace.log``: + + .. parsed-literal:: + + |qemu_system| -nic passt,param=--trace,param=--log=trace.log + ``-netdev user,id=id[,option][,option][,...]`` Configure user mode host network backend which requires no administrator privilege to run. Valid options are: @@ -3711,7 +3852,7 @@ SRST Use ``-net nic,model=help`` for a list of available devices for your target. -``-net user|tap|bridge|socket|l2tpv3|vde[,...][,name=name]`` +``-net user|passt|tap|bridge|socket|l2tpv3|vde[,...][,name=name]`` Configure a host network backend (with the options corresponding to the same ``-netdev`` option) and connect it to the emulated hub 0 (the default hub). Use name to specify the name of the hub port. diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 73e0770f42..bb3e34d852 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -162,6 +162,7 @@ meson_options_help() { printf "%s\n" ' oss OSS sound support' printf "%s\n" ' pa PulseAudio sound support' printf "%s\n" ' parallels parallels image format support' + printf "%s\n" ' passt passt network backend support' printf "%s\n" ' pipewire PipeWire sound support' printf "%s\n" ' pixman pixman support' printf "%s\n" ' plugins TCG plugins via shared library loading' @@ -422,6 +423,8 @@ _meson_option_parse() { --disable-pa) printf "%s" -Dpa=disabled ;; --enable-parallels) printf "%s" -Dparallels=enabled ;; --disable-parallels) printf "%s" -Dparallels=disabled ;; + --enable-passt) printf "%s" -Dpasst=enabled ;; + --disable-passt) printf "%s" -Dpasst=disabled ;; --enable-pipewire) printf "%s" -Dpipewire=enabled ;; --disable-pipewire) printf "%s" -Dpipewire=disabled ;; --enable-pixman) printf "%s" -Dpixman=enabled ;; From da703b06a52bfb5fe1a77b0eddbb8d68d3f70762 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Wed, 9 Jul 2025 10:24:25 +0200 Subject: [PATCH 2230/2760] net/passt: Implement vhost-user backend support This commit adds support for the vhost-user interface to the passt network backend, enabling high-performance, accelerated networking for guests using passt. The passt backend can now operate in a vhost-user mode, where it communicates with the guest's virtio-net device over a socket pair using the vhost-user protocol. This offloads the datapath from the main QEMU loop, significantly improving network performance. When the vhost-user=on option is used with -netdev passt, the new vhost initialization path is taken instead of the standard stream-based connection. Signed-off-by: Laurent Vivier Signed-off-by: Jason Wang --- docs/system/devices/net.rst | 12 +- net/passt.c | 346 ++++++++++++++++++++++++++++++++++++ qapi/net.json | 3 + qemu-options.hx | 10 +- 4 files changed, 369 insertions(+), 2 deletions(-) diff --git a/docs/system/devices/net.rst b/docs/system/devices/net.rst index c586ee0f40..4d787c3aeb 100644 --- a/docs/system/devices/net.rst +++ b/docs/system/devices/net.rst @@ -104,7 +104,7 @@ To use the passt backend interface There is no need to start the daemon as QEMU will do it for you. -passt is started in the socket-based mode. +By default, passt will be started in the socket-based mode. .. parsed-literal:: |qemu_system| [...OPTIONS...] -nic passt @@ -128,6 +128,16 @@ passt is started in the socket-based mode. virtio0: index=0,type=nic,model=virtio-net-pci,macaddr=9a:2b:2c:2d:2e:2f \ netdev0: index=0,type=passt,stream,connected to pid 25428 +To use the vhost-based interface, add the ``vhost-user=on`` parameter and +select the virtio-net device: + +.. parsed-literal:: + |qemu_system| [...OPTIONS...] -nic passt,model=virtio,vhost-user=on + + (qemu) info network + virtio-net-pci.0: index=0,type=nic,model=virtio-net-pci,macaddr=52:54:00:12:34:56 + \ #net006: index=0,type=passt,vhost-user,connected to pid 25731 + To use socket based passt interface: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/net/passt.c b/net/passt.c index 0a4a1ba6aa..6f616ba3c2 100644 --- a/net/passt.c +++ b/net/passt.c @@ -7,18 +7,75 @@ */ #include "qemu/osdep.h" #include +#include "qemu/error-report.h" #include #include "net/net.h" #include "clients.h" #include "qapi/error.h" #include "io/net-listener.h" +#include "chardev/char-fe.h" +#include "net/vhost_net.h" +#include "hw/virtio/vhost.h" +#include "hw/virtio/vhost-user.h" +#include "standard-headers/linux/virtio_net.h" #include "stream_data.h" +#ifdef CONFIG_VHOST_USER +static const int user_feature_bits[] = { + VIRTIO_F_NOTIFY_ON_EMPTY, + VIRTIO_F_NOTIFICATION_DATA, + VIRTIO_RING_F_INDIRECT_DESC, + VIRTIO_RING_F_EVENT_IDX, + + VIRTIO_F_ANY_LAYOUT, + VIRTIO_F_VERSION_1, + VIRTIO_NET_F_CSUM, + VIRTIO_NET_F_GUEST_CSUM, + VIRTIO_NET_F_GSO, + VIRTIO_NET_F_GUEST_TSO4, + VIRTIO_NET_F_GUEST_TSO6, + VIRTIO_NET_F_GUEST_ECN, + VIRTIO_NET_F_GUEST_UFO, + VIRTIO_NET_F_HOST_TSO4, + VIRTIO_NET_F_HOST_TSO6, + VIRTIO_NET_F_HOST_ECN, + VIRTIO_NET_F_HOST_UFO, + VIRTIO_NET_F_MRG_RXBUF, + VIRTIO_NET_F_MTU, + VIRTIO_F_IOMMU_PLATFORM, + VIRTIO_F_RING_PACKED, + VIRTIO_F_RING_RESET, + VIRTIO_F_IN_ORDER, + VIRTIO_NET_F_RSS, + VIRTIO_NET_F_RSC_EXT, + VIRTIO_NET_F_HASH_REPORT, + VIRTIO_NET_F_GUEST_USO4, + VIRTIO_NET_F_GUEST_USO6, + VIRTIO_NET_F_HOST_USO, + + /* This bit implies RARP isn't sent by QEMU out of band */ + VIRTIO_NET_F_GUEST_ANNOUNCE, + + VIRTIO_NET_F_MQ, + + VHOST_INVALID_FEATURE_BIT +}; +#endif + typedef struct NetPasstState { NetStreamData data; GPtrArray *args; gchar *pidfile; pid_t pid; +#ifdef CONFIG_VHOST_USER + /* vhost user */ + VhostUserState *vhost_user; + VHostNetState *vhost_net; + CharBackend vhost_chr; + guint vhost_watch; + uint64_t acked_features; + bool started; +#endif } NetPasstState; static int net_passt_stream_start(NetPasstState *s, Error **errp); @@ -27,6 +84,24 @@ static void net_passt_cleanup(NetClientState *nc) { NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); +#ifdef CONFIG_VHOST_USER + if (s->vhost_net) { + vhost_net_cleanup(s->vhost_net); + g_free(s->vhost_net); + s->vhost_net = NULL; + } + if (s->vhost_watch) { + g_source_remove(s->vhost_watch); + s->vhost_watch = 0; + } + qemu_chr_fe_deinit(&s->vhost_chr, true); + if (s->vhost_user) { + vhost_user_cleanup(s->vhost_user); + g_free(s->vhost_user); + s->vhost_user = NULL; + } +#endif + kill(s->pid, SIGTERM); g_remove(s->pidfile); g_free(s->pidfile); @@ -60,11 +135,98 @@ static gboolean net_passt_send(QIOChannel *ioc, GIOCondition condition, return G_SOURCE_CONTINUE; } +#ifdef CONFIG_VHOST_USER +static int passt_set_vnet_endianness(NetClientState *nc, bool enable) +{ + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + return 0; +} + +static bool passt_has_vnet_hdr(NetClientState *nc) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + return s->vhost_user != NULL; +} + +static bool passt_has_ufo(NetClientState *nc) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + return s->vhost_user != NULL; +} + +static bool passt_check_peer_type(NetClientState *nc, ObjectClass *oc, + Error **errp) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + const char *driver = object_class_get_name(oc); + + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + if (s->vhost_user == NULL) { + return true; + } + + if (!g_str_has_prefix(driver, "virtio-net-")) { + error_setg(errp, "vhost-user requires frontend driver virtio-net-*"); + return false; + } + + return true; +} + +static struct vhost_net *passt_get_vhost_net(NetClientState *nc) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + return s->vhost_net; +} + +static uint64_t passt_get_acked_features(NetClientState *nc) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + return s->acked_features; +} + +static void passt_save_acked_features(NetClientState *nc) +{ + NetPasstState *s = DO_UPCAST(NetPasstState, data.nc, nc); + + assert(nc->info->type == NET_CLIENT_DRIVER_PASST); + + if (s->vhost_net) { + uint64_t features = vhost_net_get_acked_features(s->vhost_net); + if (features) { + s->acked_features = features; + } + } +} +#endif + static NetClientInfo net_passt_info = { .type = NET_CLIENT_DRIVER_PASST, .size = sizeof(NetPasstState), .receive = net_passt_receive, .cleanup = net_passt_cleanup, +#ifdef CONFIG_VHOST_USER + .has_vnet_hdr = passt_has_vnet_hdr, + .has_ufo = passt_has_ufo, + .set_vnet_be = passt_set_vnet_endianness, + .set_vnet_le = passt_set_vnet_endianness, + .check_peer_type = passt_check_peer_type, + .get_vhost_net = passt_get_vhost_net, +#endif }; static void net_passt_client_connected(QIOTask *task, gpointer opaque) @@ -163,6 +325,177 @@ static int net_passt_stream_start(NetPasstState *s, Error **errp) return 0; } +#ifdef CONFIG_VHOST_USER +static gboolean passt_vhost_user_watch(void *do_not_use, GIOCondition cond, + void *opaque) +{ + NetPasstState *s = opaque; + + qemu_chr_fe_disconnect(&s->vhost_chr); + + return G_SOURCE_CONTINUE; +} + +static void passt_vhost_user_event(void *opaque, QEMUChrEvent event); + +static void chr_closed_bh(void *opaque) +{ + NetPasstState *s = opaque; + + passt_save_acked_features(&s->data.nc); + + net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, false); + + qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, passt_vhost_user_event, + NULL, s, NULL, true); +} + +static void passt_vhost_user_stop(NetPasstState *s) +{ + passt_save_acked_features(&s->data.nc); + vhost_net_cleanup(s->vhost_net); +} + +static int passt_vhost_user_start(NetPasstState *s, VhostUserState *be) +{ + struct vhost_net *net = NULL; + VhostNetOptions options; + + options.backend_type = VHOST_BACKEND_TYPE_USER; + options.net_backend = &s->data.nc; + options.opaque = be; + options.busyloop_timeout = 0; + options.nvqs = 2; + options.feature_bits = user_feature_bits; + options.max_tx_queue_size = VIRTQUEUE_MAX_SIZE; + options.get_acked_features = passt_get_acked_features; + options.save_acked_features = passt_save_acked_features; + options.is_vhost_user = true; + + net = vhost_net_init(&options); + if (!net) { + error_report("failed to init passt vhost_net"); + goto err; + } + + if (s->vhost_net) { + vhost_net_cleanup(s->vhost_net); + g_free(s->vhost_net); + } + s->vhost_net = net; + + return 0; +err: + if (net) { + vhost_net_cleanup(net); + g_free(net); + } + passt_vhost_user_stop(s); + return -1; +} + +static void passt_vhost_user_event(void *opaque, QEMUChrEvent event) +{ + NetPasstState *s = opaque; + Error *err = NULL; + + switch (event) { + case CHR_EVENT_OPENED: + if (passt_vhost_user_start(s, s->vhost_user) < 0) { + qemu_chr_fe_disconnect(&s->vhost_chr); + return; + } + s->vhost_watch = qemu_chr_fe_add_watch(&s->vhost_chr, G_IO_HUP, + passt_vhost_user_watch, s); + net_client_set_link(&(NetClientState *){ &s->data.nc }, 1, true); + s->started = true; + break; + case CHR_EVENT_CLOSED: + if (s->vhost_watch) { + AioContext *ctx = qemu_get_current_aio_context(); + + g_source_remove(s->vhost_watch); + s->vhost_watch = 0; + qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, NULL, NULL, + NULL, NULL, false); + + aio_bh_schedule_oneshot(ctx, chr_closed_bh, s); + } + break; + case CHR_EVENT_BREAK: + case CHR_EVENT_MUX_IN: + case CHR_EVENT_MUX_OUT: + /* Ignore */ + break; + } + + if (err) { + error_report_err(err); + } +} + +static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) +{ + Chardev *chr; + int sv[2]; + + if (socketpair(PF_UNIX, SOCK_STREAM, 0, sv) == -1) { + error_setg_errno(errp, errno, "socketpair() failed"); + return -1; + } + + /* connect to passt */ + qemu_set_info_str(&s->data.nc, "connecting to passt"); + + /* create chardev */ + + chr = CHARDEV(object_new(TYPE_CHARDEV_SOCKET)); + if (!chr || qemu_chr_add_client(chr, sv[0]) == -1) { + object_unref(OBJECT(chr)); + error_setg(errp, "Failed to make socket chardev"); + goto err; + } + + s->vhost_user = g_new0(struct VhostUserState, 1); + if (!qemu_chr_fe_init(&s->vhost_chr, chr, errp) || + !vhost_user_init(s->vhost_user, &s->vhost_chr, errp)) { + goto err; + } + + /* start passt */ + if (net_passt_start_daemon(s, sv[1], errp) == -1) { + goto err; + } + + do { + if (qemu_chr_fe_wait_connected(&s->vhost_chr, errp) < 0) { + goto err; + } + + qemu_chr_fe_set_handlers(&s->vhost_chr, NULL, NULL, + passt_vhost_user_event, NULL, s, NULL, + true); + } while (!s->started); + + qemu_set_info_str(&s->data.nc, "vhost-user,connected to pid %d", s->pid); + + close(sv[1]); + return 0; +err: + close(sv[0]); + close(sv[1]); + + return -1; +} +#else +static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) +{ + error_setg(errp, "vhost-user support has not been built"); + + return -1; +} +#endif + static GPtrArray *net_passt_decode_args(const NetDevPasstOptions *passt, gchar *pidfile, Error **errp) { @@ -174,6 +507,10 @@ static GPtrArray *net_passt_decode_args(const NetDevPasstOptions *passt, g_ptr_array_add(args, g_strdup("passt")); } + if (passt->has_vhost_user && passt->vhost_user) { + g_ptr_array_add(args, g_strdup("--vhost-user")); + } + /* by default, be quiet */ if (!passt->has_quiet || passt->quiet) { g_ptr_array_add(args, g_strdup("--quiet")); @@ -398,6 +735,15 @@ int net_init_passt(const Netdev *netdev, const char *name, s->args = args; s->pidfile = pidfile; + if (netdev->u.passt.has_vhost_user && netdev->u.passt.vhost_user) { + if (net_passt_vhost_user_init(s, errp) == -1) { + qemu_del_net_client(nc); + return -1; + } + + return 0; + } + if (net_passt_stream_start(s, errp) == -1) { qemu_del_net_client(nc); return -1; diff --git a/qapi/net.json b/qapi/net.json index 24999f6752..0f766041a3 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -121,6 +121,8 @@ # # @quiet: don't print informational messages (default, passed as '--quiet') # +# @vhost-user: enable vhost-user +# # @mtu: assign MTU via DHCP/NDP # # @address: IPv4 or IPv6 address @@ -189,6 +191,7 @@ 'data': { '*path': 'str', '*quiet': 'bool', + '*vhost-user': 'bool', '*mtu': 'int', '*address': 'str', '*netmask': 'str', diff --git a/qemu-options.hx b/qemu-options.hx index e8252cd5e8..a3c066c678 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2797,7 +2797,7 @@ DEFHEADING(Network options:) DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, #ifdef CONFIG_PASST - "-netdev passt,id=str[,path=file][,quiet=on|off]\n" + "-netdev passt,id=str[,path=file][,quiet=on|off][,vhost-user=on|off]\n" "[,mtu=mtu][,address=addr][,netmask=mask][,mac=addr][,gateway=addr]\n" " [,interface=name][,outbound=address][,outbound-if4=name]\n" " [,outbound-if6=name][,dns=addr][,search=list][,fqdn=name]\n" @@ -2811,6 +2811,8 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " if 'path' is not provided 'passt' will be started according to PATH\n" " by default, informational message of passt are not displayed (quiet=on)\n" " to display this message, use 'quiet=off'\n" + " by default, passt will be started in socket-based mode, to enable vhost-mode,\n" + " use 'vhost-user=on'\n" " for details on other options, refer to passt(1)\n" " 'param' allows to pass any option defined by passt(1)\n" #endif @@ -3067,6 +3069,12 @@ SRST By default, ``quiet=on`` to disable informational message from passt. ``quiet=on`` is passed as ``--quiet`` to passt. + ``vhost-user=on|off`` + By default, ``vhost-user=off`` and QEMU uses the stream network + backend to communicate with passt. If ``vhost-user=on``, passt is + started with ``--vhost-user`` and QEMU uses the vhost-user network + backend to communicate with passt. + ``@mtu`` Assign MTU via DHCP/NDP From 66c83cdd91c07575ebf30bb45da8cc5df8041c29 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 18 Jun 2025 12:53:49 -0400 Subject: [PATCH 2231/2760] docs/sphinx: adjust qapidoc to cope with same-line error sections Without this, the line the new QAPI doc generator chokes on # Errors: some in doc-good.json. We still use the old doc generator for the tests, but we're about to correct that. Signed-off-by: John Snow Message-ID: <20250618165353.1980365-2-jsnow@redhat.com> Acked-by: Markus Armbruster Fixes: e9fbf1a0c6c2 (docs/qapidoc: add visit_errors() method) [Amend commit message to point to reproducer, and add Fixes:] Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 8011ac9efa..5374dee8fa 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -267,10 +267,14 @@ def visit_returns(self, section: QAPIDoc.Section) -> None: self.add_field("return", typ, section.text, section.info) def visit_errors(self, section: QAPIDoc.Section) -> None: - # FIXME: the formatting for errors may be inconsistent and may - # or may not require different newline placement to ensure - # proper rendering as a nested list. - self.add_lines(f":error:\n{section.text}", section.info) + # If the section text does not start with a space, it means text + # began on the same line as the "Error:" string and we should + # not insert a newline in this case. + if section.text[0].isspace(): + text = f":error:\n{section.text}" + else: + text = f":error: {section.text}" + self.add_lines(text, section.info) def preamble(self, ent: QAPISchemaDefinition) -> None: """ From e56c683bae9d1dc1b637029a0595225499ef7248 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 18 Jun 2025 12:53:50 -0400 Subject: [PATCH 2232/2760] docs/sphinx: parse @references in freeform text Oversight in the new qapidoc transmogrifier: @references in freeform documentation blocks were not being transformed to literals. This fixes that, and the next patch ensures that we're testing for this O:-) Signed-off-by: John Snow Message-ID: <20250618165353.1980365-3-jsnow@redhat.com> Acked-by: Markus Armbruster Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index 5374dee8fa..adc14ade45 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -218,6 +218,11 @@ def generate_field( typ = self.format_type(member) self.add_field(kind, member.name, body, info, typ) + @staticmethod + def reformat_arobase(text: str) -> str: + """ reformats @var to ``var`` """ + return re.sub(r"@([\w-]+)", r"``\1``", text) + # Transmogrification helpers def visit_paragraph(self, section: QAPIDoc.Section) -> None: @@ -361,8 +366,7 @@ def visit_sections(self, ent: QAPISchemaDefinition) -> None: # Add sections in source order: for i, section in enumerate(sections): - # @var is translated to ``var``: - section.text = re.sub(r"@([\w-]+)", r"``\1``", section.text) + section.text = self.reformat_arobase(section.text) if section.kind == QAPIDoc.Kind.PLAIN: self.visit_paragraph(section) @@ -405,7 +409,7 @@ def visit_freeform(self, doc: QAPIDoc) -> None: assert len(doc.all_sections) == 1, doc.all_sections body = doc.all_sections[0] - text = body.text + text = self.reformat_arobase(body.text) info = doc.info if re.match(r"=+ ", text): From 8d789c8cdb8de2cae39f217b6c9607ac9c036c8c Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 18 Jun 2025 12:53:51 -0400 Subject: [PATCH 2233/2760] docs/sphinx: remove legacy QAPI manual generator Thanks for your service! Remove the old qapidoc and the option to enable the transmogrifier, leaving the "transmogrifier" as the ONLY qapi doc generator. This in effect also converts the QAPI test to use the new documentation generator, too. Update doc-good.txt output to match the new doc generator, which I should've done exactly when we switched over to the transmogrifier, but, uhh, oops! Notes on the new format: 1. per-member IFCOND documentation is missing. Known issue. 2. Freeform documentation without a header is now copied through into the output. This is a bug fix. Signed-off-by: John Snow Message-ID: <20250618165353.1980365-4-jsnow@redhat.com> Acked-by: Markus Armbruster Fixes: b61a4eb3f32 (docs/qapidoc: support header-less freeform sections) [Tweak commit message to say it's a bug fix, add Fixes:] Signed-off-by: Markus Armbruster --- docs/interop/qemu-ga-ref.rst | 1 - docs/interop/qemu-qmp-ref.rst | 1 - docs/interop/qemu-storage-daemon-qmp-ref.rst | 1 - docs/sphinx/qapidoc.py | 25 +- docs/sphinx/qapidoc_legacy.py | 440 ------------------- python/tests/qapi-isort.sh | 2 +- tests/qapi-schema/doc-good.txt | 274 ++++-------- 7 files changed, 88 insertions(+), 656 deletions(-) delete mode 100644 docs/sphinx/qapidoc_legacy.py diff --git a/docs/interop/qemu-ga-ref.rst b/docs/interop/qemu-ga-ref.rst index 25f6e24b03..ea6652ae43 100644 --- a/docs/interop/qemu-ga-ref.rst +++ b/docs/interop/qemu-ga-ref.rst @@ -2,5 +2,4 @@ QEMU Guest Agent Protocol Reference =================================== .. qapi-doc:: qga/qapi-schema.json - :transmogrify: :namespace: QGA diff --git a/docs/interop/qemu-qmp-ref.rst b/docs/interop/qemu-qmp-ref.rst index 3bc1ca12b1..f0ce39ad67 100644 --- a/docs/interop/qemu-qmp-ref.rst +++ b/docs/interop/qemu-qmp-ref.rst @@ -7,5 +7,4 @@ QEMU QMP Reference Manual :local: .. qapi-doc:: qapi/qapi-schema.json - :transmogrify: :namespace: QMP diff --git a/docs/interop/qemu-storage-daemon-qmp-ref.rst b/docs/interop/qemu-storage-daemon-qmp-ref.rst index dc7bde262a..4dbb6a2cc8 100644 --- a/docs/interop/qemu-storage-daemon-qmp-ref.rst +++ b/docs/interop/qemu-storage-daemon-qmp-ref.rst @@ -5,5 +5,4 @@ QEMU Storage Daemon QMP Reference Manual :local: .. qapi-doc:: storage-daemon/qapi/qapi-schema.json - :transmogrify: :namespace: QSD diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index adc14ade45..d5d2b5eeb5 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -64,8 +64,6 @@ from sphinx.util.docutils import SphinxDirective, switch_source_input from sphinx.util.nodes import nested_parse_with_titles -from qapidoc_legacy import QAPISchemaGenRSTVisitor # type: ignore - if TYPE_CHECKING: from typing import ( @@ -512,15 +510,9 @@ class QAPIDocDirective(NestedDirective): option_spec = { "qapifile": directives.unchanged_required, "namespace": directives.unchanged, - "transmogrify": directives.flag, } has_content = False - def new_serialno(self) -> str: - """Return a unique new ID string suitable for use as a node's ID""" - env = self.state.document.settings.env - return "qapidoc-%d" % env.new_serialno("qapidoc") - def transmogrify(self, schema: QAPISchema) -> nodes.Element: logger.info("Transmogrifying QAPI to rST ...") vis = Transmogrifier() @@ -598,21 +590,10 @@ def write_intermediate(self, content: StringList, filename: str) -> None: outfile.write(f" {rcol}") outfile.write("\n") - def legacy(self, schema: QAPISchema) -> nodes.Element: - vis = QAPISchemaGenRSTVisitor(self) - vis.visit_begin(schema) - for doc in schema.docs: - if doc.symbol: - vis.symbol(doc, schema.lookup_entity(doc.symbol)) - else: - vis.freeform(doc) - return vis.get_document_node() # type: ignore - def run(self) -> Sequence[nodes.Node]: env = self.state.document.settings.env qapifile = env.config.qapidoc_srctree + "/" + self.arguments[0] qapidir = os.path.dirname(qapifile) - transmogrify = "transmogrify" in self.options try: schema = QAPISchema(qapifile) @@ -625,11 +606,7 @@ def run(self) -> Sequence[nodes.Node]: # so they are displayed nicely to the user raise ExtensionError(str(err)) from err - if transmogrify: - contentnode = self.transmogrify(schema) - else: - contentnode = self.legacy(schema) - + contentnode = self.transmogrify(schema) return contentnode.children diff --git a/docs/sphinx/qapidoc_legacy.py b/docs/sphinx/qapidoc_legacy.py deleted file mode 100644 index 13520f4c26..0000000000 --- a/docs/sphinx/qapidoc_legacy.py +++ /dev/null @@ -1,440 +0,0 @@ -# coding=utf-8 -# type: ignore -# -# QEMU qapidoc QAPI file parsing extension -# -# Copyright (c) 2020 Linaro -# -# This work is licensed under the terms of the GNU GPLv2 or later. -# See the COPYING file in the top-level directory. - -""" -qapidoc is a Sphinx extension that implements the qapi-doc directive - -The purpose of this extension is to read the documentation comments -in QAPI schema files, and insert them all into the current document. - -It implements one new rST directive, "qapi-doc::". -Each qapi-doc:: directive takes one argument, which is the -pathname of the schema file to process, relative to the source tree. - -The docs/conf.py file must set the qapidoc_srctree config value to -the root of the QEMU source tree. - -The Sphinx documentation on writing extensions is at: -https://www.sphinx-doc.org/en/master/development/index.html -""" - -import re -import textwrap - -from docutils import nodes -from docutils.statemachine import ViewList -from qapi.error import QAPISemError -from qapi.gen import QAPISchemaVisitor -from qapi.parser import QAPIDoc - - -def dedent(text: str) -> str: - # Adjust indentation to make description text parse as paragraph. - - lines = text.splitlines(True) - if re.match(r"\s+", lines[0]): - # First line is indented; description started on the line after - # the name. dedent the whole block. - return textwrap.dedent(text) - - # Descr started on same line. Dedent line 2+. - return lines[0] + textwrap.dedent("".join(lines[1:])) - - -class QAPISchemaGenRSTVisitor(QAPISchemaVisitor): - """A QAPI schema visitor which generates docutils/Sphinx nodes - - This class builds up a tree of docutils/Sphinx nodes corresponding - to documentation for the various QAPI objects. To use it, first - create a QAPISchemaGenRSTVisitor object, and call its - visit_begin() method. Then you can call one of the two methods - 'freeform' (to add documentation for a freeform documentation - chunk) or 'symbol' (to add documentation for a QAPI symbol). These - will cause the visitor to build up the tree of document - nodes. Once you've added all the documentation via 'freeform' and - 'symbol' method calls, you can call 'get_document_nodes' to get - the final list of document nodes (in a form suitable for returning - from a Sphinx directive's 'run' method). - """ - def __init__(self, sphinx_directive): - self._cur_doc = None - self._sphinx_directive = sphinx_directive - self._top_node = nodes.section() - self._active_headings = [self._top_node] - - def _make_dlitem(self, term, defn): - """Return a dlitem node with the specified term and definition. - - term should be a list of Text and literal nodes. - defn should be one of: - - a string, which will be handed to _parse_text_into_node - - a list of Text and literal nodes, which will be put into - a paragraph node - """ - dlitem = nodes.definition_list_item() - dlterm = nodes.term('', '', *term) - dlitem += dlterm - if defn: - dldef = nodes.definition() - if isinstance(defn, list): - dldef += nodes.paragraph('', '', *defn) - else: - self._parse_text_into_node(defn, dldef) - dlitem += dldef - return dlitem - - def _make_section(self, title): - """Return a section node with optional title""" - section = nodes.section(ids=[self._sphinx_directive.new_serialno()]) - if title: - section += nodes.title(title, title) - return section - - def _nodes_for_ifcond(self, ifcond, with_if=True): - """Return list of Text, literal nodes for the ifcond - - Return a list which gives text like ' (If: condition)'. - If with_if is False, we don't return the "(If: " and ")". - """ - - doc = ifcond.docgen() - if not doc: - return [] - doc = nodes.literal('', doc) - if not with_if: - return [doc] - - nodelist = [nodes.Text(' ('), nodes.strong('', 'If: ')] - nodelist.append(doc) - nodelist.append(nodes.Text(')')) - return nodelist - - def _nodes_for_one_member(self, member): - """Return list of Text, literal nodes for this member - - Return a list of doctree nodes which give text like - 'name: type (optional) (If: ...)' suitable for use as the - 'term' part of a definition list item. - """ - term = [nodes.literal('', member.name)] - if member.type.doc_type(): - term.append(nodes.Text(': ')) - term.append(nodes.literal('', member.type.doc_type())) - if member.optional: - term.append(nodes.Text(' (optional)')) - if member.ifcond.is_present(): - term.extend(self._nodes_for_ifcond(member.ifcond)) - return term - - def _nodes_for_variant_when(self, branches, variant): - """Return list of Text, literal nodes for variant 'when' clause - - Return a list of doctree nodes which give text like - 'when tagname is variant (If: ...)' suitable for use in - the 'branches' part of a definition list. - """ - term = [nodes.Text(' when '), - nodes.literal('', branches.tag_member.name), - nodes.Text(' is '), - nodes.literal('', '"%s"' % variant.name)] - if variant.ifcond.is_present(): - term.extend(self._nodes_for_ifcond(variant.ifcond)) - return term - - def _nodes_for_members(self, doc, what, base=None, branches=None): - """Return list of doctree nodes for the table of members""" - dlnode = nodes.definition_list() - for section in doc.args.values(): - term = self._nodes_for_one_member(section.member) - # TODO drop fallbacks when undocumented members are outlawed - if section.text: - defn = dedent(section.text) - else: - defn = [nodes.Text('Not documented')] - - dlnode += self._make_dlitem(term, defn) - - if base: - dlnode += self._make_dlitem([nodes.Text('The members of '), - nodes.literal('', base.doc_type())], - None) - - if branches: - for v in branches.variants: - if v.type.name == 'q_empty': - continue - assert not v.type.is_implicit() - term = [nodes.Text('The members of '), - nodes.literal('', v.type.doc_type())] - term.extend(self._nodes_for_variant_when(branches, v)) - dlnode += self._make_dlitem(term, None) - - if not dlnode.children: - return [] - - section = self._make_section(what) - section += dlnode - return [section] - - def _nodes_for_enum_values(self, doc): - """Return list of doctree nodes for the table of enum values""" - seen_item = False - dlnode = nodes.definition_list() - for section in doc.args.values(): - termtext = [nodes.literal('', section.member.name)] - if section.member.ifcond.is_present(): - termtext.extend(self._nodes_for_ifcond(section.member.ifcond)) - # TODO drop fallbacks when undocumented members are outlawed - if section.text: - defn = dedent(section.text) - else: - defn = [nodes.Text('Not documented')] - - dlnode += self._make_dlitem(termtext, defn) - seen_item = True - - if not seen_item: - return [] - - section = self._make_section('Values') - section += dlnode - return [section] - - def _nodes_for_arguments(self, doc, arg_type): - """Return list of doctree nodes for the arguments section""" - if arg_type and not arg_type.is_implicit(): - assert not doc.args - section = self._make_section('Arguments') - dlnode = nodes.definition_list() - dlnode += self._make_dlitem( - [nodes.Text('The members of '), - nodes.literal('', arg_type.name)], - None) - section += dlnode - return [section] - - return self._nodes_for_members(doc, 'Arguments') - - def _nodes_for_features(self, doc): - """Return list of doctree nodes for the table of features""" - seen_item = False - dlnode = nodes.definition_list() - for section in doc.features.values(): - dlnode += self._make_dlitem( - [nodes.literal('', section.member.name)], dedent(section.text)) - seen_item = True - - if not seen_item: - return [] - - section = self._make_section('Features') - section += dlnode - return [section] - - def _nodes_for_sections(self, doc): - """Return list of doctree nodes for additional sections""" - nodelist = [] - for section in doc.sections: - if section.kind == QAPIDoc.Kind.TODO: - # Hide TODO: sections - continue - - if section.kind == QAPIDoc.Kind.PLAIN: - # Sphinx cannot handle sectionless titles; - # Instead, just append the results to the prior section. - container = nodes.container() - self._parse_text_into_node(section.text, container) - nodelist += container.children - continue - - snode = self._make_section(section.kind.name.title()) - self._parse_text_into_node(dedent(section.text), snode) - nodelist.append(snode) - return nodelist - - def _nodes_for_if_section(self, ifcond): - """Return list of doctree nodes for the "If" section""" - nodelist = [] - if ifcond.is_present(): - snode = self._make_section('If') - snode += nodes.paragraph( - '', '', *self._nodes_for_ifcond(ifcond, with_if=False) - ) - nodelist.append(snode) - return nodelist - - def _add_doc(self, typ, sections): - """Add documentation for a command/object/enum... - - We assume we're documenting the thing defined in self._cur_doc. - typ is the type of thing being added ("Command", "Object", etc) - - sections is a list of nodes for sections to add to the definition. - """ - - doc = self._cur_doc - snode = nodes.section(ids=[self._sphinx_directive.new_serialno()]) - snode += nodes.title('', '', *[nodes.literal(doc.symbol, doc.symbol), - nodes.Text(' (' + typ + ')')]) - self._parse_text_into_node(doc.body.text, snode) - for s in sections: - if s is not None: - snode += s - self._add_node_to_current_heading(snode) - - def visit_enum_type(self, name, info, ifcond, features, members, prefix): - doc = self._cur_doc - self._add_doc('Enum', - self._nodes_for_enum_values(doc) - + self._nodes_for_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def visit_object_type(self, name, info, ifcond, features, - base, members, branches): - doc = self._cur_doc - if base and base.is_implicit(): - base = None - self._add_doc('Object', - self._nodes_for_members(doc, 'Members', base, branches) - + self._nodes_for_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def visit_alternate_type(self, name, info, ifcond, features, - alternatives): - doc = self._cur_doc - self._add_doc('Alternate', - self._nodes_for_members(doc, 'Members') - + self._nodes_for_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def visit_command(self, name, info, ifcond, features, arg_type, - ret_type, gen, success_response, boxed, allow_oob, - allow_preconfig, coroutine): - doc = self._cur_doc - self._add_doc('Command', - self._nodes_for_arguments(doc, arg_type) - + self._nodes_for_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def visit_event(self, name, info, ifcond, features, arg_type, boxed): - doc = self._cur_doc - self._add_doc('Event', - self._nodes_for_arguments(doc, arg_type) - + self._nodes_for_features(doc) - + self._nodes_for_sections(doc) - + self._nodes_for_if_section(ifcond)) - - def symbol(self, doc, entity): - """Add documentation for one symbol to the document tree - - This is the main entry point which causes us to add documentation - nodes for a symbol (which could be a 'command', 'object', 'event', - etc). We do this by calling 'visit' on the schema entity, which - will then call back into one of our visit_* methods, depending - on what kind of thing this symbol is. - """ - self._cur_doc = doc - entity.visit(self) - self._cur_doc = None - - def _start_new_heading(self, heading, level): - """Start a new heading at the specified heading level - - Create a new section whose title is 'heading' and which is placed - in the docutils node tree as a child of the most recent level-1 - heading. Subsequent document sections (commands, freeform doc chunks, - etc) will be placed as children of this new heading section. - """ - if len(self._active_headings) < level: - raise QAPISemError(self._cur_doc.info, - 'Level %d subheading found outside a ' - 'level %d heading' - % (level, level - 1)) - snode = self._make_section(heading) - self._active_headings[level - 1] += snode - self._active_headings = self._active_headings[:level] - self._active_headings.append(snode) - return snode - - def _add_node_to_current_heading(self, node): - """Add the node to whatever the current active heading is""" - self._active_headings[-1] += node - - def freeform(self, doc): - """Add a piece of 'freeform' documentation to the document tree - - A 'freeform' document chunk doesn't relate to any particular - symbol (for instance, it could be an introduction). - - If the freeform document starts with a line of the form - '= Heading text', this is a section or subsection heading, with - the heading level indicated by the number of '=' signs. - """ - - # QAPIDoc documentation says free-form documentation blocks - # must have only a body section, nothing else. - assert not doc.sections - assert not doc.args - assert not doc.features - self._cur_doc = doc - - text = doc.body.text - if re.match(r'=+ ', text): - # Section/subsection heading (if present, will always be - # the first line of the block) - (heading, _, text) = text.partition('\n') - (leader, _, heading) = heading.partition(' ') - node = self._start_new_heading(heading, len(leader)) - if text == '': - return - else: - node = nodes.container() - - self._parse_text_into_node(text, node) - self._cur_doc = None - - def _parse_text_into_node(self, doctext, node): - """Parse a chunk of QAPI-doc-format text into the node - - The doc comment can contain most inline rST markup, including - bulleted and enumerated lists. - As an extra permitted piece of markup, @var will be turned - into ``var``. - """ - - # Handle the "@var means ``var`` case - doctext = re.sub(r'@([\w-]+)', r'``\1``', doctext) - - rstlist = ViewList() - for line in doctext.splitlines(): - # The reported line number will always be that of the start line - # of the doc comment, rather than the actual location of the error. - # Being more precise would require overhaul of the QAPIDoc class - # to track lines more exactly within all the sub-parts of the doc - # comment, as well as counting lines here. - rstlist.append(line, self._cur_doc.info.fname, - self._cur_doc.info.line) - # Append a blank line -- in some cases rST syntax errors get - # attributed to the line after one with actual text, and if there - # isn't anything in the ViewList corresponding to that then Sphinx - # 1.6's AutodocReporter will then misidentify the source/line location - # in the error message (usually attributing it to the top-level - # .rst file rather than the offending .json file). The extra blank - # line won't affect the rendered output. - rstlist.append("", self._cur_doc.info.fname, self._cur_doc.info.line) - self._sphinx_directive.do_parse(rstlist, node) - - def get_document_node(self): - """Return the root docutils node which makes up the document""" - return self._top_node diff --git a/python/tests/qapi-isort.sh b/python/tests/qapi-isort.sh index 78dd947f68..067c16d5d9 100755 --- a/python/tests/qapi-isort.sh +++ b/python/tests/qapi-isort.sh @@ -3,6 +3,6 @@ python3 -m isort --sp . -c ../scripts/qapi/ # Force isort to recognize "compat" as a local module and not third-party -python3 -m isort --sp . -c -p compat -p qapidoc_legacy \ +python3 -m isort --sp . -c -p compat \ ../docs/sphinx/qapi_domain.py \ ../docs/sphinx/qapidoc.py diff --git a/tests/qapi-schema/doc-good.txt b/tests/qapi-schema/doc-good.txt index 17a1d56ef1..74b73681d3 100644 --- a/tests/qapi-schema/doc-good.txt +++ b/tests/qapi-schema/doc-good.txt @@ -1,6 +1,8 @@ Section ******* +Just text, no heading. + Subsection ========== @@ -35,249 +37,145 @@ Example: -> in <- out Examples: - *verbatim* - {braces} +Enum Enum + *Availability*: "IFCOND" -"Enum" (Enum) -------------- - - -Values -~~~~~~ - -"one" (**If: **"IFONE") - The _one_ {and only}, description on the same line - -"two" - Not documented - - -Features -~~~~~~~~ - -"enum-feat" - Also _one_ {and only} - -"enum-member-feat" - a member feature - -"two" is undocumented - - -If -~~ - -"IFCOND" - - -"Base" (Object) ---------------- - - -Members -~~~~~~~ - -"base1": "Enum" - description starts on a new line, minimally indented - - -If -~~ - -"IFALL1 and IFALL2" - - -"Variant1" (Object) -------------------- - -A paragraph - -Another paragraph - -"var1" is undocumented + Values: + * **one** -- The _one_ {and only}, description on the same line + * **two** -- Not documented -Members -~~~~~~~ + Features: + * **enum-feat** -- Also _one_ {and only} -"var1": "string" (**If: **"IFSTR") - Not documented + * **enum-member-feat** -- a member feature + "two" is undocumented -Features -~~~~~~~~ +Object Base + *Availability*: "IFALL1 and IFALL2" -"variant1-feat" - a feature + Members: + * **base1** ("Enum") -- description starts on a new line, + minimally indented -"member-feat" - a member feature +Object Variant1 + A paragraph -"Variant2" (Object) -------------------- + Another paragraph + "var1" is undocumented -"Object" (Object) ------------------ + Members: + * **var1** ("string") -- Not documented + Features: + * **variant1-feat** -- a feature -Members -~~~~~~~ + * **member-feat** -- a member feature -The members of "Base" -The members of "Variant1" when "base1" is ""one"" -The members of "Variant2" when "base1" is ""two"" (**If: **"IFONE or -IFTWO") +Object Variant2 -Features -~~~~~~~~ +Object Object -"union-feat1" - a feature + Members: + * The members of "Base". + * When "base1" is "one": The members of "Variant1". -"Alternate" (Alternate) ------------------------ + * When "base1" is "two": The members of "Variant2". + Features: + * **union-feat1** -- a feature -Members -~~~~~~~ +Alternate Alternate + *Availability*: "not (IFONE or IFTWO)" -"i": "int" - description starts on the same line remainder indented the same "b" - is undocumented + Alternatives: + * **i** ("int") -- description starts on the same line remainder + indented the same "b" is undocumented -"b": "boolean" - Not documented + * **b** ("boolean") -- Not documented - -Features -~~~~~~~~ - -"alt-feat" - a feature - - -If -~~ - -"not (IFONE or IFTWO)" + Features: + * **alt-feat** -- a feature Another subsection ================== +Command cmd (Since: 2.10) -"cmd" (Command) ---------------- - - -Arguments -~~~~~~~~~ - -"arg1": "int" - description starts on a new line, indented - -"arg2": "string" (optional) - description starts on the same line remainder indented differently - -"arg3": "boolean" - Not documented - - -Features -~~~~~~~~ - -"cmd-feat1" - a feature + Arguments: + * **arg1** ("int") -- description starts on a new line, indented -"cmd-feat2" - another feature - -Note: - - "arg3" is undocumented - - -Returns -~~~~~~~ - -"Object" - - -Errors -~~~~~~ - -some - -Notes: - -* Lorem ipsum dolor sit amet - -* Ut enim ad minim veniam - -Duis aute irure dolor + * **arg2** ("string", *optional*) -- description starts on the + same line remainder indented differently -Example: Ideal fast-food burger situation: + * **arg3** ("boolean") -- Not documented - -> "in" - <- "out" + Features: + * **cmd-feat1** -- a feature -Examples: + * **cmd-feat2** -- another feature - - Not a QMP code block - - Merely a preformatted code block literal - It isn't even an rST list. - - *verbatim* - - {braces} + Note: -Note:: - Ceci n'est pas une note + "arg3" is undocumented + Return: + "Object" -- "Object" -Since -~~~~~ + Errors: + some -2.10 + Notes: + * Lorem ipsum dolor sit amet -"cmd-boxed" (Command) ---------------------- + * Ut enim ad minim veniam -If you're bored enough to read this, go see a video of boxed cats + Duis aute irure dolor + Example: Ideal fast-food burger situation: -Arguments -~~~~~~~~~ + -> "in" + <- "out" -The members of "Object" + Examples: -Features -~~~~~~~~ + - Not a QMP code block + - Merely a preformatted code block literal + It isn't even an rST list. + - *verbatim* + - {braces} -"cmd-feat1" - a feature + Note:: + Ceci n'est pas une note -"cmd-feat2" - another feature +Command cmd-boxed -Example:: + If you're bored enough to read this, go see a video of boxed cats - -> "this example" + Arguments: + * The members of "Object". - <- ... has no title ... + Features: + * **cmd-feat1** -- a feature + * **cmd-feat2** -- another feature -"EVT_BOXED" (Event) -------------------- + Example:: + -> "this example" -Arguments -~~~~~~~~~ + <- ... has no title ... -The members of "Object" +Event EVT_BOXED -Features -~~~~~~~~ + Members: + * The members of "Object". -"feat3" - a feature + Features: + * **feat3** -- a feature From 6c10778826a873b9012d95e63298a6f879debcaa Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 18 Jun 2025 12:53:52 -0400 Subject: [PATCH 2234/2760] docs/sphinx: remove special parsing for freeform sections Remove the QAPI doc section heading syntax, use plain rST section headings instead. Tests and documentation are updated to match. Interestingly, Plain rST headings work fine before this patch, except for over- and underlining with '=', which the doc parser rejected as invalid QAPI doc section heading in free-form comments. Signed-off-by: John Snow Message-ID: <20250618165353.1980365-5-jsnow@redhat.com> Reviewed-by: Markus Armbruster [Add more detail to commit message] Signed-off-by: Markus Armbruster --- docs/devel/qapi-code-gen.rst | 28 ++++++++++++++------- docs/interop/firmware.json | 4 ++- docs/interop/vhost-user.json | 4 ++- docs/sphinx/qapidoc.py | 37 +--------------------------- qapi/acpi.json | 4 ++- qapi/audio.json | 4 ++- qapi/authz.json | 4 ++- qapi/block-core.json | 3 ++- qapi/block-export.json | 3 ++- qapi/block.json | 7 ++++-- qapi/char.json | 4 ++- qapi/common.json | 4 ++- qapi/compat.json | 4 ++- qapi/control.json | 4 ++- qapi/crypto.json | 4 ++- qapi/cryptodev.json | 4 ++- qapi/cxl.json | 4 ++- qapi/dump.json | 4 ++- qapi/ebpf.json | 4 ++- qapi/error.json | 4 ++- qapi/introspect.json | 4 ++- qapi/job.json | 4 ++- qapi/machine-common.json | 4 ++- qapi/machine.json | 4 ++- qapi/migration.json | 4 ++- qapi/misc.json | 4 ++- qapi/net.json | 4 ++- qapi/pci.json | 4 ++- qapi/qapi-schema.json | 4 ++- qapi/qdev.json | 4 ++- qapi/qom.json | 4 ++- qapi/replay.json | 4 ++- qapi/rocker.json | 4 ++- qapi/run-state.json | 4 ++- qapi/sockets.json | 4 ++- qapi/stats.json | 4 ++- qapi/tpm.json | 4 ++- qapi/trace.json | 4 ++- qapi/transaction.json | 4 ++- qapi/uefi.json | 4 ++- qapi/ui.json | 14 ++++++++--- qapi/vfio.json | 4 ++- qapi/virtio.json | 4 ++- qapi/yank.json | 4 ++- scripts/qapi/parser.py | 7 ------ storage-daemon/qapi/qapi-schema.json | 8 ++++-- tests/qapi-schema/doc-good.json | 10 +++++--- tests/qapi-schema/doc-good.out | 10 +++++--- 48 files changed, 173 insertions(+), 106 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 231cc0fecf..dfdbeac5a5 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -876,25 +876,35 @@ structuring content. Headings and subheadings ~~~~~~~~~~~~~~~~~~~~~~~~ -A free-form documentation comment containing a line which starts with -some ``=`` symbols and then a space defines a section heading:: +Free-form documentation does not start with ``@SYMBOL`` and can contain +arbitrary rST markup. Headings can be marked up using the standard rST +syntax:: ## - # = This is a top level heading + # ************************* + # This is a level 2 heading + # ************************* # # This is a free-form comment which will go under the # top level heading. ## ## - # == This is a second level heading + # This is a third level heading + # ============================== + # + # Level 4 + # _______ + # + # Level 5 + # ^^^^^^^ + # + # Level 6 + # """"""" ## -A heading line must be the first line of the documentation -comment block. - -Section headings must always be correctly nested, so you can only -define a third-level heading inside a second-level heading, and so on. +Level 1 headings are reserved for use by the generated documentation +page itself, leaving level 2 as the highest level that should be used. Documentation markup diff --git a/docs/interop/firmware.json b/docs/interop/firmware.json index 745d21d822..f46fdedfa8 100644 --- a/docs/interop/firmware.json +++ b/docs/interop/firmware.json @@ -11,7 +11,9 @@ # later. See the COPYING file in the top-level directory. ## -# = Firmware +# ******** +# Firmware +# ******** ## { 'pragma': { diff --git a/docs/interop/vhost-user.json b/docs/interop/vhost-user.json index b6ade9e493..095eb99cf7 100644 --- a/docs/interop/vhost-user.json +++ b/docs/interop/vhost-user.json @@ -10,7 +10,9 @@ # later. See the COPYING file in the top-level directory. ## -# = vhost user backend discovery & capabilities +# ******************************************* +# vhost user backend discovery & capabilities +# ******************************************* ## ## diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index d5d2b5eeb5..b794cde697 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -399,44 +399,9 @@ def visit_module(self, path: str) -> None: self.ensure_blank_line() def visit_freeform(self, doc: QAPIDoc) -> None: - # TODO: Once the old qapidoc transformer is deprecated, freeform - # sections can be updated to pure rST, and this transformed removed. - # - # For now, translate our micro-format into rST. Code adapted - # from Peter Maydell's freeform(). - assert len(doc.all_sections) == 1, doc.all_sections body = doc.all_sections[0] - text = self.reformat_arobase(body.text) - info = doc.info - - if re.match(r"=+ ", text): - # Section/subsection heading (if present, will always be the - # first line of the block) - (heading, _, text) = text.partition("\n") - (leader, _, heading) = heading.partition(" ") - # Implicit +1 for heading in the containing .rst doc - level = len(leader) + 1 - - # https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#sections - markers = ' #*=_^"' - overline = level <= 2 - marker = markers[level] - - self.ensure_blank_line() - # This credits all 2 or 3 lines to the single source line. - if overline: - self.add_line(marker * len(heading), info) - self.add_line(heading, info) - self.add_line(marker * len(heading), info) - self.ensure_blank_line() - - # Eat blank line(s) and advance info - trimmed = text.lstrip("\n") - text = trimmed - info = info.next_line(len(text) - len(trimmed) + 1) - - self.add_lines(text, info) + self.add_lines(self.reformat_arobase(body.text), doc.info) self.ensure_blank_line() def visit_entity(self, ent: QAPISchemaDefinition) -> None: diff --git a/qapi/acpi.json b/qapi/acpi.json index 2d53b82365..90f8f564fd 100644 --- a/qapi/acpi.json +++ b/qapi/acpi.json @@ -6,7 +6,9 @@ # SPDX-License-Identifier: GPL-2.0-or-later ## -# = ACPI +# **** +# ACPI +# **** ## ## diff --git a/qapi/audio.json b/qapi/audio.json index 16de231f6d..6a22ca384a 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -7,7 +7,9 @@ # See the COPYING file in the top-level directory. ## -# = Audio +# ***** +# Audio +# ***** ## ## diff --git a/qapi/authz.json b/qapi/authz.json index 7fc6e3032e..ad1b4b3af0 100644 --- a/qapi/authz.json +++ b/qapi/authz.json @@ -2,7 +2,9 @@ # vim: filetype=python ## -# = User authorization +# ****************** +# User authorization +# ****************** ## ## diff --git a/qapi/block-core.json b/qapi/block-core.json index 1df6644f0d..8b413946ff 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -2,7 +2,8 @@ # vim: filetype=python ## -# == Block core (VM unrelated) +# Block core (VM unrelated) +# ========================= ## { 'include': 'common.json' } diff --git a/qapi/block-export.json b/qapi/block-export.json index ed4deb54db..2241bfecf2 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -2,7 +2,8 @@ # vim: filetype=python ## -# == Block device exports +# Block device exports +# ==================== ## { 'include': 'sockets.json' } diff --git a/qapi/block.json b/qapi/block.json index 1490a1a17f..2d54a81c36 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -2,13 +2,16 @@ # vim: filetype=python ## -# = Block devices +# ************* +# Block devices +# ************* ## { 'include': 'block-core.json' } ## -# == Additional block stuff (VM related) +# Additional block stuff (VM related) +# =================================== ## ## diff --git a/qapi/char.json b/qapi/char.json index df6e325e2e..f38d04986d 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -3,7 +3,9 @@ # ## -# = Character devices +# ***************** +# Character devices +# ***************** ## { 'include': 'sockets.json' } diff --git a/qapi/common.json b/qapi/common.json index 0e3a0bbbfb..af7e3d618a 100644 --- a/qapi/common.json +++ b/qapi/common.json @@ -2,7 +2,9 @@ # vim: filetype=python ## -# = Common data types +# ***************** +# Common data types +# ***************** ## ## diff --git a/qapi/compat.json b/qapi/compat.json index 42034d9368..90b8d51cf2 100644 --- a/qapi/compat.json +++ b/qapi/compat.json @@ -2,7 +2,9 @@ # vim: filetype=python ## -# = Compatibility policy +# ******************** +# Compatibility policy +# ******************** ## ## diff --git a/qapi/control.json b/qapi/control.json index 34b733f63b..ab0b3a3bbe 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -3,7 +3,9 @@ # ## -# = QMP monitor control +# ******************* +# QMP monitor control +# ******************* ## ## diff --git a/qapi/crypto.json b/qapi/crypto.json index 9ec6301e18..21006de36c 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -3,7 +3,9 @@ # ## -# = Cryptography +# ************ +# Cryptography +# ************ ## ## diff --git a/qapi/cryptodev.json b/qapi/cryptodev.json index b13db26403..1f49e1822c 100644 --- a/qapi/cryptodev.json +++ b/qapi/cryptodev.json @@ -5,7 +5,9 @@ # See the COPYING file in the top-level directory. ## -# = Cryptography devices +# ******************** +# Cryptography devices +# ******************** ## ## diff --git a/qapi/cxl.json b/qapi/cxl.json index 8f2e9237b1..52cc5d4f33 100644 --- a/qapi/cxl.json +++ b/qapi/cxl.json @@ -2,7 +2,9 @@ # vim: filetype=python ## -# = CXL devices +# *********** +# CXL devices +# *********** ## ## diff --git a/qapi/dump.json b/qapi/dump.json index d0ba1f0596..0642ca157b 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -5,7 +5,9 @@ # See the COPYING file in the top-level directory. ## -# = Dump guest memory +# ***************** +# Dump guest memory +# ***************** ## ## diff --git a/qapi/ebpf.json b/qapi/ebpf.json index db19ae850f..d45054e666 100644 --- a/qapi/ebpf.json +++ b/qapi/ebpf.json @@ -5,7 +5,9 @@ # See the COPYING file in the top-level directory. ## -# = eBPF Objects +# ************ +# eBPF Objects +# ************ # # eBPF object is an ELF binary that contains the eBPF program and eBPF # map description(BTF). Overall, eBPF object should contain the diff --git a/qapi/error.json b/qapi/error.json index 135c1e8231..54cb02fb88 100644 --- a/qapi/error.json +++ b/qapi/error.json @@ -2,7 +2,9 @@ # vim: filetype=python ## -# = QMP errors +# ********** +# QMP errors +# ********** ## ## diff --git a/qapi/introspect.json b/qapi/introspect.json index e9e0297282..26d8839f19 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -10,7 +10,9 @@ # See the COPYING file in the top-level directory. ## -# = QMP introspection +# ***************** +# QMP introspection +# ***************** ## ## diff --git a/qapi/job.json b/qapi/job.json index 126fa5ce60..16b280f52f 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -2,7 +2,9 @@ # vim: filetype=python ## -# = Background jobs +# *************** +# Background jobs +# *************** ## ## diff --git a/qapi/machine-common.json b/qapi/machine-common.json index 298e51f373..0f01599130 100644 --- a/qapi/machine-common.json +++ b/qapi/machine-common.json @@ -5,7 +5,9 @@ # See the COPYING file in the top-level directory. ## -# = Common machine types +# ******************** +# Common machine types +# ******************** ## ## diff --git a/qapi/machine.json b/qapi/machine.json index f712e7da6d..af00a20b1f 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -5,7 +5,9 @@ # See the COPYING file in the top-level directory. ## -# = Machines +# ******** +# Machines +# ******** ## { 'include': 'common.json' } diff --git a/qapi/migration.json b/qapi/migration.json index 2d39a8f748..3bf97df5d8 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -3,7 +3,9 @@ # ## -# = Migration +# ********* +# Migration +# ********* ## { 'include': 'common.json' } diff --git a/qapi/misc.json b/qapi/misc.json index 4b9e601cfa..a180c16db2 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -3,7 +3,9 @@ # ## -# = Miscellanea +# *********** +# Miscellanea +# *********** ## { 'include': 'common.json' } diff --git a/qapi/net.json b/qapi/net.json index 97ea183981..3b03843c16 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -3,7 +3,9 @@ # ## -# = Net devices +# *********** +# Net devices +# *********** ## { 'include': 'sockets.json' } diff --git a/qapi/pci.json b/qapi/pci.json index dc85a41d28..a8671cd9ac 100644 --- a/qapi/pci.json +++ b/qapi/pci.json @@ -6,7 +6,9 @@ # SPDX-License-Identifier: GPL-2.0-or-later ## -# = PCI +# *** +# PCI +# *** ## ## diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index a8f66163cb..49b9a0267d 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -1,7 +1,9 @@ # -*- Mode: Python -*- # vim: filetype=python ## -# = Introduction +# ************ +# Introduction +# ************ # # This manual describes the commands and events supported by the QEMU # Monitor Protocol (QMP). diff --git a/qapi/qdev.json b/qapi/qdev.json index 32c7d10046..441ed518b8 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -5,7 +5,9 @@ # See the COPYING file in the top-level directory. ## -# = Device infrastructure (qdev) +# **************************** +# Device infrastructure (qdev) +# **************************** ## { 'include': 'qom.json' } diff --git a/qapi/qom.json b/qapi/qom.json index b133b06447..f68f72fbbc 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -10,7 +10,9 @@ { 'include': 'crypto.json' } ## -# = QEMU Object Model (QOM) +# *********************** +# QEMU Object Model (QOM) +# *********************** ## ## diff --git a/qapi/replay.json b/qapi/replay.json index 35e0c4a692..e46c5c1d3f 100644 --- a/qapi/replay.json +++ b/qapi/replay.json @@ -3,7 +3,9 @@ # ## -# = Record/replay +# ************* +# Record/replay +# ************* ## { 'include': 'common.json' } diff --git a/qapi/rocker.json b/qapi/rocker.json index 0c7ef1f77c..e494964952 100644 --- a/qapi/rocker.json +++ b/qapi/rocker.json @@ -2,7 +2,9 @@ # vim: filetype=python ## -# = Rocker switch device +# ******************** +# Rocker switch device +# ******************** ## ## diff --git a/qapi/run-state.json b/qapi/run-state.json index fd09beb35c..083a3c5eb3 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -3,7 +3,9 @@ # ## -# = VM run state +# ************ +# VM run state +# ************ ## ## diff --git a/qapi/sockets.json b/qapi/sockets.json index f9f559daba..b5f4a8fecd 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -2,7 +2,9 @@ # vim: filetype=python ## -# = Socket data types +# ***************** +# Socket data types +# ***************** ## ## diff --git a/qapi/stats.json b/qapi/stats.json index 8902ef94e0..78b88881ea 100644 --- a/qapi/stats.json +++ b/qapi/stats.json @@ -9,7 +9,9 @@ # SPDX-License-Identifier: GPL-2.0-or-later ## -# = Statistics +# ********** +# Statistics +# ********** ## ## diff --git a/qapi/tpm.json b/qapi/tpm.json index a16a72edb9..e66b107f1d 100644 --- a/qapi/tpm.json +++ b/qapi/tpm.json @@ -3,7 +3,9 @@ # ## -# = TPM (trusted platform module) devices +# ************************************* +# TPM (trusted platform module) devices +# ************************************* ## ## diff --git a/qapi/trace.json b/qapi/trace.json index eb5f63f513..d08c9c6a88 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -7,7 +7,9 @@ # See the COPYING file in the top-level directory. ## -# = Tracing +# ******* +# Tracing +# ******* ## ## diff --git a/qapi/transaction.json b/qapi/transaction.json index 9d9e7af26c..927035f5a7 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -3,7 +3,9 @@ # ## -# = Transactions +# ************ +# Transactions +# ************ ## { 'include': 'block-core.json' } diff --git a/qapi/uefi.json b/qapi/uefi.json index 6592183d6c..a206c2e953 100644 --- a/qapi/uefi.json +++ b/qapi/uefi.json @@ -3,7 +3,9 @@ # ## -# = UEFI Variable Store +# ******************* +# UEFI Variable Store +# ******************* # # The QEMU efi variable store implementation (hw/uefi/) uses this to # store non-volatile variables in json format on disk. diff --git a/qapi/ui.json b/qapi/ui.json index 514fa159b1..f5e77ae19d 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -3,7 +3,9 @@ # ## -# = Remote desktop +# ************** +# Remote desktop +# ************** ## { 'include': 'common.json' } @@ -200,7 +202,8 @@ 'if': 'CONFIG_PIXMAN' } ## -# == Spice +# Spice +# ===== ## ## @@ -461,7 +464,8 @@ 'if': 'CONFIG_SPICE' } ## -# == VNC +# VNC +# === ## ## @@ -794,7 +798,9 @@ 'if': 'CONFIG_VNC' } ## -# = Input +# ***** +# Input +# ***** ## ## diff --git a/qapi/vfio.json b/qapi/vfio.json index b53b7caecd..a1a9c5b673 100644 --- a/qapi/vfio.json +++ b/qapi/vfio.json @@ -3,7 +3,9 @@ # ## -# = VFIO devices +# ************ +# VFIO devices +# ************ ## ## diff --git a/qapi/virtio.json b/qapi/virtio.json index 73df718a26..3cac0774f7 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -3,7 +3,9 @@ # ## -# = Virtio devices +# ************** +# Virtio devices +# ************** ## ## diff --git a/qapi/yank.json b/qapi/yank.json index 30f46c97c9..d13a8e9117 100644 --- a/qapi/yank.json +++ b/qapi/yank.json @@ -3,7 +3,9 @@ # ## -# = Yank feature +# ************ +# Yank feature +# ************ ## ## diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index 949d9e8bff..aad7e249f8 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -597,22 +597,15 @@ def get_doc(self) -> 'QAPIDoc': # Free-form documentation doc = QAPIDoc(info) doc.ensure_untagged_section(self.info) - first = True while line is not None: if match := self._match_at_name_colon(line): raise QAPIParseError( self, "'@%s:' not allowed in free-form documentation" % match.group(1)) - if line.startswith('='): - if not first: - raise QAPIParseError( - self, - "'=' heading must come first in a comment block") doc.append_line(line) self.accept(False) line = self.get_doc_line() - first = False self.accept() doc.end() diff --git a/storage-daemon/qapi/qapi-schema.json b/storage-daemon/qapi/qapi-schema.json index 0427594c98..478e7a92b2 100644 --- a/storage-daemon/qapi/qapi-schema.json +++ b/storage-daemon/qapi/qapi-schema.json @@ -14,7 +14,9 @@ # storage daemon. ## -# = Introduction +# ************ +# Introduction +# ************ # # This manual describes the commands and events supported by the QEMU # storage daemon QMP. @@ -51,7 +53,9 @@ { 'include': '../../qapi/job.json' } ## -# = Block devices +# ************* +# Block devices +# ************* ## { 'include': '../../qapi/block-core.json' } { 'include': '../../qapi/block-export.json' } diff --git a/tests/qapi-schema/doc-good.json b/tests/qapi-schema/doc-good.json index 14b808f909..fac13425b7 100644 --- a/tests/qapi-schema/doc-good.json +++ b/tests/qapi-schema/doc-good.json @@ -8,7 +8,9 @@ 'documentation-exceptions': [ 'Enum', 'Variant1', 'Alternate', 'cmd' ] } } ## -# = Section +# ******* +# Section +# ******* ## ## @@ -16,7 +18,8 @@ ## ## -# == Subsection +# Subsection +# ========== # # *with emphasis* # @var {in braces} @@ -144,7 +147,8 @@ 'if': { 'not': { 'any': [ 'IFONE', 'IFTWO' ] } } } ## -# == Another subsection +# Another subsection +# ================== ## ## diff --git a/tests/qapi-schema/doc-good.out b/tests/qapi-schema/doc-good.out index dc8352eed4..04a5507264 100644 --- a/tests/qapi-schema/doc-good.out +++ b/tests/qapi-schema/doc-good.out @@ -55,13 +55,16 @@ event EVT_BOXED Object feature feat3 doc freeform body= -= Section +******* +Section +******* doc freeform body= Just text, no heading. doc freeform body= -== Subsection +Subsection +========== *with emphasis* @var {in braces} @@ -155,7 +158,8 @@ description starts on the same line a feature doc freeform body= -== Another subsection +Another subsection +================== doc symbol=cmd body= From 62c4dc4b69ef7dcfcc476913a9c5fc15329e0290 Mon Sep 17 00:00:00 2001 From: John Snow Date: Wed, 18 Jun 2025 12:53:53 -0400 Subject: [PATCH 2235/2760] qapi: lift restriction on using '=' in doc blocks We reject lines starting with '=' in definition documentation. This made sense when such lines were headings in free-form documentation, but not in definition documentation. Before the previous commit, lines starting with '=' were headings in free-form documentation, and rejected in definition documentation, where such headings could not work. The previous commit dropped the headings feature from free-form documentation, because we can simply use plain rST headings. Rejecting them in definition documentation no longer makes sense, so drop that, too. Signed-off-by: John Snow Message-ID: <20250618165353.1980365-6-jsnow@redhat.com> Reviewed-by: Markus Armbruster [Amend commit message to explain why] Signed-off-by: Markus Armbruster --- scripts/qapi/parser.py | 4 ---- tests/qapi-schema/doc-bad-section.err | 1 - tests/qapi-schema/doc-bad-section.json | 10 ---------- tests/qapi-schema/doc-bad-section.out | 0 tests/qapi-schema/meson.build | 1 - 5 files changed, 16 deletions(-) delete mode 100644 tests/qapi-schema/doc-bad-section.err delete mode 100644 tests/qapi-schema/doc-bad-section.json delete mode 100644 tests/qapi-schema/doc-bad-section.out diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index aad7e249f8..d43a123cd7 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -584,10 +584,6 @@ def get_doc(self) -> 'QAPIDoc': doc.append_line(text) line = self.get_doc_indented(doc) no_more_args = True - elif line.startswith('='): - raise QAPIParseError( - self, - "unexpected '=' markup in definition documentation") else: # plain paragraph doc.ensure_untagged_section(self.info) diff --git a/tests/qapi-schema/doc-bad-section.err b/tests/qapi-schema/doc-bad-section.err deleted file mode 100644 index 785cacc08c..0000000000 --- a/tests/qapi-schema/doc-bad-section.err +++ /dev/null @@ -1 +0,0 @@ -doc-bad-section.json:5:1: unexpected '=' markup in definition documentation diff --git a/tests/qapi-schema/doc-bad-section.json b/tests/qapi-schema/doc-bad-section.json deleted file mode 100644 index 8175d95867..0000000000 --- a/tests/qapi-schema/doc-bad-section.json +++ /dev/null @@ -1,10 +0,0 @@ -# = section within an expression comment - -## -# @Enum: -# == No good here -# @one: The _one_ {and only} -# -# @two is undocumented -## -{ 'enum': 'Enum', 'data': [ 'one', 'two' ] } diff --git a/tests/qapi-schema/doc-bad-section.out b/tests/qapi-schema/doc-bad-section.out deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/tests/qapi-schema/meson.build b/tests/qapi-schema/meson.build index 9577178b6f..c47025d16d 100644 --- a/tests/qapi-schema/meson.build +++ b/tests/qapi-schema/meson.build @@ -61,7 +61,6 @@ schemas = [ 'doc-bad-event-arg.json', 'doc-bad-feature.json', 'doc-bad-indent.json', - 'doc-bad-section.json', 'doc-bad-symbol.json', 'doc-bad-union-member.json', 'doc-before-include.json', From c13ceeb42df5452690fc530269696f59da921b31 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 8 Jul 2025 09:28:27 +0200 Subject: [PATCH 2236/2760] qapi: Clean up "This command will do ..." command descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use imperative mood "Do ..." instead. Signed-off-by: Markus Armbruster Message-ID: <20250708072828.105185-2-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé --- qapi/control.json | 9 +++++---- qapi/misc-i386.json | 11 +++++------ qapi/qom.json | 10 ++++------ 3 files changed, 14 insertions(+), 16 deletions(-) diff --git a/qapi/control.json b/qapi/control.json index ab0b3a3bbe..5fed0701f8 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -160,10 +160,11 @@ ## # @quit: # -# This command will cause the QEMU process to exit gracefully. While -# every attempt is made to send the QMP response before terminating, -# this is not guaranteed. When using this interface, a premature EOF -# would not be unexpected. +# Request graceful QEMU process termination. +# +# While every attempt is made to send the QMP response before +# terminating, this is not guaranteed. When using this interface, a +# premature EOF would not be unexpected. # # Since: 0.14 # diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json index 5fefa0a484..b53ed39288 100644 --- a/qapi/misc-i386.json +++ b/qapi/misc-i386.json @@ -6,9 +6,9 @@ ## # @rtc-reset-reinjection: # -# This command will reset the RTC interrupt reinjection backlog. Can -# be used if another mechanism to synchronize guest time is in effect, -# for example QEMU guest agent's guest-set-time command. +# Reset the RTC interrupt reinjection backlog. Can be used if another +# mechanism to synchronize guest time is in effect, for example QEMU +# guest agent's guest-set-time command. # # Use of this command is only applicable for x86 machines with an RTC, # and on other machines will silently return without performing any @@ -233,8 +233,7 @@ ## # @sev-inject-launch-secret: # -# This command injects a secret blob into memory of a SEV/SEV-ES -# guest. +# Inject a secret blob into a SEV/SEV-ES guest's memory. # # This is only valid on x86 machines configured with KVM and the # 'sev-guest' confidential virtualization object. SEV-SNP guests do @@ -272,7 +271,7 @@ ## # @query-sev-attestation-report: # -# This command is used to get the SEV attestation report. +# Get the SEV attestation report. # # This is only valid on x86 machines configured with KVM and the # 'sev-guest' confidential virtualization object. The attestation diff --git a/qapi/qom.json b/qapi/qom.json index f68f72fbbc..6c38e865a6 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -50,8 +50,7 @@ ## # @qom-list: # -# This command will list any properties of a object given a path in -# the object model. +# List properties of a object given a path in the object model. # # @path: the path within the object model. See @qom-get for a # description of this parameter. @@ -78,8 +77,7 @@ ## # @qom-get: # -# This command will get a property from a object model path and return -# the value. +# Get a property value. # # @path: The path within the object model. There are two forms of # supported paths--absolute and partial paths. @@ -130,7 +128,7 @@ ## # @qom-set: # -# This command will set a property from a object model path. +# Set a property value. # # @path: see @qom-get for a description of this parameter # @@ -173,7 +171,7 @@ ## # @qom-list-types: # -# This command will return a list of types given search parameters +# Return a list of types given search parameters. # # @implements: if specified, only return types that implement this # type name From 92227370eb3d844528e6d7bc2fb42525d8245eaf Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 8 Jul 2025 09:28:28 +0200 Subject: [PATCH 2237/2760] qapi: Clean up a few Errors: sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the conventional "- If " phrasing, optionally with ", ". Signed-off-by: Markus Armbruster Message-ID: <20250708072828.105185-3-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé --- qapi/misc-i386.json | 7 +++---- qapi/qom.json | 4 ++-- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json index b53ed39288..24a2e143f6 100644 --- a/qapi/misc-i386.json +++ b/qapi/misc-i386.json @@ -283,10 +283,9 @@ # Returns: SevAttestationReport objects. # # Errors: -# - This will return an error if the attestation report is -# unavailable, either due to an invalid guest configuration -# or if the guest has not reached the required SEV state, -# GenericError +# - If the attestation report is unavailable, either due to an +# invalid guest configuration or because the guest has not +# reached the required SEV state, GenericError # # Since: 6.1 # diff --git a/qapi/qom.json b/qapi/qom.json index 6c38e865a6..fbf94b9e09 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -1261,7 +1261,7 @@ # Create a QOM object. # # Errors: -# - Error if @qom-type is not a valid class name +# - If @qom-type is not a valid class name # # Since: 2.0 # @@ -1283,7 +1283,7 @@ # @id: the name of the QOM object to remove # # Errors: -# - Error if @id is not a valid id for a QOM object +# - If @id is not a valid id for a QOM object # # Since: 2.0 # From a539cd26145c726fddd19eb0e2c20332960b0245 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 27 Jun 2025 11:51:26 +0800 Subject: [PATCH 2238/2760] i386/cpu: Mark EBX/ECX/EDX in CPUID 0x80000000 leaf as reserved for Intel Per SDM, 80000000H EAX Maximum Input Value for Extended Function CPUID Information. EBX Reserved. ECX Reserved. EDX Reserved. EBX/ECX/EDX in CPUID 0x80000000 leaf are reserved. Intel is using 0x0 leaf to encode vendor. Signed-off-by: Zhao Liu Reviewed-by: Tao Su Link: https://lore.kernel.org/r/20250627035129.2755537-2-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 4f0c973446..ae508fa962 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8280,9 +8280,15 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, break; case 0x80000000: *eax = env->cpuid_xlevel; - *ebx = env->cpuid_vendor1; - *edx = env->cpuid_vendor2; - *ecx = env->cpuid_vendor3; + + if (cpu->vendor_cpuid_only_v2 && + (IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { + *ebx = *ecx = *edx = 0; + } else { + *ebx = env->cpuid_vendor1; + *edx = env->cpuid_vendor2; + *ecx = env->cpuid_vendor3; + } break; case 0x80000001: *eax = env->cpuid_version; From 1c52c470baba1a2cc2d96e14c9f845ec3d2ea8c4 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 27 Jun 2025 11:51:27 +0800 Subject: [PATCH 2239/2760] i386/cpu: Mark CPUID 0x80000007[EBX] as reserved for Intel Per SDM, 80000007H EAX Reserved = 0. EBX Reserved = 0. ECX Reserved = 0. EDX Bits 07-00: Reserved = 0. Bit 08: Invariant TSC available if 1. Bits 31-09: Reserved = 0. EAX/EBX/ECX in CPUID 0x80000007 leaf are reserved for Intel. At present, EAX is reserved for AMD, too. And AMD hasn't used ECX in QEMU. So these 2 registers are both left as 0. Therefore, only fix the EBX and excode it as 0 for Intel. Signed-off-by: Zhao Liu Reviewed-by: Tao Su Link: https://lore.kernel.org/r/20250627035129.2755537-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index ae508fa962..533c9d9abc 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8376,7 +8376,11 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } case 0x80000007: *eax = 0; - *ebx = env->features[FEAT_8000_0007_EBX]; + if (cpu->vendor_cpuid_only_v2 && IS_INTEL_CPU(env)) { + *ebx = 0; + } else { + *ebx = env->features[FEAT_8000_0007_EBX]; + } *ecx = 0; *edx = env->features[FEAT_8000_0007_EDX]; break; From da84c011544b808b9ea3dface2292437dd29d053 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 27 Jun 2025 11:51:28 +0800 Subject: [PATCH 2240/2760] i386/cpu: Mark CPUID 0x80000008 ECX bits[0:7] & [12:15] as reserved for Intel/Zhaoxin Per SDM, 80000008H EAX Linear/Physical Address size. Bits 07-00: #Physical Address Bits*. Bits 15-08: #Linear Address Bits. Bits 31-16: Reserved = 0. EBX Bits 08-00: Reserved = 0. Bit 09: WBNOINVD is available if 1. Bits 31-10: Reserved = 0. ECX Reserved = 0. EDX Reserved = 0. ECX/EDX in CPUID 0x80000008 leaf are reserved. Currently, in QEMU, only ECX bits[0:7] and ECX bits[12:15] are encoded, and both are emulated in QEMU. Considering that Intel and Zhaoxin are already using the 0x1f leaf to describe CPU topology, which includes similar information, Intel and Zhaoxin will not implement ECX bits[0:7] and bits[12:15] of 0x80000008. Therefore, mark these two fields as reserved and clear them for Intel and Zhaoxin guests. Reviewed-by: Tao Su Tested-by: Yi Lai Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250714080859.1960104-3-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 533c9d9abc..1a2cae6ea1 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8393,6 +8393,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *eax |= (cpu->guest_phys_bits << 16); } *ebx = env->features[FEAT_8000_0008_EBX]; + + /* + * Don't emulate Bits [7:0] & Bits [15:12] for Intel/Zhaoxin, since + * they're using 0x1f leaf. + */ + if (cpu->vendor_cpuid_only_v2 && + (IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { + *ecx = *edx = 0; + break; + } + if (threads_per_pkg > 1) { /* * Bits 15:12 is "The number of bits in the initial From 1b74aa47c33f2aa37ab7339826e30abb22ca0a5f Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 12 Jul 2025 09:21:14 +0200 Subject: [PATCH 2241/2760] tests/functional: test_x86_cpu_model_versions: remove dead tests Tests that require machines older than 4.2 are now unconditionally skipped. Remove them if they test legacy behavior, or use the latest machine if they test current behavior. Signed-off-by: Paolo Bonzini --- .../functional/test_x86_cpu_model_versions.py | 110 ++---------------- 1 file changed, 12 insertions(+), 98 deletions(-) diff --git a/tests/functional/test_x86_cpu_model_versions.py b/tests/functional/test_x86_cpu_model_versions.py index bd18acd44f..36c968f1c0 100755 --- a/tests/functional/test_x86_cpu_model_versions.py +++ b/tests/functional/test_x86_cpu_model_versions.py @@ -72,44 +72,11 @@ def validate_variant_aliases(self, cpus): self.assertNotIn("EPYC-IBPB-v1", cpus, "EPYC-IBPB shouldn't be versioned") - def test_4_0_alias_compatibility(self): - """ - Check if pc-*-4.0 unversioned CPU model won't be reported as aliases - """ - self.set_machine('pc-i440fx-4.0') - # pc-*-4.0 won't expose non-versioned CPU models as aliases - # We do this to help management software to keep compatibility - # with older QEMU versions that didn't have the versioned CPU model - self.vm.add_args('-S') - self.vm.launch() - cpus = dict((m['name'], m) for m in - self.vm.cmd('query-cpu-definitions')) - - self.assertFalse(cpus['Cascadelake-Server']['static'], - 'unversioned Cascadelake-Server CPU model must not be static') - self.assertNotIn('alias-of', cpus['Cascadelake-Server'], - 'Cascadelake-Server must not be an alias') - self.assertNotIn('alias-of', cpus['Cascadelake-Server-v1'], - 'Cascadelake-Server-v1 must not be an alias') - - self.assertFalse(cpus['qemu64']['static'], - 'unversioned qemu64 CPU model must not be static') - self.assertNotIn('alias-of', cpus['qemu64'], - 'qemu64 must not be an alias') - self.assertNotIn('alias-of', cpus['qemu64-v1'], - 'qemu64-v1 must not be an alias') - - self.validate_variant_aliases(cpus) - - # On pc-*-4.0, no CPU model should be reported as an alias: - for name,c in cpus.items(): - self.assertNotIn('alias-of', c, "%s shouldn't be an alias" % (name)) - - def test_4_1_alias(self): + def test_unversioned_alias(self): """ Check if unversioned CPU model is an alias pointing to right version """ - self.set_machine('pc-i440fx-4.1') + self.set_machine('pc') self.vm.add_args('-S') self.vm.launch() @@ -133,7 +100,7 @@ def test_4_1_alias(self): self.validate_variant_aliases(cpus) - # On pc-*-4.1, -noTSX and -IBRS models should be aliases: + # On recent PC machines, -noTSX and -IBRS models should be aliases: self.assertEqual(cpus["Haswell"].get('alias-of'), "Haswell-v1", "Haswell must be an alias") @@ -247,8 +214,8 @@ def get_cpu_prop(self, prop): cpu_path = self.vm.cmd('query-cpus-fast')[0].get('qom-path') return self.vm.cmd('qom-get', path=cpu_path, property=prop) - def test_4_1(self): - self.set_machine('pc-i440fx-4.1') + def test(self): + self.set_machine('pc') # machine-type only: self.vm.add_args('-S') self.set_vm_arg('-cpu', @@ -256,80 +223,27 @@ def test_4_1(self): 'enforce=off') self.vm.launch() self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.1 + Cascadelake-Server should not have arch-capabilities') - - def test_4_0(self): - self.set_machine('pc-i440fx-4.0') - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server,x-force-features=on,check=off,' - 'enforce=off') - self.vm.launch() - self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server should not have arch-capabilities') - - def test_set_4_0(self): - self.set_machine('pc-i440fx-4.0') - # command line must override machine-type if CPU model is not versioned: - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server,x-force-features=on,check=off,' - 'enforce=off,+arch-capabilities') - self.vm.launch() - self.assertTrue(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server,+arch-capabilities should have arch-capabilities') + 'pc + Cascadelake-Server should not have arch-capabilities') - def test_unset_4_1(self): - self.set_machine('pc-i440fx-4.1') + def test_unset(self): + self.set_machine('pc') self.vm.add_args('-S') self.set_vm_arg('-cpu', 'Cascadelake-Server,x-force-features=on,check=off,' 'enforce=off,-arch-capabilities') self.vm.launch() self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.1 + Cascadelake-Server,-arch-capabilities should not have arch-capabilities') - - def test_v1_4_0(self): - self.set_machine('pc-i440fx-4.0') - # versioned CPU model overrides machine-type: - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server-v1,x-force-features=on,check=off,' - 'enforce=off') - self.vm.launch() - self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server-v1 should not have arch-capabilities') - - def test_v2_4_0(self): - self.set_machine('pc-i440fx-4.0') - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server-v2,x-force-features=on,check=off,' - 'enforce=off') - self.vm.launch() - self.assertTrue(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server-v2 should have arch-capabilities') - - def test_v1_set_4_0(self): - self.set_machine('pc-i440fx-4.0') - # command line must override machine-type and versioned CPU model: - self.vm.add_args('-S') - self.set_vm_arg('-cpu', - 'Cascadelake-Server-v1,x-force-features=on,check=off,' - 'enforce=off,+arch-capabilities') - self.vm.launch() - self.assertTrue(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.0 + Cascadelake-Server-v1,+arch-capabilities should have arch-capabilities') + 'pc + Cascadelake-Server,-arch-capabilities should not have arch-capabilities') - def test_v2_unset_4_1(self): - self.set_machine('pc-i440fx-4.1') + def test_v2_unset(self): + self.set_machine('pc') self.vm.add_args('-S') self.set_vm_arg('-cpu', 'Cascadelake-Server-v2,x-force-features=on,check=off,' 'enforce=off,-arch-capabilities') self.vm.launch() self.assertFalse(self.get_cpu_prop('arch-capabilities'), - 'pc-i440fx-4.1 + Cascadelake-Server-v2,-arch-capabilities should not have arch-capabilities') + 'pc + Cascadelake-Server-v2,-arch-capabilities should not have arch-capabilities') if __name__ == '__main__': QemuSystemTest.main() From c8958b7eb494d06a209b1befb6c34edfbce68867 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Sat, 12 Jul 2025 10:02:01 +0200 Subject: [PATCH 2242/2760] tests/vm: bump FreeBSD image to 14.3 Signed-off-by: Paolo Bonzini --- tests/vm/freebsd | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/vm/freebsd b/tests/vm/freebsd index 74b3b1e520..2e96c9eba5 100755 --- a/tests/vm/freebsd +++ b/tests/vm/freebsd @@ -28,8 +28,8 @@ class FreeBSDVM(basevm.BaseVM): name = "freebsd" arch = "x86_64" - link = "https://download.freebsd.org/releases/CI-IMAGES/14.1-RELEASE/amd64/Latest/FreeBSD-14.1-RELEASE-amd64-BASIC-CI.raw.xz" - csum = "202fe27a05427f0a86d3ebb97712745186f2776ccc4f70d95466dd99a0238ba5" + link = "https://download.freebsd.org/releases/CI-IMAGES/14.3-RELEASE/amd64/Latest/FreeBSD-14.3-RELEASE-amd64-BASIC-CI.raw.xz" + csum = "ec0f5a4bbe63aa50a725d9fee0f1931f850e9a21cbebdadb991df00f168d6805" size = "20G" BUILD_SCRIPT = """ From 075e91a4a42dad834d6488cd8141fa29c7b218bd Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Fri, 27 Jun 2025 11:51:29 +0800 Subject: [PATCH 2243/2760] i386/cpu: Reorder CPUID leaves in cpu_x86_cpuid() Sort the CPUID leaves strictly by index to facilitate checking and changing. Signed-off-by: Zhao Liu Reviewed-by: Tao Su Link: https://lore.kernel.org/r/20250627035129.2755537-5-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 60 +++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 1a2cae6ea1..3b7c22e5d3 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8052,21 +8052,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, assert(!(*eax & ~0x1f)); *ebx &= 0xffff; /* The count doesn't need to be reliable. */ break; - case 0x1C: - if (cpu->enable_pmu && (env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_ARCH_LBR)) { - x86_cpu_get_supported_cpuid(0x1C, 0, eax, ebx, ecx, edx); - *edx = 0; - } - break; - case 0x1F: - /* V2 Extended Topology Enumeration Leaf */ - if (!x86_has_cpuid_0x1f(cpu)) { - *eax = *ebx = *ecx = *edx = 0; - break; - } - - encode_topo_cpuid1f(env, count, topo_info, eax, ebx, ecx, edx); - break; case 0xD: { /* Processor Extended State */ *eax = 0; @@ -8207,6 +8192,12 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } break; } + case 0x1C: + if (cpu->enable_pmu && (env->features[FEAT_7_0_EDX] & CPUID_7_0_EDX_ARCH_LBR)) { + x86_cpu_get_supported_cpuid(0x1C, 0, eax, ebx, ecx, edx); + *edx = 0; + } + break; case 0x1D: { /* AMX TILE, for now hardcoded for Sapphire Rapids*/ *eax = 0; @@ -8244,6 +8235,15 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } break; } + case 0x1F: + /* V2 Extended Topology Enumeration Leaf */ + if (!x86_has_cpuid_0x1f(cpu)) { + *eax = *ebx = *ecx = *edx = 0; + break; + } + + encode_topo_cpuid1f(env, count, topo_info, eax, ebx, ecx, edx); + break; case 0x24: { *eax = 0; *ebx = 0; @@ -8472,6 +8472,21 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *edx = 0; } break; + case 0x8000001F: + *eax = *ebx = *ecx = *edx = 0; + if (sev_enabled()) { + *eax = 0x2; + *eax |= sev_es_enabled() ? 0x8 : 0; + *eax |= sev_snp_enabled() ? 0x10 : 0; + *ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */ + *ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */ + } + break; + case 0x80000021: + *eax = *ebx = *ecx = *edx = 0; + *eax = env->features[FEAT_8000_0021_EAX]; + *ebx = env->features[FEAT_8000_0021_EBX]; + break; case 0x80000022: *eax = *ebx = *ecx = *edx = 0; /* AMD Extended Performance Monitoring and Debug */ @@ -8504,21 +8519,6 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, *ecx = 0; *edx = 0; break; - case 0x8000001F: - *eax = *ebx = *ecx = *edx = 0; - if (sev_enabled()) { - *eax = 0x2; - *eax |= sev_es_enabled() ? 0x8 : 0; - *eax |= sev_snp_enabled() ? 0x10 : 0; - *ebx = sev_get_cbit_position() & 0x3f; /* EBX[5:0] */ - *ebx |= (sev_get_reduced_phys_bits() & 0x3f) << 6; /* EBX[11:6] */ - } - break; - case 0x80000021: - *eax = *ebx = *ecx = *edx = 0; - *eax = env->features[FEAT_8000_0021_EAX]; - *ebx = env->features[FEAT_8000_0021_EBX]; - break; default: /* reserved values: zero */ *eax = 0; From f985a1195ba2d9c6f6f33e83fe2e419a7e8acb60 Mon Sep 17 00:00:00 2001 From: Chuang Xu Date: Mon, 14 Jul 2025 16:08:56 +0800 Subject: [PATCH 2244/2760] i386/cpu: Fix number of addressable IDs field for CPUID.01H.EBX[23:16] When QEMU is started with: -cpu host,migratable=on,host-cache-info=on,l3-cache=off -smp 180,sockets=2,dies=1,cores=45,threads=2 On Intel platform: CPUID.01H.EBX[23:16] is defined as "max number of addressable IDs for logical processors in the physical package". When executing "cpuid -1 -l 1 -r" in the guest, we obtain a value of 90 for CPUID.01H.EBX[23:16], whereas the expected value is 128. Additionally, executing "cpuid -1 -l 4 -r" in the guest yields a value of 63 for CPUID.04H.EAX[31:26], which matches the expected result. As (1+CPUID.04H.EAX[31:26]) rounds up to the nearest power-of-2 integer, it's necessary to round up CPUID.01H.EBX[23:16] to the nearest power-of-2 integer too. Otherwise there would be unexpected results in guest with older kernel. For example, when QEMU is started with CLI above and xtopology is disabled, guest kernel 5.15.120 uses CPUID.01H.EBX[23:16]/(1+CPUID.04H.EAX[31:26]) to calculate threads-per-core in detect_ht(). Then guest will get "90/(1+63)=1" as the result, even though threads-per-core should actually be 2. And on AMD platform: CPUID.01H.EBX[23:16] is defined as "Logical processor count". Current result meets our expectation. So round up CPUID.01H.EBX[23:16] to the nearest power-of-2 integer only for Intel platform to solve the unexpected result. Use the "x-vendor-cpuid-only-v2" compat option to fix this issue. Reviewed-by: Zhao Liu Signed-off-by: Guixiong Wei Signed-off-by: Yipeng Yin Signed-off-by: Chuang Xu Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250714080859.1960104-5-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 3b7c22e5d3..12e719e995 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7871,7 +7871,17 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } *edx = env->features[FEAT_1_EDX]; if (threads_per_pkg > 1) { - *ebx |= threads_per_pkg << 16; + /* + * For CPUID.01H.EBX[Bits 23-16], AMD requires logical processor + * count, but Intel needs maximum number of addressable IDs for + * logical processors per package. + */ + if (cpu->vendor_cpuid_only_v2 && + (IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { + *ebx |= 1 << apicid_pkg_offset(topo_info) << 16; + } else { + *ebx |= threads_per_pkg << 16; + } } break; case 2: { /* cache info: needed for Pentium Pro compatibility */ From a62fef58299562aae6667b8d8552247423e886b3 Mon Sep 17 00:00:00 2001 From: Qian Wen Date: Mon, 14 Jul 2025 16:08:57 +0800 Subject: [PATCH 2245/2760] i386/cpu: Fix cpu number overflow in CPUID.01H.EBX[23:16] The legacy topology enumerated by CPUID.1.EBX[23:16] is defined in SDM Vol2: Bits 23-16: Maximum number of addressable IDs for logical processors in this physical package. When threads_per_socket > 255, it will 1) overwrite bits[31:24] which is apic_id, 2) bits [23:16] get truncated. Specifically, if launching the VM with -smp 256, the value written to EBX[23:16] is 0 because of data overflow. If the guest only supports legacy topology, without V2 Extended Topology enumerated by CPUID.0x1f or Extended Topology enumerated by CPUID.0x0b to support over 255 CPUs, the return of the kernel invoking cpu_smt_allowed() is false and APs (application processors) will fail to bring up. Then only CPU 0 is online, and others are offline. For example, launch VM via: qemu-system-x86_64 -M q35,accel=kvm,kernel-irqchip=split \ -cpu qemu64,cpuid-0xb=off -smp 256 -m 32G \ -drive file=guest.img,if=none,id=virtio-disk0,format=raw \ -device virtio-blk-pci,drive=virtio-disk0,bootindex=1 --nographic The guest shows: CPU(s): 256 On-line CPU(s) list: 0 Off-line CPU(s) list: 1-255 To avoid this issue caused by overflow, limit the max value written to EBX[23:16] to 255 as the HW does. Cc: qemu-stable@nongnu.org Reviewed-by: Xiaoyao Li Signed-off-by: Qian Wen Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250714080859.1960104-6-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 12e719e995..608fdcf757 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7871,6 +7871,8 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, } *edx = env->features[FEAT_1_EDX]; if (threads_per_pkg > 1) { + uint32_t num; + /* * For CPUID.01H.EBX[Bits 23-16], AMD requires logical processor * count, but Intel needs maximum number of addressable IDs for @@ -7878,10 +7880,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, */ if (cpu->vendor_cpuid_only_v2 && (IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { - *ebx |= 1 << apicid_pkg_offset(topo_info) << 16; + num = 1 << apicid_pkg_offset(topo_info); } else { - *ebx |= threads_per_pkg << 16; + num = threads_per_pkg; } + + /* Fixup overflow: max value for bits 23-16 is 255. */ + *ebx |= MIN(num, 255) << 16; } break; case 2: { /* cache info: needed for Pentium Pro compatibility */ From 3e86124e7cb9b66e07fb992667865a308f16fcf2 Mon Sep 17 00:00:00 2001 From: Qian Wen Date: Mon, 14 Jul 2025 16:08:58 +0800 Subject: [PATCH 2246/2760] i386/cpu: Fix overflow of cache topology fields in CPUID.04H According to SDM, CPUID.0x4:EAX[31:26] indicates the Maximum number of addressable IDs for processor cores in the physical package. If we launch over 64 cores VM, the 6-bit field will overflow, and the wrong core_id number will be reported. Since the HW reports 0x3f when the intel processor has over 64 cores, limit the max value written to EAX[31:26] to 63, so max num_cores should be 64. For EAX[14:25], though at present Q35 supports up to 4096 CPUs, by constructing a specific topology, the width of the APIC ID can be extended beyond 12 bits. For example, using `-smp threads=33,cores=9, modules=9` results in a die level offset of 6 + 4 + 4 = 14 bits, which can also cause overflow. check and honor the maximum value for EAX[14:25] as well. In addition, for host-cache-info case, also apply the same checks and fixes. Reviewed-by: Xiaoyao Li Signed-off-by: Qian Wen Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250714080859.1960104-7-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 608fdcf757..fdc677614d 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -349,11 +349,17 @@ static void encode_cache_cpuid4(CPUCacheInfo *cache, assert(cache->size == cache->line_size * cache->associativity * cache->partitions * cache->sets); + /* + * The following fields have bit-width limitations, so consider the + * maximum values to avoid overflow: + * Bits 25-14: maximum 4095. + * Bits 31-26: maximum 63. + */ *eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) | (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0) | - (max_core_ids_in_package(topo_info) << 26) | - (max_thread_ids_for_cache(topo_info, cache->share_level) << 14); + (MIN(max_core_ids_in_package(topo_info), 63) << 26) | + (MIN(max_thread_ids_for_cache(topo_info, cache->share_level), 4095) << 14); assert(cache->line_size > 0); assert(cache->partitions > 0); @@ -7930,13 +7936,13 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, int host_vcpus_per_cache = 1 + ((*eax & 0x3FFC000) >> 14); *eax &= ~0xFC000000; - *eax |= max_core_ids_in_package(topo_info) << 26; + *eax |= MIN(max_core_ids_in_package(topo_info), 63) << 26; if (host_vcpus_per_cache > threads_per_pkg) { *eax &= ~0x3FFC000; /* Share the cache at package level. */ - *eax |= max_thread_ids_for_cache(topo_info, - CPU_TOPOLOGY_LEVEL_SOCKET) << 14; + *eax |= MIN(max_thread_ids_for_cache(topo_info, + CPU_TOPOLOGY_LEVEL_SOCKET), 4095) << 14; } } } else if (cpu->vendor_cpuid_only && IS_AMD_CPU(env)) { From 5d21ee453ad8e3f95f75e542cb3b35c5bb7cf23a Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 14 Jul 2025 16:08:59 +0800 Subject: [PATCH 2247/2760] i386/cpu: Honor maximum value for CPUID.8000001DH.EAX[25:14] CPUID.8000001DH:EAX[25:14] is "NumSharingCache", and the number of logical processors sharing this cache is the value of this field incremented by 1. Because of its width limitation, the maximum value currently supported is 4095. Though at present Q35 supports up to 4096 CPUs, by constructing a specific topology, the width of the APIC ID can be extended beyond 12 bits. For example, using `-smp threads=33,cores=9,modules=9` results in a die level offset of 6 + 4 + 4 = 14 bits, which can also cause overflow. Check and honor the maximum value as CPUID.04H did. Cc: Babu Moger Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250714080859.1960104-8-zhao1.liu@intel.com Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index fdc677614d..da7d8dca63 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -560,7 +560,8 @@ static void encode_cache_cpuid8000001d(CPUCacheInfo *cache, *eax = CACHE_TYPE(cache->type) | CACHE_LEVEL(cache->level) | (cache->self_init ? CACHE_SELF_INIT_LEVEL : 0); - *eax |= max_thread_ids_for_cache(topo_info, cache->share_level) << 14; + /* Bits 25:14 - NumSharingCache: maximum 4095. */ + *eax |= MIN(max_thread_ids_for_cache(topo_info, cache->share_level), 4095) << 14; assert(cache->line_size > 0); assert(cache->partitions > 0); From 52f45faa4f843617ae09e7965aec963fab3fb564 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 30 May 2025 13:33:16 +0900 Subject: [PATCH 2248/2760] qdev-properties: Add DEFINE_PROP_ON_OFF_AUTO_BIT64() DEFINE_PROP_ON_OFF_AUTO_BIT64() corresponds to DEFINE_PROP_ON_OFF_AUTO() as DEFINE_PROP_BIT64() corresponds to DEFINE_PROP_BOOL(). The difference is that DEFINE_PROP_ON_OFF_AUTO_BIT64() exposes OnOffAuto instead of bool. Signed-off-by: Akihiko Odaki Message-Id: <20250530-vdpa-v1-1-5af4109b1c19@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/core/qdev-properties.c | 67 +++++++++++++++++++++++++++++++++++- include/hw/qdev-properties.h | 18 ++++++++++ 2 files changed, 84 insertions(+), 1 deletion(-) diff --git a/hw/core/qdev-properties.c b/hw/core/qdev-properties.c index 147b3ffd16..b7e8a89ba5 100644 --- a/hw/core/qdev-properties.c +++ b/hw/core/qdev-properties.c @@ -2,6 +2,7 @@ #include "hw/qdev-properties.h" #include "qapi/error.h" #include "qapi/qapi-types-misc.h" +#include "qapi/qapi-visit-common.h" #include "qobject/qlist.h" #include "qemu/ctype.h" #include "qemu/error-report.h" @@ -180,7 +181,8 @@ const PropertyInfo qdev_prop_bit = { static uint64_t qdev_get_prop_mask64(const Property *prop) { - assert(prop->info == &qdev_prop_bit64); + assert(prop->info == &qdev_prop_bit64 || + prop->info == &qdev_prop_on_off_auto_bit64); return 0x1ull << prop->bitnr; } @@ -225,6 +227,69 @@ const PropertyInfo qdev_prop_bit64 = { .set_default_value = set_default_value_bool, }; +static void prop_get_on_off_auto_bit64(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + OnOffAutoBit64 *p = object_field_prop_ptr(obj, prop); + OnOffAuto value; + uint64_t mask = qdev_get_prop_mask64(prop); + + if (p->auto_bits & mask) { + value = ON_OFF_AUTO_AUTO; + } else if (p->on_bits & mask) { + value = ON_OFF_AUTO_ON; + } else { + value = ON_OFF_AUTO_OFF; + } + + visit_type_OnOffAuto(v, name, &value, errp); +} + +static void prop_set_on_off_auto_bit64(Object *obj, Visitor *v, + const char *name, void *opaque, + Error **errp) +{ + Property *prop = opaque; + OnOffAutoBit64 *p = object_field_prop_ptr(obj, prop); + OnOffAuto value; + uint64_t mask = qdev_get_prop_mask64(prop); + + if (!visit_type_OnOffAuto(v, name, &value, errp)) { + return; + } + + switch (value) { + case ON_OFF_AUTO_AUTO: + p->on_bits &= ~mask; + p->auto_bits |= mask; + break; + + case ON_OFF_AUTO_ON: + p->on_bits |= mask; + p->auto_bits &= ~mask; + break; + + case ON_OFF_AUTO_OFF: + p->on_bits &= ~mask; + p->auto_bits &= ~mask; + break; + + case ON_OFF_AUTO__MAX: + g_assert_not_reached(); + } +} + +const PropertyInfo qdev_prop_on_off_auto_bit64 = { + .type = "OnOffAuto", + .description = "on/off/auto", + .enum_table = &OnOffAuto_lookup, + .get = prop_get_on_off_auto_bit64, + .set = prop_set_on_off_auto_bit64, + .set_default_value = qdev_propinfo_set_default_value_enum, +}; + /* --- bool --- */ static void get_bool(Object *obj, Visitor *v, const char *name, void *opaque, diff --git a/include/hw/qdev-properties.h b/include/hw/qdev-properties.h index 2c99856caa..0197aa4995 100644 --- a/include/hw/qdev-properties.h +++ b/include/hw/qdev-properties.h @@ -43,11 +43,22 @@ struct PropertyInfo { ObjectPropertyRelease *release; }; +/** + * struct OnOffAutoBit64 - OnOffAuto storage with 64 elements. + * @on_bits: Bitmap of elements with "on". + * @auto_bits: Bitmap of elements with "auto". + */ +typedef struct OnOffAutoBit64 { + uint64_t on_bits; + uint64_t auto_bits; +} OnOffAutoBit64; + /*** qdev-properties.c ***/ extern const PropertyInfo qdev_prop_bit; extern const PropertyInfo qdev_prop_bit64; +extern const PropertyInfo qdev_prop_on_off_auto_bit64; extern const PropertyInfo qdev_prop_bool; extern const PropertyInfo qdev_prop_uint8; extern const PropertyInfo qdev_prop_uint16; @@ -100,6 +111,13 @@ extern const PropertyInfo qdev_prop_link; .set_default = true, \ .defval.u = (bool)_defval) +#define DEFINE_PROP_ON_OFF_AUTO_BIT64(_name, _state, _field, _bit, _defval) \ + DEFINE_PROP(_name, _state, _field, qdev_prop_on_off_auto_bit64, \ + OnOffAutoBit64, \ + .bitnr = (_bit), \ + .set_default = true, \ + .defval.i = (OnOffAuto)_defval) + #define DEFINE_PROP_BOOL(_name, _state, _field, _defval) \ DEFINE_PROP(_name, _state, _field, qdev_prop_bool, bool, \ .set_default = true, \ From 14f521f491063136bb4aa282c9d29735c78ee433 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 30 May 2025 13:33:17 +0900 Subject: [PATCH 2249/2760] net/vhost-vdpa: Report hashing capability Report hashing capability so that virtio-net can deliver the correct capability information to the guest. Signed-off-by: Akihiko Odaki Message-Id: <20250530-vdpa-v1-2-5af4109b1c19@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/net/net.h | 3 +++ net/net.c | 9 +++++++++ net/vhost-vdpa.c | 28 ++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+) diff --git a/include/net/net.h b/include/net/net.h index cdd5b109b0..545f4339ce 100644 --- a/include/net/net.h +++ b/include/net/net.h @@ -60,6 +60,7 @@ typedef bool (HasVnetHdrLen)(NetClientState *, int); typedef void (SetOffload)(NetClientState *, int, int, int, int, int, int, int); typedef int (GetVnetHdrLen)(NetClientState *); typedef void (SetVnetHdrLen)(NetClientState *, int); +typedef bool (GetVnetHashSupportedTypes)(NetClientState *, uint32_t *); typedef int (SetVnetLE)(NetClientState *, bool); typedef int (SetVnetBE)(NetClientState *, bool); typedef struct SocketReadState SocketReadState; @@ -89,6 +90,7 @@ typedef struct NetClientInfo { SetVnetHdrLen *set_vnet_hdr_len; SetVnetLE *set_vnet_le; SetVnetBE *set_vnet_be; + GetVnetHashSupportedTypes *get_vnet_hash_supported_types; NetAnnounce *announce; SetSteeringEBPF *set_steering_ebpf; NetCheckPeerType *check_peer_type; @@ -189,6 +191,7 @@ void qemu_set_offload(NetClientState *nc, int csum, int tso4, int tso6, int ecn, int ufo, int uso4, int uso6); int qemu_get_vnet_hdr_len(NetClientState *nc); void qemu_set_vnet_hdr_len(NetClientState *nc, int len); +bool qemu_get_vnet_hash_supported_types(NetClientState *nc, uint32_t *types); int qemu_set_vnet_le(NetClientState *nc, bool is_le); int qemu_set_vnet_be(NetClientState *nc, bool is_be); void qemu_macaddr_default_if_unset(MACAddr *macaddr); diff --git a/net/net.c b/net/net.c index 39d6f28158..d0ae3db0d8 100644 --- a/net/net.c +++ b/net/net.c @@ -573,6 +573,15 @@ void qemu_set_vnet_hdr_len(NetClientState *nc, int len) nc->info->set_vnet_hdr_len(nc, len); } +bool qemu_get_vnet_hash_supported_types(NetClientState *nc, uint32_t *types) +{ + if (!nc || !nc->info->get_vnet_hash_supported_types) { + return false; + } + + return nc->info->get_vnet_hash_supported_types(nc, types); +} + int qemu_set_vnet_le(NetClientState *nc, bool is_le) { #if HOST_BIG_ENDIAN diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 58d738945d..cb63e09453 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -252,6 +252,32 @@ static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) return true; } +static bool vhost_vdpa_get_vnet_hash_supported_types(NetClientState *nc, + uint32_t *types) +{ + assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); + VhostVDPAState *s = DO_UPCAST(VhostVDPAState, nc, nc); + uint64_t features = s->vhost_vdpa.dev->features; + int fd = s->vhost_vdpa.shared->device_fd; + struct { + struct vhost_vdpa_config hdr; + uint32_t supported_hash_types; + } config; + + if (!virtio_has_feature(features, VIRTIO_NET_F_HASH_REPORT) && + !virtio_has_feature(features, VIRTIO_NET_F_RSS)) { + return false; + } + + config.hdr.off = offsetof(struct virtio_net_config, supported_hash_types); + config.hdr.len = sizeof(config.supported_hash_types); + + assert(!ioctl(fd, VHOST_VDPA_GET_CONFIG, &config)); + *types = le32_to_cpu(config.supported_hash_types); + + return true; +} + static bool vhost_vdpa_has_ufo(NetClientState *nc) { assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); @@ -428,6 +454,7 @@ static NetClientInfo net_vhost_vdpa_info = { .stop = vhost_vdpa_net_client_stop, .cleanup = vhost_vdpa_cleanup, .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, + .get_vnet_hash_supported_types = vhost_vdpa_get_vnet_hash_supported_types, .has_ufo = vhost_vdpa_has_ufo, .set_vnet_le = vhost_vdpa_set_vnet_le, .check_peer_type = vhost_vdpa_check_peer_type, @@ -1284,6 +1311,7 @@ static NetClientInfo net_vhost_vdpa_cvq_info = { .stop = vhost_vdpa_net_cvq_stop, .cleanup = vhost_vdpa_cleanup, .has_vnet_hdr = vhost_vdpa_has_vnet_hdr, + .get_vnet_hash_supported_types = vhost_vdpa_get_vnet_hash_supported_types, .has_ufo = vhost_vdpa_has_ufo, .check_peer_type = vhost_vdpa_check_peer_type, .set_steering_ebpf = vhost_vdpa_set_steering_ebpf, From 2deec9ab7d25d7cd8f57033bd0421c1f9f28d905 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 30 May 2025 13:33:18 +0900 Subject: [PATCH 2250/2760] virtio-net: Move virtio_net_get_features() down Move virtio_net_get_features() to the later part of the file so that it can call other functions. Signed-off-by: Akihiko Odaki Message-Id: <20250530-vdpa-v1-3-5af4109b1c19@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 146 ++++++++++++++++++++++---------------------- 1 file changed, 73 insertions(+), 73 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index eb93607b8c..34fe5909c5 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -752,79 +752,6 @@ static void virtio_net_set_queue_pairs(VirtIONet *n) static void virtio_net_set_multiqueue(VirtIONet *n, int multiqueue); -static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, - Error **errp) -{ - VirtIONet *n = VIRTIO_NET(vdev); - NetClientState *nc = qemu_get_queue(n->nic); - - /* Firstly sync all virtio-net possible supported features */ - features |= n->host_features; - - virtio_add_feature(&features, VIRTIO_NET_F_MAC); - - if (!peer_has_vnet_hdr(n)) { - virtio_clear_feature(&features, VIRTIO_NET_F_CSUM); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN); - - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN); - - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6); - - virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT); - } - - if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) { - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO); - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO); - } - - if (!peer_has_uso(n)) { - virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4); - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6); - } - - if (!get_vhost_net(nc->peer)) { - return features; - } - - if (!ebpf_rss_is_loaded(&n->ebpf_rss)) { - virtio_clear_feature(&features, VIRTIO_NET_F_RSS); - } - features = vhost_net_get_features(get_vhost_net(nc->peer), features); - vdev->backend_features = features; - - if (n->mtu_bypass_backend && - (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) { - features |= (1ULL << VIRTIO_NET_F_MTU); - } - - /* - * Since GUEST_ANNOUNCE is emulated the feature bit could be set without - * enabled. This happens in the vDPA case. - * - * Make sure the feature set is not incoherent, as the driver could refuse - * to start. - * - * TODO: QEMU is able to emulate a CVQ just for guest_announce purposes, - * helping guest to notify the new location with vDPA devices that does not - * support it. - */ - if (!virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_CTRL_VQ)) { - virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ANNOUNCE); - } - - return features; -} - static uint64_t virtio_net_bad_features(VirtIODevice *vdev) { uint64_t features = 0; @@ -3076,6 +3003,79 @@ static int virtio_net_pre_load_queues(VirtIODevice *vdev) return 0; } +static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, + Error **errp) +{ + VirtIONet *n = VIRTIO_NET(vdev); + NetClientState *nc = qemu_get_queue(n->nic); + + /* Firstly sync all virtio-net possible supported features */ + features |= n->host_features; + + virtio_add_feature(&features, VIRTIO_NET_F_MAC); + + if (!peer_has_vnet_hdr(n)) { + virtio_clear_feature(&features, VIRTIO_NET_F_CSUM); + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO4); + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_TSO6); + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_ECN); + + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_CSUM); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO4); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_TSO6); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ECN); + + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6); + + virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT); + } + + if (!peer_has_vnet_hdr(n) || !peer_has_ufo(n)) { + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_UFO); + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_UFO); + } + + if (!peer_has_uso(n)) { + virtio_clear_feature(&features, VIRTIO_NET_F_HOST_USO); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO4); + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_USO6); + } + + if (!get_vhost_net(nc->peer)) { + return features; + } + + if (!ebpf_rss_is_loaded(&n->ebpf_rss)) { + virtio_clear_feature(&features, VIRTIO_NET_F_RSS); + } + features = vhost_net_get_features(get_vhost_net(nc->peer), features); + vdev->backend_features = features; + + if (n->mtu_bypass_backend && + (n->host_features & 1ULL << VIRTIO_NET_F_MTU)) { + features |= (1ULL << VIRTIO_NET_F_MTU); + } + + /* + * Since GUEST_ANNOUNCE is emulated the feature bit could be set without + * enabled. This happens in the vDPA case. + * + * Make sure the feature set is not incoherent, as the driver could refuse + * to start. + * + * TODO: QEMU is able to emulate a CVQ just for guest_announce purposes, + * helping guest to notify the new location with vDPA devices that does not + * support it. + */ + if (!virtio_has_feature(vdev->backend_features, VIRTIO_NET_F_CTRL_VQ)) { + virtio_clear_feature(&features, VIRTIO_NET_F_GUEST_ANNOUNCE); + } + + return features; +} + static int virtio_net_post_load_device(void *opaque, int version_id) { VirtIONet *n = opaque; From 7b6e7e49905d0682f67ae4d02fc9edc4a2ec7df5 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 30 May 2025 13:33:19 +0900 Subject: [PATCH 2251/2760] virtio-net: Retrieve peer hashing capability Retrieve peer hashing capability instead of hardcoding. Signed-off-by: Akihiko Odaki Message-Id: <20250530-vdpa-v1-4-5af4109b1c19@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 71 +++++++++++++++++++++++++++------- include/hw/virtio/virtio-net.h | 5 ++- net/vhost-vdpa.c | 4 +- 3 files changed, 64 insertions(+), 16 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 34fe5909c5..9888ff22bd 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -158,7 +158,7 @@ static void virtio_net_get_config(VirtIODevice *vdev, uint8_t *config) virtio_host_has_feature(vdev, VIRTIO_NET_F_RSS) ? VIRTIO_NET_RSS_MAX_TABLE_LEN : 1); virtio_stl_p(vdev, &netcfg.supported_hash_types, - VIRTIO_NET_RSS_SUPPORTED_HASHES); + n->rss_data.supported_hash_types); memcpy(config, &netcfg, n->config_size); /* @@ -1178,7 +1178,7 @@ static void rss_data_to_rss_config(struct VirtioNetRssData *data, { config->redirect = data->redirect; config->populate_hash = data->populate_hash; - config->hash_types = data->hash_types; + config->hash_types = data->runtime_hash_types; config->indirections_len = data->indirections_len; config->default_queue = data->default_queue; } @@ -1213,6 +1213,10 @@ static void virtio_net_detach_ebpf_rss(VirtIONet *n) static void virtio_net_commit_rss_config(VirtIONet *n) { + if (n->rss_data.peer_hash_available) { + return; + } + if (n->rss_data.enabled) { n->rss_data.enabled_software_rss = n->rss_data.populate_hash; if (n->rss_data.populate_hash) { @@ -1227,7 +1231,7 @@ static void virtio_net_commit_rss_config(VirtIONet *n) } trace_virtio_net_rss_enable(n, - n->rss_data.hash_types, + n->rss_data.runtime_hash_types, n->rss_data.indirections_len, sizeof(n->rss_data.key)); } else { @@ -1338,7 +1342,7 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, err_value = (uint32_t)s; goto error; } - n->rss_data.hash_types = virtio_ldl_p(vdev, &cfg.hash_types); + n->rss_data.runtime_hash_types = virtio_ldl_p(vdev, &cfg.hash_types); n->rss_data.indirections_len = virtio_lduw_p(vdev, &cfg.indirection_table_mask); if (!do_rss) { @@ -1401,12 +1405,12 @@ static uint16_t virtio_net_handle_rss(VirtIONet *n, err_value = temp.b; goto error; } - if (!temp.b && n->rss_data.hash_types) { + if (!temp.b && n->rss_data.runtime_hash_types) { err_msg = "No key provided"; err_value = 0; goto error; } - if (!temp.b && !n->rss_data.hash_types) { + if (!temp.b && !n->rss_data.runtime_hash_types) { virtio_net_disable_rss(n); return queue_pairs; } @@ -1808,7 +1812,7 @@ static int virtio_net_process_rss(NetClientState *nc, const uint8_t *buf, net_rx_pkt_set_protocols(pkt, &iov, 1, n->host_hdr_len); net_rx_pkt_get_protocols(pkt, &hasip4, &hasip6, &l4hdr_proto); net_hash_type = virtio_net_get_hash_type(hasip4, hasip6, l4hdr_proto, - n->rss_data.hash_types); + n->rss_data.runtime_hash_types); if (net_hash_type > NetPktRssIpV6UdpEx) { if (n->rss_data.populate_hash) { hdr->hash_value = VIRTIO_NET_HASH_REPORT_NONE; @@ -3008,6 +3012,14 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, { VirtIONet *n = VIRTIO_NET(vdev); NetClientState *nc = qemu_get_queue(n->nic); + uint32_t supported_hash_types = n->rss_data.supported_hash_types; + uint32_t peer_hash_types = n->rss_data.peer_hash_types; + bool use_own_hash = + (supported_hash_types & VIRTIO_NET_RSS_SUPPORTED_HASHES) == + supported_hash_types; + bool use_peer_hash = + n->rss_data.peer_hash_available && + (supported_hash_types & peer_hash_types) == supported_hash_types; /* Firstly sync all virtio-net possible supported features */ features |= n->host_features; @@ -3044,12 +3056,28 @@ static uint64_t virtio_net_get_features(VirtIODevice *vdev, uint64_t features, } if (!get_vhost_net(nc->peer)) { + if (!use_own_hash) { + virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT); + virtio_clear_feature(&features, VIRTIO_NET_F_RSS); + } else if (virtio_has_feature(features, VIRTIO_NET_F_RSS)) { + virtio_net_load_ebpf(n, errp); + } + return features; } - if (!ebpf_rss_is_loaded(&n->ebpf_rss)) { - virtio_clear_feature(&features, VIRTIO_NET_F_RSS); + if (!use_peer_hash) { + virtio_clear_feature(&features, VIRTIO_NET_F_HASH_REPORT); + + if (!use_own_hash || !virtio_net_attach_ebpf_to_backend(n->nic, -1)) { + if (!virtio_net_load_ebpf(n, errp)) { + return features; + } + + virtio_clear_feature(&features, VIRTIO_NET_F_RSS); + } } + features = vhost_net_get_features(get_vhost_net(nc->peer), features); vdev->backend_features = features; @@ -3314,6 +3342,17 @@ static const VMStateDescription vmstate_virtio_net_has_vnet = { }, }; +static int virtio_net_rss_post_load(void *opaque, int version_id) +{ + VirtIONet *n = VIRTIO_NET(opaque); + + if (version_id == 1) { + n->rss_data.supported_hash_types = VIRTIO_NET_RSS_SUPPORTED_HASHES; + } + + return 0; +} + static bool virtio_net_rss_needed(void *opaque) { return VIRTIO_NET(opaque)->rss_data.enabled; @@ -3321,14 +3360,16 @@ static bool virtio_net_rss_needed(void *opaque) static const VMStateDescription vmstate_virtio_net_rss = { .name = "virtio-net-device/rss", - .version_id = 1, + .version_id = 2, .minimum_version_id = 1, + .post_load = virtio_net_rss_post_load, .needed = virtio_net_rss_needed, .fields = (const VMStateField[]) { VMSTATE_BOOL(rss_data.enabled, VirtIONet), VMSTATE_BOOL(rss_data.redirect, VirtIONet), VMSTATE_BOOL(rss_data.populate_hash, VirtIONet), - VMSTATE_UINT32(rss_data.hash_types, VirtIONet), + VMSTATE_UINT32(rss_data.runtime_hash_types, VirtIONet), + VMSTATE_UINT32_V(rss_data.supported_hash_types, VirtIONet, 2), VMSTATE_UINT16(rss_data.indirections_len, VirtIONet), VMSTATE_UINT16(rss_data.default_queue, VirtIONet), VMSTATE_UINT8_ARRAY(rss_data.key, VirtIONet, @@ -3915,8 +3956,12 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) net_rx_pkt_init(&n->rx_pkt); - if (virtio_has_feature(n->host_features, VIRTIO_NET_F_RSS)) { - virtio_net_load_ebpf(n, errp); + if (qemu_get_vnet_hash_supported_types(qemu_get_queue(n->nic)->peer, + &n->rss_data.peer_hash_types)) { + n->rss_data.peer_hash_available = true; + n->rss_data.supported_hash_types = n->rss_data.peer_hash_types; + } else { + n->rss_data.supported_hash_types = VIRTIO_NET_RSS_SUPPORTED_HASHES; } } diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index b9ea9e824e..c4957c44c0 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -144,7 +144,10 @@ typedef struct VirtioNetRssData { bool enabled_software_rss; bool redirect; bool populate_hash; - uint32_t hash_types; + bool peer_hash_available; + uint32_t runtime_hash_types; + uint32_t supported_hash_types; + uint32_t peer_hash_types; uint8_t key[VIRTIO_NET_RSS_MAX_KEY_SIZE]; uint16_t indirections_len; uint16_t *indirections_table; diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index cb63e09453..3452835ca9 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -865,13 +865,13 @@ static int vhost_vdpa_net_load_rss(VhostVDPAState *s, const VirtIONet *n, * configuration only at live migration. */ if (!n->rss_data.enabled || - n->rss_data.hash_types == VIRTIO_NET_HASH_REPORT_NONE) { + n->rss_data.runtime_hash_types == VIRTIO_NET_HASH_REPORT_NONE) { return 0; } table = g_malloc_n(n->rss_data.indirections_len, sizeof(n->rss_data.indirections_table[0])); - cfg.hash_types = cpu_to_le32(n->rss_data.hash_types); + cfg.hash_types = cpu_to_le32(n->rss_data.runtime_hash_types); if (do_rss) { /* From abef963a12e2bc266884b27677ff3cf94c09e512 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 30 May 2025 13:33:20 +0900 Subject: [PATCH 2252/2760] net/vhost-vdpa: Remove dummy SetSteeringEBPF It is no longer used. Signed-off-by: Akihiko Odaki Message-Id: <20250530-vdpa-v1-5-5af4109b1c19@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- net/vhost-vdpa.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 3452835ca9..0f782ed444 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -239,12 +239,6 @@ static void vhost_vdpa_cleanup(NetClientState *nc) g_free(s->vhost_vdpa.shared); } -/** Dummy SetSteeringEBPF to support RSS for vhost-vdpa backend */ -static bool vhost_vdpa_set_steering_ebpf(NetClientState *nc, int prog_fd) -{ - return true; -} - static bool vhost_vdpa_has_vnet_hdr(NetClientState *nc) { assert(nc->info->type == NET_CLIENT_DRIVER_VHOST_VDPA); @@ -458,7 +452,6 @@ static NetClientInfo net_vhost_vdpa_info = { .has_ufo = vhost_vdpa_has_ufo, .set_vnet_le = vhost_vdpa_set_vnet_le, .check_peer_type = vhost_vdpa_check_peer_type, - .set_steering_ebpf = vhost_vdpa_set_steering_ebpf, }; static int64_t vhost_vdpa_get_vring_group(int device_fd, unsigned vq_index, @@ -1314,7 +1307,6 @@ static NetClientInfo net_vhost_vdpa_cvq_info = { .get_vnet_hash_supported_types = vhost_vdpa_get_vnet_hash_supported_types, .has_ufo = vhost_vdpa_has_ufo, .check_peer_type = vhost_vdpa_check_peer_type, - .set_steering_ebpf = vhost_vdpa_set_steering_ebpf, }; /* From 729b5734197d1afad364bb025a827438bb85e050 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Fri, 30 May 2025 13:33:21 +0900 Subject: [PATCH 2253/2760] virtio-net: Add hash type options By default, virtio-net limits the hash types that will be advertised to the guest so that all hash types are covered by the offloading capability the client provides. This change allows to override this behavior and to advertise hash types that require user-space hash calculation by specifying "on" for the corresponding properties. Signed-off-by: Akihiko Odaki Message-Id: <20250530-vdpa-v1-6-5af4109b1c19@daynix.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 45 ++++++++++++++++++++++++++++++++-- include/hw/virtio/virtio-net.h | 1 + 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index 9888ff22bd..6af230bca9 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -3959,9 +3959,14 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) if (qemu_get_vnet_hash_supported_types(qemu_get_queue(n->nic)->peer, &n->rss_data.peer_hash_types)) { n->rss_data.peer_hash_available = true; - n->rss_data.supported_hash_types = n->rss_data.peer_hash_types; + n->rss_data.supported_hash_types = + n->rss_data.specified_hash_types.on_bits | + (n->rss_data.specified_hash_types.auto_bits & + n->rss_data.peer_hash_types); } else { - n->rss_data.supported_hash_types = VIRTIO_NET_RSS_SUPPORTED_HASHES; + n->rss_data.supported_hash_types = + n->rss_data.specified_hash_types.on_bits | + n->rss_data.specified_hash_types.auto_bits; } } @@ -4178,6 +4183,42 @@ static const Property virtio_net_properties[] = { VIRTIO_NET_F_GUEST_USO6, true), DEFINE_PROP_BIT64("host_uso", VirtIONet, host_features, VIRTIO_NET_F_HOST_USO, true), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-ipv4", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_IPv4 - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-tcp4", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_TCPv4 - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-udp4", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_UDPv4 - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-ipv6", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_IPv6 - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-tcp6", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_TCPv6 - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-udp6", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_UDPv6 - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-ipv6ex", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_IPv6_EX - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-tcp6ex", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_TCPv6_EX - 1, + ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO_BIT64("hash-udp6ex", VirtIONet, + rss_data.specified_hash_types, + VIRTIO_NET_HASH_REPORT_UDPv6_EX - 1, + ON_OFF_AUTO_AUTO), }; static void virtio_net_class_init(ObjectClass *klass, const void *data) diff --git a/include/hw/virtio/virtio-net.h b/include/hw/virtio/virtio-net.h index c4957c44c0..73fdefc0dc 100644 --- a/include/hw/virtio/virtio-net.h +++ b/include/hw/virtio/virtio-net.h @@ -148,6 +148,7 @@ typedef struct VirtioNetRssData { uint32_t runtime_hash_types; uint32_t supported_hash_types; uint32_t peer_hash_types; + OnOffAutoBit64 specified_hash_types; uint8_t key[VIRTIO_NET_RSS_MAX_KEY_SIZE]; uint16_t indirections_len; uint16_t *indirections_table; From 9f749129e2629b19f424df106c92c5a5647e396c Mon Sep 17 00:00:00 2001 From: David Hildenbrand Date: Tue, 3 Jun 2025 13:13:36 +0200 Subject: [PATCH 2254/2760] vhost: Fix used memslot tracking when destroying a vhost device When we unplug a vhost device, we end up calling vhost_dev_cleanup() where we do a memory_listener_unregister(). This memory_listener_unregister() call will end up disconnecting the listener from the address space through listener_del_address_space(). In that process, we effectively communicate the removal of all memory regions from that listener, resulting in region_del() + commit() callbacks getting triggered. So in case of vhost, we end up calling vhost_commit() with no remaining memory slots (0). In vhost_commit() we end up overwriting the global variables used_memslots / used_shared_memslots, used for detecting the number of free memslots. With used_memslots / used_shared_memslots set to 0 by vhost_commit() during device removal, we'll later assume that the other vhost devices still have plenty of memslots left when calling vhost_get_free_memslots(). Let's fix it by simply removing the global variables and depending only on the actual per-device count. Easy to reproduce by adding two vhost-user devices to a VM and then hot-unplugging one of them. While at it, detect unexpected underflows in vhost_get_free_memslots() and issue a warning. Reported-by: yuanminghao Link: https://lore.kernel.org/qemu-devel/20241121060755.164310-1-yuanmh12@chinatelecom.cn/ Fixes: 2ce68e4cf5be ("vhost: add vhost_has_free_slot() interface") Cc: Igor Mammedov Cc: Michael S. Tsirkin Cc: Stefano Garzarella Signed-off-by: David Hildenbrand Message-Id: <20250603111336.1858888-1-david@redhat.com> Reviewed-by: Igor Mammedov Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 37 ++++++++++--------------------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index fc43853704..c87861b31f 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -47,12 +47,6 @@ static struct vhost_log *vhost_log[VHOST_BACKEND_TYPE_MAX]; static struct vhost_log *vhost_log_shm[VHOST_BACKEND_TYPE_MAX]; static QLIST_HEAD(, vhost_dev) vhost_log_devs[VHOST_BACKEND_TYPE_MAX]; -/* Memslots used by backends that support private memslots (without an fd). */ -static unsigned int used_memslots; - -/* Memslots used by backends that only support shared memslots (with an fd). */ -static unsigned int used_shared_memslots; - static QLIST_HEAD(, vhost_dev) vhost_devices = QLIST_HEAD_INITIALIZER(vhost_devices); @@ -74,15 +68,15 @@ unsigned int vhost_get_free_memslots(void) QLIST_FOREACH(hdev, &vhost_devices, entry) { unsigned int r = hdev->vhost_ops->vhost_backend_memslots_limit(hdev); - unsigned int cur_free; + unsigned int cur_free = r - hdev->mem->nregions; - if (hdev->vhost_ops->vhost_backend_no_private_memslots && - hdev->vhost_ops->vhost_backend_no_private_memslots(hdev)) { - cur_free = r - used_shared_memslots; + if (unlikely(r < hdev->mem->nregions)) { + warn_report_once("used (%u) vhost backend memory slots exceed" + " the device limit (%u).", hdev->mem->nregions, r); + free = 0; } else { - cur_free = r - used_memslots; + free = MIN(free, cur_free); } - free = MIN(free, cur_free); } return free; } @@ -666,13 +660,6 @@ static void vhost_commit(MemoryListener *listener) dev->mem = g_realloc(dev->mem, regions_size); dev->mem->nregions = dev->n_mem_sections; - if (dev->vhost_ops->vhost_backend_no_private_memslots && - dev->vhost_ops->vhost_backend_no_private_memslots(dev)) { - used_shared_memslots = dev->mem->nregions; - } else { - used_memslots = dev->mem->nregions; - } - for (i = 0; i < dev->n_mem_sections; i++) { struct vhost_memory_region *cur_vmr = dev->mem->regions + i; struct MemoryRegionSection *mrs = dev->mem_sections + i; @@ -1619,15 +1606,11 @@ int vhost_dev_init(struct vhost_dev *hdev, void *opaque, QLIST_INSERT_HEAD(&vhost_devices, hdev, entry); /* - * The listener we registered properly updated the corresponding counter. - * So we can trust that these values are accurate. + * The listener we registered properly setup the number of required + * memslots in vhost_commit(). */ - if (hdev->vhost_ops->vhost_backend_no_private_memslots && - hdev->vhost_ops->vhost_backend_no_private_memslots(hdev)) { - used = used_shared_memslots; - } else { - used = used_memslots; - } + used = hdev->mem->nregions; + /* * We assume that all reserved memslots actually require a real memslot * in our vhost backend. This might not be true, for example, if the From 5ad6432880959ca373d62d715de8f0e2ca507a38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 10 Jul 2025 11:45:24 +0100 Subject: [PATCH 2255/2760] gitlab: use argparse in check-units script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Modernise the argument parsing so we can easily add to the script. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250710104531.3099313-2-alex.bennee@linaro.org> --- .gitlab-ci.d/check-units.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.d/check-units.py b/.gitlab-ci.d/check-units.py index 268a4118d5..cdc62ae5ee 100755 --- a/.gitlab-ci.d/check-units.py +++ b/.gitlab-ci.d/check-units.py @@ -8,8 +8,10 @@ # SPDX-License-Identifier: GPL-2.0-or-later from os import access, R_OK, path -from sys import argv, exit +from sys import exit import json +import argparse +from pathlib import Path from collections import Counter @@ -51,16 +53,17 @@ def analyse_units(build_units): if __name__ == "__main__": - if len(argv) != 2: - script_name = path.basename(argv[0]) - print(f"Usage: {script_name} ") - exit(1) + parser = argparse.ArgumentParser( + description="analyse number of build units in compile_commands.json") + parser.add_argument("cc_path", type=Path, default=None, + help="Path to compile_commands.json") + + args = parser.parse_args() - cc_path = argv[1] - if path.isfile(cc_path) and access(cc_path, R_OK): - units = extract_build_units(cc_path) + if path.isfile(args.cc_path) and access(args.cc_path, R_OK): + units = extract_build_units(args.cc_path) analyse_units(units) exit(0) else: - print(f"{cc_path} doesn't exist or isn't readable") + print(f"{args.cc_path} doesn't exist or isn't readable") exit(1) From 2e51072ae5c0275733be748fbee3ce59df6d6261 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:10:42 -0400 Subject: [PATCH 2256/2760] docs/qapi-domain: add return-nodesc This form is used to annotate a return type without an accompanying description, for when there is no "Returns:" information in the source doc, but we have a return type we want to generate a cross-reference to. The syntax is: :return-nodesc: TypeName It's primarily necessary because Sphinx always expects both a type and a description for the prior form and will format it accordingly. To have a reasonable rendering when the body is missing, we need to use a different info field list entirely. Signed-off-by: John Snow Message-ID: <20250711051045.51110-2-jsnow@redhat.com> Acked-by: Markus Armbruster [Long line wrapped] Signed-off-by: Markus Armbruster --- docs/devel/qapi-domain.rst | 31 +++++++++++++++++++++++++++++++ docs/sphinx/qapi_domain.py | 8 ++++++++ 2 files changed, 39 insertions(+) diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst index 11238723c2..b71890f660 100644 --- a/docs/devel/qapi-domain.rst +++ b/docs/devel/qapi-domain.rst @@ -242,6 +242,37 @@ Example:: } +``:return-nodesc:`` +------------------- + +Document the return type of a QAPI command, without an accompanying +description. + +:availability: This field list is only available in the body of the + Command directive. +:syntax: ``:return-nodesc: type`` +:type: `sphinx.util.docfields.Field + `_ + + +Example:: + + .. qapi:command:: query-replay + :since: 5.2 + + Retrieve the record/replay information. It includes current + instruction count which may be used for ``replay-break`` and + ``replay-seek`` commands. + + :return-nodesc: ReplayInfo + + .. qmp-example:: + + -> { "execute": "query-replay" } + <- { "return": { + "mode": "play", "filename": "log.rr", "icount": 220414 } + } + ``:value:`` ----------- diff --git a/docs/sphinx/qapi_domain.py b/docs/sphinx/qapi_domain.py index ebc46a72c6..f561dc465f 100644 --- a/docs/sphinx/qapi_domain.py +++ b/docs/sphinx/qapi_domain.py @@ -532,6 +532,14 @@ class QAPICommand(QAPIObject): names=("return",), can_collapse=True, ), + # :return-nodesc: TypeName + CompatField( + "returnvalue", + label=_("Return"), + names=("return-nodesc",), + bodyrolename="type", + has_arg=False, + ), ] ) From 636c96cd77d39aaec3e1c09b9990b76b015566e1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:10:43 -0400 Subject: [PATCH 2257/2760] qapi: Fix undocumented return values by generating something MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generated command documentation lacks information on return value in several cases, e.g. query-tpm. The obvious fix would be to require a Returns: section when a command returns something. However, note that many existing Returns: sections are pretty useless: the description is basically the return type, which then gets rendered like "Return: ". This suggests that a description is often not really necessary, and requiring one isn't useful. Instead, generate the obvious minimal thing when Returns: is absent: "Return: ". This auto-generated Return documentation is placed is as follows: 1. If we have arguments, return goes right after them. 2. Else if we have errors, return goes right before them. 3. Else if we have features, return goes right before them. 4. Else return goes right after the intro To facilitate this algorithm, a "TODO:" hack line is used to separate the intro from the remainder of the documentation block in cases where there are no other sections to separate the intro from e.g. examples and additional detail meant to appear below the key sections of interest. Signed-off-by: John Snow Message-ID: <20250711051045.51110-3-jsnow@redhat.com> Reviewed-by: Markus Armbruster [_insert_near_kind() code replaced by something simpler, commit message amended to explain why we're doing this] Signed-off-by: Markus Armbruster --- docs/sphinx/qapidoc.py | 14 ++++++++------ qapi/machine.json | 2 ++ scripts/qapi/parser.py | 37 +++++++++++++++++++++++++++++++++++++ scripts/qapi/schema.py | 3 +++ 4 files changed, 50 insertions(+), 6 deletions(-) diff --git a/docs/sphinx/qapidoc.py b/docs/sphinx/qapidoc.py index b794cde697..c2f09bac16 100644 --- a/docs/sphinx/qapidoc.py +++ b/docs/sphinx/qapidoc.py @@ -258,16 +258,18 @@ def visit_feature(self, section: QAPIDoc.ArgSection) -> None: def visit_returns(self, section: QAPIDoc.Section) -> None: assert isinstance(self.entity, QAPISchemaCommand) rtype = self.entity.ret_type - # q_empty can produce None, but we won't be documenting anything - # without an explicit return statement in the doc block, and we - # should not have any such explicit statements when there is no - # return value. + # return statements will not be present (and won't be + # autogenerated) for any command that doesn't return + # *something*, so rtype will always be defined here. assert rtype typ = self.format_type(rtype) assert typ - assert section.text - self.add_field("return", typ, section.text, section.info) + + if section.text: + self.add_field("return", typ, section.text, section.info) + else: + self.add_lines(f":return-nodesc: {typ}", section.info) def visit_errors(self, section: QAPIDoc.Section) -> None: # If the section text does not start with a space, it means text diff --git a/qapi/machine.json b/qapi/machine.json index af00a20b1f..2d6a137f21 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -1303,6 +1303,8 @@ # Return the amount of initially allocated and present hotpluggable # (if enabled) memory in bytes. # +# TODO: This line is a hack to separate the example from the body +# # .. qmp-example:: # # -> { "execute": "query-memory-size-summary" } diff --git a/scripts/qapi/parser.py b/scripts/qapi/parser.py index d43a123cd7..2529edf81a 100644 --- a/scripts/qapi/parser.py +++ b/scripts/qapi/parser.py @@ -804,6 +804,43 @@ def connect_feature(self, feature: 'QAPISchemaFeature') -> None: % feature.name) self.features[feature.name].connect(feature) + def ensure_returns(self, info: QAPISourceInfo) -> None: + + def _insert_near_kind( + kind: QAPIDoc.Kind, + new_sect: QAPIDoc.Section, + after: bool = False, + ) -> bool: + for idx, sect in enumerate(reversed(self.all_sections)): + if sect.kind == kind: + pos = len(self.all_sections) - idx - 1 + if after: + pos += 1 + self.all_sections.insert(pos, new_sect) + return True + return False + + if any(s.kind == QAPIDoc.Kind.RETURNS for s in self.all_sections): + return + + # Stub "Returns" section for undocumented returns value + stub = QAPIDoc.Section(info, QAPIDoc.Kind.RETURNS) + + if any(_insert_near_kind(kind, stub, after) for kind, after in ( + # 1. If arguments, right after those. + (QAPIDoc.Kind.MEMBER, True), + # 2. Elif errors, right *before* those. + (QAPIDoc.Kind.ERRORS, False), + # 3. Elif features, right *before* those. + (QAPIDoc.Kind.FEATURE, False), + )): + return + + # Otherwise, it should go right after the intro. The intro + # is always the first section and is always present (even + # when empty), so we can insert directly at index=1 blindly. + self.all_sections.insert(1, stub) + def check_expr(self, expr: QAPIExpression) -> None: if 'command' in expr: if self.returns and 'returns' not in expr: diff --git a/scripts/qapi/schema.py b/scripts/qapi/schema.py index cbe3b5aa91..3abddea352 100644 --- a/scripts/qapi/schema.py +++ b/scripts/qapi/schema.py @@ -1062,6 +1062,9 @@ def connect_doc(self, doc: Optional[QAPIDoc] = None) -> None: if self.arg_type and self.arg_type.is_implicit(): self.arg_type.connect_doc(doc) + if self.ret_type and self.info: + doc.ensure_returns(self.info) + def visit(self, visitor: QAPISchemaVisitor) -> None: super().visit(visitor) visitor.visit_command( From 0462da9d6b1918c741c632f57d743d5851ec5983 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:10:44 -0400 Subject: [PATCH 2258/2760] qapi: remove trivial "Returns:" sections The new qapidoc.py can generate "Returns" statements with type information just fine, so we can remove it from the source where it doesn't add anything particularly novel or helpful and just repeats the type info. This patch is fairly "gentle" and doesn't aggressively touch other "Returns" lines that could be rephrased to omit repeating type information; it only removes lines that appear appropriate to wholly remove. To help facilitate auto-generated placement, a few doc blocks have a "TODO:" line inserted to help the placement algorithm differentiate the introductory paragraph(s) from the rest of the documentation. The auto-generated returns are in the exact same spot, except for query-migrationthreads, query-machines, and x-query-virtio-queue-element. These auto-generation moves to a better spot. Signed-off-by: John Snow Message-ID: <20250711051045.51110-4-jsnow@redhat.com> Reviewed-by: Markus Armbruster [Two more Returns: removed, commit message amended to explain auto-generated returns generated into a different spot] Signed-off-by: Markus Armbruster --- qapi/audio.json | 2 -- qapi/block-core.json | 8 -------- qapi/char.json | 8 -------- qapi/cryptodev.json | 2 -- qapi/machine.json | 24 ------------------------ qapi/migration.json | 12 ++---------- qapi/misc-arm.json | 2 -- qapi/misc-i386.json | 10 ---------- qapi/misc.json | 7 ------- qapi/rocker.json | 4 ---- qapi/run-state.json | 2 -- qapi/tpm.json | 4 ---- qapi/ui.json | 8 -------- qapi/virtio.json | 2 -- qapi/yank.json | 2 +- 15 files changed, 3 insertions(+), 94 deletions(-) diff --git a/qapi/audio.json b/qapi/audio.json index 6a22ca384a..53142080f7 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -537,8 +537,6 @@ # # Return information about audiodev configuration # -# Returns: array of @Audiodev -# # Since: 8.0 ## { 'command': 'query-audiodevs', diff --git a/qapi/block-core.json b/qapi/block-core.json index 8b413946ff..01bf41b8b0 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -1951,8 +1951,6 @@ # @flat: Omit the nested data about backing image ("backing-image" # key) if true. Default is false (Since 5.0) # -# Returns: the list of BlockDeviceInfo -# # Since: 2.0 # # .. qmp-example:: @@ -2465,9 +2463,6 @@ # # @unstable: This command is meant for debugging. # -# Returns: -# BlockDirtyBitmapSha256 -# # Errors: # - If @node is not a valid block device, DeviceNotFound # - If @name is not found or if hashing has failed, GenericError @@ -6203,9 +6198,6 @@ # # @name: optional the snapshot's name to be deleted # -# Returns: -# SnapshotInfo -# # Errors: # - If @device is not a valid block device, GenericError # - If snapshot not found, GenericError diff --git a/qapi/char.json b/qapi/char.json index f38d04986d..f0a53f742c 100644 --- a/qapi/char.json +++ b/qapi/char.json @@ -38,8 +38,6 @@ # # Return information about current character devices. # -# Returns: a list of @ChardevInfo -# # Since: 0.14 # # .. qmp-example:: @@ -84,8 +82,6 @@ # # Return information about character device backends. # -# Returns: a list of @ChardevBackendInfo -# # Since: 2.0 # # .. qmp-example:: @@ -774,8 +770,6 @@ # # @backend: backend type and parameters # -# Returns: ChardevReturn. -# # Since: 1.4 # # .. qmp-example:: @@ -814,8 +808,6 @@ # # @backend: new backend type and parameters # -# Returns: ChardevReturn. -# # Since: 2.10 # # .. qmp-example:: diff --git a/qapi/cryptodev.json b/qapi/cryptodev.json index 1f49e1822c..eb309c22f8 100644 --- a/qapi/cryptodev.json +++ b/qapi/cryptodev.json @@ -98,8 +98,6 @@ # # Return information about current crypto devices. # -# Returns: a list of @QCryptodevInfo -# # Since: 8.0 ## { 'command': 'query-cryptodev', 'returns': ['QCryptodevInfo']} diff --git a/qapi/machine.json b/qapi/machine.json index 2d6a137f21..2364893cba 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -103,8 +103,6 @@ # # Return information about all virtual CPUs. # -# Returns: list of @CpuInfoFast -# # Since: 2.12 # # .. qmp-example:: @@ -220,8 +218,6 @@ # # @unstable: Argument @compat-props is experimental. # -# Returns: a list of MachineInfo -# # Since: 1.2 # # .. qmp-example:: @@ -270,8 +266,6 @@ # # Return information on the current virtual machine. # -# Returns: CurrentMachineParams -# # Since: 4.0 ## { 'command': 'query-current-machine', 'returns': 'CurrentMachineParams' } @@ -293,8 +287,6 @@ # # Return information about the target for this QEMU # -# Returns: QemuTargetInfo -# # Since: 1.2 ## { 'command': 'query-target', 'returns': 'QemuTargetInfo' } @@ -318,8 +310,6 @@ # # Query the guest UUID information. # -# Returns: The @UuidInfo for the guest -# # Since: 0.14 # # .. qmp-example:: @@ -471,8 +461,6 @@ # # Return information about KVM acceleration # -# Returns: @KvmInfo -# # Since: 0.14 # # .. qmp-example:: @@ -934,8 +922,6 @@ # # Return information for all memory backends. # -# Returns: a list of @Memdev. -# # Since: 2.1 # # .. qmp-example:: @@ -1051,8 +1037,6 @@ # # TODO: Better documentation; currently there is none. # -# Returns: a list of HotpluggableCPU objects. -# # Since: 2.7 # # .. qmp-example:: @@ -1174,9 +1158,6 @@ # # Return information about the balloon device. # -# Returns: -# @BalloonInfo -# # Errors: # - If the balloon driver is enabled but not functional because # the KVM kernel module cannot support it, KVMMissingCap @@ -1240,9 +1221,6 @@ # Return the hv-balloon driver data contained in the last received # "STATUS" message from the guest. # -# Returns: -# @HvBalloonInfo -# # Errors: # - If no hv-balloon device is present, guest memory status # reporting is not enabled or no guest memory status report @@ -2260,8 +2238,6 @@ # # Return a list of supported virtual CPU definitions # -# Returns: a list of CpuDefinitionInfo -# # Since: 1.2 ## { 'command': 'query-cpu-definitions', 'returns': ['CpuDefinitionInfo'] } diff --git a/qapi/migration.json b/qapi/migration.json index 3bf97df5d8..57653160eb 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -326,8 +326,6 @@ # is active there will be another json-object with RAM migration # status. # -# Returns: @MigrationInfo -# # Since: 0.14 # # .. qmp-example:: @@ -577,8 +575,6 @@ # # Return information about the current migration capabilities status # -# Returns: @MigrationCapabilityStatus -# # Since: 1.2 # # .. qmp-example:: @@ -1364,8 +1360,6 @@ # # Return information about the current migration parameters # -# Returns: @MigrationParameters -# # Since: 2.4 # # .. qmp-example:: @@ -1946,7 +1940,7 @@ # # Query replication status while the vm is running. # -# Returns: A @ReplicationStatus object showing the status. +# TODO: This line is a hack to separate the example from the body # # .. qmp-example:: # @@ -2000,7 +1994,7 @@ # # Query COLO status while the vm is running. # -# Returns: A @COLOStatus object showing the status. +# TODO: This line is a hack to separate the example from the body # # .. qmp-example:: # @@ -2375,8 +2369,6 @@ # # @deprecated: This command is deprecated with no replacement yet. # -# Returns: @MigrationThreadInfo -# # Since: 7.2 ## { 'command': 'query-migrationthreads', diff --git a/qapi/misc-arm.json b/qapi/misc-arm.json index f5341372f5..f9dd743b52 100644 --- a/qapi/misc-arm.json +++ b/qapi/misc-arm.json @@ -36,8 +36,6 @@ # On non-ARM targets this command will report an error as the GIC # technology is not applicable. # -# Returns: a list of GICCapability objects. -# # Since: 2.6 # # .. qmp-example:: diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json index 24a2e143f6..5c3c9a14e0 100644 --- a/qapi/misc-i386.json +++ b/qapi/misc-i386.json @@ -132,8 +132,6 @@ # @enabled field is set to 'false' and the state of all other fields # is unspecified. # -# Returns: @SevInfo -# # Since: 2.12 # # .. qmp-example:: @@ -214,8 +212,6 @@ # # This is only supported on AMD X86 platforms with KVM enabled. # -# Returns: SevCapability objects. -# # Errors: # - If SEV is not available on the platform, GenericError # @@ -280,8 +276,6 @@ # @mnonce: a random 16 bytes value encoded in base64 (it will be # included in report) # -# Returns: SevAttestationReport objects. -# # Errors: # - If the attestation report is unavailable, either due to an # invalid guest configuration or because the guest has not @@ -343,8 +337,6 @@ # # Return information about configured SGX capabilities of guest # -# Returns: @SgxInfo -# # Since: 6.2 # # .. qmp-example:: @@ -362,8 +354,6 @@ # # Return information about SGX capabilities of host # -# Returns: @SgxInfo -# # Since: 6.2 # # .. qmp-example:: diff --git a/qapi/misc.json b/qapi/misc.json index a180c16db2..b19eb08606 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -58,8 +58,6 @@ # # Return the name information of a guest. # -# Returns: @NameInfo of the guest -# # Since: 0.14 # # .. qmp-example:: @@ -334,9 +332,6 @@ # # @opaque: A free-form string that can be used to describe the fd. # -# Returns: -# @AddfdInfo -# # Errors: # - If file descriptor was not received, GenericError # - If @fdset-id is a negative value, GenericError @@ -417,8 +412,6 @@ # # Return information describing all fd sets. # -# Returns: A list of @FdsetInfo -# # Since: 1.2 # # .. note:: The list of fd sets is shared by all monitor connections. diff --git a/qapi/rocker.json b/qapi/rocker.json index e494964952..5d2dbd2603 100644 --- a/qapi/rocker.json +++ b/qapi/rocker.json @@ -30,8 +30,6 @@ # # @name: switch name # -# Returns: @Rocker information -# # Since: 2.4 # # .. qmp-example:: @@ -100,8 +98,6 @@ # # @name: port name # -# Returns: a list of @RockerPort information -# # Since: 2.4 # # .. qmp-example:: diff --git a/qapi/run-state.json b/qapi/run-state.json index 083a3c5eb3..76f14569ff 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -121,8 +121,6 @@ # # Query the run status of the VM # -# Returns: @StatusInfo reflecting the VM -# # Since: 0.14 # # .. qmp-example:: diff --git a/qapi/tpm.json b/qapi/tpm.json index e66b107f1d..3f2850a573 100644 --- a/qapi/tpm.json +++ b/qapi/tpm.json @@ -29,8 +29,6 @@ # # Return a list of supported TPM models # -# Returns: a list of TpmModel -# # Since: 1.5 # # .. qmp-example:: @@ -60,8 +58,6 @@ # # Return a list of supported TPM types # -# Returns: a list of TpmType -# # Since: 1.5 # # .. qmp-example:: diff --git a/qapi/ui.json b/qapi/ui.json index f5e77ae19d..53489ab65f 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -328,8 +328,6 @@ # # Return information about the current SPICE server # -# Returns: @SpiceInfo -# # Since: 0.14 # # .. qmp-example:: @@ -660,8 +658,6 @@ # # Return information about the current VNC server # -# Returns: @VncInfo -# # Since: 0.14 # # .. qmp-example:: @@ -691,8 +687,6 @@ # # Return a list of vnc servers. The list can be empty. # -# Returns: a list of @VncInfo2 -# # Since: 2.3 ## { 'command': 'query-vnc-servers', 'returns': ['VncInfo2'], @@ -1570,8 +1564,6 @@ # # Return information about display configuration # -# Returns: @DisplayOptions -# # Since: 3.1 ## { 'command': 'query-display-options', diff --git a/qapi/virtio.json b/qapi/virtio.json index 3cac0774f7..f0753cde71 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -849,8 +849,6 @@ # # @unstable: This command is meant for debugging. # -# Returns: VirtioQueueElement information -# # Since: 7.2 # # .. qmp-example:: diff --git a/qapi/yank.json b/qapi/yank.json index d13a8e9117..c14de5229e 100644 --- a/qapi/yank.json +++ b/qapi/yank.json @@ -104,7 +104,7 @@ # # Query yank instances. See @YankInstance for more information. # -# Returns: list of @YankInstance +# TODO: This line is a hack to separate the example from the body # # .. qmp-example:: # From f7296f8de5c96c09452a0774435c1e5b512fab19 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:10:45 -0400 Subject: [PATCH 2259/2760] qapi: rephrase return docs to avoid type name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Returns: " is rendered like "Return: ". Mentioning the type in the description again is commonly redundant. Rephrase such descriptions not to. Well, I tried. Maybe not very hard. Sorry! Signed-off-by: John Snow Message-ID: <20250711051045.51110-5-jsnow@redhat.com> Acked-by: Markus Armbruster [Commit message amended to explain why] Signed-off-by: Markus Armbruster --- qapi/block-core.json | 6 +++--- qapi/block-export.json | 2 +- qapi/block.json | 2 +- qapi/control.json | 5 ++--- qapi/dump.json | 5 ++--- qapi/introspect.json | 6 +++--- qapi/job.json | 2 +- qapi/misc-i386.json | 2 +- qapi/misc.json | 5 ++--- qapi/net.json | 2 +- qapi/pci.json | 2 +- qapi/qdev.json | 3 +-- qapi/qom.json | 8 +++----- qapi/stats.json | 2 +- qapi/trace.json | 2 +- qapi/ui.json | 2 +- qapi/virtio.json | 6 +++--- 17 files changed, 28 insertions(+), 34 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 01bf41b8b0..848ebaff5a 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -764,7 +764,7 @@ # # Get a list of BlockInfo for all virtual block devices. # -# Returns: a list of @BlockInfo describing each virtual block device. +# Returns: a list describing each virtual block device. # Filter nodes that were created implicitly are skipped over. # # Since: 0.14 @@ -1169,7 +1169,7 @@ # nodes that were created implicitly are skipped over in this # mode. (Since 2.3) # -# Returns: A list of @BlockStats for each virtual block devices. +# Returns: A list of statistics for each virtual block device. # # Since: 0.14 # @@ -1441,7 +1441,7 @@ # # Return information about long-running block device operations. # -# Returns: a list of @BlockJobInfo for each active block job +# Returns: a list of job info for each active block job # # Since: 1.1 ## diff --git a/qapi/block-export.json b/qapi/block-export.json index 2241bfecf2..fb004e35fd 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -473,7 +473,7 @@ ## # @query-block-exports: # -# Returns: A list of BlockExportInfo describing all block exports +# Returns: A list describing all block exports # # Since: 5.2 ## diff --git a/qapi/block.json b/qapi/block.json index 2d54a81c36..f0436ce871 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -89,7 +89,7 @@ # Return a list of information about each persistent reservation # manager. # -# Returns: a list of @PRManagerInfo for each persistent reservation +# Returns: a list of manager info for each persistent reservation # manager # # Since: 3.0 diff --git a/qapi/control.json b/qapi/control.json index 5fed0701f8..931a860b30 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -95,8 +95,7 @@ # # Return the current version of QEMU. # -# Returns: A @VersionInfo object describing the current version of -# QEMU. +# Returns: An object describing the current version of QEMU. # # Since: 0.14 # @@ -133,7 +132,7 @@ # # Return a list of supported QMP commands by this server # -# Returns: A list of @CommandInfo for all supported commands +# Returns: A list of all supported commands # # Since: 0.14 # diff --git a/qapi/dump.json b/qapi/dump.json index 0642ca157b..dc92b53247 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -148,7 +148,7 @@ # # Query latest dump status. # -# Returns: A @DumpStatus object showing the dump status. +# Returns: An object showing the dump status. # # Since: 2.6 # @@ -199,8 +199,7 @@ # # Return the available formats for dump-guest-memory # -# Returns: A @DumpGuestMemoryCapability object listing available -# formats for dump-guest-memory +# Returns: An object listing available formats for dump-guest-memory # # Since: 2.0 # diff --git a/qapi/introspect.json b/qapi/introspect.json index 26d8839f19..be8511b067 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -36,10 +36,10 @@ # string into a specific enum or from one specific type into an # alternate that includes the original type alongside something else. # -# Returns: array of @SchemaInfo, where each element describes an -# entity in the ABI: command, event, type, ... +# Returns: an array where each element describes an entity in the ABI: +# command, event, type, ... # -# The order of the various SchemaInfo is unspecified; however, all +# The order of the various elements is unspecified; however, all # names are guaranteed to be unique (no name will be duplicated # with different meta-types). # diff --git a/qapi/job.json b/qapi/job.json index 16b280f52f..51a57c523e 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -297,7 +297,7 @@ # # Return information about jobs. # -# Returns: a list with a @JobInfo for each active job +# Returns: a list with info for each active job # # Since: 3.0 ## diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json index 5c3c9a14e0..282bd3627b 100644 --- a/qapi/misc-i386.json +++ b/qapi/misc-i386.json @@ -163,7 +163,7 @@ # 'sev-guest' confidential virtualization object. The launch # measurement for SEV-SNP guests is only available within the guest. # -# Returns: The @SevLaunchMeasureInfo for the guest +# Returns: The guest's SEV guest launch measurement info # # Errors: # - If the launch measurement is unavailable, either due to an diff --git a/qapi/misc.json b/qapi/misc.json index b19eb08606..7dbc29dbd6 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -107,7 +107,7 @@ # declared using the ``-object iothread`` command-line option. It # is always the main thread of the process. # -# Returns: a list of @IOThreadInfo for each iothread +# Returns: a list of info for each iothread # # Since: 2.0 # @@ -511,8 +511,7 @@ # # @option: option name # -# Returns: list of @CommandLineOptionInfo for all options (or for the -# given @option). +# Returns: list of objects for all options (or for the given @option). # # Errors: # - if the given @option doesn't exist diff --git a/qapi/net.json b/qapi/net.json index 3b03843c16..a266c6786e 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -847,7 +847,7 @@ # # @name: net client name # -# Returns: list of @RxFilterInfo for all NICs (or for the given NIC). +# Returns: list of info for all NICs (or for the given NIC). # # Errors: # - if the given @name doesn't exist diff --git a/qapi/pci.json b/qapi/pci.json index a8671cd9ac..418ea4fc93 100644 --- a/qapi/pci.json +++ b/qapi/pci.json @@ -177,7 +177,7 @@ # # Return information about the PCI bus topology of the guest. # -# Returns: a list of @PciInfo for each PCI bus. Each bus is +# Returns: a list of info for each PCI bus. Each bus is # represented by a json-object, which has a key with a json-array # of all PCI devices attached to it. Each device is represented # by a json-object. diff --git a/qapi/qdev.json b/qapi/qdev.json index 441ed518b8..6441357e00 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -19,8 +19,7 @@ # # @typename: the type name of a device # -# Returns: a list of ObjectPropertyInfo describing a devices -# properties +# Returns: a list describing a devices properties # # .. note:: Objects can create properties at runtime, for example to # describe links between different devices and/or objects. These diff --git a/qapi/qom.json b/qapi/qom.json index fbf94b9e09..aa09eddbf9 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -55,8 +55,7 @@ # @path: the path within the object model. See @qom-get for a # description of this parameter. # -# Returns: a list of @ObjectPropertyInfo that describe the properties -# of the object. +# Returns: a list that describe the properties of the object. # # Since: 1.2 # @@ -178,8 +177,7 @@ # # @abstract: if true, include abstract types in the results # -# Returns: a list of @ObjectTypeInfo or an empty list if no results -# are found +# Returns: a list of types, or an empty list if no results are found # # Since: 1.1 ## @@ -199,7 +197,7 @@ # describe links between different devices and/or objects. These # properties are not included in the output of this command. # -# Returns: a list of ObjectPropertyInfo describing object properties +# Returns: a list describing object properties # # Since: 2.12 ## diff --git a/qapi/stats.json b/qapi/stats.json index 78b88881ea..b63a7369dd 100644 --- a/qapi/stats.json +++ b/qapi/stats.json @@ -188,7 +188,7 @@ # The arguments are a StatsFilter and specify the provider and objects # to return statistics about. # -# Returns: a list of StatsResult, one for each provider and object +# Returns: a list of statistics, one for each provider and object # (e.g., for each vCPU). # # Since: 7.1 diff --git a/qapi/trace.json b/qapi/trace.json index d08c9c6a88..de369dae6b 100644 --- a/qapi/trace.json +++ b/qapi/trace.json @@ -49,7 +49,7 @@ # # @name: Event name pattern (case-sensitive glob). # -# Returns: a list of @TraceEventInfo for the matching events +# Returns: a list of info for the matching events # # Since: 2.2 # diff --git a/qapi/ui.json b/qapi/ui.json index 53489ab65f..b48266c458 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -822,7 +822,7 @@ # # Return information about each active mouse device # -# Returns: a list of @MouseInfo for each device +# Returns: a list of info for each device # # Since: 0.14 # diff --git a/qapi/virtio.json b/qapi/virtio.json index f0753cde71..2e23512085 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -201,7 +201,7 @@ # # @unstable: This command is meant for debugging. # -# Returns: VirtioStatus of the virtio device +# Returns: Status of the virtio device # # Since: 7.2 # @@ -565,7 +565,7 @@ # # @unstable: This command is meant for debugging. # -# Returns: VirtQueueStatus of the VirtQueue +# Returns: Status of the queue # # .. note:: last_avail_idx will not be displayed in the case where the # selected VirtIODevice has a running vhost device and the @@ -700,7 +700,7 @@ # # @unstable: This command is meant for debugging. # -# Returns: VirtVhostQueueStatus of the vhost_virtqueue +# Returns: Status of the vhost_virtqueue # # Since: 7.2 # From 17c2c399bdf75c314bfce97fb9fb21badc9e4465 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 10 Jul 2025 11:45:25 +0100 Subject: [PATCH 2260/2760] gitlab: add -n option to check-units script MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mostly a developer aid for those who want to look at the full backlog of multiple build units. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250710104531.3099313-3-alex.bennee@linaro.org> --- .gitlab-ci.d/check-units.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.gitlab-ci.d/check-units.py b/.gitlab-ci.d/check-units.py index cdc62ae5ee..cebef0e8be 100755 --- a/.gitlab-ci.d/check-units.py +++ b/.gitlab-ci.d/check-units.py @@ -30,7 +30,7 @@ def extract_build_units(cc_path): return build_units -def analyse_units(build_units): +def analyse_units(build_units, top_n): """ Analyse the build units and report stats and the top 10 rebuilds """ @@ -44,7 +44,7 @@ def analyse_units(build_units): reverse=True) print("Most rebuilt units:") - for unit, count in sorted_build_units[:20]: + for unit, count in sorted_build_units[:top_n]: print(f" {unit} built {count} times") print("Least rebuilt units:") @@ -57,12 +57,14 @@ def analyse_units(build_units): description="analyse number of build units in compile_commands.json") parser.add_argument("cc_path", type=Path, default=None, help="Path to compile_commands.json") + parser.add_argument("-n", type=int, default=20, + help="Dump the top entries") args = parser.parse_args() if path.isfile(args.cc_path) and access(args.cc_path, R_OK): units = extract_build_units(args.cc_path) - analyse_units(units) + analyse_units(units, args.n) exit(0) else: print(f"{args.cc_path} doesn't exist or isn't readable") From 81143b7f9d9f6645dcd6e9ea3fca0942ab25c337 Mon Sep 17 00:00:00 2001 From: Yodel Eldar Date: Thu, 10 Jul 2025 11:45:26 +0100 Subject: [PATCH 2261/2760] contrib/plugins/execlog: Add tab to the separator search of insn_disas MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, execlog searches for a space separator between the instruction mnemonic and operands, but some disassemblers, e.g. Alpha's, use a tab separator instead; this results in a null pointer being passed as the haystack in g_strstr during a subsequent register search, i.e. undefined behavior, because of a missing null check. This patch adds tab to the separator search and a null check on the result. Also, an affected pointer is changed to const. Lastly, a break statement was added to immediately terminate the register search when a user-requested register is found in the current instruction as a trivial optimization, because searching for the remaining requested registers is unnecessary once one is found. Suggested-by: Alex Bennée Signed-off-by: Yodel Eldar Message-ID: <20250630164124.26315-2-yodel.eldar@gmail.com> Reviewed-by: Pierrick Bouvier Signed-off-by: Alex Bennée Message-ID: <20250710104531.3099313-4-alex.bennee@linaro.org> --- contrib/plugins/execlog.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/contrib/plugins/execlog.c b/contrib/plugins/execlog.c index d67d010761..06ec76d6e9 100644 --- a/contrib/plugins/execlog.c +++ b/contrib/plugins/execlog.c @@ -232,12 +232,15 @@ static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) */ if (disas_assist && rmatches) { check_regs_next = false; - gchar *args = g_strstr_len(insn_disas, -1, " "); - for (int n = 0; n < all_reg_names->len; n++) { - gchar *reg = g_ptr_array_index(all_reg_names, n); - if (g_strrstr(args, reg)) { - check_regs_next = true; - skip = false; + g_auto(GStrv) args = g_strsplit_set(insn_disas, " \t", 2); + if (args && args[1]) { + for (int n = 0; n < all_reg_names->len; n++) { + const gchar *reg = g_ptr_array_index(all_reg_names, n); + if (g_strrstr(args[1], reg)) { + check_regs_next = true; + skip = false; + break; + } } } } From 5a28fa5ba17254d0398a854657b47af3096bd86a Mon Sep 17 00:00:00 2001 From: Yodel Eldar Date: Thu, 10 Jul 2025 11:45:27 +0100 Subject: [PATCH 2262/2760] target/alpha: Add GDB XML feature file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the GDB XML feature file that describes Alpha's core registers. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2569 Reviewed-by: Richard Henderson Signed-off-by: Yodel Eldar Message-ID: <20250630164124.26315-3-yodel.eldar@gmail.com> Signed-off-by: Alex Bennée Message-ID: <20250710104531.3099313-5-alex.bennee@linaro.org> --- configs/targets/alpha-linux-user.mak | 1 + configs/targets/alpha-softmmu.mak | 1 + gdb-xml/alpha-core.xml | 136 +++++++++++++++++++++++++++ target/alpha/cpu.c | 1 + 4 files changed, 139 insertions(+) create mode 100644 gdb-xml/alpha-core.xml diff --git a/configs/targets/alpha-linux-user.mak b/configs/targets/alpha-linux-user.mak index ef8e365b09..aa25766236 100644 --- a/configs/targets/alpha-linux-user.mak +++ b/configs/targets/alpha-linux-user.mak @@ -2,3 +2,4 @@ TARGET_ARCH=alpha TARGET_SYSTBL_ABI=common TARGET_SYSTBL=syscall.tbl TARGET_LONG_BITS=64 +TARGET_XML_FILES= gdb-xml/alpha-core.xml diff --git a/configs/targets/alpha-softmmu.mak b/configs/targets/alpha-softmmu.mak index 5275076e50..e31f059a52 100644 --- a/configs/targets/alpha-softmmu.mak +++ b/configs/targets/alpha-softmmu.mak @@ -1,2 +1,3 @@ TARGET_ARCH=alpha TARGET_LONG_BITS=64 +TARGET_XML_FILES= gdb-xml/alpha-core.xml diff --git a/gdb-xml/alpha-core.xml b/gdb-xml/alpha-core.xml new file mode 100644 index 0000000000..c9e12f4ffd --- /dev/null +++ b/gdb-xml/alpha-core.xml @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/target/alpha/cpu.c b/target/alpha/cpu.c index 2082db45ea..bf1787a69d 100644 --- a/target/alpha/cpu.c +++ b/target/alpha/cpu.c @@ -286,6 +286,7 @@ static void alpha_cpu_class_init(ObjectClass *oc, const void *data) cc->get_pc = alpha_cpu_get_pc; cc->gdb_read_register = alpha_cpu_gdb_read_register; cc->gdb_write_register = alpha_cpu_gdb_write_register; + cc->gdb_core_xml_file = "alpha-core.xml"; #ifndef CONFIG_USER_ONLY dc->vmsd = &vmstate_alpha_cpu; cc->sysemu_ops = &alpha_sysemu_ops; From dcc83c3e41a89a1426fb56106c882479185f6383 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 10 Jul 2025 11:45:28 +0100 Subject: [PATCH 2263/2760] plugins: fix inclusion of user-mode APIs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In 903e870f24 (plugins/api: split out binary path/start/end/entry code) we didn't actually enable the building of the new plugin helper. However this was missed because only contrib plugins like drcov actually used the helpers. With that fixed we discover we also need some more includes to be able to extract the relevant data from TaskState. Fixes: 903e870f24 (plugins/api: split out binary path/start/end/entry code) Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3014 Reviewed-by: Pierrick Bouvier Signed-off-by: Alex Bennée Message-ID: <20250710104531.3099313-6-alex.bennee@linaro.org> --- common-user/plugin-api.c.inc | 1 + linux-user/meson.build | 5 ++++- linux-user/plugin-api.c | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/common-user/plugin-api.c.inc b/common-user/plugin-api.c.inc index 5b8a1396b6..63f3983271 100644 --- a/common-user/plugin-api.c.inc +++ b/common-user/plugin-api.c.inc @@ -13,6 +13,7 @@ #include "qemu/osdep.h" #include "qemu/main-loop.h" #include "qemu/plugin.h" +#include "accel/tcg/vcpu-state.h" #include "qemu.h" /* diff --git a/linux-user/meson.build b/linux-user/meson.build index f47a213ca3..efca843369 100644 --- a/linux-user/meson.build +++ b/linux-user/meson.build @@ -27,7 +27,10 @@ linux_user_ss.add(libdw) linux_user_ss.add(when: 'TARGET_HAS_BFLT', if_true: files('flatload.c')) linux_user_ss.add(when: 'TARGET_I386', if_true: files('vm86.c')) linux_user_ss.add(when: 'CONFIG_ARM_COMPATIBLE_SEMIHOSTING', if_true: files('semihost.c')) -linux_user_ss.add(when: 'CONFIG_TCG_PLUGINS', if_true: files('plugin-api.c')) + +if get_option('plugins') + linux_user_ss.add(files('plugin-api.c')) +endif syscall_nr_generators = {} diff --git a/linux-user/plugin-api.c b/linux-user/plugin-api.c index 66755df526..8d6fbb60e0 100644 --- a/linux-user/plugin-api.c +++ b/linux-user/plugin-api.c @@ -12,4 +12,5 @@ #include "qemu/osdep.h" #include "qemu.h" +#include "loader.h" #include "common-user/plugin-api.c.inc" From e962d3c921e00395a7d151adba617249b7579f7a Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 10 Jul 2025 11:45:29 +0100 Subject: [PATCH 2264/2760] docs: use :kbd: role in sphinx docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sphinx supports the :kbd: role for notating keyboard input. They get formatted as HTML elements in the readthedocs theme we currently use for Sphinx. Besides the better visual formatting, it also helps with accessibility as screen readers can announce the semantics of the element to the user. Signed-off-by: Manos Pitsidianakis Message-ID: <20250709-docs_rst_improvements-v2-1-cb5096ad0022@linaro.org> Signed-off-by: Alex Bennée Message-ID: <20250710104531.3099313-7-alex.bennee@linaro.org> --- docs/devel/testing/main.rst | 4 +-- docs/system/images.rst | 2 +- docs/system/keys.rst.inc | 49 +++++++++++++++++---------------- docs/system/linuxboot.rst | 2 +- docs/system/mux-chardev.rst.inc | 38 ++++++++++++++----------- 5 files changed, 51 insertions(+), 44 deletions(-) diff --git a/docs/devel/testing/main.rst b/docs/devel/testing/main.rst index 6b18ed875c..2b5cb0c148 100644 --- a/docs/devel/testing/main.rst +++ b/docs/devel/testing/main.rst @@ -604,9 +604,9 @@ below steps to debug it: 2. Add "V=1" to the command line, try again, to see the verbose output. 3. Further add "DEBUG=1" to the command line. This will pause in a shell prompt in the container right before testing starts. You could either manually - build QEMU and run tests from there, or press Ctrl-D to let the Docker + build QEMU and run tests from there, or press :kbd:`Ctrl+d` to let the Docker testing continue. -4. If you press Ctrl-D, the same building and testing procedure will begin, and +4. If you press :kbd:`Ctrl+d`, the same building and testing procedure will begin, and will hopefully run into the error again. After that, you will be dropped to the prompt for debug. diff --git a/docs/system/images.rst b/docs/system/images.rst index a5551173c9..43706969fd 100644 --- a/docs/system/images.rst +++ b/docs/system/images.rst @@ -30,7 +30,7 @@ Snapshot mode If you use the option ``-snapshot``, all disk images are considered as read only. When sectors in written, they are written in a temporary file created in ``/tmp``. You can however force the write back to the raw -disk images by using the ``commit`` monitor command (or C-a s in the +disk images by using the ``commit`` monitor command (or :kbd:`Ctrl+a s` in the serial console). .. _vm_005fsnapshots: diff --git a/docs/system/keys.rst.inc b/docs/system/keys.rst.inc index 59966a3fe7..c28ae1a227 100644 --- a/docs/system/keys.rst.inc +++ b/docs/system/keys.rst.inc @@ -1,36 +1,37 @@ During the graphical emulation, you can use special key combinations from -the following table to change modes. By default the modifier is Ctrl-Alt +the following table to change modes. By default the modifier is :kbd:`Ctrl+Alt` (used in the table below) which can be changed with ``-display`` suboption ``mod=`` where appropriate. For example, ``-display sdl, -grab-mod=lshift-lctrl-lalt`` changes the modifier key to Ctrl-Alt-Shift, -while ``-display sdl,grab-mod=rctrl`` changes it to the right Ctrl key. +grab-mod=lshift-lctrl-lalt`` changes the modifier key to :kbd:`Ctrl+Alt+Shift`, +while ``-display sdl,grab-mod=rctrl`` changes it to the right :kbd:`Ctrl` key. -Ctrl-Alt-f - Toggle full screen +.. list-table:: Multiplexer Keys + :widths: 10 90 + :header-rows: 1 -Ctrl-Alt-+ - Enlarge the screen + * - Key Sequence + - Action -Ctrl-Alt\-- - Shrink the screen + * - :kbd:`Ctrl+Alt+f` + - Toggle full screen -Ctrl-Alt-u - Restore the screen's un-scaled dimensions + * - :kbd:`Ctrl+Alt++` + - Enlarge the screen -Ctrl-Alt-n - Switch to virtual console 'n'. Standard console mappings are: + * - :kbd:`Ctrl+Alt+-` + - Shrink the screen - *1* - Target system display + * - :kbd:`Ctrl+Alt+u` + - Restore the screen's un-scaled dimensions - *2* - Monitor + * - :kbd:`Ctrl+Alt+n` + - Switch to virtual console 'n'. Standard console mappings are: - *3* - Serial port + - *1*: Target system display + - *2*: Monitor + - *3*: Serial port + * - :kbd:`Ctrl+Alt+g` + - Toggle mouse and keyboard grab. -Ctrl-Alt-g - Toggle mouse and keyboard grab. - -In the virtual consoles, you can use Ctrl-Up, Ctrl-Down, Ctrl-PageUp and -Ctrl-PageDown to move in the back log. +In the virtual consoles, you can use :kbd:`Ctrl+Up`, :kbd:`Ctrl+Down`, :kbd:`Ctrl+PageUp` and +:kbd:`Ctrl+PageDown` to move in the back log. diff --git a/docs/system/linuxboot.rst b/docs/system/linuxboot.rst index 2328b4a73d..f7573ab80a 100644 --- a/docs/system/linuxboot.rst +++ b/docs/system/linuxboot.rst @@ -26,5 +26,5 @@ virtual serial port and the QEMU monitor to the console with the |qemu_system| -kernel bzImage -drive file=rootdisk.img,format=raw \ -append "root=/dev/sda console=ttyS0" -nographic -Use Ctrl-a c to switch between the serial console and the monitor (see +Use :kbd:`Ctrl+a c` to switch between the serial console and the monitor (see :ref:`GUI_keys`). diff --git a/docs/system/mux-chardev.rst.inc b/docs/system/mux-chardev.rst.inc index 84ea12cbf5..c87ba31362 100644 --- a/docs/system/mux-chardev.rst.inc +++ b/docs/system/mux-chardev.rst.inc @@ -1,27 +1,33 @@ During emulation, if you are using a character backend multiplexer (which is the default if you are using ``-nographic``) then several commands are available via an escape sequence. These key sequences all -start with an escape character, which is Ctrl-a by default, but can be +start with an escape character, which is :kbd:`Ctrl+a` by default, but can be changed with ``-echr``. The list below assumes you're using the default. -Ctrl-a h - Print this help +.. list-table:: Multiplexer Keys + :widths: 20 80 + :header-rows: 1 -Ctrl-a x - Exit emulator + * - Key Sequence + - Action -Ctrl-a s - Save disk data back to file (if -snapshot) + * - :kbd:`Ctrl+a h` + - Print this help -Ctrl-a t - Toggle console timestamps + * - :kbd:`Ctrl+a x` + - Exit emulator -Ctrl-a b - Send break (magic sysrq in Linux) + * - :kbd:`Ctrl+a s` + - Save disk data back to file (if -snapshot) -Ctrl-a c - Rotate between the frontends connected to the multiplexer (usually - this switches between the monitor and the console) + * - :kbd:`Ctrl+a t` + - Toggle console timestamps -Ctrl-a Ctrl-a - Send the escape character to the frontend + * - :kbd:`Ctrl+a b` + - Send break (magic sysrq in Linux) + + * - :kbd:`Ctrl+a c` + - Rotate between the frontends connected to the multiplexer (usually this switches between the monitor and the console) + + * - :kbd:`Ctrl+a Ctrl+a` + - Send the escape character to the frontend From 15f5acb8102c94d7a6aa79370fad776cf42b63d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 10 Jul 2025 11:45:30 +0100 Subject: [PATCH 2265/2760] docs/system: clean-up formatting of virtio-net-failover MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We didn't clean-up the rst formatting when we moved this into the docs so lets do that now: - un-indent the usage/hotplug/migration paragraphs - properly wrap the command line fragments in code-block - highlight parameters in text with ``double quotes`` No changes to the actual text. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250710104531.3099313-8-alex.bennee@linaro.org> --- docs/system/virtio-net-failover.rst | 51 ++++++++++++++++------------- 1 file changed, 28 insertions(+), 23 deletions(-) diff --git a/docs/system/virtio-net-failover.rst b/docs/system/virtio-net-failover.rst index 6002dc5d96..0cc465454c 100644 --- a/docs/system/virtio-net-failover.rst +++ b/docs/system/virtio-net-failover.rst @@ -26,43 +26,48 @@ and standby devices are not plugged into the same PCIe slot. Usecase ------- - Virtio-net standby allows easy migration while using a passed-through fast - networking device by falling back to a virtio-net device for the duration of - the migration. It is like a simple version of a bond, the difference is that it - requires no configuration in the guest. When a guest is live-migrated to - another host QEMU will unplug the primary device via the PCIe based hotplug - handler and traffic will go through the virtio-net device. On the target - system the primary device will be automatically plugged back and the - net_failover module registers it again as the primary device. +Virtio-net standby allows easy migration while using a passed-through +fast networking device by falling back to a virtio-net device for the +duration of the migration. It is like a simple version of a bond, the +difference is that it requires no configuration in the guest. When a +guest is live-migrated to another host QEMU will unplug the primary +device via the PCIe based hotplug handler and traffic will go through +the virtio-net device. On the target system the primary device will be +automatically plugged back and the net_failover module registers it +again as the primary device. Usage ----- - The primary device can be hotplugged or be part of the startup configuration +The primary device can be hotplugged or be part of the startup configuration - -device virtio-net-pci,netdev=hostnet1,id=net1,mac=52:54:00:6f:55:cc, \ - bus=root2,failover=on +.. code-block:: shell - With the parameter failover=on the VIRTIO_NET_F_STANDBY feature will be enabled. + -device virtio-net-pci,netdev=hostnet1,id=net1,mac=52:54:00:6f:55:cc,bus=root2,failover=on + +With the parameter ``failover=on`` the VIRTIO_NET_F_STANDBY feature will be enabled. + +.. code-block:: shell -device vfio-pci,host=5e:00.2,id=hostdev0,bus=root1,failover_pair_id=net1 - failover_pair_id references the id of the virtio-net standby device. This - is only for pairing the devices within QEMU. The guest kernel module - net_failover will match devices with identical MAC addresses. +``failover_pair_id`` references the id of the virtio-net standby device. +This is only for pairing the devices within QEMU. The guest kernel +module net_failover will match devices with identical MAC addresses. Hotplug ------- - Both primary and standby device can be hotplugged via the QEMU monitor. Note - that if the virtio-net device is plugged first a warning will be issued that it - couldn't find the primary device. +Both primary and standby device can be hotplugged via the QEMU +monitor. Note that if the virtio-net device is plugged first a warning +will be issued that it couldn't find the primary device. Migration --------- - A new migration state wait-unplug was added for this feature. If failover primary - devices are present in the configuration, migration will go into this state. - It will wait until the device unplug is completed in the guest and then move into - active state. On the target system the primary devices will be automatically hotplugged - when the feature bit was negotiated for the virtio-net standby device. +A new migration state wait-unplug was added for this feature. If +failover primary devices are present in the configuration, migration +will go into this state. It will wait until the device unplug is +completed in the guest and then move into active state. On the target +system the primary devices will be automatically hotplugged when the +feature bit was negotiated for the virtio-net standby device. From 9152540f4ef3528ff003493cbe6a27b6c0f322e8 Mon Sep 17 00:00:00 2001 From: Rot127 Date: Fri, 11 Jul 2025 10:51:39 -0500 Subject: [PATCH 2266/2760] gdbstub: add the GDB register XML files for sparc64. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Rot127 Message-ID: <20250711155141.62916-2-unisono@quyllur.org> [AJB: clean up commit msg] Signed-off-by: Alex Bennée --- configs/targets/sparc64-linux-user.mak | 1 + configs/targets/sparc64-softmmu.mak | 1 + gdb-xml/sparc64-core.xml | 99 ++++++++++++++++++++++++++ target/sparc/cpu.c | 1 + 4 files changed, 102 insertions(+) create mode 100644 gdb-xml/sparc64-core.xml diff --git a/configs/targets/sparc64-linux-user.mak b/configs/targets/sparc64-linux-user.mak index 64ea04e3e2..7c2ecb7be0 100644 --- a/configs/targets/sparc64-linux-user.mak +++ b/configs/targets/sparc64-linux-user.mak @@ -4,4 +4,5 @@ TARGET_ABI_DIR=sparc TARGET_SYSTBL_ABI=common,64 TARGET_SYSTBL=syscall.tbl TARGET_BIG_ENDIAN=y +TARGET_XML_FILES=gdb-xml/sparc64-core.xml TARGET_LONG_BITS=64 diff --git a/configs/targets/sparc64-softmmu.mak b/configs/targets/sparc64-softmmu.mak index 2504e31ae3..d9d51d21e5 100644 --- a/configs/targets/sparc64-softmmu.mak +++ b/configs/targets/sparc64-softmmu.mak @@ -1,4 +1,5 @@ TARGET_ARCH=sparc64 TARGET_BASE_ARCH=sparc TARGET_BIG_ENDIAN=y +TARGET_XML_FILES=gdb-xml/sparc64-core.xml TARGET_LONG_BITS=64 diff --git a/gdb-xml/sparc64-core.xml b/gdb-xml/sparc64-core.xml new file mode 100644 index 0000000000..375b9bb0cc --- /dev/null +++ b/gdb-xml/sparc64-core.xml @@ -0,0 +1,99 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/target/sparc/cpu.c b/target/sparc/cpu.c index ed7701b02f..245caf2de0 100644 --- a/target/sparc/cpu.c +++ b/target/sparc/cpu.c @@ -1090,6 +1090,7 @@ static void sparc_cpu_class_init(ObjectClass *oc, const void *data) cc->disas_set_info = cpu_sparc_disas_set_info; #if defined(TARGET_SPARC64) && !defined(TARGET_ABI32) + cc->gdb_core_xml_file = "sparc64-core.xml"; cc->gdb_num_core_regs = 86; #else cc->gdb_num_core_regs = 72; From aef22331b5a4670f42638a5f63a26e93bf779aae Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 3 Jun 2025 18:18:28 +0900 Subject: [PATCH 2267/2760] ui/vnc: Do not copy z_stream MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit vnc_worker_thread_loop() copies z_stream stored in its local VncState to the persistent VncState, and the copied one is freed with deflateEnd() later. However, deflateEnd() refuses to operate with a copied z_stream and returns Z_STREAM_ERROR, leaking the allocated memory. Avoid copying the zlib state to fix the memory leak. Fixes: bd023f953e5e ("vnc: threaded VNC server") Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250603-zlib-v3-1-20b857bd8d05@rsg.ci.i.u-tokyo.ac.jp> --- ui/vnc-enc-zlib.c | 30 +++++++++++++++--------------- ui/vnc.c | 13 ++++++++++--- ui/vnc.h | 2 +- 3 files changed, 26 insertions(+), 19 deletions(-) diff --git a/ui/vnc-enc-zlib.c b/ui/vnc-enc-zlib.c index 900ae5b30f..52e9193eab 100644 --- a/ui/vnc-enc-zlib.c +++ b/ui/vnc-enc-zlib.c @@ -48,21 +48,21 @@ void vnc_zlib_zfree(void *x, void *addr) static void vnc_zlib_start(VncState *vs) { - buffer_reset(&vs->zlib.zlib); + buffer_reset(&vs->zlib->zlib); // make the output buffer be the zlib buffer, so we can compress it later - vs->zlib.tmp = vs->output; - vs->output = vs->zlib.zlib; + vs->zlib->tmp = vs->output; + vs->output = vs->zlib->zlib; } static int vnc_zlib_stop(VncState *vs) { - z_streamp zstream = &vs->zlib.stream; + z_streamp zstream = &vs->zlib->stream; int previous_out; // switch back to normal output/zlib buffers - vs->zlib.zlib = vs->output; - vs->output = vs->zlib.tmp; + vs->zlib->zlib = vs->output; + vs->output = vs->zlib->tmp; // compress the zlib buffer @@ -85,24 +85,24 @@ static int vnc_zlib_stop(VncState *vs) return -1; } - vs->zlib.level = vs->tight->compression; + vs->zlib->level = vs->tight->compression; zstream->opaque = vs; } - if (vs->tight->compression != vs->zlib.level) { + if (vs->tight->compression != vs->zlib->level) { if (deflateParams(zstream, vs->tight->compression, Z_DEFAULT_STRATEGY) != Z_OK) { return -1; } - vs->zlib.level = vs->tight->compression; + vs->zlib->level = vs->tight->compression; } // reserve memory in output buffer - buffer_reserve(&vs->output, vs->zlib.zlib.offset + 64); + buffer_reserve(&vs->output, vs->zlib->zlib.offset + 64); // set pointers - zstream->next_in = vs->zlib.zlib.buffer; - zstream->avail_in = vs->zlib.zlib.offset; + zstream->next_in = vs->zlib->zlib.buffer; + zstream->avail_in = vs->zlib->zlib.offset; zstream->next_out = vs->output.buffer + vs->output.offset; zstream->avail_out = vs->output.capacity - vs->output.offset; previous_out = zstream->avail_out; @@ -147,8 +147,8 @@ int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) void vnc_zlib_clear(VncState *vs) { - if (vs->zlib.stream.opaque) { - deflateEnd(&vs->zlib.stream); + if (vs->zlib->stream.opaque) { + deflateEnd(&vs->zlib->stream); } - buffer_free(&vs->zlib.zlib); + buffer_free(&vs->zlib->zlib); } diff --git a/ui/vnc.c b/ui/vnc.c index e9c30aad62..ab74154e4c 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -56,6 +56,11 @@ #include "io/dns-resolver.h" #include "monitor/monitor.h" +typedef struct VncConnection { + VncState vs; + VncZlib zlib; +} VncConnection; + #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 #define VNC_REFRESH_INTERVAL_MAX GUI_REFRESH_INTERVAL_IDLE @@ -1362,7 +1367,7 @@ void vnc_disconnect_finish(VncState *vs) vs->magic = 0; g_free(vs->zrle); g_free(vs->tight); - g_free(vs); + g_free(container_of(vs, VncConnection, vs)); } size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error *err) @@ -3241,11 +3246,13 @@ static void vnc_refresh(DisplayChangeListener *dcl) static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, bool skipauth, bool websocket) { - VncState *vs = g_new0(VncState, 1); + VncConnection *vc = g_new0(VncConnection, 1); + VncState *vs = &vc->vs; bool first_client = QTAILQ_EMPTY(&vd->clients); int i; trace_vnc_client_connect(vs, sioc); + vs->zlib = &vc->zlib; vs->zrle = g_new0(VncZrle, 1); vs->tight = g_new0(VncTight, 1); vs->magic = VNC_MAGIC; @@ -3268,7 +3275,7 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, #ifdef CONFIG_PNG buffer_init(&vs->tight->png, "vnc-tight-png/%p", sioc); #endif - buffer_init(&vs->zlib.zlib, "vnc-zlib/%p", sioc); + buffer_init(&vc->zlib.zlib, "vnc-zlib/%p", sioc); buffer_init(&vs->zrle->zrle, "vnc-zrle/%p", sioc); buffer_init(&vs->zrle->fb, "vnc-zrle-fb/%p", sioc); buffer_init(&vs->zrle->zlib, "vnc-zrle-zlib/%p", sioc); diff --git a/ui/vnc.h b/ui/vnc.h index b3e07269bb..8df0cbab25 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -342,7 +342,7 @@ struct VncState * update vnc_async_encoding_start() */ VncTight *tight; - VncZlib zlib; + VncZlib *zlib; VncHextile hextile; VncZrle *zrle; VncZywrle zywrle; From 37ff925d5d4a70a951ade42094896246c989742f Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 3 Jun 2025 18:18:29 +0900 Subject: [PATCH 2268/2760] ui/vnc: Introduce the VncWorker type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The worker thread copies data in VncState to avoid race, but some data are too big to copy. Such data are held with pointers to avoid the overhead to copy, but it requires tedious memory management and makes them vulnerable to race. Introduce the VncWorker type to contain all data shared without copying. It allows allocating and freeing all shared data at once and shows that the race with the worker thread needs to be taken care of when accessing them. Signed-off-by: Akihiko Odaki Reviewed-by: Marc-André Lureau Message-Id: <20250603-zlib-v3-2-20b857bd8d05@rsg.ci.i.u-tokyo.ac.jp> --- ui/vnc-enc-tight.c | 456 ++++++++++++++++++++++-------------------- ui/vnc-enc-zlib.c | 47 ++--- ui/vnc-enc-zrle.c | 122 +++++------ ui/vnc-enc-zrle.c.inc | 20 +- ui/vnc-jobs.c | 13 +- ui/vnc.c | 86 ++++---- ui/vnc.h | 49 +++-- 7 files changed, 405 insertions(+), 388 deletions(-) diff --git a/ui/vnc-enc-tight.c b/ui/vnc-enc-tight.c index 25c7b2c788..9dfe6ae5a2 100644 --- a/ui/vnc-enc-tight.c +++ b/ui/vnc-enc-tight.c @@ -72,8 +72,8 @@ static const struct { }; -static int tight_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h); +static int tight_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); #ifdef CONFIG_VNC_JPEG static const struct { @@ -111,12 +111,12 @@ static const struct { { 9, PNG_ALL_FILTERS }, }; -static int send_png_rect(VncState *vs, int x, int y, int w, int h, - VncPalette *palette); +static int send_png_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, VncPalette *palette); -static bool tight_can_send_png_rect(VncState *vs, int w, int h) +static bool tight_can_send_png_rect(VncState *vs, VncTight *tight, int w, int h) { - if (vs->tight->type != VNC_ENCODING_TIGHT_PNG) { + if (tight->type != VNC_ENCODING_TIGHT_PNG) { return false; } @@ -135,7 +135,7 @@ static bool tight_can_send_png_rect(VncState *vs, int w, int h) */ static unsigned int -tight_detect_smooth_image24(VncState *vs, int w, int h) +tight_detect_smooth_image24(VncState *vs, VncTight *tight, int w, int h) { int off; int x, y, d, dx; @@ -144,7 +144,7 @@ tight_detect_smooth_image24(VncState *vs, int w, int h) int pixels = 0; int pix, left[3]; unsigned int errors; - unsigned char *buf = vs->tight->tight.buffer; + unsigned char *buf = tight->tight.buffer; /* * If client is big-endian, color samples begin from the second @@ -205,7 +205,8 @@ tight_detect_smooth_image24(VncState *vs, int w, int h) #define DEFINE_DETECT_FUNCTION(bpp) \ \ static unsigned int \ - tight_detect_smooth_image##bpp(VncState *vs, int w, int h) { \ + tight_detect_smooth_image##bpp(VncState *vs, VncTight *tight, \ + int w, int h) { \ bool endian; \ uint##bpp##_t pix; \ int max[3], shift[3]; \ @@ -215,7 +216,7 @@ tight_detect_smooth_image24(VncState *vs, int w, int h) int pixels = 0; \ int sample, sum, left[3]; \ unsigned int errors; \ - unsigned char *buf = vs->tight->tight.buffer; \ + unsigned char *buf = tight->tight.buffer; \ \ endian = 0; /* FIXME */ \ \ @@ -293,11 +294,11 @@ DEFINE_DETECT_FUNCTION(16) DEFINE_DETECT_FUNCTION(32) static int -tight_detect_smooth_image(VncState *vs, int w, int h) +tight_detect_smooth_image(VncState *vs, VncTight *tight, int w, int h) { unsigned int errors; - int compression = vs->tight->compression; - int quality = vs->tight->quality; + int compression = tight->compression; + int quality = tight->quality; if (!vs->vd->lossy) { return 0; @@ -309,7 +310,7 @@ tight_detect_smooth_image(VncState *vs, int w, int h) return 0; } - if (vs->tight->quality != (uint8_t)-1) { + if (tight->quality != (uint8_t)-1) { if (w * h < VNC_TIGHT_JPEG_MIN_RECT_SIZE) { return 0; } @@ -320,17 +321,17 @@ tight_detect_smooth_image(VncState *vs, int w, int h) } if (vs->client_pf.bytes_per_pixel == 4) { - if (vs->tight->pixel24) { - errors = tight_detect_smooth_image24(vs, w, h); - if (vs->tight->quality != (uint8_t)-1) { + if (tight->pixel24) { + errors = tight_detect_smooth_image24(vs, tight, w, h); + if (tight->quality != (uint8_t)-1) { return (errors < tight_conf[quality].jpeg_threshold24); } return (errors < tight_conf[compression].gradient_threshold24); } else { - errors = tight_detect_smooth_image32(vs, w, h); + errors = tight_detect_smooth_image32(vs, tight, w, h); } } else { - errors = tight_detect_smooth_image16(vs, w, h); + errors = tight_detect_smooth_image16(vs, tight, w, h); } if (quality != (uint8_t)-1) { return (errors < tight_conf[quality].jpeg_threshold); @@ -344,15 +345,15 @@ tight_detect_smooth_image(VncState *vs, int w, int h) #define DEFINE_FILL_PALETTE_FUNCTION(bpp) \ \ static int \ - tight_fill_palette##bpp(VncState *vs, int x, int y, \ - int max, size_t count, \ + tight_fill_palette##bpp(VncState *vs, VncTight *tight, \ + int x, int y, int max, size_t count, \ uint32_t *bg, uint32_t *fg, \ VncPalette *palette) { \ uint##bpp##_t *data; \ uint##bpp##_t c0, c1, ci; \ int i, n0, n1; \ \ - data = (uint##bpp##_t *)vs->tight->tight.buffer; \ + data = (uint##bpp##_t *)tight->tight.buffer; \ \ c0 = data[0]; \ i = 1; \ @@ -417,15 +418,15 @@ DEFINE_FILL_PALETTE_FUNCTION(8) DEFINE_FILL_PALETTE_FUNCTION(16) DEFINE_FILL_PALETTE_FUNCTION(32) -static int tight_fill_palette(VncState *vs, int x, int y, +static int tight_fill_palette(VncState *vs, VncTight *tight, int x, int y, size_t count, uint32_t *bg, uint32_t *fg, VncPalette *palette) { int max; - max = count / tight_conf[vs->tight->compression].idx_max_colors_divisor; + max = count / tight_conf[tight->compression].idx_max_colors_divisor; if (max < 2 && - count >= tight_conf[vs->tight->compression].mono_min_rect_size) { + count >= tight_conf[tight->compression].mono_min_rect_size) { max = 2; } if (max >= 256) { @@ -434,12 +435,15 @@ static int tight_fill_palette(VncState *vs, int x, int y, switch (vs->client_pf.bytes_per_pixel) { case 4: - return tight_fill_palette32(vs, x, y, max, count, bg, fg, palette); + return tight_fill_palette32(vs, tight, x, y, max, count, bg, fg, + palette); case 2: - return tight_fill_palette16(vs, x, y, max, count, bg, fg, palette); + return tight_fill_palette16(vs, tight, x, y, max, count, bg, fg, + palette); default: max = 2; - return tight_fill_palette8(vs, x, y, max, count, bg, fg, palette); + return tight_fill_palette8(vs, tight, x, y, max, count, bg, fg, + palette); } return 0; } @@ -547,7 +551,8 @@ DEFINE_MONO_ENCODE_FUNCTION(32) */ static void -tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) +tight_filter_gradient24(VncState *vs, VncTight *tight, uint8_t *buf, + int w, int h) { uint32_t *buf32; uint32_t pix32; @@ -558,7 +563,7 @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) int x, y, c; buf32 = (uint32_t *)buf; - memset(vs->tight->gradient.buffer, 0, w * 3 * sizeof(int)); + memset(tight->gradient.buffer, 0, w * 3 * sizeof(int)); if (1 /* FIXME */) { shift[0] = vs->client_pf.rshift; @@ -575,7 +580,7 @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) upper[c] = 0; here[c] = 0; } - prev = (int *)vs->tight->gradient.buffer; + prev = (int *)tight->gradient.buffer; for (x = 0; x < w; x++) { pix32 = *buf32++; for (c = 0; c < 3; c++) { @@ -605,8 +610,8 @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) #define DEFINE_GRADIENT_FILTER_FUNCTION(bpp) \ \ static void \ - tight_filter_gradient##bpp(VncState *vs, uint##bpp##_t *buf, \ - int w, int h) { \ + tight_filter_gradient##bpp(VncState *vs, VncTight *tight, \ + uint##bpp##_t *buf, int w, int h) { \ uint##bpp##_t pix, diff; \ bool endian; \ int *prev; \ @@ -615,7 +620,7 @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) int prediction; \ int x, y, c; \ \ - memset(vs->tight->gradient.buffer, 0, w * 3 * sizeof(int)); \ + memset(tight->gradient.buffer, 0, w * 3 * sizeof(int)); \ \ endian = 0; /* FIXME */ \ \ @@ -631,7 +636,7 @@ tight_filter_gradient24(VncState *vs, uint8_t *buf, int w, int h) upper[c] = 0; \ here[c] = 0; \ } \ - prev = (int *)vs->tight->gradient.buffer; \ + prev = (int *)tight->gradient.buffer; \ for (x = 0; x < w; x++) { \ pix = *buf; \ if (endian) { \ @@ -782,10 +787,10 @@ static void extend_solid_area(VncState *vs, int x, int y, int w, int h, *w_ptr += cx - (*x_ptr + *w_ptr); } -static int tight_init_stream(VncState *vs, int stream_id, +static int tight_init_stream(VncState *vs, VncTight *tight, int stream_id, int level, int strategy) { - z_streamp zstream = &vs->tight->stream[stream_id]; + z_streamp zstream = &tight->stream[stream_id]; if (zstream->opaque == NULL) { int err; @@ -803,15 +808,15 @@ static int tight_init_stream(VncState *vs, int stream_id, return -1; } - vs->tight->levels[stream_id] = level; + tight->levels[stream_id] = level; zstream->opaque = vs; } - if (vs->tight->levels[stream_id] != level) { + if (tight->levels[stream_id] != level) { if (deflateParams(zstream, level, strategy) != Z_OK) { return -1; } - vs->tight->levels[stream_id] = level; + tight->levels[stream_id] = level; } return 0; } @@ -836,29 +841,29 @@ static void tight_send_compact_size(VncState *vs, size_t len) } } -static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, - int level, int strategy) +static int tight_compress_data(VncState *vs, VncTight *tight, int stream_id, + size_t bytes, int level, int strategy) { - z_streamp zstream = &vs->tight->stream[stream_id]; + z_streamp zstream = &tight->stream[stream_id]; int previous_out; if (bytes < VNC_TIGHT_MIN_TO_COMPRESS) { - vnc_write(vs, vs->tight->tight.buffer, vs->tight->tight.offset); + vnc_write(vs, tight->tight.buffer, tight->tight.offset); return bytes; } - if (tight_init_stream(vs, stream_id, level, strategy)) { + if (tight_init_stream(vs, tight, stream_id, level, strategy)) { return -1; } /* reserve memory in output buffer */ - buffer_reserve(&vs->tight->zlib, bytes + 64); + buffer_reserve(&tight->zlib, bytes + 64); /* set pointers */ - zstream->next_in = vs->tight->tight.buffer; - zstream->avail_in = vs->tight->tight.offset; - zstream->next_out = vs->tight->zlib.buffer + vs->tight->zlib.offset; - zstream->avail_out = vs->tight->zlib.capacity - vs->tight->zlib.offset; + zstream->next_in = tight->tight.buffer; + zstream->avail_in = tight->tight.offset; + zstream->next_out = tight->zlib.buffer + tight->zlib.offset; + zstream->avail_out = tight->zlib.capacity - tight->zlib.offset; previous_out = zstream->avail_out; zstream->data_type = Z_BINARY; @@ -868,14 +873,14 @@ static int tight_compress_data(VncState *vs, int stream_id, size_t bytes, return -1; } - vs->tight->zlib.offset = vs->tight->zlib.capacity - zstream->avail_out; + tight->zlib.offset = tight->zlib.capacity - zstream->avail_out; /* ...how much data has actually been produced by deflate() */ bytes = previous_out - zstream->avail_out; tight_send_compact_size(vs, bytes); - vnc_write(vs, vs->tight->zlib.buffer, bytes); + vnc_write(vs, tight->zlib.buffer, bytes); - buffer_reset(&vs->tight->zlib); + buffer_reset(&tight->zlib); return bytes; } @@ -914,67 +919,69 @@ static void tight_pack24(VncState *vs, uint8_t *buf, size_t count, size_t *ret) } } -static int send_full_color_rect(VncState *vs, int x, int y, int w, int h) +static int send_full_color_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { + VncTight *tight = &worker->tight; + int level = tight_conf[tight->compression].raw_zlib_level; int stream = 0; ssize_t bytes; #ifdef CONFIG_PNG - if (tight_can_send_png_rect(vs, w, h)) { - return send_png_rect(vs, x, y, w, h, NULL); + if (tight_can_send_png_rect(vs, tight, w, h)) { + return send_png_rect(vs, worker, x, y, w, h, NULL); } #endif vnc_write_u8(vs, stream << 4); /* no flushing, no filter */ - if (vs->tight->pixel24) { - tight_pack24(vs, vs->tight->tight.buffer, w * h, - &vs->tight->tight.offset); + if (tight->pixel24) { + tight_pack24(vs, tight->tight.buffer, w * h, &tight->tight.offset); bytes = 3; } else { bytes = vs->client_pf.bytes_per_pixel; } - bytes = tight_compress_data(vs, stream, w * h * bytes, - tight_conf[vs->tight->compression].raw_zlib_level, + bytes = tight_compress_data(vs, tight, stream, w * h * bytes, level, Z_DEFAULT_STRATEGY); return (bytes >= 0); } -static int send_solid_rect(VncState *vs) +static int send_solid_rect(VncState *vs, VncWorker *worker) { + VncTight *tight = &worker->tight; size_t bytes; vnc_write_u8(vs, VNC_TIGHT_FILL << 4); /* no flushing, no filter */ - if (vs->tight->pixel24) { - tight_pack24(vs, vs->tight->tight.buffer, 1, &vs->tight->tight.offset); + if (tight->pixel24) { + tight_pack24(vs, tight->tight.buffer, 1, &tight->tight.offset); bytes = 3; } else { bytes = vs->client_pf.bytes_per_pixel; } - vnc_write(vs, vs->tight->tight.buffer, bytes); + vnc_write(vs, tight->tight.buffer, bytes); return 1; } -static int send_mono_rect(VncState *vs, int x, int y, +static int send_mono_rect(VncState *vs, VncWorker *worker, int x, int y, int w, int h, uint32_t bg, uint32_t fg) { ssize_t bytes; int stream = 1; - int level = tight_conf[vs->tight->compression].mono_zlib_level; + int level = tight_conf[worker->tight.compression].mono_zlib_level; #ifdef CONFIG_PNG - if (tight_can_send_png_rect(vs, w, h)) { + if (tight_can_send_png_rect(vs, &worker->tight, w, h)) { int ret; int bpp = vs->client_pf.bytes_per_pixel * 8; VncPalette *palette = palette_new(2, bpp); palette_put(palette, bg); palette_put(palette, fg); - ret = send_png_rect(vs, x, y, w, h, palette); + ret = send_png_rect(vs, worker, x, y, w, h, palette); palette_destroy(palette); return ret; } @@ -992,12 +999,12 @@ static int send_mono_rect(VncState *vs, int x, int y, uint32_t buf[2] = {bg, fg}; size_t ret = sizeof (buf); - if (vs->tight->pixel24) { + if (worker->tight.pixel24) { tight_pack24(vs, (unsigned char*)buf, 2, &ret); } vnc_write(vs, buf, ret); - tight_encode_mono_rect32(vs->tight->tight.buffer, w, h, bg, fg); + tight_encode_mono_rect32(worker->tight.tight.buffer, w, h, bg, fg); break; } case 2: @@ -1006,7 +1013,7 @@ static int send_mono_rect(VncState *vs, int x, int y, uint16_t fg16 = fg; vnc_write(vs, &bg16, 2); vnc_write(vs, &fg16, 2); - tight_encode_mono_rect16(vs->tight->tight.buffer, w, h, bg, fg); + tight_encode_mono_rect16(worker->tight.tight.buffer, w, h, bg, fg); break; } default: @@ -1015,18 +1022,20 @@ static int send_mono_rect(VncState *vs, int x, int y, uint8_t fg8 = fg; vnc_write_u8(vs, bg8); vnc_write_u8(vs, fg8); - tight_encode_mono_rect8(vs->tight->tight.buffer, w, h, bg, fg); + tight_encode_mono_rect8(worker->tight.tight.buffer, w, h, bg, fg); break; } } - vs->tight->tight.offset = bytes; + worker->tight.tight.offset = bytes; - bytes = tight_compress_data(vs, stream, bytes, level, Z_DEFAULT_STRATEGY); + bytes = tight_compress_data(vs, &worker->tight, stream, bytes, level, + Z_DEFAULT_STRATEGY); return (bytes >= 0); } struct palette_cb_priv { VncState *vs; + VncTight *tight; uint8_t *header; #ifdef CONFIG_PNG png_colorp png_palette; @@ -1046,53 +1055,58 @@ static void write_palette(int idx, uint32_t color, void *opaque) } } -static bool send_gradient_rect(VncState *vs, int x, int y, int w, int h) +static bool send_gradient_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { + VncTight *tight = &worker->tight; int stream = 3; - int level = tight_conf[vs->tight->compression].gradient_zlib_level; + int level = tight_conf[tight->compression].gradient_zlib_level; ssize_t bytes; if (vs->client_pf.bytes_per_pixel == 1) { - return send_full_color_rect(vs, x, y, w, h); + return send_full_color_rect(vs, worker, x, y, w, h); } vnc_write_u8(vs, (stream | VNC_TIGHT_EXPLICIT_FILTER) << 4); vnc_write_u8(vs, VNC_TIGHT_FILTER_GRADIENT); - buffer_reserve(&vs->tight->gradient, w * 3 * sizeof(int)); + buffer_reserve(&tight->gradient, w * 3 * sizeof(int)); - if (vs->tight->pixel24) { - tight_filter_gradient24(vs, vs->tight->tight.buffer, w, h); + if (tight->pixel24) { + tight_filter_gradient24(vs, tight, tight->tight.buffer, w, h); bytes = 3; } else if (vs->client_pf.bytes_per_pixel == 4) { - tight_filter_gradient32(vs, (uint32_t *)vs->tight->tight.buffer, w, h); + tight_filter_gradient32(vs, tight, (uint32_t *)tight->tight.buffer, + w, h); bytes = 4; } else { - tight_filter_gradient16(vs, (uint16_t *)vs->tight->tight.buffer, w, h); + tight_filter_gradient16(vs, tight, (uint16_t *)tight->tight.buffer, + w, h); bytes = 2; } - buffer_reset(&vs->tight->gradient); + buffer_reset(&tight->gradient); bytes = w * h * bytes; - vs->tight->tight.offset = bytes; + tight->tight.offset = bytes; - bytes = tight_compress_data(vs, stream, bytes, + bytes = tight_compress_data(vs, tight, stream, bytes, level, Z_FILTERED); return (bytes >= 0); } -static int send_palette_rect(VncState *vs, int x, int y, +static int send_palette_rect(VncState *vs, VncWorker *worker, int x, int y, int w, int h, VncPalette *palette) { + VncTight *tight = &worker->tight; int stream = 2; - int level = tight_conf[vs->tight->compression].idx_zlib_level; + int level = tight_conf[tight->compression].idx_zlib_level; int colors; ssize_t bytes; #ifdef CONFIG_PNG - if (tight_can_send_png_rect(vs, w, h)) { - return send_png_rect(vs, x, y, w, h, palette); + if (tight_can_send_png_rect(vs, tight, w, h)) { + return send_png_rect(vs, worker, x, y, w, h, palette); } #endif @@ -1107,38 +1121,38 @@ static int send_palette_rect(VncState *vs, int x, int y, { size_t old_offset, offset, palette_sz = palette_size(palette); g_autofree uint32_t *header = g_new(uint32_t, palette_sz); - struct palette_cb_priv priv = { vs, (uint8_t *)header }; + struct palette_cb_priv priv = { vs, tight, (uint8_t *)header }; old_offset = vs->output.offset; palette_iter(palette, write_palette, &priv); vnc_write(vs, header, palette_sz * sizeof(uint32_t)); - if (vs->tight->pixel24) { + if (tight->pixel24) { tight_pack24(vs, vs->output.buffer + old_offset, colors, &offset); vs->output.offset = old_offset + offset; } - tight_encode_indexed_rect32(vs->tight->tight.buffer, w * h, palette); + tight_encode_indexed_rect32(tight->tight.buffer, w * h, palette); break; } case 2: { size_t palette_sz = palette_size(palette); g_autofree uint16_t *header = g_new(uint16_t, palette_sz); - struct palette_cb_priv priv = { vs, (uint8_t *)header }; + struct palette_cb_priv priv = { vs, tight, (uint8_t *)header }; palette_iter(palette, write_palette, &priv); vnc_write(vs, header, palette_sz * sizeof(uint16_t)); - tight_encode_indexed_rect16(vs->tight->tight.buffer, w * h, palette); + tight_encode_indexed_rect16(tight->tight.buffer, w * h, palette); break; } default: return -1; /* No palette for 8bits colors */ } bytes = w * h; - vs->tight->tight.offset = bytes; + tight->tight.offset = bytes; - bytes = tight_compress_data(vs, stream, bytes, + bytes = tight_compress_data(vs, tight, stream, bytes, level, Z_DEFAULT_STRATEGY); return (bytes >= 0); } @@ -1154,8 +1168,8 @@ static int send_palette_rect(VncState *vs, int x, int y, /* This is called once per encoding */ static void jpeg_init_destination(j_compress_ptr cinfo) { - VncState *vs = cinfo->client_data; - Buffer *buffer = &vs->tight->jpeg; + VncTight *tight = cinfo->client_data; + Buffer *buffer = &tight->jpeg; cinfo->dest->next_output_byte = (JOCTET *)buffer->buffer + buffer->offset; cinfo->dest->free_in_buffer = (size_t)(buffer->capacity - buffer->offset); @@ -1164,8 +1178,8 @@ static void jpeg_init_destination(j_compress_ptr cinfo) /* This is called when we ran out of buffer (shouldn't happen!) */ static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo) { - VncState *vs = cinfo->client_data; - Buffer *buffer = &vs->tight->jpeg; + VncTight *tight = cinfo->client_data; + Buffer *buffer = &tight->jpeg; buffer->offset = buffer->capacity; buffer_reserve(buffer, 2048); @@ -1176,13 +1190,14 @@ static boolean jpeg_empty_output_buffer(j_compress_ptr cinfo) /* This is called when we are done processing data */ static void jpeg_term_destination(j_compress_ptr cinfo) { - VncState *vs = cinfo->client_data; - Buffer *buffer = &vs->tight->jpeg; + VncTight *tight = cinfo->client_data; + Buffer *buffer = &tight->jpeg; buffer->offset = buffer->capacity - cinfo->dest->free_in_buffer; } -static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality) +static int send_jpeg_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, int quality) { struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; @@ -1193,15 +1208,15 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality) int dy; if (surface_bytes_per_pixel(vs->vd->ds) == 1) { - return send_full_color_rect(vs, x, y, w, h); + return send_full_color_rect(vs, worker, x, y, w, h); } - buffer_reserve(&vs->tight->jpeg, 2048); + buffer_reserve(&worker->tight.jpeg, 2048); cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); - cinfo.client_data = vs; + cinfo.client_data = &worker->tight; cinfo.image_width = w; cinfo.image_height = h; cinfo.input_components = 3; @@ -1231,9 +1246,9 @@ static int send_jpeg_rect(VncState *vs, int x, int y, int w, int h, int quality) vnc_write_u8(vs, VNC_TIGHT_JPEG << 4); - tight_send_compact_size(vs, vs->tight->jpeg.offset); - vnc_write(vs, vs->tight->jpeg.buffer, vs->tight->jpeg.offset); - buffer_reset(&vs->tight->jpeg); + tight_send_compact_size(vs, worker->tight.jpeg.offset); + vnc_write(vs, worker->tight.jpeg.buffer, worker->tight.jpeg.offset); + buffer_reset(&worker->tight.jpeg); return 1; } @@ -1249,7 +1264,7 @@ static void write_png_palette(int idx, uint32_t pix, void *opaque) VncState *vs = priv->vs; png_colorp color = &priv->png_palette[idx]; - if (vs->tight->pixel24) + if (priv->tight->pixel24) { color->red = (pix >> vs->client_pf.rshift) & vs->client_pf.rmax; color->green = (pix >> vs->client_pf.gshift) & vs->client_pf.gmax; @@ -1274,12 +1289,12 @@ static void write_png_palette(int idx, uint32_t pix, void *opaque) static void png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) { - VncState *vs = png_get_io_ptr(png_ptr); + VncWorker *worker = png_get_io_ptr(png_ptr); - buffer_reserve(&vs->tight->png, vs->tight->png.offset + length); - memcpy(vs->tight->png.buffer + vs->tight->png.offset, data, length); + buffer_reserve(&worker->tight.png, worker->tight.png.offset + length); + memcpy(worker->tight.png.buffer + worker->tight.png.offset, data, length); - vs->tight->png.offset += length; + worker->tight.png.offset += length; } static void png_flush_data(png_structp png_ptr) @@ -1296,16 +1311,16 @@ static void vnc_png_free(png_structp png_ptr, png_voidp ptr) g_free(ptr); } -static int send_png_rect(VncState *vs, int x, int y, int w, int h, - VncPalette *palette) +static int send_png_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, VncPalette *palette) { png_byte color_type; png_structp png_ptr; png_infop info_ptr; png_colorp png_palette = NULL; pixman_image_t *linebuf; - int level = tight_png_conf[vs->tight->compression].png_zlib_level; - int filters = tight_png_conf[vs->tight->compression].png_filters; + int level = tight_png_conf[worker->tight.compression].png_zlib_level; + int filters = tight_png_conf[worker->tight.compression].png_filters; uint8_t *buf; int dy; @@ -1322,7 +1337,7 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h, return -1; } - png_set_write_fn(png_ptr, (void *) vs, png_write_data, png_flush_data); + png_set_write_fn(png_ptr, worker, png_write_data, png_flush_data); png_set_compression_level(png_ptr, level); png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, filters); @@ -1343,29 +1358,30 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h, palette_size(palette)); priv.vs = vs; + priv.tight = &worker->tight; priv.png_palette = png_palette; palette_iter(palette, write_png_palette, &priv); png_set_PLTE(png_ptr, info_ptr, png_palette, palette_size(palette)); if (vs->client_pf.bytes_per_pixel == 4) { - tight_encode_indexed_rect32(vs->tight->tight.buffer, w * h, + tight_encode_indexed_rect32(worker->tight.tight.buffer, w * h, palette); } else { - tight_encode_indexed_rect16(vs->tight->tight.buffer, w * h, + tight_encode_indexed_rect16(worker->tight.tight.buffer, w * h, palette); } } png_write_info(png_ptr, info_ptr); - buffer_reserve(&vs->tight->png, 2048); + buffer_reserve(&worker->tight.png, 2048); linebuf = qemu_pixman_linebuf_create(PIXMAN_BE_r8g8b8, w); buf = (uint8_t *)pixman_image_get_data(linebuf); for (dy = 0; dy < h; dy++) { if (color_type == PNG_COLOR_TYPE_PALETTE) { - memcpy(buf, vs->tight->tight.buffer + (dy * w), w); + memcpy(buf, worker->tight.tight.buffer + (dy * w), w); } else { qemu_pixman_linebuf_fill(linebuf, vs->vd->server, w, x, y + dy); } @@ -1383,46 +1399,47 @@ static int send_png_rect(VncState *vs, int x, int y, int w, int h, vnc_write_u8(vs, VNC_TIGHT_PNG << 4); - tight_send_compact_size(vs, vs->tight->png.offset); - vnc_write(vs, vs->tight->png.buffer, vs->tight->png.offset); - buffer_reset(&vs->tight->png); + tight_send_compact_size(vs, worker->tight.png.offset); + vnc_write(vs, worker->tight.png.buffer, worker->tight.png.offset); + buffer_reset(&worker->tight.png); return 1; } #endif /* CONFIG_PNG */ -static void vnc_tight_start(VncState *vs) +static void vnc_tight_start(VncState *vs, VncTight *tight) { - buffer_reset(&vs->tight->tight); + buffer_reset(&tight->tight); // make the output buffer be the zlib buffer, so we can compress it later - vs->tight->tmp = vs->output; - vs->output = vs->tight->tight; + tight->tmp = vs->output; + vs->output = tight->tight; } -static void vnc_tight_stop(VncState *vs) +static void vnc_tight_stop(VncState *vs, VncTight *tight) { // switch back to normal output/zlib buffers - vs->tight->tight = vs->output; - vs->output = vs->tight->tmp; + tight->tight = vs->output; + vs->output = tight->tmp; } -static int send_sub_rect_nojpeg(VncState *vs, int x, int y, int w, int h, +static int send_sub_rect_nojpeg(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, int bg, int fg, int colors, VncPalette *palette) { int ret; if (colors == 0) { - if (tight_detect_smooth_image(vs, w, h)) { - ret = send_gradient_rect(vs, x, y, w, h); + if (tight_detect_smooth_image(vs, &worker->tight, w, h)) { + ret = send_gradient_rect(vs, worker, x, y, w, h); } else { - ret = send_full_color_rect(vs, x, y, w, h); + ret = send_full_color_rect(vs, worker, x, y, w, h); } } else if (colors == 1) { - ret = send_solid_rect(vs); + ret = send_solid_rect(vs, worker); } else if (colors == 2) { - ret = send_mono_rect(vs, x, y, w, h, bg, fg); + ret = send_mono_rect(vs, worker, x, y, w, h, bg, fg); } else if (colors <= 256) { - ret = send_palette_rect(vs, x, y, w, h, palette); + ret = send_palette_rect(vs, worker, x, y, w, h, palette); } else { ret = 0; } @@ -1430,34 +1447,35 @@ static int send_sub_rect_nojpeg(VncState *vs, int x, int y, int w, int h, } #ifdef CONFIG_VNC_JPEG -static int send_sub_rect_jpeg(VncState *vs, int x, int y, int w, int h, +static int send_sub_rect_jpeg(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, int bg, int fg, int colors, VncPalette *palette, bool force) { int ret; if (colors == 0) { - if (force || (tight_jpeg_conf[vs->tight->quality].jpeg_full && - tight_detect_smooth_image(vs, w, h))) { - int quality = tight_conf[vs->tight->quality].jpeg_quality; + if (force || (tight_jpeg_conf[worker->tight.quality].jpeg_full && + tight_detect_smooth_image(vs, &worker->tight, w, h))) { + int quality = tight_conf[worker->tight.quality].jpeg_quality; - ret = send_jpeg_rect(vs, x, y, w, h, quality); + ret = send_jpeg_rect(vs, worker, x, y, w, h, quality); } else { - ret = send_full_color_rect(vs, x, y, w, h); + ret = send_full_color_rect(vs, worker, x, y, w, h); } } else if (colors == 1) { - ret = send_solid_rect(vs); + ret = send_solid_rect(vs, worker); } else if (colors == 2) { - ret = send_mono_rect(vs, x, y, w, h, bg, fg); + ret = send_mono_rect(vs, worker, x, y, w, h, bg, fg); } else if (colors <= 256) { if (force || (colors > 96 && - tight_jpeg_conf[vs->tight->quality].jpeg_idx && - tight_detect_smooth_image(vs, w, h))) { - int quality = tight_conf[vs->tight->quality].jpeg_quality; + tight_jpeg_conf[worker->tight.quality].jpeg_idx && + tight_detect_smooth_image(vs, &worker->tight, w, h))) { + int quality = tight_conf[worker->tight.quality].jpeg_quality; - ret = send_jpeg_rect(vs, x, y, w, h, quality); + ret = send_jpeg_rect(vs, worker, x, y, w, h, quality); } else { - ret = send_palette_rect(vs, x, y, w, h, palette); + ret = send_palette_rect(vs, worker, x, y, w, h, palette); } } else { ret = 0; @@ -1475,8 +1493,10 @@ static void vnc_tight_cleanup(Notifier *n, void *value) color_count_palette = NULL; } -static int send_sub_rect(VncState *vs, int x, int y, int w, int h) +static int send_sub_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { + VncTight *tight = &worker->tight; uint32_t bg = 0, fg = 0; int colors; int ret = 0; @@ -1491,57 +1511,59 @@ static int send_sub_rect(VncState *vs, int x, int y, int w, int h) qemu_thread_atexit_add(&vnc_tight_cleanup_notifier); } - vnc_framebuffer_update(vs, x, y, w, h, vs->tight->type); + vnc_framebuffer_update(vs, x, y, w, h, tight->type); - vnc_tight_start(vs); + vnc_tight_start(vs, tight); vnc_raw_send_framebuffer_update(vs, x, y, w, h); - vnc_tight_stop(vs); + vnc_tight_stop(vs, tight); #ifdef CONFIG_VNC_JPEG - if (!vs->vd->non_adaptive && vs->tight->quality != (uint8_t)-1) { + if (!vs->vd->non_adaptive && tight->quality != (uint8_t)-1) { double freq = vnc_update_freq(vs, x, y, w, h); - if (freq < tight_jpeg_conf[vs->tight->quality].jpeg_freq_min) { + if (freq < tight_jpeg_conf[tight->quality].jpeg_freq_min) { allow_jpeg = false; } - if (freq >= tight_jpeg_conf[vs->tight->quality].jpeg_freq_threshold) { + if (freq >= tight_jpeg_conf[tight->quality].jpeg_freq_threshold) { force_jpeg = true; - vnc_sent_lossy_rect(vs, x, y, w, h); + vnc_sent_lossy_rect(worker, x, y, w, h); } } #endif - colors = tight_fill_palette(vs, x, y, w * h, &bg, &fg, color_count_palette); + colors = tight_fill_palette(vs, tight, x, y, w * h, &bg, &fg, + color_count_palette); #ifdef CONFIG_VNC_JPEG - if (allow_jpeg && vs->tight->quality != (uint8_t)-1) { - ret = send_sub_rect_jpeg(vs, x, y, w, h, bg, fg, colors, + if (allow_jpeg && tight->quality != (uint8_t)-1) { + ret = send_sub_rect_jpeg(vs, worker, x, y, w, h, bg, fg, colors, color_count_palette, force_jpeg); } else { - ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, - color_count_palette); + ret = send_sub_rect_nojpeg(vs, worker, x, y, w, h, bg, fg, + colors, color_count_palette); } #else - ret = send_sub_rect_nojpeg(vs, x, y, w, h, bg, fg, colors, + ret = send_sub_rect_nojpeg(vs, worker, x, y, w, h, bg, fg, colors, color_count_palette); #endif return ret; } -static int send_sub_rect_solid(VncState *vs, int x, int y, int w, int h) +static int send_sub_rect_solid(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { - vnc_framebuffer_update(vs, x, y, w, h, vs->tight->type); + vnc_framebuffer_update(vs, x, y, w, h, worker->tight.type); - vnc_tight_start(vs); + vnc_tight_start(vs, &worker->tight); vnc_raw_send_framebuffer_update(vs, x, y, w, h); - vnc_tight_stop(vs); + vnc_tight_stop(vs, &worker->tight); - return send_solid_rect(vs); + return send_solid_rect(vs, worker); } -static int send_rect_simple(VncState *vs, int x, int y, int w, int h, - bool split) +static int send_rect_simple(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, bool split) { int max_size, max_width; int max_sub_width, max_sub_height; @@ -1549,8 +1571,8 @@ static int send_rect_simple(VncState *vs, int x, int y, int w, int h, int rw, rh; int n = 0; - max_size = tight_conf[vs->tight->compression].max_rect_size; - max_width = tight_conf[vs->tight->compression].max_rect_width; + max_size = tight_conf[worker->tight.compression].max_rect_size; + max_width = tight_conf[worker->tight.compression].max_rect_width; if (split && (w > max_width || w * h > max_size)) { max_sub_width = (w > max_width) ? max_width : w; @@ -1560,18 +1582,18 @@ static int send_rect_simple(VncState *vs, int x, int y, int w, int h, for (dx = 0; dx < w; dx += max_width) { rw = MIN(max_sub_width, w - dx); rh = MIN(max_sub_height, h - dy); - n += send_sub_rect(vs, x+dx, y+dy, rw, rh); + n += send_sub_rect(vs, worker, x + dx, y + dy, rw, rh); } } } else { - n += send_sub_rect(vs, x, y, w, h); + n += send_sub_rect(vs, worker, x, y, w, h); } return n; } -static int find_large_solid_color_rect(VncState *vs, int x, int y, - int w, int h, int max_rows) +static int find_large_solid_color_rect(VncState *vs, VncWorker *worker, + int x, int y, int w, int h, int max_rows) { int dx, dy, dw, dh; int n = 0; @@ -1583,7 +1605,7 @@ static int find_large_solid_color_rect(VncState *vs, int x, int y, /* If a rectangle becomes too large, send its upper part now. */ if (dy - y >= max_rows) { - n += send_rect_simple(vs, x, y, w, max_rows, true); + n += send_rect_simple(vs, worker, x, y, w, max_rows, true); y += max_rows; h -= max_rows; } @@ -1622,26 +1644,28 @@ static int find_large_solid_color_rect(VncState *vs, int x, int y, /* Send rectangles at top and left to solid-color area. */ if (y_best != y) { - n += send_rect_simple(vs, x, y, w, y_best-y, true); + n += send_rect_simple(vs, worker, x, y, w, y_best - y, true); } if (x_best != x) { - n += tight_send_framebuffer_update(vs, x, y_best, + n += tight_send_framebuffer_update(vs, worker, x, y_best, x_best-x, h_best); } /* Send solid-color rectangle. */ - n += send_sub_rect_solid(vs, x_best, y_best, w_best, h_best); + n += send_sub_rect_solid(vs, worker, + x_best, y_best, w_best, h_best); /* Send remaining rectangles (at right and bottom). */ if (x_best + w_best != x + w) { - n += tight_send_framebuffer_update(vs, x_best+w_best, + n += tight_send_framebuffer_update(vs, worker, x_best + w_best, y_best, w-(x_best-x)-w_best, h_best); } if (y_best + h_best != y + h) { - n += tight_send_framebuffer_update(vs, x, y_best+h_best, + n += tight_send_framebuffer_update(vs, worker, + x, y_best + h_best, w, h-(y_best-y)-h_best); } @@ -1649,73 +1673,73 @@ static int find_large_solid_color_rect(VncState *vs, int x, int y, return n; } } - return n + send_rect_simple(vs, x, y, w, h, true); + return n + send_rect_simple(vs, worker, x, y, w, h, true); } -static int tight_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h) +static int tight_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { int max_rows; if (vs->client_pf.bytes_per_pixel == 4 && vs->client_pf.rmax == 0xFF && vs->client_pf.bmax == 0xFF && vs->client_pf.gmax == 0xFF) { - vs->tight->pixel24 = true; + worker->tight.pixel24 = true; } else { - vs->tight->pixel24 = false; + worker->tight.pixel24 = false; } #ifdef CONFIG_VNC_JPEG - if (vs->tight->quality != (uint8_t)-1) { + if (worker->tight.quality != (uint8_t)-1) { double freq = vnc_update_freq(vs, x, y, w, h); - if (freq > tight_jpeg_conf[vs->tight->quality].jpeg_freq_threshold) { - return send_rect_simple(vs, x, y, w, h, false); + if (freq > tight_jpeg_conf[worker->tight.quality].jpeg_freq_threshold) { + return send_rect_simple(vs, worker, x, y, w, h, false); } } #endif if (w * h < VNC_TIGHT_MIN_SPLIT_RECT_SIZE) { - return send_rect_simple(vs, x, y, w, h, true); + return send_rect_simple(vs, worker, x, y, w, h, true); } /* Calculate maximum number of rows in one non-solid rectangle. */ - max_rows = tight_conf[vs->tight->compression].max_rect_size; - max_rows /= MIN(tight_conf[vs->tight->compression].max_rect_width, w); + max_rows = tight_conf[worker->tight.compression].max_rect_size; + max_rows /= MIN(tight_conf[worker->tight.compression].max_rect_width, w); - return find_large_solid_color_rect(vs, x, y, w, h, max_rows); + return find_large_solid_color_rect(vs, worker, x, y, w, h, max_rows); } -int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h) +int vnc_tight_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { - vs->tight->type = VNC_ENCODING_TIGHT; - return tight_send_framebuffer_update(vs, x, y, w, h); + worker->tight.type = VNC_ENCODING_TIGHT; + return tight_send_framebuffer_update(vs, worker, x, y, w, h); } -int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h) +int vnc_tight_png_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { - vs->tight->type = VNC_ENCODING_TIGHT_PNG; - return tight_send_framebuffer_update(vs, x, y, w, h); + worker->tight.type = VNC_ENCODING_TIGHT_PNG; + return tight_send_framebuffer_update(vs, worker, x, y, w, h); } -void vnc_tight_clear(VncState *vs) +void vnc_tight_clear(VncWorker *worker) { int i; - for (i = 0; i < ARRAY_SIZE(vs->tight->stream); i++) { - if (vs->tight->stream[i].opaque) { - deflateEnd(&vs->tight->stream[i]); + for (i = 0; i < ARRAY_SIZE(worker->tight.stream); i++) { + if (worker->tight.stream[i].opaque) { + deflateEnd(&worker->tight.stream[i]); } } - buffer_free(&vs->tight->tight); - buffer_free(&vs->tight->zlib); - buffer_free(&vs->tight->gradient); + buffer_free(&worker->tight.tight); + buffer_free(&worker->tight.zlib); + buffer_free(&worker->tight.gradient); #ifdef CONFIG_VNC_JPEG - buffer_free(&vs->tight->jpeg); + buffer_free(&worker->tight.jpeg); #endif #ifdef CONFIG_PNG - buffer_free(&vs->tight->png); + buffer_free(&worker->tight.png); #endif } diff --git a/ui/vnc-enc-zlib.c b/ui/vnc-enc-zlib.c index 52e9193eab..a6d287118a 100644 --- a/ui/vnc-enc-zlib.c +++ b/ui/vnc-enc-zlib.c @@ -46,23 +46,23 @@ void vnc_zlib_zfree(void *x, void *addr) g_free(addr); } -static void vnc_zlib_start(VncState *vs) +static void vnc_zlib_start(VncState *vs, VncWorker *worker) { - buffer_reset(&vs->zlib->zlib); + buffer_reset(&worker->zlib.zlib); // make the output buffer be the zlib buffer, so we can compress it later - vs->zlib->tmp = vs->output; - vs->output = vs->zlib->zlib; + worker->zlib.tmp = vs->output; + vs->output = worker->zlib.zlib; } -static int vnc_zlib_stop(VncState *vs) +static int vnc_zlib_stop(VncState *vs, VncWorker *worker) { - z_streamp zstream = &vs->zlib->stream; + z_streamp zstream = &worker->zlib.stream; int previous_out; // switch back to normal output/zlib buffers - vs->zlib->zlib = vs->output; - vs->output = vs->zlib->tmp; + worker->zlib.zlib = vs->output; + vs->output = worker->zlib.tmp; // compress the zlib buffer @@ -76,7 +76,7 @@ static int vnc_zlib_stop(VncState *vs) zstream->zalloc = vnc_zlib_zalloc; zstream->zfree = vnc_zlib_zfree; - err = deflateInit2(zstream, vs->tight->compression, Z_DEFLATED, + err = deflateInit2(zstream, worker->tight.compression, Z_DEFLATED, MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY); @@ -85,24 +85,24 @@ static int vnc_zlib_stop(VncState *vs) return -1; } - vs->zlib->level = vs->tight->compression; + worker->zlib.level = worker->tight.compression; zstream->opaque = vs; } - if (vs->tight->compression != vs->zlib->level) { - if (deflateParams(zstream, vs->tight->compression, + if (worker->tight.compression != worker->zlib.level) { + if (deflateParams(zstream, worker->tight.compression, Z_DEFAULT_STRATEGY) != Z_OK) { return -1; } - vs->zlib->level = vs->tight->compression; + worker->zlib.level = worker->tight.compression; } // reserve memory in output buffer - buffer_reserve(&vs->output, vs->zlib->zlib.offset + 64); + buffer_reserve(&vs->output, worker->zlib.zlib.offset + 64); // set pointers - zstream->next_in = vs->zlib->zlib.buffer; - zstream->avail_in = vs->zlib->zlib.offset; + zstream->next_in = worker->zlib.zlib.buffer; + zstream->avail_in = worker->zlib.zlib.offset; zstream->next_out = vs->output.buffer + vs->output.offset; zstream->avail_out = vs->output.capacity - vs->output.offset; previous_out = zstream->avail_out; @@ -118,7 +118,8 @@ static int vnc_zlib_stop(VncState *vs) return previous_out - zstream->avail_out; } -int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +int vnc_zlib_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { int old_offset, new_offset, bytes_written; @@ -129,9 +130,9 @@ int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) vnc_write_s32(vs, 0); // compress the stream - vnc_zlib_start(vs); + vnc_zlib_start(vs, worker); vnc_raw_send_framebuffer_update(vs, x, y, w, h); - bytes_written = vnc_zlib_stop(vs); + bytes_written = vnc_zlib_stop(vs, worker); if (bytes_written == -1) return 0; @@ -145,10 +146,10 @@ int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) return 1; } -void vnc_zlib_clear(VncState *vs) +void vnc_zlib_clear(VncWorker *worker) { - if (vs->zlib->stream.opaque) { - deflateEnd(&vs->zlib->stream); + if (worker->zlib.stream.opaque) { + deflateEnd(&worker->zlib.stream); } - buffer_free(&vs->zlib->zlib); + buffer_free(&worker->zlib.zlib); } diff --git a/ui/vnc-enc-zrle.c b/ui/vnc-enc-zrle.c index 97ec6c7119..7679014c9e 100644 --- a/ui/vnc-enc-zrle.c +++ b/ui/vnc-enc-zrle.c @@ -35,45 +35,45 @@ static const int bits_per_packed_pixel[] = { }; -static void vnc_zrle_start(VncState *vs) +static void vnc_zrle_start(VncState *vs, VncZrle *zrle) { - buffer_reset(&vs->zrle->zrle); + buffer_reset(&zrle->zrle); /* make the output buffer be the zlib buffer, so we can compress it later */ - vs->zrle->tmp = vs->output; - vs->output = vs->zrle->zrle; + zrle->tmp = vs->output; + vs->output = zrle->zrle; } -static void vnc_zrle_stop(VncState *vs) +static void vnc_zrle_stop(VncState *vs, VncZrle *zrle) { /* switch back to normal output/zlib buffers */ - vs->zrle->zrle = vs->output; - vs->output = vs->zrle->tmp; + zrle->zrle = vs->output; + vs->output = zrle->tmp; } -static void *zrle_convert_fb(VncState *vs, int x, int y, int w, int h, - int bpp) +static void *zrle_convert_fb(VncState *vs, VncZrle *zrle, + int x, int y, int w, int h, int bpp) { Buffer tmp; - buffer_reset(&vs->zrle->fb); - buffer_reserve(&vs->zrle->fb, w * h * bpp + bpp); + buffer_reset(&zrle->fb); + buffer_reserve(&zrle->fb, w * h * bpp + bpp); tmp = vs->output; - vs->output = vs->zrle->fb; + vs->output = zrle->fb; vnc_raw_send_framebuffer_update(vs, x, y, w, h); - vs->zrle->fb = vs->output; + zrle->fb = vs->output; vs->output = tmp; - return vs->zrle->fb.buffer; + return zrle->fb.buffer; } -static int zrle_compress_data(VncState *vs, int level) +static int zrle_compress_data(VncState *vs, VncZrle *zrle, int level) { - z_streamp zstream = &vs->zrle->stream; + z_streamp zstream = &zrle->stream; - buffer_reset(&vs->zrle->zlib); + buffer_reset(&zrle->zlib); if (zstream->opaque != vs) { int err; @@ -93,13 +93,13 @@ static int zrle_compress_data(VncState *vs, int level) } /* reserve memory in output buffer */ - buffer_reserve(&vs->zrle->zlib, vs->zrle->zrle.offset + 64); + buffer_reserve(&zrle->zlib, zrle->zrle.offset + 64); /* set pointers */ - zstream->next_in = vs->zrle->zrle.buffer; - zstream->avail_in = vs->zrle->zrle.offset; - zstream->next_out = vs->zrle->zlib.buffer; - zstream->avail_out = vs->zrle->zlib.capacity; + zstream->next_in = zrle->zrle.buffer; + zstream->avail_in = zrle->zrle.offset; + zstream->next_out = zrle->zlib.buffer; + zstream->avail_out = zrle->zlib.capacity; zstream->data_type = Z_BINARY; /* start encoding */ @@ -108,8 +108,8 @@ static int zrle_compress_data(VncState *vs, int level) return -1; } - vs->zrle->zlib.offset = vs->zrle->zlib.capacity - zstream->avail_out; - return vs->zrle->zlib.offset; + zrle->zlib.offset = zrle->zlib.capacity - zstream->avail_out; + return zrle->zlib.offset; } /* Try to work out whether to use RLE and/or a palette. We do this by @@ -252,21 +252,21 @@ static void zrle_write_u8(VncState *vs, uint8_t value) #undef ZRLE_COMPACT_PIXEL #undef ZRLE_BPP -static int zrle_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h) +static int zrle_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { bool be = vs->client_endian == G_BIG_ENDIAN; size_t bytes; int zywrle_level; - if (vs->zrle->type == VNC_ENCODING_ZYWRLE) { - if (!vs->vd->lossy || vs->tight->quality == (uint8_t)-1 - || vs->tight->quality == 9) { + if (worker->zrle.type == VNC_ENCODING_ZYWRLE) { + if (!vs->vd->lossy || worker->tight.quality == (uint8_t)-1 + || worker->tight.quality == 9) { zywrle_level = 0; - vs->zrle->type = VNC_ENCODING_ZRLE; - } else if (vs->tight->quality < 3) { + worker->zrle.type = VNC_ENCODING_ZRLE; + } else if (worker->tight.quality < 3) { zywrle_level = 3; - } else if (vs->tight->quality < 6) { + } else if (worker->tight.quality < 6) { zywrle_level = 2; } else { zywrle_level = 1; @@ -275,25 +275,25 @@ static int zrle_send_framebuffer_update(VncState *vs, int x, int y, zywrle_level = 0; } - vnc_zrle_start(vs); + vnc_zrle_start(vs, &worker->zrle); switch (vs->client_pf.bytes_per_pixel) { case 1: - zrle_encode_8ne(vs, x, y, w, h, zywrle_level); + zrle_encode_8ne(vs, &worker->zrle, x, y, w, h, zywrle_level); break; case 2: if (vs->client_pf.gmax > 0x1F) { if (be) { - zrle_encode_16be(vs, x, y, w, h, zywrle_level); + zrle_encode_16be(vs, &worker->zrle, x, y, w, h, zywrle_level); } else { - zrle_encode_16le(vs, x, y, w, h, zywrle_level); + zrle_encode_16le(vs, &worker->zrle, x, y, w, h, zywrle_level); } } else { if (be) { - zrle_encode_15be(vs, x, y, w, h, zywrle_level); + zrle_encode_15be(vs, &worker->zrle, x, y, w, h, zywrle_level); } else { - zrle_encode_15le(vs, x, y, w, h, zywrle_level); + zrle_encode_15le(vs, &worker->zrle, x, y, w, h, zywrle_level); } } break; @@ -314,53 +314,55 @@ static int zrle_send_framebuffer_update(VncState *vs, int x, int y, if ((fits_in_ls3bytes && !be) || (fits_in_ms3bytes && be)) { if (be) { - zrle_encode_24abe(vs, x, y, w, h, zywrle_level); + zrle_encode_24abe(vs, &worker->zrle, x, y, w, h, zywrle_level); } else { - zrle_encode_24ale(vs, x, y, w, h, zywrle_level); + zrle_encode_24ale(vs, &worker->zrle, x, y, w, h, zywrle_level); } } else if ((fits_in_ls3bytes && be) || (fits_in_ms3bytes && !be)) { if (be) { - zrle_encode_24bbe(vs, x, y, w, h, zywrle_level); + zrle_encode_24bbe(vs, &worker->zrle, x, y, w, h, zywrle_level); } else { - zrle_encode_24ble(vs, x, y, w, h, zywrle_level); + zrle_encode_24ble(vs, &worker->zrle, x, y, w, h, zywrle_level); } } else { if (be) { - zrle_encode_32be(vs, x, y, w, h, zywrle_level); + zrle_encode_32be(vs, &worker->zrle, x, y, w, h, zywrle_level); } else { - zrle_encode_32le(vs, x, y, w, h, zywrle_level); + zrle_encode_32le(vs, &worker->zrle, x, y, w, h, zywrle_level); } } } break; } - vnc_zrle_stop(vs); - bytes = zrle_compress_data(vs, Z_DEFAULT_COMPRESSION); - vnc_framebuffer_update(vs, x, y, w, h, vs->zrle->type); + vnc_zrle_stop(vs, &worker->zrle); + bytes = zrle_compress_data(vs, &worker->zrle, Z_DEFAULT_COMPRESSION); + vnc_framebuffer_update(vs, x, y, w, h, worker->zrle.type); vnc_write_u32(vs, bytes); - vnc_write(vs, vs->zrle->zlib.buffer, vs->zrle->zlib.offset); + vnc_write(vs, worker->zrle.zlib.buffer, worker->zrle.zlib.offset); return 1; } -int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +int vnc_zrle_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { - vs->zrle->type = VNC_ENCODING_ZRLE; - return zrle_send_framebuffer_update(vs, x, y, w, h); + worker->zrle.type = VNC_ENCODING_ZRLE; + return zrle_send_framebuffer_update(vs, worker, x, y, w, h); } -int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +int vnc_zywrle_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { - vs->zrle->type = VNC_ENCODING_ZYWRLE; - return zrle_send_framebuffer_update(vs, x, y, w, h); + worker->zrle.type = VNC_ENCODING_ZYWRLE; + return zrle_send_framebuffer_update(vs, worker, x, y, w, h); } -void vnc_zrle_clear(VncState *vs) +void vnc_zrle_clear(VncWorker *worker) { - if (vs->zrle->stream.opaque) { - deflateEnd(&vs->zrle->stream); + if (worker->zrle.stream.opaque) { + deflateEnd(&worker->zrle.stream); } - buffer_free(&vs->zrle->zrle); - buffer_free(&vs->zrle->fb); - buffer_free(&vs->zrle->zlib); + buffer_free(&worker->zrle.zrle); + buffer_free(&worker->zrle.fb); + buffer_free(&worker->zrle.zlib); } diff --git a/ui/vnc-enc-zrle.c.inc b/ui/vnc-enc-zrle.c.inc index 2ef7501d52..68d28f58b7 100644 --- a/ui/vnc-enc-zrle.c.inc +++ b/ui/vnc-enc-zrle.c.inc @@ -62,16 +62,16 @@ #define ZRLE_ENCODE_TILE ZRLE_CONCAT2(zrle_encode_tile, ZRLE_ENCODE_SUFFIX) #define ZRLE_WRITE_PALETTE ZRLE_CONCAT2(zrle_write_palette,ZRLE_ENCODE_SUFFIX) -static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, - int zywrle_level); +static void ZRLE_ENCODE_TILE(VncState *vs, VncZrle *zrle, ZRLE_PIXEL *data, + int w, int h, int zywrle_level); #if ZRLE_BPP != 8 #include "vnc-enc-zywrle-template.c" #endif -static void ZRLE_ENCODE(VncState *vs, int x, int y, int w, int h, - int zywrle_level) +static void ZRLE_ENCODE(VncState *vs, VncZrle *zrle, + int x, int y, int w, int h, int zywrle_level) { int ty; @@ -87,16 +87,16 @@ static void ZRLE_ENCODE(VncState *vs, int x, int y, int w, int h, tw = MIN(VNC_ZRLE_TILE_WIDTH, x + w - tx); - buf = zrle_convert_fb(vs, tx, ty, tw, th, ZRLE_BPP); - ZRLE_ENCODE_TILE(vs, buf, tw, th, zywrle_level); + buf = zrle_convert_fb(vs, zrle, tx, ty, tw, th, ZRLE_BPP); + ZRLE_ENCODE_TILE(vs, zrle, buf, tw, th, zywrle_level); } } } -static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, - int zywrle_level) +static void ZRLE_ENCODE_TILE(VncState *vs, VncZrle *zrle, ZRLE_PIXEL *data, + int w, int h, int zywrle_level) { - VncPalette *palette = &vs->zrle->palette; + VncPalette *palette = &zrle->palette; int runs = 0; int single_pixels = 0; @@ -236,7 +236,7 @@ static void ZRLE_ENCODE_TILE(VncState *vs, ZRLE_PIXEL *data, int w, int h, #if ZRLE_BPP != 8 if (zywrle_level > 0 && !(zywrle_level & 0x80)) { ZYWRLE_ANALYZE(data, data, w, h, w, zywrle_level, vs->zywrle.buf); - ZRLE_ENCODE_TILE(vs, data, w, h, zywrle_level | 0x80); + ZRLE_ENCODE_TILE(vs, zrle, data, w, h, zywrle_level | 0x80); } else #endif diff --git a/ui/vnc-jobs.c b/ui/vnc-jobs.c index d3486af9e2..bed33950a8 100644 --- a/ui/vnc-jobs.c +++ b/ui/vnc-jobs.c @@ -185,14 +185,10 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) local->vnc_encoding = orig->vnc_encoding; local->features = orig->features; local->vd = orig->vd; - local->lossy_rect = orig->lossy_rect; local->write_pixels = orig->write_pixels; local->client_pf = orig->client_pf; local->client_endian = orig->client_endian; - local->tight = orig->tight; - local->zlib = orig->zlib; local->hextile = orig->hextile; - local->zrle = orig->zrle; local->client_width = orig->client_width; local->client_height = orig->client_height; } @@ -200,11 +196,7 @@ static void vnc_async_encoding_start(VncState *orig, VncState *local) static void vnc_async_encoding_end(VncState *orig, VncState *local) { buffer_free(&local->output); - orig->tight = local->tight; - orig->zlib = local->zlib; orig->hextile = local->hextile; - orig->zrle = local->zrle; - orig->lossy_rect = local->lossy_rect; } static bool vnc_worker_clamp_rect(VncState *vs, VncJob *job, VncRect *rect) @@ -237,6 +229,7 @@ static bool vnc_worker_clamp_rect(VncState *vs, VncJob *job, VncRect *rect) static int vnc_worker_thread_loop(VncJobQueue *queue) { + VncConnection *vc; VncJob *job; VncRectEntry *entry, *tmp; VncState vs = {}; @@ -256,6 +249,7 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) } assert(job->vs->magic == VNC_MAGIC); + vc = container_of(job->vs, VncConnection, vs); vnc_lock_output(job->vs); if (job->vs->ioc == NULL || job->vs->abort == true) { @@ -295,7 +289,8 @@ static int vnc_worker_thread_loop(VncJobQueue *queue) } if (vnc_worker_clamp_rect(&vs, job, &entry->rect)) { - n = vnc_send_framebuffer_update(&vs, entry->rect.x, entry->rect.y, + n = vnc_send_framebuffer_update(&vs, &vc->worker, + entry->rect.x, entry->rect.y, entry->rect.w, entry->rect.h); if (n >= 0) { diff --git a/ui/vnc.c b/ui/vnc.c index ab74154e4c..1df35832d5 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -56,11 +56,6 @@ #include "io/dns-resolver.h" #include "monitor/monitor.h" -typedef struct VncConnection { - VncState vs; - VncZlib zlib; -} VncConnection; - #define VNC_REFRESH_INTERVAL_BASE GUI_REFRESH_INTERVAL_DEFAULT #define VNC_REFRESH_INTERVAL_INC 50 #define VNC_REFRESH_INTERVAL_MAX GUI_REFRESH_INTERVAL_IDLE @@ -951,29 +946,30 @@ int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) return 1; } -int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h) +int vnc_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h) { int n = 0; switch(vs->vnc_encoding) { case VNC_ENCODING_ZLIB: - n = vnc_zlib_send_framebuffer_update(vs, x, y, w, h); + n = vnc_zlib_send_framebuffer_update(vs, worker, x, y, w, h); break; case VNC_ENCODING_HEXTILE: vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_HEXTILE); n = vnc_hextile_send_framebuffer_update(vs, x, y, w, h); break; case VNC_ENCODING_TIGHT: - n = vnc_tight_send_framebuffer_update(vs, x, y, w, h); + n = vnc_tight_send_framebuffer_update(vs, worker, x, y, w, h); break; case VNC_ENCODING_TIGHT_PNG: - n = vnc_tight_png_send_framebuffer_update(vs, x, y, w, h); + n = vnc_tight_png_send_framebuffer_update(vs, worker, x, y, w, h); break; case VNC_ENCODING_ZRLE: - n = vnc_zrle_send_framebuffer_update(vs, x, y, w, h); + n = vnc_zrle_send_framebuffer_update(vs, worker, x, y, w, h); break; case VNC_ENCODING_ZYWRLE: - n = vnc_zywrle_send_framebuffer_update(vs, x, y, w, h); + n = vnc_zywrle_send_framebuffer_update(vs, worker, x, y, w, h); break; default: vnc_framebuffer_update(vs, x, y, w, h, VNC_ENCODING_RAW); @@ -1311,7 +1307,7 @@ static void vnc_disconnect_start(VncState *vs) void vnc_disconnect_finish(VncState *vs) { - int i; + VncConnection *vc = container_of(vs, VncConnection, vs); trace_vnc_client_disconnect_finish(vs, vs->ioc); @@ -1325,9 +1321,9 @@ void vnc_disconnect_finish(VncState *vs) qapi_free_VncClientInfo(vs->info); - vnc_zlib_clear(vs); - vnc_tight_clear(vs); - vnc_zrle_clear(vs); + vnc_zlib_clear(&vc->worker); + vnc_tight_clear(&vc->worker); + vnc_zrle_clear(&vc->worker); #ifdef CONFIG_VNC_SASL vnc_sasl_client_cleanup(vs); @@ -1355,19 +1351,12 @@ void vnc_disconnect_finish(VncState *vs) } buffer_free(&vs->jobs_buffer); - for (i = 0; i < VNC_STAT_ROWS; ++i) { - g_free(vs->lossy_rect[i]); - } - g_free(vs->lossy_rect); - object_unref(OBJECT(vs->ioc)); vs->ioc = NULL; object_unref(OBJECT(vs->sioc)); vs->sioc = NULL; vs->magic = 0; - g_free(vs->zrle); - g_free(vs->tight); - g_free(container_of(vs, VncConnection, vs)); + g_free(vc); } size_t vnc_client_io_error(VncState *vs, ssize_t ret, Error *err) @@ -2131,13 +2120,14 @@ static void send_xvp_message(VncState *vs, int code) static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) { + VncConnection *vc = container_of(vs, VncConnection, vs); int i; unsigned int enc = 0; vs->features = 0; vs->vnc_encoding = 0; - vs->tight->compression = 9; - vs->tight->quality = -1; /* Lossless by default */ + vc->worker.tight.compression = 9; + vc->worker.tight.quality = -1; /* Lossless by default */ vs->absolute = -1; /* @@ -2225,11 +2215,11 @@ static void set_encodings(VncState *vs, int32_t *encodings, size_t n_encodings) vnc_server_cut_text_caps(vs); break; case VNC_ENCODING_COMPRESSLEVEL0 ... VNC_ENCODING_COMPRESSLEVEL0 + 9: - vs->tight->compression = (enc & 0x0F); + vc->worker.tight.compression = (enc & 0x0F); break; case VNC_ENCODING_QUALITYLEVEL0 ... VNC_ENCODING_QUALITYLEVEL0 + 9: if (vs->vd->lossy) { - vs->tight->quality = (enc & 0x0F); + vc->worker.tight.quality = (enc & 0x0F); } break; default: @@ -2958,7 +2948,7 @@ static VncRectStat *vnc_stat_rect(VncDisplay *vd, int x, int y) return &vs->stats[y / VNC_STAT_RECT][x / VNC_STAT_RECT]; } -void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h) +void vnc_sent_lossy_rect(VncWorker *worker, int x, int y, int w, int h) { int i, j; @@ -2969,7 +2959,7 @@ void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h) for (j = y; j <= h; j++) { for (i = x; i <= w; i++) { - vs->lossy_rect[j][i] = 1; + worker->lossy_rect[j][i] = 1; } } } @@ -2985,6 +2975,7 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) x = QEMU_ALIGN_DOWN(x, VNC_STAT_RECT); QTAILQ_FOREACH(vs, &vd->clients, next) { + VncConnection *vc = container_of(vs, VncConnection, vs); int j; /* kernel send buffers are full -> refresh later */ @@ -2992,11 +2983,11 @@ static int vnc_refresh_lossy_rect(VncDisplay *vd, int x, int y) continue; } - if (!vs->lossy_rect[sty][stx]) { + if (!vc->worker.lossy_rect[sty][stx]) { continue; } - vs->lossy_rect[sty][stx] = 0; + vc->worker.lossy_rect[sty][stx] = 0; for (j = 0; j < VNC_STAT_RECT; ++j) { bitmap_set(vs->dirty[y + j], x / VNC_DIRTY_PIXELS_PER_BIT, @@ -3249,12 +3240,8 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, VncConnection *vc = g_new0(VncConnection, 1); VncState *vs = &vc->vs; bool first_client = QTAILQ_EMPTY(&vd->clients); - int i; trace_vnc_client_connect(vs, sioc); - vs->zlib = &vc->zlib; - vs->zrle = g_new0(VncZrle, 1); - vs->tight = g_new0(VncTight, 1); vs->magic = VNC_MAGIC; vs->sioc = sioc; object_ref(OBJECT(vs->sioc)); @@ -3262,23 +3249,23 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, object_ref(OBJECT(vs->ioc)); vs->vd = vd; - buffer_init(&vs->input, "vnc-input/%p", sioc); - buffer_init(&vs->output, "vnc-output/%p", sioc); - buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc); + buffer_init(&vs->input, "vnc-input/%p", sioc); + buffer_init(&vs->output, "vnc-output/%p", sioc); + buffer_init(&vs->jobs_buffer, "vnc-jobs_buffer/%p", sioc); - buffer_init(&vs->tight->tight, "vnc-tight/%p", sioc); - buffer_init(&vs->tight->zlib, "vnc-tight-zlib/%p", sioc); - buffer_init(&vs->tight->gradient, "vnc-tight-gradient/%p", sioc); + buffer_init(&vc->worker.tight.tight, "vnc-tight/%p", sioc); + buffer_init(&vc->worker.tight.zlib, "vnc-tight-zlib/%p", sioc); + buffer_init(&vc->worker.tight.gradient, "vnc-tight-gradient/%p", sioc); #ifdef CONFIG_VNC_JPEG - buffer_init(&vs->tight->jpeg, "vnc-tight-jpeg/%p", sioc); + buffer_init(&vc->worker.tight.jpeg, "vnc-tight-jpeg/%p", sioc); #endif #ifdef CONFIG_PNG - buffer_init(&vs->tight->png, "vnc-tight-png/%p", sioc); + buffer_init(&vc->worker.tight.png, "vnc-tight-png/%p", sioc); #endif - buffer_init(&vc->zlib.zlib, "vnc-zlib/%p", sioc); - buffer_init(&vs->zrle->zrle, "vnc-zrle/%p", sioc); - buffer_init(&vs->zrle->fb, "vnc-zrle-fb/%p", sioc); - buffer_init(&vs->zrle->zlib, "vnc-zrle-zlib/%p", sioc); + buffer_init(&vc->worker.zlib.zlib, "vnc-zlib/%p", sioc); + buffer_init(&vc->worker.zrle.zrle, "vnc-zrle/%p", sioc); + buffer_init(&vc->worker.zrle.fb, "vnc-zrle-fb/%p", sioc); + buffer_init(&vc->worker.zrle.zlib, "vnc-zrle-zlib/%p", sioc); if (skipauth) { vs->auth = VNC_AUTH_NONE; @@ -3295,11 +3282,6 @@ static void vnc_connect(VncDisplay *vd, QIOChannelSocket *sioc, VNC_DEBUG("Client sioc=%p ws=%d auth=%d subauth=%d\n", sioc, websocket, vs->auth, vs->subauth); - vs->lossy_rect = g_malloc0(VNC_STAT_ROWS * sizeof (*vs->lossy_rect)); - for (i = 0; i < VNC_STAT_ROWS; ++i) { - vs->lossy_rect[i] = g_new0(uint8_t, VNC_STAT_COLS); - } - VNC_DEBUG("New client on socket %p\n", vs->sioc); update_displaychangelistener(&vd->dcl, VNC_REFRESH_INTERVAL_BASE); qio_channel_set_blocking(vs->ioc, false, NULL); diff --git a/ui/vnc.h b/ui/vnc.h index 8df0cbab25..f2dab2f4d9 100644 --- a/ui/vnc.h +++ b/ui/vnc.h @@ -272,8 +272,6 @@ struct VncState gboolean disconnecting; DECLARE_BITMAP(dirty[VNC_MAX_HEIGHT], VNC_DIRTY_BITS); - uint8_t **lossy_rect; /* Not an Array to avoid costly memcpy in - * vnc-jobs-async.c */ VncDisplay *vd; VncStateUpdate update; /* Most recent pending request from client */ @@ -341,10 +339,7 @@ struct VncState /* Encoding specific, if you add something here, don't forget to * update vnc_async_encoding_start() */ - VncTight *tight; - VncZlib *zlib; VncHextile hextile; - VncZrle *zrle; VncZywrle zywrle; Notifier mouse_mode_notifier; @@ -356,6 +351,19 @@ struct VncState QTAILQ_ENTRY(VncState) next; }; +typedef struct VncWorker { + uint8_t lossy_rect[VNC_STAT_ROWS][VNC_STAT_COLS]; + + VncTight tight; + VncZlib zlib; + VncZrle zrle; +} VncWorker; + +typedef struct VncConnection { + VncState vs; + VncWorker worker; +} VncConnection; + /***************************************************************************** * @@ -602,10 +610,11 @@ int vnc_server_fb_stride(VncDisplay *vd); void vnc_convert_pixel(VncState *vs, uint8_t *buf, uint32_t v); double vnc_update_freq(VncState *vs, int x, int y, int w, int h); -void vnc_sent_lossy_rect(VncState *vs, int x, int y, int w, int h); +void vnc_sent_lossy_rect(VncWorker *worker, int x, int y, int w, int h); /* Encodings */ -int vnc_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); +int vnc_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); int vnc_raw_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); @@ -615,17 +624,21 @@ void vnc_hextile_set_pixel_conversion(VncState *vs, int generic); void *vnc_zlib_zalloc(void *x, unsigned items, unsigned size); void vnc_zlib_zfree(void *x, void *addr); -int vnc_zlib_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); -void vnc_zlib_clear(VncState *vs); - -int vnc_tight_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); -int vnc_tight_png_send_framebuffer_update(VncState *vs, int x, int y, - int w, int h); -void vnc_tight_clear(VncState *vs); - -int vnc_zrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); -int vnc_zywrle_send_framebuffer_update(VncState *vs, int x, int y, int w, int h); -void vnc_zrle_clear(VncState *vs); +int vnc_zlib_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); +void vnc_zlib_clear(VncWorker *worker); + +int vnc_tight_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); +int vnc_tight_png_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); +void vnc_tight_clear(VncWorker *worker); + +int vnc_zrle_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); +int vnc_zywrle_send_framebuffer_update(VncState *vs, VncWorker *worker, + int x, int y, int w, int h); +void vnc_zrle_clear(VncWorker *worker); /* vnc-clipboard.c */ void vnc_server_cut_text_caps(VncState *vs); From 46c798f0471ca341ce607b5ca15f76b001712f3f Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Mon, 16 Jun 2025 21:32:25 -0700 Subject: [PATCH 2269/2760] ui/egl-helpers: Error check the fds in egl_dmabuf_export_texture() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While trying to export and obtain fds associated with a texture, it is possible that the fds returned after eglExportDMABUFImageMESA() call have error values. Therefore, we need to evaluate the value of all fds and return false if any of them are negative. Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Dmitry Osipenko Cc: Frediano Ziglio Cc: Dongwon Kim Cc: Michael Scherle Reviewed-by: Marc-André Lureau Signed-off-by: Vivek Kasireddy Message-Id: <20250617043546.1022779-2-vivek.kasireddy@intel.com> --- ui/egl-helpers.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 5503a795e4..e3f2872cc1 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -295,6 +295,7 @@ bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset, { EGLImageKHR image; EGLuint64KHR modifiers[DMABUF_MAX_PLANES]; + int i; image = eglCreateImageKHR(qemu_egl_display, eglGetCurrentContext(), EGL_GL_TEXTURE_2D_KHR, @@ -314,6 +315,11 @@ bool egl_dmabuf_export_texture(uint32_t tex_id, int *fd, EGLint *offset, *modifier = modifiers[0]; } + for (i = 0; i < *num_planes; i++) { + if (fd[i] < 0) { + return false; + } + } return true; } From 376d4b22e4d7dd81cb0c1ea1dfe1db0a0dc4b0e2 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Mon, 16 Jun 2025 21:32:26 -0700 Subject: [PATCH 2270/2760] ui/spice: Enable gl=on option for non-local or remote clients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Newer versions of Spice server should be able to accept dmabuf fds from Qemu for clients that are connected via the network. In other words, when this option is enabled, Qemu would share a dmabuf fd with Spice which would encode and send the data associated with the fd to a client that could be located on a different machine. Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Dmitry Osipenko Cc: Frediano Ziglio Cc: Dongwon Kim Cc: Michael Scherle Reviewed-by: Marc-André Lureau Signed-off-by: Vivek Kasireddy Message-Id: <20250617043546.1022779-3-vivek.kasireddy@intel.com> --- include/ui/spice-display.h | 1 + ui/spice-core.c | 4 ++++ ui/spice-display.c | 1 + 3 files changed, 6 insertions(+) diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index e1a9b36185..6c55f38c8b 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -151,6 +151,7 @@ struct SimpleSpiceCursor { }; extern bool spice_opengl; +extern bool spice_remote_client; int qemu_spice_rect_is_empty(const QXLRect* r); void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r); diff --git a/ui/spice-core.c b/ui/spice-core.c index 0326c63bec..5acbdd3955 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -836,9 +836,13 @@ static void qemu_spice_init(void) #ifdef HAVE_SPICE_GL if (qemu_opt_get_bool(opts, "gl", 0)) { if ((port != 0) || (tls_port != 0)) { +#if SPICE_SERVER_VERSION >= 0x000f03 /* release 0.15.3 */ + spice_remote_client = 1; +#else error_report("SPICE GL support is local-only for now and " "incompatible with -spice port/tls-port"); exit(1); +#endif } egl_init(qemu_opt_get(opts, "rendernode"), DISPLAY_GL_MODE_ON, &error_fatal); spice_opengl = 1; diff --git a/ui/spice-display.c b/ui/spice-display.c index 9c39d2c5c8..0fb72f6d6f 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -31,6 +31,7 @@ #include "standard-headers/drm/drm_fourcc.h" bool spice_opengl; +bool spice_remote_client; int qemu_spice_rect_is_empty(const QXLRect* r) { From 95b3be1965779e77a12a95d531914fa0fecc24ec Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:39:48 -0400 Subject: [PATCH 2271/2760] qapi: add cross-references to acpi.json Signed-off-by: John Snow Message-ID: <20250711054005.60969-2-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/acpi.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/acpi.json b/qapi/acpi.json index 90f8f564fd..906b3687a5 100644 --- a/qapi/acpi.json +++ b/qapi/acpi.json @@ -108,7 +108,7 @@ ## # @query-acpi-ospm-status: # -# Return a list of ACPIOSTInfo for devices that support status +# Return a list of `ACPIOSTInfo` for devices that support status # reporting via ACPI _OST method. # # Since: 2.1 From a724defe3213999db735496daee94abb52e93f43 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:39:49 -0400 Subject: [PATCH 2272/2760] qapi: add cross-references to authz.json Signed-off-by: John Snow Message-ID: <20250711054005.60969-3-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/authz.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/authz.json b/qapi/authz.json index ad1b4b3af0..bc1123cc05 100644 --- a/qapi/authz.json +++ b/qapi/authz.json @@ -77,7 +77,7 @@ # Properties for authz-listfile objects. # # @filename: File name to load the configuration from. The file must -# contain valid JSON for AuthZListProperties. +# contain valid JSON for `AuthZListProperties`. # # @refresh: If true, inotify is used to monitor the file, # automatically reloading changes. If an error occurs during From a3763337ad0f4e6252abc894a8bbced11ddf3998 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:39:50 -0400 Subject: [PATCH 2273/2760] qapi: add cross-references to block layer Signed-off-by: John Snow Message-ID: <20250711054005.60969-4-jsnow@redhat.com> Reviewed-by: Markus Armbruster [Two unwanted cross-references dropped] Signed-off-by: Markus Armbruster --- qapi/block-core.json | 184 ++++++++++++++++++++--------------------- qapi/block-export.json | 36 ++++---- qapi/block.json | 14 ++-- qapi/transaction.json | 12 +-- 4 files changed, 123 insertions(+), 123 deletions(-) diff --git a/qapi/block-core.json b/qapi/block-core.json index 848ebaff5a..25b57919a7 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -32,7 +32,7 @@ # @icount: Current instruction count. Appears when execution # record/replay is enabled. Used for "time-traveling" to match # the moment in the recorded execution with the snapshots. This -# counter may be obtained through @query-replay command +# counter may be obtained through `query-replay` command # (since 5.2) # # Since: 1.3 @@ -224,7 +224,7 @@ { 'struct': 'ImageInfoSpecificLUKSWrapper', 'data': { 'data': 'QCryptoBlockInfoLUKS' } } # If we need to add block driver specific parameters for -# LUKS in future, then we'll subclass QCryptoBlockInfoLUKS +# LUKS in future, then we'll subclass `QCryptoBlockInfoLUKS` # to define a ImageInfoSpecificLUKS ## @@ -333,7 +333,7 @@ # node, annotated with information about that node in relation to its # parent. # -# @name: Child name of the root node in the BlockGraphInfo struct, in +# @name: Child name of the root node in the `BlockGraphInfo` struct, in # its role as the child of some undescribed parent node # # @info: Block graph information starting at this node @@ -350,7 +350,7 @@ # @BlockGraphInfo: # # Information about all nodes in a block (sub)graph in the form of -# BlockNodeInfo data. The base BlockNodeInfo struct contains the +# `BlockNodeInfo` data. The base `BlockNodeInfo` struct contains the # information for the (sub)graph's root node. # # @children: Array of links to this node's child nodes' information @@ -615,7 +615,7 @@ # @inconsistent: true if this is a persistent bitmap that was # improperly stored. Implies @persistent to be true; @recording # and @busy to be false. This bitmap cannot be used. To remove -# it, use @block-dirty-bitmap-remove. (Since 4.0) +# it, use `block-dirty-bitmap-remove`. (Since 4.0) # # Since: 1.3 ## @@ -710,12 +710,12 @@ # @tray_open: True if the device's tray is open (only present if it # has a tray) # -# @io-status: @BlockDeviceIoStatus. Only present if the device +# @io-status: `BlockDeviceIoStatus`. Only present if the device # supports it and the VM is configured to stop on errors # (supported device models: virtio-blk, IDE, SCSI except # scsi-generic) # -# @inserted: @BlockDeviceInfo describing the device if media is +# @inserted: `BlockDeviceInfo` describing the device if media is # present # # Since: 0.14 @@ -762,7 +762,7 @@ ## # @query-block: # -# Get a list of BlockInfo for all virtual block devices. +# Get a list of `BlockInfo` for all virtual block devices. # # Returns: a list describing each virtual block device. # Filter nodes that were created implicitly are skipped over. @@ -1027,14 +1027,14 @@ # @timed_stats: Statistics specific to the set of previously defined # intervals of time (Since 2.5) # -# @rd_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0) +# @rd_latency_histogram: `BlockLatencyHistogramInfo`. (Since 4.0) # -# @wr_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0) +# @wr_latency_histogram: `BlockLatencyHistogramInfo`. (Since 4.0) # -# @zone_append_latency_histogram: @BlockLatencyHistogramInfo. +# @zone_append_latency_histogram: `BlockLatencyHistogramInfo`. # (since 8.1) # -# @flush_latency_histogram: @BlockLatencyHistogramInfo. (Since 4.0) +# @flush_latency_histogram: `BlockLatencyHistogramInfo`. (Since 4.0) # # Since: 0.14 ## @@ -1135,7 +1135,7 @@ # @qdev: The qdev ID, or if no ID is assigned, the QOM path of the # block device. (since 3.0) # -# @stats: A @BlockDeviceStats for the device. +# @stats: A `BlockDeviceStats` for the device. # # @driver-specific: Optional driver-specific stats. (Since 4.2) # @@ -1159,7 +1159,7 @@ ## # @query-blockstats: # -# Query the @BlockStats for all virtual block devices. +# Query the `BlockStats` for all virtual block devices. # # @query-nodes: If true, the command will query all the block nodes # that have a node name, in a list which will include "parent" @@ -1290,8 +1290,8 @@ # @report: for guest operations, report the error to the guest; for # jobs, cancel the job # -# @ignore: ignore the error, only report a QMP event (BLOCK_IO_ERROR -# or BLOCK_JOB_ERROR). The backup, mirror and commit block jobs +# @ignore: ignore the error, only report a QMP event (`BLOCK_IO_ERROR` +# or `BLOCK_JOB_ERROR`). The backup, mirror and commit block jobs # retry the failing request later and may still complete # successfully. The stream block job continues to stream and will # complete with an error. @@ -1324,7 +1324,7 @@ # (since: 2.4) # # @bitmap: only copy data described by the dirty bitmap. Behavior on -# completion is determined by the BitmapSyncMode. (since: 4.2) +# completion is determined by the `BitmapSyncMode`. (since: 4.2) # # Since: 1.3 ## @@ -1531,7 +1531,7 @@ # @overlay: reference to the existing block device that will become # the overlay of @node, as part of taking the snapshot. It must # not have a current backing file (this can be achieved by passing -# "backing": null to blockdev-add). +# "backing": null to `blockdev-add`). # # Since: 2.5 ## @@ -1586,7 +1586,7 @@ # @bitmap: The name of a dirty bitmap to use. Must be present if sync # is "bitmap" or "incremental". Can be present if sync is "full" # or "top". Must not be present otherwise. -# (Since 2.4 (drive-backup), 3.1 (blockdev-backup)) +# (Since 2.4 (`drive-backup`), 3.1 (`blockdev-backup`)) # # @bitmap-mode: Specifies the type of data the bitmap should contain # after the operation concludes. Must be present if a bitmap was @@ -1597,7 +1597,7 @@ # # @on-source-error: the action to take on an error on the source, # default 'report'. 'stop' and 'enospc' can only be used if the -# block device supports io-status (see BlockInfo). +# block device supports io-status (see `BlockInfo`). # # @on-target-error: the action to take on an error on the target, # default 'report' (no limitations, since this applies to a @@ -1607,14 +1607,14 @@ # copy-before-write jobs; defaults to break-guest-write. (Since 10.1) # # @auto-finalize: When false, this job will wait in a PENDING state -# after it has finished its work, waiting for @job-finalize before +# after it has finished its work, waiting for `job-finalize` before # making any block graph changes. When true, this job will # automatically perform its abort or commit actions. Defaults to # true. (Since 2.12) # # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits -# @job-dismiss. When true, this job will automatically disappear +# `job-dismiss`. When true, this job will automatically disappear # without user intervention. Defaults to true. (Since 2.12) # # @filter-node-name: the node name that should be assigned to the @@ -1720,7 +1720,7 @@ # # @allow-write-only-overlay: If present, the check whether this # operation is safe was relaxed so that it can be used to change -# backing file of a destination of a blockdev-mirror. (since 5.0) +# backing file of a destination of a `blockdev-mirror`. (since 5.0) # # Since: 2.5 # @@ -1785,7 +1785,7 @@ # If top == base, that is an error. If top has no overlays on top of # it, or if it is in use by a writer, the job will not be completed by # itself. The user needs to complete the job with the -# job-complete command after getting the ready event. (Since 2.0) +# `job-complete` command after getting the ready event. (Since 2.0) # # If the base image is smaller than top, then the base image will be # resized to be the same size as top. If top is smaller than the base @@ -1847,14 +1847,14 @@ # autogenerated. (Since: 2.9) # # @auto-finalize: When false, this job will wait in a PENDING state -# after it has finished its work, waiting for @job-finalize before +# after it has finished its work, waiting for `job-finalize` before # making any block graph changes. When true, this job will # automatically perform its abort or commit actions. Defaults to # true. (Since 3.1) # # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits -# @job-dismiss. When true, this job will automatically disappear +# `job-dismiss`. When true, this job will automatically disappear # without user intervention. Defaults to true. (Since 3.1) # # Features: @@ -1890,14 +1890,14 @@ # @drive-backup: # # Start a point-in-time copy of a block device to a new destination. -# The status of ongoing drive-backup operations can be checked with -# query-block-jobs where the BlockJobInfo.type field has the value +# The status of ongoing `drive-backup` operations can be checked with +# `query-block-jobs` where the `BlockJobInfo`.type field has the value # 'backup'. The operation can be stopped before it has completed -# using the job-cancel or block-job-cancel command. +# using the `job-cancel` or `block-job-cancel` command. # # Features: # -# @deprecated: This command is deprecated. Use @blockdev-backup +# @deprecated: This command is deprecated. Use `blockdev-backup` # instead. # # Errors: @@ -1921,10 +1921,10 @@ # @blockdev-backup: # # Start a point-in-time copy of a block device to a new destination. -# The status of ongoing blockdev-backup operations can be checked with -# query-block-jobs where the BlockJobInfo.type field has the value +# The status of ongoing `blockdev-backup` operations can be checked with +# `query-block-jobs` where the `BlockJobInfo`.type field has the value # 'backup'. The operation can be stopped before it has completed -# using the job-cancel or block-job-cancel command. +# using the `job-cancel` or `block-job-cancel` command. # # Errors: # - If @device is not a valid block device, DeviceNotFound @@ -2025,7 +2025,7 @@ # @XDbgBlockGraphNode: # # @id: Block graph node identifier. This @id is generated only for -# x-debug-query-block-graph and does not relate to any other +# `x-debug-query-block-graph` and does not relate to any other # identifiers in QEMU. # # @type: Type of graph node. Can be one of block-backend, block-job @@ -2074,7 +2074,7 @@ ## # @XDbgBlockGraphEdge: # -# Block Graph edge description for x-debug-query-block-graph. +# Block Graph edge description for `x-debug-query-block-graph`. # # @parent: parent id # @@ -2192,7 +2192,7 @@ # # @on-source-error: the action to take on an error on the source, # default 'report'. 'stop' and 'enospc' can only be used if the -# block device supports io-status (see BlockInfo). +# block device supports io-status (see `BlockInfo`). # # @on-target-error: the action to take on an error on the target, # default 'report' (no limitations, since this applies to a @@ -2208,14 +2208,14 @@ # 'background' (Since: 3.0) # # @auto-finalize: When false, this job will wait in a PENDING state -# after it has finished its work, waiting for @job-finalize before +# after it has finished its work, waiting for `job-finalize` before # making any block graph changes. When true, this job will # automatically perform its abort or commit actions. Defaults to # true. (Since 3.1) # # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits -# @job-dismiss. When true, this job will automatically disappear +# `job-dismiss`. When true, this job will automatically disappear # without user intervention. Defaults to true. (Since 3.1) # # Since: 1.3 @@ -2250,16 +2250,16 @@ # @name: name of the dirty bitmap (must be less than 1024 bytes) # # @granularity: the bitmap granularity, default is 64k for -# block-dirty-bitmap-add +# `block-dirty-bitmap-add` # # @persistent: the bitmap is persistent, i.e. it will be saved to the # corresponding block device image file on its close. For now # only Qcow2 disks support persistent bitmaps. Default is false -# for block-dirty-bitmap-add. (Since: 2.10) +# for `block-dirty-bitmap-add`. (Since: 2.10) # # @disabled: the bitmap is created in the disabled state, which means # that it will not track drive changes. The bitmap may be enabled -# with block-dirty-bitmap-enable. Default is false. (Since: 4.0) +# with `block-dirty-bitmap-enable`. Default is false. (Since: 4.0) # # Since: 2.4 ## @@ -2289,7 +2289,7 @@ # @target: name of the destination dirty bitmap # # @bitmaps: name(s) of the source dirty bitmap(s) at @node and/or -# fully specified BlockDirtyBitmap elements. The latter are +# fully specified `BlockDirtyBitmap` elements. The latter are # supported since 4.1. # # Since: 4.0 @@ -2324,7 +2324,7 @@ # @block-dirty-bitmap-remove: # # Stop write tracking and remove the dirty bitmap that was created -# with block-dirty-bitmap-add. If the bitmap is persistent, remove it +# with `block-dirty-bitmap-add`. If the bitmap is persistent, remove it # from its storage too. # # Errors: @@ -2508,7 +2508,7 @@ # # @on-source-error: the action to take on an error on the source, # default 'report'. 'stop' and 'enospc' can only be used if the -# block device supports io-status (see BlockInfo). +# block device supports io-status (see `BlockInfo`). # # @on-target-error: the action to take on an error on the target, # default 'report' (no limitations, since this applies to a @@ -2523,14 +2523,14 @@ # 'background' (Since: 3.0) # # @auto-finalize: When false, this job will wait in a PENDING state -# after it has finished its work, waiting for @job-finalize before +# after it has finished its work, waiting for `job-finalize` before # making any block graph changes. When true, this job will # automatically perform its abort or commit actions. Defaults to # true. (Since 3.1) # # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits -# @job-dismiss. When true, this job will automatically disappear +# `job-dismiss`. When true, this job will automatically disappear # without user intervention. Defaults to true. (Since 3.1) # # @target-is-zero: Assume the destination reads as all zeroes before @@ -2778,9 +2778,9 @@ # The block streaming operation is performed in the background until # the entire backing file has been copied. This command returns # immediately once streaming has started. The status of ongoing block -# streaming operations can be checked with query-block-jobs. The +# streaming operations can be checked with `query-block-jobs`. The # operation can be stopped before it has completed using the -# job-cancel or block-job-cancel command. +# `job-cancel` or `block-job-cancel` command. # # The node that receives the data is called the top image, can be # located in any part of the chain (but always above the base image; @@ -2799,9 +2799,9 @@ # will be the new backing file. # # On successful completion the image file is updated to drop the -# backing file and the BLOCK_JOB_COMPLETED event is emitted. +# backing file and the `BLOCK_JOB_COMPLETED` event is emitted. # -# In case @device is a filter node, block-stream modifies the first +# In case @device is a filter node, `block-stream` modifies the first # non-filter overlay node below it to point to the new backing node # instead of modifying @device itself. # @@ -2842,7 +2842,7 @@ # # @on-error: the action to take on an error (default report). 'stop' # and 'enospc' can only be used if the block device supports -# io-status (see BlockInfo). (Since 1.3) +# io-status (see `BlockInfo`). (Since 1.3) # # @filter-node-name: the node name that should be assigned to the # filter driver that the stream job inserts into the graph above @@ -2850,14 +2850,14 @@ # autogenerated. (Since: 6.0) # # @auto-finalize: When false, this job will wait in a PENDING state -# after it has finished its work, waiting for @job-finalize before +# after it has finished its work, waiting for `job-finalize` before # making any block graph changes. When true, this job will # automatically perform its abort or commit actions. Defaults to # true. (Since 3.1) # # @auto-dismiss: When false, this job will wait in a CONCLUDED state # after it has completely ceased all work, and awaits -# @job-dismiss. When true, this job will automatically disappear +# `job-dismiss`. When true, this job will automatically disappear # without user intervention. Defaults to true. (Since 3.1) # # Errors: @@ -2918,13 +2918,13 @@ # command if no operation is in progress. # # The operation will cancel as soon as possible and then emit the -# BLOCK_JOB_CANCELLED event. Before that happens the job is still -# visible when enumerated using query-block-jobs. +# `BLOCK_JOB_CANCELLED` event. Before that happens the job is still +# visible when enumerated using `query-block-jobs`. # -# Note that if you issue 'block-job-cancel' after 'drive-mirror' has -# indicated (via the event BLOCK_JOB_READY) that the source and +# Note that if you issue `block-job-cancel` after `drive-mirror` has +# indicated (via the event `BLOCK_JOB_READY`) that the source and # destination are synchronized, then the event triggered by this -# command changes to BLOCK_JOB_COMPLETED, to indicate that the +# command changes to `BLOCK_JOB_COMPLETED`, to indicate that the # mirroring has ended and the destination now has a point-in-time copy # tied to the time of the cancellation. # @@ -2938,7 +2938,7 @@ # values. # # @force: If true, and the job has already emitted the event -# BLOCK_JOB_READY, abandon the job immediately (even if it is +# `BLOCK_JOB_READY`, abandon the job immediately (even if it is # paused) instead of waiting for the destination to complete its # final synchronization (since 1.3) # @@ -2961,7 +2961,7 @@ # # The job will pause as soon as possible, which means transitioning # into the PAUSED state if it was RUNNING, or into STANDBY if it was -# READY. The corresponding JOB_STATUS_CHANGE event will be emitted. +# READY. The corresponding `JOB_STATUS_CHANGE` event will be emitted. # # Cancelling a paused job automatically resumes it. # @@ -2971,7 +2971,7 @@ # # Features: # -# @deprecated: This command is deprecated. Use @job-pause +# @deprecated: This command is deprecated. Use `job-pause` # instead. # # Errors: @@ -3000,7 +3000,7 @@ # # Features: # -# @deprecated: This command is deprecated. Use @job-resume +# @deprecated: This command is deprecated. Use `job-resume` # instead. # # Errors: @@ -3021,14 +3021,14 @@ # # This is supported only for drive mirroring, where it also switches # the device to write to the target path only. Note that drive -# mirroring includes drive-mirror, blockdev-mirror and block-commit +# mirroring includes `drive-mirror`, `blockdev-mirror` and `block-commit` # job (only in case of "active commit", when the node being commited # is used by the guest). The ability to complete is signaled with a -# BLOCK_JOB_READY event. +# `BLOCK_JOB_READY` event. # # This command completes an active background block operation # synchronously. The ordering of this command's return with the -# BLOCK_JOB_COMPLETED event is not defined. Note that if an I/O error +# `BLOCK_JOB_COMPLETED` event is not defined. Note that if an I/O error # occurs during the processing of this command: 1) the command itself # will fail; 2) the error will be processed according to the # rerror/werror arguments that were specified when starting the @@ -3040,7 +3040,7 @@ # # Features: # -# @deprecated: This command is deprecated. Use @job-complete +# @deprecated: This command is deprecated. Use `job-complete` # instead. # # Errors: @@ -3059,21 +3059,21 @@ # Deletes a job that is in the CONCLUDED state. This command only # needs to be run explicitly for jobs that don't have automatic # dismiss enabled. In turn, automatic dismiss may be enabled only -# for jobs that have @auto-dismiss option, which are drive-backup, -# blockdev-backup, drive-mirror, blockdev-mirror, block-commit and -# block-stream. @auto-dismiss is enabled by default for these +# for jobs that have @auto-dismiss option, which are `drive-backup`, +# `blockdev-backup`, `drive-mirror`, `blockdev-mirror`, `block-commit` and +# `block-stream`. @auto-dismiss is enabled by default for these # jobs. # # This command will refuse to operate on any job that has not yet # reached its terminal state, CONCLUDED. For jobs that make use of -# the BLOCK_JOB_READY event, job-cancel, block-job-cancel or -# job-complete will still need to be used as appropriate. +# the `BLOCK_JOB_READY` event, `job-cancel`, `block-job-cancel` or +# `job-complete` will still need to be used as appropriate. # # @id: The job identifier. # # Features: # -# @deprecated: This command is deprecated. Use @job-dismiss +# @deprecated: This command is deprecated. Use `job-dismiss` # instead. # # Since: 2.12 @@ -3101,7 +3101,7 @@ # # Features: # -# @deprecated: This command is deprecated. Use @job-finalize +# @deprecated: This command is deprecated. Use `job-finalize` # instead. # # Since: 2.12 @@ -3173,7 +3173,7 @@ # @on: Enabled # # @unmap: Enabled and even try to unmap blocks if possible. This -# requires also that @BlockdevDiscardOptions is set to unmap for +# requires also that `BlockdevDiscardOptions` is set to unmap for # this device. # # Since: 2.1 @@ -4715,7 +4715,7 @@ # @driver: block driver name # # @node-name: the node name of the new node. This option is required -# on the top level of blockdev-add. Valid node names start with +# on the top level of `blockdev-add`. Valid node names start with # an alphabetic character and may contain only alphanumeric # characters, '-', '.' and '_'. Their maximum length is 31 # characters. (Since 2.0) @@ -4919,7 +4919,7 @@ # cancelled. # # The command receives a list of block devices to reopen. For each -# one of them, the top-level @node-name option (from BlockdevOptions) +# one of them, the top-level @node-name option (from `BlockdevOptions`) # must be specified and is used to select the block device to be # reopened. Other @node-name options must be either omitted or set to # the current name of the appropriate node. This command won't change @@ -4928,7 +4928,7 @@ # In the case of options that refer to child nodes, the behavior of # this command depends on the value: # -# 1) A set of options (BlockdevOptions): the child is reopened with +# 1) A set of options (`BlockdevOptions`): the child is reopened with # the specified set of options. # # 2) A reference to the current child: the child is reopened using @@ -4942,7 +4942,7 @@ # Options (1) and (2) are supported in all cases. Option (3) is # supported for @file and @backing, and option (4) for @backing only. # -# Unlike with blockdev-add, the @backing option must always be present +# Unlike with `blockdev-add`, the @backing option must always be present # unless the node being reopened does not have a backing file and its # image does not have a default backing file name as part of its # metadata. @@ -4956,7 +4956,7 @@ ## # @blockdev-del: # -# Deletes a block device that has been added using blockdev-add. The +# Deletes a block device that has been added using `blockdev-add`. The # command will fail if the node is attached to a device or is # otherwise being used. # @@ -5511,7 +5511,7 @@ # @blockdev-create: # # Starts a job to create an image format on a given node. The job is -# automatically finalized, but a manual job-dismiss is required. +# automatically finalized, but a manual `job-dismiss` is required. # # @job-id: Identifier for the newly created job. # @@ -5571,7 +5571,7 @@ # # Starts a job to amend format specific options of an existing open # block device. The job is automatically finalized, but a manual -# job-dismiss is required. +# `job-dismiss` is required. # # @job-id: Identifier for the newly created job. # @@ -5640,10 +5640,10 @@ # # @fatal: if set, the image is marked corrupt and therefore unusable # after this event and must be repaired (Since 2.2; before, every -# BLOCK_IMAGE_CORRUPTED event was fatal) +# `BLOCK_IMAGE_CORRUPTED` event was fatal) # -# .. note:: If action is "stop", a STOP event will eventually follow -# the BLOCK_IO_ERROR event. +# .. note:: If action is "stop", a `STOP` event will eventually follow +# the `BLOCK_IO_ERROR` event. # # .. qmp-example:: # @@ -5684,15 +5684,15 @@ # # @nospace: true if I/O error was caused due to a no-space condition. # This key is only present if query-block's io-status is present, -# please see query-block documentation for more information +# please see `query-block` documentation for more information # (since: 2.2) # # @reason: human readable string describing the error cause. (This # field is a debugging aid for humans, it should not be parsed by # applications) (since: 2.2) # -# .. note:: If action is "stop", a STOP event will eventually follow -# the BLOCK_IO_ERROR event. +# .. note:: If action is "stop", a `STOP` event will eventually follow +# the `BLOCK_IO_ERROR` event. # # .. note:: This event is rate-limited. # @@ -5834,7 +5834,7 @@ # @speed: rate limit, bytes per second # # .. note:: The "ready to complete" status is always reset by a -# @BLOCK_JOB_ERROR event. +# `BLOCK_JOB_ERROR` event. # # Since: 1.3 # @@ -5856,7 +5856,7 @@ # @BLOCK_JOB_PENDING: # # Emitted when a block job is awaiting explicit authorization to -# finalize graph changes via @job-finalize. If this job is part +# finalize graph changes via `job-finalize`. If this job is part # of a transaction, it will not emit this event until the transaction # has converged first. # @@ -5905,7 +5905,7 @@ # configured write threshold. For thin-provisioned devices, this # means the device should be extended to avoid pausing for disk # exhaustion. The event is one shot. Once triggered, it needs to be -# re-registered with another block-set-write-threshold command. +# re-registered with another `block-set-write-threshold` command. # # @node-name: graph node name on which the threshold was exceeded. # @@ -5976,7 +5976,7 @@ # # TODO: Removing children from a quorum node means introducing # gaps in the child indices. This cannot be represented in the -# 'children' list of BlockdevOptionsQuorum, as returned by +# 'children' list of `BlockdevOptionsQuorum`, as returned by # .bdrv_refresh_filename(). # # Since: 2.7 @@ -6189,7 +6189,7 @@ # Synchronously delete an internal snapshot of a block device, when # the format of the image used support it. The snapshot is identified # by name or id or both. One of the name or id is required. Return -# SnapshotInfo for the successfully deleted snapshot. +# `SnapshotInfo` for the successfully deleted snapshot. # # @device: the device name or node-name of a root node to delete the # snapshot from diff --git a/qapi/block-export.json b/qapi/block-export.json index fb004e35fd..6878b89dcf 100644 --- a/qapi/block-export.json +++ b/qapi/block-export.json @@ -38,9 +38,9 @@ ## # @NbdServerOptions: # -# Keep this type consistent with the NbdServerOptionsLegacy type. The -# only intended difference is using SocketAddress instead of -# SocketAddressLegacy. +# Keep this type consistent with the `NbdServerOptionsLegacy` type. The +# only intended difference is using `SocketAddress` instead of +# `SocketAddressLegacy`. # # @addr: Address on which to listen (since 4.2). ## @@ -51,9 +51,9 @@ ## # @NbdServerOptionsLegacy: # -# Keep this type consistent with the NbdServerOptions type. The only -# intended difference is using SocketAddressLegacy instead of -# SocketAddress. +# Keep this type consistent with the `NbdServerOptions` type. The only +# intended difference is using `SocketAddressLegacy` instead of +# `SocketAddress`. # # @addr: Address on which to listen (since 1.3). ## @@ -65,7 +65,7 @@ # @nbd-server-start: # # Start an NBD server listening on the given host and port. Block -# devices can then be exported using @nbd-server-add. The NBD server +# devices can then be exported using `nbd-server-add`. The NBD server # will present them as named exports; for example, another QEMU # instance could refer to them as "nbd:HOST:PORT:exportname=NAME". # @@ -81,8 +81,8 @@ ## # @BlockExportOptionsNbdBase: # -# An NBD block export (common options shared between nbd-server-add -# and the NBD branch of block-export-add). +# An NBD block export (common options shared between `nbd-server-add` +# and the NBD branch of `block-export-add`). # # @name: Export name. If unspecified, the @device parameter is used # as the export name. (Since 2.12) @@ -99,7 +99,7 @@ # @BlockExportOptionsNbd: # # An NBD block export (distinct options used in the NBD branch of -# block-export-add). +# `block-export-add`). # # @bitmaps: Also export each of the named dirty bitmaps reachable from # @device, so the NBD client can use NBD_OPT_SET_META_CONTEXT with @@ -125,7 +125,7 @@ # A vhost-user-blk block export. # # @addr: The vhost-user socket on which to listen. Both 'unix' and -# 'fd' SocketAddress types are supported. Passed fds must be UNIX +# 'fd' `SocketAddress` types are supported. Passed fds must be UNIX # domain sockets. # # @logical-block-size: Logical block size in bytes. Defaults to 512 @@ -217,7 +217,7 @@ ## # @NbdServerAddOptions: # -# An NBD block export, per legacy nbd-server-add command. +# An NBD block export, per legacy `nbd-server-add` command. # # @device: The device name or node name of the node to be exported # @@ -246,7 +246,7 @@ # # Features: # -# @deprecated: This command is deprecated. Use @block-export-add +# @deprecated: This command is deprecated. Use `block-export-add` # instead. # # Errors: @@ -289,12 +289,12 @@ # # @name: Block export id. # -# @mode: Mode of command operation. See @BlockExportRemoveMode +# @mode: Mode of command operation. See `BlockExportRemoveMode` # description. Default is 'safe'. # # Features: # -# @deprecated: This command is deprecated. Use @block-export-del +# @deprecated: This command is deprecated. Use `block-export-del` # instead. # # Errors: @@ -313,7 +313,7 @@ # @nbd-server-stop: # # Stop QEMU's embedded NBD server, and unregister all devices -# previously added via @nbd-server-add. +# previously added via `nbd-server-add`. # # Since: 1.3 ## @@ -422,7 +422,7 @@ # # @id: Block export id. # -# @mode: Mode of command operation. See @BlockExportRemoveMode +# @mode: Mode of command operation. See `BlockExportRemoveMode` # description. Default is 'safe'. # # Errors: @@ -460,7 +460,7 @@ # @node-name: The node name of the block node that is exported # # @shutting-down: True if the export is shutting down (e.g. after a -# block-export-del command, but before the shutdown has completed) +# `block-export-del` command, but before the shutdown has completed) # # Since: 5.2 ## diff --git a/qapi/block.json b/qapi/block.json index f0436ce871..46955bbb3e 100644 --- a/qapi/block.json +++ b/qapi/block.json @@ -140,7 +140,7 @@ # # If the tray was already open before, this will be a no-op. # -# Once the tray opens, a DEVICE_TRAY_MOVED event is emitted. There +# Once the tray opens, a `DEVICE_TRAY_MOVED` event is emitted. There # are cases in which no such event will be generated, these include: # # - if the guest has locked the tray, @force is false and the guest @@ -299,7 +299,7 @@ # @BlockdevChangeReadOnlyMode: # # Specifies the new read-only mode of a block device subject to the -# @blockdev-change-medium command. +# `blockdev-change-medium` command. # # @retain: Retains the current read-only mode # @@ -317,9 +317,9 @@ # # Changes the medium inserted into a block device by ejecting the # current medium and loading a new image file which is inserted as the -# new medium (this command combines blockdev-open-tray, -# blockdev-remove-medium, blockdev-insert-medium and -# blockdev-close-tray). +# new medium (this command combines `blockdev-open-tray`, +# `blockdev-remove-medium`, `blockdev-insert-medium` and +# `blockdev-close-tray`). # # @device: Block device name # @@ -334,7 +334,7 @@ # to 'retain' # # @force: if false (the default), an eject request through -# blockdev-open-tray will be sent to the guest if it has locked +# `blockdev-open-tray` will be sent to the guest if it has locked # the tray (and the tray will not be opened immediately); if true, # the tray will be opened regardless of whether it is locked. # (since 7.1) @@ -522,7 +522,7 @@ # @id: The name or QOM path of the guest device. # # @boundaries: list of interval boundary values (see description in -# BlockLatencyHistogramInfo definition). If specified, all +# `BlockLatencyHistogramInfo` definition). If specified, all # latency histograms are removed, and empty ones created for all # io types with intervals corresponding to @boundaries (except for # io types, for which specific boundaries are set through the diff --git a/qapi/transaction.json b/qapi/transaction.json index 927035f5a7..4b4eb09bf3 100644 --- a/qapi/transaction.json +++ b/qapi/transaction.json @@ -69,8 +69,8 @@ # # Features: # -# @deprecated: Member @drive-backup is deprecated. Use member -# @blockdev-backup instead. +# @deprecated: Member `drive-backup` is deprecated. Use member +# `blockdev-backup` instead. # # Since: 1.1 ## @@ -158,7 +158,7 @@ # @TransactionAction: # # A discriminated record of operations that can be performed with -# @transaction. +# `transaction`. # # @type: the operation to be performed # @@ -189,7 +189,7 @@ # # @completion-mode: Controls how jobs launched asynchronously by # Actions will complete or fail as a group. See -# @ActionCompletionMode for details. +# `ActionCompletionMode` for details. # # Since: 2.5 ## @@ -229,11 +229,11 @@ # in the transaction. When an I/O error occurs during deletion, the # user needs to fix it later with qemu-img or other command. # -# @actions: List of @TransactionAction; information needed for the +# @actions: List of `TransactionAction`; information needed for the # respective operations. # # @properties: structure of additional options to control the -# execution of the transaction. See @TransactionProperties for +# execution of the transaction. See `TransactionProperties` for # additional detail. # # Errors: From ed17e68732d2efa4dc813a02de0bbf68ed40775c Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:39:51 -0400 Subject: [PATCH 2274/2760] qapi: add cross-references to crypto.json Signed-off-by: John Snow Message-ID: <20250711054005.60969-5-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/crypto.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qapi/crypto.json b/qapi/crypto.json index 21006de36c..ab6eda4c2f 100644 --- a/qapi/crypto.json +++ b/qapi/crypto.json @@ -591,9 +591,9 @@ # # Specific parameters for RSA algorithm. # -# @hash-alg: QCryptoHashAlgo +# @hash-alg: `QCryptoHashAlgo` # -# @padding-alg: QCryptoRSAPaddingAlgo +# @padding-alg: `QCryptoRSAPaddingAlgo` # # Since: 7.1 ## From dc79c02898dd5b2fdc9851f6c226421c701f6478 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:39:52 -0400 Subject: [PATCH 2275/2760] qapi: add cross-references to dump.json Signed-off-by: John Snow Message-ID: <20250711054005.60969-6-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/dump.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/qapi/dump.json b/qapi/dump.json index dc92b53247..32c8c1f06e 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -112,7 +112,7 @@ # # Describe the status of a long-running background guest memory dump. # -# @none: no dump-guest-memory has started yet. +# @none: no `dump-guest-memory` has started yet. # # @active: there is one dump running in background. # @@ -128,9 +128,9 @@ ## # @DumpQueryResult: # -# The result format for 'query-dump'. +# The result format for `query-dump`. # -# @status: enum of @DumpStatus, which shows current dump status +# @status: enum of `DumpStatus`, which shows current dump status # # @completed: bytes written in latest dump (uncompressed) # @@ -186,7 +186,7 @@ ## # @DumpGuestMemoryCapability: # -# @formats: the available formats for dump-guest-memory +# @formats: the available formats for `dump-guest-memory` # # Since: 2.0 ## @@ -197,9 +197,9 @@ ## # @query-dump-guest-memory-capability: # -# Return the available formats for dump-guest-memory +# Return the available formats for `dump-guest-memory` # -# Returns: An object listing available formats for dump-guest-memory +# Returns: An object listing available formats for `dump-guest-memory` # # Since: 2.0 # From d1813fb4c4b052ec24814b7781431ee60bd57fdd Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:39:53 -0400 Subject: [PATCH 2276/2760] qapi: add cross-references to job.json Signed-off-by: John Snow Message-ID: <20250711054005.60969-7-jsnow@redhat.com> Reviewed-by: Markus Armbruster [Accidental line rewrap and an unwanted cross-refence dropped] Signed-off-by: Markus Armbruster --- qapi/job.json | 56 +++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/qapi/job.json b/qapi/job.json index 51a57c523e..8b08350af2 100644 --- a/qapi/job.json +++ b/qapi/job.json @@ -12,26 +12,26 @@ # # Type of a background job. # -# @commit: block commit job type, see "block-commit" +# @commit: block commit job type, see `block-commit` # -# @stream: block stream job type, see "block-stream" +# @stream: block stream job type, see `block-stream` # -# @mirror: drive mirror job type, see "drive-mirror" +# @mirror: drive mirror job type, see `drive-mirror` # -# @backup: drive backup job type, see "drive-backup" +# @backup: drive backup job type, see `drive-backup` # -# @create: image creation job type, see "blockdev-create" (since 3.0) +# @create: image creation job type, see `blockdev-create` (since 3.0) # -# @amend: image options amend job type, see "x-blockdev-amend" +# @amend: image options amend job type, see `x-blockdev-amend` # (since 5.1) # -# @snapshot-load: snapshot load job type, see "snapshot-load" +# @snapshot-load: snapshot load job type, see `snapshot-load` # (since 6.0) # -# @snapshot-save: snapshot save job type, see "snapshot-save" +# @snapshot-save: snapshot save job type, see `snapshot-save` # (since 6.0) # -# @snapshot-delete: snapshot delete job type, see "snapshot-delete" +# @snapshot-delete: snapshot delete job type, see `snapshot-delete` # (since 6.0) # # Since: 1.7 @@ -67,7 +67,7 @@ # # @pending: The job has finished its work, but has finalization steps # that it needs to make prior to completing. These changes will -# require manual intervention via @job-finalize if auto-finalize +# require manual intervention via `job-finalize` if auto-finalize # was set to false. These pending changes may still fail. # # @aborting: The job is in the process of being aborted, and will @@ -77,7 +77,7 @@ # # @concluded: The job has finished all work. If auto-dismiss was set # to false, the job will remain in this state until it is -# dismissed via @job-dismiss. +# dismissed via `job-dismiss`. # # @null: The job is in the process of being dismantled. This state # should not ever be visible externally. @@ -93,21 +93,21 @@ # # Represents command verbs that can be applied to a job. # -# @cancel: see @job-cancel +# @cancel: see `job-cancel` # -# @pause: see @job-pause +# @pause: see `job-pause` # -# @resume: see @job-resume +# @resume: see `job-resume` # -# @set-speed: see @block-job-set-speed +# @set-speed: see `block-job-set-speed` # -# @complete: see @job-complete +# @complete: see `job-complete` # -# @dismiss: see @job-dismiss +# @dismiss: see `job-dismiss` # -# @finalize: see @job-finalize +# @finalize: see `job-finalize` # -# @change: see @block-job-change (since 8.2) +# @change: see `block-job-change` (since 8.2) # # Since: 2.12 ## @@ -140,7 +140,7 @@ # # The job will pause as soon as possible, which means transitioning # into the PAUSED state if it was RUNNING, or into STANDBY if it was -# READY. The corresponding JOB_STATUS_CHANGE event will be emitted. +# READY. The corresponding `JOB_STATUS_CHANGE` event will be emitted. # # Cancelling a paused job automatically resumes it. # @@ -175,7 +175,7 @@ # cancellation. # # The job will cancel as soon as possible and then emit a -# JOB_STATUS_CHANGE event. Usually, the status will change to +# `JOB_STATUS_CHANGE` event. Usually, the status will change to # ABORTING, but it is possible that a job successfully completes (e.g. # because it was almost done and there was no opportunity to cancel # earlier than completing the job) and transitions to PENDING instead. @@ -194,14 +194,14 @@ # # This is supported only for drive mirroring, where it also switches # the device to write to the target path only. Note that drive -# mirroring includes drive-mirror, blockdev-mirror and block-commit +# mirroring includes `drive-mirror`, `blockdev-mirror` and `block-commit` # job (only in case of "active commit", when the node being commited # is used by the guest). The ability to complete is signaled with a -# BLOCK_JOB_READY event. +# `BLOCK_JOB_READY` event. # # This command completes an active background block operation # synchronously. The ordering of this command's return with the -# BLOCK_JOB_COMPLETED event is not defined. Note that if an I/O error +# `BLOCK_JOB_COMPLETED` event is not defined. Note that if an I/O error # occurs during the processing of this command: 1) the command itself # will fail; 2) the error will be processed according to the # rerror/werror arguments that were specified when starting the @@ -219,14 +219,14 @@ # Deletes a job that is in the CONCLUDED state. This command only # needs to be run explicitly for jobs that don't have automatic # dismiss enabled. In turn, automatic dismiss may be enabled only -# for jobs that have @auto-dismiss option, which are drive-backup, -# blockdev-backup, drive-mirror, blockdev-mirror, block-commit and -# block-stream. @auto-dismiss is enabled by default for these +# for jobs that have @auto-dismiss option, which are `drive-backup`, +# `blockdev-backup`, `drive-mirror`, `blockdev-mirror`, `block-commit` and +# `block-stream`. @auto-dismiss is enabled by default for these # jobs. # # This command will refuse to operate on any job that has not yet # reached its terminal state, CONCLUDED. For jobs that make use of -# the JOB_READY event, job-cancel or job-complete will still need to +# the JOB_READY event, `job-cancel` or `job-complete` will still need to # be used as appropriate. # # @id: The job identifier. From 6082825e95cd1d3e638c1df68c05c8847cac5c08 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:39:54 -0400 Subject: [PATCH 2277/2760] qapi: add cross-references to Machine core Signed-off-by: John Snow Message-ID: <20250711054005.60969-8-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/machine-common.json | 20 +++++----- qapi/machine.json | 80 ++++++++++++++++++++-------------------- 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/qapi/machine-common.json b/qapi/machine-common.json index 0f01599130..ed3d20a2fb 100644 --- a/qapi/machine-common.json +++ b/qapi/machine-common.json @@ -28,29 +28,29 @@ # # @thread: thread level, which would also be called SMT level or # logical processor level. The @threads option in -# SMPConfiguration is used to configure the topology of this +# `SMPConfiguration` is used to configure the topology of this # level. # -# @core: core level. The @cores option in SMPConfiguration is used +# @core: core level. The @cores option in `SMPConfiguration` is used # to configure the topology of this level. # -# @module: module level. The @modules option in SMPConfiguration is +# @module: module level. The @modules option in `SMPConfiguration` is # used to configure the topology of this level. # -# @cluster: cluster level. The @clusters option in SMPConfiguration +# @cluster: cluster level. The @clusters option in `SMPConfiguration` # is used to configure the topology of this level. # -# @die: die level. The @dies option in SMPConfiguration is used to +# @die: die level. The @dies option in `SMPConfiguration` is used to # configure the topology of this level. # # @socket: socket level, which would also be called package level. -# The @sockets option in SMPConfiguration is used to configure +# The @sockets option in `SMPConfiguration` is used to configure # the topology of this level. # -# @book: book level. The @books option in SMPConfiguration is used +# @book: book level. The @books option in `SMPConfiguration` is used # to configure the topology of this level. # -# @drawer: drawer level. The @drawers option in SMPConfiguration is +# @drawer: drawer level. The @drawers option in `SMPConfiguration` is # used to configure the topology of this level. # # @default: default level. Some architectures will have default @@ -104,9 +104,9 @@ ## # @SmpCachePropertiesWrapper: # -# List wrapper of SmpCacheProperties. +# List wrapper of `SmpCacheProperties`. # -# @caches: the list of SmpCacheProperties. +# @caches: the list of `SmpCacheProperties`. # # Since 9.2 ## diff --git a/qapi/machine.json b/qapi/machine.json index 2364893cba..f9bfda2151 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -188,7 +188,7 @@ # @acpi: machine type supports ACPI (since 8.0) # # @compat-props: The machine type's compatibility properties. Only -# present when query-machines argument @compat-props is true. +# present when `query-machines` argument @compat-props is true. # (since 9.1) # # Features: @@ -377,7 +377,7 @@ # # Wake up guest from suspend. If the guest has wake-up from suspend # support enabled (wakeup-suspend-support flag from -# query-current-machine), wake-up guest from suspend if the guest is +# `query-current-machine`), wake-up guest from suspend if the guest is # in SUSPENDED state. Return an error otherwise. # # Since: 1.1 @@ -589,7 +589,7 @@ # # List of CXL Fixed Memory Windows. # -# @cxl-fmw: List of CXLFixedMemoryWindowOptions +# @cxl-fmw: List of `CXLFixedMemoryWindowOptions` # # Since: 7.1 ## @@ -644,10 +644,10 @@ ## # @NumaCpuOptions: # -# Option "-numa cpu" overrides default cpu to node mapping. It -# accepts the same set of cpu properties as returned by -# query-hotpluggable-cpus[].props, where node-id could be used to -# override default node mapping. +# Option "-numa cpu" overrides default cpu to node mapping. It accepts +# the same set of cpu properties as returned by +# `query-hotpluggable-cpus[].props `, where +# node-id could be used to override default node mapping. # # Since: 2.10 ## @@ -661,7 +661,7 @@ # The memory hierarchy in the System Locality Latency and Bandwidth # Information Structure of HMAT (Heterogeneous Memory Attribute Table) # -# For more information about @HmatLBMemoryHierarchy, see chapter +# For more information about `HmatLBMemoryHierarchy`, see chapter # 5.2.27.4: Table 5-146: Field "Flags" of ACPI 6.3 spec. # # @memory: the structure represents the memory performance @@ -683,7 +683,7 @@ # Data type in the System Locality Latency and Bandwidth Information # Structure of HMAT (Heterogeneous Memory Attribute Table) # -# For more information about @HmatLBDataType, see chapter 5.2.27.4: +# For more information about `HmatLBDataType`, see chapter 5.2.27.4: # Table 5-146: Field "Data Type" of ACPI 6.3 spec. # # @access-latency: access latency (nanoseconds) @@ -710,7 +710,7 @@ # Set the system locality latency and bandwidth information between # Initiator and Target proximity Domains. # -# For more information about @NumaHmatLBOptions, see chapter 5.2.27.4: +# For more information about `NumaHmatLBOptions`, see chapter 5.2.27.4: # Table 5-146 of ACPI 6.3 spec. # # @initiator: the Initiator Proximity Domain. @@ -746,7 +746,7 @@ # Cache associativity in the Memory Side Cache Information Structure # of HMAT # -# For more information of @HmatCacheAssociativity, see chapter +# For more information of `HmatCacheAssociativity`, see chapter # 5.2.27.5: Table 5-147 of ACPI 6.3 spec. # # @none: None (no memory side cache in this proximity domain, or cache @@ -767,7 +767,7 @@ # Cache write policy in the Memory Side Cache Information Structure of # HMAT # -# For more information of @HmatCacheWritePolicy, see chapter 5.2.27.5: +# For more information of `HmatCacheWritePolicy`, see chapter 5.2.27.5: # Table 5-147: Field "Cache Attributes" of ACPI 6.3 spec. # # @none: None (no memory side cache in this proximity domain, or cache @@ -787,7 +787,7 @@ # # Set the memory side cache information for a given memory domain. # -# For more information of @NumaHmatCacheOptions, see chapter 5.2.27.5: +# For more information of `NumaHmatCacheOptions`, see chapter 5.2.27.5: # Table 5-147: Field "Cache Attributes" of ACPI 6.3 spec. # # @node-id: the memory proximity domain to which the memory belongs. @@ -964,7 +964,7 @@ # # The ids other than the node-id specify the position of the CPU # within the CPU topology (as defined by the machine property "smp", -# thus see also type @SMPConfiguration) +# thus see also type `SMPConfiguration`) # # @node-id: NUMA node ID the CPU belongs to # @@ -992,7 +992,7 @@ # Since: 2.7 ## { 'struct': 'CpuInstanceProperties', - # Keep these in sync with the properties device_add accepts + # Keep these in sync with the properties `device_add` accepts 'data': { '*node-id': 'int', '*drawer-id': 'int', '*book-id': 'int', @@ -1008,19 +1008,19 @@ ## # @HotpluggableCPU: # -# @type: CPU object type for usage with device_add command +# @type: CPU object type for usage with `device_add` command # # @props: list of properties to pass for hotplugging a CPU with -# device_add +# `device_add` # -# @vcpus-count: number of logical VCPU threads @HotpluggableCPU +# @vcpus-count: number of logical VCPU threads `HotpluggableCPU` # provides # # @qom-path: link to existing CPU object if CPU is present or omitted # if CPU is not present. # # .. note:: Management should be prepared to pass through additional -# properties with device_add. +# properties with `device_add`. # # Since: 2.7 ## @@ -1179,7 +1179,7 @@ # @BALLOON_CHANGE: # # Emitted when the guest changes the actual BALLOON level. This value -# is equivalent to the @actual field return by the 'query-balloon' +# is equivalent to the @actual field return by the `query-balloon` # command # # @actual: the logical size of the VM in bytes. Formula used: @@ -1965,7 +1965,7 @@ # # The result of a CPU model baseline. # -# @model: the baselined CpuModelInfo. +# @model: the baselined `CpuModelInfo`. # # Since: 2.8 ## @@ -2014,28 +2014,28 @@ # # * QEMU version: CPU models may look different depending on the QEMU # version. (Except for CPU models reported as "static" in -# query-cpu-definitions.) +# `query-cpu-definitions`.) # * machine-type: CPU model may look different depending on the # machine-type. (Except for CPU models reported as "static" in -# query-cpu-definitions.) +# `query-cpu-definitions`.) # * machine options (including accelerator): in some architectures, # CPU models may look different depending on machine and accelerator # options. (Except for CPU models reported as "static" in -# query-cpu-definitions.) +# `query-cpu-definitions`.) # * "-cpu" arguments and global properties: arguments to the -cpu # option and global properties may affect expansion of CPU models. -# Using query-cpu-model-expansion while using these is not advised. +# Using `query-cpu-model-expansion` while using these is not advised. # # Some architectures may not support comparing CPU models. s390x # supports comparing CPU models. # # @modela: description of the first CPU model to compare, referred to -# as "model A" in CpuModelCompareResult +# as "model A" in `CpuModelCompareResult` # # @modelb: description of the second CPU model to compare, referred to -# as "model B" in CpuModelCompareResult +# as "model B" in `CpuModelCompareResult` # -# Returns: a CpuModelCompareInfo describing how both CPU models +# Returns: a `CpuModelCompareInfo` describing how both CPU models # compare # # Errors: @@ -2068,17 +2068,17 @@ # # * QEMU version: CPU models may look different depending on the QEMU # version. (Except for CPU models reported as "static" in -# query-cpu-definitions.) +# `query-cpu-definitions`.) # * machine-type: CPU model may look different depending on the # machine-type. (Except for CPU models reported as "static" in -# query-cpu-definitions.) +# `query-cpu-definitions`.) # * machine options (including accelerator): in some architectures, # CPU models may look different depending on machine and accelerator # options. (Except for CPU models reported as "static" in -# query-cpu-definitions.) +# `query-cpu-definitions`.) # * "-cpu" arguments and global properties: arguments to the -cpu # option and global properties may affect expansion of CPU models. -# Using query-cpu-model-expansion while using these is not advised. +# Using `query-cpu-model-expansion` while using these is not advised. # # Some architectures may not support baselining CPU models. s390x # supports baselining CPU models. @@ -2087,7 +2087,7 @@ # # @modelb: description of the second CPU model to baseline # -# Returns: a CpuModelBaselineInfo describing the baselined CPU model +# Returns: a `CpuModelBaselineInfo` describing the baselined CPU model # # Errors: # - if baselining CPU models is not supported by the target @@ -2107,7 +2107,7 @@ # # The result of a cpu model expansion. # -# @model: the expanded CpuModelInfo. +# @model: the expanded `CpuModelInfo`. # # @deprecated-props: an optional list of properties that are flagged as # deprecated by the CPU vendor. The list depends on the @@ -2136,17 +2136,17 @@ # # * QEMU version: CPU models may look different depending on the QEMU # version. (Except for CPU models reported as "static" in -# query-cpu-definitions.) +# `query-cpu-definitions`.) # * machine-type: CPU model may look different depending on the # machine-type. (Except for CPU models reported as "static" in -# query-cpu-definitions.) +# `query-cpu-definitions`.) # * machine options (including accelerator): in some architectures, # CPU models may look different depending on machine and accelerator # options. (Except for CPU models reported as "static" in -# query-cpu-definitions.) +# `query-cpu-definitions`.) # * "-cpu" arguments and global properties: arguments to the -cpu # option and global properties may affect expansion of CPU models. -# Using query-cpu-model-expansion while using these is not advised. +# Using `query-cpu-model-expansion` while using these is not advised. # # Some architectures may not support all expansion types. s390x # supports "full" and "static". Arm only supports "full". @@ -2155,7 +2155,7 @@ # # @type: expansion type, specifying how to expand the CPU model # -# Returns: a CpuModelExpansionInfo describing the expanded CPU model +# Returns: a `CpuModelExpansionInfo` describing the expanded CPU model # # Errors: # - if expanding CPU models is not supported @@ -2194,7 +2194,7 @@ # from running in the current host. (since 2.8) # # @typename: Type name that can be used as argument to -# @device-list-properties, to introspect properties configurable +# `device-list-properties`, to introspect properties configurable # using -cpu or -global. (since 2.9) # # @alias-of: Name of CPU model this model is an alias for. The target From b51ea1c42111464961a4b2bb3f1d3f4f0ddfc006 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:39:55 -0400 Subject: [PATCH 2278/2760] qapi: add cross-references to migration.json Note that a reference to MIGRATION needs to be disambiguated with a :qapi:event: prefix. Without this, Sphinx complains more than one target found for 'any' cross-reference 'MIGRATION': could be :std:ref:`Migration framework` or :qapi:event:`QMP:migration.MIGRATION` Signed-off-by: John Snow Message-ID: <20250711054005.60969-9-jsnow@redhat.com> Reviewed-by: Markus Armbruster [Commit message amended to explain need for :qapi:event:] Signed-off-by: Markus Armbruster --- qapi/migration.json | 68 ++++++++++++++++++++++----------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/qapi/migration.json b/qapi/migration.json index 57653160eb..e08a99bb82 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -195,14 +195,14 @@ # # Information about current migration process. # -# @status: @MigrationStatus describing the current migration status. +# @status: `MigrationStatus` describing the current migration status. # If this field is not returned, no migration process has been # initiated # -# @ram: @MigrationStats containing detailed migration status, only +# @ram: `MigrationStats` containing detailed migration status, only # returned if status is 'active' or 'completed'(since 1.2) # -# @xbzrle-cache: @XBZRLECacheStats containing detailed XBZRLE +# @xbzrle-cache: `XBZRLECacheStats` containing detailed XBZRLE # migration statistics, only returned if XBZRLE feature is on and # status is 'active' or 'completed' (since 1.2) # @@ -266,7 +266,7 @@ # @socket-address: Only used for tcp, to know what the real port is # (Since 4.0) # -# @vfio: @VfioStats containing detailed VFIO devices migration +# @vfio: `VfioStats` containing detailed VFIO devices migration # statistics, only returned if VFIO device is present, migration # is supported by all VFIO devices and status is 'active' or # 'completed' (since 5.2) @@ -277,7 +277,7 @@ # # @dirty-limit-throttle-time-per-round: Maximum throttle time (in # microseconds) of virtual CPUs each dirty ring full round, which -# shows how MigrationCapability dirty-limit affects the guest +# shows how `MigrationCapability` dirty-limit affects the guest # during live migration. (Since 8.1) # # @dirty-limit-ring-full-time: Estimated average dirty ring full time @@ -627,7 +627,7 @@ # # @normal: the original form of migration. (since 8.2) # -# @cpr-reboot: The migrate command stops the VM and saves state to the +# @cpr-reboot: The `migrate` command stops the VM and saves state to the # URI. After quitting QEMU, the user resumes by running QEMU # -incoming. # @@ -677,7 +677,7 @@ # # New QEMU reads the CPR channel before opening a monitor, hence # the CPR channel cannot be specified in the list of channels for -# a migrate-incoming command. It may only be specified on the +# a `migrate-incoming` command. It may only be specified on the # command line. # # The main channel address cannot be a file type, and for an @@ -688,10 +688,10 @@ # memory-backend-epc is not supported. The VM must be started # with the '-machine aux-ram-share=on' option. # -# When using -incoming defer, you must issue the migrate command +# When using -incoming defer, you must issue the `migrate` command # to old QEMU before issuing any monitor commands to new QEMU. # However, new QEMU does not open and read the migration stream -# until you issue the migrate incoming command. +# until you issue the `migrate-incoming` command. # # (since 10.0) ## @@ -913,11 +913,11 @@ # @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. # Defaults to 1. (Since 8.1) # -# @mode: Migration mode. See description in @MigMode. Default is +# @mode: Migration mode. See description in `MigMode`. Default is # 'normal'. (Since 8.2) # # @zero-page-detection: Whether and how to detect zero pages. -# See description in @ZeroPageDetection. Default is 'multifd'. +# See description in `ZeroPageDetection`. Default is 'multifd'. # (since 9.0) # # @direct-io: Open migration files with O_DIRECT when possible. This @@ -1094,11 +1094,11 @@ # @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. # Defaults to 1. (Since 8.1) # -# @mode: Migration mode. See description in @MigMode. Default is +# @mode: Migration mode. See description in `MigMode`. Default is # 'normal'. (Since 8.2) # # @zero-page-detection: Whether and how to detect zero pages. -# See description in @ZeroPageDetection. Default is 'multifd'. +# See description in `ZeroPageDetection`. Default is 'multifd'. # (since 9.0) # # @direct-io: Open migration files with O_DIRECT when possible. This @@ -1110,8 +1110,8 @@ # @unstable: Members @x-checkpoint-delay and # @x-vcpu-dirty-limit-period are experimental. # -# TODO: either fuse back into MigrationParameters, or make -# MigrationParameters members mandatory +# TODO: either fuse back into `MigrationParameters`, or make +# `MigrationParameters` members mandatory # # Since: 2.4 ## @@ -1304,11 +1304,11 @@ # @vcpu-dirty-limit: Dirtyrate limit (MB/s) during live migration. # Defaults to 1. (Since 8.1) # -# @mode: Migration mode. See description in @MigMode. Default is +# @mode: Migration mode. See description in `MigMode`. Default is # 'normal'. (Since 8.2) # # @zero-page-detection: Whether and how to detect zero pages. -# See description in @ZeroPageDetection. Default is 'multifd'. +# See description in `ZeroPageDetection`. Default is 'multifd'. # (since 9.0) # # @direct-io: Open migration files with O_DIRECT when possible. This @@ -1398,7 +1398,7 @@ # # Emitted when a migration event happens # -# @status: @MigrationStatus describing the current migration status. +# @status: `MigrationStatus` describing the current migration status. # # Since: 2.4 # @@ -1519,8 +1519,8 @@ # The reason for a COLO exit. # # @none: failover has never happened. This state does not occur in -# the COLO_EXIT event, and is only visible in the result of -# query-colo-status. +# the `COLO_EXIT` event, and is only visible in the result of +# `query-colo-status`. # # @request: COLO exit is due to an external request. # @@ -1775,8 +1775,8 @@ # list connected to a destination interface endpoint. # # @exit-on-error: Exit on incoming migration failure. Default true. -# When set to false, the failure triggers a MIGRATION event, and -# error details could be retrieved with query-migrate. +# When set to false, the failure triggers a :qapi:event:`MIGRATION` +# event, and error details could be retrieved with `query-migrate`. # (since 9.1) # # Since: 2.3 @@ -1788,7 +1788,7 @@ # already exposed above libvirt. # # 2. QEMU must be started with -incoming defer to allow -# migrate-incoming to be used. +# `migrate-incoming` to be used. # # 3. The uri format is the same as for -incoming # @@ -1841,7 +1841,7 @@ # devices of the VM are not saved by this command. # # @filename: the file to save the state of the devices to as binary -# data. See xen-save-devices-state.txt for a description of the +# data. See `xen-save-devices-state`.txt for a description of the # binary format. # # @live: Optional argument to ask QEMU to treat this command as part @@ -1882,7 +1882,7 @@ # devices of the VM are not loaded by this command. # # @filename: the file to load the state of the devices from as binary -# data. See xen-save-devices-state.txt for a description of the +# data. See `xen-save-devices-state`.txt for a description of the # binary format. # # Since: 2.7 @@ -1922,7 +1922,7 @@ ## # @ReplicationStatus: # -# The result format for 'query-xen-replication-status'. +# The result format for `query-xen-replication-status`. # # @error: true if an error happened, false if replication is normal. # @@ -1971,7 +1971,7 @@ ## # @COLOStatus: # -# The result format for 'query-colo-status'. +# The result format for `query-colo-status`. # # @mode: COLO running mode. If COLO is running, this field will # return 'primary' or 'secondary'. @@ -2095,7 +2095,7 @@ # @DirtyRateMeasureMode: # # Method used to measure dirty page rate. Differences between -# available methods are explained in @calc-dirty-rate. +# available methods are explained in `calc-dirty-rate`. # # @page-sampling: use page sampling # @@ -2163,7 +2163,7 @@ # @calc-dirty-rate: # # Start measuring dirty page rate of the VM. Results can be retrieved -# with @query-dirty-rate after measurements are completed. +# with `query-dirty-rate` after measurements are completed. # # Dirty page rate is the number of pages changed in a given time # period expressed in MiB/s. The following methods of calculation are @@ -2236,7 +2236,7 @@ ## # @query-dirty-rate: # -# Query results of the most recent invocation of @calc-dirty-rate. +# Query results of the most recent invocation of `calc-dirty-rate`. # # @calc-time-unit: time unit in which to report calculation time. # By default it is reported in seconds. (Since 8.2) @@ -2286,7 +2286,7 @@ # # Requires KVM with accelerator property "dirty-ring-size" set. A # virtual CPU's dirty page rate is a measure of its memory load. To -# observe dirty page rates, use @calc-dirty-rate. +# observe dirty page rates, use `calc-dirty-rate`. # # @cpu-index: index of a virtual CPU, default is all. # @@ -2311,8 +2311,8 @@ # Cancel the upper limit of dirty page rate for virtual CPUs. # # Cancel the dirty page limit for the vCPU which has been set with -# set-vcpu-dirty-limit command. Note that this command requires -# support from dirty ring, same as the "set-vcpu-dirty-limit". +# `set-vcpu-dirty-limit` command. Note that this command requires +# support from dirty ring, same as the `set-vcpu-dirty-limit`. # # @cpu-index: index of a virtual CPU, default is all. # @@ -2469,7 +2469,7 @@ # time it takes to load the snapshot. # # It is strongly recommended that @devices contain all writable block -# device nodes that can have changed since the original @snapshot-save +# device nodes that can have changed since the original `snapshot-save` # command execution. # # .. qmp-example:: From 4411a50e31157a1ee81147c2a6d6f1372a7bf6e8 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:39:56 -0400 Subject: [PATCH 2279/2760] qapi: add cross-references to net.json Signed-off-by: John Snow Message-ID: <20250711054005.60969-10-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/net.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qapi/net.json b/qapi/net.json index a266c6786e..1dda21fe61 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -657,7 +657,7 @@ # this to zero disables this function. This member is mutually # exclusive with @reconnect. (default: 0) (Since: 9.2) # -# Only SocketAddress types 'unix', 'inet' and 'fd' are supported. +# Only `SocketAddress` types 'unix', 'inet' and 'fd' are supported. # # Features: # @@ -682,7 +682,7 @@ # # @local: local address # -# Only SocketAddress types 'unix', 'inet' and 'fd' are supported. +# Only `SocketAddress` types 'unix', 'inet' and 'fd' are supported. # # If remote address is present and it's a multicast address, local # address is optional. Otherwise local address is required and remote @@ -892,7 +892,7 @@ ## # @NIC_RX_FILTER_CHANGED: # -# Emitted once until the 'query-rx-filter' command is executed, the +# Emitted once until the `query-rx-filter` command is executed, the # first event will always be emitted # # @name: net client name From b9abf7dd3f926e50029bd450e2d9c7cd2f1dde51 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:39:57 -0400 Subject: [PATCH 2280/2760] qapi: add cross-references to pci.json Signed-off-by: John Snow Message-ID: <20250711054005.60969-11-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/pci.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qapi/pci.json b/qapi/pci.json index 418ea4fc93..694c741e42 100644 --- a/qapi/pci.json +++ b/qapi/pci.json @@ -85,7 +85,7 @@ # # @bus: information about the bus the device resides on # -# @devices: a list of @PciDeviceInfo for each device on this bridge +# @devices: a list of `PciDeviceInfo` for each device on this bridge # # Since: 0.14 ## From 91581b49e1d1910c1e5af641e7b8afa95c1b0a53 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:39:58 -0400 Subject: [PATCH 2281/2760] qapi: add cross-references to QOM Signed-off-by: John Snow Message-ID: <20250711054005.60969-12-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/qdev.json | 4 ++-- qapi/qom.json | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/qapi/qdev.json b/qapi/qdev.json index 6441357e00..e14a0c9259 100644 --- a/qapi/qdev.json +++ b/qapi/qdev.json @@ -97,10 +97,10 @@ # from the guest. Hot removal is an operation that requires guest # cooperation. This command merely requests that the guest begin # the hot removal process. Completion of the device removal -# process is signaled with a DEVICE_DELETED event. Guest reset +# process is signaled with a `DEVICE_DELETED` event. Guest reset # will automatically complete removal for all devices. If a # guest-side error in the hot removal process is detected, the -# device will not be removed and a DEVICE_UNPLUG_GUEST_ERROR event +# device will not be removed and a `DEVICE_UNPLUG_GUEST_ERROR` event # is sent. Some errors cannot be detected. # # Since: 0.14 diff --git a/qapi/qom.json b/qapi/qom.json index aa09eddbf9..4e85baae47 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -52,7 +52,7 @@ # # List properties of a object given a path in the object model. # -# @path: the path within the object model. See @qom-get for a +# @path: the path within the object model. See `qom-get` for a # description of this parameter. # # Returns: a list that describe the properties of the object. @@ -129,12 +129,12 @@ # # Set a property value. # -# @path: see @qom-get for a description of this parameter +# @path: see `qom-get` for a description of this parameter # # @property: the property name to set # # @value: a value who's type is appropriate for the property type. -# See @qom-get for a description of type mapping. +# See `qom-get` for a description of type mapping. # # Since: 1.2 # @@ -153,7 +153,7 @@ ## # @ObjectTypeInfo: # -# This structure describes a search result from @qom-list-types +# This structure describes a search result from `qom-list-types` # # @name: the type name found in the search # @@ -193,6 +193,7 @@ # # @typename: the type name of an object # +# # .. note:: Objects can create properties at runtime, for example to # describe links between different devices and/or objects. These # properties are not included in the output of this command. @@ -787,7 +788,7 @@ # # Properties for x-remote-object objects. # -# @fd: file descriptor name previously passed via 'getfd' command +# @fd: file descriptor name previously passed via `getfd` command # # @devid: the id of the device to be associated with the file # descriptor @@ -816,7 +817,7 @@ # # Properties for iommufd objects. # -# @fd: file descriptor name previously passed via 'getfd' command, +# @fd: file descriptor name previously passed via `getfd` command, # which represents a pre-opened /dev/iommu. This allows the # iommufd object to be shared across several subsystems (VFIO, # VDPA, ...), and the file descriptor to be shared with other From 551c00b8fac1f0532ba10bac05a5da7b7b508f71 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:39:59 -0400 Subject: [PATCH 2282/2760] qapi: add cross-references to replay.json Signed-off-by: John Snow Message-ID: <20250711054005.60969-13-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/replay.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/qapi/replay.json b/qapi/replay.json index e46c5c1d3f..ccf84da68e 100644 --- a/qapi/replay.json +++ b/qapi/replay.json @@ -49,8 +49,8 @@ # @query-replay: # # Retrieve the record/replay information. It includes current -# instruction count which may be used for @replay-break and -# @replay-seek commands. +# instruction count which may be used for `replay-break` and +# `replay-seek` commands. # # Returns: record/replay information. # @@ -72,7 +72,7 @@ # breakpoint. When breakpoint is set, any prior one is removed. The # breakpoint may be set only in replay mode and only "in the future", # i.e. at instruction counts greater than the current one. The -# current instruction count can be observed with @query-replay. +# current instruction count can be observed with `query-replay`. # # @icount: instruction count to stop at # @@ -88,7 +88,7 @@ ## # @replay-delete-break: # -# Remove replay breakpoint which was set with @replay-break. The +# Remove replay breakpoint which was set with `replay-break`. The # command is ignored when there are no replay breakpoints. # # Since: 5.2 @@ -108,7 +108,7 @@ # snapshot and replays the execution to find the desired instruction. # When there is no preceding snapshot or the execution is not # replayed, then the command fails. Instruction count can be obtained -# with the @query-replay command. +# with the `query-replay` command. # # @icount: target instruction count # From 57d2ad7d2f534d510c7afb44d574d6313a0ab91e Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:40:00 -0400 Subject: [PATCH 2283/2760] qapi: add cross-references to run-state.json Signed-off-by: John Snow Message-ID: <20250711054005.60969-14-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/run-state.json | 46 ++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/qapi/run-state.json b/qapi/run-state.json index 76f14569ff..54ba5c9a3f 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -70,9 +70,9 @@ # # @host-error: An error prevents further use of guest # -# @host-qmp-quit: Reaction to the QMP command 'quit' +# @host-qmp-quit: Reaction to the QMP command `quit` # -# @host-qmp-system-reset: Reaction to the QMP command 'system_reset' +# @host-qmp-system-reset: Reaction to the QMP command `system_reset` # # @host-signal: Reaction to a signal, such as SIGINT # @@ -108,7 +108,7 @@ # # @running: true if all VCPUs are runnable, false if not runnable # -# @status: the virtual machine @RunState +# @status: the virtual machine `RunState` # # Since: 0.14 ## @@ -143,12 +143,12 @@ # hardware-specific action) rather than a host request (such as # sending QEMU a SIGINT). (since 2.10) # -# @reason: The @ShutdownCause which resulted in the SHUTDOWN. +# @reason: The `ShutdownCause` which resulted in the `SHUTDOWN`. # (since 4.0) # # .. note:: If the command-line option ``-no-shutdown`` has been -# specified, QEMU will not exit, and a STOP event will eventually -# follow the SHUTDOWN event. +# specified, QEMU will not exit, and a `STOP` event will eventually +# follow the `SHUTDOWN` event. # # Since: 0.12 # @@ -183,9 +183,9 @@ # @guest: If true, the reset was triggered by a guest request (such as # a guest-initiated ACPI reboot request or other hardware-specific # action) rather than a host request (such as the QMP command -# system_reset). (since 2.10) +# `system_reset`). (since 2.10) # -# @reason: The @ShutdownCause of the RESET. (since 4.0) +# @reason: The `ShutdownCause` of the `RESET`. (since 4.0) # # Since: 0.12 # @@ -247,7 +247,7 @@ # saved on disk, for example, S4 state, which is sometimes called # hibernate state # -# .. note:: QEMU shuts down (similar to event @SHUTDOWN) when entering +# .. note:: QEMU shuts down (similar to event `SHUTDOWN`) when entering # this state. # # Since: 1.2 @@ -281,8 +281,8 @@ # # @action: action that has been taken # -# .. note:: If action is "reset", "shutdown", or "pause" the WATCHDOG -# event is followed respectively by the RESET, SHUTDOWN, or STOP +# .. note:: If action is "reset", "shutdown", or "pause" the `WATCHDOG` +# event is followed respectively by the `RESET`, `SHUTDOWN`, or `STOP` # events. # # .. note:: This event is rate-limited. @@ -378,7 +378,7 @@ # # Set watchdog action. # -# @action: @WatchdogAction action taken when watchdog timer expires. +# @action: `WatchdogAction` action taken when watchdog timer expires. # # Since: 2.11 # @@ -396,13 +396,13 @@ # Set the actions that will be taken by the emulator in response to # guest events. # -# @reboot: @RebootAction action taken on guest reboot. +# @reboot: `RebootAction` action taken on guest reboot. # -# @shutdown: @ShutdownAction action taken on guest shutdown. +# @shutdown: `ShutdownAction` action taken on guest shutdown. # -# @panic: @PanicAction action taken on guest panic. +# @panic: `PanicAction` action taken on guest panic. # -# @watchdog: @WatchdogAction action taken when watchdog timer expires. +# @watchdog: `WatchdogAction` action taken when watchdog timer expires. # # Since: 6.0 # @@ -529,20 +529,20 @@ # # Hyper-V specific guest panic information (HV crash MSRs) # -# @arg1: for Windows, STOP code for the guest crash. For Linux, +# @arg1: for Windows, `STOP` code for the guest crash. For Linux, # an error code. # -# @arg2: for Windows, first argument of the STOP. For Linux, the +# @arg2: for Windows, first argument of the `STOP`. For Linux, the # guest OS ID, which has the kernel version in bits 16-47 and # 0x8100 in bits 48-63. # -# @arg3: for Windows, second argument of the STOP. For Linux, the +# @arg3: for Windows, second argument of the `STOP`. For Linux, the # program counter of the guest. # -# @arg4: for Windows, third argument of the STOP. For Linux, the +# @arg4: for Windows, third argument of the `STOP`. For Linux, the # RAX register (x86) or the stack pointer (aarch64) of the guest. # -# @arg5: for Windows, fourth argument of the STOP. For x86 Linux, the +# @arg5: for Windows, fourth argument of the `STOP`. For x86 Linux, the # stack pointer of the guest. # # Since: 2.9 @@ -630,11 +630,11 @@ # # Emitted when a memory failure occurs on host side. # -# @recipient: recipient is defined as @MemoryFailureRecipient. +# @recipient: recipient is defined as `MemoryFailureRecipient`. # # @action: action that has been taken. # -# @flags: flags for MemoryFailureAction. +# @flags: flags for `MemoryFailureAction`. # # Since: 5.2 # From bfdfd96388fed2c23b2a42c32c5dd1a96d437c20 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:40:01 -0400 Subject: [PATCH 2284/2760] qapi: add cross-references to sockets.json Signed-off-by: John Snow Reviewed-by: Eric Blake Message-ID: <20250711054005.60969-15-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/sockets.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qapi/sockets.json b/qapi/sockets.json index b5f4a8fecd..82046b0b3a 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -211,14 +211,14 @@ 'unix': 'UnixSocketAddressWrapper', 'vsock': 'VsockSocketAddressWrapper', 'fd': 'FdSocketAddressWrapper' } } -# Note: This type is deprecated in favor of SocketAddress. The -# difference between SocketAddressLegacy and SocketAddress is that the +# Note: This type is deprecated in favor of `SocketAddress`. The +# difference between `SocketAddressLegacy` and `SocketAddress` is that the # latter has fewer ``{}`` on the wire. ## # @SocketAddressType: # -# Available SocketAddress types +# Available `SocketAddress` types # # @inet: Internet address # From 64ebc732e1a1bc3a76b7412eca9f0fc6b0c235d1 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:40:02 -0400 Subject: [PATCH 2285/2760] qapi: add cross-references to ui.json Signed-off-by: John Snow Message-ID: <20250711054005.60969-16-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/ui.json | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/qapi/ui.json b/qapi/ui.json index b48266c458..c672cf10f9 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -41,7 +41,7 @@ ## # @SetPasswordOptions: # -# Options for set_password. +# Options for `set_password`. # # @protocol: # - 'vnc' to modify the VNC server password @@ -65,7 +65,7 @@ ## # @SetPasswordOptionsVnc: # -# Options for set_password specific to the VNC protocol. +# Options for `set_password` specific to the VNC protocol. # # @display: The id of the display where the password should be # changed. Defaults to the first. @@ -96,7 +96,7 @@ ## # @ExpirePasswordOptions: # -# General options for expire_password. +# General options for `expire_password`. # # @protocol: # - 'vnc' to modify the VNC server expiration @@ -126,7 +126,7 @@ ## # @ExpirePasswordOptionsVnc: # -# Options for expire_password specific to the VNC protocol. +# Options for `expire_password` specific to the VNC protocol. # # @display: The id of the display where the expiration should be # changed. Defaults to the first. @@ -185,7 +185,7 @@ # the head can only be specified in conjunction with the device # ID. (Since 2.12) # -# @format: image format for screendump. (default: ppm) (Since 7.1) +# @format: image format for `screendump`. (default: ppm) (Since 7.1) # # Since: 0.14 # @@ -313,7 +313,7 @@ # unknown if spice server doesn't provide this information. # (since: 1.1) # -# @channels: a list of @SpiceChannel for each active spice channel +# @channels: a list of `SpiceChannel` for each active spice channel # # Since: 0.14 ## @@ -563,7 +563,7 @@ # - 'vencrypt+x509+sasl' if VEncrypt is used with x509 and SASL # auth # -# @clients: a list of @VncClientInfo of all currently connected +# @clients: a list of `VncClientInfo` of all currently connected # clients # # Since: 0.14 @@ -626,12 +626,12 @@ # # @id: vnc server name. # -# @server: A list of @VncBasincInfo describing all listening sockets. +# @server: A list of `VncBasicInfo` describing all listening sockets. # The list can be empty (in case the vnc server is disabled). It # also may have multiple entries: normal + websocket, possibly # also ipv4 + ipv6 in the future. # -# @clients: A list of @VncClientInfo of all currently connected +# @clients: A list of `VncClientInfo` of all currently connected # clients. The list can be empty, for obvious reasons. # # @auth: The current authentication type used by the non-websockets @@ -852,7 +852,7 @@ # # An enumeration of key name. # -# This is used by the @send-key command. +# This is used by the `send-key` command. # # @unmapped: since 2.0 # @@ -1023,10 +1023,10 @@ # # Send keys to guest. # -# @keys: An array of @KeyValue elements. All @KeyValues in this array -# are simultaneously sent to the guest. A @KeyValue.number value -# is sent directly to the guest, while @KeyValue.qcode must be a -# valid @QKeyCode value +# @keys: An array of `KeyValue` elements. All @KeyValues in this array +# are simultaneously sent to the guest. A `KeyValue`.number value +# is sent directly to the guest, while `KeyValue`.qcode must be a +# valid `QKeyCode` value # # @hold-time: time to delay key up events, milliseconds. Defaults to # 100 @@ -1263,7 +1263,7 @@ # @head: head to send event(s) to, in case the display device supports # multiple scanouts. # -# @events: List of InputEvent union. +# @events: List of `InputEvent` union. # # Since: 2.6 # @@ -1367,7 +1367,7 @@ # first available node on the host. # # @p2p: Whether to use peer-to-peer connections (accepted through -# @add_client). +# `add_client`). # # @audiodev: Use the specified DBus audiodev to export audio. # @@ -1526,7 +1526,7 @@ # # Display (user interface) options. # -# @type: Which DisplayType QEMU should use. +# @type: Which `DisplayType` QEMU should use. # # @full-screen: Start user interface in fullscreen mode # (default: off). From 6e202378d1533d87f77c73ffb22fbb784d08f545 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:40:03 -0400 Subject: [PATCH 2286/2760] qapi: add cross-references to virtio.json Signed-off-by: John Snow Message-ID: <20250711054005.60969-17-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/virtio.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qapi/virtio.json b/qapi/virtio.json index 2e23512085..b29c850eaf 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -137,7 +137,7 @@ # @num-vqs: VirtIODevice virtqueue count. This is the number of # active virtqueues being used by the VirtIODevice. # -# @status: VirtIODevice configuration status (VirtioDeviceStatus) +# @status: VirtIODevice configuration status (`VirtioDeviceStatus`) # # @isr: VirtIODevice ISR # @@ -579,7 +579,7 @@ # .. qmp-example:: # :annotated: # -# Get VirtQueueStatus for virtio-vsock (vhost-vsock running) +# Get `VirtQueueStatus` for virtio-vsock (vhost-vsock running) # :: # # -> { "execute": "x-query-virtio-queue-status", @@ -606,7 +606,7 @@ # .. qmp-example:: # :annotated: # -# Get VirtQueueStatus for virtio-serial (no vhost) +# Get `VirtQueueStatus` for virtio-serial (no vhost) # :: # # -> { "execute": "x-query-virtio-queue-status", @@ -818,7 +818,7 @@ # # @index: Index of the element in the queue # -# @descs: List of descriptors (VirtioRingDesc) +# @descs: List of descriptors (`VirtioRingDesc`) # # @avail: VRingAvail info # From 13f53cc902b7fa6d1864f7c13c3b3c755d1001a5 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:40:04 -0400 Subject: [PATCH 2287/2760] qapi: add cross-references to yank.json Signed-off-by: John Snow Acked-by: Lukas Straub Message-ID: <20250711054005.60969-18-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/yank.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/qapi/yank.json b/qapi/yank.json index c14de5229e..f3cd5c15d6 100644 --- a/qapi/yank.json +++ b/qapi/yank.json @@ -11,7 +11,7 @@ ## # @YankInstanceType: # -# An enumeration of yank instance types. See @YankInstance for more +# An enumeration of yank instance types. See `YankInstance` for more # information. # # Since: 6.0 @@ -22,7 +22,7 @@ ## # @YankInstanceBlockNode: # -# Specifies which block graph node to yank. See @YankInstance for +# Specifies which block graph node to yank. See `YankInstance` for # more information. # # @node-name: the name of the block graph node @@ -35,7 +35,7 @@ ## # @YankInstanceChardev: # -# Specifies which character device to yank. See @YankInstance for +# Specifies which character device to yank. See `YankInstance` for # more information. # # @id: the chardev's ID @@ -48,7 +48,7 @@ ## # @YankInstance: # -# A yank instance can be yanked with the @yank qmp command to recover +# A yank instance can be yanked with the `yank` qmp command to recover # from a hanging QEMU. # # @type: yank instance type @@ -59,9 +59,9 @@ # nbd server without attempting to reconnect. # - socket chardev: Yanking it will shut down the connected socket. # - migration: Yanking it will shut down all migration connections. -# Unlike @migrate_cancel, it will not notify the migration process, +# Unlike `migrate_cancel`, it will not notify the migration process, # so migration will go into @failed state, instead of @cancelled -# state. @yank should be used to recover from hangs. +# state. `yank` should be used to recover from hangs. # # Since: 6.0 ## @@ -76,7 +76,7 @@ # @yank: # # Try to recover from hanging QEMU by yanking the specified instances. -# See @YankInstance for more information. +# See `YankInstance` for more information. # # @instances: the instances to be yanked # @@ -102,7 +102,7 @@ ## # @query-yank: # -# Query yank instances. See @YankInstance for more information. +# Query yank instances. See `YankInstance` for more information. # # TODO: This line is a hack to separate the example from the body # From 68c0156de2581b69e62b2f04b58a89bdc762e603 Mon Sep 17 00:00:00 2001 From: John Snow Date: Fri, 11 Jul 2025 01:40:05 -0400 Subject: [PATCH 2288/2760] qapi: add cross-references to misc modules These modules don't have specific maintainers, so they're lumped in together here as miscellaneous. Signed-off-by: John Snow Message-ID: <20250711054005.60969-19-jsnow@redhat.com> Reviewed-by: Markus Armbruster Signed-off-by: Markus Armbruster --- qapi/control.json | 2 +- qapi/ebpf.json | 2 +- qapi/introspect.json | 24 ++++++++++++------------ qapi/misc-arm.json | 2 +- qapi/misc-i386.json | 2 +- qapi/misc.json | 12 ++++++------ qapi/stats.json | 8 ++++---- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/qapi/control.json b/qapi/control.json index 931a860b30..9a5302193d 100644 --- a/qapi/control.json +++ b/qapi/control.json @@ -13,7 +13,7 @@ # # Enable QMP capabilities. # -# @enable: An optional list of QMPCapability values to enable. The +# @enable: An optional list of `QMPCapability` values to enable. The # client must not enable any capability that is not mentioned in # the QMP greeting message. If the field is not provided, it # means no QMP capabilities will be enabled. (since 2.12) diff --git a/qapi/ebpf.json b/qapi/ebpf.json index d45054e666..f0257955fa 100644 --- a/qapi/ebpf.json +++ b/qapi/ebpf.json @@ -34,7 +34,7 @@ ## # @EbpfProgramID: # -# The eBPF programs that can be gotten with request-ebpf. +# The eBPF programs that can be gotten with `request-ebpf`. # # @rss: Receive side scaling, technology that allows steering traffic # between queues by calculation hash. Users may set up diff --git a/qapi/introspect.json b/qapi/introspect.json index be8511b067..53100714a8 100644 --- a/qapi/introspect.json +++ b/qapi/introspect.json @@ -18,11 +18,11 @@ ## # @query-qmp-schema: # -# Command query-qmp-schema exposes the QMP wire ABI as an array of -# SchemaInfo. This lets QMP clients figure out what commands and +# Command `query-qmp-schema` exposes the QMP wire ABI as an array of +# `SchemaInfo`. This lets QMP clients figure out what commands and # events are available in this QEMU, and their parameters and results. # -# However, the SchemaInfo can't reflect all the rules and restrictions +# However, the `SchemaInfo` can't reflect all the rules and restrictions # that apply to QMP. It's interface introspection (figuring out # what's there), not interface specification. The specification is in # the QAPI schema. @@ -56,7 +56,7 @@ ## # @SchemaMetaType: # -# This is a @SchemaInfo's meta type, i.e. the kind of entity it +# This is a `SchemaInfo`'s meta type, i.e. the kind of entity it # describes. # # @builtin: a predefined type such as 'int' or 'bool'. @@ -82,7 +82,7 @@ ## # @SchemaInfo: # -# @name: the entity's name, inherited from @base. The SchemaInfo is +# @name: the entity's name, inherited from @base. The `SchemaInfo` is # always referenced by this name. Commands and events have the # name defined in the QAPI schema. Unlike command and event # names, type names are not part of the wire ABI. Consequently, @@ -113,7 +113,7 @@ ## # @SchemaInfoBuiltin: # -# Additional SchemaInfo members for meta-type 'builtin'. +# Additional `SchemaInfo` members for meta-type 'builtin'. # # @json-type: the JSON type used for this type on the wire. # @@ -154,7 +154,7 @@ ## # @SchemaInfoEnum: # -# Additional SchemaInfo members for meta-type 'enum'. +# Additional `SchemaInfo` members for meta-type 'enum'. # # @members: the enum type's members, in no particular order. # (since 6.2) @@ -194,7 +194,7 @@ ## # @SchemaInfoArray: # -# Additional SchemaInfo members for meta-type 'array'. +# Additional `SchemaInfo` members for meta-type 'array'. # # @element-type: the array type's element type. # @@ -208,7 +208,7 @@ ## # @SchemaInfoObject: # -# Additional SchemaInfo members for meta-type 'object'. +# Additional `SchemaInfo` members for meta-type 'object'. # # @members: the object type's (non-variant) members, in no particular # order. @@ -273,7 +273,7 @@ ## # @SchemaInfoAlternate: # -# Additional SchemaInfo members for meta-type 'alternate'. +# Additional `SchemaInfo` members for meta-type 'alternate'. # # @members: the alternate type's members, in no particular order. The # members' wire encoding is distinct, see @@ -301,7 +301,7 @@ ## # @SchemaInfoCommand: # -# Additional SchemaInfo members for meta-type 'command'. +# Additional `SchemaInfo` members for meta-type 'command'. # # @arg-type: the name of the object type that provides the command's # parameters. @@ -323,7 +323,7 @@ ## # @SchemaInfoEvent: # -# Additional SchemaInfo members for meta-type 'event'. +# Additional `SchemaInfo` members for meta-type 'event'. # # @arg-type: the name of the object type that provides the event's # parameters. diff --git a/qapi/misc-arm.json b/qapi/misc-arm.json index f9dd743b52..f921d740f1 100644 --- a/qapi/misc-arm.json +++ b/qapi/misc-arm.json @@ -30,7 +30,7 @@ ## # @query-gic-capabilities: # -# It will return a list of GICCapability objects that describe its +# It will return a list of `GICCapability` objects that describe its # capability bits. # # On non-ARM targets this command will report an error as the GIC diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json index 282bd3627b..c8c91a241c 100644 --- a/qapi/misc-i386.json +++ b/qapi/misc-i386.json @@ -26,7 +26,7 @@ ## # @SevState: # -# An enumeration of SEV state information used during @query-sev. +# An enumeration of SEV state information used during `query-sev`. # # @uninit: The guest is uninitialized. # diff --git a/qapi/misc.json b/qapi/misc.json index 7dbc29dbd6..28c641fe2f 100644 --- a/qapi/misc.json +++ b/qapi/misc.json @@ -23,7 +23,7 @@ # "@dbus-display" or the name of a character device (e.g. from # -chardev id=XXXX) # -# @fdname: file descriptor name previously passed via 'getfd' command +# @fdname: file descriptor name previously passed via `getfd` command # # @skipauth: whether to skip authentication. Only applies to "vnc" # and "spice" protocols @@ -228,7 +228,7 @@ # Known limitations: # # * This command is stateless, this means that commands that depend -# on state information (such as getfd) might not work. +# on state information (such as `getfd`) might not work. # # * Commands that prompt the user for data don't currently work. # @@ -255,7 +255,7 @@ # .. note:: If @fdname already exists, the file descriptor assigned to # it will be closed and replaced by the received file descriptor. # -# The 'closefd' command can be used to explicitly close the file +# The `closefd` command can be used to explicitly close the file # descriptor when it is no longer needed. # # .. qmp-example:: @@ -282,7 +282,7 @@ # .. note:: If @fdname already exists, the file descriptor assigned to # it will be closed and replaced by the received file descriptor. # -# The 'closefd' command can be used to explicitly close the file +# The `closefd` command can be used to explicitly close the file # descriptor when it is no longer needed. # # .. qmp-example:: @@ -475,7 +475,7 @@ # # @name: parameter name # -# @type: parameter @CommandLineParameterType +# @type: parameter `CommandLineParameterType` # # @help: human readable text string, not suitable for parsing. # @@ -497,7 +497,7 @@ # # @option: option name # -# @parameters: an array of @CommandLineParameterInfo +# @parameters: an array of `CommandLineParameterInfo` # # Since: 1.5 ## diff --git a/qapi/stats.json b/qapi/stats.json index b63a7369dd..151ac43c48 100644 --- a/qapi/stats.json +++ b/qapi/stats.json @@ -89,7 +89,7 @@ # @StatsRequest: # # Indicates a set of statistics that should be returned by -# query-stats. +# `query-stats`. # # @provider: provider for which to return statistics. # @@ -114,7 +114,7 @@ ## # @StatsFilter: # -# The arguments to the query-stats command; specifies a target for +# The arguments to the `query-stats` command; specifies a target for # which to request statistics and optionally the required subset of # information for that target. # @@ -185,7 +185,7 @@ # Return runtime-collected statistics for objects such as the VM or # its vCPUs. # -# The arguments are a StatsFilter and specify the provider and objects +# The arguments are a `StatsFilter` and specify the provider and objects # to return statistics about. # # Returns: a list of statistics, one for each provider and object @@ -205,7 +205,7 @@ # # @name: name of the statistic; each element of the schema is uniquely # identified by a target, a provider (both available in -# @StatsSchema) and the name. +# `StatsSchema`) and the name. # # @type: kind of statistic. # From 260f826cf8f2fe54f3cf4de541d761cf616e15ea Mon Sep 17 00:00:00 2001 From: Daniil Tatianin Date: Tue, 10 Jun 2025 00:25:45 +0300 Subject: [PATCH 2289/2760] softmmu/runstate: add a way to detect force shutdowns This can be useful for devices that might take too long to shut down gracefully, but may have a way to shutdown quickly otherwise if needed or explicitly requested by a force shutdown. For now we only consider SIGTERM or the QMP quit() command a force shutdown, since those bypass the guest entirely and are equivalent to pulling the power plug. Signed-off-by: Daniil Tatianin Message-Id: <20250609212547.2859224-2-d-tatianin@yandex-team.ru> Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/system/runstate.h | 1 + system/runstate.c | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/include/system/runstate.h b/include/system/runstate.h index fdd5c4a517..b406a3960e 100644 --- a/include/system/runstate.h +++ b/include/system/runstate.h @@ -107,6 +107,7 @@ void qemu_system_vmstop_request(RunState reason); void qemu_system_vmstop_request_prepare(void); bool qemu_vmstop_requested(RunState *r); ShutdownCause qemu_shutdown_requested_get(void); +bool qemu_force_shutdown_requested(void); ShutdownCause qemu_reset_requested_get(void); void qemu_system_killed(int signal, pid_t pid); void qemu_system_reset(ShutdownCause reason); diff --git a/system/runstate.c b/system/runstate.c index 38900c935a..e18eb8cb0c 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -437,6 +437,7 @@ static ShutdownCause reset_requested; static ShutdownCause shutdown_requested; static int shutdown_exit_code = EXIT_SUCCESS; static int shutdown_signal; +static bool force_shutdown; static pid_t shutdown_pid; static int powerdown_requested; static int debug_requested; @@ -457,6 +458,11 @@ ShutdownCause qemu_shutdown_requested_get(void) return shutdown_requested; } +bool qemu_force_shutdown_requested(void) +{ + return force_shutdown; +} + ShutdownCause qemu_reset_requested_get(void) { return reset_requested; @@ -805,6 +811,7 @@ void qemu_system_killed(int signal, pid_t pid) * we are in a signal handler. */ shutdown_requested = SHUTDOWN_CAUSE_HOST_SIGNAL; + force_shutdown = true; qemu_notify_event(); } @@ -820,6 +827,9 @@ void qemu_system_shutdown_request(ShutdownCause reason) trace_qemu_system_shutdown_request(reason); replay_shutdown_request(reason); shutdown_requested = reason; + if (reason == SHUTDOWN_CAUSE_HOST_QMP_QUIT) { + force_shutdown = true; + } qemu_notify_event(); } From 2f527fff460a2c67fd37298dbd7fe42fffdb738b Mon Sep 17 00:00:00 2001 From: Daniil Tatianin Date: Tue, 10 Jun 2025 00:25:46 +0300 Subject: [PATCH 2290/2760] vhost: add a helper for force stopping a device This adds an ability to skip GET_VRING_BASE during device stop entirely, and thus the expensive drain operation that this call entails as well, which may be useful during a non-graceful shutdown in case the guest operating system hangs or refuses to react to a previously requested ACPI shutdown for whatever reason. Signed-off-by: Daniil Tatianin Message-Id: <20250609212547.2859224-3-d-tatianin@yandex-team.ru> Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 52 +++++++++++++++++++++++++++++---------- include/hw/virtio/vhost.h | 15 +++++++++++ 2 files changed, 54 insertions(+), 13 deletions(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index c87861b31f..c30ea1156e 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1354,25 +1354,30 @@ int vhost_virtqueue_start(struct vhost_dev *dev, return r; } -int vhost_virtqueue_stop(struct vhost_dev *dev, - struct VirtIODevice *vdev, - struct vhost_virtqueue *vq, - unsigned idx) +static int do_vhost_virtqueue_stop(struct vhost_dev *dev, + struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, + unsigned idx, bool force) { int vhost_vq_index = dev->vhost_ops->vhost_get_vq_index(dev, idx); struct vhost_vring_state state = { .index = vhost_vq_index, }; - int r; + int r = 0; if (virtio_queue_get_desc_addr(vdev, idx) == 0) { /* Don't stop the virtqueue which might have not been started */ return 0; } - r = dev->vhost_ops->vhost_get_vring_base(dev, &state); - if (r < 0) { - VHOST_OPS_DEBUG(r, "vhost VQ %u ring restore failed: %d", idx, r); + if (!force) { + r = dev->vhost_ops->vhost_get_vring_base(dev, &state); + if (r < 0) { + VHOST_OPS_DEBUG(r, "vhost VQ %u ring restore failed: %d", idx, r); + } + } + + if (r < 0 || force) { /* Connection to the backend is broken, so let's sync internal * last avail idx to the device used idx. */ @@ -1401,6 +1406,14 @@ int vhost_virtqueue_stop(struct vhost_dev *dev, return r; } +int vhost_virtqueue_stop(struct vhost_dev *dev, + struct VirtIODevice *vdev, + struct vhost_virtqueue *vq, + unsigned idx) +{ + return do_vhost_virtqueue_stop(dev, vdev, vq, idx, false); +} + static int vhost_virtqueue_set_busyloop_timeout(struct vhost_dev *dev, int n, uint32_t timeout) { @@ -2119,7 +2132,8 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) } /* Host notifiers must be enabled at this point. */ -int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) +static int do_vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, + bool vrings, bool force) { int i; int rc = 0; @@ -2141,10 +2155,11 @@ int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) vhost_dev_set_vring_enable(hdev, false); } for (i = 0; i < hdev->nvqs; ++i) { - rc |= vhost_virtqueue_stop(hdev, - vdev, - hdev->vqs + i, - hdev->vq_index + i); + rc |= do_vhost_virtqueue_stop(hdev, + vdev, + hdev->vqs + i, + hdev->vq_index + i, + force); } if (hdev->vhost_ops->vhost_reset_status) { hdev->vhost_ops->vhost_reset_status(hdev); @@ -2164,6 +2179,17 @@ int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) return rc; } +int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings) +{ + return do_vhost_dev_stop(hdev, vdev, vrings, false); +} + +int vhost_dev_force_stop(struct vhost_dev *hdev, VirtIODevice *vdev, + bool vrings) +{ + return do_vhost_dev_stop(hdev, vdev, vrings, true); +} + int vhost_net_set_backend(struct vhost_dev *hdev, struct vhost_vring_file *file) { diff --git a/include/hw/virtio/vhost.h b/include/hw/virtio/vhost.h index 38800a7156..eb3dd7616b 100644 --- a/include/hw/virtio/vhost.h +++ b/include/hw/virtio/vhost.h @@ -237,6 +237,21 @@ int vhost_dev_start(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); */ int vhost_dev_stop(struct vhost_dev *hdev, VirtIODevice *vdev, bool vrings); +/** + * vhost_dev_force_stop() - force stop the vhost device + * @hdev: common vhost_dev structure + * @vdev: the VirtIODevice structure + * @vrings: true to have vrings disabled in this call + * + * Force stop the vhost device. After the device is stopped the notifiers + * can be disabled (@vhost_dev_disable_notifiers) and the device can + * be torn down (@vhost_dev_cleanup). Unlike @vhost_dev_stop, this doesn't + * attempt to flush in-flight backend requests by skipping GET_VRING_BASE + * entirely. + */ +int vhost_dev_force_stop(struct vhost_dev *hdev, VirtIODevice *vdev, + bool vrings); + /** * DOC: vhost device configuration handling * From 07fde5901b5254d3b9706df22dbb16cfacf966d5 Mon Sep 17 00:00:00 2001 From: Daniil Tatianin Date: Tue, 10 Jun 2025 00:25:47 +0300 Subject: [PATCH 2291/2760] vhost-user-blk: add an option to skip GET_VRING_BASE for force shutdown If we have a server running disk requests that is for whatever reason hanging or not able to process any more IO requests but still has some in-flight requests previously issued by the guest OS, QEMU will still try to drain the vring before shutting down even if it was explicitly asked to do a "force shutdown" via SIGTERM or QMP quit. This is not useful since the guest is no longer running at this point since it was killed by QEMU earlier in the process. At this point, we don't care about whatever in-flight IO it might have pending, we just want QEMU to shut down. Add an option called "skip-get-vring-base-on-force-shutdown" to allow SIGTERM/QMP quit() to actually act like a "force shutdown" at least for vhost-user-blk devices since those require the drain operation to shut down gracefully unlike, for example, network devices. Signed-off-by: Daniil Tatianin Message-Id: <20250609212547.2859224-4-d-tatianin@yandex-team.ru> Acked-by: Raphael Norwitz Reviewed-by: Vladimir Sementsov-Ogievskiy Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/vhost-user-blk.c | 9 ++++++++- include/hw/virtio/vhost-user-blk.h | 2 ++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/block/vhost-user-blk.c b/hw/block/vhost-user-blk.c index 0eebbcd80d..c0cc5f6942 100644 --- a/hw/block/vhost-user-blk.c +++ b/hw/block/vhost-user-blk.c @@ -210,6 +210,7 @@ static int vhost_user_blk_stop(VirtIODevice *vdev) BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); int ret; + bool force_stop = false; if (!s->started_vu) { return 0; @@ -220,7 +221,11 @@ static int vhost_user_blk_stop(VirtIODevice *vdev) return 0; } - ret = vhost_dev_stop(&s->dev, vdev, true); + force_stop = s->skip_get_vring_base_on_force_shutdown && + qemu_force_shutdown_requested(); + + ret = force_stop ? vhost_dev_force_stop(&s->dev, vdev, true) : + vhost_dev_stop(&s->dev, vdev, true); if (k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false) < 0) { error_report("vhost guest notifier cleanup failed: %d", ret); @@ -584,6 +589,8 @@ static const Property vhost_user_blk_properties[] = { VIRTIO_BLK_F_DISCARD, true), DEFINE_PROP_BIT64("write-zeroes", VHostUserBlk, parent_obj.host_features, VIRTIO_BLK_F_WRITE_ZEROES, true), + DEFINE_PROP_BOOL("skip-get-vring-base-on-force-shutdown", VHostUserBlk, + skip_get_vring_base_on_force_shutdown, false), }; static void vhost_user_blk_class_init(ObjectClass *klass, const void *data) diff --git a/include/hw/virtio/vhost-user-blk.h b/include/hw/virtio/vhost-user-blk.h index ea085ee1ed..a10f785672 100644 --- a/include/hw/virtio/vhost-user-blk.h +++ b/include/hw/virtio/vhost-user-blk.h @@ -50,6 +50,8 @@ struct VHostUserBlk { bool connected; /* vhost_user_blk_start/vhost_user_blk_stop */ bool started_vu; + + bool skip_get_vring_base_on_force_shutdown; }; #endif From f3bc2c3f33cc875a3b7e5349aa4141ea6789a01d Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 12 Jun 2025 17:03:17 +0800 Subject: [PATCH 2292/2760] tests/acpi: Add empty ACPI data files for LoongArch Add empty acpi table for LoongArch virt machine, it is only empty file and there is no data in these files. Signed-off-by: Bibo Mao Message-Id: <20250612090321.3416594-2-maobibo@loongson.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/loongarch64/virt/APIC | 0 tests/data/acpi/loongarch64/virt/DSDT | 0 tests/data/acpi/loongarch64/virt/FACP | 0 tests/data/acpi/loongarch64/virt/MCFG | 0 tests/data/acpi/loongarch64/virt/PPTT | 0 tests/data/acpi/loongarch64/virt/SLIT | 0 tests/data/acpi/loongarch64/virt/SPCR | 0 tests/data/acpi/loongarch64/virt/SRAT | 0 tests/qtest/bios-tables-test-allowed-diff.h | 8 ++++++++ 9 files changed, 8 insertions(+) create mode 100644 tests/data/acpi/loongarch64/virt/APIC create mode 100644 tests/data/acpi/loongarch64/virt/DSDT create mode 100644 tests/data/acpi/loongarch64/virt/FACP create mode 100644 tests/data/acpi/loongarch64/virt/MCFG create mode 100644 tests/data/acpi/loongarch64/virt/PPTT create mode 100644 tests/data/acpi/loongarch64/virt/SLIT create mode 100644 tests/data/acpi/loongarch64/virt/SPCR create mode 100644 tests/data/acpi/loongarch64/virt/SRAT diff --git a/tests/data/acpi/loongarch64/virt/APIC b/tests/data/acpi/loongarch64/virt/APIC new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/loongarch64/virt/DSDT b/tests/data/acpi/loongarch64/virt/DSDT new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/loongarch64/virt/FACP b/tests/data/acpi/loongarch64/virt/FACP new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/loongarch64/virt/MCFG b/tests/data/acpi/loongarch64/virt/MCFG new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/loongarch64/virt/PPTT b/tests/data/acpi/loongarch64/virt/PPTT new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/loongarch64/virt/SLIT b/tests/data/acpi/loongarch64/virt/SLIT new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/loongarch64/virt/SPCR b/tests/data/acpi/loongarch64/virt/SPCR new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/loongarch64/virt/SRAT b/tests/data/acpi/loongarch64/virt/SRAT new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..bad1380eec 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,9 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/loongarch64/virt/APIC", +"tests/data/acpi/loongarch64/virt/DSDT", +"tests/data/acpi/loongarch64/virt/FACP", +"tests/data/acpi/loongarch64/virt/MCFG", +"tests/data/acpi/loongarch64/virt/PPTT", +"tests/data/acpi/loongarch64/virt/SLIT", +"tests/data/acpi/loongarch64/virt/SPCR", +"tests/data/acpi/loongarch64/virt/SRAT", From 73e2cba058c74c9a0d5b640711a1ff2428551952 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 12 Jun 2025 17:03:18 +0800 Subject: [PATCH 2293/2760] tests/qtest/bios-tables-test: Add basic testing for LoongArch Add basic ACPI table test case for LoongArch, including cpu topology, numa memory, memory hotplug and oem-id test cases. Signed-off-by: Bibo Mao Message-Id: <20250612090321.3416594-3-maobibo@loongson.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 79 ++++++++++++++++++++++++++++++++++ tests/qtest/meson.build | 1 + 2 files changed, 80 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 4dbc07ec5e..f41e638014 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -2439,6 +2439,74 @@ static void test_acpi_aarch64_virt_oem_fields(void) g_free(args); } +#define LOONGARCH64_INIT_TEST_DATA(data) \ + test_data data = { \ + .machine = "virt", \ + .arch = "loongarch64", \ + .tcg_only = true, \ + .uefi_fl1 = "pc-bios/edk2-loongarch64-code.fd", \ + .uefi_fl2 = "pc-bios/edk2-loongarch64-vars.fd", \ + .cd = "tests/data/uefi-boot-images/" \ + "bios-tables-test.loongarch64.iso.qcow2", \ + .ram_start = 0, \ + .scan_len = 128ULL * MiB, \ + } + +static void test_acpi_loongarch64_virt(void) +{ + LOONGARCH64_INIT_TEST_DATA(data); + + test_acpi_one("-cpu la464 ", &data); + free_test_data(&data); +} + +static void test_acpi_loongarch64_virt_topology(void) +{ + LOONGARCH64_INIT_TEST_DATA(data); + + data.variant = ".topology"; + test_acpi_one("-cpu la464 -smp sockets=1,cores=2,threads=2", &data); + free_test_data(&data); +} + +static void test_acpi_loongarch64_virt_numamem(void) +{ + LOONGARCH64_INIT_TEST_DATA(data); + + data.variant = ".numamem"; + test_acpi_one(" -cpu la464 -m 128" + " -object memory-backend-ram,id=ram0,size=64M" + " -object memory-backend-ram,id=ram1,size=64M" + " -numa node,memdev=ram0 -numa node,memdev=ram1" + " -numa dist,src=0,dst=1,val=21", + &data); + free_test_data(&data); +} + +static void test_acpi_loongarch64_virt_memhp(void) +{ + LOONGARCH64_INIT_TEST_DATA(data); + + data.variant = ".memhp"; + test_acpi_one(" -cpu la464 -m 128,slots=2,maxmem=256M" + " -object memory-backend-ram,id=ram0,size=128M", + &data); + free_test_data(&data); +} + +static void test_acpi_loongarch64_virt_oem_fields(void) +{ + LOONGARCH64_INIT_TEST_DATA(data); + char *args; + + args = test_acpi_create_args(&data, "-cpu la464 "OEM_TEST_ARGS); + data.qts = qtest_init(args); + test_acpi_load_tables(&data); + test_oem_fields(&data); + qtest_quit(data.qts); + free_test_data(&data); + g_free(args); +} int main(int argc, char *argv[]) { @@ -2614,6 +2682,17 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/virt/numamem", test_acpi_riscv64_virt_tcg_numamem); } + } else if (strcmp(arch, "loongarch64") == 0) { + if (has_tcg) { + qtest_add_func("acpi/virt", test_acpi_loongarch64_virt); + qtest_add_func("acpi/virt/topology", + test_acpi_loongarch64_virt_topology); + qtest_add_func("acpi/virt/numamem", + test_acpi_loongarch64_virt_numamem); + qtest_add_func("acpi/virt/memhp", test_acpi_loongarch64_virt_memhp); + qtest_add_func("acpi/virt/oem-fields", + test_acpi_loongarch64_virt_oem_fields); + } } ret = g_test_run(); boot_sector_cleanup(disk); diff --git a/tests/qtest/meson.build b/tests/qtest/meson.build index 5ad969f616..669d07c06b 100644 --- a/tests/qtest/meson.build +++ b/tests/qtest/meson.build @@ -148,6 +148,7 @@ qtests_hppa = \ qtests_loongarch64 = qtests_filter + \ (config_all_devices.has_key('CONFIG_LOONGARCH_VIRT') ? ['numa-test'] : []) + \ + (unpack_edk2_blobs ? ['bios-tables-test'] : []) + \ ['boot-serial-test', 'cpu-plug-test'] From c43ca0de62749286ee099baaf628b7f7b6e9844f Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 12 Jun 2025 17:03:19 +0800 Subject: [PATCH 2294/2760] rebuild-expected-aml.sh: Add support for LoongArch Update the list of supported architectures to include LoongArch. Signed-off-by: Bibo Mao Message-Id: <20250612090321.3416594-4-maobibo@loongson.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/rebuild-expected-aml.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/data/acpi/rebuild-expected-aml.sh b/tests/data/acpi/rebuild-expected-aml.sh index c1092fb8ba..cbf9ffe0dd 100755 --- a/tests/data/acpi/rebuild-expected-aml.sh +++ b/tests/data/acpi/rebuild-expected-aml.sh @@ -12,7 +12,7 @@ # This work is licensed under the terms of the GNU GPLv2. # See the COPYING.LIB file in the top-level directory. -qemu_arches="x86_64 aarch64 riscv64" +qemu_arches="x86_64 aarch64 riscv64 loongarch64" if [ ! -e "tests/qtest/bios-tables-test" ]; then echo "Test: bios-tables-test is required! Run make check before this script." @@ -37,7 +37,7 @@ if [ -z "$qemu_bins" ]; then echo "Only the following architectures are currently supported: $qemu_arches" echo "None of these configured!" echo "To fix, run configure \ - --target-list=x86_64-softmmu,aarch64-softmmu,riscv64-softmmu" + --target-list=x86_64-softmmu,aarch64-softmmu,riscv64-softmmu,loongarch64-softmmu" exit 1; fi From 67fbf12288df10eb1ea76d4f48d6a8efa46ae65c Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 12 Jun 2025 17:03:20 +0800 Subject: [PATCH 2295/2760] tests/acpi: Fill acpi table data for LoongArch The acpi table data is filled for LoongArch virt machine with the following command: tests/data/acpi/rebuild-expected-aml.sh Signed-off-by: Bibo Mao Message-Id: <20250612090321.3416594-5-maobibo@loongson.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/loongarch64/virt/APIC | Bin 0 -> 108 bytes tests/data/acpi/loongarch64/virt/APIC.topology | Bin 0 -> 153 bytes tests/data/acpi/loongarch64/virt/DSDT | Bin 0 -> 4641 bytes tests/data/acpi/loongarch64/virt/DSDT.memhp | Bin 0 -> 5862 bytes tests/data/acpi/loongarch64/virt/DSDT.numamem | Bin 0 -> 4647 bytes tests/data/acpi/loongarch64/virt/DSDT.topology | Bin 0 -> 4943 bytes tests/data/acpi/loongarch64/virt/FACP | Bin 0 -> 268 bytes tests/data/acpi/loongarch64/virt/MCFG | Bin 0 -> 60 bytes tests/data/acpi/loongarch64/virt/PPTT | Bin 0 -> 76 bytes tests/data/acpi/loongarch64/virt/PPTT.topology | Bin 0 -> 176 bytes tests/data/acpi/loongarch64/virt/SLIT.numamem | Bin 0 -> 48 bytes tests/data/acpi/loongarch64/virt/SPCR | Bin 0 -> 80 bytes tests/data/acpi/loongarch64/virt/SRAT | Bin 0 -> 104 bytes tests/data/acpi/loongarch64/virt/SRAT.memhp | Bin 0 -> 144 bytes tests/data/acpi/loongarch64/virt/SRAT.numamem | Bin 0 -> 144 bytes tests/data/acpi/loongarch64/virt/SRAT.topology | Bin 0 -> 152 bytes 16 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/data/acpi/loongarch64/virt/APIC.topology create mode 100644 tests/data/acpi/loongarch64/virt/DSDT.memhp create mode 100644 tests/data/acpi/loongarch64/virt/DSDT.numamem create mode 100644 tests/data/acpi/loongarch64/virt/DSDT.topology create mode 100644 tests/data/acpi/loongarch64/virt/PPTT.topology create mode 100644 tests/data/acpi/loongarch64/virt/SLIT.numamem create mode 100644 tests/data/acpi/loongarch64/virt/SRAT.memhp create mode 100644 tests/data/acpi/loongarch64/virt/SRAT.numamem create mode 100644 tests/data/acpi/loongarch64/virt/SRAT.topology diff --git a/tests/data/acpi/loongarch64/virt/APIC b/tests/data/acpi/loongarch64/virt/APIC index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3477789f422cad54f16734b3ec9ad1ff5135165d 100644 GIT binary patch literal 108 zcmZ<^@N~{$U|?YU>g4b25v<@85#X!<1dKp25F12;0Eiakhw;E%5ne`ShX4P;(hQ=) ZK)Db4AdUkN9{^%8L6C9*AO@2T3;;5a3;_TD literal 0 HcmV?d00001 diff --git a/tests/data/acpi/loongarch64/virt/APIC.topology b/tests/data/acpi/loongarch64/virt/APIC.topology new file mode 100644 index 0000000000000000000000000000000000000000..da0089d57f608aca230a76b67e72f9f0ad8e71f2 GIT binary patch literal 153 zcmZ<^@N}NZz`(#5=H&0}5v<@85#X!<1dKp25F12;0Eiakhw)%s5FdttvP?iO2tyS! r1DPNU;)?JxGBf=D4`cy>s4!6IgFcAq0K^A?SWFP4N&tw#qyqy0Q#cF( literal 0 HcmV?d00001 diff --git a/tests/data/acpi/loongarch64/virt/DSDT b/tests/data/acpi/loongarch64/virt/DSDT index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f32e732b11a557ae01c7f383625d3b6f459ac9f7 100644 GIT binary patch literal 4641 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 F@gG>(gG>Mb literal 0 HcmV?d00001 diff --git a/tests/data/acpi/loongarch64/virt/DSDT.numamem b/tests/data/acpi/loongarch64/virt/DSDT.numamem new file mode 100644 index 0000000000000000000000000000000000000000..9b462869cd4911714e7c2a22025c465afa2a7d52 GIT binary patch literal 4647 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 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/loongarch64/virt/FACP b/tests/data/acpi/loongarch64/virt/FACP index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..04d8d4c26fa2be24af40cd7a72959ec6b12790e5 100644 GIT binary patch literal 268 zcmZ>BbPnKQWME+Ra`Jcf2v%^42yj*a0-z8Bhz+8t3Rnaf7&sUh B2LS*8 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/loongarch64/virt/PPTT.topology b/tests/data/acpi/loongarch64/virt/PPTT.topology new file mode 100644 index 0000000000000000000000000000000000000000..d91e55b2399d9949dbb8e4c8cf634af1a0e56df4 GIT binary patch literal 176 zcmWFt2npH1z`($y@8s|75v<@85#X!<1dKp25F11@h%hjKX%HI*fMOt^0-+!zP>c`A cvcMq*Hv>s8vKUBj2@o^E^h3=9i7`XP0J3}u0RR91 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/loongarch64/virt/SLIT.numamem b/tests/data/acpi/loongarch64/virt/SLIT.numamem new file mode 100644 index 0000000000000000000000000000000000000000..67f00813af7b2356fe74eed943ab8dcf2291578b GIT binary patch literal 48 scmWIc@eDCwU|?W;;pFe^5v<@85#X!<1dKp25F11@0Wk=0iHdRo0OYg>0RR91 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/loongarch64/virt/SPCR b/tests/data/acpi/loongarch64/virt/SPCR index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3cc9bbcfb8051e632592d9db0fe3dba0af53ed8d 100644 GIT binary patch literal 80 zcmWFza1IJ!U|?W6=;ZJ05v<@85#X!<1dKp25F12;fdT{L1I7pP5RMa&VrKx#G5-ew Ih!}(f071qL0RR91 literal 0 HcmV?d00001 diff --git a/tests/data/acpi/loongarch64/virt/SRAT b/tests/data/acpi/loongarch64/virt/SRAT index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..ff234ce45cbdd32f57fc0668aba135992e5ca887 100644 GIT binary patch literal 104 zcmWFzatz5}U|?We=j89~5v<@85#X!<1dKp25F12;FdPV=@)c?|5v<@85#X!<1dKp25F12;FdPV=@)g4b25v<@85#X!<1dKp25F12;FdPVA@EOtMnb7#m$b3c(xJfVu J2aJKN4gjE82LS*8 literal 0 HcmV?d00001 From 85240876b2f6f9edd72a6f324cac0ee9ee28ab0a Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Thu, 12 Jun 2025 17:03:21 +0800 Subject: [PATCH 2296/2760] tests/acpi: Remove stale allowed tables Remove stale allowed tables for LoongArch virt machine. Signed-off-by: Bibo Mao Message-Id: <20250612090321.3416594-6-maobibo@loongson.cn> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 8 -------- 1 file changed, 8 deletions(-) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index bad1380eec..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,9 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/loongarch64/virt/APIC", -"tests/data/acpi/loongarch64/virt/DSDT", -"tests/data/acpi/loongarch64/virt/FACP", -"tests/data/acpi/loongarch64/virt/MCFG", -"tests/data/acpi/loongarch64/virt/PPTT", -"tests/data/acpi/loongarch64/virt/SLIT", -"tests/data/acpi/loongarch64/virt/SPCR", -"tests/data/acpi/loongarch64/virt/SRAT", From d7fb5693d9ffbeb9b49c981e1f9774392f1d41e5 Mon Sep 17 00:00:00 2001 From: Li Zhijian Date: Fri, 13 Jun 2025 16:51:10 +0800 Subject: [PATCH 2297/2760] hw/acpi: Fix GPtrArray memory leak in crs_range_merge MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This leak was detected by the valgrind. The crs_range_merge() function unconditionally allocated a GPtrArray 'even when range->len was zero, causing an early return without freeing the allocated array. This resulted in a memory leak when an empty range was processed. Instead of moving the allocation after the check (as previously attempted), use g_autoptr for automatic cleanup. This ensures the array is freed even on early returns, and also removes the need for the explicit free at the end of the function. Signed-off-by: Li Zhijian Message-Id: <20250613085110.111204-1-lizhijian@fujitsu.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: Ani Sinha Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index f8f93a9f66..cb817a0f31 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -160,7 +160,7 @@ void crs_replace_with_free_ranges(GPtrArray *ranges, */ static void crs_range_merge(GPtrArray *range) { - GPtrArray *tmp = g_ptr_array_new_with_free_func(crs_range_free); + g_autoptr(GPtrArray) tmp = g_ptr_array_new_with_free_func(crs_range_free); CrsRangeEntry *entry; uint64_t range_base, range_limit; int i; @@ -191,7 +191,6 @@ static void crs_range_merge(GPtrArray *range) entry = g_ptr_array_index(tmp, i); crs_range_insert(range, entry->base, entry->limit); } - g_ptr_array_free(tmp, true); } static void From 091c7d7924f33781c2fb8e7297dc54971e0c3785 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Tue, 17 Jun 2025 15:04:20 +0000 Subject: [PATCH 2298/2760] amd_iommu: Fix Miscellaneous Information Register 0 encoding The definitions encoding the maximum Virtual, Physical, and Guest Virtual Address sizes supported by the IOMMU are using incorrect offsets i.e. the VASize and GVASize offsets are switched. The value in the GVAsize field is also modified, since it was incorrectly encoded. Cc: qemu-stable@nongnu.org Fixes: d29a09ca6842 ("hw/i386: Introduce AMD IOMMU") Co-developed-by: Ethan MILON Signed-off-by: Ethan MILON Signed-off-by: Alejandro Jimenez Message-Id: <20250617150427.20585-2-alejandro.j.jimenez@oracle.com> Reviewed-by: Vasant Hegde Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 5672bdef89..3b1d2e9da5 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -196,9 +196,9 @@ #define AMDVI_PAGE_SHIFT_4K 12 #define AMDVI_PAGE_MASK_4K (~((1ULL << AMDVI_PAGE_SHIFT_4K) - 1)) -#define AMDVI_MAX_VA_ADDR (48UL << 5) -#define AMDVI_MAX_PH_ADDR (40UL << 8) -#define AMDVI_MAX_GVA_ADDR (48UL << 15) +#define AMDVI_MAX_GVA_ADDR (2UL << 5) +#define AMDVI_MAX_PH_ADDR (40UL << 8) +#define AMDVI_MAX_VA_ADDR (48UL << 15) /* Completion Wait data size */ #define AMDVI_COMPLETION_DATA_SIZE 8 From c63b8d1425ba8b3b08ee4f7346457fd8a7f12a24 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Tue, 17 Jun 2025 15:04:21 +0000 Subject: [PATCH 2299/2760] amd_iommu: Fix Device ID decoding for INVALIDATE_IOTLB_PAGES command The DeviceID bits are extracted using an incorrect offset in the call to amdvi_iotlb_remove_page(). This field is read (correctly) earlier, so use the value already retrieved for devid. Cc: qemu-stable@nongnu.org Fixes: d29a09ca6842 ("hw/i386: Introduce AMD IOMMU") Signed-off-by: Alejandro Jimenez Reviewed-by: Vasant Hegde Message-Id: <20250617150427.20585-3-alejandro.j.jimenez@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 963aa2450c..c27efa504d 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -508,7 +508,7 @@ static void amdvi_inval_inttable(AMDVIState *s, uint64_t *cmd) static void iommu_inval_iotlb(AMDVIState *s, uint64_t *cmd) { - uint16_t devid = extract64(cmd[0], 0, 16); + uint16_t devid = cpu_to_le16(extract64(cmd[0], 0, 16)); if (extract64(cmd[1], 1, 1) || extract64(cmd[1], 3, 1) || extract64(cmd[1], 6, 6)) { amdvi_log_illegalcom_error(s, extract64(cmd[0], 60, 4), @@ -521,7 +521,7 @@ static void iommu_inval_iotlb(AMDVIState *s, uint64_t *cmd) &devid); } else { amdvi_iotlb_remove_page(s, cpu_to_le64(extract64(cmd[1], 12, 52)) << 12, - cpu_to_le16(extract64(cmd[1], 0, 16))); + devid); } trace_amdvi_iotlb_inval(); } From ff3dcb3bf652912466dcc1cd10d3267f185c212e Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Tue, 17 Jun 2025 15:04:22 +0000 Subject: [PATCH 2300/2760] amd_iommu: Update bitmasks representing DTE reserved fields The DTE validation method verifies that all bits in reserved DTE fields are unset. Update them according to the latest definition available in AMD I/O Virtualization Technology (IOMMU) Specification - Section 2.2.2.1 Device Table Entry Format. Remove the magic numbers and use a macro helper to generate bitmasks covering the specified ranges for better legibility. Note that some reserved fields specify that events are generated when they contain non-zero bits, or checks are skipped under certain configurations. This change only updates the reserved masks, checks for special conditions are not yet implemented. Cc: qemu-stable@nongnu.org Signed-off-by: Alejandro Jimenez Reviewed-by: Vasant Hegde Message-Id: <20250617150427.20585-4-alejandro.j.jimenez@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 7 ++++--- hw/i386/amd_iommu.h | 9 ++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index c27efa504d..6e78047919 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -848,9 +848,10 @@ static inline uint64_t amdvi_get_perms(uint64_t entry) static bool amdvi_validate_dte(AMDVIState *s, uint16_t devid, uint64_t *dte) { - if ((dte[0] & AMDVI_DTE_LOWER_QUAD_RESERVED) - || (dte[1] & AMDVI_DTE_MIDDLE_QUAD_RESERVED) - || (dte[2] & AMDVI_DTE_UPPER_QUAD_RESERVED) || dte[3]) { + if ((dte[0] & AMDVI_DTE_QUAD0_RESERVED) || + (dte[1] & AMDVI_DTE_QUAD1_RESERVED) || + (dte[2] & AMDVI_DTE_QUAD2_RESERVED) || + (dte[3] & AMDVI_DTE_QUAD3_RESERVED)) { amdvi_log_illegaldevtab_error(s, devid, s->devtab + devid * AMDVI_DEVTAB_ENTRY_SIZE, 0); diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 3b1d2e9da5..aacb29b617 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -25,6 +25,8 @@ #include "hw/i386/x86-iommu.h" #include "qom/object.h" +#define GENMASK64(h, l) (((~0ULL) >> (63 - (h) + (l))) << (l)) + /* Capability registers */ #define AMDVI_CAPAB_BAR_LOW 0x04 #define AMDVI_CAPAB_BAR_HIGH 0x08 @@ -162,9 +164,10 @@ #define AMDVI_FEATURE_PC (1ULL << 9) /* Perf counters */ /* reserved DTE bits */ -#define AMDVI_DTE_LOWER_QUAD_RESERVED 0x80300000000000fc -#define AMDVI_DTE_MIDDLE_QUAD_RESERVED 0x0000000000000100 -#define AMDVI_DTE_UPPER_QUAD_RESERVED 0x08f0000000000000 +#define AMDVI_DTE_QUAD0_RESERVED (GENMASK64(6, 2) | GENMASK64(63, 63)) +#define AMDVI_DTE_QUAD1_RESERVED 0 +#define AMDVI_DTE_QUAD2_RESERVED GENMASK64(53, 52) +#define AMDVI_DTE_QUAD3_RESERVED (GENMASK64(14, 0) | GENMASK64(53, 48)) /* AMDVI paging mode */ #define AMDVI_GATS_MODE (2ULL << 12) From 108e10ff69099c3ebe147f505246be7c2ad2a499 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Tue, 17 Jun 2025 15:04:23 +0000 Subject: [PATCH 2301/2760] amd_iommu: Fix masks for various IOMMU MMIO Registers Address various issues with definitions of the MMIO registers e.g. for the Device Table Address Register, the size mask currently encompasses reserved bits [11:9], so change it to only extract the bits [8:0] encoding size. Convert masks to use GENMASK64 for consistency, and make unrelated definitions independent. Cc: qemu-stable@nongnu.org Fixes: d29a09ca6842 ("hw/i386: Introduce AMD IOMMU") Signed-off-by: Alejandro Jimenez Reviewed-by: Vasant Hegde Message-Id: <20250617150427.20585-5-alejandro.j.jimenez@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.h | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index aacb29b617..988a485f80 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -68,34 +68,34 @@ #define AMDVI_MMIO_SIZE 0x4000 -#define AMDVI_MMIO_DEVTAB_SIZE_MASK ((1ULL << 12) - 1) -#define AMDVI_MMIO_DEVTAB_BASE_MASK (((1ULL << 52) - 1) & ~ \ - AMDVI_MMIO_DEVTAB_SIZE_MASK) +#define AMDVI_MMIO_DEVTAB_SIZE_MASK GENMASK64(8, 0) +#define AMDVI_MMIO_DEVTAB_BASE_MASK GENMASK64(51, 12) + #define AMDVI_MMIO_DEVTAB_ENTRY_SIZE 32 #define AMDVI_MMIO_DEVTAB_SIZE_UNIT 4096 /* some of this are similar but just for readability */ #define AMDVI_MMIO_CMDBUF_SIZE_BYTE (AMDVI_MMIO_COMMAND_BASE + 7) #define AMDVI_MMIO_CMDBUF_SIZE_MASK 0x0f -#define AMDVI_MMIO_CMDBUF_BASE_MASK AMDVI_MMIO_DEVTAB_BASE_MASK -#define AMDVI_MMIO_CMDBUF_HEAD_MASK (((1ULL << 19) - 1) & ~0x0f) -#define AMDVI_MMIO_CMDBUF_TAIL_MASK AMDVI_MMIO_EVTLOG_HEAD_MASK +#define AMDVI_MMIO_CMDBUF_BASE_MASK GENMASK64(51, 12) +#define AMDVI_MMIO_CMDBUF_HEAD_MASK GENMASK64(18, 4) +#define AMDVI_MMIO_CMDBUF_TAIL_MASK GENMASK64(18, 4) #define AMDVI_MMIO_EVTLOG_SIZE_BYTE (AMDVI_MMIO_EVENT_BASE + 7) -#define AMDVI_MMIO_EVTLOG_SIZE_MASK AMDVI_MMIO_CMDBUF_SIZE_MASK -#define AMDVI_MMIO_EVTLOG_BASE_MASK AMDVI_MMIO_CMDBUF_BASE_MASK -#define AMDVI_MMIO_EVTLOG_HEAD_MASK (((1ULL << 19) - 1) & ~0x0f) -#define AMDVI_MMIO_EVTLOG_TAIL_MASK AMDVI_MMIO_EVTLOG_HEAD_MASK +#define AMDVI_MMIO_EVTLOG_SIZE_MASK 0x0f +#define AMDVI_MMIO_EVTLOG_BASE_MASK GENMASK64(51, 12) +#define AMDVI_MMIO_EVTLOG_HEAD_MASK GENMASK64(18, 4) +#define AMDVI_MMIO_EVTLOG_TAIL_MASK GENMASK64(18, 4) -#define AMDVI_MMIO_PPRLOG_SIZE_BYTE (AMDVI_MMIO_EVENT_BASE + 7) -#define AMDVI_MMIO_PPRLOG_HEAD_MASK AMDVI_MMIO_EVTLOG_HEAD_MASK -#define AMDVI_MMIO_PPRLOG_TAIL_MASK AMDVI_MMIO_EVTLOG_HEAD_MASK -#define AMDVI_MMIO_PPRLOG_BASE_MASK AMDVI_MMIO_EVTLOG_BASE_MASK -#define AMDVI_MMIO_PPRLOG_SIZE_MASK AMDVI_MMIO_EVTLOG_SIZE_MASK +#define AMDVI_MMIO_PPRLOG_SIZE_BYTE (AMDVI_MMIO_PPR_BASE + 7) +#define AMDVI_MMIO_PPRLOG_SIZE_MASK 0x0f +#define AMDVI_MMIO_PPRLOG_BASE_MASK GENMASK64(51, 12) +#define AMDVI_MMIO_PPRLOG_HEAD_MASK GENMASK64(18, 4) +#define AMDVI_MMIO_PPRLOG_TAIL_MASK GENMASK64(18, 4) #define AMDVI_MMIO_EXCL_ENABLED_MASK (1ULL << 0) #define AMDVI_MMIO_EXCL_ALLOW_MASK (1ULL << 1) -#define AMDVI_MMIO_EXCL_LIMIT_MASK AMDVI_MMIO_DEVTAB_BASE_MASK +#define AMDVI_MMIO_EXCL_LIMIT_MASK GENMASK64(51, 12) #define AMDVI_MMIO_EXCL_LIMIT_LOW 0xfff /* mmio control register flags */ @@ -132,14 +132,14 @@ #define AMDVI_DEV_TRANSLATION_VALID (1ULL << 1) #define AMDVI_DEV_MODE_MASK 0x7 #define AMDVI_DEV_MODE_RSHIFT 9 -#define AMDVI_DEV_PT_ROOT_MASK 0xffffffffff000 +#define AMDVI_DEV_PT_ROOT_MASK GENMASK64(51, 12) #define AMDVI_DEV_PT_ROOT_RSHIFT 12 #define AMDVI_DEV_PERM_SHIFT 61 #define AMDVI_DEV_PERM_READ (1ULL << 61) #define AMDVI_DEV_PERM_WRITE (1ULL << 62) /* Device table entry bits 64:127 */ -#define AMDVI_DEV_DOMID_ID_MASK ((1ULL << 16) - 1) +#define AMDVI_DEV_DOMID_ID_MASK GENMASK64(15, 0) /* Event codes and flags, as stored in the info field */ #define AMDVI_EVENT_ILLEGAL_DEVTAB_ENTRY (0x1U << 12) @@ -197,7 +197,7 @@ #define AMDVI_PAGE_SIZE (1ULL << AMDVI_PAGE_SHIFT) #define AMDVI_PAGE_SHIFT_4K 12 -#define AMDVI_PAGE_MASK_4K (~((1ULL << AMDVI_PAGE_SHIFT_4K) - 1)) +#define AMDVI_PAGE_MASK_4K GENMASK64(63, 12) #define AMDVI_MAX_GVA_ADDR (2UL << 5) #define AMDVI_MAX_PH_ADDR (40UL << 8) From 123cf4bdd378f746dfa2f5415ba084148dded3e3 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Tue, 17 Jun 2025 15:04:24 +0000 Subject: [PATCH 2302/2760] amd_iommu: Fix mask to retrieve Interrupt Table Root Pointer from DTE Fix an off-by-one error in the definition of AMDVI_IR_PHYS_ADDR_MASK. The current definition masks off the most significant bit of the Interrupt Table Root ptr i.e. it only generates a mask with bits [50:6] set. See the AMD I/O Virtualization Technology (IOMMU) Specification for the Interrupt Table Root Pointer[51:6] field in the Device Table Entry format. Cc: qemu-stable@nongnu.org Fixes: b44159fe0078 ("x86_iommu/amd: Add interrupt remap support when VAPIC is not enabled") Signed-off-by: Alejandro Jimenez Reviewed-by: Vasant Hegde Message-Id: <20250617150427.20585-6-alejandro.j.jimenez@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 988a485f80..96fc5b621e 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -231,7 +231,7 @@ #define AMDVI_IR_INTCTL_PASS 1 #define AMDVI_IR_INTCTL_REMAP 2 -#define AMDVI_IR_PHYS_ADDR_MASK (((1ULL << 45) - 1) << 6) +#define AMDVI_IR_PHYS_ADDR_MASK GENMASK64(51, 6) /* MSI data 10:0 bits (section 2.2.5.1 Fig 14) */ #define AMDVI_IRTE_OFFSET 0x7ff From 67d3077ee403472d45794399e97c9f329242fce9 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Tue, 17 Jun 2025 15:04:25 +0000 Subject: [PATCH 2303/2760] amd_iommu: Fix the calculation for Device Table size Correctly calculate the Device Table size using the format encoded in the Device Table Base Address Register (MMIO Offset 0000h). Cc: qemu-stable@nongnu.org Fixes: d29a09ca6842 ("hw/i386: Introduce AMD IOMMU") Signed-off-by: Alejandro Jimenez Reviewed-by: Vasant Hegde Message-Id: <20250617150427.20585-7-alejandro.j.jimenez@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 6e78047919..92f94dc788 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -665,8 +665,8 @@ static inline void amdvi_handle_devtab_write(AMDVIState *s) uint64_t val = amdvi_readq(s, AMDVI_MMIO_DEVICE_TABLE); s->devtab = (val & AMDVI_MMIO_DEVTAB_BASE_MASK); - /* set device table length */ - s->devtab_len = ((val & AMDVI_MMIO_DEVTAB_SIZE_MASK) + 1 * + /* set device table length (i.e. number of entries table can hold) */ + s->devtab_len = (((val & AMDVI_MMIO_DEVTAB_SIZE_MASK) + 1) * (AMDVI_MMIO_DEVTAB_SIZE_UNIT / AMDVI_MMIO_DEVTAB_ENTRY_SIZE)); } From 5959b641c98b5ae9677e2c1d89902dac31b344d9 Mon Sep 17 00:00:00 2001 From: Alejandro Jimenez Date: Tue, 17 Jun 2025 15:04:26 +0000 Subject: [PATCH 2304/2760] amd_iommu: Remove duplicated definitions No functional change. Signed-off-by: Alejandro Jimenez Reviewed-by: Vasant Hegde Message-Id: <20250617150427.20585-8-alejandro.j.jimenez@oracle.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 96fc5b621e..8b42913ed8 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -206,10 +206,6 @@ /* Completion Wait data size */ #define AMDVI_COMPLETION_DATA_SIZE 8 -#define AMDVI_COMMAND_SIZE 16 -/* Completion Wait data size */ -#define AMDVI_COMPLETION_DATA_SIZE 8 - #define AMDVI_COMMAND_SIZE 16 #define AMDVI_INT_ADDR_FIRST 0xfee00000 From 5788929e05e18ed5f76dc8ade4210f022c9ba5a1 Mon Sep 17 00:00:00 2001 From: Ethan Milon Date: Tue, 17 Jun 2025 15:04:27 +0000 Subject: [PATCH 2305/2760] amd_iommu: Fix truncation of oldval in amdvi_writeq The variable `oldval` was incorrectly declared as a 32-bit `uint32_t`. This could lead to truncation and incorrect behavior where the upper read-only 32 bits are significant. Fix the type of `oldval` to match the return type of `ldq_le_p()`. Cc: qemu-stable@nongnu.org Fixes: d29a09ca6842 ("hw/i386: Introduce AMD IOMMU") Signed-off-by: Ethan Milon Message-Id: <20250617150427.20585-9-alejandro.j.jimenez@oracle.com> Reviewed-by: Vasant Hegde Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 92f94dc788..5a24c17548 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -140,7 +140,7 @@ static void amdvi_writeq(AMDVIState *s, hwaddr addr, uint64_t val) { uint64_t romask = ldq_le_p(&s->romask[addr]); uint64_t w1cmask = ldq_le_p(&s->w1cmask[addr]); - uint32_t oldval = ldq_le_p(&s->mmior[addr]); + uint64_t oldval = ldq_le_p(&s->mmior[addr]); stq_le_p(&s->mmior[addr], ((oldval & romask) | (val & ~romask)) & ~(val & w1cmask)); } From 54401d5abd13c7f2ff5d1cb65e73a067743230e3 Mon Sep 17 00:00:00 2001 From: Li Chen Date: Wed, 28 May 2025 18:53:35 +0800 Subject: [PATCH 2306/2760] acpi: Add machine option to disable SPCR table The ACPI SPCR (Serial Port Console Redirection) table allows firmware to specify a preferred serial console device to the operating system. On ARM64 systems, Linux by default respects this table: even if the kernel command line does not include a hardware serial console (e.g., "console=ttyAMA0"), the kernel still register the serial device referenced by SPCR as a printk console. While this behavior is standard-compliant, it can lead to situations where guest console behavior is influenced by platform firmware rather than user-specified configuration. To make guest console behavior more predictable and under user control, this patch introduces a machine option to explicitly disable SPCR table exposure: -machine spcr=off By default, the option is enabled (spcr=on), preserving existing behavior. When disabled, QEMU will omit the SPCR table from the guest's ACPI namespace, ensuring that only consoles explicitly declared in the kernel command line are registered. Signed-off-by: Li Chen Reviewed-by: Bibo Mao Acked-by: Michael S. Tsirkin Reviewed-by: Gavin Shan Reviewed-by: Sunil V L Message-Id: <20250528105404.457729-2-me@linux.beauty> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 5 ++++- hw/core/machine.c | 22 ++++++++++++++++++++++ hw/loongarch/virt-acpi-build.c | 4 +++- hw/riscv/virt-acpi-build.c | 5 ++++- include/hw/boards.h | 1 + qemu-options.hx | 5 +++++ 6 files changed, 39 insertions(+), 3 deletions(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 0dfb8ec2c3..782b17b966 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -1023,7 +1023,10 @@ void virt_acpi_build(VirtMachineState *vms, AcpiBuildTables *tables) } acpi_add_table(table_offsets, tables_blob); - spcr_setup(tables_blob, tables->linker, vms); + + if (ms->acpi_spcr_enabled) { + spcr_setup(tables_blob, tables->linker, vms); + } acpi_add_table(table_offsets, tables_blob); build_dbg2(tables_blob, tables->linker, vms); diff --git a/hw/core/machine.c b/hw/core/machine.c index e869821b22..ceee058cad 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -577,6 +577,20 @@ static void machine_set_nvdimm(Object *obj, bool value, Error **errp) ms->nvdimms_state->is_enabled = value; } +static bool machine_get_spcr(Object *obj, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + return ms->acpi_spcr_enabled; +} + +static void machine_set_spcr(Object *obj, bool value, Error **errp) +{ + MachineState *ms = MACHINE(obj); + + ms->acpi_spcr_enabled = value; +} + static bool machine_get_hmat(Object *obj, Error **errp) { MachineState *ms = MACHINE(obj); @@ -1281,6 +1295,14 @@ static void machine_initfn(Object *obj) "Table (HMAT)"); } + /* SPCR */ + ms->acpi_spcr_enabled = true; + object_property_add_bool(obj, "spcr", machine_get_spcr, machine_set_spcr); + object_property_set_description(obj, "spcr", + "Set on/off to enable/disable " + "ACPI Serial Port Console Redirection " + "Table (spcr)"); + /* default to mc->default_cpus */ ms->smp.cpus = mc->default_cpus; ms->smp.max_cpus = mc->default_cpus; diff --git a/hw/loongarch/virt-acpi-build.c b/hw/loongarch/virt-acpi-build.c index 2cd2d9d842..8c2228a772 100644 --- a/hw/loongarch/virt-acpi-build.c +++ b/hw/loongarch/virt-acpi-build.c @@ -557,7 +557,9 @@ static void acpi_build(AcpiBuildTables *tables, MachineState *machine) acpi_add_table(table_offsets, tables_blob); build_srat(tables_blob, tables->linker, machine); acpi_add_table(table_offsets, tables_blob); - spcr_setup(tables_blob, tables->linker, machine); + + if (machine->acpi_spcr_enabled) + spcr_setup(tables_blob, tables->linker, machine); if (machine->numa_state->num_nodes) { if (machine->numa_state->have_numa_distance) { diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index 8b5683dbde..ee1416d264 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -894,7 +894,10 @@ static void virt_acpi_build(RISCVVirtState *s, AcpiBuildTables *tables) } acpi_add_table(table_offsets, tables_blob); - spcr_setup(tables_blob, tables->linker, s); + + if (ms->acpi_spcr_enabled) { + spcr_setup(tables_blob, tables->linker, s); + } acpi_add_table(table_offsets, tables_blob); { diff --git a/include/hw/boards.h b/include/hw/boards.h index f424b2b505..f94713e6e2 100644 --- a/include/hw/boards.h +++ b/include/hw/boards.h @@ -443,6 +443,7 @@ struct MachineState { SmpCache smp_cache; struct NVDIMMState *nvdimms_state; struct NumaState *numa_state; + bool acpi_spcr_enabled; }; /* diff --git a/qemu-options.hx b/qemu-options.hx index 1f862b19a6..9b2d5f4b7a 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -38,6 +38,7 @@ DEF("machine", HAS_ARG, QEMU_OPTION_machine, \ " nvdimm=on|off controls NVDIMM support (default=off)\n" " memory-encryption=@var{} memory encryption object to use (default=none)\n" " hmat=on|off controls ACPI HMAT support (default=off)\n" + " spcr=on|off controls ACPI SPCR support (default=on)\n" #ifdef CONFIG_POSIX " aux-ram-share=on|off allocate auxiliary guest RAM as shared (default: off)\n" #endif @@ -105,6 +106,10 @@ SRST Enables or disables ACPI Heterogeneous Memory Attribute Table (HMAT) support. The default is off. + ``spcr=on|off`` + Enables or disables ACPI Serial Port Console Redirection Table + (SPCR) support. The default is on. + ``aux-ram-share=on|off`` Allocate auxiliary guest RAM as an anonymous file that is shareable with an external process. This option applies to From 9ce87106c7bcb1c2ada17e578db87f13ac29ae92 Mon Sep 17 00:00:00 2001 From: Li Chen Date: Wed, 28 May 2025 18:53:36 +0800 Subject: [PATCH 2307/2760] tests/qtest/bios-tables-test: Add test for disabling SPCR on AArch64 Add ACPI SPCR table test case for ARM when SPCR was off. Signed-off-by: Li Chen Message-Id: <20250528105404.457729-3-me@linux.beauty> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index f41e638014..c84cf1070d 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1789,6 +1789,24 @@ static void test_acpi_aarch64_virt_tcg_pxb(void) free_test_data(&data); } +static void test_acpi_aarch64_virt_tcg_acpi_spcr(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 128ULL * 1024 * 1024, + .variant = ".acpispcr", + }; + + test_acpi_one("-cpu cortex-a57 " + " -machine spcr=off", &data); + free_test_data(&data); +} static void test_acpi_tcg_acpi_hmat(const char *machine, const char *arch) { test_data data = {}; @@ -2672,6 +2690,8 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/virt/pxb", test_acpi_aarch64_virt_tcg_pxb); qtest_add_func("acpi/virt/oem-fields", test_acpi_aarch64_virt_oem_fields); + qtest_add_func("acpi/virt/acpispcr", + test_acpi_aarch64_virt_tcg_acpi_spcr); if (qtest_has_device("virtio-iommu-pci")) { qtest_add_func("acpi/virt/viot", test_acpi_aarch64_virt_viot); } From da77fc6c2e28a17249a6f459213247720e22e170 Mon Sep 17 00:00:00 2001 From: Li Chen Date: Wed, 28 May 2025 18:53:37 +0800 Subject: [PATCH 2308/2760] tests/qtest/bios-tables-test: Add test for disabling SPCR on RISC-V Add ACPI SPCR table test case for RISC-V when SPCR was off. Signed-off-by: Li Chen Reviewed-by: Sunil V L Message-Id: <20250528105404.457729-4-me@linux.beauty> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index c84cf1070d..e988deac02 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1807,6 +1807,26 @@ static void test_acpi_aarch64_virt_tcg_acpi_spcr(void) " -machine spcr=off", &data); free_test_data(&data); } + +static void test_acpi_riscv64_virt_tcg_acpi_spcr(void) +{ + test_data data = { + .machine = "virt", + .arch = "riscv64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-riscv-code.fd", + .uefi_fl2 = "pc-bios/edk2-riscv-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.riscv64.iso.qcow2", + .ram_start = 0x80000000ULL, + .scan_len = 128ULL * 1024 * 1024, + .variant = ".acpispcr", + }; + + test_acpi_one("-cpu rva22s64 " + "-machine spcr=off", &data); + free_test_data(&data); +} + static void test_acpi_tcg_acpi_hmat(const char *machine, const char *arch) { test_data data = {}; @@ -2701,6 +2721,8 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/virt", test_acpi_riscv64_virt_tcg); qtest_add_func("acpi/virt/numamem", test_acpi_riscv64_virt_tcg_numamem); + qtest_add_func("acpi/virt/acpispcr", + test_acpi_riscv64_virt_tcg_acpi_spcr); } } else if (strcmp(arch, "loongarch64") == 0) { if (has_tcg) { From 502f00c51ad9a133c728b52e96c7e8e0cffef191 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:00 +0200 Subject: [PATCH 2309/2760] block: never use atomics to access bs->quiesce_counter All accesses of bs->quiesce_counter are in the main thread, either after a GLOBAL_STATE_CODE() macro or in a function with GRAPH_WRLOCK annotation. This is essentially a revert of 414c2ec358 ("block: access quiesce_counter with atomic ops"). At that time, neither the GLOBAL_STATE_CODE() macro nor the GRAPH_WRLOCK annotation existed. Even if the field was only accessed in the main thread back then (did not check if that is actually the case), it wouldn't have been easy to verify. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-24-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/io.c | 7 ++----- include/block/block_int-common.h | 2 +- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/block/io.c b/block/io.c index ac5c7174f6..9bd8ba8431 100644 --- a/block/io.c +++ b/block/io.c @@ -361,7 +361,7 @@ static void bdrv_do_drained_begin(BlockDriverState *bs, BdrvChild *parent, GLOBAL_STATE_CODE(); /* Stop things in parent-to-child order */ - if (qatomic_fetch_inc(&bs->quiesce_counter) == 0) { + if (bs->quiesce_counter++ == 0) { GRAPH_RDLOCK_GUARD_MAINLOOP(); bdrv_parent_drained_begin(bs, parent); if (bs->drv && bs->drv->bdrv_drain_begin) { @@ -401,8 +401,6 @@ bdrv_drained_begin(BlockDriverState *bs) */ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) { - int old_quiesce_counter; - IO_OR_GS_CODE(); if (qemu_in_coroutine()) { @@ -415,8 +413,7 @@ static void bdrv_do_drained_end(BlockDriverState *bs, BdrvChild *parent) assert(bs->quiesce_counter > 0); /* Re-enable things in child-to-parent order */ - old_quiesce_counter = qatomic_fetch_dec(&bs->quiesce_counter); - if (old_quiesce_counter == 1) { + if (--bs->quiesce_counter == 0) { GRAPH_RDLOCK_GUARD_MAINLOOP(); if (bs->drv && bs->drv->bdrv_drain_end) { bs->drv->bdrv_drain_end(bs); diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index 925a3e7353..e96c6a6a03 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -1253,7 +1253,7 @@ struct BlockDriverState { /* do we need to tell the quest if we have a volatile write cache? */ int enable_write_cache; - /* Accessed with atomic ops. */ + /* Accessed only in the main thread. */ int quiesce_counter; unsigned int write_gen; /* Current data generation */ From 6b89e851fabf78d7fb090bcdc71789ea1ef55c9b Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:01 +0200 Subject: [PATCH 2310/2760] block: add bdrv_graph_wrlock_drained() convenience wrapper Many write-locked sections are also drained sections. A new bdrv_graph_wrunlock_drained() wrapper around bdrv_graph_wrunlock() is introduced, which will begin a drained section first. A global variable is used so bdrv_graph_wrunlock() knows if it also needs to end such a drained section. Both the aio_poll call in bdrv_graph_wrlock() and the aio_bh_poll() in bdrv_graph_wrunlock() can re-enter a write-locked section. While for the latter, ending the drain could be moved to before the call, the former requires that the variable is a counter and not just a boolean. Since the wrapper calls bdrv_drain_all_begin(), which must be called with the graph unlocked, mark the wrapper as GRAPH_UNLOCKED too. The switch to the new helpers was generated with the following commands and then manually checked: find . -name '*.c' -exec sed -i -z 's/bdrv_drain_all_begin();\n\s*bdrv_graph_wrlock();/bdrv_graph_wrlock_drained();/g' {} ';' find . -name '*.c' -exec sed -i -z 's/bdrv_graph_wrunlock();\n\s*bdrv_drain_all_end();/bdrv_graph_wrunlock();/g' {} ';' Suggested-by: Kevin Wolf Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-25-f.ebner@proxmox.com> [kwolf: Removed redundant GRAPH_UNLOCKED] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 20 ++++------------ block/backup.c | 4 +--- block/blklogwrites.c | 8 ++----- block/blkverify.c | 4 +--- block/block-backend.c | 8 ++----- block/commit.c | 6 +---- block/graph-lock.c | 40 +++++++++++++++++++++++++++++--- block/mirror.c | 7 +----- block/qcow2.c | 4 +--- block/quorum.c | 8 ++----- block/replication.c | 11 ++------- block/snapshot.c | 4 +--- block/stream.c | 6 +---- block/vmdk.c | 20 ++++------------ blockdev.c | 4 +--- blockjob.c | 10 ++------ include/block/graph-lock.h | 11 +++++++++ tests/unit/test-bdrv-drain.c | 36 +++++++--------------------- tests/unit/test-bdrv-graph-mod.c | 20 ++++------------ 19 files changed, 90 insertions(+), 141 deletions(-) diff --git a/block.c b/block.c index bfd4340b24..1da10d55f0 100644 --- a/block.c +++ b/block.c @@ -1721,14 +1721,12 @@ bdrv_open_driver(BlockDriverState *bs, BlockDriver *drv, const char *node_name, open_failed: bs->drv = NULL; - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); if (bs->file != NULL) { bdrv_unref_child(bs, bs->file); assert(!bs->file); } bdrv_graph_wrunlock(); - bdrv_drain_all_end(); g_free(bs->opaque); bs->opaque = NULL; @@ -3602,11 +3600,9 @@ int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, int ret; GLOBAL_STATE_CODE(); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); return ret; } @@ -3797,12 +3793,10 @@ static BdrvChild *bdrv_open_child_common(const char *filename, return NULL; } - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); child = bdrv_attach_child(parent, bs, bdref_key, child_class, child_role, errp); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); return child; } @@ -5180,8 +5174,7 @@ static void bdrv_close(BlockDriverState *bs) bs->drv = NULL; } - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); QLIST_FOREACH_SAFE(child, &bs->children, next, next) { bdrv_unref_child(bs, child); } @@ -5189,7 +5182,6 @@ static void bdrv_close(BlockDriverState *bs) assert(!bs->backing); assert(!bs->file); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); g_free(bs->opaque); bs->opaque = NULL; @@ -5515,8 +5507,7 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, assert(!bs_new->backing); bdrv_graph_rdunlock_main_loop(); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); child = bdrv_attach_child_noperm(bs_new, bs_top, "backing", &child_of_bds, bdrv_backing_role(bs_new), @@ -5537,7 +5528,6 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, bdrv_refresh_limits(bs_top, NULL, NULL); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); return ret; } diff --git a/block/backup.c b/block/backup.c index 909027c17a..d4713fa1cd 100644 --- a/block/backup.c +++ b/block/backup.c @@ -498,12 +498,10 @@ BlockJob *backup_job_create(const char *job_id, BlockDriverState *bs, block_copy_set_speed(bcs, speed); /* Required permissions are taken by copy-before-write filter target */ - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); block_job_add_bdrv(&job->common, "target", target, 0, BLK_PERM_ALL, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); return &job->common; diff --git a/block/blklogwrites.c b/block/blklogwrites.c index 70ac76f401..aa1f888869 100644 --- a/block/blklogwrites.c +++ b/block/blklogwrites.c @@ -281,11 +281,9 @@ static int blk_log_writes_open(BlockDriverState *bs, QDict *options, int flags, ret = 0; fail_log: if (ret < 0) { - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_unref_child(bs, s->log_file); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); s->log_file = NULL; qemu_mutex_destroy(&s->mutex); } @@ -298,12 +296,10 @@ static void blk_log_writes_close(BlockDriverState *bs) { BDRVBlkLogWritesState *s = bs->opaque; - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_unref_child(bs, s->log_file); s->log_file = NULL; bdrv_graph_wrunlock(); - bdrv_drain_all_end(); qemu_mutex_destroy(&s->mutex); } diff --git a/block/blkverify.c b/block/blkverify.c index 3a71f7498c..72efcbe7ef 100644 --- a/block/blkverify.c +++ b/block/blkverify.c @@ -151,12 +151,10 @@ static void blkverify_close(BlockDriverState *bs) { BDRVBlkverifyState *s = bs->opaque; - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_unref_child(bs, s->test_file); s->test_file = NULL; bdrv_graph_wrunlock(); - bdrv_drain_all_end(); } static int64_t coroutine_fn GRAPH_RDLOCK diff --git a/block/block-backend.c b/block/block-backend.c index 68209bb2f7..f8d6ba65c1 100644 --- a/block/block-backend.c +++ b/block/block-backend.c @@ -889,11 +889,9 @@ void blk_remove_bs(BlockBackend *blk) root = blk->root; blk->root = NULL; - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_root_unref_child(root); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); } /* @@ -906,8 +904,7 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) GLOBAL_STATE_CODE(); bdrv_ref(bs); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); if ((bs->open_flags & BDRV_O_INACTIVE) && blk_can_inactivate(blk)) { blk->disable_perm = true; @@ -922,7 +919,6 @@ int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp) BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, perm, shared_perm, blk, errp); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); if (blk->root == NULL) { return -EPERM; } diff --git a/block/commit.c b/block/commit.c index 6c4b736ff8..dc1942483b 100644 --- a/block/commit.c +++ b/block/commit.c @@ -392,8 +392,7 @@ void commit_start(const char *job_id, BlockDriverState *bs, * this is the responsibility of the interface (i.e. whoever calls * commit_start()). */ - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); s->base_overlay = bdrv_find_overlay(top, base); assert(s->base_overlay); @@ -425,21 +424,18 @@ void commit_start(const char *job_id, BlockDriverState *bs, iter_shared_perms, errp); if (ret < 0) { bdrv_graph_wrunlock(); - bdrv_drain_all_end(); goto fail; } } if (bdrv_freeze_backing_chain(commit_top_bs, base, errp) < 0) { bdrv_graph_wrunlock(); - bdrv_drain_all_end(); goto fail; } s->chain_frozen = true; ret = block_job_add_bdrv(&s->common, "base", base, 0, BLK_PERM_ALL, errp); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); if (ret < 0) { goto fail; diff --git a/block/graph-lock.c b/block/graph-lock.c index c81162b147..b7319473a1 100644 --- a/block/graph-lock.c +++ b/block/graph-lock.c @@ -33,6 +33,17 @@ static QemuMutex aio_context_list_lock; /* Written and read with atomic operations. */ static int has_writer; +/* + * Many write-locked sections are also drained sections. There is a convenience + * wrapper bdrv_graph_wrlock_drained() which begins a drained section before + * acquiring the lock. This variable here is used so bdrv_graph_wrunlock() knows + * if it also needs to end such a drained section. It needs to be a counter, + * because the aio_poll() call in bdrv_graph_wrlock() might re-enter + * bdrv_graph_wrlock_drained(). And note that aio_bh_poll() in + * bdrv_graph_wrunlock() might also re-enter a write-locked section. + */ +static int wrlock_quiesced_counter; + /* * A reader coroutine could move from an AioContext to another. * If this happens, there is no problem from the point of view of @@ -112,8 +123,14 @@ void no_coroutine_fn bdrv_graph_wrlock(void) assert(!qatomic_read(&has_writer)); assert(!qemu_in_coroutine()); - /* Make sure that constantly arriving new I/O doesn't cause starvation */ - bdrv_drain_all_begin_nopoll(); + bool need_drain = wrlock_quiesced_counter == 0; + + if (need_drain) { + /* + * Make sure that constantly arriving new I/O doesn't cause starvation + */ + bdrv_drain_all_begin_nopoll(); + } /* * reader_count == 0: this means writer will read has_reader as 1 @@ -139,7 +156,18 @@ void no_coroutine_fn bdrv_graph_wrlock(void) smp_mb(); } while (reader_count() >= 1); - bdrv_drain_all_end(); + if (need_drain) { + bdrv_drain_all_end(); + } +} + +void no_coroutine_fn bdrv_graph_wrlock_drained(void) +{ + GLOBAL_STATE_CODE(); + + bdrv_drain_all_begin(); + wrlock_quiesced_counter++; + bdrv_graph_wrlock(); } void no_coroutine_fn bdrv_graph_wrunlock(void) @@ -168,6 +196,12 @@ void no_coroutine_fn bdrv_graph_wrunlock(void) * progress. */ aio_bh_poll(qemu_get_aio_context()); + + if (wrlock_quiesced_counter > 0) { + bdrv_drain_all_end(); + wrlock_quiesced_counter--; + } + } void coroutine_fn bdrv_graph_co_rdlock(void) diff --git a/block/mirror.c b/block/mirror.c index 6e8caf4b49..82a6e50cf8 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -2014,15 +2014,13 @@ static BlockJob *mirror_start_job( */ bdrv_disable_dirty_bitmap(s->dirty_bitmap); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); ret = block_job_add_bdrv(&s->common, "source", bs, 0, BLK_PERM_WRITE_UNCHANGED | BLK_PERM_WRITE | BLK_PERM_CONSISTENT_READ, errp); if (ret < 0) { bdrv_graph_wrunlock(); - bdrv_drain_all_end(); goto fail; } @@ -2068,19 +2066,16 @@ static BlockJob *mirror_start_job( iter_shared_perms, errp); if (ret < 0) { bdrv_graph_wrunlock(); - bdrv_drain_all_end(); goto fail; } } if (bdrv_freeze_backing_chain(mirror_top_bs, target, errp) < 0) { bdrv_graph_wrunlock(); - bdrv_drain_all_end(); goto fail; } } bdrv_graph_wrunlock(); - bdrv_drain_all_end(); QTAILQ_INIT(&s->ops_in_flight); diff --git a/block/qcow2.c b/block/qcow2.c index 45451a7ee8..4aa9f9e068 100644 --- a/block/qcow2.c +++ b/block/qcow2.c @@ -2823,11 +2823,9 @@ qcow2_do_close(BlockDriverState *bs, bool close_data_file) if (close_data_file && has_data_file(bs)) { GLOBAL_STATE_CODE(); bdrv_graph_rdunlock_main_loop(); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_unref_child(bs, s->data_file); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); s->data_file = NULL; bdrv_graph_rdlock_main_loop(); } diff --git a/block/quorum.c b/block/quorum.c index cc3bc5f4e7..76a4feb2d9 100644 --- a/block/quorum.c +++ b/block/quorum.c @@ -1037,8 +1037,7 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, close_exit: /* cleanup on error */ - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); for (i = 0; i < s->num_children; i++) { if (!opened[i]) { continue; @@ -1046,7 +1045,6 @@ static int quorum_open(BlockDriverState *bs, QDict *options, int flags, bdrv_unref_child(bs, s->children[i]); } bdrv_graph_wrunlock(); - bdrv_drain_all_end(); g_free(s->children); g_free(opened); exit: @@ -1059,13 +1057,11 @@ static void quorum_close(BlockDriverState *bs) BDRVQuorumState *s = bs->opaque; int i; - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); for (i = 0; i < s->num_children; i++) { bdrv_unref_child(bs, s->children[i]); } bdrv_graph_wrunlock(); - bdrv_drain_all_end(); g_free(s->children); } diff --git a/block/replication.c b/block/replication.c index 0879718854..83978b61f5 100644 --- a/block/replication.c +++ b/block/replication.c @@ -540,8 +540,7 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, return; } - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_ref(hidden_disk->bs); s->hidden_disk = bdrv_attach_child(bs, hidden_disk->bs, "hidden disk", @@ -550,7 +549,6 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, if (local_err) { error_propagate(errp, local_err); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); return; } @@ -561,7 +559,6 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, if (local_err) { error_propagate(errp, local_err); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); return; } @@ -574,14 +571,12 @@ static void replication_start(ReplicationState *rs, ReplicationMode mode, !check_top_bs(top_bs, bs)) { error_setg(errp, "No top_bs or it is invalid"); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); reopen_backing_file(bs, false, NULL); return; } bdrv_op_block_all(top_bs, s->blocker); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); s->backup_job = backup_job_create( NULL, s->secondary_disk->bs, s->hidden_disk->bs, @@ -656,14 +651,12 @@ static void replication_done(void *opaque, int ret) if (ret == 0) { s->stage = BLOCK_REPLICATION_DONE; - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_unref_child(bs, s->secondary_disk); s->secondary_disk = NULL; bdrv_unref_child(bs, s->hidden_disk); s->hidden_disk = NULL; bdrv_graph_wrunlock(); - bdrv_drain_all_end(); s->error = 0; } else { diff --git a/block/snapshot.c b/block/snapshot.c index 28c9c43621..bd9d759b32 100644 --- a/block/snapshot.c +++ b/block/snapshot.c @@ -291,11 +291,9 @@ int bdrv_snapshot_goto(BlockDriverState *bs, } /* .bdrv_open() will re-attach it */ - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_unref_child(bs, fallback); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); ret = bdrv_snapshot_goto(fallback_bs, snapshot_id, errp); memset(bs->opaque, 0, drv->instance_size); diff --git a/block/stream.c b/block/stream.c index f5441f27f4..a6ef840e29 100644 --- a/block/stream.c +++ b/block/stream.c @@ -371,12 +371,10 @@ void stream_start(const char *job_id, BlockDriverState *bs, * already have our own plans. Also don't allow resize as the image size is * queried only at the job start and then cached. */ - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); if (block_job_add_bdrv(&s->common, "active node", bs, 0, basic_flags | BLK_PERM_WRITE, errp)) { bdrv_graph_wrunlock(); - bdrv_drain_all_end(); goto fail; } @@ -397,12 +395,10 @@ void stream_start(const char *job_id, BlockDriverState *bs, basic_flags, errp); if (ret < 0) { bdrv_graph_wrunlock(); - bdrv_drain_all_end(); goto fail; } } bdrv_graph_wrunlock(); - bdrv_drain_all_end(); s->base_overlay = base_overlay; s->above_base = above_base; diff --git a/block/vmdk.c b/block/vmdk.c index 89a7250120..04986c8d55 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -271,8 +271,7 @@ static void vmdk_free_extents(BlockDriverState *bs) BDRVVmdkState *s = bs->opaque; VmdkExtent *e; - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); for (i = 0; i < s->num_extents; i++) { e = &s->extents[i]; g_free(e->l1_table); @@ -284,7 +283,6 @@ static void vmdk_free_extents(BlockDriverState *bs) } } bdrv_graph_wrunlock(); - bdrv_drain_all_end(); g_free(s->extents); } @@ -1249,11 +1247,9 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, 0, 0, 0, 0, 0, &extent, errp); if (ret < 0) { bdrv_graph_rdunlock_main_loop(); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_unref_child(bs, extent_file); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); bdrv_graph_rdlock_main_loop(); goto out; } @@ -1270,11 +1266,9 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, g_free(buf); if (ret) { bdrv_graph_rdunlock_main_loop(); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_unref_child(bs, extent_file); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); bdrv_graph_rdlock_main_loop(); goto out; } @@ -1283,11 +1277,9 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, ret = vmdk_open_se_sparse(bs, extent_file, bs->open_flags, errp); if (ret) { bdrv_graph_rdunlock_main_loop(); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_unref_child(bs, extent_file); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); bdrv_graph_rdlock_main_loop(); goto out; } @@ -1295,11 +1287,9 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, } else { error_setg(errp, "Unsupported extent type '%s'", type); bdrv_graph_rdunlock_main_loop(); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_unref_child(bs, extent_file); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); bdrv_graph_rdlock_main_loop(); ret = -ENOTSUP; goto out; diff --git a/blockdev.c b/blockdev.c index 2e7fda6780..e625534925 100644 --- a/blockdev.c +++ b/blockdev.c @@ -3561,8 +3561,7 @@ void qmp_x_blockdev_change(const char *parent, const char *child, BlockDriverState *parent_bs, *new_bs = NULL; BdrvChild *p_child; - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); parent_bs = bdrv_lookup_bs(parent, parent, errp); if (!parent_bs) { @@ -3599,7 +3598,6 @@ void qmp_x_blockdev_change(const char *parent, const char *child, out: bdrv_graph_wrunlock(); - bdrv_drain_all_end(); } BlockJobInfoList *qmp_query_block_jobs(Error **errp) diff --git a/blockjob.c b/blockjob.c index e68181a35b..db7c3a69a0 100644 --- a/blockjob.c +++ b/blockjob.c @@ -198,8 +198,7 @@ void block_job_remove_all_bdrv(BlockJob *job) * one to make sure that such a concurrent access does not attempt * to process an already freed BdrvChild. */ - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); while (job->nodes) { GSList *l = job->nodes; BdrvChild *c = l->data; @@ -212,7 +211,6 @@ void block_job_remove_all_bdrv(BlockJob *job) g_slist_free_1(l); } bdrv_graph_wrunlock(); - bdrv_drain_all_end(); } bool block_job_has_bdrv(BlockJob *job, BlockDriverState *bs) @@ -498,8 +496,7 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, int ret; GLOBAL_STATE_CODE(); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); if (job_id == NULL && !(flags & JOB_INTERNAL)) { job_id = bdrv_get_device_name(bs); @@ -509,7 +506,6 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, flags, cb, opaque, errp); if (job == NULL) { bdrv_graph_wrunlock(); - bdrv_drain_all_end(); return NULL; } @@ -548,12 +544,10 @@ void *block_job_create(const char *job_id, const BlockJobDriver *driver, } bdrv_graph_wrunlock(); - bdrv_drain_all_end(); return job; fail: bdrv_graph_wrunlock(); - bdrv_drain_all_end(); job_early_fail(&job->job); return NULL; } diff --git a/include/block/graph-lock.h b/include/block/graph-lock.h index 2c26c72108..95bf5ede40 100644 --- a/include/block/graph-lock.h +++ b/include/block/graph-lock.h @@ -112,10 +112,21 @@ void unregister_aiocontext(AioContext *ctx); void no_coroutine_fn TSA_ACQUIRE(graph_lock) TSA_NO_TSA bdrv_graph_wrlock(void); +/* + * bdrv_graph_wrlock_drained: + * Similar to bdrv_graph_wrlock, but will begin a drained section before + * locking. + */ +void no_coroutine_fn TSA_ACQUIRE(graph_lock) TSA_NO_TSA +bdrv_graph_wrlock_drained(void); + /* * bdrv_graph_wrunlock: * Write finished, reset global has_writer to 0 and restart * all readers that are waiting. + * + * Also ends the drained section if bdrv_graph_wrlock_drained() was used to lock + * the graph. */ void no_coroutine_fn TSA_RELEASE(graph_lock) TSA_NO_TSA bdrv_graph_wrunlock(void); diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 59c2793725..3369c2c2aa 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -772,11 +772,9 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, tjob->bs = src; job = &tjob->common; - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); block_job_add_bdrv(job, "target", target, 0, BLK_PERM_ALL, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); switch (result) { case TEST_JOB_SUCCESS: @@ -955,13 +953,11 @@ static void bdrv_test_top_close(BlockDriverState *bs) { BdrvChild *c, *next_c; - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); QLIST_FOREACH_SAFE(c, &bs->children, next, next_c) { bdrv_unref_child(bs, c); } bdrv_graph_wrunlock(); - bdrv_drain_all_end(); } static int coroutine_fn GRAPH_RDLOCK @@ -1053,12 +1049,10 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, &error_abort); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); /* This child will be the one to pass to requests through to, and * it will stall until a drain occurs */ @@ -1066,25 +1060,21 @@ static void do_test_delete_by_drain(bool detach_instead_of_delete, &error_abort); child_bs->total_sectors = 65536 >> BDRV_SECTOR_BITS; /* Takes our reference to child_bs */ - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); tts->wait_child = bdrv_attach_child(bs, child_bs, "wait-child", &child_of_bds, BDRV_CHILD_DATA | BDRV_CHILD_PRIMARY, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); /* This child is just there to be deleted * (for detach_instead_of_delete == true) */ null_bs = bdrv_open("null-co://", NULL, NULL, BDRV_O_RDWR | BDRV_O_PROTOCOL, &error_abort); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_attach_child(bs, null_bs, "null-child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); blk = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); blk_insert_bs(blk, bs, &error_abort); @@ -1167,8 +1157,7 @@ static void no_coroutine_fn detach_indirect_bh(void *opaque) bdrv_dec_in_flight(data->child_b->bs); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_unref_child(data->parent_b, data->child_b); bdrv_ref(data->c); @@ -1176,7 +1165,6 @@ static void no_coroutine_fn detach_indirect_bh(void *opaque) &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); } static void coroutine_mixed_fn detach_by_parent_aio_cb(void *opaque, int ret) @@ -1274,8 +1262,7 @@ static void TSA_NO_TSA test_detach_indirect(bool by_parent_cb) /* Set child relationships */ bdrv_ref(b); bdrv_ref(a); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); child_b = bdrv_attach_child(parent_b, b, "PB-B", &child_of_bds, BDRV_CHILD_DATA, &error_abort); child_a = bdrv_attach_child(parent_b, a, "PB-A", &child_of_bds, @@ -1286,7 +1273,6 @@ static void TSA_NO_TSA test_detach_indirect(bool by_parent_cb) by_parent_cb ? &child_of_bds : &detach_by_driver_cb_class, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); g_assert_cmpint(parent_a->refcnt, ==, 1); g_assert_cmpint(parent_b->refcnt, ==, 1); @@ -1699,8 +1685,7 @@ static void test_drop_intermediate_poll(void) * Establish the chain last, so the chain links are the first * elements in the BDS.parents lists */ - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); for (i = 0; i < 3; i++) { if (i) { /* Takes the reference to chain[i - 1] */ @@ -1709,7 +1694,6 @@ static void test_drop_intermediate_poll(void) } } bdrv_graph_wrunlock(); - bdrv_drain_all_end(); job = block_job_create("job", &test_simple_job_driver, NULL, job_node, 0, BLK_PERM_ALL, 0, 0, NULL, NULL, &error_abort); @@ -1956,12 +1940,10 @@ static void do_test_replace_child_mid_drain(int old_drain_count, new_child_bs->total_sectors = 1; bdrv_ref(old_child_bs); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_attach_child(parent_bs, old_child_bs, "child", &child_of_bds, BDRV_CHILD_COW, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); parent_s->setup_completed = true; for (i = 0; i < old_drain_count; i++) { diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c index 7b03ebe4b0..b077f0e3e3 100644 --- a/tests/unit/test-bdrv-graph-mod.c +++ b/tests/unit/test-bdrv-graph-mod.c @@ -137,12 +137,10 @@ static void test_update_perm_tree(void) blk_insert_bs(root, bs, &error_abort); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_attach_child(filter, bs, "child", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); ret = bdrv_append(filter, bs, NULL); g_assert_cmpint(ret, <, 0); @@ -206,13 +204,11 @@ static void test_should_update_child(void) bdrv_set_backing_hd(target, bs, &error_abort); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); g_assert(target->backing->bs == bs); bdrv_attach_child(filter, target, "target", &child_of_bds, BDRV_CHILD_DATA, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); bdrv_append(filter, bs, &error_abort); bdrv_graph_rdlock_main_loop(); @@ -248,8 +244,7 @@ static void test_parallel_exclusive_write(void) bdrv_ref(base); bdrv_ref(fl1); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_attach_child(top, fl1, "backing", &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); @@ -262,7 +257,6 @@ static void test_parallel_exclusive_write(void) bdrv_replace_node(fl1, fl2, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); bdrv_drained_end(fl2); bdrv_drained_end(fl1); @@ -369,8 +363,7 @@ static void test_parallel_perm_update(void) */ bdrv_ref(base); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_attach_child(top, ws, "file", &child_of_bds, BDRV_CHILD_DATA, &error_abort); c_fl1 = bdrv_attach_child(ws, fl1, "first", &child_of_bds, @@ -384,7 +377,6 @@ static void test_parallel_perm_update(void) BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); /* Select fl1 as first child to be active */ s->selected = c_fl1; @@ -438,13 +430,11 @@ static void test_append_greedy_filter(void) BlockDriverState *base = no_perm_node("base"); BlockDriverState *fl = exclusive_writer_node("fl1"); - bdrv_drain_all_begin(); - bdrv_graph_wrlock(); + bdrv_graph_wrlock_drained(); bdrv_attach_child(top, base, "backing", &child_of_bds, BDRV_CHILD_FILTERED | BDRV_CHILD_PRIMARY, &error_abort); bdrv_graph_wrunlock(); - bdrv_drain_all_end(); bdrv_append(fl, base, &error_abort); bdrv_unref(fl); From 9918b2e95ed55680e5b2016149dd51610c698611 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:02 +0200 Subject: [PATCH 2311/2760] block/mirror: switch to bdrv_set_backing_hd_drained() variant This is in preparation to mark bdrv_set_backing_hd() as GRAPH_UNLOCKED. Switch to using the bdrv_set_backing_hd_drained() variant, so that the drained and locked section can also cover the calls to bdrv_skip_filters() and bdrv_cow_bs(). Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-26-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/mirror.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/block/mirror.c b/block/mirror.c index 82a6e50cf8..873e95d029 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -761,30 +761,36 @@ static int mirror_exit_common(Job *job) bdrv_graph_rdlock_main_loop(); bdrv_child_refresh_perms(mirror_top_bs, mirror_top_bs->backing, &error_abort); + bdrv_graph_rdunlock_main_loop(); if (!abort && s->backing_mode == MIRROR_SOURCE_BACKING_CHAIN) { BlockDriverState *backing; - BlockDriverState *unfiltered_target = bdrv_skip_filters(target_bs); + BlockDriverState *unfiltered_target; + + bdrv_graph_wrlock_drained(); + unfiltered_target = bdrv_skip_filters(target_bs); backing = s->sync_mode == MIRROR_SYNC_MODE_NONE ? src : s->base; if (bdrv_cow_bs(unfiltered_target) != backing) { - bdrv_set_backing_hd(unfiltered_target, backing, &local_err); + bdrv_set_backing_hd_drained(unfiltered_target, backing, &local_err); if (local_err) { error_report_err(local_err); local_err = NULL; ret = -EPERM; } } + bdrv_graph_wrunlock(); } else if (!abort && s->backing_mode == MIRROR_OPEN_BACKING_CHAIN) { + bdrv_graph_rdlock_main_loop(); assert(!bdrv_backing_chain_next(target_bs)); ret = bdrv_open_backing_file(bdrv_skip_filters(target_bs), NULL, "backing", &local_err); + bdrv_graph_rdunlock_main_loop(); if (ret < 0) { error_report_err(local_err); local_err = NULL; } } - bdrv_graph_rdunlock_main_loop(); if (s->should_complete && !abort) { BlockDriverState *to_replace = s->to_replace ?: src; From 47bc2ed6f6a8eb637ede90c0545bca082b68379f Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:03 +0200 Subject: [PATCH 2312/2760] block/commit: switch to bdrv_set_backing_hd_drained() variant This is in preparation to mark bdrv_set_backing_hd() as GRAPH_UNLOCKED. Switch to using the bdrv_set_backing_hd_drained() variant. For the first pair of calls to avoid draining and locking twice in a row within the individual calls. For the third call, so that the drained and locked section can also cover bdrv_cow_bs(). Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-27-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/commit.c | 32 +++++++++++++++++++++++++------- 1 file changed, 25 insertions(+), 7 deletions(-) diff --git a/block/commit.c b/block/commit.c index dc1942483b..c9690a5da0 100644 --- a/block/commit.c +++ b/block/commit.c @@ -514,28 +514,32 @@ int bdrv_commit(BlockDriverState *bs) Error *local_err = NULL; GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!drv) return -ENOMEDIUM; + bdrv_graph_rdlock_main_loop(); + backing_file_bs = bdrv_cow_bs(bs); if (!backing_file_bs) { - return -ENOTSUP; + ret = -ENOTSUP; + goto out; } if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_COMMIT_SOURCE, NULL) || bdrv_op_is_blocked(backing_file_bs, BLOCK_OP_TYPE_COMMIT_TARGET, NULL)) { - return -EBUSY; + ret = -EBUSY; + goto out; } ro = bdrv_is_read_only(backing_file_bs); if (ro) { if (bdrv_reopen_set_read_only(backing_file_bs, false, NULL)) { - return -EACCES; + ret = -EACCES; + goto out; } } @@ -559,8 +563,14 @@ int bdrv_commit(BlockDriverState *bs) goto ro_cleanup; } - bdrv_set_backing_hd(commit_top_bs, backing_file_bs, &error_abort); - bdrv_set_backing_hd(bs, commit_top_bs, &error_abort); + bdrv_graph_rdunlock_main_loop(); + + bdrv_graph_wrlock_drained(); + bdrv_set_backing_hd_drained(commit_top_bs, backing_file_bs, &error_abort); + bdrv_set_backing_hd_drained(bs, commit_top_bs, &error_abort); + bdrv_graph_wrunlock(); + + bdrv_graph_rdlock_main_loop(); ret = blk_insert_bs(backing, backing_file_bs, &local_err); if (ret < 0) { @@ -635,9 +645,14 @@ int bdrv_commit(BlockDriverState *bs) ret = 0; ro_cleanup: blk_unref(backing); + + bdrv_graph_rdunlock_main_loop(); + bdrv_graph_wrlock_drained(); if (bdrv_cow_bs(bs) != backing_file_bs) { - bdrv_set_backing_hd(bs, backing_file_bs, &error_abort); + bdrv_set_backing_hd_drained(bs, backing_file_bs, &error_abort); } + bdrv_graph_wrunlock(); + bdrv_graph_rdlock_main_loop(); bdrv_unref(commit_top_bs); blk_unref(src); @@ -646,5 +661,8 @@ int bdrv_commit(BlockDriverState *bs) bdrv_reopen_set_read_only(backing_file_bs, true, NULL); } +out: + bdrv_graph_rdunlock_main_loop(); + return ret; } From d7573eba14b214d20340eed6880fb8f5cf90d051 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:04 +0200 Subject: [PATCH 2313/2760] block: call bdrv_set_backing_hd() while unlocked in bdrv_open_backing_file() This is in preparation to mark bdrv_set_backing_hd() as GRAPH_UNLOCKED. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-28-f.ebner@proxmox.com> [kwolf: Removed an extra blank line] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/block.c b/block.c index 1da10d55f0..9ef3b0262c 100644 --- a/block.c +++ b/block.c @@ -3632,7 +3632,8 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, Error *local_err = NULL; GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); + + bdrv_graph_rdlock_main_loop(); if (bs->backing != NULL) { goto free_exit; @@ -3713,7 +3714,9 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, /* Hook up the backing file link; drop our reference, bs owns the * backing_hd reference now */ + bdrv_graph_rdunlock_main_loop(); ret = bdrv_set_backing_hd(bs, backing_hd, errp); + bdrv_graph_rdlock_main_loop(); bdrv_unref(backing_hd); if (ret < 0) { @@ -3725,6 +3728,7 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, free_exit: g_free(backing_filename); qobject_unref(tmp_parent_options); + bdrv_graph_rdunlock_main_loop(); return ret; } From de0d24c711f6d4deaf51de2d5001c0516a10ef22 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:05 +0200 Subject: [PATCH 2314/2760] block: mark bdrv_set_backing_hd() as GRAPH_UNLOCKED The function bdrv_set_backing_hd() calls bdrv_drain_all_begin(), which must be called with the graph unlocked. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-29-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/block-global-state.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 84a2a4ecd5..009b9ac946 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -100,8 +100,9 @@ bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp); BlockDriverState * coroutine_fn no_co_wrapper bdrv_co_open_blockdev_ref(BlockdevRef *ref, Error **errp); -int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, - Error **errp); +int GRAPH_UNLOCKED +bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, + Error **errp); int GRAPH_WRLOCK bdrv_set_backing_hd_drained(BlockDriverState *bs, BlockDriverState *backing_hd, Error **errp); From c7af387c7b58f8ffcd412b175768f6ccd591b1fc Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:06 +0200 Subject: [PATCH 2315/2760] blockdev: avoid locking and draining multiple times in external_snapshot_abort() By using the appropriate variants bdrv_set_backing_hd_drained() and bdrv_try_change_aio_context_locked(), there only needs to be a single drained and write-locked section in external_snapshot_abort(). Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-30-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- blockdev.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/blockdev.c b/blockdev.c index e625534925..3c53472a23 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1580,11 +1580,19 @@ static void external_snapshot_abort(void *opaque) AioContext *tmp_context; int ret; + bdrv_graph_wrlock_drained(); + aio_context = bdrv_get_aio_context(state->old_bs); - bdrv_ref(state->old_bs); /* we can't let bdrv_set_backind_hd() - close state->old_bs; we need it */ - bdrv_set_backing_hd(state->new_bs, NULL, &error_abort); + /* + * Note that state->old_bs would not disappear during the + * write-locked section, because the unref from + * bdrv_set_backing_hd_drained() only happens at the end of the + * write-locked section. However, just be explicit about keeping a + * reference and don't rely on that implicit detail. + */ + bdrv_ref(state->old_bs); + bdrv_set_backing_hd_drained(state->new_bs, NULL, &error_abort); /* * The call to bdrv_set_backing_hd() above returns state->old_bs to @@ -1593,16 +1601,14 @@ static void external_snapshot_abort(void *opaque) */ tmp_context = bdrv_get_aio_context(state->old_bs); if (aio_context != tmp_context) { - ret = bdrv_try_change_aio_context(state->old_bs, - aio_context, NULL, NULL); + ret = bdrv_try_change_aio_context_locked(state->old_bs, + aio_context, NULL, + NULL); assert(ret == 0); } - bdrv_drained_begin(state->new_bs); - bdrv_graph_wrlock(); bdrv_replace_node(state->new_bs, state->old_bs, &error_abort); bdrv_graph_wrunlock(); - bdrv_drained_end(state->new_bs); bdrv_unref(state->old_bs); /* bdrv_replace_node() ref'ed old_bs */ } From 54eb59d668d6e4e7584188628ca44f3e9bd39d17 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:07 +0200 Subject: [PATCH 2316/2760] block: drop wrapper for bdrv_set_backing_hd_drained() Nearly all callers (outside of the tests) are already using the _drained() variant of the function. It doesn't seem worth keeping. Simply adapt the remaining callers of bdrv_set_backing_hd() and rename bdrv_set_backing_hd_drained() to bdrv_set_backing_hd(). Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-31-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 20 ++++---------------- block/commit.c | 6 +++--- block/mirror.c | 2 +- block/stream.c | 13 ++++++------- blockdev.c | 13 ++++++++----- include/block/block-global-state.h | 5 +---- tests/unit/test-bdrv-drain.c | 12 +++++++++++- tests/unit/test-bdrv-graph-mod.c | 2 +- 8 files changed, 35 insertions(+), 38 deletions(-) diff --git a/block.c b/block.c index 9ef3b0262c..4754705bfd 100644 --- a/block.c +++ b/block.c @@ -3570,9 +3570,8 @@ bdrv_set_file_or_backing_noperm(BlockDriverState *parent_bs, * * All block nodes must be drained. */ -int bdrv_set_backing_hd_drained(BlockDriverState *bs, - BlockDriverState *backing_hd, - Error **errp) +int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, + Error **errp) { int ret; Transaction *tran = tran_new(); @@ -3594,19 +3593,6 @@ int bdrv_set_backing_hd_drained(BlockDriverState *bs, return ret; } -int bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, - Error **errp) -{ - int ret; - GLOBAL_STATE_CODE(); - - bdrv_graph_wrlock_drained(); - ret = bdrv_set_backing_hd_drained(bs, backing_hd, errp); - bdrv_graph_wrunlock(); - - return ret; -} - /* * Opens the backing file for a BlockDriverState if not yet open * @@ -3715,7 +3701,9 @@ int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, /* Hook up the backing file link; drop our reference, bs owns the * backing_hd reference now */ bdrv_graph_rdunlock_main_loop(); + bdrv_graph_wrlock_drained(); ret = bdrv_set_backing_hd(bs, backing_hd, errp); + bdrv_graph_wrunlock(); bdrv_graph_rdlock_main_loop(); bdrv_unref(backing_hd); diff --git a/block/commit.c b/block/commit.c index c9690a5da0..7496cf732e 100644 --- a/block/commit.c +++ b/block/commit.c @@ -566,8 +566,8 @@ int bdrv_commit(BlockDriverState *bs) bdrv_graph_rdunlock_main_loop(); bdrv_graph_wrlock_drained(); - bdrv_set_backing_hd_drained(commit_top_bs, backing_file_bs, &error_abort); - bdrv_set_backing_hd_drained(bs, commit_top_bs, &error_abort); + bdrv_set_backing_hd(commit_top_bs, backing_file_bs, &error_abort); + bdrv_set_backing_hd(bs, commit_top_bs, &error_abort); bdrv_graph_wrunlock(); bdrv_graph_rdlock_main_loop(); @@ -649,7 +649,7 @@ int bdrv_commit(BlockDriverState *bs) bdrv_graph_rdunlock_main_loop(); bdrv_graph_wrlock_drained(); if (bdrv_cow_bs(bs) != backing_file_bs) { - bdrv_set_backing_hd_drained(bs, backing_file_bs, &error_abort); + bdrv_set_backing_hd(bs, backing_file_bs, &error_abort); } bdrv_graph_wrunlock(); bdrv_graph_rdlock_main_loop(); diff --git a/block/mirror.c b/block/mirror.c index 873e95d029..b344182c74 100644 --- a/block/mirror.c +++ b/block/mirror.c @@ -772,7 +772,7 @@ static int mirror_exit_common(Job *job) backing = s->sync_mode == MIRROR_SYNC_MODE_NONE ? src : s->base; if (bdrv_cow_bs(unfiltered_target) != backing) { - bdrv_set_backing_hd_drained(unfiltered_target, backing, &local_err); + bdrv_set_backing_hd(unfiltered_target, backing, &local_err); if (local_err) { error_report_err(local_err); local_err = NULL; diff --git a/block/stream.c b/block/stream.c index a6ef840e29..17e240460c 100644 --- a/block/stream.c +++ b/block/stream.c @@ -73,12 +73,11 @@ static int stream_prepare(Job *job) s->cor_filter_bs = NULL; /* - * bdrv_set_backing_hd() requires that the unfiltered_bs and the COW child - * of unfiltered_bs is drained. Drain already here and use - * bdrv_set_backing_hd_drained() instead because the polling during - * drained_begin() might change the graph, and if we do this only later, we - * may end up working with the wrong base node (or it might even have gone - * away by the time we want to use it). + * bdrv_set_backing_hd() requires that all block nodes are drained. Drain + * already here, because the polling during drained_begin() might change the + * graph, and if we do this only later, we may end up working with the wrong + * base node (or it might even have gone away by the time we want to use + * it). */ if (unfiltered_bs_cow) { bdrv_ref(unfiltered_bs_cow); @@ -105,7 +104,7 @@ static int stream_prepare(Job *job) } bdrv_graph_wrlock(); - bdrv_set_backing_hd_drained(unfiltered_bs, base, &local_err); + bdrv_set_backing_hd(unfiltered_bs, base, &local_err); bdrv_graph_wrunlock(); /* diff --git a/blockdev.c b/blockdev.c index 3c53472a23..9f3f42d896 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1587,12 +1587,12 @@ static void external_snapshot_abort(void *opaque) /* * Note that state->old_bs would not disappear during the * write-locked section, because the unref from - * bdrv_set_backing_hd_drained() only happens at the end of the - * write-locked section. However, just be explicit about keeping a - * reference and don't rely on that implicit detail. + * bdrv_set_backing_hd() only happens at the end of the write-locked + * section. However, just be explicit about keeping a reference and + * don't rely on that implicit detail. */ bdrv_ref(state->old_bs); - bdrv_set_backing_hd_drained(state->new_bs, NULL, &error_abort); + bdrv_set_backing_hd(state->new_bs, NULL, &error_abort); /* * The call to bdrv_set_backing_hd() above returns state->old_bs to @@ -1776,7 +1776,10 @@ static void drive_backup_action(DriveBackup *backup, } if (set_backing_hd) { - if (bdrv_set_backing_hd(target_bs, source, errp) < 0) { + bdrv_graph_wrlock_drained(); + ret = bdrv_set_backing_hd(target_bs, source, errp); + bdrv_graph_wrunlock(); + if (ret < 0) { goto unref; } } diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 009b9ac946..bcbb624a7b 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -100,12 +100,9 @@ bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp); BlockDriverState * coroutine_fn no_co_wrapper bdrv_co_open_blockdev_ref(BlockdevRef *ref, Error **errp); -int GRAPH_UNLOCKED +int GRAPH_WRLOCK bdrv_set_backing_hd(BlockDriverState *bs, BlockDriverState *backing_hd, Error **errp); -int GRAPH_WRLOCK -bdrv_set_backing_hd_drained(BlockDriverState *bs, BlockDriverState *backing_hd, - Error **errp); int bdrv_open_backing_file(BlockDriverState *bs, QDict *parent_options, const char *bdref_key, Error **errp); diff --git a/tests/unit/test-bdrv-drain.c b/tests/unit/test-bdrv-drain.c index 3369c2c2aa..43b0ba8648 100644 --- a/tests/unit/test-bdrv-drain.c +++ b/tests/unit/test-bdrv-drain.c @@ -193,7 +193,9 @@ static BlockBackend * no_coroutine_fn test_setup(void) blk_insert_bs(blk, bs, &error_abort); backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); + bdrv_graph_wrlock_drained(); bdrv_set_backing_hd(bs, backing, &error_abort); + bdrv_graph_wrunlock(); bdrv_unref(backing); bdrv_unref(bs); @@ -386,7 +388,9 @@ static void test_nested(void) backing = bdrv_new_open_driver(&bdrv_test, "backing", 0, &error_abort); backing_s = backing->opaque; + bdrv_graph_wrlock_drained(); bdrv_set_backing_hd(bs, backing, &error_abort); + bdrv_graph_wrunlock(); for (outer = 0; outer < DRAIN_TYPE_MAX; outer++) { for (inner = 0; inner < DRAIN_TYPE_MAX; inner++) { @@ -733,10 +737,12 @@ static void test_blockjob_common_drain_node(enum drain_type drain_type, src_overlay = bdrv_new_open_driver(&bdrv_test, "source-overlay", BDRV_O_RDWR, &error_abort); + bdrv_graph_wrlock_drained(); bdrv_set_backing_hd(src_overlay, src, &error_abort); bdrv_unref(src); bdrv_set_backing_hd(src, src_backing, &error_abort); bdrv_unref(src_backing); + bdrv_graph_wrunlock(); blk_src = blk_new(qemu_get_aio_context(), BLK_PERM_ALL, BLK_PERM_ALL); blk_insert_bs(blk_src, src_overlay, &error_abort); @@ -1436,8 +1442,10 @@ static void test_drop_backing_job_commit(Job *job) TestDropBackingBlockJob *s = container_of(job, TestDropBackingBlockJob, common.job); + bdrv_graph_wrlock_drained(); bdrv_set_backing_hd(s->bs, NULL, &error_abort); bdrv_set_backing_hd(s->detach_also, NULL, &error_abort); + bdrv_graph_wrunlock(); *s->did_complete = true; } @@ -1530,7 +1538,9 @@ static void test_blockjob_commit_by_drained_end(void) snprintf(name, sizeof(name), "parent-node-%i", i); bs_parents[i] = bdrv_new_open_driver(&bdrv_test, name, BDRV_O_RDWR, &error_abort); + bdrv_graph_wrlock_drained(); bdrv_set_backing_hd(bs_parents[i], bs_child, &error_abort); + bdrv_graph_wrunlock(); } job = block_job_create("job", &test_drop_backing_job_driver, NULL, @@ -1679,13 +1689,13 @@ static void test_drop_intermediate_poll(void) job_node = bdrv_new_open_driver(&bdrv_test, "job-node", BDRV_O_RDWR, &error_abort); + bdrv_graph_wrlock_drained(); bdrv_set_backing_hd(job_node, chain[1], &error_abort); /* * Establish the chain last, so the chain links are the first * elements in the BDS.parents lists */ - bdrv_graph_wrlock_drained(); for (i = 0; i < 3; i++) { if (i) { /* Takes the reference to chain[i - 1] */ diff --git a/tests/unit/test-bdrv-graph-mod.c b/tests/unit/test-bdrv-graph-mod.c index b077f0e3e3..567db99e4f 100644 --- a/tests/unit/test-bdrv-graph-mod.c +++ b/tests/unit/test-bdrv-graph-mod.c @@ -202,9 +202,9 @@ static void test_should_update_child(void) blk_insert_bs(root, bs, &error_abort); + bdrv_graph_wrlock_drained(); bdrv_set_backing_hd(target, bs, &error_abort); - bdrv_graph_wrlock_drained(); g_assert(target->backing->bs == bs); bdrv_attach_child(filter, target, "target", &child_of_bds, BDRV_CHILD_DATA, &error_abort); From 9ec8c4793f0f5a6f1d342a5d5eebeec516e3c107 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:08 +0200 Subject: [PATCH 2317/2760] block-backend: mark blk_drain_all() as GRAPH_UNLOCKED The function blk_drain_all() calls bdrv_drain_all_begin(), which must be called with the graph unlocked. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-32-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/system/block-backend-global-state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/system/block-backend-global-state.h b/include/system/block-backend-global-state.h index 35b5e8380b..a62dbdf0dc 100644 --- a/include/system/block-backend-global-state.h +++ b/include/system/block-backend-global-state.h @@ -79,7 +79,7 @@ void blk_aio_cancel(BlockAIOCB *acb); int blk_commit_all(void); bool blk_in_drain(BlockBackend *blk); void blk_drain(BlockBackend *blk); -void blk_drain_all(void); +void GRAPH_UNLOCKED blk_drain_all(void); void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, BlockdevOnError on_write_error); bool blk_supports_write_perm(BlockBackend *blk); From c6b5328b5b81a358df766094a361b747eadc55a8 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:09 +0200 Subject: [PATCH 2318/2760] block/snapshot: mark bdrv_all_delete_snapshot() as GRAPH_UNLOCKED The function bdrv_all_delete_snapshot() calls bdrv_drain_all_begin(), which must be called with the graph unlocked. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-33-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/snapshot.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/block/snapshot.h b/include/block/snapshot.h index 304cc6ea61..2316a43e26 100644 --- a/include/block/snapshot.h +++ b/include/block/snapshot.h @@ -90,9 +90,9 @@ int bdrv_snapshot_load_tmp_by_id_or_name(BlockDriverState *bs, bool bdrv_all_can_snapshot(bool has_devices, strList *devices, Error **errp); -int bdrv_all_delete_snapshot(const char *name, - bool has_devices, strList *devices, - Error **errp); +int GRAPH_UNLOCKED +bdrv_all_delete_snapshot(const char *name, bool has_devices, strList *devices, + Error **errp); int bdrv_all_goto_snapshot(const char *name, bool has_devices, strList *devices, Error **errp); From 0a0474b065b97e9a25c2723bde87afbc77ba4bd9 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:10 +0200 Subject: [PATCH 2319/2760] block/stream: mark stream_prepare() as GRAPH_UNLOCKED The function stream_prepare() calls bdrv_drain_all_begin(), which must be called with the graph unlocked. Also mark the JobDriver's prepare() callback as GRAPH_UNLOCKED_PTR, because that is the callback via which stream_prepare() is reached. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-34-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/stream.c | 2 +- include/qemu/job.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/block/stream.c b/block/stream.c index 17e240460c..c0616b69e2 100644 --- a/block/stream.c +++ b/block/stream.c @@ -51,7 +51,7 @@ static int coroutine_fn stream_populate(BlockBackend *blk, return blk_co_preadv(blk, offset, bytes, NULL, BDRV_REQ_PREFETCH); } -static int stream_prepare(Job *job) +static int GRAPH_UNLOCKED stream_prepare(Job *job) { StreamBlockJob *s = container_of(job, StreamBlockJob, common.job); BlockDriverState *unfiltered_bs; diff --git a/include/qemu/job.h b/include/qemu/job.h index a5a04155ea..bb8ee766ef 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -263,7 +263,7 @@ struct JobDriver { * This callback will not be invoked if the job has already failed. * If it fails, abort and then clean will be called. */ - int (*prepare)(Job *job); + int GRAPH_UNLOCKED_PTR (*prepare)(Job *job); /** * If the callback is not NULL, it will be invoked when all the jobs From 6717dc307523c9e57b0d6c7dd302eab764c19265 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:11 +0200 Subject: [PATCH 2320/2760] block: mark bdrv_reopen_queue() and bdrv_reopen_multiple() as GRAPH_UNLOCKED The function bdrv_reopen_queue() can call bdrv_drain_all_begin(), which must be called with the graph unlocked. The function bdrv_reopen_multiple() calls bdrv_reopen_prepare() which must be called with the graph unlocked. To mark bdrv_reopen_queue() as GRAPH_UNLOCKED, it is necessary to make the locked section in reopen_backing_file() shorter. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-35-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/replication.c | 3 ++- include/block/block-global-state.h | 9 +++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/block/replication.c b/block/replication.c index 83978b61f5..3a431e908c 100644 --- a/block/replication.c +++ b/block/replication.c @@ -364,14 +364,15 @@ static void reopen_backing_file(BlockDriverState *bs, bool writable, BlockReopenQueue *reopen_queue = NULL; GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); + bdrv_graph_rdlock_main_loop(); /* * s->hidden_disk and s->secondary_disk may not be set yet, as they will * only be set after the children are writable. */ hidden_disk = bs->file->bs->backing; secondary_disk = hidden_disk->bs->backing; + bdrv_graph_rdunlock_main_loop(); if (writable) { s->orig_hidden_read_only = bdrv_is_read_only(hidden_disk->bs); diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index bcbb624a7b..f25c65c1b4 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -121,11 +121,12 @@ BlockDriverState *bdrv_new_open_driver_opts(BlockDriver *drv, Error **errp); BlockDriverState *bdrv_new_open_driver(BlockDriver *drv, const char *node_name, int flags, Error **errp); -BlockReopenQueue *bdrv_reopen_queue(BlockReopenQueue *bs_queue, - BlockDriverState *bs, QDict *options, - bool keep_old_opts); +BlockReopenQueue * GRAPH_UNLOCKED +bdrv_reopen_queue(BlockReopenQueue *bs_queue, BlockDriverState *bs, + QDict *options, bool keep_old_opts); void bdrv_reopen_queue_free(BlockReopenQueue *bs_queue); -int bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); +int GRAPH_UNLOCKED +bdrv_reopen_multiple(BlockReopenQueue *bs_queue, Error **errp); int bdrv_reopen(BlockDriverState *bs, QDict *opts, bool keep_old_opts, Error **errp); int bdrv_reopen_set_read_only(BlockDriverState *bs, bool read_only, From e2d9cc57905eaabd2b212de2f77993935a7fc271 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:12 +0200 Subject: [PATCH 2321/2760] block: mark bdrv_inactivate() as GRAPH_RDLOCK and move drain to callers The function bdrv_inactivate() calls bdrv_drain_all_begin(), which needs to be called with the graph unlocked, so either bdrv_inactivate() should be marked as GRAPH_UNLOCKED or the drain needs to be moved to the callers. The caller in qmp_blockdev_set_active() requires that the locked section covers bdrv_find_node() too, so the latter alternative is chosen. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-36-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 14 ++---- blockdev.c | 73 ++++++++++++++++++------------ include/block/block-global-state.h | 2 +- 3 files changed, 50 insertions(+), 39 deletions(-) diff --git a/block.c b/block.c index 4754705bfd..932e599f45 100644 --- a/block.c +++ b/block.c @@ -7058,31 +7058,25 @@ bdrv_inactivate_recurse(BlockDriverState *bs, bool top_level) return 0; } +/* All block nodes must be drained. */ int bdrv_inactivate(BlockDriverState *bs, Error **errp) { int ret; GLOBAL_STATE_CODE(); - bdrv_drain_all_begin(); - bdrv_graph_rdlock_main_loop(); - if (bdrv_has_bds_parent(bs, true)) { error_setg(errp, "Node has active parent node"); - ret = -EPERM; - goto out; + return -EPERM; } ret = bdrv_inactivate_recurse(bs, true); if (ret < 0) { error_setg_errno(errp, -ret, "Failed to inactivate node"); - goto out; + return ret; } -out: - bdrv_graph_rdunlock_main_loop(); - bdrv_drain_all_end(); - return ret; + return 0; } int bdrv_inactivate_all(void) diff --git a/blockdev.c b/blockdev.c index 9f3f42d896..b451fee6e1 100644 --- a/blockdev.c +++ b/blockdev.c @@ -1421,7 +1421,7 @@ static void external_snapshot_action(TransactionAction *action, bdrv_graph_rdunlock_main_loop(); /* Paired with .clean() */ bdrv_drained_begin(state->old_bs); - GRAPH_RDLOCK_GUARD_MAINLOOP(); + bdrv_graph_rdlock_main_loop(); /* Make sure the associated bs did not change with the drain. */ check_bs = bdrv_lookup_bs(device, node_name, errp); @@ -1430,18 +1430,18 @@ static void external_snapshot_action(TransactionAction *action, error_setg(errp, "Block node of device '%s' unexpectedly changed", device); } /* else errp is already set */ - return; + goto unlock; } if (!bdrv_is_inserted(state->old_bs)) { error_setg(errp, "Device '%s' has no medium", bdrv_get_device_or_node_name(state->old_bs)); - return; + goto unlock; } if (bdrv_op_is_blocked(state->old_bs, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, errp)) { - return; + goto unlock; } if (!bdrv_is_read_only(state->old_bs)) { @@ -1449,7 +1449,7 @@ static void external_snapshot_action(TransactionAction *action, if (ret < 0) { error_setg_errno(errp, -ret, "Write to node '%s' failed", bdrv_get_device_or_node_name(state->old_bs)); - return; + goto unlock; } } @@ -1461,13 +1461,13 @@ static void external_snapshot_action(TransactionAction *action, if (node_name && !snapshot_node_name) { error_setg(errp, "New overlay node-name missing"); - return; + goto unlock; } if (snapshot_node_name && bdrv_lookup_bs(snapshot_node_name, snapshot_node_name, NULL)) { error_setg(errp, "New overlay node-name already in use"); - return; + goto unlock; } flags = state->old_bs->open_flags; @@ -1480,7 +1480,7 @@ static void external_snapshot_action(TransactionAction *action, int64_t size = bdrv_getlength(state->old_bs); if (size < 0) { error_setg_errno(errp, -size, "bdrv_getlength failed"); - return; + goto unlock; } bdrv_refresh_filename(state->old_bs); @@ -1491,7 +1491,7 @@ static void external_snapshot_action(TransactionAction *action, if (local_err) { error_propagate(errp, local_err); - return; + goto unlock; } } @@ -1507,7 +1507,7 @@ static void external_snapshot_action(TransactionAction *action, /* We will manually add the backing_hd field to the bs later */ if (!state->new_bs) { - return; + goto unlock; } /* @@ -1518,22 +1518,22 @@ static void external_snapshot_action(TransactionAction *action, bdrv_get_cumulative_perm(state->new_bs, &perm, &shared); if (perm & BLK_PERM_CONSISTENT_READ) { error_setg(errp, "The overlay is already in use"); - return; + goto unlock; } if (state->new_bs->drv->is_filter) { error_setg(errp, "Filters cannot be used as overlays"); - return; + goto unlock; } if (bdrv_cow_child(state->new_bs)) { error_setg(errp, "The overlay already has a backing image"); - return; + goto unlock; } if (!state->new_bs->drv->supports_backing) { error_setg(errp, "The overlay does not support backing images"); - return; + goto unlock; } /* @@ -1546,17 +1546,23 @@ static void external_snapshot_action(TransactionAction *action, * to keep this working. */ if (bdrv_is_inactive(state->old_bs) && !bdrv_is_inactive(state->new_bs)) { + bdrv_graph_rdunlock_main_loop(); + bdrv_drain_all_begin(); + bdrv_graph_rdlock_main_loop(); ret = bdrv_inactivate(state->new_bs, errp); + bdrv_drain_all_end(); if (ret < 0) { - return; + goto unlock; } } ret = bdrv_append(state->new_bs, state->old_bs, errp); if (ret < 0) { - return; + goto unlock; } state->overlay_appended = true; +unlock: + bdrv_graph_rdunlock_main_loop(); } static void external_snapshot_commit(void *opaque) @@ -3520,10 +3526,10 @@ void qmp_blockdev_del(const char *node_name, Error **errp) void qmp_blockdev_set_active(const char *node_name, bool active, Error **errp) { + BlockDriverState *bs; int ret; GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); if (!node_name) { if (active) { @@ -3534,19 +3540,30 @@ void qmp_blockdev_set_active(const char *node_name, bool active, Error **errp) error_setg_errno(errp, -ret, "Failed to inactivate all nodes"); } } + return; + } + + if (!active) { + bdrv_drain_all_begin(); + } + bdrv_graph_rdlock_main_loop(); + + bs = bdrv_find_node(node_name); + if (!bs) { + error_setg(errp, "Failed to find node with node-name='%s'", + node_name); + goto unlock; + } + if (active) { + bdrv_activate(bs, errp); } else { - BlockDriverState *bs = bdrv_find_node(node_name); - if (!bs) { - error_setg(errp, "Failed to find node with node-name='%s'", - node_name); - return; - } + bdrv_inactivate(bs, errp); + } - if (active) { - bdrv_activate(bs, errp); - } else { - bdrv_inactivate(bs, errp); - } +unlock: + bdrv_graph_rdunlock_main_loop(); + if (!active) { + bdrv_drain_all_end(); } } diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index f25c65c1b4..a641beb270 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -183,7 +183,7 @@ bdrv_activate(BlockDriverState *bs, Error **errp); int coroutine_fn no_co_wrapper_bdrv_rdlock bdrv_co_activate(BlockDriverState *bs, Error **errp); -int no_coroutine_fn +int no_coroutine_fn GRAPH_RDLOCK bdrv_inactivate(BlockDriverState *bs, Error **errp); void bdrv_activate_all(Error **errp); From 7525aa25dbee5213c10a325a4633fd80f577c440 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:13 +0200 Subject: [PATCH 2322/2760] block: mark bdrv_inactivate_all() as GRAPH_UNLOCKED The function bdrv_inactivate_all() calls bdrv_drain_all_begin(), which must be called with the graph unlocked. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-37-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/block-global-state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index a641beb270..eec92a98da 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -187,7 +187,7 @@ int no_coroutine_fn GRAPH_RDLOCK bdrv_inactivate(BlockDriverState *bs, Error **errp); void bdrv_activate_all(Error **errp); -int bdrv_inactivate_all(void); +int GRAPH_UNLOCKED bdrv_inactivate_all(void); int bdrv_flush_all(void); void bdrv_close_all(void); From b326b127df9eef8e41818ff88d33f99ea54af984 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:14 +0200 Subject: [PATCH 2323/2760] block: mark blk_remove_bs() as GRAPH_UNLOCKED The function blk_remove_bs() calls bdrv_graph_wrlock_drained() and can also call bdrv_drained_begin(), both of which which must be called with the graph unlocked. Marking blk_remove_bs() as GRAPH_UNLOCKED requires temporarily unlocking in hmp_drive_del(). Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-38-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/monitor/block-hmp-cmds.c | 15 ++++++++++----- include/system/block-backend-global-state.h | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/block/monitor/block-hmp-cmds.c b/block/monitor/block-hmp-cmds.c index 6919a49bf5..282d1c386e 100644 --- a/block/monitor/block-hmp-cmds.c +++ b/block/monitor/block-hmp-cmds.c @@ -144,7 +144,7 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) Error *local_err = NULL; GLOBAL_STATE_CODE(); - GRAPH_RDLOCK_GUARD_MAINLOOP(); + bdrv_graph_rdlock_main_loop(); bs = bdrv_find_node(id); if (bs) { @@ -152,29 +152,31 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) if (local_err) { error_report_err(local_err); } - return; + goto unlock; } blk = blk_by_name(id); if (!blk) { error_report("Device '%s' not found", id); - return; + goto unlock; } if (!blk_legacy_dinfo(blk)) { error_report("Deleting device added with blockdev-add" " is not supported"); - return; + goto unlock; } bs = blk_bs(blk); if (bs) { if (bdrv_op_is_blocked(bs, BLOCK_OP_TYPE_DRIVE_DEL, &local_err)) { error_report_err(local_err); - return; + goto unlock; } + bdrv_graph_rdunlock_main_loop(); blk_remove_bs(blk); + bdrv_graph_rdlock_main_loop(); } /* Make the BlockBackend and the attached BlockDriverState anonymous */ @@ -191,6 +193,9 @@ void hmp_drive_del(Monitor *mon, const QDict *qdict) } else { blk_unref(blk); } + +unlock: + bdrv_graph_rdunlock_main_loop(); } void hmp_commit(Monitor *mon, const QDict *qdict) diff --git a/include/system/block-backend-global-state.h b/include/system/block-backend-global-state.h index a62dbdf0dc..1a134083b7 100644 --- a/include/system/block-backend-global-state.h +++ b/include/system/block-backend-global-state.h @@ -55,7 +55,7 @@ void monitor_remove_blk(BlockBackend *blk); BlockBackendPublic *blk_get_public(BlockBackend *blk); -void blk_remove_bs(BlockBackend *blk); +void GRAPH_UNLOCKED blk_remove_bs(BlockBackend *blk); int blk_insert_bs(BlockBackend *blk, BlockDriverState *bs, Error **errp); int blk_replace_bs(BlockBackend *blk, BlockDriverState *new_bs, Error **errp); bool GRAPH_RDLOCK bdrv_has_blk(BlockDriverState *bs); From f3e84330f730b2f734d9fec6762d9f4ff570468d Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:15 +0200 Subject: [PATCH 2324/2760] block: mark blk_drain() as GRAPH_UNLOCKED The function blk_drain() calls bdrv_drained_begin(), which must be called with the graph unlocked. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-39-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/system/block-backend-global-state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/system/block-backend-global-state.h b/include/system/block-backend-global-state.h index 1a134083b7..f6ec1174e6 100644 --- a/include/system/block-backend-global-state.h +++ b/include/system/block-backend-global-state.h @@ -78,7 +78,7 @@ int blk_make_zero(BlockBackend *blk, BdrvRequestFlags flags); void blk_aio_cancel(BlockAIOCB *acb); int blk_commit_all(void); bool blk_in_drain(BlockBackend *blk); -void blk_drain(BlockBackend *blk); +void GRAPH_UNLOCKED blk_drain(BlockBackend *blk); void GRAPH_UNLOCKED blk_drain_all(void); void blk_set_on_error(BlockBackend *blk, BlockdevOnError on_read_error, BlockdevOnError on_write_error); From 7bb9bd52ec2b058acc1957a92ea505d8a4e12077 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:16 +0200 Subject: [PATCH 2325/2760] block-backend: mark blk_io_limits_disable() as GRAPH_UNLOCKED The function blk_io_limits_disable() calls bdrv_drained_begin(), which must be called with the graph unlocked. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-40-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/system/block-backend-global-state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/system/block-backend-global-state.h b/include/system/block-backend-global-state.h index f6ec1174e6..c3849640df 100644 --- a/include/system/block-backend-global-state.h +++ b/include/system/block-backend-global-state.h @@ -109,7 +109,7 @@ int blk_probe_blocksizes(BlockBackend *blk, BlockSizes *bsz); int blk_probe_geometry(BlockBackend *blk, HDGeometry *geo); void blk_set_io_limits(BlockBackend *blk, ThrottleConfig *cfg); -void blk_io_limits_disable(BlockBackend *blk); +void GRAPH_UNLOCKED blk_io_limits_disable(BlockBackend *blk); void blk_io_limits_enable(BlockBackend *blk, const char *group); void blk_io_limits_update_group(BlockBackend *blk, const char *group); void blk_set_force_allow_inactivate(BlockBackend *blk); From 60f609c1526102df35d8de8f513a80e6d3528bd8 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:17 +0200 Subject: [PATCH 2326/2760] block/commit: mark commit_abort() as GRAPH_UNLOCKED The function commit_abort() calls bdrv_drained_begin(), which must be called with the graph unlocked. Also mark the JobDriver's abort() callback as GRAPH_UNLOCKED_PTR, because that is the callback via which commit_abort() is reached. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-41-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/commit.c | 2 +- include/qemu/job.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/block/commit.c b/block/commit.c index 7496cf732e..0d9e1a16d7 100644 --- a/block/commit.c +++ b/block/commit.c @@ -68,7 +68,7 @@ static int commit_prepare(Job *job) s->backing_mask_protocol); } -static void commit_abort(Job *job) +static void GRAPH_UNLOCKED commit_abort(Job *job) { CommitBlockJob *s = container_of(job, CommitBlockJob, common.job); BlockDriverState *top_bs = blk_bs(s->top); diff --git a/include/qemu/job.h b/include/qemu/job.h index bb8ee766ef..ead31578d3 100644 --- a/include/qemu/job.h +++ b/include/qemu/job.h @@ -283,7 +283,7 @@ struct JobDriver { * All jobs will complete with a call to either .commit() or .abort() but * never both. */ - void (*abort)(Job *job); + void GRAPH_UNLOCKED_PTR (*abort)(Job *job); /** * If the callback is not NULL, it will be invoked after a call to either From 975d9ff32e37f67e82d0da546cfb567bdee9c6fb Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 14 Jul 2025 15:01:53 +0200 Subject: [PATCH 2327/2760] block: Allow bdrv_new() with and without graph lock bdrv_new() calls bdrv_drained_begin(), which can poll and therefore can't be called while holding the graph lock. One option to make sure that this call is allowed would be marking bdrv_new() GRAPH_UNLOCKED. However, this is actually an unnecessary restriction because we know that we only just created the BlockDriverState and it isn't even part of the graph yet. We can use bdrv_do_drained_begin_quiesce() instead to avoid the polling, which means that bdrv_new() can now safely be called from callers that hold the graph lock as well as from callers that don't. Signed-off-by: Kevin Wolf --- block.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/block.c b/block.c index 932e599f45..fe5aa2e767 100644 --- a/block.c +++ b/block.c @@ -431,7 +431,7 @@ BlockDriverState *bdrv_new(void) bs->block_status_cache = g_new0(BdrvBlockStatusCache, 1); for (i = 0; i < bdrv_drain_all_count; i++) { - bdrv_drained_begin(bs); + bdrv_do_drained_begin_quiesce(bs, NULL); } QTAILQ_INSERT_TAIL(&all_bdrv_states, bs, bs_list); From 5d048233474b57b8b4321f06a45df97461473589 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:19 +0200 Subject: [PATCH 2328/2760] block: mark bdrv_replace_child_bs() as GRAPH_UNLOCKED The function bdrv_replace_child_bs() calls bdrv_drained_begin() which must be called with the graph unlocked. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-43-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/block-global-state.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index eec92a98da..706a2cde36 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -74,8 +74,8 @@ int bdrv_append(BlockDriverState *bs_new, BlockDriverState *bs_top, int GRAPH_WRLOCK bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp); -int bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, - Error **errp); +int GRAPH_UNLOCKED +bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, Error **errp); BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, int flags, Error **errp); int bdrv_drop_filter(BlockDriverState *bs, Error **errp); From 04f4d9c555d194526126235d752c092017818d6b Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:20 +0200 Subject: [PATCH 2329/2760] block: mark bdrv_insert_node() as GRAPH_UNLOCKED The function bdrv_insert_node() calls bdrv_drained_begin() which must be called with the graph unlocked. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-44-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/block-global-state.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index 706a2cde36..cb4d8bca22 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -76,8 +76,9 @@ bdrv_replace_node(BlockDriverState *from, BlockDriverState *to, Error **errp); int GRAPH_UNLOCKED bdrv_replace_child_bs(BdrvChild *child, BlockDriverState *new_bs, Error **errp); -BlockDriverState *bdrv_insert_node(BlockDriverState *bs, QDict *node_options, - int flags, Error **errp); +BlockDriverState * GRAPH_UNLOCKED +bdrv_insert_node(BlockDriverState *bs, QDict *node_options, int flags, + Error **errp); int bdrv_drop_filter(BlockDriverState *bs, Error **errp); BdrvChild * no_coroutine_fn From 94371745d763755efde8442c40a2cd02571f2b5e Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:21 +0200 Subject: [PATCH 2330/2760] block: mark bdrv_drop_intermediate() as GRAPH_UNLOCKED The function bdrv_drop_intermediate() calls bdrv_drained_begin(), which must be called with the graph unlocked. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-45-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/block-global-state.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index cb4d8bca22..d3dd951013 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -143,9 +143,10 @@ int bdrv_commit(BlockDriverState *bs); int GRAPH_RDLOCK bdrv_make_empty(BdrvChild *c, Error **errp); void bdrv_register(BlockDriver *bdrv); -int bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, - const char *backing_file_str, - bool backing_mask_protocol); +int GRAPH_UNLOCKED +bdrv_drop_intermediate(BlockDriverState *top, BlockDriverState *base, + const char *backing_file_str, + bool backing_mask_protocol); BlockDriverState * GRAPH_RDLOCK bdrv_find_overlay(BlockDriverState *active, BlockDriverState *bs); From 6d7e3f8de09d92f9ebea530be3696ed053ae8508 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:22 +0200 Subject: [PATCH 2331/2760] block: mark bdrv_close_all() as GRAPH_UNLOCKED The function bdrv_close_all() calls bdrv_drain_all(), which must be called with the graph unlocked. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-46-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/block-global-state.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index d3dd951013..e7c9acd2ba 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -192,7 +192,7 @@ void bdrv_activate_all(Error **errp); int GRAPH_UNLOCKED bdrv_inactivate_all(void); int bdrv_flush_all(void); -void bdrv_close_all(void); +void GRAPH_UNLOCKED bdrv_close_all(void); void GRAPH_UNLOCKED bdrv_drain_all_begin(void); void bdrv_drain_all_begin_nopoll(void); void bdrv_drain_all_end(void); From ede0859311e46ca8654eaf11456e19868ac06c66 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:23 +0200 Subject: [PATCH 2332/2760] block: mark bdrv_close() as GRAPH_UNLOCKED The functions blk_log_writes_close(), blkverify_close(), quorum_close(), vmdk_close() via vmdk_free_extents(), and other bdrv_close() implementations call bdrv_graph_wrlock_drained(), which must be called with the graph unlocked. They are reached via the BlockDriver's bdrv_close() callback and the bdrv_close() wrapper, which are also marked as GRAPH_UNLOCKED_PTR and GRAPH_UNLOCKED. Furthermore, the function bdrv_close() also calls bdrv_drained_begin() and bdrv_graph_wrlock_drained(), so there are additional reasons for marking it GRAPH_UNLOCKED. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-47-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 2 +- include/block/block_int-common.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/block.c b/block.c index fe5aa2e767..8d3dd5a8e3 100644 --- a/block.c +++ b/block.c @@ -5146,7 +5146,7 @@ static void GRAPH_UNLOCKED bdrv_reopen_abort(BDRVReopenState *reopen_state) } -static void bdrv_close(BlockDriverState *bs) +static void GRAPH_UNLOCKED bdrv_close(BlockDriverState *bs) { BdrvAioNotifier *ban, *ban_next; BdrvChild *child, *next; diff --git a/include/block/block_int-common.h b/include/block/block_int-common.h index e96c6a6a03..034c0634c8 100644 --- a/include/block/block_int-common.h +++ b/include/block/block_int-common.h @@ -248,7 +248,7 @@ struct BlockDriver { int GRAPH_UNLOCKED_PTR (*bdrv_open)( BlockDriverState *bs, QDict *options, int flags, Error **errp); - void (*bdrv_close)(BlockDriverState *bs); + void GRAPH_UNLOCKED_PTR (*bdrv_close)(BlockDriverState *bs); int coroutine_fn GRAPH_UNLOCKED_PTR (*bdrv_co_create)( BlockdevCreateOptions *opts, Error **errp); From 2cf92b15cda38a34cd2508e5f51323e601a588d3 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:24 +0200 Subject: [PATCH 2333/2760] block: mark bdrv_open_child_common() and its callers GRAPH_UNLOCKED The function bdrv_open_child_common() calls bdrv_graph_wrlock_drained(), which must be called with the graph unlocked. Mark it and its two callers bdrv_open_file_child() and bdrv_open_child() as GRAPH_UNLOCKED. This requires temporarily unlocking in vmdk_parse_extents() and making the locked section shorter in vmdk_open(). Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-48-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block.c | 13 ++++++------- block/vmdk.c | 6 ++++-- include/block/block-global-state.h | 9 +++++---- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/block.c b/block.c index 8d3dd5a8e3..8848e9a7ed 100644 --- a/block.c +++ b/block.c @@ -3766,13 +3766,12 @@ bdrv_open_child_bs(const char *filename, QDict *options, const char *bdref_key, return bs; } -static BdrvChild *bdrv_open_child_common(const char *filename, - QDict *options, const char *bdref_key, - BlockDriverState *parent, - const BdrvChildClass *child_class, - BdrvChildRole child_role, - bool allow_none, bool parse_filename, - Error **errp) +static BdrvChild * GRAPH_UNLOCKED +bdrv_open_child_common(const char *filename, QDict *options, + const char *bdref_key, BlockDriverState *parent, + const BdrvChildClass *child_class, + BdrvChildRole child_role, bool allow_none, + bool parse_filename, Error **errp) { BlockDriverState *bs; BdrvChild *child; diff --git a/block/vmdk.c b/block/vmdk.c index 04986c8d55..7b98debc2b 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1229,9 +1229,11 @@ vmdk_parse_extents(const char *desc, BlockDriverState *bs, QDict *options, extent_role |= BDRV_CHILD_METADATA; } + bdrv_graph_rdunlock_main_loop(); extent_file = bdrv_open_child(extent_path, options, extent_opt_prefix, bs, &child_of_bds, extent_role, false, &local_err); + bdrv_graph_rdlock_main_loop(); g_free(extent_path); if (!extent_file) { error_propagate(errp, local_err); @@ -1352,13 +1354,13 @@ static int vmdk_open(BlockDriverState *bs, QDict *options, int flags, BDRVVmdkState *s = bs->opaque; uint32_t magic; - GRAPH_RDLOCK_GUARD_MAINLOOP(); - ret = bdrv_open_file_child(NULL, options, "file", bs, errp); if (ret < 0) { return ret; } + GRAPH_RDLOCK_GUARD_MAINLOOP(); + buf = vmdk_read_desc(bs->file, 0, errp); if (!buf) { return -EINVAL; diff --git a/include/block/block-global-state.h b/include/block/block-global-state.h index e7c9acd2ba..62da83c616 100644 --- a/include/block/block-global-state.h +++ b/include/block/block-global-state.h @@ -81,7 +81,7 @@ bdrv_insert_node(BlockDriverState *bs, QDict *node_options, int flags, Error **errp); int bdrv_drop_filter(BlockDriverState *bs, Error **errp); -BdrvChild * no_coroutine_fn +BdrvChild * no_coroutine_fn GRAPH_UNLOCKED bdrv_open_child(const char *filename, QDict *options, const char *bdref_key, BlockDriverState *parent, const BdrvChildClass *child_class, BdrvChildRole child_role, bool allow_none, Error **errp); @@ -91,9 +91,10 @@ bdrv_co_open_child(const char *filename, QDict *options, const char *bdref_key, BlockDriverState *parent, const BdrvChildClass *child_class, BdrvChildRole child_role, bool allow_none, Error **errp); -int bdrv_open_file_child(const char *filename, - QDict *options, const char *bdref_key, - BlockDriverState *parent, Error **errp); +int GRAPH_UNLOCKED +bdrv_open_file_child(const char *filename, QDict *options, + const char *bdref_key, BlockDriverState *parent, + Error **errp); BlockDriverState * no_coroutine_fn bdrv_open_blockdev_ref(BlockdevRef *ref, Error **errp); From a256a427b026f26be33ce2ace7158f5ab0931fa8 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Fri, 30 May 2025 17:11:25 +0200 Subject: [PATCH 2334/2760] blockjob: mark block_job_remove_all_bdrv() as GRAPH_UNLOCKED The function block_job_remove_all_bdrv() calls bdrv_graph_wrlock_drained(), which must be called with the graph unlocked. Signed-off-by: Fiona Ebner Message-ID: <20250530151125.955508-49-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- include/block/blockjob.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/block/blockjob.h b/include/block/blockjob.h index 990f3e179a..85284cb25e 100644 --- a/include/block/blockjob.h +++ b/include/block/blockjob.h @@ -151,7 +151,7 @@ block_job_add_bdrv(BlockJob *job, const char *name, BlockDriverState *bs, * Remove all BlockDriverStates from the list of nodes that are involved in the * job. This removes the blockers added with block_job_add_bdrv(). */ -void block_job_remove_all_bdrv(BlockJob *job); +void GRAPH_UNLOCKED block_job_remove_all_bdrv(BlockJob *job); /** * block_job_has_bdrv: From cfac5a963e4bf287a194b5df80b7984bc8e41221 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Wed, 2 Jul 2025 14:31:27 +0200 Subject: [PATCH 2335/2760] block/qapi: include child references in block device info In combination with using a throttle filter to enforce IO limits for a guest device, knowing the 'file' child of a block device can be useful. If the throttle filter is only intended for guest IO, block jobs should not also be limited by the throttle filter, so the block operations need to be done with the 'file' child of the top throttle node as the target. In combination with mirroring, the name of that child is not fixed. Another scenario is when unplugging a guest device after mirroring below a top throttle node, where the mirror target is added explicitly via blockdev-add. After mirroring, the target becomes the new 'file' child of the throttle node. For unplugging, both the top throttle node and the mirror target need to be deleted, because only implicitly added child nodes are deleted automatically, and the current 'file' child of the throttle node was explicitly added (as the mirror target). In other scenarios, it could be useful to follow the backing chain. Note that iotests 191 and 273 use _filter_img_info, so the 'children' information is filtered out there. Signed-off-by: Fiona Ebner Message-ID: <20250702123204.325470-2-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/qapi.c | 10 ++++++++++ qapi/block-core.json | 16 ++++++++++++++++ tests/qemu-iotests/184.out | 8 ++++++++ 3 files changed, 34 insertions(+) diff --git a/block/qapi.c b/block/qapi.c index 2c50a6bf3b..e08a1e970f 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -51,6 +51,8 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, ImageInfo *backing_info; BlockDriverState *backing; BlockDeviceInfo *info; + BlockdevChildList **children_list_tail; + BdrvChild *child; if (!bs->drv) { error_setg(errp, "Block device %s is ejected", bs->node_name); @@ -77,6 +79,14 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, info->node_name = g_strdup(bs->node_name); } + children_list_tail = &info->children; + QLIST_FOREACH(child, &bs->children, next) { + BlockdevChild *child_ref = g_new0(BlockdevChild, 1); + child_ref->child = g_strdup(child->name); + child_ref->node_name = g_strdup(child->bs->node_name); + QAPI_LIST_APPEND(children_list_tail, child_ref); + } + backing = bdrv_cow_bs(bs); if (backing) { info->backing_file = g_strdup(backing->filename); diff --git a/qapi/block-core.json b/qapi/block-core.json index 1df6644f0d..3e720af5ad 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -461,6 +461,19 @@ 'direct': 'bool', 'no-flush': 'bool' } } +## +# @BlockdevChild: +# +# @child: The name of the child, for example 'file' or 'backing'. +# +# @node-name: The name of the child's block driver node. +# +# Since: 10.1 +## +{ 'struct': 'BlockdevChild', + 'data': { 'child': 'str', + 'node-name': 'str' } } + ## # @BlockDeviceInfo: # @@ -486,6 +499,8 @@ # @backing_file_depth: number of files in the backing file chain # (since: 1.2) # +# @children: Information about child block nodes. (since: 10.1) +# # @active: true if the backend is active; typical cases for inactive backends # are on the migration source instance after migration completes and on the # destination before it completes. (since: 10.0) @@ -560,6 +575,7 @@ { 'struct': 'BlockDeviceInfo', 'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str', '*backing_file': 'str', 'backing_file_depth': 'int', + 'children': ['BlockdevChild'], 'active': 'bool', 'encrypted': 'bool', 'detect_zeroes': 'BlockdevDetectZeroesOptions', 'bps': 'int', 'bps_rd': 'int', 'bps_wr': 'int', diff --git a/tests/qemu-iotests/184.out b/tests/qemu-iotests/184.out index 52692b6b3b..ef99bb2e9a 100644 --- a/tests/qemu-iotests/184.out +++ b/tests/qemu-iotests/184.out @@ -41,6 +41,12 @@ Testing: }, "iops_wr": 0, "ro": false, + "children": [ + { + "node-name": "disk0", + "child": "file" + } + ], "node-name": "throttle0", "backing_file_depth": 1, "drv": "throttle", @@ -69,6 +75,8 @@ Testing: }, "iops_wr": 0, "ro": false, + "children": [ + ], "node-name": "disk0", "backing_file_depth": 0, "drv": "null-co", From 430e2be81e0970ee06c1c956f7698262b2ec514f Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Wed, 2 Jul 2025 14:31:28 +0200 Subject: [PATCH 2336/2760] block/qapi: make @node-name in @BlockDeviceInfo non-optional Since commit 15489c769b ("block: auto-generated node-names"), if the node name of a block driver state is not explicitly specified, it will be auto-generated. Signed-off-by: Fiona Ebner Message-ID: <20250702123204.325470-3-f.ebner@proxmox.com> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- block/qapi.c | 4 +--- qapi/block-core.json | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/block/qapi.c b/block/qapi.c index e08a1e970f..12fbf8d1b7 100644 --- a/block/qapi.c +++ b/block/qapi.c @@ -75,9 +75,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockBackend *blk, .no_flush = !!(bs->open_flags & BDRV_O_NO_FLUSH), }; - if (bs->node_name[0]) { - info->node_name = g_strdup(bs->node_name); - } + info->node_name = g_strdup(bs->node_name); children_list_tail = &info->children; QLIST_FOREACH(child, &bs->children, next) { diff --git a/qapi/block-core.json b/qapi/block-core.json index 3e720af5ad..9d36927fc1 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -573,7 +573,7 @@ # Since: 0.14 ## { 'struct': 'BlockDeviceInfo', - 'data': { 'file': 'str', '*node-name': 'str', 'ro': 'bool', 'drv': 'str', + 'data': { 'file': 'str', 'node-name': 'str', 'ro': 'bool', 'drv': 'str', '*backing_file': 'str', 'backing_file_depth': 'int', 'children': ['BlockdevChild'], 'active': 'bool', 'encrypted': 'bool', From d402da1360c2240e81f0e5fc80ddbfc6238e0da8 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Wed, 25 Jun 2025 10:50:19 +0200 Subject: [PATCH 2337/2760] file-posix: Fix aio=threads performance regression after enablign FUA For aio=threads, we're currently not implementing REQ_FUA in any useful way, but just do a separate raw_co_flush_to_disk() call. This changes behaviour compared to the old state, which used bdrv_co_flush() with its optimisations. As a quick fix, call bdrv_co_flush() again like before. Eventually, we can use pwritev2() to make use of RWF_DSYNC if available, but we'll still have to keep this code path as a fallback, so this fix is required either way. While the fix itself is a one-liner, some new graph locking annotations are needed to convince TSA that the locking is correct. Cc: qemu-stable@nongnu.org Fixes: 984a32f17e8d ("file-posix: Support FUA writes") Buglink: https://issues.redhat.com/browse/RHEL-96854 Reported-by: Tingting Mao Signed-off-by: Kevin Wolf Message-ID: <20250625085019.27735-1-kwolf@redhat.com> Reviewed-by: Eric Blake Signed-off-by: Kevin Wolf --- block/file-posix.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/block/file-posix.c b/block/file-posix.c index 9b5f08ccb2..8c738674ce 100644 --- a/block/file-posix.c +++ b/block/file-posix.c @@ -2564,9 +2564,9 @@ static inline bool raw_check_linux_aio(BDRVRawState *s) } #endif -static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, - uint64_t bytes, QEMUIOVector *qiov, int type, - int flags) +static int coroutine_fn GRAPH_RDLOCK +raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, uint64_t bytes, + QEMUIOVector *qiov, int type, int flags) { BDRVRawState *s = bs->opaque; RawPosixAIOData acb; @@ -2625,7 +2625,7 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, ret = raw_thread_pool_submit(handle_aiocb_rw, &acb); if (ret == 0 && (flags & BDRV_REQ_FUA)) { /* TODO Use pwritev2() instead if it's available */ - ret = raw_co_flush_to_disk(bs); + ret = bdrv_co_flush(bs); } goto out; /* Avoid the compiler err of unused label */ @@ -2660,16 +2660,16 @@ static int coroutine_fn raw_co_prw(BlockDriverState *bs, int64_t *offset_ptr, return ret; } -static int coroutine_fn raw_co_preadv(BlockDriverState *bs, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +raw_co_preadv(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_READ, flags); } -static int coroutine_fn raw_co_pwritev(BlockDriverState *bs, int64_t offset, - int64_t bytes, QEMUIOVector *qiov, - BdrvRequestFlags flags) +static int coroutine_fn GRAPH_RDLOCK +raw_co_pwritev(BlockDriverState *bs, int64_t offset, int64_t bytes, + QEMUIOVector *qiov, BdrvRequestFlags flags) { return raw_co_prw(bs, &offset, bytes, qiov, QEMU_AIO_WRITE, flags); } @@ -3606,10 +3606,11 @@ static int coroutine_fn raw_co_zone_mgmt(BlockDriverState *bs, BlockZoneOp op, #endif #if defined(CONFIG_BLKZONED) -static int coroutine_fn raw_co_zone_append(BlockDriverState *bs, - int64_t *offset, - QEMUIOVector *qiov, - BdrvRequestFlags flags) { +static int coroutine_fn GRAPH_RDLOCK +raw_co_zone_append(BlockDriverState *bs, + int64_t *offset, + QEMUIOVector *qiov, + BdrvRequestFlags flags) { assert(flags == 0); int64_t zone_size_mask = bs->bl.zone_size - 1; int64_t iov_len = 0; From 9253773cb7ff10d85ccaf4d5f418ea5c7fa31834 Mon Sep 17 00:00:00 2001 From: Yongbok Kim Date: Mon, 29 Jul 2019 19:23:33 +0200 Subject: [PATCH 2338/2760] target/mips: Add support for emulation of CRC32 instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add emulation of MIPS' CRC32 (Cyclic Redundancy Check) instructions. Reuse zlib crc32() and Linux crc32c(). Corresponding disassembly has been added in commit 99029be1c28 ("target/mips: Add implementation of GINVT instruction"). Signed-off-by: Yongbok Kim Signed-off-by: Aleksandar Markovic Signed-off-by: Aleksandar Rakic Reviewed-by: Aleksandar Rikalo Signed-off-by: Philippe Mathieu-Daudé Message-ID: --- target/mips/cpu-defs.c.inc | 10 ++++++---- target/mips/helper.h | 2 ++ target/mips/meson.build | 1 + target/mips/tcg/op_helper.c | 26 ++++++++++++++++++++++++++ target/mips/tcg/rel6.decode | 5 +++++ target/mips/tcg/rel6_translate.c | 12 ++++++++++++ target/mips/tcg/translate.c | 24 ++++++++++++++++++++++++ target/mips/tcg/translate.h | 2 ++ 8 files changed, 78 insertions(+), 4 deletions(-) diff --git a/target/mips/cpu-defs.c.inc b/target/mips/cpu-defs.c.inc index 922fc39138..d93b9d341a 100644 --- a/target/mips/cpu-defs.c.inc +++ b/target/mips/cpu-defs.c.inc @@ -756,8 +756,9 @@ const mips_def_t mips_defs[] = (1 << CP0C3_RXI) | (1 << CP0C3_LPA) | (1 << CP0C3_VInt), .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) | (1 << CP0C4_AE) | (0xfc << CP0C4_KScrExist), - .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_VP) | - (1 << CP0C5_LLB) | (1 << CP0C5_MRP) | (3 << CP0C5_GI), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_CRCP) | (1 << CP0C5_XNP) | + (1 << CP0C5_VP) | (1 << CP0C5_LLB) | (1 << CP0C5_MRP) | + (3 << CP0C5_GI), .CP0_Config5_rw_bitmask = (1 << CP0C5_MSAEn) | (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | (1 << CP0C5_UFE), .CP0_LLAddr_rw_bitmask = 0, @@ -796,8 +797,9 @@ const mips_def_t mips_defs[] = (1 << CP0C3_RXI) | (1 << CP0C3_LPA) | (1 << CP0C3_VInt), .CP0_Config4 = MIPS_CONFIG4 | (1U << CP0C4_M) | (3 << CP0C4_IE) | (1 << CP0C4_AE) | (0xfc << CP0C4_KScrExist), - .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_XNP) | (1 << CP0C5_VP) | - (1 << CP0C5_LLB) | (1 << CP0C5_MRP) | (3 << CP0C5_GI), + .CP0_Config5 = MIPS_CONFIG5 | (1 << CP0C5_CRCP) | (1 << CP0C5_XNP) | + (1 << CP0C5_VP) | (1 << CP0C5_LLB) | (1 << CP0C5_MRP) | + (3 << CP0C5_GI), .CP0_Config5_rw_bitmask = (1 << CP0C5_MSAEn) | (1 << CP0C5_SBRI) | (1 << CP0C5_FRE) | (1 << CP0C5_UFE), .CP0_LLAddr_rw_bitmask = 0, diff --git a/target/mips/helper.h b/target/mips/helper.h index 7e40041828..b6cd53c853 100644 --- a/target/mips/helper.h +++ b/target/mips/helper.h @@ -21,6 +21,8 @@ DEF_HELPER_FLAGS_1(bitswap, TCG_CALL_NO_RWG_SE, tl, tl) DEF_HELPER_FLAGS_1(dbitswap, TCG_CALL_NO_RWG_SE, tl, tl) #endif +DEF_HELPER_3(crc32, tl, tl, tl, i32) +DEF_HELPER_3(crc32c, tl, tl, tl, i32) DEF_HELPER_FLAGS_4(rotx, TCG_CALL_NO_RWG_SE, tl, tl, i32, i32, i32) /* microMIPS functions */ diff --git a/target/mips/meson.build b/target/mips/meson.build index 247979a2cf..abf0ce3e8b 100644 --- a/target/mips/meson.build +++ b/target/mips/meson.build @@ -7,6 +7,7 @@ mips_ss.add(files( 'gdbstub.c', 'msa.c', )) +mips_ss.add(zlib) if have_system subdir('system') diff --git a/target/mips/tcg/op_helper.c b/target/mips/tcg/op_helper.c index b906d10204..4502ae2b5b 100644 --- a/target/mips/tcg/op_helper.c +++ b/target/mips/tcg/op_helper.c @@ -24,6 +24,8 @@ #include "exec/helper-proto.h" #include "exec/memop.h" #include "fpu_helper.h" +#include "qemu/crc32c.h" +#include static inline target_ulong bitswap(target_ulong v) { @@ -142,6 +144,30 @@ target_ulong helper_rotx(target_ulong rs, uint32_t shift, uint32_t shiftx, return (int64_t)(int32_t)(uint32_t)tmp5; } +/* these crc32 functions are based on target/loongarch/tcg/op_helper.c */ +target_ulong helper_crc32(target_ulong val, target_ulong m, uint32_t sz) +{ + uint8_t buf[8]; + target_ulong mask = ((sz * 8) == 64) ? + (target_ulong) -1ULL : + ((1ULL << (sz * 8)) - 1); + + m &= mask; + stq_le_p(buf, m); + return (int32_t) (crc32(val ^ 0xffffffff, buf, sz) ^ 0xffffffff); +} + +target_ulong helper_crc32c(target_ulong val, target_ulong m, uint32_t sz) +{ + uint8_t buf[8]; + target_ulong mask = ((sz * 8) == 64) ? + (target_ulong) -1ULL : + ((1ULL << (sz * 8)) - 1); + m &= mask; + stq_le_p(buf, m); + return (int32_t) (crc32c(val, buf, sz) ^ 0xffffffff); +} + void helper_fork(target_ulong arg1, target_ulong arg2) { /* diff --git a/target/mips/tcg/rel6.decode b/target/mips/tcg/rel6.decode index d6989cf56e..7fbcb109b4 100644 --- a/target/mips/tcg/rel6.decode +++ b/target/mips/tcg/rel6.decode @@ -16,11 +16,16 @@ &r rs rt rd sa +&special3_crc rs rt c sz + @lsa ...... rs:5 rt:5 rd:5 ... sa:2 ...... &r +@crc32 ...... rs:5 rt:5 ..... c:3 sz:2 ...... &special3_crc LSA 000000 ..... ..... ..... 000 .. 000101 @lsa DLSA 000000 ..... ..... ..... 000 .. 010101 @lsa +CRC32 011111 ..... ..... 00000 ... .. 001111 @crc32 + REMOVED 010011 ----- ----- ----- ----- ------ # COP1X (COP3) REMOVED 011100 ----- ----- ----- ----- ------ # SPECIAL2 diff --git a/target/mips/tcg/rel6_translate.c b/target/mips/tcg/rel6_translate.c index 59f237ba3b..8933506397 100644 --- a/target/mips/tcg/rel6_translate.c +++ b/target/mips/tcg/rel6_translate.c @@ -33,3 +33,15 @@ static bool trans_DLSA(DisasContext *ctx, arg_r *a) } return gen_dlsa(ctx, a->rd, a->rt, a->rs, a->sa); } + +static bool trans_CRC32(DisasContext *ctx, arg_special3_crc *a) +{ + if (unlikely(!ctx->crcp) + || unlikely((a->sz == 3) && (!(ctx->hflags & MIPS_HFLAG_64))) + || unlikely((a->c >= 2))) { + gen_reserved_instruction(ctx); + return true; + } + gen_crc32(ctx, a->rt, a->rs, a->rt, a->sz, a->c); + return true; +} diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 8658315f93..5c80b03032 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -13449,6 +13449,29 @@ static void decode_opc_special2_legacy(CPUMIPSState *env, DisasContext *ctx) } } +void gen_crc32(DisasContext *ctx, int rd, int rs, int rt, int sz, + int crc32c) +{ + TCGv t0; + TCGv t1; + TCGv_i32 tsz = tcg_constant_i32(1 << sz); + if (rd == 0) { + /* Treat as NOP. */ + return; + } + t0 = tcg_temp_new(); + t1 = tcg_temp_new(); + + gen_load_gpr(t0, rt); + gen_load_gpr(t1, rs); + + if (crc32c) { + gen_helper_crc32c(cpu_gpr[rd], t0, t1, tsz); + } else { + gen_helper_crc32(cpu_gpr[rd], t0, t1, tsz); + } +} + static void decode_opc_special3_r6(CPUMIPSState *env, DisasContext *ctx) { int rs, rt, rd, sa; @@ -15095,6 +15118,7 @@ static void mips_tr_init_disas_context(DisasContextBase *dcbase, CPUState *cs) ctx->abs2008 = (env->active_fpu.fcr31 >> FCR31_ABS2008) & 1; ctx->mi = (env->CP0_Config5 >> CP0C5_MI) & 1; ctx->gi = (env->CP0_Config5 >> CP0C5_GI) & 3; + ctx->crcp = (env->CP0_Config5 >> CP0C5_CRCP) & 1; restore_cpu_state(env, ctx); #ifdef CONFIG_USER_ONLY ctx->mem_idx = MIPS_HFLAG_UM; diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index 1bf153d183..428b53a0da 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -51,6 +51,7 @@ typedef struct DisasContext { bool abs2008; bool mi; int gi; + bool crcp; } DisasContext; #define DISAS_STOP DISAS_TARGET_0 @@ -181,6 +182,7 @@ bool gen_lsa(DisasContext *ctx, int rd, int rt, int rs, int sa); bool gen_dlsa(DisasContext *ctx, int rd, int rt, int rs, int sa); void gen_rdhwr(DisasContext *ctx, int rt, int rd, int sel); +void gen_crc32(DisasContext *ctx, int rd, int rs, int rt, int sz, int crc32c); extern TCGv cpu_gpr[32], cpu_PC; #if defined(TARGET_MIPS64) From 263ce6008ffd15e152b8ff5ecd16a000d88276ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Nov 2024 05:30:07 -0300 Subject: [PATCH 2339/2760] target/mips: Extract gen_base_index_addr() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor out gen_base_index_addr() which is used twice but we'll use it more. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241111222936.59869-2-philmd@linaro.org> --- target/mips/tcg/translate.c | 27 +++++++++++++-------------- target/mips/tcg/translate.h | 1 + 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 5c80b03032..8816237e92 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -1957,6 +1957,17 @@ void gen_base_offset_addr(DisasContext *ctx, TCGv addr, int base, int offset) } } +void gen_base_index_addr(DisasContext *ctx, TCGv addr, int base, int index) +{ + if (base == 0) { + gen_load_gpr(addr, index); + } else if (index == 0) { + gen_load_gpr(addr, base); + } else { + gen_op_addr_add(ctx, addr, cpu_gpr[base], cpu_gpr[index]); + } +} + static target_ulong pc_relative_pc(DisasContext *ctx) { target_ulong pc = ctx->base.pc_next; @@ -10546,13 +10557,7 @@ static void gen_flt3_ldst(DisasContext *ctx, uint32_t opc, { TCGv t0 = tcg_temp_new(); - if (base == 0) { - gen_load_gpr(t0, index); - } else if (index == 0) { - gen_load_gpr(t0, base); - } else { - gen_op_addr_add(ctx, t0, cpu_gpr[base], cpu_gpr[index]); - } + gen_base_index_addr(ctx, t0, base, index); /* * Don't do NOP if destination is zero: we must perform the actual * memory access. @@ -11334,13 +11339,7 @@ static void gen_mips_lx(DisasContext *ctx, uint32_t opc, } t0 = tcg_temp_new(); - if (base == 0) { - gen_load_gpr(t0, offset); - } else if (offset == 0) { - gen_load_gpr(t0, base); - } else { - gen_op_addr_add(ctx, t0, cpu_gpr[base], cpu_gpr[offset]); - } + gen_base_index_addr(ctx, t0, base, offset); switch (opc) { case OPC_LBUX: diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index 428b53a0da..e65593815e 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -154,6 +154,7 @@ void check_cp1_registers(DisasContext *ctx, int regs); void check_cop1x(DisasContext *ctx); void gen_base_offset_addr(DisasContext *ctx, TCGv addr, int base, int offset); +void gen_base_index_addr(DisasContext *ctx, TCGv addr, int base, int index); void gen_move_low32(TCGv ret, TCGv_i64 arg); void gen_move_high32(TCGv ret, TCGv_i64 arg); void gen_load_gpr(TCGv t, int reg); From f0be3f9769d72f231269d3d746ea1a391301ff36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Nov 2024 05:35:19 -0300 Subject: [PATCH 2340/2760] target/mips: Extract generic gen_lx() helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract gen_lx() from gen_mips_lx(); inline the Octeon check in decode_opc_special3_legacy(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241111222936.59869-3-philmd@linaro.org> --- target/mips/tcg/translate.c | 55 +++++++++++++------------------------ target/mips/tcg/translate.h | 1 + 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index 8816237e92..b9b2d8bb7e 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -2036,6 +2036,15 @@ static void gen_lxr(DisasContext *ctx, TCGv reg, TCGv addr, tcg_gen_or_tl(reg, t0, t1); } +void gen_lx(DisasContext *ctx, int rd, int base, int index, MemOp mop) +{ + TCGv t0 = tcg_temp_new(); + + gen_base_index_addr(ctx, t0, base, index); + tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | mop); + gen_store_gpr(t0, rd); +} + /* Load */ static void gen_ld(DisasContext *ctx, uint32_t opc, int rt, int base, int offset) @@ -11328,41 +11337,6 @@ enum { /* MIPSDSP functions. */ -/* Indexed load is not for DSP only */ -static void gen_mips_lx(DisasContext *ctx, uint32_t opc, - int rd, int base, int offset) -{ - TCGv t0; - - if (!(ctx->insn_flags & INSN_OCTEON)) { - check_dsp(ctx); - } - t0 = tcg_temp_new(); - - gen_base_index_addr(ctx, t0, base, offset); - - switch (opc) { - case OPC_LBUX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, MO_UB); - gen_store_gpr(t0, rd); - break; - case OPC_LHX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_SW); - gen_store_gpr(t0, rd); - break; - case OPC_LWX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_SL); - gen_store_gpr(t0, rd); - break; -#if defined(TARGET_MIPS64) - case OPC_LDX: - tcg_gen_qemu_ld_tl(t0, t0, ctx->mem_idx, mo_endian(ctx) | MO_UQ); - gen_store_gpr(t0, rd); - break; -#endif - } -} - static void gen_mipsdsp_arith(DisasContext *ctx, uint32_t op1, uint32_t op2, int ret, int v1, int v2) { @@ -13633,15 +13607,24 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) } break; case OPC_LX_DSP: + if (!(ctx->insn_flags & INSN_OCTEON)) { + check_dsp(ctx); + } op2 = MASK_LX(ctx->opcode); switch (op2) { #if defined(TARGET_MIPS64) case OPC_LDX: + gen_lx(ctx, rd, rs, rt, MO_UQ); + break; #endif case OPC_LBUX: + gen_lx(ctx, rd, rs, rt, MO_UB); + break; case OPC_LHX: + gen_lx(ctx, rd, rs, rt, MO_SW); + break; case OPC_LWX: - gen_mips_lx(ctx, op2, rd, rs, rt); + gen_lx(ctx, rd, rs, rt, MO_SL); break; default: /* Invalid */ MIPS_INVAL("MASK LX"); diff --git a/target/mips/tcg/translate.h b/target/mips/tcg/translate.h index e65593815e..89dde1e712 100644 --- a/target/mips/tcg/translate.h +++ b/target/mips/tcg/translate.h @@ -169,6 +169,7 @@ void gen_store_fpr32(DisasContext *ctx, TCGv_i32 t, int reg); void gen_store_fpr64(DisasContext *ctx, TCGv_i64 t, int reg); int get_fp_bit(int cc); +void gen_lx(DisasContext *ctx, int rd, int base, int index, MemOp mop); void gen_ldxs(DisasContext *ctx, int base, int index, int rd); void gen_align(DisasContext *ctx, int wordsz, int rd, int rs, int rt, int bp); void gen_addiupc(DisasContext *ctx, int rx, int imm, From 23ecff81ac881e18dd06ee87d0c6319244353ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Sun, 3 Nov 2024 05:37:10 -0300 Subject: [PATCH 2341/2760] target/mips: Convert Octeon LX instructions to decodetree MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use Octeon decodetree to call gen_lx() for the LX instructions. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pavel Dovgalyuk Reviewed-by: Richard Henderson Message-Id: <20241111222936.59869-4-philmd@linaro.org> --- target/mips/tcg/octeon.decode | 8 ++++++++ target/mips/tcg/octeon_translate.c | 12 ++++++++++++ target/mips/tcg/translate.c | 4 +--- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/target/mips/tcg/octeon.decode b/target/mips/tcg/octeon.decode index 0c787cb498..102a05860d 100644 --- a/target/mips/tcg/octeon.decode +++ b/target/mips/tcg/octeon.decode @@ -1,6 +1,7 @@ # Octeon Architecture Module instruction set # # Copyright (C) 2022 Pavel Dovgalyuk +# Copyright (C) 2024 Philippe Mathieu-Daudé # # SPDX-License-Identifier: LGPL-2.1-or-later # @@ -39,3 +40,10 @@ CINS 011100 ..... ..... ..... ..... 11001 . @bitfield POP 011100 rs:5 00000 rd:5 00000 10110 dw:1 SEQNE 011100 rs:5 rt:5 rd:5 00000 10101 ne:1 SEQNEI 011100 rs:5 rt:5 imm:s10 10111 ne:1 + +&lx base index rd +@lx ...... base:5 index:5 rd:5 ...... ..... &lx +LWX 011111 ..... ..... ..... 00000 001010 @lx +LHX 011111 ..... ..... ..... 00100 001010 @lx +LBUX 011111 ..... ..... ..... 00110 001010 @lx +LDX 011111 ..... ..... ..... 01000 001010 @lx diff --git a/target/mips/tcg/octeon_translate.c b/target/mips/tcg/octeon_translate.c index d9eb43716e..b2eca29e06 100644 --- a/target/mips/tcg/octeon_translate.c +++ b/target/mips/tcg/octeon_translate.c @@ -174,3 +174,15 @@ static bool trans_SEQNEI(DisasContext *ctx, arg_SEQNEI *a) } return true; } + +static bool trans_lx(DisasContext *ctx, arg_lx *a, MemOp mop) +{ + gen_lx(ctx, a->rd, a->base, a->index, mop); + + return true; +} + +TRANS(LBUX, trans_lx, MO_UB); +TRANS(LHX, trans_lx, MO_SW); +TRANS(LWX, trans_lx, MO_SL); +TRANS(LDX, trans_lx, MO_UQ); diff --git a/target/mips/tcg/translate.c b/target/mips/tcg/translate.c index b9b2d8bb7e..d91d6efe02 100644 --- a/target/mips/tcg/translate.c +++ b/target/mips/tcg/translate.c @@ -13607,9 +13607,7 @@ static void decode_opc_special3_legacy(CPUMIPSState *env, DisasContext *ctx) } break; case OPC_LX_DSP: - if (!(ctx->insn_flags & INSN_OCTEON)) { - check_dsp(ctx); - } + check_dsp(ctx); op2 = MASK_LX(ctx->opcode); switch (op2) { #if defined(TARGET_MIPS64) From 65a67f0fe5adcff442d80efba5094333f7ed7a21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 12 Nov 2024 16:19:18 +0100 Subject: [PATCH 2342/2760] target/mips: Have gen_[d]lsa() callers add 1 to shift amount argument MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Having the callee add 1 to shift amount is misleading (see the NM_LSA case in decode_nanomips_32_48_opc() where we have to manually substract 1). Rather have the callers pass a modified $sa. Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20241112172022.88348-4-philmd@linaro.org> --- target/mips/tcg/micromips_translate.c.inc | 2 +- target/mips/tcg/msa_translate.c | 4 ++-- target/mips/tcg/nanomips_translate.c.inc | 7 +------ target/mips/tcg/rel6_translate.c | 4 ++-- target/mips/tcg/translate_addr_const.c | 4 ++-- 5 files changed, 8 insertions(+), 13 deletions(-) diff --git a/target/mips/tcg/micromips_translate.c.inc b/target/mips/tcg/micromips_translate.c.inc index c479bec108..8fda7c8a21 100644 --- a/target/mips/tcg/micromips_translate.c.inc +++ b/target/mips/tcg/micromips_translate.c.inc @@ -1795,7 +1795,7 @@ static void decode_micromips32_opc(CPUMIPSState *env, DisasContext *ctx) return; case LSA: check_insn(ctx, ISA_MIPS_R6); - gen_lsa(ctx, rd, rt, rs, extract32(ctx->opcode, 9, 2)); + gen_lsa(ctx, rd, rt, rs, extract32(ctx->opcode, 9, 2) + 1); break; case ALIGN: check_insn(ctx, ISA_MIPS_R6); diff --git a/target/mips/tcg/msa_translate.c b/target/mips/tcg/msa_translate.c index 75cf80a20e..82b149922f 100644 --- a/target/mips/tcg/msa_translate.c +++ b/target/mips/tcg/msa_translate.c @@ -780,7 +780,7 @@ TRANS_DF_iv(ST, trans_msa_ldst, gen_helper_msa_st); static bool trans_LSA(DisasContext *ctx, arg_r *a) { - return gen_lsa(ctx, a->rd, a->rt, a->rs, a->sa); + return gen_lsa(ctx, a->rd, a->rt, a->rs, a->sa + 1); } static bool trans_DLSA(DisasContext *ctx, arg_r *a) @@ -788,5 +788,5 @@ static bool trans_DLSA(DisasContext *ctx, arg_r *a) if (TARGET_LONG_BITS != 64) { return false; } - return gen_dlsa(ctx, a->rd, a->rt, a->rs, a->sa); + return gen_dlsa(ctx, a->rd, a->rt, a->rs, a->sa + 1); } diff --git a/target/mips/tcg/nanomips_translate.c.inc b/target/mips/tcg/nanomips_translate.c.inc index 1e274143bb..9d4e0bee81 100644 --- a/target/mips/tcg/nanomips_translate.c.inc +++ b/target/mips/tcg/nanomips_translate.c.inc @@ -3626,12 +3626,7 @@ static int decode_nanomips_32_48_opc(CPUMIPSState *env, DisasContext *ctx) gen_p_lsx(ctx, rd, rs, rt); break; case NM_LSA: - /* - * In nanoMIPS, the shift field directly encodes the shift - * amount, meaning that the supported shift values are in - * the range 0 to 3 (instead of 1 to 4 in MIPSR6). - */ - gen_lsa(ctx, rd, rt, rs, extract32(ctx->opcode, 9, 2) - 1); + gen_lsa(ctx, rd, rt, rs, extract32(ctx->opcode, 9, 2)); break; case NM_EXTW: gen_ext(ctx, 32, rd, rs, rt, extract32(ctx->opcode, 6, 5)); diff --git a/target/mips/tcg/rel6_translate.c b/target/mips/tcg/rel6_translate.c index 8933506397..4c056621c9 100644 --- a/target/mips/tcg/rel6_translate.c +++ b/target/mips/tcg/rel6_translate.c @@ -23,7 +23,7 @@ bool trans_REMOVED(DisasContext *ctx, arg_REMOVED *a) static bool trans_LSA(DisasContext *ctx, arg_r *a) { - return gen_lsa(ctx, a->rd, a->rt, a->rs, a->sa); + return gen_lsa(ctx, a->rd, a->rt, a->rs, a->sa + 1); } static bool trans_DLSA(DisasContext *ctx, arg_r *a) @@ -31,7 +31,7 @@ static bool trans_DLSA(DisasContext *ctx, arg_r *a) if (TARGET_LONG_BITS != 64) { return false; } - return gen_dlsa(ctx, a->rd, a->rt, a->rs, a->sa); + return gen_dlsa(ctx, a->rd, a->rt, a->rs, a->sa + 1); } static bool trans_CRC32(DisasContext *ctx, arg_special3_crc *a) diff --git a/target/mips/tcg/translate_addr_const.c b/target/mips/tcg/translate_addr_const.c index 6f4b39f715..1d140e918d 100644 --- a/target/mips/tcg/translate_addr_const.c +++ b/target/mips/tcg/translate_addr_const.c @@ -26,7 +26,7 @@ bool gen_lsa(DisasContext *ctx, int rd, int rt, int rs, int sa) t1 = tcg_temp_new(); gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); - tcg_gen_shli_tl(t0, t0, sa + 1); + tcg_gen_shli_tl(t0, t0, sa); tcg_gen_add_tl(cpu_gpr[rd], t0, t1); tcg_gen_ext32s_tl(cpu_gpr[rd], cpu_gpr[rd]); return true; @@ -47,7 +47,7 @@ bool gen_dlsa(DisasContext *ctx, int rd, int rt, int rs, int sa) t1 = tcg_temp_new(); gen_load_gpr(t0, rs); gen_load_gpr(t1, rt); - tcg_gen_shli_tl(t0, t0, sa + 1); + tcg_gen_shli_tl(t0, t0, sa); tcg_gen_add_tl(cpu_gpr[rd], t0, t1); return true; } From c083f1f3de93c80df757444aa22837f0a2db3172 Mon Sep 17 00:00:00 2001 From: Aleksandar Rakic Date: Fri, 14 Feb 2025 17:37:42 +0000 Subject: [PATCH 2343/2760] tests/tcg/mips: Add tests for MIPS CRC32[c] instructions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Yongbok Kim Signed-off-by: Aleksandar Markovic Signed-off-by: Aleksandar Rakic Reviewed-by: Aleksandar Rikalo Message-ID: <20250214173702.2308488-3-aleksandar.rakic@htecgroup.com> Signed-off-by: Philippe Mathieu-Daudé --- tests/tcg/mips/include/wrappers_mips64r6.h | 32 ++++ tests/tcg/mips/user/isa/mips64r6/crc/Makefile | 40 +++++ .../isa/mips64r6/crc/test_mips64r6_crc32b.c | 142 ++++++++++++++++++ .../isa/mips64r6/crc/test_mips64r6_crc32cb.c | 142 ++++++++++++++++++ .../isa/mips64r6/crc/test_mips64r6_crc32cd.c | 142 ++++++++++++++++++ .../isa/mips64r6/crc/test_mips64r6_crc32ch.c | 142 ++++++++++++++++++ .../isa/mips64r6/crc/test_mips64r6_crc32cw.c | 142 ++++++++++++++++++ .../isa/mips64r6/crc/test_mips64r6_crc32d.c | 142 ++++++++++++++++++ .../isa/mips64r6/crc/test_mips64r6_crc32h.c | 142 ++++++++++++++++++ .../isa/mips64r6/crc/test_mips64r6_crc32w.c | 142 ++++++++++++++++++ 10 files changed, 1208 insertions(+) create mode 100644 tests/tcg/mips/user/isa/mips64r6/crc/Makefile create mode 100644 tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32b.c create mode 100644 tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cb.c create mode 100644 tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cd.c create mode 100644 tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32ch.c create mode 100644 tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cw.c create mode 100644 tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32d.c create mode 100644 tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32h.c create mode 100644 tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32w.c diff --git a/tests/tcg/mips/include/wrappers_mips64r6.h b/tests/tcg/mips/include/wrappers_mips64r6.h index d1e5edb632..33d03de50b 100644 --- a/tests/tcg/mips/include/wrappers_mips64r6.h +++ b/tests/tcg/mips/include/wrappers_mips64r6.h @@ -23,6 +23,7 @@ #ifndef WRAPPERS_MIPS64R6_H #define WRAPPERS_MIPS64R6_H +#include #define DO_MIPS64R6__RD__RS(suffix, mnemonic) \ static inline void do_mips64r6_##suffix(const void *input, \ @@ -80,4 +81,35 @@ DO_MIPS64R6__RD__RS_RT(DMULU, dmulu) DO_MIPS64R6__RD__RS_RT(DMUHU, dmuhu) +#define DO_MIPS64R6__RT__RS_RT(suffix, mnemonic) \ +static inline void do_mips64r6_##suffix(const void *input1, \ + const void *input2, \ + void *output) \ +{ \ + if (strncmp(#mnemonic, "crc32", 5) == 0) \ + __asm__ volatile ( \ + ".set crc\n\t" \ + ); \ + \ + __asm__ volatile ( \ + "ld $t1, 0(%0)\n\t" \ + "ld $t2, 0(%1)\n\t" \ + #mnemonic " $t2, $t1, $t2\n\t" \ + "sd $t2, 0(%2)\n\t" \ + : \ + : "r" (input1), "r" (input2), "r" (output) \ + : "t0", "t1", "t2", "memory" \ + ); \ +} + +DO_MIPS64R6__RT__RS_RT(CRC32B, crc32b) +DO_MIPS64R6__RT__RS_RT(CRC32H, crc32h) +DO_MIPS64R6__RT__RS_RT(CRC32W, crc32w) +DO_MIPS64R6__RT__RS_RT(CRC32D, crc32d) + +DO_MIPS64R6__RT__RS_RT(CRC32CB, crc32cb) +DO_MIPS64R6__RT__RS_RT(CRC32CH, crc32ch) +DO_MIPS64R6__RT__RS_RT(CRC32CW, crc32cw) +DO_MIPS64R6__RT__RS_RT(CRC32CD, crc32cd) + #endif diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/Makefile b/tests/tcg/mips/user/isa/mips64r6/crc/Makefile new file mode 100644 index 0000000000..b7f5811a5e --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/Makefile @@ -0,0 +1,40 @@ +# +# Test program for MIPS64R6 CRC32 instructions +# +# Copyright (C) 2025 Aleksandar Rakic +# +# SPDX-License-Identifier: GPL-2.0-or-later +# + +ifndef PREFIX + $(error "PREFIX not set, please export GNU Toolchain install directory.") +endif + +ifndef SYSROOT + $(error "SYSROOT not set, please export GNU Toolchain system root directory.") +endif + +SIM = ../../../../../../../build/qemu-mips64 +SIM_FLAGS = -L $(SYSROOT) + +CC = $(PREFIX)/bin/mips64-r6-linux-gnu-gcc + +TESTCASES = test_mips64r6_crc32b.tst +TESTCASES += test_mips64r6_crc32h.tst +TESTCASES += test_mips64r6_crc32w.tst +TESTCASES += test_mips64r6_crc32d.tst +TESTCASES += test_mips64r6_crc32cb.tst +TESTCASES += test_mips64r6_crc32ch.tst +TESTCASES += test_mips64r6_crc32cw.tst +TESTCASES += test_mips64r6_crc32cd.tst + +all: $(TESTCASES) + @for case in $(TESTCASES); do \ + echo $(SIM) $(SIM_FLAGS) ./$$case; \ + $(SIM) $(SIM_FLAGS) ./$$case; \ + echo $(RM) -rf ./$$case; \ + $(RM) -rf ./$$case; \ + done + +%.tst: %.c + $(CC) $< -o $@ diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32b.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32b.c new file mode 100644 index 0000000000..bb1f3f6924 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32b.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32B + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * Copyright (C) 2025 Aleksandar Rakic + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0xEDB88320"; + char *instruction_name = "CRC32B"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000ffffffULL, /* 0 */ + 0x000000002d02ef8dULL, + 0x000000001bab0fd1ULL, + 0x0000000036561fa3ULL, + 0xffffffffbf1caddaULL, + 0xffffffff92e1bda8ULL, + 0x00000000278c7949ULL, + 0x000000000a71693bULL, + 0x000000002dfd1072ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000036a9e05cULL, + 0x000000001b54f02eULL, + 0xffffffff921e4257ULL, + 0xffffffffbfe35225ULL, + 0x000000000a8e96c4ULL, + 0x00000000277386b6ULL, + 0x000000001bfe5a84ULL, /* 16 */ + 0x0000000036034af6ULL, + 0x0000000000aaaaaaULL, + 0x000000002d57bad8ULL, + 0xffffffffa41d08a1ULL, + 0xffffffff89e018d3ULL, + 0x000000003c8ddc32ULL, + 0x000000001170cc40ULL, + 0x0000000036fcb509ULL, /* 24 */ + 0x000000001b01a57bULL, + 0x000000002da84527ULL, + 0x0000000000555555ULL, + 0xffffffff891fe72cULL, + 0xffffffffa4e2f75eULL, + 0x00000000118f33bfULL, + 0x000000003c7223cdULL, + 0xffffffffbf2f9ee9ULL, /* 32 */ + 0xffffffff92d28e9bULL, + 0xffffffffa47b6ec7ULL, + 0xffffffff89867eb5ULL, + 0x0000000000ccccccULL, + 0x000000002d31dcbeULL, + 0xffffffff985c185fULL, + 0xffffffffb5a1082dULL, + 0xffffffff922d7164ULL, /* 40 */ + 0xffffffffbfd06116ULL, + 0xffffffff8979814aULL, + 0xffffffffa4849138ULL, + 0x000000002dce2341ULL, + 0x0000000000333333ULL, + 0xffffffffb55ef7d2ULL, + 0xffffffff98a3e7a0ULL, + 0x0000000027fdbe55ULL, /* 48 */ + 0x000000000a00ae27ULL, + 0x000000003ca94e7bULL, + 0x0000000011545e09ULL, + 0xffffffff981eec70ULL, + 0xffffffffb5e3fc02ULL, + 0x00000000008e38e3ULL, + 0x000000002d732891ULL, + 0x000000000aff51d8ULL, /* 56 */ + 0x00000000270241aaULL, + 0x0000000011aba1f6ULL, + 0x000000003c56b184ULL, + 0xffffffffb51c03fdULL, + 0xffffffff98e1138fULL, + 0x000000002d8cd76eULL, + 0x000000000071c71cULL, + 0x0000000000286255ULL, /* 64 */ + 0x00000000784a5a65ULL, + 0xffffffff9bdd0d3bULL, + 0xffffffffe7e61ce5ULL, + 0x00000000782fabf7ULL, + 0x00000000004d93c7ULL, + 0xffffffffe3dac499ULL, + 0xffffffff9fe1d547ULL, + 0xffffffff9b4ca0e5ULL, /* 72 */ + 0xffffffffe32e98d5ULL, + 0x0000000000b9cf8bULL, + 0x000000007c82de55ULL, + 0xffffffffe7904f52ULL, + 0xffffffff9ff27762ULL, + 0x000000007c65203cULL, + 0x00000000005e31e2ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32B(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32B(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cb.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cb.c new file mode 100644 index 0000000000..1439d44bf2 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cb.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32CB + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * Copyright (C) 2025 Aleksandar Rakic + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0x82F63B78"; + char *instruction_name = "CRC32CB"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000ffffffULL, /* 0 */ + 0xffffffffad7d5351ULL, + 0x00000000647e6465ULL, + 0xffffffffc9fcc8cbULL, + 0x00000000237f7689ULL, + 0xffffffff8efdda27ULL, + 0xffffffff837defedULL, + 0x000000002eff4343ULL, + 0xffffffffad82acaeULL, /* 8 */ + 0x0000000000000000ULL, + 0xffffffffc9033734ULL, + 0x0000000064819b9aULL, + 0xffffffff8e0225d8ULL, + 0x0000000023808976ULL, + 0x000000002e00bcbcULL, + 0xffffffff83821012ULL, + 0x00000000642b3130ULL, /* 16 */ + 0xffffffffc9a99d9eULL, + 0x0000000000aaaaaaULL, + 0xffffffffad280604ULL, + 0x0000000047abb846ULL, + 0xffffffffea2914e8ULL, + 0xffffffffe7a92122ULL, + 0x000000004a2b8d8cULL, + 0xffffffffc9566261ULL, /* 24 */ + 0x0000000064d4cecfULL, + 0xffffffffadd7f9fbULL, + 0x0000000000555555ULL, + 0xffffffffead6eb17ULL, + 0x00000000475447b9ULL, + 0x000000004ad47273ULL, + 0xffffffffe756deddULL, + 0x00000000234c45baULL, /* 32 */ + 0xffffffff8ecee914ULL, + 0x0000000047cdde20ULL, + 0xffffffffea4f728eULL, + 0x0000000000ccccccULL, + 0xffffffffad4e6062ULL, + 0xffffffffa0ce55a8ULL, + 0x000000000d4cf906ULL, + 0xffffffff8e3116ebULL, /* 40 */ + 0x0000000023b3ba45ULL, + 0xffffffffeab08d71ULL, + 0x00000000473221dfULL, + 0xffffffffadb19f9dULL, + 0x0000000000333333ULL, + 0x000000000db306f9ULL, + 0xffffffffa031aa57ULL, + 0xffffffff830c28f1ULL, /* 48 */ + 0x000000002e8e845fULL, + 0xffffffffe78db36bULL, + 0x000000004a0f1fc5ULL, + 0xffffffffa08ca187ULL, + 0x000000000d0e0d29ULL, + 0x00000000008e38e3ULL, + 0xffffffffad0c944dULL, + 0x000000002e717ba0ULL, /* 56 */ + 0xffffffff83f3d70eULL, + 0x000000004af0e03aULL, + 0xffffffffe7724c94ULL, + 0x000000000df1f2d6ULL, + 0xffffffffa0735e78ULL, + 0xffffffffadf36bb2ULL, + 0x000000000071c71cULL, + 0x0000000000286255ULL, /* 64 */ + 0xffffffffcbefd6b4ULL, + 0xffffffffc334e94fULL, + 0xffffffffac268ec5ULL, + 0xffffffffcb8a2726ULL, + 0x00000000004d93c7ULL, + 0x000000000896ac3cULL, + 0x000000006784cbb6ULL, + 0xffffffffc3a54491ULL, /* 72 */ + 0x000000000862f070ULL, + 0x0000000000b9cf8bULL, + 0x000000006faba801ULL, + 0xffffffffac50dd72ULL, + 0x0000000067976993ULL, + 0x000000006f4c5668ULL, + 0x00000000005e31e2ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CB(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CB(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cd.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cd.c new file mode 100644 index 0000000000..bf258e0696 --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cd.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32CD + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * Copyright (C) 2025 Aleksandar Rakic + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0x82F63B78"; + char *instruction_name = "CRC32CD"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xffffffffb798b438ULL, /* 0 */ + 0xffffffffc44ff94dULL, + 0xffffffff992a70ebULL, + 0xffffffffeafd3d9eULL, + 0x000000005152da26ULL, + 0x0000000022859753ULL, + 0x0000000015cb6d32ULL, + 0x00000000661c2047ULL, + 0x0000000073d74d75ULL, /* 8 */ + 0x0000000000000000ULL, + 0x000000005d6589a6ULL, + 0x000000002eb2c4d3ULL, + 0xffffffff951d236bULL, + 0xffffffffe6ca6e1eULL, + 0xffffffffd184947fULL, + 0xffffffffa253d90aULL, + 0x0000000008f9ceacULL, /* 16 */ + 0x000000007b2e83d9ULL, + 0x00000000264b0a7fULL, + 0x00000000559c470aULL, + 0xffffffffee33a0b2ULL, + 0xffffffff9de4edc7ULL, + 0xffffffffaaaa17a6ULL, + 0xffffffffd97d5ad3ULL, + 0xffffffffccb637e1ULL, /* 24 */ + 0xffffffffbf617a94ULL, + 0xffffffffe204f332ULL, + 0xffffffff91d3be47ULL, + 0x000000002a7c59ffULL, + 0x0000000059ab148aULL, + 0x000000006ee5eeebULL, + 0x000000001d32a39eULL, + 0x0000000021e3b01bULL, /* 32 */ + 0x000000005234fd6eULL, + 0x000000000f5174c8ULL, + 0x000000007c8639bdULL, + 0xffffffffc729de05ULL, + 0xffffffffb4fe9370ULL, + 0xffffffff83b06911ULL, + 0xfffffffff0672464ULL, + 0xffffffffe5ac4956ULL, /* 40 */ + 0xffffffff967b0423ULL, + 0xffffffffcb1e8d85ULL, + 0xffffffffb8c9c0f0ULL, + 0x0000000003662748ULL, + 0x0000000070b16a3dULL, + 0x0000000047ff905cULL, + 0x000000003428dd29ULL, + 0xffffffffb89d59a6ULL, /* 48 */ + 0xffffffffcb4a14d3ULL, + 0xffffffff962f9d75ULL, + 0xffffffffe5f8d000ULL, + 0x000000005e5737b8ULL, + 0x000000002d807acdULL, + 0x000000001ace80acULL, + 0x000000006919cdd9ULL, + 0x000000007cd2a0ebULL, /* 56 */ + 0x000000000f05ed9eULL, + 0x0000000052606438ULL, + 0x0000000021b7294dULL, + 0xffffffff9a18cef5ULL, + 0xffffffffe9cf8380ULL, + 0xffffffffde8179e1ULL, + 0xffffffffad563494ULL, + 0x000000003a358bb3ULL, /* 64 */ + 0xffffffff975446ebULL, + 0x0000000041d37ad6ULL, + 0x000000004be84fe1ULL, + 0xffffffff9671b1b3ULL, + 0x000000003b107cebULL, + 0xffffffffed9740d6ULL, + 0xffffffffe7ac75e1ULL, + 0xffffffffa1489696ULL, /* 72 */ + 0x000000000c295bceULL, + 0xffffffffdaae67f3ULL, + 0xffffffffd09552c4ULL, + 0x0000000042bd7071ULL, + 0xffffffffefdcbd29ULL, + 0x00000000395b8114ULL, + 0x000000003360b423ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CD(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CD(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32ch.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32ch.c new file mode 100644 index 0000000000..0e7b67732e --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32ch.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32CH + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * Copyright (C) 2025 Aleksandar Rakic + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0x82F63B78"; + char *instruction_name = "CRC32CH"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x000000000000ffffULL, /* 0 */ + 0x000000000e9e77d2ULL, + 0xfffffffff92eaa4bULL, + 0xfffffffff7b02266ULL, + 0x00000000571acc93ULL, + 0x00000000598444beULL, + 0xfffffffff1e6ca77ULL, + 0xffffffffff78425aULL, + 0x000000000e9e882dULL, /* 8 */ + 0x0000000000000000ULL, + 0xfffffffff7b0dd99ULL, + 0xfffffffff92e55b4ULL, + 0x000000005984bb41ULL, + 0x00000000571a336cULL, + 0xffffffffff78bda5ULL, + 0xfffffffff1e63588ULL, + 0xfffffffff92eff1eULL, /* 16 */ + 0xfffffffff7b07733ULL, + 0x000000000000aaaaULL, + 0x000000000e9e2287ULL, + 0xffffffffae34cc72ULL, + 0xffffffffa0aa445fULL, + 0x0000000008c8ca96ULL, + 0x00000000065642bbULL, + 0xfffffffff7b088ccULL, /* 24 */ + 0xfffffffff92e00e1ULL, + 0x000000000e9edd78ULL, + 0x0000000000005555ULL, + 0xffffffffa0aabba0ULL, + 0xffffffffae34338dULL, + 0x000000000656bd44ULL, + 0x0000000008c83569ULL, + 0x00000000571affa0ULL, /* 32 */ + 0x000000005984778dULL, + 0xffffffffae34aa14ULL, + 0xffffffffa0aa2239ULL, + 0x000000000000ccccULL, + 0x000000000e9e44e1ULL, + 0xffffffffa6fcca28ULL, + 0xffffffffa8624205ULL, + 0x0000000059848872ULL, /* 40 */ + 0x00000000571a005fULL, + 0xffffffffa0aaddc6ULL, + 0xffffffffae3455ebULL, + 0x000000000e9ebb1eULL, + 0x0000000000003333ULL, + 0xffffffffa862bdfaULL, + 0xffffffffa6fc35d7ULL, + 0xfffffffff1e6bbb0ULL, /* 48 */ + 0xffffffffff78339dULL, + 0x0000000008c8ee04ULL, + 0x0000000006566629ULL, + 0xffffffffa6fc88dcULL, + 0xffffffffa86200f1ULL, + 0x0000000000008e38ULL, + 0x000000000e9e0615ULL, + 0xffffffffff78cc62ULL, /* 56 */ + 0xfffffffff1e6444fULL, + 0x00000000065699d6ULL, + 0x0000000008c811fbULL, + 0xffffffffa862ff0eULL, + 0xffffffffa6fc7723ULL, + 0x000000000e9ef9eaULL, + 0x00000000000071c7ULL, + 0x0000000000002862ULL, /* 64 */ + 0x000000001190c4cfULL, + 0x000000007b7fdbbeULL, + 0xffffffff9204da99ULL, + 0x000000001190a13eULL, + 0x0000000000004d93ULL, + 0x000000006aef52e2ULL, + 0xffffffff839453c5ULL, + 0x000000007b7f4a13ULL, /* 72 */ + 0x000000006aefa6beULL, + 0x000000000000b9cfULL, + 0xffffffffe97bb8e8ULL, + 0xffffffff9204accaULL, + 0xffffffff83944067ULL, + 0xffffffffe97b5f16ULL, + 0x0000000000005e31ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CH(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CH(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cw.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cw.c new file mode 100644 index 0000000000..f7110b3a0a --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32cw.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32CW + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * Copyright (C) 2025 Aleksandar Rakic + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0x82F63B78"; + char *instruction_name = "CRC32CW"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000000ULL, /* 0 */ + 0xffffffffb798b438ULL, + 0xffffffff91d3be47ULL, + 0x00000000264b0a7fULL, + 0x0000000070b16a3dULL, + 0xffffffffc729de05ULL, + 0x0000000063c5950aULL, + 0xffffffffd45d2132ULL, + 0xffffffffb798b438ULL, /* 8 */ + 0x0000000000000000ULL, + 0x00000000264b0a7fULL, + 0xffffffff91d3be47ULL, + 0xffffffffc729de05ULL, + 0x0000000070b16a3dULL, + 0xffffffffd45d2132ULL, + 0x0000000063c5950aULL, + 0xffffffff91d3be47ULL, /* 16 */ + 0x00000000264b0a7fULL, + 0x0000000000000000ULL, + 0xffffffffb798b438ULL, + 0xffffffffe162d47aULL, + 0x0000000056fa6042ULL, + 0xfffffffff2162b4dULL, + 0x00000000458e9f75ULL, + 0x00000000264b0a7fULL, /* 24 */ + 0xffffffff91d3be47ULL, + 0xffffffffb798b438ULL, + 0x0000000000000000ULL, + 0x0000000056fa6042ULL, + 0xffffffffe162d47aULL, + 0x00000000458e9f75ULL, + 0xfffffffff2162b4dULL, + 0x0000000070b16a3dULL, /* 32 */ + 0xffffffffc729de05ULL, + 0xffffffffe162d47aULL, + 0x0000000056fa6042ULL, + 0x0000000000000000ULL, + 0xffffffffb798b438ULL, + 0x000000001374ff37ULL, + 0xffffffffa4ec4b0fULL, + 0xffffffffc729de05ULL, /* 40 */ + 0x0000000070b16a3dULL, + 0x0000000056fa6042ULL, + 0xffffffffe162d47aULL, + 0xffffffffb798b438ULL, + 0x0000000000000000ULL, + 0xffffffffa4ec4b0fULL, + 0x000000001374ff37ULL, + 0x0000000063c5950aULL, /* 48 */ + 0xffffffffd45d2132ULL, + 0xfffffffff2162b4dULL, + 0x00000000458e9f75ULL, + 0x000000001374ff37ULL, + 0xffffffffa4ec4b0fULL, + 0x0000000000000000ULL, + 0xffffffffb798b438ULL, + 0xffffffffd45d2132ULL, /* 56 */ + 0x0000000063c5950aULL, + 0x00000000458e9f75ULL, + 0xfffffffff2162b4dULL, + 0xffffffffa4ec4b0fULL, + 0x000000001374ff37ULL, + 0xffffffffb798b438ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 64 */ + 0xffffffffea0755b2ULL, + 0x0000000008b188e6ULL, + 0xffffffffff3cc8d9ULL, + 0xffffffffea0755b2ULL, + 0x0000000000000000ULL, + 0xffffffffe2b6dd54ULL, + 0x00000000153b9d6bULL, + 0x0000000008b188e6ULL, /* 72 */ + 0xffffffffe2b6dd54ULL, + 0x0000000000000000ULL, + 0xfffffffff78d403fULL, + 0xffffffffff3cc8d9ULL, + 0x00000000153b9d6bULL, + 0xfffffffff78d403fULL, + 0x0000000000000000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CW(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32CW(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32d.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32d.c new file mode 100644 index 0000000000..e391be803f --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32d.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32D + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * Copyright (C) 2025 Aleksandar Rakic + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0xEDB88320"; + char *instruction_name = "CRC32D"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0xffffffffdebb20e3ULL, /* 0 */ + 0x0000000044660075ULL, + 0x000000001e20c2aeULL, + 0xffffffff84fde238ULL, + 0x00000000281d7ce7ULL, + 0xffffffffb2c05c71ULL, + 0xffffffffd660a024ULL, + 0x000000004cbd80b2ULL, + 0xffffffff9add2096ULL, /* 8 */ + 0x0000000000000000ULL, + 0x000000005a46c2dbULL, + 0xffffffffc09be24dULL, + 0x000000006c7b7c92ULL, + 0xfffffffff6a65c04ULL, + 0xffffffff9206a051ULL, + 0x0000000008db80c7ULL, + 0x000000005449dd0fULL, /* 16 */ + 0xffffffffce94fd99ULL, + 0xffffffff94d23f42ULL, + 0x000000000e0f1fd4ULL, + 0xffffffffa2ef810bULL, + 0x000000003832a19dULL, + 0x000000005c925dc8ULL, + 0xffffffffc64f7d5eULL, + 0x00000000102fdd7aULL, /* 24 */ + 0xffffffff8af2fdecULL, + 0xffffffffd0b43f37ULL, + 0x000000004a691fa1ULL, + 0xffffffffe689817eULL, + 0x000000007c54a1e8ULL, + 0x0000000018f45dbdULL, + 0xffffffff82297d2bULL, + 0xffffffffa7157447ULL, /* 32 */ + 0x000000003dc854d1ULL, + 0x00000000678e960aULL, + 0xfffffffffd53b69cULL, + 0x0000000051b32843ULL, + 0xffffffffcb6e08d5ULL, + 0xffffffffafcef480ULL, + 0x000000003513d416ULL, + 0xffffffffe3737432ULL, /* 40 */ + 0x0000000079ae54a4ULL, + 0x0000000023e8967fULL, + 0xffffffffb935b6e9ULL, + 0x0000000015d52836ULL, + 0xffffffff8f0808a0ULL, + 0xffffffffeba8f4f5ULL, + 0x000000007175d463ULL, + 0x000000007a6adc3eULL, /* 48 */ + 0xffffffffe0b7fca8ULL, + 0xffffffffbaf13e73ULL, + 0x00000000202c1ee5ULL, + 0xffffffff8ccc803aULL, + 0x000000001611a0acULL, + 0x0000000072b15cf9ULL, + 0xffffffffe86c7c6fULL, + 0x000000003e0cdc4bULL, /* 56 */ + 0xffffffffa4d1fcddULL, + 0xfffffffffe973e06ULL, + 0x00000000644a1e90ULL, + 0xffffffffc8aa804fULL, + 0x000000005277a0d9ULL, + 0x0000000036d75c8cULL, + 0xffffffffac0a7c1aULL, + 0xffffffffed857593ULL, /* 64 */ + 0xffffffffe0b6f95fULL, + 0x00000000253b462cULL, + 0xffffffffe15579b9ULL, + 0x0000000074897c83ULL, + 0x0000000079baf04fULL, + 0xffffffffbc374f3cULL, + 0x00000000785970a9ULL, + 0xffffffffa6bae0a9ULL, /* 72 */ + 0xffffffffab896c65ULL, + 0x000000006e04d316ULL, + 0xffffffffaa6aec83ULL, + 0x000000005ae171feULL, + 0x0000000057d2fd32ULL, + 0xffffffff925f4241ULL, + 0x0000000056317dd4ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32D(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32D(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32h.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32h.c new file mode 100644 index 0000000000..100f02c7dd --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32h.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32H + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * Copyright (C) 2025 Aleksandar Rakic + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0xEDB88320"; + char *instruction_name = "CRC32H"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x000000000000ffffULL, /* 0 */ + 0xffffffffbe2612ffULL, + 0xffffffffdccda6c0ULL, + 0x0000000062eb4bc0ULL, + 0x000000004bbbc8eaULL, + 0xfffffffff59d25eaULL, + 0x0000000022259ac0ULL, + 0xffffffff9c0377c0ULL, + 0xffffffffbe26ed00ULL, /* 8 */ + 0x0000000000000000ULL, + 0x0000000062ebb43fULL, + 0xffffffffdccd593fULL, + 0xfffffffff59dda15ULL, + 0x000000004bbb3715ULL, + 0xffffffff9c03883fULL, + 0x000000002225653fULL, + 0xffffffffdccdf395ULL, /* 16 */ + 0x0000000062eb1e95ULL, + 0x000000000000aaaaULL, + 0xffffffffbe2647aaULL, + 0xffffffff9776c480ULL, + 0x0000000029502980ULL, + 0xfffffffffee896aaULL, + 0x0000000040ce7baaULL, + 0x0000000062ebe16aULL, /* 24 */ + 0xffffffffdccd0c6aULL, + 0xffffffffbe26b855ULL, + 0x0000000000005555ULL, + 0x000000002950d67fULL, + 0xffffffff97763b7fULL, + 0x0000000040ce8455ULL, + 0xfffffffffee86955ULL, + 0x000000004bbbfbd9ULL, /* 32 */ + 0xfffffffff59d16d9ULL, + 0xffffffff9776a2e6ULL, + 0x0000000029504fe6ULL, + 0x000000000000ccccULL, + 0xffffffffbe2621ccULL, + 0x00000000699e9ee6ULL, + 0xffffffffd7b873e6ULL, + 0xfffffffff59de926ULL, /* 40 */ + 0x000000004bbb0426ULL, + 0x000000002950b019ULL, + 0xffffffff97765d19ULL, + 0xffffffffbe26de33ULL, + 0x0000000000003333ULL, + 0xffffffffd7b88c19ULL, + 0x00000000699e6119ULL, + 0x000000002225eb07ULL, /* 48 */ + 0xffffffff9c030607ULL, + 0xfffffffffee8b238ULL, + 0x0000000040ce5f38ULL, + 0x00000000699edc12ULL, + 0xffffffffd7b83112ULL, + 0x0000000000008e38ULL, + 0xffffffffbe266338ULL, + 0xffffffff9c03f9f8ULL, /* 56 */ + 0x00000000222514f8ULL, + 0x0000000040cea0c7ULL, + 0xfffffffffee84dc7ULL, + 0xffffffffd7b8ceedULL, + 0x00000000699e23edULL, + 0xffffffffbe269cc7ULL, + 0x00000000000071c7ULL, + 0x0000000000002862ULL, /* 64 */ + 0x0000000026a17af6ULL, + 0xffffffffaa919152ULL, + 0xffffffffcb865590ULL, + 0x0000000026a11f07ULL, + 0x0000000000004d93ULL, + 0xffffffff8c30a637ULL, + 0xffffffffed2762f5ULL, + 0xffffffffaa9100ffULL, /* 72 */ + 0xffffffff8c30526bULL, + 0x000000000000b9cfULL, + 0x0000000061177d0dULL, + 0xffffffffcb8623c3ULL, + 0xffffffffed277157ULL, + 0x0000000061179af3ULL, + 0x0000000000005e31ULL + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32H(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32H(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} diff --git a/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32w.c b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32w.c new file mode 100644 index 0000000000..b4f5f4bbee --- /dev/null +++ b/tests/tcg/mips/user/isa/mips64r6/crc/test_mips64r6_crc32w.c @@ -0,0 +1,142 @@ +/* + * Test program for MIPS64R6 instruction CRC32W + * + * Copyright (C) 2019 Wave Computing, Inc. + * Copyright (C) 2019 Aleksandar Markovic + * Copyright (C) 2025 Aleksandar Rakic + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include +#include + +#include "../../../../include/wrappers_mips64r6.h" +#include "../../../../include/test_inputs_64.h" +#include "../../../../include/test_utils_64.h" + +#define TEST_COUNT_TOTAL (PATTERN_INPUTS_64_COUNT + RANDOM_INPUTS_64_COUNT) + +int32_t main(void) +{ + char *isa_ase_name = "mips64r6"; + char *group_name = "CRC with reversed polynomial 0xEDB88320"; + char *instruction_name = "CRC32W"; + int32_t ret; + uint32_t i, j; + struct timeval start, end; + double elapsed_time; + + uint64_t b64_result[TEST_COUNT_TOTAL]; + uint64_t b64_expect[TEST_COUNT_TOTAL] = { + 0x0000000000000000ULL, /* 0 */ + 0xffffffffdebb20e3ULL, + 0x000000004a691fa1ULL, + 0xffffffff94d23f42ULL, + 0xffffffff8f0808a0ULL, + 0x0000000051b32843ULL, + 0x0000000065069dceULL, + 0xffffffffbbbdbd2dULL, + 0xffffffffdebb20e3ULL, /* 8 */ + 0x0000000000000000ULL, + 0xffffffff94d23f42ULL, + 0x000000004a691fa1ULL, + 0x0000000051b32843ULL, + 0xffffffff8f0808a0ULL, + 0xffffffffbbbdbd2dULL, + 0x0000000065069dceULL, + 0x000000004a691fa1ULL, /* 16 */ + 0xffffffff94d23f42ULL, + 0x0000000000000000ULL, + 0xffffffffdebb20e3ULL, + 0xffffffffc5611701ULL, + 0x000000001bda37e2ULL, + 0x000000002f6f826fULL, + 0xfffffffff1d4a28cULL, + 0xffffffff94d23f42ULL, /* 24 */ + 0x000000004a691fa1ULL, + 0xffffffffdebb20e3ULL, + 0x0000000000000000ULL, + 0x000000001bda37e2ULL, + 0xffffffffc5611701ULL, + 0xfffffffff1d4a28cULL, + 0x000000002f6f826fULL, + 0xffffffff8f0808a0ULL, /* 32 */ + 0x0000000051b32843ULL, + 0xffffffffc5611701ULL, + 0x000000001bda37e2ULL, + 0x0000000000000000ULL, + 0xffffffffdebb20e3ULL, + 0xffffffffea0e956eULL, + 0x0000000034b5b58dULL, + 0x0000000051b32843ULL, /* 40 */ + 0xffffffff8f0808a0ULL, + 0x000000001bda37e2ULL, + 0xffffffffc5611701ULL, + 0xffffffffdebb20e3ULL, + 0x0000000000000000ULL, + 0x0000000034b5b58dULL, + 0xffffffffea0e956eULL, + 0x0000000065069dceULL, /* 48 */ + 0xffffffffbbbdbd2dULL, + 0x000000002f6f826fULL, + 0xfffffffff1d4a28cULL, + 0xffffffffea0e956eULL, + 0x0000000034b5b58dULL, + 0x0000000000000000ULL, + 0xffffffffdebb20e3ULL, + 0xffffffffbbbdbd2dULL, /* 56 */ + 0x0000000065069dceULL, + 0xfffffffff1d4a28cULL, + 0x000000002f6f826fULL, + 0x0000000034b5b58dULL, + 0xffffffffea0e956eULL, + 0xffffffffdebb20e3ULL, + 0x0000000000000000ULL, + 0x0000000000000000ULL, /* 64 */ + 0xffffffff90485967ULL, + 0x000000006dfb974aULL, + 0x00000000083e4538ULL, + 0xffffffff90485967ULL, + 0x0000000000000000ULL, + 0xfffffffffdb3ce2dULL, + 0xffffffff98761c5fULL, + 0x000000006dfb974aULL, /* 72 */ + 0xfffffffffdb3ce2dULL, + 0x0000000000000000ULL, + 0x0000000065c5d272ULL, + 0x00000000083e4538ULL, + 0xffffffff98761c5fULL, + 0x0000000065c5d272ULL, + 0x0000000000000000ULL, + }; + + gettimeofday(&start, NULL); + + for (i = 0; i < PATTERN_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < PATTERN_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32W(b64_pattern + i, b64_pattern + j, + b64_result + (PATTERN_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + for (i = 0; i < RANDOM_INPUTS_64_SHORT_COUNT; i++) { + for (j = 0; j < RANDOM_INPUTS_64_SHORT_COUNT; j++) { + do_mips64r6_CRC32W(b64_random + i, b64_random + j, + b64_result + (((PATTERN_INPUTS_64_SHORT_COUNT) * + (PATTERN_INPUTS_64_SHORT_COUNT)) + + RANDOM_INPUTS_64_SHORT_COUNT * i + j)); + } + } + + gettimeofday(&end, NULL); + + elapsed_time = (end.tv_sec - start.tv_sec) * 1000.0; + elapsed_time += (end.tv_usec - start.tv_usec) / 1000.0; + + ret = check_results_64(isa_ase_name, group_name, instruction_name, + TEST_COUNT_TOTAL, elapsed_time, b64_result, + b64_expect); + + return ret; +} From a598090ebaeb930ce33c2df0d80d87da13be8848 Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Sun, 18 May 2025 13:54:20 -0400 Subject: [PATCH 2344/2760] roms: re-remove execute bit from hppa-firmware* MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was fixed in c9d77526bddba0803a1fa982fb59ec98057150f9 for 9.2.0 but regressed in db34be329162cf6b06192703065e6c1010dbe3c5 in 10.0.0 When the bit is present, rpmbuild complains about missing ELF build-id Signed-off-by: Cole Robinson Reviewed-by: Daniel P. Berrangé Acked-by: Helge Deller Message-ID: <52d0edfbb9b2f63a866f0065a721f3a95da6f8ba.1747590860.git.crobinso@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- pc-bios/hppa-firmware.img | Bin pc-bios/hppa-firmware64.img | Bin 2 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 pc-bios/hppa-firmware.img mode change 100755 => 100644 pc-bios/hppa-firmware64.img diff --git a/pc-bios/hppa-firmware.img b/pc-bios/hppa-firmware.img old mode 100755 new mode 100644 diff --git a/pc-bios/hppa-firmware64.img b/pc-bios/hppa-firmware64.img old mode 100755 new mode 100644 From 63e7af2035242dda6e2460f4eadbbe6f58c67614 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 25 Jun 2025 23:59:49 +0200 Subject: [PATCH 2345/2760] hw/mips: Restrict ITU to TCG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit MIPS Inter-Thread Communication Unit is implemented using TCG. Check for TCG both in Kconfig and CPS source. Fixes: 2321d971b6f ("hw/mips: Add dependency MIPS_CPS -> MIPS_ITU") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250702164953.18579-1-philmd@linaro.org> --- hw/mips/Kconfig | 2 +- hw/mips/cps.c | 4 ++-- hw/misc/Kconfig | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/mips/Kconfig b/hw/mips/Kconfig index b09c89a017..f84fffcd32 100644 --- a/hw/mips/Kconfig +++ b/hw/mips/Kconfig @@ -76,7 +76,7 @@ config LOONGSON3V config MIPS_CPS bool - select MIPS_ITU + select MIPS_ITU if TCG config MIPS_BOSTON bool diff --git a/hw/mips/cps.c b/hw/mips/cps.c index 2a3ba3f58d..e47695e2b0 100644 --- a/hw/mips/cps.c +++ b/hw/mips/cps.c @@ -24,7 +24,7 @@ #include "hw/mips/mips.h" #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" -#include "system/kvm.h" +#include "system/tcg.h" #include "system/reset.h" qemu_irq get_cps_irq(MIPSCPSState *s, int pin_number) @@ -59,7 +59,7 @@ static bool cpu_mips_itu_supported(CPUMIPSState *env) { bool is_mt = (env->CP0_Config5 & (1 << CP0C5_VP)) || ase_mt_available(env); - return is_mt && !kvm_enabled(); + return is_mt && tcg_enabled(); } static void mips_cps_realize(DeviceState *dev, Error **errp) diff --git a/hw/misc/Kconfig b/hw/misc/Kconfig index c27285b47a..4e35657468 100644 --- a/hw/misc/Kconfig +++ b/hw/misc/Kconfig @@ -119,6 +119,7 @@ config STM32L4X5_RCC config MIPS_ITU bool + depends on TCG config MPS2_FPGAIO bool From 83110a6c662f392360b2d235de6c96cd09370823 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 10:55:20 +0200 Subject: [PATCH 2346/2760] hw/intc/loongarch_extioi: Remove unnecessary 'qemu/typedefs.h' include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "qemu/typedefs.h" is already included by "qemu/osdep.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Song Gao Message-Id: <20250708085859.7885-2-philmd@linaro.org> --- hw/intc/loongarch_extioi_kvm.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/intc/loongarch_extioi_kvm.c b/hw/intc/loongarch_extioi_kvm.c index 0133540c45..aa2e8c753f 100644 --- a/hw/intc/loongarch_extioi_kvm.c +++ b/hw/intc/loongarch_extioi_kvm.c @@ -6,7 +6,6 @@ */ #include "qemu/osdep.h" -#include "qemu/typedefs.h" #include "hw/intc/loongarch_extioi.h" #include "linux/kvm.h" #include "qapi/error.h" From 07e8cd2b8c757d646178879169df79fbf0a09eda Mon Sep 17 00:00:00 2001 From: Bernhard Beschow Date: Tue, 8 Jul 2025 22:48:04 +0200 Subject: [PATCH 2347/2760] hw/microblaze: Add missing FDT dependency MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit These boards ship with a bundled DTB, and dtc will be required for generating these from device tree sources. Prepare for that by adding an FDT dependency. Signed-off-by: Bernhard Beschow Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250708204806.1898-2-shentey@gmail.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/microblaze/Kconfig | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/microblaze/Kconfig b/hw/microblaze/Kconfig index b0214b2c8b..72d8072f76 100644 --- a/hw/microblaze/Kconfig +++ b/hw/microblaze/Kconfig @@ -1,7 +1,7 @@ config PETALOGIX_S3ADSP1800 bool default y - depends on MICROBLAZE + depends on MICROBLAZE && FDT select PFLASH_CFI01 select XILINX select XILINX_AXI @@ -11,7 +11,7 @@ config PETALOGIX_S3ADSP1800 config PETALOGIX_ML605 bool default y - depends on MICROBLAZE + depends on MICROBLAZE && FDT select PFLASH_CFI01 select SERIAL_MM select SSI_M25P80 From 1fa3812ee884dba8dbbd9d2f121b10c67469cae3 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 11 Jul 2025 21:46:30 +0100 Subject: [PATCH 2348/2760] esp.c: only raise IRQ in esp_transfer_data() for CMD_SEL, CMD_SELATN and CMD_TI commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clarify the logic in esp_transfer_data() to ensure that the deferred interrupt code can only be triggered for CMD_SEL, CMD_SELATN and CMD_TI commands. This should already be the case, but make it explicit to ensure the logic isn't triggered unexpectedly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250711204636.542964-2-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé --- hw/scsi/esp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index f24991fd16..9181c8810f 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -1012,6 +1012,7 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len) */ s->rregs[ESP_RINTR] |= INTR_BS | INTR_FC; s->rregs[ESP_RSEQ] = SEQ_CD; + esp_raise_irq(s); break; case CMD_SELATNS | CMD_DMA: @@ -1022,6 +1023,7 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len) */ s->rregs[ESP_RINTR] |= INTR_BS; s->rregs[ESP_RSEQ] = SEQ_MO; + esp_raise_irq(s); break; case CMD_TI | CMD_DMA: @@ -1032,10 +1034,9 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len) */ s->rregs[ESP_CMD] = 0; s->rregs[ESP_RINTR] |= INTR_BS; + esp_raise_irq(s); break; } - - esp_raise_irq(s); } /* From daaaec4f54ae0ad3e4304dc55c3c0f159988d773 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 11 Jul 2025 21:46:31 +0100 Subject: [PATCH 2349/2760] esp.c: improve comment in esp_transfer_data() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Whilst working on the previous patch, the existing comment was not enough to document when the TI command codepath was being used. Update and improve the comment accordingly. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250711204636.542964-3-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé --- hw/scsi/esp.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 9181c8810f..62ba406149 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -1029,8 +1029,9 @@ void esp_transfer_data(SCSIRequest *req, uint32_t len) case CMD_TI | CMD_DMA: case CMD_TI: /* - * Bus service interrupt raised because of initial change to - * DATA phase + * If the final COMMAND phase data was transferred using a TI + * command, clear ESP_CMD to terminate the TI command and raise + * the completion interrupt */ s->rregs[ESP_CMD] = 0; s->rregs[ESP_RINTR] |= INTR_BS; From e70aa5dc299c49c59bd91c80e771db56996c2188 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 11 Jul 2025 21:46:32 +0100 Subject: [PATCH 2350/2760] esp.h: remove separate ESPState typedef MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is not needed as it is now handled by the OBJECT_DECLARE_SIMPLE_TYPE() macro. Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250711204636.542964-4-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé --- include/hw/scsi/esp.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h index 533d856aa3..c9afcb7cac 100644 --- a/include/hw/scsi/esp.h +++ b/include/hw/scsi/esp.h @@ -14,8 +14,6 @@ typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len); #define ESP_FIFO_SZ 16 #define ESP_CMDFIFO_SZ 32 -typedef struct ESPState ESPState; - #define TYPE_ESP "esp" OBJECT_DECLARE_SIMPLE_TYPE(ESPState, ESP) From 9d6df740ec5e43bd8e4521901aca95f9780e524a Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 11 Jul 2025 21:46:33 +0100 Subject: [PATCH 2351/2760] esp.c: only call dma_memory_read function if transfer length is non-zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the cases where mixed DMA/non-DMA transfers are used or no data is available, it is possible for the calculated transfer length to be zero. Only call the dma_memory_read function where the transfer length is non-zero to avoid invoking the DMA engine for a zero length transfer which can have side-effects (along with generating additional tracing noise). Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250711204636.542964-5-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé --- hw/scsi/esp.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 62ba406149..ec9fcbeddf 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -487,8 +487,10 @@ static void esp_do_dma(ESPState *s) case STAT_MO: if (s->dma_memory_read) { len = MIN(len, fifo8_num_free(&s->cmdfifo)); - s->dma_memory_read(s->dma_opaque, buf, len); - esp_set_tc(s, esp_get_tc(s) - len); + if (len) { + s->dma_memory_read(s->dma_opaque, buf, len); + esp_set_tc(s, esp_get_tc(s) - len); + } } else { len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo)); len = MIN(fifo8_num_free(&s->cmdfifo), len); @@ -541,9 +543,11 @@ static void esp_do_dma(ESPState *s) trace_esp_do_dma(cmdlen, len); if (s->dma_memory_read) { len = MIN(len, fifo8_num_free(&s->cmdfifo)); - s->dma_memory_read(s->dma_opaque, buf, len); - fifo8_push_all(&s->cmdfifo, buf, len); - esp_set_tc(s, esp_get_tc(s) - len); + if (len) { + s->dma_memory_read(s->dma_opaque, buf, len); + fifo8_push_all(&s->cmdfifo, buf, len); + esp_set_tc(s, esp_get_tc(s) - len); + } } else { len = esp_fifo_pop_buf(s, buf, fifo8_num_used(&s->fifo)); len = MIN(fifo8_num_free(&s->cmdfifo), len); @@ -572,8 +576,10 @@ static void esp_do_dma(ESPState *s) switch (s->rregs[ESP_CMD]) { case CMD_TI | CMD_DMA: if (s->dma_memory_read) { - s->dma_memory_read(s->dma_opaque, s->async_buf, len); - esp_set_tc(s, esp_get_tc(s) - len); + if (len) { + s->dma_memory_read(s->dma_opaque, s->async_buf, len); + esp_set_tc(s, esp_get_tc(s) - len); + } } else { /* Copy FIFO data to device */ len = MIN(s->async_len, ESP_FIFO_SZ); From 28a579a349015a7ed5a57cb4bdcdc5c60ba6b6fc Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 11 Jul 2025 21:46:34 +0100 Subject: [PATCH 2352/2760] esp.c: only call dma_memory_write function if transfer length is non-zero MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the cases where mixed DMA/non-DMA transfers are used or no data is available, it is possible for the calculated transfer length to be zero. Only call the dma_memory_write function where the transfer length is non-zero to avoid invoking the DMA engine for a zero length transfer which can have side-effects (along with generating additional tracing noise). Signed-off-by: Mark Cave-Ayland Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250711204636.542964-6-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé --- hw/scsi/esp.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index ec9fcbeddf..1c7bad8fc0 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -631,7 +631,9 @@ static void esp_do_dma(ESPState *s) switch (s->rregs[ESP_CMD]) { case CMD_TI | CMD_DMA: if (s->dma_memory_write) { - s->dma_memory_write(s->dma_opaque, s->async_buf, len); + if (len) { + s->dma_memory_write(s->dma_opaque, s->async_buf, len); + } } else { /* Copy device data to FIFO */ len = MIN(len, fifo8_num_free(&s->fifo)); @@ -681,6 +683,7 @@ static void esp_do_dma(ESPState *s) buf[0] = s->status; if (s->dma_memory_write) { + /* Length already non-zero */ s->dma_memory_write(s->dma_opaque, buf, len); } else { esp_fifo_push_buf(s, buf, len); @@ -715,6 +718,7 @@ static void esp_do_dma(ESPState *s) buf[0] = 0; if (s->dma_memory_write) { + /* Length already non-zero */ s->dma_memory_write(s->dma_opaque, buf, len); } else { esp_fifo_push_buf(s, buf, len); From ab1207401edc19d17fad6cb473cd6beae31b1dd1 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 11 Jul 2025 21:46:35 +0100 Subject: [PATCH 2353/2760] esp.c: add asc_mode property to indicate the current ESP mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a new asc_mode property to ESPState which indicates the current mode of the ESP and update the ESP state machine accordingly. Bump the vmstate version and include migration logic to ensure that asc_mode is set to initiator mode such that any commands in progress will always continue. Signed-off-by: Mark Cave-Ayland Tested-by: Philippe Mathieu-Daudé Message-ID: <20250711204636.542964-7-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé --- hw/scsi/esp.c | 21 ++++++++++++++++++++- include/hw/scsi/esp.h | 7 +++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 1c7bad8fc0..4aa58f9e48 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -275,6 +275,7 @@ static int esp_select(ESPState *s) if (!s->current_dev) { /* No such drive */ s->rregs[ESP_RSTAT] = 0; + s->asc_mode = ESP_ASC_MODE_DIS; s->rregs[ESP_RINTR] = INTR_DC; esp_raise_irq(s); return -1; @@ -284,6 +285,7 @@ static int esp_select(ESPState *s) * Note that we deliberately don't raise the IRQ here: this will be done * either in esp_transfer_data() or esp_command_complete() */ + s->asc_mode = ESP_ASC_MODE_INI; return 0; } @@ -308,6 +310,7 @@ static void do_command_phase(ESPState *s) if (!current_lun) { /* No such drive */ s->rregs[ESP_RSTAT] = 0; + s->asc_mode = ESP_ASC_MODE_DIS; s->rregs[ESP_RINTR] = INTR_DC; s->rregs[ESP_RSEQ] = SEQ_0; esp_raise_irq(s); @@ -1102,6 +1105,7 @@ void esp_hard_reset(ESPState *s) fifo8_reset(&s->cmdfifo); s->dma = 0; s->dma_cb = NULL; + s->asc_mode = ESP_ASC_MODE_DIS; s->rregs[ESP_CFG1] = 7; } @@ -1170,6 +1174,7 @@ static void esp_run_cmd(ESPState *s) break; case CMD_MSGACC: trace_esp_mem_writeb_cmd_msgacc(cmd); + s->asc_mode = ESP_ASC_MODE_DIS; s->rregs[ESP_RINTR] |= INTR_DC; s->rregs[ESP_RSEQ] = 0; s->rregs[ESP_RFLAGS] = 0; @@ -1337,6 +1342,14 @@ static bool esp_is_between_version_5_and_6(void *opaque, int version_id) return version_id >= 5 && version_id <= 6; } +static bool esp_is_version_8(void *opaque, int version_id) +{ + ESPState *s = ESP(opaque); + + version_id = MIN(version_id, s->mig_version_id); + return version_id >= 8; +} + int esp_pre_save(void *opaque) { ESPState *s = ESP(object_resolve_path_component( @@ -1368,13 +1381,18 @@ static int esp_post_load(void *opaque, int version_id) } } + if (version_id < 8) { + /* Assume initiator mode to allow all commands to continue */ + s->asc_mode = ESP_ASC_MODE_INI; + } + s->mig_version_id = vmstate_esp.version_id; return 0; } const VMStateDescription vmstate_esp = { .name = "esp", - .version_id = 7, + .version_id = 8, .minimum_version_id = 3, .post_load = esp_post_load, .fields = (const VMStateField[]) { @@ -1406,6 +1424,7 @@ const VMStateDescription vmstate_esp = { esp_is_between_version_5_and_6), VMSTATE_UINT8_TEST(lun, ESPState, esp_is_version_6), VMSTATE_BOOL(drq_state, ESPState), + VMSTATE_UINT8_TEST(asc_mode, ESPState, esp_is_version_8), VMSTATE_END_OF_LIST() }, }; diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h index c9afcb7cac..6327060c7c 100644 --- a/include/hw/scsi/esp.h +++ b/include/hw/scsi/esp.h @@ -14,6 +14,12 @@ typedef void (*ESPDMAMemoryReadWriteFunc)(void *opaque, uint8_t *buf, int len); #define ESP_FIFO_SZ 16 #define ESP_CMDFIFO_SZ 32 +enum ESPASCMode { + ESP_ASC_MODE_DIS = 0, /* Disconnected */ + ESP_ASC_MODE_INI = 1, /* Initiator */ + ESP_ASC_MODE_TGT = 2 /* Target */ +}; + #define TYPE_ESP "esp" OBJECT_DECLARE_SIMPLE_TYPE(ESPState, ESP) @@ -38,6 +44,7 @@ struct ESPState { uint8_t cmdfifo_cdb_offset; uint8_t lun; uint32_t do_cmd; + uint8_t asc_mode; bool data_ready; int dma_enabled; From 6f8ce26bb00db66e6cec632f16df3cd13e4df934 Mon Sep 17 00:00:00 2001 From: Mark Cave-Ayland Date: Fri, 11 Jul 2025 21:46:36 +0100 Subject: [PATCH 2354/2760] esp.c: only allow ESP commands permitted in the current asc_mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If an ESP command is issued in an incorrect mode then an illegal command interrupt should be generated. Add a new esp_cmd_is_valid() function to indicate whether the ESP command is valid for the current mode, and if not then raise the illegal command interrupt. This fixes WinNT MIPS which issues ICCS after a Chip Reset which is not permitted, but will fail with an INACCESSIBLE_BOOT_DEVICE error unless an interrupt is generated. Signed-off-by: Mark Cave-Ayland Fixes: 83428f7a97 ("esp.c: move write_response() non-DMA logic to esp_do_nodma()") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2464 Tested-by: Philippe Mathieu-Daudé Message-ID: <20250711204636.542964-8-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé --- hw/scsi/esp.c | 37 +++++++++++++++++++++++++++++++++++++ hw/scsi/trace-events | 1 + include/hw/scsi/esp.h | 8 ++++++++ 3 files changed, 46 insertions(+) diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c index 4aa58f9e48..1d264c40e5 100644 --- a/hw/scsi/esp.c +++ b/hw/scsi/esp.c @@ -1129,6 +1129,38 @@ static void parent_esp_reset(ESPState *s, int irq, int level) } } +static bool esp_cmd_is_valid(ESPState *s, uint8_t cmd) +{ + uint8_t cmd_group = (cmd & CMD_GRP_MASK) >> 4; + + /* Always allow misc commands */ + if (cmd_group == CMD_GRP_MISC) { + return true; + } + + switch (s->asc_mode) { + case ESP_ASC_MODE_DIS: + /* Disconnected mode: only allow disconnected commands */ + if (cmd_group == CMD_GRP_DISC) { + return true; + } + break; + + case ESP_ASC_MODE_INI: + /* Initiator mode: allow initiator commands */ + if (cmd_group == CMD_GRP_INIT) { + return true; + } + break; + + default: + g_assert_not_reached(); + } + + trace_esp_invalid_cmd(cmd, s->asc_mode); + return false; +} + static void esp_run_cmd(ESPState *s) { uint8_t cmd = s->rregs[ESP_CMD]; @@ -1285,6 +1317,11 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val) break; case ESP_CMD: s->rregs[saddr] = val; + if (!esp_cmd_is_valid(s, s->rregs[saddr])) { + s->rregs[ESP_RSTAT] |= INTR_IL; + esp_raise_irq(s); + break; + } esp_run_cmd(s); break; case ESP_WBUSID ... ESP_WSYNO: diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events index f0f2a98c2e..6c2788e202 100644 --- a/hw/scsi/trace-events +++ b/hw/scsi/trace-events @@ -198,6 +198,7 @@ esp_mem_writeb_cmd_ensel(uint32_t val) "Enable selection (0x%2.2x)" esp_mem_writeb_cmd_dissel(uint32_t val) "Disable selection (0x%2.2x)" esp_mem_writeb_cmd_ti(uint32_t val) "Transfer Information (0x%2.2x)" esp_set_phase(const char *phase) "setting bus phase to %s" +esp_invalid_cmd(uint8_t cmd, uint8_t asc_mode) "command 0x%x asc_mode 0x%x" # esp-pci.c esp_pci_error_invalid_dma_direction(void) "invalid DMA transfer direction" diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h index 6327060c7c..3526bad746 100644 --- a/include/hw/scsi/esp.h +++ b/include/hw/scsi/esp.h @@ -111,6 +111,13 @@ struct SysBusESPState { #define CMD_DMA 0x80 #define CMD_CMD 0x7f +#define CMD_GRP_MASK 0x70 + +#define CMD_GRP_MISC 0x00 +#define CMD_GRP_INIT 0x01 +#define CMD_GRP_TRGT 0x02 +#define CMD_GRP_DISC 0x04 + #define CMD_NOP 0x00 #define CMD_FLUSH 0x01 #define CMD_RESET 0x02 @@ -145,6 +152,7 @@ struct SysBusESPState { #define INTR_FC 0x08 #define INTR_BS 0x10 #define INTR_DC 0x20 +#define INTR_IL 0x40 #define INTR_RST 0x80 #define SEQ_0 0x0 From d38c5e0d0c206dc52421b8bb777cefdf47c13b7e Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 11 Jul 2025 11:44:14 +0200 Subject: [PATCH 2355/2760] net/af-xdp: Remove XDP program cleanup logic There are two issues with the XDP program removal in af_xdp_cleanup(): 1) Starting from libxdp 1.3.0 [0] the XDP program gets automatically detached when we call xsk_socket__delete() for the last successfully configured queue. libxdp internally keeps track of that. For QEMU we require libxdp >= 1.4.0. Given QEMU is not loading the program, lets also not attempt to remove it and delegate this instead. 2) The removal logic is incorrect anyway because we are setting n_queues into the last queue that never has xdp_flags on failure, so the logic is always skipped since the non-zero test for s->xdp_flags in af_xdp_cleanup() fails. Fixes: cb039ef3d9e3 ("net: add initial support for AF_XDP network backend") Suggested-by: Ilya Maximets Signed-off-by: Daniel Borkmann Reviewed-by: Ilya Maximets Cc: Ilya Maximets Cc: Jason Wang Cc: Anton Protopopov Link: https://github.com/xdp-project/xdp-tools/commit/38c2914988fd5c1ef65f2381fc8af9f3e8404e2b [0] Signed-off-by: Jason Wang --- net/af-xdp.c | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/net/af-xdp.c b/net/af-xdp.c index d022534d76..3d3268e18b 100644 --- a/net/af-xdp.c +++ b/net/af-xdp.c @@ -49,7 +49,6 @@ typedef struct AFXDPState { char *buffer; struct xsk_umem *umem; - uint32_t n_queues; uint32_t xdp_flags; bool inhibit; } AFXDPState; @@ -274,14 +273,6 @@ static void af_xdp_cleanup(NetClientState *nc) s->umem = NULL; qemu_vfree(s->buffer); s->buffer = NULL; - - /* Remove the program if it's the last open queue. */ - if (!s->inhibit && nc->queue_index == s->n_queues - 1 && s->xdp_flags - && bpf_xdp_detach(s->ifindex, s->xdp_flags, NULL) != 0) { - fprintf(stderr, - "af-xdp: unable to remove XDP program from '%s', ifindex: %d\n", - s->ifname, s->ifindex); - } } static int af_xdp_umem_create(AFXDPState *s, int sock_fd, Error **errp) @@ -490,12 +481,9 @@ int net_init_af_xdp(const Netdev *netdev, pstrcpy(s->ifname, sizeof(s->ifname), opts->ifname); s->ifindex = ifindex; - s->n_queues = queues; if (af_xdp_umem_create(s, sock_fds ? sock_fds[i] : -1, errp) || af_xdp_socket_create(s, opts, errp)) { - /* Make sure the XDP program will be removed. */ - s->n_queues = i; error_propagate(errp, err); goto err; } From bdebcb49f459237c094a5b9cb084515cfe05e1bc Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 11 Jul 2025 11:44:15 +0200 Subject: [PATCH 2356/2760] net/af-xdp: Fix up cleanup path upon failure in queue creation While testing, it turned out that upon error in the queue creation loop, we never trigger the af_xdp_cleanup() handler. This is because we pass errp instead of a local err pointer into the various AF_XDP setup functions instead of a scheme like: bool fn(..., Error **errp) { Error *err = NULL; foo(arg, &err); if (err) { handle the error... error_propagate(errp, err); return false; } ... } The same is true for the attachment probing with bpf_xdp_query_id(). With a conversion into the above format, the af_xdp_cleanup() handler is called as expected. Note the error_propagate() handles a NULL err internally. Fixes: cb039ef3d9e3 ("net: add initial support for AF_XDP network backend") Reviewed-by: Ilya Maximets Signed-off-by: Daniel Borkmann Cc: Ilya Maximets Cc: Jason Wang Cc: Anton Protopopov Signed-off-by: Jason Wang --- net/af-xdp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/net/af-xdp.c b/net/af-xdp.c index 3d3268e18b..cbe1cae700 100644 --- a/net/af-xdp.c +++ b/net/af-xdp.c @@ -482,9 +482,8 @@ int net_init_af_xdp(const Netdev *netdev, pstrcpy(s->ifname, sizeof(s->ifname), opts->ifname); s->ifindex = ifindex; - if (af_xdp_umem_create(s, sock_fds ? sock_fds[i] : -1, errp) - || af_xdp_socket_create(s, opts, errp)) { - error_propagate(errp, err); + if (af_xdp_umem_create(s, sock_fds ? sock_fds[i] : -1, &err) || + af_xdp_socket_create(s, opts, &err)) { goto err; } } @@ -492,7 +491,7 @@ int net_init_af_xdp(const Netdev *netdev, if (nc0) { s = DO_UPCAST(AFXDPState, nc, nc0); if (bpf_xdp_query_id(s->ifindex, s->xdp_flags, &prog_id) || !prog_id) { - error_setg_errno(errp, errno, + error_setg_errno(&err, errno, "no XDP program loaded on '%s', ifindex: %d", s->ifname, s->ifindex); goto err; @@ -506,6 +505,7 @@ int net_init_af_xdp(const Netdev *netdev, err: if (nc0) { qemu_del_net_client(nc0); + error_propagate(errp, err); } return -1; From e53d9ec7ccc2dbb9378353fe2a89ebdca5cd7015 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Fri, 11 Jul 2025 11:44:16 +0200 Subject: [PATCH 2357/2760] net/af-xdp: Support pinned map path for AF_XDP sockets Extend 'inhibit=on' setting with the option to specify a pinned XSK map path along with a starting index (default 0) to push the created XSK sockets into. Example usage: # ./build/qemu-system-x86_64 [...] \ -netdev af-xdp,ifname=enp2s0f0np0,id=net0,mode=native,queues=2,start-queue=14,inhibit=on,map-path=/sys/fs/bpf/xsks_map,map-start-index=14 \ -device virtio-net-pci,netdev=net0 [...] This is useful for the case where an existing XDP program with XSK map is present on the AF_XDP supported phys device and the XSK map is not yet populated. For example, the former could have been pre-loaded onto the netdevice by a control plane, which later launches QEMU to populate it with XSK sockets. Normally, the main idea behind 'inhibit=on' is that the QEMU instance doesn't need to have a lot of privileges to use the pre-loaded program and the pre-created sockets, but this mentioned use-case here is different where QEMU still needs privileges to create the sockets. The 'map-start-index' parameter is optional and defaults to 0. It allows flexible placement of the XSK sockets, and is up to the user to specify when the XDP program with XSK map was already preloaded. In the simplest case the queue-to-map-slot mapping is just 1:1 based on ctx->rx_queue_index but the user might as well have a different scheme (or smaller map size, e.g. ctx->rx_queue_index % max_size) to push the inbound traffic to one of the XSK sockets. Note that the bpf_xdp_query_id() is now only tested for 'inhibit=off' since only in the latter case the libxdp takes care of installing the XDP program which was installed based on the s->xdp_flags pointing to either driver or skb mode. For 'inhibit=on' we don't make any assumptions and neither go down the path of probing all possible options in which way the user installed the XDP program. Reviewed-by: Ilya Maximets Signed-off-by: Daniel Borkmann Cc: Ilya Maximets Cc: Jason Wang Cc: Anton Protopopov Signed-off-by: Jason Wang --- net/af-xdp.c | 83 ++++++++++++++++++++++++++++++++++++++++++++++--- qapi/net.json | 29 +++++++++++------ qemu-options.hx | 23 ++++++++++++-- 3 files changed, 118 insertions(+), 17 deletions(-) diff --git a/net/af-xdp.c b/net/af-xdp.c index cbe1cae700..14f302ea21 100644 --- a/net/af-xdp.c +++ b/net/af-xdp.c @@ -51,6 +51,10 @@ typedef struct AFXDPState { uint32_t xdp_flags; bool inhibit; + + char *map_path; + int map_fd; + uint32_t map_start_index; } AFXDPState; #define AF_XDP_BATCH_SIZE 64 @@ -260,6 +264,7 @@ static void af_xdp_send(void *opaque) static void af_xdp_cleanup(NetClientState *nc) { AFXDPState *s = DO_UPCAST(AFXDPState, nc, nc); + int idx; qemu_purge_queued_packets(nc); @@ -273,6 +278,18 @@ static void af_xdp_cleanup(NetClientState *nc) s->umem = NULL; qemu_vfree(s->buffer); s->buffer = NULL; + + if (s->map_fd >= 0) { + idx = nc->queue_index + s->map_start_index; + if (bpf_map_delete_elem(s->map_fd, &idx)) { + fprintf(stderr, "af-xdp: unable to remove AF_XDP socket from map" + " %s\n", s->map_path); + } + close(s->map_fd); + s->map_fd = -1; + } + g_free(s->map_path); + s->map_path = NULL; } static int af_xdp_umem_create(AFXDPState *s, int sock_fd, Error **errp) @@ -336,7 +353,6 @@ static int af_xdp_socket_create(AFXDPState *s, }; int queue_id, error = 0; - s->inhibit = opts->has_inhibit && opts->inhibit; if (s->inhibit) { cfg.libxdp_flags |= XSK_LIBXDP_FLAGS__INHIBIT_PROG_LOAD; } @@ -387,6 +403,35 @@ static int af_xdp_socket_create(AFXDPState *s, return 0; } +static int af_xdp_update_xsk_map(AFXDPState *s, Error **errp) +{ + int xsk_fd, idx, error = 0; + + if (!s->map_path) { + return 0; + } + + s->map_fd = bpf_obj_get(s->map_path); + if (s->map_fd < 0) { + error = errno; + } else { + xsk_fd = xsk_socket__fd(s->xsk); + idx = s->nc.queue_index + s->map_start_index; + if (bpf_map_update_elem(s->map_fd, &idx, &xsk_fd, 0)) { + error = errno; + } + } + + if (error) { + error_setg_errno(errp, error, + "failed to insert AF_XDP socket into map %s", + s->map_path); + return -1; + } + + return 0; +} + /* NetClientInfo methods. */ static NetClientInfo net_af_xdp_info = { .type = NET_CLIENT_DRIVER_AF_XDP, @@ -435,12 +480,14 @@ int net_init_af_xdp(const Netdev *netdev, { const NetdevAFXDPOptions *opts = &netdev->u.af_xdp; NetClientState *nc, *nc0 = NULL; + int32_t map_start_index; unsigned int ifindex; uint32_t prog_id = 0; g_autofree int *sock_fds = NULL; int64_t i, queues; Error *err = NULL; AFXDPState *s; + bool inhibit; ifindex = if_nametoindex(opts->ifname); if (!ifindex) { @@ -456,8 +503,28 @@ int net_init_af_xdp(const Netdev *netdev, return -1; } - if ((opts->has_inhibit && opts->inhibit) != !!opts->sock_fds) { - error_setg(errp, "'inhibit=on' requires 'sock-fds' and vice versa"); + inhibit = opts->has_inhibit && opts->inhibit; + if (inhibit && !opts->sock_fds && !opts->map_path) { + error_setg(errp, "'inhibit=on' requires 'sock-fds' or 'map-path'"); + return -1; + } + if (!inhibit && (opts->sock_fds || opts->map_path)) { + error_setg(errp, "'sock-fds' and 'map-path' require 'inhibit=on'"); + return -1; + } + if (opts->sock_fds && opts->map_path) { + error_setg(errp, "'sock-fds' and 'map-path' are mutually exclusive"); + return -1; + } + if (!opts->map_path && opts->has_map_start_index) { + error_setg(errp, "'map-start-index' requires 'map-path'"); + return -1; + } + + map_start_index = opts->has_map_start_index ? opts->map_start_index : 0; + if (map_start_index < 0) { + error_setg(errp, "'map-start-index' cannot be negative (%d)", + map_start_index); return -1; } @@ -481,14 +548,20 @@ int net_init_af_xdp(const Netdev *netdev, pstrcpy(s->ifname, sizeof(s->ifname), opts->ifname); s->ifindex = ifindex; + s->inhibit = inhibit; + + s->map_path = g_strdup(opts->map_path); + s->map_start_index = map_start_index; + s->map_fd = -1; if (af_xdp_umem_create(s, sock_fds ? sock_fds[i] : -1, &err) || - af_xdp_socket_create(s, opts, &err)) { + af_xdp_socket_create(s, opts, &err) || + af_xdp_update_xsk_map(s, &err)) { goto err; } } - if (nc0) { + if (nc0 && !inhibit) { s = DO_UPCAST(AFXDPState, nc, nc0); if (bpf_xdp_query_id(s->ifindex, s->xdp_flags, &prog_id) || !prog_id) { error_setg_errno(&err, errno, diff --git a/qapi/net.json b/qapi/net.json index 0f766041a3..1f40bf46bb 100644 --- a/qapi/net.json +++ b/qapi/net.json @@ -567,25 +567,34 @@ # (default: 0). # # @inhibit: Don't load a default XDP program, use one already loaded -# to the interface (default: false). Requires @sock-fds. +# to the interface (default: false). Requires @sock-fds or @map-path. # # @sock-fds: A colon (:) separated list of file descriptors for # already open but not bound AF_XDP sockets in the queue order. # One fd per queue. These descriptors should already be added -# into XDP socket map for corresponding queues. Requires -# @inhibit. +# into XDP socket map for corresponding queues. @sock-fds and +# @map-path are mutually exclusive. Requires @inhibit. +# +# @map-path: The path to a pinned xsk map to push file descriptors +# for bound AF_XDP sockets into. @map-path and @sock-fds are +# mutually exclusive. Requires @inhibit. (Since 10.1) +# +# @map-start-index: Use @map-path to insert xsk sockets starting from +# this index number (default: 0). Requires @map-path. (Since 10.1) # # Since: 8.2 ## { 'struct': 'NetdevAFXDPOptions', 'data': { - 'ifname': 'str', - '*mode': 'AFXDPMode', - '*force-copy': 'bool', - '*queues': 'int', - '*start-queue': 'int', - '*inhibit': 'bool', - '*sock-fds': 'str' }, + 'ifname': 'str', + '*mode': 'AFXDPMode', + '*force-copy': 'bool', + '*queues': 'int', + '*start-queue': 'int', + '*inhibit': 'bool', + '*sock-fds': 'str', + '*map-path': 'str', + '*map-start-index': 'int32' }, 'if': 'CONFIG_AF_XDP' } ## diff --git a/qemu-options.hx b/qemu-options.hx index a3c066c678..bf19987cb0 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2929,6 +2929,7 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, #ifdef CONFIG_AF_XDP "-netdev af-xdp,id=str,ifname=name[,mode=native|skb][,force-copy=on|off]\n" " [,queues=n][,start-queue=m][,inhibit=on|off][,sock-fds=x:y:...:z]\n" + " [,map-path=/path/to/socket/map][,map-start-index=i]\n" " attach to the existing network interface 'name' with AF_XDP socket\n" " use 'mode=MODE' to specify an XDP program attach mode\n" " use 'force-copy=on|off' to force XDP copy mode even if device supports zero-copy (default: off)\n" @@ -2936,6 +2937,8 @@ DEF("netdev", HAS_ARG, QEMU_OPTION_netdev, " with inhibit=on,\n" " use 'sock-fds' to provide file descriptors for already open AF_XDP sockets\n" " added to a socket map in XDP program. One socket per queue.\n" + " use 'map-path' to provide the socket map location to populate AF_XDP sockets with,\n" + " and use 'map-start-index' to specify the starting index for the map (default: 0) (Since 10.1)\n" " use 'queues=n' to specify how many queues of a multiqueue interface should be used\n" " use 'start-queue=m' to specify the first queue that should be used\n" #endif @@ -3759,7 +3762,7 @@ SRST # launch QEMU instance |qemu_system| linux.img -nic vde,sock=/tmp/myswitch -``-netdev af-xdp,id=str,ifname=name[,mode=native|skb][,force-copy=on|off][,queues=n][,start-queue=m][,inhibit=on|off][,sock-fds=x:y:...:z]`` +``-netdev af-xdp,id=str,ifname=name[,mode=native|skb][,force-copy=on|off][,queues=n][,start-queue=m][,inhibit=on|off][,sock-fds=x:y:...:z][,map-path=/path/to/socket/map][,map-start-index=i]`` Configure AF_XDP backend to connect to a network interface 'name' using AF_XDP socket. A specific program attach mode for a default XDP program can be forced with 'mode', defaults to best-effort, @@ -3799,7 +3802,8 @@ SRST -netdev af-xdp,id=n1,ifname=eth0,queues=1,start-queue=1 XDP program can also be loaded externally. In this case 'inhibit' option - should be set to 'on' and 'sock-fds' provided with file descriptors for + should be set to 'on'. Either 'sock-fds' or 'map-path' can be used with + 'inhibit' enabled. 'sock-fds' can be provided with file descriptors for already open but not bound XDP sockets already added to a socket map for corresponding queues. One socket per queue. @@ -3808,6 +3812,21 @@ SRST |qemu_system| linux.img -device virtio-net-pci,netdev=n1 \\ -netdev af-xdp,id=n1,ifname=eth0,queues=3,inhibit=on,sock-fds=15:16:17 + For the 'inhibit' option set to 'on' used together with 'map-path' it is + expected that the XDP program with the socket map is already loaded on + the networking device and the map pinned into BPF file system. The path + to the pinned map is then passed to QEMU which then creates the file + descriptors and inserts them into the existing socket map. + + .. parsed-literal:: + + |qemu_system| linux.img -device virtio-net-pci,netdev=n1 \\ + -netdev af-xdp,id=n1,ifname=eth0,queues=2,inhibit=on,map-path=/sys/fs/bpf/xsks_map + + Additionally, 'map-start-index' can be used to specify the start offset + for insertion into the socket map. The combination of 'map-path' and + 'sock-fds' together is not supported. + ``-netdev vhost-user,chardev=id[,vhostforce=on|off][,queues=n]`` Establish a vhost-user netdev, backed by a chardev id. The chardev should be a unix domain socket backed one. The vhost-user uses a From bd46161dd1bef1da9402fa10aa6e8f38b4dfe1d3 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Mon, 16 Jun 2025 21:32:27 -0700 Subject: [PATCH 2358/2760] ui/spice: Add an option for users to provide a preferred video codec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Giving users an option to choose a particular codec will enable them to make an appropriate decision based on their hardware and use-case. Note that, the Spice server would use this codec with Gstreamer encoder and only when gl=on is specified. If no codec is provided, then the codec gstreamer:h264 would be used as default. And, for the case where gl=off, the default codec to be used is determined by the Spice server. Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Dmitry Osipenko Cc: Frediano Ziglio Cc: Dongwon Kim Cc: Michael Scherle Cc: Daniel P. Berrangé [ Marc-Andre - fix unused variables warnings ] Reviewed-by: Marc-André Lureau Signed-off-by: Vivek Kasireddy Message-Id: <20250617043546.1022779-4-vivek.kasireddy@intel.com> --- qemu-options.hx | 8 ++++++++ ui/spice-core.c | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/qemu-options.hx b/qemu-options.hx index 1f862b19a6..8f6a228a89 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2281,6 +2281,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice, " [,streaming-video=[off|all|filter]][,disable-copy-paste=on|off]\n" " [,disable-agent-file-xfer=on|off][,agent-mouse=[on|off]]\n" " [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n" + " [,video-codec=\n" " [,gl=[on|off]][,rendernode=]\n" " enable spice\n" " at least one of {port, tls-port} is mandatory\n", @@ -2369,6 +2370,13 @@ SRST ``seamless-migration=[on|off]`` Enable/disable spice seamless migration. Default is off. + ``video-codec=`` + Provide the preferred codec the Spice server should use with the + Gstreamer encoder. This option is only relevant when gl=on is + specified. If no codec is provided, then the codec gstreamer:h264 + would be used as default. And, for the case where gl=off, the + default codec to be used is determined by the Spice server. + ``gl=[on|off]`` Enable/disable OpenGL context. Default is off. diff --git a/ui/spice-core.c b/ui/spice-core.c index 5acbdd3955..51bdcef1af 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -488,6 +488,9 @@ static QemuOptsList qemu_spice_opts = { },{ .name = "streaming-video", .type = QEMU_OPT_STRING, + },{ + .name = "video-codec", + .type = QEMU_OPT_STRING, },{ .name = "agent-mouse", .type = QEMU_OPT_BOOL, @@ -837,7 +840,20 @@ static void qemu_spice_init(void) if (qemu_opt_get_bool(opts, "gl", 0)) { if ((port != 0) || (tls_port != 0)) { #if SPICE_SERVER_VERSION >= 0x000f03 /* release 0.15.3 */ + const char *video_codec = NULL; + g_autofree char *enc_codec = NULL; + spice_remote_client = 1; + + video_codec = qemu_opt_get(opts, "video-codec"); + if (video_codec) { + enc_codec = g_strconcat("gstreamer:", video_codec, NULL); + } + if (spice_server_set_video_codecs(spice_server, + enc_codec ?: "gstreamer:h264")) { + error_report("invalid video codec"); + exit(1); + } #else error_report("SPICE GL support is local-only for now and " "incompatible with -spice port/tls-port"); From 50d135e3779f276eba93c63dff49a940b85e23a5 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Mon, 16 Jun 2025 21:32:28 -0700 Subject: [PATCH 2359/2760] ui/spice: Add an option to submit gl_draw requests at fixed rate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In the specific case where the display layer (virtio-gpu) is using dmabuf, and if remote clients are enabled (-spice gl=on,port=xxxx), it makes sense to limit the maximum (streaming) rate (refresh rate) to a fixed value using the GUI refresh timer. Otherwise, the updates or gl_draw requests would be sent as soon as the Guest submits a new frame which is not optimal as it would lead to increased network traffic and wastage of GPU cycles if the frames get dropped. Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Dmitry Osipenko Cc: Frediano Ziglio Cc: Dongwon Kim Cc: Michael Scherle Reviewed-by: Marc-André Lureau Signed-off-by: Vivek Kasireddy Message-Id: <20250617043546.1022779-5-vivek.kasireddy@intel.com> --- include/ui/spice-display.h | 1 + qemu-options.hx | 5 +++ ui/spice-core.c | 12 ++++++++ ui/spice-display.c | 62 ++++++++++++++++++++++++++++++++------ 4 files changed, 70 insertions(+), 10 deletions(-) diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index 6c55f38c8b..9bdde78266 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -152,6 +152,7 @@ struct SimpleSpiceCursor { extern bool spice_opengl; extern bool spice_remote_client; +extern int spice_max_refresh_rate; int qemu_spice_rect_is_empty(const QXLRect* r); void qemu_spice_rect_union(QXLRect *dest, const QXLRect *r); diff --git a/qemu-options.hx b/qemu-options.hx index 8f6a228a89..ac09abfd71 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -2282,6 +2282,7 @@ DEF("spice", HAS_ARG, QEMU_OPTION_spice, " [,disable-agent-file-xfer=on|off][,agent-mouse=[on|off]]\n" " [,playback-compression=[on|off]][,seamless-migration=[on|off]]\n" " [,video-codec=\n" + " [,max-refresh-rate=rate\n" " [,gl=[on|off]][,rendernode=]\n" " enable spice\n" " at least one of {port, tls-port} is mandatory\n", @@ -2377,6 +2378,10 @@ SRST would be used as default. And, for the case where gl=off, the default codec to be used is determined by the Spice server. + ``max-refresh-rate=rate`` + Provide the maximum refresh rate (or FPS) at which the encoding + requests should be sent to the Spice server. Default would be 30. + ``gl=[on|off]`` Enable/disable OpenGL context. Default is off. diff --git a/ui/spice-core.c b/ui/spice-core.c index 51bdcef1af..5992f9daec 100644 --- a/ui/spice-core.c +++ b/ui/spice-core.c @@ -56,6 +56,8 @@ struct SpiceTimer { QEMUTimer *timer; }; +#define DEFAULT_MAX_REFRESH_RATE 30 + static SpiceTimer *timer_add(SpiceTimerFunc func, void *opaque) { SpiceTimer *timer; @@ -491,6 +493,9 @@ static QemuOptsList qemu_spice_opts = { },{ .name = "video-codec", .type = QEMU_OPT_STRING, + },{ + .name = "max-refresh-rate", + .type = QEMU_OPT_NUMBER, },{ .name = "agent-mouse", .type = QEMU_OPT_BOOL, @@ -804,6 +809,13 @@ static void qemu_spice_init(void) spice_server_set_streaming_video(spice_server, SPICE_STREAM_VIDEO_OFF); } + spice_max_refresh_rate = qemu_opt_get_number(opts, "max-refresh-rate", + DEFAULT_MAX_REFRESH_RATE); + if (spice_max_refresh_rate <= 0) { + error_report("max refresh rate/fps is invalid"); + exit(1); + } + spice_server_set_agent_mouse (spice_server, qemu_opt_get_bool(opts, "agent-mouse", 1)); spice_server_set_playback_compression diff --git a/ui/spice-display.c b/ui/spice-display.c index 0fb72f6d6f..e409b6bdb2 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -32,6 +32,7 @@ bool spice_opengl; bool spice_remote_client; +int spice_max_refresh_rate; int qemu_spice_rect_is_empty(const QXLRect* r) { @@ -844,12 +845,32 @@ static void qemu_spice_gl_block_timer(void *opaque) warn_report("spice: no gl-draw-done within one second"); } +static void spice_gl_draw(SimpleSpiceDisplay *ssd, + uint32_t x, uint32_t y, uint32_t w, uint32_t h) +{ + uint64_t cookie; + + cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); + spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie); +} + static void spice_gl_refresh(DisplayChangeListener *dcl) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); - uint64_t cookie; - if (!ssd->ds || qemu_console_is_gl_blocked(ssd->dcl.con)) { + if (!ssd->ds) { + return; + } + + if (qemu_console_is_gl_blocked(ssd->dcl.con)) { + if (spice_remote_client && ssd->gl_updates && ssd->have_scanout) { + glFlush(); + spice_gl_draw(ssd, 0, 0, + surface_width(ssd->ds), surface_height(ssd->ds)); + ssd->gl_updates = 0; + /* E.g, to achieve 60 FPS, update_interval needs to be ~16.66 ms */ + dcl->update_interval = 1000 / spice_max_refresh_rate; + } return; } @@ -857,11 +878,8 @@ static void spice_gl_refresh(DisplayChangeListener *dcl) if (ssd->gl_updates && ssd->have_surface) { qemu_spice_gl_block(ssd, true); glFlush(); - cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); - spice_qxl_gl_draw_async(&ssd->qxl, 0, 0, - surface_width(ssd->ds), - surface_height(ssd->ds), - cookie); + spice_gl_draw(ssd, 0, 0, + surface_width(ssd->ds), surface_height(ssd->ds)); ssd->gl_updates = 0; } } @@ -954,6 +972,20 @@ static void qemu_spice_gl_scanout_disable(DisplayChangeListener *dcl) SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); trace_qemu_spice_gl_scanout_disable(ssd->qxl.id); + + /* + * We need to check for the case of "lost" updates, where a gl_draw + * was not submitted because the timer did not get a chance to run. + * One case where this happens is when the Guest VM is getting + * rebooted. If the console is blocked in this situation, we need + * to unblock it. Otherwise, newer updates would not take effect. + */ + if (qemu_console_is_gl_blocked(ssd->dcl.con)) { + if (spice_remote_client && ssd->gl_updates && ssd->have_scanout) { + ssd->gl_updates = 0; + qemu_spice_gl_block(ssd, false); + } + } spice_server_gl_scanout(&ssd->qxl, NULL, 0, 0, NULL, NULL, 0, DRM_FORMAT_INVALID, DRM_FORMAT_MOD_INVALID, false); qemu_spice_gl_monitor_config(ssd, 0, 0, 0, 0); @@ -1061,7 +1093,6 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, EGLint fourcc = 0; bool render_cursor = false; bool y_0_top = false; /* FIXME */ - uint64_t cookie; uint32_t width, height, texture; if (!ssd->have_scanout) { @@ -1159,8 +1190,19 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y); qemu_spice_gl_block(ssd, true); glFlush(); - cookie = (uintptr_t)qxl_cookie_new(QXL_COOKIE_TYPE_GL_DRAW_DONE, 0); - spice_qxl_gl_draw_async(&ssd->qxl, x, y, w, h, cookie); + + /* + * In the case of remote clients, the submission of gl_draw request is + * deferred here, so that it can be submitted later (to spice server) + * from spice_gl_refresh() timer callback. This is done to ensure that + * Guest updates are submitted at a steady rate (e.g. 60 FPS) instead + * of submitting them arbitrarily. + */ + if (spice_remote_client) { + ssd->gl_updates++; + } else { + spice_gl_draw(ssd, x, y, w, h); + } } static const DisplayChangeListenerOps display_listener_gl_ops = { From e6f0fe8f7c058af6e95e8f845c25e90453b2aec6 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Mon, 16 Jun 2025 21:32:29 -0700 Subject: [PATCH 2360/2760] ui/console-gl: Add a helper to create a texture with linear memory layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are cases where we do not want the memory layout of a texture to be tiled as the component processing the texture would not know how to de-tile either via software or hardware. Therefore, ensuring that the memory backing the texture has a linear layout is absolutely necessary in these situations. Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Dmitry Osipenko Cc: Frediano Ziglio Cc: Dongwon Kim Reviewed-by: Marc-André Lureau Co-developed-by: Michael Scherle Signed-off-by: Vivek Kasireddy Reviewed-by: Dmitry Osipenko Message-Id: <20250617043546.1022779-6-vivek.kasireddy@intel.com> --- include/ui/console.h | 3 +++ ui/console-gl.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/include/ui/console.h b/include/ui/console.h index 46b3128185..98feaa58bd 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -422,6 +422,9 @@ bool console_gl_check_format(DisplayChangeListener *dcl, pixman_format_code_t format); void surface_gl_create_texture(QemuGLShader *gls, DisplaySurface *surface); +bool surface_gl_create_texture_from_fd(DisplaySurface *surface, + int fd, GLuint *texture, + GLuint *mem_obj); void surface_gl_update_texture(QemuGLShader *gls, DisplaySurface *surface, int x, int y, int w, int h); diff --git a/ui/console-gl.c b/ui/console-gl.c index 103b954017..afb36dba64 100644 --- a/ui/console-gl.c +++ b/ui/console-gl.c @@ -25,6 +25,7 @@ * THE SOFTWARE. */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "ui/console.h" #include "ui/shader.h" @@ -96,6 +97,53 @@ void surface_gl_create_texture(QemuGLShader *gls, glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); } +bool surface_gl_create_texture_from_fd(DisplaySurface *surface, + int fd, GLuint *texture, + GLuint *mem_obj) +{ + unsigned long size = surface_stride(surface) * surface_height(surface); + GLenum err = glGetError(); + *texture = 0; + *mem_obj = 0; + + if (!epoxy_has_gl_extension("GL_EXT_memory_object") || + !epoxy_has_gl_extension("GL_EXT_memory_object_fd")) { + error_report("spice: required OpenGL extensions not supported: " + "GL_EXT_memory_object and GL_EXT_memory_object_fd"); + return false; + } + +#ifdef GL_EXT_memory_object_fd + glCreateMemoryObjectsEXT(1, mem_obj); + glImportMemoryFdEXT(*mem_obj, size, GL_HANDLE_TYPE_OPAQUE_FD_EXT, fd); + + err = glGetError(); + if (err != GL_NO_ERROR) { + error_report("spice: cannot import memory object from fd"); + goto cleanup_mem; + } + + glGenTextures(1, texture); + glBindTexture(GL_TEXTURE_2D, *texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_TILING_EXT, GL_LINEAR_TILING_EXT); + glTexStorageMem2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8, surface_width(surface), + surface_height(surface), *mem_obj, 0); + err = glGetError(); + if (err != GL_NO_ERROR) { + error_report("spice: cannot create texture from memory object"); + goto cleanup_tex_and_mem; + } + return true; + +cleanup_tex_and_mem: + glDeleteTextures(1, texture); +cleanup_mem: + glDeleteMemoryObjectsEXT(1, mem_obj); + +#endif + return false; +} + void surface_gl_update_texture(QemuGLShader *gls, DisplaySurface *surface, int x, int y, int w, int h) From 2103690b1a7d98f88f7c150f48fcd951d3ee8b36 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Mon, 16 Jun 2025 21:32:30 -0700 Subject: [PATCH 2361/2760] ui/spice: Create a new texture with linear layout when gl=on is specified MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since most encoders/decoders (invoked by Spice) may not work properly with tiled memory associated with a texture, we need to create another texture that has linear memory layout and use that instead. Note that, there does not seem to be a direct way to indicate to the GL implementation that a texture's backing memory needs to be linear. Instead, we have to do it in a roundabout way where we need to first create a tiled texture and import that as a memory object to create a new texture that has a linear memory layout. Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Dmitry Osipenko Cc: Frediano Ziglio Cc: Dongwon Kim Reviewed-by: Marc-André Lureau Co-developed-by: Michael Scherle Signed-off-by: Vivek Kasireddy Reviewed-by: Dmitry Osipenko Message-Id: <20250617043546.1022779-7-vivek.kasireddy@intel.com> --- include/ui/surface.h | 1 + ui/console-gl.c | 6 ++++ ui/spice-display.c | 82 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+) diff --git a/include/ui/surface.h b/include/ui/surface.h index f16f7be8be..006b1986bb 100644 --- a/include/ui/surface.h +++ b/include/ui/surface.h @@ -22,6 +22,7 @@ typedef struct DisplaySurface { GLenum glformat; GLenum gltype; GLuint texture; + GLuint mem_obj; #endif qemu_pixman_shareable share_handle; uint32_t share_handle_offset; diff --git a/ui/console-gl.c b/ui/console-gl.c index afb36dba64..403fc36fbd 100644 --- a/ui/console-gl.c +++ b/ui/console-gl.c @@ -184,6 +184,12 @@ void surface_gl_destroy_texture(QemuGLShader *gls, } glDeleteTextures(1, &surface->texture); surface->texture = 0; +#ifdef GL_EXT_memory_object_fd + if (surface->mem_obj) { + glDeleteMemoryObjectsEXT(1, &surface->mem_obj); + surface->mem_obj = 0; + } +#endif } void surface_gl_setup_viewport(QemuGLShader *gls, diff --git a/ui/spice-display.c b/ui/spice-display.c index e409b6bdb2..854a97c198 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -893,6 +893,81 @@ static void spice_gl_update(DisplayChangeListener *dcl, ssd->gl_updates++; } +static bool spice_gl_replace_fd_texture(SimpleSpiceDisplay *ssd, + int *fds, uint64_t *modifier, + int *num_planes) +{ + uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES]; + GLuint texture; + GLuint mem_obj; + int fourcc; + bool ret; + + if (!spice_remote_client) { + return true; + } + + if (*modifier == DRM_FORMAT_MOD_LINEAR) { + return true; + } + + if (*num_planes > 1) { + error_report("spice: cannot replace texture with multiple planes"); + return false; + } + + /* + * We really want to ensure that the memory layout of the texture + * is linear; otherwise, the encoder's output may show corruption. + */ + if (!surface_gl_create_texture_from_fd(ssd->ds, fds[0], &texture, + &mem_obj)) { + error_report("spice: cannot create new texture from fd"); + return false; + } + + /* + * A successful return after glImportMemoryFdEXT() means that + * the ownership of fd has been passed to GL. In other words, + * the fd we got above should not be used anymore. + */ + ret = egl_dmabuf_export_texture(texture, + fds, + (EGLint *)offsets, + (EGLint *)strides, + &fourcc, + num_planes, + modifier); + if (!ret) { + glDeleteTextures(1, &texture); +#ifdef GL_EXT_memory_object_fd + glDeleteMemoryObjectsEXT(1, &mem_obj); +#endif + + /* + * Since we couldn't export our newly create texture (or create, + * an fd associated with it) we need to backtrack and try to + * recreate the fd associated with the original texture. + */ + ret = egl_dmabuf_export_texture(ssd->ds->texture, + fds, + (EGLint *)offsets, + (EGLint *)strides, + &fourcc, + num_planes, + modifier); + if (!ret) { + surface_gl_destroy_texture(ssd->gls, ssd->ds); + warn_report("spice: no texture available to display"); + } + } else { + surface_gl_destroy_texture(ssd->gls, ssd->ds); + ssd->ds->texture = texture; + ssd->ds->mem_obj = mem_obj; + } + return ret; +} + static void spice_server_gl_scanout(QXLInstance *qxl, const int *fd, uint32_t width, uint32_t height, @@ -917,6 +992,7 @@ static void spice_gl_switch(DisplayChangeListener *dcl, struct DisplaySurface *new_surface) { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); + bool ret; if (ssd->ds) { surface_gl_destroy_texture(ssd->gls, ssd->ds); @@ -939,6 +1015,12 @@ static void spice_gl_switch(DisplayChangeListener *dcl, return; } + ret = spice_gl_replace_fd_texture(ssd, fd, &modifier, &num_planes); + if (!ret) { + surface_gl_destroy_texture(ssd->gls, ssd->ds); + return; + } + trace_qemu_spice_gl_surface(ssd->qxl.id, surface_width(ssd->ds), surface_height(ssd->ds), From f851cd65ebe24cc716a70a2fa68c149e5440f2f4 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Mon, 16 Jun 2025 21:32:31 -0700 Subject: [PATCH 2362/2760] ui/spice: Blit the scanout texture if its memory layout is not linear MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In cases where the scanout buffer is provided as a texture (e.g. Virgl) we need to check to see if it has a linear memory layout or not. If it doesn't have a linear layout, then blitting it onto the texture associated with the display surface (which already has a linear layout) seems to ensure that there is no corruption seen regardless of which encoder or decoder is used. Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Dmitry Osipenko Cc: Frediano Ziglio Cc: Dongwon Kim Cc: Michael Scherle Reviewed-by: Marc-André Lureau Signed-off-by: Vivek Kasireddy Message-Id: <20250617043546.1022779-8-vivek.kasireddy@intel.com> --- include/ui/spice-display.h | 3 ++ ui/spice-display.c | 81 +++++++++++++++++++++++++++++++++++--- 2 files changed, 78 insertions(+), 6 deletions(-) diff --git a/include/ui/spice-display.h b/include/ui/spice-display.h index 9bdde78266..690ece7380 100644 --- a/include/ui/spice-display.h +++ b/include/ui/spice-display.h @@ -132,6 +132,9 @@ struct SimpleSpiceDisplay { egl_fb guest_fb; egl_fb blit_fb; egl_fb cursor_fb; + bool backing_y_0_top; + bool blit_scanout_texture; + bool new_scanout_texture; bool have_hot; #endif }; diff --git a/ui/spice-display.c b/ui/spice-display.c index 854a97c198..9ce622cefc 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -1086,7 +1086,7 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, { SimpleSpiceDisplay *ssd = container_of(dcl, SimpleSpiceDisplay, dcl); EGLint offset[DMABUF_MAX_PLANES], stride[DMABUF_MAX_PLANES], fourcc = 0; - int fd[DMABUF_MAX_PLANES], num_planes; + int fd[DMABUF_MAX_PLANES], num_planes, i; uint64_t modifier; assert(tex_id); @@ -1098,11 +1098,26 @@ static void qemu_spice_gl_scanout_texture(DisplayChangeListener *dcl, trace_qemu_spice_gl_scanout_texture(ssd->qxl.id, w, h, fourcc); - /* note: spice server will close the fd */ - spice_server_gl_scanout(&ssd->qxl, fd, backing_width, backing_height, - (uint32_t *)offset, (uint32_t *)stride, num_planes, - fourcc, modifier, y_0_top); - qemu_spice_gl_monitor_config(ssd, x, y, w, h); + if (spice_remote_client && modifier != DRM_FORMAT_MOD_LINEAR) { + egl_fb_destroy(&ssd->guest_fb); + egl_fb_setup_for_tex(&ssd->guest_fb, + backing_width, backing_height, + tex_id, false); + ssd->backing_y_0_top = y_0_top; + ssd->blit_scanout_texture = true; + ssd->new_scanout_texture = true; + + for (i = 0; i < num_planes; i++) { + close(fd[i]); + } + } else { + /* note: spice server will close the fd */ + spice_server_gl_scanout(&ssd->qxl, fd, backing_width, backing_height, + (uint32_t *)offset, (uint32_t *)stride, + num_planes, fourcc, modifier, y_0_top); + qemu_spice_gl_monitor_config(ssd, x, y, w, h); + } + ssd->have_surface = false; ssd->have_scanout = true; } @@ -1168,6 +1183,50 @@ static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl, egl_dmabuf_release_texture(dmabuf); } +static bool spice_gl_blit_scanout_texture(SimpleSpiceDisplay *ssd, + egl_fb *scanout_tex_fb) +{ + uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES]; + int fds[DMABUF_MAX_PLANES], num_planes, fourcc; + uint64_t modifier; + bool ret; + + egl_fb_destroy(scanout_tex_fb); + egl_fb_setup_for_tex(scanout_tex_fb, + surface_width(ssd->ds), surface_height(ssd->ds), + ssd->ds->texture, false); + egl_fb_blit(scanout_tex_fb, &ssd->guest_fb, false); + glFlush(); + + if (!ssd->new_scanout_texture) { + return true; + } + + ret = egl_dmabuf_export_texture(ssd->ds->texture, + fds, + (EGLint *)offsets, + (EGLint *)strides, + &fourcc, + &num_planes, + &modifier); + if (!ret) { + error_report("spice: failed to get fd for texture"); + return false; + } + + spice_server_gl_scanout(&ssd->qxl, fds, + surface_width(ssd->ds), + surface_height(ssd->ds), + (uint32_t *)offsets, (uint32_t *)strides, + num_planes, fourcc, modifier, + ssd->backing_y_0_top); + qemu_spice_gl_monitor_config(ssd, 0, 0, + surface_width(ssd->ds), + surface_height(ssd->ds)); + ssd->new_scanout_texture = false; + return true; +} + static void qemu_spice_gl_update(DisplayChangeListener *dcl, uint32_t x, uint32_t y, uint32_t w, uint32_t h) { @@ -1175,6 +1234,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, EGLint fourcc = 0; bool render_cursor = false; bool y_0_top = false; /* FIXME */ + bool ret; uint32_t width, height, texture; if (!ssd->have_scanout) { @@ -1269,6 +1329,15 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, glFlush(); } + if (spice_remote_client && ssd->blit_scanout_texture) { + egl_fb scanout_tex_fb; + + ret = spice_gl_blit_scanout_texture(ssd, &scanout_tex_fb); + if (!ret) { + return; + } + } + trace_qemu_spice_gl_update(ssd->qxl.id, w, h, x, y); qemu_spice_gl_block(ssd, true); glFlush(); From 454f4b0f593b149c7a6d8192e1ed3de00de9ae24 Mon Sep 17 00:00:00 2001 From: Andrew Keesler Date: Wed, 9 Jul 2025 12:11:26 +0000 Subject: [PATCH 2363/2760] hw/display: Allow injection of virtio-gpu EDID name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thanks to 72d277a7, 1ed2cb32, and others, EDID (Extended Display Identification Data) is propagated by QEMU such that a virtual display presents legitimate metadata (e.g., name, serial number, preferred resolutions, etc.) to its connected guest. This change adds the ability to specify the EDID name for a particular virtio-vga display. Previously, every virtual display would have the same name: "QEMU Monitor". Now, we can inject names of displays in order to test guest behavior that is specific to display names. We provide the ability to inject the display name from the frontend since this is guest visible data. Furthermore, this makes it clear where N potential display outputs would get their name from (which will be added in a future change). Note that we have elected to use a struct here for output data for extensibility - we intend to add per-output fields like resolution in a future change. It should be noted that EDID names longer than 12 bytes will be truncated per spec (I think?). Testing: verified that when I specified 2 outputs for a virtio-gpu with edid_name set, the names matched those that I configured with my vnc display. -display vnc=localhost:0,id=aaa,display=vga,head=0 \ -display vnc=localhost:1,id=bbb,display=vga,head=1 \ -device '{"driver":"virtio-vga", "max_outputs":2, "id":"vga", "outputs":[ { "name":"AAA" }, { "name":"BBB" } ]}' Signed-off-by: Andrew Keesler Reviewed-by: Marc-André Lureau Message-Id: <20250709121126.2946088-2-ankeesler@google.com> --- hw/core/qdev-properties-system.c | 44 +++++++++++++++++++++++++++++ hw/display/virtio-gpu-base.c | 27 ++++++++++++++++++ include/hw/display/edid.h | 2 ++ include/hw/qdev-properties-system.h | 5 ++++ include/hw/virtio/virtio-gpu.h | 3 ++ qapi/virtio.json | 18 ++++++++++-- 6 files changed, 97 insertions(+), 2 deletions(-) diff --git a/hw/core/qdev-properties-system.c b/hw/core/qdev-properties-system.c index 24e145d870..1f810b7ddf 100644 --- a/hw/core/qdev-properties-system.c +++ b/hw/core/qdev-properties-system.c @@ -1299,3 +1299,47 @@ const PropertyInfo qdev_prop_vmapple_virtio_blk_variant = { .set = qdev_propinfo_set_enum, .set_default_value = qdev_propinfo_set_default_value_enum, }; + +/* --- VirtIOGPUOutputList --- */ + +static void get_virtio_gpu_output_list(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + VirtIOGPUOutputList **prop_ptr = + object_field_prop_ptr(obj, opaque); + + visit_type_VirtIOGPUOutputList(v, name, prop_ptr, errp); +} + +static void set_virtio_gpu_output_list(Object *obj, Visitor *v, + const char *name, void *opaque, Error **errp) +{ + VirtIOGPUOutputList **prop_ptr = + object_field_prop_ptr(obj, opaque); + VirtIOGPUOutputList *list; + + if (!visit_type_VirtIOGPUOutputList(v, name, &list, errp)) { + return; + } + + qapi_free_VirtIOGPUOutputList(*prop_ptr); + *prop_ptr = list; +} + +static void release_virtio_gpu_output_list(Object *obj, + const char *name, void *opaque) +{ + VirtIOGPUOutputList **prop_ptr = + object_field_prop_ptr(obj, opaque); + + qapi_free_VirtIOGPUOutputList(*prop_ptr); + *prop_ptr = NULL; +} + +const PropertyInfo qdev_prop_virtio_gpu_output_list = { + .type = "VirtIOGPUOutputList", + .description = "VirtIO GPU output list [{\"name\":\"\"},...]", + .get = get_virtio_gpu_output_list, + .set = set_virtio_gpu_output_list, + .release = release_virtio_gpu_output_list, +}; diff --git a/hw/display/virtio-gpu-base.c b/hw/display/virtio-gpu-base.c index 9eb806b71f..7269477a1c 100644 --- a/hw/display/virtio-gpu-base.c +++ b/hw/display/virtio-gpu-base.c @@ -19,6 +19,7 @@ #include "qemu/error-report.h" #include "hw/display/edid.h" #include "trace.h" +#include "qapi/qapi-types-virtio.h" void virtio_gpu_base_reset(VirtIOGPUBase *g) @@ -56,6 +57,8 @@ void virtio_gpu_base_generate_edid(VirtIOGPUBase *g, int scanout, struct virtio_gpu_resp_edid *edid) { + size_t output_idx; + VirtIOGPUOutputList *node; qemu_edid_info info = { .width_mm = g->req_state[scanout].width_mm, .height_mm = g->req_state[scanout].height_mm, @@ -64,6 +67,14 @@ virtio_gpu_base_generate_edid(VirtIOGPUBase *g, int scanout, .refresh_rate = g->req_state[scanout].refresh_rate, }; + for (output_idx = 0, node = g->conf.outputs; + output_idx <= scanout && node; output_idx++, node = node->next) { + if (output_idx == scanout && node->value && node->value->name) { + info.name = node->value->name; + break; + } + } + edid->size = cpu_to_le32(sizeof(edid->edid)); qemu_edid_generate(edid->edid, sizeof(edid->edid), &info); } @@ -172,6 +183,8 @@ virtio_gpu_base_device_realize(DeviceState *qdev, VirtIOHandleOutput cursor_cb, Error **errp) { + size_t output_idx; + VirtIOGPUOutputList *node; VirtIODevice *vdev = VIRTIO_DEVICE(qdev); VirtIOGPUBase *g = VIRTIO_GPU_BASE(qdev); int i; @@ -181,6 +194,20 @@ virtio_gpu_base_device_realize(DeviceState *qdev, return false; } + for (output_idx = 0, node = g->conf.outputs; + node; output_idx++, node = node->next) { + if (output_idx == g->conf.max_outputs) { + error_setg(errp, "invalid outputs > %d", g->conf.max_outputs); + return false; + } + if (node->value && node->value->name && + strlen(node->value->name) > EDID_NAME_MAX_LENGTH) { + error_setg(errp, "invalid output name '%s' > %d", + node->value->name, EDID_NAME_MAX_LENGTH); + return false; + } + } + if (virtio_gpu_virgl_enabled(g->conf)) { error_setg(&g->migration_blocker, "virgl is not yet migratable"); if (migrate_add_blocker(&g->migration_blocker, errp) < 0) { diff --git a/include/hw/display/edid.h b/include/hw/display/edid.h index 520f8ec202..91c0a428af 100644 --- a/include/hw/display/edid.h +++ b/include/hw/display/edid.h @@ -1,6 +1,8 @@ #ifndef EDID_H #define EDID_H +#define EDID_NAME_MAX_LENGTH 12 + typedef struct qemu_edid_info { const char *vendor; /* http://www.uefi.org/pnp_id_list */ const char *name; diff --git a/include/hw/qdev-properties-system.h b/include/hw/qdev-properties-system.h index b921392c52..9601a11a09 100644 --- a/include/hw/qdev-properties-system.h +++ b/include/hw/qdev-properties-system.h @@ -32,6 +32,7 @@ extern const PropertyInfo qdev_prop_cpus390entitlement; extern const PropertyInfo qdev_prop_iothread_vq_mapping_list; extern const PropertyInfo qdev_prop_endian_mode; extern const PropertyInfo qdev_prop_vmapple_virtio_blk_variant; +extern const PropertyInfo qdev_prop_virtio_gpu_output_list; #define DEFINE_PROP_PCI_DEVFN(_n, _s, _f, _d) \ DEFINE_PROP_SIGNED(_n, _s, _f, _d, qdev_prop_pci_devfn, int32_t) @@ -110,4 +111,8 @@ extern const PropertyInfo qdev_prop_vmapple_virtio_blk_variant; qdev_prop_vmapple_virtio_blk_variant, \ VMAppleVirtioBlkVariant) +#define DEFINE_PROP_VIRTIO_GPU_OUTPUT_LIST(_name, _state, _field) \ + DEFINE_PROP(_name, _state, _field, qdev_prop_virtio_gpu_output_list, \ + VirtIOGPUOutputList *) + #endif diff --git a/include/hw/virtio/virtio-gpu.h b/include/hw/virtio/virtio-gpu.h index a42957c4e2..9f16f89a36 100644 --- a/include/hw/virtio/virtio-gpu.h +++ b/include/hw/virtio/virtio-gpu.h @@ -20,6 +20,7 @@ #include "hw/virtio/virtio.h" #include "qemu/log.h" #include "system/vhost-user-backend.h" +#include "qapi/qapi-types-virtio.h" #include "standard-headers/linux/virtio_gpu.h" #include "standard-headers/linux/virtio_ids.h" @@ -128,6 +129,7 @@ struct virtio_gpu_base_conf { uint32_t xres; uint32_t yres; uint64_t hostmem; + VirtIOGPUOutputList *outputs; }; struct virtio_gpu_ctrl_command { @@ -167,6 +169,7 @@ struct VirtIOGPUBaseClass { #define VIRTIO_GPU_BASE_PROPERTIES(_state, _conf) \ DEFINE_PROP_UINT32("max_outputs", _state, _conf.max_outputs, 1), \ + DEFINE_PROP_VIRTIO_GPU_OUTPUT_LIST("outputs", _state, _conf.outputs), \ DEFINE_PROP_BIT("edid", _state, _conf.flags, \ VIRTIO_GPU_FLAG_EDID_ENABLED, true), \ DEFINE_PROP_UINT32("xres", _state, _conf.xres, 1280), \ diff --git a/qapi/virtio.json b/qapi/virtio.json index 73df718a26..5e658a7033 100644 --- a/qapi/virtio.json +++ b/qapi/virtio.json @@ -963,17 +963,31 @@ { 'struct': 'IOThreadVirtQueueMapping', 'data': { 'iothread': 'str', '*vqs': ['uint16'] } } +## +# @VirtIOGPUOutput: +# +# Describes configuration of a VirtIO GPU output. +# +# @name: the name of the output +# +# Since: 10.1 +## + +{ 'struct': 'VirtIOGPUOutput', + 'data': { 'name': 'str' } } + ## # @DummyVirtioForceArrays: # # Not used by QMP; hack to let us use IOThreadVirtQueueMappingList -# internally +# and VirtIOGPUOutputList internally # # Since: 9.0 ## { 'struct': 'DummyVirtioForceArrays', - 'data': { 'unused-iothread-vq-mapping': ['IOThreadVirtQueueMapping'] } } + 'data': { 'unused-iothread-vq-mapping': ['IOThreadVirtQueueMapping'], + 'unused-virtio-gpu-output': ['VirtIOGPUOutput'] } } ## # @GranuleMode: From c65680a76c190d187be1bfd18207b22227541d77 Mon Sep 17 00:00:00 2001 From: Weifeng Liu Date: Sun, 1 Jun 2025 12:52:32 +0800 Subject: [PATCH 2364/2760] ui/gtk: Add keep-aspect-ratio option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When aspect ratio of host window and that of guest display are not aligned, we can either zoom the guest content to fill the whole host window or add padding to respect aspect ratio of the guest. Add an option keep-aspect-ratio to allow users to select their preferred behavior in this case. Suggested-by: BALATON Zoltan Suggested-by: Kim, Dongwon Signed-off-by: Weifeng Liu Reviewed-by: Marc-André Lureau Tested-by: Marc-André Lureau Message-Id: <20250601045245.36778-2-weifeng.liu.z@gmail.com> --- include/ui/gtk.h | 1 + qapi/ui.json | 12 ++++++++---- ui/gtk.c | 12 ++++++++++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/include/ui/gtk.h b/include/ui/gtk.h index d3944046db..b7cfbf218e 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -140,6 +140,7 @@ struct GtkDisplayState { GdkCursor *null_cursor; Notifier mouse_mode_notifier; gboolean free_scale; + gboolean keep_aspect_ratio; bool external_pause_update; diff --git a/qapi/ui.json b/qapi/ui.json index 514fa159b1..9e496b4835 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1335,13 +1335,17 @@ # @show-menubar: Display the main window menubar. Defaults to "on". # (Since 8.0) # +# @keep-aspect-ratio: Keep width/height aspect ratio of guest content when +# resizing host window. Defaults to "on". (Since 10.1) +# # Since: 2.12 ## { 'struct' : 'DisplayGTK', - 'data' : { '*grab-on-hover' : 'bool', - '*zoom-to-fit' : 'bool', - '*show-tabs' : 'bool', - '*show-menubar' : 'bool' } } + 'data' : { '*grab-on-hover' : 'bool', + '*zoom-to-fit' : 'bool', + '*show-tabs' : 'bool', + '*show-menubar' : 'bool', + '*keep-aspect-ratio' : 'bool' } } ## # @DisplayEGLHeadless: diff --git a/ui/gtk.c b/ui/gtk.c index 8c4a94c8f6..9104509ee1 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -828,8 +828,12 @@ void gd_update_scale(VirtualConsole *vc, int ww, int wh, int fbw, int fbh) sx = (double)ww / fbw; sy = (double)wh / fbh; - - vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); + if (vc->s->keep_aspect_ratio) { + vc->gfx.scale_x = vc->gfx.scale_y = MIN(sx, sy); + } else { + vc->gfx.scale_x = sx; + vc->gfx.scale_y = sy; + } } } /** @@ -2328,6 +2332,10 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, s->free_scale = true; } + s->keep_aspect_ratio = true; + if (s->opts->u.gtk.has_keep_aspect_ratio) + s->keep_aspect_ratio = s->opts->u.gtk.keep_aspect_ratio; + for (i = 0; i < INPUT_EVENT_SLOTS_MAX; i++) { struct touch_slot *slot = &touch_slots[i]; slot->tracking_id = -1; From 0ba45b79452b7f1fb7d45a1a555cbd88ccedb228 Mon Sep 17 00:00:00 2001 From: Weifeng Liu Date: Sun, 1 Jun 2025 12:52:33 +0800 Subject: [PATCH 2365/2760] ui/gtk: Add scale option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow user to set a preferred scale (defaulting to 1) of the virtual display. Along with zoom-to-fix=false, this would be helpful for users running QEMU on hi-dpi host desktop to achieve pixel to pixel display -- e.g., if the scale factor of a user's host desktop is set to 200%, then they can set a 0.5 scale for the virtual display to avoid magnification that might cause blurriness. Signed-off-by: Weifeng Liu Reviewed-by: Marc-André Lureau Tested-by: Marc-André Lureau Message-Id: <20250601045245.36778-3-weifeng.liu.z@gmail.com> --- include/ui/gtk.h | 1 + qapi/ui.json | 5 ++++- ui/gtk.c | 46 +++++++++++++++++++++++++++++----------------- 3 files changed, 34 insertions(+), 18 deletions(-) diff --git a/include/ui/gtk.h b/include/ui/gtk.h index b7cfbf218e..3e6ce3cb48 100644 --- a/include/ui/gtk.h +++ b/include/ui/gtk.h @@ -41,6 +41,7 @@ typedef struct VirtualGfxConsole { DisplaySurface *ds; pixman_image_t *convert; cairo_surface_t *surface; + double preferred_scale; double scale_x; double scale_y; #if defined(CONFIG_OPENGL) diff --git a/qapi/ui.json b/qapi/ui.json index 9e496b4835..a465d671c7 100644 --- a/qapi/ui.json +++ b/qapi/ui.json @@ -1338,6 +1338,8 @@ # @keep-aspect-ratio: Keep width/height aspect ratio of guest content when # resizing host window. Defaults to "on". (Since 10.1) # +# @scale: Set preferred scale of the display. Defaults to 1.0. (Since 10.1) +# # Since: 2.12 ## { 'struct' : 'DisplayGTK', @@ -1345,7 +1347,8 @@ '*zoom-to-fit' : 'bool', '*show-tabs' : 'bool', '*show-menubar' : 'bool', - '*keep-aspect-ratio' : 'bool' } } + '*keep-aspect-ratio' : 'bool', + '*scale' : 'number' } } ## # @DisplayEGLHeadless: diff --git a/ui/gtk.c b/ui/gtk.c index 9104509ee1..e91d093a49 100644 --- a/ui/gtk.c +++ b/ui/gtk.c @@ -67,6 +67,7 @@ #define VC_TERM_X_MIN 80 #define VC_TERM_Y_MIN 25 #define VC_SCALE_MIN 0.25 +#define VC_SCALE_MAX 4 #define VC_SCALE_STEP 0.25 #ifdef GDK_WINDOWING_X11 @@ -272,15 +273,11 @@ static void gd_update_geometry_hints(VirtualConsole *vc) if (!vc->gfx.ds) { return; } - if (s->free_scale) { - geo.min_width = surface_width(vc->gfx.ds) * VC_SCALE_MIN; - geo.min_height = surface_height(vc->gfx.ds) * VC_SCALE_MIN; - mask |= GDK_HINT_MIN_SIZE; - } else { - geo.min_width = surface_width(vc->gfx.ds) * vc->gfx.scale_x; - geo.min_height = surface_height(vc->gfx.ds) * vc->gfx.scale_y; - mask |= GDK_HINT_MIN_SIZE; - } + double scale_x = s->free_scale ? VC_SCALE_MIN : vc->gfx.scale_x; + double scale_y = s->free_scale ? VC_SCALE_MIN : vc->gfx.scale_y; + geo.min_width = surface_width(vc->gfx.ds) * scale_x; + geo.min_height = surface_height(vc->gfx.ds) * scale_y; + mask |= GDK_HINT_MIN_SIZE; geo_widget = vc->gfx.drawing_area; gtk_widget_set_size_request(geo_widget, geo.min_width, geo.min_height); @@ -1579,8 +1576,8 @@ static void gd_menu_full_screen(GtkMenuItem *item, void *opaque) } s->full_screen = FALSE; if (vc->type == GD_VC_GFX) { - vc->gfx.scale_x = 1.0; - vc->gfx.scale_y = 1.0; + vc->gfx.scale_x = vc->gfx.preferred_scale; + vc->gfx.scale_y = vc->gfx.preferred_scale; gd_update_windowsize(vc); } } @@ -1636,8 +1633,8 @@ static void gd_menu_zoom_fixed(GtkMenuItem *item, void *opaque) GtkDisplayState *s = opaque; VirtualConsole *vc = gd_vc_find_current(s); - vc->gfx.scale_x = 1.0; - vc->gfx.scale_y = 1.0; + vc->gfx.scale_x = vc->gfx.preferred_scale; + vc->gfx.scale_y = vc->gfx.preferred_scale; gd_update_windowsize(vc); } @@ -1651,8 +1648,8 @@ static void gd_menu_zoom_fit(GtkMenuItem *item, void *opaque) s->free_scale = TRUE; } else { s->free_scale = FALSE; - vc->gfx.scale_x = 1.0; - vc->gfx.scale_y = 1.0; + vc->gfx.scale_x = vc->gfx.preferred_scale; + vc->gfx.scale_y = vc->gfx.preferred_scale; } gd_update_windowsize(vc); @@ -2243,6 +2240,11 @@ static void gl_area_realize(GtkGLArea *area, VirtualConsole *vc) } #endif +static bool gd_scale_valid(double scale) +{ + return scale >= VC_SCALE_MIN && scale <= VC_SCALE_MAX; +} + static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, QemuConsole *con, int idx, GSList *group, GtkWidget *view_menu) @@ -2252,8 +2254,18 @@ static GSList *gd_vc_gfx_init(GtkDisplayState *s, VirtualConsole *vc, vc->label = qemu_console_get_label(con); vc->s = s; - vc->gfx.scale_x = 1.0; - vc->gfx.scale_y = 1.0; + vc->gfx.preferred_scale = 1.0; + if (s->opts->u.gtk.has_scale) { + if (gd_scale_valid(s->opts->u.gtk.scale)) { + vc->gfx.preferred_scale = s->opts->u.gtk.scale; + } else { + error_report("Invalid scale value %lf given, being ignored", + s->opts->u.gtk.scale); + s->opts->u.gtk.has_scale = false; + } + } + vc->gfx.scale_x = vc->gfx.preferred_scale; + vc->gfx.scale_y = vc->gfx.preferred_scale; #if defined(CONFIG_OPENGL) if (display_opengl) { From df892b3954e5b2782165e6c59e5ffd55c2f7ec5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 7 Jul 2025 14:14:12 +0400 Subject: [PATCH 2366/2760] tpm: "qemu -tpmdev help" should return success MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Like other "-foo help" CLI, the qemu process should return 0 for "-tpmdev help". While touching this, switch to is_help_option() utility function as suggested by Peter Maydell. Signed-off-by: Marc-André Lureau Reviewed-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250707101412.2055581-1-marcandre.lureau@redhat.com> --- system/tpm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/system/tpm.c b/system/tpm.c index 8df0f6e72b..903b29c043 100644 --- a/system/tpm.c +++ b/system/tpm.c @@ -21,6 +21,7 @@ #include "system/tpm.h" #include "qemu/config-file.h" #include "qemu/error-report.h" +#include "qemu/help_option.h" static QLIST_HEAD(, TPMBackend) tpm_backends = QLIST_HEAD_INITIALIZER(tpm_backends); @@ -179,9 +180,9 @@ int tpm_config_parse(QemuOptsList *opts_list, const char *optstr) { QemuOpts *opts; - if (!strcmp(optstr, "help")) { + if (is_help_option(optstr)) { tpm_display_backend_drivers(); - return -1; + exit(EXIT_SUCCESS); } opts = qemu_opts_parse_noisily(opts_list, optstr, true); if (!opts) { From 4bf06bcf072968c702712ed7fcb2a53f5a252216 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 14 Jul 2025 16:42:12 -0400 Subject: [PATCH 2367/2760] rust: bindings: allow any number of params MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are going to be adding more parameters, and this makes rust unhappy: Functions with lots of parameters are considered bad style and reduce readability (“what does the 5th parameter mean?”). Consider grouping some parameters into a new type. Specifically: error: this function has too many arguments (8/7) --> /builds/mstredhat/qemu/build/rust/qemu-api/rust-qemu-api-tests.p/structured/bindings.inc.rs:3840:5 | 3840 | / pub fn new_bitfield_1( 3841 | | secure: std::os::raw::c_uint, 3842 | | space: std::os::raw::c_uint, 3843 | | user: std::os::raw::c_uint, ... | 3848 | | address_type: std::os::raw::c_uint, 3849 | | ) -> __BindgenBitfieldUnit<[u8; 4usize]> { | |____________________________________________^ | = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments = note: `-D clippy::too-many-arguments` implied by `-D warnings` = help: to override `-D warnings` add `#[allow(clippy::too_many_arguments)]` Reviewed-by: Philippe Mathieu-Daudé Acked-by: Paolo Bonzini Message-Id: Signed-off-by: Michael S. Tsirkin --- rust/qemu-api/src/bindings.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs index 057de4b646..c4f1f755ce 100644 --- a/rust/qemu-api/src/bindings.rs +++ b/rust/qemu-api/src/bindings.rs @@ -13,7 +13,8 @@ clippy::missing_const_for_fn, clippy::ptr_offset_with_cast, clippy::useless_transmute, - clippy::missing_safety_doc + clippy::missing_safety_doc, + clippy::too_many_arguments )] //! `bindgen`-generated declarations. From 06895f7948a32e833a8686d8394064ca2a4d66cc Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Sat, 28 Jun 2025 18:03:58 +0000 Subject: [PATCH 2368/2760] pci: Add a memory attribute for pre-translated DMA operations The address_type bit will be set to PCI_AT_TRANSLATED by devices that use cached addresses obtained via ATS. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250628180226.133285-2-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/exec/memattrs.h | 3 +++ include/hw/pci/pci.h | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/include/exec/memattrs.h b/include/exec/memattrs.h index 8db1d30464..52ee955249 100644 --- a/include/exec/memattrs.h +++ b/include/exec/memattrs.h @@ -54,6 +54,9 @@ typedef struct MemTxAttrs { */ unsigned int pid:8; + /* PCI - IOMMU operations, see PCIAddressType */ + unsigned int address_type:1; + /* * Bus masters which don't specify any attributes will get this * (via the MEMTXATTRS_UNSPECIFIED constant), so that we can diff --git a/include/hw/pci/pci.h b/include/hw/pci/pci.h index df3cc7b875..6b7d3ac8a3 100644 --- a/include/hw/pci/pci.h +++ b/include/hw/pci/pci.h @@ -134,6 +134,15 @@ struct PCIHostDeviceAddress { unsigned int function; }; +/* + * Represents the Address Type (AT) field in a PCI request, + * see MemTxAttrs.address_type + */ +typedef enum PCIAddressType { + PCI_AT_UNTRANSLATED = 0, /* Default when no attribute is set */ + PCI_AT_TRANSLATED = 1, +} PCIAddressType; + typedef void PCIConfigWriteFunc(PCIDevice *pci_dev, uint32_t address, uint32_t data, int len); typedef uint32_t PCIConfigReadFunc(PCIDevice *pci_dev, From 4b05c720aaf32c404003a3235300466b7b3342c3 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Sat, 28 Jun 2025 18:03:59 +0000 Subject: [PATCH 2369/2760] memory: Add permissions in IOMMUAccessFlags This will be necessary for devices implementing ATS. We also define a new macro IOMMU_ACCESS_FLAG_FULL in addition to IOMMU_ACCESS_FLAG to support more access flags. IOMMU_ACCESS_FLAG is kept for convenience and backward compatibility. Here are the flags added (defined by the PCIe 5 specification) : - Execute Requested - Privileged Mode Requested - Global - Untranslated Only IOMMU_ACCESS_FLAG sets the additional flags to 0 Signed-off-by: Clement Mathieu--Drif Message-Id: <20250628180226.133285-3-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/system/memory.h | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/include/system/memory.h b/include/system/memory.h index 46248d4a52..1672622d70 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -109,15 +109,34 @@ struct MemoryRegionSection { typedef struct IOMMUTLBEntry IOMMUTLBEntry; -/* See address_space_translate: bit 0 is read, bit 1 is write. */ +/* + * See address_space_translate: + * - bit 0 : read + * - bit 1 : write + * - bit 2 : exec + * - bit 3 : priv + * - bit 4 : global + * - bit 5 : untranslated only + */ typedef enum { IOMMU_NONE = 0, IOMMU_RO = 1, IOMMU_WO = 2, IOMMU_RW = 3, + IOMMU_EXEC = 4, + IOMMU_PRIV = 8, + IOMMU_GLOBAL = 16, + IOMMU_UNTRANSLATED_ONLY = 32, } IOMMUAccessFlags; -#define IOMMU_ACCESS_FLAG(r, w) (((r) ? IOMMU_RO : 0) | ((w) ? IOMMU_WO : 0)) +#define IOMMU_ACCESS_FLAG(r, w) (((r) ? IOMMU_RO : 0) | \ + ((w) ? IOMMU_WO : 0)) +#define IOMMU_ACCESS_FLAG_FULL(r, w, x, p, g, uo) \ + (IOMMU_ACCESS_FLAG(r, w) | \ + ((x) ? IOMMU_EXEC : 0) | \ + ((p) ? IOMMU_PRIV : 0) | \ + ((g) ? IOMMU_GLOBAL : 0) | \ + ((uo) ? IOMMU_UNTRANSLATED_ONLY : 0)) struct IOMMUTLBEntry { AddressSpace *target_as; From 1f81edd7007f00be44966148daaf415158f49009 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Sat, 28 Jun 2025 18:04:00 +0000 Subject: [PATCH 2370/2760] memory: Allow to store the PASID in IOMMUTLBEntry This will be useful for devices that support ATS and need to store entries in an ATC (device IOTLB). Signed-off-by: Clement Mathieu--Drif Message-Id: <20250628180226.133285-4-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/system/memory.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/system/memory.h b/include/system/memory.h index 1672622d70..d6d069fd50 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -144,6 +144,7 @@ struct IOMMUTLBEntry { hwaddr translated_addr; hwaddr addr_mask; /* 0xfff = 4k translation */ IOMMUAccessFlags perm; + uint32_t pasid; }; /* From f8eee3452ffb6257bbb3537b987e1e95c2bd5d95 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Sat, 28 Jun 2025 18:04:02 +0000 Subject: [PATCH 2371/2760] intel_iommu: Fill the PASID field when creating an IOMMUTLBEntry PASID value must be used by devices as a key (or part of a key) when populating their ATC with the IOTLB entries returned by the IOMMU. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250628180226.133285-5-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 69d72ad35c..0fb4350d48 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2503,6 +2503,7 @@ static void vtd_iotlb_page_invalidate_notify(IntelIOMMUState *s, .translated_addr = 0, .addr_mask = size - 1, .perm = IOMMU_NONE, + .pasid = vtd_as->pasid, }, }; memory_region_notify_iommu(&vtd_as->iommu, 0, event); @@ -3090,6 +3091,7 @@ static void do_invalidate_device_tlb(VTDAddressSpace *vtd_dev_as, event.entry.iova = addr; event.entry.perm = IOMMU_NONE; event.entry.translated_addr = 0; + event.entry.pasid = vtd_dev_as->pasid; memory_region_notify_iommu(&vtd_dev_as->iommu, 0, event); } @@ -3672,6 +3674,7 @@ static IOMMUTLBEntry vtd_iommu_translate(IOMMUMemoryRegion *iommu, hwaddr addr, IOMMUTLBEntry iotlb = { /* We'll fill in the rest later. */ .target_as = &address_space_memory, + .pasid = vtd_as->pasid, }; bool success; From 9b3725eec592140ec3d6557ec0f4c86991af7bd9 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Sat, 28 Jun 2025 18:04:03 +0000 Subject: [PATCH 2372/2760] intel_iommu: Declare supported PASID size the PSS field of the extended capabilities stores the supported PASID size minus 1. This commit adds support for 8bits PASIDs (limited by MemTxAttrs::pid). Signed-off-by: Clement Mathieu--Drif Message-Id: <20250628180226.133285-6-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 2 +- hw/i386/intel_iommu_internal.h | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 0fb4350d48..71497f1936 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -4590,7 +4590,7 @@ static void vtd_cap_init(IntelIOMMUState *s) } if (s->pasid) { - s->ecap |= VTD_ECAP_PASID; + s->ecap |= VTD_ECAP_PASID | VTD_ECAP_PSS; } } diff --git a/hw/i386/intel_iommu_internal.h b/hw/i386/intel_iommu_internal.h index e8b211e8b0..360e937989 100644 --- a/hw/i386/intel_iommu_internal.h +++ b/hw/i386/intel_iommu_internal.h @@ -192,6 +192,7 @@ #define VTD_ECAP_SC (1ULL << 7) #define VTD_ECAP_MHMV (15ULL << 20) #define VTD_ECAP_SRS (1ULL << 31) +#define VTD_ECAP_PSS (7ULL << 35) /* limit: MemTxAttrs::pid */ #define VTD_ECAP_PASID (1ULL << 40) #define VTD_ECAP_SMTS (1ULL << 43) #define VTD_ECAP_SLTS (1ULL << 46) From d6f6467b7cad6615bba78a40f1a1ed703a667f44 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Sat, 28 Jun 2025 18:04:05 +0000 Subject: [PATCH 2373/2760] intel_iommu: Implement vtd_get_iotlb_info from PCIIOMMUOps Signed-off-by: Clement Mathieu--Drif Message-Id: <20250628180226.133285-7-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 71497f1936..affa7768e6 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -4733,10 +4733,20 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &vtd_as->as; } +static void vtd_get_iotlb_info(void *opaque, uint8_t *addr_width, + uint32_t *min_page_size) +{ + IntelIOMMUState *s = opaque; + + *addr_width = s->aw_bits; + *min_page_size = VTD_PAGE_SIZE; +} + static PCIIOMMUOps vtd_iommu_ops = { .get_address_space = vtd_host_dma_iommu, .set_iommu_device = vtd_dev_set_iommu_device, .unset_iommu_device = vtd_dev_unset_iommu_device, + .get_iotlb_info = vtd_get_iotlb_info, }; static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) From 838efe99fde22f7ee120cddae3cce732653db869 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Sat, 28 Jun 2025 18:04:07 +0000 Subject: [PATCH 2374/2760] intel_iommu: Implement the PCIIOMMUOps callbacks related to invalidations of device-IOTLB Signed-off-by: Clement Mathieu--Drif Message-Id: <20250628180226.133285-8-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index affa7768e6..234c452849 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -4733,6 +4733,15 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &vtd_as->as; } +static void vtd_init_iotlb_notifier(PCIBus *bus, void *opaque, int devfn, + IOMMUNotifier *n, IOMMUNotify fn, + void *user_opaque) +{ + n->opaque = user_opaque; + iommu_notifier_init(n, fn, IOMMU_NOTIFIER_DEVIOTLB_EVENTS, 0, + HWADDR_MAX, 0); +} + static void vtd_get_iotlb_info(void *opaque, uint8_t *addr_width, uint32_t *min_page_size) { @@ -4742,11 +4751,37 @@ static void vtd_get_iotlb_info(void *opaque, uint8_t *addr_width, *min_page_size = VTD_PAGE_SIZE; } +static void vtd_register_iotlb_notifier(PCIBus *bus, void *opaque, + int devfn, uint32_t pasid, + IOMMUNotifier *n) +{ + IntelIOMMUState *s = opaque; + VTDAddressSpace *vtd_as; + + vtd_as = vtd_find_add_as(s, bus, devfn, pasid); + memory_region_register_iommu_notifier(MEMORY_REGION(&vtd_as->iommu), n, + &error_fatal); +} + +static void vtd_unregister_iotlb_notifier(PCIBus *bus, void *opaque, + int devfn, uint32_t pasid, + IOMMUNotifier *n) +{ + IntelIOMMUState *s = opaque; + VTDAddressSpace *vtd_as; + + vtd_as = vtd_find_add_as(s, bus, devfn, pasid); + memory_region_unregister_iommu_notifier(MEMORY_REGION(&vtd_as->iommu), n); +} + static PCIIOMMUOps vtd_iommu_ops = { .get_address_space = vtd_host_dma_iommu, .set_iommu_device = vtd_dev_set_iommu_device, .unset_iommu_device = vtd_dev_unset_iommu_device, .get_iotlb_info = vtd_get_iotlb_info, + .init_iotlb_notifier = vtd_init_iotlb_notifier, + .register_iotlb_notifier = vtd_register_iotlb_notifier, + .unregister_iotlb_notifier = vtd_unregister_iotlb_notifier, }; static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) From 35b47759c720cc6a3b5ce51c76f981b45df64a7d Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Sat, 28 Jun 2025 18:04:08 +0000 Subject: [PATCH 2375/2760] intel_iommu: Return page walk level even when the translation fails We will use this information in vtd_do_iommu_translate to populate the IOMMUTLBEntry and indicate the correct page mask. This prevents ATS devices from sending many useless translation requests when a megapage or gigapage is not present. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250628180226.133285-9-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 234c452849..bff307b9bc 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -1987,9 +1987,9 @@ static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, uint32_t pasid) { dma_addr_t addr = vtd_get_iova_pgtbl_base(s, ce, pasid); - uint32_t level = vtd_get_iova_level(s, ce, pasid); uint32_t offset; uint64_t flpte, flag_ad = VTD_FL_A; + *flpte_level = vtd_get_iova_level(s, ce, pasid); if (!vtd_iova_fl_check_canonical(s, iova, ce, pasid)) { error_report_once("%s: detected non canonical IOVA (iova=0x%" PRIx64 "," @@ -1998,11 +1998,11 @@ static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, } while (true) { - offset = vtd_iova_level_offset(iova, level); + offset = vtd_iova_level_offset(iova, *flpte_level); flpte = vtd_get_pte(addr, offset); if (flpte == (uint64_t)-1) { - if (level == vtd_get_iova_level(s, ce, pasid)) { + if (*flpte_level == vtd_get_iova_level(s, ce, pasid)) { /* Invalid programming of pasid-entry */ return -VTD_FR_PASID_ENTRY_FSPTPTR_INV; } else { @@ -2028,15 +2028,15 @@ static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, if (is_write && !(flpte & VTD_FL_RW)) { return -VTD_FR_SM_WRITE; } - if (vtd_flpte_nonzero_rsvd(flpte, level)) { + if (vtd_flpte_nonzero_rsvd(flpte, *flpte_level)) { error_report_once("%s: detected flpte reserved non-zero " "iova=0x%" PRIx64 ", level=0x%" PRIx32 "flpte=0x%" PRIx64 ", pasid=0x%" PRIX32 ")", - __func__, iova, level, flpte, pasid); + __func__, iova, *flpte_level, flpte, pasid); return -VTD_FR_FS_PAGING_ENTRY_RSVD; } - if (vtd_is_last_pte(flpte, level) && is_write) { + if (vtd_is_last_pte(flpte, *flpte_level) && is_write) { flag_ad |= VTD_FL_D; } @@ -2044,14 +2044,13 @@ static int vtd_iova_to_flpte(IntelIOMMUState *s, VTDContextEntry *ce, return -VTD_FR_FS_BIT_UPDATE_FAILED; } - if (vtd_is_last_pte(flpte, level)) { + if (vtd_is_last_pte(flpte, *flpte_level)) { *flptep = flpte; - *flpte_level = level; return 0; } addr = vtd_get_pte_addr(flpte, aw_bits); - level--; + (*flpte_level)--; } } From 580b926344a88997264b0a72576bba35f76c59e1 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Sat, 28 Jun 2025 18:04:09 +0000 Subject: [PATCH 2376/2760] intel_iommu: Set address mask when a translation fails and adjust W permission Implements the behavior defined in section 10.2.3.5 of PCIe spec rev 5. This is needed by devices that support ATS. Signed-off-by: Clement Mathieu--Drif Message-Id: <20250628180226.133285-10-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index bff307b9bc..1b1b0b5632 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2091,7 +2091,8 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, uint8_t bus_num = pci_bus_num(bus); VTDContextCacheEntry *cc_entry; uint64_t pte, page_mask; - uint32_t level, pasid = vtd_as->pasid; + uint32_t level = UINT32_MAX; + uint32_t pasid = vtd_as->pasid; uint16_t source_id = PCI_BUILD_BDF(bus_num, devfn); int ret_fr; bool is_fpd_set = false; @@ -2250,14 +2251,19 @@ static bool vtd_do_iommu_translate(VTDAddressSpace *vtd_as, PCIBus *bus, entry->iova = addr & page_mask; entry->translated_addr = vtd_get_pte_addr(pte, s->aw_bits) & page_mask; entry->addr_mask = ~page_mask; - entry->perm = access_flags; + entry->perm = (is_write ? access_flags : (access_flags & (~IOMMU_WO))); return true; error: vtd_iommu_unlock(s); entry->iova = 0; entry->translated_addr = 0; - entry->addr_mask = 0; + /* + * Set the mask for ATS (the range must be present even when the + * translation fails : PCIe rev 5 10.2.3.5) + */ + entry->addr_mask = (level != UINT32_MAX) ? + (~vtd_pt_level_page_mask(level)) : (~VTD_PAGE_MASK_4K); entry->perm = IOMMU_NONE; return false; } From c049bf5bb9dd2cd12d6089cb102aeee268d06423 Mon Sep 17 00:00:00 2001 From: CLEMENT MATHIEU--DRIF Date: Sat, 28 Jun 2025 18:04:10 +0000 Subject: [PATCH 2377/2760] intel_iommu: Add support for ATS Signed-off-by: Clement Mathieu--Drif Message-Id: <20250628180226.133285-11-clement.mathieu--drif@eviden.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index 1b1b0b5632..fe9a5f2872 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -4738,6 +4738,68 @@ static AddressSpace *vtd_host_dma_iommu(PCIBus *bus, void *opaque, int devfn) return &vtd_as->as; } +static IOMMUTLBEntry vtd_iommu_ats_do_translate(IOMMUMemoryRegion *iommu, + hwaddr addr, + IOMMUAccessFlags flags) +{ + IOMMUTLBEntry entry; + VTDAddressSpace *vtd_as = container_of(iommu, VTDAddressSpace, iommu); + + if (vtd_is_interrupt_addr(addr)) { + vtd_report_ir_illegal_access(vtd_as, addr, flags & IOMMU_WO); + entry.target_as = &address_space_memory; + entry.iova = 0; + entry.translated_addr = 0; + entry.addr_mask = ~VTD_PAGE_MASK_4K; + entry.perm = IOMMU_NONE; + entry.pasid = PCI_NO_PASID; + } else { + entry = vtd_iommu_translate(iommu, addr, flags, 0); + } + + return entry; +} + +static ssize_t vtd_ats_request_translation(PCIBus *bus, void *opaque, + int devfn, uint32_t pasid, + bool priv_req, bool exec_req, + hwaddr addr, size_t length, + bool no_write, IOMMUTLBEntry *result, + size_t result_length, + uint32_t *err_count) +{ + IntelIOMMUState *s = opaque; + VTDAddressSpace *vtd_as; + IOMMUAccessFlags flags = IOMMU_ACCESS_FLAG_FULL(true, !no_write, exec_req, + priv_req, false, false); + ssize_t res_index = 0; + hwaddr target_address = addr + length; + IOMMUTLBEntry entry; + + vtd_as = vtd_find_add_as(s, bus, devfn, pasid); + *err_count = 0; + + while ((addr < target_address) && (res_index < result_length)) { + entry = vtd_iommu_ats_do_translate(&vtd_as->iommu, addr, flags); + entry.perm &= ~IOMMU_GLOBAL; /* Spec 4.1.2: Global Mapping never set */ + + if ((entry.perm & flags) != flags) { + *err_count += 1; /* Less than expected */ + } + + result[res_index] = entry; + res_index += 1; + addr = (addr & (~entry.addr_mask)) + (entry.addr_mask + 1); + } + + /* Buffer too small */ + if (addr < target_address) { + return -ENOMEM; + } + + return res_index; +} + static void vtd_init_iotlb_notifier(PCIBus *bus, void *opaque, int devfn, IOMMUNotifier *n, IOMMUNotify fn, void *user_opaque) @@ -4787,6 +4849,7 @@ static PCIIOMMUOps vtd_iommu_ops = { .init_iotlb_notifier = vtd_init_iotlb_notifier, .register_iotlb_notifier = vtd_register_iotlb_notifier, .unregister_iotlb_notifier = vtd_unregister_iotlb_notifier, + .ats_request_translation = vtd_ats_request_translation, }; static bool vtd_decide_config(IntelIOMMUState *s, Error **errp) From fafcff5f306ee91b20b7838ad1817ff6738642ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 23:53:12 +0200 Subject: [PATCH 2378/2760] target/qmp: Use target_cpu_type() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20250708215320.70426-2-philmd@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- target/arm/arm-qmp-cmds.c | 3 ++- target/loongarch/loongarch-qmp-cmds.c | 3 ++- target/mips/system/mips-qmp-cmds.c | 3 ++- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/target/arm/arm-qmp-cmds.c b/target/arm/arm-qmp-cmds.c index cefd235263..d292c974c4 100644 --- a/target/arm/arm-qmp-cmds.c +++ b/target/arm/arm-qmp-cmds.c @@ -21,6 +21,7 @@ */ #include "qemu/osdep.h" +#include "qemu/target-info.h" #include "hw/boards.h" #include "kvm_arm.h" #include "qapi/error.h" @@ -241,7 +242,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) CpuDefinitionInfoList *cpu_list = NULL; GSList *list; - list = object_class_get_list(TYPE_ARM_CPU, false); + list = object_class_get_list(target_cpu_type(), false); g_slist_foreach(list, arm_cpu_add_definition, &cpu_list); g_slist_free(list); diff --git a/target/loongarch/loongarch-qmp-cmds.c b/target/loongarch/loongarch-qmp-cmds.c index f5f1cd0009..1d8cd32f5f 100644 --- a/target/loongarch/loongarch-qmp-cmds.c +++ b/target/loongarch/loongarch-qmp-cmds.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qemu/target-info.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" #include "cpu.h" @@ -32,7 +33,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) CpuDefinitionInfoList *cpu_list = NULL; GSList *list; - list = object_class_get_list(TYPE_LOONGARCH_CPU, false); + list = object_class_get_list(target_cpu_type(), false); g_slist_foreach(list, loongarch_cpu_add_definition, &cpu_list); g_slist_free(list); diff --git a/target/mips/system/mips-qmp-cmds.c b/target/mips/system/mips-qmp-cmds.c index d98d6623f2..b6a2874f2d 100644 --- a/target/mips/system/mips-qmp-cmds.c +++ b/target/mips/system/mips-qmp-cmds.c @@ -7,6 +7,7 @@ */ #include "qemu/osdep.h" +#include "qemu/target-info.h" #include "qapi/error.h" #include "qapi/qapi-commands-machine.h" #include "cpu.h" @@ -40,7 +41,7 @@ CpuDefinitionInfoList *qmp_query_cpu_definitions(Error **errp) CpuDefinitionInfoList *cpu_list = NULL; GSList *list; - list = object_class_get_list(TYPE_MIPS_CPU, false); + list = object_class_get_list(target_cpu_type(), false); g_slist_foreach(list, mips_cpu_add_definition, &cpu_list); g_slist_free(list); From 0af00042a9290626e3c9f05cdc35b6e3d62ecd49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 23:53:13 +0200 Subject: [PATCH 2379/2760] qemu/target-info: Factor target_arch() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To keep "qemu/target-info.h" self-contained to native types, declare target_arch() -- which returns a QAPI type -- in "qemu/target-info-qapi.h". No logical change. Keeping native types in "qemu/target-info.h" is necessary to keep building tests such tests/tcg/plugins/mem.c, as per the comment added in commit ecbcc9ead2f ("tests/tcg: add a system test to check memory instrumentation"): /* * plugins should not include anything from QEMU aside from the * API header. However as this is a test plugin to exercise the * internals of QEMU and we want to avoid needless code duplication we * do so here. bswap.h is pretty self-contained although it needs a * few things provided by compiler.h. */ Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20250708215320.70426-3-philmd@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/core/machine-qmp-cmds.c | 8 +++----- include/qemu/target-info-qapi.h | 21 +++++++++++++++++++++ include/qemu/target-info.h | 2 +- target-info.c | 8 ++++++++ 4 files changed, 33 insertions(+), 6 deletions(-) create mode 100644 include/qemu/target-info-qapi.h diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index d82043e1c6..cd98daedd1 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -19,7 +19,7 @@ #include "qapi/qobject-input-visitor.h" #include "qapi/type-helpers.h" #include "qemu/uuid.h" -#include "qemu/target-info.h" +#include "qemu/target-info-qapi.h" #include "qom/qom-qobject.h" #include "system/hostmem.h" #include "system/hw_accel.h" @@ -37,8 +37,7 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) MachineState *ms = MACHINE(qdev_get_machine()); MachineClass *mc = MACHINE_GET_CLASS(ms); CpuInfoFastList *head = NULL, **tail = &head; - SysEmuTarget target = qapi_enum_parse(&SysEmuTarget_lookup, target_name(), - -1, &error_abort); + SysEmuTarget target = target_arch(); CPUState *cpu; CPU_FOREACH(cpu) { @@ -139,8 +138,7 @@ QemuTargetInfo *qmp_query_target(Error **errp) { QemuTargetInfo *info = g_malloc0(sizeof(*info)); - info->arch = qapi_enum_parse(&SysEmuTarget_lookup, target_name(), -1, - &error_abort); + info->arch = target_arch(); return info; } diff --git a/include/qemu/target-info-qapi.h b/include/qemu/target-info-qapi.h new file mode 100644 index 0000000000..a337c867bf --- /dev/null +++ b/include/qemu/target-info-qapi.h @@ -0,0 +1,21 @@ +/* + * QEMU target info API (returning QAPI types) + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef QEMU_TARGET_INFO_EXTRA_H +#define QEMU_TARGET_INFO_EXTRA_H + +#include "qapi/qapi-types-machine.h" + +/** + * target_arch: + * + * Returns: QAPI SysEmuTarget enum (e.g. SYS_EMU_TARGET_X86_64). + */ +SysEmuTarget target_arch(void); + +#endif diff --git a/include/qemu/target-info.h b/include/qemu/target-info.h index 850a2958b9..dde0e7d968 100644 --- a/include/qemu/target-info.h +++ b/include/qemu/target-info.h @@ -1,5 +1,5 @@ /* - * QEMU target info API + * QEMU target info API (returning native types) * * Copyright (c) Linaro * diff --git a/target-info.c b/target-info.c index 16fdca7aaa..9ebabec988 100644 --- a/target-info.c +++ b/target-info.c @@ -8,7 +8,9 @@ #include "qemu/osdep.h" #include "qemu/target-info.h" +#include "qemu/target-info-qapi.h" #include "qemu/target-info-impl.h" +#include "qapi/error.h" const char *target_name(void) { @@ -20,6 +22,12 @@ unsigned target_long_bits(void) return target_info()->long_bits; } +SysEmuTarget target_arch(void) +{ + return qapi_enum_parse(&SysEmuTarget_lookup, target_name(), -1, + &error_abort); +} + const char *target_cpu_type(void) { return target_info()->cpu_type; From 536613be40c7005a956a52487c1fed4530d6613f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 23:53:14 +0200 Subject: [PATCH 2380/2760] qemu/target-info: Add %target_arch field to TargetInfo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-Id: <20250708215320.70426-4-philmd@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/qemu/target-info-impl.h | 4 +++- target-info-stub.c | 1 + target-info.c | 9 +++++++-- 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/include/qemu/target-info-impl.h b/include/qemu/target-info-impl.h index 1b51cbcfe1..a8b34d150a 100644 --- a/include/qemu/target-info-impl.h +++ b/include/qemu/target-info-impl.h @@ -9,11 +9,13 @@ #ifndef QEMU_TARGET_INFO_IMPL_H #define QEMU_TARGET_INFO_IMPL_H -#include "qemu/target-info.h" +#include "qapi/qapi-types-machine.h" typedef struct TargetInfo { /* runtime equivalent of TARGET_NAME definition */ const char *target_name; + /* related to TARGET_ARCH definition */ + SysEmuTarget target_arch; /* runtime equivalent of TARGET_LONG_BITS definition */ unsigned long_bits; /* runtime equivalent of CPU_RESOLVING_TYPE definition */ diff --git a/target-info-stub.c b/target-info-stub.c index fecc0e7128..2e4407ff04 100644 --- a/target-info-stub.c +++ b/target-info-stub.c @@ -14,6 +14,7 @@ static const TargetInfo target_info_stub = { .target_name = TARGET_NAME, + .target_arch = SYS_EMU_TARGET__MAX, .long_bits = TARGET_LONG_BITS, .cpu_type = CPU_RESOLVING_TYPE, .machine_typename = TYPE_MACHINE, diff --git a/target-info.c b/target-info.c index 9ebabec988..8e29553b4e 100644 --- a/target-info.c +++ b/target-info.c @@ -24,8 +24,13 @@ unsigned target_long_bits(void) SysEmuTarget target_arch(void) { - return qapi_enum_parse(&SysEmuTarget_lookup, target_name(), -1, - &error_abort); + SysEmuTarget arch = target_info()->target_arch; + + if (arch == SYS_EMU_TARGET__MAX) { + arch = qapi_enum_parse(&SysEmuTarget_lookup, target_name(), -1, + &error_abort); + } + return arch; } const char *target_cpu_type(void) From a37aec2e7d8f7191d29c700629850da5057e446b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 23:53:15 +0200 Subject: [PATCH 2381/2760] qemu/target-info: Add target_endian_mode() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit target_endian_mode() returns the default endianness (QAPI type) of a target. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-Id: <20250708215320.70426-5-philmd@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/qemu/target-info-impl.h | 2 ++ include/qemu/target-info-qapi.h | 8 ++++++++ target-info-stub.c | 1 + target-info.c | 5 +++++ 4 files changed, 16 insertions(+) diff --git a/include/qemu/target-info-impl.h b/include/qemu/target-info-impl.h index a8b34d150a..17887f64e2 100644 --- a/include/qemu/target-info-impl.h +++ b/include/qemu/target-info-impl.h @@ -22,6 +22,8 @@ typedef struct TargetInfo { const char *cpu_type; /* QOM typename machines for this binary must implement */ const char *machine_typename; + /* related to TARGET_BIG_ENDIAN definition */ + EndianMode endianness; } TargetInfo; /** diff --git a/include/qemu/target-info-qapi.h b/include/qemu/target-info-qapi.h index a337c867bf..d5ce052323 100644 --- a/include/qemu/target-info-qapi.h +++ b/include/qemu/target-info-qapi.h @@ -9,6 +9,7 @@ #ifndef QEMU_TARGET_INFO_EXTRA_H #define QEMU_TARGET_INFO_EXTRA_H +#include "qapi/qapi-types-common.h" #include "qapi/qapi-types-machine.h" /** @@ -18,4 +19,11 @@ */ SysEmuTarget target_arch(void); +/** + * target_endian_mode: + * + * Returns: QAPI EndianMode enum (e.g. ENDIAN_MODE_LITTLE). + */ +EndianMode target_endian_mode(void); + #endif diff --git a/target-info-stub.c b/target-info-stub.c index 2e4407ff04..ca0caa3686 100644 --- a/target-info-stub.c +++ b/target-info-stub.c @@ -18,6 +18,7 @@ static const TargetInfo target_info_stub = { .long_bits = TARGET_LONG_BITS, .cpu_type = CPU_RESOLVING_TYPE, .machine_typename = TYPE_MACHINE, + .endianness = TARGET_BIG_ENDIAN ? ENDIAN_MODE_BIG : ENDIAN_MODE_LITTLE, }; const TargetInfo *target_info(void) diff --git a/target-info.c b/target-info.c index 8e29553b4e..a756c0714c 100644 --- a/target-info.c +++ b/target-info.c @@ -42,3 +42,8 @@ const char *target_machine_typename(void) { return target_info()->machine_typename; } + +EndianMode target_endian_mode(void) +{ + return target_info()->endianness; +} From 749c21cf6d8ff8eff094137c4ca1298c8f714066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 23:53:16 +0200 Subject: [PATCH 2382/2760] qemu: Convert target_words_bigendian() to TargetInfo API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewed-by: Pierrick Bouvier Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250708215320.70426-6-philmd@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- cpu-target.c | 7 ------- hw/core/cpu-system.c | 2 +- hw/display/vga.c | 2 +- hw/virtio/virtio.c | 2 +- include/exec/tswap.h | 13 +------------ include/qemu/target-info.h | 12 ++++++++++++ system/memory.c | 1 + system/qtest.c | 1 + target-info.c | 5 +++++ 9 files changed, 23 insertions(+), 22 deletions(-) diff --git a/cpu-target.c b/cpu-target.c index 1c90a30759..20db5ff310 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -22,7 +22,6 @@ #include "system/accel-ops.h" #include "system/cpus.h" #include "exec/cpu-common.h" -#include "exec/tswap.h" #include "exec/replay-core.h" #include "exec/log.h" #include "hw/core/cpu.h" @@ -85,9 +84,3 @@ void cpu_abort(CPUState *cpu, const char *fmt, ...) #endif abort(); } - -#undef target_big_endian -bool target_big_endian(void) -{ - return TARGET_BIG_ENDIAN; -} diff --git a/hw/core/cpu-system.c b/hw/core/cpu-system.c index 3c84176a0c..a975405d3a 100644 --- a/hw/core/cpu-system.c +++ b/hw/core/cpu-system.c @@ -24,7 +24,7 @@ #include "exec/cputlb.h" #include "system/memory.h" #include "exec/tb-flush.h" -#include "exec/tswap.h" +#include "qemu/target-info.h" #include "hw/qdev-core.h" #include "hw/qdev-properties.h" #include "hw/core/sysemu-cpu-ops.h" diff --git a/hw/display/vga.c b/hw/display/vga.c index 20475ebbd3..90b89cf404 100644 --- a/hw/display/vga.c +++ b/hw/display/vga.c @@ -26,7 +26,7 @@ #include "qemu/units.h" #include "system/reset.h" #include "qapi/error.h" -#include "exec/tswap.h" +#include "qemu/target-info.h" #include "hw/display/vga.h" #include "hw/i386/x86.h" #include "hw/pci/pci.h" diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 82a285a31d..0f4d28033d 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -20,7 +20,7 @@ #include "qemu/log.h" #include "qemu/main-loop.h" #include "qemu/module.h" -#include "exec/tswap.h" +#include "qemu/target-info.h" #include "qom/object_interfaces.h" #include "hw/core/cpu.h" #include "hw/virtio/virtio.h" diff --git a/include/exec/tswap.h b/include/exec/tswap.h index 49511f2611..55ffa63359 100644 --- a/include/exec/tswap.h +++ b/include/exec/tswap.h @@ -9,18 +9,7 @@ #define TSWAP_H #include "qemu/bswap.h" - -/** - * target_big_endian: - * Returns true if the (default) endianness of the target is big endian, - * false otherwise. Common code should normally never need to know about the - * endianness of the target, so please do *not* use this function unless you - * know very well what you are doing! - */ -bool target_big_endian(void); -#ifdef COMPILING_PER_TARGET -#define target_big_endian() TARGET_BIG_ENDIAN -#endif +#include "qemu/target-info.h" /* * If we're in target-specific code, we can hard-code the swapping diff --git a/include/qemu/target-info.h b/include/qemu/target-info.h index dde0e7d968..abcf25db6f 100644 --- a/include/qemu/target-info.h +++ b/include/qemu/target-info.h @@ -38,4 +38,16 @@ const char *target_machine_typename(void); */ const char *target_cpu_type(void); +/** + * target_big_endian: + * + * Returns: %true if the (default) endianness of the target is big endian, + * %false otherwise. + * + * Common code should normally never need to know about the endianness of + * the target, so please do *not* use this function unless you know very + * well what you are doing! + */ +bool target_big_endian(void); + #endif diff --git a/system/memory.c b/system/memory.c index e8d9b15b28..38da62f505 100644 --- a/system/memory.c +++ b/system/memory.c @@ -22,6 +22,7 @@ #include "qemu/error-report.h" #include "qemu/main-loop.h" #include "qemu/qemu-print.h" +#include "qemu/target-info.h" #include "qom/object.h" #include "trace.h" #include "system/ram_addr.h" diff --git a/system/qtest.c b/system/qtest.c index 301b03be2d..fa42c9f921 100644 --- a/system/qtest.c +++ b/system/qtest.c @@ -29,6 +29,7 @@ #include "qemu/error-report.h" #include "qemu/module.h" #include "qemu/cutils.h" +#include "qemu/target-info.h" #include "qom/object_interfaces.h" #define MAX_IRQ 256 diff --git a/target-info.c b/target-info.c index a756c0714c..3110ab32f7 100644 --- a/target-info.c +++ b/target-info.c @@ -47,3 +47,8 @@ EndianMode target_endian_mode(void) { return target_info()->endianness; } + +bool target_big_endian(void) +{ + return target_endian_mode() == ENDIAN_MODE_BIG; +} From 16c9cb7187ef2414e3ad509048d59a8a4e7d1cd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 23:53:17 +0200 Subject: [PATCH 2383/2760] gdbstub/helpers: Replace TARGET_BIG_ENDIAN -> target_big_endian() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Check endianness at runtime to remove the target-specific TARGET_BIG_ENDIAN definition. Use cpu_to_[be,le]XX() from "qemu/bswap.h" instead of tswapXX() from "exec/tswap.h". Suggested-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250708215320.70426-7-philmd@linaro.org> Reviewed-by: Manos Pitsidianakis Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- include/gdbstub/helpers.h | 48 +++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 19 deletions(-) diff --git a/include/gdbstub/helpers.h b/include/gdbstub/helpers.h index 6f7cc48adc..b685afac43 100644 --- a/include/gdbstub/helpers.h +++ b/include/gdbstub/helpers.h @@ -16,7 +16,8 @@ #error "gdbstub helpers should only be included by target specific code" #endif -#include "exec/tswap.h" +#include "qemu/bswap.h" +#include "qemu/target-info.h" #include "cpu-param.h" /* @@ -33,40 +34,49 @@ static inline int gdb_get_reg8(GByteArray *buf, uint8_t val) static inline int gdb_get_reg16(GByteArray *buf, uint16_t val) { - uint16_t to_word = tswap16(val); - g_byte_array_append(buf, (uint8_t *) &to_word, 2); + if (target_big_endian()) { + cpu_to_be16s(&val); + } else { + cpu_to_le16s(&val); + } + g_byte_array_append(buf, (uint8_t *) &val, 2); return 2; } static inline int gdb_get_reg32(GByteArray *buf, uint32_t val) { - uint32_t to_long = tswap32(val); - g_byte_array_append(buf, (uint8_t *) &to_long, 4); + if (target_big_endian()) { + cpu_to_be32s(&val); + } else { + cpu_to_le32s(&val); + } + g_byte_array_append(buf, (uint8_t *) &val, 4); return 4; } static inline int gdb_get_reg64(GByteArray *buf, uint64_t val) { - uint64_t to_quad = tswap64(val); - g_byte_array_append(buf, (uint8_t *) &to_quad, 8); + if (target_big_endian()) { + cpu_to_be64s(&val); + } else { + cpu_to_le64s(&val); + } + g_byte_array_append(buf, (uint8_t *) &val, 8); return 8; } static inline int gdb_get_reg128(GByteArray *buf, uint64_t val_hi, uint64_t val_lo) { - uint64_t to_quad; -#if TARGET_BIG_ENDIAN - to_quad = tswap64(val_hi); - g_byte_array_append(buf, (uint8_t *) &to_quad, 8); - to_quad = tswap64(val_lo); - g_byte_array_append(buf, (uint8_t *) &to_quad, 8); -#else - to_quad = tswap64(val_lo); - g_byte_array_append(buf, (uint8_t *) &to_quad, 8); - to_quad = tswap64(val_hi); - g_byte_array_append(buf, (uint8_t *) &to_quad, 8); -#endif + uint64_t tmp[2]; + if (target_big_endian()) { + tmp[0] = cpu_to_be64(val_hi); + tmp[1] = cpu_to_be64(val_lo); + } else { + tmp[0] = cpu_to_le64(val_lo); + tmp[1] = cpu_to_le64(val_hi); + } + g_byte_array_append(buf, (uint8_t *)&tmp, 16); return 16; } From 0f64fb674360393ae09605d8d53bf81c02c78a3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 23:53:18 +0200 Subject: [PATCH 2384/2760] qemu: Declare all load/store helper in 'qemu/bswap.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Restrict "exec/tswap.h" to the tswap*() methods, move the load/store helpers with the other ones declared in "qemu/bswap.h". Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-Id: <20250708215320.70426-8-philmd@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/bios-linker-loader.c | 2 - hw/arm/allwinner-r40.c | 1 - hw/arm/boot.c | 2 + hw/arm/npcm7xx.c | 2 +- hw/block/hd-geometry.c | 1 - hw/char/riscv_htif.c | 1 - hw/cxl/cxl-events.c | 2 - hw/display/artist.c | 1 + hw/display/ati.c | 1 + hw/net/can/ctucan_core.c | 1 - hw/net/lan9118.c | 1 + hw/net/rtl8139.c | 1 + hw/net/vmxnet3.c | 1 - hw/pci-host/gt64120.c | 1 + hw/pci-host/pnv_phb3.c | 1 + hw/pci-host/pnv_phb4.c | 1 + hw/pci-host/ppce500.c | 1 - hw/pci-host/sh_pci.c | 1 - hw/s390x/s390-pci-inst.c | 1 + hw/sensor/lsm303dlhc_mag.c | 1 - hw/smbios/smbios.c | 1 + hw/vfio/migration-multifd.c | 1 - hw/virtio/virtio-pci.c | 1 + hw/vmapple/virtio-blk.c | 1 - include/exec/tswap.h | 70 -------------------------- include/qemu/bswap.h | 73 ++++++++++++++++++++++++++++ include/system/memory.h | 1 - include/user/abitypes.h | 1 - target/arm/cpu.c | 1 - target/i386/tcg/system/excp_helper.c | 1 - target/i386/xsave_helper.c | 1 - target/ppc/mmu-hash64.h | 2 - target/riscv/vector_helper.c | 1 - tests/tcg/plugins/mem.c | 1 + 34 files changed, 87 insertions(+), 93 deletions(-) diff --git a/hw/acpi/bios-linker-loader.c b/hw/acpi/bios-linker-loader.c index 108061828b..c9ffe449aa 100644 --- a/hw/acpi/bios-linker-loader.c +++ b/hw/acpi/bios-linker-loader.c @@ -22,8 +22,6 @@ #include "hw/acpi/bios-linker-loader.h" #include "hw/nvram/fw_cfg.h" -#include "qemu/bswap.h" - /* * Linker/loader is a paravirtualized interface that passes commands to guest. * The commands can be used to request guest to diff --git a/hw/arm/allwinner-r40.c b/hw/arm/allwinner-r40.c index 0bf700865c..c8eda39957 100644 --- a/hw/arm/allwinner-r40.c +++ b/hw/arm/allwinner-r40.c @@ -20,7 +20,6 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "qemu/error-report.h" -#include "qemu/bswap.h" #include "qemu/module.h" #include "qemu/units.h" #include "hw/boards.h" diff --git a/hw/arm/boot.c b/hw/arm/boot.c index becd827af1..d391cd01bb 100644 --- a/hw/arm/boot.c +++ b/hw/arm/boot.c @@ -15,6 +15,7 @@ #include "hw/arm/boot.h" #include "hw/arm/linux-boot-if.h" #include "cpu.h" +#include "exec/tswap.h" #include "exec/target_page.h" #include "system/kvm.h" #include "system/tcg.h" @@ -29,6 +30,7 @@ #include "qemu/config-file.h" #include "qemu/option.h" #include "qemu/units.h" +#include "qemu/bswap.h" /* Kernel boot protocol is specified in the kernel docs * Documentation/arm/Booting and Documentation/arm64/booting.txt diff --git a/hw/arm/npcm7xx.c b/hw/arm/npcm7xx.c index 2f30c49df5..ecfae328a9 100644 --- a/hw/arm/npcm7xx.c +++ b/hw/arm/npcm7xx.c @@ -24,7 +24,7 @@ #include "hw/qdev-clock.h" #include "hw/qdev-properties.h" #include "qapi/error.h" -#include "qemu/bswap.h" +#include "exec/tswap.h" #include "qemu/units.h" #include "system/system.h" #include "target/arm/cpu-qom.h" diff --git a/hw/block/hd-geometry.c b/hw/block/hd-geometry.c index f3939e73f4..db221901cf 100644 --- a/hw/block/hd-geometry.c +++ b/hw/block/hd-geometry.c @@ -33,7 +33,6 @@ #include "qemu/osdep.h" #include "system/block-backend.h" #include "qapi/qapi-types-block.h" -#include "qemu/bswap.h" #include "hw/block/block.h" #include "trace.h" diff --git a/hw/char/riscv_htif.c b/hw/char/riscv_htif.c index c884be5d75..a78ea9b01c 100644 --- a/hw/char/riscv_htif.c +++ b/hw/char/riscv_htif.c @@ -29,7 +29,6 @@ #include "qemu/timer.h" #include "qemu/error-report.h" #include "system/address-spaces.h" -#include "exec/tswap.h" #include "system/dma.h" #include "system/runstate.h" #include "trace.h" diff --git a/hw/cxl/cxl-events.c b/hw/cxl/cxl-events.c index 12dee2e467..f90470930d 100644 --- a/hw/cxl/cxl-events.c +++ b/hw/cxl/cxl-events.c @@ -8,8 +8,6 @@ */ #include "qemu/osdep.h" - -#include "qemu/bswap.h" #include "qemu/error-report.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" diff --git a/hw/display/artist.c b/hw/display/artist.c index 3fafc8a222..3c884c9243 100644 --- a/hw/display/artist.c +++ b/hw/display/artist.c @@ -12,6 +12,7 @@ #include "qemu/log.h" #include "qemu/module.h" #include "qemu/units.h" +#include "qemu/bswap.h" #include "qapi/error.h" #include "hw/sysbus.h" #include "hw/loader.h" diff --git a/hw/display/ati.c b/hw/display/ati.c index 7de27732cd..f7c0006a87 100644 --- a/hw/display/ati.c +++ b/hw/display/ati.c @@ -22,6 +22,7 @@ #include "vga-access.h" #include "hw/qdev-properties.h" #include "vga_regs.h" +#include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/module.h" #include "qemu/error-report.h" diff --git a/hw/net/can/ctucan_core.c b/hw/net/can/ctucan_core.c index 17131a4e18..6bd99c477b 100644 --- a/hw/net/can/ctucan_core.c +++ b/hw/net/can/ctucan_core.c @@ -28,7 +28,6 @@ #include "qemu/osdep.h" #include "qemu/log.h" -#include "qemu/bswap.h" #include "qemu/bitops.h" #include "hw/irq.h" #include "migration/vmstate.h" diff --git a/hw/net/lan9118.c b/hw/net/lan9118.c index 6dda1e5c94..3017e12971 100644 --- a/hw/net/lan9118.c +++ b/hw/net/lan9118.c @@ -21,6 +21,7 @@ #include "hw/ptimer.h" #include "hw/qdev-properties.h" #include "qapi/error.h" +#include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/module.h" #include /* for crc32 */ diff --git a/hw/net/rtl8139.c b/hw/net/rtl8139.c index 654a087d80..324fb932aa 100644 --- a/hw/net/rtl8139.c +++ b/hw/net/rtl8139.c @@ -57,6 +57,7 @@ #include "system/dma.h" #include "qemu/module.h" #include "qemu/timer.h" +#include "qemu/bswap.h" #include "net/net.h" #include "net/eth.h" #include "system/system.h" diff --git a/hw/net/vmxnet3.c b/hw/net/vmxnet3.c index 7c0ca56b7c..af73aa8ef2 100644 --- a/hw/net/vmxnet3.c +++ b/hw/net/vmxnet3.c @@ -22,7 +22,6 @@ #include "net/tap.h" #include "net/checksum.h" #include "system/system.h" -#include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/module.h" #include "hw/pci/msix.h" diff --git a/hw/pci-host/gt64120.c b/hw/pci-host/gt64120.c index b12a25696c..b1d96f62fe 100644 --- a/hw/pci-host/gt64120.c +++ b/hw/pci-host/gt64120.c @@ -28,6 +28,7 @@ #include "qapi/error.h" #include "qemu/units.h" #include "qemu/log.h" +#include "qemu/bswap.h" #include "hw/qdev-properties.h" #include "hw/registerfields.h" #include "hw/pci/pci_device.h" diff --git a/hw/pci-host/pnv_phb3.c b/hw/pci-host/pnv_phb3.c index a4335f44f2..5d8383fac3 100644 --- a/hw/pci-host/pnv_phb3.c +++ b/hw/pci-host/pnv_phb3.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/bswap.h" #include "qapi/visitor.h" #include "qapi/error.h" #include "hw/pci-host/pnv_phb3_regs.h" diff --git a/hw/pci-host/pnv_phb4.c b/hw/pci-host/pnv_phb4.c index 77ea35299d..18992054e8 100644 --- a/hw/pci-host/pnv_phb4.c +++ b/hw/pci-host/pnv_phb4.c @@ -8,6 +8,7 @@ */ #include "qemu/osdep.h" #include "qemu/log.h" +#include "qemu/bswap.h" #include "qapi/visitor.h" #include "qapi/error.h" #include "target/ppc/cpu.h" diff --git a/hw/pci-host/ppce500.c b/hw/pci-host/ppce500.c index 52269b05bb..975d191ccb 100644 --- a/hw/pci-host/ppce500.c +++ b/hw/pci-host/ppce500.c @@ -20,7 +20,6 @@ #include "migration/vmstate.h" #include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" -#include "qemu/bswap.h" #include "hw/pci-host/ppce500.h" #include "qom/object.h" diff --git a/hw/pci-host/sh_pci.c b/hw/pci-host/sh_pci.c index de8f6a84aa..62fb945075 100644 --- a/hw/pci-host/sh_pci.c +++ b/hw/pci-host/sh_pci.c @@ -28,7 +28,6 @@ #include "hw/irq.h" #include "hw/pci/pci_device.h" #include "hw/pci/pci_host.h" -#include "qemu/bswap.h" #include "qemu/module.h" #include "qom/object.h" diff --git a/hw/s390x/s390-pci-inst.c b/hw/s390x/s390-pci-inst.c index b5dddb22b8..a3bb5aa221 100644 --- a/hw/s390x/s390-pci-inst.c +++ b/hw/s390x/s390-pci-inst.c @@ -16,6 +16,7 @@ #include "exec/target_page.h" #include "system/memory.h" #include "qemu/error-report.h" +#include "qemu/bswap.h" #include "system/hw_accel.h" #include "hw/boards.h" #include "hw/pci/pci_device.h" diff --git a/hw/sensor/lsm303dlhc_mag.c b/hw/sensor/lsm303dlhc_mag.c index f9e501da84..cd5773ae64 100644 --- a/hw/sensor/lsm303dlhc_mag.c +++ b/hw/sensor/lsm303dlhc_mag.c @@ -28,7 +28,6 @@ #include "qapi/visitor.h" #include "qemu/module.h" #include "qemu/log.h" -#include "qemu/bswap.h" enum LSM303DLHCMagReg { LSM303DLHC_MAG_REG_CRA = 0x00, diff --git a/hw/smbios/smbios.c b/hw/smbios/smbios.c index ad4cd6721e..1ac063cfb4 100644 --- a/hw/smbios/smbios.c +++ b/hw/smbios/smbios.c @@ -17,6 +17,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" +#include "qemu/bswap.h" #include "qapi/error.h" #include "qemu/config-file.h" #include "qemu/module.h" diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index 55635486c8..9dc70fdf16 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -13,7 +13,6 @@ #include "hw/vfio/vfio-device.h" #include "migration/misc.h" #include "qapi/error.h" -#include "qemu/bswap.h" #include "qemu/error-report.h" #include "qemu/lockable.h" #include "qemu/main-loop.h" diff --git a/hw/virtio/virtio-pci.c b/hw/virtio/virtio-pci.c index fba2372c93..767216d795 100644 --- a/hw/virtio/virtio-pci.c +++ b/hw/virtio/virtio-pci.c @@ -30,6 +30,7 @@ #include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/module.h" +#include "qemu/bswap.h" #include "hw/pci/msi.h" #include "hw/pci/msix.h" #include "hw/loader.h" diff --git a/hw/vmapple/virtio-blk.c b/hw/vmapple/virtio-blk.c index 532b5649ab..9de9aaae0b 100644 --- a/hw/vmapple/virtio-blk.c +++ b/hw/vmapple/virtio-blk.c @@ -19,7 +19,6 @@ #include "hw/vmapple/vmapple.h" #include "hw/virtio/virtio-blk.h" #include "hw/virtio/virtio-pci.h" -#include "qemu/bswap.h" #include "qemu/log.h" #include "qemu/module.h" #include "qapi/error.h" diff --git a/include/exec/tswap.h b/include/exec/tswap.h index 55ffa63359..72219e2c43 100644 --- a/include/exec/tswap.h +++ b/include/exec/tswap.h @@ -69,74 +69,4 @@ static inline void tswap64s(uint64_t *s) } } -/* Return ld{word}_{le,be}_p following target endianness. */ -#define LOAD_IMPL(word, args...) \ -do { \ - if (target_big_endian()) { \ - return glue(glue(ld, word), _be_p)(args); \ - } else { \ - return glue(glue(ld, word), _le_p)(args); \ - } \ -} while (0) - -static inline int lduw_p(const void *ptr) -{ - LOAD_IMPL(uw, ptr); -} - -static inline int ldsw_p(const void *ptr) -{ - LOAD_IMPL(sw, ptr); -} - -static inline int ldl_p(const void *ptr) -{ - LOAD_IMPL(l, ptr); -} - -static inline uint64_t ldq_p(const void *ptr) -{ - LOAD_IMPL(q, ptr); -} - -static inline uint64_t ldn_p(const void *ptr, int sz) -{ - LOAD_IMPL(n, ptr, sz); -} - -#undef LOAD_IMPL - -/* Call st{word}_{le,be}_p following target endianness. */ -#define STORE_IMPL(word, args...) \ -do { \ - if (target_big_endian()) { \ - glue(glue(st, word), _be_p)(args); \ - } else { \ - glue(glue(st, word), _le_p)(args); \ - } \ -} while (0) - - -static inline void stw_p(void *ptr, uint16_t v) -{ - STORE_IMPL(w, ptr, v); -} - -static inline void stl_p(void *ptr, uint32_t v) -{ - STORE_IMPL(l, ptr, v); -} - -static inline void stq_p(void *ptr, uint64_t v) -{ - STORE_IMPL(q, ptr, v); -} - -static inline void stn_p(void *ptr, int sz, uint64_t v) -{ - STORE_IMPL(n, ptr, sz, v); -} - -#undef STORE_IMPL - #endif /* TSWAP_H */ diff --git a/include/qemu/bswap.h b/include/qemu/bswap.h index 9a11764536..39ba64046a 100644 --- a/include/qemu/bswap.h +++ b/include/qemu/bswap.h @@ -1,6 +1,8 @@ #ifndef BSWAP_H #define BSWAP_H +#include "qemu/target-info.h" + #undef bswap16 #define bswap16(_x) __builtin_bswap16(_x) #undef bswap32 @@ -432,4 +434,75 @@ DO_STN_LDN_P(be) #undef le_bswaps #undef be_bswaps + +/* Return ld{word}_{le,be}_p following target endianness. */ +#define LOAD_IMPL(word, args...) \ +do { \ + if (target_big_endian()) { \ + return glue(glue(ld, word), _be_p)(args); \ + } else { \ + return glue(glue(ld, word), _le_p)(args); \ + } \ +} while (0) + +static inline int lduw_p(const void *ptr) +{ + LOAD_IMPL(uw, ptr); +} + +static inline int ldsw_p(const void *ptr) +{ + LOAD_IMPL(sw, ptr); +} + +static inline int ldl_p(const void *ptr) +{ + LOAD_IMPL(l, ptr); +} + +static inline uint64_t ldq_p(const void *ptr) +{ + LOAD_IMPL(q, ptr); +} + +static inline uint64_t ldn_p(const void *ptr, int sz) +{ + LOAD_IMPL(n, ptr, sz); +} + +#undef LOAD_IMPL + +/* Call st{word}_{le,be}_p following target endianness. */ +#define STORE_IMPL(word, args...) \ +do { \ + if (target_big_endian()) { \ + glue(glue(st, word), _be_p)(args); \ + } else { \ + glue(glue(st, word), _le_p)(args); \ + } \ +} while (0) + + +static inline void stw_p(void *ptr, uint16_t v) +{ + STORE_IMPL(w, ptr, v); +} + +static inline void stl_p(void *ptr, uint32_t v) +{ + STORE_IMPL(l, ptr, v); +} + +static inline void stq_p(void *ptr, uint64_t v) +{ + STORE_IMPL(q, ptr, v); +} + +static inline void stn_p(void *ptr, int sz, uint64_t v) +{ + STORE_IMPL(n, ptr, sz, v); +} + +#undef STORE_IMPL + #endif /* BSWAP_H */ diff --git a/include/system/memory.h b/include/system/memory.h index d6d069fd50..e2cd6ed126 100644 --- a/include/system/memory.h +++ b/include/system/memory.h @@ -19,7 +19,6 @@ #include "exec/memattrs.h" #include "exec/memop.h" #include "exec/ramlist.h" -#include "exec/tswap.h" #include "qemu/bswap.h" #include "qemu/queue.h" #include "qemu/int128.h" diff --git a/include/user/abitypes.h b/include/user/abitypes.h index 7528124b62..be7a876523 100644 --- a/include/user/abitypes.h +++ b/include/user/abitypes.h @@ -6,7 +6,6 @@ #endif #include "exec/cpu-defs.h" -#include "exec/tswap.h" #include "user/tswap-target.h" #ifdef TARGET_ABI32 diff --git a/target/arm/cpu.c b/target/arm/cpu.c index 08c43f674a..e2b2337399 100644 --- a/target/arm/cpu.c +++ b/target/arm/cpu.c @@ -23,7 +23,6 @@ #include "qemu/timer.h" #include "qemu/log.h" #include "exec/page-vary.h" -#include "exec/tswap.h" #include "target/arm/idau.h" #include "qemu/module.h" #include "qapi/error.h" diff --git a/target/i386/tcg/system/excp_helper.c b/target/i386/tcg/system/excp_helper.c index c162621587..50040f6fca 100644 --- a/target/i386/tcg/system/excp_helper.c +++ b/target/i386/tcg/system/excp_helper.c @@ -25,7 +25,6 @@ #include "exec/page-protection.h" #include "exec/target_page.h" #include "exec/tlb-flags.h" -#include "exec/tswap.h" #include "tcg/helper-tcg.h" typedef struct TranslateParams { diff --git a/target/i386/xsave_helper.c b/target/i386/xsave_helper.c index 24ab7be8e9..996e9f3bfe 100644 --- a/target/i386/xsave_helper.c +++ b/target/i386/xsave_helper.c @@ -5,7 +5,6 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "exec/tswap.h" void x86_cpu_xsave_all_areas(X86CPU *cpu, void *buf, uint32_t buflen) { diff --git a/target/ppc/mmu-hash64.h b/target/ppc/mmu-hash64.h index b8fb12a970..ae8d4b37ae 100644 --- a/target/ppc/mmu-hash64.h +++ b/target/ppc/mmu-hash64.h @@ -1,8 +1,6 @@ #ifndef MMU_HASH64_H #define MMU_HASH64_H -#include "exec/tswap.h" - #ifndef CONFIG_USER_ONLY #ifdef TARGET_PPC64 diff --git a/target/riscv/vector_helper.c b/target/riscv/vector_helper.c index b41c29da0b..7c67d67a13 100644 --- a/target/riscv/vector_helper.c +++ b/target/riscv/vector_helper.c @@ -27,7 +27,6 @@ #include "exec/helper-proto.h" #include "exec/tlb-flags.h" #include "exec/target_page.h" -#include "exec/tswap.h" #include "fpu/softfloat.h" #include "tcg/tcg-gvec-desc.h" #include "internals.h" diff --git a/tests/tcg/plugins/mem.c b/tests/tcg/plugins/mem.c index ca4e8883dd..9649bce99c 100644 --- a/tests/tcg/plugins/mem.c +++ b/tests/tcg/plugins/mem.c @@ -20,6 +20,7 @@ * few things provided by compiler.h. */ #include +#include #include #include From 7f2e88837cb0b03a8ace4a01628d9c26a1196452 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 23:53:19 +0200 Subject: [PATCH 2385/2760] hw/virtio: Build various files once MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that various VirtIO files don't use target specific API anymore, we can move them to the system_ss[] source set to build them once. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Pierrick Bouvier Message-Id: <20250708215320.70426-9-philmd@linaro.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/block/meson.build | 6 ++++-- hw/virtio/meson.build | 20 +++++++++++--------- hw/virtio/virtio-config-io.c | 1 - 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/hw/block/meson.build b/hw/block/meson.build index 655704471a..43ed296cf4 100644 --- a/hw/block/meson.build +++ b/hw/block/meson.build @@ -13,7 +13,9 @@ system_ss.add(when: 'CONFIG_SSI_M25P80', if_true: files('m25p80_sfdp.c')) system_ss.add(when: 'CONFIG_SWIM', if_true: files('swim.c')) system_ss.add(when: 'CONFIG_XEN_BUS', if_true: files('xen-block.c')) -specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c', 'virtio-blk-common.c')) -specific_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk.c', 'virtio-blk-common.c')) +specific_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk.c')) +system_ss.add(when: 'CONFIG_VIRTIO_BLK', if_true: files('virtio-blk-common.c')) +specific_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk.c')) +system_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('virtio-blk-common.c')) subdir('dataplane') diff --git a/hw/virtio/meson.build b/hw/virtio/meson.build index 164f6fd995..3ea7b3cec8 100644 --- a/hw/virtio/meson.build +++ b/hw/virtio/meson.build @@ -1,6 +1,7 @@ system_virtio_ss = ss.source_set() system_virtio_ss.add(files('virtio-bus.c')) system_virtio_ss.add(files('iothread-vq-mapping.c')) +system_virtio_ss.add(files('virtio-config-io.c')) system_virtio_ss.add(when: 'CONFIG_VIRTIO_PCI', if_true: files('virtio-pci.c')) system_virtio_ss.add(when: 'CONFIG_VIRTIO_MMIO', if_true: files('virtio-mmio.c')) system_virtio_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto.c')) @@ -10,11 +11,11 @@ system_virtio_ss.add(when: 'CONFIG_VHOST_VDPA_DEV', if_true: files('vdpa-dev.c') specific_virtio_ss = ss.source_set() specific_virtio_ss.add(files('virtio.c')) -specific_virtio_ss.add(files('virtio-config-io.c', 'virtio-qmp.c')) +specific_virtio_ss.add(files('virtio-qmp.c')) if have_vhost system_virtio_ss.add(files('vhost.c')) - specific_virtio_ss.add(files('vhost-backend.c', 'vhost-iova-tree.c')) + system_virtio_ss.add(files('vhost-backend.c', 'vhost-iova-tree.c')) if have_vhost_user # fixme - this really should be generic specific_virtio_ss.add(files('vhost-user.c')) @@ -43,22 +44,22 @@ if have_vhost endif if have_vhost_vdpa system_virtio_ss.add(files('vhost-vdpa.c')) - specific_virtio_ss.add(files('vhost-shadow-virtqueue.c')) + system_virtio_ss.add(files('vhost-shadow-virtqueue.c')) endif else system_virtio_ss.add(files('vhost-stub.c')) endif +system_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c')) +system_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c')) specific_virtio_ss.add(when: 'CONFIG_VIRTIO_BALLOON', if_true: files('virtio-balloon.c')) specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs.c')) specific_virtio_ss.add(when: 'CONFIG_VIRTIO_PMEM', if_true: files('virtio-pmem.c')) specific_virtio_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock.c')) -specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_VSOCK', if_true: files('vhost-user-vsock.c')) -specific_virtio_ss.add(when: 'CONFIG_VIRTIO_RNG', if_true: files('virtio-rng.c')) -specific_virtio_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: [files('virtio-nsm.c', 'cbor-helpers.c'), libcbor]) specific_virtio_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem.c')) -specific_virtio_ss.add(when: 'CONFIG_VHOST_USER_SCMI', if_true: files('vhost-user-scmi.c')) -specific_virtio_ss.add(when: ['CONFIG_VIRTIO_PCI', 'CONFIG_VHOST_USER_SCMI'], if_true: files('vhost-user-scmi-pci.c')) +system_virtio_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: files('virtio-nsm.c')) +system_virtio_ss.add(when: 'CONFIG_VIRTIO_NSM', if_true: [files('cbor-helpers.c'), libcbor]) +system_virtio_ss.add(when: 'CONFIG_VHOST_USER_SCMI', if_true: files('vhost-user-scmi.c')) virtio_pci_ss = ss.source_set() virtio_pci_ss.add(when: 'CONFIG_VHOST_VSOCK', if_true: files('vhost-vsock-pci.c')) @@ -67,6 +68,7 @@ virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_BLK', if_true: files('vhost-user-blk- virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_SCSI', if_true: files('vhost-user-scsi-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VHOST_SCSI', if_true: files('vhost-scsi-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_FS', if_true: files('vhost-user-fs-pci.c')) +virtio_pci_ss.add(when: 'CONFIG_VHOST_USER_SCMI', if_true: files('vhost-user-scmi-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_CRYPTO', if_true: files('virtio-crypto-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_INPUT_HOST', if_true: files('virtio-input-host-pci.c')) @@ -85,7 +87,7 @@ virtio_pci_ss.add(when: 'CONFIG_VIRTIO_MEM', if_true: files('virtio-mem-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VHOST_VDPA_DEV', if_true: files('vdpa-dev-pci.c')) virtio_pci_ss.add(when: 'CONFIG_VIRTIO_MD', if_true: files('virtio-md-pci.c')) -specific_virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', if_true: virtio_pci_ss) +system_virtio_ss.add_all(when: 'CONFIG_VIRTIO_PCI', if_true: virtio_pci_ss) system_ss.add_all(when: 'CONFIG_VIRTIO', if_true: system_virtio_ss) system_ss.add(when: 'CONFIG_VIRTIO', if_false: files('vhost-stub.c')) diff --git a/hw/virtio/virtio-config-io.c b/hw/virtio/virtio-config-io.c index ad78e0b9bc..f58d90b6e3 100644 --- a/hw/virtio/virtio-config-io.c +++ b/hw/virtio/virtio-config-io.c @@ -11,7 +11,6 @@ #include "qemu/osdep.h" #include "hw/virtio/virtio.h" -#include "cpu.h" uint32_t virtio_config_readb(VirtIODevice *vdev, uint32_t addr) { From 25e84c02e7aafbcb7a677569e9a4198e97cc38b8 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:45 +0200 Subject: [PATCH 2386/2760] hw/i386/acpi-build: Make aml_pci_device_dsm() static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No need to export aml_pci_device_dsm() as it is only used in hw/i386/acpi-build.c. Signed-off-by: Eric Auger Reviewed-by: Gustavo Romero Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <20250714080639.2525563-2-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/acpi-build.c | 2 +- include/hw/acpi/pci.h | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 61851cc840..f59026524f 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -338,7 +338,7 @@ build_facs(GArray *table_data) g_array_append_vals(table_data, reserved, 40); /* Reserved */ } -Aml *aml_pci_device_dsm(void) +static Aml *aml_pci_device_dsm(void) { Aml *method; diff --git a/include/hw/acpi/pci.h b/include/hw/acpi/pci.h index 6359d574fd..ab0187a894 100644 --- a/include/hw/acpi/pci.h +++ b/include/hw/acpi/pci.h @@ -36,7 +36,6 @@ typedef struct AcpiMcfgInfo { void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info, const char *oem_id, const char *oem_table_id); -Aml *aml_pci_device_dsm(void); void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus); void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope); From c242101c3c92410b7f5196f9d04a2844867be682 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:46 +0200 Subject: [PATCH 2387/2760] hw/acpi: Rename and move build_x86_acpi_pci_hotplug to pcihp We plan to reuse build_x86_acpi_pci_hotplug() implementation for ARM so let's move the code to generic pcihp. Associated static aml_pci_pdsm() helper is also moved along. build_x86_acpi_pci_hotplug is renamed into build_acpi_pci_hotplug(). No code change intended. Also fix the reference to acpi_pci_hotplug.rst documentation Signed-off-by: Eric Auger Reviewed-by: Gustavo Romero Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <20250714080639.2525563-3-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/pcihp.c | 174 ++++++++++++++++++++++++++++++++++++++- hw/i386/acpi-build.c | 176 +--------------------------------------- hw/i386/acpi-build.h | 4 - include/hw/acpi/pcihp.h | 7 ++ 4 files changed, 182 insertions(+), 179 deletions(-) diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 497281ae20..cbe7e01385 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "hw/acpi/pcihp.h" - +#include "hw/acpi/aml-build.h" #include "hw/pci-host/i440fx.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bridge.h" @@ -513,6 +513,178 @@ void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, OBJ_PROP_FLAG_READ); } +void build_append_pci_dsm_func0_common(Aml *ctx, Aml *retvar) +{ + Aml *UUID, *ifctx1; + uint8_t byte_list[1] = { 0 }; /* nothing supported yet */ + + aml_append(ctx, aml_store(aml_buffer(1, byte_list), retvar)); + /* + * PCI Firmware Specification 3.1 + * 4.6. _DSM Definitions for PCI + */ + UUID = aml_touuid("E5C937D0-3553-4D7A-9117-EA4D19C3434D"); + ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(0), UUID))); + { + /* call is for unsupported UUID, bail out */ + aml_append(ifctx1, aml_return(retvar)); + } + aml_append(ctx, ifctx1); + + ifctx1 = aml_if(aml_lless(aml_arg(1), aml_int(2))); + { + /* call is for unsupported REV, bail out */ + aml_append(ifctx1, aml_return(retvar)); + } + aml_append(ctx, ifctx1); +} + +static Aml *aml_pci_pdsm(void) +{ + Aml *method, *ifctx, *ifctx1; + Aml *ret = aml_local(0); + Aml *caps = aml_local(1); + Aml *acpi_index = aml_local(2); + Aml *zero = aml_int(0); + Aml *one = aml_int(1); + Aml *not_supp = aml_int(0xFFFFFFFF); + Aml *func = aml_arg(2); + Aml *params = aml_arg(4); + Aml *bnum = aml_derefof(aml_index(params, aml_int(0))); + Aml *sunum = aml_derefof(aml_index(params, aml_int(1))); + + method = aml_method("PDSM", 5, AML_SERIALIZED); + + /* get supported functions */ + ifctx = aml_if(aml_equal(func, zero)); + { + build_append_pci_dsm_func0_common(ifctx, ret); + + aml_append(ifctx, aml_store(zero, caps)); + aml_append(ifctx, + aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); + /* + * advertise function 7 if device has acpi-index + * acpi_index values: + * 0: not present (default value) + * FFFFFFFF: not supported (old QEMU without PIDX reg) + * other: device's acpi-index + */ + ifctx1 = aml_if(aml_lnot( + aml_or(aml_equal(acpi_index, zero), + aml_equal(acpi_index, not_supp), NULL) + )); + { + /* have supported functions */ + aml_append(ifctx1, aml_or(caps, one, caps)); + /* support for function 7 */ + aml_append(ifctx1, + aml_or(caps, aml_shiftleft(one, aml_int(7)), caps)); + } + aml_append(ifctx, ifctx1); + + aml_append(ifctx, aml_store(caps, aml_index(ret, zero))); + aml_append(ifctx, aml_return(ret)); + } + aml_append(method, ifctx); + + /* handle specific functions requests */ + /* + * PCI Firmware Specification 3.1 + * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under + * Operating Systems + */ + ifctx = aml_if(aml_equal(func, aml_int(7))); + { + Aml *pkg = aml_package(2); + + aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); + aml_append(ifctx, aml_store(pkg, ret)); + /* + * Windows calls func=7 without checking if it's available, + * as workaround Microsoft has suggested to return invalid for func7 + * Package, so return 2 elements package but only initialize elements + * when acpi_index is supported and leave them uninitialized, which + * leads elements to being Uninitialized ObjectType and should trip + * Windows into discarding result as an unexpected and prevent setting + * bogus 'PCI Label' on the device. + */ + ifctx1 = aml_if(aml_lnot(aml_lor( + aml_equal(acpi_index, zero), aml_equal(acpi_index, not_supp) + ))); + { + aml_append(ifctx1, aml_store(acpi_index, aml_index(ret, zero))); + /* + * optional, if not impl. should return null string + */ + aml_append(ifctx1, aml_store(aml_string("%s", ""), + aml_index(ret, one))); + } + aml_append(ifctx, ifctx1); + + aml_append(ifctx, aml_return(ret)); + } + + aml_append(method, ifctx); + return method; +} + +void build_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr) +{ + Aml *scope; + Aml *field; + Aml *method; + + scope = aml_scope("_SB.PCI0"); + + aml_append(scope, + aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(pcihp_addr), 0x08)); + field = aml_field("PCST", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); + aml_append(field, aml_named_field("PCIU", 32)); + aml_append(field, aml_named_field("PCID", 32)); + aml_append(scope, field); + + aml_append(scope, + aml_operation_region("SEJ", AML_SYSTEM_IO, + aml_int(pcihp_addr + ACPI_PCIHP_SEJ_BASE), 0x04)); + field = aml_field("SEJ", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); + aml_append(field, aml_named_field("B0EJ", 32)); + aml_append(scope, field); + + aml_append(scope, + aml_operation_region("BNMR", AML_SYSTEM_IO, + aml_int(pcihp_addr + ACPI_PCIHP_BNMR_BASE), 0x08)); + field = aml_field("BNMR", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); + aml_append(field, aml_named_field("BNUM", 32)); + aml_append(field, aml_named_field("PIDX", 32)); + aml_append(scope, field); + + aml_append(scope, aml_mutex("BLCK", 0)); + + method = aml_method("PCEJ", 2, AML_NOTSERIALIZED); + aml_append(method, aml_acquire(aml_name("BLCK"), 0xFFFF)); + aml_append(method, aml_store(aml_arg(0), aml_name("BNUM"))); + aml_append(method, + aml_store(aml_shiftleft(aml_int(1), aml_arg(1)), aml_name("B0EJ"))); + aml_append(method, aml_release(aml_name("BLCK"))); + aml_append(method, aml_return(aml_int(0))); + aml_append(scope, method); + + method = aml_method("AIDX", 2, AML_NOTSERIALIZED); + aml_append(method, aml_acquire(aml_name("BLCK"), 0xFFFF)); + aml_append(method, aml_store(aml_arg(0), aml_name("BNUM"))); + aml_append(method, + aml_store(aml_shiftleft(aml_int(1), aml_arg(1)), aml_name("PIDX"))); + aml_append(method, aml_store(aml_name("PIDX"), aml_local(0))); + aml_append(method, aml_release(aml_name("BLCK"))); + aml_append(method, aml_return(aml_local(0))); + aml_append(scope, method); + + aml_append(scope, aml_pci_pdsm()); + + aml_append(table, scope); +} + const VMStateDescription vmstate_acpi_pcihp_pci_status = { .name = "acpi_pcihp_pci_status", .version_id = 1, diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index f59026524f..4f8572eebe 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -361,32 +361,6 @@ static Aml *aml_pci_device_dsm(void) return method; } -static void build_append_pci_dsm_func0_common(Aml *ctx, Aml *retvar) -{ - Aml *UUID, *ifctx1; - uint8_t byte_list[1] = { 0 }; /* nothing supported yet */ - - aml_append(ctx, aml_store(aml_buffer(1, byte_list), retvar)); - /* - * PCI Firmware Specification 3.1 - * 4.6. _DSM Definitions for PCI - */ - UUID = aml_touuid("E5C937D0-3553-4D7A-9117-EA4D19C3434D"); - ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(0), UUID))); - { - /* call is for unsupported UUID, bail out */ - aml_append(ifctx1, aml_return(retvar)); - } - aml_append(ctx, ifctx1); - - ifctx1 = aml_if(aml_lless(aml_arg(1), aml_int(2))); - { - /* call is for unsupported REV, bail out */ - aml_append(ifctx1, aml_return(retvar)); - } - aml_append(ctx, ifctx1); -} - static Aml *aml_pci_edsm(void) { Aml *method, *ifctx; @@ -647,96 +621,6 @@ static bool build_append_notification_callback(Aml *parent_scope, return !!nr_notifiers; } -static Aml *aml_pci_pdsm(void) -{ - Aml *method, *ifctx, *ifctx1; - Aml *ret = aml_local(0); - Aml *caps = aml_local(1); - Aml *acpi_index = aml_local(2); - Aml *zero = aml_int(0); - Aml *one = aml_int(1); - Aml *not_supp = aml_int(0xFFFFFFFF); - Aml *func = aml_arg(2); - Aml *params = aml_arg(4); - Aml *bnum = aml_derefof(aml_index(params, aml_int(0))); - Aml *sunum = aml_derefof(aml_index(params, aml_int(1))); - - method = aml_method("PDSM", 5, AML_SERIALIZED); - - /* get supported functions */ - ifctx = aml_if(aml_equal(func, zero)); - { - build_append_pci_dsm_func0_common(ifctx, ret); - - aml_append(ifctx, aml_store(zero, caps)); - aml_append(ifctx, - aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); - /* - * advertise function 7 if device has acpi-index - * acpi_index values: - * 0: not present (default value) - * FFFFFFFF: not supported (old QEMU without PIDX reg) - * other: device's acpi-index - */ - ifctx1 = aml_if(aml_lnot( - aml_or(aml_equal(acpi_index, zero), - aml_equal(acpi_index, not_supp), NULL) - )); - { - /* have supported functions */ - aml_append(ifctx1, aml_or(caps, one, caps)); - /* support for function 7 */ - aml_append(ifctx1, - aml_or(caps, aml_shiftleft(one, aml_int(7)), caps)); - } - aml_append(ifctx, ifctx1); - - aml_append(ifctx, aml_store(caps, aml_index(ret, zero))); - aml_append(ifctx, aml_return(ret)); - } - aml_append(method, ifctx); - - /* handle specific functions requests */ - /* - * PCI Firmware Specification 3.1 - * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under - * Operating Systems - */ - ifctx = aml_if(aml_equal(func, aml_int(7))); - { - Aml *pkg = aml_package(2); - - aml_append(ifctx, aml_store(aml_call2("AIDX", bnum, sunum), acpi_index)); - aml_append(ifctx, aml_store(pkg, ret)); - /* - * Windows calls func=7 without checking if it's available, - * as workaround Microsoft has suggested to return invalid for func7 - * Package, so return 2 elements package but only initialize elements - * when acpi_index is supported and leave them uninitialized, which - * leads elements to being Uninitialized ObjectType and should trip - * Windows into discarding result as an unexpected and prevent setting - * bogus 'PCI Label' on the device. - */ - ifctx1 = aml_if(aml_lnot(aml_lor( - aml_equal(acpi_index, zero), aml_equal(acpi_index, not_supp) - ))); - { - aml_append(ifctx1, aml_store(acpi_index, aml_index(ret, zero))); - /* - * optional, if not impl. should return null string - */ - aml_append(ifctx1, aml_store(aml_string("%s", ""), - aml_index(ret, one))); - } - aml_append(ifctx, ifctx1); - - aml_append(ifctx, aml_return(ret)); - } - - aml_append(method, ifctx); - return method; -} - /* * build_prt - Define interrupt routing rules * @@ -1227,62 +1111,6 @@ static Aml *build_q35_dram_controller(const AcpiMcfgInfo *mcfg) return dev; } -static void build_x86_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr) -{ - Aml *scope; - Aml *field; - Aml *method; - - scope = aml_scope("_SB.PCI0"); - - aml_append(scope, - aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(pcihp_addr), 0x08)); - field = aml_field("PCST", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); - aml_append(field, aml_named_field("PCIU", 32)); - aml_append(field, aml_named_field("PCID", 32)); - aml_append(scope, field); - - aml_append(scope, - aml_operation_region("SEJ", AML_SYSTEM_IO, - aml_int(pcihp_addr + ACPI_PCIHP_SEJ_BASE), 0x04)); - field = aml_field("SEJ", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); - aml_append(field, aml_named_field("B0EJ", 32)); - aml_append(scope, field); - - aml_append(scope, - aml_operation_region("BNMR", AML_SYSTEM_IO, - aml_int(pcihp_addr + ACPI_PCIHP_BNMR_BASE), 0x08)); - field = aml_field("BNMR", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); - aml_append(field, aml_named_field("BNUM", 32)); - aml_append(field, aml_named_field("PIDX", 32)); - aml_append(scope, field); - - aml_append(scope, aml_mutex("BLCK", 0)); - - method = aml_method("PCEJ", 2, AML_NOTSERIALIZED); - aml_append(method, aml_acquire(aml_name("BLCK"), 0xFFFF)); - aml_append(method, aml_store(aml_arg(0), aml_name("BNUM"))); - aml_append(method, - aml_store(aml_shiftleft(aml_int(1), aml_arg(1)), aml_name("B0EJ"))); - aml_append(method, aml_release(aml_name("BLCK"))); - aml_append(method, aml_return(aml_int(0))); - aml_append(scope, method); - - method = aml_method("AIDX", 2, AML_NOTSERIALIZED); - aml_append(method, aml_acquire(aml_name("BLCK"), 0xFFFF)); - aml_append(method, aml_store(aml_arg(0), aml_name("BNUM"))); - aml_append(method, - aml_store(aml_shiftleft(aml_int(1), aml_arg(1)), aml_name("PIDX"))); - aml_append(method, aml_store(aml_name("PIDX"), aml_local(0))); - aml_append(method, aml_release(aml_name("BLCK"))); - aml_append(method, aml_return(aml_local(0))); - aml_append(scope, method); - - aml_append(scope, aml_pci_pdsm()); - - aml_append(table, scope); -} - static Aml *build_q35_osc_method(bool enable_native_pcie_hotplug) { Aml *if_ctx; @@ -1394,7 +1222,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, sb_scope); if (pm->pcihp_bridge_en || pm->pcihp_root_en) { - build_x86_acpi_pci_hotplug(dsdt, pm->pcihp_io_base); + build_acpi_pci_hotplug(dsdt, pm->pcihp_io_base); } build_piix4_pci0_int(dsdt); } else if (q35) { @@ -1438,7 +1266,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, sb_scope); if (pm->pcihp_bridge_en) { - build_x86_acpi_pci_hotplug(dsdt, pm->pcihp_io_base); + build_acpi_pci_hotplug(dsdt, pm->pcihp_io_base); } build_q35_pci0_int(dsdt); } diff --git a/hw/i386/acpi-build.h b/hw/i386/acpi-build.h index 275ec058a1..8ba3c33e48 100644 --- a/hw/i386/acpi-build.h +++ b/hw/i386/acpi-build.h @@ -5,10 +5,6 @@ extern const struct AcpiGenericAddress x86_nvdimm_acpi_dsmio; -/* PCI Hot-plug registers' base. See docs/specs/acpi_pci_hotplug.rst */ -#define ACPI_PCIHP_SEJ_BASE 0x8 -#define ACPI_PCIHP_BNMR_BASE 0x10 - void acpi_setup(void); Object *acpi_get_i386_pci_host(void); diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index cdc0cb8e43..971451e8ea 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -33,6 +33,10 @@ #define ACPI_PCIHP_IO_BASE_PROP "acpi-pcihp-io-base" #define ACPI_PCIHP_IO_LEN_PROP "acpi-pcihp-io-len" +/* PCI Hot-plug registers bases. See docs/specs/acpi_pci_hotplug.rst */ +#define ACPI_PCIHP_SEJ_BASE 0x8 +#define ACPI_PCIHP_BNMR_BASE 0x10 + typedef struct AcpiPciHpPciStatus { uint32_t up; uint32_t down; @@ -69,6 +73,9 @@ void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp); +void build_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr); +void build_append_pci_dsm_func0_common(Aml *ctx, Aml *retvar); + /* Called on reset */ void acpi_pcihp_reset(AcpiPciHpState *s); From eb3feb8747754d2e41654368133c2d61cc8dd72c Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:47 +0200 Subject: [PATCH 2388/2760] hw/pci-host/gpex-acpi: Add native_pci_hotplug arg to acpi_dsdt_add_pci_osc Add a new argument to acpi_dsdt_add_pci_osc to be able to disable native pci hotplug. Signed-off-by: Eric Auger Reviewed-by: Gustavo Romero Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <20250714080639.2525563-4-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-host/gpex-acpi.c | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/pci-host/gpex-acpi.c b/hw/pci-host/gpex-acpi.c index 0aba47c71c..f34b7cf25e 100644 --- a/hw/pci-host/gpex-acpi.c +++ b/hw/pci-host/gpex-acpi.c @@ -50,7 +50,7 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq, } } -static void acpi_dsdt_add_pci_osc(Aml *dev) +static void acpi_dsdt_add_pci_osc(Aml *dev, bool enable_native_pcie_hotplug) { Aml *method, *UUID, *ifctx, *ifctx1, *elsectx, *buf; @@ -77,11 +77,12 @@ static void acpi_dsdt_add_pci_osc(Aml *dev) aml_append(ifctx, aml_store(aml_name("CDW3"), aml_name("CTRL"))); /* - * Allow OS control for all 5 features: - * PCIeHotplug SHPCHotplug PME AER PCIeCapability. + * Allow OS control for SHPCHotplug, PME, AER, PCIeCapability, + * and PCIeHotplug depending on enable_native_pcie_hotplug */ - aml_append(ifctx, aml_and(aml_name("CTRL"), aml_int(0x1F), - aml_name("CTRL"))); + aml_append(ifctx, aml_and(aml_name("CTRL"), + aml_int(0x1E | (enable_native_pcie_hotplug ? 0x1 : 0x0)), + aml_name("CTRL"))); ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1)))); aml_append(ifctx1, aml_or(aml_name("CDW1"), aml_int(0x08), @@ -192,7 +193,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) if (is_cxl) { build_cxl_osc_method(dev); } else { - acpi_dsdt_add_pci_osc(dev); + acpi_dsdt_add_pci_osc(dev, true); } aml_append(scope, dev); @@ -267,7 +268,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) } aml_append(dev, aml_name_decl("_CRS", rbuf)); - acpi_dsdt_add_pci_osc(dev); + acpi_dsdt_add_pci_osc(dev, true); Aml *dev_res0 = aml_device("%s", "RES0"); aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02"))); From 9748673735d93da3b1da12041f2525e0fbca5dc6 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Mon, 14 Jul 2025 10:04:48 +0200 Subject: [PATCH 2389/2760] tests/qtest/bios-tables-test: Prepare for changes in the DSDT table This commit adds DSDT blobs to the whilelist in the prospect to allow changes in the GPEX _OSC method. Signed-off-by: Gustavo Romero Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Acked-by: Igor Mammedov Message-Id: <20250714080639.2525563-5-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..8d9673cb5d 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,12 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/DSDT", +"tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt", +"tests/data/acpi/aarch64/virt/DSDT.memhp", +"tests/data/acpi/aarch64/virt/DSDT.pxb", +"tests/data/acpi/aarch64/virt/DSDT.topology", +"tests/data/acpi/loongarch64/virt/DSDT.memhp", +"tests/data/acpi/loongarch64/virt/DSDT.topology", +"tests/data/acpi/loongarch64/virt/DSDT.numamem", +"tests/data/acpi/loongarch64/virt/DSDT", +"tests/data/acpi/x86/microvm/DSDT.pcie", +"tests/data/acpi/riscv64/virt/DSDT", From ec9af8919826d57126d9b83fa1715d1333b74a18 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:49 +0200 Subject: [PATCH 2390/2760] hw/pci-host/gpex-acpi: Split host bridge OSC and DSM generation acpi_dsdt_add_pci_osc() name is confusing as it gives the impression it appends the _OSC method but in fact it also appends the _DSM method for the host bridge. Let's split the function into two separate ones and let them return the method Aml pointer instead. This matches the way it is done on x86 (build_q35_osc_method). In a subsequent patch we will replace the gpex method by the q35 implementation that will become shared between ARM and x86. acpi_dsdt_add_host_bridge_methods is a new top helper that generates both the _OSC and _DSM methods. We take the opportunity to move SUPP and CTRL in the _osc method that use them. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-6-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-host/gpex-acpi.c | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/hw/pci-host/gpex-acpi.c b/hw/pci-host/gpex-acpi.c index f34b7cf25e..80fc2bf032 100644 --- a/hw/pci-host/gpex-acpi.c +++ b/hw/pci-host/gpex-acpi.c @@ -50,14 +50,12 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq, } } -static void acpi_dsdt_add_pci_osc(Aml *dev, bool enable_native_pcie_hotplug) +static Aml *build_host_bridge_osc(bool enable_native_pcie_hotplug) { - Aml *method, *UUID, *ifctx, *ifctx1, *elsectx, *buf; - - /* Declare an _OSC (OS Control Handoff) method */ - aml_append(dev, aml_name_decl("SUPP", aml_int(0))); - aml_append(dev, aml_name_decl("CTRL", aml_int(0))); + Aml *method, *UUID, *ifctx, *ifctx1, *elsectx; method = aml_method("_OSC", 4, AML_NOTSERIALIZED); + aml_append(method, aml_name_decl("SUPP", aml_int(0))); + aml_append(method, aml_name_decl("CTRL", aml_int(0))); aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); @@ -103,9 +101,13 @@ static void acpi_dsdt_add_pci_osc(Aml *dev, bool enable_native_pcie_hotplug) aml_name("CDW1"))); aml_append(elsectx, aml_return(aml_arg(3))); aml_append(method, elsectx); - aml_append(dev, method); + return method; +} - method = aml_method("_DSM", 4, AML_NOTSERIALIZED); +static Aml *build_host_bridge_dsm(void) +{ + Aml *method = aml_method("_DSM", 4, AML_NOTSERIALIZED); + Aml *UUID, *ifctx, *ifctx1, *buf; /* PCI Firmware Specification 3.0 * 4.6.1. _DSM for PCI Express Slot Information @@ -124,7 +126,15 @@ static void acpi_dsdt_add_pci_osc(Aml *dev, bool enable_native_pcie_hotplug) byte_list[0] = 0; buf = aml_buffer(1, byte_list); aml_append(method, aml_return(buf)); - aml_append(dev, method); + return method; +} + +static void acpi_dsdt_add_host_bridge_methods(Aml *dev, + bool enable_native_pcie_hotplug) +{ + /* Declare an _OSC (OS Control Handoff) method */ + aml_append(dev, build_host_bridge_osc(enable_native_pcie_hotplug)); + aml_append(dev, build_host_bridge_dsm()); } void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) @@ -193,7 +203,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) if (is_cxl) { build_cxl_osc_method(dev); } else { - acpi_dsdt_add_pci_osc(dev, true); + acpi_dsdt_add_host_bridge_methods(dev, true); } aml_append(scope, dev); @@ -268,7 +278,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) } aml_append(dev, aml_name_decl("_CRS", rbuf)); - acpi_dsdt_add_pci_osc(dev, true); + acpi_dsdt_add_host_bridge_methods(dev, true); Aml *dev_res0 = aml_device("%s", "RES0"); aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02"))); From c4ad7513565979ae4c3910bd37c453f5737343a1 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:50 +0200 Subject: [PATCH 2391/2760] hw/acpi/ged: Add a acpi-pci-hotplug-with-bridge-support property A new boolean property is introduced. This will be used to turn ACPI PCI hotplug support. By default it is unset. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-7-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/generic_event_device.c | 2 ++ include/hw/acpi/generic_event_device.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 7a62f8d5bc..7831db412b 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -318,6 +318,8 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) static const Property acpi_ged_properties[] = { DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0), + DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, AcpiGedState, + pcihp_state.use_acpi_hotplug_bridge, 0), }; static const VMStateDescription vmstate_memhp_state = { diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index d2dac87b4a..f5ffa67a39 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -63,6 +63,7 @@ #include "hw/acpi/memory_hotplug.h" #include "hw/acpi/ghes.h" #include "hw/acpi/cpu.h" +#include "hw/acpi/pcihp.h" #include "qom/object.h" #define ACPI_POWER_BUTTON_DEVICE "PWRB" @@ -114,6 +115,7 @@ struct AcpiGedState { MemoryRegion container_memhp; CPUHotplugState cpuhp_state; MemoryRegion container_cpuhp; + AcpiPciHpState pcihp_state; GEDState ged_state; uint32_t ged_event_bitmap; qemu_irq irq; From 1136309df5e3de7d031a137221318f48784cca5d Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:51 +0200 Subject: [PATCH 2392/2760] hw/pci-host/gpex-acpi: Use GED acpi pcihp property Retrieve the acpi pcihp property value from the ged. In case this latter is not set, PCI native hotplug is used on pci0. For expander bridges we keep pci native hotplug, as done on x86 q35. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Message-Id: <20250714080639.2525563-8-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt-acpi-build.c | 9 +++++++++ hw/pci-host/gpex-acpi.c | 3 ++- include/hw/pci-host/gpex.h | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 782b17b966..f3bad69aa7 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -144,12 +144,21 @@ static void acpi_dsdt_add_pci(Aml *scope, const MemMapEntry *memmap, int ecam_id = VIRT_ECAM_ID(vms->highmem_ecam); bool cxl_present = false; PCIBus *bus = vms->bus; + bool acpi_pcihp = false; + + if (vms->acpi_dev) { + acpi_pcihp = object_property_get_bool(OBJECT(vms->acpi_dev), + ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, + NULL); + } + struct GPEXConfig cfg = { .mmio32 = memmap[VIRT_PCIE_MMIO], .pio = memmap[VIRT_PCIE_PIO], .ecam = memmap[ecam_id], .irq = irq, .bus = vms->bus, + .pci_native_hotplug = !acpi_pcihp, }; if (vms->highmem_mmio) { diff --git a/hw/pci-host/gpex-acpi.c b/hw/pci-host/gpex-acpi.c index 80fc2bf032..44737a8d81 100644 --- a/hw/pci-host/gpex-acpi.c +++ b/hw/pci-host/gpex-acpi.c @@ -203,6 +203,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) if (is_cxl) { build_cxl_osc_method(dev); } else { + /* pxb bridges do not have ACPI PCI Hot-plug enabled */ acpi_dsdt_add_host_bridge_methods(dev, true); } @@ -278,7 +279,7 @@ void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) } aml_append(dev, aml_name_decl("_CRS", rbuf)); - acpi_dsdt_add_host_bridge_methods(dev, true); + acpi_dsdt_add_host_bridge_methods(dev, cfg->pci_native_hotplug); Aml *dev_res0 = aml_device("%s", "RES0"); aml_append(dev_res0, aml_name_decl("_HID", aml_string("PNP0C02"))); diff --git a/include/hw/pci-host/gpex.h b/include/hw/pci-host/gpex.h index 84471533af..feaf827474 100644 --- a/include/hw/pci-host/gpex.h +++ b/include/hw/pci-host/gpex.h @@ -45,6 +45,7 @@ struct GPEXConfig { MemMapEntry pio; int irq; PCIBus *bus; + bool pci_native_hotplug; }; typedef struct GPEXIrq GPEXIrq; From dc925e4b1d19dcce93107d1d20f7232244f48924 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:52 +0200 Subject: [PATCH 2393/2760] hw/i386/acpi-build: Turn build_q35_osc_method into a generic method GPEX acpi_dsdt_add_pci_osc() does basically the same as build_q35_osc_method(). Rename build_q35_osc_method() into build_pci_host_bridge_osc_method() and move it into hw/acpi/pci.c. In a subsequent patch we will use this later in place of acpi_dsdt_add_pci_osc(). Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-9-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/pci.c | 50 +++++++++++++++++++++++++++++++++++++++ hw/i386/acpi-build.c | 54 ++----------------------------------------- include/hw/acpi/pci.h | 2 ++ 3 files changed, 54 insertions(+), 52 deletions(-) diff --git a/hw/acpi/pci.c b/hw/acpi/pci.c index d511a85029..2228f1245e 100644 --- a/hw/acpi/pci.c +++ b/hw/acpi/pci.c @@ -301,3 +301,53 @@ void build_srat_generic_affinity_structures(GArray *table_data) object_child_foreach_recursive(object_get_root(), build_acpi_generic_port, table_data); } + +Aml *build_pci_host_bridge_osc_method(bool enable_native_pcie_hotplug) +{ + Aml *if_ctx; + Aml *if_ctx2; + Aml *else_ctx; + Aml *method; + Aml *a_cwd1 = aml_name("CDW1"); + Aml *a_ctrl = aml_local(0); + + method = aml_method("_OSC", 4, AML_NOTSERIALIZED); + aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); + + if_ctx = aml_if(aml_equal( + aml_arg(0), aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766"))); + aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2")); + aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); + + aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl)); + + /* + * Always allow native PME, AER (no dependencies) + * Allow SHPC (PCI bridges can have SHPC controller) + * Disable PCIe Native Hot-plug if ACPI PCI Hot-plug is enabled. + */ + aml_append(if_ctx, aml_and(a_ctrl, + aml_int(0x1E | (enable_native_pcie_hotplug ? 0x1 : 0x0)), a_ctrl)); + + if_ctx2 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1)))); + /* Unknown revision */ + aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x08), a_cwd1)); + aml_append(if_ctx, if_ctx2); + + if_ctx2 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl))); + /* Capabilities bits were masked */ + aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x10), a_cwd1)); + aml_append(if_ctx, if_ctx2); + + /* Update DWORD3 in the buffer */ + aml_append(if_ctx, aml_store(a_ctrl, aml_name("CDW3"))); + aml_append(method, if_ctx); + + else_ctx = aml_else(); + /* Unrecognized UUID */ + aml_append(else_ctx, aml_or(a_cwd1, aml_int(4), a_cwd1)); + aml_append(method, else_ctx); + + aml_append(method, aml_return(aml_arg(3))); + return method; +} diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 4f8572eebe..91945f716c 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1111,56 +1111,6 @@ static Aml *build_q35_dram_controller(const AcpiMcfgInfo *mcfg) return dev; } -static Aml *build_q35_osc_method(bool enable_native_pcie_hotplug) -{ - Aml *if_ctx; - Aml *if_ctx2; - Aml *else_ctx; - Aml *method; - Aml *a_cwd1 = aml_name("CDW1"); - Aml *a_ctrl = aml_local(0); - - method = aml_method("_OSC", 4, AML_NOTSERIALIZED); - aml_append(method, aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); - - if_ctx = aml_if(aml_equal( - aml_arg(0), aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766"))); - aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2")); - aml_append(if_ctx, aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); - - aml_append(if_ctx, aml_store(aml_name("CDW3"), a_ctrl)); - - /* - * Always allow native PME, AER (no dependencies) - * Allow SHPC (PCI bridges can have SHPC controller) - * Disable PCIe Native Hot-plug if ACPI PCI Hot-plug is enabled. - */ - aml_append(if_ctx, aml_and(a_ctrl, - aml_int(0x1E | (enable_native_pcie_hotplug ? 0x1 : 0x0)), a_ctrl)); - - if_ctx2 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(1)))); - /* Unknown revision */ - aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x08), a_cwd1)); - aml_append(if_ctx, if_ctx2); - - if_ctx2 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), a_ctrl))); - /* Capabilities bits were masked */ - aml_append(if_ctx2, aml_or(a_cwd1, aml_int(0x10), a_cwd1)); - aml_append(if_ctx, if_ctx2); - - /* Update DWORD3 in the buffer */ - aml_append(if_ctx, aml_store(a_ctrl, aml_name("CDW3"))); - aml_append(method, if_ctx); - - else_ctx = aml_else(); - /* Unrecognized UUID */ - aml_append(else_ctx, aml_or(a_cwd1, aml_int(4), a_cwd1)); - aml_append(method, else_ctx); - - aml_append(method, aml_return(aml_arg(3))); - return method; -} - static void build_acpi0017(Aml *table) { Aml *dev, *scope, *method; @@ -1231,7 +1181,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A08"))); aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03"))); aml_append(dev, aml_name_decl("_UID", aml_int(pcmc->pci_root_uid))); - aml_append(dev, build_q35_osc_method(!pm->pcihp_bridge_en)); + aml_append(dev, build_pci_host_bridge_osc_method(!pm->pcihp_bridge_en)); aml_append(dev, aml_pci_edsm()); aml_append(sb_scope, dev); if (mcfg_valid) { @@ -1353,7 +1303,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03"))); /* Expander bridges do not have ACPI PCI Hot-plug enabled */ - aml_append(dev, build_q35_osc_method(true)); + aml_append(dev, build_pci_host_bridge_osc_method(true)); } else { aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03"))); } diff --git a/include/hw/acpi/pci.h b/include/hw/acpi/pci.h index ab0187a894..8a328b580c 100644 --- a/include/hw/acpi/pci.h +++ b/include/hw/acpi/pci.h @@ -42,4 +42,6 @@ void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope); void build_srat_generic_affinity_structures(GArray *table_data); +Aml *build_pci_host_bridge_osc_method(bool enable_native_pcie_hotplug); + #endif From af151d50eac24604be19d0fc3bd6492979b0f399 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:53 +0200 Subject: [PATCH 2394/2760] hw/pci-host/gpex-acpi: Use build_pci_host_bridge_osc_method gpex build_host_bridge_osc() and x86 originated build_pci_host_bridge_osc_method() are mostly identical. In GPEX, SUPP is set to CDW2 but is not further used. CTRL is same as Local0. So let gpex code reuse build_pci_host_bridge_osc_method() and remove build_host_bridge_osc(). Also add an imply ACPI_PCI clause along with PCI_EXPRESS_GENERIC_BRIDGE to compile hw/acpi/pci.c when its dependency is resolved (ie. CONFIG_ACPI_PCI). This is requested to link qemu-system-mips64el. The disassembled DSDT difference is given below: * Original Table Header: * Signature "DSDT" - * Length 0x00001A4F (6735) + * Length 0x00001A35 (6709) * Revision 0x02 - * Checksum 0xBF + * Checksum 0xDD * OEM ID "BOCHS " * OEM Table ID "BXPC " * OEM Revision 0x00000001 (1) @@ -1849,27 +1849,26 @@ DefinitionBlock ("", "DSDT", 2, "BOCHS ", "BXPC ", 0x00000001) { CreateDWordField (Arg3, 0x04, CDW2) CreateDWordField (Arg3, 0x08, CDW3) - SUPP = CDW2 /* \_SB_.PCI0._OSC.CDW2 */ - CTRL = CDW3 /* \_SB_.PCI0._OSC.CDW3 */ - CTRL &= 0x1F + Local0 = CDW3 /* \_SB_.PCI0._OSC.CDW3 */ + Local0 &= 0x1F If ((Arg1 != One)) { CDW1 |= 0x08 } - If ((CDW3 != CTRL)) + If ((CDW3 != Local0)) { CDW1 |= 0x10 } - CDW3 = CTRL /* \_SB_.PCI0.CTRL */ - Return (Arg3) + CDW3 = Local0 } Else { CDW1 |= 0x04 - Return (Arg3) } + + Return (Arg3) } Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method Signed-off-by: Eric Auger Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <20250714080639.2525563-10-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci-host/Kconfig | 1 + hw/pci-host/gpex-acpi.c | 62 ++++------------------------------------- 2 files changed, 6 insertions(+), 57 deletions(-) diff --git a/hw/pci-host/Kconfig b/hw/pci-host/Kconfig index 35c0415242..9824fa188d 100644 --- a/hw/pci-host/Kconfig +++ b/hw/pci-host/Kconfig @@ -54,6 +54,7 @@ config PCI_EXPRESS_Q35 config PCI_EXPRESS_GENERIC_BRIDGE bool select PCI_EXPRESS + imply ACPI_PCI config PCI_EXPRESS_XILINX bool diff --git a/hw/pci-host/gpex-acpi.c b/hw/pci-host/gpex-acpi.c index 44737a8d81..952a0ace19 100644 --- a/hw/pci-host/gpex-acpi.c +++ b/hw/pci-host/gpex-acpi.c @@ -1,5 +1,6 @@ #include "qemu/osdep.h" #include "hw/acpi/aml-build.h" +#include "hw/acpi/pci.h" #include "hw/pci-host/gpex.h" #include "hw/arm/virt.h" #include "hw/pci/pci_bus.h" @@ -50,61 +51,7 @@ static void acpi_dsdt_add_pci_route_table(Aml *dev, uint32_t irq, } } -static Aml *build_host_bridge_osc(bool enable_native_pcie_hotplug) -{ - Aml *method, *UUID, *ifctx, *ifctx1, *elsectx; - method = aml_method("_OSC", 4, AML_NOTSERIALIZED); - aml_append(method, aml_name_decl("SUPP", aml_int(0))); - aml_append(method, aml_name_decl("CTRL", aml_int(0))); - aml_append(method, - aml_create_dword_field(aml_arg(3), aml_int(0), "CDW1")); - - /* PCI Firmware Specification 3.0 - * 4.5.1. _OSC Interface for PCI Host Bridge Devices - * The _OSC interface for a PCI/PCI-X/PCI Express hierarchy is - * identified by the Universal Unique IDentifier (UUID) - * 33DB4D5B-1FF7-401C-9657-7441C03DD766 - */ - UUID = aml_touuid("33DB4D5B-1FF7-401C-9657-7441C03DD766"); - ifctx = aml_if(aml_equal(aml_arg(0), UUID)); - aml_append(ifctx, - aml_create_dword_field(aml_arg(3), aml_int(4), "CDW2")); - aml_append(ifctx, - aml_create_dword_field(aml_arg(3), aml_int(8), "CDW3")); - aml_append(ifctx, aml_store(aml_name("CDW2"), aml_name("SUPP"))); - aml_append(ifctx, aml_store(aml_name("CDW3"), aml_name("CTRL"))); - - /* - * Allow OS control for SHPCHotplug, PME, AER, PCIeCapability, - * and PCIeHotplug depending on enable_native_pcie_hotplug - */ - aml_append(ifctx, aml_and(aml_name("CTRL"), - aml_int(0x1E | (enable_native_pcie_hotplug ? 0x1 : 0x0)), - aml_name("CTRL"))); - - ifctx1 = aml_if(aml_lnot(aml_equal(aml_arg(1), aml_int(0x1)))); - aml_append(ifctx1, aml_or(aml_name("CDW1"), aml_int(0x08), - aml_name("CDW1"))); - aml_append(ifctx, ifctx1); - - ifctx1 = aml_if(aml_lnot(aml_equal(aml_name("CDW3"), aml_name("CTRL")))); - aml_append(ifctx1, aml_or(aml_name("CDW1"), aml_int(0x10), - aml_name("CDW1"))); - aml_append(ifctx, ifctx1); - - aml_append(ifctx, aml_store(aml_name("CTRL"), aml_name("CDW3"))); - aml_append(ifctx, aml_return(aml_arg(3))); - aml_append(method, ifctx); - - elsectx = aml_else(); - aml_append(elsectx, aml_or(aml_name("CDW1"), aml_int(4), - aml_name("CDW1"))); - aml_append(elsectx, aml_return(aml_arg(3))); - aml_append(method, elsectx); - return method; -} - -static Aml *build_host_bridge_dsm(void) +static Aml *build_pci_host_bridge_dsm_method(void) { Aml *method = aml_method("_DSM", 4, AML_NOTSERIALIZED); Aml *UUID, *ifctx, *ifctx1, *buf; @@ -133,8 +80,9 @@ static void acpi_dsdt_add_host_bridge_methods(Aml *dev, bool enable_native_pcie_hotplug) { /* Declare an _OSC (OS Control Handoff) method */ - aml_append(dev, build_host_bridge_osc(enable_native_pcie_hotplug)); - aml_append(dev, build_host_bridge_dsm()); + aml_append(dev, + build_pci_host_bridge_osc_method(enable_native_pcie_hotplug)); + aml_append(dev, build_pci_host_bridge_dsm_method()); } void acpi_dsdt_add_gpex(Aml *scope, struct GPEXConfig *cfg) From f47d6e6a8f69c8890207d3f771e079700418f7a6 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 14 Jul 2025 17:49:03 -0400 Subject: [PATCH 2395/2760] tests/qtest/bios-tables-test: Update DSDT blobs after GPEX _OSC change Update the reference DSDT blobs after GPEX _OSC change. The _OSC change affects the aarch64 'virt' and the x86 'microvm' machines. DSDT diff is the same for all the machines/tests: * Original Table Header: * Signature "DSDT" - * Length 0x00001A4F (6735) + * Length 0x00001A35 (6709) * Revision 0x02 - * Checksum 0xBF + * Checksum 0xDD * OEM ID "BOCHS " * OEM Table ID "BXPC " * OEM Revision 0x00000001 (1) @@ -1849,27 +1849,26 @@ DefinitionBlock ("", "DSDT", 2, "BOCHS ", "BXPC ", 0x00000001) { CreateDWordField (Arg3, 0x04, CDW2) CreateDWordField (Arg3, 0x08, CDW3) - SUPP = CDW2 /* \_SB_.PCI0._OSC.CDW2 */ - CTRL = CDW3 /* \_SB_.PCI0._OSC.CDW3 */ - CTRL &= 0x1F + Local0 = CDW3 /* \_SB_.PCI0._OSC.CDW3 */ + Local0 &= 0x1F If ((Arg1 != One)) { CDW1 |= 0x08 } - If ((CDW3 != CTRL)) + If ((CDW3 != Local0)) { CDW1 |= 0x10 } - CDW3 = CTRL /* \_SB_.PCI0.CTRL */ - Return (Arg3) + CDW3 = Local0 } Else { CDW1 |= 0x04 - Return (Arg3) } + + Return (Arg3) } Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method Signed-off-by: Eric Auger Signed-off-by: Gustavo Romero Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-11-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/aarch64/virt/DSDT | Bin 5196 -> 5158 bytes .../data/acpi/aarch64/virt/DSDT.acpihmatvirt | Bin 5282 -> 5244 bytes tests/data/acpi/aarch64/virt/DSDT.memhp | Bin 6557 -> 6519 bytes tests/data/acpi/aarch64/virt/DSDT.pxb | Bin 7679 -> 7603 bytes tests/data/acpi/aarch64/virt/DSDT.topology | Bin 5398 -> 5360 bytes tests/data/acpi/loongarch64/virt/DSDT | Bin 4641 -> 4603 bytes tests/data/acpi/loongarch64/virt/DSDT.memhp | Bin 5862 -> 5824 bytes tests/data/acpi/loongarch64/virt/DSDT.numamem | Bin 4647 -> 4609 bytes .../data/acpi/loongarch64/virt/DSDT.topology | Bin 4943 -> 4905 bytes tests/data/acpi/riscv64/virt/DSDT | Bin 3576 -> 3538 bytes tests/data/acpi/x86/microvm/DSDT.pcie | Bin 3023 -> 2985 bytes tests/qtest/bios-tables-test-allowed-diff.h | 11 ----------- 12 files changed, 11 deletions(-) diff --git a/tests/data/acpi/aarch64/virt/DSDT b/tests/data/acpi/aarch64/virt/DSDT index 36d3e5d5a5e47359b6dcb3706f98b4f225677591..acab6e65febbc210158d4c39be0680bbb90250f5 100644 GIT binary patch delta 114 zcmX@3u}p)@CDC8kfX{JVpjp}Y(OkR^V=X2?C2#NTx$NL96vvg%MIJ<-! zF7RWWyjw`i#5kckflEGNfxx86nT)j{87>a6yzr#SK-xF~A|?P5D@Xu|Eacg2EG)?g E02*^2-2eap delta 152 zcmZ3caYlp7CDsj3DMT=F1tfxx86nT)j{wOkxv9b%Iv a!_+~91wg_DAjKf@CD{x4HuDNgG6Db)q$$w= diff --git a/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt b/tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt index e6154d0355f84fdcc51387b4db8f9ee63acae4e9..54c27e7d95b4956ed1b5dee0d299ccb08dc2a73e 100644 GIT binary patch delta 114 zcmZ3a`A37xCDa6yzr#SK-xF~A|?P5D@Xu|EachTDJ;nd E08cC+fdBvi delta 152 zcmeyPu}G84CDNm0-%U7NL4k6;F1TC3j`)j&Sb0wspaAT>kykX a8Kw>*EC3QN04WBEFUelWw>eo@k`Vyo+beYd diff --git a/tests/data/acpi/aarch64/virt/DSDT.memhp b/tests/data/acpi/aarch64/virt/DSDT.memhp index 33f011d6b635035a04c0b39ce9b4e219f7ae74b7..4330bc97cba0950191c45ac833533db7a190db81 100644 GIT binary patch delta 114 zcmbPh{N0GlCDFq==X{M&B8`a&on7k%!&gatO5EAiWkM|FDX6ediaCQkd zT;Rt#dAE?3iE%=80+)Qk0)a`BGZ||^GF%*BdErTufwXY~L`(oAR*(P^S;({5SU8If E0D^}hHvj+t delta 152 zcmexvG}oBRCDi~V&gatOkmCpr4G3W1a1IIbVGwcS zjQ0MXEN4;)N*lvb%;%x a3{wXY761tsfE0tomt-&G+srGR#RdSNPAe+_ diff --git a/tests/data/acpi/aarch64/virt/DSDT.pxb b/tests/data/acpi/aarch64/virt/DSDT.pxb index c0fdc6e9c1396cc2259dc4bc665ba023adcf4c9b..7fdbc03e2bf9fb7d35704779253de36e362f0bf9 100644 GIT binary patch delta 207 zcmexwz1f<}CDaVT thN*)H3xI?RK#D=)OR^X8ZSEJ!W@K`hy7`Z=9tZucpWH8GJNdfI9RN}mQ+EIW diff --git a/tests/data/acpi/aarch64/virt/DSDT.topology b/tests/data/acpi/aarch64/virt/DSDT.topology index 029d03eecc4efddc001e5377e85ac8e831294362..969b4f6560d3ae39f5b7e0064b7122905476fce8 100644 GIT binary patch delta 114 zcmbQH^+A)%CDa6yzr#SK-xF~A|?P5D@Xu|EachzS6Gq} E07m>E`k9CD@Gb delta 146 zcmX@0`%IV1CDH>8AMDK1 zmCfMn5^lJ_gMBg=kCvYiNJ#-u#2BQi8bol(gUAH}lO|^})`HY>ae#G*O_~f-2N4zk U2^WAAgT$9)FXY>t!4ts+03tFfeE;M1& diff --git a/tests/data/acpi/loongarch64/virt/DSDT.topology b/tests/data/acpi/loongarch64/virt/DSDT.topology index 65111aa822663a907b83487cb496be38a4bdff05..6dfbb495f88b74b87849b58473e46717bc588a56 100644 GIT binary patch delta 108 zcmX@Fwo;ADCDMXEN4;WVkrM^1_oQ18L&~h?oFKtRMj-vXEzT9&Z-_ DZM-3& delta 151 zcmca4{X?3|CDNm0-%U7NL4k6;F1TC3j`)j&Sb0wspaAT>kykX Z8Kw>*EC3QN04WBEFUelWH`#-?3jhyIER_HN diff --git a/tests/data/acpi/x86/microvm/DSDT.pcie b/tests/data/acpi/x86/microvm/DSDT.pcie index 8eacd21d6ecdf9a3cd3e4f03cf1b40748dcbf53e..ba258f454dc0e59ef2fd67e0ce37e270e7c122e8 100644 GIT binary patch delta 113 zcmX>vzEYgaCD#&HG5Ji`+{YxqAtd6%9`7IQ%+i(3;Or7^ zxWJEf@*XZN6XS&H1TMLR1p<>MXEN4;WVkrM^1_oQ18L&~h?oFKtRMj-vXE!83HNLO DNoOGe delta 151 zcmZ1}eqNl*CDpn4BhW?qd?*kmCpr4G3W1a1IIbVGwcS zjQ0|Z0pAkq&0Z_ykq^cT3aLI$n1p<>MXEN4;)N*lvb%;%x Z3{wXY761tsfE0tomt-&Go6N^O8vs+pD)j&W diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 8d9673cb5d..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,12 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/aarch64/virt/DSDT", -"tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt", -"tests/data/acpi/aarch64/virt/DSDT.memhp", -"tests/data/acpi/aarch64/virt/DSDT.pxb", -"tests/data/acpi/aarch64/virt/DSDT.topology", -"tests/data/acpi/loongarch64/virt/DSDT.memhp", -"tests/data/acpi/loongarch64/virt/DSDT.topology", -"tests/data/acpi/loongarch64/virt/DSDT.numamem", -"tests/data/acpi/loongarch64/virt/DSDT", -"tests/data/acpi/x86/microvm/DSDT.pcie", -"tests/data/acpi/riscv64/virt/DSDT", From c5dee204b0ae45c92b1a9640055ec068c6748b3c Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:55 +0200 Subject: [PATCH 2396/2760] hw/i386/acpi-build: Introduce build_append_pcihp_resources() helper Extract the code that reserves resources for ACPI PCI hotplug into a new helper named build_append_pcihp_resources() and move it to pcihp.c. We will reuse it on ARM. Signed-off-by: Eric Auger Reviewed-by: Gustavo Romero Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <20250714080639.2525563-12-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/pcihp.c | 18 ++++++++++++++++++ hw/i386/acpi-build.c | 15 ++------------- include/hw/acpi/pcihp.h | 2 ++ 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index cbe7e01385..5ca36c8619 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -685,6 +685,24 @@ void build_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr) aml_append(table, scope); } +/* Reserve PCIHP resources */ +void build_append_pcihp_resources(Aml *scope /* \\_SB.PCI0 */, + uint64_t io_addr, uint64_t io_len) +{ + Aml *dev, *crs; + + dev = aml_device("PHPR"); + aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06"))); + aml_append(dev, + aml_name_decl("_UID", aml_string("PCI Hotplug resources"))); + /* device present, functioning, decoding, not shown in UI */ + aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); + crs = aml_resource_template(); + aml_append(crs, aml_io(AML_DECODE16, io_addr, io_addr, 1, io_len)); + aml_append(dev, aml_name_decl("_CRS", crs)); + aml_append(scope, dev); +} + const VMStateDescription vmstate_acpi_pcihp_pci_status = { .name = "acpi_pcihp_pci_status", .version_id = 1, diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 91945f716c..52cef834ed 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1432,19 +1432,8 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, /* reserve PCIHP resources */ if (pm->pcihp_io_len && (pm->pcihp_bridge_en || pm->pcihp_root_en)) { - dev = aml_device("PHPR"); - aml_append(dev, aml_name_decl("_HID", aml_string("PNP0A06"))); - aml_append(dev, - aml_name_decl("_UID", aml_string("PCI Hotplug resources"))); - /* device present, functioning, decoding, not shown in UI */ - aml_append(dev, aml_name_decl("_STA", aml_int(0xB))); - crs = aml_resource_template(); - aml_append(crs, - aml_io(AML_DECODE16, pm->pcihp_io_base, pm->pcihp_io_base, 1, - pm->pcihp_io_len) - ); - aml_append(dev, aml_name_decl("_CRS", crs)); - aml_append(scope, dev); + build_append_pcihp_resources(scope, + pm->pcihp_io_base, pm->pcihp_io_len); } aml_append(dsdt, scope); diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index 971451e8ea..8a46a414cc 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -75,6 +75,8 @@ void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, void build_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr); void build_append_pci_dsm_func0_common(Aml *ctx, Aml *retvar); +void build_append_pcihp_resources(Aml *table, + uint64_t io_addr, uint64_t io_len); /* Called on reset */ void acpi_pcihp_reset(AcpiPciHpState *s); From b412e0557c03b9aac946133a443a76aa86c3f8b4 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:56 +0200 Subject: [PATCH 2397/2760] hw/acpi/pcihp: Add an AmlRegionSpace arg to build_acpi_pci_hotplug On ARM we will put the operation regions in AML_SYSTEM_MEMORY. So let's allow this configuration. Signed-off-by: Eric Auger Reviewed-by: Gustavo Romero Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <20250714080639.2525563-13-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/pcihp.c | 8 ++++---- hw/i386/acpi-build.c | 4 ++-- include/hw/acpi/pcihp.h | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 5ca36c8619..afa3ec5f4d 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -629,7 +629,7 @@ static Aml *aml_pci_pdsm(void) return method; } -void build_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr) +void build_acpi_pci_hotplug(Aml *table, AmlRegionSpace rs, uint64_t pcihp_addr) { Aml *scope; Aml *field; @@ -638,21 +638,21 @@ void build_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr) scope = aml_scope("_SB.PCI0"); aml_append(scope, - aml_operation_region("PCST", AML_SYSTEM_IO, aml_int(pcihp_addr), 0x08)); + aml_operation_region("PCST", rs, aml_int(pcihp_addr), 0x08)); field = aml_field("PCST", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); aml_append(field, aml_named_field("PCIU", 32)); aml_append(field, aml_named_field("PCID", 32)); aml_append(scope, field); aml_append(scope, - aml_operation_region("SEJ", AML_SYSTEM_IO, + aml_operation_region("SEJ", rs, aml_int(pcihp_addr + ACPI_PCIHP_SEJ_BASE), 0x04)); field = aml_field("SEJ", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); aml_append(field, aml_named_field("B0EJ", 32)); aml_append(scope, field); aml_append(scope, - aml_operation_region("BNMR", AML_SYSTEM_IO, + aml_operation_region("BNMR", rs, aml_int(pcihp_addr + ACPI_PCIHP_BNMR_BASE), 0x08)); field = aml_field("BNMR", AML_DWORD_ACC, AML_NOLOCK, AML_WRITE_AS_ZEROS); aml_append(field, aml_named_field("BNUM", 32)); diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 52cef834ed..6ca2b34ef8 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1172,7 +1172,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, sb_scope); if (pm->pcihp_bridge_en || pm->pcihp_root_en) { - build_acpi_pci_hotplug(dsdt, pm->pcihp_io_base); + build_acpi_pci_hotplug(dsdt, AML_SYSTEM_IO, pm->pcihp_io_base); } build_piix4_pci0_int(dsdt); } else if (q35) { @@ -1216,7 +1216,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dsdt, sb_scope); if (pm->pcihp_bridge_en) { - build_acpi_pci_hotplug(dsdt, pm->pcihp_io_base); + build_acpi_pci_hotplug(dsdt, AML_SYSTEM_IO, pm->pcihp_io_base); } build_q35_pci0_int(dsdt); } diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index 8a46a414cc..253ac6e483 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -28,6 +28,7 @@ #define HW_ACPI_PCIHP_H #include "hw/acpi/acpi.h" +#include "hw/acpi/aml-build.h" #include "hw/hotplug.h" #define ACPI_PCIHP_IO_BASE_PROP "acpi-pcihp-io-base" @@ -73,7 +74,7 @@ void acpi_pcihp_device_unplug_request_cb(HotplugHandler *hotplug_dev, AcpiPciHpState *s, DeviceState *dev, Error **errp); -void build_acpi_pci_hotplug(Aml *table, uint64_t pcihp_addr); +void build_acpi_pci_hotplug(Aml *table, AmlRegionSpace rs, uint64_t pcihp_addr); void build_append_pci_dsm_func0_common(Aml *ctx, Aml *retvar); void build_append_pcihp_resources(Aml *table, uint64_t io_addr, uint64_t io_len); From d962f199c7e8625c91e8d1cc54a0323180e6f3e1 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:57 +0200 Subject: [PATCH 2398/2760] hw/i386/acpi-build: Move build_append_notification_callback to pcihp We plan to reuse build_append_notification_callback() on ARM so let's move it to pcihp.c. No functional change intended. Signed-off-by: Eric Auger Reviewed-by: Gustavo Romero Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <20250714080639.2525563-14-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/pcihp.c | 58 +++++++++++++++++++++++++++++++++++++++++ hw/i386/acpi-build.c | 58 ----------------------------------------- include/hw/acpi/pcihp.h | 1 + 3 files changed, 59 insertions(+), 58 deletions(-) diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index afa3ec5f4d..b64d06afc9 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -39,6 +39,7 @@ #include "migration/vmstate.h" #include "qapi/error.h" #include "qom/qom-qobject.h" +#include "qobject/qnum.h" #include "trace.h" #define ACPI_PCIHP_SIZE 0x0018 @@ -703,6 +704,63 @@ void build_append_pcihp_resources(Aml *scope /* \\_SB.PCI0 */, aml_append(scope, dev); } +bool build_append_notification_callback(Aml *parent_scope, const PCIBus *bus) +{ + Aml *method; + PCIBus *sec; + QObject *bsel; + int nr_notifiers = 0; + GQueue *pcnt_bus_list = g_queue_new(); + + QLIST_FOREACH(sec, &bus->child, sibling) { + Aml *br_scope = aml_scope("S%.02X", sec->parent_dev->devfn); + if (pci_bus_is_root(sec)) { + continue; + } + nr_notifiers = nr_notifiers + + build_append_notification_callback(br_scope, sec); + /* + * add new child scope to parent + * and keep track of bus that have PCNT, + * bus list is used later to call children PCNTs from this level PCNT + */ + if (nr_notifiers) { + g_queue_push_tail(pcnt_bus_list, sec); + aml_append(parent_scope, br_scope); + } + } + + /* + * Append PCNT method to notify about events on local and child buses. + * ps: hostbridge might not have hotplug (bsel) enabled but might have + * child bridges that do have bsel. + */ + method = aml_method("PCNT", 0, AML_NOTSERIALIZED); + + /* If bus supports hotplug select it and notify about local events */ + bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); + if (bsel) { + uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); + + aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM"))); + aml_append(method, aml_call2("DVNT", aml_name("PCIU"), + aml_int(1))); /* Device Check */ + aml_append(method, aml_call2("DVNT", aml_name("PCID"), + aml_int(3))); /* Eject Request */ + nr_notifiers++; + } + + /* Notify about child bus events in any case */ + while ((sec = g_queue_pop_head(pcnt_bus_list))) { + aml_append(method, aml_name("^S%.02X.PCNT", sec->parent_dev->devfn)); + } + + aml_append(parent_scope, method); + qobject_unref(bsel); + g_queue_free(pcnt_bus_list); + return !!nr_notifiers; +} + const VMStateDescription vmstate_acpi_pcihp_pci_status = { .name = "acpi_pcihp_pci_status", .version_id = 1, diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 6ca2b34ef8..3275675e60 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -563,64 +563,6 @@ void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus) } } -static bool build_append_notification_callback(Aml *parent_scope, - const PCIBus *bus) -{ - Aml *method; - PCIBus *sec; - QObject *bsel; - int nr_notifiers = 0; - GQueue *pcnt_bus_list = g_queue_new(); - - QLIST_FOREACH(sec, &bus->child, sibling) { - Aml *br_scope = aml_scope("S%.02X", sec->parent_dev->devfn); - if (pci_bus_is_root(sec)) { - continue; - } - nr_notifiers = nr_notifiers + - build_append_notification_callback(br_scope, sec); - /* - * add new child scope to parent - * and keep track of bus that have PCNT, - * bus list is used later to call children PCNTs from this level PCNT - */ - if (nr_notifiers) { - g_queue_push_tail(pcnt_bus_list, sec); - aml_append(parent_scope, br_scope); - } - } - - /* - * Append PCNT method to notify about events on local and child buses. - * ps: hostbridge might not have hotplug (bsel) enabled but might have - * child bridges that do have bsel. - */ - method = aml_method("PCNT", 0, AML_NOTSERIALIZED); - - /* If bus supports hotplug select it and notify about local events */ - bsel = object_property_get_qobject(OBJECT(bus), ACPI_PCIHP_PROP_BSEL, NULL); - if (bsel) { - uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); - - aml_append(method, aml_store(aml_int(bsel_val), aml_name("BNUM"))); - aml_append(method, aml_call2("DVNT", aml_name("PCIU"), - aml_int(1))); /* Device Check */ - aml_append(method, aml_call2("DVNT", aml_name("PCID"), - aml_int(3))); /* Eject Request */ - nr_notifiers++; - } - - /* Notify about child bus events in any case */ - while ((sec = g_queue_pop_head(pcnt_bus_list))) { - aml_append(method, aml_name("^S%.02X.PCNT", sec->parent_dev->devfn)); - } - - aml_append(parent_scope, method); - qobject_unref(bsel); - g_queue_free(pcnt_bus_list); - return !!nr_notifiers; -} - /* * build_prt - Define interrupt routing rules * diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index 253ac6e483..f4fd44cb32 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -78,6 +78,7 @@ void build_acpi_pci_hotplug(Aml *table, AmlRegionSpace rs, uint64_t pcihp_addr); void build_append_pci_dsm_func0_common(Aml *ctx, Aml *retvar); void build_append_pcihp_resources(Aml *table, uint64_t io_addr, uint64_t io_len); +bool build_append_notification_callback(Aml *parent_scope, const PCIBus *bus); /* Called on reset */ void acpi_pcihp_reset(AcpiPciHpState *s); From e87ab64e8f614ae8702209dd801ac08ca5897ff1 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:58 +0200 Subject: [PATCH 2399/2760] hw/i386/acpi-build: Move build_append_pci_bus_devices/pcihp_slots to pcihp We intend to reuse build_append_pci_bus_devices and build_append_pcihp_slots on ARM. So let's move them to hw/acpi/pcihp.c as well as all static helpers they use. No functional change intended. Signed-off-by: Eric Auger Reviewed-by: Gustavo Romero Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <20250714080639.2525563-15-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/pcihp.c | 173 ++++++++++++++++++++++++++++++++++++++++ hw/i386/acpi-build.c | 172 --------------------------------------- include/hw/acpi/pci.h | 1 - include/hw/acpi/pcihp.h | 2 + 4 files changed, 175 insertions(+), 173 deletions(-) diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index b64d06afc9..2c76edeb15 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -27,6 +27,7 @@ #include "qemu/osdep.h" #include "hw/acpi/pcihp.h" #include "hw/acpi/aml-build.h" +#include "hw/acpi/acpi_aml_interface.h" #include "hw/pci-host/i440fx.h" #include "hw/pci/pci.h" #include "hw/pci/pci_bridge.h" @@ -761,6 +762,178 @@ bool build_append_notification_callback(Aml *parent_scope, const PCIBus *bus) return !!nr_notifiers; } +static Aml *aml_pci_device_dsm(void) +{ + Aml *method; + + method = aml_method("_DSM", 4, AML_SERIALIZED); + { + Aml *params = aml_local(0); + Aml *pkg = aml_package(2); + aml_append(pkg, aml_int(0)); + aml_append(pkg, aml_int(0)); + aml_append(method, aml_store(pkg, params)); + aml_append(method, + aml_store(aml_name("BSEL"), aml_index(params, aml_int(0)))); + aml_append(method, + aml_store(aml_name("ASUN"), aml_index(params, aml_int(1)))); + aml_append(method, + aml_return(aml_call5("PDSM", aml_arg(0), aml_arg(1), + aml_arg(2), aml_arg(3), params)) + ); + } + return method; +} + +static Aml *aml_pci_static_endpoint_dsm(PCIDevice *pdev) +{ + Aml *method; + + g_assert(pdev->acpi_index != 0); + method = aml_method("_DSM", 4, AML_SERIALIZED); + { + Aml *params = aml_local(0); + Aml *pkg = aml_package(1); + aml_append(pkg, aml_int(pdev->acpi_index)); + aml_append(method, aml_store(pkg, params)); + aml_append(method, + aml_return(aml_call5("EDSM", aml_arg(0), aml_arg(1), + aml_arg(2), aml_arg(3), params)) + ); + } + return method; +} + +static void build_append_pcihp_notify_entry(Aml *method, int slot) +{ + Aml *if_ctx; + int32_t devfn = PCI_DEVFN(slot, 0); + + if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL)); + aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1))); + aml_append(method, if_ctx); +} + +static bool is_devfn_ignored_generic(const int devfn, const PCIBus *bus) +{ + const PCIDevice *pdev = bus->devices[devfn]; + + if (PCI_FUNC(devfn)) { + if (IS_PCI_BRIDGE(pdev)) { + /* + * Ignore only hotplugged PCI bridges on !0 functions, but + * allow describing cold plugged bridges on all functions + */ + if (DEVICE(pdev)->hotplugged) { + return true; + } + } + } + return false; +} + +static bool is_devfn_ignored_hotplug(const int devfn, const PCIBus *bus) +{ + PCIDevice *pdev = bus->devices[devfn]; + if (pdev) { + return is_devfn_ignored_generic(devfn, bus) || + !DEVICE_GET_CLASS(pdev)->hotpluggable || + /* Cold plugged bridges aren't themselves hot-pluggable */ + (IS_PCI_BRIDGE(pdev) && !DEVICE(pdev)->hotplugged); + } else { /* non populated slots */ + /* + * hotplug is supported only for non-multifunction device + * so generate device description only for function 0 + */ + if (PCI_FUNC(devfn) || + (pci_bus_is_express(bus) && PCI_SLOT(devfn) > 0)) { + return true; + } + } + return false; +} + +void build_append_pcihp_slots(Aml *parent_scope, PCIBus *bus) +{ + int devfn; + Aml *dev, *notify_method = NULL, *method; + QObject *bsel = object_property_get_qobject(OBJECT(bus), + ACPI_PCIHP_PROP_BSEL, NULL); + uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); + qobject_unref(bsel); + + aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val))); + notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED); + + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + int slot = PCI_SLOT(devfn); + int adr = slot << 16 | PCI_FUNC(devfn); + + if (is_devfn_ignored_hotplug(devfn, bus)) { + continue; + } + + if (bus->devices[devfn]) { + dev = aml_scope("S%.02X", devfn); + } else { + dev = aml_device("S%.02X", devfn); + aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); + } + + /* + * Can't declare _SUN here for every device as it changes 'slot' + * enumeration order in linux kernel, so use another variable for it + */ + aml_append(dev, aml_name_decl("ASUN", aml_int(slot))); + aml_append(dev, aml_pci_device_dsm()); + + aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); + /* add _EJ0 to make slot hotpluggable */ + method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); + aml_append(method, + aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) + ); + aml_append(dev, method); + + build_append_pcihp_notify_entry(notify_method, slot); + + /* device descriptor has been composed, add it into parent context */ + aml_append(parent_scope, dev); + } + aml_append(parent_scope, notify_method); +} + +void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus) +{ + int devfn; + Aml *dev; + + for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { + /* ACPI spec: 1.0b: Table 6-2 _ADR Object Bus Types, PCI type */ + int adr = PCI_SLOT(devfn) << 16 | PCI_FUNC(devfn); + PCIDevice *pdev = bus->devices[devfn]; + + if (!pdev || is_devfn_ignored_generic(devfn, bus)) { + continue; + } + + /* start to compose PCI device descriptor */ + dev = aml_device("S%.02X", devfn); + aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); + + call_dev_aml_func(DEVICE(bus->devices[devfn]), dev); + /* add _DSM if device has acpi-index set */ + if (pdev->acpi_index && + !object_property_get_bool(OBJECT(pdev), "hotpluggable", + &error_abort)) { + aml_append(dev, aml_pci_static_endpoint_dsm(pdev)); + } + + /* device descriptor has been composed, add it into parent context */ + aml_append(parent_scope, dev); + } +} + const VMStateDescription vmstate_acpi_pcihp_pci_status = { .name = "acpi_pcihp_pci_status", .version_id = 1, diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 3275675e60..fe8bc62c03 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -338,29 +338,6 @@ build_facs(GArray *table_data) g_array_append_vals(table_data, reserved, 40); /* Reserved */ } -static Aml *aml_pci_device_dsm(void) -{ - Aml *method; - - method = aml_method("_DSM", 4, AML_SERIALIZED); - { - Aml *params = aml_local(0); - Aml *pkg = aml_package(2); - aml_append(pkg, aml_int(0)); - aml_append(pkg, aml_int(0)); - aml_append(method, aml_store(pkg, params)); - aml_append(method, - aml_store(aml_name("BSEL"), aml_index(params, aml_int(0)))); - aml_append(method, - aml_store(aml_name("ASUN"), aml_index(params, aml_int(1)))); - aml_append(method, - aml_return(aml_call5("PDSM", aml_arg(0), aml_arg(1), - aml_arg(2), aml_arg(3), params)) - ); - } - return method; -} - static Aml *aml_pci_edsm(void) { Aml *method, *ifctx; @@ -414,155 +391,6 @@ static Aml *aml_pci_edsm(void) return method; } -static Aml *aml_pci_static_endpoint_dsm(PCIDevice *pdev) -{ - Aml *method; - - g_assert(pdev->acpi_index != 0); - method = aml_method("_DSM", 4, AML_SERIALIZED); - { - Aml *params = aml_local(0); - Aml *pkg = aml_package(1); - aml_append(pkg, aml_int(pdev->acpi_index)); - aml_append(method, aml_store(pkg, params)); - aml_append(method, - aml_return(aml_call5("EDSM", aml_arg(0), aml_arg(1), - aml_arg(2), aml_arg(3), params)) - ); - } - return method; -} - -static void build_append_pcihp_notify_entry(Aml *method, int slot) -{ - Aml *if_ctx; - int32_t devfn = PCI_DEVFN(slot, 0); - - if_ctx = aml_if(aml_and(aml_arg(0), aml_int(0x1U << slot), NULL)); - aml_append(if_ctx, aml_notify(aml_name("S%.02X", devfn), aml_arg(1))); - aml_append(method, if_ctx); -} - -static bool is_devfn_ignored_generic(const int devfn, const PCIBus *bus) -{ - const PCIDevice *pdev = bus->devices[devfn]; - - if (PCI_FUNC(devfn)) { - if (IS_PCI_BRIDGE(pdev)) { - /* - * Ignore only hotplugged PCI bridges on !0 functions, but - * allow describing cold plugged bridges on all functions - */ - if (DEVICE(pdev)->hotplugged) { - return true; - } - } - } - return false; -} - -static bool is_devfn_ignored_hotplug(const int devfn, const PCIBus *bus) -{ - PCIDevice *pdev = bus->devices[devfn]; - if (pdev) { - return is_devfn_ignored_generic(devfn, bus) || - !DEVICE_GET_CLASS(pdev)->hotpluggable || - /* Cold plugged bridges aren't themselves hot-pluggable */ - (IS_PCI_BRIDGE(pdev) && !DEVICE(pdev)->hotplugged); - } else { /* non populated slots */ - /* - * hotplug is supported only for non-multifunction device - * so generate device description only for function 0 - */ - if (PCI_FUNC(devfn) || - (pci_bus_is_express(bus) && PCI_SLOT(devfn) > 0)) { - return true; - } - } - return false; -} - -void build_append_pcihp_slots(Aml *parent_scope, PCIBus *bus) -{ - int devfn; - Aml *dev, *notify_method = NULL, *method; - QObject *bsel = object_property_get_qobject(OBJECT(bus), - ACPI_PCIHP_PROP_BSEL, NULL); - uint64_t bsel_val = qnum_get_uint(qobject_to(QNum, bsel)); - qobject_unref(bsel); - - aml_append(parent_scope, aml_name_decl("BSEL", aml_int(bsel_val))); - notify_method = aml_method("DVNT", 2, AML_NOTSERIALIZED); - - for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - int slot = PCI_SLOT(devfn); - int adr = slot << 16 | PCI_FUNC(devfn); - - if (is_devfn_ignored_hotplug(devfn, bus)) { - continue; - } - - if (bus->devices[devfn]) { - dev = aml_scope("S%.02X", devfn); - } else { - dev = aml_device("S%.02X", devfn); - aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); - } - - /* - * Can't declare _SUN here for every device as it changes 'slot' - * enumeration order in linux kernel, so use another variable for it - */ - aml_append(dev, aml_name_decl("ASUN", aml_int(slot))); - aml_append(dev, aml_pci_device_dsm()); - - aml_append(dev, aml_name_decl("_SUN", aml_int(slot))); - /* add _EJ0 to make slot hotpluggable */ - method = aml_method("_EJ0", 1, AML_NOTSERIALIZED); - aml_append(method, - aml_call2("PCEJ", aml_name("BSEL"), aml_name("_SUN")) - ); - aml_append(dev, method); - - build_append_pcihp_notify_entry(notify_method, slot); - - /* device descriptor has been composed, add it into parent context */ - aml_append(parent_scope, dev); - } - aml_append(parent_scope, notify_method); -} - -void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus) -{ - int devfn; - Aml *dev; - - for (devfn = 0; devfn < ARRAY_SIZE(bus->devices); devfn++) { - /* ACPI spec: 1.0b: Table 6-2 _ADR Object Bus Types, PCI type */ - int adr = PCI_SLOT(devfn) << 16 | PCI_FUNC(devfn); - PCIDevice *pdev = bus->devices[devfn]; - - if (!pdev || is_devfn_ignored_generic(devfn, bus)) { - continue; - } - - /* start to compose PCI device descriptor */ - dev = aml_device("S%.02X", devfn); - aml_append(dev, aml_name_decl("_ADR", aml_int(adr))); - - call_dev_aml_func(DEVICE(bus->devices[devfn]), dev); - /* add _DSM if device has acpi-index set */ - if (pdev->acpi_index && - !object_property_get_bool(OBJECT(pdev), "hotpluggable", - &error_abort)) { - aml_append(dev, aml_pci_static_endpoint_dsm(pdev)); - } - - /* device descriptor has been composed, add it into parent context */ - aml_append(parent_scope, dev); - } -} - /* * build_prt - Define interrupt routing rules * diff --git a/include/hw/acpi/pci.h b/include/hw/acpi/pci.h index 8a328b580c..69bae95eac 100644 --- a/include/hw/acpi/pci.h +++ b/include/hw/acpi/pci.h @@ -37,7 +37,6 @@ typedef struct AcpiMcfgInfo { void build_mcfg(GArray *table_data, BIOSLinker *linker, AcpiMcfgInfo *info, const char *oem_id, const char *oem_table_id); -void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus); void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope); void build_srat_generic_affinity_structures(GArray *table_data); diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index f4fd44cb32..5506a58862 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -80,6 +80,8 @@ void build_append_pcihp_resources(Aml *table, uint64_t io_addr, uint64_t io_len); bool build_append_notification_callback(Aml *parent_scope, const PCIBus *bus); +void build_append_pci_bus_devices(Aml *parent_scope, PCIBus *bus); + /* Called on reset */ void acpi_pcihp_reset(AcpiPciHpState *s); From b9dc4e04c807ad3860a9ba8773b3b0e7057c31aa Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:04:59 +0200 Subject: [PATCH 2400/2760] hw/i386/acpi-build: Use AcpiPciHpState::root in acpi_set_pci_info pcihp acpi_set_pci_info() generic code currently uses acpi_get_i386_pci_host() to retrieve the pci host bridge. To make it work also on ARM we get rid of that call and directly use AcpiPciHpState::root. Signed-off-by: Eric Auger Suggested-by: Igor Mammedov Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <20250714080639.2525563-16-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/pcihp.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 2c76edeb15..2db2f16940 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -99,10 +99,10 @@ static void *acpi_set_bsel(PCIBus *bus, void *opaque) return info; } -static void acpi_set_pci_info(bool has_bridge_hotplug) +static void acpi_set_pci_info(AcpiPciHpState *s) { static bool bsel_is_set; - Object *host = acpi_get_i386_pci_host(); + bool has_bridge_hotplug = s->use_acpi_hotplug_bridge; PCIBus *bus; BSELInfo info = { .bsel_alloc = ACPI_PCIHP_BSEL_DEFAULT, .has_bridge_hotplug = has_bridge_hotplug }; @@ -112,11 +112,8 @@ static void acpi_set_pci_info(bool has_bridge_hotplug) } bsel_is_set = true; - if (!host) { - return; - } - bus = PCI_HOST_BRIDGE(host)->bus; + bus = s->root; if (bus) { /* Scan all PCI buses. Set property to enable acpi based hotplug. */ pci_for_each_bus_depth_first(bus, acpi_set_bsel, NULL, &info); @@ -266,7 +263,7 @@ static void acpi_pcihp_update(AcpiPciHpState *s) void acpi_pcihp_reset(AcpiPciHpState *s) { - acpi_set_pci_info(s->use_acpi_hotplug_bridge); + acpi_set_pci_info(s); acpi_pcihp_update(s); } From bc8f29df27e68b0476360526634097608171e783 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:00 +0200 Subject: [PATCH 2401/2760] hw/i386/acpi-build: Move aml_pci_edsm to a generic place Move aml_pci_edsm to pci-bridge.c since we want to reuse that for ARM and acpi-index support. Also rename it into build_pci_bridge_edsm. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-17-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/pci-bridge.c | 54 ++++++++++++++++++++++++++++++++++++++++ hw/i386/acpi-build.c | 57 ++----------------------------------------- include/hw/acpi/pci.h | 1 + 3 files changed, 57 insertions(+), 55 deletions(-) diff --git a/hw/acpi/pci-bridge.c b/hw/acpi/pci-bridge.c index 7baa7034a1..394a919479 100644 --- a/hw/acpi/pci-bridge.c +++ b/hw/acpi/pci-bridge.c @@ -35,3 +35,57 @@ void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope) } } } + +Aml *build_pci_bridge_edsm(void) +{ + Aml *method, *ifctx; + Aml *zero = aml_int(0); + Aml *func = aml_arg(2); + Aml *ret = aml_local(0); + Aml *aidx = aml_local(1); + Aml *params = aml_arg(4); + + method = aml_method("EDSM", 5, AML_SERIALIZED); + + /* get supported functions */ + ifctx = aml_if(aml_equal(func, zero)); + { + /* 1: have supported functions */ + /* 7: support for function 7 */ + const uint8_t caps = 1 | BIT(7); + build_append_pci_dsm_func0_common(ifctx, ret); + aml_append(ifctx, aml_store(aml_int(caps), aml_index(ret, zero))); + aml_append(ifctx, aml_return(ret)); + } + aml_append(method, ifctx); + + /* handle specific functions requests */ + /* + * PCI Firmware Specification 3.1 + * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under + * Operating Systems + */ + ifctx = aml_if(aml_equal(func, aml_int(7))); + { + Aml *pkg = aml_package(2); + aml_append(pkg, zero); + /* optional, if not impl. should return null string */ + aml_append(pkg, aml_string("%s", "")); + aml_append(ifctx, aml_store(pkg, ret)); + + /* + * IASL is fine when initializing Package with computational data, + * however it makes guest unhappy /it fails to process such AML/. + * So use runtime assignment to set acpi-index after initializer + * to make OSPM happy. + */ + aml_append(ifctx, + aml_store(aml_derefof(aml_index(params, aml_int(0))), aidx)); + aml_append(ifctx, aml_store(aidx, aml_index(ret, zero))); + aml_append(ifctx, aml_return(ret)); + } + aml_append(method, ifctx); + + return method; +} + diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index fe8bc62c03..423c4959fe 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -338,59 +338,6 @@ build_facs(GArray *table_data) g_array_append_vals(table_data, reserved, 40); /* Reserved */ } -static Aml *aml_pci_edsm(void) -{ - Aml *method, *ifctx; - Aml *zero = aml_int(0); - Aml *func = aml_arg(2); - Aml *ret = aml_local(0); - Aml *aidx = aml_local(1); - Aml *params = aml_arg(4); - - method = aml_method("EDSM", 5, AML_SERIALIZED); - - /* get supported functions */ - ifctx = aml_if(aml_equal(func, zero)); - { - /* 1: have supported functions */ - /* 7: support for function 7 */ - const uint8_t caps = 1 | BIT(7); - build_append_pci_dsm_func0_common(ifctx, ret); - aml_append(ifctx, aml_store(aml_int(caps), aml_index(ret, zero))); - aml_append(ifctx, aml_return(ret)); - } - aml_append(method, ifctx); - - /* handle specific functions requests */ - /* - * PCI Firmware Specification 3.1 - * 4.6.7. _DSM for Naming a PCI or PCI Express Device Under - * Operating Systems - */ - ifctx = aml_if(aml_equal(func, aml_int(7))); - { - Aml *pkg = aml_package(2); - aml_append(pkg, zero); - /* optional, if not impl. should return null string */ - aml_append(pkg, aml_string("%s", "")); - aml_append(ifctx, aml_store(pkg, ret)); - - /* - * IASL is fine when initializing Package with computational data, - * however it makes guest unhappy /it fails to process such AML/. - * So use runtime assignment to set acpi-index after initializer - * to make OSPM happy. - */ - aml_append(ifctx, - aml_store(aml_derefof(aml_index(params, aml_int(0))), aidx)); - aml_append(ifctx, aml_store(aidx, aml_index(ret, zero))); - aml_append(ifctx, aml_return(ret)); - } - aml_append(method, ifctx); - - return method; -} - /* * build_prt - Define interrupt routing rules * @@ -937,7 +884,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, dev = aml_device("PCI0"); aml_append(dev, aml_name_decl("_HID", aml_eisaid("PNP0A03"))); aml_append(dev, aml_name_decl("_UID", aml_int(pcmc->pci_root_uid))); - aml_append(dev, aml_pci_edsm()); + aml_append(dev, build_pci_bridge_edsm()); aml_append(sb_scope, dev); aml_append(dsdt, sb_scope); @@ -952,7 +899,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_CID", aml_eisaid("PNP0A03"))); aml_append(dev, aml_name_decl("_UID", aml_int(pcmc->pci_root_uid))); aml_append(dev, build_pci_host_bridge_osc_method(!pm->pcihp_bridge_en)); - aml_append(dev, aml_pci_edsm()); + aml_append(dev, build_pci_bridge_edsm()); aml_append(sb_scope, dev); if (mcfg_valid) { aml_append(sb_scope, build_q35_dram_controller(&mcfg)); diff --git a/include/hw/acpi/pci.h b/include/hw/acpi/pci.h index 69bae95eac..20b672575f 100644 --- a/include/hw/acpi/pci.h +++ b/include/hw/acpi/pci.h @@ -42,5 +42,6 @@ void build_pci_bridge_aml(AcpiDevAmlIf *adev, Aml *scope); void build_srat_generic_affinity_structures(GArray *table_data); Aml *build_pci_host_bridge_osc_method(bool enable_native_pcie_hotplug); +Aml *build_pci_bridge_edsm(void); #endif From c82a9d740758644493bbb32711237c41070babb8 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:01 +0200 Subject: [PATCH 2402/2760] qtest/bios-tables-test: Prepare for fixing the aarch64 viot test The test misses a variant and this puts the mess on subsequent rebuild-expected-aml.sh where a first DSDT reference blob is overriden by another one. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-18-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/aarch64/virt/DSDT.viot | 0 tests/qtest/bios-tables-test-allowed-diff.h | 1 + 2 files changed, 1 insertion(+) create mode 100644 tests/data/acpi/aarch64/virt/DSDT.viot diff --git a/tests/data/acpi/aarch64/virt/DSDT.viot b/tests/data/acpi/aarch64/virt/DSDT.viot new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..7a74beab3d 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/DSDT.viot", From 070c0892d528d66651cb2f3cc6a63eaa13c01ddf Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:02 +0200 Subject: [PATCH 2403/2760] qtest/bios-tables-test: Add a variant to the aarch64 viot test Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-19-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index e988deac02..4701975c05 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -2275,6 +2275,7 @@ static void test_acpi_aarch64_virt_viot(void) test_data data = { .machine = "virt", .arch = "aarch64", + .variant = ".viot", .tcg_only = true, .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", From c34115091fab11342370c7af51589ba24d5cece9 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:03 +0200 Subject: [PATCH 2404/2760] qtest/bios-tables-test: Generate DSDT.viot Use a specific DSDT.viot reference blob instead of relying on the default DSDT blob. The content is unchanged. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-20-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/aarch64/virt/DSDT.viot | Bin 0 -> 5158 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/aarch64/virt/DSDT.viot b/tests/data/acpi/aarch64/virt/DSDT.viot index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..acab6e65febbc210158d4c39be0680bbb90250f5 100644 GIT binary patch literal 5158 zcmZvg%WoT16o>EFlh__V;#b}er%)oEksasN(3aR^Cvk}r<4GH&RB|K)qGS_^3aKEZ zG>Zb+kw~nAls^QC6%q?}ELgMQA7F<$ckWE`JIWa;_MEx*e0RR_aeQVPUSn^rs8r&DKBexU;lbN-4ds-?J~*t*1S&-a9y*aLeU#ytmbAq-$=cMZt=S_a3(z>fm(E z-ED_s-S$?wT&V_|Dj*0|}ml07ht8;{2_7)U{sN@C^Bq`5eLR1GL}TffW&Zsv4o6ekufMS zQovY7#)`;@NsJ6IR*7!Qyk*J(y#OabE|GUPfPk{C0<_y8Glon|G* zEHKuQA=hb6V!Q^78ZzWM%}b2Cz^EfbuG50Vm;;6jj0L$)ixT5aU^I{+*XgjtcpDf^ zWXN?oA~D_t#s)IvIvtf5^T60dhFqs(5~B=^Eo8`bIxaC5fYCyRT&EKfV-XmSkRjLU zq{LVT#)rs|>vT$DtO8>j8FHOYON=#Ow1H8T>vTq9+y}-EGUPffNsNcU*bNvhd;7W} z>g;sNZh7On`RGv3lW~%MpYysupQyKv6$g7>*Ru{zeN=Ed3`I={qV5VPbL<-RNS{^k z?O`b0j3Da`fn|uo)jeNxlV0of zo$Kr9?R9e{XR4!I)l#3TSyiUL2JL;No~cjhU1^Blrgge|h3zk$lYG)n_WMeIHp=W< zv|myAls@i6^yLYICh<<^_??qKO$Z=I7DWFw~jE_vcbiY32rz&J3qg z!>CuJseUGfIx}1)>Su;$5LG%eoEkNmG}X_fP-oH^`K#D|W_Sj+pBYZ21~ahqGbu#R z0p>a2XNH~wCNn&F3ej_rc~T2c=Mzq)h7>(1M9&!Wj4{s`_oNU#geW}d^`lS1?yVV)z* zlWqvmlS1?yWuBwVbCi2hh@NyS4xVGolWri;lS1?yXP)ECbDVoph@KP7bAox&4F`Hs zh@O+obCP*Za!(4;bBcLRF;BWdK~DviY3tLdrOw`g9Dn#kkLk^iZeH~97pk}&>+O23eRibW#(w2;XW-)V zT*1j6wEwF9Tbf$`Y5#fcmj{1-c6Q|0G+}|>QrNTVuON5%Jg4Xp#NM4PVtM-!k{FE_s*MZ>04e{cki+#Okr9^ZJ?FX0y>V z&mPPJw~B6(=z&|B26W$Z-B3oYw@m6uvr9b`eOB>=V;{Us>d-N7s Date: Mon, 14 Jul 2025 10:05:04 +0200 Subject: [PATCH 2405/2760] tests/qtest/bios-tables-test: Prepare for changes in the arm virt DSDT table This commit adds DSDT blobs to the whilelist in the prospect to allow changes in the arm virt DSDT method. Signed-off-by: Gustavo Romero Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-21-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..023fbc6059 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,7 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/DSDT", +"tests/data/acpi/aarch64/virt/DSDT.acpihmatvirt", +"tests/data/acpi/aarch64/virt/DSDT.memhp", +"tests/data/acpi/aarch64/virt/DSDT.pxb", +"tests/data/acpi/aarch64/virt/DSDT.topology", +"tests/data/acpi/aarch64/virt/DSDT.viot", From ff692ee7bcb506d318c7a279abf570b1f220a3da Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:05 +0200 Subject: [PATCH 2406/2760] hw/arm/virt-acpi-build: Let non hotplug ports support static acpi-index hw/arm/virt-acpi-build: Let non hotplug ports support static acpi-index Add the requested ACPI bits requested to support static acpi-index for non hotplug ports. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-22-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/Kconfig | 2 ++ hw/arm/virt-acpi-build.c | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/hw/arm/Kconfig b/hw/arm/Kconfig index 1634e26fcc..2aa4b5d778 100644 --- a/hw/arm/Kconfig +++ b/hw/arm/Kconfig @@ -34,6 +34,8 @@ config ARM_VIRT select ACPI_HW_REDUCED select ACPI_APEI select ACPI_VIOT + select ACPI_PCIHP + select ACPI_PCI_BRIDGE select VIRTIO_MEM_SUPPORTED select ACPI_CXL select ACPI_HMAT diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index f3bad69aa7..3dc1cfcd67 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -34,6 +34,7 @@ #include "hw/core/cpu.h" #include "hw/acpi/acpi-defs.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/pcihp.h" #include "hw/nvram/fw_cfg_acpi.h" #include "hw/acpi/bios-linker-loader.h" #include "hw/acpi/aml-build.h" @@ -906,6 +907,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) const int *irqmap = vms->irqmap; AcpiTable table = { .sig = "DSDT", .rev = 2, .oem_id = vms->oem_id, .oem_table_id = vms->oem_table_id }; + Aml *pci0_scope; acpi_table_begin(&table, table_data); dsdt = init_aml_allocator(); @@ -959,6 +961,16 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) aml_append(dsdt, scope); + pci0_scope = aml_scope("\\_SB.PCI0"); + + aml_append(pci0_scope, build_pci_bridge_edsm()); + build_append_pci_bus_devices(pci0_scope, vms->bus); + if (object_property_find(OBJECT(vms->bus), ACPI_PCIHP_PROP_BSEL)) { + build_append_pcihp_slots(pci0_scope, vms->bus); + } + + aml_append(dsdt, pci0_scope); + /* copy AML table into ACPI tables blob */ g_array_append_vals(table_data, dsdt->buf->data, dsdt->buf->len); From 4dfb1418278a43f3acbf177dfcafe955da8ffaf6 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:06 +0200 Subject: [PATCH 2407/2760] tests/qtest/bios-tables-test: Update ARM DSDT reference blobs Changes relate to the introduction of pieces related to acpi-index static support along with root ports with no hotplug. + + Scope (\_SB.PCI0) + { + Method (EDSM, 5, Serialized) + { + If ((Arg2 == Zero)) + { + Local0 = Buffer (One) + { + 0x00 // . + } + If ((Arg0 != ToUUID ("e5c937d0-3553-4d7a-9117-ea4d19c3434d") /* Device Labeling Interface */)) + { + Return (Local0) + } + + If ((Arg1 < 0x02)) + { + Return (Local0) + } + + Local0 [Zero] = 0x81 + Return (Local0) + } + + If ((Arg2 == 0x07)) + { + Local0 = Package (0x02) + { + Zero, + "" + } + Local1 = DerefOf (Arg4 [Zero]) + Local0 [Zero] = Local1 + Return (Local0) + } + } + + Device (S00) + { + Name (_ADR, Zero) // _ADR: Address + } + + Device (S08) + { + Name (_ADR, 0x00010000) // _ADR: Address + } + + Device (S10) + { + Name (_ADR, 0x00020000) // _ADR: Address + } + } } Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-23-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/aarch64/virt/DSDT | Bin 5158 -> 5293 bytes .../data/acpi/aarch64/virt/DSDT.acpihmatvirt | Bin 5244 -> 5379 bytes tests/data/acpi/aarch64/virt/DSDT.memhp | Bin 6519 -> 6654 bytes tests/data/acpi/aarch64/virt/DSDT.pxb | Bin 7603 -> 7768 bytes tests/data/acpi/aarch64/virt/DSDT.topology | Bin 5360 -> 5495 bytes tests/data/acpi/aarch64/virt/DSDT.viot | Bin 5158 -> 5310 bytes tests/qtest/bios-tables-test-allowed-diff.h | 6 ------ 7 files changed, 6 deletions(-) diff --git a/tests/data/acpi/aarch64/virt/DSDT b/tests/data/acpi/aarch64/virt/DSDT index acab6e65febbc210158d4c39be0680bbb90250f5..18d97e8f22979411a528705c0e314acb424bbfa5 100644 GIT binary patch delta 156 zcmZ3cu~w7ICDC8qh84&Fq|YTuA^&WCm0K diff --git a/tests/data/acpi/aarch64/virt/DSDT.pxb b/tests/data/acpi/aarch64/virt/DSDT.pxb index 7fdbc03e2bf9fb7d35704779253de36e362f0bf9..c2779882494e16920787b8ab7b4cb3c3b70f224b 100644 GIT binary patch delta 168 zcmdmNeZz*!CDYJ1{M$w4+8@uPyj`iKiCke6e!EY003kaE9L+I delta 19 acmca%v)P)HJ2nXCn0iZX7Xs@xe~<0nVNVBHpa7F2TOM3(O{GF%$?g zGcqJBkeW0(Lr|DY;DY(dr@^LGz7xe?`AQyk_Fa;&fPHEv7t@l20D? delta 19 acmeya^+A)%CDC8qh84& Date: Mon, 14 Jul 2025 10:05:07 +0200 Subject: [PATCH 2408/2760] hw/arm/virt-acpi-build: Modify the DSDT ACPI table to enable ACPI PCI hotplug Modify the DSDT ACPI table to enable ACPI PCI hotplug. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-24-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/pcihp.c | 1 - hw/arm/virt-acpi-build.c | 17 +++++++++++++++++ hw/arm/virt.c | 2 ++ include/hw/acpi/pcihp.h | 2 ++ include/hw/arm/virt.h | 1 + 5 files changed, 22 insertions(+), 1 deletion(-) diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index 2db2f16940..f1594e664a 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -43,7 +43,6 @@ #include "qobject/qnum.h" #include "trace.h" -#define ACPI_PCIHP_SIZE 0x0018 #define PCI_UP_BASE 0x0000 #define PCI_DOWN_BASE 0x0004 #define PCI_EJ_BASE 0x0008 diff --git a/hw/arm/virt-acpi-build.c b/hw/arm/virt-acpi-build.c index 3dc1cfcd67..b01fc4f8ef 100644 --- a/hw/arm/virt-acpi-build.c +++ b/hw/arm/virt-acpi-build.c @@ -969,6 +969,23 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, VirtMachineState *vms) build_append_pcihp_slots(pci0_scope, vms->bus); } + if (vms->acpi_dev) { + bool acpi_pcihp; + + acpi_pcihp = object_property_get_bool(OBJECT(vms->acpi_dev), + ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, + NULL); + + if (acpi_pcihp) { + build_acpi_pci_hotplug(dsdt, AML_SYSTEM_MEMORY, + memmap[VIRT_ACPI_PCIHP].base); + build_append_pcihp_resources(pci0_scope, + memmap[VIRT_ACPI_PCIHP].base, + memmap[VIRT_ACPI_PCIHP].size); + + build_append_notification_callback(pci0_scope, vms->bus); + } + } aml_append(dsdt, pci0_scope); /* copy AML table into ACPI tables blob */ diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 8070ff7b11..817adedb31 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -76,6 +76,7 @@ #include "standard-headers/linux/input.h" #include "hw/arm/smmuv3.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/pcihp.h" #include "target/arm/cpu-qom.h" #include "target/arm/internals.h" #include "target/arm/multiprocessing.h" @@ -186,6 +187,7 @@ static const MemMapEntry base_memmap[] = { [VIRT_NVDIMM_ACPI] = { 0x09090000, NVDIMM_ACPI_IO_LEN}, [VIRT_PVTIME] = { 0x090a0000, 0x00010000 }, [VIRT_SECURE_GPIO] = { 0x090b0000, 0x00001000 }, + [VIRT_ACPI_PCIHP] = { 0x090c0000, ACPI_PCIHP_SIZE }, [VIRT_MMIO] = { 0x0a000000, 0x00000200 }, /* ...repeating for a total of NUM_VIRTIO_TRANSPORTS, each of that size */ [VIRT_PLATFORM_BUS] = { 0x0c000000, 0x02000000 }, diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index 5506a58862..9ff548650b 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -38,6 +38,8 @@ #define ACPI_PCIHP_SEJ_BASE 0x8 #define ACPI_PCIHP_BNMR_BASE 0x10 +#define ACPI_PCIHP_SIZE 0x0018 + typedef struct AcpiPciHpPciStatus { uint32_t up; uint32_t down; diff --git a/include/hw/arm/virt.h b/include/hw/arm/virt.h index 4375819ea0..365a28b082 100644 --- a/include/hw/arm/virt.h +++ b/include/hw/arm/virt.h @@ -80,6 +80,7 @@ enum { VIRT_ACPI_GED, VIRT_NVDIMM_ACPI, VIRT_PVTIME, + VIRT_ACPI_PCIHP, VIRT_LOWMEMMAP_LAST, }; From 7b3e231f0902c6aec5fd0ed38b84eb33fb7271e0 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:08 +0200 Subject: [PATCH 2409/2760] hw/acpi/ged: Add a bus link property This property will be set by the machine code on the object creation. It will be used by acpi pcihp hotplug code. Signed-off-by: Eric Auger Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <20250714080639.2525563-25-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/generic_event_device.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 7831db412b..ef1c1ec51f 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -13,6 +13,7 @@ #include "qapi/error.h" #include "hw/acpi/acpi.h" #include "hw/acpi/generic_event_device.h" +#include "hw/pci/pci.h" #include "hw/irq.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" @@ -320,6 +321,8 @@ static const Property acpi_ged_properties[] = { DEFINE_PROP_UINT32("ged-event", AcpiGedState, ged_event_bitmap, 0), DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, AcpiGedState, pcihp_state.use_acpi_hotplug_bridge, 0), + DEFINE_PROP_LINK("bus", AcpiGedState, pcihp_state.root, + TYPE_PCI_BUS, PCIBus *), }; static const VMStateDescription vmstate_memhp_state = { From 1913b8e68670223cfe8101c332a3b903031fc4d2 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:09 +0200 Subject: [PATCH 2410/2760] hw/arm/virt: Pass the bus on the ged creation The bus will be needed on ged realize for acpi pci hp setup. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-26-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 817adedb31..41b5086b55 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -701,6 +701,7 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", event); + object_property_set_link(OBJECT(dev), "bus", OBJECT(vms->bus), &error_abort); sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_ACPI_GED].base); From 8c58dc0531d868ea706a68114c22601cdf4c4e67 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:10 +0200 Subject: [PATCH 2411/2760] hw/acpi/ged: Call pcihp plug callbacks in hotplug handler implementation Add PCI device related code in the TYPE_HOTPLUG_HANDLER implementation. For a PCI device hotplug/hotunplug event, the code routes to acpi_pcihp_device callbacks (pre_plug_cb, plug_cb, unplug_request_cb, unplug_cb). Signed-off-by: Eric Auger Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <20250714080639.2525563-27-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/generic_event_device.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index ef1c1ec51f..92b931758f 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -17,6 +17,7 @@ #include "hw/irq.h" #include "hw/mem/pc-dimm.h" #include "hw/mem/nvdimm.h" +#include "hw/pci/pci_device.h" #include "hw/qdev-properties.h" #include "migration/vmstate.h" #include "qemu/error-report.h" @@ -228,6 +229,14 @@ static const MemoryRegionOps ged_regs_ops = { }, }; +static void acpi_ged_device_pre_plug_cb(HotplugHandler *hotplug_dev, + DeviceState *dev, Error **errp) +{ + if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + acpi_pcihp_device_pre_plug_cb(hotplug_dev, dev, errp); + } +} + static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, DeviceState *dev, Error **errp) { @@ -241,6 +250,8 @@ static void acpi_ged_device_plug_cb(HotplugHandler *hotplug_dev, } } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { acpi_cpu_plug_cb(hotplug_dev, &s->cpuhp_state, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + acpi_pcihp_device_plug_cb(hotplug_dev, &s->pcihp_state, dev, errp); } else { error_setg(errp, "virt: device plug request for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -257,6 +268,9 @@ static void acpi_ged_unplug_request_cb(HotplugHandler *hotplug_dev, acpi_memory_unplug_request_cb(hotplug_dev, &s->memhp_state, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { acpi_cpu_unplug_request_cb(hotplug_dev, &s->cpuhp_state, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + acpi_pcihp_device_unplug_request_cb(hotplug_dev, &s->pcihp_state, + dev, errp); } else { error_setg(errp, "acpi: device unplug request for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -272,6 +286,8 @@ static void acpi_ged_unplug_cb(HotplugHandler *hotplug_dev, acpi_memory_unplug_cb(&s->memhp_state, dev, errp); } else if (object_dynamic_cast(OBJECT(dev), TYPE_CPU)) { acpi_cpu_unplug_cb(&s->cpuhp_state, dev, errp); + } else if (object_dynamic_cast(OBJECT(dev), TYPE_PCI_DEVICE)) { + acpi_pcihp_device_unplug_cb(hotplug_dev, &s->pcihp_state, dev, errp); } else { error_setg(errp, "acpi: device unplug for unsupported device" " type: %s", object_get_typename(OBJECT(dev))); @@ -485,6 +501,7 @@ static void acpi_ged_class_init(ObjectClass *class, const void *data) dc->vmsd = &vmstate_acpi_ged; dc->realize = acpi_ged_realize; + hc->pre_plug = acpi_ged_device_pre_plug_cb; hc->plug = acpi_ged_device_plug_cb; hc->unplug_request = acpi_ged_unplug_request_cb; hc->unplug = acpi_ged_unplug_cb; From b8c7ebbb70a316c8ea4a2a31eb071456bcc350b5 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:11 +0200 Subject: [PATCH 2412/2760] hw/acpi/pcihp: Remove root arg in acpi_pcihp_init Let pass the root bus to ich9 and piix4 through a property link instead of through an argument passed to acpi_pcihp_init(). Also make sure the root bus is set at the entry of acpi_pcihp_init(). The rationale of that change is to be consistent with the forecoming ARM implementation where the machine passes the root bus (steming from GPEX) to the GED device through a link property. Signed-off-by: Eric Auger Suggested-by: Igor Mammedov Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-28-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/acpi-pci-hotplug-stub.c | 2 +- hw/acpi/ich9.c | 7 ++++++- hw/acpi/pcihp.c | 4 ++-- hw/acpi/piix4.c | 5 ++++- include/hw/acpi/pcihp.h | 2 +- 5 files changed, 14 insertions(+), 6 deletions(-) diff --git a/hw/acpi/acpi-pci-hotplug-stub.c b/hw/acpi/acpi-pci-hotplug-stub.c index b7bc6e40a1..d58ea726a8 100644 --- a/hw/acpi/acpi-pci-hotplug-stub.c +++ b/hw/acpi/acpi-pci-hotplug-stub.c @@ -4,7 +4,7 @@ const VMStateDescription vmstate_acpi_pcihp_pci_status; -void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, +void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, MemoryRegion *address_space_io, uint16_t io_base) { } diff --git a/hw/acpi/ich9.c b/hw/acpi/ich9.c index 967b67485e..2b3b493c01 100644 --- a/hw/acpi/ich9.c +++ b/hw/acpi/ich9.c @@ -322,9 +322,10 @@ void ich9_pm_init(PCIDevice *lpc_pci, ICH9LPCPMRegs *pm, qemu_irq sci_irq) } if (pm->acpi_pci_hotplug.use_acpi_hotplug_bridge) { + object_property_set_link(OBJECT(lpc_pci), "bus", + OBJECT(pci_get_bus(lpc_pci)), &error_abort); acpi_pcihp_init(OBJECT(lpc_pci), &pm->acpi_pci_hotplug, - pci_get_bus(lpc_pci), pci_address_space_io(lpc_pci), ACPI_PCIHP_ADDR_ICH9); @@ -428,6 +429,10 @@ void ich9_pm_add_properties(Object *obj, ICH9LPCPMRegs *pm) object_property_add_uint32_ptr(obj, ACPI_PM_PROP_PM_IO_BASE, &pm->pm_io_base, OBJ_PROP_FLAG_READ); + object_property_add_link(obj, "bus", TYPE_PCI_BUS, + (Object **)&pm->acpi_pci_hotplug.root, + object_property_allow_set_link, + OBJ_PROP_LINK_STRONG); object_property_add(obj, ACPI_PM_PROP_GPE0_BLK, "uint32", ich9_pm_get_gpe0_blk, NULL, NULL, pm); diff --git a/hw/acpi/pcihp.c b/hw/acpi/pcihp.c index f1594e664a..4922bbc778 100644 --- a/hw/acpi/pcihp.c +++ b/hw/acpi/pcihp.c @@ -493,13 +493,13 @@ static const MemoryRegionOps acpi_pcihp_io_ops = { }, }; -void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, PCIBus *root_bus, +void acpi_pcihp_init(Object *owner, AcpiPciHpState *s, MemoryRegion *io, uint16_t io_base) { s->io_len = ACPI_PCIHP_SIZE; s->io_base = io_base; - s->root = root_bus; + assert(s->root); memory_region_init_io(&s->io, owner, &acpi_pcihp_io_ops, s, "acpi-pci-hotplug", s->io_len); diff --git a/hw/acpi/piix4.c b/hw/acpi/piix4.c index d98b80df6d..7a18f18dda 100644 --- a/hw/acpi/piix4.c +++ b/hw/acpi/piix4.c @@ -567,7 +567,8 @@ static void piix4_acpi_system_hot_add_init(MemoryRegion *parent, if (s->acpi_pci_hotplug.use_acpi_hotplug_bridge || s->acpi_pci_hotplug.use_acpi_root_pci_hotplug) { - acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, bus, parent, + object_property_set_link(OBJECT(s), "bus", OBJECT(bus), &error_abort); + acpi_pcihp_init(OBJECT(s), &s->acpi_pci_hotplug, parent, ACPI_PCIHP_ADDR_PIIX4); qbus_set_hotplug_handler(BUS(pci_get_bus(PCI_DEVICE(s))), OBJECT(s)); } @@ -611,6 +612,8 @@ static const Property piix4_pm_properties[] = { acpi_pci_hotplug.use_acpi_hotplug_bridge, true), DEFINE_PROP_BOOL(ACPI_PM_PROP_ACPI_PCI_ROOTHP, PIIX4PMState, acpi_pci_hotplug.use_acpi_root_pci_hotplug, true), + DEFINE_PROP_LINK("bus", PIIX4PMState, acpi_pci_hotplug.root, + TYPE_PCI_BUS, PCIBus *), DEFINE_PROP_BOOL("memory-hotplug-support", PIIX4PMState, acpi_memory_hotplug.is_enabled, true), DEFINE_PROP_BOOL("smm-compat", PIIX4PMState, smm_compat, false), diff --git a/include/hw/acpi/pcihp.h b/include/hw/acpi/pcihp.h index 9ff548650b..ca6a258825 100644 --- a/include/hw/acpi/pcihp.h +++ b/include/hw/acpi/pcihp.h @@ -62,7 +62,7 @@ typedef struct AcpiPciHpState { bool use_acpi_root_pci_hotplug; } AcpiPciHpState; -void acpi_pcihp_init(Object *owner, AcpiPciHpState *, PCIBus *root, +void acpi_pcihp_init(Object *owner, AcpiPciHpState *, MemoryRegion *io, uint16_t io_base); bool acpi_pcihp_is_hotpluggable_bus(AcpiPciHpState *s, BusState *bus); From 1816a337a126281de43aabb4b55689f69ee876bc Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:12 +0200 Subject: [PATCH 2413/2760] hw/acpi/ged: Prepare the device to react to PCI hotplug events QEMU will notify the OS about PCI hotplug/hotunplug events through GED interrupts. Let the GED device handle a new PCI hotplug event. On its occurrence it calls the \\_SB.PCI0.PCNT method with the BLCK mutex held. The GED device uses a dedicated MMIO region that will be mapped by the machine code. At this point the GED still does not support PCI device hotplug in its TYPE_HOTPLUG_HANDLER implementation. This will come in a subsequent patch. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-29-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/generic_event_device.c | 35 ++++++++++++++++++++++++++ include/hw/acpi/generic_event_device.h | 14 ++++++++++- 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 92b931758f..7535d07737 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -12,6 +12,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" #include "hw/acpi/acpi.h" +#include "hw/acpi/pcihp.h" #include "hw/acpi/generic_event_device.h" #include "hw/pci/pci.h" #include "hw/irq.h" @@ -28,6 +29,7 @@ static const uint32_t ged_supported_events[] = { ACPI_GED_PWR_DOWN_EVT, ACPI_GED_NVDIMM_HOTPLUG_EVT, ACPI_GED_CPU_HOTPLUG_EVT, + ACPI_GED_PCI_HOTPLUG_EVT, }; /* @@ -123,6 +125,12 @@ void build_ged_aml(Aml *table, const char *name, HotplugHandler *hotplug_dev, aml_notify(aml_name("\\_SB.NVDR"), aml_int(0x80))); break; + case ACPI_GED_PCI_HOTPLUG_EVT: + aml_append(if_ctx, + aml_acquire(aml_name("\\_SB.PCI0.BLCK"), 0xFFFF)); + aml_append(if_ctx, aml_call0("\\_SB.PCI0.PCNT")); + aml_append(if_ctx, aml_release(aml_name("\\_SB.PCI0.BLCK"))); + break; default: /* * Please make sure all the events in ged_supported_events[] @@ -316,6 +324,8 @@ static void acpi_ged_send_event(AcpiDeviceIf *adev, AcpiEventStatusBits ev) sel = ACPI_GED_NVDIMM_HOTPLUG_EVT; } else if (ev & ACPI_CPU_HOTPLUG_STATUS) { sel = ACPI_GED_CPU_HOTPLUG_EVT; + } else if (ev & ACPI_PCI_HOTPLUG_STATUS) { + sel = ACPI_GED_PCI_HOTPLUG_EVT; } else { /* Unknown event. Return without generating interrupt. */ warn_report("GED: Unsupported event %d. No irq injected", ev); @@ -427,9 +437,13 @@ static void acpi_ged_realize(DeviceState *dev, Error **errp) { SysBusDevice *sbd = SYS_BUS_DEVICE(dev); AcpiGedState *s = ACPI_GED(dev); + AcpiPciHpState *pcihp_state = &s->pcihp_state; uint32_t ged_events; int i; + if (pcihp_state->use_acpi_hotplug_bridge) { + s->ged_event_bitmap |= ACPI_GED_PCI_HOTPLUG_EVT; + } ged_events = ctpop32(s->ged_event_bitmap); for (i = 0; i < ARRAY_SIZE(ged_supported_events) && ged_events; i++) { @@ -449,6 +463,13 @@ static void acpi_ged_realize(DeviceState *dev, Error **errp) cpu_hotplug_hw_init(&s->container_cpuhp, OBJECT(dev), &s->cpuhp_state, 0); break; + case ACPI_GED_PCI_HOTPLUG_EVT: + memory_region_init(&s->container_pcihp, OBJECT(dev), + ACPI_PCIHP_REGION_NAME, ACPI_PCIHP_SIZE); + sysbus_init_mmio(sbd, &s->container_pcihp); + acpi_pcihp_init(OBJECT(s), &s->pcihp_state, + &s->container_pcihp, 0); + qbus_set_hotplug_handler(BUS(s->pcihp_state.root), OBJECT(dev)); } ged_events--; } @@ -490,11 +511,22 @@ static void acpi_ged_initfn(Object *obj) sysbus_init_mmio(sbd, &ged_st->regs); } +static void ged_reset_hold(Object *obj, ResetType type) +{ + AcpiGedState *s = ACPI_GED(obj); + + if (s->pcihp_state.use_acpi_hotplug_bridge) { + acpi_pcihp_reset(&s->pcihp_state); + } +} + static void acpi_ged_class_init(ObjectClass *class, const void *data) { DeviceClass *dc = DEVICE_CLASS(class); HotplugHandlerClass *hc = HOTPLUG_HANDLER_CLASS(class); AcpiDeviceIfClass *adevc = ACPI_DEVICE_IF_CLASS(class); + ResettableClass *rc = RESETTABLE_CLASS(class); + AcpiGedClass *gedc = ACPI_GED_CLASS(class); dc->desc = "ACPI Generic Event Device"; device_class_set_props(dc, acpi_ged_properties); @@ -505,6 +537,8 @@ static void acpi_ged_class_init(ObjectClass *class, const void *data) hc->plug = acpi_ged_device_plug_cb; hc->unplug_request = acpi_ged_unplug_request_cb; hc->unplug = acpi_ged_unplug_cb; + resettable_class_set_parent_phases(rc, NULL, ged_reset_hold, NULL, + &gedc->parent_phases); adevc->ospm_status = acpi_ged_ospm_status; adevc->send_event = acpi_ged_send_event; @@ -516,6 +550,7 @@ static const TypeInfo acpi_ged_info = { .instance_size = sizeof(AcpiGedState), .instance_init = acpi_ged_initfn, .class_init = acpi_ged_class_init, + .class_size = sizeof(AcpiGedClass), .interfaces = (const InterfaceInfo[]) { { TYPE_HOTPLUG_HANDLER }, { TYPE_ACPI_DEVICE_IF }, diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index f5ffa67a39..d56adaa626 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -69,7 +69,7 @@ #define ACPI_POWER_BUTTON_DEVICE "PWRB" #define TYPE_ACPI_GED "acpi-ged" -OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) +OBJECT_DECLARE_TYPE(AcpiGedState, AcpiGedClass, ACPI_GED) #define ACPI_GED_EVT_SEL_OFFSET 0x0 #define ACPI_GED_EVT_SEL_LEN 0x4 @@ -102,6 +102,7 @@ OBJECT_DECLARE_SIMPLE_TYPE(AcpiGedState, ACPI_GED) #define ACPI_GED_PWR_DOWN_EVT 0x2 #define ACPI_GED_NVDIMM_HOTPLUG_EVT 0x4 #define ACPI_GED_CPU_HOTPLUG_EVT 0x8 +#define ACPI_GED_PCI_HOTPLUG_EVT 0x10 typedef struct GEDState { MemoryRegion evt; @@ -109,6 +110,8 @@ typedef struct GEDState { uint32_t sel; } GEDState; +#define ACPI_PCIHP_REGION_NAME "pcihp container" + struct AcpiGedState { SysBusDevice parent_obj; MemHotplugState memhp_state; @@ -116,12 +119,21 @@ struct AcpiGedState { CPUHotplugState cpuhp_state; MemoryRegion container_cpuhp; AcpiPciHpState pcihp_state; + MemoryRegion container_pcihp; GEDState ged_state; uint32_t ged_event_bitmap; qemu_irq irq; AcpiGhesState ghes_state; }; +typedef struct AcpiGedClass { + /* */ + SysBusDeviceClass parent_class; + + /*< public >*/ + ResettablePhases parent_phases; +} AcpiGedClass; + void build_ged_aml(Aml *table, const char* name, HotplugHandler *hotplug_dev, uint32_t ged_irq, AmlRegionSpace rs, hwaddr ged_base); void acpi_dsdt_add_power_button(Aml *scope); From 745315784db4315644421f5def482ff1324503bd Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:13 +0200 Subject: [PATCH 2414/2760] hw/acpi/ged: Support migration of AcpiPciHpState Add a subsection to migrate the AcpiPciHpState state. Signed-off-by: Eric Auger Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Reviewed-by: Prasad Pandit Message-Id: <20250714080639.2525563-30-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/generic_event_device.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/hw/acpi/generic_event_device.c b/hw/acpi/generic_event_device.c index 7535d07737..95682b79a2 100644 --- a/hw/acpi/generic_event_device.c +++ b/hw/acpi/generic_event_device.c @@ -417,6 +417,25 @@ static const VMStateDescription vmstate_ghes_state = { } }; +static bool pcihp_needed(void *opaque) +{ + AcpiGedState *s = opaque; + return s->pcihp_state.use_acpi_hotplug_bridge; +} + +static const VMStateDescription vmstate_pcihp_state = { + .name = "acpi-ged/pcihp", + .version_id = 1, + .minimum_version_id = 1, + .needed = pcihp_needed, + .fields = (const VMStateField[]) { + VMSTATE_PCI_HOTPLUG(pcihp_state, + AcpiGedState, + NULL, NULL), + VMSTATE_END_OF_LIST() + } +}; + static const VMStateDescription vmstate_acpi_ged = { .name = "acpi-ged", .version_id = 1, @@ -429,6 +448,7 @@ static const VMStateDescription vmstate_acpi_ged = { &vmstate_memhp_state, &vmstate_cpuhp_state, &vmstate_ghes_state, + &vmstate_pcihp_state, NULL } }; From 1a102856652268cf2ec4cbd2cb9b229ef920c31d Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:14 +0200 Subject: [PATCH 2415/2760] hw/core/sysbus: Introduce sysbus_mmio_map_name() helper Some sysbus devices have conditional mmio regions. This happens for instance with the hw/acpi/ged device. In that case it becomes difficult to predict which index a specific MMIO region corresponds to when one needs to mmio map the region. Introduce a new helper that takes the name of the region instead of its index. If the region is not found this returns -1. Otherwise it maps the corresponding index and returns this latter. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-31-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/core/sysbus.c | 11 +++++++++++ include/hw/sysbus.h | 1 + 2 files changed, 12 insertions(+) diff --git a/hw/core/sysbus.c b/hw/core/sysbus.c index e71367adfb..ec69e877a2 100644 --- a/hw/core/sysbus.c +++ b/hw/core/sysbus.c @@ -151,6 +151,17 @@ void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr) sysbus_mmio_map_common(dev, n, addr, false, 0); } +int sysbus_mmio_map_name(SysBusDevice *dev, const char *name, hwaddr addr) +{ + for (int i = 0; i < dev->num_mmio; i++) { + if (!strcmp(dev->mmio[i].memory->name, name)) { + sysbus_mmio_map(dev, i, addr); + return i; + } + } + return -1; +} + void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr, int priority) { diff --git a/include/hw/sysbus.h b/include/hw/sysbus.h index 7dc88aaa27..18fde8a7b4 100644 --- a/include/hw/sysbus.h +++ b/include/hw/sysbus.h @@ -82,6 +82,7 @@ void sysbus_connect_irq(SysBusDevice *dev, int n, qemu_irq irq); bool sysbus_is_irq_connected(SysBusDevice *dev, int n); qemu_irq sysbus_get_connected_irq(SysBusDevice *dev, int n); void sysbus_mmio_map(SysBusDevice *dev, int n, hwaddr addr); +int sysbus_mmio_map_name(SysBusDevice *dev, const char*name, hwaddr addr); void sysbus_mmio_map_overlap(SysBusDevice *dev, int n, hwaddr addr, int priority); From cfd4ace6e5faf3064defa559e1b5fd674109186f Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:15 +0200 Subject: [PATCH 2416/2760] hw/arm/virt: Minor code reshuffling in create_acpi_ged Use a local SysBusDevice handle. Also use the newly introduced sysbus_mmio_map_name which brings better readability about the region being mapped. GED device has regions which exist depending on some external properties and it becomes difficult to guess the index of a region. Better refer to a region by its name. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-32-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt.c | 11 +++++++---- include/hw/acpi/generic_event_device.h | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index 41b5086b55..f127a668ef 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -688,6 +688,7 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) { DeviceState *dev; MachineState *ms = MACHINE(vms); + SysBusDevice *sbdev; int irq = vms->irqmap[VIRT_ACPI_GED]; uint32_t event = ACPI_GED_PWR_DOWN_EVT; @@ -702,11 +703,13 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) dev = qdev_new(TYPE_ACPI_GED); qdev_prop_set_uint32(dev, "ged-event", event); object_property_set_link(OBJECT(dev), "bus", OBJECT(vms->bus), &error_abort); - sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal); + sbdev = SYS_BUS_DEVICE(dev); + sysbus_realize_and_unref(sbdev, &error_fatal); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 0, vms->memmap[VIRT_ACPI_GED].base); - sysbus_mmio_map(SYS_BUS_DEVICE(dev), 1, vms->memmap[VIRT_PCDIMM_ACPI].base); - sysbus_connect_irq(SYS_BUS_DEVICE(dev), 0, qdev_get_gpio_in(vms->gic, irq)); + sysbus_mmio_map_name(sbdev, TYPE_ACPI_GED, vms->memmap[VIRT_ACPI_GED].base); + sysbus_mmio_map_name(sbdev, ACPI_MEMHP_REGION_NAME, + vms->memmap[VIRT_PCDIMM_ACPI].base); + sysbus_connect_irq(sbdev, 0, qdev_get_gpio_in(vms->gic, irq)); return dev; } diff --git a/include/hw/acpi/generic_event_device.h b/include/hw/acpi/generic_event_device.h index d56adaa626..2c5b055327 100644 --- a/include/hw/acpi/generic_event_device.h +++ b/include/hw/acpi/generic_event_device.h @@ -111,6 +111,7 @@ typedef struct GEDState { } GEDState; #define ACPI_PCIHP_REGION_NAME "pcihp container" +#define ACPI_MEMHP_REGION_NAME "memhp container" struct AcpiGedState { SysBusDevice parent_obj; From 6af97d127eb40bd8a3b25db86d3644b25287be51 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:16 +0200 Subject: [PATCH 2417/2760] hw/arm/virt: Let virt support pci hotplug/unplug GED event Set up the IO registers used to communicate between QEMU and ACPI. Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-33-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/arm/virt.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/hw/arm/virt.c b/hw/arm/virt.c index f127a668ef..ef6be3660f 100644 --- a/hw/arm/virt.c +++ b/hw/arm/virt.c @@ -691,6 +691,7 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) SysBusDevice *sbdev; int irq = vms->irqmap[VIRT_ACPI_GED]; uint32_t event = ACPI_GED_PWR_DOWN_EVT; + bool acpi_pcihp; if (ms->ram_slots) { event |= ACPI_GED_MEM_HOTPLUG_EVT; @@ -709,6 +710,18 @@ static inline DeviceState *create_acpi_ged(VirtMachineState *vms) sysbus_mmio_map_name(sbdev, TYPE_ACPI_GED, vms->memmap[VIRT_ACPI_GED].base); sysbus_mmio_map_name(sbdev, ACPI_MEMHP_REGION_NAME, vms->memmap[VIRT_PCDIMM_ACPI].base); + + acpi_pcihp = object_property_get_bool(OBJECT(dev), + ACPI_PM_PROP_ACPI_PCIHP_BRIDGE, NULL); + + if (acpi_pcihp) { + int pcihp_region_index; + + pcihp_region_index = sysbus_mmio_map_name(sbdev, ACPI_PCIHP_REGION_NAME, + vms->memmap[VIRT_ACPI_PCIHP].base); + assert(pcihp_region_index >= 0); + } + sysbus_connect_irq(sbdev, 0, qdev_get_gpio_in(vms->gic, irq)); return dev; From 099ea5daeae0b1acb386b9e3c99838798cb0f690 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Mon, 14 Jul 2025 10:05:17 +0200 Subject: [PATCH 2418/2760] tests/qtest/bios-tables-test: Prepare for addition of acpi pci hp tests Soon we will introduce new tests related to ACPI PCI hotplug and acpi-index that will use a new reference blob: tests/data/acpi/aarch64/virt/DSDT.acpipcihp tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex Signed-off-by: Gustavo Romero Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-34-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/aarch64/virt/DSDT.acpipcihp | 0 tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex | 0 tests/qtest/bios-tables-test-allowed-diff.h | 2 ++ 3 files changed, 2 insertions(+) create mode 100644 tests/data/acpi/aarch64/virt/DSDT.acpipcihp create mode 100644 tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex diff --git a/tests/data/acpi/aarch64/virt/DSDT.acpipcihp b/tests/data/acpi/aarch64/virt/DSDT.acpipcihp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex b/tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..02f4f0b29f 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,3 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/DSDT.acpipcihp", +"tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex", From 72140ff2a14545f3c7835c8531cb51255b652d2e Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Mon, 14 Jul 2025 10:05:18 +0200 Subject: [PATCH 2419/2760] tests/qtest/bios-tables-test: Add aarch64 ACPI PCI hotplug test Add 2 new tests: - test_acpi_aarch64_virt_acpi_pci_hotplug tests the acpi pci hotplug using -global acpi-ged.acpi-pci-hotplug-with-bridge-support=on - test_acpi_aarch64_virt_pcie_root_port_hpoff tests static-acpi index on a root port with disabled hotplug Signed-off-by: Gustavo Romero Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-35-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test.c | 52 ++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 4701975c05..6aec68decc 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -1643,6 +1643,54 @@ static void test_acpi_aarch64_virt_tcg_memhp(void) } +static void test_acpi_aarch64_virt_acpi_pci_hotplug(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 256ULL * MiB, + .variant = ".acpipcihp", + }; + + /* Use ACPI PCI Hotplug */ + test_acpi_one(" -global acpi-ged.acpi-pci-hotplug-with-bridge-support=on" + " -cpu cortex-a57" + " -device pcie-root-port,id=pcie.1,bus=pcie.0,chassis=0,slot=1,addr=7.0" + " -device pci-testdev,bus=pcie.1", + &data); + + free_test_data(&data); +} + +static void test_acpi_aarch64_virt_pcie_root_port_hpoff(void) +{ + test_data data = { + .machine = "virt", + .arch = "aarch64", + .tcg_only = true, + .uefi_fl1 = "pc-bios/edk2-aarch64-code.fd", + .uefi_fl2 = "pc-bios/edk2-arm-vars.fd", + .cd = "tests/data/uefi-boot-images/bios-tables-test.aarch64.iso.qcow2", + .ram_start = 0x40000000ULL, + .scan_len = 256ULL * MiB, + .variant = ".hpoffacpiindex", + }; + + /* turn hotplug off on the pcie-root-port and use static acpi-index*/ + test_acpi_one(" -device pcie-root-port,id=pcie.1,chassis=0," + "slot=1,hotplug=off,addr=7.0" + " -device pci-testdev,bus=pcie.1,acpi-index=12" + " -cpu cortex-a57", + &data); + + free_test_data(&data); +} + static void test_acpi_microvm_prepare(test_data *data) { data->machine = "microvm"; @@ -2708,6 +2756,10 @@ int main(int argc, char *argv[]) qtest_add_func("acpi/virt/numamem", test_acpi_aarch64_virt_tcg_numamem); qtest_add_func("acpi/virt/memhp", test_acpi_aarch64_virt_tcg_memhp); + qtest_add_func("acpi/virt/acpipcihp", + test_acpi_aarch64_virt_acpi_pci_hotplug); + qtest_add_func("acpi/virt/hpoffacpiindex", + test_acpi_aarch64_virt_pcie_root_port_hpoff); qtest_add_func("acpi/virt/pxb", test_acpi_aarch64_virt_tcg_pxb); qtest_add_func("acpi/virt/oem-fields", test_acpi_aarch64_virt_oem_fields); From 84dfc6c07495052c16886f3bf279342fc30e99b0 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 14 Jul 2025 10:05:19 +0200 Subject: [PATCH 2420/2760] qtest/bios-tables-test: Generate reference blob for DSDT.hpoffacpiindex The disassembled DSDT table is given below * Original Table Header: * Signature "DSDT" * Length 0x000014E3 (5347) * Revision 0x02 * Checksum 0x92 * OEM ID "BOCHS " * OEM Table ID "BXPC " * OEM Revision 0x00000001 (1) * Compiler ID "BXPC" * Compiler Version 0x00000001 (1) */ DefinitionBlock ("", "DSDT", 2, "BOCHS ", "BXPC ", 0x00000001) { Scope (\_SB) { Device (C000) { Name (_HID, "ACPI0007" /* Processor Device */) // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID } Device (COM0) { Name (_HID, "ARMH0011") // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Memory32Fixed (ReadWrite, 0x09000000, // Address Base 0x00001000, // Address Length ) Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x00000021, } }) } Device (FWCF) { Name (_HID, "QEMU0002") // _HID: Hardware ID Name (_STA, 0x0B) // _STA: Status Name (_CCA, One) // _CCA: Cache Coherency Attribute Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Memory32Fixed (ReadWrite, 0x09020000, // Address Base 0x00000018, // Address Length ) }) } Device (VR00) { Name (_HID, "LNRO0005") // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID Name (_CCA, One) // _CCA: Cache Coherency Attribute Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Memory32Fixed (ReadWrite, 0x0A000000, // Address Base 0x00000200, // Address Length ) Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x00000030, } }) } ../.. Device (L000) { Name (_HID, "PNP0C0F" /* PCI Interrupt Link Device */) // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID Name (_PRS, ResourceTemplate () // _PRS: Possible Resource Settings { Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x00000023, } }) Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x00000023, } }) Method (_SRS, 1, NotSerialized) // _SRS: Set Resource Settings { } } ../.. Device (PCI0) { Name (_HID, "PNP0A08" /* PCI Express Bus */) // _HID: Hardware ID Name (_CID, "PNP0A03" /* PCI Bus */) // _CID: Compatible ID Name (_SEG, Zero) // _SEG: PCI Segment Name (_BBN, Zero) // _BBN: BIOS Bus Number Name (_UID, Zero) // _UID: Unique ID Name (_STR, Unicode ("PCIe 0 Device")) // _STR: Description String Name (_CCA, One) // _CCA: Cache Coherency Attribute Name (_PRT, Package (0x80) // _PRT: PCI Routing Table { Package (0x04) { 0xFFFF, Zero, L000, Zero }, ../.. }) Method (_CBA, 0, NotSerialized) // _CBA: Configuration Base Address { Return (0x0000004010000000) } Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode, 0x0000, // Granularity 0x0000, // Range Minimum 0x00FF, // Range Maximum 0x0000, // Translation Offset 0x0100, // Length ,, ) DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, 0x00000000, // Granularity 0x10000000, // Range Minimum 0x3EFEFFFF, // Range Maximum 0x00000000, // Translation Offset 0x2EFF0000, // Length ,, , AddressRangeMemory, TypeStatic) DWordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, 0x00000000, // Granularity 0x00000000, // Range Minimum 0x0000FFFF, // Range Maximum 0x3EFF0000, // Translation Offset 0x00010000, // Length ,, , TypeStatic, DenseTranslation) QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, 0x0000000000000000, // Granularity 0x0000008000000000, // Range Minimum 0x000000FFFFFFFFFF, // Range Maximum 0x0000000000000000, // Translation Offset 0x0000008000000000, // Length ,, , AddressRangeMemory, TypeStatic) }) Method (_OSC, 4, NotSerialized) // _OSC: Operating System Capabilities { CreateDWordField (Arg3, Zero, CDW1) If ((Arg0 == ToUUID ("33db4d5b-1ff7-401c-9657-7441c03dd766") /* PCI Host Bridge Device */)) { CreateDWordField (Arg3, 0x04, CDW2) CreateDWordField (Arg3, 0x08, CDW3) Local0 = CDW3 /* \_SB_.PCI0._OSC.CDW3 */ Local0 &= 0x1F If ((Arg1 != One)) { CDW1 |= 0x08 } If ((CDW3 != Local0)) { CDW1 |= 0x10 } CDW3 = Local0 } Else { CDW1 |= 0x04 } Return (Arg3) } Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method { If ((Arg0 == ToUUID ("e5c937d0-3553-4d7a-9117-ea4d19c3434d") /* Device Labeling Interface */)) { If ((Arg2 == Zero)) { Return (Buffer (One) { 0x01 // . }) } } Return (Buffer (One) { 0x00 // . }) } Device (RES0) { Name (_HID, "PNP0C02" /* PNP Motherboard Resources */) // _HID: Hardware ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, 0x0000000000000000, // Granularity 0x0000004010000000, // Range Minimum 0x000000401FFFFFFF, // Range Maximum 0x0000000000000000, // Translation Offset 0x0000000010000000, // Length ,, , AddressRangeMemory, TypeStatic) }) } } Device (\_SB.GED) { Name (_HID, "ACPI0013" /* Generic Event Device */) // _HID: Hardware ID Name (_UID, "GED") // _UID: Unique ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, ) { 0x00000029, } }) OperationRegion (EREG, SystemMemory, 0x09080000, 0x04) Field (EREG, DWordAcc, NoLock, WriteAsZeros) { ESEL, 32 } Method (_EVT, 1, Serialized) // _EVT: Event { Local0 = ESEL /* \_SB_.GED_.ESEL */ If (((Local0 & 0x02) == 0x02)) { Notify (PWRB, 0x80) // Status Change } } } Device (PWRB) { Name (_HID, "PNP0C0C" /* Power Button Device */) // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID } } Scope (\_SB.PCI0) { Method (EDSM, 5, Serialized) { If ((Arg2 == Zero)) { Local0 = Buffer (One) { 0x00 // . } If ((Arg0 != ToUUID ("e5c937d0-3553-4d7a-9117-ea4d19c3434d") /* Device Labeling Interface */)) { Return (Local0) } If ((Arg1 < 0x02)) { Return (Local0) } Local0 [Zero] = 0x81 Return (Local0) } If ((Arg2 == 0x07)) { Local0 = Package (0x02) { Zero, "" } Local1 = DerefOf (Arg4 [Zero]) Local0 [Zero] = Local1 Return (Local0) } } Device (S00) { Name (_ADR, Zero) // _ADR: Address } Device (S08) { Name (_ADR, 0x00010000) // _ADR: Address } Device (S38) { Name (_ADR, 0x00070000) // _ADR: Address Device (S00) { Name (_ADR, Zero) // _ADR: Address Method (_DSM, 4, Serialized) // _DSM: Device-Specific Method { Local0 = Package (0x01) { 0x0C } Return (EDSM (Arg0, Arg1, Arg2, Arg3, Local0)) } } } } } Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-36-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex | Bin 0 -> 5347 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex b/tests/data/acpi/aarch64/virt/DSDT.hpoffacpiindex index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..970d43f68bca060361105f70dbb00b3a25646db4 100644 GIT binary patch literal 5347 zcmZvg%WoQ26o>C%Ffcp}7{9S`nl$P&8jN2_lhiQan8etWapFpj1X0y0j=Ml=R4J`0 zIj)qZ+0|CNrjq}VN?mo=T^C(e-Si*muJm{3f;mSyBN^t*z2}?xE(ddFDqdypudGsm z)AFvn&!Zn%rsETxp**YBz48`i^?S8g4g%({-_L|R+5N}}X8Y6#X;q;AL8(s?#s`!k*J)h3j%mWUNf~mTCM3ozVcen&xlWT3BS#pwDMPN)F^RE27#~uG zT&F3Cu}Byjlp)t?T4KCG7$wS(>og-Vt`J6vUFP zEEC3E%8=`HPGYPQ#y!fA>og}Z)(K;qGUPg)ml!t)qfQt_xlR`(#!bT5p$xfB^Ah7W zVeIx9wcz$eL)6%5IIgpK(R?(}^Caz<-{)NR=Y#s%vFvEeYkJnfiI3`E4nv^{p{OeY z${f2u-PKps|MoDHZbm5UO@U>OaaR9Z#93%cDC?@gGRKCj(}r8?e`=ala@Mh0E}BUb z6?M;7y@S_sdFNssy}f2SZ|54yRW-CiWY?RrzuwPJ_xIS(N^zsaX5d=k*Nd)7)o~3jS=Jg|;g^5Ws=I3GRAk>-Q`*YD# znz{W*XAY*KVbo{DR6i4;&K#U&#LpaD!HCkCgQ+7X6I1<6ggO&fWPqgMwusWKFsR*OA_Ut2&t#dJZN&J#)-6$2@U^LOl^u&w1uK&phY3CqnAEz&sb2CvIq{ zCqn9(XP$ZHndhDejb}F1a?2%kj{gcOwPAi((XniW?WZpjDu91b*n0ZK`VRFH$M2=T zrFy;p^lcfo-ZhL^ar~;84L`%!dKk6Tt9MC`KYXmm^yWvy7k&Jh%5H~SyPg|-b)?+N zexbV)I(wWRw^Il8Ka2n7a~nVHKP~-o>yOV~9oa!lSi*Y*@5PROX@A|e^X+cx^z6|3 zMyLB@h3@$2nLlifrSwRrt&jX1H-|K;aDJ41zg6+-!R{q<&fnL5``cUjs{X^&KlPbk z-Fi2A_C%ehM=VRf;1O0m*sOY_xpDnRSi0dW+Rkg{{_4UzzcG~7d-&gIo`{vh53lKG zZg;AcmU;GI9=H{BlY|Fu2@L4I=c1t;vEDYR$JHi!D126ts2x0b9@C*)^{U&8+4)xW z!CthZ1FXy$R!H|IFB<#JvVDATGNX6Q$xN%~nscetcFbIj?%%TaLRG9xqPvO}=xF(N zclK0XrSo<-{Nte=IB$3C=P%nz;Xv-}i5>1_LIIUf?asF^KUeBe=aCsaIEi_V1L0Do ti8USboOKgGM=$cSR~#M) Date: Mon, 14 Jul 2025 10:05:20 +0200 Subject: [PATCH 2421/2760] qtest/bios-tables-test: Generate reference blob for DSDT.acpipcihp The disassembled DSDT table is given below. /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20210604 (64-bit version) * Copyright (c) 2000 - 2021 Intel Corporation * * Disassembling to symbolic ASL+ operators * * Disassembly of ../tests/data/acpi/aarch64/virt/DSDT.acpipcihp, Thu Jul 3 05:16:27 2025 * * Original Table Header: * Signature "DSDT" * Length 0x0000183A (6202) * Revision 0x02 * Checksum 0x98 * OEM ID "BOCHS " * OEM Table ID "BXPC " * OEM Revision 0x00000001 (1) * Compiler ID "BXPC" * Compiler Version 0x00000001 (1) */ DefinitionBlock ("", "DSDT", 2, "BOCHS ", "BXPC ", 0x00000001) { Scope (\_SB) { Device (C000) { Name (_HID, "ACPI0007" /* Processor Device */) // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID } Device (COM0) { Name (_HID, "ARMH0011") // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Memory32Fixed (ReadWrite, 0x09000000, // Address Base 0x00001000, // Address Length ) Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x00000021, } }) } Device (FWCF) { Name (_HID, "QEMU0002") // _HID: Hardware ID Name (_STA, 0x0B) // _STA: Status Name (_CCA, One) // _CCA: Cache Coherency Attribute Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Memory32Fixed (ReadWrite, 0x09020000, // Address Base 0x00000018, // Address Length ) }) } Device (VR00) { Name (_HID, "LNRO0005") // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID Name (_CCA, One) // _CCA: Cache Coherency Attribute Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Memory32Fixed (ReadWrite, 0x0A000000, // Address Base 0x00000200, // Address Length ) Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x00000030, } }) } ../.. Device (L000) { Name (_HID, "PNP0C0F" /* PCI Interrupt Link Device */) // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID Name (_PRS, ResourceTemplate () // _PRS: Possible Resource Settings { Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x00000023, } }) Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Interrupt (ResourceConsumer, Level, ActiveHigh, Exclusive, ,, ) { 0x00000023, } }) Method (_SRS, 1, NotSerialized) // _SRS: Set Resource Settings { } } ../.. Device (PCI0) { Name (_HID, "PNP0A08" /* PCI Express Bus */) // _HID: Hardware ID Name (_CID, "PNP0A03" /* PCI Bus */) // _CID: Compatible ID Name (_SEG, Zero) // _SEG: PCI Segment Name (_BBN, Zero) // _BBN: BIOS Bus Number Name (_UID, Zero) // _UID: Unique ID Name (_STR, Unicode ("PCIe 0 Device")) // _STR: Description String Name (_CCA, One) // _CCA: Cache Coherency Attribute Name (_PRT, Package (0x80) // _PRT: PCI Routing Table { Package (0x04) { 0xFFFF, Zero, L000, Zero }, ../.. }) Method (_CBA, 0, NotSerialized) // _CBA: Configuration Base Address { Return (0x0000004010000000) } Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { WordBusNumber (ResourceProducer, MinFixed, MaxFixed, PosDecode, 0x0000, // Granularity 0x0000, // Range Minimum 0x00FF, // Range Maximum 0x0000, // Translation Offset 0x0100, // Length ,, ) DWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, 0x00000000, // Granularity 0x10000000, // Range Minimum 0x3EFEFFFF, // Range Maximum 0x00000000, // Translation Offset 0x2EFF0000, // Length ,, , AddressRangeMemory, TypeStatic) DWordIO (ResourceProducer, MinFixed, MaxFixed, PosDecode, EntireRange, 0x00000000, // Granularity 0x00000000, // Range Minimum 0x0000FFFF, // Range Maximum 0x3EFF0000, // Translation Offset 0x00010000, // Length ,, , TypeStatic, DenseTranslation) QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, 0x0000000000000000, // Granularity 0x0000008000000000, // Range Minimum 0x000000FFFFFFFFFF, // Range Maximum 0x0000000000000000, // Translation Offset 0x0000008000000000, // Length ,, , AddressRangeMemory, TypeStatic) }) Method (_OSC, 4, NotSerialized) // _OSC: Operating System Capabilities { CreateDWordField (Arg3, Zero, CDW1) If ((Arg0 == ToUUID ("33db4d5b-1ff7-401c-9657-7441c03dd766") /* PCI Host Bridge Device */)) { CreateDWordField (Arg3, 0x04, CDW2) CreateDWordField (Arg3, 0x08, CDW3) Local0 = CDW3 /* \_SB_.PCI0._OSC.CDW3 */ Local0 &= 0x1E If ((Arg1 != One)) { CDW1 |= 0x08 } If ((CDW3 != Local0)) { CDW1 |= 0x10 } CDW3 = Local0 } Else { CDW1 |= 0x04 } Return (Arg3) } Method (_DSM, 4, NotSerialized) // _DSM: Device-Specific Method { If ((Arg0 == ToUUID ("e5c937d0-3553-4d7a-9117-ea4d19c3434d") /* Device Labeling Interface */)) { If ((Arg2 == Zero)) { Return (Buffer (One) { 0x01 // . }) } } Return (Buffer (One) { 0x00 // . }) } Device (RES0) { Name (_HID, "PNP0C02" /* PNP Motherboard Resources */) // _HID: Hardware ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { QWordMemory (ResourceProducer, PosDecode, MinFixed, MaxFixed, NonCacheable, ReadWrite, 0x0000000000000000, // Granularity 0x0000004010000000, // Range Minimum 0x000000401FFFFFFF, // Range Maximum 0x0000000000000000, // Translation Offset 0x0000000010000000, // Length ,, , AddressRangeMemory, TypeStatic) }) } } Device (\_SB.GED) { Name (_HID, "ACPI0013" /* Generic Event Device */) // _HID: Hardware ID Name (_UID, "GED") // _UID: Unique ID Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { Interrupt (ResourceConsumer, Edge, ActiveHigh, Exclusive, ,, ) { 0x00000029, } }) OperationRegion (EREG, SystemMemory, 0x09080000, 0x04) Field (EREG, DWordAcc, NoLock, WriteAsZeros) { ESEL, 32 } Method (_EVT, 1, Serialized) // _EVT: Event { Local0 = ESEL /* \_SB_.GED_.ESEL */ If (((Local0 & 0x02) == 0x02)) { Notify (PWRB, 0x80) // Status Change } If (((Local0 & 0x10) == 0x10)) { Acquire (\_SB.PCI0.BLCK, 0xFFFF) \_SB.PCI0.PCNT () Release (\_SB.PCI0.BLCK) } } } Device (PWRB) { Name (_HID, "PNP0C0C" /* Power Button Device */) // _HID: Hardware ID Name (_UID, Zero) // _UID: Unique ID } } Scope (_SB.PCI0) { OperationRegion (PCST, SystemMemory, 0x090C0000, 0x08) Field (PCST, DWordAcc, NoLock, WriteAsZeros) { PCIU, 32, PCID, 32 } OperationRegion (SEJ, SystemMemory, 0x090C0008, 0x04) Field (SEJ, DWordAcc, NoLock, WriteAsZeros) { B0EJ, 32 } OperationRegion (BNMR, SystemMemory, 0x090C0010, 0x08) Field (BNMR, DWordAcc, NoLock, WriteAsZeros) { BNUM, 32, PIDX, 32 } Mutex (BLCK, 0x00) Method (PCEJ, 2, NotSerialized) { Acquire (BLCK, 0xFFFF) BNUM = Arg0 B0EJ = (One << Arg1) Release (BLCK) Return (Zero) } Method (AIDX, 2, NotSerialized) { Acquire (BLCK, 0xFFFF) BNUM = Arg0 PIDX = (One << Arg1) Local0 = PIDX /* \_SB_.PCI0.PIDX */ Release (BLCK) Return (Local0) } Method (PDSM, 5, Serialized) { If ((Arg2 == Zero)) { Local0 = Buffer (One) { 0x00 // . } If ((Arg0 != ToUUID ("e5c937d0-3553-4d7a-9117-ea4d19c3434d") /* Device Labeling Interface */)) { Return (Local0) } If ((Arg1 < 0x02)) { Return (Local0) } Local1 = Zero Local2 = AIDX (DerefOf (Arg4 [Zero]), DerefOf (Arg4 [One] )) If (!((Local2 == Zero) | (Local2 == 0xFFFFFFFF))) { Local1 |= One Local1 |= (One << 0x07) } Local0 [Zero] = Local1 Return (Local0) } If ((Arg2 == 0x07)) { Local2 = AIDX (DerefOf (Arg4 [Zero]), DerefOf (Arg4 [One] )) Local0 = Package (0x02) {} If (!((Local2 == Zero) || (Local2 == 0xFFFFFFFF))) { Local0 [Zero] = Local2 Local0 [One] = "" } Return (Local0) } } } Scope (\_SB.PCI0) { Method (EDSM, 5, Serialized) { If ((Arg2 == Zero)) { Local0 = Buffer (One) { 0x00 // . } If ((Arg0 != ToUUID ("e5c937d0-3553-4d7a-9117-ea4d19c3434d") /* Device Labeling Interface */)) { Return (Local0) } If ((Arg1 < 0x02)) { Return (Local0) } Local0 [Zero] = 0x81 Return (Local0) } If ((Arg2 == 0x07)) { Local0 = Package (0x02) { Zero, "" } Local1 = DerefOf (Arg4 [Zero]) Local0 [Zero] = Local1 Return (Local0) } } Device (S00) { Name (_ADR, Zero) // _ADR: Address } Device (S08) { Name (_ADR, 0x00010000) // _ADR: Address } Device (S38) { Name (_ADR, 0x00070000) // _ADR: Address Device (S00) { Name (_ADR, Zero) // _ADR: Address } Name (BSEL, One) Scope (S00) { Name (ASUN, Zero) Method (_DSM, 4, Serialized) // _DSM: Device-Specific Method { Local0 = Package (0x02) { Zero, Zero } Local0 [Zero] = BSEL /* \_SB_.PCI0.S38_.BSEL */ Local0 [One] = ASUN /* \_SB_.PCI0.S38_.S00_.ASUN */ Return (PDSM (Arg0, Arg1, Arg2, Arg3, Local0)) } Name (_SUN, Zero) // _SUN: Slot User Number Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 { PCEJ (BSEL, _SUN) } } Method (DVNT, 2, NotSerialized) { If ((Arg0 & One)) { Notify (S00, Arg1) } } } Name (BSEL, Zero) Scope (S00) { Name (ASUN, Zero) Method (_DSM, 4, Serialized) // _DSM: Device-Specific Method { Local0 = Package (0x02) { Zero, Zero } Local0 [Zero] = BSEL /* \_SB_.PCI0.BSEL */ Local0 [One] = ASUN /* \_SB_.PCI0.S00_.ASUN */ Return (PDSM (Arg0, Arg1, Arg2, Arg3, Local0)) } Name (_SUN, Zero) // _SUN: Slot User Number Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 { PCEJ (BSEL, _SUN) } } Scope (S08) { Name (ASUN, One) Method (_DSM, 4, Serialized) // _DSM: Device-Specific Method { Local0 = Package (0x02) { Zero, Zero } Local0 [Zero] = BSEL /* \_SB_.PCI0.BSEL */ Local0 [One] = ASUN /* \_SB_.PCI0.S08_.ASUN */ Return (PDSM (Arg0, Arg1, Arg2, Arg3, Local0)) } Name (_SUN, One) // _SUN: Slot User Number Method (_EJ0, 1, NotSerialized) // _EJx: Eject Device, x=0-9 { PCEJ (BSEL, _SUN) } } Method (DVNT, 2, NotSerialized) { If ((Arg0 & One)) { Notify (S00, Arg1) } If ((Arg0 & 0x02)) { Notify (S08, Arg1) } } Device (PHPR) { Name (_HID, "PNP0A06" /* Generic Container Device */) // _HID: Hardware ID Name (_UID, "PCI Hotplug resources") // _UID: Unique ID Name (_STA, 0x0B) // _STA: Status Name (_CRS, ResourceTemplate () // _CRS: Current Resource Settings { IO (Decode16, 0x0000, // Range Minimum 0x0000, // Range Maximum 0x01, // Alignment 0x18, // Length ) }) } Scope (S38) { Method (PCNT, 0, NotSerialized) { BNUM = One DVNT (PCIU, One) DVNT (PCID, 0x03) } } Method (PCNT, 0, NotSerialized) { BNUM = Zero DVNT (PCIU, One) DVNT (PCID, 0x03) ^S38.PCNT () } } } Signed-off-by: Eric Auger Reviewed-by: Jonathan Cameron Reviewed-by: Igor Mammedov Message-Id: <20250714080639.2525563-37-eric.auger@redhat.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/aarch64/virt/DSDT.acpipcihp | Bin 0 -> 6202 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/aarch64/virt/DSDT.acpipcihp b/tests/data/acpi/aarch64/virt/DSDT.acpipcihp index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8d55a877a40cb4c4dffdc70378204e12d2261a75 100644 GIT binary patch literal 6202 zcmb`L&2Jl35WwHsPHeC3WbL^5Y8t!ri&7}-xJ}Z~me{*alQedWH)$)m!cM9}(p2iI zA4s8!lm@AkUQio>I2tK`2ofi5+&FON!au+bGxK&G&(O!A5|(0n_PzP-`|Zv;vuiu{ z%FM73fgg&?*1VG~-l|$zcxgfi{EnZ-x0ZIh;S?M7y-~|F%}8T@!A=yc>H-8)BGOo0 zutj}u##%0S#%kqx)6C`ku}H(JImuCd=y_BiFe(fozUdX3b{wK@$eo_MY0Y`uPfF!g z;7&lUvr^DwkZct+50TXSJVYQ0YnGXX(2ZJWNlTU5GNfMY?)EUVyK`Lx{GFQ=k@NP> z5xCno5CPFeLp4kykQ;Dn>71iHd_)=QI*qH=F-#bjDMMYS2^C|MFs@LBx=#C4j4{HvN*U@p z?N>3z3FBkRP}ixUVq^(pmNL|Jnp82~A&dfLsOvPPVw@z5B4w!SbU?*8O&AtoOsMNL ztzx`O7&c|7>-30<@g8B6C_`PRgDS@RgfT}M>N*`#G0qdlHOf%e>9C4n62?4bsOxk@ z#h4(B18OfvfiM;+LtUpwRg6i(xK0`BIvrCnrU_$-GSqc?OvRWXj51;5)pa_q zVq7MS3T3G4G^1i%C5&aCu@G81Y>29rs%e>Xhs{S7dY(Q#Ci|Qdaz0Ss*p}{XI5kJB z?@5%u9EO4>grZI=P~Nsf)FQ6Rzj_!7n-R)7rC@p6ILp6^I18E*$~vuJdE2_I*Hvr5 zKWoS;nA32yENCXZD33i~=^DHji5z|Fwc)L3nun? zV=@n>c0$YqzMKm^r8n1)Ftali8b;hBOqH1sVrFMAL7CZEK~P|3XKK*PgsCzULd=8} z$-RVRW@iNKSLAapnmPGWCRzdM21>f_WymCxq0qk9qbnPiUyACxq0qpLzB(&wlO+A@zit zKd;7R6&uVG8gS|fA@xi$&m{9qa!&}UXNq~Im?t#s)DuGLIlw#znCAfZgphisnP-}L zLW55|A*7y1nCB7Zd4zjHNIeIc=OFWh8v^PHA@v+$ogcIoX=tA2{#bb z6GG}a!aPTq=Lq+Nka~_X&r#+HHyqRxLh5;xc^+k+N4Y12)N_n^jxkTTL7|=yQqN<| z^BD6y#yugVp5x4OoO!|x4fTYOdS;kshIwYVCxo77+GtqCf;fO*1!2s3|5%}8nGfra zUnE2TexTsh#y9P2sFyhWzmzYd-ToVI%iz^M4af z0iO~0%)9v3eA?82fUB8hE`ghrMkEj<`+S@?+fFgs9xXR{Dd6?UX~U z zTt%XKsQxU5p;dB9OWE}Go<`}$O4P*&4$VFsB5Y2cS8vve`flqoZh)U$qkiJ{he6ys z_G`r@>w3FQHdL+3O8pGkfHUfQBS`U<>NSd%-@+O8pj1IveYa{kE4aFNRFBl385mkH zvYLfAJ6qp%N{bBWbN#)i~V)zHKFEr*0i;qwQWrg!_>7GO(7tA zYX6=d{&%9A3IuSUue#apIwrUY@uI0go-upID;PY=aH30c#glKfVEviDhB3K@R8K%8 zMAMZ5hYPIl#T?Uwnk?8gDEofLoc01Zr+6LAMVx$hOBlA0`H>=2o@OkEB%EZ+_hJ#WKkNw8xy{#tP9N_#^qqJyhc#?50ZcC@^8b9`{==whj;Eig0ap+bNFFM==wGJ6Rizz3*!`?mvjcNUg2tZRp8F?($oAe TwjNBM^~S}2jDLC_DLCgJQv(W2 literal 0 HcmV?d00001 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dc3ab24d05..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,2 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/aarch64/virt/DSDT.acpipcihp", From ce1b67acd6fa6b3cc959340b2b45a96b1b809c76 Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Mon, 14 Jul 2025 18:31:43 +0100 Subject: [PATCH 2422/2760] tests: virt: Allow changes to PPTT test table Allow changes to PPTT test table, preparing for adding identical implementation flags support and for adding a root node for all the system. This is related to both loongarch64 and aarch64. Signed-off-by: Yicong Yang Reviewed-by: Jonathan Cameron Reviewed-by: Zhao Liu Signed-off-by: Alireza Sanaee Message-Id: <20250714173146.511-2-alireza.sanaee@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/qtest/bios-tables-test-allowed-diff.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..5c3ff47748 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,6 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/PPTT", +"tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt", +"tests/data/acpi/aarch64/virt/PPTT.topology", +"tests/data/acpi/loongarch64/virt/PPTT", +"tests/data/acpi/loongarch64/virt/PPTT.topology", From ec57e4778e3c64d17d57e629a0c37209ff36900b Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Mon, 14 Jul 2025 18:31:44 +0100 Subject: [PATCH 2423/2760] hw/acpi/aml-build: Set identical implementation flag for PPTT processor nodes Per ACPI 6.5 Table 5.158: Processor Structure Flags, the identical implementation flag indicates whether all the children processors of this node share the same identical implementation revision. Currently Linux support parsing this field [1] and maybe used to identify the heterogeneous platform. Since qemu only support homogeneous emulation, set this flag for all the processor node to indicates the facts when building the PPTT table. Node leaf is an exception since spec says this flag should be ignored on leaf nodes by OSPM. [1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/acpi/pptt.c?h=v6.11-rc1#n810 Signed-off-by: Yicong Yang Reviewed-by: Jonathan Cameron Signed-off-by: Alireza Sanaee Message-Id: <20250714173146.511-3-alireza.sanaee@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index cb817a0f31..9b9be4ea0f 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -2172,7 +2172,8 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, core_id = -1; socket_offset = table_data->len - pptt_start; build_processor_hierarchy_node(table_data, - (1 << 0), /* Physical package */ + (1 << 0) | /* Physical package */ + (1 << 4), /* Identical Implementation */ 0, socket_id, NULL, 0); } @@ -2183,7 +2184,8 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, core_id = -1; cluster_offset = table_data->len - pptt_start; build_processor_hierarchy_node(table_data, - (0 << 0), /* Not a physical package */ + (0 << 0) | /* Not a physical package */ + (1 << 4), /* Identical Implementation */ socket_offset, cluster_id, NULL, 0); } } else { @@ -2201,7 +2203,8 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, core_id = cpus->cpus[n].props.core_id; core_offset = table_data->len - pptt_start; build_processor_hierarchy_node(table_data, - (0 << 0), /* Not a physical package */ + (0 << 0) | /* Not a physical package */ + (1 << 4), /* Identical Implementation */ cluster_offset, core_id, NULL, 0); } From 9bf96fc2df494053bcd8697b5c5ad8954bf3510f Mon Sep 17 00:00:00 2001 From: Yicong Yang Date: Mon, 14 Jul 2025 18:31:45 +0100 Subject: [PATCH 2424/2760] hw/acpi/aml-build: Build a root node in the PPTT table Currently we build the PPTT starting from the socket node and each socket will be a separate tree. For a multi-socket system it'll be hard for the OS to know the whole system is homogeneous or not (actually we're in the current implementation) since no parent node to telling the identical implementation informentation. Add a root node for indicating this. Signed-off-by: Yicong Yang Reviewed-by: Jonathan Cameron Signed-off-by: Alireza Sanaee Message-Id: <20250714173146.511-4-alireza.sanaee@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/acpi/aml-build.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/hw/acpi/aml-build.c b/hw/acpi/aml-build.c index 9b9be4ea0f..1e685f982f 100644 --- a/hw/acpi/aml-build.c +++ b/hw/acpi/aml-build.c @@ -2152,12 +2152,25 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, int64_t socket_id = -1, cluster_id = -1, core_id = -1; uint32_t socket_offset = 0, cluster_offset = 0, core_offset = 0; uint32_t pptt_start = table_data->len; + uint32_t root_offset; int n; AcpiTable table = { .sig = "PPTT", .rev = 2, .oem_id = oem_id, .oem_table_id = oem_table_id }; acpi_table_begin(&table, table_data); + /* + * Build a root node for all the processor nodes. Otherwise when + * building a multi-socket system each socket tree is separated + * and will be hard for the OS like Linux to know whether the + * system is homogeneous. + */ + root_offset = table_data->len - pptt_start; + build_processor_hierarchy_node(table_data, + (1 << 0) | /* Physical package */ + (1 << 4), /* Identical Implementation */ + 0, 0, NULL, 0); + /* * This works with the assumption that cpus[n].props.*_id has been * sorted from top to down levels in mc->possible_cpu_arch_ids(). @@ -2174,7 +2187,7 @@ void build_pptt(GArray *table_data, BIOSLinker *linker, MachineState *ms, build_processor_hierarchy_node(table_data, (1 << 0) | /* Physical package */ (1 << 4), /* Identical Implementation */ - 0, socket_id, NULL, 0); + root_offset, socket_id, NULL, 0); } if (mc->smp_props.clusters_supported && mc->smp_props.has_clusters) { From 98f030e7c9b4ac42e886b140899f7ae12ab4f028 Mon Sep 17 00:00:00 2001 From: Alireza Sanaee Date: Mon, 14 Jul 2025 18:31:46 +0100 Subject: [PATCH 2425/2760] tests: virt: Update expected ACPI tables for virt test Update the ACPI tables according to the acpi aml_build change, also empty bios-tables-test-allowed-diff.h. The disassembled differences between actual and expected PPTT shows below. Only about the root node adding and identification flag set as expected. Diff regarding Loongarch64: /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20230628 (64-bit version) * Copyright (c) 2000 - 2023 Intel Corporation * - * Disassembly of tests/data/acpi/loongarch64/virt/PPTT, Mon Jul 14 16:15:12 2025 + * Disassembly of /tmp/aml-4A0092, Mon Jul 14 16:15:12 2025 * * ACPI Data Table [PPTT] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue (in hex) */ [000h 0000 004h] Signature : "PPTT" [Processor Properties Topology Table] -[004h 0004 004h] Table Length : 0000004C +[004h 0004 004h] Table Length : 00000060 [008h 0008 001h] Revision : 02 -[009h 0009 001h] Checksum : A8 +[009h 0009 001h] Checksum : 27 [00Ah 0010 006h] Oem ID : "BOCHS " [010h 0016 008h] Oem Table ID : "BXPC " [018h 0024 004h] Oem Revision : 00000001 [01Ch 0028 004h] Asl Compiler ID : "BXPC" [020h 0032 004h] Asl Compiler Revision : 00000001 [024h 0036 001h] Subtable Type : 00 [Processor Hierarchy Node] [025h 0037 001h] Length : 14 [026h 0038 002h] Reserved : 0000 -[028h 0040 004h] Flags (decoded below) : 00000001 +[028h 0040 004h] Flags (decoded below) : 00000011 Physical package : 1 ACPI Processor ID valid : 0 Processor is a thread : 0 Node is a leaf : 0 - Identical Implementation : 0 + Identical Implementation : 1 [02Ch 0044 004h] Parent : 00000000 [030h 0048 004h] ACPI Processor ID : 00000000 [034h 0052 004h] Private Resource Number : 00000000 [038h 0056 001h] Subtable Type : 00 [Processor Hierarchy Node] [039h 0057 001h] Length : 14 [03Ah 0058 002h] Reserved : 0000 -[03Ch 0060 004h] Flags (decoded below) : 0000000A +[03Ch 0060 004h] Flags (decoded below) : 00000011 + Physical package : 1 + ACPI Processor ID valid : 0 + Processor is a thread : 0 + Node is a leaf : 0 + Identical Implementation : 1 +[040h 0064 004h] Parent : 00000024 +[044h 0068 004h] ACPI Processor ID : 00000000 +[048h 0072 004h] Private Resource Number : 00000000 + +[04Ch 0076 001h] Subtable Type : 00 [Processor Hierarchy Node] +[04Dh 0077 001h] Length : 14 +[04Eh 0078 002h] Reserved : 0000 +[050h 0080 004h] Flags (decoded below) : 0000000A Physical package : 0 ACPI Processor ID valid : 1 Processor is a thread : 0 Node is a leaf : 1 Identical Implementation : 0 -[040h 0064 004h] Parent : 00000024 -[044h 0068 004h] ACPI Processor ID : 00000000 -[048h 0072 004h] Private Resource Number : 00000000 +[054h 0084 004h] Parent : 00000038 +[058h 0088 004h] ACPI Processor ID : 00000000 +[05Ch 0092 004h] Private Resource Number : 00000000 -Raw Table Data: Length 76 (0x4C) +Raw Table Data: Length 96 (0x60) - 0000: 50 50 54 54 4C 00 00 00 02 A8 42 4F 43 48 53 20 // PPTTL.....BOCHS + 0000: 50 50 54 54 60 00 00 00 02 27 42 4F 43 48 53 20 // PPTT`....'BOCHS 0010: 42 58 50 43 20 20 20 20 01 00 00 00 42 58 50 43 // BXPC ....BXPC - 0020: 01 00 00 00 00 14 00 00 01 00 00 00 00 00 00 00 // ................ - 0030: 00 00 00 00 00 00 00 00 00 14 00 00 0A 00 00 00 // ................ - 0040: 24 00 00 00 00 00 00 00 00 00 00 00 // $........... + 0020: 01 00 00 00 00 14 00 00 11 00 00 00 00 00 00 00 // ................ + 0030: 00 00 00 00 00 00 00 00 00 14 00 00 11 00 00 00 // ................ + 0040: 24 00 00 00 00 00 00 00 00 00 00 00 00 14 00 00 // $............... + 0050: 0A 00 00 00 38 00 00 00 00 00 00 00 00 00 00 00 // ....8........... Diff regarding ARM64: /* * Intel ACPI Component Architecture * AML/ASL+ Disassembler version 20200925 (64-bit version) * Copyright (c) 2000 - 2020 Intel Corporation * - * Disassembly of tests/data/acpi/aarch64/virt/PPTT, Thu Apr 24 11:02:39 2025 + * Disassembly of /tmp/aml-E0RF52, Thu Apr 24 11:02:39 2025 * * ACPI Data Table [PPTT] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue */ [000h 0000 4] Signature : "PPTT" [Processor Properties Topology Table] -[004h 0004 4] Table Length : 0000004C +[004h 0004 4] Table Length : 00000060 [008h 0008 1] Revision : 02 -[009h 0009 1] Checksum : A8 +[009h 0009 1] Checksum : 27 [00Ah 0010 6] Oem ID : "BOCHS " [010h 0016 8] Oem Table ID : "BXPC " [018h 0024 4] Oem Revision : 00000001 [01Ch 0028 4] Asl Compiler ID : "BXPC" [020h 0032 4] Asl Compiler Revision : 00000001 [024h 0036 1] Subtable Type : 00 [Processor Hierarchy Node] [025h 0037 1] Length : 14 [026h 0038 2] Reserved : 0000 -[028h 0040 4] Flags (decoded below) : 00000001 +[028h 0040 4] Flags (decoded below) : 00000011 Physical package : 1 ACPI Processor ID valid : 0 Processor is a thread : 0 Node is a leaf : 0 - Identical Implementation : 0 + Identical Implementation : 1 [02Ch 0044 4] Parent : 00000000 [030h 0048 4] ACPI Processor ID : 00000000 [034h 0052 4] Private Resource Number : 00000000 [038h 0056 1] Subtable Type : 00 [Processor Hierarchy Node] [039h 0057 1] Length : 14 [03Ah 0058 2] Reserved : 0000 -[03Ch 0060 4] Flags (decoded below) : 0000000A +[03Ch 0060 4] Flags (decoded below) : 00000011 + Physical package : 1 + ACPI Processor ID valid : 0 + Processor is a thread : 0 + Node is a leaf : 0 + Identical Implementation : 1 +[040h 0064 4] Parent : 00000024 +[044h 0068 4] ACPI Processor ID : 00000000 +[048h 0072 4] Private Resource Number : 00000000 + +[04Ch 0076 1] Subtable Type : 00 [Processor Hierarchy Node] +[04Dh 0077 1] Length : 14 +[04Eh 0078 2] Reserved : 0000 +[050h 0080 4] Flags (decoded below) : 0000000A Physical package : 0 ACPI Processor ID valid : 1 Processor is a thread : 0 Node is a leaf : 1 Identical Implementation : 0 -[040h 0064 4] Parent : 00000024 -[044h 0068 4] ACPI Processor ID : 00000000 -[048h 0072 4] Private Resource Number : 00000000 +[054h 0084 4] Parent : 00000038 +[058h 0088 4] ACPI Processor ID : 00000000 +[05Ch 0092 4] Private Resource Number : 00000000 -Raw Table Data: Length 76 (0x4C) +Raw Table Data: Length 96 (0x60) - 0000: 50 50 54 54 4C 00 00 00 02 A8 42 4F 43 48 53 20 // PPTTL.....BOCHS + 0000: 50 50 54 54 60 00 00 00 02 27 42 4F 43 48 53 20 // PPTT`....'BOCHS 0010: 42 58 50 43 20 20 20 20 01 00 00 00 42 58 50 43 // BXPC ....BXPC - 0020: 01 00 00 00 00 14 00 00 01 00 00 00 00 00 00 00 // ................ - 0030: 00 00 00 00 00 00 00 00 00 14 00 00 0A 00 00 00 // ................ - 0040: 24 00 00 00 00 00 00 00 00 00 00 00 // $........... + 0020: 01 00 00 00 00 14 00 00 11 00 00 00 00 00 00 00 // ................ + 0030: 00 00 00 00 00 00 00 00 00 14 00 00 11 00 00 00 // ................ + 0040: 24 00 00 00 00 00 00 00 00 00 00 00 00 14 00 00 // $............... + 0050: 0A 00 00 00 38 00 00 00 00 00 00 00 00 00 00 00 // ....8........... Signed-off-by: Alireza Sanaee Message-Id: <20250714173146.511-5-alireza.sanaee@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- tests/data/acpi/aarch64/virt/PPTT | Bin 76 -> 96 bytes tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt | Bin 156 -> 176 bytes tests/data/acpi/aarch64/virt/PPTT.topology | Bin 336 -> 356 bytes tests/data/acpi/loongarch64/virt/PPTT | Bin 76 -> 96 bytes tests/data/acpi/loongarch64/virt/PPTT.topology | Bin 176 -> 196 bytes tests/qtest/bios-tables-test-allowed-diff.h | 5 ----- 6 files changed, 5 deletions(-) diff --git a/tests/data/acpi/aarch64/virt/PPTT b/tests/data/acpi/aarch64/virt/PPTT index 7a1258ecf123555b24462c98ccbb76b4ac1d0c2b..15598a9b8a3cc0cdd50bc1f77c73ae0ba728a272 100644 GIT binary patch literal 96 zcmWFt2nk7GU|?WUck*}k2v%^42yj*a0!E-1hz+6{L>L$ZK{PUeim9N9aRK=jNMZmJ Cwg&+K delta 38 kcmYfB;R*-{3GrcIU|?D?kxP!15y)bg=qSvi0%AY`0D`Lo$p8QV diff --git a/tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt b/tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt index 4eef303a5b6168c6bc3795c2e2c53f65b4c4cfd4..7b613ddaf4b8cfa13821aa2e835d290077221897 100644 GIT binary patch literal 176 zcmWFt2npH1z`(%7>*Vk35v<@85#X!<1dKp25F11@h%hh+f@ov_6;nYI;{x(6aEO7; b0?8riMHU0;EdgRCkQxvGs)LC!Lqr$=thxyS literal 156 zcmWFt2nm_Pz`(%t&&l7}BUr&HBEVSz2pEB4AU23*5Mf{d(;zks0L8d~Y!w(EL?em8 b)g$Re76a)`0AeN}1_P+x1R#eQBEkRwWK9VH diff --git a/tests/data/acpi/aarch64/virt/PPTT.topology b/tests/data/acpi/aarch64/virt/PPTT.topology index 3fbcae5ff08aaf16fedf4da45e941661d79c1174..6b864f035c9f48845e9a3beb482c5171074864a5 100644 GIT binary patch literal 356 zcmWFt2nk7HWME*L?&R<65v<@85#X!<1VAAM5F11@h%hh+f@ov_6;nYI69Dopu!#Af ziSYsX2{^>Sc7o)9c7V(S=|vU;>74__Oh60L&rG>8oYKrs+dflv?45YUMh?!vef$Csl%t&G&Cde(wdO>1GKm-gx_1*yTS+Iz)B8h>R aAic=uf$S9l3b27BK>%tVNQ@mK!TL$ZK{PUeim9N9aRK=jNMZmJ Cwg&+K delta 38 kcmYfB;R*-{3GrcIU|?D?kxP!15y)bg=qSvi0%AY`0D`Lo$p8QV diff --git a/tests/data/acpi/loongarch64/virt/PPTT.topology b/tests/data/acpi/loongarch64/virt/PPTT.topology index d91e55b2399d9949dbb8e4c8cf634af1a0e56df4..7fc92984694d2cac7bf867c7fea696e135f8c758 100644 GIT binary patch literal 196 zcmWFt2njjDz`($y<>c?|5v<@85#X!<1dKp25F11@h%hh+f@ov_6;nYI69Dop(8TzF jd>R@8b5D^9dn=lFi literal 176 zcmWFt2npH1z`($y@8s|75v<@85#X!<1dKp25F11@h%hjKX%HI*fMOt^0-+!zP>c`A cvcMq*Hv>s8vKUBj2@o^E^h3=9i7`XP0J3}u0RR91 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 5c3ff47748..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,6 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/aarch64/virt/PPTT", -"tests/data/acpi/aarch64/virt/PPTT.acpihmatvirt", -"tests/data/acpi/aarch64/virt/PPTT.topology", -"tests/data/acpi/loongarch64/virt/PPTT", -"tests/data/acpi/loongarch64/virt/PPTT.topology", From fe63feb7e8f2d62621008fdf7c884f3573de249b Mon Sep 17 00:00:00 2001 From: Fan Ni Date: Mon, 14 Jul 2025 18:44:57 +0100 Subject: [PATCH 2426/2760] hw/cxl: fix DC extent capacity tracking Per cxl r3.2 Section 9.13.3.3, extent capacity tracking should include extents in different states including added, pending, etc. Before the change, for the in-device extent number tracking purpose, we only have "total_extent_count" defined, which only tracks the number of extents accepted. However, we need to track number of extents in other states also, for now it is extents pending-to-add. To fix that, we introduce a new counter for dynamic capacity "nr_extents_accepted" which explicitly tracks number of the extents accepted by the hosts, and fix "total_extent_count" to include both accepted and pending extents counting. Signed-off-by: Fan Ni Signed-off-by: Jonathan Cameron Message-Id: <20250714174509.1984430-2-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 26 ++++++++++++++++++-------- hw/mem/cxl_type3.c | 1 + include/hw/cxl/cxl_device.h | 3 ++- 3 files changed, 21 insertions(+), 9 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 299f232f26..0b615ea37a 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -2750,7 +2750,7 @@ static CXLRetCode cmd_dcd_get_dyn_cap_ext_list(const struct cxl_cmd *cmd, uint16_t out_pl_len, size; CXLDCExtent *ent; - if (start_extent_id > ct3d->dc.total_extent_count) { + if (start_extent_id > ct3d->dc.nr_extents_accepted) { return CXL_MBOX_INVALID_INPUT; } @@ -2761,7 +2761,7 @@ static CXLRetCode cmd_dcd_get_dyn_cap_ext_list(const struct cxl_cmd *cmd, out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); stl_le_p(&out->count, record_count); - stl_le_p(&out->total_extents, ct3d->dc.total_extent_count); + stl_le_p(&out->total_extents, ct3d->dc.nr_extents_accepted); stl_le_p(&out->generation_num, ct3d->dc.ext_list_gen_seq); if (record_count > 0) { @@ -2883,16 +2883,20 @@ void cxl_extent_group_list_insert_tail(CXLDCExtentGroupList *list, QTAILQ_INSERT_TAIL(list, group, node); } -void cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list) +uint32_t cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list) { CXLDCExtent *ent, *ent_next; CXLDCExtentGroup *group = QTAILQ_FIRST(list); + uint32_t extents_deleted = 0; QTAILQ_REMOVE(list, group, node); QTAILQ_FOREACH_SAFE(ent, &group->list, node, ent_next) { cxl_remove_extent_from_extent_list(&group->list, ent); + extents_deleted++; } g_free(group); + + return extents_deleted; } /* @@ -3011,7 +3015,7 @@ static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd, CXLUpdateDCExtentListInPl *in = (void *)payload_in; CXLType3Dev *ct3d = CXL_TYPE3(cci->d); CXLDCExtentList *extent_list = &ct3d->dc.extents; - uint32_t i; + uint32_t i, num; uint64_t dpa, len; CXLRetCode ret; @@ -3020,7 +3024,8 @@ static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd, } if (in->num_entries_updated == 0) { - cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); + num = cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); + ct3d->dc.total_extent_count -= num; return CXL_MBOX_SUCCESS; } @@ -3051,10 +3056,12 @@ static CXLRetCode cmd_dcd_add_dyn_cap_rsp(const struct cxl_cmd *cmd, cxl_insert_extent_to_extent_list(extent_list, dpa, len, NULL, 0); ct3d->dc.total_extent_count += 1; + ct3d->dc.nr_extents_accepted += 1; ct3_set_region_block_backed(ct3d, dpa, len); } /* Remove the first extent group in the pending list */ - cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); + num = cxl_extent_group_list_delete_front(&ct3d->dc.extents_pending); + ct3d->dc.total_extent_count -= num; return CXL_MBOX_SUCCESS; } @@ -3160,7 +3167,7 @@ static CXLRetCode cxl_dc_extent_release_dry_run(CXLType3Dev *ct3d, } *updated_list_size = 0; } else { - *updated_list_size = ct3d->dc.total_extent_count + cnt_delta; + *updated_list_size = ct3d->dc.nr_extents_accepted + cnt_delta; } return ret; @@ -3222,7 +3229,10 @@ static CXLRetCode cmd_dcd_release_dyn_cap(const struct cxl_cmd *cmd, ct3_set_region_block_backed(ct3d, ent->start_dpa, ent->len); cxl_remove_extent_from_extent_list(&updated_list, ent); } - ct3d->dc.total_extent_count = updated_list_size; + ct3d->dc.total_extent_count += (updated_list_size - + ct3d->dc.nr_extents_accepted); + + ct3d->dc.nr_extents_accepted = updated_list_size; return CXL_MBOX_SUCCESS; } diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 94e7274912..f283178d88 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -2076,6 +2076,7 @@ static void qmp_cxl_process_dynamic_capacity_prescriptive(const char *path, } if (group) { cxl_extent_group_list_insert_tail(&dcd->dc.extents_pending, group); + dcd->dc.total_extent_count += num_extents; } /* diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index ed6cd50c67..a151e19da8 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -618,6 +618,7 @@ struct CXLType3Dev { CXLDCExtentList extents; CXLDCExtentGroupList extents_pending; uint32_t total_extent_count; + uint32_t nr_extents_accepted; uint32_t ext_list_gen_seq; uint8_t num_regions; /* 0-8 regions */ @@ -696,7 +697,7 @@ CXLDCExtentGroup *cxl_insert_extent_to_extent_group(CXLDCExtentGroup *group, uint16_t shared_seq); void cxl_extent_group_list_insert_tail(CXLDCExtentGroupList *list, CXLDCExtentGroup *group); -void cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list); +uint32_t cxl_extent_group_list_delete_front(CXLDCExtentGroupList *list); void ct3_set_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len); void ct3_clear_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, From 9fde6eb39d8f2354c8f8da91ee6854c60929270f Mon Sep 17 00:00:00 2001 From: Anisa Su Date: Mon, 14 Jul 2025 18:44:58 +0100 Subject: [PATCH 2427/2760] hw/cxl: mailbox-utils: 0x5600 - FMAPI Get DCD Info FM DCD Management command 0x5600 implemented per CXL 3.2 Spec Section 7.6.7.6.1. Reviewed-by: Fan Ni Signed-off-by: Anisa Su Signed-off-by: Jonathan Cameron Message-Id: <20250714174509.1984430-3-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 59 +++++++++++++++++++++++++++++++++++++ hw/mem/cxl_type3.c | 4 +++ include/hw/cxl/cxl_device.h | 1 + 3 files changed, 64 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 0b615ea37a..3304048922 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -23,6 +23,7 @@ #include "qemu/uuid.h" #include "system/hostmem.h" #include "qemu/range.h" +#include "qapi/qapi-types-cxl.h" #define CXL_CAPACITY_MULTIPLIER (256 * MiB) #define CXL_DC_EVENT_LOG_SIZE 8 @@ -117,6 +118,8 @@ enum { #define GET_PHYSICAL_PORT_STATE 0x1 TUNNEL = 0x53, #define MANAGEMENT_COMMAND 0x0 + FMAPI_DCD_MGMT = 0x56, + #define GET_DCD_INFO 0x0 }; /* CCI Message Format CXL r3.1 Figure 7-19 */ @@ -3237,6 +3240,52 @@ static CXLRetCode cmd_dcd_release_dyn_cap(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* CXL r3.2 section 7.6.7.6.1: Get DCD Info (Opcode 5600h) */ +static CXLRetCode cmd_fm_get_dcd_info(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t num_hosts; + uint8_t num_regions_supported; + uint8_t rsvd1[2]; + uint16_t supported_add_sel_policy_bitmask; + uint8_t rsvd2[2]; + uint16_t supported_removal_policy_bitmask; + uint8_t sanitize_on_release_bitmask; + uint8_t rsvd3; + uint64_t total_dynamic_capacity; + uint64_t region_blk_size_bitmasks[8]; + } QEMU_PACKED *out = (void *)payload_out; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDCRegion *region; + int i; + + out->num_hosts = 1; + out->num_regions_supported = ct3d->dc.num_regions; + stw_le_p(&out->supported_add_sel_policy_bitmask, + BIT(CXL_EXTENT_SELECTION_POLICY_PRESCRIPTIVE)); + stw_le_p(&out->supported_removal_policy_bitmask, + BIT(CXL_EXTENT_REMOVAL_POLICY_PRESCRIPTIVE)); + out->sanitize_on_release_bitmask = 0; + + stq_le_p(&out->total_dynamic_capacity, + ct3d->dc.total_capacity / CXL_CAPACITY_MULTIPLIER); + + for (i = 0; i < ct3d->dc.num_regions; i++) { + region = &ct3d->dc.regions[i]; + memcpy(&out->region_blk_size_bitmasks[i], + ®ion->supported_blk_size_bitmask, + sizeof(out->region_blk_size_bitmasks[i])); + } + + *len_out = sizeof(*out); + return CXL_MBOX_SUCCESS; +} + static const struct cxl_cmd cxl_cmd_set[256][256] = { [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT", cmd_infostat_bg_op_abort, 0, 0 }, @@ -3350,6 +3399,11 @@ static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { cmd_tunnel_management_cmd, ~0, 0 }, }; +static const struct cxl_cmd cxl_cmd_set_fm_dcd[256][256] = { + [FMAPI_DCD_MGMT][GET_DCD_INFO] = { "GET_DCD_INFO", + cmd_fm_get_dcd_info, 0, 0 }, +}; + /* * While the command is executing in the background, the device should * update the percentage complete in the Background Command Status Register @@ -3624,7 +3678,12 @@ void cxl_initialize_t3_fm_owned_ld_mctpcci(CXLCCI *cci, DeviceState *d, DeviceState *intf, size_t payload_max) { + CXLType3Dev *ct3d = CXL_TYPE3(d); + cxl_copy_cci_commands(cci, cxl_cmd_set_t3_fm_owned_ld_mctp); + if (ct3d->dc.num_regions) { + cxl_copy_cci_commands(cci, cxl_cmd_set_fm_dcd); + } cci->d = d; cci->intf = intf; cxl_init_cci(cci, payload_max); diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index f283178d88..d898cfd617 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -8,6 +8,7 @@ * * SPDX-License-Identifier: GPL-v2-only */ +#include #include "qemu/osdep.h" #include "qemu/units.h" @@ -634,6 +635,8 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) uint64_t region_len; uint64_t decode_len; uint64_t blk_size = 2 * MiB; + /* Only 1 block size is supported for now. */ + uint64_t supported_blk_size_bitmask = blk_size; CXLDCRegion *region; MemoryRegion *mr; uint64_t dc_size; @@ -679,6 +682,7 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) .block_size = blk_size, /* dsmad_handle set when creating CDAT table entries */ .flags = 0, + .supported_blk_size_bitmask = supported_blk_size_bitmask, }; ct3d->dc.total_capacity += region->len; region->blk_bitmap = bitmap_new(region->len / region->block_size); diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index a151e19da8..7eade9cf8a 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -530,6 +530,7 @@ typedef struct CXLDCRegion { uint32_t dsmadhandle; uint8_t flags; unsigned long *blk_bitmap; + uint64_t supported_blk_size_bitmask; } CXLDCRegion; typedef struct CXLSetFeatureInfo { From 3d1607cc299f40d0f0749ce808e9776196f7ef85 Mon Sep 17 00:00:00 2001 From: Anisa Su Date: Mon, 14 Jul 2025 18:44:59 +0100 Subject: [PATCH 2428/2760] hw/mem: cxl_type3: Add dsmas_flags to CXLDCRegion struct Add booleans to DC Region struct to represent dsmas flags (defined in CDAT) in preparation for the next command, which returns the flags in the next mailbox command 0x5601. Reviewed-by: Fan Ni Signed-off-by: Anisa Su Signed-off-by: Jonathan Cameron Message-Id: <20250714174509.1984430-4-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/cxl_type3.c | 8 +++++++- include/hw/cxl/cxl_device.h | 15 +++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index d898cfd617..6b0889c9ae 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -226,10 +226,16 @@ static int ct3_build_cdat_table(CDATSubHeader ***cdat_table, void *priv) * future. */ for (i = 0; i < ct3d->dc.num_regions; i++) { + ct3d->dc.regions[i].nonvolatile = false; + ct3d->dc.regions[i].sharable = false; + ct3d->dc.regions[i].hw_managed_coherency = false; + ct3d->dc.regions[i].ic_specific_dc_management = false; + ct3d->dc.regions[i].rdonly = false; ct3_build_cdat_entries_for_mr(&(table[cur_ent]), dsmad_handle++, ct3d->dc.regions[i].len, - false, true, region_base); + ct3d->dc.regions[i].nonvolatile, + true, region_base); ct3d->dc.regions[i].dsmadhandle = dsmad_handle - 1; cur_ent += CT3_CDAT_NUM_ENTRIES; diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 7eade9cf8a..7e0a66906f 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -133,6 +133,15 @@ typedef enum { CXL_MBOX_MAX = 0x20 } CXLRetCode; +/* r3.2 Section 7.6.7.6.2: Table 7-66: DSMAS Flags Bits */ +typedef enum { + CXL_DSMAS_FLAGS_NONVOLATILE = 2, + CXL_DSMAS_FLAGS_SHARABLE = 3, + CXL_DSMAS_FLAGS_HW_MANAGED_COHERENCY = 4, + CXL_DSMAS_FLAGS_IC_SPECIFIC_DC_MANAGEMENT = 5, + CXL_DSMAS_FLAGS_RDONLY = 6, +} CXLDSMASFlags; + typedef struct CXLCCI CXLCCI; typedef struct cxl_device_state CXLDeviceState; struct cxl_cmd; @@ -531,6 +540,12 @@ typedef struct CXLDCRegion { uint8_t flags; unsigned long *blk_bitmap; uint64_t supported_blk_size_bitmask; + /* Following bools make up dsmas flags, as defined in the CDAT */ + bool nonvolatile; + bool sharable; + bool hw_managed_coherency; + bool ic_specific_dc_management; + bool rdonly; } CXLDCRegion; typedef struct CXLSetFeatureInfo { From a8f1cbbbcad6e1b2211a201ad95d68c451a990e3 Mon Sep 17 00:00:00 2001 From: Anisa Su Date: Mon, 14 Jul 2025 18:45:00 +0100 Subject: [PATCH 2429/2760] hw/cxl: mailbox-utils: 0x5601 - FMAPI Get Host Region Config FM DCD Management command 0x5601 implemented per CXL r3.2 Spec Section 7.6.7.6.2 Reviewed-by: Fan Ni Signed-off-by: Anisa Su Signed-off-by: Jonathan Cameron Message-Id: <20250714174509.1984430-5-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 106 +++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 3304048922..bf1710b251 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -120,6 +120,7 @@ enum { #define MANAGEMENT_COMMAND 0x0 FMAPI_DCD_MGMT = 0x56, #define GET_DCD_INFO 0x0 + #define GET_HOST_DC_REGION_CONFIG 0x1 }; /* CCI Message Format CXL r3.1 Figure 7-19 */ @@ -3286,6 +3287,109 @@ static CXLRetCode cmd_fm_get_dcd_info(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +static void build_dsmas_flags(uint8_t *flags, CXLDCRegion *region) +{ + *flags = 0; + + if (region->nonvolatile) { + *flags |= BIT(CXL_DSMAS_FLAGS_NONVOLATILE); + } + if (region->sharable) { + *flags |= BIT(CXL_DSMAS_FLAGS_SHARABLE); + } + if (region->hw_managed_coherency) { + *flags |= BIT(CXL_DSMAS_FLAGS_HW_MANAGED_COHERENCY); + } + if (region->ic_specific_dc_management) { + *flags |= BIT(CXL_DSMAS_FLAGS_IC_SPECIFIC_DC_MANAGEMENT); + } + if (region->rdonly) { + *flags |= BIT(CXL_DSMAS_FLAGS_RDONLY); + } +} + +/* + * CXL r3.2 section 7.6.7.6.2: + * Get Host DC Region Configuration (Opcode 5601h) + */ +static CXLRetCode cmd_fm_get_host_dc_region_config(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint16_t host_id; + uint8_t region_cnt; + uint8_t start_rid; + } QEMU_PACKED *in = (void *)payload_in; + struct { + uint16_t host_id; + uint8_t num_regions; + uint8_t regions_returned; + struct { + uint64_t base; + uint64_t decode_len; + uint64_t region_len; + uint64_t block_size; + uint8_t flags; + uint8_t rsvd1[3]; + uint8_t sanitize; + uint8_t rsvd2[3]; + } QEMU_PACKED records[]; + } QEMU_PACKED *out = (void *)payload_out; + struct { + uint32_t num_extents_supported; + uint32_t num_extents_available; + uint32_t num_tags_supported; + uint32_t num_tags_available; + } QEMU_PACKED *extra_out; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + uint16_t record_count, out_pl_len, i; + + if (in->start_rid >= ct3d->dc.num_regions) { + return CXL_MBOX_INVALID_INPUT; + } + record_count = MIN(ct3d->dc.num_regions - in->start_rid, in->region_cnt); + + out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); + extra_out = (void *)out + out_pl_len; + out_pl_len += sizeof(*extra_out); + + assert(out_pl_len <= CXL_MAILBOX_MAX_PAYLOAD_SIZE); + + stw_le_p(&out->host_id, 0); + out->num_regions = ct3d->dc.num_regions; + out->regions_returned = record_count; + + for (i = 0; i < record_count; i++) { + stq_le_p(&out->records[i].base, + ct3d->dc.regions[in->start_rid + i].base); + stq_le_p(&out->records[i].decode_len, + ct3d->dc.regions[in->start_rid + i].decode_len / + CXL_CAPACITY_MULTIPLIER); + stq_le_p(&out->records[i].region_len, + ct3d->dc.regions[in->start_rid + i].len); + stq_le_p(&out->records[i].block_size, + ct3d->dc.regions[in->start_rid + i].block_size); + build_dsmas_flags(&out->records[i].flags, + &ct3d->dc.regions[in->start_rid + i]); + /* Sanitize is bit 0 of flags. */ + out->records[i].sanitize = + ct3d->dc.regions[in->start_rid + i].flags & BIT(0); + } + + stl_le_p(&extra_out->num_extents_supported, CXL_NUM_EXTENTS_SUPPORTED); + stl_le_p(&extra_out->num_extents_available, CXL_NUM_EXTENTS_SUPPORTED - + ct3d->dc.total_extent_count); + stl_le_p(&extra_out->num_tags_supported, CXL_NUM_TAGS_SUPPORTED); + stl_le_p(&extra_out->num_tags_available, CXL_NUM_TAGS_SUPPORTED); + + *len_out = out_pl_len; + return CXL_MBOX_SUCCESS; +} + static const struct cxl_cmd cxl_cmd_set[256][256] = { [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT", cmd_infostat_bg_op_abort, 0, 0 }, @@ -3402,6 +3506,8 @@ static const struct cxl_cmd cxl_cmd_set_sw[256][256] = { static const struct cxl_cmd cxl_cmd_set_fm_dcd[256][256] = { [FMAPI_DCD_MGMT][GET_DCD_INFO] = { "GET_DCD_INFO", cmd_fm_get_dcd_info, 0, 0 }, + [FMAPI_DCD_MGMT][GET_HOST_DC_REGION_CONFIG] = { "GET_HOST_DC_REGION_CONFIG", + cmd_fm_get_host_dc_region_config, 4, 0 }, }; /* From 0686f160b767615279ca39cfebf77c0b2e67816d Mon Sep 17 00:00:00 2001 From: Anisa Su Date: Mon, 14 Jul 2025 18:45:01 +0100 Subject: [PATCH 2430/2760] hw/cxl: Move definition for dynamic_capacity_uuid and enum for DC event types to header Move definition/enum to cxl_events.h for shared use in next patch Reviewed-by: Fan Ni Signed-off-by: Anisa Su Signed-off-by: Jonathan Cameron Message-Id: <20250714174509.1984430-6-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/cxl_type3.c | 15 --------------- include/hw/cxl/cxl_events.h | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 6b0889c9ae..52d3910ed9 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1876,21 +1876,6 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, } } -/* CXL r3.1 Table 8-50: Dynamic Capacity Event Record */ -static const QemuUUID dynamic_capacity_uuid = { - .data = UUID(0xca95afa7, 0xf183, 0x4018, 0x8c, 0x2f, - 0x95, 0x26, 0x8e, 0x10, 0x1a, 0x2a), -}; - -typedef enum CXLDCEventType { - DC_EVENT_ADD_CAPACITY = 0x0, - DC_EVENT_RELEASE_CAPACITY = 0x1, - DC_EVENT_FORCED_RELEASE_CAPACITY = 0x2, - DC_EVENT_REGION_CONFIG_UPDATED = 0x3, - DC_EVENT_ADD_CAPACITY_RSP = 0x4, - DC_EVENT_CAPACITY_RELEASED = 0x5, -} CXLDCEventType; - /* * Check whether the range [dpa, dpa + len - 1] has overlaps with extents in * the list. diff --git a/include/hw/cxl/cxl_events.h b/include/hw/cxl/cxl_events.h index 38cadaa0f3..758b075a64 100644 --- a/include/hw/cxl/cxl_events.h +++ b/include/hw/cxl/cxl_events.h @@ -184,4 +184,19 @@ typedef struct CXLEventDynamicCapacity { uint32_t tags_avail; } QEMU_PACKED CXLEventDynamicCapacity; +/* CXL r3.1 Table 8-50: Dynamic Capacity Event Record */ +static const QemuUUID dynamic_capacity_uuid = { + .data = UUID(0xca95afa7, 0xf183, 0x4018, 0x8c, 0x2f, + 0x95, 0x26, 0x8e, 0x10, 0x1a, 0x2a), +}; + +typedef enum CXLDCEventType { + DC_EVENT_ADD_CAPACITY = 0x0, + DC_EVENT_RELEASE_CAPACITY = 0x1, + DC_EVENT_FORCED_RELEASE_CAPACITY = 0x2, + DC_EVENT_REGION_CONFIG_UPDATED = 0x3, + DC_EVENT_ADD_CAPACITY_RSP = 0x4, + DC_EVENT_CAPACITY_RELEASED = 0x5, +} CXLDCEventType; + #endif /* CXL_EVENTS_H */ From 69798d000b95a147f73dfce8c80cff92aa929dcb Mon Sep 17 00:00:00 2001 From: Anisa Su Date: Mon, 14 Jul 2025 18:45:02 +0100 Subject: [PATCH 2431/2760] hw/mem: cxl_type3: Add DC Region bitmap lock Add a lock on the bitmap of each CXLDCRegion in preparation for the next patch which implements FMAPI Set DC Region Configuration. This command can modify the block size, which means the region's bitmap must be updated accordingly. The lock becomes necessary when commands that add/release extents (meaning they update the bitmap too) are enabled on a different CCI than the CCI on which the FMAPI commands are enabled. Reviewed-by: Fan Ni Signed-off-by: Anisa Su Signed-off-by: Jonathan Cameron Message-Id: <20250714174509.1984430-7-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/mem/cxl_type3.c | 4 ++++ include/hw/cxl/cxl_device.h | 1 + 2 files changed, 5 insertions(+) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 52d3910ed9..a7a1857a0c 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -692,6 +692,7 @@ static bool cxl_create_dc_regions(CXLType3Dev *ct3d, Error **errp) }; ct3d->dc.total_capacity += region->len; region->blk_bitmap = bitmap_new(region->len / region->block_size); + qemu_mutex_init(®ion->bitmap_lock); } QTAILQ_INIT(&ct3d->dc.extents); QTAILQ_INIT(&ct3d->dc.extents_pending); @@ -1020,6 +1021,7 @@ void ct3_set_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, return; } + QEMU_LOCK_GUARD(®ion->bitmap_lock); bitmap_set(region->blk_bitmap, (dpa - region->base) / region->block_size, len / region->block_size); } @@ -1046,6 +1048,7 @@ bool ct3_test_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, * if bits between [dpa, dpa + len) are all 1s, meaning the DPA range is * backed with DC extents, return true; else return false. */ + QEMU_LOCK_GUARD(®ion->bitmap_lock); return find_next_zero_bit(region->blk_bitmap, nr + nbits, nr) == nr + nbits; } @@ -1067,6 +1070,7 @@ void ct3_clear_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, nr = (dpa - region->base) / region->block_size; nbits = len / region->block_size; + QEMU_LOCK_GUARD(®ion->bitmap_lock); bitmap_clear(region->blk_bitmap, nr, nbits); } diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 7e0a66906f..42ae5b7479 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -540,6 +540,7 @@ typedef struct CXLDCRegion { uint8_t flags; unsigned long *blk_bitmap; uint64_t supported_blk_size_bitmask; + QemuMutex bitmap_lock; /* Following bools make up dsmas flags, as defined in the CDAT */ bool nonvolatile; bool sharable; From 1befc7bde30d2e09f20a44584aaefdfa31f60074 Mon Sep 17 00:00:00 2001 From: Anisa Su Date: Mon, 14 Jul 2025 18:45:03 +0100 Subject: [PATCH 2432/2760] hw/cxl: mailbox-utils: 0x5602 - FMAPI Set DC Region Config FM DCD Management command 0x5602 implemented per CXL r3.2 Spec Section 7.6.7.6.3 Reviewed-by: Fan Ni Signed-off-by: Anisa Su Signed-off-by: Jonathan Cameron Message-Id: <20250714174509.1984430-8-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 87 ++++++++++++++++++++++++++++++++++++ hw/mem/cxl_type3.c | 6 +-- include/hw/cxl/cxl_device.h | 3 ++ include/hw/cxl/cxl_mailbox.h | 6 +++ 4 files changed, 99 insertions(+), 3 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index bf1710b251..b4a0f7d664 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -18,6 +18,7 @@ #include "hw/pci/pci.h" #include "hw/pci-bridge/cxl_upstream_port.h" #include "qemu/cutils.h" +#include "qemu/host-utils.h" #include "qemu/log.h" #include "qemu/units.h" #include "qemu/uuid.h" @@ -121,6 +122,7 @@ enum { FMAPI_DCD_MGMT = 0x56, #define GET_DCD_INFO 0x0 #define GET_HOST_DC_REGION_CONFIG 0x1 + #define SET_DC_REGION_CONFIG 0x2 }; /* CCI Message Format CXL r3.1 Figure 7-19 */ @@ -3390,6 +3392,84 @@ static CXLRetCode cmd_fm_get_host_dc_region_config(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* CXL r3.2 section 7.6.7.6.3: Set Host DC Region Configuration (Opcode 5602) */ +static CXLRetCode cmd_fm_set_dc_region_config(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint8_t reg_id; + uint8_t rsvd[3]; + uint64_t block_sz; + uint8_t flags; + uint8_t rsvd2[3]; + } QEMU_PACKED *in = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLEventDynamicCapacity dcEvent = {}; + CXLDCRegion *region = &ct3d->dc.regions[in->reg_id]; + + /* + * CXL r3.2 7.6.7.6.3: Set DC Region Configuration + * This command shall fail with Unsupported when the Sanitize on Release + * field does not match the region’s configuration... and the device + * does not support reconfiguration of the Sanitize on Release setting. + * + * Currently not reconfigurable, so always fail if sanitize bit (bit 0) + * doesn't match. + */ + if ((in->flags & 0x1) != (region->flags & 0x1)) { + return CXL_MBOX_UNSUPPORTED; + } + + if (in->reg_id >= DCD_MAX_NUM_REGION) { + return CXL_MBOX_UNSUPPORTED; + } + + /* Check that no extents are in the region being reconfigured */ + if (!bitmap_empty(region->blk_bitmap, region->len / region->block_size)) { + return CXL_MBOX_UNSUPPORTED; + } + + /* Check that new block size is supported */ + if (!is_power_of_2(in->block_sz) || + !(in->block_sz & region->supported_blk_size_bitmask)) { + return CXL_MBOX_INVALID_INPUT; + } + + /* Return success if new block size == current block size */ + if (in->block_sz == region->block_size) { + return CXL_MBOX_SUCCESS; + } + + /* Free bitmap and create new one for new block size. */ + qemu_mutex_lock(®ion->bitmap_lock); + g_free(region->blk_bitmap); + region->blk_bitmap = bitmap_new(region->len / in->block_sz); + qemu_mutex_unlock(®ion->bitmap_lock); + region->block_size = in->block_sz; + + /* Create event record and insert into event log */ + cxl_assign_event_header(&dcEvent.hdr, + &dynamic_capacity_uuid, + (1 << CXL_EVENT_TYPE_INFO), + sizeof(dcEvent), + cxl_device_get_timestamp(&ct3d->cxl_dstate)); + dcEvent.type = DC_EVENT_REGION_CONFIG_UPDATED; + dcEvent.validity_flags = 1; + dcEvent.host_id = 0; + dcEvent.updated_region_id = in->reg_id; + + if (cxl_event_insert(&ct3d->cxl_dstate, + CXL_EVENT_TYPE_DYNAMIC_CAP, + (CXLEventRecordRaw *)&dcEvent)) { + cxl_event_irq_assert(ct3d); + } + return CXL_MBOX_SUCCESS; +} + static const struct cxl_cmd cxl_cmd_set[256][256] = { [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT", cmd_infostat_bg_op_abort, 0, 0 }, @@ -3508,6 +3588,13 @@ static const struct cxl_cmd cxl_cmd_set_fm_dcd[256][256] = { cmd_fm_get_dcd_info, 0, 0 }, [FMAPI_DCD_MGMT][GET_HOST_DC_REGION_CONFIG] = { "GET_HOST_DC_REGION_CONFIG", cmd_fm_get_host_dc_region_config, 4, 0 }, + [FMAPI_DCD_MGMT][SET_DC_REGION_CONFIG] = { "SET_DC_REGION_CONFIG", + cmd_fm_set_dc_region_config, 16, + (CXL_MBOX_CONFIG_CHANGE_COLD_RESET | + CXL_MBOX_CONFIG_CHANGE_CONV_RESET | + CXL_MBOX_CONFIG_CHANGE_CXL_RESET | + CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | + CXL_MBOX_IMMEDIATE_DATA_CHANGE) }, }; /* diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index a7a1857a0c..4a45b4510e 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1590,9 +1590,9 @@ void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type, pcie_aer_inject_error(PCI_DEVICE(obj), &err); } -static void cxl_assign_event_header(CXLEventRecordHdr *hdr, - const QemuUUID *uuid, uint32_t flags, - uint8_t length, uint64_t timestamp) +void cxl_assign_event_header(CXLEventRecordHdr *hdr, + const QemuUUID *uuid, uint32_t flags, + uint8_t length, uint64_t timestamp) { st24_le_p(&hdr->flags, flags); hdr->length = length; diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 42ae5b7479..c836fc17f0 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -721,4 +721,7 @@ void ct3_clear_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len); bool ct3_test_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, uint64_t len); +void cxl_assign_event_header(CXLEventRecordHdr *hdr, + const QemuUUID *uuid, uint32_t flags, + uint8_t length, uint64_t timestamp); #endif diff --git a/include/hw/cxl/cxl_mailbox.h b/include/hw/cxl/cxl_mailbox.h index 9008402d1c..a05d7cb5b7 100644 --- a/include/hw/cxl/cxl_mailbox.h +++ b/include/hw/cxl/cxl_mailbox.h @@ -8,6 +8,7 @@ #ifndef CXL_MAILBOX_H #define CXL_MAILBOX_H +#define CXL_MBOX_CONFIG_CHANGE_COLD_RESET (1) #define CXL_MBOX_IMMEDIATE_CONFIG_CHANGE (1 << 1) #define CXL_MBOX_IMMEDIATE_DATA_CHANGE (1 << 2) #define CXL_MBOX_IMMEDIATE_POLICY_CHANGE (1 << 3) @@ -15,5 +16,10 @@ #define CXL_MBOX_SECURITY_STATE_CHANGE (1 << 5) #define CXL_MBOX_BACKGROUND_OPERATION (1 << 6) #define CXL_MBOX_BACKGROUND_OPERATION_ABORT (1 << 7) +#define CXL_MBOX_SECONDARY_MBOX_SUPPORTED (1 << 8) +#define CXL_MBOX_REQUEST_ABORT_BACKGROUND_OP_SUPPORTED (1 << 9) +#define CXL_MBOX_CEL_10_TO_11_VALID (1 << 10) +#define CXL_MBOX_CONFIG_CHANGE_CONV_RESET (1 << 11) +#define CXL_MBOX_CONFIG_CHANGE_CXL_RESET (1 << 12) #endif From 21af9a917eca69996891dcbb93edcb6c5c53e52c Mon Sep 17 00:00:00 2001 From: Anisa Su Date: Mon, 14 Jul 2025 18:45:04 +0100 Subject: [PATCH 2433/2760] hw/cxl: mailbox-utils: 0x5603 - FMAPI Get DC Region Extent Lists FM DCD Management command 0x5603 implemented per CXL r3.2 Spec Section 7.6.7.6.4 Very similar to previously implemented command 0x4801. Reviewed-by: Fan Ni Signed-off-by: Anisa Su Signed-off-by: Jonathan Cameron Message-Id: <20250714174509.1984430-9-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 76 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index b4a0f7d664..4b0fdbbdd8 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -123,6 +123,7 @@ enum { #define GET_DCD_INFO 0x0 #define GET_HOST_DC_REGION_CONFIG 0x1 #define SET_DC_REGION_CONFIG 0x2 + #define GET_DC_REGION_EXTENT_LIST 0x3 }; /* CCI Message Format CXL r3.1 Figure 7-19 */ @@ -3470,6 +3471,79 @@ static CXLRetCode cmd_fm_set_dc_region_config(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* CXL r3.2 section 7.6.7.6.4: Get DC Region Extent Lists (Opcode 5603h) */ +static CXLRetCode cmd_fm_get_dc_region_extent_list(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint16_t host_id; + uint8_t rsvd[2]; + uint32_t extent_cnt; + uint32_t start_extent_id; + } QEMU_PACKED *in = (void *)payload_in; + struct { + uint16_t host_id; + uint8_t rsvd[2]; + uint32_t start_extent_id; + uint32_t extents_returned; + uint32_t total_extents; + uint32_t list_generation_num; + uint8_t rsvd2[4]; + CXLDCExtentRaw records[]; + } QEMU_PACKED *out = (void *)payload_out; + QEMU_BUILD_BUG_ON(sizeof(*in) != 0xc); + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + CXLDCExtent *ent; + CXLDCExtentRaw *out_rec; + uint16_t record_count = 0, record_done = 0, i = 0; + uint16_t out_pl_len, max_size; + + if (in->host_id != 0) { + return CXL_MBOX_INVALID_INPUT; + } + + if (in->start_extent_id > ct3d->dc.nr_extents_accepted) { + return CXL_MBOX_INVALID_INPUT; + } + + record_count = MIN(in->extent_cnt, + ct3d->dc.nr_extents_accepted - in->start_extent_id); + max_size = CXL_MAILBOX_MAX_PAYLOAD_SIZE - sizeof(*out); + record_count = MIN(record_count, max_size / sizeof(out->records[0])); + out_pl_len = sizeof(*out) + record_count * sizeof(out->records[0]); + + stw_le_p(&out->host_id, in->host_id); + stl_le_p(&out->start_extent_id, in->start_extent_id); + stl_le_p(&out->extents_returned, record_count); + stl_le_p(&out->total_extents, ct3d->dc.nr_extents_accepted); + stl_le_p(&out->list_generation_num, ct3d->dc.ext_list_gen_seq); + + if (record_count > 0) { + QTAILQ_FOREACH(ent, &ct3d->dc.extents, node) { + if (i++ < in->start_extent_id) { + continue; + } + out_rec = &out->records[record_done]; + stq_le_p(&out_rec->start_dpa, ent->start_dpa); + stq_le_p(&out_rec->len, ent->len); + memcpy(&out_rec->tag, ent->tag, 0x10); + stw_le_p(&out_rec->shared_seq, ent->shared_seq); + + record_done++; + if (record_done == record_count) { + break; + } + } + } + + *len_out = out_pl_len; + return CXL_MBOX_SUCCESS; +} + static const struct cxl_cmd cxl_cmd_set[256][256] = { [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT", cmd_infostat_bg_op_abort, 0, 0 }, @@ -3595,6 +3669,8 @@ static const struct cxl_cmd cxl_cmd_set_fm_dcd[256][256] = { CXL_MBOX_CONFIG_CHANGE_CXL_RESET | CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | CXL_MBOX_IMMEDIATE_DATA_CHANGE) }, + [FMAPI_DCD_MGMT][GET_DC_REGION_EXTENT_LIST] = { "GET_DC_REGION_EXTENT_LIST", + cmd_fm_get_dc_region_extent_list, 12, 0 }, }; /* From 0a362772e0cff8745d0bda73757ba4c7cfbbc841 Mon Sep 17 00:00:00 2001 From: Anisa Su Date: Mon, 14 Jul 2025 18:45:05 +0100 Subject: [PATCH 2434/2760] hw/cxl: Create helper function to create DC Event Records from extents Prepatory patch for following FMAPI Add/Release Patches. Refactors part of qmp_cxl_process_dynamic_capacity_prescriptive() into a helper function to create DC Event Records and insert in the event log. Moves definition for CXL_NUM_EXTENTS_SUPPORTED to cxl.h so it can be accessed by cxl-mailbox-utils.c and cxl-events.c, where the helper function is defined. Reviewed-by: Fan Ni Signed-off-by: Anisa Su Signed-off-by: Jonathan Cameron Message-Id: <20250714174509.1984430-10-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-events.c | 38 +++++++++++++++++++++++++++++++++++++ hw/cxl/cxl-mailbox-utils.c | 1 - hw/mem/cxl_type3.c | 37 +----------------------------------- include/hw/cxl/cxl.h | 1 + include/hw/cxl/cxl_device.h | 4 ++++ 5 files changed, 44 insertions(+), 37 deletions(-) diff --git a/hw/cxl/cxl-events.c b/hw/cxl/cxl-events.c index f90470930d..7583dd9162 100644 --- a/hw/cxl/cxl-events.c +++ b/hw/cxl/cxl-events.c @@ -258,3 +258,41 @@ void cxl_event_irq_assert(CXLType3Dev *ct3d) } } } + +void cxl_create_dc_event_records_for_extents(CXLType3Dev *ct3d, + CXLDCEventType type, + CXLDCExtentRaw extents[], + uint32_t ext_count) +{ + CXLEventDynamicCapacity event_rec = {}; + int i; + + cxl_assign_event_header(&event_rec.hdr, + &dynamic_capacity_uuid, + (1 << CXL_EVENT_TYPE_INFO), + sizeof(event_rec), + cxl_device_get_timestamp(&ct3d->cxl_dstate)); + event_rec.type = type; + event_rec.validity_flags = 1; + event_rec.host_id = 0; + event_rec.updated_region_id = 0; + event_rec.extents_avail = CXL_NUM_EXTENTS_SUPPORTED - + ct3d->dc.total_extent_count; + + for (i = 0; i < ext_count; i++) { + memcpy(&event_rec.dynamic_capacity_extent, + &extents[i], + sizeof(CXLDCExtentRaw)); + event_rec.flags = 0; + if (i < ext_count - 1) { + /* Set "More" flag */ + event_rec.flags |= BIT(0); + } + + if (cxl_event_insert(&ct3d->cxl_dstate, + CXL_EVENT_TYPE_DYNAMIC_CAP, + (CXLEventRecordRaw *)&event_rec)) { + cxl_event_irq_assert(ct3d); + } + } +} diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 4b0fdbbdd8..b6f30e8689 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -28,7 +28,6 @@ #define CXL_CAPACITY_MULTIPLIER (256 * MiB) #define CXL_DC_EVENT_LOG_SIZE 8 -#define CXL_NUM_EXTENTS_SUPPORTED 512 #define CXL_NUM_TAGS_SUPPORTED 0 #define CXL_ALERTS_LIFE_USED_WARN_THRESH (1 << 0) #define CXL_ALERTS_OVER_TEMP_WARN_THRESH (1 << 1) diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 4a45b4510e..4e975b1a42 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1957,15 +1957,11 @@ static void qmp_cxl_process_dynamic_capacity_prescriptive(const char *path, CxlDynamicCapacityExtentList *records, Error **errp) { Object *obj; - CXLEventDynamicCapacity dCap = {}; - CXLEventRecordHdr *hdr = &dCap.hdr; CXLType3Dev *dcd; - uint8_t flags = 1 << CXL_EVENT_TYPE_INFO; uint32_t num_extents = 0; CxlDynamicCapacityExtentList *list; CXLDCExtentGroup *group = NULL; g_autofree CXLDCExtentRaw *extents = NULL; - uint8_t enc_log = CXL_EVENT_TYPE_DYNAMIC_CAP; uint64_t dpa, offset, len, block_size; g_autofree unsigned long *blk_bitmap = NULL; int i; @@ -2078,38 +2074,7 @@ static void qmp_cxl_process_dynamic_capacity_prescriptive(const char *path, dcd->dc.total_extent_count += num_extents; } - /* - * CXL r3.1 section 8.2.9.2.1.6: Dynamic Capacity Event Record - * - * All Dynamic Capacity event records shall set the Event Record Severity - * field in the Common Event Record Format to Informational Event. All - * Dynamic Capacity related events shall be logged in the Dynamic Capacity - * Event Log. - */ - cxl_assign_event_header(hdr, &dynamic_capacity_uuid, flags, sizeof(dCap), - cxl_device_get_timestamp(&dcd->cxl_dstate)); - - dCap.type = type; - /* FIXME: for now, validity flag is cleared */ - dCap.validity_flags = 0; - stw_le_p(&dCap.host_id, hid); - /* only valid for DC_REGION_CONFIG_UPDATED event */ - dCap.updated_region_id = 0; - for (i = 0; i < num_extents; i++) { - memcpy(&dCap.dynamic_capacity_extent, &extents[i], - sizeof(CXLDCExtentRaw)); - - dCap.flags = 0; - if (i < num_extents - 1) { - /* Set "More" flag */ - dCap.flags |= BIT(0); - } - - if (cxl_event_insert(&dcd->cxl_dstate, enc_log, - (CXLEventRecordRaw *)&dCap)) { - cxl_event_irq_assert(dcd); - } - } + cxl_create_dc_event_records_for_extents(dcd, type, extents, num_extents); } void qmp_cxl_add_dynamic_capacity(const char *path, uint16_t host_id, diff --git a/include/hw/cxl/cxl.h b/include/hw/cxl/cxl.h index de66ab8c35..998f495a98 100644 --- a/include/hw/cxl/cxl.h +++ b/include/hw/cxl/cxl.h @@ -23,6 +23,7 @@ #define CXL_DEVICE_REG_BAR_IDX 2 #define CXL_WINDOW_MAX 10 +#define CXL_NUM_EXTENTS_SUPPORTED 512 typedef struct PXBCXLDev PXBCXLDev; diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index c836fc17f0..71a5834c3d 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -724,4 +724,8 @@ bool ct3_test_region_block_backed(CXLType3Dev *ct3d, uint64_t dpa, void cxl_assign_event_header(CXLEventRecordHdr *hdr, const QemuUUID *uuid, uint32_t flags, uint8_t length, uint64_t timestamp); +void cxl_create_dc_event_records_for_extents(CXLType3Dev *ct3d, + CXLDCEventType type, + CXLDCExtentRaw extents[], + uint32_t ext_count); #endif From 42cbc937d4f6502b398674e38e393f354db52c4c Mon Sep 17 00:00:00 2001 From: Anisa Su Date: Mon, 14 Jul 2025 18:45:06 +0100 Subject: [PATCH 2435/2760] hw/cxl: mailbox-utils: 0x5604 - FMAPI Initiate DC Add FM DCD Management command 0x5604 implemented per CXL r3.2 Spec Section 7.6.7.6.5 Reviewed-by: Fan Ni Signed-off-by: Anisa Su Signed-off-by: Jonathan Cameron Message-Id: <20250714174509.1984430-11-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 109 ++++++++++++++++++++++++++++++++++++ hw/mem/cxl_type3.c | 8 +-- include/hw/cxl/cxl_device.h | 4 ++ 3 files changed, 117 insertions(+), 4 deletions(-) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index b6f30e8689..5d08525529 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -123,6 +123,7 @@ enum { #define GET_HOST_DC_REGION_CONFIG 0x1 #define SET_DC_REGION_CONFIG 0x2 #define GET_DC_REGION_EXTENT_LIST 0x3 + #define INITIATE_DC_ADD 0x4 }; /* CCI Message Format CXL r3.1 Figure 7-19 */ @@ -3543,6 +3544,107 @@ static CXLRetCode cmd_fm_get_dc_region_extent_list(const struct cxl_cmd *cmd, return CXL_MBOX_SUCCESS; } +/* + * Helper function to convert CXLDCExtentRaw to CXLUpdateDCExtentListInPl + * in order to reuse cxl_detect_malformed_extent_list() function which accepts + * CXLUpdateDCExtentListInPl as a parameter. + */ +static void convert_raw_extents(CXLDCExtentRaw raw_extents[], + CXLUpdateDCExtentListInPl *extent_list, + int count) +{ + int i; + + extent_list->num_entries_updated = count; + + for (i = 0; i < count; i++) { + extent_list->updated_entries[i].start_dpa = raw_extents[i].start_dpa; + extent_list->updated_entries[i].len = raw_extents[i].len; + } +} + +/* CXL r3.2 Section 7.6.7.6.5: Initiate Dynamic Capacity Add (Opcode 5604h) */ +static CXLRetCode cmd_fm_initiate_dc_add(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint16_t host_id; + uint8_t selection_policy; + uint8_t reg_num; + uint64_t length; + uint8_t tag[0x10]; + uint32_t ext_count; + CXLDCExtentRaw extents[]; + } QEMU_PACKED *in = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + int i, rc; + + switch (in->selection_policy) { + case CXL_EXTENT_SELECTION_POLICY_PRESCRIPTIVE: { + /* Adding extents exceeds device's extent tracking ability. */ + if (in->ext_count + ct3d->dc.total_extent_count > + CXL_NUM_EXTENTS_SUPPORTED) { + return CXL_MBOX_RESOURCES_EXHAUSTED; + } + + g_autofree CXLUpdateDCExtentListInPl *list = + g_malloc0(sizeof(*list) + + in->ext_count * sizeof(*list->updated_entries)); + + convert_raw_extents(in->extents, list, in->ext_count); + rc = cxl_detect_malformed_extent_list(ct3d, list); + + for (i = 0; i < in->ext_count; i++) { + CXLDCExtentRaw *ext = &in->extents[i]; + + /* Check requested extents do not overlap with pending ones. */ + if (cxl_extent_groups_overlaps_dpa_range(&ct3d->dc.extents_pending, + ext->start_dpa, + ext->len)) { + return CXL_MBOX_INVALID_EXTENT_LIST; + } + /* Check requested extents do not overlap with existing ones. */ + if (cxl_extents_overlaps_dpa_range(&ct3d->dc.extents, + ext->start_dpa, + ext->len)) { + return CXL_MBOX_INVALID_EXTENT_LIST; + } + } + + if (rc) { + return rc; + } + + CXLDCExtentGroup *group = NULL; + for (i = 0; i < in->ext_count; i++) { + CXLDCExtentRaw *ext = &in->extents[i]; + + group = cxl_insert_extent_to_extent_group(group, ext->start_dpa, + ext->len, ext->tag, + ext->shared_seq); + } + + cxl_extent_group_list_insert_tail(&ct3d->dc.extents_pending, group); + ct3d->dc.total_extent_count += in->ext_count; + cxl_create_dc_event_records_for_extents(ct3d, + DC_EVENT_ADD_CAPACITY, + in->extents, + in->ext_count); + + return CXL_MBOX_SUCCESS; + } + default: { + qemu_log_mask(LOG_UNIMP, + "CXL extent selection policy not supported.\n"); + return CXL_MBOX_INVALID_INPUT; + } + } +} + static const struct cxl_cmd cxl_cmd_set[256][256] = { [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT", cmd_infostat_bg_op_abort, 0, 0 }, @@ -3670,6 +3772,13 @@ static const struct cxl_cmd cxl_cmd_set_fm_dcd[256][256] = { CXL_MBOX_IMMEDIATE_DATA_CHANGE) }, [FMAPI_DCD_MGMT][GET_DC_REGION_EXTENT_LIST] = { "GET_DC_REGION_EXTENT_LIST", cmd_fm_get_dc_region_extent_list, 12, 0 }, + [FMAPI_DCD_MGMT][INITIATE_DC_ADD] = { "INIT_DC_ADD", + cmd_fm_initiate_dc_add, ~0, + (CXL_MBOX_CONFIG_CHANGE_COLD_RESET | + CXL_MBOX_CONFIG_CHANGE_CONV_RESET | + CXL_MBOX_CONFIG_CHANGE_CXL_RESET | + CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | + CXL_MBOX_IMMEDIATE_DATA_CHANGE) }, }; /* diff --git a/hw/mem/cxl_type3.c b/hw/mem/cxl_type3.c index 4e975b1a42..be609ff9d0 100644 --- a/hw/mem/cxl_type3.c +++ b/hw/mem/cxl_type3.c @@ -1885,8 +1885,8 @@ void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log, * the list. * Return value: return true if has overlaps; otherwise, return false */ -static bool cxl_extents_overlaps_dpa_range(CXLDCExtentList *list, - uint64_t dpa, uint64_t len) +bool cxl_extents_overlaps_dpa_range(CXLDCExtentList *list, + uint64_t dpa, uint64_t len) { CXLDCExtent *ent; Range range1, range2; @@ -1931,8 +1931,8 @@ bool cxl_extents_contains_dpa_range(CXLDCExtentList *list, return false; } -static bool cxl_extent_groups_overlaps_dpa_range(CXLDCExtentGroupList *list, - uint64_t dpa, uint64_t len) +bool cxl_extent_groups_overlaps_dpa_range(CXLDCExtentGroupList *list, + uint64_t dpa, uint64_t len) { CXLDCExtentGroup *group; diff --git a/include/hw/cxl/cxl_device.h b/include/hw/cxl/cxl_device.h index 71a5834c3d..89411c8093 100644 --- a/include/hw/cxl/cxl_device.h +++ b/include/hw/cxl/cxl_device.h @@ -728,4 +728,8 @@ void cxl_create_dc_event_records_for_extents(CXLType3Dev *ct3d, CXLDCEventType type, CXLDCExtentRaw extents[], uint32_t ext_count); +bool cxl_extents_overlaps_dpa_range(CXLDCExtentList *list, + uint64_t dpa, uint64_t len); +bool cxl_extent_groups_overlaps_dpa_range(CXLDCExtentGroupList *list, + uint64_t dpa, uint64_t len); #endif From fdd48ce20ce125cc992dfaace925f1bfae3dfdc1 Mon Sep 17 00:00:00 2001 From: Anisa Su Date: Mon, 14 Jul 2025 18:45:07 +0100 Subject: [PATCH 2436/2760] hw/cxl: mailbox-utils: 0x5605 - FMAPI Initiate DC Release FM DCD Management command 0x5605 implemented per CXL r3.2 Spec Section 7.6.7.6.6 Reviewed-by: Fan Ni Signed-off-by: Anisa Su Signed-off-by: Jonathan Cameron Message-Id: <20250714174509.1984430-12-Jonathan.Cameron@huawei.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/cxl/cxl-mailbox-utils.c | 88 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c index 5d08525529..68c7cc9891 100644 --- a/hw/cxl/cxl-mailbox-utils.c +++ b/hw/cxl/cxl-mailbox-utils.c @@ -124,6 +124,7 @@ enum { #define SET_DC_REGION_CONFIG 0x2 #define GET_DC_REGION_EXTENT_LIST 0x3 #define INITIATE_DC_ADD 0x4 + #define INITIATE_DC_RELEASE 0x5 }; /* CCI Message Format CXL r3.1 Figure 7-19 */ @@ -3645,6 +3646,86 @@ static CXLRetCode cmd_fm_initiate_dc_add(const struct cxl_cmd *cmd, } } +#define CXL_EXTENT_REMOVAL_POLICY_MASK 0x0F +#define CXL_FORCED_REMOVAL_MASK (1 << 4) +/* + * CXL r3.2 Section 7.6.7.6.6: + * Initiate Dynamic Capacity Release (Opcode 5605h) + */ +static CXLRetCode cmd_fm_initiate_dc_release(const struct cxl_cmd *cmd, + uint8_t *payload_in, + size_t len_in, + uint8_t *payload_out, + size_t *len_out, + CXLCCI *cci) +{ + struct { + uint16_t host_id; + uint8_t flags; + uint8_t reg_num; + uint64_t length; + uint8_t tag[0x10]; + uint32_t ext_count; + CXLDCExtentRaw extents[]; + } QEMU_PACKED *in = (void *)payload_in; + CXLType3Dev *ct3d = CXL_TYPE3(cci->d); + int i, rc; + + switch (in->flags & CXL_EXTENT_REMOVAL_POLICY_MASK) { + case CXL_EXTENT_REMOVAL_POLICY_PRESCRIPTIVE: { + CXLDCExtentList updated_list; + uint32_t updated_list_size; + g_autofree CXLUpdateDCExtentListInPl *list = + g_malloc0(sizeof(*list) + + in->ext_count * sizeof(*list->updated_entries)); + + convert_raw_extents(in->extents, list, in->ext_count); + rc = cxl_detect_malformed_extent_list(ct3d, list); + if (rc) { + return rc; + } + + /* + * Fail with Invalid PA if an extent is pending and Forced Removal + * flag not set. + */ + if (!(in->flags & CXL_FORCED_REMOVAL_MASK)) { + for (i = 0; i < in->ext_count; i++) { + CXLDCExtentRaw ext = in->extents[i]; + /* + * Check requested extents don't overlap with pending + * extents. + */ + if (cxl_extent_groups_overlaps_dpa_range( + &ct3d->dc.extents_pending, + ext.start_dpa, + ext.len)) { + return CXL_MBOX_INVALID_PA; + } + } + } + + rc = cxl_dc_extent_release_dry_run(ct3d, + list, + &updated_list, + &updated_list_size); + if (rc) { + return rc; + } + cxl_create_dc_event_records_for_extents(ct3d, + DC_EVENT_RELEASE_CAPACITY, + in->extents, + in->ext_count); + return CXL_MBOX_SUCCESS; + } + default: { + qemu_log_mask(LOG_UNIMP, + "CXL extent removal policy not supported.\n"); + return CXL_MBOX_INVALID_INPUT; + } + } +} + static const struct cxl_cmd cxl_cmd_set[256][256] = { [INFOSTAT][BACKGROUND_OPERATION_ABORT] = { "BACKGROUND_OPERATION_ABORT", cmd_infostat_bg_op_abort, 0, 0 }, @@ -3779,6 +3860,13 @@ static const struct cxl_cmd cxl_cmd_set_fm_dcd[256][256] = { CXL_MBOX_CONFIG_CHANGE_CXL_RESET | CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | CXL_MBOX_IMMEDIATE_DATA_CHANGE) }, + [FMAPI_DCD_MGMT][INITIATE_DC_RELEASE] = { "INIT_DC_RELEASE", + cmd_fm_initiate_dc_release, ~0, + (CXL_MBOX_CONFIG_CHANGE_COLD_RESET | + CXL_MBOX_CONFIG_CHANGE_CONV_RESET | + CXL_MBOX_CONFIG_CHANGE_CXL_RESET | + CXL_MBOX_IMMEDIATE_CONFIG_CHANGE | + CXL_MBOX_IMMEDIATE_DATA_CHANGE) }, }; /* From 0c1a109fe6d1bcad98f217dd9ce8767b07039df0 Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 15 Jul 2025 13:59:51 +0200 Subject: [PATCH 2437/2760] =?UTF-8?q?hw/vfio-user:=20add=20C=C3=A9dric=20L?= =?UTF-8?q?e=20Goater=20as=20a=20maintainer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: John Levon Acked-by: Mark Cave-Ayland Link: https://lore.kernel.org/qemu-devel/20250715115954.515819-2-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index e88ed2c0a9..30e9b71e6e 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4287,6 +4287,7 @@ F: tests/functional/test_multiprocess.py VFIO-USER: M: John Levon M: Thanos Makatos +M: Cédric Le Goater S: Supported F: docs/interop/vfio-user.rst F: docs/system/devices/vfio-user.rst From 09353802f0021af9f13ebe9336e1994da4505626 Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 15 Jul 2025 13:59:52 +0200 Subject: [PATCH 2438/2760] hw/vfio: fix region fd initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We were not initializing the region fd array to -1, so we would accidentally try to close(0) on cleanup for any region that is not referenced. Fixes: 95cdb024 ("vfio: add region info cache") Signed-off-by: John Levon Reviewed-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250715115954.515819-3-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio/device.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/vfio/device.c b/hw/vfio/device.c index 96cf21462c..52a1996dc4 100644 --- a/hw/vfio/device.c +++ b/hw/vfio/device.c @@ -463,6 +463,8 @@ void vfio_device_detach(VFIODevice *vbasedev) void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainerBase *bcontainer, struct vfio_device_info *info) { + int i; + vbasedev->num_irqs = info->num_irqs; vbasedev->num_regions = info->num_regions; vbasedev->flags = info->flags; @@ -477,6 +479,9 @@ void vfio_device_prepare(VFIODevice *vbasedev, VFIOContainerBase *bcontainer, vbasedev->num_regions); if (vbasedev->use_region_fds) { vbasedev->region_fds = g_new0(int, vbasedev->num_regions); + for (i = 0; i < vbasedev->num_regions; i++) { + vbasedev->region_fds[i] = -1; + } } } @@ -489,7 +494,6 @@ void vfio_device_unprepare(VFIODevice *vbasedev) if (vbasedev->region_fds != NULL && vbasedev->region_fds[i] != -1) { close(vbasedev->region_fds[i]); } - } g_clear_pointer(&vbasedev->reginfo, g_free); From ea6788440df37495de6e257ca204cdd669d32b83 Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 15 Jul 2025 13:59:53 +0200 Subject: [PATCH 2439/2760] hw/vfio-user: wait for proxy close correctly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity reported: CID 1611806: Concurrent data access violations (BAD_CHECK_OF_WAIT_COND) A wait is performed without a loop. If there is a spurious wakeup, the condition may not be satisfied. Fix this by checking ->state for VFIO_PROXY_CLOSED in a loop. Also rename the callback for clarity. Signed-off-by: John Levon Reviewed-by: Mark Cave-Ayland Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250715115954.515819-4-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/proxy.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hw/vfio-user/proxy.c b/hw/vfio-user/proxy.c index c418954440..2275d3fe39 100644 --- a/hw/vfio-user/proxy.c +++ b/hw/vfio-user/proxy.c @@ -32,7 +32,6 @@ static void vfio_user_recycle(VFIOUserProxy *proxy, VFIOUserMsg *msg); static void vfio_user_recv(void *opaque); static void vfio_user_send(void *opaque); -static void vfio_user_cb(void *opaque); static void vfio_user_request(void *opaque); @@ -492,7 +491,7 @@ static void vfio_user_send(void *opaque) } } -static void vfio_user_cb(void *opaque) +static void vfio_user_close_cb(void *opaque) { VFIOUserProxy *proxy = opaque; @@ -984,8 +983,11 @@ void vfio_user_disconnect(VFIOUserProxy *proxy) * handler to run after the proxy fd handlers were * deleted above. */ - aio_bh_schedule_oneshot(proxy->ctx, vfio_user_cb, proxy); - qemu_cond_wait(&proxy->close_cv, &proxy->lock); + aio_bh_schedule_oneshot(proxy->ctx, vfio_user_close_cb, proxy); + + while (proxy->state != VFIO_PROXY_CLOSED) { + qemu_cond_wait(&proxy->close_cv, &proxy->lock); + } /* we now hold the only ref to proxy */ qemu_mutex_unlock(&proxy->lock); From 622740aad9f39c4266ce00d7478b32c7506e6642 Mon Sep 17 00:00:00 2001 From: John Levon Date: Tue, 15 Jul 2025 13:59:54 +0200 Subject: [PATCH 2440/2760] hw/vfio-user: fix use of uninitialized variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity reported: CID 1611805: Uninitialized variables in vfio_user_dma_map(). This can occur in the happy path when ->async_ops was not set; as this doesn't typically happen, it wasn't caught during testing. Align both map and unmap implementations to initialize ret the same way to resolve this. Resolves: Coverity CID 1611805 Fixes: 18e899e6 ("vfio-user: implement VFIO_USER_DMA_MAP/UNMAP") Reported-by: Cédric Le Goater Signed-off-by: John Levon Reviewed-by: Cédric Le Goater Reviewed-by: Mark Cave-Ayland Link: https://lore.kernel.org/qemu-devel/20250715115954.515819-5-john.levon@nutanix.com Signed-off-by: Cédric Le Goater --- hw/vfio-user/container.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/hw/vfio-user/container.c b/hw/vfio-user/container.c index d318e6a339..d589dd90f5 100644 --- a/hw/vfio-user/container.c +++ b/hw/vfio-user/container.c @@ -64,8 +64,6 @@ static int vfio_user_dma_unmap(const VFIOContainerBase *bcontainer, 0, &local_err)) { error_report_err(local_err); ret = -EFAULT; - } else { - ret = 0; } } else { if (!vfio_user_send_wait(container->proxy, &msgp->hdr, NULL, @@ -92,7 +90,7 @@ static int vfio_user_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, bcontainer); int fd = memory_region_get_fd(mrp); Error *local_err = NULL; - int ret; + int ret = 0; VFIOUserFDs *fds = NULL; VFIOUserDMAMap *msgp = g_malloc0(sizeof(*msgp)); @@ -135,8 +133,6 @@ static int vfio_user_dma_map(const VFIOContainerBase *bcontainer, hwaddr iova, 0, &local_err)) { error_report_err(local_err); ret = -EFAULT; - } else { - ret = 0; } } else { VFIOUserFDs local_fds = { 1, 0, &fd }; From a59d06305fff9d10ddeeaebc66590af422362701 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Tue, 8 Jul 2025 22:52:11 +0800 Subject: [PATCH 2441/2760] vfio/pci: Introduce x-pci-class-code option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce x-pci-class-code option to allow users to override PCI class code of a device, similar to the existing x-pci-vendor-id option. Only the lower 24 bits of this option are used, though a uint32 is used here for determining whether the value is valid and set by user. Additionally, to ensure VGA ranges are only exposed on VGA devices, pci_register_vga() is now called in vfio_pci_config_setup(), after the class code override is completed. This is mainly intended for IGD devices that expose themselves either as VGA controller (primary display) or Display controller (non-primary display). The UEFI GOP driver depends on the device reporting a VGA controller class code (0x030000). Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250708145211.6179-1-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 29 +++++++++++++++++++++++++---- hw/vfio/pci.h | 6 ++---- hw/vfio/trace-events | 1 + 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 1093b28df7..910042c6c2 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2893,10 +2893,6 @@ bool vfio_populate_vga(VFIOPCIDevice *vdev, Error **errp) "vfio-vga-io@0x3c0", QEMU_PCI_VGA_IO_HI_SIZE); - pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, - &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, - &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); - return true; } @@ -3228,6 +3224,23 @@ bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) vdev->sub_device_id); } + /* + * Class code is a 24-bit value at config space 0x09. Allow overriding it + * with any 24-bit value. + */ + if (vdev->class_code != PCI_ANY_ID) { + if (vdev->class_code > 0xffffff) { + error_setg(errp, "invalid PCI class code provided"); + return false; + } + /* Higher 24 bits of PCI_CLASS_REVISION are class code */ + vfio_add_emulated_long(vdev, PCI_CLASS_REVISION, + vdev->class_code << 8, ~0xff); + trace_vfio_pci_emulated_class_code(vbasedev->name, vdev->class_code); + } else { + vdev->class_code = pci_get_long(pdev->config + PCI_CLASS_REVISION) >> 8; + } + /* QEMU can change multi-function devices to single function, or reverse */ vdev->emulated_config_bits[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_MULTI_FUNCTION; @@ -3257,6 +3270,12 @@ bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) vfio_bars_register(vdev); + if (vdev->vga && vfio_is_vga(vdev)) { + pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, + &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, + &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); + } + return true; } @@ -3643,6 +3662,8 @@ static const Property vfio_pci_dev_properties[] = { sub_vendor_id, PCI_ANY_ID), DEFINE_PROP_UINT32("x-pci-sub-device-id", VFIOPCIDevice, sub_device_id, PCI_ANY_ID), + DEFINE_PROP_UINT32("x-pci-class-code", VFIOPCIDevice, + class_code, PCI_ANY_ID), DEFINE_PROP_UINT32("x-igd-gms", VFIOPCIDevice, igd_gms, 0), DEFINE_PROP_UNSIGNED_NODEFAULT("x-nv-gpudirect-clique", VFIOPCIDevice, nv_gpudirect_clique, diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 495fae737d..4aa6461117 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -157,6 +157,7 @@ struct VFIOPCIDevice { uint32_t device_id; uint32_t sub_vendor_id; uint32_t sub_device_id; + uint32_t class_code; uint32_t features; #define VFIO_FEATURE_ENABLE_VGA_BIT 0 #define VFIO_FEATURE_ENABLE_VGA (1 << VFIO_FEATURE_ENABLE_VGA_BIT) @@ -205,10 +206,7 @@ static inline bool vfio_pci_is(VFIOPCIDevice *vdev, uint32_t vendor, uint32_t de static inline bool vfio_is_vga(VFIOPCIDevice *vdev) { - PCIDevice *pdev = &vdev->pdev; - uint16_t class = pci_get_word(pdev->config + PCI_CLASS_DEVICE); - - return class == PCI_CLASS_DISPLAY_VGA; + return (vdev->class_code >> 8) == PCI_CLASS_DISPLAY_VGA; } /* MSI/MSI-X/INTx */ diff --git a/hw/vfio/trace-events b/hw/vfio/trace-events index 8ec0ad0cde..fc6ed230d0 100644 --- a/hw/vfio/trace-events +++ b/hw/vfio/trace-events @@ -48,6 +48,7 @@ vfio_pci_emulated_vendor_id(const char *name, uint16_t val) "%s 0x%04x" vfio_pci_emulated_device_id(const char *name, uint16_t val) "%s 0x%04x" vfio_pci_emulated_sub_vendor_id(const char *name, uint16_t val) "%s 0x%04x" vfio_pci_emulated_sub_device_id(const char *name, uint16_t val) "%s 0x%04x" +vfio_pci_emulated_class_code(const char *name, uint32_t val) "%s 0x%06x" # pci-quirks.c vfio_quirk_rom_in_denylist(const char *name, uint16_t vid, uint16_t did) "%s %04x:%04x" From 6380b0a02fbdac253b8a98b300398319ab655237 Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 15 Jul 2025 16:37:36 +0200 Subject: [PATCH 2442/2760] vfio/migration: Add x-migration-load-config-after-iter VFIO property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This property allows configuring whether to start the config load only after all iterables were loaded, during non-iterables loading phase. Such interlocking is required for ARM64 due to this platform VFIO dependency on interrupt controller being loaded first. The property defaults to AUTO, which means ON for ARM, OFF for other platforms. Reviewed-by: Fabiano Rosas Reviewed-by: Avihai Horon Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/0e03c60dbc91f9a9ba2516929574df605b7dfcb4.1752589295.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- docs/devel/migration/vfio.rst | 6 +++ hw/core/machine.c | 1 + hw/vfio/helpers.c | 17 +++++++ hw/vfio/migration-multifd.c | 79 +++++++++++++++++++++++++++++++ hw/vfio/migration-multifd.h | 3 ++ hw/vfio/migration.c | 10 +++- hw/vfio/pci.c | 10 ++++ hw/vfio/vfio-helpers.h | 2 + hw/vfio/vfio-migration-internal.h | 1 + include/hw/vfio/vfio-device.h | 1 + 10 files changed, 129 insertions(+), 1 deletion(-) diff --git a/docs/devel/migration/vfio.rst b/docs/devel/migration/vfio.rst index 2d8e5ca9dd..dae3a98830 100644 --- a/docs/devel/migration/vfio.rst +++ b/docs/devel/migration/vfio.rst @@ -247,3 +247,9 @@ The multifd VFIO device state transfer is controlled by "x-migration-multifd-transfer" VFIO device property. This property defaults to AUTO, which means that VFIO device state transfer via multifd channels is attempted in configurations that otherwise support it. + +Some host platforms (like ARM64) require that VFIO device config is loaded only +after all iterables were loaded, during non-iterables loading phase. +Such interlocking is controlled by "x-migration-load-config-after-iter" VFIO +device property, which in its default setting (AUTO) does so only on platforms +that actually require it. diff --git a/hw/core/machine.c b/hw/core/machine.c index e869821b22..16640b700f 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -39,6 +39,7 @@ GlobalProperty hw_compat_10_0[] = { { "scsi-hd", "dpofua", "off" }, + { "vfio-pci", "x-migration-load-config-after-iter", "off" }, }; const size_t hw_compat_10_0_len = G_N_ELEMENTS(hw_compat_10_0); diff --git a/hw/vfio/helpers.c b/hw/vfio/helpers.c index 9a5f621545..23d13e5db5 100644 --- a/hw/vfio/helpers.c +++ b/hw/vfio/helpers.c @@ -209,3 +209,20 @@ struct vfio_device_info *vfio_get_device_info(int fd) return info; } + +bool vfio_arch_wants_loading_config_after_iter(void) +{ + /* + * Starting the config load only after all iterables were loaded (during + * non-iterables loading phase) is required for ARM64 due to this platform + * VFIO dependency on interrupt controller being loaded first. + * + * See commit d329f5032e17 ("vfio: Move the saving of the config space to + * the right place in VFIO migration"). + */ +#if defined(TARGET_ARM) + return true; +#else + return false; +#endif +} diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index 55635486c8..e539befaa9 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -23,6 +23,7 @@ #include "migration-multifd.h" #include "vfio-migration-internal.h" #include "trace.h" +#include "vfio-helpers.h" #define VFIO_DEVICE_STATE_CONFIG_STATE (1) @@ -35,6 +36,18 @@ typedef struct VFIODeviceStatePacket { uint8_t data[0]; } QEMU_PACKED VFIODeviceStatePacket; +bool vfio_load_config_after_iter(VFIODevice *vbasedev) +{ + if (vbasedev->migration_load_config_after_iter == ON_OFF_AUTO_ON) { + return true; + } else if (vbasedev->migration_load_config_after_iter == ON_OFF_AUTO_OFF) { + return false; + } + + assert(vbasedev->migration_load_config_after_iter == ON_OFF_AUTO_AUTO); + return vfio_arch_wants_loading_config_after_iter(); +} + /* type safety */ typedef struct VFIOStateBuffers { GArray *array; @@ -50,6 +63,9 @@ typedef struct VFIOMultifd { bool load_bufs_thread_running; bool load_bufs_thread_want_exit; + bool load_bufs_iter_done; + QemuCond load_bufs_iter_done_cond; + VFIOStateBuffers load_bufs; QemuCond load_bufs_buffer_ready_cond; QemuCond load_bufs_thread_finished_cond; @@ -394,6 +410,22 @@ static bool vfio_load_bufs_thread(void *opaque, bool *should_quit, Error **errp) multifd->load_buf_idx++; } + if (vfio_load_config_after_iter(vbasedev)) { + while (!multifd->load_bufs_iter_done) { + qemu_cond_wait(&multifd->load_bufs_iter_done_cond, + &multifd->load_bufs_mutex); + + /* + * Need to re-check cancellation immediately after wait in case + * cond was signalled by vfio_load_cleanup_load_bufs_thread(). + */ + if (vfio_load_bufs_thread_want_exit(multifd, should_quit)) { + error_setg(errp, "operation cancelled"); + goto thread_exit; + } + } + } + if (!vfio_load_bufs_thread_load_config(vbasedev, errp)) { goto thread_exit; } @@ -413,6 +445,48 @@ static bool vfio_load_bufs_thread(void *opaque, bool *should_quit, Error **errp) return ret; } +int vfio_load_state_config_load_ready(VFIODevice *vbasedev) +{ + VFIOMigration *migration = vbasedev->migration; + VFIOMultifd *multifd = migration->multifd; + int ret = 0; + + if (!vfio_multifd_transfer_enabled(vbasedev)) { + error_report("%s: got DEV_CONFIG_LOAD_READY outside multifd transfer", + vbasedev->name); + return -EINVAL; + } + + if (!vfio_load_config_after_iter(vbasedev)) { + error_report("%s: got DEV_CONFIG_LOAD_READY but was disabled", + vbasedev->name); + return -EINVAL; + } + + assert(multifd); + + /* The lock order is load_bufs_mutex -> BQL so unlock BQL here first */ + bql_unlock(); + WITH_QEMU_LOCK_GUARD(&multifd->load_bufs_mutex) { + if (multifd->load_bufs_iter_done) { + /* Can't print error here as we're outside BQL */ + ret = -EINVAL; + break; + } + + multifd->load_bufs_iter_done = true; + qemu_cond_signal(&multifd->load_bufs_iter_done_cond); + } + bql_lock(); + + if (ret) { + error_report("%s: duplicate DEV_CONFIG_LOAD_READY", + vbasedev->name); + } + + return ret; +} + static VFIOMultifd *vfio_multifd_new(void) { VFIOMultifd *multifd = g_new(VFIOMultifd, 1); @@ -425,6 +499,9 @@ static VFIOMultifd *vfio_multifd_new(void) multifd->load_buf_idx_last = UINT32_MAX; qemu_cond_init(&multifd->load_bufs_buffer_ready_cond); + multifd->load_bufs_iter_done = false; + qemu_cond_init(&multifd->load_bufs_iter_done_cond); + multifd->load_bufs_thread_running = false; multifd->load_bufs_thread_want_exit = false; qemu_cond_init(&multifd->load_bufs_thread_finished_cond); @@ -448,6 +525,7 @@ static void vfio_load_cleanup_load_bufs_thread(VFIOMultifd *multifd) multifd->load_bufs_thread_want_exit = true; qemu_cond_signal(&multifd->load_bufs_buffer_ready_cond); + qemu_cond_signal(&multifd->load_bufs_iter_done_cond); qemu_cond_wait(&multifd->load_bufs_thread_finished_cond, &multifd->load_bufs_mutex); } @@ -460,6 +538,7 @@ static void vfio_multifd_free(VFIOMultifd *multifd) vfio_load_cleanup_load_bufs_thread(multifd); qemu_cond_destroy(&multifd->load_bufs_thread_finished_cond); + qemu_cond_destroy(&multifd->load_bufs_iter_done_cond); vfio_state_buffers_destroy(&multifd->load_bufs); qemu_cond_destroy(&multifd->load_bufs_buffer_ready_cond); qemu_mutex_destroy(&multifd->load_bufs_mutex); diff --git a/hw/vfio/migration-multifd.h b/hw/vfio/migration-multifd.h index ebf22a7997..82d2d3a1fd 100644 --- a/hw/vfio/migration-multifd.h +++ b/hw/vfio/migration-multifd.h @@ -20,9 +20,12 @@ void vfio_multifd_cleanup(VFIODevice *vbasedev); bool vfio_multifd_transfer_supported(void); bool vfio_multifd_transfer_enabled(VFIODevice *vbasedev); +bool vfio_load_config_after_iter(VFIODevice *vbasedev); bool vfio_multifd_load_state_buffer(void *opaque, char *data, size_t data_size, Error **errp); +int vfio_load_state_config_load_ready(VFIODevice *vbasedev); + void vfio_multifd_emit_dummy_eos(VFIODevice *vbasedev, QEMUFile *f); bool diff --git a/hw/vfio/migration.c b/hw/vfio/migration.c index c329578eec..4c06e3db93 100644 --- a/hw/vfio/migration.c +++ b/hw/vfio/migration.c @@ -675,7 +675,11 @@ static void vfio_save_state(QEMUFile *f, void *opaque) int ret; if (vfio_multifd_transfer_enabled(vbasedev)) { - vfio_multifd_emit_dummy_eos(vbasedev, f); + if (vfio_load_config_after_iter(vbasedev)) { + qemu_put_be64(f, VFIO_MIG_FLAG_DEV_CONFIG_LOAD_READY); + } else { + vfio_multifd_emit_dummy_eos(vbasedev, f); + } return; } @@ -784,6 +788,10 @@ static int vfio_load_state(QEMUFile *f, void *opaque, int version_id) return ret; } + case VFIO_MIG_FLAG_DEV_CONFIG_LOAD_READY: + { + return vfio_load_state_config_load_ready(vbasedev); + } default: error_report("%s: Unknown tag 0x%"PRIx64, vbasedev->name, data); return -EINVAL; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 910042c6c2..09acad002a 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3642,6 +3642,9 @@ static const Property vfio_pci_dev_properties[] = { vbasedev.migration_multifd_transfer, vfio_pci_migration_multifd_transfer_prop, OnOffAuto, .set_default = true, .defval.i = ON_OFF_AUTO_AUTO), + DEFINE_PROP_ON_OFF_AUTO("x-migration-load-config-after-iter", VFIOPCIDevice, + vbasedev.migration_load_config_after_iter, + ON_OFF_AUTO_AUTO), DEFINE_PROP_BOOL("migration-events", VFIOPCIDevice, vbasedev.migration_events, false), DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false), @@ -3818,6 +3821,13 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, const void *data) "x-migration-multifd-transfer", "Transfer this device state via " "multifd channels when live migrating it"); + object_class_property_set_description(klass, /* 10.1 */ + "x-migration-load-config-after-iter", + "Start the config load only after " + "all iterables were loaded (during " + "non-iterables loading phase) when " + "doing live migration of device state " + "via multifd channels"); } static const TypeInfo vfio_pci_dev_info = { diff --git a/hw/vfio/vfio-helpers.h b/hw/vfio/vfio-helpers.h index 54a327ffbc..ce31758080 100644 --- a/hw/vfio/vfio-helpers.h +++ b/hw/vfio/vfio-helpers.h @@ -32,4 +32,6 @@ struct vfio_device_info *vfio_get_device_info(int fd); int vfio_kvm_device_add_fd(int fd, Error **errp); int vfio_kvm_device_del_fd(int fd, Error **errp); +bool vfio_arch_wants_loading_config_after_iter(void); + #endif /* HW_VFIO_VFIO_HELPERS_H */ diff --git a/hw/vfio/vfio-migration-internal.h b/hw/vfio/vfio-migration-internal.h index a8b456b239..54141e27e6 100644 --- a/hw/vfio/vfio-migration-internal.h +++ b/hw/vfio/vfio-migration-internal.h @@ -32,6 +32,7 @@ #define VFIO_MIG_FLAG_DEV_SETUP_STATE (0xffffffffef100003ULL) #define VFIO_MIG_FLAG_DEV_DATA_STATE (0xffffffffef100004ULL) #define VFIO_MIG_FLAG_DEV_INIT_DATA_SENT (0xffffffffef100005ULL) +#define VFIO_MIG_FLAG_DEV_CONFIG_LOAD_READY (0xffffffffef100006ULL) typedef struct VFIODevice VFIODevice; typedef struct VFIOMultifd VFIOMultifd; diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index 1901a35aa9..dac3fdce15 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -67,6 +67,7 @@ typedef struct VFIODevice { bool ram_block_discard_allowed; OnOffAuto enable_migration; OnOffAuto migration_multifd_transfer; + OnOffAuto migration_load_config_after_iter; bool migration_events; bool use_region_fds; VFIODeviceOps *ops; From 300dcf58b72fa1635190b19f102231b0775e93cb Mon Sep 17 00:00:00 2001 From: "Maciej S. Szmigiero" Date: Tue, 15 Jul 2025 16:37:37 +0200 Subject: [PATCH 2443/2760] vfio/migration: Max in-flight VFIO device state buffers size limit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow capping the maximum total size of in-flight VFIO device state buffers queued at the destination, otherwise a malicious QEMU source could theoretically cause the target QEMU to allocate unlimited amounts of memory for buffers-in-flight. Since this is not expected to be a realistic threat in most of VFIO live migration use cases and the right value depends on the particular setup disable this limit by default by setting it to UINT64_MAX. Reviewed-by: Fabiano Rosas Reviewed-by: Avihai Horon Signed-off-by: Maciej S. Szmigiero Link: https://lore.kernel.org/qemu-devel/4f7cad490988288f58e36b162d7a888ed7e7fd17.1752589295.git.maciej.szmigiero@oracle.com Signed-off-by: Cédric Le Goater --- docs/devel/migration/vfio.rst | 13 +++++++++++++ hw/vfio/migration-multifd.c | 21 +++++++++++++++++++-- hw/vfio/pci.c | 9 +++++++++ include/hw/vfio/vfio-device.h | 1 + 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/docs/devel/migration/vfio.rst b/docs/devel/migration/vfio.rst index dae3a98830..0790e5031d 100644 --- a/docs/devel/migration/vfio.rst +++ b/docs/devel/migration/vfio.rst @@ -248,6 +248,19 @@ The multifd VFIO device state transfer is controlled by AUTO, which means that VFIO device state transfer via multifd channels is attempted in configurations that otherwise support it. +Since the target QEMU needs to load device state buffers in-order it needs to +queue incoming buffers until they can be loaded into the device. +This means that a malicious QEMU source could theoretically cause the target +QEMU to allocate unlimited amounts of memory for such buffers-in-flight. + +The "x-migration-max-queued-buffers-size" property allows capping the total size +of these VFIO device state buffers queued at the destination. + +Because a malicious QEMU source causing OOM on the target is not expected to be +a realistic threat in most of VFIO live migration use cases and the right value +depends on the particular setup by default this queued buffers size limit is +disabled by setting it to UINT64_MAX. + Some host platforms (like ARM64) require that VFIO device config is loaded only after all iterables were loaded, during non-iterables loading phase. Such interlocking is controlled by "x-migration-load-config-after-iter" VFIO diff --git a/hw/vfio/migration-multifd.c b/hw/vfio/migration-multifd.c index e539befaa9..d522671b8d 100644 --- a/hw/vfio/migration-multifd.c +++ b/hw/vfio/migration-multifd.c @@ -72,6 +72,7 @@ typedef struct VFIOMultifd { QemuMutex load_bufs_mutex; /* Lock order: this lock -> BQL */ uint32_t load_buf_idx; uint32_t load_buf_idx_last; + size_t load_buf_queued_pending_buffers_size; } VFIOMultifd; static void vfio_state_buffer_clear(gpointer data) @@ -128,6 +129,7 @@ static bool vfio_load_state_buffer_insert(VFIODevice *vbasedev, VFIOMigration *migration = vbasedev->migration; VFIOMultifd *multifd = migration->multifd; VFIOStateBuffer *lb; + size_t data_size = packet_total_size - sizeof(*packet); vfio_state_buffers_assert_init(&multifd->load_bufs); if (packet->idx >= vfio_state_buffers_size_get(&multifd->load_bufs)) { @@ -143,8 +145,19 @@ static bool vfio_load_state_buffer_insert(VFIODevice *vbasedev, assert(packet->idx >= multifd->load_buf_idx); - lb->data = g_memdup2(&packet->data, packet_total_size - sizeof(*packet)); - lb->len = packet_total_size - sizeof(*packet); + multifd->load_buf_queued_pending_buffers_size += data_size; + if (multifd->load_buf_queued_pending_buffers_size > + vbasedev->migration_max_queued_buffers_size) { + error_setg(errp, + "%s: queuing state buffer %" PRIu32 + " would exceed the size max of %" PRIu64, + vbasedev->name, packet->idx, + vbasedev->migration_max_queued_buffers_size); + return false; + } + + lb->data = g_memdup2(&packet->data, data_size); + lb->len = data_size; lb->is_present = true; return true; @@ -328,6 +341,9 @@ static bool vfio_load_state_buffer_write(VFIODevice *vbasedev, assert(wr_ret <= buf_len); buf_len -= wr_ret; buf_cur += wr_ret; + + assert(multifd->load_buf_queued_pending_buffers_size >= wr_ret); + multifd->load_buf_queued_pending_buffers_size -= wr_ret; } trace_vfio_load_state_device_buffer_load_end(vbasedev->name, @@ -497,6 +513,7 @@ static VFIOMultifd *vfio_multifd_new(void) multifd->load_buf_idx = 0; multifd->load_buf_idx_last = UINT32_MAX; + multifd->load_buf_queued_pending_buffers_size = 0; qemu_cond_init(&multifd->load_bufs_buffer_ready_cond); multifd->load_bufs_iter_done = false; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 09acad002a..be05002b98 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3645,6 +3645,8 @@ static const Property vfio_pci_dev_properties[] = { DEFINE_PROP_ON_OFF_AUTO("x-migration-load-config-after-iter", VFIOPCIDevice, vbasedev.migration_load_config_after_iter, ON_OFF_AUTO_AUTO), + DEFINE_PROP_SIZE("x-migration-max-queued-buffers-size", VFIOPCIDevice, + vbasedev.migration_max_queued_buffers_size, UINT64_MAX), DEFINE_PROP_BOOL("migration-events", VFIOPCIDevice, vbasedev.migration_events, false), DEFINE_PROP_BOOL("x-no-mmap", VFIOPCIDevice, vbasedev.no_mmap, false), @@ -3828,6 +3830,13 @@ static void vfio_pci_dev_class_init(ObjectClass *klass, const void *data) "non-iterables loading phase) when " "doing live migration of device state " "via multifd channels"); + object_class_property_set_description(klass, /* 10.1 */ + "x-migration-max-queued-buffers-size", + "Maximum size of in-flight VFIO " + "device state buffers queued at the " + "destination when doing live " + "migration of device state via " + "multifd channels"); } static const TypeInfo vfio_pci_dev_info = { diff --git a/include/hw/vfio/vfio-device.h b/include/hw/vfio/vfio-device.h index dac3fdce15..6e4d5ccdac 100644 --- a/include/hw/vfio/vfio-device.h +++ b/include/hw/vfio/vfio-device.h @@ -68,6 +68,7 @@ typedef struct VFIODevice { OnOffAuto enable_migration; OnOffAuto migration_multifd_transfer; OnOffAuto migration_load_config_after_iter; + uint64_t migration_max_queued_buffers_size; bool migration_events; bool use_region_fds; VFIODeviceOps *ops; From 62b8cc1ecb376f39df67590f8c82914b923a6f15 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Apr 2025 16:40:31 +0200 Subject: [PATCH 2444/2760] hw/xen/arch_hvm: Unify x86 and ARM variants MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As each target declares the same prototypes, we can use a single header, removing the TARGET_XXX uses. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Stefano Stabellini Message-Id: <20250513171737.74386-1-philmd@linaro.org> --- include/hw/arm/xen_arch_hvm.h | 9 --------- include/hw/i386/xen_arch_hvm.h | 11 ----------- include/hw/xen/arch_hvm.h | 14 ++++++++++---- 3 files changed, 10 insertions(+), 24 deletions(-) delete mode 100644 include/hw/arm/xen_arch_hvm.h delete mode 100644 include/hw/i386/xen_arch_hvm.h diff --git a/include/hw/arm/xen_arch_hvm.h b/include/hw/arm/xen_arch_hvm.h deleted file mode 100644 index 8fd645e723..0000000000 --- a/include/hw/arm/xen_arch_hvm.h +++ /dev/null @@ -1,9 +0,0 @@ -#ifndef HW_XEN_ARCH_ARM_HVM_H -#define HW_XEN_ARCH_ARM_HVM_H - -#include -void arch_handle_ioreq(XenIOState *state, ioreq_t *req); -void arch_xen_set_memory(XenIOState *state, - MemoryRegionSection *section, - bool add); -#endif diff --git a/include/hw/i386/xen_arch_hvm.h b/include/hw/i386/xen_arch_hvm.h deleted file mode 100644 index 1000f8f543..0000000000 --- a/include/hw/i386/xen_arch_hvm.h +++ /dev/null @@ -1,11 +0,0 @@ -#ifndef HW_XEN_ARCH_I386_HVM_H -#define HW_XEN_ARCH_I386_HVM_H - -#include -#include "hw/xen/xen-hvm-common.h" - -void arch_handle_ioreq(XenIOState *state, ioreq_t *req); -void arch_xen_set_memory(XenIOState *state, - MemoryRegionSection *section, - bool add); -#endif diff --git a/include/hw/xen/arch_hvm.h b/include/hw/xen/arch_hvm.h index df39c819c8..8bacaa4ec4 100644 --- a/include/hw/xen/arch_hvm.h +++ b/include/hw/xen/arch_hvm.h @@ -1,5 +1,11 @@ -#if defined(TARGET_I386) || defined(TARGET_X86_64) -#include "hw/i386/xen_arch_hvm.h" -#elif defined(TARGET_ARM) || defined(TARGET_AARCH64) -#include "hw/arm/xen_arch_hvm.h" +#ifndef HW_XEN_ARCH_HVM_H +#define HW_XEN_ARCH_HVM_H + +#include +#include "hw/xen/xen-hvm-common.h" + +void arch_handle_ioreq(XenIOState *state, ioreq_t *req); +void arch_xen_set_memory(XenIOState *state, + MemoryRegionSection *section, + bool add); #endif From 187aad2896a3775941a9a02ce8d8a4a15970c1e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Apr 2025 16:40:31 +0200 Subject: [PATCH 2445/2760] hw/arm/xen-pvh: Remove unnecessary 'hw/xen/arch_hvm.h' header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "hw/xen/arch_hvm.h" only declares the arch_handle_ioreq() and arch_xen_set_memory() prototypes, which are not used by xen-pvh.c. Remove the unnecessary header inclusion. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Reviewed-by: Manos Pitsidianakis Message-Id: <20250715071528.46196-1-philmd@linaro.org> --- hw/arm/xen-pvh.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/arm/xen-pvh.c b/hw/arm/xen-pvh.c index 4b26bcff7a..1a9eeb01c8 100644 --- a/hw/arm/xen-pvh.c +++ b/hw/arm/xen-pvh.c @@ -10,7 +10,6 @@ #include "hw/boards.h" #include "system/system.h" #include "hw/xen/xen-pvh-common.h" -#include "hw/xen/arch_hvm.h" #define TYPE_XEN_ARM MACHINE_TYPE_NAME("xenpvh") From 18da42ee4273a66f240bcca7aa4d8ce3b97b1a77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 2 Jul 2025 14:44:11 +0200 Subject: [PATCH 2446/2760] qapi/accel: Move definitions related to accelerators in their own file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extract KVM definitions from machine.json to accelerator.json. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Zhao Liu Message-Id: <20250703105540.67664-29-philmd@linaro.org> --- MAINTAINERS | 1 + hw/core/machine-hmp-cmds.c | 1 + hw/core/machine-qmp-cmds.c | 1 + qapi/accelerator.json | 39 ++++++++++++++++++++++++++++++++++++++ qapi/machine.json | 29 ---------------------------- qapi/meson.build | 1 + qapi/qapi-schema.json | 1 + 7 files changed, 44 insertions(+), 29 deletions(-) create mode 100644 qapi/accelerator.json diff --git a/MAINTAINERS b/MAINTAINERS index e88ed2c0a9..0e945f3bd2 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -509,6 +509,7 @@ F: accel/Makefile.objs F: accel/stubs/Makefile.objs F: cpu-common.c F: cpu-target.c +F: qapi/accelerator.json F: system/cpus.c Apple Silicon HVF CPUs diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index c6325cdcaa..5ca0da77b1 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -18,6 +18,7 @@ #include "monitor/monitor.h" #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" +#include "qapi/qapi-commands-accelerator.h" #include "qapi/qapi-commands-machine.h" #include "qobject/qdict.h" #include "qapi/string-output-visitor.h" diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index d82043e1c6..1af0f29f7d 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -14,6 +14,7 @@ #include "hw/mem/memory-device.h" #include "qapi/error.h" #include "qapi/qapi-builtin-visit.h" +#include "qapi/qapi-commands-accelerator.h" #include "qapi/qapi-commands-machine.h" #include "qobject/qobject.h" #include "qapi/qobject-input-visitor.h" diff --git a/qapi/accelerator.json b/qapi/accelerator.json new file mode 100644 index 0000000000..d55fe1aa0a --- /dev/null +++ b/qapi/accelerator.json @@ -0,0 +1,39 @@ +# -*- Mode: Python -*- +# vim: filetype=python +# +# SPDX-License-Identifier: GPL-2.0-or-later + +## +# = Accelerators +## + +{ 'include': 'common.json' } + +## +# @KvmInfo: +# +# Information about support for KVM acceleration +# +# @enabled: true if KVM acceleration is active +# +# @present: true if KVM acceleration is built into this executable +# +# Since: 0.14 +## +{ 'struct': 'KvmInfo', 'data': {'enabled': 'bool', 'present': 'bool'} } + +## +# @query-kvm: +# +# Return information about KVM acceleration +# +# Returns: @KvmInfo +# +# Since: 0.14 +# +# .. qmp-example:: +# +# -> { "execute": "query-kvm" } +# <- { "return": { "enabled": true, "present": true } } +## +{ 'command': 'query-kvm', 'returns': 'KvmInfo' } diff --git a/qapi/machine.json b/qapi/machine.json index f712e7da6d..f80ba264b5 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -451,35 +451,6 @@ ## { 'command': 'inject-nmi' } -## -# @KvmInfo: -# -# Information about support for KVM acceleration -# -# @enabled: true if KVM acceleration is active -# -# @present: true if KVM acceleration is built into this executable -# -# Since: 0.14 -## -{ 'struct': 'KvmInfo', 'data': {'enabled': 'bool', 'present': 'bool'} } - -## -# @query-kvm: -# -# Return information about KVM acceleration -# -# Returns: @KvmInfo -# -# Since: 0.14 -# -# .. qmp-example:: -# -# -> { "execute": "query-kvm" } -# <- { "return": { "enabled": true, "present": true } } -## -{ 'command': 'query-kvm', 'returns': 'KvmInfo' } - ## # @NumaOptionsType: # diff --git a/qapi/meson.build b/qapi/meson.build index 3b035aea33..ca6b61a608 100644 --- a/qapi/meson.build +++ b/qapi/meson.build @@ -57,6 +57,7 @@ qapi_all_modules = [ ] if have_system qapi_all_modules += [ + 'accelerator', 'acpi', 'audio', 'cryptodev', diff --git a/qapi/qapi-schema.json b/qapi/qapi-schema.json index a8f66163cb..0477696ff0 100644 --- a/qapi/qapi-schema.json +++ b/qapi/qapi-schema.json @@ -37,6 +37,7 @@ { 'include': 'run-state.json' } { 'include': 'crypto.json' } { 'include': 'job.json' } +{ 'include': 'accelerator.json' } { 'include': 'block.json' } { 'include': 'block-export.json' } { 'include': 'char.json' } From 6ca2eba6d3decff61a725c379876e18869521a5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 15 Jul 2025 10:53:49 +0200 Subject: [PATCH 2447/2760] qapi/machine: Add @qom-type field to CpuInfoFast structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Knowing the QOM type name of a CPU can be useful, in particular to infer its model name. Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Message-Id: <20250715090624.52377-2-philmd@linaro.org> --- hw/core/machine-qmp-cmds.c | 1 + qapi/machine.json | 3 +++ 2 files changed, 4 insertions(+) diff --git a/hw/core/machine-qmp-cmds.c b/hw/core/machine-qmp-cmds.c index 1af0f29f7d..b9e7eb64e0 100644 --- a/hw/core/machine-qmp-cmds.c +++ b/hw/core/machine-qmp-cmds.c @@ -48,6 +48,7 @@ CpuInfoFastList *qmp_query_cpus_fast(Error **errp) value->cpu_index = cpu->cpu_index; value->qom_path = object_get_canonical_path(OBJECT(cpu)); value->thread_id = cpu->thread_id; + value->qom_type = g_strdup(object_get_typename(OBJECT(cpu))); if (mc->cpu_index_to_instance_props) { CpuInstanceProperties *props; diff --git a/qapi/machine.json b/qapi/machine.json index f80ba264b5..6d3a480bea 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -76,6 +76,8 @@ # # @cpu-index: index of the virtual CPU # +# @qom-type: QOM type name of the CPU (since 10.1) +# # @qom-path: path to the CPU object in the QOM tree # # @thread-id: ID of the underlying host thread @@ -89,6 +91,7 @@ ## { 'union' : 'CpuInfoFast', 'base' : { 'cpu-index' : 'int', + 'qom-type' : 'str', 'qom-path' : 'str', 'thread-id' : 'int', '*props' : 'CpuInstanceProperties', From d6c73ac306a8fca6d98f91e447904c5149d56ed8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 19 Jun 2025 18:05:43 +0200 Subject: [PATCH 2448/2760] hw/core/machine: Display CPU model name in 'info cpus' command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Display the CPU model in 'info cpus'. Example before: $ qemu-system-aarch64 -M xlnx-versal-virt -S -monitor stdio QEMU 10.0.0 monitor - type 'help' for more information (qemu) info cpus * CPU #0: thread_id=42924 CPU #1: thread_id=42924 CPU #2: thread_id=42924 CPU #3: thread_id=42924 (qemu) q and after: $ qemu-system-aarch64 -M xlnx-versal-virt -S -monitor stdio QEMU 10.0.50 monitor - type 'help' for more information (qemu) info cpus * CPU #0: thread_id=42916 model=cortex-a72 CPU #1: thread_id=42916 model=cortex-a72 CPU #2: thread_id=42916 model=cortex-r5f CPU #3: thread_id=42916 model=cortex-r5f (qemu) Reviewed-by: Richard Henderson Reviewed-by: Zhao Liu Tested-by: Zhao Liu Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Xiaoyao Li Message-Id: <20250715090624.52377-3-philmd@linaro.org> --- hw/core/machine-hmp-cmds.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/core/machine-hmp-cmds.c b/hw/core/machine-hmp-cmds.c index 5ca0da77b1..3a612e2232 100644 --- a/hw/core/machine-hmp-cmds.c +++ b/hw/core/machine-hmp-cmds.c @@ -33,6 +33,7 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict) cpu_list = qmp_query_cpus_fast(NULL); for (cpu = cpu_list; cpu; cpu = cpu->next) { + g_autofree char *cpu_model = cpu_model_from_type(cpu->value->qom_type); int active = ' '; if (cpu->value->cpu_index == monitor_get_cpu_index(mon)) { @@ -41,7 +42,8 @@ void hmp_info_cpus(Monitor *mon, const QDict *qdict) monitor_printf(mon, "%c CPU #%" PRId64 ":", active, cpu->value->cpu_index); - monitor_printf(mon, " thread_id=%" PRId64 "\n", cpu->value->thread_id); + monitor_printf(mon, " thread_id=%" PRId64 " model=%s\n", + cpu->value->thread_id, cpu_model); } qapi_free_CpuInfoFastList(cpu_list); From b2ed4df79e82a6c95feadbe6efefced7bbeb4db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 9 Jul 2025 16:14:59 +0200 Subject: [PATCH 2449/2760] accel/tcg: Do not dump NaN statistics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Message-Id: <20250710111303.8917-1-philmd@linaro.org> --- accel/tcg/monitor.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index e7ed7281a4..778b12613f 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -19,7 +19,7 @@ #include "tcg/tcg.h" #include "internal-common.h" #include "tb-context.h" - +#include static void dump_drift_info(GString *buf) { @@ -57,6 +57,7 @@ static void print_qht_statistics(struct qht_stats hst, GString *buf) uint32_t hgram_opts; size_t hgram_bins; char *hgram; + double avg; if (!hst.head_buckets) { return; @@ -73,9 +74,13 @@ static void print_qht_statistics(struct qht_stats hst, GString *buf) hgram_opts |= QDIST_PR_NODECIMAL; } hgram = qdist_pr(&hst.occupancy, 10, hgram_opts); - g_string_append_printf(buf, "TB hash occupancy %0.2f%% avg chain occ. " - "Histogram: %s\n", - qdist_avg(&hst.occupancy) * 100, hgram); + avg = qdist_avg(&hst.occupancy); + if (!isnan(avg)) { + g_string_append_printf(buf, "TB hash occupancy " + "%0.2f%% avg chain occ. " + "Histogram: %s\n", + avg * 100, hgram); + } g_free(hgram); hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; @@ -87,9 +92,12 @@ static void print_qht_statistics(struct qht_stats hst, GString *buf) hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE; } hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts); - g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. " - "Histogram: %s\n", - qdist_avg(&hst.chain), hgram); + avg = qdist_avg(&hst.chain); + if (!isnan(avg)) { + g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. " + "Histogram: %s\n", + avg, hgram); + } g_free(hgram); } From 05927e9dc9371766d20c97ac609ec8698e95bb45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Jul 2025 16:18:32 +0200 Subject: [PATCH 2450/2760] accel: Rename 'system/accel-ops.h' -> 'accel/accel-cpu-ops.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately "system/accel-ops.h" handlers are not only system-specific. For example, the cpu_reset_hold() hook is part of the vCPU creation, after it is realized. Mechanical rename to drop 'system' using: $ sed -i -e s_system/accel-ops.h_accel/accel-cpu-ops.h_g \ $(git grep -l system/accel-ops.h) Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Richard Henderson Message-Id: <20250703173248.44995-38-philmd@linaro.org> --- accel/accel-system.c | 2 +- accel/hvf/hvf-accel-ops.c | 2 +- accel/kvm/kvm-accel-ops.c | 2 +- accel/qtest/qtest.c | 2 +- accel/tcg/tcg-accel-ops.c | 2 +- accel/xen/xen-all.c | 2 +- cpu-target.c | 2 +- gdbstub/system.c | 2 +- include/{system/accel-ops.h => accel/accel-cpu-ops.h} | 8 ++++---- system/cpus.c | 2 +- target/i386/nvmm/nvmm-accel-ops.c | 2 +- target/i386/whpx/whpx-accel-ops.c | 2 +- 12 files changed, 15 insertions(+), 15 deletions(-) rename include/{system/accel-ops.h => accel/accel-cpu-ops.h} (95%) diff --git a/accel/accel-system.c b/accel/accel-system.c index c54c30f18b..c2a955a6da 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -26,7 +26,7 @@ #include "qemu/osdep.h" #include "qemu/accel.h" #include "hw/boards.h" -#include "system/accel-ops.h" +#include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "qemu/error-report.h" #include "accel-internal.h" diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index be8724ac89..214454bd0b 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -54,7 +54,7 @@ #include "gdbstub/enums.h" #include "exec/cpu-common.h" #include "hw/core/cpu.h" -#include "system/accel-ops.h" +#include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "system/hvf.h" #include "system/hvf_int.h" diff --git a/accel/kvm/kvm-accel-ops.c b/accel/kvm/kvm-accel-ops.c index 0eafc902c3..b709187c7d 100644 --- a/accel/kvm/kvm-accel-ops.c +++ b/accel/kvm/kvm-accel-ops.c @@ -16,7 +16,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" #include "qemu/main-loop.h" -#include "system/accel-ops.h" +#include "accel/accel-cpu-ops.h" #include "system/kvm.h" #include "system/kvm_int.h" #include "system/runstate.h" diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index 2b83126020..a7fc8bee6d 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -18,7 +18,7 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/accel.h" -#include "system/accel-ops.h" +#include "accel/accel-cpu-ops.h" #include "system/qtest.h" #include "system/cpus.h" #include "qemu/guest-random.h" diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 279dbfa1cf..58ded9d6f0 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -26,7 +26,7 @@ */ #include "qemu/osdep.h" -#include "system/accel-ops.h" +#include "accel/accel-cpu-ops.h" #include "system/tcg.h" #include "system/replay.h" #include "exec/icount.h" diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index bd0ff64bef..55a60bb42c 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -19,7 +19,7 @@ #include "chardev/char.h" #include "qemu/accel.h" #include "accel/dummy-cpus.h" -#include "system/accel-ops.h" +#include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "system/xen.h" #include "system/runstate.h" diff --git a/cpu-target.c b/cpu-target.c index 1c90a30759..2049eb1d0f 100644 --- a/cpu-target.c +++ b/cpu-target.c @@ -19,7 +19,7 @@ #include "qemu/osdep.h" #include "cpu.h" -#include "system/accel-ops.h" +#include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "exec/cpu-common.h" #include "exec/tswap.h" diff --git a/gdbstub/system.c b/gdbstub/system.c index 8a32d8e1a1..5b6f8d0733 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -19,7 +19,7 @@ #include "gdbstub/commands.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" -#include "system/accel-ops.h" +#include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "system/runstate.h" #include "system/replay.h" diff --git a/include/system/accel-ops.h b/include/accel/accel-cpu-ops.h similarity index 95% rename from include/system/accel-ops.h rename to include/accel/accel-cpu-ops.h index bf7383511d..a9191dded7 100644 --- a/include/system/accel-ops.h +++ b/include/accel/accel-cpu-ops.h @@ -1,5 +1,5 @@ /* - * Accelerator OPS, used for cpus.c module + * Accelerator per-vCPU handlers * * Copyright 2021 SUSE LLC * @@ -7,8 +7,8 @@ * See the COPYING file in the top-level directory. */ -#ifndef ACCEL_OPS_H -#define ACCEL_OPS_H +#ifndef QEMU_ACCEL_CPU_OPS_H +#define QEMU_ACCEL_CPU_OPS_H #include "qemu/accel.h" #include "exec/vaddr.h" @@ -89,4 +89,4 @@ struct AccelOpsClass { void generic_handle_interrupt(CPUState *cpu, int mask); -#endif /* ACCEL_OPS_H */ +#endif /* QEMU_ACCEL_CPU_OPS_H */ diff --git a/system/cpus.c b/system/cpus.c index 8e6da2e060..256723558d 100644 --- a/system/cpus.c +++ b/system/cpus.c @@ -31,7 +31,7 @@ #include "qapi/qapi-events-run-state.h" #include "qapi/qmp/qerror.h" #include "exec/gdbstub.h" -#include "system/accel-ops.h" +#include "accel/accel-cpu-ops.h" #include "system/hw_accel.h" #include "exec/cpu-common.h" #include "qemu/thread.h" diff --git a/target/i386/nvmm/nvmm-accel-ops.c b/target/i386/nvmm/nvmm-accel-ops.c index a5517b0abf..3799260bbd 100644 --- a/target/i386/nvmm/nvmm-accel-ops.c +++ b/target/i386/nvmm/nvmm-accel-ops.c @@ -10,7 +10,7 @@ #include "qemu/osdep.h" #include "system/kvm_int.h" #include "qemu/main-loop.h" -#include "system/accel-ops.h" +#include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "qemu/guest-random.h" diff --git a/target/i386/whpx/whpx-accel-ops.c b/target/i386/whpx/whpx-accel-ops.c index 5f4841c9fa..da58805b1a 100644 --- a/target/i386/whpx/whpx-accel-ops.c +++ b/target/i386/whpx/whpx-accel-ops.c @@ -11,7 +11,7 @@ #include "qemu/osdep.h" #include "system/kvm_int.h" #include "qemu/main-loop.h" -#include "system/accel-ops.h" +#include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "qemu/guest-random.h" From f7a7e7dd2179e7189064e0b7b637e6906617221f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Jul 2025 16:44:39 +0200 Subject: [PATCH 2451/2760] accel: Extract AccelClass definition to 'accel/accel-ops.h' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Only accelerator implementations (and the common accelator code) need to know about AccelClass internals. Move the definition out but forward declare AccelState and AccelClass. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Richard Henderson Message-Id: <20250703173248.44995-39-philmd@linaro.org> --- MAINTAINERS | 2 +- accel/accel-common.c | 2 ++ accel/accel-system.c | 1 + accel/hvf/hvf-all.c | 1 + accel/kvm/kvm-all.c | 1 + accel/qtest/qtest.c | 1 + accel/tcg/tcg-accel-ops.c | 1 + accel/tcg/tcg-all.c | 2 ++ accel/xen/xen-all.c | 1 + bsd-user/main.c | 1 + gdbstub/system.c | 1 + include/accel/accel-ops.h | 49 +++++++++++++++++++++++++++++++++++++ include/qemu/accel.h | 39 ++--------------------------- include/system/hvf_int.h | 3 ++- include/system/kvm_int.h | 1 + linux-user/main.c | 1 + system/memory.c | 1 + target/i386/nvmm/nvmm-all.c | 1 + target/i386/whpx/whpx-all.c | 1 + 19 files changed, 71 insertions(+), 39 deletions(-) create mode 100644 include/accel/accel-ops.h diff --git a/MAINTAINERS b/MAINTAINERS index 0e945f3bd2..9d88c17433 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -502,7 +502,7 @@ F: include/exec/target_long.h F: include/qemu/accel.h F: include/system/accel-*.h F: include/system/cpus.h -F: include/accel/accel-cpu*.h +F: include/accel/accel-*.h F: accel/accel-*.? F: accel/dummy-cpus.? F: accel/Makefile.objs diff --git a/accel/accel-common.c b/accel/accel-common.c index 591ff4cbb6..850c5ab4b8 100644 --- a/accel/accel-common.c +++ b/accel/accel-common.c @@ -10,7 +10,9 @@ #include "qemu/osdep.h" #include "qemu/accel.h" #include "qemu/target-info.h" +#include "accel/accel-ops.h" #include "accel/accel-cpu.h" +#include "accel/accel-cpu-ops.h" #include "accel-internal.h" /* Lookup AccelClass from opt_name. Returns NULL if not found */ diff --git a/accel/accel-system.c b/accel/accel-system.c index c2a955a6da..8df561b953 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu/accel.h" #include "hw/boards.h" +#include "accel/accel-ops.h" #include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "qemu/error-report.h" diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index 1fa07c8b69..e67a8105a6 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -10,6 +10,7 @@ #include "qemu/osdep.h" #include "qemu/error-report.h" +#include "accel/accel-ops.h" #include "system/address-spaces.h" #include "system/memory.h" #include "system/hvf.h" diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index a106d1ba0f..659ff88156 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -32,6 +32,7 @@ #include "system/runstate.h" #include "system/cpus.h" #include "system/accel-blocker.h" +#include "accel/accel-ops.h" #include "qemu/bswap.h" #include "exec/tswap.h" #include "system/memory.h" diff --git a/accel/qtest/qtest.c b/accel/qtest/qtest.c index a7fc8bee6d..1d4337d698 100644 --- a/accel/qtest/qtest.c +++ b/accel/qtest/qtest.c @@ -18,6 +18,7 @@ #include "qemu/option.h" #include "qemu/config-file.h" #include "qemu/accel.h" +#include "accel/accel-ops.h" #include "accel/accel-cpu-ops.h" #include "system/qtest.h" #include "system/cpus.h" diff --git a/accel/tcg/tcg-accel-ops.c b/accel/tcg/tcg-accel-ops.c index 58ded9d6f0..3b0d7d298e 100644 --- a/accel/tcg/tcg-accel-ops.c +++ b/accel/tcg/tcg-accel-ops.c @@ -26,6 +26,7 @@ */ #include "qemu/osdep.h" +#include "accel/accel-ops.h" #include "accel/accel-cpu-ops.h" #include "system/tcg.h" #include "system/replay.h" diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index 5904582a68..eaeb465dfd 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -39,6 +39,8 @@ #ifndef CONFIG_USER_ONLY #include "hw/boards.h" #endif +#include "accel/accel-ops.h" +#include "accel/accel-cpu-ops.h" #include "accel/tcg/cpu-ops.h" #include "internal-common.h" diff --git a/accel/xen/xen-all.c b/accel/xen/xen-all.c index 55a60bb42c..97377d67d1 100644 --- a/accel/xen/xen-all.c +++ b/accel/xen/xen-all.c @@ -19,6 +19,7 @@ #include "chardev/char.h" #include "qemu/accel.h" #include "accel/dummy-cpus.h" +#include "accel/accel-ops.h" #include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "system/xen.h" diff --git a/bsd-user/main.c b/bsd-user/main.c index d0cc8e0088..7e5d4bbce0 100644 --- a/bsd-user/main.c +++ b/bsd-user/main.c @@ -38,6 +38,7 @@ #include "qemu/plugin.h" #include "user/guest-base.h" #include "user/page-protection.h" +#include "accel/accel-ops.h" #include "tcg/startup.h" #include "qemu/timer.h" #include "qemu/envlist.h" diff --git a/gdbstub/system.c b/gdbstub/system.c index 5b6f8d0733..5be0d3c58c 100644 --- a/gdbstub/system.c +++ b/gdbstub/system.c @@ -19,6 +19,7 @@ #include "gdbstub/commands.h" #include "exec/hwaddr.h" #include "exec/tb-flush.h" +#include "accel/accel-ops.h" #include "accel/accel-cpu-ops.h" #include "system/cpus.h" #include "system/runstate.h" diff --git a/include/accel/accel-ops.h b/include/accel/accel-ops.h new file mode 100644 index 0000000000..86a27c30fa --- /dev/null +++ b/include/accel/accel-ops.h @@ -0,0 +1,49 @@ +/* + * Accelerator handlers + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef ACCEL_OPS_H +#define ACCEL_OPS_H + +#include "exec/hwaddr.h" +#include "qemu/accel.h" +#include "qom/object.h" + +struct AccelState { + Object parent_obj; +}; + +struct AccelClass { + ObjectClass parent_class; + + const char *name; + /* Cached by accel_init_ops_interfaces() when created */ + AccelOpsClass *ops; + + int (*init_machine)(AccelState *as, MachineState *ms); + bool (*cpu_common_realize)(CPUState *cpu, Error **errp); + void (*cpu_common_unrealize)(CPUState *cpu); + + /* system related hooks */ + void (*setup_post)(AccelState *as); + void (*pre_resume_vm)(AccelState *as, bool step_pending); + bool (*has_memory)(AccelState *accel, AddressSpace *as, + hwaddr start_addr, hwaddr size); + + /* gdbstub related hooks */ + int (*gdbstub_supported_sstep_flags)(AccelState *as); + + bool *allowed; + /* + * Array of global properties that would be applied when specific + * accelerator is chosen. It works like MachineClass.compat_props + * but it's for accelerators not machines. Accelerator-provided + * global properties may be overridden by machine-type + * compat_props or user-provided global properties. + */ + GPtrArray *compat_props; +}; + +#endif /* ACCEL_OPS_H */ diff --git a/include/qemu/accel.h b/include/qemu/accel.h index 9e821d0fae..d3638c7bfd 100644 --- a/include/qemu/accel.h +++ b/include/qemu/accel.h @@ -26,43 +26,8 @@ #include "qom/object.h" #include "exec/hwaddr.h" -struct AccelState { - /*< private >*/ - Object parent_obj; -}; - -typedef struct AccelClass { - /*< private >*/ - ObjectClass parent_class; - /*< public >*/ - - const char *name; - /* Cached by accel_init_ops_interfaces() when created */ - AccelOpsClass *ops; - - int (*init_machine)(AccelState *as, MachineState *ms); - bool (*cpu_common_realize)(CPUState *cpu, Error **errp); - void (*cpu_common_unrealize)(CPUState *cpu); - - /* system related hooks */ - void (*setup_post)(AccelState *as); - void (*pre_resume_vm)(AccelState *as, bool step_pending); - bool (*has_memory)(AccelState *accel, AddressSpace *as, - hwaddr start_addr, hwaddr size); - - /* gdbstub related hooks */ - int (*gdbstub_supported_sstep_flags)(AccelState *as); - - bool *allowed; - /* - * Array of global properties that would be applied when specific - * accelerator is chosen. It works like MachineClass.compat_props - * but it's for accelerators not machines. Accelerator-provided - * global properties may be overridden by machine-type - * compat_props or user-provided global properties. - */ - GPtrArray *compat_props; -} AccelClass; +typedef struct AccelState AccelState; +typedef struct AccelClass AccelClass; #define TYPE_ACCEL "accel" diff --git a/include/system/hvf_int.h b/include/system/hvf_int.h index 5150c7dd9c..a3b06a3e75 100644 --- a/include/system/hvf_int.h +++ b/include/system/hvf_int.h @@ -14,6 +14,7 @@ #include "qemu/queue.h" #include "exec/vaddr.h" #include "qom/object.h" +#include "accel/accel-ops.h" #ifdef __aarch64__ #include @@ -45,7 +46,7 @@ typedef struct hvf_vcpu_caps { } hvf_vcpu_caps; struct HVFState { - AccelState parent; + AccelState parent_obj; hvf_slot slots[32]; int num_slots; diff --git a/include/system/kvm_int.h b/include/system/kvm_int.h index 756a3c0a25..9247493b02 100644 --- a/include/system/kvm_int.h +++ b/include/system/kvm_int.h @@ -14,6 +14,7 @@ #include "qemu/accel.h" #include "qemu/queue.h" #include "system/kvm.h" +#include "accel/accel-ops.h" #include "hw/boards.h" #include "hw/i386/topology.h" #include "io/channel-socket.h" diff --git a/linux-user/main.c b/linux-user/main.c index f4f2007439..68972f00a1 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -42,6 +42,7 @@ #include "user/page-protection.h" #include "exec/gdbstub.h" #include "gdbstub/user.h" +#include "accel/accel-ops.h" #include "tcg/startup.h" #include "qemu/timer.h" #include "qemu/envlist.h" diff --git a/system/memory.c b/system/memory.c index e8d9b15b28..0983ff8962 100644 --- a/system/memory.c +++ b/system/memory.c @@ -29,6 +29,7 @@ #include "system/runstate.h" #include "system/tcg.h" #include "qemu/accel.h" +#include "accel/accel-ops.h" #include "hw/boards.h" #include "migration/vmstate.h" #include "system/address-spaces.h" diff --git a/target/i386/nvmm/nvmm-all.c b/target/i386/nvmm/nvmm-all.c index b4a4d50e86..aab12d7732 100644 --- a/target/i386/nvmm/nvmm-all.c +++ b/target/i386/nvmm/nvmm-all.c @@ -12,6 +12,7 @@ #include "system/address-spaces.h" #include "system/ioport.h" #include "qemu/accel.h" +#include "accel/accel-ops.h" #include "system/nvmm.h" #include "system/cpus.h" #include "system/runstate.h" diff --git a/target/i386/whpx/whpx-all.c b/target/i386/whpx/whpx-all.c index faf56e1972..10df2d398f 100644 --- a/target/i386/whpx/whpx-all.c +++ b/target/i386/whpx/whpx-all.c @@ -14,6 +14,7 @@ #include "system/ioport.h" #include "gdbstub/helpers.h" #include "qemu/accel.h" +#include "accel/accel-ops.h" #include "system/whpx.h" #include "system/cpus.h" #include "system/runstate.h" From 2d7dc398ab48d09386c718708b43bd431758f1a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 15 Jul 2025 11:28:10 +0200 Subject: [PATCH 2452/2760] Revert "accel/tcg: Unregister the RCU before exiting RR thread" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit bc93332fe460211c2d2f4ff50e1a0e030c7b5159, which was merged prematurely, re-introducing Coverity CID 1547782 (unreachable code). Reported-by: Peter Maydell Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Manos Pitsidianakis Reviewed-by: Richard Henderson Message-Id: <20250715104015.72663-2-philmd@linaro.org> --- accel/tcg/tcg-accel-ops-rr.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/accel/tcg/tcg-accel-ops-rr.c b/accel/tcg/tcg-accel-ops-rr.c index a578698d07..6eec5c9eee 100644 --- a/accel/tcg/tcg-accel-ops-rr.c +++ b/accel/tcg/tcg-accel-ops-rr.c @@ -302,8 +302,6 @@ static void *rr_cpu_thread_fn(void *arg) rr_deal_with_unplugged_cpus(); } - rcu_unregister_thread(); - g_assert_not_reached(); } From 8cc04fd9df3b9775eaf0fb1b17d82a1a075aa3ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 3 Jul 2025 11:33:46 +0200 Subject: [PATCH 2453/2760] accel/tcg: Extract statistic related code to tcg-stats.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Statistic code is not specific to system emulation (except cross-page checks) and can be used to analyze user-mode binaries. Extract statistic related code to its own file: tcg-stats.c, keeping the original LGPL-2.1-or-later license tag. Note, this code is not yet reachable by user-mode. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Reviewed-by: Richard Henderson Message-Id: <20250715140048.84942-3-philmd@linaro.org> --- accel/tcg/meson.build | 1 + accel/tcg/monitor.c | 201 --------------------------------------- accel/tcg/tcg-stats.c | 215 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 216 insertions(+), 201 deletions(-) create mode 100644 accel/tcg/tcg-stats.c diff --git a/accel/tcg/meson.build b/accel/tcg/meson.build index 575e92bb9e..002aa8f458 100644 --- a/accel/tcg/meson.build +++ b/accel/tcg/meson.build @@ -11,6 +11,7 @@ tcg_ss.add(files( 'tcg-runtime-gvec.c', 'tb-maint.c', 'tcg-all.c', + 'tcg-stats.c', 'translate-all.c', 'translator.c', )) diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index 778b12613f..adb9de5a1c 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -7,213 +7,12 @@ */ #include "qemu/osdep.h" -#include "qemu/accel.h" -#include "qemu/qht.h" #include "qapi/error.h" #include "qapi/type-helpers.h" #include "qapi/qapi-commands-machine.h" #include "monitor/monitor.h" -#include "system/cpu-timers.h" -#include "exec/icount.h" #include "system/tcg.h" -#include "tcg/tcg.h" #include "internal-common.h" -#include "tb-context.h" -#include - -static void dump_drift_info(GString *buf) -{ - if (!icount_enabled()) { - return; - } - - g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n", - (cpu_get_clock() - icount_get()) / SCALE_MS); - if (icount_align_option) { - g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n", - -max_delay / SCALE_MS); - g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n", - max_advance / SCALE_MS); - } else { - g_string_append_printf(buf, "Max guest delay NA\n"); - g_string_append_printf(buf, "Max guest advance NA\n"); - } -} - -static void dump_accel_info(GString *buf) -{ - AccelState *accel = current_accel(); - bool one_insn_per_tb = object_property_get_bool(OBJECT(accel), - "one-insn-per-tb", - &error_fatal); - - g_string_append_printf(buf, "Accelerator settings:\n"); - g_string_append_printf(buf, "one-insn-per-tb: %s\n\n", - one_insn_per_tb ? "on" : "off"); -} - -static void print_qht_statistics(struct qht_stats hst, GString *buf) -{ - uint32_t hgram_opts; - size_t hgram_bins; - char *hgram; - double avg; - - if (!hst.head_buckets) { - return; - } - g_string_append_printf(buf, "TB hash buckets %zu/%zu " - "(%0.2f%% head buckets used)\n", - hst.used_head_buckets, hst.head_buckets, - (double)hst.used_head_buckets / - hst.head_buckets * 100); - - hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; - hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT; - if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) { - hgram_opts |= QDIST_PR_NODECIMAL; - } - hgram = qdist_pr(&hst.occupancy, 10, hgram_opts); - avg = qdist_avg(&hst.occupancy); - if (!isnan(avg)) { - g_string_append_printf(buf, "TB hash occupancy " - "%0.2f%% avg chain occ. " - "Histogram: %s\n", - avg * 100, hgram); - } - g_free(hgram); - - hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; - hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain); - if (hgram_bins > 10) { - hgram_bins = 10; - } else { - hgram_bins = 0; - hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE; - } - hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts); - avg = qdist_avg(&hst.chain); - if (!isnan(avg)) { - g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. " - "Histogram: %s\n", - avg, hgram); - } - g_free(hgram); -} - -struct tb_tree_stats { - size_t nb_tbs; - size_t host_size; - size_t target_size; - size_t max_target_size; - size_t direct_jmp_count; - size_t direct_jmp2_count; - size_t cross_page; -}; - -static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data) -{ - const TranslationBlock *tb = value; - struct tb_tree_stats *tst = data; - - tst->nb_tbs++; - tst->host_size += tb->tc.size; - tst->target_size += tb->size; - if (tb->size > tst->max_target_size) { - tst->max_target_size = tb->size; - } - if (tb->page_addr[1] != -1) { - tst->cross_page++; - } - if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) { - tst->direct_jmp_count++; - if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) { - tst->direct_jmp2_count++; - } - } - return false; -} - -static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) -{ - CPUState *cpu; - size_t full = 0, part = 0, elide = 0; - - CPU_FOREACH(cpu) { - full += qatomic_read(&cpu->neg.tlb.c.full_flush_count); - part += qatomic_read(&cpu->neg.tlb.c.part_flush_count); - elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count); - } - *pfull = full; - *ppart = part; - *pelide = elide; -} - -static void tcg_dump_flush_info(GString *buf) -{ - size_t flush_full, flush_part, flush_elide; - - g_string_append_printf(buf, "TB flush count %u\n", - qatomic_read(&tb_ctx.tb_flush_count)); - g_string_append_printf(buf, "TB invalidate count %u\n", - qatomic_read(&tb_ctx.tb_phys_invalidate_count)); - - tlb_flush_counts(&flush_full, &flush_part, &flush_elide); - g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); - g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); - g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); -} - -static void dump_exec_info(GString *buf) -{ - struct tb_tree_stats tst = {}; - struct qht_stats hst; - size_t nb_tbs; - - tcg_tb_foreach(tb_tree_stats_iter, &tst); - nb_tbs = tst.nb_tbs; - /* XXX: avoid using doubles ? */ - g_string_append_printf(buf, "Translation buffer state:\n"); - /* - * Report total code size including the padding and TB structs; - * otherwise users might think "-accel tcg,tb-size" is not honoured. - * For avg host size we use the precise numbers from tb_tree_stats though. - */ - g_string_append_printf(buf, "gen code size %zu/%zu\n", - tcg_code_size(), tcg_code_capacity()); - g_string_append_printf(buf, "TB count %zu\n", nb_tbs); - g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n", - nb_tbs ? tst.target_size / nb_tbs : 0, - tst.max_target_size); - g_string_append_printf(buf, "TB avg host size %zu bytes " - "(expansion ratio: %0.1f)\n", - nb_tbs ? tst.host_size / nb_tbs : 0, - tst.target_size ? - (double)tst.host_size / tst.target_size : 0); - g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n", - tst.cross_page, - nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0); - g_string_append_printf(buf, "direct jump count %zu (%zu%%) " - "(2 jumps=%zu %zu%%)\n", - tst.direct_jmp_count, - nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0, - tst.direct_jmp2_count, - nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0); - - qht_statistics_init(&tb_ctx.htable, &hst); - print_qht_statistics(hst, buf); - qht_statistics_destroy(&hst); - - g_string_append_printf(buf, "\nStatistics:\n"); - tcg_dump_flush_info(buf); -} - -void tcg_dump_stats(GString *buf) -{ - dump_accel_info(buf); - dump_exec_info(buf); - dump_drift_info(buf); -} HumanReadableText *qmp_x_query_jit(Error **errp) { diff --git a/accel/tcg/tcg-stats.c b/accel/tcg/tcg-stats.c new file mode 100644 index 0000000000..eb6e20ae98 --- /dev/null +++ b/accel/tcg/tcg-stats.c @@ -0,0 +1,215 @@ +/* + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * QEMU TCG statistics + * + * Copyright (c) 2003-2005 Fabrice Bellard + */ + +#include "qemu/osdep.h" +#include "qemu/accel.h" +#include "qemu/qht.h" +#include "qapi/error.h" +#include "system/cpu-timers.h" +#include "exec/icount.h" +#include "hw/core/cpu.h" +#include "tcg/tcg.h" +#include "internal-common.h" +#include "tb-context.h" +#include + +static void dump_drift_info(GString *buf) +{ + if (!icount_enabled()) { + return; + } + + g_string_append_printf(buf, "Host - Guest clock %"PRIi64" ms\n", + (cpu_get_clock() - icount_get()) / SCALE_MS); + if (icount_align_option) { + g_string_append_printf(buf, "Max guest delay %"PRIi64" ms\n", + -max_delay / SCALE_MS); + g_string_append_printf(buf, "Max guest advance %"PRIi64" ms\n", + max_advance / SCALE_MS); + } else { + g_string_append_printf(buf, "Max guest delay NA\n"); + g_string_append_printf(buf, "Max guest advance NA\n"); + } +} + +static void dump_accel_info(GString *buf) +{ + AccelState *accel = current_accel(); + bool one_insn_per_tb = object_property_get_bool(OBJECT(accel), + "one-insn-per-tb", + &error_fatal); + + g_string_append_printf(buf, "Accelerator settings:\n"); + g_string_append_printf(buf, "one-insn-per-tb: %s\n\n", + one_insn_per_tb ? "on" : "off"); +} + +static void print_qht_statistics(struct qht_stats hst, GString *buf) +{ + uint32_t hgram_opts; + size_t hgram_bins; + char *hgram; + double avg; + + if (!hst.head_buckets) { + return; + } + g_string_append_printf(buf, "TB hash buckets %zu/%zu " + "(%0.2f%% head buckets used)\n", + hst.used_head_buckets, hst.head_buckets, + (double)hst.used_head_buckets / + hst.head_buckets * 100); + + hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; + hgram_opts |= QDIST_PR_100X | QDIST_PR_PERCENT; + if (qdist_xmax(&hst.occupancy) - qdist_xmin(&hst.occupancy) == 1) { + hgram_opts |= QDIST_PR_NODECIMAL; + } + hgram = qdist_pr(&hst.occupancy, 10, hgram_opts); + avg = qdist_avg(&hst.occupancy); + if (!isnan(avg)) { + g_string_append_printf(buf, "TB hash occupancy " + "%0.2f%% avg chain occ. " + "Histogram: %s\n", + avg * 100, hgram); + } + g_free(hgram); + + hgram_opts = QDIST_PR_BORDER | QDIST_PR_LABELS; + hgram_bins = qdist_xmax(&hst.chain) - qdist_xmin(&hst.chain); + if (hgram_bins > 10) { + hgram_bins = 10; + } else { + hgram_bins = 0; + hgram_opts |= QDIST_PR_NODECIMAL | QDIST_PR_NOBINRANGE; + } + hgram = qdist_pr(&hst.chain, hgram_bins, hgram_opts); + avg = qdist_avg(&hst.chain); + if (!isnan(avg)) { + g_string_append_printf(buf, "TB hash avg chain %0.3f buckets. " + "Histogram: %s\n", + avg, hgram); + } + g_free(hgram); +} + +struct tb_tree_stats { + size_t nb_tbs; + size_t host_size; + size_t target_size; + size_t max_target_size; + size_t direct_jmp_count; + size_t direct_jmp2_count; + size_t cross_page; +}; + +static gboolean tb_tree_stats_iter(gpointer key, gpointer value, gpointer data) +{ + const TranslationBlock *tb = value; + struct tb_tree_stats *tst = data; + + tst->nb_tbs++; + tst->host_size += tb->tc.size; + tst->target_size += tb->size; + if (tb->size > tst->max_target_size) { + tst->max_target_size = tb->size; + } +#ifndef CONFIG_USER_ONLY + if (tb->page_addr[1] != -1) { + tst->cross_page++; + } +#endif + if (tb->jmp_reset_offset[0] != TB_JMP_OFFSET_INVALID) { + tst->direct_jmp_count++; + if (tb->jmp_reset_offset[1] != TB_JMP_OFFSET_INVALID) { + tst->direct_jmp2_count++; + } + } + return false; +} + +static void tlb_flush_counts(size_t *pfull, size_t *ppart, size_t *pelide) +{ + CPUState *cpu; + size_t full = 0, part = 0, elide = 0; + + CPU_FOREACH(cpu) { + full += qatomic_read(&cpu->neg.tlb.c.full_flush_count); + part += qatomic_read(&cpu->neg.tlb.c.part_flush_count); + elide += qatomic_read(&cpu->neg.tlb.c.elide_flush_count); + } + *pfull = full; + *ppart = part; + *pelide = elide; +} + +static void tcg_dump_flush_info(GString *buf) +{ + size_t flush_full, flush_part, flush_elide; + + g_string_append_printf(buf, "TB flush count %u\n", + qatomic_read(&tb_ctx.tb_flush_count)); + g_string_append_printf(buf, "TB invalidate count %u\n", + qatomic_read(&tb_ctx.tb_phys_invalidate_count)); + + tlb_flush_counts(&flush_full, &flush_part, &flush_elide); + g_string_append_printf(buf, "TLB full flushes %zu\n", flush_full); + g_string_append_printf(buf, "TLB partial flushes %zu\n", flush_part); + g_string_append_printf(buf, "TLB elided flushes %zu\n", flush_elide); +} + +static void dump_exec_info(GString *buf) +{ + struct tb_tree_stats tst = {}; + struct qht_stats hst; + size_t nb_tbs; + + tcg_tb_foreach(tb_tree_stats_iter, &tst); + nb_tbs = tst.nb_tbs; + /* XXX: avoid using doubles ? */ + g_string_append_printf(buf, "Translation buffer state:\n"); + /* + * Report total code size including the padding and TB structs; + * otherwise users might think "-accel tcg,tb-size" is not honoured. + * For avg host size we use the precise numbers from tb_tree_stats though. + */ + g_string_append_printf(buf, "gen code size %zu/%zu\n", + tcg_code_size(), tcg_code_capacity()); + g_string_append_printf(buf, "TB count %zu\n", nb_tbs); + g_string_append_printf(buf, "TB avg target size %zu max=%zu bytes\n", + nb_tbs ? tst.target_size / nb_tbs : 0, + tst.max_target_size); + g_string_append_printf(buf, "TB avg host size %zu bytes " + "(expansion ratio: %0.1f)\n", + nb_tbs ? tst.host_size / nb_tbs : 0, + tst.target_size ? + (double)tst.host_size / tst.target_size : 0); + g_string_append_printf(buf, "cross page TB count %zu (%zu%%)\n", + tst.cross_page, + nb_tbs ? (tst.cross_page * 100) / nb_tbs : 0); + g_string_append_printf(buf, "direct jump count %zu (%zu%%) " + "(2 jumps=%zu %zu%%)\n", + tst.direct_jmp_count, + nb_tbs ? (tst.direct_jmp_count * 100) / nb_tbs : 0, + tst.direct_jmp2_count, + nb_tbs ? (tst.direct_jmp2_count * 100) / nb_tbs : 0); + + qht_statistics_init(&tb_ctx.htable, &hst); + print_qht_statistics(hst, buf); + qht_statistics_destroy(&hst); + + g_string_append_printf(buf, "\nStatistics:\n"); + tcg_dump_flush_info(buf); +} + +void tcg_dump_stats(GString *buf) +{ + dump_accel_info(buf); + dump_exec_info(buf); + dump_drift_info(buf); +} From 1861993f1fc13e42afed6a618c45a5a95a1457ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 2 Jul 2025 15:03:12 +0200 Subject: [PATCH 2454/2760] accel/system: Introduce @x-accel-stats QMP command MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unstable QMP 'x-accel-stats' dispatches to the AccelOpsClass::get_stats() and get_vcpu_stats() handlers. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Reviewed-by: Markus Armbruster Reviewed-by: Zhao Liu Message-Id: <20250715140048.84942-4-philmd@linaro.org> --- accel/accel-qmp.c | 35 +++++++++++++++++++++++++++++++++++ accel/accel-system.c | 1 + accel/meson.build | 2 +- include/accel/accel-cpu-ops.h | 3 +++ include/accel/accel-ops.h | 2 ++ qapi/accelerator.json | 17 +++++++++++++++++ 6 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 accel/accel-qmp.c diff --git a/accel/accel-qmp.c b/accel/accel-qmp.c new file mode 100644 index 0000000000..5fb70c6631 --- /dev/null +++ b/accel/accel-qmp.c @@ -0,0 +1,35 @@ +/* + * QMP commands related to accelerators + * + * Copyright (c) Linaro + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "qemu/osdep.h" +#include "qemu/accel.h" +#include "qapi/type-helpers.h" +#include "qapi/qapi-commands-accelerator.h" +#include "accel/accel-ops.h" +#include "accel/accel-cpu-ops.h" +#include "hw/core/cpu.h" + +HumanReadableText *qmp_x_accel_stats(Error **errp) +{ + AccelState *accel = current_accel(); + AccelClass *acc = ACCEL_GET_CLASS(accel); + g_autoptr(GString) buf = g_string_new(""); + + if (acc->get_stats) { + acc->get_stats(accel, buf); + } + if (acc->ops->get_vcpu_stats) { + CPUState *cpu; + + CPU_FOREACH(cpu) { + acc->ops->get_vcpu_stats(cpu, buf); + } + } + + return human_readable_text_from_str(buf); +} diff --git a/accel/accel-system.c b/accel/accel-system.c index 8df561b953..76cf4e7ef7 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu/accel.h" #include "hw/boards.h" +#include "hw/core/cpu.h" #include "accel/accel-ops.h" #include "accel/accel-cpu-ops.h" #include "system/cpus.h" diff --git a/accel/meson.build b/accel/meson.build index 52909314bf..25b0f100b5 100644 --- a/accel/meson.build +++ b/accel/meson.build @@ -1,6 +1,6 @@ common_ss.add(files('accel-common.c')) specific_ss.add(files('accel-target.c')) -system_ss.add(files('accel-system.c', 'accel-blocker.c')) +system_ss.add(files('accel-system.c', 'accel-blocker.c', 'accel-qmp.c')) user_ss.add(files('accel-user.c')) subdir('tcg') diff --git a/include/accel/accel-cpu-ops.h b/include/accel/accel-cpu-ops.h index a9191dded7..0674764914 100644 --- a/include/accel/accel-cpu-ops.h +++ b/include/accel/accel-cpu-ops.h @@ -65,6 +65,9 @@ struct AccelOpsClass { /* handle_interrupt is mandatory. */ void (*handle_interrupt)(CPUState *cpu, int mask); + /* get_vcpu_stats: Append statistics of this @cpu to @buf */ + void (*get_vcpu_stats)(CPUState *cpu, GString *buf); + /** * @get_virtual_clock: fetch virtual clock * @set_virtual_clock: set virtual clock diff --git a/include/accel/accel-ops.h b/include/accel/accel-ops.h index 86a27c30fa..23a8c246e1 100644 --- a/include/accel/accel-ops.h +++ b/include/accel/accel-ops.h @@ -25,6 +25,8 @@ struct AccelClass { int (*init_machine)(AccelState *as, MachineState *ms); bool (*cpu_common_realize)(CPUState *cpu, Error **errp); void (*cpu_common_unrealize)(CPUState *cpu); + /* get_stats: Append statistics to @buf */ + void (*get_stats)(AccelState *as, GString *buf); /* system related hooks */ void (*setup_post)(AccelState *as); diff --git a/qapi/accelerator.json b/qapi/accelerator.json index d55fe1aa0a..6029e7307a 100644 --- a/qapi/accelerator.json +++ b/qapi/accelerator.json @@ -37,3 +37,20 @@ # <- { "return": { "enabled": true, "present": true } } ## { 'command': 'query-kvm', 'returns': 'KvmInfo' } + +## +# @x-accel-stats: +# +# Query accelerator statistics +# +# Features: +# +# @unstable: This command is meant for debugging. +# +# Returns: accelerator statistics +# +# Since: 10.1 +## +{ 'command': 'x-accel-stats', + 'returns': 'HumanReadableText', + 'features': [ 'unstable' ] } From c10eb740108c24c65f049e5ae85ed10b2779e75d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 14:44:14 +0200 Subject: [PATCH 2455/2760] accel/system: Add 'info accel' on human monitor MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'info accel' dispatches to the AccelOpsClass::get_stats() and get_vcpu_stats() handlers. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Reviewed-by: Pierrick Bouvier Message-Id: <20250715140048.84942-5-philmd@linaro.org> --- accel/accel-system.c | 8 ++++++++ hmp-commands-info.hx | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/accel/accel-system.c b/accel/accel-system.c index 76cf4e7ef7..1e97c64fdc 100644 --- a/accel/accel-system.c +++ b/accel/accel-system.c @@ -25,6 +25,8 @@ #include "qemu/osdep.h" #include "qemu/accel.h" +#include "qapi/qapi-commands-accelerator.h" +#include "monitor/monitor.h" #include "hw/boards.h" #include "hw/core/cpu.h" #include "accel/accel-ops.h" @@ -103,11 +105,17 @@ void accel_init_ops_interfaces(AccelClass *ac) cpus_register_accel(ops); } +static void accel_ops_class_init(ObjectClass *oc, const void *data) +{ + monitor_register_hmp_info_hrt("accel", qmp_x_accel_stats); +} + static const TypeInfo accel_ops_type_info = { .name = TYPE_ACCEL_OPS, .parent = TYPE_OBJECT, .abstract = true, .class_size = sizeof(AccelOpsClass), + .class_init = accel_ops_class_init, }; static void accel_system_register_types(void) diff --git a/hmp-commands-info.hx b/hmp-commands-info.hx index d797922275..6142f60e7b 100644 --- a/hmp-commands-info.hx +++ b/hmp-commands-info.hx @@ -267,6 +267,18 @@ ERST .cmd = hmp_info_sync_profile, }, + { + .name = "accel", + .args_type = "", + .params = "", + .help = "show accelerator info", + }, + +SRST + ``info accel`` + Show accelerator info. +ERST + SRST ``info sync-profile [-m|-n]`` [*max*] Show synchronization profiling info, up to *max* entries (default: 10), From 2320453031f87e4245a5625f3714d444e987ea0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 14:09:29 +0200 Subject: [PATCH 2456/2760] accel/tcg: Propagate AccelState to dump_accel_info() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Declare tcg_dump_stats() in "tcg/tcg.h" so it can be used out of accel/tcg/, like by {bsd,linux}-user. Next commit will register the TCG AccelClass::get_stats handler, which expects a AccelState, so propagate it to dump_accel_info(). Reviewed-by: Manos Pitsidianakis Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250715140048.84942-6-philmd@linaro.org> --- accel/tcg/internal-common.h | 2 -- accel/tcg/monitor.c | 1 + accel/tcg/tcg-stats.c | 5 ++--- include/tcg/tcg.h | 2 ++ 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index 77a3a0684a..1dbc45dd95 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -139,6 +139,4 @@ G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); -void tcg_dump_stats(GString *buf); - #endif diff --git a/accel/tcg/monitor.c b/accel/tcg/monitor.c index adb9de5a1c..be5c195017 100644 --- a/accel/tcg/monitor.c +++ b/accel/tcg/monitor.c @@ -12,6 +12,7 @@ #include "qapi/qapi-commands-machine.h" #include "monitor/monitor.h" #include "system/tcg.h" +#include "tcg/tcg.h" #include "internal-common.h" HumanReadableText *qmp_x_query_jit(Error **errp) diff --git a/accel/tcg/tcg-stats.c b/accel/tcg/tcg-stats.c index eb6e20ae98..e1a1c4cf4a 100644 --- a/accel/tcg/tcg-stats.c +++ b/accel/tcg/tcg-stats.c @@ -37,9 +37,8 @@ static void dump_drift_info(GString *buf) } } -static void dump_accel_info(GString *buf) +static void dump_accel_info(AccelState *accel, GString *buf) { - AccelState *accel = current_accel(); bool one_insn_per_tb = object_property_get_bool(OBJECT(accel), "one-insn-per-tb", &error_fatal); @@ -209,7 +208,7 @@ static void dump_exec_info(GString *buf) void tcg_dump_stats(GString *buf) { - dump_accel_info(buf); + dump_accel_info(current_accel(), buf); dump_exec_info(buf); dump_drift_info(buf); } diff --git a/include/tcg/tcg.h b/include/tcg/tcg.h index 0c2a319c11..a6d9aa50d4 100644 --- a/include/tcg/tcg.h +++ b/include/tcg/tcg.h @@ -1005,5 +1005,7 @@ static inline const TCGOpcode *tcg_swap_vecop_list(const TCGOpcode *n) bool tcg_can_emit_vecop_list(const TCGOpcode *, TCGType, unsigned); void tcg_dump_ops(TCGContext *s, FILE *f, bool have_prefs); +/* tcg_dump_stats: Append TCG statistics to @buf */ +void tcg_dump_stats(GString *buf); #endif /* TCG_H */ From cf4305ed7a24f25cef0bd35609d03ea688fa24fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 14:45:29 +0200 Subject: [PATCH 2457/2760] accel/tcg: Implement AccelClass::get_stats() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Factor tcg_get_stats() out of tcg_dump_stats(), passing the current accelerator argument to match the AccelClass::get_stats() prototype. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250715140048.84942-7-philmd@linaro.org> --- accel/tcg/internal-common.h | 2 ++ accel/tcg/tcg-all.c | 1 + accel/tcg/tcg-stats.c | 9 +++++++-- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/accel/tcg/internal-common.h b/accel/tcg/internal-common.h index 1dbc45dd95..6adfeefe13 100644 --- a/accel/tcg/internal-common.h +++ b/accel/tcg/internal-common.h @@ -139,4 +139,6 @@ G_NORETURN void cpu_io_recompile(CPUState *cpu, uintptr_t retaddr); void tb_phys_invalidate(TranslationBlock *tb, tb_page_addr_t page_addr); void tb_set_jmp_target(TranslationBlock *tb, int n, uintptr_t addr); +void tcg_get_stats(AccelState *accel, GString *buf); + #endif diff --git a/accel/tcg/tcg-all.c b/accel/tcg/tcg-all.c index eaeb465dfd..5125e1a4e2 100644 --- a/accel/tcg/tcg-all.c +++ b/accel/tcg/tcg-all.c @@ -243,6 +243,7 @@ static void tcg_accel_class_init(ObjectClass *oc, const void *data) ac->init_machine = tcg_init_machine; ac->cpu_common_realize = tcg_exec_realizefn; ac->cpu_common_unrealize = tcg_exec_unrealizefn; + ac->get_stats = tcg_get_stats; ac->allowed = &tcg_allowed; ac->gdbstub_supported_sstep_flags = tcg_gdbstub_supported_sstep_flags; diff --git a/accel/tcg/tcg-stats.c b/accel/tcg/tcg-stats.c index e1a1c4cf4a..ced5dec0c4 100644 --- a/accel/tcg/tcg-stats.c +++ b/accel/tcg/tcg-stats.c @@ -206,9 +206,14 @@ static void dump_exec_info(GString *buf) tcg_dump_flush_info(buf); } -void tcg_dump_stats(GString *buf) +void tcg_get_stats(AccelState *accel, GString *buf) { - dump_accel_info(current_accel(), buf); + dump_accel_info(accel, buf); dump_exec_info(buf); dump_drift_info(buf); } + +void tcg_dump_stats(GString *buf) +{ + tcg_get_stats(current_accel(), buf); +} From 96acc034ffffc8d7dc0cf3dfbf2996cbdbe3dde2 Mon Sep 17 00:00:00 2001 From: Fiona Ebner Date: Thu, 5 Jun 2025 12:09:38 +0200 Subject: [PATCH 2458/2760] iotests: add test for changing the 'drive' property via 'qom-set' Signed-off-by: Fiona Ebner Message-ID: <20250605100938.43133-1-f.ebner@proxmox.com> [kwolf: Fixed up pylint warnings flagged by 297] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- tests/qemu-iotests/tests/qom-set-drive | 75 ++++++++++++++++++++++ tests/qemu-iotests/tests/qom-set-drive.out | 11 ++++ 2 files changed, 86 insertions(+) create mode 100755 tests/qemu-iotests/tests/qom-set-drive create mode 100644 tests/qemu-iotests/tests/qom-set-drive.out diff --git a/tests/qemu-iotests/tests/qom-set-drive b/tests/qemu-iotests/tests/qom-set-drive new file mode 100755 index 0000000000..ec8ddac4fd --- /dev/null +++ b/tests/qemu-iotests/tests/qom-set-drive @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +# group: quick +# +# Test how changing the 'drive' property via 'qom-set' behaves. +# +# Copyright (C) Proxmox Server Solutions GmbH +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +import os +import iotests +from iotests import imgfmt, log, qemu_img_create, QMPTestCase + +image_size = 1 * 1024 * 1024 +images = [os.path.join(iotests.test_dir, f'{i}.img') for i in range(0, 4)] + +class TestQOMSetDrive(QMPTestCase): + def setUp(self) -> None: + for image in images: + qemu_img_create('-f', imgfmt, image, str(image_size)) + + self.vm = iotests.VM() + for i, image in enumerate(images): + self.vm.add_blockdev(self.vm.qmp_to_opts({ + 'driver': imgfmt, + 'node-name': f'node{i}', + 'file': { + 'driver': 'file', + 'filename': image, + } + })) + self.vm.add_object('iothread,id=iothread0') + self.vm.add_device('virtio-scsi,iothread=iothread0') + self.vm.add_device('scsi-hd,id=iot,drive=node0') + self.vm.add_device('virtio-scsi') + self.vm.add_device('scsi-hd,id=no-iot,drive=node1') + self.vm.launch() + + def tearDown(self) -> None: + self.vm.shutdown() + for image in images: + os.remove(image) + + def test_qom_set_drive(self) -> None: + log(self.vm.qmp('qom-get', path='/machine/peripheral/iot', + property='drive')) + log(self.vm.qmp('qom-set', path='/machine/peripheral/iot', + property='drive', value='node2')) + log(self.vm.qmp('qom-get', path='/machine/peripheral/iot', + property='drive')) + + log(self.vm.qmp('qom-get', path='/machine/peripheral/no-iot', + property='drive')) + log(self.vm.qmp('qom-set', path='/machine/peripheral/no-iot', + property='drive', value='node3')) + log(self.vm.qmp('qom-get', path='/machine/peripheral/no-iot', + property='drive')) + +if __name__ == '__main__': + iotests.activate_logging() + # LUKS would require special key-secret handling in add_blockdevs() + iotests.main(supported_fmts=['generic'], + unsupported_fmts=['luks']) diff --git a/tests/qemu-iotests/tests/qom-set-drive.out b/tests/qemu-iotests/tests/qom-set-drive.out new file mode 100644 index 0000000000..7fc243dca6 --- /dev/null +++ b/tests/qemu-iotests/tests/qom-set-drive.out @@ -0,0 +1,11 @@ +{"return": "node0"} +{"error": {"class": "GenericError", "desc": "Different aio context is not supported for new node"}} +{"return": "node0"} +{"return": "node1"} +{"return": {}} +{"return": "node3"} +. +---------------------------------------------------------------------- +Ran 1 tests + +OK From 1c47abc5779dfaaee303f0bff144e88dc8fb74d5 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:43 +0300 Subject: [PATCH 2459/2760] qemu-img: measure: convert img_size to signed, simplify handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit qemu_opt_set_number() expects signed int64_t. Use int64_t instead of uint64_t for img_size, use -1 as "unset" value instead of UINT64_MAX, and do not require temporary sval for conversion from string. Signed-off-by: Michael Tokarev Reviewed-by: Daniel P. Berrangé Reviewed-by: Kevin Wolf Message-ID: <20250531171609.197078-2-mjt@tls.msk.ru> Signed-off-by: Kevin Wolf --- qemu-img.c | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index e75707180d..e676602fc7 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -5370,7 +5370,7 @@ static int img_measure(int argc, char **argv) QemuOpts *sn_opts = NULL; QemuOptsList *create_opts = NULL; bool image_opts = false; - uint64_t img_size = UINT64_MAX; + int64_t img_size = -1; BlockMeasureInfo *info = NULL; Error *local_err = NULL; int ret = 1; @@ -5428,16 +5428,11 @@ static int img_measure(int argc, char **argv) } break; case OPTION_SIZE: - { - int64_t sval; - - sval = cvtnum("image size", optarg); - if (sval < 0) { + img_size = cvtnum("image size", optarg); + if (img_size < 0) { goto out; } - img_size = (uint64_t)sval; - } - break; + break; } } @@ -5452,11 +5447,11 @@ static int img_measure(int argc, char **argv) error_report("--image-opts, -f, and -l require a filename argument."); goto out; } - if (filename && img_size != UINT64_MAX) { + if (filename && img_size != -1) { error_report("--size N cannot be used together with a filename."); goto out; } - if (!filename && img_size == UINT64_MAX) { + if (!filename && img_size == -1) { error_report("Either --size N or one filename must be specified."); goto out; } @@ -5504,7 +5499,7 @@ static int img_measure(int argc, char **argv) goto out; } } - if (img_size != UINT64_MAX) { + if (img_size != -1) { qemu_opt_set_number(opts, BLOCK_OPT_SIZE, img_size, &error_abort); } From 443761ab3874d0ffc2df0a0cd73dd1ca833b277e Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:44 +0300 Subject: [PATCH 2460/2760] qemu-img: create: convert img_size to signed, simplify handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Initializing an unsigned as -1, or using temporary sval for conversion is awkward. Since we don't allow other "negative" values anyway, use signed value and pass it to bdrv_img_create() (where it is properly converted to unsigned), simplifying code. Signed-off-by: Michael Tokarev Reviewed-by: Daniel P. Berrangé Reviewed-by: Kevin Wolf Message-ID: <20250531171609.197078-3-mjt@tls.msk.ru> Signed-off-by: Kevin Wolf --- qemu-img.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index e676602fc7..6750d0657c 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -511,7 +511,7 @@ static int64_t cvtnum(const char *name, const char *value) static int img_create(int argc, char **argv) { int c; - uint64_t img_size = -1; + int64_t img_size = -1; const char *fmt = "raw"; const char *base_fmt = NULL; const char *filename; @@ -582,13 +582,10 @@ static int img_create(int argc, char **argv) /* Get image size, if specified */ if (optind < argc) { - int64_t sval; - - sval = cvtnum("image size", argv[optind++]); - if (sval < 0) { + img_size = cvtnum("image size", argv[optind++]); + if (img_size < 0) { goto fail; } - img_size = (uint64_t)sval; } if (optind != argc) { error_exit("Unexpected argument: %s", argv[optind]); From fef5f1d4aa60360226bc15288611998b3699618f Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:45 +0300 Subject: [PATCH 2461/2760] qemu-img: global option processing and error printing In order to correctly print executable name in various error messages, pass argv[0] to error_exit() function. This way, error messages will refer to actual executable name, which may be different from 'qemu-img'. For subcommands, pass original command name from the qemu-img argv[0], plus the subcommand name, as its own argv[0] element, so error messages can be more useful. Also don't require at least 3 options on the command line: it makes no sense with options before subcommand. Introduce tryhelp() function which just prints try 'command-name --help' for more info and exits. When tryhelp() is called from within a subcommand handler, the message will look like: try 'command-name subcommand --help' for more information qemu-img uses getopt_long() with ':' as the first char in optstring parameter, which means it doesn't print error messages but return ':' or '?' instead, and qemu-img uses unrecognized_option() or missing_argument() function to print error messages. But it doesn't quite work: $ ./qemu-img -xx qemu-img: unrecognized option './qemu-img' so the aim is to let getopt_long() to print regular error messages instead (removing ':' prefix from optstring) and remove handling of '?' and ':' "options" entirely. With concatenated argv[0] and the subcommand, it all finally does the right thing in all cases. This will be done in subsequent changes command by command, with main() done last. unrecognized_option() and missing_argument() functions prototypes aren't changed by this patch, since they're called from many places and will be removed a few patches later. Only artifical "qemu-img" argv0 is provided in there for now. Signed-off-by: Michael Tokarev Reviewed-by: Kevin Wolf Message-ID: <20250531171609.197078-4-mjt@tls.msk.ru> Signed-off-by: Kevin Wolf --- qemu-img.c | 80 +++++++++++++++++++++++++++++------------------------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 6750d0657c..2b46c952bd 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -101,8 +101,15 @@ static void format_print(void *opaque, const char *name) printf(" %s", name); } -static G_NORETURN G_GNUC_PRINTF(1, 2) -void error_exit(const char *fmt, ...) +static G_NORETURN +void tryhelp(const char *argv0) +{ + error_printf("Try '%s --help' for more information\n", argv0); + exit(EXIT_FAILURE); +} + +static G_NORETURN G_GNUC_PRINTF(2, 3) +void error_exit(const char *argv0, const char *fmt, ...) { va_list ap; @@ -110,20 +117,19 @@ void error_exit(const char *fmt, ...) error_vreport(fmt, ap); va_end(ap); - error_printf("Try 'qemu-img --help' for more information\n"); - exit(EXIT_FAILURE); + tryhelp(argv0); } static G_NORETURN void missing_argument(const char *option) { - error_exit("missing argument for option '%s'", option); + error_exit("qemu-img", "missing argument for option '%s'", option); } static G_NORETURN void unrecognized_option(const char *option) { - error_exit("unrecognized option '%s'", option); + error_exit("qemu-img", "unrecognized option '%s'", option); } /* Please keep in synch with docs/tools/qemu-img.rst */ @@ -576,7 +582,7 @@ static int img_create(int argc, char **argv) } if (optind >= argc) { - error_exit("Expecting image file name"); + error_exit(argv[0], "Expecting image file name"); } optind++; @@ -588,7 +594,7 @@ static int img_create(int argc, char **argv) } } if (optind != argc) { - error_exit("Unexpected argument: %s", argv[optind]); + error_exit(argv[0], "Unexpected argument: %s", argv[optind]); } bdrv_img_create(filename, fmt, base_filename, base_fmt, @@ -770,7 +776,7 @@ static int img_check(int argc, char **argv) } else if (!strcmp(optarg, "all")) { fix = BDRV_FIX_LEAKS | BDRV_FIX_ERRORS; } else { - error_exit("Unknown option value for -r " + error_exit(argv[0], "Unknown option value for -r " "(expecting 'leaks' or 'all'): %s", optarg); } break; @@ -795,7 +801,7 @@ static int img_check(int argc, char **argv) } } if (optind != argc - 1) { - error_exit("Expecting one image file name"); + error_exit(argv[0], "Expecting one image file name"); } filename = argv[optind++]; @@ -1025,7 +1031,7 @@ static int img_commit(int argc, char **argv) } if (optind != argc - 1) { - error_exit("Expecting one image file name"); + error_exit(argv[0], "Expecting one image file name"); } filename = argv[optind++]; @@ -1446,7 +1452,7 @@ static int img_compare(int argc, char **argv) if (optind != argc - 2) { - error_exit("Expecting two image file names"); + error_exit(argv[0], "Expecting two image file names"); } filename1 = argv[optind++]; filename2 = argv[optind++]; @@ -3056,7 +3062,7 @@ static int img_info(int argc, char **argv) } } if (optind != argc - 1) { - error_exit("Expecting one image file name"); + error_exit(argv[0], "Expecting one image file name"); } filename = argv[optind++]; @@ -3296,7 +3302,7 @@ static int img_map(int argc, char **argv) } } if (optind != argc - 1) { - error_exit("Expecting one image file name"); + error_exit(argv[0], "Expecting one image file name"); } filename = argv[optind]; @@ -3411,7 +3417,7 @@ static int img_snapshot(int argc, char **argv) return 0; case 'l': if (action) { - error_exit("Cannot mix '-l', '-a', '-c', '-d'"); + error_exit(argv[0], "Cannot mix '-l', '-a', '-c', '-d'"); return 0; } action = SNAPSHOT_LIST; @@ -3419,7 +3425,7 @@ static int img_snapshot(int argc, char **argv) break; case 'a': if (action) { - error_exit("Cannot mix '-l', '-a', '-c', '-d'"); + error_exit(argv[0], "Cannot mix '-l', '-a', '-c', '-d'"); return 0; } action = SNAPSHOT_APPLY; @@ -3427,7 +3433,7 @@ static int img_snapshot(int argc, char **argv) break; case 'c': if (action) { - error_exit("Cannot mix '-l', '-a', '-c', '-d'"); + error_exit(argv[0], "Cannot mix '-l', '-a', '-c', '-d'"); return 0; } action = SNAPSHOT_CREATE; @@ -3435,7 +3441,7 @@ static int img_snapshot(int argc, char **argv) break; case 'd': if (action) { - error_exit("Cannot mix '-l', '-a', '-c', '-d'"); + error_exit(argv[0], "Cannot mix '-l', '-a', '-c', '-d'"); return 0; } action = SNAPSHOT_DELETE; @@ -3457,7 +3463,7 @@ static int img_snapshot(int argc, char **argv) } if (optind != argc - 1) { - error_exit("Expecting one image file name"); + error_exit(argv[0], "Expecting one image file name"); } filename = argv[optind++]; @@ -3626,10 +3632,11 @@ static int img_rebase(int argc, char **argv) } if (optind != argc - 1) { - error_exit("Expecting one image file name"); + error_exit(argv[0], "Expecting one image file name"); } if (!unsafe && !out_baseimg) { - error_exit("Must specify backing file (-b) or use unsafe mode (-u)"); + error_exit(argv[0], + "Must specify backing file (-b) or use unsafe mode (-u)"); } filename = argv[optind++]; @@ -4053,7 +4060,7 @@ static int img_resize(int argc, char **argv) /* Remove size from argv manually so that negative numbers are not treated * as options by getopt. */ if (argc < 3) { - error_exit("Not enough arguments"); + error_exit(argv[0], "Not enough arguments"); return 1; } @@ -4111,7 +4118,7 @@ static int img_resize(int argc, char **argv) } } if (optind != argc - 1) { - error_exit("Expecting image file name and size"); + error_exit(argv[0], "Expecting image file name and size"); } filename = argv[optind++]; @@ -4308,7 +4315,7 @@ static int img_amend(int argc, char **argv) } if (!options) { - error_exit("Must specify options (-o)"); + error_exit(argv[0], "Must specify options (-o)"); } if (quiet) { @@ -4674,7 +4681,7 @@ static int img_bench(int argc, char **argv) } if (optind != argc - 1) { - error_exit("Expecting one image file name"); + error_exit(argv[0], "Expecting one image file name"); } filename = argv[argc - 1]; @@ -5564,9 +5571,6 @@ int main(int argc, char **argv) module_call_init(MODULE_INIT_QOM); bdrv_init(); - if (argc < 2) { - error_exit("Not enough arguments"); - } qemu_add_opts(&qemu_source_opts); qemu_add_opts(&qemu_trace_opts); @@ -5591,15 +5595,11 @@ int main(int argc, char **argv) } } - cmdname = argv[optind]; - - /* reset getopt_long scanning */ - argc -= optind; - if (argc < 1) { - return 0; + if (optind >= argc) { + error_exit(argv[0], "Not enough arguments"); } - argv += optind; - qemu_reset_optind(); + + cmdname = argv[optind]; if (!trace_init_backends()) { exit(1); @@ -5610,10 +5610,16 @@ int main(int argc, char **argv) /* find the command */ for (cmd = img_cmds; cmd->name != NULL; cmd++) { if (!strcmp(cmdname, cmd->name)) { + g_autofree char *argv0 = g_strdup_printf("%s %s", argv[0], cmdname); + /* reset options and getopt processing (incl return order) */ + argv += optind; + argc -= optind; + qemu_reset_optind(); + argv[0] = argv0; return cmd->handler(argc, argv); } } /* not found */ - error_exit("Command not found: %s", cmdname); + error_exit(argv[0], "Command not found: %s", cmdname); } From 80415af893644e440f73e8c87dc104f4ebd36434 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:46 +0300 Subject: [PATCH 2462/2760] qemu-img: pass current cmd info into command handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This info will be used to generate --help output. Signed-off-by: Michael Tokarev Reviewed-by: Daniel P. Berrangé Reviewed-by: Kevin Wolf Message-ID: <20250531171609.197078-5-mjt@tls.msk.ru> Signed-off-by: Kevin Wolf --- qemu-img.c | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 2b46c952bd..a414837355 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -60,7 +60,7 @@ typedef struct img_cmd_t { const char *name; - int (*handler)(int argc, char **argv); + int (*handler)(const struct img_cmd_t *ccmd, int argc, char **argv); } img_cmd_t; enum { @@ -514,7 +514,7 @@ static int64_t cvtnum(const char *name, const char *value) return cvtnum_full(name, value, 0, INT64_MAX); } -static int img_create(int argc, char **argv) +static int img_create(const img_cmd_t *ccmd, int argc, char **argv) { int c; int64_t img_size = -1; @@ -719,7 +719,7 @@ static int collect_image_check(BlockDriverState *bs, * 3 - Check completed, image has leaked clusters, but is good otherwise * 63 - Checks are not supported by the image format */ -static int img_check(int argc, char **argv) +static int img_check(const img_cmd_t *ccmd, int argc, char **argv) { int c, ret; OutputFormat output_format = OFORMAT_HUMAN; @@ -951,7 +951,7 @@ static void run_block_job(BlockJob *job, Error **errp) } } -static int img_commit(int argc, char **argv) +static int img_commit(const img_cmd_t *ccmd, int argc, char **argv) { int c, ret, flags; const char *filename, *fmt, *cache, *base; @@ -1358,7 +1358,7 @@ static int check_empty_sectors(BlockBackend *blk, int64_t offset, * 1 - Images differ * >1 - Error occurred */ -static int img_compare(int argc, char **argv) +static int img_compare(const img_cmd_t *ccmd, int argc, char **argv) { const char *fmt1 = NULL, *fmt2 = NULL, *cache, *filename1, *filename2; BlockBackend *blk1, *blk2; @@ -2234,7 +2234,7 @@ static void set_rate_limit(BlockBackend *blk, int64_t rate_limit) blk_set_io_limits(blk, &cfg); } -static int img_convert(int argc, char **argv) +static int img_convert(const img_cmd_t *ccmd, int argc, char **argv) { int c, bs_i, flags, src_flags = BDRV_O_NO_SHARE; const char *fmt = NULL, *out_fmt = NULL, *cache = "unsafe", @@ -3002,7 +3002,7 @@ static BlockGraphInfoList *collect_image_info_list(bool image_opts, return NULL; } -static int img_info(int argc, char **argv) +static int img_info(const img_cmd_t *ccmd, int argc, char **argv) { int c; OutputFormat output_format = OFORMAT_HUMAN; @@ -3227,7 +3227,7 @@ static inline bool entry_mergeable(const MapEntry *curr, const MapEntry *next) return true; } -static int img_map(int argc, char **argv) +static int img_map(const img_cmd_t *ccmd, int argc, char **argv) { int c; OutputFormat output_format = OFORMAT_HUMAN; @@ -3376,7 +3376,7 @@ static int img_map(int argc, char **argv) #define SNAPSHOT_APPLY 3 #define SNAPSHOT_DELETE 4 -static int img_snapshot(int argc, char **argv) +static int img_snapshot(const img_cmd_t *ccmd, int argc, char **argv) { BlockBackend *blk; BlockDriverState *bs; @@ -3536,7 +3536,7 @@ static int img_snapshot(int argc, char **argv) return 0; } -static int img_rebase(int argc, char **argv) +static int img_rebase(const img_cmd_t *ccmd, int argc, char **argv) { BlockBackend *blk = NULL, *blk_old_backing = NULL, *blk_new_backing = NULL; uint8_t *buf_old = NULL; @@ -4030,7 +4030,7 @@ static int img_rebase(int argc, char **argv) return 0; } -static int img_resize(int argc, char **argv) +static int img_resize(const img_cmd_t *ccmd, int argc, char **argv) { Error *err = NULL; int c, ret, relative; @@ -4243,7 +4243,7 @@ static int print_amend_option_help(const char *format) return 0; } -static int img_amend(int argc, char **argv) +static int img_amend(const img_cmd_t *ccmd, int argc, char **argv) { Error *err = NULL; int c, ret = 0; @@ -4511,7 +4511,7 @@ static void bench_cb(void *opaque, int ret) } } -static int img_bench(int argc, char **argv) +static int img_bench(const img_cmd_t *ccmd, int argc, char **argv) { int c, ret = 0; const char *fmt = NULL, *filename; @@ -4781,7 +4781,7 @@ typedef struct ImgBitmapAction { QSIMPLEQ_ENTRY(ImgBitmapAction) next; } ImgBitmapAction; -static int img_bitmap(int argc, char **argv) +static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv) { Error *err = NULL; int c, ret = 1; @@ -5081,7 +5081,7 @@ static int img_dd_skip(const char *arg, return 0; } -static int img_dd(int argc, char **argv) +static int img_dd(const img_cmd_t *ccmd, int argc, char **argv) { int ret = 0; char *arg = NULL; @@ -5349,7 +5349,7 @@ static void dump_json_block_measure_info(BlockMeasureInfo *info) g_string_free(str, true); } -static int img_measure(int argc, char **argv) +static int img_measure(const img_cmd_t *ccmd, int argc, char **argv) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, @@ -5616,7 +5616,7 @@ int main(int argc, char **argv) argc -= optind; qemu_reset_optind(); argv[0] = argv0; - return cmd->handler(argc, argv); + return cmd->handler(cmd, argc, argv); } } From 52292ba8b65723546478bc2c146542a601382e82 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:47 +0300 Subject: [PATCH 2463/2760] qemu-img: create: refresh options/--help (short option change) Create helper function cmd_help() to display command-specific help text, and use it to print --help for 'create' subcommand. Add missing long options (eg --format) in img_create(). Recognize -B option for --backing-format, keep -F for backward compatibility, Reorder options for consistency. Remove usage of missing_argument()/unrecognized_option() in img_create(). Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-6-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- docs/tools/qemu-img.rst | 10 ++--- qemu-img.c | 84 +++++++++++++++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 21 deletions(-) diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index 3653adb963..a21e439082 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -467,11 +467,11 @@ Command description: ``--skip-broken-bitmaps`` is also specified to copy only the consistent bitmaps. -.. option:: create [--object OBJECTDEF] [-q] [-f FMT] [-b BACKING_FILE [-F BACKING_FMT]] [-u] [-o OPTIONS] FILENAME [SIZE] +.. option:: create [-f FMT] [-o FMT_OPTS] [-b BACKING_FILE [-B BACKING_FMT]] [-u] [-q] [--object OBJDEF] FILE [SIZE] - Create the new disk image *FILENAME* of size *SIZE* and format - *FMT*. Depending on the file format, you can add one or more *OPTIONS* - that enable additional features of this format. + Create the new disk image *FILE* of size *SIZE* and format + *FMT*. Depending on the file format, you can add one or more *FMT_OPTS* + options that enable additional features of this format. If the option *BACKING_FILE* is specified, then the image will record only the differences from *BACKING_FILE*. No size needs to be specified in @@ -479,7 +479,7 @@ Command description: ``commit`` monitor command (or ``qemu-img commit``). If a relative path name is given, the backing file is looked up relative to - the directory containing *FILENAME*. + the directory containing *FILE*. Note that a given backing file will be opened to check that it is valid. Use the ``-u`` option to enable unsafe backing file mode, which means that the diff --git a/qemu-img.c b/qemu-img.c index a414837355..6569efd310 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -132,6 +132,32 @@ void unrecognized_option(const char *option) error_exit("qemu-img", "unrecognized option '%s'", option); } +/* + * Print --help output for a command and exit. + * @syntax and @description are multi-line with trailing EOL + * (to allow easy extending of the text) + * @syntax has each subsequent line indented by 8 chars. + * @description is indented by 2 chars for argument on each own line, + * and with 5 chars for argument description (like -h arg below). + */ +static G_NORETURN +void cmd_help(const img_cmd_t *ccmd, + const char *syntax, const char *arguments) +{ + printf( +"Usage:\n" +"\n" +" %s %s %s" +"\n" +"Arguments:\n" +" -h, --help\n" +" print this help and exit\n" +"%s\n", + "qemu-img", ccmd->name, + syntax, arguments); + exit(EXIT_SUCCESS); +} + /* Please keep in synch with docs/tools/qemu-img.rst */ static G_NORETURN void help(void) @@ -530,29 +556,46 @@ static int img_create(const img_cmd_t *ccmd, int argc, char **argv) for(;;) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, + {"format", required_argument, 0, 'f'}, + {"options", required_argument, 0, 'o'}, + {"backing", required_argument, 0, 'b'}, + {"backing-format", required_argument, 0, 'B'}, /* was -F in 10.0 */ + {"backing-unsafe", no_argument, 0, 'u'}, + {"quiet", no_argument, 0, 'q'}, {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":F:b:f:ho:qu", + c = getopt_long(argc, argv, "hf:o:b:F:B:uq", long_options, NULL); if (c == -1) { break; } switch(c) { - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); - break; case 'h': - help(); - break; - case 'F': - base_fmt = optarg; - break; - case 'b': - base_filename = optarg; + cmd_help(ccmd, "[-f FMT] [-o FMT_OPTS]\n" +" [-b BACKING_FILE [-B BACKING_FMT]] [-u]\n" +" [-q] [--object OBJDEF] FILE [SIZE]\n" +, +" -f, --format FMT\n" +" specifies the format of the new image (default: raw)\n" +" -o, --options FMT_OPTS\n" +" format-specific options (specify '-o help' for help)\n" +" -b, --backing BACKING_FILE\n" +" create target image to be a CoW on top of BACKING_FILE\n" +" -B, --backing-format BACKING_FMT (was -F in <= 10.0)\n" +" specifies the format of BACKING_FILE (default: probing is used)\n" +" -u, --backing-unsafe\n" +" do not fail if BACKING_FILE can not be read\n" +" -q, --quiet\n" +" quiet mode (produce only error messages if any)\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" FILE\n" +" name of the image file to create (will be overritten if already exists)\n" +" SIZE[bKMGTPE]\n" +" image size with optional multiplier suffix (powers of 1024)\n" +" (required unless BACKING_FILE is specified)\n" +); break; case 'f': fmt = optarg; @@ -562,15 +605,24 @@ static int img_create(const img_cmd_t *ccmd, int argc, char **argv) goto fail; } break; - case 'q': - quiet = true; + case 'b': + base_filename = optarg; + break; + case 'F': /* <=10.0 */ + case 'B': + base_fmt = optarg; break; case 'u': flags |= BDRV_O_NO_BACKING; break; + case 'q': + quiet = true; + break; case OPTION_OBJECT: user_creatable_process_cmdline(optarg); break; + default: + tryhelp(argv[0]); } } From a03cd2982d84c6901f3583e31ef12ac7fd170052 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:48 +0300 Subject: [PATCH 2464/2760] qemu-img: factor out parse_output_format() and use it in the code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use common code and simplify error message Signed-off-by: Michael Tokarev Reviewed-by: Daniel P. Berrangé Reviewed-by: Kevin Wolf Message-ID: <20250531171609.197078-7-mjt@tls.msk.ru> Signed-off-by: Kevin Wolf --- qemu-img.c | 63 +++++++++----------------------- tests/qemu-iotests/178 | 2 +- tests/qemu-iotests/178.out.qcow2 | 3 +- tests/qemu-iotests/178.out.raw | 3 +- tests/qemu-iotests/common.filter | 6 +++ 5 files changed, 29 insertions(+), 48 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 6569efd310..b60b4d38df 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -158,6 +158,17 @@ void cmd_help(const img_cmd_t *ccmd, exit(EXIT_SUCCESS); } +static OutputFormat parse_output_format(const char *argv0, const char *arg) +{ + if (!strcmp(arg, "json")) { + return OFORMAT_JSON; + } else if (!strcmp(arg, "human")) { + return OFORMAT_HUMAN; + } else { + error_exit(argv0, "--output expects 'human' or 'json', not '%s'", arg); + } +} + /* Please keep in synch with docs/tools/qemu-img.rst */ static G_NORETURN void help(void) @@ -775,7 +786,7 @@ static int img_check(const img_cmd_t *ccmd, int argc, char **argv) { int c, ret; OutputFormat output_format = OFORMAT_HUMAN; - const char *filename, *fmt, *output, *cache; + const char *filename, *fmt, *cache; BlockBackend *blk; BlockDriverState *bs; int fix = 0; @@ -787,7 +798,6 @@ static int img_check(const img_cmd_t *ccmd, int argc, char **argv) bool force_share = false; fmt = NULL; - output = NULL; cache = BDRV_DEFAULT_CACHE; for(;;) { @@ -833,7 +843,7 @@ static int img_check(const img_cmd_t *ccmd, int argc, char **argv) } break; case OPTION_OUTPUT: - output = optarg; + output_format = parse_output_format(argv[0], optarg); break; case 'T': cache = optarg; @@ -857,15 +867,6 @@ static int img_check(const img_cmd_t *ccmd, int argc, char **argv) } filename = argv[optind++]; - if (output && !strcmp(output, "json")) { - output_format = OFORMAT_JSON; - } else if (output && !strcmp(output, "human")) { - output_format = OFORMAT_HUMAN; - } else if (output) { - error_report("--output must be used with human or json as argument."); - return 1; - } - ret = bdrv_parse_cache_mode(cache, &flags, &writethrough); if (ret < 0) { error_report("Invalid source cache option: %s", cache); @@ -3059,13 +3060,12 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv) int c; OutputFormat output_format = OFORMAT_HUMAN; bool chain = false; - const char *filename, *fmt, *output; + const char *filename, *fmt; BlockGraphInfoList *list; bool image_opts = false; bool force_share = false; fmt = NULL; - output = NULL; for(;;) { int option_index = 0; static const struct option long_options[] = { @@ -3100,7 +3100,7 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv) force_share = true; break; case OPTION_OUTPUT: - output = optarg; + output_format = parse_output_format(argv[0], optarg); break; case OPTION_BACKING_CHAIN: chain = true; @@ -3118,15 +3118,6 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv) } filename = argv[optind++]; - if (output && !strcmp(output, "json")) { - output_format = OFORMAT_JSON; - } else if (output && !strcmp(output, "human")) { - output_format = OFORMAT_HUMAN; - } else if (output) { - error_report("--output must be used with human or json as argument."); - return 1; - } - list = collect_image_info_list(image_opts, filename, fmt, chain, force_share); if (!list) { @@ -3285,7 +3276,7 @@ static int img_map(const img_cmd_t *ccmd, int argc, char **argv) OutputFormat output_format = OFORMAT_HUMAN; BlockBackend *blk; BlockDriverState *bs; - const char *filename, *fmt, *output; + const char *filename, *fmt; int64_t length; MapEntry curr = { .length = 0 }, next; int ret = 0; @@ -3295,7 +3286,6 @@ static int img_map(const img_cmd_t *ccmd, int argc, char **argv) int64_t max_length = -1; fmt = NULL; - output = NULL; for (;;) { int option_index = 0; static const struct option long_options[] = { @@ -3331,7 +3321,7 @@ static int img_map(const img_cmd_t *ccmd, int argc, char **argv) force_share = true; break; case OPTION_OUTPUT: - output = optarg; + output_format = parse_output_format(argv[0], optarg); break; case 's': start_offset = cvtnum("start offset", optarg); @@ -3358,15 +3348,6 @@ static int img_map(const img_cmd_t *ccmd, int argc, char **argv) } filename = argv[optind]; - if (output && !strcmp(output, "json")) { - output_format = OFORMAT_JSON; - } else if (output && !strcmp(output, "human")) { - output_format = OFORMAT_HUMAN; - } else if (output) { - error_report("--output must be used with human or json as argument."); - return 1; - } - blk = img_open(image_opts, filename, fmt, 0, false, false, force_share); if (!blk) { return 1; @@ -5473,15 +5454,7 @@ static int img_measure(const img_cmd_t *ccmd, int argc, char **argv) image_opts = true; break; case OPTION_OUTPUT: - if (!strcmp(optarg, "json")) { - output_format = OFORMAT_JSON; - } else if (!strcmp(optarg, "human")) { - output_format = OFORMAT_HUMAN; - } else { - error_report("--output must be used with human or json " - "as argument."); - goto out; - } + output_format = parse_output_format(argv[0], optarg); break; case OPTION_SIZE: img_size = cvtnum("image size", optarg); diff --git a/tests/qemu-iotests/178 b/tests/qemu-iotests/178 index 8df241ead8..463c59a77f 100755 --- a/tests/qemu-iotests/178 +++ b/tests/qemu-iotests/178 @@ -58,7 +58,7 @@ $QEMU_IMG measure -f qcow2 # missing filename $QEMU_IMG measure -l snap1 # missing filename $QEMU_IMG measure -o , # invalid option list $QEMU_IMG measure -l snapshot.foo=bar # invalid snapshot option -$QEMU_IMG measure --output foo # invalid output format +$QEMU_IMG measure --output foo 2>&1 | _filter_qemu_img # invalid output format $QEMU_IMG measure --size -1 # invalid image size $QEMU_IMG measure -O foo "$TEST_IMG" # unknown image file format diff --git a/tests/qemu-iotests/178.out.qcow2 b/tests/qemu-iotests/178.out.qcow2 index fe193fd5f4..61506b519f 100644 --- a/tests/qemu-iotests/178.out.qcow2 +++ b/tests/qemu-iotests/178.out.qcow2 @@ -12,7 +12,8 @@ qemu-img: --image-opts, -f, and -l require a filename argument. qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar' -qemu-img: --output must be used with human or json as argument. +qemu-img: --output expects 'human' or 'json', not 'foo' +Try 'qemu-img measure --help' for more information qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. qemu-img: Unknown file format 'foo' diff --git a/tests/qemu-iotests/178.out.raw b/tests/qemu-iotests/178.out.raw index 445e460fad..6d994a433a 100644 --- a/tests/qemu-iotests/178.out.raw +++ b/tests/qemu-iotests/178.out.raw @@ -12,7 +12,8 @@ qemu-img: --image-opts, -f, and -l require a filename argument. qemu-img: Invalid option list: , qemu-img: Invalid parameter 'snapshot.foo' qemu-img: Failed in parsing snapshot param 'snapshot.foo=bar' -qemu-img: --output must be used with human or json as argument. +qemu-img: --output expects 'human' or 'json', not 'foo' +Try 'qemu-img measure --help' for more information qemu-img: Invalid image size specified. Must be between 0 and 9223372036854775807. qemu-img: Unknown file format 'foo' diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index fc3c64bcb8..67f819d866 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -86,6 +86,12 @@ _filter_qemu() -e $'s#\r##' # QEMU monitor uses \r\n line endings } +# replace occurrences of QEMU_IMG_PROG with "qemu-img" +_filter_qemu_img() +{ + sed -e "s#$QEMU_IMG_PROG#qemu-img#g" +} + # replace problematic QMP output like timestamps _filter_qmp() { From d1dcf1422440d9aa207d34e62e7a6b067b87f242 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:49 +0300 Subject: [PATCH 2465/2760] qemu-img: check: refresh options/--help Add missing long options and --help output, reorder options for consistency. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-8-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 60 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 19 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index b60b4d38df..d310f0de0d 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -805,31 +805,57 @@ static int img_check(const img_cmd_t *ccmd, int argc, char **argv) static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"format", required_argument, 0, 'f'}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {"cache", required_argument, 0, 'T'}, {"repair", required_argument, 0, 'r'}, + {"force-share", no_argument, 0, 'U'}, {"output", required_argument, 0, OPTION_OUTPUT}, + {"quiet", no_argument, 0, 'q'}, {"object", required_argument, 0, OPTION_OBJECT}, - {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, - {"force-share", no_argument, 0, 'U'}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hf:r:T:qU", + c = getopt_long(argc, argv, "hf:T:r:Uq", long_options, &option_index); if (c == -1) { break; } switch(c) { - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); - break; case 'h': - help(); + cmd_help(ccmd, "[-f FMT | --image-opts] [-T CACHE_MODE] [-r leaks|all]\n" +" [-U] [--output human|json] [-q] [--object OBJDEF] FILE\n" +, +" -f, --format FMT\n" +" specifies the format of the image explicitly (default: probing is used)\n" +" --image-opts\n" +" treat FILE as an option string (key=value,..), not a file name\n" +" (incompatible with -f|--format)\n" +" -T, --cache CACHE_MODE\n" /* why not -t ? */ +" cache mode (default: " BDRV_DEFAULT_CACHE ")\n" +" -r, --repair leaks|all\n" +" repair errors of the given category in the image (image will be\n" +" opened in read-write mode, incompatible with -U|--force-share)\n" +" -U, --force-share\n" +" open image in shared mode for concurrent access\n" +" --output human|json\n" +" output format (default: human)\n" +" -q, --quiet\n" +" quiet mode (produce only error messages if any)\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" FILE\n" +" name of the image file, or an option string (key=value,..)\n" +" with --image-opts, to operate on\n" +); break; case 'f': fmt = optarg; break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; + case 'T': + cache = optarg; + break; case 'r': flags |= BDRV_O_RDWR; @@ -842,24 +868,20 @@ static int img_check(const img_cmd_t *ccmd, int argc, char **argv) "(expecting 'leaks' or 'all'): %s", optarg); } break; + case 'U': + force_share = true; + break; case OPTION_OUTPUT: output_format = parse_output_format(argv[0], optarg); break; - case 'T': - cache = optarg; - break; case 'q': quiet = true; break; - case 'U': - force_share = true; - break; case OPTION_OBJECT: user_creatable_process_cmdline(optarg); break; - case OPTION_IMAGE_OPTS: - image_opts = true; - break; + default: + tryhelp(argv[0]); } } if (optind != argc - 1) { From bf1e1cd357b0dd395f880ac9a9cb436c0c304139 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:50 +0300 Subject: [PATCH 2466/2760] qemu-img: simplify --repair error message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Michael Tokarev Reviewed-by: Daniel P. Berrangé Message-ID: <20250531171609.197078-9-mjt@tls.msk.ru> [kwolf: Added missing comma in help text] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index d310f0de0d..ec6802b03b 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -864,8 +864,9 @@ static int img_check(const img_cmd_t *ccmd, int argc, char **argv) } else if (!strcmp(optarg, "all")) { fix = BDRV_FIX_LEAKS | BDRV_FIX_ERRORS; } else { - error_exit(argv[0], "Unknown option value for -r " - "(expecting 'leaks' or 'all'): %s", optarg); + error_exit(argv[0], + "--repair (-r) expects 'leaks' or 'all', not '%s'", + optarg); } break; case 'U': From a79d282fc9e1dfa887b13e079279277ea257162a Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:51 +0300 Subject: [PATCH 2467/2760] qemu-img: commit: refresh options/--help Add missing long options and --help output, reorder options for consistency. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-10-mjt@tls.msk.ru> [kwolf: Fixed up qemu-iotests] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 68 +++++++++++++++++++++++++++----------- tests/qemu-iotests/153 | 2 +- tests/qemu-iotests/153.out | 12 +++---- 3 files changed, 55 insertions(+), 27 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index ec6802b03b..9d2eba3b36 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1047,38 +1047,73 @@ static int img_commit(const img_cmd_t *ccmd, int argc, char **argv) for(;;) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, - {"object", required_argument, 0, OPTION_OBJECT}, + {"format", required_argument, 0, 'f'}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {"cache", required_argument, 0, 't'}, + {"drop", no_argument, 0, 'd'}, + {"base", required_argument, 0, 'b'}, + {"rate-limit", required_argument, 0, 'r'}, + {"progress", no_argument, 0, 'p'}, + {"quiet", no_argument, 0, 'q'}, + {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":f:ht:b:dpqr:", + c = getopt_long(argc, argv, "hf:t:db:r:pq", long_options, NULL); if (c == -1) { break; } switch(c) { - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); - break; case 'h': - help(); + cmd_help(ccmd, "[-f FMT | --image-opts] [-t CACHE_MODE] [-b BASE_IMG]\n" +" [-d] [-r RATE] [-q] [--object OBJDEF] FILE\n" +, +" -f, --format FMT\n" +" specify FILE image format explicitly (default: probing is used)\n" +" --image-opts\n" +" treat FILE as an option string (key=value,..), not a file name\n" +" (incompatible with -f|--format)\n" +" -t, --cache CACHE_MODE image cache mode (default: " BDRV_DEFAULT_CACHE ")\n" +" -d, --drop\n" +" skip emptying FILE on completion\n" +" -b, --base BASE_IMG\n" +" image in the backing chain to commit change to\n" +" (default: immediate backing file; implies --drop)\n" +" -r, --rate-limit RATE\n" +" I/O rate limit, in bytes per second\n" +" -p, --progress\n" +" display progress information\n" +" -q, --quiet\n" +" quiet mode (produce only error messages if any)\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" FILE\n" +" name of the image file, or an option string (key=value,..)\n" +" with --image-opts, to operate on\n" +); break; case 'f': fmt = optarg; break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; case 't': cache = optarg; break; + case 'd': + drop = true; + break; case 'b': base = optarg; /* -b implies -d */ drop = true; break; - case 'd': - drop = true; + case 'r': + rate_limit = cvtnum("rate limit", optarg); + if (rate_limit < 0) { + return 1; + } break; case 'p': progress = true; @@ -1086,18 +1121,11 @@ static int img_commit(const img_cmd_t *ccmd, int argc, char **argv) case 'q': quiet = true; break; - case 'r': - rate_limit = cvtnum("rate limit", optarg); - if (rate_limit < 0) { - return 1; - } - break; case OPTION_OBJECT: user_creatable_process_cmdline(optarg); break; - case OPTION_IMAGE_OPTS: - image_opts = true; - break; + default: + tryhelp(argv[0]); } } diff --git a/tests/qemu-iotests/153 b/tests/qemu-iotests/153 index 9bc3be8f75..1e02f6a6e3 100755 --- a/tests/qemu-iotests/153 +++ b/tests/qemu-iotests/153 @@ -63,7 +63,7 @@ _supported_proto file _run_cmd() { echo - (echo "$@"; "$@" 2>&1 1>/dev/null) | _filter_testdir + (echo "$@"; "$@" 2>&1 1>/dev/null) | _filter_testdir | _filter_qemu_img } _do_run_qemu() diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out index ff8e55864a..a8ffd01266 100644 --- a/tests/qemu-iotests/153.out +++ b/tests/qemu-iotests/153.out @@ -124,8 +124,8 @@ qemu-img: unrecognized option '-U' Try 'qemu-img --help' for more information _qemu_img_wrapper commit -U TEST_DIR/t.qcow2 -qemu-img: unrecognized option '-U' -Try 'qemu-img --help' for more information +qemu-img commit: invalid option -- 'U' +Try 'qemu-img commit --help' for more information _qemu_img_wrapper resize -U TEST_DIR/t.qcow2 32M qemu-img: unrecognized option '-U' @@ -248,8 +248,8 @@ qemu-img: unrecognized option '-U' Try 'qemu-img --help' for more information _qemu_img_wrapper commit -U TEST_DIR/t.qcow2 -qemu-img: unrecognized option '-U' -Try 'qemu-img --help' for more information +qemu-img commit: invalid option -- 'U' +Try 'qemu-img commit --help' for more information _qemu_img_wrapper resize -U TEST_DIR/t.qcow2 32M qemu-img: unrecognized option '-U' @@ -353,8 +353,8 @@ qemu-img: unrecognized option '-U' Try 'qemu-img --help' for more information _qemu_img_wrapper commit -U TEST_DIR/t.qcow2 -qemu-img: unrecognized option '-U' -Try 'qemu-img --help' for more information +qemu-img commit: invalid option -- 'U' +Try 'qemu-img commit --help' for more information _qemu_img_wrapper resize -U TEST_DIR/t.qcow2 32M qemu-img: unrecognized option '-U' From eedfb5c07e270eca9bfe1f8f16303b23447f8176 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:52 +0300 Subject: [PATCH 2468/2760] qemu-img: compare: use helper function for --object Use the same function to parse --object as used by all other qemu-img subcommands. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-11-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 9d2eba3b36..a7a07ed4bc 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1529,20 +1529,8 @@ static int img_compare(const img_cmd_t *ccmd, int argc, char **argv) force_share = true; break; case OPTION_OBJECT: - { - Error *local_err = NULL; - - if (!user_creatable_add_from_str(optarg, &local_err)) { - if (local_err) { - error_report_err(local_err); - exit(2); - } else { - /* Help was printed */ - exit(EXIT_SUCCESS); - } - } - break; - } + user_creatable_process_cmdline(optarg); + break; case OPTION_IMAGE_OPTS: image_opts = true; break; From 3c8296dfcfe0f43cd5b2d07703f795748200962b Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:53 +0300 Subject: [PATCH 2469/2760] qemu-img: compare: refresh options/--help Add long options, add help, reorder options for consistency. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-12-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 64 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index a7a07ed4bc..5697e45ae6 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -1487,25 +1487,51 @@ static int img_compare(const img_cmd_t *ccmd, int argc, char **argv) for (;;) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, - {"object", required_argument, 0, OPTION_OBJECT}, + {"a-format", required_argument, 0, 'f'}, + {"b-format", required_argument, 0, 'F'}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {"strict", no_argument, 0, 's'}, + {"cache", required_argument, 0, 'T'}, {"force-share", no_argument, 0, 'U'}, + {"progress", no_argument, 0, 'p'}, + {"quiet", no_argument, 0, 'q'}, + {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hf:F:T:pqsU", + c = getopt_long(argc, argv, "hf:F:sT:Upq", long_options, NULL); if (c == -1) { break; } switch (c) { - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); - break; case 'h': - help(); + cmd_help(ccmd, +"[[-f FMT] [-F FMT] | --image-opts] [-s] [-T CACHE]\n" +" [-U] [-p] [-q] [--object OBJDEF] FILE1 FILE2\n" +, +" -f, --a-format FMT\n" +" specify FILE1 image format explicitly (default: probing is used)\n" +" -F, --b-format FMT\n" +" specify FILE2 image format explicitly (default: probing is used)\n" +" --image-opts\n" +" treat FILE1 and FILE2 as option strings (key=value,..), not file names\n" +" (incompatible with -f|--a-format and -F|--b-format)\n" +" -s, --strict\n" +" strict mode, also check if sizes are equal\n" +" -T, --cache CACHE_MODE\n" +" images caching mode (default: " BDRV_DEFAULT_CACHE ")\n" +" -U, --force-share\n" +" open images in shared mode for concurrent access\n" +" -p, --progress\n" +" display progress information\n" +" -q, --quiet\n" +" quiet mode (produce only error messages if any)\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" FILE1, FILE2\n" +" names of the image files, or option strings (key=value,..)\n" +" with --image-opts, to compare\n" +); break; case 'f': fmt1 = optarg; @@ -1513,27 +1539,29 @@ static int img_compare(const img_cmd_t *ccmd, int argc, char **argv) case 'F': fmt2 = optarg; break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; + case 's': + strict = true; + break; case 'T': cache = optarg; break; + case 'U': + force_share = true; + break; case 'p': progress = true; break; case 'q': quiet = true; break; - case 's': - strict = true; - break; - case 'U': - force_share = true; - break; case OPTION_OBJECT: user_creatable_process_cmdline(optarg); break; - case OPTION_IMAGE_OPTS: - image_opts = true; - break; + default: + tryhelp(argv[0]); } } From c02276223ff312dd20d6535e243a94192342b170 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Tue, 15 Jul 2025 17:07:02 +0300 Subject: [PATCH 2470/2760] qemu-img: convert: refresh options/--help (short option change) Add missing long options and --help output. Reorder options for consistency. Use -b for --backing, and recognize -B for backwards compatibility. Unfortunately we can't use -B to specify backing format. Signed-off-by: Michael Tokarev Message-ID: <20250715140702.131321-1-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- docs/tools/qemu-img.rst | 2 +- qemu-img.c | 230 +++++++++++++++++++++++++++------------- 2 files changed, 159 insertions(+), 73 deletions(-) diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index a21e439082..23715811e2 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -419,7 +419,7 @@ Command description: 4 Error on reading data -.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps [--skip-broken-bitmaps]] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-B BACKING_FILE [-F BACKING_FMT]] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-r RATE_LIMIT] [-m NUM_COROUTINES] [-W] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME +.. option:: convert [--object OBJECTDEF] [--image-opts] [--target-image-opts] [--target-is-zero] [--bitmaps [--skip-broken-bitmaps]] [-U] [-C] [-c] [-p] [-q] [-n] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-O OUTPUT_FMT] [-b BACKING_FILE [-F BACKING_FMT]] [-o OPTIONS] [-l SNAPSHOT_PARAM] [-S SPARSE_SIZE] [-r RATE_LIMIT] [-m NUM_COROUTINES] [-W] FILENAME [FILENAME2 [...]] OUTPUT_FILENAME Convert the disk image *FILENAME* or a snapshot *SNAPSHOT_PARAM* to disk image *OUTPUT_FILENAME* using format *OUTPUT_FMT*. It can diff --git a/qemu-img.c b/qemu-img.c index 5697e45ae6..950f319a97 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -2390,53 +2390,122 @@ static int img_convert(const img_cmd_t *ccmd, int argc, char **argv) for(;;) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, - {"object", required_argument, 0, OPTION_OBJECT}, + {"source-format", required_argument, 0, 'f'}, + /* + * XXX: historic --image-opts acts on source file only, + * it seems better to have it affect both source and target, + * and have separate --source-image-opts for source, + * but this might break existing setups. + */ {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, - {"force-share", no_argument, 0, 'U'}, - {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, - {"salvage", no_argument, 0, OPTION_SALVAGE}, - {"target-is-zero", no_argument, 0, OPTION_TARGET_IS_ZERO}, + {"source-cache", required_argument, 0, 'T'}, + {"snapshot", required_argument, 0, 'l'}, {"bitmaps", no_argument, 0, OPTION_BITMAPS}, {"skip-broken-bitmaps", no_argument, 0, OPTION_SKIP_BROKEN}, + {"salvage", no_argument, 0, OPTION_SALVAGE}, + {"target-format", required_argument, 0, 'O'}, + {"target-image-opts", no_argument, 0, OPTION_TARGET_IMAGE_OPTS}, + {"target-format-options", required_argument, 0, 'o'}, + {"target-cache", required_argument, 0, 't'}, + {"backing", required_argument, 0, 'b'}, + {"backing-format", required_argument, 0, 'F'}, + {"sparse-size", required_argument, 0, 'S'}, + {"no-create", no_argument, 0, 'n'}, + {"target-is-zero", no_argument, 0, OPTION_TARGET_IS_ZERO}, + {"force-share", no_argument, 0, 'U'}, + {"rate-limit", required_argument, 0, 'r'}, + {"parallel", required_argument, 0, 'm'}, + {"oob-writes", no_argument, 0, 'W'}, + {"copy-range-offloading", no_argument, 0, 'C'}, + {"progress", no_argument, 0, 'p'}, + {"quiet", no_argument, 0, 'q'}, + {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hf:O:B:CcF:o:l:S:pt:T:qnm:WUr:", + c = getopt_long(argc, argv, "hf:O:b:B:CcF:o:l:S:pt:T:nm:WUr:q", long_options, NULL); if (c == -1) { break; } - switch(c) { - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); - break; + switch (c) { case 'h': - help(); + cmd_help(ccmd, "[-f SRC_FMT | --image-opts] [-T SRC_CACHE]\n" +" [-l SNAPSHOT] [--bitmaps [--skip-broken-bitmaps]] [--salvage]\n" +" [-O TGT_FMT | --target-image-opts] [-o TGT_FMT_OPTS] [-t TGT_CACHE]\n" +" [-b BACKING_FILE [-F BACKING_FMT]] [-S SPARSE_SIZE]\n" +" [-n] [--target-is-zero] [-c]\n" +" [-U] [-r RATE] [-m NUM_PARALLEL] [-W] [-C] [-p] [-q] [--object OBJDEF]\n" +" SRC_FILE [SRC_FILE2...] TGT_FILE\n" +, +" -f, --source-format SRC_FMT\n" +" specify format of all SRC_FILEs explicitly (default: probing is used)\n" +" --image-opts\n" +" treat each SRC_FILE as an option string (key=value,...), not a file name\n" +" (incompatible with -f|--source-format)\n" +" -T, --source-cache SRC_CACHE\n" +" source image(s) cache mode (" BDRV_DEFAULT_CACHE ")\n" +" -l, --snapshot SNAPSHOT\n" +" specify source snapshot\n" +" --bitmaps\n" +" also copy any persistent bitmaps present in source\n" +" --skip-broken-bitmaps\n" +" skip (do not error out) any broken bitmaps\n" +" --salvage\n" +" ignore errors on input (convert unreadable areas to zeros)\n" +" -O, --target-format TGT_FMT\n" +" specify TGT_FILE image format (default: raw)\n" +" --target-image-opts\n" +" treat TGT_FILE as an option string (key=value,...), not a file name\n" +" (incompatible with -O|--target-format)\n" +" -o, --target-format-options TGT_FMT_OPTS\n" +" TGT_FMT-specific options\n" +" -t, --target-cache TGT_CACHE\n" +" cache mode when opening output image (default: unsafe)\n" +" -b, --backing BACKING_FILE (was -B in <= 10.0)\n" +" create target image to be a CoW on top of BACKING_FILE\n" +" -F, --backing-format BACKING_FMT\n" /* -B used for -b in <=10.0 */ +" specify BACKING_FILE image format explicitly (default: probing is used)\n" +" -S, --sparse-size SPARSE_SIZE[bkKMGTPE]\n" +" specify number of consecutive zero bytes to treat as a gap on output\n" +" (rounded down to nearest 512 bytes), with optional multiplier suffix\n" +" -n, --no-create\n" +" omit target volume creation (e.g. on rbd)\n" +" --target-is-zero\n" +" indicates that the target volume is pre-zeroed\n" +" -c, --compress\n" +" create compressed output image (qcow and qcow2 formats only)\n" +" -U, --force-share\n" +" open images in shared mode for concurrent access\n" +" -r, --rate-limit RATE\n" +" I/O rate limit, in bytes per second\n" +" -m, --parallel NUM_PARALLEL\n" +" specify parallelism (default: 8)\n" +" -C, --copy-range-offloading\n" +" try to use copy offloading\n" +" -W, --oob-writes\n" +" enable out-of-order writes to improve performance\n" +" -p, --progress\n" +" display progress information\n" +" -q, --quiet\n" +" quiet mode (produce only error messages if any)\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" SRC_FILE...\n" +" one or more source image file names,\n" +" or option strings (key=value,..) with --source-image-opts\n" +" TGT_FILE\n" +" target (output) image file name,\n" +" or option string (key=value,..) with --target-image-opts\n" +); break; case 'f': fmt = optarg; break; - case 'O': - out_fmt = optarg; - break; - case 'B': - out_baseimg = optarg; - break; - case 'C': - s.copy_range = true; - break; - case 'c': - s.compressed = true; - break; - case 'F': - backing_fmt = optarg; + case OPTION_IMAGE_OPTS: + image_opts = true; break; - case 'o': - if (accumulate_options(&options, optarg) < 0) { - goto fail_getopt; - } + case 'T': + src_cache = optarg; break; case 'l': if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { @@ -2451,6 +2520,36 @@ static int img_convert(const img_cmd_t *ccmd, int argc, char **argv) snapshot_name = optarg; } break; + case OPTION_BITMAPS: + bitmaps = true; + break; + case OPTION_SKIP_BROKEN: + skip_broken = true; + break; + case OPTION_SALVAGE: + s.salvage = true; + break; + case 'O': + out_fmt = optarg; + break; + case OPTION_TARGET_IMAGE_OPTS: + tgt_image_opts = true; + break; + case 'o': + if (accumulate_options(&options, optarg) < 0) { + goto fail_getopt; + } + break; + case 't': + cache = optarg; + break; + case 'B': /* <=10.0 */ + case 'b': + out_baseimg = optarg; + break; + case 'F': /* can't use -B as it used as -b in <=10.0 */ + backing_fmt = optarg; + break; case 'S': { int64_t sval; @@ -2471,20 +2570,28 @@ static int img_convert(const img_cmd_t *ccmd, int argc, char **argv) explict_min_sparse = true; break; } - case 'p': - progress = true; + case 'n': + skip_create = true; break; - case 't': - cache = optarg; + case OPTION_TARGET_IS_ZERO: + /* + * The user asserting that the target is blank has the + * same effect as the target driver supporting zero + * initialisation. + */ + s.has_zero_init = true; break; - case 'T': - src_cache = optarg; + case 'c': + s.compressed = true; break; - case 'q': - s.quiet = true; + case 'U': + force_share = true; break; - case 'n': - skip_create = true; + case 'r': + rate_limit = cvtnum("rate limit", optarg); + if (rate_limit < 0) { + goto fail_getopt; + } break; case 'm': if (qemu_strtol(optarg, NULL, 0, &s.num_coroutines) || @@ -2497,41 +2604,20 @@ static int img_convert(const img_cmd_t *ccmd, int argc, char **argv) case 'W': s.wr_in_order = false; break; - case 'U': - force_share = true; + case 'C': + s.copy_range = true; break; - case 'r': - rate_limit = cvtnum("rate limit", optarg); - if (rate_limit < 0) { - goto fail_getopt; - } + case 'p': + progress = true; + break; + case 'q': + s.quiet = true; break; case OPTION_OBJECT: user_creatable_process_cmdline(optarg); break; - case OPTION_IMAGE_OPTS: - image_opts = true; - break; - case OPTION_SALVAGE: - s.salvage = true; - break; - case OPTION_TARGET_IMAGE_OPTS: - tgt_image_opts = true; - break; - case OPTION_TARGET_IS_ZERO: - /* - * The user asserting that the target is blank has the - * same effect as the target driver supporting zero - * initialisation. - */ - s.has_zero_init = true; - break; - case OPTION_BITMAPS: - bitmaps = true; - break; - case OPTION_SKIP_BROKEN: - skip_broken = true; - break; + default: + tryhelp(argv[0]); } } From 403197d2f0edb28c8dff03ee7e7d478c99992a1c Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:55 +0300 Subject: [PATCH 2471/2760] qemu-img: info: refresh options/--help Add missing long options and --help output. Also add -b short option for --backing-chain, and remove now-unused OPTION_BACKING_CHAIN. Reorder options for consistency. While at it, remove unused option_index variable. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-14-mjt@tls.msk.ru> [kwolf: Fixed up help text formatting] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 51 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 950f319a97..530123bfdf 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3220,50 +3220,63 @@ static int img_info(const img_cmd_t *ccmd, int argc, char **argv) fmt = NULL; for(;;) { - int option_index = 0; static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"format", required_argument, 0, 'f'}, - {"output", required_argument, 0, OPTION_OUTPUT}, - {"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN}, - {"object", required_argument, 0, OPTION_OBJECT}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {"backing-chain", no_argument, 0, OPTION_BACKING_CHAIN}, {"force-share", no_argument, 0, 'U'}, + {"output", required_argument, 0, OPTION_OUTPUT}, + {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":f:hU", - long_options, &option_index); + c = getopt_long(argc, argv, "hf:U", long_options, NULL); if (c == -1) { break; } switch(c) { - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); - break; case 'h': - help(); + cmd_help(ccmd, "[-f FMT | --image-opts] [--backing-chain] [-U]\n" +" [--output human|json] [--object OBJDEF] FILE\n" +, +" -f, --format FMT\n" +" specify FILE image format explicitly (default: probing is used)\n" +" --image-opts\n" +" treat FILE as an option string (key=value,..), not a file name\n" +" (incompatible with -f|--format)\n" +" --backing-chain\n" +" display information about the backing chain for copy-on-write overlays\n" +" -U, --force-share\n" +" open image in shared mode for concurrent access\n" +" --output human|json\n" +" specify output format (default: human)\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" FILE\n" +" name of the image file, or option string (key=value,..)\n" +" with --image-opts, to operate on\n" +); break; case 'f': fmt = optarg; break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; + case OPTION_BACKING_CHAIN: + chain = true; + break; case 'U': force_share = true; break; case OPTION_OUTPUT: output_format = parse_output_format(argv[0], optarg); break; - case OPTION_BACKING_CHAIN: - chain = true; - break; case OPTION_OBJECT: user_creatable_process_cmdline(optarg); break; - case OPTION_IMAGE_OPTS: - image_opts = true; - break; + default: + tryhelp(argv[0]); } } if (optind != argc - 1) { From fed0efefc24144ffacbd3452819909e83e14f595 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Tue, 15 Jul 2025 17:09:03 +0300 Subject: [PATCH 2472/2760] qemu-img: map: refresh options/--help Add missing long options and --help output, reorder options for consistency. While at it, remove unused option_index variable. Signed-off-by: Michael Tokarev Message-ID: <20250715140903.131529-1-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 59 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 530123bfdf..a02ccad943 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3453,41 +3453,53 @@ static int img_map(const img_cmd_t *ccmd, int argc, char **argv) fmt = NULL; for (;;) { - int option_index = 0; static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, {"format", required_argument, 0, 'f'}, - {"output", required_argument, 0, OPTION_OUTPUT}, - {"object", required_argument, 0, OPTION_OBJECT}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, - {"force-share", no_argument, 0, 'U'}, {"start-offset", required_argument, 0, 's'}, {"max-length", required_argument, 0, 'l'}, + {"force-share", no_argument, 0, 'U'}, + {"output", required_argument, 0, OPTION_OUTPUT}, + {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":f:s:l:hU", - long_options, &option_index); + c = getopt_long(argc, argv, "hf:s:l:U", + long_options, NULL); if (c == -1) { break; } switch (c) { - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); - break; case 'h': - help(); + cmd_help(ccmd, "[-f FMT | --image-opts]\n" +" [--start-offset OFFSET] [--max-length LENGTH]\n" +" [--output human|json] [-U] [--object OBJDEF] FILE\n" +, +" -f, --format FMT\n" +" specify FILE image format explicitly (default: probing is used)\n" +" --image-opts\n" +" treat FILE as an option string (key=value,..), not a file name\n" +" (incompatible with -f|--format)\n" +" -s, --start-offset OFFSET\n" +" start at the given OFFSET in the image, not at the beginning\n" +" -l, --max-length LENGTH\n" +" process at most LENGTH bytes instead of up to the end of the image\n" +" --output human|json\n" +" specify output format name (default: human)\n" +" -U, --force-share\n" +" open image in shared mode for concurrent access\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" FILE\n" +" the image file name, or option string (key=value,..)\n" +" with --image-opts, to operate on\n" +); break; case 'f': fmt = optarg; break; - case 'U': - force_share = true; - break; - case OPTION_OUTPUT: - output_format = parse_output_format(argv[0], optarg); + case OPTION_IMAGE_OPTS: + image_opts = true; break; case 's': start_offset = cvtnum("start offset", optarg); @@ -3501,12 +3513,17 @@ static int img_map(const img_cmd_t *ccmd, int argc, char **argv) return 1; } break; + case OPTION_OUTPUT: + output_format = parse_output_format(argv[0], optarg); + break; + case 'U': + force_share = true; + break; case OPTION_OBJECT: user_creatable_process_cmdline(optarg); break; - case OPTION_IMAGE_OPTS: - image_opts = true; - break; + default: + tryhelp(argv[0]); } } if (optind != argc - 1) { From e19597bc552f2db08b5595682cc4de6ec259ade6 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:57 +0300 Subject: [PATCH 2473/2760] qemu-img: snapshot: allow specifying -f fmt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For consistency with other commands, and since it already accepts --image-opts, allow specifying -f fmt too. Signed-off-by: Michael Tokarev Reviewed-by: Daniel P. Berrangé Message-ID: <20250531171609.197078-16-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- docs/tools/qemu-img.rst | 2 +- qemu-img-cmds.hx | 4 ++-- qemu-img.c | 9 ++++++--- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index 23715811e2..935f28bbc5 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -663,7 +663,7 @@ Command description: bitmap support, or 0 if bitmaps are supported but there is nothing to copy. -.. option:: snapshot [--object OBJECTDEF] [--image-opts] [-U] [-q] [-l | -a SNAPSHOT | -c SNAPSHOT | -d SNAPSHOT] FILENAME +.. option:: snapshot [--object OBJECTDEF] [-f FMT | --image-opts] [-U] [-q] [-l | -a SNAPSHOT | -c SNAPSHOT | -d SNAPSHOT] FILENAME List, apply, create or delete snapshots in image *FILENAME*. diff --git a/qemu-img-cmds.hx b/qemu-img-cmds.hx index c9dd70a892..2c5a8a28f9 100644 --- a/qemu-img-cmds.hx +++ b/qemu-img-cmds.hx @@ -84,9 +84,9 @@ SRST ERST DEF("snapshot", img_snapshot, - "snapshot [--object objectdef] [--image-opts] [-U] [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename") + "snapshot [--object objectdef] [-f fmt | --image-opts] [-U] [-q] [-l | -a snapshot | -c snapshot | -d snapshot] filename") SRST -.. option:: snapshot [--object OBJECTDEF] [--image-opts] [-U] [-q] [-l | -a SNAPSHOT | -c SNAPSHOT | -d SNAPSHOT] FILENAME +.. option:: snapshot [--object OBJECTDEF] [-f FMT | --image-opts] [-U] [-q] [-l | -a SNAPSHOT | -c SNAPSHOT | -d SNAPSHOT] FILENAME ERST DEF("rebase", img_rebase, diff --git a/qemu-img.c b/qemu-img.c index a02ccad943..49c61c8b54 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3597,7 +3597,7 @@ static int img_snapshot(const img_cmd_t *ccmd, int argc, char **argv) BlockBackend *blk; BlockDriverState *bs; QEMUSnapshotInfo sn; - char *filename, *snapshot_name = NULL; + char *filename, *fmt = NULL, *snapshot_name = NULL; int c, ret = 0, bdrv_oflags; int action = 0; bool quiet = false; @@ -3616,7 +3616,7 @@ static int img_snapshot(const img_cmd_t *ccmd, int argc, char **argv) {"force-share", no_argument, 0, 'U'}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":la:c:d:hqU", + c = getopt_long(argc, argv, ":la:c:d:f:hqU", long_options, NULL); if (c == -1) { break; @@ -3631,6 +3631,9 @@ static int img_snapshot(const img_cmd_t *ccmd, int argc, char **argv) case 'h': help(); return 0; + case 'f': + fmt = optarg; + break; case 'l': if (action) { error_exit(argv[0], "Cannot mix '-l', '-a', '-c', '-d'"); @@ -3684,7 +3687,7 @@ static int img_snapshot(const img_cmd_t *ccmd, int argc, char **argv) filename = argv[optind++]; /* Open the image */ - blk = img_open(image_opts, filename, NULL, bdrv_oflags, false, quiet, + blk = img_open(image_opts, filename, fmt, bdrv_oflags, false, quiet, force_share); if (!blk) { return 1; From 0473674b6f4797985ac1023ed469c6766b77a21d Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:58 +0300 Subject: [PATCH 2474/2760] qemu-img: snapshot: make -l (list) the default, simplify option handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When no -l/-a/-c/-d specified, assume -l (list). Use the same values for SNAPSHOT_LIST/etc constants as the option chars (lacd), this makes it possible to simplify option handling a lot, combining cases for 4 options into one. Also remove bdrv_oflags handling (only list can use RO mode). Signed-off-by: Michael Tokarev Reviewed-by: Daniel P. Berrangé Message-ID: <20250531171609.197078-17-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- docs/tools/qemu-img.rst | 2 +- qemu-img.c | 52 ++++++++++++++--------------------------- 2 files changed, 19 insertions(+), 35 deletions(-) diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index 935f28bbc5..a346988292 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -256,7 +256,7 @@ Parameters to snapshot subcommand: .. option:: -l - Lists all snapshots in the given image + Lists all snapshots in the given image (default action) Command description: diff --git a/qemu-img.c b/qemu-img.c index 49c61c8b54..f117efe687 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3587,10 +3587,11 @@ static int img_map(const img_cmd_t *ccmd, int argc, char **argv) return ret < 0; } -#define SNAPSHOT_LIST 1 -#define SNAPSHOT_CREATE 2 -#define SNAPSHOT_APPLY 3 -#define SNAPSHOT_DELETE 4 +/* the same as options */ +#define SNAPSHOT_LIST 'l' +#define SNAPSHOT_CREATE 'c' +#define SNAPSHOT_APPLY 'a' +#define SNAPSHOT_DELETE 'd' static int img_snapshot(const img_cmd_t *ccmd, int argc, char **argv) { @@ -3598,7 +3599,7 @@ static int img_snapshot(const img_cmd_t *ccmd, int argc, char **argv) BlockDriverState *bs; QEMUSnapshotInfo sn; char *filename, *fmt = NULL, *snapshot_name = NULL; - int c, ret = 0, bdrv_oflags; + int c, ret = 0; int action = 0; bool quiet = false; Error *err = NULL; @@ -3606,7 +3607,6 @@ static int img_snapshot(const img_cmd_t *ccmd, int argc, char **argv) bool force_share = false; int64_t rt; - bdrv_oflags = BDRV_O_RDWR; /* Parse commandline parameters */ for(;;) { static const struct option long_options[] = { @@ -3634,36 +3634,15 @@ static int img_snapshot(const img_cmd_t *ccmd, int argc, char **argv) case 'f': fmt = optarg; break; - case 'l': - if (action) { - error_exit(argv[0], "Cannot mix '-l', '-a', '-c', '-d'"); - return 0; - } - action = SNAPSHOT_LIST; - bdrv_oflags &= ~BDRV_O_RDWR; /* no need for RW */ - break; - case 'a': + case SNAPSHOT_LIST: + case SNAPSHOT_APPLY: + case SNAPSHOT_CREATE: + case SNAPSHOT_DELETE: if (action) { error_exit(argv[0], "Cannot mix '-l', '-a', '-c', '-d'"); return 0; } - action = SNAPSHOT_APPLY; - snapshot_name = optarg; - break; - case 'c': - if (action) { - error_exit(argv[0], "Cannot mix '-l', '-a', '-c', '-d'"); - return 0; - } - action = SNAPSHOT_CREATE; - snapshot_name = optarg; - break; - case 'd': - if (action) { - error_exit(argv[0], "Cannot mix '-l', '-a', '-c', '-d'"); - return 0; - } - action = SNAPSHOT_DELETE; + action = c; snapshot_name = optarg; break; case 'q': @@ -3686,9 +3665,14 @@ static int img_snapshot(const img_cmd_t *ccmd, int argc, char **argv) } filename = argv[optind++]; + if (!action) { + action = SNAPSHOT_LIST; + } + /* Open the image */ - blk = img_open(image_opts, filename, fmt, bdrv_oflags, false, quiet, - force_share); + blk = img_open(image_opts, filename, fmt, + action == SNAPSHOT_LIST ? 0 : BDRV_O_RDWR, + false, quiet, force_share); if (!blk) { return 1; } From 9fd7e6658cd42cc09905f0b950cd6e9acfc81354 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:15:59 +0300 Subject: [PATCH 2475/2760] qemu-img: snapshot: refresh options/--help Add missing long options and --help output, reorder options for consistency. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-18-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 60 +++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index f117efe687..cc26ae9100 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3611,29 +3611,58 @@ static int img_snapshot(const img_cmd_t *ccmd, int argc, char **argv) for(;;) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, - {"object", required_argument, 0, OPTION_OBJECT}, + {"format", required_argument, 0, 'f'}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {"list", no_argument, 0, SNAPSHOT_LIST}, + {"apply", required_argument, 0, SNAPSHOT_APPLY}, + {"create", required_argument, 0, SNAPSHOT_CREATE}, + {"delete", required_argument, 0, SNAPSHOT_DELETE}, {"force-share", no_argument, 0, 'U'}, + {"quiet", no_argument, 0, 'q'}, + {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":la:c:d:f:hqU", + c = getopt_long(argc, argv, "hf:la:c:d:Uq", long_options, NULL); if (c == -1) { break; } switch(c) { - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); - break; case 'h': - help(); - return 0; + cmd_help(ccmd, "[-f FMT | --image-opts] [-l | -a|-c|-d SNAPSHOT]\n" +" [-U] [-q] [--object OBJDEF] FILE\n" +, +" -f, --format FMT\n" +" specify FILE format explicitly (default: probing is used)\n" +" --image-opts\n" +" treat FILE as an option string (key=value,..), not a file name\n" +" (incompatible with -f|--format)\n" +" -l, --list\n" +" list snapshots in FILE (default action if no -l|-c|-a|-d is given)\n" +" -c, --create SNAPSHOT\n" +" create named snapshot\n" +" -a, --apply SNAPSHOT\n" +" apply named snapshot to the base\n" +" -d, --delete SNAPSHOT\n" +" delete named snapshot\n" +" (only one of -l|-c|-a|-d can be specified)\n" +" -U, --force-share\n" +" open image in shared mode for concurrent access\n" +" -q, --quiet\n" +" quiet mode (produce only error messages if any)\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" FILE\n" +" name of the image file, or option string (key=value,..)\n" +" with --image-opts) to operate on\n" +); + break; case 'f': fmt = optarg; break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; case SNAPSHOT_LIST: case SNAPSHOT_APPLY: case SNAPSHOT_CREATE: @@ -3645,18 +3674,17 @@ static int img_snapshot(const img_cmd_t *ccmd, int argc, char **argv) action = c; snapshot_name = optarg; break; - case 'q': - quiet = true; - break; case 'U': force_share = true; break; + case 'q': + quiet = true; + break; case OPTION_OBJECT: user_creatable_process_cmdline(optarg); break; - case OPTION_IMAGE_OPTS: - image_opts = true; - break; + default: + tryhelp(argv[0]); } } From c1563952521d4d5947b4279efe8dcc432791287e Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:16:00 +0300 Subject: [PATCH 2476/2760] qemu-img: rebase: refresh options/--help (short option change) Add missing long options and --help output, reorder options for consistency. Use -B for --backing-format, keep -F for backwards compatibility. Options added: --format, --cache - for the image in question --backing, --backing-format, --backing-cache, --backing-unsafe - for the new backing file (was eg CACHE vs SRC_CACHE, which is unclear). Probably should rename local variables. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-19-mjt@tls.msk.ru> [kwolf: Removed command description from the argument list] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- docs/tools/qemu-img.rst | 2 +- qemu-img.c | 87 +++++++++++++++++++++++++++++------------ 2 files changed, 63 insertions(+), 26 deletions(-) diff --git a/docs/tools/qemu-img.rst b/docs/tools/qemu-img.rst index a346988292..5e7b85079d 100644 --- a/docs/tools/qemu-img.rst +++ b/docs/tools/qemu-img.rst @@ -667,7 +667,7 @@ Command description: List, apply, create or delete snapshots in image *FILENAME*. -.. option:: rebase [--object OBJECTDEF] [--image-opts] [-U] [-q] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-p] [-u] [-c] -b BACKING_FILE [-F BACKING_FMT] FILENAME +.. option:: rebase [--object OBJECTDEF] [--image-opts] [-U] [-q] [-f FMT] [-t CACHE] [-T SRC_CACHE] [-p] [-u] [-c] -b BACKING_FILE [-B BACKING_FMT] FILENAME Changes the backing file of an image. Only the formats ``qcow2`` and ``qed`` support changing the backing file. diff --git a/qemu-img.c b/qemu-img.c index cc26ae9100..a02d271af1 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -3798,45 +3798,89 @@ static int img_rebase(const img_cmd_t *ccmd, int argc, char **argv) for(;;) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, - {"object", required_argument, 0, OPTION_OBJECT}, + {"format", required_argument, 0, 'f'}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, - {"force-share", no_argument, 0, 'U'}, + {"cache", required_argument, 0, 't'}, {"compress", no_argument, 0, 'c'}, + {"backing", required_argument, 0, 'b'}, + {"backing-format", required_argument, 0, 'B'}, + {"backing-cache", required_argument, 0, 'T'}, + {"backing-unsafe", no_argument, 0, 'u'}, + {"force-share", no_argument, 0, 'U'}, + {"progress", no_argument, 0, 'p'}, + {"quiet", no_argument, 0, 'q'}, + {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hf:F:b:upt:T:qUc", + c = getopt_long(argc, argv, "hf:t:cb:F:B:T:uUpq", long_options, NULL); if (c == -1) { break; } - switch(c) { - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); - break; + switch (c) { case 'h': - help(); + cmd_help(ccmd, "[-f FMT | --image-opts] [-t CACHE]\n" +" [-b BACKING_FILE [-B BACKING_FMT] [-T BACKING_CACHE]] [-u]\n" +" [-c] [-U] [-p] [-q] [--object OBJDEF] FILE\n" +, +" -f, --format FMT\n" +" specify FILE format explicitly (default: probing is used)\n" +" --image-opts\n" +" treat FILE as an option string (key=value,..), not a file name\n" +" (incompatible with -f|--format)\n" +" -t, --cache CACHE\n" +" cache mode for FILE (default: " BDRV_DEFAULT_CACHE ")\n" +" -b, --backing BACKING_FILE|\"\"\n" +" rebase onto this file (specify empty name for no backing file)\n" +" -B, --backing-format BACKING_FMT (was -F in <=10.0)\n" +" specify format for BACKING_FILE explicitly (default: probing is used)\n" +" -T, --backing-cache CACHE\n" +" BACKING_FILE cache mode (default: " BDRV_DEFAULT_CACHE ")\n" +" -u, --backing-unsafe\n" +" do not fail if BACKING_FILE can not be read\n" +" -c, --compress\n" +" compress image (when image supports this)\n" +" -U, --force-share\n" +" open image in shared mode for concurrent access\n" +" -p, --progress\n" +" display progress information\n" +" -q, --quiet\n" +" quiet mode (produce only error messages if any)\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" FILE\n" +" name of the image file, or option string (key=value,..)\n" +" with --image-opts, to operate on\n" +); return 0; case 'f': fmt = optarg; break; - case 'F': - out_basefmt = optarg; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; + case 't': + cache = optarg; break; case 'b': out_baseimg = optarg; break; + case 'F': /* <=10.0 */ + case 'B': + out_basefmt = optarg; + break; case 'u': unsafe = 1; break; + case 'c': + compress = true; + break; + case 'U': + force_share = true; + break; case 'p': progress = 1; break; - case 't': - cache = optarg; - break; case 'T': src_cache = optarg; break; @@ -3846,15 +3890,8 @@ static int img_rebase(const img_cmd_t *ccmd, int argc, char **argv) case OPTION_OBJECT: user_creatable_process_cmdline(optarg); break; - case OPTION_IMAGE_OPTS: - image_opts = true; - break; - case 'U': - force_share = true; - break; - case 'c': - compress = true; - break; + default: + tryhelp(argv[0]); } } From 8737b342e236c8cf5e4ae1f5cc0f519a23a6dd2b Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:16:01 +0300 Subject: [PATCH 2477/2760] qemu-img: resize: do not always eat last argument 'qemu-img resize --help' does not work, since it wants more arguments. Also -size is only recognized as a very last argument, but it is common for tools to handle other options after positional arguments too. Tell getopt_long() to return non-options together with options, and process filename and size in the loop, and check if there's an argument right after filename which looks like -N (number), and treat it as size (decrement). This way we can handle --help, and we can also have options after filename and size, and `--' will be handled fine too. The only case which is not handled right is when there's an option between filename and size, and size is given as decrement, - in this case -size will be treated as option, not as size. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-20-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index a02d271af1..36ef998a34 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -4302,7 +4302,7 @@ static int img_resize(const img_cmd_t *ccmd, int argc, char **argv) { Error *err = NULL; int c, ret, relative; - const char *filename, *fmt, *size; + const char *filename = NULL, *fmt = NULL, *size = NULL; int64_t n, total_size, current_size; bool quiet = false; BlockBackend *blk = NULL; @@ -4325,17 +4325,7 @@ static int img_resize(const img_cmd_t *ccmd, int argc, char **argv) bool image_opts = false; bool shrink = false; - /* Remove size from argv manually so that negative numbers are not treated - * as options by getopt. */ - if (argc < 3) { - error_exit(argv[0], "Not enough arguments"); - return 1; - } - - size = argv[--argc]; - /* Parse getopt arguments */ - fmt = NULL; for(;;) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, @@ -4345,7 +4335,7 @@ static int img_resize(const img_cmd_t *ccmd, int argc, char **argv) {"shrink", no_argument, 0, OPTION_SHRINK}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":f:hq", + c = getopt_long(argc, argv, "-:f:hq", long_options, NULL); if (c == -1) { break; @@ -4383,12 +4373,35 @@ static int img_resize(const img_cmd_t *ccmd, int argc, char **argv) case OPTION_SHRINK: shrink = true; break; + case 1: /* a non-optional argument */ + if (!filename) { + filename = optarg; + /* see if we have -size (number) next to filename */ + if (optind < argc) { + size = argv[optind]; + if (size[0] == '-' && size[1] >= '0' && size[1] <= '9') { + ++optind; + } else { + size = NULL; + } + } + } else if (!size) { + size = optarg; + } else { + error_exit(argv[0], "Extra argument(s) in command line"); + } + break; } } - if (optind != argc - 1) { + if (!filename && optind < argc) { + filename = argv[optind++]; + } + if (!size && optind < argc) { + size = argv[optind++]; + } + if (!filename || !size || optind < argc) { error_exit(argv[0], "Expecting image file name and size"); } - filename = argv[optind++]; /* Choose grow, shrink, or absolute resize mode */ switch (size[0]) { From a8ce9adfe1b30999b2f51cec61d44e525365077d Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:16:02 +0300 Subject: [PATCH 2478/2760] qemu-img: resize: refresh options/--help Add missing long options and --help output, reorder options for consistency. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-21-mjt@tls.msk.ru> [kwolf: Fixed up qemu-iotests] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 52 ++++++++++++++++++++++++++------------ tests/qemu-iotests/153.out | 12 ++++----- 2 files changed, 42 insertions(+), 22 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 36ef998a34..c16de265e0 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -4329,36 +4329,48 @@ static int img_resize(const img_cmd_t *ccmd, int argc, char **argv) for(;;) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, - {"object", required_argument, 0, OPTION_OBJECT}, + {"format", required_argument, 0, 'f'}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {"preallocation", required_argument, 0, OPTION_PREALLOCATION}, {"shrink", no_argument, 0, OPTION_SHRINK}, + {"quiet", no_argument, 0, 'q'}, + {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, "-:f:hq", + c = getopt_long(argc, argv, "-hf:q", long_options, NULL); if (c == -1) { break; } switch(c) { - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); - break; case 'h': - help(); - break; + cmd_help(ccmd, "[-f FMT | --image-opts] [--preallocation PREALLOC] [--shrink]\n" +" [-q] [--object OBJDEF] FILE [+-]SIZE[bkKMGTPE]\n" +, +" -f, --format FMT\n" +" specify FILE format explicitly (default: probing is used)\n" +" --image-opts\n" +" treat FILE as an option string (key=value,...), not a file name\n" +" (incompatible with -f|--format)\n" +" --shrink\n" +" allow operation when the new size is smaller than the original\n" +" --preallocation PREALLOC\n" +" specify FMT-specific preallocation type for the new areas\n" +" -q, --quiet\n" +" quiet mode (produce only error messages if any)\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" FILE\n" +" name of the image file, or option string (key=value,..)\n" +" with --image-opts, to operate on\n" +" [+-]SIZE[bkKMGTPE]\n" +" new image size or amount by which to shrink (-)/grow (+),\n" +" with optional multiplier suffix (powers of 1024, default is bytes)\n" +); + return 0; case 'f': fmt = optarg; break; - case 'q': - quiet = true; - break; - case OPTION_OBJECT: - user_creatable_process_cmdline(optarg); - break; case OPTION_IMAGE_OPTS: image_opts = true; break; @@ -4373,6 +4385,12 @@ static int img_resize(const img_cmd_t *ccmd, int argc, char **argv) case OPTION_SHRINK: shrink = true; break; + case 'q': + quiet = true; + break; + case OPTION_OBJECT: + user_creatable_process_cmdline(optarg); + break; case 1: /* a non-optional argument */ if (!filename) { filename = optarg; @@ -4391,6 +4409,8 @@ static int img_resize(const img_cmd_t *ccmd, int argc, char **argv) error_exit(argv[0], "Extra argument(s) in command line"); } break; + default: + tryhelp(argv[0]); } } if (!filename && optind < argc) { diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out index a8ffd01266..ac3e340e15 100644 --- a/tests/qemu-iotests/153.out +++ b/tests/qemu-iotests/153.out @@ -128,8 +128,8 @@ qemu-img commit: invalid option -- 'U' Try 'qemu-img commit --help' for more information _qemu_img_wrapper resize -U TEST_DIR/t.qcow2 32M -qemu-img: unrecognized option '-U' -Try 'qemu-img --help' for more information +qemu-img resize: invalid option -- 'U' +Try 'qemu-img resize --help' for more information _qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base -F qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock @@ -252,8 +252,8 @@ qemu-img commit: invalid option -- 'U' Try 'qemu-img commit --help' for more information _qemu_img_wrapper resize -U TEST_DIR/t.qcow2 32M -qemu-img: unrecognized option '-U' -Try 'qemu-img --help' for more information +qemu-img resize: invalid option -- 'U' +Try 'qemu-img resize --help' for more information _qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base -F qcow2 qemu-img: Could not open 'TEST_DIR/t.qcow2': Failed to get "write" lock @@ -357,8 +357,8 @@ qemu-img commit: invalid option -- 'U' Try 'qemu-img commit --help' for more information _qemu_img_wrapper resize -U TEST_DIR/t.qcow2 32M -qemu-img: unrecognized option '-U' -Try 'qemu-img --help' for more information +qemu-img resize: invalid option -- 'U' +Try 'qemu-img resize --help' for more information _qemu_img_wrapper rebase -U TEST_DIR/t.qcow2 -b TEST_DIR/t.qcow2.base -F qcow2 From 03b5de62d2af64c8349c62909bc7d11641491b24 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:16:03 +0300 Subject: [PATCH 2479/2760] qemu-img: amend: refresh options/--help Add missing long options and --help output, reorder options for consistency. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-22-mjt@tls.msk.ru> [kwolf: Fixed up qemu-iotests] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 54 +++++++++++++++++++++++++++----------- tests/qemu-iotests/153.out | 12 ++++----- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index c16de265e0..bb10569372 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -4564,26 +4564,48 @@ static int img_amend(const img_cmd_t *ccmd, int argc, char **argv) for (;;) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, - {"object", required_argument, 0, OPTION_OBJECT}, + {"options", required_argument, 0, 'o'}, + {"format", required_argument, 0, 'f'}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {"cache", required_argument, 0, 't'}, {"force", no_argument, 0, OPTION_FORCE}, + {"progress", no_argument, 0, 'p'}, + {"quiet", no_argument, 0, 'q'}, + {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":ho:f:t:pq", + c = getopt_long(argc, argv, "ho:f:t:pq", long_options, NULL); if (c == -1) { break; } switch (c) { - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); - break; case 'h': - help(); + cmd_help(ccmd, "-o FMT_OPTS [-f FMT | --image-opts]\n" +" [-t CACHE] [--force] [-p] [-q] [--object OBJDEF] FILE\n" +, +" -o, --options FMT_OPTS\n" +" FMT-specfic format options (required)\n" +" -f, --format FMT\n" +" specify FILE format explicitly (default: probing is used)\n" +" --image-opts\n" +" treat FILE as an option string (key=value,..), not a file name\n" +" (incompatible with -f|--format)\n" +" -t, --cache CACHE\n" +" cache mode for FILE (default: " BDRV_DEFAULT_CACHE ")\n" +" --force\n" +" allow certain unsafe operations\n" +" -p, --progres\n" +" show operation progress\n" +" -q, --quiet\n" +" quiet mode (produce only error messages if any)\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" FILE\n" +" name of the image file, or option string (key=value,..)\n" +" with --image-opts, to operate on\n" +); break; case 'o': if (accumulate_options(&options, optarg) < 0) { @@ -4594,9 +4616,15 @@ static int img_amend(const img_cmd_t *ccmd, int argc, char **argv) case 'f': fmt = optarg; break; + case OPTION_IMAGE_OPTS: + image_opts = true; + break; case 't': cache = optarg; break; + case OPTION_FORCE: + force = true; + break; case 'p': progress = true; break; @@ -4606,12 +4634,8 @@ static int img_amend(const img_cmd_t *ccmd, int argc, char **argv) case OPTION_OBJECT: user_creatable_process_cmdline(optarg); break; - case OPTION_IMAGE_OPTS: - image_opts = true; - break; - case OPTION_FORCE: - force = true; - break; + default: + tryhelp(argv[0]); } } diff --git a/tests/qemu-iotests/153.out b/tests/qemu-iotests/153.out index ac3e340e15..28e1a22952 100644 --- a/tests/qemu-iotests/153.out +++ b/tests/qemu-iotests/153.out @@ -120,8 +120,8 @@ _qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2 _qemu_img_wrapper map -U TEST_DIR/t.qcow2 _qemu_img_wrapper amend -o size=32M -U TEST_DIR/t.qcow2 -qemu-img: unrecognized option '-U' -Try 'qemu-img --help' for more information +qemu-img amend: invalid option -- 'U' +Try 'qemu-img amend --help' for more information _qemu_img_wrapper commit -U TEST_DIR/t.qcow2 qemu-img commit: invalid option -- 'U' @@ -244,8 +244,8 @@ _qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2 _qemu_img_wrapper map -U TEST_DIR/t.qcow2 _qemu_img_wrapper amend -o size=32M -U TEST_DIR/t.qcow2 -qemu-img: unrecognized option '-U' -Try 'qemu-img --help' for more information +qemu-img amend: invalid option -- 'U' +Try 'qemu-img amend --help' for more information _qemu_img_wrapper commit -U TEST_DIR/t.qcow2 qemu-img commit: invalid option -- 'U' @@ -349,8 +349,8 @@ _qemu_img_wrapper compare -U TEST_DIR/t.qcow2 TEST_DIR/t.qcow2 _qemu_img_wrapper map -U TEST_DIR/t.qcow2 _qemu_img_wrapper amend -o size=32M -U TEST_DIR/t.qcow2 -qemu-img: unrecognized option '-U' -Try 'qemu-img --help' for more information +qemu-img amend: invalid option -- 'U' +Try 'qemu-img amend --help' for more information _qemu_img_wrapper commit -U TEST_DIR/t.qcow2 qemu-img commit: invalid option -- 'U' From 609cbaf893f6bb4143dacde0be2b82a5134a7f5f Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:16:04 +0300 Subject: [PATCH 2480/2760] qemu-img: bench: refresh options/--help Add missing long options and --help output, reorder options for consistency. Add missing --object option. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-23-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 112 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 84 insertions(+), 28 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index bb10569372..17f7516a68 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -4864,28 +4864,93 @@ static int img_bench(const img_cmd_t *ccmd, int argc, char **argv) for (;;) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, - {"flush-interval", required_argument, 0, OPTION_FLUSH_INTERVAL}, + {"format", required_argument, 0, 'f'}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {"cache", required_argument, 0, 't'}, + {"count", required_argument, 0, 'c'}, + {"depth", required_argument, 0, 'd'}, + {"offset", required_argument, 0, 'o'}, + {"buffer-size", required_argument, 0, 's'}, + {"step-size", required_argument, 0, 'S'}, + {"write", no_argument, 0, 'w'}, {"pattern", required_argument, 0, OPTION_PATTERN}, + {"flush-interval", required_argument, 0, OPTION_FLUSH_INTERVAL}, {"no-drain", no_argument, 0, OPTION_NO_DRAIN}, + {"aio", required_argument, 0, 'i'}, + {"native", no_argument, 0, 'n'}, {"force-share", no_argument, 0, 'U'}, + {"quiet", no_argument, 0, 'q'}, + {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":hc:d:f:ni:o:qs:S:t:wU", long_options, - NULL); + c = getopt_long(argc, argv, "hf:t:c:d:o:s:S:wi:nUq", + long_options, NULL); if (c == -1) { break; } switch (c) { - case ':': - missing_argument(argv[optind - 1]); + case 'h': + cmd_help(ccmd, "[-f FMT | --image-opts] [-t CACHE]\n" +" [-c COUNT] [-d DEPTH] [-o OFFSET] [-s BUFFER_SIZE] [-S STEP_SIZE]\n" +" [-w [--pattern PATTERN] [--flush-interval INTERVAL [--no-drain]]]\n" +" [-i AIO] [-n] [-U] [-q] FILE\n" +, +" -f, --format FMT\n" +" specify FILE format explicitly\n" +" --image-opts\n" +" indicates that FILE is a complete image specification\n" +" instead of a file name (incompatible with --format)\n" +" -t, --cache CACHE\n" +" cache mode for FILE (default: " BDRV_DEFAULT_CACHE ")\n" +" -c, --count COUNT\n" +" number of I/O requests to perform\n" +" -d, --depth DEPTH\n" +" number of requests to perform in parallel\n" +" -o, --offset OFFSET\n" +" start first request at this OFFSET\n" +" -s, --buffer-size BUFFER_SIZE[bkKMGTPE]\n" +" size of each I/O request, with optional multiplier suffix\n" +" (powers of 1024, default is 4K)\n" +" -S, --step-size STEP_SIZE[bkKMGTPE]\n" +" each next request offset increment, with optional multiplier suffix\n" +" (powers of 1024, default is the same as BUFFER_SIZE)\n" +" -w, --write\n" +" perform write test (default is read)\n" +" --pattern PATTERN\n" +" write this pattern byte instead of zero\n" +" --flush-interval FLUSH_INTERVAL\n" +" issue flush after this number of requests\n" +" --no-drain\n" +" do not wait when flushing pending requests\n" +" -i, --aio AIO\n" +" async-io backend (threads, native, io_uring)\n" +" -n, --native\n" +" use native AIO backend if possible\n" +" -U, --force-share\n" +" open images in shared mode for concurrent access\n" +" -q, --quiet\n" +" quiet mode (produce only error messages if any)\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" FILE\n" +" name of the image file, or option string (key=value,..)\n" +" with --image-opts, to operate on\n" +); break; - case '?': - unrecognized_option(argv[optind - 1]); + case 'f': + fmt = optarg; break; - case 'h': - help(); + case OPTION_IMAGE_OPTS: + image_opts = true; + break; + case 't': + ret = bdrv_parse_cache_mode(optarg, &flags, &writethrough); + if (ret < 0) { + error_report("Invalid cache mode"); + ret = -1; + goto out; + } break; case 'c': { @@ -4909,9 +4974,6 @@ static int img_bench(const img_cmd_t *ccmd, int argc, char **argv) depth = res; break; } - case 'f': - fmt = optarg; - break; case 'n': flags |= BDRV_O_NATIVE_AIO; break; @@ -4932,9 +4994,6 @@ static int img_bench(const img_cmd_t *ccmd, int argc, char **argv) break; } break; - case 'q': - quiet = true; - break; case 's': { int64_t sval; @@ -4959,21 +5018,10 @@ static int img_bench(const img_cmd_t *ccmd, int argc, char **argv) step = sval; break; } - case 't': - ret = bdrv_parse_cache_mode(optarg, &flags, &writethrough); - if (ret < 0) { - error_report("Invalid cache mode"); - ret = -1; - goto out; - } - break; case 'w': flags |= BDRV_O_RDWR; is_write = true; break; - case 'U': - force_share = true; - break; case OPTION_PATTERN: { unsigned long res; @@ -4999,9 +5047,17 @@ static int img_bench(const img_cmd_t *ccmd, int argc, char **argv) case OPTION_NO_DRAIN: drain_on_flush = false; break; - case OPTION_IMAGE_OPTS: - image_opts = true; + case 'U': + force_share = true; break; + case 'q': + quiet = true; + break; + case OPTION_OBJECT: + user_creatable_process_cmdline(optarg); + break; + default: + tryhelp(argv[0]); } } From 0668a468d2c1ee6783f53fb4ef6abc6d8f9f26b8 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:16:05 +0300 Subject: [PATCH 2481/2760] qemu-img: bitmap: refresh options/--help Add missing long options and --help output, reorder options for consistency. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-24-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 80 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 56 insertions(+), 24 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 17f7516a68..d7f69c4bfb 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -5184,48 +5184,69 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv) for (;;) { static const struct option long_options[] = { {"help", no_argument, 0, 'h'}, - {"object", required_argument, 0, OPTION_OBJECT}, + {"format", required_argument, 0, 'f'}, {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, {"add", no_argument, 0, OPTION_ADD}, + {"granularity", required_argument, 0, 'g'}, {"remove", no_argument, 0, OPTION_REMOVE}, {"clear", no_argument, 0, OPTION_CLEAR}, {"enable", no_argument, 0, OPTION_ENABLE}, {"disable", no_argument, 0, OPTION_DISABLE}, {"merge", required_argument, 0, OPTION_MERGE}, - {"granularity", required_argument, 0, 'g'}, {"source-file", required_argument, 0, 'b'}, {"source-format", required_argument, 0, 'F'}, + {"object", required_argument, 0, OPTION_OBJECT}, {0, 0, 0, 0} }; - c = getopt_long(argc, argv, ":b:f:F:g:h", long_options, NULL); + c = getopt_long(argc, argv, "hf:g:b:F:", + long_options, NULL); if (c == -1) { break; } switch (c) { - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); - break; case 'h': - help(); - break; - case 'b': - src_filename = optarg; + cmd_help(ccmd, "[-f FMT | --image-opts]\n" +" ( --add [-g SIZE] | --remove | --clear | --enable | --disable |\n" +" --merge SOURCE [-b SRC_FILE [-F SRC_FMT]] )..\n" +" [--object OBJDEF] FILE BITMAP\n" +, +" -f, --format FMT\n" +" specify FILE format explicitly (default: probing is used)\n" +" --image-opts\n" +" treat FILE as an option string (key=value,..), not a file name\n" +" (incompatible with -f|--format)\n" +" --add\n" +" creates BITMAP in FILE, enables to record future edits\n" +" -g, --granularity SIZE[bKMGTPE]\n" +" sets non-default granularity for the bitmap being added,\n" +" with optional multiplier suffix (in powers of 1024)\n" +" --remove\n" +" removes BITMAP from FILE\n" +" --clear\n" +" clears BITMAP in FILE\n" +" --enable, --disable\n" +" starts and stops recording future edits to BITMAP in FILE\n" +" --merge SOURCE\n" +" merges contents of the SOURCE bitmap into BITMAP in FILE\n" +" -b, --source-file SRC_FILE\n" +" select alternative source file for --merge\n" +" -F, --source-format SRC_FMT\n" +" specify format for SRC_FILE explicitly\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" FILE\n" +" name of the image file, or option string (key=value,..)\n" +" with --image-opts, to operate on\n" +" BITMAP\n" +" name of the bitmap to add, remove, clear, enable, disable or merge to\n" +); break; case 'f': fmt = optarg; break; - case 'F': - src_fmt = optarg; - break; - case 'g': - granularity = cvtnum("granularity", optarg); - if (granularity < 0) { - return 1; - } + case OPTION_IMAGE_OPTS: + image_opts = true; break; case OPTION_ADD: act = g_new0(ImgBitmapAction, 1); @@ -5233,6 +5254,12 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv) QSIMPLEQ_INSERT_TAIL(&actions, act, next); add = true; break; + case 'g': + granularity = cvtnum("granularity", optarg); + if (granularity < 0) { + return 1; + } + break; case OPTION_REMOVE: act = g_new0(ImgBitmapAction, 1); act->act = BITMAP_REMOVE; @@ -5260,12 +5287,17 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv) QSIMPLEQ_INSERT_TAIL(&actions, act, next); merge = true; break; + case 'b': + src_filename = optarg; + break; + case 'F': + src_fmt = optarg; + break; case OPTION_OBJECT: user_creatable_process_cmdline(optarg); break; - case OPTION_IMAGE_OPTS: - image_opts = true; - break; + default: + tryhelp(argv[0]); } } From 892b0abf971f1b7ce8b363616ec42cc6248096cf Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:16:06 +0300 Subject: [PATCH 2482/2760] qemu-img: dd: refresh options/--help Add missing long options and --help output, reorder options for consistency. Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-25-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 50 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 14 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index d7f69c4bfb..35c3d9ad48 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -5538,31 +5538,54 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv) }; const struct option long_options[] = { { "help", no_argument, 0, 'h'}, - { "object", required_argument, 0, OPTION_OBJECT}, + { "format", required_argument, 0, 'f'}, { "image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + { "output-format", required_argument, 0, 'O'}, { "force-share", no_argument, 0, 'U'}, + { "object", required_argument, 0, OPTION_OBJECT}, { 0, 0, 0, 0 } }; - while ((c = getopt_long(argc, argv, ":hf:O:U", long_options, NULL))) { + while ((c = getopt_long(argc, argv, "hf:O:U", long_options, NULL))) { if (c == EOF) { break; } switch (c) { - case 'O': - out_fmt = optarg; + case 'h': + cmd_help(ccmd, "[-f FMT|--image-opts] [-O OUTPUT_FMT] [-U]\n" +" [--object OBJDEF] [bs=BLOCK_SIZE] [count=BLOCKS] if=INPUT of=OUTPUT\n" +, +" -f, --format FMT\n" +" specify format for INPUT explicitly (default: probing is used)\n" +" --image-opts\n" +" treat INPUT as an option string (key=value,..), not a file name\n" +" (incompatible with -f|--format)\n" +" -O, --output-format OUTPUT_FMT\n" +" format of the OUTPUT (default: raw)\n" +" -U, --force-share\n" +" open images in shared mode for concurrent access\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" bs=BLOCK_SIZE[bKMGTP]\n" +" size of the I/O block, with optional multiplier suffix (powers of 1024)\n" +" (default: 512)\n" +" count=COUNT\n" +" number of blocks to convert (default whole INPUT)\n" +" if=INPUT\n" +" name of the file, or option string (key=value,..)\n" +" with --image-opts, to use for input\n" +" of=OUTPUT\n" +" output file name to create (will be overridden if alrady exists)\n" +); break; case 'f': fmt = optarg; break; - case ':': - missing_argument(argv[optind - 1]); - break; - case '?': - unrecognized_option(argv[optind - 1]); + case OPTION_IMAGE_OPTS: + image_opts = true; break; - case 'h': - help(); + case 'O': + out_fmt = optarg; break; case 'U': force_share = true; @@ -5570,9 +5593,8 @@ static int img_dd(const img_cmd_t *ccmd, int argc, char **argv) case OPTION_OBJECT: user_creatable_process_cmdline(optarg); break; - case OPTION_IMAGE_OPTS: - image_opts = true; - break; + default: + tryhelp(argv[0]); } } From d7d0e936f9d665622badf3913de8925581d8fa8c Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:16:07 +0300 Subject: [PATCH 2483/2760] qemu-img: measure: refresh options/--help Add missing long options and --help output, reorder options for consistency. Also add -s short option for --size (and remove OPTION_SIZE). Signed-off-by: Michael Tokarev Message-ID: <20250531171609.197078-26-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 89 +++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 62 insertions(+), 27 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index 35c3d9ad48..a7d9d50250 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -72,7 +72,6 @@ enum { OPTION_FLUSH_INTERVAL = 261, OPTION_NO_DRAIN = 262, OPTION_TARGET_IMAGE_OPTS = 263, - OPTION_SIZE = 264, OPTION_PREALLOCATION = 265, OPTION_SHRINK = 266, OPTION_SALVAGE = 267, @@ -5786,15 +5785,6 @@ static void dump_json_block_measure_info(BlockMeasureInfo *info) static int img_measure(const img_cmd_t *ccmd, int argc, char **argv) { - static const struct option long_options[] = { - {"help", no_argument, 0, 'h'}, - {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, - {"object", required_argument, 0, OPTION_OBJECT}, - {"output", required_argument, 0, OPTION_OUTPUT}, - {"size", required_argument, 0, OPTION_SIZE}, - {"force-share", no_argument, 0, 'U'}, - {0, 0, 0, 0} - }; OutputFormat output_format = OFORMAT_HUMAN; BlockBackend *in_blk = NULL; BlockDriver *drv; @@ -5815,23 +5805,61 @@ static int img_measure(const img_cmd_t *ccmd, int argc, char **argv) int ret = 1; int c; - while ((c = getopt_long(argc, argv, "hf:O:o:l:U", + static const struct option long_options[] = { + {"help", no_argument, 0, 'h'}, + {"source-format", required_argument, 0, 'f'}, /* img_convert */ + {"format", required_argument, 0, 'f'}, + {"image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, + {"source-image-opts", no_argument, 0, OPTION_IMAGE_OPTS}, /* img_convert */ + {"snapshot", required_argument, 0, 'l'}, + {"target-format", required_argument, 0, 'O'}, + {"target-format-options", required_argument, 0, 'o'}, /* img_convert */ + {"options", required_argument, 0, 'o'}, + {"force-share", no_argument, 0, 'U'}, + {"output", required_argument, 0, OPTION_OUTPUT}, + {"object", required_argument, 0, OPTION_OBJECT}, + {"size", required_argument, 0, 's'}, + {0, 0, 0, 0} + }; + + while ((c = getopt_long(argc, argv, "hf:l:O:o:Us:", long_options, NULL)) != -1) { switch (c) { - case '?': case 'h': - help(); + cmd_help(ccmd, "[-f FMT|--image-opts] [-l SNAPSHOT]\n" +" [-O TARGET_FMT] [-o TARGET_FMT_OPTS] [--output human|json]\n" +" [--object OBJDEF] (--size SIZE | FILE)\n" +, +" -f, --format\n" +" specify format of FILE explicitly (default: probing is used)\n" +" --image-opts\n" +" indicates that FILE is a complete image specification\n" +" instead of a file name (incompatible with --format)\n" +" -l, --snapshot SNAPSHOT\n" +" use this snapshot in FILE as source\n" +" -O, --target-format TARGET_FMT\n" +" desired target/output image format (default: raw)\n" +" -o TARGET_FMT_OPTS\n" +" options specific to TARGET_FMT\n" +" --output human|json\n" +" output format (default: human)\n" +" -U, --force-share\n" +" open images in shared mode for concurrent access\n" +" --object OBJDEF\n" +" defines QEMU user-creatable object\n" +" -s, --size SIZE[bKMGTPE]\n" +" measure file size for given image size,\n" +" with optional multiplier suffix (powers of 1024)\n" +" FILE\n" +" measure file size required to convert from FILE (either a file name\n" +" or an option string (key=value,..) with --image-options)\n" +); break; case 'f': fmt = optarg; break; - case 'O': - out_fmt = optarg; - break; - case 'o': - if (accumulate_options(&options, optarg) < 0) { - goto out; - } + case OPTION_IMAGE_OPTS: + image_opts = true; break; case 'l': if (strstart(optarg, SNAPSHOT_OPT_BASE, NULL)) { @@ -5846,24 +5874,31 @@ static int img_measure(const img_cmd_t *ccmd, int argc, char **argv) snapshot_name = optarg; } break; - case 'U': - force_share = true; + case 'O': + out_fmt = optarg; break; - case OPTION_OBJECT: - user_creatable_process_cmdline(optarg); + case 'o': + if (accumulate_options(&options, optarg) < 0) { + goto out; + } break; - case OPTION_IMAGE_OPTS: - image_opts = true; + case 'U': + force_share = true; break; case OPTION_OUTPUT: output_format = parse_output_format(argv[0], optarg); break; - case OPTION_SIZE: + case OPTION_OBJECT: + user_creatable_process_cmdline(optarg); + break; + case 's': img_size = cvtnum("image size", optarg); if (img_size < 0) { goto out; } break; + default: + tryhelp(argv[0]); } } From bf5647ae24f3a539eb40003f3d3db95b9484b978 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:16:08 +0300 Subject: [PATCH 2484/2760] qemu-img: implement short --help, remove global help() function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit now once all individual subcommands has --help support, remove the large unreadable help() thing and replace it with small global --help, which refers to individual command --help for more info. While at it, also line-wrap list of formats after 75 chars. Since missing_argument() and unrecognized_option() are now unused, remove them. Signed-off-by: Michael Tokarev Reviewed-by: Daniel P. Berrangé Message-ID: <20250531171609.197078-27-mjt@tls.msk.ru> [kwolf: Fixed up style and formatting] Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 214 ++++++++++++++++++----------------------------------- 1 file changed, 73 insertions(+), 141 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index a7d9d50250..b34b1390bb 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -61,6 +61,7 @@ typedef struct img_cmd_t { const char *name; int (*handler)(const struct img_cmd_t *ccmd, int argc, char **argv); + const char *description; } img_cmd_t; enum { @@ -95,11 +96,6 @@ typedef enum OutputFormat { /* Default to cache=writeback as data integrity is not important for qemu-img */ #define BDRV_DEFAULT_CACHE "writeback" -static void format_print(void *opaque, const char *name) -{ - printf(" %s", name); -} - static G_NORETURN void tryhelp(const char *argv0) { @@ -119,18 +115,6 @@ void error_exit(const char *argv0, const char *fmt, ...) tryhelp(argv0); } -static G_NORETURN -void missing_argument(const char *option) -{ - error_exit("qemu-img", "missing argument for option '%s'", option); -} - -static G_NORETURN -void unrecognized_option(const char *option) -{ - error_exit("qemu-img", "unrecognized option '%s'", option); -} - /* * Print --help output for a command and exit. * @syntax and @description are multi-line with trailing EOL @@ -145,15 +129,14 @@ void cmd_help(const img_cmd_t *ccmd, { printf( "Usage:\n" -"\n" -" %s %s %s" +" %s %s %s\n" +"%s.\n" "\n" "Arguments:\n" " -h, --help\n" " print this help and exit\n" "%s\n", - "qemu-img", ccmd->name, - syntax, arguments); + "qemu-img", ccmd->name, syntax, ccmd->description, arguments); exit(EXIT_SUCCESS); } @@ -168,114 +151,6 @@ static OutputFormat parse_output_format(const char *argv0, const char *arg) } } -/* Please keep in synch with docs/tools/qemu-img.rst */ -static G_NORETURN -void help(void) -{ - const char *help_msg = - QEMU_IMG_VERSION - "usage: qemu-img [standard options] command [command options]\n" - "QEMU disk image utility\n" - "\n" - " '-h', '--help' display this help and exit\n" - " '-V', '--version' output version information and exit\n" - " '-T', '--trace' [[enable=]][,events=][,file=]\n" - " specify tracing options\n" - "\n" - "Command syntax:\n" -#define DEF(option, callback, arg_string) \ - " " arg_string "\n" -#include "qemu-img-cmds.h" -#undef DEF - "\n" - "Command parameters:\n" - " 'filename' is a disk image filename\n" - " 'objectdef' is a QEMU user creatable object definition. See the qemu(1)\n" - " manual page for a description of the object properties. The most common\n" - " object type is a 'secret', which is used to supply passwords and/or\n" - " encryption keys.\n" - " 'fmt' is the disk image format. It is guessed automatically in most cases\n" - " 'cache' is the cache mode used to write the output disk image, the valid\n" - " options are: 'none', 'writeback' (default, except for convert), 'writethrough',\n" - " 'directsync' and 'unsafe' (default for convert)\n" - " 'src_cache' is the cache mode used to read input disk images, the valid\n" - " options are the same as for the 'cache' option\n" - " 'size' is the disk image size in bytes. Optional suffixes\n" - " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M),\n" - " 'T' (terabyte, 1024G), 'P' (petabyte, 1024T) and 'E' (exabyte, 1024P) are\n" - " supported. 'b' is ignored.\n" - " 'output_filename' is the destination disk image filename\n" - " 'output_fmt' is the destination format\n" - " 'options' is a comma separated list of format specific options in a\n" - " name=value format. Use -o help for an overview of the options supported by\n" - " the used format\n" - " 'snapshot_param' is param used for internal snapshot, format\n" - " is 'snapshot.id=[ID],snapshot.name=[NAME]', or\n" - " '[ID_OR_NAME]'\n" - " '-c' indicates that target image must be compressed (qcow format only)\n" - " '-u' allows unsafe backing chains. For rebasing, it is assumed that old and\n" - " new backing file match exactly. The image doesn't need a working\n" - " backing file before rebasing in this case (useful for renaming the\n" - " backing file). For image creation, allow creating without attempting\n" - " to open the backing file.\n" - " '-h' with or without a command shows this help and lists the supported formats\n" - " '-p' show progress of command (only certain commands)\n" - " '-q' use Quiet mode - do not print any output (except errors)\n" - " '-S' indicates the consecutive number of bytes (defaults to 4k) that must\n" - " contain only zeros for qemu-img to create a sparse image during\n" - " conversion. If the number of bytes is 0, the source will not be scanned for\n" - " unallocated or zero sectors, and the destination image will always be\n" - " fully allocated\n" - " '--output' takes the format in which the output must be done (human or json)\n" - " '-n' skips the target volume creation (useful if the volume is created\n" - " prior to running qemu-img)\n" - "\n" - "Parameters to bitmap subcommand:\n" - " 'bitmap' is the name of the bitmap to manipulate, through one or more\n" - " actions from '--add', '--remove', '--clear', '--enable', '--disable',\n" - " or '--merge source'\n" - " '-g granularity' sets the granularity for '--add' actions\n" - " '-b source' and '-F src_fmt' tell '--merge' actions to find the source\n" - " bitmaps from an alternative file\n" - "\n" - "Parameters to check subcommand:\n" - " '-r' tries to repair any inconsistencies that are found during the check.\n" - " '-r leaks' repairs only cluster leaks, whereas '-r all' fixes all\n" - " kinds of errors, with a higher risk of choosing the wrong fix or\n" - " hiding corruption that has already occurred.\n" - "\n" - "Parameters to convert subcommand:\n" - " '--bitmaps' copies all top-level persistent bitmaps to destination\n" - " '-m' specifies how many coroutines work in parallel during the convert\n" - " process (defaults to 8)\n" - " '-W' allow to write to the target out of order rather than sequential\n" - "\n" - "Parameters to snapshot subcommand:\n" - " 'snapshot' is the name of the snapshot to create, apply or delete\n" - " '-a' applies a snapshot (revert disk to saved state)\n" - " '-c' creates a snapshot\n" - " '-d' deletes a snapshot\n" - " '-l' lists all snapshots in the given image\n" - "\n" - "Parameters to compare subcommand:\n" - " '-f' first image format\n" - " '-F' second image format\n" - " '-s' run in Strict mode - fail on different image size or sector allocation\n" - "\n" - "Parameters to dd subcommand:\n" - " 'bs=BYTES' read and write up to BYTES bytes at a time " - "(default: 512)\n" - " 'count=N' copy only N input blocks\n" - " 'if=FILE' read from FILE\n" - " 'of=FILE' write to FILE\n" - " 'skip=N' skip N bs-sized blocks at the start of input\n"; - - printf("%s\nSupported formats:", help_msg); - bdrv_iterate_format(format_print, NULL, false); - printf("\n\n" QEMU_HELP_BOTTOM "\n"); - exit(EXIT_SUCCESS); -} - /* * Is @list safe for accumulate_options()? * It is when multiple of them can be joined together separated by ','. @@ -5999,13 +5874,49 @@ static int img_measure(const img_cmd_t *ccmd, int argc, char **argv) } static const img_cmd_t img_cmds[] = { -#define DEF(option, callback, arg_string) \ - { option, callback }, -#include "qemu-img-cmds.h" -#undef DEF + { "amend", img_amend, + "Update format-specific options of the image" }, + { "bench", img_bench, + "Run a simple image benchmark" }, + { "bitmap", img_bitmap, + "Perform modifications of the persistent bitmap in the image" }, + { "check", img_check, + "Check basic image integrity" }, + { "commit", img_commit, + "Commit image to its backing file" }, + { "compare", img_compare, + "Check if two images have the same contents" }, + { "convert", img_convert, + "Copy one or more images to another with optional format conversion" }, + { "create", img_create, + "Create and format a new image file" }, + { "dd", img_dd, + "Copy input to output with optional format conversion" }, + { "info", img_info, + "Display information about the image" }, + { "map", img_map, + "Dump image metadata" }, + { "measure", img_measure, + "Calculate the file size required for a new image" }, + { "rebase", img_rebase, + "Change the backing file of the image" }, + { "resize", img_resize, + "Resize the image" }, + { "snapshot", img_snapshot, + "List or manipulate snapshots in the image" }, { NULL, NULL, }, }; +static void format_print(void *opaque, const char *name) +{ + int *np = opaque; + if (*np + strlen(name) > 75) { + printf("\n "); + *np = 1; + } + *np += printf(" %s", name); +} + int main(int argc, char **argv) { const img_cmd_t *cmd; @@ -6037,16 +5948,35 @@ int main(int argc, char **argv) qemu_add_opts(&qemu_source_opts); qemu_add_opts(&qemu_trace_opts); - while ((c = getopt_long(argc, argv, "+:hVT:", long_options, NULL)) != -1) { + while ((c = getopt_long(argc, argv, "+hVT:", long_options, NULL)) != -1) { switch (c) { - case ':': - missing_argument(argv[optind - 1]); - return 0; - case '?': - unrecognized_option(argv[optind - 1]); - return 0; case 'h': - help(); + printf( +QEMU_IMG_VERSION +"QEMU disk image utility. Usage:\n" +"\n" +" qemu-img [standard options] COMMAND [--help | command options]\n" +"\n" +"Standard options:\n" +" -h, --help\n" +" display this help and exit\n" +" -V, --version\n" +" display version info and exit\n" +" -T,--trace TRACE\n" +" specify tracing options:\n" +" [[enable=]][,events=][,file=]\n" +"\n" +"Recognized commands (run qemu-img COMMAND --help for command-specific help):\n\n"); + for (cmd = img_cmds; cmd->name != NULL; cmd++) { + printf(" %s - %s\n", cmd->name, cmd->description); + } + printf("\nSupported image formats:\n"); + c = 99; /* force a newline */ + bdrv_iterate_format(format_print, &c, false); + if (c) { + printf("\n"); + } + printf("\n" QEMU_HELP_BOTTOM "\n"); return 0; case 'V': printf(QEMU_IMG_VERSION); @@ -6054,6 +5984,8 @@ int main(int argc, char **argv) case 'T': trace_opt_parse(optarg); break; + default: + tryhelp(argv[0]); } } From d7bd47bf84707d21324ad079c5aa8cca16fd138f Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Sat, 31 May 2025 20:16:09 +0300 Subject: [PATCH 2485/2760] qemu-img: extend cvtnum() and use it in more places MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cvtnum() expects input string to specify some sort of size (optionally with KMG... suffix). However, there are a lot of other number conversions in there (using qemu_strtol &Co), also, not all conversions which use cvtnum, actually expects size, - like dd count=nn. Add bool is_size argument to cvtnum() to specify if it should treat the argument as a size or something else, - this changes conversion routine in use and error text. Use the new cvtnum() in more places (like where strtol were used), since it never return negative number in successful conversion. When it makes sense, also specify upper or lower bounds at the same time. This simplifies option processing in multiple places, removing the need of local temporary variables and longer error reporting code. While at it, fix errors, like depth in measure must be >= 1, while the previous code allowed it to be 0. In a few places, change unsigned variables (like of type size_t) to be signed instead, - to avoid the need of temporary conversion variable. All these variables are okay to be signed, we never assign <0 value to them except of the cases of conversion error, where we return immediately. While at it, remove allowed size suffixes from the error message as it makes no sense most of the time (should be in help instead). Signed-off-by: Michael Tokarev Reviewed-by: Daniel P. Berrangé Message-ID: <20250531171609.197078-28-mjt@tls.msk.ru> Reviewed-by: Kevin Wolf Signed-off-by: Kevin Wolf --- qemu-img.c | 111 +++++++++++++------------------------ tests/qemu-iotests/049.out | 9 +-- 2 files changed, 40 insertions(+), 80 deletions(-) diff --git a/qemu-img.c b/qemu-img.c index b34b1390bb..7a162fdc08 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -398,18 +398,16 @@ static int add_old_style_options(const char *fmt, QemuOpts *opts, return 0; } -static int64_t cvtnum_full(const char *name, const char *value, int64_t min, - int64_t max) +static int64_t cvtnum_full(const char *name, const char *value, + bool is_size, int64_t min, int64_t max) { int err; uint64_t res; - err = qemu_strtosz(value, NULL, &res); + err = is_size ? qemu_strtosz(value, NULL, &res) : + qemu_strtou64(value, NULL, 0, &res); if (err < 0 && err != -ERANGE) { - error_report("Invalid %s specified. You may use " - "k, M, G, T, P or E suffixes for", name); - error_report("kilobytes, megabytes, gigabytes, terabytes, " - "petabytes and exabytes."); + error_report("Invalid %s specified: '%s'", name, value); return err; } if (err == -ERANGE || res > max || res < min) { @@ -420,9 +418,9 @@ static int64_t cvtnum_full(const char *name, const char *value, int64_t min, return res; } -static int64_t cvtnum(const char *name, const char *value) +static int64_t cvtnum(const char *name, const char *value, bool is_size) { - return cvtnum_full(name, value, 0, INT64_MAX); + return cvtnum_full(name, value, is_size, 0, INT64_MAX); } static int img_create(const img_cmd_t *ccmd, int argc, char **argv) @@ -525,7 +523,7 @@ static int img_create(const img_cmd_t *ccmd, int argc, char **argv) /* Get image size, if specified */ if (optind < argc) { - img_size = cvtnum("image size", argv[optind++]); + img_size = cvtnum("image size", argv[optind++], true); if (img_size < 0) { goto fail; } @@ -984,7 +982,7 @@ static int img_commit(const img_cmd_t *ccmd, int argc, char **argv) drop = true; break; case 'r': - rate_limit = cvtnum("rate limit", optarg); + rate_limit = cvtnum("rate limit", optarg, true); if (rate_limit < 0) { return 1; } @@ -2428,7 +2426,7 @@ static int img_convert(const img_cmd_t *ccmd, int argc, char **argv) { int64_t sval; - sval = cvtnum("buffer size for sparse output", optarg); + sval = cvtnum("buffer size for sparse output", optarg, true); if (sval < 0) { goto fail_getopt; } else if (!QEMU_IS_ALIGNED(sval, BDRV_SECTOR_SIZE) || @@ -2462,16 +2460,15 @@ static int img_convert(const img_cmd_t *ccmd, int argc, char **argv) force_share = true; break; case 'r': - rate_limit = cvtnum("rate limit", optarg); + rate_limit = cvtnum("rate limit", optarg, true); if (rate_limit < 0) { goto fail_getopt; } break; case 'm': - if (qemu_strtol(optarg, NULL, 0, &s.num_coroutines) || - s.num_coroutines < 1 || s.num_coroutines > MAX_COROUTINES) { - error_report("Invalid number of coroutines. Allowed number of" - " coroutines is between 1 and %d", MAX_COROUTINES); + s.num_coroutines = cvtnum_full("number of coroutines", optarg, + false, 1, MAX_COROUTINES); + if (s.num_coroutines < 0) { goto fail_getopt; } break; @@ -3376,13 +3373,13 @@ static int img_map(const img_cmd_t *ccmd, int argc, char **argv) image_opts = true; break; case 's': - start_offset = cvtnum("start offset", optarg); + start_offset = cvtnum("start offset", optarg, true); if (start_offset < 0) { return 1; } break; case 'l': - max_length = cvtnum("max length", optarg); + max_length = cvtnum("max length", optarg, true); if (max_length < 0) { return 1; } @@ -4720,9 +4717,9 @@ static int img_bench(const img_cmd_t *ccmd, int argc, char **argv) int count = 75000; int depth = 64; int64_t offset = 0; - size_t bufsize = 4096; + ssize_t bufsize = 4096; int pattern = 0; - size_t step = 0; + ssize_t step = 0; int flush_interval = 0; bool drain_on_flush = true; int64_t image_size; @@ -4827,27 +4824,17 @@ static int img_bench(const img_cmd_t *ccmd, int argc, char **argv) } break; case 'c': - { - unsigned long res; - - if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) { - error_report("Invalid request count specified"); + count = cvtnum_full("request count", optarg, false, 1, INT_MAX); + if (count < 0) { return 1; } - count = res; break; - } case 'd': - { - unsigned long res; - - if (qemu_strtoul(optarg, NULL, 0, &res) <= 0 || res > INT_MAX) { - error_report("Invalid queue depth specified"); + depth = cvtnum_full("queue depth", optarg, false, 1, INT_MAX); + if (depth < 0) { return 1; } - depth = res; break; - } case 'n': flags |= BDRV_O_NATIVE_AIO; break; @@ -4860,64 +4847,40 @@ static int img_bench(const img_cmd_t *ccmd, int argc, char **argv) } break; case 'o': - { - offset = cvtnum("offset", optarg); + offset = cvtnum("offset", optarg, true); if (offset < 0) { return 1; } break; - } - break; case 's': - { - int64_t sval; - - sval = cvtnum_full("buffer size", optarg, 0, INT_MAX); - if (sval < 0) { + bufsize = cvtnum_full("buffer size", optarg, true, 1, INT_MAX); + if (bufsize < 0) { return 1; } - - bufsize = sval; break; - } case 'S': - { - int64_t sval; - - sval = cvtnum_full("step_size", optarg, 0, INT_MAX); - if (sval < 0) { + step = cvtnum_full("step size", optarg, true, 0, INT_MAX); + if (step < 0) { return 1; } - - step = sval; break; - } case 'w': flags |= BDRV_O_RDWR; is_write = true; break; case OPTION_PATTERN: - { - unsigned long res; - - if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > 0xff) { - error_report("Invalid pattern byte specified"); + pattern = cvtnum_full("pattern byte", optarg, false, 0, 0xff); + if (pattern < 0) { return 1; } - pattern = res; break; - } case OPTION_FLUSH_INTERVAL: - { - unsigned long res; - - if (qemu_strtoul(optarg, NULL, 0, &res) < 0 || res > INT_MAX) { - error_report("Invalid flush interval specified"); + flush_interval = cvtnum_full("flush interval", optarg, + false, 0, INT_MAX); + if (flush_interval < 0) { return 1; } - flush_interval = res; break; - } case OPTION_NO_DRAIN: drain_on_flush = false; break; @@ -5129,7 +5092,7 @@ static int img_bitmap(const img_cmd_t *ccmd, int argc, char **argv) add = true; break; case 'g': - granularity = cvtnum("granularity", optarg); + granularity = cvtnum("granularity", optarg, true); if (granularity < 0) { return 1; } @@ -5314,7 +5277,7 @@ static int img_dd_bs(const char *arg, { int64_t res; - res = cvtnum_full("bs", arg, 1, INT_MAX); + res = cvtnum_full("bs", arg, true, 1, INT_MAX); if (res < 0) { return 1; @@ -5328,7 +5291,7 @@ static int img_dd_count(const char *arg, struct DdIo *in, struct DdIo *out, struct DdInfo *dd) { - dd->count = cvtnum("count", arg); + dd->count = cvtnum("count", arg, true); if (dd->count < 0) { return 1; @@ -5359,7 +5322,7 @@ static int img_dd_skip(const char *arg, struct DdIo *in, struct DdIo *out, struct DdInfo *dd) { - in->offset = cvtnum("skip", arg); + in->offset = cvtnum("skip", arg, true); if (in->offset < 0) { return 1; @@ -5767,7 +5730,7 @@ static int img_measure(const img_cmd_t *ccmd, int argc, char **argv) user_creatable_process_cmdline(optarg); break; case 's': - img_size = cvtnum("image size", optarg); + img_size = cvtnum("image size", optarg, true); if (img_size < 0) { goto out; } diff --git a/tests/qemu-iotests/049.out b/tests/qemu-iotests/049.out index 34e1b452e6..70c627538b 100644 --- a/tests/qemu-iotests/049.out +++ b/tests/qemu-iotests/049.out @@ -98,8 +98,7 @@ qemu-img create -f qcow2 -o size=-1024 TEST_DIR/t.qcow2 qemu-img: TEST_DIR/t.qcow2: Value '-1024' is out of range for parameter 'size' qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- -1k -qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for -qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. +qemu-img: Invalid image size specified: '-1k' qemu-img create -f qcow2 -o size=-1k TEST_DIR/t.qcow2 qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64 @@ -107,8 +106,7 @@ Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta- and exabytes, respectively. qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- 1kilobyte -qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for -qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. +qemu-img: Invalid image size specified: '1kilobyte' qemu-img create -f qcow2 -o size=1kilobyte TEST_DIR/t.qcow2 qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64 @@ -116,8 +114,7 @@ Optional suffix k, M, G, T, P or E means kilo-, mega-, giga-, tera-, peta- and exabytes, respectively. qemu-img create -f qcow2 TEST_DIR/t.qcow2 -- foobar -qemu-img: Invalid image size specified. You may use k, M, G, T, P or E suffixes for -qemu-img: kilobytes, megabytes, gigabytes, terabytes, petabytes and exabytes. +qemu-img: Invalid image size specified: 'foobar' qemu-img create -f qcow2 -o size=foobar TEST_DIR/t.qcow2 qemu-img: TEST_DIR/t.qcow2: Parameter 'size' expects a non-negative number below 2^64 From ec5c2e7f38ee05c3edc81f2d0873141e4d8d074e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 4 Jul 2025 00:25:04 +0200 Subject: [PATCH 2486/2760] accel/hvf: Implement AccelClass::get_vcpu_stats() handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-developed-by: Mads Ynddal Signed-off-by: Mads Ynddal Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250715104015.72663-8-philmd@linaro.org> --- accel/hvf/hvf-accel-ops.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/accel/hvf/hvf-accel-ops.c b/accel/hvf/hvf-accel-ops.c index 214454bd0b..d488d6afba 100644 --- a/accel/hvf/hvf-accel-ops.c +++ b/accel/hvf/hvf-accel-ops.c @@ -58,6 +58,7 @@ #include "system/cpus.h" #include "system/hvf.h" #include "system/hvf_int.h" +#include HVFState *hvf_state; @@ -118,6 +119,12 @@ static void dummy_signal(int sig) { } +static void do_hvf_get_vcpu_exec_time(CPUState *cpu, run_on_cpu_data arg) +{ + int r = hv_vcpu_get_exec_time(cpu->accel->fd, arg.host_ptr); + assert_hvf_ok(r); +} + static void hvf_vcpu_destroy(CPUState *cpu) { hv_return_t ret = hv_vcpu_destroy(cpu->accel->fd); @@ -347,6 +354,21 @@ static void hvf_remove_all_breakpoints(CPUState *cpu) } } +static void hvf_get_vcpu_stats(CPUState *cpu, GString *buf) +{ + uint64_t time_mach; /* units of mach_absolute_time() */ + + run_on_cpu(cpu, do_hvf_get_vcpu_exec_time, RUN_ON_CPU_HOST_PTR(&time_mach)); + + mach_timebase_info_data_t timebase; + mach_timebase_info(&timebase); + uint64_t time_ns = time_mach * timebase.numer / timebase.denom; + + g_string_append_printf(buf, "HVF cumulative execution time: %llu.%.3llus\n", + time_ns / 1000000000, + (time_ns % 1000000000) / 1000000); +} + static void hvf_accel_ops_class_init(ObjectClass *oc, const void *data) { AccelOpsClass *ops = ACCEL_OPS_CLASS(oc); @@ -365,7 +387,10 @@ static void hvf_accel_ops_class_init(ObjectClass *oc, const void *data) ops->remove_all_breakpoints = hvf_remove_all_breakpoints; ops->update_guest_debug = hvf_update_guest_debug; ops->supports_guest_debug = hvf_arch_supports_guest_debug; + + ops->get_vcpu_stats = hvf_get_vcpu_stats; }; + static const TypeInfo hvf_accel_ops_type = { .name = ACCEL_OPS_NAME("hvf"), From 5a67e43096484ba11470b5b9aea5d78e32fded63 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 17 Jun 2025 06:44:18 +0200 Subject: [PATCH 2487/2760] system/runstate: Document qemu_add_vm_change_state_handler() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Reviewed-by: Alex Bennée Reviewed-by: Zhao Liu Message-Id: <20250703173248.44995-4-philmd@linaro.org> --- include/system/runstate.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/include/system/runstate.h b/include/system/runstate.h index fdd5c4a517..b6e8d6beab 100644 --- a/include/system/runstate.h +++ b/include/system/runstate.h @@ -14,6 +14,16 @@ void runstate_replay_enable(void); typedef void VMChangeStateHandler(void *opaque, bool running, RunState state); typedef int VMChangeStateHandlerWithRet(void *opaque, bool running, RunState state); +/** + * qemu_add_vm_change_state_handler: + * @cb: the callback to invoke + * @opaque: user data passed to the callback + * + * Register a callback function that is invoked when the vm starts or stops + * running. + * + * Returns: an entry to be freed using qemu_del_vm_change_state_handler() + */ VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, void *opaque); VMChangeStateEntry *qemu_add_vm_change_state_handler_prio( From 0a94a7b8802b7f6dc2521e48d837d1b5173f9db4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 15 Jul 2025 19:14:34 +0200 Subject: [PATCH 2488/2760] system/runstate: Document qemu_add_vm_change_state_handler_prio* in hdr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generally APIs to the rest of QEMU should be documented in the headers. Comments on individual functions or internal details are fine to live in the C files. Make qemu_add_vm_change_state_handler_prio[_full]() docstrings consistent by moving them from source to header. Suggested-by: Xiaoyao Li Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Pierrick Bouvier Message-Id: <20250715171920.89670-1-philmd@linaro.org> --- include/system/runstate.h | 30 ++++++++++++++++++++++++++++++ system/runstate.c | 30 ------------------------------ 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/include/system/runstate.h b/include/system/runstate.h index b6e8d6beab..b8d1bc3d27 100644 --- a/include/system/runstate.h +++ b/include/system/runstate.h @@ -26,9 +26,39 @@ typedef int VMChangeStateHandlerWithRet(void *opaque, bool running, RunState sta */ VMChangeStateEntry *qemu_add_vm_change_state_handler(VMChangeStateHandler *cb, void *opaque); +/** + * qemu_add_vm_change_state_handler_prio: + * @cb: the callback to invoke + * @opaque: user data passed to the callback + * @priority: low priorities execute first when the vm runs and the reverse is + * true when the vm stops + * + * Register a callback function that is invoked when the vm starts or stops + * running. + * + * Returns: an entry to be freed using qemu_del_vm_change_state_handler() + */ VMChangeStateEntry *qemu_add_vm_change_state_handler_prio( VMChangeStateHandler *cb, void *opaque, int priority); VMChangeStateEntry * +/** + * qemu_add_vm_change_state_handler_prio_full: + * @cb: the main callback to invoke + * @prepare_cb: a callback to invoke before the main callback + * @cb_ret: the main callback to invoke with return value + * @opaque: user data passed to the callbacks + * @priority: low priorities execute first when the vm runs and the reverse is + * true when the vm stops + * + * Register a main callback function and an optional prepare callback function + * that are invoked when the vm starts or stops running. The main callback and + * the prepare callback are called in two separate phases: First all prepare + * callbacks are called and only then all main callbacks are called. As its + * name suggests, the prepare callback can be used to do some preparatory work + * before invoking the main callback. + * + * Returns: an entry to be freed using qemu_del_vm_change_state_handler() + */ qemu_add_vm_change_state_handler_prio_full(VMChangeStateHandler *cb, VMChangeStateHandler *prepare_cb, VMChangeStateHandlerWithRet *cb_ret, diff --git a/system/runstate.c b/system/runstate.c index 38900c935a..fa32aa4795 100644 --- a/system/runstate.c +++ b/system/runstate.c @@ -306,18 +306,6 @@ struct VMChangeStateEntry { static QTAILQ_HEAD(, VMChangeStateEntry) vm_change_state_head = QTAILQ_HEAD_INITIALIZER(vm_change_state_head); -/** - * qemu_add_vm_change_state_handler_prio: - * @cb: the callback to invoke - * @opaque: user data passed to the callback - * @priority: low priorities execute first when the vm runs and the reverse is - * true when the vm stops - * - * Register a callback function that is invoked when the vm starts or stops - * running. - * - * Returns: an entry to be freed using qemu_del_vm_change_state_handler() - */ VMChangeStateEntry *qemu_add_vm_change_state_handler_prio( VMChangeStateHandler *cb, void *opaque, int priority) { @@ -325,24 +313,6 @@ VMChangeStateEntry *qemu_add_vm_change_state_handler_prio( opaque, priority); } -/** - * qemu_add_vm_change_state_handler_prio_full: - * @cb: the main callback to invoke - * @prepare_cb: a callback to invoke before the main callback - * @cb_ret: the main callback to invoke with return value - * @opaque: user data passed to the callbacks - * @priority: low priorities execute first when the vm runs and the reverse is - * true when the vm stops - * - * Register a main callback function and an optional prepare callback function - * that are invoked when the vm starts or stops running. The main callback and - * the prepare callback are called in two separate phases: First all prepare - * callbacks are called and only then all main callbacks are called. As its - * name suggests, the prepare callback can be used to do some preparatory work - * before invoking the main callback. - * - * Returns: an entry to be freed using qemu_del_vm_change_state_handler() - */ VMChangeStateEntry * qemu_add_vm_change_state_handler_prio_full(VMChangeStateHandler *cb, VMChangeStateHandler *prepare_cb, From 3ac6daa9e1c5d7dae2a3cd1c6a388174b462f3e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 4 Jun 2025 16:47:31 +0100 Subject: [PATCH 2489/2760] ui: fix setting client_endian field defaults MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a VNC client sends a "set pixel format" message, the 'client_endian' field will get initialized, however, it is valid to omit this message if the client wants to use the server's native pixel format. In the latter scenario nothing is initializing the 'client_endian' field, so it remains set to 0, matching neither G_LITTLE_ENDIAN nor G_BIG_ENDIAN. This then results in pixel format conversion routines taking the wrong code paths. This problem existed before the 'client_be' flag was changed into the 'client_endian' value, but the lack of initialization meant it semantically defaulted to little endian, so only big endian systems would potentially be exposed to incorrect pixel translation. The 'virt-viewer' / 'remote-viewer' apps always send a "set pixel format" message so aren't exposed to any problems, but the classical 'vncviewer' app will show the problem easily. Fixes: 7ed96710e82c385c6cfc3d064eec7dde20f0f3fd Reported-by: Thomas Huth Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Signed-off-by: Daniel P. Berrangé --- ui/vnc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/vnc.c b/ui/vnc.c index e9c30aad62..a16be468b9 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2329,6 +2329,7 @@ static void pixel_format_message (VncState *vs) { char pad[3] = { 0, 0, 0 }; vs->client_pf = qemu_default_pixelformat(32); + vs->client_endian = G_BYTE_ORDER; vnc_write_u8(vs, vs->client_pf.bits_per_pixel); /* bits-per-pixel */ vnc_write_u8(vs, vs->client_pf.depth); /* depth */ From 8fc3d63d685751734fb9c8c0284dc44a36a8e053 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Wed, 4 Jun 2025 17:14:38 +0100 Subject: [PATCH 2490/2760] ui: add trace events for all client messages MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This lets us see the full flow of RFB messages received from the client. Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau Signed-off-by: Daniel P. Berrangé --- ui/trace-events | 14 +++++++++++++ ui/vnc.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/ui/trace-events b/ui/trace-events index 3da0d5e280..3eba9ca3a8 100644 --- a/ui/trace-events +++ b/ui/trace-events @@ -48,13 +48,27 @@ vnc_msg_server_ext_desktop_resize(void *state, void *ioc, int width, int height, vnc_msg_client_audio_enable(void *state, void *ioc) "VNC client msg audio enable state=%p ioc=%p" vnc_msg_client_audio_disable(void *state, void *ioc) "VNC client msg audio disable state=%p ioc=%p" vnc_msg_client_audio_format(void *state, void *ioc, int fmt, int channels, int freq) "VNC client msg audio format state=%p ioc=%p fmt=%d channels=%d freq=%d" +vnc_msg_client_cut_text(void *state, void *ioc, int len) "VNC client msg cut text state=%p ioc=%p len=%u" +vnc_msg_client_cut_text_ext(void *state, void *ioc, int len, int flags) "VNC client msg cut text state=%p ioc=%p len=%u flags=%u" +vnc_msg_client_ext_key_event(void *state, void *ioc, int down, int sym, int keycode) "VNC client msg ext key event state=%p ioc=%p down=%u sym=%u keycode=%u" +vnc_msg_client_framebuffer_update_request(void *state, void *ioc, int incremental, int x, int y, int w, int h) "VNC client msg framebuffer update request state=%p ioc=%p incremental=%u x=%u y=%u w=%u h=%u" +vnc_msg_client_key_event(void *state, void *ioc, int down, int sym) "VNC client msg key event state=%p ioc=%p down=%u sym=%u" +vnc_msg_client_pointer_event(void *state, void *ioc, int button_mask, int x, int y) "VNC client msg pointer event state=%p ioc=%p button_mask=%u x=%u y=%u" vnc_msg_client_set_desktop_size(void *state, void *ioc, int width, int height, int screens) "VNC client msg set desktop size state=%p ioc=%p size=%dx%d screens=%d" +vnc_msg_client_set_encodings(void *state, void *ioc, int limit) "VNC client msg set encodings state=%p ioc=%p limit=%u" +vnc_msg_client_set_pixel_format(void *state, void *ioc, int bpp, int big_endian, int true_color) "VNC client msg set pixel format state=%p ioc=%p bpp=%u big_endian=%u true_color=%u" +vnc_msg_client_set_pixel_format_rgb(void *state, void *ioc, int red_max, int green_max, int blue_max, int red_shift, int green_shift, int blue_shift) "VNC client msg set pixel format RGB state=%p ioc=%p red_max=%u green_max=%u blue_max=%u red_shift=%u green_shift=%u blue_shift=%u" +vnc_msg_client_xvp(void *state, void *ioc, int version, int action) "VNC client msg XVP state=%p ioc=%p version=%u action=%u" vnc_client_eof(void *state, void *ioc) "VNC client EOF state=%p ioc=%p" vnc_client_io_error(void *state, void *ioc, const char *msg) "VNC client I/O error state=%p ioc=%p errmsg=%s" vnc_client_connect(void *state, void *ioc) "VNC client connect state=%p ioc=%p" vnc_client_disconnect_start(void *state, void *ioc) "VNC client disconnect start state=%p ioc=%p" vnc_client_disconnect_finish(void *state, void *ioc) "VNC client disconnect finish state=%p ioc=%p" vnc_client_io_wrap(void *state, void *ioc, const char *type) "VNC client I/O wrap state=%p ioc=%p type=%s" +vnc_client_pixel_format(void *state, void *ioc, int bpp, int depth, int endian) "VNC client pixel format state=%p ioc=%p bpp=%u depth=%u endian=%u" +vnc_client_pixel_format_red(void *state, void *ioc, int max, int bits, int shift, int mask) "VNC client pixel format red state=%p ioc=%p max=%u bits=%u shift=%u mask=%u" +vnc_client_pixel_format_green(void *state, void *ioc, int max, int bits, int shift, int mask) "VNC client pixel format green state=%p ioc=%p max=%u bits=%u shift=%u mask=%u" +vnc_client_pixel_format_blue(void *state, void *ioc, int max, int bits, int shift, int mask) "VNC client pixel format blue state=%p ioc=%p max=%u bits=%u shift=%u mask=%u" vnc_client_throttle_threshold(void *state, void *ioc, size_t oldoffset, size_t offset, int client_width, int client_height, int bytes_per_pixel, void *audio_cap) "VNC client throttle threshold state=%p ioc=%p oldoffset=%zu newoffset=%zu width=%d height=%d bpp=%d audio=%p" vnc_client_throttle_incremental(void *state, void *ioc, int job_update, size_t offset) "VNC client throttle incremental state=%p ioc=%p job-update=%d offset=%zu" vnc_client_throttle_forced(void *state, void *ioc, int job_update, size_t offset) "VNC client throttle forced state=%p ioc=%p job-update=%d offset=%zu" diff --git a/ui/vnc.c b/ui/vnc.c index a16be468b9..c309882ddb 100644 --- a/ui/vnc.c +++ b/ui/vnc.c @@ -2314,6 +2314,25 @@ static void set_pixel_format(VncState *vs, int bits_per_pixel, vs->client_pf.bytes_per_pixel = bits_per_pixel / 8; vs->client_pf.depth = bits_per_pixel == 32 ? 24 : bits_per_pixel; vs->client_endian = big_endian_flag ? G_BIG_ENDIAN : G_LITTLE_ENDIAN; + trace_vnc_client_pixel_format(vs, vs->ioc, + vs->client_pf.bits_per_pixel, + vs->client_pf.depth, + vs->client_endian); + trace_vnc_client_pixel_format_red(vs, vs->ioc, + vs->client_pf.rmax, + vs->client_pf.rbits, + vs->client_pf.rshift, + vs->client_pf.rmask); + trace_vnc_client_pixel_format_green(vs, vs->ioc, + vs->client_pf.gmax, + vs->client_pf.gbits, + vs->client_pf.gshift, + vs->client_pf.gmask); + trace_vnc_client_pixel_format_blue(vs, vs->ioc, + vs->client_pf.bmax, + vs->client_pf.bbits, + vs->client_pf.bshift, + vs->client_pf.bmask); if (!true_color_flag) { send_color_map(vs); @@ -2388,6 +2407,17 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 1) return 20; + trace_vnc_msg_client_set_pixel_format(vs, vs->ioc, + read_u8(data, 4), + read_u8(data, 6), + read_u8(data, 7)); + trace_vnc_msg_client_set_pixel_format_rgb(vs, vs->ioc, + read_u16(data, 8), + read_u16(data, 10), + read_u16(data, 12), + read_u8(data, 14), + read_u8(data, 15), + read_u8(data, 16)); set_pixel_format(vs, read_u8(data, 4), read_u8(data, 6), read_u8(data, 7), read_u16(data, 8), read_u16(data, 10), @@ -2410,12 +2440,19 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) memcpy(data + 4 + (i * 4), &val, sizeof(val)); } + trace_vnc_msg_client_set_encodings(vs, vs->ioc, limit); set_encodings(vs, (int32_t *)(data + 4), limit); break; case VNC_MSG_CLIENT_FRAMEBUFFER_UPDATE_REQUEST: if (len == 1) return 10; + trace_vnc_msg_client_framebuffer_update_request(vs, vs->ioc, + read_u8(data, 1), + read_u16(data, 2), + read_u16(data, 4), + read_u16(data, 6), + read_u16(data, 8)); framebuffer_update_request(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4), read_u16(data, 6), read_u16(data, 8)); @@ -2424,12 +2461,19 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 1) return 8; + trace_vnc_msg_client_key_event(vs, vs->ioc, + read_u8(data, 1), + read_u32(data, 4)); key_event(vs, read_u8(data, 1), read_u32(data, 4)); break; case VNC_MSG_CLIENT_POINTER_EVENT: if (len == 1) return 6; + trace_vnc_msg_client_pointer_event(vs, vs->ioc, + read_u8(data, 1), + read_u16(data, 2), + read_u16(data, 4)); pointer_event(vs, read_u8(data, 1), read_u16(data, 2), read_u16(data, 4)); break; case VNC_MSG_CLIENT_CUT_TEXT: @@ -2461,9 +2505,12 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) vnc_client_error(vs); break; } + trace_vnc_msg_client_cut_text_ext(vs, vs->ioc, + dlen, read_u32(data, 8)); vnc_client_cut_text_ext(vs, dlen, read_u32(data, 8), data + 12); break; } + trace_vnc_msg_client_cut_text(vs, vs->ioc, read_u32(data, 4)); vnc_client_cut_text(vs, read_u32(data, 4), data + 8); break; case VNC_MSG_CLIENT_XVP: @@ -2478,6 +2525,7 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 4) { uint8_t version = read_u8(data, 2); uint8_t action = read_u8(data, 3); + trace_vnc_msg_client_xvp(vs, vs->ioc, version, action); if (version != 1) { error_report("vnc: xvp client message version %d != 1", @@ -2511,6 +2559,10 @@ static int protocol_client_msg(VncState *vs, uint8_t *data, size_t len) if (len == 2) return 12; + trace_vnc_msg_client_ext_key_event(vs, vs->ioc, + read_u16(data, 2), + read_u32(data, 4), + read_u32(data, 8)); ext_key_event(vs, read_u16(data, 2), read_u32(data, 4), read_u32(data, 8)); break; From 2183ab62512c6253293e83cce3970b0b42e65630 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 Jul 2025 15:44:17 +0100 Subject: [PATCH 2491/2760] crypto/x509-utils: Check for error from gnutls_x509_crt_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity notes that in qcrypto_get_x509_cert_fingerprint() we call gnutls_x509_crt_init() but don't check for an error return. Add the missing check. Coverity: CID 1593155 Fixes: 10a1d34fc0d ("crypto: Introduce x509 utils") Signed-off-by: Peter Maydell Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé --- crypto/x509-utils.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crypto/x509-utils.c b/crypto/x509-utils.c index 8bad00a51b..39bb6d4d8c 100644 --- a/crypto/x509-utils.c +++ b/crypto/x509-utils.c @@ -46,7 +46,11 @@ int qcrypto_get_x509_cert_fingerprint(uint8_t *cert, size_t size, return -1; } - gnutls_x509_crt_init(&crt); + if (gnutls_x509_crt_init(&crt) < 0) { + error_setg(errp, "Unable to initialize certificate: %s", + gnutls_strerror(ret)); + return -1; + } if (gnutls_x509_crt_import(crt, &datum, GNUTLS_X509_FMT_PEM) != 0) { error_setg(errp, "Failed to import certificate"); From 44b540338a3e271866a3d636359bfe6b2edecbb6 Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 22:08:40 -0400 Subject: [PATCH 2492/2760] fsdev/9p-marshal: move G_GNUC_PRINTF to header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v9fs_string_sprintf() is annotated with G_GNUC_PRINTF(2, 3) in 9p-marshal.c, but the prototype in fsdev/9p-marshal.h is missing the attribute, so callers that include only the header do not get format checking. Move the annotation to the header and delete the duplicate in the source file. No behavior change. Signed-off-by: Sean Wei Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250613.qemu.9p.01@sean.taipei> [CS: fix code style (max. 80 chars per line)] Signed-off-by: Christian Schoenebeck --- fsdev/9p-marshal.c | 3 +-- fsdev/9p-marshal.h | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/fsdev/9p-marshal.c b/fsdev/9p-marshal.c index f9b0336cd5..3455580703 100644 --- a/fsdev/9p-marshal.c +++ b/fsdev/9p-marshal.c @@ -27,8 +27,7 @@ void v9fs_string_free(V9fsString *str) str->size = 0; } -void G_GNUC_PRINTF(2, 3) -v9fs_string_sprintf(V9fsString *str, const char *fmt, ...) +void v9fs_string_sprintf(V9fsString *str, const char *fmt, ...) { va_list ap; diff --git a/fsdev/9p-marshal.h b/fsdev/9p-marshal.h index f1abbe151c..8995e42067 100644 --- a/fsdev/9p-marshal.h +++ b/fsdev/9p-marshal.h @@ -76,7 +76,8 @@ static inline void v9fs_string_init(V9fsString *str) str->size = 0; } void v9fs_string_free(V9fsString *str); -void v9fs_string_sprintf(V9fsString *str, const char *fmt, ...); +void G_GNUC_PRINTF(2, 3) v9fs_string_sprintf(V9fsString *str, const char *fmt, + ...); void v9fs_string_copy(V9fsString *lhs, V9fsString *rhs); #endif From 44f51c1a3cf435daa82eb757740b59b1fd4fe71c Mon Sep 17 00:00:00 2001 From: Sean Wei Date: Fri, 13 Jun 2025 22:09:20 -0400 Subject: [PATCH 2493/2760] hw/9pfs: move G_GNUC_PRINTF to header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit v9fs_path_sprintf() is annotated with G_GNUC_PRINTF(2, 3) in hw/9pfs/9p.c, but the prototype in hw/9pfs/9p.h is missing the attribute, so callers that include only the header do not get format checking. Move the annotation to the header and delete the duplicate in the source file. No behavior change. Signed-off-by: Sean Wei Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250613.qemu.9p.02@sean.taipei> [CS: fix code style (max. 80 chars per line)] Signed-off-by: Christian Schoenebeck --- hw/9pfs/9p.c | 3 +-- hw/9pfs/9p.h | 3 ++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/9pfs/9p.c b/hw/9pfs/9p.c index 8b001b9112..acfa7db4e1 100644 --- a/hw/9pfs/9p.c +++ b/hw/9pfs/9p.c @@ -201,8 +201,7 @@ void v9fs_path_free(V9fsPath *path) } -void G_GNUC_PRINTF(2, 3) -v9fs_path_sprintf(V9fsPath *path, const char *fmt, ...) +void v9fs_path_sprintf(V9fsPath *path, const char *fmt, ...) { va_list ap; diff --git a/hw/9pfs/9p.h b/hw/9pfs/9p.h index 259ad32ed1..65cc45e344 100644 --- a/hw/9pfs/9p.h +++ b/hw/9pfs/9p.h @@ -456,7 +456,8 @@ static inline uint8_t v9fs_request_cancelled(V9fsPDU *pdu) void coroutine_fn v9fs_reclaim_fd(V9fsPDU *pdu); void v9fs_path_init(V9fsPath *path); void v9fs_path_free(V9fsPath *path); -void v9fs_path_sprintf(V9fsPath *path, const char *fmt, ...); +void G_GNUC_PRINTF(2, 3) v9fs_path_sprintf(V9fsPath *path, const char *fmt, + ...); void v9fs_path_copy(V9fsPath *dst, const V9fsPath *src); size_t v9fs_readdir_response_size(V9fsString *name); int v9fs_name_to_path(V9fsState *s, V9fsPath *dirpath, From 8eb6d39e22a2f9ea1a415d95d3c3ac5d5752b6d7 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Fri, 11 Jul 2025 08:45:01 -0700 Subject: [PATCH 2494/2760] qom: qom-list-get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Using qom-list and qom-get to get all the nodes and property values in a QOM tree can take multiple seconds because it requires 1000's of individual QOM requests. Some managers fetch the entire tree or a large subset of it when starting a new VM, and this cost is a substantial fraction of start up time. Define the qom-list-get command, which fetches all the properties and values for a list of paths. This can be much faster than qom-list plus qom-get. When getting an entire QOM tree, I measured a 10x speedup in elapsed time. Signed-off-by: Steve Sistare Tested-by: Philippe Mathieu-Daudé Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Markus Armbruster Message-ID: <1752248703-217318-2-git-send-email-steven.sistare@oracle.com> Signed-off-by: Markus Armbruster --- qapi/qom.json | 50 +++++++++++++++++++++++++++++++++++++++++++ qom/qom-qmp-cmds.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/qapi/qom.json b/qapi/qom.json index 96d56df6cd..830cb2ffe7 100644 --- a/qapi/qom.json +++ b/qapi/qom.json @@ -47,6 +47,34 @@ '*description': 'str', '*default-value': 'any' } } +## +# @ObjectPropertyValue: +# +# @name: the name of the property. +# +# @type: the type of the property, as described in `ObjectPropertyInfo`. +# +# @value: the value of the property. Absent when the property cannot +# be read. +# +# Since 10.1 +## +{ 'struct': 'ObjectPropertyValue', + 'data': { 'name': 'str', + 'type': 'str', + '*value': 'any' } } + +## +# @ObjectPropertiesValues: +# +# @properties: a list of properties. +# +# Since 10.1 +## +{ 'struct': 'ObjectPropertiesValues', + 'data': { 'properties': [ 'ObjectPropertyValue' ] }} + + ## # @qom-list: # @@ -124,6 +152,28 @@ 'returns': 'any', 'allow-preconfig': true } +## +# @qom-list-get: +# +# List properties and their values for each object path in the input +# list. +# +# @paths: The absolute or partial path for each object, as described +# in `qom-get`. +# +# Errors: +# - If any path is not valid or is ambiguous +# +# Returns: A list where each element is the result for the +# corresponding element of @paths. +# +# Since 10.1 +## +{ 'command': 'qom-list-get', + 'data': { 'paths': [ 'str' ] }, + 'returns': [ 'ObjectPropertiesValues' ], + 'allow-preconfig': true } + ## # @qom-set: # diff --git a/qom/qom-qmp-cmds.c b/qom/qom-qmp-cmds.c index 293755f409..57f1898cf6 100644 --- a/qom/qom-qmp-cmds.c +++ b/qom/qom-qmp-cmds.c @@ -69,6 +69,59 @@ ObjectPropertyInfoList *qmp_qom_list(const char *path, Error **errp) return props; } +static void qom_list_add_property_value(Object *obj, ObjectProperty *prop, + ObjectPropertyValueList **props) +{ + ObjectPropertyValue *item = g_new0(ObjectPropertyValue, 1); + + QAPI_LIST_PREPEND(*props, item); + + item->name = g_strdup(prop->name); + item->type = g_strdup(prop->type); + item->value = object_property_get_qobject(obj, prop->name, NULL); +} + +static ObjectPropertyValueList *qom_get_property_value_list(const char *path, + Error **errp) +{ + Object *obj; + ObjectProperty *prop; + ObjectPropertyIterator iter; + ObjectPropertyValueList *props = NULL; + + obj = qom_resolve_path(path, errp); + if (obj == NULL) { + return NULL; + } + + object_property_iter_init(&iter, obj); + while ((prop = object_property_iter_next(&iter))) { + qom_list_add_property_value(obj, prop, &props); + } + + return props; +} + +ObjectPropertiesValuesList *qmp_qom_list_get(strList *paths, Error **errp) +{ + ObjectPropertiesValuesList *head = NULL, **tail = &head; + strList *path; + + for (path = paths; path; path = path->next) { + ObjectPropertiesValues *item = g_new0(ObjectPropertiesValues, 1); + + QAPI_LIST_APPEND(tail, item); + + item->properties = qom_get_property_value_list(path->value, errp); + if (!item->properties) { + qapi_free_ObjectPropertiesValuesList(head); + return NULL; + } + } + + return head; +} + void qmp_qom_set(const char *path, const char *property, QObject *value, Error **errp) { From 4ece9b61c90ce8cd5edb28ce1ba9e14992382fb0 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Fri, 11 Jul 2025 08:45:02 -0700 Subject: [PATCH 2495/2760] python: use qom-list-get Use qom-list-get to speed up the qom-tree command. Signed-off-by: Steve Sistare Acked-by: Markus Armbruster Message-ID: <1752248703-217318-3-git-send-email-steven.sistare@oracle.com> Tested-by: Markus Armbruster [Lint picked off to mollify make check-minreqs] Signed-off-by: Markus Armbruster --- python/qemu/utils/qom.py | 45 +++++++++++++++------------ python/qemu/utils/qom_common.py | 55 +++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+), 20 deletions(-) diff --git a/python/qemu/utils/qom.py b/python/qemu/utils/qom.py index 426a0f245f..05e5f14179 100644 --- a/python/qemu/utils/qom.py +++ b/python/qemu/utils/qom.py @@ -31,8 +31,7 @@ ## import argparse - -from qemu.qmp import ExecuteError +from typing import List from .qom_common import QOMCommand @@ -224,28 +223,34 @@ def __init__(self, args: argparse.Namespace): super().__init__(args) self.path = args.path - def _list_node(self, path: str) -> None: - print(path) - items = self.qom_list(path) - for item in items: - if item.child: - continue - try: - rsp = self.qmp.cmd('qom-get', path=path, - property=item.name) - print(f" {item.name}: {rsp} ({item.type})") - except ExecuteError as err: - print(f" {item.name}: ({item.type})") - print('') - for item in items: - if not item.child: - continue + def _list_nodes(self, paths: List[str]) -> None: + all_paths_props = self.qom_list_get(paths) + i = 0 + + for props in all_paths_props: + path = paths[i] + i = i + 1 + print(path) if path == '/': path = '' - self._list_node(f"{path}/{item.name}") + newpaths = [] + + for item in props.properties: + if item.child: + newpaths += [f"{path}/{item.name}"] + else: + value = item.value + if value is None: + value = "" + print(f" {item.name}: {value} ({item.type})") + + print('') + + if newpaths: + self._list_nodes(newpaths) def run(self) -> int: - self._list_node(self.path) + self._list_nodes([self.path]) return 0 diff --git a/python/qemu/utils/qom_common.py b/python/qemu/utils/qom_common.py index dd2c8b1908..ab21a4d364 100644 --- a/python/qemu/utils/qom_common.py +++ b/python/qemu/utils/qom_common.py @@ -65,6 +65,52 @@ def link(self) -> bool: return self.type.startswith('link<') +class ObjectPropertyValue: + """ + Represents a property return from e.g. qom-tree-get + """ + def __init__(self, name: str, type_: str, value: object): + self.name = name + self.type = type_ + self.value = value + + @classmethod + def make(cls, value: Dict[str, Any]) -> 'ObjectPropertyValue': + """ + Build an ObjectPropertyValue from a Dict with an unknown shape. + """ + assert value.keys() >= {'name', 'type'} + assert value.keys() <= {'name', 'type', 'value'} + return cls(value['name'], value['type'], value.get('value')) + + @property + def child(self) -> bool: + """Is this property a child property?""" + return self.type.startswith('child<') + + +class ObjectPropertiesValues: + """ + Represents the return type from e.g. qom-list-get + """ + # pylint: disable=too-few-public-methods + + def __init__(self, properties: List[ObjectPropertyValue]) -> None: + self.properties = properties + + @classmethod + def make(cls, value: Dict[str, Any]) -> 'ObjectPropertiesValues': + """ + Build an ObjectPropertiesValues from a Dict with an unknown shape. + """ + assert value.keys() == {'properties'} + props = [ObjectPropertyValue(item['name'], + item['type'], + item.get('value')) + for item in value['properties']] + return cls(props) + + CommandT = TypeVar('CommandT', bound='QOMCommand') @@ -145,6 +191,15 @@ def qom_list(self, path: str) -> List[ObjectPropertyInfo]: assert isinstance(rsp, list) return [ObjectPropertyInfo.make(x) for x in rsp] + def qom_list_get(self, paths: List[str]) -> List[ObjectPropertiesValues]: + """ + :return: a strongly typed list from the 'qom-list-get' command. + """ + rsp = self.qmp.cmd('qom-list-get', paths=paths) + # qom-list-get returns List[ObjectPropertiesValues] + assert isinstance(rsp, list) + return [ObjectPropertiesValues.make(x) for x in rsp] + @classmethod def command_runner( cls: Type[CommandT], From 3dd93992ffbd86a520e5f887d17179fd00ff0928 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Fri, 11 Jul 2025 08:45:03 -0700 Subject: [PATCH 2496/2760] tests/qtest/qom-test: unit test for qom-list-get MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a unit test for qom-list-get. Signed-off-by: Steve Sistare Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Markus Armbruster Message-ID: <1752248703-217318-4-git-send-email-steven.sistare@oracle.com> Signed-off-by: Markus Armbruster --- tests/qtest/qom-test.c | 116 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 115 insertions(+), 1 deletion(-) diff --git a/tests/qtest/qom-test.c b/tests/qtest/qom-test.c index 27d70bc11c..4ade1c728c 100644 --- a/tests/qtest/qom-test.c +++ b/tests/qtest/qom-test.c @@ -11,11 +11,119 @@ #include "qobject/qdict.h" #include "qobject/qlist.h" +#include "qobject/qstring.h" #include "qemu/cutils.h" #include "libqtest.h" +#define RAM_NAME "node0" +#define RAM_SIZE 65536 + static int verbosity_level; +/* + * Verify that the /object/RAM_NAME 'size' property is RAM_SIZE. + */ +static void test_list_get_value(QTestState *qts) +{ + QDict *args = qdict_new(); + g_autoptr(QDict) response = NULL; + g_autoptr(QList) paths = qlist_new(); + QListEntry *entry, *prop_entry; + const char *prop_name; + QList *properties, *return_list; + QDict *obj; + + qlist_append_str(paths, "/objects/" RAM_NAME); + qdict_put_obj(args, "paths", QOBJECT(qlist_copy(paths))); + response = qtest_qmp(qts, "{ 'execute': 'qom-list-get'," + " 'arguments': %p }", args); + g_assert(response); + g_assert(qdict_haskey(response, "return")); + return_list = qobject_to(QList, qdict_get(response, "return")); + + entry = QTAILQ_FIRST(&return_list->head); + obj = qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(qdict_haskey(obj, "properties")); + properties = qobject_to(QList, qdict_get(obj, "properties")); + + QLIST_FOREACH_ENTRY(properties, prop_entry) { + QDict *prop = qobject_to(QDict, qlist_entry_obj(prop_entry)); + + g_assert(qdict_haskey(prop, "name")); + g_assert(qdict_haskey(prop, "value")); + + prop_name = qdict_get_str(prop, "name"); + if (!strcmp(prop_name, "type")) { + g_assert_cmpstr(qdict_get_str(prop, "value"), ==, + "memory-backend-ram"); + + } else if (!strcmp(prop_name, "size")) { + g_assert_cmpint(qdict_get_int(prop, "value"), ==, RAM_SIZE); + } + } +} + +static void test_list_get(QTestState *qts, QList *paths) +{ + QListEntry *entry, *prop_entry, *path_entry; + g_autoptr(QDict) response = NULL; + QDict *args = qdict_new(); + QDict *prop; + QList *return_list; + + if (verbosity_level >= 2) { + g_test_message("Obtaining properties for paths:"); + QLIST_FOREACH_ENTRY(paths, path_entry) { + QString *qstr = qobject_to(QString, qlist_entry_obj(path_entry)); + g_test_message(" %s", qstring_get_str(qstr)); + } + } + + qdict_put_obj(args, "paths", QOBJECT(qlist_copy(paths))); + response = qtest_qmp(qts, "{ 'execute': 'qom-list-get'," + " 'arguments': %p }", args); + g_assert(response); + g_assert(qdict_haskey(response, "return")); + return_list = qobject_to(QList, qdict_get(response, "return")); + g_assert(!qlist_empty(return_list)); + + path_entry = QTAILQ_FIRST(&paths->head); + QLIST_FOREACH_ENTRY(return_list, entry) { + QDict *obj = qobject_to(QDict, qlist_entry_obj(entry)); + g_assert(qdict_haskey(obj, "properties")); + QList *properties = qobject_to(QList, qdict_get(obj, "properties")); + bool has_child = false; + + QLIST_FOREACH_ENTRY(properties, prop_entry) { + prop = qobject_to(QDict, qlist_entry_obj(prop_entry)); + g_assert(qdict_haskey(prop, "name")); + g_assert(qdict_haskey(prop, "type")); + has_child |= strstart(qdict_get_str(prop, "type"), "child<", NULL); + } + + if (has_child) { + /* build a list of child paths */ + QString *qstr = qobject_to(QString, qlist_entry_obj(path_entry)); + const char *path = qstring_get_str(qstr); + g_autoptr(QList) child_paths = qlist_new(); + + QLIST_FOREACH_ENTRY(properties, prop_entry) { + prop = qobject_to(QDict, qlist_entry_obj(prop_entry)); + if (strstart(qdict_get_str(prop, "type"), "child<", NULL)) { + g_autofree char *child_path = g_strdup_printf( + "%s/%s", path, qdict_get_str(prop, "name")); + qlist_append_str(child_paths, child_path); + } + } + + /* fetch props for all children with one qom-list-get call */ + test_list_get(qts, child_paths); + } + + path_entry = QTAILQ_NEXT(path_entry, next); + } +} + static void test_properties(QTestState *qts, const char *path, bool recurse) { char *child_path; @@ -85,8 +193,10 @@ static void test_machine(gconstpointer data) const char *machine = data; QDict *response; QTestState *qts; + g_autoptr(QList) paths = qlist_new(); - qts = qtest_initf("-machine %s", machine); + qts = qtest_initf("-machine %s -object memory-backend-ram,id=%s,size=%d", + machine, RAM_NAME, RAM_SIZE); if (g_test_slow()) { /* Make sure we can get the machine class properties: */ @@ -101,6 +211,10 @@ static void test_machine(gconstpointer data) test_properties(qts, "/machine", true); + qlist_append_str(paths, "/machine"); + test_list_get(qts, paths); + test_list_get_value(qts); + response = qtest_qmp(qts, "{ 'execute': 'quit' }"); g_assert(qdict_haskey(response, "return")); qobject_unref(response); From 5dd68db9cd005aeb2870d8754fd90e1b6af793a7 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 15 Jul 2025 17:28:47 -0400 Subject: [PATCH 2497/2760] docs: Bump sphinx to 6.2.1 sphinx 5.3.0 fails with Python 3.13.1: ../docs/meson.build:37: WARNING: /home/me/qemu/build/pyvenv/bin/sphinx-build: Extension error: Could not import extension sphinx.builders.epub3 (exception: No module named 'imghdr') ../docs/meson.build:39:6: ERROR: Problem encountered: Install a Python 3 version of python-sphinx and the readthedoc theme Bump sphinx to 6.2.1 and also sphinx_rtd_theme as required for the new sphinx version. (jsnow note: this patch bumps the recommended version for Sphinx to install when it is missing, but allows old versions to be used if they are present and functional. The version used for building docs on readthedocs is pinned to the recommended version, 6.2.1.) Signed-off-by: Akihiko Odaki Tested-by: Markus Armbruster Reviewed-by: John Snow Signed-off-by: John Snow Message-ID: <20250715212848.171879-2-jsnow@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Markus Armbruster --- docs/requirements.txt | 4 ++-- pythondeps.toml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/requirements.txt b/docs/requirements.txt index 02583f209a..87f7afcb2e 100644 --- a/docs/requirements.txt +++ b/docs/requirements.txt @@ -1,5 +1,5 @@ # Used by readthedocs.io # Should be in sync with the "installed" key of pythondeps.toml -sphinx==5.3.0 -sphinx_rtd_theme==1.1.1 +sphinx==6.2.1 +sphinx_rtd_theme==1.2.2 diff --git a/pythondeps.toml b/pythondeps.toml index 7884ab521d..b2eec940ce 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -24,8 +24,8 @@ pycotap = { accepted = ">=1.1.0", installed = "1.3.1" } [docs] # Please keep the installed versions in sync with docs/requirements.txt -sphinx = { accepted = ">=3.4.3", installed = "5.3.0", canary = "sphinx-build" } -sphinx_rtd_theme = { accepted = ">=0.5", installed = "1.1.1" } +sphinx = { accepted = ">=3.4.3", installed = "6.2.1", canary = "sphinx-build" } +sphinx_rtd_theme = { accepted = ">=0.5", installed = "1.2.2" } [testdeps] qemu.qmp = { accepted = ">=0.0.3", installed = "0.0.3" } From b417dd98f442a11f8a7fa79ae7e78279a42483cb Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 15 Jul 2025 17:28:48 -0400 Subject: [PATCH 2498/2760] MAINTAINERS: Add docs/requirements.txt Add docs/requirements.txt to "Sphinx documentation configuration and build machinery". Signed-off-by: Akihiko Odaki Signed-off-by: John Snow Message-ID: <20250715212848.171879-3-jsnow@redhat.com> Reviewed-by: Thomas Huth Signed-off-by: Markus Armbruster --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index bc0af3e8dd..a462345618 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4428,6 +4428,7 @@ M: Peter Maydell S: Maintained F: docs/conf.py F: docs/*/conf.py +F: docs/requirements.txt F: docs/sphinx/ F: docs/_templates/ F: docs/devel/docs.rst From 64e4375b2b1efb634876a119a7378c50be5d195b Mon Sep 17 00:00:00 2001 From: John Snow Date: Tue, 15 Jul 2025 18:25:48 -0400 Subject: [PATCH 2499/2760] python: fix editable installs for modern pip/setuptools The way editable installs work has changed at some point since Fedora 40 was released. Generally, we should be opting to use pyproject.toml installs (PEP517/518) - but those are not fully supported until v61 of setuptools, and CentOS Stream 9 ships v53. Until that time, we can make use of a transitional feature in pip/setuptools to use "legacy" editable installs, which is enough to fix "make check-dev" on modern local workstations for now. By using the environment variable approach to configure pip, we avoid any problems for older versions of pip that don't recognize this option, so it's harmless. The config-settings option first appeared in v23 of pip. editable_mode was first supported by setuptools in v64. (I'm not currently precisely aware of when the default behavior of '-e' switched away from 'compat', but it appears to be a joint effect between setuptools and pip versions.) Version information for supported build platforms: distro python3 pip setuptools sphinx -------------------------------------------------------- centos_stream_9 3.9.23 21.3.1 53.0.0 3.4.3 ubuntu_22_04 3.10.12 22.0.2 59.6.0 4.3.2 ** pyproject.toml installs supported as of here ** freebsd 3.11.13 23.3.2 63.1.0 5.3.0 debian_12 3.11.2 23.0.1 66.1.1 5.3.0 ubuntu_24_04 3.12.3 24.0 68.1.2 7.2.6 centos_stream_10 3.12.11 23.3.2 69.0.3 7.2.6 fedora_41 3.13.5 24.2 69.2.0 7.3.7 alpine_3_19 3.11.13 23.3.1 70.3.0 6.2.1 alpine_3_20 3.12.11 24.0 70.3.0 7.2.6 alpine_3_21 3.12.11 24.3.1 70.3.0 8.1.3 ubuntu_24_10 3.12.7 24.2 74.1.2 7.4.7 fedora_42 3.13.5 24.3.1 74.1.3 8.1.3 ubuntu_25_04 3.13.3 25.0 75.8.0 8.1.3 macports 3.13.5 25.1.1 78.1.1 8.2.3 openbsd 3.12.11 25.1.1 79.0.1 8.2.3 alpine_3_22 3.12.11 25.1.1 80.9.0 8.2.3 homebrew 3.13.5 --- 80.9.0 8.2.3 pkgsrc_current 3.12.11 25.1.1 80.9.0 8.2.3 Signed-off-by: John Snow Message-ID: <20250715222548.198888-1-jsnow@redhat.com> Tested-by: Markus Armbruster Signed-off-by: Markus Armbruster --- python/Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python/Makefile b/python/Makefile index 764b79ccb2..32aedce413 100644 --- a/python/Makefile +++ b/python/Makefile @@ -68,7 +68,7 @@ $(QEMU_MINVENV_DIR) $(QEMU_MINVENV_DIR)/bin/activate: setup.cfg tests/minreqs.tx echo "INSTALL -r tests/minreqs.txt $(QEMU_MINVENV_DIR)";\ $(PIP_INSTALL) -r tests/minreqs.txt 1>/dev/null; \ echo "INSTALL -e qemu $(QEMU_MINVENV_DIR)"; \ - $(PIP_INSTALL) -e . 1>/dev/null; \ + PIP_CONFIG_SETTINGS="editable_mode=compat" $(PIP_INSTALL) -e . 1>/dev/null; \ ) @touch $(QEMU_MINVENV_DIR) @@ -103,7 +103,7 @@ check-dev: dev-venv .PHONY: develop develop: - $(PIP_INSTALL) -e .[devel] + PIP_CONFIG_SETTINGS="editable_mode=compat" $(PIP_INSTALL) -e .[devel] .PHONY: check check: From 350785d41d8bb0b799dd16ea04a7232dc8d6093a Mon Sep 17 00:00:00 2001 From: Shaoqin Huang Date: Thu, 17 Jul 2025 06:09:39 -0400 Subject: [PATCH 2500/2760] ramfb: Add property to control if load the romfile Currently the ramfb device loads the vgabios-ramfb.bin unconditionally, but only the x86 need the vgabios-ramfb.bin, this can cause that when use the release package on arm64 it can't find the vgabios-ramfb.bin. Because only seabios will use the vgabios-ramfb.bin, load the rom logic is x86-specific. For other !x86 platforms, the edk2 ships an EFI driver for ramfb, so they don't need to load the romfile. So add a new property use-legacy-x86-rom in both ramfb and vfio_pci device, because the vfio display also use the ramfb_setup() to load the vgabios-ramfb.bin file. After have this property, the machine type can set the compatibility to not load the vgabios-ramfb.bin if the arch doesn't need it. For now the default value is true but it will be turned off by default in subsequent patch when compats get properly handled. Reviewed-by: Zhao Liu Reviewed-by: Eric Auger Signed-off-by: Shaoqin Huang Message-ID: <20250717100941.2230408-2-shahuang@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/display/ramfb-standalone.c | 5 ++++- hw/display/ramfb-stubs.c | 2 +- hw/display/ramfb.c | 6 ++++-- hw/vfio/display.c | 4 ++-- hw/vfio/pci.c | 2 ++ hw/vfio/pci.h | 1 + include/hw/display/ramfb.h | 2 +- 7 files changed, 15 insertions(+), 7 deletions(-) diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c index 08f2d5db4e..82d8c69f89 100644 --- a/hw/display/ramfb-standalone.c +++ b/hw/display/ramfb-standalone.c @@ -17,6 +17,7 @@ struct RAMFBStandaloneState { QemuConsole *con; RAMFBState *state; bool migrate; + bool use_legacy_x86_rom; }; static void display_update_wrapper(void *dev) @@ -39,7 +40,7 @@ static void ramfb_realizefn(DeviceState *dev, Error **errp) RAMFBStandaloneState *ramfb = RAMFB(dev); ramfb->con = graphic_console_init(dev, 0, &wrapper_ops, dev); - ramfb->state = ramfb_setup(errp); + ramfb->state = ramfb_setup(ramfb->use_legacy_x86_rom, errp); } static bool migrate_needed(void *opaque) @@ -62,6 +63,8 @@ static const VMStateDescription ramfb_dev_vmstate = { static const Property ramfb_properties[] = { DEFINE_PROP_BOOL("x-migrate", RAMFBStandaloneState, migrate, true), + DEFINE_PROP_BOOL("use-legacy-x86-rom", RAMFBStandaloneState, + use_legacy_x86_rom, true), }; static void ramfb_class_initfn(ObjectClass *klass, const void *data) diff --git a/hw/display/ramfb-stubs.c b/hw/display/ramfb-stubs.c index cf64733b10..b83551357b 100644 --- a/hw/display/ramfb-stubs.c +++ b/hw/display/ramfb-stubs.c @@ -8,7 +8,7 @@ void ramfb_display_update(QemuConsole *con, RAMFBState *s) { } -RAMFBState *ramfb_setup(Error **errp) +RAMFBState *ramfb_setup(bool romfile, Error **errp) { error_setg(errp, "ramfb support not available"); return NULL; diff --git a/hw/display/ramfb.c b/hw/display/ramfb.c index 8c0f907673..9a17d97d07 100644 --- a/hw/display/ramfb.c +++ b/hw/display/ramfb.c @@ -135,7 +135,7 @@ const VMStateDescription ramfb_vmstate = { } }; -RAMFBState *ramfb_setup(Error **errp) +RAMFBState *ramfb_setup(bool romfile, Error **errp) { FWCfgState *fw_cfg = fw_cfg_find(); RAMFBState *s; @@ -147,7 +147,9 @@ RAMFBState *ramfb_setup(Error **errp) s = g_new0(RAMFBState, 1); - rom_add_vga("vgabios-ramfb.bin"); + if (romfile) { + rom_add_vga("vgabios-ramfb.bin"); + } fw_cfg_add_file_callback(fw_cfg, "etc/ramfb", NULL, ramfb_fw_cfg_write, s, &s->cfg, sizeof(s->cfg), false); diff --git a/hw/vfio/display.c b/hw/vfio/display.c index 9c6f5aa265..faacd9019a 100644 --- a/hw/vfio/display.c +++ b/hw/vfio/display.c @@ -365,7 +365,7 @@ static bool vfio_display_dmabuf_init(VFIOPCIDevice *vdev, Error **errp) &vfio_display_dmabuf_ops, vdev); if (vdev->enable_ramfb) { - vdev->dpy->ramfb = ramfb_setup(errp); + vdev->dpy->ramfb = ramfb_setup(vdev->use_legacy_x86_rom, errp); if (!vdev->dpy->ramfb) { return false; } @@ -494,7 +494,7 @@ static bool vfio_display_region_init(VFIOPCIDevice *vdev, Error **errp) &vfio_display_region_ops, vdev); if (vdev->enable_ramfb) { - vdev->dpy->ramfb = ramfb_setup(errp); + vdev->dpy->ramfb = ramfb_setup(vdev->use_legacy_x86_rom, errp); if (!vdev->dpy->ramfb) { return false; } diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 1093b28df7..0b969b3359 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3809,6 +3809,8 @@ static const TypeInfo vfio_pci_dev_info = { static const Property vfio_pci_dev_nohotplug_properties[] = { DEFINE_PROP_BOOL("ramfb", VFIOPCIDevice, enable_ramfb, false), + DEFINE_PROP_BOOL("use-legacy-x86-rom", VFIOPCIDevice, + use_legacy_x86_rom, true), DEFINE_PROP_ON_OFF_AUTO("x-ramfb-migrate", VFIOPCIDevice, ramfb_migrate, ON_OFF_AUTO_AUTO), }; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 495fae737d..826db8c131 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -188,6 +188,7 @@ struct VFIOPCIDevice { bool no_kvm_ioeventfd; bool no_vfio_ioeventfd; bool enable_ramfb; + bool use_legacy_x86_rom; OnOffAuto ramfb_migrate; bool defer_kvm_irq_routing; bool clear_parent_atomics_on_exit; diff --git a/include/hw/display/ramfb.h b/include/hw/display/ramfb.h index a7e0019144..172aa6dc89 100644 --- a/include/hw/display/ramfb.h +++ b/include/hw/display/ramfb.h @@ -6,7 +6,7 @@ /* ramfb.c */ typedef struct RAMFBState RAMFBState; void ramfb_display_update(QemuConsole *con, RAMFBState *s); -RAMFBState *ramfb_setup(Error **errp); +RAMFBState *ramfb_setup(bool romfile, Error **errp); extern const VMStateDescription ramfb_vmstate; From d3a24134e37d57abd3e7445842cda2717f49e96d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 14 Jul 2025 10:19:36 +0200 Subject: [PATCH 2501/2760] target/i386: do not expose ARCH_CAPABILITIES on AMD CPU MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM emulates the ARCH_CAPABILITIES on x86 for both Intel and AMD cpus, although the IA32_ARCH_CAPABILITIES MSR is an Intel-specific MSR and it makes no sense to emulate it on AMD. As a consequence, VMs created on AMD with qemu -cpu host and using KVM will advertise the ARCH_CAPABILITIES feature and provide the IA32_ARCH_CAPABILITIES MSR. This can cause issues (like Windows BSOD) as the guest OS might not expect this MSR to exist on such cpus (the AMD documentation specifies that ARCH_CAPABILITIES feature and MSR are not defined on the AMD architecture). A fix was proposed in KVM code, however KVM maintainers don't want to change this behavior that exists for 6+ years and suggest changes to be done in QEMU instead. Therefore, hide the bit from "-cpu host": migration of -cpu host guests is only possible between identical host kernel and QEMU versions, therefore this is not a problematic breakage. If a future AMD machine does include the MSR, that would re-expose the Windows guest bug; but it would not be KVM/QEMU's problem at that point, as we'd be following a genuine physical CPU impl. Reported-by: Alexandre Chartre Suggested-by: Daniel P. Berrangé Reviewed-by: Xiaoyao Li Signed-off-by: Paolo Bonzini --- target/i386/kvm/kvm.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/target/i386/kvm/kvm.c b/target/i386/kvm/kvm.c index e8c8be09ba..369626f8c8 100644 --- a/target/i386/kvm/kvm.c +++ b/target/i386/kvm/kvm.c @@ -503,8 +503,12 @@ uint32_t kvm_arch_get_supported_cpuid(KVMState *s, uint32_t function, * Linux v4.17-v4.20 incorrectly return ARCH_CAPABILITIES on SVM hosts. * We can detect the bug by checking if MSR_IA32_ARCH_CAPABILITIES is * returned by KVM_GET_MSR_INDEX_LIST. + * + * But also, because Windows does not like ARCH_CAPABILITIES on AMD + * mcahines at all, do not show the fake ARCH_CAPABILITIES MSR that + * KVM sets up. */ - if (!has_msr_arch_capabs) { + if (!has_msr_arch_capabs || !(edx & CPUID_7_0_EDX_ARCH_CAPABILITIES)) { ret &= ~CPUID_7_0_EDX_ARCH_CAPABILITIES; } } else if (function == 7 && index == 1 && reg == R_EAX) { From e52af92e9e6f8fc00f2ae6b63214b3d6213b3cec Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Thu, 17 Jul 2025 10:39:33 +0800 Subject: [PATCH 2502/2760] i386/cpu: Move x86_ext_save_areas[] initialization to .instance_init In x86_cpu_post_initfn(), the initialization of x86_ext_save_areas[] marks the unsupported xsave areas based on Host support. This step must be done before accel_cpu_instance_init(), otherwise, KVM's assertion on host xsave support would fail: qemu-system-x86_64: ../target/i386/kvm/kvm-cpu.c:149: kvm_cpu_xsave_init: Assertion `esa->size == eax' failed. (on AMD EPYC 7302 16-Core Processor) Move x86_ext_save_areas[] initialization to .instance_init and place it before accel_cpu_instance_init(). Fixes: commit 5f158abef44c ("target/i386: move accel_cpu_instance_init to .instance_init") Reported-by: Paolo Abeni Tested-by: Paolo Abeni Signed-off-by: Zhao Liu Link: https://lore.kernel.org/r/20250717023933.2502109-1-zhao1.liu@intel.com Reviewed-by: Xiaoyao Li Signed-off-by: Paolo Bonzini --- target/i386/cpu.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index da7d8dca63..251d5760a0 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -9619,6 +9619,16 @@ static void x86_cpu_register_feature_bit_props(X86CPUClass *xcc, } static void x86_cpu_post_initfn(Object *obj) +{ +#ifndef CONFIG_USER_ONLY + if (current_machine && current_machine->cgs) { + x86_confidential_guest_cpu_instance_init( + X86_CONFIDENTIAL_GUEST(current_machine->cgs), (CPU(obj))); + } +#endif +} + +static void x86_cpu_init_xsave(void) { static bool first = true; uint64_t supported_xcr0; @@ -9639,13 +9649,6 @@ static void x86_cpu_post_initfn(Object *obj) } } } - -#ifndef CONFIG_USER_ONLY - if (current_machine && current_machine->cgs) { - x86_confidential_guest_cpu_instance_init( - X86_CONFIDENTIAL_GUEST(current_machine->cgs), (CPU(obj))); - } -#endif } static void x86_cpu_init_default_topo(X86CPU *cpu) @@ -9715,6 +9718,11 @@ static void x86_cpu_initfn(Object *obj) x86_cpu_load_model(cpu, xcc->model); } + /* + * accel's cpu_instance_init may have the xsave check, + * so x86_ext_save_areas[] must be initialized before this. + */ + x86_cpu_init_xsave(); accel_cpu_instance_init(CPU(obj)); } From e00cb2189a920bcfb428622c5d112469c4a094d1 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Thu, 17 Jul 2025 15:12:56 +0200 Subject: [PATCH 2503/2760] meson: re-generate scripts/meson-buildoptions.sh to fix IGVM entry MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 84fe49d94a ("meson: Add optional dependency on IGVM library") was inconsistent with the contents of meson_options.txt and the one generated in scripts/meson-buildoptions.sh Let's regenerate the file in this way to keep them consistent and prevent future changes from including the spurious diff: touch meson_options.txt make update-buildoptions Fixes: 84fe49d94a ("meson: Add optional dependency on IGVM library") Cc: roy.hopkins@randomman.co.uk Reported-by: Daniel P. Berrangé Signed-off-by: Stefano Garzarella Link: https://lore.kernel.org/r/20250717131256.157383-1-sgarzare@redhat.com Signed-off-by: Paolo Bonzini --- scripts/meson-buildoptions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index e8504689e8..0ebe6bc52a 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -130,7 +130,7 @@ meson_options_help() { printf "%s\n" ' hv-balloon hv-balloon driver (requires Glib 2.68+ GTree API)' printf "%s\n" ' hvf HVF acceleration support' printf "%s\n" ' iconv Font glyph conversion support' - printf "%s\n" ' igvm IGVM file support' + printf "%s\n" ' igvm Independent Guest Virtual Machine (IGVM) file support' printf "%s\n" ' jack JACK sound support' printf "%s\n" ' keyring Linux keyring support' printf "%s\n" ' kvm KVM acceleration support' From f2b787976342a9e1d47810f3146ad74b86a5088a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 17 Jul 2025 12:32:23 +0200 Subject: [PATCH 2504/2760] target/i386: tdx: fix locking for interrupt injection Take tdx_guest->lock when injecting the event notification interrupt into the guest. Fixes CID 1612364. Reported-by: Peter Maydell Cc: Xiaoyao Li Reviewed-by: Xiaoyao Li Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 7d69d6d7b0..1574e7d76f 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -1126,10 +1126,15 @@ int tdx_parse_tdvf(void *flash_ptr, int size) return tdvf_parse_metadata(&tdx_guest->tdvf, flash_ptr, size); } -static void tdx_inject_interrupt(uint32_t apicid, uint32_t vector) +static void tdx_inject_interrupt(TdxGuest *tdx) { int ret; + uint32_t apicid, vector; + qemu_mutex_lock(&tdx->lock); + vector = tdx->event_notify_vector; + apicid = tdx->event_notify_apicid; + qemu_mutex_unlock(&tdx->lock); if (vector < 32 || vector > 255) { return; } @@ -1179,8 +1184,7 @@ static void tdx_get_quote_completion(TdxGenerateQuoteTask *task) error_report("TDX: get-quote: failed to update GetQuote header."); } - tdx_inject_interrupt(tdx_guest->event_notify_apicid, - tdx_guest->event_notify_vector); + tdx_inject_interrupt(tdx); g_free(task->send_data); g_free(task->receive_buf); From 5fe6b9a854a91df86fdb794cbeb67d0656756137 Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Wed, 16 Jul 2025 14:31:16 +0800 Subject: [PATCH 2505/2760] i386/cpu: Cleanup host_cpu_max_instance_init() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation of host_cpu_max_instance_init() was merged into host_cpu_instance_init() by commit 29f1ba338baf ("target/i386: merge host_cpu_instance_init() and host_cpu_max_instance_init()"), while the declaration of it remains in host-cpu.h. Clean it up. Signed-off-by: Xiaoyao Li Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Zhao Liu Link: https://lore.kernel.org/r/20250716063117.602050-1-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/host-cpu.h | 1 - 1 file changed, 1 deletion(-) diff --git a/target/i386/host-cpu.h b/target/i386/host-cpu.h index 10df4b3a3a..ee65324225 100644 --- a/target/i386/host-cpu.h +++ b/target/i386/host-cpu.h @@ -12,7 +12,6 @@ uint32_t host_cpu_phys_bits(void); void host_cpu_instance_init(X86CPU *cpu); -void host_cpu_max_instance_init(X86CPU *cpu); bool host_cpu_realizefn(CPUState *cs, Error **errp); void host_cpu_vendor_fms(char *vendor, int *family, int *model, int *stepping); From f64832033d1262983bfe759669b4f65080f760dc Mon Sep 17 00:00:00 2001 From: Xiaoyao Li Date: Thu, 17 Jul 2025 18:37:07 +0800 Subject: [PATCH 2506/2760] i386/tdx: Remove the redundant qemu_mutex_init(&tdx->lock) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 40da501d8989 ("i386/tdx: handle TDG.VP.VMCALL") added redundant qemu_mutex_init(&tdx->lock) in tdx_guest_init by mistake. Fix it by removing the redundant one. Fixes: 40da501d8989 ("i386/tdx: handle TDG.VP.VMCALL") Reported-by: Peter Maydell Signed-off-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/r/20250717103707.688929-1-xiaoyao.li@intel.com Signed-off-by: Paolo Bonzini --- target/i386/kvm/tdx.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/target/i386/kvm/tdx.c b/target/i386/kvm/tdx.c index 1574e7d76f..dbf0fa2c91 100644 --- a/target/i386/kvm/tdx.c +++ b/target/i386/kvm/tdx.c @@ -1527,8 +1527,6 @@ static void tdx_guest_init(Object *obj) tdx_guest_set_qgs, NULL, NULL); - qemu_mutex_init(&tdx->lock); - tdx->event_notify_vector = -1; tdx->event_notify_apicid = -1; } From b53a3bba5e02df7cbdb26f8bf8bcb11b8290e863 Mon Sep 17 00:00:00 2001 From: Shaoqin Huang Date: Thu, 17 Jul 2025 06:09:40 -0400 Subject: [PATCH 2507/2760] vfio: Move the TYPE_* to hw/vfio/types.h Move the TYPE_* to a new file hw/vfio/types.h because the TYPE_VFIO_PCI will be used in later patch, but directly include the hw/vfio/pci.h can cause some compilation error when cross build the windows version. The hw/vfio/types.h can be included to mitigate that problem. Signed-off-by: Shaoqin Huang Message-ID: <20250717100941.2230408-3-shahuang@redhat.com> Reviewed-by: Zhao Liu Signed-off-by: Gerd Hoffmann --- hw/vfio/pci.h | 10 +--------- hw/vfio/types.h | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 9 deletions(-) create mode 100644 hw/vfio/types.h diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 826db8c131..0fd151c5dc 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -14,6 +14,7 @@ #include "system/memory.h" #include "hw/pci/pci_device.h" +#include "hw/vfio/types.h" #include "hw/vfio/vfio-device.h" #include "hw/vfio/vfio-region.h" #include "qemu/event_notifier.h" @@ -119,17 +120,8 @@ typedef struct VFIOMSIXInfo { MemoryRegion *pba_region; } VFIOMSIXInfo; -/* - * TYPE_VFIO_PCI_BASE is an abstract type used to share code - * between VFIO implementations that use a kernel driver - * with those that use user sockets. - */ -#define TYPE_VFIO_PCI_BASE "vfio-pci-base" OBJECT_DECLARE_SIMPLE_TYPE(VFIOPCIDevice, VFIO_PCI_BASE) -#define TYPE_VFIO_PCI "vfio-pci" -/* TYPE_VFIO_PCI shares struct VFIOPCIDevice. */ - struct VFIOPCIDevice { PCIDevice pdev; VFIODevice vbasedev; diff --git a/hw/vfio/types.h b/hw/vfio/types.h new file mode 100644 index 0000000000..fa20c29b9f --- /dev/null +++ b/hw/vfio/types.h @@ -0,0 +1,21 @@ +/* + * VFIO types definition + * + * Copyright Red Hat, Inc. 2025 + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ +#ifndef HW_VFIO_VFIO_TYPES_H +#define HW_VFIO_VFIO_TYPES_H + +/* + * TYPE_VFIO_PCI_BASE is an abstract type used to share code + * between VFIO implementations that use a kernel driver + * with those that use user sockets. + */ +#define TYPE_VFIO_PCI_BASE "vfio-pci-base" + +#define TYPE_VFIO_PCI "vfio-pci" +/* TYPE_VFIO_PCI shares struct VFIOPCIDevice. */ + +#endif /* HW_VFIO_VFIO_TYPES_H */ From d5fcf0d960d893b1765e6388cefca9c690839267 Mon Sep 17 00:00:00 2001 From: Shaoqin Huang Date: Thu, 17 Jul 2025 06:09:41 -0400 Subject: [PATCH 2508/2760] hw/i386: Add the ramfb romfile compatibility ramfb is a sysbus device so it can only used for machine types where it is explicitly enabled: # git grep machine_class_allow_dynamic_sysbus_dev.*TYPE_RAMFB_DEVICE hw/arm/virt.c: machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); hw/i386/microvm.c: machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); hw/i386/pc_piix.c: machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); hw/i386/pc_q35.c: machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); hw/loongarch/virt.c: machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); hw/riscv/virt.c: machine_class_allow_dynamic_sysbus_dev(mc, TYPE_RAMFB_DEVICE); So these six are the only machine types we have to worry about. The three x86 machine types (pc, q35, microvm) will actually use the rom (when booting with seabios). For arm/riscv/loongarch virt we want to disable the rom. This patch sets ramfb romfile option to false by default, except for x86 machines types (pc, q35, microvm) which need the rom file when booting with seabios and machine types <= 10.0 (handling the case of arm virt, for compat reasons). At the same time, set the "use-legacy-x86-rom" property to true on those historical versioned machine types in order to avoid the memory layout being changed. Acked-by: Michael S. Tsirkin Reviewed-by: Zhao Liu Reviewed-by: Eric Auger Signed-off-by: Shaoqin Huang Message-ID: <20250717100941.2230408-4-shahuang@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/core/machine.c | 2 ++ hw/display/ramfb-standalone.c | 2 +- hw/i386/microvm.c | 3 +++ hw/i386/pc_piix.c | 10 ++++++++++ hw/i386/pc_q35.c | 3 +++ hw/vfio/pci.c | 2 +- 6 files changed, 20 insertions(+), 2 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index e869821b22..a7043e2a34 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -39,6 +39,8 @@ GlobalProperty hw_compat_10_0[] = { { "scsi-hd", "dpofua", "off" }, + { "ramfb", "use-legacy-x86-rom", "true"}, + { "vfio-pci", "use-legacy-x86-rom", "true" }, }; const size_t hw_compat_10_0_len = G_N_ELEMENTS(hw_compat_10_0); diff --git a/hw/display/ramfb-standalone.c b/hw/display/ramfb-standalone.c index 82d8c69f89..72b2071aed 100644 --- a/hw/display/ramfb-standalone.c +++ b/hw/display/ramfb-standalone.c @@ -64,7 +64,7 @@ static const VMStateDescription ramfb_dev_vmstate = { static const Property ramfb_properties[] = { DEFINE_PROP_BOOL("x-migrate", RAMFBStandaloneState, migrate, true), DEFINE_PROP_BOOL("use-legacy-x86-rom", RAMFBStandaloneState, - use_legacy_x86_rom, true), + use_legacy_x86_rom, false), }; static void ramfb_class_initfn(ObjectClass *klass, const void *data) diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index e0daf0d4fc..d90b69a162 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -49,6 +49,7 @@ #include "hw/acpi/generic_event_device.h" #include "hw/pci-host/gpex.h" #include "hw/usb/xhci.h" +#include "hw/vfio/types.h" #include "elf.h" #include "kvm/kvm_i386.h" @@ -633,6 +634,8 @@ GlobalProperty microvm_properties[] = { * so reserving io space is not going to work. Turn it off. */ { "pcie-root-port", "io-reserve", "0" }, + { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, + { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" }, }; static void microvm_class_init(ObjectClass *oc, const void *data) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index a3285fbc64..ad5caff3a5 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -49,6 +49,7 @@ #include "hw/i2c/smbus_eeprom.h" #include "system/memory.h" #include "hw/acpi/acpi.h" +#include "hw/vfio/types.h" #include "qapi/error.h" #include "qemu/error-report.h" #include "system/xen.h" @@ -77,6 +78,13 @@ static const int ide_iobase2[MAX_IDE_BUS] = { 0x3f6, 0x376 }; static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; #endif +static GlobalProperty pc_piix_compat_defaults[] = { + { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, + { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" }, +}; +static const size_t pc_piix_compat_defaults_len = + G_N_ELEMENTS(pc_piix_compat_defaults); + /* * Return the global irq number corresponding to a given device irq * pin. We could also use the bus number to have a more precise mapping. @@ -492,6 +500,8 @@ static void pc_i440fx_machine_options(MachineClass *m) pc_set_south_bridge); object_class_property_set_description(oc, "x-south-bridge", "Use a different south bridge than PIIX3"); + compat_props_add(m->compat_props, + pc_piix_compat_defaults, pc_piix_compat_defaults_len); } static void pc_i440fx_machine_10_1_options(MachineClass *m) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index cf871cfdad..9b9519fa02 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -45,6 +45,7 @@ #include "hw/i386/pc.h" #include "hw/i386/amd_iommu.h" #include "hw/i386/intel_iommu.h" +#include "hw/vfio/types.h" #include "hw/virtio/virtio-iommu.h" #include "hw/display/ramfb.h" #include "hw/ide/pci.h" @@ -67,6 +68,8 @@ static GlobalProperty pc_q35_compat_defaults[] = { { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "39" }, + { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, + { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" }, }; static const size_t pc_q35_compat_defaults_len = G_N_ELEMENTS(pc_q35_compat_defaults); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 0b969b3359..174499ecec 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3810,7 +3810,7 @@ static const TypeInfo vfio_pci_dev_info = { static const Property vfio_pci_dev_nohotplug_properties[] = { DEFINE_PROP_BOOL("ramfb", VFIOPCIDevice, enable_ramfb, false), DEFINE_PROP_BOOL("use-legacy-x86-rom", VFIOPCIDevice, - use_legacy_x86_rom, true), + use_legacy_x86_rom, false), DEFINE_PROP_ON_OFF_AUTO("x-ramfb-migrate", VFIOPCIDevice, ramfb_migrate, ON_OFF_AUTO_AUTO), }; From 3f9f6299a1bc46ff462142c7398a5953e3640cc2 Mon Sep 17 00:00:00 2001 From: Vladimir Sementsov-Ogievskiy Date: Wed, 16 Jul 2025 10:28:53 +0300 Subject: [PATCH 2509/2760] net/tap: drop too small packets Theoretically tap_read_packet() may return size less than s->host_vnet_hdr_len, and next, we'll work with negative size (in case of !s->using_vnet_hdr). Let's avoid it. Don't proceed with size == s->host_vnet_hdr_len as well in case of !s->using_vnet_hdr, it doesn't make sense. Tested-by: Lei Yang Signed-off-by: Vladimir Sementsov-Ogievskiy Signed-off-by: Jason Wang --- net/tap.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/net/tap.c b/net/tap.c index 23536c09b4..2a85936019 100644 --- a/net/tap.c +++ b/net/tap.c @@ -190,6 +190,11 @@ static void tap_send(void *opaque) break; } + if (s->host_vnet_hdr_len && size <= s->host_vnet_hdr_len) { + /* Invalid packet */ + break; + } + if (s->host_vnet_hdr_len && !s->using_vnet_hdr) { buf += s->host_vnet_hdr_len; size -= s->host_vnet_hdr_len; From ea263e8fd9a8f9cff615631a9bda775f5ddd3f98 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Mon, 14 Jul 2025 13:36:54 -0700 Subject: [PATCH 2510/2760] tap: fix net_init_tap() return code net_init_tap intends to return 0 for success and -1 on error. However, when net_init_tap() succeeds for a multi-queue device, it returns 1, because of this code where ret becomes 1 when g_unix_set_fd_nonblocking succeeds: ret = g_unix_set_fd_nonblocking(fd, true, NULL); if (!ret) { ... error ... free_fail: ... return ret; Luckily, the only current call site checks for negative, rather than non-zero: net_client_init1() if (net_client_init_fun[](...) < 0) Also, in the unlikely case that g_unix_set_fd_nonblocking fails and returns false, ret=0 is returned, and net_client_init1 will use a broken interface. Fix it to be future proof. Signed-off-by: Steve Sistare Signed-off-by: Jason Wang --- net/tap.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/net/tap.c b/net/tap.c index 2a85936019..f7df702f97 100644 --- a/net/tap.c +++ b/net/tap.c @@ -895,8 +895,8 @@ int net_init_tap(const Netdev *netdev, const char *name, goto free_fail; } - ret = g_unix_set_fd_nonblocking(fd, true, NULL); - if (!ret) { + if (!g_unix_set_fd_nonblocking(fd, true, NULL)) { + ret = -1; error_setg_errno(errp, errno, "%s: Can't use file descriptor %d", name, fd); goto free_fail; From 871a6e5b339f0b5e71925ec7d3f452944a1c82d3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 14 Jul 2025 17:55:20 +0100 Subject: [PATCH 2511/2760] hw/net/npcm_gmac.c: Send the right data for second packet in a row The transmit loop in gmac_try_send_next_packet() is constructed in a way that means it will send incorrect data if it it sends more than one packet. The function assembles the outbound data in a dynamically allocated block of memory which is pointed to by tx_send_buffer. We track the first point in this block of memory which is not yet used with the prev_buf_size offset, initially zero. We track the size of the packet we're sending with the length variable, also initially zero. As we read chunks of data out of guest memory, we write them to tx_send_buffer[prev_buf_size], and then increment both prev_buf_size and length. (We might dynamically reallocate the buffer if needed.) When we send a packet, we checksum and send length bytes, starting at tx_send_buffer, and then we reset length to 0. This gives the right data for the first packet. But we don't reset prev_buf_size. This means that if we process more descriptors with further data for the next packet, that data will continue to accumulate at offset prev_buf_size, i.e. after the data for the first packet. But when we transmit that second packet, we send length bytes from tx_send_buffer, so we will send a packet which has the length of the second packet but the data of the first one. The fix for this is to also clear prev_buf_size after the packet has been sent -- we never need the data from packet one after we've sent it, so we can write packet two's data starting at the beginning of the buffer. Cc: qemu-stable@nongnu.org Signed-off-by: Peter Maydell Signed-off-by: Jason Wang --- hw/net/npcm_gmac.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index a434112580..921327dd8c 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -615,6 +615,7 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) trace_npcm_gmac_packet_sent(DEVICE(gmac)->canonical_path, length); buf = tx_send_buffer; length = 0; + prev_buf_size = 0; } /* step 6 */ From 01b327c9a609cb613da1e04c5af5f4fcc7d9c0a3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 14 Jul 2025 17:55:21 +0100 Subject: [PATCH 2512/2760] hw/net/npcm_gmac.c: Unify length and prev_buf_size variables After the bug fix in the previous commit, the length and prev_buf_size variables are identical, except that prev_buf_size is uint32_t and length is uint16_t. We can therefore unify them. The only place where the type makes a difference is that we will truncate the packet at 64K when sending it; this commit preserves that behaviour by using a local variable when doing the packet send. Signed-off-by: Peter Maydell Signed-off-by: Jason Wang --- hw/net/npcm_gmac.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index 921327dd8c..a0050a7725 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -516,7 +516,6 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) uint32_t desc_addr; struct NPCMGMACTxDesc tx_desc; uint32_t tx_buf_addr, tx_buf_len; - uint16_t length = 0; uint8_t *buf = tx_send_buffer; uint32_t prev_buf_size = 0; int csum = 0; @@ -583,7 +582,6 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) __func__, tx_buf_addr); return; } - length += tx_buf_len; prev_buf_size += tx_buf_len; /* If not chained we'll have a second buffer. */ @@ -606,15 +604,18 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) __func__, tx_buf_addr); return; } - length += tx_buf_len; prev_buf_size += tx_buf_len; } if (tx_desc.tdes1 & TX_DESC_TDES1_LAST_SEG_MASK) { + /* + * This will truncate the packet at 64K. + * TODO: find out if this is the correct behaviour. + */ + uint16_t length = prev_buf_size; net_checksum_calculate(tx_send_buffer, length, csum); qemu_send_packet(qemu_get_queue(gmac->nic), tx_send_buffer, length); trace_npcm_gmac_packet_sent(DEVICE(gmac)->canonical_path, length); buf = tx_send_buffer; - length = 0; prev_buf_size = 0; } From e96a1aa6766f1b188dada3ad8d172c0fbe2f3a5f Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 14 Jul 2025 17:55:22 +0100 Subject: [PATCH 2513/2760] hw/net/npcm_gmac.c: Correct test for when to reallocate packet buffer In gmac_try_send_next_packet() we have code that does "if this block of data won't fit in the buffer, reallocate it". However, the condition it uses is if ((prev_buf_size + tx_buf_len) > sizeof(buf)) where buf is a uint8_t *. This means that sizeof(buf) is always 8 bytes, and the condition will almost always be true, so we will reallocate the buffer more often than we need to. Correct the condition to test against tx_buffer_size, which is where we track how big the allocated buffer is. Signed-off-by: Peter Maydell Signed-off-by: Jason Wang --- hw/net/npcm_gmac.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index a0050a7725..0c17ae9b2a 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -569,7 +569,7 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) tx_buf_len = TX_DESC_TDES1_BFFR1_SZ_MASK(tx_desc.tdes1); buf = &tx_send_buffer[prev_buf_size]; - if ((prev_buf_size + tx_buf_len) > sizeof(buf)) { + if ((prev_buf_size + tx_buf_len) > tx_buffer_size) { tx_buffer_size = prev_buf_size + tx_buf_len; tx_send_buffer = g_realloc(tx_send_buffer, tx_buffer_size); buf = &tx_send_buffer[prev_buf_size]; @@ -591,7 +591,7 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) tx_buf_len = TX_DESC_TDES1_BFFR2_SZ_MASK(tx_desc.tdes1); buf = &tx_send_buffer[prev_buf_size]; - if ((prev_buf_size + tx_buf_len) > sizeof(buf)) { + if ((prev_buf_size + tx_buf_len) > tx_buffer_size) { tx_buffer_size = prev_buf_size + tx_buf_len; tx_send_buffer = g_realloc(tx_send_buffer, tx_buffer_size); buf = &tx_send_buffer[prev_buf_size]; From a6d6e37901551cbd61b0a61c6bab5745fb1833ff Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 14 Jul 2025 17:55:23 +0100 Subject: [PATCH 2514/2760] hw/net/npcm_gmac.c: Drop 'buf' local variable We use the local variable 'buf' only when we call dma_memory_read(), and it is always set to &tx_send_buffer[prev_buf_size] immediately before both of those calls. So remove the variable and pass tx_send_buffer + prev_buf_size to dma_memory_read(). This fixes in passing a place where we set buf = tx_send_buffer but never used that value because we always updated buf to something else later before using it. Coverity: CID 1534027 Signed-off-by: Peter Maydell Signed-off-by: Jason Wang --- hw/net/npcm_gmac.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/hw/net/npcm_gmac.c b/hw/net/npcm_gmac.c index 0c17ae9b2a..5e32cd3edf 100644 --- a/hw/net/npcm_gmac.c +++ b/hw/net/npcm_gmac.c @@ -516,7 +516,6 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) uint32_t desc_addr; struct NPCMGMACTxDesc tx_desc; uint32_t tx_buf_addr, tx_buf_len; - uint8_t *buf = tx_send_buffer; uint32_t prev_buf_size = 0; int csum = 0; @@ -567,16 +566,15 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) tx_buf_addr = tx_desc.tdes2; gmac->regs[R_NPCM_DMA_CUR_TX_BUF_ADDR] = tx_buf_addr; tx_buf_len = TX_DESC_TDES1_BFFR1_SZ_MASK(tx_desc.tdes1); - buf = &tx_send_buffer[prev_buf_size]; if ((prev_buf_size + tx_buf_len) > tx_buffer_size) { tx_buffer_size = prev_buf_size + tx_buf_len; tx_send_buffer = g_realloc(tx_send_buffer, tx_buffer_size); - buf = &tx_send_buffer[prev_buf_size]; } /* step 5 */ - if (dma_memory_read(&address_space_memory, tx_buf_addr, buf, + if (dma_memory_read(&address_space_memory, tx_buf_addr, + tx_send_buffer + prev_buf_size, tx_buf_len, MEMTXATTRS_UNSPECIFIED)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read packet @ 0x%x\n", __func__, tx_buf_addr); @@ -589,15 +587,14 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) tx_buf_addr = tx_desc.tdes3; gmac->regs[R_NPCM_DMA_CUR_TX_BUF_ADDR] = tx_buf_addr; tx_buf_len = TX_DESC_TDES1_BFFR2_SZ_MASK(tx_desc.tdes1); - buf = &tx_send_buffer[prev_buf_size]; if ((prev_buf_size + tx_buf_len) > tx_buffer_size) { tx_buffer_size = prev_buf_size + tx_buf_len; tx_send_buffer = g_realloc(tx_send_buffer, tx_buffer_size); - buf = &tx_send_buffer[prev_buf_size]; } - if (dma_memory_read(&address_space_memory, tx_buf_addr, buf, + if (dma_memory_read(&address_space_memory, tx_buf_addr, + tx_send_buffer + prev_buf_size, tx_buf_len, MEMTXATTRS_UNSPECIFIED)) { qemu_log_mask(LOG_GUEST_ERROR, "%s: Failed to read packet @ 0x%x\n", @@ -615,7 +612,6 @@ static void gmac_try_send_next_packet(NPCMGMACState *gmac) net_checksum_calculate(tx_send_buffer, length, csum); qemu_send_packet(qemu_get_queue(gmac->nic), tx_send_buffer, length); trace_npcm_gmac_packet_sent(DEVICE(gmac)->canonical_path, length); - buf = tx_send_buffer; prev_buf_size = 0; } From 2ed74e3c005f236acbf7d85d396a653bfb004a17 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Jul 2025 17:08:00 +0200 Subject: [PATCH 2515/2760] net/passt: Remove unused "err" from passt_vhost_user_event() (CID 1612375) The "err" variable was declared but never used within the passt_vhost_user_event() function. This resulted in a dead code warning (CID 1612375) from Coverity. Remove the unused variable and the associated error block to resolve the issue. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- net/passt.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/passt.c b/net/passt.c index 6f616ba3c2..9cd5b3e6f2 100644 --- a/net/passt.c +++ b/net/passt.c @@ -397,7 +397,6 @@ static int passt_vhost_user_start(NetPasstState *s, VhostUserState *be) static void passt_vhost_user_event(void *opaque, QEMUChrEvent event) { NetPasstState *s = opaque; - Error *err = NULL; switch (event) { case CHR_EVENT_OPENED: @@ -428,10 +427,6 @@ static void passt_vhost_user_event(void *opaque, QEMUChrEvent event) /* Ignore */ break; } - - if (err) { - error_report_err(err); - } } static int net_passt_vhost_user_init(NetPasstState *s, Error **errp) From 37c8b208cc3161c2bcd4e2ff1e1077a64872ced3 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Jul 2025 17:08:01 +0200 Subject: [PATCH 2516/2760] net/vhost-user: Remove unused "err" from net_vhost_user_event() (CID 1612372) The "err" variable was declared but never used within the net_vhost_user_event() function. This resulted in a dead code warning (CID 1612372) from Coverity. Remove the unused variable and the associated error block to resolve the issue. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- net/vhost-user.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/net/vhost-user.c b/net/vhost-user.c index 1c3b8b36f3..cec83e925f 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -329,7 +329,6 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event) NetClientState *ncs[MAX_QUEUE_NUM]; NetVhostUserState *s; Chardev *chr; - Error *err = NULL; int queues; queues = qemu_find_net_clients_except(name, ncs, @@ -375,10 +374,6 @@ static void net_vhost_user_event(void *opaque, QEMUChrEvent event) /* Ignore */ break; } - - if (err) { - error_report_err(err); - } } static int net_vhost_user_init(NetClientState *peer, const char *device, From c40ef7243fdbec816242cb0ded569a61270ea7c3 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Jul 2025 17:08:02 +0200 Subject: [PATCH 2517/2760] net/passt: Remove dead code in passt_vhost_user_start error path (CID 1612371) In passt_vhost_user_start(), if vhost_net_init() fails, the "net" variable is NULL and execution jumps to the "err:" label. The cleanup code within this label is conditioned on "if (net)", which can never be true in this error case. This makes the cleanup block dead code, as reported by Coverity (CID 1612371). Refactor the error handling to occur inline, removing the goto and the unreachable cleanup block. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- net/passt.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/net/passt.c b/net/passt.c index 9cd5b3e6f2..ef59d0682b 100644 --- a/net/passt.c +++ b/net/passt.c @@ -375,7 +375,8 @@ static int passt_vhost_user_start(NetPasstState *s, VhostUserState *be) net = vhost_net_init(&options); if (!net) { error_report("failed to init passt vhost_net"); - goto err; + passt_vhost_user_stop(s); + return -1; } if (s->vhost_net) { @@ -385,13 +386,6 @@ static int passt_vhost_user_start(NetPasstState *s, VhostUserState *be) s->vhost_net = net; return 0; -err: - if (net) { - vhost_net_cleanup(net); - g_free(net); - } - passt_vhost_user_stop(s); - return -1; } static void passt_vhost_user_event(void *opaque, QEMUChrEvent event) From f74e4f2e60c3b2c6e2f13eb115478df116148fe6 Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Jul 2025 17:08:03 +0200 Subject: [PATCH 2518/2760] net/passt: Check return value of g_remove() in net_passt_cleanup() (CID 1612369) If g_remove() fails, use warn_report() to log an error. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- net/passt.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/passt.c b/net/passt.c index ef59d0682b..43c336e596 100644 --- a/net/passt.c +++ b/net/passt.c @@ -103,7 +103,10 @@ static void net_passt_cleanup(NetClientState *nc) #endif kill(s->pid, SIGTERM); - g_remove(s->pidfile); + if (g_remove(s->pidfile) != 0) { + warn_report("Failed to remove passt pidfile %s: %s", + s->pidfile, strerror(errno)); + } g_free(s->pidfile); g_ptr_array_free(s->args, TRUE); } From 667ad57b7636881eb79b0b319951f3cba9bbf27e Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Jul 2025 17:08:04 +0200 Subject: [PATCH 2519/2760] net/passt: Initialize "error" variable in net_passt_send() (CID 1612368) This was flagged by Coverity as a memory illegal access. Initialize the pointer to NULL at declaration. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- net/passt.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/net/passt.c b/net/passt.c index 43c336e596..32ecffb763 100644 --- a/net/passt.c +++ b/net/passt.c @@ -124,7 +124,7 @@ static gboolean net_passt_send(QIOChannel *ioc, GIOCondition condition, { if (net_stream_data_send(ioc, condition, data) == G_SOURCE_REMOVE) { NetPasstState *s = DO_UPCAST(NetPasstState, data, data); - Error *error; + Error *error = NULL; /* we need to restart passt */ kill(s->pid, SIGTERM); From ae9b09972bbf8ff49ae0edf3241fb413391b15ce Mon Sep 17 00:00:00 2001 From: Laurent Vivier Date: Thu, 17 Jul 2025 17:08:05 +0200 Subject: [PATCH 2520/2760] net/vhost-user: Remove unused "err" from chr_closed_bh() (CID 1612365) The "err" variable was declared but never used within the chr_closed_bh() function. This resulted in a dead code warning (CID 1612365) from Coverity. Remove the unused variable and the associated error block to resolve the issue. Signed-off-by: Laurent Vivier Reviewed-by: Peter Maydell Signed-off-by: Jason Wang --- net/vhost-user.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/net/vhost-user.c b/net/vhost-user.c index cec83e925f..8b96157145 100644 --- a/net/vhost-user.c +++ b/net/vhost-user.c @@ -298,7 +298,6 @@ static void chr_closed_bh(void *opaque) const char *name = opaque; NetClientState *ncs[MAX_QUEUE_NUM]; NetVhostUserState *s; - Error *err = NULL; int queues, i; queues = qemu_find_net_clients_except(name, ncs, @@ -317,9 +316,6 @@ static void chr_closed_bh(void *opaque) qemu_chr_fe_set_handlers(&s->chr, NULL, NULL, net_vhost_user_event, NULL, opaque, NULL, true); - if (err) { - error_report_err(err); - } qapi_event_send_netdev_vhost_user_disconnected(name); } From 99c6e970a4a6cfd862c6bb0a363c28538436de13 Mon Sep 17 00:00:00 2001 From: Thomas Huth Date: Thu, 10 Jul 2025 14:00:35 +0200 Subject: [PATCH 2521/2760] linux-headers: Remove the 32-bit arm headers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit KVM support for 32-bit arm has been dropped a while ago, so we don't need these headers in QEMU anymore. Fixes: 82bf7ae84ce ("target/arm: Remove KVM support for 32-bit Arm hosts") Acked-by: Cornelia Huck Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Thomas Huth Message-ID: <20250710120035.169376-1-thuth@redhat.com> --- configure | 1 - linux-headers/asm-arm/bitsperlong.h | 1 - linux-headers/asm-arm/kvm.h | 312 -------------------- linux-headers/asm-arm/mman.h | 4 - linux-headers/asm-arm/unistd-common.h | 397 -------------------------- linux-headers/asm-arm/unistd-eabi.h | 5 - linux-headers/asm-arm/unistd-oabi.h | 17 -- linux-headers/asm-arm/unistd.h | 41 --- scripts/update-linux-headers.sh | 5 - 9 files changed, 783 deletions(-) delete mode 100644 linux-headers/asm-arm/bitsperlong.h delete mode 100644 linux-headers/asm-arm/kvm.h delete mode 100644 linux-headers/asm-arm/mman.h delete mode 100644 linux-headers/asm-arm/unistd-common.h delete mode 100644 linux-headers/asm-arm/unistd-eabi.h delete mode 100644 linux-headers/asm-arm/unistd-oabi.h delete mode 100644 linux-headers/asm-arm/unistd.h diff --git a/configure b/configure index 2b2b3d6597..95f67c1a82 100755 --- a/configure +++ b/configure @@ -453,7 +453,6 @@ case "$cpu" in armv*b|armv*l|arm) cpu=arm host_arch=arm - linux_arch=arm ;; i386|i486|i586|i686) diff --git a/linux-headers/asm-arm/bitsperlong.h b/linux-headers/asm-arm/bitsperlong.h deleted file mode 100644 index 6dc0bb0c13..0000000000 --- a/linux-headers/asm-arm/bitsperlong.h +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/linux-headers/asm-arm/kvm.h b/linux-headers/asm-arm/kvm.h deleted file mode 100644 index 0db5644e27..0000000000 --- a/linux-headers/asm-arm/kvm.h +++ /dev/null @@ -1,312 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * Copyright (C) 2012 - Virtual Open Systems and Columbia University - * Author: Christoffer Dall - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License, version 2, as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __ARM_KVM_H__ -#define __ARM_KVM_H__ - -#include -#include -#include - -#define __KVM_HAVE_GUEST_DEBUG -#define __KVM_HAVE_IRQ_LINE -#define __KVM_HAVE_READONLY_MEM -#define __KVM_HAVE_VCPU_EVENTS - -#define KVM_COALESCED_MMIO_PAGE_OFFSET 1 - -#define KVM_REG_SIZE(id) \ - (1U << (((id) & KVM_REG_SIZE_MASK) >> KVM_REG_SIZE_SHIFT)) - -/* Valid for svc_regs, abt_regs, und_regs, irq_regs in struct kvm_regs */ -#define KVM_ARM_SVC_sp svc_regs[0] -#define KVM_ARM_SVC_lr svc_regs[1] -#define KVM_ARM_SVC_spsr svc_regs[2] -#define KVM_ARM_ABT_sp abt_regs[0] -#define KVM_ARM_ABT_lr abt_regs[1] -#define KVM_ARM_ABT_spsr abt_regs[2] -#define KVM_ARM_UND_sp und_regs[0] -#define KVM_ARM_UND_lr und_regs[1] -#define KVM_ARM_UND_spsr und_regs[2] -#define KVM_ARM_IRQ_sp irq_regs[0] -#define KVM_ARM_IRQ_lr irq_regs[1] -#define KVM_ARM_IRQ_spsr irq_regs[2] - -/* Valid only for fiq_regs in struct kvm_regs */ -#define KVM_ARM_FIQ_r8 fiq_regs[0] -#define KVM_ARM_FIQ_r9 fiq_regs[1] -#define KVM_ARM_FIQ_r10 fiq_regs[2] -#define KVM_ARM_FIQ_fp fiq_regs[3] -#define KVM_ARM_FIQ_ip fiq_regs[4] -#define KVM_ARM_FIQ_sp fiq_regs[5] -#define KVM_ARM_FIQ_lr fiq_regs[6] -#define KVM_ARM_FIQ_spsr fiq_regs[7] - -struct kvm_regs { - struct pt_regs usr_regs; /* R0_usr - R14_usr, PC, CPSR */ - unsigned long svc_regs[3]; /* SP_svc, LR_svc, SPSR_svc */ - unsigned long abt_regs[3]; /* SP_abt, LR_abt, SPSR_abt */ - unsigned long und_regs[3]; /* SP_und, LR_und, SPSR_und */ - unsigned long irq_regs[3]; /* SP_irq, LR_irq, SPSR_irq */ - unsigned long fiq_regs[8]; /* R8_fiq - R14_fiq, SPSR_fiq */ -}; - -/* Supported Processor Types */ -#define KVM_ARM_TARGET_CORTEX_A15 0 -#define KVM_ARM_TARGET_CORTEX_A7 1 -#define KVM_ARM_NUM_TARGETS 2 - -/* KVM_ARM_SET_DEVICE_ADDR ioctl id encoding */ -#define KVM_ARM_DEVICE_TYPE_SHIFT 0 -#define KVM_ARM_DEVICE_TYPE_MASK (0xffff << KVM_ARM_DEVICE_TYPE_SHIFT) -#define KVM_ARM_DEVICE_ID_SHIFT 16 -#define KVM_ARM_DEVICE_ID_MASK (0xffff << KVM_ARM_DEVICE_ID_SHIFT) - -/* Supported device IDs */ -#define KVM_ARM_DEVICE_VGIC_V2 0 - -/* Supported VGIC address types */ -#define KVM_VGIC_V2_ADDR_TYPE_DIST 0 -#define KVM_VGIC_V2_ADDR_TYPE_CPU 1 - -#define KVM_VGIC_V2_DIST_SIZE 0x1000 -#define KVM_VGIC_V2_CPU_SIZE 0x2000 - -/* Supported VGICv3 address types */ -#define KVM_VGIC_V3_ADDR_TYPE_DIST 2 -#define KVM_VGIC_V3_ADDR_TYPE_REDIST 3 -#define KVM_VGIC_ITS_ADDR_TYPE 4 -#define KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION 5 - -#define KVM_VGIC_V3_DIST_SIZE SZ_64K -#define KVM_VGIC_V3_REDIST_SIZE (2 * SZ_64K) -#define KVM_VGIC_V3_ITS_SIZE (2 * SZ_64K) - -#define KVM_ARM_VCPU_POWER_OFF 0 /* CPU is started in OFF state */ -#define KVM_ARM_VCPU_PSCI_0_2 1 /* CPU uses PSCI v0.2 */ - -struct kvm_vcpu_init { - __u32 target; - __u32 features[7]; -}; - -struct kvm_sregs { -}; - -struct kvm_fpu { -}; - -struct kvm_guest_debug_arch { -}; - -struct kvm_debug_exit_arch { -}; - -struct kvm_sync_regs { - /* Used with KVM_CAP_ARM_USER_IRQ */ - __u64 device_irq_level; -}; - -struct kvm_arch_memory_slot { -}; - -/* for KVM_GET/SET_VCPU_EVENTS */ -struct kvm_vcpu_events { - struct { - __u8 serror_pending; - __u8 serror_has_esr; - __u8 ext_dabt_pending; - /* Align it to 8 bytes */ - __u8 pad[5]; - __u64 serror_esr; - } exception; - __u32 reserved[12]; -}; - -/* If you need to interpret the index values, here is the key: */ -#define KVM_REG_ARM_COPROC_MASK 0x000000000FFF0000 -#define KVM_REG_ARM_COPROC_SHIFT 16 -#define KVM_REG_ARM_32_OPC2_MASK 0x0000000000000007 -#define KVM_REG_ARM_32_OPC2_SHIFT 0 -#define KVM_REG_ARM_OPC1_MASK 0x0000000000000078 -#define KVM_REG_ARM_OPC1_SHIFT 3 -#define KVM_REG_ARM_CRM_MASK 0x0000000000000780 -#define KVM_REG_ARM_CRM_SHIFT 7 -#define KVM_REG_ARM_32_CRN_MASK 0x0000000000007800 -#define KVM_REG_ARM_32_CRN_SHIFT 11 -/* - * For KVM currently all guest registers are nonsecure, but we reserve a bit - * in the encoding to distinguish secure from nonsecure for AArch32 system - * registers that are banked by security. This is 1 for the secure banked - * register, and 0 for the nonsecure banked register or if the register is - * not banked by security. - */ -#define KVM_REG_ARM_SECURE_MASK 0x0000000010000000 -#define KVM_REG_ARM_SECURE_SHIFT 28 - -#define ARM_CP15_REG_SHIFT_MASK(x,n) \ - (((x) << KVM_REG_ARM_ ## n ## _SHIFT) & KVM_REG_ARM_ ## n ## _MASK) - -#define __ARM_CP15_REG(op1,crn,crm,op2) \ - (KVM_REG_ARM | (15 << KVM_REG_ARM_COPROC_SHIFT) | \ - ARM_CP15_REG_SHIFT_MASK(op1, OPC1) | \ - ARM_CP15_REG_SHIFT_MASK(crn, 32_CRN) | \ - ARM_CP15_REG_SHIFT_MASK(crm, CRM) | \ - ARM_CP15_REG_SHIFT_MASK(op2, 32_OPC2)) - -#define ARM_CP15_REG32(...) (__ARM_CP15_REG(__VA_ARGS__) | KVM_REG_SIZE_U32) - -#define __ARM_CP15_REG64(op1,crm) \ - (__ARM_CP15_REG(op1, 0, crm, 0) | KVM_REG_SIZE_U64) -#define ARM_CP15_REG64(...) __ARM_CP15_REG64(__VA_ARGS__) - -/* PL1 Physical Timer Registers */ -#define KVM_REG_ARM_PTIMER_CTL ARM_CP15_REG32(0, 14, 2, 1) -#define KVM_REG_ARM_PTIMER_CNT ARM_CP15_REG64(0, 14) -#define KVM_REG_ARM_PTIMER_CVAL ARM_CP15_REG64(2, 14) - -/* Virtual Timer Registers */ -#define KVM_REG_ARM_TIMER_CTL ARM_CP15_REG32(0, 14, 3, 1) -#define KVM_REG_ARM_TIMER_CNT ARM_CP15_REG64(1, 14) -#define KVM_REG_ARM_TIMER_CVAL ARM_CP15_REG64(3, 14) - -/* Normal registers are mapped as coprocessor 16. */ -#define KVM_REG_ARM_CORE (0x0010 << KVM_REG_ARM_COPROC_SHIFT) -#define KVM_REG_ARM_CORE_REG(name) (offsetof(struct kvm_regs, name) / 4) - -/* Some registers need more space to represent values. */ -#define KVM_REG_ARM_DEMUX (0x0011 << KVM_REG_ARM_COPROC_SHIFT) -#define KVM_REG_ARM_DEMUX_ID_MASK 0x000000000000FF00 -#define KVM_REG_ARM_DEMUX_ID_SHIFT 8 -#define KVM_REG_ARM_DEMUX_ID_CCSIDR (0x00 << KVM_REG_ARM_DEMUX_ID_SHIFT) -#define KVM_REG_ARM_DEMUX_VAL_MASK 0x00000000000000FF -#define KVM_REG_ARM_DEMUX_VAL_SHIFT 0 - -/* VFP registers: we could overload CP10 like ARM does, but that's ugly. */ -#define KVM_REG_ARM_VFP (0x0012 << KVM_REG_ARM_COPROC_SHIFT) -#define KVM_REG_ARM_VFP_MASK 0x000000000000FFFF -#define KVM_REG_ARM_VFP_BASE_REG 0x0 -#define KVM_REG_ARM_VFP_FPSID 0x1000 -#define KVM_REG_ARM_VFP_FPSCR 0x1001 -#define KVM_REG_ARM_VFP_MVFR1 0x1006 -#define KVM_REG_ARM_VFP_MVFR0 0x1007 -#define KVM_REG_ARM_VFP_FPEXC 0x1008 -#define KVM_REG_ARM_VFP_FPINST 0x1009 -#define KVM_REG_ARM_VFP_FPINST2 0x100A - -/* KVM-as-firmware specific pseudo-registers */ -#define KVM_REG_ARM_FW (0x0014 << KVM_REG_ARM_COPROC_SHIFT) -#define KVM_REG_ARM_FW_REG(r) (KVM_REG_ARM | KVM_REG_SIZE_U64 | \ - KVM_REG_ARM_FW | ((r) & 0xffff)) -#define KVM_REG_ARM_PSCI_VERSION KVM_REG_ARM_FW_REG(0) -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1 KVM_REG_ARM_FW_REG(1) - /* Higher values mean better protection. */ -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_AVAIL 0 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_AVAIL 1 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_1_NOT_REQUIRED 2 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2 KVM_REG_ARM_FW_REG(2) - /* Higher values mean better protection. */ -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_AVAIL 0 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_UNKNOWN 1 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_AVAIL 2 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_NOT_REQUIRED 3 -#define KVM_REG_ARM_SMCCC_ARCH_WORKAROUND_2_ENABLED (1U << 4) - -/* Device Control API: ARM VGIC */ -#define KVM_DEV_ARM_VGIC_GRP_ADDR 0 -#define KVM_DEV_ARM_VGIC_GRP_DIST_REGS 1 -#define KVM_DEV_ARM_VGIC_GRP_CPU_REGS 2 -#define KVM_DEV_ARM_VGIC_CPUID_SHIFT 32 -#define KVM_DEV_ARM_VGIC_CPUID_MASK (0xffULL << KVM_DEV_ARM_VGIC_CPUID_SHIFT) -#define KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT 32 -#define KVM_DEV_ARM_VGIC_V3_MPIDR_MASK \ - (0xffffffffULL << KVM_DEV_ARM_VGIC_V3_MPIDR_SHIFT) -#define KVM_DEV_ARM_VGIC_OFFSET_SHIFT 0 -#define KVM_DEV_ARM_VGIC_OFFSET_MASK (0xffffffffULL << KVM_DEV_ARM_VGIC_OFFSET_SHIFT) -#define KVM_DEV_ARM_VGIC_SYSREG_INSTR_MASK (0xffff) -#define KVM_DEV_ARM_VGIC_GRP_NR_IRQS 3 -#define KVM_DEV_ARM_VGIC_GRP_CTRL 4 -#define KVM_DEV_ARM_VGIC_GRP_REDIST_REGS 5 -#define KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS 6 -#define KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO 7 -#define KVM_DEV_ARM_VGIC_GRP_ITS_REGS 8 -#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT 10 -#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_MASK \ - (0x3fffffULL << KVM_DEV_ARM_VGIC_LINE_LEVEL_INFO_SHIFT) -#define KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK 0x3ff -#define VGIC_LEVEL_INFO_LINE_LEVEL 0 - -/* Device Control API on vcpu fd */ -#define KVM_ARM_VCPU_PMU_V3_CTRL 0 -#define KVM_ARM_VCPU_PMU_V3_IRQ 0 -#define KVM_ARM_VCPU_PMU_V3_INIT 1 -#define KVM_ARM_VCPU_TIMER_CTRL 1 -#define KVM_ARM_VCPU_TIMER_IRQ_VTIMER 0 -#define KVM_ARM_VCPU_TIMER_IRQ_PTIMER 1 - -#define KVM_DEV_ARM_VGIC_CTRL_INIT 0 -#define KVM_DEV_ARM_ITS_SAVE_TABLES 1 -#define KVM_DEV_ARM_ITS_RESTORE_TABLES 2 -#define KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES 3 -#define KVM_DEV_ARM_ITS_CTRL_RESET 4 - -/* KVM_IRQ_LINE irq field index values */ -#define KVM_ARM_IRQ_VCPU2_SHIFT 28 -#define KVM_ARM_IRQ_VCPU2_MASK 0xf -#define KVM_ARM_IRQ_TYPE_SHIFT 24 -#define KVM_ARM_IRQ_TYPE_MASK 0xf -#define KVM_ARM_IRQ_VCPU_SHIFT 16 -#define KVM_ARM_IRQ_VCPU_MASK 0xff -#define KVM_ARM_IRQ_NUM_SHIFT 0 -#define KVM_ARM_IRQ_NUM_MASK 0xffff - -/* irq_type field */ -#define KVM_ARM_IRQ_TYPE_CPU 0 -#define KVM_ARM_IRQ_TYPE_SPI 1 -#define KVM_ARM_IRQ_TYPE_PPI 2 - -/* out-of-kernel GIC cpu interrupt injection irq_number field */ -#define KVM_ARM_IRQ_CPU_IRQ 0 -#define KVM_ARM_IRQ_CPU_FIQ 1 - -/* - * This used to hold the highest supported SPI, but it is now obsolete - * and only here to provide source code level compatibility with older - * userland. The highest SPI number can be set via KVM_DEV_ARM_VGIC_GRP_NR_IRQS. - */ -#define KVM_ARM_IRQ_GIC_MAX 127 - -/* One single KVM irqchip, ie. the VGIC */ -#define KVM_NR_IRQCHIPS 1 - -/* PSCI interface */ -#define KVM_PSCI_FN_BASE 0x95c1ba5e -#define KVM_PSCI_FN(n) (KVM_PSCI_FN_BASE + (n)) - -#define KVM_PSCI_FN_CPU_SUSPEND KVM_PSCI_FN(0) -#define KVM_PSCI_FN_CPU_OFF KVM_PSCI_FN(1) -#define KVM_PSCI_FN_CPU_ON KVM_PSCI_FN(2) -#define KVM_PSCI_FN_MIGRATE KVM_PSCI_FN(3) - -#define KVM_PSCI_RET_SUCCESS PSCI_RET_SUCCESS -#define KVM_PSCI_RET_NI PSCI_RET_NOT_SUPPORTED -#define KVM_PSCI_RET_INVAL PSCI_RET_INVALID_PARAMS -#define KVM_PSCI_RET_DENIED PSCI_RET_DENIED - -#endif /* __ARM_KVM_H__ */ diff --git a/linux-headers/asm-arm/mman.h b/linux-headers/asm-arm/mman.h deleted file mode 100644 index 41f99c573b..0000000000 --- a/linux-headers/asm-arm/mman.h +++ /dev/null @@ -1,4 +0,0 @@ -#include - -#define arch_mmap_check(addr, len, flags) \ - (((flags) & MAP_FIXED && (addr) < FIRST_USER_ADDRESS) ? -EINVAL : 0) diff --git a/linux-headers/asm-arm/unistd-common.h b/linux-headers/asm-arm/unistd-common.h deleted file mode 100644 index 57cd1f21db..0000000000 --- a/linux-headers/asm-arm/unistd-common.h +++ /dev/null @@ -1,397 +0,0 @@ -#ifndef _ASM_ARM_UNISTD_COMMON_H -#define _ASM_ARM_UNISTD_COMMON_H 1 - -#define __NR_restart_syscall (__NR_SYSCALL_BASE + 0) -#define __NR_exit (__NR_SYSCALL_BASE + 1) -#define __NR_fork (__NR_SYSCALL_BASE + 2) -#define __NR_read (__NR_SYSCALL_BASE + 3) -#define __NR_write (__NR_SYSCALL_BASE + 4) -#define __NR_open (__NR_SYSCALL_BASE + 5) -#define __NR_close (__NR_SYSCALL_BASE + 6) -#define __NR_creat (__NR_SYSCALL_BASE + 8) -#define __NR_link (__NR_SYSCALL_BASE + 9) -#define __NR_unlink (__NR_SYSCALL_BASE + 10) -#define __NR_execve (__NR_SYSCALL_BASE + 11) -#define __NR_chdir (__NR_SYSCALL_BASE + 12) -#define __NR_mknod (__NR_SYSCALL_BASE + 14) -#define __NR_chmod (__NR_SYSCALL_BASE + 15) -#define __NR_lchown (__NR_SYSCALL_BASE + 16) -#define __NR_lseek (__NR_SYSCALL_BASE + 19) -#define __NR_getpid (__NR_SYSCALL_BASE + 20) -#define __NR_mount (__NR_SYSCALL_BASE + 21) -#define __NR_setuid (__NR_SYSCALL_BASE + 23) -#define __NR_getuid (__NR_SYSCALL_BASE + 24) -#define __NR_ptrace (__NR_SYSCALL_BASE + 26) -#define __NR_pause (__NR_SYSCALL_BASE + 29) -#define __NR_access (__NR_SYSCALL_BASE + 33) -#define __NR_nice (__NR_SYSCALL_BASE + 34) -#define __NR_sync (__NR_SYSCALL_BASE + 36) -#define __NR_kill (__NR_SYSCALL_BASE + 37) -#define __NR_rename (__NR_SYSCALL_BASE + 38) -#define __NR_mkdir (__NR_SYSCALL_BASE + 39) -#define __NR_rmdir (__NR_SYSCALL_BASE + 40) -#define __NR_dup (__NR_SYSCALL_BASE + 41) -#define __NR_pipe (__NR_SYSCALL_BASE + 42) -#define __NR_times (__NR_SYSCALL_BASE + 43) -#define __NR_brk (__NR_SYSCALL_BASE + 45) -#define __NR_setgid (__NR_SYSCALL_BASE + 46) -#define __NR_getgid (__NR_SYSCALL_BASE + 47) -#define __NR_geteuid (__NR_SYSCALL_BASE + 49) -#define __NR_getegid (__NR_SYSCALL_BASE + 50) -#define __NR_acct (__NR_SYSCALL_BASE + 51) -#define __NR_umount2 (__NR_SYSCALL_BASE + 52) -#define __NR_ioctl (__NR_SYSCALL_BASE + 54) -#define __NR_fcntl (__NR_SYSCALL_BASE + 55) -#define __NR_setpgid (__NR_SYSCALL_BASE + 57) -#define __NR_umask (__NR_SYSCALL_BASE + 60) -#define __NR_chroot (__NR_SYSCALL_BASE + 61) -#define __NR_ustat (__NR_SYSCALL_BASE + 62) -#define __NR_dup2 (__NR_SYSCALL_BASE + 63) -#define __NR_getppid (__NR_SYSCALL_BASE + 64) -#define __NR_getpgrp (__NR_SYSCALL_BASE + 65) -#define __NR_setsid (__NR_SYSCALL_BASE + 66) -#define __NR_sigaction (__NR_SYSCALL_BASE + 67) -#define __NR_setreuid (__NR_SYSCALL_BASE + 70) -#define __NR_setregid (__NR_SYSCALL_BASE + 71) -#define __NR_sigsuspend (__NR_SYSCALL_BASE + 72) -#define __NR_sigpending (__NR_SYSCALL_BASE + 73) -#define __NR_sethostname (__NR_SYSCALL_BASE + 74) -#define __NR_setrlimit (__NR_SYSCALL_BASE + 75) -#define __NR_getrusage (__NR_SYSCALL_BASE + 77) -#define __NR_gettimeofday (__NR_SYSCALL_BASE + 78) -#define __NR_settimeofday (__NR_SYSCALL_BASE + 79) -#define __NR_getgroups (__NR_SYSCALL_BASE + 80) -#define __NR_setgroups (__NR_SYSCALL_BASE + 81) -#define __NR_symlink (__NR_SYSCALL_BASE + 83) -#define __NR_readlink (__NR_SYSCALL_BASE + 85) -#define __NR_uselib (__NR_SYSCALL_BASE + 86) -#define __NR_swapon (__NR_SYSCALL_BASE + 87) -#define __NR_reboot (__NR_SYSCALL_BASE + 88) -#define __NR_munmap (__NR_SYSCALL_BASE + 91) -#define __NR_truncate (__NR_SYSCALL_BASE + 92) -#define __NR_ftruncate (__NR_SYSCALL_BASE + 93) -#define __NR_fchmod (__NR_SYSCALL_BASE + 94) -#define __NR_fchown (__NR_SYSCALL_BASE + 95) -#define __NR_getpriority (__NR_SYSCALL_BASE + 96) -#define __NR_setpriority (__NR_SYSCALL_BASE + 97) -#define __NR_statfs (__NR_SYSCALL_BASE + 99) -#define __NR_fstatfs (__NR_SYSCALL_BASE + 100) -#define __NR_syslog (__NR_SYSCALL_BASE + 103) -#define __NR_setitimer (__NR_SYSCALL_BASE + 104) -#define __NR_getitimer (__NR_SYSCALL_BASE + 105) -#define __NR_stat (__NR_SYSCALL_BASE + 106) -#define __NR_lstat (__NR_SYSCALL_BASE + 107) -#define __NR_fstat (__NR_SYSCALL_BASE + 108) -#define __NR_vhangup (__NR_SYSCALL_BASE + 111) -#define __NR_wait4 (__NR_SYSCALL_BASE + 114) -#define __NR_swapoff (__NR_SYSCALL_BASE + 115) -#define __NR_sysinfo (__NR_SYSCALL_BASE + 116) -#define __NR_fsync (__NR_SYSCALL_BASE + 118) -#define __NR_sigreturn (__NR_SYSCALL_BASE + 119) -#define __NR_clone (__NR_SYSCALL_BASE + 120) -#define __NR_setdomainname (__NR_SYSCALL_BASE + 121) -#define __NR_uname (__NR_SYSCALL_BASE + 122) -#define __NR_adjtimex (__NR_SYSCALL_BASE + 124) -#define __NR_mprotect (__NR_SYSCALL_BASE + 125) -#define __NR_sigprocmask (__NR_SYSCALL_BASE + 126) -#define __NR_init_module (__NR_SYSCALL_BASE + 128) -#define __NR_delete_module (__NR_SYSCALL_BASE + 129) -#define __NR_quotactl (__NR_SYSCALL_BASE + 131) -#define __NR_getpgid (__NR_SYSCALL_BASE + 132) -#define __NR_fchdir (__NR_SYSCALL_BASE + 133) -#define __NR_bdflush (__NR_SYSCALL_BASE + 134) -#define __NR_sysfs (__NR_SYSCALL_BASE + 135) -#define __NR_personality (__NR_SYSCALL_BASE + 136) -#define __NR_setfsuid (__NR_SYSCALL_BASE + 138) -#define __NR_setfsgid (__NR_SYSCALL_BASE + 139) -#define __NR__llseek (__NR_SYSCALL_BASE + 140) -#define __NR_getdents (__NR_SYSCALL_BASE + 141) -#define __NR__newselect (__NR_SYSCALL_BASE + 142) -#define __NR_flock (__NR_SYSCALL_BASE + 143) -#define __NR_msync (__NR_SYSCALL_BASE + 144) -#define __NR_readv (__NR_SYSCALL_BASE + 145) -#define __NR_writev (__NR_SYSCALL_BASE + 146) -#define __NR_getsid (__NR_SYSCALL_BASE + 147) -#define __NR_fdatasync (__NR_SYSCALL_BASE + 148) -#define __NR__sysctl (__NR_SYSCALL_BASE + 149) -#define __NR_mlock (__NR_SYSCALL_BASE + 150) -#define __NR_munlock (__NR_SYSCALL_BASE + 151) -#define __NR_mlockall (__NR_SYSCALL_BASE + 152) -#define __NR_munlockall (__NR_SYSCALL_BASE + 153) -#define __NR_sched_setparam (__NR_SYSCALL_BASE + 154) -#define __NR_sched_getparam (__NR_SYSCALL_BASE + 155) -#define __NR_sched_setscheduler (__NR_SYSCALL_BASE + 156) -#define __NR_sched_getscheduler (__NR_SYSCALL_BASE + 157) -#define __NR_sched_yield (__NR_SYSCALL_BASE + 158) -#define __NR_sched_get_priority_max (__NR_SYSCALL_BASE + 159) -#define __NR_sched_get_priority_min (__NR_SYSCALL_BASE + 160) -#define __NR_sched_rr_get_interval (__NR_SYSCALL_BASE + 161) -#define __NR_nanosleep (__NR_SYSCALL_BASE + 162) -#define __NR_mremap (__NR_SYSCALL_BASE + 163) -#define __NR_setresuid (__NR_SYSCALL_BASE + 164) -#define __NR_getresuid (__NR_SYSCALL_BASE + 165) -#define __NR_poll (__NR_SYSCALL_BASE + 168) -#define __NR_nfsservctl (__NR_SYSCALL_BASE + 169) -#define __NR_setresgid (__NR_SYSCALL_BASE + 170) -#define __NR_getresgid (__NR_SYSCALL_BASE + 171) -#define __NR_prctl (__NR_SYSCALL_BASE + 172) -#define __NR_rt_sigreturn (__NR_SYSCALL_BASE + 173) -#define __NR_rt_sigaction (__NR_SYSCALL_BASE + 174) -#define __NR_rt_sigprocmask (__NR_SYSCALL_BASE + 175) -#define __NR_rt_sigpending (__NR_SYSCALL_BASE + 176) -#define __NR_rt_sigtimedwait (__NR_SYSCALL_BASE + 177) -#define __NR_rt_sigqueueinfo (__NR_SYSCALL_BASE + 178) -#define __NR_rt_sigsuspend (__NR_SYSCALL_BASE + 179) -#define __NR_pread64 (__NR_SYSCALL_BASE + 180) -#define __NR_pwrite64 (__NR_SYSCALL_BASE + 181) -#define __NR_chown (__NR_SYSCALL_BASE + 182) -#define __NR_getcwd (__NR_SYSCALL_BASE + 183) -#define __NR_capget (__NR_SYSCALL_BASE + 184) -#define __NR_capset (__NR_SYSCALL_BASE + 185) -#define __NR_sigaltstack (__NR_SYSCALL_BASE + 186) -#define __NR_sendfile (__NR_SYSCALL_BASE + 187) -#define __NR_vfork (__NR_SYSCALL_BASE + 190) -#define __NR_ugetrlimit (__NR_SYSCALL_BASE + 191) -#define __NR_mmap2 (__NR_SYSCALL_BASE + 192) -#define __NR_truncate64 (__NR_SYSCALL_BASE + 193) -#define __NR_ftruncate64 (__NR_SYSCALL_BASE + 194) -#define __NR_stat64 (__NR_SYSCALL_BASE + 195) -#define __NR_lstat64 (__NR_SYSCALL_BASE + 196) -#define __NR_fstat64 (__NR_SYSCALL_BASE + 197) -#define __NR_lchown32 (__NR_SYSCALL_BASE + 198) -#define __NR_getuid32 (__NR_SYSCALL_BASE + 199) -#define __NR_getgid32 (__NR_SYSCALL_BASE + 200) -#define __NR_geteuid32 (__NR_SYSCALL_BASE + 201) -#define __NR_getegid32 (__NR_SYSCALL_BASE + 202) -#define __NR_setreuid32 (__NR_SYSCALL_BASE + 203) -#define __NR_setregid32 (__NR_SYSCALL_BASE + 204) -#define __NR_getgroups32 (__NR_SYSCALL_BASE + 205) -#define __NR_setgroups32 (__NR_SYSCALL_BASE + 206) -#define __NR_fchown32 (__NR_SYSCALL_BASE + 207) -#define __NR_setresuid32 (__NR_SYSCALL_BASE + 208) -#define __NR_getresuid32 (__NR_SYSCALL_BASE + 209) -#define __NR_setresgid32 (__NR_SYSCALL_BASE + 210) -#define __NR_getresgid32 (__NR_SYSCALL_BASE + 211) -#define __NR_chown32 (__NR_SYSCALL_BASE + 212) -#define __NR_setuid32 (__NR_SYSCALL_BASE + 213) -#define __NR_setgid32 (__NR_SYSCALL_BASE + 214) -#define __NR_setfsuid32 (__NR_SYSCALL_BASE + 215) -#define __NR_setfsgid32 (__NR_SYSCALL_BASE + 216) -#define __NR_getdents64 (__NR_SYSCALL_BASE + 217) -#define __NR_pivot_root (__NR_SYSCALL_BASE + 218) -#define __NR_mincore (__NR_SYSCALL_BASE + 219) -#define __NR_madvise (__NR_SYSCALL_BASE + 220) -#define __NR_fcntl64 (__NR_SYSCALL_BASE + 221) -#define __NR_gettid (__NR_SYSCALL_BASE + 224) -#define __NR_readahead (__NR_SYSCALL_BASE + 225) -#define __NR_setxattr (__NR_SYSCALL_BASE + 226) -#define __NR_lsetxattr (__NR_SYSCALL_BASE + 227) -#define __NR_fsetxattr (__NR_SYSCALL_BASE + 228) -#define __NR_getxattr (__NR_SYSCALL_BASE + 229) -#define __NR_lgetxattr (__NR_SYSCALL_BASE + 230) -#define __NR_fgetxattr (__NR_SYSCALL_BASE + 231) -#define __NR_listxattr (__NR_SYSCALL_BASE + 232) -#define __NR_llistxattr (__NR_SYSCALL_BASE + 233) -#define __NR_flistxattr (__NR_SYSCALL_BASE + 234) -#define __NR_removexattr (__NR_SYSCALL_BASE + 235) -#define __NR_lremovexattr (__NR_SYSCALL_BASE + 236) -#define __NR_fremovexattr (__NR_SYSCALL_BASE + 237) -#define __NR_tkill (__NR_SYSCALL_BASE + 238) -#define __NR_sendfile64 (__NR_SYSCALL_BASE + 239) -#define __NR_futex (__NR_SYSCALL_BASE + 240) -#define __NR_sched_setaffinity (__NR_SYSCALL_BASE + 241) -#define __NR_sched_getaffinity (__NR_SYSCALL_BASE + 242) -#define __NR_io_setup (__NR_SYSCALL_BASE + 243) -#define __NR_io_destroy (__NR_SYSCALL_BASE + 244) -#define __NR_io_getevents (__NR_SYSCALL_BASE + 245) -#define __NR_io_submit (__NR_SYSCALL_BASE + 246) -#define __NR_io_cancel (__NR_SYSCALL_BASE + 247) -#define __NR_exit_group (__NR_SYSCALL_BASE + 248) -#define __NR_lookup_dcookie (__NR_SYSCALL_BASE + 249) -#define __NR_epoll_create (__NR_SYSCALL_BASE + 250) -#define __NR_epoll_ctl (__NR_SYSCALL_BASE + 251) -#define __NR_epoll_wait (__NR_SYSCALL_BASE + 252) -#define __NR_remap_file_pages (__NR_SYSCALL_BASE + 253) -#define __NR_set_tid_address (__NR_SYSCALL_BASE + 256) -#define __NR_timer_create (__NR_SYSCALL_BASE + 257) -#define __NR_timer_settime (__NR_SYSCALL_BASE + 258) -#define __NR_timer_gettime (__NR_SYSCALL_BASE + 259) -#define __NR_timer_getoverrun (__NR_SYSCALL_BASE + 260) -#define __NR_timer_delete (__NR_SYSCALL_BASE + 261) -#define __NR_clock_settime (__NR_SYSCALL_BASE + 262) -#define __NR_clock_gettime (__NR_SYSCALL_BASE + 263) -#define __NR_clock_getres (__NR_SYSCALL_BASE + 264) -#define __NR_clock_nanosleep (__NR_SYSCALL_BASE + 265) -#define __NR_statfs64 (__NR_SYSCALL_BASE + 266) -#define __NR_fstatfs64 (__NR_SYSCALL_BASE + 267) -#define __NR_tgkill (__NR_SYSCALL_BASE + 268) -#define __NR_utimes (__NR_SYSCALL_BASE + 269) -#define __NR_arm_fadvise64_64 (__NR_SYSCALL_BASE + 270) -#define __NR_pciconfig_iobase (__NR_SYSCALL_BASE + 271) -#define __NR_pciconfig_read (__NR_SYSCALL_BASE + 272) -#define __NR_pciconfig_write (__NR_SYSCALL_BASE + 273) -#define __NR_mq_open (__NR_SYSCALL_BASE + 274) -#define __NR_mq_unlink (__NR_SYSCALL_BASE + 275) -#define __NR_mq_timedsend (__NR_SYSCALL_BASE + 276) -#define __NR_mq_timedreceive (__NR_SYSCALL_BASE + 277) -#define __NR_mq_notify (__NR_SYSCALL_BASE + 278) -#define __NR_mq_getsetattr (__NR_SYSCALL_BASE + 279) -#define __NR_waitid (__NR_SYSCALL_BASE + 280) -#define __NR_socket (__NR_SYSCALL_BASE + 281) -#define __NR_bind (__NR_SYSCALL_BASE + 282) -#define __NR_connect (__NR_SYSCALL_BASE + 283) -#define __NR_listen (__NR_SYSCALL_BASE + 284) -#define __NR_accept (__NR_SYSCALL_BASE + 285) -#define __NR_getsockname (__NR_SYSCALL_BASE + 286) -#define __NR_getpeername (__NR_SYSCALL_BASE + 287) -#define __NR_socketpair (__NR_SYSCALL_BASE + 288) -#define __NR_send (__NR_SYSCALL_BASE + 289) -#define __NR_sendto (__NR_SYSCALL_BASE + 290) -#define __NR_recv (__NR_SYSCALL_BASE + 291) -#define __NR_recvfrom (__NR_SYSCALL_BASE + 292) -#define __NR_shutdown (__NR_SYSCALL_BASE + 293) -#define __NR_setsockopt (__NR_SYSCALL_BASE + 294) -#define __NR_getsockopt (__NR_SYSCALL_BASE + 295) -#define __NR_sendmsg (__NR_SYSCALL_BASE + 296) -#define __NR_recvmsg (__NR_SYSCALL_BASE + 297) -#define __NR_semop (__NR_SYSCALL_BASE + 298) -#define __NR_semget (__NR_SYSCALL_BASE + 299) -#define __NR_semctl (__NR_SYSCALL_BASE + 300) -#define __NR_msgsnd (__NR_SYSCALL_BASE + 301) -#define __NR_msgrcv (__NR_SYSCALL_BASE + 302) -#define __NR_msgget (__NR_SYSCALL_BASE + 303) -#define __NR_msgctl (__NR_SYSCALL_BASE + 304) -#define __NR_shmat (__NR_SYSCALL_BASE + 305) -#define __NR_shmdt (__NR_SYSCALL_BASE + 306) -#define __NR_shmget (__NR_SYSCALL_BASE + 307) -#define __NR_shmctl (__NR_SYSCALL_BASE + 308) -#define __NR_add_key (__NR_SYSCALL_BASE + 309) -#define __NR_request_key (__NR_SYSCALL_BASE + 310) -#define __NR_keyctl (__NR_SYSCALL_BASE + 311) -#define __NR_semtimedop (__NR_SYSCALL_BASE + 312) -#define __NR_vserver (__NR_SYSCALL_BASE + 313) -#define __NR_ioprio_set (__NR_SYSCALL_BASE + 314) -#define __NR_ioprio_get (__NR_SYSCALL_BASE + 315) -#define __NR_inotify_init (__NR_SYSCALL_BASE + 316) -#define __NR_inotify_add_watch (__NR_SYSCALL_BASE + 317) -#define __NR_inotify_rm_watch (__NR_SYSCALL_BASE + 318) -#define __NR_mbind (__NR_SYSCALL_BASE + 319) -#define __NR_get_mempolicy (__NR_SYSCALL_BASE + 320) -#define __NR_set_mempolicy (__NR_SYSCALL_BASE + 321) -#define __NR_openat (__NR_SYSCALL_BASE + 322) -#define __NR_mkdirat (__NR_SYSCALL_BASE + 323) -#define __NR_mknodat (__NR_SYSCALL_BASE + 324) -#define __NR_fchownat (__NR_SYSCALL_BASE + 325) -#define __NR_futimesat (__NR_SYSCALL_BASE + 326) -#define __NR_fstatat64 (__NR_SYSCALL_BASE + 327) -#define __NR_unlinkat (__NR_SYSCALL_BASE + 328) -#define __NR_renameat (__NR_SYSCALL_BASE + 329) -#define __NR_linkat (__NR_SYSCALL_BASE + 330) -#define __NR_symlinkat (__NR_SYSCALL_BASE + 331) -#define __NR_readlinkat (__NR_SYSCALL_BASE + 332) -#define __NR_fchmodat (__NR_SYSCALL_BASE + 333) -#define __NR_faccessat (__NR_SYSCALL_BASE + 334) -#define __NR_pselect6 (__NR_SYSCALL_BASE + 335) -#define __NR_ppoll (__NR_SYSCALL_BASE + 336) -#define __NR_unshare (__NR_SYSCALL_BASE + 337) -#define __NR_set_robust_list (__NR_SYSCALL_BASE + 338) -#define __NR_get_robust_list (__NR_SYSCALL_BASE + 339) -#define __NR_splice (__NR_SYSCALL_BASE + 340) -#define __NR_arm_sync_file_range (__NR_SYSCALL_BASE + 341) -#define __NR_tee (__NR_SYSCALL_BASE + 342) -#define __NR_vmsplice (__NR_SYSCALL_BASE + 343) -#define __NR_move_pages (__NR_SYSCALL_BASE + 344) -#define __NR_getcpu (__NR_SYSCALL_BASE + 345) -#define __NR_epoll_pwait (__NR_SYSCALL_BASE + 346) -#define __NR_kexec_load (__NR_SYSCALL_BASE + 347) -#define __NR_utimensat (__NR_SYSCALL_BASE + 348) -#define __NR_signalfd (__NR_SYSCALL_BASE + 349) -#define __NR_timerfd_create (__NR_SYSCALL_BASE + 350) -#define __NR_eventfd (__NR_SYSCALL_BASE + 351) -#define __NR_fallocate (__NR_SYSCALL_BASE + 352) -#define __NR_timerfd_settime (__NR_SYSCALL_BASE + 353) -#define __NR_timerfd_gettime (__NR_SYSCALL_BASE + 354) -#define __NR_signalfd4 (__NR_SYSCALL_BASE + 355) -#define __NR_eventfd2 (__NR_SYSCALL_BASE + 356) -#define __NR_epoll_create1 (__NR_SYSCALL_BASE + 357) -#define __NR_dup3 (__NR_SYSCALL_BASE + 358) -#define __NR_pipe2 (__NR_SYSCALL_BASE + 359) -#define __NR_inotify_init1 (__NR_SYSCALL_BASE + 360) -#define __NR_preadv (__NR_SYSCALL_BASE + 361) -#define __NR_pwritev (__NR_SYSCALL_BASE + 362) -#define __NR_rt_tgsigqueueinfo (__NR_SYSCALL_BASE + 363) -#define __NR_perf_event_open (__NR_SYSCALL_BASE + 364) -#define __NR_recvmmsg (__NR_SYSCALL_BASE + 365) -#define __NR_accept4 (__NR_SYSCALL_BASE + 366) -#define __NR_fanotify_init (__NR_SYSCALL_BASE + 367) -#define __NR_fanotify_mark (__NR_SYSCALL_BASE + 368) -#define __NR_prlimit64 (__NR_SYSCALL_BASE + 369) -#define __NR_name_to_handle_at (__NR_SYSCALL_BASE + 370) -#define __NR_open_by_handle_at (__NR_SYSCALL_BASE + 371) -#define __NR_clock_adjtime (__NR_SYSCALL_BASE + 372) -#define __NR_syncfs (__NR_SYSCALL_BASE + 373) -#define __NR_sendmmsg (__NR_SYSCALL_BASE + 374) -#define __NR_setns (__NR_SYSCALL_BASE + 375) -#define __NR_process_vm_readv (__NR_SYSCALL_BASE + 376) -#define __NR_process_vm_writev (__NR_SYSCALL_BASE + 377) -#define __NR_kcmp (__NR_SYSCALL_BASE + 378) -#define __NR_finit_module (__NR_SYSCALL_BASE + 379) -#define __NR_sched_setattr (__NR_SYSCALL_BASE + 380) -#define __NR_sched_getattr (__NR_SYSCALL_BASE + 381) -#define __NR_renameat2 (__NR_SYSCALL_BASE + 382) -#define __NR_seccomp (__NR_SYSCALL_BASE + 383) -#define __NR_getrandom (__NR_SYSCALL_BASE + 384) -#define __NR_memfd_create (__NR_SYSCALL_BASE + 385) -#define __NR_bpf (__NR_SYSCALL_BASE + 386) -#define __NR_execveat (__NR_SYSCALL_BASE + 387) -#define __NR_userfaultfd (__NR_SYSCALL_BASE + 388) -#define __NR_membarrier (__NR_SYSCALL_BASE + 389) -#define __NR_mlock2 (__NR_SYSCALL_BASE + 390) -#define __NR_copy_file_range (__NR_SYSCALL_BASE + 391) -#define __NR_preadv2 (__NR_SYSCALL_BASE + 392) -#define __NR_pwritev2 (__NR_SYSCALL_BASE + 393) -#define __NR_pkey_mprotect (__NR_SYSCALL_BASE + 394) -#define __NR_pkey_alloc (__NR_SYSCALL_BASE + 395) -#define __NR_pkey_free (__NR_SYSCALL_BASE + 396) -#define __NR_statx (__NR_SYSCALL_BASE + 397) -#define __NR_rseq (__NR_SYSCALL_BASE + 398) -#define __NR_io_pgetevents (__NR_SYSCALL_BASE + 399) -#define __NR_migrate_pages (__NR_SYSCALL_BASE + 400) -#define __NR_kexec_file_load (__NR_SYSCALL_BASE + 401) -#define __NR_clock_gettime64 (__NR_SYSCALL_BASE + 403) -#define __NR_clock_settime64 (__NR_SYSCALL_BASE + 404) -#define __NR_clock_adjtime64 (__NR_SYSCALL_BASE + 405) -#define __NR_clock_getres_time64 (__NR_SYSCALL_BASE + 406) -#define __NR_clock_nanosleep_time64 (__NR_SYSCALL_BASE + 407) -#define __NR_timer_gettime64 (__NR_SYSCALL_BASE + 408) -#define __NR_timer_settime64 (__NR_SYSCALL_BASE + 409) -#define __NR_timerfd_gettime64 (__NR_SYSCALL_BASE + 410) -#define __NR_timerfd_settime64 (__NR_SYSCALL_BASE + 411) -#define __NR_utimensat_time64 (__NR_SYSCALL_BASE + 412) -#define __NR_pselect6_time64 (__NR_SYSCALL_BASE + 413) -#define __NR_ppoll_time64 (__NR_SYSCALL_BASE + 414) -#define __NR_io_pgetevents_time64 (__NR_SYSCALL_BASE + 416) -#define __NR_recvmmsg_time64 (__NR_SYSCALL_BASE + 417) -#define __NR_mq_timedsend_time64 (__NR_SYSCALL_BASE + 418) -#define __NR_mq_timedreceive_time64 (__NR_SYSCALL_BASE + 419) -#define __NR_semtimedop_time64 (__NR_SYSCALL_BASE + 420) -#define __NR_rt_sigtimedwait_time64 (__NR_SYSCALL_BASE + 421) -#define __NR_futex_time64 (__NR_SYSCALL_BASE + 422) -#define __NR_sched_rr_get_interval_time64 (__NR_SYSCALL_BASE + 423) -#define __NR_pidfd_send_signal (__NR_SYSCALL_BASE + 424) -#define __NR_io_uring_setup (__NR_SYSCALL_BASE + 425) -#define __NR_io_uring_enter (__NR_SYSCALL_BASE + 426) -#define __NR_io_uring_register (__NR_SYSCALL_BASE + 427) -#define __NR_open_tree (__NR_SYSCALL_BASE + 428) -#define __NR_move_mount (__NR_SYSCALL_BASE + 429) -#define __NR_fsopen (__NR_SYSCALL_BASE + 430) -#define __NR_fsconfig (__NR_SYSCALL_BASE + 431) -#define __NR_fsmount (__NR_SYSCALL_BASE + 432) -#define __NR_fspick (__NR_SYSCALL_BASE + 433) -#define __NR_pidfd_open (__NR_SYSCALL_BASE + 434) -#define __NR_clone3 (__NR_SYSCALL_BASE + 435) -#define __NR_openat2 (__NR_SYSCALL_BASE + 437) -#define __NR_pidfd_getfd (__NR_SYSCALL_BASE + 438) -#define __NR_faccessat2 (__NR_SYSCALL_BASE + 439) - -#endif /* _ASM_ARM_UNISTD_COMMON_H */ diff --git a/linux-headers/asm-arm/unistd-eabi.h b/linux-headers/asm-arm/unistd-eabi.h deleted file mode 100644 index 266f1fcdfb..0000000000 --- a/linux-headers/asm-arm/unistd-eabi.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef _ASM_ARM_UNISTD_EABI_H -#define _ASM_ARM_UNISTD_EABI_H 1 - - -#endif /* _ASM_ARM_UNISTD_EABI_H */ diff --git a/linux-headers/asm-arm/unistd-oabi.h b/linux-headers/asm-arm/unistd-oabi.h deleted file mode 100644 index 47d9afb96d..0000000000 --- a/linux-headers/asm-arm/unistd-oabi.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef _ASM_ARM_UNISTD_OABI_H -#define _ASM_ARM_UNISTD_OABI_H 1 - -#define __NR_time (__NR_SYSCALL_BASE + 13) -#define __NR_umount (__NR_SYSCALL_BASE + 22) -#define __NR_stime (__NR_SYSCALL_BASE + 25) -#define __NR_alarm (__NR_SYSCALL_BASE + 27) -#define __NR_utime (__NR_SYSCALL_BASE + 30) -#define __NR_getrlimit (__NR_SYSCALL_BASE + 76) -#define __NR_select (__NR_SYSCALL_BASE + 82) -#define __NR_readdir (__NR_SYSCALL_BASE + 89) -#define __NR_mmap (__NR_SYSCALL_BASE + 90) -#define __NR_socketcall (__NR_SYSCALL_BASE + 102) -#define __NR_syscall (__NR_SYSCALL_BASE + 113) -#define __NR_ipc (__NR_SYSCALL_BASE + 117) - -#endif /* _ASM_ARM_UNISTD_OABI_H */ diff --git a/linux-headers/asm-arm/unistd.h b/linux-headers/asm-arm/unistd.h deleted file mode 100644 index 18b0825885..0000000000 --- a/linux-headers/asm-arm/unistd.h +++ /dev/null @@ -1,41 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ -/* - * arch/arm/include/asm/unistd.h - * - * Copyright (C) 2001-2005 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Please forward _all_ changes to this file to rmk@arm.linux.org.uk, - * no matter what the change is. Thanks! - */ -#ifndef __ASM_ARM_UNISTD_H -#define __ASM_ARM_UNISTD_H - -#define __NR_OABI_SYSCALL_BASE 0x900000 - -#if defined(__thumb__) || defined(__ARM_EABI__) -#define __NR_SYSCALL_BASE 0 -#include -#else -#define __NR_SYSCALL_BASE __NR_OABI_SYSCALL_BASE -#include -#endif - -#include -#define __NR_sync_file_range2 __NR_arm_sync_file_range - -/* - * The following SWIs are ARM private. - */ -#define __ARM_NR_BASE (__NR_SYSCALL_BASE+0x0f0000) -#define __ARM_NR_breakpoint (__ARM_NR_BASE+1) -#define __ARM_NR_cacheflush (__ARM_NR_BASE+2) -#define __ARM_NR_usr26 (__ARM_NR_BASE+3) -#define __ARM_NR_usr32 (__ARM_NR_BASE+4) -#define __ARM_NR_set_tls (__ARM_NR_BASE+5) -#define __ARM_NR_get_tls (__ARM_NR_BASE+6) - -#endif /* __ASM_ARM_UNISTD_H */ diff --git a/scripts/update-linux-headers.sh b/scripts/update-linux-headers.sh index b43b8ef75a..717c379f9e 100755 --- a/scripts/update-linux-headers.sh +++ b/scripts/update-linux-headers.sh @@ -156,11 +156,6 @@ EOF cp "$hdrdir/include/asm/unistd_32.h" "$output/linux-headers/asm-s390/" cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-s390/" fi - if [ $arch = arm ]; then - cp "$hdrdir/include/asm/unistd-eabi.h" "$output/linux-headers/asm-arm/" - cp "$hdrdir/include/asm/unistd-oabi.h" "$output/linux-headers/asm-arm/" - cp "$hdrdir/include/asm/unistd-common.h" "$output/linux-headers/asm-arm/" - fi if [ $arch = arm64 ]; then cp "$hdrdir/include/asm/sve_context.h" "$output/linux-headers/asm-arm64/" cp "$hdrdir/include/asm/unistd_64.h" "$output/linux-headers/asm-arm64/" From 069a2ce8a75c9b59a4d08d6d2da3b36bfc5af3f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:17 +0100 Subject: [PATCH 2522/2760] functional: ensure log handlers are closed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This avoids a resource leak warning from python when the log handler is garbage collected. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250715143023.1851000-9-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 2082c6fce4..71c7160adc 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -232,6 +232,7 @@ def tearDown(self): self.socketdir = None self.machinelog.removeHandler(self._log_fh) self.log.removeHandler(self._log_fh) + self._log_fh.close() def main(): path = os.path.basename(sys.argv[0])[:-3] @@ -399,4 +400,5 @@ def tearDown(self): for vm in self._vms.values(): vm.shutdown() logging.getLogger('console').removeHandler(self._console_log_fh) + self._console_log_fh.close() super().tearDown() From 72bc0134b500d599f0f1c253c78c68df642d1634 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:18 +0100 Subject: [PATCH 2523/2760] functional: ensure sockets and files are closed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The multiprocess and virtio_gpu tests open sockets but then forget to close them, which triggers resource leak warnings The virtio_gpu test also fails to close a log file it opens. Signed-off-by: Daniel P. Berrangé Reviewed-by: Thomas Huth Message-ID: <20250715143023.1851000-10-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/test_multiprocess.py | 3 +++ tests/functional/test_virtio_gpu.py | 2 ++ 2 files changed, 5 insertions(+) diff --git a/tests/functional/test_multiprocess.py b/tests/functional/test_multiprocess.py index 751cf10e63..92d5207b0e 100755 --- a/tests/functional/test_multiprocess.py +++ b/tests/functional/test_multiprocess.py @@ -83,6 +83,9 @@ def do_test(self, kernel_asset, initrd_asset, 'cat /sys/bus/pci/devices/*/uevent', 'PCI_ID=1000:0012') + proxy_sock.close() + remote_sock.close() + def test_multiprocess(self): kernel_command_line = self.KERNEL_COMMON_COMMAND_LINE if self.arch == 'x86_64': diff --git a/tests/functional/test_virtio_gpu.py b/tests/functional/test_virtio_gpu.py index 81c9156d63..be96de24da 100755 --- a/tests/functional/test_virtio_gpu.py +++ b/tests/functional/test_virtio_gpu.py @@ -108,6 +108,7 @@ def test_vhost_user_vga_virgl(self): shell=False, close_fds=False, ) + self._vug_log_file.close() self.vm.set_console() self.vm.add_args("-cpu", "host") @@ -135,6 +136,7 @@ def test_vhost_user_vga_virgl(self): "features: +virgl +edid") self.vm.shutdown() qemu_sock.close() + vug_sock.close() vugp.terminate() vugp.wait() From c3fd296cf7b1016671205e1525e4e9caf870f68e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 15 Jul 2025 15:30:19 +0100 Subject: [PATCH 2524/2760] functional: always enable all python warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Of most importance is that this gives us a heads-up if anything we rely on has been deprecated. The default python behaviour only emits a warning if triggered from __main__ which is very limited. Setting the env variable further ensures that any python child processes will also display warnings. Signed-off-by: Daniel P. Berrangé Acked-by: Thomas Huth Message-ID: <20250715143023.1851000-11-berrange@redhat.com> Signed-off-by: Thomas Huth --- tests/functional/qemu_test/testcase.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 71c7160adc..2a78e735f1 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -19,6 +19,7 @@ from subprocess import run import sys import tempfile +import warnings import unittest import uuid @@ -235,6 +236,9 @@ def tearDown(self): self._log_fh.close() def main(): + warnings.simplefilter("default") + os.environ["PYTHONWARNINGS"] = "default" + path = os.path.basename(sys.argv[0])[:-3] cache = os.environ.get("QEMU_TEST_PRECACHE", None) From 2c182e1213a0fd334f13948cb80b886e6fd1ab88 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Thu, 17 Jul 2025 11:41:05 +0100 Subject: [PATCH 2525/2760] docs/devel: fix over-quoting of QEMU_TEST_KEEP_SCRATCH MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-ID: <20250717104105.2656786-1-alex.bennee@linaro.org> Signed-off-by: Thomas Huth --- docs/devel/testing/functional.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/devel/testing/functional.rst b/docs/devel/testing/functional.rst index 9e56dd1b11..3728bab6c0 100644 --- a/docs/devel/testing/functional.rst +++ b/docs/devel/testing/functional.rst @@ -65,7 +65,7 @@ directory should be your build folder. For example:: The test framework will automatically purge any scratch files created during the tests. If needing to debug a failed test, it is possible to keep these -files around on disk by setting ```QEMU_TEST_KEEP_SCRATCH=1``` as an env +files around on disk by setting ``QEMU_TEST_KEEP_SCRATCH=1`` as an env variable. Any preserved files will be deleted the next time the test is run without this variable set. From 301fbbaf03fb114a1e45833987ddfd7bbb403963 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:10 +1000 Subject: [PATCH 2526/2760] ppc/xive: Fix xive trace event output MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Typo, IBP should be IPB. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-2-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/trace-events | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 334aa6a97b..9ed2616e58 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -274,9 +274,9 @@ kvm_xive_cpu_connect(uint32_t id) "connect CPU%d to KVM device" kvm_xive_source_reset(uint32_t srcno) "IRQ 0x%x" # xive.c -xive_tctx_accept(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IBP=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x ACK" -xive_tctx_notify(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IBP=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x raise !" -xive_tctx_set_cppr(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IBP=0x%02x PIPR=0x%02x new CPPR=0x%02x NSR=0x%02x" +xive_tctx_accept(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IPB=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x ACK" +xive_tctx_notify(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IPB=0x%02x PIPR=0x%02x CPPR=0x%02x NSR=0x%02x raise !" +xive_tctx_set_cppr(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IPB=0x%02x PIPR=0x%02x new CPPR=0x%02x NSR=0x%02x" xive_source_esb_read(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64 xive_source_esb_write(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64 xive_router_end_notify(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "END 0x%02x/0x%04x -> enqueue 0x%08x" From f0aab779418ed883ea2b5ffcc3985ef26f6e3545 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:11 +1000 Subject: [PATCH 2527/2760] ppc/xive: Report access size in XIVE TM operation error logs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Report access size in XIVE TM operation error logs. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-3-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 27b473e4d7..120376fb6b 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -326,7 +326,7 @@ static void xive_tm_raw_write(XiveTCTX *tctx, hwaddr offset, uint64_t value, */ if (size < 4 || !mask || ring_offset == TM_QW0_USER) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA @%" - HWADDR_PRIx"\n", offset); + HWADDR_PRIx" size %d\n", offset, size); return; } @@ -357,7 +357,7 @@ static uint64_t xive_tm_raw_read(XiveTCTX *tctx, hwaddr offset, unsigned size) */ if (size < 4 || !mask || ring_offset == TM_QW0_USER) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access at TIMA @%" - HWADDR_PRIx"\n", offset); + HWADDR_PRIx" size %d\n", offset, size); return -1; } @@ -688,7 +688,7 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, xto = xive_tm_find_op(tctx->xptr, offset, size, true); if (!xto) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA " - "@%"HWADDR_PRIx"\n", offset); + "@%"HWADDR_PRIx" size %d\n", offset, size); } else { xto->write_handler(xptr, tctx, offset, value, size); } @@ -727,7 +727,7 @@ uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, xto = xive_tm_find_op(tctx->xptr, offset, size, false); if (!xto) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access to TIMA" - "@%"HWADDR_PRIx"\n", offset); + "@%"HWADDR_PRIx" size %d\n", offset, size); return -1; } ret = xto->read_handler(xptr, tctx, offset, size); From f16697292add6c3c15014a20fd5fce70b8c56734 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:12 +1000 Subject: [PATCH 2528/2760] ppc/xive2: Fix calculation of END queue sizes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The queue size of an Event Notification Descriptor (END) is determined by the 'cl' and QsZ fields of the END. If the cl field is 1, then the queue size (in bytes) will be the size of a cache line 128B * 2^QsZ and QsZ is limited to 4. Otherwise, it will be 4096B * 2^QsZ with QsZ limited to 12. Fixes: f8a233dedf2 ("ppc/xive2: Introduce a XIVE2 core framework") Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-4-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 25 +++++++++++++++++++------ include/hw/ppc/xive2_regs.h | 1 + 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index a08cf906d0..cb75ca8798 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -188,12 +188,27 @@ void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf) (uint32_t) xive_get_field64(EAS2_END_DATA, eas->w)); } +#define XIVE2_QSIZE_CHUNK_CL 128 +#define XIVE2_QSIZE_CHUNK_4k 4096 +/* Calculate max number of queue entries for an END */ +static uint32_t xive2_end_get_qentries(Xive2End *end) +{ + uint32_t w3 = end->w3; + uint32_t qsize = xive_get_field32(END2_W3_QSIZE, w3); + if (xive_get_field32(END2_W3_CL, w3)) { + g_assert(qsize <= 4); + return (XIVE2_QSIZE_CHUNK_CL << qsize) / sizeof(uint32_t); + } else { + g_assert(qsize <= 12); + return (XIVE2_QSIZE_CHUNK_4k << qsize) / sizeof(uint32_t); + } +} + void xive2_end_queue_pic_print_info(Xive2End *end, uint32_t width, GString *buf) { uint64_t qaddr_base = xive2_end_qaddr(end); - uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1); - uint32_t qentries = 1 << (qsize + 10); + uint32_t qentries = xive2_end_get_qentries(end); int i; /* @@ -223,8 +238,7 @@ void xive2_end_pic_print_info(Xive2End *end, uint32_t end_idx, GString *buf) uint64_t qaddr_base = xive2_end_qaddr(end); uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1); uint32_t qgen = xive_get_field32(END2_W1_GENERATION, end->w1); - uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); - uint32_t qentries = 1 << (qsize + 10); + uint32_t qentries = xive2_end_get_qentries(end); uint32_t nvx_blk = xive_get_field32(END2_W6_VP_BLOCK, end->w6); uint32_t nvx_idx = xive_get_field32(END2_W6_VP_OFFSET, end->w6); @@ -341,13 +355,12 @@ void xive2_nvgc_pic_print_info(Xive2Nvgc *nvgc, uint32_t nvgc_idx, GString *buf) static void xive2_end_enqueue(Xive2End *end, uint32_t data) { uint64_t qaddr_base = xive2_end_qaddr(end); - uint32_t qsize = xive_get_field32(END2_W3_QSIZE, end->w3); uint32_t qindex = xive_get_field32(END2_W1_PAGE_OFF, end->w1); uint32_t qgen = xive_get_field32(END2_W1_GENERATION, end->w1); uint64_t qaddr = qaddr_base + (qindex << 2); uint32_t qdata = cpu_to_be32((qgen << 31) | (data & 0x7fffffff)); - uint32_t qentries = 1 << (qsize + 10); + uint32_t qentries = xive2_end_get_qentries(end); if (dma_memory_write(&address_space_memory, qaddr, &qdata, sizeof(qdata), MEMTXATTRS_UNSPECIFIED)) { diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index b11395c563..3c28de8a30 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -87,6 +87,7 @@ typedef struct Xive2End { #define END2_W2_EQ_ADDR_HI PPC_BITMASK32(8, 31) uint32_t w3; #define END2_W3_EQ_ADDR_LO PPC_BITMASK32(0, 24) +#define END2_W3_CL PPC_BIT32(27) #define END2_W3_QSIZE PPC_BITMASK32(28, 31) uint32_t w4; #define END2_W4_END_BLOCK PPC_BITMASK32(4, 7) From e8cf73b849879cd93b1d1b5fd3bde79fb42064dc Mon Sep 17 00:00:00 2001 From: Michael Kowal Date: Mon, 12 May 2025 13:10:13 +1000 Subject: [PATCH 2529/2760] ppc/xive2: Remote VSDs need to match on forwarding address MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In a multi chip environment there will be remote/forwarded VSDs. The check to find a matching INT controller (XIVE) of the remote block number was checking the INTs chip number. Block numbers are not tied to a chip number. The matching remote INT is the one that matches the forwarded VSD address with VSD types associated MMIO BAR. Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-5-npiggin@gmail.com [ clg: Fixed log format in pnv_xive2_get_remote() ] Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index ec8b0c68f1..6b724fe762 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -101,12 +101,10 @@ static uint32_t pnv_xive2_block_id(PnvXive2 *xive) } /* - * Remote access to controllers. HW uses MMIOs. For now, a simple scan - * of the chips is good enough. - * - * TODO: Block scope support + * Remote access to INT controllers. HW uses MMIOs(?). For now, a simple + * scan of all the chips INT controller is good enough. */ -static PnvXive2 *pnv_xive2_get_remote(uint8_t blk) +static PnvXive2 *pnv_xive2_get_remote(uint32_t vsd_type, hwaddr fwd_addr) { PnvMachineState *pnv = PNV_MACHINE(qdev_get_machine()); int i; @@ -115,10 +113,23 @@ static PnvXive2 *pnv_xive2_get_remote(uint8_t blk) Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); PnvXive2 *xive = &chip10->xive; - if (pnv_xive2_block_id(xive) == blk) { + /* + * Is this the XIVE matching the forwarded VSD address is for this + * VSD type + */ + if ((vsd_type == VST_ESB && fwd_addr == xive->esb_base) || + (vsd_type == VST_END && fwd_addr == xive->end_base) || + ((vsd_type == VST_NVP || + vsd_type == VST_NVG) && fwd_addr == xive->nvpg_base) || + (vsd_type == VST_NVC && fwd_addr == xive->nvc_base)) { return xive; } } + + qemu_log_mask(LOG_GUEST_ERROR, + "XIVE: >>>>> %s vsd_type %u fwd_addr 0x%"HWADDR_PRIx + " NOT FOUND\n", + __func__, vsd_type, fwd_addr); return NULL; } @@ -251,8 +262,7 @@ static uint64_t pnv_xive2_vst_addr(PnvXive2 *xive, uint32_t type, uint8_t blk, /* Remote VST access */ if (GETFIELD(VSD_MODE, vsd) == VSD_MODE_FORWARD) { - xive = pnv_xive2_get_remote(blk); - + xive = pnv_xive2_get_remote(type, (vsd & VSD_ADDRESS_MASK)); return xive ? pnv_xive2_vst_addr(xive, type, blk, idx) : 0; } From d1023a296c8297454fc4b207d58707c0a5e62e0a Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:14 +1000 Subject: [PATCH 2530/2760] ppc/xive2: fix context push calculation of IPB priority MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Pushing a context and loading IPB from NVP is defined to merge ('or') that IPB into the TIMA IPB register. PIPR should therefore be calculated based on the final IPB value, not just the NVP value. Fixes: 9d2b6058c5b ("ppc/xive2: Add grouping level to notification") Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-6-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index cb75ca8798..01cf96a2af 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -835,8 +835,9 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, 0); xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 2); } + /* IPB bits in the backlog are merged with the TIMA IPB bits */ regs[TM_IPB] |= ipb; - backlog_prio = xive_ipb_to_pipr(ipb); + backlog_prio = xive_ipb_to_pipr(regs[TM_IPB]); backlog_level = 0; first_group = xive_get_field32(NVP2_W0_PGOFIRST, nvp.w0); From bde8c148bb22b99cb84cda800fa555851b8cb358 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:15 +1000 Subject: [PATCH 2531/2760] ppc/xive: Fix PHYS NSR ring matching MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Test that the NSR exception bit field is equal to the pool ring value, rather than any common bits set, which is more correct (although there is no practical bug because the LSI NSR type is not implemented and POOL/PHYS NSR are encoded with exclusive bits). Fixes: 4c3ccac636 ("pnv/xive: Add special handling for pool targets") Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-7-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 120376fb6b..bc829bebe9 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -54,7 +54,8 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) uint8_t *alt_regs; /* POOL interrupt uses IPB in QW2, POOL ring */ - if ((ring == TM_QW3_HV_PHYS) && (nsr & (TM_QW3_NSR_HE_POOL << 6))) { + if ((ring == TM_QW3_HV_PHYS) && + ((nsr & TM_QW3_NSR_HE) == (TM_QW3_NSR_HE_POOL << 6))) { alt_ring = TM_QW2_HV_POOL; } else { alt_ring = ring; From 576830428eea6ebfc85792851a343214b834e401 Mon Sep 17 00:00:00 2001 From: Michael Kowal Date: Mon, 12 May 2025 13:10:16 +1000 Subject: [PATCH 2532/2760] ppc/xive2: Reset Generation Flipped bit on END Cache Watch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the END Event Queue wraps the END EQ Generation bit is flipped and the Generation Flipped bit is set to one. On a END cache Watch read operation, the Generation Flipped bit needs to be reset. While debugging an error modified END not valid error messages to include the method since all were the same. Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-8-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 3 ++- hw/intc/xive2.c | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 6b724fe762..ec247ce48f 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1325,10 +1325,11 @@ static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, case VC_ENDC_WATCH3_DATA0: /* * Load DATA registers from cache with data requested by the - * SPEC register + * SPEC register. Clear gen_flipped bit in word 1. */ watch_engine = (offset - VC_ENDC_WATCH0_DATA0) >> 6; pnv_xive2_end_cache_load(xive, watch_engine); + xive->vc_regs[reg] &= ~(uint64_t)END2_W1_GEN_FLIPPED; val = xive->vc_regs[reg]; break; diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 01cf96a2af..edf5d9eb94 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -374,8 +374,8 @@ static void xive2_end_enqueue(Xive2End *end, uint32_t data) qgen ^= 1; end->w1 = xive_set_field32(END2_W1_GENERATION, end->w1, qgen); - /* TODO(PowerNV): reset GF bit on a cache watch operation */ - end->w1 = xive_set_field32(END2_W1_GEN_FLIPPED, end->w1, qgen); + /* Set gen flipped to 1, it gets reset on a cache watch operation */ + end->w1 = xive_set_field32(END2_W1_GEN_FLIPPED, end->w1, 1); } end->w1 = xive_set_field32(END2_W1_PAGE_OFF, end->w1, qindex); } From a1577527e212efd27a8ceefbd95321c306abf739 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:17 +1000 Subject: [PATCH 2533/2760] ppc/xive2: Use fair irq target search algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current xive algorithm for finding a matching group vCPU target always uses the first vCPU found. And, since it always starts the search with thread 0 of a core, thread 0 is almost always used to handle group interrupts. This can lead to additional interrupt latency and poor performance for interrupt intensive work loads. Changing this to use a simple round-robin algorithm for deciding which thread number to use when starting a search, which leads to a more distributed use of threads for handling group interrupts. [npiggin: Also round-robin among threads, not just cores] Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-9-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index ec247ce48f..25dc8a372d 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -643,13 +643,18 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, int i, j; bool gen1_tima_os = xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; + static int next_start_core; + static int next_start_thread; + int start_core = next_start_core; + int start_thread = next_start_thread; for (i = 0; i < chip->nr_cores; i++) { - PnvCore *pc = chip->cores[i]; + PnvCore *pc = chip->cores[(i + start_core) % chip->nr_cores]; CPUCore *cc = CPU_CORE(pc); for (j = 0; j < cc->nr_threads; j++) { - PowerPCCPU *cpu = pc->threads[j]; + /* Start search for match with different thread each call */ + PowerPCCPU *cpu = pc->threads[(j + start_thread) % cc->nr_threads]; XiveTCTX *tctx; int ring; @@ -694,6 +699,15 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, if (!match->tctx) { match->ring = ring; match->tctx = tctx; + + next_start_thread = j + start_thread + 1; + if (next_start_thread >= cc->nr_threads) { + next_start_thread = 0; + next_start_core = i + start_core + 1; + if (next_start_core >= chip->nr_cores) { + next_start_core = 0; + } + } } count++; } From 8d373176181fbc11f8d8eae2b4532b867f083ea6 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:18 +1000 Subject: [PATCH 2534/2760] ppc/xive2: Fix irq preempted by lower priority group irq MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A problem was seen where uart interrupts would be lost resulting in the console hanging. Traces showed that a lower priority interrupt was preempting a higher priority interrupt, which would result in the higher priority interrupt never being handled. The new interrupt's priority was being compared against the CPPR (Current Processor Priority Register) instead of the PIPR (Post Interrupt Priority Register), as was required by the XIVE spec. This allowed for a window between raising an interrupt and ACK'ing the interrupt where a lower priority interrupt could slip in. Fixes: 26c55b99418 ("ppc/xive2: Process group backlog when updating the CPPR") Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-10-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index edf5d9eb94..36e842f041 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1283,7 +1283,7 @@ bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority) * priority to know if the thread can take the interrupt now or if * it is precluded. */ - if (priority < alt_regs[TM_CPPR]) { + if (priority < alt_regs[TM_PIPR]) { return false; } return true; From d4720a7faf4bb415f3fe7f10e5c888212b81316a Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:19 +1000 Subject: [PATCH 2535/2760] ppc/xive2: Fix treatment of PIPR in CPPR update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the XIVE spec, updating the CPPR should also update the PIPR. The final value of the PIPR depends on other factors, but it should never be set to a value that is above the CPPR. Also added support for redistributing an active group interrupt when it is precluded as a result of changing the CPPR value. Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-11-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 36e842f041..c23933f8f5 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -995,7 +995,9 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } } } - regs[TM_PIPR] = pipr_min; + + /* PIPR should not be set to a value greater than CPPR */ + regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min; rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx); if (rc) { From 3abbec04e627396c32f2b7b7461961fb68c5c122 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:20 +1000 Subject: [PATCH 2536/2760] ppc/xive2: Do not present group interrupt on OS-push if precluded by CPPR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Group interrupts should not be taken from the backlog and presented if they are precluded by CPPR. Fixes: 855434b3b8 ("ppc/xive2: Process group backlog when pushing an OS context") Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-12-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index c23933f8f5..181d1ae5f9 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -845,7 +845,9 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, group_prio = xive2_presenter_backlog_scan(xptr, nvp_blk, nvp_idx, first_group, &group_level); regs[TM_LSMFB] = group_prio; - if (regs[TM_LGS] && group_prio < backlog_prio) { + if (regs[TM_LGS] && group_prio < backlog_prio && + group_prio < regs[TM_CPPR]) { + /* VP can take a group interrupt */ xive2_presenter_backlog_decr(xptr, nvp_blk, nvp_idx, group_prio, group_level); From ad9175f8a2d7b4ef7d63e9663a42e7f7a44bc3f5 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:21 +1000 Subject: [PATCH 2537/2760] ppc/xive2: Set CPPR delivery should account for group priority MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The group interrupt delivery flow selects the group backlog scan if LSMFB < IPB, but that scan may find an interrupt with a priority >= IPB. In that case, the VP-direct interrupt should be chosen. This extends to selecting the lowest prio between POOL and PHYS rings. Implement this just by re-starting the selection logic if the backlog irq was not found or priority did not match LSMFB (LSMFB is updated so next time around it would see the right value and not loop infinitely). Signed-off-by: Nicholas Piggin Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-13-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 181d1ae5f9..cca121b5f1 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -939,7 +939,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) { uint8_t *regs = &tctx->regs[ring]; Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr); - uint8_t old_cppr, backlog_prio, first_group, group_level = 0; + uint8_t old_cppr, backlog_prio, first_group, group_level; uint8_t pipr_min, lsmfb_min, ring_min; bool group_enabled; uint32_t nvp_blk, nvp_idx; @@ -961,10 +961,12 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) * Recompute the PIPR based on local pending interrupts. It will * be adjusted below if needed in case of pending group interrupts. */ +again: pipr_min = xive_ipb_to_pipr(regs[TM_IPB]); group_enabled = !!regs[TM_LGS]; - lsmfb_min = (group_enabled) ? regs[TM_LSMFB] : 0xff; + lsmfb_min = group_enabled ? regs[TM_LSMFB] : 0xff; ring_min = ring; + group_level = 0; /* PHYS updates also depend on POOL values */ if (ring == TM_QW3_HV_PHYS) { @@ -998,9 +1000,6 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } } - /* PIPR should not be set to a value greater than CPPR */ - regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min; - rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx); if (rc) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: set CPPR on invalid context\n"); @@ -1019,7 +1018,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) if (group_enabled && lsmfb_min < cppr && - lsmfb_min < regs[TM_PIPR]) { + lsmfb_min < pipr_min) { /* * Thread has seen a group interrupt with a higher priority * than the new cppr or pending local interrupt. Check the @@ -1048,12 +1047,25 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) nvp_blk, nvp_idx, first_group, &group_level); tctx->regs[ring_min + TM_LSMFB] = backlog_prio; - if (backlog_prio != 0xFF) { - xive2_presenter_backlog_decr(tctx->xptr, nvp_blk, nvp_idx, - backlog_prio, group_level); - regs[TM_PIPR] = backlog_prio; + if (backlog_prio != lsmfb_min) { + /* + * If the group backlog scan finds a less favored or no interrupt, + * then re-do the processing which may turn up a more favored + * interrupt from IPB or the other pool. Backlog should not + * find a priority < LSMFB. + */ + g_assert(backlog_prio >= lsmfb_min); + goto again; } + + xive2_presenter_backlog_decr(tctx->xptr, nvp_blk, nvp_idx, + backlog_prio, group_level); + pipr_min = backlog_prio; } + + /* PIPR should not be set to a value greater than CPPR */ + regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min; + /* CPPR has changed, check if we need to raise a pending exception */ xive_tctx_notify(tctx, ring_min, group_level); } From 14bcc5239f4d4780ec52881779161c62c46e7243 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:22 +1000 Subject: [PATCH 2538/2760] ppc/xive: tctx_notify should clear the precluded interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If CPPR is lowered to preclude the pending interrupt, NSR should be cleared and the qemu_irq should be lowered. This avoids some cases of supurious interrupts. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-14-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index bc829bebe9..a0a60a24f5 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -110,6 +110,9 @@ void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level) regs[TM_IPB], alt_regs[TM_PIPR], alt_regs[TM_CPPR], alt_regs[TM_NSR]); qemu_irq_raise(xive_tctx_output(tctx, ring)); + } else { + alt_regs[TM_NSR] = 0; + qemu_irq_lower(xive_tctx_output(tctx, ring)); } } From 9d466ab9b6f27a5d5b7a0ec5a7ad6f60e82fafda Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:23 +1000 Subject: [PATCH 2539/2760] ppc/xive: Explicitly zero NSR after accepting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have xive_tctx_accept clear NSR in one shot rather than masking out bits as they are tested, which makes it clear it's reset to 0, and does not have a partial NSR value in the register. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-15-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index a0a60a24f5..b35d2ec179 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -68,13 +68,11 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) * If the interrupt was for a specific VP, reset the pending * buffer bit, otherwise clear the logical server indicator */ - if (regs[TM_NSR] & TM_NSR_GRP_LVL) { - regs[TM_NSR] &= ~TM_NSR_GRP_LVL; - } else { + if (!(regs[TM_NSR] & TM_NSR_GRP_LVL)) { alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); } - /* Drop the exception bit and any group/crowd */ + /* Clear the exception from NSR */ regs[TM_NSR] = 0; trace_xive_tctx_accept(tctx->cs->cpu_index, alt_ring, From 261626dce11311ba4e866272c9a2c0990c53d85c Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:24 +1000 Subject: [PATCH 2540/2760] ppc/xive: Move NSR decoding into helper functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Rather than functions to return masks to test NSR bits, have functions to test those bits directly. This should be no functional change, it just makes the code more readable. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-16-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 51 +++++++++++++++++++++++++++++++++++-------- include/hw/ppc/xive.h | 4 ++++ 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index b35d2ec179..8537cad27b 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -25,6 +25,45 @@ /* * XIVE Thread Interrupt Management context */ +bool xive_nsr_indicates_exception(uint8_t ring, uint8_t nsr) +{ + switch (ring) { + case TM_QW1_OS: + return !!(nsr & TM_QW1_NSR_EO); + case TM_QW2_HV_POOL: + case TM_QW3_HV_PHYS: + return !!(nsr & TM_QW3_NSR_HE); + default: + g_assert_not_reached(); + } +} + +bool xive_nsr_indicates_group_exception(uint8_t ring, uint8_t nsr) +{ + if ((nsr & TM_NSR_GRP_LVL) > 0) { + g_assert(xive_nsr_indicates_exception(ring, nsr)); + return true; + } + return false; +} + +uint8_t xive_nsr_exception_ring(uint8_t ring, uint8_t nsr) +{ + /* NSR determines if pool/phys ring is for phys or pool interrupt */ + if ((ring == TM_QW3_HV_PHYS) || (ring == TM_QW2_HV_POOL)) { + uint8_t he = (nsr & TM_QW3_NSR_HE) >> 6; + + if (he == TM_QW3_NSR_HE_PHYS) { + return TM_QW3_HV_PHYS; + } else if (he == TM_QW3_NSR_HE_POOL) { + return TM_QW2_HV_POOL; + } else { + /* Don't support LSI mode */ + g_assert_not_reached(); + } + } + return ring; +} static qemu_irq xive_tctx_output(XiveTCTX *tctx, uint8_t ring) { @@ -48,18 +87,12 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) qemu_irq_lower(xive_tctx_output(tctx, ring)); - if (regs[TM_NSR] != 0) { + if (xive_nsr_indicates_exception(ring, nsr)) { uint8_t cppr = regs[TM_PIPR]; uint8_t alt_ring; uint8_t *alt_regs; - /* POOL interrupt uses IPB in QW2, POOL ring */ - if ((ring == TM_QW3_HV_PHYS) && - ((nsr & TM_QW3_NSR_HE) == (TM_QW3_NSR_HE_POOL << 6))) { - alt_ring = TM_QW2_HV_POOL; - } else { - alt_ring = ring; - } + alt_ring = xive_nsr_exception_ring(ring, nsr); alt_regs = &tctx->regs[alt_ring]; regs[TM_CPPR] = cppr; @@ -68,7 +101,7 @@ static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) * If the interrupt was for a specific VP, reset the pending * buffer bit, otherwise clear the logical server indicator */ - if (!(regs[TM_NSR] & TM_NSR_GRP_LVL)) { + if (!xive_nsr_indicates_group_exception(ring, nsr)) { alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); } diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 538f438681..28f0f1b79a 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -365,6 +365,10 @@ static inline uint32_t xive_tctx_word2(uint8_t *ring) return *((uint32_t *) &ring[TM_WORD2]); } +bool xive_nsr_indicates_exception(uint8_t ring, uint8_t nsr); +bool xive_nsr_indicates_group_exception(uint8_t ring, uint8_t nsr); +uint8_t xive_nsr_exception_ring(uint8_t ring, uint8_t nsr); + /* * XIVE Router */ From 279031bc03e6f59d58f4de37b06bddaa03e4f209 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:25 +1000 Subject: [PATCH 2541/2760] ppc/xive: Fix pulling pool and phys contexts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This improves the implementation of pulling pool and phys contexts in XIVE1, by following closer the OS pulling code. In particular, the old ring data is returned rather than the modified, and irq signals are reset on pull. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-17-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 66 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 58 insertions(+), 8 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 8537cad27b..5483a815ef 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -241,25 +241,75 @@ static uint64_t xive_tm_ack_hv_reg(XivePresenter *xptr, XiveTCTX *tctx, return xive_tctx_accept(tctx, TM_QW3_HV_PHYS); } +static void xive_pool_cam_decode(uint32_t cam, uint8_t *nvt_blk, + uint32_t *nvt_idx, bool *vp) +{ + if (nvt_blk) { + *nvt_blk = xive_nvt_blk(cam); + } + if (nvt_idx) { + *nvt_idx = xive_nvt_idx(cam); + } + if (vp) { + *vp = !!(cam & TM_QW2W2_VP); + } +} + +static uint32_t xive_tctx_get_pool_cam(XiveTCTX *tctx, uint8_t *nvt_blk, + uint32_t *nvt_idx, bool *vp) +{ + uint32_t qw2w2 = xive_tctx_word2(&tctx->regs[TM_QW2_HV_POOL]); + uint32_t cam = be32_to_cpu(qw2w2); + + xive_pool_cam_decode(cam, nvt_blk, nvt_idx, vp); + return qw2w2; +} + +static void xive_tctx_set_pool_cam(XiveTCTX *tctx, uint32_t qw2w2) +{ + memcpy(&tctx->regs[TM_QW2_HV_POOL + TM_WORD2], &qw2w2, 4); +} + static uint64_t xive_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size) { - uint32_t qw2w2_prev = xive_tctx_word2(&tctx->regs[TM_QW2_HV_POOL]); uint32_t qw2w2; + uint32_t qw2w2_new; + uint8_t nvt_blk; + uint32_t nvt_idx; + bool vp; - qw2w2 = xive_set_field32(TM_QW2W2_VP, qw2w2_prev, 0); - memcpy(&tctx->regs[TM_QW2_HV_POOL + TM_WORD2], &qw2w2, 4); + qw2w2 = xive_tctx_get_pool_cam(tctx, &nvt_blk, &nvt_idx, &vp); + + if (!vp) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pull invalid POOL NVT %x/%x !?\n", + nvt_blk, nvt_idx); + } + + /* Invalidate CAM line */ + qw2w2_new = xive_set_field32(TM_QW2W2_VP, qw2w2, 0); + xive_tctx_set_pool_cam(tctx, qw2w2_new); + + xive_tctx_reset_signal(tctx, TM_QW1_OS); + xive_tctx_reset_signal(tctx, TM_QW2_HV_POOL); return qw2w2; } static uint64_t xive_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size) { - uint8_t qw3b8_prev = tctx->regs[TM_QW3_HV_PHYS + TM_WORD2]; - uint8_t qw3b8; + uint8_t qw3b8 = tctx->regs[TM_QW3_HV_PHYS + TM_WORD2]; + uint8_t qw3b8_new; + + qw3b8 = tctx->regs[TM_QW3_HV_PHYS + TM_WORD2]; + if (!(qw3b8 & TM_QW3B8_VT)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid PHYS thread!?\n"); + } + qw3b8_new = qw3b8 & ~TM_QW3B8_VT; + tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] = qw3b8_new; - qw3b8 = qw3b8_prev & ~TM_QW3B8_VT; - tctx->regs[TM_QW3_HV_PHYS + TM_WORD2] = qw3b8; + xive_tctx_reset_signal(tctx, TM_QW1_OS); + xive_tctx_reset_signal(tctx, TM_QW3_HV_PHYS); return qw3b8; } @@ -489,7 +539,7 @@ static uint64_t xive_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, qw1w2 = xive_tctx_get_os_cam(tctx, &nvt_blk, &nvt_idx, &vo); if (!vo) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVT %x/%x !?\n", + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pull invalid OS NVT %x/%x !?\n", nvt_blk, nvt_idx); } From c2cee7477f4af8cdfc33c5bb5928a3a1862655ee Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:26 +1000 Subject: [PATCH 2542/2760] pnv/xive2: Support ESB Escalation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for XIVE ESB Interrupt Escalation. Suggested-by: Michael Kowal [This change was taken from a patch provided by Michael Kowal.] Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-18-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 62 ++++++++++++++++++++++++++++++------- include/hw/ppc/xive2.h | 1 + include/hw/ppc/xive2_regs.h | 13 +++++--- 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index cca121b5f1..541b05225c 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1552,18 +1552,39 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, } } - /* - * The END trigger becomes an Escalation trigger - */ - xive2_router_end_notify(xrtr, - xive_get_field32(END2_W4_END_BLOCK, end.w4), - xive_get_field32(END2_W4_ESC_END_INDEX, end.w4), - xive_get_field32(END2_W5_ESC_END_DATA, end.w5)); + if (xive2_end_is_escalate_end(&end)) { + /* + * Perform END Adaptive escalation processing + * The END trigger becomes an Escalation trigger + */ + xive2_router_end_notify(xrtr, + xive_get_field32(END2_W4_END_BLOCK, end.w4), + xive_get_field32(END2_W4_ESC_END_INDEX, end.w4), + xive_get_field32(END2_W5_ESC_END_DATA, end.w5)); + } /* end END adaptive escalation */ + + else { + uint32_t lisn; /* Logical Interrupt Source Number */ + + /* + * Perform ESB escalation processing + * E[N] == 1 --> N + * Req[Block] <- E[ESB_Block] + * Req[Index] <- E[ESB_Index] + * Req[Offset] <- 0x000 + * Execute Req command + */ + lisn = XIVE_EAS(xive_get_field32(END2_W4_END_BLOCK, end.w4), + xive_get_field32(END2_W4_ESC_END_INDEX, end.w4)); + + xive2_notify(xrtr, lisn, true /* pq_checked */); + } + + return; } -void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) +void xive2_notify(Xive2Router *xrtr , uint32_t lisn, bool pq_checked) { - Xive2Router *xrtr = XIVE2_ROUTER(xn); uint8_t eas_blk = XIVE_EAS_BLOCK(lisn); uint32_t eas_idx = XIVE_EAS_INDEX(lisn); Xive2Eas eas; @@ -1606,13 +1627,30 @@ void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) return; } + /* TODO: add support for EAS resume */ + if (xive2_eas_is_resume(&eas)) { + qemu_log_mask(LOG_UNIMP, + "XIVE: EAS resume processing unimplemented - LISN %x\n", + lisn); + return; + } + /* * The event trigger becomes an END trigger */ xive2_router_end_notify(xrtr, - xive_get_field64(EAS2_END_BLOCK, eas.w), - xive_get_field64(EAS2_END_INDEX, eas.w), - xive_get_field64(EAS2_END_DATA, eas.w)); + xive_get_field64(EAS2_END_BLOCK, eas.w), + xive_get_field64(EAS2_END_INDEX, eas.w), + xive_get_field64(EAS2_END_DATA, eas.w)); + return; +} + +void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xn); + + xive2_notify(xrtr, lisn, pq_checked); + return; } static const Property xive2_router_properties[] = { diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index 8cdf819174..2436ddb5e5 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -80,6 +80,7 @@ int xive2_router_write_nvgc(Xive2Router *xrtr, bool crowd, uint32_t xive2_router_get_config(Xive2Router *xrtr); void xive2_router_notify(XiveNotifier *xn, uint32_t lisn, bool pq_checked); +void xive2_notify(Xive2Router *xrtr, uint32_t lisn, bool pq_checked); /* * XIVE2 Presenter (POWER10) diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 3c28de8a30..2c535ec0d0 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -39,15 +39,18 @@ typedef struct Xive2Eas { uint64_t w; -#define EAS2_VALID PPC_BIT(0) -#define EAS2_END_BLOCK PPC_BITMASK(4, 7) /* Destination EQ block# */ -#define EAS2_END_INDEX PPC_BITMASK(8, 31) /* Destination EQ index */ -#define EAS2_MASKED PPC_BIT(32) /* Masked */ -#define EAS2_END_DATA PPC_BITMASK(33, 63) /* written to the EQ */ +#define EAS2_VALID PPC_BIT(0) +#define EAS2_QOS PPC_BIT(1, 2) /* Quality of Service(unimp) */ +#define EAS2_RESUME PPC_BIT(3) /* END Resume(unimp) */ +#define EAS2_END_BLOCK PPC_BITMASK(4, 7) /* Destination EQ block# */ +#define EAS2_END_INDEX PPC_BITMASK(8, 31) /* Destination EQ index */ +#define EAS2_MASKED PPC_BIT(32) /* Masked */ +#define EAS2_END_DATA PPC_BITMASK(33, 63) /* written to the EQ */ } Xive2Eas; #define xive2_eas_is_valid(eas) (be64_to_cpu((eas)->w) & EAS2_VALID) #define xive2_eas_is_masked(eas) (be64_to_cpu((eas)->w) & EAS2_MASKED) +#define xive2_eas_is_resume(eas) (be64_to_cpu((eas)->w) & EAS2_RESUME) void xive2_eas_pic_print_info(Xive2Eas *eas, uint32_t lisn, GString *buf); From 14cbb7bf1245c2d9166be0309c9407845783b281 Mon Sep 17 00:00:00 2001 From: Michael Kowal Date: Mon, 12 May 2025 13:10:27 +1000 Subject: [PATCH 2543/2760] pnv/xive2: Print value in invalid register write logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This can make it easier to see what the target system is trying to do. [npiggin: split from larger patch] Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-19-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 25dc8a372d..9d53537e3e 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1197,7 +1197,8 @@ static void pnv_xive2_ic_cq_write(void *opaque, hwaddr offset, case CQ_FIRMASK_OR: /* FIR error reporting */ break; default: - xive2_error(xive, "CQ: invalid write 0x%"HWADDR_PRIx, offset); + xive2_error(xive, "CQ: invalid write 0x%"HWADDR_PRIx" value 0x%"PRIx64, + offset, val); return; } @@ -1495,7 +1496,8 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, break; default: - xive2_error(xive, "VC: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "VC: invalid write @0x%"HWADDR_PRIx" value 0x%"PRIx64, + offset, val); return; } @@ -1703,7 +1705,8 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, break; default: - xive2_error(xive, "PC: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "PC: invalid write @0x%"HWADDR_PRIx" value 0x%"PRIx64, + offset, val); return; } @@ -1790,7 +1793,8 @@ static void pnv_xive2_ic_tctxt_write(void *opaque, hwaddr offset, xive->tctxt_regs[reg] = val; break; default: - xive2_error(xive, "TCTXT: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "TCTXT: invalid write @0x%"HWADDR_PRIx + " data 0x%"PRIx64, offset, val); return; } } @@ -1861,7 +1865,8 @@ static void pnv_xive2_xscom_write(void *opaque, hwaddr offset, pnv_xive2_ic_tctxt_write(opaque, mmio_offset, val, size); break; default: - xive2_error(xive, "XSCOM: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "XSCOM: invalid write @%"HWADDR_PRIx + " value 0x%"PRIx64, offset, val); } } @@ -1929,7 +1934,8 @@ static void pnv_xive2_ic_notify_write(void *opaque, hwaddr offset, break; default: - xive2_error(xive, "NOTIFY: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "NOTIFY: invalid write @%"HWADDR_PRIx + " value 0x%"PRIx64, offset, val); } } @@ -1971,7 +1977,8 @@ static void pnv_xive2_ic_lsi_write(void *opaque, hwaddr offset, { PnvXive2 *xive = PNV_XIVE2(opaque); - xive2_error(xive, "LSI: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "LSI: invalid write @%"HWADDR_PRIx" value 0x%"PRIx64, + offset, val); } static const MemoryRegionOps pnv_xive2_ic_lsi_ops = { @@ -2074,7 +2081,8 @@ static void pnv_xive2_ic_sync_write(void *opaque, hwaddr offset, inject_type = PNV_XIVE2_QUEUE_NXC_ST_RMT_CI; break; default: - xive2_error(xive, "SYNC: invalid write @%"HWADDR_PRIx, offset); + xive2_error(xive, "SYNC: invalid write @%"HWADDR_PRIx" value 0x%"PRIx64, + offset, val); return; } From d273abbfba47ef47e331dc22731c7d2a15e95c9a Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:28 +1000 Subject: [PATCH 2544/2760] pnv/xive2: VC_ENDC_WATCH_SPEC regs should read back WATCH_FULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Firmware expects to read back the WATCH_FULL bit from the VC_ENDC_WATCH_SPEC register, so don't clear it on read. Don't bother clearing the reads-as-zero CONFLICT bit because it's masked at write already. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-20-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 9d53537e3e..e15f414d0b 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1329,7 +1329,6 @@ static uint64_t pnv_xive2_ic_vc_read(void *opaque, hwaddr offset, case VC_ENDC_WATCH2_SPEC: case VC_ENDC_WATCH3_SPEC: watch_engine = (offset - VC_ENDC_WATCH0_SPEC) >> 6; - xive->vc_regs[reg] &= ~(VC_ENDC_WATCH_FULL | VC_ENDC_WATCH_CONFLICT); pnv_xive2_endc_cache_watch_release(xive, watch_engine); val = xive->vc_regs[reg]; break; From 9898cc80306588eb6a1c44b7ba38f207ca8bdcfa Mon Sep 17 00:00:00 2001 From: Michael Kowal Date: Mon, 12 May 2025 13:10:29 +1000 Subject: [PATCH 2545/2760] pnv/xive2: Permit valid writes to VC/PC Flush Control registers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Writes to the Flush Control registers were logged as invalid when they are allowed. Clearing the unsupported want_cache_disable feature is supported, so don't log an error in that case. Signed-off-by: Michael Kowal Reviewed-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Reviewed-by: Caleb Schlossin Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-21-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index e15f414d0b..386175a68b 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -1411,7 +1411,14 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* * ESB cache updates (not modeled) */ - /* case VC_ESBC_FLUSH_CTRL: */ + case VC_ESBC_FLUSH_CTRL: + if (val & VC_ESBC_FLUSH_CTRL_WANT_CACHE_DISABLE) { + xive2_error(xive, "VC: unsupported write @0x%"HWADDR_PRIx + " value 0x%"PRIx64" bit[2] poll_want_cache_disable", + offset, val); + return; + } + break; case VC_ESBC_FLUSH_POLL: xive->vc_regs[VC_ESBC_FLUSH_CTRL >> 3] |= VC_ESBC_FLUSH_CTRL_POLL_VALID; /* ESB update */ @@ -1427,7 +1434,14 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, /* * EAS cache updates (not modeled) */ - /* case VC_EASC_FLUSH_CTRL: */ + case VC_EASC_FLUSH_CTRL: + if (val & VC_EASC_FLUSH_CTRL_WANT_CACHE_DISABLE) { + xive2_error(xive, "VC: unsupported write @0x%"HWADDR_PRIx + " value 0x%"PRIx64" bit[2] poll_want_cache_disable", + offset, val); + return; + } + break; case VC_EASC_FLUSH_POLL: xive->vc_regs[VC_EASC_FLUSH_CTRL >> 3] |= VC_EASC_FLUSH_CTRL_POLL_VALID; /* EAS update */ @@ -1466,7 +1480,14 @@ static void pnv_xive2_ic_vc_write(void *opaque, hwaddr offset, break; - /* case VC_ENDC_FLUSH_CTRL: */ + case VC_ENDC_FLUSH_CTRL: + if (val & VC_ENDC_FLUSH_CTRL_WANT_CACHE_DISABLE) { + xive2_error(xive, "VC: unsupported write @0x%"HWADDR_PRIx + " value 0x%"PRIx64" bit[2] poll_want_cache_disable", + offset, val); + return; + } + break; case VC_ENDC_FLUSH_POLL: xive->vc_regs[VC_ENDC_FLUSH_CTRL >> 3] |= VC_ENDC_FLUSH_CTRL_POLL_VALID; break; @@ -1687,7 +1708,14 @@ static void pnv_xive2_ic_pc_write(void *opaque, hwaddr offset, pnv_xive2_nxc_update(xive, watch_engine); break; - /* case PC_NXC_FLUSH_CTRL: */ + case PC_NXC_FLUSH_CTRL: + if (val & PC_NXC_FLUSH_CTRL_WANT_CACHE_DISABLE) { + xive2_error(xive, "VC: unsupported write @0x%"HWADDR_PRIx + " value 0x%"PRIx64" bit[2] poll_want_cache_disable", + offset, val); + return; + } + break; case PC_NXC_FLUSH_POLL: xive->pc_regs[PC_NXC_FLUSH_CTRL >> 3] |= PC_NXC_FLUSH_CTRL_POLL_VALID; break; From b22ffb42999504614a1bef3a52c5b2e6549e8de6 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:30 +1000 Subject: [PATCH 2546/2760] ppc/xive2: add interrupt priority configuration flags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds support for extracting additional configuration flags from the XIVE configuration register that are needed for redistribution of group interrupts. Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-22-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive2.c | 16 ++++++++++++---- hw/intc/pnv_xive2_regs.h | 1 + include/hw/ppc/xive2.h | 8 +++++--- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 386175a68b..7b4a33228e 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -605,20 +605,28 @@ static uint32_t pnv_xive2_get_config(Xive2Router *xrtr) { PnvXive2 *xive = PNV_XIVE2(xrtr); uint32_t cfg = 0; + uint64_t reg = xive->cq_regs[CQ_XIVE_CFG >> 3]; - if (xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS) { + if (reg & CQ_XIVE_CFG_GEN1_TIMA_OS) { cfg |= XIVE2_GEN1_TIMA_OS; } - if (xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_EN_VP_SAVE_RESTORE) { + if (reg & CQ_XIVE_CFG_EN_VP_SAVE_RESTORE) { cfg |= XIVE2_VP_SAVE_RESTORE; } - if (GETFIELD(CQ_XIVE_CFG_HYP_HARD_RANGE, - xive->cq_regs[CQ_XIVE_CFG >> 3]) == CQ_XIVE_CFG_THREADID_8BITS) { + if (GETFIELD(CQ_XIVE_CFG_HYP_HARD_RANGE, reg) == + CQ_XIVE_CFG_THREADID_8BITS) { cfg |= XIVE2_THREADID_8BITS; } + if (reg & CQ_XIVE_CFG_EN_VP_GRP_PRIORITY) { + cfg |= XIVE2_EN_VP_GRP_PRIORITY; + } + + cfg = SETFIELD(XIVE2_VP_INT_PRIO, cfg, + GETFIELD(CQ_XIVE_CFG_VP_INT_PRIO, reg)); + return cfg; } diff --git a/hw/intc/pnv_xive2_regs.h b/hw/intc/pnv_xive2_regs.h index e8b87b3d2c..d53300f709 100644 --- a/hw/intc/pnv_xive2_regs.h +++ b/hw/intc/pnv_xive2_regs.h @@ -66,6 +66,7 @@ #define CQ_XIVE_CFG_GEN1_TIMA_HYP_BLK0 PPC_BIT(26) /* 0 if bit[25]=0 */ #define CQ_XIVE_CFG_GEN1_TIMA_CROWD_DIS PPC_BIT(27) /* 0 if bit[25]=0 */ #define CQ_XIVE_CFG_GEN1_END_ESX PPC_BIT(28) +#define CQ_XIVE_CFG_EN_VP_GRP_PRIORITY PPC_BIT(32) /* 0 if bit[25]=1 */ #define CQ_XIVE_CFG_EN_VP_SAVE_RESTORE PPC_BIT(38) /* 0 if bit[25]=1 */ #define CQ_XIVE_CFG_EN_VP_SAVE_REST_STRICT PPC_BIT(39) /* 0 if bit[25]=1 */ diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index 2436ddb5e5..760b94a962 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -29,9 +29,11 @@ OBJECT_DECLARE_TYPE(Xive2Router, Xive2RouterClass, XIVE2_ROUTER); * Configuration flags */ -#define XIVE2_GEN1_TIMA_OS 0x00000001 -#define XIVE2_VP_SAVE_RESTORE 0x00000002 -#define XIVE2_THREADID_8BITS 0x00000004 +#define XIVE2_GEN1_TIMA_OS 0x00000001 +#define XIVE2_VP_SAVE_RESTORE 0x00000002 +#define XIVE2_THREADID_8BITS 0x00000004 +#define XIVE2_EN_VP_GRP_PRIORITY 0x00000008 +#define XIVE2_VP_INT_PRIO 0x00000030 typedef struct Xive2RouterClass { SysBusDeviceClass parent; From 555e446019f58e488ccf9fc416667be450e3f32f Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:31 +1000 Subject: [PATCH 2547/2760] ppc/xive2: Support redistribution of group interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When an XIVE context is pulled while it has an active, unacknowledged group interrupt, XIVE will check to see if a context on another thread can handle the interrupt and, if so, notify that context. If there are no contexts that can handle the interrupt, then the interrupt is added to a backlog and XIVE will attempt to escalate the interrupt, if configured to do so, allowing the higher privileged handler to activate a context that can handle the original interrupt. Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-23-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 84 +++++++++++++++++++++++++++++++++++-- include/hw/ppc/xive2_regs.h | 3 ++ 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 541b05225c..9ef372b6d1 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -19,6 +19,10 @@ #include "hw/ppc/xive2_regs.h" #include "trace.h" +static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, + uint32_t end_idx, uint32_t end_data, + bool redistribute); + uint32_t xive2_router_get_config(Xive2Router *xrtr) { Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); @@ -597,6 +601,68 @@ static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) return xive2_nvp_cam_line(blk, 1 << tid_shift | (pir & tid_mask)); } +static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, + uint8_t nvp_blk, uint32_t nvp_idx, uint8_t ring) +{ + uint8_t nsr = tctx->regs[ring + TM_NSR]; + uint8_t crowd = NVx_CROWD_LVL(nsr); + uint8_t group = NVx_GROUP_LVL(nsr); + uint8_t nvgc_blk; + uint8_t nvgc_idx; + uint8_t end_blk; + uint32_t end_idx; + uint8_t pipr = tctx->regs[ring + TM_PIPR]; + Xive2Nvgc nvgc; + uint8_t prio_limit; + uint32_t cfg; + + /* convert crowd/group to blk/idx */ + if (group > 0) { + nvgc_idx = (nvp_idx & (0xffffffff << group)) | + ((1 << (group - 1)) - 1); + } else { + nvgc_idx = nvp_idx; + } + + if (crowd > 0) { + crowd = (crowd == 3) ? 4 : crowd; + nvgc_blk = (nvp_blk & (0xffffffff << crowd)) | + ((1 << (crowd - 1)) - 1); + } else { + nvgc_blk = nvp_blk; + } + + /* Use blk/idx to retrieve the NVGC */ + if (xive2_router_get_nvgc(xrtr, crowd, nvgc_blk, nvgc_idx, &nvgc)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: no %s %x/%x\n", + crowd ? "NVC" : "NVG", nvgc_blk, nvgc_idx); + return; + } + + /* retrieve the END blk/idx from the NVGC */ + end_blk = xive_get_field32(NVGC2_W1_END_BLK, nvgc.w1); + end_idx = xive_get_field32(NVGC2_W1_END_IDX, nvgc.w1); + + /* determine number of priorities being used */ + cfg = xive2_router_get_config(xrtr); + if (cfg & XIVE2_EN_VP_GRP_PRIORITY) { + prio_limit = 1 << GETFIELD(NVGC2_W1_PSIZE, nvgc.w1); + } else { + prio_limit = 1 << GETFIELD(XIVE2_VP_INT_PRIO, cfg); + } + + /* add priority offset to end index */ + end_idx += pipr % prio_limit; + + /* trigger the group END */ + xive2_router_end_notify(xrtr, end_blk, end_idx, 0, true); + + /* clear interrupt indication for the context */ + tctx->regs[ring + TM_NSR] = 0; + tctx->regs[ring + TM_PIPR] = tctx->regs[ring + TM_CPPR]; + xive_tctx_reset_signal(tctx, ring); +} + static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size, uint8_t ring) { @@ -608,6 +674,7 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, uint8_t cur_ring; bool valid; bool do_save; + uint8_t nsr; xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &valid, &do_save); @@ -624,6 +691,12 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, memcpy(&tctx->regs[cur_ring + TM_WORD2], &ringw2_new, 4); } + /* Active group/crowd interrupts need to be redistributed */ + nsr = tctx->regs[ring + TM_NSR]; + if (xive_nsr_indicates_group_exception(ring, nsr)) { + xive2_redistribute(xrtr, tctx, nvp_blk, nvp_idx, ring); + } + if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { xive2_tctx_save_ctx(xrtr, tctx, nvp_blk, nvp_idx, ring); } @@ -1352,7 +1425,8 @@ static bool xive2_router_end_es_notify(Xive2Router *xrtr, uint8_t end_blk, * message has the same parameters than in the function below. */ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, - uint32_t end_idx, uint32_t end_data) + uint32_t end_idx, uint32_t end_data, + bool redistribute) { Xive2End end; uint8_t priority; @@ -1380,7 +1454,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, return; } - if (xive2_end_is_enqueue(&end)) { + if (!redistribute && xive2_end_is_enqueue(&end)) { xive2_end_enqueue(&end, end_data); /* Enqueuing event data modifies the EQ toggle and index */ xive2_router_write_end(xrtr, end_blk, end_idx, &end, 1); @@ -1560,7 +1634,8 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, xive2_router_end_notify(xrtr, xive_get_field32(END2_W4_END_BLOCK, end.w4), xive_get_field32(END2_W4_ESC_END_INDEX, end.w4), - xive_get_field32(END2_W5_ESC_END_DATA, end.w5)); + xive_get_field32(END2_W5_ESC_END_DATA, end.w5), + false); } /* end END adaptive escalation */ else { @@ -1641,7 +1716,8 @@ void xive2_notify(Xive2Router *xrtr , uint32_t lisn, bool pq_checked) xive2_router_end_notify(xrtr, xive_get_field64(EAS2_END_BLOCK, eas.w), xive_get_field64(EAS2_END_INDEX, eas.w), - xive_get_field64(EAS2_END_DATA, eas.w)); + xive_get_field64(EAS2_END_DATA, eas.w), + false); return; } diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index 2c535ec0d0..e222038143 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -224,6 +224,9 @@ typedef struct Xive2Nvgc { #define NVGC2_W0_VALID PPC_BIT32(0) #define NVGC2_W0_PGONEXT PPC_BITMASK32(26, 31) uint32_t w1; +#define NVGC2_W1_PSIZE PPC_BITMASK32(0, 1) +#define NVGC2_W1_END_BLK PPC_BITMASK32(4, 7) +#define NVGC2_W1_END_IDX PPC_BITMASK32(8, 31) uint32_t w2; uint32_t w3; uint32_t w4; From 701ab1857a9175a86e3ad6f18958df631af86a62 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:32 +1000 Subject: [PATCH 2548/2760] ppc/xive: Add more interrupt notification tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add more tracing around notification, redistribution, and escalation. Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-24-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/trace-events | 6 ++++++ hw/intc/xive.c | 3 +++ hw/intc/xive2.c | 13 ++++++++----- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/hw/intc/trace-events b/hw/intc/trace-events index 9ed2616e58..018c609ca5 100644 --- a/hw/intc/trace-events +++ b/hw/intc/trace-events @@ -279,6 +279,8 @@ xive_tctx_notify(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_ xive_tctx_set_cppr(uint32_t index, uint8_t ring, uint8_t ipb, uint8_t pipr, uint8_t cppr, uint8_t nsr) "target=%d ring=0x%x IPB=0x%02x PIPR=0x%02x new CPPR=0x%02x NSR=0x%02x" xive_source_esb_read(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64 xive_source_esb_write(uint64_t addr, uint32_t srcno, uint64_t value) "@0x%"PRIx64" IRQ 0x%x val=0x%"PRIx64 +xive_source_notify(uint32_t srcno) "Processing notification for queued IRQ 0x%x" +xive_source_blocked(uint32_t srcno) "No action needed for IRQ 0x%x currently" xive_router_end_notify(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "END 0x%02x/0x%04x -> enqueue 0x%08x" xive_router_end_escalate(uint8_t end_blk, uint32_t end_idx, uint8_t esc_blk, uint32_t esc_idx, uint32_t end_data) "END 0x%02x/0x%04x -> escalate END 0x%02x/0x%04x data 0x%08x" xive_tctx_tm_write(uint32_t index, uint64_t offset, unsigned int size, uint64_t value) "target=%d @0x%"PRIx64" sz=%d val=0x%" PRIx64 @@ -289,6 +291,10 @@ xive_end_source_read(uint8_t end_blk, uint32_t end_idx, uint64_t addr) "END 0x%x # xive2.c xive_nvp_backlog_op(uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint8_t rc) "NVP 0x%x/0x%x operation=%d priority=%d rc=%d" xive_nvgc_backlog_op(bool c, uint8_t blk, uint32_t idx, uint8_t op, uint8_t priority, uint32_t rc) "NVGC crowd=%d 0x%x/0x%x operation=%d priority=%d rc=%d" +xive_redistribute(uint32_t index, uint8_t ring, uint8_t end_blk, uint32_t end_idx) "Redistribute from target=%d ring=0x%x NVP 0x%x/0x%x" +xive_end_enqueue(uint8_t end_blk, uint32_t end_idx, uint32_t end_data) "Queue event for END 0x%x/0x%x data=0x%x" +xive_escalate_end(uint8_t end_blk, uint32_t end_idx, uint8_t esc_blk, uint32_t esc_idx, uint32_t esc_data) "Escalate from END 0x%x/0x%x to END 0x%x/0x%x data=0x%x" +xive_escalate_esb(uint8_t end_blk, uint32_t end_idx, uint32_t lisn) "Escalate from END 0x%x/0x%x to LISN=0x%x" # pnv_xive.c pnv_xive_ic_hw_trigger(uint64_t addr, uint64_t val) "@0x%"PRIx64" val=0x%"PRIx64 diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 5483a815ef..d653946516 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -1276,6 +1276,7 @@ static uint64_t xive_source_esb_read(void *opaque, hwaddr addr, unsigned size) /* Forward the source event notification for routing */ if (ret) { + trace_xive_source_notify(srcno); xive_source_notify(xsrc, srcno); } break; @@ -1371,6 +1372,8 @@ static void xive_source_esb_write(void *opaque, hwaddr addr, /* Forward the source event notification for routing */ if (notify) { xive_source_notify(xsrc, srcno); + } else { + trace_xive_source_blocked(srcno); } } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 9ef372b6d1..f810e716de 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -616,6 +616,7 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t prio_limit; uint32_t cfg; + trace_xive_redistribute(tctx->cs->cpu_index, ring, nvp_blk, nvp_idx); /* convert crowd/group to blk/idx */ if (group > 0) { nvgc_idx = (nvp_idx & (0xffffffff << group)) | @@ -1455,6 +1456,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, } if (!redistribute && xive2_end_is_enqueue(&end)) { + trace_xive_end_enqueue(end_blk, end_idx, end_data); xive2_end_enqueue(&end, end_data); /* Enqueuing event data modifies the EQ toggle and index */ xive2_router_write_end(xrtr, end_blk, end_idx, &end, 1); @@ -1631,11 +1633,11 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, * Perform END Adaptive escalation processing * The END trigger becomes an Escalation trigger */ - xive2_router_end_notify(xrtr, - xive_get_field32(END2_W4_END_BLOCK, end.w4), - xive_get_field32(END2_W4_ESC_END_INDEX, end.w4), - xive_get_field32(END2_W5_ESC_END_DATA, end.w5), - false); + uint8_t esc_blk = xive_get_field32(END2_W4_END_BLOCK, end.w4); + uint32_t esc_idx = xive_get_field32(END2_W4_ESC_END_INDEX, end.w4); + uint32_t esc_data = xive_get_field32(END2_W5_ESC_END_DATA, end.w5); + trace_xive_escalate_end(end_blk, end_idx, esc_blk, esc_idx, esc_data); + xive2_router_end_notify(xrtr, esc_blk, esc_idx, esc_data, false); } /* end END adaptive escalation */ else { @@ -1652,6 +1654,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, lisn = XIVE_EAS(xive_get_field32(END2_W4_END_BLOCK, end.w4), xive_get_field32(END2_W4_ESC_END_INDEX, end.w4)); + trace_xive_escalate_esb(end_blk, end_idx, lisn); xive2_notify(xrtr, lisn, true /* pq_checked */); } From 1a0cd94252bf111b0ace7b9cd88258e837d95ea4 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:33 +1000 Subject: [PATCH 2549/2760] ppc/xive2: Improve pool regs variable name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change pregs to pool_regs, for clarity. [npiggin: split from larger patch] Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-25-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index f810e716de..1f4713dabe 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1044,13 +1044,12 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) /* PHYS updates also depend on POOL values */ if (ring == TM_QW3_HV_PHYS) { - uint8_t *pregs = &tctx->regs[TM_QW2_HV_POOL]; + uint8_t *pool_regs = &tctx->regs[TM_QW2_HV_POOL]; /* POOL values only matter if POOL ctx is valid */ - if (pregs[TM_WORD2] & 0x80) { - - uint8_t pool_pipr = xive_ipb_to_pipr(pregs[TM_IPB]); - uint8_t pool_lsmfb = pregs[TM_LSMFB]; + if (pool_regs[TM_WORD2] & 0x80) { + uint8_t pool_pipr = xive_ipb_to_pipr(pool_regs[TM_IPB]); + uint8_t pool_lsmfb = pool_regs[TM_LSMFB]; /* * Determine highest priority interrupt and @@ -1064,7 +1063,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } /* Values needed for group priority calculation */ - if (pregs[TM_LGS] && (pool_lsmfb < lsmfb_min)) { + if (pool_regs[TM_LGS] && (pool_lsmfb < lsmfb_min)) { group_enabled = true; lsmfb_min = pool_lsmfb; if (lsmfb_min < pipr_min) { From 97cd373e6ca0024132e78496b8a585c00531f7a4 Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:34 +1000 Subject: [PATCH 2550/2760] ppc/xive2: Implement "Ack OS IRQ to even report line" TIMA op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Booting AIX in a PowerVM partition requires the use of the "Acknowledge O/S Interrupt to even O/S reporting line" special operation provided by the IBM XIVE interrupt controller. This operation is invoked by writing a byte (data is irrelevant) to offset 0xC10 of the Thread Interrupt Management Area (TIMA). It can be used by software to notify the XIVE logic that the interrupt was received. Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-26-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 8 ++++--- hw/intc/xive2.c | 50 ++++++++++++++++++++++++++++++++++++++++++ include/hw/ppc/xive.h | 1 + include/hw/ppc/xive2.h | 3 ++- 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index d653946516..8b705727ae 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -80,7 +80,7 @@ static qemu_irq xive_tctx_output(XiveTCTX *tctx, uint8_t ring) } } -static uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) +uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) { uint8_t *regs = &tctx->regs[ring]; uint8_t nsr = regs[TM_NSR]; @@ -340,14 +340,14 @@ static uint64_t xive_tm_vt_poll(XivePresenter *xptr, XiveTCTX *tctx, static const uint8_t xive_tm_hw_view[] = { 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-0 User */ - 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-1 OS */ + 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 3, /* QW-1 OS */ 0, 0, 3, 3, 0, 3, 3, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-2 POOL */ 3, 3, 3, 3, 0, 3, 0, 2, 3, 0, 0, 3, 3, 3, 3, 0, /* QW-3 PHYS */ }; static const uint8_t xive_tm_hv_view[] = { 3, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-0 User */ - 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 0, /* QW-1 OS */ + 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 3, 3, 0, 0, 0, 3, /* QW-1 OS */ 0, 0, 3, 3, 0, 3, 3, 0, 0, 3, 3, 3, 0, 0, 0, 0, /* QW-2 POOL */ 3, 3, 3, 3, 0, 3, 0, 2, 3, 0, 0, 3, 0, 0, 0, 0, /* QW-3 PHYS */ }; @@ -718,6 +718,8 @@ static const XiveTmOp xive2_tm_operations[] = { xive_tm_pull_phys_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_OL, 1, xive2_tm_pull_phys_ctx_ol, NULL }, + { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_EL, 1, xive2_tm_ack_os_el, + NULL }, }; static const XiveTmOp *xive_tm_find_op(XivePresenter *xptr, hwaddr offset, diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 1f4713dabe..e7e364c13e 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1009,6 +1009,56 @@ static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring, return 0; } +static void xive2_tctx_accept_el(XivePresenter *xptr, XiveTCTX *tctx, + uint8_t ring, uint8_t cl_ring) +{ + uint64_t rd; + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint32_t nvp_blk, nvp_idx, xive2_cfg; + Xive2Nvp nvp; + uint64_t phys_addr; + uint8_t OGen = 0; + + xive2_tctx_get_nvp_indexes(tctx, cl_ring, &nvp_blk, &nvp_idx); + + if (xive2_router_get_nvp(xrtr, (uint8_t)nvp_blk, nvp_idx, &nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + if (!xive2_nvp_is_valid(&nvp)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid NVP %x/%x\n", + nvp_blk, nvp_idx); + return; + } + + + rd = xive_tctx_accept(tctx, ring); + + if (ring == TM_QW1_OS) { + OGen = tctx->regs[ring + TM_OGEN]; + } + xive2_cfg = xive2_router_get_config(xrtr); + phys_addr = xive2_nvp_reporting_addr(&nvp); + uint8_t report_data[REPORT_LINE_GEN1_SIZE]; + memset(report_data, 0xff, sizeof(report_data)); + if ((OGen == 1) || (xive2_cfg & XIVE2_GEN1_TIMA_OS)) { + report_data[8] = (rd >> 8) & 0xff; + report_data[9] = rd & 0xff; + } else { + report_data[0] = (rd >> 8) & 0xff; + report_data[1] = rd & 0xff; + } + cpu_physical_memory_write(phys_addr, report_data, REPORT_LINE_GEN1_SIZE); +} + +void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tctx_accept_el(xptr, tctx, TM_QW1_OS, TM_QW1_OS); +} + static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) { uint8_t *regs = &tctx->regs[ring]; diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 28f0f1b79a..46d05d74fb 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -561,6 +561,7 @@ void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level); void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring); void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level); +uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring); /* * KVM XIVE device helpers diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index 760b94a962..ff02ce2549 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -142,5 +142,6 @@ void xive2_tm_set_hv_target(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); - +void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); #endif /* PPC_XIVE2_H */ From 9ad30401ce9aefa319364cb8efdc6893a5bc20ad Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:35 +1000 Subject: [PATCH 2551/2760] ppc/xive2: Redistribute group interrupt precluded by CPPR update MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for redistributing a presented group interrupt if it is precluded as a result of changing the CPPR value. Without this, group interrupts can be lost. Signed-off-by: Glenn Miles Reviewed-by: Nicholas Piggin Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-27-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 82 ++++++++++++++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 22 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index e7e364c13e..624620e5b4 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -601,20 +601,37 @@ static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) return xive2_nvp_cam_line(blk, 1 << tid_shift | (pir & tid_mask)); } -static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, - uint8_t nvp_blk, uint32_t nvp_idx, uint8_t ring) +static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) { - uint8_t nsr = tctx->regs[ring + TM_NSR]; + uint8_t *regs = &tctx->regs[ring]; + uint8_t nsr = regs[TM_NSR]; + uint8_t pipr = regs[TM_PIPR]; uint8_t crowd = NVx_CROWD_LVL(nsr); uint8_t group = NVx_GROUP_LVL(nsr); - uint8_t nvgc_blk; - uint8_t nvgc_idx; - uint8_t end_blk; - uint32_t end_idx; - uint8_t pipr = tctx->regs[ring + TM_PIPR]; + uint8_t nvgc_blk, end_blk, nvp_blk; + uint32_t nvgc_idx, end_idx, nvp_idx; Xive2Nvgc nvgc; uint8_t prio_limit; uint32_t cfg; + uint8_t alt_ring; + uint32_t target_ringw2; + uint32_t cam; + bool valid; + bool hw; + + /* redistribution is only for group/crowd interrupts */ + if (!xive_nsr_indicates_group_exception(ring, nsr)) { + return; + } + + alt_ring = xive_nsr_exception_ring(ring, nsr); + target_ringw2 = xive_tctx_word2(&tctx->regs[alt_ring]); + cam = be32_to_cpu(target_ringw2); + + /* extract nvp block and index from targeted ring's cam */ + xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &valid, &hw); + + trace_xive_redistribute(tctx->cs->cpu_index, alt_ring, nvp_blk, nvp_idx); trace_xive_redistribute(tctx->cs->cpu_index, ring, nvp_blk, nvp_idx); /* convert crowd/group to blk/idx */ @@ -659,8 +676,8 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, xive2_router_end_notify(xrtr, end_blk, end_idx, 0, true); /* clear interrupt indication for the context */ - tctx->regs[ring + TM_NSR] = 0; - tctx->regs[ring + TM_PIPR] = tctx->regs[ring + TM_CPPR]; + regs[TM_NSR] = 0; + regs[TM_PIPR] = regs[TM_CPPR]; xive_tctx_reset_signal(tctx, ring); } @@ -695,7 +712,7 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, /* Active group/crowd interrupts need to be redistributed */ nsr = tctx->regs[ring + TM_NSR]; if (xive_nsr_indicates_group_exception(ring, nsr)) { - xive2_redistribute(xrtr, tctx, nvp_blk, nvp_idx, ring); + xive2_redistribute(xrtr, tctx, ring); } if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { @@ -1059,6 +1076,7 @@ void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx, xive2_tctx_accept_el(xptr, tctx, TM_QW1_OS, TM_QW1_OS); } +/* NOTE: CPPR only exists for TM_QW1_OS and TM_QW3_HV_PHYS */ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) { uint8_t *regs = &tctx->regs[ring]; @@ -1069,10 +1087,11 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) uint32_t nvp_blk, nvp_idx; Xive2Nvp nvp; int rc; + uint8_t nsr = regs[TM_NSR]; trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring, regs[TM_IPB], regs[TM_PIPR], - cppr, regs[TM_NSR]); + cppr, nsr); if (cppr > XIVE_PRIORITY_MAX) { cppr = 0xff; @@ -1081,6 +1100,35 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) old_cppr = regs[TM_CPPR]; regs[TM_CPPR] = cppr; + /* Handle increased CPPR priority (lower value) */ + if (cppr < old_cppr) { + if (cppr <= regs[TM_PIPR]) { + /* CPPR lowered below PIPR, must un-present interrupt */ + if (xive_nsr_indicates_exception(ring, nsr)) { + if (xive_nsr_indicates_group_exception(ring, nsr)) { + /* redistribute precluded active grp interrupt */ + xive2_redistribute(xrtr, tctx, ring); + return; + } + } + + /* interrupt is VP directed, pending in IPB */ + regs[TM_PIPR] = cppr; + xive_tctx_notify(tctx, ring, 0); /* Ensure interrupt is cleared */ + return; + } else { + /* CPPR was lowered, but still above PIPR. No action needed. */ + return; + } + } + + /* CPPR didn't change, nothing needs to be done */ + if (cppr == old_cppr) { + return; + } + + /* CPPR priority decreased (higher value) */ + /* * Recompute the PIPR based on local pending interrupts. It will * be adjusted below if needed in case of pending group interrupts. @@ -1129,16 +1177,6 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) return; } - if (cppr < old_cppr) { - /* - * FIXME: check if there's a group interrupt being presented - * and if the new cppr prevents it. If so, then the group - * interrupt needs to be re-added to the backlog and - * re-triggered (see re-trigger END info in the NVGC - * structure) - */ - } - if (group_enabled && lsmfb_min < cppr && lsmfb_min < pipr_min) { From 64c772ca16fcb539380a777ff273278c42f5dfef Mon Sep 17 00:00:00 2001 From: Glenn Miles Date: Mon, 12 May 2025 13:10:36 +1000 Subject: [PATCH 2552/2760] ppc/xive2: redistribute irqs for pool and phys ctx pull MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When disabling (pulling) an xive interrupt context, we need to redistribute any active group interrupts to other threads that can handle the interrupt if possible. This support had already been added for the OS context but had not yet been added to the pool or physical context. Signed-off-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-28-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 12 ++--- hw/intc/xive2.c | 94 ++++++++++++++++++++++++++----------- include/hw/ppc/xive2.h | 4 ++ include/hw/ppc/xive2_regs.h | 4 +- 4 files changed, 79 insertions(+), 35 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 8b705727ae..2f72d6ecd5 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -693,7 +693,7 @@ static const XiveTmOp xive2_tm_operations[] = { /* MMIOs above 2K : special operations with side effects */ { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, NULL, - xive_tm_ack_os_reg }, + xive_tm_ack_os_reg }, { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, xive_tm_set_os_pending, NULL }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_G2, 4, NULL, @@ -705,17 +705,17 @@ static const XiveTmOp xive2_tm_operations[] = { { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, NULL, xive_tm_ack_hv_reg }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX_G2, 4, NULL, - xive_tm_pull_pool_ctx }, + xive2_tm_pull_pool_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, NULL, - xive_tm_pull_pool_ctx }, + xive2_tm_pull_pool_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL, - xive_tm_pull_pool_ctx }, + xive2_tm_pull_pool_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_OL, 1, xive2_tm_pull_os_ctx_ol, NULL }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_G2, 4, NULL, - xive_tm_pull_phys_ctx }, + xive2_tm_pull_phys_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, NULL, - xive_tm_pull_phys_ctx }, + xive2_tm_pull_phys_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_OL, 1, xive2_tm_pull_phys_ctx_ol, NULL }, { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_EL, 1, xive2_tm_ack_os_el, diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 624620e5b4..2791985cf2 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -23,6 +23,9 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, uint32_t end_idx, uint32_t end_data, bool redistribute); +static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring, + uint8_t *nvp_blk, uint32_t *nvp_idx); + uint32_t xive2_router_get_config(Xive2Router *xrtr) { Xive2RouterClass *xrc = XIVE2_ROUTER_GET_CLASS(xrtr); @@ -604,8 +607,10 @@ static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) { uint8_t *regs = &tctx->regs[ring]; - uint8_t nsr = regs[TM_NSR]; - uint8_t pipr = regs[TM_PIPR]; + uint8_t *alt_regs = (ring == TM_QW2_HV_POOL) ? &tctx->regs[TM_QW3_HV_PHYS] : + regs; + uint8_t nsr = alt_regs[TM_NSR]; + uint8_t pipr = alt_regs[TM_PIPR]; uint8_t crowd = NVx_CROWD_LVL(nsr); uint8_t group = NVx_GROUP_LVL(nsr); uint8_t nvgc_blk, end_blk, nvp_blk; @@ -614,10 +619,6 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) uint8_t prio_limit; uint32_t cfg; uint8_t alt_ring; - uint32_t target_ringw2; - uint32_t cam; - bool valid; - bool hw; /* redistribution is only for group/crowd interrupts */ if (!xive_nsr_indicates_group_exception(ring, nsr)) { @@ -625,11 +626,9 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) } alt_ring = xive_nsr_exception_ring(ring, nsr); - target_ringw2 = xive_tctx_word2(&tctx->regs[alt_ring]); - cam = be32_to_cpu(target_ringw2); - /* extract nvp block and index from targeted ring's cam */ - xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &valid, &hw); + /* Don't check return code since ring is expected to be invalidated */ + xive2_tctx_get_nvp_indexes(tctx, alt_ring, &nvp_blk, &nvp_idx); trace_xive_redistribute(tctx->cs->cpu_index, alt_ring, nvp_blk, nvp_idx); @@ -676,11 +675,23 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) xive2_router_end_notify(xrtr, end_blk, end_idx, 0, true); /* clear interrupt indication for the context */ - regs[TM_NSR] = 0; - regs[TM_PIPR] = regs[TM_CPPR]; + alt_regs[TM_NSR] = 0; + alt_regs[TM_PIPR] = alt_regs[TM_CPPR]; xive_tctx_reset_signal(tctx, ring); } +static uint8_t xive2_hv_irq_ring(uint8_t nsr) +{ + switch (nsr >> 6) { + case TM_QW3_NSR_HE_POOL: + return TM_QW2_HV_POOL; + case TM_QW3_NSR_HE_PHYS: + return TM_QW3_HV_PHYS; + default: + return -1; + } +} + static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size, uint8_t ring) { @@ -696,7 +707,7 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &valid, &do_save); - if (!valid) { + if (xive2_tctx_get_nvp_indexes(tctx, ring, &nvp_blk, &nvp_idx)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: pulling invalid NVP %x/%x !?\n", nvp_blk, nvp_idx); } @@ -706,13 +717,25 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, cur_ring += XIVE_TM_RING_SIZE) { uint32_t ringw2 = xive_tctx_word2(&tctx->regs[cur_ring]); uint32_t ringw2_new = xive_set_field32(TM2_QW1W2_VO, ringw2, 0); + bool is_valid = !!(xive_get_field32(TM2_QW1W2_VO, ringw2)); + uint8_t alt_ring; memcpy(&tctx->regs[cur_ring + TM_WORD2], &ringw2_new, 4); - } - /* Active group/crowd interrupts need to be redistributed */ - nsr = tctx->regs[ring + TM_NSR]; - if (xive_nsr_indicates_group_exception(ring, nsr)) { - xive2_redistribute(xrtr, tctx, ring); + /* Skip the rest for USER or invalid contexts */ + if ((cur_ring == TM_QW0_USER) || !is_valid) { + continue; + } + + /* Active group/crowd interrupts need to be redistributed */ + alt_ring = (cur_ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : cur_ring; + nsr = tctx->regs[alt_ring + TM_NSR]; + if (xive_nsr_indicates_group_exception(alt_ring, nsr)) { + /* For HV rings, only redistribute if cur_ring matches NSR */ + if ((cur_ring == TM_QW1_OS) || + (cur_ring == xive2_hv_irq_ring(nsr))) { + xive2_redistribute(xrtr, tctx, cur_ring); + } + } } if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { @@ -736,6 +759,18 @@ uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, return xive2_tm_pull_ctx(xptr, tctx, offset, size, TM_QW1_OS); } +uint64_t xive2_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size) +{ + return xive2_tm_pull_ctx(xptr, tctx, offset, size, TM_QW2_HV_POOL); +} + +uint64_t xive2_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size) +{ + return xive2_tm_pull_ctx(xptr, tctx, offset, size, TM_QW3_HV_PHYS); +} + #define REPORT_LINE_GEN1_SIZE 16 static void xive2_tm_report_line_gen1(XiveTCTX *tctx, uint8_t *data, @@ -993,37 +1028,40 @@ void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, } } +/* returns -1 if ring is invalid, but still populates block and index */ static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring, - uint32_t *nvp_blk, uint32_t *nvp_idx) + uint8_t *nvp_blk, uint32_t *nvp_idx) { - uint32_t w2, cam; + uint32_t w2; + uint32_t cam = 0; + int rc = 0; w2 = xive_tctx_word2(&tctx->regs[ring]); switch (ring) { case TM_QW1_OS: if (!(be32_to_cpu(w2) & TM2_QW1W2_VO)) { - return -1; + rc = -1; } cam = xive_get_field32(TM2_QW1W2_OS_CAM, w2); break; case TM_QW2_HV_POOL: if (!(be32_to_cpu(w2) & TM2_QW2W2_VP)) { - return -1; + rc = -1; } cam = xive_get_field32(TM2_QW2W2_POOL_CAM, w2); break; case TM_QW3_HV_PHYS: if (!(be32_to_cpu(w2) & TM2_QW3W2_VT)) { - return -1; + rc = -1; } cam = xive2_tctx_hw_cam_line(tctx->xptr, tctx); break; default: - return -1; + rc = -1; } *nvp_blk = xive2_nvp_blk(cam); *nvp_idx = xive2_nvp_idx(cam); - return 0; + return rc; } static void xive2_tctx_accept_el(XivePresenter *xptr, XiveTCTX *tctx, @@ -1031,7 +1069,8 @@ static void xive2_tctx_accept_el(XivePresenter *xptr, XiveTCTX *tctx, { uint64_t rd; Xive2Router *xrtr = XIVE2_ROUTER(xptr); - uint32_t nvp_blk, nvp_idx, xive2_cfg; + uint32_t nvp_idx, xive2_cfg; + uint8_t nvp_blk; Xive2Nvp nvp; uint64_t phys_addr; uint8_t OGen = 0; @@ -1084,7 +1123,8 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) uint8_t old_cppr, backlog_prio, first_group, group_level; uint8_t pipr_min, lsmfb_min, ring_min; bool group_enabled; - uint32_t nvp_blk, nvp_idx; + uint8_t nvp_blk; + uint32_t nvp_idx; Xive2Nvp nvp; int rc; uint8_t nsr = regs[TM_NSR]; diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index ff02ce2549..a91b99057c 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -140,6 +140,10 @@ bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority); void xive2_tm_set_lsmfb(XiveTCTX *tctx, int ring, uint8_t priority); void xive2_tm_set_hv_target(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); +uint64_t xive2_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size); +uint64_t xive2_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, unsigned size); void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx, diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index e222038143..f82054661b 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -209,9 +209,9 @@ static inline uint32_t xive2_nvp_idx(uint32_t cam_line) return cam_line & ((1 << XIVE2_NVP_SHIFT) - 1); } -static inline uint32_t xive2_nvp_blk(uint32_t cam_line) +static inline uint8_t xive2_nvp_blk(uint32_t cam_line) { - return (cam_line >> XIVE2_NVP_SHIFT) & 0xf; + return (uint8_t)((cam_line >> XIVE2_NVP_SHIFT) & 0xf); } void xive2_nvp_pic_print_info(Xive2Nvp *nvp, uint32_t nvp_idx, GString *buf); From 1319cb8997b9d0fdaae04a79398a2ed06d4cb5e1 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:37 +1000 Subject: [PATCH 2553/2760] ppc/xive: Change presenter .match_nvt to match not present MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have the match_nvt method only perform a TCTX match but don't present the interrupt, the caller presents. This has no functional change, but allows for more complicated presentation logic after matching. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-29-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/pnv_xive.c | 16 +++++++------- hw/intc/pnv_xive2.c | 16 +++++++------- hw/intc/spapr_xive.c | 18 +++++++-------- hw/intc/xive.c | 51 +++++++++++++++---------------------------- hw/intc/xive2.c | 31 +++++++++++++------------- hw/ppc/pnv.c | 48 ++++++++++++++-------------------------- hw/ppc/spapr.c | 21 +++++++----------- include/hw/ppc/xive.h | 27 +++++++++++++---------- 8 files changed, 97 insertions(+), 131 deletions(-) diff --git a/hw/intc/pnv_xive.c b/hw/intc/pnv_xive.c index 935c0e4742..c2ca40b8be 100644 --- a/hw/intc/pnv_xive.c +++ b/hw/intc/pnv_xive.c @@ -470,14 +470,13 @@ static bool pnv_xive_is_cpu_enabled(PnvXive *xive, PowerPCCPU *cpu) return xive->regs[reg >> 3] & PPC_BIT(bit); } -static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match) +static bool pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) { PnvXive *xive = PNV_XIVE(xptr); PnvChip *chip = xive->chip; - int count = 0; int i, j; for (i = 0; i < chip->nr_cores; i++) { @@ -510,17 +509,18 @@ static int pnv_xive_match_nvt(XivePresenter *xptr, uint8_t format, qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a " "thread context NVT %x/%x\n", nvt_blk, nvt_idx); - return -1; + match->count++; + continue; } match->ring = ring; match->tctx = tctx; - count++; + match->count++; } } } - return count; + return !!match->count; } static uint32_t pnv_xive_presenter_get_config(XivePresenter *xptr) diff --git a/hw/intc/pnv_xive2.c b/hw/intc/pnv_xive2.c index 7b4a33228e..e019cad5c1 100644 --- a/hw/intc/pnv_xive2.c +++ b/hw/intc/pnv_xive2.c @@ -640,14 +640,13 @@ static bool pnv_xive2_is_cpu_enabled(PnvXive2 *xive, PowerPCCPU *cpu) return xive->tctxt_regs[reg >> 3] & PPC_BIT(bit); } -static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match) +static bool pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) { PnvXive2 *xive = PNV_XIVE2(xptr); PnvChip *chip = xive->chip; - int count = 0; int i, j; bool gen1_tima_os = xive->cq_regs[CQ_XIVE_CFG >> 3] & CQ_XIVE_CFG_GEN1_TIMA_OS; @@ -692,7 +691,8 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, "thread context NVT %x/%x\n", nvt_blk, nvt_idx); /* Should set a FIR if we ever model it */ - return -1; + match->count++; + continue; } /* * For a group notification, we need to know if the @@ -717,13 +717,13 @@ static int pnv_xive2_match_nvt(XivePresenter *xptr, uint8_t format, } } } - count++; + match->count++; } } } } - return count; + return !!match->count; } static uint32_t pnv_xive2_presenter_get_config(XivePresenter *xptr) diff --git a/hw/intc/spapr_xive.c b/hw/intc/spapr_xive.c index 440edb97d8..e393f5dcdc 100644 --- a/hw/intc/spapr_xive.c +++ b/hw/intc/spapr_xive.c @@ -428,14 +428,13 @@ static int spapr_xive_write_nvt(XiveRouter *xrtr, uint8_t nvt_blk, g_assert_not_reached(); } -static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, - uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match) +static bool spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, + uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) { CPUState *cs; - int count = 0; CPU_FOREACH(cs) { PowerPCCPU *cpu = POWERPC_CPU(cs); @@ -463,16 +462,17 @@ static int spapr_xive_match_nvt(XivePresenter *xptr, uint8_t format, if (match->tctx) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: already found a thread " "context NVT %x/%x\n", nvt_blk, nvt_idx); - return -1; + match->count++; + continue; } match->ring = ring; match->tctx = tctx; - count++; + match->count++; } } - return count; + return !!match->count; } static uint32_t spapr_xive_presenter_get_config(XivePresenter *xptr) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 2f72d6ecd5..c92e819053 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -1762,8 +1762,8 @@ uint32_t xive_get_vpgroup_size(uint32_t nvp_index) return 1U << (first_zero + 1); } -static uint8_t xive_get_group_level(bool crowd, bool ignore, - uint32_t nvp_blk, uint32_t nvp_index) +uint8_t xive_get_group_level(bool crowd, bool ignore, + uint32_t nvp_blk, uint32_t nvp_index) { int first_zero; uint8_t level; @@ -1881,15 +1881,14 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, * This is our simple Xive Presenter Engine model. It is merged in the * Router as it does not require an extra object. */ -bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, +bool xive_presenter_match(XiveFabric *xfb, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, bool *precluded) + uint32_t logic_serv, XiveTCTXMatch *match) { XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xfb); - XiveTCTXMatch match = { .tctx = NULL, .ring = 0, .precluded = false }; - uint8_t group_level; - int count; + + memset(match, 0, sizeof(*match)); /* * Ask the machine to scan the interrupt controllers for a match. @@ -1914,22 +1913,8 @@ bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, * a new command to the presenters (the equivalent of the "assign" * power bus command in the documented full notify sequence. */ - count = xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, crowd, cam_ignore, - priority, logic_serv, &match); - if (count < 0) { - return false; - } - - /* handle CPU exception delivery */ - if (count) { - group_level = xive_get_group_level(crowd, cam_ignore, nvt_blk, nvt_idx); - trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, group_level); - xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level); - } else { - *precluded = match.precluded; - } - - return !!count; + return xfc->match_nvt(xfb, format, nvt_blk, nvt_idx, crowd, cam_ignore, + priority, logic_serv, match); } /* @@ -1966,7 +1951,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas) uint8_t nvt_blk; uint32_t nvt_idx; XiveNVT nvt; - bool found, precluded; + XiveTCTXMatch match; uint8_t end_blk = xive_get_field64(EAS_END_BLOCK, eas->w); uint32_t end_idx = xive_get_field64(EAS_END_INDEX, eas->w); @@ -2046,16 +2031,16 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas) return; } - found = xive_presenter_notify(xrtr->xfb, format, nvt_blk, nvt_idx, - false /* crowd */, - xive_get_field32(END_W7_F0_IGNORE, end.w7), - priority, - xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7), - &precluded); - /* we don't support VP-group notification on P9, so precluded is not used */ /* TODO: Auto EOI. */ - - if (found) { + /* we don't support VP-group notification on P9, so precluded is not used */ + if (xive_presenter_match(xrtr->xfb, format, nvt_blk, nvt_idx, + false /* crowd */, + xive_get_field32(END_W7_F0_IGNORE, end.w7), + priority, + xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7), + &match)) { + trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, 0); + xive_tctx_pipr_update(match.tctx, match.ring, priority, 0); return; } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 2791985cf2..602b23d06d 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1559,7 +1559,8 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, Xive2End end; uint8_t priority; uint8_t format; - bool found, precluded; + XiveTCTXMatch match; + bool crowd, cam_ignore; uint8_t nvx_blk; uint32_t nvx_idx; @@ -1629,16 +1630,19 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, */ nvx_blk = xive_get_field32(END2_W6_VP_BLOCK, end.w6); nvx_idx = xive_get_field32(END2_W6_VP_OFFSET, end.w6); - - found = xive_presenter_notify(xrtr->xfb, format, nvx_blk, nvx_idx, - xive2_end_is_crowd(&end), xive2_end_is_ignore(&end), - priority, - xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7), - &precluded); + crowd = xive2_end_is_crowd(&end); + cam_ignore = xive2_end_is_ignore(&end); /* TODO: Auto EOI. */ - - if (found) { + if (xive_presenter_match(xrtr->xfb, format, nvx_blk, nvx_idx, + crowd, cam_ignore, priority, + xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7), + &match)) { + uint8_t group_level; + + group_level = xive_get_group_level(crowd, cam_ignore, nvx_blk, nvx_idx); + trace_xive_presenter_notify(nvx_blk, nvx_idx, match.ring, group_level); + xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level); return; } @@ -1656,7 +1660,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, return; } - if (!xive2_end_is_ignore(&end)) { + if (!cam_ignore) { uint8_t ipb; Xive2Nvp nvp; @@ -1685,9 +1689,6 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, } else { Xive2Nvgc nvgc; uint32_t backlog; - bool crowd; - - crowd = xive2_end_is_crowd(&end); /* * For groups and crowds, the per-priority backlog @@ -1719,9 +1720,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, if (backlog == 1) { XiveFabricClass *xfc = XIVE_FABRIC_GET_CLASS(xrtr->xfb); xfc->broadcast(xrtr->xfb, nvx_blk, nvx_idx, - xive2_end_is_crowd(&end), - xive2_end_is_ignore(&end), - priority); + crowd, cam_ignore, priority); if (!xive2_end_is_precluded_escalation(&end)) { /* diff --git a/hw/ppc/pnv.c b/hw/ppc/pnv.c index 4a49e9d1a8..d84c9067ed 100644 --- a/hw/ppc/pnv.c +++ b/hw/ppc/pnv.c @@ -2608,62 +2608,46 @@ static void pnv_pic_print_info(InterruptStatsProvider *obj, GString *buf) } } -static int pnv_match_nvt(XiveFabric *xfb, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, - XiveTCTXMatch *match) +static bool pnv_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, + XiveTCTXMatch *match) { PnvMachineState *pnv = PNV_MACHINE(xfb); - int total_count = 0; int i; for (i = 0; i < pnv->num_chips; i++) { Pnv9Chip *chip9 = PNV9_CHIP(pnv->chips[i]); XivePresenter *xptr = XIVE_PRESENTER(&chip9->xive); XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); - int count; - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, - cam_ignore, priority, logic_serv, match); - - if (count < 0) { - return count; - } - - total_count += count; + xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, + cam_ignore, priority, logic_serv, match); } - return total_count; + return !!match->count; } -static int pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, - XiveTCTXMatch *match) +static bool pnv10_xive_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, + XiveTCTXMatch *match) { PnvMachineState *pnv = PNV_MACHINE(xfb); - int total_count = 0; int i; for (i = 0; i < pnv->num_chips; i++) { Pnv10Chip *chip10 = PNV10_CHIP(pnv->chips[i]); XivePresenter *xptr = XIVE_PRESENTER(&chip10->xive); XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); - int count; - - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, - cam_ignore, priority, logic_serv, match); - - if (count < 0) { - return count; - } - total_count += count; + xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, + cam_ignore, priority, logic_serv, match); } - return total_count; + return !!match->count; } static int pnv10_xive_broadcast(XiveFabric *xfb, diff --git a/hw/ppc/spapr.c b/hw/ppc/spapr.c index 40f53ad7b3..1855a3cd8d 100644 --- a/hw/ppc/spapr.c +++ b/hw/ppc/spapr.c @@ -4468,21 +4468,14 @@ static void spapr_pic_print_info(InterruptStatsProvider *obj, GString *buf) /* * This is a XIVE only operation */ -static int spapr_match_nvt(XiveFabric *xfb, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match) +static bool spapr_match_nvt(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match) { SpaprMachineState *spapr = SPAPR_MACHINE(xfb); XivePresenter *xptr = XIVE_PRESENTER(spapr->active_intc); XivePresenterClass *xpc = XIVE_PRESENTER_GET_CLASS(xptr); - int count; - - count = xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, cam_ignore, - priority, logic_serv, match); - if (count < 0) { - return count; - } /* * When we implement the save and restore of the thread interrupt @@ -4493,12 +4486,14 @@ static int spapr_match_nvt(XiveFabric *xfb, uint8_t format, * Until this is done, the sPAPR machine should find at least one * matching context always. */ - if (count == 0) { + if (!xpc->match_nvt(xptr, format, nvt_blk, nvt_idx, crowd, cam_ignore, + priority, logic_serv, match)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: NVT %x/%x is not dispatched\n", nvt_blk, nvt_idx); + return false; } - return count; + return true; } int spapr_get_vcpu_id(PowerPCCPU *cpu) diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 46d05d74fb..8152a9df3d 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -425,6 +425,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas); typedef struct XiveTCTXMatch { XiveTCTX *tctx; + int count; uint8_t ring; bool precluded; } XiveTCTXMatch; @@ -440,10 +441,10 @@ DECLARE_CLASS_CHECKERS(XivePresenterClass, XIVE_PRESENTER, struct XivePresenterClass { InterfaceClass parent; - int (*match_nvt)(XivePresenter *xptr, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match); + bool (*match_nvt)(XivePresenter *xptr, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match); bool (*in_kernel)(const XivePresenter *xptr); uint32_t (*get_config)(XivePresenter *xptr); int (*broadcast)(XivePresenter *xptr, @@ -455,12 +456,14 @@ int xive_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, uint8_t format, uint8_t nvt_blk, uint32_t nvt_idx, bool cam_ignore, uint32_t logic_serv); -bool xive_presenter_notify(XiveFabric *xfb, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, bool *precluded); +bool xive_presenter_match(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match); uint32_t xive_get_vpgroup_size(uint32_t nvp_index); +uint8_t xive_get_group_level(bool crowd, bool ignore, + uint32_t nvp_blk, uint32_t nvp_index); /* * XIVE Fabric (Interface between Interrupt Controller and Machine) @@ -475,10 +478,10 @@ DECLARE_CLASS_CHECKERS(XiveFabricClass, XIVE_FABRIC, struct XiveFabricClass { InterfaceClass parent; - int (*match_nvt)(XiveFabric *xfb, uint8_t format, - uint8_t nvt_blk, uint32_t nvt_idx, - bool crowd, bool cam_ignore, uint8_t priority, - uint32_t logic_serv, XiveTCTXMatch *match); + bool (*match_nvt)(XiveFabric *xfb, uint8_t format, + uint8_t nvt_blk, uint32_t nvt_idx, + bool crowd, bool cam_ignore, uint8_t priority, + uint32_t logic_serv, XiveTCTXMatch *match); int (*broadcast)(XiveFabric *xfb, uint8_t nvt_blk, uint32_t nvt_idx, bool crowd, bool cam_ignore, uint8_t priority); }; From cc15d50b6e8a4090d667755259ea36144b79d22f Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:38 +1000 Subject: [PATCH 2554/2760] ppc/xive2: Redistribute group interrupt preempted by higher priority interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A group interrupt that gets preempted by a higher priority interrupt delivery must be redistributed otherwise it would get lost. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-30-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 602b23d06d..f51fd38a13 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1638,11 +1638,21 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, crowd, cam_ignore, priority, xive_get_field32(END2_W7_F1_LOG_SERVER_ID, end.w7), &match)) { + XiveTCTX *tctx = match.tctx; + uint8_t ring = match.ring; + uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; + uint8_t *aregs = &tctx->regs[alt_ring]; + uint8_t nsr = aregs[TM_NSR]; uint8_t group_level; + if (priority < aregs[TM_PIPR] && + xive_nsr_indicates_group_exception(alt_ring, nsr)) { + xive2_redistribute(xrtr, tctx, alt_ring); + } + group_level = xive_get_group_level(crowd, cam_ignore, nvx_blk, nvx_idx); - trace_xive_presenter_notify(nvx_blk, nvx_idx, match.ring, group_level); - xive_tctx_pipr_update(match.tctx, match.ring, priority, group_level); + trace_xive_presenter_notify(nvx_blk, nvx_idx, ring, group_level); + xive_tctx_pipr_update(tctx, ring, priority, group_level); return; } From d16214ed2c57a31b5de7e2c115c65b831170a60e Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:39 +1000 Subject: [PATCH 2555/2760] ppc/xive: Add xive_tctx_pipr_present() to present new interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xive_tctx_pipr_update() is used for multiple things. In an effort to make things simpler and less overloaded, split out the function that is used to present a new interrupt to the tctx. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-31-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 8 +++++++- hw/intc/xive2.c | 2 +- include/hw/ppc/xive.h | 2 ++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index c92e819053..038c35846d 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -225,6 +225,12 @@ void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, xive_tctx_notify(tctx, ring, group_level); } +void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, + uint8_t group_level) +{ + xive_tctx_pipr_update(tctx, ring, priority, group_level); +} + /* * XIVE Thread Interrupt Management Area (TIMA) */ @@ -2040,7 +2046,7 @@ void xive_router_end_notify(XiveRouter *xrtr, XiveEAS *eas) xive_get_field32(END_W7_F1_LOG_SERVER_ID, end.w7), &match)) { trace_xive_presenter_notify(nvt_blk, nvt_idx, match.ring, 0); - xive_tctx_pipr_update(match.tctx, match.ring, priority, 0); + xive_tctx_pipr_present(match.tctx, match.ring, priority, 0); return; } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index f51fd38a13..fe40f7f07b 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1652,7 +1652,7 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, group_level = xive_get_group_level(crowd, cam_ignore, nvx_blk, nvx_idx); trace_xive_presenter_notify(nvx_blk, nvx_idx, ring, group_level); - xive_tctx_pipr_update(tctx, ring, priority, group_level); + xive_tctx_pipr_present(tctx, ring, priority, group_level); return; } diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 8152a9df3d..0d6b11e818 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -562,6 +562,8 @@ void xive_tctx_reset(XiveTCTX *tctx); void xive_tctx_destroy(XiveTCTX *tctx); void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level); +void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, + uint8_t group_level); void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring); void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level); uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring); From 46f5ee8885a521c56e60820bf35aba4e94e16cf7 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:40 +1000 Subject: [PATCH 2556/2760] ppc/xive: Fix high prio group interrupt being preempted by low prio VP MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xive_tctx_pipr_present() as implemented with xive_tctx_pipr_update() causes VP-directed (group==0) interrupt to be presented in PIPR and NSR despite being a lower priority than the currently presented group interrupt. This must not happen. The IPB bit should record the low priority VP interrupt, but PIPR and NSR must not present the lower priority interrupt. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-32-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 038c35846d..7110cf45d7 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -228,7 +228,23 @@ void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level) { - xive_tctx_pipr_update(tctx, ring, priority, group_level); + /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ + uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; + uint8_t *aregs = &tctx->regs[alt_ring]; + uint8_t *regs = &tctx->regs[ring]; + uint8_t pipr = xive_priority_to_pipr(priority); + + if (group_level == 0) { + regs[TM_IPB] |= xive_priority_to_ipb(priority); + if (pipr >= aregs[TM_PIPR]) { + /* VP interrupts can come here with lower priority than PIPR */ + return; + } + } + g_assert(pipr <= xive_ipb_to_pipr(regs[TM_IPB])); + g_assert(pipr < aregs[TM_PIPR]); + aregs[TM_PIPR] = pipr; + xive_tctx_notify(tctx, ring, group_level); } /* From 3516b9b6739714068d83bb5ed9ce25cc1b20be8d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:41 +1000 Subject: [PATCH 2557/2760] ppc/xive: Split xive recompute from IPB function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Further split xive_tctx_pipr_update() by splitting out a new function that is used to re-compute the PIPR from IPB. This is generally only used with XIVE1, because group interrputs require more logic. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-33-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 7110cf45d7..5deb2f478f 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -225,6 +225,20 @@ void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, xive_tctx_notify(tctx, ring, group_level); } +static void xive_tctx_pipr_recompute_from_ipb(XiveTCTX *tctx, uint8_t ring) +{ + /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ + uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; + uint8_t *aregs = &tctx->regs[alt_ring]; + uint8_t *regs = &tctx->regs[ring]; + + /* Does not support a presented group interrupt */ + g_assert(!xive_nsr_indicates_group_exception(alt_ring, aregs[TM_NSR])); + + aregs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); + xive_tctx_notify(tctx, ring, 0); +} + void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level) { @@ -517,7 +531,12 @@ static void xive_tm_set_os_lgs(XivePresenter *xptr, XiveTCTX *tctx, static void xive_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { - xive_tctx_pipr_update(tctx, TM_QW1_OS, value & 0xff, 0); + uint8_t ring = TM_QW1_OS; + uint8_t *regs = &tctx->regs[ring]; + + /* XXX: how should this work exactly? */ + regs[TM_IPB] |= xive_priority_to_ipb(value & 0xff); + xive_tctx_pipr_recompute_from_ipb(tctx, ring); } static void xive_os_cam_decode(uint32_t cam, uint8_t *nvt_blk, @@ -601,14 +620,14 @@ static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx, } /* - * Always call xive_tctx_pipr_update(). Even if there were no + * Always call xive_tctx_recompute_from_ipb(). Even if there were no * escalation triggered, there could be a pending interrupt which * was saved when the context was pulled and that we need to take * into account by recalculating the PIPR (which is not * saved/restored). * It will also raise the External interrupt signal if needed. */ - xive_tctx_pipr_update(tctx, TM_QW1_OS, 0xFF, 0); /* fxb */ + xive_tctx_pipr_recompute_from_ipb(tctx, TM_QW1_OS); /* fxb */ } /* From 581bec5a04c5c27a86cfae93ca531c101f2df2ec Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:42 +1000 Subject: [PATCH 2558/2760] ppc/xive: tctx signaling registers rework MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The tctx "signaling" registers (PIPR, CPPR, NSR) raise an interrupt on the target CPU thread. The POOL and PHYS rings both raise hypervisor interrupts, so they both share one set of signaling registers in the PHYS ring. The PHYS NSR register contains a field that indicates which ring has presented the interrupt being signaled to the CPU. This sharing results in all the "alt_regs" throughout the code. alt_regs is not very descriptive, and worse is that the name is used for conversions in both directions, i.e., to find the presenting ring from the signaling ring, and the signaling ring from the presenting ring. Instead of alt_regs, use the names sig_regs and sig_ring, and regs and ring for the presenting ring being worked on. Add a helper function to get the sign_regs, and add some asserts to ensure the POOL regs are never used to signal interrupts. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-34-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 112 ++++++++++++++++++++++-------------------- hw/intc/xive2.c | 94 ++++++++++++++++------------------- include/hw/ppc/xive.h | 26 +++++++++- 3 files changed, 126 insertions(+), 106 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 5deb2f478f..119a178f2e 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -80,69 +80,77 @@ static qemu_irq xive_tctx_output(XiveTCTX *tctx, uint8_t ring) } } -uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring) +/* + * interrupt is accepted on the presentation ring, for PHYS ring the NSR + * directs it to the PHYS or POOL rings. + */ +uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t sig_ring) { - uint8_t *regs = &tctx->regs[ring]; - uint8_t nsr = regs[TM_NSR]; + uint8_t *sig_regs = &tctx->regs[sig_ring]; + uint8_t nsr = sig_regs[TM_NSR]; - qemu_irq_lower(xive_tctx_output(tctx, ring)); + g_assert(sig_ring == TM_QW1_OS || sig_ring == TM_QW3_HV_PHYS); + + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); + + qemu_irq_lower(xive_tctx_output(tctx, sig_ring)); - if (xive_nsr_indicates_exception(ring, nsr)) { - uint8_t cppr = regs[TM_PIPR]; - uint8_t alt_ring; - uint8_t *alt_regs; + if (xive_nsr_indicates_exception(sig_ring, nsr)) { + uint8_t cppr = sig_regs[TM_PIPR]; + uint8_t ring; + uint8_t *regs; - alt_ring = xive_nsr_exception_ring(ring, nsr); - alt_regs = &tctx->regs[alt_ring]; + ring = xive_nsr_exception_ring(sig_ring, nsr); + regs = &tctx->regs[ring]; - regs[TM_CPPR] = cppr; + sig_regs[TM_CPPR] = cppr; /* * If the interrupt was for a specific VP, reset the pending * buffer bit, otherwise clear the logical server indicator */ - if (!xive_nsr_indicates_group_exception(ring, nsr)) { - alt_regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); + if (!xive_nsr_indicates_group_exception(sig_ring, nsr)) { + regs[TM_IPB] &= ~xive_priority_to_ipb(cppr); } /* Clear the exception from NSR */ - regs[TM_NSR] = 0; + sig_regs[TM_NSR] = 0; - trace_xive_tctx_accept(tctx->cs->cpu_index, alt_ring, - alt_regs[TM_IPB], regs[TM_PIPR], - regs[TM_CPPR], regs[TM_NSR]); + trace_xive_tctx_accept(tctx->cs->cpu_index, ring, + regs[TM_IPB], sig_regs[TM_PIPR], + sig_regs[TM_CPPR], sig_regs[TM_NSR]); } - return ((uint64_t)nsr << 8) | regs[TM_CPPR]; + return ((uint64_t)nsr << 8) | sig_regs[TM_CPPR]; } void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level) { - /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ - uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; - uint8_t *alt_regs = &tctx->regs[alt_ring]; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; - if (alt_regs[TM_PIPR] < alt_regs[TM_CPPR]) { + if (sig_regs[TM_PIPR] < sig_regs[TM_CPPR]) { switch (ring) { case TM_QW1_OS: - regs[TM_NSR] = TM_QW1_NSR_EO | (group_level & 0x3F); + sig_regs[TM_NSR] = TM_QW1_NSR_EO | (group_level & 0x3F); break; case TM_QW2_HV_POOL: - alt_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6) | (group_level & 0x3F); + sig_regs[TM_NSR] = (TM_QW3_NSR_HE_POOL << 6) | (group_level & 0x3F); break; case TM_QW3_HV_PHYS: - regs[TM_NSR] = (TM_QW3_NSR_HE_PHYS << 6) | (group_level & 0x3F); + sig_regs[TM_NSR] = (TM_QW3_NSR_HE_PHYS << 6) | (group_level & 0x3F); break; default: g_assert_not_reached(); } trace_xive_tctx_notify(tctx->cs->cpu_index, ring, - regs[TM_IPB], alt_regs[TM_PIPR], - alt_regs[TM_CPPR], alt_regs[TM_NSR]); + regs[TM_IPB], sig_regs[TM_PIPR], + sig_regs[TM_CPPR], sig_regs[TM_NSR]); qemu_irq_raise(xive_tctx_output(tctx, ring)); } else { - alt_regs[TM_NSR] = 0; + sig_regs[TM_NSR] = 0; qemu_irq_lower(xive_tctx_output(tctx, ring)); } } @@ -159,25 +167,32 @@ void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring) static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) { - uint8_t *regs = &tctx->regs[ring]; + uint8_t *sig_regs = &tctx->regs[ring]; uint8_t pipr_min; uint8_t ring_min; + g_assert(ring == TM_QW1_OS || ring == TM_QW3_HV_PHYS); + + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); + + /* XXX: should show pool IPB for PHYS ring */ trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring, - regs[TM_IPB], regs[TM_PIPR], - cppr, regs[TM_NSR]); + sig_regs[TM_IPB], sig_regs[TM_PIPR], + cppr, sig_regs[TM_NSR]); if (cppr > XIVE_PRIORITY_MAX) { cppr = 0xff; } - tctx->regs[ring + TM_CPPR] = cppr; + sig_regs[TM_CPPR] = cppr; /* * Recompute the PIPR based on local pending interrupts. The PHYS * ring must take the minimum of both the PHYS and POOL PIPR values. */ - pipr_min = xive_ipb_to_pipr(regs[TM_IPB]); + pipr_min = xive_ipb_to_pipr(sig_regs[TM_IPB]); ring_min = ring; /* PHYS updates also depend on POOL values */ @@ -186,7 +201,6 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) /* POOL values only matter if POOL ctx is valid */ if (pool_regs[TM_WORD2] & 0x80) { - uint8_t pool_pipr = xive_ipb_to_pipr(pool_regs[TM_IPB]); /* @@ -200,7 +214,7 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } } - regs[TM_PIPR] = pipr_min; + sig_regs[TM_PIPR] = pipr_min; /* CPPR has changed, check if we need to raise a pending exception */ xive_tctx_notify(tctx, ring_min, 0); @@ -208,56 +222,50 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level) - { - /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ - uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; - uint8_t *alt_regs = &tctx->regs[alt_ring]; +{ + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; if (group_level == 0) { /* VP-specific */ regs[TM_IPB] |= xive_priority_to_ipb(priority); - alt_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); + sig_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); } else { /* VP-group */ - alt_regs[TM_PIPR] = xive_priority_to_pipr(priority); + sig_regs[TM_PIPR] = xive_priority_to_pipr(priority); } xive_tctx_notify(tctx, ring, group_level); } static void xive_tctx_pipr_recompute_from_ipb(XiveTCTX *tctx, uint8_t ring) { - /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ - uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; - uint8_t *aregs = &tctx->regs[alt_ring]; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; /* Does not support a presented group interrupt */ - g_assert(!xive_nsr_indicates_group_exception(alt_ring, aregs[TM_NSR])); + g_assert(!xive_nsr_indicates_group_exception(ring, sig_regs[TM_NSR])); - aregs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); + sig_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); xive_tctx_notify(tctx, ring, 0); } void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level) { - /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ - uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; - uint8_t *aregs = &tctx->regs[alt_ring]; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; uint8_t pipr = xive_priority_to_pipr(priority); if (group_level == 0) { regs[TM_IPB] |= xive_priority_to_ipb(priority); - if (pipr >= aregs[TM_PIPR]) { + if (pipr >= sig_regs[TM_PIPR]) { /* VP interrupts can come here with lower priority than PIPR */ return; } } g_assert(pipr <= xive_ipb_to_pipr(regs[TM_IPB])); - g_assert(pipr < aregs[TM_PIPR]); - aregs[TM_PIPR] = pipr; + g_assert(pipr < sig_regs[TM_PIPR]); + sig_regs[TM_PIPR] = pipr; xive_tctx_notify(tctx, ring, group_level); } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index fe40f7f07b..71b40f702a 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -606,11 +606,9 @@ static uint32_t xive2_tctx_hw_cam_line(XivePresenter *xptr, XiveTCTX *tctx) static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) { - uint8_t *regs = &tctx->regs[ring]; - uint8_t *alt_regs = (ring == TM_QW2_HV_POOL) ? &tctx->regs[TM_QW3_HV_PHYS] : - regs; - uint8_t nsr = alt_regs[TM_NSR]; - uint8_t pipr = alt_regs[TM_PIPR]; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); + uint8_t nsr = sig_regs[TM_NSR]; + uint8_t pipr = sig_regs[TM_PIPR]; uint8_t crowd = NVx_CROWD_LVL(nsr); uint8_t group = NVx_GROUP_LVL(nsr); uint8_t nvgc_blk, end_blk, nvp_blk; @@ -618,19 +616,16 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) Xive2Nvgc nvgc; uint8_t prio_limit; uint32_t cfg; - uint8_t alt_ring; /* redistribution is only for group/crowd interrupts */ if (!xive_nsr_indicates_group_exception(ring, nsr)) { return; } - alt_ring = xive_nsr_exception_ring(ring, nsr); - /* Don't check return code since ring is expected to be invalidated */ - xive2_tctx_get_nvp_indexes(tctx, alt_ring, &nvp_blk, &nvp_idx); + xive2_tctx_get_nvp_indexes(tctx, ring, &nvp_blk, &nvp_idx); - trace_xive_redistribute(tctx->cs->cpu_index, alt_ring, nvp_blk, nvp_idx); + trace_xive_redistribute(tctx->cs->cpu_index, ring, nvp_blk, nvp_idx); trace_xive_redistribute(tctx->cs->cpu_index, ring, nvp_blk, nvp_idx); /* convert crowd/group to blk/idx */ @@ -675,23 +670,11 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) xive2_router_end_notify(xrtr, end_blk, end_idx, 0, true); /* clear interrupt indication for the context */ - alt_regs[TM_NSR] = 0; - alt_regs[TM_PIPR] = alt_regs[TM_CPPR]; + sig_regs[TM_NSR] = 0; + sig_regs[TM_PIPR] = sig_regs[TM_CPPR]; xive_tctx_reset_signal(tctx, ring); } -static uint8_t xive2_hv_irq_ring(uint8_t nsr) -{ - switch (nsr >> 6) { - case TM_QW3_NSR_HE_POOL: - return TM_QW2_HV_POOL; - case TM_QW3_NSR_HE_PHYS: - return TM_QW3_HV_PHYS; - default: - return -1; - } -} - static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size, uint8_t ring) { @@ -718,7 +701,8 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, uint32_t ringw2 = xive_tctx_word2(&tctx->regs[cur_ring]); uint32_t ringw2_new = xive_set_field32(TM2_QW1W2_VO, ringw2, 0); bool is_valid = !!(xive_get_field32(TM2_QW1W2_VO, ringw2)); - uint8_t alt_ring; + uint8_t *sig_regs; + memcpy(&tctx->regs[cur_ring + TM_WORD2], &ringw2_new, 4); /* Skip the rest for USER or invalid contexts */ @@ -727,12 +711,11 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, } /* Active group/crowd interrupts need to be redistributed */ - alt_ring = (cur_ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : cur_ring; - nsr = tctx->regs[alt_ring + TM_NSR]; - if (xive_nsr_indicates_group_exception(alt_ring, nsr)) { - /* For HV rings, only redistribute if cur_ring matches NSR */ - if ((cur_ring == TM_QW1_OS) || - (cur_ring == xive2_hv_irq_ring(nsr))) { + sig_regs = xive_tctx_signal_regs(tctx, ring); + nsr = sig_regs[TM_NSR]; + if (xive_nsr_indicates_group_exception(cur_ring, nsr)) { + /* Ensure ring matches NSR (for HV NSR POOL vs PHYS rings) */ + if (cur_ring == xive_nsr_exception_ring(cur_ring, nsr)) { xive2_redistribute(xrtr, tctx, cur_ring); } } @@ -1118,7 +1101,7 @@ void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx, /* NOTE: CPPR only exists for TM_QW1_OS and TM_QW3_HV_PHYS */ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) { - uint8_t *regs = &tctx->regs[ring]; + uint8_t *sig_regs = &tctx->regs[ring]; Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr); uint8_t old_cppr, backlog_prio, first_group, group_level; uint8_t pipr_min, lsmfb_min, ring_min; @@ -1127,33 +1110,41 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) uint32_t nvp_idx; Xive2Nvp nvp; int rc; - uint8_t nsr = regs[TM_NSR]; + uint8_t nsr = sig_regs[TM_NSR]; + + g_assert(ring == TM_QW1_OS || ring == TM_QW3_HV_PHYS); + + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); + /* XXX: should show pool IPB for PHYS ring */ trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring, - regs[TM_IPB], regs[TM_PIPR], + sig_regs[TM_IPB], sig_regs[TM_PIPR], cppr, nsr); if (cppr > XIVE_PRIORITY_MAX) { cppr = 0xff; } - old_cppr = regs[TM_CPPR]; - regs[TM_CPPR] = cppr; + old_cppr = sig_regs[TM_CPPR]; + sig_regs[TM_CPPR] = cppr; /* Handle increased CPPR priority (lower value) */ if (cppr < old_cppr) { - if (cppr <= regs[TM_PIPR]) { + if (cppr <= sig_regs[TM_PIPR]) { /* CPPR lowered below PIPR, must un-present interrupt */ if (xive_nsr_indicates_exception(ring, nsr)) { if (xive_nsr_indicates_group_exception(ring, nsr)) { /* redistribute precluded active grp interrupt */ - xive2_redistribute(xrtr, tctx, ring); + xive2_redistribute(xrtr, tctx, + xive_nsr_exception_ring(ring, nsr)); return; } } /* interrupt is VP directed, pending in IPB */ - regs[TM_PIPR] = cppr; + sig_regs[TM_PIPR] = cppr; xive_tctx_notify(tctx, ring, 0); /* Ensure interrupt is cleared */ return; } else { @@ -1174,9 +1165,9 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) * be adjusted below if needed in case of pending group interrupts. */ again: - pipr_min = xive_ipb_to_pipr(regs[TM_IPB]); - group_enabled = !!regs[TM_LGS]; - lsmfb_min = group_enabled ? regs[TM_LSMFB] : 0xff; + pipr_min = xive_ipb_to_pipr(sig_regs[TM_IPB]); + group_enabled = !!sig_regs[TM_LGS]; + lsmfb_min = group_enabled ? sig_regs[TM_LSMFB] : 0xff; ring_min = ring; group_level = 0; @@ -1265,7 +1256,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } /* PIPR should not be set to a value greater than CPPR */ - regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min; + sig_regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min; /* CPPR has changed, check if we need to raise a pending exception */ xive_tctx_notify(tctx, ring_min, group_level); @@ -1490,9 +1481,7 @@ int xive2_presenter_tctx_match(XivePresenter *xptr, XiveTCTX *tctx, bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority) { - /* HV_POOL ring uses HV_PHYS NSR, CPPR and PIPR registers */ - uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; - uint8_t *alt_regs = &tctx->regs[alt_ring]; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); /* * The xive2_presenter_tctx_match() above tells if there's a match @@ -1500,7 +1489,7 @@ bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority) * priority to know if the thread can take the interrupt now or if * it is precluded. */ - if (priority < alt_regs[TM_PIPR]) { + if (priority < sig_regs[TM_PIPR]) { return false; } return true; @@ -1640,14 +1629,13 @@ static void xive2_router_end_notify(Xive2Router *xrtr, uint8_t end_blk, &match)) { XiveTCTX *tctx = match.tctx; uint8_t ring = match.ring; - uint8_t alt_ring = (ring == TM_QW2_HV_POOL) ? TM_QW3_HV_PHYS : ring; - uint8_t *aregs = &tctx->regs[alt_ring]; - uint8_t nsr = aregs[TM_NSR]; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); + uint8_t nsr = sig_regs[TM_NSR]; uint8_t group_level; - if (priority < aregs[TM_PIPR] && - xive_nsr_indicates_group_exception(alt_ring, nsr)) { - xive2_redistribute(xrtr, tctx, alt_ring); + if (priority < sig_regs[TM_PIPR] && + xive_nsr_indicates_group_exception(ring, nsr)) { + xive2_redistribute(xrtr, tctx, xive_nsr_exception_ring(ring, nsr)); } group_level = xive_get_group_level(crowd, cam_ignore, nvx_blk, nvx_idx); diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 0d6b11e818..a3c2f50ece 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -539,7 +539,7 @@ static inline uint8_t xive_ipb_to_pipr(uint8_t ibp) } /* - * XIVE Thread Interrupt Management Aera (TIMA) + * XIVE Thread Interrupt Management Area (TIMA) * * This region gives access to the registers of the thread interrupt * management context. It is four page wide, each page providing a @@ -551,6 +551,30 @@ static inline uint8_t xive_ipb_to_pipr(uint8_t ibp) #define XIVE_TM_OS_PAGE 0x2 #define XIVE_TM_USER_PAGE 0x3 +/* + * The TCTX (TIMA) has 4 rings (phys, pool, os, user), but only signals + * (raises an interrupt on) the CPU from 3 of them. Phys and pool both + * cause a hypervisor privileged interrupt so interrupts presented on + * those rings signal using the phys ring. This helper returns the signal + * regs from the given ring. + */ +static inline uint8_t *xive_tctx_signal_regs(XiveTCTX *tctx, uint8_t ring) +{ + /* + * This is a good point to add invariants to ensure nothing has tried to + * signal using the POOL ring. + */ + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); + + if (ring == TM_QW2_HV_POOL) { + /* POOL and PHYS rings share the signal regs (PIPR, NSR, CPPR) */ + ring = TM_QW3_HV_PHYS; + } + return &tctx->regs[ring]; +} + void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, From cf454eaa96e8a0c3c1de63b0f7b85542d7c5ecbf Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:43 +1000 Subject: [PATCH 2559/2760] ppc/xive: tctx_accept only lower irq line if an interrupt was presented MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The relationship between an interrupt signaled in the TIMA and the QEMU irq line to the processor to be 1:1, so they should be raised and lowered together and "just in case" lowering should be avoided (it could mask Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-35-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 119a178f2e..db26dae7db 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -95,8 +95,6 @@ uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t sig_ring) g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); - qemu_irq_lower(xive_tctx_output(tctx, sig_ring)); - if (xive_nsr_indicates_exception(sig_ring, nsr)) { uint8_t cppr = sig_regs[TM_PIPR]; uint8_t ring; @@ -117,6 +115,7 @@ uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t sig_ring) /* Clear the exception from NSR */ sig_regs[TM_NSR] = 0; + qemu_irq_lower(xive_tctx_output(tctx, sig_ring)); trace_xive_tctx_accept(tctx->cs->cpu_index, ring, regs[TM_IPB], sig_regs[TM_PIPR], From 64a18e0c37a6b5c3d94541ff0599ea84fec998c0 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:44 +1000 Subject: [PATCH 2560/2760] ppc/xive: Add xive_tctx_pipr_set() helper function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have xive_tctx_notify() also set the new PIPR value and rename it to xive_tctx_pipr_set(). This can replace the last xive_tctx_pipr_update() caller because it does not need to update IPB (it already sets it). Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-36-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 39 +++++++++++---------------------------- hw/intc/xive2.c | 16 +++++++--------- include/hw/ppc/xive.h | 5 ++--- 3 files changed, 20 insertions(+), 40 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index db26dae7db..6ad84f93c7 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -125,12 +125,16 @@ uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t sig_ring) return ((uint64_t)nsr << 8) | sig_regs[TM_CPPR]; } -void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level) +/* Change PIPR and calculate NSR and irq based on PIPR, CPPR, group */ +void xive_tctx_pipr_set(XiveTCTX *tctx, uint8_t ring, uint8_t pipr, + uint8_t group_level) { uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; - if (sig_regs[TM_PIPR] < sig_regs[TM_CPPR]) { + sig_regs[TM_PIPR] = pipr; + + if (pipr < sig_regs[TM_CPPR]) { switch (ring) { case TM_QW1_OS: sig_regs[TM_NSR] = TM_QW1_NSR_EO | (group_level & 0x3F); @@ -145,7 +149,7 @@ void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level) g_assert_not_reached(); } trace_xive_tctx_notify(tctx->cs->cpu_index, ring, - regs[TM_IPB], sig_regs[TM_PIPR], + regs[TM_IPB], pipr, sig_regs[TM_CPPR], sig_regs[TM_NSR]); qemu_irq_raise(xive_tctx_output(tctx, ring)); } else { @@ -213,29 +217,10 @@ static void xive_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } } - sig_regs[TM_PIPR] = pipr_min; - - /* CPPR has changed, check if we need to raise a pending exception */ - xive_tctx_notify(tctx, ring_min, 0); + /* CPPR has changed, this may present or preclude a pending exception */ + xive_tctx_pipr_set(tctx, ring_min, pipr_min, 0); } -void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, - uint8_t group_level) -{ - uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); - uint8_t *regs = &tctx->regs[ring]; - - if (group_level == 0) { - /* VP-specific */ - regs[TM_IPB] |= xive_priority_to_ipb(priority); - sig_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); - } else { - /* VP-group */ - sig_regs[TM_PIPR] = xive_priority_to_pipr(priority); - } - xive_tctx_notify(tctx, ring, group_level); - } - static void xive_tctx_pipr_recompute_from_ipb(XiveTCTX *tctx, uint8_t ring) { uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); @@ -244,8 +229,7 @@ static void xive_tctx_pipr_recompute_from_ipb(XiveTCTX *tctx, uint8_t ring) /* Does not support a presented group interrupt */ g_assert(!xive_nsr_indicates_group_exception(ring, sig_regs[TM_NSR])); - sig_regs[TM_PIPR] = xive_ipb_to_pipr(regs[TM_IPB]); - xive_tctx_notify(tctx, ring, 0); + xive_tctx_pipr_set(tctx, ring, xive_ipb_to_pipr(regs[TM_IPB]), 0); } void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, @@ -264,8 +248,7 @@ void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, } g_assert(pipr <= xive_ipb_to_pipr(regs[TM_IPB])); g_assert(pipr < sig_regs[TM_PIPR]); - sig_regs[TM_PIPR] = pipr; - xive_tctx_notify(tctx, ring, group_level); + xive_tctx_pipr_set(tctx, ring, pipr, group_level); } /* diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 71b40f702a..0ee50a6bca 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -966,10 +966,10 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, } /* - * Compute the PIPR based on the restored state. + * Set the PIPR/NSR based on the restored state. * It will raise the External interrupt signal if needed. */ - xive_tctx_pipr_update(tctx, TM_QW1_OS, backlog_prio, backlog_level); + xive_tctx_pipr_set(tctx, TM_QW1_OS, backlog_prio, backlog_level); } /* @@ -1144,8 +1144,7 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } /* interrupt is VP directed, pending in IPB */ - sig_regs[TM_PIPR] = cppr; - xive_tctx_notify(tctx, ring, 0); /* Ensure interrupt is cleared */ + xive_tctx_pipr_set(tctx, ring, cppr, 0); return; } else { /* CPPR was lowered, but still above PIPR. No action needed. */ @@ -1255,11 +1254,10 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) pipr_min = backlog_prio; } - /* PIPR should not be set to a value greater than CPPR */ - sig_regs[TM_PIPR] = (pipr_min > cppr) ? cppr : pipr_min; - - /* CPPR has changed, check if we need to raise a pending exception */ - xive_tctx_notify(tctx, ring_min, group_level); + if (pipr_min > cppr) { + pipr_min = cppr; + } + xive_tctx_pipr_set(tctx, ring_min, pipr_min, group_level); } void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index a3c2f50ece..2372d1014b 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -584,12 +584,11 @@ void xive_tctx_pic_print_info(XiveTCTX *tctx, GString *buf); Object *xive_tctx_create(Object *cpu, XivePresenter *xptr, Error **errp); void xive_tctx_reset(XiveTCTX *tctx); void xive_tctx_destroy(XiveTCTX *tctx); -void xive_tctx_pipr_update(XiveTCTX *tctx, uint8_t ring, uint8_t priority, - uint8_t group_level); +void xive_tctx_pipr_set(XiveTCTX *tctx, uint8_t ring, uint8_t priority, + uint8_t group_level); void xive_tctx_pipr_present(XiveTCTX *tctx, uint8_t ring, uint8_t priority, uint8_t group_level); void xive_tctx_reset_signal(XiveTCTX *tctx, uint8_t ring); -void xive_tctx_notify(XiveTCTX *tctx, uint8_t ring, uint8_t group_level); uint64_t xive_tctx_accept(XiveTCTX *tctx, uint8_t ring); /* From 384f0365abae49b3d6a1cd46e8ae3cf4b9d4013e Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:45 +1000 Subject: [PATCH 2561/2760] ppc/xive2: split tctx presentation processing from set CPPR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The second part of the set CPPR operation is to process (or re-present) any pending interrupts after CPPR is adjusted. Split this presentation processing out into a standalone function that can be used in other places. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-37-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 137 +++++++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 61 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 0ee50a6bca..c7356c5b2f 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1098,66 +1098,19 @@ void xive2_tm_ack_os_el(XivePresenter *xptr, XiveTCTX *tctx, xive2_tctx_accept_el(xptr, tctx, TM_QW1_OS, TM_QW1_OS); } -/* NOTE: CPPR only exists for TM_QW1_OS and TM_QW3_HV_PHYS */ -static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) +/* Re-calculate and present pending interrupts */ +static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring) { - uint8_t *sig_regs = &tctx->regs[ring]; + uint8_t *sig_regs = &tctx->regs[sig_ring]; Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr); - uint8_t old_cppr, backlog_prio, first_group, group_level; + uint8_t backlog_prio, first_group, group_level; uint8_t pipr_min, lsmfb_min, ring_min; + uint8_t cppr = sig_regs[TM_CPPR]; bool group_enabled; - uint8_t nvp_blk; - uint32_t nvp_idx; Xive2Nvp nvp; int rc; - uint8_t nsr = sig_regs[TM_NSR]; - - g_assert(ring == TM_QW1_OS || ring == TM_QW3_HV_PHYS); - - g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0); - g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); - g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); - - /* XXX: should show pool IPB for PHYS ring */ - trace_xive_tctx_set_cppr(tctx->cs->cpu_index, ring, - sig_regs[TM_IPB], sig_regs[TM_PIPR], - cppr, nsr); - - if (cppr > XIVE_PRIORITY_MAX) { - cppr = 0xff; - } - - old_cppr = sig_regs[TM_CPPR]; - sig_regs[TM_CPPR] = cppr; - - /* Handle increased CPPR priority (lower value) */ - if (cppr < old_cppr) { - if (cppr <= sig_regs[TM_PIPR]) { - /* CPPR lowered below PIPR, must un-present interrupt */ - if (xive_nsr_indicates_exception(ring, nsr)) { - if (xive_nsr_indicates_group_exception(ring, nsr)) { - /* redistribute precluded active grp interrupt */ - xive2_redistribute(xrtr, tctx, - xive_nsr_exception_ring(ring, nsr)); - return; - } - } - /* interrupt is VP directed, pending in IPB */ - xive_tctx_pipr_set(tctx, ring, cppr, 0); - return; - } else { - /* CPPR was lowered, but still above PIPR. No action needed. */ - return; - } - } - - /* CPPR didn't change, nothing needs to be done */ - if (cppr == old_cppr) { - return; - } - - /* CPPR priority decreased (higher value) */ + g_assert(sig_ring == TM_QW3_HV_PHYS || sig_ring == TM_QW1_OS); /* * Recompute the PIPR based on local pending interrupts. It will @@ -1167,11 +1120,11 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) pipr_min = xive_ipb_to_pipr(sig_regs[TM_IPB]); group_enabled = !!sig_regs[TM_LGS]; lsmfb_min = group_enabled ? sig_regs[TM_LSMFB] : 0xff; - ring_min = ring; + ring_min = sig_ring; group_level = 0; /* PHYS updates also depend on POOL values */ - if (ring == TM_QW3_HV_PHYS) { + if (sig_ring == TM_QW3_HV_PHYS) { uint8_t *pool_regs = &tctx->regs[TM_QW2_HV_POOL]; /* POOL values only matter if POOL ctx is valid */ @@ -1201,20 +1154,25 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) } } - rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx); - if (rc) { - qemu_log_mask(LOG_GUEST_ERROR, "XIVE: set CPPR on invalid context\n"); - return; - } - if (group_enabled && lsmfb_min < cppr && lsmfb_min < pipr_min) { + + uint8_t nvp_blk; + uint32_t nvp_idx; + /* * Thread has seen a group interrupt with a higher priority * than the new cppr or pending local interrupt. Check the * backlog */ + rc = xive2_tctx_get_nvp_indexes(tctx, ring_min, &nvp_blk, &nvp_idx); + if (rc) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: set CPPR on invalid " + "context\n"); + return; + } + if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: No NVP %x/%x\n", nvp_blk, nvp_idx); @@ -1260,6 +1218,63 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t ring, uint8_t cppr) xive_tctx_pipr_set(tctx, ring_min, pipr_min, group_level); } +/* NOTE: CPPR only exists for TM_QW1_OS and TM_QW3_HV_PHYS */ +static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t sig_ring, uint8_t cppr) +{ + uint8_t *sig_regs = &tctx->regs[sig_ring]; + Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr); + uint8_t old_cppr; + uint8_t nsr = sig_regs[TM_NSR]; + + g_assert(sig_ring == TM_QW1_OS || sig_ring == TM_QW3_HV_PHYS); + + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_NSR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_PIPR] == 0); + g_assert(tctx->regs[TM_QW2_HV_POOL + TM_CPPR] == 0); + + /* XXX: should show pool IPB for PHYS ring */ + trace_xive_tctx_set_cppr(tctx->cs->cpu_index, sig_ring, + sig_regs[TM_IPB], sig_regs[TM_PIPR], + cppr, nsr); + + if (cppr > XIVE_PRIORITY_MAX) { + cppr = 0xff; + } + + old_cppr = sig_regs[TM_CPPR]; + sig_regs[TM_CPPR] = cppr; + + /* Handle increased CPPR priority (lower value) */ + if (cppr < old_cppr) { + if (cppr <= sig_regs[TM_PIPR]) { + /* CPPR lowered below PIPR, must un-present interrupt */ + if (xive_nsr_indicates_exception(sig_ring, nsr)) { + if (xive_nsr_indicates_group_exception(sig_ring, nsr)) { + /* redistribute precluded active grp interrupt */ + xive2_redistribute(xrtr, tctx, + xive_nsr_exception_ring(sig_ring, nsr)); + return; + } + } + + /* interrupt is VP directed, pending in IPB */ + xive_tctx_pipr_set(tctx, sig_ring, cppr, 0); + return; + } else { + /* CPPR was lowered, but still above PIPR. No action needed. */ + return; + } + } + + /* CPPR didn't change, nothing needs to be done */ + if (cppr == old_cppr) { + return; + } + + /* CPPR priority decreased (higher value) */ + xive2_tctx_process_pending(tctx, sig_ring); +} + void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { From 04627e2206dce95ecda486abf70d0ef7dd9f39f4 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:46 +1000 Subject: [PATCH 2562/2760] ppc/xive2: Consolidate presentation processing in context push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit OS-push operation must re-present pending interrupts. Use the newly created xive2_tctx_process_pending() function instead of duplicating the logic. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-38-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 42 ++++++++++-------------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index c7356c5b2f..be945bef1c 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -903,18 +903,14 @@ static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, return cppr; } +static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring); + static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) { - XivePresenter *xptr = XIVE_PRESENTER(xrtr); - uint8_t ipb; - uint8_t backlog_level; - uint8_t group_level; - uint8_t first_group; - uint8_t backlog_prio; - uint8_t group_prio; uint8_t *regs = &tctx->regs[TM_QW1_OS]; + uint8_t ipb; Xive2Nvp nvp; /* @@ -946,30 +942,8 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, } /* IPB bits in the backlog are merged with the TIMA IPB bits */ regs[TM_IPB] |= ipb; - backlog_prio = xive_ipb_to_pipr(regs[TM_IPB]); - backlog_level = 0; - - first_group = xive_get_field32(NVP2_W0_PGOFIRST, nvp.w0); - if (first_group && regs[TM_LSMFB] < backlog_prio) { - group_prio = xive2_presenter_backlog_scan(xptr, nvp_blk, nvp_idx, - first_group, &group_level); - regs[TM_LSMFB] = group_prio; - if (regs[TM_LGS] && group_prio < backlog_prio && - group_prio < regs[TM_CPPR]) { - - /* VP can take a group interrupt */ - xive2_presenter_backlog_decr(xptr, nvp_blk, nvp_idx, - group_prio, group_level); - backlog_prio = group_prio; - backlog_level = group_level; - } - } - /* - * Set the PIPR/NSR based on the restored state. - * It will raise the External interrupt signal if needed. - */ - xive_tctx_pipr_set(tctx, TM_QW1_OS, backlog_prio, backlog_level); + xive2_tctx_process_pending(tctx, TM_QW1_OS); } /* @@ -1103,8 +1077,12 @@ static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring) { uint8_t *sig_regs = &tctx->regs[sig_ring]; Xive2Router *xrtr = XIVE2_ROUTER(tctx->xptr); - uint8_t backlog_prio, first_group, group_level; - uint8_t pipr_min, lsmfb_min, ring_min; + uint8_t backlog_prio; + uint8_t first_group; + uint8_t group_level; + uint8_t pipr_min; + uint8_t lsmfb_min; + uint8_t ring_min; uint8_t cppr = sig_regs[TM_CPPR]; bool group_enabled; Xive2Nvp nvp; From 370ea4a4b6fffc30324fc8f8134483e5a749114d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:47 +1000 Subject: [PATCH 2563/2760] ppc/xive2: Avoid needless interrupt re-check on CPPR set MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When CPPR priority is decreased, pending interrupts do not need to be re-checked if one is already presented because by definition that will be the highest priority. This prevents a presented group interrupt from being lost. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-39-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index be945bef1c..531e6517ba 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1250,7 +1250,9 @@ static void xive2_tctx_set_cppr(XiveTCTX *tctx, uint8_t sig_ring, uint8_t cppr) } /* CPPR priority decreased (higher value) */ - xive2_tctx_process_pending(tctx, sig_ring); + if (!xive_nsr_indicates_exception(sig_ring, nsr)) { + xive2_tctx_process_pending(tctx, sig_ring); + } } void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, From 203181cebdb96283520b496d6eaa49634eb51579 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:48 +1000 Subject: [PATCH 2564/2760] ppc/xive: Assert group interrupts were redistributed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add some assertions to try to ensure presented group interrupts do not get lost without being redistributed, if they become precluded by CPPR or preempted by a higher priority interrupt. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-40-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 2 ++ hw/intc/xive2.c | 1 + 2 files changed, 3 insertions(+) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 6ad84f93c7..d609d552e8 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -132,6 +132,8 @@ void xive_tctx_pipr_set(XiveTCTX *tctx, uint8_t ring, uint8_t pipr, uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; + g_assert(!xive_nsr_indicates_group_exception(ring, sig_regs[TM_NSR])); + sig_regs[TM_PIPR] = pipr; if (pipr < sig_regs[TM_CPPR]) { diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 531e6517ba..a0a6b1a881 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1089,6 +1089,7 @@ static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring) int rc; g_assert(sig_ring == TM_QW3_HV_PHYS || sig_ring == TM_QW1_OS); + g_assert(!xive_nsr_indicates_group_exception(sig_ring, sig_regs[TM_NSR])); /* * Recompute the PIPR based on local pending interrupts. It will From 365e322cfb86b2e7131c3290c3a61f8d2bb224d3 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:49 +1000 Subject: [PATCH 2565/2760] ppc/xive2: implement NVP context save restore for POOL ring MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to implement POOL context push, add support for POOL NVP context save/restore. The NVP p bit is defined in the spec as follows: If TRUE, the CPPR of a Pool VP in the NVP is updated during store of the context with the CPPR of the Hard context it was running under. It's not clear whether non-pool VPs always or never get CPPR updated. Before this patch, OS contexts always save CPPR, so we will assume that is the behaviour. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-41-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 51 +++++++++++++++++++++++++------------ include/hw/ppc/xive2_regs.h | 1 + 2 files changed, 36 insertions(+), 16 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index a0a6b1a881..7631d48862 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -512,12 +512,13 @@ static void xive2_presenter_backlog_decr(XivePresenter *xptr, */ static void xive2_tctx_save_ctx(Xive2Router *xrtr, XiveTCTX *tctx, - uint8_t nvp_blk, uint32_t nvp_idx, - uint8_t ring) + uint8_t ring, + uint8_t nvp_blk, uint32_t nvp_idx) { CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env; uint32_t pir = env->spr_cb[SPR_PIR].default_value; Xive2Nvp nvp; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; if (xive2_router_get_nvp(xrtr, nvp_blk, nvp_idx, &nvp)) { @@ -553,7 +554,14 @@ static void xive2_tctx_save_ctx(Xive2Router *xrtr, XiveTCTX *tctx, } nvp.w2 = xive_set_field32(NVP2_W2_IPB, nvp.w2, regs[TM_IPB]); - nvp.w2 = xive_set_field32(NVP2_W2_CPPR, nvp.w2, regs[TM_CPPR]); + + if ((nvp.w0 & NVP2_W0_P) || ring != TM_QW2_HV_POOL) { + /* + * Non-pool contexts always save CPPR (ignore p bit). XXX: Clarify + * whether that is the correct behaviour. + */ + nvp.w2 = xive_set_field32(NVP2_W2_CPPR, nvp.w2, sig_regs[TM_CPPR]); + } if (nvp.w0 & NVP2_W0_L) { /* * Typically not used. If LSMFB is restored with 0, it will @@ -722,7 +730,7 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, } if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { - xive2_tctx_save_ctx(xrtr, tctx, nvp_blk, nvp_idx, ring); + xive2_tctx_save_ctx(xrtr, tctx, ring, nvp_blk, nvp_idx); } /* @@ -863,12 +871,15 @@ void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, xive2_tm_pull_ctx_ol(xptr, tctx, offset, value, size, TM_QW3_HV_PHYS); } -static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, - uint8_t nvp_blk, uint32_t nvp_idx, - Xive2Nvp *nvp) +static uint8_t xive2_tctx_restore_ctx(Xive2Router *xrtr, XiveTCTX *tctx, + uint8_t ring, + uint8_t nvp_blk, uint32_t nvp_idx, + Xive2Nvp *nvp) { CPUPPCState *env = &POWERPC_CPU(tctx->cs)->env; uint32_t pir = env->spr_cb[SPR_PIR].default_value; + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); + uint8_t *regs = &tctx->regs[ring]; uint8_t cppr; if (!xive2_nvp_is_hw(nvp)) { @@ -881,10 +892,10 @@ static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, nvp->w2 = xive_set_field32(NVP2_W2_CPPR, nvp->w2, 0); xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, nvp, 2); - tctx->regs[TM_QW1_OS + TM_CPPR] = cppr; - tctx->regs[TM_QW1_OS + TM_LSMFB] = xive_get_field32(NVP2_W2_LSMFB, nvp->w2); - tctx->regs[TM_QW1_OS + TM_LGS] = xive_get_field32(NVP2_W2_LGS, nvp->w2); - tctx->regs[TM_QW1_OS + TM_T] = xive_get_field32(NVP2_W2_T, nvp->w2); + sig_regs[TM_CPPR] = cppr; + regs[TM_LSMFB] = xive_get_field32(NVP2_W2_LSMFB, nvp->w2); + regs[TM_LGS] = xive_get_field32(NVP2_W2_LGS, nvp->w2); + regs[TM_T] = xive_get_field32(NVP2_W2_T, nvp->w2); nvp->w1 = xive_set_field32(NVP2_W1_CO, nvp->w1, 1); nvp->w1 = xive_set_field32(NVP2_W1_CO_THRID_VALID, nvp->w1, 1); @@ -893,9 +904,18 @@ static uint8_t xive2_tctx_restore_os_ctx(Xive2Router *xrtr, XiveTCTX *tctx, /* * Checkout privilege: 0:OS, 1:Pool, 2:Hard * - * TODO: we only support OS push/pull + * TODO: we don't support hard push/pull */ - nvp->w1 = xive_set_field32(NVP2_W1_CO_PRIV, nvp->w1, 0); + switch (ring) { + case TM_QW1_OS: + nvp->w1 = xive_set_field32(NVP2_W1_CO_PRIV, nvp->w1, 0); + break; + case TM_QW2_HV_POOL: + nvp->w1 = xive_set_field32(NVP2_W1_CO_PRIV, nvp->w1, 1); + break; + default: + g_assert_not_reached(); + } xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, nvp, 1); @@ -930,9 +950,8 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, } /* Automatically restore thread context registers */ - if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && - do_restore) { - xive2_tctx_restore_os_ctx(xrtr, tctx, nvp_blk, nvp_idx, &nvp); + if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_restore) { + xive2_tctx_restore_ctx(xrtr, tctx, TM_QW1_OS, nvp_blk, nvp_idx, &nvp); } ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2); diff --git a/include/hw/ppc/xive2_regs.h b/include/hw/ppc/xive2_regs.h index f82054661b..2a3e60abad 100644 --- a/include/hw/ppc/xive2_regs.h +++ b/include/hw/ppc/xive2_regs.h @@ -158,6 +158,7 @@ typedef struct Xive2Nvp { #define NVP2_W0_L PPC_BIT32(8) #define NVP2_W0_G PPC_BIT32(9) #define NVP2_W0_T PPC_BIT32(10) +#define NVP2_W0_P PPC_BIT32(11) #define NVP2_W0_ESC_END PPC_BIT32(25) /* 'N' bit 0:ESB 1:END */ #define NVP2_W0_PGOFIRST PPC_BITMASK32(26, 31) uint32_t w1; From 7a40b50757b55c2d4233c304f32df5afdd1bd63a Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:50 +1000 Subject: [PATCH 2566/2760] ppc/xive2: Prevent pulling of pool context losing phys interrupt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the pool context is pulled, the shared pool/phys signal is reset, which loses the qemu irq if a phys interrupt was presented. Only reset the signal if a poll irq was presented. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-42-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 7631d48862..112397459a 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -727,20 +727,22 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, xive2_redistribute(xrtr, tctx, cur_ring); } } + + /* + * Lower external interrupt line of requested ring and below except for + * USER, which doesn't exist. + */ + if (xive_nsr_indicates_exception(cur_ring, nsr)) { + if (cur_ring == xive_nsr_exception_ring(cur_ring, nsr)) { + xive_tctx_reset_signal(tctx, cur_ring); + } + } } if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { xive2_tctx_save_ctx(xrtr, tctx, ring, nvp_blk, nvp_idx); } - /* - * Lower external interrupt line of requested ring and below except for - * USER, which doesn't exist. - */ - for (cur_ring = TM_QW1_OS; cur_ring <= ring; - cur_ring += XIVE_TM_RING_SIZE) { - xive_tctx_reset_signal(tctx, cur_ring); - } return target_ringw2; } From 565e6d4d2151e856026ee60d16c12a61e667cd15 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:51 +1000 Subject: [PATCH 2567/2760] ppc/xive: Redistribute phys after pulling of pool context MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After pulling the pool context, if a pool irq had been presented and was cleared in the process, there could be a pending irq in phys that should be presented. Process the phys irq ring after pulling pool ring to catch this case and avoid losing irqs. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-43-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 3 +++ hw/intc/xive2.c | 16 ++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index d609d552e8..50a38bbf2e 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -320,6 +320,9 @@ static uint64_t xive_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, xive_tctx_reset_signal(tctx, TM_QW1_OS); xive_tctx_reset_signal(tctx, TM_QW2_HV_POOL); + /* Re-check phys for interrupts if pool was disabled */ + xive_tctx_pipr_recompute_from_ipb(tctx, TM_QW3_HV_PHYS); + return qw2w2; } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 112397459a..e3f9ff384a 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -683,6 +683,8 @@ static void xive2_redistribute(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring) xive_tctx_reset_signal(tctx, ring); } +static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring); + static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size, uint8_t ring) { @@ -739,6 +741,18 @@ static uint64_t xive2_tm_pull_ctx(XivePresenter *xptr, XiveTCTX *tctx, } } + if (ring == TM_QW2_HV_POOL) { + /* Re-check phys for interrupts if pool was disabled */ + nsr = tctx->regs[TM_QW3_HV_PHYS + TM_NSR]; + if (xive_nsr_indicates_exception(TM_QW3_HV_PHYS, nsr)) { + /* Ring must be PHYS because POOL would have been redistributed */ + g_assert(xive_nsr_exception_ring(TM_QW3_HV_PHYS, nsr) == + TM_QW3_HV_PHYS); + } else { + xive2_tctx_process_pending(tctx, TM_QW3_HV_PHYS); + } + } + if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_save) { xive2_tctx_save_ctx(xrtr, tctx, ring, nvp_blk, nvp_idx); } @@ -925,8 +939,6 @@ static uint8_t xive2_tctx_restore_ctx(Xive2Router *xrtr, XiveTCTX *tctx, return cppr; } -static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring); - static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) From ca0081ef7ece566e877e9f4ceab9e9988d08fb78 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:52 +1000 Subject: [PATCH 2568/2760] ppc/xive: Check TIMA operations validity MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Certain TIMA operations should only be performed when a ring is valid, others when the ring is invalid, and they are considered undefined if used incorrectly. Add checks for this condition. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-44-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 196 +++++++++++++++++++++++++----------------- include/hw/ppc/xive.h | 1 + 2 files changed, 116 insertions(+), 81 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 50a38bbf2e..6589c0a523 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -25,6 +25,19 @@ /* * XIVE Thread Interrupt Management context */ +bool xive_ring_valid(XiveTCTX *tctx, uint8_t ring) +{ + uint8_t cur_ring; + + for (cur_ring = ring; cur_ring <= TM_QW3_HV_PHYS; + cur_ring += XIVE_TM_RING_SIZE) { + if (!(tctx->regs[cur_ring + TM_WORD2] & 0x80)) { + return false; + } + } + return true; +} + bool xive_nsr_indicates_exception(uint8_t ring, uint8_t nsr) { switch (ring) { @@ -663,6 +676,8 @@ typedef struct XiveTmOp { uint8_t page_offset; uint32_t op_offset; unsigned size; + bool hw_ok; + bool sw_ok; void (*write_handler)(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); @@ -675,34 +690,34 @@ static const XiveTmOp xive_tm_operations[] = { * MMIOs below 2K : raw values and special operations without side * effects */ - { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive_tm_set_os_cppr, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive_tm_push_os_ctx, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive_tm_set_hv_cppr, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL, - xive_tm_vt_poll }, + { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, true, true, + xive_tm_set_os_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, true, true, + xive_tm_push_os_ctx, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, true, true, + xive_tm_set_hv_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, false, true, + xive_tm_vt_push, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, true, true, + NULL, xive_tm_vt_poll }, /* MMIOs above 2K : special operations with side effects */ - { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, NULL, - xive_tm_ack_os_reg }, - { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, xive_tm_set_os_pending, - NULL }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, NULL, - xive_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, NULL, - xive_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, NULL, - xive_tm_ack_hv_reg }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, NULL, - xive_tm_pull_pool_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL, - xive_tm_pull_pool_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, NULL, - xive_tm_pull_phys_ctx }, + { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, true, false, + NULL, xive_tm_ack_os_reg }, + { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, true, false, + xive_tm_set_os_pending, NULL }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, true, false, + NULL, xive_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, true, false, + NULL, xive_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, true, false, + NULL, xive_tm_ack_hv_reg }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, true, false, + NULL, xive_tm_pull_pool_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, true, false, + NULL, xive_tm_pull_pool_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, true, false, + NULL, xive_tm_pull_phys_ctx }, }; static const XiveTmOp xive2_tm_operations[] = { @@ -710,52 +725,48 @@ static const XiveTmOp xive2_tm_operations[] = { * MMIOs below 2K : raw values and special operations without side * effects */ - { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, xive2_tm_set_os_cppr, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, xive2_tm_push_os_ctx, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 8, xive2_tm_push_os_ctx, - NULL }, - { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_LGS, 1, xive_tm_set_os_lgs, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, xive2_tm_set_hv_cppr, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, xive_tm_vt_push, - NULL }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, NULL, - xive_tm_vt_poll }, - { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_T, 1, xive2_tm_set_hv_target, - NULL }, + { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_CPPR, 1, true, true, + xive2_tm_set_os_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 4, true, true, + xive2_tm_push_os_ctx, NULL }, + { XIVE_TM_HV_PAGE, TM_QW1_OS + TM_WORD2, 8, true, true, + xive2_tm_push_os_ctx, NULL }, + { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_LGS, 1, true, true, + xive_tm_set_os_lgs, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, true, true, + xive2_tm_set_hv_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, true, true, + NULL, xive_tm_vt_poll }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_T, 1, true, true, + xive2_tm_set_hv_target, NULL }, /* MMIOs above 2K : special operations with side effects */ - { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, NULL, - xive_tm_ack_os_reg }, - { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, xive_tm_set_os_pending, - NULL }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_G2, 4, NULL, - xive2_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, NULL, - xive2_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, NULL, - xive2_tm_pull_os_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, NULL, - xive_tm_ack_hv_reg }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX_G2, 4, NULL, - xive2_tm_pull_pool_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, NULL, - xive2_tm_pull_pool_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, NULL, - xive2_tm_pull_pool_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_OL, 1, xive2_tm_pull_os_ctx_ol, - NULL }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_G2, 4, NULL, - xive2_tm_pull_phys_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, NULL, - xive2_tm_pull_phys_ctx }, - { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_OL, 1, xive2_tm_pull_phys_ctx_ol, - NULL }, - { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_EL, 1, xive2_tm_ack_os_el, - NULL }, + { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, true, false, + NULL, xive_tm_ack_os_reg }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_G2, 4, true, false, + NULL, xive2_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, true, false, + NULL, xive2_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 8, true, false, + NULL, xive2_tm_pull_os_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_ACK_HV_REG, 2, true, false, + NULL, xive_tm_ack_hv_reg }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX_G2, 4, true, false, + NULL, xive2_tm_pull_pool_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 4, true, false, + NULL, xive2_tm_pull_pool_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_POOL_CTX, 8, true, false, + NULL, xive2_tm_pull_pool_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_OL, 1, true, false, + xive2_tm_pull_os_ctx_ol, NULL }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_G2, 4, true, false, + NULL, xive2_tm_pull_phys_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX, 1, true, false, + NULL, xive2_tm_pull_phys_ctx }, + { XIVE_TM_HV_PAGE, TM_SPC_PULL_PHYS_CTX_OL, 1, true, false, + xive2_tm_pull_phys_ctx_ol, NULL }, + { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_EL, 1, true, false, + xive2_tm_ack_os_el, NULL }, }; static const XiveTmOp *xive_tm_find_op(XivePresenter *xptr, hwaddr offset, @@ -797,18 +808,28 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size) { const XiveTmOp *xto; + uint8_t ring = offset & TM_RING_OFFSET; + bool is_valid = xive_ring_valid(tctx, ring); + bool hw_owned = is_valid; trace_xive_tctx_tm_write(tctx->cs->cpu_index, offset, size, value); - /* - * TODO: check V bit in Q[0-3]W2 - */ - /* * First, check for special operations in the 2K region */ + xto = xive_tm_find_op(tctx->xptr, offset, size, true); + if (xto) { + if (hw_owned && !xto->hw_ok) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined write to HW TIMA " + "@%"HWADDR_PRIx" size %d\n", offset, size); + } + if (!hw_owned && !xto->sw_ok) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined write to SW TIMA " + "@%"HWADDR_PRIx" size %d\n", offset, size); + } + } + if (offset & TM_SPECIAL_OP) { - xto = xive_tm_find_op(tctx->xptr, offset, size, true); if (!xto) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid write access at TIMA " "@%"HWADDR_PRIx" size %d\n", offset, size); @@ -821,7 +842,6 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, /* * Then, for special operations in the region below 2K. */ - xto = xive_tm_find_op(tctx->xptr, offset, size, true); if (xto) { xto->write_handler(xptr, tctx, offset, value, size); return; @@ -830,6 +850,11 @@ void xive_tctx_tm_write(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, /* * Finish with raw access to the register values */ + if (hw_owned) { + /* Store context operations are dangerous when context is valid */ + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined write to HW TIMA " + "@%"HWADDR_PRIx" size %d\n", offset, size); + } xive_tm_raw_write(tctx, offset, value, size); } @@ -837,17 +862,27 @@ uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size) { const XiveTmOp *xto; + uint8_t ring = offset & TM_RING_OFFSET; + bool is_valid = xive_ring_valid(tctx, ring); + bool hw_owned = is_valid; uint64_t ret; - /* - * TODO: check V bit in Q[0-3]W2 - */ + xto = xive_tm_find_op(tctx->xptr, offset, size, false); + if (xto) { + if (hw_owned && !xto->hw_ok) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined read to HW TIMA " + "@%"HWADDR_PRIx" size %d\n", offset, size); + } + if (!hw_owned && !xto->sw_ok) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: undefined read to SW TIMA " + "@%"HWADDR_PRIx" size %d\n", offset, size); + } + } /* * First, check for special operations in the 2K region */ if (offset & TM_SPECIAL_OP) { - xto = xive_tm_find_op(tctx->xptr, offset, size, false); if (!xto) { qemu_log_mask(LOG_GUEST_ERROR, "XIVE: invalid read access to TIMA" "@%"HWADDR_PRIx" size %d\n", offset, size); @@ -860,7 +895,6 @@ uint64_t xive_tctx_tm_read(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, /* * Then, for special operations in the region below 2K. */ - xto = xive_tm_find_op(tctx->xptr, offset, size, false); if (xto) { ret = xto->read_handler(xptr, tctx, offset, size); goto out; diff --git a/include/hw/ppc/xive.h b/include/hw/ppc/xive.h index 2372d1014b..b7ca8544e4 100644 --- a/include/hw/ppc/xive.h +++ b/include/hw/ppc/xive.h @@ -365,6 +365,7 @@ static inline uint32_t xive_tctx_word2(uint8_t *ring) return *((uint32_t *) &ring[TM_WORD2]); } +bool xive_ring_valid(XiveTCTX *tctx, uint8_t ring); bool xive_nsr_indicates_exception(uint8_t ring, uint8_t nsr); bool xive_nsr_indicates_group_exception(uint8_t ring, uint8_t nsr); uint8_t xive_nsr_exception_ring(uint8_t ring, uint8_t nsr); From ba127a1e48d34f6c805000dc881e240fc4f2bd1d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:53 +1000 Subject: [PATCH 2569/2760] ppc/xive2: Implement pool context push TIMA op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement pool context push TIMA op. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-45-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 4 ++++ hw/intc/xive2.c | 50 ++++++++++++++++++++++++++++-------------- include/hw/ppc/xive2.h | 2 ++ 3 files changed, 39 insertions(+), 17 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 6589c0a523..e7f77be2f7 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -733,6 +733,10 @@ static const XiveTmOp xive2_tm_operations[] = { xive2_tm_push_os_ctx, NULL }, { XIVE_TM_OS_PAGE, TM_QW1_OS + TM_LGS, 1, true, true, xive_tm_set_os_lgs, NULL }, + { XIVE_TM_HV_PAGE, TM_QW2_HV_POOL + TM_WORD2, 4, true, true, + xive2_tm_push_pool_ctx, NULL }, + { XIVE_TM_HV_PAGE, TM_QW2_HV_POOL + TM_WORD2, 8, true, true, + xive2_tm_push_pool_ctx, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, true, true, xive2_tm_set_hv_cppr, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, true, true, diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index e3f9ff384a..4244e1d02b 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -583,6 +583,7 @@ static void xive2_tctx_save_ctx(Xive2Router *xrtr, XiveTCTX *tctx, xive2_router_write_nvp(xrtr, nvp_blk, nvp_idx, &nvp, 1); } +/* POOL cam is the same as OS cam encoding */ static void xive2_cam_decode(uint32_t cam, uint8_t *nvp_blk, uint32_t *nvp_idx, bool *valid, bool *hw) { @@ -940,10 +941,11 @@ static uint8_t xive2_tctx_restore_ctx(Xive2Router *xrtr, XiveTCTX *tctx, } static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, + uint8_t ring, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) { - uint8_t *regs = &tctx->regs[TM_QW1_OS]; + uint8_t *regs = &tctx->regs[ring]; uint8_t ipb; Xive2Nvp nvp; @@ -965,7 +967,7 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, /* Automatically restore thread context registers */ if (xive2_router_get_config(xrtr) & XIVE2_VP_SAVE_RESTORE && do_restore) { - xive2_tctx_restore_ctx(xrtr, tctx, TM_QW1_OS, nvp_blk, nvp_idx, &nvp); + xive2_tctx_restore_ctx(xrtr, tctx, ring, nvp_blk, nvp_idx, &nvp); } ipb = xive_get_field32(NVP2_W2_IPB, nvp.w2); @@ -976,48 +978,62 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, /* IPB bits in the backlog are merged with the TIMA IPB bits */ regs[TM_IPB] |= ipb; - xive2_tctx_process_pending(tctx, TM_QW1_OS); + xive2_tctx_process_pending(tctx, ring == TM_QW2_HV_POOL ? + TM_QW3_HV_PHYS : ring); } /* - * Updating the OS CAM line can trigger a resend of interrupt + * Updating the ring CAM line can trigger a resend of interrupt */ -void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, - hwaddr offset, uint64_t value, unsigned size) +static void xive2_tm_push_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size, + uint8_t ring) { uint32_t cam; - uint32_t qw1w2; - uint64_t qw1dw1; + uint32_t w2; + uint64_t dw1; uint8_t nvp_blk; uint32_t nvp_idx; - bool vo; + bool v; bool do_restore; /* First update the thead context */ switch (size) { case 4: cam = value; - qw1w2 = cpu_to_be32(cam); - memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1w2, 4); + w2 = cpu_to_be32(cam); + memcpy(&tctx->regs[ring + TM_WORD2], &w2, 4); break; case 8: cam = value >> 32; - qw1dw1 = cpu_to_be64(value); - memcpy(&tctx->regs[TM_QW1_OS + TM_WORD2], &qw1dw1, 8); + dw1 = cpu_to_be64(value); + memcpy(&tctx->regs[ring + TM_WORD2], &dw1, 8); break; default: g_assert_not_reached(); } - xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &vo, &do_restore); + xive2_cam_decode(cam, &nvp_blk, &nvp_idx, &v, &do_restore); /* Check the interrupt pending bits */ - if (vo) { - xive2_tctx_need_resend(XIVE2_ROUTER(xptr), tctx, nvp_blk, nvp_idx, - do_restore); + if (v) { + xive2_tctx_need_resend(XIVE2_ROUTER(xptr), tctx, ring, + nvp_blk, nvp_idx, do_restore); } } +void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tm_push_ctx(xptr, tctx, offset, value, size, TM_QW1_OS); +} + +void xive2_tm_push_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tm_push_ctx(xptr, tctx, offset, value, size, TM_QW2_HV_POOL); +} + /* returns -1 if ring is invalid, but still populates block and index */ static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring, uint8_t *nvp_blk, uint32_t *nvp_idx) diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index a91b99057c..c1ab06a55a 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -140,6 +140,8 @@ bool xive2_tm_irq_precluded(XiveTCTX *tctx, int ring, uint8_t priority); void xive2_tm_set_lsmfb(XiveTCTX *tctx, int ring, uint8_t priority); void xive2_tm_set_hv_target(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); +void xive2_tm_push_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); uint64_t xive2_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size); uint64_t xive2_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, From 6936d2f561759c00993217a424ddeb1554c5f1ff Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:54 +1000 Subject: [PATCH 2570/2760] ppc/xive2: redistribute group interrupts on context push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pushing a context, any presented group interrupt should be redistributed before processing pending interrupts to present highest priority. This can occur when pushing the POOL ring when the valid PHYS ring has a group interrupt presented, because they share signal registers. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-46-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 4244e1d02b..23eb85bb86 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -945,8 +945,9 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) { + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; - uint8_t ipb; + uint8_t ipb, nsr = sig_regs[TM_NSR]; Xive2Nvp nvp; /* @@ -978,6 +979,11 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, /* IPB bits in the backlog are merged with the TIMA IPB bits */ regs[TM_IPB] |= ipb; + if (xive_nsr_indicates_group_exception(ring, nsr)) { + /* redistribute precluded active grp interrupt */ + g_assert(ring == TM_QW2_HV_POOL); /* PHYS ring has the grp interrupt */ + xive2_redistribute(xrtr, tctx, xive_nsr_exception_ring(ring, nsr)); + } xive2_tctx_process_pending(tctx, ring == TM_QW2_HV_POOL ? TM_QW3_HV_PHYS : ring); } From 6ef77843603b89b1e48a06ca0644e74e45297839 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:55 +1000 Subject: [PATCH 2571/2760] ppc/xive2: Implement set_os_pending TIMA op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit xive2 must take into account redistribution of group interrupts if the VP directed priority exceeds the group interrupt priority after this operation. The xive1 code is not group aware so implement this for xive2. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-47-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 2 ++ hw/intc/xive2.c | 28 ++++++++++++++++++++++++++++ include/hw/ppc/xive2.h | 2 ++ 3 files changed, 32 insertions(+) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index e7f77be2f7..25cb3877cb 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -747,6 +747,8 @@ static const XiveTmOp xive2_tm_operations[] = { /* MMIOs above 2K : special operations with side effects */ { XIVE_TM_OS_PAGE, TM_SPC_ACK_OS_REG, 2, true, false, NULL, xive_tm_ack_os_reg }, + { XIVE_TM_OS_PAGE, TM_SPC_SET_OS_PENDING, 1, true, false, + xive2_tm_set_os_pending, NULL }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX_G2, 4, true, false, NULL, xive2_tm_pull_os_ctx }, { XIVE_TM_HV_PAGE, TM_SPC_PULL_OS_CTX, 4, true, false, diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 23eb85bb86..f9eaea1192 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1323,6 +1323,34 @@ void xive2_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx, xive2_tctx_set_cppr(tctx, TM_QW1_OS, value & 0xff); } +/* + * Adjust the IPB to allow a CPU to process event queues of other + * priorities during one physical interrupt cycle. + */ +void xive2_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint8_t ring = TM_QW1_OS; + uint8_t *regs = &tctx->regs[ring]; + uint8_t priority = value & 0xff; + + /* + * XXX: should this simply set a bit in IPB and wait for it to be picked + * up next cycle, or is it supposed to present it now? We implement the + * latter here. + */ + regs[TM_IPB] |= xive_priority_to_ipb(priority); + if (xive_ipb_to_pipr(regs[TM_IPB]) >= regs[TM_PIPR]) { + return; + } + if (xive_nsr_indicates_group_exception(ring, regs[TM_NSR])) { + xive2_redistribute(xrtr, tctx, ring); + } + + xive_tctx_pipr_present(tctx, ring, priority, 0); +} + static void xive2_tctx_set_target(XiveTCTX *tctx, uint8_t ring, uint8_t target) { uint8_t *regs = &tctx->regs[ring]; diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index c1ab06a55a..45266c2a8b 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -130,6 +130,8 @@ void xive2_tm_set_hv_cppr(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); void xive2_tm_set_os_cppr(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); +void xive2_tm_set_os_pending(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); void xive2_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); uint64_t xive2_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, From f030f35109062a3cf815e12939a66c9df8354714 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:56 +1000 Subject: [PATCH 2572/2760] ppc/xive2: Implement POOL LGS push TIMA op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement set LGS for the POOL ring. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-48-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 25cb3877cb..725ba72b8f 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -532,6 +532,12 @@ static void xive_tm_set_os_lgs(XivePresenter *xptr, XiveTCTX *tctx, xive_tctx_set_lgs(tctx, TM_QW1_OS, value & 0xff); } +static void xive_tm_set_pool_lgs(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive_tctx_set_lgs(tctx, TM_QW2_HV_POOL, value & 0xff); +} + /* * Adjust the PIPR to allow a CPU to process event queues of other * priorities during one physical interrupt cycle. @@ -737,6 +743,8 @@ static const XiveTmOp xive2_tm_operations[] = { xive2_tm_push_pool_ctx, NULL }, { XIVE_TM_HV_PAGE, TM_QW2_HV_POOL + TM_WORD2, 8, true, true, xive2_tm_push_pool_ctx, NULL }, + { XIVE_TM_HV_PAGE, TM_QW2_HV_POOL + TM_LGS, 1, true, true, + xive_tm_set_pool_lgs, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, true, true, xive2_tm_set_hv_cppr, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, true, true, From 714bae7351d9c85643d6b48109f59f20f05c8466 Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:57 +1000 Subject: [PATCH 2573/2760] ppc/xive2: Implement PHYS ring VP push TIMA op MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement the phys (aka hard) VP push. PowerVM uses this operation. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-49-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 2 ++ hw/intc/xive2.c | 11 +++++++++++ include/hw/ppc/xive2.h | 2 ++ 3 files changed, 15 insertions(+) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 725ba72b8f..8b7182fbb8 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -747,6 +747,8 @@ static const XiveTmOp xive2_tm_operations[] = { xive_tm_set_pool_lgs, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_CPPR, 1, true, true, xive2_tm_set_hv_cppr, NULL }, + { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, false, true, + xive2_tm_push_phys_ctx, NULL }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_WORD2, 1, true, true, NULL, xive_tm_vt_poll }, { XIVE_TM_HV_PAGE, TM_QW3_HV_PHYS + TM_T, 1, true, true, diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index f9eaea1192..1b00568796 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -1005,6 +1005,11 @@ static void xive2_tm_push_ctx(XivePresenter *xptr, XiveTCTX *tctx, /* First update the thead context */ switch (size) { + case 1: + tctx->regs[ring + TM_WORD2] = value & 0xff; + cam = xive2_tctx_hw_cam_line(xptr, tctx); + cam |= ((value & 0xc0) << 24); /* V and H bits */ + break; case 4: cam = value; w2 = cpu_to_be32(cam); @@ -1040,6 +1045,12 @@ void xive2_tm_push_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, xive2_tm_push_ctx(xptr, tctx, offset, value, size, TM_QW2_HV_POOL); } +void xive2_tm_push_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size) +{ + xive2_tm_push_ctx(xptr, tctx, offset, value, size, TM_QW3_HV_PHYS); +} + /* returns -1 if ring is invalid, but still populates block and index */ static int xive2_tctx_get_nvp_indexes(XiveTCTX *tctx, uint8_t ring, uint8_t *nvp_blk, uint32_t *nvp_idx) diff --git a/include/hw/ppc/xive2.h b/include/hw/ppc/xive2.h index 45266c2a8b..f4437e2c79 100644 --- a/include/hw/ppc/xive2.h +++ b/include/hw/ppc/xive2.h @@ -146,6 +146,8 @@ void xive2_tm_push_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, uint64_t value, unsigned size); uint64_t xive2_tm_pull_pool_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size); +void xive2_tm_push_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, + hwaddr offset, uint64_t value, unsigned size); uint64_t xive2_tm_pull_phys_ctx(XivePresenter *xptr, XiveTCTX *tctx, hwaddr offset, unsigned size); void xive2_tm_pull_phys_ctx_ol(XivePresenter *xptr, XiveTCTX *tctx, From 3a50f36469c318d3c66360a8e5ada6f2dc1a349d Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:58 +1000 Subject: [PATCH 2574/2760] ppc/xive: Split need_resend into restore_nvp MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed by the next patch which will re-send on all lower rings when pushing a context. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-50-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive.c | 24 ++++++++++++------------ hw/intc/xive2.c | 28 ++++++++++++++++------------ 2 files changed, 28 insertions(+), 24 deletions(-) diff --git a/hw/intc/xive.c b/hw/intc/xive.c index 8b7182fbb8..e0ffcf89eb 100644 --- a/hw/intc/xive.c +++ b/hw/intc/xive.c @@ -606,7 +606,7 @@ static uint64_t xive_tm_pull_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, return qw1w2; } -static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx, +static void xive_tctx_restore_nvp(XiveRouter *xrtr, XiveTCTX *tctx, uint8_t nvt_blk, uint32_t nvt_idx) { XiveNVT nvt; @@ -632,16 +632,6 @@ static void xive_tctx_need_resend(XiveRouter *xrtr, XiveTCTX *tctx, uint8_t *regs = &tctx->regs[TM_QW1_OS]; regs[TM_IPB] |= ipb; } - - /* - * Always call xive_tctx_recompute_from_ipb(). Even if there were no - * escalation triggered, there could be a pending interrupt which - * was saved when the context was pulled and that we need to take - * into account by recalculating the PIPR (which is not - * saved/restored). - * It will also raise the External interrupt signal if needed. - */ - xive_tctx_pipr_recompute_from_ipb(tctx, TM_QW1_OS); /* fxb */ } /* @@ -663,7 +653,17 @@ static void xive_tm_push_os_ctx(XivePresenter *xptr, XiveTCTX *tctx, /* Check the interrupt pending bits */ if (vo) { - xive_tctx_need_resend(XIVE_ROUTER(xptr), tctx, nvt_blk, nvt_idx); + xive_tctx_restore_nvp(XIVE_ROUTER(xptr), tctx, nvt_blk, nvt_idx); + + /* + * Always call xive_tctx_recompute_from_ipb(). Even if there were no + * escalation triggered, there could be a pending interrupt which + * was saved when the context was pulled and that we need to take + * into account by recalculating the PIPR (which is not + * saved/restored). + * It will also raise the External interrupt signal if needed. + */ + xive_tctx_pipr_recompute_from_ipb(tctx, TM_QW1_OS); /* fxb */ } } diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index 1b00568796..c3c6871e91 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -940,14 +940,14 @@ static uint8_t xive2_tctx_restore_ctx(Xive2Router *xrtr, XiveTCTX *tctx, return cppr; } -static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, +/* Restore TIMA VP context from NVP backlog */ +static void xive2_tctx_restore_nvp(Xive2Router *xrtr, XiveTCTX *tctx, uint8_t ring, uint8_t nvp_blk, uint32_t nvp_idx, bool do_restore) { - uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); uint8_t *regs = &tctx->regs[ring]; - uint8_t ipb, nsr = sig_regs[TM_NSR]; + uint8_t ipb; Xive2Nvp nvp; /* @@ -978,14 +978,6 @@ static void xive2_tctx_need_resend(Xive2Router *xrtr, XiveTCTX *tctx, } /* IPB bits in the backlog are merged with the TIMA IPB bits */ regs[TM_IPB] |= ipb; - - if (xive_nsr_indicates_group_exception(ring, nsr)) { - /* redistribute precluded active grp interrupt */ - g_assert(ring == TM_QW2_HV_POOL); /* PHYS ring has the grp interrupt */ - xive2_redistribute(xrtr, tctx, xive_nsr_exception_ring(ring, nsr)); - } - xive2_tctx_process_pending(tctx, ring == TM_QW2_HV_POOL ? - TM_QW3_HV_PHYS : ring); } /* @@ -1028,8 +1020,20 @@ static void xive2_tm_push_ctx(XivePresenter *xptr, XiveTCTX *tctx, /* Check the interrupt pending bits */ if (v) { - xive2_tctx_need_resend(XIVE2_ROUTER(xptr), tctx, ring, + Xive2Router *xrtr = XIVE2_ROUTER(xptr); + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); + uint8_t nsr = sig_regs[TM_NSR]; + + xive2_tctx_restore_nvp(xrtr, tctx, ring, nvp_blk, nvp_idx, do_restore); + + if (xive_nsr_indicates_group_exception(ring, nsr)) { + /* redistribute precluded active grp interrupt */ + g_assert(ring == TM_QW2_HV_POOL); /* PHYS ring has the interrupt */ + xive2_redistribute(xrtr, tctx, xive_nsr_exception_ring(ring, nsr)); + } + xive2_tctx_process_pending(tctx, ring == TM_QW2_HV_POOL ? + TM_QW3_HV_PHYS : ring); } } From df3614b7983e0629b0d422259968985ca0117bfa Mon Sep 17 00:00:00 2001 From: Nicholas Piggin Date: Mon, 12 May 2025 13:10:59 +1000 Subject: [PATCH 2575/2760] ppc/xive2: Enable lower level contexts on VP push MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When pushing a context, the lower-level context becomes valid if it had V=1, and so on. Iterate lower level contexts and send them pending interrupts if they become enabled. Signed-off-by: Nicholas Piggin Reviewed-by: Glenn Miles Reviewed-by: Michael Kowal Tested-by: Gautam Menghani Link: https://lore.kernel.org/qemu-devel/20250512031100.439842-51-npiggin@gmail.com Signed-off-by: Cédric Le Goater --- hw/intc/xive2.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/hw/intc/xive2.c b/hw/intc/xive2.c index c3c6871e91..ee5fa26178 100644 --- a/hw/intc/xive2.c +++ b/hw/intc/xive2.c @@ -995,6 +995,12 @@ static void xive2_tm_push_ctx(XivePresenter *xptr, XiveTCTX *tctx, bool v; bool do_restore; + if (xive_ring_valid(tctx, ring)) { + qemu_log_mask(LOG_GUEST_ERROR, "XIVE: Attempt to push VP to enabled" + " ring 0x%02x\n", ring); + return; + } + /* First update the thead context */ switch (size) { case 1: @@ -1021,19 +1027,32 @@ static void xive2_tm_push_ctx(XivePresenter *xptr, XiveTCTX *tctx, /* Check the interrupt pending bits */ if (v) { Xive2Router *xrtr = XIVE2_ROUTER(xptr); - uint8_t *sig_regs = xive_tctx_signal_regs(tctx, ring); - uint8_t nsr = sig_regs[TM_NSR]; + uint8_t cur_ring; xive2_tctx_restore_nvp(xrtr, tctx, ring, nvp_blk, nvp_idx, do_restore); - if (xive_nsr_indicates_group_exception(ring, nsr)) { - /* redistribute precluded active grp interrupt */ - g_assert(ring == TM_QW2_HV_POOL); /* PHYS ring has the interrupt */ - xive2_redistribute(xrtr, tctx, xive_nsr_exception_ring(ring, nsr)); + for (cur_ring = TM_QW1_OS; cur_ring <= ring; + cur_ring += XIVE_TM_RING_SIZE) { + uint8_t *sig_regs = xive_tctx_signal_regs(tctx, cur_ring); + uint8_t nsr = sig_regs[TM_NSR]; + + if (!xive_ring_valid(tctx, cur_ring)) { + continue; + } + + if (cur_ring == TM_QW2_HV_POOL) { + if (xive_nsr_indicates_exception(cur_ring, nsr)) { + g_assert(xive_nsr_exception_ring(cur_ring, nsr) == + TM_QW3_HV_PHYS); + xive2_redistribute(xrtr, tctx, + xive_nsr_exception_ring(ring, nsr)); + } + xive2_tctx_process_pending(tctx, TM_QW3_HV_PHYS); + break; + } + xive2_tctx_process_pending(tctx, cur_ring); } - xive2_tctx_process_pending(tctx, ring == TM_QW2_HV_POOL ? - TM_QW3_HV_PHYS : ring); } } @@ -1159,6 +1178,7 @@ static void xive2_tctx_process_pending(XiveTCTX *tctx, uint8_t sig_ring) int rc; g_assert(sig_ring == TM_QW3_HV_PHYS || sig_ring == TM_QW1_OS); + g_assert(sig_regs[TM_WORD2] & 0x80); g_assert(!xive_nsr_indicates_group_exception(sig_ring, sig_regs[TM_NSR])); /* From 8c8f62baa61b55b297453a8d416c6b0932398948 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Mon, 21 Jul 2025 10:07:52 +0100 Subject: [PATCH 2576/2760] hvf: arm: Remove $pc from trace_hvf_data_abort() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't synchronize vcpu registers from the hardware accelerator (e.g., by cpu_synchronize_state()) in the Dabort handler, so env->pc points to the instruction which has nothing to do with the Dabort at all. And it doesn't seem to make much sense to log PC in every Dabort handler, let's just remove it from this trace event. Signed-off-by: Zenghui Yu Reviewed-by: Mads Ynddal Message-id: 20250713154719.4248-1-zenghui.yu@linux.dev Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 2 +- target/arm/hvf/trace-events | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index c9cfcdc08b..8f93e42b34 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -2005,7 +2005,7 @@ int hvf_vcpu_exec(CPUState *cpu) uint32_t cm = (syndrome >> 8) & 0x1; uint64_t val = 0; - trace_hvf_data_abort(env->pc, hvf_exit->exception.virtual_address, + trace_hvf_data_abort(hvf_exit->exception.virtual_address, hvf_exit->exception.physical_address, isv, iswrite, s1ptw, len, srt); diff --git a/target/arm/hvf/trace-events b/target/arm/hvf/trace-events index b49746f28d..b29a995f3d 100644 --- a/target/arm/hvf/trace-events +++ b/target/arm/hvf/trace-events @@ -2,7 +2,7 @@ hvf_unhandled_sysreg_read(uint64_t pc, uint32_t reg, uint32_t op0, uint32_t op1, hvf_unhandled_sysreg_write(uint64_t pc, uint32_t reg, uint32_t op0, uint32_t op1, uint32_t crn, uint32_t crm, uint32_t op2) "unhandled sysreg write at pc=0x%"PRIx64": 0x%08x (op0=%d op1=%d crn=%d crm=%d op2=%d)" hvf_inject_fiq(void) "injecting FIQ" hvf_inject_irq(void) "injecting IRQ" -hvf_data_abort(uint64_t pc, uint64_t va, uint64_t pa, bool isv, bool iswrite, bool s1ptw, uint32_t len, uint32_t srt) "data abort: [pc=0x%"PRIx64" va=0x%016"PRIx64" pa=0x%016"PRIx64" isv=%d iswrite=%d s1ptw=%d len=%d srt=%d]" +hvf_data_abort(uint64_t va, uint64_t pa, bool isv, bool iswrite, bool s1ptw, uint32_t len, uint32_t srt) "data abort: [va=0x%016"PRIx64" pa=0x%016"PRIx64" isv=%d iswrite=%d s1ptw=%d len=%d srt=%d]" hvf_sysreg_read(uint32_t reg, uint32_t op0, uint32_t op1, uint32_t crn, uint32_t crm, uint32_t op2, uint64_t val) "sysreg read 0x%08x (op0=%d op1=%d crn=%d crm=%d op2=%d) = 0x%016"PRIx64 hvf_sysreg_write(uint32_t reg, uint32_t op0, uint32_t op1, uint32_t crn, uint32_t crm, uint32_t op2, uint64_t val) "sysreg write 0x%08x (op0=%d op1=%d crn=%d crm=%d op2=%d, val=0x%016"PRIx64")" hvf_unknown_hvc(uint64_t pc, uint64_t x0) "pc=0x%"PRIx64" unknown HVC! 0x%016"PRIx64 From 655659a74a36b63e33d2dc969d3c44beb1b008b3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 21 Jul 2025 10:07:52 +0100 Subject: [PATCH 2577/2760] target/arm: Correct encoding of Debug Communications Channel registers We don't implement the Debug Communications Channel (DCC), but we do attempt to provide dummy versions of its system registers so that software that tries to access them doesn't fall over. However, we got the tx/rx register definitions wrong. These should be: AArch32: DBGDTRTX p14 0 c0 c5 0 (on writes) DBGDTRRX p14 0 c0 c5 0 (on reads) AArch64: DBGDTRTX_EL0 2 3 0 5 0 (on writes) DBGDTRRX_EL0 2 3 0 5 0 (on reads) DBGDTR_EL0 2 3 0 4 0 (reads and writes) where DBGDTRTX and DBGDTRRX are effectively different names for the same 32-bit register, which has tx behaviour on writes and rx behaviour on reads. The AArch64-only DBGDTR_EL0 is a 64-bit wide register whose top and bottom halves map to the DBGDTRRX and DBGDTRTX registers. Currently we have just one cpreg struct, which: * calls itself DBGDTR_EL0 * uses the DBGDTRTX_EL0/DBGDTRRX_EL0 encoding * is marked as ARM_CP_STATE_BOTH but has the wrong opc1 value for AArch32 * is implemented as RAZ/WI Correct the encoding so: * we name the DBGDTRTX/DBGDTRRX register correctly * we split it into AA64 and AA32 versions so we can get the AA32 encoding right * we implement DBGDTR_EL0 at its correct encoding Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2986 Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250708141049.778361-1-peter.maydell@linaro.org --- target/arm/debug_helper.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index 69fb1d0d9f..aee06d4d42 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -988,11 +988,20 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .opc0 = 2, .opc1 = 0, .crn = 0, .crm = 3, .opc2 = 2, .access = PL1_RW, .accessfn = access_tdcc, .type = ARM_CP_CONST, .resetvalue = 0 }, - /* DBGDTRTX_EL0/DBGDTRRX_EL0 depend on direction */ - { .name = "DBGDTR_EL0", .state = ARM_CP_STATE_BOTH, .cp = 14, + /* Architecturally DBGDTRTX is named DBGDTRRX when used for reads */ + { .name = "DBGDTRTX_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 5, .opc2 = 0, .access = PL0_RW, .accessfn = access_tdcc, .type = ARM_CP_CONST, .resetvalue = 0 }, + { .name = "DBGDTRTX", .state = ARM_CP_STATE_AA32, .cp = 14, + .opc1 = 0, .crn = 0, .crm = 5, .opc2 = 0, + .access = PL0_RW, .accessfn = access_tdcc, + .type = ARM_CP_CONST, .resetvalue = 0 }, + /* This is AArch64-only and is a combination of DBGDTRTX and DBGDTRRX */ + { .name = "DBGDTR_EL0", .state = ARM_CP_STATE_AA64, + .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 4, .opc2 = 0, + .access = PL0_RW, .accessfn = access_tdcc, + .type = ARM_CP_CONST, .resetvalue = 0 }, /* * OSECCR_EL1 provides a mechanism for an operating system * to access the contents of EDECCR. EDECCR is not implemented though, From 8ccd35f25cdf2e03f44585a11b7daf93d1d46a3a Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 21 Jul 2025 10:07:53 +0100 Subject: [PATCH 2578/2760] hw/misc/ivshmem-pci: Improve error handling Coverity points out that the ivshmem-pci code has some error handling cases where it incorrectly tries to use an invalid filedescriptor. These generally happen because ivshmem_recv_msg() calls qemu_chr_fe_get_msgfd(), which might return -1, but the code in process_msg() generally assumes that the file descriptor was provided when it was supposed to be. In particular: * the error case in process_msg() only needs to close the fd if one was provided * process_msg_shmem() should fail if no fd was provided Coverity: CID 1508726 Signed-off-by: Peter Maydell Reviewed-by: Markus Armbruster Message-id: 20250711145012.1521936-1-peter.maydell@linaro.org --- hw/misc/ivshmem-pci.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/hw/misc/ivshmem-pci.c b/hw/misc/ivshmem-pci.c index 5a10bca633..d47ae739d6 100644 --- a/hw/misc/ivshmem-pci.c +++ b/hw/misc/ivshmem-pci.c @@ -479,6 +479,11 @@ static void process_msg_shmem(IVShmemState *s, int fd, Error **errp) struct stat buf; size_t size; + if (fd < 0) { + error_setg(errp, "server didn't provide fd with shared memory message"); + return; + } + if (s->ivshmem_bar2) { error_setg(errp, "server sent unexpected shared memory message"); close(fd); @@ -553,7 +558,9 @@ static void process_msg(IVShmemState *s, int64_t msg, int fd, Error **errp) if (msg < -1 || msg > IVSHMEM_MAX_PEERS) { error_setg(errp, "server sent invalid message %" PRId64, msg); - close(fd); + if (fd >= 0) { + close(fd); + } return; } From 32d8fb61e5a1fb5feeecce85d461be019cc30a54 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 21 Jul 2025 10:07:53 +0100 Subject: [PATCH 2579/2760] target/arm: Provide always-false kvm_arm_*_supported() stubs for usermode If you try to build aarch64-linux-user with clang and --enable-debug then it fails to compile: ld: libqemu-aarch64-linux-user.a.p/target_arm_cpu64.c.o: in function `cpu_arm_set_sve': ../../target/arm/cpu64.c:321:(.text+0x1254): undefined reference to `kvm_arm_sve_supported' This is a regression introduced in commit f86d4220, which switched the kvm-stub.c file away from being built for all arm targets to only being built for system emulation binaries. It doesn't affect gcc, presumably because even at -O0 gcc folds away the always-false kvm_enabled() condition but clang does not. We would prefer not to build kvm-stub.c once for usermode and once for system-emulation binaries, and we can't build it just once for both because it includes cpu.h. So instead provide always-false versions of the five functions that are valid to call without KVM support in kvm_arm.h. Fixes: f86d42205c2eba ("target/arm/meson: accelerator files are not needed in user mode") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3033 Signed-off-by: Peter Maydell Reviewed-by: Pierrick Bouvier Message-id: 20250714135152.1896214-1-peter.maydell@linaro.org --- target/arm/kvm_arm.h | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/target/arm/kvm_arm.h b/target/arm/kvm_arm.h index b4cad05155..6a9b6374a6 100644 --- a/target/arm/kvm_arm.h +++ b/target/arm/kvm_arm.h @@ -161,6 +161,14 @@ void kvm_arm_add_vcpu_properties(ARMCPU *cpu); */ void kvm_arm_steal_time_finalize(ARMCPU *cpu, Error **errp); +/* + * These "is some KVM subfeature enabled?" functions may be called + * when KVM support is not present, including in the user-mode + * emulators. The kvm-stub.c file is only built into the system + * emulators, so for user-mode emulation we provide "always false" + * stubs here. + */ +#ifndef CONFIG_USER_ONLY /** * kvm_arm_aarch32_supported: * @@ -197,6 +205,33 @@ bool kvm_arm_mte_supported(void); * Returns true if KVM can enable EL2 and false otherwise. */ bool kvm_arm_el2_supported(void); +#else + +static inline bool kvm_arm_aarch32_supported(void) +{ + return false; +} + +static inline bool kvm_arm_pmu_supported(void) +{ + return false; +} + +static inline bool kvm_arm_sve_supported(void) +{ + return false; +} + +static inline bool kvm_arm_mte_supported(void) +{ + return false; +} + +static inline bool kvm_arm_el2_supported(void) +{ + return false; +} +#endif /** * kvm_arm_get_max_vm_ipa_size: From e74aad9f81cc2bfee2057086610e21bd98e9c5a5 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 21 Jul 2025 10:07:53 +0100 Subject: [PATCH 2580/2760] host-utils: Drop workaround for buggy Apple Clang __builtin_subcll() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit b0438861efe ("host-utils: Avoid using __builtin_subcll on buggy versions of Apple Clang") we added a workaround for a bug in Apple Clang 14 where its __builtin_subcll() implementation was wrong. This bug was only present in Apple Clang 14, not in upstream clang, and is not present in Apple Clang versions 15 and newer. Since commit 4e035201 we have required at least Apple Clang 15, so we no longer build with the buggy versions. We can therefore drop the workaround. This is effectively a revert of b0438861efe. This should not be backported to stable branches, which may still need to support Apple Clang 14. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3030 Signed-off-by: Peter Maydell Reviewed-by: Thomas Huth Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250714145033.1908788-1-peter.maydell@linaro.org --- include/qemu/compiler.h | 13 ------------- include/qemu/host-utils.h | 2 +- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/include/qemu/compiler.h b/include/qemu/compiler.h index 65b89958d3..1c2b673c05 100644 --- a/include/qemu/compiler.h +++ b/include/qemu/compiler.h @@ -182,19 +182,6 @@ #define QEMU_DISABLE_CFI #endif -/* - * Apple clang version 14 has a bug in its __builtin_subcll(); define - * BUILTIN_SUBCLL_BROKEN for the offending versions so we can avoid it. - * When a version of Apple clang which has this bug fixed is released - * we can add an upper bound to this check. - * See https://gitlab.com/qemu-project/qemu/-/issues/1631 - * and https://gitlab.com/qemu-project/qemu/-/issues/1659 for details. - * The bug never made it into any upstream LLVM releases, only Apple ones. - */ -#if defined(__apple_build_version__) && __clang_major__ >= 14 -#define BUILTIN_SUBCLL_BROKEN -#endif - #if __has_attribute(annotate) #define QEMU_ANNOTATE(x) __attribute__((annotate(x))) #else diff --git a/include/qemu/host-utils.h b/include/qemu/host-utils.h index 4d28fa22cf..dd558589cb 100644 --- a/include/qemu/host-utils.h +++ b/include/qemu/host-utils.h @@ -677,7 +677,7 @@ static inline uint64_t uadd64_carry(uint64_t x, uint64_t y, bool *pcarry) */ static inline uint64_t usub64_borrow(uint64_t x, uint64_t y, bool *pborrow) { -#if __has_builtin(__builtin_subcll) && !defined(BUILTIN_SUBCLL_BROKEN) +#if __has_builtin(__builtin_subcll) unsigned long long b = *pborrow; x = __builtin_subcll(x, y, b, &b); *pborrow = b & 1; From 30dbcd9283988ba352181cb42c6a69ae32075363 Mon Sep 17 00:00:00 2001 From: Jackson Donaldson Date: Mon, 21 Jul 2025 10:07:53 +0100 Subject: [PATCH 2581/2760] hw/misc/max78000_aes: Comment Internal Key Storage Coverity Scan noted an unusual pattern in the MAX78000 aes device, with duplicated calls to set_decrypt. This commit adds a comment noting why the implementation is correct. Signed-off-by: Jackson Donaldson Message-id: 20250716002622.84685-1-jcksn@duck.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/misc/max78000_aes.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/hw/misc/max78000_aes.c b/hw/misc/max78000_aes.c index 0bfb2f02b5..d883ddd2b6 100644 --- a/hw/misc/max78000_aes.c +++ b/hw/misc/max78000_aes.c @@ -79,6 +79,12 @@ static void max78000_aes_do_crypto(Max78000AesState *s) keydata += 8; } + /* + * The MAX78000 AES engine stores an internal key, which it uses only + * for decryption. This results in the slighly odd looking pairs of + * set_encrypt and set_decrypt calls below; s->internal_key is + * being stored for later use in both cases. + */ AES_KEY key; if ((s->ctrl & TYPE) == 0) { AES_set_encrypt_key(keydata, keylen, &key); From 012bf8ad134ecec5d7e68101c216204107fd9741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Mon, 21 Jul 2025 10:07:54 +0100 Subject: [PATCH 2582/2760] docs: Fix Aspeed title MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit ad8e0e8a0088 removed the "======" underlining the file title which broke documentation rendering. Add it back. Fixes: ad8e0e8a0088 ("docs: add support for gb200-bmc") Cc: Ed Tanous Reported-by: Peter Maydell Signed-off-by: Cédric Le Goater Reviewed-by: Thomas Huth Reviewed-by: Ed Tanous Message-id: 20250715061904.97540-1-clg@redhat.com Signed-off-by: Peter Maydell --- docs/system/arm/aspeed.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/system/arm/aspeed.rst b/docs/system/arm/aspeed.rst index bec0a1dfa8..bf18c56347 100644 --- a/docs/system/arm/aspeed.rst +++ b/docs/system/arm/aspeed.rst @@ -1,4 +1,5 @@ Aspeed family boards (``ast2500-evb``, ``ast2600-evb``, ``ast2700-evb``, ``bletchley-bmc``, ``fuji-bmc``, ``gb200nvl-bmc``, ``fby35-bmc``, ``fp5280g2-bmc``, ``g220a-bmc``, ``palmetto-bmc``, ``qcom-dc-scm-v1-bmc``, ``qcom-firework-bmc``, ``quanta-q71l-bmc``, ``rainier-bmc``, ``romulus-bmc``, ``sonorapass-bmc``, ``supermicrox11-bmc``, ``supermicrox11spi-bmc``, ``tiogapass-bmc``, ``witherspoon-bmc``, ``yosemitev2-bmc``) +==================================================================================================================================================================================================================================================================================================================================================================================================================================== The QEMU Aspeed machines model BMCs of various OpenPOWER systems and Aspeed evaluation boards. They are based on different releases of the From 2b5a9bbbadb39750584f36e6bee3a36ebe134418 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:23 +0100 Subject: [PATCH 2583/2760] target/arm: Add BFADD, BFSUB, BFMUL (unpredicated) FEAT_SVE_B16B16 adds bfloat16 versions of the SVE floating point (unpredicated) instructions, which are encoded via sz==0b00. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-2-peter.maydell@linaro.org --- target/arm/tcg/helper.h | 3 +++ target/arm/tcg/translate-sve.c | 6 +++++- target/arm/tcg/vec_helper.c | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index 0a006d9514..d9ca5b7c56 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -728,16 +728,19 @@ DEF_HELPER_FLAGS_4(gvec_fclt0_h, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(gvec_fclt0_s, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_4(gvec_fclt0_d, TCG_CALL_NO_RWG, void, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fadd_b16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fadd_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_bfadd, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fsub_b16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fsub_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_bfsub, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_b16, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 7b575734fd..f00cccf154 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -190,6 +190,10 @@ static bool gen_gvec_fpst_zzz(DisasContext *s, gen_helper_gvec_3_ptr *fn, static bool gen_gvec_fpst_arg_zzz(DisasContext *s, gen_helper_gvec_3_ptr *fn, arg_rrr_esz *a, int data) { + /* These insns use MO_8 to encode BFloat16 */ + if (a->esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { + return false; + } return gen_gvec_fpst_zzz(s, fn, a->rd, a->rn, a->rm, data, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); } @@ -4146,7 +4150,7 @@ static bool trans_FADDA(DisasContext *s, arg_rprr_esz *a) #define DO_FP3(NAME, name) \ static gen_helper_gvec_3_ptr * const name##_fns[4] = { \ - NULL, gen_helper_gvec_##name##_h, \ + gen_helper_gvec_##name##_b16, gen_helper_gvec_##name##_h, \ gen_helper_gvec_##name##_s, gen_helper_gvec_##name##_d \ }; \ TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_arg_zzz, name##_fns[a->esz], a, 0) diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index bae6165b50..76a9ab0da3 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1467,16 +1467,19 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, \ clear_tail(d, oprsz, simd_maxsz(desc)); \ } +DO_3OP(gvec_fadd_b16, bfloat16_add, float16) DO_3OP(gvec_fadd_h, float16_add, float16) DO_3OP(gvec_fadd_s, float32_add, float32) DO_3OP(gvec_fadd_d, float64_add, float64) DO_3OP(gvec_bfadd, bfloat16_add, bfloat16) +DO_3OP(gvec_fsub_b16, bfloat16_sub, float16) DO_3OP(gvec_fsub_h, float16_sub, float16) DO_3OP(gvec_fsub_s, float32_sub, float32) DO_3OP(gvec_fsub_d, float64_sub, float64) DO_3OP(gvec_bfsub, bfloat16_sub, bfloat16) +DO_3OP(gvec_fmul_b16, bfloat16_mul, float16) DO_3OP(gvec_fmul_h, float16_mul, float16) DO_3OP(gvec_fmul_s, float32_mul, float32) DO_3OP(gvec_fmul_d, float64_mul, float64) From 86fa06f8d9f953252a7919fa56a402d789bf1b78 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:24 +0100 Subject: [PATCH 2584/2760] target/arm: Add BFADD, BFSUB, BFMUL, BFMAXNM, BFMINNM (predicated) FEAT_SVE_B16B16 adds bfloat16 versions of the SVE floating point (predicated) instructions, which are encoded via sz=0b00. Add BFADD, BFSUB, BFMUL, BFMAXNM, BFMINNM; these are all the insns in this group which do not change behaviour for AH=1. We will deal with BFMAX/BFMIN (which do have different AH=1 behaviour) in a following commit. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-3-peter.maydell@linaro.org --- target/arm/tcg/helper-sve.h | 10 ++++++++++ target/arm/tcg/sve_helper.c | 5 +++++ target/arm/tcg/translate-sve.c | 22 +++++++++++++++++----- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index c36090d13d..d612bcaded 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1196,6 +1196,8 @@ DEF_HELPER_FLAGS_5(sve_fcmne0_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(sve_fcmne0_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fadd_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadd_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fadd_s, TCG_CALL_NO_RWG, @@ -1203,6 +1205,8 @@ DEF_HELPER_FLAGS_6(sve_fadd_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fsub_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsub_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fsub_s, TCG_CALL_NO_RWG, @@ -1210,6 +1214,8 @@ DEF_HELPER_FLAGS_6(sve_fsub_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fsub_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fmul_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmul_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmul_s, TCG_CALL_NO_RWG, @@ -1252,6 +1258,8 @@ DEF_HELPER_FLAGS_6(sve_ah_fmax_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_ah_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fminnum_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fminnum_s, TCG_CALL_NO_RWG, @@ -1259,6 +1267,8 @@ DEF_HELPER_FLAGS_6(sve_fminnum_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fminnum_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fmaxnum_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnum_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmaxnum_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 43b872c7fd..a229503bc2 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4629,14 +4629,17 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, void *vg, \ } while (i != 0); \ } +DO_ZPZZ_FP(sve_fadd_b16, uint16_t, H1_2, bfloat16_add) DO_ZPZZ_FP(sve_fadd_h, uint16_t, H1_2, float16_add) DO_ZPZZ_FP(sve_fadd_s, uint32_t, H1_4, float32_add) DO_ZPZZ_FP(sve_fadd_d, uint64_t, H1_8, float64_add) +DO_ZPZZ_FP(sve_fsub_b16, uint16_t, H1_2, bfloat16_sub) DO_ZPZZ_FP(sve_fsub_h, uint16_t, H1_2, float16_sub) DO_ZPZZ_FP(sve_fsub_s, uint32_t, H1_4, float32_sub) DO_ZPZZ_FP(sve_fsub_d, uint64_t, H1_8, float64_sub) +DO_ZPZZ_FP(sve_fmul_b16, uint16_t, H1_2, bfloat16_mul) DO_ZPZZ_FP(sve_fmul_h, uint16_t, H1_2, float16_mul) DO_ZPZZ_FP(sve_fmul_s, uint32_t, H1_4, float32_mul) DO_ZPZZ_FP(sve_fmul_d, uint64_t, H1_8, float64_mul) @@ -4661,10 +4664,12 @@ DO_ZPZZ_FP(sve_ah_fmax_h, uint16_t, H1_2, helper_vfp_ah_maxh) DO_ZPZZ_FP(sve_ah_fmax_s, uint32_t, H1_4, helper_vfp_ah_maxs) DO_ZPZZ_FP(sve_ah_fmax_d, uint64_t, H1_8, helper_vfp_ah_maxd) +DO_ZPZZ_FP(sve_fminnum_b16, uint16_t, H1_2, bfloat16_minnum) DO_ZPZZ_FP(sve_fminnum_h, uint16_t, H1_2, float16_minnum) DO_ZPZZ_FP(sve_fminnum_s, uint32_t, H1_4, float32_minnum) DO_ZPZZ_FP(sve_fminnum_d, uint64_t, H1_8, float64_minnum) +DO_ZPZZ_FP(sve_fmaxnum_b16, uint16_t, H1_2, bfloat16_maxnum) DO_ZPZZ_FP(sve_fmaxnum_h, uint16_t, H1_2, float16_maxnum) DO_ZPZZ_FP(sve_fmaxnum_s, uint32_t, H1_4, float32_maxnum) DO_ZPZZ_FP(sve_fmaxnum_d, uint64_t, H1_8, float64_maxnum) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index f00cccf154..2739c226d7 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -407,6 +407,10 @@ static bool gen_gvec_fpst_zzzp(DisasContext *s, gen_helper_gvec_4_ptr *fn, static bool gen_gvec_fpst_arg_zpzz(DisasContext *s, gen_helper_gvec_4_ptr *fn, arg_rprr_esz *a) { + /* These insns use MO_8 to encode BFloat16. */ + if (a->esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { + return false; + } return gen_gvec_fpst_zzzp(s, fn, a->rd, a->rn, a->rm, a->pg, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); } @@ -4206,13 +4210,21 @@ TRANS_FEAT_NONSTREAMING(FTSMUL, aa64_sve, gen_gvec_fpst_arg_zzz, s->fpcr_ah ? name##_ah_zpzz_fns[a->esz] : \ name##_zpzz_fns[a->esz], a) -DO_ZPZZ_FP(FADD_zpzz, aa64_sve, sve_fadd) -DO_ZPZZ_FP(FSUB_zpzz, aa64_sve, sve_fsub) -DO_ZPZZ_FP(FMUL_zpzz, aa64_sve, sve_fmul) +/* Similar, but for insns where sz == 0 encodes bfloat16 */ +#define DO_ZPZZ_FP_B16(NAME, FEAT, name) \ + static gen_helper_gvec_4_ptr * const name##_zpzz_fns[4] = { \ + gen_helper_##name##_b16, gen_helper_##name##_h, \ + gen_helper_##name##_s, gen_helper_##name##_d \ + }; \ + TRANS_FEAT(NAME, FEAT, gen_gvec_fpst_arg_zpzz, name##_zpzz_fns[a->esz], a) + +DO_ZPZZ_FP_B16(FADD_zpzz, aa64_sve, sve_fadd) +DO_ZPZZ_FP_B16(FSUB_zpzz, aa64_sve, sve_fsub) +DO_ZPZZ_FP_B16(FMUL_zpzz, aa64_sve, sve_fmul) DO_ZPZZ_AH_FP(FMIN_zpzz, aa64_sve, sve_fmin, sve_ah_fmin) DO_ZPZZ_AH_FP(FMAX_zpzz, aa64_sve, sve_fmax, sve_ah_fmax) -DO_ZPZZ_FP(FMINNM_zpzz, aa64_sve, sve_fminnum) -DO_ZPZZ_FP(FMAXNM_zpzz, aa64_sve, sve_fmaxnum) +DO_ZPZZ_FP_B16(FMINNM_zpzz, aa64_sve, sve_fminnum) +DO_ZPZZ_FP_B16(FMAXNM_zpzz, aa64_sve, sve_fmaxnum) DO_ZPZZ_AH_FP(FABD, aa64_sve, sve_fabd, sve_ah_fabd) DO_ZPZZ_FP(FSCALE, aa64_sve, sve_fscalbn) DO_ZPZZ_FP(FDIV, aa64_sve, sve_fdiv) From 279438560ba8575266e9105202c6e87044d24885 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:25 +0100 Subject: [PATCH 2585/2760] target/arm: Add BFMIN, BFMAX (predicated) FEAT_SVE_B16B16 adds bfloat16 versions of the SVE floating point (predicated) instructions, which are encoded via sz=0b00. Add the BFMAX and BFMIN insns. These have separate behaviour for AH=1 and AH=0; we have already implemented the AH=1 helper for the SME2 versions of these insns. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-4-peter.maydell@linaro.org --- target/arm/tcg/helper-sve.h | 8 ++++++++ target/arm/tcg/sve_helper.c | 4 ++++ target/arm/tcg/translate-sve.c | 17 +++++++++++++++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index d612bcaded..cb6c2355e5 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1230,6 +1230,8 @@ DEF_HELPER_FLAGS_6(sve_fdiv_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fdiv_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fmin_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmin_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmin_s, TCG_CALL_NO_RWG, @@ -1237,6 +1239,8 @@ DEF_HELPER_FLAGS_6(sve_fmin_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_fmax_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_fmax_s, TCG_CALL_NO_RWG, @@ -1244,6 +1248,8 @@ DEF_HELPER_FLAGS_6(sve_fmax_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fmax_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmin_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_ah_fmin_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_ah_fmin_s, TCG_CALL_NO_RWG, @@ -1251,6 +1257,8 @@ DEF_HELPER_FLAGS_6(sve_ah_fmin_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_ah_fmin_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_6(sve_ah_fmax_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_ah_fmax_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_6(sve_ah_fmax_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index a229503bc2..1a56fa86d9 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4648,18 +4648,22 @@ DO_ZPZZ_FP(sve_fdiv_h, uint16_t, H1_2, float16_div) DO_ZPZZ_FP(sve_fdiv_s, uint32_t, H1_4, float32_div) DO_ZPZZ_FP(sve_fdiv_d, uint64_t, H1_8, float64_div) +DO_ZPZZ_FP(sve_fmin_b16, uint16_t, H1_2, bfloat16_min) DO_ZPZZ_FP(sve_fmin_h, uint16_t, H1_2, float16_min) DO_ZPZZ_FP(sve_fmin_s, uint32_t, H1_4, float32_min) DO_ZPZZ_FP(sve_fmin_d, uint64_t, H1_8, float64_min) +DO_ZPZZ_FP(sve_fmax_b16, uint16_t, H1_2, bfloat16_max) DO_ZPZZ_FP(sve_fmax_h, uint16_t, H1_2, float16_max) DO_ZPZZ_FP(sve_fmax_s, uint32_t, H1_4, float32_max) DO_ZPZZ_FP(sve_fmax_d, uint64_t, H1_8, float64_max) +DO_ZPZZ_FP(sve_ah_fmin_b16, uint16_t, H1_2, helper_sme2_ah_fmin_b16) DO_ZPZZ_FP(sve_ah_fmin_h, uint16_t, H1_2, helper_vfp_ah_minh) DO_ZPZZ_FP(sve_ah_fmin_s, uint32_t, H1_4, helper_vfp_ah_mins) DO_ZPZZ_FP(sve_ah_fmin_d, uint64_t, H1_8, helper_vfp_ah_mind) +DO_ZPZZ_FP(sve_ah_fmax_b16, uint16_t, H1_2, helper_sme2_ah_fmax_b16) DO_ZPZZ_FP(sve_ah_fmax_h, uint16_t, H1_2, helper_vfp_ah_maxh) DO_ZPZZ_FP(sve_ah_fmax_s, uint32_t, H1_4, helper_vfp_ah_maxs) DO_ZPZZ_FP(sve_ah_fmax_d, uint64_t, H1_8, helper_vfp_ah_maxd) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 2739c226d7..27af3df9a4 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4218,11 +4218,24 @@ TRANS_FEAT_NONSTREAMING(FTSMUL, aa64_sve, gen_gvec_fpst_arg_zzz, }; \ TRANS_FEAT(NAME, FEAT, gen_gvec_fpst_arg_zpzz, name##_zpzz_fns[a->esz], a) +#define DO_ZPZZ_AH_FP_B16(NAME, FEAT, name, ah_name) \ + static gen_helper_gvec_4_ptr * const name##_zpzz_fns[4] = { \ + gen_helper_##name##_b16, gen_helper_##name##_h, \ + gen_helper_##name##_s, gen_helper_##name##_d \ + }; \ + static gen_helper_gvec_4_ptr * const name##_ah_zpzz_fns[4] = { \ + gen_helper_##ah_name##_b16, gen_helper_##ah_name##_h, \ + gen_helper_##ah_name##_s, gen_helper_##ah_name##_d \ + }; \ + TRANS_FEAT(NAME, FEAT, gen_gvec_fpst_arg_zpzz, \ + s->fpcr_ah ? name##_ah_zpzz_fns[a->esz] : \ + name##_zpzz_fns[a->esz], a) + DO_ZPZZ_FP_B16(FADD_zpzz, aa64_sve, sve_fadd) DO_ZPZZ_FP_B16(FSUB_zpzz, aa64_sve, sve_fsub) DO_ZPZZ_FP_B16(FMUL_zpzz, aa64_sve, sve_fmul) -DO_ZPZZ_AH_FP(FMIN_zpzz, aa64_sve, sve_fmin, sve_ah_fmin) -DO_ZPZZ_AH_FP(FMAX_zpzz, aa64_sve, sve_fmax, sve_ah_fmax) +DO_ZPZZ_AH_FP_B16(FMIN_zpzz, aa64_sve, sve_fmin, sve_ah_fmin) +DO_ZPZZ_AH_FP_B16(FMAX_zpzz, aa64_sve, sve_fmax, sve_ah_fmax) DO_ZPZZ_FP_B16(FMINNM_zpzz, aa64_sve, sve_fminnum) DO_ZPZZ_FP_B16(FMAXNM_zpzz, aa64_sve, sve_fmaxnum) DO_ZPZZ_AH_FP(FABD, aa64_sve, sve_fabd, sve_ah_fabd) From f71c3f470f48bece6d0601171bfda63a0b746879 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:26 +0100 Subject: [PATCH 2586/2760] target/arm: Add BFMUL (indexed) FEAT_SVE_B16B16 adds a bfloat16 version of the FMUL insn in the floating-point multiply (indexed) instruction group. The encoding is slightly bespoke; in our implementation we use MO_8 to indicate bfloat16, as with the other B16B16 insns. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-5-peter.maydell@linaro.org --- target/arm/tcg/helper.h | 2 ++ target/arm/tcg/sve.decode | 1 + target/arm/tcg/translate-sve.c | 2 +- target/arm/tcg/vec_helper.c | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/target/arm/tcg/helper.h b/target/arm/tcg/helper.h index d9ca5b7c56..4da32db902 100644 --- a/target/arm/tcg/helper.h +++ b/target/arm/tcg/helper.h @@ -823,6 +823,8 @@ DEF_HELPER_FLAGS_5(gvec_ftsmul_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_5(gvec_ftsmul_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_5(gvec_fmul_idx_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_idx_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_5(gvec_fmul_idx_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index 2efd5f57e4..a76f2236f4 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1062,6 +1062,7 @@ FMLS_zzxz 01100100 11 1 ..... 000001 ..... ..... @rrxr_1 esz=3 ### SVE FP Multiply Indexed Group # SVE floating-point multiply (indexed) +FMUL_zzx 01100100 0. 1 ..... 001010 ..... ..... @rrx_3 esz=0 FMUL_zzx 01100100 0. 1 ..... 001000 ..... ..... @rrx_3 esz=1 FMUL_zzx 01100100 10 1 ..... 001000 ..... ..... @rrx_2 esz=2 FMUL_zzx 01100100 11 1 ..... 001000 ..... ..... @rrx_1 esz=3 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 27af3df9a4..918cf6e1bd 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3907,7 +3907,7 @@ TRANS_FEAT(FMLS_zzxz, aa64_sve, gen_gvec_fpst_zzzz, */ static gen_helper_gvec_3_ptr * const fmul_idx_fns[4] = { - NULL, gen_helper_gvec_fmul_idx_h, + gen_helper_gvec_fmul_idx_b16, gen_helper_gvec_fmul_idx_h, gen_helper_gvec_fmul_idx_s, gen_helper_gvec_fmul_idx_d, }; TRANS_FEAT(FMUL_zzx, aa64_sve, gen_gvec_fpst_zzz, diff --git a/target/arm/tcg/vec_helper.c b/target/arm/tcg/vec_helper.c index 76a9ab0da3..33a136b90a 100644 --- a/target/arm/tcg/vec_helper.c +++ b/target/arm/tcg/vec_helper.c @@ -1785,6 +1785,7 @@ void HELPER(NAME)(void *vd, void *vn, void *vm, \ #define nop(N, M, S) (M) +DO_FMUL_IDX(gvec_fmul_idx_b16, nop, bfloat16_mul, float16, H2) DO_FMUL_IDX(gvec_fmul_idx_h, nop, float16_mul, float16, H2) DO_FMUL_IDX(gvec_fmul_idx_s, nop, float32_mul, float32, H4) DO_FMUL_IDX(gvec_fmul_idx_d, nop, float64_mul, float64, H8) From 929bec5581966c43d083588f9be38af48150c4fe Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:27 +0100 Subject: [PATCH 2587/2760] target/arm: Add BFMLA, BFMLS (vectors) FEAT_SVE_B16B16 adds bfloat16 versions of the FMLA and FMLS insns in the "SVE floating-point multiply-accumulate writing addend" group, encoded as sz=0b00. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-6-peter.maydell@linaro.org --- target/arm/tcg/helper-sve.h | 14 +++++++ target/arm/tcg/sve_helper.c | 69 ++++++++++++++++++++++++++++++++++ target/arm/tcg/translate-sve.c | 21 ++++++++--- 3 files changed, 98 insertions(+), 6 deletions(-) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index cb6c2355e5..5e4b7fd8cf 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1541,6 +1541,8 @@ DEF_HELPER_FLAGS_6(sve_fcadd_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_6(sve_fcadd_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_s, TCG_CALL_NO_RWG, @@ -1548,6 +1550,8 @@ DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_fmla_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_s, TCG_CALL_NO_RWG, @@ -1555,6 +1559,8 @@ DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_fmls_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_s, TCG_CALL_NO_RWG, @@ -1562,6 +1568,8 @@ DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_fnmla_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_s, TCG_CALL_NO_RWG, @@ -1569,6 +1577,8 @@ DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_fnmls_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_s, TCG_CALL_NO_RWG, @@ -1576,6 +1586,8 @@ DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_ah_fmls_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_s, TCG_CALL_NO_RWG, @@ -1583,6 +1595,8 @@ DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_s, TCG_CALL_NO_RWG, DEF_HELPER_FLAGS_7(sve_ah_fnmla_zpzzz_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) +DEF_HELPER_FLAGS_7(sve_ah_fnmls_zpzzz_b16, TCG_CALL_NO_RWG, + void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_ah_fnmls_zpzzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, ptr, fpst, i32) DEF_HELPER_FLAGS_7(sve_ah_fnmls_zpzzz_s, TCG_CALL_NO_RWG, diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 1a56fa86d9..105cc5dd12 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -5099,6 +5099,75 @@ DO_ZPZ_FP(flogb_d, float64, H1_8, do_float64_logb_as_int) #undef DO_ZPZ_FP +static void do_fmla_zpzzz_b16(void *vd, void *vn, void *vm, void *va, void *vg, + float_status *status, uint32_t desc, + uint16_t neg1, uint16_t neg3, int flags) +{ + intptr_t i = simd_oprsz(desc); + uint64_t *g = vg; + + do { + uint64_t pg = g[(i - 1) >> 6]; + do { + i -= 2; + if (likely((pg >> (i & 63)) & 1)) { + float16 e1, e2, e3, r; + + e1 = *(uint16_t *)(vn + H1_2(i)) ^ neg1; + e2 = *(uint16_t *)(vm + H1_2(i)); + e3 = *(uint16_t *)(va + H1_2(i)) ^ neg3; + r = bfloat16_muladd(e1, e2, e3, flags, status); + *(uint16_t *)(vd + H1_2(i)) = r; + } + } while (i & 63); + } while (i != 0); +} + +void HELPER(sve_fmla_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0, 0, 0); +} + +void HELPER(sve_fmls_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0x8000, 0, 0); +} + +void HELPER(sve_fnmla_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0x8000, 0x8000, 0); +} + +void HELPER(sve_fnmls_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0, 0x8000, 0); +} + +void HELPER(sve_ah_fmls_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product); +} + +void HELPER(sve_ah_fnmla_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_product | float_muladd_negate_c); +} + +void HELPER(sve_ah_fnmls_zpzzz_b16)(void *vd, void *vn, void *vm, void *va, + void *vg, float_status *status, uint32_t desc) +{ + do_fmla_zpzzz_b16(vd, vn, vm, va, vg, status, desc, 0, 0, + float_muladd_negate_c); +} + static void do_fmla_zpzzz_h(void *vd, void *vn, void *vm, void *va, void *vg, float_status *status, uint32_t desc, uint16_t neg1, uint16_t neg3, int flags) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 918cf6e1bd..37ecbc2b7c 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4368,19 +4368,28 @@ TRANS_FEAT(FCADD, aa64_sve, gen_gvec_fpst_zzzp, fcadd_fns[a->esz], a->rd, a->rn, a->rm, a->pg, a->rot | (s->fpcr_ah << 1), a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) +static bool do_fmla_zpzzz(DisasContext *s, arg_rprrr_esz *a, + gen_helper_gvec_5_ptr *fn) +{ + /* These insns use MO_8 to encode BFloat16 */ + if (a->esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { + return false; + } + return gen_gvec_fpst_zzzzp(s, fn, a->rd, a->rn, a->rm, a->ra, a->pg, 0, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); +} + #define DO_FMLA(NAME, name, ah_name) \ static gen_helper_gvec_5_ptr * const name##_fns[4] = { \ - NULL, gen_helper_sve_##name##_h, \ + gen_helper_sve_##name##_b16, gen_helper_sve_##name##_h, \ gen_helper_sve_##name##_s, gen_helper_sve_##name##_d \ }; \ static gen_helper_gvec_5_ptr * const name##_ah_fns[4] = { \ - NULL, gen_helper_sve_##ah_name##_h, \ + gen_helper_sve_##ah_name##_b16, gen_helper_sve_##ah_name##_h, \ gen_helper_sve_##ah_name##_s, gen_helper_sve_##ah_name##_d \ }; \ - TRANS_FEAT(NAME, aa64_sve, gen_gvec_fpst_zzzzp, \ - s->fpcr_ah ? name##_ah_fns[a->esz] : name##_fns[a->esz], \ - a->rd, a->rn, a->rm, a->ra, a->pg, 0, \ - a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) + TRANS_FEAT(NAME, aa64_sve, do_fmla_zpzzz, a, \ + s->fpcr_ah ? name##_ah_fns[a->esz] : name##_fns[a->esz]) /* We don't need an ah_fmla_zpzzz because fmla doesn't negate anything */ DO_FMLA(FMLA_zpzzz, fmla_zpzzz, fmla_zpzzz) From 67fbc4c8079226eb9e47369cc45eb3fe56c3c9c3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:28 +0100 Subject: [PATCH 2588/2760] target/arm: Add BFMLA, BFMLS (indexed) FEAT_SVE_B16B16 adds bfloat16 versions of the FMLA and FMLS insns in the SVE floating-point multiply-add (indexed) insn group. Implement these. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-7-peter.maydell@linaro.org --- target/arm/tcg/sve.decode | 2 ++ target/arm/tcg/translate-sve.c | 25 ++++++++++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index a76f2236f4..a77b725c87 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1052,9 +1052,11 @@ FCMLA_zzxz 01100100 11 1 index:1 rm:4 0001 rot:2 rn:5 rd:5 \ ### SVE FP Multiply-Add Indexed Group # SVE floating-point multiply-add (indexed) +FMLA_zzxz 01100100 0. 1 ..... 000010 ..... ..... @rrxr_3 esz=0 FMLA_zzxz 01100100 0. 1 ..... 000000 ..... ..... @rrxr_3 esz=1 FMLA_zzxz 01100100 10 1 ..... 000000 ..... ..... @rrxr_2 esz=2 FMLA_zzxz 01100100 11 1 ..... 000000 ..... ..... @rrxr_1 esz=3 +FMLS_zzxz 01100100 0. 1 ..... 000011 ..... ..... @rrxr_3 esz=0 FMLS_zzxz 01100100 0. 1 ..... 000001 ..... ..... @rrxr_3 esz=1 FMLS_zzxz 01100100 10 1 ..... 000001 ..... ..... @rrxr_2 esz=2 FMLS_zzxz 01100100 11 1 ..... 000001 ..... ..... @rrxr_1 esz=3 diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 37ecbc2b7c..fc76624b5a 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -3883,24 +3883,31 @@ DO_SVE2_RRXR_ROT(CDOT_zzxw_d, gen_helper_sve2_cdot_idx_d) *** SVE Floating Point Multiply-Add Indexed Group */ +static bool do_fmla_zzxz(DisasContext *s, arg_rrxr_esz *a, + gen_helper_gvec_4_ptr *fn) +{ + /* These insns use MO_8 to encode BFloat16 */ + if (a->esz == MO_8 && !dc_isar_feature(aa64_sve_b16b16, s)) { + return false; + } + return gen_gvec_fpst_zzzz(s, fn, a->rd, a->rn, a->rm, a->ra, a->index, + a->esz == MO_16 ? FPST_A64_F16 : FPST_A64); +} + static gen_helper_gvec_4_ptr * const fmla_idx_fns[4] = { - NULL, gen_helper_gvec_fmla_idx_h, + gen_helper_gvec_bfmla_idx, gen_helper_gvec_fmla_idx_h, gen_helper_gvec_fmla_idx_s, gen_helper_gvec_fmla_idx_d }; -TRANS_FEAT(FMLA_zzxz, aa64_sve, gen_gvec_fpst_zzzz, - fmla_idx_fns[a->esz], a->rd, a->rn, a->rm, a->ra, a->index, - a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) +TRANS_FEAT(FMLA_zzxz, aa64_sve, do_fmla_zzxz, a, fmla_idx_fns[a->esz]) static gen_helper_gvec_4_ptr * const fmls_idx_fns[4][2] = { - { NULL, NULL }, + { gen_helper_gvec_bfmls_idx, gen_helper_gvec_ah_bfmls_idx }, { gen_helper_gvec_fmls_idx_h, gen_helper_gvec_ah_fmls_idx_h }, { gen_helper_gvec_fmls_idx_s, gen_helper_gvec_ah_fmls_idx_s }, { gen_helper_gvec_fmls_idx_d, gen_helper_gvec_ah_fmls_idx_d }, }; -TRANS_FEAT(FMLS_zzxz, aa64_sve, gen_gvec_fpst_zzzz, - fmls_idx_fns[a->esz][s->fpcr_ah], - a->rd, a->rn, a->rm, a->ra, a->index, - a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) +TRANS_FEAT(FMLS_zzxz, aa64_sve, do_fmla_zzxz, a, + fmls_idx_fns[a->esz][s->fpcr_ah]) /* *** SVE Floating Point Multiply Indexed Group From 17f6436822ff600cae4590c4b06b3321c97f1f42 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:29 +0100 Subject: [PATCH 2589/2760] target/arm: Correct sense of FPCR.AH test for FMAXQV and FMINQV MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When we implemented the FMAXQV and FMINQV insns we accidentally inverted the sense of the FPCR.AH test, so we gave the AH=1 behaviour when FPCR.AH was zero, and vice-versa. (The difference is limited to handling of negative zero and NaN inputs.) Fixes: 1de7ecfc12d05 ("target/arm: Implement FADDQV, F{MIN, MAX}{NM}QV for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250718173032.2498900-8-peter.maydell@linaro.org --- target/arm/tcg/translate-sve.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index fc76624b5a..2ed440aff1 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4020,7 +4020,7 @@ static gen_helper_gvec_3_ptr * const fmaxqv_ah_fns[4] = { gen_helper_sve2p1_ah_fmaxqv_s, gen_helper_sve2p1_ah_fmaxqv_d, }; TRANS_FEAT(FMAXQV, aa64_sme2p1_or_sve2p1, gen_gvec_fpst_arg_zpz, - (s->fpcr_ah ? fmaxqv_fns : fmaxqv_ah_fns)[a->esz], a, 0, + (s->fpcr_ah ? fmaxqv_ah_fns : fmaxqv_fns)[a->esz], a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) static gen_helper_gvec_3_ptr * const fminqv_fns[4] = { @@ -4032,7 +4032,7 @@ static gen_helper_gvec_3_ptr * const fminqv_ah_fns[4] = { gen_helper_sve2p1_ah_fminqv_s, gen_helper_sve2p1_ah_fminqv_d, }; TRANS_FEAT(FMINQV, aa64_sme2p1_or_sve2p1, gen_gvec_fpst_arg_zpz, - (s->fpcr_ah ? fminqv_fns : fminqv_ah_fns)[a->esz], a, 0, + (s->fpcr_ah ? fminqv_ah_fns : fminqv_fns)[a->esz], a, 0, a->esz == MO_16 ? FPST_A64_F16 : FPST_A64) /* From 07327d5f451162a841747836ff05cc6dd6e8c023 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:30 +0100 Subject: [PATCH 2590/2760] target/arm: Don't nest H() macro calls in SVE DO_REDUCE In the part of the SVE DO_REDUCE macro used by the SVE2p1 FMAXQV, FMINQV, etc insns, we incorrectly applied the H() macro twice when calculating an offset to add to the vn pointer. This has no effect on little-endian hosts but on big-endian hosts the two invocations will cancel each other out and we will access the wrong part of the array. The "s * 16" part of the expression is already aligned, so we only need to use the H macro on the "e". Correct the macro usage. Fixes: 1de7ecfc12d05 ("target/arm: Implement FADDQV, F{MIN, MAX}{NM}QV for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-9-peter.maydell@linaro.org --- target/arm/tcg/sve_helper.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 105cc5dd12..bf894f0bf1 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4509,7 +4509,7 @@ void helper_sve2p1_##NAME##qv_##SUF(void *vd, void *vn, void *vg, \ TYPE data[ARM_MAX_VQ]; \ for (unsigned s = 0; s < segments; s++) { \ uint16_t pg = *(uint16_t *)(vg + H1_2(s * 2)); \ - TYPE nn = *(TYPE *)(vn + H(s * 16 + H(e))); \ + TYPE nn = *(TYPE *)(vn + (s * 16 + H(e))); \ data[s] = (pg >> e) & 1 ? nn : IDENT; \ } \ *(TYPE *)(vd + H(e)) = FUNC##_reduce(data, status, segments); \ From 82a1c5c661ef9ab567b7946b75240963c153a3b0 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:31 +0100 Subject: [PATCH 2591/2760] target/arm: Honour FPCR.AH=1 default NaN value in FMAXNMQV, FMINNMQV The FMAXNMQV and FMINNMQV insns use the default NaN as their identity value for inactive source vector elements. We open-coded this in sve_helper.c, hoping to avoid a function call. However, this fails to account for FPCR.AH=1 changing the default NaN value to set the sign bit. Use a call to floatN_default_nan() to obtain this value. Fixes: 1de7ecfc12d05 ("target/arm: Implement FADDQV, F{MIN, MAX}{NM}QV for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-10-peter.maydell@linaro.org --- target/arm/tcg/sve_helper.c | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index bf894f0bf1..803f0a094d 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -4484,33 +4484,35 @@ static TYPE FUNC##_reduce(TYPE *data, float_status *status, uintptr_t n) \ } \ } \ uint64_t helper_sve_##NAME##v_##SUF(void *vn, void *vg, \ - float_status *s, uint32_t desc) \ + float_status *status, uint32_t desc) \ { \ uintptr_t i, oprsz = simd_oprsz(desc), maxsz = simd_data(desc); \ TYPE data[sizeof(ARMVectorReg) / sizeof(TYPE)]; \ + TYPE ident = IDENT; \ for (i = 0; i < oprsz; ) { \ uint16_t pg = *(uint16_t *)(vg + H1_2(i >> 3)); \ do { \ TYPE nn = *(TYPE *)(vn + H(i)); \ - *(TYPE *)((void *)data + i) = (pg & 1 ? nn : IDENT); \ + *(TYPE *)((void *)data + i) = (pg & 1 ? nn : ident); \ i += sizeof(TYPE), pg >>= sizeof(TYPE); \ } while (i & 15); \ } \ for (; i < maxsz; i += sizeof(TYPE)) { \ - *(TYPE *)((void *)data + i) = IDENT; \ + *(TYPE *)((void *)data + i) = ident; \ } \ - return FUNC##_reduce(data, s, maxsz / sizeof(TYPE)); \ + return FUNC##_reduce(data, status, maxsz / sizeof(TYPE)); \ } \ void helper_sve2p1_##NAME##qv_##SUF(void *vd, void *vn, void *vg, \ float_status *status, uint32_t desc) \ { \ unsigned oprsz = simd_oprsz(desc), segments = oprsz / 16; \ + TYPE ident = IDENT; \ for (unsigned e = 0; e < 16; e += sizeof(TYPE)) { \ TYPE data[ARM_MAX_VQ]; \ for (unsigned s = 0; s < segments; s++) { \ uint16_t pg = *(uint16_t *)(vg + H1_2(s * 2)); \ TYPE nn = *(TYPE *)(vn + (s * 16 + H(e))); \ - data[s] = (pg >> e) & 1 ? nn : IDENT; \ + data[s] = (pg >> e) & 1 ? nn : ident; \ } \ *(TYPE *)(vd + H(e)) = FUNC##_reduce(data, status, segments); \ } \ @@ -4521,14 +4523,17 @@ DO_REDUCE(fadd,h, float16, H1_2, float16_add, float16_zero) DO_REDUCE(fadd,s, float32, H1_4, float32_add, float32_zero) DO_REDUCE(fadd,d, float64, H1_8, float64_add, float64_zero) -/* Identity is floatN_default_nan, without the function call. */ -DO_REDUCE(fminnm,h, float16, H1_2, float16_minnum, 0x7E00) -DO_REDUCE(fminnm,s, float32, H1_4, float32_minnum, 0x7FC00000) -DO_REDUCE(fminnm,d, float64, H1_8, float64_minnum, 0x7FF8000000000000ULL) +/* + * We can't avoid the function call for the default NaN value, because + * it changes when FPCR.AH is set. + */ +DO_REDUCE(fminnm,h, float16, H1_2, float16_minnum, float16_default_nan(status)) +DO_REDUCE(fminnm,s, float32, H1_4, float32_minnum, float32_default_nan(status)) +DO_REDUCE(fminnm,d, float64, H1_8, float64_minnum, float64_default_nan(status)) -DO_REDUCE(fmaxnm,h, float16, H1_2, float16_maxnum, 0x7E00) -DO_REDUCE(fmaxnm,s, float32, H1_4, float32_maxnum, 0x7FC00000) -DO_REDUCE(fmaxnm,d, float64, H1_8, float64_maxnum, 0x7FF8000000000000ULL) +DO_REDUCE(fmaxnm,h, float16, H1_2, float16_maxnum, float16_default_nan(status)) +DO_REDUCE(fmaxnm,s, float32, H1_4, float32_maxnum, float32_default_nan(status)) +DO_REDUCE(fmaxnm,d, float64, H1_8, float64_maxnum, float64_default_nan(status)) DO_REDUCE(fmin,h, float16, H1_2, float16_min, float16_infinity) DO_REDUCE(fmin,s, float32, H1_4, float32_min, float32_infinity) From 082933a1f7d3c8e4a9e999c3d284928ef866c67d Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 18 Jul 2025 18:30:32 +0100 Subject: [PATCH 2592/2760] target/arm: Make LD1Q decode and trans fn agree about a->u For the LD1Q instruction (gather load of quadwords) we use the LD1_zprz pattern with MO_128 elements. At this element size there is no signed vs unsigned distinction, and we only set the 'u' bit in the arg_LD1_zprz struct because we share the code and decode struct with smaller element sizes. However, we set u=0 in the decode pattern line but then accidentally asserted that it was 1 in the trans function. Since our usual convention is that the "default" is unsigned and we only mark operations as signed when they really do need to extend, change the decode pattern line to set u=1 to match the assert. Fixes: d2aa9a804ee6 ("target/arm: Implement LD1Q, ST1Q for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250718173032.2498900-11-peter.maydell@linaro.org --- target/arm/tcg/sve.decode | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index a77b725c87..aea7f51973 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1345,7 +1345,7 @@ LD1_zprz 1100010 11 1. ..... 11. ... ..... ..... \ # LD1Q LD1_zprz 1100 0100 000 rm:5 101 pg:3 rn:5 rd:5 \ - &rprr_gather_load u=0 ff=0 xs=2 esz=4 msz=4 scale=0 + &rprr_gather_load u=1 ff=0 xs=2 esz=4 msz=4 scale=0 # SVE 64-bit gather load (vector plus immediate) LD1_zpiz 1100010 .. 01 ..... 1.. ... ..... ..... \ From f19310b23a00b5c19f930e4d57fc298744d11740 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Tue, 15 Jul 2025 00:01:38 +0800 Subject: [PATCH 2593/2760] hvf: arm: Add permission check in GIC sysreg handlers Quoting Peter Maydell: " hvf_sysreg_read_cp() and hvf_sysreg_write_cp() do not check the .access field of the ARMCPRegInfo to ensure that they forbid writes to registers that are marked with a .access field that says they're read-only (and ditto reads to write-only registers). " Before we add more registers in GIC sysreg handlers, let's get it correct by adding the .access checks to hvf_sysreg_read_cp() and hvf_sysreg_write_cp(). With that, a sysreg access with invalid permission will result in an UNDEFINED exception. Suggested-by: Peter Maydell Signed-off-by: Zenghui Yu Message-id: 20250714160139.10404-2-zenghui.yu@linux.dev Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 8f93e42b34..861657df96 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1263,6 +1263,9 @@ static bool hvf_sysreg_read_cp(CPUState *cpu, uint32_t reg, uint64_t *val) ri = get_arm_cp_reginfo(arm_cpu->cp_regs, hvf_reg2cp_reg(reg)); if (ri) { + if (!cp_access_ok(1, ri, true)) { + return false; + } if (ri->accessfn) { if (ri->accessfn(env, ri, true) != CP_ACCESS_OK) { return false; @@ -1543,6 +1546,9 @@ static bool hvf_sysreg_write_cp(CPUState *cpu, uint32_t reg, uint64_t val) ri = get_arm_cp_reginfo(arm_cpu->cp_regs, hvf_reg2cp_reg(reg)); if (ri) { + if (!cp_access_ok(1, ri, false)) { + return false; + } if (ri->accessfn) { if (ri->accessfn(env, ri, false) != CP_ACCESS_OK) { return false; From e6da704b711d5d731e4d933ad56cbbc25ee0a825 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Tue, 15 Jul 2025 00:01:39 +0800 Subject: [PATCH 2594/2760] hvf: arm: Emulate ICC_RPR_EL1 accesses properly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit a2260983c655 ("hvf: arm: Add support for GICv3") added GICv3 support by implementing emulation for a few system registers. ICC_RPR_EL1 was defined but not plugged in the sysreg handlers (for no good reason). Fix it. Fixes: a2260983c655 ("hvf: arm: Add support for GICv3") Signed-off-by: Zenghui Yu Reviewed-by: Philippe Mathieu-Daudé Message-id: 20250714160139.10404-3-zenghui.yu@linux.dev Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index 861657df96..bd6b5d11de 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -1361,6 +1361,7 @@ static int hvf_sysreg_read(CPUState *cpu, uint32_t reg, uint64_t *val) case SYSREG_ICC_IGRPEN0_EL1: case SYSREG_ICC_IGRPEN1_EL1: case SYSREG_ICC_PMR_EL1: + case SYSREG_ICC_RPR_EL1: case SYSREG_ICC_SGI0R_EL1: case SYSREG_ICC_SGI1R_EL1: case SYSREG_ICC_SRE_EL1: @@ -1678,6 +1679,7 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_ICC_IGRPEN0_EL1: case SYSREG_ICC_IGRPEN1_EL1: case SYSREG_ICC_PMR_EL1: + case SYSREG_ICC_RPR_EL1: case SYSREG_ICC_SGI0R_EL1: case SYSREG_ICC_SGI1R_EL1: case SYSREG_ICC_SRE_EL1: From 6f7f3419cce86553dd239f10a5deb9ab872bd8c2 Mon Sep 17 00:00:00 2001 From: Henry Kleynhans Date: Wed, 22 Dec 2021 15:05:59 +0000 Subject: [PATCH 2595/2760] crypto: load all certificates in X509 CA file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some CA files may contain multiple intermediaries and roots of trust. These may not fit into the hard-coded limit of 16. Extend the validation code to allocate enough space to load all of the certificates present in the CA file and ensure they are cleaned up. Reviewed-by: Daniel P. Berrangé Signed-off-by: Henry Kleynhans [DB: drop MAX_CERTS constant & whitespace tweaks] Signed-off-by: Daniel P. Berrangé --- crypto/tlscredsx509.c | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/crypto/tlscredsx509.c b/crypto/tlscredsx509.c index 63a72fe47c..cd1f504471 100644 --- a/crypto/tlscredsx509.c +++ b/crypto/tlscredsx509.c @@ -426,9 +426,8 @@ qcrypto_tls_creds_load_cert(QCryptoTLSCredsX509 *creds, static int qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds, const char *certFile, - gnutls_x509_crt_t *certs, - unsigned int certMax, - size_t *ncerts, + gnutls_x509_crt_t **certs, + unsigned int *ncerts, Error **errp) { gnutls_datum_t data; @@ -449,20 +448,18 @@ qcrypto_tls_creds_load_ca_cert_list(QCryptoTLSCredsX509 *creds, data.data = (unsigned char *)buf; data.size = strlen(buf); - if (gnutls_x509_crt_list_import(certs, &certMax, &data, - GNUTLS_X509_FMT_PEM, 0) < 0) { + if (gnutls_x509_crt_list_import2(certs, ncerts, &data, + GNUTLS_X509_FMT_PEM, 0) < 0) { error_setg(errp, "Unable to import CA certificate list %s", certFile); return -1; } - *ncerts = certMax; return 0; } -#define MAX_CERTS 16 static int qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, bool isServer, @@ -471,12 +468,11 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, Error **errp) { gnutls_x509_crt_t cert = NULL; - gnutls_x509_crt_t cacerts[MAX_CERTS]; - size_t ncacerts = 0; + gnutls_x509_crt_t *cacerts = NULL; + unsigned int ncacerts = 0; size_t i; int ret = -1; - memset(cacerts, 0, sizeof(cacerts)); if (certFile && access(certFile, R_OK) == 0) { cert = qcrypto_tls_creds_load_cert(creds, @@ -488,8 +484,9 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, } if (access(cacertFile, R_OK) == 0) { if (qcrypto_tls_creds_load_ca_cert_list(creds, - cacertFile, cacerts, - MAX_CERTS, &ncacerts, + cacertFile, + &cacerts, + &ncacerts, errp) < 0) { goto cleanup; } @@ -526,6 +523,8 @@ qcrypto_tls_creds_x509_sanity_check(QCryptoTLSCredsX509 *creds, for (i = 0; i < ncacerts; i++) { gnutls_x509_crt_deinit(cacerts[i]); } + g_free(cacerts); + return ret; } From 7724ca9a772594b96939ff549c74f46e11d7870b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 16 Jul 2025 19:28:11 +0200 Subject: [PATCH 2596/2760] accel/hvf: Display executable bit as 'X' MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Developers are accustomed to read RWX, not RWE. Replace E -> X. Reported-by: Alex Bennée Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Alex Bennée Reviewed-by: Mads Ynddal Reviewed-by: Xiaoyao Li Signed-off-by: Peter Maydell --- accel/hvf/hvf-all.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/accel/hvf/hvf-all.c b/accel/hvf/hvf-all.c index e67a8105a6..0a4b498e83 100644 --- a/accel/hvf/hvf-all.c +++ b/accel/hvf/hvf-all.c @@ -84,7 +84,7 @@ static int do_hvf_set_memory(hvf_slot *slot, hv_memory_flags_t flags) trace_hvf_vm_map(slot->start, slot->size, slot->mem, flags, flags & HV_MEMORY_READ ? 'R' : '-', flags & HV_MEMORY_WRITE ? 'W' : '-', - flags & HV_MEMORY_EXEC ? 'E' : '-'); + flags & HV_MEMORY_EXEC ? 'X' : '-'); ret = hv_vm_map(slot->mem, slot->start, slot->size, flags); assert_hvf_ok(ret); return 0; From 2c047bdb77e8f636936edd9ac5000521c9580477 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Fri, 18 Jul 2025 17:34:06 +0000 Subject: [PATCH 2597/2760] tcg/optimize: Don't fold INDEX_op_and_vec to extract There is no such thing as vector extract. Fixes: 932522a9ddc1 ("tcg/optimize: Fold and to extract during optimize") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3036 Signed-off-by: Richard Henderson Reviewed-by: Pierrick Bouvier Tested-by: Pierrick Bouvier Reviewed-by: Peter Maydell --- tcg/optimize.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tcg/optimize.c b/tcg/optimize.c index 62a128bc9b..3638ab9fea 100644 --- a/tcg/optimize.c +++ b/tcg/optimize.c @@ -1454,7 +1454,7 @@ static bool fold_and(OptContext *ctx, TCGOp *op) a_mask = t1->z_mask & ~t2->o_mask; if (!fold_masks_zosa_int(ctx, op, z_mask, o_mask, s_mask, a_mask)) { - if (ti_is_const(t2)) { + if (op->opc == INDEX_op_and && ti_is_const(t2)) { /* * Canonicalize on extract, if valid. This aids x86 with its * 2 operand MOVZBL and 2 operand AND, selecting the TCGOpcode From 0d70c5aa1bbfb0f5099d53d6e084337a8246cc0c Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Mon, 21 Jul 2025 12:51:31 +0200 Subject: [PATCH 2598/2760] rust: devices are not staticlibs This is only cosmetic for now, but hopefully later on Meson will parse more of Cargo.toml. Devices are linked into a staticlib but are not staticlibs themselves. Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/Cargo.toml | 3 --- rust/hw/timer/hpet/Cargo.toml | 3 --- 2 files changed, 6 deletions(-) diff --git a/rust/hw/char/pl011/Cargo.toml b/rust/hw/char/pl011/Cargo.toml index 003ef9613d..88ef110507 100644 --- a/rust/hw/char/pl011/Cargo.toml +++ b/rust/hw/char/pl011/Cargo.toml @@ -12,9 +12,6 @@ license.workspace = true repository.workspace = true rust-version.workspace = true -[lib] -crate-type = ["staticlib"] - [dependencies] bilge = { version = "0.2.0" } bilge-impl = { version = "0.2.0" } diff --git a/rust/hw/timer/hpet/Cargo.toml b/rust/hw/timer/hpet/Cargo.toml index 6f07502784..ac5df23c1d 100644 --- a/rust/hw/timer/hpet/Cargo.toml +++ b/rust/hw/timer/hpet/Cargo.toml @@ -10,9 +10,6 @@ license.workspace = true repository.workspace = true rust-version.workspace = true -[lib] -crate-type = ["staticlib"] - [dependencies] qemu_api = { path = "../../../qemu-api" } qemu_api_macros = { path = "../../../qemu-api-macros" } From f63000d943bd7db60b338f12546a96efaa6058aa Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Fri, 18 Jul 2025 10:00:52 +0300 Subject: [PATCH 2599/2760] rust/pl011: merge device_class.rs into device.rs The split was a relic of early development and is not necessary. Signed-off-by: Manos Pitsidianakis Link: https://lore.kernel.org/r/20250718-rust-pl011-cleanup-v1-1-c71b1d6a69a5@linaro.org Signed-off-by: Paolo Bonzini --- rust/hw/char/pl011/src/device.rs | 103 +++++++++++++++++++++++-- rust/hw/char/pl011/src/device_class.rs | 103 ------------------------- rust/hw/char/pl011/src/lib.rs | 1 - 3 files changed, 96 insertions(+), 111 deletions(-) delete mode 100644 rust/hw/char/pl011/src/device_class.rs diff --git a/rust/hw/char/pl011/src/device.rs b/rust/hw/char/pl011/src/device.rs index 5b53f2649f..ceb71dd99b 100644 --- a/rust/hw/char/pl011/src/device.rs +++ b/rust/hw/char/pl011/src/device.rs @@ -2,9 +2,14 @@ // Author(s): Manos Pitsidianakis // SPDX-License-Identifier: GPL-2.0-or-later -use std::{ffi::CStr, mem::size_of}; +use std::{ + ffi::{c_int, c_void, CStr}, + mem::size_of, + ptr::NonNull, +}; use qemu_api::{ + bindings::{qdev_prop_bool, qdev_prop_chr}, chardev::{CharBackend, Chardev, Event}, impl_vmstate_forward, irq::{IRQState, InterruptSource}, @@ -18,12 +23,11 @@ use qemu_api::{ sysbus::{SysBusDevice, SysBusDeviceImpl}, uninit_field_mut, vmstate::VMStateDescription, + vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused, + zeroable::Zeroable, }; -use crate::{ - device_class, - registers::{self, Interrupt, RegisterOffset}, -}; +use crate::registers::{self, Interrupt, RegisterOffset}; // TODO: You must disable the UART before any of the control registers are // reprogrammed. When the UART is disabled in the middle of transmission or @@ -173,10 +177,10 @@ impl ObjectImpl for PL011State { impl DeviceImpl for PL011State { fn properties() -> &'static [Property] { - &device_class::PL011_PROPERTIES + &PL011_PROPERTIES } fn vmsd() -> Option<&'static VMStateDescription> { - Some(&device_class::VMSTATE_PL011) + Some(&VMSTATE_PL011) } const REALIZE: Option qemu_api::Result<()>> = Some(Self::realize); } @@ -712,3 +716,88 @@ impl PL011Impl for PL011Luminary { impl DeviceImpl for PL011Luminary {} impl ResettablePhasesImpl for PL011Luminary {} impl SysBusDeviceImpl for PL011Luminary {} + +extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { + let state = NonNull::new(opaque).unwrap().cast::(); + unsafe { state.as_ref().migrate_clock } +} + +/// Migration subsection for [`PL011State`] clock. +static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { + name: c"pl011/clock".as_ptr(), + version_id: 1, + minimum_version_id: 1, + needed: Some(pl011_clock_needed), + fields: vmstate_fields! { + vmstate_clock!(PL011State, clock), + }, + ..Zeroable::ZERO +}; + +extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { + let state = NonNull::new(opaque).unwrap().cast::(); + let result = unsafe { state.as_ref().post_load(version_id as u32) }; + if result.is_err() { + -1 + } else { + 0 + } +} + +static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription { + name: c"pl011/regs".as_ptr(), + version_id: 2, + minimum_version_id: 2, + fields: vmstate_fields! { + vmstate_of!(PL011Registers, flags), + vmstate_of!(PL011Registers, line_control), + vmstate_of!(PL011Registers, receive_status_error_clear), + vmstate_of!(PL011Registers, control), + vmstate_of!(PL011Registers, dmacr), + vmstate_of!(PL011Registers, int_enabled), + vmstate_of!(PL011Registers, int_level), + vmstate_of!(PL011Registers, read_fifo), + vmstate_of!(PL011Registers, ilpr), + vmstate_of!(PL011Registers, ibrd), + vmstate_of!(PL011Registers, fbrd), + vmstate_of!(PL011Registers, ifl), + vmstate_of!(PL011Registers, read_pos), + vmstate_of!(PL011Registers, read_count), + vmstate_of!(PL011Registers, read_trigger), + }, + ..Zeroable::ZERO +}; + +pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { + name: c"pl011".as_ptr(), + version_id: 2, + minimum_version_id: 2, + post_load: Some(pl011_post_load), + fields: vmstate_fields! { + vmstate_unused!(core::mem::size_of::()), + vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell), + }, + subsections: vmstate_subsections! { + VMSTATE_PL011_CLOCK + }, + ..Zeroable::ZERO +}; + +qemu_api::declare_properties! { + PL011_PROPERTIES, + qemu_api::define_property!( + c"chardev", + PL011State, + char_backend, + unsafe { &qdev_prop_chr }, + CharBackend + ), + qemu_api::define_property!( + c"migrate-clk", + PL011State, + migrate_clock, + unsafe { &qdev_prop_bool }, + bool, + default = true + ), +} diff --git a/rust/hw/char/pl011/src/device_class.rs b/rust/hw/char/pl011/src/device_class.rs deleted file mode 100644 index d328d84632..0000000000 --- a/rust/hw/char/pl011/src/device_class.rs +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2024, Linaro Limited -// Author(s): Manos Pitsidianakis -// SPDX-License-Identifier: GPL-2.0-or-later - -use std::{ - ffi::{c_int, c_void}, - ptr::NonNull, -}; - -use qemu_api::{ - bindings::{qdev_prop_bool, qdev_prop_chr}, - prelude::*, - vmstate::VMStateDescription, - vmstate_clock, vmstate_fields, vmstate_of, vmstate_struct, vmstate_subsections, vmstate_unused, - zeroable::Zeroable, -}; - -use crate::device::{PL011Registers, PL011State}; - -extern "C" fn pl011_clock_needed(opaque: *mut c_void) -> bool { - let state = NonNull::new(opaque).unwrap().cast::(); - unsafe { state.as_ref().migrate_clock } -} - -/// Migration subsection for [`PL011State`] clock. -static VMSTATE_PL011_CLOCK: VMStateDescription = VMStateDescription { - name: c"pl011/clock".as_ptr(), - version_id: 1, - minimum_version_id: 1, - needed: Some(pl011_clock_needed), - fields: vmstate_fields! { - vmstate_clock!(PL011State, clock), - }, - ..Zeroable::ZERO -}; - -extern "C" fn pl011_post_load(opaque: *mut c_void, version_id: c_int) -> c_int { - let state = NonNull::new(opaque).unwrap().cast::(); - let result = unsafe { state.as_ref().post_load(version_id as u32) }; - if result.is_err() { - -1 - } else { - 0 - } -} - -static VMSTATE_PL011_REGS: VMStateDescription = VMStateDescription { - name: c"pl011/regs".as_ptr(), - version_id: 2, - minimum_version_id: 2, - fields: vmstate_fields! { - vmstate_of!(PL011Registers, flags), - vmstate_of!(PL011Registers, line_control), - vmstate_of!(PL011Registers, receive_status_error_clear), - vmstate_of!(PL011Registers, control), - vmstate_of!(PL011Registers, dmacr), - vmstate_of!(PL011Registers, int_enabled), - vmstate_of!(PL011Registers, int_level), - vmstate_of!(PL011Registers, read_fifo), - vmstate_of!(PL011Registers, ilpr), - vmstate_of!(PL011Registers, ibrd), - vmstate_of!(PL011Registers, fbrd), - vmstate_of!(PL011Registers, ifl), - vmstate_of!(PL011Registers, read_pos), - vmstate_of!(PL011Registers, read_count), - vmstate_of!(PL011Registers, read_trigger), - }, - ..Zeroable::ZERO -}; - -pub static VMSTATE_PL011: VMStateDescription = VMStateDescription { - name: c"pl011".as_ptr(), - version_id: 2, - minimum_version_id: 2, - post_load: Some(pl011_post_load), - fields: vmstate_fields! { - vmstate_unused!(core::mem::size_of::()), - vmstate_struct!(PL011State, regs, &VMSTATE_PL011_REGS, BqlRefCell), - }, - subsections: vmstate_subsections! { - VMSTATE_PL011_CLOCK - }, - ..Zeroable::ZERO -}; - -qemu_api::declare_properties! { - PL011_PROPERTIES, - qemu_api::define_property!( - c"chardev", - PL011State, - char_backend, - unsafe { &qdev_prop_chr }, - CharBackend - ), - qemu_api::define_property!( - c"migrate-clk", - PL011State, - migrate_clock, - unsafe { &qdev_prop_bool }, - bool, - default = true - ), -} diff --git a/rust/hw/char/pl011/src/lib.rs b/rust/hw/char/pl011/src/lib.rs index 5c4fbc9d14..2b70d2ff56 100644 --- a/rust/hw/char/pl011/src/lib.rs +++ b/rust/hw/char/pl011/src/lib.rs @@ -13,7 +13,6 @@ //! the [`registers`] module for register types. mod device; -mod device_class; mod registers; pub use device::pl011_create; From 2b1791323e7ce043cbc3857699e5d5b0ad021cbc Mon Sep 17 00:00:00 2001 From: Tanish Desai Date: Tue, 22 Jul 2025 11:43:52 +0000 Subject: [PATCH 2600/2760] tracetool: removed the unused vcpu property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The vcpu property is no longer used in these backends. Removing it avoids unnecessary checks and simplifies the code generation for these trace backends. Reviewed-by: Stefan Hajnoczi Reviewed-by: Alex Bennée Signed-off-by: Tanish Desai Message-id: 20250722114352.3624-1-tanishdesai37@gmail.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/__init__.py | 2 +- scripts/tracetool/backend/log.py | 6 +----- scripts/tracetool/backend/simple.py | 6 +----- scripts/tracetool/backend/syslog.py | 6 +----- 4 files changed, 4 insertions(+), 16 deletions(-) diff --git a/scripts/tracetool/__init__.py b/scripts/tracetool/__init__.py index 6dfcbf71e1..2ae2e562d6 100644 --- a/scripts/tracetool/__init__.py +++ b/scripts/tracetool/__init__.py @@ -219,7 +219,7 @@ class Event(object): r"(?:(?:(?P\".+),)?\s*(?P\".+))?" r"\s*") - _VALID_PROPS = set(["disable", "vcpu"]) + _VALID_PROPS = set(["disable"]) def __init__(self, name, props, fmt, args, lineno, filename, orig=None, event_trans=None, event_exec=None): diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index 17ba1cd90e..5c9d09dd11 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -29,11 +29,7 @@ def generate_h(event, group): if len(event.args) > 0: argnames = ", " + argnames - if "vcpu" in event.properties: - # already checked on the generic format code - cond = "true" - else: - cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) + cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) out(' if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {', ' if (message_with_timestamp) {', diff --git a/scripts/tracetool/backend/simple.py b/scripts/tracetool/backend/simple.py index 2688d4b64b..7c84c06b20 100644 --- a/scripts/tracetool/backend/simple.py +++ b/scripts/tracetool/backend/simple.py @@ -37,11 +37,7 @@ def generate_h_begin(events, group): def generate_h(event, group): event_id = 'TRACE_' + event.name.upper() - if "vcpu" in event.properties: - # already checked on the generic format code - cond = "true" - else: - cond = "trace_event_get_state(%s)" % event_id + cond = "trace_event_get_state(%s)" % event_id out(' if (%(cond)s) {', ' _simple_%(api)s(%(args)s);', ' }', diff --git a/scripts/tracetool/backend/syslog.py b/scripts/tracetool/backend/syslog.py index 5a3a00fe31..3f82e54aab 100644 --- a/scripts/tracetool/backend/syslog.py +++ b/scripts/tracetool/backend/syslog.py @@ -28,11 +28,7 @@ def generate_h(event, group): if len(event.args) > 0: argnames = ", " + argnames - if "vcpu" in event.properties: - # already checked on the generic format code - cond = "true" - else: - cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) + cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) out(' if (%(cond)s) {', '#line %(event_lineno)d "%(event_filename)s"', From 9e601684dc24a521bb1d23215a63e5c6e79ea0bb Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 22 Jul 2025 15:48:48 -0400 Subject: [PATCH 2601/2760] Update version for the v10.1.0-rc0 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 54e6ccf854..c52f1cc9fe 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.50 +10.0.90 From 2251f9ac9261cda05b6b19e9ba329b15d9d89bae Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 16 Jul 2025 15:26:46 -0300 Subject: [PATCH 2602/2760] migration: HMP: Fix possible out-of-bounds access MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Coverity has caught a bug in the formatting of time intervals for postcopy latency distribution display in 'info migrate'. While bounds checking the labels array, sizeof is incorrectly being used. ARRAY_SIZE is the correct form of obtaining the size of an array. Fixes: 3345fb3b6d ("migration/postcopy: Add latency distribution report for blocktime") Resolves: Coverity CID 1612248 Suggested-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250716182648.30202-2-farosas@suse.de Signed-off-by: Fabiano Rosas --- migration/migration-hmp-cmds.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index cef5608210..bb954881d7 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -57,11 +57,9 @@ static const gchar *format_time_str(uint64_t us) const char *units[] = {"us", "ms", "sec"}; int index = 0; - while (us > 1000) { + while (us > 1000 && index + 1 < ARRAY_SIZE(units)) { us /= 1000; - if (++index >= (sizeof(units) - 1)) { - break; - } + index++; } return g_strdup_printf("%"PRIu64" %s", us, units[index]); From fd1514cbd97bcad5b3dc5d002cab6fee4d7cd45e Mon Sep 17 00:00:00 2001 From: Fabiano Rosas Date: Wed, 16 Jul 2025 15:26:47 -0300 Subject: [PATCH 2603/2760] migration: HMP: Fix postcopy latency distribution label MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix the loop condition to avoid having a label with "1000 us" instead of "1 ms". Reported-by: Prasad Pandit Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250716182648.30202-3-farosas@suse.de Signed-off-by: Fabiano Rosas --- migration/migration-hmp-cmds.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index bb954881d7..a8b879c9d6 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -57,7 +57,7 @@ static const gchar *format_time_str(uint64_t us) const char *units[] = {"us", "ms", "sec"}; int index = 0; - while (us > 1000 && index + 1 < ARRAY_SIZE(units)) { + while (us >= 1000 && index + 1 < ARRAY_SIZE(units)) { us /= 1000; index++; } From eaec556bc88cc1196f7bbf23d5de311aac1d812f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 21 Jul 2025 14:39:13 +0100 Subject: [PATCH 2604/2760] migration: show error message when postcopy fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'info migrate' command only shows the error message when the migration state is 'failed'. When postcopy is used, however, the 'postcopy-paused' state is used instead of 'failed', so we must show the error message there too. Signed-off-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250721133913.2914669-1-berrange@redhat.com [line break to satisfy checkpatch] Signed-off-by: Fabiano Rosas --- migration/migration-hmp-cmds.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/migration/migration-hmp-cmds.c b/migration/migration-hmp-cmds.c index a8b879c9d6..0fc21f0647 100644 --- a/migration/migration-hmp-cmds.c +++ b/migration/migration-hmp-cmds.c @@ -151,7 +151,9 @@ void hmp_info_migrate(Monitor *mon, const QDict *qdict) if (info->has_status) { monitor_printf(mon, "Status: \t\t%s", MigrationStatus_str(info->status)); - if (info->status == MIGRATION_STATUS_FAILED && info->error_desc) { + if ((info->status == MIGRATION_STATUS_FAILED || + info->status == MIGRATION_STATUS_POSTCOPY_PAUSED) && + info->error_desc) { monitor_printf(mon, " (%s)\n", info->error_desc); } else { monitor_printf(mon, "\n"); From 24ad5e19952b326796c8a3a1595c57ff180dab84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 18 Jul 2025 16:05:11 +0100 Subject: [PATCH 2605/2760] crypto: implement workaround for GNUTLS thread safety problems MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When TLS 1.3 is negotiated on a TLS session, GNUTLS will perform automatic rekeying of the session after 16 million records. This is done for all algorithms except CHACHA20_POLY1305 which does not require rekeying. Unfortunately the rekeying breaks GNUTLS' promise that it is safe to use a gnutls_session_t object concurrently from multiple threads if they are exclusively calling gnutls_record_send/recv. This patch implements a workaround for QEMU that adds a mutex lock around any gnutls_record_send/recv call to serialize execution within GNUTLS code. When GNUTLS calls into the push/pull functions we can release the lock so the OS level I/O calls can at least have some parallelism. The big downside of this is that the actual encryption/decryption code is fully serialized, which will halve performance of that cipher operations if two threads are contending. The workaround is not enabled by default, since most use of GNUTLS in QEMU does not tickle the problem, only non-multifd migration with a return path open is affected. Fortunately the migration code also won't trigger the halving of performance, since only the outbound channel diretion needs to sustain high data rates, the inbound direction is low volume. Signed-off-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250718150514.2635338-2-berrange@redhat.com [add stub for qcrypto_tls_session_require_thread_safety; fix unused var] Signed-off-by: Fabiano Rosas --- crypto/tlssession.c | 92 +++++++++++++++++++++++++++++++++-- include/crypto/tlssession.h | 14 ++++++ meson.build | 9 ++++ meson_options.txt | 2 + scripts/meson-buildoptions.sh | 5 ++ 5 files changed, 119 insertions(+), 3 deletions(-) diff --git a/crypto/tlssession.c b/crypto/tlssession.c index 6d8f8df623..baef878fa0 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/thread.h" #include "crypto/tlssession.h" #include "crypto/tlscredsanon.h" #include "crypto/tlscredspsk.h" @@ -51,6 +52,14 @@ struct QCryptoTLSSession { */ Error *rerr; Error *werr; + + /* + * Used to protect against broken GNUTLS thread safety + * https://gitlab.com/gnutls/gnutls/-/issues/1717 + */ + bool requireThreadSafety; + bool lockEnabled; + QemuMutex lock; }; @@ -69,6 +78,7 @@ qcrypto_tls_session_free(QCryptoTLSSession *session) g_free(session->peername); g_free(session->authzid); object_unref(OBJECT(session->creds)); + qemu_mutex_destroy(&session->lock); g_free(session); } @@ -84,10 +94,19 @@ qcrypto_tls_session_push(void *opaque, const void *buf, size_t len) return -1; }; + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } + error_free(session->werr); session->werr = NULL; ret = session->writeFunc(buf, len, session->opaque, &session->werr); + + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { errno = EAGAIN; return -1; @@ -114,7 +133,16 @@ qcrypto_tls_session_pull(void *opaque, void *buf, size_t len) error_free(session->rerr); session->rerr = NULL; + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } + ret = session->readFunc(buf, len, session->opaque, &session->rerr); + + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } + if (ret == QCRYPTO_TLS_SESSION_ERR_BLOCK) { errno = EAGAIN; return -1; @@ -153,6 +181,8 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, session->creds = creds; object_ref(OBJECT(creds)); + qemu_mutex_init(&session->lock); + if (creds->endpoint != endpoint) { error_setg(errp, "Credentials endpoint doesn't match session"); goto error; @@ -289,6 +319,11 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds, return NULL; } +void qcrypto_tls_session_require_thread_safety(QCryptoTLSSession *sess) +{ + sess->requireThreadSafety = true; +} + static int qcrypto_tls_session_check_certificate(QCryptoTLSSession *session, Error **errp) @@ -480,7 +515,17 @@ qcrypto_tls_session_write(QCryptoTLSSession *session, size_t len, Error **errp) { - ssize_t ret = gnutls_record_send(session->handle, buf, len); + ssize_t ret; + + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } + + ret = gnutls_record_send(session->handle, buf, len); + + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } if (ret < 0) { if (ret == GNUTLS_E_AGAIN) { @@ -509,7 +554,17 @@ qcrypto_tls_session_read(QCryptoTLSSession *session, bool gracefulTermination, Error **errp) { - ssize_t ret = gnutls_record_recv(session->handle, buf, len); + ssize_t ret; + + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } + + ret = gnutls_record_recv(session->handle, buf, len); + + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } if (ret < 0) { if (ret == GNUTLS_E_AGAIN) { @@ -545,8 +600,29 @@ int qcrypto_tls_session_handshake(QCryptoTLSSession *session, Error **errp) { - int ret = gnutls_handshake(session->handle); + int ret; + ret = gnutls_handshake(session->handle); + if (!ret) { +#ifdef CONFIG_GNUTLS_BUG1717_WORKAROUND + gnutls_cipher_algorithm_t cipher = + gnutls_cipher_get(session->handle); + + /* + * Any use of rekeying in TLS 1.3 is unsafe for + * a gnutls with bug 1717, however, we know that + * QEMU won't initiate manual rekeying. Thus we + * only have to protect against automatic rekeying + * which doesn't trigger with CHACHA20 + */ + if (session->requireThreadSafety && + gnutls_protocol_get_version(session->handle) == + GNUTLS_TLS1_3 && + cipher != GNUTLS_CIPHER_CHACHA20_POLY1305) { + session->lockEnabled = true; + } +#endif + session->handshakeComplete = true; return QCRYPTO_TLS_HANDSHAKE_COMPLETE; } @@ -584,8 +660,15 @@ qcrypto_tls_session_bye(QCryptoTLSSession *session, Error **errp) return 0; } + if (session->lockEnabled) { + qemu_mutex_lock(&session->lock); + } ret = gnutls_bye(session->handle, GNUTLS_SHUT_WR); + if (session->lockEnabled) { + qemu_mutex_unlock(&session->lock); + } + if (!ret) { return QCRYPTO_TLS_BYE_COMPLETE; } @@ -651,6 +734,9 @@ qcrypto_tls_session_new(QCryptoTLSCreds *creds G_GNUC_UNUSED, return NULL; } +void qcrypto_tls_session_require_thread_safety(QCryptoTLSSession *sess) +{ +} void qcrypto_tls_session_free(QCryptoTLSSession *sess G_GNUC_UNUSED) diff --git a/include/crypto/tlssession.h b/include/crypto/tlssession.h index d77ae0d423..2f62ce2d67 100644 --- a/include/crypto/tlssession.h +++ b/include/crypto/tlssession.h @@ -165,6 +165,20 @@ void qcrypto_tls_session_free(QCryptoTLSSession *sess); G_DEFINE_AUTOPTR_CLEANUP_FUNC(QCryptoTLSSession, qcrypto_tls_session_free) +/** + * qcrypto_tls_session_require_thread_safety: + * @sess: the TLS session object + * + * Mark that this TLS session will require thread safety + * for concurrent I/O in both directions. This must be + * called before the handshake is performed. + * + * This will activate a workaround for GNUTLS thread + * safety issues, where appropriate for the negotiated + * TLS session parameters. + */ +void qcrypto_tls_session_require_thread_safety(QCryptoTLSSession *sess); + /** * qcrypto_tls_session_check_credentials: * @sess: the TLS session object diff --git a/meson.build b/meson.build index c2bc3eeedc..e53cd5b413 100644 --- a/meson.build +++ b/meson.build @@ -1809,6 +1809,7 @@ endif gnutls = not_found gnutls_crypto = not_found +gnutls_bug1717_workaround = false if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_system) # For general TLS support our min gnutls matches # that implied by our platform support matrix @@ -1834,6 +1835,12 @@ if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_syste method: 'pkg-config', required: get_option('gnutls')) endif + + if gnutls.found() and not get_option('gnutls-bug1717-workaround').disabled() + # XXX: when bug 1717 is resolved, add logic to probe for + # the GNUTLS fixed version number to handle the 'auto' case + gnutls_bug1717_workaround = true + endif endif # We prefer use of gnutls for crypto, unless the options @@ -2585,6 +2592,7 @@ config_host_data.set('CONFIG_KEYUTILS', keyutils.found()) config_host_data.set('CONFIG_GETTID', has_gettid) config_host_data.set('CONFIG_GNUTLS', gnutls.found()) config_host_data.set('CONFIG_GNUTLS_CRYPTO', gnutls_crypto.found()) +config_host_data.set('CONFIG_GNUTLS_BUG1717_WORKAROUND', gnutls_bug1717_workaround) config_host_data.set('CONFIG_TASN1', tasn1.found()) config_host_data.set('CONFIG_GCRYPT', gcrypt.found()) config_host_data.set('CONFIG_NETTLE', nettle.found()) @@ -4869,6 +4877,7 @@ summary_info += {'TLS priority': get_option('tls_priority')} summary_info += {'GNUTLS support': gnutls} if gnutls.found() summary_info += {' GNUTLS crypto': gnutls_crypto.found()} + summary_info += {' GNUTLS bug 1717 workaround': gnutls_bug1717_workaround } endif summary_info += {'libgcrypt': gcrypt} summary_info += {'nettle': nettle} diff --git a/meson_options.txt b/meson_options.txt index fff1521e58..dd33530750 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -174,6 +174,8 @@ option('libcbor', type : 'feature', value : 'auto', description: 'libcbor support') option('gnutls', type : 'feature', value : 'auto', description: 'GNUTLS cryptography support') +option('gnutls-bug1717-workaround', type: 'feature', value : 'auto', + description: 'GNUTLS workaround for https://gitlab.com/gnutls/gnutls/-/issues/1717') option('nettle', type : 'feature', value : 'auto', description: 'nettle cryptography support') option('gcrypt', type : 'feature', value : 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 0ebe6bc52a..d559e260ed 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -123,6 +123,9 @@ meson_options_help() { printf "%s\n" ' gio use libgio for D-Bus support' printf "%s\n" ' glusterfs Glusterfs block device driver' printf "%s\n" ' gnutls GNUTLS cryptography support' + printf "%s\n" ' gnutls-bug1717-workaround' + printf "%s\n" ' GNUTLS workaround for' + printf "%s\n" ' https://gitlab.com/gnutls/gnutls/-/issues/1717' printf "%s\n" ' gtk GTK+ user interface' printf "%s\n" ' gtk-clipboard clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)' printf "%s\n" ' guest-agent Build QEMU Guest Agent' @@ -331,6 +334,8 @@ _meson_option_parse() { --disable-glusterfs) printf "%s" -Dglusterfs=disabled ;; --enable-gnutls) printf "%s" -Dgnutls=enabled ;; --disable-gnutls) printf "%s" -Dgnutls=disabled ;; + --enable-gnutls-bug1717-workaround) printf "%s" -Dgnutls-bug1717-workaround=enabled ;; + --disable-gnutls-bug1717-workaround) printf "%s" -Dgnutls-bug1717-workaround=disabled ;; --enable-gtk) printf "%s" -Dgtk=enabled ;; --disable-gtk) printf "%s" -Dgtk=disabled ;; --enable-gtk-clipboard) printf "%s" -Dgtk_clipboard=enabled ;; From edea818371bd7179b55f38d3b113d342251d8f9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 18 Jul 2025 16:05:12 +0100 Subject: [PATCH 2606/2760] io: add support for activating TLS thread safety workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a QIO_CHANNEL_FEATURE_CONCURRENT_IO feature flag. If this is set on a QIOChannelTLS session object, the TLS session will be marked as requiring thread safety, which will activate the workaround for GNUTLS bug 1717 if needed. Signed-off-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250718150514.2635338-3-berrange@redhat.com Signed-off-by: Fabiano Rosas --- include/io/channel.h | 1 + io/channel-tls.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/include/io/channel.h b/include/io/channel.h index 62b657109c..234e5db70d 100644 --- a/include/io/channel.h +++ b/include/io/channel.h @@ -46,6 +46,7 @@ enum QIOChannelFeature { QIO_CHANNEL_FEATURE_WRITE_ZERO_COPY, QIO_CHANNEL_FEATURE_READ_MSG_PEEK, QIO_CHANNEL_FEATURE_SEEKABLE, + QIO_CHANNEL_FEATURE_CONCURRENT_IO, }; diff --git a/io/channel-tls.c b/io/channel-tls.c index db2ac1deae..a8248a9216 100644 --- a/io/channel-tls.c +++ b/io/channel-tls.c @@ -241,6 +241,11 @@ void qio_channel_tls_handshake(QIOChannelTLS *ioc, { QIOTask *task; + if (qio_channel_has_feature(QIO_CHANNEL(ioc), + QIO_CHANNEL_FEATURE_CONCURRENT_IO)) { + qcrypto_tls_session_require_thread_safety(ioc->session); + } + task = qio_task_new(OBJECT(ioc), func, opaque, destroy); From eb3618e9e259ef93f5a1a76867fbccae540fcd61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 18 Jul 2025 16:05:13 +0100 Subject: [PATCH 2607/2760] migration: activate TLS thread safety workaround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When either the postcopy or return path capabilities are enabled, the migration code will use the primary channel for bidirectional I/O. If either of those capabilities are enabled, the migration code needs to mark the channel as expecting concurrent I/O in order to activate the thread safety workarounds for GNUTLS bug 1717 Closes: https://gitlab.com/qemu-project/qemu/-/issues/1937 Signed-off-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250718150514.2635338-4-berrange@redhat.com Signed-off-by: Fabiano Rosas --- migration/tls.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/migration/tls.c b/migration/tls.c index 5cbf952383..284a6194b2 100644 --- a/migration/tls.c +++ b/migration/tls.c @@ -90,6 +90,10 @@ void migration_tls_channel_process_incoming(MigrationState *s, trace_migration_tls_incoming_handshake_start(); qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-incoming"); + if (migrate_postcopy_ram() || migrate_return_path()) { + qio_channel_set_feature(QIO_CHANNEL(tioc), + QIO_CHANNEL_FEATURE_CONCURRENT_IO); + } qio_channel_tls_handshake(tioc, migration_tls_incoming_handshake, NULL, @@ -149,6 +153,11 @@ void migration_tls_channel_connect(MigrationState *s, s->hostname = g_strdup(hostname); trace_migration_tls_outgoing_handshake_start(hostname); qio_channel_set_name(QIO_CHANNEL(tioc), "migration-tls-outgoing"); + + if (migrate_postcopy_ram() || migrate_return_path()) { + qio_channel_set_feature(QIO_CHANNEL(tioc), + QIO_CHANNEL_FEATURE_CONCURRENT_IO); + } qio_channel_tls_handshake(tioc, migration_tls_outgoing_handshake, s, From 0db6f798024ea6f57ecf2020209b761b50a01d71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Fri, 18 Jul 2025 16:05:14 +0100 Subject: [PATCH 2608/2760] crypto: add tracing & warning about GNUTLS countermeasures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We want some visibility on stderr when the GNUTLS thread safety countermeasures are activated, to encourage people to get the real fix deployed (once it exists). Some trace points will also help if we see any further wierd crash scenario we've not anticipated. Reviewed-by: Daniel P. Berrangé Signed-off-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250718150514.2635338-5-berrange@redhat.com [add missing include] Signed-off-by: Fabiano Rosas --- crypto/tlssession.c | 11 +++++++++++ crypto/trace-events | 2 ++ 2 files changed, 13 insertions(+) diff --git a/crypto/tlssession.c b/crypto/tlssession.c index baef878fa0..86d407a142 100644 --- a/crypto/tlssession.c +++ b/crypto/tlssession.c @@ -19,6 +19,7 @@ */ #include "qemu/osdep.h" +#include "qemu/error-report.h" #include "qemu/thread.h" #include "crypto/tlssession.h" #include "crypto/tlscredsanon.h" @@ -615,10 +616,20 @@ qcrypto_tls_session_handshake(QCryptoTLSSession *session, * only have to protect against automatic rekeying * which doesn't trigger with CHACHA20 */ + trace_qcrypto_tls_session_parameters( + session, + session->requireThreadSafety, + gnutls_protocol_get_version(session->handle), + cipher); + if (session->requireThreadSafety && gnutls_protocol_get_version(session->handle) == GNUTLS_TLS1_3 && cipher != GNUTLS_CIPHER_CHACHA20_POLY1305) { + warn_report("WARNING: activating thread safety countermeasures " + "for potentially broken GNUTLS with TLS1.3 cipher=%d", + cipher); + trace_qcrypto_tls_session_bug1717_workaround(session); session->lockEnabled = true; } #endif diff --git a/crypto/trace-events b/crypto/trace-events index bccd0bbf29..d0e33427fa 100644 --- a/crypto/trace-events +++ b/crypto/trace-events @@ -21,6 +21,8 @@ qcrypto_tls_creds_x509_load_cert_list(void *creds, const char *file) "TLS creds # tlssession.c qcrypto_tls_session_new(void *session, void *creds, const char *hostname, const char *authzid, int endpoint) "TLS session new session=%p creds=%p hostname=%s authzid=%s endpoint=%d" qcrypto_tls_session_check_creds(void *session, const char *status) "TLS session check creds session=%p status=%s" +qcrypto_tls_session_parameters(void *session, int threadSafety, int protocol, int cipher) "TLS session parameters session=%p threadSafety=%d protocol=%d cipher=%d" +qcrypto_tls_session_bug1717_workaround(void *session) "TLS session bug1717 workaround session=%p" # tls-cipher-suites.c qcrypto_tls_cipher_suite_priority(const char *name) "priority: %s" From c4103b27973653a7a9f64244de8fb5243e4397c5 Mon Sep 17 00:00:00 2001 From: Harsh Prateek Bora Date: Thu, 24 Jul 2025 15:36:15 +0530 Subject: [PATCH 2609/2760] MAINTAINERS: Adding myself as a co-maintainer for ppc/spapr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have been contributing in ppc/spapr and related areas for quite some time as a contributor and reviewer. I think its time to step up as a co-maintainer to help with maintainer activities. Signed-off-by: Harsh Prateek Bora Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250724100623.3071131-2-harshpb@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index a462345618..afc94e6e67 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1550,6 +1550,7 @@ F: tests/functional/test_ppc_40p.py sPAPR (pseries) M: Nicholas Piggin +M: Harsh Prateek Bora R: Daniel Henrique Barboza R: Harsh Prateek Bora L: qemu-ppc@nongnu.org From 1da3e7f863d65bb850a7662c6b8dfba623ac7cb7 Mon Sep 17 00:00:00 2001 From: Harsh Prateek Bora Date: Thu, 24 Jul 2025 15:36:16 +0530 Subject: [PATCH 2610/2760] MAINTAINERS: Adding myself as reviewer for PPC KVM cpus. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Have been contributing in ppc/spapr from tcg/kvm perspective, stepping up to help with patch reviews and get notified of incoming changes. Signed-off-by: Harsh Prateek Bora Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250724100623.3071131-3-harshpb@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index afc94e6e67..6e95ef00c1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -453,6 +453,7 @@ F: target/mips/system/ PPC KVM CPUs M: Nicholas Piggin R: Daniel Henrique Barboza +R: Harsh Prateek Bora S: Odd Fixes F: target/ppc/kvm.c From 884216cf419bb56664e3a8e7c0ce19d180fe2b24 Mon Sep 17 00:00:00 2001 From: Chinmay Rath Date: Thu, 24 Jul 2025 15:36:17 +0530 Subject: [PATCH 2611/2760] MAINTAINERS: Add myself as reviewer for PowerPC TCG CPUs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I have been working on Power ISA for a long time now and have mostly contributed in TCG instruction translation area (moved 300+ instructions to decodetree as of yet) and would like to continue contributing to PPC TCG in best possible ways I can. I think it's time to step up and assist in reviewing related patches to enable myself contribute more effectively in this direction. Signed-off-by: Chinmay Rath Reviewed-by: Cédric Le Goater Signed-off-by: Harsh Prateek Bora Link: https://lore.kernel.org/qemu-devel/20250724100623.3071131-4-harshpb@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 6e95ef00c1..31bcb82e93 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -296,6 +296,7 @@ F: tests/tcg/openrisc/ PowerPC TCG CPUs M: Nicholas Piggin M: Daniel Henrique Barboza +R: Chinmay Rath L: qemu-ppc@nongnu.org S: Odd Fixes F: target/ppc/ From 01286ee41ea3fc030a171dc5813945370f18955b Mon Sep 17 00:00:00 2001 From: Aditya Gupta Date: Thu, 24 Jul 2025 15:36:18 +0530 Subject: [PATCH 2612/2760] MAINTAINERS: Add myself as a reviewer of PowerNV emulation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proposing myself as a reviewer in the PowerNV emulation in QEMU Have been working on PowerNV QEMU for sometime, with contributions in Power11, MPIPL and minor fixes and things such as dtb support Cc: Cédric Le Goater Cc: Frédéric Barrat Cc: Mahesh J Salgaonkar Cc: Madhavan Srinivasan Cc: Nicholas Piggin Signed-off-by: Aditya Gupta Signed-off-by: Harsh Prateek Bora Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250724100623.3071131-5-harshpb@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 31bcb82e93..1ba161d75b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1579,6 +1579,7 @@ F: tests/functional/test_ppc64_tuxrun.py PowerNV (Non-Virtualized) M: Nicholas Piggin R: Frédéric Barrat +R: Aditya Gupta L: qemu-ppc@nongnu.org S: Odd Fixes F: docs/system/ppc/powernv.rst From 076b4306f9b6743059592e9632b149ac7708fa40 Mon Sep 17 00:00:00 2001 From: Gautam Menghani Date: Thu, 24 Jul 2025 15:36:19 +0530 Subject: [PATCH 2613/2760] MAINTAINERS: Add myself as a reviewer for XIVE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proposing myself as a reviewer for XIVE on PPC. I have been looking at XIVE in context of KVM internally at IBM for some time in addition to testing a few XIVE upstream patches; and I'll be closely looking at XIVE going forward. Signed-off-by: Gautam Menghani Signed-off-by: Harsh Prateek Bora Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250724100623.3071131-6-harshpb@linux.ibm.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 1ba161d75b..f3f981f90d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2781,6 +2781,7 @@ T: git https://github.com/philmd/qemu.git fw_cfg-next XIVE R: Frédéric Barrat +R: Gautam Menghani L: qemu-ppc@nongnu.org S: Odd Fixes F: hw/*/*xive* From cf8f0f006d380d50ef10ab77a4673e83eb19006c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Thu, 24 Jul 2025 09:59:16 +0200 Subject: [PATCH 2614/2760] =?UTF-8?q?MAINTAINERS:=20Remove=20Fr=C3=A9d?= =?UTF-8?q?=C3=A9ric=20as=20reviewer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Frédéric has moved to other tasks within IBM and no longer does QEMU development. Cc: Frédéric Barrat Acked-by: Frédéric Barrat Reviewed-by: Daniel Henrique Barboza Link: https://lore.kernel.org/qemu-devel/20250724075916.1593420-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- MAINTAINERS | 2 -- 1 file changed, 2 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index f3f981f90d..9481a21c80 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1578,7 +1578,6 @@ F: tests/functional/test_ppc64_tuxrun.py PowerNV (Non-Virtualized) M: Nicholas Piggin -R: Frédéric Barrat R: Aditya Gupta L: qemu-ppc@nongnu.org S: Odd Fixes @@ -2780,7 +2779,6 @@ F: tests/qtest/fw_cfg-test.c T: git https://github.com/philmd/qemu.git fw_cfg-next XIVE -R: Frédéric Barrat R: Gautam Menghani L: qemu-ppc@nongnu.org S: Odd Fixes From 0fb961e392e2055adc5429236989b01bb763f12c Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 24 Jul 2025 09:34:16 -0300 Subject: [PATCH 2615/2760] MAINTAINERS: remove myself as ppc maintainer/reviewer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It has been awhile since I actively did anything for qemu-ppc aside from reading the qemu-ppc inbox a couple of times each month. It's not enough to justify a reviewer role, let alone being a maintainer. Given that we're doing qemu-ppc maintainership changes across the board I'll take the opportunity and remove myself from the premises too. Feel free to reach out with questions about code I did in the past, but at this moment I'm no longer able to keep up with qemu-ppc activities. Signed-off-by: Daniel Henrique Barboza Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/20250724123416.3115941-1-danielhb413@gmail.com [ clg: Adjusted context ] Signed-off-by: Cédric Le Goater --- MAINTAINERS | 3 --- 1 file changed, 3 deletions(-) diff --git a/MAINTAINERS b/MAINTAINERS index 9481a21c80..f1bd69c3db 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -295,7 +295,6 @@ F: tests/tcg/openrisc/ PowerPC TCG CPUs M: Nicholas Piggin -M: Daniel Henrique Barboza R: Chinmay Rath L: qemu-ppc@nongnu.org S: Odd Fixes @@ -453,7 +452,6 @@ F: target/mips/system/ PPC KVM CPUs M: Nicholas Piggin -R: Daniel Henrique Barboza R: Harsh Prateek Bora S: Odd Fixes F: target/ppc/kvm.c @@ -1553,7 +1551,6 @@ F: tests/functional/test_ppc_40p.py sPAPR (pseries) M: Nicholas Piggin M: Harsh Prateek Bora -R: Daniel Henrique Barboza R: Harsh Prateek Bora L: qemu-ppc@nongnu.org S: Odd Fixes From 012842c075520dbe1bd96a2fdcf4e218874ba443 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Mon, 21 Jul 2025 19:54:52 +0100 Subject: [PATCH 2616/2760] log: make '-msg timestamp=on' apply to all qemu_log usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently the tracing 'log' back emits special code to add timestamps to trace points sent via qemu_log(). This current impl is a bad design for a number of reasons. * It changes the QEMU headers, such that 'error-report.h' content is visible to all files using tracing, but only when the 'log' backend is enabled. This has led to build failure bugs as devs rarely test without the (default) 'log' backend enabled, and CI can't cover every scenario for every trace backend. * It bloats the trace points definitions which are inlined into every probe location due to repeated inlining of timestamp formatting code, adding MBs of overhead to QEMU. * The tracing subsystem should not be treated any differently from other users of qemu_log. They all would benefit from having timestamps present. * The timestamp emitted with the tracepoints is in a needlessly different format to that used by error_report() in response to '-msg timestamp=on'. This fixes all these issues simply by moving timestamp formatting into qemu_log, using the same approach as for error_report. The code before: static inline void _nocheck__trace_qcrypto_tls_creds_get_path(void * creds, const char * filename, const char * path) { if (trace_event_get_state(TRACE_QCRYPTO_TLS_CREDS_GET_PATH) && qemu_loglevel_mask(LOG_TRACE)) { if (message_with_timestamp) { struct timeval _now; gettimeofday(&_now, NULL); qemu_log("%d@%zu.%06zu:qcrypto_tls_creds_get_path " "TLS creds path creds=%p filename=%s path=%s" "\n", qemu_get_thread_id(), (size_t)_now.tv_sec, (size_t)_now.tv_usec , creds, filename, path); } else { qemu_log("qcrypto_tls_creds_get_path " "TLS creds path creds=%p filename=%s path=%s" "\n", creds, filename, path); } } } and after: static inline void _nocheck__trace_qcrypto_tls_creds_get_path(void * creds, const char * filename, const char * path) { if (trace_event_get_state(TRACE_QCRYPTO_TLS_CREDS_GET_PATH) && qemu_loglevel_mask(LOG_TRACE)) { qemu_log("qcrypto_tls_creds_get_path " "TLS creds path creds=%p filename=%s path=%s" "\n", creds, filename, path); } } The log and error messages before: $ qemu-system-x86_64 -trace qcrypto* -object tls-creds-x509,id=tls0,dir=$HOME/tls -msg timestamp=on 2986097@1753122905.917608:qcrypto_tls_creds_x509_load TLS creds x509 load creds=0x55d925bd9490 dir=/var/home/berrange/tls 2986097@1753122905.917621:qcrypto_tls_creds_get_path TLS creds path creds=0x55d925bd9490 filename=ca-cert.pem path= 2025-07-21T18:35:05.917626Z qemu-system-x86_64: Unable to access credentials /var/home/berrange/tls/ca-cert.pem: No such file or directory and after: $ qemu-system-x86_64 -trace qcrypto* -object tls-creds-x509,id=tls0,dir=$HOME/tls -msg timestamp=on 2025-07-21T18:43:28.089797Z qcrypto_tls_creds_x509_load TLS creds x509 load creds=0x55bf5bf12380 dir=/var/home/berrange/tls 2025-07-21T18:43:28.089815Z qcrypto_tls_creds_get_path TLS creds path creds=0x55bf5bf12380 filename=ca-cert.pem path= 2025-07-21T18:43:28.089819Z qemu-system-x86_64: Unable to access credentials /var/home/berrange/tls/ca-cert.pem: No such file or directory The binary size before: $ ls -alh qemu-system-x86_64 -rwxr-xr-x. 1 berrange berrange 87M Jul 21 19:39 qemu-system-x86_64 $ strip qemu-system-x86_64 $ ls -alh qemu-system-x86_64 -rwxr-xr-x. 1 berrange berrange 30M Jul 21 19:39 qemu-system-x86_64 and after: $ ls -alh qemu-system-x86_64 -rwxr-xr-x. 1 berrange berrange 85M Jul 21 19:41 qemu-system-x86_64 $ strip qemu-system-x86_64 $ ls -alh qemu-system-x86_64 -rwxr-xr-x. 1 berrange berrange 29M Jul 21 19:41 qemu-system-x86_64 Signed-off-by: Daniel P. Berrangé Reviewed-by: Markus Armbruster Reviewed-by: Vladimir Sementsov-Ogievskiy Message-id: 20250721185452.3016488-1-berrange@redhat.com Signed-off-by: Stefan Hajnoczi --- scripts/tracetool/backend/log.py | 14 +------------- util/log.c | 20 +++++++++++++++++++- 2 files changed, 20 insertions(+), 14 deletions(-) diff --git a/scripts/tracetool/backend/log.py b/scripts/tracetool/backend/log.py index 5c9d09dd11..eb50ceea34 100644 --- a/scripts/tracetool/backend/log.py +++ b/scripts/tracetool/backend/log.py @@ -20,7 +20,6 @@ def generate_h_begin(events, group): out('#include "qemu/log-for-trace.h"', - '#include "qemu/error-report.h"', '') @@ -32,20 +31,9 @@ def generate_h(event, group): cond = "trace_event_get_state(%s)" % ("TRACE_" + event.name.upper()) out(' if (%(cond)s && qemu_loglevel_mask(LOG_TRACE)) {', - ' if (message_with_timestamp) {', - ' struct timeval _now;', - ' gettimeofday(&_now, NULL);', '#line %(event_lineno)d "%(event_filename)s"', - ' qemu_log("%%d@%%zu.%%06zu:%(name)s " %(fmt)s "\\n",', - ' qemu_get_thread_id(),', - ' (size_t)_now.tv_sec, (size_t)_now.tv_usec', - ' %(argnames)s);', + ' qemu_log("%(name)s " %(fmt)s "\\n"%(argnames)s);', '#line %(out_next_lineno)d "%(out_filename)s"', - ' } else {', - '#line %(event_lineno)d "%(event_filename)s"', - ' qemu_log("%(name)s " %(fmt)s "\\n"%(argnames)s);', - '#line %(out_next_lineno)d "%(out_filename)s"', - ' }', ' }', cond=cond, event_lineno=event.lineno, diff --git a/util/log.c b/util/log.c index 58d24de48a..abdcb6b311 100644 --- a/util/log.c +++ b/util/log.c @@ -145,10 +145,28 @@ void qemu_log_unlock(FILE *logfile) void qemu_log(const char *fmt, ...) { - FILE *f = qemu_log_trylock(); + FILE *f; + g_autofree const char *timestr = NULL; + + /* + * Prepare the timestamp *outside* the logging + * lock so it better reflects when the message + * was emitted if we are delayed acquiring the + * mutex + */ + if (message_with_timestamp) { + g_autoptr(GDateTime) dt = g_date_time_new_now_utc(); + timestr = g_date_time_format_iso8601(dt); + } + + f = qemu_log_trylock(); if (f) { va_list ap; + if (timestr) { + fprintf(f, "%s ", timestr); + } + va_start(ap, fmt); vfprintf(f, fmt, ap); va_end(ap); From b8882becd572d3afb888c836a6ffc7f92c17d1c5 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Fri, 21 Feb 2025 16:34:52 +0300 Subject: [PATCH 2617/2760] hw/display/qxl-render.c: fix qxl_unpack_chunks() chunk size calculation In case of multiple chunks, code in qxl_unpack_chunks() takes size of the wrong (next in the chain) chunk, instead of using current chunk size. This leads to wrong number of bytes being copied, and to crashes if next chunk size is larger than the current one. Based on the code by Gao Yong. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1628 Signed-off-by: Michael Tokarev Reviewed-by: Thomas Huth --- hw/display/qxl-render.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index eda6d3de37..c6a9ac1da1 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -222,6 +222,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, uint32_t max_chunks = 32; size_t offset = 0; size_t bytes; + QXLPHYSICAL next_chunk_phys = 0; for (;;) { bytes = MIN(size - offset, chunk->data_size); @@ -230,7 +231,15 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, if (offset == size) { return; } - chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id, + next_chunk_phys = chunk->next_chunk; + /* fist time, only get the next chunk's data size */ + chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, + sizeof(QXLDataChunk)); + if (!chunk) { + return; + } + /* second time, check data size and get data */ + chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, sizeof(QXLDataChunk) + chunk->data_size); if (!chunk) { return; From 0e171b427b1d2f68d76a2b7cae987432c10ca8aa Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Jul 2025 17:54:53 +0100 Subject: [PATCH 2618/2760] target/arm: Expand the descriptor for SME/SVE memory ops to i64 We have run out of room attempting to pack both the gvec descriptor and the mte descriptor into 32 bits. Here, change nothing except the parameter type, which affects all declarations, the function typedefs, and the type used with tcg expansion. Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Message-id: 20250723165458.3509150-2-peter.maydell@linaro.org --- target/arm/tcg/helper-sme.h | 162 ++-- target/arm/tcg/helper-sve.h | 1340 ++++++++++++++++---------------- target/arm/tcg/sme_helper.c | 16 +- target/arm/tcg/sve_helper.c | 96 +-- target/arm/tcg/translate-a64.h | 2 +- target/arm/tcg/translate-sme.c | 6 +- target/arm/tcg/translate-sve.c | 33 +- 7 files changed, 828 insertions(+), 827 deletions(-) diff --git a/target/arm/tcg/helper-sme.h b/target/arm/tcg/helper-sme.h index 1fc756bec6..c551797c6f 100644 --- a/target/arm/tcg/helper-sme.h +++ b/target/arm/tcg/helper-sme.h @@ -48,87 +48,87 @@ DEF_HELPER_FLAGS_3(sme2p1_movaz_zc_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2p1_movaz_zc_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(sme2p1_movaz_zc_q, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sme_ld1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1b_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_ld1h_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1h_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_ld1s_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1s_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_ld1d_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1d_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_ld1q_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_ld1q_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_st1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1b_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_st1h_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1h_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_st1s_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1s_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_st1d_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1d_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) - -DEF_HELPER_FLAGS_5(sme_st1q_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) -DEF_HELPER_FLAGS_5(sme_st1q_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i32) +DEF_HELPER_FLAGS_5(sme_ld1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1b_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_ld1h_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1h_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_ld1s_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1s_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_ld1d_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1d_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_ld1q_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_ld1q_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_st1b_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1b_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1b_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1b_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_st1h_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1h_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_st1s_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1s_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_st1d_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1d_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) + +DEF_HELPER_FLAGS_5(sme_st1q_be_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_le_h, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_be_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_le_v, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_be_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_le_h_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_be_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) +DEF_HELPER_FLAGS_5(sme_st1q_le_v_mte, TCG_CALL_NO_WG, void, env, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_5(sme_addha_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_5(sme_addva_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, ptr, i32) diff --git a/target/arm/tcg/helper-sve.h b/target/arm/tcg/helper-sve.h index 5e4b7fd8cf..c3541a8ca8 100644 --- a/target/arm/tcg/helper-sve.h +++ b/target/arm/tcg/helper-sve.h @@ -1655,1015 +1655,1015 @@ DEF_HELPER_FLAGS_4(sve2_usubw_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_usubw_s, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) DEF_HELPER_FLAGS_4(sve2_usubw_d, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) -DEF_HELPER_FLAGS_4(sve_ld1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld2qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld2qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1squ_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1dqu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1squ_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1dqu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld2dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld2qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld2qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld3qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld4qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bhs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bss_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1bds_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hsu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hdu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hds_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1hsu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hdu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1hds_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1sdu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1sds_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1squ_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1dqu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1sdu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1sds_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ld1squ_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ld1dqu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldff1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bhs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bss_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldff1bds_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) +DEF_HELPER_FLAGS_4(sve_ld1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld2qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld2qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1squ_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1dqu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1squ_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1dqu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld2dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld2qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld2qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld3qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld4qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bhs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bss_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1bds_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hsu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hdu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hds_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1hsu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hdu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1hds_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1sdu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1sds_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1squ_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1dqu_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1sdu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1sds_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ld1squ_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ld1dqu_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldff1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bhs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bss_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldff1bds_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hh_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hsu_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hdu_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hss_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hds_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hh_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hsu_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hdu_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hss_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1hds_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1ss_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1sdu_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1sds_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1ss_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1sdu_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1sds_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1dd_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldff1dd_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_ldnf1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bhs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bss_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_ldnf1bds_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bhu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bsu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bdu_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bhs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bss_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bds_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hsu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hsu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1hds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1sdu_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1sds_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1sdu_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1sds_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_ldnf1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bhu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bsu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bdu_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bhs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bss_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_ldnf1bds_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hh_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hsu_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hdu_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hss_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hds_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hh_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hsu_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hdu_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hss_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1hds_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1ss_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1sdu_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1sds_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1ss_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1sdu_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1sds_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1dd_le_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) + void, env, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve_ldnf1dd_be_r_mte, TCG_CALL_NO_WG, - void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st2qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st2qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1bh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1bs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1bd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1hs_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hs_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1sd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1sd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1sq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1sq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1dq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1dq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st2dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st2qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st2qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st3qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st4qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1bh_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1bs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1bd_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1hs_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hs_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1hd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1sd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1sd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) - -DEF_HELPER_FLAGS_4(sve_st1sq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1sq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1dq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) -DEF_HELPER_FLAGS_4(sve_st1dq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i32) + void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4bb_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4hh_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4hh_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4ss_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4ss_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4dd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4dd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st2qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4qq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st2qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4qq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1bh_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1bs_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1bd_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1hs_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1hd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1hs_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1hd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1sd_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1sd_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1sq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1sq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1dq_le_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1dq_be_r, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4bb_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4hh_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4hh_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4ss_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4ss_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4dd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st2dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4dd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st2qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4qq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st2qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st3qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st4qq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1bh_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1bs_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1bd_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1hs_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1hd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1hs_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1hd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1sd_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1sd_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) + +DEF_HELPER_FLAGS_4(sve_st1sq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1sq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1dq_le_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) +DEF_HELPER_FLAGS_4(sve_st1dq_be_r_mte, TCG_CALL_NO_WG, void, env, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbsu_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbss_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbsu_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbss_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbdu_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbds_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbdu_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbds_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbdu_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbds_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldqq_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldqq_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbsu_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbss_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbsu_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhsu_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldss_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbss_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhss_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbdu_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbds_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbdu_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbds_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbdu_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhdu_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsdu_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_lddd_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldbds_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldhds_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldsds_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldqq_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldqq_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbsu_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbss_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbsu_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbss_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbdu_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbds_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbdu_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbds_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbdu_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbds_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbsu_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbss_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbsu_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhsu_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffss_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbss_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhss_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbdu_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbds_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbdu_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbds_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbdu_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhdu_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsdu_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffdd_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffbds_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffhds_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_ldffsds_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbs_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbs_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbd_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_le_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_be_zsu, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbd_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_le_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_be_zss, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbd_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stqq_le_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stqq_be_zd, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbs_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbs_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sths_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stss_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbd_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_le_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_be_zsu_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbd_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_le_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_be_zss_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stbd_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_sthd_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stsd_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stdd_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stqq_le_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_6(sve_stqq_be_zd_mte, TCG_CALL_NO_WG, - void, env, ptr, ptr, ptr, tl, i32) + void, env, ptr, ptr, ptr, tl, i64) DEF_HELPER_FLAGS_4(sve2_sqdmull_zzz_h, TCG_CALL_NO_RWG, void, ptr, ptr, ptr, i32) @@ -3151,18 +3151,18 @@ DEF_HELPER_FLAGS_3(pmov_vp_h, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(pmov_vp_s, TCG_CALL_NO_RWG, void, ptr, ptr, i32) DEF_HELPER_FLAGS_3(pmov_vp_d, TCG_CALL_NO_RWG, void, ptr, ptr, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1bb_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1hh_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1hh_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1ss_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1ss_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1dd_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_ld1dd_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) - -DEF_HELPER_FLAGS_5(sve2p1_st1bb_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_st1hh_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_st1hh_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_st1ss_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_st1ss_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_st1dd_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) -DEF_HELPER_FLAGS_5(sve2p1_st1dd_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i32) +DEF_HELPER_FLAGS_5(sve2p1_ld1bb_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_ld1hh_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_ld1hh_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_ld1ss_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_ld1ss_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_ld1dd_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_ld1dd_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) + +DEF_HELPER_FLAGS_5(sve2p1_st1bb_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_st1hh_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_st1hh_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_st1ss_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_st1ss_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_st1dd_le_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) +DEF_HELPER_FLAGS_5(sve2p1_st1dd_be_c, TCG_CALL_NO_WG, void, env, ptr, tl, i32, i64) diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index bb8ed1ed0e..0b55f13f8c 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -691,28 +691,28 @@ void sme_ld1_mte(CPUARMState *env, void *za, uint64_t *vg, #define DO_LD(L, END, ESZ) \ void HELPER(sme_ld1##L##END##_h)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_ld1(env, za, vg, addr, desc, GETPC(), ESZ, 0, false, \ sve_ld1##L##L##END##_host, sve_ld1##L##L##END##_tlb, \ clear_horizontal, copy_horizontal); \ } \ void HELPER(sme_ld1##L##END##_v)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_ld1(env, za, vg, addr, desc, GETPC(), ESZ, 0, true, \ sme_ld1##L##END##_v_host, sme_ld1##L##END##_v_tlb, \ clear_vertical_##L, copy_vertical_##L); \ } \ void HELPER(sme_ld1##L##END##_h_mte)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_ld1_mte(env, za, vg, addr, desc, GETPC(), ESZ, false, \ sve_ld1##L##L##END##_host, sve_ld1##L##L##END##_tlb, \ clear_horizontal, copy_horizontal); \ } \ void HELPER(sme_ld1##L##END##_v_mte)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_ld1_mte(env, za, vg, addr, desc, GETPC(), ESZ, true, \ sme_ld1##L##END##_v_host, sme_ld1##L##END##_v_tlb, \ @@ -876,25 +876,25 @@ void sme_st1_mte(CPUARMState *env, void *za, uint64_t *vg, target_ulong addr, #define DO_ST(L, END, ESZ) \ void HELPER(sme_st1##L##END##_h)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_st1(env, za, vg, addr, desc, GETPC(), ESZ, 0, false, \ sve_st1##L##L##END##_host, sve_st1##L##L##END##_tlb); \ } \ void HELPER(sme_st1##L##END##_v)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_st1(env, za, vg, addr, desc, GETPC(), ESZ, 0, true, \ sme_st1##L##END##_v_host, sme_st1##L##END##_v_tlb); \ } \ void HELPER(sme_st1##L##END##_h_mte)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_st1_mte(env, za, vg, addr, desc, GETPC(), ESZ, false, \ sve_st1##L##L##END##_host, sve_st1##L##L##END##_tlb); \ } \ void HELPER(sme_st1##L##END##_v_mte)(CPUARMState *env, void *za, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sme_st1_mte(env, za, vg, addr, desc, GETPC(), ESZ, true, \ sme_st1##L##END##_v_host, sme_st1##L##END##_v_tlb); \ diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 803f0a094d..9fc2c05879 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -6384,13 +6384,13 @@ void sve_ldN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr, #define DO_LD1_1(NAME, ESZ) \ void HELPER(sve_##NAME##_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, MO_8, 1, 0, \ sve_##NAME##_host, sve_##NAME##_tlb); \ } \ void HELPER(sve_##NAME##_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MO_8, 1, \ sve_##NAME##_host, sve_##NAME##_tlb); \ @@ -6398,25 +6398,25 @@ void HELPER(sve_##NAME##_r_mte)(CPUARMState *env, void *vg, \ #define DO_LD1_2(NAME, ESZ, MSZ) \ void HELPER(sve_##NAME##_le_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, 1, 0, \ sve_##NAME##_le_host, sve_##NAME##_le_tlb); \ } \ void HELPER(sve_##NAME##_be_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, 1, 0, \ sve_##NAME##_be_host, sve_##NAME##_be_tlb); \ } \ void HELPER(sve_##NAME##_le_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, 1, \ sve_##NAME##_le_host, sve_##NAME##_le_tlb); \ } \ void HELPER(sve_##NAME##_be_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, 1, \ sve_##NAME##_be_host, sve_##NAME##_be_tlb); \ @@ -6450,13 +6450,13 @@ DO_LD1_2(ld1dqu, MO_64, MO_128) #define DO_LDN_1(N) \ void HELPER(sve_ld##N##bb_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r(env, vg, addr, desc, GETPC(), MO_8, MO_8, N, 0, \ sve_ld1bb_host, sve_ld1bb_tlb); \ } \ void HELPER(sve_ld##N##bb_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r_mte(env, vg, addr, desc, GETPC(), MO_8, MO_8, N, \ sve_ld1bb_host, sve_ld1bb_tlb); \ @@ -6464,25 +6464,25 @@ void HELPER(sve_ld##N##bb_r_mte)(CPUARMState *env, void *vg, \ #define DO_LDN_2(N, SUFF, ESZ) \ void HELPER(sve_ld##N##SUFF##_le_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, ESZ, N, 0, \ sve_ld1##SUFF##_le_host, sve_ld1##SUFF##_le_tlb); \ } \ void HELPER(sve_ld##N##SUFF##_be_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r(env, vg, addr, desc, GETPC(), ESZ, ESZ, N, 0, \ sve_ld1##SUFF##_be_host, sve_ld1##SUFF##_be_tlb); \ } \ void HELPER(sve_ld##N##SUFF##_le_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r_mte(env, vg, addr, desc, GETPC(), ESZ, ESZ, N, \ sve_ld1##SUFF##_le_host, sve_ld1##SUFF##_le_tlb); \ } \ void HELPER(sve_ld##N##SUFF##_be_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldN_r_mte(env, vg, addr, desc, GETPC(), ESZ, ESZ, N, \ sve_ld1##SUFF##_be_host, sve_ld1##SUFF##_be_tlb); \ @@ -6750,25 +6750,25 @@ void sve_ldnfff1_r_mte(CPUARMState *env, void *vg, target_ulong addr, #define DO_LDFF1_LDNF1_1(PART, ESZ) \ void HELPER(sve_ldff1##PART##_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r(env, vg, addr, desc, GETPC(), 0, ESZ, MO_8, FAULT_FIRST, \ sve_ld1##PART##_host, sve_ld1##PART##_tlb); \ } \ void HELPER(sve_ldnf1##PART##_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r(env, vg, addr, desc, GETPC(), 0, ESZ, MO_8, FAULT_NO, \ sve_ld1##PART##_host, sve_ld1##PART##_tlb); \ } \ void HELPER(sve_ldff1##PART##_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r_mte(env, vg, addr, desc, GETPC(), ESZ, MO_8, FAULT_FIRST, \ sve_ld1##PART##_host, sve_ld1##PART##_tlb); \ } \ void HELPER(sve_ldnf1##PART##_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r_mte(env, vg, addr, desc, GETPC(), ESZ, MO_8, FAULT_NO, \ sve_ld1##PART##_host, sve_ld1##PART##_tlb); \ @@ -6776,49 +6776,49 @@ void HELPER(sve_ldnf1##PART##_r_mte)(CPUARMState *env, void *vg, \ #define DO_LDFF1_LDNF1_2(PART, ESZ, MSZ) \ void HELPER(sve_ldff1##PART##_le_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r(env, vg, addr, desc, GETPC(), 0, ESZ, MSZ, FAULT_FIRST, \ sve_ld1##PART##_le_host, sve_ld1##PART##_le_tlb); \ } \ void HELPER(sve_ldnf1##PART##_le_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r(env, vg, addr, desc, GETPC(), 0, ESZ, MSZ, FAULT_NO, \ sve_ld1##PART##_le_host, sve_ld1##PART##_le_tlb); \ } \ void HELPER(sve_ldff1##PART##_be_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r(env, vg, addr, desc, GETPC(), 0, ESZ, MSZ, FAULT_FIRST, \ sve_ld1##PART##_be_host, sve_ld1##PART##_be_tlb); \ } \ void HELPER(sve_ldnf1##PART##_be_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r(env, vg, addr, desc, GETPC(), 0, ESZ, MSZ, FAULT_NO, \ sve_ld1##PART##_be_host, sve_ld1##PART##_be_tlb); \ } \ void HELPER(sve_ldff1##PART##_le_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, FAULT_FIRST, \ sve_ld1##PART##_le_host, sve_ld1##PART##_le_tlb); \ } \ void HELPER(sve_ldnf1##PART##_le_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, FAULT_NO, \ sve_ld1##PART##_le_host, sve_ld1##PART##_le_tlb); \ } \ void HELPER(sve_ldff1##PART##_be_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, FAULT_FIRST, \ sve_ld1##PART##_be_host, sve_ld1##PART##_be_tlb); \ } \ void HELPER(sve_ldnf1##PART##_be_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_ldnfff1_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, FAULT_NO, \ sve_ld1##PART##_be_host, sve_ld1##PART##_be_tlb); \ @@ -7007,13 +7007,13 @@ void sve_stN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr, #define DO_STN_1(N, NAME, ESZ) \ void HELPER(sve_st##N##NAME##_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_stN_r(env, vg, addr, desc, GETPC(), ESZ, MO_8, N, 0, \ sve_st1##NAME##_host, sve_st1##NAME##_tlb); \ } \ void HELPER(sve_st##N##NAME##_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_stN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MO_8, N, \ sve_st1##NAME##_host, sve_st1##NAME##_tlb); \ @@ -7021,25 +7021,25 @@ void HELPER(sve_st##N##NAME##_r_mte)(CPUARMState *env, void *vg, \ #define DO_STN_2(N, NAME, ESZ, MSZ) \ void HELPER(sve_st##N##NAME##_le_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_stN_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, N, 0, \ sve_st1##NAME##_le_host, sve_st1##NAME##_le_tlb); \ } \ void HELPER(sve_st##N##NAME##_be_r)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_stN_r(env, vg, addr, desc, GETPC(), ESZ, MSZ, N, 0, \ sve_st1##NAME##_be_host, sve_st1##NAME##_be_tlb); \ } \ void HELPER(sve_st##N##NAME##_le_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_stN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, N, \ sve_st1##NAME##_le_host, sve_st1##NAME##_le_tlb); \ } \ void HELPER(sve_st##N##NAME##_be_r_mte)(CPUARMState *env, void *vg, \ - target_ulong addr, uint32_t desc) \ + target_ulong addr, uint64_t desc) \ { \ sve_stN_r_mte(env, vg, addr, desc, GETPC(), ESZ, MSZ, N, \ sve_st1##NAME##_be_host, sve_st1##NAME##_be_tlb); \ @@ -7204,13 +7204,13 @@ void sve_ld1_z_mte(CPUARMState *env, void *vd, uint64_t *vg, void *vm, #define DO_LD1_ZPZ_S(MEM, OFS, MSZ) \ void HELPER(sve_ld##MEM##_##OFS)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ld1_z(env, vd, vg, vm, base, desc, GETPC(), 0, 4, 1 << MSZ, \ off_##OFS##_s, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ } \ void HELPER(sve_ld##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ld1_z_mte(env, vd, vg, vm, base, desc, GETPC(), 4, 1 << MSZ, \ off_##OFS##_s, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ @@ -7218,13 +7218,13 @@ void HELPER(sve_ld##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ #define DO_LD1_ZPZ_D(MEM, OFS, MSZ) \ void HELPER(sve_ld##MEM##_##OFS)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ld1_z(env, vd, vg, vm, base, desc, GETPC(), 0, 8, 1 << MSZ, \ off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ } \ void HELPER(sve_ld##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ld1_z_mte(env, vd, vg, vm, base, desc, GETPC(), 8, 1 << MSZ, \ off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ @@ -7418,14 +7418,14 @@ void sve_ldff1_z_mte(CPUARMState *env, void *vd, uint64_t *vg, void *vm, #define DO_LDFF1_ZPZ_S(MEM, OFS, MSZ) \ void HELPER(sve_ldff##MEM##_##OFS) \ (CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ldff1_z(env, vd, vg, vm, base, desc, GETPC(), 0, MO_32, MSZ, \ off_##OFS##_s, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ } \ void HELPER(sve_ldff##MEM##_##OFS##_mte) \ (CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ldff1_z_mte(env, vd, vg, vm, base, desc, GETPC(), MO_32, MSZ, \ off_##OFS##_s, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ @@ -7434,14 +7434,14 @@ void HELPER(sve_ldff##MEM##_##OFS##_mte) \ #define DO_LDFF1_ZPZ_D(MEM, OFS, MSZ) \ void HELPER(sve_ldff##MEM##_##OFS) \ (CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ldff1_z(env, vd, vg, vm, base, desc, GETPC(), 0, MO_64, MSZ, \ off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ } \ void HELPER(sve_ldff##MEM##_##OFS##_mte) \ (CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_ldff1_z_mte(env, vd, vg, vm, base, desc, GETPC(), MO_64, MSZ, \ off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ @@ -7621,13 +7621,13 @@ void sve_st1_z_mte(CPUARMState *env, void *vd, uint64_t *vg, void *vm, #define DO_ST1_ZPZ_S(MEM, OFS, MSZ) \ void HELPER(sve_st##MEM##_##OFS)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_st1_z(env, vd, vg, vm, base, desc, GETPC(), 0, 4, 1 << MSZ, \ off_##OFS##_s, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ } \ void HELPER(sve_st##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_st1_z_mte(env, vd, vg, vm, base, desc, GETPC(), 4, 1 << MSZ, \ off_##OFS##_s, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ @@ -7635,13 +7635,13 @@ void HELPER(sve_st##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ #define DO_ST1_ZPZ_D(MEM, OFS, MSZ) \ void HELPER(sve_st##MEM##_##OFS)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_st1_z(env, vd, vg, vm, base, desc, GETPC(), 0, 8, 1 << MSZ, \ off_##OFS##_d, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ } \ void HELPER(sve_st##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ - void *vm, target_ulong base, uint32_t desc) \ + void *vm, target_ulong base, uint64_t desc) \ { \ sve_st1_z_mte(env, vd, vg, vm, base, desc, GETPC(), 8, 1 << MSZ, \ off_##OFS##_d, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ @@ -7995,7 +7995,7 @@ void sve2p1_ld1_c(CPUARMState *env, ARMVectorReg *zd, const vaddr addr, } void HELPER(sve2p1_ld1bb_c)(CPUARMState *env, void *vd, target_ulong addr, - uint32_t png, uint32_t desc) + uint32_t png, uint64_t desc) { sve2p1_ld1_c(env, vd, addr, png, desc, GETPC(), MO_8, sve_ld1bb_host, sve_ld1bb_tlb); @@ -8004,14 +8004,14 @@ void HELPER(sve2p1_ld1bb_c)(CPUARMState *env, void *vd, target_ulong addr, #define DO_LD1_2(NAME, ESZ) \ void HELPER(sve2p1_##NAME##_le_c)(CPUARMState *env, void *vd, \ target_ulong addr, uint32_t png, \ - uint32_t desc) \ + uint64_t desc) \ { \ sve2p1_ld1_c(env, vd, addr, png, desc, GETPC(), ESZ, \ sve_##NAME##_le_host, sve_##NAME##_le_tlb); \ } \ void HELPER(sve2p1_##NAME##_be_c)(CPUARMState *env, void *vd, \ target_ulong addr, uint32_t png, \ - uint32_t desc) \ + uint64_t desc) \ { \ sve2p1_ld1_c(env, vd, addr, png, desc, GETPC(), ESZ, \ sve_##NAME##_be_host, sve_##NAME##_be_tlb); \ @@ -8154,7 +8154,7 @@ void sve2p1_st1_c(CPUARMState *env, ARMVectorReg *zd, const vaddr addr, } void HELPER(sve2p1_st1bb_c)(CPUARMState *env, void *vd, target_ulong addr, - uint32_t png, uint32_t desc) + uint32_t png, uint64_t desc) { sve2p1_st1_c(env, vd, addr, png, desc, GETPC(), MO_8, sve_st1bb_host, sve_st1bb_tlb); @@ -8163,14 +8163,14 @@ void HELPER(sve2p1_st1bb_c)(CPUARMState *env, void *vd, target_ulong addr, #define DO_ST1_2(NAME, ESZ) \ void HELPER(sve2p1_##NAME##_le_c)(CPUARMState *env, void *vd, \ target_ulong addr, uint32_t png, \ - uint32_t desc) \ + uint64_t desc) \ { \ sve2p1_st1_c(env, vd, addr, png, desc, GETPC(), ESZ, \ sve_##NAME##_le_host, sve_##NAME##_le_tlb); \ } \ void HELPER(sve2p1_##NAME##_be_c)(CPUARMState *env, void *vd, \ target_ulong addr, uint32_t png, \ - uint32_t desc) \ + uint64_t desc) \ { \ sve2p1_st1_c(env, vd, addr, png, desc, GETPC(), ESZ, \ sve_##NAME##_be_host, sve_##NAME##_be_tlb); \ diff --git a/target/arm/tcg/translate-a64.h b/target/arm/tcg/translate-a64.h index 993dde61a4..9c45f89305 100644 --- a/target/arm/tcg/translate-a64.h +++ b/target/arm/tcg/translate-a64.h @@ -28,7 +28,7 @@ bool logic_imm_decode_wmask(uint64_t *result, unsigned int immn, bool sve_access_check(DisasContext *s); bool sme_enabled_check(DisasContext *s); bool sme_enabled_check_with_svcr(DisasContext *s, unsigned); -uint32_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, +uint64_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, uint32_t msz, bool is_write, uint32_t data); /* This function corresponds to CheckStreamingSVEEnabled. */ diff --git a/target/arm/tcg/translate-sme.c b/target/arm/tcg/translate-sme.c index 65fc8bc9b2..091c56da4f 100644 --- a/target/arm/tcg/translate-sme.c +++ b/target/arm/tcg/translate-sme.c @@ -387,7 +387,7 @@ TRANS_FEAT(MOVT_ztr, aa64_sme2, do_movt, a, tcg_gen_st_i64) static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) { - typedef void GenLdSt1(TCGv_env, TCGv_ptr, TCGv_ptr, TCGv, TCGv_i32); + typedef void GenLdSt1(TCGv_env, TCGv_ptr, TCGv_ptr, TCGv, TCGv_i64); /* * Indexed by [esz][be][v][mte][st], which is (except for load/store) @@ -415,7 +415,7 @@ static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) TCGv_ptr t_za, t_pg; TCGv_i64 addr; - uint32_t desc; + uint64_t desc; bool be = s->be_data == MO_BE; bool mte = s->mte_active[0]; @@ -440,7 +440,7 @@ static bool trans_LDST1(DisasContext *s, arg_LDST1 *a) desc = make_svemte_desc(s, streaming_vec_reg_size(s), 1, a->esz, a->st, 0); fns[a->esz][be][a->v][mte][a->st](tcg_env, t_za, t_pg, addr, - tcg_constant_i32(desc)); + tcg_constant_i64(desc)); return true; } diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 2ed440aff1..849151826e 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -31,9 +31,9 @@ typedef void gen_helper_gvec_flags_3(TCGv_i32, TCGv_ptr, TCGv_ptr, typedef void gen_helper_gvec_flags_4(TCGv_i32, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_ptr, TCGv_i32); -typedef void gen_helper_gvec_mem(TCGv_env, TCGv_ptr, TCGv_i64, TCGv_i32); +typedef void gen_helper_gvec_mem(TCGv_env, TCGv_ptr, TCGv_i64, TCGv_i64); typedef void gen_helper_gvec_mem_scatter(TCGv_env, TCGv_ptr, TCGv_ptr, - TCGv_ptr, TCGv_i64, TCGv_i32); + TCGv_ptr, TCGv_i64, TCGv_i64); /* * Helpers for extracting complex instruction fields. @@ -4883,11 +4883,11 @@ static const uint8_t dtype_esz[19] = { 4, 4, 4, }; -uint32_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, +uint64_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, uint32_t msz, bool is_write, uint32_t data) { uint32_t sizem1; - uint32_t desc = 0; + uint64_t desc = 0; /* Assert all of the data fits, with or without MTE enabled. */ assert(nregs >= 1 && nregs <= 4); @@ -4911,7 +4911,7 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, gen_helper_gvec_mem *fn) { TCGv_ptr t_pg; - uint32_t desc; + uint64_t desc; if (!s->mte_active[0]) { addr = clean_data_tbi(s, addr); @@ -4927,7 +4927,7 @@ static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, t_pg = tcg_temp_new_ptr(); tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); - fn(tcg_env, t_pg, addr, tcg_constant_i32(desc)); + fn(tcg_env, t_pg, addr, tcg_constant_i64(desc)); } /* Indexed by [mte][be][dtype][nreg] */ @@ -5379,7 +5379,7 @@ static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) unsigned vsz = vec_full_reg_size(s); TCGv_ptr t_pg; int poff; - uint32_t desc; + uint64_t desc; /* Load the first quadword using the normal predicated load helpers. */ if (!s->mte_active[0]) { @@ -5410,7 +5410,7 @@ static void do_ldrq(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) gen_helper_gvec_mem *fn = ldr_fns[s->mte_active[0]][s->be_data == MO_BE][dtype][0]; desc = make_svemte_desc(s, 16, 1, dtype_msz(dtype), false, zt); - fn(tcg_env, t_pg, addr, tcg_constant_i32(desc)); + fn(tcg_env, t_pg, addr, tcg_constant_i64(desc)); /* Replicate that first quadword. */ if (vsz > 16) { @@ -5453,7 +5453,7 @@ static void do_ldro(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) unsigned vsz_r32; TCGv_ptr t_pg; int poff, doff; - uint32_t desc; + uint64_t desc; if (vsz < 32) { /* @@ -5494,7 +5494,7 @@ static void do_ldro(DisasContext *s, int zt, int pg, TCGv_i64 addr, int dtype) gen_helper_gvec_mem *fn = ldr_fns[s->mte_active[0]][s->be_data == MO_BE][dtype][0]; desc = make_svemte_desc(s, 32, 1, dtype_msz(dtype), false, zt); - fn(tcg_env, t_pg, addr, tcg_constant_i32(desc)); + fn(tcg_env, t_pg, addr, tcg_constant_i64(desc)); /* * Replicate that first octaword. @@ -5828,14 +5828,14 @@ static void do_mem_zpz(DisasContext *s, int zt, int pg, int zm, TCGv_ptr t_zm = tcg_temp_new_ptr(); TCGv_ptr t_pg = tcg_temp_new_ptr(); TCGv_ptr t_zt = tcg_temp_new_ptr(); - uint32_t desc; + uint64_t desc; tcg_gen_addi_ptr(t_pg, tcg_env, pred_full_reg_offset(s, pg)); tcg_gen_addi_ptr(t_zm, tcg_env, vec_full_reg_offset(s, zm)); tcg_gen_addi_ptr(t_zt, tcg_env, vec_full_reg_offset(s, zt)); desc = make_svemte_desc(s, vec_full_reg_size(s), 1, msz, is_write, scale); - fn(tcg_env, t_zt, t_pg, t_zm, scalar, tcg_constant_i32(desc)); + fn(tcg_env, t_zt, t_pg, t_zm, scalar, tcg_constant_i64(desc)); } /* Indexed by [mte][be][ff][xs][u][msz]. */ @@ -8079,7 +8079,7 @@ static bool gen_ldst_c(DisasContext *s, TCGv_i64 addr, int zd, int png, MemOp esz, bool is_write, int n, bool strided) { typedef void ldst_c_fn(TCGv_env, TCGv_ptr, TCGv_i64, - TCGv_i32, TCGv_i32); + TCGv_i32, TCGv_i64); static ldst_c_fn * const f_ldst[2][2][4] = { { { gen_helper_sve2p1_ld1bb_c, gen_helper_sve2p1_ld1hh_le_c, @@ -8100,9 +8100,10 @@ static bool gen_ldst_c(DisasContext *s, TCGv_i64 addr, int zd, int png, gen_helper_sve2p1_st1dd_be_c, } } }; - TCGv_i32 t_png, t_desc; + TCGv_i32 t_png; + TCGv_i64 t_desc; TCGv_ptr t_zd; - uint32_t desc, lg2_rstride = 0; + uint64_t desc, lg2_rstride = 0; bool be = s->be_data == MO_BE; assert(n == 2 || n == 4); @@ -8132,7 +8133,7 @@ static bool gen_ldst_c(DisasContext *s, TCGv_i64 addr, int zd, int png, desc = n == 2 ? 0 : 1; desc = desc | (lg2_rstride << 1); desc = make_svemte_desc(s, vec_full_reg_size(s), 1, esz, is_write, desc); - t_desc = tcg_constant_i32(desc); + t_desc = tcg_constant_i64(desc); t_png = tcg_temp_new_i32(); tcg_gen_ld16u_i32(t_png, tcg_env, From aba39946baaf5ca73aae0b79e2cd0790ddafe291 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Jul 2025 17:54:54 +0100 Subject: [PATCH 2619/2760] target/arm: Pack mtedesc into upper 32 bits of descriptor Instead of trying to pack mtedesc into the upper 17 bits of a 32-bit gvec descriptor, pass the gvec descriptor in the lower 32 bits and the mte descriptor in the upper 32 bits of a 64-bit operand. This fixes two bugs: (1) in gen_sve_ldr() and gen_sve_str() call gen_mte_checkN() with a length value which is the SVE vector length and can be up to 256 bytes. We don't assert there that it fits in the descriptor, so we would just fail to do the MTE checks on the right length of memory if the VL is more than 32 bytes (2) the new-in-SVE2p1 insns LD3Q, LD4Q, ST3Q, ST4Q also involve transfers of more than 32 bytes of memory. In this case we would assert at translate time. (Note for potential backporting: this commit depends on the previous "target/arm: Expand the descriptor for SME/SVE memory ops to i64".) Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Richard Henderson Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell Message-id: 20250723165458.3509150-3-peter.maydell@linaro.org [PMM: expand commit message to clarify that we are fixing bugs here] Signed-off-by: Peter Maydell --- target/arm/internals.h | 8 +----- target/arm/tcg/sme_helper.c | 14 +++------- target/arm/tcg/sve_helper.c | 49 +++++++++++++--------------------- target/arm/tcg/translate-sve.c | 5 ++-- 4 files changed, 25 insertions(+), 51 deletions(-) diff --git a/target/arm/internals.h b/target/arm/internals.h index c4765e4489..1b3d0244fd 100644 --- a/target/arm/internals.h +++ b/target/arm/internals.h @@ -1623,19 +1623,13 @@ FIELD(PREDDESC, OPRSZ, 0, 6) FIELD(PREDDESC, ESZ, 6, 2) FIELD(PREDDESC, DATA, 8, 24) -/* - * The SVE simd_data field, for memory ops, contains either - * rd (5 bits) or a shift count (2 bits). - */ -#define SVE_MTEDESC_SHIFT 5 - /* Bits within a descriptor passed to the helper_mte_check* functions. */ FIELD(MTEDESC, MIDX, 0, 4) FIELD(MTEDESC, TBI, 4, 2) FIELD(MTEDESC, TCMA, 6, 2) FIELD(MTEDESC, WRITE, 8, 1) FIELD(MTEDESC, ALIGN, 9, 3) -FIELD(MTEDESC, SIZEM1, 12, SIMD_DATA_BITS - SVE_MTEDESC_SHIFT - 12) /* size - 1 */ +FIELD(MTEDESC, SIZEM1, 12, 32 - 12) /* size - 1 */ bool mte_probe(CPUARMState *env, uint32_t desc, uint64_t ptr); uint64_t mte_check(CPUARMState *env, uint32_t desc, uint64_t ptr, uintptr_t ra); diff --git a/target/arm/tcg/sme_helper.c b/target/arm/tcg/sme_helper.c index 0b55f13f8c..075360d8b8 100644 --- a/target/arm/tcg/sme_helper.c +++ b/target/arm/tcg/sme_helper.c @@ -666,19 +666,16 @@ void sme_ld1(CPUARMState *env, void *za, uint64_t *vg, static inline QEMU_ALWAYS_INLINE void sme_ld1_mte(CPUARMState *env, void *za, uint64_t *vg, - target_ulong addr, uint32_t desc, uintptr_t ra, + target_ulong addr, uint64_t desc, uintptr_t ra, const int esz, bool vertical, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn, ClearFn *clr_fn, CopyFn *cpy_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; int bit55 = extract64(addr, 55, 1); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Perform gross MTE suppression early. */ if (!tbi_check(mtedesc, bit55) || tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { @@ -854,16 +851,13 @@ void sme_st1(CPUARMState *env, void *za, uint64_t *vg, static inline QEMU_ALWAYS_INLINE void sme_st1_mte(CPUARMState *env, void *za, uint64_t *vg, target_ulong addr, - uint32_t desc, uintptr_t ra, int esz, bool vertical, + uint64_t desc, uintptr_t ra, int esz, bool vertical, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; int bit55 = extract64(addr, 55, 1); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Perform gross MTE suppression early. */ if (!tbi_check(mtedesc, bit55) || tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index 9fc2c05879..d0fb4138d2 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -6362,17 +6362,14 @@ void sve_ldN_r(CPUARMState *env, uint64_t *vg, const target_ulong addr, static inline QEMU_ALWAYS_INLINE void sve_ldN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr, - uint32_t desc, const uintptr_t ra, + uint64_t desc, const uintptr_t ra, const int esz, const int msz, const int N, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; int bit55 = extract64(addr, 55, 1); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Perform gross MTE suppression early. */ if (!tbi_check(mtedesc, bit55) || tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { @@ -6727,17 +6724,14 @@ void sve_ldnfff1_r(CPUARMState *env, void *vg, const target_ulong addr, static inline QEMU_ALWAYS_INLINE void sve_ldnfff1_r_mte(CPUARMState *env, void *vg, target_ulong addr, - uint32_t desc, const uintptr_t retaddr, + uint64_t desc, const uintptr_t retaddr, const int esz, const int msz, const SVEContFault fault, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; int bit55 = extract64(addr, 55, 1); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Perform gross MTE suppression early. */ if (!tbi_check(mtedesc, bit55) || tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { @@ -6985,17 +6979,14 @@ void sve_stN_r(CPUARMState *env, uint64_t *vg, target_ulong addr, static inline QEMU_ALWAYS_INLINE void sve_stN_r_mte(CPUARMState *env, uint64_t *vg, target_ulong addr, - uint32_t desc, const uintptr_t ra, + uint64_t desc, const uintptr_t ra, const int esz, const int msz, const int N, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; int bit55 = extract64(addr, 55, 1); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Perform gross MTE suppression early. */ if (!tbi_check(mtedesc, bit55) || tcma_check(mtedesc, bit55, allocation_tag_from_addr(addr))) { @@ -7183,14 +7174,12 @@ void sve_ld1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, static inline QEMU_ALWAYS_INLINE void sve_ld1_z_mte(CPUARMState *env, void *vd, uint64_t *vg, void *vm, - target_ulong base, uint32_t desc, uintptr_t retaddr, + target_ulong base, uint64_t desc, uintptr_t retaddr, int esize, int msize, zreg_off_fn *off_fn, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; /* * ??? TODO: For the 32-bit offset extractions, base + ofs cannot @@ -7395,15 +7384,13 @@ void sve_ldff1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, static inline QEMU_ALWAYS_INLINE void sve_ldff1_z_mte(CPUARMState *env, void *vd, uint64_t *vg, void *vm, - target_ulong base, uint32_t desc, uintptr_t retaddr, + target_ulong base, uint64_t desc, uintptr_t retaddr, const int esz, const int msz, zreg_off_fn *off_fn, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; /* * ??? TODO: For the 32-bit offset extractions, base + ofs cannot @@ -7600,14 +7587,12 @@ void sve_st1_z(CPUARMState *env, void *vd, uint64_t *vg, void *vm, static inline QEMU_ALWAYS_INLINE void sve_st1_z_mte(CPUARMState *env, void *vd, uint64_t *vg, void *vm, - target_ulong base, uint32_t desc, uintptr_t retaddr, + target_ulong base, uint64_t desc, uintptr_t retaddr, int esize, int msize, zreg_off_fn *off_fn, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); - /* Remove mtedesc from the normal sve descriptor. */ - desc = extract32(desc, 0, SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); + uint32_t mtedesc = desc >> 32; /* * ??? TODO: For the 32-bit offset extractions, base + ofs cannot @@ -7853,14 +7838,15 @@ static void sve2p1_cont_ldst_mte_check(SVEContLdSt *info, CPUARMState *env, static inline QEMU_ALWAYS_INLINE void sve2p1_ld1_c(CPUARMState *env, ARMVectorReg *zd, const vaddr addr, - uint32_t png, uint32_t desc, + uint32_t png, uint64_t desc64, const uintptr_t ra, const MemOp esz, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { + uint32_t mtedesc = desc64 >> 32; + uint32_t desc = desc64; const unsigned N = (desc >> SIMD_DATA_SHIFT) & 1 ? 4 : 2; const unsigned rstride = 1 << ((desc >> (SIMD_DATA_SHIFT + 1)) % 4); - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); const intptr_t reg_max = simd_oprsz(desc); const unsigned esize = 1 << esz; intptr_t count_off, count_last; @@ -8025,14 +8011,15 @@ DO_LD1_2(ld1dd, MO_64) static inline QEMU_ALWAYS_INLINE void sve2p1_st1_c(CPUARMState *env, ARMVectorReg *zd, const vaddr addr, - uint32_t png, uint32_t desc, + uint32_t png, uint64_t desc64, const uintptr_t ra, const int esz, sve_ldst1_host_fn *host_fn, sve_ldst1_tlb_fn *tlb_fn) { + uint32_t mtedesc = desc64 >> 32; + uint32_t desc = desc64; const unsigned N = (desc >> SIMD_DATA_SHIFT) & 1 ? 4 : 2; const unsigned rstride = 1 << ((desc >> (SIMD_DATA_SHIFT + 1)) % 4); - uint32_t mtedesc = desc >> (SIMD_DATA_SHIFT + SVE_MTEDESC_SHIFT); const intptr_t reg_max = simd_oprsz(desc); const unsigned esize = 1 << esz; intptr_t count_off, count_last; diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 849151826e..5cba7b87bd 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -4893,7 +4893,6 @@ uint64_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, assert(nregs >= 1 && nregs <= 4); sizem1 = (nregs << msz) - 1; assert(sizem1 <= R_MTEDESC_SIZEM1_MASK >> R_MTEDESC_SIZEM1_SHIFT); - assert(data < 1u << SVE_MTEDESC_SHIFT); if (s->mte_active[0]) { desc = FIELD_DP32(desc, MTEDESC, MIDX, get_mem_index(s)); @@ -4901,9 +4900,9 @@ uint64_t make_svemte_desc(DisasContext *s, unsigned vsz, uint32_t nregs, desc = FIELD_DP32(desc, MTEDESC, TCMA, s->tcma); desc = FIELD_DP32(desc, MTEDESC, WRITE, is_write); desc = FIELD_DP32(desc, MTEDESC, SIZEM1, sizem1); - desc <<= SVE_MTEDESC_SHIFT; + desc <<= 32; } - return simd_desc(vsz, vsz, desc | data); + return simd_desc(vsz, vsz, data) | desc; } static void do_mem_zpa(DisasContext *s, int zt, int pg, TCGv_i64 addr, From b79f944e09657f63b6dd6e78ac7966fdc7a3e6d1 Mon Sep 17 00:00:00 2001 From: Richard Henderson Date: Wed, 23 Jul 2025 17:54:55 +0100 Subject: [PATCH 2620/2760] decodetree: Infer argument set before inferring format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Failure to confirm an argument set first may result in the selection of a format which leaves extra arguments to be filled in by the pattern. Signed-off-by: Richard Henderson Signed-off-by: Peter Maydell Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Tested-by: Peter Maydell Message-id: 20250723165458.3509150-4-peter.maydell@linaro.org Message-id: 20250722183343.273533-1-richard.henderson@linaro.org Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Tested-by: Peter Maydell Signed-off-by: Peter Maydell --- scripts/decodetree.py | 7 ++++--- tests/decode/meson.build | 1 + tests/decode/succ_infer1.decode | 4 ++++ 3 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 tests/decode/succ_infer1.decode diff --git a/scripts/decodetree.py b/scripts/decodetree.py index e8b72da3a9..f992472b73 100644 --- a/scripts/decodetree.py +++ b/scripts/decodetree.py @@ -1016,9 +1016,12 @@ def infer_format(arg, fieldmask, flds, width): else: var_flds[n] = c + if not arg: + arg = infer_argument_set(flds) + # Look for an existing format with the same argument set and fields for fmt in formats.values(): - if arg and fmt.base != arg: + if fmt.base != arg: continue if fieldmask != fmt.fieldmask: continue @@ -1029,8 +1032,6 @@ def infer_format(arg, fieldmask, flds, width): return (fmt, const_flds) name = decode_function + '_Fmt_' + str(len(formats)) - if not arg: - arg = infer_argument_set(flds) fmt = Format(name, 0, arg, 0, 0, 0, fieldmask, var_flds, width) formats[name] = fmt diff --git a/tests/decode/meson.build b/tests/decode/meson.build index b13fada980..63405ca08f 100644 --- a/tests/decode/meson.build +++ b/tests/decode/meson.build @@ -41,6 +41,7 @@ succ_tests = [ 'succ_argset_type1.decode', 'succ_function.decode', 'succ_ident1.decode', + 'succ_infer1.decode', 'succ_named_field.decode', 'succ_pattern_group_nest1.decode', 'succ_pattern_group_nest2.decode', diff --git a/tests/decode/succ_infer1.decode b/tests/decode/succ_infer1.decode new file mode 100644 index 0000000000..6fa40bada5 --- /dev/null +++ b/tests/decode/succ_infer1.decode @@ -0,0 +1,4 @@ +&rprr_load rd pg rn rm dtype nreg +@rprr_load .... .... ... rm:5 ... pg:3 rn:5 rd:5 &rprr_load + +LD1Q 1100 0100 000 rm:5 101 pg:3 rn:5 rd:5 From 1c6aae5efbd28ac35003dea341364cd63a4515a1 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 23 Jul 2025 17:54:56 +0100 Subject: [PATCH 2621/2760] target/arm: LD1Q, ST1Q are vector + scalar, not scalar + vector Unlike the "LD1D (scalar + vector)" etc instructions, LD1Q is vector + scalar. This means that: * the vector and the scalar register are in opposite fields in the encoding * 31 in the scalar register field is XZR, not XSP The same applies for ST1Q. This means we can't reuse the trans_LD1_zprz() and trans_ST1_zprz() functions for LD1Q and ST1Q. Split them out to use their own trans functions. Note that the change made here to sve.decode requires the decodetree bugfix "decodetree: Infer argument set before inferring format" to avoid a spurious compile-time error about "dtype". Fixes: d2aa9a804ee678f ("target/arm: Implement LD1Q, ST1Q for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250723165458.3509150-5-peter.maydell@linaro.org --- target/arm/tcg/sve.decode | 12 +++---- target/arm/tcg/translate-sve.c | 65 ++++++++++++++++++++++++++-------- 2 files changed, 57 insertions(+), 20 deletions(-) diff --git a/target/arm/tcg/sve.decode b/target/arm/tcg/sve.decode index aea7f51973..ab63cfaa0f 100644 --- a/target/arm/tcg/sve.decode +++ b/target/arm/tcg/sve.decode @@ -1343,9 +1343,9 @@ LD1_zprz 1100010 10 1. ..... 1.. ... ..... ..... \ LD1_zprz 1100010 11 1. ..... 11. ... ..... ..... \ @rprr_g_load_sc esz=3 msz=3 u=1 -# LD1Q -LD1_zprz 1100 0100 000 rm:5 101 pg:3 rn:5 rd:5 \ - &rprr_gather_load u=1 ff=0 xs=2 esz=4 msz=4 scale=0 +# LD1Q. Note that this is subtly different from LD1_zprz because +# it is vector + scalar, not scalar + vector. +LD1Q 1100 0100 000 rm:5 101 pg:3 rn:5 rd:5 # SVE 64-bit gather load (vector plus immediate) LD1_zpiz 1100010 .. 01 ..... 1.. ... ..... ..... \ @@ -1450,9 +1450,9 @@ ST1_zprz 1110010 .. 01 ..... 101 ... ..... ..... \ ST1_zprz 1110010 .. 00 ..... 101 ... ..... ..... \ @rprr_scatter_store xs=2 esz=3 scale=0 -# ST1Q -ST1_zprz 1110 0100 001 rm:5 001 pg:3 rn:5 rd:5 \ - &rprr_scatter_store xs=2 msz=4 esz=4 scale=0 +# ST1Q. Note that this is subtly different from ST1_zprz because +# it is vector + scalar, not scalar + vector. +ST1Q 1110 0100 001 rm:5 001 pg:3 rn:5 rd:5 # SVE 64-bit scatter store (vector plus immediate) ST1_zpiz 1110010 .. 10 ..... 101 ... ..... ..... \ diff --git a/target/arm/tcg/translate-sve.c b/target/arm/tcg/translate-sve.c index 5cba7b87bd..07b827fa8e 100644 --- a/target/arm/tcg/translate-sve.c +++ b/target/arm/tcg/translate-sve.c @@ -6179,9 +6179,7 @@ static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a) bool be = s->be_data == MO_BE; bool mte = s->mte_active[0]; - if (a->esz < MO_128 - ? !dc_isar_feature(aa64_sve, s) - : !dc_isar_feature(aa64_sve2p1, s)) { + if (!dc_isar_feature(aa64_sve, s)) { return false; } s->is_nonstreaming = true; @@ -6196,10 +6194,6 @@ static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a) case MO_64: fn = gather_load_fn64[mte][be][a->ff][a->xs][a->u][a->msz]; break; - case MO_128: - assert(!a->ff && a->u && a->xs == 2 && a->msz == MO_128); - fn = gather_load_fn128[mte][be]; - break; default: g_assert_not_reached(); } @@ -6210,6 +6204,32 @@ static bool trans_LD1_zprz(DisasContext *s, arg_LD1_zprz *a) return true; } +static bool trans_LD1Q(DisasContext *s, arg_LD1Q *a) +{ + gen_helper_gvec_mem_scatter *fn = NULL; + bool be = s->be_data == MO_BE; + bool mte = s->mte_active[0]; + + if (!dc_isar_feature(aa64_sve2p1, s)) { + return false; + } + s->is_nonstreaming = true; + if (!sve_access_check(s)) { + return true; + } + + fn = gather_load_fn128[mte][be]; + assert(fn != NULL); + + /* + * Unlike LD1_zprz, a->rm is the scalar register and it can be XZR, not XSP. + * a->rn is the vector register. + */ + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, + cpu_reg(s, a->rm), MO_128, false, fn); + return true; +} + static bool trans_LD1_zpiz(DisasContext *s, arg_LD1_zpiz *a) { gen_helper_gvec_mem_scatter *fn = NULL; @@ -6386,9 +6406,7 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a) if (a->esz < a->msz || (a->msz == 0 && a->scale)) { return false; } - if (a->esz < MO_128 - ? !dc_isar_feature(aa64_sve, s) - : !dc_isar_feature(aa64_sve2p1, s)) { + if (!dc_isar_feature(aa64_sve, s)) { return false; } s->is_nonstreaming = true; @@ -6402,10 +6420,6 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a) case MO_64: fn = scatter_store_fn64[mte][be][a->xs][a->msz]; break; - case MO_128: - assert(a->xs == 2 && a->msz == MO_128); - fn = scatter_store_fn128[mte][be]; - break; default: g_assert_not_reached(); } @@ -6414,6 +6428,29 @@ static bool trans_ST1_zprz(DisasContext *s, arg_ST1_zprz *a) return true; } +static bool trans_ST1Q(DisasContext *s, arg_ST1Q *a) +{ + gen_helper_gvec_mem_scatter *fn; + bool be = s->be_data == MO_BE; + bool mte = s->mte_active[0]; + + if (!dc_isar_feature(aa64_sve2p1, s)) { + return false; + } + s->is_nonstreaming = true; + if (!sve_access_check(s)) { + return true; + } + fn = scatter_store_fn128[mte][be]; + /* + * Unlike ST1_zprz, a->rm is the scalar register, and it + * can be XZR, not XSP. a->rn is the vector register. + */ + do_mem_zpz(s, a->rd, a->pg, a->rn, 0, + cpu_reg(s, a->rm), MO_128, true, fn); + return true; +} + static bool trans_ST1_zpiz(DisasContext *s, arg_ST1_zpiz *a) { gen_helper_gvec_mem_scatter *fn = NULL; From 4726be1c69606e34c3cc4c26e39e252a9856b3d3 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 23 Jul 2025 17:54:57 +0100 Subject: [PATCH 2622/2760] target/arm: Pass correct esize to sve_st1_z() for LD1Q, ST1Q Our implementation of the helper functions for the LD1Q and ST1Q insns reused the existing DO_LD1_ZPZ_D and DO_ST1_ZPZ_D macros. This passes the wrong esize (8, not 16) to sve_ldl_z(). Create new macros DO_LD1_ZPZ_Q and DO_ST1_ZPZ_Q which pass the correct esize, and use them for the LD1Q and ST1Q helpers. Fixes: d2aa9a804ee ("target/arm: Implement LD1Q, ST1Q for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250723165458.3509150-6-peter.maydell@linaro.org --- target/arm/tcg/sve_helper.c | 36 ++++++++++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index d0fb4138d2..c4aaf0cc45 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -7219,6 +7219,20 @@ void HELPER(sve_ld##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ } +#define DO_LD1_ZPZ_Q(MEM, OFS, MSZ) \ +void HELPER(sve_ld##MEM##_##OFS)(CPUARMState *env, void *vd, void *vg, \ + void *vm, target_ulong base, uint64_t desc) \ +{ \ + sve_ld1_z(env, vd, vg, vm, base, desc, GETPC(), 0, 16, 1 << MSZ, \ + off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ +} \ +void HELPER(sve_ld##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ + void *vm, target_ulong base, uint64_t desc) \ +{ \ + sve_ld1_z_mte(env, vd, vg, vm, base, desc, GETPC(), 16, 1 << MSZ, \ + off_##OFS##_d, sve_ld1##MEM##_host, sve_ld1##MEM##_tlb); \ +} + DO_LD1_ZPZ_S(bsu, zsu, MO_8) DO_LD1_ZPZ_S(bsu, zss, MO_8) DO_LD1_ZPZ_D(bdu, zsu, MO_8) @@ -7283,8 +7297,8 @@ DO_LD1_ZPZ_D(dd_be, zsu, MO_64) DO_LD1_ZPZ_D(dd_be, zss, MO_64) DO_LD1_ZPZ_D(dd_be, zd, MO_64) -DO_LD1_ZPZ_D(qq_le, zd, MO_128) -DO_LD1_ZPZ_D(qq_be, zd, MO_128) +DO_LD1_ZPZ_Q(qq_le, zd, MO_128) +DO_LD1_ZPZ_Q(qq_be, zd, MO_128) #undef DO_LD1_ZPZ_S #undef DO_LD1_ZPZ_D @@ -7632,6 +7646,20 @@ void HELPER(sve_st##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ off_##OFS##_d, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ } +#define DO_ST1_ZPZ_Q(MEM, OFS, MSZ) \ +void HELPER(sve_st##MEM##_##OFS)(CPUARMState *env, void *vd, void *vg, \ + void *vm, target_ulong base, uint64_t desc) \ +{ \ + sve_st1_z(env, vd, vg, vm, base, desc, GETPC(), 0, 16, 1 << MSZ, \ + off_##OFS##_d, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ +} \ +void HELPER(sve_st##MEM##_##OFS##_mte)(CPUARMState *env, void *vd, void *vg, \ + void *vm, target_ulong base, uint64_t desc) \ +{ \ + sve_st1_z_mte(env, vd, vg, vm, base, desc, GETPC(), 16, 1 << MSZ, \ + off_##OFS##_d, sve_st1##MEM##_host, sve_st1##MEM##_tlb); \ +} + DO_ST1_ZPZ_S(bs, zsu, MO_8) DO_ST1_ZPZ_S(hs_le, zsu, MO_16) DO_ST1_ZPZ_S(hs_be, zsu, MO_16) @@ -7668,8 +7696,8 @@ DO_ST1_ZPZ_D(sd_be, zd, MO_32) DO_ST1_ZPZ_D(dd_le, zd, MO_64) DO_ST1_ZPZ_D(dd_be, zd, MO_64) -DO_ST1_ZPZ_D(qq_le, zd, MO_128) -DO_ST1_ZPZ_D(qq_be, zd, MO_128) +DO_ST1_ZPZ_Q(qq_le, zd, MO_128) +DO_ST1_ZPZ_Q(qq_be, zd, MO_128) #undef DO_ST1_ZPZ_S #undef DO_ST1_ZPZ_D From 7428c46c06b4365ee5131dcdcc3da218c5e99ddd Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Wed, 23 Jul 2025 17:54:58 +0100 Subject: [PATCH 2623/2760] target/arm: Fix LD1W, LD1D to 128-bit elements In our implementation of the SVE2p1 contiguous load to 128-bit element insns such as LD1D (scalar plus scalar, single register), we got the order of the arguments to the DO_LD1_2() macro wrong. Here the first argument is the element size and the second is the memory size, and the element size is always the same size or larger than the memory size. For the 128-bit versions, we want to load either 32-bit or 64-bit values from memory and extend them to the 128-bit vector element, but were trying to load 128 bit values and then stuff them into 32-bit or 64-bit vector elements. Correct the macro ordering. Fixes: fc5f060bcb7b ("target/arm: Implement {LD1, ST1}{W, D} (128-bit element) for SVE2p1") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Message-id: 20250723165458.3509150-7-peter.maydell@linaro.org --- target/arm/tcg/sve_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/arm/tcg/sve_helper.c b/target/arm/tcg/sve_helper.c index c4aaf0cc45..c442fcb540 100644 --- a/target/arm/tcg/sve_helper.c +++ b/target/arm/tcg/sve_helper.c @@ -6439,8 +6439,8 @@ DO_LD1_2(ld1sds, MO_64, MO_32) DO_LD1_2(ld1dd, MO_64, MO_64) -DO_LD1_2(ld1squ, MO_32, MO_128) -DO_LD1_2(ld1dqu, MO_64, MO_128) +DO_LD1_2(ld1squ, MO_128, MO_32) +DO_LD1_2(ld1dqu, MO_128, MO_64) #undef DO_LD1_1 #undef DO_LD1_2 From bd52d8bc9e01dcf68731dcdd9d2b8ebcb9fc5692 Mon Sep 17 00:00:00 2001 From: JianChunfu Date: Fri, 14 Feb 2025 15:20:29 +0800 Subject: [PATCH 2624/2760] hw/arm/smmu-common: Avoid using inlined functions with external linkage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similarly to commit 9de9fa5c ("hw/arm/smmu-common: Avoid using inlined functions with external linkage"): None of our code base require / use inlined functions with external linkage. Some places use internal inlining in the hot path. These two functions are certainly not in any hot path and don't justify any inlining, so these are likely oversights rather than intentional. Fixes: b8fa4c23 (hw/arm/smmu: Support nesting in the rest of commands) Signed-off-by: JianChunfu Reviewed-by: Richard Henderson Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Peter Maydell --- hw/arm/smmu-common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/arm/smmu-common.c b/hw/arm/smmu-common.c index f39b99e526..0dcaf2f589 100644 --- a/hw/arm/smmu-common.c +++ b/hw/arm/smmu-common.c @@ -319,7 +319,7 @@ void smmu_iotlb_inv_vmid(SMMUState *s, int vmid) g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid, &vmid); } -inline void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid) +void smmu_iotlb_inv_vmid_s1(SMMUState *s, int vmid) { trace_smmu_iotlb_inv_vmid_s1(vmid); g_hash_table_foreach_remove(s->iotlb, smmu_hash_remove_by_vmid_s1, &vmid); From a7aa2af13e287e11cb2d73972353bfec161803a4 Mon Sep 17 00:00:00 2001 From: Mohamed Mediouni Date: Mon, 21 Jul 2025 17:29:02 +0200 Subject: [PATCH 2625/2760] target/arm: hvf: stubbing reads to LORC_EL1 Linux zeroes LORC_EL1 on boot at EL2, without further interaction with FEAT_LOR afterwards. Stub out LORC_EL1 accesses as FEAT_LOR is a mandatory extension on Armv8.1+. Signed-off-by: Mohamed Mediouni Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/hvf/hvf.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/target/arm/hvf/hvf.c b/target/arm/hvf/hvf.c index bd6b5d11de..47b0cd3a35 100644 --- a/target/arm/hvf/hvf.c +++ b/target/arm/hvf/hvf.c @@ -186,6 +186,7 @@ void hvf_arm_init_debug(void) #define SYSREG_OSLAR_EL1 SYSREG(2, 0, 1, 0, 4) #define SYSREG_OSLSR_EL1 SYSREG(2, 0, 1, 1, 4) #define SYSREG_OSDLR_EL1 SYSREG(2, 0, 1, 3, 4) +#define SYSREG_LORC_EL1 SYSREG(3, 0, 10, 4, 3) #define SYSREG_CNTPCT_EL0 SYSREG(3, 3, 14, 0, 1) #define SYSREG_CNTP_CTL_EL0 SYSREG(3, 3, 14, 2, 1) #define SYSREG_PMCR_EL0 SYSREG(3, 3, 9, 12, 0) @@ -1657,6 +1658,9 @@ static int hvf_sysreg_write(CPUState *cpu, uint32_t reg, uint64_t val) case SYSREG_OSDLR_EL1: /* Dummy register */ return 0; + case SYSREG_LORC_EL1: + /* Dummy register */ + return 0; case SYSREG_ICC_AP0R0_EL1: case SYSREG_ICC_AP0R1_EL1: case SYSREG_ICC_AP0R2_EL1: From fbc8fb36e3636854195705cba13278befb94158d Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 22 Jul 2025 09:50:01 +0200 Subject: [PATCH 2626/2760] scripts: add script to help distros use global Rust packages Some distros prefer to avoid vendored crate sources, and instead use local sources from e.g. ``/usr/share/cargo/registry``. Add a script, inspired by the Mesa spec file(*), that automatically performs this task. The script is meant to be invoked after unpacking the QEMU tarball. (*) This is the hack that Mesa uses: export MESON_PACKAGE_CACHE_DIR="%{cargo_registry}/" %define inst_crate_nameversion() %(basename %{cargo_registry}/%{1}-*) %define rewrite_wrap_file() sed -e "/source.*/d" -e "s/%{1}-.*/%{inst_crate_nameversion %{1}}/" -i subprojects/%{1}.wrap %rewrite_wrap_file proc-macro2 ... more %rewrite_wrap_file invocations follow ... Reviewed-by: Neal Gompa Signed-off-by: Paolo Bonzini --- MAINTAINERS | 1 + docs/about/build-platforms.rst | 8 + scripts/get-wraps-from-cargo-registry.py | 190 +++++++++++++++++++++++ 3 files changed, 199 insertions(+) create mode 100755 scripts/get-wraps-from-cargo-registry.py diff --git a/MAINTAINERS b/MAINTAINERS index a462345618..1604f3dfc1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3511,6 +3511,7 @@ S: Maintained F: rust/qemu-api F: rust/qemu-api-macros F: rust/rustfmt.toml +F: scripts/get-wraps-from-cargo-registry.py Rust-related patches CC here L: qemu-rust@nongnu.org diff --git a/docs/about/build-platforms.rst b/docs/about/build-platforms.rst index 8ecbd6b26f..8671c3be9c 100644 --- a/docs/about/build-platforms.rst +++ b/docs/about/build-platforms.rst @@ -127,6 +127,14 @@ Rust build dependencies (or newer) package. The path to ``rustc`` and ``rustdoc`` must be provided manually to the configure script. + Some distros prefer to avoid vendored crate sources, and instead use + local sources from e.g. ``/usr/share/cargo/registry``. QEMU includes a + script, ``scripts/get-wraps-from-cargo-registry.py``, that automatically + performs this task. The script is meant to be invoked after unpacking + the QEMU tarball. QEMU also includes ``rust/Cargo.toml`` and + ``rust/Cargo.lock`` files that can be used to compute QEMU's build + dependencies, e.g. using ``cargo2rpm -p rust/Cargo.toml buildrequires``. + Optional build dependencies Build components whose absence does not affect the ability to build QEMU may not be available in distros, or may be too old for our requirements. diff --git a/scripts/get-wraps-from-cargo-registry.py b/scripts/get-wraps-from-cargo-registry.py new file mode 100755 index 0000000000..31eed5c2dd --- /dev/null +++ b/scripts/get-wraps-from-cargo-registry.py @@ -0,0 +1,190 @@ +#!/usr/bin/env python3 + +# SPDX-License-Identifier: GPL-2.0-or-later + +""" +get-wraps-from-cargo-registry.py - Update Meson subprojects from a global registry +""" + +# Copyright (C) 2025 Red Hat, Inc. +# +# Author: Paolo Bonzini + +import argparse +import configparser +import filecmp +import glob +import os +import subprocess +import sys + + +def get_name_and_semver(namever: str) -> tuple[str, str]: + """Split a subproject name into its name and semantic version parts""" + parts = namever.rsplit("-", 1) + if len(parts) != 2: + return namever, "" + + return parts[0], parts[1] + + +class UpdateSubprojects: + cargo_registry: str + top_srcdir: str + dry_run: bool + changes: int = 0 + + def find_installed_crate(self, namever: str) -> str | None: + """Find installed crate matching name and semver prefix""" + name, semver = get_name_and_semver(namever) + + # exact version match + path = os.path.join(self.cargo_registry, f"{name}-{semver}") + if os.path.exists(path): + return f"{name}-{semver}" + + # semver match + matches = sorted(glob.glob(f"{path}.*")) + return os.path.basename(matches[0]) if matches else None + + def compare_build_rs(self, orig_dir: str, registry_namever: str) -> None: + """Warn if the build.rs in the original directory differs from the registry version.""" + orig_build_rs = os.path.join(orig_dir, "build.rs") + new_build_rs = os.path.join(self.cargo_registry, registry_namever, "build.rs") + + msg = None + if os.path.isfile(orig_build_rs) != os.path.isfile(new_build_rs): + if os.path.isfile(orig_build_rs): + msg = f"build.rs removed in {registry_namever}" + if os.path.isfile(new_build_rs): + msg = f"build.rs added in {registry_namever}" + + elif os.path.isfile(orig_build_rs) and not filecmp.cmp(orig_build_rs, new_build_rs): + msg = f"build.rs changed from {orig_dir} to {registry_namever}" + + if msg: + print(f"⚠️ Warning: {msg}") + print(" This may affect the build process - please review the differences.") + + def update_subproject(self, wrap_file: str, registry_namever: str) -> None: + """Modify [wrap-file] section to point to self.cargo_registry.""" + assert wrap_file.endswith("-rs.wrap") + wrap_name = wrap_file[:-5] + + env = os.environ.copy() + env["MESON_PACKAGE_CACHE_DIR"] = self.cargo_registry + + config = configparser.ConfigParser() + config.read(wrap_file) + if "wrap-file" not in config: + return + + # do not download the wrap, always use the local copy + orig_dir = config["wrap-file"]["directory"] + if os.path.exists(orig_dir) and orig_dir != registry_namever: + self.compare_build_rs(orig_dir, registry_namever) + + if self.dry_run: + if orig_dir == registry_namever: + print(f"Will install {orig_dir} from registry.") + else: + print(f"Will replace {orig_dir} with {registry_namever}.") + self.changes += 1 + return + + config["wrap-file"]["directory"] = registry_namever + for key in list(config["wrap-file"].keys()): + if key.startswith("source"): + del config["wrap-file"][key] + + # replace existing directory with installed version + if os.path.exists(orig_dir): + subprocess.run( + ["meson", "subprojects", "purge", "--confirm", wrap_name], + cwd=self.top_srcdir, + env=env, + check=True, + ) + + with open(wrap_file, "w") as f: + config.write(f) + + if orig_dir == registry_namever: + print(f"Installing {orig_dir} from registry.") + else: + print(f"Replacing {orig_dir} with {registry_namever}.") + patch_dir = config["wrap-file"]["patch_directory"] + patch_dir = os.path.join("packagefiles", patch_dir) + _, ver = registry_namever.rsplit("-", 1) + subprocess.run( + ["meson", "rewrite", "kwargs", "set", "project", "/", "version", ver], + cwd=patch_dir, + env=env, + check=True, + ) + + subprocess.run( + ["meson", "subprojects", "download", wrap_name], + cwd=self.top_srcdir, + env=env, + check=True, + ) + self.changes += 1 + + @staticmethod + def parse_cmdline() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Replace Meson subprojects with packages in a Cargo registry" + ) + parser.add_argument( + "--cargo-registry", + default=os.environ.get("CARGO_REGISTRY"), + help="Path to Cargo registry (default: CARGO_REGISTRY env var)", + ) + parser.add_argument( + "--dry-run", + action="store_true", + default=False, + help="Do not actually replace anything", + ) + + args = parser.parse_args() + if not args.cargo_registry: + print("error: CARGO_REGISTRY environment variable not set and --cargo-registry not provided") + sys.exit(1) + + return args + + def __init__(self, args: argparse.Namespace): + self.cargo_registry = args.cargo_registry + self.dry_run = args.dry_run + self.top_srcdir = os.getcwd() + + def main(self) -> None: + if not os.path.exists("subprojects"): + print("'subprojects' directory not found, nothing to do.") + return + + os.chdir("subprojects") + for wrap_file in sorted(glob.glob("*-rs.wrap")): + namever = wrap_file[:-8] # Remove '-rs.wrap' + + registry_namever = self.find_installed_crate(namever) + if not registry_namever: + print(f"No installed crate found for {wrap_file}") + continue + + self.update_subproject(wrap_file, registry_namever) + + if self.changes: + if self.dry_run: + print("Rerun without --dry-run to apply changes.") + else: + print(f"✨ {self.changes} subproject(s) updated!") + else: + print("No changes.") + + +if __name__ == "__main__": + args = UpdateSubprojects.parse_cmdline() + UpdateSubprojects(args).main() From feea87cd6b645d5166bdd304aac88f47f63dc2ef Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Fri, 25 Jul 2025 01:10:12 +0200 Subject: [PATCH 2627/2760] target/i386: fix width of third operand of VINSERTx128 Table A-5 of the Intel manual incorrectly lists the third operand of VINSERTx128 as Wqq, but it is actually a 128-bit value. This is visible when W is a memory operand close to the end of the page. Fixes the recently-added poly1305_kunit test in linux-next. (No testcase yet, but I plan to modify test-avx2 to use memory close to the end of the page. This would work because the test vectors correctly have the memory operand as xmm2/m128). Reported-by: Eric Biggers Tested-by: Eric Biggers Cc: Ard Biesheuvel Cc: "Jason A. Donenfeld" Cc: Guenter Roeck Cc: qemu-stable@nongnu.org Fixes: 79068477686 ("target/i386: reimplement 0x0f 0x3a, add AVX", 2022-10-18) Signed-off-by: Paolo Bonzini --- target/i386/tcg/decode-new.c.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/i386/tcg/decode-new.c.inc b/target/i386/tcg/decode-new.c.inc index 853b1c8bf9..51038657f0 100644 --- a/target/i386/tcg/decode-new.c.inc +++ b/target/i386/tcg/decode-new.c.inc @@ -878,10 +878,10 @@ static const X86OpEntry opcodes_0F3A[256] = { [0x0e] = X86_OP_ENTRY4(VPBLENDW, V,x, H,x, W,x, vex4 cpuid(SSE41) avx2_256 p_66), [0x0f] = X86_OP_ENTRY4(PALIGNR, V,x, H,x, W,x, vex4 cpuid(SSSE3) mmx avx2_256 p_00_66), - [0x18] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX) p_66), + [0x18] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,dq, vex6 chk(W0) cpuid(AVX) p_66), [0x19] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 chk(W0) cpuid(AVX) p_66), - [0x38] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,qq, vex6 chk(W0) cpuid(AVX2) p_66), + [0x38] = X86_OP_ENTRY4(VINSERTx128, V,qq, H,qq, W,dq, vex6 chk(W0) cpuid(AVX2) p_66), [0x39] = X86_OP_ENTRY3(VEXTRACTx128, W,dq, V,qq, I,b, vex6 chk(W0) cpuid(AVX2) p_66), /* Listed incorrectly as type 4 */ From 3cdd990aa920ec8f2994b634f758dab4a86ac167 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 25 Jul 2025 18:55:08 +0100 Subject: [PATCH 2628/2760] linux-user/aarch64: Clear TPIDR2_EL0 when delivering signals A recent change to the kernel (Linux commit b376108e1f88 "arm64/fpsimd: signal: Clear TPIDR2 when delivering signals") updated the signal-handler entry code to always clear TPIDR2_EL0. This is necessary for the userspace ZA lazy saving scheme to work correctly when unwinding exceptions across a signal boundary. (For the essay-length description of the incorrect behaviour and why this is the correct fix, see the commit message for the kernel commit.) Make QEMU also clear TPIDR2_EL0 on signal entry, applying the equivalent bugfix to our implementation. Note that getting this unwinding to work correctly also requires changes to the userspace code, e.g. as implemented in gcc in https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=b5ffc8e75a8 This change is technically an ABI change; from the kernel's point of view SME was never enabled (it was hidden behind CONFIG_BROKEN) before the change. From QEMU's point of view our SME-related signal handling was broken anyway as we weren't saving and restoring TPIDR2_EL0. Cc: qemu-stable@nongnu.org Fixes: 78011586b90d1 ("target/arm: Enable SME for user-only") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250725175510.3864231-2-peter.maydell@linaro.org> --- linux-user/aarch64/signal.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index d50cab78d8..6514b73ad9 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -666,8 +666,12 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, env->btype = 2; } - /* Invoke the signal handler with both SM and ZA disabled. */ + /* + * Invoke the signal handler with a clean SME state: both SM and ZA + * disabled and TPIDR2_EL0 cleared. + */ aarch64_set_svcr(env, 0, R_SVCR_SM_MASK | R_SVCR_ZA_MASK); + env->cp15.tpidr2_el0 = 0; if (info) { frame->info = *info; From 99870aff907b1c863cd32558b543f0ab0d0e74ba Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 25 Jul 2025 18:55:09 +0100 Subject: [PATCH 2629/2760] linux-user/aarch64: Support TPIDR2_MAGIC signal frame record FEAT_SME adds the TPIDR2 userspace-accessible system register, which is used as part of the procedure calling standard's lazy saving scheme for the ZA registers: https://github.com/ARM-software/abi-aa/blob/main/aapcs64/aapcs64.rst#66the-za-lazy-saving-scheme The Linux kernel has a signal frame record for saving and restoring this value when calling signal handlers, but we forgot to implement this. The result is that code which tries to unwind an exception out of a signal handler will not work correctly. Add support for the missing record. Cc: qemu-stable@nongnu.org Fixes: 78011586b90d1 ("target/arm: Enable SME for user-only") Signed-off-by: Peter Maydell Reviewed-by: Richard Henderson Reviewed-by: Pierrick Bouvier Signed-off-by: Richard Henderson Message-ID: <20250725175510.3864231-3-peter.maydell@linaro.org> --- linux-user/aarch64/signal.c | 42 +++++++++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index 6514b73ad9..f28ba80754 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -121,6 +121,13 @@ struct target_za_context { #define TARGET_ZA_SIG_CONTEXT_SIZE(VQ) \ TARGET_ZA_SIG_ZAV_OFFSET(VQ, VQ * TARGET_SVE_VQ_BYTES) +#define TARGET_TPIDR2_MAGIC 0x54504902 + +struct target_tpidr2_context { + struct target_aarch64_ctx head; + uint64_t tpidr2; +}; + struct target_rt_sigframe { struct target_siginfo info; struct target_ucontext uc; @@ -253,6 +260,14 @@ static void target_setup_za_record(struct target_za_context *za, } } +static void target_setup_tpidr2_record(struct target_tpidr2_context *tpidr2, + CPUARMState *env) +{ + __put_user(TARGET_TPIDR2_MAGIC, &tpidr2->head.magic); + __put_user(sizeof(struct target_tpidr2_context), &tpidr2->head.size); + __put_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2); +} + static void target_restore_general_frame(CPUARMState *env, struct target_rt_sigframe *sf) { @@ -403,6 +418,12 @@ static bool target_restore_za_record(CPUARMState *env, return true; } +static void target_restore_tpidr2_record(CPUARMState *env, + struct target_tpidr2_context *tpidr2) +{ + __get_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2); +} + static int target_restore_sigframe(CPUARMState *env, struct target_rt_sigframe *sf) { @@ -410,6 +431,7 @@ static int target_restore_sigframe(CPUARMState *env, struct target_fpsimd_context *fpsimd = NULL; struct target_sve_context *sve = NULL; struct target_za_context *za = NULL; + struct target_tpidr2_context *tpidr2 = NULL; uint64_t extra_datap = 0; bool used_extra = false; int sve_size = 0; @@ -460,6 +482,14 @@ static int target_restore_sigframe(CPUARMState *env, za_size = size; break; + case TARGET_TPIDR2_MAGIC: + if (tpidr2 || size != sizeof(struct target_tpidr2_context) || + !cpu_isar_feature(aa64_sme, env_archcpu(env))) { + goto err; + } + tpidr2 = (struct target_tpidr2_context *)ctx; + break; + case TARGET_EXTRA_MAGIC: if (extra || size != sizeof(struct target_extra_context)) { goto err; @@ -497,6 +527,9 @@ static int target_restore_sigframe(CPUARMState *env, if (za && !target_restore_za_record(env, za, za_size, &svcr)) { goto err; } + if (tpidr2) { + target_restore_tpidr2_record(env, tpidr2); + } if (env->svcr != svcr) { env->svcr = svcr; arm_rebuild_hflags(env); @@ -568,8 +601,8 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, .total_size = offsetof(struct target_rt_sigframe, uc.tuc_mcontext.__reserved), }; - int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0; - int sve_size = 0, za_size = 0; + int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0, tpidr2_ofs = 0; + int sve_size = 0, za_size = 0, tpidr2_size = 0; struct target_rt_sigframe *frame; struct target_rt_frame_record *fr; abi_ulong frame_addr, return_addr; @@ -585,6 +618,8 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, sve_ofs = alloc_sigframe_space(sve_size, &layout); } if (cpu_isar_feature(aa64_sme, env_archcpu(env))) { + tpidr2_size = sizeof(struct target_tpidr2_context); + tpidr2_ofs = alloc_sigframe_space(tpidr2_size, &layout); /* ZA state needs saving only if it is enabled. */ if (FIELD_EX64(env->svcr, SVCR, ZA)) { za_size = TARGET_ZA_SIG_CONTEXT_SIZE(sme_vq(env)); @@ -644,6 +679,9 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, if (za_ofs) { target_setup_za_record((void *)frame + za_ofs, env, za_size); } + if (tpidr2_ofs) { + target_setup_tpidr2_record((void *)frame + tpidr2_ofs, env); + } /* Set up the stack frame for unwinding. */ fr = (void *)frame + fr_ofs; From e35215db401113867e20634622a370c0e8931797 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Fri, 25 Jul 2025 18:55:10 +0100 Subject: [PATCH 2630/2760] linux-user/aarch64: Support ZT_MAGIC signal frame record FEAT_SME2 adds the ZT0 register, whose contents may need to be preserved and restored on signal handler entry and exit. This is done with a new ZT_MAGIC record. We forgot to implement support for this in our linux-user code before enabling the SME2p1 emulation, which meant that a signal handler using SME would corrupt the ZT0 register value, and code that attempted to unwind an exception from inside a signal handler would not work. Add the missing record handling. Fixes: 7b1613a1020d2942 ("target/arm: Enable FEAT_SME2p1 on -cpu max") Signed-off-by: Peter Maydell Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Signed-off-by: Richard Henderson Message-ID: <20250725175510.3864231-4-peter.maydell@linaro.org> --- linux-user/aarch64/signal.c | 93 ++++++++++++++++++++++++++++++++++++- 1 file changed, 92 insertions(+), 1 deletion(-) diff --git a/linux-user/aarch64/signal.c b/linux-user/aarch64/signal.c index f28ba80754..668353bbda 100644 --- a/linux-user/aarch64/signal.c +++ b/linux-user/aarch64/signal.c @@ -128,6 +128,23 @@ struct target_tpidr2_context { uint64_t tpidr2; }; +#define TARGET_ZT_MAGIC 0x5a544e01 + +struct target_zt_context { + struct target_aarch64_ctx head; + uint16_t nregs; + uint16_t reserved[3]; + /* ZTn register data immediately follows */ +}; + +#define TARGET_ZT_SIG_REG_BYTES (512 / 8) +#define TARGET_ZT_SIG_REGS_SIZE(n) (TARGET_ZT_SIG_REG_BYTES * (n)) +#define TARGET_ZT_SIG_CONTEXT_SIZE(n) (sizeof(struct target_zt_context) + \ + TARGET_ZT_SIG_REGS_SIZE(n)) +#define TARGET_ZT_SIG_REGS_OFFSET sizeof(struct target_zt_context) +QEMU_BUILD_BUG_ON(TARGET_ZT_SIG_REG_BYTES != \ + sizeof_field(CPUARMState, za_state.zt0)); + struct target_rt_sigframe { struct target_siginfo info; struct target_ucontext uc; @@ -268,6 +285,28 @@ static void target_setup_tpidr2_record(struct target_tpidr2_context *tpidr2, __put_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2); } +static void target_setup_zt_record(struct target_zt_context *zt, + CPUARMState *env, int size) +{ + uint64_t *z; + + memset(zt, 0, sizeof(*zt)); + __put_user(TARGET_ZT_MAGIC, &zt->head.magic); + __put_user(size, &zt->head.size); + /* + * The record format allows for multiple ZT regs, but + * currently there is only one, ZT0. + */ + __put_user(1, &zt->nregs); + assert(size == TARGET_ZT_SIG_CONTEXT_SIZE(1)); + + /* ZT0 is the same byte-stream format as SVE regs and ZA */ + z = (void *)zt + TARGET_ZT_SIG_REGS_OFFSET; + for (int i = 0; i < ARRAY_SIZE(env->za_state.zt0); i++) { + __put_user_e(env->za_state.zt0[i], z + i, le); + } +} + static void target_restore_general_frame(CPUARMState *env, struct target_rt_sigframe *sf) { @@ -424,6 +463,30 @@ static void target_restore_tpidr2_record(CPUARMState *env, __get_user(env->cp15.tpidr2_el0, &tpidr2->tpidr2); } +static bool target_restore_zt_record(CPUARMState *env, + struct target_zt_context *zt, int size, + int svcr) +{ + uint16_t nregs; + uint64_t *z; + + if (!(FIELD_EX64(svcr, SVCR, ZA))) { + return false; + } + + __get_user(nregs, &zt->nregs); + + if (nregs != 1) { + return false; + } + + z = (void *)zt + TARGET_ZT_SIG_REGS_OFFSET; + for (int i = 0; i < ARRAY_SIZE(env->za_state.zt0); i++) { + __get_user_e(env->za_state.zt0[i], z + i, le); + } + return true; +} + static int target_restore_sigframe(CPUARMState *env, struct target_rt_sigframe *sf) { @@ -432,10 +495,12 @@ static int target_restore_sigframe(CPUARMState *env, struct target_sve_context *sve = NULL; struct target_za_context *za = NULL; struct target_tpidr2_context *tpidr2 = NULL; + struct target_zt_context *zt = NULL; uint64_t extra_datap = 0; bool used_extra = false; int sve_size = 0; int za_size = 0; + int zt_size = 0; int svcr = 0; target_restore_general_frame(env, sf); @@ -490,6 +555,15 @@ static int target_restore_sigframe(CPUARMState *env, tpidr2 = (struct target_tpidr2_context *)ctx; break; + case TARGET_ZT_MAGIC: + if (zt || size != TARGET_ZT_SIG_CONTEXT_SIZE(1) || + !cpu_isar_feature(aa64_sme2, env_archcpu(env))) { + goto err; + } + zt = (struct target_zt_context *)ctx; + zt_size = size; + break; + case TARGET_EXTRA_MAGIC: if (extra || size != sizeof(struct target_extra_context)) { goto err; @@ -530,6 +604,13 @@ static int target_restore_sigframe(CPUARMState *env, if (tpidr2) { target_restore_tpidr2_record(env, tpidr2); } + /* + * NB that we must restore ZT after ZA so the check that there's + * no ZT record if SVCR.ZA is 0 gets the right value of SVCR. + */ + if (zt && !target_restore_zt_record(env, zt, zt_size, svcr)) { + goto err; + } if (env->svcr != svcr) { env->svcr = svcr; arm_rebuild_hflags(env); @@ -602,7 +683,8 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, uc.tuc_mcontext.__reserved), }; int fpsimd_ofs, fr_ofs, sve_ofs = 0, za_ofs = 0, tpidr2_ofs = 0; - int sve_size = 0, za_size = 0, tpidr2_size = 0; + int zt_ofs = 0; + int sve_size = 0, za_size = 0, tpidr2_size = 0, zt_size = 0; struct target_rt_sigframe *frame; struct target_rt_frame_record *fr; abi_ulong frame_addr, return_addr; @@ -628,6 +710,12 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, } za_ofs = alloc_sigframe_space(za_size, &layout); } + if (cpu_isar_feature(aa64_sme2, env_archcpu(env)) && + FIELD_EX64(env->svcr, SVCR, ZA)) { + /* If SME ZA storage is enabled, we must also save SME2 ZT0 */ + zt_size = TARGET_ZT_SIG_CONTEXT_SIZE(1); + zt_ofs = alloc_sigframe_space(zt_size, &layout); + } if (layout.extra_ofs) { /* Reserve space for the extra end marker. The standard end marker @@ -682,6 +770,9 @@ static void target_setup_frame(int usig, struct target_sigaction *ka, if (tpidr2_ofs) { target_setup_tpidr2_record((void *)frame + tpidr2_ofs, env); } + if (zt_ofs) { + target_setup_zt_record((void *)frame + zt_ofs, env, zt_size); + } /* Set up the stack frame for unwinding. */ fr = (void *)frame + fr_ofs; From 8d6c7de1cc71207ccc047583df0c84363a5da16b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:04 +0100 Subject: [PATCH 2631/2760] docs/user: clarify user-mode expects the same OS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While we somewhat cover this later when we talk about supported operating systems make it clear in the front matter. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-2-alex.bennee@linaro.org> --- docs/user/index.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/user/index.rst b/docs/user/index.rst index 782d27cda2..2307580cb9 100644 --- a/docs/user/index.rst +++ b/docs/user/index.rst @@ -5,8 +5,9 @@ User Mode Emulation ------------------- This section of the manual is the overall guide for users using QEMU -for user-mode emulation. In this mode, QEMU can launch -processes compiled for one CPU on another CPU. +for user-mode emulation. In this mode, QEMU can launch programs +compiled for one CPU architecture on the same Operating System (OS) +but running on a different CPU architecture. .. toctree:: :maxdepth: 2 From 9b6656668f01144288e1190980e1689394cc236c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:05 +0100 Subject: [PATCH 2632/2760] docs/system: reword the TAP notes to remove tarball ref MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't ship the tarball and users should generally look to the distribution specific packaging. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/560 Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-3-alex.bennee@linaro.org> --- docs/system/devices/net.rst | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/system/devices/net.rst b/docs/system/devices/net.rst index 4d787c3aeb..7d76fe88c4 100644 --- a/docs/system/devices/net.rst +++ b/docs/system/devices/net.rst @@ -21,11 +21,17 @@ configure it as if it was a real ethernet card. Linux host ^^^^^^^^^^ -As an example, you can download the ``linux-test-xxx.tar.gz`` archive -and copy the script ``qemu-ifup`` in ``/etc`` and configure properly -``sudo`` so that the command ``ifconfig`` contained in ``qemu-ifup`` can -be executed as root. You must verify that your host kernel supports the -TAP network interfaces: the device ``/dev/net/tun`` must be present. +A distribution will generally provide specific helper scripts when it +packages QEMU. By default these are found at ``/etc/qemu-ifup`` and +``/etc/qemu-ifdown`` and are called appropriately when QEMU wants to +change the network state. + +If QEMU is being run as a non-privileged user you may need properly +configure ``sudo`` so that network commands in the scripts can be +executed as root. + +You must verify that your host kernel supports the TAP network +interfaces: the device ``/dev/net/tun`` must be present. See :ref:`sec_005finvocation` to have examples of command lines using the TAP network interfaces. From 1ab41da6bb17b8c8abaabe5c9dbf65d0667cd9ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:06 +0100 Subject: [PATCH 2633/2760] docs/user: clean up headings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This was a slightly duff format for rst, make it use proper headings. Reviewed-by: Manos Pitsidianakis Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-4-alex.bennee@linaro.org> --- docs/user/main.rst | 50 ++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 22 deletions(-) diff --git a/docs/user/main.rst b/docs/user/main.rst index 9a1c60448c..b8ff203c21 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -17,28 +17,34 @@ Features QEMU user space emulation has the following notable features: -**System call translation:** - QEMU includes a generic system call translator. This means that the - parameters of the system calls can be converted to fix endianness and - 32/64-bit mismatches between hosts and targets. IOCTLs can be - converted too. - -**POSIX signal handling:** - QEMU can redirect to the running program all signals coming from the - host (such as ``SIGALRM``), as well as synthesize signals from - virtual CPU exceptions (for example ``SIGFPE`` when the program - executes a division by zero). - - QEMU relies on the host kernel to emulate most signal system calls, - for example to emulate the signal mask. On Linux, QEMU supports both - normal and real-time signals. - -**Threading:** - On Linux, QEMU can emulate the ``clone`` syscall and create a real - host thread (with a separate virtual CPU) for each emulated thread. - Note that not all targets currently emulate atomic operations - correctly. x86 and Arm use a global lock in order to preserve their - semantics. +System call translation +~~~~~~~~~~~~~~~~~~~~~~~ + +QEMU includes a generic system call translator. This means that the +parameters of the system calls can be converted to fix endianness +and 32/64-bit mismatches between hosts and targets. IOCTLs can be +converted too. + +POSIX signal handling +~~~~~~~~~~~~~~~~~~~~~ + +QEMU can redirect to the running program all signals coming from the +host (such as ``SIGALRM``), as well as synthesize signals from +virtual CPU exceptions (for example ``SIGFPE`` when the program +executes a division by zero). + +QEMU relies on the host kernel to emulate most signal system calls, +for example to emulate the signal mask. On Linux, QEMU supports both +normal and real-time signals. + +Threading +~~~~~~~~~ + +On Linux, QEMU can emulate the ``clone`` syscall and create a real +host thread (with a separate virtual CPU) for each emulated thread. +Note that not all targets currently emulate atomic operations +correctly. x86 and Arm use a global lock in order to preserve their +semantics. QEMU was conceived so that ultimately it can emulate itself. Although it is not very useful, it is an important test to show the power of the From bd0eb9b0cdb69ce8eafa85258a564596eeb165b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:07 +0100 Subject: [PATCH 2634/2760] docs/user: slightly reword section on system calls MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Expand the description slightly and quote ioctl(). I did ponder mentioning something about why DRM ioctls are often missing but I see we have the I915 ones so I guess its just no one has done them. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-5-alex.bennee@linaro.org> --- docs/user/main.rst | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/user/main.rst b/docs/user/main.rst index b8ff203c21..05de904225 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -20,10 +20,14 @@ QEMU user space emulation has the following notable features: System call translation ~~~~~~~~~~~~~~~~~~~~~~~ -QEMU includes a generic system call translator. This means that the -parameters of the system calls can be converted to fix endianness -and 32/64-bit mismatches between hosts and targets. IOCTLs can be -converted too. +System calls are the principle interface between user-space and the +kernel. Generally the same system calls exist on all versions of the +kernel so QEMU includes a generic system call translator. The +translator takes care of adjusting endianess, 32/64 bit parameter size +and then calling the equivalent host system call. + +QEMU can also adjust device specific ``ioctl()`` calls in a similar +fashion. POSIX signal handling ~~~~~~~~~~~~~~~~~~~~~ From f1f25eed03308f9f85770e0b6b911b6caf83c268 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:08 +0100 Subject: [PATCH 2635/2760] docs/user: expand section on threading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Potentially too many weasel words when describing atomic and memory order issues. Reviewed-by: Richard Henderson Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-6-alex.bennee@linaro.org> --- docs/user/main.rst | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/user/main.rst b/docs/user/main.rst index 05de904225..347bdfabf8 100644 --- a/docs/user/main.rst +++ b/docs/user/main.rst @@ -46,9 +46,15 @@ Threading On Linux, QEMU can emulate the ``clone`` syscall and create a real host thread (with a separate virtual CPU) for each emulated thread. -Note that not all targets currently emulate atomic operations -correctly. x86 and Arm use a global lock in order to preserve their -semantics. +However as QEMU relies on the system libc to call ``clone`` on its +behalf we limit the flags accepted to those it uses. Specifically this +means flags affecting namespaces (e.g. container runtimes) are not +supported. QEMU user-mode processes can still be run inside containers +though. + +While QEMU does its best to emulate atomic operations properly +differences between the host and guest memory models can cause issues +for software that makes assumptions about the memory model. QEMU was conceived so that ultimately it can emulate itself. Although it is not very useful, it is an important test to show the power of the From ebbc04adbb079066f8d180b8744c1c01c6de23f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:09 +0100 Subject: [PATCH 2636/2760] tests/functional: add hypervisor test for aarch64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is a simple test case that runs an image with kvmtool and kvm-unit-tests which can validate virtualisation works. This is useful for exercising TCG but can also be applied to any nested virt setup which is why it doesn't specify an accelerator. Tested-by: Philippe Mathieu-Daudé Tested-by: Manos Pitsidianakis Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-7-alex.bennee@linaro.org> --- tests/functional/meson.build | 1 + tests/functional/test_aarch64_kvm.py | 71 ++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100755 tests/functional/test_aarch64_kvm.py diff --git a/tests/functional/meson.build b/tests/functional/meson.build index 8bebcd4d94..ecf965adc6 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -89,6 +89,7 @@ tests_aarch64_system_thorough = [ 'aarch64_device_passthrough', 'aarch64_hotplug_pci', 'aarch64_imx8mp_evk', + 'aarch64_kvm', 'aarch64_raspi3', 'aarch64_raspi4', 'aarch64_replay', diff --git a/tests/functional/test_aarch64_kvm.py b/tests/functional/test_aarch64_kvm.py new file mode 100755 index 0000000000..9fb9286139 --- /dev/null +++ b/tests/functional/test_aarch64_kvm.py @@ -0,0 +1,71 @@ +#!/usr/bin/env python3 +# +# Functional test that runs subsets of kvm-unit-tests on Aarch64. +# These can run on TCG and any accelerator supporting nested +# virtualisation. +# +# Copyright (c) 2025 Linaro +# +# Author: +# Alex Bennée +# +# SPDX-License-Identifier: GPL-2.0-or-later + +from qemu_test import Asset +from qemu_test import exec_command_and_wait_for_pattern as ec_and_wait +from qemu_test.linuxkernel import LinuxKernelTest + + +class Aarch64VirtKVMTests(LinuxKernelTest): + + ASSET_KVM_TEST_KERNEL = Asset( + 'https://fileserver.linaro.org/s/HmjaxXXYHYSqbes/' + 'download?path=%2F&files=' + 'image-with-kvm-tool-and-unit-tests.gz', + '34de4aaea90db5da42729e7d28b77f392c37a2f4da859f889a5234aaf0970696') + + # make it easier to detect successful return to shell + PS1 = 'RES=[$?] # ' + OK_CMD = 'RES=[0] # ' + + # base of tests + KUT_BASE = "/usr/share/kvm-unit-tests/" + + def _launch_guest(self, kvm_mode="nvhe"): + + self.set_machine('virt') + kernel_path = self.ASSET_KVM_TEST_KERNEL.fetch() + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + f"console=ttyAMA0 kvm-arm.mode={kvm_mode}") + + self.vm.add_args("-cpu", "cortex-a72") + self.vm.add_args("-machine", "virt,gic-version=3,virtualization=on", + '-kernel', kernel_path, + '-append', kernel_command_line) + self.vm.add_args("-smp", "2", "-m", "320") + + self.vm.launch() + + self.wait_for_console_pattern('buildroot login:') + ec_and_wait(self, 'root', '#') + ec_and_wait(self, f"export PS1='{self.PS1}'", self.OK_CMD) + + # this is just a smoketest, we don't run all the tests in the image + def _smoketest_kvm(self): + ec_and_wait(self, f"{self.KUT_BASE}/selftest-setup", self.OK_CMD) + ec_and_wait(self, f"{self.KUT_BASE}/selftest-smp", self.OK_CMD) + ec_and_wait(self, f"{self.KUT_BASE}/selftest-vectors-kernel", self.OK_CMD) + ec_and_wait(self, f"{self.KUT_BASE}/selftest-vectors-user", self.OK_CMD) + + def test_aarch64_nvhe_selftest(self): + self._launch_guest("nvhe") + self._smoketest_kvm() + + def test_aarch64_vhe_selftest(self): + self._launch_guest("vhe") + self._smoketest_kvm() + +if __name__ == '__main__': + LinuxKernelTest.main() From 78029e9283f69140d377f589b76e369971dbe0e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:10 +0100 Subject: [PATCH 2637/2760] tests/tcg: skip libsyscall.so on softmmu tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It isn't testing anything and just expanding the runtime of testing. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-8-alex.bennee@linaro.org> --- tests/tcg/Makefile.target | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index af68f11664..3d96182a7b 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -155,6 +155,12 @@ VPATH+=$(PLUGIN_LIB) # For example, libpatch.so only needs to run against the arch-specific patch # target test, so we explicitly run it in the arch-specific Makefile. DISABLE_PLUGINS=libpatch.so + +# Likewise don't bother with the syscall plugin for softmmu +ifneq ($(filter %-softmmu, $(TARGET)),) +DISABLE_PLUGINS += libsyscall.so +endif + PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ $(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c)))) From a80e2c26f1cbac07d923415fecbf274ce4fa2d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:11 +0100 Subject: [PATCH 2638/2760] tests/tcg: remove ADDITIONAL_PLUGINS_TESTS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We never actually used this is the end. Remove it to enable re-factoring. Fixes: 7cefff22d54 (tests/tcg: add mechanism to run specific tests with plugins) Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-9-alex.bennee@linaro.org> --- tests/tcg/Makefile.target | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 3d96182a7b..97ebe8f9bc 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -169,11 +169,10 @@ PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ # only expand MULTIARCH_TESTS which are common on most of our targets # to avoid an exponential explosion as new tests are added. We also # add some special helpers the run-plugin- rules can use below. -# In more, extra tests can be added using ADDITIONAL_PLUGINS_TESTS variable. ifneq ($(MULTIARCH_TESTS),) $(foreach p,$(PLUGINS), \ - $(foreach t,$(MULTIARCH_TESTS) $(ADDITIONAL_PLUGINS_TESTS),\ + $(foreach t,$(MULTIARCH_TESTS),\ $(eval run-plugin-$(t)-with-$(p): $t $p) \ $(eval RUN_TESTS+=run-plugin-$(t)-with-$(p)))) endif # MULTIARCH_TESTS From ab8bf8f6e42e321dfb313cfdf95200135e9f7f8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:12 +0100 Subject: [PATCH 2639/2760] tests/tcg: don't include multiarch tests if not supported MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We are about to change the way the plugin runs are done and having this included by default will complicate things. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-10-alex.bennee@linaro.org> --- tests/tcg/Makefile.target | 6 ++++++ tests/tcg/multiarch/system/Makefile.softmmu-target | 5 +++++ 2 files changed, 11 insertions(+) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 97ebe8f9bc..a12b15637e 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -127,8 +127,14 @@ else # build options for bare programs are usually pretty different. They # are expected to provide their own build recipes. EXTRA_CFLAGS += -ffreestanding -fno-stack-protector + +# We skip the multiarch tests if the target hasn't provided a boot.S +MULTIARCH_SOFTMMU_TARGETS = i386 alpha aarch64 arm loongarch64 s390x x86_64 + +ifneq ($(filter $(TARGET_NAME),$(MULTIARCH_SOFTMMU_TARGETS)),) -include $(SRC_PATH)/tests/tcg/minilib/Makefile.target -include $(SRC_PATH)/tests/tcg/multiarch/system/Makefile.softmmu-target +endif -include $(SRC_PATH)/tests/tcg/$(TARGET_NAME)/Makefile.softmmu-target endif diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target index 07be001102..5acf270081 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -6,6 +6,11 @@ # architecture to add to the test dependencies and deal with the # complications of building. # +# To support the multiarch guests the target arch needs to provide a +# boot.S that jumps to main and provides a __sys_outc functions. +# Remember to update MULTIARCH_SOFTMMU_TARGETS in the tcg test +# Makefile.target when this is done. +# MULTIARCH_SRC=$(SRC_PATH)/tests/tcg/multiarch MULTIARCH_SYSTEM_SRC=$(MULTIARCH_SRC)/system From d0aa5df7752deb4bbce34fcc625ce59fe36862be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:13 +0100 Subject: [PATCH 2640/2760] configure: expose PYTHON to test/tcg/config-host.mak MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This will be useful for making $shell calls to something more flexible than the shell builtins. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-11-alex.bennee@linaro.org> --- configure | 1 + 1 file changed, 1 insertion(+) diff --git a/configure b/configure index 95f67c1a82..825057ebf1 100755 --- a/configure +++ b/configure @@ -1800,6 +1800,7 @@ echo "SRC_PATH=$source_path" >> tests/tcg/$config_host_mak if test "$plugins" = "yes" ; then echo "CONFIG_PLUGIN=y" >> tests/tcg/$config_host_mak fi +echo "PYTHON=$python" >> tests/tcg/$config_host_mak tcg_tests_targets= for target in $target_list; do From 25aaf0cb7f13aa9e537fb6202099f142d9ffc58a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:14 +0100 Subject: [PATCH 2641/2760] tests/tcg: reduce the number of plugin tests combinations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As our set of multiarch tests has grown the practice of running every plugin with every test is becoming unsustainable. If we switch to ensuring every test gets run with at least one plugin we can speed things up. Some plugins do need to be run with specific tests (for example the memory instrumentation test). We can handle this by manually adding them to EXTRA_RUNS. We also need to wrap rules in a CONFIG_PLUGIN test so we don't enable the runs when plugins are not enabled. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-12-alex.bennee@linaro.org> --- tests/tcg/Makefile.target | 23 ++++++++++++++----- tests/tcg/multiarch/Makefile.target | 8 +++++-- .../multiarch/system/Makefile.softmmu-target | 11 +++++---- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index a12b15637e..18afd5be19 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -173,14 +173,25 @@ PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ # We need to ensure expand the run-plugin-TEST-with-PLUGIN # pre-requistes manually here as we can't use stems to handle it. We # only expand MULTIARCH_TESTS which are common on most of our targets -# to avoid an exponential explosion as new tests are added. We also -# add some special helpers the run-plugin- rules can use below. +# and rotate the plugins so we don't grow too out of control as new +# tests are added. Plugins that need to run with a specific test +# should ensure they add their combination to EXTRA_RUNS. ifneq ($(MULTIARCH_TESTS),) -$(foreach p,$(PLUGINS), \ - $(foreach t,$(MULTIARCH_TESTS),\ - $(eval run-plugin-$(t)-with-$(p): $t $p) \ - $(eval RUN_TESTS+=run-plugin-$(t)-with-$(p)))) + +NUM_PLUGINS := $(words $(PLUGINS)) +NUM_TESTS := $(words $(MULTIARCH_TESTS)) + +define mod_plus_one + $(shell $(PYTHON) -c "print( ($(1) % $(2)) + 1 )") +endef + +$(foreach _idx, $(shell seq 1 $(NUM_TESTS)), \ + $(eval _test := $(word $(_idx), $(MULTIARCH_TESTS))) \ + $(eval _plugin := $(word $(call mod_plus_one, $(_idx), $(NUM_PLUGINS)), $(PLUGINS))) \ + $(eval run-plugin-$(_test)-with-$(_plugin): $(_test) $(_plugin)) \ + $(eval RUN_TESTS+=run-plugin-$(_test)-with-$(_plugin))) + endif # MULTIARCH_TESTS endif # CONFIG_PLUGIN diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index bfdf7197a7..38345ff880 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -189,6 +189,10 @@ run-plugin-semiconsole-with-%: TESTS += semihosting semiconsole endif +test-plugin-mem-access: CFLAGS+=-pthread -O0 +test-plugin-mem-access: LDFLAGS+=-pthread -O0 + +ifeq ($(CONFIG_PLUGIN),y) # Test plugin memory access instrumentation run-plugin-test-plugin-mem-access-with-libmem.so: \ PLUGIN_ARGS=$(COMMA)print-accesses=true @@ -197,8 +201,8 @@ run-plugin-test-plugin-mem-access-with-libmem.so: \ $(SRC_PATH)/tests/tcg/multiarch/check-plugin-output.sh \ $(QEMU) $< -test-plugin-mem-access: CFLAGS+=-pthread -O0 -test-plugin-mem-access: LDFLAGS+=-pthread -O0 +EXTRA_RUNS += run-plugin-test-plugin-mem-access-with-libmem.so +endif # Update TESTS TESTS += $(MULTIARCH_TESTS) diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target index 5acf270081..4171b4e6aa 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -71,8 +71,11 @@ endif MULTIARCH_RUNS += run-gdbstub-memory run-gdbstub-interrupt \ run-gdbstub-untimely-packet run-gdbstub-registers +ifeq ($(CONFIG_PLUGIN),y) # Test plugin memory access instrumentation -run-plugin-memory-with-libmem.so: \ - PLUGIN_ARGS=$(COMMA)region-summary=true -run-plugin-memory-with-libmem.so: \ - CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py $@.out +run-plugin-memory-with-libmem.so: memory libmem.so +run-plugin-memory-with-libmem.so: PLUGIN_ARGS=$(COMMA)region-summary=true +run-plugin-memory-with-libmem.so: CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py $@.out + +EXTRA_RUNS += run-plugin-memory-with-libmem.so +endif From 408c8629105f32aa1d02d3004998ea453f69809b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:15 +0100 Subject: [PATCH 2642/2760] tests/docker: add --arch-only to qemu deps for all-test-cross MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If we want to build this container on non-x86 systems we might not have all the cross-compilers needed for the ROM blobs we don't actually build. Use --arch-only to avoid stalling on these missing bits. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-13-alex.bennee@linaro.org> --- tests/docker/dockerfiles/debian-all-test-cross.docker | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index 8ab244e018..5aa43749eb 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -15,7 +15,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ apt-get install -y eatmydata && \ eatmydata apt-get dist-upgrade -y && \ - apt build-dep -yy qemu + apt build-dep -yy --arch-only qemu # Add extra build tools and as many cross compilers as we can for testing RUN DEBIAN_FRONTEND=noninteractive eatmydata \ From 6da616bb17004f9332b2798353ebef88cac61cc2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:16 +0100 Subject: [PATCH 2643/2760] tests/docker: handle host-arch selection for all-test-cross MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When building on non-x86 we get a bunch but not all of the compilers. Handle this in the Dockerfile by probing the arch and expanding the list available. Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-14-alex.bennee@linaro.org> --- .../dockerfiles/debian-all-test-cross.docker | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index 5aa43749eb..ef69bbc8a5 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -23,7 +23,9 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ bison \ ccache \ clang \ + dpkg-dev \ flex \ + gcc \ git \ libclang-rt-dev \ ninja-build \ @@ -33,16 +35,11 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ python3-venv \ python3-wheel -RUN DEBIAN_FRONTEND=noninteractive eatmydata \ - apt install -y --no-install-recommends \ - gcc-aarch64-linux-gnu \ +# All the generally available compilers +ENV AVAILABLE_COMPILERS gcc-aarch64-linux-gnu \ libc6-dev-arm64-cross \ gcc-arm-linux-gnueabihf \ libc6-dev-armhf-cross \ - gcc-hppa-linux-gnu \ - libc6-dev-hppa-cross \ - gcc-m68k-linux-gnu \ - libc6-dev-m68k-cross \ gcc-mips-linux-gnu \ libc6-dev-mips-cross \ gcc-mips64-linux-gnuabi64 \ @@ -51,18 +48,25 @@ RUN DEBIAN_FRONTEND=noninteractive eatmydata \ libc6-dev-mips64el-cross \ gcc-mipsel-linux-gnu \ libc6-dev-mipsel-cross \ - gcc-powerpc-linux-gnu \ - libc6-dev-powerpc-cross \ - gcc-powerpc64-linux-gnu \ - libc6-dev-ppc64-cross \ gcc-powerpc64le-linux-gnu \ libc6-dev-ppc64el-cross \ gcc-riscv64-linux-gnu \ libc6-dev-riscv64-cross \ gcc-s390x-linux-gnu \ - libc6-dev-s390x-cross \ - gcc-sparc64-linux-gnu \ - libc6-dev-sparc64-cross && \ + libc6-dev-s390x-cross + +RUN if dpkg-architecture -e amd64; then \ + export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-hppa-linux-gnu libc6-dev-hppa-cross"; \ + export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-m68k-linux-gnu libc6-dev-m68k-cross"; \ + export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-powerpc-linux-gnu libc6-dev-powerpc-cross"; \ + export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross"; \ + export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-sparc64-linux-gnu libc6-dev-sparc64-cross"; \ + fi && \ + echo "compilers: ${AVAILABLE_COMPILERS}" + +RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + apt install -y --no-install-recommends \ + ${AVAILABLE_COMPILERS} && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt From cac08383f06dc378afb23913d65a95442bbd5516 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Fri, 25 Jul 2025 16:45:17 +0100 Subject: [PATCH 2644/2760] tests/functional: expose sys.argv to unittest.main MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With this we can call the supported we can take advantage of the argument the module supports: env PYTHONPATH=/home/alex/lsrc/qemu.git/python:/home/alex/lsrc/qemu.git/tests/functional ./pyvenv/bin/python /home/alex/lsrc/qemu.git/tests/functional/test_aarch64_kvm.py --help usage: test_aarch64_kvm.py [-h] [-v] [-q] [--locals] [--durations N] [-f] [-c] [-b] [-k TESTNAMEPATTERNS] [tests ...] positional arguments: tests a list of any number of test modules, classes and test methods. options: -h, --help show this help message and exit -v, --verbose Verbose output -q, --quiet Quiet output --locals Show local variables in tracebacks --durations N Show the N slowest test cases (N=0 for all) -f, --failfast Stop on first fail or error -c, --catch Catch Ctrl-C and display results so far -b, --buffer Buffer stdout and stderr during tests -k TESTNAMEPATTERNS Only run tests which match the given substring Examples: test_aarch64_kvm.py test_module - run tests from test_module test_aarch64_kvm.py module.TestClass - run tests from module.TestClass test_aarch64_kvm.py module.Class.test_method - run specified test method test_aarch64_kvm.py path/to/test_file.py - run tests from test_file.py usage: test_aarch64_kvm.py discover [-h] [-v] [-q] [--locals] [--durations N] [-f] [-c] [-b] [-k TESTNAMEPATTERNS] [-s START] [-p PATTERN] [-t TOP] options: -h, --help show this help message and exit -v, --verbose Verbose output -q, --quiet Quiet output --locals Show local variables in tracebacks --durations N Show the N slowest test cases (N=0 for all) -f, --failfast Stop on first fail or error -c, --catch Catch Ctrl-C and display results so far -b, --buffer Buffer stdout and stderr during tests -k TESTNAMEPATTERNS Only run tests which match the given substring -s, --start-directory START Directory to start discovery ('.' default) -p, --pattern PATTERN Pattern to match tests ('test*.py' default) -t, --top-level-directory TOP Top level directory of project (defaults to start directory) For test discovery all test modules must be importable from the top level directory of the project. Suggested-by: Daniel P. Berrangé Reviewed-by: Daniel P. Berrangé Signed-off-by: Alex Bennée Message-ID: <20250725154517.3523095-15-alex.bennee@linaro.org> --- tests/functional/qemu_test/testcase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/functional/qemu_test/testcase.py b/tests/functional/qemu_test/testcase.py index 2a78e735f1..5caf7b13fe 100644 --- a/tests/functional/qemu_test/testcase.py +++ b/tests/functional/qemu_test/testcase.py @@ -249,7 +249,7 @@ def main(): tr = pycotap.TAPTestRunner(message_log = pycotap.LogMode.LogToError, test_output_log = pycotap.LogMode.LogToError) res = unittest.main(module = None, testRunner = tr, exit = False, - argv=["__dummy__", path]) + argv=[sys.argv[0], path] + sys.argv[1:]) for (test, message) in res.result.errors + res.result.failures: if hasattr(test, "log_filename"): From bb743978f0aaf6aa7f7c796bae05bb9deb83b3f9 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 17 Jul 2025 13:52:43 +0200 Subject: [PATCH 2645/2760] qga: Fix guest-network-get-route return value documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Tagged sections are only recognized at the beginning of a paragraph. guest-network-get-route's Returns: isn't, and therefore gets rendered as ordinary text within its paragraph: Retrieve information about route of network. Returns: List of route info of guest. Since there is no (recognized) Returns: section, the doc generator adds Return: [GuestNetworkRoute] Note: only since recent commit 636c96cd77d (qapi: Fix undocumented return values by generating something). Insert the required blank line so that Returns: is recognized. Result: Retrieve information about route of network. Return: [GuestNetworkRoute] -- List of route info of guest. Fixes: commit 8e326d36dd16 (qga/linux: Add new api 'guest-network-get-route') Signed-off-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Message-ID: <20250717115246.3830007-2-armbru@redhat.com> Reviewed-by: John Snow --- qga/qapi-schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 6d770f7b8e..a569a14b55 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1966,6 +1966,7 @@ # @guest-network-get-route: # # Retrieve information about route of network. +# # Returns: List of route info of guest. # # Since: 9.1 From d27340ff8a1036f9d174e9389dd3510083ad276d Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 17 Jul 2025 13:52:44 +0200 Subject: [PATCH 2646/2760] qga: Remove trivial "Returns:" sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The QAPI doc generator recently started to auto-generate return documentation when there is no "Returns:" section (commit 636c96cd77d "qapi: Fix undocumented return values by generating something"). Remove "Returns:" sections where the auto-generated text is obviously no worse. For instance, guest-info's documentation changes from Return: GuestAgentInfo -- GuestAgentInfo to Return: GuestAgentInfo The auto-generated returns all are in the exact same spot. We did this for qapi/ in commit 0462da9d6b1 (qapi: remove trivial "Returns:" sections). Signed-off-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Message-ID: <20250717115246.3830007-3-armbru@redhat.com> Reviewed-by: John Snow --- qga/qapi-schema.json | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index a569a14b55..a9cc9150dc 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -202,8 +202,6 @@ # # Get some information about the guest agent. # -# Returns: @GuestAgentInfo -# # Since: 0.15.0 ## { 'command': 'guest-info', @@ -285,8 +283,6 @@ # @count: maximum number of bytes to read (default is 4KB, maximum is # 48MB) # -# Returns: @GuestFileRead -# # Since: 0.15.0 ## { 'command': 'guest-file-read', @@ -320,8 +316,6 @@ # @count: bytes to write (actual bytes, after base64-decode), default # is all content in buf-b64 buffer after base64 decoding # -# Returns: @GuestFileWrite -# # Since: 0.15.0 ## { 'command': 'guest-file-write', @@ -387,8 +381,6 @@ # # @whence: Symbolic or numeric code for interpreting offset # -# Returns: @GuestFileSeek -# # Since: 0.15.0 ## { 'command': 'guest-file-seek', @@ -428,9 +420,6 @@ # # Get guest fsfreeze state. # -# Returns: GuestFsfreezeStatus ("thawed", "frozen", etc., as defined -# below) -# # .. note:: This may fail to properly report the current state as a # result of some other guest processes having issued an fs # freeze/thaw. @@ -749,8 +738,6 @@ # # Get list of guest IP addresses, MAC addresses and netmasks. # -# Returns: List of GuestNetworkInterface -# # Since: 1.1 ## { 'command': 'guest-network-get-interfaces', @@ -1251,8 +1238,6 @@ # # Get information relating to guest memory blocks. # -# Returns: @GuestMemoryBlockInfo -# # Since: 2.3 ## { 'command': 'guest-get-memory-block-info', @@ -1298,8 +1283,6 @@ # # @pid: pid returned from guest-exec # -# Returns: GuestExecStatus -# # Since: 2.5 ## { 'command': 'guest-exec-status', @@ -1458,8 +1441,6 @@ # # Retrieves the timezone information from the guest. # -# Returns: A GuestTimezone dictionary. -# # Since: 2.10 ## { 'command': 'guest-get-timezone', @@ -1533,8 +1514,6 @@ # # Retrieve guest operating system information # -# Returns: @GuestOSInfo -# # Since: 2.10 ## { 'command': 'guest-get-osinfo', @@ -1604,8 +1583,6 @@ # # Retrieve information about device drivers in Windows guest # -# Returns: @GuestDeviceInfo -# # Since: 5.2 ## { 'command': 'guest-get-devices', @@ -1633,8 +1610,6 @@ # # @username: the user account to add the authorized keys # -# Returns: @GuestAuthorizedKeys -# # Since: 5.2 ## { 'command': 'guest-ssh-get-authorized-keys', From ef7e21964d54db7b7267c30578c3c6ec0f16ca08 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 17 Jul 2025 13:52:45 +0200 Subject: [PATCH 2647/2760] qga: Rephrase return docs to avoid type name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "Returns: " is rendered like Return: Mentioning the type in the description again is commonly redundant. There is just one such description. Rephrase it not to mention the type. We did this for qapi/ in commit f7296f8de5c (qapi: rephrase return docs to avoid type name). Signed-off-by: Markus Armbruster Reviewed-by: Daniel P. Berrangé Message-ID: <20250717115246.3830007-4-armbru@redhat.com> Reviewed-by: John Snow --- qga/qapi-schema.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index a9cc9150dc..6c26ace3c9 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -534,8 +534,7 @@ # discarded. The default value is zero, meaning "discard every # free block". # -# Returns: A @GuestFilesystemTrimResponse which contains the status of -# all trimmed paths. (since 2.4) +# Returns: status of all trimmed paths. (since 2.4) # # Since: 1.2 ## From 62e1fa22f5734d1325e3b75cdc59f02c16339330 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 17 Jul 2025 13:52:46 +0200 Subject: [PATCH 2648/2760] qga: Add cross-references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enclose command and type names in `backquotes`, so they become links in generated HTML. We did this for qapi/ in merge commit 504632dcc631. Signed-off-by: Markus Armbruster Message-ID: <20250717115246.3830007-5-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: John Snow --- qga/qapi-schema.json | 80 ++++++++++++++++++++++---------------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 6c26ace3c9..8162d888bb 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -96,11 +96,11 @@ # In cases where a partial stale response was previously received by # the client, this cannot always be done reliably. One particular # scenario being if qemu-ga responses are fed character-by-character -# into a JSON parser. In these situations, using guest-sync-delimited +# into a JSON parser. In these situations, using `guest-sync-delimited` # may be optimal. # # For clients that fetch responses line by line and convert them to -# JSON objects, guest-sync should be sufficient, but note that in +# JSON objects, `guest-sync` should be sufficient, but note that in # cases where the channel is dirty some attempts at parsing the # response may result in a parser error. # @@ -217,7 +217,7 @@ # # This command does NOT return a response on success. Success # condition is indicated by the VM exiting with a zero exit status or, -# when running with --no-shutdown, by issuing the query-status QMP +# when running with --no-shutdown, by issuing the `query-status` QMP # command to confirm the VM status is "shutdown". # # Since: 0.15.0 @@ -247,7 +247,7 @@ # # Close an open file in the guest # -# @handle: filehandle returned by guest-file-open +# @handle: filehandle returned by `guest-file-open` # # Since: 0.15.0 ## @@ -278,7 +278,7 @@ # As this command is just for limited, ad-hoc debugging, such as log # file access, the number of bytes to read is limited to 48 MB. # -# @handle: filehandle returned by guest-file-open +# @handle: filehandle returned by `guest-file-open` # # @count: maximum number of bytes to read (default is 4KB, maximum is # 48MB) @@ -309,7 +309,7 @@ # # Write to an open file in the guest. # -# @handle: filehandle returned by guest-file-open +# @handle: filehandle returned by `guest-file-open` # # @buf-b64: base64-encoded string representing data to be written # @@ -340,7 +340,7 @@ ## # @QGASeek: # -# Symbolic names for use in @guest-file-seek +# Symbolic names for use in `guest-file-seek` # # @set: Set to the specified offset (same effect as 'whence':0) # @@ -355,7 +355,7 @@ ## # @GuestFileWhence: # -# Controls the meaning of offset to @guest-file-seek. +# Controls the meaning of offset to `guest-file-seek`. # # @value: Integral value (0 for set, 1 for cur, 2 for end), available # for historical reasons, and might differ from the host's or @@ -375,7 +375,7 @@ # current file position afterward. Also encapsulates ftell()'s # functionality, with offset=0 and whence=1. # -# @handle: filehandle returned by guest-file-open +# @handle: filehandle returned by `guest-file-open` # # @offset: bytes to skip over in the file stream # @@ -393,7 +393,7 @@ # # Write file changes buffered in userspace to disk/kernel buffers # -# @handle: filehandle returned by guest-file-open +# @handle: filehandle returned by `guest-file-open` # # Since: 0.15.0 ## @@ -434,12 +434,12 @@ # @guest-fsfreeze-freeze: # # Sync and freeze all freezable, local guest filesystems. If this -# command succeeded, you may call @guest-fsfreeze-thaw later to +# command succeeded, you may call `guest-fsfreeze-thaw` later to # unfreeze. # # On error, all filesystems will be thawed. If no filesystems are -# frozen as a result of this call, then @guest-fsfreeze-status will -# remain "thawed" and calling @guest-fsfreeze-thaw is not necessary. +# frozen as a result of this call, then `guest-fsfreeze-status` will +# remain "thawed" and calling `guest-fsfreeze-thaw` is not necessary. # # Returns: Number of file systems currently frozen. # @@ -457,7 +457,7 @@ # @guest-fsfreeze-freeze-list: # # Sync and freeze specified guest filesystems. See also -# @guest-fsfreeze-freeze. +# `guest-fsfreeze-freeze`. # # On error, all filesystems will be thawed. # @@ -482,7 +482,7 @@ # Returns: Number of file systems thawed by this call # # .. note:: If the return value does not match the previous call to -# guest-fsfreeze-freeze, this likely means some freezable filesystems +# `guest-fsfreeze-freeze`, this likely means some freezable filesystems # were unfrozen before this call, and that the filesystem state may # have changed before issuing this command. # @@ -513,7 +513,7 @@ ## # @GuestFilesystemTrimResponse: # -# @paths: list of @GuestFilesystemTrimResult per path that was trimmed +# @paths: list of `GuestFilesystemTrimResult` per path that was trimmed # # Since: 2.4 ## @@ -557,7 +557,7 @@ # # This command does NOT return a response on success. There is a high # chance the command succeeded if the VM exits with a zero exit status -# or, when running with --no-shutdown, by issuing the query-status QMP +# or, when running with --no-shutdown, by issuing the `query-status` QMP # command to to confirm the VM status is "shutdown". However, the VM # could also exit (or set its status to "shutdown") due to other # reasons. @@ -565,7 +565,7 @@ # Errors: # - If suspend to disk is not supported, Unsupported # -# .. note:: It's strongly recommended to issue the guest-sync command +# .. note:: It's strongly recommended to issue the `guest-sync` command # before sending commands when the guest resumes. # # Since: 1.1 @@ -585,8 +585,8 @@ # - pm-utils (via pm-hibernate) # - manual write into sysfs # -# IMPORTANT: guest-suspend-ram requires working wakeup support in -# QEMU. You should check QMP command query-current-machine returns +# IMPORTANT: `guest-suspend-ram` requires working wakeup support in +# QEMU. You should check QMP command `query-current-machine` returns # wakeup-suspend-support: true before issuing this command. Failure # in doing so can result in a suspended guest that QEMU will not be # able to awaken, forcing the user to power cycle the guest to bring @@ -595,14 +595,14 @@ # This command does NOT return a response on success. There are two # options to check for success: # -# 1. Wait for the SUSPEND QMP event from QEMU -# 2. Issue the query-status QMP command to confirm the VM status is +# 1. Wait for the `SUSPEND` QMP event from QEMU +# 2. Issue the `query-status` QMP command to confirm the VM status is # "suspended" # # Errors: # - If suspend to ram is not supported, Unsupported # -# .. note:: It's strongly recommended to issue the guest-sync command +# .. note:: It's strongly recommended to issue the `guest-sync` command # before sending commands when the guest resumes. # # Since: 1.1 @@ -621,8 +621,8 @@ # - systemd hybrid-sleep # - pm-utils (via pm-suspend-hybrid) # -# IMPORTANT: guest-suspend-hybrid requires working wakeup support in -# QEMU. You should check QMP command query-current-machine returns +# IMPORTANT: `guest-suspend-hybrid` requires working wakeup support in +# QEMU. You should check QMP command `query-current-machine` returns # wakeup-suspend-support: true before issuing this command. Failure # in doing so can result in a suspended guest that QEMU will not be # able to awaken, forcing the user to power cycle the guest to bring @@ -631,14 +631,14 @@ # This command does NOT return a response on success. There are two # options to check for success: # -# 1. Wait for the SUSPEND QMP event from QEMU -# 2. Issue the query-status QMP command to confirm the VM status is +# 1. Wait for the `SUSPEND` QMP event from QEMU +# 2. Issue the `query-status` QMP command to confirm the VM status is # "suspended" # # Errors: # - If hybrid suspend is not supported, Unsupported # -# .. note:: It's strongly recommended to issue the guest-sync command +# .. note:: It's strongly recommended to issue the `guest-sync` command # before sending commands when the guest resumes. # # Since: 1.1 @@ -793,7 +793,7 @@ # There's no restriction on list length or on repeating the same # @logical-id (with possibly different @online field). Preferably # the input list should describe a modified subset of -# @guest-get-vcpus' return value. +# `guest-get-vcpus`' return value. # # Returns: The length of the initial sublist that has been # successfully processed. The guest agent maximizes this value. @@ -1069,7 +1069,7 @@ # # Returns: The list of filesystems information mounted in the guest. # The returned mountpoints may be specified to -# @guest-fsfreeze-freeze-list. Network filesystems (such as CIFS +# `guest-fsfreeze-freeze-list`. Network filesystems (such as CIFS # and NFS) are not listed. # # Since: 2.2 @@ -1171,7 +1171,7 @@ ## # @GuestMemoryBlockResponse: # -# @phys-index: same with the 'phys-index' member of @GuestMemoryBlock. +# @phys-index: same with the 'phys-index' member of `GuestMemoryBlock`. # # @response: the result of memory block operation. # @@ -1201,11 +1201,11 @@ # guest-supported identifiers. There's no restriction on list # length or on repeating the same @phys-index (with possibly # different @online field). Preferably the input list should -# describe a modified subset of @guest-get-memory-blocks' return +# describe a modified subset of `guest-get-memory-blocks`' return # value. # # Returns: The operation results, it is a list of -# @GuestMemoryBlockResponse, which is corresponding to the input +# `GuestMemoryBlockResponse`, which is corresponding to the input # list. # # Note: it will return an empty list if the @mem-blks list was @@ -1258,7 +1258,7 @@ # # @err-data: base64-encoded stderr of the process. Note: @out-data # and @err-data are present only if 'capture-output' was specified -# for 'guest-exec'. This field will only be populated after the +# for `guest-exec`. This field will only be populated after the # process exits. # # @out-truncated: true if stdout was not fully captured due to size @@ -1277,10 +1277,10 @@ # @guest-exec-status: # # Check status of process associated with PID retrieved via -# guest-exec. Reap the process and associated metadata if it has +# `guest-exec`. Reap the process and associated metadata if it has # exited. # -# @pid: pid returned from guest-exec +# @pid: pid returned from `guest-exec` # # Since: 2.5 ## @@ -1301,7 +1301,7 @@ ## # @GuestExecCaptureOutputMode: # -# An enumeration of guest-exec capture modes. +# An enumeration of `guest-exec` capture modes. # # @none: do not capture any output # @@ -1310,7 +1310,7 @@ # @stderr: only capture stderr # # @separated: capture both stdout and stderr, but separated into -# GuestExecStatus out-data and err-data, respectively +# `GuestExecStatus` out-data and err-data, respectively # # @merged: capture both stdout and stderr, but merge together into # out-data. Not effective on windows guests. @@ -1324,10 +1324,10 @@ ## # @GuestExecCaptureOutput: # -# Controls what guest-exec output gets captures. +# Controls what `guest-exec` output gets captures. # # @flag: captures both stdout and stderr if true. Equivalent to -# GuestExecCaptureOutputMode::all. (since 2.5) +# `GuestExecCaptureOutputMode`::all. (since 2.5) # # @mode: capture mode; preferred interface # From 1ebdd2d92611a573f9fcdf0fc7f31b1efd07ac30 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 23 Jul 2025 08:27:14 +0200 Subject: [PATCH 2649/2760] hw/i386: Fix 'use-legacy-x86-rom' property compatibility MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 350785d41d8b ("ramfb: Add property to control if load the romfile") introduced the `use-legacy-x86-rom` property for the `vfio-pci-nohotplug` device, allowing control over VGA BIOS ROM loading. However, the property compatibility setting was incorrectly applied to the `vfio-pci` device instead, which causes all `vfio-pci` devices to fail to load. This change fixes the issue by ensuring the property is set on the correct device. Fixes: d5fcf0d960d8 ("hw/i386: Add the ramfb romfile compatibility") Cc: Gerd Hoffmann Cc: Shaoqin Huang Reviewed-by: Zhao Liu Reviewed-by: Thomas Huth Acked-by: Michael S. Tsirkin Acked-by: Gerd Hoffmann Link: https://lore.kernel.org/qemu-devel/20250723062714.1245826-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/core/machine.c | 2 +- hw/i386/microvm.c | 2 +- hw/i386/pc_piix.c | 2 +- hw/i386/pc_q35.c | 2 +- hw/vfio/pci.c | 2 -- hw/vfio/types.h | 2 ++ 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hw/core/machine.c b/hw/core/machine.c index d6b2240fc2..bd47527479 100644 --- a/hw/core/machine.c +++ b/hw/core/machine.c @@ -41,7 +41,7 @@ GlobalProperty hw_compat_10_0[] = { { "scsi-hd", "dpofua", "off" }, { "vfio-pci", "x-migration-load-config-after-iter", "off" }, { "ramfb", "use-legacy-x86-rom", "true"}, - { "vfio-pci", "use-legacy-x86-rom", "true" }, + { "vfio-pci-nohotplug", "use-legacy-x86-rom", "true" }, }; const size_t hw_compat_10_0_len = G_N_ELEMENTS(hw_compat_10_0); diff --git a/hw/i386/microvm.c b/hw/i386/microvm.c index d90b69a162..94d22a232a 100644 --- a/hw/i386/microvm.c +++ b/hw/i386/microvm.c @@ -635,7 +635,7 @@ GlobalProperty microvm_properties[] = { */ { "pcie-root-port", "io-reserve", "0" }, { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, - { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" }, + { TYPE_VFIO_PCI_NOHOTPLUG, "use-legacy-x86-rom", "true" }, }; static void microvm_class_init(ObjectClass *oc, const void *data) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index ad5caff3a5..c03324281b 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -80,7 +80,7 @@ static const int ide_irq[MAX_IDE_BUS] = { 14, 15 }; static GlobalProperty pc_piix_compat_defaults[] = { { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, - { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" }, + { TYPE_VFIO_PCI_NOHOTPLUG, "use-legacy-x86-rom", "true" }, }; static const size_t pc_piix_compat_defaults_len = G_N_ELEMENTS(pc_piix_compat_defaults); diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index 9b9519fa02..b309b2b378 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -69,7 +69,7 @@ static GlobalProperty pc_q35_compat_defaults[] = { { TYPE_VIRTIO_IOMMU_PCI, "aw-bits", "39" }, { TYPE_RAMFB_DEVICE, "use-legacy-x86-rom", "true" }, - { TYPE_VFIO_PCI, "use-legacy-x86-rom", "true" }, + { TYPE_VFIO_PCI_NOHOTPLUG, "use-legacy-x86-rom", "true" }, }; static const size_t pc_q35_compat_defaults_len = G_N_ELEMENTS(pc_q35_compat_defaults); diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index e72d514a4c..0c4606d9cb 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -49,8 +49,6 @@ #include "vfio-migration-internal.h" #include "vfio-helpers.h" -#define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug" - /* Protected by BQL */ static KVMRouteChange vfio_route_change; diff --git a/hw/vfio/types.h b/hw/vfio/types.h index fa20c29b9f..c19334ff25 100644 --- a/hw/vfio/types.h +++ b/hw/vfio/types.h @@ -18,4 +18,6 @@ #define TYPE_VFIO_PCI "vfio-pci" /* TYPE_VFIO_PCI shares struct VFIOPCIDevice. */ +#define TYPE_VFIO_PCI_NOHOTPLUG "vfio-pci-nohotplug" + #endif /* HW_VFIO_VFIO_TYPES_H */ From 1dc1220fbd30a200aea972fc3aa53d439aff466b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Wed, 16 Jul 2025 09:15:54 +0200 Subject: [PATCH 2650/2760] i386: Build SEV only for 64-bit target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent changes broke build on 32-bit host. Since there is no 32-bit support, restrict SEV to 64-bit. Reviewed-by: Xiaoyao Li Reviewed-by: Daniel P. Berrangé Link: https://lore.kernel.org/qemu-devel/20250716071554.377356-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/i386/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 14d23e27b5..5139d23087 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -4,7 +4,7 @@ config X86_FW_OVMF config SEV bool select X86_FW_OVMF - depends on KVM + depends on KVM && X86_64 config SGX bool From 9751377c3a9445f597c5d0bf134a79c8cb58e45d Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Mon, 14 Jul 2025 12:21:30 -0700 Subject: [PATCH 2651/2760] vfio: fix sub-page bar after cpr MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Regions for sub-page BARs are normally mapped here, in response to the guest writing to PCI config space: vfio_pci_write_config() pci_default_write_config() pci_update_mappings() memory_region_add_subregion() vfio_sub_page_bar_update_mapping() ... vfio_dma_map() However, after CPR, the guest does not reconfigure the device and the code path above is not taken. To fix, in vfio_cpr_pci_post_load, call vfio_sub_page_bar_update_mapping for each sub-page BAR with a valid address. Fixes: 7e9f21411302 ("vfio/container: restore DMA vaddr") Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1752520890-223356-1-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 2 ++ hw/vfio/pci.c | 14 ++++++++++++++ hw/vfio/pci.h | 1 + 3 files changed, 17 insertions(+) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index af0f12a7ad..384b56c4c7 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -116,6 +116,8 @@ static int vfio_cpr_pci_post_load(void *opaque, int version_id) PCIDevice *pdev = &vdev->pdev; int nr_vectors; + vfio_sub_page_bar_update_mappings(vdev); + if (msix_enabled(pdev)) { vfio_pci_msix_set_notifiers(vdev); nr_vectors = vdev->msix->entries; diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 0c4606d9cb..f5bc5359eb 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -2824,6 +2824,20 @@ static int vfio_pci_load_config(VFIODevice *vbasedev, QEMUFile *f) return ret; } +void vfio_sub_page_bar_update_mappings(VFIOPCIDevice *vdev) +{ + PCIDevice *pdev = &vdev->pdev; + int page_size = qemu_real_host_page_size(); + int bar; + + for (bar = 0; bar < PCI_ROM_SLOT; bar++) { + PCIIORegion *r = &pdev->io_regions[bar]; + if (r->addr != PCI_BAR_UNMAPPED && r->size > 0 && r->size < page_size) { + vfio_sub_page_bar_update_mapping(pdev, bar); + } + } +} + static VFIODeviceOps vfio_pci_ops = { .vfio_compute_needs_reset = vfio_pci_compute_needs_reset, .vfio_hot_reset_multi = vfio_pci_hot_reset_multi, diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 248e5c4b16..bca289035a 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -219,6 +219,7 @@ void vfio_pci_write_config(PCIDevice *pdev, uint64_t vfio_vga_read(void *opaque, hwaddr addr, unsigned size); void vfio_vga_write(void *opaque, hwaddr addr, uint64_t data, unsigned size); +void vfio_sub_page_bar_update_mappings(VFIOPCIDevice *vdev); bool vfio_opt_rom_in_denylist(VFIOPCIDevice *vdev); bool vfio_config_quirk_setup(VFIOPCIDevice *vdev, Error **errp); void vfio_vga_quirk_setup(VFIOPCIDevice *vdev); From e0b33efe2acc3c6c651bb761394ef597e763e349 Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Thu, 24 Jul 2025 00:09:05 +0800 Subject: [PATCH 2652/2760] vfio/igd: Require host VGA decode for legacy mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit a59d06305fff ("vfio/pci: Introduce x-pci-class-code option") allows user to expose non-VGA IGD device as VGA controller to the guest. However, legacy mode requires host VGA range access. Check that GGC.IVD == 0 before enabling legacy mode to ensure IGD is a real VGA device claiming host VGA ranges. Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250723160906.44941-2-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- docs/igd-assign.txt | 1 + hw/vfio/igd.c | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/igd-assign.txt b/docs/igd-assign.txt index af4e8391fc..e54040335b 100644 --- a/docs/igd-assign.txt +++ b/docs/igd-assign.txt @@ -48,6 +48,7 @@ Intel document [1] shows how to dump VBIOS to file. For UEFI Option ROM, see QEMU also provides a "Legacy" mode that implicitly enables full functionality on IGD, it is automatically enabled when * IGD generation is 6 to 9 (Sandy Bridge to Comet Lake) +* IGD claims VGA cycles on host (IGD is VGA controller on host) * Machine type is i440fx * IGD is assigned to guest BDF 00:02.0 * ROM BAR or romfile is present diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index e7a9d1ffc1..5b1ad1a804 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -113,6 +113,7 @@ static int igd_gen(VFIOPCIDevice *vdev) #define IGD_BDSM 0x5c /* Base Data of Stolen Memory */ #define IGD_BDSM_GEN11 0xc0 /* Base Data of Stolen Memory of gen 11 and later */ +#define IGD_GMCH_VGA_DISABLE BIT(1) #define IGD_GMCH_GEN6_GMS_SHIFT 3 /* SNB_GMCH in i915 */ #define IGD_GMCH_GEN6_GMS_MASK 0x1f #define IGD_GMCH_GEN8_GMS_SHIFT 8 /* BDW_GMCH in i915 */ @@ -533,12 +534,14 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) /* * For backward compatibility, enable legacy mode when * - Device geneation is 6 to 9 (including both) + * - IGD claims VGA cycles on host * - Machine type is i440fx (pc_piix) * - IGD device is at guest BDF 00:02.0 * - Not manually disabled by x-igd-legacy-mode=off */ if ((vdev->igd_legacy_mode != ON_OFF_AUTO_OFF) && (gen >= 6 && gen <= 9) && + !(gmch & IGD_GMCH_VGA_DISABLE) && !strcmp(MACHINE_GET_CLASS(qdev_get_machine())->family, "pc_piix") && (&vdev->pdev == pci_find_device(pci_device_root_bus(&vdev->pdev), 0, PCI_DEVFN(0x2, 0)))) { @@ -568,12 +571,10 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) } /* - * If IGD VGA Disable is clear (expected) and VGA is not already - * enabled, try to enable it. Probably shouldn't be using legacy mode - * without VGA, but also no point in us enabling VGA if disabled in - * hardware. + * If VGA is not already enabled, try to enable it. We shouldn't be + * using legacy mode without VGA. */ - if (!(gmch & 0x2) && !vdev->vga && !vfio_populate_vga(vdev, &err)) { + if (!vdev->vga && !vfio_populate_vga(vdev, &err)) { error_setg(&err, "Unable to enable VGA access"); goto error; } From 0db7e4cb62026196f06755c77f943294d9879e5a Mon Sep 17 00:00:00 2001 From: Tomita Moeko Date: Thu, 24 Jul 2025 00:09:06 +0800 Subject: [PATCH 2653/2760] vfio/igd: Fix VGA regions are not exposed in legacy mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit a59d06305fff ("vfio/pci: Introduce x-pci-class-code option"), pci_register_vga() has been moved ouside of vfio_populate_vga(). As a result, IGD VGA ranges are no longer properly exposed to guest. To fix this, call pci_register_vga() after vfio_populate_vga() legacy mode. A wrapper function vfio_pci_config_register_vga() is introduced to handle it. Fixes: a59d06305fff ("vfio/pci: Introduce x-pci-class-code option") Signed-off-by: Tomita Moeko Reviewed-by: Alex Williamson Link: https://lore.kernel.org/qemu-devel/20250723160906.44941-3-tomitamoeko@gmail.com Signed-off-by: Cédric Le Goater --- hw/vfio/igd.c | 10 +++++++--- hw/vfio/pci.c | 13 ++++++++++--- hw/vfio/pci.h | 1 + 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/hw/vfio/igd.c b/hw/vfio/igd.c index 5b1ad1a804..ee0767b0b8 100644 --- a/hw/vfio/igd.c +++ b/hw/vfio/igd.c @@ -574,9 +574,13 @@ static bool vfio_pci_igd_config_quirk(VFIOPCIDevice *vdev, Error **errp) * If VGA is not already enabled, try to enable it. We shouldn't be * using legacy mode without VGA. */ - if (!vdev->vga && !vfio_populate_vga(vdev, &err)) { - error_setg(&err, "Unable to enable VGA access"); - goto error; + if (!vdev->vga) { + if (vfio_populate_vga(vdev, &err)) { + vfio_pci_config_register_vga(vdev); + } else { + error_setg(&err, "Unable to enable VGA access"); + goto error; + } } /* Enable OpRegion and LPC bridge quirk */ diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index f5bc5359eb..4fa692c1a3 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3162,6 +3162,15 @@ static void vfio_unregister_req_notifier(VFIOPCIDevice *vdev) vdev->req_enabled = false; } +void vfio_pci_config_register_vga(VFIOPCIDevice *vdev) +{ + assert(vdev->vga != NULL); + + pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, + &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, + &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); +} + bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) { PCIDevice *pdev = &vdev->pdev; @@ -3283,9 +3292,7 @@ bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp) vfio_bars_register(vdev); if (vdev->vga && vfio_is_vga(vdev)) { - pci_register_vga(&vdev->pdev, &vdev->vga->region[QEMU_PCI_VGA_MEM].mem, - &vdev->vga->region[QEMU_PCI_VGA_IO_LO].mem, - &vdev->vga->region[QEMU_PCI_VGA_IO_HI].mem); + vfio_pci_config_register_vga(vdev); } return true; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index bca289035a..81465a8214 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -253,6 +253,7 @@ extern const VMStateDescription vfio_display_vmstate; void vfio_pci_bars_exit(VFIOPCIDevice *vdev); bool vfio_pci_add_capabilities(VFIOPCIDevice *vdev, Error **errp); +void vfio_pci_config_register_vga(VFIOPCIDevice *vdev); bool vfio_pci_config_setup(VFIOPCIDevice *vdev, Error **errp); bool vfio_pci_interrupt_setup(VFIOPCIDevice *vdev, Error **errp); void vfio_pci_intx_eoi(VFIODevice *vbasedev); From e895095c78ab877d40df2dd31ee79d85757d963b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 27 Apr 2021 15:33:37 +0200 Subject: [PATCH 2654/2760] target/mips: Only update MVPControl.EVP bit if executed by master VPE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit According to the 'MIPS MT Application-Specific Extension' manual: If the VPE executing the instruction is not a Master VPE, with the MVP bit of the VPEConf0 register set, the EVP bit is unchanged by the instruction. Modify the DVPE/EVPE opcodes to only update the MVPControl.EVP bit if executed on a master VPE. Cc: qemu-stable@nongnu.org Reported-by: Hansni Bu Buglink: https://bugs.launchpad.net/qemu/+bug/1926277 Fixes: f249412c749 ("mips: Add MT halting and waking of VPEs") Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Jiaxun Yang Message-ID: <20210427133343.159718-1-f4bug@amsat.org> Signed-off-by: Philippe Mathieu-Daudé --- target/mips/tcg/system/cp0_helper.c | 32 ++++++++++++++++------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/target/mips/tcg/system/cp0_helper.c b/target/mips/tcg/system/cp0_helper.c index 101b1e65fd..b69e70d7fc 100644 --- a/target/mips/tcg/system/cp0_helper.c +++ b/target/mips/tcg/system/cp0_helper.c @@ -1562,12 +1562,14 @@ target_ulong helper_dvpe(CPUMIPSState *env) CPUState *other_cs = first_cpu; target_ulong prev = env->mvp->CP0_MVPControl; - CPU_FOREACH(other_cs) { - MIPSCPU *other_cpu = MIPS_CPU(other_cs); - /* Turn off all VPEs except the one executing the dvpe. */ - if (&other_cpu->env != env) { - other_cpu->env.mvp->CP0_MVPControl &= ~(1 << CP0MVPCo_EVP); - mips_vpe_sleep(other_cpu); + if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) { + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); + /* Turn off all VPEs except the one executing the dvpe. */ + if (&other_cpu->env != env) { + other_cpu->env.mvp->CP0_MVPControl &= ~(1 << CP0MVPCo_EVP); + mips_vpe_sleep(other_cpu); + } } } return prev; @@ -1578,15 +1580,17 @@ target_ulong helper_evpe(CPUMIPSState *env) CPUState *other_cs = first_cpu; target_ulong prev = env->mvp->CP0_MVPControl; - CPU_FOREACH(other_cs) { - MIPSCPU *other_cpu = MIPS_CPU(other_cs); + if (env->CP0_VPEConf0 & (1 << CP0VPEC0_MVP)) { + CPU_FOREACH(other_cs) { + MIPSCPU *other_cpu = MIPS_CPU(other_cs); - if (&other_cpu->env != env - /* If the VPE is WFI, don't disturb its sleep. */ - && !mips_vpe_is_wfi(other_cpu)) { - /* Enable the VPE. */ - other_cpu->env.mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP); - mips_vpe_wake(other_cpu); /* And wake it up. */ + if (&other_cpu->env != env + /* If the VPE is WFI, don't disturb its sleep. */ + && !mips_vpe_is_wfi(other_cpu)) { + /* Enable the VPE. */ + other_cpu->env.mvp->CP0_MVPControl |= (1 << CP0MVPCo_EVP); + mips_vpe_wake(other_cpu); /* And wake it up. */ + } } } return prev; From 8e8cb3b5722babe7e7b597b3805bf09f24ed6979 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Fri, 21 Feb 2025 16:48:56 +0300 Subject: [PATCH 2655/2760] hw/display/qxl-render: fix qxl_unpack_chunks() chunk size calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case of multiple chunks, code in qxl_unpack_chunks() takes size of the wrong (next in the chain) chunk, instead of using current chunk size. This leads to wrong number of bytes being copied, and to crashes if next chunk size is larger than the current one. Based on the code by Gao Yong. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/1628 Tested-by: Thaddeus Hogan Tested-by: Vadim Zeitlin Signed-off-by: Michael Tokarev Reviewed-by: Thomas Huth Message-ID: <20250221134856.478806-1-mjt@tls.msk.ru> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/qxl-render.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/hw/display/qxl-render.c b/hw/display/qxl-render.c index eda6d3de37..c6a9ac1da1 100644 --- a/hw/display/qxl-render.c +++ b/hw/display/qxl-render.c @@ -222,6 +222,7 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, uint32_t max_chunks = 32; size_t offset = 0; size_t bytes; + QXLPHYSICAL next_chunk_phys = 0; for (;;) { bytes = MIN(size - offset, chunk->data_size); @@ -230,7 +231,15 @@ static void qxl_unpack_chunks(void *dest, size_t size, PCIQXLDevice *qxl, if (offset == size) { return; } - chunk = qxl_phys2virt(qxl, chunk->next_chunk, group_id, + next_chunk_phys = chunk->next_chunk; + /* fist time, only get the next chunk's data size */ + chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, + sizeof(QXLDataChunk)); + if (!chunk) { + return; + } + /* second time, check data size and get data */ + chunk = qxl_phys2virt(qxl, next_chunk_phys, group_id, sizeof(QXLDataChunk) + chunk->data_size); if (!chunk) { return; From 962316a6a339367e187b4728ef131ec6e635f364 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 8 Jul 2025 10:55:33 +0200 Subject: [PATCH 2656/2760] hw/vfio/vfio-migration: Remove unnecessary 'qemu/typedefs.h' include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "qemu/typedefs.h" is already included by "qemu/osdep.h". Reviewed-by: Cédric Le Goater Signed-off-by: Philippe Mathieu-Daudé Message-ID: <20250708085859.7885-3-philmd@linaro.org> --- hw/vfio/vfio-migration-internal.h | 1 - 1 file changed, 1 deletion(-) diff --git a/hw/vfio/vfio-migration-internal.h b/hw/vfio/vfio-migration-internal.h index 54141e27e6..814fbd9eba 100644 --- a/hw/vfio/vfio-migration-internal.h +++ b/hw/vfio/vfio-migration-internal.h @@ -13,7 +13,6 @@ #include #endif -#include "qemu/typedefs.h" #include "qemu/notify.h" /* From b496a392fec656d8e7af81c496efa3d45fd59023 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Fri, 25 Jul 2025 13:17:28 -0700 Subject: [PATCH 2657/2760] migration: rename target.c to vfio.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Acked-by: Fabiano Rosas Reviewed-by: Peter Xu Message-ID: <20250725201729.17100-3-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- migration/meson.build | 2 +- migration/{target.c => vfio.c} | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) rename migration/{target.c => vfio.c} (90%) diff --git a/migration/meson.build b/migration/meson.build index 9aa48b290e..276da3be5a 100644 --- a/migration/meson.build +++ b/migration/meson.build @@ -51,4 +51,4 @@ system_ss.add(when: qatzip, if_true: files('multifd-qatzip.c')) specific_ss.add(when: 'CONFIG_SYSTEM_ONLY', if_true: files('ram.c', - 'target.c')) + 'vfio.c')) diff --git a/migration/target.c b/migration/vfio.c similarity index 90% rename from migration/target.c rename to migration/vfio.c index 12fd399f0c..0b64e49ef0 100644 --- a/migration/target.c +++ b/migration/vfio.c @@ -1,5 +1,5 @@ /* - * QEMU live migration - functions that need to be compiled target-specific + * QEMU live migration - VFIO * * This work is licensed under the terms of the GNU GPL, version 2 * or (at your option) any later version. From 2bfcd27e00a49da2efa5d703121b94cd9cd4948b Mon Sep 17 00:00:00 2001 From: Luc Michel Date: Wed, 16 Jul 2025 11:53:43 +0200 Subject: [PATCH 2658/2760] hw/net/cadence_gem: fix register mask initialization MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The gem_init_register_masks function was called at init time but it relies on the num-priority-queues property. Call it at realize time instead. Cc: qemu-stable@nongnu.org Fixes: 4c70e32f05f ("net: cadence_gem: Define access permission for interrupt registers") Signed-off-by: Luc Michel Reviewed-by: Francisco Iglesias Reviewed-by: Sai Pavan Boddu Message-ID: <20250716095432.81923-2-luc.michel@amd.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/net/cadence_gem.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/net/cadence_gem.c b/hw/net/cadence_gem.c index 50025d5a6f..44446666de 100644 --- a/hw/net/cadence_gem.c +++ b/hw/net/cadence_gem.c @@ -1756,6 +1756,7 @@ static void gem_realize(DeviceState *dev, Error **errp) sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq[i]); } + gem_init_register_masks(s); qemu_macaddr_default_if_unset(&s->conf.macaddr); s->nic = qemu_new_nic(&net_gem_info, &s->conf, @@ -1776,7 +1777,6 @@ static void gem_init(Object *obj) DB_PRINT("\n"); - gem_init_register_masks(s); memory_region_init_io(&s->iomem, OBJECT(s), &gem_ops, s, "enet", sizeof(s->regs)); From 014bb30d218ef0d91c300324dec99138e999e32d Mon Sep 17 00:00:00 2001 From: Adam Williamson Date: Thu, 17 Jul 2025 15:02:07 -0700 Subject: [PATCH 2659/2760] hw/xen/passthrough: add missing error-report include MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit cfcacbab38e ("xen/passthrough: use gsi to map pirq when dom0 is PVH") an `error_report` was added to this file, but the corresponding include of `qemu/error-report.h` was missed. This only becomes apparent when building against Xen 4.20+ with trace backend log disabled. Fixes: cfcacbab38e4 (xen/passthrough: use gsi to map pirq when dom0 is PVH) Signed-off-by: Adam Williamson Reviewed-by: Markus Armbruster Reviewed-by: Philippe Mathieu-Daudé Message-ID: <20250717220207.171040-1-awilliam@redhat.com> [PMD: Improved commit description, added Fixes: tag] Signed-off-by: Philippe Mathieu-Daudé --- hw/xen/xen_pt.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/xen/xen_pt.c b/hw/xen/xen_pt.c index 9d16644d82..006b5b55f2 100644 --- a/hw/xen/xen_pt.c +++ b/hw/xen/xen_pt.c @@ -54,6 +54,7 @@ #include "qemu/osdep.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include #include "hw/pci/pci.h" From 2865bf1c5795371ef441e9029bc22566ccff8299 Mon Sep 17 00:00:00 2001 From: Pierrick Bouvier Date: Thu, 24 Jul 2025 09:11:42 -0700 Subject: [PATCH 2660/2760] system/physmem: fix use-after-free with dispatch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A use-after-free bug was reported when booting a Linux kernel during the pci setup phase. It's quite hard to reproduce (needs smp, and favored by having several pci devices with BAR and specific Linux config, which is Debian default one in this case). After investigation (see the associated bug ticket), it appears that, under specific conditions, we might access a cached AddressSpaceDispatch that was reclaimed by RCU thread meanwhile. In the Linux boot scenario, during the pci phase, memory region are destroyed/recreated, resulting in exposition of the bug. The core of the issue is that we cache the dispatch associated to current cpu in cpu->cpu_ases[asidx].memory_dispatch. It is updated with tcg_commit, which runs asynchronously on a given cpu. At some point, we leave the rcu critial section, and the RCU thread starts reclaiming it, but tcg_commit is not yet invoked, resulting in the use-after-free. It's not the first problem around this area, and commit 0d58c660689 [1] ("softmmu: Use async_run_on_cpu in tcg_commit") already tried to address it. It did a good job, but it seems that we found a specific situation where it's not enough. This patch takes a simple approach: remove the cached value creating the issue, and make sure we always get the current mapping for address space, using address_space_to_dispatch(cpu->cpu_ases[asidx].as). It's equivalent to qatomic_rcu_read(&as->current_map)->dispatch; This is not really costly, we just need two dereferences, including one atomic (rcu) read, which is negligible considering we are already on mmu slow path anyway. Note that tcg_commit is still needed, as it's taking care of flushing TLB, removing previously mapped entries. Another solution would be to cache directly values under the dispatch (dispatch themselves are not ref counted), keep an active reference on associated memory section, and release it when appropriate (tricky). Given the time already spent debugging this area now and previously, I strongly prefer eliminating the root of the issue, instead of adding more complexity for a hypothetical performance gain. RCU is precisely used to ensure good performance when reading data, so caching is not as beneficial as it might seem IMHO. [1] https://gitlab.com/qemu-project/qemu/-/commit/0d58c660689f6da1e3feff8a997014003d928b3b Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3040 Signed-off-by: Pierrick Bouvier Reviewed-by: Richard Henderson Reviewed-by: Michael Tokarev Tested-by: Michael Tokarev Message-ID: <20250724161142.2803091-1-pierrick.bouvier@linaro.org> Signed-off-by: Philippe Mathieu-Daudé --- system/physmem.c | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/system/physmem.c b/system/physmem.c index 130c148ffb..e5dd760e0b 100644 --- a/system/physmem.c +++ b/system/physmem.c @@ -165,13 +165,11 @@ static bool ram_is_cpr_compatible(RAMBlock *rb); * CPUAddressSpace: all the information a CPU needs about an AddressSpace * @cpu: the CPU whose AddressSpace this is * @as: the AddressSpace itself - * @memory_dispatch: its dispatch pointer (cached, RCU protected) * @tcg_as_listener: listener for tracking changes to the AddressSpace */ typedef struct CPUAddressSpace { CPUState *cpu; AddressSpace *as; - struct AddressSpaceDispatch *memory_dispatch; MemoryListener tcg_as_listener; } CPUAddressSpace; @@ -692,7 +690,7 @@ address_space_translate_for_iotlb(CPUState *cpu, int asidx, hwaddr orig_addr, IOMMUTLBEntry iotlb; int iommu_idx; hwaddr addr = orig_addr; - AddressSpaceDispatch *d = cpu->cpu_ases[asidx].memory_dispatch; + AddressSpaceDispatch *d = address_space_to_dispatch(cpu->cpu_ases[asidx].as); for (;;) { section = address_space_translate_internal(d, addr, &addr, plen, false); @@ -753,7 +751,7 @@ MemoryRegionSection *iotlb_to_section(CPUState *cpu, { int asidx = cpu_asidx_from_attrs(cpu, attrs); CPUAddressSpace *cpuas = &cpu->cpu_ases[asidx]; - AddressSpaceDispatch *d = cpuas->memory_dispatch; + AddressSpaceDispatch *d = address_space_to_dispatch(cpuas->as); int section_index = index & ~TARGET_PAGE_MASK; MemoryRegionSection *ret; @@ -2780,9 +2778,6 @@ static void tcg_log_global_after_sync(MemoryListener *listener) static void tcg_commit_cpu(CPUState *cpu, run_on_cpu_data data) { - CPUAddressSpace *cpuas = data.host_ptr; - - cpuas->memory_dispatch = address_space_to_dispatch(cpuas->as); tlb_flush(cpu); } @@ -2798,11 +2793,7 @@ static void tcg_commit(MemoryListener *listener) cpu = cpuas->cpu; /* - * Defer changes to as->memory_dispatch until the cpu is quiescent. - * Otherwise we race between (1) other cpu threads and (2) ongoing - * i/o for the current cpu thread, with data cached by mmu_lookup(). - * - * In addition, queueing the work function will kick the cpu back to + * Queueing the work function will kick the cpu back to * the main loop, which will end the RCU critical section and reclaim * the memory data structures. * From 653a75a9d7f957c4ba9387d1a54bccfdce49cb9c Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Mon, 28 Jul 2025 00:55:07 +0300 Subject: [PATCH 2661/2760] roms/Makefile: fix npcmNxx_bootrom build rules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since commit 70ce076fa6dff60, the actual rom source dirs are subdirs of vbootrom/ submodule, not in top-level of it. Fixes: 70ce076fa6dff60 "roms: Update vbootrom to 1287b6e" Fixes: 269b7effd90 ("pc-bios: Add NPCM8XX vBootrom") Cc: qemu-stable@nongnu.org Signed-off-by: Michael Tokarev Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Message-ID: <20250727215511.807880-1-mjt@tls.msk.ru> Signed-off-by: Philippe Mathieu-Daudé --- roms/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/roms/Makefile b/roms/Makefile index beff58d9d5..6af68a922f 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -193,12 +193,12 @@ qboot: cp qboot/build/bios.bin ../pc-bios/qboot.rom npcm7xx_bootrom: - $(MAKE) -C vbootrom CROSS_COMPILE=$(arm_cross_prefix) - cp vbootrom/npcm7xx_bootrom.bin ../pc-bios/npcm7xx_bootrom.bin + $(MAKE) -C vbootrom/npcm7xx CROSS_COMPILE=$(arm_cross_prefix) + cp vbootrom/npcm7xx/npcm7xx_bootrom.bin ../pc-bios/npcm7xx_bootrom.bin npcm8xx_bootrom: - $(MAKE) -C vbootrom CROSS_COMPILE=$(aarch64_cross_prefix) - cp vbootrom/npcm8xx_bootrom.bin ../pc-bios/npcm8xx_bootrom.bin + $(MAKE) -C vbootrom/npcm8xx CROSS_COMPILE=$(aarch64_cross_prefix) + cp vbootrom/npcm8xx/npcm8xx_bootrom.bin ../pc-bios/npcm8xx_bootrom.bin hppa-firmware: $(MAKE) -C seabios-hppa parisc From 67e4808403471427b73c8d2c3f4273d64908f480 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Chigot?= Date: Mon, 28 Jul 2025 11:05:18 +0200 Subject: [PATCH 2662/2760] hw/display/sm501: fix missing error-report.h MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "qemu/error-report.h" was previously implicitly included. This is no longer the case following 012842c075520dbe1bd96a2fdcf4e218874ba443. However, the issue predates this change as `error-report.h` should have been included when the `warn_report` call was introduced. Fixes: fa140b9562 ("hw/sm501: allow compiling without PIXMAN") Signed-off-by: Clément Chigot Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Daniel P. Berrangé Message-ID: <20250728090518.963573-1-chigot@adacore.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/display/sm501.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/display/sm501.c b/hw/display/sm501.c index 6d2f18684c..bc091b3c9f 100644 --- a/hw/display/sm501.c +++ b/hw/display/sm501.c @@ -26,6 +26,7 @@ #include "qemu/osdep.h" #include "qemu/units.h" #include "qapi/error.h" +#include "qemu/error-report.h" #include "qemu/log.h" #include "qemu/module.h" #include "hw/usb/hcd-ohci.h" From d4d91ed42eb93f64950a036f35efbd915feb95b3 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 17 Jul 2025 13:57:51 +0200 Subject: [PATCH 2663/2760] qapi: Add more cross-references MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We recently (merge commit 504632dcc631) enclosed command and type names in `backquotes`, so they become links in generated HTML. Take care of a few we missed. Signed-off-by: Markus Armbruster Message-ID: <20250717115751.3832597-1-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: John Snow --- qapi/dump.json | 2 +- qapi/machine.json | 2 +- qapi/migration.json | 4 ++-- qapi/misc-i386.json | 2 +- qapi/run-state.json | 2 +- qapi/sockets.json | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/qapi/dump.json b/qapi/dump.json index 32c8c1f06e..726b520870 100644 --- a/qapi/dump.json +++ b/qapi/dump.json @@ -79,7 +79,7 @@ # # @detach: if true, QMP will return immediately rather than waiting # for the dump to finish. The user can track progress using -# "query-dump". (since 2.6). +# `query-dump`. (since 2.6). # # @begin: if specified, the starting physical address. # diff --git a/qapi/machine.json b/qapi/machine.json index 6f59f70ca6..038eab281c 100644 --- a/qapi/machine.json +++ b/qapi/machine.json @@ -2087,7 +2087,7 @@ # # @deprecated-props: an optional list of properties that are flagged as # deprecated by the CPU vendor. The list depends on the -# CpuModelExpansionType: "static" properties are a subset of the +# `CpuModelExpansionType`: "static" properties are a subset of the # enabled-properties for the expanded model; "full" properties are # a set of properties that are deprecated across all models for # the architecture. (since: 10.1 -- since 9.1 on s390x --). diff --git a/qapi/migration.json b/qapi/migration.json index e08a99bb82..2387c21e9c 100644 --- a/qapi/migration.json +++ b/qapi/migration.json @@ -641,7 +641,7 @@ # # This mode supports VFIO devices provided the user first puts the # guest in the suspended runstate, such as by issuing -# guest-suspend-ram to the QEMU guest agent. +# `guest-suspend-ram` to the QEMU guest agent. # # Best performance is achieved when the memory backend is shared # and the @x-ignore-shared migration capability is set, but this @@ -1704,7 +1704,7 @@ # # .. admonition:: Notes # -# 1. The 'query-migrate' command should be used to check +# 1. The `query-migrate` command should be used to check # migration's progress and final result (this information is # provided by the 'status' member). # diff --git a/qapi/misc-i386.json b/qapi/misc-i386.json index c8c91a241c..d1ce8caf25 100644 --- a/qapi/misc-i386.json +++ b/qapi/misc-i386.json @@ -8,7 +8,7 @@ # # Reset the RTC interrupt reinjection backlog. Can be used if another # mechanism to synchronize guest time is in effect, for example QEMU -# guest agent's guest-set-time command. +# guest agent's `guest-set-time` command. # # Use of this command is only applicable for x86 machines with an RTC, # and on other machines will silently return without performing any diff --git a/qapi/run-state.json b/qapi/run-state.json index 54ba5c9a3f..4757947ca6 100644 --- a/qapi/run-state.json +++ b/qapi/run-state.json @@ -20,7 +20,7 @@ # @inmigrate: guest is paused waiting for an incoming migration. Note # that this state does not tell whether the machine will start at # the end of the migration. This depends on the command-line -S -# option and any invocation of 'stop' or 'cont' that has happened +# option and any invocation of `stop` or `cont` that has happened # since QEMU was started. # # @internal-error: An internal error that prevents further guest diff --git a/qapi/sockets.json b/qapi/sockets.json index 82046b0b3a..32fac51728 100644 --- a/qapi/sockets.json +++ b/qapi/sockets.json @@ -143,7 +143,7 @@ # # @str: decimal is for file descriptor number, otherwise it's a file # descriptor name. Named file descriptors are permitted in -# monitor commands, in combination with the 'getfd' command. +# monitor commands, in combination with the `getfd` command. # Decimal file descriptors are permitted at startup or other # contexts where no monitor context is active. # From a3004697f7342258de6fdc3ed85943a33a4809cf Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 24 Jul 2025 11:17:41 +0200 Subject: [PATCH 2664/2760] qapi/accelerator: Fix markup of heading The docs generated for qapi/accelerator.json shows text "= Accelerators" instead of a heading. This is because the patch that added the heading crossed with the commit that changed heading markup (commit 6c10778826a "docs/sphinx: remove special parsing for freeform sections"). Fix the markup. Fixes: 18da42ee4273 (qapi/accel: Move definitions related to accelerators in their own file) Signed-off-by: Markus Armbruster Message-ID: <20250724091742.1950167-2-armbru@redhat.com> --- qapi/accelerator.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/qapi/accelerator.json b/qapi/accelerator.json index 28d5ff4c49..fb28c8d920 100644 --- a/qapi/accelerator.json +++ b/qapi/accelerator.json @@ -4,7 +4,9 @@ # SPDX-License-Identifier: GPL-2.0-or-later ## -# = Accelerators +# ************ +# Accelerators +# ************ ## { 'include': 'common.json' } From 1047cc281631063ff2db481b8ea7eb755005236a Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 24 Jul 2025 11:17:42 +0200 Subject: [PATCH 2665/2760] tests/qapi-schema: Bury dead test case doc-non-first-section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The test passed when it was added. However, the commit adding it neglected to make Meson aware of it, so it never ran automatically. The test stopped making sense when we changed headings markup, and ceased to pass then. It should've been removed then. Do that now. Fixes: 6c10778826a8 (docs/sphinx: remove special parsing for freeform sections) Signed-off-by: Markus Armbruster Message-ID: <20250724091742.1950167-3-armbru@redhat.com> Reviewed-by: Daniel P. Berrangé Reviewed-by: John Snow --- tests/qapi-schema/doc-non-first-section.err | 1 - tests/qapi-schema/doc-non-first-section.json | 6 ------ tests/qapi-schema/doc-non-first-section.out | 0 3 files changed, 7 deletions(-) delete mode 100644 tests/qapi-schema/doc-non-first-section.err delete mode 100644 tests/qapi-schema/doc-non-first-section.json delete mode 100644 tests/qapi-schema/doc-non-first-section.out diff --git a/tests/qapi-schema/doc-non-first-section.err b/tests/qapi-schema/doc-non-first-section.err deleted file mode 100644 index eeced2bca7..0000000000 --- a/tests/qapi-schema/doc-non-first-section.err +++ /dev/null @@ -1 +0,0 @@ -doc-non-first-section.json:5:1: '=' heading must come first in a comment block diff --git a/tests/qapi-schema/doc-non-first-section.json b/tests/qapi-schema/doc-non-first-section.json deleted file mode 100644 index 1590876061..0000000000 --- a/tests/qapi-schema/doc-non-first-section.json +++ /dev/null @@ -1,6 +0,0 @@ -# = section must be first line - -## -# -# = Not first -## diff --git a/tests/qapi-schema/doc-non-first-section.out b/tests/qapi-schema/doc-non-first-section.out deleted file mode 100644 index e69de29bb2..0000000000 From a14f6d3288bcfd4f3f7e6aca4572e2443cc1f91b Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 29 Jul 2025 11:16:41 +0200 Subject: [PATCH 2666/2760] docs/qapi-domain: Fix typos Signed-off-by: Markus Armbruster Message-ID: <20250729091642.3513895-2-armbru@redhat.com> Reviewed-by: Manos Pitsidianakis --- docs/devel/qapi-domain.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst index b71890f660..fe540d1e40 100644 --- a/docs/devel/qapi-domain.rst +++ b/docs/devel/qapi-domain.rst @@ -9,7 +9,7 @@ in Sphinx is provided by the QAPI Domain, located in `Python Domain `_ included with Sphinx, but provides special directives and roles -speciically for annotating and documenting QAPI definitions +for annotating and documenting QAPI definitions specifically. A `Domain @@ -101,7 +101,7 @@ without types. The QAPI domain uses this class for features, returns, and enum values. TypedField: - * Creates a grouped, typed field. Multiple adjacent entres will be + * Creates a grouped, typed field. Multiple adjacent entries will be merged into one section, and the content will form a bulleted list. * *Must* take at least one argument, but supports up to two - nominally, a name and a type. From a95b3c0ad857055198cdb272bca2af975092d060 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Tue, 29 Jul 2025 11:16:42 +0200 Subject: [PATCH 2667/2760] MAINTAINERS: Cover docs/devel/qapi-domain.rst properly Section QAPI already covers it, and that's fine. It's missing from "Sphinx documentation configuration and build machinery". Add it there. Signed-off-by: Markus Armbruster Message-ID: <20250729091642.3513895-3-armbru@redhat.com> Reviewed-by: Manos Pitsidianakis [Improved commit message] --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 37879ab64e..069d77f2f8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4433,6 +4433,7 @@ F: docs/requirements.txt F: docs/sphinx/ F: docs/_templates/ F: docs/devel/docs.rst +F: docs/devel/qapi-domain.rst Rust build system integration M: Manos Pitsidianakis From 9b80226ece693197af8a981b424391b68b5bc38e Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 29 Jul 2025 13:00:41 -0400 Subject: [PATCH 2668/2760] Update version for the v10.1.0-rc1 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c52f1cc9fe..8ae905b0dd 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.90 +10.0.91 From 77707bfdf871199bbee665e721ced961aaf3a798 Mon Sep 17 00:00:00 2001 From: Vac Chen Date: Sun, 6 Jul 2025 14:55:54 +0800 Subject: [PATCH 2669/2760] target/riscv: Fix pmp range wraparound on zero pmp_is_in_range() prefers to match addresses within the interval [start, end]. To archieve this, pmpaddrX is decremented during the end address update. In TOR mode, a rule is ignored if its start address is greater than or equal to its end address. However, if pmpaddrX is set to 0, this decrement operation causes the calulated end address to wrap around to UINT_MAX. In this scenario, the address guard for this PMP entry would become ineffective. This patch addresses the issue by moving the guard check earlier, preventing the problematic wraparound when pmpaddrX is zero. Signed-off-by: Vac Chen Reviewed-by: Alistair Francis Message-ID: <20250706065554.42953-1-vacantron@gmail.com> Signed-off-by: Alistair Francis --- target/riscv/pmp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/target/riscv/pmp.c b/target/riscv/pmp.c index 3540327c9a..72f1372a49 100644 --- a/target/riscv/pmp.c +++ b/target/riscv/pmp.c @@ -211,11 +211,12 @@ void pmp_update_rule_addr(CPURISCVState *env, uint32_t pmp_index) break; case PMP_AMATCH_TOR: - sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ - ea = (this_addr << 2) - 1u; - if (sa > ea) { + if (prev_addr >= this_addr) { sa = ea = 0u; + break; } + sa = prev_addr << 2; /* shift up from [xx:0] to [xx+2:2] */ + ea = (this_addr << 2) - 1u; break; case PMP_AMATCH_NA4: From 35d129399d319b7ab806e82ebacd52392e36bf61 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Thu, 24 Jul 2025 16:33:48 +0530 Subject: [PATCH 2670/2760] bios-tables-test-allowed-diff.h: Allow RISC-V FADT and MADT changes Signed-off-by: Sunil V L Acked-by: Michael S. Tsirkin Reviewed-by: Daniel Henrique Barboza Reviewed-by: Nutty Liu Message-ID: <20250724110350.452828-2-sunilvl@ventanamicro.com> Signed-off-by: Alistair Francis --- tests/qtest/bios-tables-test-allowed-diff.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..0c3f7a6cac 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,3 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/riscv64/virt/APIC", +"tests/data/acpi/riscv64/virt/FACP", From a3b95362ce90d03d150dffed3add0cb600fb0850 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Thu, 24 Jul 2025 16:33:49 +0530 Subject: [PATCH 2671/2760] hw/riscv/virt-acpi-build.c: Update FADT and MADT versions RISC-V support is added only in ACPI 6.6. According to the ACPI 6.6 specification, the minor version of the Fixed ACPI Description Table (FADT) should be 6, and the Multiple APIC Description Table (MADT) should use revision 7. So, update the RISC-V FADT and MADT to reflect correct versions. Update the code comments to reflect ACPI 6.6 version details. Signed-off-by: Sunil V L Acked-by: Michael S. Tsirkin Reviewed-by: Daniel Henrique Barboza Reviewed-by: Nutty Liu Message-ID: <20250724110350.452828-3-sunilvl@ventanamicro.com> Signed-off-by: Alistair Francis --- hw/riscv/virt-acpi-build.c | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/hw/riscv/virt-acpi-build.c b/hw/riscv/virt-acpi-build.c index ee1416d264..f1406cb683 100644 --- a/hw/riscv/virt-acpi-build.c +++ b/hw/riscv/virt-acpi-build.c @@ -270,11 +270,8 @@ spcr_setup(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s) #define RHCT_NODE_ARRAY_OFFSET 56 /* - * ACPI spec, Revision 6.5+ - * 5.2.36 RISC-V Hart Capabilities Table (RHCT) - * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/16 - * https://drive.google.com/file/d/1nP3nFiH4jkPMp6COOxP6123DCZKR-tia/view - * https://drive.google.com/file/d/1sKbOa8m1UZw1JkquZYe3F1zQBN1xXsaf/view + * ACPI spec, Revision 6.6 + * 5.2.37 RISC-V Hart Capabilities Table (RHCT) */ static void build_rhct(GArray *table_data, BIOSLinker *linker, @@ -421,7 +418,10 @@ static void build_rhct(GArray *table_data, acpi_table_end(linker, &table); } -/* FADT */ +/* + * ACPI spec, Revision 6.6 + * 5.2.9 Fixed ACPI Description Table (MADT) + */ static void build_fadt_rev6(GArray *table_data, BIOSLinker *linker, RISCVVirtState *s, @@ -429,7 +429,7 @@ static void build_fadt_rev6(GArray *table_data, { AcpiFadtData fadt = { .rev = 6, - .minor_ver = 5, + .minor_ver = 6, .flags = 1 << ACPI_FADT_F_HW_REDUCED_ACPI, .xdsdt_tbl_offset = &dsdt_tbl_offset, }; @@ -508,11 +508,8 @@ static void build_dsdt(GArray *table_data, } /* - * ACPI spec, Revision 6.5+ + * ACPI spec, Revision 6.6 * 5.2.12 Multiple APIC Description Table (MADT) - * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/15 - * https://drive.google.com/file/d/1R6k4MshhN3WTT-hwqAquu5nX6xSEqK2l/view - * https://drive.google.com/file/d/1oMGPyOD58JaPgMl1pKasT-VKsIKia7zR/view */ static void build_madt(GArray *table_data, BIOSLinker *linker, @@ -537,7 +534,7 @@ static void build_madt(GArray *table_data, hart_index_bits = imsic_num_bits(imsic_max_hart_per_socket); - AcpiTable table = { .sig = "APIC", .rev = 6, .oem_id = s->oem_id, + AcpiTable table = { .sig = "APIC", .rev = 7, .oem_id = s->oem_id, .oem_table_id = s->oem_table_id }; acpi_table_begin(&table, table_data); @@ -812,10 +809,8 @@ static void build_rimt(GArray *table_data, BIOSLinker *linker, } /* - * ACPI spec, Revision 6.5+ + * ACPI spec, Revision 6.6 * 5.2.16 System Resource Affinity Table (SRAT) - * REF: https://github.com/riscv-non-isa/riscv-acpi/issues/25 - * https://drive.google.com/file/d/1YTdDx2IPm5IeZjAW932EYU-tUtgS08tX/view */ static void build_srat(GArray *table_data, BIOSLinker *linker, RISCVVirtState *vms) From f3c8b7767f2e1fac37c727ca17b69e4f1e3351f2 Mon Sep 17 00:00:00 2001 From: Sunil V L Date: Thu, 24 Jul 2025 16:33:50 +0530 Subject: [PATCH 2672/2760] tests/data/acpi/riscv64: Update expected FADT and MADT Update the expected tables for the version change. /* * * ACPI Data Table [FACP] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue (in hex) */ [000h 0000 004h] Signature : "FACP" [Fixed ACPI Description Table (FADT)] [004h 0004 004h] Table Length : 00000114 [008h 0008 001h] Revision : 06 -[009h 0009 001h] Checksum : 13 +[009h 0009 001h] Checksum : 12 [00Ah 0010 006h] Oem ID : "BOCHS " [010h 0016 008h] Oem Table ID : "BXPC " [018h 0024 004h] Oem Revision : 00000001 [01Ch 0028 004h] Asl Compiler ID : "BXPC" [020h 0032 004h] Asl Compiler Revision : 00000001 [024h 0036 004h] FACS Address : 00000000 [028h 0040 004h] DSDT Address : 00000000 [02Ch 0044 001h] Model : 00 [02Dh 0045 001h] PM Profile : 00 [Unspecified] [02Eh 0046 002h] SCI Interrupt : 0000 [030h 0048 004h] SMI Command Port : 00000000 [034h 0052 001h] ACPI Enable Value : 00 [035h 0053 001h] ACPI Disable Value : 00 [036h 0054 001h] S4BIOS Command : 00 [037h 0055 001h] P-State Control : 00 @@ -86,33 +86,33 @@ Use APIC Physical Destination Mode (V4) : 0 Hardware Reduced (V5) : 1 Low Power S0 Idle (V5) : 0 [074h 0116 00Ch] Reset Register : [Generic Address Structure] [074h 0116 001h] Space ID : 00 [SystemMemory] [075h 0117 001h] Bit Width : 00 [076h 0118 001h] Bit Offset : 00 [077h 0119 001h] Encoded Access Width : 00 [Undefined/Legacy] [078h 0120 008h] Address : 0000000000000000 [080h 0128 001h] Value to cause reset : 00 [081h 0129 002h] ARM Flags (decoded below) : 0000 PSCI Compliant : 0 Must use HVC for PSCI : 0 -[083h 0131 001h] FADT Minor Revision : 05 +[083h 0131 001h] FADT Minor Revision : 06 [084h 0132 008h] FACS Address : 0000000000000000 [...] /* * * ACPI Data Table [APIC] * * Format: [HexOffset DecimalOffset ByteLength] FieldName : FieldValue (in hex) */ [000h 0000 004h] Signature : "APIC" [Multiple APIC Description Table (MADT)] [004h 0004 004h] Table Length : 00000074 -[008h 0008 001h] Revision : 06 -[009h 0009 001h] Checksum : B4 +[008h 0008 001h] Revision : 07 +[009h 0009 001h] Checksum : B3 [00Ah 0010 006h] Oem ID : "BOCHS " [010h 0016 008h] Oem Table ID : "BXPC " [...] Signed-off-by: Sunil V L Acked-by: Michael S. Tsirkin Reviewed-by: Daniel Henrique Barboza Reviewed-by: Nutty Liu Message-ID: <20250724110350.452828-4-sunilvl@ventanamicro.com> Signed-off-by: Alistair Francis --- tests/data/acpi/riscv64/virt/APIC | Bin 116 -> 116 bytes tests/data/acpi/riscv64/virt/FACP | Bin 276 -> 276 bytes tests/qtest/bios-tables-test-allowed-diff.h | 2 -- 3 files changed, 2 deletions(-) diff --git a/tests/data/acpi/riscv64/virt/APIC b/tests/data/acpi/riscv64/virt/APIC index 66a25dfd2d6ea2b607c024722b2eab95873a01e9..3fb5b753596fc7c2618b3d5210be8b00b0c177f6 100644 GIT binary patch delta 16 XcmXRZ;c^V{bS`0FU|`=okt+)TB&q~M delta 16 XcmXRZ;c^V{bS`0FU|`!akt+)TB&h^L diff --git a/tests/data/acpi/riscv64/virt/FACP b/tests/data/acpi/riscv64/virt/FACP index a5276b65ea8ce46cc9b40d96d98f0669c9089ed4..78e1b14b1d4ff0533a6a4c041f195a69b4ef114d 100644 GIT binary patch delta 30 mcmbQjG=+)F&CxkPgpq-PO=u!lB_rF!iPaMgcqj8PasU8k76z;U delta 30 mcmbQjG=+)F&CxkPgpq-PO?V<#B_r#^iPaMgcqj8PasU8k8wRWZ diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index 0c3f7a6cac..dfb8523c8b 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1,3 +1 @@ /* List of comma-separated changed AML files to ignore */ -"tests/data/acpi/riscv64/virt/APIC", -"tests/data/acpi/riscv64/virt/FACP", From b6f1244678bebaf7e2c775cfc66d452f95678ebf Mon Sep 17 00:00:00 2001 From: Yang Jialong Date: Mon, 28 Jul 2025 13:51:14 +0800 Subject: [PATCH 2673/2760] intc/riscv_aplic: Fix target register read when source is inactive The RISC-V Advanced interrupt Architecture: 4.5.16. Interrupt targets: If interrupt source i is inactive in this domain, register target[i] is read-only zero. Signed-off-by: Yang Jialong Reviewed-by: Daniel Henrique Barboza Message-ID: <20250728055114.252024-1-z_bajeer@yeah.net> Signed-off-by: Alistair Francis --- hw/intc/riscv_aplic.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hw/intc/riscv_aplic.c b/hw/intc/riscv_aplic.c index 4fa5f7597b..a1d9fa5085 100644 --- a/hw/intc/riscv_aplic.c +++ b/hw/intc/riscv_aplic.c @@ -628,7 +628,7 @@ static void riscv_aplic_request(void *opaque, int irq, int level) static uint64_t riscv_aplic_read(void *opaque, hwaddr addr, unsigned size) { - uint32_t irq, word, idc; + uint32_t irq, word, idc, sm; RISCVAPLICState *aplic = opaque; /* Reads must be 4 byte words */ @@ -696,6 +696,10 @@ static uint64_t riscv_aplic_read(void *opaque, hwaddr addr, unsigned size) } else if ((APLIC_TARGET_BASE <= addr) && (addr < (APLIC_TARGET_BASE + (aplic->num_irqs - 1) * 4))) { irq = ((addr - APLIC_TARGET_BASE) >> 2) + 1; + sm = aplic->sourcecfg[irq] & APLIC_SOURCECFG_SM_MASK; + if (sm == APLIC_SOURCECFG_SM_INACTIVE) { + return 0; + } return aplic->target[irq]; } else if (!aplic->msimode && (APLIC_IDC_BASE <= addr) && (addr < (APLIC_IDC_BASE + aplic->num_harts * APLIC_IDC_SIZE))) { From e111ffe48b29ca8abd450af9ee5dd71af3f93536 Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 28 Jul 2025 14:06:33 -0300 Subject: [PATCH 2674/2760] linux-user/strace.list: add riscv_hwprobe entry We're missing a strace entry for riscv_hwprobe, and using -strace will report it as "Unknown syscall 258". After this patch we'll have: $ ./build/qemu-riscv64 -strace test_mutex_riscv 110182 riscv_hwprobe(0x7f207efdc700,1,0,0,0,0) = 0 110182 brk(NULL) = 0x0000000000082000 (...) Signed-off-by: Daniel Henrique Barboza Reviewed-by: Richard Henderson Message-ID: <20250728170633.113384-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- linux-user/strace.list | 3 +++ 1 file changed, 3 insertions(+) diff --git a/linux-user/strace.list b/linux-user/strace.list index fdf94ef32a..ab818352a9 100644 --- a/linux-user/strace.list +++ b/linux-user/strace.list @@ -1716,3 +1716,6 @@ { TARGET_NR_clock_gettime64, "clock_gettime64" , NULL, print_clock_gettime64, print_syscall_ret_clock_gettime64 }, #endif +#ifdef TARGET_NR_riscv_hwprobe +{ TARGET_NR_riscv_hwprobe, "riscv_hwprobe" , "%s(%p,%d,%d,%d,%d,%d)", NULL, NULL }, +#endif From 16aa7771afeac422dcf7be2833d5426da6b814fa Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Mon, 14 Jul 2025 10:37:39 -0300 Subject: [PATCH 2675/2760] target/riscv: do not call GETPC() in check_ret_from_m_mode() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GETPC() should always be called from the top level helper, e.g. the first helper that is called by the translation code. We stopped doing that in commit 3157a553ec, and then we introduced problems when unwinding the exceptions being thrown by helper_mret(), as reported by [1]. Call GETPC() at the top level helper and pass the value along. [1] https://gitlab.com/qemu-project/qemu/-/issues/3020 Suggested-by: Richard Henderson Fixes: 3157a553ec ("target/riscv: Add Smrnmi mnret instruction") Closes: https://gitlab.com/qemu-project/qemu/-/issues/3020 Signed-off-by: Daniel Henrique Barboza Reviewed-by: Nutty Liu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-ID: <20250714133739.1248296-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/op_helper.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/target/riscv/op_helper.c b/target/riscv/op_helper.c index 15460bf84b..110292e84d 100644 --- a/target/riscv/op_helper.c +++ b/target/riscv/op_helper.c @@ -355,21 +355,22 @@ target_ulong helper_sret(CPURISCVState *env) } static void check_ret_from_m_mode(CPURISCVState *env, target_ulong retpc, - target_ulong prev_priv) + target_ulong prev_priv, + uintptr_t ra) { if (!(env->priv >= PRV_M)) { - riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_ILLEGAL_INST, ra); } if (!riscv_cpu_allow_16bit_insn(&env_archcpu(env)->cfg, env->priv_ver, env->misa_ext) && (retpc & 0x3)) { - riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_INST_ADDR_MIS, ra); } if (riscv_cpu_cfg(env)->pmp && !pmp_get_num_rules(env) && (prev_priv != PRV_M)) { - riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, GETPC()); + riscv_raise_exception(env, RISCV_EXCP_INST_ACCESS_FAULT, ra); } } static target_ulong ssdbltrp_mxret(CPURISCVState *env, target_ulong mstatus, @@ -394,8 +395,9 @@ target_ulong helper_mret(CPURISCVState *env) target_ulong retpc = env->mepc & get_xepc_mask(env); uint64_t mstatus = env->mstatus; target_ulong prev_priv = get_field(mstatus, MSTATUS_MPP); + uintptr_t ra = GETPC(); - check_ret_from_m_mode(env, retpc, prev_priv); + check_ret_from_m_mode(env, retpc, prev_priv, ra); target_ulong prev_virt = get_field(env->mstatus, MSTATUS_MPV) && (prev_priv != PRV_M); @@ -443,8 +445,9 @@ target_ulong helper_mnret(CPURISCVState *env) target_ulong retpc = env->mnepc; target_ulong prev_priv = get_field(env->mnstatus, MNSTATUS_MNPP); target_ulong prev_virt; + uintptr_t ra = GETPC(); - check_ret_from_m_mode(env, retpc, prev_priv); + check_ret_from_m_mode(env, retpc, prev_priv, ra); prev_virt = get_field(env->mnstatus, MNSTATUS_MNPV) && (prev_priv != PRV_M); From 09ac27a9b59bf87786cb35f7126fb5788b0b4bca Mon Sep 17 00:00:00 2001 From: Daniel Henrique Barboza Date: Thu, 10 Jul 2025 07:05:25 -0300 Subject: [PATCH 2676/2760] riscv: Revert "Generate strided vector loads/stores with tcg nodes." This reverts commit 28c12c1f2f50d7f7f1ebfc587c4777ecd50aac5b. As reported in [1] this commit is breaking Linux vector code, and although a simpler reproducer was provided, the fix itself isn't trivial due to the amount and the nature of the changes. And we really do not want to keep Linux broken while we work on it. The revert will fix Linux and will give us time to do a proper fix. [1] https://mail.gnu.org/archive/html/qemu-devel/2025-07/msg02525.html Signed-off-by: Daniel Henrique Barboza Tested-by: Eric Biggers Message-ID: <20250710100525.372985-1-dbarboza@ventanamicro.com> Signed-off-by: Alistair Francis --- target/riscv/insn_trans/trans_rvv.c.inc | 323 ++++-------------------- 1 file changed, 50 insertions(+), 273 deletions(-) diff --git a/target/riscv/insn_trans/trans_rvv.c.inc b/target/riscv/insn_trans/trans_rvv.c.inc index 610bf9ff30..71f98fb350 100644 --- a/target/riscv/insn_trans/trans_rvv.c.inc +++ b/target/riscv/insn_trans/trans_rvv.c.inc @@ -864,286 +864,32 @@ GEN_VEXT_TRANS(vlm_v, MO_8, vlm_v, ld_us_mask_op, ld_us_mask_check) GEN_VEXT_TRANS(vsm_v, MO_8, vsm_v, st_us_mask_op, st_us_mask_check) /* - * MAXSZ returns the maximum vector size can be operated in bytes, - * which is used in GVEC IR when vl_eq_vlmax flag is set to true - * to accelerate vector operation. - */ -static inline uint32_t MAXSZ(DisasContext *s) -{ - int max_sz = s->cfg_ptr->vlenb << 3; - return max_sz >> (3 - s->lmul); -} - -static inline uint32_t get_log2(uint32_t a) -{ - uint32_t i = 0; - for (; a > 0;) { - a >>= 1; - i++; - } - return i; -} - -typedef void gen_tl_ldst(TCGv, TCGv_ptr, tcg_target_long); - -/* - * Simulate the strided load/store main loop: - * - * for (i = env->vstart; i < env->vl; env->vstart = ++i) { - * k = 0; - * while (k < nf) { - * if (!vm && !vext_elem_mask(v0, i)) { - * vext_set_elems_1s(vd, vma, (i + k * max_elems) * esz, - * (i + k * max_elems + 1) * esz); - * k++; - * continue; - * } - * target_ulong addr = base + stride * i + (k << log2_esz); - * ldst(env, adjust_addr(env, addr), i + k * max_elems, vd, ra); - * k++; - * } - * } - */ -static void gen_ldst_stride_main_loop(DisasContext *s, TCGv dest, uint32_t rs1, - uint32_t rs2, uint32_t vm, uint32_t nf, - gen_tl_ldst *ld_fn, gen_tl_ldst *st_fn, - bool is_load) -{ - TCGv addr = tcg_temp_new(); - TCGv base = get_gpr(s, rs1, EXT_NONE); - TCGv stride = get_gpr(s, rs2, EXT_NONE); - - TCGv i = tcg_temp_new(); - TCGv i_esz = tcg_temp_new(); - TCGv k = tcg_temp_new(); - TCGv k_esz = tcg_temp_new(); - TCGv k_max = tcg_temp_new(); - TCGv mask = tcg_temp_new(); - TCGv mask_offs = tcg_temp_new(); - TCGv mask_offs_64 = tcg_temp_new(); - TCGv mask_elem = tcg_temp_new(); - TCGv mask_offs_rem = tcg_temp_new(); - TCGv vreg = tcg_temp_new(); - TCGv dest_offs = tcg_temp_new(); - TCGv stride_offs = tcg_temp_new(); - - uint32_t max_elems = MAXSZ(s) >> s->sew; - - TCGLabel *start = gen_new_label(); - TCGLabel *end = gen_new_label(); - TCGLabel *start_k = gen_new_label(); - TCGLabel *inc_k = gen_new_label(); - TCGLabel *end_k = gen_new_label(); - - MemOp atomicity = MO_ATOM_NONE; - if (s->sew == 0) { - atomicity = MO_ATOM_NONE; - } else { - atomicity = MO_ATOM_IFALIGN_PAIR; - } - - mark_vs_dirty(s); - - tcg_gen_addi_tl(mask, (TCGv)tcg_env, vreg_ofs(s, 0)); - - /* Start of outer loop. */ - tcg_gen_mov_tl(i, cpu_vstart); - gen_set_label(start); - tcg_gen_brcond_tl(TCG_COND_GE, i, cpu_vl, end); - tcg_gen_shli_tl(i_esz, i, s->sew); - /* Start of inner loop. */ - tcg_gen_movi_tl(k, 0); - gen_set_label(start_k); - tcg_gen_brcond_tl(TCG_COND_GE, k, tcg_constant_tl(nf), end_k); - /* - * If we are in mask agnostic regime and the operation is not unmasked we - * set the inactive elements to 1. - */ - if (!vm && s->vma) { - TCGLabel *active_element = gen_new_label(); - /* (i + k * max_elems) * esz */ - tcg_gen_shli_tl(mask_offs, k, get_log2(max_elems << s->sew)); - tcg_gen_add_tl(mask_offs, mask_offs, i_esz); - - /* - * Check whether the i bit of the mask is 0 or 1. - * - * static inline int vext_elem_mask(void *v0, int index) - * { - * int idx = index / 64; - * int pos = index % 64; - * return (((uint64_t *)v0)[idx] >> pos) & 1; - * } - */ - tcg_gen_shri_tl(mask_offs_64, mask_offs, 3); - tcg_gen_add_tl(mask_offs_64, mask_offs_64, mask); - tcg_gen_ld_i64((TCGv_i64)mask_elem, (TCGv_ptr)mask_offs_64, 0); - tcg_gen_rem_tl(mask_offs_rem, mask_offs, tcg_constant_tl(8)); - tcg_gen_shr_tl(mask_elem, mask_elem, mask_offs_rem); - tcg_gen_andi_tl(mask_elem, mask_elem, 1); - tcg_gen_brcond_tl(TCG_COND_NE, mask_elem, tcg_constant_tl(0), - active_element); - /* - * Set masked-off elements in the destination vector register to 1s. - * Store instructions simply skip this bit as memory ops access memory - * only for active elements. - */ - if (is_load) { - tcg_gen_shli_tl(mask_offs, mask_offs, s->sew); - tcg_gen_add_tl(mask_offs, mask_offs, dest); - st_fn(tcg_constant_tl(-1), (TCGv_ptr)mask_offs, 0); - } - tcg_gen_br(inc_k); - gen_set_label(active_element); - } - /* - * The element is active, calculate the address with stride: - * target_ulong addr = base + stride * i + (k << log2_esz); - */ - tcg_gen_mul_tl(stride_offs, stride, i); - tcg_gen_shli_tl(k_esz, k, s->sew); - tcg_gen_add_tl(stride_offs, stride_offs, k_esz); - tcg_gen_add_tl(addr, base, stride_offs); - /* Calculate the offset in the dst/src vector register. */ - tcg_gen_shli_tl(k_max, k, get_log2(max_elems)); - tcg_gen_add_tl(dest_offs, i, k_max); - tcg_gen_shli_tl(dest_offs, dest_offs, s->sew); - tcg_gen_add_tl(dest_offs, dest_offs, dest); - if (is_load) { - tcg_gen_qemu_ld_tl(vreg, addr, s->mem_idx, MO_LE | s->sew | atomicity); - st_fn((TCGv)vreg, (TCGv_ptr)dest_offs, 0); - } else { - ld_fn((TCGv)vreg, (TCGv_ptr)dest_offs, 0); - tcg_gen_qemu_st_tl(vreg, addr, s->mem_idx, MO_LE | s->sew | atomicity); - } - /* - * We don't execute the load/store above if the element was inactive. - * We jump instead directly to incrementing k and continuing the loop. - */ - if (!vm && s->vma) { - gen_set_label(inc_k); - } - tcg_gen_addi_tl(k, k, 1); - tcg_gen_br(start_k); - /* End of the inner loop. */ - gen_set_label(end_k); - - tcg_gen_addi_tl(i, i, 1); - tcg_gen_mov_tl(cpu_vstart, i); - tcg_gen_br(start); - - /* End of the outer loop. */ - gen_set_label(end); - - return; -} - - -/* - * Set the tail bytes of the strided loads/stores to 1: - * - * for (k = 0; k < nf; ++k) { - * cnt = (k * max_elems + vl) * esz; - * tot = (k * max_elems + max_elems) * esz; - * for (i = cnt; i < tot; i += esz) { - * store_1s(-1, vd[vl+i]); - * } - * } + *** stride load and store */ -static void gen_ldst_stride_tail_loop(DisasContext *s, TCGv dest, uint32_t nf, - gen_tl_ldst *st_fn) -{ - TCGv i = tcg_temp_new(); - TCGv k = tcg_temp_new(); - TCGv tail_cnt = tcg_temp_new(); - TCGv tail_tot = tcg_temp_new(); - TCGv tail_addr = tcg_temp_new(); - - TCGLabel *start = gen_new_label(); - TCGLabel *end = gen_new_label(); - TCGLabel *start_i = gen_new_label(); - TCGLabel *end_i = gen_new_label(); - - uint32_t max_elems_b = MAXSZ(s); - uint32_t esz = 1 << s->sew; - - /* Start of the outer loop. */ - tcg_gen_movi_tl(k, 0); - tcg_gen_shli_tl(tail_cnt, cpu_vl, s->sew); - tcg_gen_movi_tl(tail_tot, max_elems_b); - tcg_gen_add_tl(tail_addr, dest, tail_cnt); - gen_set_label(start); - tcg_gen_brcond_tl(TCG_COND_GE, k, tcg_constant_tl(nf), end); - /* Start of the inner loop. */ - tcg_gen_mov_tl(i, tail_cnt); - gen_set_label(start_i); - tcg_gen_brcond_tl(TCG_COND_GE, i, tail_tot, end_i); - /* store_1s(-1, vd[vl+i]); */ - st_fn(tcg_constant_tl(-1), (TCGv_ptr)tail_addr, 0); - tcg_gen_addi_tl(tail_addr, tail_addr, esz); - tcg_gen_addi_tl(i, i, esz); - tcg_gen_br(start_i); - /* End of the inner loop. */ - gen_set_label(end_i); - /* Update the counts */ - tcg_gen_addi_tl(tail_cnt, tail_cnt, max_elems_b); - tcg_gen_addi_tl(tail_tot, tail_cnt, max_elems_b); - tcg_gen_addi_tl(k, k, 1); - tcg_gen_br(start); - /* End of the outer loop. */ - gen_set_label(end); - - return; -} +typedef void gen_helper_ldst_stride(TCGv_ptr, TCGv_ptr, TCGv, + TCGv, TCGv_env, TCGv_i32); static bool ldst_stride_trans(uint32_t vd, uint32_t rs1, uint32_t rs2, - uint32_t data, DisasContext *s, bool is_load) + uint32_t data, gen_helper_ldst_stride *fn, + DisasContext *s) { - if (!s->vstart_eq_zero) { - return false; - } - - TCGv dest = tcg_temp_new(); - - uint32_t nf = FIELD_EX32(data, VDATA, NF); - uint32_t vm = FIELD_EX32(data, VDATA, VM); - - /* Destination register and mask register */ - tcg_gen_addi_tl(dest, (TCGv)tcg_env, vreg_ofs(s, vd)); - - /* - * Select the appropriate load/tore to retrieve data from the vector - * register given a specific sew. - */ - static gen_tl_ldst * const ld_fns[4] = { - tcg_gen_ld8u_tl, tcg_gen_ld16u_tl, - tcg_gen_ld32u_tl, tcg_gen_ld_tl - }; - - static gen_tl_ldst * const st_fns[4] = { - tcg_gen_st8_tl, tcg_gen_st16_tl, - tcg_gen_st32_tl, tcg_gen_st_tl - }; + TCGv_ptr dest, mask; + TCGv base, stride; + TCGv_i32 desc; - gen_tl_ldst *ld_fn = ld_fns[s->sew]; - gen_tl_ldst *st_fn = st_fns[s->sew]; + dest = tcg_temp_new_ptr(); + mask = tcg_temp_new_ptr(); + base = get_gpr(s, rs1, EXT_NONE); + stride = get_gpr(s, rs2, EXT_NONE); + desc = tcg_constant_i32(simd_desc(s->cfg_ptr->vlenb, + s->cfg_ptr->vlenb, data)); - if (ld_fn == NULL || st_fn == NULL) { - return false; - } + tcg_gen_addi_ptr(dest, tcg_env, vreg_ofs(s, vd)); + tcg_gen_addi_ptr(mask, tcg_env, vreg_ofs(s, 0)); mark_vs_dirty(s); - gen_ldst_stride_main_loop(s, dest, rs1, rs2, vm, nf, ld_fn, st_fn, is_load); - - tcg_gen_movi_tl(cpu_vstart, 0); - - /* - * Set the tail bytes to 1 if tail agnostic: - */ - if (s->vta != 0 && is_load) { - gen_ldst_stride_tail_loop(s, dest, nf, st_fn); - } + fn(dest, mask, base, stride, tcg_env, desc); finalize_rvv_inst(s); return true; @@ -1152,6 +898,16 @@ static bool ldst_stride_trans(uint32_t vd, uint32_t rs1, uint32_t rs2, static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) { uint32_t data = 0; + gen_helper_ldst_stride *fn; + static gen_helper_ldst_stride * const fns[4] = { + gen_helper_vlse8_v, gen_helper_vlse16_v, + gen_helper_vlse32_v, gen_helper_vlse64_v + }; + + fn = fns[eew]; + if (fn == NULL) { + return false; + } uint8_t emul = vext_get_emul(s, eew); data = FIELD_DP32(data, VDATA, VM, a->vm); @@ -1159,7 +915,7 @@ static bool ld_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) data = FIELD_DP32(data, VDATA, NF, a->nf); data = FIELD_DP32(data, VDATA, VTA, s->vta); data = FIELD_DP32(data, VDATA, VMA, s->vma); - return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, s, true); + return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s); } static bool ld_stride_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) @@ -1177,13 +933,23 @@ GEN_VEXT_TRANS(vlse64_v, MO_64, rnfvm, ld_stride_op, ld_stride_check) static bool st_stride_op(DisasContext *s, arg_rnfvm *a, uint8_t eew) { uint32_t data = 0; + gen_helper_ldst_stride *fn; + static gen_helper_ldst_stride * const fns[4] = { + /* masked stride store */ + gen_helper_vsse8_v, gen_helper_vsse16_v, + gen_helper_vsse32_v, gen_helper_vsse64_v + }; uint8_t emul = vext_get_emul(s, eew); data = FIELD_DP32(data, VDATA, VM, a->vm); data = FIELD_DP32(data, VDATA, LMUL, emul); data = FIELD_DP32(data, VDATA, NF, a->nf); + fn = fns[eew]; + if (fn == NULL) { + return false; + } - return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, s, false); + return ldst_stride_trans(a->rd, a->rs1, a->rs2, data, fn, s); } static bool st_stride_check(DisasContext *s, arg_rnfvm* a, uint8_t eew) @@ -1534,6 +1300,17 @@ GEN_LDST_WHOLE_TRANS(vs8r_v, int8_t, 8, false) *** Vector Integer Arithmetic Instructions */ +/* + * MAXSZ returns the maximum vector size can be operated in bytes, + * which is used in GVEC IR when vl_eq_vlmax flag is set to true + * to accelerate vector operation. + */ +static inline uint32_t MAXSZ(DisasContext *s) +{ + int max_sz = s->cfg_ptr->vlenb * 8; + return max_sz >> (3 - s->lmul); +} + static bool opivv_check(DisasContext *s, arg_rmrr *a) { return require_rvv(s) && From 30ef718423e8018723087cd17be0fd9c6dfa2e53 Mon Sep 17 00:00:00 2001 From: Xu Lu Date: Tue, 8 Jul 2025 14:07:20 +0800 Subject: [PATCH 2677/2760] target/riscv: Fix exception type when VU accesses supervisor CSRs When supervisor CSRs are accessed from VU-mode, a virtual instruction exception should be raised instead of an illegal instruction. Fixes: c1fbcecb3a (target/riscv: Fix csr number based privilege checking) Signed-off-by: Xu Lu Reviewed-by: Anup Patel Reviewed-by: Nutty Liu Message-ID: <20250708060720.7030-1-luxu.kernel@bytedance.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 8631be97c5..9bebfae3f0 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -5577,7 +5577,7 @@ static inline RISCVException riscv_csrrw_check(CPURISCVState *env, csr_priv = get_field(csrno, 0x300); if (!env->debugger && (effective_priv < csr_priv)) { - if (csr_priv == (PRV_S + 1) && env->virt_enabled) { + if (csr_priv <= (PRV_S + 1) && env->virt_enabled) { return RISCV_EXCP_VIRT_INSTRUCTION_FAULT; } return RISCV_EXCP_ILLEGAL_INST; From e443ba03361b63218e6c3aa4f73d2cb5b9b1d372 Mon Sep 17 00:00:00 2001 From: Jay Chang Date: Tue, 1 Jul 2025 11:00:20 +0800 Subject: [PATCH 2678/2760] target/riscv: Restrict mideleg/medeleg/medelegh access to S-mode harts RISC-V Privileged Spec states: "In harts with S-mode, the medeleg and mideleg registers must exist, and setting a bit in medeleg or mideleg will delegate the corresponding trap , when occurring in S-mode or U-mode, to the S-mode trap handler. In harts without S-mode, the medeleg and mideleg registers should not exist." Add smode predicate to ensure these CSRs are only accessible when S-mode is supported. Reviewed-by: Frank Chang Reviewed-by: Alistair Francis Signed-off-by: Jay Chang Reviewed-by: Nutty Liu Message-ID: <20250701030021.99218-2-jay.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 9bebfae3f0..5a6de07486 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -5862,8 +5862,8 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { NULL, read_mstatus_i128 }, [CSR_MISA] = { "misa", any, read_misa, write_misa, NULL, read_misa_i128 }, - [CSR_MIDELEG] = { "mideleg", any, NULL, NULL, rmw_mideleg }, - [CSR_MEDELEG] = { "medeleg", any, read_medeleg, write_medeleg }, + [CSR_MIDELEG] = { "mideleg", smode, NULL, NULL, rmw_mideleg }, + [CSR_MEDELEG] = { "medeleg", smode, read_medeleg, write_medeleg }, [CSR_MIE] = { "mie", any, NULL, NULL, rmw_mie }, [CSR_MTVEC] = { "mtvec", any, read_mtvec, write_mtvec }, [CSR_MCOUNTEREN] = { "mcounteren", umode, read_mcounteren, @@ -5871,7 +5871,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MSTATUSH] = { "mstatush", any32, read_mstatush, write_mstatush }, - [CSR_MEDELEGH] = { "medelegh", any32, read_zero, write_ignore, + [CSR_MEDELEGH] = { "medelegh", smode32, read_zero, write_ignore, .min_priv_ver = PRIV_VERSION_1_13_0 }, [CSR_HEDELEGH] = { "hedelegh", hmode32, read_hedelegh, write_hedelegh, .min_priv_ver = PRIV_VERSION_1_13_0 }, From 86bc3a0abf10072081cddd8dff25aa72c60e67b8 Mon Sep 17 00:00:00 2001 From: Jay Chang Date: Tue, 1 Jul 2025 11:00:21 +0800 Subject: [PATCH 2679/2760] target/riscv: Restrict midelegh access to S-mode harts RISC-V AIA Spec states: "For a machine-level environment, extension Smaia encompasses all added CSRs and all modifications to interrupt response behavior that the AIA specifies for a hart, over all privilege levels. For a supervisor-level environment, extension Ssaia is essentially the same as Smaia except excluding the machine-level CSRs and behavior not directly visible to supervisor level." Since midelegh is an AIA machine-mode CSR, add Smaia extension check in aia_smode32 predicate. Reviewed-by: Frank Chang Reviewed-by: Alistair Francis Signed-off-by: Jay Chang Reviewed-by: Nutty Liu Message-ID: <20250701030021.99218-3-jay.chang@sifive.com> Signed-off-by: Alistair Francis --- target/riscv/csr.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/target/riscv/csr.c b/target/riscv/csr.c index 5a6de07486..8842e07a73 100644 --- a/target/riscv/csr.c +++ b/target/riscv/csr.c @@ -374,8 +374,11 @@ static RISCVException aia_smode(CPURISCVState *env, int csrno) static RISCVException aia_smode32(CPURISCVState *env, int csrno) { int ret; + int csr_priv = get_field(csrno, 0x300); - if (!riscv_cpu_cfg(env)->ext_ssaia) { + if (csr_priv == PRV_M && !riscv_cpu_cfg(env)->ext_smaia) { + return RISCV_EXCP_ILLEGAL_INST; + } else if (!riscv_cpu_cfg(env)->ext_ssaia) { return RISCV_EXCP_ILLEGAL_INST; } @@ -5911,7 +5914,7 @@ riscv_csr_operations csr_ops[CSR_TABLE_SIZE] = { [CSR_MVIP] = { "mvip", aia_any, NULL, NULL, rmw_mvip }, /* Machine-Level High-Half CSRs (AIA) */ - [CSR_MIDELEGH] = { "midelegh", aia_any32, NULL, NULL, rmw_midelegh }, + [CSR_MIDELEGH] = { "midelegh", aia_smode32, NULL, NULL, rmw_midelegh }, [CSR_MIEH] = { "mieh", aia_any32, NULL, NULL, rmw_mieh }, [CSR_MVIENH] = { "mvienh", aia_any32, NULL, NULL, rmw_mvienh }, [CSR_MVIPH] = { "mviph", aia_any32, NULL, NULL, rmw_mviph }, From caab7ac83507e3e9a5fe2f37be5cfa759e766ba2 Mon Sep 17 00:00:00 2001 From: Bibo Mao Date: Mon, 14 Jul 2025 09:54:46 +0800 Subject: [PATCH 2680/2760] target/loongarch: Fix valid virtual address checking On LoongArch64 system, the high 32 bit of 64 bit virtual address should be 0x00000[0-7]yyy or 0xffff8yyy. The bit from 47 to 63 should be all 0 or all 1. Function get_physical_address() only checks bit 48 to 63, there will be problem with the following test case. On physical machine, there is bus error report and program exits abnormally. However on qemu TCG system emulation mode, the program runs normally. The virtual address 0xffff000000000000ULL + addr and addr are treated the same on TLB entry checking. This patch fixes this issue. void main() { void *addr, *addr1; int val; addr = malloc(100); *(int *)addr = 1; addr1 = 0xffff000000000000ULL + addr; val = *(int *)addr1; printf("val %d \n", val); } Cc: qemu-stable@nongnu.org Signed-off-by: Bibo Mao Acked-by: Song Gao Reviewed-by: Song Gao Message-ID: <20250714015446.746163-1-maobibo@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/cpu_helper.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/target/loongarch/cpu_helper.c b/target/loongarch/cpu_helper.c index e172b11ce1..b5f732f15b 100644 --- a/target/loongarch/cpu_helper.c +++ b/target/loongarch/cpu_helper.c @@ -196,8 +196,8 @@ int get_physical_address(CPULoongArchState *env, hwaddr *physical, } /* Check valid extension */ - addr_high = sextract64(address, TARGET_VIRT_ADDR_SPACE_BITS, 16); - if (!(addr_high == 0 || addr_high == -1)) { + addr_high = (int64_t)address >> (TARGET_VIRT_ADDR_SPACE_BITS - 1); + if (!(addr_high == 0 || addr_high == -1ULL)) { return TLBRET_BADADDR; } From 31995cc4087123a13e9345153e0c39ffb44b9277 Mon Sep 17 00:00:00 2001 From: Song Gao Date: Fri, 25 Jul 2025 16:12:13 +0800 Subject: [PATCH 2681/2760] hw/intc/loongarch_ipi: Fix start fail with smp cpu < smp maxcpus on KVM QEMU start failed when smp cpu < smp maxcpus , because qemu send a NULL cpu to KVM, this patch adds a check for kvm_ipi_access_regs() to fix it. run with '-smp 1,maxcpus=4,sockets=4,cores=1,threads=1' we got: Unexpected error in kvm_device_access() at ../accel/kvm/kvm-all.c:3477: qemu-system-loongarch64: KVM_SET_DEVICE_ATTR failed: Group 1073741825 attr 0x0000000000010000: Invalid argument Signed-off-by: Song Gao Reviewed-by: Bibo Mao Message-ID: <20250725081213.3867592-1-gaosong@loongson.cn> --- hw/intc/loongarch_ipi_kvm.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/hw/intc/loongarch_ipi_kvm.c b/hw/intc/loongarch_ipi_kvm.c index 4cb3acc921..dd4c367abf 100644 --- a/hw/intc/loongarch_ipi_kvm.c +++ b/hw/intc/loongarch_ipi_kvm.c @@ -23,36 +23,41 @@ static void kvm_ipi_access_regs(void *opaque, bool write) LoongarchIPIState *lis = LOONGARCH_IPI(opaque); IPICore *core; uint64_t attr; - int cpu, fd = lis->dev_fd; + int i, cpu_index, fd = lis->dev_fd; if (fd == 0) { return; } - for (cpu = 0; cpu < ipi->num_cpu; cpu++) { - core = &ipi->cpu[cpu]; - attr = (cpu << 16) | CORE_STATUS_OFF; + for (i = 0; i < ipi->num_cpu; i++) { + core = &ipi->cpu[i]; + if (core->cpu == NULL) { + continue; + } + cpu_index = i; + + attr = (cpu_index << 16) | CORE_STATUS_OFF; kvm_ipi_access_reg(fd, attr, &core->status, write); - attr = (cpu << 16) | CORE_EN_OFF; + attr = (cpu_index << 16) | CORE_EN_OFF; kvm_ipi_access_reg(fd, attr, &core->en, write); - attr = (cpu << 16) | CORE_SET_OFF; + attr = (cpu_index << 16) | CORE_SET_OFF; kvm_ipi_access_reg(fd, attr, &core->set, write); - attr = (cpu << 16) | CORE_CLEAR_OFF; + attr = (cpu_index << 16) | CORE_CLEAR_OFF; kvm_ipi_access_reg(fd, attr, &core->clear, write); - attr = (cpu << 16) | CORE_BUF_20; + attr = (cpu_index << 16) | CORE_BUF_20; kvm_ipi_access_reg(fd, attr, &core->buf[0], write); - attr = (cpu << 16) | CORE_BUF_28; + attr = (cpu_index << 16) | CORE_BUF_28; kvm_ipi_access_reg(fd, attr, &core->buf[2], write); - attr = (cpu << 16) | CORE_BUF_30; + attr = (cpu_index << 16) | CORE_BUF_30; kvm_ipi_access_reg(fd, attr, &core->buf[4], write); - attr = (cpu << 16) | CORE_BUF_38; + attr = (cpu_index << 16) | CORE_BUF_38; kvm_ipi_access_reg(fd, attr, &core->buf[6], write); } } From cd9f752fee75238f842a91be1146c988bd16a010 Mon Sep 17 00:00:00 2001 From: Alex Richardson Date: Fri, 25 Jul 2025 10:01:36 -0700 Subject: [PATCH 2682/2760] target/arm: add support for 64-bit PMCCNTR in AArch32 mode In the PMUv3, a new AArch32 64-bit (MCRR/MRRC) accessor for the PMCCNTR was added. In QEMU we forgot to implement this, so only provide the 32-bit accessor. Since we have a 64-bit PMCCNTR sysreg for AArch64, adding the 64-bit AArch32 version is easy. We add the PMCCNTR to the v8_cp_reginfo because PMUv3 was added in the ARMv8 architecture. This is consistent with how we handle the existing PMCCNTR support, where we always implement it for all v7 CPUs. This is arguably something we should clean up so it is gated on ARM_FEATURE_PMU and/or an ID register check for the relevant PMU version, but we should do that as its own tidyup rather than being inconsistent between this PMCCNTR accessor and the others. Since the register name is the same as the 32-bit PMCCNTR, we set ARM_CP_NO_GDB on the 32-bit one to avoid generating an invalid GDB XML. See https://developer.arm.com/documentation/ddi0601/2024-06/AArch32-Registers/PMCCNTR--Performance-Monitors-Cycle-Count-Register?lang=en Note for potential backporting: * this code in cpregs-pmu.c will be in helper.c on stable branches that don't have commit ae2086426d37 Cc: qemu-stable@nongnu.org Signed-off-by: Alex Richardson Message-id: 20250725170136.145116-1-alexrichardson@google.com Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/cpregs-pmu.c | 29 ++++++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/target/arm/cpregs-pmu.c b/target/arm/cpregs-pmu.c index 0f295b1376..9c4431c18b 100644 --- a/target/arm/cpregs-pmu.c +++ b/target/arm/cpregs-pmu.c @@ -1067,11 +1067,6 @@ static const ARMCPRegInfo v7_pm_reginfo[] = { .fgt = FGT_PMSELR_EL0, .fieldoffset = offsetof(CPUARMState, cp15.c9_pmselr), .writefn = pmselr_write, .raw_writefn = raw_write, }, - { .name = "PMCCNTR", .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, - .access = PL0_RW, .resetvalue = 0, .type = ARM_CP_ALIAS | ARM_CP_IO, - .fgt = FGT_PMCCNTR_EL0, - .readfn = pmccntr_read, .writefn = pmccntr_write32, - .accessfn = pmreg_access_ccntr }, { .name = "PMCCNTR_EL0", .state = ARM_CP_STATE_AA64, .opc0 = 3, .opc1 = 3, .crn = 9, .crm = 13, .opc2 = 0, .access = PL0_RW, .accessfn = pmreg_access_ccntr, @@ -1211,6 +1206,23 @@ void define_pm_cpregs(ARMCPU *cpu) define_one_arm_cp_reg(cpu, &pmcr); define_one_arm_cp_reg(cpu, &pmcr64); define_arm_cp_regs(cpu, v7_pm_reginfo); + /* + * 32-bit AArch32 PMCCNTR. We don't expose this to GDB if the + * new-in-v8 PMUv3 64-bit AArch32 PMCCNTR register is implemented + * (as that will provide the GDB user's view of "PMCCNTR"). + */ + ARMCPRegInfo pmccntr = { + .name = "PMCCNTR", + .cp = 15, .crn = 9, .crm = 13, .opc1 = 0, .opc2 = 0, + .access = PL0_RW, .accessfn = pmreg_access_ccntr, + .resetvalue = 0, .type = ARM_CP_ALIAS | ARM_CP_IO, + .fgt = FGT_PMCCNTR_EL0, + .readfn = pmccntr_read, .writefn = pmccntr_write32, + }; + if (arm_feature(env, ARM_FEATURE_V8)) { + pmccntr.type |= ARM_CP_NO_GDB; + } + define_one_arm_cp_reg(cpu, &pmccntr); for (unsigned i = 0, pmcrn = pmu_num_counters(env); i < pmcrn; i++) { g_autofree char *pmevcntr_name = g_strdup_printf("PMEVCNTR%d", i); @@ -1276,6 +1288,13 @@ void define_pm_cpregs(ARMCPU *cpu) .access = PL0_R, .accessfn = pmreg_access, .type = ARM_CP_CONST, .fgt = FGT_PMCEIDN_EL0, .resetvalue = cpu->pmceid1 }, + /* AArch32 64-bit PMCCNTR view: added in PMUv3 with Armv8 */ + { .name = "PMCCNTR", .state = ARM_CP_STATE_AA32, + .cp = 15, .crm = 9, .opc1 = 0, + .access = PL0_RW, .accessfn = pmreg_access_ccntr, .resetvalue = 0, + .type = ARM_CP_ALIAS | ARM_CP_IO | ARM_CP_64BIT, + .fgt = FGT_PMCCNTR_EL0, .readfn = pmccntr_read, + .writefn = pmccntr_write, }, }; define_arm_cp_regs(cpu, v8_pm_reginfo); } From 6fcf5ebafad65adc19a616260ca7dc90005785d1 Mon Sep 17 00:00:00 2001 From: Jonah Palmer Date: Mon, 21 Jul 2025 15:02:08 +0000 Subject: [PATCH 2683/2760] virtio: fix off-by-one and invalid access in virtqueue_ordered_fill Commit b44135daa372 introduced virtqueue_ordered_fill for VIRTIO_F_IN_ORDER support but had a few issues: * Conditional while loop used 'steps <= max_steps' but should've been 'steps < max_steps' since reaching steps == max_steps would indicate that we didn't find an element, which is an error. Without this change, the code would attempt to read invalid data at an index outside of our search range. * Incremented 'steps' using the next chain's ndescs instead of the current one. This patch corrects the loop bounds and synchronizes 'steps' and index increments. We also add a defensive sanity check against malicious or invalid descriptor counts to avoid a potential infinite loop and DoS. Fixes: b44135daa372 ("virtio: virtqueue_ordered_fill - VIRTIO_F_IN_ORDER support") Reported-by: terrynini Signed-off-by: Jonah Palmer Message-Id: <20250721150208.2409779-1-jonah.palmer@oracle.com> Reviewed-by: Si-Wei Liu Acked-by: Jason Wang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/virtio.c | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/hw/virtio/virtio.c b/hw/virtio/virtio.c index 2ab1d20769..9a81ad912e 100644 --- a/hw/virtio/virtio.c +++ b/hw/virtio/virtio.c @@ -938,18 +938,18 @@ static void virtqueue_packed_fill(VirtQueue *vq, const VirtQueueElement *elem, static void virtqueue_ordered_fill(VirtQueue *vq, const VirtQueueElement *elem, unsigned int len) { - unsigned int i, steps, max_steps; + unsigned int i, steps, max_steps, ndescs; i = vq->used_idx % vq->vring.num; steps = 0; /* - * We shouldn't need to increase 'i' by more than the distance - * between used_idx and last_avail_idx. + * We shouldn't need to increase 'i' by more than or equal to + * the distance between used_idx and last_avail_idx (max_steps). */ max_steps = (vq->last_avail_idx - vq->used_idx) % vq->vring.num; /* Search for element in vq->used_elems */ - while (steps <= max_steps) { + while (steps < max_steps) { /* Found element, set length and mark as filled */ if (vq->used_elems[i].index == elem->index) { vq->used_elems[i].len = len; @@ -957,8 +957,18 @@ static void virtqueue_ordered_fill(VirtQueue *vq, const VirtQueueElement *elem, break; } - i += vq->used_elems[i].ndescs; - steps += vq->used_elems[i].ndescs; + ndescs = vq->used_elems[i].ndescs; + + /* Defensive sanity check */ + if (unlikely(ndescs == 0 || ndescs > vq->vring.num)) { + qemu_log_mask(LOG_GUEST_ERROR, + "%s: %s invalid ndescs %u at position %u\n", + __func__, vq->vdev->name, ndescs, i); + return; + } + + i += ndescs; + steps += ndescs; if (i >= vq->vring.num) { i -= vq->vring.num; From c1997099dc26d95eb9f2249f2894aabf4cf0bf8b Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Thu, 24 Jul 2025 14:59:27 +0200 Subject: [PATCH 2684/2760] vhost: Do not abort on log-start error Commit 3688fec8923 ("memory: Add Error** argument to .log_global_start() handler") enabled vhost_log_global_start() to return a proper error, but did not change it to do so; instead, it still aborts the whole process on error. This crash can be reproduced by e.g. killing a virtiofsd daemon before initiating migration. In such a case, qemu should not crash, but just make the attempted migration fail. Buglink: https://issues.redhat.com/browse/RHEL-94534 Reported-by: Tingting Mao Signed-off-by: Hanna Czenczek Message-Id: <20250724125928.61045-2-hreitz@redhat.com> Reviewed-by: Manos Pitsidianakis Reviewed-by: Stefano Garzarella Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index c30ea1156e..05ad5de629 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1110,7 +1110,8 @@ static bool vhost_log_global_start(MemoryListener *listener, Error **errp) r = vhost_migration_log(listener, true); if (r < 0) { - abort(); + error_setg_errno(errp, -r, "vhost: Failed to start logging"); + return false; } return true; } From d63c388dadb7717f6391e1bb7f11728c0c1a3e36 Mon Sep 17 00:00:00 2001 From: Hanna Czenczek Date: Thu, 24 Jul 2025 14:59:28 +0200 Subject: [PATCH 2685/2760] vhost: Do not abort on log-stop error Failing to stop logging in a vhost device is not exactly fatal. We can log such an error, but there is no need to abort the whole qemu process because of it. Signed-off-by: Hanna Czenczek Message-Id: <20250724125928.61045-3-hreitz@redhat.com> Reviewed-by: Manos Pitsidianakis Reviewed-by: Stefano Garzarella Tested-by: Lei Yang Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/virtio/vhost.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c index 05ad5de629..6557c58d12 100644 --- a/hw/virtio/vhost.c +++ b/hw/virtio/vhost.c @@ -1122,7 +1122,8 @@ static void vhost_log_global_stop(MemoryListener *listener) r = vhost_migration_log(listener, false); if (r < 0) { - abort(); + /* Not fatal, so report it, but take no further action */ + warn_report("vhost: Failed to stop logging"); } } From 6071d13c6a37493a6b26e1609b09a98aa058038a Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 27 Jul 2025 15:22:36 +0900 Subject: [PATCH 2686/2760] virtio-net: Fix VLAN filter table reset timing Problem ------- The expected initial state of the table depends on feature negotiation: With VIRTIO_NET_F_CTRL_VLAN: The table must be empty in accordance with the specification. Without VIRTIO_NET_F_CTRL_VLAN: The table must be filled to permit all VLAN traffic. Prior to commit 06b636a1e2ad ("virtio-net: do not reset vlan filtering at set_features"), virtio_net_set_features() always reset the VLAN table. That commit changed the behavior to skip table reset when VIRTIO_NET_F_CTRL_VLAN was negotiated, assuming the table would be properly cleared during device reset and remain stable. However, this assumption breaks when a driver renegotiates features: 1. Initial negotiation without VIRTIO_NET_F_CTRL_VLAN (table filled) 2. Renegotiation with VIRTIO_NET_F_CTRL_VLAN (table will not be cleared) The problem was exacerbated by commit 0caed25cd171 ("virtio: Call set_features during reset"), which triggered virtio_net_set_features() during device reset, exposing the bug whenever VIRTIO_NET_F_CTRL_VLAN was negotiated after a device reset. Solution -------- Fix the issue by initializing the table when virtio_net_set_features() is called to change the VIRTIO_NET_F_CTRL_VLAN bit of vdev->guest_features. This approach ensures the correct table state regardless of feature negotiation sequence by performing initialization in virtio_net_set_features() as QEMU did prior to commit 06b636a1e2ad ("virtio-net: do not reset vlan filtering at set_features"). This change still preserves the goal of the commit, which was to avoid resetting the table during migration, by checking whether the VIRTIO_NET_F_CTRL_VLAN bit of vdev->guest_features is being changed; vdev->guest_features is set before virtio_net_set_features() gets called during migration. It also avoids resetting the table when the driver sets a feature bitmask with no change for the VIRTIO_NET_F_CTRL_VLAN bit, which makes the operation idempotent and its semantics cleaner. Additionally, this change ensures the table is initialized after feature negotiation and before the DRIVER_OK status bit being set for compatibility with the Linux driver before commit 50c0ada627f5 ("virtio-net: fix race between ndo_open() and virtio_device_ready()"), which did not ensure to set the DRIVER_OK status bit before modifying the table. Fixes: 06b636a1e2ad ("virtio-net: do not reset vlan filtering at set_features") Cc: qemu-stable@nongnu.org Reported-by: Konstantin Shkolnyy Signed-off-by: Akihiko Odaki Tested-by: Konstantin Shkolnyy Tested-by: Lei Yang Message-Id: <20250727-vlan-v3-1-bbee738619b1@rsg.ci.i.u-tokyo.ac.jp> Tested-by: Konstantin Shkolnyy Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/net/virtio-net.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hw/net/virtio-net.c b/hw/net/virtio-net.c index c4c49b0f9c..6b5b5dace3 100644 --- a/hw/net/virtio-net.c +++ b/hw/net/virtio-net.c @@ -929,8 +929,9 @@ static void virtio_net_set_features(VirtIODevice *vdev, uint64_t features) vhost_net_save_acked_features(nc->peer); } - if (!virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN)) { - memset(n->vlans, 0xff, MAX_VLAN >> 3); + if (virtio_has_feature(vdev->guest_features ^ features, VIRTIO_NET_F_CTRL_VLAN)) { + bool vlan = virtio_has_feature(features, VIRTIO_NET_F_CTRL_VLAN); + memset(n->vlans, vlan ? 0 : 0xff, MAX_VLAN >> 3); } if (virtio_has_feature(features, VIRTIO_NET_F_STANDBY)) { @@ -3942,6 +3943,7 @@ static void virtio_net_device_realize(DeviceState *dev, Error **errp) n->mac_table.macs = g_malloc0(MAC_TABLE_ENTRIES * ETH_ALEN); n->vlans = g_malloc0(MAX_VLAN >> 3); + memset(n->vlans, 0xff, MAX_VLAN >> 3); nc = qemu_get_queue(n->nic); nc->rxfilter_notify_enabled = 1; @@ -4041,7 +4043,6 @@ static void virtio_net_reset(VirtIODevice *vdev) memset(n->mac_table.macs, 0, MAC_TABLE_ENTRIES * ETH_ALEN); memcpy(&n->mac[0], &n->nic->conf->macaddr, sizeof(n->mac)); qemu_format_nic_info_str(qemu_get_queue(n->nic), n->mac); - memset(n->vlans, 0, MAX_VLAN >> 3); /* Flush any async TX */ for (i = 0; i < n->max_queue_pairs; i++) { From cad9aa6fbdccd95e56e10cfa57c354a20a333717 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 27 Jul 2025 15:50:08 +0900 Subject: [PATCH 2687/2760] pcie_sriov: Fix configuration and state synchronization Fix issues in PCIe SR-IOV configuration register handling that caused inconsistent internal state due to improper write mask handling and incorrect migration behavior. Two main problems were identified: 1. VF Enable bit write mask handling: pcie_sriov_config_write() incorrectly assumed that its val parameter was already masked, causing it to ignore the actual write mask. This led to the VF Enable bit being processed even when masked, resulting in incorrect VF registration/unregistration. It is identified as CVE-2025-54567. 2. Migration state inconsistency: pcie_sriov_pf_post_load() unconditionally called register_vfs() regardless of the VF Enable bit state, creating inconsistent internal state when VFs should not be enabled. Additionally, it failed to properly update the NumVFs write mask based on the current configuration. It is identified as CVE-2025-54566. Root cause analysis revealed that both functions relied on incorrect special-case assumptions instead of properly reading and consuming the actual configuration values. This change introduces a unified consume_config() function that reads actual configuration values and synchronize the internal state without special-case assumptions. The solution only adds register read overhead in non-hot-path code while ensuring correct SR-IOV state management across configuration writes and migration scenarios. Fixes: 5e7dd17e4348 ("pcie_sriov: Remove num_vfs from PCIESriovPF") Fixes: f9efcd47110d ("pcie_sriov: Register VFs after migration") Fixes: CVE-2025-54566 Fixes: CVE-2025-54567 Cc: qemu-stable@nongnu.org Reported-by: Corentin BAYET Signed-off-by: Akihiko Odaki Message-Id: <20250727-wmask-v2-1-394910b1c0b6@rsg.ci.i.u-tokyo.ac.jp> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/pci/pcie_sriov.c | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/hw/pci/pcie_sriov.c b/hw/pci/pcie_sriov.c index 3ad18744f4..8a4bf0d6f7 100644 --- a/hw/pci/pcie_sriov.c +++ b/hw/pci/pcie_sriov.c @@ -64,6 +64,27 @@ static void unregister_vfs(PCIDevice *dev) pci_set_word(dev->wmask + dev->exp.sriov_cap + PCI_SRIOV_NUM_VF, 0xffff); } +static void consume_config(PCIDevice *dev) +{ + uint8_t *cfg = dev->config + dev->exp.sriov_cap; + + if (pci_get_word(cfg + PCI_SRIOV_CTRL) & PCI_SRIOV_CTRL_VFE) { + register_vfs(dev); + } else { + uint8_t *wmask = dev->wmask + dev->exp.sriov_cap; + uint16_t num_vfs = pci_get_word(cfg + PCI_SRIOV_NUM_VF); + uint16_t wmask_val = PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI; + + unregister_vfs(dev); + + if (num_vfs <= pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)) { + wmask_val |= PCI_SRIOV_CTRL_VFE; + } + + pci_set_word(wmask + PCI_SRIOV_CTRL, wmask_val); + } +} + static bool pcie_sriov_pf_init_common(PCIDevice *dev, uint16_t offset, uint16_t vf_dev_id, uint16_t init_vfs, uint16_t total_vfs, uint16_t vf_offset, @@ -416,30 +437,13 @@ void pcie_sriov_config_write(PCIDevice *dev, uint32_t address, trace_sriov_config_write(dev->name, PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn), off, val, len); - if (range_covers_byte(off, len, PCI_SRIOV_CTRL)) { - if (val & PCI_SRIOV_CTRL_VFE) { - register_vfs(dev); - } else { - unregister_vfs(dev); - } - } else if (range_covers_byte(off, len, PCI_SRIOV_NUM_VF)) { - uint8_t *cfg = dev->config + sriov_cap; - uint8_t *wmask = dev->wmask + sriov_cap; - uint16_t num_vfs = pci_get_word(cfg + PCI_SRIOV_NUM_VF); - uint16_t wmask_val = PCI_SRIOV_CTRL_MSE | PCI_SRIOV_CTRL_ARI; - - if (num_vfs <= pci_get_word(cfg + PCI_SRIOV_TOTAL_VF)) { - wmask_val |= PCI_SRIOV_CTRL_VFE; - } - - pci_set_word(wmask + PCI_SRIOV_CTRL, wmask_val); - } + consume_config(dev); } void pcie_sriov_pf_post_load(PCIDevice *dev) { if (dev->exp.sriov_cap) { - register_vfs(dev); + consume_config(dev); } } From 96c75abc872c1a90c8e1f4a4d2ed35e89a1befe9 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 1 Aug 2025 11:35:02 +0530 Subject: [PATCH 2688/2760] hw/i386/amd_iommu: Fix MMIO register write tracing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Define separate functions to trace MMIO write accesses instead of using `trace_amdvi_mmio_read()` for both read and write. Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20250801060507.3382-2-sarunkod@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 5a24c17548..7fb0bb68f0 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -592,18 +592,31 @@ static void amdvi_cmdbuf_run(AMDVIState *s) } } -static void amdvi_mmio_trace(hwaddr addr, unsigned size) +static inline uint8_t amdvi_mmio_get_index(hwaddr addr) { uint8_t index = (addr & ~0x2000) / 8; if ((addr & 0x2000)) { /* high table */ index = index >= AMDVI_MMIO_REGS_HIGH ? AMDVI_MMIO_REGS_HIGH : index; - trace_amdvi_mmio_read(amdvi_mmio_high[index], addr, size, addr & ~0x07); } else { index = index >= AMDVI_MMIO_REGS_LOW ? AMDVI_MMIO_REGS_LOW : index; - trace_amdvi_mmio_read(amdvi_mmio_low[index], addr, size, addr & ~0x07); } + + return index; +} + +static void amdvi_mmio_trace_read(hwaddr addr, unsigned size) +{ + uint8_t index = amdvi_mmio_get_index(addr); + trace_amdvi_mmio_read(amdvi_mmio_low[index], addr, size, addr & ~0x07); +} + +static void amdvi_mmio_trace_write(hwaddr addr, unsigned size, uint64_t val) +{ + uint8_t index = amdvi_mmio_get_index(addr); + trace_amdvi_mmio_write(amdvi_mmio_low[index], addr, size, val, + addr & ~0x07); } static uint64_t amdvi_mmio_read(void *opaque, hwaddr addr, unsigned size) @@ -623,7 +636,7 @@ static uint64_t amdvi_mmio_read(void *opaque, hwaddr addr, unsigned size) } else if (size == 8) { val = amdvi_readq(s, addr); } - amdvi_mmio_trace(addr, size); + amdvi_mmio_trace_read(addr, size); return val; } @@ -770,7 +783,7 @@ static void amdvi_mmio_write(void *opaque, hwaddr addr, uint64_t val, return; } - amdvi_mmio_trace(addr, size); + amdvi_mmio_trace_write(addr, size, val); switch (addr & ~0x07) { case AMDVI_MMIO_CONTROL: amdvi_mmio_reg_write(s, size, val, addr); From 47d50cc421b832650822d73431d920bb8a80bc38 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 1 Aug 2025 11:35:03 +0530 Subject: [PATCH 2689/2760] hw/i386/amd_iommu: Remove unused and wrongly set ats_enabled field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ats_enabled field is set using HTTUNEN, which is wrong. Fix this by removing the field as it is never used. MST: includes a tweak suggested by Philippe Fixes: d29a09ca68428 ("hw/i386: Introduce AMD IOMMU") Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Message-Id: <20250801060507.3382-3-sarunkod@amd.com> Message-ID: <948a6ac3-ded9-475b-8c45-9d36220b442b@linaro.org> Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 5 ++--- hw/i386/amd_iommu.h | 1 - 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 7fb0bb68f0..037e78056d 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -646,7 +646,6 @@ static void amdvi_handle_control_write(AMDVIState *s) unsigned long control = amdvi_readq(s, AMDVI_MMIO_CONTROL); s->enabled = !!(control & AMDVI_MMIO_CONTROL_AMDVIEN); - s->ats_enabled = !!(control & AMDVI_MMIO_CONTROL_HTTUNEN); s->evtlog_enabled = s->enabled && !!(control & AMDVI_MMIO_CONTROL_EVENTLOGEN); @@ -1555,7 +1554,6 @@ static void amdvi_init(AMDVIState *s) s->excl_allow = false; s->mmio_enabled = false; s->enabled = false; - s->ats_enabled = false; s->cmdbuf_enabled = false; /* reset MMIO */ @@ -1626,7 +1624,8 @@ static const VMStateDescription vmstate_amdvi_sysbus_migratable = { /* Updated in amdvi_handle_control_write() */ VMSTATE_BOOL(enabled, AMDVIState), VMSTATE_BOOL(ga_enabled, AMDVIState), - VMSTATE_BOOL(ats_enabled, AMDVIState), + /* bool ats_enabled is obsolete */ + VMSTATE_UNUSED(1), /* was ats_enabled */ VMSTATE_BOOL(cmdbuf_enabled, AMDVIState), VMSTATE_BOOL(completion_wait_intr, AMDVIState), VMSTATE_BOOL(evtlog_enabled, AMDVIState), diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 8b42913ed8..67078c6f1e 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -322,7 +322,6 @@ struct AMDVIState { uint64_t mmio_addr; bool enabled; /* IOMMU enabled */ - bool ats_enabled; /* address translation enabled */ bool cmdbuf_enabled; /* command buffer enabled */ bool evtlog_enabled; /* event log enabled */ bool excl_enabled; From a7842d94067cddc80b47ac42fb6e49e2fc02a3c5 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 1 Aug 2025 11:35:04 +0530 Subject: [PATCH 2690/2760] hw/i386/amd_iommu: Move IOAPIC memory region initialization to the end Setting up IOAPIC memory region requires mr_sys and mr_ir. Currently these two memory regions are setup after the initializing the IOAPIC memory region, which cause `amdvi_host_dma_iommu()` to use unitialized mr_sys and mr_ir. Move the IOAPIC memory region initialization to the end in order to use the mr_sys and mr_ir regions after they are fully initialized. Fixes: 577c470f4326 ("x86_iommu/amd: Prepare for interrupt remap support") Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Message-Id: <20250801060507.3382-4-sarunkod@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 037e78056d..7308611bf1 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -1698,9 +1698,6 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) s->iotlb = g_hash_table_new_full(amdvi_uint64_hash, amdvi_uint64_equal, g_free, g_free); - /* Pseudo address space under root PCI bus. */ - x86ms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID); - /* set up MMIO */ memory_region_init_io(&s->mr_mmio, OBJECT(s), &mmio_mem_ops, s, "amdvi-mmio", AMDVI_MMIO_SIZE); @@ -1723,6 +1720,9 @@ static void amdvi_sysbus_realize(DeviceState *dev, Error **errp) memory_region_add_subregion_overlap(&s->mr_sys, AMDVI_INT_ADDR_FIRST, &s->mr_ir, 1); + /* Pseudo address space under root PCI bus. */ + x86ms->ioapic_as = amdvi_host_dma_iommu(bus, s, AMDVI_IOAPIC_SB_DEVID); + if (kvm_enabled() && x86ms->apic_id_limit > 255 && !s->xtsup) { error_report("AMD IOMMU with x2APIC configuration requires xtsup=on"); exit(EXIT_FAILURE); From 47d3b32d6fb1c6ec8afb78d12d2420dbbb4c3499 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 1 Aug 2025 11:35:05 +0530 Subject: [PATCH 2691/2760] hw/i386/amd_iommu: Fix amdvi_write*() amdvi_write*() function do not preserve the older values of W1C bits in the MMIO register. This results in all W1C bits set to 0, when guest tries to reset a single bit by writing 1 to it. Fix this by preserving W1C bits in the old value of the MMIO register. Fixes: d29a09ca68428 ("hw/i386: Introduce AMD IOMMU") Suggested-by: Ethan MILON Signed-off-by: Sairaj Kodilkar Message-Id: <20250801060507.3382-5-sarunkod@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 7308611bf1..c9c32cf7b0 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -123,8 +123,13 @@ static void amdvi_writew(AMDVIState *s, hwaddr addr, uint16_t val) uint16_t romask = lduw_le_p(&s->romask[addr]); uint16_t w1cmask = lduw_le_p(&s->w1cmask[addr]); uint16_t oldval = lduw_le_p(&s->mmior[addr]); + + uint16_t oldval_preserved = oldval & (romask | w1cmask); + uint16_t newval_write = val & ~romask; + uint16_t newval_w1c_set = val & w1cmask; + stw_le_p(&s->mmior[addr], - ((oldval & romask) | (val & ~romask)) & ~(val & w1cmask)); + (oldval_preserved | newval_write) & ~newval_w1c_set); } static void amdvi_writel(AMDVIState *s, hwaddr addr, uint32_t val) @@ -132,8 +137,13 @@ static void amdvi_writel(AMDVIState *s, hwaddr addr, uint32_t val) uint32_t romask = ldl_le_p(&s->romask[addr]); uint32_t w1cmask = ldl_le_p(&s->w1cmask[addr]); uint32_t oldval = ldl_le_p(&s->mmior[addr]); + + uint32_t oldval_preserved = oldval & (romask | w1cmask); + uint32_t newval_write = val & ~romask; + uint32_t newval_w1c_set = val & w1cmask; + stl_le_p(&s->mmior[addr], - ((oldval & romask) | (val & ~romask)) & ~(val & w1cmask)); + (oldval_preserved | newval_write) & ~newval_w1c_set); } static void amdvi_writeq(AMDVIState *s, hwaddr addr, uint64_t val) @@ -141,8 +151,13 @@ static void amdvi_writeq(AMDVIState *s, hwaddr addr, uint64_t val) uint64_t romask = ldq_le_p(&s->romask[addr]); uint64_t w1cmask = ldq_le_p(&s->w1cmask[addr]); uint64_t oldval = ldq_le_p(&s->mmior[addr]); + + uint64_t oldval_preserved = oldval & (romask | w1cmask); + uint64_t newval_write = val & ~romask; + uint64_t newval_w1c_set = val & w1cmask; + stq_le_p(&s->mmior[addr], - ((oldval & romask) | (val & ~romask)) & ~(val & w1cmask)); + (oldval_preserved | newval_write) & ~newval_w1c_set); } /* OR a 64-bit register with a 64-bit value */ From 10690920b0efb3ed8b166443bae8077104bb129d Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 1 Aug 2025 11:35:06 +0530 Subject: [PATCH 2692/2760] hw/i386/amd_iommu: Support MMIO writes to the status register Support the writes to the status register so that guest can reset the EventOverflow, EventLogInt, ComWaitIntr, etc bits after servicing the respective interrupt. Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Message-Id: <20250801060507.3382-6-sarunkod@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index c9c32cf7b0..6925085d29 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -862,6 +862,9 @@ static void amdvi_mmio_write(void *opaque, hwaddr addr, uint64_t val, amdvi_mmio_reg_write(s, size, val, addr); amdvi_handle_pprtail_write(s); break; + case AMDVI_MMIO_STATUS: + amdvi_mmio_reg_write(s, size, val, addr); + break; } } From c0ef803a879b97f3d269348c968fb3874c2761f6 Mon Sep 17 00:00:00 2001 From: Sairaj Kodilkar Date: Fri, 1 Aug 2025 11:35:07 +0530 Subject: [PATCH 2693/2760] hw/i386/amd_iommu: Fix event log generation Current event logging code is broken, because of following issues 1. The code uses '|' instead of '&' to test the bit field, which causes vIOMMU to generate overflow interrupt for every log entry. 2. Code does not update the eventlog tail MMIO register after adding an entry to the buffer, because of which guest cannot process new entries (as head == tail means buffer is empty). 3. Compares eventlog tail (which is byte offset in the buffer) to eventlog length (which is number of maximum entries in the buffer). This causes vIOMMU to generate only fix number of event logs, after which it keeps on generating overflow interrupts, without actually resetting the log buffer. 4. Updates ComWaitInt instead of EventLogInt bitfield in Status register. Guest checks this field to see if there are new event log entries in the buffer. 5. Does not reset event log head and tail pointers when guest writes to eventlog base register. Fix above issues, so that guest can process event log entries. Fixes: d29a09ca68428 ("hw/i386: Introduce AMD IOMMU") Signed-off-by: Sairaj Kodilkar Reviewed-by: Vasant Hegde Message-Id: <20250801060507.3382-7-sarunkod@amd.com> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/amd_iommu.c | 44 +++++++++++++++++++++++++++++++++++--------- hw/i386/amd_iommu.h | 1 + 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/hw/i386/amd_iommu.c b/hw/i386/amd_iommu.c index 6925085d29..26be69bec8 100644 --- a/hw/i386/amd_iommu.c +++ b/hw/i386/amd_iommu.c @@ -160,10 +160,10 @@ static void amdvi_writeq(AMDVIState *s, hwaddr addr, uint64_t val) (oldval_preserved | newval_write) & ~newval_w1c_set); } -/* OR a 64-bit register with a 64-bit value */ +/* AND a 64-bit register with a 64-bit value */ static bool amdvi_test_mask(AMDVIState *s, hwaddr addr, uint64_t val) { - return amdvi_readq(s, addr) | val; + return amdvi_readq(s, addr) & val; } /* OR a 64-bit register with a 64-bit value storing result in the register */ @@ -192,19 +192,31 @@ static void amdvi_generate_msi_interrupt(AMDVIState *s) } } +static uint32_t get_next_eventlog_entry(AMDVIState *s) +{ + uint32_t evtlog_size = s->evtlog_len * AMDVI_EVENT_LEN; + return (s->evtlog_tail + AMDVI_EVENT_LEN) % evtlog_size; +} + static void amdvi_log_event(AMDVIState *s, uint64_t *evt) { + uint32_t evtlog_tail_next; + /* event logging not enabled */ if (!s->evtlog_enabled || amdvi_test_mask(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_EVT_OVF)) { return; } + evtlog_tail_next = get_next_eventlog_entry(s); + /* event log buffer full */ - if (s->evtlog_tail >= s->evtlog_len) { - amdvi_assign_orq(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_EVT_OVF); - /* generate interrupt */ - amdvi_generate_msi_interrupt(s); + if (evtlog_tail_next == s->evtlog_head) { + /* generate overflow interrupt */ + if (s->evtlog_intr) { + amdvi_assign_orq(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_EVT_OVF); + amdvi_generate_msi_interrupt(s); + } return; } @@ -213,9 +225,13 @@ static void amdvi_log_event(AMDVIState *s, uint64_t *evt) trace_amdvi_evntlog_fail(s->evtlog, s->evtlog_tail); } - s->evtlog_tail += AMDVI_EVENT_LEN; - amdvi_assign_orq(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_COMP_INT); - amdvi_generate_msi_interrupt(s); + s->evtlog_tail = evtlog_tail_next; + amdvi_writeq_raw(s, AMDVI_MMIO_EVENT_TAIL, s->evtlog_tail); + + if (s->evtlog_intr) { + amdvi_assign_orq(s, AMDVI_MMIO_STATUS, AMDVI_MMIO_STATUS_EVENT_INT); + amdvi_generate_msi_interrupt(s); + } } static void amdvi_setevent_bits(uint64_t *buffer, uint64_t value, int start, @@ -731,9 +747,19 @@ static inline void amdvi_handle_excllim_write(AMDVIState *s) static inline void amdvi_handle_evtbase_write(AMDVIState *s) { uint64_t val = amdvi_readq(s, AMDVI_MMIO_EVENT_BASE); + + if (amdvi_readq(s, AMDVI_MMIO_STATUS) & AMDVI_MMIO_STATUS_EVENT_INT) + /* Do not reset if eventlog interrupt bit is set*/ + return; + s->evtlog = val & AMDVI_MMIO_EVTLOG_BASE_MASK; s->evtlog_len = 1UL << (amdvi_readq(s, AMDVI_MMIO_EVTLOG_SIZE_BYTE) & AMDVI_MMIO_EVTLOG_SIZE_MASK); + + /* clear tail and head pointer to 0 when event base is updated */ + s->evtlog_tail = s->evtlog_head = 0; + amdvi_writeq_raw(s, AMDVI_MMIO_EVENT_HEAD, s->evtlog_head); + amdvi_writeq_raw(s, AMDVI_MMIO_EVENT_TAIL, s->evtlog_tail); } static inline void amdvi_handle_evttail_write(AMDVIState *s) diff --git a/hw/i386/amd_iommu.h b/hw/i386/amd_iommu.h index 67078c6f1e..2476296c49 100644 --- a/hw/i386/amd_iommu.h +++ b/hw/i386/amd_iommu.h @@ -111,6 +111,7 @@ #define AMDVI_MMIO_STATUS_CMDBUF_RUN (1 << 4) #define AMDVI_MMIO_STATUS_EVT_RUN (1 << 3) #define AMDVI_MMIO_STATUS_COMP_INT (1 << 2) +#define AMDVI_MMIO_STATUS_EVENT_INT (1 << 1) #define AMDVI_MMIO_STATUS_EVT_OVF (1 << 0) #define AMDVI_CMDBUF_ID_BYTE 0x07 From 8d5613d2eefa6ca94b3c642583488e20915c48f7 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 12 Jun 2025 17:17:25 +0200 Subject: [PATCH 2694/2760] tests/acpi: virt: add an empty HEST file Such file will be used to track HEST table changes. For now, disallow HEST table check until we update it to the current data. Signed-off-by: Mauro Carvalho Chehab Acked-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: --- tests/data/acpi/aarch64/virt/HEST | 0 tests/qtest/bios-tables-test-allowed-diff.h | 1 + 2 files changed, 1 insertion(+) create mode 100644 tests/data/acpi/aarch64/virt/HEST diff --git a/tests/data/acpi/aarch64/virt/HEST b/tests/data/acpi/aarch64/virt/HEST new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/qtest/bios-tables-test-allowed-diff.h b/tests/qtest/bios-tables-test-allowed-diff.h index dfb8523c8b..39901c58d6 100644 --- a/tests/qtest/bios-tables-test-allowed-diff.h +++ b/tests/qtest/bios-tables-test-allowed-diff.h @@ -1 +1,2 @@ /* List of comma-separated changed AML files to ignore */ +"tests/data/acpi/aarch64/virt/HEST", From 5088651138b94542807414eb841363b16d6aa535 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 12 Jun 2025 17:17:26 +0200 Subject: [PATCH 2695/2760] tests/qtest/bios-tables-test: extend to also check HEST table Currently, aarch64 can generate a HEST table when loaded with -machine ras=on. Add support for it. Signed-off-by: Mauro Carvalho Chehab Reviewed-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: <9ce77140500ef68cc939d63952c25579f711ea52.1749741085.git.mchehab+huawei@kernel.org> --- tests/qtest/bios-tables-test.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qtest/bios-tables-test.c b/tests/qtest/bios-tables-test.c index 6aec68decc..e7e6926c81 100644 --- a/tests/qtest/bios-tables-test.c +++ b/tests/qtest/bios-tables-test.c @@ -2208,7 +2208,7 @@ static void test_acpi_aarch64_virt_tcg(void) data.smbios_cpu_max_speed = 2900; data.smbios_cpu_curr_speed = 2700; - test_acpi_one("-cpu cortex-a57 " + test_acpi_one("-cpu cortex-a57 -machine ras=on " "-smbios type=4,max-speed=2900,current-speed=2700", &data); free_test_data(&data); } From cd16f08ad4bda8191e4de986d17184c9da5466cd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Thu, 12 Jun 2025 17:17:27 +0200 Subject: [PATCH 2696/2760] tests/acpi: virt: update HEST file with its current data Now that HEST table is checked for aarch64, add the current firmware file. Signed-off-by: Mauro Carvalho Chehab Acked-by: Igor Mammedov Reviewed-by: Jonathan Cameron Message-Id: --- tests/data/acpi/aarch64/virt/HEST | Bin 0 -> 132 bytes tests/qtest/bios-tables-test-allowed-diff.h | 1 - 2 files changed, 1 deletion(-) diff --git a/tests/data/acpi/aarch64/virt/HEST b/tests/data/acpi/aarch64/virt/HEST index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..4c5d8c5b5da5b3241f93cd0839e94272bf6b1486 100644 GIT binary patch literal 132 zcmeZp4Gw8xU|?W; Date: Mon, 14 Jul 2025 09:00:47 +0100 Subject: [PATCH 2697/2760] intel_iommu: Allow both Status Write and Interrupt Flag in QI wait FreeBSD does both, and this appears to be perfectly valid. The VT-d spec even talks about the ordering (the status write should be done first, unsurprisingly). We certainly shouldn't assert() and abort QEMU if the guest asks for both. Fixes: ed7b8fbcfb88 ("intel-iommu: add supports for queued invalidation interface") Closes: https://gitlab.com/qemu-project/qemu/-/issues/3028 Signed-off-by: David Woodhouse Message-Id: <0122cbabc0adcc3cf878f5fd7834d8f258c7a2f2.camel@infradead.org> Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- hw/i386/intel_iommu.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hw/i386/intel_iommu.c b/hw/i386/intel_iommu.c index fe9a5f2872..83c5e44413 100644 --- a/hw/i386/intel_iommu.c +++ b/hw/i386/intel_iommu.c @@ -2828,6 +2828,7 @@ static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) { uint64_t mask[4] = {VTD_INV_DESC_WAIT_RSVD_LO, VTD_INV_DESC_WAIT_RSVD_HI, VTD_INV_DESC_ALL_ONE, VTD_INV_DESC_ALL_ONE}; + bool ret = true; if (!vtd_inv_desc_reserved_check(s, inv_desc, mask, false, __func__, "wait")) { @@ -2839,8 +2840,6 @@ static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) uint32_t status_data = (uint32_t)(inv_desc->lo >> VTD_INV_DESC_WAIT_DATA_SHIFT); - assert(!(inv_desc->lo & VTD_INV_DESC_WAIT_IF)); - /* FIXME: need to be masked with HAW? */ dma_addr_t status_addr = inv_desc->hi; trace_vtd_inv_desc_wait_sw(status_addr, status_data); @@ -2849,18 +2848,22 @@ static bool vtd_process_wait_desc(IntelIOMMUState *s, VTDInvDesc *inv_desc) &status_data, sizeof(status_data), MEMTXATTRS_UNSPECIFIED)) { trace_vtd_inv_desc_wait_write_fail(inv_desc->hi, inv_desc->lo); - return false; + ret = false; } - } else if (inv_desc->lo & VTD_INV_DESC_WAIT_IF) { + } + + if (inv_desc->lo & VTD_INV_DESC_WAIT_IF) { /* Interrupt flag */ vtd_generate_completion_event(s); - } else { + } + + if (!(inv_desc->lo & (VTD_INV_DESC_WAIT_IF | VTD_INV_DESC_WAIT_SW))) { error_report_once("%s: invalid wait desc: hi=%"PRIx64", lo=%"PRIx64 " (unknown type)", __func__, inv_desc->hi, inv_desc->lo); return false; } - return true; + return ret; } static bool vtd_process_context_cache_desc(IntelIOMMUState *s, From 4164adc476d85d46ef4901c05a9807b24473b00d Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Mon, 14 Jul 2025 12:26:23 +0200 Subject: [PATCH 2698/2760] MAINTAINERS: add net/vhost* files under `vhost` net/vhost* files should be interesting for vhost maintainers/reviewers. Suggested-by: Peter Maydell Signed-off-by: Stefano Garzarella Message-Id: <20250714102626.34431-1-sgarzare@redhat.com> Reviewed-by: Manos Pitsidianakis Reviewed-by: Michael S. Tsirkin Signed-off-by: Michael S. Tsirkin --- MAINTAINERS | 1 + 1 file changed, 1 insertion(+) diff --git a/MAINTAINERS b/MAINTAINERS index 069d77f2f8..28cea34271 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2322,6 +2322,7 @@ F: include/*/vhost* F: subprojects/libvhost-user/ F: block/export/vhost-user* F: util/vhost-user-server.c +F: net/vhost* vhost-shadow-virtqueue R: Eugenio Pérez From 4caf74916d09019e61c91f8cb1166510836d35e8 Mon Sep 17 00:00:00 2001 From: Stefano Garzarella Date: Mon, 14 Jul 2025 12:11:56 +0200 Subject: [PATCH 2699/2760] net/vdpa: fix potential fd leak in net_init_vhost_vdpa() Coverity reported a file descriptor leak (CID 1490785) that happens if `vhost_vdpa_get_max_queue_pairs()` returns 0, since in that case net_host_vdpa_init(), which should take ownership of the fd, is never called. vhost_vdpa_get_max_queue_pairs() returns 1 if VIRTIO_NET_F_MQ is not negotiated, or a negative error if the ioctl() fails, or the maximum number of queue pairs exposed by the device in the config space in the `max_virtqueue_pairs` field. In the VIRTIO spec we have: The device MUST set max_virtqueue_pairs to between 1 and 0x8000 inclusive, if it offers VIRTIO_NET_F_MQ. So, if `vhost_vdpa_get_max_queue_pairs()` returns 0, it's really an error since the device is violating the VIRTIO spec. Treat also `queue_pairs == 0` as an error, and jump to the `err` label, to return a negative value to the caller in any case. Coverity: CID 1490785 Suggested-by: Peter Maydell Signed-off-by: Stefano Garzarella Message-Id: <20250714101156.30024-1-sgarzare@redhat.com> Suggested-by: Peter Maydell Signed-off-by: Stefano Garzarella Reviewed-by: Manos Pitsidianakis Acked-by: Jason Wang --- net/vhost-vdpa.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/net/vhost-vdpa.c b/net/vhost-vdpa.c index 6a30a44d2b..74d26a9497 100644 --- a/net/vhost-vdpa.c +++ b/net/vhost-vdpa.c @@ -1840,9 +1840,8 @@ int net_init_vhost_vdpa(const Netdev *netdev, const char *name, queue_pairs = vhost_vdpa_get_max_queue_pairs(vdpa_device_fd, features, &has_cvq, errp); - if (queue_pairs < 0) { - qemu_close(vdpa_device_fd); - return queue_pairs; + if (queue_pairs <= 0) { + goto err; } r = vhost_vdpa_get_iova_range(vdpa_device_fd, &iova_range); From a0555e36fc44ea98edf7c50924de8b973cd4267d Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Wed, 30 Jul 2025 00:16:49 +0800 Subject: [PATCH 2700/2760] hw/intc/arm_gicv3_kvm: Remove writes to ICPENDR registers As per the arm-vgic-v3 kernel doc [1]: Accesses to GICD_ICPENDR register region and GICR_ICPENDR0 registers have RAZ/WI semantics, meaning that reads always return 0 and writes are always ignored. The state behind these registers (both 0 and 1 bits) is written by writing to the GICD_ISPENDR and GICR_ISPENDR0 registers, unlike some of the other set/clear register pairs. Remove the useless writes to ICPENDR registers in kvm_arm_gicv3_put(). [1] https://docs.kernel.org/virt/kvm/devices/arm-vgic-v3.html Signed-off-by: Zenghui Yu Message-id: 20250729161650.43758-2-zenghui.yu@linux.dev Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index 8ed88e7429..f798a6e28c 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -387,8 +387,6 @@ static void kvm_arm_gicv3_put(GICv3State *s) reg = c->level; kvm_gic_line_level_access(s, 0, ncpu, ®, true); - reg = ~0; - kvm_gicr_access(s, GICR_ICPENDR0, ncpu, ®, true); reg = c->gicr_ipendr0; kvm_gicr_access(s, GICR_ISPENDR0, ncpu, ®, true); @@ -445,7 +443,7 @@ static void kvm_arm_gicv3_put(GICv3State *s) kvm_gic_put_line_level_bmp(s, s->level); /* s->pending bitmap -> GICD_ISPENDRn */ - kvm_dist_putbmp(s, GICD_ISPENDR, GICD_ICPENDR, s->pending); + kvm_dist_putbmp(s, GICD_ISPENDR, 0, s->pending); /* s->active bitmap -> GICD_ISACTIVERn */ kvm_dist_putbmp(s, GICD_ISACTIVER, GICD_ICACTIVER, s->active); From b10bd4bd17ac8628ede8735a08ad82dc3b721c64 Mon Sep 17 00:00:00 2001 From: Zenghui Yu Date: Wed, 30 Jul 2025 00:16:50 +0800 Subject: [PATCH 2701/2760] hw/intc/arm_gicv3_kvm: Write all 1's to clear enable/active KVM's userspace access interface to the GICD enable and active bits is via set/clear register pairs which implement the hardware's "write 1s to the clear register to clear the 0 bits, and write 1s to the set register to set the 1 bits" semantics. We didn't get this right, because we were writing 0 to the clear register. Writing 0 to GICD_IC{ENABLE,ACTIVE}R architecturally has no effect on interrupt status (all writes are simply ignored by KVM) and doesn't comply with the intention of "first write to the clear-reg to clear all bits". Write all 1's to actually clear the enable/active status. This didn't have any adverse effects on migration because there we start with a clean VM state; it would be guest-visible when doing a system reset, but since Linux always cleans up the register state of the GIC during bootup before it enables it most users won't have run into a problem here. Cc: qemu-stable@nongnu.org Fixes: 367b9f527bec ("hw/intc/arm_gicv3_kvm: Implement get/put functions") Signed-off-by: Zenghui Yu Message-id: 20250729161650.43758-3-zenghui.yu@linux.dev Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- hw/intc/arm_gicv3_kvm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/intc/arm_gicv3_kvm.c b/hw/intc/arm_gicv3_kvm.c index f798a6e28c..6166283cd1 100644 --- a/hw/intc/arm_gicv3_kvm.c +++ b/hw/intc/arm_gicv3_kvm.c @@ -295,7 +295,7 @@ static void kvm_dist_putbmp(GICv3State *s, uint32_t offset, * the 1 bits. */ if (clroffset != 0) { - reg = 0; + reg = ~0; kvm_gicd_access(s, clroffset, ®, true); clroffset += 4; } From e7b77e681f8ecf7d9360e47243f7c1a0fb88f51c Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 10 Jul 2025 18:43:12 +0100 Subject: [PATCH 2702/2760] hw/display/framebuffer: Add cast to force 64x64 multiply In framebuffer_update_display(), Coverity complains because we multiply two values of type 'int' (which will be done as a 32x32 multiply and so in theory might overflow) and then add the result to a ram_addr_t, which can be 64 bits. 4GB framebuffers are not plausible anyway, but keep Coverity happy by adding casts which force these multiplies to be done as 64x64. Coverity: CID 1487248 Signed-off-by: Peter Maydell Reviewed-by: Manos Pitsidianakis Message-id: 20250710174312.1313177-1-peter.maydell@linaro.org --- hw/display/framebuffer.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hw/display/framebuffer.c b/hw/display/framebuffer.c index 4485aa335b..b4296e8a33 100644 --- a/hw/display/framebuffer.c +++ b/hw/display/framebuffer.c @@ -95,9 +95,9 @@ void framebuffer_update_display( } first = -1; - addr += i * src_width; - src += i * src_width; - dest += i * dest_row_pitch; + addr += (uint64_t)i * src_width; + src += (uint64_t)i * src_width; + dest += (uint64_t)i * dest_row_pitch; snap = memory_region_snapshot_and_clear_dirty(mem, addr, src_width * rows, DIRTY_MEMORY_VGA); From 4f2b82f60431e4792ecfd86a4d6b824248ee4c21 Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Thu, 31 Jul 2025 14:43:38 +0100 Subject: [PATCH 2703/2760] target/arm: Reinstate bogus AArch32 DBGDTRTX register for migration compat In commit 655659a74a we fixed some bugs in the encoding of the Debug Communications Channel registers, including that we were incorrectly exposing an AArch32 register at p14, 3, c0, c5, 0. Unfortunately removing a register is a break of forwards migration compatibility for TCG, because we will fail the migration if the source QEMU passes us a cpreg which the destination QEMU does not have. We don't have a mechanism for saying "it's OK to ignore this sysreg in the inbound data", so for the 10.1 release reinstate the incorrect AArch32 register. (We probably have had other cases in the past of breaking migration compatibility like this, but we didn't notice because we didn't test and in any case not that many people care about TCG migration compatibility. KVM migration compat is not affected because for KVM we treat the kernel as the source of truth for what system registers are present.) Fixes: 655659a74a36b ("target/arm: Correct encoding of Debug Communications Channel registers") Reported-by: Fabiano Rosas Signed-off-by: Peter Maydell Reviewed-by: Fabiano Rosas Reviewed-by: Pierrick Bouvier Reviewed-by: Richard Henderson Message-id: 20250731134338.250203-1-peter.maydell@linaro.org --- target/arm/debug_helper.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/target/arm/debug_helper.c b/target/arm/debug_helper.c index aee06d4d42..579516e154 100644 --- a/target/arm/debug_helper.c +++ b/target/arm/debug_helper.c @@ -940,6 +940,13 @@ static void dbgclaimclr_write(CPUARMState *env, const ARMCPRegInfo *ri, env->cp15.dbgclaim &= ~(value & 0xFF); } +static CPAccessResult access_bogus(CPUARMState *env, const ARMCPRegInfo *ri, + bool isread) +{ + /* Always UNDEF, as if this cpreg didn't exist */ + return CP_ACCESS_UNDEFINED; +} + static const ARMCPRegInfo debug_cp_reginfo[] = { /* * DBGDRAR, DBGDSAR: always RAZ since we don't implement memory mapped @@ -1002,6 +1009,28 @@ static const ARMCPRegInfo debug_cp_reginfo[] = { .opc0 = 2, .opc1 = 3, .crn = 0, .crm = 4, .opc2 = 0, .access = PL0_RW, .accessfn = access_tdcc, .type = ARM_CP_CONST, .resetvalue = 0 }, + /* + * This is not a real AArch32 register. We used to incorrectly expose + * this due to a QEMU bug; to avoid breaking migration compatibility we + * need to continue to provide it so that we don't fail the inbound + * migration when it tells us about a sysreg that we don't have. + * We set an always-fails .accessfn, which means that the guest doesn't + * actually see this register (it will always UNDEF, identically to if + * there were no cpreg definition for it other than that we won't print + * a LOG_UNIMP message about it), and we set the ARM_CP_NO_GDB flag so the + * gdbstub won't see it either. + * (We can't just set .access = 0, because add_cpreg_to_hashtable() + * helpfully ignores cpregs which aren't accessible to the highest + * implemented EL.) + * + * TODO: implement a system for being able to describe "this register + * can be ignored if it appears in the inbound stream"; then we can + * remove this temporary hack. + */ + { .name = "BOGUS_DBGDTR_EL0", .state = ARM_CP_STATE_AA32, + .cp = 14, .opc1 = 3, .crn = 0, .crm = 5, .opc2 = 0, + .access = PL0_RW, .accessfn = access_bogus, + .type = ARM_CP_CONST | ARM_CP_NO_GDB, .resetvalue = 0 }, /* * OSECCR_EL1 provides a mechanism for an operating system * to access the contents of EDECCR. EDECCR is not implemented though, From 35cca0f95ff5345f54c11d116efc8940a0dab8aa Mon Sep 17 00:00:00 2001 From: Vacha Bhavsar Date: Tue, 22 Jul 2025 17:37:35 +0000 Subject: [PATCH 2704/2760] target/arm: Fix big-endian handling of NEON gdb remote debugging In the code for allowing the gdbstub to set the value of an AArch64 FP/SIMD register, we weren't accounting for target_big_endian() being true. This meant that for aarch64_be-linux-user we would set the two halves of the FP register the wrong way around. The much more common case of a little-endian guest is not affected; nor are big-endian hosts. Correct the handling of this case. Cc: qemu-stable@nongnu.org Signed-off-by: Vacha Bhavsar Message-id: 20250722173736.2332529-2-vacha.bhavsar@oss.qualcomm.com [PMM: added comment, expanded commit message, fixed missing space] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/gdbstub64.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 64ee9b3b56..4fce58d895 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -115,8 +115,22 @@ int aarch64_gdb_set_fpu_reg(CPUState *cs, uint8_t *buf, int reg) /* 128 bit FP register */ { uint64_t *q = aa64_vfp_qreg(env, reg); - q[0] = ldq_le_p(buf); - q[1] = ldq_le_p(buf + 8); + + /* + * On the wire these are target-endian 128 bit values. + * In the CPU state these are host-order uint64_t values + * with the least-significant one first. This means they're + * the other way around for target_big_endian() (which is + * only true for us for aarch64_be-linux-user). + */ + if (target_big_endian()) { + q[1] = ldq_p(buf); + q[0] = ldq_p(buf + 8); + } else{ + q[0] = ldq_p(buf); + q[1] = ldq_p(buf + 8); + } + return 16; } case 32: From 97b3d732afec9b165c33697452e31267a845338f Mon Sep 17 00:00:00 2001 From: Vacha Bhavsar Date: Tue, 22 Jul 2025 17:37:36 +0000 Subject: [PATCH 2705/2760] target/arm: Fix handling of setting SVE registers from gdb The code to handle setting SVE registers via the gdbstub is broken: * it sets each pair of elements in the zregs[].d[] array in the wrong order for the most common (little endian) case: the least significant 64-bit value comes first * it makes no attempt to handle target_endian() * it does a simple copy out of the (target endian) gdbstub buffer into the (host endan) zregs data structure, which is wrong on big endian hosts Fix all these problems: * use ldq_p() to read from the gdbstub buffer * check target_big_endian() to see if we need to handle the 128-bit values the opposite way around Cc: qemu-stable@nongnu.org Signed-off-by: Vacha Bhavsar Message-id: 20250722173736.2332529-3-vacha.bhavsar@oss.qualcomm.com [PMM: adjusted commit message, fixed spacing] Reviewed-by: Peter Maydell Signed-off-by: Peter Maydell --- target/arm/gdbstub64.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/target/arm/gdbstub64.c b/target/arm/gdbstub64.c index 4fce58d895..08e2858539 100644 --- a/target/arm/gdbstub64.c +++ b/target/arm/gdbstub64.c @@ -206,10 +206,17 @@ int aarch64_gdb_set_sve_reg(CPUState *cs, uint8_t *buf, int reg) case 0 ... 31: { int vq, len = 0; - uint64_t *p = (uint64_t *) buf; for (vq = 0; vq < cpu->sve_max_vq; vq++) { - env->vfp.zregs[reg].d[vq * 2 + 1] = *p++; - env->vfp.zregs[reg].d[vq * 2] = *p++; + if (target_big_endian()) { + env->vfp.zregs[reg].d[vq * 2 + 1] = ldq_p(buf); + buf += 8; + env->vfp.zregs[reg].d[vq * 2] = ldq_p(buf); + } else{ + env->vfp.zregs[reg].d[vq * 2] = ldq_p(buf); + buf += 8; + env->vfp.zregs[reg].d[vq * 2 + 1] = ldq_p(buf); + } + buf += 8; len += 16; } return len; @@ -224,9 +231,9 @@ int aarch64_gdb_set_sve_reg(CPUState *cs, uint8_t *buf, int reg) { int preg = reg - 34; int vq, len = 0; - uint64_t *p = (uint64_t *) buf; for (vq = 0; vq < cpu->sve_max_vq; vq = vq + 4) { - env->vfp.pregs[preg].p[vq / 4] = *p++; + env->vfp.pregs[preg].p[vq / 4] = ldq_p(buf); + buf += 8; len += 8; } return len; From 676ab6a21117858393a4440e4cdc3d314277cf20 Mon Sep 17 00:00:00 2001 From: Gustavo Romero Date: Fri, 1 Aug 2025 00:13:05 +0000 Subject: [PATCH 2706/2760] tests/tcg: Fix run for tests with specific plugin MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 25aaf0cb7f (“tests/tcg: reduce the number of plugin test combinations”) added support for running tests with specific plugins passed via the EXTRA_RUNS variable. However, due to the optimization, the rules generated as a shuffled combination of tests and plugins might not cover the rules required to run the tests with a specific plugin passed via EXTRA_RUNS. This commit fixes it by correctly generating the rules for the tests that require a specific plugin to run, which are now passed via the EXTRA_RUNS_WITH_PLUGIN instead of via the EXTRA_RUNS variable. The fix essentially excludes the tests passed via EXTRA_RUNS_WITH_PLUGIN from the rules created by the shuffled combination of tests and plugins, to avoid running the tests twice, and generates the rules for the test/plugin combinations listed in the EXTRA_RUNS_WITH_PLUGIN variable. Signed-off-by: Gustavo Romero Reviewed-by: Pierrick Bouvier Tested-by: Pierrick Bouvier Message-id: 20250801001305.2352554-1-gustavo.romero@linaro.org Signed-off-by: Peter Maydell --- tests/tcg/Makefile.target | 20 ++++++++++++++++--- tests/tcg/multiarch/Makefile.target | 2 +- .../multiarch/system/Makefile.softmmu-target | 2 +- tests/tcg/x86_64/Makefile.softmmu-target | 2 +- 4 files changed, 20 insertions(+), 6 deletions(-) diff --git a/tests/tcg/Makefile.target b/tests/tcg/Makefile.target index 18afd5be19..af72903f89 100644 --- a/tests/tcg/Makefile.target +++ b/tests/tcg/Makefile.target @@ -170,6 +170,10 @@ endif PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ $(patsubst %.c, lib%.so, $(notdir $(wildcard $(PLUGIN_SRC)/*.c)))) +strip-plugin = $(wordlist 1, 1, $(subst -with-, ,$1)) +extract-plugin = $(wordlist 2, 2, $(subst -with-, ,$1)) +extract-test = $(subst run-plugin-,,$(wordlist 1, 1, $(subst -with-, ,$1))) + # We need to ensure expand the run-plugin-TEST-with-PLUGIN # pre-requistes manually here as we can't use stems to handle it. We # only expand MULTIARCH_TESTS which are common on most of our targets @@ -179,6 +183,13 @@ PLUGINS=$(filter-out $(DISABLE_PLUGINS), \ ifneq ($(MULTIARCH_TESTS),) +# Extract extra tests from the extra test+plugin combination. +EXTRA_TESTS_WITH_PLUGIN=$(foreach test, \ + $(EXTRA_RUNS_WITH_PLUGIN),$(call extract-test,$(test))) +# Exclude tests that were specified to run with specific plugins from the tests +# which can run with any plugin combination, so we don't run it twice. +MULTIARCH_TESTS:=$(filter-out $(EXTRA_TESTS_WITH_PLUGIN), $(MULTIARCH_TESTS)) + NUM_PLUGINS := $(words $(PLUGINS)) NUM_TESTS := $(words $(MULTIARCH_TESTS)) @@ -186,19 +197,22 @@ define mod_plus_one $(shell $(PYTHON) -c "print( ($(1) % $(2)) + 1 )") endef +# Rules for running tests with any plugin combination, i.e., no specific plugin. $(foreach _idx, $(shell seq 1 $(NUM_TESTS)), \ $(eval _test := $(word $(_idx), $(MULTIARCH_TESTS))) \ $(eval _plugin := $(word $(call mod_plus_one, $(_idx), $(NUM_PLUGINS)), $(PLUGINS))) \ $(eval run-plugin-$(_test)-with-$(_plugin): $(_test) $(_plugin)) \ $(eval RUN_TESTS+=run-plugin-$(_test)-with-$(_plugin))) +# Rules for running extra tests with specific plugins. +$(foreach f,$(EXTRA_RUNS_WITH_PLUGIN), \ + $(eval $(f): $(call extract-test,$(f)) $(call extract-plugin,$(f)))) + endif # MULTIARCH_TESTS endif # CONFIG_PLUGIN -strip-plugin = $(wordlist 1, 1, $(subst -with-, ,$1)) -extract-plugin = $(wordlist 2, 2, $(subst -with-, ,$1)) - RUN_TESTS+=$(EXTRA_RUNS) +RUN_TESTS+=$(EXTRA_RUNS_WITH_PLUGIN) # Some plugins need additional arguments above the default to fully # exercise things. We can define them on a per-test basis here. diff --git a/tests/tcg/multiarch/Makefile.target b/tests/tcg/multiarch/Makefile.target index 38345ff880..8dc65d7a06 100644 --- a/tests/tcg/multiarch/Makefile.target +++ b/tests/tcg/multiarch/Makefile.target @@ -201,7 +201,7 @@ run-plugin-test-plugin-mem-access-with-libmem.so: \ $(SRC_PATH)/tests/tcg/multiarch/check-plugin-output.sh \ $(QEMU) $< -EXTRA_RUNS += run-plugin-test-plugin-mem-access-with-libmem.so +EXTRA_RUNS_WITH_PLUGIN += run-plugin-test-plugin-mem-access-with-libmem.so endif # Update TESTS diff --git a/tests/tcg/multiarch/system/Makefile.softmmu-target b/tests/tcg/multiarch/system/Makefile.softmmu-target index 4171b4e6aa..98c4eda5e0 100644 --- a/tests/tcg/multiarch/system/Makefile.softmmu-target +++ b/tests/tcg/multiarch/system/Makefile.softmmu-target @@ -77,5 +77,5 @@ run-plugin-memory-with-libmem.so: memory libmem.so run-plugin-memory-with-libmem.so: PLUGIN_ARGS=$(COMMA)region-summary=true run-plugin-memory-with-libmem.so: CHECK_PLUGIN_OUTPUT_COMMAND=$(MULTIARCH_SYSTEM_SRC)/validate-memory-counts.py $@.out -EXTRA_RUNS += run-plugin-memory-with-libmem.so +EXTRA_RUNS_WITH_PLUGIN += run-plugin-memory-with-libmem.so endif diff --git a/tests/tcg/x86_64/Makefile.softmmu-target b/tests/tcg/x86_64/Makefile.softmmu-target index 3e30ca9307..4e65f58b57 100644 --- a/tests/tcg/x86_64/Makefile.softmmu-target +++ b/tests/tcg/x86_64/Makefile.softmmu-target @@ -40,5 +40,5 @@ run-plugin-patch-target-with-libpatch.so: \ run-plugin-patch-target-with-libpatch.so: \ CHECK_PLUGIN_OUTPUT_COMMAND=$(X64_SYSTEM_SRC)/validate-patch.py $@.out run-plugin-patch-target-with-libpatch.so: patch-target libpatch.so -EXTRA_RUNS+=run-plugin-patch-target-with-libpatch.so +EXTRA_RUNS_WITH_PLUGIN+=run-plugin-patch-target-with-libpatch.so endif From 7bf9ae8cc2484f83ec77ad475538d78355e424ea Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Mon, 28 Jul 2025 01:19:18 +0300 Subject: [PATCH 2707/2760] roms/vbootrom: update to 7b1eb5f7fe6a MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes: 7b1eb5f ast27x0: Fix Makefile to unconditionally set CC to support correct cross-compilation 601d410 ast27x0: Fix missing SCU module reset for SSP and TSP initialization 80768e4 ast27x0: Initialize and enable SSP/TSP using SCU with reserved-memory from DTB f8ab635 ast27x0: Show build date and git version 53294f5 Add initial support for AST27x0 b1c2803 Dynamically detects NPCM8XX UBOOT destination and size. 4f54dfc Automatically search for UBOOT location for NPCM8xx images. The actual bootroms are not updated yet. Signed-off-by: Michael Tokarev Link: https://lore.kernel.org/qemu-devel/2a89ad4c8f5665d07952a4f1749caa6ec0cd3d9c.1753654515.git.mjt@tls.msk.ru [ clg: Update to latest vbootrom ] Reviewed-by: Jamin Lin Signed-off-by: Cédric Le Goater --- roms/vbootrom | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/roms/vbootrom b/roms/vbootrom index 1287b6e42e..183c9ff805 160000 --- a/roms/vbootrom +++ b/roms/vbootrom @@ -1 +1 @@ -Subproject commit 1287b6e42e839ba2ab0f06268c5b53ae60df3537 +Subproject commit 183c9ff8056b7946db1ae49cc23e8980ac413174 From 5ff7ad61c0af08d81dc7fd050c14b944fbeb9c35 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Mon, 28 Jul 2025 01:19:19 +0300 Subject: [PATCH 2708/2760] roms/Makefile: build ast27x0_bootrom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3052 Signed-off-by: Michael Tokarev Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/607a943a587248fbe0ff0897de80aee98a093caa.1753654515.git.mjt@tls.msk.ru [ clg: Removed make CC= workaround ] Reviewed-by: Jamin Lin Signed-off-by: Cédric Le Goater --- roms/Makefile | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/roms/Makefile b/roms/Makefile index 6af68a922f..4c8793c5bd 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -68,6 +68,7 @@ default help: @echo " u-boot.sam460 -- update u-boot.sam460" @echo " npcm7xx_bootrom -- update vbootrom for npcm7xx" @echo " npcm8xx_bootrom -- update vbootrom for npcm8xx" + @echo " ast27x0_bootrom -- update vbootrom for ast27x0" @echo " efi -- update UEFI (edk2) platform firmware" @echo " opensbi32-generic -- update OpenSBI for 32-bit generic machine" @echo " opensbi64-generic -- update OpenSBI for 64-bit generic machine" @@ -200,6 +201,10 @@ npcm8xx_bootrom: $(MAKE) -C vbootrom/npcm8xx CROSS_COMPILE=$(aarch64_cross_prefix) cp vbootrom/npcm8xx/npcm8xx_bootrom.bin ../pc-bios/npcm8xx_bootrom.bin +ast27x0_bootrom: + $(MAKE) -C vbootrom/ast27x0 CROSS_COMPILE=$(aarch64_cross_prefix) + cp vbootrom/ast27x0/ast27x0_bootrom.bin ../pc-bios/ast27x0_bootrom.bin + hppa-firmware: $(MAKE) -C seabios-hppa parisc cp seabios-hppa/out/hppa-firmware.img ../pc-bios/ From d63961f957ffaa586b166846f2c6a580923b08f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 29 Jul 2025 19:34:59 +0200 Subject: [PATCH 2709/2760] pc-bios: Update vbootrom image to commit 183c9ff8056b MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Full changelog since last update (1287b6e42e83) : Hao Wu (2): Automatically search for UBOOT location for NPCM8xx images. Dynamically detects NPCM8XX UBOOT destination and size. Jamin Lin (5): Add initial support for AST27x0 ast27x0: Show build date and git version ast27x0: Initialize and enable SSP/TSP using SCU with reserved-memory from DTB ast27x0: Fix missing SCU module reset for SSP and TSP initialization ast27x0: Fix Makefile to unconditionally set CC to support correct cross-compilation Compiled with gcc version 13.3.0 Reviewed-by: Jamin Lin Reviewed-by: Michael Tokarev Signed-off-by: Cédric Le Goater --- pc-bios/ast27x0_bootrom.bin | Bin 15552 -> 16408 bytes pc-bios/npcm7xx_bootrom.bin | Bin 768 -> 672 bytes pc-bios/npcm8xx_bootrom.bin | Bin 608 -> 672 bytes 3 files changed, 0 insertions(+), 0 deletions(-) diff --git a/pc-bios/ast27x0_bootrom.bin b/pc-bios/ast27x0_bootrom.bin index 0b9b3a2360e375bb6007ecdf13b39d931870f6fa..a4c94d64da55ade9b4beb2a409132f315fb51347 100644 GIT binary patch literal 16408 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 H*FOIj5gCL{ literal 15552 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 F{ugK9U^D;# diff --git a/pc-bios/npcm7xx_bootrom.bin b/pc-bios/npcm7xx_bootrom.bin index 903f126636f9ef5d1100c056656ccfb2b32e5e10..92282892b70dbcd6828c5054dff33ffe3385c485 100644 GIT binary patch delta 218 zcmZo*Tfn-ZgR!26f#J8ofBlEI{{Mf)!0`7!1H=FS4NU+4D=_{AV+Q4dtyd(DNipaw ze9g?taHn9eP6ms$Ny3-s+{*qRs+|P3PEPm#z~G{kA?7{HiTQ%0M}^D(?-Sl9We9`$ zVqkv9djW6hYVVCel}MH_TFGPFz+dgf%7AV11(R#0rhhO)&5q2sIM?+ddlRu;4y>3 zl&38Q^Pe&^1U+P8VBo4@nE#Z4q5r|Y|NmdJC`_q;T7V>(0hQbiluQ7M^B7zPDh__k z2Gq!;2sB(|!D9}^NlzIR8lSo-01Y)>@Gt^ok>G;I4vZ6lY6Bj#0M)uEPJGIw)AZ;( z&>SY7hDSihzK{T#4Wa|sLsv~;U|v H6ovr+8yI@# diff --git a/pc-bios/npcm8xx_bootrom.bin b/pc-bios/npcm8xx_bootrom.bin index 6370d6475635c4d445d2b927311edcd591949c82..45fb40fb5987ab681cc902659d85e76a7385cb53 100644 GIT binary patch delta 215 zcmaFBvVe6%2BV+@69X75fYFmH8MW&beohQhuv<{X$k23&QDEOE1r~;#P7Av(IlWl< z=~3sYPfi^#KRGit1Ti-(0;<_?iGgLqB^3sTpYs`*UN2;12vK2h+Bu(rIolD){#(6& zTzdWg|MW!0rXa% nCLPt3|NsB1Kx|@=0n$1^TmlsH0f{;C3Cv}h#~3)dkx3o^*ts%% From 13ed972b4ce57198914a37217251d30fbec20e41 Mon Sep 17 00:00:00 2001 From: Jamin Lin Date: Mon, 4 Aug 2025 09:46:33 +0800 Subject: [PATCH 2710/2760] hw/ssi/aspeed_smc: Fix incorrect FMC_WDT2 register read on AST1030 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On AST1030, reading the FMC_WDT2 register always returns 0xFFFFFFFF. This issue is due to the aspeed_smc_read function, which checks for the ASPEED_SMC_FEATURE_WDT_CONTROL feature. Since AST1030 was missing this feature flag, the read operation fails and returns -1. To resolve this, add the WDT_CONTROL feature to AST1030's feature set so that FMC_WDT2 can be correctly accessed by firmware. Signed-off-by: Jamin Lin Reviewed-by: Cédric Le Goater Fixes: 2850df6a81bcdc2e063dfdd56751ee2d11c58030 ("aspeed/smc: Add AST1030 support ") Link: https://lore.kernel.org/qemu-devel/20250804014633.512737-1-jamin_lin@aspeedtech.com Signed-off-by: Cédric Le Goater --- hw/ssi/aspeed_smc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hw/ssi/aspeed_smc.c b/hw/ssi/aspeed_smc.c index 614528b8ef..e33496f502 100644 --- a/hw/ssi/aspeed_smc.c +++ b/hw/ssi/aspeed_smc.c @@ -1857,7 +1857,8 @@ static void aspeed_1030_fmc_class_init(ObjectClass *klass, const void *data) asc->resets = aspeed_1030_fmc_resets; asc->flash_window_base = 0x80000000; asc->flash_window_size = 0x10000000; - asc->features = ASPEED_SMC_FEATURE_DMA; + asc->features = ASPEED_SMC_FEATURE_DMA | + ASPEED_SMC_FEATURE_WDT_CONTROL; asc->dma_flash_mask = 0x0FFFFFFC; asc->dma_dram_mask = 0x000BFFFC; asc->dma_start_length = 1; From 41ae2640c4d88dabed9469b3a3bda4d6c0317240 Mon Sep 17 00:00:00 2001 From: Manos Pitsidianakis Date: Thu, 17 Jul 2025 21:59:28 +0300 Subject: [PATCH 2711/2760] docs/devel/submitting-a-patch.rst: add b4 section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a section about b4, an actively maintained and widely packaged CLI tool for contributing to patch-based development projects. Reviewed-by: Gustavo Romero Signed-off-by: Manos Pitsidianakis Message-ID: <20250717-docs_add_b4_section-v2-1-69212ed39299@linaro.org> Signed-off-by: Alex Bennée --- docs/devel/submitting-a-patch.rst | 40 +++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/docs/devel/submitting-a-patch.rst b/docs/devel/submitting-a-patch.rst index f7917b899f..dd1cf32ad3 100644 --- a/docs/devel/submitting-a-patch.rst +++ b/docs/devel/submitting-a-patch.rst @@ -235,6 +235,38 @@ to another list.) ``git send-email`` (`step-by-step setup guide works best for delivering the patch without mangling it, but attachments can be used as a last resort on a first-time submission. +.. _use_b4: + +Use B4 +~~~~~~ + +The `b4`_ tool, used for Linux kernel development, can also be used for QEMU +development. It is packaged in most distros and PyPi. The QEMU source tree +includes a ``b4`` project configuration file at the root: ``.b4-config``. + +Example workflow to prepare a patch series: + +1. Start with a clean checkout of the ``master`` branch. +2. Create a new series with a topical branch name using ``b4 prep -n descriptive-name``. + ``b4`` will create a ``b4/descriptive-name`` branch and switch to it. +3. Commit your changes, following this page's guidelines about proper commit messages etc. +4. Write a descriptive cover letter with ``b4 prep --edit-cover``. +5. Add maintainer and reviewer CCs with ``b4 prep --auto-to-cc``. You can make + changes to Cc: and To: recipients by editing the cover letter. +6. Run patch checks with ``b4 prep --check``. +7. Optionally review the patches with ``b4 send --dry-run`` which will print the + raw patches in standard output. + +To send the patches, you can: + +- Setup ``git-send-email`` and use ``b4 send``, or +- Export the patches to files using ``b4 send -o OUTPUT_DIR`` and send them manually. + +For more details, consult the `b4 documentation`_. + +.. _b4 documentation: https://b4.docs.kernel.org/ +.. _b4: https://github.com/mricon/b4/ + .. _use_git_publish: Use git-publish @@ -418,7 +450,7 @@ Retrieve an existing series --------------------------- If you want to apply an existing series on top of your tree, you can simply use -`b4 `__. +`b4`_. :: @@ -533,7 +565,11 @@ summary belongs. The `git-publish `__ script can help with tracking a good summary across versions. Also, the `git-backport-diff `__ script can help focus -reviewers on what changed between revisions. +reviewers on what changed between revisions. The ``b4`` tool automatically +generates a version history section in the cover letter, including links to the +previous versions on `Lore`_. + +.. _Lore: https://lore.kernel.org/ .. _tips_and_tricks: From 0311a6edb9db34a41a2662d94c37e1fbaabf6ecf Mon Sep 17 00:00:00 2001 From: Peter Maydell Date: Mon, 21 Jul 2025 16:33:41 +0100 Subject: [PATCH 2712/2760] scripts/make-release: Go back to cloning all the EDK2 submodules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit bd0da3a3d4f we changed make-release so that instead of cloning every git submodule of EDK2 we only cloned a fixed list. The original motivation for this was that one of the submodules: * was from a non-github repo * that repo had a "SSL certificate expired" failure * wasn't actually needed for the set of EDK2 binaries we build and at the time we were trying to build the EDK2 binaries in one of our CI jobs. Unfortunately this change meant that we were exposed to bugs where EDK2 adds a new submodule and the sources we ship in the release tarball won't build any more. In particular, in EDK2 commit c6bb7d54beb05 the MipiSysTLib submodule was added, causing failure of the ROM build in our tarball starting from QEMU release 8.2.0: /tmp/qemu-10.0.0/roms/edk2/MdePkg/MdePkg.dec(32): error 000E: File/directory not found in workspace Library/MipiSysTLib/mipisyst/library/include is not found in packages path: /tmp/qemu-10.0.0/roms/. /tmp/qemu-10.0.0/roms/edk2 (Building from a QEMU git checkout works fine.) In the intervening time EDK2 moved the submodule that had a problem to be one they mirrored themselves (and at time of writing all their submodules are hosted on github), and we stopped trying to build EDK2 binaries in our own CI jobs with commit 690ceb71936f9037f6. Go back to cloning every EDK2 submodule, so we don't have an untested explicit list of submodules which will break without our noticing it. This increases the size of the QEMU tarball .tar.xz file from 133M to 139M in my testing. Cc: qemu-stable@nongnu.org Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3041 Signed-off-by: Peter Maydell Reviewed-by: Michael Tokarev Message-ID: <20250721153341.2910800-1-peter.maydell@linaro.org> Signed-off-by: Alex Bennée --- scripts/make-release | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/scripts/make-release b/scripts/make-release index 4509a9fabf..87f563ef5f 100755 --- a/scripts/make-release +++ b/scripts/make-release @@ -62,17 +62,15 @@ meson subprojects download $SUBPROJECTS (cd roms/skiboot && ./make_version.sh > .version) # Fetch edk2 submodule's submodules, since it won't have access to them via # the tarball later. -# -# A more uniform way to handle this sort of situation would be nice, but we -# don't necessarily have much control over how a submodule handles its -# submodule dependencies, so we continue to handle these on a case-by-case -# basis for now. -(cd roms/edk2 && \ - git submodule update --init --depth 1 -- \ - ArmPkg/Library/ArmSoftFloatLib/berkeley-softfloat-3 \ - BaseTools/Source/C/BrotliCompress/brotli \ - CryptoPkg/Library/OpensslLib/openssl \ - MdeModulePkg/Library/BrotliCustomDecompressLib/brotli) + +# As recommended by the EDK2 readme, we don't use --recursive here. +# EDK2 won't use any code or feature from a submodule of a submodule, +# so we don't need to add them to the tarball. +# Although we don't necessarily need all of the submodules that EDK2 +# has, we clone them all, to avoid running into problems where EDK2 +# adds a new submodule or changes its use of an existing one and +# the sources we ship in the tarball then fail to build. +(cd roms/edk2 && git submodule update --init --depth 1) popd exclude=(--exclude=.git) From 61432e805e5028df0a3df5a76915cdc3007ecd41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 4 Aug 2025 11:43:08 +0100 Subject: [PATCH 2713/2760] tests/docker: fix debian-all-test-cross MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It turns out you can't easily expand an ENV var across multiple steps in a dockerfile. This meant we silently dropped the architectures we should have even on amd64 hosts. As the updated AVAILABLE_COMPILERS is only needed for the following apt install line just merge them. Fixes: 6da616bb170 (tests/docker: handle host-arch selection for all-test-cross) Reviewed-by: Manos Pitsidianakis Signed-off-by: Alex Bennée Message-ID: <20250804104308.250949-1-alex.bennee@linaro.org> --- tests/docker/dockerfiles/debian-all-test-cross.docker | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/docker/dockerfiles/debian-all-test-cross.docker b/tests/docker/dockerfiles/debian-all-test-cross.docker index ef69bbc8a5..420a4e33e6 100644 --- a/tests/docker/dockerfiles/debian-all-test-cross.docker +++ b/tests/docker/dockerfiles/debian-all-test-cross.docker @@ -62,9 +62,7 @@ RUN if dpkg-architecture -e amd64; then \ export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-powerpc64-linux-gnu libc6-dev-ppc64-cross"; \ export AVAILABLE_COMPILERS="${AVAILABLE_COMPILERS} gcc-sparc64-linux-gnu libc6-dev-sparc64-cross"; \ fi && \ - echo "compilers: ${AVAILABLE_COMPILERS}" - -RUN DEBIAN_FRONTEND=noninteractive eatmydata \ + DEBIAN_FRONTEND=noninteractive eatmydata \ apt install -y --no-install-recommends \ ${AVAILABLE_COMPILERS} && \ dpkg-query --showformat '${Package}_${Version}_${Architecture}\n' --show > /packages.txt From 0a9a27305da030a83e65714f6ba202dbcaf448a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Jul 2025 15:56:39 +0200 Subject: [PATCH 2714/2760] hw/sd/sdcard: Do not ignore errors in sd_cmd_to_sendingdata() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unfortunately when adding sd_cmd_to_sendingdata() in commit f486bf7d109 we neglected to return any possible error. Fix. Fixes: f486bf7d109 ("hw/sd/sdcard: Introduce sd_cmd_to_sendingdata and sd_generic_read_byte") Reviewed-by: Richard Henderson Signed-off-by: Philippe Mathieu-Daudé Message-Id: <20250804133406.17456-2-philmd@linaro.org> --- hw/sd/sd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index c275fdda2d..0bb385268e 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1305,7 +1305,7 @@ static sd_rsp_type_t sd_cmd_to_sendingdata(SDState *sd, SDRequest req, const void *data, size_t size) { if (sd->state != sd_transfer_state) { - sd_invalid_state_for_cmd(sd, req); + return sd_invalid_state_for_cmd(sd, req); } sd->state = sd_sendingdata_state; From 3025ea65bd515196e871adc8959336c51b9d27bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Mon, 4 Aug 2025 11:32:44 +0200 Subject: [PATCH 2715/2760] hw/sd/sdcard: Factor sd_response_size() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set @rsplen once before switching to fill the response buffer. This will allow to assert in a single place that the buffer is big enough to be filled with the response. Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250804133406.17456-3-philmd@linaro.org> --- hw/sd/sd.c | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 0bb385268e..76ce54664f 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -729,6 +729,33 @@ static int sd_req_crc_validate(SDRequest *req) return sd_crc7(buffer, 5) != req->crc; /* TODO */ } +static size_t sd_response_size(SDState *sd, sd_rsp_type_t rtype) +{ + switch (rtype) { + case sd_r1: + case sd_r1b: + return 4; + + case sd_r2_i: + case sd_r2_s: + return 16; + + case sd_r3: + case sd_r7: + return 4; + + case sd_r6: + return 4; + + case sd_r0: + case sd_illegal: + return 0; + + default: + g_assert_not_reached(); + } +} + static void sd_response_r1_make(SDState *sd, uint8_t *response) { stl_be_p(response, sd->card_status); @@ -2203,36 +2230,32 @@ static int sd_do_command(SDState *sd, SDRequest *req, } send_response: + rsplen = sd_response_size(sd, rtype); + switch (rtype) { case sd_r1: case sd_r1b: sd_response_r1_make(sd, response); - rsplen = 4; break; case sd_r2_i: memcpy(response, sd->cid, sizeof(sd->cid)); - rsplen = 16; break; case sd_r2_s: memcpy(response, sd->csd, sizeof(sd->csd)); - rsplen = 16; break; case sd_r3: sd_response_r3_make(sd, response); - rsplen = 4; break; case sd_r6: sd_response_r6_make(sd, response); - rsplen = 4; break; case sd_r7: sd_response_r7_make(sd, response); - rsplen = 4; break; case sd_r0: @@ -2244,7 +2267,6 @@ static int sd_do_command(SDState *sd, SDRequest *req, sd->data_offset = 0; /* fall-through */ case sd_illegal: - rsplen = 0; break; default: g_assert_not_reached(); From b82e7a2a1da5638c4c51fcf5a254b65762080b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 31 Jul 2025 13:55:25 +0200 Subject: [PATCH 2716/2760] hw/sd/sdbus: Provide buffer size to sdbus_do_command() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We provide to sdbus_do_command() a pointer to a buffer to be filled with a varying number of bytes. By not providing the buffer size, the callee can not check the buffer is big enough. Pass the buffer size as argument to follow good practices. sdbus_do_command() doesn't return any error, only the size filled in the buffer. Convert the returned type to unsigned and remove the few unreachable lines in callers. This allow to check for possible overflow in sd_do_command(). Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Richard Henderson Message-Id: <20250804133406.17456-4-philmd@linaro.org> --- hw/sd/allwinner-sdhost.c | 7 ++----- hw/sd/bcm2835_sdhost.c | 7 ++----- hw/sd/core.c | 5 +++-- hw/sd/omap_mmc.c | 5 +++-- hw/sd/pl181.c | 6 ++---- hw/sd/sd.c | 6 ++++-- hw/sd/sdhci.c | 6 +++--- hw/sd/ssi-sd.c | 12 +++++++----- include/hw/sd/sd.h | 23 +++++++++++++++++++++-- 9 files changed, 47 insertions(+), 30 deletions(-) diff --git a/hw/sd/allwinner-sdhost.c b/hw/sd/allwinner-sdhost.c index b31da5c399..9d61b372e7 100644 --- a/hw/sd/allwinner-sdhost.c +++ b/hw/sd/allwinner-sdhost.c @@ -233,7 +233,7 @@ static void allwinner_sdhost_send_command(AwSdHostState *s) { SDRequest request; uint8_t resp[16]; - int rlen; + size_t rlen; /* Auto clear load flag */ s->command &= ~SD_CMDR_LOAD; @@ -246,10 +246,7 @@ static void allwinner_sdhost_send_command(AwSdHostState *s) request.arg = s->command_arg; /* Send request to SD bus */ - rlen = sdbus_do_command(&s->sdbus, &request, resp); - if (rlen < 0) { - goto error; - } + rlen = sdbus_do_command(&s->sdbus, &request, resp, sizeof(resp)); /* If the command has a response, store it in the response registers */ if ((s->command & SD_CMDR_RESPONSE)) { diff --git a/hw/sd/bcm2835_sdhost.c b/hw/sd/bcm2835_sdhost.c index 29debdf59e..f7cef7bb1c 100644 --- a/hw/sd/bcm2835_sdhost.c +++ b/hw/sd/bcm2835_sdhost.c @@ -113,15 +113,12 @@ static void bcm2835_sdhost_send_command(BCM2835SDHostState *s) { SDRequest request; uint8_t rsp[16]; - int rlen; + size_t rlen; request.cmd = s->cmd & SDCMD_CMD_MASK; request.arg = s->cmdarg; - rlen = sdbus_do_command(&s->sdbus, &request, rsp); - if (rlen < 0) { - goto error; - } + rlen = sdbus_do_command(&s->sdbus, &request, rsp, sizeof(rsp)); if (!(s->cmd & SDCMD_NO_RESPONSE)) { if (rlen == 0 || (rlen == 4 && (s->cmd & SDCMD_LONG_RESPONSE))) { goto error; diff --git a/hw/sd/core.c b/hw/sd/core.c index 4b30218b52..d3c9017445 100644 --- a/hw/sd/core.c +++ b/hw/sd/core.c @@ -90,7 +90,8 @@ void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts) } } -int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response) +size_t sdbus_do_command(SDBus *sdbus, SDRequest *req, + uint8_t *resp, size_t respsz) { SDState *card = get_card(sdbus); @@ -98,7 +99,7 @@ int sdbus_do_command(SDBus *sdbus, SDRequest *req, uint8_t *response) if (card) { SDCardClass *sc = SDMMC_COMMON_GET_CLASS(card); - return sc->do_command(card, req, response); + return sc->do_command(card, req, resp, respsz); } return 0; diff --git a/hw/sd/omap_mmc.c b/hw/sd/omap_mmc.c index b7648d41cc..5a1d25defa 100644 --- a/hw/sd/omap_mmc.c +++ b/hw/sd/omap_mmc.c @@ -130,7 +130,8 @@ static void omap_mmc_command(OMAPMMCState *host, int cmd, int dir, sd_rsp_type_t resptype, int init) { uint32_t rspstatus, mask; - int rsplen, timeout; + size_t rsplen; + int timeout; SDRequest request; uint8_t response[16]; @@ -157,7 +158,7 @@ static void omap_mmc_command(OMAPMMCState *host, int cmd, int dir, request.arg = host->arg; request.crc = 0; /* FIXME */ - rsplen = sdbus_do_command(&host->sdbus, &request, response); + rsplen = sdbus_do_command(&host->sdbus, &request, response, sizeof(response)); /* TODO: validate CRCs */ switch (resptype) { diff --git a/hw/sd/pl181.c b/hw/sd/pl181.c index b8fc9f86f1..5d56ead4d9 100644 --- a/hw/sd/pl181.c +++ b/hw/sd/pl181.c @@ -173,14 +173,12 @@ static void pl181_do_command(PL181State *s) { SDRequest request; uint8_t response[16]; - int rlen; + size_t rlen; request.cmd = s->cmd & PL181_CMD_INDEX; request.arg = s->cmdarg; trace_pl181_command_send(request.cmd, request.arg); - rlen = sdbus_do_command(&s->sdbus, &request, response); - if (rlen < 0) - goto error; + rlen = sdbus_do_command(&s->sdbus, &request, response, sizeof(response)); if (s->cmd & PL181_CMD_RESPONSE) { if (rlen == 0 || (rlen == 4 && (s->cmd & PL181_CMD_LONGRESP))) goto error; diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 76ce54664f..069107a2e7 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -2166,8 +2166,9 @@ static bool cmd_valid_while_locked(SDState *sd, unsigned cmd) return cmd_class == 0 || cmd_class == 7; } -static int sd_do_command(SDState *sd, SDRequest *req, - uint8_t *response) { +static size_t sd_do_command(SDState *sd, SDRequest *req, + uint8_t *response, size_t respsz) +{ int last_state; sd_rsp_type_t rtype; int rsplen; @@ -2231,6 +2232,7 @@ static int sd_do_command(SDState *sd, SDRequest *req, send_response: rsplen = sd_response_size(sd, rtype); + assert(rsplen <= respsz); switch (rtype) { case sd_r1: diff --git a/hw/sd/sdhci.c b/hw/sd/sdhci.c index 226ff133ff..3c897e54b7 100644 --- a/hw/sd/sdhci.c +++ b/hw/sd/sdhci.c @@ -337,7 +337,7 @@ static void sdhci_send_command(SDHCIState *s) { SDRequest request; uint8_t response[16]; - int rlen; + size_t rlen; bool timeout = false; s->errintsts = 0; @@ -346,7 +346,7 @@ static void sdhci_send_command(SDHCIState *s) request.arg = s->argument; trace_sdhci_send_command(request.cmd, request.arg); - rlen = sdbus_do_command(&s->sdbus, &request, response); + rlen = sdbus_do_command(&s->sdbus, &request, response, sizeof(response)); if (s->cmdreg & SDHC_CMD_RESPONSE) { if (rlen == 4) { @@ -400,7 +400,7 @@ static void sdhci_end_transfer(SDHCIState *s) request.cmd = 0x0C; request.arg = 0; trace_sdhci_end_transfer(request.cmd, request.arg); - sdbus_do_command(&s->sdbus, &request, response); + sdbus_do_command(&s->sdbus, &request, response, sizeof(response)); /* Auto CMD12 response goes to the upper Response register */ s->rspreg[3] = ldl_be_p(response); } diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 6c90a86ab4..3025f8f15f 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -146,8 +146,9 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) /* manually issue cmd12 to stop the transfer */ request.cmd = 12; request.arg = 0; - s->arglen = sdbus_do_command(&s->sdbus, &request, longresp); - if (s->arglen <= 0) { + s->arglen = sdbus_do_command(&s->sdbus, &request, + longresp, sizeof(longresp)); + if (s->arglen == 0) { s->arglen = 1; /* a zero value indicates the card is busy */ s->response[0] = 0; @@ -171,8 +172,9 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) request.cmd = s->cmd; request.arg = ldl_be_p(s->cmdarg); DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg); - s->arglen = sdbus_do_command(&s->sdbus, &request, longresp); - if (s->arglen <= 0) { + s->arglen = sdbus_do_command(&s->sdbus, &request, + longresp, sizeof(longresp)); + if (s->arglen == 0) { s->arglen = 1; s->response[0] = 4; DPRINTF("SD command failed\n"); @@ -333,7 +335,7 @@ static int ssi_sd_post_load(void *opaque, int version_id) return -EINVAL; } if (s->mode == SSI_SD_CMDARG && - (s->arglen < 0 || s->arglen >= ARRAY_SIZE(s->cmdarg))) { + (s->arglen >= ARRAY_SIZE(s->cmdarg))) { return -EINVAL; } if (s->mode == SSI_SD_RESPONSE && diff --git a/include/hw/sd/sd.h b/include/hw/sd/sd.h index d6bad17513..55d363f58f 100644 --- a/include/hw/sd/sd.h +++ b/include/hw/sd/sd.h @@ -96,7 +96,17 @@ struct SDCardClass { DeviceClass parent_class; /*< public >*/ - int (*do_command)(SDState *sd, SDRequest *req, uint8_t *response); + /** + * Process a SD command request. + * @sd: card + * @req: command request + * @resp: buffer to receive the command response + * @respsz: size of @resp buffer + * + * Return: size of the response + */ + size_t (*do_command)(SDState *sd, SDRequest *req, + uint8_t *resp, size_t respsz); /** * Write a byte to a SD card. * @sd: card @@ -153,7 +163,16 @@ struct SDBusClass { void sdbus_set_voltage(SDBus *sdbus, uint16_t millivolts); uint8_t sdbus_get_dat_lines(SDBus *sdbus); bool sdbus_get_cmd_line(SDBus *sdbus); -int sdbus_do_command(SDBus *sd, SDRequest *req, uint8_t *response); +/** + * sdbus_do_command: Process a SD command request + * @sd: card + * @req: command request + * @resp: buffer to receive the command response + * @respsz: size of @resp buffer + * + * Return: size of the response + */ +size_t sdbus_do_command(SDBus *sd, SDRequest *req, uint8_t *resp, size_t respsz); /** * Write a byte to a SD bus. * @sd: bus From 1585ab9f1baf6f0b471483455a74ea7abe9add0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Jul 2025 14:04:43 +0200 Subject: [PATCH 2717/2760] hw/sd/sdcard: Fill SPI response bits in card code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ssi-sd.c contains the SPI link layer adaptation, while sd.c contains all the SD card internal details. We already handle the response values in sd.c, but missed the SPI case. Complete them (fill R1, prepend R1 in R3/R7 and always return something in SPI mode). Remove all the duplication in ssi-sd.c. Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-5-philmd@linaro.org> --- hw/sd/sd.c | 32 ++++++++++++++++--- hw/sd/ssi-sd.c | 87 ++++---------------------------------------------- 2 files changed, 35 insertions(+), 84 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 069107a2e7..cbcc180f6a 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -734,22 +734,24 @@ static size_t sd_response_size(SDState *sd, sd_rsp_type_t rtype) switch (rtype) { case sd_r1: case sd_r1b: - return 4; + return sd_is_spi(sd) ? 1 : 4; case sd_r2_i: case sd_r2_s: + assert(!sd_is_spi(sd)); return 16; case sd_r3: case sd_r7: - return 4; + return sd_is_spi(sd) ? 5 : 4; case sd_r6: + assert(!sd_is_spi(sd)); return 4; case sd_r0: case sd_illegal: - return 0; + return sd_is_spi(sd) ? 1 : 0; default: g_assert_not_reached(); @@ -758,7 +760,19 @@ static size_t sd_response_size(SDState *sd, sd_rsp_type_t rtype) static void sd_response_r1_make(SDState *sd, uint8_t *response) { - stl_be_p(response, sd->card_status); + if (sd_is_spi(sd)) { + response[0] = sd->state == sd_idle_state + && !FIELD_EX32(sd->ocr, OCR, CARD_POWER_UP); + response[0] |= FIELD_EX32(sd->card_status, CSR, ERASE_RESET) << 1; + response[0] |= FIELD_EX32(sd->card_status, CSR, ILLEGAL_COMMAND) << 2; + response[0] |= FIELD_EX32(sd->card_status, CSR, COM_CRC_ERROR) << 3; + response[0] |= FIELD_EX32(sd->card_status, CSR, ERASE_SEQ_ERROR) << 4; + response[0] |= FIELD_EX32(sd->card_status, CSR, ADDRESS_ERROR) << 5; + response[0] |= FIELD_EX32(sd->card_status, CSR, BLOCK_LEN_ERROR) << 6; + response[0] |= 0 << 7; + } else { + stl_be_p(response, sd->card_status); + } /* Clear the "clear on read" status bits */ sd->card_status &= ~CARD_STATUS_C; @@ -766,6 +780,11 @@ static void sd_response_r1_make(SDState *sd, uint8_t *response) static void sd_response_r3_make(SDState *sd, uint8_t *response) { + if (sd_is_spi(sd)) { + /* Prepend R1 */ + sd_response_r1_make(sd, response); + response++; + } stl_be_p(response, sd->ocr & ACMD41_R3_MASK); } @@ -783,6 +802,11 @@ static void sd_response_r6_make(SDState *sd, uint8_t *response) static void sd_response_r7_make(SDState *sd, uint8_t *response) { + if (sd_is_spi(sd)) { + /* Prepend R1 */ + sd_response_r1_make(sd, response); + response++; + } stl_be_p(response, sd->vhs); } diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 3025f8f15f..2d5c0ad501 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -70,23 +70,6 @@ struct ssi_sd_state { #define TYPE_SSI_SD "ssi-sd" OBJECT_DECLARE_SIMPLE_TYPE(ssi_sd_state, SSI_SD) -/* State word bits. */ -#define SSI_SDR_LOCKED 0x0001 -#define SSI_SDR_WP_ERASE 0x0002 -#define SSI_SDR_ERROR 0x0004 -#define SSI_SDR_CC_ERROR 0x0008 -#define SSI_SDR_ECC_FAILED 0x0010 -#define SSI_SDR_WP_VIOLATION 0x0020 -#define SSI_SDR_ERASE_PARAM 0x0040 -#define SSI_SDR_OUT_OF_RANGE 0x0080 -#define SSI_SDR_IDLE 0x0100 -#define SSI_SDR_ERASE_RESET 0x0200 -#define SSI_SDR_ILLEGAL_COMMAND 0x0400 -#define SSI_SDR_COM_CRC_ERROR 0x0800 -#define SSI_SDR_ERASE_SEQ_ERROR 0x1000 -#define SSI_SDR_ADDRESS_ERROR 0x2000 -#define SSI_SDR_PARAMETER_ERROR 0x4000 - /* multiple block write */ #define SSI_TOKEN_MULTI_WRITE 0xfc /* terminate multiple block write */ @@ -104,7 +87,7 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) { ssi_sd_state *s = SSI_SD(dev); SDRequest request; - uint8_t longresp[16]; + uint8_t longresp[5]; /* * Special case: allow CMD12 (STOP TRANSMISSION) while reading data. @@ -171,74 +154,18 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) /* FIXME: Check CRC. */ request.cmd = s->cmd; request.arg = ldl_be_p(s->cmdarg); - DPRINTF("CMD%d arg 0x%08x\n", s->cmd, request.arg); s->arglen = sdbus_do_command(&s->sdbus, &request, longresp, sizeof(longresp)); - if (s->arglen == 0) { - s->arglen = 1; - s->response[0] = 4; - DPRINTF("SD command failed\n"); - } else if (s->cmd == 8 || s->cmd == 58) { - /* CMD8/CMD58 returns R3/R7 response */ - DPRINTF("Returned R3/R7\n"); - s->arglen = 5; - s->response[0] = 1; - memcpy(&s->response[1], longresp, 4); - } else if (s->arglen != 4) { - BADF("Unexpected response to cmd %d\n", s->cmd); - /* Illegal command is about as near as we can get. */ - s->arglen = 1; - s->response[0] = 4; - } else { - /* All other commands return status. */ - uint32_t cardstatus; - uint16_t status; + DPRINTF("CMD%d arg 0x%08x = %d\n", s->cmd, request.arg, s->arglen); + assert(s->arglen > 0); /* CMD13 returns a 2-byte statuse work. Other commands only return the first byte. */ s->arglen = (s->cmd == 13) ? 2 : 1; + memcpy(s->response, longresp, s->arglen); - /* handle R1b */ - if (s->cmd == 28 || s->cmd == 29 || s->cmd == 38) { - s->stopping = 1; - } - - cardstatus = ldl_be_p(longresp); - status = 0; - if (((cardstatus >> 9) & 0xf) < 4) - status |= SSI_SDR_IDLE; - if (cardstatus & ERASE_RESET) - status |= SSI_SDR_ERASE_RESET; - if (cardstatus & ILLEGAL_COMMAND) - status |= SSI_SDR_ILLEGAL_COMMAND; - if (cardstatus & COM_CRC_ERROR) - status |= SSI_SDR_COM_CRC_ERROR; - if (cardstatus & ERASE_SEQ_ERROR) - status |= SSI_SDR_ERASE_SEQ_ERROR; - if (cardstatus & ADDRESS_ERROR) - status |= SSI_SDR_ADDRESS_ERROR; - if (cardstatus & CARD_IS_LOCKED) - status |= SSI_SDR_LOCKED; - if (cardstatus & (LOCK_UNLOCK_FAILED | WP_ERASE_SKIP)) - status |= SSI_SDR_WP_ERASE; - if (cardstatus & SD_ERROR) - status |= SSI_SDR_ERROR; - if (cardstatus & CC_ERROR) - status |= SSI_SDR_CC_ERROR; - if (cardstatus & CARD_ECC_FAILED) - status |= SSI_SDR_ECC_FAILED; - if (cardstatus & WP_VIOLATION) - status |= SSI_SDR_WP_VIOLATION; - if (cardstatus & ERASE_PARAM) - status |= SSI_SDR_ERASE_PARAM; - if (cardstatus & (OUT_OF_RANGE | CID_CSD_OVERWRITE)) - status |= SSI_SDR_OUT_OF_RANGE; - /* ??? Don't know what Parameter Error really means, so - assume it's set if the second byte is nonzero. */ - if (status & 0xff) - status |= SSI_SDR_PARAMETER_ERROR; - s->response[0] = status >> 8; - s->response[1] = status; - DPRINTF("Card status 0x%02x\n", status); + /* handle R1b (busy signal) */ + if (s->cmd == 28 || s->cmd == 29 || s->cmd == 38) { + s->stopping = 1; } s->mode = SSI_SD_PREP_RESP; s->response_pos = 0; From 0f2ff994793247c710c79f95302f3e8c10d070de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Tue, 29 Jul 2025 14:05:09 +0200 Subject: [PATCH 2718/2760] hw/sd/sdcard: Implement SPI R2 return value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In SPI mode, R2 is a 2-byte value. Implement in spi_response_r2_make() and return SPI R2 in the SEND_STATUS commands. Reported-by: Guenter Roeck Fixes: 775616c3ae8 ("Partial SD card SPI mode support") Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-6-philmd@linaro.org> --- hw/sd/sd.c | 38 +++++++++++++++++++++++++++++++++++--- hw/sd/ssi-sd.c | 3 --- 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index cbcc180f6a..01ec6d951c 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -61,6 +61,7 @@ typedef enum { sd_r0 = 0, /* no response */ sd_r1, /* normal response command */ + spi_r2, /* STATUS */ sd_r2_i, /* CID register */ sd_r2_s, /* CSD register */ sd_r3, /* OCR register */ @@ -247,6 +248,7 @@ static const char *sd_response_name(sd_rsp_type_t rsp) static const char *response_name[] = { [sd_r0] = "RESP#0 (no response)", [sd_r1] = "RESP#1 (normal cmd)", + [spi_r2] = "RESP#2 (STATUS reg)", [sd_r2_i] = "RESP#2 (CID reg)", [sd_r2_s] = "RESP#2 (CSD reg)", [sd_r3] = "RESP#3 (OCR reg)", @@ -736,6 +738,10 @@ static size_t sd_response_size(SDState *sd, sd_rsp_type_t rtype) case sd_r1b: return sd_is_spi(sd) ? 1 : 4; + case spi_r2: + assert(sd_is_spi(sd)); + return 2; + case sd_r2_i: case sd_r2_s: assert(!sd_is_spi(sd)); @@ -778,6 +784,22 @@ static void sd_response_r1_make(SDState *sd, uint8_t *response) sd->card_status &= ~CARD_STATUS_C; } +static void spi_response_r2_make(SDState *sd, uint8_t *resp) +{ + /* Prepend R1 */ + sd_response_r1_make(sd, resp); + + resp[1] = FIELD_EX32(sd->card_status, CSR, CARD_IS_LOCKED) << 0; + resp[1] |= (FIELD_EX32(sd->card_status, CSR, LOCK_UNLOCK_FAILED) + || FIELD_EX32(sd->card_status, CSR, WP_ERASE_SKIP)) << 1; + resp[1] |= FIELD_EX32(sd->card_status, CSR, ERROR) << 2; + resp[1] |= FIELD_EX32(sd->card_status, CSR, CC_ERROR) << 3; + resp[1] |= FIELD_EX32(sd->card_status, CSR, CARD_ECC_FAILED) << 4; + resp[1] |= FIELD_EX32(sd->card_status, CSR, WP_VIOLATION) << 5; + resp[1] |= FIELD_EX32(sd->card_status, CSR, ERASE_PARAM) << 6; + resp[1] |= FIELD_EX32(sd->card_status, CSR, OUT_OF_RANGE) << 7; +} + static void sd_response_r3_make(SDState *sd, uint8_t *response) { if (sd_is_spi(sd)) { @@ -1643,7 +1665,7 @@ static sd_rsp_type_t sd_cmd_SEND_STATUS(SDState *sd, SDRequest req) } if (sd_is_spi(sd)) { - return sd_r2_s; + return spi_r2; } return sd_req_rca_same(sd, req) ? sd_r1 : sd_r0; @@ -1957,8 +1979,14 @@ static sd_rsp_type_t sd_acmd_SET_BUS_WIDTH(SDState *sd, SDRequest req) /* ACMD13 */ static sd_rsp_type_t sd_acmd_SD_STATUS(SDState *sd, SDRequest req) { - return sd_cmd_to_sendingdata(sd, req, 0, - sd->sd_status, sizeof(sd->sd_status)); + sd_rsp_type_t rsp; + + rsp = sd_cmd_to_sendingdata(sd, req, 0, + sd->sd_status, sizeof(sd->sd_status)); + if (sd_is_spi(sd) && rsp != sd_illegal) { + return spi_r2; + } + return rsp; } /* ACMD22 */ @@ -2264,6 +2292,10 @@ static size_t sd_do_command(SDState *sd, SDRequest *req, sd_response_r1_make(sd, response); break; + case spi_r2: + spi_response_r2_make(sd, response); + break; + case sd_r2_i: memcpy(response, sd->cid, sizeof(sd->cid)); break; diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 2d5c0ad501..594dead19e 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -158,9 +158,6 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) longresp, sizeof(longresp)); DPRINTF("CMD%d arg 0x%08x = %d\n", s->cmd, request.arg, s->arglen); assert(s->arglen > 0); - /* CMD13 returns a 2-byte statuse work. Other commands - only return the first byte. */ - s->arglen = (s->cmd == 13) ? 2 : 1; memcpy(s->response, longresp, s->arglen); /* handle R1b (busy signal) */ From 3241a61a1374b4d9b0e835fb3fa5b4085377eebb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 30 Jul 2025 17:35:06 +0200 Subject: [PATCH 2719/2760] hw/sd/sdcard: Use complete SEND_OP_COND implementation in SPI mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit While spi_cmd_SEND_OP_COND() is incomplete, sd_cmd_SEND_OP_COND() is, except it doesn't return the correct value in SPI mode. Correct and use, removing the need for spi_cmd_SEND_OP_COND(). Fixes: 775616c3ae8 ("Partial SD card SPI mode support") Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-7-philmd@linaro.org> --- hw/sd/sd.c | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 01ec6d951c..df2a272c6a 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1414,14 +1414,6 @@ static sd_rsp_type_t sd_cmd_GO_IDLE_STATE(SDState *sd, SDRequest req) return sd_is_spi(sd) ? sd_r1 : sd_r0; } -/* CMD1 */ -static sd_rsp_type_t spi_cmd_SEND_OP_COND(SDState *sd, SDRequest req) -{ - sd->state = sd_transfer_state; - - return sd_r1; -} - /* CMD2 */ static sd_rsp_type_t sd_cmd_ALL_SEND_CID(SDState *sd, SDRequest req) { @@ -2046,6 +2038,9 @@ static sd_rsp_type_t sd_cmd_SEND_OP_COND(SDState *sd, SDRequest req) sd->state = sd_ready_state; } + if (sd_is_spi(sd)) { + return sd_r1; + } return sd_r3; } @@ -2590,7 +2585,7 @@ static const SDProto sd_proto_spi = { .name = "SPI", .cmd = { [0] = {0, sd_spi, "GO_IDLE_STATE", sd_cmd_GO_IDLE_STATE}, - [1] = {0, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, + [1] = {0, sd_spi, "SEND_OP_COND", sd_cmd_SEND_OP_COND}, [5] = {9, sd_spi, "IO_SEND_OP_COND", sd_cmd_optional}, [6] = {10, sd_spi, "SWITCH_FUNCTION", sd_cmd_SWITCH_FUNCTION}, [8] = {0, sd_spi, "SEND_IF_COND", sd_cmd_SEND_IF_COND}, @@ -2626,7 +2621,7 @@ static const SDProto sd_proto_spi = { [13] = {8, sd_spi, "SD_STATUS", sd_acmd_SD_STATUS}, [22] = {8, sd_spi, "SEND_NUM_WR_BLOCKS", sd_acmd_SEND_NUM_WR_BLOCKS}, [23] = {8, sd_spi, "SET_WR_BLK_ERASE_COUNT", sd_acmd_SET_WR_BLK_ERASE_COUNT}, - [41] = {8, sd_spi, "SEND_OP_COND", spi_cmd_SEND_OP_COND}, + [41] = {8, sd_spi, "SEND_OP_COND", sd_cmd_SEND_OP_COND}, [42] = {8, sd_spi, "SET_CLR_CARD_DETECT", sd_acmd_SET_CLR_CARD_DETECT}, [51] = {8, sd_spi, "SEND_SCR", sd_acmd_SEND_SCR}, }, From 3c7bde41a37546a49e10adafc54e9201ac087585 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 31 Jul 2025 11:17:52 +0200 Subject: [PATCH 2720/2760] hw/sd/sdcard: Allow using SWITCH_FUNCTION in more SPI states MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In SPI mode, SWITCH_FUNCTION is valid in all mode (except the IDLE one). Fixes: 775616c3ae8 ("Partial SD card SPI mode support") Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-8-philmd@linaro.org> --- hw/sd/sd.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index df2a272c6a..a9efa15859 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1488,8 +1488,14 @@ static sd_rsp_type_t sd_cmd_SWITCH_FUNCTION(SDState *sd, SDRequest req) if (sd->mode != sd_data_transfer_mode) { return sd_invalid_mode_for_cmd(sd, req); } - if (sd->state != sd_transfer_state) { - return sd_invalid_state_for_cmd(sd, req); + if (sd_is_spi(sd)) { + if (sd->state == sd_idle_state) { + return sd_invalid_state_for_cmd(sd, req); + } + } else { + if (sd->state != sd_transfer_state) { + return sd_invalid_state_for_cmd(sd, req); + } } sd_function_switch(sd, req.arg); From 7574baef43f0d1bd6982be7d5087af3bca2a7a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 31 Jul 2025 15:38:18 +0200 Subject: [PATCH 2721/2760] hw/sd/sdcard: Factor spi_cmd_SEND_CxD() out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit spi_cmd_SEND_CSD() and spi_cmd_SEND_CID() are very similar. Factor the common code as spi_cmd_SEND_CxD(). Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-9-philmd@linaro.org> --- hw/sd/sd.c | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index a9efa15859..ee81dc0999 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1588,14 +1588,19 @@ static sd_rsp_type_t emmc_cmd_SEND_EXT_CSD(SDState *sd, SDRequest req) sd->ext_csd, sizeof(sd->ext_csd)); } -/* CMD9 */ -static sd_rsp_type_t spi_cmd_SEND_CSD(SDState *sd, SDRequest req) +static sd_rsp_type_t spi_cmd_SEND_CxD(SDState *sd, SDRequest req, + const void *data, size_t size) { if (sd->state != sd_standby_state) { return sd_invalid_state_for_cmd(sd, req); } - return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), - sd->csd, 16); + return sd_cmd_to_sendingdata(sd, req, 0, data, size); +} + +/* CMD9 */ +static sd_rsp_type_t spi_cmd_SEND_CSD(SDState *sd, SDRequest req) +{ + return spi_cmd_SEND_CxD(sd, req, sd->csd, sizeof(sd->csd)); } static sd_rsp_type_t sd_cmd_SEND_CSD(SDState *sd, SDRequest req) @@ -1610,11 +1615,7 @@ static sd_rsp_type_t sd_cmd_SEND_CSD(SDState *sd, SDRequest req) /* CMD10 */ static sd_rsp_type_t spi_cmd_SEND_CID(SDState *sd, SDRequest req) { - if (sd->state != sd_standby_state) { - return sd_invalid_state_for_cmd(sd, req); - } - return sd_cmd_to_sendingdata(sd, req, sd_req_get_address(sd, req), - sd->cid, 16); + return spi_cmd_SEND_CxD(sd, req, sd->cid, sizeof(sd->cid)); } static sd_rsp_type_t sd_cmd_SEND_CID(SDState *sd, SDRequest req) From 823d9b00452b62dcee1b0692c578c6fa5eef517e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 31 Jul 2025 15:58:31 +0200 Subject: [PATCH 2722/2760] hw/sd/sdcard: Disable checking STBY mode in SPI SEND_CSD/CID MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The card should be in STANDBY mode to process SEND_CSD or SEND_CID, but is still in IDLE mode. Unfortunately I don't have enough time to keep debugging this issue, so disable the check for the time being and the next release, as it blocks Linux. I'll keep looking. Reported-by: Guenter Roeck Reported-by: Ben Dooks Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-10-philmd@linaro.org> --- hw/sd/sd.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index ee81dc0999..22f3099771 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -1591,9 +1591,20 @@ static sd_rsp_type_t emmc_cmd_SEND_EXT_CSD(SDState *sd, SDRequest req) static sd_rsp_type_t spi_cmd_SEND_CxD(SDState *sd, SDRequest req, const void *data, size_t size) { + /* + * XXX as of v10.1.0-rc1 command is reached in sd_idle_state, + * so disable this check. if (sd->state != sd_standby_state) { return sd_invalid_state_for_cmd(sd, req); } + */ + + /* + * Since SPI returns CSD and CID on the DAT lines, + * switch to sd_transfer_state. + */ + sd->state = sd_transfer_state; + return sd_cmd_to_sendingdata(sd, req, 0, data, size); } From 40b242884ed7aeb30613dd42eca6e8712d607c46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Thu, 31 Jul 2025 11:44:28 +0200 Subject: [PATCH 2723/2760] hw/sd/sdcard: Remove SDState::mode field MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SD card mode is a superset of its state (SDState::state), no need to migrate it. Use sd_mode() to get the SDCardModes from the SDCardStates. Fixes: 50a5be6c3d5 ("hw/sd.c: add SD card save/load support") Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-11-philmd@linaro.org> --- hw/sd/sd.c | 35 +++++++++++++++++------------------ hw/sd/trace-events | 4 ++-- 2 files changed, 19 insertions(+), 20 deletions(-) diff --git a/hw/sd/sd.c b/hw/sd/sd.c index 22f3099771..8c290595f0 100644 --- a/hw/sd/sd.c +++ b/hw/sd/sd.c @@ -147,7 +147,6 @@ struct SDState { /* Runtime changeables */ - uint32_t mode; /* current card mode, one of SDCardModes */ int32_t state; /* current card state, one of SDCardStates */ uint32_t vhs; bool wp_switch; @@ -315,27 +314,24 @@ static void sd_set_voltage(SDState *sd, uint16_t millivolts) } } -static void sd_set_mode(SDState *sd) +static enum SDCardModes sd_mode(SDState *sd) { switch (sd->state) { case sd_inactive_state: - sd->mode = sd_inactive; - break; - + return sd_inactive; case sd_idle_state: case sd_ready_state: case sd_identification_state: - sd->mode = sd_card_identification_mode; - break; - + return sd_card_identification_mode; case sd_standby_state: case sd_transfer_state: case sd_sendingdata_state: case sd_receivingdata_state: case sd_programming_state: case sd_disconnect_state: - sd->mode = sd_data_transfer_mode; - break; + return sd_data_transfer_mode; + default: + g_assert_not_reached(); } } @@ -1025,7 +1021,7 @@ static const VMStateDescription sd_vmstate = { .minimum_version_id = 2, .pre_load = sd_vmstate_pre_load, .fields = (const VMStateField[]) { - VMSTATE_UINT32(mode, SDState), + VMSTATE_UNUSED(4), VMSTATE_INT32(state, SDState), VMSTATE_UINT8_ARRAY(cid, SDState, 16), VMSTATE_UINT8_ARRAY(csd, SDState, 16), @@ -1325,7 +1321,7 @@ static sd_rsp_type_t sd_invalid_state_for_cmd(SDState *sd, SDRequest req) static sd_rsp_type_t sd_invalid_mode_for_cmd(SDState *sd, SDRequest req) { qemu_log_mask(LOG_GUEST_ERROR, "%s: CMD%i in a wrong mode: %s (spec %s)\n", - sd->proto->name, req.cmd, sd_mode_name(sd->mode), + sd->proto->name, req.cmd, sd_mode_name(sd_mode(sd)), sd_version_str(sd->spec_version)); return sd_illegal; @@ -1485,7 +1481,7 @@ static sd_rsp_type_t emmc_cmd_sleep_awake(SDState *sd, SDRequest req) /* CMD6 */ static sd_rsp_type_t sd_cmd_SWITCH_FUNCTION(SDState *sd, SDRequest req) { - if (sd->mode != sd_data_transfer_mode) { + if (sd_mode(sd) != sd_data_transfer_mode) { return sd_invalid_mode_for_cmd(sd, req); } if (sd_is_spi(sd)) { @@ -1658,7 +1654,7 @@ static sd_rsp_type_t sd_cmd_STOP_TRANSMISSION(SDState *sd, SDRequest req) /* CMD13 */ static sd_rsp_type_t sd_cmd_SEND_STATUS(SDState *sd, SDRequest req) { - if (sd->mode != sd_data_transfer_mode) { + if (sd_mode(sd) != sd_data_transfer_mode) { return sd_invalid_mode_for_cmd(sd, req); } @@ -1684,7 +1680,7 @@ static sd_rsp_type_t sd_cmd_SEND_STATUS(SDState *sd, SDRequest req) /* CMD15 */ static sd_rsp_type_t sd_cmd_GO_INACTIVE_STATE(SDState *sd, SDRequest req) { - if (sd->mode != sd_data_transfer_mode) { + if (sd_mode(sd) != sd_data_transfer_mode) { return sd_invalid_mode_for_cmd(sd, req); } switch (sd->state) { @@ -2090,7 +2086,9 @@ static sd_rsp_type_t sd_normal_command(SDState *sd, SDRequest req) if (req.cmd != 55 || sd->expecting_acmd) { trace_sdcard_normal_command(sd->proto->name, sd->last_cmd_name, req.cmd, - req.arg, sd_state_name(sd->state)); + req.arg, + sd_mode_name(sd_mode(sd)), + sd_state_name(sd->state)); } /* Not interpreting this as an app command */ @@ -2176,7 +2174,9 @@ static sd_rsp_type_t sd_app_command(SDState *sd, { sd->last_cmd_name = sd_acmd_name(sd, req.cmd); trace_sdcard_app_command(sd->proto->name, sd->last_cmd_name, - req.cmd, req.arg, sd_state_name(sd->state)); + req.cmd, req.arg, + sd_mode_name(sd_mode(sd)), + sd_state_name(sd->state)); sd->card_status |= APP_CMD; if (sd->proto->acmd[req.cmd].handler) { @@ -2276,7 +2276,6 @@ static size_t sd_do_command(SDState *sd, SDRequest *req, } last_state = sd->state; - sd_set_mode(sd); if (sd->expecting_acmd) { sd->expecting_acmd = false; diff --git a/hw/sd/trace-events b/hw/sd/trace-events index db0644256d..8d49840917 100644 --- a/hw/sd/trace-events +++ b/hw/sd/trace-events @@ -37,8 +37,8 @@ sdhci_write_dataport(uint16_t data_count) "write buffer filled with %u bytes of sdhci_capareg(const char *desc, uint16_t val) "%s: %u" # sd.c -sdcard_normal_command(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t arg, const char *state) "%s %20s/ CMD%02d arg 0x%08x (state %s)" -sdcard_app_command(const char *proto, const char *acmd_desc, uint8_t acmd, uint32_t arg, const char *state) "%s %23s/ACMD%02d arg 0x%08x (state %s)" +sdcard_normal_command(const char *proto, const char *cmd_desc, uint8_t cmd, uint32_t arg, const char *mode, const char *state) "%s %20s/ CMD%02d arg 0x%08x (mode %s, state %s)" +sdcard_app_command(const char *proto, const char *acmd_desc, uint8_t acmd, uint32_t arg, const char *mode, const char *state) "%s %23s/ACMD%02d arg 0x%08x (mode %s, state %s)" sdcard_response(const char *rspdesc, int rsplen) "%s (sz:%d)" sdcard_powerup(void) "" sdcard_inquiry_cmd41(void) "" From 90fd1591314781e7da8560e4008f411aba501b4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Wed, 30 Jul 2025 17:10:24 +0200 Subject: [PATCH 2724/2760] tests/functional: Test SD cards in SPI mode (using sifive_u machine) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a test which uses the sifive_u machine to boot a Linux kernel from a SD card connected via a SPI interface. Inspired from the command provided in: - https://lore.kernel.org/qemu-devel/94b2c5bf-53d0-4c74-8264-f3021916f38c@roeck-us.net/ - https://lore.kernel.org/qemu-devel/840016d0-0d49-4ef4-8372-b62b3bcd0ac6@codethink.co.uk/ Inspired-by: Guenter Roeck Inspired-by: Ben Dooks Signed-off-by: Philippe Mathieu-Daudé Acked-by: Richard Henderson Message-Id: <20250804133406.17456-12-philmd@linaro.org> --- MAINTAINERS | 1 + tests/functional/meson.build | 1 + tests/functional/test_riscv64_sifive_u.py | 51 +++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100755 tests/functional/test_riscv64_sifive_u.py diff --git a/MAINTAINERS b/MAINTAINERS index 28cea34271..a07086ed76 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -1695,6 +1695,7 @@ S: Supported F: docs/system/riscv/sifive_u.rst F: hw/*/*sifive*.c F: include/hw/*/*sifive*.h +F: tests/functional/test_riscv64_sifive_u.py AMD Microblaze-V Generic Board M: Sai Pavan Boddu diff --git a/tests/functional/meson.build b/tests/functional/meson.build index ecf965adc6..311c6f1806 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -274,6 +274,7 @@ tests_riscv64_system_quick = [ ] tests_riscv64_system_thorough = [ + 'riscv64_sifive_u', 'riscv64_tuxrun', ] diff --git a/tests/functional/test_riscv64_sifive_u.py b/tests/functional/test_riscv64_sifive_u.py new file mode 100755 index 0000000000..dc4cb8a4a9 --- /dev/null +++ b/tests/functional/test_riscv64_sifive_u.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +# +# Functional test that boots a Linux kernel on a Sifive U machine +# and checks the console +# +# Copyright (c) Linaro Ltd. +# +# Author: +# Philippe Mathieu-Daudé +# +# SPDX-License-Identifier: GPL-2.0-or-later + +import os + +from qemu_test import Asset, LinuxKernelTest +from qemu_test import skipIfMissingCommands + + +class SifiveU(LinuxKernelTest): + + ASSET_KERNEL = Asset( + 'https://storage.tuxboot.com/buildroot/20241119/riscv64/Image', + '2bd8132a3bf21570290042324fff48c987f42f2a00c08de979f43f0662ebadba') + ASSET_ROOTFS = Asset( + ('https://github.com/groeck/linux-build-test/raw/' + '9819da19e6eef291686fdd7b029ea00e764dc62f/rootfs/riscv64/' + 'rootfs.ext2.gz'), + 'b6ed95610310b7956f9bf20c4c9c0c05fea647900df441da9dfe767d24e8b28b') + + def test_riscv64_sifive_u_mmc_spi(self): + self.set_machine('sifive_u') + kernel_path = self.ASSET_KERNEL.fetch() + rootfs_path = self.uncompress(self.ASSET_ROOTFS) + + self.vm.set_console() + kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + + 'root=/dev/mmcblk0 rootwait ' + 'earlycon=sbi console=ttySIF0 ' + 'panic=-1 noreboot') + self.vm.add_args('-kernel', kernel_path, + '-drive', f'file={rootfs_path},if=sd,format=raw', + '-append', kernel_command_line, + '-no-reboot') + self.vm.launch() + self.wait_for_console_pattern('Boot successful.') + + os.remove(rootfs_path) + + +if __name__ == '__main__': + LinuxKernelTest.main() From 4e5d58969ed6927a7f1b08ba6223c34c8b990c92 Mon Sep 17 00:00:00 2001 From: Zhao Liu Date: Mon, 4 Aug 2025 13:35:48 +0800 Subject: [PATCH 2725/2760] target/i386/cpu: Move addressable ID encoding out of compat property in CPUID[0x1] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Currently, the addressable ID encoding for CPUID[0x1].EBX[bits 16-23] (Maximum number of addressable IDs for logical processors in this physical package) is covered by vendor_cpuid_only_v2 compat property. The previous consideration was to avoid breaking migration and this compat property makes it unfriendly to backport the commit f985a1195ba2 ("i386/cpu: Fix number of addressable IDs field for CPUID.01H.EBX [23:16]"). However, NetBSD booting is broken since the commit 88dd4ca06c83 ("i386/cpu: Use APIC ID info to encode cache topo in CPUID[4]"), because NetBSD calculates smt information via `lp_max` / `core_max` for legacy Intel CPUs which doesn't support 0xb leaf, where `lp_max` is from CPUID[0x1].EBX.bits[16-23] and `core_max` is from CPUID[0x4].0x0.bits[26 -31]. The commit 88dd4ca0 changed the encoding rule of `core_max` but didn't update `lp_max`, so that NetBSD would get the wrong smt information, which leads to the module loading failure. Luckily, the commit f985a1195ba2 ("i386/cpu: Fix number of addressable IDs field for CPUID.01H.EBX[23:16]") updated the encoding rule for `lp_max` and accidentally fixed the NetBSD issue too. This also shows that using CPUID[0x1] and CPUID[0x4].0x0 to calculate HT/SMT information is a common practice to detect CPU topology on legacy Intel CPUs. Therefore, it's necessary to backport the commit f985a1195ba2 to previous stable QEMU to help address the similar issues as well. Then the compat property is not needed any more since all stable QEMUs will follow the same encoding way. So, in CPUID[0x1], move addressable ID encoding out of compat property. Reported-by: Michael Tokarev Inspired-by: Chuang Xu Fixes: commit f985a1195ba2 ("i386/cpu: Fix number of addressable IDs field for CPUID.01H.EBX[23:16]") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3061 Signed-off-by: Zhao Liu Reviewed-by: Michael Tokarev Tested-by: Michael Tokarev Message-ID: <20250804053548.1808629-1-zhao1.liu@intel.com> Signed-off-by: Philippe Mathieu-Daudé --- target/i386/cpu.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 251d5760a0..673f8583c8 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -7885,8 +7885,7 @@ void cpu_x86_cpuid(CPUX86State *env, uint32_t index, uint32_t count, * count, but Intel needs maximum number of addressable IDs for * logical processors per package. */ - if (cpu->vendor_cpuid_only_v2 && - (IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { + if ((IS_INTEL_CPU(env) || IS_ZHAOXIN_CPU(env))) { num = 1 << apicid_pkg_offset(topo_info); } else { num = threads_per_pkg; From eb013cd6a11951a8d76e737e9f6e89c96b059b48 Mon Sep 17 00:00:00 2001 From: Eric Auger Date: Mon, 4 Aug 2025 17:20:07 +0200 Subject: [PATCH 2726/2760] hw/i386/microvm: Explicitly select ACPI_PCI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With a microvm-only build based on a custom device config, we get a link failure due to undefined reference to build_pci_host_bridge_osc_method() which is defined in hw/acpi/pci.c and whose compilation depends on CONFIG_ACPI_PCI. Although CONFIG_ACPI and CONFIG_PCI are set with such configuration, implied CONFIG_ACPI_PCI in config PCI_EXPRESS_GENERIC_BRIDGE is not selected as expected. It Looks like CONFIG_ACPI_PCI must be enforced and this patch selects CONFIG_ACPI_PCI in MICROVM config directly as done for PC config. Reproducer: ../configure \ --without-default-features \ --target-list=x86_64-softmmu \ --enable-kvm --disable-tcg \ --enable-pixman \ --enable-vnc \ --audio-drv-list="" \ --without-default-devices \ --with-devices-x86_64=microvm \ --enable-vhost-user with configs/devices/x86_64-softmmu/microvm.mak: CONFIG_PCI_DEVICES=n CONFIG_MICROVM=y CONFIG_VIRTIO_BLK=y CONFIG_VIRTIO_SERIAL=y CONFIG_VIRTIO_INPUT=y CONFIG_VIRTIO_INPUT_HOST=y CONFIG_VHOST_USER_INPUT=y CONFIG_VIRTIO_NET=y CONFIG_VIRTIO_SCSI=y CONFIG_VIRTIO_RNG=y CONFIG_VIRTIO_CRYPTO=y CONFIG_VIRTIO_BALLOON=y CONFIG_VIRTIO_GPU=y CONFIG_VHOST_USER_GPU=y FAILED: qemu-system-x86_64 cc -m64 @qemu-system-x86_64.rsp /usr/bin/ld: libsystem.a.p/hw_pci-host_gpex-acpi.c.o: in function `acpi_dsdt_add_host_bridge_methods': hw/pci-host/gpex-acpi.c:83:(.text+0x274): undefined reference to `build_pci_host_bridge_osc_method' collect2: error: ld returned 1 exit status Fixes: af151d50eac24 "hw/pci-host/gpex-acpi: Use build_pci_host_bridge_osc_method" Signed-off-by: Eric Auger Reported-by: Michael Tokarev Reviewed-by: Michael Tokarev Message-ID: <20250804152008.247673-1-eric.auger@redhat.com> Signed-off-by: Philippe Mathieu-Daudé --- hw/i386/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i386/Kconfig b/hw/i386/Kconfig index 5139d23087..3a0e2b8ebb 100644 --- a/hw/i386/Kconfig +++ b/hw/i386/Kconfig @@ -131,6 +131,7 @@ config MICROVM select I8259 select MC146818RTC select VIRTIO_MMIO + select ACPI_PCI select ACPI_HW_REDUCED select PCI_EXPRESS_GENERIC_BRIDGE select USB_XHCI_SYSBUS From b217d987a3c5c6e2473956723b396bc1ff0f5b2b Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Fri, 1 Aug 2025 14:53:14 +0300 Subject: [PATCH 2727/2760] qga: correctly write to /sys/power/state on linux Commit v9.0.0-343-g2048129625 introduced usage of g_file_set_contents() function to write to /sys/power/state. This function uses G_FILE_SET_CONTENTS_CONSISTENT flag to g_file_set_contents_full(), which is implemented by creating a temp file in the same directory and renaming it to the final destination. Which is not how sysfs works. Here, there's not a big deal to do open/write/close - it becomes almost the same as using g_file_set_contents[_full](). But it does not have surprises like this. Also, since this is linux code, it should be ok to use %m in the error reporting function. Fixes: 2048129625 "qga/commands-posix: don't do fork()/exec() when suspending via sysfs" Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3057 Signed-off-by: Michael Tokarev Reviewed-by: Stefan Hajnoczi Message-ID: <20250801115316.6845-1-mjt@tls.msk.ru> Signed-off-by: Stefan Hajnoczi --- qga/commands-linux.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/qga/commands-linux.c b/qga/commands-linux.c index 9e8a934b9a..9dc0c82503 100644 --- a/qga/commands-linux.c +++ b/qga/commands-linux.c @@ -1400,20 +1400,22 @@ static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp) static void linux_sys_state_suspend(SuspendMode mode, Error **errp) { - g_autoptr(GError) local_gerr = NULL; const char *sysfile_strs[3] = {"disk", "mem", NULL}; const char *sysfile_str = sysfile_strs[mode]; + int fd; if (!sysfile_str) { error_setg(errp, "unknown guest suspend mode"); return; } - if (!g_file_set_contents(LINUX_SYS_STATE_FILE, sysfile_str, - -1, &local_gerr)) { - error_setg(errp, "suspend: cannot write to '%s': %s", - LINUX_SYS_STATE_FILE, local_gerr->message); - return; + fd = open(LINUX_SYS_STATE_FILE, O_WRONLY); + if (fd < 0 || write(fd, sysfile_str, strlen(sysfile_str)) < 0) { + error_setg(errp, "suspend: cannot write to '%s': %m", + LINUX_SYS_STATE_FILE); + } + if (fd >= 0) { + close(fd); } } From afeb002e0ad49dcf388dedbc6995f6561885e376 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Tue, 5 Aug 2025 21:17:30 +0300 Subject: [PATCH 2728/2760] tests/qemu-iotests/tests/mirror-sparse: skip if O_DIRECT is not supported This test uses cache.direct=true, but does not check if O_DIRECT is supported by the underlying filesystem, and fails, for example, on a tmpfs (which is rather common on various auto-builders, in CI, etc). Fix this by using `_supported_cache_modes none directsync`. Fixes: c0ddcb2cbc146e "tests: Add iotest mirror-sparse for recent patches" Signed-off-by: Michael Tokarev Reviewed-by: Stefan Hajnoczi Message-ID: <20250805181731.282677-1-mjt@tls.msk.ru> Signed-off-by: Stefan Hajnoczi --- tests/qemu-iotests/tests/mirror-sparse | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/qemu-iotests/tests/mirror-sparse b/tests/qemu-iotests/tests/mirror-sparse index cfcaa600ab..3b183eea88 100755 --- a/tests/qemu-iotests/tests/mirror-sparse +++ b/tests/qemu-iotests/tests/mirror-sparse @@ -40,6 +40,7 @@ cd .. _supported_fmt qcow2 raw # Format of the source. dst is always raw file _supported_proto file _supported_os Linux +_supported_cache_modes none directsync _require_disk_usage echo From e0006a86615baa70bc9d8b183e528aed91c1ac90 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 5 Aug 2025 15:05:57 -0400 Subject: [PATCH 2729/2760] Update version for the v10.1.0-rc2 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 8ae905b0dd..c08f99e23e 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.91 +10.0.92 From a04ba043a3b03b49466b8ba95290e0507f268069 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20P=2E=20Berrang=C3=A9?= Date: Tue, 5 Aug 2025 19:24:31 +0100 Subject: [PATCH 2730/2760] meson: remove 'gnutls-bug1717-workaround' for migration TLS crashes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The implementation of this workaround does not currently work, so remove the option entirely to avoid exposing it to users. The code will remain (temporarily dormant) to be fixed in the next release cycle. Signed-off-by: Daniel P. Berrangé Reviewed-by: Fabiano Rosas Link: https://lore.kernel.org/qemu-devel/20250805182431.504158-1-berrange@redhat.com Signed-off-by: Fabiano Rosas --- meson.build | 6 +++--- meson_options.txt | 2 -- scripts/meson-buildoptions.sh | 5 ----- 3 files changed, 3 insertions(+), 10 deletions(-) diff --git a/meson.build b/meson.build index e53cd5b413..a7b3c683ce 100644 --- a/meson.build +++ b/meson.build @@ -1836,11 +1836,11 @@ if get_option('gnutls').enabled() or (get_option('gnutls').auto() and have_syste required: get_option('gnutls')) endif - if gnutls.found() and not get_option('gnutls-bug1717-workaround').disabled() + #if gnutls.found() and not get_option('gnutls-bug1717-workaround').disabled() # XXX: when bug 1717 is resolved, add logic to probe for # the GNUTLS fixed version number to handle the 'auto' case - gnutls_bug1717_workaround = true - endif + # gnutls_bug1717_workaround = true + #endif endif # We prefer use of gnutls for crypto, unless the options diff --git a/meson_options.txt b/meson_options.txt index dd33530750..fff1521e58 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -174,8 +174,6 @@ option('libcbor', type : 'feature', value : 'auto', description: 'libcbor support') option('gnutls', type : 'feature', value : 'auto', description: 'GNUTLS cryptography support') -option('gnutls-bug1717-workaround', type: 'feature', value : 'auto', - description: 'GNUTLS workaround for https://gitlab.com/gnutls/gnutls/-/issues/1717') option('nettle', type : 'feature', value : 'auto', description: 'nettle cryptography support') option('gcrypt', type : 'feature', value : 'auto', diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index d559e260ed..0ebe6bc52a 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -123,9 +123,6 @@ meson_options_help() { printf "%s\n" ' gio use libgio for D-Bus support' printf "%s\n" ' glusterfs Glusterfs block device driver' printf "%s\n" ' gnutls GNUTLS cryptography support' - printf "%s\n" ' gnutls-bug1717-workaround' - printf "%s\n" ' GNUTLS workaround for' - printf "%s\n" ' https://gitlab.com/gnutls/gnutls/-/issues/1717' printf "%s\n" ' gtk GTK+ user interface' printf "%s\n" ' gtk-clipboard clipboard support for the gtk UI (EXPERIMENTAL, MAY HANG)' printf "%s\n" ' guest-agent Build QEMU Guest Agent' @@ -334,8 +331,6 @@ _meson_option_parse() { --disable-glusterfs) printf "%s" -Dglusterfs=disabled ;; --enable-gnutls) printf "%s" -Dgnutls=enabled ;; --disable-gnutls) printf "%s" -Dgnutls=disabled ;; - --enable-gnutls-bug1717-workaround) printf "%s" -Dgnutls-bug1717-workaround=enabled ;; - --disable-gnutls-bug1717-workaround) printf "%s" -Dgnutls-bug1717-workaround=disabled ;; --enable-gtk) printf "%s" -Dgtk=enabled ;; --disable-gtk) printf "%s" -Dgtk=disabled ;; --enable-gtk-clipboard) printf "%s" -Dgtk_clipboard=enabled ;; From c7ac771ee750e37808928b62388fd27dcbf00f46 Mon Sep 17 00:00:00 2001 From: William Hu Date: Thu, 3 Apr 2025 01:07:56 +0000 Subject: [PATCH 2731/2760] ui/curses: Fix infinite loop on windows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace -1 comparisons for wint_t with WEOF to fix infinite loop caused by a 65535 == -1 comparison. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2905 Signed-off-by: William Hu Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Marc-André Lureau [ Marc-André - Add missing similar code change, remove a comment ] Signed-off-by: Marc-André Lureau Message-ID: --- ui/curses.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/ui/curses.c b/ui/curses.c index a39aee8762..161f78c35c 100644 --- a/ui/curses.c +++ b/ui/curses.c @@ -265,7 +265,8 @@ static int curses2foo(const int _curses2foo[], const int _curseskey2foo[], static void curses_refresh(DisplayChangeListener *dcl) { - int chr, keysym, keycode, keycode_alt; + wint_t chr = 0; + int keysym, keycode, keycode_alt; enum maybe_keycode maybe_keycode = CURSES_KEYCODE; curses_winch_check(); @@ -284,8 +285,9 @@ static void curses_refresh(DisplayChangeListener *dcl) /* while there are any pending key strokes to process */ chr = console_getch(&maybe_keycode); - if (chr == -1) + if (chr == WEOF) { break; + } #ifdef KEY_RESIZE /* this shouldn't occur when we use a custom SIGWINCH handler */ @@ -304,9 +306,9 @@ static void curses_refresh(DisplayChangeListener *dcl) /* alt or esc key */ if (keycode == 1) { enum maybe_keycode next_maybe_keycode = CURSES_KEYCODE; - int nextchr = console_getch(&next_maybe_keycode); + wint_t nextchr = console_getch(&next_maybe_keycode); - if (nextchr != -1) { + if (nextchr != WEOF) { chr = nextchr; maybe_keycode = next_maybe_keycode; keycode_alt = ALT; From 5a5d18c75666378ff5db70268dbd39bd38488f21 Mon Sep 17 00:00:00 2001 From: Michael Tokarev Date: Wed, 6 Aug 2025 10:48:37 +0300 Subject: [PATCH 2732/2760] tests/qemu-iotests/tests/mirror-sparse: actually require O_DIRECT Commit c0ddcb2cbc146e introduced the test which uses cache=direct mode, without checking if the scratch filesystem supports O_DIRECT. A subsequent commit, afeb002e0ad49d, tried to fix that issue, but instead of checking for o_direct, it checked for `_supported_cache_modes none directsync`, which is not what the original mirror-sparse test uses. Fix both by actually checking for o_direct. Fixes: c0ddcb2cbc146e "tests: Add iotest mirror-sparse for recent patches" Fixes: afeb002e0ad49d "tests/qemu-iotests/tests/mirror-sparse: skip if O_DIRECT is not supported" Reviewed-by: Stefan Hajnoczi Signed-off-by: Michael Tokarev --- tests/qemu-iotests/tests/mirror-sparse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/qemu-iotests/tests/mirror-sparse b/tests/qemu-iotests/tests/mirror-sparse index 3b183eea88..ee7101bd50 100755 --- a/tests/qemu-iotests/tests/mirror-sparse +++ b/tests/qemu-iotests/tests/mirror-sparse @@ -40,7 +40,7 @@ cd .. _supported_fmt qcow2 raw # Format of the source. dst is always raw file _supported_proto file _supported_os Linux -_supported_cache_modes none directsync +_require_o_direct _require_disk_usage echo From 8c9aae4364b25f0fd791eb3128b7650d7720d473 Mon Sep 17 00:00:00 2001 From: Stefan Weil via Date: Wed, 6 Aug 2025 22:45:58 +0200 Subject: [PATCH 2733/2760] meson: Fix brlapi compile test for Windows builds brlapi__openConnection returns a brlapi_fileDescriptor which is a pointer for Windows builds. The test for brlapi fails with cross builds on Debian trixie (x86_64-w64-mingw32-gcc (GCC) 14-win32): testfile.c:4:30: error: returning 'brlapi_fileDescriptor' {aka 'void *'} from a function with return type 'int' makes integer from pointer without a cast [-Wint-conversion] 4 | int main(void) { return brlapi__openConnection (NULL, NULL, NULL); } | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ----------- ../../../meson.build:1607: WARNING: could not link brlapi, disabling Signed-off-by: Stefan Weil Reviewed-by: Pierrick Bouvier Reviewed-by: Michael Tokarev Signed-off-by: Michael Tokarev --- meson.build | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/meson.build b/meson.build index a7b3c683ce..50c774a195 100644 --- a/meson.build +++ b/meson.build @@ -1586,9 +1586,11 @@ if not get_option('brlapi').auto() or have_system brlapi = cc.find_library('brlapi', has_headers: ['brlapi.h'], required: get_option('brlapi')) if brlapi.found() and not cc.links(''' - #include - #include - int main(void) { return brlapi__openConnection (NULL, NULL, NULL); }''', dependencies: brlapi) + #include + #include + int main(void) { + return brlapi__openConnection(NULL, NULL, NULL) == BRLAPI_INVALID_FILE_DESCRIPTOR; + }''', dependencies: brlapi) brlapi = not_found if get_option('brlapi').enabled() error('could not link brlapi') From e66644c48e96e81848c6aa94b185f59fc212d080 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Mon, 4 Aug 2025 21:22:12 +0800 Subject: [PATCH 2734/2760] target/loongarch: Fix [X]VLDI raising exception incorrectly According to the specification, [X]VLDI should trigger an invalid instruction exception only when Bit[12] is 1 and Bit[11:8] > 12. This patch fixes an issue where an exception was incorrectly raised even when Bit[12] was 0. Test case: ``` .global main main: vldi $vr0, 3328 ret ``` Reported-by: Zhou Qiankang Signed-off-by: WANG Rui Reviewed-by: Song Gao Message-ID: <20250804132212.4816-1-wangrui@loongson.cn> Signed-off-by: Song Gao --- target/loongarch/tcg/insn_trans/trans_vec.c.inc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/target/loongarch/tcg/insn_trans/trans_vec.c.inc b/target/loongarch/tcg/insn_trans/trans_vec.c.inc index 78730029cb..38bccf2838 100644 --- a/target/loongarch/tcg/insn_trans/trans_vec.c.inc +++ b/target/loongarch/tcg/insn_trans/trans_vec.c.inc @@ -3585,7 +3585,9 @@ static bool gen_vldi(DisasContext *ctx, arg_vldi *a, uint32_t oprsz) int sel, vece; uint64_t value; - if (!check_valid_vldi_mode(a)) { + sel = (a->imm >> 12) & 0x1; + + if (sel && !check_valid_vldi_mode(a)) { generate_exception(ctx, EXCCODE_INE); return true; } @@ -3594,8 +3596,6 @@ static bool gen_vldi(DisasContext *ctx, arg_vldi *a, uint32_t oprsz) return true; } - sel = (a->imm >> 12) & 0x1; - if (sel) { value = vldi_get_value(ctx, a->imm); vece = MO_64; From 76cfb87f5fcad4008359e44bf37508c265da1221 Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 16 Jul 2025 11:06:08 -0700 Subject: [PATCH 2735/2760] vfio/pci: augment set_handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Extend vfio_pci_msi_set_handler() so it can set or clear the handler. Add a similar accessor for INTx. No functional change. Signed-off-by: Steve Sistare Reviewed-by: Cédric Le Goater Link: https://lore.kernel.org/qemu-devel/1752689169-233452-2-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 2 +- hw/vfio/pci.c | 13 +++++++++++-- hw/vfio/pci.h | 3 ++- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index 384b56c4c7..ade7ff745d 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -70,7 +70,7 @@ static void vfio_cpr_claim_vectors(VFIOPCIDevice *vdev, int nr_vectors, fd = vfio_cpr_load_vector_fd(vdev, "interrupt", i); if (fd >= 0) { vfio_pci_vector_init(vdev, i); - vfio_pci_msi_set_handler(vdev, i); + vfio_pci_msi_set_handler(vdev, i, true); } if (vfio_cpr_load_vector_fd(vdev, "kvm_interrupt", i) >= 0) { diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 4fa692c1a3..4cd6894ca8 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -413,6 +413,14 @@ bool vfio_pci_intx_enable(VFIOPCIDevice *vdev, Error **errp) return vfio_intx_enable(vdev, errp); } +void vfio_pci_intx_set_handler(VFIOPCIDevice *vdev, bool enable) +{ + int fd = event_notifier_get_fd(&vdev->intx.interrupt); + IOHandler *handler = (enable ? vfio_intx_interrupt : NULL); + + qemu_set_fd_handler(fd, handler, NULL, vdev); +} + /* * MSI/X */ @@ -451,12 +459,13 @@ static void vfio_msi_interrupt(void *opaque) notify(&vdev->pdev, nr); } -void vfio_pci_msi_set_handler(VFIOPCIDevice *vdev, int nr) +void vfio_pci_msi_set_handler(VFIOPCIDevice *vdev, int nr, bool enable) { VFIOMSIVector *vector = &vdev->msi_vectors[nr]; int fd = event_notifier_get_fd(&vector->interrupt); + IOHandler *handler = (enable ? vfio_msi_interrupt : NULL); - qemu_set_fd_handler(fd, vfio_msi_interrupt, NULL, vector); + qemu_set_fd_handler(fd, handler, NULL, vector); } /* diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 81465a8214..2b564baf88 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -209,8 +209,9 @@ void vfio_pci_add_kvm_msi_virq(VFIOPCIDevice *vdev, VFIOMSIVector *vector, void vfio_pci_prepare_kvm_msi_virq_batch(VFIOPCIDevice *vdev); void vfio_pci_commit_kvm_msi_virq_batch(VFIOPCIDevice *vdev); bool vfio_pci_intx_enable(VFIOPCIDevice *vdev, Error **errp); +void vfio_pci_intx_set_handler(VFIOPCIDevice *vdev, bool enable); void vfio_pci_msix_set_notifiers(VFIOPCIDevice *vdev); -void vfio_pci_msi_set_handler(VFIOPCIDevice *vdev, int nr); +void vfio_pci_msi_set_handler(VFIOPCIDevice *vdev, int nr, bool enable); uint32_t vfio_pci_read_config(PCIDevice *pdev, uint32_t addr, int len); void vfio_pci_write_config(PCIDevice *pdev, From 322ee16824dc3d301477812a5dacb0249e1efe8c Mon Sep 17 00:00:00 2001 From: Steve Sistare Date: Wed, 16 Jul 2025 11:06:09 -0700 Subject: [PATCH 2736/2760] vfio/pci: preserve pending interrupts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cpr-transfer may lose a VFIO interrupt because the KVM instance is destroyed and recreated. If an interrupt arrives in the middle, it is dropped. To fix, stop pending new interrupts during cpr save, and pick up the pieces. In more detail: Stop the VCPUs. Call kvm_irqchip_remove_irqfd_notifier_gsi --> KVM_IRQFD to deassign the irqfd gsi that routes interrupts directly to the VCPU and KVM. After this call, interrupts fall back to the kernel vfio_msihandler, which writes to QEMU's kvm_interrupt eventfd. CPR already preserves that eventfd. When the route is re-established in new QEMU, the kernel tests the eventfd and injects an interrupt to KVM if necessary. Deassign INTx in a similar manner. For both MSI and INTx, remove the eventfd handler so old QEMU does not consume an event. If an interrupt was already pended to KVM prior to the completion of kvm_irqchip_remove_irqfd_notifier_gsi, it will be recovered by the subsequent call to cpu_synchronize_all_states, which pulls KVM interrupt state to userland prior to saving it in vmstate. Signed-off-by: Steve Sistare Reviewed-by: Zhenzhong Duan Link: https://lore.kernel.org/qemu-devel/1752689169-233452-3-git-send-email-steven.sistare@oracle.com Signed-off-by: Cédric Le Goater --- hw/vfio/cpr.c | 91 ++++++++++++++++++++++++++++++++++++++ hw/vfio/pci.c | 2 + hw/vfio/pci.h | 1 + include/hw/vfio/vfio-cpr.h | 6 +++ 4 files changed, 100 insertions(+) diff --git a/hw/vfio/cpr.c b/hw/vfio/cpr.c index ade7ff745d..a831243e02 100644 --- a/hw/vfio/cpr.c +++ b/hw/vfio/cpr.c @@ -200,3 +200,94 @@ void vfio_cpr_add_kvm_notifier(void) MIG_MODE_CPR_TRANSFER); } } + +static int set_irqfd_notifier_gsi(KVMState *s, EventNotifier *n, + EventNotifier *rn, int virq, bool enable) +{ + if (enable) { + return kvm_irqchip_add_irqfd_notifier_gsi(s, n, rn, virq); + } else { + return kvm_irqchip_remove_irqfd_notifier_gsi(s, n, virq); + } +} + +static int vfio_cpr_set_msi_virq(VFIOPCIDevice *vdev, Error **errp, bool enable) +{ + const char *op = (enable ? "enable" : "disable"); + PCIDevice *pdev = &vdev->pdev; + int i, nr_vectors, ret = 0; + + if (msix_enabled(pdev)) { + nr_vectors = vdev->msix->entries; + + } else if (msi_enabled(pdev)) { + nr_vectors = msi_nr_vectors_allocated(pdev); + + } else if (vfio_pci_read_config(pdev, PCI_INTERRUPT_PIN, 1)) { + ret = set_irqfd_notifier_gsi(kvm_state, &vdev->intx.interrupt, + &vdev->intx.unmask, vdev->intx.route.irq, + enable); + if (ret) { + error_setg_errno(errp, -ret, "failed to %s INTx irq %d", + op, vdev->intx.route.irq); + return ret; + } + vfio_pci_intx_set_handler(vdev, enable); + return ret; + + } else { + return 0; + } + + for (i = 0; i < nr_vectors; i++) { + VFIOMSIVector *vector = &vdev->msi_vectors[i]; + if (vector->use) { + ret = set_irqfd_notifier_gsi(kvm_state, &vector->kvm_interrupt, + NULL, vector->virq, enable); + if (ret) { + error_setg_errno(errp, -ret, + "failed to %s msi vector %d virq %d", + op, i, vector->virq); + return ret; + } + vfio_pci_msi_set_handler(vdev, i, enable); + } + } + + return ret; +} + +/* + * When CPR starts, detach IRQs from the VFIO device so future interrupts + * are posted to kvm_interrupt, which is preserved in new QEMU. Interrupts + * that were already posted to the old KVM instance, but not delivered to the + * VCPU, are recovered via KVM_GET_LAPIC and pushed to the new KVM instance + * in new QEMU. + * + * If CPR fails, reattach the IRQs. + */ +static int vfio_cpr_pci_notifier(NotifierWithReturn *notifier, + MigrationEvent *e, Error **errp) +{ + VFIOPCIDevice *vdev = + container_of(notifier, VFIOPCIDevice, cpr.transfer_notifier); + + if (e->type == MIG_EVENT_PRECOPY_SETUP) { + return vfio_cpr_set_msi_virq(vdev, errp, false); + } else if (e->type == MIG_EVENT_PRECOPY_FAILED) { + return vfio_cpr_set_msi_virq(vdev, errp, true); + } + return 0; +} + +void vfio_cpr_pci_register_device(VFIOPCIDevice *vdev) +{ + migration_add_notifier_mode(&vdev->cpr.transfer_notifier, + vfio_cpr_pci_notifier, + MIG_MODE_CPR_TRANSFER); +} + +void vfio_cpr_pci_unregister_device(VFIOPCIDevice *vdev) +{ + migration_remove_notifier(&vdev->cpr.transfer_notifier); +} diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index 4cd6894ca8..d5ea4a5a83 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3001,6 +3001,7 @@ void vfio_pci_put_device(VFIOPCIDevice *vdev) { vfio_display_finalize(vdev); vfio_bars_finalize(vdev); + vfio_cpr_pci_unregister_device(vdev); g_free(vdev->emulated_config_bits); g_free(vdev->rom); /* @@ -3480,6 +3481,7 @@ static void vfio_pci_realize(PCIDevice *pdev, Error **errp) vfio_pci_register_err_notifier(vdev); vfio_pci_register_req_notifier(vdev); vfio_setup_resetfn_quirk(vdev); + vfio_cpr_pci_register_device(vdev); return; diff --git a/hw/vfio/pci.h b/hw/vfio/pci.h index 2b564baf88..810a842f4a 100644 --- a/hw/vfio/pci.h +++ b/hw/vfio/pci.h @@ -188,6 +188,7 @@ struct VFIOPCIDevice { bool skip_vsc_check; VFIODisplay *dpy; Notifier irqchip_change_notifier; + VFIOPCICPR cpr; }; /* Use uin32_t for vendor & device so PCI_ANY_ID expands and cannot match hw */ diff --git a/include/hw/vfio/vfio-cpr.h b/include/hw/vfio/vfio-cpr.h index 80ad20d216..d37daffbc5 100644 --- a/include/hw/vfio/vfio-cpr.h +++ b/include/hw/vfio/vfio-cpr.h @@ -38,6 +38,10 @@ typedef struct VFIODeviceCPR { uint32_t ioas_id; } VFIODeviceCPR; +typedef struct VFIOPCICPR { + NotifierWithReturn transfer_notifier; +} VFIOPCICPR; + bool vfio_legacy_cpr_register_container(struct VFIOContainer *container, Error **errp); void vfio_legacy_cpr_unregister_container(struct VFIOContainer *container); @@ -77,5 +81,7 @@ extern const VMStateDescription vfio_cpr_pci_vmstate; extern const VMStateDescription vmstate_cpr_vfio_devices; void vfio_cpr_add_kvm_notifier(void); +void vfio_cpr_pci_register_device(struct VFIOPCIDevice *vdev); +void vfio_cpr_pci_unregister_device(struct VFIOPCIDevice *vdev); #endif /* HW_VFIO_VFIO_CPR_H */ From d9f4b45713e4e000a42ed1f33cb06b806173b559 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Le=20Goater?= Date: Tue, 5 Aug 2025 08:55:43 +0200 Subject: [PATCH 2737/2760] vfio: Document 'use-legacy-x86-rom' property MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 350785d41d8b ("ramfb: Add property to control if load the romfile") introduced the `use-legacy-x86-rom` property for the `vfio-pci-nohotplug` device. Add documentation for the property. Fixes: d5fcf0d960d8 ("hw/i386: Add the ramfb romfile compatibility") Reviewed-by: Philippe Mathieu-Daudé Link: https://lore.kernel.org/qemu-devel/20250805065543.120091-1-clg@redhat.com Signed-off-by: Cédric Le Goater --- hw/vfio/pci.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/vfio/pci.c b/hw/vfio/pci.c index d5ea4a5a83..07257d0fa0 100644 --- a/hw/vfio/pci.c +++ b/hw/vfio/pci.c @@ -3901,6 +3901,9 @@ static void vfio_pci_nohotplug_dev_class_init(ObjectClass *klass, "x-ramfb-migrate", "Override default migration support for ramfb support " "(DEBUG)"); + object_class_property_set_description(klass, /* 10.1 */ + "use-legacy-x86-rom", + "Controls loading of a legacy VGA BIOS ROM"); } static const TypeInfo vfio_pci_nohotplug_dev_info = { From 13b4d19ced4430a777a0ab9d08bda86b3fd01fca Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 31 Jul 2025 07:40:42 +0200 Subject: [PATCH 2738/2760] docs/devel/qapi-code-gen: Add two cross-references we missed Missed in commit 9c66762a601 (docs/qapi-code-gen: add cross-references). Signed-off-by: Markus Armbruster Message-ID: <20250731054044.4011789-2-armbru@redhat.com> Reviewed-by: John Snow --- docs/devel/qapi-code-gen.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index dfdbeac5a5..138921b386 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -646,9 +646,9 @@ Member 'event' names the event. This is the event name used in the Client JSON Protocol. Member 'data' defines the event-specific data. It defaults to an -empty MEMBERS object. +empty MEMBERS_ object. -If 'data' is a MEMBERS object, then MEMBERS defines event-specific +If 'data' is a MEMBERS_ object, then MEMBERS defines event-specific data just like a struct type's 'data' defines struct type members. If 'data' is a STRING, then STRING names a complex type whose members From 60e847dcf06aa4cc41cf7535d5ea4b1f5b34a616 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 31 Jul 2025 07:40:43 +0200 Subject: [PATCH 2739/2760] docs/devel/qapi-code-gen: Fix typos in QAPI schema language grammar Fixes: 3248c1aaf2db (docs: update the documentation upfront about schema configuration) Signed-off-by: Markus Armbruster Message-ID: <20250731054044.4011789-3-armbru@redhat.com> Reviewed-by: John Snow --- docs/devel/qapi-code-gen.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 138921b386..2cd51729c3 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -786,8 +786,8 @@ Configuring the schema Syntax:: COND = STRING - | { 'all: [ COND, ... ] } - | { 'any: [ COND, ... ] } + | { 'all': [ COND, ... ] } + | { 'any': [ COND, ... ] } | { 'not': COND } All definitions take an optional 'if' member. Its value must be a From 79f57adce686d0027608af4be363cde2409e5740 Mon Sep 17 00:00:00 2001 From: Markus Armbruster Date: Thu, 31 Jul 2025 07:40:44 +0200 Subject: [PATCH 2740/2760] docs/devel/qapi-code-gen: Update cross-reference syntax The new QAPI code generator creates a cross-reference target for each definition documentation. Enabled for the QEMU QMP Reference manual in commit a377f39f38f, and for the QEMU Storage Daemon QMP Reference Manual and the QEMU Guest Agent Protocol Reference in commit a6af5443440. We've put these targets to use since, but neglected to update doc comment markup documentation. Do that now. Co-developed-by: John Snow Signed-off-by: John Snow Signed-off-by: Markus Armbruster Message-ID: <20250731054044.4011789-4-armbru@redhat.com> --- docs/devel/qapi-code-gen.rst | 11 ++++++++--- docs/devel/qapi-domain.rst | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/devel/qapi-code-gen.rst b/docs/devel/qapi-code-gen.rst index 2cd51729c3..d97602f464 100644 --- a/docs/devel/qapi-code-gen.rst +++ b/docs/devel/qapi-code-gen.rst @@ -943,9 +943,14 @@ The usual ****strong****, *\*emphasized\** and ````literal```` markup should be used. If you need a single literal ``*``, you will need to backslash-escape it. -Use ``@foo`` to reference a name in the schema. This is an rST -extension. It is rendered the same way as ````foo````, but carries -additional meaning. +Use ```foo``` to reference a definition in the schema. This generates +a link to the definition. In the event that such a cross-reference is +ambiguous, you can use `QAPI cross-reference roles +` to disambiguate. + +Use @foo to reference a member description within the current +definition. This is an rST extension. It is currently rendered the +same way as ````foo````, but carries additional meaning. Example:: diff --git a/docs/devel/qapi-domain.rst b/docs/devel/qapi-domain.rst index fe540d1e40..1924f12d42 100644 --- a/docs/devel/qapi-domain.rst +++ b/docs/devel/qapi-domain.rst @@ -375,6 +375,7 @@ Will allow you to add arbitrary field lists in QAPI directives:: :see also: Lorem ipsum, dolor sit amet ... +.. _QAPI-domain-cross-references: Cross-references ================ From 31b737b19dca4d50758f5e9834d27b683102f2f1 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Tue, 3 Jun 2025 14:59:05 +0200 Subject: [PATCH 2741/2760] hw/nvme: fix namespace attachment Commit 6ccca4b6bb9f ("hw/nvme: rework csi handling") introduced a bug in Namespace Attachment, causing it to a) not allow a controller to attach namespaces to other controllers b) assert if a valid non-attached namespace is detached This fixes both issues. Fixes: 6ccca4b6bb9f ("hw/nvme: rework csi handling") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2976 Reviewed-by: Jesper Wendel Devantier Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index e764ec7683..6c06d7f8f9 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -6816,7 +6816,7 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) switch (sel) { case NVME_NS_ATTACHMENT_ATTACH: - if (nvme_ns(n, nsid)) { + if (nvme_ns(ctrl, nsid)) { return NVME_NS_ALREADY_ATTACHED | NVME_DNR; } @@ -6824,7 +6824,7 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) return NVME_NS_PRIVATE | NVME_DNR; } - if (!nvme_csi_supported(n, ns->csi)) { + if (!nvme_csi_supported(ctrl, ns->csi)) { return NVME_IOCS_NOT_SUPPORTED | NVME_DNR; } @@ -6834,6 +6834,10 @@ static uint16_t nvme_ns_attachment(NvmeCtrl *n, NvmeRequest *req) break; case NVME_NS_ATTACHMENT_DETACH: + if (!nvme_ns(ctrl, nsid)) { + return NVME_NS_NOT_ATTACHED | NVME_DNR; + } + nvme_detach_ns(ctrl, ns); nvme_update_dsm_limits(ctrl, NULL); From bc0c24fdb157b014932a5012fe4ccbeba7617f17 Mon Sep 17 00:00:00 2001 From: Klaus Jensen Date: Tue, 3 Jun 2025 14:59:06 +0200 Subject: [PATCH 2742/2760] hw/nvme: revert CMIC behavior Commit cd59f50ab017 ("hw/nvme: always initialize a subsystem") causes the controller to always set the CMIC.MCTRS ("Multiple Controllers") bit. While spec-compliant, this is a deviation from the previous behavior where this was only set if an nvme-subsys device was explicitly created (to configure a subsystem with multiple controllers/namespaces). Revert the behavior to only set CMIC.MCTRS if an nvme-subsys device is created explicitly. Reported-by: Alan Adamson Fixes: cd59f50ab017 ("hw/nvme: always initialize a subsystem") Reviewed-by: Alan Adamson Tested-by: Alan Adamson Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index 6c06d7f8f9..fa48412ef4 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8780,7 +8780,7 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) uint8_t *pci_conf = pci_dev->config; uint64_t cap = ldq_le_p(&n->bar.cap); NvmeSecCtrlEntry *sctrl = nvme_sctrl(n); - uint32_t ctratt; + uint32_t ctratt = le32_to_cpu(id->ctratt); uint16_t oacs; memcpy(n->cse.acs, nvme_cse_acs_default, sizeof(n->cse.acs)); @@ -8798,10 +8798,11 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->oaes = cpu_to_le32(NVME_OAES_NS_ATTR); - ctratt = NVME_CTRATT_ELBAS; + ctratt |= NVME_CTRATT_ELBAS; if (n->params.ctratt.mem) { ctratt |= NVME_CTRATT_MEM; } + id->ctratt = cpu_to_le32(ctratt); id->rab = 6; @@ -8884,17 +8885,6 @@ static void nvme_init_ctrl(NvmeCtrl *n, PCIDevice *pci_dev) id->psd[0].enlat = cpu_to_le32(0x10); id->psd[0].exlat = cpu_to_le32(0x4); - id->cmic |= NVME_CMIC_MULTI_CTRL; - ctratt |= NVME_CTRATT_ENDGRPS; - - id->endgidmax = cpu_to_le16(0x1); - - if (n->subsys->endgrp.fdp.enabled) { - ctratt |= NVME_CTRATT_FDPS; - } - - id->ctratt = cpu_to_le32(ctratt); - NVME_CAP_SET_MQES(cap, n->params.mqes); NVME_CAP_SET_CQR(cap, 1); NVME_CAP_SET_TO(cap, 0xf); @@ -8927,6 +8917,20 @@ static int nvme_init_subsys(NvmeCtrl *n, Error **errp) } n->subsys = NVME_SUBSYS(dev); + } else { + NvmeIdCtrl *id = &n->id_ctrl; + uint32_t ctratt = le32_to_cpu(id->ctratt); + + id->cmic |= NVME_CMIC_MULTI_CTRL; + ctratt |= NVME_CTRATT_ENDGRPS; + + id->endgidmax = cpu_to_le16(0x1); + + if (n->subsys->endgrp.fdp.enabled) { + ctratt |= NVME_CTRATT_FDPS; + } + + id->ctratt = cpu_to_le32(ctratt); } cntlid = nvme_subsys_register_ctrl(n, errp); From 53493c1f836f4dda90a6b5f2fb3d9264918c6871 Mon Sep 17 00:00:00 2001 From: Keith Busch Date: Fri, 1 Aug 2025 07:24:57 -0700 Subject: [PATCH 2743/2760] hw/nvme: cap MDTS value for internal limitation The emulated device had let the user set whatever max transfers size they wanted, including no limit. However the device does have an internal limit of 1024 segments. NVMe doesn't report max segments, though. This is implicitly inferred based on the MDTS and MPSMIN values. IOV_MAX is currently 1024 which 4k PRPs can exceed with 2MB transfers. Don't allow MDTS values that can exceed this, otherwise users risk seeing "internal error" status to their otherwise protocol compliant commands. Signed-off-by: Keith Busch Signed-off-by: Klaus Jensen --- hw/nvme/ctrl.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/hw/nvme/ctrl.c b/hw/nvme/ctrl.c index fa48412ef4..f5ee6bf260 100644 --- a/hw/nvme/ctrl.c +++ b/hw/nvme/ctrl.c @@ -8339,6 +8339,11 @@ static bool nvme_check_params(NvmeCtrl *n, Error **errp) host_memory_backend_set_mapped(n->pmr.dev, true); } + if (!n->params.mdts || ((1 << n->params.mdts) + 1) > IOV_MAX) { + error_setg(errp, "mdts exceeds IOV_MAX"); + return false; + } + if (n->params.zasl > n->params.mdts) { error_setg(errp, "zoned.zasl (Zone Append Size Limit) must be less " "than or equal to mdts (Maximum Data Transfer Size)"); From 621dbaee0152a283042db3cac6a51f91b3e06024 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Mon, 11 Aug 2025 12:23:15 -0400 Subject: [PATCH 2744/2760] tests/functional: fix URLs in PCI hotplug test for aarch64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Debian trixie has been released. The "stable" alias no longer refers to the Debian bookworm release, so URLs referring to bookworm artifacts via the "stable" alias no longer work. Switch to explicit release naming ("bookworm") to make a permalink so the test passes again. Suggested-by: Peter Maydell Fixes: 374a245573b8 ("tests/functional: Add PCI hotplug test for aarch64") Resolves: #3073 ("PCI hotplug test for aarch64 fails due to broken Debian installer URL") Reviewed-by: Alex Bennée Reviewed-by: Philippe Mathieu-Daudé Tested-by: Philippe Mathieu-Daudé Reviewed-by: Peter Maydell Message-ID: <20250811162315.59997-1-stefanha@redhat.com> Signed-off-by: Stefan Hajnoczi --- tests/functional/test_aarch64_hotplug_pci.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/functional/test_aarch64_hotplug_pci.py b/tests/functional/test_aarch64_hotplug_pci.py index c9bb7f1d97..0c67991b89 100755 --- a/tests/functional/test_aarch64_hotplug_pci.py +++ b/tests/functional/test_aarch64_hotplug_pci.py @@ -15,12 +15,12 @@ class HotplugPCI(LinuxKernelTest): ASSET_KERNEL = Asset( - ('https://ftp.debian.org/debian/dists/stable/main/installer-arm64/' + ('https://ftp.debian.org/debian/dists/bookworm/main/installer-arm64/' '20230607+deb12u11/images/netboot/debian-installer/arm64/linux'), 'd92a60392ce1e379ca198a1a820899f8f0d39a62d047c41ab79492f81541a9d9') ASSET_INITRD = Asset( - ('https://ftp.debian.org/debian/dists/stable/main/installer-arm64/' + ('https://ftp.debian.org/debian/dists/bookworm/main/installer-arm64/' '20230607+deb12u11/images/netboot/debian-installer/arm64/initrd.gz'), '9f817f76951f3237bca8216bee35267bfb826815687f4b2fcdd5e6c2a917790c') From f757d9d90d19b914d4023663bfc4da73bbbf007e Mon Sep 17 00:00:00 2001 From: Mauro Matteo Cascella Date: Mon, 11 Aug 2025 12:11:24 +0200 Subject: [PATCH 2745/2760] hw/uefi: clear uefi-vars buffer in uefi_vars_write callback When the guest writes to register UEFI_VARS_REG_BUFFER_SIZE, the .write callback `uefi_vars_write` is invoked. The function allocates a heap buffer without zeroing the memory, leaving the buffer filled with residual data from prior allocations. When the guest later reads from register UEFI_VARS_REG_PIO_BUFFER_TRANSFER, the .read callback `uefi_vars_read` returns leftover metadata or other sensitive process memory from the previously allocated buffer, leading to an information disclosure vulnerability. Fixes: CVE-2025-8860 Fixes: 90ca4e03c27d ("hw/uefi: add var-service-core.c") Reported-by: ZDI Suggested-by: Gerd Hoffmann Signed-off-by: Mauro Matteo Cascella Message-ID: <20250811101128.17661-1-mcascell@redhat.com> Signed-off-by: Gerd Hoffmann --- hw/uefi/var-service-core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hw/uefi/var-service-core.c b/hw/uefi/var-service-core.c index feec5a5958..6ab8df091a 100644 --- a/hw/uefi/var-service-core.c +++ b/hw/uefi/var-service-core.c @@ -259,8 +259,8 @@ static void uefi_vars_write(void *opaque, hwaddr addr, uint64_t val, unsigned si uv->buf_size = val; g_free(uv->buffer); g_free(uv->pio_xfer_buffer); - uv->buffer = g_malloc(uv->buf_size); - uv->pio_xfer_buffer = g_malloc(uv->buf_size); + uv->buffer = g_malloc0(uv->buf_size); + uv->pio_xfer_buffer = g_malloc0(uv->buf_size); break; case UEFI_VARS_REG_DMA_BUFFER_ADDR_LO: uv->buf_addr_lo = val; From 88e5a28d5aabb57f44c1805fbba0a458023f5106 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 11 Aug 2025 15:01:08 +0200 Subject: [PATCH 2746/2760] hw/uefi: return success for notifications MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Set status to SUCCESS for ready-to-boot and exit-boot-services notification calls. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Gerd Hoffmann Message-ID: <20250811130110.820958-2-kraxel@redhat.com> --- hw/uefi/var-service-vars.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c index 37d05b71cf..cbeccdbd26 100644 --- a/hw/uefi/var-service-vars.c +++ b/hw/uefi/var-service-vars.c @@ -702,12 +702,14 @@ uint32_t uefi_vars_mm_vars_proto(uefi_vars_state *uv) case SMM_VARIABLE_FUNCTION_READY_TO_BOOT: trace_uefi_event("ready-to-boot"); uv->ready_to_boot = true; + mvar->status = EFI_SUCCESS; length = 0; break; case SMM_VARIABLE_FUNCTION_EXIT_BOOT_SERVICE: trace_uefi_event("exit-boot-service"); uv->exit_boot_service = true; + mvar->status = EFI_SUCCESS; length = 0; break; From fc8ee8fe58ad410f27fca64e4ad212c5a3eabe00 Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 11 Aug 2025 15:01:09 +0200 Subject: [PATCH 2747/2760] hw/uefi: check access for first variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When listing variables (via get-next-variable-name) only the names of variables which can be accessed will be returned. That check was missing for the first variable though. Add it. Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Gerd Hoffmann Message-ID: <20250811130110.820958-3-kraxel@redhat.com> --- hw/uefi/var-service-vars.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hw/uefi/var-service-vars.c b/hw/uefi/var-service-vars.c index cbeccdbd26..8533533ea5 100644 --- a/hw/uefi/var-service-vars.c +++ b/hw/uefi/var-service-vars.c @@ -357,6 +357,9 @@ uefi_vars_mm_get_next_variable(uefi_vars_state *uv, mm_header *mhdr, if (uefi_strlen(name, nv->name_size) == 0) { /* empty string -> first */ var = QTAILQ_FIRST(&uv->variables); + while (var && !check_access(uv, var)) { + var = QTAILQ_NEXT(var, next); + } if (!var) { return uefi_vars_mm_error(mhdr, mvar, EFI_NOT_FOUND); } From 040237436f423253f3397547aa78d449394dfbca Mon Sep 17 00:00:00 2001 From: Gerd Hoffmann Date: Mon, 11 Aug 2025 15:01:10 +0200 Subject: [PATCH 2748/2760] hw/uefi: open json file in binary mode MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes file length discrepancies due to line ending conversions on windows hosts. Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3058 Reviewed-by: Philippe Mathieu-Daudé Signed-off-by: Gerd Hoffmann Message-ID: <20250811130110.820958-4-kraxel@redhat.com> --- hw/uefi/var-service-json.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hw/uefi/var-service-json.c b/hw/uefi/var-service-json.c index ad3462cd15..f5f1556833 100644 --- a/hw/uefi/var-service-json.c +++ b/hw/uefi/var-service-json.c @@ -172,7 +172,7 @@ static GString *uefi_vars_to_json(uefi_vars_state *uv) void uefi_vars_json_init(uefi_vars_state *uv, Error **errp) { if (uv->jsonfile) { - uv->jsonfd = qemu_create(uv->jsonfile, O_RDWR, 0666, errp); + uv->jsonfd = qemu_create(uv->jsonfile, O_RDWR | O_BINARY, 0666, errp); } } From 65677b7aad05edd20a8d2fe1c27b944f1ff9a002 Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 11 Aug 2025 16:29:23 +0200 Subject: [PATCH 2749/2760] configure: Don't disable Rust for too old meson version If the user explicitly specified --enable-rust, don't just fail if meson is too old for Rust support, but do the same thing as if meson was too old for the C code: Just download a newer one. In order to avoid the additional download for people who aren't intentionally opting in to Rust, keep the automatic disabling based on the meson version as the default if neither --enable-rust nor --disable-rust were given. Signed-off-by: Kevin Wolf Message-ID: <20250811142923.89983-1-kwolf@redhat.com> Reviewed-by: Paolo Bonzini Signed-off-by: Kevin Wolf --- configure | 8 +++++--- pythondeps.toml | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/configure b/configure index 825057ebf1..274a778764 100755 --- a/configure +++ b/configure @@ -1186,10 +1186,12 @@ fi meson_version=$($meson --version) if test "$rust" != disabled && ! version_ge "$meson_version" 1.8.1; then if test "$rust" = enabled; then - error_exit "Rust support needs Meson 1.8.1 or newer" + $mkvenv ensuregroup --dir "${source_path}/python/wheels" \ + ${source_path}/pythondeps.toml meson-rust || exit 1 + else + echo "Rust needs Meson 1.8.1, disabling" 2>&1 + rust=disabled fi - echo "Rust needs Meson 1.8.1, disabling" 2>&1 - rust=disabled fi if test "$rust" != disabled && has "$rustc" && $rustc -vV > "${TMPDIR1}/${TMPB}.out"; then rust_host_triple=$(sed -n 's/^host: //p' "${TMPDIR1}/${TMPB}.out") diff --git a/pythondeps.toml b/pythondeps.toml index b2eec940ce..d0f52b14f7 100644 --- a/pythondeps.toml +++ b/pythondeps.toml @@ -22,6 +22,10 @@ meson = { accepted = ">=1.5.0", installed = "1.8.1", canary = "meson" } pycotap = { accepted = ">=1.1.0", installed = "1.3.1" } +[meson-rust] +# The install key should match the version in python/wheels/ +meson = { accepted = ">=1.8.1", installed = "1.8.1", canary = "meson" } + [docs] # Please keep the installed versions in sync with docs/requirements.txt sphinx = { accepted = ">=3.4.3", installed = "6.2.1", canary = "sphinx-build" } From 4af976ef398e4e823addc00bf1c58787ba4952fe Mon Sep 17 00:00:00 2001 From: Kevin Wolf Date: Mon, 11 Aug 2025 15:40:10 +0200 Subject: [PATCH 2750/2760] rbd: Fix .bdrv_get_specific_info implementation qemu_rbd_get_specific_info() has at least two problems: The first is that it issues a blocking rbd_read() call in order to probe the encryption format for the image while querying the node. This means that if the connection to the server goes down, not only I/O is stuck (which is unavoidable), but query-names-block-nodes will actually make the whole QEMU instance unresponsive. .bdrv_get_specific_info implementations shouldn't perform blocking operations, but only return what is already known. The second is that the information returned isn't even correct. If the image is already opened with encryption enabled at the RBD level, we'll probe for "double encryption", i.e. if the encrypted data contains another encryption header. If it doesn't (which is the normal case), we won't return the encryption format. If it does, we return misleading information because it looks like we're talking about the outer level (the encryption format of the image itself) while the information is about an encryption header in the guest data. Fix this by storing the encryption format in BDRVRBDState when the image is opened (and we do blocking operations anyway) and returning only the stored information in qemu_rbd_get_specific_info(). The information we'll store is either the actual encryption format that we enabled on the RBD level, or if the image is unencrypted, the result of the same probing as we previously did when querying the node. Probing image formats based on content that can be modified by the guest has long been known as problematic, but as long as we only output it to the user instead of making decisions based on it, it should be okay. It is undoubtedly useful in the context of 'qemu-img info' when you're trying to figure out which encryption options you have to use to open the image successfully. Fixes: 42e4ac9ef5a6 ("block/rbd: Add support for rbd image encryption") Buglink: https://issues.redhat.com/browse/RHEL-105440 Signed-off-by: Kevin Wolf Message-ID: <20250811134010.81787-1-kwolf@redhat.com> Reviewed-by: Hanna Czenczek Signed-off-by: Kevin Wolf --- block/rbd.c | 104 ++++++++++++++++++++++++++++--------------- qapi/block-core.json | 9 +++- 2 files changed, 76 insertions(+), 37 deletions(-) diff --git a/block/rbd.c b/block/rbd.c index 951cd63f9a..3611dc81cf 100644 --- a/block/rbd.c +++ b/block/rbd.c @@ -99,6 +99,14 @@ typedef struct BDRVRBDState { char *namespace; uint64_t image_size; uint64_t object_size; + + /* + * If @bs->encrypted is true, this is the encryption format actually loaded + * at the librbd level. If it is false, it is the result of probing. + * RBD_IMAGE_ENCRYPTION_FORMAT__MAX means that encryption is not enabled and + * probing didn't find any known encryption header either. + */ + RbdImageEncryptionFormat encryption_format; } BDRVRBDState; typedef struct RBDTask { @@ -470,10 +478,12 @@ static int qemu_rbd_encryption_format(rbd_image_t image, return 0; } -static int qemu_rbd_encryption_load(rbd_image_t image, +static int qemu_rbd_encryption_load(BlockDriverState *bs, + rbd_image_t image, RbdEncryptionOptions *encrypt, Error **errp) { + BDRVRBDState *s = bs->opaque; int r = 0; g_autofree char *passphrase = NULL; rbd_encryption_luks1_format_options_t luks_opts; @@ -544,15 +554,19 @@ static int qemu_rbd_encryption_load(rbd_image_t image, error_setg_errno(errp, -r, "encryption load fail"); return r; } + bs->encrypted = true; + s->encryption_format = encrypt->format; return 0; } #ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2 -static int qemu_rbd_encryption_load2(rbd_image_t image, +static int qemu_rbd_encryption_load2(BlockDriverState *bs, + rbd_image_t image, RbdEncryptionOptions *encrypt, Error **errp) { + BDRVRBDState *s = bs->opaque; int r = 0; int encrypt_count = 1; int i; @@ -638,6 +652,8 @@ static int qemu_rbd_encryption_load2(rbd_image_t image, error_setg_errno(errp, -r, "layered encryption load fail"); goto exit; } + bs->encrypted = true; + s->encryption_format = encrypt->format; exit: for (i = 0; i < encrypt_count; ++i) { @@ -671,6 +687,45 @@ static int qemu_rbd_encryption_load2(rbd_image_t image, #endif #endif +/* + * For an image without encryption enabled on the rbd layer, probe the start of + * the image if it could be opened as an encrypted image so that we can display + * it when the user queries the node (most importantly in qemu-img). + * + * If the guest writes an encryption header to its disk after this probing, this + * won't be reflected when queried, but that's okay. There is no reason why the + * user should want to apply encryption at the rbd level while the image is + * still in use. This is just guest data. + */ +static void qemu_rbd_encryption_probe(BlockDriverState *bs) +{ + BDRVRBDState *s = bs->opaque; + char buf[RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {0}; + int r; + + assert(s->encryption_format == RBD_IMAGE_ENCRYPTION_FORMAT__MAX); + + r = rbd_read(s->image, 0, + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN, buf); + if (r < RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) { + return; + } + + if (memcmp(buf, rbd_luks_header_verification, + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { + s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; + } else if (memcmp(buf, rbd_luks2_header_verification, + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { + s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; + } else if (memcmp(buf, rbd_layered_luks_header_verification, + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { + s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; + } else if (memcmp(buf, rbd_layered_luks2_header_verification, + RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { + s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; + } +} + /* FIXME Deprecate and remove keypairs or make it available in QMP. */ static int qemu_rbd_do_create(BlockdevCreateOptions *options, const char *keypairs, const char *password_secret, @@ -1133,17 +1188,18 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, goto failed_open; } + s->encryption_format = RBD_IMAGE_ENCRYPTION_FORMAT__MAX; if (opts->encrypt) { #ifdef LIBRBD_SUPPORTS_ENCRYPTION if (opts->encrypt->parent) { #ifdef LIBRBD_SUPPORTS_ENCRYPTION_LOAD2 - r = qemu_rbd_encryption_load2(s->image, opts->encrypt, errp); + r = qemu_rbd_encryption_load2(bs, s->image, opts->encrypt, errp); #else r = -ENOTSUP; error_setg(errp, "RBD library does not support layered encryption"); #endif } else { - r = qemu_rbd_encryption_load(s->image, opts->encrypt, errp); + r = qemu_rbd_encryption_load(bs, s->image, opts->encrypt, errp); } if (r < 0) { goto failed_post_open; @@ -1153,6 +1209,8 @@ static int qemu_rbd_open(BlockDriverState *bs, QDict *options, int flags, error_setg(errp, "RBD library does not support image encryption"); goto failed_post_open; #endif + } else { + qemu_rbd_encryption_probe(bs); } r = rbd_stat(s->image, &info, sizeof(info)); @@ -1412,17 +1470,6 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs, { BDRVRBDState *s = bs->opaque; ImageInfoSpecific *spec_info; - char buf[RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN] = {0}; - int r; - - if (s->image_size >= RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) { - r = rbd_read(s->image, 0, - RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN, buf); - if (r < 0) { - error_setg_errno(errp, -r, "cannot read image start for probe"); - return NULL; - } - } spec_info = g_new(ImageInfoSpecific, 1); *spec_info = (ImageInfoSpecific){ @@ -1430,28 +1477,13 @@ static ImageInfoSpecific *qemu_rbd_get_specific_info(BlockDriverState *bs, .u.rbd.data = g_new0(ImageInfoSpecificRbd, 1), }; - if (memcmp(buf, rbd_luks_header_verification, - RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { - spec_info->u.rbd.data->encryption_format = - RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; - spec_info->u.rbd.data->has_encryption_format = true; - } else if (memcmp(buf, rbd_luks2_header_verification, - RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { - spec_info->u.rbd.data->encryption_format = - RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; - spec_info->u.rbd.data->has_encryption_format = true; - } else if (memcmp(buf, rbd_layered_luks_header_verification, - RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { - spec_info->u.rbd.data->encryption_format = - RBD_IMAGE_ENCRYPTION_FORMAT_LUKS; - spec_info->u.rbd.data->has_encryption_format = true; - } else if (memcmp(buf, rbd_layered_luks2_header_verification, - RBD_ENCRYPTION_LUKS_HEADER_VERIFICATION_LEN) == 0) { - spec_info->u.rbd.data->encryption_format = - RBD_IMAGE_ENCRYPTION_FORMAT_LUKS2; - spec_info->u.rbd.data->has_encryption_format = true; + if (s->encryption_format == RBD_IMAGE_ENCRYPTION_FORMAT__MAX) { + assert(!bs->encrypted); } else { - spec_info->u.rbd.data->has_encryption_format = false; + ImageInfoSpecificRbd *rbd_info = spec_info->u.rbd.data; + + rbd_info->has_encryption_format = true; + rbd_info->encryption_format = s->encryption_format; } return spec_info; diff --git a/qapi/block-core.json b/qapi/block-core.json index ebbe95b3d8..dc6eb4ae23 100644 --- a/qapi/block-core.json +++ b/qapi/block-core.json @@ -159,7 +159,14 @@ ## # @ImageInfoSpecificRbd: # -# @encryption-format: Image encryption format +# @encryption-format: Image encryption format. If encryption is enabled for the +# image (see encrypted in BlockNodeInfo), this is the actual format in which the +# image is accessed. If encryption is not enabled, this is the result of +# probing when the image was opened, to give a suggestion which encryption +# format could be enabled. Note that probing results can be changed by the +# guest by writing a (possibly partial) encryption format header to the +# image, so don't treat this information as trusted if the guest is not +# trusted. # # Since: 6.1 ## From c0df98ab1f3d348bc05f09d1c093abc529f2b530 Mon Sep 17 00:00:00 2001 From: Werner Fink Date: Wed, 6 Aug 2025 08:54:51 +0200 Subject: [PATCH 2751/2760] qemu-iotests: Ignore indentation in Killed messages New bash 5.3 uses a different padding for reporting job status. Resolves: boo#1246830 Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3050 Signed-off-by: Werner Fink Message-ID: Reviewed-by: Kevin Wolf Tested-by: Martin Kletzander Signed-off-by: Kevin Wolf --- tests/qemu-iotests/039.out | 10 +++++----- tests/qemu-iotests/061.out | 4 ++-- tests/qemu-iotests/137.out | 2 +- tests/qemu-iotests/common.filter | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/qemu-iotests/039.out b/tests/qemu-iotests/039.out index e52484d4be..8fdbcc528a 100644 --- a/tests/qemu-iotests/039.out +++ b/tests/qemu-iotests/039.out @@ -11,7 +11,7 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) incompatible_features [0] ERROR cluster 5 refcount=0 reference=1 ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 @@ -46,7 +46,7 @@ read 512/512 bytes at offset 0 Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) incompatible_features [0] ERROR cluster 5 refcount=0 reference=1 Rebuilding refcount structure @@ -60,7 +60,7 @@ incompatible_features [] Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) incompatible_features [] No errors were found on the image. @@ -79,7 +79,7 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) incompatible_features [0] ERROR cluster 5 refcount=0 reference=1 ERROR OFLAG_COPIED data cluster: l2_entry=8000000000050000 refcount=0 @@ -89,7 +89,7 @@ Data may be corrupted, or further writes to the image may corrupt it. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) incompatible_features [] No errors were found on the image. *** done diff --git a/tests/qemu-iotests/061.out b/tests/qemu-iotests/061.out index 24c33add7c..951c6bf3e6 100644 --- a/tests/qemu-iotests/061.out +++ b/tests/qemu-iotests/061.out @@ -118,7 +118,7 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) magic 0x514649fb version 3 backing_file_offset 0x0 @@ -304,7 +304,7 @@ No errors were found on the image. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 wrote 131072/131072 bytes at offset 0 128 KiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) magic 0x514649fb version 3 backing_file_offset 0x0 diff --git a/tests/qemu-iotests/137.out b/tests/qemu-iotests/137.out index 86377c80cd..e19df5b6ba 100644 --- a/tests/qemu-iotests/137.out +++ b/tests/qemu-iotests/137.out @@ -35,7 +35,7 @@ Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: Unsupported value 'blubb' for qcow2 option 'overlap-check'. Allowed are any of the following: none, constant, cached, all wrote 512/512 bytes at offset 0 512 bytes, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) -./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) +./common.rc: Killed ( VALGRIND_QEMU="${VALGRIND_QEMU_IO}" _qemu_proc_exec "${VALGRIND_LOGFILE}" "$QEMU_IO_PROG" $QEMU_IO_ARGS "$@" ) OK: Dirty bit not set Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=67108864 qemu-io: Parameter 'lazy-refcounts' expects 'on' or 'off' diff --git a/tests/qemu-iotests/common.filter b/tests/qemu-iotests/common.filter index 67f819d866..511a55b1e8 100644 --- a/tests/qemu-iotests/common.filter +++ b/tests/qemu-iotests/common.filter @@ -74,7 +74,7 @@ _filter_qemu_io() { _filter_win32 | \ gsed -e "s/[0-9]* ops\; [0-9/:. sec]* ([0-9/.inf]* [EPTGMKiBbytes]*\/sec and [0-9/.inf]* ops\/sec)/X ops\; XX:XX:XX.X (XXX YYY\/sec and XXX ops\/sec)/" \ - -e "s/: line [0-9][0-9]*: *[0-9][0-9]*\( Aborted\| Killed\)/:\1/" \ + -e "s/: line [0-9][0-9]*: *[0-9][0-9]*\( Aborted\| Killed\) \{2,\}/:\1 /" \ -e "s/qemu-io> //g" } From e262646e12acd6c1132e03d57fea20680a503251 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Aug 2025 14:57:44 +0200 Subject: [PATCH 2752/2760] hw/sd/ssi-sd: Return noise (dummy byte) when no card connected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 1585ab9f1ba ("hw/sd/sdcard: Fill SPI response bits in card code") exposed a bug in the SPI adapter: if no SD card is plugged, we are returning "there is a card with an error". This is wrong, we shouldn't return any particular packet response, but the noise shifted on the MISO line. Return the dummy byte, otherwise we get: qemu-system-riscv64: ../hw/sd/ssi-sd.c:160: ssi_sd_transfer: Assertion `s->arglen > 0' failed. Reported-by: Guenter Roeck Fixes: 775616c3ae8 ("Partial SD card SPI mode support") Signed-off-by: Philippe Mathieu-Daudé Tested-by: Guenter Roeck Reviewed-by: Alex Bennée Reviewed-by: Gustavo Romero Tested-by: Alex Bennée Message-Id: <20250812140415.70153-2-philmd@linaro.org> --- hw/sd/ssi-sd.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hw/sd/ssi-sd.c b/hw/sd/ssi-sd.c index 594dead19e..3aacbd0387 100644 --- a/hw/sd/ssi-sd.c +++ b/hw/sd/ssi-sd.c @@ -89,6 +89,10 @@ static uint32_t ssi_sd_transfer(SSIPeripheral *dev, uint32_t val) SDRequest request; uint8_t longresp[5]; + if (!sdbus_get_inserted(&s->sdbus)) { + return SSI_DUMMY; + } + /* * Special case: allow CMD12 (STOP TRANSMISSION) while reading data. * From 7db162fa013878b06a528686ece79ad99f699c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philippe=20Mathieu-Daud=C3=A9?= Date: Fri, 8 Aug 2025 15:45:34 +0200 Subject: [PATCH 2753/2760] tests/functional: Test SPI-SD adapter without SD card connected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SPI-SD adapter should be usable, even without any SD card wired. Refactor test_riscv64_sifive_u_mmc_spi() to make it more generic and add another test, inspired by this report: https://lore.kernel.org/qemu-devel/5b2dc427-f0db-4332-a997-fe0c82415acd@roeck-us.net/ Inspired-by: Guenter Roeck Signed-off-by: Philippe Mathieu-Daudé Reviewed-by: Gustavo Romero Reviewed-by: Alex Bennée Tested-by: Alex Bennée Message-Id: <20250812140415.70153-3-philmd@linaro.org> --- tests/functional/test_riscv64_sifive_u.py | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/tests/functional/test_riscv64_sifive_u.py b/tests/functional/test_riscv64_sifive_u.py index dc4cb8a4a9..358ff0d1f6 100755 --- a/tests/functional/test_riscv64_sifive_u.py +++ b/tests/functional/test_riscv64_sifive_u.py @@ -27,25 +27,37 @@ class SifiveU(LinuxKernelTest): 'rootfs.ext2.gz'), 'b6ed95610310b7956f9bf20c4c9c0c05fea647900df441da9dfe767d24e8b28b') - def test_riscv64_sifive_u_mmc_spi(self): + def do_test_riscv64_sifive_u_mmc_spi(self, connect_card): self.set_machine('sifive_u') kernel_path = self.ASSET_KERNEL.fetch() rootfs_path = self.uncompress(self.ASSET_ROOTFS) self.vm.set_console() kernel_command_line = (self.KERNEL_COMMON_COMMAND_LINE + - 'root=/dev/mmcblk0 rootwait ' 'earlycon=sbi console=ttySIF0 ' - 'panic=-1 noreboot') + 'root=/dev/mmcblk0 ') self.vm.add_args('-kernel', kernel_path, - '-drive', f'file={rootfs_path},if=sd,format=raw', '-append', kernel_command_line, '-no-reboot') + if connect_card: + kernel_command_line += 'panic=-1 noreboot rootwait ' + self.vm.add_args('-drive', f'file={rootfs_path},if=sd,format=raw') + pattern = 'Boot successful.' + else: + kernel_command_line += 'panic=0 noreboot ' + pattern = 'Cannot open root device "mmcblk0" or unknown-block(0,0)' + self.vm.launch() - self.wait_for_console_pattern('Boot successful.') + self.wait_for_console_pattern(pattern) os.remove(rootfs_path) + def test_riscv64_sifive_u_nommc_spi(self): + self.do_test_riscv64_sifive_u_mmc_spi(False) + + def test_riscv64_sifive_u_mmc_spi(self): + self.do_test_riscv64_sifive_u_mmc_spi(True) + if __name__ == '__main__': LinuxKernelTest.main() From 6ad034e71232c2929ed546304c9d249312bb632f Mon Sep 17 00:00:00 2001 From: "Sv. Lockal" Date: Mon, 11 Aug 2025 15:01:59 -0400 Subject: [PATCH 2754/2760] mkvenv: Support pip 25.2 Fix compilation with pip-25.2 due to missing distlib.version Bug: https://gitlab.com/qemu-project/qemu/-/issues/3062 Signed-off-by: Sv. Lockal [Edits: Type "safety" whackamole --js] Signed-off-by: John Snow Message-ID: <20250811190159.237321-1-jsnow@redhat.com> Signed-off-by: Stefan Hajnoczi --- python/scripts/mkvenv.py | 64 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/python/scripts/mkvenv.py b/python/scripts/mkvenv.py index 8ac5b0b2a0..f102527c4d 100644 --- a/python/scripts/mkvenv.py +++ b/python/scripts/mkvenv.py @@ -84,6 +84,7 @@ Sequence, Tuple, Union, + cast, ) import venv @@ -94,17 +95,39 @@ HAVE_DISTLIB = True try: import distlib.scripts - import distlib.version except ImportError: try: # Reach into pip's cookie jar. pylint and flake8 don't understand # that these imports will be used via distlib.xxx. from pip._vendor import distlib import pip._vendor.distlib.scripts # noqa, pylint: disable=unused-import - import pip._vendor.distlib.version # noqa, pylint: disable=unused-import except ImportError: HAVE_DISTLIB = False +# pip 25.2 does not vendor distlib.version, but it uses vendored +# packaging.version +HAVE_DISTLIB_VERSION = True +try: + import distlib.version # pylint: disable=ungrouped-imports +except ImportError: + try: + # pylint: disable=unused-import,ungrouped-imports + import pip._vendor.distlib.version # noqa + except ImportError: + HAVE_DISTLIB_VERSION = False + +HAVE_PACKAGING_VERSION = True +try: + # Do not bother importing non-vendored packaging, because it is not + # in stdlib. + from pip._vendor import packaging + # pylint: disable=unused-import + import pip._vendor.packaging.requirements # noqa + import pip._vendor.packaging.version # noqa +except ImportError: + HAVE_PACKAGING_VERSION = False + + # Try to load tomllib, with a fallback to tomli. # HAVE_TOMLLIB is checked below, just-in-time, so that mkvenv does not fail # outside the venv or before a potential call to ensurepip in checkpip(). @@ -133,6 +156,39 @@ class Ouch(RuntimeError): """An Exception class we can't confuse with a builtin.""" +class Matcher: + """Compatibility appliance for version/requirement string parsing.""" + def __init__(self, name_and_constraint: str): + """Create a matcher from a requirement-like string.""" + if HAVE_DISTLIB_VERSION: + self._m = distlib.version.LegacyMatcher(name_and_constraint) + elif HAVE_PACKAGING_VERSION: + self._m = packaging.requirements.Requirement(name_and_constraint) + else: + raise Ouch("found neither distlib.version nor packaging.version") + self.name = self._m.name + + def match(self, version_str: str) -> bool: + """Return True if `version` satisfies the stored constraint.""" + if HAVE_DISTLIB_VERSION: + return cast( + bool, + self._m.match(distlib.version.LegacyVersion(version_str)) + ) + + assert HAVE_PACKAGING_VERSION + return cast( + bool, + self._m.specifier.contains( + packaging.version.Version(version_str), prereleases=True + ) + ) + + def __repr__(self) -> str: + """Stable debug representation delegated to the backend.""" + return repr(self._m) + + class QemuEnvBuilder(venv.EnvBuilder): """ An extension of venv.EnvBuilder for building QEMU's configure-time venv. @@ -669,7 +725,7 @@ def _do_ensure( canary = None for name, info in group.items(): constraint = _make_version_constraint(info, False) - matcher = distlib.version.LegacyMatcher(name + constraint) + matcher = Matcher(name + constraint) print(f"mkvenv: checking for {matcher}", file=sys.stderr) dist: Optional[Distribution] = None @@ -683,7 +739,7 @@ def _do_ensure( # Always pass installed package to pip, so that they can be # updated if the requested version changes or not _is_system_package(dist) - or not matcher.match(distlib.version.LegacyVersion(dist.version)) + or not matcher.match(dist.version) ): absent.append(name + _make_version_constraint(info, True)) if len(absent) == 1: From b2a948220763b0d5184285a6b72d7eb13d51aad2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alex=20Benn=C3=A9e?= Date: Mon, 4 Aug 2025 17:29:59 +0100 Subject: [PATCH 2755/2760] readthedocs: don't build extra formats MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We don't build the PDFs ourselves for the hosted docs and it looks like rtd can't manage building PDFs now they have gone over a certain size. Disable the extra formats so we can at least have the online stuff again. Signed-off-by: Alex Bennée Reviewed-by: Daniel P. Berrangé Message-ID: <20250804162959.330060-1-alex.bennee@linaro.org> Signed-off-by: Stefan Hajnoczi --- .readthedocs.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 0b262469ce..639f628612 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -21,5 +21,3 @@ python: install: - requirements: docs/requirements.txt -# We want all the document formats -formats: all From 0c0729b46a3680c233e0d45647d5193c5c5083f9 Mon Sep 17 00:00:00 2001 From: Vivek Kasireddy Date: Fri, 18 Jul 2025 16:40:39 -0700 Subject: [PATCH 2756/2760] ui/spice: Destroy the temporary egl fb after the blit is submitted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The temporary egl fb scanout_tex_fb is only needed to facilitate the blit to the display surface's texture (ssd->ds->texture). Therefore, destroy it after the blit is submitted. And, also make sure that it is empty initialized before it is actually used. Fixes: f851cd65 ("ui/spice: Blit the scanout texture if its memory layout is not linear") Reported-by: Peter Maydell Cc: Marc-André Lureau Signed-off-by: Vivek Kasireddy Reviewed-by: Marc-André Lureau Message-ID: <20250718234039.2266704-1-vivek.kasireddy@intel.com> Signed-off-by: Stefan Hajnoczi --- ui/spice-display.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/ui/spice-display.c b/ui/spice-display.c index 9ce622cefc..669832c561 100644 --- a/ui/spice-display.c +++ b/ui/spice-display.c @@ -1183,20 +1183,20 @@ static void qemu_spice_gl_release_dmabuf(DisplayChangeListener *dcl, egl_dmabuf_release_texture(dmabuf); } -static bool spice_gl_blit_scanout_texture(SimpleSpiceDisplay *ssd, - egl_fb *scanout_tex_fb) +static bool spice_gl_blit_scanout_texture(SimpleSpiceDisplay *ssd) { uint32_t offsets[DMABUF_MAX_PLANES], strides[DMABUF_MAX_PLANES]; int fds[DMABUF_MAX_PLANES], num_planes, fourcc; + egl_fb scanout_tex_fb = {}; uint64_t modifier; bool ret; - egl_fb_destroy(scanout_tex_fb); - egl_fb_setup_for_tex(scanout_tex_fb, + egl_fb_setup_for_tex(&scanout_tex_fb, surface_width(ssd->ds), surface_height(ssd->ds), ssd->ds->texture, false); - egl_fb_blit(scanout_tex_fb, &ssd->guest_fb, false); + egl_fb_blit(&scanout_tex_fb, &ssd->guest_fb, false); glFlush(); + egl_fb_destroy(&scanout_tex_fb); if (!ssd->new_scanout_texture) { return true; @@ -1330,9 +1330,7 @@ static void qemu_spice_gl_update(DisplayChangeListener *dcl, } if (spice_remote_client && ssd->blit_scanout_texture) { - egl_fb scanout_tex_fb; - - ret = spice_gl_blit_scanout_texture(ssd, &scanout_tex_fb); + ret = spice_gl_blit_scanout_texture(ssd); if (!ret) { return; } From de784dc0a0128146a88437d57ea27a58af507de0 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 12 Aug 2025 16:26:39 -0400 Subject: [PATCH 2757/2760] Update version for the v10.1.0-rc3 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index c08f99e23e..d53c9cff27 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.92 +10.0.93 From faaaf017d5b9c9f84fb86dcee016944176eee0d9 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Tue, 19 Aug 2025 17:02:35 +0200 Subject: [PATCH 2758/2760] Revert "i386/cpu: Warn about why CPUID_EXT_PDCM is not available" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 00268e00027459abede448662f8794d78eb4b0a4. (The only conflict is in the !is_tdx_vm() part of the condition, which is safe to keep). mark_unavailable_features() actively blocks usage of the feature, so it is a functional change, not merely a emitting warning. The commit was intended to merely warn if PDCM was enabled when the performance counters are not, so revert it. Reported-by: Christian A. Ehrhardt Analyzed-by: Daniel P. Berrangé Analyzed-by: Xiaoyao Li Signed-off-by: Paolo Bonzini Reviewed-by: Daniel P. Berrangé Message-ID: <20250819150235.785559-1-pbonzini@redhat.com> Signed-off-by: Stefan Hajnoczi --- target/i386/cpu.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/target/i386/cpu.c b/target/i386/cpu.c index 673f8583c8..6d85149e6e 100644 --- a/target/i386/cpu.c +++ b/target/i386/cpu.c @@ -8946,9 +8946,6 @@ void x86_cpu_expand_features(X86CPU *cpu, Error **errp) /* PDCM is fixed1 bit for TDX */ if (!cpu->enable_pmu && !is_tdx_vm()) { - mark_unavailable_features(cpu, FEAT_1_ECX, - env->user_features[FEAT_1_ECX] & CPUID_EXT_PDCM, - "This feature is not available due to PMU being disabled"); env->features[FEAT_1_ECX] &= ~CPUID_EXT_PDCM; } From 88f72048d2f5835a1b9eaba690c7861393aef283 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 19 Aug 2025 10:39:59 -0400 Subject: [PATCH 2759/2760] Update version for the v10.1.0-rc4 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index d53c9cff27..dadcbd47d3 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.93 +10.0.94 From f8b2f64e2336a28bf0d50b6ef8a7d8c013e9bcf3 Mon Sep 17 00:00:00 2001 From: Stefan Hajnoczi Date: Tue, 26 Aug 2025 11:23:27 -0400 Subject: [PATCH 2760/2760] Update version for the v10.1.0 release Signed-off-by: Stefan Hajnoczi --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index dadcbd47d3..4149c39eec 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -10.0.94 +10.1.0

    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 diff --git a/python/wheels/meson-1.8.1-py3-none-any.whl b/python/wheels/meson-1.8.1-py3-none-any.whl new file mode 100644 index 0000000000000000000000000000000000000000..a885f0e18cea1d1c32987540dc12d91cb74bcb9d GIT binary patch literal 1013001 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%0AX@<|mWCOhR9Kd-*SZ#hE=mvYd<)?0!5Y z0lTpmv^9VQK$YQ%U5crwnkXXuM;|%{>W6y!zI2{B z%YsZE%S|Ad`v{fLDd9d{>dp&VCQ=KE@#28m$~HGM{YTAX_+Zq@^MeR zRxxqM>!fN1u;#&xK^~h<%sMlqj139xP2uptK(vR!u9@4xJJ00Fi~$3}Pv}>S@?s|| zgOZBr5NpczW?|}nDD?f%Eze&7G-T^BN0v!lcs1^}h!P57hECXm!hnYL%-(pK;fksp z%xrVcaT`=80En3vtc52QfTOWoVZAE=ecD0sa~dN|bW1w~$wziZ>i6IqrfO4}Ne0e0 zSog)nq6Ed6X3*me%MH&M2tZW<U>aHCoO~VlGL_(88>I?3m)vTq_Vhf?CpcFpa zl?eVIKcmaFxn905Oo0e~BiZk4M)S}8<8YW56#<7wRE%?DPiRPR2Xnp{J=(xl5Sl{i zCJ^eSYalG}10GgJ;&*9F(We_b$N3X8B?aU;h;@ zz@vnj%YO``1iULM;)1MSZIpA+5)3>r)#FwCLyx$-_)jB-;ToD>m^HZZ68h5W$|&*g0>TU+;0lPNeFJpj9#e;W7WI*ANg^ zq-fv^I8dBd2Ldiz@2SLe13AG3kO;;4`Grm@tYhY=SOTFoghcMm0Y9DeJO%`$)q+*Uppy`7pJ{5Hj7%HH5kXZWQd`vZ(}f_ zu$8cgbf->^5Z&~5utkR>C6P9Pjmklxm!YDdW2Xs(f$Kp=J5VF;m@{f1RiqtPZsjCF zMnv*ImV|vLCZK&IKK9#y{!&1DN+{Ek;49c8R@6i{m{K2~ilUBswo!O+jqRz|>*ZGd zLqPdqMX3V;#ev&4NoP{UN@sB{oB@njt_8ivWc-$|MPXoRvI>?wqYAlc;?SN1%bc>M zRvgrV*Nk#Bn2YP@6+%lD0H!BRs~A|Ona6y*1Jpt32TvVs1#DDhGd@GmLHj0_B=)us zNoEgd#wBFd!y~oo*3)wPypBK>pS>HCPpZBWo29TnrMngWHIc zj{~}_Ohu3mG_S=|t2!bJ@KIF)Lfv;p{bN}-XhkVEJa6jwL7M^AUVW)j;L#YX@&=m9 zn{5_xltmPsw>N~X(8YA?Uw1o&Z?fe>9wr{W&$KI2CNyosoP^m~$B`P_=IG4+AV>